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