From kde-commits Thu Aug 04 12:58:41 2016 From: Marco Martin Date: Thu, 04 Aug 2016 12:58:41 +0000 To: kde-commits Subject: [kglobalaccel] /: Launch jumplist actions via KGlobalAccel Message-Id: X-MARC-Message: https://marc.info/?l=kde-commits&m=147031553407312 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 entr= y) 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 ${KGLOBAL= ACCEL_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, con= st QString &friendlyName, const QString &shortcutString, const QString &def= aultShortcutString) + { + // The shortcut will register itself with us + GlobalShortcut *shortcut =3D new GlobalShortcut( + uniqueName, + friendlyName, + currentContext()); + + QList keys =3D keysFromString(shortcutString); + shortcut->setDefaultKeys(keysFromString(defaultShortcutString)); + shortcut->setIsFresh(false); + + Q_FOREACH (int key, keys) + { + if (key !=3D 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 kglob= alshortcutsrc."<setKeys(keys); + return shortcut; + } + = void Component::loadSettings(KConfigGroup &configGroup) { @@ -366,30 +394,10 @@ void Component::loadSettings(KConfigGroup &configGrou= p) continue; } = - // The shortcut will register itself with us - GlobalShortcut *shortcut =3D new GlobalShortcut( - confKey, - entry[2], - _current); - - QList keys =3D keysFromString(entry[0]); - shortcut->setDefaultKeys(keysFromString(entry[1])); - shortcut->setIsFresh(false); - - Q_FOREACH (int key, keys) - { - if (key !=3D 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 k= globalshortcutsrc."; - } - } - } - shortcut->setKeys(keys); + GlobalShortcut *shortcut =3D registerShortcut(confKey, entry[2], e= ntry[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 us= er + * @param shortcutString string representation of the shortcut, such a= s "CTRL+S" + * @param defaultShortcutString string representation of the default s= hortcut, + * such as "CTRL+S", when the user choses to reset t= o default + * the keyboard shortcut will return to this one. + */ + GlobalShortcut *registerShortcut(const QString &uniqueName, const QStr= ing &friendlyName, const QString &shortcutString, const QString &defaultSho= rtcutString); + 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 QS= tring &context =3D "default"); = diff --git a/src/runtime/globalshortcutsregistry.cpp b/src/runtime/globalsh= ortcutsregistry.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 #include "logging_p.h" #include "kglobalaccel_interface.h" = +#include +#include #include #include #include #include #include +#include = #include #include @@ -271,23 +275,21 @@ void GlobalShortcutsRegistry::loadSettings() = // We previously stored the friendly name in a separate group. mig= rate // that - QString friendlyName; - KConfigGroup friendlyGroup(&configGroup, "Friendly Name"); - if (friendlyGroup.isValid()) - { - friendlyName =3D friendlyGroup.readEntry("Friendly Name"); - friendlyGroup.deleteGroup(); - } - else - { - friendlyName =3D configGroup.readEntry("_k_friendly_name"); - } + const QString friendlyName =3D configGroup.readEntry("_k_friendly_= name"); = // Create the component - KdeDGlobalAccel::Component *component =3D new KdeDGlobalAccel::Com= ponent( + KdeDGlobalAccel::Component *component =3D nullptr; + if (groupName.endsWith(QLatin1String(".desktop"))) { + component =3D new KdeDGlobalAccel::KServiceActionComponent( groupName, friendlyName, this); + } else { + component =3D 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 =3D QStandardPaths::locateAll(QStan= dardPaths::GenericDataLocation, QStringLiteral("kglobalaccel"), QStandardPa= ths::LocateDirectory); + foreach (const QString &path, desktopPaths) { + QDir dir(path); + if (!dir.exists()) { + continue; + } + const QStringList patterns =3D {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 =3D ne= w KdeDGlobalAccel::KServiceActionComponent( + desktopFile, + f.readName(), + this); + component->activateGlobalShortcutContext(QStringLiteral("d= efault")); + 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 #include @@ -146,14 +147,22 @@ KdeDGlobalAccel::Component *KGlobalAccelDPrivate::com= ponent(const QStringList &a { // Get the component for the action. If we have none create a new one KdeDGlobalAccel::Component *component =3D GlobalShortcutsRegistry::sel= f()->getComponent(actionId.at(KGlobalAccel::ComponentUnique)); - if (!component) - { - component =3D new KdeDGlobalAccel::Component( - actionId.at(KGlobalAccel::ComponentUnique), - actionId.at(KGlobalAccel::ComponentFriendly), - GlobalShortcutsRegistry::self()); - Q_ASSERT(component); + if (!component) { + if (actionId.at(KGlobalAccel::ComponentUnique).endsWith(QLatin1Str= ing(".desktop"))) { + component =3D new KdeDGlobalAccel::KServiceActionComponent( + actionId.at(KGlobalAccel::ComponentUnique), + actionId.at(KGlobalAccel::ComponentFriendly), + GlobalShortcutsRegistry::self()); + component->activateGlobalShortcutContext(QStringLiteral("defau= lt")); + static_cast(compon= ent)->loadFromService(); + } else { + component =3D 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/kservice= actioncomponent.cpp new file mode 100644 index 0000000..c64bf15 --- /dev/null +++ b/src/runtime/kserviceactioncomponent.cpp @@ -0,0 +1,109 @@ +/* Copyright (C) 2008 Michael Jansen + Copyright (C) 2016 Marco Martin + + 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 Licen= se + 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 + +#include +#include +#include + + +namespace KdeDGlobalAccel { + +KServiceActionComponent::KServiceActionComponent( + const QString &serviceStorageId, + const QString &friendlyName, + GlobalShortcutsRegistry *registry) + : Component(serviceStorageId, friendlyName, registry), + m_serviceStorageId(serviceStorageId), + m_desktopFile(QStandardPaths::locate(QStandardPaths::GenericDataLo= cation, QStringLiteral("kglobalaccel/") + serviceStorageId)) + { + } + + +KServiceActionComponent::~KServiceActionComponent() + { + } + +void KServiceActionComponent::emitGlobalShortcutPressed( const GlobalShort= cut &shortcut ) + { + if (shortcut.uniqueName() =3D=3D QStringLiteral("_launch")) + { + KRun::run(m_desktopFile.desktopGroup().readEntry(QStringLiteral("E= xec"), QString()), {}, nullptr); + return; + } + foreach(const QString &action, m_desktopFile.readActions()) + { + if (action =3D=3D shortcut.uniqueName()) + { + KRun::run(m_desktopFile.actionGroup(action).readEntry(QStringL= iteral("Exec"), QString()), {}, nullptr); + return; + } + } + } + + + +void KServiceActionComponent::loadFromService() + { + + QString shortcutString; + + QStringList shortcuts =3D m_desktopFile.desktopGroup().readEntry(QStri= ngLiteral("X-KDE-Shortcuts"), QString()).split(QChar(',')); + if (shortcuts.size() > 0) { + shortcutString =3D shortcuts.join(QChar('\\')); + } + + GlobalShortcut *shortcut =3D registerShortcut(QStringLiteral("_launch"= ), m_desktopFile.readName(), shortcutString, shortcutString); + shortcut->setIsPresent(true); + + foreach(const QString &action, m_desktopFile.readActions()) + { + shortcuts =3D m_desktopFile.actionGroup(action).readEntry(QStringL= iteral("X-KDE-Shortcuts"), QString()).split(QChar(',')); + if (shortcuts.size() > 0) + { + shortcutString =3D shortcuts.join(QChar('\\')); + } + + GlobalShortcut *shortcut =3D registerShortcut(action, m_desktopFil= e.actionGroup(action).readEntry(QStringLiteral("Name")), shortcutString, sh= ortcutString); + 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/kserviceac= tioncomponent.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 + Copyright (C) 2016 Marco Martin + + 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 Licen= se + 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 + +namespace KdeDGlobalAccel { + +/** + * @author Michael Jansen + */ +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 =3D 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 */