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