~sil2100/unity-2d/precise-security

« back to all changes in this revision

Viewing changes to launcher/UnityApplications/launchermenu.cpp

  • Committer: Aurelien Gateau
  • Date: 2010-11-10 08:57:29 UTC
  • mto: This revision was merged to the branch mainline in revision 284.
  • Revision ID: aurelien.gateau@canonical.com-20101110085729-fl1ye7impkqhm0w6
Added a section about const correct-ness

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
18
 */
19
19
 
 
20
#include "config.h"
20
21
#include "launchermenu.h"
21
 
#include "launcheritem.h"
22
22
 
23
 
#include <QAction>
24
23
#include <QFile>
25
24
#include <QApplication>
26
 
#include <QResizeEvent>
27
 
#include <QBitmap>
28
 
#include <QX11Info>
29
25
#include <QDesktopWidget>
30
 
#include <QPainter>
 
26
#include <QPoint>
 
27
#include <QCursor>
31
28
 
32
 
LauncherContextualMenu::LauncherContextualMenu():
33
 
    QMenu(0), m_folded(true), m_launcherItem(NULL), m_titleAction(NULL)
 
29
QLauncherContextualMenu::QLauncherContextualMenu():
 
30
    QMenu(0), m_application(NULL)
34
31
{
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()));
38
 
 
39
32
    /* The tooltip/menu shouldn’t be modal. */
40
33
    setWindowFlags(Qt::ToolTip);
41
34
 
42
 
    /* The tooltip/menu should not move when switching workspaces. */
43
 
    setAttribute(Qt::WA_X11NetWmWindowTypeDock);
44
 
 
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
50
 
    */
51
 
    if (transparencyAvailable()) {
52
 
        setAttribute(Qt::WA_TranslucentBackground);
53
 
    }
54
 
 
55
35
    /* Custom appearance. */
56
36
    loadCSS();
57
37
 
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");
63
 
        } else {
64
 
            m_arrow.load(":/launchermenu/arrow_rtl.png");
65
 
        }
66
 
    } else {
67
 
        if (QApplication::isLeftToRight()) {
68
 
            m_arrow.load(":/launchermenu/arrow_no_transparency.png");
69
 
        } else {
70
 
            m_arrow.load(":/launchermenu/arrow_no_transparency_rtl.png");
71
 
        }
72
 
    }
73
 
 
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);
 
40
    addAction(m_title);
 
41
 
 
42
    addSeparator();
 
43
 
 
44
    m_keep = new QAction(this);
 
45
    addAction(m_keep);
 
46
    m_keep->setVisible(false);
 
47
    QObject::connect(m_keep, SIGNAL(triggered()), this, SLOT(onKeepTriggered()));
 
48
 
 
49
    m_quit = new QAction(this);
 
50
    m_quit->setText("Quit");
 
51
    m_quit->setVisible(false);
 
52
    addAction(m_quit);
 
53
    QObject::connect(m_quit, SIGNAL(triggered()), this, SLOT(onQuitTriggered()));
77
54
}
78
55
 
79
 
LauncherContextualMenu::~LauncherContextualMenu()
 
56
QLauncherContextualMenu::~QLauncherContextualMenu()
80
57
{
81
58
}
82
59
 
83
60
void
84
 
LauncherContextualMenu::loadCSS()
 
61
QLauncherContextualMenu::loadCSS()
85
62
{
86
 
    QString cssFilePath = ":/launchermenu/launchermenu.css";
87
 
    if (QApplication::isRightToLeft()) {
88
 
        cssFilePath = ":/launchermenu/launchermenu_rtl.css";
89
 
    }
 
63
    /* FIXME: surely there must be a cleaner way to do that in Qt… */
 
64
    QString path;
 
65
    if (QCoreApplication::applicationDirPath() == INSTALL_PREFIX "/bin")
 
66
    {
 
67
        /* Running installed */
 
68
        path = INSTALL_PREFIX "/" UNITY_QT_DIR "/launcher/";
 
69
    }
 
70
    else
 
71
    {
 
72
        /* Uninstalled */
 
73
        path = "UnityApplications/";
 
74
    }
 
75
    path += "launchermenu.css";
90
76
 
91
 
    QFile file(cssFilePath);
 
77
    QFile file(path);
92
78
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
93
79
        return;
94
80
 
97
83
    setStyleSheet(css);
98
84
}
99
85
 
100
 
QString
101
 
LauncherContextualMenu::title() const
102
 
{
103
 
    return m_title;
104
 
}
105
 
 
106
 
void
107
 
LauncherContextualMenu::setTitle(const QString& title)
108
 
{
109
 
    setAccessibleName(title);
110
 
    m_title = title;
111
 
    /* Escaping ampersands so that they are not considered as keyboard
112
 
       accelerators. */
113
 
    m_titleAction->setText(m_title.replace("&", "&&"));
114
 
    Q_EMIT titleChanged(m_title);
115
 
}
116
 
 
117
 
void
118
 
LauncherContextualMenu::updateMask()
119
 
{
120
 
    QPixmap pixmap(size());
121
 
    render(&pixmap, QPoint(), QRegion(),
122
 
           QWidget::DrawWindowBackground | QWidget::DrawChildren | QWidget::IgnoreMask);
123
 
    setMask(pixmap.createMaskFromColor("red"));
124
 
}
125
 
 
126
 
void
127
 
LauncherContextualMenu::resizeEvent(QResizeEvent* event)
128
 
{
129
 
    QMenu::resizeEvent(event);
130
 
    /* If transparent windows are not available use the XShape extension */
131
 
    if (!transparencyAvailable()) {
132
 
        updateMask();
133
 
    }
134
 
}
135
 
 
136
 
bool
137
 
LauncherContextualMenu::transparencyAvailable() const
138
 
{
139
 
    return QX11Info::isCompositingManagerRunning();
140
 
}
141
 
 
142
 
void
143
 
LauncherContextualMenu::leaveEvent(QEvent* event)
144
 
{
145
 
    /* Prepare for hiding the menu when the mouse leaves it */
146
 
    m_hidingDelayTimer.start();
147
 
    QMenu::leaveEvent(event);
148
 
}
149
 
 
150
 
void
151
 
LauncherContextualMenu::enterEvent(QEvent* event)
152
 
{
153
 
    /* Always keep the menu visible when the mouse hovers it */
154
 
    m_hidingDelayTimer.stop();
155
 
    QMenu::enterEvent(event);
156
 
}
157
 
 
158
 
void
159
 
LauncherContextualMenu::focusOutEvent(QFocusEvent* event)
160
 
{
161
 
    /* Hide menu if mouse click outside widget */
162
 
    m_hidingDelayTimer.stop();
163
 
    hide();
164
 
}
165
 
 
166
 
void
167
 
LauncherContextualMenu::show(int x, int y)
168
 
{
169
 
    m_hidingDelayTimer.stop();
 
86
void
 
87
QLauncherContextualMenu::show(int y, const QVariant& application)
 
88
{
 
89
    QLauncherApplication* app = (QLauncherApplication*) application.value<QObject*>();
170
90
 
171
91
    if (isVisible())
172
 
        return;
173
 
 
174
 
    m_arrowY = 6;
175
 
 
176
 
    if (QApplication::isRightToLeft()) {
177
 
        x = QApplication::desktop()->width() - x - sizeHint().width();
178
 
    }
179
 
 
180
 
    move(x, y - sizeHint().height() / 2 );
181
 
    QMenu::show();
182
 
 
183
 
    /* FIXME: adjust the position of the menu if it goes offscreen,
184
 
              as is done in setFolded(false). */
185
 
}
186
 
 
187
 
void
188
 
LauncherContextualMenu::hide()
189
 
{
190
 
    /* Fold the menu upon hiding */
191
 
    QMenu::hide();
192
 
    setFolded(true);
193
 
}
194
 
 
195
 
void
196
 
LauncherContextualMenu::hideWithDelay(int delay)
197
 
{
198
 
    m_hidingDelayTimer.setInterval(delay);
199
 
    m_hidingDelayTimer.start();
200
 
}
201
 
 
202
 
bool
203
 
LauncherContextualMenu::folded() const
204
 
{
205
 
    return m_folded;
206
 
}
207
 
 
208
 
void
209
 
LauncherContextualMenu::setFolded(int folded)
210
 
{
211
 
    if (folded == m_folded) {
212
 
        return;
213
 
    }
214
 
 
215
 
    if (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. */
224
 
                    delete action;
225
 
                }
226
 
            }
227
 
        }
228
 
    } else {
229
 
        int prevWidth = width();
230
 
        int left = x(), top = y();
231
 
        addSeparator();
232
 
        m_launcherItem->createMenuActions();
233
 
 
234
 
        QRect screenGeometry = QApplication::desktop()->screenGeometry(this);
235
 
        if (QApplication::isRightToLeft()) {
236
 
            left -= width() - prevWidth;
237
 
        }
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();
246
 
            }
247
 
        }
248
 
        move(left, top);
249
 
        if (!transparencyAvailable()) {
250
 
            /* The arrow has moved relatively to the menu. */
251
 
            updateMask();
252
 
        }
253
 
    }
254
 
 
255
 
    m_folded = folded;
256
 
 
257
 
    Q_EMIT foldedChanged(m_folded);
258
 
}
259
 
 
260
 
void
261
 
LauncherContextualMenu::paintEvent(QPaintEvent* event)
262
 
{
263
 
    QMenu::paintEvent(event);
264
 
 
265
 
    /* Draw the arrow. */
266
 
    QPainter painter(this);
267
 
    painter.setCompositionMode(QPainter::CompositionMode_Source);
268
 
    int left = 0;
269
 
    if (QApplication::isRightToLeft()) {
270
 
        left = width() - m_arrow.width();
271
 
    }
272
 
    painter.drawPixmap(left, m_arrowY, m_arrow);
273
 
}
274
 
 
275
 
