2
* Copyright (C) 2010 Canonical, Ltd.
4
* This program is free software; you can redistribute it and/or modify
5
* it under the terms of the GNU General Public License as published by
6
* the Free Software Foundation; version 3.
8
* This program is distributed in the hope that it will be useful,
9
* but WITHOUT ANY WARRANTY; without even the implied warranty of
10
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
* GNU General Public License for more details.
13
* You should have received a copy of the GNU General Public License
14
* along with this program. If not, see <http://www.gnu.org/licenses/>.
17
/* Note regarding the use of wnck: it is critically important that the client
18
type be set to pager because wnck will pass that type over to the window
19
manager through XEvents.
20
Window managers tend to respect orders from pagers to the letter by for
21
example bypassing focus stealing prevention. Compiz does exactly that in
22
src/event.c:handleEvent(…) in the ClientMessage case (line 1702). Metacity
23
has a similar policy in src/core/window.c:window_activate(…) (line 2951).
25
The client type has already been set in Unity2dPlugin::initializeEngine(…),
26
and the corresponding shared library (libunity-2d-private-qml.so) is loaded
27
via QML’s import statement (`import Unity2d 1.0`), so there is no need to
31
#include "launcherapplication.h"
32
#include "launchermenu.h"
33
#include "launcherutility.h"
34
#include "bamf-matcher.h"
35
#include "bamf-indicator.h"
37
#include "dbusmenuimporter.h"
42
#include <unity2dtr.h>
49
#include <QDBusInterface>
51
#include <QDBusServiceWatcher>
53
#include <QFileSystemWatcher>
54
#include <QScopedPointer>
62
const char* SHORTCUT_NICK_PROPERTY = "nick";
64
LauncherApplication::LauncherApplication()
66
, m_desktopFileWatcher(NULL)
68
, m_has_visible_window(false)
69
, m_progress(0), m_progressBarVisible(false)
70
, m_counter(0), m_counterVisible(false)
71
, m_emblem(QString()), m_emblemVisible(false)
72
, m_forceUrgent(false)
73
, m_dynamicQuicklistServiceWatcher(NULL)
75
m_launching_timer.setSingleShot(true);
76
m_launching_timer.setInterval(8000);
77
QObject::connect(&m_launching_timer, SIGNAL(timeout()), this, SLOT(onLaunchingTimeouted()));
80
LauncherApplication::LauncherApplication(const LauncherApplication& other)
82
/* FIXME: a number of members are not copied over */
83
QObject::connect(&m_launching_timer, SIGNAL(timeout()), this, SLOT(onLaunchingTimeouted()));
84
if (other.m_application != NULL) {
85
setBamfApplication(other.m_application);
89
LauncherApplication::~LauncherApplication()
94
LauncherApplication::active() const
96
if (m_application != NULL) {
97
return m_application->active();
104
LauncherApplication::running() const
106
if (m_application != NULL) {
107
return m_application->running();
114
LauncherApplication::windowCount() const
116
if (m_application == NULL) {
120
QScopedPointer<BamfWindowList> windows(m_application->windows());
121
return windows->size();
125
LauncherApplication::urgent() const
131
if (m_application != NULL) {
132
return m_application->urgent();
139
LauncherApplication::beginForceUrgent(int duration)
141
bool wasUrgent = urgent();
142
m_forceUrgent = true;
143
if (wasUrgent != urgent()) {
144
Q_EMIT urgentChanged(urgent());
146
QTimer::singleShot(duration, this, SLOT(endForceUrgent()));
150
LauncherApplication::endForceUrgent()
152
bool wasUrgent = urgent();
153
m_forceUrgent = false;
154
if (wasUrgent != urgent()) {
155
Q_EMIT urgentChanged(urgent());
160
LauncherApplication::sticky() const
166
LauncherApplication::name() const
168
if (sticky() && (m_appInfo != NULL)) {
169
return QString::fromUtf8(g_app_info_get_name(m_appInfo.data()));
172
if (m_application != NULL) {
173
return m_application->name();
176
if (m_appInfo != NULL) {
177
return QString::fromUtf8(g_app_info_get_name(m_appInfo.data()));
180
if (m_snStartupSequence != NULL) {
181
return QString::fromUtf8(sn_startup_sequence_get_name(m_snStartupSequence.data()));
188
LauncherApplication::icon() const
190
if (sticky() && (m_appInfo != NULL)) {
191
GCharPointer ptr(g_icon_to_string(g_app_info_get_icon(m_appInfo.data())));
192
return QString::fromUtf8(ptr.data());
195
if (m_application != NULL) {
196
return m_application->icon();
199
if (m_appInfo != NULL) {
200
GCharPointer ptr(g_icon_to_string(g_app_info_get_icon(m_appInfo.data())));
201
return QString::fromUtf8(ptr.data());
204
if (m_snStartupSequence != NULL) {
205
return QString::fromUtf8(sn_startup_sequence_get_icon_name(m_snStartupSequence.data()));
212
LauncherApplication::application_type() const
214
if (m_application != NULL) {
215
return m_application->application_type();
222
LauncherApplication::desktop_file() const
224
if (m_application != NULL) {
225
return m_application->desktop_file();
228
if (m_appInfo != NULL) {
229
return QString::fromUtf8(g_desktop_app_info_get_filename((GDesktopAppInfo*)m_appInfo.data()));
236
LauncherApplication::executable() const
238
if (m_appInfo != NULL) {
239
return QString::fromUtf8(g_app_info_get_executable(m_appInfo.data()));
242
if (m_snStartupSequence != NULL) {
243
return QString::fromUtf8(sn_startup_sequence_get_binary_name(m_snStartupSequence.data()));
250
LauncherApplication::setSticky(bool sticky)
252
if (sticky == m_sticky) {
257
stickyChanged(sticky);
261
LauncherApplication::setDesktopFile(const QString& desktop_file)
263
QString oldDesktopFile = this->desktop_file();
265
QByteArray byte_array = desktop_file.toUtf8();
266
gchar *file = byte_array.data();
268
if(desktop_file.startsWith("/")) {
269
/* It looks like a full path to a desktop file */
270
m_appInfo.reset((GAppInfo*)g_desktop_app_info_new_from_filename(file));
272
/* It might just be a desktop file name; let GIO look for the actual
273
desktop file for us */
274
/* The docs for g_desktop_app_info_new() says it respects "-" to "/"
275
substitution as per XDG Menu Spec, but it only seems to work for
276
exactly 1 substitution where as Wine programs often require many.
277
Bottom line: We must do some manual trial and error to find desktop
278
files in deeply nested directories.
280
Same workaround is implemented in Unity: plugins/unityshell/src/PlacesView.cpp:731
282
https://bugzilla.gnome.org/show_bug.cgi?id=654566
283
https://bugs.launchpad.net/unity-2d/+bug/794471
287
m_appInfo.reset((GAppInfo*)g_desktop_app_info_new(file));
288
slash_index = byte_array.indexOf("-");
289
if (slash_index == -1) {
292
byte_array.replace(slash_index, 1, "/");
293
file = byte_array.data();
294
} while (m_appInfo.isNull());
297
/* setDesktopFile(…) may be called with the same desktop file, when e.g. the
298
contents of the file have changed. Some properties may have changed. */
299
QString newDesktopFile = this->desktop_file();
300
if (newDesktopFile != oldDesktopFile) {
301
Q_EMIT desktopFileChanged(newDesktopFile);
303
/* Emit the Changed signal on all properties that can depend on m_appInfo
304
m_application's properties take precedence over m_appInfo's
306
if (m_appInfo != NULL) {
307
if (m_application == NULL) {
308
Q_EMIT nameChanged(name());
309
Q_EMIT iconChanged(icon());
311
Q_EMIT executableChanged(executable());
314
/* Update the list of static shortcuts
315
(quicklist entries defined in the desktop file). */
316
m_staticShortcuts.reset(indicator_desktop_shortcuts_new(newDesktopFile.toUtf8().constData(), "Unity"));
318
monitorDesktopFile(newDesktopFile);
322
LauncherApplication::monitorDesktopFile(const QString& path)
324
/* Monitor the desktop file for live changes */
325
if (m_desktopFileWatcher == NULL) {
326
/* FIXME: deleting a QFileSystemWatcher can be quite slow (sometimes
327
around 100ms on a powerful computer) and can provoke visual glitches
328
where the user interface is blocked for a short moment.
330
m_desktopFileWatcher = new QFileSystemWatcher(this);
331
connect(m_desktopFileWatcher, SIGNAL(fileChanged(const QString&)),
332
SLOT(onDesktopFileChanged(const QString&)));
335
/* If the file is already being monitored, we shouldn’t need to do anything.
336
However it seems that in some cases, a change to the file will stop
337
emiting further fileChanged signals, despite the file still being in the
338
list of monitored files. This is the case when the desktop file is being
339
edited in gedit for example. This may be a bug in QT itself.
340
To work around this issue, remove the path and add it again. */
341
if (m_desktopFileWatcher->files().contains(path)) {
342
m_desktopFileWatcher->removePath(path);
345
if (!path.isEmpty()) {
346
m_desktopFileWatcher->addPath(path);
351
LauncherApplication::onDesktopFileChanged(const QString& path)
353
if (m_desktopFileWatcher->files().contains(path) || QFile::exists(path)) {
354
/* The contents of the file have changed. */
355
setDesktopFile(path);
357
/* The desktop file has been deleted.
358
This can happen in a number of cases:
359
- the package it belongs to has been uninstalled
360
- the package it belongs to has been upgraded, in which case it is
361
likely that the desktop file has been removed and a new version of
362
it has been installed in place of the old version
363
- the file has been written to using an editor that first saves to a
364
temporary file and then moves this temporary file to the
365
destination file, which effectively results in the file being
366
temporarily deleted (vi for example does that, whereas gedit
368
In the first case, we want to remove the application from the
369
launcher. In the last two cases, we need to consider that the desktop
370
file’s contents have changed. At this point there is no way to be
371
sure that the file has been permanently removed, so we want to give
372
the application a grace period before checking for real deletion. */
373
QTimer::singleShot(1000, this, SLOT(checkDesktopFileReallyRemoved()));
378
LauncherApplication::checkDesktopFileReallyRemoved()
380
QString path = desktop_file();
381
if (QFile::exists(path)) {
382
/* The desktop file hasn’t really been removed, it was only temporarily
384
setDesktopFile(path);
386
/* The desktop file has really been removed. */
392
LauncherApplication::setBamfApplication(BamfApplication *application)
394
if (application == NULL) {
398
m_application = application;
400
setDesktopFile(application->desktop_file());
403
QObject::connect(application, SIGNAL(ActiveChanged(bool)), this, SIGNAL(activeChanged(bool)));
405
/* FIXME: a bug somewhere makes connecting to the Closed() signal not work even though
406
the emit Closed() in bamf-view.cpp is reached. */
407
/* Connect first the onBamfApplicationClosed slot, then the runningChanged
408
signal, to avoid a race condition when an application is closed.
409
See https://launchpad.net/bugs/634057 */
410
QObject::connect(application, SIGNAL(RunningChanged(bool)), this, SLOT(onBamfApplicationClosed(bool)));
411
QObject::connect(application, SIGNAL(RunningChanged(bool)), this, SIGNAL(runningChanged(bool)));
412
QObject::connect(application, SIGNAL(UrgentChanged(bool)), this, SIGNAL(urgentChanged(bool)));
413
QObject::connect(application, SIGNAL(WindowAdded(BamfWindow*)), this, SLOT(updateHasVisibleWindow()));
414
QObject::connect(application, SIGNAL(WindowRemoved(BamfWindow*)), this, SLOT(updateHasVisibleWindow()));
415
QObject::connect(application, SIGNAL(WindowAdded(BamfWindow*)), this, SLOT(updateWindowCount()));
416
QObject::connect(application, SIGNAL(WindowRemoved(BamfWindow*)), this, SLOT(updateWindowCount()));
417
connect(application, SIGNAL(ChildAdded(BamfView*)), SLOT(slotChildAdded(BamfView*)));
418
connect(application, SIGNAL(ChildRemoved(BamfView*)), SLOT(slotChildRemoved(BamfView*)));
420
connect(application, SIGNAL(WindowAdded(BamfWindow*)), SLOT(onWindowAdded(BamfWindow*)));
422
updateBamfApplicationDependentProperties();
426
LauncherApplication::updateBamfApplicationDependentProperties()
428
activeChanged(active());
429
runningChanged(running());
430
urgentChanged(urgent());
433
applicationTypeChanged(application_type());
434
desktopFileChanged(desktop_file());
435
m_launching_timer.stop();
436
launchingChanged(launching());
437
updateHasVisibleWindow();
439
fetchIndicatorMenus();
443
LauncherApplication::onBamfApplicationClosed(bool running)
448
m_application->disconnect(this);
449
m_application = NULL;
450
updateBamfApplicationDependentProperties();
455
LauncherApplication::setSnStartupSequence(SnStartupSequence* sequence)
457
if (sequence != NULL) {
458
if (!sn_startup_sequence_get_completed(sequence)) {
459
/* 'launching' property becomes true for a few seconds */
460
m_launching_timer.start();
462
m_launching_timer.stop();
464
sn_startup_sequence_ref(sequence);
467
m_snStartupSequence.reset(sequence);
471
executableChanged(executable());
472
launchingChanged(launching());
476
LauncherApplication::setIconGeometry(int x, int y, int width, int height, uint xid)
478
if (m_application == NULL) {
482
QScopedPointer<BamfUintList> xids;
484
xids.reset(m_application->xids());
488
xids.reset(new BamfUintList(list));
490
int size = xids->size();
495
WnckScreen* screen = wnck_screen_get_default();
496
wnck_screen_force_update(screen);
498
for (int i = 0; i < size; ++i) {
499
WnckWindow* window = wnck_window_get(xids->at(i));
500
wnck_window_set_icon_geometry(window, x, y, width, height);
505
LauncherApplication::onWindowAdded(BamfWindow* window)
507
if (window != NULL) {
508
windowAdded(window->xid());
513
LauncherApplication::launching() const
515
return m_launching_timer.isActive();
519
LauncherApplication::updateHasVisibleWindow()
521
bool prev = m_has_visible_window;
523
if (m_application != NULL) {
524
m_has_visible_window = QScopedPointer<BamfUintList>(m_application->xids())->size() > 0;
526
m_has_visible_window = false;
529
if (m_has_visible_window != prev) {
530
hasVisibleWindowChanged(m_has_visible_window);
535
LauncherApplication::updateWindowCount()
537
Q_EMIT windowCountChanged(windowCount());
541
LauncherApplication::has_visible_window() const
543
return m_has_visible_window;
547
LauncherApplication::progress() const
553
LauncherApplication::counter() const
559
LauncherApplication::emblem() const
565
LauncherApplication::progressBarVisible() const
567
return m_progressBarVisible;
571
LauncherApplication::counterVisible() const
573
return m_counterVisible;
577
LauncherApplication::emblemVisible() const
579
return m_emblemVisible;
582
/* Returns the number of window for this application that reside on the
585
LauncherApplication::windowCountOnCurrentWorkspace()
588
WnckWorkspace *current = wnck_screen_get_active_workspace(wnck_screen_get_default());
590
for (int i = 0; i < m_application->windows()->size(); i++) {
591
BamfWindow *window = m_application->windows()->at(i);
592
if (window == NULL) {
596
/* When geting the wnck window, it's possible we get a NULL
597
return value because wnck hasn't updated its internal list yet,
598
so we need to force it once to be sure */
599
WnckWindow *wnck_window = wnck_window_get(window->xid());
600
if (wnck_window == NULL) {
601
wnck_screen_force_update(wnck_screen_get_default());
602
wnck_window = wnck_window_get(window->xid());
603
if (wnck_window == NULL) {
608
WnckWorkspace *workspace = wnck_window_get_workspace(wnck_window);
609
if (workspace == current) {
617
LauncherApplication::activate()
621
} else if (active()) {
622
if (windowCountOnCurrentWorkspace() > 0 && windowCount() > 1) {
623
spread(windowCount() > windowCountOnCurrentWorkspace());
625
} else if (running() && has_visible_window()) {
633
LauncherApplication::launchNewInstance()
639
LauncherApplication::launch()
641
if (m_appInfo == NULL) {
645
GError* error = NULL;
648
g_get_current_time (&timeval);
649
GdkDisplay* display = gdk_display_get_default();
650
GObjectScopedPointer<GdkAppLaunchContext> context(gdk_display_get_app_launch_context(display));
651
/* Using GDK_CURRENT_TIME doesn’t seem to work, launched windows
652
sometimes don’t get focus (see https://launchpad.net/bugs/643616). */
653
gdk_app_launch_context_set_timestamp(context.data(), timeval.tv_sec);
655
g_app_info_launch(m_appInfo.data(), NULL, (GAppLaunchContext*)context.data(), &error);
658
UQ_WARNING << "Failed to launch application:" << error->message;
663
/* 'launching' property becomes true for a few seconds and becomes
664
false as soon as the application is launched */
665
m_launching_timer.start();
666
launchingChanged(true);
672
LauncherApplication::onLaunchingTimeouted()
674
launchingChanged(false);
678
LauncherApplication::close()
680
if (m_application == NULL) {
684
QScopedPointer<BamfUintList> xids(m_application->xids());
685
int size = xids->size();
690
WnckScreen* screen = wnck_screen_get_default();
691
wnck_screen_force_update(screen);
693
for (int i = 0; i < size; ++i) {
694
WnckWindow* window = wnck_window_get(xids->at(i));
695
wnck_window_close(window, CurrentTime);
700
LauncherApplication::show()
702
if (m_application == NULL) {
706
QScopedPointer<BamfWindowList> windows(m_application->windows());
707
int size = windows->size();
712
/* Pick the most important window.
713
The primary criterion to determine the most important window is urgency.
714
The secondary criterion is the last_active timestamp (the last time the
715
window was activated). */
716
BamfWindow* important = windows->at(0);
717
for (int i = 0; i < size; ++i) {
718
BamfWindow* current = windows->at(i);
719
if (current->urgent() && !important->urgent()) {
721
} else if (current->urgent() || !important->urgent()) {
722
if (current->last_active() > important->last_active()) {
728
WnckScreen* screen = wnck_screen_get_default();
729
wnck_screen_force_update(screen);
731
WnckWindow* window = wnck_window_get(important->xid());
732
LauncherUtility::showWindow(window);
736
LauncherApplication::spread(bool showAllWorkspaces)
738
QDBusInterface compiz("org.freedesktop.compiz",
739
"/org/freedesktop/compiz/scale/screen0/initiate_all_key",
740
"org.freedesktop.compiz");
742
if (compiz.isValid()) {
743
Qt::HANDLE root = QX11Info::appRootWindow();
744
BamfUintList* xids = m_application->xids();
745
QStringList fragments;
746
for (int i = 0; i < xids->size(); i++) {
747
uint xid = xids->at(i);
748
fragments.append("xid=" + QString::number(xid));
751
compiz.asyncCall("activate", "root", static_cast<int>(root), "match", fragments.join(" | "));
753
QDBusInterface spread("com.canonical.Unity2d.Spread", "/Spread",
754
"com.canonical.Unity2d.Spread");
755
QDBusReply<bool> isShown = spread.call("IsShown");
756
if (isShown.isValid()) {
757
if (isShown.value() == true) {
758
spread.asyncCall("FilterByApplication", m_application->desktop_file());
760
if (showAllWorkspaces) {
761
spread.asyncCall("ShowAllWorkspaces", m_application->desktop_file());
763
spread.asyncCall("ShowCurrentWorkspace", m_application->desktop_file());
767
UQ_WARNING << "Failed to get property IsShown on com.canonical.Unity2d.Spread";
773
LauncherApplication::slotChildAdded(BamfView* child)
775
BamfIndicator* indicator = qobject_cast<BamfIndicator*>(child);
776
if (indicator != NULL) {
777
QString path = indicator->dbus_menu_path();
778
if (!m_indicatorMenus.contains(path)) {
779
DBusMenuImporter* importer = new DBusMenuImporter(indicator->address(), path, this);
780
connect(importer, SIGNAL(menuUpdated()), SLOT(onIndicatorMenuUpdated()));
781
m_indicatorMenus[path] = importer;
787
LauncherApplication::slotChildRemoved(BamfView* child)
789
BamfIndicator* indicator = qobject_cast<BamfIndicator*>(child);
790
if (indicator != NULL) {
791
QString path = indicator->dbus_menu_path();
792
if (m_indicatorMenus.contains(path)) {
793
m_indicatorMenus.take(path)->deleteLater();
799
LauncherApplication::fetchIndicatorMenus()
801
Q_FOREACH(QString path, m_indicatorMenus.keys()) {
802
m_indicatorMenus.take(path)->deleteLater();
805
if (m_application != NULL) {
806
QScopedPointer<BamfViewList> children(m_application->children());
807
for (int i = 0; i < children->size(); ++i) {
808
slotChildAdded(children->at(i));
814
LauncherApplication::createMenuActions()
816
if (m_application != NULL && !m_indicatorMenus.isEmpty()) {
817
/* Request indicator menus to be updated: this is asynchronous
818
and the corresponding actions are added to the menu in
819
SLOT(onIndicatorMenuUpdated()).
820
Static menu actions are appended after all indicator menus
822
m_indicatorMenusReady = 0;
823
Q_FOREACH(DBusMenuImporter* importer, m_indicatorMenus) {
824
importer->updateMenu();
827
createDynamicMenuActions();
828
createStaticMenuActions();
833
LauncherApplication::createDynamicMenuActions()
835
if (!m_dynamicQuicklistImporter.isNull()) {
836
/* FIXME: the menu is only partially updated while visible: stale
837
actions will correctly be removed from the menu, but new actions
838
will not be added until the menu is closed and opened again.
839
This is an acceptable limitation for now. */
840
QList<QAction*> actions = m_dynamicQuicklistImporter->menu()->actions();
841
Q_FOREACH(QAction* action, actions) {
842
if (action->isSeparator()) {
843
m_menu->insertSeparatorBeforeTitle();
845
connect(action, SIGNAL(triggered()), m_menu, SLOT(hide()));
846
m_menu->insertActionBeforeTitle(action);
853
LauncherApplication::createStaticMenuActions()
855
/* Custom menu actions from the desktop file. */
856
if (!m_staticShortcuts.isNull()) {
857
const gchar** nicks = indicator_desktop_shortcuts_get_nicks(m_staticShortcuts.data());
860
while (((gpointer*) nicks)[i]) {
861
const gchar* nick = nicks[i];
862
QAction* action = new QAction(m_menu);
863
action->setText(QString::fromUtf8(indicator_desktop_shortcuts_nick_get_name(m_staticShortcuts.data(), nick)));
864
action->setProperty(SHORTCUT_NICK_PROPERTY, QVariant(nick));
865
m_menu->insertActionBeforeTitle(action);
866
connect(action, SIGNAL(triggered()), SLOT(onStaticShortcutTriggered()));
871
m_menu->insertSeparatorBeforeTitle();
873
QList<QAction*> actions;
874
bool is_running = running();
876
/* Only applications with a corresponding desktop file can be kept in the launcher */
877
if (QFile::exists(desktop_file())) {
878
QAction* keep = new QAction(m_menu);
879
keep->setCheckable(is_running);
880
keep->setChecked(sticky());
881
keep->setText(is_running ? u2dTr("Keep In Launcher") : u2dTr("Remove From Launcher"));
882
actions.append(keep);
883
QObject::connect(keep, SIGNAL(triggered()), this, SLOT(onKeepTriggered()));
887
QAction* quit = new QAction(m_menu);
888
quit->setText(u2dTr("Quit"));
889
actions.append(quit);
890
QObject::connect(quit, SIGNAL(triggered()), this, SLOT(onQuitTriggered()));
893
/* Filter out duplicate actions. This typically happens with indicator
894
menus that contain a "Quit" action: we don’t want two "Quit" actions in
896
Q_FOREACH(QAction* pending, actions) {
897
bool duplicate = false;
898
Q_FOREACH(QAction* action, m_menu->actions()) {
899
/* The filtering is done on the text of the action. This will
900
obviously break if for example only one of the two actions is
901
localized ("Quit" != "Quitter"), but we don’t have a better way
902
to identify duplicate actions. */
903
if (pending->text() == action->text()) {
910
m_menu->addAction(pending);
918
LauncherApplication::onIndicatorMenuUpdated()
920
if (!m_menu->isVisible()) {
924
DBusMenuImporter* importer = static_cast<DBusMenuImporter*>(sender());
925
QList<QAction*> actions = importer->menu()->actions();
926
Q_FOREACH(QAction* action, actions) {
927
if (action->isSeparator()) {
928
m_menu->insertSeparatorBeforeTitle();
930
connect(action, SIGNAL(triggered()), m_menu, SLOT(hide()));
931
m_menu->insertActionBeforeTitle(action);
935
if (++m_indicatorMenusReady == m_indicatorMenus.size()) {
936
/* All indicator menus have been updated. */
937
createDynamicMenuActions();
938
createStaticMenuActions();
943
LauncherApplication::onStaticShortcutTriggered()
945
QAction* action = static_cast<QAction*>(sender());
946
QString nick = action->property(SHORTCUT_NICK_PROPERTY).toString();
948
indicator_desktop_shortcuts_nick_exec(m_staticShortcuts.data(), nick.toUtf8().constData());
952
LauncherApplication::onKeepTriggered()
954
QAction* keep = static_cast<QAction*>(sender());
955
bool sticky = keep->isChecked();
961
LauncherApplication::onQuitTriggered()
968
bool LauncherApplication::updateOverlayState(QMap<QString, QVariant> properties,
969
QString propertyName, T* member)
971
if (properties.contains(propertyName)) {
972
T value = properties.value(propertyName).value<T>();
973
if (value != *member) {
982
LauncherApplication::updateOverlaysState(const QString& sender, QMap<QString, QVariant> properties)
984
if (updateOverlayState(properties, "progress", &m_progress)) {
985
Q_EMIT progressChanged(m_progress);
987
if (updateOverlayState(properties, "progress-visible", &m_progressBarVisible)) {
988
Q_EMIT progressBarVisibleChanged(m_progressBarVisible);
990
if (updateOverlayState(properties, "count", &m_counter)) {
991
Q_EMIT counterChanged(m_counter);
993
if (updateOverlayState(properties, "count-visible", &m_counterVisible)) {
994
Q_EMIT counterVisibleChanged(m_counterVisible);
996
if (updateOverlayState(properties, "emblem", &m_emblem)) {
997
Q_EMIT emblemChanged(m_emblem);
999
if (updateOverlayState(properties, "emblem-visible", &m_emblemVisible)) {
1000
Q_EMIT emblemVisibleChanged(m_emblemVisible);
1002
if (updateOverlayState(properties, "quicklist", &m_dynamicQuicklistPath)) {
1003
setDynamicQuicklistImporter(sender);
1008
LauncherApplication::setDynamicQuicklistImporter(const QString& service)
1010
if (m_dynamicQuicklistPath.isEmpty() || service.isEmpty()) {
1011
m_dynamicQuicklistImporter.reset();
1013
m_dynamicQuicklistImporter.reset(new DBusMenuImporter(service, m_dynamicQuicklistPath));
1014
m_dynamicQuicklistImporter->updateMenu();
1015
if (m_dynamicQuicklistServiceWatcher == NULL) {
1016
m_dynamicQuicklistServiceWatcher = new QDBusServiceWatcher(this);
1017
m_dynamicQuicklistServiceWatcher->setConnection(QDBusConnection::sessionBus());
1018
connect(m_dynamicQuicklistServiceWatcher,
1019
SIGNAL(serviceOwnerChanged(const QString&, const QString&, const QString&)),
1020
SLOT(dynamicQuicklistImporterServiceOwnerChanged(const QString&, const QString&, const QString&)));
1022
m_dynamicQuicklistServiceWatcher->addWatchedService(service);
1027
LauncherApplication::dynamicQuicklistImporterServiceOwnerChanged(const QString& serviceName, const QString& oldOwner, const QString& newOwner)
1029
m_dynamicQuicklistServiceWatcher->removeWatchedService(oldOwner);
1030
setDynamicQuicklistImporter(newOwner);