Merge lp:~osomon/unity-2d/custom-quicklists-dynamic into lp:unity-2d/3.0

Proposed by Olivier Tilloy
Status: Merged
Approved by: Ugo Riboni
Approved revision: 560
Merged at revision: 563
Proposed branch: lp:~osomon/unity-2d/custom-quicklists-dynamic
Merge into: lp:unity-2d/3.0
Diff against target: 296 lines (+93/-13)
6 files modified
launcher/UnityApplications/launcherapplication.cpp (+59/-9)
launcher/UnityApplications/launcherapplication.h (+10/-1)
launcher/UnityApplications/launcherapplicationslist.cpp (+7/-1)
launcher/UnityApplications/launcherapplicationslist.h (+2/-1)
launcher/UnityApplications/launchermenu.cpp (+12/-1)
launcher/UnityApplications/launchermenu.h (+3/-0)
To merge this branch: bzr merge lp:~osomon/unity-2d/custom-quicklists-dynamic
Reviewer Review Type Date Requested Status
Ugo Riboni (community) Approve
Florian Boucault Pending
Review via email: mp+59396@code.launchpad.net

Commit message

[launcher] Support dynamic shortcuts in the quicklists.

Dynamic shortcuts are exposed over D-Bus by the application through the use of libunity.
See https://wiki.ubuntu.com/Unity/LauncherAPI#Dynamic%20Quicklist%20entries for documentation.

Description of the change

Note: at the moment, it seems the only application in the archive that implements dynamic quicklists is deja-dup.
To test, you will need to install deja-dup, start a backup, and observe the quicklist while backing up.
Alternatively, you can run example programs that pretend to be another application and expose dynamic a quicklist for it, see https://wiki.ubuntu.com/Unity/LauncherAPI#Example%20Code for details.

To post a comment you must log in.
Revision history for this message
Ugo Riboni (uriboni) wrote :

Functionally it works OK. I tested it both with dejadup and with the example code.

The only issue that might be interesting to fix is that if the menu is already open, it's not updated. This leads to having some menu items that are not necessarily valid anymore. Not sure what will happen if you try to activate a menu item that the application has removed already.

review: Approve
Revision history for this message
Ugo Riboni (uriboni) wrote :

208 LauncherApplicationsList::onRemoteEntryUpdated(QString applicationURI, QMap<QString, QVariant> properties)
209 {
210 + QString sender = message().service();

For safety I would would check the result of calledFromDBus() before proceeding to access message()

Other than that, code looks good.

Revision history for this message
Olivier Tilloy (osomon) wrote :

> The only issue that might be interesting to fix is that if the menu is already
> open, it's not updated. This leads to having some menu items that are not
> necessarily valid anymore. Not sure what will happen if you try to activate a
> menu item that the application has removed already.

It is actually updated auto-magically :) (yes, I was surprised myself)
Try with the example code: launch the executable/script, open the contextual menu for evolution, and while it’s open, kill the example application: the menu items it added will disappear.

Revision history for this message
Olivier Tilloy (osomon) wrote :

