1 | /* This file is part of the KDE project |
2 | * Copyright (C) 2010 Sebastian Sauer <sebsauer@kdab.com> |
3 | * |
4 | * This library is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU Library General Public |
6 | * License as published by the Free Software Foundation; either |
7 | * version 2 of the License, or (at your option) any later version. |
8 | * |
9 | * This library is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | * Library General Public License for more details. |
13 | * |
14 | * You should have received a copy of the GNU Library General Public License |
15 | * along with this library; see the file COPYING.LIB. If not, write to |
16 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
17 | * Boston, MA 02110-1301, USA. |
18 | */ |
19 | |
20 | #include "kaccessiblebridge.h" |
21 | #include "kaccessibleinterface.h" |
22 | |
23 | #include <QAccessibleInterface> |
24 | #include <QWidget> |
25 | #include <QFile> |
26 | #include <QDBusConnection> |
27 | #include <QDBusConnectionInterface> |
28 | #include <QDBusInterface> |
29 | #include <QDBusPendingCall> |
30 | #include <QDBusArgument> |
31 | #include <QDBusMetaType> |
32 | #include <kdebug.h> |
33 | |
34 | Q_EXPORT_PLUGIN(BridgePlugin) |
35 | |
36 | class Bridge::Private |
37 | { |
38 | public: |
39 | BridgePlugin *m_plugin; |
40 | const QString m_key; |
41 | QAccessibleInterface *m_root; |
42 | QList<QObject*> ; |
43 | QRect m_lastFocusRect; |
44 | QString m_lastFocusName; |
45 | |
46 | Private(BridgePlugin *plugin, const QString& key) |
47 | : m_plugin(plugin) |
48 | , m_key(key) |
49 | , m_root(0) |
50 | , m_lastFocusRect(QRect(0,0,0,0)) |
51 | , m_app(0) |
52 | { |
53 | } |
54 | |
55 | ~Private() |
56 | { |
57 | delete m_app; |
58 | } |
59 | |
60 | QDBusInterface* app() |
61 | { |
62 | Q_ASSERT(m_root); |
63 | if(!m_app) { |
64 | m_app = new QDBusInterface(QLatin1String("org.kde.kaccessibleapp" ), QLatin1String("/Adaptor" )); |
65 | if(m_app->isValid()) { |
66 | kDebug() << "Connected with the org.kde.kaccessibleapp dbus-service" ; |
67 | KAccessibleInterface dbusIface; |
68 | dbusIface.set(m_root, 0); |
69 | m_app->asyncCall(QLatin1String("setRootObject" ), qVariantFromValue(dbusIface)); |
70 | } |
71 | } |
72 | if(m_app->lastError().isValid()) { |
73 | kDebug() << "DBus error:" << m_app->lastError().name() << m_app->lastError().message(); |
74 | delete m_app; m_app = 0; |
75 | } else if(!m_app->isValid()) { |
76 | kDebug() << "Failed to connect with the org.kde.kaccessibleapp dbus-service" ; |
77 | delete m_app; m_app = 0; |
78 | } |
79 | return m_app; |
80 | } |
81 | private: |
82 | QDBusInterface *m_app; |
83 | }; |
84 | Bridge::Bridge(BridgePlugin *plugin, const QString& key) |
85 | : QObject(plugin) |
86 | , QAccessibleBridge() |
87 | , d(new Private(plugin, key)) |
88 | { |
89 | } |
90 | |
91 | Bridge::~Bridge() |
92 | { |
93 | delete d; |
94 | } |
95 | |
96 | void Bridge::notifyAccessibilityUpdate(int reason, QAccessibleInterface *interface, int child) |
97 | { |
98 | if(reason == QAccessible::ObjectShow || reason == QAccessible::ObjectHide) { |
99 | return; |
100 | } |
101 | |
102 | if(!d->m_root) { |
103 | return; |
104 | } |
105 | |
106 | QObject *obj = interface->object(); |
107 | if(!obj) { |
108 | return; |
109 | } |
110 | |
111 | QDBusInterface* app = d->app(); |
112 | if(!app) { |
113 | return; |
114 | } |
115 | |
116 | const QString name = interface->text(QAccessible::Name, child); |
117 | const QString description = interface->text(QAccessible::Description, child); |
118 | |
119 | KAccessibleInterface dbusIface; |
120 | dbusIface.set(interface, child); |
121 | |
122 | QAccessibleInterface *childInterface = 0; |
123 | //if(child > 0) interface->navigate(QAccessible::Child, child, &childInterface); |
124 | |
125 | switch(reason) { |
126 | case QAccessible::PopupMenuStart: { |
127 | d->m_popupMenus.append(obj); |
128 | } break; |
129 | case QAccessible::PopupMenuEnd: { |
130 | const int index = d->m_popupMenus.lastIndexOf(obj); |
131 | if(index >= 0) d->m_popupMenus.removeAt(index); |
132 | } break; |
133 | |
134 | case QAccessible::Alert: { |
135 | //kDebug() << reasonToString(reason) << "object=" << (obj ? QString("%1 (%2)").arg(obj->objectName()).arg(obj->metaObject()->className()) : "NULL") << "name=" << name; |
136 | app->asyncCall(QLatin1String( "setAlert" ), qVariantFromValue(dbusIface)); |
137 | } break; |
138 | |
139 | case QAccessible::DialogStart: { |
140 | kDebug() << reasonToString(reason) << QLatin1String( "object=" ) << (obj ? QString(QLatin1String( "%1 (%2)" )).arg(obj->objectName()).arg(QLatin1String( obj->metaObject()->className() )) : QLatin1String( "NULL" )) << QLatin1String( "name=" ) << name; |
141 | //app->asyncCall("sayText", name); |
142 | } break; |
143 | case QAccessible::DialogEnd: { |
144 | kDebug() << reasonToString(reason) << QLatin1String( "object=" ) << (obj ? QString(QLatin1String( "%1 (%2)" )).arg(obj->objectName()).arg(QLatin1String( obj->metaObject()->className() )) : QLatin1String( "NULL" )) << QLatin1String( "name=" ) << name; |
145 | //app->asyncCall("sayText", name); |
146 | } break; |
147 | |
148 | case QAccessible::NameChanged: { |
149 | kDebug() << reasonToString(reason) << QLatin1String( "object=" ) << (obj ? QString(QLatin1String( "%1 (%2)" )).arg(obj->objectName()).arg(QLatin1String( obj->metaObject()->className() )) : QLatin1String( "NULL" )) << QLatin1String( "name=" ) << name; |
150 | //app->asyncCall("sayText", name); |
151 | } break; |
152 | case QAccessible::ValueChanged: { |
153 | QString value = interface->text(QAccessible::Value, child); |
154 | kDebug() << reasonToString(reason) << QLatin1String( "object=" ) << (obj ? QString(QLatin1String( "%1 (%2)" )).arg(obj->objectName() ).arg(QLatin1String( obj->metaObject()->className() )) : QLatin1String( "NULL" )) << QLatin1String( "name=" ) << name << QLatin1String( "value=" ) << value; |
155 | app->asyncCall(QLatin1String( "setValueChanged" ), qVariantFromValue(dbusIface)); |
156 | } break; |
157 | case QAccessible::StateChanged: { |
158 | kDebug() << reasonToString(reason) << QLatin1String( "object=" ) << (obj ? QString(QLatin1String( "%1 (%2)" ) ).arg(obj->objectName()).arg(QLatin1String( obj->metaObject()->className() )) : QLatin1String( "NULL" )) << QLatin1String( "name=" )<< name << QLatin1String( "state=" ) << stateToString(dbusIface.state); |
159 | } break; |
160 | |
161 | case QAccessible::Focus: { |
162 | // abort if the focus would interrupt a popupmenu |
163 | if(!d->m_popupMenus.isEmpty()) { |
164 | bool ok = false; |
165 | QObject* = d->m_popupMenus.last(); |
166 | for(QObject* o = obj; o; o = o->parent()) |
167 | if(o == lastPopupMenu) { ok = true; break; } |
168 | if(!ok) |
169 | return; |
170 | } |
171 | |
172 | // don't emit the focus changed signal if the focus didn't really changed since last time |
173 | if(dbusIface.rect == d->m_lastFocusRect && dbusIface.name == d->m_lastFocusName) |
174 | return; |
175 | d->m_lastFocusRect = dbusIface.rect; |
176 | d->m_lastFocusName = dbusIface.name; |
177 | |
178 | // here we could add hacks to special case applications/widgets :) |
179 | // |
180 | // QWidget *w = childInterface ? dynamic_cast<QWidget*>(childobj) : 0; |
181 | // if(!w) w = dynamic_cast<QWidget*>(obj); |
182 | // if(w) r = QRect(w->mapToGlobal(QPoint(w->x(), w->y())), w->size()); |
183 | |
184 | kDebug() << reasonToString(reason) << QLatin1String( "object=" ) << (obj ? QString(QLatin1String( "%1 (%2)" )).arg(obj->objectName()).arg(QLatin1String( obj->metaObject()->className() )) : QLatin1String( "NULL" )) << QLatin1String( "name=" ) << name << QLatin1String( "rect=" ) << dbusIface.rect; |
185 | app->asyncCall(QLatin1String( "setFocusChanged" ), qVariantFromValue(dbusIface)); |
186 | } break; |
187 | default: |
188 | kDebug() << reasonToString(reason) << QLatin1String( "object=" ) << (obj ? QString(QLatin1String( "%1 (%2)" )).arg(obj->objectName()).arg(QLatin1String( obj->metaObject()->className() )) : QLatin1String( "NULL" )) << QLatin1String( "name=" ) << name; |
189 | break; |
190 | } |
191 | |
192 | delete childInterface; |
193 | //delete d->m_app; d->m_app = 0; |
194 | } |
195 | |
196 | void Bridge::focusChanged(int px, int py, int rx, int ry, int rwidth, int rheight) |
197 | { |
198 | kDebug()<<"KAccessibleBridge: focusChanged px=" << px << "py=" << py << "rx=" << rx << "ry=" << ry << "rwidth=" << rwidth << "rheight=" << rheight; |
199 | } |
200 | |
201 | void Bridge::setRootObject(QAccessibleInterface *interface) |
202 | { |
203 | d->m_root = interface; |
204 | |
205 | kDebug()<<QLatin1String( "KAccessibleBridge: setRootObject object=" ) << (interface->object() ? QString(QLatin1String( "%1 (%2)" )).arg(interface->object()->objectName()).arg(QLatin1String( interface->object()->metaObject()->className() )) : QLatin1String( "NULL" )); |
206 | |
207 | if( ! QDBusConnection::sessionBus().isConnected()) { |
208 | kWarning()<<"KAccessibleBridge: Failed to connect to session bus" ; |
209 | d->m_root = 0; |
210 | return; |
211 | } |
212 | |
213 | if( ! QDBusConnection::sessionBus().interface()->isServiceRegistered(QLatin1String( "org.kde.kaccessibleapp" ))) { |
214 | kDebug()<<"KAccessibleBridge: Starting kaccessibleapp dbus service" ; |
215 | QDBusConnection::sessionBus().interface()->startService(QLatin1String( "org.kde.kaccessibleapp" )); |
216 | if( ! QDBusConnection::sessionBus().interface()->isServiceRegistered(QLatin1String( "org.kde.kaccessibleapp" ))) { |
217 | kWarning()<<"KAccessibleBridge: Failed to start kaccessibleapp dbus service" ; |
218 | d->m_root = 0; |
219 | return; |
220 | } |
221 | |
222 | //for testing; |
223 | //QDBusConnection::sessionBus().connect("org.kde.kaccessibleapp", "/Adaptor", "org.kde.kaccessibleapp.Adaptor", "focusChanged", this, SLOT(focusChanged(int,int,int,int,int,int))); |
224 | } |
225 | } |
226 | |
227 | BridgePlugin::BridgePlugin(QObject *parent) |
228 | : QAccessibleBridgePlugin(parent) |
229 | { |
230 | qDBusRegisterMetaType<KAccessibleInterface>(); |
231 | } |
232 | |
233 | BridgePlugin::~BridgePlugin() |
234 | { |
235 | } |
236 | |
237 | QAccessibleBridge* BridgePlugin::create(const QString &key) |
238 | { |
239 | return new Bridge(this, key); |
240 | } |
241 | |
242 | QStringList BridgePlugin::keys() const |
243 | { |
244 | return QStringList() << QLatin1String( "KAccessibleBridge" ); |
245 | } |
246 | |