[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-commits
Subject:    [kglobalaccel] /: Launch jumplist actions via KGlobalAccel
From:       Marco Martin <notmart () gmail ! com>
Date:       2016-08-04 12:58:41
Message-ID: E1bVIET-0001Es-Uj () code ! kde ! org
[Download RAW message or body]

Git commit e5fa5cbc4fa7ebb96a2ec7803e2963038e3bf1a6 by Marco Martin.
Committed on 04/08/2016 at 12:58.
Pushed by mart into branch 'master'.

Launch jumplist actions via KGlobalAccel

Summary:
make the kglobalaccel daemon capable of launching applications
from their desktop file or to launch a particular action defined by
their jumplist action entries in their desktop file

In order to be enabled by default, the application would have
to install a copy of its desktop file (which has extra X-KDE-Shortcuts entry)
in prefix/share/kglobalaccel

from the kcm side the user will be able to add shortcuts to launch any
app that has a desktop file (or any action defined by it) making
possible to drop khotkeys and its kcm

Reviewers: graesslin

Reviewed By: graesslin

Differential Revision: https://phabricator.kde.org/D2103

M  +2    -0    CMakeLists.txt
M  +3    -0    src/runtime/CMakeLists.txt
M  +35   -27   src/runtime/component.cpp
M  +14   -2    src/runtime/component.h
M  +41   -12   src/runtime/globalshortcutsregistry.cpp
M  +16   -7    src/runtime/kglobalacceld.cpp
A  +109  -0    src/runtime/kserviceactioncomponent.cpp     [License: LGPL (v2+)]
A  +61   -0    src/runtime/kserviceactioncomponent.h     [License: LGPL (v2+)]

http://commits.kde.org/kglobalaccel/e5fa5cbc4fa7ebb96a2ec7803e2963038e3bf1a6

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2816703..c30d4fc 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -40,6 +40,8 @@ find_package(KF5CoreAddons ${KF5_DEP_VERSION} REQUIRED)
 find_package(KF5Crash ${KF5_DEP_VERSION} REQUIRED)
 find_package(KF5DBusAddons ${KF5_DEP_VERSION} REQUIRED)
 find_package(KF5WindowSystem ${KF5_DEP_VERSION} REQUIRED)
+find_package(KF5Service ${KF5_DEP_VERSION} REQUIRED)
+find_package(KF5KIO ${KF5_DEP_VERSION} REQUIRED)
 
 # no X11 stuff on mac
 if (NOT APPLE)
diff --git a/src/runtime/CMakeLists.txt b/src/runtime/CMakeLists.txt
index cb286d0..bc7c3f8 100644
--- a/src/runtime/CMakeLists.txt
+++ b/src/runtime/CMakeLists.txt
@@ -7,6 +7,7 @@ remove_definitions(-DQT_NO_CAST_FROM_ASCII)
 set(kglobalaccelprivate_SRCS
     kglobalacceld.cpp
     kglobalaccel_interface.cpp
+    kserviceactioncomponent.cpp
     component.cpp
     logging.cpp
     globalshortcut.cpp
@@ -29,6 +30,8 @@ target_link_libraries(KF5GlobalAccelPrivate
    KF5::WindowSystem # KKeyServer
    KF5::CoreAddons   # KAboutData
    KF5::ConfigCore
+   KF5::Service
+   KF5::KIOWidgets
 )
 
 set_target_properties(KF5GlobalAccelPrivate PROPERTIES VERSION   \
                ${KGLOBALACCEL_VERSION_STRING}
diff --git a/src/runtime/component.cpp b/src/runtime/component.cpp
index 35b1b5c..3b4dfad 100644
--- a/src/runtime/component.cpp
+++ b/src/runtime/component.cpp
@@ -174,11 +174,10 @@ bool Component::cleanUp()
             }
         }
 
-    if (changed)
-        {
+    if (changed) {
         _registry->writeSettings();
         // We could be destroyed after this call!
-        }
+    }
 
     return changed;
     }
