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
qmdisubwindow.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/*!
6 \class QMdiSubWindow
7 \brief The QMdiSubWindow class provides a subwindow class for
8 QMdiArea.
9 \since 4.3
10 \ingroup mainwindow-classes
11 \inmodule QtWidgets
12
13 QMdiSubWindow represents a top-level window in a QMdiArea, and consists
14 of a title bar with window decorations, an internal widget, and
15 (depending on the current style) a window frame and a size
16 grip. QMdiSubWindow has its own layout, which consists of the
17 title bar and a center area for the internal widget.
18
19 \image qmdisubwindowlayout.png
20 {MDI window with a title bar and an internal widget}
21
22 The most common way to construct a QMdiSubWindow is to call
23 QMdiArea::addSubWindow() with the internal widget as the argument.
24 You can also create a subwindow yourself, and set an internal
25 widget by calling setWidget().
26
27 You use the same API when programming with subwindows as with
28 regular top-level windows (e.g., you can call functions such as
29 show(), hide(), showMaximized(), and setWindowTitle()).
30
31 \section1 Subwindow Handling
32
33 QMdiSubWindow also supports behavior specific to subwindows in
34 an MDI area.
35
36 By default, each QMdiSubWindow is visible inside the MDI area
37 viewport when moved around, but it is also possible to specify
38 transparent window movement and resizing behavior, where only
39 the outline of a subwindow is updated during these operations.
40 The setOption() function is used to enable this behavior.
41
42 The isShaded() function detects whether the subwindow is
43 currently shaded (i.e., the window is collapsed so that only the
44 title bar is visible). To enter shaded mode, call showShaded().
45 QMdiSubWindow emits the windowStateChanged() signal whenever the
46 window state has changed (e.g., when the window becomes minimized,
47 or is restored). It also emits aboutToActivate() before it is
48 activated.
49
50 In keyboard-interactive mode, the windows are moved and resized
51 with the keyboard. You can enter this mode through the system menu
52 of the window. The keyboardSingleStep and keyboardPageStep
53 properties control the distance the widget is moved or resized for
54 each keypress event. When shift is pressed down page step is used;
55 otherwise single step is used.
56
57 You can also change the active window with the keyboard. By
58 pressing the control and tab keys at the same time, the next
59 (using the current \l{QMdiArea::}{WindowOrder}) subwindow will be
60 activated. By pressing control, shift, and tab, you will activate
61 the previous window. This is equivalent to calling
62 \l{QMdiArea::}{activateNextSubWindow()} and
63 \l{QMdiArea::}{activatePreviousSubWindow()}. Note that these
64 shortcuts overrides global shortcuts, but not the \l{QMdiArea}s
65 shortcuts.
66
67 \sa QMdiArea
68*/
69
70/*!
71 \enum QMdiSubWindow::SubWindowOption
72
73 This enum describes options that customize the behavior
74 of QMdiSubWindow.
75
76 \omitvalue AllowOutsideAreaHorizontally
77 \omitvalue AllowOutsideAreaVertically
78
79 \value RubberBandResize If you enable this option, a rubber band
80 control is used to represent the subwindow's outline, and the user
81 resizes this instead of the subwindow itself.
82 As a result, the subwindow maintains its original position and size
83 until the resize operation has been completed, at which time it will
84 receive a single QResizeEvent.
85 By default, this option is disabled.
86
87 \value RubberBandMove If you enable this option, a rubber band
88 control is used to represent the subwindow's outline, and the user
89 moves this instead of the subwindow itself.
90 As a result, the subwindow remains in its original position until
91 the move operation has completed, at which time a QMoveEvent is
92 sent to the window. By default, this option is disabled.
93*/
94
95/*!
96 \fn QMdiSubWindow::windowStateChanged(Qt::WindowStates oldState, Qt::WindowStates newState)
97
98 QMdiSubWindow emits this signal after the window state changes. \a
99 oldState is the window state before it changed, and \a newState is the
100 new, current state.
101*/
102
103/*!
104 \fn QMdiSubWindow::aboutToActivate()
105
106 QMdiSubWindow emits this signal immediately before it is
107 activated. After the subwindow has been activated, the QMdiArea that
108 manages the subwindow will also emit the
109 \l{QMdiArea::}{subWindowActivated()} signal.
110
111 \sa QMdiArea::subWindowActivated()
112*/
113
114#include "qmdisubwindow_p.h"
115
116#include <QApplication>
117#include <QStylePainter>
118#include <QVBoxLayout>
119#include <QMouseEvent>
120#if QT_CONFIG(whatsthis)
121#include <QWhatsThis>
122#endif
123#if QT_CONFIG(tooltip)
124#include <QToolTip>
125#endif
126#if QT_CONFIG(mainwindow)
127#include <QMainWindow>
128#endif
129#include <QScrollBar>
130#include <QDebug>
131#include <QMdiArea>
132#include <QScopedValueRollback>
133#if QT_CONFIG(action)
134# include <qaction.h>
135#endif
136#if QT_CONFIG(menu)
137#include <QMenu>
138#endif
139#include <QProxyStyle>
140
142
143using namespace Qt::StringLiterals;
144using namespace std::chrono_literals;
145
146using namespace QMdi;
147
149{
150 QStyle::SC_TitleBarLabel, // 1
151 QStyle::SC_TitleBarSysMenu, // 2
152 QStyle::SC_TitleBarMinButton, // 3
153 QStyle::SC_TitleBarMaxButton, // 4
154 QStyle::SC_TitleBarShadeButton, // 5
155 QStyle::SC_TitleBarCloseButton, // 6
156 QStyle::SC_TitleBarNormalButton, // 7
157 QStyle::SC_TitleBarUnshadeButton, // 8
158 QStyle::SC_TitleBarContextHelpButton // 9
159};
160static const int NumSubControls = sizeof(SubControls) / sizeof(SubControls[0]);
161
163 Qt::FramelessWindowHint
164 | Qt::CustomizeWindowHint
165 | Qt::WindowTitleHint
166 | Qt::WindowSystemMenuHint
167 | Qt::WindowMinimizeButtonHint
168 | Qt::WindowMaximizeButtonHint
169 | Qt::WindowMinMaxButtonsHint;
170
171
172static const int BoundaryMargin = 5;
173
174static inline bool isMacStyle(QStyle *style)
175{
176 auto proxyStyle = qobject_cast<QProxyStyle *>(style);
177 auto styleToCheck = proxyStyle ? proxyStyle->baseStyle() : style;
178 return styleToCheck->inherits("QMacStyle");
179}
180
181static inline int getMoveDeltaComponent(uint cflags, uint moveFlag, uint resizeFlag,
182 int delta, int maxDelta, int minDelta)
183{
184 if (cflags & moveFlag) {
185 if (delta > 0)
186 return (cflags & resizeFlag) ? qMin(delta, maxDelta) : delta;
187 return (cflags & resizeFlag) ? qMax(delta, minDelta) : delta;
188 }
189 return 0;
190}
191
192static inline int getResizeDeltaComponent(uint cflags, uint resizeFlag,
193 uint resizeReverseFlag, int delta)
194{
195 if (cflags & resizeFlag) {
196 if (cflags & resizeReverseFlag)
197 return -delta;
198 return delta;
199 }
200 return 0;
201}
202
203static inline bool isChildOfQMdiSubWindow(const QWidget *child)
204{
205 Q_ASSERT(child);
206 QWidget *parent = child->parentWidget();
207 while (parent) {
208 if (qobject_cast<QMdiSubWindow *>(parent))
209 return true;
210 parent = parent->parentWidget();
211 }
212 return false;
213}
214
215static inline bool isChildOfTabbedQMdiArea(const QMdiSubWindow *child)
216{
217 Q_ASSERT(child);
218 if (QMdiArea *mdiArea = child->mdiArea()) {
219 if (mdiArea->viewMode() == QMdiArea::TabbedView)
220 return true;
221 }
222 return false;
223}
224
225template<typename T>
226static inline ControlElement<T> *ptr(QWidget *widget)
227{
228 if (widget && widget->qt_metacast("ControlElement")
229 && strcmp(widget->metaObject()->className(), T::staticMetaObject.className()) == 0) {
230 return static_cast<ControlElement<T> *>(widget);
231 }
232 return nullptr;
233}
234
235QString QMdiSubWindowPrivate::originalWindowTitleHelper() const
236{
237 Q_Q(const QMdiSubWindow);
238 // QTBUG-92240: When DontMaximizeSubWindowOnActivation is set and
239 // there is another subwindow maximized, use its original title.
240 if (auto *mdiArea = q->mdiArea()) {
241 const auto &subWindows = mdiArea->subWindowList();
242 for (auto *subWindow : subWindows) {
243 if (subWindow != q && subWindow->isMaximized()) {
244 auto *subWindowD = static_cast<QMdiSubWindowPrivate *>(qt_widget_private(subWindow));
245 if (!subWindowD->originalTitle.isNull())
246 return subWindowD->originalTitle;
247 }
248 }
249 }
250 return q->window()->windowTitle();
251}
252
253QString QMdiSubWindowPrivate::originalWindowTitle()
254{
255 if (originalTitle.isNull()) {
256 originalTitle = originalWindowTitleHelper();
257 if (originalTitle.isNull())
258 originalTitle = ""_L1;
259 }
260 return originalTitle;
261}
262
263void QMdiSubWindowPrivate::setNewWindowTitle()
264{
265 Q_Q(QMdiSubWindow);
266 QString childTitle = q->windowTitle();
267 if (childTitle.isEmpty())
268 return;
269 QString original = originalWindowTitle();
270 if (!original.isEmpty()) {
271 if (!original.contains(QMdiSubWindow::tr("- [%1]").arg(childTitle))) {
272 auto title = QMdiSubWindow::tr("%1 - [%2]").arg(original, childTitle);
273 ignoreWindowTitleChange = true;
274 q->window()->setWindowTitle(title);
275 ignoreWindowTitleChange = false;
276 }
277
278 } else {
279 ignoreWindowTitleChange = true;
280 q->window()->setWindowTitle(childTitle);
281 ignoreWindowTitleChange = false;
282 }
283}
284
285static inline bool isHoverControl(QStyle::SubControl control)
286{
287 return control != QStyle::SC_None && control != QStyle::SC_TitleBarLabel;
288}
289
290#if QT_CONFIG(tooltip)
291static void showToolTip(QHelpEvent *helpEvent, QWidget *widget, const QStyleOptionComplex &opt,
292 QStyle::ComplexControl complexControl, QStyle::SubControl subControl)
293{
294 Q_ASSERT(helpEvent);
295 Q_ASSERT(helpEvent->type() == QEvent::ToolTip);
296 Q_ASSERT(widget);
297
298 if (widget->style()->styleHint(QStyle::SH_TitleBar_ShowToolTipsOnButtons, &opt, widget))
299 return;
300
301 // Convert CC_MdiControls to CC_TitleBar. Sub controls of different complex
302 // controls cannot be in the same switch as they might have the same value.
303 if (complexControl == QStyle::CC_MdiControls) {
304 if (subControl == QStyle::SC_MdiMinButton)
305 subControl = QStyle::SC_TitleBarMinButton;
306 else if (subControl == QStyle::SC_MdiCloseButton)
307 subControl = QStyle::SC_TitleBarCloseButton;
308 else if (subControl == QStyle::SC_MdiNormalButton)
309 subControl = QStyle::SC_TitleBarNormalButton;
310 else
311 subControl = QStyle::SC_None;
312 }
313
314 // Don't change the tooltip for the base widget itself.
315 if (subControl == QStyle::SC_None)
316 return;
317
318 QString toolTip;
319
320 switch (subControl) {
321 case QStyle::SC_TitleBarMinButton:
322 toolTip = QMdiSubWindow::tr("Minimize");
323 break;
324 case QStyle::SC_TitleBarMaxButton:
325 toolTip = QMdiSubWindow::tr("Maximize");
326 break;
327 case QStyle::SC_TitleBarUnshadeButton:
328 toolTip = QMdiSubWindow::tr("Unshade");
329 break;
330 case QStyle::SC_TitleBarShadeButton:
331 toolTip = QMdiSubWindow::tr("Shade");
332 break;
333 case QStyle::SC_TitleBarNormalButton:
334 if (widget->isMaximized() || !qobject_cast<QMdiSubWindow *>(widget))
335 toolTip = QMdiSubWindow::tr("Restore Down");
336 else
337 toolTip = QMdiSubWindow::tr("Restore");
338 break;
339 case QStyle::SC_TitleBarCloseButton:
340 toolTip = QMdiSubWindow::tr("Close");
341 break;
342 case QStyle::SC_TitleBarContextHelpButton:
343 toolTip = QMdiSubWindow::tr("Help");
344 break;
345 case QStyle::SC_TitleBarSysMenu:
346 toolTip = QMdiSubWindow::tr("Menu");
347 break;
348 default:
349 break;
350 }
351
352 const QRect rect = widget->style()->subControlRect(complexControl, &opt, subControl, widget);
353 QToolTip::showText(helpEvent->globalPos(), toolTip, widget, rect);
354}
355#endif // QT_CONFIG(tooltip)
356
357namespace QMdi {
358/*
359 \class ControlLabel
360 \internal
361*/
362class ControlLabel : public QWidget
363{
365public:
367
368 QSize sizeHint() const override;
369
370signals:
373
374protected:
375 bool event(QEvent *event) override;
376 void paintEvent(QPaintEvent *paintEvent) override;
377 void mousePressEvent(QMouseEvent *mouseEvent) override;
378 void mouseDoubleClickEvent(QMouseEvent *mouseEvent) override;
379 void mouseReleaseEvent(QMouseEvent *mouseEvent) override;
380
381private:
382 QPixmap label;
383 bool isPressed;
384 void updateWindowIcon();
385};
386} // namespace QMdi
387
388ControlLabel::ControlLabel(QWidget *parent)
389 : QWidget(parent), isPressed(false)
390{
391 setFocusPolicy(Qt::NoFocus);
392 updateWindowIcon();
393 setFixedSize(label.deviceIndependentSize().toSize());
394}
395
396/*
397 \internal
398*/
400{
401 return label.deviceIndependentSize().toSize();
402}
403
404/*
405 \internal
406*/
407bool ControlLabel::event(QEvent *event)
408{
409 if (event->type() == QEvent::WindowIconChange)
410 updateWindowIcon();
411 else if (event->type() == QEvent::StyleChange) {
412 updateWindowIcon();
413 setFixedSize(label.size());
414 }
415#if QT_CONFIG(tooltip)
416 else if (event->type() == QEvent::ToolTip) {
417 QStyleOptionTitleBar options;
418 options.initFrom(this);
419 showToolTip(static_cast<QHelpEvent *>(event), this, options,
420 QStyle::CC_TitleBar, QStyle::SC_TitleBarSysMenu);
421 }
422#endif
423 return QWidget::event(event);
424}
425
426/*
427 \internal
428*/
429void ControlLabel::paintEvent(QPaintEvent * /*paintEvent*/)
430{
431 QPainter painter(this);
432 painter.drawPixmap(0, 0, label);
433}
434
435/*
436 \internal
437*/
438void ControlLabel::mousePressEvent(QMouseEvent *mouseEvent)
439{
440 if (mouseEvent->button() != Qt::LeftButton) {
441 mouseEvent->ignore();
442 return;
443 }
444 isPressed = true;
445}
446
447/*
448 \internal
449*/
450void ControlLabel::mouseDoubleClickEvent(QMouseEvent *mouseEvent)
451{
452 if (mouseEvent->button() != Qt::LeftButton) {
453 mouseEvent->ignore();
454 return;
455 }
456 isPressed = false;
457 emit _q_doubleClicked();
458}
459
460/*
461 \internal
462*/
463void ControlLabel::mouseReleaseEvent(QMouseEvent *mouseEvent)
464{
465 if (mouseEvent->button() != Qt::LeftButton) {
466 mouseEvent->ignore();
467 return;
468 }
469 if (isPressed) {
470 isPressed = false;
471 emit _q_clicked();
472 }
473}
474
475/*
476 \internal
477*/
478void ControlLabel::updateWindowIcon()
479{
480 QIcon menuIcon = windowIcon();
481 if (menuIcon.isNull())
482 menuIcon = style()->standardIcon(QStyle::SP_TitleBarMenuButton, nullptr, parentWidget());
483 const int iconSize = style()->pixelMetric(QStyle::PM_TitleBarButtonIconSize, nullptr, parentWidget());
484 label = menuIcon.pixmap(QSize(iconSize, iconSize), devicePixelRatio());
485 update();
486}
487
488namespace QMdi {
489/*
490 \class ControllerWidget
491 \internal
492*/
494{
496public:
498 QSize sizeHint() const override;
499 void setControlVisible(QMdiSubWindowPrivate::WindowStateAction action, bool visible);
500 inline bool hasVisibleControls() const
501 {
502 return (visibleControls & QStyle::SC_MdiMinButton)
503 || (visibleControls & QStyle::SC_MdiNormalButton)
504 || (visibleControls & QStyle::SC_MdiCloseButton);
505 }
506
507signals:
510 void _q_close();
511
512protected:
513 void paintEvent(QPaintEvent *event) override;
514 void mousePressEvent(QMouseEvent *event) override;
515 void mouseReleaseEvent(QMouseEvent *event) override;
516 void mouseMoveEvent(QMouseEvent *event) override;
517 void leaveEvent(QEvent *event) override;
518 bool event(QEvent *event) override;
519
520private:
521 QStyle::SubControl activeControl;
522 QStyle::SubControl hoverControl;
523 QStyle::SubControls visibleControls;
524 void initStyleOption(QStyleOptionComplex *option) const;
525 inline QStyle::SubControl getSubControl(const QPoint &pos) const
526 {
527 QStyleOptionComplex opt;
528 initStyleOption(&opt);
529 return style()->hitTestComplexControl(QStyle::CC_MdiControls, &opt, pos, this);
530 }
531};
532} // namespace QMdi
533
534/*
535 \internal
536*/
537ControllerWidget::ControllerWidget(QWidget *parent)
538 : QWidget(parent),
539 activeControl(QStyle::SC_None),
540 hoverControl(QStyle::SC_None),
541 visibleControls(QStyle::SC_None)
542{
543 setFocusPolicy(Qt::NoFocus);
544 setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
545 setMouseTracking(true);
546}
547
548/*
549 \internal
550*/
552{
553 ensurePolished();
554 QStyleOptionComplex opt;
555 initStyleOption(&opt);
556 const int buttonSize = style()->pixelMetric(QStyle::PM_TitleBarButtonSize, &opt, this);
557 QSize size(3 * buttonSize, buttonSize);
558 return style()->sizeFromContents(QStyle::CT_MdiControls, &opt, size, this);
559}
560
561void ControllerWidget::setControlVisible(QMdiSubWindowPrivate::WindowStateAction action, bool visible)
562{
563 QStyle::SubControl subControl = QStyle::SC_None;
564
565 // Map action from QMdiSubWindowPrivate::WindowStateAction to QStyle::SubControl.
566 if (action == QMdiSubWindowPrivate::MaximizeAction)
567 subControl = QStyle::SC_MdiNormalButton;
568 else if (action == QMdiSubWindowPrivate::CloseAction)
569 subControl = QStyle::SC_MdiCloseButton;
570 else if (action == QMdiSubWindowPrivate::MinimizeAction)
571 subControl = QStyle::SC_MdiMinButton;
572
573 if (subControl == QStyle::SC_None)
574 return;
575
576 visibleControls.setFlag(subControl, visible && !(visibleControls & subControl));
577}
578
579/*
580 \internal
581*/
582void ControllerWidget::paintEvent(QPaintEvent * /*paintEvent*/)
583{
584 QStyleOptionComplex opt;
585 initStyleOption(&opt);
586 if (activeControl == hoverControl) {
587 opt.activeSubControls = activeControl;
588 opt.state |= QStyle::State_Sunken;
589 } else if (hoverControl != QStyle::SC_None && (activeControl == QStyle::SC_None)) {
590 opt.activeSubControls = hoverControl;
591 opt.state |= QStyle::State_MouseOver;
592 }
593 QPainter painter(this);
594 style()->drawComplexControl(QStyle::CC_MdiControls, &opt, &painter, this);
595}
596
597/*
598 \internal
599*/
600void ControllerWidget::mousePressEvent(QMouseEvent *event)
601{
602 if (event->button() != Qt::LeftButton) {
603 event->ignore();
604 return;
605 }
606 activeControl = getSubControl(event->position().toPoint());
607 update();
608}
609
610/*
611 \internal
612*/
613void ControllerWidget::mouseReleaseEvent(QMouseEvent *event)
614{
615 if (event->button() != Qt::LeftButton) {
616 event->ignore();
617 return;
618 }
619
620 QStyle::SubControl under_mouse = getSubControl(event->position().toPoint());
621 if (under_mouse == activeControl) {
622 switch (activeControl) {
623 case QStyle::SC_MdiCloseButton:
624 emit _q_close();
625 break;
626 case QStyle::SC_MdiNormalButton:
627 emit _q_restore();
628 break;
629 case QStyle::SC_MdiMinButton:
630 emit _q_minimize();
631 break;
632 default:
633 break;
634 }
635 }
636
637 activeControl = QStyle::SC_None;
638 update();
639}
640
641/*
642 \internal
643*/
644void ControllerWidget::mouseMoveEvent(QMouseEvent *event)
645{
646 QStyle::SubControl under_mouse = getSubControl(event->position().toPoint());
647 //test if hover state changes
648 if (hoverControl != under_mouse) {
649 hoverControl = under_mouse;
650 update();
651 }
652}
653
654/*
655 \internal
656*/
657void ControllerWidget::leaveEvent(QEvent * /*event*/)
658{
659 hoverControl = QStyle::SC_None;
660 update();
661}
662
663/*
664 \internal
665*/
666bool ControllerWidget::event(QEvent *event)
667{
668#if QT_CONFIG(tooltip)
669 if (event->type() == QEvent::ToolTip) {
670 QStyleOptionComplex opt;
671 initStyleOption(&opt);
672 QHelpEvent *helpEvent = static_cast<QHelpEvent *>(event);
673 showToolTip(helpEvent, this, opt, QStyle::CC_MdiControls, getSubControl(helpEvent->pos()));
674 }
675#endif // QT_CONFIG(tooltip)
676 return QWidget::event(event);
677}
678
679/*
680 \internal
681*/
682void ControllerWidget::initStyleOption(QStyleOptionComplex *option) const
683{
684 option->initFrom(this);
685 option->subControls = visibleControls;
686 option->activeSubControls = QStyle::SC_None;
687}
688
689/*
690 \internal
691*/
692ControlContainer::ControlContainer(QMdiSubWindow *mdiChild)
693 : QObject(mdiChild),
694 previousLeft(nullptr),
695 previousRight(nullptr),
696#if QT_CONFIG(menubar)
697 m_menuBar(nullptr),
698#endif
700{
701 Q_ASSERT(mdiChild);
702
703 m_controllerWidget = new ControlElement<ControllerWidget>(mdiChild);
704 connect(m_controllerWidget, SIGNAL(_q_close()), mdiChild, SLOT(close()));
705 connect(m_controllerWidget, SIGNAL(_q_restore()), mdiChild, SLOT(showNormal()));
706 connect(m_controllerWidget, SIGNAL(_q_minimize()), mdiChild, SLOT(showMinimized()));
707
708 m_menuLabel = new ControlElement<ControlLabel>(mdiChild);
709 m_menuLabel->setWindowIcon(mdiChild->windowIcon());
710#if QT_CONFIG(menu)
711 connect(m_menuLabel, SIGNAL(_q_clicked()), mdiChild, SLOT(showSystemMenu()));
712#endif
713 connect(m_menuLabel, SIGNAL(_q_doubleClicked()), mdiChild, SLOT(close()));
714}
715
717{
718#if QT_CONFIG(menubar)
719 removeButtonsFromMenuBar();
720#endif
721 delete m_menuLabel;
722 m_menuLabel = nullptr;
723 delete m_controllerWidget;
724 m_controllerWidget = nullptr;
725}
726
727#if QT_CONFIG(menubar)
728/*
729 \internal
730*/
731QMenuBar *QMdiSubWindowPrivate::menuBar() const
732{
733#if !QT_CONFIG(mainwindow)
734 return nullptr;
735#else
736 Q_Q(const QMdiSubWindow);
737 if (!q->isMaximized() || drawTitleBarWhenMaximized() || isChildOfTabbedQMdiArea(q))
738 return nullptr;
739
740 if (QMainWindow *mainWindow = qobject_cast<QMainWindow *>(q->window()))
741 return mainWindow->menuBar();
742
743 return nullptr;
744#endif
745}
746
747/*
748 \internal
749*/
750void ControlContainer::showButtonsInMenuBar(QMenuBar *menuBar)
751{
752 if (!menuBar || !mdiChild || mdiChild->windowFlags() & Qt::FramelessWindowHint)
753 return;
754 m_menuBar = menuBar;
755
756 if (m_menuLabel && mdiChild->windowFlags() & Qt::WindowSystemMenuHint) {
757 QWidget *currentLeft = menuBar->cornerWidget(Qt::TopLeftCorner);
758 if (currentLeft)
759 currentLeft->hide();
760 if (currentLeft != m_menuLabel) {
761 menuBar->setCornerWidget(m_menuLabel, Qt::TopLeftCorner);
762 previousLeft = currentLeft;
763 }
764 m_menuLabel->show();
765 }
766 ControllerWidget *controllerWidget = qobject_cast<ControllerWidget *>(m_controllerWidget);
767 if (controllerWidget && controllerWidget->hasVisibleControls()) {
768 QWidget *currentRight = menuBar->cornerWidget(Qt::TopRightCorner);
769 if (currentRight)
770 currentRight->hide();
771 if (currentRight != m_controllerWidget) {
772 menuBar->setCornerWidget(m_controllerWidget, Qt::TopRightCorner);
773 previousRight = currentRight;
774 }
775 m_controllerWidget->show();
776 }
777 mdiChild->d_func()->setNewWindowTitle();
778}
779
780/*
781 \internal
782*/
783void ControlContainer::removeButtonsFromMenuBar(QMenuBar *menuBar)
784{
785 if (menuBar && menuBar != m_menuBar) {
786 // m_menubar was deleted while sub-window was maximized
787 previousRight = nullptr;
788 previousLeft = nullptr;
789 m_menuBar = menuBar;
790 }
791
792 if (!m_menuBar || !mdiChild || qt_widget_private(mdiChild->window())->data.in_destructor)
793 return;
794
795 QMdiSubWindow *child = nullptr;
796 if (m_controllerWidget) {
797 QWidget *currentRight = m_menuBar->cornerWidget(Qt::TopRightCorner);
798 if (currentRight == m_controllerWidget) {
799 if (ControlElement<ControllerWidget> *ce = ptr<ControllerWidget>(previousRight)) {
800 if (!ce->mdiChild || !ce->mdiChild->isMaximized())
801 previousRight = nullptr;
802 else
803 child = ce->mdiChild;
804 }
805 m_menuBar->setCornerWidget(previousRight, Qt::TopRightCorner);
806 if (previousRight) {
807 previousRight->show();
808 previousRight = nullptr;
809 }
810 }
811 m_controllerWidget->hide();
812 m_controllerWidget->setParent(nullptr);
813 }
814 if (m_menuLabel) {
815 QWidget *currentLeft = m_menuBar->cornerWidget(Qt::TopLeftCorner);
816 if (currentLeft == m_menuLabel) {
817 if (ControlElement<ControlLabel> *ce = ptr<ControlLabel>(previousLeft)) {
818 if (!ce->mdiChild || !ce->mdiChild->isMaximized())
819 previousLeft = nullptr;
820 else if (!child)
821 child = mdiChild;
822 }
823 m_menuBar->setCornerWidget(previousLeft, Qt::TopLeftCorner);
824 if (previousLeft) {
825 previousLeft->show();
826 previousLeft = nullptr;
827 }
828 }
829 m_menuLabel->hide();
830 m_menuLabel->setParent(nullptr);
831 }
832 m_menuBar->update();
833 if (child)
834 child->d_func()->setNewWindowTitle();
835 else if (mdiChild)
836 mdiChild->window()->setWindowTitle(mdiChild->d_func()->originalWindowTitle());
837}
838
839#endif // QT_CONFIG(menubar)
840
841void ControlContainer::updateWindowIcon(const QIcon &windowIcon)
842{
843 if (m_menuLabel)
844 m_menuLabel->setWindowIcon(windowIcon);
845}
846
847/*!
848 \internal
849*/
850QMdiSubWindowPrivate::QMdiSubWindowPrivate()
851 : baseWidget(nullptr),
852 restoreFocusWidget(nullptr),
853 controlContainer(nullptr),
854#if QT_CONFIG(sizegrip)
855 sizeGrip(nullptr),
856#endif
857#if QT_CONFIG(rubberband)
858 rubberBand(nullptr),
859#endif
860 userMinimumSize(0,0),
861 resizeEnabled(true),
862 moveEnabled(true),
863 isInInteractiveMode(false),
864#if QT_CONFIG(rubberband)
865 isInRubberBandMode(false),
866#endif
867 isShadeMode(false),
868 ignoreWindowTitleChange(false),
869 ignoreNextActivationEvent(false),
870 activationEnabled(true),
871 isShadeRequestFromMinimizeMode(false),
872 isMaximizeMode(false),
873 isWidgetHiddenByUs(false),
874 isActive(false),
875 isExplicitlyDeactivated(false),
876 keyboardSingleStep(5),
877 keyboardPageStep(20),
878 currentOperation(None),
879 hoveredSubControl(QStyle::SC_None),
880 activeSubControl(QStyle::SC_None),
881 focusInReason(Qt::ActiveWindowFocusReason)
882{
883 initOperationMap();
884}
885
886/*!
887 \internal
888*/
889void QMdiSubWindowPrivate::_q_updateStaysOnTopHint()
890{
891#if QT_CONFIG(action)
892 Q_Q(QMdiSubWindow);
893 if (QAction *senderAction = qobject_cast<QAction *>(q->sender())) {
894 if (senderAction->isChecked()) {
895 q->setWindowFlags(q->windowFlags() | Qt::WindowStaysOnTopHint);
896 q->raise();
897 } else {
898 q->setWindowFlags(q->windowFlags() & ~Qt::WindowStaysOnTopHint);
899 q->lower();
900 }
901 }
902#endif // QT_CONFIG(action)
903}
904
905/*!
906 \internal
907*/
908void QMdiSubWindowPrivate::_q_enterInteractiveMode()
909{
910#ifndef QT_NO_ACTION
911 Q_Q(QMdiSubWindow);
912 QAction *action = qobject_cast<QAction *>(q->sender());
913 if (!action)
914 return;
915
916 QPoint pressPos;
917 if (actions[MoveAction] && actions[MoveAction] == action) {
918 currentOperation = Move;
919 pressPos = QPoint(q->width() / 2, titleBarHeight() - 1);
920 } else if (actions[ResizeAction] && actions[ResizeAction] == action) {
921 currentOperation = q->isLeftToRight() ? BottomRightResize : BottomLeftResize;
922 int offset = q->style()->pixelMetric(QStyle::PM_MdiSubWindowFrameWidth, nullptr, q) / 2;
923 int x = q->isLeftToRight() ? q->width() - offset : offset;
924 pressPos = QPoint(x, q->height() - offset);
925 } else {
926 return;
927 }
928
929 updateCursor();
930#ifndef QT_NO_CURSOR
931 q->cursor().setPos(q->mapToGlobal(pressPos));
932#endif
933 mousePressPosition = q->mapToParent(pressPos);
934 oldGeometry = q->geometry();
935 isInInteractiveMode = true;
936 q->setFocus();
937#if QT_CONFIG(rubberband)
938 if ((q->testOption(QMdiSubWindow::RubberBandResize)
939 && (currentOperation == BottomRightResize || currentOperation == BottomLeftResize))
940 || (q->testOption(QMdiSubWindow::RubberBandMove) && currentOperation == Move)) {
941 enterRubberBandMode();
942 } else
943#endif // QT_CONFIG(rubberband)
944 {
945 q->grabMouse();
946 }
947#endif // QT_NO_ACTION
948}
949
950/*!
951 \internal
952*/
953void QMdiSubWindowPrivate::_q_processFocusChanged(QWidget *old, QWidget *now)
954{
955 Q_UNUSED(old);
956 Q_Q(QMdiSubWindow);
957 if (now && (now == q || q->isAncestorOf(now))) {
958 if (now == q && !isInInteractiveMode)
959 setFocusWidget();
960 setActive(true);
961 }
962}
963
964/*!
965 \internal
966*/
967void QMdiSubWindowPrivate::leaveInteractiveMode()
968{
969 Q_Q(QMdiSubWindow);
970#if QT_CONFIG(rubberband)
971 if (isInRubberBandMode)
972 leaveRubberBandMode();
973 else
974#endif
975 q->releaseMouse();
976 isInInteractiveMode = false;
977 currentOperation = None;
978 updateDirtyRegions();
979 updateCursor();
980 if (baseWidget && baseWidget->focusWidget())
981 baseWidget->focusWidget()->setFocus();
982}
983
984/*!
985 \internal
986*/
987void QMdiSubWindowPrivate::removeBaseWidget()
988{
989 if (!baseWidget)
990 return;
991
992 Q_Q(QMdiSubWindow);
993 baseWidget->removeEventFilter(q);
994 if (layout)
995 layout->removeWidget(baseWidget);
996 if (baseWidget->windowTitle() == q->windowTitle()) {
997 ignoreWindowTitleChange = true;
998 q->setWindowTitle(QString());
999 ignoreWindowTitleChange = false;
1000 q->setWindowModified(false);
1001 }
1002 lastChildWindowTitle.clear();
1003 // QTBUG-47993: parent widget can be reset before this call
1004 if (baseWidget->parentWidget() == q)
1005 baseWidget->setParent(nullptr);
1006 baseWidget = nullptr;
1007 isWidgetHiddenByUs = false;
1008}
1009
1010/*!
1011 \internal
1012*/
1013void QMdiSubWindowPrivate::initOperationMap()
1014{
1015 operationMap.insert(Move, OperationInfo(HMove | VMove, Qt::ArrowCursor, false));
1016 operationMap.insert(TopResize, OperationInfo(VMove | VResize | VResizeReverse, Qt::SizeVerCursor));
1017 operationMap.insert(BottomResize, OperationInfo(VResize, Qt::SizeVerCursor));
1018 operationMap.insert(LeftResize, OperationInfo(HMove | HResize | HResizeReverse, Qt::SizeHorCursor));
1019 operationMap.insert(RightResize, OperationInfo(HResize, Qt::SizeHorCursor));
1020 operationMap.insert(TopLeftResize, OperationInfo(HMove | VMove | HResize | VResize | VResizeReverse
1021 | HResizeReverse, Qt::SizeFDiagCursor));
1022 operationMap.insert(TopRightResize, OperationInfo(VMove | HResize | VResize
1023 | VResizeReverse, Qt::SizeBDiagCursor));
1024 operationMap.insert(BottomLeftResize, OperationInfo(HMove | HResize | VResize | HResizeReverse,
1025 Qt::SizeBDiagCursor));
1026 operationMap.insert(BottomRightResize, OperationInfo(HResize | VResize, Qt::SizeFDiagCursor));
1027}
1028
1029#if QT_CONFIG(menu)
1030
1031/*!
1032 \internal
1033*/
1034void QMdiSubWindowPrivate::createSystemMenu()
1035{
1036 Q_Q(QMdiSubWindow);
1037 Q_ASSERT_X(q, "QMdiSubWindowPrivate::createSystemMenu",
1038 "You can NOT call this function before QMdiSubWindow's ctor");
1039 systemMenu = new QMenu(q);
1040 systemMenu->installEventFilter(q);
1041 const QStyle *style = q->style();
1042 addToSystemMenu(RestoreAction, QMdiSubWindow::tr("&Restore"), SLOT(showNormal()));
1043 actions[RestoreAction]->setIcon(style->standardIcon(QStyle::SP_TitleBarNormalButton, nullptr, q));
1044 actions[RestoreAction]->setEnabled(false);
1045 addToSystemMenu(MoveAction, QMdiSubWindow::tr("&Move"), SLOT(_q_enterInteractiveMode()));
1046 addToSystemMenu(ResizeAction, QMdiSubWindow::tr("&Size"), SLOT(_q_enterInteractiveMode()));
1047 addToSystemMenu(MinimizeAction, QMdiSubWindow::tr("Mi&nimize"), SLOT(showMinimized()));
1048 actions[MinimizeAction]->setIcon(style->standardIcon(QStyle::SP_TitleBarMinButton, nullptr, q));
1049 addToSystemMenu(MaximizeAction, QMdiSubWindow::tr("Ma&ximize"), SLOT(showMaximized()));
1050 actions[MaximizeAction]->setIcon(style->standardIcon(QStyle::SP_TitleBarMaxButton, nullptr, q));
1051 addToSystemMenu(StayOnTopAction, QMdiSubWindow::tr("Stay on &Top"), SLOT(_q_updateStaysOnTopHint()));
1052 actions[StayOnTopAction]->setCheckable(true);
1053 systemMenu->addSeparator();
1054 addToSystemMenu(CloseAction, QMdiSubWindow::tr("&Close"), SLOT(close()));
1055 actions[CloseAction]->setIcon(style->standardIcon(QStyle::SP_TitleBarCloseButton, nullptr, q));
1056#if !defined(QT_NO_SHORTCUT)
1057 actions[CloseAction]->setShortcuts(QKeySequence::Close);
1058#endif
1059 updateActions();
1060}
1061#endif
1062
1063/*!
1064 \internal
1065*/
1066void QMdiSubWindowPrivate::updateCursor()
1067{
1068#ifndef QT_NO_CURSOR
1069 Q_Q(QMdiSubWindow);
1070 if (isMacStyle(q->style()))
1071 return;
1072
1073 if (currentOperation == None) {
1074 q->unsetCursor();
1075 return;
1076 }
1077
1078 if (currentOperation == Move || operationMap.find(currentOperation).value().hover) {
1079 q->setCursor(operationMap.find(currentOperation).value().cursorShape);
1080 return;
1081 }
1082#endif
1083}
1084
1085/*!
1086 \internal
1087*/
1088void QMdiSubWindowPrivate::updateDirtyRegions()
1089{
1090 // No update necessary
1091 if (!parent)
1092 return;
1093
1094 for (OperationInfoMap::iterator it = operationMap.begin(), end = operationMap.end(); it != end; ++it)
1095 it.value().region = getRegion(it.key());
1096}
1097
1098/*!
1099 \internal
1100*/
1101void QMdiSubWindowPrivate::updateGeometryConstraints()
1102{
1103 Q_Q(QMdiSubWindow);
1104 if (!parent)
1105 return;
1106
1107 internalMinimumSize = (!q->isMinimized() && !q->minimumSize().isNull())
1108 ? q->minimumSize() : q->minimumSizeHint();
1109 int margin, minWidth;
1110 sizeParameters(&margin, &minWidth);
1111 q->setContentsMargins(margin, titleBarHeight(), margin, margin);
1112 if (q->isMaximized() || (q->isMinimized() && !q->isShaded())) {
1113 moveEnabled = false;
1114 resizeEnabled = false;
1115 } else {
1116 moveEnabled = true;
1117 if ((q->windowFlags() & Qt::MSWindowsFixedSizeDialogHint) || q->isShaded())
1118 resizeEnabled = false;
1119 else
1120 resizeEnabled = true;
1121 }
1122 updateDirtyRegions();
1123}
1124
1125/*!
1126 \internal
1127*/
1128void QMdiSubWindowPrivate::updateMask()
1129{
1130 Q_Q(QMdiSubWindow);
1131 if (!q->mask().isEmpty())
1132 q->clearMask();
1133
1134 if (!parent)
1135 return;
1136
1137 if ((q->isMaximized() && !drawTitleBarWhenMaximized())
1138 || q->windowFlags() & Qt::FramelessWindowHint)
1139 return;
1140
1141 if (!resizeTimer.isActive())
1142 cachedStyleOptions = titleBarOptions();
1143 cachedStyleOptions.rect = q->rect();
1144 QStyleHintReturnMask frameMask;
1145 q->style()->styleHint(QStyle::SH_WindowFrame_Mask, &cachedStyleOptions, q, &frameMask);
1146 if (!frameMask.region.isEmpty())
1147 q->setMask(frameMask.region);
1148}
1149
1150/*!
1151 \internal
1152*/
1153void QMdiSubWindowPrivate::setNewGeometry(const QPoint &pos)
1154{
1155 Q_Q(QMdiSubWindow);
1156 Q_ASSERT(currentOperation != None);
1157 Q_ASSERT(parent);
1158
1159 uint cflags = operationMap.find(currentOperation).value().changeFlags;
1160 int posX = pos.x();
1161 int posY = pos.y();
1162
1163 const bool restrictHorizontal = !q->testOption(QMdiSubWindow::AllowOutsideAreaHorizontally);
1164 const bool restrictVertical = !q->testOption(QMdiSubWindow::AllowOutsideAreaVertically);
1165
1166 if (restrictHorizontal || restrictVertical) {
1167 QRect parentRect = q->parentWidget()->rect();
1168 if (restrictVertical && (cflags & VResizeReverse || currentOperation == Move)) {
1169 posY = qMin(qMax(mousePressPosition.y() - oldGeometry.y(), posY),
1170 parentRect.height() - BoundaryMargin);
1171 }
1172 if (currentOperation == Move) {
1173 if (restrictHorizontal)
1174 posX = qMin(qMax(BoundaryMargin, posX), parentRect.width() - BoundaryMargin);
1175 if (restrictVertical)
1176 posY = qMin(posY, parentRect.height() - BoundaryMargin);
1177 } else {
1178 if (restrictHorizontal) {
1179 if (cflags & HResizeReverse)
1180 posX = qMax(mousePressPosition.x() - oldGeometry.x(), posX);
1181 else
1182 posX = qMin(parentRect.width() - (oldGeometry.x() + oldGeometry.width()
1183 - mousePressPosition.x()), posX);
1184 }
1185 if (restrictVertical && !(cflags & VResizeReverse)) {
1186 posY = qMin(parentRect.height() - (oldGeometry.y() + oldGeometry.height()
1187 - mousePressPosition.y()), posY);
1188 }
1189 }
1190 }
1191
1192 QRect geometry;
1193 if (cflags & (HMove | VMove)) {
1194 int dx = getMoveDeltaComponent(cflags, HMove, HResize, posX - mousePressPosition.x(),
1195 oldGeometry.width() - internalMinimumSize.width(),
1196 oldGeometry.width() - q->maximumWidth());
1197 int dy = getMoveDeltaComponent(cflags, VMove, VResize, posY - mousePressPosition.y(),
1198 oldGeometry.height() - internalMinimumSize.height(),
1199 oldGeometry.height() - q->maximumHeight());
1200 geometry.setTopLeft(oldGeometry.topLeft() + QPoint(dx, dy));
1201 } else {
1202 geometry.setTopLeft(q->geometry().topLeft());
1203 }
1204
1205 if (cflags & (HResize | VResize)) {
1206 int dx = getResizeDeltaComponent(cflags, HResize, HResizeReverse,
1207 posX - mousePressPosition.x());
1208 int dy = getResizeDeltaComponent(cflags, VResize, VResizeReverse,
1209 posY - mousePressPosition.y());
1210 geometry.setSize(oldGeometry.size() + QSize(dx, dy));
1211 } else {
1212 geometry.setSize(q->geometry().size());
1213 }
1214
1215 setNewGeometry(&geometry);
1216}
1217
1218/*!
1219 \internal
1220*/
1221void QMdiSubWindowPrivate::setMinimizeMode()
1222{
1223 Q_Q(QMdiSubWindow);
1224 Q_ASSERT(parent);
1225
1226 ensureWindowState(Qt::WindowMinimized);
1227 isShadeRequestFromMinimizeMode = true;
1228 q->showShaded();
1229 isShadeRequestFromMinimizeMode = false;
1230
1231 moveEnabled = false;
1232#ifndef QT_NO_ACTION
1233 setEnabled(MoveAction, moveEnabled);
1234#endif
1235
1236 Q_ASSERT(q->windowState() & Qt::WindowMinimized);
1237 Q_ASSERT(!(q->windowState() & Qt::WindowMaximized));
1238 // This should be a valid assert, but people can actually re-implement
1239 // setVisible and do crazy stuff, so we're not guaranteed that
1240 // the widget is hidden after calling hide().
1241 // Q_ASSERT(baseWidget ? baseWidget->isHidden() : true);
1242
1243 setActive(true);
1244}
1245
1246/*!
1247 \internal
1248*/
1249void QMdiSubWindowPrivate::setNormalMode()
1250{
1251 Q_Q(QMdiSubWindow);
1252 Q_ASSERT(parent);
1253
1254 isShadeMode = false;
1255 isMaximizeMode = false;
1256
1257 ensureWindowState(Qt::WindowNoState);
1258#if QT_CONFIG(menubar)
1259 removeButtonsFromMenuBar();
1260#endif
1261
1262 // Hide the window before we change the geometry to avoid multiple resize
1263 // events and wrong window state.
1264 const bool wasVisible = q->isVisible();
1265 if (wasVisible)
1266 q->setVisible(false);
1267
1268 // Restore minimum size if set by user.
1269 if (!userMinimumSize.isNull()) {
1270 q->setMinimumSize(userMinimumSize);
1271 userMinimumSize = QSize(0, 0);
1272 }
1273
1274 // Show the internal widget if it was hidden by us,
1275 if (baseWidget && isWidgetHiddenByUs) {
1276 baseWidget->show();
1277 isWidgetHiddenByUs = false;
1278 }
1279
1280 updateGeometryConstraints();
1281 QRect newGeometry = oldGeometry;
1282 newGeometry.setSize(restoreSize.expandedTo(internalMinimumSize));
1283 q->setGeometry(newGeometry);
1284
1285 if (wasVisible)
1286 q->setVisible(true);
1287
1288 // Invalidate the restore size.
1289 restoreSize.setWidth(-1);
1290 restoreSize.setHeight(-1);
1291
1292#if QT_CONFIG(sizegrip)
1293 setSizeGripVisible(true);
1294#endif
1295
1296#ifndef QT_NO_ACTION
1297 setEnabled(MoveAction, true);
1298 setEnabled(MaximizeAction, true);
1299 setEnabled(MinimizeAction, true);
1300 setEnabled(RestoreAction, false);
1301 setEnabled(ResizeAction, resizeEnabled);
1302#endif // QT_NO_ACTION
1303
1304 Q_ASSERT(!(q_func()->windowState() & Qt::WindowMinimized));
1305 // This sub-window can be maximized when shown above if not the
1306 // QMdiArea::DontMaximizeSubWindowOnActionvation is set. Make sure
1307 // the Qt::WindowMaximized flag is set accordingly.
1308 Q_ASSERT((isMaximizeMode && q_func()->windowState() & Qt::WindowMaximized)
1309 || (!isMaximizeMode && !(q_func()->windowState() & Qt::WindowMaximized)));
1310 Q_ASSERT(!isShadeMode);
1311
1312 setActive(true);
1313 restoreFocus();
1314 updateMask();
1315}
1316
1317inline void QMdiSubWindowPrivate::storeFocusWidget()
1318{
1319 if (QWidget *focus = QApplication::focusWidget()) {
1320 if (!restoreFocusWidget && q_func()->isAncestorOf(focus))
1321 restoreFocusWidget = focus;
1322 }
1323}
1324
1325/*!
1326 \internal
1327*/
1328void QMdiSubWindowPrivate::setMaximizeMode()
1329{
1330 Q_Q(QMdiSubWindow);
1331 Q_ASSERT(parent);
1332
1333 ensureWindowState(Qt::WindowMaximized);
1334 isShadeMode = false;
1335 isMaximizeMode = true;
1336
1337 storeFocusWidget();
1338
1339#if QT_CONFIG(sizegrip)
1340 setSizeGripVisible(false);
1341#endif
1342
1343 // Store old geometry and set restore size if not already set.
1344 if (!restoreSize.isValid()) {
1345 oldGeometry = q->geometry();
1346 restoreSize.setWidth(oldGeometry.width());
1347 restoreSize.setHeight(oldGeometry.height());
1348 }
1349
1350 // Hide the window before we change the geometry to avoid multiple resize
1351 // events and wrong window state.
1352 const bool wasVisible = q->isVisible();
1353 if (wasVisible)
1354 q->setVisible(false);
1355
1356 // Show the internal widget if it was hidden by us.
1357 if (baseWidget && isWidgetHiddenByUs) {
1358 baseWidget->show();
1359 isWidgetHiddenByUs = false;
1360 }
1361
1362 updateGeometryConstraints();
1363
1364 if (wasVisible) {
1365#if QT_CONFIG(menubar)
1366 if (QMenuBar *mBar = menuBar())
1367 showButtonsInMenuBar(mBar);
1368 else
1369#endif
1370 if (!controlContainer)
1371 controlContainer = new ControlContainer(q);
1372 }
1373
1374 QWidget *parent = q->parentWidget();
1375 QRect availableRect = parent->contentsRect();
1376
1377 // Adjust geometry if the sub-window is inside a scroll area.
1378 QAbstractScrollArea *scrollArea = qobject_cast<QAbstractScrollArea *>(parent->parentWidget());
1379 if (scrollArea && scrollArea->viewport() == parent) {
1380 QScrollBar *hbar = scrollArea->horizontalScrollBar();
1381 QScrollBar *vbar = scrollArea->verticalScrollBar();
1382 const int xOffset = hbar ? hbar->value() : 0;
1383 const int yOffset = vbar ? vbar->value() : 0;
1384 availableRect.adjust(-xOffset, -yOffset, -xOffset, -yOffset);
1385 oldGeometry.adjust(xOffset, yOffset, xOffset, yOffset);
1386 }
1387
1388 setNewGeometry(&availableRect);
1389 // QWidget::setGeometry will reset Qt::WindowMaximized so we have to update it here.
1390 ensureWindowState(Qt::WindowMaximized);
1391
1392 if (wasVisible)
1393 q->setVisible(true);
1394
1395 resizeEnabled = false;
1396 moveEnabled = false;
1397
1398#ifndef QT_NO_ACTION
1399 setEnabled(MoveAction, moveEnabled);
1400 setEnabled(MaximizeAction, false);
1401 setEnabled(MinimizeAction, true);
1402 setEnabled(RestoreAction, true);
1403 setEnabled(ResizeAction, resizeEnabled);
1404#endif // QT_NO_ACTION
1405
1406 Q_ASSERT(q->windowState() & Qt::WindowMaximized);
1407 Q_ASSERT(!(q->windowState() & Qt::WindowMinimized));
1408
1409 restoreFocus();
1410 updateMask();
1411}
1412
1413/*!
1414 \internal
1415*/
1416void QMdiSubWindowPrivate::setActive(bool activate, bool changeFocus)
1417{
1418 Q_Q(QMdiSubWindow);
1419 if (!parent || !activationEnabled)
1420 return;
1421
1422 if (activate && !isActive && q->isEnabled()) {
1423 isActive = true;
1424 isExplicitlyDeactivated = false;
1425 Qt::WindowStates oldWindowState = q->windowState();
1426 ensureWindowState(Qt::WindowActive);
1427 emit q->aboutToActivate();
1428#if QT_CONFIG(menubar)
1429 if (QMenuBar *mBar = menuBar())
1430 showButtonsInMenuBar(mBar);
1431#endif
1432 Q_ASSERT(isActive);
1433 emit q->windowStateChanged(oldWindowState, q->windowState());
1434 } else if (!activate && isActive) {
1435 isActive = false;
1436 Qt::WindowStates oldWindowState = q->windowState();
1437 q->overrideWindowState(q->windowState() & ~Qt::WindowActive);
1438 if (changeFocus) {
1439 storeFocusWidget();
1440 QWidget *focusWidget = QApplication::focusWidget();
1441 if (focusWidget && (focusWidget == q || q->isAncestorOf(focusWidget)))
1442 focusWidget->clearFocus();
1443 }
1444 if (baseWidget)
1445 baseWidget->overrideWindowState(baseWidget->windowState() & ~Qt::WindowActive);
1446 Q_ASSERT(!isActive);
1447 emit q->windowStateChanged(oldWindowState, q->windowState());
1448 }
1449
1450 if (activate && isActive && q->isEnabled() && !q->hasFocus()
1451 && !q->isAncestorOf(QApplication::focusWidget())) {
1452 if (changeFocus)
1453 setFocusWidget();
1454 ensureWindowState(Qt::WindowActive);
1455 }
1456
1457 int frameWidth = q->style()->pixelMetric(QStyle::PM_MdiSubWindowFrameWidth, nullptr, q);
1458 int titleBarHeight = this->titleBarHeight();
1459 QRegion windowDecoration = QRegion(0, 0, q->width(), q->height());
1460 windowDecoration -= QRegion(frameWidth, titleBarHeight, q->width() - 2 * frameWidth,
1461 q->height() - titleBarHeight - frameWidth);
1462
1463 // Make sure we don't use cached style options if we get
1464 // resize events right before activation/deactivation.
1465 if (resizeTimer.isActive()) {
1466 resizeTimer.stop();
1467 updateDirtyRegions();
1468 }
1469
1470 q->update(windowDecoration);
1471}
1472
1473/*!
1474 \internal
1475*/
1476void QMdiSubWindowPrivate::processClickedSubControl()
1477{
1478 Q_Q(QMdiSubWindow);
1479 switch (activeSubControl) {
1480 case QStyle::SC_TitleBarContextHelpButton:
1481#if QT_CONFIG(whatsthis)
1482 QWhatsThis::enterWhatsThisMode();
1483#endif
1484 break;
1485 case QStyle::SC_TitleBarShadeButton:
1486 q->showShaded();
1487 hoveredSubControl = QStyle::SC_TitleBarUnshadeButton;
1488 break;
1489 case QStyle::SC_TitleBarUnshadeButton:
1490 if (q->isShaded())
1491 hoveredSubControl = QStyle::SC_TitleBarShadeButton;
1492 q->showNormal();
1493 break;
1494 case QStyle::SC_TitleBarMinButton:
1495 if (isMacStyle(q->style())) {
1496 if (q->isMinimized())
1497 q->showNormal();
1498 else
1499 q->showMinimized();
1500 break;
1501 }
1502
1503 q->showMinimized();
1504 break;
1505 case QStyle::SC_TitleBarNormalButton:
1506 if (q->isShaded())
1507 hoveredSubControl = QStyle::SC_TitleBarMinButton;
1508 q->showNormal();
1509 break;
1510 case QStyle::SC_TitleBarMaxButton:
1511 if (isMacStyle(q->style())) {
1512 if (q->isMaximized())
1513 q->showNormal();
1514 else
1515 q->showMaximized();
1516 break;
1517 }
1518
1519 q->showMaximized();
1520 break;
1521 case QStyle::SC_TitleBarCloseButton:
1522 q->close();
1523 break;
1524 default:
1525 break;
1526 }
1527}
1528
1529/*!
1530 \internal
1531*/
1532QRegion QMdiSubWindowPrivate::getRegion(Operation operation) const
1533{
1534 Q_Q(const QMdiSubWindow);
1535 int width = q->width();
1536 int height = q->height();
1537 int titleBarHeight = this->titleBarHeight();
1538 int frameWidth = q->style()->pixelMetric(QStyle::PM_MdiSubWindowFrameWidth, nullptr, q);
1539 int cornerConst = titleBarHeight - frameWidth;
1540 int titleBarConst = 2 * titleBarHeight;
1541
1542 if (operation == Move) {
1543 QStyleOptionTitleBar titleBarOptions = this->titleBarOptions();
1544 QRegion move(frameWidth, frameWidth, width - 2 * frameWidth, cornerConst);
1545 // Depending on which window flags are set, activated sub controllers will
1546 // be subtracted from the 'move' region.
1547 for (int i = 0; i < NumSubControls; ++i) {
1548 if (SubControls[i] == QStyle::SC_TitleBarLabel)
1549 continue;
1550 move -= QRegion(q->style()->subControlRect(QStyle::CC_TitleBar, &titleBarOptions,
1551 SubControls[i]));
1552 }
1553 return move;
1554 }
1555
1556 QRegion region;
1557 if (isMacStyle(q->style()))
1558 return region;
1559
1560 switch (operation) {
1561 case TopResize:
1562 region = QRegion(titleBarHeight, 0, width - titleBarConst, frameWidth);
1563 break;
1564 case BottomResize:
1565 region = QRegion(titleBarHeight, height - frameWidth, width - titleBarConst, frameWidth);
1566 break;
1567 case LeftResize:
1568 region = QRegion(0, titleBarHeight, frameWidth, height - titleBarConst);
1569 break;
1570 case RightResize:
1571 region = QRegion(width - frameWidth, titleBarHeight, frameWidth, height - titleBarConst);
1572 break;
1573 case TopLeftResize:
1574 region = QRegion(0, 0, titleBarHeight, titleBarHeight)
1575 - QRegion(frameWidth, frameWidth, cornerConst, cornerConst);
1576 break;
1577 case TopRightResize:
1578 region = QRegion(width - titleBarHeight, 0, titleBarHeight, titleBarHeight)
1579 - QRegion(width - titleBarHeight, frameWidth, cornerConst, cornerConst);
1580 break;
1581 case BottomLeftResize:
1582 region = QRegion(0, height - titleBarHeight, titleBarHeight, titleBarHeight)
1583 - QRegion(frameWidth, height - titleBarHeight, cornerConst, cornerConst);
1584 break;
1585 case BottomRightResize:
1586 region = QRegion(width - titleBarHeight, height - titleBarHeight, titleBarHeight, titleBarHeight)
1587 - QRegion(width - titleBarHeight, height - titleBarHeight, cornerConst, cornerConst);
1588 break;
1589 default:
1590 break;
1591 }
1592
1593 return region;
1594}
1595
1596/*!
1597 \internal
1598*/
1599QMdiSubWindowPrivate::Operation QMdiSubWindowPrivate::getOperation(const QPoint &pos) const
1600{
1601 OperationInfoMap::const_iterator it;
1602 for (it = operationMap.constBegin(); it != operationMap.constEnd(); ++it)
1603 if (it.value().region.contains(pos))
1604 return it.key();
1605 return None;
1606}
1607
1608extern QString qt_setWindowTitle_helperHelper(const QString&, const QWidget*);
1609
1610/*!
1611 \internal
1612*/
1613QStyleOptionTitleBar QMdiSubWindowPrivate::titleBarOptions() const
1614{
1615 Q_Q(const QMdiSubWindow);
1616 QStyleOptionTitleBar titleBarOptions;
1617 titleBarOptions.initFrom(q);
1618 if (activeSubControl != QStyle::SC_None) {
1619 if (hoveredSubControl == activeSubControl) {
1620 titleBarOptions.state |= QStyle::State_Sunken;
1621 titleBarOptions.activeSubControls = activeSubControl;
1622 }
1623 } else if (autoRaise() && hoveredSubControl != QStyle::SC_None
1624 && hoveredSubControl != QStyle::SC_TitleBarLabel) {
1625 titleBarOptions.state |= QStyle::State_MouseOver;
1626 titleBarOptions.activeSubControls = hoveredSubControl;
1627 } else {
1628 titleBarOptions.state &= ~QStyle::State_MouseOver;
1629 titleBarOptions.activeSubControls = QStyle::SC_None;
1630 }
1631
1632 titleBarOptions.subControls = QStyle::SC_All;
1633 titleBarOptions.titleBarFlags = q->windowFlags();
1634 titleBarOptions.titleBarState = q->windowState();
1635 titleBarOptions.palette = titleBarPalette;
1636 titleBarOptions.icon = menuIcon;
1637
1638 if (isActive) {
1639 titleBarOptions.state |= QStyle::State_Active;
1640 titleBarOptions.titleBarState |= QStyle::State_Active;
1641 titleBarOptions.palette.setCurrentColorGroup(QPalette::Active);
1642 } else {
1643 titleBarOptions.state &= ~QStyle::State_Active;
1644 titleBarOptions.palette.setCurrentColorGroup(QPalette::Inactive);
1645 }
1646
1647 int border = hasBorder(titleBarOptions) ? 4 : 0;
1648 int paintHeight = titleBarHeight(titleBarOptions);
1649 paintHeight -= q->isMinimized() ? 2 * border : border;
1650 titleBarOptions.rect = QRect(border, border, q->width() - 2 * border, paintHeight);
1651
1652 if (!windowTitle.isEmpty()) {
1653 // Set the text here before asking for the width of the title bar label
1654 // in case people uses the actual text to calculate the width.
1655 titleBarOptions.text = windowTitle;
1656 titleBarOptions.fontMetrics = QFontMetrics(font);
1657 int width = q->style()->subControlRect(QStyle::CC_TitleBar, &titleBarOptions,
1658 QStyle::SC_TitleBarLabel, q).width();
1659 // Set elided text if we don't have enough space for the entire title.
1660 titleBarOptions.text = titleBarOptions.fontMetrics.elidedText(windowTitle, Qt::ElideRight, width);
1661 }
1662 return titleBarOptions;
1663}
1664
1665/*!
1666 \internal
1667*/
1668void QMdiSubWindowPrivate::ensureWindowState(Qt::WindowState state)
1669{
1670 Q_Q(QMdiSubWindow);
1671 Qt::WindowStates windowStates = q->windowState() | state;
1672 switch (state) {
1673 case Qt::WindowMinimized:
1674 windowStates &= ~Qt::WindowMaximized;
1675 windowStates &= ~Qt::WindowFullScreen;
1676 windowStates &= ~Qt::WindowNoState;
1677 break;
1678 case Qt::WindowMaximized:
1679 windowStates &= ~Qt::WindowMinimized;
1680 windowStates &= ~Qt::WindowFullScreen;
1681 windowStates &= ~Qt::WindowNoState;
1682 break;
1683 case Qt::WindowNoState:
1684 windowStates &= ~Qt::WindowMinimized;
1685 windowStates &= ~Qt::WindowMaximized;
1686 windowStates &= ~Qt::WindowFullScreen;
1687 break;
1688 default:
1689 break;
1690 }
1691 if (baseWidget) {
1692 if (!(baseWidget->windowState() & Qt::WindowActive) && windowStates & Qt::WindowActive)
1693 baseWidget->overrideWindowState(windowStates & ~Qt::WindowActive);
1694 else
1695 baseWidget->overrideWindowState(windowStates);
1696 }
1697 q->overrideWindowState(windowStates);
1698}
1699
1700/*!
1701 \internal
1702*/
1703int QMdiSubWindowPrivate::titleBarHeight(const QStyleOptionTitleBar &options) const
1704{
1705 Q_Q(const QMdiSubWindow);
1706 if (!parent || q->windowFlags() & Qt::FramelessWindowHint
1707 || (q->isMaximized() && !drawTitleBarWhenMaximized())) {
1708 return 0;
1709 }
1710
1711 int height = q->style()->pixelMetric(QStyle::PM_TitleBarHeight, &options, q);
1712 if (hasBorder(options))
1713 height += q->isMinimized() ? 8 : 4;
1714 return height;
1715}
1716
1717/*!
1718 \internal
1719*/
1720void QMdiSubWindowPrivate::sizeParameters(int *margin, int *minWidth) const
1721{
1722 Q_Q(const QMdiSubWindow);
1723 Qt::WindowFlags flags = q->windowFlags();
1724 if (!parent || flags & Qt::FramelessWindowHint) {
1725 *margin = 0;
1726 *minWidth = 0;
1727 return;
1728 }
1729
1730 if (q->isMaximized() && !drawTitleBarWhenMaximized())
1731 *margin = 0;
1732 else
1733 *margin = q->style()->pixelMetric(QStyle::PM_MdiSubWindowFrameWidth, nullptr, q);
1734
1735 QStyleOptionTitleBar opt = this->titleBarOptions();
1736 int tempWidth = 0;
1737 for (int i = 0; i < NumSubControls; ++i) {
1738 if (SubControls[i] == QStyle::SC_TitleBarLabel) {
1739 tempWidth += 30;
1740 continue;
1741 }
1742 QRect rect = q->style()->subControlRect(QStyle::CC_TitleBar, &opt, SubControls[i], q);
1743 if (!rect.isValid())
1744 continue;
1745 tempWidth += rect.width();
1746 }
1747 *minWidth = tempWidth;
1748}
1749
1750/*!
1751 \internal
1752*/
1753bool QMdiSubWindowPrivate::drawTitleBarWhenMaximized() const
1754{
1755 Q_Q(const QMdiSubWindow);
1756 if (q->window()->testAttribute(Qt::WA_CanHostQMdiSubWindowTitleBar))
1757 return false;
1758
1759 if (isChildOfTabbedQMdiArea(q))
1760 return false;
1761
1762 if (q->style()->styleHint(QStyle::SH_Workspace_FillSpaceOnMaximize, nullptr, q))
1763 return true;
1764#if !QT_CONFIG(menubar) || !QT_CONFIG(mainwindow)
1765 Q_UNUSED(isChildOfQMdiSubWindow);
1766 return true;
1767#else
1768 QMainWindow *mainWindow = qobject_cast<QMainWindow *>(q->window());
1769 if (!mainWindow || !qobject_cast<QMenuBar *>(mainWindow->menuWidget())
1770 || mainWindow->menuWidget()->isHidden())
1771 return true;
1772
1773 return isChildOfQMdiSubWindow(q);
1774#endif
1775}
1776
1777#if QT_CONFIG(menubar)
1778
1779/*!
1780 \internal
1781*/
1782void QMdiSubWindowPrivate::showButtonsInMenuBar(QMenuBar *menuBar)
1783{
1784 Q_Q(QMdiSubWindow);
1785 Q_ASSERT(q->isMaximized() && !drawTitleBarWhenMaximized());
1786
1787 if (isChildOfTabbedQMdiArea(q))
1788 return;
1789
1790 removeButtonsFromMenuBar();
1791 if (!controlContainer)
1792 controlContainer = new ControlContainer(q);
1793
1794 ignoreWindowTitleChange = true;
1795 controlContainer->showButtonsInMenuBar(menuBar);
1796 ignoreWindowTitleChange = false;
1797
1798 QWidget *topLevelWindow = q->window();
1799 topLevelWindow->setWindowModified(q->isWindowModified());
1800 topLevelWindow->installEventFilter(q);
1801
1802 int buttonHeight = 0;
1803 if (controlContainer->controllerWidget())
1804 buttonHeight = controlContainer->controllerWidget()->height();
1805 else if (controlContainer->systemMenuLabel())
1806 buttonHeight = controlContainer->systemMenuLabel()->height();
1807
1808 // This will rarely happen.
1809 if (menuBar && menuBar->height() < buttonHeight
1810 && topLevelWindow->layout()) {
1811 // Make sure topLevelWindow->contentsRect returns correct geometry.
1812 // topLevelWidget->updateGeoemtry will not do the trick here since it will post the event.
1813 QEvent event(QEvent::LayoutRequest);
1814 QCoreApplication::sendEvent(topLevelWindow, &event);
1815 }
1816}
1817
1818/*!
1819 \internal
1820*/
1821void QMdiSubWindowPrivate::removeButtonsFromMenuBar()
1822{
1823 Q_Q(QMdiSubWindow);
1824
1825 if (!controlContainer || isChildOfTabbedQMdiArea(q))
1826 return;
1827
1828 QMenuBar *currentMenuBar = nullptr;
1829#if QT_CONFIG(mainwindow)
1830 if (QMainWindow *mainWindow = qobject_cast<QMainWindow *>(q->window())) {
1831 // NB! We can't use menuBar() here because that one will actually create
1832 // a menubar for us if not set. That's not what we want :-)
1833 currentMenuBar = qobject_cast<QMenuBar *>(mainWindow->menuWidget());
1834 }
1835#endif
1836
1837 ignoreWindowTitleChange = true;
1838 controlContainer->removeButtonsFromMenuBar(currentMenuBar);
1839 ignoreWindowTitleChange = false;
1840
1841 QWidget *topLevelWindow = q->window();
1842 topLevelWindow->removeEventFilter(q);
1843 if (baseWidget && !drawTitleBarWhenMaximized())
1844 topLevelWindow->setWindowModified(false);
1845 originalTitle.clear();
1846}
1847
1848#endif // QT_CONFIG(menubar)
1849
1850void QMdiSubWindowPrivate::updateWindowTitle(bool isRequestFromChild)
1851{
1852 Q_Q(QMdiSubWindow);
1853 if (isRequestFromChild && !q->windowTitle().isEmpty() && !lastChildWindowTitle.isEmpty()
1854 && lastChildWindowTitle != q->windowTitle()) {
1855 return;
1856 }
1857
1858 QWidget *titleWidget = nullptr;
1859 if (isRequestFromChild)
1860 titleWidget = baseWidget;
1861 else
1862 titleWidget = q;
1863 if (!titleWidget || titleWidget->windowTitle().isEmpty())
1864 return;
1865
1866 ignoreWindowTitleChange = true;
1867 q->setWindowTitle(titleWidget->windowTitle());
1868 if (q->maximizedButtonsWidget())
1869 setNewWindowTitle();
1870 ignoreWindowTitleChange = false;
1871}
1872
1873#if QT_CONFIG(rubberband)
1874void QMdiSubWindowPrivate::enterRubberBandMode()
1875{
1876 Q_Q(QMdiSubWindow);
1877 if (q->isMaximized())
1878 return;
1879 Q_ASSERT(oldGeometry.isValid());
1880 Q_ASSERT(parent);
1881 if (!rubberBand) {
1882 rubberBand = new QRubberBand(QRubberBand::Rectangle, q->parentWidget());
1883 // For accessibility to identify this special widget.
1884 rubberBand->setObjectName("qt_rubberband"_L1);
1885 }
1886 QPoint rubberBandPos = q->mapToParent(QPoint(0, 0));
1887 rubberBand->setGeometry(rubberBandPos.x(), rubberBandPos.y(),
1888 oldGeometry.width(), oldGeometry.height());
1889 rubberBand->show();
1890 isInRubberBandMode = true;
1891 q->grabMouse();
1892}
1893
1894void QMdiSubWindowPrivate::leaveRubberBandMode()
1895{
1896 Q_Q(QMdiSubWindow);
1897 Q_ASSERT(rubberBand);
1898 Q_ASSERT(isInRubberBandMode);
1899 q->releaseMouse();
1900 isInRubberBandMode = false;
1901 q->setGeometry(rubberBand->geometry());
1902 rubberBand->hide();
1903 currentOperation = None;
1904}
1905#endif // QT_CONFIG(rubberband)
1906
1907// Taken from the old QWorkspace (::readColors())
1908QPalette QMdiSubWindowPrivate::desktopPalette() const
1909{
1910 Q_Q(const QMdiSubWindow);
1911 QPalette newPalette = q->palette();
1912
1913 newPalette.setColor(QPalette::Active, QPalette::Highlight,
1914 newPalette.color(QPalette::Active, QPalette::Highlight));
1915 newPalette.setColor(QPalette::Active, QPalette::Base,
1916 newPalette.color(QPalette::Active, QPalette::Highlight));
1917 newPalette.setColor(QPalette::Inactive, QPalette::Highlight,
1918 newPalette.color(QPalette::Inactive, QPalette::Dark));
1919 newPalette.setColor(QPalette::Inactive, QPalette::Base,
1920 newPalette.color(QPalette::Inactive, QPalette::Dark));
1921 newPalette.setColor(QPalette::Inactive, QPalette::HighlightedText,
1922 newPalette.color(QPalette::Inactive, QPalette::Window));
1923
1924 return newPalette;
1925}
1926
1927void QMdiSubWindowPrivate::updateActions()
1928{
1929 Qt::WindowFlags windowFlags = q_func()->windowFlags();
1930 // Hide all
1931 for (int i = 0; i < NumWindowStateActions; ++i)
1932 setVisible(WindowStateAction(i), false);
1933
1934#if defined(Q_OS_MACOS) && QT_CONFIG(action)
1935 if (q_func()->style()->inherits("QMacStyle"))
1936 for (int i = 0; i < NumWindowStateActions; ++i)
1937 if (QAction *action = actions[i])
1938 action->setIconVisibleInMenu(false);
1939#endif
1940
1941 if (windowFlags & Qt::FramelessWindowHint)
1942 return;
1943
1944 setVisible(StayOnTopAction, true);
1945 setVisible(MoveAction, moveEnabled);
1946 setVisible(ResizeAction, resizeEnabled);
1947
1948 // CloseAction
1949 if (windowFlags & Qt::WindowSystemMenuHint)
1950 setVisible(CloseAction, true);
1951
1952 // RestoreAction
1953 if (windowFlags & (Qt::WindowMinimizeButtonHint | Qt::WindowMaximizeButtonHint))
1954 setVisible(RestoreAction, true);
1955
1956 // MinimizeAction
1957 if (windowFlags & Qt::WindowMinimizeButtonHint)
1958 setVisible(MinimizeAction, true);
1959
1960 // MaximizeAction
1961 if (windowFlags & Qt::WindowMaximizeButtonHint)
1962 setVisible(MaximizeAction, true);
1963}
1964
1965void QMdiSubWindowPrivate::setFocusWidget()
1966{
1967 Q_Q(QMdiSubWindow);
1968 if (!baseWidget) {
1969 q->setFocus();
1970 return;
1971 }
1972
1973 // This will give focus to the next child if possible, otherwise
1974 // do nothing, hence it's not possible to tab between windows with
1975 // just hitting tab (unless Qt::TabFocus is removed from the focus policy).
1976 if (focusInReason == Qt::TabFocusReason) {
1977 q->focusNextChild();
1978 return;
1979 }
1980
1981 // Same as above, but gives focus to the previous child.
1982 if (focusInReason == Qt::BacktabFocusReason) {
1983 q->focusPreviousChild();
1984 return;
1985 }
1986
1987 if (!(q->windowState() & Qt::WindowMinimized) && restoreFocus())
1988 return;
1989
1990 if (QWidget *focusWidget = baseWidget->focusWidget()) {
1991 if (!focusWidget->hasFocus() && q->isAncestorOf(focusWidget)
1992 && focusWidget->isVisible() && !q->isMinimized()
1993 && focusWidget->focusPolicy() != Qt::NoFocus) {
1994 focusWidget->setFocus();
1995 } else {
1996 q->setFocus();
1997 }
1998 return;
1999 }
2000
2001 QWidget *focusWidget = q->nextInFocusChain();
2002 while (focusWidget && focusWidget != q && focusWidget->focusPolicy() == Qt::NoFocus)
2003 focusWidget = focusWidget->nextInFocusChain();
2004 if (focusWidget && q->isAncestorOf(focusWidget))
2005 focusWidget->setFocus();
2006 else if (baseWidget->focusPolicy() != Qt::NoFocus)
2007 baseWidget->setFocus();
2008 else if (!q->hasFocus())
2009 q->setFocus();
2010}
2011
2012bool QMdiSubWindowPrivate::restoreFocus()
2013{
2014 if (restoreFocusWidget.isNull())
2015 return false;
2016 QWidget *candidate = restoreFocusWidget;
2017 restoreFocusWidget.clear();
2018 if (!candidate->hasFocus() && q_func()->isAncestorOf(candidate)
2019 && candidate->isVisible()
2020 && candidate->focusPolicy() != Qt::NoFocus) {
2021 candidate->setFocus();
2022 return true;
2023 }
2024 return candidate->hasFocus();
2025}
2026
2027/*!
2028 \internal
2029*/
2030void QMdiSubWindowPrivate::setWindowFlags(Qt::WindowFlags windowFlags)
2031{
2032 Q_Q(QMdiSubWindow);
2033
2034 if (!parent) {
2035 QWidgetPrivate::setWindowFlags(windowFlags);
2036 return;
2037 }
2038
2039 Qt::WindowFlags windowType = windowFlags & Qt::WindowType_Mask;
2040 if (windowType == Qt::Dialog || windowFlags & Qt::MSWindowsFixedSizeDialogHint)
2041 windowFlags |= Qt::WindowTitleHint | Qt::WindowSystemMenuHint;
2042
2043 // Set standard flags if none of the customize flags are set
2044 if (!(windowFlags & CustomizeWindowFlags))
2045 windowFlags |= Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint;
2046 else if (windowFlags & Qt::FramelessWindowHint && windowFlags & Qt::WindowStaysOnTopHint)
2047 windowFlags = Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint;
2048 else if (windowFlags & Qt::FramelessWindowHint)
2049 windowFlags = Qt::FramelessWindowHint;
2050
2051 windowFlags &= ~windowType;
2052 windowFlags &= ~Qt::WindowFullscreenButtonHint;
2053 windowFlags |= Qt::SubWindow;
2054
2055#ifndef QT_NO_ACTION
2056 if (QAction *stayOnTopAction = actions[QMdiSubWindowPrivate::StayOnTopAction]) {
2057 if (windowFlags & Qt::WindowStaysOnTopHint)
2058 stayOnTopAction->setChecked(true);
2059 else
2060 stayOnTopAction->setChecked(false);
2061 }
2062#endif
2063
2064#if QT_CONFIG(sizegrip)
2065 if ((windowFlags & Qt::FramelessWindowHint) && sizeGrip)
2066 delete sizeGrip;
2067#endif
2068
2069 QWidgetPrivate::setWindowFlags(windowFlags);
2070 updateGeometryConstraints();
2071 updateActions();
2072 QSize currentSize = q->size();
2073 if (q->isVisible() && (currentSize.width() < internalMinimumSize.width()
2074 || currentSize.height() < internalMinimumSize.height())) {
2075 q->resize(currentSize.expandedTo(internalMinimumSize));
2076 }
2077}
2078
2079void QMdiSubWindowPrivate::setVisible(WindowStateAction action, bool visible)
2080{
2081#ifndef QT_NO_ACTION
2082 if (actions[action])
2083 actions[action]->setVisible(visible);
2084#endif
2085
2086 Q_Q(QMdiSubWindow);
2087 if (!controlContainer)
2088 controlContainer = new ControlContainer(q);
2089
2090 if (ControllerWidget *ctrlWidget = qobject_cast<ControllerWidget *>
2091 (controlContainer->controllerWidget())) {
2092 ctrlWidget->setControlVisible(action, visible);
2093 }
2094 q->update();
2095}
2096
2097#ifndef QT_NO_ACTION
2098void QMdiSubWindowPrivate::setEnabled(WindowStateAction action, bool enable)
2099{
2100 if (actions[action])
2101 actions[action]->setEnabled(enable);
2102}
2103
2104#if QT_CONFIG(menu)
2105void QMdiSubWindowPrivate::addToSystemMenu(WindowStateAction action, const QString &text,
2106 const char *slot)
2107{
2108 if (!systemMenu)
2109 return;
2110 actions[action] = systemMenu->addAction(text, q_func(), slot);
2111}
2112#endif
2113#endif // QT_NO_ACTION
2114
2115/*!
2116 \internal
2117*/
2118QSize QMdiSubWindowPrivate::iconSize() const
2119{
2120 Q_Q(const QMdiSubWindow);
2121 if (!parent || q->windowFlags() & Qt::FramelessWindowHint)
2122 return QSize(-1, -1);
2123 return QSize(q->style()->pixelMetric(QStyle::PM_MdiSubWindowMinimizedWidth, nullptr, q), titleBarHeight());
2124}
2125
2126#if QT_CONFIG(sizegrip)
2127
2128/*!
2129 \internal
2130*/
2131void QMdiSubWindowPrivate::setSizeGrip(QSizeGrip *newSizeGrip)
2132{
2133 Q_Q(QMdiSubWindow);
2134 if (!newSizeGrip || sizeGrip || q->windowFlags() & Qt::FramelessWindowHint)
2135 return;
2136
2137 if (layout && layout->indexOf(newSizeGrip) != -1)
2138 return;
2139 newSizeGrip->setFixedSize(newSizeGrip->sizeHint());
2140 bool putSizeGripInLayout = layout ? true : false;
2141 if (isMacStyle(q->style()))
2142 putSizeGripInLayout = false;
2143 if (putSizeGripInLayout) {
2144 layout->addWidget(newSizeGrip);
2145 layout->setAlignment(newSizeGrip, Qt::AlignBottom | Qt::AlignRight);
2146 } else {
2147 newSizeGrip->setParent(q);
2148 newSizeGrip->move(q->isLeftToRight() ? q->width() - newSizeGrip->width() : 0,
2149 q->height() - newSizeGrip->height());
2150 sizeGrip = newSizeGrip;
2151 }
2152 newSizeGrip->raise();
2153 updateGeometryConstraints();
2154 newSizeGrip->installEventFilter(q);
2155}
2156
2157/*!
2158 \internal
2159*/
2160void QMdiSubWindowPrivate::setSizeGripVisible(bool visible) const
2161{
2162 // See if we can find any size grips
2163 const QList<QSizeGrip *> sizeGrips = q_func()->findChildren<QSizeGrip *>();
2164 for (QSizeGrip *grip : sizeGrips)
2165 grip->setVisible(visible);
2166}
2167
2168#endif // QT_CONFIG(sizegrip)
2169
2170/*!
2171 \internal
2172*/
2173void QMdiSubWindowPrivate::updateInternalWindowTitle()
2174{
2175 Q_Q(QMdiSubWindow);
2176 if (q->isWindowModified()) {
2177 windowTitle = q->windowTitle();
2178 windowTitle.replace("[*]"_L1, "*"_L1);
2179 } else {
2180 windowTitle = qt_setWindowTitle_helperHelper(q->windowTitle(), q);
2181 }
2182 q->update(0, 0, q->width(), titleBarHeight());
2183}
2184
2185/*!
2186 Constructs a new QMdiSubWindow widget. The \a parent and \a
2187 flags arguments are passed to QWidget's constructor.
2188
2189 Instead of using addSubWindow(), it is also simply possible to
2190 use setParent() when you add the subwindow to a QMdiArea.
2191
2192 Note that only \l{QMdiSubWindow}s can be set as children of
2193 QMdiArea; you cannot, for instance, write:
2194
2195 \code
2196 //bad code
2197 QMdiArea mdiArea;
2198 QTextEdit editor(&mdiArea); // invalid child widget
2199 \endcode
2200
2201 \sa QMdiArea::addSubWindow()
2202*/
2203QMdiSubWindow::QMdiSubWindow(QWidget *parent, Qt::WindowFlags flags)
2204 : QWidget(*new QMdiSubWindowPrivate, parent, { })
2205{
2206 Q_D(QMdiSubWindow);
2207#if QT_CONFIG(menu)
2208 d->createSystemMenu();
2209 addActions(d->systemMenu->actions());
2210#endif
2211 d->setWindowFlags(flags);
2212 setBackgroundRole(QPalette::Window);
2213 setAutoFillBackground(true);
2214 setMouseTracking(true);
2215 setLayout(new QVBoxLayout);
2216 setFocusPolicy(Qt::StrongFocus);
2217 layout()->setContentsMargins(QMargins());
2218 d->updateGeometryConstraints();
2219 setAttribute(Qt::WA_Resized, false);
2220 d->titleBarPalette = d->desktopPalette();
2221 d->font = QApplication::font("QMdiSubWindowTitleBar");
2222 // We don't want the menu icon by default on mac.
2223#ifndef Q_OS_MAC
2224 if (windowIcon().isNull())
2225 d->menuIcon = style()->standardIcon(QStyle::SP_TitleBarMenuButton, nullptr, this);
2226 else
2227 d->menuIcon = windowIcon();
2228#endif
2229 connect(qApp, SIGNAL(focusChanged(QWidget*,QWidget*)),
2230 this, SLOT(_q_processFocusChanged(QWidget*,QWidget*)));
2231}
2232
2233/*!
2234 Destroys the subwindow.
2235
2236 \sa QMdiArea::removeSubWindow()
2237*/
2238QMdiSubWindow::~QMdiSubWindow()
2239{
2240 Q_D(QMdiSubWindow);
2241#if QT_CONFIG(menubar)
2242 d->removeButtonsFromMenuBar();
2243#endif
2244 d->setActive(false);
2245 delete d->controlContainer;
2246}
2247
2248/*!
2249 Sets \a widget as the internal widget of this subwindow. The
2250 internal widget is displayed in the center of the subwindow
2251 beneath the title bar.
2252
2253 QMdiSubWindow takes temporary ownership of \a widget; you do
2254 not have to delete it. Any existing internal widget will be
2255 removed and reparented to the root window.
2256
2257 \sa widget()
2258*/
2259void QMdiSubWindow::setWidget(QWidget *widget)
2260{
2261 Q_D(QMdiSubWindow);
2262 if (!widget) {
2263 d->removeBaseWidget();
2264 return;
2265 }
2266
2267 if (Q_UNLIKELY(widget == d->baseWidget)) {
2268 qWarning("QMdiSubWindow::setWidget: widget is already set");
2269 return;
2270 }
2271
2272 bool wasResized = testAttribute(Qt::WA_Resized);
2273 d->removeBaseWidget();
2274
2275 if (QLayout *layout = this->layout())
2276 layout->addWidget(widget);
2277 else
2278 widget->setParent(this);
2279
2280#if QT_CONFIG(sizegrip)
2281 QSizeGrip *sizeGrip = widget->findChild<QSizeGrip *>();
2282 if (sizeGrip)
2283 sizeGrip->installEventFilter(this);
2284 if (d->sizeGrip)
2285 d->sizeGrip->raise();
2286#endif
2287
2288 d->baseWidget = widget;
2289 d->baseWidget->installEventFilter(this);
2290
2291 d->ignoreWindowTitleChange = true;
2292 bool isWindowModified = this->isWindowModified();
2293 if (windowTitle().isEmpty()) {
2294 d->updateWindowTitle(true);
2295 isWindowModified = d->baseWidget->isWindowModified();
2296 }
2297 if (!this->isWindowModified() && isWindowModified && windowTitle().contains("[*]"_L1))
2298 setWindowModified(isWindowModified);
2299 d->lastChildWindowTitle = d->baseWidget->windowTitle();
2300 d->ignoreWindowTitleChange = false;
2301
2302 if (windowIcon().isNull() && !d->baseWidget->windowIcon().isNull())
2303 setWindowIcon(d->baseWidget->windowIcon());
2304
2305 d->updateGeometryConstraints();
2306 if (!wasResized && testAttribute(Qt::WA_Resized))
2307 setAttribute(Qt::WA_Resized, false);
2308}
2309
2310/*!
2311 Returns the current internal widget.
2312
2313 \sa setWidget()
2314*/
2315QWidget *QMdiSubWindow::widget() const
2316{
2317 return d_func()->baseWidget;
2318}
2319
2320
2321/*!
2322 \internal
2323*/
2324QWidget *QMdiSubWindow::maximizedButtonsWidget() const
2325{
2326 Q_D(const QMdiSubWindow);
2327 if (isVisible() && d->controlContainer && isMaximized() && !d->drawTitleBarWhenMaximized()
2328 && !isChildOfTabbedQMdiArea(this)) {
2329 return d->controlContainer->controllerWidget();
2330 }
2331 return nullptr;
2332}
2333
2334/*!
2335 \internal
2336*/
2337QWidget *QMdiSubWindow::maximizedSystemMenuIconWidget() const
2338{
2339 Q_D(const QMdiSubWindow);
2340 if (isVisible() && d->controlContainer && isMaximized() && !d->drawTitleBarWhenMaximized()
2341 && !isChildOfTabbedQMdiArea(this)) {
2342 return d->controlContainer->systemMenuLabel();
2343 }
2344 return nullptr;
2345}
2346
2347/*!
2348 Returns \c true if this window is shaded; otherwise returns \c false.
2349
2350 A window is shaded if it is collapsed so that only the title bar is
2351 visible.
2352*/
2353bool QMdiSubWindow::isShaded() const
2354{
2355 return d_func()->isShadeMode;
2356}
2357
2358/*!
2359 If \a on is true, \a option is enabled on the subwindow; otherwise it is
2360 disabled. See SubWindowOption for the effect of each option.
2361
2362 \sa SubWindowOption, testOption()
2363*/
2364void QMdiSubWindow::setOption(SubWindowOption option, bool on)
2365{
2366 Q_D(QMdiSubWindow);
2367 d->options.setFlag(option, on);
2368
2369#if QT_CONFIG(rubberband)
2370 if ((option & (RubberBandResize | RubberBandMove)) && !on && d->isInRubberBandMode)
2371 d->leaveRubberBandMode();
2372#endif
2373}
2374
2375/*!
2376 Returns \c true if \a option is enabled; otherwise returns \c false.
2377
2378 \sa SubWindowOption, setOption()
2379*/
2380bool QMdiSubWindow::testOption(SubWindowOption option) const
2381{
2382 return d_func()->options & option;
2383}
2384
2385/*!
2386 \property QMdiSubWindow::keyboardSingleStep
2387 \brief sets how far a widget should move or resize when using the
2388 keyboard arrow keys.
2389
2390 When in keyboard-interactive mode, you can use the arrow and page keys to
2391 either move or resize the window. This property controls the arrow keys.
2392 The common way to enter keyboard interactive mode is to enter the
2393 subwindow menu, and select either "resize" or "move".
2394
2395 The default keyboard single step value is 5 pixels.
2396
2397 \sa keyboardPageStep
2398*/
2399int QMdiSubWindow::keyboardSingleStep() const
2400{
2401 return d_func()->keyboardSingleStep;
2402}
2403
2404void QMdiSubWindow::setKeyboardSingleStep(int step)
2405{
2406 // Haven't done any boundary check here since negative step only
2407 // means inverted behavior, which is OK if the user want it.
2408 // A step equal to zero means "do nothing".
2409 d_func()->keyboardSingleStep = step;
2410}
2411
2412/*!
2413 \property QMdiSubWindow::keyboardPageStep
2414 \brief sets how far a widget should move or resize when using the
2415 keyboard page keys.
2416
2417 When in keyboard-interactive mode, you can use the arrow and page keys to
2418 either move or resize the window. This property controls the page
2419 keys. The common way to enter keyboard interactive mode is to enter the
2420 subwindow menu, and select either "resize" or "move".
2421
2422 The default keyboard page step value is 20 pixels.
2423
2424 \sa keyboardSingleStep
2425*/
2426int QMdiSubWindow::keyboardPageStep() const
2427{
2428 return d_func()->keyboardPageStep;
2429}
2430
2431void QMdiSubWindow::setKeyboardPageStep(int step)
2432{
2433 // Haven't done any boundary check here since negative step only
2434 // means inverted behavior, which is OK if the user want it.
2435 // A step equal to zero means "do nothing".
2436 d_func()->keyboardPageStep = step;
2437}
2438
2439#if QT_CONFIG(menu)
2440/*!
2441 Sets \a systemMenu as the current system menu for this subwindow.
2442
2443 By default, each QMdiSubWindow has a standard system menu.
2444
2445 QActions for the system menu created by QMdiSubWindow will
2446 automatically be updated depending on the current window state;
2447 e.g., the minimize action will be disabled after the window is
2448 minimized.
2449
2450 QActions added by the user are not updated by QMdiSubWindow.
2451
2452 QMdiSubWindow takes ownership of \a systemMenu; you do not have to
2453 delete it. Any existing menus will be deleted.
2454
2455 \sa systemMenu(), showSystemMenu()
2456*/
2457void QMdiSubWindow::setSystemMenu(QMenu *systemMenu)
2458{
2459 Q_D(QMdiSubWindow);
2460 if (Q_UNLIKELY(systemMenu && systemMenu == d->systemMenu)) {
2461 qWarning("QMdiSubWindow::setSystemMenu: system menu is already set");
2462 return;
2463 }
2464
2465 if (d->systemMenu) {
2466 delete d->systemMenu;
2467 d->systemMenu = nullptr;
2468 }
2469
2470 if (!systemMenu)
2471 return;
2472
2473 if (systemMenu->parent() != this)
2474 systemMenu->setParent(this);
2475 d->systemMenu = systemMenu;
2476}
2477
2478/*!
2479 Returns a pointer to the current system menu, or zero if no system
2480 menu is set. QMdiSubWindow provides a default system menu, but you can
2481 also set the menu with setSystemMenu().
2482
2483 \sa setSystemMenu(), showSystemMenu()
2484*/
2485QMenu *QMdiSubWindow::systemMenu() const
2486{
2487 return d_func()->systemMenu;
2488}
2489
2490/*!
2491 Shows the system menu below the system menu icon in the title bar.
2492
2493 \sa setSystemMenu(), systemMenu()
2494*/
2495void QMdiSubWindow::showSystemMenu()
2496{
2497 Q_D(QMdiSubWindow);
2498 if (!d->systemMenu)
2499 return;
2500
2501 QPoint globalPopupPos;
2502 if (QWidget *icon = maximizedSystemMenuIconWidget()) {
2503 if (isLeftToRight())
2504 globalPopupPos = icon->mapToGlobal(QPoint(0, icon->y() + icon->height()));
2505 else
2506 globalPopupPos = icon->mapToGlobal(QPoint(icon->width(), icon->y() + icon->height()));
2507 } else {
2508 if (isLeftToRight())
2509 globalPopupPos = mapToGlobal(contentsRect().topLeft());
2510 else // + QPoint(1, 0) because topRight() == QPoint(left() + width() -1, top())
2511 globalPopupPos = mapToGlobal(contentsRect().topRight()) + QPoint(1, 0);
2512 }
2513
2514 // Adjust x() with -menuwidth in reverse mode.
2515 if (isRightToLeft())
2516 globalPopupPos -= QPoint(d->systemMenu->sizeHint().width(), 0);
2517 d->systemMenu->popup(globalPopupPos);
2518}
2519#endif // QT_CONFIG(menu)
2520
2521/*!
2522 \since 4.4
2523
2524 Returns the area containing this sub-window, or \nullptr if there
2525 is none.
2526
2527 \sa QMdiArea::addSubWindow()
2528*/
2529QMdiArea *QMdiSubWindow::mdiArea() const
2530{
2531 QWidget *parent = parentWidget();
2532 while (parent) {
2533 if (QMdiArea *area = qobject_cast<QMdiArea *>(parent)) {
2534 if (area->viewport() == parentWidget())
2535 return area;
2536 }
2537 parent = parent->parentWidget();
2538 }
2539 return nullptr;
2540}
2541
2542/*!
2543 Calling this function makes the subwindow enter the shaded mode.
2544 When the subwindow is shaded, only the title bar is visible.
2545
2546 Although shading is not supported by all styles, this function will
2547 still show the subwindow as shaded, regardless of whether support
2548 for shading is available. However, when used with styles without
2549 shading support, the user will be unable to return from shaded mode
2550 through the user interface (e.g., through a shade button in the title
2551 bar).
2552
2553 \sa isShaded()
2554*/
2555void QMdiSubWindow::showShaded()
2556{
2557 if (!parent())
2558 return;
2559
2560 Q_D(QMdiSubWindow);
2561 // setMinimizeMode uses this function.
2562 if (!d->isShadeRequestFromMinimizeMode && isShaded())
2563 return;
2564
2565 d->isMaximizeMode = false;
2566
2567 d->storeFocusWidget();
2568
2569 if (!d->isShadeRequestFromMinimizeMode) {
2570 d->isShadeMode = true;
2571 d->ensureWindowState(Qt::WindowMinimized);
2572 }
2573
2574#if QT_CONFIG(menubar)
2575 d->removeButtonsFromMenuBar();
2576#endif
2577
2578 // showMinimized() will reset Qt::WindowActive, which makes sense
2579 // for top level widgets, but in MDI it makes sense to have an
2580 // active window which is minimized.
2581 if (hasFocus() || isAncestorOf(QApplication::focusWidget()))
2582 d->ensureWindowState(Qt::WindowActive);
2583
2584#if QT_CONFIG(sizegrip)
2585 d->setSizeGripVisible(false);
2586#endif
2587
2588 if (!d->restoreSize.isValid() || d->isShadeMode) {
2589 d->oldGeometry = geometry();
2590 d->restoreSize.setWidth(d->oldGeometry.width());
2591 d->restoreSize.setHeight(d->oldGeometry.height());
2592 }
2593
2594 // Hide the window before we change the geometry to avoid multiple resize
2595 // events and wrong window state.
2596 const bool wasVisible = isVisible();
2597 if (wasVisible)
2598 setVisible(false);
2599
2600 d->updateGeometryConstraints();
2601 // Update minimum size to internalMinimumSize if set by user.
2602 if (!minimumSize().isNull()) {
2603 d->userMinimumSize = minimumSize();
2604 setMinimumSize(d->internalMinimumSize);
2605 }
2606 resize(d->internalMinimumSize);
2607
2608 // Hide the internal widget if not already hidden by the user.
2609 if (d->baseWidget && !d->baseWidget->isHidden() && !(windowFlags() & Qt::FramelessWindowHint)) {
2610 d->baseWidget->hide();
2611 d->isWidgetHiddenByUs = true;
2612 }
2613
2614 if (wasVisible)
2615 setVisible(true);
2616
2617 d->setFocusWidget();
2618 d->resizeEnabled = false;
2619 d->moveEnabled = true;
2620 d->updateDirtyRegions();
2621 d->updateMask();
2622
2623#ifndef QT_NO_ACTION
2624 d->setEnabled(QMdiSubWindowPrivate::MinimizeAction, false);
2625 d->setEnabled(QMdiSubWindowPrivate::ResizeAction, d->resizeEnabled);
2626 d->setEnabled(QMdiSubWindowPrivate::MaximizeAction, true);
2627 d->setEnabled(QMdiSubWindowPrivate::RestoreAction, true);
2628 d->setEnabled(QMdiSubWindowPrivate::MoveAction, d->moveEnabled);
2629#endif
2630}
2631
2632/*!
2633 \reimp
2634*/
2635bool QMdiSubWindow::eventFilter(QObject *object, QEvent *event)
2636{
2637 Q_D(QMdiSubWindow);
2638 if (!object)
2639 return QWidget::eventFilter(object, event);
2640
2641#if QT_CONFIG(menu)
2642 // System menu events.
2643 if (d->systemMenu && d->systemMenu == object) {
2644 if (event->type() == QEvent::MouseButtonDblClick) {
2645 const QMouseEvent *mouseEvent = static_cast<const QMouseEvent *>(event);
2646 const QAction *action = d->systemMenu->actionAt(mouseEvent->position().toPoint());
2647 if (!action || action->isEnabled())
2648 close();
2649 } else if (event->type() == QEvent::MouseMove) {
2650 QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
2651 d->hoveredSubControl = d->getSubControl(mapFromGlobal(mouseEvent->globalPosition().toPoint()));
2652 } else if (event->type() == QEvent::Hide) {
2653 d->activeSubControl = QStyle::SC_None;
2654 update(QRegion(0, 0, width(), d->titleBarHeight()));
2655 }
2656 return QWidget::eventFilter(object, event);
2657 }
2658#endif
2659
2660#if QT_CONFIG(sizegrip)
2661 if (object != d->baseWidget && parent() && qobject_cast<QSizeGrip *>(object)) {
2662 if (event->type() != QEvent::MouseButtonPress || !testOption(QMdiSubWindow::RubberBandResize))
2663 return QWidget::eventFilter(object, event);
2664 const QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
2665 d->mousePressPosition = parentWidget()->mapFromGlobal(mouseEvent->globalPosition().toPoint());
2666 d->oldGeometry = geometry();
2667 d->currentOperation = isLeftToRight() ? QMdiSubWindowPrivate::BottomRightResize
2668 : QMdiSubWindowPrivate::BottomLeftResize;
2669#if QT_CONFIG(rubberband)
2670 d->enterRubberBandMode();
2671#endif
2672 return true;
2673 }
2674#endif
2675
2676 if (object != d->baseWidget && event->type() != QEvent::WindowTitleChange)
2677 return QWidget::eventFilter(object, event);
2678
2679 switch (event->type()) {
2680 case QEvent::Show:
2681 d->setActive(true);
2682 break;
2683 case QEvent::ShowToParent:
2684 if (!d->isWidgetHiddenByUs)
2685 show();
2686 break;
2687 case QEvent::WindowStateChange: {
2688 QWindowStateChangeEvent *changeEvent = static_cast<QWindowStateChangeEvent*>(event);
2689 if (changeEvent->isOverride())
2690 break;
2691 Qt::WindowStates oldState = changeEvent->oldState();
2692 Qt::WindowStates newState = d->baseWidget->windowState();
2693 if (!(oldState & Qt::WindowMinimized) && (newState & Qt::WindowMinimized))
2694 showMinimized();
2695 else if (!(oldState & Qt::WindowMaximized) && (newState & Qt::WindowMaximized))
2696 showMaximized();
2697 else if (!(newState & (Qt::WindowMaximized | Qt::WindowMinimized | Qt::WindowFullScreen)))
2698 showNormal();
2699 break;
2700 }
2701 case QEvent::Enter:
2702 d->currentOperation = QMdiSubWindowPrivate::None;
2703 d->updateCursor();
2704 break;
2705 case QEvent::LayoutRequest:
2706 d->updateGeometryConstraints();
2707 break;
2708 case QEvent::WindowTitleChange:
2709 if (d->ignoreWindowTitleChange)
2710 break;
2711 if (object == d->baseWidget) {
2712 d->updateWindowTitle(true);
2713 d->lastChildWindowTitle = d->baseWidget->windowTitle();
2714#if QT_CONFIG(menubar)
2715 } else if (maximizedButtonsWidget() && d->controlContainer->menuBar() && d->controlContainer->menuBar()
2716 ->cornerWidget(Qt::TopRightCorner) == maximizedButtonsWidget()) {
2717 d->originalTitle.clear();
2718 if (d->baseWidget && d->baseWidget->windowTitle() == windowTitle())
2719 d->updateWindowTitle(true);
2720 else
2721 d->updateWindowTitle(false);
2722#endif
2723 }
2724 break;
2725 case QEvent::ModifiedChange: {
2726 if (object != d->baseWidget)
2727 break;
2728 bool windowModified = d->baseWidget->isWindowModified();
2729 if (!windowModified && d->baseWidget->windowTitle() != windowTitle())
2730 break;
2731 if (windowTitle().contains("[*]"_L1))
2732 setWindowModified(windowModified);
2733 break;
2734 }
2735 default:
2736 break;
2737 }
2738 return QWidget::eventFilter(object, event);
2739}
2740
2741/*!
2742 \reimp
2743*/
2744bool QMdiSubWindow::event(QEvent *event)
2745{
2746 Q_D(QMdiSubWindow);
2747 switch (event->type()) {
2748 case QEvent::StyleChange: {
2749 bool wasShaded = isShaded();
2750 bool wasMinimized = isMinimized();
2751 bool wasMaximized = isMaximized();
2752 // Don't emit subWindowActivated, the app doesn't have to know about our hacks
2753 const QScopedValueRollback<bool> activationEnabledSaver(d->activationEnabled);
2754 d->activationEnabled = false;
2755
2756 ensurePolished();
2757 setContentsMargins(0, 0, 0, 0);
2758 if (wasMinimized || wasMaximized || wasShaded)
2759 showNormal();
2760 d->updateGeometryConstraints();
2761 resize(d->internalMinimumSize.expandedTo(size()));
2762 d->updateMask();
2763 d->updateDirtyRegions();
2764 if (wasShaded)
2765 showShaded();
2766 else if (wasMinimized)
2767 showMinimized();
2768 else if (wasMaximized)
2769 showMaximized();
2770 break;
2771 }
2772 case QEvent::ParentAboutToChange:
2773 d->setActive(false);
2774 break;
2775 case QEvent::ParentChange: {
2776 bool wasResized = testAttribute(Qt::WA_Resized);
2777#if QT_CONFIG(menubar)
2778 d->removeButtonsFromMenuBar();
2779#endif
2780 d->currentOperation = QMdiSubWindowPrivate::None;
2781 d->activeSubControl = QStyle::SC_None;
2782 d->hoveredSubControl = QStyle::SC_None;
2783#if QT_CONFIG(rubberband)
2784 if (d->isInRubberBandMode)
2785 d->leaveRubberBandMode();
2786#endif
2787 d->isShadeMode = false;
2788 d->isMaximizeMode = false;
2789 d->isWidgetHiddenByUs = false;
2790 if (!parent()) {
2791#if QT_CONFIG(sizegrip)
2792 if (isMacStyle(style()))
2793 delete d->sizeGrip;
2794#endif
2795 setOption(RubberBandResize, false);
2796 setOption(RubberBandMove, false);
2797 } else {
2798 d->setWindowFlags(windowFlags());
2799 }
2800 setContentsMargins(0, 0, 0, 0);
2801 d->updateGeometryConstraints();
2802 d->updateCursor();
2803 d->updateMask();
2804 d->updateDirtyRegions();
2805 d->updateActions();
2806 if (!wasResized && testAttribute(Qt::WA_Resized))
2807 setAttribute(Qt::WA_Resized, false);
2808 break;
2809 }
2810 case QEvent::WindowActivate:
2811 if (d->ignoreNextActivationEvent) {
2812 d->ignoreNextActivationEvent = false;
2813 break;
2814 }
2815 d->isExplicitlyDeactivated = false;
2816 d->setActive(true);
2817 break;
2818 case QEvent::WindowDeactivate:
2819 if (d->ignoreNextActivationEvent) {
2820 d->ignoreNextActivationEvent = false;
2821 break;
2822 }
2823 d->isExplicitlyDeactivated = true;
2824 d->setActive(false);
2825 break;
2826 case QEvent::WindowTitleChange:
2827 if (!d->ignoreWindowTitleChange)
2828 d->updateWindowTitle(false);
2829 d->updateInternalWindowTitle();
2830 break;
2831 case QEvent::ModifiedChange:
2832 if (!windowTitle().contains("[*]"_L1))
2833 break;
2834#if QT_CONFIG(menubar)
2835 if (maximizedButtonsWidget() && d->controlContainer->menuBar() && d->controlContainer->menuBar()
2836 ->cornerWidget(Qt::TopRightCorner) == maximizedButtonsWidget()) {
2837 window()->setWindowModified(isWindowModified());
2838 }
2839#endif // QT_CONFIG(menubar)
2840 d->updateInternalWindowTitle();
2841 break;
2842 case QEvent::LayoutDirectionChange:
2843 d->updateDirtyRegions();
2844 break;
2845 case QEvent::LayoutRequest:
2846 d->updateGeometryConstraints();
2847 break;
2848 case QEvent::WindowIconChange:
2849 d->menuIcon = windowIcon();
2850 if (d->menuIcon.isNull())
2851 d->menuIcon = style()->standardIcon(QStyle::SP_TitleBarMenuButton, nullptr, this);
2852 if (d->controlContainer)
2853 d->controlContainer->updateWindowIcon(d->menuIcon);
2854 if (!maximizedSystemMenuIconWidget())
2855 update(0, 0, width(), d->titleBarHeight());
2856 break;
2857 case QEvent::PaletteChange:
2858 d->titleBarPalette = d->desktopPalette();
2859 break;
2860 case QEvent::FontChange:
2861 d->font = font();
2862 break;
2863#if QT_CONFIG(tooltip)
2864 case QEvent::ToolTip:
2865 showToolTip(static_cast<QHelpEvent *>(event), this, d->titleBarOptions(),
2866 QStyle::CC_TitleBar, d->hoveredSubControl);
2867 break;
2868#endif
2869#ifndef QT_NO_ACTION
2870 case QEvent::ActionAdded:
2871 case QEvent::ActionChanged:
2872 case QEvent::ActionRemoved:
2873 update();
2874 break;
2875#endif
2876 default:
2877 break;
2878 }
2879 return QWidget::event(event);
2880}
2881
2882/*!
2883 \reimp
2884*/
2885void QMdiSubWindow::showEvent(QShowEvent *showEvent)
2886{
2887 Q_D(QMdiSubWindow);
2888 if (!parent()) {
2889 QWidget::showEvent(showEvent);
2890 return;
2891 }
2892
2893#if QT_CONFIG(sizegrip)
2894 if (isMacStyle(style()) && !d->sizeGrip
2895 && !(windowFlags() & Qt::FramelessWindowHint)) {
2896 d->setSizeGrip(new QSizeGrip(this));
2897 Q_ASSERT(d->sizeGrip);
2898 if (isMinimized())
2899 d->setSizeGripVisible(false);
2900 else
2901 d->setSizeGripVisible(true);
2902 resize(size().expandedTo(d->internalMinimumSize));
2903 }
2904#endif
2905
2906 d->updateDirtyRegions();
2907 // Show buttons in the menu bar if they're already not there.
2908 // We want to do this when QMdiSubWindow becomes visible after being hidden.
2909#if QT_CONFIG(menubar)
2910 if (d->controlContainer) {
2911 if (QMenuBar *menuBar = d->menuBar()) {
2912 if (menuBar->cornerWidget(Qt::TopRightCorner) != maximizedButtonsWidget())
2913 d->showButtonsInMenuBar(menuBar);
2914 }
2915 }
2916#endif
2917 d->setActive(true);
2918}
2919
2920/*!
2921 \reimp
2922*/
2923void QMdiSubWindow::hideEvent(QHideEvent * /*hideEvent*/)
2924{
2925#if QT_CONFIG(menubar)
2926 d_func()->removeButtonsFromMenuBar();
2927#endif
2928}
2929
2930/*!
2931 \reimp
2932*/
2933void QMdiSubWindow::changeEvent(QEvent *changeEvent)
2934{
2935 if (!parent()) {
2936 QWidget::changeEvent(changeEvent);
2937 return;
2938 }
2939
2940 if (changeEvent->type() != QEvent::WindowStateChange) {
2941 QWidget::changeEvent(changeEvent);
2942 return;
2943 }
2944
2945 QWindowStateChangeEvent *event = static_cast<QWindowStateChangeEvent *>(changeEvent);
2946 if (event->isOverride()) {
2947 event->ignore();
2948 return;
2949 }
2950
2951 Qt::WindowStates oldState = event->oldState();
2952 Qt::WindowStates newState = windowState();
2953 if (oldState == newState) {
2954 changeEvent->ignore();
2955 return;
2956 }
2957
2958 // QWidget ensures that the widget is visible _after_ setWindowState(),
2959 // but we need to ensure that the widget is visible _before_
2960 // setWindowState() returns.
2961 Q_D(QMdiSubWindow);
2962 if (!isVisible()) {
2963 d->ensureWindowState(Qt::WindowNoState);
2964 setVisible(true);
2965 }
2966
2967 if (!d->oldGeometry.isValid())
2968 d->oldGeometry = geometry();
2969
2970 if ((oldState & Qt::WindowActive) && (newState & Qt::WindowActive))
2971 d->currentOperation = QMdiSubWindowPrivate::None;
2972
2973 if (!(oldState & Qt::WindowMinimized) && (newState & Qt::WindowMinimized))
2974 d->setMinimizeMode();
2975 else if (!(oldState & Qt::WindowMaximized) && (newState & Qt::WindowMaximized))
2976 d->setMaximizeMode();
2977 else if (!(newState & (Qt::WindowMaximized | Qt::WindowMinimized | Qt::WindowFullScreen)))
2978 d->setNormalMode();
2979
2980 if (d->isActive)
2981 d->ensureWindowState(Qt::WindowActive);
2982 if (d->activationEnabled)
2983 emit windowStateChanged(oldState, windowState());
2984}
2985
2986/*!
2987 \reimp
2988*/
2989void QMdiSubWindow::closeEvent(QCloseEvent *closeEvent)
2990{
2991 Q_D(QMdiSubWindow);
2992 bool acceptClose = true;
2993 if (d->baseWidget)
2994 acceptClose = d->baseWidget->close();
2995 if (!acceptClose) {
2996 closeEvent->ignore();
2997 return;
2998 }
2999#if QT_CONFIG(menubar)
3000 d->removeButtonsFromMenuBar();
3001#endif
3002 d->setActive(false);
3003 if (parentWidget() && testAttribute(Qt::WA_DeleteOnClose)) {
3004 QChildEvent childRemoved(QEvent::ChildRemoved, this);
3005 QCoreApplication::sendEvent(parentWidget(), &childRemoved);
3006 }
3007 closeEvent->accept();
3008}
3009
3010/*!
3011 \reimp
3012*/
3013void QMdiSubWindow::leaveEvent(QEvent * /*leaveEvent*/)
3014{
3015 Q_D(QMdiSubWindow);
3016 if (d->hoveredSubControl != QStyle::SC_None) {
3017 d->hoveredSubControl = QStyle::SC_None;
3018 update(QRegion(0, 0, width(), d->titleBarHeight()));
3019 }
3020}
3021
3022/*!
3023 \reimp
3024
3025 \warning When maximizing or restoring a subwindow, the resulting call to this function
3026 may have an invalid QResizeEvent::oldSize().
3027*/
3028void QMdiSubWindow::resizeEvent(QResizeEvent *resizeEvent)
3029{
3030 Q_D(QMdiSubWindow);
3031#if QT_CONFIG(sizegrip)
3032 if (d->sizeGrip) {
3033 d->sizeGrip->move(isLeftToRight() ? width() - d->sizeGrip->width() : 0,
3034 height() - d->sizeGrip->height());
3035 }
3036#endif
3037
3038 if (!parent()) {
3039 QWidget::resizeEvent(resizeEvent);
3040 return;
3041 }
3042
3043 if (d->isMaximizeMode)
3044 d->ensureWindowState(Qt::WindowMaximized);
3045
3046 d->updateMask();
3047 if (!isVisible())
3048 return;
3049
3050 if (!d->resizeTimer.isActive())
3051 d->cachedStyleOptions = d->titleBarOptions();
3052 d->resizeTimer.start(200ms, this);
3053}
3054
3055/*!
3056 \reimp
3057*/
3058void QMdiSubWindow::timerEvent(QTimerEvent *timerEvent)
3059{
3060 Q_D(QMdiSubWindow);
3061 if (timerEvent->id() == d->resizeTimer.id()) {
3062 d->resizeTimer.stop();
3063 d->updateDirtyRegions();
3064 }
3065}
3066
3067/*!
3068 \reimp
3069*/
3070void QMdiSubWindow::moveEvent(QMoveEvent *moveEvent)
3071{
3072 if (!parent()) {
3073 QWidget::moveEvent(moveEvent);
3074 return;
3075 }
3076
3077 Q_D(QMdiSubWindow);
3078 if (d->isMaximizeMode)
3079 d->ensureWindowState(Qt::WindowMaximized);
3080}
3081
3082/*!
3083 \reimp
3084*/
3085void QMdiSubWindow::paintEvent(QPaintEvent *paintEvent)
3086{
3087 if (!parent() || (windowFlags() & Qt::FramelessWindowHint)) {
3088 QWidget::paintEvent(paintEvent);
3089 return;
3090 }
3091
3092 Q_D(QMdiSubWindow);
3093
3094 if (d->resizeTimer.isActive()) {
3095 // Only update the style option rect and the window title.
3096 int border = d->hasBorder(d->cachedStyleOptions) ? 4 : 0;
3097 int titleBarHeight = d->titleBarHeight(d->cachedStyleOptions);
3098 titleBarHeight -= isMinimized() ? 2 * border : border;
3099 d->cachedStyleOptions.rect = QRect(border, border, width() - 2 * border, titleBarHeight);
3100 if (!d->windowTitle.isEmpty()) {
3101 int width = style()->subControlRect(QStyle::CC_TitleBar, &d->cachedStyleOptions,
3102 QStyle::SC_TitleBarLabel, this).width();
3103 d->cachedStyleOptions.text = d->cachedStyleOptions.fontMetrics
3104 .elidedText(d->windowTitle, Qt::ElideRight, width);
3105 }
3106 } else {
3107 // Force full update.
3108 d->cachedStyleOptions = d->titleBarOptions();
3109 }
3110
3111 QStylePainter painter(this);
3112 QStyleOptionFrame frameOptions;
3113 frameOptions.initFrom(this);
3114 frameOptions.state.setFlag(QStyle::State_Active, d->isActive);
3115 if (isMaximized() && !d->drawTitleBarWhenMaximized()) {
3116 if (!autoFillBackground() && (!widget() || !qt_widget_private(widget())->isOpaque)) {
3117 // make sure we paint all pixels of a maximized QMdiSubWindow if no-one else does
3118 painter.drawPrimitive(QStyle::PE_FrameWindow, frameOptions);
3119 }
3120 return;
3121 }
3122
3123 if (!d->windowTitle.isEmpty())
3124 painter.setFont(d->font);
3125 painter.drawComplexControl(QStyle::CC_TitleBar, d->cachedStyleOptions);
3126
3127 if (isMinimized() && !d->hasBorder(d->cachedStyleOptions))
3128 return;
3129
3130 frameOptions.lineWidth = style()->pixelMetric(QStyle::PM_MdiSubWindowFrameWidth, nullptr, this);
3131
3132 // ### Ensure that we do not require setting the cliprect for 4.4
3133 if (!isMinimized() && !d->hasBorder(d->cachedStyleOptions))
3134 painter.setClipRect(rect().adjusted(0, d->titleBarHeight(d->cachedStyleOptions), 0, 0));
3135 if (!isMinimized() || d->hasBorder(d->cachedStyleOptions))
3136 painter.drawPrimitive(QStyle::PE_FrameWindow, frameOptions);
3137}
3138
3139/*!
3140 \reimp
3141*/
3142void QMdiSubWindow::mousePressEvent(QMouseEvent *mouseEvent)
3143{
3144 if (!parent()) {
3145 QWidget::mousePressEvent(mouseEvent);
3146 return;
3147 }
3148
3149 Q_D(QMdiSubWindow);
3150 if (d->isInInteractiveMode)
3151 d->leaveInteractiveMode();
3152#if QT_CONFIG(rubberband)
3153 if (d->isInRubberBandMode)
3154 d->leaveRubberBandMode();
3155#endif
3156
3157 if (mouseEvent->button() != Qt::LeftButton) {
3158 mouseEvent->ignore();
3159 return;
3160 }
3161
3162 if (d->currentOperation != QMdiSubWindowPrivate::None) {
3163 d->updateCursor();
3164 d->mousePressPosition = mapToParent(mouseEvent->position().toPoint());
3165 if (d->resizeEnabled || d->moveEnabled)
3166 d->oldGeometry = geometry();
3167#if QT_CONFIG(rubberband)
3168 if ((testOption(QMdiSubWindow::RubberBandResize) && d->isResizeOperation())
3169 || (testOption(QMdiSubWindow::RubberBandMove) && d->isMoveOperation())) {
3170 d->enterRubberBandMode();
3171 }
3172#endif
3173 return;
3174 }
3175
3176 d->activeSubControl = d->hoveredSubControl;
3177#if QT_CONFIG(menu)
3178 if (d->activeSubControl == QStyle::SC_TitleBarSysMenu)
3179 showSystemMenu();
3180 else
3181#endif
3182 update(QRegion(0, 0, width(), d->titleBarHeight()));
3183}
3184
3185/*!
3186 \reimp
3187*/
3188void QMdiSubWindow::mouseDoubleClickEvent(QMouseEvent *mouseEvent)
3189{
3190 if (!parent()) {
3191 QWidget::mouseDoubleClickEvent(mouseEvent);
3192 return;
3193 }
3194
3195 if (mouseEvent->button() != Qt::LeftButton) {
3196 mouseEvent->ignore();
3197 return;
3198 }
3199
3200 Q_D(QMdiSubWindow);
3201 if (!d->isMoveOperation()) {
3202#if QT_CONFIG(menu)
3203 if (d->hoveredSubControl == QStyle::SC_TitleBarSysMenu)
3204 close();
3205#endif
3206 return;
3207 }
3208
3209 Qt::WindowFlags flags = windowFlags();
3210 if (isMinimized()) {
3211 if ((isShaded() && (flags & Qt::WindowShadeButtonHint))
3212 || (flags & Qt::WindowMinimizeButtonHint)) {
3213 showNormal();
3214 }
3215 return;
3216 }
3217
3218 if (isMaximized()) {
3219 if (flags & Qt::WindowMaximizeButtonHint)
3220 showNormal();
3221 return;
3222 }
3223
3224 if (flags & Qt::WindowShadeButtonHint)
3225 showShaded();
3226 else if (flags & Qt::WindowMaximizeButtonHint)
3227 showMaximized();
3228}
3229
3230/*!
3231 \reimp
3232*/
3233void QMdiSubWindow::mouseReleaseEvent(QMouseEvent *mouseEvent)
3234{
3235 if (!parent()) {
3236 QWidget::mouseReleaseEvent(mouseEvent);
3237 return;
3238 }
3239
3240 if (mouseEvent->button() != Qt::LeftButton) {
3241 mouseEvent->ignore();
3242 return;
3243 }
3244
3245 Q_D(QMdiSubWindow);
3246 if (d->currentOperation != QMdiSubWindowPrivate::None) {
3247#if QT_CONFIG(rubberband)
3248 if (d->isInRubberBandMode && !d->isInInteractiveMode)
3249 d->leaveRubberBandMode();
3250#endif
3251 if (d->resizeEnabled || d->moveEnabled)
3252 d->oldGeometry = geometry();
3253 }
3254
3255 d->currentOperation = d->getOperation(mouseEvent->position().toPoint());
3256 d->updateCursor();
3257
3258 d->hoveredSubControl = d->getSubControl(mouseEvent->position().toPoint());
3259 if (d->activeSubControl != QStyle::SC_None
3260 && d->activeSubControl == d->hoveredSubControl) {
3261 d->processClickedSubControl();
3262 }
3263 d->activeSubControl = QStyle::SC_None;
3264 update(QRegion(0, 0, width(), d->titleBarHeight()));
3265}
3266
3267/*!
3268 \reimp
3269*/
3270void QMdiSubWindow::mouseMoveEvent(QMouseEvent *mouseEvent)
3271{
3272 if (!parent()) {
3273 QWidget::mouseMoveEvent(mouseEvent);
3274 return;
3275 }
3276
3277 Q_D(QMdiSubWindow);
3278 // No update needed if we're in a move/resize operation.
3279 if (!d->isMoveOperation() && !d->isResizeOperation()) {
3280 // Find previous and current hover region.
3281 const QStyleOptionTitleBar options = d->titleBarOptions();
3282 QStyle::SubControl oldHover = d->hoveredSubControl;
3283 d->hoveredSubControl = d->getSubControl(mouseEvent->position().toPoint());
3284 QRegion hoverRegion;
3285 if (isHoverControl(oldHover) && oldHover != d->hoveredSubControl)
3286 hoverRegion += style()->subControlRect(QStyle::CC_TitleBar, &options, oldHover, this);
3287 if (isHoverControl(d->hoveredSubControl) && d->hoveredSubControl != oldHover) {
3288 hoverRegion += style()->subControlRect(QStyle::CC_TitleBar, &options,
3289 d->hoveredSubControl, this);
3290 }
3291
3292 if (isMacStyle(style()) && !hoverRegion.isEmpty())
3293 hoverRegion += QRegion(0, 0, width(), d->titleBarHeight(options));
3294
3295 if (!hoverRegion.isEmpty())
3296 update(hoverRegion);
3297 }
3298
3299 if ((mouseEvent->buttons() & Qt::LeftButton) || d->isInInteractiveMode) {
3300 if ((d->isResizeOperation() && d->resizeEnabled) || (d->isMoveOperation() && d->moveEnabled)) {
3301 // As setNewGeometry moves the window, it invalidates the pos() value of any mouse move events that are
3302 // currently queued in the event loop. Map to parent using globalPos() instead.
3303 d->setNewGeometry(parentWidget()->mapFromGlobal(mouseEvent->globalPosition().toPoint()));
3304 }
3305 return;
3306 }
3307
3308 // Do not resize/move if not allowed.
3309 d->currentOperation = d->getOperation(mouseEvent->position().toPoint());
3310 if ((d->isResizeOperation() && !d->resizeEnabled) || (d->isMoveOperation() && !d->moveEnabled))
3311 d->currentOperation = QMdiSubWindowPrivate::None;
3312 d->updateCursor();
3313}
3314
3315/*!
3316 \reimp
3317*/
3318void QMdiSubWindow::keyPressEvent(QKeyEvent *keyEvent)
3319{
3320 Q_D(QMdiSubWindow);
3321 if (!d->isInInteractiveMode || !parent()) {
3322 keyEvent->ignore();
3323 return;
3324 }
3325
3326 QPoint delta;
3327 switch (keyEvent->key()) {
3328 case Qt::Key_Right:
3329 if (keyEvent->modifiers() & Qt::ShiftModifier)
3330 delta = QPoint(d->keyboardPageStep, 0);
3331 else
3332 delta = QPoint(d->keyboardSingleStep, 0);
3333 break;
3334 case Qt::Key_Up:
3335 if (keyEvent->modifiers() & Qt::ShiftModifier)
3336 delta = QPoint(0, -d->keyboardPageStep);
3337 else
3338 delta = QPoint(0, -d->keyboardSingleStep);
3339 break;
3340 case Qt::Key_Left:
3341 if (keyEvent->modifiers() & Qt::ShiftModifier)
3342 delta = QPoint(-d->keyboardPageStep, 0);
3343 else
3344 delta = QPoint(-d->keyboardSingleStep, 0);
3345 break;
3346 case Qt::Key_Down:
3347 if (keyEvent->modifiers() & Qt::ShiftModifier)
3348 delta = QPoint(0, d->keyboardPageStep);
3349 else
3350 delta = QPoint(0, d->keyboardSingleStep);
3351 break;
3352 case Qt::Key_Escape:
3353 case Qt::Key_Return:
3354 case Qt::Key_Enter:
3355 d->leaveInteractiveMode();
3356 return;
3357 default:
3358 keyEvent->ignore();
3359 return;
3360 }
3361
3362#ifndef QT_NO_CURSOR
3363 QPoint newPosition = parentWidget()->mapFromGlobal(cursor().pos() + delta);
3364 QRect oldGeometry =
3365#if QT_CONFIG(rubberband)
3366 d->isInRubberBandMode ? d->rubberBand->geometry() :
3367#endif
3368 geometry();
3369 d->setNewGeometry(newPosition);
3370 QRect currentGeometry =
3371#if QT_CONFIG(rubberband)
3372 d->isInRubberBandMode ? d->rubberBand->geometry() :
3373#endif
3374 geometry();
3375 if (currentGeometry == oldGeometry)
3376 return;
3377
3378 // Update cursor position
3379
3380 QPoint actualDelta;
3381 if (d->isMoveOperation()) {
3382 actualDelta = QPoint(currentGeometry.x() - oldGeometry.x(),
3383 currentGeometry.y() - oldGeometry.y());
3384 } else {
3385 int dx = isLeftToRight() ? currentGeometry.width() - oldGeometry.width()
3386 : currentGeometry.x() - oldGeometry.x();
3387 actualDelta = QPoint(dx, currentGeometry.height() - oldGeometry.height());
3388 }
3389
3390 // Adjust in case we weren't able to move as long as wanted.
3391 if (actualDelta != delta)
3392 newPosition += (actualDelta - delta);
3393 cursor().setPos(parentWidget()->mapToGlobal(newPosition));
3394#endif
3395}
3396
3397#ifndef QT_NO_CONTEXTMENU
3398/*!
3399 \reimp
3400*/
3401void QMdiSubWindow::contextMenuEvent(QContextMenuEvent *contextMenuEvent)
3402{
3403 Q_D(QMdiSubWindow);
3404 if (!d->systemMenu) {
3405 contextMenuEvent->ignore();
3406 return;
3407 }
3408
3409 if (d->hoveredSubControl == QStyle::SC_TitleBarSysMenu
3410 || d->getRegion(QMdiSubWindowPrivate::Move).contains(contextMenuEvent->pos())) {
3411 d->systemMenu->exec(contextMenuEvent->globalPos());
3412 } else {
3413 contextMenuEvent->ignore();
3414 }
3415}
3416#endif // QT_NO_CONTEXTMENU
3417
3418/*!
3419 \reimp
3420*/
3421void QMdiSubWindow::focusInEvent(QFocusEvent *focusInEvent)
3422{
3423 d_func()->focusInReason = focusInEvent->reason();
3424}
3425
3426/*!
3427 \reimp
3428*/
3429void QMdiSubWindow::focusOutEvent(QFocusEvent * /*focusOutEvent*/)
3430{
3431 // To avoid update() in QWidget::focusOutEvent.
3432}
3433
3434/*!
3435 \reimp
3436*/
3437void QMdiSubWindow::childEvent(QChildEvent *childEvent)
3438{
3439 if (childEvent->type() != QEvent::ChildPolished)
3440 return;
3441#if QT_CONFIG(sizegrip)
3442 if (QSizeGrip *sizeGrip = qobject_cast<QSizeGrip *>(childEvent->child()))
3443 d_func()->setSizeGrip(sizeGrip);
3444#endif
3445}
3446
3447/*!
3448 \reimp
3449*/
3450QSize QMdiSubWindow::sizeHint() const
3451{
3452 Q_D(const QMdiSubWindow);
3453 int margin, minWidth;
3454 d->sizeParameters(&margin, &minWidth);
3455 QSize size(2 * margin, d->titleBarHeight() + margin);
3456 if (d->baseWidget && d->baseWidget->sizeHint().isValid())
3457 size += d->baseWidget->sizeHint();
3458 return size.expandedTo(minimumSizeHint());
3459}
3460
3461/*!
3462 \reimp
3463*/
3464QSize QMdiSubWindow::minimumSizeHint() const
3465{
3466 Q_D(const QMdiSubWindow);
3467 if (isVisible())
3468 ensurePolished();
3469
3470 // Minimized window.
3471 if (parent() && isMinimized() && !isShaded())
3472 return d->iconSize();
3473
3474 // Calculate window decoration.
3475 int margin, minWidth;
3476 d->sizeParameters(&margin, &minWidth);
3477 int decorationHeight = margin + d->titleBarHeight();
3478 int minHeight = decorationHeight;
3479
3480 // Shaded window.
3481 if (parent() && isShaded())
3482 return QSize(qMax(minWidth, width()), d->titleBarHeight());
3483
3484 // Content
3485 if (layout()) {
3486 QSize minLayoutSize = layout()->minimumSize();
3487 if (minLayoutSize.isValid()) {
3488 minWidth = qMax(minWidth, minLayoutSize.width() + 2 * margin);
3489 minHeight += minLayoutSize.height();
3490 }
3491 } else if (d->baseWidget && d->baseWidget->isVisible()) {
3492 QSize minBaseWidgetSize = d->baseWidget->minimumSizeHint();
3493 if (minBaseWidgetSize.isValid()) {
3494 minWidth = qMax(minWidth, minBaseWidgetSize.width() + 2 * margin);
3495 minHeight += minBaseWidgetSize.height();
3496 }
3497 }
3498
3499#if QT_CONFIG(sizegrip)
3500 // SizeGrip
3501 int sizeGripHeight = 0;
3502 if (d->sizeGrip && d->sizeGrip->isVisibleTo(const_cast<QMdiSubWindow *>(this)))
3503 sizeGripHeight = d->sizeGrip->height();
3504 else if (parent() && isMacStyle(style()) && !d->sizeGrip)
3505 sizeGripHeight = style()->pixelMetric(QStyle::PM_SizeGripSize, nullptr, this);
3506 minHeight = qMax(minHeight, decorationHeight + sizeGripHeight);
3507#endif
3508
3509 return QSize(minWidth, minHeight);
3510}
3511
3512QT_END_NAMESPACE
3513
3514#include "moc_qmdisubwindow.cpp"
3515#include "qmdisubwindow.moc"
void updateWindowIcon(const QIcon &windowIcon)
ControlContainer(QMdiSubWindow *mdiChild)
QSize sizeHint() const override
bool event(QEvent *event) override
This virtual function receives events to an object and should return true if the event e was recogniz...
void mousePressEvent(QMouseEvent *mouseEvent) override
This event handler, for event event, can be reimplemented in a subclass to receive mouse press events...
void mouseReleaseEvent(QMouseEvent *mouseEvent) override
This event handler, for event event, can be reimplemented in a subclass to receive mouse release even...
void mouseDoubleClickEvent(QMouseEvent *mouseEvent) override
This event handler, for event event, can be reimplemented in a subclass to receive mouse double click...
void paintEvent(QPaintEvent *paintEvent) override
This event handler can be reimplemented in a subclass to receive paint events passed in event.
bool event(QEvent *event) override
This virtual function receives events to an object and should return true if the event e was recogniz...
void mousePressEvent(QMouseEvent *event) override
This event handler, for event event, can be reimplemented in a subclass to receive mouse press events...
void mouseReleaseEvent(QMouseEvent *event) override
This event handler, for event event, can be reimplemented in a subclass to receive mouse release even...
void setControlVisible(QMdiSubWindowPrivate::WindowStateAction action, bool visible)
void mouseMoveEvent(QMouseEvent *event) override
This event handler, for event event, can be reimplemented in a subclass to receive mouse move events ...
void leaveEvent(QEvent *event) override
This event handler can be reimplemented in a subclass to receive widget leave events which are passed...
QSize sizeHint() const override
bool hasVisibleControls() const
void paintEvent(QPaintEvent *event) override
This event handler can be reimplemented in a subclass to receive paint events passed in event.
static const int BoundaryMargin
static bool isChildOfQMdiSubWindow(const QWidget *child)
static const int NumSubControls
static const QStyle::SubControl SubControls[]
static int getMoveDeltaComponent(uint cflags, uint moveFlag, uint resizeFlag, int delta, int maxDelta, int minDelta)
static bool isHoverControl(QStyle::SubControl control)
static const Qt::WindowFlags CustomizeWindowFlags
static int getResizeDeltaComponent(uint cflags, uint resizeFlag, uint resizeReverseFlag, int delta)
static bool isMacStyle(QStyle *style)
QString qt_setWindowTitle_helperHelper(const QString &, const QWidget *)
Returns a modified window title with the [*] place holder replaced according to the rules described i...
Definition qwidget.cpp:6051
static ControlElement< T > * ptr(QWidget *widget)
static bool isChildOfTabbedQMdiArea(const QMdiSubWindow *child)