Krita Source Code Documentation
Loading...
Searching...
No Matches
kkeysequencewidget.cpp
Go to the documentation of this file.
1/* This file is part of the KDE libraries
2 SPDX-FileCopyrightText: 1998 Mark Donohoe <donohoe@kde.org>
3 SPDX-FileCopyrightText: 2001 Ellis Whitehead <ellis@kde.org>
4 SPDX-FileCopyrightText: 2007 Andreas Hartmetz <ahartmetz@gmail.com>
5
6 SPDX-License-Identifier: LGPL-2.0-or-later
7*/
8
11#include "config-xmlgui.h"
12#include <QAction>
13#include <QKeyEvent>
14#include <QTimer>
15#include <QHBoxLayout>
16#include <QToolButton>
17#include <QApplication>
18#include <QDebug>
19
20#include <klocalizedstring.h>
21#include <kmessagebox.h>
22#include "kactioncollection.h"
23
24#include <kis_icon_utils.h>
25
26#include <QtGui/private/qkeymapper_p.h>
27
28
29uint qHash(const QKeySequence &seq)
30{
31 return qHash(seq.toString());
32}
33
35{
36public:
38
39 void init();
40
41 static QKeySequence appendToSequence(const QKeySequence &seq, int keyQt);
42 static bool isOkWhenModifierless(int keyQt);
43
45 void startRecording();
46
53 bool conflictWithStandardShortcuts(const QKeySequence &seq);
54
59 bool conflictWithLocalShortcuts(const QKeySequence &seq);
60
64 bool conflictWithGlobalShortcuts(const QKeySequence &seq);
65
69 bool stealStandardShortcut(KStandardShortcut::StandardShortcut std, const QKeySequence &seq);
70
75
80
85
87 {
88 if (nKey != 0 && !modifierKeys) {
89 // No modifier key pressed currently. Start the timeout
90 modifierlessTimeout.start(600);
91 } else {
92 // A modifier is pressed. Stop the timeout
94 }
95
96 }
97
103
104//private slot
105 void doneRecording(bool validate = true);
106
107//members
109 QHBoxLayout *layout;
111 QToolButton *clearButton;
112
113 QKeySequence keySequence;
114 QKeySequence oldKeySequence;
122
124 KisKKeySequenceWidget::ShortcutTypes checkAgainstShortcutTypes;
125
130
135
140
141 bool stealShortcuts(const QList<QAction *> &actions, const QKeySequence &seq);
142 void wontStealShortcut(QAction *item, const QKeySequence &seq);
143
144};
145
147 : q(q)
148 , layout(0)
149 , keyButton(0)
150 , clearButton(0)
151 , allowModifierless(false)
152 , nKey(0)
153 , modifierKeys(0)
154 , isRecording(false)
155 , multiKeyShortcutsAllowed(true)
156 , componentName()
157 , checkAgainstShortcutTypes(KisKKeySequenceWidget::LocalShortcuts | KisKKeySequenceWidget::GlobalShortcuts)
158 , stealActions()
159{}
160
162 const QList<QAction *> &actions,
163 const QKeySequence &seq)
164{
165
166 const int listSize = actions.size();
167
168 QString title = i18ncp("%1 is the number of conflicts", "Shortcut Conflict", "Shortcut Conflicts", listSize);
169
170 QString conflictingShortcuts;
171 Q_FOREACH (const QAction *action, actions) {
172 conflictingShortcuts += i18n("Shortcut '%1' for action '%2'\n",
173 action->shortcut().toString(QKeySequence::NativeText),
174 KLocalizedString::removeAcceleratorMarker(action->text()));
175 }
176 QString message = i18ncp("%1 is the number of ambiguous shortcut clashes (hidden)",
177 "The \"%2\" shortcut is ambiguous with the following shortcut.\n"
178 "Do you want to assign an empty shortcut to this action?\n"
179 "%3",
180 "The \"%2\" shortcut is ambiguous with the following shortcuts.\n"
181 "Do you want to assign an empty shortcut to these actions?\n"
182 "%3",
183 listSize,
184 seq.toString(QKeySequence::NativeText),
185 conflictingShortcuts);
186
187 if (KMessageBox::warningContinueCancel(q, message, title, KGuiItem(i18n("Reassign"))) != KMessageBox::Continue) {
188 return false;
189 }
190
191 return true;
192}
193
194void KisKKeySequenceWidgetPrivate::wontStealShortcut(QAction *item, const QKeySequence &seq)
195{
196 QString title(i18n("Shortcut conflict"));
197 QString msg(i18n("<qt>The '%1' key combination is already used by the <b>%2</b> action.<br>"
198 "Please select a different one.</qt>", seq.toString(QKeySequence::NativeText),
199 KLocalizedString::removeAcceleratorMarker(item->text())));
200 KMessageBox::error(q, msg, title);
201}
202
204 : QWidget(parent),
206{
207 d->init();
208 setFocusProxy(d->keyButton);
209 connect(d->keyButton, SIGNAL(clicked()), this, SLOT(captureKeySequence()));
210 connect(d->clearButton, SIGNAL(clicked()), this, SLOT(clearKeySequence()));
211 connect(&d->modifierlessTimeout, SIGNAL(timeout()), this, SLOT(doneRecording()));
212 //TODO: how to adopt style changes at runtime?
213 /*QFont modFont = d->clearButton->font();
214 modFont.setStyleHint(QFont::TypeWriter);
215 d->clearButton->setFont(modFont);*/
217}
218
220{
221 layout = new QHBoxLayout(q);
222 layout->setContentsMargins(0, 0, 0, 0);
223
224 keyButton = new KKeySequenceButton(this, q);
225 keyButton->setFocusPolicy(Qt::StrongFocus);
226 keyButton->setIcon(KisIconUtils::loadIcon(QStringLiteral("configure")));
227 keyButton->setToolTip(i18n("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."));
228 layout->addWidget(keyButton);
229
230 clearButton = new QToolButton(q);
231 layout->addWidget(clearButton);
232
233 if (qApp->isLeftToRight()) {
234 clearButton->setIcon(KisIconUtils::loadIcon(QStringLiteral("edit-clear-locationbar-rtl")));
235 } else {
236 clearButton->setIcon(KisIconUtils::loadIcon(QStringLiteral("edit-clear-locationbar-ltr")));
237 }
238}
239
244
245KisKKeySequenceWidget::ShortcutTypes KisKKeySequenceWidget::checkForConflictsAgainst() const
246{
248}
249
250void KisKKeySequenceWidget::setComponentName(const QString &componentName)
251{
252 d->componentName = componentName;
253}
254
259
264
269
274
275bool KisKKeySequenceWidget::isKeySequenceAvailable(const QKeySequence &keySequence) const
276{
277 if (keySequence.isEmpty()) {
278 // qDebug() << "Key sequence" << keySequence.toString() << "is empty and available.";
279 return true;
280 }
281
282 bool hasConflict = (d->conflictWithLocalShortcuts(keySequence)
285
286 if (hasConflict) {
287 /* qInfo() << "Key sequence" << keySequence.toString() << "has an unresolvable conflict." <<
288 QString("Local conflict: %1. Windows conflict: %2. Standard Shortcut conflict: %3") \
289 .arg(d->conflictWithLocalShortcuts(keySequence)) \
290 .arg(d->conflictWithGlobalShortcuts(keySequence)) \
291 .arg(d->conflictWithStandardShortcuts(keySequence)); */
292 }
293 return !(hasConflict);
294
295}
296
301
303{
304 d->clearButton->setVisible(show);
305}
306
308{
309 d->checkActionCollections = actionCollections;
310}
311
312//slot
317
319{
320 return d->keySequence;
321}
322
323//slot
324void KisKKeySequenceWidget::setKeySequence(const QKeySequence &seq, Validation validate)
325{
326 // oldKeySequence holds the key sequence before recording started, if setKeySequence()
327 // is called while not recording then set oldKeySequence to the existing sequence so
328 // that the keySequenceChanged() signal is emitted if the new and previous key
329 // sequences are different
330 if (!d->isRecording) {
332 }
333
334 d->keySequence = seq;
335 d->doneRecording(validate == Validate);
336}
337
338//slot
340{
341 setKeySequence(QKeySequence());
342}
343
344//slot
346{
347 QSet<KisKActionCollection *> changedCollections;
348
349 Q_FOREACH (QAction *stealAction, d->stealActions) {
350
351 // Stealing a shortcut means setting it to an empty one.
352 stealAction->setShortcuts(QList<QKeySequence>());
353
354 // The following code will find the action we are about to
355 // steal from and save its action collection.
356 KisKActionCollection *parentCollection = 0;
357 foreach (KisKActionCollection *collection, d->checkActionCollections) {
358 if (collection->actions().contains(stealAction)) {
359 parentCollection = collection;
360 break;
361 }
362 }
363
364 // Remember the changed collection
365 if (parentCollection) {
366 changedCollections.insert(parentCollection);
367 }
368 }
369
370 Q_FOREACH (KisKActionCollection *col, changedCollections) {
371 col->writeSettings();
372 }
373
374 d->stealActions.clear();
375}
376
378{
379 nKey = 0;
380 modifierKeys = 0;
382 keySequence = QKeySequence();
383 isRecording = true;
384 keyButton->grabKeyboard();
385
386 if (!QWidget::keyboardGrabber()) {
387 qWarning() << "Failed to grab the keyboard! Most likely qt's nograb option is active";
388 }
389
390 keyButton->setDown(true);
392}
393
395{
396 modifierlessTimeout.stop();
397 isRecording = false;
398 keyButton->releaseKeyboard();
399 keyButton->setDown(false);
400 stealActions.clear();
401
403 // The sequence hasn't changed
405 return;
406 }
407
408 if (validate && !q->isKeySequenceAvailable(keySequence)) {
409 // The sequence had conflicts and the user said no to stealing it
411 } else {
413 }
414
416}
417
419{
420 // This could hold some OS-specific stuff, or it could be linked back with
421 // the KDE global shortcut code at some point in the future.
422
423#ifdef Q_OS_WIN
424#else
425#endif
426 Q_UNUSED(keySequence);
427
428 return false;
429}
430
431bool shortcutsConflictWith(const QList<QKeySequence> &shortcuts, const QKeySequence &needle)
432{
433 if (needle.isEmpty() || needle.toString(QKeySequence::NativeText).isEmpty()) {
434 return false;
435 }
436
437 foreach (const QKeySequence &sequence, shortcuts) {
438 if (sequence.isEmpty()) {
439 continue;
440 }
441
442 if (sequence.matches(needle) != QKeySequence::NoMatch
443 || needle.matches(sequence) != QKeySequence::NoMatch) {
444 return true;
445 }
446 }
447
448 return false;
449}
450
452{
454 return false;
455 }
456
457 // We have actions both in the deprecated checkList and the
458 // checkActionCollections list. Add all the actions to a single list to
459 // be able to process them in a single loop below.
460 // Note that this can't be done in setCheckActionCollections(), because we
461 // keep pointers to the action collections, and between the call to
462 // setCheckActionCollections() and this function some actions might already be
463 // removed from the collection again.
464 QList<QAction *> allActions;
465 allActions += checkList;
466 foreach (KisKActionCollection *collection, checkActionCollections) {
467 allActions += collection->actions();
468 }
469
470 // Because of multikey shortcuts we can have clashes with many shortcuts.
471 //
472 // Example 1:
473 //
474 // Application currently uses 'CTRL-X,a', 'CTRL-X,f' and 'CTRL-X,CTRL-F'
475 // and the user wants to use 'CTRL-X'. 'CTRL-X' will only trigger as
476 // 'activatedAmbiguously()' for obvious reasons.
477 //
478 // Example 2:
479 //
480 // Application currently uses 'CTRL-X'. User wants to use 'CTRL-X,CTRL-F'.
481 // This will shadow 'CTRL-X' for the same reason as above.
482 //
483 // Example 3:
484 //
485 // Some weird combination of Example 1 and 2 with three shortcuts using
486 // 1/2/3 key shortcuts. I think you can imagine.
487 QList<QAction *> conflictingActions;
488
489 //find conflicting shortcuts with existing actions
490 foreach (QAction *qaction, allActions) {
491 if (shortcutsConflictWith(qaction->shortcuts(), keySequence)) {
492 // A conflict with a KAction. If that action is configurable
493 // ask the user what to do. If not reject this keySequence.
494 if (checkActionCollections.first()->isShortcutsConfigurable(qaction)) {
495 conflictingActions.append(qaction);
496 } else {
498 return true;
499 }
500 }
501 }
502
503 if (conflictingActions.isEmpty()) {
504 // No conflicting shortcuts found.
505 return false;
506 }
507
508 if (stealShortcuts(conflictingActions, keySequence)) {
509 stealActions = conflictingActions;
510
511 // Announce that the user agreed to override the other shortcut
512 Q_FOREACH (QAction *stealAction, stealActions) {
513 Q_EMIT q->stealShortcut(
515 stealAction);
516 }
517 return false;
518 } else {
519 return true;
520 }
521}
522
524{
526 return false;
527 }
528 KStandardShortcut::StandardShortcut ssc = KStandardShortcut::find(keySequence);
529 if (ssc != KStandardShortcut::AccelNone && !stealStandardShortcut(ssc, keySequence)) {
530 return true;
531 }
532 return false;
533}
534
535bool KisKKeySequenceWidgetPrivate::stealStandardShortcut(KStandardShortcut::StandardShortcut std, const QKeySequence &seq)
536{
537 QString title = i18n("Conflict with Standard Application Shortcut");
538 QString message = i18n("The '%1' key combination is also used for the standard action "
539 "\"%2\" that some applications use.\n"
540 "Do you really want to use it as a global shortcut as well?",
541 seq.toString(QKeySequence::NativeText), KStandardShortcut::label(std));
542
543 if (KMessageBox::warningContinueCancel(q, message, title, KGuiItem(i18n("Reassign"))) != KMessageBox::Continue) {
544 return false;
545 }
546 return true;
547}
548
550{
551 //empty string if no non-modifier was pressed
552 QString s = keySequence.toString(QKeySequence::NativeText);
553 s.replace(QLatin1Char('&'), QStringLiteral("&&"));
554
555 if (isRecording) {
556 if (modifierKeys) {
557 if (!s.isEmpty()) {
558 s.append(QLatin1Char(','));
559 }
560 if (modifierKeys & Qt::MetaModifier) {
561 s += QKeySequence(Qt::MetaModifier).toString(QKeySequence::NativeText);
562 }
563#if defined(Q_OS_MAC)
564 if (modifierKeys & Qt::AltModifier) {
565 s += QKeySequence(Qt::AltModifier).toString(QKeySequence::NativeText);
566 }
567 if (modifierKeys & Qt::ControlModifier) {
568 s += QKeySequence(Qt::ControlModifier).toString(QKeySequence::NativeText);
569 }
570#else
571 if (modifierKeys & Qt::ControlModifier) {
572 s += QKeySequence(Qt::ControlModifier).toString(QKeySequence::NativeText);
573 }
574 if (modifierKeys & Qt::AltModifier) {
575 s += QKeySequence(Qt::AltModifier).toString(QKeySequence::NativeText);
576 }
577#endif
578 if (modifierKeys & Qt::ShiftModifier) {
579 s += QKeySequence(Qt::ShiftModifier).toString(QKeySequence::NativeText);
580 }
581 if (modifierKeys & Qt::KeypadModifier) {
582 s += QKeySequence(Qt::KeypadModifier).toString(QKeySequence::NativeText);
583 }
584
585 } else if (nKey == 0) {
586 s = i18nc("What the user inputs now will be taken as the new shortcut", "Input");
587 }
588 //make it clear that input is still going on
589 s.append(QStringLiteral(" ..."));
590 }
591
592 if (s.isEmpty()) {
593 s = i18nc("No shortcut defined", "None");
594 }
595
596 s.prepend(QLatin1Char(' '));
597 s.append(QLatin1Char(' '));
598 keyButton->setText(s);
599
600}
601
605
606//prevent Qt from special casing Tab and Backtab
608{
609 if (d->isRecording && e->type() == QEvent::KeyPress) {
610 keyPressEvent(static_cast<QKeyEvent *>(e));
611 return true;
612 }
613
614 // The shortcut 'alt+c' ( or any other dialog local action shortcut )
615 // ended the recording and triggered the action associated with the
616 // action. In case of 'alt+c' ending the dialog. It seems that those
617 // ShortcutOverride events get sent even if grabKeyboard() is active.
618 if (d->isRecording && e->type() == QEvent::ShortcutOverride) {
619 e->accept();
620 return true;
621 }
622
623 if (d->isRecording && e->type() == QEvent::ContextMenu) {
624 // is caused by Qt::Key_Menu
625 e->accept();
626 return true;
627 }
628
629 return QPushButton::event(e);
630}
631
632namespace {
633
634// Copied here from KKeyServer
635static bool isShiftAsModifierAllowed(int keyQt)
636{
637 // remove any modifiers
638 keyQt &= ~Qt::KeyboardModifierMask;
639
640 // Shift only works as a modifier with certain keys. It's not possible
641 // to enter the SHIFT+5 key sequence for me because this is handled as
642 // '%' by qt on my keyboard.
643 // The working keys are all hardcoded here :-(
644 if (keyQt >= Qt::Key_F1 && keyQt <= Qt::Key_F35) {
645 return true;
646 }
647
648 if (QChar::isLetter(keyQt)) {
649 return true;
650 }
651
652 switch (keyQt) {
653 case Qt::Key_Return:
654 case Qt::Key_Space:
655 case Qt::Key_Backspace:
656 case Qt::Key_Tab:
657 case Qt::Key_Backtab:
658 case Qt::Key_Escape:
659 case Qt::Key_Print:
660 case Qt::Key_ScrollLock:
661 case Qt::Key_Pause:
662 case Qt::Key_PageUp:
663 case Qt::Key_PageDown:
664 case Qt::Key_Insert:
665 case Qt::Key_Delete:
666 case Qt::Key_Home:
667 case Qt::Key_End:
668 case Qt::Key_Up:
669 case Qt::Key_Down:
670 case Qt::Key_Left:
671 case Qt::Key_Right:
672 case Qt::Key_Enter:
673 case Qt::Key_SysReq:
674 case Qt::Key_CapsLock:
675 case Qt::Key_NumLock:
676 case Qt::Key_Help:
677 case Qt::Key_Back:
678 case Qt::Key_Forward:
679 case Qt::Key_Stop:
680 case Qt::Key_Refresh:
681 case Qt::Key_Favorites:
682 case Qt::Key_LaunchMedia:
683 case Qt::Key_OpenUrl:
684 case Qt::Key_HomePage:
685 case Qt::Key_Search:
686 case Qt::Key_VolumeDown:
687 case Qt::Key_VolumeMute:
688 case Qt::Key_VolumeUp:
689 case Qt::Key_BassBoost:
690 case Qt::Key_BassUp:
691 case Qt::Key_BassDown:
692 case Qt::Key_TrebleUp:
693 case Qt::Key_TrebleDown:
694 case Qt::Key_MediaPlay:
695 case Qt::Key_MediaStop:
696 case Qt::Key_MediaPrevious:
697 case Qt::Key_MediaNext:
698 case Qt::Key_MediaRecord:
699 case Qt::Key_MediaPause:
700 case Qt::Key_MediaTogglePlayPause:
701 case Qt::Key_LaunchMail:
702 case Qt::Key_Calculator:
703 case Qt::Key_Memo:
704 case Qt::Key_ToDoList:
705 case Qt::Key_Calendar:
706 case Qt::Key_PowerDown:
707 case Qt::Key_ContrastAdjust:
708 case Qt::Key_Standby:
709 case Qt::Key_MonBrightnessUp:
710 case Qt::Key_MonBrightnessDown:
711 case Qt::Key_KeyboardLightOnOff:
712 case Qt::Key_KeyboardBrightnessUp:
713 case Qt::Key_KeyboardBrightnessDown:
714 case Qt::Key_PowerOff:
715 case Qt::Key_WakeUp:
716 case Qt::Key_Eject:
717 case Qt::Key_ScreenSaver:
718 case Qt::Key_WWW:
719 case Qt::Key_Sleep:
720 case Qt::Key_LightBulb:
721 case Qt::Key_Shop:
722 case Qt::Key_History:
723 case Qt::Key_AddFavorite:
724 case Qt::Key_HotLinks:
725 case Qt::Key_BrightnessAdjust:
726 case Qt::Key_Finance:
727 case Qt::Key_Community:
728 case Qt::Key_AudioRewind:
729 case Qt::Key_BackForward:
730 case Qt::Key_ApplicationLeft:
731 case Qt::Key_ApplicationRight:
732 case Qt::Key_Book:
733 case Qt::Key_CD:
734 case Qt::Key_Clear:
735 case Qt::Key_ClearGrab:
736 case Qt::Key_Close:
737 case Qt::Key_Copy:
738 case Qt::Key_Cut:
739 case Qt::Key_Display:
740 case Qt::Key_DOS:
741 case Qt::Key_Documents:
742 case Qt::Key_Excel:
743 case Qt::Key_Explorer:
744 case Qt::Key_Game:
745 case Qt::Key_Go:
746 case Qt::Key_iTouch:
747 case Qt::Key_LogOff:
748 case Qt::Key_Market:
749 case Qt::Key_Meeting:
750 case Qt::Key_MenuKB:
751 case Qt::Key_MenuPB:
752 case Qt::Key_MySites:
753 case Qt::Key_News:
754 case Qt::Key_OfficeHome:
755 case Qt::Key_Option:
756 case Qt::Key_Paste:
757 case Qt::Key_Phone:
758 case Qt::Key_Reply:
759 case Qt::Key_Reload:
760 case Qt::Key_RotateWindows:
761 case Qt::Key_RotationPB:
762 case Qt::Key_RotationKB:
763 case Qt::Key_Save:
764 case Qt::Key_Send:
765 case Qt::Key_Spell:
766 case Qt::Key_SplitScreen:
767 case Qt::Key_Support:
768 case Qt::Key_TaskPane:
769 case Qt::Key_Terminal:
770 case Qt::Key_Tools:
771 case Qt::Key_Travel:
772 case Qt::Key_Video:
773 case Qt::Key_Word:
774 case Qt::Key_Xfer:
775 case Qt::Key_ZoomIn:
776 case Qt::Key_ZoomOut:
777 case Qt::Key_Away:
778 case Qt::Key_Messenger:
779 case Qt::Key_WebCam:
780 case Qt::Key_MailForward:
781 case Qt::Key_Pictures:
782 case Qt::Key_Music:
783 case Qt::Key_Battery:
784 case Qt::Key_Bluetooth:
785 case Qt::Key_WLAN:
786 case Qt::Key_UWB:
787 case Qt::Key_AudioForward:
788 case Qt::Key_AudioRepeat:
789 case Qt::Key_AudioRandomPlay:
790 case Qt::Key_Subtitle:
791 case Qt::Key_AudioCycleTrack:
792 case Qt::Key_Time:
793 case Qt::Key_Select:
794 case Qt::Key_View:
795 case Qt::Key_TopMenu:
796 case Qt::Key_Suspend:
797 case Qt::Key_Hibernate:
798 case Qt::Key_Launch0:
799 case Qt::Key_Launch1:
800 case Qt::Key_Launch2:
801 case Qt::Key_Launch3:
802 case Qt::Key_Launch4:
803 case Qt::Key_Launch5:
804 case Qt::Key_Launch6:
805 case Qt::Key_Launch7:
806 case Qt::Key_Launch8:
807 case Qt::Key_Launch9:
808 case Qt::Key_LaunchA:
809 case Qt::Key_LaunchB:
810 case Qt::Key_LaunchC:
811 case Qt::Key_LaunchD:
812 case Qt::Key_LaunchE:
813 case Qt::Key_LaunchF:
814 case Qt::Key_Shift:
815 case Qt::Key_Control:
816 case Qt::Key_Meta:
817 case Qt::Key_Alt:
818 case Qt::Key_Super_L:
819 case Qt::Key_Super_R:
820 return true;
821
822 default:
823 return false;
824 }
825}
826
827} // namespace
828
830{
831 int keyQt = e->key();
832 if (keyQt == -1) {
833 // Qt sometimes returns garbage keycodes, I observed -1, if it doesn't know a key.
834 // We cannot do anything useful with those (several keys have -1, indistinguishable)
835 // and QKeySequence.toString() will also yield a garbage string.
836 KMessageBox::error(this,
837 i18n("The key you just pressed is not supported by Qt."),
838 i18n("Unsupported Key"));
839 return d->cancelRecording();
840 }
841
842 uint newModifiers = e->modifiers() & (Qt::SHIFT | Qt::CTRL | Qt::ALT | Qt::META);
843
844 //don't have the return or space key appear as first key of the sequence when they
845 //were pressed to start editing - catch and them and imitate their effect
846 if (!d->isRecording && ((keyQt == Qt::Key_Return || keyQt == Qt::Key_Space))) {
847 d->startRecording();
848 d->modifierKeys = newModifiers;
850 return;
851 }
852
853 // We get events even if recording isn't active.
854 if (!d->isRecording) {
855 return QPushButton::keyPressEvent(e);
856 }
857
858 e->accept();
859 d->modifierKeys = newModifiers;
860
861 switch (keyQt) {
862 case Qt::Key_AltGr: //or else we get unicode salad
863 return;
864 case Qt::Key_Shift:
865 case Qt::Key_Control:
866 case Qt::Key_Alt:
867 case Qt::Key_Meta:
868 case Qt::Key_Super_L:
869 case Qt::Key_Super_R:
872 break;
873 default:
874
875 if (d->nKey == 0 && !(d->modifierKeys & ~Qt::SHIFT)) {
876 // It's the first key and no modifier pressed. Check if this is
877 // allowed
879 || d->allowModifierless)) {
880 // No it's not
881 return;
882 }
883 }
884
885 // We now have a valid key press.
886 if (keyQt) {
899#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
900 const QList<int> vec = QKeyMapper::possibleKeys(e);
901#else
902 const QList<QKeyCombination> vec = QKeyMapper::possibleKeys(e);
903#endif
904
905 if (!vec.isEmpty() && e->modifiers() != Qt::NoModifier) {
910 keyQt = vec.first();
911 } else if ((keyQt == Qt::Key_Backtab) && (d->modifierKeys & Qt::SHIFT)) {
912 keyQt = Qt::Key_Tab | d->modifierKeys;
913 } else if (isShiftAsModifierAllowed(keyQt)) {
914 keyQt |= d->modifierKeys;
915 } else {
916 keyQt |= (d->modifierKeys & ~Qt::SHIFT);
917 }
918
919 if (d->nKey == 0) {
920 d->keySequence = QKeySequence(keyQt);
921 } else {
922 d->keySequence =
924 }
925
926 d->nKey++;
927 if ((!d->multiKeyShortcutsAllowed) || (d->nKey >= 4)) {
928 d->doneRecording();
929 return;
930 }
933 }
934 }
935}
936
938{
939 if (e->key() == -1) {
940 // ignore garbage, see keyPressEvent()
941 return;
942 }
943
944 if (!d->isRecording) {
945 return QPushButton::keyReleaseEvent(e);
946 }
947
948 e->accept();
949
950 uint newModifiers = e->modifiers() & (Qt::SHIFT | Qt::CTRL | Qt::ALT | Qt::META);
951
952 //if a modifier that belongs to the shortcut was released...
953 if ((newModifiers & d->modifierKeys) < d->modifierKeys) {
954 d->modifierKeys = newModifiers;
957 }
958}
959
960//static
961QKeySequence KisKKeySequenceWidgetPrivate::appendToSequence(const QKeySequence &seq, int keyQt)
962{
963 switch (seq.count()) {
964 case 0:
965 return QKeySequence(keyQt);
966 case 1:
967 return QKeySequence(seq[0], keyQt);
968 case 2:
969 return QKeySequence(seq[0], seq[1], keyQt);
970 case 3:
971 return QKeySequence(seq[0], seq[1], seq[2], keyQt);
972 default:
973 return seq;
974 }
975}
976
977//static
979{
980 //this whole function is a hack, but especially the first line of code
981 if (QKeySequence(keyQt).toString().length() == 1) {
982 return false;
983 }
984
985 switch (keyQt) {
986 case Qt::Key_Return:
987 case Qt::Key_Space:
988 case Qt::Key_Tab:
989 case Qt::Key_Backtab: //does this ever happen?
990 case Qt::Key_Backspace:
991 case Qt::Key_Delete:
992 return false;
993 default:
994 return true;
995 }
996}
997
998#include "moc_kkeysequencewidget.cpp"
999#include "moc_kkeysequencewidget_p.cpp"
qreal length(const QPointF &vec)
Definition Ellipse.cc:82
unsigned int uint
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
bool event(QEvent *event) override
KisKKeySequenceWidgetPrivate *const d
void keyReleaseEvent(QKeyEvent *event) override
void keyPressEvent(QKeyEvent *event) override
A container for a set of QAction objects.
void writeSettings(KConfigGroup *config=0, bool writeScheme=false, QAction *oneAction=0) const
QList< QAction * > actions() const
KisKKeySequenceWidget *const q
bool stealStandardShortcut(KStandardShortcut::StandardShortcut std, const QKeySequence &seq)
QList< KisKActionCollection * > checkActionCollections
bool stealShortcuts(const QList< QAction * > &actions, const QKeySequence &seq)
bool conflictWithStandardShortcuts(const QKeySequence &seq)
bool conflictWithGlobalShortcuts(const QKeySequence &seq)
static bool isOkWhenModifierless(int keyQt)
KisKKeySequenceWidgetPrivate(KisKKeySequenceWidget *q)
void doneRecording(bool validate=true)
static QKeySequence appendToSequence(const QKeySequence &seq, int keyQt)
bool conflictWithLocalShortcuts(const QKeySequence &seq)
void wontStealShortcut(QAction *item, const QKeySequence &seq)
KisKKeySequenceWidget::ShortcutTypes checkAgainstShortcutTypes
Check the key sequence against KStandardShortcut::find()
A widget to input a QKeySequence.
KisKKeySequenceWidget(QWidget *parent=0)
void setComponentName(const QString &componentName)
ShortcutTypes checkForConflictsAgainst
void setClearButtonShown(bool show)
void keySequenceChanged(const QKeySequence &seq)
void setModifierlessAllowed(bool allow)
void setCheckActionCollections(const QList< KisKActionCollection * > &actionCollections)
KisKKeySequenceWidgetPrivate *const d
@ GlobalShortcuts
Check against global shortcuts.
@ StandardShortcuts
Check against standard shortcuts.
@ LocalShortcuts
Check with local shortcuts.
void setKeySequence(const QKeySequence &seq, Validation val=NoValidate)
void stealShortcut(const QKeySequence &seq, QAction *action)
QKeySequence keySequence() const
@ Validate
Validate key sequence.
bool isKeySequenceAvailable(const QKeySequence &seq) const
void setCheckForConflictsAgainst(ShortcutTypes types)
bool shortcutsConflictWith(const QList< QKeySequence > &shortcuts, const QKeySequence &needle)
uint qHash(const QKeySequence &seq)
QIcon loadIcon(const QString &name)