Merge lp:~mzanetti/unity8/rework-launcher-backend into lp:unity8

Proposed by Michael Zanetti
Status: Superseded
Proposed branch: lp:~mzanetti/unity8/rework-launcher-backend
Merge into: lp:unity8
Diff against target: 2509 lines (+914/-1104)
33 files modified
data/unity8-filewatcher.conf (+8/-0)
debian/unity8.install (+1/-0)
plugins/Unity/Launcher/CMakeLists.txt (+5/-3)
plugins/Unity/Launcher/backend/launcherbackend.cpp (+0/-584)
plugins/Unity/Launcher/backend/launcherbackend.h (+0/-196)
plugins/Unity/Launcher/dbusinterface.cpp (+232/-0)
plugins/Unity/Launcher/dbusinterface.h (+50/-0)
plugins/Unity/Launcher/desktopfilehandler.cpp (+124/-0)
plugins/Unity/Launcher/desktopfilehandler.h (+37/-0)
plugins/Unity/Launcher/gsettings.cpp (+76/-0)
plugins/Unity/Launcher/gsettings.h (+37/-0)
plugins/Unity/Launcher/launcheritem.cpp (+30/-0)
plugins/Unity/Launcher/launcheritem.h (+16/-13)
plugins/Unity/Launcher/launchermodel.cpp (+83/-16)
plugins/Unity/Launcher/launchermodel.h (+17/-8)
plugins/Unity/Launcher/quicklistmodel.h (+1/-1)
qml/Dash/DashApplication.qml (+0/-6)
qml/Launcher/LauncherDelegate.qml (+3/-2)
qml/Launcher/LauncherPanel.qml (+1/-0)
qml/Notifications/Notification.qml (+3/-3)
qml/Shell.qml (+0/-6)
run.sh (+1/-0)
run_on_device.sh (+1/-1)
src/Dash/main.cpp (+1/-0)
src/main.cpp (+1/-0)
tests/mocks/Unity/Launcher/CMakeLists.txt (+1/-1)
tests/mocks/Unity/Launcher/MockLauncherItem.cpp (+13/-0)
tests/mocks/Unity/Launcher/MockLauncherItem.h (+4/-0)
tests/plugins/Unity/Launcher/CMakeLists.txt (+9/-24)
tests/plugins/Unity/Launcher/gsettings.cpp (+40/-0)
tests/plugins/Unity/Launcher/gsettings.h (+38/-0)
tests/plugins/Unity/Launcher/launcherbackendtest.cpp (+0/-239)
tests/plugins/Unity/Launcher/launchermodeltest.cpp (+81/-1)
To merge this branch: bzr merge lp:~mzanetti/unity8/rework-launcher-backend
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Needs Fixing
Unity Team Pending
Review via email: mp+232200@code.launchpad.net

This proposal has been superseded by a proposal from 2014-09-01.

Commit message

Rework LauncherBackend

* This splits the LauncherBackend class into 3 separate classes: DBusInterface, GSettings and DesktopFileHandler in order to reduce complexity
* Fixes all currently known backend related bugs
* Moves storage from AccountService to DConf (drops AS support for now)

Description of the change

 * Are there any related MPs required for this MP to build/function as expected? Please list.

https://code.launchpad.net/~mzanetti/unity-api/launcher-v4/+merge/232198
https://code.launchpad.net/~mzanetti/unity/new-key-in-launcher-schema/+merge/232199

 * Did you perform an exploratory manual test run of your code change and any related functionality?

yes (in phone-right-edge ppa)

 * Did you make sure that your branch does not contain spurious tags?

yes

 * If you changed the packaging (debian), did you subscribe the ubuntu-unity team to this MP?

no

 * If you changed the UI, has there been a design review?

no

To post a comment you must log in.
1206. By Michael Zanetti

drop some debug prints

1207. By Michael Zanetti

drop more debug prints

1208. By Michael Zanetti

and one more debug print dropped

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
1209. By Michael Zanetti

merge prereq also adding support for in desktop file translations

1210. By Michael Zanetti

ooops. this shouldn't have gone in

1211. By Michael Zanetti

also revert the other workarounds for being able to work

1212. By Michael Zanetti

make DesktopFileHander static to save some memory

1213. By Michael Zanetti

make it a namespace only

1214. By Michael Zanetti

drop unneeded debug print and microoptimize an if/else

1215. By Michael Zanetti

change DesktopFileHandler back into a stateful class but more polished

1216. By Michael Zanetti

require 7.90 of libunuty-api

1217. By Michael Zanetti

properly mark overrides

1218. By Michael Zanetti

fix dependency on unity-schemas

1219. By Michael Zanetti

bump launcher-impl version requirement

1220. By Michael Zanetti

improve countEmblem test and fix some warnings along the way

1221. By Michael Zanetti

make some count emblems visible in the mock

1222. By Michael Zanetti

don't load unity7 favorites but only work with the new items key

1223. By Michael Zanetti

merge trunk

1224. By Michael Zanetti

fix dependency on unity-schemas

1225. By Michael Zanetti

