1/*
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 SPDX-FileCopyrightText: 2020 David Redondo <kde@david-redondo.de>
6
7 SPDX-License-Identifier: LGPL-2.0-or-later
8*/
9
10#include "kkeysequencerecorder.h"
11
12#include "keyboardgrabber_p.h"
13#include "kguiaddons_debug.h"
14#include "shortcutinhibition_p.h"
15#include "waylandinhibition_p.h"
16
17#include <QGuiApplication>
18#include <QKeyEvent>
19#include <QPointer>
20#include <QTimer>
21#include <QWindow>
22
23#include <array>
24#include <chrono>
25
26/// Singleton whose only purpose is to tell us about other sequence recorders getting started
27class KKeySequenceRecorderGlobal : public QObject
28{
29 Q_OBJECT
30public:
31 static KKeySequenceRecorderGlobal *self()
32 {
33 static KKeySequenceRecorderGlobal s_self;
34 return &s_self;
35 }
36
37Q_SIGNALS:
38 void sequenceRecordingStarted();
39};
40
41class KKeySequenceRecorderPrivate : public QObject
42{
43 Q_OBJECT
44public:
45 // Copy of QKeySequencePrivate::MaxKeyCount from private header
46 enum { MaxKeyCount = 4 };
47
48 KKeySequenceRecorderPrivate(KKeySequenceRecorder *qq);
49
50 bool isKeyCombinationAccepted(QKeyCombination keyCombination) const;
51
52 void controlModifierlessTimeout();
53 bool eventFilter(QObject *watched, QEvent *event) override;
54 void handleKeyPress(QKeyEvent *event);
55 void handleKeyRelease(QKeyEvent *event);
56 void finishRecording();
57 void receivedRecording();
58
59 KKeySequenceRecorder *q;
60 QKeySequence m_currentKeySequence;
61 QKeySequence m_previousKeySequence;
62 QPointer<QWindow> m_window;
63 KKeySequenceRecorder::Patterns m_patterns;
64 bool m_isRecording;
65 bool m_multiKeyShortcutsAllowed;
66
67 Qt::KeyboardModifiers m_currentModifiers;
68 QTimer m_modifierlessTimer;
69 std::unique_ptr<ShortcutInhibition> m_inhibition;
70 // For use in modifier only shortcuts
71 Qt::KeyboardModifiers m_lastPressedModifiers;
72 bool m_isReleasingModifierOnly = false;
73 std::chrono::nanoseconds m_modifierFirstReleaseTime;
74};
75
76constexpr Qt::KeyboardModifiers modifierMask = Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier | Qt::KeypadModifier;
77
78// Copied here from KKeyServer
79static bool isShiftAsModifierAllowed(int keyQt)
80{
81 // remove any modifiers
82 keyQt &= ~Qt::KeyboardModifierMask;
83
84 // Shift only works as a modifier with certain keys. It's not possible
85 // to enter the SHIFT+5 key sequence for me because this is handled as
86 // '%' by qt on my keyboard.
87 // The working keys are all hardcoded here :-(
88 if (keyQt >= Qt::Key_F1 && keyQt <= Qt::Key_F35) {
89 return true;
90 }
91
92 if (QChar::isLetter(ucs4: keyQt)) {
93 return true;
94 }
95
96 switch (keyQt) {
97 case Qt::Key_Return:
98 case Qt::Key_Space:
99 case Qt::Key_Backspace:
100 case Qt::Key_Tab:
101 case Qt::Key_Backtab:
102 case Qt::Key_Escape:
103 case Qt::Key_Print:
104 case Qt::Key_ScrollLock:
105 case Qt::Key_Pause:
106 case Qt::Key_PageUp:
107 case Qt::Key_PageDown:
108 case Qt::Key_Insert:
109 case Qt::Key_Delete:
110 case Qt::Key_Home:
111 case Qt::Key_End:
112 case Qt::Key_Up:
113 case Qt::Key_Down:
114 case Qt::Key_Left:
115 case Qt::Key_Right:
116 case Qt::Key_Enter:
117 case Qt::Key_SysReq:
118 case Qt::Key_CapsLock:
119 case Qt::Key_NumLock:
120 case Qt::Key_Help:
121 case Qt::Key_Back:
122 case Qt::Key_Forward:
123 case Qt::Key_Stop:
124 case Qt::Key_Refresh:
125 case Qt::Key_Favorites:
126 case Qt::Key_LaunchMedia:
127 case Qt::Key_OpenUrl:
128 case Qt::Key_HomePage:
129 case Qt::Key_Search:
130 case Qt::Key_VolumeDown:
131 case Qt::Key_VolumeMute:
132 case Qt::Key_VolumeUp:
133 case Qt::Key_BassBoost:
134 case Qt::Key_BassUp:
135 case Qt::Key_BassDown:
136 case Qt::Key_TrebleUp:
137 case Qt::Key_TrebleDown:
138 case Qt::Key_MediaPlay:
139 case Qt::Key_MediaStop:
140 case Qt::Key_MediaPrevious:
141 case Qt::Key_MediaNext:
142 case Qt::Key_MediaRecord:
143 case Qt::Key_MediaPause:
144 case Qt::Key_MediaTogglePlayPause:
145 case Qt::Key_LaunchMail:
146 case Qt::Key_Calculator:
147 case Qt::Key_Memo:
148 case Qt::Key_ToDoList:
149 case Qt::Key_Calendar:
150 case Qt::Key_PowerDown:
151 case Qt::Key_ContrastAdjust:
152 case Qt::Key_Standby:
153 case Qt::Key_MonBrightnessUp:
154 case Qt::Key_MonBrightnessDown:
155 case Qt::Key_KeyboardLightOnOff:
156 case Qt::Key_KeyboardBrightnessUp:
157 case Qt::Key_KeyboardBrightnessDown:
158 case Qt::Key_PowerOff:
159 case Qt::Key_WakeUp:
160 case Qt::Key_Eject:
161 case Qt::Key_ScreenSaver:
162 case Qt::Key_WWW:
163 case Qt::Key_Sleep:
164 case Qt::Key_LightBulb:
165 case Qt::Key_Shop:
166 case Qt::Key_History:
167 case Qt::Key_AddFavorite:
168 case Qt::Key_HotLinks:
169 case Qt::Key_BrightnessAdjust:
170 case Qt::Key_Finance:
171 case Qt::Key_Community:
172 case Qt::Key_AudioRewind:
173 case Qt::Key_BackForward:
174 case Qt::Key_ApplicationLeft:
175 case Qt::Key_ApplicationRight:
176 case Qt::Key_Book:
177 case Qt::Key_CD:
178 case Qt::Key_Clear:
179 case Qt::Key_ClearGrab:
180 case Qt::Key_Close:
181 case Qt::Key_Copy:
182 case Qt::Key_Cut:
183 case Qt::Key_Display:
184 case Qt::Key_DOS:
185 case Qt::Key_Documents:
186 case Qt::Key_Excel:
187 case Qt::Key_Explorer:
188 case Qt::Key_Game:
189 case Qt::Key_Go:
190 case Qt::Key_iTouch:
191 case Qt::Key_LogOff:
192 case Qt::Key_Market:
193 case Qt::Key_Meeting:
194 case Qt::Key_MenuKB:
195 case Qt::Key_MenuPB:
196 case Qt::Key_MySites:
197 case Qt::Key_News:
198 case Qt::Key_OfficeHome:
199 case Qt::Key_Option:
200 case Qt::Key_Paste:
201 case Qt::Key_Phone:
202 case Qt::Key_Reply:
203 case Qt::Key_Reload:
204 case Qt::Key_RotateWindows:
205 case Qt::Key_RotationPB:
206 case Qt::Key_RotationKB:
207 case Qt::Key_Save:
208 case Qt::Key_Send:
209 case Qt::Key_Spell:
210 case Qt::Key_SplitScreen:
211 case Qt::Key_Support:
212 case Qt::Key_TaskPane:
213 case Qt::Key_Terminal:
214 case Qt::Key_Tools:
215 case Qt::Key_Travel:
216 case Qt::Key_Video:
217 case Qt::Key_Word:
218 case Qt::Key_Xfer:
219 case Qt::Key_ZoomIn:
220 case Qt::Key_ZoomOut:
221 case Qt::Key_Away:
222 case Qt::Key_Messenger:
223 case Qt::Key_WebCam:
224 case Qt::Key_MailForward:
225 case Qt::Key_Pictures:
226 case Qt::Key_Music:
227 case Qt::Key_Battery:
228 case Qt::Key_Bluetooth:
229 case Qt::Key_WLAN:
230 case Qt::Key_UWB:
231 case Qt::Key_AudioForward:
232 case Qt::Key_AudioRepeat:
233 case Qt::Key_AudioRandomPlay:
234 case Qt::Key_Subtitle:
235 case Qt::Key_AudioCycleTrack:
236 case Qt::Key_Time:
237 case Qt::Key_Select:
238 case Qt::Key_View:
239 case Qt::Key_TopMenu:
240 case Qt::Key_Suspend:
241 case Qt::Key_Hibernate:
242 case Qt::Key_Launch0:
243 case Qt::Key_Launch1:
244 case Qt::Key_Launch2:
245 case Qt::Key_Launch3:
246 case Qt::Key_Launch4:
247 case Qt::Key_Launch5:
248 case Qt::Key_Launch6:
249 case Qt::Key_Launch7:
250 case Qt::Key_Launch8:
251 case Qt::Key_Launch9:
252 case Qt::Key_LaunchA:
253 case Qt::Key_LaunchB:
254 case Qt::Key_LaunchC:
255 case Qt::Key_LaunchD:
256 case Qt::Key_LaunchE:
257 case Qt::Key_LaunchF:
258 case Qt::Key_Shift:
259 case Qt::Key_Control:
260 case Qt::Key_Meta:
261 case Qt::Key_Alt:
262 case Qt::Key_Super_L:
263 case Qt::Key_Super_R:
264 // Copilot key reports as Meta+Shift+TouchpadOff...
265 case Qt::Key_TouchpadOff:
266 return true;
267
268 default:
269 return false;
270 }
271}
272
273static bool isOkWhenModifierless(int key)
274{
275 // this whole function is a hack, but especially the first line of code
276 if (QKeySequence(key).toString().length() == 1) {
277 return false;
278 }
279
280 switch (key) {
281 case Qt::Key_Return:
282 case Qt::Key_Space:
283 case Qt::Key_Tab:
284 case Qt::Key_Backtab: // does this ever happen?
285 case Qt::Key_Backspace:
286 case Qt::Key_Delete:
287 return false;
288 default:
289 return true;
290 }
291}
292
293bool KKeySequenceRecorderPrivate::isKeyCombinationAccepted(QKeyCombination keyCombination) const
294{
295 const bool inputIncludesModifiers = keyCombination.keyboardModifiers();
296 const bool inputIncludesKey = keyCombination.key();
297
298 const KKeySequenceRecorder::Pattern basePatterns[] = {
299 KKeySequenceRecorder::Key,
300 KKeySequenceRecorder::Modifier,
301 KKeySequenceRecorder::ModifierAndKey,
302 };
303
304 for (const auto &pattern : basePatterns) {
305 if (!(m_patterns & pattern)) {
306 continue;
307 }
308
309 const bool patternIncludesModifiers = pattern & (KKeySequenceRecorder::Modifier | KKeySequenceRecorder::ModifierAndKey);
310 const bool patternIncludesKey = pattern & (KKeySequenceRecorder::Key | KKeySequenceRecorder::ModifierAndKey);
311
312 if (inputIncludesModifiers != patternIncludesModifiers) {
313 // TODO KF7: drop isOkWhenModifierless() and require users to pass the Key pattern explicitly
314 if (!(patternIncludesModifiers && patternIncludesKey && isOkWhenModifierless(key: keyCombination.key()))) {
315 continue;
316 }
317 }
318
319 if (inputIncludesKey == patternIncludesKey) {
320 return true;
321 }
322 }
323
324 return false;
325}
326
327static QKeySequence appendToSequence(const QKeySequence &sequence, int key)
328{
329 if (sequence.count() >= KKeySequenceRecorderPrivate::MaxKeyCount) {
330 qCWarning(KGUIADDONS_LOG) << "Cannot append to a key to a sequence which is already of length" << sequence.count();
331 return sequence;
332 }
333
334 std::array<int, KKeySequenceRecorderPrivate::MaxKeyCount> keys{sequence[0].toCombined(),
335 sequence[1].toCombined(),
336 sequence[2].toCombined(),
337 sequence[3].toCombined()};
338 // When the user presses Mod(s)+Alt+Print, the SysReq event is fired only
339 // when the Alt key is released. Before we get the Mod(s)+SysReq event, we
340 // first get a Mod(s)+Alt event, which we have to ignore.
341 // Known limitation: only works when the Alt key is released before the Mod(s) key(s).
342 if ((key & ~Qt::KeyboardModifierMask) == Qt::Key_SysReq) {
343 key = Qt::Key_Print | (key & Qt::KeyboardModifierMask) | Qt::AltModifier;
344 if (sequence.count() > 0 && (sequence[sequence.count() - 1].toCombined() & ~Qt::KeyboardModifierMask) == Qt::Key_Alt) {
345 keys[sequence.count() - 1] = key;
346 return QKeySequence(keys[0], keys[1], keys[2], keys[3]);
347 }
348 }
349 keys[sequence.count()] = key;
350 return QKeySequence(keys[0], keys[1], keys[2], keys[3]);
351}
352
353KKeySequenceRecorderPrivate::KKeySequenceRecorderPrivate(KKeySequenceRecorder *qq)
354 : QObject(qq)
355 , q(qq)
356{
357}
358
359void KKeySequenceRecorderPrivate::controlModifierlessTimeout()
360{
361 if (m_currentKeySequence != 0 && !m_currentModifiers) {
362 // No modifier key pressed currently. Start the timeout
363 m_modifierlessTimer.start(msec: 600);
364 } else {
365 // A modifier is pressed. Stop the timeout
366 m_modifierlessTimer.stop();
367 }
368}
369
370bool KKeySequenceRecorderPrivate::eventFilter(QObject *watched, QEvent *event)
371{
372 if (!m_isRecording) {
373 return QObject::eventFilter(watched, event);
374 }
375
376 if (event->type() == QEvent::ShortcutOverride || event->type() == QEvent::ContextMenu) {
377 event->accept();
378 return true;
379 }
380 if (event->type() == QEvent::KeyRelease) {
381 handleKeyRelease(event: static_cast<QKeyEvent *>(event));
382 return true;
383 }
384 if (event->type() == QEvent::KeyPress) {
385 handleKeyPress(event: static_cast<QKeyEvent *>(event));
386 return true;
387 }
388 return QObject::eventFilter(watched, event);
389}
390
391static Qt::KeyboardModifiers keyToModifier(int key)
392{
393 switch (key) {
394 case Qt::Key_Meta:
395 case Qt::Key_Super_L:
396 case Qt::Key_Super_R:
397 // Qt doesn't properly recognize Super_L/Super_R as MetaModifier
398 return Qt::MetaModifier;
399 case Qt::Key_Shift:
400 return Qt::ShiftModifier;
401 case Qt::Key_Control:
402 return Qt::ControlModifier;
403 case Qt::Key_Alt:
404 return Qt::AltModifier;
405 default:
406 return Qt::NoModifier;
407 }
408}
409
410void KKeySequenceRecorderPrivate::handleKeyPress(QKeyEvent *event)
411{
412 m_isReleasingModifierOnly = false;
413 m_currentModifiers = event->modifiers() & modifierMask;
414 const int key = event->key();
415 switch (key) {
416 case -1:
417 qCWarning(KGUIADDONS_LOG) << "Got unknown key";
418 // Old behavior was to stop recording here instead of continuing like this
419 return;
420 case 0:
421 break;
422 case Qt::Key_AltGr:
423 // or else we get unicode salad
424 break;
425 case Qt::Key_Super_L:
426 case Qt::Key_Super_R:
427 case Qt::Key_Shift:
428 case Qt::Key_Control:
429 case Qt::Key_Alt:
430 case Qt::Key_Meta:
431 m_currentModifiers |= keyToModifier(key);
432 m_lastPressedModifiers = m_currentModifiers;
433 controlModifierlessTimeout();
434 Q_EMIT q->currentKeySequenceChanged();
435 break;
436 default:
437 m_lastPressedModifiers = Qt::NoModifier;
438
439 QKeyCombination keyCombination;
440 if ((key == Qt::Key_Backtab) && (m_currentModifiers & Qt::ShiftModifier)) {
441 keyCombination = QKeyCombination(m_currentModifiers, Qt::Key_Tab);
442 } else if (isShiftAsModifierAllowed(keyQt: key)) {
443 keyCombination = QKeyCombination(m_currentModifiers, Qt::Key(key));
444 } else {
445 keyCombination = QKeyCombination(m_currentModifiers & ~Qt::ShiftModifier, Qt::Key(key));
446 }
447
448 if (!isKeyCombinationAccepted(keyCombination)) {
449 return;
450 }
451
452 m_currentKeySequence = appendToSequence(sequence: m_currentKeySequence, key: keyCombination.toCombined());
453 Q_EMIT q->currentKeySequenceChanged();
454 // Now we are in a critical region (race), where recording is still
455 // ongoing, but key sequence has already changed (potentially) to the
456 // longest. But we still want currentKeySequenceChanged to trigger
457 // before gotKeySequence, so there's only so much we can do about it.
458 if ((!m_multiKeyShortcutsAllowed) || (m_currentKeySequence.count() == MaxKeyCount)) {
459 finishRecording();
460 break;
461 }
462 controlModifierlessTimeout();
463 }
464 event->accept();
465}
466
467// Turn a bunch of modifiers into mods + key
468// so that the ordering is always Meta + Ctrl + Alt + Shift
469static QKeyCombination prettifyModifierOnly(QKeyCombination modifierOnly)
470{
471 const Qt::KeyboardModifiers modifier = modifierOnly.keyboardModifiers();
472 if (modifier & Qt::ShiftModifier) {
473 return Qt::Key_Shift | (modifier & ~Qt::ShiftModifier);
474 } else if (modifier & Qt::AltModifier) {
475 return Qt::Key_Alt | (modifier & ~Qt::AltModifier);
476 } else if (modifier & Qt::ControlModifier) {
477 return Qt::Key_Control | (modifier & ~Qt::ControlModifier);
478 } else if (modifier & Qt::MetaModifier) {
479 return Qt::Key_Meta | (modifier & ~Qt::MetaModifier);
480 } else {
481 return Qt::Key(0);
482 }
483}
484
485void KKeySequenceRecorderPrivate::handleKeyRelease(QKeyEvent *event)
486{
487 Qt::KeyboardModifiers modifiers = event->modifiers() & modifierMask;
488
489 switch (event->key()) {
490 case -1:
491 return;
492 case Qt::Key_Super_L:
493 case Qt::Key_Super_R:
494 case Qt::Key_Meta:
495 case Qt::Key_Shift:
496 case Qt::Key_Control:
497 case Qt::Key_Alt:
498 modifiers &= ~keyToModifier(key: event->key());
499 }
500 if ((modifiers & m_currentModifiers) < m_currentModifiers) {
501 constexpr auto releaseTimeout = std::chrono::milliseconds(200);
502 const auto currentTime = std::chrono::steady_clock::now().time_since_epoch();
503 if (!m_isReleasingModifierOnly) {
504 m_isReleasingModifierOnly = true;
505 m_modifierFirstReleaseTime = currentTime;
506 }
507 if (!modifiers && (currentTime - m_modifierFirstReleaseTime) < releaseTimeout) {
508 const auto keyCombination = QKeyCombination(m_lastPressedModifiers, Qt::Key(0));
509 if (isKeyCombinationAccepted(keyCombination)) {
510 m_currentKeySequence = appendToSequence(sequence: m_currentKeySequence, key: prettifyModifierOnly(modifierOnly: keyCombination).toCombined());
511 m_lastPressedModifiers = Qt::NoModifier;
512 }
513 }
514 m_currentModifiers = modifiers;
515 Q_EMIT q->currentKeySequenceChanged();
516 if (m_currentKeySequence.count() == (m_multiKeyShortcutsAllowed ? MaxKeyCount : 1)) {
517 finishRecording();
518 }
519 controlModifierlessTimeout();
520 };
521}
522
523void KKeySequenceRecorderPrivate::receivedRecording()
524{
525 m_modifierlessTimer.stop();
526 m_isRecording = false;
527 m_currentModifiers = Qt::NoModifier;
528 m_lastPressedModifiers = Qt::NoModifier;
529 m_isReleasingModifierOnly = false;
530 if (m_inhibition) {
531 m_inhibition->disableInhibition();
532 }
533 QObject::disconnect(sender: KKeySequenceRecorderGlobal::self(), signal: &KKeySequenceRecorderGlobal::sequenceRecordingStarted, receiver: q, slot: &KKeySequenceRecorder::cancelRecording);
534 Q_EMIT q->recordingChanged();
535}
536
537void KKeySequenceRecorderPrivate::finishRecording()
538{
539 receivedRecording();
540 Q_EMIT q->gotKeySequence(keySequence: m_currentKeySequence);
541}
542
543KKeySequenceRecorder::KKeySequenceRecorder(QWindow *window, QObject *parent)
544 : QObject(parent)
545 , d(new KKeySequenceRecorderPrivate(this))
546{
547 d->m_isRecording = false;
548 d->m_patterns = ModifierAndKey;
549 d->m_multiKeyShortcutsAllowed = true;
550
551 setWindow(window);
552 connect(sender: &d->m_modifierlessTimer, signal: &QTimer::timeout, context: d.get(), slot: &KKeySequenceRecorderPrivate::finishRecording);
553}
554
555KKeySequenceRecorder::~KKeySequenceRecorder() noexcept
556{
557 if (d->m_inhibition && d->m_inhibition->shortcutsAreInhibited()) {
558 d->m_inhibition->disableInhibition();
559 }
560}
561
562void KKeySequenceRecorder::startRecording()
563{
564 d->m_previousKeySequence = d->m_currentKeySequence;
565
566 KKeySequenceRecorderGlobal::self()->sequenceRecordingStarted();
567 connect(sender: KKeySequenceRecorderGlobal::self(),
568 signal: &KKeySequenceRecorderGlobal::sequenceRecordingStarted,
569 context: this,
570 slot: &KKeySequenceRecorder::cancelRecording,
571 type: Qt::UniqueConnection);
572
573 if (!d->m_window) {
574 qCWarning(KGUIADDONS_LOG) << "Cannot record without a window";
575 return;
576 }
577 d->m_isRecording = true;
578 d->m_currentKeySequence = QKeySequence();
579 if (d->m_inhibition) {
580 d->m_inhibition->enableInhibition();
581 }
582 Q_EMIT recordingChanged();
583 Q_EMIT currentKeySequenceChanged();
584}
585
586void KKeySequenceRecorder::cancelRecording()
587{
588 setCurrentKeySequence(d->m_previousKeySequence);
589 d->receivedRecording();
590 Q_ASSERT(!isRecording());
591}
592
593bool KKeySequenceRecorder::isRecording() const
594{
595 return d->m_isRecording;
596}
597
598QKeySequence KKeySequenceRecorder::currentKeySequence() const
599{
600 // We need a check for count() here because there's a race between the
601 // state of recording and a length of currentKeySequence.
602 if (d->m_isRecording && d->m_currentKeySequence.count() < KKeySequenceRecorderPrivate::MaxKeyCount) {
603 return appendToSequence(sequence: d->m_currentKeySequence, key: d->m_currentModifiers);
604 } else {
605 return d->m_currentKeySequence;
606 }
607}
608
609void KKeySequenceRecorder::setCurrentKeySequence(const QKeySequence &sequence)
610{
611 if (d->m_currentKeySequence == sequence) {
612 return;
613 }
614 d->m_currentKeySequence = sequence;
615 Q_EMIT currentKeySequenceChanged();
616}
617
618QWindow *KKeySequenceRecorder::window() const
619{
620 return d->m_window;
621}
622
623void KKeySequenceRecorder::setWindow(QWindow *window)
624{
625 if (window == d->m_window) {
626 return;
627 }
628
629 if (d->m_window) {
630 d->m_window->removeEventFilter(obj: d.get());
631 }
632
633 if (window) {
634 window->installEventFilter(filterObj: d.get());
635 qCDebug(KGUIADDONS_LOG) << "listening for events in" << window;
636 }
637
638 if (qGuiApp->platformName() == QLatin1String("wayland")) {
639#ifdef WITH_WAYLAND
640 d->m_inhibition.reset(p: new WaylandInhibition(window));
641#endif
642 } else {
643 d->m_inhibition.reset(p: new KeyboardGrabber(window));
644 }
645
646 d->m_window = window;
647
648 Q_EMIT windowChanged();
649}
650
651bool KKeySequenceRecorder::multiKeyShortcutsAllowed() const
652{
653 return d->m_multiKeyShortcutsAllowed;
654}
655
656void KKeySequenceRecorder::setMultiKeyShortcutsAllowed(bool allowed)
657{
658 if (allowed == d->m_multiKeyShortcutsAllowed) {
659 return;
660 }
661 d->m_multiKeyShortcutsAllowed = allowed;
662 Q_EMIT multiKeyShortcutsAllowedChanged();
663}
664
665#if KGUIADDONS_BUILD_DEPRECATED_SINCE(6, 12)
666bool KKeySequenceRecorder::modifierlessAllowed() const
667{
668 return d->m_patterns & Key;
669}
670
671void KKeySequenceRecorder::setModifierlessAllowed(bool allowed)
672{
673 if (allowed) {
674 setPatterns(d->m_patterns | Key);
675 } else {
676 setPatterns(d->m_patterns & ~Key);
677 }
678}
679
680bool KKeySequenceRecorder::modifierOnlyAllowed() const
681{
682 return d->m_patterns & Modifier;
683}
684
685void KKeySequenceRecorder::setModifierOnlyAllowed(bool allowed)
686{
687 if (allowed) {
688 setPatterns(d->m_patterns | Modifier);
689 } else {
690 setPatterns(d->m_patterns & ~Modifier);
691 }
692}
693#endif
694
695void KKeySequenceRecorder::setPatterns(Patterns patterns)
696{
697 if (!patterns) {
698 return;
699 }
700
701 if (patterns == d->m_patterns) {
702 return;
703 }
704
705#if KGUIADDONS_BUILD_DEPRECATED_SINCE(6, 12)
706 const bool oldModifierlessAllowed = modifierlessAllowed();
707 const bool oldModifierOnlyAllowed = modifierOnlyAllowed();
708#endif
709
710 d->m_patterns = patterns;
711 Q_EMIT patternsChanged();
712
713#if KGUIADDONS_BUILD_DEPRECATED_SINCE(6, 12)
714 if (modifierlessAllowed() != oldModifierlessAllowed) {
715 Q_EMIT modifierlessAllowedChanged();
716 }
717 if (modifierOnlyAllowed() != oldModifierOnlyAllowed) {
718 Q_EMIT modifierOnlyAllowedChanged();
719 }
720#endif
721}
722
723KKeySequenceRecorder::Patterns KKeySequenceRecorder::patterns() const
724{
725 return d->m_patterns;
726}
727
728#include "kkeysequencerecorder.moc"
729#include "moc_kkeysequencerecorder.cpp"
730

source code of kguiaddons/src/recorder/kkeysequencerecorder.cpp