@@ -354,6 +353,35 @@ bool Component::isShortcutAvailable(
     return true;
     }
 
+GlobalShortcut *Component::registerShortcut(const QString &uniqueName, const QString \
&friendlyName, const QString &shortcutString, const QString &defaultShortcutString) + \
{ +    // The shortcut will register itself with us
+    GlobalShortcut *shortcut = new GlobalShortcut(
+            uniqueName,
+            friendlyName,
+            currentContext());
+
+    QList<int> keys = keysFromString(shortcutString);
+    shortcut->setDefaultKeys(keysFromString(defaultShortcutString));
+    shortcut->setIsFresh(false);
+
+    Q_FOREACH (int key, keys)
+        {
+        if (key != 0)
+            {
+            if (GlobalShortcutsRegistry::self()->getShortcutByKey(key))
+                {
+                // The shortcut is already used. The config file is
+                // broken. Ignore the request.
+                keys.removeAll(key);
+                qCWarning(KGLOBALACCELD) << "Shortcut found twice in \
kglobalshortcutsrc."<<key; +                }
+            }
+        }
+    shortcut->setKeys(keys);
+    return shortcut;
+    }
+
 
 void Component::loadSettings(KConfigGroup &configGroup)
     {
@@ -366,30 +394,10 @@ void Component::loadSettings(KConfigGroup &configGroup)
             continue;
             }
 
-        // The shortcut will register itself with us
-        GlobalShortcut *shortcut = new GlobalShortcut(
-                confKey,
-                entry[2],
-                _current);
-
-        QList<int> keys = keysFromString(entry[0]);
-        shortcut->setDefaultKeys(keysFromString(entry[1]));
-        shortcut->setIsFresh(false);
-
-        Q_FOREACH (int key, keys)
-            {
-            if (key != 0)
-                {
-                if (GlobalShortcutsRegistry::self()->getShortcutByKey(key))
-                    {
-                    // The shortcut is already used. The config file is
-                    // broken. Ignore the request.
-                    keys.removeAll(key);
-                    qCWarning(KGLOBALACCELD) << "Shortcut found twice in \
                kglobalshortcutsrc.";
-                    }
-                }
-            }
-        shortcut->setKeys(keys);
+        GlobalShortcut *shortcut = registerShortcut(confKey, entry[2], entry[0], \
entry[1]); +        if (configGroup.name().endsWith(QLatin1String(".desktop"))) {
+            shortcut->setIsPresent(true);
+        }
         }
     }
 
diff --git a/src/runtime/component.h b/src/runtime/component.h
index 66881e3..b08736d 100644
--- a/src/runtime/component.h
+++ b/src/runtime/component.h
@@ -118,6 +118,18 @@ public:
 
     void writeSettings(KConfigGroup &config) const;
 
+protected:
+    /**
+     * Create a new globalShortcut by its name
+     * @param uniqueName internal unique name to identify the shortcut
+     * @param friendlyName name for the shortcut to be presented to the user
+     * @param shortcutString string representation of the shortcut, such as "CTRL+S"
+     * @param defaultShortcutString string representation of the default shortcut,
+     *                   such as "CTRL+S", when the user choses to reset to default
+     *                   the keyboard shortcut will return to this one.
+     */
+    GlobalShortcut *registerShortcut(const QString &uniqueName, const QString \
&friendlyName, const QString &shortcutString, const QString &defaultShortcutString); \
+  public Q_SLOTS:
 
     // For dbus Q_SCRIPTABLE has to be on slots. Scriptable methods are not
@@ -134,7 +146,7 @@ public Q_SLOTS:
      *
      * @return @c true if a change was made, @c false if not.
      */
