KDeclarative

keysequencevalidator.cpp
1/*
2 SPDX-FileCopyrightText: 2024 Arjen Hiemstra <ahiemstra@heimr.nl>
3 SPDX-FileCopyrightText: 2020 David Redondo <davidedmundson@kde.org>
4 SPDX-FileCopyrightText: 2014 David Edmundson <davidedmundson@kde.org>
5 SPDX-FileCopyrightText: 1998 Mark Donohoe <donohoe@kde.org>
6 SPDX-FileCopyrightText: 2001 Ellis Whitehead <ellis@kde.org>
7 SPDX-FileCopyrightText: 2007 Andreas Hartmetz <ahartmetz@gmail.com>
8
9 SPDX-License-Identifier: LGPL-2.1-or-later
10*/
11
12#include "keysequencevalidator.h"
13
14#include <KLocalizedString>
15#include <KStandardShortcut>
16
17#include <config-kdeclarative.h>
18#if HAVE_KGLOBALACCEL
19#include <KGlobalAccel>
20#include <KGlobalShortcutInfo>
21#endif
22
23KeySequenceValidator::KeySequenceValidator(QObject *parent)
24 : QObject(parent)
25{
26}
27
28QKeySequence KeySequenceValidator::currentKeySequence() const
29{
30 return m_currentKeySequence;
31}
32
33void KeySequenceValidator::setCurrentKeySequence(const QKeySequence &sequence)
34{
35 if (m_currentKeySequence == sequence) {
36 return;
37 }
38
39 m_currentKeySequence = sequence;
40 Q_EMIT currentKeySequenceChanged();
41}
42
43KeySequenceEnums::ShortcutTypes KeySequenceValidator::validateTypes() const
44{
45 return m_validateTypes;
46}
47
48void KeySequenceValidator::setValidateTypes(KeySequenceEnums::ShortcutTypes types)
49{
50 if (m_validateTypes == types) {
51 return;
52 }
53
54 m_validateTypes = types;
55 Q_EMIT validateTypesChanged();
56}
57
58void KeySequenceValidator::validateSequence(const QKeySequence &keySequence)
59{
60 ValidationResult result = ValidationResult::Accept;
61 if (m_validateTypes & KeySequenceEnums::GlobalShortcuts) {
62 result = validateGlobalShortcut(keySequence);
63 }
64
65 if (result == ValidationResult::Reject) {
66 Q_EMIT finished(m_currentKeySequence);
67 return;
68 }
69
70 if (result == ValidationResult::QuestionPending) {
71 return;
72 }
73
74 if (m_validateTypes & KeySequenceEnums::StandardShortcuts) {
75 result = validateStandardShortcut(keySequence);
76 }
77
78 if (result == ValidationResult::Reject) {
79 Q_EMIT finished(m_currentKeySequence);
80 } else if (result == ValidationResult::Accept) {
81 Q_EMIT finished(keySequence);
82 }
83}
84
85void KeySequenceValidator::accept()
86{
87 QKeySequence keySequence = m_pendingKeySequence;
88 m_pendingKeySequence = QKeySequence{};
89
90 ValidationResult result = ValidationResult::Accept;
91 if (m_validateGlobalPending) {
92 m_validateGlobalPending = false;
93 if (m_validateTypes & KeySequenceEnums::StandardShortcuts) {
94 result = validateStandardShortcut(keySequence);
95 }
96 }
97
98 if (result == ValidationResult::Reject) {
99 Q_EMIT finished(m_currentKeySequence);
100 } else if (result == ValidationResult::Accept) {
101#if HAVE_KGLOBALACCEL
102 if (m_validateTypes & KeySequenceEnums::GlobalShortcuts) {
104 }
105#endif
106 Q_EMIT finished(keySequence);
107 }
108}
109
110void KeySequenceValidator::reject()
111{
112 m_pendingKeySequence = QKeySequence{};
113 Q_EMIT finished(m_currentKeySequence);
114}
115
116KeySequenceValidator::ValidationResult KeySequenceValidator::validateGlobalShortcut(const QKeySequence &keySequence)
117{
118#ifdef Q_OS_WIN
119 // on windows F12 is reserved by the debugger at all times, so we can't use it for a global shortcut
120 if (keySequence.toString().contains(QLatin1String("F12"))) {
121 QString title = i18n("Reserved Shortcut");
122 QString message = i18n(
123 "The F12 key is reserved on Windows, so cannot be used for a global shortcut.\n"
124 "Please choose another one.");
125
126 Q_EMIT error(title, message);
127 return ValidationResult::Reject;
128 } else {
129 return ValidationResult::Accept;
130 }
131#elif HAVE_KGLOBALACCEL
132 // Global shortcuts are on key+modifier shortcuts. They can clash with a multi key shortcut.
133 QList<KGlobalShortcutInfo> others;
134 QList<KGlobalShortcutInfo> shadow;
135 QList<KGlobalShortcutInfo> shadowed;
136 if (!KGlobalAccel::isGlobalShortcutAvailable(keySequence, QString())) {
137 others << KGlobalAccel::globalShortcutsByKey(keySequence);
138
139 // look for shortcuts shadowing
140 shadow << KGlobalAccel::globalShortcutsByKey(keySequence, KGlobalAccel::MatchType::Shadows);
141 shadowed << KGlobalAccel::globalShortcutsByKey(keySequence, KGlobalAccel::MatchType::Shadowed);
142 }
143
144 if (!shadow.isEmpty() || !shadowed.isEmpty()) {
145 QString title = i18n("Global Shortcut Shadowing");
146 QString message;
147 if (!shadowed.isEmpty()) {
148 message += i18n("The '%1' key combination is shadowed by following global actions:\n").arg(keySequence.toString());
149 for (const KGlobalShortcutInfo &info : std::as_const(shadowed)) {
150 message += i18n("Action '%1' in context '%2'\n").arg(info.friendlyName(), info.contextFriendlyName());
151 }
152 }
153 if (!shadow.isEmpty()) {
154 message += i18n("The '%1' key combination shadows following global actions:\n").arg(keySequence.toString());
155 for (const KGlobalShortcutInfo &info : std::as_const(shadow)) {
156 message += i18n("Action '%1' in context '%2'\n").arg(info.friendlyName(), info.contextFriendlyName());
157 }
158 }
159
160 Q_EMIT error(title, message);
161 return ValidationResult::Reject;
162 }
163
164 if (!others.isEmpty()) {
165 QString title = i18nc("@dialog:title", "Found Conflict");
166 QString message;
167
168 if (others.size() == 1) {
169 auto info = others.at(0);
170 message = i18n("Shortcut '%1' is already assigned to action '%2' of %3.\nDo you want to reassign it?",
171 keySequence.toString(),
172 info.friendlyName(),
173 info.componentFriendlyName());
174 } else {
175 message = i18n("Shortcut '%1' is already assigned to the following actions:\n");
176 for (const auto &info : std::as_const(others)) {
177 message += i18n("Action '%1' of %2\n", info.friendlyName(), info.componentFriendlyName());
178 }
179 message += i18n("Do you want to reassign it?");
180 }
181
182 m_pendingKeySequence = keySequence;
183 m_validateGlobalPending = true;
184 Q_EMIT question(title, message);
185 return ValidationResult::QuestionPending;
186 }
187#endif
188 return ValidationResult::Accept;
189}
190
191KeySequenceValidator::ValidationResult KeySequenceValidator::validateStandardShortcut(const QKeySequence &keySequence)
192{
194 if (ssc != KStandardShortcut::AccelNone) {
195 QString title = i18n("Conflict with Standard Application Shortcut");
196 QString message = i18n(
197 "The '%1' key combination is also used for the standard action "
198 "\"%2\" that some applications use.\n"
199 "Do you really want to use it as a global shortcut as well?",
202
203 m_pendingKeySequence = keySequence;
204 Q_EMIT question(title, message);
205 return ValidationResult::QuestionPending;
206 } else {
207 return ValidationResult::Accept;
208 }
209}
210
211#include "moc_keysequencevalidator.cpp"
static QList< KGlobalShortcutInfo > globalShortcutsByKey(const QKeySequence &seq, MatchType type=Equal)
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...)
QString i18n(const char *text, const TYPE &arg...)
const QList< QKeySequence > & find()
QString label(StandardShortcut id)
const_reference at(qsizetype i) const const
bool isEmpty() const const
qsizetype size() const const
Q_EMITQ_EMIT
QString arg(Args &&... args) const const
void keySequence(QWidget *widget, const QKeySequence &keySequence)
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Apr 11 2025 11:51:39 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.