Commit e5fa5cbc authored by Marco Martin's avatar Marco Martin

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
parent c49adf3e
......@@ -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)
......
......@@ -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}
......
......@@ -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);
}
}
}
......
......@@ -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");
......
......@@ -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();
}
}
}
......
......@@ -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;
}
......
/* 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"
#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 */
Markdown is supported
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