17
17
* along with this program. If not, see <http://www.gnu.org/licenses/>.
20
21
#include "launchermenu.h"
21
#include "launcheritem.h"
25
24
#include <QApplication>
26
#include <QResizeEvent>
29
25
#include <QDesktopWidget>
32
LauncherContextualMenu::LauncherContextualMenu():
33
QMenu(0), m_folded(true), m_launcherItem(NULL), m_titleAction(NULL)
29
QLauncherContextualMenu::QLauncherContextualMenu():
30
QMenu(0), m_application(NULL)
35
/* Timer used for to hide the menu after a given delay (hideWithDelay()) */
36
m_hidingDelayTimer.setSingleShot(true);
37
QObject::connect(&m_hidingDelayTimer, SIGNAL(timeout()), this, SLOT(hide()));
39
32
/* The tooltip/menu shouldn’t be modal. */
40
33
setWindowFlags(Qt::ToolTip);
42
/* The tooltip/menu should not move when switching workspaces. */
43
setAttribute(Qt::WA_X11NetWmWindowTypeDock);
45
/* Use transparency if available.
46
Warning: if the availability of transparency changes over time, for
47
example because a compositing window manager is launched, we
48
do not react to that and the menu is likely to break visually.
49
Ref: http://bugreports.qt.nokia.com/browse/QTBUG-6044
51
if (transparencyAvailable()) {
52
setAttribute(Qt::WA_TranslucentBackground);
55
35
/* Custom appearance. */
58
/* Load the pixmap for the arrow. It is drawn separately as its position
59
may vary depending on the position of the menu on the screen. */
60
if (transparencyAvailable()) {
61
if (QApplication::isLeftToRight()) {
62
m_arrow.load(":/launchermenu/arrow.png");
64
m_arrow.load(":/launchermenu/arrow_rtl.png");
67
if (QApplication::isLeftToRight()) {
68
m_arrow.load(":/launchermenu/arrow_no_transparency.png");
70
m_arrow.load(":/launchermenu/arrow_no_transparency_rtl.png");
74
/* First action used to display the title of the item */
75
m_titleAction = new QAction(this);
76
addAction(m_titleAction);
38
m_title = new QAction(this);
39
m_title->setEnabled(false);
44
m_keep = new QAction(this);
46
m_keep->setVisible(false);
47
QObject::connect(m_keep, SIGNAL(triggered()), this, SLOT(onKeepTriggered()));
49
m_quit = new QAction(this);
50
m_quit->setText("Quit");
51
m_quit->setVisible(false);
53
QObject::connect(m_quit, SIGNAL(triggered()), this, SLOT(onQuitTriggered()));
79
LauncherContextualMenu::~LauncherContextualMenu()
56
QLauncherContextualMenu::~QLauncherContextualMenu()
84
LauncherContextualMenu::loadCSS()
61
QLauncherContextualMenu::loadCSS()
86
QString cssFilePath = ":/launchermenu/launchermenu.css";
87
if (QApplication::isRightToLeft()) {
88
cssFilePath = ":/launchermenu/launchermenu_rtl.css";
63
/* FIXME: surely there must be a cleaner way to do that in Qt… */
65
if (QCoreApplication::applicationDirPath() == INSTALL_PREFIX "/bin")
67
/* Running installed */
68
path = INSTALL_PREFIX "/" UNITY_QT_DIR "/launcher/";
73
path = "UnityApplications/";
75
path += "launchermenu.css";
91
QFile file(cssFilePath);
92
78
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
97
83
setStyleSheet(css);
101
LauncherContextualMenu::title() const
107
LauncherContextualMenu::setTitle(const QString& title)
109
setAccessibleName(title);
111
/* Escaping ampersands so that they are not considered as keyboard
113
m_titleAction->setText(m_title.replace("&", "&&"));
114
Q_EMIT titleChanged(m_title);
118
LauncherContextualMenu::updateMask()
120
QPixmap pixmap(size());
121
render(&pixmap, QPoint(), QRegion(),
122
QWidget::DrawWindowBackground | QWidget::DrawChildren | QWidget::IgnoreMask);
123
setMask(pixmap.createMaskFromColor("red"));
127
LauncherContextualMenu::resizeEvent(QResizeEvent* event)
129
QMenu::resizeEvent(event);
130
/* If transparent windows are not available use the XShape extension */
131
if (!transparencyAvailable()) {
137
LauncherContextualMenu::transparencyAvailable() const
139
return QX11Info::isCompositingManagerRunning();
143
LauncherContextualMenu::leaveEvent(QEvent* event)
145
/* Prepare for hiding the menu when the mouse leaves it */
146
m_hidingDelayTimer.start();
147
QMenu::leaveEvent(event);
151
LauncherContextualMenu::enterEvent(QEvent* event)
153
/* Always keep the menu visible when the mouse hovers it */
154
m_hidingDelayTimer.stop();
155
QMenu::enterEvent(event);
159
LauncherContextualMenu::focusOutEvent(QFocusEvent* event)
161
/* Hide menu if mouse click outside widget */
162
m_hidingDelayTimer.stop();
167
LauncherContextualMenu::show(int x, int y)
169
m_hidingDelayTimer.stop();
87
QLauncherContextualMenu::show(int y, const QVariant& application)
89
QLauncherApplication* app = (QLauncherApplication*) application.value<QObject*>();
176
if (QApplication::isRightToLeft()) {
177
x = QApplication::desktop()->width() - x - sizeHint().width();
180
move(x, y - sizeHint().height() / 2 );
183
/* FIXME: adjust the position of the menu if it goes offscreen,
184
as is done in setFolded(false). */
188
LauncherContextualMenu::hide()
190
/* Fold the menu upon hiding */
196
LauncherContextualMenu::hideWithDelay(int delay)
198
m_hidingDelayTimer.setInterval(delay);
199
m_hidingDelayTimer.start();
203
LauncherContextualMenu::folded() const
209
LauncherContextualMenu::setFolded(int folded)
211
if (folded == m_folded) {
216
/* Remove all actions but the title. */
217
for (int i = actions().size(); i > 0; --i) {
218
QAction* action = actions().at(i - 1);
219
if (action != m_titleAction) {
220
removeAction(action);
221
if (action->parent() == this) {
222
/* Delete the action only if we "own" it,
223
otherwise let its parent take care of it. */
229
int prevWidth = width();
230
int left = x(), top = y();
232
m_launcherItem->createMenuActions();
234
QRect screenGeometry = QApplication::desktop()->screenGeometry(this);
235
if (QApplication::isRightToLeft()) {
236
left -= width() - prevWidth;
238
if (height() <= screenGeometry.height()) {
239
/* Adjust the position of the menu only if it fits entirely on the screen. */
240
int menuBottomEdge = y() + height();
241
int screenBottomEdge = screenGeometry.y() + screenGeometry.height();
242
if (menuBottomEdge > screenBottomEdge) {
243
/* The menu goes offscreen, shift it upwards. */
244
m_arrowY += menuBottomEdge - screenBottomEdge;
245
top = screenBottomEdge - height();
249
if (!transparencyAvailable()) {
250
/* The arrow has moved relatively to the menu. */
257
Q_EMIT foldedChanged(m_folded);
261
LauncherContextualMenu::paintEvent(QPaintEvent* event)
263
QMenu::paintEvent(event);
265
/* Draw the arrow. */
266
QPainter painter(this);
267
painter.setCompositionMode(QPainter::CompositionMode_Source);
269
if (QApplication::isRightToLeft()) {
270
left = width() - m_arrow.width();
272
painter.drawPixmap(left, m_arrowY, m_arrow);
276
LauncherContextualMenu::launcherItem() const
278
return m_launcherItem;
282
LauncherContextualMenu::setLauncherItem(LauncherItem* launcherItem)
284
m_launcherItem = launcherItem;
285
connect(m_titleAction, SIGNAL(triggered()), SLOT(titleTriggered()));
288
void LauncherContextualMenu::titleTriggered()
291
m_launcherItem->activate();
295
LauncherContextualMenu::setVisible(bool value)
297
bool changed = isVisible() != value;
298
// The call to QMenu::setVisible() could probably be skipped if 'changed'
299
// is false, but I'd rather keep the class behavior as close to the default
300
// setVisible() implementation as possible.
301
QMenu::setVisible(value);
303
visibleChanged(value);
308
LauncherContextualMenu::setFocus()
310
/* FIXME: for some reason, invoking QMenu::activateWindow() directly here
311
doesn’t work, the active window remains unchanged. */
312
QTimer::singleShot(1, this, SLOT(activateWindow()));
314
/* Set the first enabled action active. */
315
Q_FOREACH(QAction* action, actions()) {
316
if (action->isEnabled() && !action->isSeparator()) {
317
setActiveAction(action);
325
LauncherContextualMenu::activateWindow()
327
QMenu::activateWindow();
331
LauncherContextualMenu::keyPressEvent(QKeyEvent* event)
333
int key = event->key();
334
if (key == Qt::Key_Left || key == Qt::Key_Escape) {
335
Q_EMIT dismissedByKeyEvent();
341
QMenu::keyPressEvent(event);
342
if (event->isAccepted() && isHidden()) {
343
Q_EMIT dismissedByKeyEvent();
348
LauncherContextualMenu::insertActionBeforeTitle(QAction* action)
350
insertAction(m_titleAction, action);
354
LauncherContextualMenu::insertSeparatorBeforeTitle()
356
return insertSeparator(m_titleAction);
359
#include "launchermenu.moc"
93
if (app == m_application)
101
QDesktopWidget* desktop = QApplication::desktop();
102
const QRect available = desktop->availableGeometry(this);
103
move(available.x(), y + available.y() - sizeHint().height() / 2);
105
/* Set the title after showing so that the width is correctly updated. */
106
m_title->setText(m_application->name());
110
QLauncherContextualMenu::show_menu()
112
bool running = m_application->running();
114
m_keep->setText(running ? "Keep In Launcher" : "Remove From Launcher");
115
m_keep->setCheckable(running);
116
m_keep->setChecked(m_application->sticky());
117
m_keep->setVisible(true);
118
m_quit->setVisible(running);
122
QLauncherContextualMenu::hide(bool force)
124
if (!force && m_keep->isVisible())
126
QDesktopWidget* desktop = QApplication::desktop();
127
const QRect available = desktop->availableGeometry(this);
128
QPoint cursor = QCursor::pos();
129
if (cursor.x() >= available.x())
134
m_application = NULL;
135
m_keep->setVisible(false);
136
m_quit->setVisible(false);
140
QLauncherContextualMenu::leaveEvent(QEvent* event)
142
/* Since our menu is not a modal popup, it doesn’t capture all events, and
143
thus doesn’t know when e.g. the mouse was clicked outside of its window
144
(which should close it).
145
Re-implementing leaveEvent(…) is a cheap workaround: hide the menu when
146
the cursor leaves it. This is not the same behaviour as in unity, but it
148
QDesktopWidget* desktop = QApplication::desktop();
149
const QRect available = desktop->availableGeometry(this);
150
QPoint cursor = QCursor::pos();
151
if (cursor.x() <= available.x())
158
QLauncherContextualMenu::onKeepTriggered()
160
m_application->setSticky(m_keep->isChecked());
165
QLauncherContextualMenu::onQuitTriggered()
167
m_application->close();