Add classes to be documented.
[qt-at-spi:qt-at-spi.git] / src / application.cpp
1 /*
2  * D-Bus AT-SPI, Qt Adaptor
3  *
4  * Copyright 2009-2011 Nokia Corporation and/or its subsidiary(-ies).
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include "application.h"
21
22 #include "qapplication.h"
23 #include <QDBusPendingReply>
24 #include <QDebug>
25
26 #include "generated/dec_proxy.h"
27
28 //#define KEYBOARD_DEBUG
29
30
31 /*!
32     \class QSpiApplicationAdaptor
33
34     \brief QSpiApplicationAdaptor
35
36     QSpiApplicationAdaptor
37 */
38
39 QSpiApplicationAdaptor::QSpiApplicationAdaptor(const QDBusConnection &connection, QObject *parent)
40     : QObject(parent), dbusConnection(connection)
41 {
42     qApp->installEventFilter(this);
43 }
44
45 enum QSpiKeyEventType {
46       QSPI_KEY_EVENT_PRESS,
47       QSPI_KEY_EVENT_RELEASE,
48       QSPI_KEY_EVENT_LAST_DEFINED
49 };
50
51 bool QSpiApplicationAdaptor::eventFilter(QObject *target, QEvent *event)
52 {
53     if (!event->spontaneous())
54         return false;
55
56     switch (event->type()) {
57     case QEvent::WindowActivate: {
58         emit windowActivated(target, true);
59         break;
60     case QEvent::WindowDeactivate:
61             emit windowActivated(target, false);
62             break;
63     }
64         case QEvent::KeyPress:
65         case QEvent::KeyRelease: {
66             QKeyEvent *keyEvent = static_cast <QKeyEvent *>(event);
67             QSpiDeviceEvent de;
68
69             if (event->type() == QEvent::KeyPress)
70                 de.type = QSPI_KEY_EVENT_PRESS;
71             else
72                 de.type = QSPI_KEY_EVENT_RELEASE;
73
74             de.id = keyEvent->nativeVirtualKey();
75             de.hardwareCode = keyEvent->nativeScanCode();
76
77             de.modifiers = keyEvent->nativeModifiers();
78             de.timestamp = QDateTime::currentMSecsSinceEpoch();
79
80             // FIXME: how to generate key strings?
81             // FIXME: localize?
82             if (keyEvent->key() == Qt::Key_Tab) {
83                 de.text = "Tab";
84             } else if (keyEvent->key() == Qt::Key_Backtab) {
85                 de.text = "Backtab";
86             } else if (keyEvent->key() == Qt::Key_Left) {
87                 de.text = "Left";
88             } else if (keyEvent->key() == Qt::Key_Right) {
89                 de.text = "Right";
90             } else if (keyEvent->key() == Qt::Key_Up) {
91                 de.text = "Up";
92             } else if (keyEvent->key() == Qt::Key_Down) {
93                 de.text = "Down";
94             } else if (keyEvent->key() == Qt::Key_Enter || keyEvent->key() == Qt::Key_Return) {
95                 de.text = "Return";
96             } else if (keyEvent->key() == Qt::Key_Backspace) {
97                 de.text = "BackSpace";
98             } else if (keyEvent->key() == Qt::Key_Delete) {
99                 de.text = "Delete";
100             } else if (keyEvent->key() == Qt::Key_PageUp) {
101                 de.text = "Page_Up";
102             } else if (keyEvent->key() == Qt::Key_PageDown) {
103                 de.text = "Page_Down";
104             } else if (keyEvent->key() == Qt::Key_Home) {
105                 de.text = "Home";
106             } else if (keyEvent->key() == Qt::Key_End) {
107                 de.text = "End";
108             } else if (keyEvent->key() == Qt::Key_Escape) {
109                 de.text = "Escape";
110             } else if (keyEvent->key() == Qt::Key_Space) {
111                 de.text = "space";
112             } else if (keyEvent->key() == Qt::Key_CapsLock) {
113                 de.text = "Caps_Lock";
114             } else if (keyEvent->key() == Qt::Key_NumLock) {
115                 de.text = "Num_Lock";
116             } else {
117                 de.text = keyEvent->text();
118             }
119 //            "F1", "F2", "F3", "F4", "F5", "F6",
120 //            "F7", "F8", "F9", "F10", "F11", "F12"
121
122             // FIXME
123             de.isText = !keyEvent->text().trimmed().isEmpty();
124
125 #ifdef KEYBOARD_DEBUG
126             qDebug() << "Key event text: " << event->type() << de.isText << " " << de.text
127                      << " hardware code: " << de.hardwareCode
128                      << " native sc: " << keyEvent->nativeScanCode()
129                      << " native mod: " << keyEvent->nativeModifiers()
130                      << "native virt: " << keyEvent->nativeVirtualKey();
131 #endif
132
133             QDBusMessage m = QDBusMessage::createMethodCall("org.a11y.atspi.Registry",
134                                                             "/org/a11y/atspi/registry/deviceeventcontroller",
135                                                             "org.a11y.atspi.DeviceEventController", "NotifyListenersSync");
136             m.setArguments(QVariantList() <<QVariant::fromValue(de));
137
138             // FIXME: this is critical, the timeout should probably be pretty low to allow normal processing
139             int timeout = 100;
140             bool sent = dbusConnection.callWithCallback(m, this, SLOT(notifyKeyboardListenerCallback(QDBusMessage)),
141                             SLOT(notifyKeyboardListenerError(QDBusError, QDBusMessage)), timeout);
142             if (!sent)
143                 return false;
144
145             //queue the event and send it after callback
146             keyEvents.enqueue(QPair<QObject*, QKeyEvent*> (target, copyKeyEvent(keyEvent)));
147             return true;
148     }
149         default:
150             break;
151     }
152     return false;
153 }
154
155 QKeyEvent* QSpiApplicationAdaptor::copyKeyEvent(QKeyEvent* old)
156 {
157     return new QKeyEvent(old->type(), old->key(), old->modifiers(), old->text(), old->isAutoRepeat(), old->count());
158 }
159
160 void QSpiApplicationAdaptor::notifyKeyboardListenerCallback(const QDBusMessage& message)
161 {
162     Q_ASSERT(message.arguments().length() == 1);
163     if (message.arguments().at(0).toBool() == true) {
164         if (!keyEvents.length()) {
165             qWarning() << "QSpiApplication::notifyKeyboardListenerCallback with no queued key called";
166             return;
167         }
168         keyEvents.dequeue();
169     } else {
170         if (!keyEvents.length()) {
171             qWarning() << "QSpiApplication::notifyKeyboardListenerCallback with no queued key called";
172             return;
173         }
174         QPair<QObject*, QKeyEvent*> event = keyEvents.dequeue();
175         QApplication::postEvent(event.first, event.second);
176     }
177 }
178
179 void QSpiApplicationAdaptor::notifyKeyboardListenerError(const QDBusError& error, const QDBusMessage& /*message*/)
180 {
181     qWarning() << "QSpiApplication::keyEventError " << error.name() << error.message();
182     while (!keyEvents.isEmpty()) {
183         QPair<QObject*, QKeyEvent*> event = keyEvents.dequeue();
184         QApplication::postEvent(event.first, event.second);
185     }
186 }