> 208 LauncherApplicationsList::onRemoteEntryUpdated(QString
> applicationURI, QMap<QString, QVariant> properties)
> 209 {
> 210 + QString sender = message().service();
>
> For safety I would would check the result of calledFromDBus() before
> proceeding to access message()

This is true on principle (better safe than sorry). However onRemoteEntryUpdated is a private slot and it is only connected to D-Bus signals, so we are guaranteed that it is actually called from DBus.

559. By Olivier Tilloy

For safety, always make sure we are called from DBus.

560. By Olivier Tilloy

Add a FIXME to explain a current limitation of the implementation.

Revision history for this message
Ugo Riboni (uriboni) wrote :

Good to go now. Good job, and please open a bug for the missing feature in the FIXME.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'launcher/UnityApplications/launcherapplication.cpp'
2--- launcher/UnityApplications/launcherapplication.cpp 2011-04-28 13:09:25 +0000
3+++ launcher/UnityApplications/launcherapplication.cpp 2011-05-18 10:59:25 +0000
4@@ -54,9 +54,9 @@
5 #include <QAction>
6 #include <QDBusInterface>
7 #include <QDBusReply>
8+#include <QDBusServiceWatcher>
9 #include <QFile>
10 #include <QFileSystemWatcher>
11-#include <QScopedPointer>
12
13 extern "C" {
14 #include <libsn/sn.h>
15@@ -73,6 +73,7 @@
16 , m_counter(0), m_counterVisible(false)
17 , m_emblem(QString()), m_emblemVisible(false)
18 , m_forceUrgent(false)
19+ , m_dynamicQuicklistServiceWatcher(NULL)
20 {
21 m_launching_timer.setSingleShot(true);
22 m_launching_timer.setInterval(8000);
23@@ -840,15 +841,34 @@
24 importer->updateMenu();
25 }
26 } else {
27+ createDynamicMenuActions();
28 createStaticMenuActions();
29 }
30 }
31
32 void
33+LauncherApplication::createDynamicMenuActions()
34+{
35+ if (!m_dynamicQuicklistImporter.isNull()) {
36+ /* FIXME: the menu is only partially updated while visible: stale
37+ actions will correctly be removed from the menu, but new actions
38+ will not be added until the menu is closed and opened again.
39+ This is an acceptable limitation for now. */
40+ QList<QAction*> actions = m_dynamicQuicklistImporter->menu()->actions();
41+ Q_FOREACH(QAction* action, actions) {
42+ if (action->isSeparator()) {
43+ m_menu->insertSeparatorBeforeTitle();
44+ } else {
45+ connect(action, SIGNAL(triggered()), m_menu, SLOT(hide()));
46+ m_menu->insertActionBeforeTitle(action);
47+ }
48+ }
49+ }
50+}
51+
52+void
53 LauncherApplication::createStaticMenuActions()
54 {
55- QList<QAction*> actions;
56-
57 /* Custom menu actions from the desktop file. */
58 if (!m_staticShortcuts.isNull()) {
59 const gchar** nicks = indicator_desktop_shortcuts_get_nicks(m_staticShortcuts.data());
60@@ -859,15 +879,15 @@
61 QAction* action = new QAction(m_menu);
62 action->setText(QString::fromUtf8(indicator_desktop_shortcuts_nick_get_name(m_staticShortcuts.data(), nick)));
63 action->setProperty(SHORTCUT_NICK_PROPERTY, QVariant(nick));
64- actions.append(action);
65+ m_menu->insertActionBeforeTitle(action);
66 connect(action, SIGNAL(triggered()), SLOT(onStaticShortcutTriggered()));
67 ++i;
68 }
69 }
70 }
71- m_menu->insertActions(m_menu->actions().first(), actions);
72+ m_menu->insertSeparatorBeforeTitle();
73
74- actions.clear();
75+ QList<QAction*> actions;
76 bool is_running = running();
77
78 /* Only applications with a corresponding desktop file can be kept in the launcher */
79@@ -922,15 +942,16 @@
80 QList<QAction*> actions = importer->menu()->actions();
81 Q_FOREACH(QAction* action, actions) {
82 if (action->isSeparator()) {
83- m_menu->addSeparator();
84+ m_menu->insertSeparatorBeforeTitle();
85 } else {
86 connect(action, SIGNAL(triggered()), m_menu, SLOT(hide()));
87- m_menu->addAction(action);
88+ m_menu->insertActionBeforeTitle(action);
89 }
90 }
91
92 if (++m_indicatorMenusReady == m_indicatorMenus.size()) {
93 /* All indicator menus have been updated. */
94+ createDynamicMenuActions();
95 createStaticMenuActions();
96 }
97 }
98@@ -975,7 +996,7 @@
99 }
100
101 void
102-LauncherApplication::updateOverlaysState(QMap<QString, QVariant> properties)
103+LauncherApplication::updateOverlaysState(const QString& sender, QMap<QString, QVariant> properties)
104 {
105 if (updateOverlayState(properties, "progress", &m_progress)) {
106 Q_EMIT progressChanged(m_progress);
107@@ -995,5 +1016,34 @@
108 if (updateOverlayState(properties, "emblem-visible", &m_emblemVisible)) {
109 Q_EMIT emblemVisibleChanged(m_emblemVisible);
110 }
111+ if (updateOverlayState(properties, "quicklist", &m_dynamicQuicklistPath)) {
112+ setDynamicQuicklistImporter(sender);
113+ }
114+}
115+
116+void
117+LauncherApplication::setDynamicQuicklistImporter(const QString& service)
118+{
119+ if (m_dynamicQuicklistPath.isEmpty() || service.isEmpty()) {
120+ m_dynamicQuicklistImporter.reset();
121+ } else {
122+ m_dynamicQuicklistImporter.reset(new DBusMenuImporter(service, m_dynamicQuicklistPath));
123+ m_dynamicQuicklistImporter->updateMenu();
124+ if (m_dynamicQuicklistServiceWatcher == NULL) {
125+ m_dynamicQuicklistServiceWatcher = new QDBusServiceWatcher(this);
126+ m_dynamicQuicklistServiceWatcher->setConnection(QDBusConnection::sessionBus());
127+ connect(m_dynamicQuicklistServiceWatcher,
128+ SIGNAL(serviceOwnerChanged(const QString&, const QString&, const QString&)),
129+ SLOT(dynamicQuicklistImporterServiceOwnerChanged(const QString&, const QString&, const QString&)));
130+ }
131+ m_dynamicQuicklistServiceWatcher->addWatchedService(service);
132+ }
133+}
134+
135+void
136+LauncherApplication::dynamicQuicklistImporterServiceOwnerChanged(const QString& serviceName, const QString& oldOwner, const QString& newOwner)
137+{
138+ m_dynamicQuicklistServiceWatcher->removeWatchedService(oldOwner);
139+ setDynamicQuicklistImporter(newOwner);
140 }
141
142
143=== modified file 'launcher/UnityApplications/launcherapplication.h'
144--- launcher/UnityApplications/launcherapplication.h 2011-04-28 06:59:39 +0000
145+++ launcher/UnityApplications/launcherapplication.h 2011-05-18 10:59:25 +0000
146@@ -36,6 +36,7 @@
147 #include <QTimer>
148 #include <QHash>
149 #include <QPointer>
150+#include <QScopedPointer>
151
152 #include "bamf-application.h"
153
154@@ -46,6 +47,7 @@
155
156 class DBusMenuImporter;
157 class QFileSystemWatcher;
158+class QDBusServiceWatcher;
159
160 typedef GObjectScopedPointer<GAppInfo> GAppInfoPointer;
161 typedef GObjectScopedPointer<GDesktopAppInfo> GDesktopAppInfoPointer;
162@@ -110,7 +112,7 @@
163
164 static void showWindow(WnckWindow* window);
165 static void moveViewportToWindow(WnckWindow* window);
166- void updateOverlaysState(QMap<QString, QVariant> properties);
167+ void updateOverlaysState(const QString& sender, QMap<QString, QVariant> properties);
168
169 Q_SIGNALS:
170 void stickyChanged(bool);
171@@ -154,6 +156,8 @@
172 void beginForceUrgent(int duration);
173 void endForceUrgent();
174
175+ void dynamicQuicklistImporterServiceOwnerChanged(const QString& serviceName, const QString& oldOwner, const QString& newOwner);
176+
177 private:
178 QPointer<BamfApplication> m_application;
179 QFileSystemWatcher *m_desktopFileWatcher;
180@@ -175,12 +179,17 @@
181 void updateBamfApplicationDependentProperties();
182 void monitorDesktopFile(const QString&);
183 void fetchIndicatorMenus();
184+ void createDynamicMenuActions();
185 void createStaticMenuActions();
186 int windowCountOnCurrentWorkspace();
187 template<typename T>
188 bool updateOverlayState(QMap<QString, QVariant> properties,
189 QString propertyName, T* member);
190
191+ QString m_dynamicQuicklistPath;
192+ QScopedPointer<DBusMenuImporter> m_dynamicQuicklistImporter;
193+ QDBusServiceWatcher* m_dynamicQuicklistServiceWatcher;
194+ void setDynamicQuicklistImporter(const QString& service);
195 IndicatorDesktopShortcutsPointer m_staticShortcuts;
196 };
197
198
199=== modified file 'launcher/UnityApplications/launcherapplicationslist.cpp'
200--- launcher/UnityApplications/launcherapplicationslist.cpp 2011-04-28 13:09:25 +0000
201+++ launcher/UnityApplications/launcherapplicationslist.cpp 2011-05-18 10:59:25 +0000
202@@ -23,9 +23,13 @@
203 #include "bamf-application.h"
204 #include "gconfitem-qml-wrapper.h"
205
206+// unity-2d
207+#include <debug_p.h>
208+
209 #include <QStringList>
210 #include <QDir>
211 #include <QDBusConnection>
212+#include <QDBusMessage>
213 #include <QFileInfo>
214 #include <QX11Info>
215
216@@ -134,6 +138,8 @@
217 void
218 LauncherApplicationsList::onRemoteEntryUpdated(QString applicationURI, QMap<QString, QVariant> properties)
219 {
220+ UQ_RETURN_IF_FAIL(calledFromDBus());
221+ QString sender = message().service();
222 QString desktopFile;
223 if (applicationURI.indexOf("application://") == 0) {
224 desktopFile = applicationURI.mid(14);
225@@ -144,7 +150,7 @@
226
227 Q_FOREACH(LauncherApplication *application, m_applications) {
228 if (QFileInfo(application->desktop_file()).fileName() == desktopFile) {
229- application->updateOverlaysState(properties);
230+ application->updateOverlaysState(sender, properties);
231 return;
232 }
233 }
234
235=== modified file 'launcher/UnityApplications/launcherapplicationslist.h'
236--- launcher/UnityApplications/launcherapplicationslist.h 2011-04-15 12:09:09 +0000
237+++ launcher/UnityApplications/launcherapplicationslist.h 2011-05-18 10:59:25 +0000
238@@ -25,6 +25,7 @@
239 #include <QObject>
240 #include <QtDeclarative/qdeclarative.h>
241 #include <QMap>
242+#include <QDBusContext>
243
244 #include <unity2dapplication.h>
245
246@@ -37,7 +38,7 @@
247 class BamfView;
248 class GConfItemQmlWrapper;
249
250-class LauncherApplicationsList : public QAbstractListModel, protected AbstractX11EventFilter
251+class LauncherApplicationsList : public QAbstractListModel, protected AbstractX11EventFilter, protected QDBusContext
252 {
253 Q_OBJECT
254 friend class LauncherApplicationsListDBUS;
255
256=== modified file 'launcher/UnityApplications/launchermenu.cpp'
257--- launcher/UnityApplications/launchermenu.cpp 2011-04-28 07:45:05 +0000
258+++ launcher/UnityApplications/launchermenu.cpp 2011-05-18 10:59:25 +0000
259@@ -203,7 +203,6 @@
260 }
261 }
262 } else {
263- insertSeparator(m_titleAction);
264 addSeparator();
265 m_launcherItem->createMenuActions();
266
267@@ -304,3 +303,15 @@
268 }
269 }
270
271+void
272+LauncherContextualMenu::insertActionBeforeTitle(QAction* action)
273+{
274+ insertAction(m_titleAction, action);
275+}
276+
277+QAction*
278+LauncherContextualMenu::insertSeparatorBeforeTitle()
279+{
280+ return insertSeparator(m_titleAction);
281+}
282+
283
284=== modified file 'launcher/UnityApplications/launchermenu.h'
285--- launcher/UnityApplications/launchermenu.h 2011-03-21 10:33:16 +0000
286+++ launcher/UnityApplications/launchermenu.h 2011-05-18 10:59:25 +0000
287@@ -55,6 +55,9 @@
288 Q_INVOKABLE void hide();
289 Q_INVOKABLE void hideWithDelay(int delay);
290
291+ void insertActionBeforeTitle(QAction* action);
292+ QAction* insertSeparatorBeforeTitle();
293+
294 protected:
295 void resizeEvent(QResizeEvent* event);
296 void leaveEvent(QEvent* event);

Subscribers

People subscribed via source and target branches