LauncherItem*
276
 
LauncherContextualMenu::launcherItem() const
277
 
{
278
 
    return m_launcherItem;
279
 
}
280
 
 
281
 
void
282
 
LauncherContextualMenu::setLauncherItem(LauncherItem* launcherItem)
283
 
{
284
 
    m_launcherItem = launcherItem;
285
 
    connect(m_titleAction, SIGNAL(triggered()), SLOT(titleTriggered()));
286
 
}
287
 
 
288
 
void LauncherContextualMenu::titleTriggered()
289
 
{
290
 
    hide();
291
 
    m_launcherItem->activate();
292
 
}
293
 
 
294
 
void
295
 
LauncherContextualMenu::setVisible(bool value)
296
 
{
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);
302
 
    if (changed) {
303
 
        visibleChanged(value);
304
 
    }
305
 
}
306
 
 
307
 
void
308
 
LauncherContextualMenu::setFocus()
309
 
{
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()));
313
 
 
314
 
    /* Set the first enabled action active. */
315
 
    Q_FOREACH(QAction* action, actions()) {
316
 
        if (action->isEnabled() && !action->isSeparator()) {
317
 
            setActiveAction(action);
318
 
            break;
319
 
        }
320
 
    }
321
 
    QMenu::setFocus();
322
 
}
323
 
 
324
 
void
325
 
LauncherContextualMenu::activateWindow()
326
 
{
327
 
    QMenu::activateWindow();
328
 
}
329
 
 
330
 
void
331
 
LauncherContextualMenu::keyPressEvent(QKeyEvent* event)
332
 
{
333
 
    int key = event->key();
334
 
    if (key == Qt::Key_Left || key == Qt::Key_Escape) {
335
 
        Q_EMIT dismissedByKeyEvent();
336
 
        hide();
337
 
        event->accept();
338
 
        return;
339
 
    }
340
 
 
341
 
    QMenu::keyPressEvent(event);
342
 
    if (event->isAccepted() && isHidden()) {
343
 
        Q_EMIT dismissedByKeyEvent();
344
 
    }
345
 
}
346
 
 
347
 
void
348
 
LauncherContextualMenu::insertActionBeforeTitle(QAction* action)
349
 
{
350
 
    insertAction(m_titleAction, action);
351
 
}
352
 
 
353
 
QAction*
354
 
LauncherContextualMenu::insertSeparatorBeforeTitle()
355
 
{
356
 
    return insertSeparator(m_titleAction);
357
 
}
358
 
 
359
 
#include "launchermenu.moc"
 
92
    {
 
93
        if (app == m_application)
 
94
            return;
 
95
        else
 
96
            hide();
 
97
    }
 
98
 
 
99
    m_application = app;
 
100
 
 
101
    QDesktopWidget* desktop = QApplication::desktop();
 
102
    const QRect available = desktop->availableGeometry(this);
 
103
    move(available.x(), y + available.y() - sizeHint().height() / 2);
 
104
    QWidget::show();
 
105
    /* Set the title after showing so that the width is correctly updated. */
 
106
    m_title->setText(m_application->name());
 
107
}
 
108
 
 
109
void
 
110
QLauncherContextualMenu::show_menu()
 
111
{
 
112
    bool running = m_application->running();
 
113
 
 
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);
 
119
}
 
120
 
 
121
void
 
122
QLauncherContextualMenu::hide(bool force)
 
123
{
 
124
    if (!force && m_keep->isVisible())
 
125
    {
 
126
        QDesktopWidget* desktop = QApplication::desktop();
 
127
        const QRect available = desktop->availableGeometry(this);
 
128
        QPoint cursor = QCursor::pos();
 
129
        if (cursor.x() >= available.x())
 
130
            return;
 
131
    }
 
132
 
 
133
    QWidget::hide();
 
134
    m_application = NULL;
 
135
    m_keep->setVisible(false);
 
136
    m_quit->setVisible(false);
 
137
}
 
138
 
 
139
void
 
140
QLauncherContextualMenu::leaveEvent(QEvent* event)
 
141
{
 
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
 
147
       will do for now… */
 
148
    QDesktopWidget* desktop = QApplication::desktop();
 
149
    const QRect available = desktop->availableGeometry(this);
 
150
    QPoint cursor = QCursor::pos();
 
151
    if (cursor.x() <= available.x())
 
152
        return;
 
153
 
 
154
    hide(true);
 
155
}
 
156
 
 
157
void
 
158
QLauncherContextualMenu::onKeepTriggered()
 
159
{
 
160
    m_application->setSticky(m_keep->isChecked());
 
161
    hide(true);
 
162
}
 
163
 
 
164
void
 
165
QLauncherContextualMenu::onQuitTriggered()
 
166
{
 
167
    m_application->close();
 
168
    hide(true);
 
169
}
 
170