From a47d1f19e1e0bda300ff799dfc6f377802a2a678 Mon Sep 17 00:00:00 2001 From: Eike Hein Date: Sat, 26 Aug 2017 22:11:15 +0900 Subject: [PATCH] Avoid absolute paths to .desktop files in launcher URLs. Summary: In any place we look up a KService, check if it has a menuId, and then generate KAStats-style applications: URL with it. Also learn how to handle applications: URLs. This was requested by Kai in D7203. His patch makes Kate dynamically add its sessions as jump list actions to a copy of its .desktop file in $HOME. As the library would generate the absolute path to a .desktop file e.g. in /usr as launcher URL before, the TM applet would ignore the overriding copy in $HOME and the actions wouldn't be found. This patch won't rewrite existing config, but LauncherTasksModel will resolve the absolute path to the applications: URL as it goes through TaskTools, and then return that. The window and startup tasks models produce those URLs, too, so things match up. Newly- added launchers will then store the applications: URL in config directly. Moving away from storing absolute paths when we can is also nice in case there is a $HOME .desktop file at the time a launcher is added which later gets deleted. Using the new storage format, we will now fall back to a system file instead. Note the conservative approach taken: We only generate an applications: URL when the service returns something for menuId(). We don't use KService::storageId() here, which can fall back to KService::entryPath() - in this case we're better off just using the absolute path we already have. We still use storageId() when generating the applications: URL for KAStats db insertions, as that is more liberal (and matches Kicker). It makes sense to look at the Kicker backend code to see if should store applications: URLs (which it already produces to insert into KAStats) as well. This will need a subsequent patch to the Task Manager applet to handle applications: URLs there in code that consumes launcher URLs to parse things out of .desktop files. Contains other minor cleanup and fixes, such as porting the LauncherTasksModel to use TaskTools::runApp (which now understands preferred: URLs as well), a fix for a small logic error there (the && before the !isApplication should have been ||, now moot), and not needlessly opening and parsing a .desktop file in TaskTools::appDataFromUrl. Subscribers: plasma-devel Tags: #plasma Differential Revision: https://phabricator.kde.org/D7561 --- libtaskmanager/launchertasksmodel.cpp | 36 +---------- libtaskmanager/startuptasksmodel.cpp | 8 +++ libtaskmanager/tasktools.cpp | 87 +++++++++++++++++++++------ libtaskmanager/xwindowtasksmodel.cpp | 8 +++ 4 files changed, 87 insertions(+), 52 deletions(-) diff --git a/libtaskmanager/launchertasksmodel.cpp b/libtaskmanager/launchertasksmodel.cpp index ea2a04518..bc4ae726b 100644 --- a/libtaskmanager/launchertasksmodel.cpp +++ b/libtaskmanager/launchertasksmodel.cpp @@ -544,41 +544,7 @@ void LauncherTasksModel::requestNewInstance(const QModelIndex &index) return; } - const QUrl &url = d->launchersOrder.at(index.row()); - - quint32 timeStamp = 0; - -#if HAVE_X11 - if (KWindowSystem::isPlatformX11()) { - timeStamp = QX11Info::appUserTime(); - } -#endif - - if (url.scheme() == QLatin1String("preferred")) { - const KService::Ptr service = KService::serviceByStorageId(defaultApplication(url)); - - if (!service && !service->isApplication()) { - return; - } - - KRun::runApplication(*service, QList(), nullptr, 0, {}, - KStartupInfo::createNewStartupIdForTimestamp(timeStamp)); - - KActivities::ResourceInstance::notifyAccessed(QUrl(QStringLiteral("applications:") + service->storageId()), - QStringLiteral("org.kde.libtaskmanager")); - } else { - const KService::Ptr service = KService::serviceByDesktopPath(url.toLocalFile()); - - if (service && service->isApplication()) { - KRun::runApplication(*service, QList(), nullptr, 0, {}, - KStartupInfo::createNewStartupIdForTimestamp(timeStamp)); - - KActivities::ResourceInstance::notifyAccessed(QUrl(QStringLiteral("applications:") + service->storageId()), - QStringLiteral("org.kde.libtaskmanager")); - } else { - new KRun(url, 0, false, KStartupInfo::createNewStartupIdForTimestamp(timeStamp)); - } - } + runApp(d->appData(d->launchersOrder.at(index.row()))); } void LauncherTasksModel::requestOpenUrls(const QModelIndex &index, const QList &urls) diff --git a/libtaskmanager/startuptasksmodel.cpp b/libtaskmanager/startuptasksmodel.cpp index 2847ffe98..13977b356 100644 --- a/libtaskmanager/startuptasksmodel.cpp +++ b/libtaskmanager/startuptasksmodel.cpp @@ -193,6 +193,14 @@ QUrl StartupTasksModel::Private::launcherUrl(const KStartupInfoData &data) } if (!services.empty()) { + const QString &menuId = services.at(0)->menuId(); + + // applications: URLs are used to refer to applications by their KService::menuId + // (i.e. .desktop file name) rather than the absolute path to a .desktop file. + if (!menuId.isEmpty()) { + return QUrl(QStringLiteral("applications:") + menuId); + } + QString path = services.at(0)->entryPath(); if (path.isEmpty()) { diff --git a/libtaskmanager/tasktools.cpp b/libtaskmanager/tasktools.cpp index 08499148a..342d3c462 100644 --- a/libtaskmanager/tasktools.cpp +++ b/libtaskmanager/tasktools.cpp @@ -69,11 +69,36 @@ AppData appDataFromUrl(const QUrl &url, const QIcon &fallbackIcon) } } + // applications: URLs are used to refer to applications by their KService::menuId + // (i.e. .desktop file name) rather than the absolute path to a .desktop file. + if (url.scheme() == QStringLiteral("applications")) { + const KService::Ptr service = KService::serviceByMenuId(url.path()); + + if (service && url.path() == service->menuId()) { + data.name = service->name(); + data.genericName = service->genericName(); + data.id = service->storageId(); + + if (data.icon.isNull()) { + data.icon = QIcon::fromTheme(service->icon()); + } + } + } + if (url.isLocalFile() && KDesktopFile::isDesktopFile(url.toLocalFile())) { KDesktopFile f(url.toLocalFile()); const KService::Ptr service = KService::serviceByStorageId(f.fileName()); + // Resolve to non-absolute menuId-based URL if possible. + if (service) { + const QString &menuId = service->menuId(); + + if (!menuId.isEmpty()) { + data.url = QUrl(QStringLiteral("applications:") + menuId); + } + } + if (service && QUrl::fromLocalFile(service->entryPath()) == url) { data.name = service->name(); data.genericName = service->genericName(); @@ -101,23 +126,23 @@ AppData appDataFromUrl(const QUrl &url, const QIcon &fallbackIcon) const KService::Ptr service = KService::serviceByStorageId(data.id); if (service) { - QString desktopFile = service->entryPath(); - - // Update with resolved URL. - data.url = QUrl::fromLocalFile(desktopFile); - - KDesktopFile f(desktopFile); - KConfigGroup cg(&f, "Desktop Entry"); + const QString &menuId = service->menuId(); + const QString &desktopFile = service->entryPath(); - data.icon = QIcon::fromTheme(f.readIcon()); - const QString exec = cg.readEntry("Exec", QString()); - data.name = cg.readEntry("Name", QString()); + data.name = service->name(); + data.genericName = service->genericName(); + data.id = service->storageId(); - if (data.name.isEmpty() && !exec.isEmpty()) { - data.name = exec.split(' ').at(0); + if (data.icon.isNull()) { + data.icon = QIcon::fromTheme(service->icon()); } - data.genericName = f.readGenericName(); + // Update with resolved URL. + if (!menuId.isEmpty()) { + data.url = QUrl(QStringLiteral("applications:") + menuId); + } else { + data.url = QUrl::fromLocalFile(desktopFile); + } } } @@ -142,7 +167,16 @@ AppData appDataFromAppId(const QString &appId) data.id = service->storageId(); data.name = service->name(); data.genericName = service->genericName(); - data.url = QUrl::fromLocalFile(service->entryPath()); + + const QString &menuId = service->menuId(); + + // applications: URLs are used to refer to applications by their KService::menuId + // (i.e. .desktop file name) rather than the absolute path to a .desktop file. + if (!menuId.isEmpty()) { + data.url = QUrl(QStringLiteral("applications:") + menuId); + } else { + data.url = QUrl::fromLocalFile(service->entryPath()); + } return data; } @@ -375,9 +409,18 @@ QUrl windowUrlFromMetadata(const QString &appId, quint32 pid, } if (!services.empty()) { - QString path = services[0]->entryPath(); + const QString &menuId = services.at(0)->menuId(); + + // applications: URLs are used to refer to applications by their KService::menuId + // (i.e. .desktop file name) rather than the absolute path to a .desktop file. + if (!menuId.isEmpty()) { + return QUrl(QStringLiteral("applications:") + menuId); + } + + QString path = services.at(0)->entryPath(); + if (path.isEmpty()) { - path = services[0]->exec(); + path = services.at(0)->exec(); } if (!path.isEmpty()) { @@ -640,7 +683,17 @@ void runApp(const AppData &appData, const QList &urls) } #endif - const KService::Ptr service = KService::serviceByDesktopPath(appData.url.toLocalFile()); + KService::Ptr service; + + // applications: URLs are used to refer to applications by their KService::menuId + // (i.e. .desktop file name) rather than the absolute path to a .desktop file. + if (appData.url.scheme() == QStringLiteral("applications")) { + service = KService::serviceByMenuId(appData.url.path()); + } else if (appData.url.scheme() == QLatin1String("preferred")) { + const KService::Ptr service = KService::serviceByStorageId(defaultApplication(appData.url)); + } else { + service = KService::serviceByDesktopPath(appData.url.toLocalFile()); + } if (service && service->isApplication()) { KRun::runApplication(*service, urls, nullptr, 0, {}, diff --git a/libtaskmanager/xwindowtasksmodel.cpp b/libtaskmanager/xwindowtasksmodel.cpp index 1cef6c97d..349faae93 100644 --- a/libtaskmanager/xwindowtasksmodel.cpp +++ b/libtaskmanager/xwindowtasksmodel.cpp @@ -484,6 +484,14 @@ QUrl XWindowTasksModel::Private::windowUrl(WId window) KService::Ptr service = KService::serviceByStorageId(desktopFile); if (service) { + const QString &menuId = service->menuId(); + + // applications: URLs are used to refer to applications by their KService::menuId + // (i.e. .desktop file name) rather than the absolute path to a .desktop file. + if (!menuId.isEmpty()) { + return QUrl(QStringLiteral("applications:") + menuId); + } + return QUrl::fromLocalFile(service->entryPath()); }