KXmlGui

kkeysequencewidget.cpp
1 /*
2  This file is part of the KDE libraries
3  SPDX-FileCopyrightText: 1998 Mark Donohoe <[email protected]>
4  SPDX-FileCopyrightText: 2001 Ellis Whitehead <[email protected]>
5  SPDX-FileCopyrightText: 2007 Andreas Hartmetz <[email protected]>
6 
7  SPDX-License-Identifier: LGPL-2.0-or-later
8 */
9 
10 #include "config-xmlgui.h"
11 
12 #include "kkeysequencewidget.h"
13 #include "kkeysequencewidget_p.h"
14 #include "debug.h"
15 
16 #include <QAction>
17 #include <QKeyEvent>
18 #include <QTimer>
19 #include <QHash>
20 #include <QHBoxLayout>
21 #include <QToolButton>
22 #include <QApplication>
23 
24 #include <klocalizedstring.h>
25 #include <kmessagebox.h>
26 #include <kkeyserver.h>
27 #if HAVE_GLOBALACCEL
28 # include <kglobalaccel.h>
29 #endif
30 
31 #include "kactioncollection.h"
32 
33 class KKeySequenceWidgetPrivate
34 {
35 public:
36  KKeySequenceWidgetPrivate(KKeySequenceWidget *q);
37 
38  void init();
39 
40  static QKeySequence appendToSequence(const QKeySequence &seq, int keyQt);
41  static bool isOkWhenModifierless(int keyQt);
42 
43  void updateShortcutDisplay();
44  void startRecording();
45 
50  bool conflictWithStandardShortcuts(const QKeySequence &seq);
51 
56  bool conflictWithLocalShortcuts(const QKeySequence &seq);
57 
62  bool conflictWithGlobalShortcuts(const QKeySequence &seq);
63 
67  bool stealStandardShortcut(KStandardShortcut::StandardShortcut std, const QKeySequence &seq);
68 
69  bool checkAgainstStandardShortcuts() const
70  {
71  return checkAgainstShortcutTypes & KKeySequenceWidget::StandardShortcuts;
72  }
73 
74  bool checkAgainstGlobalShortcuts() const
75  {
76  return checkAgainstShortcutTypes & KKeySequenceWidget::GlobalShortcuts;
77  }
78 
79  bool checkAgainstLocalShortcuts() const
80  {
81  return checkAgainstShortcutTypes & KKeySequenceWidget::LocalShortcuts;
82  }
83 
84  void controlModifierlessTimout()
85  {
86  if (nKey != 0 && !modifierKeys) {
87  // No modifier key pressed currently. Start the timout
88  modifierlessTimeout.start(600);
89  } else {
90  // A modifier is pressed. Stop the timeout
91  modifierlessTimeout.stop();
92  }
93 
94  }
95 
96  void cancelRecording()
97  {
98  keySequence = oldKeySequence;
99  doneRecording();
100  }
101 
102 #if HAVE_GLOBALACCEL
103  bool promptStealShortcutSystemwide(
104  QWidget *parent,
105  const QHash<QKeySequence, QList<KGlobalShortcutInfo> > &shortcuts,
106  const QKeySequence &sequence)
107  {
108  if (shortcuts.isEmpty()) {
109  // Usage error. Just say no
110  return false;
111  }
112 
113  QString clashingKeys;
114  for (auto it = shortcuts.begin(), end = shortcuts.end(); it != end; ++it) {
115  const auto seqAsString = it.key().toString();
116  for (const KGlobalShortcutInfo &info : it.value()) {
117  clashingKeys += i18n("Shortcut '%1' in Application %2 for action %3\n",
118  seqAsString,
119  info.componentFriendlyName(),
120  info.friendlyName());
121  }
122  }
123 
124  const int hashSize = shortcuts.size();
125 
126  QString message = i18ncp("%1 is the number of conflicts (hidden), %2 is the key sequence of the shortcut that is problematic",
127  "The shortcut '%2' conflicts with the following key combination:\n",
128  "The shortcut '%2' conflicts with the following key combinations:\n",
129  hashSize, sequence.toString());
130  message += clashingKeys;
131 
132  QString title = i18ncp("%1 is the number of shortcuts with which there is a conflict",
133  "Conflict with Registered Global Shortcut", "Conflict with Registered Global Shortcuts", hashSize);
134 
135  return KMessageBox::warningContinueCancel(parent, message, title, KGuiItem(i18nc("@action:button", "Reassign")))
136  == KMessageBox::Continue;
137  }
138 #endif
139 
140 //private slot
141  void doneRecording(bool validate = true);
142 
143 //members
144  KKeySequenceWidget *const q;
145  QHBoxLayout *layout;
146  KKeySequenceButton *keyButton;
147  QToolButton *clearButton;
148 
150  QKeySequence oldKeySequence;
151  QTimer modifierlessTimeout;
152  bool allowModifierless;
153  uint nKey;
154  uint modifierKeys;
155  bool isRecording;
156  bool multiKeyShortcutsAllowed;
157  QString componentName;
158 
160  KKeySequenceWidget::ShortcutTypes checkAgainstShortcutTypes;
161 
165  QList<QAction *> checkList; // deprecated
166 
170  QList<KActionCollection *> checkActionCollections;
171 
175  QList<QAction *> stealActions;
176 
177  bool stealShortcuts(const QList<QAction *> &actions, const QKeySequence &seq);
178  void wontStealShortcut(QAction *item, const QKeySequence &seq);
179 
180 };
181 
182 KKeySequenceWidgetPrivate::KKeySequenceWidgetPrivate(KKeySequenceWidget *q)
183  : q(q)
184  , layout(nullptr)
185  , keyButton(nullptr)
186  , clearButton(nullptr)
187  , allowModifierless(false)
188  , nKey(0)
189  , modifierKeys(0)
190  , isRecording(false)
191  , multiKeyShortcutsAllowed(true)
192  , componentName()
193  , checkAgainstShortcutTypes(KKeySequenceWidget::LocalShortcuts | KKeySequenceWidget::GlobalShortcuts)
194  , stealActions()
195 {}
196 
197 bool KKeySequenceWidgetPrivate::stealShortcuts(
198  const QList<QAction *> &actions,
199  const QKeySequence &seq)
200 {
201 
202  const int listSize = actions.size();
203 
204  QString title = i18ncp("%1 is the number of conflicts", "Shortcut Conflict", "Shortcut Conflicts", listSize);
205 
206  QString conflictingShortcuts;
207  for (const QAction *action : actions) {
208  conflictingShortcuts += i18n("Shortcut '%1' for action '%2'\n",
211  }
212  QString message = i18ncp("%1 is the number of ambigious shortcut clashes (hidden)",
213  "The \"%2\" shortcut is ambiguous with the following shortcut.\n"
214  "Do you want to assign an empty shortcut to this action?\n"
215  "%3",
216  "The \"%2\" shortcut is ambiguous with the following shortcuts.\n"
217  "Do you want to assign an empty shortcut to these actions?\n"
218  "%3",
219  listSize,
221  conflictingShortcuts);
222 
223  if (KMessageBox::warningContinueCancel(q, message, title, KGuiItem(i18nc("@action:button", "Reassign"))) != KMessageBox::Continue) {
224  return false;
225  }
226 
227  return true;
228 }
229 
230 void KKeySequenceWidgetPrivate::wontStealShortcut(QAction *item, const QKeySequence &seq)
231 {
232  QString title(i18nc("@title:window", "Shortcut conflict"));
233  QString msg(i18n("<qt>The '%1' key combination is already used by the <b>%2</b> action.<br>"
234  "Please select a different one.</qt>", seq.toString(QKeySequence::NativeText),
236  KMessageBox::sorry(q, msg, title);
237 }
238 
240  : QWidget(parent),
241  d(new KKeySequenceWidgetPrivate(this))
242 {
243  d->init();
244  setFocusProxy(d->keyButton);
245  connect(d->keyButton, &KKeySequenceButton::clicked, this, &KKeySequenceWidget::captureKeySequence);
247  connect(&d->modifierlessTimeout, SIGNAL(timeout()), this, SLOT(doneRecording()));
248  //TODO: how to adopt style changes at runtime?
249  /*QFont modFont = d->clearButton->font();
250  modFont.setStyleHint(QFont::TypeWriter);
251  d->clearButton->setFont(modFont);*/
252  d->updateShortcutDisplay();
253 }
254 
255 void KKeySequenceWidgetPrivate::init()
256 {
257  layout = new QHBoxLayout(q);
258  layout->setContentsMargins(0, 0, 0, 0);
259 
260  keyButton = new KKeySequenceButton(this, q);
261  keyButton->setFocusPolicy(Qt::StrongFocus);
262  keyButton->setIcon(QIcon::fromTheme(QStringLiteral("configure")));
263  keyButton->setToolTip(i18nc("@info:tooltip", "Click on the button, then enter the shortcut like you would in the program.\nExample for Ctrl+A: hold the Ctrl key and press A."));
264  layout->addWidget(keyButton);
265 
266  clearButton = new QToolButton(q);
267  layout->addWidget(clearButton);
268 
269  if (qApp->isLeftToRight()) {
270  clearButton->setIcon(QIcon::fromTheme(QStringLiteral("edit-clear-locationbar-rtl")));
271  } else {
272  clearButton->setIcon(QIcon::fromTheme(QStringLiteral("edit-clear-locationbar-ltr")));
273  }
274 }
275 
277 {
278  delete d;
279 }
280 
282 {
283  return d->checkAgainstShortcutTypes;
284 }
285 
287 {
288  d->componentName = componentName;
289 }
290 
291 bool KKeySequenceWidget::multiKeyShortcutsAllowed() const
292 {
293  return d->multiKeyShortcutsAllowed;
294 }
295 
297 {
298  d->multiKeyShortcutsAllowed = allowed;
299 }
300 
302 {
303  d->checkAgainstShortcutTypes = types;
304 }
305 
307 {
308  d->allowModifierless = allow;
309 }
310 
312 {
313  if (keySequence.isEmpty()) {
314  return true;
315  }
316  return !(d->conflictWithLocalShortcuts(keySequence)
317  || d->conflictWithGlobalShortcuts(keySequence)
318  || d->conflictWithStandardShortcuts(keySequence));
319 }
320 
322 {
323  return d->allowModifierless;
324 }
325 
327 {
328  d->clearButton->setVisible(show);
329 }
330 
331 #if KXMLGUI_BUILD_DEPRECATED_SINCE(4, 1)
332 void KKeySequenceWidget::setCheckActionList(const QList<QAction *> &checkList) // deprecated
333 {
334  d->checkList = checkList;
335  Q_ASSERT(d->checkActionCollections.isEmpty()); // don't call this method if you call setCheckActionCollections!
336 }
337 #endif
338 
340 {
341  d->checkActionCollections = actionCollections;
342 }
343 
344 //slot
346 {
347  d->startRecording();
348 }
349 
351 {
352  return d->keySequence;
353 }
354 
355 //slot
357 {
358  // oldKeySequence holds the key sequence before recording started, if setKeySequence()
359  // is called while not recording then set oldKeySequence to the existing sequence so
360  // that the keySequenceChanged() signal is emitted if the new and previous key
361  // sequences are different
362  if (!d->isRecording) {
363  d->oldKeySequence = d->keySequence;
364  }
365 
366  d->keySequence = seq;
367  d->doneRecording(validate == Validate);
368 }
369 
370 //slot
372 {
374 }
375 
376 //slot
378 {
379  QSet<KActionCollection *> changedCollections;
380 
381  for (QAction *stealAction : qAsConst(d->stealActions)) {
382 
383  // Stealing a shortcut means setting it to an empty one.
384  stealAction->setShortcuts(QList<QKeySequence>());
385 
386  // The following code will find the action we are about to
387  // steal from and save it's actioncollection.
388  KActionCollection *parentCollection = nullptr;
389  for (KActionCollection *collection : qAsConst(d->checkActionCollections)) {
390  if (collection->actions().contains(stealAction)) {
391  parentCollection = collection;
392  break;
393  }
394  }
395 
396  // Remember the changed collection
397  if (parentCollection) {
398  changedCollections.insert(parentCollection);
399  }
400  }
401 
402  for (KActionCollection *col : qAsConst(changedCollections)) {
403  col->writeSettings();
404  }
405 
406  d->stealActions.clear();
407 }
408 
409 void KKeySequenceWidgetPrivate::startRecording()
410 {
411  nKey = 0;
412  modifierKeys = 0;
413  oldKeySequence = keySequence;
415  isRecording = true;
416  keyButton->grabKeyboard();
417 
418  if (!QWidget::keyboardGrabber()) {
419  qCWarning(DEBUG_KXMLGUI) << "Failed to grab the keyboard! Most likely qt's nograb option is active";
420  }
421 
422  keyButton->setDown(true);
423  updateShortcutDisplay();
424 }
425 
426 void KKeySequenceWidgetPrivate::doneRecording(bool validate)
427 {
428  modifierlessTimeout.stop();
429  isRecording = false;
430  keyButton->releaseKeyboard();
431  keyButton->setDown(false);
432  stealActions.clear();
433 
434  if (keySequence == oldKeySequence) {
435  // The sequence hasn't changed
436  updateShortcutDisplay();
437  return;
438  }
439 
440  if (validate && !q->isKeySequenceAvailable(keySequence)) {
441  // The sequence had conflicts and the user said no to stealing it
442  keySequence = oldKeySequence;
443  } else {
444  emit q->keySequenceChanged(keySequence);
445  }
446 
447  updateShortcutDisplay();
448 }
449 
450 bool KKeySequenceWidgetPrivate::conflictWithGlobalShortcuts(const QKeySequence &keySequence)
451 {
452 #ifdef Q_OS_WIN
453  //on windows F12 is reserved by the debugger at all times, so we can't use it for a global shortcut
455  QString title = i18n("Reserved Shortcut");
456  QString message = i18n("The F12 key is reserved on Windows, so cannot be used for a global shortcut.\n"
457  "Please choose another one.");
458 
459  KMessageBox::sorry(q, message, title);
460  return false;
461  }
462 #endif
463 
464 #if HAVE_GLOBALACCEL
465  if (!(checkAgainstShortcutTypes & KKeySequenceWidget::GlobalShortcuts)) {
466  return false;
467  }
468 
469  // Global shortcuts are on key+modifier shortcuts. They can clash with
470  // each of the keys of a multi key shortcut.
472  for (int i = 0; i < keySequence.count(); ++i) {
473  QKeySequence tmp(keySequence[i]);
474 
475  if (!KGlobalAccel::isGlobalShortcutAvailable(tmp, componentName)) {
477  }
478  }
479 
480  if (!others.isEmpty()
481  && !promptStealShortcutSystemwide(q, others, keySequence)) {
482  return true;
483  }
484 
485  // The user approved stealing the shortcut. We have to steal
486  // it immediately because KAction::setGlobalShortcut() refuses
487  // to set a global shortcut that is already used. There is no
488  // error it just silently fails. So be nice because this is
489  // most likely the first action that is done in the slot
490  // listening to keySequenceChanged().
491  for (int i = 0; i < keySequence.count(); ++i) {
493  }
494  return false;
495 #else
496  Q_UNUSED(keySequence);
497  return false;
498 #endif
499 }
500 
501 static bool shortcutsConflictWith(const QList<QKeySequence> &shortcuts, const QKeySequence &needle)
502 {
503  if (needle.isEmpty()) {
504  return false;
505  }
506 
507  for (const QKeySequence &sequence : shortcuts) {
508  if (sequence.isEmpty()) {
509  continue;
510  }
511 
512  if (sequence.matches(needle) != QKeySequence::NoMatch
513  || needle.matches(sequence) != QKeySequence::NoMatch) {
514  return true;
515  }
516  }
517 
518  return false;
519 }
520 
521 bool KKeySequenceWidgetPrivate::conflictWithLocalShortcuts(const QKeySequence &keySequence)
522 {
523  if (!(checkAgainstShortcutTypes & KKeySequenceWidget::LocalShortcuts)) {
524  return false;
525  }
526 
527  // We have actions both in the deprecated checkList and the
528  // checkActionCollections list. Add all the actions to a single list to
529  // be able to process them in a single loop below.
530  // Note that this can't be done in setCheckActionCollections(), because we
531  // keep pointers to the action collections, and between the call to
532  // setCheckActionCollections() and this function some actions might already be
533  // removed from the collection again.
534  QList<QAction *> allActions;
535  allActions += checkList;
536  for (KActionCollection *collection : qAsConst(checkActionCollections)) {
537  allActions += collection->actions();
538  }
539 
540  // Because of multikey shortcuts we can have clashes with many shortcuts.
541  //
542  // Example 1:
543  //
544  // Application currently uses 'CTRL-X,a', 'CTRL-X,f' and 'CTRL-X,CTRL-F'
545  // and the user wants to use 'CTRL-X'. 'CTRL-X' will only trigger as
546  // 'activatedAmbiguously()' for obvious reasons.
547  //
548  // Example 2:
549  //
550  // Application currently uses 'CTRL-X'. User wants to use 'CTRL-X,CTRL-F'.
551  // This will shadow 'CTRL-X' for the same reason as above.
552  //
553  // Example 3:
554  //
555  // Some weird combination of Example 1 and 2 with three shortcuts using
556  // 1/2/3 key shortcuts. I think you can imagine.
557  QList<QAction *> conflictingActions;
558 
559  //find conflicting shortcuts with existing actions
560  for (QAction *qaction : qAsConst(allActions)) {
561  if (shortcutsConflictWith(qaction->shortcuts(), keySequence)) {
562  // A conflict with a KAction. If that action is configurable
563  // ask the user what to do. If not reject this keySequence.
564  if (checkActionCollections.first()->isShortcutsConfigurable(qaction)) {
565  conflictingActions.append(qaction);
566  } else {
567  wontStealShortcut(qaction, keySequence);
568  return true;
569  }
570  }
571  }
572 
573  if (conflictingActions.isEmpty()) {
574  // No conflicting shortcuts found.
575  return false;
576  }
577 
578  if (stealShortcuts(conflictingActions, keySequence)) {
579  stealActions = conflictingActions;
580  // Announce that the user
581  // agreed
582  for (QAction *stealAction : qAsConst(stealActions)) {
583  emit q->stealShortcut(
584  keySequence,
585  stealAction);
586  }
587  return false;
588  } else {
589  return true;
590  }
591 }
592 
593 bool KKeySequenceWidgetPrivate::conflictWithStandardShortcuts(const QKeySequence &keySequence)
594 {
595  if (!(checkAgainstShortcutTypes & KKeySequenceWidget::StandardShortcuts)) {
596  return false;
597  }
598 
600  if (ssc != KStandardShortcut::AccelNone && !stealStandardShortcut(ssc, keySequence)) {
601  return true;
602  }
603  return false;
604 }
605 
606 bool KKeySequenceWidgetPrivate::stealStandardShortcut(KStandardShortcut::StandardShortcut std, const QKeySequence &seq)
607 {
608  QString title = i18nc("@title:window", "Conflict with Standard Application Shortcut");
609  QString message = i18n("The '%1' key combination is also used for the standard action "
610  "\"%2\" that some applications use.\n"
611  "Do you really want to use it as a global shortcut as well?",
613 
614  if (KMessageBox::warningContinueCancel(q, message, title, KGuiItem(i18nc("@action:button", "Reassign"))) != KMessageBox::Continue) {
615  return false;
616  }
617  return true;
618 }
619 
620 void KKeySequenceWidgetPrivate::updateShortcutDisplay()
621 {
622  //empty string if no non-modifier was pressed
623  QString s = keySequence.toString(QKeySequence::NativeText);
624  s.replace(QLatin1Char('&'), QLatin1String("&&"));
625 
626  if (isRecording) {
627  if (modifierKeys) {
628  if (!s.isEmpty()) {
629  s.append(QLatin1Char(','));
630  }
631  if (modifierKeys & Qt::MetaModifier) {
632  s += QKeySequence(Qt::MetaModifier).toString(QKeySequence::NativeText);
633  }
634 #if defined(Q_OS_MAC)
635  if (modifierKeys & Qt::AltModifier) {
636  s += QKeySequence(Qt::AltModifier).toString(QKeySequence::NativeText);
637  }
638  if (modifierKeys & Qt::ControlModifier) {
639  s += QKeySequence(Qt::ControlModifier).toString(QKeySequence::NativeText);
640  }
641 #else
642  if (modifierKeys & Qt::ControlModifier) {
643  s += QKeySequence(Qt::ControlModifier).toString(QKeySequence::NativeText);
644  }
645  if (modifierKeys & Qt::AltModifier) {
646  s += QKeySequence(Qt::AltModifier).toString(QKeySequence::NativeText);
647  }
648 #endif
649  if (modifierKeys & Qt::ShiftModifier) {
650  s += QKeySequence(Qt::ShiftModifier).toString(QKeySequence::NativeText);
651  }
652  if (modifierKeys & Qt::KeypadModifier) {
653  s += QKeySequence(Qt::KeypadModifier).toString(QKeySequence::NativeText);
654  }
655 
656  } else if (nKey == 0) {
657  s = i18nc("What the user inputs now will be taken as the new shortcut", "Input");
658  }
659  //make it clear that input is still going on
660  s.append(QLatin1String(" ..."));
661  }
662 
663  if (s.isEmpty()) {
664  s = i18nc("No shortcut defined", "None");
665  }
666 
667  s = QLatin1Char(' ') + s + QLatin1Char(' ');
668  keyButton->setText(s);
669 }
670 
671 KKeySequenceButton::~KKeySequenceButton()
672 {
673 }
674 
675 //prevent Qt from special casing Tab and Backtab
676 bool KKeySequenceButton::event(QEvent *e)
677 {
678  if (d->isRecording && e->type() == QEvent::KeyPress) {
679  keyPressEvent(static_cast<QKeyEvent *>(e));
680  return true;
681  }
682 
683  // The shortcut 'alt+c' ( or any other dialog local action shortcut )
684  // ended the recording and triggered the action associated with the
685  // action. In case of 'alt+c' ending the dialog. It seems that those
686  // ShortcutOverride events get sent even if grabKeyboard() is active.
687  if (d->isRecording && e->type() == QEvent::ShortcutOverride) {
688  e->accept();
689  return true;
690  }
691 
692  if (d->isRecording && e->type() == QEvent::ContextMenu) {
693  // is caused by Qt::Key_Menu
694  e->accept();
695  return true;
696  }
697 
698  return QPushButton::event(e);
699 }
700 
701 void KKeySequenceButton::keyPressEvent(QKeyEvent *e)
702 {
703  int keyQt = e->key();
704  if (keyQt == -1) {
705  // Qt sometimes returns garbage keycodes, I observed -1, if it doesn't know a key.
706  // We cannot do anything useful with those (several keys have -1, indistinguishable)
707  // and QKeySequence.toString() will also yield a garbage string.
708  KMessageBox::sorry(this,
709  i18n("The key you just pressed is not supported by Qt."),
710  i18nc("@title:window", "Unsupported Key"));
711  d->cancelRecording();
712  return;
713  }
714 
716 
717 
718  // Qt doesn't properly recognize Super_L/Super_R as MetaModifier
719  if (e->key() == Qt::Key_Super_L || e->key() == Qt::Key_Super_R) {
720  newModifiers |= Qt::MetaModifier;
721  }
722 
723  //don't have the return or space key appear as first key of the sequence when they
724  //were pressed to start editing - catch and them and imitate their effect
725  if (!d->isRecording && ((keyQt == Qt::Key_Return || keyQt == Qt::Key_Space))) {
726  d->startRecording();
727  d->modifierKeys = newModifiers;
728  d->updateShortcutDisplay();
729  return;
730  }
731 
732  // We get events even if recording isn't active.
733  if (!d->isRecording) {
735  return;
736  }
737 
738  e->accept();
739  d->modifierKeys = newModifiers;
740 
741  switch (keyQt) {
742  case Qt::Key_AltGr: //or else we get unicode salad
743  return;
744  case Qt::Key_Shift:
745  case Qt::Key_Control:
746  case Qt::Key_Alt:
747  case Qt::Key_Meta:
748  case Qt::Key_Super_L:
749  case Qt::Key_Super_R:
750  d->controlModifierlessTimout();
751  d->updateShortcutDisplay();
752  break;
753  default:
754 
755  if (d->nKey == 0 && !(d->modifierKeys & ~Qt::SHIFT)) {
756  // It's the first key and no modifier pressed. Check if this is
757  // allowed
758  if (!(KKeySequenceWidgetPrivate::isOkWhenModifierless(keyQt)
759  || d->allowModifierless)) {
760  // No it's not
761  return;
762  }
763  }
764 
765  // We now have a valid key press.
766  if (keyQt) {
767  if ((keyQt == Qt::Key_Backtab) && (d->modifierKeys & Qt::SHIFT)) {
768  keyQt = Qt::Key_Tab | d->modifierKeys;
769  } else if (KKeyServer::isShiftAsModifierAllowed(keyQt)) {
770  keyQt |= d->modifierKeys;
771  } else {
772  keyQt |= (d->modifierKeys & ~Qt::SHIFT);
773  }
774 
775  if (d->nKey == 0) {
776  d->keySequence = QKeySequence(keyQt);
777  } else {
778  d->keySequence =
779  KKeySequenceWidgetPrivate::appendToSequence(d->keySequence, keyQt);
780  }
781 
782  d->nKey++;
783  if ((!d->multiKeyShortcutsAllowed) || (d->nKey >= 4)) {
784  d->doneRecording();
785  return;
786  }
787  d->controlModifierlessTimout();
788  d->updateShortcutDisplay();
789  }
790  }
791 }
792 
793 void KKeySequenceButton::keyReleaseEvent(QKeyEvent *e)
794 {
795  if (e->key() == -1) {
796  // ignore garbage, see keyPressEvent()
797  return;
798  }
799 
800  if (!d->isRecording) {
802  return;
803  }
804 
805  e->accept();
806 
808 
809  // Qt doesn't properly recognize Super_L/Super_R as MetaModifier
810  if (e->key() == Qt::Key_Super_L || e->key() == Qt::Key_Super_R) {
811  newModifiers &= ~Qt::MetaModifier;
812  }
813 
814  //if a modifier that belongs to the shortcut was released...
815  if ((newModifiers & d->modifierKeys) < d->modifierKeys) {
816  d->modifierKeys = newModifiers;
817  d->controlModifierlessTimout();
818  d->updateShortcutDisplay();
819  }
820 }
821 
822 //static
823 QKeySequence KKeySequenceWidgetPrivate::appendToSequence(const QKeySequence &seq, int keyQt)
824 {
825  switch (seq.count()) {
826  case 0:
827  return QKeySequence(keyQt);
828  case 1:
829  return QKeySequence(seq[0], keyQt);
830  case 2:
831  return QKeySequence(seq[0], seq[1], keyQt);
832  case 3:
833  return QKeySequence(seq[0], seq[1], seq[2], keyQt);
834  default:
835  return seq;
836  }
837 }
838 
839 //static
840 bool KKeySequenceWidgetPrivate::isOkWhenModifierless(int keyQt)
841 {
842  //this whole function is a hack, but especially the first line of code
843  if (QKeySequence(keyQt).toString().length() == 1) {
844  return false;
845  }
846 
847  switch (keyQt) {
848  case Qt::Key_Return:
849  case Qt::Key_Space:
850  case Qt::Key_Tab:
851  case Qt::Key_Backtab: //does this ever happen?
852  case Qt::Key_Backspace:
853  case Qt::Key_Delete:
854  return false;
855  default:
856  return true;
857  }
858 }
859 
860 #include "moc_kkeysequencewidget.cpp"
QLayout * layout() const const
KCOREADDONS_EXPORT void message(KMessage::MessageType messageType, const QString &text, const QString &caption=QString())
A widget to input a QKeySequence.
A container for a set of QAction objects.
MetaModifier
void setComponentName(const QString &componentName)
If the component using this widget supports shortcuts contexts, it has to set its component name so w...
Qt::KeyboardModifiers modifiers() const const
QString & append(QChar ch)
StrongFocus
QEvent::Type type() const const
void setContentsMargins(int left, int top, int right, int bottom)
QHash::iterator insert(const Key &key, const T &value)
Check against global shortcuts.
int count() const const
void setModifierlessAllowed(bool allow)
This only applies to user input, not to setKeySequence().
static QString removeAcceleratorMarker(const QString &label)
int size() const const
bool isShiftAsModifierAllowed(int keyQt)
StandardShortcut find(const QKeySequence &keySeq)
void setMultiKeyShortcutsAllowed(bool)
Allow multikey shortcuts?
QSet::iterator insert(const T &value)
KKeySequenceWidget(QWidget *parent=nullptr)
Constructor.
int size() const const
Check with local shortcuts.
void append(const T &value)
QKeySequence::SequenceMatch matches(const QKeySequence &seq) const const
ShortcutTypes checkForConflictsAgainst() const
The shortcut types we check for conflicts.
ButtonCode warningContinueCancel(QWidget *parent, const QString &text, const QString &caption=QString(), const KGuiItem &buttonContinue=KStandardGuiItem::cont(), const KGuiItem &buttonCancel=KStandardGuiItem::cancel(), const QString &dontAskAgainName=QString(), Options options=Notify)
void setKeySequence(const QKeySequence &seq, Validation val=NoValidate)
Set the key sequence.
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString label(StandardShortcut id)
QWidget * keyboardGrabber()
bool isEmpty() const const
void setFocusProxy(QWidget *w)
bool isEmpty() const const
void clicked(bool checked)
void clearKeySequence()
Clear the key sequence.
virtual ~KKeySequenceWidget()
Destructs the widget.
void captureKeySequence()
Capture a shortcut from the keyboard.
Validation
An enum about validation when setting a key sequence.
void addWidget(QWidget *w)
void applyStealShortcut()
Actually remove the shortcut that the user wanted to steal, from the action that was using it...
QList::iterator end()
int key() const const
void accept()
virtual void keyReleaseEvent(QKeyEvent *e) override
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
Check against standard shortcuts.
bool isEmpty() const const
QString i18ncp(const char *context, const char *singular, const char *plural, const TYPE &arg...)
QCA_EXPORT void init()
void setCheckActionCollections(const QList< KActionCollection * > &actionCollections)
Set a list of action collections to check against for conflictuous shortcut.
void setCheckActionList(const QList< QAction * > &checkList)
QString i18n(const char *text, const TYPE &arg...)
static QList< KGlobalShortcutInfo > getGlobalShortcutsByKey(const QKeySequence &seq)
QString & replace(int position, int n, QChar after)
const QList< QKeySequence > & end()
QString toString(QKeySequence::SequenceFormat format) const const
bool isEmpty() const const
QAction * action(MenuId id) const
Returns the QAction * associated with the given parameter Will return a nullptr if menu() has not bee...
Definition: khelpmenu.cpp:213
QKeySequence keySequence() const
Return the currently selected key sequence.
virtual void keyPressEvent(QKeyEvent *event)
virtual bool event(QEvent *e) override
void setClearButtonShown(bool show)
Set whether a small button to set an empty key sequence should be displayed next to the main input wi...
void keySequence(QWindow *window, const QKeySequence &keySequence)
QIcon fromTheme(const QString &name)
void setCheckForConflictsAgainst(ShortcutTypes types)
Configure if the widget should check for conflicts with existing shortcuts.
void show()
bool isKeySequenceAvailable(const QKeySequence &seq) const
Checks whether the key sequence seq is available to grab.
virtual void keyPressEvent(QKeyEvent *e) override
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
Validate key sequence.
static void stealShortcutSystemwide(const QKeySequence &seq)
static bool isGlobalShortcutAvailable(const QKeySequence &seq, const QString &component=QString())
Key_Super_L
void sorry(QWidget *parent, const QString &text, const QString &caption=QString(), Options options=Notify)
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Wed Aug 12 2020 22:50:46 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.