source: trunk/src/gui/widgets/qmdiarea.cpp@ 1021

Last change on this file since 1021 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

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