KGlobalAccel

kserviceactioncomponent.cpp
1 /*
2  SPDX-FileCopyrightText: 2008 Michael Jansen <[email protected]>
3  SPDX-FileCopyrightText: 2016 Marco Martin <[email protected]>
4  SPDX-FileCopyrightText: 2021 David Redondo <[email protected]>
5 
6  SPDX-License-Identifier: LGPL-2.0-or-later
7 */
8 
9 #include "kserviceactioncomponent.h"
10 #include "globalshortcutcontext.h"
11 #include "logging_p.h"
12 
13 #include <QDBusConnectionInterface>
14 #include <QProcess>
15 
16 namespace KdeDGlobalAccel
17 {
18 KServiceActionComponent::KServiceActionComponent(const QString &serviceStorageId, const QString &friendlyName, GlobalShortcutsRegistry *registry)
19  : Component(serviceStorageId, friendlyName, registry)
20  , m_serviceStorageId(serviceStorageId)
21  , m_desktopFile(nullptr)
22 {
23  auto fileName = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kglobalaccel/") + serviceStorageId);
24  if (fileName.isEmpty()) {
25  // Fallback to applications data dir
26  // for custom shortcut for instance
27  fileName = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("applications/") + serviceStorageId);
28  }
29  if (fileName.isEmpty()) {
30  qCWarning(KGLOBALACCELD) << "No desktop file found for service " << serviceStorageId;
31  }
32  m_desktopFile.reset(new KDesktopFile(fileName));
33 }
34 
35 KServiceActionComponent::~KServiceActionComponent()
36 {
37 }
38 
39 void runProcess(const KConfigGroup &group, bool klauncherAvailable)
40 {
41  QStringList parts = group.readEntry(QStringLiteral("Exec"), QString()).split(QChar(' '));
42  if (parts.isEmpty()) {
43  return;
44  }
45  // sometimes entries have an %u for command line parameters
46  if (parts.last().contains(QChar('%'))) {
47  parts.pop_back();
48  }
49 
50  const QString command = parts.takeFirst();
51 
52  const auto kstart = QStandardPaths::findExecutable(QStringLiteral("kstart5"));
53  if (!kstart.isEmpty()) {
54  parts.prepend(command);
55  parts.prepend(QStringLiteral("--"));
56  QProcess::startDetached(kstart, parts);
57  } else if (klauncherAvailable) {
58  QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("org.kde.klauncher5"),
59  QStringLiteral("/KLauncher"),
60  QStringLiteral("org.kde.KLauncher"),
61  QStringLiteral("exec_blind"));
62  msg << command << parts;
63 
65  } else {
66  QProcess::startDetached(command, parts);
67  }
68 }
69 
70 void KServiceActionComponent::emitGlobalShortcutPressed(const GlobalShortcut &shortcut)
71 {
72  // TODO KF6 use ApplicationLauncherJob to start processes when it's available in a framework that we depend on
73 
74  // DBusActivatatable spec as per https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#dbus
75  if (m_desktopFile->desktopGroup().readEntry("DBusActivatable", false)) {
76  QString method;
77  const QString serviceName = m_serviceStorageId.chopped(strlen(".desktop"));
78  const QString objectPath = QStringLiteral("/%1").arg(serviceName).replace(QLatin1Char('.'), QLatin1Char('/'));
79  const QString interface = QStringLiteral("org.freedesktop.Application");
80  QDBusMessage message;
81  if (shortcut.uniqueName() == QLatin1String("_launch")) {
82  message = QDBusMessage::createMethodCall(serviceName, objectPath, interface, QStringLiteral("Activate"));
83  } else {
84  message = QDBusMessage::createMethodCall(serviceName, objectPath, interface, QStringLiteral("ActivateAction"));
85  message << shortcut.uniqueName() << QVariantList();
86  }
87  message << QVariantMap();
89  return;
90  }
91 
93  const bool klauncherAvailable = dbusDaemon->isServiceRegistered(QStringLiteral("org.kde.klauncher5"));
94 
95  // we can't use KRun there as it depends from KIO and would create a circular dep
96  if (shortcut.uniqueName() == QLatin1String("_launch")) {
97  runProcess(m_desktopFile->desktopGroup(), klauncherAvailable);
98  return;
99  }
100  const auto lstActions = m_desktopFile->readActions();
101  for (const QString &action : lstActions) {
102  if (action == shortcut.uniqueName()) {
103  runProcess(m_desktopFile->actionGroup(action), klauncherAvailable);
104  return;
105  }
106  }
107 }
108 
109 void KServiceActionComponent::loadFromService()
110 {
111  QString shortcutString;
112 
113  QStringList shortcuts = m_desktopFile->desktopGroup().readEntry(QStringLiteral("X-KDE-Shortcuts"), QString()).split(QChar(','));
114  if (!shortcuts.isEmpty()) {
115  shortcutString = shortcuts.join(QChar('\t'));
116  }
117 
118  GlobalShortcut *shortcut = registerShortcut(QStringLiteral("_launch"), m_desktopFile->readName(), shortcutString, shortcutString);
119  shortcut->setIsPresent(true);
120  const auto lstActions = m_desktopFile->readActions();
121  for (const QString &action : lstActions) {
122  shortcuts = m_desktopFile->actionGroup(action).readEntry(QStringLiteral("X-KDE-Shortcuts"), QString()).split(QChar(','));
123  if (!shortcuts.isEmpty()) {
124  shortcutString = shortcuts.join(QChar('\t'));
125  }
126 
127  GlobalShortcut *shortcut =
128  registerShortcut(action, m_desktopFile->actionGroup(action).readEntry(QStringLiteral("Name")), shortcutString, shortcutString);
129  shortcut->setIsPresent(true);
130  }
131 }
132 
134 {
135  qCDebug(KGLOBALACCELD) << "Disabling desktop file";
136 
137  const auto shortcuts = allShortcuts();
138  for (GlobalShortcut *shortcut : shortcuts) {
139  shortcut->setIsPresent(false);
140  }
141 
142  m_desktopFile->desktopGroup().writeEntry("NoDisplay", true);
143  m_desktopFile->desktopGroup().sync();
144 
145  return Component::cleanUp();
146 }
147 
148 } // namespace KdeDGlobalAccel
149 
150 #include "moc_kserviceactioncomponent.cpp"
bool cleanUp() override
Remove all currently not used global shortcuts registrations for this component and if nothing is lef...
QDBusConnectionInterface * interface() const const
bool startDetached(qint64 *pid)
QString findExecutable(const QString &executableName, const QStringList &paths)
QDBusConnection sessionBus()
QString join(const QString &separator) const const
QDBusReply< bool > isServiceRegistered(const QString &serviceName) const const
void reset(T *other)
QString uniqueName() const
Returns the unique name aka id for the shortcuts.
Global Shortcut Registry.
QString chopped(int len) const const
bool isEmpty() const const
QList< GlobalShortcut * > allShortcuts(const QString &context=QStringLiteral("default")) const
Returns all shortcuts in context .
Definition: component.cpp:106
Represents a global shortcut.
void pop_back()
QString & replace(int position, int n, QChar after)
GlobalShortcut * registerShortcut(const QString &uniqueName, const QString &friendlyName, const QString &shortcutString, const QString &defaultShortcutString)
Create a new globalShortcut by its name.
Definition: component.cpp:299
T takeFirst()
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
T & last()
QDBusPendingCall asyncCall(const QDBusMessage &message, int timeout) const const
void prepend(const T &value)
KServiceActionComponent(const QString &serviceStorageId, const QString &friendlyName, GlobalShortcutsRegistry *registry=nullptr)
Creates a new component.
T readEntry(const QString &key, const T &aDefault) const
QDBusMessage createMethodCall(const QString &service, const QString &path, const QString &interface, const QString &method)
QString locate(QStandardPaths::StandardLocation type, const QString &fileName, QStandardPaths::LocateOptions options)
virtual Q_SCRIPTABLE bool cleanUp()
Remove all currently not used global shortcuts registrations for this component and if nothing is lef...
Definition: component.cpp:126
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Thu Sep 23 2021 22:54:33 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.