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

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