Commit f1148d9c authored by David Redondo's avatar David Redondo 🏎 Committed by David Edmundson
Browse files

Load additional actions for app entries in application launchers

This allows 3rd parties to provide additional actions in the context
menu of applications in kicker, kickoff,... by supplying suitable
desktopfiles in GENERIC_DATA_LOCATION/plasma/kickeractions similar
to mechanism used for service menues.
Entries for which the actions are displayed can be controlled by
X-KDE-OnlyForAppIds in the [Desktop Entry], for example
X-KDE-OnlyForAppIds=org.kde.kate,org.kde.dolphin
This was requested as a patch for steam, but we've made a generic
solution so that it can be used by everyone.
parent 3422348c
Pipeline #172254 passed with stage
in 6 minutes and 29 seconds
......@@ -16,6 +16,8 @@
#include <QStandardPaths>
#include <KApplicationTrader>
#include <KDesktopFileActions>
#include <KFileUtils>
#include <KIO/ApplicationLauncherJob>
#include <KLocalizedString>
#include <KNotificationJobUiDelegate>
......@@ -456,6 +458,53 @@ bool handleAppstreamActions(const QString &actionId, const QVariant &argument)
return false;
}
static QList<KServiceAction> additionalActions(const KService::Ptr &service)
{
QList<KServiceAction> actions;
const static auto locations =
QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("plasma/kickeractions"), QStandardPaths::LocateDirectory);
const auto files = KFileUtils::findAllUniqueFiles(locations);
for (const auto &file : files) {
KService actionsService(file);
const auto filter = actionsService.property(QStringLiteral("X-KDE-OnlyForAppIds"), QVariant::StringList).toStringList();
if (filter.empty() || filter.contains(storageIdFromService(service))) {
actions.append(KDesktopFileActions::userDefinedServices(actionsService, true));
}
}
return actions;
}
QVariantList additionalAppActions(const KService::Ptr &service)
{
QVariantList list;
const auto actions = additionalActions(service);
list.reserve(actions.size());
for (const auto &action : actions) {
list << createActionItem(action.text(), action.icon(), action.name(), action.service()->entryPath());
}
return list;
}
bool handleAdditionalAppActions(const QString &actionId, const KService::Ptr &service, const QVariant &argument)
{
const KService actionProvider(argument.toString());
if (!actionProvider.isValid()) {
return false;
}
const auto actions = actionProvider.actions();
auto action = std::find_if(actions.begin(), actions.end(), [&actionId](const KServiceAction &action) {
return action.name() == actionId;
});
if (action == actions.end()) {
return false;
}
auto *job = new KIO::ApplicationLauncherJob(*action);
job->setUrls({QUrl::fromLocalFile(resolvedServiceEntryPath(service))});
job->setUiDelegate(new KNotificationJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled));
job->start();
return true;
}
QString resolvedServiceEntryPath(const KService::Ptr &service)
{
QString path = service->entryPath();
......
......@@ -56,6 +56,9 @@ bool handleEditApplicationAction(const QString &actionId, const KService::Ptr &s
QVariantList appstreamActions(const KService::Ptr &service);
bool handleAppstreamActions(const QString &actionId, const QVariant &argument);
QVariantList additionalAppActions(const KService::Ptr &service);
bool handleAdditionalAppActions(const QString &actionId, const KService::Ptr &service, const QVariant &argument);
QString resolvedServiceEntryPath(const KService::Ptr &service);
}
......@@ -168,6 +168,11 @@ QVariantList AppEntry::actions() const
actionList << recentDocuments << Kicker::createSeparatorActionItem();
}
const QVariantList &additionalActions = Kicker::additionalAppActions(m_service);
if (!additionalActions.isEmpty()) {
actionList << additionalActions << Kicker::createSeparatorActionItem();
}
// Don't allow adding launchers, editing, hiding, or uninstalling applications
// when system is immutable.
if (systemImmutable) {
......@@ -237,6 +242,8 @@ bool AppEntry::run(const QString &actionId, const QVariant &argument)
job->setDesktopName(m_service->entryPath());
job->setIcon(m_service->icon());
return job->exec();
} else if (Kicker::handleAdditionalAppActions(actionId, m_service, argument)) {
return true;
}
return Kicker::handleRecentDocumentAction(m_service, actionId, argument);
......
......@@ -131,6 +131,11 @@ QVariant RunnerMatchesModel::data(const QModelIndex &index, int role) const
actionList << recentDocuments << Kicker::createSeparatorActionItem();
}
const QVariantList &additionalActions = Kicker::additionalAppActions(service);
if (!additionalActions.isEmpty()) {
actionList << additionalActions << Kicker::createSeparatorActionItem();
}
// Don't allow adding launchers, editing, hiding, or uninstalling applications
// when system is immutable.
if (systemImmutable) {
......@@ -209,6 +214,8 @@ bool RunnerMatchesModel::trigger(int row, const QString &actionId, const QVarian
return job->exec();
} else if (actionId == QLatin1String("_kicker_recentDocument") || actionId == QLatin1String("_kicker_forgetRecentDocuments")) {
return Kicker::handleRecentDocumentAction(service, actionId, argument);
} else if (Kicker::handleAdditionalAppActions(actionId, service, argument)) {
return true;
}
return false;
......
Supports Markdown
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