Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qshortcut_widgets.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5#include "qshortcut.h"
6#include "private/qshortcut_p.h"
7
8#include "private/qwidget_p.h"
9
10#include <qevent.h>
11#if QT_CONFIG(whatsthis)
12#include <qwhatsthis.h>
13#endif
14#if QT_CONFIG(menu)
15#include <qmenu.h>
16#endif
17#if QT_CONFIG(menubar)
18#include <qmenubar.h>
19#endif
20#include <qapplication.h>
21#include <private/qapplication_p.h>
22#include <private/qshortcutmap_p.h>
23#if QT_CONFIG(action)
24# include <private/qaction_p.h>
25#endif
26#include <private/qwidgetwindow_p.h>
27#include <qpa/qplatformmenu.h>
28
30
31static bool correctWidgetContext(Qt::ShortcutContext context, QWidget *w, QWidget *active_window);
32#if QT_CONFIG(graphicsview)
33static bool correctGraphicsWidgetContext(Qt::ShortcutContext context, QGraphicsWidget *w, QWidget *active_window);
34#endif
35#if QT_CONFIG(action)
36static bool correctActionContext(Qt::ShortcutContext context, QAction *a, QWidget *active_window);
37#endif
38
39
40/*! \internal
41 Returns \c true if the widget \a w is a logical sub window of the current
42 top-level widget.
43*/
44bool qWidgetShortcutContextMatcher(QObject *object, Qt::ShortcutContext context)
45{
46 Q_ASSERT_X(object, "QShortcutMap", "Shortcut has no owner. Illegal map state!");
47
48 QWidget *active_window = QApplication::activeWindow();
49
50 // popups do not become the active window,
51 // so we fake it here to get the correct context
52 // for the shortcut system.
53 if (QApplication::activePopupWidget())
54 active_window = QApplication::activePopupWidget();
55
56 if (!active_window) {
57 QWindow *qwindow = QGuiApplication::focusWindow();
58 if (qwindow && qwindow->isActive()) {
59 while (qwindow) {
60 if (auto widgetWindow = qobject_cast<QWidgetWindow *>(qwindow)) {
61 active_window = widgetWindow->widget();
62 break;
63 }
64 qwindow = qwindow->parent();
65 }
66 }
67 }
68
69#if QT_CONFIG(action)
70 if (auto a = qobject_cast<QAction *>(object))
71 return correctActionContext(context, a, active_window);
72#endif
73
74#if QT_CONFIG(graphicsview)
75 if (auto gw = qobject_cast<QGraphicsWidget *>(object))
76 return correctGraphicsWidgetContext(context, gw, active_window);
77#endif
78
79 auto w = qobject_cast<QWidget *>(object);
80 if (!w) {
81 if (auto s = qobject_cast<QShortcut *>(object))
82 w = qobject_cast<QWidget *>(s->parent());
83 }
84
85 if (!w) {
86 auto qwindow = qobject_cast<QWindow *>(object);
87 while (qwindow) {
88 if (auto widget_window = qobject_cast<QWidgetWindow *>(qwindow)) {
89 w = widget_window->widget();
90 break;
91 }
92 qwindow = qwindow->parent();
93 }
94 }
95
96 if (w)
97 return correctWidgetContext(context, w, active_window);
98
99 return QShortcutPrivate::simpleContextMatcher(object, context);
100}
101
102static bool correctWidgetContext(Qt::ShortcutContext context, QWidget *w, QWidget *active_window)
103{
104 if (!active_window)
105 return false;
106
107 bool visible = w->isVisible();
108#if QT_CONFIG(menubar)
109 if (auto menuBar = qobject_cast<QMenuBar *>(w)) {
110 if (auto *pmb = menuBar->platformMenuBar()) {
111 if (menuBar->parentWidget()) {
112 visible = true;
113 } else {
114 if (auto *ww = qobject_cast<QWidgetWindow *>(pmb->parentWindow()))
115 w = ww->widget(); // Good enough since we only care about the window
116 else
117 return false; // This is not a QWidget window. We won't deliver
118 }
119 }
120 }
121#endif
122
123 if (!visible || !w->isEnabled())
124 return false;
125
126 if (context == Qt::ApplicationShortcut)
127 return QApplicationPrivate::tryModalHelper(w, nullptr); // true, unless w is shadowed by a modal dialog
128
129 if (context == Qt::WidgetShortcut)
130 return w == QApplication::focusWidget();
131
132 if (context == Qt::WidgetWithChildrenShortcut) {
133 const QWidget *tw = QApplication::focusWidget();
134 while (tw && tw != w && (tw->windowType() == Qt::Widget || tw->windowType() == Qt::Popup || tw->windowType() == Qt::SubWindow))
135 tw = tw->parentWidget();
136 return tw == w;
137 }
138
139 // Below is Qt::WindowShortcut context
140 QWidget *tlw = w->window();
141#if QT_CONFIG(graphicsview)
142 if (auto topData = static_cast<QWidgetPrivate *>(QObjectPrivate::get(tlw))->extra.get()) {
143 if (topData->proxyWidget) {
144 bool res = correctGraphicsWidgetContext(context, topData->proxyWidget, active_window);
145 return res;
146 }
147 }
148#endif
149
150 if (active_window && active_window != tlw) {
151 /* if a floating tool window is active, keep shortcuts on the parent working.
152 * and if a popup window is active (f.ex a completer), keep shortcuts on the
153 * focus proxy working */
154 if (active_window->windowType() == Qt::Tool && active_window->parentWidget()) {
155 active_window = active_window->parentWidget()->window();
156 } else if (active_window->windowType() == Qt::Popup && active_window->focusProxy()) {
157 active_window = active_window->focusProxy()->window();
158 }
159 }
160
161 if (active_window != tlw) {
162#if QT_CONFIG(menubar)
163 // If the tlw is a QMenuBar then we allow it to proceed as this indicates that
164 // the QMenuBar is a parentless one and is therefore used for multiple top level
165 // windows in the application. This is common on macOS platforms for example.
166 if (!qobject_cast<QMenuBar *>(tlw))
167#endif
168 return false;
169 }
170
171 /* if we live in a MDI subwindow, ignore the event if we are
172 not the active document window */
173 const QWidget* sw = w;
174 while (sw && !(sw->windowType() == Qt::SubWindow) && !sw->isWindow())
175 sw = sw->parentWidget();
176 if (sw && (sw->windowType() == Qt::SubWindow)) {
177 QWidget *focus_widget = QApplication::focusWidget();
178 while (focus_widget && focus_widget != sw)
179 focus_widget = focus_widget->parentWidget();
180 return sw == focus_widget;
181 }
182
183#if defined(DEBUG_QSHORTCUTMAP)
184 qDebug().nospace() << "..true [Pass-through]";
185#endif
186 return QApplicationPrivate::tryModalHelper(w, nullptr);
187}
188
189#if QT_CONFIG(graphicsview)
190static bool correctGraphicsWidgetContext(Qt::ShortcutContext context, QGraphicsWidget *w, QWidget *active_window)
191{
192 if (!active_window)
193 return false;
194
195 bool visible = w->isVisible();
196#if defined(Q_OS_DARWIN) && QT_CONFIG(menubar)
197 if (!QCoreApplication::testAttribute(Qt::AA_DontUseNativeMenuBar) && qobject_cast<QMenuBar *>(w))
198 visible = true;
199#endif
200
201 if (!visible || !w->isEnabled() || !w->scene())
202 return false;
203
204 if (context == Qt::ApplicationShortcut) {
205 // Applicationwide shortcuts are always reachable unless their owner
206 // is shadowed by modality. In QGV there's no modality concept, but we
207 // must still check if all views are shadowed.
208 const auto &views = w->scene()->views();
209 for (auto view : views) {
210 if (QApplicationPrivate::tryModalHelper(view, nullptr))
211 return true;
212 }
213 return false;
214 }
215
216 if (context == Qt::WidgetShortcut)
217 return static_cast<QGraphicsItem *>(w) == w->scene()->focusItem();
218
219 if (context == Qt::WidgetWithChildrenShortcut) {
220 const QGraphicsItem *ti = w->scene()->focusItem();
221 if (ti && ti->isWidget()) {
222 const auto *tw = static_cast<const QGraphicsWidget *>(ti);
223 while (tw && tw != w && (tw->windowType() == Qt::Widget || tw->windowType() == Qt::Popup))
224 tw = tw->parentWidget();
225 return tw == w;
226 }
227 return false;
228 }
229
230 // Below is Qt::WindowShortcut context
231
232 // Find the active view (if any).
233 const auto &views = w->scene()->views();
234 QGraphicsView *activeView = nullptr;
235 for (auto view : views) {
236 if (view->window() == active_window) {
237 activeView = view;
238 break;
239 }
240 }
241 if (!activeView)
242 return false;
243
244 // The shortcut is reachable if owned by a windowless widget, or if the
245 // widget's window is the same as the focus item's window.
246 QGraphicsWidget *a = w->scene()->activeWindow();
247 return !w->window() || a == w->window();
248}
249#endif
250
251#if QT_CONFIG(action)
252static bool correctActionContext(Qt::ShortcutContext context, QAction *a, QWidget *active_window)
253{
254 if (!active_window)
255 return false;
256
257 const QObjectList associatedObjects = a->associatedObjects();
258#if defined(DEBUG_QSHORTCUTMAP)
259 if (associatedObjects.isEmpty())
260 qDebug() << a << "not connected to any widgets; won't trigger";
261#endif
262 for (auto object : associatedObjects) {
263#if QT_CONFIG(menu)
264 if (auto menu = qobject_cast<QMenu *>(object)) {
265#ifdef Q_OS_DARWIN
266 // On Mac, menu item shortcuts are processed before reaching any window.
267 // That means that if a menu action shortcut has not been already processed
268 // (and reaches this point), then the menu item itself has been disabled.
269 // This occurs at the QPA level on Mac, where we disable all the Cocoa menus
270 // when showing a modal window. (Notice that only the QPA menu is disabled,
271 // not the QMenu.) Since we can also reach this code by climbing the menu
272 // hierarchy (see below), or when the shortcut is not a key-equivalent, we
273 // need to check whether the QPA menu is actually disabled.
274 // When there is no QPA menu, there will be no QCocoaMenuDelegate checking
275 // for the actual shortcuts. We can then fallback to our own logic.
276 QPlatformMenu *pm = menu->platformMenu();
277 if (pm && !pm->isEnabled())
278 continue;
279#endif
280 QAction *a = menu->menuAction();
281 if (a->isVisible() && a->isEnabled() && correctActionContext(context, a, active_window))
282 return true;
283 } else
284#endif
285 if (auto widget = qobject_cast<QWidget*>(object)) {
286 if (correctWidgetContext(context, widget, active_window))
287 return true;
288 }
289#if QT_CONFIG(graphicsview)
290 else if (auto graphicsWidget = qobject_cast<QGraphicsWidget*>(object)) {
291 if (correctGraphicsWidgetContext(context, graphicsWidget, active_window))
292 return true;
293 }
294#endif
295 }
296
297 return false;
298}
299#endif // QT_CONFIG(action)
300
302{
303 Q_DECLARE_PUBLIC(QShortcut)
304public:
306
308 { return qWidgetShortcutContextMatcher; }
309
311};
312
314{
315#if QT_CONFIG(whatsthis)
316 if (QWhatsThis::inWhatsThisMode()) {
317 QWhatsThis::showText(QCursor::pos(), sc_whatsthis);
318 return true;
319 }
320#endif
321 return false;
322}
323
324QShortcutPrivate *QApplicationPrivate::createShortcutPrivate() const
325{
326 return new QtWidgetsShortcutPrivate;
327}
328
329QT_END_NAMESPACE
QShortcutMap::ContextMatcher contextMatcher() const override
static QT_BEGIN_NAMESPACE bool correctWidgetContext(Qt::ShortcutContext context, QWidget *w, QWidget *active_window)
bool qWidgetShortcutContextMatcher(QObject *object, Qt::ShortcutContext context)