1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2019 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the QtGui module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or (at your option) the GNU General |
28 | ** Public license version 3 or any later version approved by the KDE Free |
29 | ** Qt Foundation. The licenses are as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | ** included in the packaging of this file. Please review the following |
32 | ** information to ensure the GNU General Public License requirements will |
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | ** |
36 | ** $QT_END_LICENSE$ |
37 | ** |
38 | ****************************************************************************/ |
39 | |
40 | #include "qshortcut.h" |
41 | #include "qshortcut_p.h" |
42 | |
43 | #include <qevent.h> |
44 | #include <qguiapplication.h> |
45 | #include <qwindow.h> |
46 | #include <private/qguiapplication_p.h> |
47 | #include <qpa/qplatformmenu.h> |
48 | |
49 | QT_BEGIN_NAMESPACE |
50 | |
51 | #define QAPP_CHECK(functionName) \ |
52 | if (Q_UNLIKELY(!qApp)) { \ |
53 | qWarning("QShortcut: Initialize QGuiApplication before calling '" functionName "'."); \ |
54 | return; \ |
55 | } |
56 | |
57 | /*! |
58 | \class QShortcut |
59 | \brief The QShortcut class is used to create keyboard shortcuts. |
60 | |
61 | \ingroup events |
62 | \inmodule QtGui |
63 | |
64 | The QShortcut class provides a way of connecting keyboard |
65 | shortcuts to Qt's \l{signals and slots} mechanism, so that |
66 | objects can be informed when a shortcut is executed. The shortcut |
67 | can be set up to contain all the key presses necessary to |
68 | describe a keyboard shortcut, including the states of modifier |
69 | keys such as \uicontrol Shift, \uicontrol Ctrl, and \uicontrol Alt. |
70 | |
71 | \target mnemonic |
72 | |
73 | In widget applications, certain widgets can use '&' in front of a character. |
74 | This will automatically create a mnemonic (a shortcut) for that character, |
75 | e.g. "E&xit" will create the shortcut \uicontrol Alt+X (use '&&' |
76 | to display an actual ampersand). The widget might consume and perform |
77 | an action on a given shortcut. On X11 the ampersand will not be |
78 | shown and the character will be underlined. On Windows, shortcuts |
79 | are normally not displayed until the user presses the \uicontrol Alt |
80 | key, but this is a setting the user can change. On Mac, shortcuts |
81 | are disabled by default. Call \l qt_set_sequence_auto_mnemonic() to |
82 | enable them. However, because mnemonic shortcuts do not fit in |
83 | with Aqua's guidelines, Qt will not show the shortcut character |
84 | underlined. |
85 | |
86 | For applications that use menus, it may be more convenient to |
87 | use the convenience functions provided in the QMenu class to |
88 | assign keyboard shortcuts to menu items as they are created. |
89 | Alternatively, shortcuts may be associated with other types of |
90 | actions in the QAction class. |
91 | |
92 | The simplest way to create a shortcut for a particular widget is |
93 | to construct the shortcut with a key sequence. For example: |
94 | |
95 | \snippet code/src_gui_kernel_qshortcut.cpp 0 |
96 | |
97 | When the user types the \l{QKeySequence}{key sequence} |
98 | for a given shortcut, the shortcut's activated() signal is |
99 | emitted. (In the case of ambiguity, the activatedAmbiguously() |
100 | signal is emitted.) A shortcut is "listened for" by Qt's event |
101 | loop when the shortcut's parent widget is receiving events. |
102 | |
103 | A shortcut's key sequence can be set with setKey() and retrieved |
104 | with key(). A shortcut can be enabled or disabled with |
105 | setEnabled(), and can have "What's This?" help text set with |
106 | setWhatsThis(). |
107 | |
108 | \sa QShortcutEvent, QKeySequence, QAction |
109 | */ |
110 | |
111 | /*! |
112 | \fn void QShortcut::activated() |
113 | |
114 | This signal is emitted when the user types the shortcut's key |
115 | sequence. |
116 | |
117 | \sa activatedAmbiguously() |
118 | */ |
119 | |
120 | /*! |
121 | \fn void QShortcut::activatedAmbiguously() |
122 | |
123 | When a key sequence is being typed at the keyboard, it is said to |
124 | be ambiguous as long as it matches the start of more than one |
125 | shortcut. |
126 | |
127 | When a shortcut's key sequence is completed, |
128 | activatedAmbiguously() is emitted if the key sequence is still |
129 | ambiguous (i.e., it is the start of one or more other shortcuts). |
130 | The activated() signal is not emitted in this case. |
131 | |
132 | \sa activated() |
133 | */ |
134 | |
135 | static bool simpleContextMatcher(QObject *object, Qt::ShortcutContext context) |
136 | { |
137 | auto guiShortcut = qobject_cast<QShortcut *>(object); |
138 | if (QGuiApplication::applicationState() != Qt::ApplicationActive || guiShortcut == nullptr) |
139 | return false; |
140 | if (context == Qt::ApplicationShortcut) |
141 | return true; |
142 | auto focusWindow = QGuiApplication::focusWindow(); |
143 | if (!focusWindow) |
144 | return false; |
145 | auto window = qobject_cast<const QWindow *>(guiShortcut->parent()); |
146 | if (!window) |
147 | return false; |
148 | if (focusWindow == window && focusWindow->isTopLevel()) |
149 | return context == Qt::WindowShortcut || context == Qt::WidgetWithChildrenShortcut; |
150 | return focusWindow->isAncestorOf(window, QWindow::ExcludeTransients); |
151 | } |
152 | |
153 | QShortcutMap::ContextMatcher QShortcutPrivate::contextMatcher() const |
154 | { |
155 | return simpleContextMatcher; |
156 | } |
157 | |
158 | void QShortcutPrivate::redoGrab(QShortcutMap &map) |
159 | { |
160 | Q_Q(QShortcut); |
161 | if (Q_UNLIKELY(!parent)) { |
162 | qWarning("QShortcut: No window parent defined" ); |
163 | return; |
164 | } |
165 | |
166 | if (sc_id) |
167 | map.removeShortcut(sc_id, q); |
168 | if (sc_sequence.isEmpty()) |
169 | return; |
170 | sc_id = map.addShortcut(q, sc_sequence, sc_context, contextMatcher()); |
171 | if (!sc_enabled) |
172 | map.setShortcutEnabled(false, sc_id, q); |
173 | if (!sc_autorepeat) |
174 | map.setShortcutAutoRepeat(false, sc_id, q); |
175 | } |
176 | |
177 | QShortcutPrivate *QGuiApplicationPrivate::createShortcutPrivate() const |
178 | { |
179 | return new QShortcutPrivate; |
180 | } |
181 | |
182 | /*! |
183 | Constructs a QShortcut object for the \a parent, which should be a |
184 | QWindow or a QWidget. |
185 | |
186 | Since no shortcut key sequence is specified, the shortcut will not emit any |
187 | signals. |
188 | |
189 | \sa setKey() |
190 | */ |
191 | QShortcut::QShortcut(QObject *parent) |
192 | : QObject(*QGuiApplicationPrivate::instance()->createShortcutPrivate(), parent) |
193 | { |
194 | Q_ASSERT(parent != nullptr); |
195 | } |
196 | |
197 | /*! |
198 | Constructs a QShortcut object for the \a parent, which should be a |
199 | QWindow or a QWidget. |
200 | |
201 | The shortcut operates on its parent, listening for \l{QShortcutEvent}s that |
202 | match the \a key sequence. Depending on the ambiguity of the event, the |
203 | shortcut will call the \a member function, or the \a ambiguousMember function, |
204 | if the key press was in the shortcut's \a context. |
205 | */ |
206 | QShortcut::QShortcut(const QKeySequence &key, QObject *parent, |
207 | const char *member, const char *ambiguousMember, |
208 | Qt::ShortcutContext context) |
209 | : QShortcut(parent) |
210 | { |
211 | Q_D(QShortcut); |
212 | d->sc_context = context; |
213 | d->sc_sequence = key; |
214 | d->redoGrab(QGuiApplicationPrivate::instance()->shortcutMap); |
215 | if (member) |
216 | connect(this, SIGNAL(activated()), parent, member); |
217 | if (ambiguousMember) |
218 | connect(this, SIGNAL(activatedAmbiguously()), parent, ambiguousMember); |
219 | } |
220 | |
221 | |
222 | /*! |
223 | \fn template<typename Functor> QShortcut::QShortcut(const QKeySequence &key, QObject *parent, Functor functor, Qt::ShortcutContext shortcutContext = Qt::WindowShortcut) |
224 | \since 5.15 |
225 | \overload |
226 | |
227 | This is a QShortcut convenience constructor which connects the shortcut's |
228 | \l{QShortcut::activated()}{activated()} signal to the \a functor. |
229 | */ |
230 | /*! |
231 | \fn template<typename Functor> QShortcut::QShortcut(const QKeySequence &key, QObject *parent, const QObject *context, Functor functor, Qt::ShortcutContext shortcutContext = Qt::WindowShortcut) |
232 | \since 5.15 |
233 | \overload |
234 | |
235 | This is a QShortcut convenience constructor which connects the shortcut's |
236 | \l{QShortcut::activated()}{activated()} signal to the \a functor. |
237 | |
238 | The \a functor can be a pointer to a member function of the \a context object. |
239 | |
240 | If the \a context object is destroyed, the \a functor will not be called. |
241 | */ |
242 | /*! |
243 | \fn template<typename Functor, typename FunctorAmbiguous> QShortcut::QShortcut(const QKeySequence &key, QObject *parent, const QObject *context, Functor functor, FunctorAmbiguous functorAmbiguous, Qt::ShortcutContext shortcutContext = Qt::WindowShortcut) |
244 | \since 5.15 |
245 | \overload |
246 | |
247 | This is a QShortcut convenience constructor which connects the shortcut's |
248 | \l{QShortcut::activated()}{activated()} signal to the \a functor and |
249 | \l{QShortcut::activatedAmbiguously()}{activatedAmbiguously()} |
250 | signal to the \a functorAmbiguous. |
251 | |
252 | The \a functor and \a functorAmbiguous can be a pointer to a member |
253 | function of the \a context object. |
254 | |
255 | If the \a context object is destroyed, the \a functor and |
256 | \a functorAmbiguous will not be called. |
257 | */ |
258 | /*! |
259 | \fn template<typename Functor, typename FunctorAmbiguous> QShortcut::QShortcut(const QKeySequence &key, QObject *parent, const QObject *context1, Functor functor, const QObject *context2, FunctorAmbiguous functorAmbiguous, Qt::ShortcutContext shortcutContext = Qt::WindowShortcut) |
260 | \since 5.15 |
261 | \overload |
262 | |
263 | This is a QShortcut convenience constructor which connects the shortcut's |
264 | \l{QShortcut::activated()}{activated()} signal to the \a functor and |
265 | \l{QShortcut::activatedAmbiguously()}{activatedAmbiguously()} |
266 | signal to the \a functorAmbiguous. |
267 | |
268 | The \a functor can be a pointer to a member function of the |
269 | \a context1 object. |
270 | The \a functorAmbiguous can be a pointer to a member function of the |
271 | \a context2 object. |
272 | |
273 | If the \a context1 object is destroyed, the \a functor will not be called. |
274 | If the \a context2 object is destroyed, the \a functorAmbiguous |
275 | will not be called. |
276 | */ |
277 | |
278 | /*! |
279 | Destroys the shortcut. |
280 | */ |
281 | QShortcut::~QShortcut() |
282 | { |
283 | Q_D(QShortcut); |
284 | if (qApp) |
285 | QGuiApplicationPrivate::instance()->shortcutMap.removeShortcut(d->sc_id, this); |
286 | } |
287 | |
288 | /*! |
289 | \property QShortcut::key |
290 | \brief the shortcut's key sequence |
291 | |
292 | This is a key sequence with an optional combination of Shift, Ctrl, |
293 | and Alt. The key sequence may be supplied in a number of ways: |
294 | |
295 | \snippet code/src_gui_kernel_qshortcut.cpp 1 |
296 | |
297 | By default, this property contains an empty key sequence. |
298 | */ |
299 | void QShortcut::setKey(const QKeySequence &key) |
300 | { |
301 | Q_D(QShortcut); |
302 | if (d->sc_sequence == key) |
303 | return; |
304 | QAPP_CHECK("setKey" ); |
305 | d->sc_sequence = key; |
306 | d->redoGrab(QGuiApplicationPrivate::instance()->shortcutMap); |
307 | } |
308 | |
309 | QKeySequence QShortcut::key() const |
310 | { |
311 | Q_D(const QShortcut); |
312 | return d->sc_sequence; |
313 | } |
314 | |
315 | /*! |
316 | \property QShortcut::enabled |
317 | \brief whether the shortcut is enabled |
318 | |
319 | An enabled shortcut emits the activated() or activatedAmbiguously() |
320 | signal when a QShortcutEvent occurs that matches the shortcut's |
321 | key() sequence. |
322 | |
323 | If the application is in \c WhatsThis mode the shortcut will not emit |
324 | the signals, but will show the "What's This?" text instead. |
325 | |
326 | By default, this property is \c true. |
327 | |
328 | \sa whatsThis |
329 | */ |
330 | void QShortcut::setEnabled(bool enable) |
331 | { |
332 | Q_D(QShortcut); |
333 | if (d->sc_enabled == enable) |
334 | return; |
335 | QAPP_CHECK("setEnabled" ); |
336 | d->sc_enabled = enable; |
337 | QGuiApplicationPrivate::instance()->shortcutMap.setShortcutEnabled(enable, d->sc_id, this); |
338 | } |
339 | |
340 | bool QShortcut::isEnabled() const |
341 | { |
342 | Q_D(const QShortcut); |
343 | return d->sc_enabled; |
344 | } |
345 | |
346 | /*! |
347 | \property QShortcut::context |
348 | \brief the context in which the shortcut is valid |
349 | |
350 | A shortcut's context decides in which circumstances a shortcut is |
351 | allowed to be triggered. The normal context is Qt::WindowShortcut, |
352 | which allows the shortcut to trigger if the parent (the widget |
353 | containing the shortcut) is a subwidget of the active top-level |
354 | window. |
355 | |
356 | By default, this property is set to Qt::WindowShortcut. |
357 | */ |
358 | void QShortcut::setContext(Qt::ShortcutContext context) |
359 | { |
360 | Q_D(QShortcut); |
361 | if (d->sc_context == context) |
362 | return; |
363 | QAPP_CHECK("setContext" ); |
364 | d->sc_context = context; |
365 | d->redoGrab(QGuiApplicationPrivate::instance()->shortcutMap); |
366 | } |
367 | |
368 | Qt::ShortcutContext QShortcut::context() const |
369 | { |
370 | Q_D(const QShortcut); |
371 | return d->sc_context; |
372 | } |
373 | |
374 | /*! |
375 | \property QShortcut::autoRepeat |
376 | \brief whether the shortcut can auto repeat |
377 | |
378 | If true, the shortcut will auto repeat when the keyboard shortcut |
379 | combination is held down, provided that keyboard auto repeat is |
380 | enabled on the system. |
381 | The default value is true. |
382 | */ |
383 | void QShortcut::setAutoRepeat(bool on) |
384 | { |
385 | Q_D(QShortcut); |
386 | if (d->sc_autorepeat == on) |
387 | return; |
388 | QAPP_CHECK("setAutoRepeat" ); |
389 | d->sc_autorepeat = on; |
390 | QGuiApplicationPrivate::instance()->shortcutMap.setShortcutAutoRepeat(on, d->sc_id, this); |
391 | } |
392 | |
393 | bool QShortcut::autoRepeat() const |
394 | { |
395 | Q_D(const QShortcut); |
396 | return d->sc_autorepeat; |
397 | } |
398 | |
399 | |
400 | /*! |
401 | Sets the shortcut's "What's This?" help \a text. |
402 | |
403 | The text will be shown when a widget application is in "What's |
404 | This?" mode and the user types the shortcut key() sequence. |
405 | |
406 | To set "What's This?" help on a menu item (with or without a |
407 | shortcut key), set the help on the item's action. |
408 | |
409 | By default, the help text is an empty string. |
410 | |
411 | This function has no effect in applications that don't use |
412 | widgets. |
413 | |
414 | \sa QWhatsThis::inWhatsThisMode(), QAction::setWhatsThis() |
415 | */ |
416 | void QShortcut::setWhatsThis(const QString &text) |
417 | { |
418 | Q_D(QShortcut); |
419 | d->sc_whatsthis = text; |
420 | } |
421 | |
422 | /*! |
423 | Returns the shortcut's "What's This?" help text. |
424 | |
425 | \sa setWhatsThis() |
426 | */ |
427 | QString QShortcut::whatsThis() const |
428 | { |
429 | Q_D(const QShortcut); |
430 | return d->sc_whatsthis; |
431 | } |
432 | |
433 | /*! |
434 | Returns the shortcut's ID. |
435 | |
436 | \sa QShortcutEvent::shortcutId() |
437 | */ |
438 | int QShortcut::id() const |
439 | { |
440 | Q_D(const QShortcut); |
441 | return d->sc_id; |
442 | } |
443 | |
444 | /*! |
445 | \fn QWidget *QShortcut::parentWidget() const |
446 | |
447 | Returns the shortcut's parent widget. |
448 | */ |
449 | |
450 | /*! |
451 | \internal |
452 | */ |
453 | bool QShortcut::event(QEvent *e) |
454 | { |
455 | Q_D(QShortcut); |
456 | if (d->sc_enabled && e->type() == QEvent::Shortcut) { |
457 | auto se = static_cast<QShortcutEvent *>(e); |
458 | if (se->shortcutId() == d->sc_id && se->key() == d->sc_sequence |
459 | && !d->handleWhatsThis()) { |
460 | if (se->isAmbiguous()) |
461 | emit activatedAmbiguously(); |
462 | else |
463 | emit activated(); |
464 | return true; |
465 | } |
466 | } |
467 | return QObject::event(e); |
468 | } |
469 | |
470 | QT_END_NAMESPACE |
471 | |
472 | #include "moc_qshortcut.cpp" |
473 | |