2
* Copyright (C) 2010 Canonical, Ltd.
5
* Olivier Tilloy <olivier.tilloy@canonical.com>
7
* This program is free software; you can redistribute it and/or modify
8
* it under the terms of the GNU General Public License as published by
9
* the Free Software Foundation; version 3.
11
* This program 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
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with this program. If not, see <http://www.gnu.org/licenses/>.
20
#include "launcherview.h"
23
#include <keyboardmodifiersmonitor.h>
25
#include <hotkeymonitor.h>
26
#include <keymonitor.h>
27
#include <screeninfo.h>
30
#include <QApplication>
31
#include <QDesktopWidget>
34
#include <QGraphicsObject>
36
#include <QtDeclarative/qdeclarative.h>
37
#include <QDeclarativeEngine>
38
#include <QDeclarativeContext>
39
#include <QDeclarativeImageProvider>
40
#include <QtDBus/QDBusInterface>
41
#include <QtDBus/QDBusPendingCall>
42
#include <QtDBus/QDBusReply>
43
#include <QtDBus/QDBusConnectionInterface>
46
#include <X11/Xatom.h>
48
static const int KEY_HOLD_THRESHOLD = 250;
50
static const char* DASH_DBUS_SERVICE = "com.canonical.Unity2d.Dash";
51
static const char* DASH_DBUS_PATH = "/Dash";
52
static const char* DASH_DBUS_INTERFACE = "com.canonical.Unity2d.Dash";
53
static const char* SPREAD_DBUS_SERVICE = "com.canonical.Unity2d.Spread";
54
static const char* SPREAD_DBUS_PATH = "/Spread";
55
static const char* SPREAD_DBUS_INTERFACE = "com.canonical.Unity2d.Spread";
57
static const char* DASH_DBUS_PROPERTY_ACTIVE = "active";
58
static const char* DASH_DBUS_METHOD_ACTIVATE_HOME = "activateHome";
59
static const char* SPREAD_DBUS_METHOD_IS_SHOWN = "IsShown";
60
static const char* COMMANDS_LENS_ID = "commands.lens";
62
LauncherView::LauncherView(QWidget* parent) :
63
Unity2DDeclarativeView(parent),
64
m_superKeyPressed(false), m_superKeyHeld(false)
66
setTransparentBackground(QX11Info::isCompositingManagerRunning());
68
m_superKeyHoldTimer.setSingleShot(true);
69
m_superKeyHoldTimer.setInterval(KEY_HOLD_THRESHOLD);
70
connect(&m_superKeyHoldTimer, SIGNAL(timeout()), SLOT(updateSuperKeyHoldState()));
71
connect(this, SIGNAL(superKeyTapped()), SLOT(toggleDash()));
73
m_screenInfo = new ScreenInfo(ScreenInfo::TopLeft, this);
75
connect(&launcher2dConfiguration(), SIGNAL(superKeyEnableChanged(bool)), SLOT(updateSuperKeyMonitoring()));
76
updateSuperKeyMonitoring();
78
/* Alt+F1 toggle the keyboard focus between laucher and other(previous) application. */
79
Hotkey* altF1 = HotkeyMonitor::instance().getHotkeyFor(Qt::Key_F1, Qt::AltModifier);
80
connect(altF1, SIGNAL(pressed()), SLOT(onAltF1Pressed()));
82
/* Alt+F2 shows the dash with the commands lens activated. */
83
Hotkey* altF2 = HotkeyMonitor::instance().getHotkeyFor(Qt::Key_F2, Qt::AltModifier);
84
connect(altF2, SIGNAL(pressed()), SLOT(showCommandsLens()));
86
/* Super+S before 'Spread'ing, close all the contextual menus/tooltips in the launcher. */
87
Hotkey* superS = HotkeyMonitor::instance().getHotkeyFor(Qt::Key_S, Qt::MetaModifier);
88
connect(superS, SIGNAL(pressed()), SLOT(onSuperSPressed()));
90
/* Super+{n} for 0 ≤ n ≤ 9 activates the item with index (n + 9) % 10. */
91
for (Qt::Key key = Qt::Key_0; key <= Qt::Key_9; key = (Qt::Key) (key + 1)) {
92
Hotkey* hotkey = HotkeyMonitor::instance().getHotkeyFor(key, Qt::MetaModifier);
93
connect(hotkey, SIGNAL(pressed()), SLOT(forwardNumericHotkey()));
94
hotkey = HotkeyMonitor::instance().getHotkeyFor(key, Qt::MetaModifier | Qt::ShiftModifier);
95
connect(hotkey, SIGNAL(pressed()), SLOT(forwardNumericHotkey()));
99
LauncherView::~LauncherView()
104
LauncherView::focusInEvent(QFocusEvent* event)
106
QDeclarativeView::focusInEvent(event);
107
Q_EMIT focusChanged(true);
111
LauncherView::focusOutEvent(QFocusEvent* event)
113
QDeclarativeView::focusOutEvent(event);
114
Q_EMIT focusChanged(false);
118
LauncherView::updateSuperKeyMonitoring()
120
KeyboardModifiersMonitor *modifiersMonitor = KeyboardModifiersMonitor::instance();
121
KeyMonitor *keyMonitor = KeyMonitor::instance();
122
HotkeyMonitor& hotkeyMonitor = HotkeyMonitor::instance();
124
QVariant value = launcher2dConfiguration().property("superKeyEnable");
125
if (!value.isValid() || value.toBool() == true) {
126
hotkeyMonitor.enableModifiers(Qt::MetaModifier);
127
QObject::connect(modifiersMonitor,
128
SIGNAL(keyboardModifiersChanged(Qt::KeyboardModifiers)),
129
this, SLOT(setHotkeysForModifiers(Qt::KeyboardModifiers)));
130
/* Ignore Super presses if another key was pressed simultaneously
131
(i.e. a shortcut). https://bugs.launchpad.net/unity-2d/+bug/801073 */
132
QObject::connect(keyMonitor,
133
SIGNAL(keyPressed()),
134
this, SLOT(ignoreSuperPress()));
135
setHotkeysForModifiers(modifiersMonitor->keyboardModifiers());
137
hotkeyMonitor.disableModifiers(Qt::MetaModifier);
138
QObject::disconnect(modifiersMonitor,
139
SIGNAL(keyboardModifiersChanged(Qt::KeyboardModifiers)),
140
this, SLOT(setHotkeysForModifiers(Qt::KeyboardModifiers)));
141
QObject::disconnect(keyMonitor,
142
SIGNAL(keyPressed()),
143
this, SLOT(ignoreSuperPress()));
144
m_superKeyHoldTimer.stop();
145
m_superKeyPressed = false;
146
if (m_superKeyHeld) {
147
m_superKeyHeld = false;
148
Q_EMIT superKeyHeldChanged(false);
154
LauncherView::setHotkeysForModifiers(Qt::KeyboardModifiers modifiers)
156
/* This is the new new state of the Super key (AKA Meta key), while
157
m_superKeyPressed is the previous state of the key at the last modifiers change. */
158
bool superKeyPressed = modifiers.testFlag(Qt::MetaModifier);
160
if (m_superKeyPressed != superKeyPressed) {
161
m_superKeyPressed = superKeyPressed;
162
if (superKeyPressed) {
163
m_superPressIgnored = false;
164
/* If the key is pressed, start up a timer to monitor if it's being held short
165
enough to qualify as just a "tap" or as a proper hold */
166
m_superKeyHoldTimer.start();
168
m_superKeyHoldTimer.stop();
170
/* If the key is released, and was not being held, it means that the user just
171
performed a "tap". Unless we're told to ignore that tap, that is. */
172
if (!m_superKeyHeld && !m_superPressIgnored) {
173
Q_EMIT superKeyTapped();
175
/* Otherwise the user just terminated a hold. */
176
else if(m_superKeyHeld){
177
m_superKeyHeld = false;
178
Q_EMIT superKeyHeldChanged(m_superKeyHeld);
185
LauncherView::updateSuperKeyHoldState()
187
/* If the key was released in the meantime, just do nothing, otherwise
188
consider the key being held, unless we're told to ignore it. */
189
if (m_superKeyPressed && !m_superPressIgnored) {
190
m_superKeyHeld = true;
191
Q_EMIT superKeyHeldChanged(m_superKeyHeld);
196
LauncherView::ignoreSuperPress()
198
/* There was a key pressed, ignore current super tap/hold */
199
m_superPressIgnored = true;
203
LauncherView::forwardNumericHotkey()
205
Hotkey* hotkey = qobject_cast<Hotkey*>(sender());
206
if (hotkey != NULL) {
207
/* Shortcuts from 1 to 9 should activate the items with index
208
from 1 to 9 (index 0 being the so-called "BFB" or Dash launcher).
209
Shortcut for 0 should activate item with index 10.
210
In other words, the indexes are activated in the same order as
211
the keys appear on a standard keyboard. */
212
Qt::Key key = hotkey->key();
213
if (key >= Qt::Key_1 && key <= Qt::Key_9) {
214
int index = key - Qt::Key_0;
215
if (hotkey->modifiers() & Qt::ShiftModifier) {
216
Q_EMIT newInstanceShortcutPressed(index);
218
Q_EMIT activateShortcutPressed(index);
220
} else if (key == Qt::Key_0) {
221
if (hotkey->modifiers() & Qt::ShiftModifier) {
222
Q_EMIT newInstanceShortcutPressed(10);
224
Q_EMIT activateShortcutPressed(10);
231
LauncherView::toggleDash()
233
QDBusInterface dashInterface(DASH_DBUS_SERVICE, DASH_DBUS_PATH, DASH_DBUS_INTERFACE);
235
QVariant dashActiveResult = dashInterface.property(DASH_DBUS_PROPERTY_ACTIVE);
236
if (!dashActiveResult.isValid()) {
237
UQ_WARNING << "Can't read the DBUS Dash property" << DASH_DBUS_PROPERTY_ACTIVE
238
<< "on" << DASH_DBUS_SERVICE << DASH_DBUS_PATH << DASH_DBUS_INTERFACE;
242
bool dashActive = dashActiveResult.toBool();
244
if (!dashInterface.setProperty(DASH_DBUS_PROPERTY_ACTIVE, false)) {
245
UQ_WARNING << "Can't set the DBUS Dash property" << DASH_DBUS_PROPERTY_ACTIVE
246
<< "on" << DASH_DBUS_SERVICE << DASH_DBUS_PATH << DASH_DBUS_INTERFACE;
249
/* Check if the spread is active before activating the dash.
250
We need to do this since the spread can't prevent the launcher from
251
monitoring the super key and therefore getting to this point if
254
/* Check if the spread is present on DBUS first, as we don't want to have DBUS
255
activate it if it's not running yet */
256
QDBusConnectionInterface* sessionBusIFace = QDBusConnection::sessionBus().interface();
257
QDBusReply<bool> reply = sessionBusIFace->isServiceRegistered(SPREAD_DBUS_SERVICE);
258
if (reply.isValid() && reply.value() == true) {
259
QDBusInterface spreadInterface(SPREAD_DBUS_SERVICE, SPREAD_DBUS_PATH,
260
SPREAD_DBUS_INTERFACE);
262
QDBusReply<bool> spreadActiveResult = spreadInterface.call(SPREAD_DBUS_METHOD_IS_SHOWN);
263
if (spreadActiveResult.isValid() && spreadActiveResult.value() == true) {
268
dashInterface.asyncCall(DASH_DBUS_METHOD_ACTIVATE_HOME);
273
LauncherView::showCommandsLens()
275
QDBusInterface dashInterface(DASH_DBUS_SERVICE, DASH_DBUS_PATH, DASH_DBUS_INTERFACE);
276
dashInterface.asyncCall("activateLens", COMMANDS_LENS_ID);
281
LauncherView::onSuperSPressed()
283
QGraphicsObject* launcher = rootObject();
284
QMetaObject::invokeMethod(launcher, "hideMenu", Qt::AutoConnection);
288
LauncherView::onAltF1Pressed()
290
QGraphicsObject* launcher = rootObject();
293
QMetaObject::invokeMethod(launcher, "hideMenu", Qt::AutoConnection);
294
forceDeactivateWindow();
296
forceActivateWindow();
297
QMetaObject::invokeMethod(launcher, "focusBFB", Qt::AutoConnection);