-    Q_SCRIPTABLE bool cleanUp();
+    Q_SCRIPTABLE virtual bool cleanUp();
 
     /**
      * Check if the component is currently active.
@@ -153,7 +165,7 @@ public Q_SLOTS:
     //! Returns the shortcut contexts available for the component.
     Q_SCRIPTABLE QStringList getShortcutContexts() const;
 
-    void emitGlobalShortcutPressed(const GlobalShortcut &shortcut);
+    virtual void emitGlobalShortcutPressed(const GlobalShortcut &shortcut);
 
     Q_SCRIPTABLE void invokeShortcut(const QString &shortcutName, const QString \
&context = "default");  
diff --git a/src/runtime/globalshortcutsregistry.cpp \
b/src/runtime/globalshortcutsregistry.cpp index c61f59f..7cc712b 100644
--- a/src/runtime/globalshortcutsregistry.cpp
+++ b/src/runtime/globalshortcutsregistry.cpp
@@ -18,17 +18,21 @@
 
 #include "globalshortcutsregistry.h"
 #include "component.h"
+#include "kserviceactioncomponent.h"
 #include "globalshortcut.h"
 #include "globalshortcutcontext.h"
 #include <config-kglobalaccel.h>
 #include "logging_p.h"
 #include "kglobalaccel_interface.h"
 
+#include <QDir>
+#include <QStandardPaths>
 #include <QGuiApplication>
 #include <QDebug>
 #include <QJsonArray>
 #include <KPluginLoader>
 #include <KPluginMetaData>
+#include <KDesktopFile>
 
 #include <QKeySequence>
 #include <QDBusConnection>
@@ -271,23 +275,21 @@ void GlobalShortcutsRegistry::loadSettings()
 
         // We previously stored the friendly name in a separate group. migrate
         // that
-        QString friendlyName;
-        KConfigGroup friendlyGroup(&configGroup, "Friendly Name");
-        if (friendlyGroup.isValid())
-            {
-            friendlyName = friendlyGroup.readEntry("Friendly Name");
-            friendlyGroup.deleteGroup();
-            }
-        else
-            {
-            friendlyName = configGroup.readEntry("_k_friendly_name");
-            }
+        const QString friendlyName = configGroup.readEntry("_k_friendly_name");
 
         // Create the component
-        KdeDGlobalAccel::Component *component = new KdeDGlobalAccel::Component(
+        KdeDGlobalAccel::Component *component = nullptr;
+        if (groupName.endsWith(QLatin1String(".desktop"))) {
+            component = new KdeDGlobalAccel::KServiceActionComponent(
                 groupName,
                 friendlyName,
                 this);
+        } else {
+            component = new KdeDGlobalAccel::Component(
+                    groupName,
+                    friendlyName,
+                    this);
+        }
 
         // Now load the contexts
         Q_FOREACH(const QString& context, configGroup.groupList())
@@ -306,6 +308,33 @@ void GlobalShortcutsRegistry::loadSettings()
         component->activateGlobalShortcutContext("default");
         component->loadSettings(configGroup);
         }
+
+        // Load the configured KServiceActions
+        const QStringList desktopPaths = \
QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, \
QStringLiteral("kglobalaccel"), QStandardPaths::LocateDirectory); +        foreach \
(const QString &path, desktopPaths) { +            QDir dir(path);
+            if (!dir.exists()) {
+                continue;
+            }
+            const QStringList patterns = {QStringLiteral("*.desktop")};
+            foreach (const QString &desktopFile, dir.entryList(patterns)) {
+                if (_components.contains(desktopFile)) {
+                    continue;
+                }
+
+                KDesktopFile f(dir.filePath(desktopFile));
+                if (f.noDisplay()) {
+                    continue;
+                }
+
+                KdeDGlobalAccel::KServiceActionComponent *component = new \
KdeDGlobalAccel::KServiceActionComponent( +                desktopFile,
+                f.readName(),
+                this);
+                component->activateGlobalShortcutContext(QStringLiteral("default"));
+                component->loadFromService();
+            }
+        }
     }
 
 
diff --git a/src/runtime/kglobalacceld.cpp b/src/runtime/kglobalacceld.cpp
index b8e172e..07e7e2b 100644
--- a/src/runtime/kglobalacceld.cpp
+++ b/src/runtime/kglobalacceld.cpp
@@ -27,6 +27,7 @@
 #include "globalshortcutcontext.h"
 #include "globalshortcutsregistry.h"
 #include "logging_p.h"
+#include "kserviceactioncomponent.h"
 
 #include <QtCore/QTimer>
 #include <QtCore/QMetaMethod>
@@ -146,14 +147,22 @@ KdeDGlobalAccel::Component \
*KGlobalAccelDPrivate::component(const QStringList &a  {
     // Get the component for the action. If we have none create a new one
     KdeDGlobalAccel::Component *component = \
GlobalShortcutsRegistry::self()->getComponent(actionId.at(KGlobalAccel::ComponentUnique));
                
-    if (!component)
-        {
-        component = new KdeDGlobalAccel::Component(
-                actionId.at(KGlobalAccel::ComponentUnique),
-                actionId.at(KGlobalAccel::ComponentFriendly),
-                GlobalShortcutsRegistry::self());
-        Q_ASSERT(component);
+    if (!component) {
+        if (actionId.at(KGlobalAccel::ComponentUnique).endsWith(QLatin1String(".desktop"))) \
{ +            component = new KdeDGlobalAccel::KServiceActionComponent(
+                    actionId.at(KGlobalAccel::ComponentUnique),
+                    actionId.at(KGlobalAccel::ComponentFriendly),
+                    GlobalShortcutsRegistry::self());
+            component->activateGlobalShortcutContext(QStringLiteral("default"));
+            static_cast<KdeDGlobalAccel::KServiceActionComponent \
*>(component)->loadFromService(); +        } else {
+            component = new KdeDGlobalAccel::Component(
+                    actionId.at(KGlobalAccel::ComponentUnique),
+                    actionId.at(KGlobalAccel::ComponentFriendly),
+                    GlobalShortcutsRegistry::self());
         }
+        Q_ASSERT(component);
+    }
     return component;
 }
 
diff --git a/src/runtime/kserviceactioncomponent.cpp \
b/src/runtime/kserviceactioncomponent.cpp new file mode 100644
index 0000000..c64bf15
--- /dev/null
+++ b/src/runtime/kserviceactioncomponent.cpp
@@ -0,0 +1,109 @@
+/* Copyright (C) 2008 Michael Jansen <kde@michael-jansen.biz>
+   Copyright (C) 2016 Marco Martin <mart@kde.org>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public License
+   along with this library; see the file COPYING.LIB.  If not, write to
+   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301, USA.
+*/
+
+#include "kserviceactioncomponent.h"
+#include "globalshortcutcontext.h"
+#include "logging_p.h"
+
+#include <KRun>
+
+#include <QDebug>
+#include <QProcess>
+#include <QDir>
+
+
+namespace KdeDGlobalAccel {
+
+KServiceActionComponent::KServiceActionComponent(
+            const QString &serviceStorageId,
+            const QString &friendlyName,
+            GlobalShortcutsRegistry *registry)
+    :   Component(serviceStorageId, friendlyName, registry),
+        m_serviceStorageId(serviceStorageId),
+        m_desktopFile(QStandardPaths::locate(QStandardPaths::GenericDataLocation, \
QStringLiteral("kglobalaccel/") + serviceStorageId)) +    {
+    }
+
+
+KServiceActionComponent::~KServiceActionComponent()
+    {
+    }
+
+void KServiceActionComponent::emitGlobalShortcutPressed( const GlobalShortcut \
&shortcut ) +    {
+    if (shortcut.uniqueName() == QStringLiteral("_launch"))
+        {
+        KRun::run(m_desktopFile.desktopGroup().readEntry(QStringLiteral("Exec"), \
QString()), {}, nullptr); +        return;
+        }
+    foreach(const QString &action, m_desktopFile.readActions())
+        {
+        if (action == shortcut.uniqueName())
+            {
+            KRun::run(m_desktopFile.actionGroup(action).readEntry(QStringLiteral("Exec"), \
QString()), {}, nullptr); +            return;
+            }
+        }
+    }
+
+
+
+void KServiceActionComponent::loadFromService()
+    {
+
+    QString shortcutString;
+
+    QStringList shortcuts = \
m_desktopFile.desktopGroup().readEntry(QStringLiteral("X-KDE-Shortcuts"), \
QString()).split(QChar(',')); +    if (shortcuts.size() > 0) {
+        shortcutString = shortcuts.join(QChar('\\'));
+    }
+
+    GlobalShortcut *shortcut = registerShortcut(QStringLiteral("_launch"), \
m_desktopFile.readName(), shortcutString, shortcutString); +    \
shortcut->setIsPresent(true); +
+    foreach(const QString &action, m_desktopFile.readActions())
+        {
+        shortcuts = \
m_desktopFile.actionGroup(action).readEntry(QStringLiteral("X-KDE-Shortcuts"), \
QString()).split(QChar(',')); +        if (shortcuts.size() > 0)
+            {
+            shortcutString = shortcuts.join(QChar('\\'));
+            }
+
+        GlobalShortcut *shortcut = registerShortcut(action, \
m_desktopFile.actionGroup(action).readEntry(QStringLiteral("Name")), shortcutString, \
shortcutString); +        shortcut->setIsPresent(true);
+        }
+    }
+
+bool KServiceActionComponent::cleanUp()
+{
+    qCDebug(KGLOBALACCELD) << "Disabling desktop file";
+
+    for (GlobalShortcut *shortcut : allShortcuts()) {
+        shortcut->setIsPresent(false);
+    }
+
+    m_desktopFile.desktopGroup().writeEntry("NoDisplay", true);
+    m_desktopFile.desktopGroup().sync();
+
+    return Component::cleanUp();
+}
+
+} // namespace KdeDGlobalAccel
+
+#include "moc_kserviceactioncomponent.cpp"
diff --git a/src/runtime/kserviceactioncomponent.h \
b/src/runtime/kserviceactioncomponent.h new file mode 100644
index 0000000..c9f27f2
--- /dev/null
+++ b/src/runtime/kserviceactioncomponent.h
@@ -0,0 +1,61 @@
+#ifndef KSERVICEACTIONCOMPONENT_H
+#define KSERVICEACTIONCOMPONENT_H
+/* Copyright (C) 2008 Michael Jansen <kde@michael-jansen.biz>
+   Copyright (C) 2016 Marco Martin <mart@kde.org>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public License
+   along with this library; see the file COPYING.LIB.  If not, write to
+   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301, USA.
+*/
+
+#include "component.h"
+
+#include <KDesktopFile>
+
+namespace KdeDGlobalAccel {
+
+/**
+ * @author Michael Jansen <kde@michael-jansen.biz>
+ */
+class KServiceActionComponent : public Component
+{
+    Q_OBJECT
+
+public:
+
+    //! Creates a new component. The component will be registered with @p
+    //! registry if specified and registered with dbus.
+    KServiceActionComponent(
+            const QString &serviceStorageId,
+            const QString &friendlyName,
+            GlobalShortcutsRegistry *registry = NULL);
+
+    ~KServiceActionComponent();
+
+    void loadFromService();
+    void emitGlobalShortcutPressed(const GlobalShortcut &shortcut) Q_DECL_OVERRIDE;
+
+    bool cleanUp() Q_DECL_OVERRIDE;
+
+private:
+    QString m_serviceStorageId;
+    KDesktopFile m_desktopFile;
+};
+
+
+
+}
+
+
+#endif /* #ifndef COMPONENT_H */


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic