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

source code of qtbase/src/widgets/widgets/qmdisubwindow.cpp