KGlobalAccel

component.cpp
1 /*
2  SPDX-FileCopyrightText: 2008 Michael Jansen <[email protected]>
3 
4  SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6 
7 #include "component.h"
8 
9 #include "globalshortcutcontext.h"
10 #include "globalshortcutsregistry.h"
11 #include "kglobalaccel_interface.h"
12 #include "logging_p.h"
13 #include <config-kglobalaccel.h>
14 
15 #include <QKeySequence>
16 #include <QStringList>
17 
18 #if HAVE_X11
19 #include <QX11Info>
20 #endif
21 
22 static QList<int> keysFromString(const QString &str)
23 {
24  QList<int> ret;
25  if (str == QLatin1String("none")) {
26  return ret;
27  }
28  const QStringList strList = str.split('\t');
29  for (const QString &s : strList) {
30  int key = QKeySequence(s)[0];
31  if (key != -1) { // sanity check just in case
32  ret.append(key);
33  }
34  }
35  return ret;
36 }
37 
38 static QString stringFromKeys(const QList<int> &keys)
39 {
40  if (keys.isEmpty()) {
41  return QStringLiteral("none");
42  }
43  QString ret;
44  for (int key : keys) {
45  ret.append(QKeySequence(key).toString());
46  ret.append('\t');
47  }
48  ret.chop(1);
49  return ret;
50 }
51 
52 namespace KdeDGlobalAccel
53 {
54 Component::Component(const QString &uniqueName, const QString &friendlyName, GlobalShortcutsRegistry *registry)
55  : _uniqueName(uniqueName)
56  , _friendlyName(friendlyName)
57  , _registry(registry)
58 {
59  // Make sure we do no get uniquenames still containing the context
60  Q_ASSERT(uniqueName.indexOf("|") == -1);
61 
62  // Register ourselve with the registry
63  if (_registry) {
64  _registry->addComponent(this);
65  }
66 
67  QString DEFAULT = QStringLiteral("default");
68  createGlobalShortcutContext(DEFAULT, QStringLiteral("Default Context"));
69  _current = _contexts.value(DEFAULT);
70 }
71 
72 Component::~Component()
73 {
74  // Remove ourselve from the registry
75  if (_registry) {
76  _registry->takeComponent(this);
77  }
78 
79  // We delete all shortcuts from all contexts
80  qDeleteAll(_contexts);
81 }
82 
83 bool Component::activateGlobalShortcutContext(const QString &uniqueName)
84 {
85  if (!_contexts.value(uniqueName)) {
86  createGlobalShortcutContext(uniqueName, "TODO4");
87  return false;
88  }
89 
90  // Deactivate the current contexts shortcuts
92 
93  // Switch the context
94  _current = _contexts.value(uniqueName);
95 
96  return true;
97 }
98 
99 void Component::activateShortcuts()
100 {
101  for (GlobalShortcut *shortcut : qAsConst(_current->_actions)) {
102  shortcut->setActive();
103  }
104 }
105 
107 {
108  GlobalShortcutContext *context = _contexts.value(contextName);
109  if (context) {
110  return context->_actions.values();
111  } else {
112  return QList<GlobalShortcut *>();
113  }
114 }
115 
117 {
118  GlobalShortcutContext *context = _contexts.value(contextName);
119  if (!context) {
121  }
122 
123  return context->allShortcutInfos();
124 }
125 
127 {
128  bool changed = false;
129 
130  const auto actions = _current->_actions;
131  for (GlobalShortcut *shortcut : actions) {
132  qCDebug(KGLOBALACCELD) << _current->_actions.size();
133  if (!shortcut->isPresent()) {
134  changed = true;
135  shortcut->unRegister();
136  }
137  }
138 
139  if (changed) {
140  _registry->writeSettings();
141  // We could be destroyed after this call!
142  }
143 
144  return changed;
145 }
146 
148 {
149  if (_contexts.value(uniqueName)) {
150  qCDebug(KGLOBALACCELD) << "Shortcut Context " << uniqueName << "already exists for component " << _uniqueName;
151  return false;
152  }
153  _contexts.insert(uniqueName, new GlobalShortcutContext(uniqueName, friendlyName, this));
154  return true;
155 }
156 
158 {
159  return _current;
160 }
161 
163 {
164  QString dbusPath = _uniqueName;
165  // Clean up for dbus usage: any non-alphanumeric char should be turned into '_'
166  const int len = dbusPath.length();
167  for (int i = 0; i < len; ++i) {
168  if (!dbusPath[i].isLetterOrNumber() || dbusPath[i].unicode() >= 0x7F)
169  // DBus path can only contain ASCII characters
170  dbusPath[i] = QLatin1Char('_');
171  }
172  // QDBusObjectPath could be a little bit easier to handle :-)
173  return QDBusObjectPath(_registry->dbusPath().path() + "component/" + dbusPath);
174 }
175 
176 void Component::deactivateShortcuts(bool temporarily)
177 {
178  for (GlobalShortcut *shortcut : qAsConst(_current->_actions)) {
179  if (temporarily //
180  && uniqueName() == QLatin1String("kwin") //
181  && shortcut->uniqueName() == QLatin1String("Block Global Shortcuts")) {
182  continue;
183  }
184  shortcut->setInactive();
185  }
186 }
187 
188 void Component::emitGlobalShortcutPressed(const GlobalShortcut &shortcut)
189 {
190 #if HAVE_X11
191  // pass X11 timestamp
192  long timestamp = QX11Info::appTime();
193  // Make sure kglobalacceld has ungrabbed the keyboard after receiving the
194  // keypress, otherwise actions in application that try to grab the
195  // keyboard (e.g. in kwin) may fail to do so. There is still a small race
196  // condition with this being out-of-process.
197  if (_registry->_manager) {
198  _registry->_manager->syncWindowingSystem();
199  }
200 #else
201  long timestamp = 0;
202 #endif
203 
204  // Make sure it is one of ours
205  if (shortcut.context()->component() != this) {
206  // In production mode do nothing
207  return;
208  }
209 
210  Q_EMIT globalShortcutPressed(shortcut.context()->component()->uniqueName(), shortcut.uniqueName(), timestamp);
211 }
212 
213 void Component::invokeShortcut(const QString &shortcutName, const QString &context)
214 {
215  GlobalShortcut *shortcut = getShortcutByName(shortcutName, context);
216  if (shortcut)
217  emitGlobalShortcutPressed(*shortcut);
218 }
219 
221 {
222  if (_friendlyName.isEmpty())
223  return _uniqueName;
224  return _friendlyName;
225 }
226 
228 {
229  return _current->getShortcutByKey(key);
230 }
231 
233 {
235  for (GlobalShortcutContext *context : qAsConst(_contexts)) {
236  GlobalShortcut *sc = context->getShortcutByKey(key);
237  if (sc)
238  rc.append(sc);
239  }
240  return rc;
241 }
242 
243 GlobalShortcut *Component::getShortcutByName(const QString &uniqueName, const QString &context) const
244 {
245  if (!_contexts.value(context)) {
246  return nullptr;
247  }
248 
249  return _contexts.value(context)->_actions.value(uniqueName);
250 }
251 
253 {
254  return _contexts.keys();
255 }
256 
258 {
259  // The component is active if at least one of it's global shortcuts is
260  // present.
261  for (GlobalShortcut *shortcut : qAsConst(_current->_actions)) {
262  if (shortcut->isPresent())
263  return true;
264  }
265  return false;
266 }
267 
268 bool Component::isShortcutAvailable(int key, const QString &component, const QString &context) const
269 {
270  qCDebug(KGLOBALACCELD) << QKeySequence(key).toString() << component;
271 
272  // if this component asks for the key. only check the keys in the same
273  // context
274  if (component == uniqueName()) {
275  const auto actions = shortcutContext(context)->_actions;
276  for (GlobalShortcut *sc : actions) {
277  if (sc->keys().contains(key))
278  return false;
279  }
280  } else {
281  for (GlobalShortcutContext *ctx : qAsConst(_contexts)) {
282  for (GlobalShortcut *sc : qAsConst(ctx->_actions)) {
283  if (sc->keys().contains(key))
284  return false;
285  }
286  }
287  }
288  return true;
289 }
290 
292 Component::registerShortcut(const QString &uniqueName, const QString &friendlyName, const QString &shortcutString, const QString &defaultShortcutString)
293 {
294  // The shortcut will register itself with us
295  GlobalShortcut *shortcut = new GlobalShortcut(uniqueName, friendlyName, currentContext());
296 
297  const QList<int> keys = keysFromString(shortcutString);
298  shortcut->setDefaultKeys(keysFromString(defaultShortcutString));
299  shortcut->setIsFresh(false);
300  QList<int> newKeys = keys;
301  for (int key : keys) {
302  if (key != 0) {
303  if (GlobalShortcutsRegistry::self()->getShortcutByKey(key)) {
304  // The shortcut is already used. The config file is
305  // broken. Ignore the request.
306  newKeys.removeAll(key);
307  qCWarning(KGLOBALACCELD) << "Shortcut found twice in kglobalshortcutsrc." << key;
308  }
309  }
310  }
311  shortcut->setKeys(keys);
312  return shortcut;
313 }
314 
316 {
317  // GlobalShortcutsRegistry::loadSettings handles contexts.
318  const auto listKeys = configGroup.keyList();
319  for (const QString &confKey : listKeys) {
320  const QStringList entry = configGroup.readEntry(confKey, QStringList());
321  if (entry.size() != 3) {
322  continue;
323  }
324 
325  GlobalShortcut *shortcut = registerShortcut(confKey, entry[2], entry[0], entry[1]);
326  if (configGroup.name().endsWith(QLatin1String(".desktop"))) {
327  shortcut->setIsPresent(true);
328  }
329  }
330 }
331 
333 {
334  _friendlyName = name;
335 }
336 
338 {
339  return _contexts.value(contextName);
340 }
341 
342 GlobalShortcutContext const *Component::shortcutContext(const QString &contextName) const
343 {
344  return _contexts.value(contextName);
345 }
346 
348 {
349  GlobalShortcutContext *context = _contexts.value(contextName);
350  if (!context) {
351  return QStringList();
352  }
353 
354  return context->_actions.keys();
355 }
356 
357 QString Component::uniqueName() const
358 {
359  return _uniqueName;
360 }
361 
362 void Component::unregisterShortcut(const QString &uniqueName)
363 {
364  // Now wrote all contexts
365  for (GlobalShortcutContext *context : qAsConst(_contexts)) {
366  if (context->_actions.value(uniqueName)) {
367  delete context->takeShortcut(context->_actions.value(uniqueName));
368  }
369  }
370 }
371 
372 void Component::writeSettings(KConfigGroup &configGroup) const
373 {
374  // If we don't delete the current content global shortcut
375  // registrations will never not deleted after forgetGlobalShortcut()
376  configGroup.deleteGroup();
377 
378  // Now write all contexts
379  for (GlobalShortcutContext *context : qAsConst(_contexts)) {
380  KConfigGroup contextGroup;
381 
382  if (context->uniqueName() == QLatin1String("default")) {
383  contextGroup = configGroup;
384  // Write the friendly name
385  contextGroup.writeEntry("_k_friendly_name", friendlyName());
386  } else {
387  contextGroup = KConfigGroup(&configGroup, context->uniqueName());
388  // Write the friendly name
389  contextGroup.writeEntry("_k_friendly_name", context->friendlyName());
390  }
391 
392  // qCDebug(KGLOBALACCELD) << "writing group " << _uniqueName << ":" << context->uniqueName();
393 
394  for (const GlobalShortcut *shortcut : qAsConst(context->_actions)) {
395  // qCDebug(KGLOBALACCELD) << "writing" << shortcut->uniqueName();
396 
397  // We do not write fresh shortcuts.
398  // We do not write session shortcuts
399  if (shortcut->isFresh() || shortcut->isSessionShortcut()) {
400  continue;
401  }
402  // qCDebug(KGLOBALACCELD) << "really writing" << shortcut->uniqueName();
403 
404  QStringList entry(stringFromKeys(shortcut->keys()));
405  entry.append(stringFromKeys(shortcut->defaultKeys()));
406  entry.append(shortcut->friendlyName());
407 
408  contextGroup.writeEntry(shortcut->uniqueName(), entry);
409  }
410  }
411 }
412 
413 } // namespace KdeDGlobalAccel
bool isFresh() const
Check if the shortcut is fresh/new. Is an internal state.
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const const
QString & append(QChar ch)
QHash::iterator insert(const Key &key, const T &value)
QList< KGlobalShortcutInfo > allShortcutInfos() const
Return KGlobalShortcutInfos for all shortcuts.
GlobalShortcut * getShortcutByKey(int key) const
Get shortcut for key or nullptr.
Q_SCRIPTABLE QStringList shortcutNames(const QString &context="default") const
Get all shortcutnames living in context.
Definition: component.cpp:347
int size() const const
void setFriendlyName(const QString &)
Sets the human readable name for this component.
Definition: component.cpp:332
GlobalShortcut * getShortcutByName(const QString &uniqueName, const QString &context=QStringLiteral("default")) const
Returns the shortcut by unique name.
Definition: component.cpp:243
Q_SCRIPTABLE QList< KGlobalShortcutInfo > allShortcutInfos(const QString &context="default") const
Returns all shortcut in context.
Definition: component.cpp:116
void writeEntry(const QString &key, const QVariant &value, WriteConfigFlags pFlags=Normal)
bool isPresent() const
Check if the shortcut is present. It application is running.
GlobalShortcutContext * context()
Returns the context the shortcuts belongs to.
GlobalShortcutContext * shortcutContext(const QString &name)
Returns the shortcut context name or nullptr.
Definition: component.cpp:337
void deleteGroup(WriteConfigFlags pFlags=Normal)
void chop(int n)
Q_SCRIPTABLE bool isActive() const
Check if the component is currently active.
Definition: component.cpp:257
virtual void syncWindowingSystem()
Allows implementing plugins to synchronize with the windowing system.
int size() const const
QString uniqueName() const
Returns the unique name aka id for the shortcuts.
Global Shortcut Registry.
QDBusObjectPath dbusPath() const
Return uniqueName converted to a valid dbus path.
Definition: component.cpp:162
void append(const T &value)
QDBusObjectPath dbusPath() const
Return the root dbus path for the registry.
bool isShortcutAvailable(int key, const QString &component, const QString &context) const
Check if key is available for component component.
Definition: component.cpp:268
QString friendlyName() const
Returns the friendly name.
bool isEmpty() const const
void loadSettings(KConfigGroup &config)
Load the settings from config group config.
Definition: component.cpp:315
bool isEmpty() const const
int removeAll(const T &value)
QStringList split(const QString &sep, QString::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
bool endsWith(const QString &s, Qt::CaseSensitivity cs) const const
QList< GlobalShortcut * > allShortcuts(const QString &context=QStringLiteral("default")) const
Returns all shortcuts in context .
Definition: component.cpp:106
Represents a global shortcut.
QStringList keyList() const
QList< Key > keys() const const
GlobalShortcut * getShortcutByKey(int key) const
Returns the currently active shortcut for key.
Definition: component.cpp:227
const T value(const Key &key) const const
void deactivateShortcuts(bool temporarily=false)
Deactivate all currently active shortcuts.
Definition: component.cpp:176
QList< int > keys() const
Returns a list of keys associated with this shortcut.
QString name() const
GlobalShortcutContext * currentContext()
Return the current context.
Definition: component.cpp:157
GlobalShortcut * registerShortcut(const QString &uniqueName, const QString &friendlyName, const QString &shortcutString, const QString &defaultShortcutString)
Create a new globalShortcut by its name.
Definition: component.cpp:292
void unregisterShortcut(const QString &uniqueName)
Unregister shortcut. This will remove its siblings from all contexts.
Definition: component.cpp:362
QString toString(QKeySequence::SequenceFormat format) const const
char * toString(const T &value)
bool createGlobalShortcutContext(const QString &context, const QString &friendlyName=QString())
Creates the new global shortcut context context.
Definition: component.cpp:147
QList< int > defaultKeys() const
Returns the default keys for this shortcut.
Q_SCRIPTABLE QStringList getShortcutContexts() const
Returns the shortcut contexts available for the component.
Definition: component.cpp:252
QList< T > values() const const
int length() const const
Q_SCRIPTABLE void globalShortcutPressed(const QString &componentUnique, const QString &shortcutUnique, qlonglong timestamp)
Signals that a action for this component was triggered.
void setKeys(const QList< int >)
Sets the keys activated with this shortcut. The old keys are freed.
Component(const QString &uniqueName, const QString &friendlyName, GlobalShortcutsRegistry *registry=nullptr)
Creates a new component.
Definition: component.cpp:54
QString path() const const
void setDefaultKeys(const QList< int >)
Sets the default keys for this shortcut.
bool isSessionShortcut() const
Returns true if the shortcut is a session shortcut.
T readEntry(const QString &key, const T &aDefault) const
Q_EMITQ_EMIT
QString friendlyName() const
Return the friendly display name for this shortcut.
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
QList< GlobalShortcut * > getShortcutsByKey(int key) const
Returns the list of shortcuts (different context) registered with key.
Definition: component.cpp:232
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Sun May 16 2021 22:53:45 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.