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 QMdiArea
42 \brief The QMdiArea widget provides an area in which MDI windows are displayed.
43 \since 4.3
44 \ingroup mainwindow-classes
45 \inmodule QtWidgets
46
47 QMdiArea functions, essentially, like a window manager for MDI
48 windows. For instance, it draws the windows it manages on itself
49 and arranges them in a cascading or tile pattern. QMdiArea is
50 commonly used as the center widget in a QMainWindow to create MDI
51 applications, but can also be placed in any layout. The following
52 code adds an area to a main window:
53
54 \snippet mdiarea/mdiareasnippets.cpp 0
55
56 Unlike the window managers for top-level windows, all window flags
57 (Qt::WindowFlags) are supported by QMdiArea as long as the flags
58 are supported by the current widget style. If a specific flag is
59 not supported by the style (e.g., the
60 \l{Qt::}{WindowShadeButtonHint}), you can still shade the window
61 with showShaded().
62
63 Subwindows in QMdiArea are instances of QMdiSubWindow. They
64 are added to an MDI area with addSubWindow(). It is common to pass
65 a QWidget, which is set as the internal widget, to this function,
66 but it is also possible to pass a QMdiSubWindow directly.The class
67 inherits QWidget, and you can use the same API as with a normal
68 top-level window when programming. QMdiSubWindow also has behavior
69 that is specific to MDI windows. See the QMdiSubWindow class
70 description for more details.
71
72 A subwindow becomes active when it gets the keyboard focus, or
73 when setFocus() is called. The user activates a window by moving
74 focus in the usual ways. The MDI area emits the
75 subWindowActivated() signal when the active window changes, and
76 the activeSubWindow() function returns the active subwindow.
77
78 The convenience function subWindowList() returns a list of all
79 subwindows. This information could be used in a popup menu
80 containing a list of windows, for example.
81
82 The subwindows are sorted by the current
83 \l{QMdiArea::}{WindowOrder}. This is used for the subWindowList()
84 and for activateNextSubWindow() and activatePreviousSubWindow().
85 Also, it is used when cascading or tiling the windows with
86 cascadeSubWindows() and tileSubWindows().
87
88 QMdiArea provides two built-in layout strategies for
89 subwindows: cascadeSubWindows() and tileSubWindows(). Both are
90 slots and are easily connected to menu entries.
91
92 \table
93 \row \li \inlineimage mdi-cascade.png
94 \li \inlineimage mdi-tile.png
95 \endtable
96
97 \note The default scroll bar property for QMdiArea is Qt::ScrollBarAlwaysOff.
98
99 \sa QMdiSubWindow
100*/
101
102/*!
103 \fn void QMdiArea::subWindowActivated(QMdiSubWindow *window)
104
105 QMdiArea emits this signal after \a window has been activated. When \a
106 window is \nullptr, QMdiArea has just deactivated its last active window,
107 and there are no active windows on the workspace.
108
109 \sa QMdiArea::activeSubWindow()
110*/
111
112/*!
113 \enum QMdiArea::AreaOption
114
115 This enum describes options that customize the behavior of the
116 QMdiArea.
117
118 \value DontMaximizeSubWindowOnActivation When the active subwindow
119 is maximized, the default behavior is to maximize the next
120 subwindow that is activated. Set this option if you do not want
121 this behavior.
122*/
123
124/*!
125 \enum QMdiArea::WindowOrder
126
127 Specifies the criteria to use for ordering the list of child windows
128 returned by subWindowList(). The functions cascadeSubWindows() and
129 tileSubWindows() follow this order when arranging the windows.
130
131 \value CreationOrder The windows are returned in the order of
132 their creation.
133
134 \value StackingOrder The windows are returned in the order in
135 which they are stacked, with the top-most window being last in
136 the list.
137
138 \value ActivationHistoryOrder The windows are returned in the order in
139 which they were activated.
140
141 \sa subWindowList()
142*/
143
144/*!
145 \enum QMdiArea::ViewMode
146 \since 4.4
147
148 This enum describes the view mode of the area; i.e. how sub-windows
149 will be displayed.
150
151 \value SubWindowView Display sub-windows with window frames (default).
152 \value TabbedView Display sub-windows with tabs in a tab bar.
153
154 \sa setViewMode()
155*/
156
157#include "qmdiarea_p.h"
158
159#include <QApplication>
160#include <QStyle>
161#include <QChildEvent>
162#include <QResizeEvent>
163#include <QScrollBar>
164#include <QtAlgorithms>
165#include <QPainter>
166#include <QFontMetrics>
167#include <QStyleOption>
168#include <QDesktopWidget>
169#include <private/qdesktopwidget_p.h>
170#include <QDebug>
171#include <qmath.h>
172#if QT_CONFIG(menu)
173#include <qmenu.h>
174#endif
175#include <private/qlayoutengine_p.h>
176
177#include <algorithm>
178
179QT_BEGIN_NAMESPACE
180
181using namespace QMdi;
182
183// Asserts in debug mode, gives warning otherwise.
184static bool sanityCheck(const QMdiSubWindow * const child, const char *where)
185{
186 if (Q_UNLIKELY(!child)) {
187 const char error[] = "null pointer";
188 Q_ASSERT_X(false, where, error);
189 qWarning(msg: "%s:%s", where, error);
190 return false;
191 }
192 return true;
193}
194
195static bool sanityCheck(const QList<QWidget *> &widgets, const int index, const char *where)
196{
197 if (Q_UNLIKELY(index < 0 || index >= widgets.size())) {
198 const char error[] = "index out of range";
199 Q_ASSERT_X(false, where, error);
200 qWarning(msg: "%s:%s", where, error);
201 return false;
202 }
203 if (Q_UNLIKELY(!widgets.at(index))) {
204 const char error[] = "null pointer";
205 Q_ASSERT_X(false, where, error);
206 qWarning(msg: "%s:%s", where, error);
207 return false;
208 }
209 return true;
210}
211
212static void setIndex(int *index, int candidate, int min, int max, bool isIncreasing)
213{
214 if (!index)
215 return;
216
217 if (isIncreasing) {
218 if (candidate > max)
219 *index = min;
220 else
221 *index = qMax(a: candidate, b: min);
222 } else {
223 if (candidate < min)
224 *index = max;
225 else
226 *index = qMin(a: candidate, b: max);
227 }
228 Q_ASSERT(*index >= min && *index <= max);
229}
230
231static inline bool useScrollBar(const QRect &childrenRect, const QSize &maxViewportSize,
232 Qt::Orientation orientation)
233{
234 if (orientation == Qt::Horizontal)
235 return childrenRect.width() > maxViewportSize.width()
236 || childrenRect.left() < 0
237 || childrenRect.right() >= maxViewportSize.width();
238 else
239 return childrenRect.height() > maxViewportSize.height()
240 || childrenRect.top() < 0
241 || childrenRect.bottom() >= maxViewportSize.height();
242}
243
244// Returns the closest mdi area containing the widget (if any).
245static inline QMdiArea *mdiAreaParent(QWidget *widget)
246{
247 if (!widget)
248 return nullptr;
249
250 QWidget *parent = widget->parentWidget();
251 while (parent) {
252 if (QMdiArea *area = qobject_cast<QMdiArea *>(object: parent))
253 return area;
254 parent = parent->parentWidget();
255 }
256 return nullptr;
257}
258
259#if QT_CONFIG(tabwidget)
260static inline QTabBar::Shape tabBarShapeFrom(QTabWidget::TabShape shape, QTabWidget::TabPosition position)
261{
262 const bool rounded = (shape == QTabWidget::Rounded);
263 if (position == QTabWidget::North)
264 return rounded ? QTabBar::RoundedNorth : QTabBar::TriangularNorth;
265 if (position == QTabWidget::South)
266 return rounded ? QTabBar::RoundedSouth : QTabBar::TriangularSouth;
267 if (position == QTabWidget::East)
268 return rounded ? QTabBar::RoundedEast : QTabBar::TriangularEast;
269 if (position == QTabWidget::West)
270 return rounded ? QTabBar::RoundedWest : QTabBar::TriangularWest;
271 return QTabBar::RoundedNorth;
272}
273#endif // QT_CONFIG(tabwidget)
274
275static inline QString tabTextFor(QMdiSubWindow *subWindow)
276{
277 if (!subWindow)
278 return QString();
279
280 QString title = subWindow->windowTitle();
281 if (subWindow->isWindowModified()) {
282 title.replace(before: QLatin1String("[*]"), after: QLatin1String("*"));
283 } else {
284 extern QString qt_setWindowTitle_helperHelper(const QString&, const QWidget*);
285 title = qt_setWindowTitle_helperHelper(title, subWindow);
286 }
287
288 return title.isEmpty() ? QMdiArea::tr(s: "(Untitled)") : title;
289}
290
291/*!
292 \internal
293*/
294void RegularTiler::rearrange(QList<QWidget *> &widgets, const QRect &domain) const
295{
296 if (widgets.isEmpty())
297 return;
298
299 const int n = widgets.size();
300 const int ncols = qMax(a: qCeil(v: qSqrt(v: qreal(n))), b: 1);
301 const int nrows = qMax(a: (n % ncols) ? (n / ncols + 1) : (n / ncols), b: 1);
302 const int nspecial = (n % ncols) ? (ncols - n % ncols) : 0;
303 const int dx = domain.width() / ncols;
304 const int dy = domain.height() / nrows;
305
306 int i = 0;
307 for (int row = 0; row < nrows; ++row) {
308 const int y1 = int(row * (dy + 1));
309 for (int col = 0; col < ncols; ++col) {
310 if (row == 1 && col < nspecial)
311 continue;
312 const int x1 = int(col * (dx + 1));
313 int x2 = int(x1 + dx);
314 int y2 = int(y1 + dy);
315 if (row == 0 && col < nspecial) {
316 y2 *= 2;
317 if (nrows != 2)
318 y2 += 1;
319 else
320 y2 = domain.bottom();
321 }
322 if (col == ncols - 1 && x2 != domain.right())
323 x2 = domain.right();
324 if (row == nrows - 1 && y2 != domain.bottom())
325 y2 = domain.bottom();
326 if (!sanityCheck(widgets, index: i, where: "RegularTiler"))
327 continue;
328 QWidget *widget = widgets.at(i: i++);
329 QRect newGeometry = QRect(QPoint(x1, y1), QPoint(x2, y2));
330 widget->setGeometry(QStyle::visualRect(direction: widget->layoutDirection(), boundingRect: domain, logicalRect: newGeometry));
331 }
332 }
333}
334
335/*!
336 \internal
337*/
338void SimpleCascader::rearrange(QList<QWidget *> &widgets, const QRect &domain) const
339{
340 if (widgets.isEmpty())
341 return;
342
343 // Tunables:
344 const int topOffset = 0;
345 const int bottomOffset = 50;
346 const int leftOffset = 0;
347 const int rightOffset = 100;
348 const int dx = 10;
349
350 QStyleOptionTitleBar options;
351 options.initFrom(w: widgets.at(i: 0));
352 int titleBarHeight = widgets.at(i: 0)->style()->pixelMetric(metric: QStyle::PM_TitleBarHeight, option: &options, widget: widgets.at(i: 0));
353 const QFontMetrics fontMetrics = QFontMetrics(QApplication::font(className: "QMdiSubWindowTitleBar"));
354 const int dy = qMax(a: titleBarHeight - (titleBarHeight - fontMetrics.height()) / 2, b: 1)
355 + widgets.at(i: 0)->style()->pixelMetric(metric: QStyle::PM_FocusFrameVMargin, option: nullptr, widget: widgets.at(i: 0));
356
357 const int n = widgets.size();
358 const int nrows = qMax(a: (domain.height() - (topOffset + bottomOffset)) / dy, b: 1);
359 const int ncols = qMax(a: n / nrows + ((n % nrows) ? 1 : 0), b: 1);
360 const int dcol = (domain.width() - (leftOffset + rightOffset)) / ncols;
361
362 int i = 0;
363 for (int row = 0; row < nrows; ++row) {
364 for (int col = 0; col < ncols; ++col) {
365 const int x = leftOffset + row * dx + col * dcol;
366 const int y = topOffset + row * dy;
367 if (!sanityCheck(widgets, index: i, where: "SimpleCascader"))
368 continue;
369 QWidget *widget = widgets.at(i: i++);
370 QRect newGeometry = QRect(QPoint(x, y), widget->sizeHint());
371 widget->setGeometry(QStyle::visualRect(direction: widget->layoutDirection(), boundingRect: domain, logicalRect: newGeometry));
372 if (i == n)
373 return;
374 }
375 }
376}
377
378/*!
379 \internal
380*/
381void IconTiler::rearrange(QList<QWidget *> &widgets, const QRect &domain) const
382{
383 if (widgets.isEmpty() || !sanityCheck(widgets, index: 0, where: "IconTiler"))
384 return;
385
386 const int n = widgets.size();
387 const int width = qMax(a: widgets.at(i: 0)->width(), b: 1);
388 const int height = widgets.at(i: 0)->height();
389 const int ncols = qMax(a: domain.width() / width, b: 1);
390 const int nrows = n / ncols + ((n % ncols) ? 1 : 0);
391
392 int i = 0;
393 for (int row = 0; row < nrows; ++row) {
394 for (int col = 0; col < ncols; ++col) {
395 const int x = col * width;
396 const int y = domain.height() - height - row * height;
397 if (!sanityCheck(widgets, index: i, where: "IconTiler"))
398 continue;
399 QWidget *widget = widgets.at(i: i++);
400 QPoint newPos(x, y);
401 QRect newGeometry = QRect(newPos.x(), newPos.y(), widget->width(), widget->height());
402 widget->setGeometry(QStyle::visualRect(direction: widget->layoutDirection(), boundingRect: domain, logicalRect: newGeometry));
403 if (i == n)
404 return;
405 }
406 }
407}
408
409/*!
410 \internal
411 Calculates the accumulated overlap (intersection area) between 'source' and 'rects'.
412*/
413int MinOverlapPlacer::accumulatedOverlap(const QRect &source, const QVector<QRect> &rects)
414{
415 int accOverlap = 0;
416 for (const QRect &rect : rects) {
417 QRect intersection = source.intersected(other: rect);
418 accOverlap += intersection.width() * intersection.height();
419 }
420 return accOverlap;
421}
422
423
424/*!
425 \internal
426 Finds among 'source' the rectangle with the minimum accumulated overlap with the
427 rectangles in 'rects'.
428*/
429QRect MinOverlapPlacer::findMinOverlapRect(const QVector<QRect> &source, const QVector<QRect> &rects)
430{
431 int minAccOverlap = -1;
432 QRect minAccOverlapRect;
433 for (const QRect &srcRect : source) {
434 const int accOverlap = accumulatedOverlap(source: srcRect, rects);
435 if (accOverlap < minAccOverlap || minAccOverlap == -1) {
436 minAccOverlap = accOverlap;
437 minAccOverlapRect = srcRect;
438 }
439 }
440 return minAccOverlapRect;
441}
442
443/*!
444 \internal
445 Gets candidates for the final placement.
446*/
447QVector<QRect> MinOverlapPlacer::getCandidatePlacements(const QSize &size, const QVector<QRect> &rects,
448 const QRect &domain)
449{
450 QVector<QRect> result;
451
452 QVector<int> xlist;
453 xlist.reserve(size: 2 + rects.size());
454 xlist << domain.left() << domain.right() - size.width() + 1;
455
456 QVector<int> ylist;
457 ylist.reserve(size: 2 + rects.size());
458 ylist << domain.top();
459 if (domain.bottom() - size.height() + 1 >= 0)
460 ylist << domain.bottom() - size.height() + 1;
461
462 for (const QRect &rect : rects) {
463 xlist << rect.right() + 1;
464 ylist << rect.bottom() + 1;
465 }
466
467 std::sort(first: xlist.begin(), last: xlist.end());
468 xlist.erase(begin: std::unique(first: xlist.begin(), last: xlist.end()), end: xlist.end());
469
470 std::sort(first: ylist.begin(), last: ylist.end());
471 ylist.erase(begin: std::unique(first: ylist.begin(), last: ylist.end()), end: ylist.end());
472
473 result.reserve(size: ylist.size() * xlist.size());
474 for (int y : qAsConst(t&: ylist))
475 for (int x : qAsConst(t&: xlist))
476 result << QRect(QPoint(x, y), size);
477 return result;
478}
479
480/*!
481 \internal
482 Finds all rectangles in 'source' not completely inside 'domain'. The result is stored
483 in 'result' and also removed from 'source'.
484*/
485QVector<QRect> MinOverlapPlacer::findNonInsiders(const QRect &domain, QVector<QRect> &source)
486{
487 const auto containedInDomain =
488 [domain](const QRect &srcRect) { return domain.contains(r: srcRect); };
489
490 const auto firstOut = std::stable_partition(first: source.begin(), last: source.end(), pred: containedInDomain);
491
492 QVector<QRect> result;
493 result.reserve(size: source.end() - firstOut);
494 std::copy(firstOut, source.end(), std::back_inserter(x&: result));
495
496 source.erase(begin: firstOut, end: source.end());
497
498 return result;
499}
500
501/*!
502 \internal
503 Finds all rectangles in 'source' that overlaps 'domain' by the maximum overlap area
504 between 'domain' and any rectangle in 'source'. The result is stored in 'result'.
505*/
506QVector<QRect> MinOverlapPlacer::findMaxOverlappers(const QRect &domain, const QVector<QRect> &source)
507{
508 QVector<QRect> result;
509 result.reserve(size: source.size());
510
511 int maxOverlap = -1;
512 for (const QRect &srcRect : source) {
513 QRect intersection = domain.intersected(other: srcRect);
514 const int overlap = intersection.width() * intersection.height();
515 if (overlap >= maxOverlap || maxOverlap == -1) {
516 if (overlap > maxOverlap) {
517 maxOverlap = overlap;
518 result.clear();
519 }
520 result << srcRect;
521 }
522 }
523
524 return result;
525}
526
527/*!
528 \internal
529 Finds among the rectangles in 'source' the best placement. Here, 'best' means the
530 placement that overlaps the rectangles in 'rects' as little as possible while at the
531 same time being as much as possible inside 'domain'.
532*/
533QPoint MinOverlapPlacer::findBestPlacement(const QRect &domain, const QVector<QRect> &rects,
534 QVector<QRect> &source)
535{
536 const QVector<QRect> nonInsiders = findNonInsiders(domain, source);
537
538 if (!source.empty())
539 return findMinOverlapRect(source, rects).topLeft();
540
541 QVector<QRect> maxOverlappers = findMaxOverlappers(domain, source: nonInsiders);
542 return findMinOverlapRect(source: maxOverlappers, rects).topLeft();
543}
544
545
546/*!
547 \internal
548 Places the rectangle defined by 'size' relative to 'rects' and 'domain' so that it
549 overlaps 'rects' as little as possible and 'domain' as much as possible.
550 Returns the position of the resulting rectangle.
551*/
552QPoint MinOverlapPlacer::place(const QSize &size, const QVector<QRect> &rects,
553 const QRect &domain) const
554{
555 if (size.isEmpty() || !domain.isValid())
556 return QPoint();
557 for (const QRect &rect : rects) {
558 if (!rect.isValid())
559 return QPoint();
560 }
561
562 QVector<QRect> candidates = getCandidatePlacements(size, rects, domain);
563 return findBestPlacement(domain, rects, source&: candidates);
564}
565
566#if QT_CONFIG(tabbar)
567class QMdiAreaTabBar : public QTabBar
568{
569public:
570 QMdiAreaTabBar(QWidget *parent) : QTabBar(parent) {}
571
572protected:
573 void mousePressEvent(QMouseEvent *event) override;
574#ifndef QT_NO_CONTEXTMENU
575 void contextMenuEvent(QContextMenuEvent *event) override;
576#endif
577
578private:
579 QMdiSubWindow *subWindowFromIndex(int index) const;
580};
581
582/*!
583 \internal
584*/
585void QMdiAreaTabBar::mousePressEvent(QMouseEvent *event)
586{
587 if (event->button() != Qt::MiddleButton) {
588 QTabBar::mousePressEvent(event);
589 return;
590 }
591
592 QMdiSubWindow *subWindow = subWindowFromIndex(index: tabAt(pos: event->pos()));
593 if (!subWindow) {
594 event->ignore();
595 return;
596 }
597
598 subWindow->close();
599}
600
601#ifndef QT_NO_CONTEXTMENU
602/*!
603 \internal
604*/
605void QMdiAreaTabBar::contextMenuEvent(QContextMenuEvent *event)
606{
607 QPointer<QMdiSubWindow> subWindow = subWindowFromIndex(index: tabAt(pos: event->pos()));
608 if (!subWindow || subWindow->isHidden()) {
609 event->ignore();
610 return;
611 }
612
613#if QT_CONFIG(menu)
614 QMdiSubWindowPrivate *subWindowPrivate = subWindow->d_func();
615 if (!subWindowPrivate->systemMenu) {
616 event->ignore();
617 return;
618 }
619
620 QMdiSubWindow *currentSubWindow = subWindowFromIndex(index: currentIndex());
621 Q_ASSERT(currentSubWindow);
622
623 // We don't want these actions to show up in the system menu when the
624 // current sub-window is maximized, i.e. covers the entire viewport.
625 if (currentSubWindow->isMaximized()) {
626 subWindowPrivate->setVisible(QMdiSubWindowPrivate::MoveAction, visible: false);
627 subWindowPrivate->setVisible(QMdiSubWindowPrivate::ResizeAction, visible: false);
628 subWindowPrivate->setVisible(QMdiSubWindowPrivate::MinimizeAction, visible: false);
629 subWindowPrivate->setVisible(QMdiSubWindowPrivate::MaximizeAction, visible: false);
630 subWindowPrivate->setVisible(QMdiSubWindowPrivate::RestoreAction, visible: false);
631 subWindowPrivate->setVisible(QMdiSubWindowPrivate::StayOnTopAction, visible: false);
632 }
633
634 // Show system menu.
635 subWindowPrivate->systemMenu->exec(pos: event->globalPos());
636 if (!subWindow)
637 return;
638
639 // Restore action visibility.
640 subWindowPrivate->updateActions();
641#endif // QT_CONFIG(menu)
642}
643#endif // QT_NO_CONTEXTMENU
644
645/*!
646 \internal
647*/
648QMdiSubWindow *QMdiAreaTabBar::subWindowFromIndex(int index) const
649{
650 if (index < 0 || index >= count())
651 return nullptr;
652
653 QMdiArea *mdiArea = qobject_cast<QMdiArea *>(object: parentWidget());
654 Q_ASSERT(mdiArea);
655
656 const QList<QMdiSubWindow *> subWindows = mdiArea->subWindowList();
657 Q_ASSERT(index < subWindows.size());
658
659 QMdiSubWindow *subWindow = mdiArea->subWindowList().at(i: index);
660 Q_ASSERT(subWindow);
661
662 return subWindow;
663}
664#endif // QT_CONFIG(tabbar)
665
666/*!
667 \internal
668*/
669QMdiAreaPrivate::QMdiAreaPrivate()
670 : cascader(nullptr),
671 regularTiler(nullptr),
672 iconTiler(nullptr),
673 placer(nullptr),
674#if QT_CONFIG(rubberband)
675 rubberBand(nullptr),
676#endif
677#if QT_CONFIG(tabbar)
678 tabBar(nullptr),
679#endif
680 activationOrder(QMdiArea::CreationOrder),
681 viewMode(QMdiArea::SubWindowView),
682#if QT_CONFIG(tabbar)
683 documentMode(false),
684 tabsClosable(false),
685 tabsMovable(false),
686#endif
687#if QT_CONFIG(tabwidget)
688 tabShape(QTabWidget::Rounded),
689 tabPosition(QTabWidget::North),
690#endif
691 ignoreGeometryChange(false),
692 ignoreWindowStateChange(false),
693 isActivated(false),
694 isSubWindowsTiled(false),
695 showActiveWindowMaximized(false),
696 tileCalledFromResizeEvent(false),
697 updatesDisabledByUs(false),
698 inViewModeChange(false),
699 indexToNextWindow(-1),
700 indexToPreviousWindow(-1),
701 indexToHighlighted(-1),
702 indexToLastActiveTab(-1),
703 resizeTimerId(-1),
704 tabToPreviousTimerId(-1)
705{
706}
707
708/*!
709 \internal
710*/
711void QMdiAreaPrivate::_q_deactivateAllWindows(QMdiSubWindow *aboutToActivate)
712{
713 if (ignoreWindowStateChange)
714 return;
715
716 Q_Q(QMdiArea);
717 if (!aboutToActivate)
718 aboutToBecomeActive = qobject_cast<QMdiSubWindow *>(object: q->sender());
719 else
720 aboutToBecomeActive = aboutToActivate;
721 Q_ASSERT(aboutToBecomeActive);
722
723 foreach (QMdiSubWindow *child, childWindows) {
724 if (!sanityCheck(child, where: "QMdiArea::deactivateAllWindows") || aboutToBecomeActive == child)
725 continue;
726 // We don't want to handle signals caused by child->showNormal().
727 ignoreWindowStateChange = true;
728 if(!(options & QMdiArea::DontMaximizeSubWindowOnActivation) && !showActiveWindowMaximized)
729 showActiveWindowMaximized = child->isMaximized() && child->isVisible();
730 if (showActiveWindowMaximized && child->isMaximized()) {
731 if (q->updatesEnabled()) {
732 updatesDisabledByUs = true;
733 q->setUpdatesEnabled(false);
734 }
735 child->showNormal();
736 }
737 if (child->isMinimized() && !child->isShaded() && !windowStaysOnTop(subWindow: child))
738 child->lower();
739 ignoreWindowStateChange = false;
740 child->d_func()->setActive(activate: false);
741 }
742}
743
744/*!
745 \internal
746*/
747void QMdiAreaPrivate::_q_processWindowStateChanged(Qt::WindowStates oldState,
748 Qt::WindowStates newState)
749{
750 if (ignoreWindowStateChange)
751 return;
752
753 Q_Q(QMdiArea);
754 QMdiSubWindow *child = qobject_cast<QMdiSubWindow *>(object: q->sender());
755 if (!child)
756 return;
757
758 // windowActivated
759 if (!(oldState & Qt::WindowActive) && (newState & Qt::WindowActive))
760 emitWindowActivated(child);
761 // windowDeactivated
762 else if ((oldState & Qt::WindowActive) && !(newState & Qt::WindowActive))
763 resetActiveWindow(child);
764
765 // windowMinimized
766 if (!(oldState & Qt::WindowMinimized) && (newState & Qt::WindowMinimized)) {
767 isSubWindowsTiled = false;
768 arrangeMinimizedSubWindows();
769 // windowMaximized
770 } else if (!(oldState & Qt::WindowMaximized) && (newState & Qt::WindowMaximized)) {
771 internalRaise(child);
772 // windowRestored
773 } else if (!(newState & (Qt::WindowMaximized | Qt::WindowMinimized))) {
774 internalRaise(child);
775 if (oldState & Qt::WindowMinimized)
776 arrangeMinimizedSubWindows();
777 }
778}
779
780void QMdiAreaPrivate::_q_currentTabChanged(int index)
781{
782#if !QT_CONFIG(tabbar)
783 Q_UNUSED(index);
784#else
785 if (!tabBar || index < 0)
786 return;
787
788 // If the previous active sub-window was hidden, disable the tab.
789 if (indexToLastActiveTab >= 0 && indexToLastActiveTab < tabBar->count()
790 && indexToLastActiveTab < childWindows.count()) {
791 QMdiSubWindow *lastActive = childWindows.at(i: indexToLastActiveTab);
792 if (lastActive && lastActive->isHidden())
793 tabBar->setTabEnabled(index: indexToLastActiveTab, enabled: false);
794 }
795
796 indexToLastActiveTab = index;
797 Q_ASSERT(childWindows.size() > index);
798 QMdiSubWindow *subWindow = childWindows.at(i: index);
799 Q_ASSERT(subWindow);
800 activateWindow(child: subWindow);
801#endif // QT_CONFIG(tabbar)
802}
803
804void QMdiAreaPrivate::_q_closeTab(int index)
805{
806#if !QT_CONFIG(tabbar)
807 Q_UNUSED(index);
808#else
809 QMdiSubWindow *subWindow = childWindows.at(i: index);
810 Q_ASSERT(subWindow);
811 subWindow->close();
812#endif // QT_CONFIG(tabbar)
813}
814
815void QMdiAreaPrivate::_q_moveTab(int from, int to)
816{
817#if !QT_CONFIG(tabbar)
818 Q_UNUSED(from);
819 Q_UNUSED(to);
820#else
821 childWindows.move(from, to);
822#endif // QT_CONFIG(tabbar)
823}
824
825/*!
826 \internal
827*/
828void QMdiAreaPrivate::appendChild(QMdiSubWindow *child)
829{
830 Q_Q(QMdiArea);
831 Q_ASSERT(child && childWindows.indexOf(child) == -1);
832
833 if (child->parent() != viewport)
834 child->setParent(parent: viewport, f: child->windowFlags());
835 childWindows.append(t: QPointer<QMdiSubWindow>(child));
836
837 if (!child->testAttribute(attribute: Qt::WA_Resized) && q->isVisible()) {
838 QSize newSize(child->sizeHint().boundedTo(otherSize: viewport->size()));
839 child->resize(newSize.expandedTo(otherSize: qSmartMinSize(w: child)));
840 }
841
842 if (!placer)
843 placer = new MinOverlapPlacer;
844 place(placer, child);
845
846 if (hbarpolicy != Qt::ScrollBarAlwaysOff)
847 child->setOption(option: QMdiSubWindow::AllowOutsideAreaHorizontally, on: true);
848 else
849 child->setOption(option: QMdiSubWindow::AllowOutsideAreaHorizontally, on: false);
850
851 if (vbarpolicy != Qt::ScrollBarAlwaysOff)
852 child->setOption(option: QMdiSubWindow::AllowOutsideAreaVertically, on: true);
853 else
854 child->setOption(option: QMdiSubWindow::AllowOutsideAreaVertically, on: false);
855
856 internalRaise(child);
857 indicesToActivatedChildren.prepend(t: childWindows.size() - 1);
858 Q_ASSERT(indicesToActivatedChildren.size() == childWindows.size());
859
860#if QT_CONFIG(tabbar)
861 if (tabBar) {
862 tabBar->addTab(icon: child->windowIcon(), text: tabTextFor(subWindow: child));
863 updateTabBarGeometry();
864 if (childWindows.count() == 1 && !(options & QMdiArea::DontMaximizeSubWindowOnActivation))
865 showActiveWindowMaximized = true;
866 }
867#endif
868
869 if (!(child->windowFlags() & Qt::SubWindow))
870 child->setWindowFlags(Qt::SubWindow);
871 child->installEventFilter(filterObj: q);
872
873 QObject::connect(sender: child, SIGNAL(aboutToActivate()), receiver: q, SLOT(_q_deactivateAllWindows()));
874 QObject::connect(sender: child, SIGNAL(windowStateChanged(Qt::WindowStates,Qt::WindowStates)),
875 receiver: q, SLOT(_q_processWindowStateChanged(Qt::WindowStates,Qt::WindowStates)));
876}
877
878/*!
879 \internal
880*/
881void QMdiAreaPrivate::place(Placer *placer, QMdiSubWindow *child)
882{
883 if (!placer || !child)
884 return;
885
886 Q_Q(QMdiArea);
887 if (!q->isVisible()) {
888 // The window is only laid out when it's added to QMdiArea,
889 // so there's no need to check that we don't have it in the
890 // list already. appendChild() ensures that.
891 pendingPlacements.append(t: child);
892 return;
893 }
894
895 QVector<QRect> rects;
896 rects.reserve(size: childWindows.size());
897 QRect parentRect = q->rect();
898 foreach (QMdiSubWindow *window, childWindows) {
899 if (!sanityCheck(child: window, where: "QMdiArea::place") || window == child || !window->isVisibleTo(q)
900 || !window->testAttribute(attribute: Qt::WA_Moved)) {
901 continue;
902 }
903 QRect occupiedGeometry;
904 if (window->isMaximized()) {
905 occupiedGeometry = QRect(window->d_func()->oldGeometry.topLeft(),
906 window->d_func()->restoreSize);
907 } else {
908 occupiedGeometry = window->geometry();
909 }
910 rects.append(t: QStyle::visualRect(direction: child->layoutDirection(), boundingRect: parentRect, logicalRect: occupiedGeometry));
911 }
912 QPoint newPos = placer->place(size: child->size(), rects, domain: parentRect);
913 QRect newGeometry = QRect(newPos.x(), newPos.y(), child->width(), child->height());
914 child->setGeometry(QStyle::visualRect(direction: child->layoutDirection(), boundingRect: parentRect, logicalRect: newGeometry));
915}
916
917/*!
918 \internal
919*/
920void QMdiAreaPrivate::rearrange(Rearranger *rearranger)
921{
922 if (!rearranger)
923 return;
924
925 Q_Q(QMdiArea);
926 if (!q->isVisible()) {
927 // Compress if we already have the rearranger in the list.
928 int index = pendingRearrangements.indexOf(t: rearranger);
929 if (index != -1)
930 pendingRearrangements.move(from: index, to: pendingRearrangements.size() - 1);
931 else
932 pendingRearrangements.append(t: rearranger);
933 return;
934 }
935
936 QList<QWidget *> widgets;
937 const bool reverseList = rearranger->type() == Rearranger::RegularTiler;
938 const QList<QMdiSubWindow *> subWindows = subWindowList(activationOrder, reversed: reverseList);
939 QSize minSubWindowSize;
940 foreach (QMdiSubWindow *child, subWindows) {
941 if (!sanityCheck(child, where: "QMdiArea::rearrange") || !child->isVisible())
942 continue;
943 if (rearranger->type() == Rearranger::IconTiler) {
944 if (child->isMinimized() && !child->isShaded())
945 widgets.append(t: child);
946 } else {
947 if (child->isMinimized() && !child->isShaded())
948 continue;
949 if (child->isMaximized() || child->isShaded())
950 child->showNormal();
951 minSubWindowSize = minSubWindowSize.expandedTo(otherSize: child->minimumSize())
952 .expandedTo(otherSize: child->d_func()->internalMinimumSize);
953 widgets.append(t: child);
954 }
955 }
956
957 QRect domain = viewport->rect();
958 if (rearranger->type() == Rearranger::RegularTiler && !widgets.isEmpty())
959 domain = resizeToMinimumTileSize(minSubWindowSize, subWindowCount: widgets.count());
960
961 rearranger->rearrange(widgets, domain);
962
963 if (rearranger->type() == Rearranger::RegularTiler && !widgets.isEmpty()) {
964 isSubWindowsTiled = true;
965 updateScrollBars();
966 } else if (rearranger->type() == Rearranger::SimpleCascader) {
967 isSubWindowsTiled = false;
968 }
969}
970
971/*!
972 \internal
973
974 Arranges all minimized windows at the bottom of the workspace.
975*/
976void QMdiAreaPrivate::arrangeMinimizedSubWindows()
977{
978 if (!iconTiler)
979 iconTiler = new IconTiler;
980 rearrange(rearranger: iconTiler);
981}
982
983/*!
984 \internal
985*/
986void QMdiAreaPrivate::activateWindow(QMdiSubWindow *child)
987{
988 if (childWindows.isEmpty()) {
989 Q_ASSERT(!child);
990 Q_ASSERT(!active);
991 return;
992 }
993
994 if (!child) {
995 if (active) {
996 Q_ASSERT(active->d_func()->isActive);
997 active->d_func()->setActive(activate: false);
998 resetActiveWindow();
999 }
1000 return;
1001 }
1002
1003 if (child->isHidden() || child == active)
1004 return;
1005 child->d_func()->setActive(activate: true);
1006}
1007
1008/*!
1009 \internal
1010*/
1011void QMdiAreaPrivate::activateCurrentWindow()
1012{
1013 QMdiSubWindow *current = q_func()->currentSubWindow();
1014 if (current && !isExplicitlyDeactivated(subWindow: current)) {
1015 current->d_func()->activationEnabled = true;
1016 current->d_func()->setActive(activate: true, /*changeFocus=*/false);
1017 }
1018}
1019
1020void QMdiAreaPrivate::activateHighlightedWindow()
1021{
1022 if (indexToHighlighted < 0)
1023 return;
1024
1025 Q_ASSERT(indexToHighlighted < childWindows.size());
1026 if (tabToPreviousTimerId != -1)
1027 activateWindow(child: nextVisibleSubWindow(increaseFactor: -1, QMdiArea::ActivationHistoryOrder));
1028 else
1029 activateWindow(child: childWindows.at(i: indexToHighlighted));
1030#if QT_CONFIG(rubberband)
1031 hideRubberBand();
1032#endif
1033}
1034
1035/*!
1036 \internal
1037*/
1038void QMdiAreaPrivate::emitWindowActivated(QMdiSubWindow *activeWindow)
1039{
1040 Q_Q(QMdiArea);
1041 Q_ASSERT(activeWindow);
1042 if (activeWindow == active)
1043 return;
1044 Q_ASSERT(activeWindow->d_func()->isActive);
1045
1046 if (!aboutToBecomeActive)
1047 _q_deactivateAllWindows(aboutToActivate: activeWindow);
1048 Q_ASSERT(aboutToBecomeActive);
1049
1050 // This is true only if 'DontMaximizeSubWindowOnActivation' is disabled
1051 // and the previous active window was maximized.
1052 if (showActiveWindowMaximized) {
1053 if (!activeWindow->isMaximized())
1054 activeWindow->showMaximized();
1055 showActiveWindowMaximized = false;
1056 }
1057
1058 // Put in front to update activation order.
1059 const int indexToActiveWindow = childWindows.indexOf(t: activeWindow);
1060 Q_ASSERT(indexToActiveWindow != -1);
1061 const int index = indicesToActivatedChildren.indexOf(t: indexToActiveWindow);
1062 Q_ASSERT(index != -1);
1063 indicesToActivatedChildren.move(from: index, to: 0);
1064 internalRaise(child: activeWindow);
1065
1066 if (updatesDisabledByUs) {
1067 q->setUpdatesEnabled(true);
1068 updatesDisabledByUs = false;
1069 }
1070
1071 Q_ASSERT(aboutToBecomeActive == activeWindow);
1072 active = activeWindow;
1073 aboutToBecomeActive = nullptr;
1074 Q_ASSERT(active->d_func()->isActive);
1075
1076#if QT_CONFIG(tabbar)
1077 if (tabBar && tabBar->currentIndex() != indexToActiveWindow)
1078 tabBar->setCurrentIndex(indexToActiveWindow);
1079#endif
1080
1081 if (active->isMaximized() && scrollBarsEnabled())
1082 updateScrollBars();
1083
1084 emit q->subWindowActivated(active);
1085}
1086
1087/*!
1088 \internal
1089*/
1090void QMdiAreaPrivate::resetActiveWindow(QMdiSubWindow *deactivatedWindow)
1091{
1092 Q_Q(QMdiArea);
1093 if (deactivatedWindow) {
1094 if (deactivatedWindow != active)
1095 return;
1096 active = nullptr;
1097 if ((aboutToBecomeActive || isActivated || lastWindowAboutToBeDestroyed())
1098 && !isExplicitlyDeactivated(subWindow: deactivatedWindow) && !q->window()->isMinimized()) {
1099 return;
1100 }
1101 emit q->subWindowActivated(nullptr);
1102 return;
1103 }
1104
1105 if (aboutToBecomeActive)
1106 return;
1107
1108 active = nullptr;
1109 emit q->subWindowActivated(nullptr);
1110}
1111
1112/*!
1113 \internal
1114*/
1115void QMdiAreaPrivate::updateActiveWindow(int removedIndex, bool activeRemoved)
1116{
1117 Q_ASSERT(indicesToActivatedChildren.size() == childWindows.size());
1118
1119#if QT_CONFIG(tabbar)
1120 if (tabBar && removedIndex >= 0) {
1121 const QSignalBlocker blocker(tabBar);
1122 tabBar->removeTab(index: removedIndex);
1123 updateTabBarGeometry();
1124 }
1125#endif
1126
1127 if (childWindows.isEmpty()) {
1128 showActiveWindowMaximized = false;
1129 resetActiveWindow();
1130 return;
1131 }
1132
1133 if (indexToHighlighted >= 0) {
1134#if QT_CONFIG(rubberband)
1135 // Hide rubber band if highlighted window is removed.
1136 if (indexToHighlighted == removedIndex)
1137 hideRubberBand();
1138 else
1139#endif
1140 // or update index if necessary.
1141 if (indexToHighlighted > removedIndex)
1142 --indexToHighlighted;
1143 }
1144
1145 // Update indices list
1146 for (int i = 0; i < indicesToActivatedChildren.size(); ++i) {
1147 int *index = &indicesToActivatedChildren[i];
1148 if (*index > removedIndex)
1149 --*index;
1150 }
1151
1152 if (!activeRemoved)
1153 return;
1154
1155 // Activate next window.
1156 QMdiSubWindow *next = nextVisibleSubWindow(increaseFactor: 0, activationOrder, removed: removedIndex);
1157 if (next)
1158 activateWindow(child: next);
1159}
1160
1161/*!
1162 \internal
1163*/
1164void QMdiAreaPrivate::updateScrollBars()
1165{
1166 if (ignoreGeometryChange || !scrollBarsEnabled())
1167 return;
1168
1169 Q_Q(QMdiArea);
1170 QSize maxSize = q->maximumViewportSize();
1171 QSize hbarExtent = hbar->sizeHint();
1172 QSize vbarExtent = vbar->sizeHint();
1173
1174 if (q->style()->styleHint(stylehint: QStyle::SH_ScrollView_FrameOnlyAroundContents, opt: nullptr, widget: q)) {
1175 const int doubleFrameWidth = frameWidth * 2;
1176 if (hbarpolicy == Qt::ScrollBarAlwaysOn)
1177 maxSize.rheight() -= doubleFrameWidth;
1178 if (vbarpolicy == Qt::ScrollBarAlwaysOn)
1179 maxSize.rwidth() -= doubleFrameWidth;
1180 hbarExtent.rheight() += doubleFrameWidth;
1181 vbarExtent.rwidth() += doubleFrameWidth;
1182 }
1183
1184 const QRect childrenRect = active && active->isMaximized()
1185 ? active->geometry() : viewport->childrenRect();
1186 bool useHorizontalScrollBar = useScrollBar(childrenRect, maxViewportSize: maxSize, orientation: Qt::Horizontal);
1187 bool useVerticalScrollBar = useScrollBar(childrenRect, maxViewportSize: maxSize, orientation: Qt::Vertical);
1188
1189 if (useHorizontalScrollBar && !useVerticalScrollBar) {
1190 const QSize max = maxSize - QSize(0, hbarExtent.height());
1191 useVerticalScrollBar = useScrollBar(childrenRect, maxViewportSize: max, orientation: Qt::Vertical);
1192 }
1193
1194 if (useVerticalScrollBar && !useHorizontalScrollBar) {
1195 const QSize max = maxSize - QSize(vbarExtent.width(), 0);
1196 useHorizontalScrollBar = useScrollBar(childrenRect, maxViewportSize: max, orientation: Qt::Horizontal);
1197 }
1198
1199 if (useHorizontalScrollBar && hbarpolicy != Qt::ScrollBarAlwaysOn)
1200 maxSize.rheight() -= hbarExtent.height();
1201 if (useVerticalScrollBar && vbarpolicy != Qt::ScrollBarAlwaysOn)
1202 maxSize.rwidth() -= vbarExtent.width();
1203
1204 QRect viewportRect(QPoint(0, 0), maxSize);
1205 const int startX = q->isLeftToRight() ? childrenRect.left() : viewportRect.right()
1206 - childrenRect.right();
1207
1208 // Horizontal scroll bar.
1209 if (isSubWindowsTiled && hbar->value() != 0)
1210 hbar->setValue(0);
1211 const int xOffset = startX + hbar->value();
1212 hbar->setRange(min: qMin(a: 0, b: xOffset),
1213 max: qMax(a: 0, b: xOffset + childrenRect.width() - viewportRect.width()));
1214 hbar->setPageStep(childrenRect.width());
1215 hbar->setSingleStep(childrenRect.width() / 20);
1216
1217 // Vertical scroll bar.
1218 if (isSubWindowsTiled && vbar->value() != 0)
1219 vbar->setValue(0);
1220 const int yOffset = childrenRect.top() + vbar->value();
1221 vbar->setRange(min: qMin(a: 0, b: yOffset),
1222 max: qMax(a: 0, b: yOffset + childrenRect.height() - viewportRect.height()));
1223 vbar->setPageStep(childrenRect.height());
1224 vbar->setSingleStep(childrenRect.height() / 20);
1225}
1226
1227/*!
1228 \internal
1229*/
1230void QMdiAreaPrivate::internalRaise(QMdiSubWindow *mdiChild) const
1231{
1232 if (!sanityCheck(child: mdiChild, where: "QMdiArea::internalRaise") || childWindows.size() < 2)
1233 return;
1234
1235 QMdiSubWindow *stackUnderChild = nullptr;
1236 if (!windowStaysOnTop(subWindow: mdiChild)) {
1237 const auto children = viewport->children(); // take a copy, as raising/stacking under changes the order
1238 for (QObject *object : children) {
1239 QMdiSubWindow *child = qobject_cast<QMdiSubWindow *>(object);
1240 if (!child || !childWindows.contains(t: child))
1241 continue;
1242 if (!child->isHidden() && windowStaysOnTop(subWindow: child)) {
1243 if (stackUnderChild)
1244 child->stackUnder(stackUnderChild);
1245 else
1246 child->raise();
1247 stackUnderChild = child;
1248 }
1249 }
1250 }
1251
1252 if (stackUnderChild)
1253 mdiChild->stackUnder(stackUnderChild);
1254 else
1255 mdiChild->raise();
1256}
1257
1258QRect QMdiAreaPrivate::resizeToMinimumTileSize(const QSize &minSubWindowSize, int subWindowCount)
1259{
1260 Q_Q(QMdiArea);
1261 if (!minSubWindowSize.isValid() || subWindowCount <= 0)
1262 return viewport->rect();
1263
1264 // Calculate minimum size.
1265 const int columns = qMax(a: qCeil(v: qSqrt(v: qreal(subWindowCount))), b: 1);
1266 const int rows = qMax(a: (subWindowCount % columns) ? (subWindowCount / columns + 1)
1267 : (subWindowCount / columns), b: 1);
1268 const int minWidth = minSubWindowSize.width() * columns;
1269 const int minHeight = minSubWindowSize.height() * rows;
1270
1271 // Increase area size if necessary. Scroll bars are provided if we're not able
1272 // to resize to the minimum size.
1273 if (!tileCalledFromResizeEvent) {
1274 QWidget *topLevel = q;
1275 // Find the topLevel for this area, either a real top-level or a sub-window.
1276 while (topLevel && !topLevel->isWindow() && topLevel->windowType() != Qt::SubWindow)
1277 topLevel = topLevel->parentWidget();
1278 // We don't want sub-subwindows to be placed at the edge, thus add 2 pixels.
1279 int minAreaWidth = minWidth + left + right + 2;
1280 int minAreaHeight = minHeight + top + bottom + 2;
1281 if (hbar->isVisible())
1282 minAreaHeight += hbar->height();
1283 if (vbar->isVisible())
1284 minAreaWidth += vbar->width();
1285 if (q->style()->styleHint(stylehint: QStyle::SH_ScrollView_FrameOnlyAroundContents, opt: nullptr, widget: q)) {
1286 const int frame = q->style()->pixelMetric(metric: QStyle::PM_DefaultFrameWidth, option: nullptr, widget: q);
1287 minAreaWidth += 2 * frame;
1288 minAreaHeight += 2 * frame;
1289 }
1290 const QSize diff = QSize(minAreaWidth, minAreaHeight).expandedTo(otherSize: q->size()) - q->size();
1291 // Only resize topLevel widget if scroll bars are disabled.
1292 if (hbarpolicy == Qt::ScrollBarAlwaysOff)
1293 topLevel->resize(w: topLevel->size().width() + diff.width(), h: topLevel->size().height());
1294 if (vbarpolicy == Qt::ScrollBarAlwaysOff)
1295 topLevel->resize(w: topLevel->size().width(), h: topLevel->size().height() + diff.height());
1296 }
1297
1298 QRect domain = viewport->rect();
1299
1300 // Adjust domain width and provide horizontal scroll bar.
1301 if (domain.width() < minWidth) {
1302 domain.setWidth(minWidth);
1303 if (hbarpolicy == Qt::ScrollBarAlwaysOff)
1304 q->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
1305 else
1306 hbar->setValue(0);
1307 }
1308 // Adjust domain height and provide vertical scroll bar.
1309 if (domain.height() < minHeight) {
1310 domain.setHeight(minHeight);
1311 if (vbarpolicy == Qt::ScrollBarAlwaysOff)
1312 q->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
1313 else
1314 vbar->setValue(0);
1315 }
1316 return domain;
1317}
1318
1319/*!
1320 \internal
1321*/
1322bool QMdiAreaPrivate::scrollBarsEnabled() const
1323{
1324 return hbarpolicy != Qt::ScrollBarAlwaysOff || vbarpolicy != Qt::ScrollBarAlwaysOff;
1325}
1326
1327/*!
1328 \internal
1329*/
1330bool QMdiAreaPrivate::lastWindowAboutToBeDestroyed() const
1331{
1332 if (childWindows.count() != 1)
1333 return false;
1334
1335 QMdiSubWindow *last = childWindows.at(i: 0);
1336 if (!last)
1337 return true;
1338
1339 if (!last->testAttribute(attribute: Qt::WA_DeleteOnClose))
1340 return false;
1341
1342 return last->d_func()->data.is_closing;
1343}
1344
1345/*!
1346 \internal
1347*/
1348void QMdiAreaPrivate::setChildActivationEnabled(bool enable, bool onlyNextActivationEvent) const
1349{
1350 foreach (QMdiSubWindow *subWindow, childWindows) {
1351 if (!subWindow || !subWindow->isVisible())
1352 continue;
1353 if (onlyNextActivationEvent)
1354 subWindow->d_func()->ignoreNextActivationEvent = !enable;
1355 else
1356 subWindow->d_func()->activationEnabled = enable;
1357 }
1358}
1359
1360/*!
1361 \internal
1362 \reimp
1363*/
1364void QMdiAreaPrivate::scrollBarPolicyChanged(Qt::Orientation orientation, Qt::ScrollBarPolicy policy)
1365{
1366 if (childWindows.isEmpty())
1367 return;
1368
1369 const QMdiSubWindow::SubWindowOption option = orientation == Qt::Horizontal ?
1370 QMdiSubWindow::AllowOutsideAreaHorizontally : QMdiSubWindow::AllowOutsideAreaVertically;
1371 const bool enable = policy != Qt::ScrollBarAlwaysOff;
1372 foreach (QMdiSubWindow *child, childWindows) {
1373 if (!sanityCheck(child, where: "QMdiArea::scrollBarPolicyChanged"))
1374 continue;
1375 child->setOption(option, on: enable);
1376 }
1377 updateScrollBars();
1378}
1379
1380QList<QMdiSubWindow*>
1381QMdiAreaPrivate::subWindowList(QMdiArea::WindowOrder order, bool reversed) const
1382{
1383 QList<QMdiSubWindow *> list;
1384 if (childWindows.isEmpty())
1385 return list;
1386
1387 if (order == QMdiArea::CreationOrder) {
1388 foreach (QMdiSubWindow *child, childWindows) {
1389 if (!child)
1390 continue;
1391 if (!reversed)
1392 list.append(t: child);
1393 else
1394 list.prepend(t: child);
1395 }
1396 } else if (order == QMdiArea::StackingOrder) {
1397 for (QObject *object : viewport->children()) {
1398 QMdiSubWindow *child = qobject_cast<QMdiSubWindow *>(object);
1399 if (!child || !childWindows.contains(t: child))
1400 continue;
1401 if (!reversed)
1402 list.append(t: child);
1403 else
1404 list.prepend(t: child);
1405 }
1406 } else { // ActivationHistoryOrder
1407 Q_ASSERT(indicesToActivatedChildren.size() == childWindows.size());
1408 for (int i = indicesToActivatedChildren.count() - 1; i >= 0; --i) {
1409 QMdiSubWindow *child = childWindows.at(i: indicesToActivatedChildren.at(i));
1410 if (!child)
1411 continue;
1412 if (!reversed)
1413 list.append(t: child);
1414 else
1415 list.prepend(t: child);
1416 }
1417 }
1418 return list;
1419}
1420
1421/*!
1422 \internal
1423*/
1424void QMdiAreaPrivate::disconnectSubWindow(QObject *subWindow)
1425{
1426 if (!subWindow)
1427 return;
1428
1429 Q_Q(QMdiArea);
1430 QObject::disconnect(sender: subWindow, signal: nullptr, receiver: q, member: nullptr);
1431 subWindow->removeEventFilter(obj: q);
1432}
1433
1434/*!
1435 \internal
1436*/
1437QMdiSubWindow *QMdiAreaPrivate::nextVisibleSubWindow(int increaseFactor, QMdiArea::WindowOrder order,
1438 int removedIndex, int fromIndex) const
1439{
1440 if (childWindows.isEmpty())
1441 return nullptr;
1442
1443 Q_Q(const QMdiArea);
1444 const QList<QMdiSubWindow *> subWindows = q->subWindowList(order);
1445 QMdiSubWindow *current = nullptr;
1446
1447 if (removedIndex < 0) {
1448 if (fromIndex >= 0 && fromIndex < subWindows.size())
1449 current = childWindows.at(i: fromIndex);
1450 else
1451 current = q->currentSubWindow();
1452 }
1453
1454 // There's no current sub-window (removed or deactivated),
1455 // so we have to pick the last active or the next in creation order.
1456 if (!current) {
1457 if (removedIndex >= 0 && order == QMdiArea::CreationOrder) {
1458 int candidateIndex = -1;
1459 setIndex(index: &candidateIndex, candidate: removedIndex, min: 0, max: subWindows.size() - 1, isIncreasing: true);
1460 current = childWindows.at(i: candidateIndex);
1461 } else {
1462 current = subWindows.back();
1463 }
1464 }
1465 Q_ASSERT(current);
1466
1467 // Find the index for the current sub-window in the given activation order
1468 const int indexToCurrent = subWindows.indexOf(t: current);
1469 const bool increasing = increaseFactor > 0;
1470
1471 // and use that index + increseFactor as a candidate.
1472 int index = -1;
1473 setIndex(index: &index, candidate: indexToCurrent + increaseFactor, min: 0, max: subWindows.size() - 1, isIncreasing: increasing);
1474 Q_ASSERT(index != -1);
1475
1476 // Try to find another window if the candidate is hidden.
1477 while (subWindows.at(i: index)->isHidden()) {
1478 setIndex(index: &index, candidate: index + increaseFactor, min: 0, max: subWindows.size() - 1, isIncreasing: increasing);
1479 if (index == indexToCurrent)
1480 break;
1481 }
1482
1483 if (!subWindows.at(i: index)->isHidden())
1484 return subWindows.at(i: index);
1485 return nullptr;
1486}
1487
1488/*!
1489 \internal
1490*/
1491void QMdiAreaPrivate::highlightNextSubWindow(int increaseFactor)
1492{
1493 if (childWindows.size() == 1)
1494 return;
1495
1496 Q_Q(QMdiArea);
1497 // There's no highlighted sub-window atm, use current.
1498 if (indexToHighlighted < 0) {
1499 QMdiSubWindow *current = q->currentSubWindow();
1500 if (!current)
1501 return;
1502 indexToHighlighted = childWindows.indexOf(t: current);
1503 }
1504
1505 Q_ASSERT(indexToHighlighted >= 0);
1506 Q_ASSERT(indexToHighlighted < childWindows.size());
1507
1508 QMdiSubWindow *highlight = nextVisibleSubWindow(increaseFactor, order: activationOrder, removedIndex: -1, fromIndex: indexToHighlighted);
1509 if (!highlight)
1510 return;
1511
1512#if QT_CONFIG(rubberband)
1513 if (!rubberBand) {
1514 rubberBand = new QRubberBand(QRubberBand::Rectangle, q);
1515 // For accessibility to identify this special widget.
1516 rubberBand->setObjectName(QLatin1String("qt_rubberband"));
1517 rubberBand->setWindowFlags(rubberBand->windowFlags() | Qt::WindowStaysOnTopHint);
1518 }
1519#endif
1520
1521 // Only highlight if we're not switching back to the previously active window (Ctrl-Tab once).
1522#if QT_CONFIG(rubberband)
1523 if (tabToPreviousTimerId == -1)
1524 showRubberBandFor(subWindow: highlight);
1525#endif
1526
1527 indexToHighlighted = childWindows.indexOf(t: highlight);
1528 Q_ASSERT(indexToHighlighted >= 0);
1529}
1530
1531#if QT_CONFIG(rubberband)
1532void QMdiAreaPrivate::showRubberBandFor(QMdiSubWindow *subWindow)
1533{
1534 if (!subWindow || !rubberBand)
1535 return;
1536
1537#if QT_CONFIG(tabbar)
1538 if (viewMode == QMdiArea::TabbedView)
1539 rubberBand->setGeometry(tabBar->tabRect(index: childWindows.indexOf(t: subWindow)));
1540 else
1541#endif
1542 rubberBand->setGeometry(subWindow->geometry());
1543
1544 rubberBand->raise();
1545 rubberBand->show();
1546}
1547#endif // QT_CONFIG(rubberBand)
1548/*!
1549 \internal
1550 \since 4.4
1551*/
1552void QMdiAreaPrivate::setViewMode(QMdiArea::ViewMode mode)
1553{
1554 Q_Q(QMdiArea);
1555 if (viewMode == mode || inViewModeChange)
1556 return;
1557
1558 // Just a guard since we cannot set viewMode = mode here.
1559 inViewModeChange = true;
1560
1561#if QT_CONFIG(tabbar)
1562 if (mode == QMdiArea::TabbedView) {
1563 Q_ASSERT(!tabBar);
1564 tabBar = new QMdiAreaTabBar(q);
1565 tabBar->setDocumentMode(documentMode);
1566 tabBar->setTabsClosable(tabsClosable);
1567 tabBar->setMovable(tabsMovable);
1568#if QT_CONFIG(tabwidget)
1569 tabBar->setShape(tabBarShapeFrom(shape: tabShape, position: tabPosition));
1570#endif
1571
1572 isSubWindowsTiled = false;
1573
1574 foreach (QMdiSubWindow *subWindow, childWindows)
1575 tabBar->addTab(icon: subWindow->windowIcon(), text: tabTextFor(subWindow));
1576
1577 QMdiSubWindow *current = q->currentSubWindow();
1578 if (current) {
1579 tabBar->setCurrentIndex(childWindows.indexOf(t: current));
1580 // Restore sub-window (i.e. cleanup buttons in menu bar and window title).
1581 if (current->isMaximized())
1582 current->showNormal();
1583
1584 viewMode = mode;
1585
1586 // Now, maximize it.
1587 if (!q->testOption(opton: QMdiArea::DontMaximizeSubWindowOnActivation)) {
1588 current->showMaximized();
1589 }
1590 } else {
1591 viewMode = mode;
1592 }
1593
1594 if (q->isVisible())
1595 tabBar->show();
1596 updateTabBarGeometry();
1597
1598 QObject::connect(sender: tabBar, SIGNAL(currentChanged(int)), receiver: q, SLOT(_q_currentTabChanged(int)));
1599 QObject::connect(sender: tabBar, SIGNAL(tabCloseRequested(int)), receiver: q, SLOT(_q_closeTab(int)));
1600 QObject::connect(sender: tabBar, SIGNAL(tabMoved(int,int)), receiver: q, SLOT(_q_moveTab(int,int)));
1601 } else
1602#endif // QT_CONFIG(tabbar)
1603 { // SubWindowView
1604#if QT_CONFIG(tabbar)
1605 delete tabBar;
1606 tabBar = nullptr;
1607#endif // QT_CONFIG(tabbar)
1608
1609 viewMode = mode;
1610 q->setViewportMargins(left: 0, top: 0, right: 0, bottom: 0);
1611 indexToLastActiveTab = -1;
1612
1613 QMdiSubWindow *current = q->currentSubWindow();
1614 if (current && current->isMaximized())
1615 current->showNormal();
1616 }
1617
1618 Q_ASSERT(viewMode == mode);
1619 inViewModeChange = false;
1620}
1621
1622#if QT_CONFIG(tabbar)
1623/*!
1624 \internal
1625*/
1626void QMdiAreaPrivate::updateTabBarGeometry()
1627{
1628 if (!tabBar)
1629 return;
1630
1631 Q_Q(QMdiArea);
1632#if QT_CONFIG(tabwidget)
1633 Q_ASSERT(tabBarShapeFrom(tabShape, tabPosition) == tabBar->shape());
1634#endif
1635 const QSize tabBarSizeHint = tabBar->sizeHint();
1636
1637 int areaHeight = q->height();
1638 if (hbar && hbar->isVisible())
1639 areaHeight -= hbar->height();
1640
1641 int areaWidth = q->width();
1642 if (vbar && vbar->isVisible())
1643 areaWidth -= vbar->width();
1644
1645 QRect tabBarRect;
1646#if QT_CONFIG(tabwidget)
1647 switch (tabPosition) {
1648 case QTabWidget::North:
1649 q->setViewportMargins(left: 0, top: tabBarSizeHint.height(), right: 0, bottom: 0);
1650 tabBarRect = QRect(0, 0, areaWidth, tabBarSizeHint.height());
1651 break;
1652 case QTabWidget::South:
1653 q->setViewportMargins(left: 0, top: 0, right: 0, bottom: tabBarSizeHint.height());
1654 tabBarRect = QRect(0, areaHeight - tabBarSizeHint.height(), areaWidth, tabBarSizeHint.height());
1655 break;
1656 case QTabWidget::East:
1657 if (q->layoutDirection() == Qt::LeftToRight)
1658 q->setViewportMargins(left: 0, top: 0, right: tabBarSizeHint.width(), bottom: 0);
1659 else
1660 q->setViewportMargins(left: tabBarSizeHint.width(), top: 0, right: 0, bottom: 0);
1661 tabBarRect = QRect(areaWidth - tabBarSizeHint.width(), 0, tabBarSizeHint.width(), areaHeight);
1662 break;
1663 case QTabWidget::West:
1664 if (q->layoutDirection() == Qt::LeftToRight)
1665 q->setViewportMargins(left: tabBarSizeHint.width(), top: 0, right: 0, bottom: 0);
1666 else
1667 q->setViewportMargins(left: 0, top: 0, right: tabBarSizeHint.width(), bottom: 0);
1668 tabBarRect = QRect(0, 0, tabBarSizeHint.width(), areaHeight);
1669 break;
1670 default:
1671 break;
1672 }
1673#endif // QT_CONFIG(tabwidget)
1674
1675 tabBar->setGeometry(QStyle::visualRect(direction: q->layoutDirection(), boundingRect: q->contentsRect(), logicalRect: tabBarRect));
1676}
1677
1678/*!
1679 \internal
1680*/
1681void QMdiAreaPrivate::refreshTabBar()
1682{
1683 if (!tabBar)
1684 return;
1685
1686 tabBar->setDocumentMode(documentMode);
1687 tabBar->setTabsClosable(tabsClosable);
1688 tabBar->setMovable(tabsMovable);
1689#if QT_CONFIG(tabwidget)
1690 tabBar->setShape(tabBarShapeFrom(shape: tabShape, position: tabPosition));
1691#endif
1692 updateTabBarGeometry();
1693}
1694#endif // QT_CONFIG(tabbar)
1695
1696/*!
1697 Constructs an empty mdi area. \a parent is passed to QWidget's
1698 constructor.
1699*/
1700QMdiArea::QMdiArea(QWidget *parent)
1701 : QAbstractScrollArea(*new QMdiAreaPrivate, parent)
1702{
1703 setBackground(palette().brush(cr: QPalette::Dark));
1704 setFrameStyle(QFrame::NoFrame);
1705 setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1706 setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1707 setViewport(nullptr);
1708 setFocusPolicy(Qt::NoFocus);
1709 QApplication::instance()->installEventFilter(filterObj: this);
1710}
1711
1712/*!
1713 Destroys the MDI area.
1714*/
1715QMdiArea::~QMdiArea()
1716{
1717 Q_D(QMdiArea);
1718 delete d->cascader;
1719 d->cascader = nullptr;
1720
1721 delete d->regularTiler;
1722 d->regularTiler = nullptr;
1723
1724 delete d->iconTiler;
1725 d->iconTiler = nullptr;
1726
1727 delete d->placer;
1728 d->placer = nullptr;
1729}
1730
1731/*!
1732 \reimp
1733*/
1734QSize QMdiArea::sizeHint() const
1735{
1736 // Calculate a proper scale factor for QDesktopWidget::size().
1737 // This also takes into account that we can have nested workspaces.
1738 int nestedCount = 0;
1739 QWidget *widget = this->parentWidget();
1740 while (widget) {
1741 if (qobject_cast<QMdiArea *>(object: widget))
1742 ++nestedCount;
1743 widget = widget->parentWidget();
1744 }
1745 const int scaleFactor = 3 * (nestedCount + 1);
1746
1747 QSize desktopSize = QDesktopWidgetPrivate::size();
1748 QSize size(desktopSize.width() * 2 / scaleFactor, desktopSize.height() * 2 / scaleFactor);
1749 for (QMdiSubWindow *child : d_func()->childWindows) {
1750 if (!sanityCheck(child, where: "QMdiArea::sizeHint"))
1751 continue;
1752 size = size.expandedTo(otherSize: child->sizeHint());
1753 }
1754 return size.expandedTo(otherSize: QApplication::globalStrut());
1755}
1756
1757/*!
1758 \reimp
1759*/
1760QSize QMdiArea::minimumSizeHint() const
1761{
1762 Q_D(const QMdiArea);
1763 QSize size(style()->pixelMetric(metric: QStyle::PM_MdiSubWindowMinimizedWidth, option: nullptr, widget: this),
1764 style()->pixelMetric(metric: QStyle::PM_TitleBarHeight, option: nullptr, widget: this));
1765 size = size.expandedTo(otherSize: QAbstractScrollArea::minimumSizeHint());
1766 if (!d->scrollBarsEnabled()) {
1767 for (QMdiSubWindow *child : d->childWindows) {
1768 if (!sanityCheck(child, where: "QMdiArea::sizeHint"))
1769 continue;
1770 size = size.expandedTo(otherSize: child->minimumSizeHint());
1771 }
1772 }
1773 return size.expandedTo(otherSize: QApplication::globalStrut());
1774}
1775
1776/*!
1777 Returns a pointer to the current subwindow, or \nullptr if there is
1778 no current subwindow.
1779
1780 This function will return the same as activeSubWindow() if
1781 the QApplication containing QMdiArea is active.
1782
1783 \sa activeSubWindow(), QApplication::activeWindow()
1784*/
1785QMdiSubWindow *QMdiArea::currentSubWindow() const
1786{
1787 Q_D(const QMdiArea);
1788 if (d->childWindows.isEmpty())
1789 return nullptr;
1790
1791 if (d->active)
1792 return d->active;
1793
1794 if (d->isActivated && !window()->isMinimized())
1795 return nullptr;
1796
1797 Q_ASSERT(d->indicesToActivatedChildren.count() > 0);
1798 int index = d->indicesToActivatedChildren.at(i: 0);
1799 Q_ASSERT(index >= 0 && index < d->childWindows.size());
1800 QMdiSubWindow *current = d->childWindows.at(i: index);
1801 Q_ASSERT(current);
1802 return current;
1803}
1804
1805/*!
1806 Returns a pointer to the current active subwindow. If no
1807 window is currently active, \nullptr is returned.
1808
1809 Subwindows are treated as top-level windows with respect to
1810 window state, i.e., if a widget outside the MDI area is the active
1811 window, no subwindow will be active. Note that if a widget in the
1812 window in which the MDI area lives gains focus, the window will be
1813 activated.
1814
1815 \sa setActiveSubWindow(), Qt::WindowState
1816*/
1817QMdiSubWindow *QMdiArea::activeSubWindow() const
1818{
1819 Q_D(const QMdiArea);
1820 return d->active;
1821}
1822
1823/*!
1824 Activates the subwindow \a window. If \a window is \nullptr, any
1825 current active window is deactivated.
1826
1827 \sa activeSubWindow()
1828*/
1829void QMdiArea::setActiveSubWindow(QMdiSubWindow *window)
1830{
1831 Q_D(QMdiArea);
1832 if (!window) {
1833 d->activateWindow(child: nullptr);
1834 return;
1835 }
1836
1837 if (Q_UNLIKELY(d->childWindows.isEmpty())) {
1838 qWarning(msg: "QMdiArea::setActiveSubWindow: workspace is empty");
1839 return;
1840 }
1841
1842 if (Q_UNLIKELY(d->childWindows.indexOf(window) == -1)) {
1843 qWarning(msg: "QMdiArea::setActiveSubWindow: window is not inside workspace");
1844 return;
1845 }
1846
1847 d->activateWindow(child: window);
1848}
1849
1850/*!
1851 Closes the active subwindow.
1852
1853 \sa closeAllSubWindows()
1854*/
1855void QMdiArea::closeActiveSubWindow()
1856{
1857 Q_D(QMdiArea);
1858 if (d->active)
1859 d->active->close();
1860}
1861
1862/*!
1863 Returns a list of all subwindows in the MDI area. If \a order is
1864 CreationOrder (the default), the windows are sorted in the order
1865 in which they were inserted into the workspace. If \a order is
1866 StackingOrder, the windows are listed in their stacking order,
1867 with the topmost window as the last item in the list. If \a order
1868 is ActivationHistoryOrder, the windows are listed according to
1869 their recent activation history.
1870
1871 \sa WindowOrder
1872*/
1873QList<QMdiSubWindow *> QMdiArea::subWindowList(WindowOrder order) const
1874{
1875 Q_D(const QMdiArea);
1876 return d->subWindowList(order, reversed: false);
1877}
1878
1879/*!
1880 Closes all subwindows by sending a QCloseEvent to each window.
1881 You may receive subWindowActivated() signals from subwindows
1882 before they are closed (if the MDI area activates the subwindow
1883 when another is closing).
1884
1885 Subwindows that ignore the close event will remain open.
1886
1887 \sa closeActiveSubWindow()
1888*/
1889void QMdiArea::closeAllSubWindows()
1890{
1891 Q_D(QMdiArea);
1892 if (d->childWindows.isEmpty())
1893 return;
1894
1895 d->isSubWindowsTiled = false;
1896 foreach (QMdiSubWindow *child, d->childWindows) {
1897 if (!sanityCheck(child, where: "QMdiArea::closeAllSubWindows"))
1898 continue;
1899 child->close();
1900 }
1901
1902 d->updateScrollBars();
1903}
1904
1905/*!
1906 Gives the keyboard focus to another window in the list of child
1907 windows. The window activated will be the next one determined
1908 by the current \l{QMdiArea::WindowOrder} {activation order}.
1909
1910 \sa activatePreviousSubWindow(), QMdiArea::WindowOrder
1911*/
1912void QMdiArea::activateNextSubWindow()
1913{
1914 Q_D(QMdiArea);
1915 if (d->childWindows.isEmpty())
1916 return;
1917
1918 QMdiSubWindow *next = d->nextVisibleSubWindow(increaseFactor: 1, order: d->activationOrder);
1919 if (next)
1920 d->activateWindow(child: next);
1921}
1922
1923/*!
1924 Gives the keyboard focus to another window in the list of child
1925 windows. The window activated will be the previous one determined
1926 by the current \l{QMdiArea::WindowOrder} {activation order}.
1927
1928 \sa activateNextSubWindow(), QMdiArea::WindowOrder
1929*/
1930void QMdiArea::activatePreviousSubWindow()
1931{
1932 Q_D(QMdiArea);
1933 if (d->childWindows.isEmpty())
1934 return;
1935
1936 QMdiSubWindow *previous = d->nextVisibleSubWindow(increaseFactor: -1, order: d->activationOrder);
1937 if (previous)
1938 d->activateWindow(child: previous);
1939}
1940
1941/*!
1942 Adds \a widget as a new subwindow to the MDI area. If \a
1943 windowFlags are non-zero, they will override the flags set on the
1944 widget.
1945
1946 The \a widget can be either a QMdiSubWindow or another QWidget
1947 (in which case the MDI area will create a subwindow and set the \a
1948 widget as the internal widget).
1949
1950 \note Once the subwindow has been added, its parent will be the
1951 \e{viewport widget} of the QMdiArea.
1952
1953 \snippet mdiarea/mdiareasnippets.cpp 1
1954
1955 When you create your own subwindow, you must set the
1956 Qt::WA_DeleteOnClose widget attribute if you want the window to be
1957 deleted when closed in the MDI area. If not, the window will be
1958 hidden and the MDI area will not activate the next subwindow.
1959
1960 Returns the QMdiSubWindow that is added to the MDI area.
1961
1962 \sa removeSubWindow()
1963*/
1964QMdiSubWindow *QMdiArea::addSubWindow(QWidget *widget, Qt::WindowFlags windowFlags)
1965{
1966 if (Q_UNLIKELY(!widget)) {
1967 qWarning(msg: "QMdiArea::addSubWindow: null pointer to widget");
1968 return nullptr;
1969 }
1970
1971 Q_D(QMdiArea);
1972 // QWidget::setParent clears focusWidget so store it
1973 QWidget *childFocus = widget->focusWidget();
1974 QMdiSubWindow *child = qobject_cast<QMdiSubWindow *>(object: widget);
1975
1976 // Widget is already a QMdiSubWindow
1977 if (child) {
1978 if (Q_UNLIKELY(d->childWindows.indexOf(child) != -1)) {
1979 qWarning(msg: "QMdiArea::addSubWindow: window is already added");
1980 return child;
1981 }
1982 child->setParent(parent: viewport(), f: windowFlags ? windowFlags : child->windowFlags());
1983 // Create a QMdiSubWindow
1984 } else {
1985 child = new QMdiSubWindow(viewport(), windowFlags);
1986 child->setAttribute(Qt::WA_DeleteOnClose);
1987 child->setWidget(widget);
1988 Q_ASSERT(child->testAttribute(Qt::WA_DeleteOnClose));
1989 }
1990
1991 d->appendChild(child);
1992
1993 if (childFocus)
1994 childFocus->setFocus();
1995
1996 return child;
1997}
1998
1999/*!
2000 Removes \a widget from the MDI area. The \a widget must be
2001 either a QMdiSubWindow or a widget that is the internal widget of
2002 a subwindow. Note \a widget is never actually deleted by QMdiArea.
2003 If a QMdiSubWindow is passed in, its parent is set to \nullptr and it is
2004 removed; but if an internal widget is passed in, the child widget
2005 is set to \nullptr and the QMdiSubWindow is \e not removed.
2006
2007 \sa addSubWindow()
2008*/
2009void QMdiArea::removeSubWindow(QWidget *widget)
2010{
2011 if (Q_UNLIKELY(!widget)) {
2012 qWarning(msg: "QMdiArea::removeSubWindow: null pointer to widget");
2013 return;
2014 }
2015
2016 Q_D(QMdiArea);
2017 if (d->childWindows.isEmpty())
2018 return;
2019
2020 if (QMdiSubWindow *child = qobject_cast<QMdiSubWindow *>(object: widget)) {
2021 int index = d->childWindows.indexOf(t: child);
2022 if (Q_UNLIKELY(index == -1)) {
2023 qWarning(msg: "QMdiArea::removeSubWindow: window is not inside workspace");
2024 return;
2025 }
2026 d->disconnectSubWindow(subWindow: child);
2027 d->childWindows.removeAll(t: child);
2028 d->indicesToActivatedChildren.removeAll(t: index);
2029 d->updateActiveWindow(removedIndex: index, activeRemoved: d->active == child);
2030 child->setParent(nullptr);
2031 return;
2032 }
2033
2034 bool found = false;
2035 foreach (QMdiSubWindow *child, d->childWindows) {
2036 if (!sanityCheck(child, where: "QMdiArea::removeSubWindow"))
2037 continue;
2038 if (child->widget() == widget) {
2039 child->setWidget(nullptr);
2040 Q_ASSERT(!child->widget());
2041 found = true;
2042 break;
2043 }
2044 }
2045
2046 if (Q_UNLIKELY(!found))
2047 qWarning(msg: "QMdiArea::removeSubWindow: widget is not child of any window inside QMdiArea");
2048}
2049
2050/*!
2051 \property QMdiArea::background
2052 \brief the background brush for the workspace
2053
2054 This property sets the background brush for the workspace area
2055 itself. By default, it is a gray color, but can be any brush
2056 (e.g., colors, gradients or pixmaps).
2057*/
2058QBrush QMdiArea::background() const
2059{
2060 return d_func()->background;
2061}
2062
2063void QMdiArea::setBackground(const QBrush &brush)
2064{
2065 Q_D(QMdiArea);
2066 if (d->background != brush) {
2067 d->background = brush;
2068 d->viewport->setAttribute(Qt::WA_OpaquePaintEvent, on: brush.isOpaque());
2069 d->viewport->update();
2070 }
2071}
2072
2073
2074/*!
2075 \property QMdiArea::activationOrder
2076 \brief the ordering criteria for subwindow lists
2077 \since 4.4
2078
2079 This property specifies the ordering criteria for the list of
2080 subwindows returned by subWindowList(). By default, it is the window
2081 creation order.
2082
2083 \sa subWindowList()
2084*/
2085QMdiArea::WindowOrder QMdiArea::activationOrder() const
2086{
2087 Q_D(const QMdiArea);
2088 return d->activationOrder;
2089}
2090
2091void QMdiArea::setActivationOrder(WindowOrder order)
2092{
2093 Q_D(QMdiArea);
2094 if (order != d->activationOrder)
2095 d->activationOrder = order;
2096}
2097
2098/*!
2099 If \a on is true, \a option is enabled on the MDI area; otherwise
2100 it is disabled. See AreaOption for the effect of each option.
2101
2102 \sa AreaOption, testOption()
2103*/
2104void QMdiArea::setOption(AreaOption option, bool on)
2105{
2106 Q_D(QMdiArea);
2107 d->options.setFlag(flag: option, on);
2108}
2109
2110/*!
2111 Returns \c true if \a option is enabled; otherwise returns \c false.
2112
2113 \sa AreaOption, setOption()
2114*/
2115bool QMdiArea::testOption(AreaOption option) const
2116{
2117 return d_func()->options & option;
2118}
2119
2120/*!
2121 \property QMdiArea::viewMode
2122 \brief the way sub-windows are displayed in the QMdiArea.
2123 \since 4.4
2124
2125 By default, the SubWindowView is used to display sub-windows.
2126
2127 \sa ViewMode, setTabShape(), setTabPosition()
2128*/
2129QMdiArea::ViewMode QMdiArea::viewMode() const
2130{
2131 Q_D(const QMdiArea);
2132 return d->viewMode;
2133}
2134
2135void QMdiArea::setViewMode(ViewMode mode)
2136{
2137 Q_D(QMdiArea);
2138 d->setViewMode(mode);
2139}
2140
2141#if QT_CONFIG(tabbar)
2142/*!
2143 \property QMdiArea::documentMode
2144 \brief whether the tab bar is set to document mode in tabbed view mode.
2145 \since 4.5
2146
2147 Document mode is disabled by default.
2148
2149 \sa QTabBar::documentMode, setViewMode()
2150*/
2151bool QMdiArea::documentMode() const
2152{
2153 Q_D(const QMdiArea);
2154 return d->documentMode;
2155}
2156
2157void QMdiArea::setDocumentMode(bool enabled)
2158{
2159 Q_D(QMdiArea);
2160 if (d->documentMode == enabled)
2161 return;
2162
2163 d->documentMode = enabled;
2164 d->refreshTabBar();
2165}
2166
2167/*!
2168 \property QMdiArea::tabsClosable
2169 \brief whether the tab bar should place close buttons on each tab in tabbed view mode.
2170 \since 4.8
2171
2172 Tabs are not closable by default.
2173
2174 \sa QTabBar::tabsClosable, setViewMode()
2175*/
2176bool QMdiArea::tabsClosable() const
2177{
2178 Q_D(const QMdiArea);
2179 return d->tabsClosable;
2180}
2181
2182void QMdiArea::setTabsClosable(bool closable)
2183{
2184 Q_D(QMdiArea);
2185 if (d->tabsClosable == closable)
2186 return;
2187
2188 d->tabsClosable = closable;
2189 d->refreshTabBar();
2190}
2191
2192/*!
2193 \property QMdiArea::tabsMovable
2194 \brief whether the user can move the tabs within the tabbar area in tabbed view mode.
2195 \since 4.8
2196
2197 Tabs are not movable by default.
2198
2199 \sa QTabBar::movable, setViewMode()
2200*/
2201bool QMdiArea::tabsMovable() const
2202{
2203 Q_D(const QMdiArea);
2204 return d->tabsMovable;
2205}
2206
2207void QMdiArea::setTabsMovable(bool movable)
2208{
2209 Q_D(QMdiArea);
2210 if (d->tabsMovable == movable)
2211 return;
2212
2213 d->tabsMovable = movable;
2214 d->refreshTabBar();
2215}
2216#endif // QT_CONFIG(tabbar)
2217
2218#if QT_CONFIG(tabwidget)
2219/*!
2220 \property QMdiArea::tabShape
2221 \brief the shape of the tabs in tabbed view mode.
2222 \since 4.4
2223
2224 Possible values for this property are QTabWidget::Rounded
2225 (default) or QTabWidget::Triangular.
2226
2227 \sa QTabWidget::TabShape, setViewMode()
2228*/
2229QTabWidget::TabShape QMdiArea::tabShape() const
2230{
2231 Q_D(const QMdiArea);
2232 return d->tabShape;
2233}
2234
2235void QMdiArea::setTabShape(QTabWidget::TabShape shape)
2236{
2237 Q_D(QMdiArea);
2238 if (d->tabShape == shape)
2239 return;
2240
2241 d->tabShape = shape;
2242 d->refreshTabBar();
2243}
2244
2245/*!
2246 \property QMdiArea::tabPosition
2247 \brief the position of the tabs in tabbed view mode.
2248 \since 4.4
2249
2250 Possible values for this property are described by the
2251 QTabWidget::TabPosition enum.
2252
2253 \sa QTabWidget::TabPosition, setViewMode()
2254*/
2255QTabWidget::TabPosition QMdiArea::tabPosition() const
2256{
2257 Q_D(const QMdiArea);
2258 return d->tabPosition;
2259}
2260
2261void QMdiArea::setTabPosition(QTabWidget::TabPosition position)
2262{
2263 Q_D(QMdiArea);
2264 if (d->tabPosition == position)
2265 return;
2266
2267 d->tabPosition = position;
2268 d->refreshTabBar();
2269}
2270#endif // QT_CONFIG(tabwidget)
2271
2272/*!
2273 \reimp
2274*/
2275void QMdiArea::childEvent(QChildEvent *childEvent)
2276{
2277 Q_D(QMdiArea);
2278 if (childEvent->type() == QEvent::ChildPolished) {
2279 if (QMdiSubWindow *mdiChild = qobject_cast<QMdiSubWindow *>(object: childEvent->child())) {
2280 if (d->childWindows.indexOf(t: mdiChild) == -1)
2281 d->appendChild(child: mdiChild);
2282 }
2283 }
2284}
2285
2286/*!
2287 \reimp
2288*/
2289void QMdiArea::resizeEvent(QResizeEvent *resizeEvent)
2290{
2291 Q_D(QMdiArea);
2292 if (d->childWindows.isEmpty()) {
2293 resizeEvent->ignore();
2294 return;
2295 }
2296
2297#if QT_CONFIG(tabbar)
2298 d->updateTabBarGeometry();
2299#endif
2300
2301 // Re-tile the views if we're in tiled mode. Re-tile means we will change
2302 // the geometry of the children, which in turn means 'isSubWindowsTiled'
2303 // is set to false, so we have to update the state at the end.
2304 if (d->isSubWindowsTiled) {
2305 d->tileCalledFromResizeEvent = true;
2306 tileSubWindows();
2307 d->tileCalledFromResizeEvent = false;
2308 d->isSubWindowsTiled = true;
2309 d->startResizeTimer();
2310 // We don't have scroll bars or any maximized views.
2311 return;
2312 }
2313
2314 // Resize maximized views.
2315 bool hasMaximizedSubWindow = false;
2316 foreach (QMdiSubWindow *child, d->childWindows) {
2317 if (sanityCheck(child, where: "QMdiArea::resizeEvent") && child->isMaximized()
2318 && child->size() != resizeEvent->size()) {
2319 child->resize(resizeEvent->size());
2320 if (!hasMaximizedSubWindow)
2321 hasMaximizedSubWindow = true;
2322 }
2323 }
2324
2325 d->updateScrollBars();
2326
2327 // Minimized views are stacked under maximized views so there's
2328 // no need to re-arrange minimized views on-demand. Start a timer
2329 // just to make things faster with subsequent resize events.
2330 if (hasMaximizedSubWindow)
2331 d->startResizeTimer();
2332 else
2333 d->arrangeMinimizedSubWindows();
2334}
2335
2336/*!
2337 \reimp
2338*/
2339void QMdiArea::timerEvent(QTimerEvent *timerEvent)
2340{
2341 Q_D(QMdiArea);
2342 if (timerEvent->timerId() == d->resizeTimerId) {
2343 killTimer(id: d->resizeTimerId);
2344 d->resizeTimerId = -1;
2345 d->arrangeMinimizedSubWindows();
2346 } else if (timerEvent->timerId() == d->tabToPreviousTimerId) {
2347 killTimer(id: d->tabToPreviousTimerId);
2348 d->tabToPreviousTimerId = -1;
2349 if (d->indexToHighlighted < 0)
2350 return;
2351#if QT_CONFIG(rubberband)
2352 // We're not doing a "quick switch" ... show rubber band.
2353 Q_ASSERT(d->indexToHighlighted < d->childWindows.size());
2354 Q_ASSERT(d->rubberBand);
2355 d->showRubberBandFor(subWindow: d->childWindows.at(i: d->indexToHighlighted));
2356#endif
2357 }
2358}
2359
2360/*!
2361 \reimp
2362*/
2363void QMdiArea::showEvent(QShowEvent *showEvent)
2364{
2365 Q_D(QMdiArea);
2366 if (!d->pendingRearrangements.isEmpty()) {
2367 bool skipPlacement = false;
2368 foreach (Rearranger *rearranger, d->pendingRearrangements) {
2369 // If this is the case, we don't have to lay out pending child windows
2370 // since the rearranger will find a placement for them.
2371 if (rearranger->type() != Rearranger::IconTiler && !skipPlacement)
2372 skipPlacement = true;
2373 d->rearrange(rearranger);
2374 }
2375 d->pendingRearrangements.clear();
2376
2377 if (skipPlacement && !d->pendingPlacements.isEmpty())
2378 d->pendingPlacements.clear();
2379 }
2380
2381 if (!d->pendingPlacements.isEmpty()) {
2382 foreach (QMdiSubWindow *window, d->pendingPlacements) {
2383 if (!window)
2384 continue;
2385 if (!window->testAttribute(attribute: Qt::WA_Resized)) {
2386 QSize newSize(window->sizeHint().boundedTo(otherSize: viewport()->size()));
2387 window->resize(newSize.expandedTo(otherSize: qSmartMinSize(w: window)));
2388 }
2389 if (!window->testAttribute(attribute: Qt::WA_Moved) && !window->isMinimized()
2390 && !window->isMaximized()) {
2391 d->place(placer: d->placer, child: window);
2392 }
2393 }
2394 d->pendingPlacements.clear();
2395 }
2396
2397 d->setChildActivationEnabled(enable: true);
2398 d->activateCurrentWindow();
2399
2400 QAbstractScrollArea::showEvent(event: showEvent);
2401}
2402
2403/*!
2404 \reimp
2405*/
2406bool QMdiArea::viewportEvent(QEvent *event)
2407{
2408 Q_D(QMdiArea);
2409 switch (event->type()) {
2410 case QEvent::ChildRemoved: {
2411 d->isSubWindowsTiled = false;
2412 QObject *removedChild = static_cast<QChildEvent *>(event)->child();
2413 for (int i = 0; i < d->childWindows.size(); ++i) {
2414 QObject *child = d->childWindows.at(i);
2415 if (!child || child == removedChild || !child->parent()
2416 || child->parent() != viewport()) {
2417 if (!testOption(option: DontMaximizeSubWindowOnActivation)) {
2418 // In this case we can only rely on the child being a QObject
2419 // (or 0), but let's try and see if we can get more information.
2420 QWidget *mdiChild = qobject_cast<QWidget *>(o: removedChild);
2421 if (mdiChild && mdiChild->isMaximized())
2422 d->showActiveWindowMaximized = true;
2423 }
2424 d->disconnectSubWindow(subWindow: child);
2425 const bool activeRemoved = i == d->indicesToActivatedChildren.at(i: 0);
2426 d->childWindows.removeAt(i);
2427 d->indicesToActivatedChildren.removeAll(t: i);
2428 d->updateActiveWindow(removedIndex: i, activeRemoved);
2429 d->arrangeMinimizedSubWindows();
2430 break;
2431 }
2432 }
2433 d->updateScrollBars();
2434 break;
2435 }
2436 case QEvent::Destroy:
2437 d->isSubWindowsTiled = false;
2438 d->resetActiveWindow();
2439 d->childWindows.clear();
2440 qWarning(msg: "QMdiArea: Deleting the view port is undefined, use setViewport instead.");
2441 break;
2442 default:
2443 break;
2444 }
2445 return QAbstractScrollArea::viewportEvent(event);
2446}
2447
2448/*!
2449 \reimp
2450*/
2451void QMdiArea::scrollContentsBy(int dx, int dy)
2452{
2453 Q_D(QMdiArea);
2454 const bool wasSubWindowsTiled = d->isSubWindowsTiled;
2455 d->ignoreGeometryChange = true;
2456 viewport()->scroll(dx: isLeftToRight() ? dx : -dx, dy);
2457 d->arrangeMinimizedSubWindows();
2458 d->ignoreGeometryChange = false;
2459 if (wasSubWindowsTiled)
2460 d->isSubWindowsTiled = true;
2461}
2462
2463/*!
2464 Arranges all child windows in a tile pattern.
2465
2466 \sa cascadeSubWindows()
2467*/
2468void QMdiArea::tileSubWindows()
2469{
2470 Q_D(QMdiArea);
2471 if (!d->regularTiler)
2472 d->regularTiler = new RegularTiler;
2473 d->rearrange(rearranger: d->regularTiler);
2474}
2475
2476/*!
2477 Arranges all the child windows in a cascade pattern.
2478
2479 \sa tileSubWindows()
2480*/
2481void QMdiArea::cascadeSubWindows()
2482{
2483 Q_D(QMdiArea);
2484 if (!d->cascader)
2485 d->cascader = new SimpleCascader;
2486 d->rearrange(rearranger: d->cascader);
2487}
2488
2489/*!
2490 \reimp
2491*/
2492bool QMdiArea::event(QEvent *event)
2493{
2494 Q_D(QMdiArea);
2495 switch (event->type()) {
2496 case QEvent::WindowActivate: {
2497 d->isActivated = true;
2498 if (d->childWindows.isEmpty())
2499 break;
2500 if (!d->active)
2501 d->activateCurrentWindow();
2502 d->setChildActivationEnabled(enable: false, onlyNextActivationEvent: true);
2503 break;
2504 }
2505 case QEvent::WindowDeactivate:
2506 d->isActivated = false;
2507 d->setChildActivationEnabled(enable: false, onlyNextActivationEvent: true);
2508 break;
2509 case QEvent::StyleChange:
2510 // Re-tile the views if we're in tiled mode. Re-tile means we will change
2511 // the geometry of the children, which in turn means 'isSubWindowsTiled'
2512 // is set to false, so we have to update the state at the end.
2513 if (d->isSubWindowsTiled) {
2514 tileSubWindows();
2515 d->isSubWindowsTiled = true;
2516 }
2517 break;
2518 case QEvent::WindowIconChange:
2519 foreach (QMdiSubWindow *window, d->childWindows) {
2520 if (sanityCheck(child: window, where: "QMdiArea::WindowIconChange"))
2521 QCoreApplication::sendEvent(receiver: window, event);
2522 }
2523 break;
2524 case QEvent::Hide:
2525 d->setActive(subWindow: d->active, active: false, changeFocus: false);
2526 d->setChildActivationEnabled(enable: false);
2527 break;
2528#if QT_CONFIG(tabbar)
2529 case QEvent::LayoutDirectionChange:
2530 d->updateTabBarGeometry();
2531 break;
2532#endif
2533 default:
2534 break;
2535 }
2536 return QAbstractScrollArea::event(event);
2537}
2538
2539/*!
2540 \reimp
2541*/
2542bool QMdiArea::eventFilter(QObject *object, QEvent *event)
2543{
2544 if (!object)
2545 return QAbstractScrollArea::eventFilter(object, event);
2546
2547 Q_D(QMdiArea);
2548 // Global key events with Ctrl modifier.
2549 if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) {
2550
2551 QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
2552 // Ingore key events without a Ctrl modifier (except for press/release on the modifier itself).
2553 if (!(keyEvent->modifiers() & Qt::ControlModifier) && keyEvent->key() != Qt::Key_Control)
2554 return QAbstractScrollArea::eventFilter(object, event);
2555
2556 // Find closest mdi area (in case we have a nested workspace).
2557 QMdiArea *area = mdiAreaParent(widget: static_cast<QWidget *>(object));
2558 if (!area)
2559 return QAbstractScrollArea::eventFilter(object, event);
2560
2561 const bool keyPress = (event->type() == QEvent::KeyPress);
2562
2563 // 1) Ctrl-Tab once -> activate the previously active window.
2564 // 2) Ctrl-Tab (Tab, Tab, ...) -> iterate through all windows (activateNextSubWindow()).
2565 // 3) Ctrl-Shift-Tab (Tab, Tab, ...) -> iterate through all windows in the opposite
2566 // direction (activatePreviousSubWindow())
2567 switch (keyEvent->key()) {
2568 case Qt::Key_Control:
2569 if (keyPress)
2570 area->d_func()->startTabToPreviousTimer();
2571 else
2572 area->d_func()->activateHighlightedWindow();
2573 break;
2574 case Qt::Key_Tab:
2575 case Qt::Key_Backtab:
2576 if (keyPress)
2577 area->d_func()->highlightNextSubWindow(increaseFactor: keyEvent->key() == Qt::Key_Tab ? 1 : -1);
2578 return true;
2579#if QT_CONFIG(rubberband)
2580 case Qt::Key_Escape:
2581 area->d_func()->hideRubberBand();
2582 break;
2583#endif
2584 default:
2585 break;
2586 }
2587 return QAbstractScrollArea::eventFilter(object, event);
2588 }
2589
2590 QMdiSubWindow *subWindow = qobject_cast<QMdiSubWindow *>(object);
2591
2592 if (!subWindow) {
2593 // QApplication events:
2594 if (event->type() == QEvent::ApplicationActivate && !d->active
2595 && isVisible() && !window()->isMinimized()) {
2596 d->activateCurrentWindow();
2597 } else if (event->type() == QEvent::ApplicationDeactivate && d->active) {
2598 d->setActive(subWindow: d->active, active: false, changeFocus: false);
2599 }
2600 return QAbstractScrollArea::eventFilter(object, event);
2601 }
2602
2603 if (subWindow->mdiArea() != this)
2604 return QAbstractScrollArea::eventFilter(object, event);
2605
2606 // QMdiSubWindow events:
2607 switch (event->type()) {
2608 case QEvent::Move:
2609 case QEvent::Resize:
2610 if (d->tileCalledFromResizeEvent)
2611 break;
2612 d->updateScrollBars();
2613 if (!subWindow->isMinimized())
2614 d->isSubWindowsTiled = false;
2615 break;
2616 case QEvent::Show:
2617#if QT_CONFIG(tabbar)
2618 if (d->tabBar) {
2619 const int tabIndex = d->childWindows.indexOf(t: subWindow);
2620 if (!d->tabBar->isTabEnabled(index: tabIndex))
2621 d->tabBar->setTabEnabled(index: tabIndex, enabled: true);
2622 }
2623#endif // QT_CONFIG(tabbar)
2624 Q_FALLTHROUGH();
2625 case QEvent::Hide:
2626 // Do not reset the isSubWindowsTiled flag if the event is a spontaneous system window event.
2627 // This ensures that tiling will be performed during the resizeEvent after an application
2628 // window minimize (hide) and then restore (show).
2629 if (!event->spontaneous())
2630 d->isSubWindowsTiled = false;
2631 break;
2632#if QT_CONFIG(rubberband)
2633 case QEvent::Close:
2634 if (d->childWindows.indexOf(t: subWindow) == d->indexToHighlighted)
2635 d->hideRubberBand();
2636 break;
2637#endif
2638#if QT_CONFIG(tabbar)
2639 case QEvent::WindowTitleChange:
2640 case QEvent::ModifiedChange:
2641 if (d->tabBar)
2642 d->tabBar->setTabText(index: d->childWindows.indexOf(t: subWindow), text: tabTextFor(subWindow));
2643 break;
2644 case QEvent::WindowIconChange:
2645 if (d->tabBar)
2646 d->tabBar->setTabIcon(index: d->childWindows.indexOf(t: subWindow), icon: subWindow->windowIcon());
2647 break;
2648#endif // QT_CONFIG(tabbar)
2649 default:
2650 break;
2651 }
2652 return QAbstractScrollArea::eventFilter(object, event);
2653}
2654
2655/*!
2656 \reimp
2657*/
2658void QMdiArea::paintEvent(QPaintEvent *paintEvent)
2659{
2660 Q_D(QMdiArea);
2661 QPainter painter(d->viewport);
2662 for (const QRect &exposedRect : paintEvent->region())
2663 painter.fillRect(exposedRect, d->background);
2664}
2665
2666/*!
2667 This slot is called by QAbstractScrollArea after setViewport() has been
2668 called. Reimplement this function in a subclass of QMdiArea to
2669 initialize the new \a viewport before it is used.
2670
2671 \sa setViewport()
2672*/
2673void QMdiArea::setupViewport(QWidget *viewport)
2674{
2675 Q_D(QMdiArea);
2676 if (viewport)
2677 viewport->setAttribute(Qt::WA_OpaquePaintEvent, on: d->background.isOpaque());
2678 foreach (QMdiSubWindow *child, d->childWindows) {
2679 if (!sanityCheck(child, where: "QMdiArea::setupViewport"))
2680 continue;
2681 child->setParent(parent: viewport, f: child->windowFlags());
2682 }
2683}
2684
2685QT_END_NAMESPACE
2686
2687#include "moc_qmdiarea.cpp"
2688

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