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

Last change on this file since 305 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 78.4 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information (qt-info@nokia.com)
5**
6** This file is part of the QtGui module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** 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 are unsure which license is appropriate for your use, please
37** contact the sales department at qt-sales@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 application
47 \mainclass
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 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() != q->viewport())
804 child->setParent(q->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(q->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 = q->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, q_func()->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 q->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 (q->horizontalScrollBar()->isVisible())
1259 minAreaHeight += q->horizontalScrollBar()->height();
1260 if (q->verticalScrollBar()->isVisible())
1261 minAreaWidth += q->verticalScrollBar()->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 = q->viewport()->rect();
1272
1273 // Adjust domain width and provide horizontal scroll bar.
1274 if (domain.width() < minWidth) {
1275 domain.setWidth(minWidth);
1276 if (q->horizontalScrollBarPolicy() == Qt::ScrollBarAlwaysOff)
1277 q->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
1278 else if (q->horizontalScrollBar()->value() != 0)
1279 q->horizontalScrollBar()->setValue(0);
1280 }
1281 // Adjust domain height and provide vertical scroll bar.
1282 if (domain.height() < minHeight) {
1283 domain.setHeight(minHeight);
1284 if (q->verticalScrollBarPolicy() == Qt::ScrollBarAlwaysOff)
1285 q->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
1286 else if (q->verticalScrollBar()->value() != 0)
1287 q->verticalScrollBar()->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 the next window in the list of child
1857 windows. The windows are activated in the order in which they are
1858 created (CreationOrder).
1859
1860 \sa activatePreviousSubWindow()
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 the previous window in the list of
1875 child windows. The windows are activated in the order in which
1876 they are created (CreationOrder).
1877
1878 \sa activateNextSubWindow()
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 that the subwindow is not deleted by QMdiArea
1951 and that its parent is set to 0.
1952
1953 \sa addSubWindow()
1954*/
1955void QMdiArea::removeSubWindow(QWidget *widget)
1956{
1957 if (!widget) {
1958 qWarning("QMdiArea::removeSubWindow: null pointer to widget");
1959 return;
1960 }
1961
1962 Q_D(QMdiArea);
1963 if (d->childWindows.isEmpty())
1964 return;
1965
1966 if (QMdiSubWindow *child = qobject_cast<QMdiSubWindow *>(widget)) {
1967 int index = d->childWindows.indexOf(child);
1968 if (index == -1) {
1969 qWarning("QMdiArea::removeSubWindow: window is not inside workspace");
1970 return;
1971 }
1972 d->disconnectSubWindow(child);
1973 d->childWindows.removeAll(child);
1974 d->indicesToActivatedChildren.removeAll(index);
1975 d->updateActiveWindow(index, d->active == child);
1976 child->setParent(0);
1977 return;
1978 }
1979
1980 bool found = false;
1981 foreach (QMdiSubWindow *child, d->childWindows) {
1982 if (!sanityCheck(child, "QMdiArea::removeSubWindow"))
1983 continue;
1984 if (child->widget() == widget) {
1985 child->setWidget(0);
1986 Q_ASSERT(!child->widget());
1987 found = true;
1988 break;
1989 }
1990 }
1991
1992 if (!found)
1993 qWarning("QMdiArea::removeSubWindow: widget is not child of any window inside QMdiArea");
1994}
1995
1996/*!
1997 \property QMdiArea::background
1998 \brief the background brush for the workspace
1999
2000 This property sets the background brush for the workspace area
2001 itself. By default, it is a gray color, but can be any brush
2002 (e.g., colors, gradients or pixmaps).
2003*/
2004QBrush QMdiArea::background() const
2005{
2006 return d_func()->background;
2007}
2008
2009void QMdiArea::setBackground(const QBrush &brush)
2010{
2011 Q_D(QMdiArea);
2012 if (d->background != brush) {
2013 d->background = brush;
2014 d->viewport->setAttribute(Qt::WA_OpaquePaintEvent, brush.isOpaque());
2015 update();
2016 }
2017}
2018
2019
2020/*!
2021 \property QMdiArea::activationOrder
2022 \brief the ordering criteria for subwindow lists
2023 \since 4.4
2024
2025 This property specifies the ordering criteria for the list of
2026 subwindows returned by subWindowList(). By default, it is the window
2027 creation order.
2028
2029 \sa subWindowList()
2030*/
2031QMdiArea::WindowOrder QMdiArea::activationOrder() const
2032{
2033 Q_D(const QMdiArea);
2034 return d->activationOrder;
2035}
2036
2037void QMdiArea::setActivationOrder(WindowOrder order)
2038{
2039 Q_D(QMdiArea);
2040 if (order != d->activationOrder)
2041 d->activationOrder = order;
2042}
2043
2044/*!
2045 If \a on is true, \a option is enabled on the MDI area; otherwise
2046 it is disabled. See AreaOption for the effect of each option.
2047
2048 \sa AreaOption, testOption()
2049*/
2050void QMdiArea::setOption(AreaOption option, bool on)
2051{
2052 Q_D(QMdiArea);
2053 if (on && !(d->options & option))
2054 d->options |= option;
2055 else if (!on && (d->options & option))
2056 d->options &= ~option;
2057}
2058
2059/*!
2060 Returns true if \a option is enabled; otherwise returns false.
2061
2062 \sa AreaOption, setOption()
2063*/
2064bool QMdiArea::testOption(AreaOption option) const
2065{
2066 return d_func()->options & option;
2067}
2068
2069/*!
2070 \property QMdiArea::viewMode
2071 \brief the way sub-windows are displayed in the QMdiArea.
2072 \since 4.4
2073
2074 By default, the SubWindowView is used to display sub-windows.
2075
2076 \sa ViewMode, setTabShape(), setTabPosition()
2077*/
2078QMdiArea::ViewMode QMdiArea::viewMode() const
2079{
2080 Q_D(const QMdiArea);
2081 return d->viewMode;
2082}
2083
2084void QMdiArea::setViewMode(ViewMode mode)
2085{
2086 Q_D(QMdiArea);
2087 d->setViewMode(mode);
2088}
2089
2090#ifndef QT_NO_TABBAR
2091/*!
2092 \property QMdiArea::documentMode
2093 \brief whether the tab bar is set to document mode in tabbed view mode.
2094 \since 4.5
2095
2096 Document mode is disabled by default.
2097
2098 \sa QTabBar::documentMode, setViewMode()
2099*/
2100bool QMdiArea::documentMode() const
2101{
2102 Q_D(const QMdiArea);
2103 return d->documentMode;
2104}
2105
2106void QMdiArea::setDocumentMode(bool enabled)
2107{
2108 Q_D(QMdiArea);
2109 if (d->documentMode == enabled)
2110 return;
2111
2112 d->documentMode = enabled;
2113 d->refreshTabBar();
2114}
2115#endif // QT_NO_TABBAR
2116
2117#ifndef QT_NO_TABWIDGET
2118/*!
2119 \property QMdiArea::tabShape
2120 \brief the shape of the tabs in tabbed view mode.
2121 \since 4.4
2122
2123 Possible values for this property are QTabWidget::Rounded
2124 (default) or QTabWidget::Triangular.
2125
2126 \sa QTabWidget::TabShape, setViewMode()
2127*/
2128QTabWidget::TabShape QMdiArea::tabShape() const
2129{
2130 Q_D(const QMdiArea);
2131 return d->tabShape;
2132}
2133
2134void QMdiArea::setTabShape(QTabWidget::TabShape shape)
2135{
2136 Q_D(QMdiArea);
2137 if (d->tabShape == shape)
2138 return;
2139
2140 d->tabShape = shape;
2141 d->refreshTabBar();
2142}
2143
2144/*!
2145 \property QMdiArea::tabPosition
2146 \brief the position of the tabs in tabbed view mode.
2147 \since 4.4
2148
2149 Possible values for this property are described by the
2150 QTabWidget::TabPosition enum.
2151
2152 \sa QTabWidget::TabPosition, setViewMode()
2153*/
2154QTabWidget::TabPosition QMdiArea::tabPosition() const
2155{
2156 Q_D(const QMdiArea);
2157 return d->tabPosition;
2158}
2159
2160void QMdiArea::setTabPosition(QTabWidget::TabPosition position)
2161{
2162 Q_D(QMdiArea);
2163 if (d->tabPosition == position)
2164 return;
2165
2166 d->tabPosition = position;
2167 d->refreshTabBar();
2168}
2169#endif // QT_NO_TABWIDGET
2170
2171/*!
2172 \reimp
2173*/
2174void QMdiArea::childEvent(QChildEvent *childEvent)
2175{
2176 Q_D(QMdiArea);
2177 if (childEvent->type() == QEvent::ChildPolished) {
2178 if (QMdiSubWindow *mdiChild = qobject_cast<QMdiSubWindow *>(childEvent->child())) {
2179 if (d->childWindows.indexOf(mdiChild) == -1)
2180 d->appendChild(mdiChild);
2181 }
2182 }
2183}
2184
2185/*!
2186 \reimp
2187*/
2188void QMdiArea::resizeEvent(QResizeEvent *resizeEvent)
2189{
2190 Q_D(QMdiArea);
2191 if (d->childWindows.isEmpty()) {
2192 resizeEvent->ignore();
2193 return;
2194 }
2195
2196#ifndef QT_NO_TABBAR
2197 d->updateTabBarGeometry();
2198#endif
2199
2200 // Re-tile the views if we're in tiled mode. Re-tile means we will change
2201 // the geometry of the children, which in turn means 'isSubWindowsTiled'
2202 // is set to false, so we have to update the state at the end.
2203 if (d->isSubWindowsTiled) {
2204 d->tileCalledFromResizeEvent = true;
2205 tileSubWindows();
2206 d->tileCalledFromResizeEvent = false;
2207 d->isSubWindowsTiled = true;
2208 d->startResizeTimer();
2209 // We don't have scroll bars or any maximized views.
2210 return;
2211 }
2212
2213 // Resize maximized views.
2214 bool hasMaximizedSubWindow = false;
2215 foreach (QMdiSubWindow *child, d->childWindows) {
2216 if (sanityCheck(child, "QMdiArea::resizeEvent") && child->isMaximized()
2217 && child->size() != resizeEvent->size()) {
2218 child->resize(resizeEvent->size());
2219 if (!hasMaximizedSubWindow)
2220 hasMaximizedSubWindow = true;
2221 }
2222 }
2223
2224 d->updateScrollBars();
2225
2226 // Minimized views are stacked under maximized views so there's
2227 // no need to re-arrange minimized views on-demand. Start a timer
2228 // just to make things faster with subsequent resize events.
2229 if (hasMaximizedSubWindow)
2230 d->startResizeTimer();
2231 else
2232 d->arrangeMinimizedSubWindows();
2233}
2234
2235/*!
2236 \reimp
2237*/
2238void QMdiArea::timerEvent(QTimerEvent *timerEvent)
2239{
2240 Q_D(QMdiArea);
2241 if (timerEvent->timerId() == d->resizeTimerId) {
2242 killTimer(d->resizeTimerId);
2243 d->resizeTimerId = -1;
2244 d->arrangeMinimizedSubWindows();
2245 } else if (timerEvent->timerId() == d->tabToPreviousTimerId) {
2246 killTimer(d->tabToPreviousTimerId);
2247 d->tabToPreviousTimerId = -1;
2248 if (d->indexToHighlighted < 0)
2249 return;
2250#ifndef QT_NO_RUBBERBAND
2251 // We're not doing a "quick switch" ... show rubber band.
2252 Q_ASSERT(d->indexToHighlighted < d->childWindows.size());
2253 Q_ASSERT(d->rubberBand);
2254 d->showRubberBandFor(d->childWindows.at(d->indexToHighlighted));
2255#endif
2256 }
2257}
2258
2259/*!
2260 \reimp
2261*/
2262void QMdiArea::showEvent(QShowEvent *showEvent)
2263{
2264 Q_D(QMdiArea);
2265 if (!d->pendingRearrangements.isEmpty()) {
2266 bool skipPlacement = false;
2267 foreach (Rearranger *rearranger, d->pendingRearrangements) {
2268 // If this is the case, we don't have to lay out pending child windows
2269 // since the rearranger will find a placement for them.
2270 if (rearranger->type() != Rearranger::IconTiler && !skipPlacement)
2271 skipPlacement = true;
2272 d->rearrange(rearranger);
2273 }
2274 d->pendingRearrangements.clear();
2275
2276 if (skipPlacement && !d->pendingPlacements.isEmpty())
2277 d->pendingPlacements.clear();
2278 }
2279
2280 if (!d->pendingPlacements.isEmpty()) {
2281 foreach (QMdiSubWindow *window, d->pendingPlacements) {
2282 if (!window)
2283 continue;
2284 if (!window->testAttribute(Qt::WA_Resized)) {
2285 QSize newSize(window->sizeHint().boundedTo(viewport()->size()));
2286 window->resize(newSize.expandedTo(qSmartMinSize(window)));
2287 }
2288 if (!window->testAttribute(Qt::WA_Moved) && !window->isMinimized()
2289 && !window->isMaximized()) {
2290 d->place(d->placer, window);
2291 }
2292 }
2293 d->pendingPlacements.clear();
2294 }
2295
2296 d->setChildActivationEnabled(true);
2297 d->activateCurrentWindow();
2298
2299 QAbstractScrollArea::showEvent(showEvent);
2300}
2301
2302/*!
2303 \reimp
2304*/
2305bool QMdiArea::viewportEvent(QEvent *event)
2306{
2307 Q_D(QMdiArea);
2308 switch (event->type()) {
2309 case QEvent::ChildRemoved: {
2310 d->isSubWindowsTiled = false;
2311 QObject *removedChild = static_cast<QChildEvent *>(event)->child();
2312 for (int i = 0; i < d->childWindows.size(); ++i) {
2313 QObject *child = d->childWindows.at(i);
2314 if (!child || child == removedChild || !child->parent()
2315 || child->parent() != viewport()) {
2316 if (!testOption(DontMaximizeSubWindowOnActivation)) {
2317 // In this case we can only rely on the child being a QObject
2318 // (or 0), but let's try and see if we can get more information.
2319 QWidget *mdiChild = qobject_cast<QWidget *>(removedChild);
2320 if (mdiChild && mdiChild->isMaximized())
2321 d->showActiveWindowMaximized = true;
2322 }
2323 d->disconnectSubWindow(child);
2324 const bool activeRemoved = i == d->indicesToActivatedChildren.at(0);
2325 d->childWindows.removeAt(i);
2326 d->indicesToActivatedChildren.removeAll(i);
2327 d->updateActiveWindow(i, activeRemoved);
2328 d->arrangeMinimizedSubWindows();
2329 break;
2330 }
2331 }
2332 d->updateScrollBars();
2333 break;
2334 }
2335 case QEvent::Destroy:
2336 d->isSubWindowsTiled = false;
2337 d->resetActiveWindow();
2338 d->childWindows.clear();
2339 qWarning("QMdiArea: Deleting the view port is undefined, use setViewport instead.");
2340 break;
2341 default:
2342 break;
2343 }
2344 return QAbstractScrollArea::viewportEvent(event);
2345}
2346
2347/*!
2348 \reimp
2349*/
2350void QMdiArea::scrollContentsBy(int dx, int dy)
2351{
2352 Q_D(QMdiArea);
2353 const bool wasSubWindowsTiled = d->isSubWindowsTiled;
2354 d->ignoreGeometryChange = true;
2355 viewport()->scroll(isLeftToRight() ? dx : -dx, dy);
2356 d->arrangeMinimizedSubWindows();
2357 d->ignoreGeometryChange = false;
2358 if (wasSubWindowsTiled)
2359 d->isSubWindowsTiled = true;
2360}
2361
2362/*!
2363 Arranges all child windows in a tile pattern.
2364
2365 \sa cascadeSubWindows()
2366*/
2367void QMdiArea::tileSubWindows()
2368{
2369 Q_D(QMdiArea);
2370 if (!d->regularTiler)
2371 d->regularTiler = new RegularTiler;
2372 d->rearrange(d->regularTiler);
2373}
2374
2375/*!
2376 Arranges all the child windows in a cascade pattern.
2377
2378 \sa tileSubWindows()
2379*/
2380void QMdiArea::cascadeSubWindows()
2381{
2382 Q_D(QMdiArea);
2383 if (!d->cascader)
2384 d->cascader = new SimpleCascader;
2385 d->rearrange(d->cascader);
2386}
2387
2388/*!
2389 \reimp
2390*/
2391bool QMdiArea::event(QEvent *event)
2392{
2393 Q_D(QMdiArea);
2394 switch (event->type()) {
2395#ifdef Q_WS_WIN
2396 // QWidgetPrivate::hide_helper activates another sub-window when closing a
2397 // modal dialog on Windows (see activateWindow() inside the the ifdef).
2398 case QEvent::WindowUnblocked:
2399 d->activateCurrentWindow();
2400 break;
2401#endif
2402 case QEvent::WindowActivate: {
2403 d->isActivated = true;
2404 if (d->childWindows.isEmpty())
2405 break;
2406 if (!d->active)
2407 d->activateCurrentWindow();
2408 d->setChildActivationEnabled(false, true);
2409 break;
2410 }
2411 case QEvent::WindowDeactivate:
2412 d->isActivated = false;
2413 d->setChildActivationEnabled(false, true);
2414 break;
2415 case QEvent::StyleChange:
2416 // Re-tile the views if we're in tiled mode. Re-tile means we will change
2417 // the geometry of the children, which in turn means 'isSubWindowsTiled'
2418 // is set to false, so we have to update the state at the end.
2419 if (d->isSubWindowsTiled) {
2420 tileSubWindows();
2421 d->isSubWindowsTiled = true;
2422 }
2423 break;
2424 case QEvent::WindowIconChange:
2425 foreach (QMdiSubWindow *window, d->childWindows) {
2426 if (sanityCheck(window, "QMdiArea::WindowIconChange"))
2427 QApplication::sendEvent(window, event);
2428 }
2429 break;
2430 case QEvent::Hide:
2431 d->setActive(d->active, false, false);
2432 d->setChildActivationEnabled(false);
2433 break;
2434#ifndef QT_NO_TABBAR
2435 case QEvent::LayoutDirectionChange:
2436 d->updateTabBarGeometry();
2437 break;
2438#endif
2439 default:
2440 break;
2441 }
2442 return QAbstractScrollArea::event(event);
2443}
2444
2445/*!
2446 \reimp
2447*/
2448bool QMdiArea::eventFilter(QObject *object, QEvent *event)
2449{
2450 if (!object)
2451 return QAbstractScrollArea::eventFilter(object, event);
2452
2453 Q_D(QMdiArea);
2454 // Global key events with Ctrl modifier.
2455 if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) {
2456
2457 QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
2458 // Ingore key events without a Ctrl modifier (except for press/release on the modifier itself).
2459#ifdef Q_WS_MAC
2460 if (!(keyEvent->modifiers() & Qt::MetaModifier) && keyEvent->key() != Qt::Key_Meta)
2461#else
2462 if (!(keyEvent->modifiers() & Qt::ControlModifier) && keyEvent->key() != Qt::Key_Control)
2463#endif
2464 return QAbstractScrollArea::eventFilter(object, event);
2465
2466 // Find closest mdi area (in case we have a nested workspace).
2467 QMdiArea *area = mdiAreaParent(static_cast<QWidget *>(object));
2468 if (!area)
2469 return QAbstractScrollArea::eventFilter(object, event);
2470
2471 const bool keyPress = (event->type() == QEvent::KeyPress) ? true : false;
2472
2473 // 1) Ctrl-Tab once -> activate the previously active window.
2474 // 2) Ctrl-Tab (Tab, Tab, ...) -> iterate through all windows (activateNextSubWindow()).
2475 // 3) Ctrl-Shift-Tab (Tab, Tab, ...) -> iterate through all windows in the opposite
2476 // direction (activatePreviousSubWindow())
2477 switch (keyEvent->key()) {
2478#ifdef Q_WS_MAC
2479 case Qt::Key_Meta:
2480#else
2481 case Qt::Key_Control:
2482#endif
2483 if (keyPress)
2484 area->d_func()->startTabToPreviousTimer();
2485 else
2486 area->d_func()->activateHighlightedWindow();
2487 break;
2488 case Qt::Key_Tab:
2489 case Qt::Key_Backtab:
2490 if (keyPress)
2491 area->d_func()->highlightNextSubWindow(keyEvent->key() == Qt::Key_Tab ? 1 : -1);
2492 return true;
2493#ifndef QT_NO_RUBBERBAND
2494 case Qt::Key_Escape:
2495 area->d_func()->hideRubberBand();
2496 break;
2497#endif
2498 default:
2499 break;
2500 }
2501 return QAbstractScrollArea::eventFilter(object, event);
2502 }
2503
2504 QMdiSubWindow *subWindow = qobject_cast<QMdiSubWindow *>(object);
2505
2506 if (!subWindow) {
2507 // QApplication events:
2508 if (event->type() == QEvent::ApplicationActivate && !d->active
2509 && isVisible() && !window()->isMinimized()) {
2510 d->activateCurrentWindow();
2511 } else if (event->type() == QEvent::ApplicationDeactivate && d->active) {
2512 d->setActive(d->active, false, false);
2513 }
2514 return QAbstractScrollArea::eventFilter(object, event);
2515 }
2516
2517 // QMdiSubWindow events:
2518 switch (event->type()) {
2519 case QEvent::Move:
2520 case QEvent::Resize:
2521 if (d->tileCalledFromResizeEvent)
2522 break;
2523 d->updateScrollBars();
2524 if (!subWindow->isMinimized())
2525 d->isSubWindowsTiled = false;
2526 break;
2527 case QEvent::Show:
2528#ifndef QT_NO_TABBAR
2529 if (d->tabBar) {
2530 const int tabIndex = d->childWindows.indexOf(subWindow);
2531 if (!d->tabBar->isTabEnabled(tabIndex))
2532 d->tabBar->setTabEnabled(tabIndex, true);
2533 }
2534#endif // QT_NO_TABBAR
2535 // fall through
2536 case QEvent::Hide:
2537 d->isSubWindowsTiled = false;
2538 break;
2539#ifndef QT_NO_RUBBERBAND
2540 case QEvent::Close:
2541 if (d->childWindows.indexOf(subWindow) == d->indexToHighlighted)
2542 d->hideRubberBand();
2543 break;
2544#endif
2545#ifndef QT_NO_TABBAR
2546 case QEvent::WindowTitleChange:
2547 case QEvent::ModifiedChange:
2548 if (d->tabBar)
2549 d->tabBar->setTabText(d->childWindows.indexOf(subWindow), tabTextFor(subWindow));
2550 break;
2551 case QEvent::WindowIconChange:
2552 if (d->tabBar)
2553 d->tabBar->setTabIcon(d->childWindows.indexOf(subWindow), subWindow->windowIcon());
2554 break;
2555#endif // QT_NO_TABBAR
2556 default:
2557 break;
2558 }
2559 return QAbstractScrollArea::eventFilter(object, event);
2560}
2561
2562/*!
2563 \reimp
2564*/
2565void QMdiArea::paintEvent(QPaintEvent *paintEvent)
2566{
2567 Q_D(QMdiArea);
2568 QPainter painter(d->viewport);
2569 const QVector<QRect> &exposedRects = paintEvent->region().rects();
2570 for (int i = 0; i < exposedRects.size(); ++i)
2571 painter.fillRect(exposedRects.at(i), d->background);
2572}
2573
2574/*!
2575 This slot is called by QAbstractScrollArea after setViewport() has been
2576 called. Reimplement this function in a subclass of QMdiArea to
2577 initialize the new \a viewport before it is used.
2578
2579 \sa setViewport()
2580*/
2581void QMdiArea::setupViewport(QWidget *viewport)
2582{
2583 Q_D(QMdiArea);
2584 if (viewport)
2585 viewport->setAttribute(Qt::WA_OpaquePaintEvent, d->background.isOpaque());
2586 foreach (QMdiSubWindow *child, d->childWindows) {
2587 if (!sanityCheck(child, "QMdiArea::setupViewport"))
2588 continue;
2589 child->setParent(viewport, child->windowFlags());
2590 }
2591}
2592
2593QT_END_NAMESPACE
2594
2595#include "moc_qmdiarea.cpp"
2596
2597#endif // QT_NO_MDIAREA
Note: See TracBrowser for help on using the repository browser.