diff --git a/applets/kicker/plugin/actionlist.h b/applets/kicker/plugin/actionlist.h --- a/applets/kicker/plugin/actionlist.h +++ b/applets/kicker/plugin/actionlist.h @@ -57,4 +57,9 @@ QVariantList recentDocumentActions(KService::Ptr service); bool handleRecentDocumentAction(KService::Ptr service, const QString &actionId, const QVariant &argument); +bool canEdit(const QString &entryPath); +void edit(const QString &entryPath, const QString &menuId); + +QVariantList appstreamActions(const KService::Ptr &service); + } diff --git a/applets/kicker/plugin/actionlist.cpp b/applets/kicker/plugin/actionlist.cpp --- a/applets/kicker/plugin/actionlist.cpp +++ b/applets/kicker/plugin/actionlist.cpp @@ -19,20 +19,28 @@ ***************************************************************************/ #include "actionlist.h" +#include "menuentryeditor.h" + +#include #include #include #include #include +#include #include #include #include #include #include "containmentinterface.h" +#ifdef HAVE_APPSTREAMQT +#include +#endif + namespace KAStats = KActivities::Stats; using namespace KAStats; @@ -308,4 +316,54 @@ return (KRun::runService(*service, QList() << QUrl(argument), QApplication::activeWindow()) != 0); } +Q_GLOBAL_STATIC(MenuEntryEditor, menuEntryEditor) + +bool canEdit(const QString &entryPath) +{ + return menuEntryEditor->canEdit(entryPath); +} + +void edit(const QString &entryPath, const QString &menuId) +{ + menuEntryEditor->edit(entryPath, menuId); +} + +#ifdef HAVE_APPSTREAMQT +Q_GLOBAL_STATIC(AppStream::Pool, appstreamPool) +#endif + +QVariantList appstreamActions(const KService::Ptr &service) +{ + QVariantList ret; + +#ifdef HAVE_APPSTREAMQT + const KService::Ptr appStreamHandler = KMimeTypeTrader::self()->preferredService(QStringLiteral("x-scheme-handler/appstream")); + + // Don't show action if we can't find any app to handle appstream:// URLs. + if (!appStreamHandler) { + if (!KProtocolInfo::isHelperProtocol(QStringLiteral("appstream")) + || KProtocolInfo::exec(QStringLiteral("appstream")).isEmpty()) { + return ret; + } + } + + if (!appstreamPool.exists()) { + appstreamPool->load(); + } + + const auto components = appstreamPool->componentsById(service->desktopEntryName()+QLatin1String(".desktop")); + for(const auto &component: components) { + const QString componentId = component.id(); + + QVariantMap appstreamAction = Kicker::createActionItem(i18nc("@action opens a software center with the application", "Manage '%1'...", component.name()), "manageApplication", QVariant(QStringLiteral("appstream://") + componentId)); + appstreamAction[QStringLiteral("icon")] = QStringLiteral("applications-other"); + ret << appstreamAction; + } +#else + Q_UNUSED(service) +#endif + + return ret; +} + } diff --git a/applets/kicker/plugin/appentry.cpp b/applets/kicker/plugin/appentry.cpp --- a/applets/kicker/plugin/appentry.cpp +++ b/applets/kicker/plugin/appentry.cpp @@ -25,7 +25,6 @@ #include "menuentryeditor.h" #include -#include #include #include @@ -40,21 +39,14 @@ #include #include #include -#include #include #include #include #include #include #include -#ifdef HAVE_APPSTREAMQT -#include -#endif - -MenuEntryEditor *AppEntry::m_menuEntryEditor = nullptr; - AppEntry::AppEntry(AbstractModel *owner, KService::Ptr service, NameFormat nameFormat) : AbstractEntry(owner) , m_service(service) @@ -89,10 +81,6 @@ } else { m_description = nameFromService(m_service, GenericNameOnly); } - - if (!m_menuEntryEditor) { - m_menuEntryEditor = new MenuEntryEditor(); - } } bool AppEntry::isValid() const @@ -142,39 +130,6 @@ return true; } -#ifdef HAVE_APPSTREAMQT -Q_GLOBAL_STATIC(AppStream::Pool, appstreamPool) - -QVariantList appstreamActions(const KService::Ptr &service) -{ - QVariantList ret; - - const KService::Ptr appStreamHandler = KMimeTypeTrader::self()->preferredService(QStringLiteral("x-scheme-handler/appstream")); - - // Don't show action if we can't find any app to handle appstream:// URLs. - if (!appStreamHandler) { - if (!KProtocolInfo::isHelperProtocol(QStringLiteral("appstream")) - || KProtocolInfo::exec(QStringLiteral("appstream")).isEmpty()) { - return ret; - } - } - - if (!appstreamPool.exists()) { - appstreamPool->load(); - } - - const auto components = appstreamPool->componentsById(service->desktopEntryName()+QLatin1String(".desktop")); - for(const auto &component: components) { - const QString componentId = component.id(); - - QVariantMap appstreamAction = Kicker::createActionItem(i18nc("@action opens a software center with the application", "Manage '%1'...", component.name()), "manageApplication", QVariant(QStringLiteral("appstream://") + componentId)); - appstreamAction[QStringLiteral("icon")] = QStringLiteral("applications-other"); - ret << appstreamAction; - } - return ret; -} -#endif - QVariantList AppEntry::actions() const { QVariantList actionList; @@ -205,19 +160,17 @@ return actionList; } - if (m_menuEntryEditor->canEdit(m_service->entryPath())) { + if (Kicker::canEdit(m_service->entryPath())) { actionList << Kicker::createSeparatorActionItem(); QVariantMap editAction = Kicker::createActionItem(i18n("Edit Application..."), "editApplication"); editAction["icon"] = "kmenuedit"; // TODO: Using the KMenuEdit icon might be misleading. actionList << editAction; } -#ifdef HAVE_APPSTREAMQT if (m_service->isApplication()) { - actionList << appstreamActions(m_service); + actionList << Kicker::appstreamActions(m_service); } -#endif QQmlPropertyMap *appletConfig = qobject_cast(appletInterface->property("configuration").value()); @@ -260,8 +213,8 @@ if (Kicker::handleAddLauncherAction(actionId, appletInterface, m_service)) { return true; - } else if (actionId == "editApplication" && m_menuEntryEditor->canEdit(m_service->entryPath())) { - m_menuEntryEditor->edit(m_service->entryPath(), m_service->menuId()); + } else if (actionId == "editApplication" && Kicker::canEdit(m_service->entryPath())) { + Kicker::edit(m_service->entryPath(), m_service->menuId()); return true; } else if (actionId == "manageApplication") { diff --git a/applets/kicker/plugin/runnermatchesmodel.cpp b/applets/kicker/plugin/runnermatchesmodel.cpp --- a/applets/kicker/plugin/runnermatchesmodel.cpp +++ b/applets/kicker/plugin/runnermatchesmodel.cpp @@ -104,7 +104,8 @@ actionList << Kicker::createSeparatorActionItem(); } - KService::Ptr service = KService::serviceByStorageId(match.data().toString()); + const KService::Ptr service = KService::serviceByStorageId(match.data().toString()); + if (service) { const QVariantList &jumpListActions = Kicker::jumpListActions(service); if (!jumpListActions.isEmpty()) { @@ -122,6 +123,18 @@ if (!recentDocuments.isEmpty()) { actionList << recentDocuments << Kicker::createSeparatorActionItem(); } + + if (Kicker::canEdit(service->entryPath())) { + actionList << Kicker::createSeparatorActionItem(); + + QVariantMap editAction = Kicker::createActionItem(i18n("Edit Application..."), "editApplication"); + editAction["icon"] = "kmenuedit"; // TODO: Using the KMenuEdit icon might be misleading. + actionList << editAction; + } + + if (service->isApplication()) { + actionList << Kicker::appstreamActions(service); + } } return actionList; @@ -149,10 +162,16 @@ QObject *appletInterface = static_cast(parent())->appletInterface(); - KService::Ptr service = KService::serviceByStorageId(match.data().toString()); + const KService::Ptr service = KService::serviceByStorageId(match.data().toString()); if (Kicker::handleAddLauncherAction(actionId, appletInterface, service)) { return true; + } else if (actionId == "editApplication" && Kicker::canEdit(service->entryPath())) { + Kicker::edit(service->entryPath(), service->menuId()); + + return true; + } else if (actionId == "manageApplication") { + return QDesktopServices::openUrl(QUrl(argument.toString())); } else if (actionId == QLatin1String("_kicker_jumpListAction")) { return KRun::run(argument.toString(), {}, nullptr, service ? service->name() : QString(), service ? service->icon() : QString()); } else if (actionId == QLatin1String("_kicker_recentDocument") diff --git a/applets/taskmanager/plugin/backend.cpp b/applets/taskmanager/plugin/backend.cpp --- a/applets/taskmanager/plugin/backend.cpp +++ b/applets/taskmanager/plugin/backend.cpp @@ -125,14 +125,27 @@ QVariantList Backend::jumpListActions(const QUrl &launcherUrl, QObject *parent) { - QVariantList actions; + if (!parent) { + return QVariantList(); + } - if (!parent || !launcherUrl.isValid() || !launcherUrl.isLocalFile() - || !KDesktopFile::isDesktopFile(launcherUrl.toLocalFile())) { - return actions; + QUrl desktopEntryUrl = launcherUrl; + + if (launcherUrl.isValid() && launcherUrl.scheme() == QStringLiteral("applications")) { + const KService::Ptr service = KService::serviceByMenuId(launcherUrl.path()); + + if (service) { + desktopEntryUrl = QUrl::fromLocalFile(service->entryPath()); + } + } + + if (!desktopEntryUrl.isValid() || !desktopEntryUrl.isLocalFile() + || !KDesktopFile::isDesktopFile(desktopEntryUrl.toLocalFile())) { + return QVariantList(); } - KDesktopFile desktopFile(launcherUrl.toLocalFile()); + QVariantList actions; + KDesktopFile desktopFile(desktopEntryUrl.toLocalFile()); const QStringList &jumpListActions = desktopFile.readActions(); @@ -178,14 +191,27 @@ QVariantList Backend::placesActions(const QUrl &launcherUrl, bool showAllPlaces, QObject *parent) { - QVariantList actions; + if (!parent) { + return QVariantList(); + } - if (!parent || !launcherUrl.isValid() || !launcherUrl.isLocalFile() - || !KDesktopFile::isDesktopFile(launcherUrl.toLocalFile())) { - return actions; + QUrl desktopEntryUrl = launcherUrl; + + if (launcherUrl.isValid() && launcherUrl.scheme() == QStringLiteral("applications")) { + const KService::Ptr service = KService::serviceByMenuId(launcherUrl.path()); + + if (service) { + desktopEntryUrl = QUrl::fromLocalFile(service->entryPath()); + } + } + + if (!desktopEntryUrl.isValid() || !desktopEntryUrl.isLocalFile() + || !KDesktopFile::isDesktopFile(desktopEntryUrl.toLocalFile())) { + return QVariantList(); } - KDesktopFile desktopFile(launcherUrl.toLocalFile()); + QVariantList actions; + KDesktopFile desktopFile(desktopEntryUrl.toLocalFile()); // Since we can't have dynamic jump list actions, at least add the user's "Places" for file managers. const QStringList &categories = desktopFile.desktopGroup().readXdgListEntry(QStringLiteral("Categories")); @@ -207,8 +233,8 @@ QAction *action = new QAction(icon, title, parent); - connect(action, &QAction::triggered, this, [this, action, url, launcherUrl] { - KService::Ptr service = KService::serviceByDesktopPath(launcherUrl.toLocalFile()); + connect(action, &QAction::triggered, this, [this, action, url, desktopEntryUrl] { + KService::Ptr service = KService::serviceByDesktopPath(desktopEntryUrl.toLocalFile()); if (!service) { return; } @@ -239,14 +265,27 @@ QVariantList Backend::recentDocumentActions(const QUrl &launcherUrl, QObject *parent) { - QVariantList actions; + if (!parent) { + return QVariantList(); + } - if (!parent || !launcherUrl.isValid() || !launcherUrl.isLocalFile() - || !KDesktopFile::isDesktopFile(launcherUrl.toLocalFile())) { - return actions; + QUrl desktopEntryUrl = launcherUrl; + + if (launcherUrl.isValid() && launcherUrl.scheme() == QStringLiteral("applications")) { + const KService::Ptr service = KService::serviceByMenuId(launcherUrl.path()); + + if (service) { + desktopEntryUrl = QUrl::fromLocalFile(service->entryPath()); + } } - QString desktopName = launcherUrl.fileName(); + if (!desktopEntryUrl.isValid() || !desktopEntryUrl.isLocalFile() + || !KDesktopFile::isDesktopFile(desktopEntryUrl.toLocalFile())) { + return QVariantList(); + } + + QVariantList actions; + QString desktopName = desktopEntryUrl.fileName(); QString storageId = desktopName; if (storageId.startsWith(QLatin1String("org.kde."))) { @@ -288,7 +327,7 @@ action->setText(url.fileName()); action->setIcon(QIcon::fromTheme(fileItem.iconName(), QIcon::fromTheme("unknown"))); action->setProperty("agent", storageId); - action->setProperty("entryPath", launcherUrl); + action->setProperty("entryPath", desktopEntryUrl); action->setData(resource); connect(action, &QAction::triggered, this, &Backend::handleRecentDocumentAction);