1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the test suite of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 
43 #include <QtTest/QtTest>
44 
45 #include <QMdiSubWindow>
46 #include <QMdiArea>
47 
48 #include <QApplication>
49 #include <QMainWindow>
50 #include <QMenuBar>
51 #include <QPushButton>
52 #include <QStyle>
53 #include <QStyleOption>
54 #include <QVBoxLayout>
55 #include <QLineEdit>
56 #include <QDesktopWidget>
57 #include <QDockWidget>
58 #include <QScrollBar>
59 #include <QTextEdit>
60 #ifndef QT_NO_OPENGL
61 #include <QtOpenGL>
62 #endif
63 #include <QMacStyle>
64 
65 #include "../../shared/util.h"
66 #include "../platformquirks.h"
67 
68 static const Qt::WindowFlags DefaultWindowFlags
69     = Qt::SubWindow | Qt::WindowSystemMenuHint
70       | Qt::WindowTitleHint | Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint;
71 
72 Q_DECLARE_METATYPE(QMdiArea::WindowOrder)
Q_DECLARE_METATYPE(QMdiSubWindow *)73 Q_DECLARE_METATYPE(QMdiSubWindow *)
74 Q_DECLARE_METATYPE(QList<int>)
75 Q_DECLARE_METATYPE(QTabWidget::TabPosition)
76 
77 //TESTED_CLASS=
78 //TESTED_FILES=
79 
80 static bool tabBetweenSubWindowsIn(QMdiArea *mdiArea, int tabCount = -1, bool reverse = false)
81 {
82     if (!mdiArea) {
83         qWarning("Null pointer to mdi area");
84         return false;
85     }
86 
87     QList<QMdiSubWindow *> subWindows = mdiArea->subWindowList();
88     const bool walkThrough = tabCount == -1;
89 
90     if (walkThrough) {
91         QMdiSubWindow *active = reverse ? subWindows.front() : subWindows.back();
92         mdiArea->setActiveSubWindow(active);
93         if (mdiArea->activeSubWindow() != active) {
94             qWarning("Failed to set active sub window");
95             return false;
96         }
97         tabCount = subWindows.size();
98     }
99 
100     QWidget *focusWidget = qApp->focusWidget();
101     if (!focusWidget) {
102         qWarning("No focus widget");
103         return false;
104     }
105 
106     Qt::KeyboardModifiers modifiers = reverse ? Qt::ShiftModifier : Qt::NoModifier;
107     Qt::Key key;
108 #ifdef Q_WS_MAC
109     key = Qt::Key_Meta;
110     modifiers |= Qt::MetaModifier;
111 #else
112     key = Qt::Key_Control;
113     modifiers |= Qt::ControlModifier;
114 #endif
115 
116     QTest::keyPress(focusWidget, key, modifiers);
117     for (int i = 0; i < tabCount; ++i) {
118         QTest::keyPress(focusWidget, reverse ? Qt::Key_Backtab : Qt::Key_Tab, modifiers);
119         if (tabCount > 1)
120             QTest::qWait(500);
121         if (walkThrough) {
122             QRubberBand *rubberBand = qFindChild<QRubberBand *>(mdiArea->viewport());
123             if (!rubberBand) {
124                 qWarning("No rubber band");
125                 return false;
126             }
127             QMdiSubWindow *subWindow = subWindows.at(reverse ? subWindows.size() -1 - i : i);
128             if (rubberBand->geometry() != subWindow->geometry()) {
129                 qWarning("Rubber band has different geometry");
130                 return false;
131             }
132         }
133         qApp->processEvents();
134     }
135     QTest::keyRelease(focusWidget, key);
136 
137     return true;
138 }
139 
tabBarShapeFrom(QTabWidget::TabShape shape,QTabWidget::TabPosition position)140 static inline QTabBar::Shape tabBarShapeFrom(QTabWidget::TabShape shape, QTabWidget::TabPosition position)
141 {
142     const bool rounded = (shape == QTabWidget::Rounded);
143     if (position == QTabWidget::North)
144         return rounded ? QTabBar::RoundedNorth : QTabBar::TriangularNorth;
145     if (position == QTabWidget::South)
146         return rounded ? QTabBar::RoundedSouth : QTabBar::TriangularSouth;
147     if (position == QTabWidget::East)
148         return rounded ? QTabBar::RoundedEast : QTabBar::TriangularEast;
149     if (position == QTabWidget::West)
150         return rounded ? QTabBar::RoundedWest : QTabBar::TriangularWest;
151     return QTabBar::RoundedNorth;
152 }
153 
154 enum Arrangement {
155     Tiled,
156     Cascaded
157 };
158 
verifyArrangement(QMdiArea * mdiArea,Arrangement arrangement,const QList<int> & expectedIndices)159 static bool verifyArrangement(QMdiArea *mdiArea, Arrangement arrangement, const QList<int> &expectedIndices)
160 {
161     if (!mdiArea || expectedIndices.isEmpty() || mdiArea->subWindowList().isEmpty())
162         return false;
163 
164     const QList<QMdiSubWindow *> subWindows = mdiArea->subWindowList();
165     const QMdiSubWindow *const firstSubWindow = subWindows.at(0);
166 
167     switch (arrangement) {
168     case Tiled:
169     {
170         // Calculate the number of rows and columns.
171         const int n = subWindows.count();
172         const int numColumns = qMax(qCeil(qSqrt(qreal(n))), 1);
173         const int numRows = qMax((n % numColumns) ? (n / numColumns + 1) : (n / numColumns), 1);
174 
175         // Ensure that the geometry of all the subwindows are as expected by using
176         // QWidget::childAt starting from the middle of the topleft cell and subsequently
177         // adding rowWidth and rowHeight (going from left to right).
178         const int columnWidth = mdiArea->viewport()->width() / numColumns;
179         const int rowHeight = mdiArea->viewport()->height() / numRows;
180         QPoint subWindowPos(columnWidth / 2, rowHeight / 2);
181         for (int i = 0; i < numRows; ++i) {
182             for (int j = 0; j < numColumns; ++j) {
183                 const int index = expectedIndices.at(i * numColumns + j);
184                 QWidget *actual = mdiArea->viewport()->childAt(subWindowPos);
185                 QMdiSubWindow *expected = subWindows.at(index);
186                 if (actual != expected && !expected->isAncestorOf(actual))
187                     return false;
188                 subWindowPos.rx() += columnWidth;
189             }
190             subWindowPos.rx() = columnWidth / 2;
191             subWindowPos.ry() += rowHeight;
192         }
193         break;
194     }
195     case Cascaded:
196     {
197         // Calculate the delta (dx, dy) between two cascaded subwindows.
198         QStyleOptionTitleBar options;
199         options.initFrom(firstSubWindow);
200         int titleBarHeight = firstSubWindow->style()->pixelMetric(QStyle::PM_TitleBarHeight, &options);
201 #ifdef Q_WS_MAC
202         // ### Remove this after the mac style has been fixed
203         if (qobject_cast<QMacStyle *>(firstSubWindow->style()))
204             titleBarHeight -= 4;
205 #endif
206         const QFontMetrics fontMetrics = QFontMetrics(QApplication::font("QWorkspaceTitleBar"));
207         const int dy = qMax(titleBarHeight - (titleBarHeight - fontMetrics.height()) / 2, 1);
208         const int dx = 10;
209 
210         // Current activation/stacking order.
211         const QList<QMdiSubWindow *> activationOrderList = mdiArea->subWindowList(QMdiArea::ActivationHistoryOrder);
212 
213         // Ensure that the geometry of all the subwindows are as expected by using
214         // QWidget::childAt with the position of the first one and subsequently adding
215         // dx and dy.
216         QPoint subWindowPos(20, 5);
217         foreach (int expectedIndex, expectedIndices) {
218             QMdiSubWindow *expected = subWindows.at(expectedIndex);
219             expected->raise();
220             if (mdiArea->viewport()->childAt(subWindowPos) != expected)
221                 return false;
222             expected->lower();
223             subWindowPos.rx() += dx;
224             subWindowPos.ry() += dy;
225         }
226 
227         // Restore stacking order.
228         foreach (QMdiSubWindow *subWindow, activationOrderList) {
229             mdiArea->setActiveSubWindow(subWindow);
230             qApp->processEvents();
231         }
232         break;
233     }
234     default:
235         return false;
236     }
237     return true;
238 }
239 
240 class tst_QMdiArea : public QObject
241 {
242     Q_OBJECT
243 public:
244     tst_QMdiArea();
245 public slots:
246     void initTestCase();
247 protected slots:
248     void activeChanged(QMdiSubWindow *child);
249 
250 private slots:
251     // Tests from QWorkspace
252     void subWindowActivated_data();
253     void subWindowActivated();
254     void subWindowActivated2();
255     void subWindowActivatedWithMinimize();
256     void showWindows();
257     void changeWindowTitle();
258     void changeModified();
259     void childSize();
260     void fixedSize();
261     // New tests
262     void minimumSizeHint();
263     void sizeHint();
264     void setActiveSubWindow();
265     void activeSubWindow();
266     void currentSubWindow();
267     void addAndRemoveWindows();
268     void addAndRemoveWindowsWithReparenting();
269     void removeSubWindow_2();
270     void closeWindows();
271     void activateNextAndPreviousWindow();
272     void subWindowList_data();
273     void subWindowList();
274     void setBackground();
275     void setViewport();
276     void tileSubWindows();
277     void cascadeAndTileSubWindows();
278     void resizeMaximizedChildWindows_data();
279     void resizeMaximizedChildWindows();
280     void focusWidgetAfterAddSubWindow();
281     void dontMaximizeSubWindowOnActivation();
282     void delayedPlacement();
283     void iconGeometryInMenuBar();
284     void resizeTimer();
285     void updateScrollBars();
286     void setActivationOrder_data();
287     void setActivationOrder();
288     void tabBetweenSubWindows();
289     void setViewMode();
290     void setTabsClosable();
291     void setTabsMovable();
292     void setTabShape();
293     void setTabPosition_data();
294     void setTabPosition();
295 #if defined(Q_WS_WIN) || defined(Q_WS_X11)
296     void nativeSubWindows();
297 #endif
298     void task_209615();
299     void task_236750();
300 
301 private:
302     QMdiSubWindow *activeWindow;
303     bool accelPressed;
304 };
305 
tst_QMdiArea()306 tst_QMdiArea::tst_QMdiArea()
307     : activeWindow(0)
308 {
309     qRegisterMetaType<QMdiSubWindow *>();
310 }
311 
initTestCase()312 void tst_QMdiArea::initTestCase()
313 {
314 #ifdef Q_OS_WINCE //disable magic for WindowsCE
315     qApp->setAutoMaximizeThreshold(-1);
316 #endif
317 }
318 
319 // Old QWorkspace tests
activeChanged(QMdiSubWindow * child)320 void tst_QMdiArea::activeChanged(QMdiSubWindow *child)
321 {
322     activeWindow = child;
323 }
324 
subWindowActivated_data()325 void tst_QMdiArea::subWindowActivated_data()
326 {
327     // define the test elements we're going to use
328     QTest::addColumn<int>("count");
329 
330     // create a first testdata instance and fill it with data
331     QTest::newRow( "data0" ) << 0;
332     QTest::newRow( "data1" ) << 1;
333     QTest::newRow( "data2" ) << 2;
334 }
335 
subWindowActivated()336 void tst_QMdiArea::subWindowActivated()
337 {
338     QMainWindow mw(0) ;
339     mw.menuBar();
340     QMdiArea *workspace = new QMdiArea(&mw);
341     workspace->setObjectName(QLatin1String("testWidget"));
342     mw.setCentralWidget(workspace);
343     QSignalSpy spy(workspace, SIGNAL(subWindowActivated(QMdiSubWindow *)));
344     connect( workspace, SIGNAL(subWindowActivated(QMdiSubWindow *)), this, SLOT(activeChanged(QMdiSubWindow *)));
345     mw.show();
346     qApp->setActiveWindow(&mw);
347 
348     QFETCH( int, count );
349     int i;
350 
351     for ( i = 0; i < count; ++i ) {
352         QWidget *widget = new QWidget(workspace, 0);
353         widget->setAttribute(Qt::WA_DeleteOnClose);
354         workspace->addSubWindow(widget)->show();
355         widget->show();
356         qApp->processEvents();
357         QVERIFY( activeWindow == workspace->activeSubWindow() );
358         QCOMPARE(spy.count(), 1);
359         spy.clear();
360     }
361 
362     QList<QMdiSubWindow *> windows = workspace->subWindowList();
363     QCOMPARE( (int)windows.count(), count );
364 
365     for ( i = 0; i < count; ++i ) {
366         QMdiSubWindow *window = windows.at(i);
367         window->showMinimized();
368         qApp->processEvents();
369         QVERIFY( activeWindow == workspace->activeSubWindow() );
370         if ( i == 1 )
371             QVERIFY( activeWindow == window );
372     }
373 
374     for ( i = 0; i < count; ++i ) {
375         QMdiSubWindow *window = windows.at(i);
376         window->showNormal();
377         qApp->processEvents();
378         QVERIFY( window == activeWindow );
379         QVERIFY( activeWindow == workspace->activeSubWindow() );
380     }
381     spy.clear();
382 
383     while (workspace->activeSubWindow() ) {
384         workspace->activeSubWindow()->close();
385         qApp->processEvents();
386         QVERIFY(activeWindow == workspace->activeSubWindow());
387         QCOMPARE(spy.count(), 1);
388         spy.clear();
389     }
390 
391     QVERIFY(activeWindow == 0);
392     QVERIFY(workspace->activeSubWindow() == 0);
393     QCOMPARE(workspace->subWindowList().count(), 0);
394 
395     {
396         workspace->hide();
397         QWidget *widget = new QWidget(workspace);
398         widget->setAttribute(Qt::WA_DeleteOnClose);
399         QMdiSubWindow *window = workspace->addSubWindow(widget);
400         widget->show();
401         QCOMPARE(spy.count(), 0);
402         workspace->show();
403         QCOMPARE(spy.count(), 1);
404         spy.clear();
405         QVERIFY( activeWindow == window );
406         window->close();
407         qApp->processEvents();
408         QCOMPARE(spy.count(), 1);
409         spy.clear();
410         QVERIFY( activeWindow == 0 );
411     }
412 
413     {
414         workspace->hide();
415         QWidget *widget = new QWidget(workspace);
416         widget->setAttribute(Qt::WA_DeleteOnClose);
417         QMdiSubWindow *window = workspace->addSubWindow(widget);
418         widget->showMaximized();
419         qApp->sendPostedEvents();
420         QCOMPARE(spy.count(), 0);
421         spy.clear();
422         workspace->show();
423         QCOMPARE(spy.count(), 1);
424         spy.clear();
425         QVERIFY( activeWindow == window );
426         window->close();
427         qApp->processEvents();
428         QCOMPARE(spy.count(), 1);
429         spy.clear();
430         QVERIFY( activeWindow == 0 );
431     }
432 
433     {
434         QWidget *widget = new QWidget(workspace);
435         widget->setAttribute(Qt::WA_DeleteOnClose);
436         QMdiSubWindow *window = workspace->addSubWindow(widget);
437         widget->showMinimized();
438         QCOMPARE(spy.count(), 1);
439         spy.clear();
440         QVERIFY( activeWindow == window );
441         QVERIFY(workspace->activeSubWindow() == window);
442         window->close();
443         qApp->processEvents();
444         QCOMPARE(spy.count(), 1);
445         spy.clear();
446         QVERIFY(workspace->activeSubWindow() == 0);
447         QVERIFY( activeWindow == 0 );
448     }
449 }
450 
451 #ifdef Q_WS_MAC
452 #include <Security/AuthSession.h>
macHasAccessToWindowsServer()453 bool macHasAccessToWindowsServer()
454 {
455     SecuritySessionId mySession;
456     SessionAttributeBits sessionInfo;
457     SessionGetInfo(callerSecuritySession, &mySession, &sessionInfo);
458     return (sessionInfo & sessionHasGraphicAccess);
459 }
460 #endif
461 
462 
subWindowActivated2()463 void tst_QMdiArea::subWindowActivated2()
464 {
465     QMdiArea mdiArea;
466     QSignalSpy spy(&mdiArea, SIGNAL(subWindowActivated(QMdiSubWindow *)));
467     for (int i = 0; i < 5; ++i)
468         mdiArea.addSubWindow(new QWidget);
469     QCOMPARE(spy.count(), 0);
470     mdiArea.show();
471 #ifdef Q_WS_X11
472     qt_x11_wait_for_window_manager(&mdiArea);
473 #endif
474     QTest::qWaitForWindowShown(&mdiArea);
475     mdiArea.activateWindow();
476     QTest::qWait(100);
477 
478     QTRY_COMPARE(spy.count(), 5);
479     QCOMPARE(mdiArea.activeSubWindow(), mdiArea.subWindowList().back());
480     spy.clear();
481 
482     // Just to make sure another widget is on top wrt. stacking order.
483     // This will typically become the active window if things are broken.
484     QMdiSubWindow *staysOnTopWindow = mdiArea.subWindowList().at(3);
485     staysOnTopWindow->setWindowFlags(Qt::WindowStaysOnTopHint);
486     mdiArea.setActiveSubWindow(staysOnTopWindow);
487     QCOMPARE(spy.count(), 1);
488     QCOMPARE(mdiArea.activeSubWindow(), staysOnTopWindow);
489     spy.clear();
490 
491     QMdiSubWindow *activeSubWindow = mdiArea.subWindowList().at(2);
492     mdiArea.setActiveSubWindow(activeSubWindow);
493     QCOMPARE(spy.count(), 1);
494     QCOMPARE(mdiArea.activeSubWindow(), activeSubWindow);
495     spy.clear();
496 
497     // Check that we only emit _one_ signal and the active window
498     // is unchanged after hide/show.
499     mdiArea.hide();
500 #ifdef Q_WS_X11
501     qt_x11_wait_for_window_manager(&mdiArea);
502 #endif
503     QTest::qWait(100);
504     QTRY_COMPARE(spy.count(), 1);
505     QVERIFY(!mdiArea.activeSubWindow());
506     QCOMPARE(mdiArea.currentSubWindow(), activeSubWindow);
507     spy.clear();
508 
509     mdiArea.show();
510 #ifdef Q_WS_X11
511     qt_x11_wait_for_window_manager(&mdiArea);
512 #endif
513     QTest::qWait(100);
514     QTRY_COMPARE(spy.count(), 1);
515     QCOMPARE(mdiArea.activeSubWindow(), activeSubWindow);
516     spy.clear();
517 
518     if (PlatformQuirks::isAutoMaximizing())
519         QSKIP("Platform is auto maximizing, so no showMinimized()", SkipAll);
520 
521     // Check that we only emit _one_ signal and the active window
522     // is unchanged after showMinimized/showNormal.
523     mdiArea.showMinimized();
524 #ifdef Q_WS_X11
525     qt_x11_wait_for_window_manager(&mdiArea);
526 #elif defined (Q_WS_MAC)
527     if (!macHasAccessToWindowsServer())
528         QEXPECT_FAIL("", "showMinimized doesn't really minimize if you don't have access to the server", Abort);
529 #endif
530     QTest::qWait(10);
531 #if defined(Q_WS_QWS)
532     QEXPECT_FAIL("", "task 168682", Abort);
533 #endif
534 #ifdef Q_OS_WINCE
535     QSKIP("Not fixed yet. See Task 197453", SkipAll);
536 #endif
537     QTRY_COMPARE(spy.count(), 1);
538     QVERIFY(!mdiArea.activeSubWindow());
539     QCOMPARE(mdiArea.currentSubWindow(), activeSubWindow);
540     spy.clear();
541 
542     mdiArea.showNormal();
543 #ifdef Q_WS_X11
544     qt_x11_wait_for_window_manager(&mdiArea);
545 #endif
546     QTest::qWait(100);
547     QTRY_COMPARE(spy.count(), 1);
548     QCOMPARE(mdiArea.activeSubWindow(), activeSubWindow);
549     spy.clear();
550 }
551 
subWindowActivatedWithMinimize()552 void tst_QMdiArea::subWindowActivatedWithMinimize()
553 {
554     QMainWindow mw(0) ;
555     mw.menuBar();
556     QMdiArea *workspace = new QMdiArea(&mw);
557     workspace->setObjectName(QLatin1String("testWidget"));
558     mw.setCentralWidget(workspace);
559     QSignalSpy spy(workspace, SIGNAL(subWindowActivated(QMdiSubWindow *)));
560     connect( workspace, SIGNAL(subWindowActivated(QMdiSubWindow *)), this, SLOT(activeChanged(QMdiSubWindow *)) );
561     mw.show();
562     qApp->setActiveWindow(&mw);
563     QWidget *widget = new QWidget(workspace);
564     widget->setAttribute(Qt::WA_DeleteOnClose);
565     QMdiSubWindow *window1 = workspace->addSubWindow(widget);
566     QWidget *widget2 = new QWidget(workspace);
567     widget2->setAttribute(Qt::WA_DeleteOnClose);
568     QMdiSubWindow *window2 = workspace->addSubWindow(widget2);
569 
570     widget->showMinimized();
571     QVERIFY( activeWindow == window1 );
572     widget2->showMinimized();
573     QVERIFY( activeWindow == window2 );
574 
575     window2->close();
576     qApp->processEvents();
577     QVERIFY( activeWindow == window1 );
578 
579     window1->close();
580     qApp->processEvents();
581     QVERIFY(workspace->activeSubWindow() == 0);
582     QVERIFY( activeWindow == 0 );
583 
584     QVERIFY( workspace->subWindowList().count() == 0 );
585 }
586 
showWindows()587 void tst_QMdiArea::showWindows()
588 {
589     QMdiArea *ws = new QMdiArea( 0 );
590 
591     QWidget *widget = 0;
592     ws->show();
593 
594     widget = new QWidget(ws);
595     widget->show();
596     QVERIFY( widget->isVisible() );
597 
598     widget = new QWidget(ws);
599     widget->showMaximized();
600     QVERIFY( widget->isMaximized() );
601     widget->showNormal();
602     QVERIFY( !widget->isMaximized() );
603 
604     widget = new QWidget(ws);
605     widget->showMinimized();
606     QVERIFY( widget->isMinimized() );
607     widget->showNormal();
608     QVERIFY( !widget->isMinimized() );
609 
610     ws->hide();
611 
612     widget = new QWidget(ws);
613     ws->show();
614     QVERIFY( widget->isVisible() );
615 
616     ws->hide();
617 
618     widget = new QWidget(ws);
619     widget->showMaximized();
620     QVERIFY( widget->isMaximized() );
621     ws->show();
622     QVERIFY( widget->isVisible() );
623     QVERIFY( widget->isMaximized() );
624     ws->hide();
625 
626     widget = new QWidget(ws);
627     widget->showMinimized();
628     ws->show();
629     QVERIFY( widget->isMinimized() );
630     ws->hide();
631 
632     delete ws;
633 }
634 
635 
636 //#define USE_SHOW
637 
changeWindowTitle()638 void tst_QMdiArea::changeWindowTitle()
639 {
640     const QString mwc = QString::fromLatin1("MainWindow's Caption");
641     const QString mwc2 = QString::fromLatin1("MainWindow's New Caption");
642     const QString wc = QString::fromLatin1("Widget's Caption");
643     const QString wc2 = QString::fromLatin1("Widget's New Caption");
644 
645     QMainWindow *mw = new QMainWindow;
646     mw->setWindowTitle( mwc );
647     QMdiArea *ws = new QMdiArea( mw );
648     mw->setCentralWidget( ws );
649     mw->menuBar();
650     mw->show();
651     QTest::qWaitForWindowShown(mw);
652 
653     QWidget *widget = new QWidget( ws );
654     widget->setWindowTitle( wc );
655     ws->addSubWindow(widget);
656 
657     QCOMPARE( mw->windowTitle(), mwc );
658 
659 #ifdef USE_SHOW
660     widget->showMaximized();
661 #else
662     widget->setWindowState(Qt::WindowMaximized);
663 #endif
664 #if !defined(Q_WS_MAC) && !defined(Q_OS_WINCE) && !defined(Q_OS_SYMBIAN)
665     QTRY_COMPARE( mw->windowTitle(), QString::fromLatin1("%1 - [%2]").arg(mwc).arg(wc) );
666 #endif
667 
668     mw->hide();
669     qApp->processEvents();
670     mw->show();
671     qApp->processEvents();
672     QTest::qWaitForWindowShown(mw);
673 
674 #if !defined(Q_WS_MAC) && !defined(Q_OS_WINCE) && !defined(Q_OS_SYMBIAN)
675     QTRY_COMPARE( mw->windowTitle(), QString::fromLatin1("%1 - [%2]").arg(mwc).arg(wc) );
676 #endif
677 
678 #ifdef USE_SHOW
679     widget->showNormal();
680 #else
681     widget->setWindowState(Qt::WindowNoState);
682 #endif
683     qApp->processEvents();
684     QCOMPARE( mw->windowTitle(), mwc );
685 
686 #ifdef USE_SHOW
687     widget->showMaximized();
688 #else
689     widget->setWindowState(Qt::WindowMaximized);
690 #endif
691     qApp->processEvents();
692 #if !defined(Q_WS_MAC) && !defined(Q_OS_WINCE) && !defined(Q_OS_SYMBIAN)
693     QTRY_COMPARE( mw->windowTitle(), QString::fromLatin1("%1 - [%2]").arg(mwc).arg(wc) );
694     widget->setWindowTitle( wc2 );
695     QCOMPARE( mw->windowTitle(), QString::fromLatin1("%1 - [%2]").arg(mwc).arg(wc2) );
696     mw->setWindowTitle( mwc2 );
697     QCOMPARE( mw->windowTitle(), QString::fromLatin1("%1 - [%2]").arg(mwc2).arg(wc2) );
698 #endif
699 
700     mw->show();
701     qApp->setActiveWindow(mw);
702 
703 #ifdef USE_SHOW
704     mw->showFullScreen();
705 #else
706     mw->setWindowState(Qt::WindowFullScreen);
707 #endif
708 
709     qApp->processEvents();
710 #if !defined(Q_WS_MAC) && !defined(Q_OS_WINCE) && !defined(Q_OS_SYMBIAN)
711     QCOMPARE( mw->windowTitle(), QString::fromLatin1("%1 - [%2]").arg(mwc2).arg(wc2) );
712 #endif
713 #ifdef USE_SHOW
714     widget->showNormal();
715 #else
716     widget->setWindowState(Qt::WindowNoState);
717 #endif
718     qApp->processEvents();
719 #if defined(Q_WS_MAC) || defined(Q_OS_WINCE) || defined(Q_OS_SYMBIAN)
720     QCOMPARE(mw->windowTitle(), mwc);
721 #else
722     QCOMPARE( mw->windowTitle(), mwc2 );
723 #endif
724 
725 #ifdef USE_SHOW
726     widget->showMaximized();
727 #else
728     widget->setWindowState(Qt::WindowMaximized);
729 #endif
730     qApp->processEvents();
731 #if !defined(Q_WS_MAC) && !defined(Q_OS_WINCE) && !defined(Q_OS_SYMBIAN)
732     QCOMPARE( mw->windowTitle(), QString::fromLatin1("%1 - [%2]").arg(mwc2).arg(wc2) );
733 #endif
734 
735 #ifdef USE_SHOW
736     mw->showNormal();
737 #else
738     mw->setWindowState(Qt::WindowNoState);
739 #endif
740     qApp->processEvents();
741 #ifdef USE_SHOW
742     widget->showNormal();
743 #else
744     widget->setWindowState(Qt::WindowNoState);
745 #endif
746 
747     delete mw;
748 }
749 
changeModified()750 void tst_QMdiArea::changeModified()
751 {
752     const QString mwc = QString::fromLatin1("MainWindow's Caption");
753     const QString wc = QString::fromLatin1("Widget's Caption[*]");
754 
755     QMainWindow *mw = new QMainWindow(0);
756     mw->setWindowTitle( mwc );
757     QMdiArea *ws = new QMdiArea( mw );
758     mw->setCentralWidget( ws );
759     mw->menuBar();
760     mw->show();
761 
762     QWidget *widget = new QWidget( ws );
763     widget->setWindowTitle( wc );
764     ws->addSubWindow(widget);
765 
766     QCOMPARE( mw->isWindowModified(), false);
767     QCOMPARE( widget->isWindowModified(), false);
768     widget->setWindowState(Qt::WindowMaximized);
769     QCOMPARE( mw->isWindowModified(), false);
770     QCOMPARE( widget->isWindowModified(), false);
771 
772     widget->setWindowState(Qt::WindowNoState);
773     QCOMPARE( mw->isWindowModified(), false);
774     QCOMPARE( widget->isWindowModified(), false);
775 
776     widget->setWindowModified(true);
777     QCOMPARE( mw->isWindowModified(), false);
778     QCOMPARE( widget->isWindowModified(), true);
779     widget->setWindowState(Qt::WindowMaximized);
780 #if !defined(Q_WS_MAC) && !defined(Q_OS_WINCE) && !defined(Q_OS_SYMBIAN)
781     QCOMPARE( mw->isWindowModified(), true);
782 #endif
783     QCOMPARE( widget->isWindowModified(), true);
784 
785     widget->setWindowState(Qt::WindowNoState);
786     QCOMPARE( mw->isWindowModified(), false);
787     QCOMPARE( widget->isWindowModified(), true);
788 
789     widget->setWindowState(Qt::WindowMaximized);
790 #if !defined(Q_WS_MAC) && !defined(Q_OS_WINCE) && !defined(Q_OS_SYMBIAN)
791     QCOMPARE( mw->isWindowModified(), true);
792 #endif
793     QCOMPARE( widget->isWindowModified(), true);
794 
795     widget->setWindowModified(false);
796     QCOMPARE( mw->isWindowModified(), false);
797     QCOMPARE( widget->isWindowModified(), false);
798 
799     widget->setWindowModified(true);
800 #if !defined(Q_WS_MAC) && !defined(Q_OS_WINCE) && !defined(Q_OS_SYMBIAN)
801     QCOMPARE( mw->isWindowModified(), true);
802 #endif
803     QCOMPARE( widget->isWindowModified(), true);
804 
805     widget->setWindowState(Qt::WindowNoState);
806     QCOMPARE( mw->isWindowModified(), false);
807     QCOMPARE( widget->isWindowModified(), true);
808 
809     delete mw;
810 }
811 
812 class MyChild : public QWidget
813 {
814 public:
MyChild(QWidget * parent=0)815     MyChild(QWidget *parent = 0) : QWidget(parent) {}
sizeHint() const816     QSize sizeHint() const { return QSize(234, 123); }
817 };
818 
childSize()819 void tst_QMdiArea::childSize()
820 {
821     QMdiArea ws;
822 
823     MyChild *child = new MyChild(&ws);
824     child->show();
825     QCOMPARE(child->size(), child->sizeHint());
826     delete child;
827 
828     child = new MyChild(&ws);
829     child->setFixedSize(200, 200);
830     child->show();
831     QCOMPARE(child->size(), child->minimumSize());
832     delete child;
833 
834     child = new MyChild(&ws);
835     child->resize(150, 150);
836     child->show();
837     QCOMPARE(child->size(), QSize(150,150));
838     delete child;
839 }
840 
fixedSize()841 void tst_QMdiArea::fixedSize()
842 {
843     QMdiArea *ws = new QMdiArea;
844     int i;
845 
846     ws->resize(500, 500);
847 //     ws->show();
848 
849     QSize fixed(300, 300);
850     for (i = 0; i < 4; ++i) {
851         QWidget *child = new QWidget(ws);
852         child->setFixedSize(fixed);
853         child->show();
854     }
855 
856     QList<QMdiSubWindow *> windows = ws->subWindowList();
857     for (i = 0; i < (int)windows.count(); ++i) {
858         QMdiSubWindow *child = windows.at(i);
859         QCOMPARE(child->size(), fixed);
860     }
861 
862     ws->cascadeSubWindows();
863     ws->resize(800, 800);
864     for (i = 0; i < (int)windows.count(); ++i) {
865         QMdiSubWindow *child = windows.at(i);
866         QCOMPARE(child->size(), fixed);
867     }
868     ws->resize(500, 500);
869 
870     ws->tileSubWindows();
871     ws->resize(800, 800);
872     for (i = 0; i < (int)windows.count(); ++i) {
873         QMdiSubWindow *child = windows.at(i);
874         QCOMPARE(child->size(), fixed);
875     }
876     ws->resize(500, 500);
877 
878     for (i = 0; i < (int)windows.count(); ++i) {
879         QMdiSubWindow *child = windows.at(i);
880         delete child;
881     }
882 
883     delete ws;
884 }
885 
886 class LargeWidget : public QWidget
887 {
888 public:
LargeWidget(QWidget * parent=0)889     LargeWidget(QWidget *parent = 0) : QWidget(parent) {}
sizeHint() const890     QSize sizeHint() const { return QSize(1280, 1024); }
minimumSizeHint() const891     QSize minimumSizeHint() const { return QSize(300, 300); }
892 };
893 
894 // New tests
minimumSizeHint()895 void tst_QMdiArea::minimumSizeHint()
896 {
897     QMdiArea workspace;
898     workspace.show();
899     QSize expectedSize(workspace.style()->pixelMetric(QStyle::PM_MDIMinimizedWidth),
900                        workspace.style()->pixelMetric(QStyle::PM_TitleBarHeight));
901     qApp->processEvents();
902     QAbstractScrollArea dummyScrollArea;
903     dummyScrollArea.setFrameStyle(QFrame::NoFrame);
904     expectedSize = expectedSize.expandedTo(dummyScrollArea.minimumSizeHint());
905     QCOMPARE(workspace.minimumSizeHint(), expectedSize.expandedTo(qApp->globalStrut()));
906 
907     QWidget *window = workspace.addSubWindow(new QWidget);
908     qApp->processEvents();
909     window->show();
910     QCOMPARE(workspace.minimumSizeHint(), expectedSize.expandedTo(window->minimumSizeHint()));
911 
912     QMdiSubWindow *subWindow = workspace.addSubWindow(new LargeWidget);
913     subWindow->show();
914     QCOMPARE(workspace.minimumSizeHint(), expectedSize.expandedTo(subWindow->minimumSizeHint()));
915 
916     workspace.setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
917     workspace.setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
918     QCOMPARE(workspace.minimumSizeHint(), expectedSize);
919 }
920 
sizeHint()921 void tst_QMdiArea::sizeHint()
922 {
923     QMdiArea workspace;
924     workspace.show();
925     QSize desktopSize = QApplication::desktop()->size();
926     QSize expectedSize(desktopSize.width() * 2/3, desktopSize.height() * 2/3);
927     QCOMPARE(workspace.sizeHint(), expectedSize.expandedTo(qApp->globalStrut()));
928 
929     QWidget *window = workspace.addSubWindow(new QWidget);
930     qApp->processEvents();
931     window->show();
932     QCOMPARE(workspace.sizeHint(), expectedSize.expandedTo(window->sizeHint()));
933 
934     QMdiSubWindow *nested = workspace.addSubWindow(new QMdiArea);
935     expectedSize = QSize(desktopSize.width() * 2/6, desktopSize.height() * 2/6);
936     QCOMPARE(nested->widget()->sizeHint(), expectedSize);
937 }
938 
setActiveSubWindow()939 void tst_QMdiArea::setActiveSubWindow()
940 {
941     QMdiArea workspace;
942     workspace.show();
943 
944     QSignalSpy spy(&workspace, SIGNAL(subWindowActivated(QMdiSubWindow *)));
945     connect(&workspace, SIGNAL(subWindowActivated(QMdiSubWindow *)), this, SLOT(activeChanged(QMdiSubWindow *)));
946     qApp->setActiveWindow(&workspace);
947 
948     // Activate hidden windows
949     const int windowCount = 10;
950     QMdiSubWindow *windows[windowCount];
951     for (int i = 0; i < windowCount; ++i) {
952         windows[i] = qobject_cast<QMdiSubWindow *>(workspace.addSubWindow(new QWidget));
953         qApp->processEvents();
954         QVERIFY(windows[i]->isHidden());
955         workspace.setActiveSubWindow(windows[i]);
956     }
957     QCOMPARE(spy.count(), 0);
958     QVERIFY(!activeWindow);
959     spy.clear();
960 
961     // Activate visible windows
962     for (int i = 0; i < windowCount; ++i) {
963         windows[i]->show();
964         QVERIFY(!windows[i]->isHidden());
965         workspace.setActiveSubWindow(windows[i]);
966         qApp->processEvents();
967         QCOMPARE(spy.count(), 1);
968         QCOMPARE(activeWindow, windows[i]);
969         spy.clear();
970     }
971 
972     // Deactivate active window
973     QCOMPARE(workspace.activeSubWindow(), windows[windowCount - 1]);
974     workspace.setActiveSubWindow(0);
975     QCOMPARE(spy.count(), 1);
976     QVERIFY(!activeWindow);
977     QVERIFY(!workspace.activeSubWindow());
978 
979     // Activate widget which is not child of any window inside workspace
980     QMdiSubWindow fakeWindow;
981     QTest::ignoreMessage(QtWarningMsg, "QMdiArea::setActiveSubWindow: window is not inside workspace");
982     workspace.setActiveSubWindow(&fakeWindow);
983 
984 }
985 
activeSubWindow()986 void tst_QMdiArea::activeSubWindow()
987 {
988     QMainWindow mainWindow;
989 
990     QMdiArea *mdiArea = new QMdiArea;
991     QLineEdit *subWindowLineEdit = new QLineEdit;
992     QMdiSubWindow *subWindow = mdiArea->addSubWindow(subWindowLineEdit);
993     mainWindow.setCentralWidget(mdiArea);
994 
995     QDockWidget *dockWidget = new QDockWidget(QLatin1String("Dock Widget"), &mainWindow);
996     dockWidget->setAllowedAreas(Qt::LeftDockWidgetArea);
997     QLineEdit *dockWidgetLineEdit = new QLineEdit;
998     dockWidget->setWidget(dockWidgetLineEdit);
999     mainWindow.addDockWidget(Qt::LeftDockWidgetArea, dockWidget);
1000 
1001     mainWindow.show();
1002 #ifdef Q_WS_X11
1003     qt_x11_wait_for_window_manager(&mainWindow);
1004 #endif
1005 
1006     qApp->setActiveWindow(&mainWindow);
1007     QCOMPARE(mdiArea->activeSubWindow(), subWindow);
1008     QCOMPARE(qApp->focusWidget(), (QWidget *)subWindowLineEdit);
1009 
1010     dockWidgetLineEdit->setFocus();
1011     QCOMPARE(qApp->focusWidget(), (QWidget *)dockWidgetLineEdit);
1012     QCOMPARE(mdiArea->activeSubWindow(), subWindow);
1013 
1014     QEvent deactivateEvent(QEvent::WindowDeactivate);
1015     qApp->sendEvent(subWindow, &deactivateEvent);
1016     QVERIFY(!mdiArea->activeSubWindow());
1017     QCOMPARE(qApp->focusWidget(), (QWidget *)dockWidgetLineEdit);
1018 
1019     QEvent activateEvent(QEvent::WindowActivate);
1020     qApp->sendEvent(subWindow, &activateEvent);
1021     QCOMPARE(mdiArea->activeSubWindow(), subWindow);
1022     QCOMPARE(qApp->focusWidget(), (QWidget *)subWindowLineEdit);
1023 
1024     QLineEdit dummyTopLevel;
1025     dummyTopLevel.show();
1026 #ifdef Q_WS_X11
1027     qt_x11_wait_for_window_manager(&dummyTopLevel);
1028 #endif
1029 
1030     qApp->setActiveWindow(&dummyTopLevel);
1031     QCOMPARE(mdiArea->activeSubWindow(), subWindow);
1032 
1033     qApp->setActiveWindow(&mainWindow);
1034     QCOMPARE(mdiArea->activeSubWindow(), subWindow);
1035 
1036 #if !defined(Q_WS_MAC) && !defined(Q_WS_WIN)
1037     qApp->setActiveWindow(0);
1038     QVERIFY(!mdiArea->activeSubWindow());
1039 #endif
1040 
1041     //task 202657
1042     dockWidgetLineEdit->setFocus();
1043     qApp->setActiveWindow(&mainWindow);
1044     QVERIFY(dockWidgetLineEdit->hasFocus());
1045 }
1046 
currentSubWindow()1047 void tst_QMdiArea::currentSubWindow()
1048 {
1049     QMdiArea mdiArea;
1050     mdiArea.show();
1051 #ifdef Q_WS_X11
1052     qt_x11_wait_for_window_manager(&mdiArea);
1053 #endif
1054 
1055     for (int i = 0; i < 5; ++i)
1056         mdiArea.addSubWindow(new QLineEdit)->show();
1057 
1058     qApp->setActiveWindow(&mdiArea);
1059     QCOMPARE(qApp->activeWindow(), (QWidget *)&mdiArea);
1060 
1061     // Check that the last added window is the active and the current.
1062     QMdiSubWindow *active = mdiArea.activeSubWindow();
1063     QVERIFY(active);
1064     QCOMPARE(mdiArea.subWindowList().back(), active);
1065     QCOMPARE(mdiArea.currentSubWindow(), active);
1066 
1067     QLineEdit dummyTopLevel;
1068     dummyTopLevel.show();
1069 #ifdef Q_WS_X11
1070     qt_x11_wait_for_window_manager(&dummyTopLevel);
1071 #endif
1072 
1073     // Move focus to another top-level and check that we still
1074     // have an active window.
1075     qApp->setActiveWindow(&dummyTopLevel);
1076     QCOMPARE(qApp->activeWindow(), (QWidget *)&dummyTopLevel);
1077     QVERIFY(mdiArea.activeSubWindow());
1078 
1079     delete active;
1080     active = 0;
1081 
1082     // We just deleted the current sub-window -> current should then
1083     // be the next in list (which in this case is the first sub-window).
1084     QVERIFY(mdiArea.currentSubWindow());
1085     QCOMPARE(mdiArea.currentSubWindow(), mdiArea.subWindowList().front());
1086 
1087     // Activate mdi area and check that active == current.
1088     qApp->setActiveWindow(&mdiArea);
1089     active = mdiArea.activeSubWindow();
1090     QVERIFY(active);
1091     QCOMPARE(mdiArea.activeSubWindow(), mdiArea.subWindowList().front());
1092 
1093     active->hide();
1094     QCOMPARE(mdiArea.activeSubWindow(), active);
1095     QCOMPARE(mdiArea.currentSubWindow(), active);
1096 
1097     qApp->setActiveWindow(&dummyTopLevel);
1098     QVERIFY(mdiArea.activeSubWindow());
1099     QCOMPARE(mdiArea.currentSubWindow(), active);
1100 
1101     qApp->setActiveWindow(&mdiArea);
1102     active->show();
1103     QCOMPARE(mdiArea.activeSubWindow(), active);
1104 
1105     mdiArea.setActiveSubWindow(0);
1106     QVERIFY(!mdiArea.activeSubWindow());
1107     QVERIFY(!mdiArea.currentSubWindow());
1108 
1109     mdiArea.setActiveSubWindow(active);
1110     QCOMPARE(mdiArea.activeSubWindow(), active);
1111     QEvent windowDeactivate(QEvent::WindowDeactivate);
1112     qApp->sendEvent(active, &windowDeactivate);
1113     QVERIFY(!mdiArea.activeSubWindow());
1114     QVERIFY(!mdiArea.currentSubWindow());
1115 
1116     QEvent windowActivate(QEvent::WindowActivate);
1117     qApp->sendEvent(active, &windowActivate);
1118     QVERIFY(mdiArea.activeSubWindow());
1119     QVERIFY(mdiArea.currentSubWindow());
1120 
1121 #if !defined(Q_WS_MAC) && !defined(Q_WS_WIN)
1122     qApp->setActiveWindow(0);
1123     QVERIFY(!mdiArea.activeSubWindow());
1124     QVERIFY(mdiArea.currentSubWindow());
1125 #endif
1126 }
1127 
addAndRemoveWindows()1128 void tst_QMdiArea::addAndRemoveWindows()
1129 {
1130     QWidget topLevel;
1131     QMdiArea workspace(&topLevel);
1132     workspace.resize(800, 600);
1133     topLevel.show();
1134 #ifdef Q_WS_X11
1135     qt_x11_wait_for_window_manager(&workspace);
1136 #endif
1137 
1138     { // addSubWindow with large widget
1139     QCOMPARE(workspace.subWindowList().count(), 0);
1140     QWidget *window = workspace.addSubWindow(new LargeWidget);
1141     QVERIFY(window);
1142     qApp->processEvents();
1143     QCOMPARE(workspace.subWindowList().count(), 1);
1144     QVERIFY(window->windowFlags() == DefaultWindowFlags);
1145     QCOMPARE(window->size(), workspace.viewport()->size());
1146     }
1147 
1148     { // addSubWindow, minimumSize set.
1149     QMdiSubWindow *window = new QMdiSubWindow;
1150     window->setMinimumSize(900, 900);
1151     workspace.addSubWindow(window);
1152     QVERIFY(window);
1153     qApp->processEvents();
1154     QCOMPARE(workspace.subWindowList().count(), 2);
1155     QVERIFY(window->windowFlags() == DefaultWindowFlags);
1156     QCOMPARE(window->size(), window->minimumSize());
1157     }
1158 
1159     { // addSubWindow, resized
1160     QMdiSubWindow *window = new QMdiSubWindow;
1161     window->setWidget(new QWidget);
1162     window->resize(1500, 1500);
1163     workspace.addSubWindow(window);
1164     QVERIFY(window);
1165     qApp->processEvents();
1166     QCOMPARE(workspace.subWindowList().count(), 3);
1167     QVERIFY(window->windowFlags() == DefaultWindowFlags);
1168     QCOMPARE(window->size(), QSize(1500, 1500));
1169     }
1170 
1171     { // addSubWindow with 0 pointer
1172     QTest::ignoreMessage(QtWarningMsg, "QMdiArea::addSubWindow: null pointer to widget");
1173     QWidget *window = workspace.addSubWindow(0);
1174     QVERIFY(!window);
1175     QCOMPARE(workspace.subWindowList().count(), 3);
1176     }
1177 
1178     { // addChildWindow
1179     QMdiSubWindow *window = new QMdiSubWindow;
1180     workspace.addSubWindow(window);
1181     qApp->processEvents();
1182     QVERIFY(window->windowFlags() == DefaultWindowFlags);
1183     window->setWidget(new QWidget);
1184     QCOMPARE(workspace.subWindowList().count(), 4);
1185     QTest::ignoreMessage(QtWarningMsg, "QMdiArea::addSubWindow: window is already added");
1186     workspace.addSubWindow(window);
1187     }
1188 
1189     { // addChildWindow with 0 pointer
1190     QTest::ignoreMessage(QtWarningMsg, "QMdiArea::addSubWindow: null pointer to widget");
1191     workspace.addSubWindow(0);
1192     QCOMPARE(workspace.subWindowList().count(), 4);
1193     }
1194 
1195     // removeSubWindow
1196     foreach (QWidget *window, workspace.subWindowList()) {
1197         workspace.removeSubWindow(window);
1198         delete window;
1199     }
1200     QCOMPARE(workspace.subWindowList().count(), 0);
1201 
1202     // removeSubWindow with 0 pointer
1203     QTest::ignoreMessage(QtWarningMsg, "QMdiArea::removeSubWindow: null pointer to widget");
1204     workspace.removeSubWindow(0);
1205 
1206     workspace.addSubWindow(new QPushButton(QLatin1String("Dummy to make workspace non-empty")));
1207     qApp->processEvents();
1208     QCOMPARE(workspace.subWindowList().count(), 1);
1209 
1210     // removeSubWindow with window not inside workspace
1211     QTest::ignoreMessage(QtWarningMsg,"QMdiArea::removeSubWindow: window is not inside workspace");
1212     QMdiSubWindow *fakeWindow = new QMdiSubWindow;
1213     workspace.removeSubWindow(fakeWindow);
1214     delete fakeWindow;
1215 
1216     // Check that newly added windows don't occupy maximized windows'
1217     // restore space.
1218     workspace.closeAllSubWindows();
1219     workspace.setOption(QMdiArea::DontMaximizeSubWindowOnActivation);
1220     workspace.show();
1221     QMdiSubWindow *window1 = workspace.addSubWindow(new QWidget);
1222     window1->show();
1223     const QRect window1RestoreGeometry = window1->geometry();
1224     QCOMPARE(window1RestoreGeometry.topLeft(), QPoint(0, 0));
1225 
1226     window1->showMinimized();
1227 
1228     // Occupy space.
1229     QMdiSubWindow *window2 = workspace.addSubWindow(new QWidget);
1230     window2->show();
1231     const QRect window2RestoreGeometry = window2->geometry();
1232     QCOMPARE(window2RestoreGeometry.topLeft(), QPoint(0, 0));
1233 
1234     window2->showMaximized();
1235 
1236     // Don't occupy space.
1237     QMdiSubWindow *window3 = workspace.addSubWindow(new QWidget);
1238     window3->show();
1239     QCOMPARE(window3->geometry().topLeft(), QPoint(window2RestoreGeometry.right() + 1, 0));
1240 }
1241 
addAndRemoveWindowsWithReparenting()1242 void tst_QMdiArea::addAndRemoveWindowsWithReparenting()
1243 {
1244     QMdiArea workspace;
1245     QMdiSubWindow window(&workspace);
1246     QVERIFY(window.windowFlags() == DefaultWindowFlags);
1247 
1248     // 0 because the window list contains widgets and not actual
1249     // windows. Silly, but that's the behavior.
1250     QCOMPARE(workspace.subWindowList().count(), 0);
1251     window.setWidget(new QWidget);
1252     qApp->processEvents();
1253 
1254     QCOMPARE(workspace.subWindowList().count(), 1);
1255     window.setParent(0); // Will also reset window flags
1256     QCOMPARE(workspace.subWindowList().count(), 0);
1257     window.setParent(&workspace);
1258     QCOMPARE(workspace.subWindowList().count(), 1);
1259     QVERIFY(window.windowFlags() == DefaultWindowFlags);
1260 
1261     QTest::ignoreMessage(QtWarningMsg, "QMdiArea::addSubWindow: window is already added");
1262     workspace.addSubWindow(&window);
1263     QCOMPARE(workspace.subWindowList().count(), 1);
1264 }
1265 
1266 class MySubWindow : public QMdiSubWindow
1267 {
1268 public:
1269     using QObject::receivers;
1270 };
1271 
numberOfConnectedSignals(MySubWindow * subWindow)1272 static int numberOfConnectedSignals(MySubWindow *subWindow)
1273 {
1274     if (!subWindow)
1275         return 0;
1276 
1277     int numConnectedSignals = 0;
1278     for (int i = 0; i < subWindow->metaObject()->methodCount(); ++i) {
1279         QMetaMethod method = subWindow->metaObject()->method(i);
1280         if (method.methodType() == QMetaMethod::Signal) {
1281             QString signature(QLatin1String("2"));
1282             signature += QLatin1String(method.signature());
1283             numConnectedSignals += subWindow->receivers(signature.toLatin1());
1284         }
1285     }
1286     return numConnectedSignals;
1287 }
1288 
removeSubWindow_2()1289 void tst_QMdiArea::removeSubWindow_2()
1290 {
1291     QMdiArea mdiArea;
1292     MySubWindow *subWindow = new MySubWindow;
1293     QCOMPARE(numberOfConnectedSignals(subWindow), 0);
1294 
1295     // Connected to aboutToActivate() and windowStateChanged().
1296     mdiArea.addSubWindow(subWindow);
1297     QVERIFY(numberOfConnectedSignals(subWindow) >= 2);
1298 
1299     // Ensure we disconnect from all signals.
1300     mdiArea.removeSubWindow(subWindow);
1301     QCOMPARE(numberOfConnectedSignals(subWindow), 0);
1302 
1303     mdiArea.addSubWindow(subWindow);
1304     QVERIFY(numberOfConnectedSignals(subWindow) >= 2);
1305     subWindow->setParent(0);
1306     QCOMPARE(numberOfConnectedSignals(subWindow), 0);
1307 }
1308 
closeWindows()1309 void tst_QMdiArea::closeWindows()
1310 {
1311     QMdiArea workspace;
1312     workspace.show();
1313     qApp->setActiveWindow(&workspace);
1314 
1315     // Close widget
1316     QWidget *widget = new QWidget;
1317     QMdiSubWindow *subWindow = workspace.addSubWindow(widget);
1318     qApp->processEvents();
1319     QCOMPARE(workspace.subWindowList().count(), 1);
1320     subWindow->close();
1321     QCOMPARE(workspace.subWindowList().count(), 0);
1322 
1323     // Close window
1324     QWidget *window = workspace.addSubWindow(new QWidget);
1325     qApp->processEvents();
1326     QCOMPARE(workspace.subWindowList().count(), 1);
1327     window->close();
1328     qApp->processEvents();
1329     QCOMPARE(workspace.subWindowList().count(), 0);
1330 
1331     const int windowCount = 10;
1332 
1333     // Close active window
1334     for (int i = 0; i < windowCount; ++i)
1335         workspace.addSubWindow(new QWidget)->show();
1336     qApp->processEvents();
1337     QCOMPARE(workspace.subWindowList().count(), windowCount);
1338     int activeSubWindowCount = 0;
1339     while (workspace.activeSubWindow()) {
1340         workspace.activeSubWindow()->close();
1341         qApp->processEvents();
1342         ++activeSubWindowCount;
1343     }
1344     QCOMPARE(activeSubWindowCount, windowCount);
1345     QCOMPARE(workspace.subWindowList().count(), 0);
1346 
1347     // Close all windows
1348     for (int i = 0; i < windowCount; ++i)
1349         workspace.addSubWindow(new QWidget)->show();
1350     qApp->processEvents();
1351     QCOMPARE(workspace.subWindowList().count(), windowCount);
1352     QSignalSpy spy(&workspace, SIGNAL(subWindowActivated(QMdiSubWindow *)));
1353     connect(&workspace, SIGNAL(subWindowActivated(QMdiSubWindow *)), this, SLOT(activeChanged(QMdiSubWindow *)));
1354     workspace.closeAllSubWindows();
1355     qApp->processEvents();
1356     QCOMPARE(workspace.subWindowList().count(), 0);
1357     QCOMPARE(spy.count(), 1);
1358     QVERIFY(!activeWindow);
1359 }
1360 
activateNextAndPreviousWindow()1361 void tst_QMdiArea::activateNextAndPreviousWindow()
1362 {
1363     QMdiArea workspace;
1364     workspace.show();
1365     qApp->setActiveWindow(&workspace);
1366 
1367     const int windowCount = 10;
1368     QMdiSubWindow *windows[windowCount];
1369     for (int i = 0; i < windowCount; ++i) {
1370         windows[i] = qobject_cast<QMdiSubWindow *>(workspace.addSubWindow(new QWidget));
1371         windows[i]->show();
1372         qApp->processEvents();
1373     }
1374 
1375     QSignalSpy spy(&workspace, SIGNAL(subWindowActivated(QMdiSubWindow *)));
1376     connect(&workspace, SIGNAL(subWindowActivated(QMdiSubWindow *)), this, SLOT(activeChanged(QMdiSubWindow *)));
1377 
1378     // activateNextSubWindow
1379     for (int i = 0; i < windowCount; ++i) {
1380         workspace.activateNextSubWindow();
1381         qApp->processEvents();
1382         QCOMPARE(workspace.activeSubWindow(), windows[i]);
1383         QCOMPARE(spy.count(), 1);
1384         spy.clear();
1385     }
1386     QVERIFY(activeWindow);
1387     QCOMPARE(workspace.activeSubWindow(), windows[windowCount - 1]);
1388     QCOMPARE(workspace.activeSubWindow(), activeWindow);
1389 
1390     // activatePreviousSubWindow
1391     for (int i = windowCount - 2; i >= 0; --i) {
1392         workspace.activatePreviousSubWindow();
1393         qApp->processEvents();
1394         QCOMPARE(workspace.activeSubWindow(), windows[i]);
1395         QCOMPARE(spy.count(), 1);
1396         spy.clear();
1397         if (i % 2 == 0)
1398             windows[i]->hide(); // 10, 8, 6, 4, 2, 0
1399     }
1400     QVERIFY(activeWindow);
1401     QCOMPARE(workspace.activeSubWindow(), windows[0]);
1402     QCOMPARE(workspace.activeSubWindow(), activeWindow);
1403 
1404     // activateNextSubWindow with every 2nd window hidden
1405     for (int i = 0; i < windowCount / 2; ++i) {
1406         workspace.activateNextSubWindow(); // 1, 3, 5, 7, 9
1407         QCOMPARE(spy.count(), 1);
1408         spy.clear();
1409     }
1410     QCOMPARE(workspace.activeSubWindow(), windows[windowCount - 1]);
1411 
1412     // activatePreviousSubWindow with every 2nd window hidden
1413     for (int i = 0; i < windowCount / 2; ++i) {
1414         workspace.activatePreviousSubWindow(); // 7, 5, 3, 1, 9
1415         QCOMPARE(spy.count(), 1);
1416         spy.clear();
1417     }
1418     QCOMPARE(workspace.activeSubWindow(), windows[windowCount - 1]);
1419 
1420     workspace.setActiveSubWindow(0);
1421     QVERIFY(!activeWindow);
1422 }
1423 
subWindowList_data()1424 void tst_QMdiArea::subWindowList_data()
1425 {
1426     QTest::addColumn<QMdiArea::WindowOrder>("windowOrder");
1427     QTest::addColumn<int>("windowCount");
1428     QTest::addColumn<int>("activeSubWindow");
1429     QTest::addColumn<int>("staysOnTop1");
1430     QTest::addColumn<int>("staysOnTop2");
1431 
1432     QTest::newRow("CreationOrder") << QMdiArea::CreationOrder << 10 << 4 << 8 << 5;
1433     QTest::newRow("StackingOrder") << QMdiArea::StackingOrder << 10 << 6 << 3 << 9;
1434     QTest::newRow("ActivationHistoryOrder") << QMdiArea::ActivationHistoryOrder << 10 << 7 << 2 << 1;
1435 }
subWindowList()1436 void tst_QMdiArea::subWindowList()
1437 {
1438     QFETCH(QMdiArea::WindowOrder, windowOrder);
1439     QFETCH(int, windowCount);
1440     QFETCH(int, activeSubWindow);
1441     QFETCH(int, staysOnTop1);
1442     QFETCH(int, staysOnTop2);
1443 
1444     QMdiArea workspace;
1445     workspace.show();
1446     qApp->setActiveWindow(&workspace);
1447 
1448     QList<QMdiSubWindow *> activationOrder;
1449     QVector<QMdiSubWindow *> windows;
1450     for (int i = 0; i < windowCount; ++i) {
1451         windows.append(qobject_cast<QMdiSubWindow *>(workspace.addSubWindow(new QWidget)));
1452         windows[i]->show();
1453         activationOrder.append(windows[i]);
1454     }
1455 
1456     {
1457     QList<QMdiSubWindow *> widgets = workspace.subWindowList(windowOrder);
1458     QCOMPARE(widgets.count(), windowCount);
1459     for (int i = 0; i < widgets.count(); ++i)
1460         QCOMPARE(widgets.at(i), windows[i]);
1461     }
1462 
1463     windows[staysOnTop1]->setWindowFlags(windows[staysOnTop1]->windowFlags() | Qt::WindowStaysOnTopHint);
1464     workspace.setActiveSubWindow(windows[activeSubWindow]);
1465     qApp->processEvents();
1466     QCOMPARE(workspace.activeSubWindow(), windows[activeSubWindow]);
1467     activationOrder.move(activationOrder.indexOf(windows[activeSubWindow]), windowCount - 1);
1468 
1469     QList<QMdiSubWindow *> subWindows = workspace.subWindowList(windowOrder);
1470     if (windowOrder == QMdiArea::CreationOrder) {
1471         QCOMPARE(subWindows.at(activeSubWindow), windows[activeSubWindow]);
1472         QCOMPARE(subWindows.at(staysOnTop1), windows[staysOnTop1]);
1473         for (int i = 0; i < windowCount; ++i)
1474             QCOMPARE(subWindows.at(i), windows[i]);
1475         return;
1476     }
1477 
1478     if (windowOrder == QMdiArea::StackingOrder) {
1479         QCOMPARE(subWindows.at(subWindows.count() - 1), windows[staysOnTop1]);
1480         QCOMPARE(subWindows.at(subWindows.count() - 2), windows[activeSubWindow]);
1481         QCOMPARE(subWindows.count(), windowCount);
1482     } else { // ActivationHistoryOrder
1483         QCOMPARE(subWindows, activationOrder);
1484     }
1485 
1486     windows[staysOnTop2]->setWindowFlags(windows[staysOnTop2]->windowFlags() | Qt::WindowStaysOnTopHint);
1487     workspace.setActiveSubWindow(windows[staysOnTop2]);
1488     qApp->processEvents();
1489     QCOMPARE(workspace.activeSubWindow(), windows[staysOnTop2]);
1490     activationOrder.move(activationOrder.indexOf(windows[staysOnTop2]), windowCount - 1);
1491 
1492     workspace.setActiveSubWindow(windows[activeSubWindow]);
1493     qApp->processEvents();
1494     QCOMPARE(workspace.activeSubWindow(), windows[activeSubWindow]);
1495     activationOrder.move(activationOrder.indexOf(windows[activeSubWindow]), windowCount - 1);
1496 
1497     QList<QMdiSubWindow *> widgets = workspace.subWindowList(windowOrder);
1498     QCOMPARE(widgets.count(), windowCount);
1499     if (windowOrder == QMdiArea::StackingOrder) {
1500         QCOMPARE(widgets.at(widgets.count() - 1), windows[staysOnTop2]);
1501         QCOMPARE(widgets.at(widgets.count() - 2), windows[staysOnTop1]);
1502         QCOMPARE(widgets.at(widgets.count() - 3), windows[activeSubWindow]);
1503     } else { // ActivationHistory
1504         QCOMPARE(widgets, activationOrder);
1505     }
1506 
1507     windows[activeSubWindow]->raise();
1508     windows[staysOnTop2]->lower();
1509 
1510     widgets = workspace.subWindowList(windowOrder);
1511     if (windowOrder == QMdiArea::StackingOrder) {
1512         QCOMPARE(widgets.at(widgets.count() - 1), windows[activeSubWindow]);
1513         QCOMPARE(widgets.at(widgets.count() - 2), windows[staysOnTop1]);
1514         QCOMPARE(widgets.at(0), windows[staysOnTop2]);
1515     } else { // ActivationHistoryOrder
1516         QCOMPARE(widgets, activationOrder);
1517     }
1518 
1519     windows[activeSubWindow]->stackUnder(windows[staysOnTop1]);
1520     windows[staysOnTop2]->raise();
1521 
1522     widgets = workspace.subWindowList(windowOrder);
1523     if (windowOrder == QMdiArea::StackingOrder) {
1524         QCOMPARE(widgets.at(widgets.count() - 1), windows[staysOnTop2]);
1525         QCOMPARE(widgets.at(widgets.count() - 2), windows[staysOnTop1]);
1526         QCOMPARE(widgets.at(widgets.count() - 3), windows[activeSubWindow]);
1527     } else { // ActivationHistoryOrder
1528         QCOMPARE(widgets, activationOrder);
1529     }
1530 
1531     workspace.setActiveSubWindow(windows[staysOnTop1]);
1532     activationOrder.move(activationOrder.indexOf(windows[staysOnTop1]), windowCount - 1);
1533 
1534     widgets = workspace.subWindowList(windowOrder);
1535     if (windowOrder == QMdiArea::StackingOrder) {
1536         QCOMPARE(widgets.at(widgets.count() - 1), windows[staysOnTop1]);
1537         QCOMPARE(widgets.at(widgets.count() - 2), windows[staysOnTop2]);
1538         QCOMPARE(widgets.at(widgets.count() - 3), windows[activeSubWindow]);
1539     } else { // ActivationHistoryOrder
1540         QCOMPARE(widgets, activationOrder);
1541     }
1542 }
1543 
setBackground()1544 void tst_QMdiArea::setBackground()
1545 {
1546     QMdiArea workspace;
1547     QCOMPARE(workspace.background(), workspace.palette().brush(QPalette::Dark));
1548     workspace.setBackground(QBrush(Qt::green));
1549     QCOMPARE(workspace.background(), QBrush(Qt::green));
1550 }
1551 
setViewport()1552 void tst_QMdiArea::setViewport()
1553 {
1554     QMdiArea workspace;
1555     workspace.show();
1556 
1557     QWidget *firstViewport = workspace.viewport();
1558     QVERIFY(firstViewport);
1559 
1560     const int windowCount = 10;
1561     for (int i = 0; i < windowCount; ++i) {
1562         QMdiSubWindow *window = workspace.addSubWindow(new QWidget);
1563         window->show();
1564         if (i % 2 == 0) {
1565             window->showMinimized();
1566             QVERIFY(window->isMinimized());
1567         } else {
1568             window->showMaximized();
1569             QVERIFY(window->isMaximized());
1570         }
1571     }
1572 
1573     qApp->processEvents();
1574     QList<QMdiSubWindow *> windowsBeforeViewportChange = workspace.subWindowList();
1575     QCOMPARE(windowsBeforeViewportChange.count(), windowCount);
1576 
1577     workspace.setViewport(new QWidget);
1578     qApp->processEvents();
1579     QVERIFY(workspace.viewport() != firstViewport);
1580 
1581     QList<QMdiSubWindow *> windowsAfterViewportChange = workspace.subWindowList();
1582     QCOMPARE(windowsAfterViewportChange.count(), windowCount);
1583     QCOMPARE(windowsAfterViewportChange, windowsBeforeViewportChange);
1584 
1585     //    for (int i = 0; i < windowCount; ++i) {
1586     //        QMdiSubWindow *window = windowsAfterViewportChange.at(i);
1587     //        if (i % 2 == 0)
1588     //            QVERIFY(!window->isMinimized());
1589     //else
1590     //    QVERIFY(!window->isMaximized());
1591     //    }
1592 
1593     QTest::ignoreMessage(QtWarningMsg, "QMdiArea: Deleting the view port is undefined, "
1594                                        "use setViewport instead.");
1595     delete workspace.viewport();
1596     qApp->processEvents();
1597 
1598     QCOMPARE(workspace.subWindowList().count(), 0);
1599     QVERIFY(!workspace.activeSubWindow());
1600 }
1601 
tileSubWindows()1602 void tst_QMdiArea::tileSubWindows()
1603 {
1604     QMdiArea workspace;
1605     workspace.resize(600,480);
1606     if (PlatformQuirks::isAutoMaximizing())
1607         workspace.setWindowFlags(workspace.windowFlags() | Qt::X11BypassWindowManagerHint);
1608     workspace.show();
1609 #ifdef Q_WS_X11
1610     qt_x11_wait_for_window_manager(&workspace);
1611 #endif
1612 
1613     const int windowCount = 10;
1614     for (int i = 0; i < windowCount; ++i) {
1615         QMdiSubWindow *subWindow = workspace.addSubWindow(new QWidget);
1616         subWindow->setMinimumSize(50, 30);
1617         subWindow->show();
1618     }
1619     workspace.tileSubWindows();
1620     workspace.setActiveSubWindow(0);
1621     QCOMPARE(workspace.viewport()->childrenRect(), workspace.viewport()->rect());
1622 
1623     QList<QMdiSubWindow *> windows = workspace.subWindowList();
1624     for (int i = 0; i < windowCount; ++i) {
1625         QMdiSubWindow *window = windows.at(i);
1626         for (int j = 0; j < windowCount; ++j) {
1627             if (i == j)
1628                 continue;
1629             QVERIFY(!window->geometry().intersects(windows.at(j)->geometry()));
1630         }
1631     }
1632 
1633     // Keep the views tiled through any subsequent resize events.
1634     for (int i = 0; i < 5; ++i) {
1635         workspace.resize(workspace.size() - QSize(10, 10));
1636         qApp->processEvents();
1637     }
1638     workspace.setActiveSubWindow(0);
1639 #ifndef Q_OS_WINCE //See Task 197453 ToDo
1640     QCOMPARE(workspace.viewport()->childrenRect(), workspace.viewport()->rect());
1641 #endif
1642 
1643     QMdiSubWindow *window = windows.at(0);
1644 
1645     // Change the geometry of one of the children and verify
1646     // that the views are not tiled anymore.
1647     window->move(window->x() + 1, window->y());
1648     workspace.resize(workspace.size() - QSize(10, 10));
1649     workspace.setActiveSubWindow(0);
1650     QVERIFY(workspace.viewport()->childrenRect() != workspace.viewport()->rect());
1651     qApp->processEvents();
1652 
1653     // Re-tile.
1654     workspace.tileSubWindows();
1655     workspace.setActiveSubWindow(0);
1656     QCOMPARE(workspace.viewport()->childrenRect(), workspace.viewport()->rect());
1657 
1658     // Close one of the children and verify that the views
1659     // are not tiled anymore.
1660     window->close();
1661     workspace.resize(workspace.size() - QSize(10, 10));
1662     workspace.setActiveSubWindow(0);
1663     QVERIFY(workspace.viewport()->childrenRect() != workspace.viewport()->rect());
1664     qApp->processEvents();
1665 
1666     // Re-tile.
1667     workspace.tileSubWindows();
1668     workspace.setActiveSubWindow(0);
1669     QCOMPARE(workspace.viewport()->childrenRect(), workspace.viewport()->rect());
1670 
1671     window = windows.at(1);
1672 
1673     // Maximize one of the children and verify that the views
1674     // are not tiled anymore.
1675     workspace.tileSubWindows();
1676     window->showMaximized();
1677     workspace.resize(workspace.size() - QSize(10, 10));
1678     workspace.setActiveSubWindow(0);
1679     QVERIFY(workspace.viewport()->childrenRect() != workspace.viewport()->rect());
1680     qApp->processEvents();
1681 
1682     // Re-tile.
1683     workspace.tileSubWindows();
1684     workspace.setActiveSubWindow(0);
1685     QCOMPARE(workspace.viewport()->childrenRect(), workspace.viewport()->rect());
1686 
1687     // Minimize one of the children and verify that the views
1688     // are not tiled anymore.
1689     workspace.tileSubWindows();
1690     window->showMinimized();
1691     workspace.resize(workspace.size() - QSize(10, 10));
1692     workspace.setActiveSubWindow(0);
1693     QVERIFY(workspace.viewport()->childrenRect() != workspace.viewport()->rect());
1694     qApp->processEvents();
1695 
1696     // Re-tile.
1697     workspace.tileSubWindows();
1698     workspace.setActiveSubWindow(0);
1699     QCOMPARE(workspace.viewport()->childrenRect(), workspace.viewport()->rect());
1700 
1701     // Active/deactivate windows and verify that the views are tiled.
1702     workspace.setActiveSubWindow(windows.at(5));
1703     workspace.resize(workspace.size() - QSize(10, 10));
1704     workspace.setActiveSubWindow(0);
1705     QTest::qWait(250); // delayed re-arrange of minimized windows
1706     QTRY_COMPARE(workspace.viewport()->childrenRect(), workspace.viewport()->rect());
1707 
1708     // Add another window and verify that the views are not tiled anymore.
1709     workspace.addSubWindow(new QPushButton(QLatin1String("I'd like to mess up tiled views")))->show();
1710     workspace.resize(workspace.size() - QSize(10, 10));
1711     workspace.setActiveSubWindow(0);
1712     QVERIFY(workspace.viewport()->childrenRect() != workspace.viewport()->rect());
1713 
1714     // Re-tile.
1715     workspace.tileSubWindows();
1716     workspace.setActiveSubWindow(0);
1717     QCOMPARE(workspace.viewport()->childrenRect(), workspace.viewport()->rect());
1718 
1719     // Cascade and verify that the views are not tiled anymore.
1720     workspace.cascadeSubWindows();
1721     workspace.resize(workspace.size() - QSize(10, 10));
1722     workspace.setActiveSubWindow(0);
1723     QVERIFY(workspace.viewport()->childrenRect() != workspace.viewport()->rect());
1724 
1725     // Make sure the active window is placed in top left corner regardless
1726     // of whether we have any windows with staysOnTopHint or not.
1727     windows.at(3)->setWindowFlags(windows.at(3)->windowFlags() | Qt::WindowStaysOnTopHint);
1728     QMdiSubWindow *activeSubWindow = windows.at(6);
1729     workspace.setActiveSubWindow(activeSubWindow);
1730     QCOMPARE(workspace.activeSubWindow(), activeSubWindow);
1731     workspace.tileSubWindows();
1732     QCOMPARE(activeSubWindow->geometry().topLeft(), QPoint(0, 0));
1733 
1734     // Verify that we try to resize the area such that all sub-windows are visible.
1735     // It's important that tiled windows are NOT overlapping.
1736     workspace.resize(350, 150);
1737     qApp->processEvents();
1738     QTRY_COMPARE(workspace.size(), QSize(350, 150));
1739 
1740     const QSize minSize(300, 100);
1741     foreach (QMdiSubWindow *subWindow, workspace.subWindowList())
1742         subWindow->setMinimumSize(minSize);
1743 
1744     QCOMPARE(workspace.size(), QSize(350, 150));
1745     workspace.tileSubWindows();
1746     // The sub-windows are now tiled like this:
1747     // | win 1 || win 2 || win 3 |
1748     // +-------++-------++-------+
1749     // +-------++-------++-------+
1750     // | win 4 || win 5 || win 6 |
1751     // +-------++-------++-------+
1752     // +-------++-------++-------+
1753     // | win 7 || win 8 || win 9 |
1754     workspace.setActiveSubWindow(0);
1755     int frameWidth = 0;
1756     if (workspace.style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents, 0, &workspace))
1757         frameWidth = workspace.style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
1758     const int spacing = 2 * frameWidth + 2;
1759     const QSize expectedViewportSize(3 * minSize.width() + spacing, 3 * minSize.height() + spacing);
1760 #ifdef Q_OS_WINCE
1761     QSKIP("Not fixed yet! See task 197453", SkipAll);
1762 #endif
1763     QTRY_COMPARE(workspace.viewport()->rect().size(), expectedViewportSize);
1764 
1765     // Not enough space for all sub-windows to be visible -> provide scroll bars.
1766     workspace.resize(160, 150);
1767     qApp->processEvents();
1768     QTRY_COMPARE(workspace.size(), QSize(160, 150));
1769 
1770     // Horizontal scroll bar.
1771     QScrollBar *hBar = workspace.horizontalScrollBar();
1772     QCOMPARE(workspace.horizontalScrollBarPolicy(), Qt::ScrollBarAsNeeded);
1773     QTRY_VERIFY(hBar->isVisible());
1774     QCOMPARE(hBar->value(), 0);
1775     QCOMPARE(hBar->minimum(), 0);
1776 
1777     // Vertical scroll bar.
1778     QScrollBar *vBar = workspace.verticalScrollBar();
1779     QCOMPARE(workspace.verticalScrollBarPolicy(), Qt::ScrollBarAsNeeded);
1780     QVERIFY(vBar->isVisible());
1781     QCOMPARE(vBar->value(), 0);
1782     QCOMPARE(vBar->minimum(), 0);
1783 
1784     workspace.tileSubWindows();
1785 #ifdef Q_WS_X11
1786     qt_x11_wait_for_window_manager(&workspace);
1787 #endif
1788     qApp->processEvents();
1789 
1790     QTRY_VERIFY(workspace.size() != QSize(150, 150));
1791     QTRY_VERIFY(!vBar->isVisible());
1792 #if defined(UBUNTU_LUCID) && !defined(Q_WS_QWS)
1793     QEXPECT_FAIL("", "QTBUG-26726", Abort);
1794 #endif
1795     QTRY_VERIFY(!hBar->isVisible());
1796 }
1797 
cascadeAndTileSubWindows()1798 void tst_QMdiArea::cascadeAndTileSubWindows()
1799 {
1800     QMdiArea workspace;
1801     workspace.resize(400, 400);
1802     workspace.show();
1803 #ifdef Q_WS_X11
1804     qt_x11_wait_for_window_manager(&workspace);
1805 #endif
1806 
1807     const int windowCount = 10;
1808     QList<QMdiSubWindow *> windows;
1809     for (int i = 0; i < windowCount; ++i) {
1810         QMdiSubWindow *window = workspace.addSubWindow(new MyChild);
1811         if (i % 3 == 0) {
1812             window->showMinimized();
1813             QVERIFY(window->isMinimized());
1814         } else {
1815             window->showMaximized();
1816             QVERIFY(window->isMaximized());
1817         }
1818         windows.append(window);
1819     }
1820 
1821     // cascadeSubWindows
1822     qApp->processEvents();
1823     workspace.cascadeSubWindows();
1824     qApp->processEvents();
1825 
1826     // Check dy between two cascaded windows
1827     QStyleOptionTitleBar options;
1828     options.initFrom(windows.at(1));
1829     int titleBarHeight = windows.at(1)->style()->pixelMetric(QStyle::PM_TitleBarHeight, &options);
1830     // ### Remove this after the mac style has been fixed
1831     if (windows.at(1)->style()->inherits("QMacStyle"))
1832         titleBarHeight -= 4;
1833     const QFontMetrics fontMetrics = QFontMetrics(QApplication::font("QWorkspaceTitleBar"));
1834     const int dy = qMax(titleBarHeight - (titleBarHeight - fontMetrics.height()) / 2, 1);
1835     QCOMPARE(windows.at(2)->geometry().top() - windows.at(1)->geometry().top(), dy);
1836 
1837     for (int i = 0; i < windows.count(); ++i) {
1838         QMdiSubWindow *window = windows.at(i);
1839         if (i % 3 == 0) {
1840             QVERIFY(window->isMinimized());
1841         } else {
1842             QVERIFY(!window->isMaximized());
1843             QCOMPARE(window->size(), window->sizeHint());
1844             window->showMaximized();
1845             QVERIFY(window->isMaximized());
1846         }
1847     }
1848 }
1849 
resizeMaximizedChildWindows_data()1850 void tst_QMdiArea::resizeMaximizedChildWindows_data()
1851 {
1852     QTest::addColumn<int>("startSize");
1853     QTest::addColumn<int>("increment");
1854     QTest::addColumn<int>("windowCount");
1855 
1856     QTest::newRow("multiple children") << 400 << 20 << 10;
1857 }
1858 
resizeMaximizedChildWindows()1859 void tst_QMdiArea::resizeMaximizedChildWindows()
1860 {
1861     QFETCH(int, startSize);
1862     QFETCH(int, increment);
1863     QFETCH(int, windowCount);
1864 
1865     QWidget topLevel;
1866     QMdiArea workspace(&topLevel);
1867     topLevel.show();
1868 #if defined(Q_WS_X11)
1869     qt_x11_wait_for_window_manager(&workspace);
1870 #endif
1871     QTest::qWait(100);
1872     workspace.resize(startSize, startSize);
1873     workspace.setOption(QMdiArea::DontMaximizeSubWindowOnActivation);
1874     QSize workspaceSize = workspace.size();
1875     QVERIFY(workspaceSize.isValid());
1876     QCOMPARE(workspaceSize, QSize(startSize, startSize));
1877 
1878     QList<QMdiSubWindow *> windows;
1879     for (int i = 0; i < windowCount; ++i) {
1880         QMdiSubWindow *window = workspace.addSubWindow(new QWidget);
1881         windows.append(window);
1882         qApp->processEvents();
1883         window->showMaximized();
1884         QTest::qWait(100);
1885         QVERIFY(window->isMaximized());
1886         QSize windowSize = window->size();
1887         QVERIFY(windowSize.isValid());
1888         QCOMPARE(window->rect(), workspace.contentsRect());
1889 
1890         workspace.resize(workspaceSize + QSize(increment, increment));
1891         QTest::qWait(100);
1892         qApp->processEvents();
1893         QTRY_COMPARE(workspace.size(), workspaceSize + QSize(increment, increment));
1894         QTRY_COMPARE(window->size(), windowSize + QSize(increment, increment));
1895         workspaceSize = workspace.size();
1896     }
1897 
1898     int newSize = startSize + increment * windowCount;
1899     QCOMPARE(workspaceSize, QSize(newSize, newSize));
1900     foreach (QWidget *window, windows)
1901         QCOMPARE(window->rect(), workspace.contentsRect());
1902 }
1903 
1904 // QWidget::setParent clears focusWidget so make sure
1905 // we restore it after QMdiArea::addSubWindow.
focusWidgetAfterAddSubWindow()1906 void tst_QMdiArea::focusWidgetAfterAddSubWindow()
1907 {
1908     QWidget *view = new QWidget;
1909     view->setLayout(new QVBoxLayout);
1910 
1911     QLineEdit *lineEdit1 = new QLineEdit;
1912     QLineEdit *lineEdit2 = new QLineEdit;
1913     view->layout()->addWidget(lineEdit1);
1914     view->layout()->addWidget(lineEdit2);
1915 
1916     lineEdit2->setFocus();
1917     QCOMPARE(view->focusWidget(), static_cast<QWidget *>(lineEdit2));
1918 
1919     QMdiArea mdiArea;
1920     mdiArea.addSubWindow(view);
1921     QCOMPARE(view->focusWidget(), static_cast<QWidget *>(lineEdit2));
1922 
1923     mdiArea.show();
1924     view->show();
1925     qApp->setActiveWindow(&mdiArea);
1926     QCOMPARE(qApp->focusWidget(), static_cast<QWidget *>(lineEdit2));
1927 }
1928 
dontMaximizeSubWindowOnActivation()1929 void tst_QMdiArea::dontMaximizeSubWindowOnActivation()
1930 {
1931     QMdiArea mdiArea;
1932     mdiArea.show();
1933 #ifdef Q_WS_X11
1934     qt_x11_wait_for_window_manager(&mdiArea);
1935 #endif
1936     qApp->setActiveWindow(&mdiArea);
1937 
1938     // Add one maximized window.
1939     mdiArea.addSubWindow(new QWidget)->showMaximized();
1940     QVERIFY(mdiArea.activeSubWindow());
1941     QVERIFY(mdiArea.activeSubWindow()->isMaximized());
1942 
1943     // Add few more windows and verify that they are maximized.
1944     for (int i = 0; i < 5; ++i) {
1945         QMdiSubWindow *window = mdiArea.addSubWindow(new QWidget);
1946         window->show();
1947         QVERIFY(window->isMaximized());
1948         qApp->processEvents();
1949     }
1950 
1951     // Verify that activated windows still are maximized on activation.
1952     QList<QMdiSubWindow *> subWindows = mdiArea.subWindowList();
1953     for (int i = 0; i < subWindows.count(); ++i) {
1954         mdiArea.activateNextSubWindow();
1955         QMdiSubWindow *window = subWindows.at(i);
1956         QCOMPARE(mdiArea.activeSubWindow(), window);
1957         QVERIFY(window->isMaximized());
1958         qApp->processEvents();
1959     }
1960 
1961     // Restore active window and verify that other windows aren't
1962     // maximized on activation.
1963     mdiArea.activeSubWindow()->showNormal();
1964     for (int i = 0; i < subWindows.count(); ++i) {
1965         mdiArea.activateNextSubWindow();
1966         QMdiSubWindow *window = subWindows.at(i);
1967         QCOMPARE(mdiArea.activeSubWindow(), window);
1968         QVERIFY(!window->isMaximized());
1969         qApp->processEvents();
1970     }
1971 
1972     // Enable 'DontMaximizedSubWindowOnActivation' and maximize the active window.
1973     mdiArea.setOption(QMdiArea::DontMaximizeSubWindowOnActivation);
1974     mdiArea.activeSubWindow()->showMaximized();
1975     int indexOfMaximized = subWindows.indexOf(mdiArea.activeSubWindow());
1976 
1977     // Verify that windows are not maximized on activation.
1978     for (int i = 0; i < subWindows.count(); ++i) {
1979         mdiArea.activateNextSubWindow();
1980         QMdiSubWindow *window = subWindows.at(i);
1981         QCOMPARE(mdiArea.activeSubWindow(), window);
1982         if (indexOfMaximized != i)
1983             QVERIFY(!window->isMaximized());
1984         qApp->processEvents();
1985     }
1986     QVERIFY(mdiArea.activeSubWindow()->isMaximized());
1987 
1988     // Minimize all windows.
1989     foreach (QMdiSubWindow *window, subWindows) {
1990         window->showMinimized();
1991         QVERIFY(window->isMinimized());
1992         qApp->processEvents();
1993     }
1994 
1995     // Disable 'DontMaximizedSubWindowOnActivation' and maximize the active window.
1996     mdiArea.setOption(QMdiArea::DontMaximizeSubWindowOnActivation, false);
1997     mdiArea.activeSubWindow()->showMaximized();
1998 
1999     // Verify that minimized windows are maximized on activation.
2000     for (int i = 0; i < subWindows.count(); ++i) {
2001         mdiArea.activateNextSubWindow();
2002         QMdiSubWindow *window = subWindows.at(i);
2003         QCOMPARE(mdiArea.activeSubWindow(), window);
2004         QVERIFY(window->isMaximized());
2005         qApp->processEvents();
2006     }
2007 
2008     // Verify that activated windows are maximized after closing
2009     // the active window
2010     for (int i = 0; i < subWindows.count(); ++i) {
2011         QVERIFY(mdiArea.activeSubWindow());
2012         QVERIFY(mdiArea.activeSubWindow()->isMaximized());
2013         mdiArea.activeSubWindow()->close();
2014         qApp->processEvents();
2015     }
2016 
2017     QVERIFY(!mdiArea.activeSubWindow());
2018     QCOMPARE(mdiArea.subWindowList().size(), 0);
2019 
2020     // Verify that new windows are not maximized.
2021     mdiArea.addSubWindow(new QWidget)->show();
2022     QVERIFY(mdiArea.activeSubWindow());
2023     QVERIFY(!mdiArea.activeSubWindow()->isMaximized());
2024 }
2025 
delayedPlacement()2026 void tst_QMdiArea::delayedPlacement()
2027 {
2028     QMdiArea mdiArea;
2029 
2030     QMdiSubWindow *window1 = mdiArea.addSubWindow(new QWidget);
2031     QCOMPARE(window1->geometry().topLeft(), QPoint(0, 0));
2032 
2033     QMdiSubWindow *window2 = mdiArea.addSubWindow(new QWidget);
2034     QCOMPARE(window2->geometry().topLeft(), QPoint(0, 0));
2035 
2036     QMdiSubWindow *window3 = mdiArea.addSubWindow(new QWidget);
2037     QCOMPARE(window3->geometry().topLeft(), QPoint(0, 0));
2038 
2039     mdiArea.resize(window3->minimumSizeHint().width() * 3, 400);
2040     mdiArea.show();
2041 #ifdef Q_WS_X11
2042     qt_x11_wait_for_window_manager(&mdiArea);
2043 #endif
2044 
2045     QCOMPARE(window1->geometry().topLeft(), QPoint(0, 0));
2046     QCOMPARE(window2->geometry().topLeft(), window1->geometry().topRight() + QPoint(1, 0));
2047     QCOMPARE(window3->geometry().topLeft(), window2->geometry().topRight() + QPoint(1, 0));
2048 }
2049 
iconGeometryInMenuBar()2050 void tst_QMdiArea::iconGeometryInMenuBar()
2051 {
2052 #if !defined (Q_WS_MAC) && !defined(Q_OS_WINCE) && !defined(Q_OS_SYMBIAN)
2053     QMainWindow mainWindow;
2054     QMenuBar *menuBar = mainWindow.menuBar();
2055     QMdiArea *mdiArea = new QMdiArea;
2056     QMdiSubWindow *subWindow = mdiArea->addSubWindow(new QWidget);
2057     mainWindow.setCentralWidget(mdiArea);
2058     mainWindow.show();
2059 #ifdef Q_WS_X11
2060     qt_x11_wait_for_window_manager(&mainWindow);
2061 #endif
2062 
2063     subWindow->showMaximized();
2064     QVERIFY(subWindow->isMaximized());
2065 
2066     QWidget *leftCornerWidget = menuBar->cornerWidget(Qt::TopLeftCorner);
2067     QVERIFY(leftCornerWidget);
2068     int topMargin = (menuBar->height() - leftCornerWidget->height()) / 2;
2069     int leftMargin = qApp->style()->pixelMetric(QStyle::PM_MenuBarHMargin)
2070                    + qApp->style()->pixelMetric(QStyle::PM_MenuBarPanelWidth);
2071     QPoint pos(leftMargin, topMargin);
2072     QRect geometry = QStyle::visualRect(qApp->layoutDirection(), menuBar->rect(),
2073                                         QRect(pos, leftCornerWidget->size()));
2074     QCOMPARE(leftCornerWidget->geometry(), geometry);
2075 #endif
2076 }
2077 
2078 class EventSpy : public QObject
2079 {
2080 public:
EventSpy(QObject * object,QEvent::Type event)2081     EventSpy(QObject *object, QEvent::Type event)
2082         : eventToSpy(event), _count(0)
2083     {
2084         if (object)
2085             object->installEventFilter(this);
2086     }
2087 
count() const2088     int count() const { return _count; }
clear()2089     void clear() { _count = 0; }
2090 
2091 protected:
eventFilter(QObject * object,QEvent * event)2092     bool eventFilter(QObject *object, QEvent *event)
2093     {
2094         if (event->type() == eventToSpy)
2095             ++_count;
2096         return  QObject::eventFilter(object, event);
2097     }
2098 
2099 private:
2100     QEvent::Type eventToSpy;
2101     int _count;
2102 };
2103 
resizeTimer()2104 void tst_QMdiArea::resizeTimer()
2105 {
2106     QMdiArea mdiArea;
2107     QMdiSubWindow *subWindow = mdiArea.addSubWindow(new QWidget);
2108     mdiArea.show();
2109 #ifdef Q_WS_X11
2110     qt_x11_wait_for_window_manager(&mdiArea);
2111 #endif
2112     QTest::qWaitForWindowShown(&mdiArea);
2113 
2114 #ifndef Q_OS_WINCE
2115     int time = 250;
2116 #else
2117     int time = 1000;
2118 #endif
2119 
2120     QTest::qWait(time);
2121 
2122     EventSpy timerEventSpy(subWindow, QEvent::Timer);
2123     QCOMPARE(timerEventSpy.count(), 0);
2124 
2125     mdiArea.tileSubWindows();
2126     QTest::qWait(time); // Wait for timer events to occur.
2127     QCOMPARE(timerEventSpy.count(), 1);
2128     timerEventSpy.clear();
2129 
2130     mdiArea.resize(mdiArea.size() + QSize(2, 2));
2131     QTest::qWait(time); // Wait for timer events to occur.
2132     QCOMPARE(timerEventSpy.count(), 1);
2133     timerEventSpy.clear();
2134 
2135     // Check that timers are killed.
2136     QTest::qWait(time); // Wait for timer events to occur.
2137     QCOMPARE(timerEventSpy.count(), 0);
2138 }
2139 
updateScrollBars()2140 void tst_QMdiArea::updateScrollBars()
2141 {
2142     QMdiArea mdiArea;
2143     mdiArea.setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
2144     mdiArea.setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
2145 
2146     QMdiSubWindow *subWindow1 = mdiArea.addSubWindow(new QWidget);
2147     QMdiSubWindow *subWindow2 = mdiArea.addSubWindow(new QWidget);
2148 
2149     mdiArea.show();
2150 #ifdef Q_WS_X11
2151     qt_x11_wait_for_window_manager(&mdiArea);
2152 #endif
2153     qApp->processEvents();
2154 
2155     QScrollBar *hbar = mdiArea.horizontalScrollBar();
2156     QVERIFY(hbar);
2157     QVERIFY(!hbar->isVisible());
2158 
2159     QScrollBar *vbar = mdiArea.verticalScrollBar();
2160     QVERIFY(vbar);
2161     QVERIFY(!vbar->isVisible());
2162 
2163     // Move sub-window 2 away.
2164     subWindow2->move(10000, 10000);
2165     qApp->processEvents();
2166     QVERIFY(hbar->isVisible());
2167     QVERIFY(vbar->isVisible());
2168 
2169     for (int i = 0; i < 2; ++i) {
2170     // Maximize sub-window 1 and make sure we don't have any scroll bars.
2171     subWindow1->showMaximized();
2172     qApp->processEvents();
2173     QVERIFY(subWindow1->isMaximized());
2174     QVERIFY(!hbar->isVisible());
2175     QVERIFY(!vbar->isVisible());
2176 
2177     // We still shouldn't get any scroll bars.
2178     mdiArea.resize(mdiArea.size() - QSize(20, 20));
2179 #ifdef Q_WS_X11
2180     qt_x11_wait_for_window_manager(&mdiArea);
2181 #endif
2182     qApp->processEvents();
2183     QVERIFY(subWindow1->isMaximized());
2184     QVERIFY(!hbar->isVisible());
2185     QVERIFY(!vbar->isVisible());
2186 
2187     // Restore sub-window 1 and make sure we have scroll bars again.
2188     subWindow1->showNormal();
2189     qApp->processEvents();
2190     QVERIFY(!subWindow1->isMaximized());
2191     QVERIFY(hbar->isVisible());
2192     QVERIFY(vbar->isVisible());
2193         if (i == 0) {
2194             // Now, do the same when the viewport is scrolled.
2195             hbar->setValue(1000);
2196             vbar->setValue(1000);
2197         }
2198     }
2199 }
2200 
setActivationOrder_data()2201 void tst_QMdiArea::setActivationOrder_data()
2202 {
2203     QTest::addColumn<QMdiArea::WindowOrder>("activationOrder");
2204     QTest::addColumn<int>("subWindowCount");
2205     QTest::addColumn<int>("staysOnTopIndex");
2206     QTest::addColumn<int>("firstActiveIndex");
2207     QTest::addColumn<QList<int> >("expectedActivationIndices");
2208     // The order of expectedCascadeIndices:
2209     // window 1 -> (index 0)
2210     //   window 2 -> (index 1)
2211     //     window 3 -> (index 2)
2212     // ....
2213     QTest::addColumn<QList<int> >("expectedCascadeIndices");
2214 
2215     // The order of expectedTileIndices (the same as reading a book LTR).
2216     // +--------------------+--------------------+--------------------+
2217     // | window 1 (index 0) | window 2 (index 1) | window 3 (index 2) |
2218     // |                    +--------------------+--------------------+
2219     // |          (index 3) | window 4 (index 4) | window 5 (index 5) |
2220     // +--------------------------------------------------------------+
2221     QTest::addColumn<QList<int> >("expectedTileIndices");
2222 
2223     QList<int> list;
2224     QList<int> list2;
2225     QList<int> list3;
2226 
2227     list << 2 << 1 << 0 << 1 << 2 << 3 << 4;
2228     list2 << 0 << 1 << 2 << 3 << 4;
2229     list3 << 1 << 4 << 3 << 1 << 2 << 0;
2230     QTest::newRow("CreationOrder") << QMdiArea::CreationOrder << 5 << 3 << 1 << list << list2 << list3;
2231 
2232     list = QList<int>();
2233     list << 3 << 1 << 4 << 3 << 1 << 2 << 0;
2234     list2 = QList<int>();
2235     list2 << 0 << 2 << 4 << 1 << 3;
2236     list3 = QList<int>();
2237     list3 << 1 << 3 << 4 << 1 << 2 << 0;
2238     QTest::newRow("StackingOrder") << QMdiArea::StackingOrder << 5 << 3 << 1 << list << list2 << list3;
2239 
2240     list = QList<int>();
2241     list << 0 << 1 << 0 << 1 << 4 << 3 << 2;
2242     list2 = QList<int>();
2243     list2 << 0 << 2 << 3 << 4 << 1;
2244     list3 = QList<int>();
2245     list3 << 1 << 4 << 3 << 1 << 2 << 0;
2246     QTest::newRow("ActivationHistoryOrder") << QMdiArea::ActivationHistoryOrder << 5 << 3 << 1 << list << list2 << list3;
2247 }
2248 
setActivationOrder()2249 void tst_QMdiArea::setActivationOrder()
2250 {
2251     QFETCH(QMdiArea::WindowOrder, activationOrder);
2252     QFETCH(int, subWindowCount);
2253     QFETCH(int, staysOnTopIndex);
2254     QFETCH(int, firstActiveIndex);
2255     QFETCH(QList<int>, expectedActivationIndices);
2256     QFETCH(QList<int>, expectedCascadeIndices);
2257     QFETCH(QList<int>, expectedTileIndices);
2258 
2259     // Default order.
2260     QMdiArea mdiArea;
2261     QCOMPARE(mdiArea.activationOrder(), QMdiArea::CreationOrder);
2262 
2263     // New order.
2264     mdiArea.setActivationOrder(activationOrder);
2265     QCOMPARE(mdiArea.activationOrder(), activationOrder);
2266 
2267     QList<QMdiSubWindow *> subWindows;
2268     for (int i = 0; i < subWindowCount; ++i)
2269         subWindows << mdiArea.addSubWindow(new QPushButton(tr("%1").arg(i)));
2270     QCOMPARE(mdiArea.subWindowList(activationOrder), subWindows);
2271 
2272     mdiArea.show();
2273 #ifdef Q_WS_X11
2274     qt_x11_wait_for_window_manager(&mdiArea);
2275 #endif
2276 
2277     for (int i = 0; i < subWindows.count(); ++i) {
2278         mdiArea.activateNextSubWindow();
2279         QCOMPARE(mdiArea.activeSubWindow(), subWindows.at(i));
2280         qApp->processEvents();
2281     }
2282 
2283     QMdiSubWindow *staysOnTop = subWindows.at(staysOnTopIndex);
2284     staysOnTop->setWindowFlags(staysOnTop->windowFlags() | Qt::WindowStaysOnTopHint);
2285     staysOnTop->raise();
2286 
2287     mdiArea.setActiveSubWindow(subWindows.at(firstActiveIndex));
2288     QCOMPARE(mdiArea.activeSubWindow(), subWindows.at(firstActiveIndex));
2289 
2290     // Verify the actual arrangement/geometry.
2291     mdiArea.tileSubWindows();
2292     QTest::qWait(100);
2293     QVERIFY(verifyArrangement(&mdiArea, Tiled, expectedTileIndices));
2294 
2295     mdiArea.cascadeSubWindows();
2296     QVERIFY(verifyArrangement(&mdiArea, Cascaded, expectedCascadeIndices));
2297     QTest::qWait(100);
2298 
2299     mdiArea.activateNextSubWindow();
2300     QCOMPARE(mdiArea.activeSubWindow(), subWindows.at(expectedActivationIndices.takeFirst()));
2301 
2302     mdiArea.activatePreviousSubWindow();
2303     QCOMPARE(mdiArea.activeSubWindow(), subWindows.at(expectedActivationIndices.takeFirst()));
2304 
2305     mdiArea.activatePreviousSubWindow();
2306     QCOMPARE(mdiArea.activeSubWindow(), subWindows.at(expectedActivationIndices.takeFirst()));
2307 
2308     for (int i = 0; i < subWindowCount; ++i) {
2309         mdiArea.closeActiveSubWindow();
2310         qApp->processEvents();
2311         if (i == subWindowCount - 1) { // Last window closed.
2312             QVERIFY(!mdiArea.activeSubWindow());
2313             break;
2314         }
2315         QVERIFY(mdiArea.activeSubWindow());
2316         QCOMPARE(mdiArea.activeSubWindow(), subWindows.at(expectedActivationIndices.takeFirst()));
2317     }
2318 
2319     QVERIFY(mdiArea.subWindowList(activationOrder).isEmpty());
2320     QVERIFY(expectedActivationIndices.isEmpty());
2321 }
2322 
tabBetweenSubWindows()2323 void tst_QMdiArea::tabBetweenSubWindows()
2324 {
2325     QMdiArea mdiArea;
2326     QList<QMdiSubWindow *> subWindows;
2327     for (int i = 0; i < 5; ++i)
2328         subWindows << mdiArea.addSubWindow(new QLineEdit);
2329 
2330     mdiArea.show();
2331 #ifdef Q_WS_X11
2332     qt_x11_wait_for_window_manager(&mdiArea);
2333 #endif
2334 
2335     qApp->setActiveWindow(&mdiArea);
2336     QWidget *focusWidget = subWindows.back()->widget();
2337     QCOMPARE(qApp->focusWidget(), focusWidget);
2338 
2339     QSignalSpy spy(&mdiArea, SIGNAL(subWindowActivated(QMdiSubWindow *)));
2340     QCOMPARE(spy.count(), 0);
2341 
2342     // Walk through the entire list of sub windows.
2343     QVERIFY(tabBetweenSubWindowsIn(&mdiArea));
2344     QCOMPARE(mdiArea.activeSubWindow(), subWindows.back());
2345     QCOMPARE(spy.count(), 0);
2346 
2347     mdiArea.setActiveSubWindow(subWindows.front());
2348     QCOMPARE(mdiArea.activeSubWindow(), subWindows.front());
2349     spy.clear();
2350 
2351     // Walk through the entire list of sub windows in the opposite direction (Ctrl-Shift-Tab).
2352     QVERIFY(tabBetweenSubWindowsIn(&mdiArea, -1, true));
2353     QCOMPARE(mdiArea.activeSubWindow(), subWindows.front());
2354     QCOMPARE(spy.count(), 0);
2355 
2356     // Ctrl-Tab-Tab-Tab
2357     QVERIFY(tabBetweenSubWindowsIn(&mdiArea, 3));
2358     QCOMPARE(mdiArea.activeSubWindow(), subWindows.at(3));
2359     QCOMPARE(spy.count(), 1);
2360 
2361     mdiArea.setActiveSubWindow(subWindows.at(1));
2362     QCOMPARE(mdiArea.activeSubWindow(), subWindows.at(1));
2363     spy.clear();
2364 
2365     // Quick switch (Ctrl-Tab once) -> switch back to the previously active sub-window.
2366     QVERIFY(tabBetweenSubWindowsIn(&mdiArea, 1));
2367     QCOMPARE(mdiArea.activeSubWindow(), subWindows.at(3));
2368     QCOMPARE(spy.count(), 1);
2369 }
2370 
setViewMode()2371 void tst_QMdiArea::setViewMode()
2372 {
2373     QMdiArea mdiArea;
2374 
2375     QPixmap iconPixmap(16, 16);
2376     iconPixmap.fill(Qt::red);
2377     for (int i = 0; i < 5; ++i) {
2378         QMdiSubWindow *subWindow = mdiArea.addSubWindow(new QWidget);
2379         subWindow->setWindowTitle(QString(QLatin1String("Title %1")).arg(i));
2380         subWindow->setWindowIcon(iconPixmap);
2381     }
2382 
2383     mdiArea.show();
2384 #ifdef Q_WS_X11
2385     qt_x11_wait_for_window_manager(&mdiArea);
2386 #endif
2387 
2388     QMdiSubWindow *activeSubWindow = mdiArea.activeSubWindow();
2389     const QList<QMdiSubWindow *> subWindows = mdiArea.subWindowList();
2390 
2391     // Default.
2392     QVERIFY(!activeSubWindow->isMaximized());
2393     QTabBar *tabBar = qFindChild<QTabBar *>(&mdiArea);
2394     QVERIFY(!tabBar);
2395     QCOMPARE(mdiArea.viewMode(), QMdiArea::SubWindowView);
2396 
2397     // Tabbed view.
2398     mdiArea.setViewMode(QMdiArea::TabbedView);
2399     QCOMPARE(mdiArea.viewMode(), QMdiArea::TabbedView);
2400     tabBar = qFindChild<QTabBar *>(&mdiArea);
2401     QVERIFY(tabBar);
2402     QVERIFY(tabBar->isVisible());
2403 
2404     QCOMPARE(tabBar->count(), subWindows.count());
2405     QVERIFY(activeSubWindow->isMaximized());
2406     QCOMPARE(tabBar->currentIndex(), subWindows.indexOf(activeSubWindow));
2407 
2408     // Check that tabIcon and tabText are set properly.
2409     for (int i = 0; i < subWindows.size(); ++i) {
2410         QMdiSubWindow *subWindow = subWindows.at(i);
2411         QCOMPARE(tabBar->tabText(i), subWindow->windowTitle());
2412         QCOMPARE(tabBar->tabIcon(i), subWindow->windowIcon());
2413     }
2414 
2415     // Check that tabText and tabIcon are updated.
2416     activeSubWindow->setWindowTitle(QLatin1String("Dude, I want another window title"));
2417     QCOMPARE(tabBar->tabText(tabBar->currentIndex()), activeSubWindow->windowTitle());
2418     iconPixmap.fill(Qt::green);
2419     activeSubWindow->setWindowIcon(iconPixmap);
2420     QCOMPARE(tabBar->tabIcon(tabBar->currentIndex()), activeSubWindow->windowIcon());
2421 
2422     // If there's an empty window title, tabText should return "(Untitled)" (as in firefox).
2423     activeSubWindow->setWindowTitle(QString());
2424     QCOMPARE(tabBar->tabText(tabBar->currentIndex()), QLatin1String("(Untitled)"));
2425 
2426     // If there's no window icon, tabIcon should return ... an empty icon :)
2427     activeSubWindow->setWindowIcon(QIcon());
2428     QCOMPARE(tabBar->tabIcon(tabBar->currentIndex()), QIcon());
2429 
2430     // Check that the current tab changes when activating another sub-window.
2431     for (int i = 0; i < subWindows.size(); ++i) {
2432         mdiArea.activateNextSubWindow();
2433         activeSubWindow = mdiArea.activeSubWindow();
2434         QCOMPARE(tabBar->currentIndex(), subWindows.indexOf(activeSubWindow));
2435     }
2436 
2437     activeSubWindow = mdiArea.activeSubWindow();
2438     const int tabIndex = tabBar->currentIndex();
2439 
2440     // The current tab should not change when the sub-window is hidden.
2441     activeSubWindow->hide();
2442     QCOMPARE(tabBar->currentIndex(), tabIndex);
2443     activeSubWindow->show();
2444     QCOMPARE(tabBar->currentIndex(), tabIndex);
2445 
2446     // Disable the tab when the sub-window is hidden and another sub-window is activated.
2447     activeSubWindow->hide();
2448     mdiArea.activateNextSubWindow();
2449     QVERIFY(tabBar->currentIndex() != tabIndex);
2450     QVERIFY(!tabBar->isTabEnabled(tabIndex));
2451 
2452     // Enable it again.
2453     activeSubWindow->show();
2454     QCOMPARE(tabBar->currentIndex(), tabIndex);
2455     QVERIFY(tabBar->isTabEnabled(tabIndex));
2456 
2457     // Remove sub-windows and make sure the tab is removed.
2458     foreach (QMdiSubWindow *subWindow, subWindows) {
2459         if (subWindow != activeSubWindow)
2460             mdiArea.removeSubWindow(subWindow);
2461     }
2462     QCOMPARE(tabBar->count(), 1);
2463 
2464     // Go back to default (QMdiArea::SubWindowView).
2465     mdiArea.setViewMode(QMdiArea::SubWindowView);
2466     QVERIFY(!activeSubWindow->isMaximized());
2467     tabBar = qFindChild<QTabBar *>(&mdiArea);
2468     QVERIFY(!tabBar);
2469     QCOMPARE(mdiArea.viewMode(), QMdiArea::SubWindowView);
2470 }
2471 
setTabsClosable()2472 void tst_QMdiArea::setTabsClosable()
2473 {
2474     QMdiArea mdiArea;
2475     mdiArea.addSubWindow(new QWidget);
2476 
2477     // test default
2478     QCOMPARE(mdiArea.tabsClosable(), false);
2479 
2480     // change value before tab bar exists
2481     QTabBar *tabBar = qFindChild<QTabBar *>(&mdiArea);
2482     QVERIFY(!tabBar);
2483     mdiArea.setTabsClosable(true);
2484     QCOMPARE(mdiArea.tabsClosable(), true);
2485 
2486     // force tab bar creation
2487     mdiArea.setViewMode(QMdiArea::TabbedView);
2488     tabBar = qFindChild<QTabBar *>(&mdiArea);
2489     QVERIFY(tabBar);
2490 
2491     // value must've been propagated
2492     QCOMPARE(tabBar->tabsClosable(), true);
2493 
2494     // change value when tab bar exists
2495     mdiArea.setTabsClosable(false);
2496     QCOMPARE(mdiArea.tabsClosable(), false);
2497     QCOMPARE(tabBar->tabsClosable(), false);
2498 }
2499 
setTabsMovable()2500 void tst_QMdiArea::setTabsMovable()
2501 {
2502     QMdiArea mdiArea;
2503     QMdiSubWindow *subWindow1 = mdiArea.addSubWindow(new QWidget);
2504     QMdiSubWindow *subWindow2 = mdiArea.addSubWindow(new QWidget);
2505     QMdiSubWindow *subWindow3 = mdiArea.addSubWindow(new QWidget);
2506 
2507     // test default
2508     QCOMPARE(mdiArea.tabsMovable(), false);
2509 
2510     // change value before tab bar exists
2511     QTabBar *tabBar = qFindChild<QTabBar *>(&mdiArea);
2512     QVERIFY(!tabBar);
2513     mdiArea.setTabsMovable(true);
2514     QCOMPARE(mdiArea.tabsMovable(), true);
2515 
2516     // force tab bar creation
2517     mdiArea.setViewMode(QMdiArea::TabbedView);
2518     tabBar = qFindChild<QTabBar *>(&mdiArea);
2519     QVERIFY(tabBar);
2520 
2521     // value must've been propagated
2522     QCOMPARE(tabBar->isMovable(), true);
2523 
2524     // test tab moving
2525     QList<QMdiSubWindow *> subWindows;
2526     subWindows << subWindow1 << subWindow2 << subWindow3;
2527     QCOMPARE(mdiArea.subWindowList(QMdiArea::CreationOrder), subWindows);
2528     tabBar->moveTab(1, 2); // 1,3,2
2529     subWindows.clear();
2530     subWindows << subWindow1 << subWindow3 << subWindow2;
2531     QCOMPARE(mdiArea.subWindowList(QMdiArea::CreationOrder), subWindows);
2532     tabBar->moveTab(0, 2); // 3,2,1
2533     subWindows.clear();
2534     subWindows << subWindow3 << subWindow2 << subWindow1;
2535     QCOMPARE(mdiArea.subWindowList(QMdiArea::CreationOrder), subWindows);
2536 
2537     // change value when tab bar exists
2538     mdiArea.setTabsMovable(false);
2539     QCOMPARE(mdiArea.tabsMovable(), false);
2540     QCOMPARE(tabBar->isMovable(), false);
2541 }
2542 
setTabShape()2543 void tst_QMdiArea::setTabShape()
2544 {
2545     QMdiArea mdiArea;
2546     mdiArea.addSubWindow(new QWidget);
2547     mdiArea.show();
2548 #ifdef Q_WS_X11
2549     qt_x11_wait_for_window_manager(&mdiArea);
2550 #endif
2551 
2552     // Default.
2553     QCOMPARE(mdiArea.tabShape(), QTabWidget::Rounded);
2554 
2555     // Triangular.
2556     mdiArea.setTabShape(QTabWidget::Triangular);
2557     QCOMPARE(mdiArea.tabShape(), QTabWidget::Triangular);
2558 
2559     mdiArea.setViewMode(QMdiArea::TabbedView);
2560 
2561     QTabBar *tabBar = qFindChild<QTabBar *>(&mdiArea);
2562     QVERIFY(tabBar);
2563     QCOMPARE(tabBar->shape(), QTabBar::TriangularNorth);
2564 
2565     // Back to default (Rounded).
2566     mdiArea.setTabShape(QTabWidget::Rounded);
2567     QCOMPARE(mdiArea.tabShape(), QTabWidget::Rounded);
2568     QCOMPARE(tabBar->shape(), QTabBar::RoundedNorth);
2569 }
2570 
setTabPosition_data()2571 void tst_QMdiArea::setTabPosition_data()
2572 {
2573     QTest::addColumn<QTabWidget::TabPosition>("tabPosition");
2574     QTest::addColumn<bool>("hasLeftMargin");
2575     QTest::addColumn<bool>("hasTopMargin");
2576     QTest::addColumn<bool>("hasRightMargin");
2577     QTest::addColumn<bool>("hasBottomMargin");
2578 
2579     QTest::newRow("North") << QTabWidget::North << false << true << false << false;
2580     QTest::newRow("South") << QTabWidget::South << false << false << false << true;
2581     QTest::newRow("East") << QTabWidget::East << false << false << true << false;
2582     QTest::newRow("West") << QTabWidget::West << true << false << false << false;
2583 }
2584 
setTabPosition()2585 void tst_QMdiArea::setTabPosition()
2586 {
2587     QFETCH(QTabWidget::TabPosition, tabPosition);
2588     QFETCH(bool, hasLeftMargin);
2589     QFETCH(bool, hasTopMargin);
2590     QFETCH(bool, hasRightMargin);
2591     QFETCH(bool, hasBottomMargin);
2592 
2593     QMdiArea mdiArea;
2594     mdiArea.addSubWindow(new QWidget);
2595     mdiArea.show();
2596 #ifdef Q_WS_X11
2597     qt_x11_wait_for_window_manager(&mdiArea);
2598 #endif
2599 
2600     // Make sure there are no margins.
2601     mdiArea.setContentsMargins(0, 0, 0, 0);
2602 
2603     // Default.
2604     QCOMPARE(mdiArea.tabPosition(), QTabWidget::North);
2605     mdiArea.setViewMode(QMdiArea::TabbedView);
2606     QTabBar *tabBar = qFindChild<QTabBar *>(&mdiArea);
2607     QVERIFY(tabBar);
2608     QCOMPARE(tabBar->shape(), QTabBar::RoundedNorth);
2609 
2610     // New position.
2611     mdiArea.setTabPosition(tabPosition);
2612     QCOMPARE(mdiArea.tabPosition(), tabPosition);
2613     QCOMPARE(tabBar->shape(), tabBarShapeFrom(QTabWidget::Rounded, tabPosition));
2614 
2615     const Qt::LayoutDirection originalLayoutDirection = qApp->layoutDirection();
2616 
2617     // Check that we have correct geometry in both RightToLeft and LeftToRight.
2618     for (int i = 0; i < 2; ++i) {
2619         // Check viewportMargins.
2620         const QRect viewportGeometry = mdiArea.viewport()->geometry();
2621         const int left = viewportGeometry.left();
2622         const int top = viewportGeometry.y();
2623         const int right = mdiArea.width() - viewportGeometry.width();
2624         const int bottom = mdiArea.height() - viewportGeometry.height();
2625 
2626         const QSize sizeHint = tabBar->sizeHint();
2627 
2628         if (hasLeftMargin)
2629             QCOMPARE(qApp->isLeftToRight() ? left : right, sizeHint.width());
2630         if (hasRightMargin)
2631             QCOMPARE(qApp->isLeftToRight() ? right : left, sizeHint.width());
2632         if (hasTopMargin || hasBottomMargin)
2633             QCOMPARE(hasTopMargin ? top : bottom, sizeHint.height());
2634 
2635         // Check actual tab bar geometry.
2636         const QRegion expectedTabBarGeometry = QRegion(mdiArea.rect()).subtracted(viewportGeometry);
2637         QVERIFY(!expectedTabBarGeometry.isEmpty());
2638         QCOMPARE(QRegion(tabBar->geometry()), expectedTabBarGeometry);
2639 
2640         if (i == 0)
2641             qApp->setLayoutDirection(originalLayoutDirection == Qt::LeftToRight ? Qt::RightToLeft : Qt::LeftToRight);
2642         qApp->processEvents();
2643     }
2644 
2645     qApp->setLayoutDirection(originalLayoutDirection);
2646 }
2647 
2648 #if defined(Q_WS_WIN) || defined(Q_WS_X11)
nativeSubWindows()2649 void tst_QMdiArea::nativeSubWindows()
2650 {
2651     { // Add native widgets after show.
2652     QMdiArea mdiArea;
2653     mdiArea.addSubWindow(new QWidget);
2654     mdiArea.addSubWindow(new QWidget);
2655     mdiArea.show();
2656 #ifdef Q_WS_X11
2657     qt_x11_wait_for_window_manager(&mdiArea);
2658 #endif
2659 
2660     // No native widgets.
2661     QVERIFY(!mdiArea.viewport()->internalWinId());
2662     foreach (QMdiSubWindow *subWindow, mdiArea.subWindowList())
2663         QVERIFY(!subWindow->internalWinId());
2664 
2665     QWidget *nativeWidget = new QWidget;
2666     QVERIFY(nativeWidget->winId()); // enforce native window.
2667     mdiArea.addSubWindow(nativeWidget);
2668 
2669     // The viewport and all the sub-windows must be native.
2670     QVERIFY(mdiArea.viewport()->internalWinId());
2671     foreach (QMdiSubWindow *subWindow, mdiArea.subWindowList())
2672         QVERIFY(subWindow->internalWinId());
2673 
2674     // Add a non-native widget. This should become native.
2675     QMdiSubWindow *subWindow = new QMdiSubWindow;
2676     subWindow->setWidget(new QWidget);
2677     QVERIFY(!subWindow->internalWinId());
2678     mdiArea.addSubWindow(subWindow);
2679     QVERIFY(subWindow->internalWinId());
2680     }
2681 
2682     { // Add native widgets before show.
2683     QMdiArea mdiArea;
2684     mdiArea.addSubWindow(new QWidget);
2685     QWidget *nativeWidget = new QWidget;
2686     (void)nativeWidget->winId();
2687     mdiArea.addSubWindow(nativeWidget);
2688     mdiArea.show();
2689 #ifdef Q_WS_X11
2690     qt_x11_wait_for_window_manager(&mdiArea);
2691 #endif
2692 
2693     // The viewport and all the sub-windows must be native.
2694     QVERIFY(mdiArea.viewport()->internalWinId());
2695     foreach (QMdiSubWindow *subWindow, mdiArea.subWindowList())
2696         QVERIFY(subWindow->internalWinId());
2697     }
2698 
2699     { // Make a sub-window native *after* it's added to the area.
2700     QMdiArea mdiArea;
2701     mdiArea.addSubWindow(new QWidget);
2702     mdiArea.addSubWindow(new QWidget);
2703     mdiArea.show();
2704 #ifdef Q_WS_X11
2705     qt_x11_wait_for_window_manager(&mdiArea);
2706 #endif
2707 
2708     QMdiSubWindow *nativeSubWindow = mdiArea.subWindowList().last();
2709     QVERIFY(!nativeSubWindow->internalWinId());
2710     (void)nativeSubWindow->winId();
2711 
2712     // All the sub-windows should be native at this point
2713     QVERIFY(mdiArea.viewport()->internalWinId());
2714     foreach (QMdiSubWindow *subWindow, mdiArea.subWindowList())
2715             QVERIFY(subWindow->internalWinId());
2716     }
2717 
2718 #ifndef QT_NO_OPENGL
2719     {
2720     if (!QGLFormat::hasOpenGL())
2721         QSKIP("QGL not supported on this platform", SkipAll);
2722 
2723     QMdiArea mdiArea;
2724     QGLWidget *glViewport = new QGLWidget;
2725     mdiArea.setViewport(glViewport);
2726     mdiArea.addSubWindow(new QWidget);
2727     mdiArea.addSubWindow(new QWidget);
2728     mdiArea.show();
2729 #ifdef Q_WS_X11
2730     qt_x11_wait_for_window_manager(&mdiArea);
2731 #endif
2732 
2733     const QGLContext *context = glViewport->context();
2734     if (!context || !context->isValid())
2735         QSKIP("QGL is broken, cannot continue test", SkipAll);
2736 
2737     // The viewport and all the sub-windows must be native.
2738     QVERIFY(mdiArea.viewport()->internalWinId());
2739     foreach (QMdiSubWindow *subWindow, mdiArea.subWindowList())
2740         QVERIFY(subWindow->internalWinId());
2741     }
2742 #endif
2743 }
2744 #endif
2745 
task_209615()2746 void tst_QMdiArea::task_209615()
2747 {
2748     QTabWidget tabWidget;
2749     QMdiArea *mdiArea1 = new QMdiArea;
2750     QMdiArea *mdiArea2 = new QMdiArea;
2751     QMdiSubWindow *subWindow = mdiArea1->addSubWindow(new QLineEdit);
2752 
2753     tabWidget.addTab(mdiArea1, QLatin1String("1"));
2754     tabWidget.addTab(mdiArea2, QLatin1String("2"));
2755     tabWidget.show();
2756 
2757     mdiArea1->removeSubWindow(subWindow);
2758     mdiArea2->addSubWindow(subWindow);
2759 
2760     // Please do not assert/crash.
2761     tabWidget.setCurrentIndex(1);
2762 }
2763 
task_236750()2764 void tst_QMdiArea::task_236750()
2765 {
2766     QMdiArea mdiArea;
2767     QMdiSubWindow *subWindow = mdiArea.addSubWindow(new QTextEdit);
2768     mdiArea.show();
2769 
2770     subWindow->setWindowFlags(subWindow->windowFlags() | Qt::FramelessWindowHint);
2771     // Please do not crash (floating point exception).
2772     subWindow->showMinimized();
2773 }
2774 
2775 QTEST_MAIN(tst_QMdiArea)
2776 #include "tst_qmdiarea.moc"
2777 
2778