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