fix schema to reflect change in unity-schemas

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added file 'data/unity8-filewatcher.conf'
--- data/unity8-filewatcher.conf 1970-01-01 00:00:00 +0000
+++ data/unity8-filewatcher.conf 2014-09-01 17:50:27 +0000
@@ -0,0 +1,8 @@
1description "File system watcher for unity8"
2author "Michael Zanetti <michael.zanetti@canonical.com>"
3
4# Workaround for bug 1360208. ~ and * in one expression doesn't work currently
5start on (file FILE=/home/phablet/.local/share/applications/*.desktop) or (file FILE=/usr/share/applications/*.desktop)
6
7exec dbus-send --type=method_call --dest=com.canonical.Unity.Launcher /com/canonical/Unity/Launcher com.canonical.Unity.Launcher.Refresh
8
09
=== modified file 'debian/unity8.install'
--- debian/unity8.install 2014-08-27 07:57:54 +0000
+++ debian/unity8.install 2014-09-01 17:50:27 +0000
@@ -1,5 +1,6 @@
1data/unity8.conf usr/share/upstart/sessions/1data/unity8.conf usr/share/upstart/sessions/
2data/unity8-dash.conf usr/share/upstart/sessions/2data/unity8-dash.conf usr/share/upstart/sessions/
3data/unity8-filewatcher.conf usr/share/upstart/sessions/
3usr/bin/unity84usr/bin/unity8
4usr/bin/unity8-dash5usr/bin/unity8-dash
5usr/share/applications/unity8.desktop6usr/share/applications/unity8.desktop
67
=== modified file 'plugins/Unity/Launcher/CMakeLists.txt'
--- plugins/Unity/Launcher/CMakeLists.txt 2014-08-18 12:45:32 +0000
+++ plugins/Unity/Launcher/CMakeLists.txt 2014-09-01 17:50:27 +0000
@@ -1,5 +1,5 @@
1include(FindPkgConfig)1include(FindPkgConfig)
2pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=3)2pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=4)
3pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=3)3pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=3)
4pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)4pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
55
@@ -16,8 +16,10 @@
16 launchermodel.cpp16 launchermodel.cpp
17 launcheritem.cpp17 launcheritem.cpp
18 quicklistmodel.cpp18 quicklistmodel.cpp
19 common/quicklistentry.cpp19 quicklistentry.cpp
20 backend/launcherbackend.cpp20 dbusinterface.cpp
21 gsettings.cpp
22 desktopfilehandler.cpp
21 ${CMAKE_SOURCE_DIR}/plugins/AccountsService/AccountsServiceDBusAdaptor.cpp23 ${CMAKE_SOURCE_DIR}/plugins/AccountsService/AccountsServiceDBusAdaptor.cpp
22 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherItemInterface.h24 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherItemInterface.h
23 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherModelInterface.h25 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherModelInterface.h
2426
=== removed directory 'plugins/Unity/Launcher/backend'
=== removed file 'plugins/Unity/Launcher/backend/launcherbackend.cpp'
--- plugins/Unity/Launcher/backend/launcherbackend.cpp 2014-06-11 15:36:51 +0000
+++ plugins/Unity/Launcher/backend/launcherbackend.cpp 1970-01-01 00:00:00 +0000
@@ -1,584 +0,0 @@
1/*
2 * Copyright (C) 2013 Canonical, Ltd.
3 *
4 * Authors:
5 * Michael Terry <michael.terry@canonical.com>
6 * Michael Zanetti <michael.zanetti@canonical.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; version 3.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21#include "AccountsServiceDBusAdaptor.h"
22#include "launcherbackend.h"
23
24#include <QDir>
25#include <QDBusArgument>
26#include <QFileInfo>
27#include <QGSettings>
28#include <QDebug>
29#include <QStandardPaths>
30
31class LauncherBackendItem
32{
33public:
34 QString displayName;
35 QString icon;
36 int count;
37 bool countVisible;
38};
39
40LauncherBackend::LauncherBackend(QObject *parent):
41 QDBusVirtualObject(parent),
42 m_accounts(nullptr)
43{
44#ifndef LAUNCHER_TESTING
45 m_accounts = new AccountsServiceDBusAdaptor(this);
46#endif
47 m_user = qgetenv("USER");
48 syncFromAccounts();
49
50 /* Set up ourselves on DBus */
51 QDBusConnection con = QDBusConnection::sessionBus();
52 if (!con.registerService("com.canonical.Unity.Launcher")) {
53 qDebug() << "Unable to register launcher name";
54 }
55 if (!con.registerVirtualObject("/com/canonical/Unity/Launcher", this, QDBusConnection::VirtualObjectRegisterOption::SubPath)) {
56 qDebug() << "Unable to register launcher object";
57 }
58}
59
60LauncherBackend::~LauncherBackend()
61{
62 /* Remove oursevles from DBus */
63 QDBusConnection con = QDBusConnection::sessionBus();
64 con.unregisterService("com.canonical.Unity.Launcher");
65 con.unregisterObject("/com/canonical/Unity/Launcher");
66
67 /* Clear data */
68 m_storedApps.clear();
69
70 Q_FOREACH(LauncherBackendItem *item, m_itemCache) {
71 delete item;
72 }
73 m_itemCache.clear();
74}
75
76QStringList LauncherBackend::storedApplications() const
77{
78 return m_storedApps;
79}
80
81void LauncherBackend::setStoredApplications(const QStringList &appIds)
82{
83 if (appIds.count() < m_storedApps.count()) {
84 Q_FOREACH(const QString &appId, m_storedApps) {
85 if (!appIds.contains(appId)) {
86 delete m_itemCache.take(appId);
87 }
88 }
89 }
90 m_storedApps = appIds;
91 Q_FOREACH(const QString &appId, appIds) {
92 if (!m_itemCache.contains(appId)) {
93 QString df = findDesktopFile(appId);
94 if (!df.isEmpty()) {
95 LauncherBackendItem *item = parseDesktopFile(df);
96 m_itemCache.insert(appId, item);
97 } else {
98 // Cannot find any data for that app... ignoring it.
99 qWarning() << "cannot find desktop file for" << appId << ". discarding app.";
100 m_storedApps.removeAll(appId);
101 }
102 }
103 }
104 syncToAccounts();
105}
106
107QString LauncherBackend::desktopFile(const QString &appId) const
108{
109 return findDesktopFile(appId);
110}
111
112QString LauncherBackend::displayName(const QString &appId) const
113{
114 LauncherBackendItem *item = m_itemCache.value(appId, nullptr);
115 if (item) {
116 return item->displayName;
117 }
118
119 QString df = findDesktopFile(appId);
120 if (!df.isEmpty()) {
121 LauncherBackendItem *item = parseDesktopFile(df);
122 m_itemCache.insert(appId, item);
123 return item->displayName;
124 }
125
126 return QString();
127}
128
129QString LauncherBackend::icon(const QString &appId) const
130{
131 QString iconName;
132 LauncherBackendItem *item = getItem(appId);
133 if (item) {
134 iconName = item->icon;
135 }
136
137 return iconName;
138}
139
140QList<QuickListEntry> LauncherBackend::quickList(const QString &appId) const
141{
142 // TODO: Get static (from .desktop file) and dynamic (from the app itself)
143 // entries and return them here. Frontend related entries (like "Pin to launcher")
144 // don't matter here. This is just the backend part.
145 // TODO: emit quickListChanged() when the dynamic part changes
146 Q_UNUSED(appId)
147 return QList<QuickListEntry>();
148}
149
150int LauncherBackend::progress(const QString &appId) const
151{
152 // TODO: Return value for progress emblem.
153 // TODO: emit progressChanged() when this value changes.
154 Q_UNUSED(appId)
155 return -1;
156}
157
158int LauncherBackend::count(const QString &appId) const
159{
160 int count = -1;
161 LauncherBackendItem *item = getItem(appId);
162
163 if (item) {
164 if (item->countVisible) {
165 count = item->count;
166 }
167 }
168
169 return count;
170}
171
172void LauncherBackend::setCount(const QString &appId, int count) const
173{
174 LauncherBackendItem *item = getItem(appId);
175
176 bool emitchange = false;
177 if (item) {
178 emitchange = (item->count != count);
179 item->count = count;
180 }
181
182 if (emitchange) {
183 /* TODO: This needs to use the accessor to handle the visibility
184 correctly, but when we have the two properties we can just use
185 the local value */
186 Q_EMIT countChanged(appId, this->count(appId));
187 QVariant vcount(item->count);
188 emitPropChangedDbus(appId, "count", vcount);
189 }
190}
191
192bool LauncherBackend::countVisible(const QString &appId) const
193{
194 bool visible = false;
195 LauncherBackendItem *item = getItem(appId);
196
197 if (item) {
198 visible = item->countVisible;
199 }
200
201 return visible;
202}
203
204void LauncherBackend::setCountVisible(const QString &appId, bool visible) const
205{
206 LauncherBackendItem *item = getItem(appId);
207
208 bool emitchange = false;
209 if (item) {
210 emitchange = (item->countVisible != visible);
211 item->countVisible = visible;
212 } else {
213 qDebug() << "Unable to find:" << appId;
214 }
215
216 if (emitchange) {
217 /* TODO: Because we're using visible in determining the
218 count we need to emit a count changed as well */
219 Q_EMIT countChanged(appId, this->count(appId));
220 Q_EMIT countVisibleChanged(appId, item->countVisible);
221 QVariant vCountVisible(item->countVisible);
222 emitPropChangedDbus(appId, "countVisible", vCountVisible);
223 }
224}
225
226void LauncherBackend::setUser(const QString &username)
227{
228 if (qgetenv("USER") == "lightdm" && m_user != username) {
229 m_user = username;
230 syncFromAccounts();
231 }
232}
233
234void LauncherBackend::triggerQuickListAction(const QString &appId, const QString &quickListId)
235{
236 // TODO: execute the given quicklist action
237 Q_UNUSED(appId)
238 Q_UNUSED(quickListId)
239}
240
241void LauncherBackend::syncFromAccounts()
242{
243 QList<QVariantMap> apps;
244 bool defaults = true;
245
246 m_storedApps.clear();
247
248 if (m_accounts && !m_user.isEmpty()) {
249 QVariant variant = m_accounts->getUserProperty(m_user, "com.canonical.unity.AccountsService", "launcher-items");
250 if (variant.isValid() && variant.canConvert<QDBusArgument>()) {
251 apps = qdbus_cast<QList<QVariantMap>>(variant.value<QDBusArgument>());
252 defaults = isDefaultsItem(apps);
253 }
254 }
255
256 if (m_accounts && defaults) { // Checking accounts as it'll be null when !useStorage
257 QGSettings gSettings("com.canonical.Unity.Launcher", "/com/canonical/unity/launcher/");
258 Q_FOREACH(const QString &entry, gSettings.get("favorites").toStringList()) {
259 if (entry.startsWith("application://")) {
260 QString appId = entry;
261 // Transform "application://foobar.desktop" to "application://foobar"
262 appId.remove("application://");
263 if (appId.endsWith(".desktop")) {
264 appId.chop(8);
265 }
266 QString df = findDesktopFile(appId);
267
268 if (!df.isEmpty()) {
269 m_storedApps << appId;
270
271 if (!m_itemCache.contains(appId)) {
272 m_itemCache.insert(appId, parseDesktopFile(df));
273 }
274 }
275 }
276 if (entry.startsWith("appid://")) {
277 QString appId = entry;
278 appId.remove("appid://");
279 // Strip hook name and current-user-version in case its there
280
281 if (appId.split('/').count() != 3) {
282 qWarning() << "ignoring entry " + appId + ". Not a valid appId.";
283 continue;
284 }
285 appId = appId.split('/').first() + "_" + appId.split('/').at(1);
286 QString df = findDesktopFile(appId);
287
288 if (!df.isEmpty()) {
289 m_storedApps << appId;
290
291 if (!m_itemCache.contains(appId)) {
292 m_itemCache.insert(appId, parseDesktopFile(df));
293 }
294 }
295 }
296 }
297 } else {
298 for (const QVariant &app: apps) {
299 loadFromVariant(app.toMap());
300 }
301 }
302}
303
304void LauncherBackend::syncToAccounts()
305{
306 if (m_accounts && !m_user.isEmpty()) {
307 QList<QVariantMap> items;
308
309 Q_FOREACH(const QString &appId, m_storedApps) {
310 items << itemToVariant(appId);
311 }
312
313 m_accounts->setUserProperty(m_user, "com.canonical.unity.AccountsService", "launcher-items", QVariant::fromValue(items));
314 }
315}
316
317QString LauncherBackend::findDesktopFile(const QString &appId) const
318{
319 int dashPos = -1;
320 QString helper = appId;
321
322 QStringList searchDirs = QStandardPaths::standardLocations(QStandardPaths::ApplicationsLocation);
323#ifdef LAUNCHER_TESTING
324 searchDirs << "";
325#endif
326
327 do {
328 if (dashPos != -1) {
329 helper = helper.replace(dashPos, 1, '/');
330 }
331
332 Q_FOREACH(const QString &searchDirName, searchDirs) {
333 QDir searchDir(searchDirName);
334 Q_FOREACH(const QString &desktopFile, searchDir.entryList(QStringList() << "*.desktop")) {
335 if (desktopFile.startsWith(helper)) {
336 QFileInfo fileInfo(searchDir, desktopFile);
337 return fileInfo.absoluteFilePath();
338 }
339 }
340 }
341
342 dashPos = helper.indexOf("-");
343 } while (dashPos != -1);
344
345 return QString();
346}
347
348LauncherBackendItem* LauncherBackend::parseDesktopFile(const QString &desktopFile) const
349{
350 QSettings settings(desktopFile, QSettings::IniFormat);
351
352 LauncherBackendItem* item = new LauncherBackendItem();
353 item->displayName = settings.value("Desktop Entry/Name").toString();
354
355 QString iconString = settings.value("Desktop Entry/Icon").toString();
356 QString pathString = settings.value("Desktop Entry/Path").toString();
357 if (QFileInfo(iconString).exists()) {
358 item->icon = QFileInfo(iconString).absoluteFilePath();
359 } else if (QFileInfo(pathString + '/' + iconString).exists()) {
360 item->icon = pathString + '/' + iconString;
361 } else {
362 item->icon = "image://theme/" + iconString;
363 }
364
365 /* TODO: These should be looked up in a cache somewhere */
366 item->count = 0;
367 item->countVisible = false;
368
369 return item;
370}
371
372/* Gets an item, and tries to create a new one if we need it to */
373LauncherBackendItem* LauncherBackend::getItem(const QString &appId) const
374{
375 LauncherBackendItem *item = m_itemCache.value(appId, nullptr);
376 if (!item) {
377 QString df = findDesktopFile(appId);
378 if (!df.isEmpty()) {
379 item = parseDesktopFile(df);
380 if (item) {
381 m_itemCache[appId] = item;
382 } else {
383 qWarning() << "Unable to parse desktop file for" << appId << "path" << df;
384 }
385 } else {
386 qWarning() << "Unable to find desktop file for:" << appId;
387 }
388 }
389
390 if (!item)
391 qWarning() << "Unable to find item for: " << appId;
392
393 return item;
394}
395
396void LauncherBackend::loadFromVariant(const QVariantMap &details)
397{
398 if (!details.contains("id")) {
399 return;
400 }
401 QString appId = details.value("id").toString();
402
403 LauncherBackendItem *item = m_itemCache.value(appId, nullptr);
404 if (item) {
405 delete item;
406 }
407
408 item = new LauncherBackendItem();
409
410 item->displayName = details.value("name").toString();
411 item->icon = details.value("icon").toString();
412 item->count = details.value("count").toInt();
413 item->countVisible = details.value("countVisible").toBool();
414
415 m_itemCache.insert(appId, item);
416 m_storedApps.append(appId);
417}
418
419QVariantMap LauncherBackend::itemToVariant(const QString &appId) const
420{
421 LauncherBackendItem *item = m_itemCache.value(appId);
422 QVariantMap details;
423 details.insert("id", appId);
424 details.insert("name", item->displayName);
425 details.insert("icon", item->icon);
426 details.insert("count", item->count);
427 details.insert("countVisible", item->countVisible);
428 return details;
429}
430
431bool LauncherBackend::isDefaultsItem(const QList<QVariantMap> &apps) const
432{
433 // To differentiate between an empty list and a list that hasn't been set
434 // yet (and should thus be populated with the defaults), we use a special
435 // list of one item with the 'defaults' field set to true.
436 return (apps.size() == 1 && apps[0].value("defaults").toBool());
437}
438
439bool LauncherBackend::handleMessage(const QDBusMessage& message, const QDBusConnection& connection)
440{
441 /* Check to make sure we're getting properties on our interface */
442 if (message.type() != QDBusMessage::MessageType::MethodCallMessage) {
443 return false;
444 }
445 if (message.interface() != "org.freedesktop.DBus.Properties") {
446 return false;
447 }
448 if (message.arguments()[0].toString() != "com.canonical.Unity.Launcher.Item") {
449 return false;
450 }
451
452 /* Break down the path to just the app id */
453 QString pathtemp = message.path();
454 if (!pathtemp.startsWith("/com/canonical/Unity/Launcher/")) {
455 return false;
456 }
457 pathtemp.remove("/com/canonical/Unity/Launcher/");
458 if (pathtemp.indexOf('/') >= 0) {
459 return false;
460 }
461
462 /* Find ourselves an appid */
463 QString appid = decodeAppId(pathtemp);
464 QVariantList retval;
465
466 if (message.member() == "Get") {
467 if (message.arguments()[1].toString() == "count") {
468 retval.append(QVariant::fromValue(QDBusVariant(this->count(appid))));
469 } else if (message.arguments()[1].toString() == "countVisible") {
470 retval.append(QVariant::fromValue(QDBusVariant(this->countVisible(appid))));
471 }
472 } else if (message.member() == "Set") {
473 if (message.arguments()[1].toString() == "count") {
474 this->setCount(appid, message.arguments()[2].value<QDBusVariant>().variant().toInt());
475 } else if (message.arguments()[1].toString() == "countVisible") {
476 this->setCountVisible(appid, message.arguments()[2].value<QDBusVariant>().variant().toBool());
477 }
478 } else if (message.member() == "GetAll") {
479 retval.append(this->itemToVariant(appid));
480 } else {
481 return false;
482 }
483
484 QDBusMessage reply = message.createReply(retval);
485 return connection.send(reply);
486}
487
488QString LauncherBackend::introspect(const QString &path) const
489{
490 /* This case we should just list the nodes */
491 if (path == "/com/canonical/Unity/Launcher/" || path == "/com/canonical/Unity/Launcher") {
492 QString nodes;
493
494 Q_FOREACH(const QString &appId, m_itemCache.keys()) {
495 nodes.append("<node name=\"");
496 nodes.append(encodeAppId(appId));
497 nodes.append("\"/>\n");
498 }
499
500 return nodes;
501 }
502
503 /* Should not happen, but let's handle it */
504 if (!path.startsWith("/com/canonical/Unity/Launcher")) {
505 return "";
506 }
507
508 /* Now we should be looking at a node */
509 QString nodeiface =
510 "<interface name=\"com.canonical.Unity.Launcher.Item\">"
511 "<property name=\"count\" type=\"i\" access=\"readwrite\" />"
512 "<property name=\"countVisible\" type=\"b\" access=\"readwrite\" />"
513 "</interface>";
514 return nodeiface;
515}
516
517QString LauncherBackend::decodeAppId(const QString& path)
518{
519 QByteArray bytes = path.toUtf8();
520 QByteArray decoded;
521
522 for (int i = 0; i < bytes.size(); ++i) {
523 char chr = bytes.at(i);
524
525 if (chr == '_') {
526 QString number;
527 number.append(bytes.at(i+1));
528 number.append(bytes.at(i+2));
529
530 bool okay;
531 char newchar = number.toUInt(&okay, 16);
532 if (okay)
533 decoded.append(newchar);
534
535 i += 2;
536 } else {
537 decoded.append(chr);
538 }
539 }
540
541 return QString::fromUtf8(decoded);
542}
543
544QString LauncherBackend::encodeAppId(const QString& appId)
545{
546 QByteArray bytes = appId.toUtf8();
547 QString encoded;
548
549 for (int i = 0; i < bytes.size(); ++i) {
550 uchar chr = bytes.at(i);
551
552 if ((chr >= 'a' && chr <= 'z') ||
553 (chr >= 'A' && chr <= 'Z') ||
554 (chr >= '0' && chr <= '9'&& i != 0)) {
555 encoded.append(chr);
556 } else {
557 QString hexval = QString("_%1").arg(chr, 2, 16, QChar('0'));
558 encoded.append(hexval.toUpper());
559 }
560 }
561
562 return encoded;
563}
564
565void LauncherBackend::emitPropChangedDbus(const QString& appId, const QString& property, QVariant &value) const
566{
567 QString path("/com/canonical/Unity/Launcher/");
568 path.append(encodeAppId(appId));
569
570 QDBusMessage message = QDBusMessage::createSignal(path, "org.freedesktop.DBus.Properties", "PropertiesChanged");
571
572 QList<QVariant> arguments;
573 QVariantHash changedprops;
574 changedprops[property] = QVariant::fromValue(QDBusVariant(value));
575 QVariantList deletedprops;
576
577 arguments.append(changedprops);
578 arguments.append(deletedprops);
579
580 message.setArguments(arguments);
581
582 QDBusConnection con = QDBusConnection::sessionBus();
583 con.send(message);
584}
5850
=== removed file 'plugins/Unity/Launcher/backend/launcherbackend.h'
--- plugins/Unity/Launcher/backend/launcherbackend.h 2014-06-11 15:36:51 +0000
+++ plugins/Unity/Launcher/backend/launcherbackend.h 1970-01-01 00:00:00 +0000
@@ -1,196 +0,0 @@
1/* Copyright (C) 2013 Canonical, Ltd.
2 *
3 * Authors:
4 * Michael Zanetti <michael.zanetti@canonical.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 3.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#ifndef LAUNCHERBACKEND_H
20#define LAUNCHERBACKEND_H
21
22#include "common/quicklistentry.h"
23
24#include <QObject>
25#include <QSettings>
26#include <QStringList>
27#include <QDBusVirtualObject>
28
29class AccountsServiceDBusAdaptor;
30
31/**
32 * @brief An interface that provides all the data needed by the launcher.
33 */
34
35class LauncherBackendItem;
36class LauncherBackendTest;
37
38class LauncherBackend : public QDBusVirtualObject
39{
40 Q_OBJECT
41
42 friend LauncherBackendTest;
43
44public:
45 LauncherBackend(QObject *parent = 0);
46 virtual ~LauncherBackend();
47
48 /**
49 * @brief Returns a list of stored applications.
50 * @returns A list of application IDs.
51 */
52 QStringList storedApplications() const;
53
54 /**
55 * @brief Set the list of stored applications.
56 *
57 * Any previously stored information for apps not contained
58 * in the list any more, e.g. the pinned state, will be
59 * discarded.
60 *
61 * @param appIds The new list of stored applications.
62 */
63 void setStoredApplications(const QStringList &appIds);
64
65 /**
66 * @brief Get the full path to the .desktop file.
67 *
68 * The application does not need to be in the list of stored applications.
69 *
70 * @returns The full path to the .dekstop file.
71 */
72 QString desktopFile(const QString &appId) const;
73
74 /**
75 * @brief Get the user friendly name of an application.
76 *
77 * The application does not need to be in the list of stored applications.
78 *
79 * @param appId The ID of the application.
80 * @returns The user friendly name of the application.
81 */
82 QString displayName(const QString &appId) const;
83
84 /**
85 * @brief Get the icon of an application.
86 *
87 * The application does not need to be in the list of stored applications.
88 *
89 * @param appId The ID of the application.
90 * @returns The full path to the icon for the application.
91 */
92 QString icon(const QString &appId) const;
93
94 /**
95 * @brief Get the QuickList for an application.
96 * @param appId The ID of the application.
97 * @returns A QuickListModelInterface containing the QuickList.
98 */
99 QList<QuickListEntry> quickList(const QString &appId) const;
100
101 /**
102 * @brief Execute an action from the quickList
103 * @param appId The app ID for which the action was triggered
104 * @param the quicklist ID of the action that was triggered
105 */
106 void triggerQuickListAction(const QString &appId, const QString &entryId);
107
108 /**
109 * @brief Get the progress for the progress overlay of an application.
110 * @param appId The ID of the application.
111 * @returns The percentage of the overlay progress bar. -1 if no progress bar available.
112 */
113 int progress(const QString &appId) const;
114
115 /**
116 * @brief Get the count of the count overlay of an application.
117 * @param appId The ID of the application.
118 * @returns The number to be displayed in the overlay. -1 if no count overlay is available.
119 */
120 int count(const QString &appId) const;
121
122 /**
123 * @brief Set the count on an item
124 * @param appId The ID of the application
125 * @param count Count to show on the application
126 */
127 void setCount(const QString &appId, int count) const;
128
129 /**
130 * @brief Get whether the count should be visible
131 * @param appId The ID of the application.
132 * @returns Whether to show a count on the launcher
133 */
134 bool countVisible(const QString &appId) const;
135
136 /**
137 * @brief Set the visibility of the count item
138 * @param appId The ID of the application
139 * @param visible Whether the count should be visible
140 */
141 void setCountVisible(const QString &appId, bool visible) const;
142
143 /**
144 * @brief Sets the username for which to look up launcher items.
145 * @param username The username to use.
146 */
147 void setUser(const QString &username);
148
149 /**
150 * @brief Handle a message to an application node
151 * @param message DBus message to handle
152 * @param connection DBus connection that we're using
153 * @returns whether the message was handled
154 */
155 virtual bool handleMessage(const QDBusMessage& message, const QDBusConnection& connection);
156
157 /**
158 * @brief Get introspection information on the objects we're exporting
159 * @param path the dbus path containing the appid
160 * @returns Introspection information for that node in the tree
161 */
162 virtual QString introspect (const QString &path) const;
163
164Q_SIGNALS:
165 void quickListChanged(const QString &appId, const QList<QuickListEntry> &quickList) const;
166 void progressChanged(const QString &appId, int progress) const;
167 void countChanged(const QString &appId, int count) const;
168 void countVisibleChanged(const QString &appId, bool visible) const;
169
170private:
171 QString findDesktopFile(const QString &appId) const;
172 LauncherBackendItem* parseDesktopFile(const QString &desktopFile) const;
173
174 QVariantMap itemToVariant(const QString &appId) const;
175 void loadFromVariant(const QVariantMap &details);
176
177 bool isDefaultsItem(const QList<QVariantMap> &apps) const;
178 void syncFromAccounts();
179 void syncToAccounts();
180
181 QList<QString> m_storedApps;
182 mutable QHash<QString, LauncherBackendItem*> m_itemCache;
183
184 AccountsServiceDBusAdaptor *m_accounts;
185 QString m_user;
186
187 void emitPropChangedDbus(const QString& appId, const QString& property, QVariant &value) const;
188
189protected: /* Protected to allow testing */
190 LauncherBackendItem* getItem(const QString& appId) const;
191
192 static QString decodeAppId(const QString& path);
193 static QString encodeAppId(const QString& appId);
194};
195
196#endif // LAUNCHERBACKEND_H
1970
=== removed directory 'plugins/Unity/Launcher/common'
=== added file 'plugins/Unity/Launcher/dbusinterface.cpp'
--- plugins/Unity/Launcher/dbusinterface.cpp 1970-01-01 00:00:00 +0000
+++ plugins/Unity/Launcher/dbusinterface.cpp 2014-09-01 17:50:27 +0000
@@ -0,0 +1,232 @@
1/*
2 * Copyright 2014 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
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 Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors:
17 * Michael Zanetti <michael.zanetti@canonical.com>
18 */
19
20#include "dbusinterface.h"
21#include "launchermodel.h"
22#include "launcheritem.h"
23
24#include <QDBusArgument>
25#include <QDBusConnection>
26#include <QDBusMessage>
27#include <QDebug>
28
29DBusInterface::DBusInterface(LauncherModel *parent):
30 QDBusVirtualObject(parent),
31 m_launcherModel(parent)
32{
33 /* Set up ourselves on DBus */
34 QDBusConnection con = QDBusConnection::sessionBus();
35 if (!con.registerService("com.canonical.Unity.Launcher")) {
36 qWarning() << "Unable to register launcher name";
37 }
38 if (!con.registerVirtualObject("/com/canonical/Unity/Launcher", this, QDBusConnection::VirtualObjectRegisterOption::SubPath)) {
39 qWarning() << "Unable to register launcher object";
40 }
41}
42
43DBusInterface::~DBusInterface()
44{
45 /* Remove oursevles from DBus */
46 QDBusConnection con = QDBusConnection::sessionBus();
47 con.unregisterService("com.canonical.Unity.Launcher");
48 con.unregisterObject("/com/canonical/Unity/Launcher");
49}
50
51QString DBusInterface::introspect(const QString &path) const
52{
53 /* This case we should just list the nodes */
54 if (path == "/com/canonical/Unity/Launcher/" || path == "/com/canonical/Unity/Launcher") {
55 QString nodes;
56
57 // Add Refresh to introspect
58 nodes = "<interface name=\"com.canonical.Unity.Launcher\">"
59 "<method name=\"Refresh\"/>"
60 "</interface>";
61
62 // Add dynamic properties for launcher emblems
63 for (int i = 0; i < m_launcherModel->rowCount(); i++) {
64 nodes.append("<node name=\"");
65 nodes.append(encodeAppId(m_launcherModel->get(i)->appId()));
66 nodes.append("\"/>\n");
67 }
68 return nodes;
69 }
70
71 /* Should not happen, but let's handle it */
72 if (!path.startsWith("/com/canonical/Unity/Launcher")) {
73 return "";
74 }
75
76 /* Now we should be looking at a node */
77 QString nodeiface =
78 "<interface name=\"com.canonical.Unity.Launcher.Item\">"
79 "<property name=\"count\" type=\"i\" access=\"readwrite\" />"
80 "<property name=\"countVisible\" type=\"b\" access=\"readwrite\" />"
81 "</interface>";
82 return nodeiface;
83}
84
85
86QString DBusInterface::decodeAppId(const QString& path)
87{
88 QByteArray bytes = path.toUtf8();
89 QByteArray decoded;
90
91 for (int i = 0; i < bytes.size(); ++i) {
92 char chr = bytes.at(i);
93
94 if (chr == '_') {
95 QString number;
96 number.append(bytes.at(i+1));
97 number.append(bytes.at(i+2));
98
99 bool okay;
100 char newchar = number.toUInt(&okay, 16);
101 if (okay)
102 decoded.append(newchar);
103
104 i += 2;
105 } else {
106 decoded.append(chr);
107 }
108 }
109
110 return QString::fromUtf8(decoded);
111}
112
113QString DBusInterface::encodeAppId(const QString& appId)
114{
115 QByteArray bytes = appId.toUtf8();
116 QString encoded;
117
118 for (int i = 0; i < bytes.size(); ++i) {
119 uchar chr = bytes.at(i);
120
121 if ((chr >= 'a' && chr <= 'z') ||
122 (chr >= 'A' && chr <= 'Z') ||
123 (chr >= '0' && chr <= '9'&& i != 0)) {
124 encoded.append(chr);
125 } else {
126 QString hexval = QString("_%1").arg(chr, 2, 16, QChar('0'));
127 encoded.append(hexval.toUpper());
128 }
129 }
130
131 return encoded;
132}
133
134bool DBusInterface::handleMessage(const QDBusMessage& message, const QDBusConnection& connection)
135{
136 /* Check to make sure we're getting properties on our interface */
137 if (message.type() != QDBusMessage::MessageType::MethodCallMessage) {
138 return false;
139 }
140
141 // First handle methods of the Launcher interface
142 if (message.interface() == "com.canonical.Unity.Launcher") {
143 if (message.member() == "Refresh") {
144 QDBusMessage reply = message.createReply();
145 Q_EMIT refreshCalled();
146 return connection.send(reply);
147 }
148 }
149
150 // Now handle dynamic properties (for launcher emblems)
151 if (message.interface() != "org.freedesktop.DBus.Properties") {
152 return false;
153 }
154
155 if (message.member() != "GetAll" && message.arguments()[0].toString() != "com.canonical.Unity.Launcher.Item") {
156 return false;
157 }
158
159 /* Break down the path to just the app id */
160 QString pathtemp = message.path();
161 if (!pathtemp.startsWith("/com/canonical/Unity/Launcher/")) {
162 return false;
163 }
164 pathtemp.remove("/com/canonical/Unity/Launcher/");
165 if (pathtemp.indexOf('/') >= 0) {
166 return false;
167 }
168
169 /* Find ourselves an appid */
170 QString appid = decodeAppId(pathtemp);
171 int index = m_launcherModel->findApplication(appid);
172 LauncherItem *item = static_cast<LauncherItem*>(m_launcherModel->get(index));
173
174 QVariantList retval;
175 if (message.member() == "Get") {
176 if (!item) {
177 return false;
178 }
179 if (message.arguments()[1].toString() == "count") {
180 retval.append(QVariant::fromValue(QDBusVariant(item->count())));
181 } else if (message.arguments()[1].toString() == "countVisible") {
182 retval.append(QVariant::fromValue(QDBusVariant(item->countVisible())));
183 }
184 } else if (message.member() == "Set") {
185 if (message.arguments()[1].toString() == "count") {
186 int newCount = message.arguments()[2].toInt();
187 if (!item || newCount != item->count()) {
188 Q_EMIT countChanged(appid, newCount);
189 emitPropChangedDbus(appid, "count", QVariant(newCount));
190 }
191 } else if (message.arguments()[1].toString() == "countVisible") {
192 bool newVisible = message.arguments()[2].toBool();
193 if (!item || newVisible != item->countVisible()) {
194 Q_EMIT countVisibleChanged(appid, newVisible);
195 emitPropChangedDbus(appid, "countVisible", newVisible);
196 }
197 }
198 } else if (message.member() == "GetAll") {
199 if (item) {
200 QVariantMap all;
201 all.insert("count", item->count());
202 all.insert("countVisible", item->countVisible());
203 retval.append(all);
204 }
205 } else {
206 return false;
207 }
208
209 QDBusMessage reply = message.createReply(retval);
210 return connection.send(reply);
211}
212
213void DBusInterface::emitPropChangedDbus(const QString& appId, const QString& property, const QVariant &value)
214{
215 QString path("/com/canonical/Unity/Launcher/");
216 path.append(encodeAppId(appId));
217
218 QDBusMessage message = QDBusMessage::createSignal(path, "org.freedesktop.DBus.Properties", "PropertiesChanged");
219
220 QList<QVariant> arguments;
221 QVariantHash changedprops;
222 changedprops[property] = QVariant::fromValue(QDBusVariant(value));
223 QVariantList deletedprops;
224
225 arguments.append(changedprops);
226 arguments.append(deletedprops);
227
228 message.setArguments(arguments);
229
230 QDBusConnection con = QDBusConnection::sessionBus();
231 con.send(message);
232}
0233
=== added file 'plugins/Unity/Launcher/dbusinterface.h'
--- plugins/Unity/Launcher/dbusinterface.h 1970-01-01 00:00:00 +0000
+++ plugins/Unity/Launcher/dbusinterface.h 2014-09-01 17:50:27 +0000
@@ -0,0 +1,50 @@
1/*
2 * Copyright 2014 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
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 Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors:
17 * Michael Zanetti <michael.zanetti@canonical.com>
18 */
19
20#include "launcheritem.h"
21
22#include <QDBusVirtualObject>
23
24class LauncherModel;
25
26class DBusInterface: public QDBusVirtualObject
27{
28 Q_OBJECT
29public:
30 DBusInterface(LauncherModel *parent);
31 ~DBusInterface();
32
33 // QDBusVirtualObject implementaition
34 QString introspect (const QString &path) const override;
35 bool handleMessage(const QDBusMessage& message, const QDBusConnection& connection) override;
36
37Q_SIGNALS:
38 void countChanged(const QString &appId, int count);
39 void countVisibleChanged(const QString &appId, bool countVisible);
40 void refreshCalled();
41
42private:
43 static QString decodeAppId(const QString& path);
44 static QString encodeAppId(const QString& appId);
45
46 void emitPropChangedDbus(const QString& appId, const QString& property, const QVariant &value);
47
48 LauncherModel *m_launcherModel;
49
50};
051
=== added file 'plugins/Unity/Launcher/desktopfilehandler.cpp'
--- plugins/Unity/Launcher/desktopfilehandler.cpp 1970-01-01 00:00:00 +0000
+++ plugins/Unity/Launcher/desktopfilehandler.cpp 2014-09-01 17:50:27 +0000
@@ -0,0 +1,124 @@
1/*
2 * Copyright 2014 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
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 Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors:
17 * Michael Zanetti <michael.zanetti@canonical.com>
18 */
19
20#include "desktopfilehandler.h"
21
22#include <QStringList>
23#include <QStandardPaths>
24#include <QDir>
25#include <QSettings>
26#include <QLocale>
27
28#include <libintl.h>
29
30DesktopFileHandler::DesktopFileHandler(QObject *parent):
31 QObject(parent)
32{
33
34}
35
36QString DesktopFileHandler::findDesktopFile(const QString &appId) const
37{
38 int dashPos = -1;
39 QString helper = appId;
40
41 QStringList searchDirs = QStandardPaths::standardLocations(QStandardPaths::ApplicationsLocation);
42#ifdef LAUNCHER_TESTING
43 searchDirs << "";
44#endif
45
46 QString path;
47 do {
48 if (dashPos != -1) {
49 helper.replace(dashPos, 1, '/');
50 }
51
52 if (helper.contains("/")) {
53 path += helper.split('/').first() + '/';
54 helper.remove(QRegExp("^" + path));
55 }
56
57 Q_FOREACH(const QString &searchDirName, searchDirs) {
58 QDir searchDir(searchDirName + "/" + path);
59 Q_FOREACH(const QString &desktopFile, searchDir.entryList(QStringList() << "*.desktop")) {
60 if (desktopFile.startsWith(helper)) {
61 QFileInfo fileInfo(searchDir, desktopFile);
62 return fileInfo.absoluteFilePath();
63 }
64 }
65 }
66
67 dashPos = helper.indexOf("-");
68 } while (dashPos != -1);
69
70 return QString();
71}
72
73QString DesktopFileHandler::displayName(const QString &appId) const
74{
75 QString desktopFile = findDesktopFile(appId);
76 if (desktopFile.isEmpty()) {
77 return QString();
78 }
79
80 QSettings settings(desktopFile, QSettings::IniFormat);
81 settings.beginGroup("Desktop Entry");
82
83 // First try to find Name[xx_YY] and Name[xx] in .desktop file
84 QString locale = QLocale::system().name();
85 QString shortLocale = locale.split('_').first();
86
87 if (locale != shortLocale && settings.contains(QString("Name[%1]").arg(locale))) {
88 return settings.value(QString("Name[%1]").arg(locale)).toString();
89 }
90
91 if (settings.contains(QString("Name[%1]").arg(shortLocale))) {
92 return settings.value(QString("Name[%1]").arg(shortLocale)).toString();
93 }
94
95 // No translation found in desktop file. Get the untranslated one and have a go with gettext.
96 QString displayName = settings.value("Name").toString();
97
98 if (settings.contains("X-Ubuntu-Gettext-Domain")) {
99 const QString domain = settings.value("X-Ubuntu-Gettext-Domain").toString();
100 return dgettext(domain.toUtf8().constData(), displayName.toUtf8().constData());
101 }
102
103 return displayName;
104}
105
106QString DesktopFileHandler::icon(const QString &appId) const
107{
108 QString desktopFile = findDesktopFile(appId);
109 if (desktopFile.isEmpty()) {
110 return QString();
111 }
112
113 QSettings settings(desktopFile, QSettings::IniFormat);
114 settings.beginGroup("Desktop Entry");
115 QString iconString = settings.value("Icon").toString();
116 QString pathString = settings.value("Path").toString();
117
118 if (QFileInfo(iconString).exists()) {
119 return QFileInfo(iconString).absoluteFilePath();
120 } else if (QFileInfo(pathString + '/' + iconString).exists()) {
121 return pathString + '/' + iconString;
122 }
123 return "image://theme/" + iconString;
124}
0125
=== added file 'plugins/Unity/Launcher/desktopfilehandler.h'
--- plugins/Unity/Launcher/desktopfilehandler.h 1970-01-01 00:00:00 +0000
+++ plugins/Unity/Launcher/desktopfilehandler.h 2014-09-01 17:50:27 +0000
@@ -0,0 +1,37 @@
1/*
2 * Copyright 2014 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
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 Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors:
17 * Michael Zanetti <michael.zanetti@canonical.com>
18 */
19
20#ifndef DESKTOPFILEHANDLER_H
21#define DESKTOPFILEHANDLER_H
22
23#include <QObject>
24
25class DesktopFileHandler: public QObject
26{
27 Q_OBJECT
28public:
29 DesktopFileHandler(QObject *parent = nullptr);
30
31 QString findDesktopFile(const QString &appId) const;
32
33 QString displayName(const QString &appId) const;
34 QString icon(const QString &appId) const;
35};
36
37#endif
038
=== added file 'plugins/Unity/Launcher/gsettings.cpp'
--- plugins/Unity/Launcher/gsettings.cpp 1970-01-01 00:00:00 +0000
+++ plugins/Unity/Launcher/gsettings.cpp 2014-09-01 17:50:27 +0000
@@ -0,0 +1,76 @@
1/*
2 * Copyright 2014 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
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 Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors:
17 * Michael Zanetti <michael.zanetti@canonical.com>
18 */
19
20#include "gsettings.h"
21
22#include <QGSettings>
23#include <QVariant>
24#include <QDebug>
25
26GSettings::GSettings(QObject *parent):
27 QObject(parent)
28{
29
30}
31
32QStringList GSettings::storedApplications() const
33{
34 QStringList storedApps;
35
36 QGSettings gSettings("com.canonical.Unity.Launcher", "/com/canonical/unity/launcher/");
37
38 QString settingsKey = "items";
39
40 // If "items" doesn't contain anything yet, import unity7's "favorites"
41 if (gSettings.get(settingsKey).toStringList().isEmpty()) {
42 settingsKey = "favorites";
43 }
44
45 Q_FOREACH(const QString &entry, gSettings.get(settingsKey).toStringList()) {
46 qDebug() << "got entry" << entry;
47 if (entry.startsWith("application://")) {
48 // convert legacy entries to new world appids
49 QString appId = entry;
50 // Transform "application://foobar.desktop" to "foobar"
51 appId.remove(QRegExp("^application://"));
52 appId.remove(QRegExp(".desktop$"));
53 storedApps << appId;
54 }
55 if (entry.startsWith("appid://")) {
56 QString appId = entry;
57 appId.remove("appid://");
58 if (appId.split('/').count() == 3) {
59 // Strip current-user-version in case its there
60 appId = appId.split('/').first() + "_" + appId.split('/').at(1);
61 }
62 storedApps << appId;
63 }
64 }
65 return storedApps;
66}
67
68void GSettings::setStoredApplications(const QStringList &storedApplications)
69{
70 QGSettings gSettings("com.canonical.Unity.Launcher", "/com/canonical/unity/launcher/");
71 QStringList gSettingsList;
72 Q_FOREACH(const QString &entry, storedApplications) {
73 gSettingsList << QString("appid://%1").arg(entry);
74 }
75 gSettings.set("items", gSettingsList);
76}
077
=== added file 'plugins/Unity/Launcher/gsettings.h'
--- plugins/Unity/Launcher/gsettings.h 1970-01-01 00:00:00 +0000
+++ plugins/Unity/Launcher/gsettings.h 2014-09-01 17:50:27 +0000
@@ -0,0 +1,37 @@
1/*
2 * Copyright 2014 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
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 Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors:
17 * Michael Zanetti <michael.zanetti@canonical.com>
18 */
19
20#ifndef GSETTINGS_H
21#define GSETTINGS_H
22
23#include <QObject>
24#include <QStringList>
25
26
27class GSettings: public QObject
28{
29 Q_OBJECT
30public:
31 GSettings(QObject *parent = nullptr);
32
33 QStringList storedApplications() const;
34 void setStoredApplications(const QStringList &storedApplications);
35};
36
37#endif
038
=== modified file 'plugins/Unity/Launcher/launcheritem.cpp'
--- plugins/Unity/Launcher/launcheritem.cpp 2014-06-23 10:32:58 +0000
+++ plugins/Unity/Launcher/launcheritem.cpp 2014-09-01 17:50:27 +0000
@@ -32,6 +32,7 @@
32 m_recent(false),32 m_recent(false),
33 m_progress(-1),33 m_progress(-1),
34 m_count(0),34 m_count(0),
35 m_countVisible(false),
35 m_focused(false),36 m_focused(false),
36 m_quickList(new QuickListModel(this))37 m_quickList(new QuickListModel(this))
37{38{
@@ -54,11 +55,27 @@
54 return m_name;55 return m_name;
55}56}
5657
58void LauncherItem::setName(const QString &name)
59{
60 if (m_name != name) {
61 m_name = name;
62 Q_EMIT nameChanged(name);
63 }
64}
65
57QString LauncherItem::icon() const66QString LauncherItem::icon() const
58{67{
59 return m_icon;68 return m_icon;
60}69}
6170
71void LauncherItem::setIcon(const QString &icon)
72{
73 if (m_icon != icon) {
74 m_icon = icon;
75 Q_EMIT iconChanged(icon);
76 }
77}
78
62bool LauncherItem::pinned() const79bool LauncherItem::pinned() const
63{80{
64 return m_pinned;81 return m_pinned;
@@ -128,6 +145,19 @@
128 }145 }
129}146}
130147
148bool LauncherItem::countVisible() const
149{
150 return m_countVisible;
151}
152
153void LauncherItem::setCountVisible(bool countVisible)
154{
155 if (m_countVisible != countVisible) {
156 m_countVisible = countVisible;
157 Q_EMIT countVisibleChanged(countVisible);
158 }
159}
160
131bool LauncherItem::focused() const161bool LauncherItem::focused() const
132{162{
133 return m_focused;163 return m_focused;
134164
=== modified file 'plugins/Unity/Launcher/launcheritem.h'
--- plugins/Unity/Launcher/launcheritem.h 2014-06-11 15:36:51 +0000
+++ plugins/Unity/Launcher/launcheritem.h 2014-09-01 17:50:27 +0000
@@ -37,27 +37,27 @@
37 QString appId() const;37 QString appId() const;
38 QString name() const;38 QString name() const;
39 QString icon() const;39 QString icon() const;
40
41 bool pinned() const;40 bool pinned() const;
41 bool running() const;
42 bool recent() const;
43 int progress() const;
44 int count() const;
45 bool countVisible() const;
46 bool focused() const;
47
48 unity::shell::launcher::QuickListModelInterface *quickList() const;
49
50private:
51 void setName(const QString &name);
52 void setIcon(const QString &icon);
42 void setPinned(bool pinned);53 void setPinned(bool pinned);
43
44 bool running() const;
45 void setRunning(bool running);54 void setRunning(bool running);
46
47 bool recent() const;
48 void setRecent(bool recent);55 void setRecent(bool recent);
49
50 int progress() const;
51 void setProgress(int progress);56 void setProgress(int progress);
52
53 int count() const;
54 void setCount(int count);57 void setCount(int count);
5558 void setCountVisible(bool countVisible);
56 bool focused() const;
57 void setFocused(bool focused);59 void setFocused(bool focused);
5860
59 unity::shell::launcher::QuickListModelInterface *quickList() const;
60
61Q_SIGNALS:61Q_SIGNALS:
62 void favoriteChanged(bool favorite);62 void favoriteChanged(bool favorite);
63 void runningChanged(bool running);63 void runningChanged(bool running);
@@ -71,8 +71,11 @@
71 bool m_recent;71 bool m_recent;
72 int m_progress;72 int m_progress;
73 int m_count;73 int m_count;
74 bool m_countVisible;
74 bool m_focused;75 bool m_focused;
75 QuickListModel *m_quickList;76 QuickListModel *m_quickList;
77
78 friend class LauncherModel;
76};79};
7780
78#endif // LAUNCHERITEM_H81#endif // LAUNCHERITEM_H
7982
=== modified file 'plugins/Unity/Launcher/launchermodel.cpp'
--- plugins/Unity/Launcher/launchermodel.cpp 2014-07-29 11:35:10 +0000
+++ plugins/Unity/Launcher/launchermodel.cpp 2014-09-01 17:50:27 +0000
@@ -19,7 +19,9 @@
1919
20#include "launchermodel.h"20#include "launchermodel.h"
21#include "launcheritem.h"21#include "launcheritem.h"
22#include "backend/launcherbackend.h"22#include "gsettings.h"
23#include "desktopfilehandler.h"
24#include "dbusinterface.h"
2325
24#include <unity/shell/application/ApplicationInfoInterface.h>26#include <unity/shell/application/ApplicationInfoInterface.h>
2527
@@ -29,16 +31,24 @@
2931
30LauncherModel::LauncherModel(QObject *parent):32LauncherModel::LauncherModel(QObject *parent):
31 LauncherModelInterface(parent),33 LauncherModelInterface(parent),
32 m_backend(new LauncherBackend(this)),34 m_settings(new GSettings(this)),
35 m_dbusIface(new DBusInterface(this)),
36 m_desktopFileHandler(new DesktopFileHandler(this)),
33 m_appManager(0)37 m_appManager(0)
34{38{
35 connect(m_backend, SIGNAL(countChanged(QString,int)), SLOT(countChanged(QString,int)));39 connect(m_dbusIface, &DBusInterface::countChanged, this, &LauncherModel::countChanged);
36 connect(m_backend, SIGNAL(progressChanged(QString,int)), SLOT(progressChanged(QString,int)));40 connect(m_dbusIface, &DBusInterface::countVisibleChanged, this, &LauncherModel::countVisibleChanged);
3741 connect(m_dbusIface, &DBusInterface::refreshCalled, this, &LauncherModel::refresh);
38 Q_FOREACH (const QString &entry, m_backend->storedApplications()) {42
43 Q_FOREACH (const QString &entry, m_settings->storedApplications()) {
44 if (m_desktopFileHandler->findDesktopFile(entry).isEmpty()) {
45 qWarning() << "Couldn't find a .desktop file for" << entry << ". Skipping...";
46 continue;
47 }
48
39 LauncherItem *item = new LauncherItem(entry,49 LauncherItem *item = new LauncherItem(entry,
40 m_backend->displayName(entry),50 m_desktopFileHandler->displayName(entry),
41 m_backend->icon(entry),51 m_desktopFileHandler->icon(entry),
42 this);52 this);
43 item->setPinned(true);53 item->setPinned(true);
44 m_list.append(item);54 m_list.append(item);
@@ -72,6 +82,8 @@
72 return item->pinned();82 return item->pinned();
73 case RoleCount:83 case RoleCount:
74 return item->count();84 return item->count();
85 case RoleCountVisible:
86 return item->countVisible();
75 case RoleProgress:87 case RoleProgress:
76 return item->progress();88 return item->progress();
77 case RoleFocused:89 case RoleFocused:
@@ -138,10 +150,16 @@
138 if (index == -1) {150 if (index == -1) {
139 index = m_list.count();151 index = m_list.count();
140 }152 }
153
154 if (m_desktopFileHandler->findDesktopFile(appId).isEmpty()) {
155 qWarning() << "Can't pin this application, there is no .destkop file available.";
156 return;
157 }
158
141 beginInsertRows(QModelIndex(), index, index);159 beginInsertRows(QModelIndex(), index, index);
142 LauncherItem *item = new LauncherItem(appId,160 LauncherItem *item = new LauncherItem(appId,
143 m_backend->displayName(appId),161 m_desktopFileHandler->displayName(appId),
144 m_backend->icon(appId),162 m_desktopFileHandler->icon(appId),
145 this);163 this);
146 item->setPinned(true);164 item->setPinned(true);
147 m_list.insert(index, item);165 m_list.insert(index, item);
@@ -194,25 +212,27 @@
194212
195 // Nope, we don't know this action, let the backend forward it to the application213 // Nope, we don't know this action, let the backend forward it to the application
196 } else {214 } else {
197 m_backend->triggerQuickListAction(appId, actionId);215 // TODO: forward quicklist action to app, possibly via m_dbusIface
198 }216 }
199 }217 }
200}218}
201219
202void LauncherModel::setUser(const QString &username)220void LauncherModel::setUser(const QString &username)
203{221{
204 m_backend->setUser(username);222 Q_UNUSED(username)
223 qWarning() << "This backend doesn't support multiple users";
205}224}
206225
207QString LauncherModel::getUrlForAppId(const QString &appId) const226QString LauncherModel::getUrlForAppId(const QString &appId) const
208{227{
209 // appId is either an appId or a legacy app name. Let's find out which228 // appId is either an appId or a legacy app name. Let's find out which
210 if (appId.isEmpty())229 if (appId.isEmpty()) {
211 return QString();230 return QString();
231 }
212232
213 QString df = m_backend->desktopFile(appId + ".desktop");233 if (!appId.contains("_")) {
214 if (!df.isEmpty())
215 return "application:///" + appId + ".desktop";234 return "application:///" + appId + ".desktop";
235 }
216236
217 QStringList parts = appId.split('_');237 QStringList parts = appId.split('_');
218 QString package = parts.value(0);238 QString package = parts.value(0);
@@ -272,7 +292,7 @@
272 appIds << item->appId();292 appIds << item->appId();
273 }293 }
274 }294 }
275 m_backend->setStoredApplications(appIds);295 m_settings->setStoredApplications(appIds);
276}296}
277297
278int LauncherModel::findApplication(const QString &appId)298int LauncherModel::findApplication(const QString &appId)
@@ -307,6 +327,53 @@
307 }327 }
308}328}
309329
330void LauncherModel::countVisibleChanged(const QString &appId, int countVisible)
331{
332 int idx = findApplication(appId);
333 if (idx >= 0) {
334 LauncherItem *item = m_list.at(idx);
335 item->setCountVisible(countVisible);
336 Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleCountVisible);
337
338 // If countVisible goes to false, and the item is neither pinned nor recent we can drop it
339 if (!countVisible && !item->pinned() && !item->recent()) {
340 beginRemoveRows(QModelIndex(), idx, idx);
341 m_list.takeAt(idx)->deleteLater();
342 endRemoveRows();
343 }
344 } else {
345 // Need to create a new LauncherItem and show the highlight
346 if (countVisible && !m_desktopFileHandler->findDesktopFile(appId).isEmpty()) {
347 LauncherItem *item = new LauncherItem(appId,
348 m_desktopFileHandler->displayName(appId),
349 m_desktopFileHandler->icon(appId));
350 item->setCountVisible(true);
351 beginInsertRows(QModelIndex(), m_list.count(), m_list.count());
352 m_list.append(item);
353 endInsertRows();
354 }
355 }
356}
357
358void LauncherModel::refresh()
359{
360 QList<LauncherItem*> toBeRemoved;
361 Q_FOREACH (LauncherItem* item, m_list) {
362 if (m_desktopFileHandler->findDesktopFile(item->appId()).isEmpty()) {
363 toBeRemoved << item;
364 } else {
365 int idx = m_list.indexOf(item);
366 item->setName(m_desktopFileHandler->displayName(item->appId()));
367 item->setIcon(m_desktopFileHandler->icon(item->appId()));
368 Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleName << RoleIcon);
369 }
370 }
371
372 Q_FOREACH (LauncherItem* item, toBeRemoved) {
373 requestRemove(item->appId());
374 }
375}
376
310void LauncherModel::applicationAdded(const QModelIndex &parent, int row)377void LauncherModel::applicationAdded(const QModelIndex &parent, int row)
311{378{
312 Q_UNUSED(parent);379 Q_UNUSED(parent);
313380
=== modified file 'plugins/Unity/Launcher/launchermodel.h'
--- plugins/Unity/Launcher/launchermodel.h 2014-06-11 15:36:51 +0000
+++ plugins/Unity/Launcher/launchermodel.h 2014-09-01 17:50:27 +0000
@@ -20,15 +20,15 @@
20#ifndef LAUNCHERMODEL_H20#ifndef LAUNCHERMODEL_H
21#define LAUNCHERMODEL_H21#define LAUNCHERMODEL_H
2222
23// unity-api
24#include <unity/shell/launcher/LauncherModelInterface.h>23#include <unity/shell/launcher/LauncherModelInterface.h>
25#include <unity/shell/application/ApplicationManagerInterface.h>24#include <unity/shell/application/ApplicationManagerInterface.h>
2625
27// Qt
28#include <QAbstractListModel>26#include <QAbstractListModel>
2927
30class LauncherItem;28class LauncherItem;
31class LauncherBackend;29class GSettings;
30class DesktopFileHandler;
31class DBusInterface;
3232
33using namespace unity::shell::launcher;33using namespace unity::shell::launcher;
34using namespace unity::shell::application;34using namespace unity::shell::application;
@@ -41,14 +41,13 @@
41 LauncherModel(QObject *parent = 0);41 LauncherModel(QObject *parent = 0);
42 ~LauncherModel();42 ~LauncherModel();
4343
44 int rowCount(const QModelIndex &parent) const;44 int rowCount(const QModelIndex &parent = QModelIndex()) const;
4545
46 QVariant data(const QModelIndex &index, int role) const;46 QVariant data(const QModelIndex &index, int role) const;
4747
48 Q_INVOKABLE unity::shell::launcher::LauncherItemInterface* get(int index) const;48 Q_INVOKABLE unity::shell::launcher::LauncherItemInterface* get(int index) const;
49 Q_INVOKABLE void move(int oldIndex, int newIndex);49 Q_INVOKABLE void move(int oldIndex, int newIndex);
50 Q_INVOKABLE void pin(const QString &appId, int index = -1);50 Q_INVOKABLE void pin(const QString &appId, int index = -1);
51 Q_INVOKABLE void requestRemove(const QString &appId);
52 Q_INVOKABLE void quickListActionInvoked(const QString &appId, int actionIndex);51 Q_INVOKABLE void quickListActionInvoked(const QString &appId, int actionIndex);
53 Q_INVOKABLE void setUser(const QString &username);52 Q_INVOKABLE void setUser(const QString &username);
54 Q_INVOKABLE QString getUrlForAppId(const QString &appId) const;53 Q_INVOKABLE QString getUrlForAppId(const QString &appId) const;
@@ -56,13 +55,19 @@
56 unity::shell::application::ApplicationManagerInterface* applicationManager() const;55 unity::shell::application::ApplicationManagerInterface* applicationManager() const;
57 void setApplicationManager(unity::shell::application::ApplicationManagerInterface *appManager);56 void setApplicationManager(unity::shell::application::ApplicationManagerInterface *appManager);
5857
58 int findApplication(const QString &appId);
59
60public Q_SLOTS:
61 void requestRemove(const QString &appId);
62
59private:63private:
60 void storeAppList();64 void storeAppList();
61 int findApplication(const QString &appId);
6265
63private Q_SLOTS:66private Q_SLOTS:
67 void countChanged(const QString &appId, int count);
68 void countVisibleChanged(const QString &appId, int count);
64 void progressChanged(const QString &appId, int progress);69 void progressChanged(const QString &appId, int progress);
65 void countChanged(const QString &appId, int count);70 void refresh();
6671
67 void applicationAdded(const QModelIndex &parent, int row);72 void applicationAdded(const QModelIndex &parent, int row);
68 void applicationRemoved(const QModelIndex &parent, int row);73 void applicationRemoved(const QModelIndex &parent, int row);
@@ -70,7 +75,11 @@
7075
71private:76private:
72 QList<LauncherItem*> m_list;77 QList<LauncherItem*> m_list;
73 LauncherBackend *m_backend;78
79 GSettings *m_settings;
80 DBusInterface *m_dbusIface;
81 DesktopFileHandler *m_desktopFileHandler;
82
74 ApplicationManagerInterface *m_appManager;83 ApplicationManagerInterface *m_appManager;
75};84};
7685
7786
=== renamed file 'plugins/Unity/Launcher/common/quicklistentry.cpp' => 'plugins/Unity/Launcher/quicklistentry.cpp'
=== renamed file 'plugins/Unity/Launcher/common/quicklistentry.h' => 'plugins/Unity/Launcher/quicklistentry.h'
=== modified file 'plugins/Unity/Launcher/quicklistmodel.h'
--- plugins/Unity/Launcher/quicklistmodel.h 2013-07-24 10:42:00 +0000
+++ plugins/Unity/Launcher/quicklistmodel.h 2014-09-01 17:50:27 +0000
@@ -20,7 +20,7 @@
20#ifndef QUICKLISTMODEL_H20#ifndef QUICKLISTMODEL_H
21#define QUICKLISTMODEL_H21#define QUICKLISTMODEL_H
2222
23#include "common/quicklistentry.h"23#include "quicklistentry.h"
2424
25#include <unity/shell/launcher/QuickListModelInterface.h>25#include <unity/shell/launcher/QuickListModelInterface.h>
2626
2727
=== modified file 'qml/Dash/DashApplication.qml'
--- qml/Dash/DashApplication.qml 2014-08-07 15:29:12 +0000
+++ qml/Dash/DashApplication.qml 2014-09-01 17:50:27 +0000
@@ -24,12 +24,6 @@
2424
25 useDeprecatedToolbar: false25 useDeprecatedToolbar: false
2626
27 Binding {
28 target: i18n
29 property: "domain"
30 value: "unity8"
31 }
32
33 Dash {27 Dash {
34 anchors.fill: parent28 anchors.fill: parent
35 }29 }
3630
=== modified file 'qml/Launcher/LauncherDelegate.qml'
--- qml/Launcher/LauncherDelegate.qml 2014-08-11 15:54:10 +0000
+++ qml/Launcher/LauncherDelegate.qml 2014-09-01 17:50:27 +0000
@@ -21,7 +21,8 @@
21 id: root21 id: root
2222
23 property string iconName23 property string iconName
24 property int count: -124 property int count: 0
25 property bool countVisible: false
25 property int progress: -126 property int progress: -1
26 property bool itemFocused: false27 property bool itemFocused: false
27 property real maxAngle: 028 property real maxAngle: 0
@@ -80,7 +81,7 @@
80 width: Math.min(root.itemWidth, Math.max(units.gu(2), countLabel.implicitWidth + units.gu(1)))81 width: Math.min(root.itemWidth, Math.max(units.gu(2), countLabel.implicitWidth + units.gu(1)))
81 height: units.gu(2)82 height: units.gu(2)
82 color: UbuntuColors.orange83 color: UbuntuColors.orange
83 visible: root.count > 084 visible: root.countVisible
84 borderSource: "none"85 borderSource: "none"
8586
86 Label {87 Label {
8788
=== modified file 'qml/Launcher/LauncherPanel.qml'
--- qml/Launcher/LauncherPanel.qml 2014-08-12 16:21:21 +0000
+++ qml/Launcher/LauncherPanel.qml 2014-09-01 17:50:27 +0000
@@ -156,6 +156,7 @@
156 height: itemHeight156 height: itemHeight
157 iconName: model.icon157 iconName: model.icon
158 count: model.count158 count: model.count
159 countVisible: model.countVisible
159 progress: model.progress160 progress: model.progress
160 clipCorner: model.pinned161 clipCorner: model.pinned
161 itemFocused: model.focused162 itemFocused: model.focused
162163
=== modified file 'qml/Notifications/Notification.qml'
--- qml/Notifications/Notification.qml 2014-08-21 08:04:40 +0000
+++ qml/Notifications/Notification.qml 2014-09-01 17:50:27 +0000
@@ -15,7 +15,7 @@
15 */15 */
1616
17import QtQuick 2.017import QtQuick 2.0
18import QtMultimedia 5.018//import QtMultimedia 5.0
19import Ubuntu.Components 1.119import Ubuntu.Components 1.1
20import Unity.Notifications 1.020import Unity.Notifications 1.0
21import QMenuModel 0.121import QMenuModel 0.1
@@ -76,10 +76,10 @@
76 return result;76 return result;
77 }77 }
7878
79 Audio {79 Item {
80 id: sound80 id: sound
81 objectName: "sound"81 objectName: "sound"
82 source: hints["suppress-sound"] != "true" && hints["sound-file"] != undefined ? hints["sound-file"] : ""82 //source: hints["suppress-sound"] != "true" && hints["sound-file"] != undefined ? hints["sound-file"] : ""
83 }83 }
8484
85 // FIXME: using onCompleted because of LP: #1354406 workaround, has to be onOpacityChanged really85 // FIXME: using onCompleted because of LP: #1354406 workaround, has to be onOpacityChanged really
8686
=== modified file 'qml/Shell.qml'
--- qml/Shell.qml 2014-08-28 20:06:18 +0000
+++ qml/Shell.qml 2014-09-01 17:50:27 +0000
@@ -592,12 +592,6 @@
592 }592 }
593 }593 }
594594
595 Binding {
596 target: i18n
597 property: "domain"
598 value: "unity8"
599 }
600
601 Dialogs {595 Dialogs {
602 id: dialogs596 id: dialogs
603 anchors.fill: parent597 anchors.fill: parent
604598
=== modified file 'run.sh'
--- run.sh 2014-07-28 12:56:55 +0000
+++ run.sh 2014-09-01 17:50:27 +0000
@@ -88,6 +88,7 @@
88 echo "Unity8 is already running, please stop it first"88 echo "Unity8 is already running, please stop it first"
89 exit 189 exit 1
90 fi90 fi
91 echo starting with $LD_LIBRARY_PATH
91 /sbin/initctl start unity8 BINARY="`readlink -f $QML_PHONE_SHELL_PATH` $QML_PHONE_SHELL_ARGS $@" QML2_IMPORT_PATH=$QML2_IMPORT_PATH LD_LIBRARY_PATH=$LD_LIBRARY_PATH92 /sbin/initctl start unity8 BINARY="`readlink -f $QML_PHONE_SHELL_PATH` $QML_PHONE_SHELL_ARGS $@" QML2_IMPORT_PATH=$QML2_IMPORT_PATH LD_LIBRARY_PATH=$LD_LIBRARY_PATH
92 tailf -n 0 ~/.cache/upstart/unity8.log93 tailf -n 0 ~/.cache/upstart/unity8.log
93fi94fi
9495
=== modified file 'run_on_device.sh'
--- run_on_device.sh 2014-08-07 15:29:12 +0000
+++ run_on_device.sh 2014-09-01 17:50:27 +0000
@@ -7,7 +7,7 @@
7TARGET_IP=${TARGET_IP-127.0.0.1}7TARGET_IP=${TARGET_IP-127.0.0.1}
8TARGET_SSH_PORT=${TARGET_SSH_PORT-2222}8TARGET_SSH_PORT=${TARGET_SSH_PORT-2222}
9TARGET_DEBUG_PORT=37689TARGET_DEBUG_PORT=3768
10RUN_OPTIONS=-qmljsdebugger=port:$TARGET_DEBUG_PORT10RUN_OPTIONS=#-qmljsdebugger=port:$TARGET_DEBUG_PORT
11SETUP=false11SETUP=false
12GDB=false12GDB=false
13PINLOCK=false13PINLOCK=false
1414
=== modified file 'src/Dash/main.cpp'
--- src/Dash/main.cpp 2014-08-12 13:49:11 +0000
+++ src/Dash/main.cpp 2014-09-01 17:50:27 +0000
@@ -68,6 +68,7 @@
68 }68 }
6969
70 bindtextdomain("unity8", translationDirectory().toUtf8().data());70 bindtextdomain("unity8", translationDirectory().toUtf8().data());
71 textdomain("unity8");
7172
72 QQuickView* view = new QQuickView();73 QQuickView* view = new QQuickView();
73 view->setResizeMode(QQuickView::SizeRootObjectToView);74 view->setResizeMode(QQuickView::SizeRootObjectToView);
7475
=== modified file 'src/main.cpp'
--- src/main.cpp 2014-07-31 10:52:30 +0000
+++ src/main.cpp 2014-09-01 17:50:27 +0000
@@ -108,6 +108,7 @@
108 }108 }
109109
110 bindtextdomain("unity8", translationDirectory().toUtf8().data());110 bindtextdomain("unity8", translationDirectory().toUtf8().data());
111 textdomain("unity8");
111112
112 QQuickView* view = new QQuickView();113 QQuickView* view = new QQuickView();
113 view->setResizeMode(QQuickView::SizeRootObjectToView);114 view->setResizeMode(QQuickView::SizeRootObjectToView);
114115
=== modified file 'tests/mocks/Unity/Launcher/CMakeLists.txt'
--- tests/mocks/Unity/Launcher/CMakeLists.txt 2014-06-17 18:23:44 +0000
+++ tests/mocks/Unity/Launcher/CMakeLists.txt 2014-09-01 17:50:27 +0000
@@ -1,4 +1,4 @@
1pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=3)1pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=4)
22
3include_directories(3include_directories(
4 ${CMAKE_CURRENT_SOURCE_DIR}4 ${CMAKE_CURRENT_SOURCE_DIR}
55
=== modified file 'tests/mocks/Unity/Launcher/MockLauncherItem.cpp'
--- tests/mocks/Unity/Launcher/MockLauncherItem.cpp 2014-06-17 18:23:44 +0000
+++ tests/mocks/Unity/Launcher/MockLauncherItem.cpp 2014-09-01 17:50:27 +0000
@@ -131,6 +131,19 @@
131 }131 }
132}132}
133133
134bool MockLauncherItem::countVisible() const
135{
136 return m_countVisible;
137}
138
139void MockLauncherItem::setCountVisible(bool countVisible)
140{
141 if (m_countVisible != countVisible) {
142 m_countVisible = countVisible;
143 Q_EMIT countVisibleChanged(countVisible);
144 }
145}
146
134bool MockLauncherItem::focused() const147bool MockLauncherItem::focused() const
135{148{
136 return m_focused;149 return m_focused;
137150
=== modified file 'tests/mocks/Unity/Launcher/MockLauncherItem.h'
--- tests/mocks/Unity/Launcher/MockLauncherItem.h 2013-09-04 22:44:01 +0000
+++ tests/mocks/Unity/Launcher/MockLauncherItem.h 2014-09-01 17:50:27 +0000
@@ -52,6 +52,9 @@
52 int count() const;52 int count() const;
53 void setCount(int count);53 void setCount(int count);
5454
55 bool countVisible() const;
56 void setCountVisible(bool countVisible);
57
55 bool focused() const;58 bool focused() const;
56 void setFocused(bool focused);59 void setFocused(bool focused);
5760
@@ -67,6 +70,7 @@
67 bool m_recent;70 bool m_recent;
68 int m_progress;71 int m_progress;
69 int m_count;72 int m_count;
73 bool m_countVisible;
70 bool m_focused;74 bool m_focused;
71 MockQuickListModel *m_quickList;75 MockQuickListModel *m_quickList;
72};76};
7377
=== modified file 'tests/plugins/Unity/Launcher/CMakeLists.txt'
--- tests/plugins/Unity/Launcher/CMakeLists.txt 2014-08-18 12:45:32 +0000
+++ tests/plugins/Unity/Launcher/CMakeLists.txt 2014-09-01 17:50:27 +0000
@@ -1,12 +1,11 @@
1pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)1pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
2pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=3)2pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=4)
3pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=3)3pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=3)
44
5include_directories(5include_directories(
6 ${CMAKE_CURRENT_BINARY_DIR}6 ${CMAKE_CURRENT_BINARY_DIR}
7 ${CMAKE_SOURCE_DIR}/plugins/AccountsService7 ${CMAKE_SOURCE_DIR}/plugins/AccountsService
8 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher8 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher
9 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/backend
10 ${GSETTINGS_QT_INCLUDE_DIRS}9 ${GSETTINGS_QT_INCLUDE_DIRS}
11 )10 )
1211
@@ -14,35 +13,21 @@
14add_definitions(-DSRCDIR="${CMAKE_CURRENT_SOURCE_DIR}")13add_definitions(-DSRCDIR="${CMAKE_CURRENT_SOURCE_DIR}")
15add_definitions(-DLAUNCHER_TESTING)14add_definitions(-DLAUNCHER_TESTING)
1615
17### LauncherBackendTest16### LauncherModelTest
18set(testBackendCommand dbus-test-runner --task ${CMAKE_CURRENT_BINARY_DIR}/launcherbackendtestExec17set(testModelCommand dbus-test-runner --task ${CMAKE_CURRENT_BINARY_DIR}/launchermodeltestExec
19 --parameter -o --parameter ${CMAKE_BINARY_DIR}/launcherbackendtest.xml,xunitxml18 --parameter -o --parameter ${CMAKE_BINARY_DIR}/launchermodeltest.xml,xunitxml
20 --parameter -o --parameter -,txt)19 --parameter -o --parameter -,txt)
21add_test(NAME launcherbackendtest COMMAND ${testBackendCommand})
22add_custom_target(launcherbackendtest ${testBackendCommand})
23add_executable(launcherbackendtestExec
24 launcherbackendtest.cpp
25 ${CMAKE_SOURCE_DIR}/plugins/AccountsService/AccountsServiceDBusAdaptor.cpp
26 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/backend/launcherbackend.cpp
27 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/common/quicklistentry.cpp
28 )
29target_link_libraries(launcherbackendtestExec ${GSETTINGS_QT_LDFLAGS})
30qt5_use_modules(launcherbackendtestExec Test Core DBus)
31
32### LauncherModelTest
33set(testModelCommand ${CMAKE_CURRENT_BINARY_DIR}/launchermodeltestExec
34 -o ${CMAKE_BINARY_DIR}/launchermodeltest.xml,xunitxml
35 -o -,txt)
36add_test(NAME launchermodeltest COMMAND ${testModelCommand})20add_test(NAME launchermodeltest COMMAND ${testModelCommand})
37add_custom_target(launchermodeltest ${testModelCommand})21add_custom_target(launchermodeltest ${testModelCommand})
38add_executable(launchermodeltestExec22add_executable(launchermodeltestExec
39 launchermodeltest.cpp23 launchermodeltest.cpp
24 gsettings.cpp
40 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/launchermodel.cpp25 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/launchermodel.cpp
41 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/launcheritem.cpp26 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/launcheritem.cpp
42 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/quicklistmodel.cpp27 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/quicklistmodel.cpp
43 ${CMAKE_SOURCE_DIR}/plugins/AccountsService/AccountsServiceDBusAdaptor.cpp28 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/dbusinterface.cpp
44 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/backend/launcherbackend.cpp29 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/desktopfilehandler.cpp
45 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/common/quicklistentry.cpp30 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/quicklistentry.cpp
46 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherItemInterface.h31 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherItemInterface.h
47 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherModelInterface.h32 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherModelInterface.h
48 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/QuickListModelInterface.h33 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/QuickListModelInterface.h
@@ -50,7 +35,7 @@
50 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h35 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h
51 )36 )
52target_link_libraries(launchermodeltestExec ${GSETTINGS_QT_LDFLAGS})37target_link_libraries(launchermodeltestExec ${GSETTINGS_QT_LDFLAGS})
53qt5_use_modules(launchermodeltestExec Test Core DBus)38qt5_use_modules(launchermodeltestExec Test Core DBus Xml)
5439
55# copy .desktop files into build directory for shadow builds40# copy .desktop files into build directory for shadow builds
56file(GLOB DESKTOP_FILES *.desktop)41file(GLOB DESKTOP_FILES *.desktop)
5742
=== added file 'tests/plugins/Unity/Launcher/gsettings.cpp'
--- tests/plugins/Unity/Launcher/gsettings.cpp 1970-01-01 00:00:00 +0000
+++ tests/plugins/Unity/Launcher/gsettings.cpp 2014-09-01 17:50:27 +0000
@@ -0,0 +1,40 @@
1/*
2 * Copyright 2014 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
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 Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors:
17 * Michael Zanetti <michael.zanetti@canonical.com>
18 */
19
20#include "gsettings.h"
21
22#include <QDebug>
23
24// This is a mock implementation to not touch GSettings for real during tests
25
26GSettings::GSettings(QObject *parent):
27 QObject(parent)
28{
29
30}
31
32QStringList GSettings::storedApplications() const
33{
34 return QStringList();
35}
36
37void GSettings::setStoredApplications(const QStringList &storedApplications)
38{
39 Q_UNUSED(storedApplications)
40}
041
=== added file 'tests/plugins/Unity/Launcher/gsettings.h'
--- tests/plugins/Unity/Launcher/gsettings.h 1970-01-01 00:00:00 +0000
+++ tests/plugins/Unity/Launcher/gsettings.h 2014-09-01 17:50:27 +0000
@@ -0,0 +1,38 @@
1/*
2 * Copyright 2014 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
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 Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors:
17 * Michael Zanetti <michael.zanetti@canonical.com>
18 */
19
20#ifndef GSETTINGS_H
21#define GSETTINGS_H
22
23#include <QObject>
24#include <QStringList>
25
26// This is a mock implementation to not touch GSettings for real during tests
27
28class GSettings: public QObject
29{
30 Q_OBJECT
31public:
32 GSettings(QObject *parent = nullptr);
33
34 QStringList storedApplications() const;
35 void setStoredApplications(const QStringList &storedApplications);
36};
37
38#endif
039
=== removed file 'tests/plugins/Unity/Launcher/launcherbackendtest.cpp'
--- tests/plugins/Unity/Launcher/launcherbackendtest.cpp 2014-04-29 14:19:41 +0000
+++ tests/plugins/Unity/Launcher/launcherbackendtest.cpp 1970-01-01 00:00:00 +0000
@@ -1,239 +0,0 @@
1/*
2 * Copyright 2013 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
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 Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors:
17 * Michael Terry <michael.terry@canonical.com>
18 */
19
20#include "launcherbackend.h"
21
22#include <QtTest>
23#include <QDBusConnection>
24#include <QDBusMessage>
25#include <QDBusVariant>
26
27
28class LauncherBackendTest : public QObject
29{
30 Q_OBJECT
31
32private Q_SLOTS:
33
34 void initTestCase()
35 {
36 // As we want to test absolute file paths in the .desktop file, we need to modify the
37 // sample file and replace the path in there with the current build dir.
38 QSettings settings(QDir::currentPath() + "/click-icon.desktop", QSettings::IniFormat);
39 settings.setValue("Desktop Entry/Path", QDir::currentPath());
40 }
41
42 void testFileNames()
43 {
44 LauncherBackend backend;
45
46 backend.setStoredApplications(QStringList() << "rel-icon" << "abs-icon" << "invalid");
47 QCOMPARE(backend.storedApplications(), QStringList() << "rel-icon" << "abs-icon");
48 }
49
50 void testIcon_data() {
51 QTest::addColumn<QString>("appId");
52 QTest::addColumn<QString>("expectedIcon");
53
54 // Needs to expand a relative icon to the absolute path
55 QTest::newRow("relative icon path") << "rel-icon" << QDir::currentPath() + "/rel-icon.svg";
56
57 // In case an icon is not found on disk, it needs to fallback on image://theme/ for it
58 QTest::newRow("fallback on theme") << "abs-icon" << "image://theme//path/to/icon.png";
59
60 // Click packages have a relative icon path but an absolute path as a separate entry
61 QTest::newRow("click package icon") << "click-icon" << QDir::currentPath() + "/click-icon.svg";
62 }
63
64 void testIcon() {
65 QFETCH(QString, appId);
66 QFETCH(QString, expectedIcon);
67
68 LauncherBackend backend;
69 backend.setStoredApplications(QStringList() << appId);
70
71 QCOMPARE(backend.icon(appId), expectedIcon);
72 }
73
74 void testGetItem_data() {
75 QTest::addColumn<QString>("appId");
76 QTest::addColumn<bool>("exists");
77
78 QTest::newRow("Exists") << "rel-icon" << true;
79 QTest::newRow("Doesn't Exist") << "does-not-exist" << false;
80 }
81
82 void testGetItem() {
83 QFETCH(QString, appId);
84 QFETCH(bool, exists);
85
86 LauncherBackend backend;
87 auto item = backend.getItem(appId);
88
89 if (exists) {
90 QVERIFY(item != nullptr);
91 } else {
92 QVERIFY(item == nullptr);
93 }
94 }
95
96 void testCount_data() {
97 QTest::addColumn<QString>("appId");
98 QTest::addColumn<bool>("setCount");
99 QTest::addColumn<int>("inCount");
100 QTest::addColumn<bool>("countVisible");
101 QTest::addColumn<int>("expectedCount");
102
103 /* Get baseline data on things working */
104 QTest::newRow("Baseline") << "rel-icon" << false << 0 << false << -1;
105
106 /* Valid count, but not visible */
107 QTest::newRow("Not visible") << "rel-icon" << true << 42 << false << -1;
108
109 /* Turn it on */
110 QTest::newRow("Visible Count") << "rel-icon" << true << 42 << true << 42;
111
112 /* Invalide app to load */
113 QTest::newRow("Invalid App ID") << "this-app-doesnt-exist" << true << 42 << true << -1;
114 }
115
116 void testCount() {
117 QFETCH(QString, appId);
118 QFETCH(bool, setCount);
119 QFETCH(int, inCount);
120 QFETCH(bool, countVisible);
121 QFETCH(int, expectedCount);
122
123 LauncherBackend backend;
124
125 if (setCount)
126 backend.setCount(appId, inCount);
127 backend.setCountVisible(appId, countVisible);
128
129 QCOMPARE(backend.count(appId), expectedCount);
130 }
131
132 void testDbusName_data() {
133 QTest::addColumn<QString>("decoded");
134 QTest::addColumn<QString>("encoded");
135
136 /* Passthrough test */
137 QTest::newRow("Passthrough") << "fine" << "fine";
138
139 /* Number as first characeter */
140 QTest::newRow("Number first") << "31337" << "_331337";
141
142 /* Underscore test */
143 QTest::newRow("Underscore test") << "this_is_c_style_namespacing" << "this_5Fis_5Fc_5Fstyle_5Fnamespacing";
144
145 /* Hyphen test */
146 QTest::newRow("Hyphen test") << "typical-application" << "typical_2Dapplication";
147
148 /* Japanese test */
149 QTest::newRow("日本語 test") << "日本語" << "_E6_97_A5_E6_9C_AC_E8_AA_9E";
150 }
151
152 void testDbusName() {
153 QFETCH(QString, decoded);
154 QFETCH(QString, encoded);
155
156 QString encodeOut = LauncherBackend::encodeAppId(decoded);
157 QCOMPARE(encoded, encodeOut);
158
159 QString decodeOut = LauncherBackend::decodeAppId(encoded);
160 QCOMPARE(decoded, decodeOut);
161 }
162
163 void testDbusIface_data() {
164 QTest::addColumn<QString>("appId");
165 QTest::addColumn<bool>("setCount");
166 QTest::addColumn<int>("inCount");
167 QTest::addColumn<bool>("countVisible");
168 QTest::addColumn<int>("expectedCount");
169
170 /* Get baseline data on things working */
171 QTest::newRow("Baseline") << "rel-icon" << false << 0 << false << -1;
172
173 /* Turn it on */
174 QTest::newRow("Visible Count") << "rel-icon" << true << 42 << true << 42;
175
176 /* Invalide app to load */
177 QTest::newRow("Invalid App ID") << "this-app-doesnt-exist" << true << 42 << true << -1;
178 }
179
180 void testDbusIface() {
181 QFETCH(QString, appId);
182 QFETCH(bool, setCount);
183 QFETCH(int, inCount);
184 QFETCH(bool, countVisible);
185 QFETCH(int, expectedCount);
186
187 QDBusConnection con = QDBusConnection::sessionBus();
188 QDBusMessage message;
189 QDBusMessage reply;
190
191 LauncherBackend backend;
192
193 if (setCount) {
194 message = QDBusMessage::createMethodCall("com.canonical.Unity.Launcher",
195 "/com/canonical/Unity/Launcher/" + LauncherBackend::encodeAppId(appId),
196 "org.freedesktop.DBus.Properties",
197 "Set");
198 QVariantList cargs;
199 cargs.append(QString("com.canonical.Unity.Launcher.Item"));
200 cargs.append(QString("count"));
201 cargs.append(QVariant::fromValue(QDBusVariant(inCount)));
202
203 message.setArguments(cargs);
204 reply = con.call(message);
205 QCOMPARE(reply.type(), QDBusMessage::ReplyMessage);
206 }
207
208 /* Set countVisible */
209 message = QDBusMessage::createMethodCall("com.canonical.Unity.Launcher",
210 "/com/canonical/Unity/Launcher/" + LauncherBackend::encodeAppId(appId),
211 "org.freedesktop.DBus.Properties",
212 "Set");
213 QVariantList cvargs;
214 cvargs.append(QString("com.canonical.Unity.Launcher.Item"));
215 cvargs.append(QString("countVisible"));
216 cvargs.append(QVariant::fromValue(QDBusVariant(countVisible)));
217
218 message.setArguments(cvargs);
219 reply = con.call(message);
220 QCOMPARE(reply.type(), QDBusMessage::ReplyMessage);
221
222 /* Get value */
223 message = QDBusMessage::createMethodCall("com.canonical.Unity.Launcher",
224 "/com/canonical/Unity/Launcher/" + LauncherBackend::encodeAppId(appId),
225 "org.freedesktop.DBus.Properties",
226 "Get");
227 QVariantList getargs;
228 getargs.append(QString("com.canonical.Unity.Launcher.Item"));
229 getargs.append(QString("count"));
230
231 message.setArguments(getargs);
232 reply = con.call(message);
233 QCOMPARE(reply.type(), QDBusMessage::ReplyMessage);
234 QCOMPARE(reply.arguments()[0].value<QDBusVariant>().variant().toInt(), expectedCount);
235 }
236};
237
238QTEST_GUILESS_MAIN(LauncherBackendTest)
239#include "launcherbackendtest.moc"
2400
=== modified file 'tests/plugins/Unity/Launcher/launchermodeltest.cpp'
--- tests/plugins/Unity/Launcher/launchermodeltest.cpp 2014-08-13 19:50:09 +0000
+++ tests/plugins/Unity/Launcher/launchermodeltest.cpp 2014-09-01 17:50:27 +0000
@@ -23,8 +23,12 @@
2323
24#include "launcheritem.h"24#include "launcheritem.h"
25#include "launchermodel.h"25#include "launchermodel.h"
26#include "dbusinterface.h"
2627
27#include <QtTest>28#include <QtTest>
29#include <QDBusInterface>
30#include <QDBusReply>
31#include <QDomDocument>
2832
29// This is a mock, specifically to test the LauncherModel33// This is a mock, specifically to test the LauncherModel
30class MockApp: public unity::shell::application::ApplicationInfoInterface34class MockApp: public unity::shell::application::ApplicationInfoInterface
@@ -287,10 +291,86 @@
287 QCOMPARE(launcherModel->getUrlForAppId(QString()), QString());291 QCOMPARE(launcherModel->getUrlForAppId(QString()), QString());
288 QCOMPARE(launcherModel->getUrlForAppId(""), QString());292 QCOMPARE(launcherModel->getUrlForAppId(""), QString());
289 QCOMPARE(launcherModel->getUrlForAppId("no-name"), QString("application:///no-name.desktop"));293 QCOMPARE(launcherModel->getUrlForAppId("no-name"), QString("application:///no-name.desktop"));
290 QCOMPARE(launcherModel->getUrlForAppId("com.test.good"), QString("appid://com.test.good/first-listed-app/current-user-version"));294 QCOMPARE(launcherModel->getUrlForAppId("com.test.good"), QString("application:///com.test.good.desktop"));
291 QCOMPARE(launcherModel->getUrlForAppId("com.test.good_application"), QString("appid://com.test.good/application/current-user-version"));295 QCOMPARE(launcherModel->getUrlForAppId("com.test.good_application"), QString("appid://com.test.good/application/current-user-version"));
292 QCOMPARE(launcherModel->getUrlForAppId("com.test.good_application_1.2.3"), QString("appid://com.test.good/application/current-user-version"));296 QCOMPARE(launcherModel->getUrlForAppId("com.test.good_application_1.2.3"), QString("appid://com.test.good/application/current-user-version"));
293 }297 }
298
299 void testIntrospection() {
300 QDBusInterface interface("com.canonical.Unity.Launcher", "/com/canonical/Unity/Launcher", "org.freedesktop.DBus.Introspectable");
301 QDBusReply<QString> reply = interface.call("Introspect");
302 QStringList nodes = extractNodes(reply.value());
303 QCOMPARE(nodes.count(), launcherModel->rowCount());
304
305 appManager->addApplication(new MockApp("foobar"));
306 reply = interface.call("Introspect");
307 nodes = extractNodes(reply.value());
308 QCOMPARE(nodes.contains("foobar"), true);
309
310 appManager->removeApplication(2);
311 reply = interface.call("Introspect");
312 nodes = extractNodes(reply.value());
313 QCOMPARE(nodes.contains("foobar"), false);
314 }
315
316 QStringList extractNodes(const QString &introspectionXml) {
317 QXmlStreamReader introspectReply(introspectionXml);
318
319 QStringList ret;
320 while (!introspectReply.atEnd() && !introspectReply.hasError()) {
321 QXmlStreamReader::TokenType token = introspectReply.readNext();
322
323 if (token == QXmlStreamReader::StartElement) {
324 if (introspectReply.name() == "node" && introspectReply.attributes().count() > 0) {
325 ret << introspectReply.attributes().value("name").toString();
326 }
327 }
328 }
329 return ret;
330 }
331
332 void testCountEmblems() {
333 // Call GetAll on abs-icon
334 QDBusInterface interface("com.canonical.Unity.Launcher", "/com/canonical/Unity/Launcher/abs_2Dicon", "org.freedesktop.DBus.Properties");
335 QDBusReply<QVariantMap> reply = interface.call("GetAll");
336 QVariantMap map = reply.value();
337
338 // Make sure GetAll returns a map with count and countVisible props
339 QCOMPARE(map.contains("count"), true);
340 QCOMPARE(map.contains("countVisible"), true);
341
342 // Make sure count is intitilized to 0 and non-visible
343 QCOMPARE(map.value("count").toInt(), 0);
344 QCOMPARE(map.value("countVisible").toBool(), false);
345
346 // Now make it visible and set it to 55 through D-Bus
347 interface.call("Set", "com.canonical.Unity.Launcher.Item", "count", 55);
348 interface.call("Set", "com.canonical.Unity.Launcher.Item", "countVisible", true);
349
350 // Fetch it again using GetAll
351 reply = interface.call("GetAll");
352 map = reply.value();
353
354 // Make sure values have changed on the D-Bus interface
355 QCOMPARE(map.value("count").toInt(), 55);
356 QCOMPARE(map.value("countVisible").toBool(), true);
357
358 // Now the item on the upper side of the API
359 int index = launcherModel->findApplication("abs-icon");
360 QCOMPARE(index >= 0, true);
361
362 // And make sure values have changed there as well
363 QCOMPARE(launcherModel->get(index)->countVisible(), true);
364 QCOMPARE(launcherModel->get(index)->count(), 55);
365 }
366
367 void testRefresh() {
368 QDBusInterface interface("com.canonical.Unity.Launcher", "/com/canonical/Unity/Launcher", "com.canonical.Unity.Launcher");
369 QDBusReply<void> reply = interface.call("Refresh");
370
371 // Make sure the call to Refresh returned without error.
372 QCOMPARE(reply.isValid(), true);
373 }
294};374};
295375
296QTEST_GUILESS_MAIN(LauncherModelTest)376QTEST_GUILESS_MAIN(LauncherModelTest)

Subscribers

People subscribed via source and target branches