src/gui/widgets/qmdiarea.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/gui/widgets/qmdiarea.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,2597 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+    \class QMdiArea
+    \brief The QMdiArea widget provides an area in which MDI windows are displayed.
+    \since 4.3
+    \ingroup mainwindow-classes
+
+
+    QMdiArea functions, essentially, like a window manager for MDI
+    windows. For instance, it draws the windows it manages on itself
+    and arranges them in a cascading or tile pattern. QMdiArea is
+    commonly used as the center widget in a QMainWindow to create MDI
+    applications, but can also be placed in any layout. The following
+    code adds an area to a main window:
+
+    \snippet doc/src/snippets/mdiareasnippets.cpp 0
+
+    Unlike the window managers for top-level windows, all window flags
+    (Qt::WindowFlags) are supported by QMdiArea as long as the flags
+    are supported by the current widget style. If a specific flag is
+    not supported by the style (e.g., the
+    \l{Qt::}{WindowShadeButtonHint}), you can still shade the window
+    with showShaded().
+
+    Subwindows in QMdiArea are instances of QMdiSubWindow. They
+    are added to an MDI area with addSubWindow(). It is common to pass
+    a QWidget, which is set as the internal widget, to this function,
+    but it is also possible to pass a QMdiSubWindow directly.The class
+    inherits QWidget, and you can use the same API as with a normal
+    top-level window when programming. QMdiSubWindow also has behavior
+    that is specific to MDI windows. See the QMdiSubWindow class
+    description for more details.
+
+    A subwindow becomes active when it gets the keyboard focus, or
+    when setFocus() is called. The user activates a window by moving
+    focus in the usual ways. The MDI area emits the
+    subWindowActivated() signal when the active window changes, and
+    the activeSubWindow() function returns the active subwindow.
+
+    The convenience function subWindowList() returns a list of all
+    subwindows. This information could be used in a popup menu
+    containing a list of windows, for example.
+
+    The subwindows are sorted by the current
+    \l{QMdiArea::}{WindowOrder}. This is used for the subWindowList()
+    and for activateNextSubWindow() and acivatePreviousSubWindow().
+    Also, it is used when cascading or tiling the windows with
+    cascadeSubWindows() and tileSubWindows().
+
+    QMdiArea provides two built-in layout strategies for
+    subwindows: cascadeSubWindows() and tileSubWindows(). Both are
+    slots and are easily connected to menu entries.
+
+    \table
+    \row \o \inlineimage mdi-cascade.png
+         \o \inlineimage mdi-tile.png
+    \endtable
+
+    \note The default scroll bar property for QMdiArea is Qt::ScrollBarAlwaysOff.
+
+    \sa QMdiSubWindow
+*/
+
+/*!
+    \fn QMdiArea::subWindowActivated(QMdiSubWindow *window)
+
+    QMdiArea emits this signal after \a window has been activated. When \a
+    window is 0, QMdiArea has just deactivated its last active window, and
+    there are no active windows on the workspace.
+
+    \sa QMdiArea::activeSubWindow()
+*/
+
+/*!
+    \enum QMdiArea::AreaOption
+
+    This enum describes options that customize the behavior of the
+    QMdiArea.
+
+    \value DontMaximizeSubWindowOnActivation When the active subwindow
+    is maximized, the default behavior is to maximize the next
+    subwindow that is activated. Set this option if you do not want
+    this behavior.
+*/
+
+/*!
+    \enum QMdiArea::WindowOrder
+
+    Specifies the criteria to use for ordering the list of child windows
+    returned by subWindowList(). The functions cascadeSubWindows() and
+    tileSubWindows() follow this order when arranging the windows.
+
+    \value CreationOrder The windows are returned in the order of
+    their creation.
+
+    \value StackingOrder The windows are returned in the order in
+    which they are stacked, with the top-most window being last in
+    the list.
+
+    \value ActivationHistoryOrder The windows are returned in the order in
+    which they were activated.
+
+    \sa subWindowList()
+*/
+
+/*!
+    \enum QMdiArea::ViewMode
+    \since 4.4
+
+    This enum describes the view mode of the area; i.e. how sub-windows
+    will be displayed.
+
+    \value SubWindowView Display sub-windows with window frames (default).
+    \value TabbedView Display sub-windows with tabs in a tab bar.
+
+    \sa setViewMode()
+*/
+
+#include "qmdiarea_p.h"
+
+#ifndef QT_NO_MDIAREA
+
+#include <QApplication>
+#include <QStyle>
+#if defined(Q_WS_MAC) && !defined(QT_NO_STYLE_MAC)
+#include <QMacStyle>
+#endif
+#include <QChildEvent>
+#include <QResizeEvent>
+#include <QScrollBar>
+#include <QtAlgorithms>
+#include <QMutableListIterator>
+#include <QPainter>
+#include <QFontMetrics>
+#include <QStyleOption>
+#include <QDesktopWidget>
+#include <QDebug>
+#include <qmath.h>
+#include <private/qlayoutengine_p.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace QMdi;
+
+// Asserts in debug mode, gives warning otherwise.
+static bool sanityCheck(const QMdiSubWindow * const child, const char *where)
+{
+    if (!child) {
+        const char error[] = "null pointer";
+        Q_ASSERT_X(false, where, error);
+        qWarning("%s:%s", where, error);
+        return false;
+    }
+    return true;
+}
+
+static bool sanityCheck(const QList<QWidget *> &widgets, const int index, const char *where)
+{
+    if (index < 0 || index >= widgets.size()) {
+        const char error[] = "index out of range";
+        Q_ASSERT_X(false, where, error);
+        qWarning("%s:%s", where, error);
+        return false;
+    }
+    if (!widgets.at(index)) {
+        const char error[] = "null pointer";
+        Q_ASSERT_X(false, where, error);
+        qWarning("%s:%s", where, error);
+        return false;
+    }
+    return true;
+}
+
+static void setIndex(int *index, int candidate, int min, int max, bool isIncreasing)
+{
+    if (!index)
+        return;
+
+    if (isIncreasing) {
+        if (candidate > max)
+            *index = min;
+        else
+            *index = qMax(candidate, min);
+    } else {
+        if (candidate < min)
+            *index = max;
+        else
+            *index = qMin(candidate, max);
+    }
+    Q_ASSERT(*index >= min && *index <= max);
+}
+
+static inline bool useScrollBar(const QRect &childrenRect, const QSize &maxViewportSize,
+                                Qt::Orientation orientation)
+{
+    if (orientation == Qt::Horizontal)
+        return  childrenRect.width() > maxViewportSize.width()
+                || childrenRect.left() < 0
+                || childrenRect.right() >= maxViewportSize.width();
+    else
+        return childrenRect.height() > maxViewportSize.height()
+               || childrenRect.top() < 0
+               || childrenRect.bottom() >= maxViewportSize.height();
+}
+
+// Returns the closest mdi area containing the widget (if any).
+static inline QMdiArea *mdiAreaParent(QWidget *widget)
+{
+    if (!widget)
+        return 0;
+
+    QWidget *parent = widget->parentWidget();
+    while (parent) {
+        if (QMdiArea *area = qobject_cast<QMdiArea *>(parent))
+            return area;
+        parent = parent->parentWidget();
+    }
+    return 0;
+}
+
+#ifndef QT_NO_TABWIDGET
+static inline QTabBar::Shape tabBarShapeFrom(QTabWidget::TabShape shape, QTabWidget::TabPosition position)
+{
+    const bool rounded = (shape == QTabWidget::Rounded);
+    if (position == QTabWidget::North)
+        return rounded ? QTabBar::RoundedNorth : QTabBar::TriangularNorth;
+    if (position == QTabWidget::South)
+        return rounded ? QTabBar::RoundedSouth : QTabBar::TriangularSouth;
+    if (position == QTabWidget::East)
+        return rounded ? QTabBar::RoundedEast : QTabBar::TriangularEast;
+    if (position == QTabWidget::West)
+        return rounded ? QTabBar::RoundedWest : QTabBar::TriangularWest;
+    return QTabBar::RoundedNorth;
+}
+#endif // QT_NO_TABWIDGET
+
+static inline QString tabTextFor(QMdiSubWindow *subWindow)
+{
+    if (!subWindow)
+        return QString();
+
+    QString title = subWindow->windowTitle();
+    if (subWindow->isWindowModified()) {
+        title.replace(QLatin1String("[*]"), QLatin1String("*"));
+    } else {
+        extern QString qt_setWindowTitle_helperHelper(const QString&, const QWidget*);
+        title = qt_setWindowTitle_helperHelper(title, subWindow);
+    }
+
+    return title.isEmpty() ? QMdiArea::tr("(Untitled)") : title;
+}
+
+/*!
+    \internal
+*/
+void RegularTiler::rearrange(QList<QWidget *> &widgets, const QRect &domain) const
+{
+    if (widgets.isEmpty())
+        return;
+
+    const int n = widgets.size();
+    const int ncols = qMax(qCeil(qSqrt(qreal(n))), 1);
+    const int nrows = qMax((n % ncols) ? (n / ncols + 1) : (n / ncols), 1);
+    const int nspecial = (n % ncols) ? (ncols - n % ncols) : 0;
+    const int dx = domain.width()  / ncols;
+    const int dy = domain.height() / nrows;
+
+    int i = 0;
+    for (int row = 0; row < nrows; ++row) {
+        const int y1 = int(row * (dy + 1));
+        for (int col = 0; col < ncols; ++col) {
+            if (row == 1 && col < nspecial)
+                continue;
+            const int x1 = int(col * (dx + 1));
+            int x2 = int(x1 + dx);
+            int y2 = int(y1 + dy);
+            if (row == 0 && col < nspecial) {
+                y2 *= 2;
+                if (nrows != 2)
+                    y2 += 1;
+                else
+                    y2 = domain.bottom();
+            }
+            if (col == ncols - 1 && x2 != domain.right())
+                x2 = domain.right();
+            if (row == nrows - 1 && y2 != domain.bottom())
+                y2 = domain.bottom();
+            if (!sanityCheck(widgets, i, "RegularTiler"))
+                continue;
+            QWidget *widget = widgets.at(i++);
+            QRect newGeometry = QRect(QPoint(x1, y1), QPoint(x2, y2));
+            widget->setGeometry(QStyle::visualRect(widget->layoutDirection(), domain, newGeometry));
+        }
+    }
+}
+
+/*!
+    \internal
+*/
+void SimpleCascader::rearrange(QList<QWidget *> &widgets, const QRect &domain) const
+{
+    if (widgets.isEmpty())
+        return;
+
+    // Tunables:
+    const int topOffset = 0;
+    const int bottomOffset = 50;
+    const int leftOffset = 0;
+    const int rightOffset = 100;
+    const int dx = 10;
+
+    QStyleOptionTitleBar options;
+    options.initFrom(widgets.at(0));
+    int titleBarHeight = widgets.at(0)->style()->pixelMetric(QStyle::PM_TitleBarHeight, &options, widgets.at(0));
+#if defined(Q_WS_MAC) && !defined(QT_NO_STYLE_MAC)
+    // ### Remove this after the mac style has been fixed
+    if (qobject_cast<QMacStyle *>(widgets.at(0)->style()))
+        titleBarHeight -= 4;
+#endif
+    const QFontMetrics fontMetrics = QFontMetrics(QApplication::font("QWorkspaceTitleBar"));
+    const int dy = qMax(titleBarHeight - (titleBarHeight - fontMetrics.height()) / 2, 1);
+
+    const int n = widgets.size();
+    const int nrows = qMax((domain.height() - (topOffset + bottomOffset)) / dy, 1);
+    const int ncols = qMax(n / nrows + ((n % nrows) ? 1 : 0), 1);
+    const int dcol = (domain.width() - (leftOffset + rightOffset)) / ncols;
+
+    int i = 0;
+    for (int row = 0; row < nrows; ++row) {
+        for (int col = 0; col < ncols; ++col) {
+            const int x = leftOffset + row * dx + col * dcol;
+            const int y = topOffset + row * dy;
+            if (!sanityCheck(widgets, i, "SimpleCascader"))
+                continue;
+            QWidget *widget = widgets.at(i++);
+            QRect newGeometry = QRect(QPoint(x, y), widget->sizeHint());
+            widget->setGeometry(QStyle::visualRect(widget->layoutDirection(), domain, newGeometry));
+            if (i == n)
+                return;
+        }
+    }
+}
+
+/*!
+    \internal
+*/
+void IconTiler::rearrange(QList<QWidget *> &widgets, const QRect &domain) const
+{
+    if (widgets.isEmpty() || !sanityCheck(widgets, 0, "IconTiler"))
+        return;
+
+    const int n = widgets.size();
+    const int width = widgets.at(0)->width();
+    const int height = widgets.at(0)->height();
+    const int ncols = qMax(domain.width() / width, 1);
+    const int nrows = n / ncols + ((n % ncols) ? 1 : 0);
+
+    int i = 0;
+    for (int row = 0; row < nrows; ++row) {
+        for (int col = 0; col < ncols; ++col) {
+            const int x = col * width;
+            const int y = domain.height() - height - row * height;
+            if (!sanityCheck(widgets, i, "IconTiler"))
+                continue;
+            QWidget *widget = widgets.at(i++);
+            QPoint newPos(x, y);
+            QRect newGeometry = QRect(newPos.x(), newPos.y(), widget->width(), widget->height());
+            widget->setGeometry(QStyle::visualRect(widget->layoutDirection(), domain, newGeometry));
+            if (i == n)
+                return;
+        }
+    }
+}
+
+/*!
+    \internal
+    Calculates the accumulated overlap (intersection area) between 'source' and 'rects'.
+*/
+int MinOverlapPlacer::accumulatedOverlap(const QRect &source, const QList<QRect> &rects)
+{
+    int accOverlap = 0;
+    foreach (const QRect &rect, rects) {
+        QRect intersection = source.intersected(rect);
+        accOverlap += intersection.width() * intersection.height();
+    }
+    return accOverlap;
+}
+
+
+/*!
+    \internal
+    Finds among 'source' the rectangle with the minimum accumulated overlap with the
+    rectangles in 'rects'.
+*/
+QRect MinOverlapPlacer::findMinOverlapRect(const QList<QRect> &source, const QList<QRect> &rects)
+{
+    int minAccOverlap = -1;
+    QRect minAccOverlapRect;
+    foreach (const QRect &srcRect, source) {
+        const int accOverlap = accumulatedOverlap(srcRect, rects);
+        if (accOverlap < minAccOverlap || minAccOverlap == -1) {
+            minAccOverlap = accOverlap;
+            minAccOverlapRect = srcRect;
+        }
+    }
+    return minAccOverlapRect;
+}
+
+/*!
+    \internal
+    Gets candidates for the final placement.
+*/
+void MinOverlapPlacer::getCandidatePlacements(const QSize &size, const QList<QRect> &rects,
+                                              const QRect &domain,QList<QRect> &candidates)
+{
+    QSet<int> xset;
+    QSet<int> yset;
+    xset << domain.left() << domain.right() - size.width() + 1;
+    yset << domain.top();
+    if (domain.bottom() - size.height() + 1 >= 0)
+        yset << domain.bottom() - size.height() + 1;
+    foreach (const QRect &rect, rects) {
+        xset << rect.right() + 1;
+        yset << rect.bottom() + 1;
+    }
+
+    QList<int> xlist = xset.values();
+    qSort(xlist.begin(), xlist.end());
+    QList<int> ylist = yset.values();
+    qSort(ylist.begin(), ylist.end());
+
+    foreach (int y, ylist)
+        foreach (int x, xlist)
+            candidates << QRect(QPoint(x, y), size);
+}
+
+/*!
+    \internal
+    Finds all rectangles in 'source' not completely inside 'domain'. The result is stored
+    in 'result' and also removed from 'source'.
+*/
+void MinOverlapPlacer::findNonInsiders(const QRect &domain, QList<QRect> &source,
+                                       QList<QRect> &result)
+{
+    QMutableListIterator<QRect> it(source);
+    while (it.hasNext()) {
+        const QRect srcRect = it.next();
+        if (!domain.contains(srcRect)) {
+            result << srcRect;
+            it.remove();
+        }
+    }
+}
+
+/*!
+   \internal
+    Finds all rectangles in 'source' that overlaps 'domain' by the maximum overlap area
+    between 'domain' and any rectangle in 'source'. The result is stored in 'result'.
+*/
+void MinOverlapPlacer::findMaxOverlappers(const QRect &domain, const QList<QRect> &source,
+                                          QList<QRect> &result)
+{
+    int maxOverlap = -1;
+    foreach (const QRect &srcRect, source) {
+        QRect intersection = domain.intersected(srcRect);
+        const int overlap = intersection.width() * intersection.height();
+        if (overlap >= maxOverlap || maxOverlap == -1) {
+            if (overlap > maxOverlap) {
+                maxOverlap = overlap;
+                result.clear();
+            }
+            result << srcRect;
+        }
+    }
+}
+
+/*!
+   \internal
+    Finds among the rectangles in 'source' the best placement. Here, 'best' means the
+    placement that overlaps the rectangles in 'rects' as little as possible while at the
+    same time being as much as possible inside 'domain'.
+*/
+QPoint MinOverlapPlacer::findBestPlacement(const QRect &domain, const QList<QRect> &rects,
+                                           QList<QRect> &source)
+{
+    QList<QRect> nonInsiders;
+    findNonInsiders(domain, source, nonInsiders);
+
+    if (!source.empty())
+        return findMinOverlapRect(source, rects).topLeft();
+
+    QList<QRect> maxOverlappers;
+    findMaxOverlappers(domain, nonInsiders, maxOverlappers);
+    return findMinOverlapRect(maxOverlappers, rects).topLeft();
+}
+
+
+/*!
+    \internal
+    Places the rectangle defined by 'size' relative to 'rects' and 'domain' so that it
+    overlaps 'rects' as little as possible and 'domain' as much as possible.
+    Returns the position of the resulting rectangle.
+*/
+QPoint MinOverlapPlacer::place(const QSize &size, const QList<QRect> &rects,
+                               const QRect &domain) const
+{
+    if (size.isEmpty() || !domain.isValid())
+        return QPoint();
+    foreach (const QRect &rect, rects) {
+        if (!rect.isValid())
+            return QPoint();
+    }
+
+    QList<QRect> candidates;
+    getCandidatePlacements(size, rects, domain, candidates);
+    return findBestPlacement(domain, rects, candidates);
+}
+
+#ifndef QT_NO_TABBAR
+class QMdiAreaTabBar : public QTabBar
+{
+public:
+    QMdiAreaTabBar(QWidget *parent) : QTabBar(parent) {}
+
+protected:
+    void mousePressEvent(QMouseEvent *event);
+#ifndef QT_NO_CONTEXTMENU
+    void contextMenuEvent(QContextMenuEvent *event);
+#endif
+
+private:
+    QMdiSubWindow *subWindowFromIndex(int index) const;
+};
+
+/*!
+    \internal
+*/
+void QMdiAreaTabBar::mousePressEvent(QMouseEvent *event)
+{
+    if (event->button() != Qt::MidButton) {
+        QTabBar::mousePressEvent(event);
+        return;
+    }
+
+    QMdiSubWindow *subWindow = subWindowFromIndex(tabAt(event->pos()));
+    if (!subWindow) {
+        event->ignore();
+        return;
+    }
+
+    subWindow->close();
+}
+
+#ifndef QT_NO_CONTEXTMENU
+/*!
+    \internal
+*/
+void QMdiAreaTabBar::contextMenuEvent(QContextMenuEvent *event)
+{
+    QPointer<QMdiSubWindow> subWindow = subWindowFromIndex(tabAt(event->pos()));
+    if (!subWindow || subWindow->isHidden()) {
+        event->ignore();
+        return;
+    }
+
+#ifndef QT_NO_MENU
+    QMdiSubWindowPrivate *subWindowPrivate = subWindow->d_func();
+    if (!subWindowPrivate->systemMenu) {
+        event->ignore();
+        return;
+    }
+
+    QMdiSubWindow *currentSubWindow = subWindowFromIndex(currentIndex());
+    Q_ASSERT(currentSubWindow);
+
+    // We don't want these actions to show up in the system menu when the
+    // current sub-window is maximized, i.e. covers the entire viewport.
+    if (currentSubWindow->isMaximized()) {
+        subWindowPrivate->setVisible(QMdiSubWindowPrivate::MoveAction, false);
+        subWindowPrivate->setVisible(QMdiSubWindowPrivate::ResizeAction, false);
+        subWindowPrivate->setVisible(QMdiSubWindowPrivate::MinimizeAction, false);
+        subWindowPrivate->setVisible(QMdiSubWindowPrivate::MaximizeAction, false);
+        subWindowPrivate->setVisible(QMdiSubWindowPrivate::MaximizeAction, false);
+        subWindowPrivate->setVisible(QMdiSubWindowPrivate::StayOnTopAction, false);
+    }
+
+    // Show system menu.
+    subWindowPrivate->systemMenu->exec(event->globalPos());
+    if (!subWindow)
+        return;
+
+    // Restore action visibility.
+    subWindowPrivate->updateActions();
+#endif // QT_NO_MENU
+}
+#endif // QT_NO_CONTEXTMENU
+
+/*!
+    \internal
+*/
+QMdiSubWindow *QMdiAreaTabBar::subWindowFromIndex(int index) const
+{
+    if (index < 0 || index >= count())
+        return 0;
+
+    QMdiArea *mdiArea = qobject_cast<QMdiArea *>(parentWidget());
+    Q_ASSERT(mdiArea);
+
+    const QList<QMdiSubWindow *> subWindows = mdiArea->subWindowList();
+    Q_ASSERT(index < subWindows.size());
+
+    QMdiSubWindow *subWindow = mdiArea->subWindowList().at(index);
+    Q_ASSERT(subWindow);
+
+    return subWindow;
+}
+#endif // QT_NO_TABBAR
+
+/*!
+    \internal
+*/
+QMdiAreaPrivate::QMdiAreaPrivate()
+    : cascader(0),
+      regularTiler(0),
+      iconTiler(0),
+      placer(0),
+#ifndef QT_NO_RUBBERBAND
+      rubberBand(0),
+#endif
+#ifndef QT_NO_TABBAR
+      tabBar(0),
+#endif
+      activationOrder(QMdiArea::CreationOrder),
+      viewMode(QMdiArea::SubWindowView),
+#ifndef QT_NO_TABBAR
+      documentMode(false),
+#endif
+#ifndef QT_NO_TABWIDGET
+      tabShape(QTabWidget::Rounded),
+      tabPosition(QTabWidget::North),
+#endif
+      ignoreGeometryChange(false),
+      ignoreWindowStateChange(false),
+      isActivated(false),
+      isSubWindowsTiled(false),
+      showActiveWindowMaximized(false),
+      tileCalledFromResizeEvent(false),
+      updatesDisabledByUs(false),
+      inViewModeChange(false),
+      indexToNextWindow(-1),
+      indexToPreviousWindow(-1),
+      indexToHighlighted(-1),
+      indexToLastActiveTab(-1),
+      resizeTimerId(-1),
+      tabToPreviousTimerId(-1)
+{
+}
+
+/*!
+    \internal
+*/
+void QMdiAreaPrivate::_q_deactivateAllWindows(QMdiSubWindow *aboutToActivate)
+{
+    if (ignoreWindowStateChange)
+        return;
+
+    Q_Q(QMdiArea);
+    if (!aboutToActivate)
+        aboutToBecomeActive = qobject_cast<QMdiSubWindow *>(q->sender());
+    else
+        aboutToBecomeActive = aboutToActivate;
+    Q_ASSERT(aboutToBecomeActive);
+
+    foreach (QMdiSubWindow *child, childWindows) {
+        if (!sanityCheck(child, "QMdiArea::deactivateAllWindows") || aboutToBecomeActive == child)
+            continue;
+        // We don't want to handle signals caused by child->showNormal().
+        ignoreWindowStateChange = true;
+        if(!(options & QMdiArea::DontMaximizeSubWindowOnActivation) && !showActiveWindowMaximized)
+            showActiveWindowMaximized = child->isMaximized() && child->isVisible();
+        if (showActiveWindowMaximized && child->isMaximized()) {
+            if (q->updatesEnabled()) {
+                updatesDisabledByUs = true;
+                q->setUpdatesEnabled(false);
+            }
+            child->showNormal();
+        }
+        if (child->isMinimized() && !child->isShaded() && !windowStaysOnTop(child))
+            child->lower();
+        ignoreWindowStateChange = false;
+        child->d_func()->setActive(false);
+    }
+}
+
+/*!
+    \internal
+*/
+void QMdiAreaPrivate::_q_processWindowStateChanged(Qt::WindowStates oldState,
+                                                   Qt::WindowStates newState)
+{
+    if (ignoreWindowStateChange)
+        return;
+
+    Q_Q(QMdiArea);
+    QMdiSubWindow *child = qobject_cast<QMdiSubWindow *>(q->sender());
+    if (!child)
+        return;
+
+    // windowActivated
+    if (!(oldState & Qt::WindowActive) && (newState & Qt::WindowActive))
+        emitWindowActivated(child);
+    // windowDeactivated
+    else if ((oldState & Qt::WindowActive) && !(newState & Qt::WindowActive))
+        resetActiveWindow(child);
+
+    // windowMinimized
+    if (!(oldState & Qt::WindowMinimized) && (newState & Qt::WindowMinimized)) {
+        isSubWindowsTiled = false;
+        arrangeMinimizedSubWindows();
+    // windowMaximized
+    } else if (!(oldState & Qt::WindowMaximized) && (newState & Qt::WindowMaximized)) {
+        internalRaise(child);
+    // windowRestored
+    } else if (!(newState & (Qt::WindowMaximized | Qt::WindowMinimized))) {
+        internalRaise(child);
+        if (oldState & Qt::WindowMinimized)
+            arrangeMinimizedSubWindows();
+    }
+}
+
+void QMdiAreaPrivate::_q_currentTabChanged(int index)
+{
+#ifdef QT_NO_TABBAR
+    Q_UNUSED(index);
+#else
+    if (!tabBar || index < 0)
+        return;
+
+    // If the previous active sub-window was hidden, disable the tab.
+    if (indexToLastActiveTab >= 0 && indexToLastActiveTab < tabBar->count()
+        && indexToLastActiveTab < childWindows.count()) {
+        QMdiSubWindow *lastActive = childWindows.at(indexToLastActiveTab);
+        if (lastActive && lastActive->isHidden())
+            tabBar->setTabEnabled(indexToLastActiveTab, false);
+    }
+
+    indexToLastActiveTab = index;
+    Q_ASSERT(childWindows.size() > index);
+    QMdiSubWindow *subWindow = childWindows.at(index);
+    Q_ASSERT(subWindow);
+    activateWindow(subWindow);
+#endif // QT_NO_TABBAR
+}
+
+/*!
+    \internal
+*/
+void QMdiAreaPrivate::appendChild(QMdiSubWindow *child)
+{
+    Q_Q(QMdiArea);
+    Q_ASSERT(child && childWindows.indexOf(child) == -1);
+
+    if (child->parent() != viewport)
+        child->setParent(viewport, child->windowFlags());
+    childWindows.append(QPointer<QMdiSubWindow>(child));
+
+    if (!child->testAttribute(Qt::WA_Resized) && q->isVisible()) {
+        QSize newSize(child->sizeHint().boundedTo(viewport->size()));
+        child->resize(newSize.expandedTo(qSmartMinSize(child)));
+    }
+
+    if (!placer)
+        placer = new MinOverlapPlacer;
+    place(placer, child);
+
+    if (hbarpolicy != Qt::ScrollBarAlwaysOff)
+        child->setOption(QMdiSubWindow::AllowOutsideAreaHorizontally, true);
+    else
+        child->setOption(QMdiSubWindow::AllowOutsideAreaHorizontally, false);
+
+    if (vbarpolicy != Qt::ScrollBarAlwaysOff)
+        child->setOption(QMdiSubWindow::AllowOutsideAreaVertically, true);
+    else
+        child->setOption(QMdiSubWindow::AllowOutsideAreaVertically, false);
+
+    internalRaise(child);
+    indicesToActivatedChildren.prepend(childWindows.size() - 1);
+    Q_ASSERT(indicesToActivatedChildren.size() == childWindows.size());
+
+#ifndef QT_NO_TABBAR
+    if (tabBar) {
+        tabBar->addTab(child->windowIcon(), tabTextFor(child));
+        updateTabBarGeometry();
+        if (childWindows.count() == 1 && !(options & QMdiArea::DontMaximizeSubWindowOnActivation))
+            showActiveWindowMaximized = true;
+    }
+#endif
+
+    if (!(child->windowFlags() & Qt::SubWindow))
+        child->setWindowFlags(Qt::SubWindow);
+    child->installEventFilter(q);
+
+    QObject::connect(child, SIGNAL(aboutToActivate()), q, SLOT(_q_deactivateAllWindows()));
+    QObject::connect(child, SIGNAL(windowStateChanged(Qt::WindowStates, Qt::WindowStates)),
+                     q, SLOT(_q_processWindowStateChanged(Qt::WindowStates, Qt::WindowStates)));
+}
+
+/*!
+    \internal
+*/
+void QMdiAreaPrivate::place(Placer *placer, QMdiSubWindow *child)
+{
+    if (!placer || !child)
+        return;
+
+    Q_Q(QMdiArea);
+    if (!q->isVisible()) {
+        // The window is only laid out when it's added to QMdiArea,
+        // so there's no need to check that we don't have it in the
+        // list already. appendChild() ensures that.
+        pendingPlacements.append(child);
+        return;
+    }
+
+    QList<QRect> rects;
+    QRect parentRect = q->rect();
+    foreach (QMdiSubWindow *window, childWindows) {
+        if (!sanityCheck(window, "QMdiArea::place") || window == child || !window->isVisibleTo(q)
+                || !window->testAttribute(Qt::WA_Moved)) {
+            continue;
+        }
+        QRect occupiedGeometry;
+        if (window->isMaximized()) {
+            occupiedGeometry = QRect(window->d_func()->oldGeometry.topLeft(),
+                                     window->d_func()->restoreSize);
+        } else {
+            occupiedGeometry = window->geometry();
+        }
+        rects.append(QStyle::visualRect(child->layoutDirection(), parentRect, occupiedGeometry));
+    }
+    QPoint newPos = placer->place(child->size(), rects, parentRect);
+    QRect newGeometry = QRect(newPos.x(), newPos.y(), child->width(), child->height());
+    child->setGeometry(QStyle::visualRect(child->layoutDirection(), parentRect, newGeometry));
+}
+
+/*!
+    \internal
+*/
+void QMdiAreaPrivate::rearrange(Rearranger *rearranger)
+{
+    if (!rearranger)
+        return;
+
+    Q_Q(QMdiArea);
+    if (!q->isVisible()) {
+        // Compress if we already have the rearranger in the list.
+        int index = pendingRearrangements.indexOf(rearranger);
+        if (index != -1)
+            pendingRearrangements.move(index, pendingRearrangements.size() - 1);
+        else
+            pendingRearrangements.append(rearranger);
+        return;
+    }
+
+    QList<QWidget *> widgets;
+    const bool reverseList = rearranger->type() == Rearranger::RegularTiler;
+    const QList<QMdiSubWindow *> subWindows = subWindowList(activationOrder, reverseList);
+    QSize minSubWindowSize;
+    foreach (QMdiSubWindow *child, subWindows) {
+        if (!sanityCheck(child, "QMdiArea::rearrange") || !child->isVisible())
+            continue;
+        if (rearranger->type() == Rearranger::IconTiler) {
+            if (child->isMinimized() && !child->isShaded() && !(child->windowFlags() & Qt::FramelessWindowHint))
+                widgets.append(child);
+        } else {
+            if (child->isMinimized() && !child->isShaded())
+                continue;
+            if (child->isMaximized() || child->isShaded())
+                child->showNormal();
+            minSubWindowSize = minSubWindowSize.expandedTo(child->minimumSize())
+                               .expandedTo(child->d_func()->internalMinimumSize);
+            widgets.append(child);
+        }
+    }
+
+    if (active && rearranger->type() == Rearranger::RegularTiler) {
+        // Move active window in front if necessary. That's the case if we
+        // have any windows with staysOnTopHint set.
+        int indexToActive = widgets.indexOf((QWidget *)active);
+        if (indexToActive > 0)
+            widgets.move(indexToActive, 0);
+    }
+
+    QRect domain = viewport->rect();
+    if (rearranger->type() == Rearranger::RegularTiler && !widgets.isEmpty())
+        domain = resizeToMinimumTileSize(minSubWindowSize, widgets.count());
+
+    rearranger->rearrange(widgets, domain);
+
+    if (rearranger->type() == Rearranger::RegularTiler && !widgets.isEmpty()) {
+        isSubWindowsTiled = true;
+        updateScrollBars();
+    } else if (rearranger->type() == Rearranger::SimpleCascader) {
+        isSubWindowsTiled = false;
+    }
+}
+
+/*!
+    \internal
+
+    Arranges all minimized windows at the bottom of the workspace.
+*/
+void QMdiAreaPrivate::arrangeMinimizedSubWindows()
+{
+    if (!iconTiler)
+        iconTiler = new IconTiler;
+    rearrange(iconTiler);
+}
+
+/*!
+    \internal
+*/
+void QMdiAreaPrivate::activateWindow(QMdiSubWindow *child)
+{
+    if (childWindows.isEmpty()) {
+        Q_ASSERT(!child);
+        Q_ASSERT(!active);
+        return;
+    }
+
+    if (!child) {
+        if (active) {
+            Q_ASSERT(active->d_func()->isActive);
+            active->d_func()->setActive(false);
+            resetActiveWindow();
+        }
+        return;
+    }
+
+    if (child->isHidden() || child == active)
+        return;
+    child->d_func()->setActive(true);
+}
+
+/*!
+    \internal
+*/
+void QMdiAreaPrivate::activateCurrentWindow()
+{
+    QMdiSubWindow *current = q_func()->currentSubWindow();
+    if (current && !isExplicitlyDeactivated(current)) {
+        current->d_func()->activationEnabled = true;
+        current->d_func()->setActive(true, /*changeFocus=*/false);
+    }
+}
+
+void QMdiAreaPrivate::activateHighlightedWindow()
+{
+    if (indexToHighlighted < 0)
+        return;
+
+    Q_ASSERT(indexToHighlighted < childWindows.size());
+    if (tabToPreviousTimerId != -1)
+        activateWindow(nextVisibleSubWindow(-1, QMdiArea::ActivationHistoryOrder));
+    else
+        activateWindow(childWindows.at(indexToHighlighted));
+#ifndef QT_NO_RUBBERBAND
+    hideRubberBand();
+#endif
+}
+
+/*!
+    \internal
+*/
+void QMdiAreaPrivate::emitWindowActivated(QMdiSubWindow *activeWindow)
+{
+    Q_Q(QMdiArea);
+    Q_ASSERT(activeWindow);
+    if (activeWindow == active)
+        return;
+    Q_ASSERT(activeWindow->d_func()->isActive);
+
+    if (!aboutToBecomeActive)
+        _q_deactivateAllWindows(activeWindow);
+    Q_ASSERT(aboutToBecomeActive);
+
+    // This is true only if 'DontMaximizeSubWindowOnActivation' is disabled
+    // and the previous active window was maximized.
+    if (showActiveWindowMaximized) {
+        if (!activeWindow->isMaximized())
+            activeWindow->showMaximized();
+        showActiveWindowMaximized = false;
+    }
+
+    // Put in front to update activation order.
+    const int indexToActiveWindow = childWindows.indexOf(activeWindow);
+    Q_ASSERT(indexToActiveWindow != -1);
+    const int index = indicesToActivatedChildren.indexOf(indexToActiveWindow);
+    Q_ASSERT(index != -1);
+    indicesToActivatedChildren.move(index, 0);
+    internalRaise(activeWindow);
+
+    if (updatesDisabledByUs) {
+        q->setUpdatesEnabled(true);
+        updatesDisabledByUs = false;
+    }
+
+    Q_ASSERT(aboutToBecomeActive == activeWindow);
+    active = activeWindow;
+    aboutToBecomeActive = 0;
+    Q_ASSERT(active->d_func()->isActive);
+
+#ifndef QT_NO_TABBAR
+    if (tabBar && tabBar->currentIndex() != indexToActiveWindow)
+        tabBar->setCurrentIndex(indexToActiveWindow);
+#endif
+
+    if (active->isMaximized() && scrollBarsEnabled())
+        updateScrollBars();
+
+    emit q->subWindowActivated(active);
+}
+
+/*!
+    \internal
+*/
+void QMdiAreaPrivate::resetActiveWindow(QMdiSubWindow *deactivatedWindow)
+{
+    Q_Q(QMdiArea);
+    if (deactivatedWindow) {
+        if (deactivatedWindow != active)
+            return;
+        active = 0;
+        if ((aboutToBecomeActive || isActivated || lastWindowAboutToBeDestroyed())
+            && !isExplicitlyDeactivated(deactivatedWindow) && !q->window()->isMinimized()) {
+            return;
+        }
+        emit q->subWindowActivated(0);
+        return;
+    }
+
+    if (aboutToBecomeActive)
+        return;
+
+    active = 0;
+    emit q->subWindowActivated(0);
+}
+
+/*!
+    \internal
+*/
+void QMdiAreaPrivate::updateActiveWindow(int removedIndex, bool activeRemoved)
+{
+    Q_ASSERT(indicesToActivatedChildren.size() == childWindows.size());
+
+#ifndef QT_NO_TABBAR
+    if (tabBar && removedIndex >= 0) {
+        tabBar->blockSignals(true);
+        tabBar->removeTab(removedIndex);
+        updateTabBarGeometry();
+        tabBar->blockSignals(false);
+    }
+#endif
+
+    if (childWindows.isEmpty()) {
+        showActiveWindowMaximized = false;
+        resetActiveWindow();
+        return;
+    }
+
+    if (indexToHighlighted >= 0) {
+#ifndef QT_NO_RUBBERBAND
+        // Hide rubber band if highlighted window is removed.
+        if (indexToHighlighted == removedIndex)
+            hideRubberBand();
+        else
+#endif
+        // or update index if necessary.
+        if (indexToHighlighted > removedIndex)
+            --indexToHighlighted;
+    }
+
+    // Update indices list
+    for (int i = 0; i < indicesToActivatedChildren.size(); ++i) {
+        int *index = &indicesToActivatedChildren[i];
+        if (*index > removedIndex)
+            --*index;
+    }
+
+    if (!activeRemoved)
+        return;
+
+    // Activate next window.
+    QMdiSubWindow *next = nextVisibleSubWindow(0, activationOrder, removedIndex);
+    if (next)
+        activateWindow(next);
+}
+
+/*!
+    \internal
+*/
+void QMdiAreaPrivate::updateScrollBars()
+{
+    if (ignoreGeometryChange || !scrollBarsEnabled())
+        return;
+
+    Q_Q(QMdiArea);
+    QSize maxSize = q->maximumViewportSize();
+    QSize hbarExtent = hbar->sizeHint();
+    QSize vbarExtent = vbar->sizeHint();
+
+    if (q->style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents, 0, q)) {
+        const int doubleFrameWidth = frameWidth * 2;
+        if (hbarpolicy == Qt::ScrollBarAlwaysOn)
+            maxSize.rheight() -= doubleFrameWidth;
+        if (vbarpolicy == Qt::ScrollBarAlwaysOn)
+            maxSize.rwidth() -= doubleFrameWidth;
+        hbarExtent.rheight() += doubleFrameWidth;
+        vbarExtent.rwidth() += doubleFrameWidth;
+    }
+
+    const QRect childrenRect = active && active->isMaximized()
+                               ? active->geometry() : viewport->childrenRect();
+    bool useHorizontalScrollBar = useScrollBar(childrenRect, maxSize, Qt::Horizontal);
+    bool useVerticalScrollBar = useScrollBar(childrenRect, maxSize, Qt::Vertical);
+
+    if (useHorizontalScrollBar && !useVerticalScrollBar) {
+        const QSize max = maxSize - QSize(0, hbarExtent.height());
+        useVerticalScrollBar = useScrollBar(childrenRect, max, Qt::Vertical);
+    }
+
+    if (useVerticalScrollBar && !useHorizontalScrollBar) {
+        const QSize max = maxSize - QSize(vbarExtent.width(), 0);
+        useHorizontalScrollBar = useScrollBar(childrenRect, max, Qt::Horizontal);
+    }
+
+    if (useHorizontalScrollBar && hbarpolicy != Qt::ScrollBarAlwaysOn)
+        maxSize.rheight() -= hbarExtent.height();
+    if (useVerticalScrollBar && vbarpolicy != Qt::ScrollBarAlwaysOn)
+        maxSize.rwidth() -= vbarExtent.width();
+
+    QRect viewportRect(QPoint(0, 0), maxSize);
+    const int startX = q->isLeftToRight() ? childrenRect.left() : viewportRect.right()
+                                                                  - childrenRect.right();
+
+    // Horizontal scroll bar.
+    if (isSubWindowsTiled && hbar->value() != 0)
+        hbar->setValue(0);
+    const int xOffset = startX + hbar->value();
+    hbar->setRange(qMin(0, xOffset),
+                   qMax(0, xOffset + childrenRect.width() - viewportRect.width()));
+    hbar->setPageStep(childrenRect.width());
+    hbar->setSingleStep(childrenRect.width() / 20);
+
+    // Vertical scroll bar.
+    if (isSubWindowsTiled && vbar->value() != 0)
+        vbar->setValue(0);
+    const int yOffset = childrenRect.top() + vbar->value();
+    vbar->setRange(qMin(0, yOffset),
+                   qMax(0, yOffset + childrenRect.height() - viewportRect.height()));
+    vbar->setPageStep(childrenRect.height());
+    vbar->setSingleStep(childrenRect.height() / 20);
+}
+
+/*!
+    \internal
+*/
+void QMdiAreaPrivate::internalRaise(QMdiSubWindow *mdiChild) const
+{
+    if (!sanityCheck(mdiChild, "QMdiArea::internalRaise") || childWindows.size() < 2)
+        return;
+
+    QMdiSubWindow *stackUnderChild = 0;
+    if (!windowStaysOnTop(mdiChild)) {
+        foreach (QObject *object, viewport->children()) {
+            QMdiSubWindow *child = qobject_cast<QMdiSubWindow *>(object);
+            if (!child || !childWindows.contains(child))
+                continue;
+            if (!child->isHidden() && windowStaysOnTop(child)) {
+                if (stackUnderChild)
+                    child->stackUnder(stackUnderChild);
+                else
+                    child->raise();
+                stackUnderChild = child;
+            }
+        }
+    }
+
+    if (stackUnderChild)
+        mdiChild->stackUnder(stackUnderChild);
+    else
+        mdiChild->raise();
+}
+
+QRect QMdiAreaPrivate::resizeToMinimumTileSize(const QSize &minSubWindowSize, int subWindowCount)
+{
+    Q_Q(QMdiArea);
+    if (!minSubWindowSize.isValid() || subWindowCount <= 0)
+        return viewport->rect();
+
+    // Calculate minimum size.
+    const int columns = qMax(qCeil(qSqrt(qreal(subWindowCount))), 1);
+    const int rows = qMax((subWindowCount % columns) ? (subWindowCount / columns + 1)
+                                                     : (subWindowCount / columns), 1);
+    const int minWidth = minSubWindowSize.width() * columns;
+    const int minHeight = minSubWindowSize.height() * rows;
+
+    // Increase area size if necessary. Scroll bars are provided if we're not able
+    // to resize to the minimum size.
+    if (!tileCalledFromResizeEvent) {
+        QWidget *topLevel = q;
+        // Find the topLevel for this area, either a real top-level or a sub-window.
+        while (topLevel && !topLevel->isWindow() && topLevel->windowType() != Qt::SubWindow)
+            topLevel = topLevel->parentWidget();
+        // We don't want sub-subwindows to be placed at the edge, thus add 2 pixels.
+        int minAreaWidth = minWidth + left + right + 2;
+        int minAreaHeight = minHeight + top + bottom + 2;
+        if (hbar->isVisible())
+            minAreaHeight += hbar->height();
+        if (vbar->isVisible())
+            minAreaWidth += vbar->width();
+        if (q->style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents, 0, q)) {
+            const int frame = q->style()->pixelMetric(QStyle::PM_DefaultFrameWidth, 0, q);
+            minAreaWidth += 2 * frame;
+            minAreaHeight += 2 * frame;
+        }
+        const QSize diff = QSize(minAreaWidth, minAreaHeight).expandedTo(q->size()) - q->size();
+        topLevel->resize(topLevel->size() + diff);
+    }
+
+    QRect domain = viewport->rect();
+
+    // Adjust domain width and provide horizontal scroll bar.
+    if (domain.width() < minWidth) {
+        domain.setWidth(minWidth);
+        if (hbarpolicy == Qt::ScrollBarAlwaysOff)
+            q->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
+        else
+            hbar->setValue(0);
+    }
+    // Adjust domain height and provide vertical scroll bar.
+    if (domain.height() < minHeight) {
+        domain.setHeight(minHeight);
+        if (vbarpolicy  == Qt::ScrollBarAlwaysOff)
+            q->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
+        else
+            vbar->setValue(0);
+    }
+    return domain;
+}
+
+/*!
+    \internal
+*/
+bool QMdiAreaPrivate::scrollBarsEnabled() const
+{
+    return hbarpolicy != Qt::ScrollBarAlwaysOff || vbarpolicy != Qt::ScrollBarAlwaysOff;
+}
+
+/*!
+    \internal
+*/
+bool QMdiAreaPrivate::lastWindowAboutToBeDestroyed() const
+{
+    if (childWindows.count() != 1)
+        return false;
+
+    QMdiSubWindow *last = childWindows.at(0);
+    if (!last)
+        return true;
+
+    if (!last->testAttribute(Qt::WA_DeleteOnClose))
+        return false;
+
+    return last->d_func()->data.is_closing;
+}
+
+/*!
+    \internal
+*/
+void QMdiAreaPrivate::setChildActivationEnabled(bool enable, bool onlyNextActivationEvent) const
+{
+    foreach (QMdiSubWindow *subWindow, childWindows) {
+        if (!subWindow || !subWindow->isVisible())
+            continue;
+        if (onlyNextActivationEvent)
+            subWindow->d_func()->ignoreNextActivationEvent = !enable;
+        else
+            subWindow->d_func()->activationEnabled = enable;
+    }
+}
+
+/*!
+    \internal
+    \reimp
+*/
+void QMdiAreaPrivate::scrollBarPolicyChanged(Qt::Orientation orientation, Qt::ScrollBarPolicy policy)
+{
+    if (childWindows.isEmpty())
+        return;
+
+    const QMdiSubWindow::SubWindowOption option = orientation == Qt::Horizontal ?
+        QMdiSubWindow::AllowOutsideAreaHorizontally : QMdiSubWindow::AllowOutsideAreaVertically;
+    const bool enable = policy != Qt::ScrollBarAlwaysOff;
+    foreach (QMdiSubWindow *child, childWindows) {
+        if (!sanityCheck(child, "QMdiArea::scrollBarPolicyChanged"))
+            continue;
+        child->setOption(option, enable);
+    }
+    updateScrollBars();
+}
+
+QList<QMdiSubWindow*>
+QMdiAreaPrivate::subWindowList(QMdiArea::WindowOrder order, bool reversed) const
+{
+    QList<QMdiSubWindow *> list;
+    if (childWindows.isEmpty())
+        return list;
+
+    if (order == QMdiArea::CreationOrder) {
+        foreach (QMdiSubWindow *child, childWindows) {
+            if (!child)
+                continue;
+            if (!reversed)
+                list.append(child);
+            else
+                list.prepend(child);
+        }
+    } else if (order == QMdiArea::StackingOrder) {
+        foreach (QObject *object, viewport->children()) {
+            QMdiSubWindow *child = qobject_cast<QMdiSubWindow *>(object);
+            if (!child || !childWindows.contains(child))
+                continue;
+            if (!reversed)
+                list.append(child);
+            else
+                list.prepend(child);
+        }
+    } else { // ActivationHistoryOrder
+        Q_ASSERT(indicesToActivatedChildren.size() == childWindows.size());
+        for (int i = indicesToActivatedChildren.count() - 1; i >= 0; --i) {
+            QMdiSubWindow *child = childWindows.at(indicesToActivatedChildren.at(i));
+            if (!child)
+                continue;
+            if (!reversed)
+                list.append(child);
+            else
+                list.prepend(child);
+        }
+    }
+    return list;
+}
+
+/*!
+    \internal
+*/
+void QMdiAreaPrivate::disconnectSubWindow(QObject *subWindow)
+{
+    if (!subWindow)
+        return;
+
+    Q_Q(QMdiArea);
+    QObject::disconnect(subWindow, 0, q, 0);
+    subWindow->removeEventFilter(q);
+}
+
+/*!
+    \internal
+*/
+QMdiSubWindow *QMdiAreaPrivate::nextVisibleSubWindow(int increaseFactor, QMdiArea::WindowOrder order,
+                                                     int removedIndex, int fromIndex) const
+{
+    if (childWindows.isEmpty())
+        return 0;
+
+    Q_Q(const QMdiArea);
+    const QList<QMdiSubWindow *> subWindows = q->subWindowList(order);
+    QMdiSubWindow *current = 0;
+
+    if (removedIndex < 0) {
+        if (fromIndex >= 0 && fromIndex < subWindows.size())
+            current = childWindows.at(fromIndex);
+        else
+            current = q->currentSubWindow();
+    }
+
+    // There's no current sub-window (removed or deactivated),
+    // so we have to pick the last active or the next in creation order.
+    if (!current) {
+        if (removedIndex >= 0 && order == QMdiArea::CreationOrder) {
+            int candidateIndex = -1;
+            setIndex(&candidateIndex, removedIndex, 0, subWindows.size() - 1, true);
+            current = childWindows.at(candidateIndex);
+        } else {
+            current = subWindows.back();
+        }
+    }
+    Q_ASSERT(current);
+
+    // Find the index for the current sub-window in the given activation order
+    const int indexToCurrent = subWindows.indexOf(current);
+    const bool increasing = increaseFactor > 0 ? true : false;
+
+    // and use that index + increseFactor as a candidate.
+    int index = -1;
+    setIndex(&index, indexToCurrent + increaseFactor, 0, subWindows.size() - 1, increasing);
+    Q_ASSERT(index != -1);
+
+    // Try to find another window if the candidate is hidden.
+    while (subWindows.at(index)->isHidden()) {
+        setIndex(&index, index + increaseFactor, 0, subWindows.size() - 1, increasing);
+        if (index == indexToCurrent)
+            break;
+    }
+
+    if (!subWindows.at(index)->isHidden())
+        return subWindows.at(index);
+    return 0;
+}
+
+/*!
+    \internal
+*/
+void QMdiAreaPrivate::highlightNextSubWindow(int increaseFactor)
+{
+    if (childWindows.size() == 1)
+        return;
+
+    Q_Q(QMdiArea);
+    // There's no highlighted sub-window atm, use current.
+    if (indexToHighlighted < 0) {
+        QMdiSubWindow *current = q->currentSubWindow();
+        if (!current)
+            return;
+        indexToHighlighted = childWindows.indexOf(current);
+    }
+
+    Q_ASSERT(indexToHighlighted >= 0);
+    Q_ASSERT(indexToHighlighted < childWindows.size());
+
+    QMdiSubWindow *highlight = nextVisibleSubWindow(increaseFactor, activationOrder, -1, indexToHighlighted);
+    if (!highlight)
+        return;
+
+#ifndef QT_NO_RUBBERBAND
+    if (!rubberBand) {
+        rubberBand = new QRubberBand(QRubberBand::Rectangle, viewport);
+        // For accessibility to identify this special widget.
+        rubberBand->setObjectName(QLatin1String("qt_rubberband"));
+        rubberBand->setWindowFlags(rubberBand->windowFlags() | Qt::WindowStaysOnTopHint);
+    }
+#endif
+
+    // Only highlight if we're not switching back to the previously active window (Ctrl-Tab once).
+#ifndef QT_NO_RUBBERBAND
+    if (tabToPreviousTimerId == -1)
+        showRubberBandFor(highlight);
+#endif
+
+    indexToHighlighted = childWindows.indexOf(highlight);
+    Q_ASSERT(indexToHighlighted >= 0);
+}
+
+/*!
+    \internal
+    \since 4.4
+*/
+void QMdiAreaPrivate::setViewMode(QMdiArea::ViewMode mode)
+{
+    Q_Q(QMdiArea);
+    if (viewMode == mode || inViewModeChange)
+        return;
+
+    // Just a guard since we cannot set viewMode = mode here.
+    inViewModeChange = true;
+
+#ifndef QT_NO_TABBAR
+    if (mode == QMdiArea::TabbedView) {
+        Q_ASSERT(!tabBar);
+        tabBar = new QMdiAreaTabBar(q);
+        tabBar->setDocumentMode(documentMode);
+#ifndef QT_NO_TABWIDGET
+        tabBar->setShape(tabBarShapeFrom(tabShape, tabPosition));
+#endif
+
+        isSubWindowsTiled = false;
+
+        foreach (QMdiSubWindow *subWindow, childWindows)
+            tabBar->addTab(subWindow->windowIcon(), tabTextFor(subWindow));
+
+        QMdiSubWindow *current = q->currentSubWindow();
+        if (current) {
+            tabBar->setCurrentIndex(childWindows.indexOf(current));
+            // Restore sub-window (i.e. cleanup buttons in menu bar and window title).
+            if (current->isMaximized())
+                current->showNormal();
+
+            viewMode = mode;
+
+            // Now, maximize it.
+            if (!q->testOption(QMdiArea::DontMaximizeSubWindowOnActivation)) {
+                current->showMaximized();
+            }
+        } else {
+            viewMode = mode;
+        }
+
+        if (q->isVisible())
+            tabBar->show();
+        updateTabBarGeometry();
+
+        QObject::connect(tabBar, SIGNAL(currentChanged(int)), q, SLOT(_q_currentTabChanged(int)));
+    } else
+#endif // QT_NO_TABBAR
+    { // SubWindowView
+#ifndef QT_NO_TABBAR
+        delete tabBar;
+        tabBar = 0;
+#endif // QT_NO_TABBAR
+
+        viewMode = mode;
+        q->setViewportMargins(0, 0, 0, 0);
+        indexToLastActiveTab = -1;
+
+        QMdiSubWindow *current = q->currentSubWindow();
+        if (current && current->isMaximized())
+            current->showNormal();
+    }
+
+    Q_ASSERT(viewMode == mode);
+    inViewModeChange = false;
+}
+
+#ifndef QT_NO_TABBAR
+/*!
+    \internal
+*/
+void QMdiAreaPrivate::updateTabBarGeometry()
+{
+    if (!tabBar)
+        return;
+
+    Q_Q(QMdiArea);
+#ifndef QT_NO_TABWIDGET
+    Q_ASSERT(tabBarShapeFrom(tabShape, tabPosition) == tabBar->shape());
+#endif
+    const QSize tabBarSizeHint = tabBar->sizeHint();
+
+    int areaHeight = q->height();
+    if (hbar && hbar->isVisible())
+        areaHeight -= hbar->height();
+
+    int areaWidth = q->width();
+    if (vbar && vbar->isVisible())
+        areaWidth -= vbar->width();
+
+    QRect tabBarRect;
+#ifndef QT_NO_TABWIDGET
+    switch (tabPosition) {
+    case QTabWidget::North:
+        q->setViewportMargins(0, tabBarSizeHint.height(), 0, 0);
+        tabBarRect = QRect(0, 0, areaWidth, tabBarSizeHint.height());
+        break;
+    case QTabWidget::South:
+        q->setViewportMargins(0, 0, 0, tabBarSizeHint.height());
+        tabBarRect = QRect(0, areaHeight - tabBarSizeHint.height(), areaWidth, tabBarSizeHint.height());
+        break;
+    case QTabWidget::East:
+        if (q->layoutDirection() == Qt::LeftToRight)
+            q->setViewportMargins(0, 0, tabBarSizeHint.width(), 0);
+        else
+            q->setViewportMargins(tabBarSizeHint.width(), 0, 0, 0);
+        tabBarRect = QRect(areaWidth - tabBarSizeHint.width(), 0, tabBarSizeHint.width(), areaHeight);
+        break;
+    case QTabWidget::West:
+        if (q->layoutDirection() == Qt::LeftToRight)
+            q->setViewportMargins(tabBarSizeHint.width(), 0, 0, 0);
+        else
+            q->setViewportMargins(0, 0, tabBarSizeHint.width(), 0);
+        tabBarRect = QRect(0, 0, tabBarSizeHint.width(), areaHeight);
+        break;
+    default:
+        break;
+    }
+#endif // QT_NO_TABWIDGET
+
+    tabBar->setGeometry(QStyle::visualRect(q->layoutDirection(), q->contentsRect(), tabBarRect));
+}
+
+/*!
+    \internal
+*/
+void QMdiAreaPrivate::refreshTabBar()
+{
+    if (!tabBar)
+        return;
+
+    tabBar->setDocumentMode(documentMode);
+#ifndef QT_NO_TABWIDGET
+    tabBar->setShape(tabBarShapeFrom(tabShape, tabPosition));
+#endif
+    updateTabBarGeometry();
+}
+#endif // QT_NO_TABBAR
+
+/*!
+    Constructs an empty mdi area. \a parent is passed to QWidget's
+    constructor.
+*/
+QMdiArea::QMdiArea(QWidget *parent)
+    : QAbstractScrollArea(*new QMdiAreaPrivate, parent)
+{
+    setBackground(palette().brush(QPalette::Dark));
+    setFrameStyle(QFrame::NoFrame);
+    setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+    setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+    setViewport(0);
+    setFocusPolicy(Qt::NoFocus);
+    QApplication::instance()->installEventFilter(this);
+}
+
+/*!
+    Destroys the MDI area.
+*/
+QMdiArea::~QMdiArea()
+{
+    Q_D(QMdiArea);
+    delete d->cascader;
+    d->cascader = 0;
+
+    delete d->regularTiler;
+    d->regularTiler = 0;
+
+    delete d->iconTiler;
+    d->iconTiler = 0;
+
+    delete d->placer;
+    d->placer = 0;
+}
+
+/*!
+    \reimp
+*/
+QSize QMdiArea::sizeHint() const
+{
+    // Calculate a proper scale factor for QDesktopWidget::size().
+    // This also takes into account that we can have nested workspaces.
+    int nestedCount = 0;
+    QWidget *widget = this->parentWidget();
+    while (widget) {
+        if (qobject_cast<QMdiArea *>(widget))
+            ++nestedCount;
+        widget = widget->parentWidget();
+    }
+    const int scaleFactor = 3 * (nestedCount + 1);
+
+    QSize desktopSize = QApplication::desktop()->size();
+    QSize size(desktopSize.width() * 2 / scaleFactor, desktopSize.height() * 2 / scaleFactor);
+    foreach (QMdiSubWindow *child, d_func()->childWindows) {
+        if (!sanityCheck(child, "QMdiArea::sizeHint"))
+            continue;
+        size = size.expandedTo(child->sizeHint());
+    }
+    return size.expandedTo(QApplication::globalStrut());
+}
+
+/*!
+    \reimp
+*/
+QSize QMdiArea::minimumSizeHint() const
+{
+    Q_D(const QMdiArea);
+    QSize size(style()->pixelMetric(QStyle::PM_MdiSubWindowMinimizedWidth, 0, this),
+               style()->pixelMetric(QStyle::PM_TitleBarHeight, 0, this));
+    size = size.expandedTo(QAbstractScrollArea::minimumSizeHint());
+    if (!d->scrollBarsEnabled()) {
+        foreach (QMdiSubWindow *child, d->childWindows) {
+            if (!sanityCheck(child, "QMdiArea::sizeHint"))
+                continue;
+            size = size.expandedTo(child->minimumSizeHint());
+        }
+    }
+    return size.expandedTo(QApplication::globalStrut());
+}
+
+/*!
+    Returns a pointer to the current subwindow, or 0 if there is
+    no current subwindow.
+
+    This function will return the same as activeSubWindow() if
+    the QApplication containing QMdiArea is active.
+
+    \sa activeSubWindow(), QApplication::activeWindow()
+*/
+QMdiSubWindow *QMdiArea::currentSubWindow() const
+{
+    Q_D(const QMdiArea);
+    if (d->childWindows.isEmpty())
+        return 0;
+
+    if (d->active)
+        return d->active;
+
+    if (d->isActivated && !window()->isMinimized())
+        return 0;
+
+    Q_ASSERT(d->indicesToActivatedChildren.count() > 0);
+    int index = d->indicesToActivatedChildren.at(0);
+    Q_ASSERT(index >= 0 && index < d->childWindows.size());
+    QMdiSubWindow *current = d->childWindows.at(index);
+    Q_ASSERT(current);
+    return current;
+}
+
+/*!
+    Returns a pointer to the current active subwindow. If no
+    window is currently active, 0 is returned.
+
+    Subwindows are treated as top-level windows with respect to
+    window state, i.e., if a widget outside the MDI area is the active
+    window, no subwindow will be active. Note that if a widget in the
+    window in which the MDI area lives gains focus, the window will be
+    activated.
+
+    \sa setActiveSubWindow(), Qt::WindowState
+*/
+QMdiSubWindow *QMdiArea::activeSubWindow() const
+{
+    Q_D(const QMdiArea);
+    return d->active;
+}
+
+/*!
+    Activates the subwindow \a window. If \a window is 0, any
+    current active window is deactivated.
+
+    \sa activeSubWindow()
+*/
+void QMdiArea::setActiveSubWindow(QMdiSubWindow *window)
+{
+    Q_D(QMdiArea);
+    if (!window) {
+        d->activateWindow(0);
+        return;
+    }
+
+    if (d->childWindows.isEmpty()) {
+        qWarning("QMdiArea::setActiveSubWindow: workspace is empty");
+        return;
+    }
+
+    if (d->childWindows.indexOf(window) == -1) {
+        qWarning("QMdiArea::setActiveSubWindow: window is not inside workspace");
+        return;
+    }
+
+    d->activateWindow(window);
+}
+
+/*!
+    Closes the active subwindow.
+
+    \sa closeAllSubWindows()
+*/
+void QMdiArea::closeActiveSubWindow()
+{
+    Q_D(QMdiArea);
+    if (d->active)
+        d->active->close();
+}
+
+/*!
+    Returns a list of all subwindows in the MDI area. If \a order is
+    CreationOrder (the default), the windows are sorted in the order
+    in which they were inserted into the workspace. If \a order is
+    StackingOrder, the windows are listed in their stacking order,
+    with the topmost window as the last item in the list. If \a order
+    is ActivationHistoryOrder, the windows are listed according to
+    their recent activation history.
+
+    \sa WindowOrder
+*/
+QList<QMdiSubWindow *> QMdiArea::subWindowList(WindowOrder order) const
+{
+    Q_D(const QMdiArea);
+    return d->subWindowList(order, false);
+}
+
+/*!
+    Closes all subwindows by sending a QCloseEvent to each window.
+    You may receive subWindowActivated() signals from subwindows
+    before they are closed (if the MDI area activates the subwindow
+    when another is closing).
+
+    Subwindows that ignore the close event will remain open.
+
+    \sa closeActiveSubWindow()
+*/
+void QMdiArea::closeAllSubWindows()
+{
+    Q_D(QMdiArea);
+    if (d->childWindows.isEmpty())
+        return;
+
+    d->isSubWindowsTiled = false;
+    foreach (QMdiSubWindow *child, d->childWindows) {
+        if (!sanityCheck(child, "QMdiArea::closeAllSubWindows"))
+            continue;
+        child->close();
+    }
+
+    d->updateScrollBars();
+}
+
+/*!
+    Gives the keyboard focus to the next window in the list of child
+    windows.  The windows are activated in the order in which they are
+    created (CreationOrder).
+
+    \sa activatePreviousSubWindow()
+*/
+void QMdiArea::activateNextSubWindow()
+{
+    Q_D(QMdiArea);
+    if (d->childWindows.isEmpty())
+        return;
+
+    QMdiSubWindow *next = d->nextVisibleSubWindow(1, d->activationOrder);
+    if (next)
+        d->activateWindow(next);
+}
+
+/*!
+    Gives the keyboard focus to the previous window in the list of
+    child windows. The windows are activated in the order in which
+    they are created (CreationOrder).
+
+    \sa activateNextSubWindow()
+*/
+void QMdiArea::activatePreviousSubWindow()
+{
+    Q_D(QMdiArea);
+    if (d->childWindows.isEmpty())
+        return;
+
+    QMdiSubWindow *previous = d->nextVisibleSubWindow(-1, d->activationOrder);
+    if (previous)
+        d->activateWindow(previous);
+}
+
+/*!
+    Adds \a widget as a new subwindow to the MDI area.  If \a
+    windowFlags are non-zero, they will override the flags set on the
+    widget.
+
+    The \a widget can be either a QMdiSubWindow or another QWidget
+    (in which case the MDI area will create a subwindow and set the \a
+    widget as the internal widget).
+
+    \note Once the subwindow has been added, its parent will be the
+    \e{viewport widget} of the QMdiArea.
+
+    \snippet doc/src/snippets/mdiareasnippets.cpp 1
+
+    When you create your own subwindow, you must set the
+    Qt::WA_DeleteOnClose widget attribute if you want the window to be
+    deleted when closed in the MDI area. If not, the window will be
+    hidden and the MDI area will not activate the next subwindow.
+
+    Returns the QMdiSubWindow that is added to the MDI area.
+
+    \sa removeSubWindow()
+*/
+QMdiSubWindow *QMdiArea::addSubWindow(QWidget *widget, Qt::WindowFlags windowFlags)
+{
+    if (!widget) {
+        qWarning("QMdiArea::addSubWindow: null pointer to widget");
+        return 0;
+    }
+
+    Q_D(QMdiArea);
+    // QWidget::setParent clears focusWidget so store it
+    QWidget *childFocus = widget->focusWidget();
+    QMdiSubWindow *child = qobject_cast<QMdiSubWindow *>(widget);
+
+    // Widget is already a QMdiSubWindow
+    if (child) {
+        if (d->childWindows.indexOf(child) != -1) {
+            qWarning("QMdiArea::addSubWindow: window is already added");
+            return child;
+        }
+        child->setParent(viewport(), windowFlags ? windowFlags : child->windowFlags());
+    // Create a QMdiSubWindow
+    } else {
+        child = new QMdiSubWindow(viewport(), windowFlags);
+        child->setAttribute(Qt::WA_DeleteOnClose);
+        child->setWidget(widget);
+        Q_ASSERT(child->testAttribute(Qt::WA_DeleteOnClose));
+    }
+
+    if (childFocus)
+        childFocus->setFocus();
+    d->appendChild(child);
+    return child;
+}
+
+/*!
+    Removes \a widget from the MDI area. The \a widget must be
+    either a QMdiSubWindow or a widget that is the internal widget of
+    a subwindow. Note that the subwindow is not deleted by QMdiArea
+    and that its parent is set to 0.
+
+    \sa addSubWindow()
+*/
+void QMdiArea::removeSubWindow(QWidget *widget)
+{
+    if (!widget) {
+        qWarning("QMdiArea::removeSubWindow: null pointer to widget");
+        return;
+    }
+
+    Q_D(QMdiArea);
+    if (d->childWindows.isEmpty())
+        return;
+
+    if (QMdiSubWindow *child = qobject_cast<QMdiSubWindow *>(widget)) {
+        int index = d->childWindows.indexOf(child);
+        if (index == -1) {
+            qWarning("QMdiArea::removeSubWindow: window is not inside workspace");
+            return;
+        }
+        d->disconnectSubWindow(child);
+        d->childWindows.removeAll(child);
+        d->indicesToActivatedChildren.removeAll(index);
+        d->updateActiveWindow(index, d->active == child);
+        child->setParent(0);
+        return;
+    }
+
+    bool found = false;
+    foreach (QMdiSubWindow *child, d->childWindows) {
+        if (!sanityCheck(child, "QMdiArea::removeSubWindow"))
+            continue;
+        if (child->widget() == widget) {
+            child->setWidget(0);
+            Q_ASSERT(!child->widget());
+            found = true;
+            break;
+        }
+    }
+
+    if (!found)
+        qWarning("QMdiArea::removeSubWindow: widget is not child of any window inside QMdiArea");
+}
+
+/*!
+    \property QMdiArea::background
+    \brief the background brush for the workspace
+
+    This property sets the background brush for the workspace area
+    itself. By default, it is a gray color, but can be any brush
+    (e.g., colors, gradients or pixmaps).
+*/
+QBrush QMdiArea::background() const
+{
+    return d_func()->background;
+}
+
+void QMdiArea::setBackground(const QBrush &brush)
+{
+    Q_D(QMdiArea);
+    if (d->background != brush) {
+        d->background = brush;
+        d->viewport->setAttribute(Qt::WA_OpaquePaintEvent, brush.isOpaque());
+        update();
+    }
+}
+
+
+/*!
+    \property QMdiArea::activationOrder
+    \brief the ordering criteria for subwindow lists
+    \since 4.4
+
+    This property specifies the ordering criteria for the list of
+    subwindows returned by subWindowList(). By default, it is the window
+    creation order.
+
+    \sa subWindowList()
+*/
+QMdiArea::WindowOrder QMdiArea::activationOrder() const
+{
+    Q_D(const QMdiArea);
+    return d->activationOrder;
+}
+
+void QMdiArea::setActivationOrder(WindowOrder order)
+{
+    Q_D(QMdiArea);
+    if (order != d->activationOrder)
+        d->activationOrder = order;
+}
+
+/*!
+    If \a on is true, \a option is enabled on the MDI area; otherwise
+    it is disabled. See AreaOption for the effect of each option.
+
+    \sa AreaOption, testOption()
+*/
+void QMdiArea::setOption(AreaOption option, bool on)
+{
+    Q_D(QMdiArea);
+    if (on && !(d->options & option))
+        d->options |= option;
+    else if (!on && (d->options & option))
+        d->options &= ~option;
+}
+
+/*!
+    Returns true if \a option is enabled; otherwise returns false.
+
+    \sa AreaOption, setOption()
+*/
+bool QMdiArea::testOption(AreaOption option) const
+{
+    return d_func()->options & option;
+}
+
+/*!
+    \property QMdiArea::viewMode
+    \brief the way sub-windows are displayed in the QMdiArea.
+    \since 4.4
+
+    By default, the SubWindowView is used to display sub-windows.
+
+    \sa ViewMode, setTabShape(), setTabPosition()
+*/
+QMdiArea::ViewMode QMdiArea::viewMode() const
+{
+    Q_D(const QMdiArea);
+    return d->viewMode;
+}
+
+void QMdiArea::setViewMode(ViewMode mode)
+{
+    Q_D(QMdiArea);
+    d->setViewMode(mode);
+}
+
+#ifndef QT_NO_TABBAR
+/*!
+    \property QMdiArea::documentMode
+    \brief whether the tab bar is set to document mode in tabbed view mode.
+    \since 4.5
+
+    Document mode is disabled by default.
+
+    \sa QTabBar::documentMode, setViewMode()
+*/
+bool QMdiArea::documentMode() const
+{
+    Q_D(const QMdiArea);
+    return d->documentMode;
+}
+
+void QMdiArea::setDocumentMode(bool enabled)
+{
+    Q_D(QMdiArea);
+    if (d->documentMode == enabled)
+        return;
+
+    d->documentMode = enabled;
+    d->refreshTabBar();
+}
+#endif // QT_NO_TABBAR
+
+#ifndef QT_NO_TABWIDGET
+/*!
+    \property QMdiArea::tabShape
+    \brief the shape of the tabs in tabbed view mode.
+    \since 4.4
+
+    Possible values for this property are QTabWidget::Rounded
+    (default) or QTabWidget::Triangular.
+
+    \sa QTabWidget::TabShape, setViewMode()
+*/
+QTabWidget::TabShape QMdiArea::tabShape() const
+{
+    Q_D(const QMdiArea);
+    return d->tabShape;
+}
+
+void QMdiArea::setTabShape(QTabWidget::TabShape shape)
+{
+    Q_D(QMdiArea);
+    if (d->tabShape == shape)
+        return;
+
+    d->tabShape = shape;
+    d->refreshTabBar();
+}
+
+/*!
+    \property QMdiArea::tabPosition
+    \brief the position of the tabs in tabbed view mode.
+    \since 4.4
+
+    Possible values for this property are described by the
+    QTabWidget::TabPosition enum.
+
+    \sa QTabWidget::TabPosition, setViewMode()
+*/
+QTabWidget::TabPosition QMdiArea::tabPosition() const
+{
+    Q_D(const QMdiArea);
+    return d->tabPosition;
+}
+
+void QMdiArea::setTabPosition(QTabWidget::TabPosition position)
+{
+    Q_D(QMdiArea);
+    if (d->tabPosition == position)
+        return;
+
+    d->tabPosition = position;
+    d->refreshTabBar();
+}
+#endif // QT_NO_TABWIDGET
+
+/*!
+    \reimp
+*/
+void QMdiArea::childEvent(QChildEvent *childEvent)
+{
+    Q_D(QMdiArea);
+    if (childEvent->type() == QEvent::ChildPolished) {
+        if (QMdiSubWindow *mdiChild = qobject_cast<QMdiSubWindow *>(childEvent->child())) {
+            if (d->childWindows.indexOf(mdiChild) == -1)
+                d->appendChild(mdiChild);
+        }
+    }
+}
+
+/*!
+    \reimp
+*/
+void QMdiArea::resizeEvent(QResizeEvent *resizeEvent)
+{
+    Q_D(QMdiArea);
+    if (d->childWindows.isEmpty()) {
+        resizeEvent->ignore();
+        return;
+    }
+
+#ifndef QT_NO_TABBAR
+    d->updateTabBarGeometry();
+#endif
+
+    // Re-tile the views if we're in tiled mode. Re-tile means we will change
+    // the geometry of the children, which in turn means 'isSubWindowsTiled'
+    // is set to false, so we have to update the state at the end.
+    if (d->isSubWindowsTiled) {
+        d->tileCalledFromResizeEvent = true;
+        tileSubWindows();
+        d->tileCalledFromResizeEvent = false;
+        d->isSubWindowsTiled = true;
+        d->startResizeTimer();
+        // We don't have scroll bars or any maximized views.
+        return;
+    }
+
+    // Resize maximized views.
+    bool hasMaximizedSubWindow = false;
+    foreach (QMdiSubWindow *child, d->childWindows) {
+        if (sanityCheck(child, "QMdiArea::resizeEvent") && child->isMaximized()
+                && child->size() != resizeEvent->size()) {
+            child->resize(resizeEvent->size());
+            if (!hasMaximizedSubWindow)
+                hasMaximizedSubWindow = true;
+        }
+    }
+
+    d->updateScrollBars();
+
+    // Minimized views are stacked under maximized views so there's
+    // no need to re-arrange minimized views on-demand. Start a timer
+    // just to make things faster with subsequent resize events.
+    if (hasMaximizedSubWindow)
+        d->startResizeTimer();
+    else
+        d->arrangeMinimizedSubWindows();
+}
+
+/*!
+    \reimp
+*/
+void QMdiArea::timerEvent(QTimerEvent *timerEvent)
+{
+    Q_D(QMdiArea);
+    if (timerEvent->timerId() == d->resizeTimerId) {
+        killTimer(d->resizeTimerId);
+        d->resizeTimerId = -1;
+        d->arrangeMinimizedSubWindows();
+    } else if (timerEvent->timerId() == d->tabToPreviousTimerId) {
+        killTimer(d->tabToPreviousTimerId);
+        d->tabToPreviousTimerId = -1;
+        if (d->indexToHighlighted < 0)
+            return;
+#ifndef QT_NO_RUBBERBAND
+        // We're not doing a "quick switch" ... show rubber band.
+        Q_ASSERT(d->indexToHighlighted < d->childWindows.size());
+        Q_ASSERT(d->rubberBand);
+        d->showRubberBandFor(d->childWindows.at(d->indexToHighlighted));
+#endif
+    }
+}
+
+/*!
+    \reimp
+*/
+void QMdiArea::showEvent(QShowEvent *showEvent)
+{
+    Q_D(QMdiArea);
+    if (!d->pendingRearrangements.isEmpty()) {
+        bool skipPlacement = false;
+        foreach (Rearranger *rearranger, d->pendingRearrangements) {
+            // If this is the case, we don't have to lay out pending child windows
+            // since the rearranger will find a placement for them.
+            if (rearranger->type() != Rearranger::IconTiler && !skipPlacement)
+                skipPlacement = true;
+            d->rearrange(rearranger);
+        }
+        d->pendingRearrangements.clear();
+
+        if (skipPlacement && !d->pendingPlacements.isEmpty())
+            d->pendingPlacements.clear();
+    }
+
+    if (!d->pendingPlacements.isEmpty()) {
+        foreach (QMdiSubWindow *window, d->pendingPlacements) {
+            if (!window)
+                continue;
+            if (!window->testAttribute(Qt::WA_Resized)) {
+                QSize newSize(window->sizeHint().boundedTo(viewport()->size()));
+                window->resize(newSize.expandedTo(qSmartMinSize(window)));
+            }
+            if (!window->testAttribute(Qt::WA_Moved) && !window->isMinimized()
+                    && !window->isMaximized()) {
+                d->place(d->placer, window);
+            }
+        }
+        d->pendingPlacements.clear();
+    }
+
+    d->setChildActivationEnabled(true);
+    d->activateCurrentWindow();
+
+    QAbstractScrollArea::showEvent(showEvent);
+}
+
+/*!
+    \reimp
+*/
+bool QMdiArea::viewportEvent(QEvent *event)
+{
+    Q_D(QMdiArea);
+    switch (event->type()) {
+    case QEvent::ChildRemoved: {
+        d->isSubWindowsTiled = false;
+        QObject *removedChild = static_cast<QChildEvent *>(event)->child();
+        for (int i = 0; i < d->childWindows.size(); ++i) {
+            QObject *child = d->childWindows.at(i);
+            if (!child || child == removedChild || !child->parent()
+                    || child->parent() != viewport()) {
+                if (!testOption(DontMaximizeSubWindowOnActivation)) {
+                    // In this case we can only rely on the child being a QObject
+                    // (or 0), but let's try and see if we can get more information.
+                    QWidget *mdiChild = qobject_cast<QWidget *>(removedChild);
+                    if (mdiChild && mdiChild->isMaximized())
+                        d->showActiveWindowMaximized = true;
+                }
+                d->disconnectSubWindow(child);
+                const bool activeRemoved = i == d->indicesToActivatedChildren.at(0);
+                d->childWindows.removeAt(i);
+                d->indicesToActivatedChildren.removeAll(i);
+                d->updateActiveWindow(i, activeRemoved);
+                d->arrangeMinimizedSubWindows();
+                break;
+            }
+        }
+        d->updateScrollBars();
+        break;
+    }
+    case QEvent::Destroy:
+        d->isSubWindowsTiled = false;
+        d->resetActiveWindow();
+        d->childWindows.clear();
+        qWarning("QMdiArea: Deleting the view port is undefined, use setViewport instead.");
+        break;
+    default:
+        break;
+    }
+    return QAbstractScrollArea::viewportEvent(event);
+}
+
+/*!
+    \reimp
+*/
+void QMdiArea::scrollContentsBy(int dx, int dy)
+{
+    Q_D(QMdiArea);
+    const bool wasSubWindowsTiled = d->isSubWindowsTiled;
+    d->ignoreGeometryChange = true;
+    viewport()->scroll(isLeftToRight() ? dx : -dx, dy);
+    d->arrangeMinimizedSubWindows();
+    d->ignoreGeometryChange = false;
+    if (wasSubWindowsTiled)
+        d->isSubWindowsTiled = true;
+}
+
+/*!
+    Arranges all child windows in a tile pattern.
+
+    \sa cascadeSubWindows()
+*/
+void QMdiArea::tileSubWindows()
+{
+    Q_D(QMdiArea);
+    if (!d->regularTiler)
+        d->regularTiler = new RegularTiler;
+    d->rearrange(d->regularTiler);
+}
+
+/*!
+    Arranges all the child windows in a cascade pattern.
+
+    \sa tileSubWindows()
+*/
+void QMdiArea::cascadeSubWindows()
+{
+    Q_D(QMdiArea);
+    if (!d->cascader)
+        d->cascader = new SimpleCascader;
+    d->rearrange(d->cascader);
+}
+
+/*!
+    \reimp
+*/
+bool QMdiArea::event(QEvent *event)
+{
+    Q_D(QMdiArea);
+    switch (event->type()) {
+#ifdef Q_WS_WIN
+    // QWidgetPrivate::hide_helper activates another sub-window when closing a
+    // modal dialog on Windows (see activateWindow() inside the the ifdef).
+    case QEvent::WindowUnblocked:
+        d->activateCurrentWindow();
+        break;
+#endif
+    case QEvent::WindowActivate: {
+        d->isActivated = true;
+        if (d->childWindows.isEmpty())
+            break;
+        if (!d->active)
+            d->activateCurrentWindow();
+        d->setChildActivationEnabled(false, true);
+        break;
+    }
+    case QEvent::WindowDeactivate:
+        d->isActivated = false;
+        d->setChildActivationEnabled(false, true);
+        break;
+    case QEvent::StyleChange:
+        // Re-tile the views if we're in tiled mode. Re-tile means we will change
+        // the geometry of the children, which in turn means 'isSubWindowsTiled'
+        // is set to false, so we have to update the state at the end.
+        if (d->isSubWindowsTiled) {
+            tileSubWindows();
+            d->isSubWindowsTiled = true;
+        }
+        break;
+    case QEvent::WindowIconChange:
+        foreach (QMdiSubWindow *window, d->childWindows) {
+            if (sanityCheck(window, "QMdiArea::WindowIconChange"))
+                QApplication::sendEvent(window, event);
+        }
+        break;
+    case QEvent::Hide:
+        d->setActive(d->active, false, false);
+        d->setChildActivationEnabled(false);
+        break;
+#ifndef QT_NO_TABBAR
+    case QEvent::LayoutDirectionChange:
+        d->updateTabBarGeometry();
+        break;
+#endif
+    default:
+        break;
+    }
+    return QAbstractScrollArea::event(event);
+}
+
+/*!
+    \reimp
+*/
+bool QMdiArea::eventFilter(QObject *object, QEvent *event)
+{
+    if (!object)
+        return QAbstractScrollArea::eventFilter(object, event);
+
+    Q_D(QMdiArea);
+    // Global key events with Ctrl modifier.
+    if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) {
+
+        QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
+        // Ingore key events without a Ctrl modifier (except for press/release on the modifier itself).
+#ifdef Q_WS_MAC
+        if (!(keyEvent->modifiers() & Qt::MetaModifier) && keyEvent->key() != Qt::Key_Meta)
+#else
+        if (!(keyEvent->modifiers() & Qt::ControlModifier) && keyEvent->key() != Qt::Key_Control)
+#endif
+            return QAbstractScrollArea::eventFilter(object, event);
+
+        // Find closest mdi area (in case we have a nested workspace).
+        QMdiArea *area = mdiAreaParent(static_cast<QWidget *>(object));
+        if (!area)
+            return QAbstractScrollArea::eventFilter(object, event);
+
+        const bool keyPress = (event->type() == QEvent::KeyPress) ? true : false;
+
+        // 1) Ctrl-Tab once -> activate the previously active window.
+        // 2) Ctrl-Tab (Tab, Tab, ...) -> iterate through all windows (activateNextSubWindow()).
+        // 3) Ctrl-Shift-Tab (Tab, Tab, ...) -> iterate through all windows in the opposite
+        //    direction (activatePreviousSubWindow())
+        switch (keyEvent->key()) {
+#ifdef Q_WS_MAC
+        case Qt::Key_Meta:
+#else
+        case Qt::Key_Control:
+#endif
+            if (keyPress)
+                area->d_func()->startTabToPreviousTimer();
+            else
+                area->d_func()->activateHighlightedWindow();
+            break;
+        case Qt::Key_Tab:
+        case Qt::Key_Backtab:
+            if (keyPress)
+                area->d_func()->highlightNextSubWindow(keyEvent->key() == Qt::Key_Tab ? 1 : -1);
+            return true;
+#ifndef QT_NO_RUBBERBAND
+        case Qt::Key_Escape:
+            area->d_func()->hideRubberBand();
+            break;
+#endif
+        default:
+            break;
+        }
+        return QAbstractScrollArea::eventFilter(object, event);
+    }
+
+    QMdiSubWindow *subWindow = qobject_cast<QMdiSubWindow *>(object);
+
+    if (!subWindow) {
+        // QApplication events:
+        if (event->type() == QEvent::ApplicationActivate && !d->active
+            && isVisible() && !window()->isMinimized()) {
+            d->activateCurrentWindow();
+        } else if (event->type() == QEvent::ApplicationDeactivate && d->active) {
+            d->setActive(d->active, false, false);
+        }
+        return QAbstractScrollArea::eventFilter(object, event);
+    }
+
+    // QMdiSubWindow events:
+    switch (event->type()) {
+    case QEvent::Move:
+    case QEvent::Resize:
+        if (d->tileCalledFromResizeEvent)
+            break;
+        d->updateScrollBars();
+        if (!subWindow->isMinimized())
+            d->isSubWindowsTiled = false;
+        break;
+    case QEvent::Show:
+#ifndef QT_NO_TABBAR
+        if (d->tabBar) {
+            const int tabIndex = d->childWindows.indexOf(subWindow);
+            if (!d->tabBar->isTabEnabled(tabIndex))
+                d->tabBar->setTabEnabled(tabIndex, true);
+        }
+#endif // QT_NO_TABBAR
+        // fall through
+    case QEvent::Hide:
+        d->isSubWindowsTiled = false;
+        break;
+#ifndef QT_NO_RUBBERBAND
+    case QEvent::Close:
+        if (d->childWindows.indexOf(subWindow) == d->indexToHighlighted)
+            d->hideRubberBand();
+        break;
+#endif
+#ifndef QT_NO_TABBAR
+    case QEvent::WindowTitleChange:
+    case QEvent::ModifiedChange:
+        if (d->tabBar)
+            d->tabBar->setTabText(d->childWindows.indexOf(subWindow), tabTextFor(subWindow));
+        break;
+    case QEvent::WindowIconChange:
+        if (d->tabBar)
+            d->tabBar->setTabIcon(d->childWindows.indexOf(subWindow), subWindow->windowIcon());
+        break;
+#endif // QT_NO_TABBAR
+    default:
+        break;
+    }
+    return QAbstractScrollArea::eventFilter(object, event);
+}
+
+/*!
+    \reimp
+*/
+void QMdiArea::paintEvent(QPaintEvent *paintEvent)
+{
+    Q_D(QMdiArea);
+    QPainter painter(d->viewport);
+    const QVector<QRect> &exposedRects = paintEvent->region().rects();
+    for (int i = 0; i < exposedRects.size(); ++i)
+        painter.fillRect(exposedRects.at(i), d->background);
+}
+
+/*!
+    This slot is called by QAbstractScrollArea after setViewport() has been
+    called. Reimplement this function in a subclass of QMdiArea to
+    initialize the new \a viewport before it is used.
+
+    \sa setViewport()
+*/
+void QMdiArea::setupViewport(QWidget *viewport)
+{
+    Q_D(QMdiArea);
+    if (viewport)
+        viewport->setAttribute(Qt::WA_OpaquePaintEvent, d->background.isOpaque());
+    foreach (QMdiSubWindow *child, d->childWindows) {
+        if (!sanityCheck(child, "QMdiArea::setupViewport"))
+            continue;
+        child->setParent(viewport, child->windowFlags());
+    }
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qmdiarea.cpp"
+
+#endif // QT_NO_MDIAREA