@@ -34,10 +34,12 @@ public:
34 QString desktopFile;
34 QString desktopFile;
35 QString displayName;
35 QString displayName;
36 QString icon;
36 QString icon;
39LauncherBackend::LauncherBackend(QObject *parent):
41LauncherBackend::LauncherBackend(QObject *parent):
40− QObject(parent),
42+ QDBusVirtualObject(parent),
41 m_accounts(nullptr)
43 m_accounts(nullptr)
43#ifndef LAUNCHER_TESTING
45#ifndef LAUNCHER_TESTING
@@ -45,10 +47,25 @@ LauncherBackend::LauncherBackend(QObject *parent):
46 m_user = qgetenv("USER");
48 m_user = qgetenv("USER");
47 syncFromAccounts();
49 syncFromAccounts();
51+ /* Set up ourselves on DBus */
52+ QDBusConnection con = QDBusConnection::sessionBus();
53+ if (!con.registerService("com.canonical.Unity.Launcher")) {
54+ qDebug() << "Unable to register launcher name";
56+ if (!con.registerVirtualObject("/com/canonical/Unity/Launcher", this, QDBusConnection::VirtualObjectRegisterOption::SubPath)) {
57+ qDebug() << "Unable to register launcher object";
50LauncherBackend::~LauncherBackend()
61LauncherBackend::~LauncherBackend()
63+ /* Remove oursevles from DBus */
64+ QDBusConnection con = QDBusConnection::sessionBus();
65+ con.unregisterService("com.canonical.Unity.Launcher");
66+ con.unregisterObject("/com/canonical/Unity/Launcher");
52 m_storedApps.clear();
69 m_storedApps.clear();
54 Q_FOREACH(LauncherBackendItem *item, m_itemCache) {
71 Q_FOREACH(LauncherBackendItem *item, m_itemCache) {
@@ -90,7 +107,7 @@ void LauncherBackend::setStoredApplications(const QStringList &appIds)
91QString LauncherBackend::desktopFile(const QString &appId) const
108QString LauncherBackend::desktopFile(const QString &appId) const
93− LauncherBackendItem *item = m_itemCache.value(appId);
110+ LauncherBackendItem *item = m_itemCache.value(appId, nullptr);
94 if (item) {
111 if (item) {
95 return item->desktopFile;
112 return item->desktopFile;
@@ -100,7 +117,7 @@ QString LauncherBackend::desktopFile(const QString &appId) const
101QString LauncherBackend::displayName(const QString &appId) const
118QString LauncherBackend::displayName(const QString &appId) const
103− LauncherBackendItem *item = m_itemCache.value(appId);
120+ LauncherBackendItem *item = m_itemCache.value(appId, nullptr);
104 if (item) {
121 if (item) {
105 return item->displayName;
122 return item->displayName;
@@ -118,16 +135,9 @@ QString LauncherBackend::displayName(const QString &appId) const
118QString LauncherBackend::icon(const QString &appId) const
135QString LauncherBackend::icon(const QString &appId) const
120 QString iconName;
137 QString iconName;
121− LauncherBackendItem *item = m_itemCache.value(appId);
138+ LauncherBackendItem *item = getItem(appId);
122 if (item) {
139 if (item) {
123 iconName = item->icon;
140 iconName = item->icon;
125− QString df = findDesktopFile(appId);
126− if (!df.isEmpty()) {
127− LauncherBackendItem *item = parseDesktopFile(df);
128− m_itemCache.insert(appId, item);
129− iconName = item->icon;
133 return iconName;
143 return iconName;
@@ -153,10 +163,70 @@ int LauncherBackend::progress(const QString &appId) const
154int LauncherBackend::count(const QString &appId) const
164int LauncherBackend::count(const QString &appId) const
156− // TODO: Return value for count emblem.
166+ int count = -1;
157− // TODO: emit countChanged() when this value changes.
167+ LauncherBackendItem *item = getItem(appId);
170+ if (item->countVisible) {
171+ count = item->count;
178+void LauncherBackend::setCount(const QString &appId, int count) const
180+ LauncherBackendItem *item = getItem(appId);
182+ bool emitchange = false;
184+ emitchange = (item->count != count);
185+ item->count = count;
189+ /* TODO: This needs to use the accessor to handle the visibility
190+ correctly, but when we have the two properties we can just use
192+ Q_EMIT countChanged(appId, this->count(appId));
193+ QVariant vcount(item->count);
194+ emitPropChangedDbus(appId, "count", vcount);
198+bool LauncherBackend::countVisible(const QString &appId) const
200+ bool visible = false;
201+ LauncherBackendItem *item = getItem(appId);
204+ visible = item->countVisible;
159− return 0;
207+ return visible;
210+void LauncherBackend::setCountVisible(const QString &appId, bool visible) const
212+ LauncherBackendItem *item = getItem(appId);
214+ bool emitchange = false;
216+ emitchange = (item->countVisible != visible);
217+ item->countVisible = visible;
219+ qDebug() << "Unable to find:" << appId;
223+ /* TODO: Because we're using visible in determining the
224+ count we need to emit a count changed as well */
225+ Q_EMIT countChanged(appId, this->count(appId));
226+ Q_EMIT countVisibleChanged(appId, item->countVisible);
227+ QVariant vCountVisible(item->countVisible);
228+ emitPropChangedDbus(appId, "countVisible", vCountVisible);
162void LauncherBackend::setUser(const QString &username)
232void LauncherBackend::setUser(const QString &username)
@@ -275,6 +345,35 @@ LauncherBackendItem* LauncherBackend::parseDesktopFile(const QString &desktopFil
276 item->icon = "image://theme/" + iconString;
346 item->icon = "image://theme/" + iconString;
349+ /* TODO: These should be looked up in a cache somewhere */
351+ item->countVisible = false;
356+/* Gets an item, and tries to create a new one if we need it to */
357+LauncherBackendItem* LauncherBackend::getItem(const QString &appId) const
359+ LauncherBackendItem *item = m_itemCache.value(appId, nullptr);
361+ QString df = findDesktopFile(appId);
362+ if (!df.isEmpty()) {
363+ item = parseDesktopFile(df);
365+ m_itemCache[appId] = item;
367+ qWarning() << "Unable to parse desktop file for" << appId << "path" << df;
370+ qDebug() << "Unable to find desktop file for:" << appId;
375+ qWarning() << "Unable to find item for: " << appId;
278 return item;
377 return item;
@@ -285,7 +384,7 @@ void LauncherBackend::loadFromVariant(const QVariantMap &details)
286 QString appId = details.value("id").toString();
385 QString appId = details.value("id").toString();
288− LauncherBackendItem *item = m_itemCache.value(appId);
387+ LauncherBackendItem *item = m_itemCache.value(appId, nullptr);
289 if (item) {
388 if (item) {
290 delete item;
389 delete item;
@@ -295,6 +394,8 @@ void LauncherBackend::loadFromVariant(const QVariantMap &details)
295 item->desktopFile = details.value("desktopFile").toString();
394 item->desktopFile = details.value("desktopFile").toString();
296 item->displayName = details.value("name").toString();
395 item->displayName = details.value("name").toString();
297 item->icon = details.value("icon").toString();
396 item->icon = details.value("icon").toString();
397+ item->count = details.value("count").toInt();
398+ item->countVisible = details.value("countVisible").toBool();
299 m_itemCache.insert(appId, item);
400 m_itemCache.insert(appId, item);
300 m_storedApps.append(appId);
401 m_storedApps.append(appId);
@@ -308,6 +409,8 @@ QVariantMap LauncherBackend::itemToVariant(const QString &appId) const
308 details.insert("name", item->displayName);
409 details.insert("name", item->displayName);
309 details.insert("icon", item->icon);
410 details.insert("icon", item->icon);
310 details.insert("desktopFile", item->desktopFile);
411 details.insert("desktopFile", item->desktopFile);
412+ details.insert("count", item->count);
413+ details.insert("countVisible", item->countVisible);
311 return details;
414 return details;
@@ -318,3 +421,150 @@ bool LauncherBackend::isDefaultsItem(const QList<QVariantMap> &apps) const
318 // list of one item with the 'defaults' field set to true.
421 // list of one item with the 'defaults' field set to true.
319 return (apps.size() == 1 && apps[0].value("defaults").toBool());
422 return (apps.size() == 1 && apps[0].value("defaults").toBool());
425+bool LauncherBackend::handleMessage(const QDBusMessage& message, const QDBusConnection& connection)
427+ /* Check to make sure we're getting properties on our interface */
428+ if (message.type() != QDBusMessage::MessageType::MethodCallMessage) {
431+ if (message.interface() != "org.freedesktop.DBus.Properties") {
434+ if (message.arguments()[0].toString() != "com.canonical.Unity.Launcher.Item") {
438+ /* Break down the path to just the app id */
439+ QString pathtemp = message.path();
440+ if (!pathtemp.startsWith("/com/canonical/Unity/Launcher/")) {
443+ pathtemp.remove("/com/canonical/Unity/Launcher/");
444+ if (pathtemp.indexOf('/') >= 0) {
448+ /* Find ourselves an appid */
449+ QString appid = decodeAppId(pathtemp);
450+ QVariantList retval;
452+ if (message.member() == "Get") {
453+ if (message.arguments()[1].toString() == "count") {
454+ retval.append(QVariant::fromValue(QDBusVariant(this->count(appid))));
455+ } else if (message.arguments()[1].toString() == "countVisible") {
456+ retval.append(QVariant::fromValue(QDBusVariant(this->countVisible(appid))));
458+ } else if (message.member() == "Set") {
459+ if (message.arguments()[1].toString() == "count") {
460+ this->setCount(appid, message.arguments()[2].value<QDBusVariant>().variant().toInt());
461+ } else if (message.arguments()[1].toString() == "countVisible") {
462+ this->setCountVisible(appid, message.arguments()[2].value<QDBusVariant>().variant().toBool());
464+ } else if (message.member() == "GetAll") {
465+ retval.append(this->itemToVariant(appid));
470+ QDBusMessage reply = message.createReply(retval);
471+ return connection.send(reply);
474+QString LauncherBackend::introspect(const QString &path) const
476+ /* This case we should just list the nodes */
477+ if (path == "/com/canonical/Unity/Launcher/" || path == "/com/canonical/Unity/Launcher") {
480+ Q_FOREACH(const QString &appId, m_itemCache.keys()) {
481+ nodes.append("<node name=\"");
482+ nodes.append(encodeAppId(appId));
483+ nodes.append("\"/>\n");
489+ /* Should not happen, but let's handle it */
490+ if (!path.startsWith("/com/canonical/Unity/Launcher")) {
494+ /* Now we should be looking at a node */
496+ "<interface name=\"com.canonical.Unity.Launcher.Item\">"
497+ "<property name=\"count\" type=\"i\" access=\"readwrite\" />"
498+ "<property name=\"countVisible\" type=\"b\" access=\"readwrite\" />"
503+QString LauncherBackend::decodeAppId(const QString& path)
505+ QByteArray bytes = path.toUtf8();
508+ for (int i = 0; i < bytes.size(); ++i) {
509+ char chr = bytes.at(i);
513+ number.append(bytes.at(i+1));
514+ number.append(bytes.at(i+2));
517+ char newchar = number.toUInt(&okay, 16);
519+ decoded.append(newchar);
523+ decoded.append(chr);
527+ return QString::fromUtf8(decoded);
530+QString LauncherBackend::encodeAppId(const QString& appId)
532+ QByteArray bytes = appId.toUtf8();
535+ for (int i = 0; i < bytes.size(); ++i) {
536+ uchar chr = bytes.at(i);
538+ if ((chr >= 'a' && chr <= 'z') ||
539+ (chr >= 'A' && chr <= 'Z') ||
540+ (chr >= '0' && chr <= '9'&& i != 0)) {
541+ encoded.append(chr);
543+ QString hexval = QString("_%1").arg(chr, 2, 16, QChar('0'));
544+ encoded.append(hexval.toUpper());
551+void LauncherBackend::emitPropChangedDbus(const QString& appId, const QString& property, QVariant &value) const
553+ QString path("/com/canonical/Unity/Launcher/");
554+ path.append(encodeAppId(appId));
556+ QDBusMessage message = QDBusMessage::createSignal(path, "org.freedesktop.DBus.Properties", "PropertiesChanged");
558+ QList<QVariant> arguments;
559+ QVariantHash changedprops;
560+ changedprops[property] = QVariant::fromValue(QDBusVariant(value));
561+ QVariantList deletedprops;
563+ arguments.append(changedprops);
564+ arguments.append(deletedprops);
566+ message.setArguments(arguments);
568+ QDBusConnection con = QDBusConnection::sessionBus();