Kirigami-addons

keysequencehelper.cpp
1/*
2 SPDX-FileCopyrightText: 2020 David Redondo <kde@david-redondo.de>
3 SPDX-FileCopyrightText: 2014 David Edmundson <davidedmundson@kde.org>
4 SPDX-FileCopyrightText: 1998 Mark Donohoe <donohoe@kde.org>
5 SPDX-FileCopyrightText: 2001 Ellis Whitehead <ellis@kde.org>
6 SPDX-FileCopyrightText: 2007 Andreas Hartmetz <ahartmetz@gmail.com>
7
8 SPDX-License-Identifier: LGPL-2.1-or-later
9*/
10
11#include "keysequencehelper.h"
12
13#include <QDebug>
14#include <QHash>
15#include <QPointer>
16#include <QQmlEngine>
17#include <QQuickRenderControl>
18#include <QQuickWindow>
19
20#include <KLocalizedString>
21#include <KStandardShortcut>
22
23#ifdef HAVE_KGLOBALACCEL
24#include <KGlobalAccel>
25#include <KGlobalShortcutInfo>
26#endif
27
28class KeySequenceHelperPrivate
29{
30public:
31 KeySequenceHelperPrivate(KeySequenceHelper *qq);
32
33 /**
34 * Conflicts the key sequence @a seq with a current standard
35 * shortcut?
36 */
37 bool conflictWithStandardShortcuts(const QKeySequence &seq);
38
39 /**
40 * Conflicts the key sequence @a seq with a current global
41 * shortcut?
42 */
43 bool conflictWithGlobalShortcuts(const QKeySequence &seq);
44
45 /**
46 * Get permission to steal the shortcut @seq from the standard shortcut @a std.
47 */
48 bool stealStandardShortcut(KStandardShortcut::StandardShortcut std, const QKeySequence &seq);
49
50 bool checkAgainstStandardShortcuts() const
51 {
52 return checkAgainstShortcutTypes & KeySequenceHelper::StandardShortcuts;
53 }
54
55 bool checkAgainstGlobalShortcuts() const
56 {
57 return checkAgainstShortcutTypes & KeySequenceHelper::GlobalShortcuts;
58 }
59
60 // members
61 KeySequenceHelper *const q;
62
63 //! Check the key sequence against KStandardShortcut::find()
64 KeySequenceHelper::ShortcutTypes checkAgainstShortcutTypes;
65};
66
67KeySequenceHelperPrivate::KeySequenceHelperPrivate(KeySequenceHelper *qq)
68 : q(qq)
69 , checkAgainstShortcutTypes(KeySequenceHelper::StandardShortcuts | KeySequenceHelper::GlobalShortcuts)
70{
71}
72
73KeySequenceHelper::KeySequenceHelper(QObject *parent)
74 : KKeySequenceRecorder(nullptr, parent)
75 , d(new KeySequenceHelperPrivate(this))
76{
77}
78
79KeySequenceHelper::~KeySequenceHelper()
80{
81 delete d;
82}
83
84bool KeySequenceHelper::isKeySequenceAvailable(const QKeySequence &keySequence) const
85{
86 if (keySequence.isEmpty()) {
87 return true;
88 }
89 bool conflict = false;
90 if (d->checkAgainstShortcutTypes.testFlag(GlobalShortcuts)) {
91 conflict |= d->conflictWithGlobalShortcuts(keySequence);
92 }
93 if (d->checkAgainstShortcutTypes.testFlag(StandardShortcuts)) {
94 conflict |= d->conflictWithStandardShortcuts(keySequence);
95 }
96 return !conflict;
97}
98
99KeySequenceHelper::ShortcutTypes KeySequenceHelper::checkAgainstShortcutTypes()
100{
101 return d->checkAgainstShortcutTypes;
102}
103
104void KeySequenceHelper::setCheckAgainstShortcutTypes(KeySequenceHelper::ShortcutTypes types)
105{
106 if (d->checkAgainstShortcutTypes != types) {
107 d->checkAgainstShortcutTypes = types;
108 }
109 Q_EMIT checkAgainstShortcutTypesChanged();
110}
111
112bool KeySequenceHelperPrivate::conflictWithGlobalShortcuts(const QKeySequence &keySequence)
113{
114#ifdef Q_OS_WIN
115 // on windows F12 is reserved by the debugger at all times, so we can't use it for a global shortcut
116 if (KeySequenceHelper::GlobalShortcuts && keySequence.toString().contains(QLatin1String("F12"))) {
117 const QString title = i18nc("@title:dialog", "Reserved Shortcut");
118 const QString message = i18nc("@info",
119 "The F12 key is reserved on Windows, so cannot be used for a global shortcut.\n"
120 "Please choose another one.");
121
122 Q_EMIT q->errorOccurred(title, message);
123 }
124 return false;
125#elif defined(HAVE_KGLOBALACCEL)
126 if (!(checkAgainstShortcutTypes & KeySequenceHelper::GlobalShortcuts)) {
127 return false;
128 }
129
130 // Global shortcuts are on key+modifier shortcuts. They can clash with a multi key shortcut.
135 others << KGlobalAccel::globalShortcutsByKey(keySequence);
136
137 // look for shortcuts shadowing
138 shadow << KGlobalAccel::globalShortcutsByKey(keySequence, KGlobalAccel::MatchType::Shadows);
139 shadowed << KGlobalAccel::globalShortcutsByKey(keySequence, KGlobalAccel::MatchType::Shadowed);
140 }
141
142 if (!shadow.isEmpty() || !shadowed.isEmpty()) {
143 const QString title = i18nc("@title:dialog This happen when a global shortcut is already defined with the same shortcut", "Global Shortcut Shadowing");
144 QString message;
145 if (!shadowed.isEmpty()) {
146 message += i18nc("@info", "The '%1' key combination is shadowed by following global actions:\n").arg(keySequence.toString());
147 for (const KGlobalShortcutInfo &info : std::as_const(shadowed)) {
148 message += i18nc("@info", "Action '%1' in context '%2'\n").arg(info.friendlyName(), info.contextFriendlyName());
149 }
150 }
151 if (!shadow.isEmpty()) {
152 message += i18nc("@info", "The '%1' key combination shadows following global actions:\n").arg(keySequence.toString());
153 for (const KGlobalShortcutInfo &info : std::as_const(shadow)) {
154 message += i18nc("@info", "Action '%1' in context '%2'\n").arg(info.friendlyName(), info.contextFriendlyName());
155 }
156 }
157
158 Q_EMIT q->errorOccurred(title, message);
159 return true;
160 }
161
162 if (!others.isEmpty() && !KGlobalAccel::promptStealShortcutSystemwide(nullptr, others, keySequence)) {
163 return true;
164 }
165
166 // The user approved stealing the shortcut. We have to steal
167 // it immediately because KAction::setGlobalShortcut() refuses
168 // to set a global shortcut that is already used. There is no
169 // error it just silently fails. So be nice because this is
170 // most likely the first action that is done in the slot
171 // listening to keySequenceChanged().
173#endif
174 return false;
175}
176
177bool KeySequenceHelperPrivate::conflictWithStandardShortcuts(const QKeySequence &keySequence)
178{
179 if (!checkAgainstStandardShortcuts()) {
180 return false;
181 }
182
184 if (ssc != KStandardShortcut::AccelNone && !stealStandardShortcut(ssc, keySequence)) {
185 return true;
186 }
187 return false;
188}
189
190bool KeySequenceHelperPrivate::stealStandardShortcut(KStandardShortcut::StandardShortcut std, const QKeySequence &seq)
191{
192#ifndef Q_OS_ANDROID
193 const QString title = i18nc("@title:dialog", "Conflict with Standard Application Shortcut");
194 const QString message = i18nc("@info",
195 "The '%1' key combination is also used for the standard action "
196 "\"%2\" that some applications use.\n"
197 "Do you really want to use it as a global shortcut as well?",
200
201 Q_EMIT q->showStealStandardShortcutDialog(title, message, seq);
202#endif
203return false;
204}
205
206bool KeySequenceHelper::keySequenceIsEmpty(const QKeySequence &keySequence)
207{
208 return keySequence.isEmpty();
209}
210
211QString KeySequenceHelper::keySequenceNativeText(const QKeySequence &keySequence)
212{
213 return keySequence.toString(QKeySequence::NativeText);
214}
215
216QWindow *KeySequenceHelper::renderWindow(QQuickWindow *quickWindow)
217{
218 QWindow *renderWindow = QQuickRenderControl::renderWindowFor(quickWindow);
219 auto window = renderWindow ? renderWindow : quickWindow;
220 // If we have CppOwnership, set it explicitly to prevent the engine taking ownership of the window
221 // and crashing on teardown
224 }
225 return window;
226}
227
228#include "moc_keysequencehelper.cpp"
static QList< KGlobalShortcutInfo > globalShortcutsByKey(const QKeySequence &seq, MatchType type=Equal)
static bool promptStealShortcutSystemwide(QWidget *parent, const QList< KGlobalShortcutInfo > &shortcuts, const QKeySequence &seq)
static void stealShortcutSystemwide(const QKeySequence &seq)
static bool isGlobalShortcutAvailable(const QKeySequence &seq, const QString &component=QString())
QString i18nc(const char *context, const char *text, const TYPE &arg...)
const QList< QKeySequence > & find()
QString label(StandardShortcut id)
bool testFlag(Enum flag) const const
ObjectOwnership objectOwnership(QObject *object)
void setObjectOwnership(QObject *object, ObjectOwnership ownership)
QString toString(SequenceFormat format) const const
bool isEmpty() const const
Q_EMITQ_EMIT
QWindow * renderWindowFor(QQuickWindow *win, QPoint *offset)
QString arg(Args &&... args) const const
void keySequence(QWidget *widget, const QKeySequence &keySequence)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:12:26 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.