Commit a47d1f19 authored by Eike Hein's avatar Eike Hein

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
parent 2aa50434
......@@ -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<QUrl>(), 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<QUrl>(), 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<QUrl> &urls)
......
......@@ -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()) {
......
......@@ -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<QUrl> &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, {},
......
......@@ -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());
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment