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