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