1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Copyright (C) 2013 Ivan Komissarov.
5** Contact: https://www.qt.io/licensing/
6**
7** This file is part of the QtWidgets module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial License Usage
11** Licensees holding valid commercial Qt licenses may use this file in
12** accordance with the commercial license agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and The Qt Company. For licensing terms
15** and conditions see https://www.qt.io/terms-conditions. For further
16** information use the contact form at https://www.qt.io/contact-us.
17**
18** GNU Lesser General Public License Usage
19** Alternatively, this file may be used under the terms of the GNU Lesser
20** General Public License version 3 as published by the Free Software
21** Foundation and appearing in the file LICENSE.LGPL3 included in the
22** packaging of this file. Please review the following information to
23** ensure the GNU Lesser General Public License version 3 requirements
24** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
25**
26** GNU General Public License Usage
27** Alternatively, this file may be used under the terms of the GNU
28** General Public License version 2.0 or (at your option) the GNU General
29** Public license version 3 or any later version approved by the KDE Free
30** Qt Foundation. The licenses are as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
32** included in the packaging of this file. Please review the following
33** information to ensure the GNU General Public License requirements will
34** be met: https://www.gnu.org/licenses/gpl-2.0.html and
35** https://www.gnu.org/licenses/gpl-3.0.html.
36**
37** $QT_END_LICENSE$
38**
39****************************************************************************/
40
41#include "qkeysequenceedit.h"
42#include "qkeysequenceedit_p.h"
43
44#include "qboxlayout.h"
45#include "qlineedit.h"
46#include <private/qkeymapper_p.h>
47
48QT_BEGIN_NAMESPACE
49
50Q_STATIC_ASSERT(QKeySequencePrivate::MaxKeyCount == 4); // assumed by the code around here
51
52void QKeySequenceEditPrivate::init()
53{
54 Q_Q(QKeySequenceEdit);
55
56 lineEdit = new QLineEdit(q);
57 lineEdit->setObjectName(QStringLiteral("qt_keysequenceedit_lineedit"));
58 keyNum = 0;
59 prevKey = -1;
60 releaseTimer = 0;
61
62 QVBoxLayout *layout = new QVBoxLayout(q);
63 layout->setContentsMargins(left: 0, top: 0, right: 0, bottom: 0);
64 layout->addWidget(lineEdit);
65
66 key[0] = key[1] = key[2] = key[3] = 0;
67
68 lineEdit->setFocusProxy(q);
69 lineEdit->installEventFilter(filterObj: q);
70 resetState();
71
72 q->setSizePolicy(hor: QSizePolicy::Expanding, ver: QSizePolicy::Fixed);
73 q->setFocusPolicy(Qt::StrongFocus);
74 q->setAttribute(Qt::WA_MacShowFocusRect, on: true);
75 q->setAttribute(Qt::WA_InputMethodEnabled, on: false);
76
77 // TODO: add clear button
78}
79
80int QKeySequenceEditPrivate::translateModifiers(Qt::KeyboardModifiers state, const QString &text)
81{
82 Q_UNUSED(text);
83 int result = 0;
84 if (state & Qt::ControlModifier)
85 result |= Qt::CTRL;
86 if (state & Qt::MetaModifier)
87 result |= Qt::META;
88 if (state & Qt::AltModifier)
89 result |= Qt::ALT;
90 return result;
91}
92
93void QKeySequenceEditPrivate::resetState()
94{
95 Q_Q(QKeySequenceEdit);
96
97 if (releaseTimer) {
98 q->killTimer(id: releaseTimer);
99 releaseTimer = 0;
100 }
101 prevKey = -1;
102 lineEdit->setText(keySequence.toString(format: QKeySequence::NativeText));
103 lineEdit->setPlaceholderText(QKeySequenceEdit::tr(s: "Press shortcut"));
104}
105
106void QKeySequenceEditPrivate::finishEditing()
107{
108 Q_Q(QKeySequenceEdit);
109
110 resetState();
111 emit q->keySequenceChanged(keySequence);
112 emit q->editingFinished();
113}
114
115/*!
116 \class QKeySequenceEdit
117 \brief The QKeySequenceEdit widget allows to input a QKeySequence.
118
119 \inmodule QtWidgets
120
121 \since 5.2
122
123 This widget lets the user choose a QKeySequence, which is usually used as
124 a shortcut. The recording is initiated when the widget receives the focus
125 and ends one second after the user releases the last key.
126
127 \sa QKeySequenceEdit::keySequence
128*/
129
130/*!
131 Constructs a QKeySequenceEdit widget with the given \a parent.
132*/
133QKeySequenceEdit::QKeySequenceEdit(QWidget *parent)
134 : QKeySequenceEdit(*new QKeySequenceEditPrivate, parent, { })
135{
136}
137
138/*!
139 Constructs a QKeySequenceEdit widget with the given \a keySequence and \a parent.
140*/
141QKeySequenceEdit::QKeySequenceEdit(const QKeySequence &keySequence, QWidget *parent)
142 : QKeySequenceEdit(parent)
143{
144 setKeySequence(keySequence);
145}
146
147/*!
148 \internal
149*/
150QKeySequenceEdit::QKeySequenceEdit(QKeySequenceEditPrivate &dd, QWidget *parent, Qt::WindowFlags f) :
151 QWidget(dd, parent, f)
152{
153 Q_D(QKeySequenceEdit);
154 d->init();
155}
156
157/*!
158 Destroys the QKeySequenceEdit object.
159*/
160QKeySequenceEdit::~QKeySequenceEdit()
161{
162}
163
164/*!
165 \property QKeySequenceEdit::keySequence
166
167 \brief This property contains the currently chosen key sequence.
168
169 The shortcut can be changed by the user or via setter function.
170*/
171QKeySequence QKeySequenceEdit::keySequence() const
172{
173 Q_D(const QKeySequenceEdit);
174
175 return d->keySequence;
176}
177
178void QKeySequenceEdit::setKeySequence(const QKeySequence &keySequence)
179{
180 Q_D(QKeySequenceEdit);
181
182 d->resetState();
183
184 if (d->keySequence == keySequence)
185 return;
186
187 d->keySequence = keySequence;
188
189 d->key[0] = d->key[1] = d->key[2] = d->key[3] = 0;
190 d->keyNum = keySequence.count();
191 for (int i = 0; i < d->keyNum; ++i)
192 d->key[i] = keySequence[i];
193
194 d->lineEdit->setText(keySequence.toString(format: QKeySequence::NativeText));
195
196 emit keySequenceChanged(keySequence);
197}
198
199/*!
200 \fn void QKeySequenceEdit::editingFinished()
201
202 This signal is emitted when the user finishes entering the shortcut.
203
204 \note there is a one second delay before releasing the last key and
205 emitting this signal.
206*/
207
208/*!
209 \brief Clears the current key sequence.
210*/
211void QKeySequenceEdit::clear()
212{
213 setKeySequence(QKeySequence());
214}
215
216/*!
217 \reimp
218*/
219bool QKeySequenceEdit::event(QEvent *e)
220{
221 switch (e->type()) {
222 case QEvent::Shortcut:
223 return true;
224 case QEvent::ShortcutOverride:
225 e->accept();
226 return true;
227 default :
228 break;
229 }
230
231 return QWidget::event(event: e);
232}
233
234/*!
235 \reimp
236*/
237void QKeySequenceEdit::keyPressEvent(QKeyEvent *e)
238{
239 Q_D(QKeySequenceEdit);
240
241 int nextKey = e->key();
242
243 if (d->prevKey == -1) {
244 clear();
245 d->prevKey = nextKey;
246 }
247
248 d->lineEdit->setPlaceholderText(QString());
249 if (nextKey == Qt::Key_Control
250 || nextKey == Qt::Key_Shift
251 || nextKey == Qt::Key_Meta
252 || nextKey == Qt::Key_Alt
253 || nextKey == Qt::Key_unknown) {
254 return;
255 }
256
257 QString selectedText = d->lineEdit->selectedText();
258 if (!selectedText.isEmpty() && selectedText == d->lineEdit->text()) {
259 clear();
260 if (nextKey == Qt::Key_Backspace)
261 return;
262 }
263
264 if (d->keyNum >= QKeySequencePrivate::MaxKeyCount)
265 return;
266
267 if (e->modifiers() & Qt::ShiftModifier) {
268 QList<int> possibleKeys = QKeyMapper::possibleKeys(e);
269 int pkTotal = possibleKeys.count();
270 if (!pkTotal)
271 return;
272 bool found = false;
273 for (int i = 0; i < possibleKeys.size(); ++i) {
274 if (possibleKeys.at(i) - nextKey == int(e->modifiers())
275 || (possibleKeys.at(i) == nextKey && e->modifiers() == Qt::ShiftModifier)) {
276 nextKey = possibleKeys.at(i);
277 found = true;
278 break;
279 }
280 }
281 // Use as fallback
282 if (!found)
283 nextKey = possibleKeys.first();
284 } else {
285 nextKey |= d->translateModifiers(state: e->modifiers(), text: e->text());
286 }
287
288
289 d->key[d->keyNum] = nextKey;
290 d->keyNum++;
291
292 QKeySequence key(d->key[0], d->key[1], d->key[2], d->key[3]);
293 d->keySequence = key;
294 QString text = key.toString(format: QKeySequence::NativeText);
295 if (d->keyNum < QKeySequencePrivate::MaxKeyCount) {
296 //: This text is an "unfinished" shortcut, expands like "Ctrl+A, ..."
297 text = tr(s: "%1, ...").arg(a: text);
298 }
299 d->lineEdit->setText(text);
300 e->accept();
301}
302
303/*!
304 \reimp
305*/
306void QKeySequenceEdit::keyReleaseEvent(QKeyEvent *e)
307{
308 Q_D(QKeySequenceEdit);
309
310 if (d->prevKey == e->key()) {
311 if (d->keyNum < QKeySequencePrivate::MaxKeyCount)
312 d->releaseTimer = startTimer(interval: 1000);
313 else
314 d->finishEditing();
315 }
316 e->accept();
317}
318
319/*!
320 \reimp
321*/
322void QKeySequenceEdit::timerEvent(QTimerEvent *e)
323{
324 Q_D(QKeySequenceEdit);
325 if (e->timerId() == d->releaseTimer) {
326 d->finishEditing();
327 return;
328 }
329
330 QWidget::timerEvent(event: e);
331}
332
333QT_END_NAMESPACE
334
335#include "moc_qkeysequenceedit.cpp"
336

source code of qtbase/src/widgets/widgets/qkeysequenceedit.cpp