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>
63
const char* SHORTCUT_NICK_PROPERTY = "nick";
65
LauncherApplication::LauncherApplication()
67
, m_desktopFileWatcher(NULL)
69
, m_has_visible_window(false)
70
, m_progress(0), m_progressBarVisible(false)
71
, m_counter(0), m_counterVisible(false)
72
, m_emblem(QString()), m_emblemVisible(false)
73
, m_forceUrgent(false)
74
, m_dynamicQuicklistServiceWatcher(NULL)
76
m_launching_timer.setSingleShot(true);
77
m_launching_timer.setInterval(8000);
78
QObject::connect(&m_launching_timer, SIGNAL(timeout()), this, SLOT(onLaunchingTimeouted()));
81
LauncherApplication::LauncherApplication(const LauncherApplication& other)
83
/* FIXME: a number of members are not copied over */
84
QObject::connect(&m_launching_timer, SIGNAL(timeout()), this, SLOT(onLaunchingTimeouted()));
85
if (other.m_application != NULL) {
86
setBamfApplication(other.m_application);
90
LauncherApplication::~LauncherApplication()
95
LauncherApplication::active() const
97
if (m_application != NULL) {
98
return m_application->active();
105
LauncherApplication::running() const
107
if (m_application != NULL) {
108
return m_application->running();
115
LauncherApplication::windowCount() const
117
if (m_application == NULL) {
121
QScopedPointer<BamfWindowList> windows(m_application->windows());
122
return windows->size();
126
LauncherApplication::urgent() const
132
if (m_application != NULL) {
133
return m_application->urgent();
140
LauncherApplication::beginForceUrgent(int duration)
142
bool wasUrgent = urgent();
143
m_forceUrgent = true;
144
if (wasUrgent != urgent()) {
145
Q_EMIT urgentChanged(urgent());
147
QTimer::singleShot(duration, this, SLOT(endForceUrgent()));
151
LauncherApplication::endForceUrgent()
153
bool wasUrgent = urgent();
154
m_forceUrgent = false;
155
if (wasUrgent != urgent()) {
156
Q_EMIT urgentChanged(urgent());
161
LauncherApplication::sticky() const
167
LauncherApplication::name() const
169
if (sticky() && (m_appInfo != NULL)) {
170
return QString::fromUtf8(g_app_info_get_name(m_appInfo.data()));
173
if (m_application != NULL) {
174
return m_application->name();
177
if (m_appInfo != NULL) {
178
return QString::fromUtf8(g_app_info_get_name(m_appInfo.data()));
181
if (m_snStartupSequence != NULL) {
182
return QString::fromUtf8(sn_startup_sequence_get_name(m_snStartupSequence.data()));
189
LauncherApplication::icon() const
191
if (sticky() && (m_appInfo != NULL)) {
192
GCharPointer ptr(g_icon_to_string(g_app_info_get_icon(m_appInfo.data())));
193
return QString::fromUtf8(ptr.data());
196
if (m_application != NULL) {
197
return m_application->icon();
200
if (m_appInfo != NULL) {
201
GCharPointer ptr(g_icon_to_string(g_app_info_get_icon(m_appInfo.data())));
202
return QString::fromUtf8(ptr.data());
205
if (m_snStartupSequence != NULL) {
206
return QString::fromUtf8(sn_startup_sequence_get_icon_name(m_snStartupSequence.data()));
213
LauncherApplication::application_type() const
215
if (m_application != NULL) {
216
return m_application->application_type();
223
LauncherApplication::desktop_file() const
225
if (m_application != NULL) {
226
return m_application->desktop_file();
229
if (m_appInfo != NULL) {
230
return QString::fromUtf8(g_desktop_app_info_get_filename((GDesktopAppInfo*)m_appInfo.data()));
237
LauncherApplication::executable() const
239
if (m_appInfo != NULL) {
240
return QString::fromUtf8(g_app_info_get_executable(m_appInfo.data()));
243
if (m_snStartupSequence != NULL) {
244
return QString::fromUtf8(sn_startup_sequence_get_binary_name(m_snStartupSequence.data()));
251
LauncherApplication::setSticky(bool sticky)
253
if (sticky == m_sticky) {
258
stickyChanged(sticky);
262
LauncherApplication::setDesktopFile(const QString& desktop_file)
264
QString oldDesktopFile = this->desktop_file();
266
QByteArray byte_array = desktop_file.toUtf8();
267
gchar *file = byte_array.data();
269
if(desktop_file.startsWith("/")) {
270
/* It looks like a full path to a desktop file */
271
m_appInfo.reset((GAppInfo*)g_desktop_app_info_new_from_filename(file));
273
/* It might just be a desktop file name; let GIO look for the actual
274
desktop file for us */
275
/* The docs for g_desktop_app_info_new() says it respects "-" to "/"
276
substitution as per XDG Menu Spec, but it only seems to work for
277
exactly 1 substitution where as Wine programs often require many.
278
Bottom line: We must do some manual trial and error to find desktop
279
files in deeply nested directories.
281
Same workaround is implemented in Unity: plugins/unityshell/src/PlacesView.cpp:731
283
https://bugzilla.gnome.org/show_bug.cgi?id=654566
284
https://bugs.launchpad.net/unity-2d/+bug/794471
288
m_appInfo.reset((GAppInfo*)g_desktop_app_info_new(file));
289
slash_index = byte_array.indexOf("-");
290
if (slash_index == -1) {
293
byte_array.replace(slash_index, 1, "/");
294
file = byte_array.data();
295
} while (m_appInfo.isNull());
298
/* setDesktopFile(…) may be called with the same desktop file, when e.g. the
299
contents of the file have changed. Some properties may have changed. */
300
QString newDesktopFile = this->desktop_file();
301
if (newDesktopFile != oldDesktopFile) {
302
Q_EMIT desktopFileChanged(newDesktopFile);
304
/* Emit the Changed signal on all properties that can depend on m_appInfo
305
m_application's properties take precedence over m_appInfo's
307
if (m_appInfo != NULL) {
308
if (m_application == NULL) {
309
Q_EMIT nameChanged(name());
310
Q_EMIT iconChanged(icon());
312
Q_EMIT executableChanged(executable());
315
/* Update the list of static shortcuts
316
(quicklist entries defined in the desktop file). */
317
m_staticShortcuts.reset(indicator_desktop_shortcuts_new(newDesktopFile.toUtf8().constData(), "Unity"));
319
monitorDesktopFile(newDesktopFile);
323
LauncherApplication::monitorDesktopFile(const QString& path)
325
/* Monitor the desktop file for live changes */
326
if (m_desktopFileWatcher == NULL) {
327
/* FIXME: deleting a QFileSystemWatcher can be quite slow (sometimes
328
around 100ms on a powerful computer) and can provoke visual glitches
329
where the user interface is blocked for a short moment.
331
m_desktopFileWatcher = new QFileSystemWatcher(this);
332
connect(m_desktopFileWatcher, SIGNAL(fileChanged(const QString&)),
333
SLOT(onDesktopFileChanged(const QString&)));
336
/* If the file is already being monitored, we shouldn’t need to do anything.
337
However it seems that in some cases, a change to the file will stop
338
emiting further fileChanged signals, despite the file still being in the
339
list of monitored files. This is the case when the desktop file is being
340
edited in gedit for example. This may be a bug in QT itself.
341
To work around this issue, remove the path and add it again. */
342
if (m_desktopFileWatcher->files().contains(path)) {
343
m_desktopFileWatcher->removePath(path);
346
if (!path.isEmpty()) {
347
m_desktopFileWatcher->addPath(path);
352
LauncherApplication::onDesktopFileChanged(const QString& path)
354
if (m_desktopFileWatcher->files().contains(path) || QFile::exists(path)) {
355
/* The contents of the file have changed. */
356
setDesktopFile(path);
358
/* The desktop file has been deleted.
359
This can happen in a number of cases:
360
- the package it belongs to has been uninstalled
361
- the package it belongs to has been upgraded, in which case it is
362
likely that the desktop file has been removed and a new version of
363
it has been installed in place of the old version
364
- the file has been written to using an editor that first saves to a
365
temporary file and then moves this temporary file to the
366
destination file, which effectively results in the file being
367
temporarily deleted (vi for example does that, whereas gedit
369
In the first case, we want to remove the application from the
370
launcher. In the last two cases, we need to consider that the desktop
371
file’s contents have changed. At this point there is no way to be
372
sure that the file has been permanently removed, so we want to give
373
the application a grace period before checking for real deletion. */
374
QTimer::singleShot(1000, this, SLOT(checkDesktopFileReallyRemoved()));
379
LauncherApplication::checkDesktopFileReallyRemoved()
381
QString path = desktop_file();
382
if (QFile::exists(path)) {
383
/* The desktop file hasn’t really been removed, it was only temporarily
385
setDesktopFile(path);
387
/* The desktop file has really been removed. */
393
LauncherApplication::setBamfApplication(BamfApplication *application)
395
if (application == NULL) {
399
m_application = application;
401
setDesktopFile(application->desktop_file());
404
QObject::connect(application, SIGNAL(ActiveChanged(bool)), this, SIGNAL(activeChanged(bool)));
406
QObject::connect(application, SIGNAL(RunningChanged(bool)), this, SLOT(updateCounterVisible()));
407
/* FIXME: a bug somewhere makes connecting to the Closed() signal not work even though
408
the emit Closed() in bamf-view.cpp is reached. */
409
/* Connect first the onBamfApplicationClosed slot, then the runningChanged
410
signal, to avoid a race condition when an application is closed.
411
See https://launchpad.net/bugs/634057 */
412
QObject::connect(application, SIGNAL(RunningChanged(bool)), this, SLOT(onBamfApplicationClosed(bool)));
413
QObject::connect(application, SIGNAL(RunningChanged(bool)), this, SIGNAL(runningChanged(bool)));
414
QObject::connect(application, SIGNAL(UrgentChanged(bool)), this, SIGNAL(urgentChanged(bool)));
415
QObject::connect(application, SIGNAL(WindowAdded(BamfWindow*)), this, SLOT(updateHasVisibleWindow()));
416
QObject::connect(application, SIGNAL(WindowRemoved(BamfWindow*)), this, SLOT(updateHasVisibleWindow()));
417
QObject::connect(application, SIGNAL(WindowAdded(BamfWindow*)), this, SLOT(updateWindowCount()));
418
QObject::connect(application, SIGNAL(WindowRemoved(BamfWindow*)), this, SLOT(updateWindowCount()));
419
connect(application, SIGNAL(ChildAdded(BamfView*)), SLOT(slotChildAdded(BamfView*)));
420
connect(application, SIGNAL(ChildRemoved(BamfView*)), SLOT(slotChildRemoved(BamfView*)));
422
connect(application, SIGNAL(WindowAdded(BamfWindow*)), SLOT(onWindowAdded(BamfWindow*)));
424
updateBamfApplicationDependentProperties();
425
updateCounterVisible();
429
LauncherApplication::updateBamfApplicationDependentProperties()
431
activeChanged(active());
432
runningChanged(running());
433
urgentChanged(urgent());
436
applicationTypeChanged(application_type());
437
desktopFileChanged(desktop_file());
438
m_launching_timer.stop();
439
launchingChanged(launching());
440
updateHasVisibleWindow();
442
fetchIndicatorMenus();
446
LauncherApplication::onBamfApplicationClosed(bool running)
451
m_application->disconnect(this);
452
m_application = NULL;
453
updateBamfApplicationDependentProperties();
458
LauncherApplication::setSnStartupSequence(SnStartupSequence* sequence)
460
if (sequence != NULL) {
461
if (!sn_startup_sequence_get_completed(sequence)) {
462
/* 'launching' property becomes true for a few seconds */
463
m_launching_timer.start();
465
m_launching_timer.stop();
467
sn_startup_sequence_ref(sequence);
470
m_snStartupSequence.reset(sequence);
474
executableChanged(executable());
475
launchingChanged(launching());
479
LauncherApplication::setIconGeometry(int x, int y, int width, int height, uint xid)
481
if (m_application == NULL) {
485
QScopedPointer<BamfUintList> xids;
487
xids.reset(m_application->xids());
491
xids.reset(new BamfUintList(list));
493
int size = xids->size();
498
WnckScreen* screen = wnck_screen_get_default();
499
wnck_screen_force_update(screen);
501
for (int i = 0; i < size; ++i) {
502
WnckWindow* window = wnck_window_get(xids->at(i));
503
wnck_window_set_icon_geometry(window, x, y, width, height);
508
LauncherApplication::onWindowAdded(BamfWindow* window)
510
if (window != NULL) {
511
windowAdded(window->xid());
516
LauncherApplication::launching() const
518
return m_launching_timer.isActive();
522
LauncherApplication::updateHasVisibleWindow()
524
bool prev = m_has_visible_window;
526
if (m_application != NULL) {
527
m_has_visible_window = QScopedPointer<BamfUintList>(m_application->xids())->size() > 0;
529
m_has_visible_window = false;
532
if (m_has_visible_window != prev) {
533
hasVisibleWindowChanged(m_has_visible_window);
538
LauncherApplication::updateWindowCount()
540
Q_EMIT windowCountChanged(windowCount());
544
LauncherApplication::updateCounterVisible()
546
bool counterVisible = running() && m_counter > 0;
548
if (m_counterVisible != counterVisible) {
549
m_counterVisible = counterVisible;
550
Q_EMIT counterVisibleChanged(m_counterVisible);
555
LauncherApplication::has_visible_window() const
557
return m_has_visible_window;
561
LauncherApplication::progress() const
567
LauncherApplication::counter() const
573
LauncherApplication::emblem() const
579
LauncherApplication::progressBarVisible() const
581
return m_progressBarVisible;
585
LauncherApplication::counterVisible() const
587
return m_counterVisible;
591
LauncherApplication::emblemVisible() const
593
return m_emblemVisible;
596
/* Returns the number of window for this application that reside on the
599
LauncherApplication::windowCountOnCurrentWorkspace()
602
WnckWorkspace *current = wnck_screen_get_active_workspace(wnck_screen_get_default());
604
for (int i = 0; i < m_application->windows()->size(); i++) {
605
BamfWindow *window = m_application->windows()->at(i);
606
if (window == NULL) {
610
/* When geting the wnck window, it's possible we get a NULL
611
return value because wnck hasn't updated its internal list yet,
612
so we need to force it once to be sure */
613
WnckWindow *wnck_window = wnck_window_get(window->xid());
614
if (wnck_window == NULL) {
615
wnck_screen_force_update(wnck_screen_get_default());
616
wnck_window = wnck_window_get(window->xid());
617
if (wnck_window == NULL) {
622
WnckWorkspace *workspace = wnck_window_get_workspace(wnck_window);
623
if (workspace == current) {
631
LauncherApplication::activate()
635
} else if (active()) {
636
if (windowCountOnCurrentWorkspace() > 0 && windowCount() > 1) {
637
spread(windowCount() > windowCountOnCurrentWorkspace());
639
} else if (running() && has_visible_window()) {
647
LauncherApplication::launchNewInstance()
653
LauncherApplication::launch()
655
if (m_appInfo == NULL) {
659
GError* error = NULL;
664
GdkDisplay* display = gdk_display_get_default();
665
GObjectScopedPointer<GdkAppLaunchContext> context(gdk_display_get_app_launch_context(display));
667
/* We need to take the timestamp from the X server for the
668
window manager to work properly
669
https://bugs.launchpad.net/unity-2d/+bug/735205 */
670
/* FIXME: ultimately we should forward the timestamps from
671
events that triggered the launch */
672
root = gdk_x11_window_lookup_for_display(display, GDK_ROOT_WINDOW());
673
timestamp = gdk_x11_get_server_time(root);
675
gdk_app_launch_context_set_timestamp(context.data(), timestamp);
677
g_app_info_launch(m_appInfo.data(), NULL, (GAppLaunchContext*)context.data(), &error);
680
UQ_WARNING << "Failed to launch application:" << error->message;
685
/* 'launching' property becomes true for a few seconds and becomes
686
false as soon as the application is launched */
687
m_launching_timer.start();
688
launchingChanged(true);
694
LauncherApplication::onLaunchingTimeouted()
696
launchingChanged(false);
700
LauncherApplication::close()
702
if (m_application == NULL) {
706
QScopedPointer<BamfUintList> xids(m_application->xids());
707
int size = xids->size();
712
WnckScreen* screen = wnck_screen_get_default();
713
wnck_screen_force_update(screen);
715
for (int i = 0; i < size; ++i) {
716
WnckWindow* window = wnck_window_get(xids->at(i));
717
wnck_window_close(window, CurrentTime);
722
LauncherApplication::show()
724
if (m_application == NULL) {
728
QScopedPointer<BamfWindowList> windows(m_application->windows());
729
int size = windows->size();
734
/* Pick the most important window.
735
The primary criterion to determine the most important window is urgency.
736
The secondary criterion is the last_active timestamp (the last time the
737
window was activated). */
738
BamfWindow* important = windows->at(0);
739
for (int i = 0; i < size; ++i) {
740
BamfWindow* current = windows->at(i);
741
if (current->urgent() && !important->urgent()) {
743
} else if (current->urgent() || !important->urgent()) {
744
if (current->last_active() > important->last_active()) {
750
WnckScreen* screen = wnck_screen_get_default();
751
wnck_screen_force_update(screen);
753
WnckWindow* window = wnck_window_get(important->xid());
754
LauncherUtility::showWindow(window);
758
LauncherApplication::spread(bool showAllWorkspaces)
760
QDBusInterface compiz("org.freedesktop.compiz",
761
"/org/freedesktop/compiz/scale/screen0/initiate_all_key",
762
"org.freedesktop.compiz");
764
if (compiz.isValid()) {
765
Qt::HANDLE root = QX11Info::appRootWindow();
766
BamfUintList* xids = m_application->xids();
767
QStringList fragments;
768
for (int i = 0; i < xids->size(); i++) {
769
uint xid = xids->at(i);
770
fragments.append("xid=" + QString::number(xid));
773
compiz.asyncCall("activate", "root", static_cast<int>(root), "match", fragments.join(" | "));
775
QDBusInterface spread("com.canonical.Unity2d.Spread", "/Spread",
776
"com.canonical.Unity2d.Spread");
777
QDBusReply<bool> isShown = spread.call("IsShown");
778
if (isShown.isValid()) {
779
if (isShown.value() == true) {
780
spread.asyncCall("FilterByApplication", m_application->desktop_file());
782
if (showAllWorkspaces) {
783
spread.asyncCall("ShowAllWorkspaces", m_application->desktop_file());
785
spread.asyncCall("ShowCurrentWorkspace", m_application->desktop_file());
789
UQ_WARNING << "Failed to get property IsShown on com.canonical.Unity2d.Spread";
795
LauncherApplication::slotChildAdded(BamfView* child)
797
BamfIndicator* indicator = qobject_cast<BamfIndicator*>(child);
798
if (indicator != NULL) {
799
QString path = indicator->dbus_menu_path();
800
if (!m_indicatorMenus.contains(path)) {
801
DBusMenuImporter* importer = new DBusMenuImporter(indicator->address(), path, this);
802
connect(importer, SIGNAL(menuUpdated()), SLOT(onIndicatorMenuUpdated()));
803
m_indicatorMenus[path] = importer;
809
LauncherApplication::slotChildRemoved(BamfView* child)
811
BamfIndicator* indicator = qobject_cast<BamfIndicator*>(child);
812
if (indicator != NULL) {
813
QString path = indicator->dbus_menu_path();
814
if (m_indicatorMenus.contains(path)) {
815
m_indicatorMenus.take(path)->deleteLater();
821
LauncherApplication::fetchIndicatorMenus()
823
Q_FOREACH(QString path, m_indicatorMenus.keys()) {
824
m_indicatorMenus.take(path)->deleteLater();
827
if (m_application != NULL) {
828
QScopedPointer<BamfViewList> children(m_application->children());
829
for (int i = 0; i < children->size(); ++i) {
830
slotChildAdded(children->at(i));
836
LauncherApplication::createMenuActions()
838
if (m_application != NULL && !m_indicatorMenus.isEmpty()) {
839
/* Request indicator menus to be updated: this is asynchronous
840
and the corresponding actions are added to the menu in
841
SLOT(onIndicatorMenuUpdated()).
842
Static menu actions are appended after all indicator menus
844
m_indicatorMenusReady = 0;
845
Q_FOREACH(DBusMenuImporter* importer, m_indicatorMenus) {
846
importer->updateMenu();
849
createDynamicMenuActions();
850
createStaticMenuActions();
855
LauncherApplication::createDynamicMenuActions()
857
if (!m_dynamicQuicklistImporter.isNull()) {
858
/* FIXME: the menu is only partially updated while visible: stale
859
actions will correctly be removed from the menu, but new actions
860
will not be added until the menu is closed and opened again.
861
This is an acceptable limitation for now. */
862
QList<QAction*> actions = m_dynamicQuicklistImporter->menu()->actions();
863
Q_FOREACH(QAction* action, actions) {
864
if (action->isSeparator()) {
865
m_menu->insertSeparatorBeforeTitle();
867
connect(action, SIGNAL(triggered()), m_menu, SLOT(hide()));
868
m_menu->insertActionBeforeTitle(action);
875
LauncherApplication::createStaticMenuActions()
877
/* Custom menu actions from the desktop file. */
878
if (!m_staticShortcuts.isNull()) {
879
const gchar** nicks = indicator_desktop_shortcuts_get_nicks(m_staticShortcuts.data());
882
while (((gpointer*) nicks)[i]) {
883
const gchar* nick = nicks[i];
884
QAction* action = new QAction(m_menu);
885
action->setText(QString::fromUtf8(indicator_desktop_shortcuts_nick_get_name(m_staticShortcuts.data(), nick)));
886
action->setProperty(SHORTCUT_NICK_PROPERTY, QVariant(nick));
887
m_menu->insertActionBeforeTitle(action);
888
connect(action, SIGNAL(triggered()), SLOT(onStaticShortcutTriggered()));
893
m_menu->insertSeparatorBeforeTitle();
895
QList<QAction*> actions;
896
bool is_running = running();
898
/* Only applications with a corresponding desktop file can be kept in the launcher */
899
if (QFile::exists(desktop_file())) {
900
QAction* keep = new QAction(m_menu);
901
keep->setCheckable(is_running);
902
keep->setChecked(sticky());
903
keep->setText(is_running ? u2dTr("Keep in launcher") : u2dTr("Remove from launcher"));
904
actions.append(keep);
905
QObject::connect(keep, SIGNAL(triggered()), this, SLOT(onKeepTriggered()));
909
QAction* quit = new QAction(m_menu);
910
quit->setText(u2dTr("Quit"));
911
actions.append(quit);
912
QObject::connect(quit, SIGNAL(triggered()), this, SLOT(onQuitTriggered()));
915
/* Filter out duplicate actions. This typically happens with indicator
916
menus that contain a "Quit" action: we don’t want two "Quit" actions in
918
Q_FOREACH(QAction* pending, actions) {
919
bool duplicate = false;
920
Q_FOREACH(QAction* action, m_menu->actions()) {
921
/* The filtering is done on the text of the action. This will
922
obviously break if for example only one of the two actions is
923
localized ("Quit" != "Quitter"), but we don’t have a better way
924
to identify duplicate actions. */
925
if (pending->text() == action->text()) {
932
m_menu->addAction(pending);
940
LauncherApplication::onIndicatorMenuUpdated()
942
if (!m_menu->isVisible()) {
946
DBusMenuImporter* importer = static_cast<DBusMenuImporter*>(sender());
947
QList<QAction*> actions = importer->menu()->actions();
948
Q_FOREACH(QAction* action, actions) {
949
if (action->isSeparator()) {
950
m_menu->insertSeparatorBeforeTitle();
952
connect(action, SIGNAL(triggered()), m_menu, SLOT(hide()));
953
m_menu->insertActionBeforeTitle(action);
957
if (++m_indicatorMenusReady == m_indicatorMenus.size()) {
958
/* All indicator menus have been updated. */
959
createDynamicMenuActions();
960
createStaticMenuActions();
965
LauncherApplication::onStaticShortcutTriggered()
967
QAction* action = static_cast<QAction*>(sender());
968
QString nick = action->property(SHORTCUT_NICK_PROPERTY).toString();
970
indicator_desktop_shortcuts_nick_exec(m_staticShortcuts.data(), nick.toUtf8().constData());
974
LauncherApplication::onKeepTriggered()
976
QAction* keep = static_cast<QAction*>(sender());
977
bool sticky = keep->isChecked();
983
LauncherApplication::onQuitTriggered()
990
bool LauncherApplication::updateOverlayState(QMap<QString, QVariant> properties,
991
QString propertyName, T* member)
993
if (properties.contains(propertyName)) {
994
T value = properties.value(propertyName).value<T>();
995
if (value != *member) {
1004
LauncherApplication::updateOverlaysState(const QString& sender, QMap<QString, QVariant> properties)
1006
if (updateOverlayState(properties, "progress", &m_progress)) {
1007
Q_EMIT progressChanged(m_progress);
1009
if (updateOverlayState(properties, "progress-visible", &m_progressBarVisible)) {
1010
Q_EMIT progressBarVisibleChanged(m_progressBarVisible);
1012
if (updateOverlayState(properties, "count", &m_counter)) {
1013
Q_EMIT counterChanged(m_counter);
1015
if (updateOverlayState(properties, "count-visible", &m_counterVisible)) {
1016
Q_EMIT counterVisibleChanged(m_counterVisible);
1018
if (updateOverlayState(properties, "emblem", &m_emblem)) {
1019
Q_EMIT emblemChanged(m_emblem);
1021
if (updateOverlayState(properties, "emblem-visible", &m_emblemVisible)) {
1022
Q_EMIT emblemVisibleChanged(m_emblemVisible);
1024
if (updateOverlayState(properties, "quicklist", &m_dynamicQuicklistPath)) {
1025
setDynamicQuicklistImporter(sender);
1030
LauncherApplication::setDynamicQuicklistImporter(const QString& service)
1032
if (m_dynamicQuicklistPath.isEmpty() || service.isEmpty()) {
1033
m_dynamicQuicklistImporter.reset();
1035
m_dynamicQuicklistImporter.reset(new DBusMenuImporter(service, m_dynamicQuicklistPath));
1036
m_dynamicQuicklistImporter->updateMenu();
1037
if (m_dynamicQuicklistServiceWatcher == NULL) {
1038
m_dynamicQuicklistServiceWatcher = new QDBusServiceWatcher(this);
1039
m_dynamicQuicklistServiceWatcher->setConnection(QDBusConnection::sessionBus());
1040
connect(m_dynamicQuicklistServiceWatcher,
1041
SIGNAL(serviceOwnerChanged(const QString&, const QString&, const QString&)),
1042
SLOT(dynamicQuicklistImporterServiceOwnerChanged(const QString&, const QString&, const QString&)));
1044
m_dynamicQuicklistServiceWatcher->addWatchedService(service);
1049
LauncherApplication::dynamicQuicklistImporterServiceOwnerChanged(const QString& serviceName, const QString& oldOwner, const QString& newOwner)
1051
m_dynamicQuicklistServiceWatcher->removeWatchedService(oldOwner);
1052
setDynamicQuicklistImporter(newOwner);
1055
#include "launcherapplication.moc"