summaryrefslogtreecommitdiffstats
path: root/src/gui/widgets/qmdiarea.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/widgets/qmdiarea.cpp')
-rw-r--r--src/gui/widgets/qmdiarea.cpp2597
1 files changed, 2597 insertions, 0 deletions
diff --git a/src/gui/widgets/qmdiarea.cpp b/src/gui/widgets/qmdiarea.cpp
new file mode 100644
index 0000000..598d3b5
--- /dev/null
+++ b/src/gui/widgets/qmdiarea.cpp
@@ -0,0 +1,2597 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \class QMdiArea
+ \brief The QMdiArea widget provides an area in which MDI windows are displayed.
+ \since 4.3
+ \ingroup application
+ \mainclass
+
+ 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 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() != q->viewport())
+ child->setParent(q->viewport(), child->windowFlags());
+ childWindows.append(QPointer<QMdiSubWindow>(child));
+
+ if (!child->testAttribute(Qt::WA_Resized) && q->isVisible()) {
+ QSize newSize(child->sizeHint().boundedTo(q->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 = q->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, q_func()->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 q->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 (q->horizontalScrollBar()->isVisible())
+ minAreaHeight += q->horizontalScrollBar()->height();
+ if (q->verticalScrollBar()->isVisible())
+ minAreaWidth += q->verticalScrollBar()->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 = q->viewport()->rect();
+
+ // Adjust domain width and provide horizontal scroll bar.
+ if (domain.width() < minWidth) {
+ domain.setWidth(minWidth);
+ if (q->horizontalScrollBarPolicy() == Qt::ScrollBarAlwaysOff)
+ q->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
+ else if (q->horizontalScrollBar()->value() != 0)
+ q->horizontalScrollBar()->setValue(0);
+ }
+ // Adjust domain height and provide vertical scroll bar.
+ if (domain.height() < minHeight) {
+ domain.setHeight(minHeight);
+ if (q->verticalScrollBarPolicy() == Qt::ScrollBarAlwaysOff)
+ q->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
+ else if (q->verticalScrollBar()->value() != 0)
+ q->verticalScrollBar()->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