File indexing completed on 2024-05-19 05:29:57

0001 /*
0002     SPDX-FileCopyrightText: 2008 Michael Jansen <kde@michael-jansen.biz>
0003     SPDX-FileCopyrightText: 2016 Marco Martin <mart@kde.org>
0004     SPDX-FileCopyrightText: 2021 David Redondo <kde@david-redondo.de>
0005 
0006     SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 
0009 #include "kserviceactioncomponent.h"
0010 #include "globalshortcutcontext.h"
0011 #include "logging_p.h"
0012 
0013 #include <QFileInfo>
0014 
0015 #include <KIO/ApplicationLauncherJob>
0016 #include <KIO/UntrustedProgramHandlerInterface>
0017 #include <KNotificationJobUiDelegate>
0018 
0019 #include "config-kglobalaccel.h"
0020 #if HAVE_X11
0021 #include <KStartupInfo>
0022 #include <private/qtx11extras_p.h>
0023 #endif
0024 
0025 class UntrustedProgramHandler : public KIO::UntrustedProgramHandlerInterface
0026 {
0027 public:
0028     UntrustedProgramHandler(QObject *parent)
0029         : KIO::UntrustedProgramHandlerInterface(parent)
0030     {
0031     }
0032 
0033     void showUntrustedProgramWarning(KJob * /*job*/, const QString & /*programName*/) override
0034     {
0035         Q_EMIT result(true);
0036     }
0037 };
0038 
0039 QString makeUniqueName(const KService::Ptr &service)
0040 {
0041     if (service->storageId().startsWith(QLatin1Char('/'))) {
0042         return QFileInfo(service->storageId()).fileName();
0043     }
0044 
0045     return service->storageId();
0046 }
0047 
0048 KServiceActionComponent::KServiceActionComponent(KService::Ptr service)
0049     : Component(makeUniqueName(service), service->name())
0050     , m_service(service)
0051 {
0052 }
0053 
0054 KServiceActionComponent::~KServiceActionComponent() = default;
0055 
0056 void KServiceActionComponent::emitGlobalShortcutPressed(const GlobalShortcut &shortcut)
0057 {
0058     KIO::ApplicationLauncherJob *job = nullptr;
0059 
0060     if (shortcut.uniqueName() == QLatin1String("_launch")) {
0061         job = new KIO::ApplicationLauncherJob(m_service);
0062     } else {
0063         const auto actions = m_service->actions();
0064         const auto it = std::find_if(actions.cbegin(), actions.cend(), [&shortcut](const KServiceAction &action) {
0065             return action.name() == shortcut.uniqueName();
0066         });
0067         if (it == actions.cend()) {
0068             qCCritical(KGLOBALACCELD, "failed to find an action matching the '%s' name", qPrintable(shortcut.uniqueName()));
0069             return;
0070         } else {
0071             job = new KIO::ApplicationLauncherJob(*it);
0072         }
0073     }
0074 
0075     auto *delegate = new KNotificationJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled);
0076     // ApplicationLauncherJob refuses to launch desktop files in /usr/share/kglobalaccel/ unless they are marked as executable
0077     // to avoid that add our own UntrustedProgramHandler that accepts the launch regardless
0078     new UntrustedProgramHandler(delegate);
0079     job->setUiDelegate(delegate);
0080 #if HAVE_X11
0081     if (QX11Info::isPlatformX11()) {
0082         // Create a startup id ourselves. Otherwise ApplicationLauncherJob will query X11 to get a timestamp, which causes a deadlock
0083         auto startupId = KStartupInfo::createNewStartupIdForTimestamp(QX11Info::appTime());
0084         job->setStartupId(startupId);
0085     }
0086 #endif
0087     job->start();
0088 }
0089 
0090 void KServiceActionComponent::loadFromService()
0091 {
0092     const QString type = m_service->property<QString>(QStringLiteral("X-KDE-GlobalShortcutType"));
0093 
0094     // Type can be Application or Service
0095     // For applications add a lauch shortcut
0096     // If no type is set assume Application
0097     if (type.isEmpty() || type == QLatin1String("Application")) {
0098         const QString shortcutString = m_service->property<QStringList>(QStringLiteral("X-KDE-Shortcuts")).join(QLatin1Char('\t'));
0099         GlobalShortcut *shortcut = registerShortcut(QStringLiteral("_launch"), m_service->name(), shortcutString, shortcutString);
0100         shortcut->setIsPresent(true);
0101     }
0102 
0103     const auto lstActions = m_service->actions();
0104     for (const KServiceAction &action : lstActions) {
0105         const QString shortcutString = action.property<QStringList>(QStringLiteral("X-KDE-Shortcuts")).join(QLatin1Char('\t'));
0106         GlobalShortcut *shortcut = registerShortcut(action.name(), action.text(), shortcutString, shortcutString);
0107         shortcut->setIsPresent(true);
0108     }
0109 }
0110 
0111 bool KServiceActionComponent::cleanUp()
0112 {
0113     qCDebug(KGLOBALACCELD) << "Disabling desktop file";
0114 
0115     const auto shortcuts = allShortcuts();
0116     for (GlobalShortcut *shortcut : shortcuts) {
0117         shortcut->setIsPresent(false);
0118     }
0119 
0120     return Component::cleanUp();
0121 }
0122 
0123 void KServiceActionComponent::writeSettings(KConfigGroup &config) const
0124 {
0125     // Clear the config so we remove entries after forgetGlobalShortcut
0126     config.deleteGroup();
0127 
0128     // Now write all contexts
0129     for (GlobalShortcutContext *context : std::as_const(_contexts)) {
0130         KConfigGroup contextGroup;
0131 
0132         if (context->uniqueName() == QLatin1String("default")) {
0133             contextGroup = config;
0134         } else {
0135             contextGroup = KConfigGroup(&config, context->uniqueName());
0136         }
0137 
0138         for (const GlobalShortcut *shortcut : std::as_const(context->_actionsMap)) {
0139             // We do not write fresh shortcuts.
0140             // We do not write session shortcuts
0141             if (shortcut->isFresh() || shortcut->isSessionShortcut()) {
0142                 continue;
0143             }
0144 
0145             if (shortcut->keys() != shortcut->defaultKeys()) {
0146                 contextGroup.writeEntry(shortcut->uniqueName(), stringFromKeys(shortcut->keys()));
0147             } else {
0148                 contextGroup.revertToDefault(shortcut->uniqueName());
0149             }
0150         }
0151     }
0152 }
0153 
0154 void KServiceActionComponent::loadSettings(KConfigGroup &configGroup)
0155 {
0156     // Action shortcuts
0157     const auto actions = m_service->actions();
0158     for (const KServiceAction &action : actions) {
0159         const QString defaultShortcutString = action.property<QString>(QStringLiteral("X-KDE-Shortcuts")).replace(QLatin1Char(','), QLatin1Char('\t'));
0160         const QString shortcutString = configGroup.readEntry(action.name(), defaultShortcutString);
0161 
0162         GlobalShortcut *shortcut = registerShortcut(action.name(), action.text(), shortcutString, defaultShortcutString);
0163         shortcut->setIsPresent(true);
0164     }
0165 
0166     const QString type = m_service->property<QString>(QStringLiteral("X-KDE-GlobalShortcutType"));
0167 
0168     // Type can be Application or Service
0169     // For applications add a lauch shortcut
0170     // If no type is set assume Application
0171     if (type.isEmpty() || type == QLatin1String("Application")) {
0172         const QString defaultShortcutString = m_service->property<QString>(QStringLiteral("X-KDE-Shortcuts")).replace(QLatin1Char(','), QLatin1Char('\t'));
0173         const QString shortcutString = configGroup.readEntry("_launch", defaultShortcutString);
0174         GlobalShortcut *shortcut = registerShortcut(QStringLiteral("_launch"), m_service->name(), shortcutString, defaultShortcutString);
0175         shortcut->setIsPresent(true);
0176     }
0177 }
0178 
0179 #include "moc_kserviceactioncomponent.cpp"