1 | // Copyright (C) 2016 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 |
3 | |
4 | #include "centralwidget.h" |
5 | |
6 | #include "findwidget.h" |
7 | #include "helpenginewrapper.h" |
8 | #include "helpviewer.h" |
9 | #include "openpagesmanager.h" |
10 | #include "tracer.h" |
11 | |
12 | #include <QtCore/QRegularExpression> |
13 | #include <QtCore/QTimer> |
14 | |
15 | #include <QtGui/QKeyEvent> |
16 | #include <QtWidgets/QMenu> |
17 | #ifndef QT_NO_PRINTER |
18 | #include <QtPrintSupport/QPageSetupDialog> |
19 | #include <QtPrintSupport/QPrintDialog> |
20 | #include <QtPrintSupport/QPrintPreviewDialog> |
21 | #include <QtPrintSupport/QPrinter> |
22 | #endif |
23 | #include <QtWidgets/QStackedWidget> |
24 | #include <QtWidgets/QTextBrowser> |
25 | #include <QtWidgets/QVBoxLayout> |
26 | |
27 | #include <QtHelp/QHelpSearchEngine> |
28 | |
29 | QT_BEGIN_NAMESPACE |
30 | |
31 | namespace { |
32 | CentralWidget *staticCentralWidget = nullptr; |
33 | } |
34 | |
35 | // -- TabBar |
36 | |
37 | TabBar::TabBar(QWidget *parent) |
38 | : QTabBar(parent) |
39 | { |
40 | TRACE_OBJ |
41 | #ifdef Q_OS_MAC |
42 | setDocumentMode(true); |
43 | #endif |
44 | setMovable(true); |
45 | setShape(QTabBar::RoundedNorth); |
46 | setContextMenuPolicy(Qt::CustomContextMenu); |
47 | setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred, |
48 | QSizePolicy::TabWidget)); |
49 | connect(sender: this, signal: &QTabBar::currentChanged, |
50 | context: this, slot: &TabBar::slotCurrentChanged); |
51 | connect(sender: this, signal: &QTabBar::tabCloseRequested, |
52 | context: this, slot: &TabBar::slotTabCloseRequested); |
53 | connect(sender: this, signal: &QWidget::customContextMenuRequested, |
54 | context: this, slot: &TabBar::slotCustomContextMenuRequested); |
55 | } |
56 | |
57 | TabBar::~TabBar() |
58 | { |
59 | TRACE_OBJ |
60 | } |
61 | |
62 | int TabBar::addNewTab(const QString &title) |
63 | { |
64 | TRACE_OBJ |
65 | const int index = addTab(text: title); |
66 | setTabsClosable(count() > 1); |
67 | return index; |
68 | } |
69 | |
70 | void TabBar::setCurrent(HelpViewer *viewer) |
71 | { |
72 | TRACE_OBJ |
73 | for (int i = 0; i < count(); ++i) { |
74 | HelpViewer *data = tabData(index: i).value<HelpViewer*>(); |
75 | if (data == viewer) { |
76 | setCurrentIndex(i); |
77 | break; |
78 | } |
79 | } |
80 | } |
81 | |
82 | void TabBar::removeTabAt(HelpViewer *viewer) |
83 | { |
84 | TRACE_OBJ |
85 | for (int i = 0; i < count(); ++i) { |
86 | HelpViewer *data = tabData(index: i).value<HelpViewer*>(); |
87 | if (data == viewer) { |
88 | removeTab(index: i); |
89 | break; |
90 | } |
91 | } |
92 | setTabsClosable(count() > 1); |
93 | } |
94 | |
95 | void TabBar::titleChanged() |
96 | { |
97 | TRACE_OBJ |
98 | for (int i = 0; i < count(); ++i) { |
99 | HelpViewer *data = tabData(index: i).value<HelpViewer*>(); |
100 | QString title = data->title(); |
101 | title.replace(c: QLatin1Char('&'), after: QLatin1String("&&" )); |
102 | setTabText(index: i, text: title.isEmpty() ? tr(s: "(Untitled)" ) : title); |
103 | } |
104 | } |
105 | |
106 | void TabBar::slotCurrentChanged(int index) |
107 | { |
108 | TRACE_OBJ |
109 | emit currentTabChanged(viewer: tabData(index).value<HelpViewer*>()); |
110 | } |
111 | |
112 | void TabBar::slotTabCloseRequested(int index) |
113 | { |
114 | TRACE_OBJ |
115 | OpenPagesManager::instance()->closePage(page: tabData(index).value<HelpViewer*>()); |
116 | } |
117 | |
118 | void TabBar::(const QPoint &pos) |
119 | { |
120 | TRACE_OBJ |
121 | const int tab = tabAt(pos); |
122 | if (tab < 0) |
123 | return; |
124 | |
125 | QMenu (QString(), this); |
126 | menu.addAction(text: tr(s: "New &Tab" ), args: OpenPagesManager::instance(), |
127 | args: &OpenPagesManager::createBlankPage); |
128 | |
129 | const bool enableAction = count() > 1; |
130 | QAction *closePage = menu.addAction(text: tr(s: "&Close Tab" )); |
131 | closePage->setEnabled(enableAction); |
132 | |
133 | QAction *closePages = menu.addAction(text: tr(s: "Close Other Tabs" )); |
134 | closePages->setEnabled(enableAction); |
135 | |
136 | menu.addSeparator(); |
137 | |
138 | HelpViewer *viewer = tabData(index: tab).value<HelpViewer*>(); |
139 | QAction *newBookmark = menu.addAction(text: tr(s: "Add Bookmark for this Page..." )); |
140 | const QString &url = viewer->source().toString(); |
141 | if (url.isEmpty() || url == QLatin1String("about:blank" )) |
142 | newBookmark->setEnabled(false); |
143 | |
144 | QAction *pickedAction = menu.exec(pos: mapToGlobal(pos)); |
145 | if (pickedAction == closePage) |
146 | slotTabCloseRequested(index: tab); |
147 | else if (pickedAction == closePages) { |
148 | for (int i = count() - 1; i >= 0; --i) { |
149 | if (i != tab) |
150 | slotTabCloseRequested(index: i); |
151 | } |
152 | } else if (pickedAction == newBookmark) |
153 | emit addBookmark(title: viewer->title(), url); |
154 | } |
155 | |
156 | // -- CentralWidget |
157 | |
158 | CentralWidget::CentralWidget(QWidget *parent) |
159 | : QWidget(parent) |
160 | #ifndef QT_NO_PRINTER |
161 | , m_printer(nullptr) |
162 | #endif |
163 | , m_findWidget(new FindWidget(this)) |
164 | , m_stackedWidget(new QStackedWidget(this)) |
165 | , m_tabBar(new TabBar(this)) |
166 | { |
167 | TRACE_OBJ |
168 | staticCentralWidget = this; |
169 | QVBoxLayout *vboxLayout = new QVBoxLayout(this); |
170 | |
171 | vboxLayout->setContentsMargins(QMargins()); |
172 | vboxLayout->setSpacing(0); |
173 | vboxLayout->addWidget(m_tabBar); |
174 | m_tabBar->setVisible(HelpEngineWrapper::instance().showTabs()); |
175 | vboxLayout->addWidget(m_stackedWidget); |
176 | vboxLayout->addWidget(m_findWidget); |
177 | m_findWidget->hide(); |
178 | |
179 | connect(sender: m_findWidget, signal: &FindWidget::findNext, context: this, slot: &CentralWidget::findNext); |
180 | connect(sender: m_findWidget, signal: &FindWidget::findPrevious, context: this, slot: &CentralWidget::findPrevious); |
181 | connect(sender: m_findWidget, signal: &FindWidget::find, context: this, slot: &CentralWidget::find); |
182 | connect(sender: m_findWidget, signal: &FindWidget::escapePressed, context: this, slot: &CentralWidget::activateTab); |
183 | connect(sender: m_tabBar, signal: &TabBar::addBookmark, context: this, slot: &CentralWidget::addBookmark); |
184 | } |
185 | |
186 | CentralWidget::~CentralWidget() |
187 | { |
188 | TRACE_OBJ |
189 | QStringList zoomFactors; |
190 | QStringList currentPages; |
191 | for (int i = 0; i < m_stackedWidget->count(); ++i) { |
192 | const HelpViewer * const viewer = viewerAt(index: i); |
193 | const QUrl &source = viewer->source(); |
194 | if (source.isValid()) { |
195 | currentPages << source.toString(); |
196 | zoomFactors << QString::number(viewer->scale()); |
197 | } |
198 | } |
199 | |
200 | HelpEngineWrapper &helpEngine = HelpEngineWrapper::instance(); |
201 | helpEngine.setLastShownPages(currentPages); |
202 | helpEngine.setLastZoomFactors(zoomFactors); |
203 | helpEngine.setLastTabPage(m_stackedWidget->currentIndex()); |
204 | |
205 | #ifndef QT_NO_PRINTER |
206 | delete m_printer; |
207 | #endif |
208 | } |
209 | |
210 | CentralWidget *CentralWidget::instance() |
211 | { |
212 | TRACE_OBJ |
213 | return staticCentralWidget; |
214 | } |
215 | |
216 | QUrl CentralWidget::currentSource() const |
217 | { |
218 | TRACE_OBJ |
219 | return currentHelpViewer()->source(); |
220 | } |
221 | |
222 | QString CentralWidget::currentTitle() const |
223 | { |
224 | TRACE_OBJ |
225 | return currentHelpViewer()->title(); |
226 | } |
227 | |
228 | bool CentralWidget::hasSelection() const |
229 | { |
230 | TRACE_OBJ |
231 | return !currentHelpViewer()->selectedText().isEmpty(); |
232 | } |
233 | |
234 | bool CentralWidget::isForwardAvailable() const |
235 | { |
236 | TRACE_OBJ |
237 | return currentHelpViewer()->isForwardAvailable(); |
238 | } |
239 | |
240 | bool CentralWidget::isBackwardAvailable() const |
241 | { |
242 | TRACE_OBJ |
243 | return currentHelpViewer()->isBackwardAvailable(); |
244 | } |
245 | |
246 | HelpViewer* CentralWidget::viewerAt(int index) const |
247 | { |
248 | TRACE_OBJ |
249 | return static_cast<HelpViewer*>(m_stackedWidget->widget(index)); |
250 | } |
251 | |
252 | HelpViewer* CentralWidget::currentHelpViewer() const |
253 | { |
254 | TRACE_OBJ |
255 | return static_cast<HelpViewer *>(m_stackedWidget->currentWidget()); |
256 | } |
257 | |
258 | void CentralWidget::addPage(HelpViewer *page, bool fromSearch) |
259 | { |
260 | TRACE_OBJ |
261 | page->installEventFilter(filterObj: this); |
262 | page->setFocus(Qt::OtherFocusReason); |
263 | connectSignals(page); |
264 | const int index = m_stackedWidget->addWidget(w: page); |
265 | m_tabBar->setTabData(index: m_tabBar->addNewTab(title: page->title()), |
266 | data: QVariant::fromValue(value: viewerAt(index))); |
267 | connect(sender: page, signal: &HelpViewer::titleChanged, context: m_tabBar, slot: &TabBar::titleChanged); |
268 | |
269 | if (fromSearch) { |
270 | connect(sender: currentHelpViewer(), signal: &HelpViewer::loadFinished, |
271 | context: this, slot: &CentralWidget::highlightSearchTerms); |
272 | } |
273 | } |
274 | |
275 | void CentralWidget::removePage(int index) |
276 | { |
277 | TRACE_OBJ |
278 | const bool currentChanged = index == currentIndex(); |
279 | m_tabBar->removeTabAt(viewer: viewerAt(index)); |
280 | m_stackedWidget->removeWidget(w: m_stackedWidget->widget(index)); |
281 | if (currentChanged) |
282 | emit currentViewerChanged(); |
283 | } |
284 | |
285 | int CentralWidget::currentIndex() const |
286 | { |
287 | TRACE_OBJ |
288 | return m_stackedWidget->currentIndex(); |
289 | } |
290 | |
291 | void CentralWidget::setCurrentPage(HelpViewer *page) |
292 | { |
293 | TRACE_OBJ |
294 | m_tabBar->setCurrent(page); |
295 | m_stackedWidget->setCurrentWidget(page); |
296 | emit currentViewerChanged(); |
297 | } |
298 | |
299 | void CentralWidget::connectTabBar() |
300 | { |
301 | TRACE_OBJ |
302 | connect(sender: m_tabBar, signal: &TabBar::currentTabChanged, context: OpenPagesManager::instance(), |
303 | slot: QOverload<HelpViewer *>::of(ptr: &OpenPagesManager::setCurrentPage)); |
304 | } |
305 | |
306 | // -- public slots |
307 | |
308 | #if QT_CONFIG(clipboard) |
309 | void CentralWidget::copy() |
310 | { |
311 | TRACE_OBJ |
312 | currentHelpViewer()->copy(); |
313 | } |
314 | #endif |
315 | |
316 | void CentralWidget::home() |
317 | { |
318 | TRACE_OBJ |
319 | currentHelpViewer()->home(); |
320 | } |
321 | |
322 | void CentralWidget::zoomIn() |
323 | { |
324 | TRACE_OBJ |
325 | currentHelpViewer()->scaleUp(); |
326 | } |
327 | |
328 | void CentralWidget::zoomOut() |
329 | { |
330 | TRACE_OBJ |
331 | currentHelpViewer()->scaleDown(); |
332 | } |
333 | |
334 | void CentralWidget::resetZoom() |
335 | { |
336 | TRACE_OBJ |
337 | currentHelpViewer()->resetScale(); |
338 | } |
339 | |
340 | void CentralWidget::forward() |
341 | { |
342 | TRACE_OBJ |
343 | currentHelpViewer()->forward(); |
344 | } |
345 | |
346 | void CentralWidget::nextPage() |
347 | { |
348 | TRACE_OBJ |
349 | m_stackedWidget->setCurrentIndex((m_stackedWidget->currentIndex() + 1) |
350 | % m_stackedWidget->count()); |
351 | } |
352 | |
353 | void CentralWidget::backward() |
354 | { |
355 | TRACE_OBJ |
356 | currentHelpViewer()->backward(); |
357 | } |
358 | |
359 | void CentralWidget::previousPage() |
360 | { |
361 | TRACE_OBJ |
362 | m_stackedWidget->setCurrentIndex((m_stackedWidget->currentIndex() - 1) |
363 | % m_stackedWidget->count()); |
364 | } |
365 | |
366 | void CentralWidget::print() |
367 | { |
368 | TRACE_OBJ |
369 | #if !defined(QT_NO_PRINTER) && !defined(QT_NO_PRINTDIALOG) |
370 | initPrinter(); |
371 | QPrintDialog dlg(m_printer, this); |
372 | |
373 | if (!currentHelpViewer()->selectedText().isEmpty()) |
374 | dlg.setOption(option: QAbstractPrintDialog::PrintSelection); |
375 | dlg.setOption(option: QAbstractPrintDialog::PrintPageRange); |
376 | dlg.setOption(option: QAbstractPrintDialog::PrintCollateCopies); |
377 | dlg.setWindowTitle(tr(s: "Print Document" )); |
378 | if (dlg.exec() == QDialog::Accepted) |
379 | currentHelpViewer()->print(printer: m_printer); |
380 | #endif |
381 | } |
382 | |
383 | void CentralWidget::pageSetup() |
384 | { |
385 | TRACE_OBJ |
386 | #if !defined(QT_NO_PRINTER) && !defined(QT_NO_PRINTDIALOG) |
387 | initPrinter(); |
388 | QPageSetupDialog dlg(m_printer); |
389 | dlg.exec(); |
390 | #endif |
391 | } |
392 | |
393 | void CentralWidget::printPreview() |
394 | { |
395 | TRACE_OBJ |
396 | #if !defined(QT_NO_PRINTER) && !defined(QT_NO_PRINTDIALOG) |
397 | initPrinter(); |
398 | QPrintPreviewDialog preview(m_printer, this); |
399 | connect(sender: &preview, signal: &QPrintPreviewDialog::paintRequested, |
400 | context: this, slot: &CentralWidget::printPreviewToPrinter); |
401 | preview.exec(); |
402 | #endif |
403 | } |
404 | |
405 | void CentralWidget::setSource(const QUrl &url) |
406 | { |
407 | TRACE_OBJ |
408 | HelpViewer *viewer = currentHelpViewer(); |
409 | viewer->setSource(url); |
410 | viewer->setFocus(Qt::OtherFocusReason); |
411 | } |
412 | |
413 | void CentralWidget::setSourceFromSearch(const QUrl &url) |
414 | { |
415 | TRACE_OBJ |
416 | connect(sender: currentHelpViewer(), signal: &HelpViewer::loadFinished, |
417 | context: this, slot: &CentralWidget::highlightSearchTerms); |
418 | currentHelpViewer()->setSource(url); |
419 | currentHelpViewer()->setFocus(Qt::OtherFocusReason); |
420 | } |
421 | |
422 | void CentralWidget::findNext() |
423 | { |
424 | TRACE_OBJ |
425 | find(text: m_findWidget->text(), forward: true, incremental: false); |
426 | } |
427 | |
428 | void CentralWidget::findPrevious() |
429 | { |
430 | TRACE_OBJ |
431 | find(text: m_findWidget->text(), forward: false, incremental: false); |
432 | } |
433 | |
434 | void CentralWidget::find(const QString &ttf, bool forward, bool incremental) |
435 | { |
436 | TRACE_OBJ |
437 | bool found = false; |
438 | if (HelpViewer *viewer = currentHelpViewer()) { |
439 | HelpViewer::FindFlags flags; |
440 | if (!forward) |
441 | flags |= HelpViewer::FindBackward; |
442 | if (m_findWidget->caseSensitive()) |
443 | flags |= HelpViewer::FindCaseSensitively; |
444 | found = viewer->findText(text: ttf, flags, incremental, fromSearch: false); |
445 | } |
446 | |
447 | if (!found && ttf.isEmpty()) |
448 | found = true; // the line edit is empty, no need to mark it red... |
449 | |
450 | if (!m_findWidget->isVisible()) |
451 | m_findWidget->show(); |
452 | m_findWidget->setPalette(found); |
453 | } |
454 | |
455 | void CentralWidget::activateTab() |
456 | { |
457 | TRACE_OBJ |
458 | currentHelpViewer()->setFocus(); |
459 | } |
460 | |
461 | void CentralWidget::showTextSearch() |
462 | { |
463 | TRACE_OBJ |
464 | m_findWidget->show(); |
465 | } |
466 | |
467 | void CentralWidget::updateBrowserFont() |
468 | { |
469 | TRACE_OBJ |
470 | const int count = m_stackedWidget->count(); |
471 | const QFont &font = viewerAt(index: count - 1)->viewerFont(); |
472 | for (int i = 0; i < count; ++i) |
473 | viewerAt(index: i)->setViewerFont(font); |
474 | } |
475 | |
476 | void CentralWidget::updateUserInterface() |
477 | { |
478 | m_tabBar->setVisible(HelpEngineWrapper::instance().showTabs()); |
479 | } |
480 | |
481 | // -- protected |
482 | |
483 | void CentralWidget::keyPressEvent(QKeyEvent *e) |
484 | { |
485 | TRACE_OBJ |
486 | const QString &text = e->text(); |
487 | if (text.startsWith(c: QLatin1Char('/'))) { |
488 | if (!m_findWidget->isVisible()) { |
489 | m_findWidget->showAndClear(); |
490 | } else { |
491 | m_findWidget->show(); |
492 | } |
493 | } else { |
494 | QWidget::keyPressEvent(event: e); |
495 | } |
496 | } |
497 | |
498 | void CentralWidget::focusInEvent(QFocusEvent * /* event */) |
499 | { |
500 | TRACE_OBJ |
501 | // If we have a current help viewer then this is the 'focus proxy', |
502 | // otherwise it's the central widget. This is needed, so an embedding |
503 | // program can just set the focus to the central widget and it does |
504 | // The Right Thing(TM) |
505 | QWidget *receiver = m_stackedWidget; |
506 | if (HelpViewer *viewer = currentHelpViewer()) |
507 | receiver = viewer; |
508 | QTimer::singleShot(interval: 1, receiver, |
509 | slot: QOverload<>::of(ptr: &QWidget::setFocus)); |
510 | } |
511 | |
512 | // -- private slots |
513 | |
514 | void CentralWidget::highlightSearchTerms() |
515 | { |
516 | TRACE_OBJ |
517 | QHelpSearchEngine *searchEngine = |
518 | HelpEngineWrapper::instance().searchEngine(); |
519 | const QString searchInput = searchEngine->searchInput(); |
520 | const bool wholePhrase = searchInput.startsWith(c: QLatin1Char('"')) && |
521 | searchInput.endsWith(c: QLatin1Char('"')); |
522 | const QStringList &words = wholePhrase ? QStringList(searchInput.mid(position: 1, n: searchInput.size() - 2)) : |
523 | searchInput.split(sep: QRegularExpression("\\W+" ), behavior: Qt::SkipEmptyParts); |
524 | HelpViewer *viewer = currentHelpViewer(); |
525 | for (const QString &word : words) |
526 | viewer->findText(text: word, flags: {}, incremental: false, fromSearch: true); |
527 | disconnect(sender: viewer, signal: &HelpViewer::loadFinished, |
528 | receiver: this, slot: &CentralWidget::highlightSearchTerms); |
529 | } |
530 | |
531 | void CentralWidget::printPreviewToPrinter(QPrinter *p) |
532 | { |
533 | TRACE_OBJ |
534 | #ifndef QT_NO_PRINTER |
535 | currentHelpViewer()->print(printer: p); |
536 | #endif |
537 | } |
538 | |
539 | void CentralWidget::handleSourceChanged(const QUrl &url) |
540 | { |
541 | TRACE_OBJ |
542 | if (sender() == currentHelpViewer()) |
543 | emit sourceChanged(url); |
544 | } |
545 | |
546 | void CentralWidget::slotHighlighted(const QUrl &link) |
547 | { |
548 | TRACE_OBJ |
549 | QUrl resolvedLink = m_resolvedLinks.value(key: link); |
550 | if (!link.isEmpty() && resolvedLink.isEmpty()) { |
551 | resolvedLink = HelpEngineWrapper::instance().findFile(url: link); |
552 | m_resolvedLinks.insert(key: link, value: resolvedLink); |
553 | } |
554 | emit highlighted(link: resolvedLink); |
555 | } |
556 | |
557 | // -- private |
558 | |
559 | void CentralWidget::initPrinter() |
560 | { |
561 | TRACE_OBJ |
562 | #ifndef QT_NO_PRINTER |
563 | if (!m_printer) |
564 | m_printer = new QPrinter(QPrinter::HighResolution); |
565 | #endif |
566 | } |
567 | |
568 | void CentralWidget::connectSignals(HelpViewer *page) |
569 | { |
570 | TRACE_OBJ |
571 | #if defined(BROWSER_QTWEBKIT) |
572 | connect(page, &HelpViewer::printRequested, |
573 | this, &CentralWidget::print); |
574 | #endif |
575 | #if QT_CONFIG(clipboard) |
576 | connect(sender: page, signal: &HelpViewer::copyAvailable, |
577 | context: this, slot: &CentralWidget::copyAvailable); |
578 | #endif |
579 | connect(sender: page, signal: &HelpViewer::forwardAvailable, |
580 | context: this, slot: &CentralWidget::forwardAvailable); |
581 | connect(sender: page, signal: &HelpViewer::backwardAvailable, |
582 | context: this, slot: &CentralWidget::backwardAvailable); |
583 | connect(sender: page, signal: &HelpViewer::sourceChanged, |
584 | context: this, slot: &CentralWidget::handleSourceChanged); |
585 | connect(sender: page, signal: QOverload<const QUrl &>::of(ptr: &HelpViewer::highlighted), |
586 | context: this, slot: &CentralWidget::slotHighlighted); |
587 | } |
588 | |
589 | bool CentralWidget::eventFilter(QObject *object, QEvent *e) |
590 | { |
591 | TRACE_OBJ |
592 | if (e->type() != QEvent::KeyPress) |
593 | return QWidget::eventFilter(watched: object, event: e); |
594 | |
595 | HelpViewer *viewer = currentHelpViewer(); |
596 | QKeyEvent *keyEvent = static_cast<QKeyEvent*> (e); |
597 | if (viewer == object && keyEvent->key() == Qt::Key_Backspace) { |
598 | if (viewer->isBackwardAvailable()) { |
599 | #if defined(BROWSER_QTWEBKIT) |
600 | // this helps in case there is an html <input> field |
601 | if (!viewer->hasFocus()) |
602 | #endif // BROWSER_QTWEBKIT |
603 | viewer->backward(); |
604 | } |
605 | } |
606 | return QWidget::eventFilter(watched: object, event: e); |
607 | } |
608 | |
609 | QT_END_NAMESPACE |
610 | |