diff options
Diffstat (limited to 'tests/auto/widgets')
582 files changed, 172546 insertions, 0 deletions
diff --git a/tests/auto/widgets/dialogs/dialogs.pro b/tests/auto/widgets/dialogs/dialogs.pro new file mode 100644 index 0000000000..e74323a305 --- /dev/null +++ b/tests/auto/widgets/dialogs/dialogs.pro @@ -0,0 +1,18 @@ +TEMPLATE=subdirs +SUBDIRS=\ + qabstractprintdialog \ + qcolordialog \ + qdialog \ + qerrormessage \ + qfiledialog \ + qfiledialog2 \ + qfilesystemmodel \ + qfontdialog \ + qinputdialog \ + qmessagebox \ + qprogressdialog \ + qsidebar \ + qwizard \ + +!contains(QT_CONFIG, private_tests): SUBDIRS -= \ + qsidebar \ diff --git a/tests/auto/widgets/dialogs/qabstractprintdialog/.gitignore b/tests/auto/widgets/dialogs/qabstractprintdialog/.gitignore new file mode 100644 index 0000000000..a768494da5 --- /dev/null +++ b/tests/auto/widgets/dialogs/qabstractprintdialog/.gitignore @@ -0,0 +1 @@ +tst_qabstractprintdialog diff --git a/tests/auto/widgets/dialogs/qabstractprintdialog/qabstractprintdialog.pro b/tests/auto/widgets/dialogs/qabstractprintdialog/qabstractprintdialog.pro new file mode 100644 index 0000000000..fb72bbf7a9 --- /dev/null +++ b/tests/auto/widgets/dialogs/qabstractprintdialog/qabstractprintdialog.pro @@ -0,0 +1,9 @@ +############################################################ +# Project file for autotest for file qabstractprintdialog.h +############################################################ + +load(qttest_p4) +QT += widgets printsupport +SOURCES += tst_qabstractprintdialog.cpp + +CONFIG += insignificant_test # QTBUG-21402 diff --git a/tests/auto/widgets/dialogs/qabstractprintdialog/tst_qabstractprintdialog.cpp b/tests/auto/widgets/dialogs/qabstractprintdialog/tst_qabstractprintdialog.cpp new file mode 100644 index 0000000000..5d6165d174 --- /dev/null +++ b/tests/auto/widgets/dialogs/qabstractprintdialog/tst_qabstractprintdialog.cpp @@ -0,0 +1,146 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtTest/QtTest> + +#include <qcoreapplication.h> +#include <qdebug.h> +#include <qabstractprintdialog.h> +#include <qprinter.h> + +//TESTED_CLASS= +//TESTED_FILES= + +class tst_QAbstractPrintDialog : public QObject +{ +Q_OBJECT + +#if defined(QT_NO_PRINTER) || defined(QT_NO_PRINTDIALOG) +public slots: + void initTestCase(); +#else +private slots: + void getSetCheck(); + void setMinMax(); + void setFromTo(); +#endif +}; + +#if defined(QT_NO_PRINTER) || defined(QT_NO_PRINTDIALOG) +void tst_QAbstractPrintDialog::initTestCase() +{ + QSKIP("This test requires printing and print dialog support", SkipAll); +} + +#else + +class MyAbstractPrintDialog : public QAbstractPrintDialog +{ +public: + MyAbstractPrintDialog(QPrinter *p) : QAbstractPrintDialog(p) {} + int exec() { return 0; } +}; + +// Testing get/set functions +void tst_QAbstractPrintDialog::getSetCheck() +{ + QPrinter printer; + MyAbstractPrintDialog obj1(&printer); + QCOMPARE(obj1.printer(), &printer); + // PrintDialogOptions QAbstractPrintDialog::enabledOptions() + // void QAbstractPrintDialog::setEnabledOptions(PrintDialogOptions) + obj1.setEnabledOptions(QAbstractPrintDialog::PrintDialogOptions(QAbstractPrintDialog::None)); + QCOMPARE(QAbstractPrintDialog::PrintDialogOptions(QAbstractPrintDialog::None), obj1.enabledOptions()); + obj1.setEnabledOptions(QAbstractPrintDialog::PrintDialogOptions(QAbstractPrintDialog::PrintToFile)); + QCOMPARE(QAbstractPrintDialog::PrintDialogOptions(QAbstractPrintDialog::PrintToFile), obj1.enabledOptions()); + obj1.setEnabledOptions(QAbstractPrintDialog::PrintDialogOptions(QAbstractPrintDialog::PrintSelection)); + QCOMPARE(QAbstractPrintDialog::PrintDialogOptions(QAbstractPrintDialog::PrintSelection), obj1.enabledOptions()); + obj1.setEnabledOptions(QAbstractPrintDialog::PrintDialogOptions(QAbstractPrintDialog::PrintPageRange)); + QCOMPARE(QAbstractPrintDialog::PrintDialogOptions(QAbstractPrintDialog::PrintPageRange), obj1.enabledOptions()); + obj1.setEnabledOptions(QAbstractPrintDialog::PrintDialogOptions(QAbstractPrintDialog::PrintCollateCopies)); + QCOMPARE(QAbstractPrintDialog::PrintDialogOptions(QAbstractPrintDialog::PrintCollateCopies), obj1.enabledOptions()); + + // PrintRange QAbstractPrintDialog::printRange() + // void QAbstractPrintDialog::setPrintRange(PrintRange) + obj1.setPrintRange(QAbstractPrintDialog::PrintRange(QAbstractPrintDialog::AllPages)); + QCOMPARE(QAbstractPrintDialog::PrintRange(QAbstractPrintDialog::AllPages), obj1.printRange()); + obj1.setPrintRange(QAbstractPrintDialog::PrintRange(QAbstractPrintDialog::Selection)); + QCOMPARE(QAbstractPrintDialog::PrintRange(QAbstractPrintDialog::Selection), obj1.printRange()); + obj1.setPrintRange(QAbstractPrintDialog::PrintRange(QAbstractPrintDialog::PageRange)); + QCOMPARE(QAbstractPrintDialog::PrintRange(QAbstractPrintDialog::PageRange), obj1.printRange()); +} + +void tst_QAbstractPrintDialog::setMinMax() +{ + QPrinter printer; + MyAbstractPrintDialog obj1(&printer); + obj1.setEnabledOptions(QAbstractPrintDialog::PrintDialogOptions(QAbstractPrintDialog::None)); + QCOMPARE(obj1.minPage(), 1); + QCOMPARE(obj1.maxPage(), INT_MAX); + QVERIFY(!obj1.isOptionEnabled(QAbstractPrintDialog::PrintPageRange)); + obj1.setMinMax(2,5); + QCOMPARE(obj1.minPage(), 2); + QCOMPARE(obj1.maxPage(), 5); + QVERIFY(obj1.enabledOptions() & QAbstractPrintDialog::PrintPageRange); + QVERIFY(obj1.isOptionEnabled(QAbstractPrintDialog::PrintPageRange)); +} + +void tst_QAbstractPrintDialog::setFromTo() +{ + QPrinter printer; + MyAbstractPrintDialog obj1(&printer); + QCOMPARE(obj1.fromPage(), 0); + QCOMPARE(obj1.toPage(), 0); + obj1.setMinMax(0,0); + QCOMPARE(obj1.minPage(), 0); + QCOMPARE(obj1.maxPage(), 0); + obj1.setFromTo(20,50); + QCOMPARE(obj1.fromPage(), 20); + QCOMPARE(obj1.toPage(), 50); + QCOMPARE(obj1.minPage(), 1); + QCOMPARE(obj1.maxPage(), 50); +} + +#endif + +QTEST_MAIN(tst_QAbstractPrintDialog) +#include "tst_qabstractprintdialog.moc" diff --git a/tests/auto/widgets/dialogs/qcolordialog/.gitignore b/tests/auto/widgets/dialogs/qcolordialog/.gitignore new file mode 100644 index 0000000000..b7a8ebd8f8 --- /dev/null +++ b/tests/auto/widgets/dialogs/qcolordialog/.gitignore @@ -0,0 +1 @@ +tst_qcolordialog diff --git a/tests/auto/widgets/dialogs/qcolordialog/qcolordialog.pro b/tests/auto/widgets/dialogs/qcolordialog/qcolordialog.pro new file mode 100644 index 0000000000..4f195dac2b --- /dev/null +++ b/tests/auto/widgets/dialogs/qcolordialog/qcolordialog.pro @@ -0,0 +1,6 @@ +load(qttest_p4) +QT += widgets +SOURCES += tst_qcolordialog.cpp + + + diff --git a/tests/auto/widgets/dialogs/qcolordialog/tst_qcolordialog.cpp b/tests/auto/widgets/dialogs/qcolordialog/tst_qcolordialog.cpp new file mode 100644 index 0000000000..3ae1e63a8e --- /dev/null +++ b/tests/auto/widgets/dialogs/qcolordialog/tst_qcolordialog.cpp @@ -0,0 +1,164 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtTest/QtTest> +#include <QtGui/QtGui> +#include <QtWidgets/QColorDialog> + +//TESTED_CLASS= +//TESTED_FILES= + +QT_FORWARD_DECLARE_CLASS(QtTestEventThread) + +class tst_QColorDialog : public QObject +{ + Q_OBJECT +public: + tst_QColorDialog(); + virtual ~tst_QColorDialog(); + +#ifndef Q_WS_MAC +public slots: + void postKeyReturn(); +private slots: + void defaultOkButton(); +#endif + +public slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + +private slots: + void native_activeModalWidget(); + void task247349_alpha(); +}; + +class TestNativeDialog : public QColorDialog +{ + Q_OBJECT +public: + QWidget *m_activeModalWidget; + + TestNativeDialog(QWidget *parent = 0) + : QColorDialog(parent), m_activeModalWidget(0) + { + QTimer::singleShot(1, this, SLOT(test_activeModalWidgetSignal())); + } + +public slots: + void test_activeModalWidgetSignal() + { + m_activeModalWidget = qApp->activeModalWidget(); + } +}; + +tst_QColorDialog::tst_QColorDialog() +{ +} + +tst_QColorDialog::~tst_QColorDialog() +{ +} + +void tst_QColorDialog::native_activeModalWidget() +{ + // Check that QApplication::activeModalWidget retruns the + // color dialog when it is executing, even when using a native + // dialog: + TestNativeDialog d; + QTimer::singleShot(1000, &d, SLOT(hide())); + d.exec(); + QVERIFY(&d == d.m_activeModalWidget); +} + +void tst_QColorDialog::initTestCase() +{ +} + +void tst_QColorDialog::cleanupTestCase() +{ +} + +void tst_QColorDialog::init() +{ +} + +void tst_QColorDialog::cleanup() +{ +} + +#ifndef Q_WS_MAC +//copied from QFontDialogTest +void tst_QColorDialog::postKeyReturn() { + QWidgetList list = QApplication::topLevelWidgets(); + for (int i=0; i<list.count(); ++i) { + QColorDialog *dialog = qobject_cast<QColorDialog *>(list[i]); + if (dialog) { + QTest::keyClick( list[i], Qt::Key_Return, Qt::NoModifier ); + return; + } + } +} + +void tst_QColorDialog::defaultOkButton() +{ + bool ok = false; + QTimer::singleShot(500, this, SLOT(postKeyReturn())); + QColorDialog::getRgba(0xffffffff, &ok); + QVERIFY(ok); +} +#endif + +void tst_QColorDialog::task247349_alpha() +{ + QColorDialog dialog; + dialog.setOption(QColorDialog::ShowAlphaChannel, true); + int alpha = 0x17; + dialog.setCurrentColor(QColor(0x01, 0x02, 0x03, alpha)); + QCOMPARE(alpha, dialog.currentColor().alpha()); + QCOMPARE(alpha, qAlpha(dialog.currentColor().rgba())); +} + +QTEST_MAIN(tst_QColorDialog) +#include "tst_qcolordialog.moc" diff --git a/tests/auto/widgets/dialogs/qdialog/.gitignore b/tests/auto/widgets/dialogs/qdialog/.gitignore new file mode 100644 index 0000000000..4a4b5ebcbe --- /dev/null +++ b/tests/auto/widgets/dialogs/qdialog/.gitignore @@ -0,0 +1 @@ +tst_qdialog diff --git a/tests/auto/widgets/dialogs/qdialog/qdialog.pro b/tests/auto/widgets/dialogs/qdialog/qdialog.pro new file mode 100644 index 0000000000..a3596b60f6 --- /dev/null +++ b/tests/auto/widgets/dialogs/qdialog/qdialog.pro @@ -0,0 +1,5 @@ +load(qttest_p4) +QT += widgets +SOURCES += tst_qdialog.cpp + +CONFIG += insignificant_test # QTBUG-21402 diff --git a/tests/auto/widgets/dialogs/qdialog/tst_qdialog.cpp b/tests/auto/widgets/dialogs/qdialog/tst_qdialog.cpp new file mode 100644 index 0000000000..0f4acdcc9e --- /dev/null +++ b/tests/auto/widgets/dialogs/qdialog/tst_qdialog.cpp @@ -0,0 +1,609 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtTest/QtTest> + +#include <qdialog.h> +#include <qapplication.h> +#include <qlineedit.h> +#include <qpushbutton.h> +#include <qstyle.h> +#include <QVBoxLayout> +#include <QSizeGrip> + +Q_DECLARE_METATYPE(QSize) + + +QT_FORWARD_DECLARE_CLASS(QDialog) + +//TESTED_CLASS= +//TESTED_FILES= + +class tst_QDialog : public QObject +{ + Q_OBJECT +public: + tst_QDialog(); + +public slots: + void initTestCase(); + void cleanupTestCase(); +private slots: + void getSetCheck(); + void showExtension_data(); + void showExtension(); + void defaultButtons(); + void showMaximized(); + void showMinimized(); + void showFullScreen(); +#if !defined(Q_WS_X11) && !defined(Q_OS_WINCE) + void showAsTool(); +#endif +#ifndef Q_OS_WINCE + void toolDialogPosition(); +#endif + void deleteMainDefault(); + void deleteInExec(); +#if !defined(QT_NO_EXCEPTIONS) && !defined(Q_OS_MAC) && !(defined(Q_OS_WINCE) && defined(_ARM_)) + void throwInExec(); +#endif + void showSizeGrip(); + void setVisible(); + void reject(); + +private: + QDialog *testWidget; +}; + +// Testing get/set functions +void tst_QDialog::getSetCheck() +{ + QDialog obj1; + // QWidget* QDialog::extension() + // void QDialog::setExtension(QWidget*) + QWidget *var1 = new QWidget; + obj1.setExtension(var1); + QCOMPARE(var1, obj1.extension()); + obj1.setExtension((QWidget *)0); + QCOMPARE((QWidget *)0, obj1.extension()); + // No delete var1, since setExtension takes ownership + + // int QDialog::result() + // void QDialog::setResult(int) + obj1.setResult(0); + QCOMPARE(0, obj1.result()); + obj1.setResult(INT_MIN); + QCOMPARE(INT_MIN, obj1.result()); + obj1.setResult(INT_MAX); + QCOMPARE(INT_MAX, obj1.result()); +} + +// work around function being protected +class DummyDialog : public QDialog { +public: + DummyDialog(): QDialog(0) {} + void showExtension( bool b ) { QDialog::showExtension( b ); } +}; + +class ToolDialog : public QDialog +{ +public: + ToolDialog(QWidget *parent = 0) : QDialog(parent, Qt::Tool), mWasActive(false), tId(-1) { + } + bool wasActive() const { return mWasActive; } + + int exec() { + tId = startTimer(300); + return QDialog::exec(); + } +protected: + void timerEvent(QTimerEvent *event) { + if (tId == event->timerId()) { + killTimer(tId); + mWasActive = isActiveWindow(); + reject(); + } + } + +private: + int mWasActive; + int tId; +}; + +tst_QDialog::tst_QDialog() + +{ +} + +void tst_QDialog::initTestCase() +{ + // Create the test class + testWidget = new QDialog(0, Qt::X11BypassWindowManagerHint); + testWidget->resize(200,200); + testWidget->show(); + qApp->setActiveWindow(testWidget); +} + +void tst_QDialog::cleanupTestCase() +{ + if (testWidget) { + delete testWidget; + testWidget = 0; + } +} + +void tst_QDialog::showExtension_data() +{ + QTest::addColumn<QSize>("dlgSize"); + QTest::addColumn<QSize>("extSize"); + QTest::addColumn<bool>("horizontal"); + QTest::addColumn<QSize>("result"); + + //next we fill it with data + QTest::newRow( "data0" ) << QSize(100,100) << QSize(50,50) << (bool)FALSE << QSize(100,150); + QTest::newRow( "data1" ) << QSize(100,100) << QSize(120,50) << (bool)FALSE << QSize(120,150); + QTest::newRow( "data2" ) << QSize(100,100) << QSize(50,50) << (bool)TRUE << QSize(150,100); + QTest::newRow( "data3" ) << QSize(100,100) << QSize(50,120) << (bool)TRUE << QSize(150,120); +} + +void tst_QDialog::showExtension() +{ + QFETCH( QSize, dlgSize ); + QFETCH( QSize, extSize ); + QFETCH( bool, horizontal ); + + // set geometry of main dialog and extension widget + testWidget->setFixedSize( dlgSize ); + QWidget *ext = new QWidget( testWidget ); + ext->setFixedSize( extSize ); + testWidget->setExtension( ext ); + testWidget->setOrientation( horizontal ? Qt::Horizontal : Qt::Vertical ); + + QCOMPARE( testWidget->size(), dlgSize ); + QPoint oldPosition = testWidget->pos(); + + // show + ((DummyDialog*)testWidget)->showExtension( TRUE ); +// while ( testWidget->size() == dlgSize ) +// qApp->processEvents(); + + QTEST( testWidget->size(), "result" ); + + QCOMPARE(testWidget->pos(), oldPosition); + + // hide extension. back to old size ? + ((DummyDialog*)testWidget)->showExtension( FALSE ); + QCOMPARE( testWidget->size(), dlgSize ); + + testWidget->setExtension( 0 ); +} + +void tst_QDialog::defaultButtons() +{ + QLineEdit *lineEdit = new QLineEdit(testWidget); + QPushButton *push = new QPushButton("Button 1", testWidget); + QPushButton *pushTwo = new QPushButton("Button 2", testWidget); + QPushButton *pushThree = new QPushButton("Button 3", testWidget); + pushThree->setAutoDefault(FALSE); + + //we need to show the buttons. Otherwise they won't get the focus + push->show(); + pushTwo->show(); + pushThree->show(); + + push->setDefault(TRUE); + QVERIFY(push->isDefault()); + + pushTwo->setFocus(); + QVERIFY(pushTwo->isDefault()); + pushThree->setFocus(); + QVERIFY(push->isDefault()); + lineEdit->setFocus(); + QVERIFY(push->isDefault()); + + pushTwo->setDefault(TRUE); + QVERIFY(pushTwo->isDefault()); + + pushTwo->setFocus(); + QVERIFY(pushTwo->isDefault()); + lineEdit->setFocus(); + QVERIFY(pushTwo->isDefault()); +} + +void tst_QDialog::showMaximized() +{ + QDialog dialog(0); + dialog.setSizeGripEnabled(true); + QSizeGrip *sizeGrip = qFindChild<QSizeGrip *>(&dialog); + QVERIFY(sizeGrip); + + dialog.showMaximized(); + QVERIFY(dialog.isMaximized()); + QVERIFY(dialog.isVisible()); +#if !defined(Q_OS_MAC) && !defined(Q_OS_IRIX) && !defined(Q_OS_HPUX) + QVERIFY(!sizeGrip->isVisible()); +#endif + + dialog.showNormal(); + QVERIFY(!dialog.isMaximized()); + QVERIFY(dialog.isVisible()); + QVERIFY(sizeGrip->isVisible()); + + dialog.showMaximized(); + QVERIFY(dialog.isMaximized()); + QVERIFY(dialog.isVisible()); + + dialog.hide(); + QVERIFY(dialog.isMaximized()); + QVERIFY(!dialog.isVisible()); + + dialog.show(); + QVERIFY(dialog.isMaximized()); + QVERIFY(dialog.isVisible()); + + dialog.hide(); + QVERIFY(dialog.isMaximized()); + QVERIFY(!dialog.isVisible()); + + dialog.showMaximized(); + QVERIFY(dialog.isMaximized()); + QVERIFY(dialog.isVisible()); +} + +void tst_QDialog::showMinimized() +{ + QDialog dialog(0); + + dialog.showMinimized(); + QVERIFY(dialog.isMinimized()); + QVERIFY(dialog.isVisible()); + + dialog.showNormal(); + QVERIFY(!dialog.isMinimized()); + QVERIFY(dialog.isVisible()); + + dialog.showMinimized(); + QVERIFY(dialog.isMinimized()); + QVERIFY(dialog.isVisible()); + + dialog.hide(); + QVERIFY(dialog.isMinimized()); + QVERIFY(!dialog.isVisible()); + + dialog.show(); + QVERIFY(dialog.isMinimized()); + QVERIFY(dialog.isVisible()); + + dialog.hide(); + QVERIFY(dialog.isMinimized()); + QVERIFY(!dialog.isVisible()); + + dialog.showMinimized(); + QVERIFY(dialog.isMinimized()); + QVERIFY(dialog.isVisible()); +} + +void tst_QDialog::showFullScreen() +{ + QDialog dialog(0, Qt::X11BypassWindowManagerHint); + dialog.setSizeGripEnabled(true); + QSizeGrip *sizeGrip = qFindChild<QSizeGrip *>(&dialog); + QVERIFY(sizeGrip); + + qApp->syncX(); + dialog.showFullScreen(); + QVERIFY(dialog.isFullScreen()); + QVERIFY(dialog.isVisible()); + QVERIFY(!sizeGrip->isVisible()); + + qApp->syncX(); + dialog.showNormal(); + QVERIFY(!dialog.isFullScreen()); + QVERIFY(dialog.isVisible()); + QVERIFY(sizeGrip->isVisible()); + + qApp->syncX(); + dialog.showFullScreen(); + QVERIFY(dialog.isFullScreen()); + QVERIFY(dialog.isVisible()); + + qApp->syncX(); + dialog.hide(); + QVERIFY(dialog.isFullScreen()); + QVERIFY(!dialog.isVisible()); + + qApp->syncX(); + dialog.show(); + QVERIFY(dialog.isFullScreen()); + QVERIFY(dialog.isVisible()); + + qApp->syncX(); + dialog.hide(); + QVERIFY(dialog.isFullScreen()); + QVERIFY(!dialog.isVisible()); + + qApp->syncX(); + dialog.showFullScreen(); + QVERIFY(dialog.isFullScreen()); + QVERIFY(dialog.isVisible()); + + qApp->syncX(); + dialog.hide(); + QVERIFY(dialog.isFullScreen()); + QVERIFY(!dialog.isVisible()); +} + +// Qt/X11: Skipped since activeWindow() is not respected by all window managers. +// Qt/WinCE: No real support for Qt::Tool on WinCE. +#if !defined(Q_WS_X11) && !defined(Q_OS_WINCE) +void tst_QDialog::showAsTool() +{ + ToolDialog dialog(testWidget); + testWidget->activateWindow(); + dialog.exec(); + QTest::qWait(100); + if (testWidget->style()->styleHint(QStyle::SH_Widget_ShareActivation, 0, testWidget)) { + QCOMPARE(dialog.wasActive(), true); + } else { + QCOMPARE(dialog.wasActive(), false); + } +} +#endif + +// Verify that pos() returns the same before and after show() +// for a dialog with the Tool window type. +// No real support for Qt::Tool on WinCE, so skip this test. +#ifndef Q_OS_WINCE +void tst_QDialog::toolDialogPosition() +{ + QDialog dialog(0, Qt::Tool); + dialog.move(QPoint(100,100)); + const QPoint beforeShowPosition = dialog.pos(); + dialog.show(); + const QPoint afterShowPosition = dialog.pos(); + QCOMPARE(afterShowPosition, beforeShowPosition); +} +#endif + +class Dialog : public QDialog +{ +public: + Dialog(QPushButton *&button) + { + button = new QPushButton(this); + } +}; + +void tst_QDialog::deleteMainDefault() +{ + QPushButton *button; + Dialog dialog(button); + button->setDefault(true); + delete button; + dialog.show(); + QTestEventLoop::instance().enterLoop(2); +} + +void tst_QDialog::deleteInExec() +{ + QDialog *dialog = new QDialog(0); + QMetaObject::invokeMethod(dialog, "deleteLater", Qt::QueuedConnection); + QCOMPARE(dialog->exec(), int(QDialog::Rejected)); +} + +// Throwing exceptions in exec() is not supported on Mac or on WinCE/ARM. +#if !defined(QT_NO_EXCEPTIONS) && !defined(Q_OS_MAC) && !(defined(Q_OS_WINCE) && defined(_ARM_)) +class QDialogTestException : public std::exception { }; + +class ExceptionDialog : public QDialog +{ + Q_OBJECT +public: + ExceptionDialog() : QDialog(0) { } +public slots: + void throwException() + { + QDialogTestException e; + throw e; + } +}; + +void tst_QDialog::throwInExec() +{ +#if defined(Q_OS_LINUX) + // C++ exceptions can't be passed through glib callbacks. Skip the test if + // we're using the glib event loop. + QByteArray dispatcher = QAbstractEventDispatcher::instance()->metaObject()->className(); + if (dispatcher.contains("Glib")) { + QSKIP( + qPrintable(QString( + "Throwing exceptions in exec() won't work if %1 event dispatcher is used.\n" + "Try running with QT_NO_GLIB=1 in environment." + ).arg(QString::fromLatin1(dispatcher))), + SkipAll + ); + } +#endif + + int caughtExceptions = 0; + try { + ExceptionDialog dialog; + QMetaObject::invokeMethod(&dialog, "throwException", Qt::QueuedConnection); + QMetaObject::invokeMethod(&dialog, "reject", Qt::QueuedConnection); + (void) dialog.exec(); + } catch(...) { + ++caughtExceptions; + } + QCOMPARE(caughtExceptions, 1); +} +#endif + +// From Task 124269 +void tst_QDialog::showSizeGrip() +{ +#ifndef QT_NO_SIZEGRIP + QDialog dialog(0); + dialog.show(); + QWidget *ext = new QWidget(&dialog); + QVERIFY(!dialog.extension()); + QVERIFY(!dialog.isSizeGripEnabled()); + + dialog.setSizeGripEnabled(true); + QPointer<QSizeGrip> sizeGrip = qFindChild<QSizeGrip *>(&dialog); + QVERIFY(sizeGrip); + QVERIFY(sizeGrip->isVisible()); + QVERIFY(dialog.isSizeGripEnabled()); + + dialog.setExtension(ext); + QVERIFY(dialog.extension() && !dialog.extension()->isVisible()); + QVERIFY(dialog.isSizeGripEnabled()); + + // normal show/hide sequence + dialog.showExtension(true); + QVERIFY(dialog.extension() && dialog.extension()->isVisible()); + QVERIFY(!dialog.isSizeGripEnabled()); + QVERIFY(!sizeGrip); + + dialog.showExtension(false); + QVERIFY(dialog.extension() && !dialog.extension()->isVisible()); + QVERIFY(dialog.isSizeGripEnabled()); + sizeGrip = qFindChild<QSizeGrip *>(&dialog); + QVERIFY(sizeGrip); + QVERIFY(sizeGrip->isVisible()); + + // show/hide sequence with interleaved size grip update + dialog.showExtension(true); + QVERIFY(dialog.extension() && dialog.extension()->isVisible()); + QVERIFY(!dialog.isSizeGripEnabled()); + QVERIFY(!sizeGrip); + + dialog.setSizeGripEnabled(false); + QVERIFY(!dialog.isSizeGripEnabled()); + + dialog.showExtension(false); + QVERIFY(dialog.extension() && !dialog.extension()->isVisible()); + QVERIFY(!dialog.isSizeGripEnabled()); + + dialog.setSizeGripEnabled(true); + sizeGrip = qFindChild<QSizeGrip *>(&dialog); + QVERIFY(sizeGrip); + QVERIFY(sizeGrip->isVisible()); + sizeGrip->hide(); + dialog.hide(); + dialog.show(); + QVERIFY(!sizeGrip->isVisible()); +#endif +} + +void tst_QDialog::setVisible() +{ + QWidget topLevel; + topLevel.show(); + + QDialog *dialog = new QDialog; + dialog->setLayout(new QVBoxLayout); + dialog->layout()->addWidget(new QPushButton("dialog button")); + + QWidget *widget = new QWidget(&topLevel); + widget->setLayout(new QVBoxLayout); + widget->layout()->addWidget(dialog); + + QVERIFY(!dialog->isVisible()); + QVERIFY(!dialog->isHidden()); + + widget->show(); + QVERIFY(dialog->isVisible()); + QVERIFY(!dialog->isHidden()); + + widget->hide(); + dialog->hide(); + widget->show(); + QVERIFY(!dialog->isVisible()); + QVERIFY(dialog->isHidden()); +} + +class TestRejectDialog : public QDialog +{ + public: + TestRejectDialog() : cancelReject(false), called(0) {} + void reject() + { + called++; + if (!cancelReject) + QDialog::reject(); + } + bool cancelReject; + int called; +}; + +void tst_QDialog::reject() +{ + TestRejectDialog dialog; + dialog.show(); + QTest::qWaitForWindowShown(&dialog); + QTRY_VERIFY(dialog.isVisible()); + dialog.reject(); + QTRY_VERIFY(!dialog.isVisible()); + QCOMPARE(dialog.called, 1); + + dialog.show(); + QTest::qWaitForWindowShown(&dialog); + QTRY_VERIFY(dialog.isVisible()); + QVERIFY(dialog.close()); + QTRY_VERIFY(!dialog.isVisible()); + QCOMPARE(dialog.called, 2); + + dialog.cancelReject = true; + dialog.show(); + QTest::qWaitForWindowShown(&dialog); + QTRY_VERIFY(dialog.isVisible()); + dialog.reject(); + QTRY_VERIFY(dialog.isVisible()); + QCOMPARE(dialog.called, 3); + QVERIFY(!dialog.close()); + QTRY_VERIFY(dialog.isVisible()); + QCOMPARE(dialog.called, 4); +} + + +QTEST_MAIN(tst_QDialog) +#include "tst_qdialog.moc" diff --git a/tests/auto/widgets/dialogs/qerrormessage/.gitignore b/tests/auto/widgets/dialogs/qerrormessage/.gitignore new file mode 100644 index 0000000000..1bb653fb72 --- /dev/null +++ b/tests/auto/widgets/dialogs/qerrormessage/.gitignore @@ -0,0 +1 @@ +tst_qerrormessage diff --git a/tests/auto/widgets/dialogs/qerrormessage/qerrormessage.pro b/tests/auto/widgets/dialogs/qerrormessage/qerrormessage.pro new file mode 100644 index 0000000000..363d085cbf --- /dev/null +++ b/tests/auto/widgets/dialogs/qerrormessage/qerrormessage.pro @@ -0,0 +1,10 @@ +load(qttest_p4) +TEMPLATE = app +TARGET = tst_qerrormessage +DEPENDPATH += . +INCLUDEPATH += . + +QT += widgets + +# Input +SOURCES += tst_qerrormessage.cpp diff --git a/tests/auto/widgets/dialogs/qerrormessage/tst_qerrormessage.cpp b/tests/auto/widgets/dialogs/qerrormessage/tst_qerrormessage.cpp new file mode 100644 index 0000000000..d3ee640b1a --- /dev/null +++ b/tests/auto/widgets/dialogs/qerrormessage/tst_qerrormessage.cpp @@ -0,0 +1,158 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <QtTest/QtTest> +#include <QErrorMessage> +#include <QDebug> +#include <QCheckBox> + +class tst_QErrorMessage : public QObject +{ + Q_OBJECT + +private slots: + void dontShowAgain(); + void dontShowCategoryAgain(); + +}; + +void tst_QErrorMessage::dontShowAgain() +{ + QString plainString = QLatin1String("foo"); + QString htmlString = QLatin1String("foo<br>bar"); + QCheckBox *checkBox = 0; + + QErrorMessage errorMessageDialog(0); + + // show an error with plain string + errorMessageDialog.showMessage(plainString); + QVERIFY(errorMessageDialog.isVisible()); + checkBox = qFindChild<QCheckBox*>(&errorMessageDialog); + QVERIFY(checkBox); + QVERIFY(checkBox->isChecked()); + errorMessageDialog.close(); + + errorMessageDialog.showMessage(plainString); + QVERIFY(errorMessageDialog.isVisible()); + checkBox = qFindChild<QCheckBox*>(&errorMessageDialog); + QVERIFY(checkBox); + QVERIFY(checkBox->isChecked()); + checkBox->setChecked(false); + errorMessageDialog.close(); + + errorMessageDialog.showMessage(plainString); + QVERIFY(!errorMessageDialog.isVisible()); + + // show an error with an html string + errorMessageDialog.showMessage(htmlString); + QVERIFY(errorMessageDialog.isVisible()); + checkBox = qFindChild<QCheckBox*>(&errorMessageDialog); + QVERIFY(checkBox); + QVERIFY(!checkBox->isChecked()); + checkBox->setChecked(true); + errorMessageDialog.close(); + + errorMessageDialog.showMessage(htmlString); + QVERIFY(errorMessageDialog.isVisible()); + checkBox = qFindChild<QCheckBox*>(&errorMessageDialog); + QVERIFY(checkBox); + QVERIFY(checkBox->isChecked()); + checkBox->setChecked(false); + errorMessageDialog.close(); + + errorMessageDialog.showMessage(htmlString); + QVERIFY(!errorMessageDialog.isVisible()); +} + +void tst_QErrorMessage::dontShowCategoryAgain() +{ + QString htmlString = QLatin1String("foo<br>bar"); + QString htmlString2 = QLatin1String("foo2<br>bar2"); + QCheckBox *checkBox = 0; + + QErrorMessage errorMessageDialog(0); + + errorMessageDialog.showMessage(htmlString,"Cat 1"); + QVERIFY(errorMessageDialog.isVisible()); + checkBox = qFindChild<QCheckBox*>(&errorMessageDialog); + QVERIFY(checkBox); + QVERIFY(checkBox->isChecked()); + checkBox->setChecked(true); + errorMessageDialog.close(); + + errorMessageDialog.showMessage(htmlString,"Cat 1"); + QVERIFY(errorMessageDialog.isVisible()); + checkBox = qFindChild<QCheckBox*>(&errorMessageDialog); + QVERIFY(checkBox); + QVERIFY(checkBox->isChecked()); + checkBox->setChecked(true); + errorMessageDialog.close(); + + errorMessageDialog.showMessage(htmlString2,"Cat 1"); + QVERIFY(errorMessageDialog.isVisible()); + checkBox = qFindChild<QCheckBox*>(&errorMessageDialog); + QVERIFY(checkBox); + QVERIFY(checkBox->isChecked()); + checkBox->setChecked(true); + errorMessageDialog.close(); + + errorMessageDialog.showMessage(htmlString,"Cat 1"); + QVERIFY(errorMessageDialog.isVisible()); + checkBox = qFindChild<QCheckBox*>(&errorMessageDialog); + QVERIFY(checkBox); + QVERIFY(checkBox->isChecked()); + checkBox->setChecked(false); + errorMessageDialog.close(); + + errorMessageDialog.showMessage(htmlString2,"Cat 1"); + QVERIFY(!errorMessageDialog.isVisible()); + + errorMessageDialog.showMessage(htmlString,"Cat 1"); + QVERIFY(!errorMessageDialog.isVisible()); + + errorMessageDialog.showMessage(htmlString); + QVERIFY(errorMessageDialog.isVisible()); + + errorMessageDialog.showMessage(htmlString,"Cat 2"); + QVERIFY(errorMessageDialog.isVisible()); +} + +QTEST_MAIN(tst_QErrorMessage) +#include "tst_qerrormessage.moc" diff --git a/tests/auto/widgets/dialogs/qfiledialog/.gitignore b/tests/auto/widgets/dialogs/qfiledialog/.gitignore new file mode 100644 index 0000000000..a696596ee9 --- /dev/null +++ b/tests/auto/widgets/dialogs/qfiledialog/.gitignore @@ -0,0 +1 @@ +tst_qfiledialog diff --git a/tests/auto/widgets/dialogs/qfiledialog/qfiledialog.pro b/tests/auto/widgets/dialogs/qfiledialog/qfiledialog.pro new file mode 100644 index 0000000000..92fba98796 --- /dev/null +++ b/tests/auto/widgets/dialogs/qfiledialog/qfiledialog.pro @@ -0,0 +1,23 @@ +############################################################ +# Project file for autotest for file qfiledialog.h +############################################################ + +load(qttest_p4) +QT += widgets widgets-private +QT += core-private gui-private +SOURCES += tst_qfiledialog.cpp + +wince* { + addFiles.files = *.cpp + addFiles.path = . + filesInDir.files = *.pro + filesInDir.path = someDir + DEPLOYMENT += addFiles filesInDir +} + +wince* { + DEFINES += SRCDIR=\\\"./\\\" +} else { + DEFINES += SRCDIR=\\\"$$PWD/\\\" +} + diff --git a/tests/auto/widgets/dialogs/qfiledialog/resources/file.txt b/tests/auto/widgets/dialogs/qfiledialog/resources/file.txt new file mode 100644 index 0000000000..8a03e0e55f --- /dev/null +++ b/tests/auto/widgets/dialogs/qfiledialog/resources/file.txt @@ -0,0 +1 @@ +This is a simple text file. diff --git a/tests/auto/widgets/dialogs/qfiledialog/tst_qfiledialog.cpp b/tests/auto/widgets/dialogs/qfiledialog/tst_qfiledialog.cpp new file mode 100644 index 0000000000..9abcd407d0 --- /dev/null +++ b/tests/auto/widgets/dialogs/qfiledialog/tst_qfiledialog.cpp @@ -0,0 +1,1349 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtTest/QtTest> + +#include <qcoreapplication.h> +#include <qdebug.h> +#include <qfiledialog.h> +#include <qabstractitemdelegate.h> +#include <qdirmodel.h> +#include <qitemdelegate.h> +#include <qlistview.h> +#include <qcombobox.h> +#include <qpushbutton.h> +#include <qtoolbutton.h> +#include <qtreeview.h> +#include <qheaderview.h> +#include <qcompleter.h> +#include <qaction.h> +#include <qdialogbuttonbox.h> +#include <qsortfilterproxymodel.h> +#include <qlineedit.h> +#include <qlayout.h> +#if defined QT_BUILD_INTERNAL +#include "../../../src/widgets/dialogs/qsidebar_p.h" +#include "../../../src/widgets/dialogs/qfilesystemmodel_p.h" +#include "../../../src/widgets/dialogs/qfiledialog_p.h" +#endif +#include <QFileDialog> +#include <QFileSystemModel> + +#include "../../../network-settings.h" + +//TESTED_CLASS= +//TESTED_FILES= + +#if defined(Q_OS_UNIX) +#ifdef QT_BUILD_INTERNAL +QT_BEGIN_NAMESPACE +extern Q_GUI_EXPORT QString qt_tildeExpansion(const QString &path, bool *expanded = 0); +QT_END_NAMESPACE +#endif +#endif + +class QNonNativeFileDialog : public QFileDialog +{ + Q_OBJECT +public: + QNonNativeFileDialog(QWidget *parent = 0, const QString &caption = QString(), const QString &directory = QString(), const QString &filter = QString()) + : QFileDialog(parent, caption, directory, filter) + { + setOption(QFileDialog::DontUseNativeDialog, true); + } +}; + +class tst_QFiledialog : public QObject +{ +Q_OBJECT + +public: + tst_QFiledialog(); + virtual ~tst_QFiledialog(); + +public slots: + void init(); + void cleanup(); + +private slots: + void currentChangedSignal(); + void directoryEnteredSignal(); + void filesSelectedSignal_data(); + void filesSelectedSignal(); + void filterSelectedSignal(); + + void args(); + void directory(); + void completer_data(); + void completer(); + void completer_up(); + void acceptMode(); + void confirmOverwrite(); + void defaultSuffix(); + void fileMode(); + void filters(); + void history(); + void iconProvider(); + void isReadOnly(); + void itemDelegate(); + void labelText(); + void resolveSymlinks(); + void selectFile_data(); + void selectFile(); + void selectFiles(); + void selectFilter(); + void viewMode(); + void proxymodel(); + void setNameFilter(); + void focus(); + void caption(); + void historyBack(); + void historyForward(); + void disableSaveButton_data(); + void disableSaveButton(); + void saveButtonText_data(); + void saveButtonText(); + void clearLineEdit(); + void enableChooseButton(); + void hooks(); +#if defined(Q_OS_UNIX) && defined(QT_BUILD_INTERNAL) + void tildeExpansion_data(); + void tildeExpansion(); +#endif + +private: + QByteArray userSettings; +}; + +tst_QFiledialog::tst_QFiledialog() +{ +} + +tst_QFiledialog::~tst_QFiledialog() +{ +} + +void tst_QFiledialog::init() +{ + // Save the developers settings so they don't get mad when their sidebar folders are gone. + QSettings settings(QSettings::UserScope, QLatin1String("Trolltech")); + settings.beginGroup(QLatin1String("Qt")); + userSettings = settings.value(QLatin1String("filedialog")).toByteArray(); + settings.remove(QLatin1String("filedialog")); + + // populate it with some default settings + QNonNativeFileDialog fd; +#if defined(Q_OS_WINCE) + QTest::qWait(1000); +#endif +} + +void tst_QFiledialog::cleanup() +{ + QSettings settings(QSettings::UserScope, QLatin1String("Trolltech")); + settings.beginGroup(QLatin1String("Qt")); + settings.setValue(QLatin1String("filedialog"), userSettings); +} + +class MyAbstractItemDelegate : public QAbstractItemDelegate +{ +public: + MyAbstractItemDelegate() : QAbstractItemDelegate() {}; + void paint(QPainter *, const QStyleOptionViewItem &, const QModelIndex &) const {} + QSize sizeHint(const QStyleOptionViewItem &, const QModelIndex &) const { return QSize(); } +}; + +// emitted any time the selection model emits current changed +void tst_QFiledialog::currentChangedSignal() +{ + QNonNativeFileDialog fd; + fd.setViewMode(QFileDialog::List); + QSignalSpy spyCurrentChanged(&fd, SIGNAL(currentChanged(const QString &))); + + QListView* listView = qFindChild<QListView*>(&fd, "listView"); + QVERIFY(listView); + fd.setDirectory(QDir::root()); + QModelIndex root = listView->rootIndex(); + QTRY_COMPARE(listView->model()->rowCount(root) > 0, true); + + QModelIndex folder; + for (int i = 0; i < listView->model()->rowCount(root); ++i) { + folder = listView->model()->index(i, 0, root); + if (listView->model()->hasChildren(folder)) + break; + } + QVERIFY(listView->model()->hasChildren(folder)); + listView->setCurrentIndex(folder); + + QCOMPARE(spyCurrentChanged.count(), 1); +} + +// only emitted from the views, sidebar, or lookin combo +void tst_QFiledialog::directoryEnteredSignal() +{ +#if defined QT_BUILD_INTERNAL + QNonNativeFileDialog fd(0, "", QDir::root().path()); + fd.setOptions(QFileDialog::DontUseNativeDialog); + fd.show(); + QTRY_COMPARE(fd.isVisible(), true); + QSignalSpy spyDirectoryEntered(&fd, SIGNAL(directoryEntered(const QString &))); + + // sidebar + QSidebar *sidebar = qFindChild<QSidebar*>(&fd, "sidebar"); + sidebar->setCurrentIndex(sidebar->model()->index(1, 0)); + QTest::keyPress(sidebar->viewport(), Qt::Key_Return); + QCOMPARE(spyDirectoryEntered.count(), 1); + spyDirectoryEntered.clear(); + + // lookInCombo + QComboBox *comboBox = qFindChild<QComboBox*>(&fd, "lookInCombo"); + comboBox->showPopup(); + QVERIFY(comboBox->view()->model()->index(1, 0).isValid()); + comboBox->view()->setCurrentIndex(comboBox->view()->model()->index(1, 0)); + QTest::keyPress(comboBox->view()->viewport(), Qt::Key_Return); + QCOMPARE(spyDirectoryEntered.count(), 1); + spyDirectoryEntered.clear(); + + // view + /* + // platform specific + fd.setViewMode(QFileDialog::ViewMode(QFileDialog::List)); + QListView* listView = qFindChild<QListView*>(&fd, "listView"); + QVERIFY(listView); + QModelIndex root = listView->rootIndex(); + QTRY_COMPARE(listView->model()->rowCount(root) > 0, true); + + QModelIndex folder; + for (int i = 0; i < listView->model()->rowCount(root); ++i) { + folder = listView->model()->index(i, 0, root); + if (listView->model()->hasChildren(folder)) + break; + } + QVERIFY(listView->model()->hasChildren(folder)); + listView->setCurrentIndex(folder); + QTRY_COMPARE((listView->indexAt(listView->visualRect(folder).center())), folder); + QTest::mouseDClick(listView->viewport(), Qt::LeftButton, 0, listView->visualRect(folder).center()); + QTRY_COMPARE(spyDirectoryEntered.count(), 1); + */ +#endif +} + +Q_DECLARE_METATYPE(QFileDialog::FileMode) +void tst_QFiledialog::filesSelectedSignal_data() +{ + QTest::addColumn<QFileDialog::FileMode>("fileMode"); + QTest::newRow("any") << QFileDialog::AnyFile; + QTest::newRow("existing") << QFileDialog::ExistingFile; + QTest::newRow("directory") << QFileDialog::Directory; + QTest::newRow("directoryOnly") << QFileDialog::DirectoryOnly; + QTest::newRow("existingFiles") << QFileDialog::ExistingFiles; +} + +// emitted when the dialog closes with the selected files +void tst_QFiledialog::filesSelectedSignal() +{ + QNonNativeFileDialog fd; + fd.setViewMode(QFileDialog::List); + fd.setOptions(QFileDialog::DontUseNativeDialog); + QDir testDir(SRCDIR); + fd.setDirectory(testDir); + QFETCH(QFileDialog::FileMode, fileMode); + fd.setFileMode(fileMode); + QSignalSpy spyFilesSelected(&fd, SIGNAL(filesSelected(const QStringList &))); + + fd.show(); + QTest::qWait(500); + QListView *listView = qFindChild<QListView*>(&fd, "listView"); + QVERIFY(listView); + + QModelIndex root = listView->rootIndex(); + QTRY_COMPARE(listView->model()->rowCount(root) > 0, true); + QModelIndex file; + for (int i = 0; i < listView->model()->rowCount(root); ++i) { + file = listView->model()->index(i, 0, root); + if (fileMode == QFileDialog::Directory || fileMode == QFileDialog::DirectoryOnly) { + if (listView->model()->hasChildren(file)) + break; + } else { + if (!listView->model()->hasChildren(file)) + break; + } + file = QModelIndex(); + } + QVERIFY(file.isValid()); + listView->selectionModel()->select(file, QItemSelectionModel::Select | QItemSelectionModel::Rows); + listView->setCurrentIndex(file); + + QDialogButtonBox *buttonBox = qFindChild<QDialogButtonBox*>(&fd, "buttonBox"); + QPushButton *button = buttonBox->button(QDialogButtonBox::Open); + QVERIFY(button); + QVERIFY(button->isEnabled()); + button->animateClick(); + QTRY_COMPARE(fd.isVisible(), false); + QCOMPARE(spyFilesSelected.count(), 1); +} + +// only emitted when the combo box is activated +void tst_QFiledialog::filterSelectedSignal() +{ + QNonNativeFileDialog fd; + fd.setAcceptMode(QFileDialog::AcceptSave); + fd.show(); + QSignalSpy spyFilterSelected(&fd, SIGNAL(filterSelected(const QString &))); + + QStringList filterChoices; + filterChoices << "Image files (*.png *.xpm *.jpg)" + << "Text files (*.txt)" + << "Any files (*.*)"; + fd.setFilters(filterChoices); + QCOMPARE(fd.filters(), filterChoices); + + QComboBox *filters = qFindChild<QComboBox*>(&fd, "fileTypeCombo"); + QVERIFY(filters); + QVERIFY(filters->view()); + QCOMPARE(filters->isVisible(), true); + + QTest::keyPress(filters, Qt::Key_Down); + + QCOMPARE(spyFilterSelected.count(), 1); +} + +void tst_QFiledialog::args() +{ + QWidget *parent = 0; + QString caption = "caption"; + QString directory = QDir::tempPath(); + QString filter = "*.mp3"; + QNonNativeFileDialog fd(parent, caption, directory, filter); + QCOMPARE(fd.parent(), (QObject *)parent); + QCOMPARE(fd.windowTitle(), caption); +#ifndef Q_OS_WIN + QCOMPARE(fd.directory(), QDir(directory)); +#endif + QCOMPARE(fd.filters(), QStringList(filter)); +} + +void tst_QFiledialog::directory() +{ + QNonNativeFileDialog fd; + fd.setViewMode(QFileDialog::List); + QFileSystemModel *model = qFindChild<QFileSystemModel*>(&fd, "qt_filesystem_model"); + QVERIFY(model); + fd.setDirectory(QDir::currentPath()); + QSignalSpy spyCurrentChanged(&fd, SIGNAL(currentChanged(const QString &))); + QSignalSpy spyDirectoryEntered(&fd, SIGNAL(directoryEntered(const QString &))); + QSignalSpy spyFilesSelected(&fd, SIGNAL(filesSelected(const QStringList &))); + QSignalSpy spyFilterSelected(&fd, SIGNAL(filterSelected(const QString &))); + + QCOMPARE(QDir::current().absolutePath(), fd.directory().absolutePath()); + QDir temp = QDir::temp(); + QString tempPath = temp.absolutePath(); +#ifdef Q_OS_WIN + // since the user can have lowercase temp dir, check that we are actually case-insensitive. + tempPath = tempPath.toLower(); +#endif + fd.setDirectory(tempPath); +#ifndef Q_OS_WIN + QCOMPARE(tempPath, fd.directory().absolutePath()); +#endif + QCOMPARE(spyCurrentChanged.count(), 0); + QCOMPARE(spyDirectoryEntered.count(), 0); + QCOMPARE(spyFilesSelected.count(), 0); + QCOMPARE(spyFilterSelected.count(), 0); + + // Check my way + QList<QListView*> list = qFindChildren<QListView*>(&fd, "listView"); + QVERIFY(list.count() > 0); +#ifdef Q_OS_WIN + QCOMPARE(list.at(0)->rootIndex().data().toString().toLower(), temp.dirName().toLower()); +#else + QCOMPARE(list.at(0)->rootIndex().data().toString(), temp.dirName()); +#endif + QNonNativeFileDialog *dlg = new QNonNativeFileDialog(0, "", tempPath); + QCOMPARE(model->index(tempPath), model->index(dlg->directory().absolutePath())); + QCOMPARE(model->index(tempPath).data(QFileSystemModel::FileNameRole).toString(), + model->index(dlg->directory().absolutePath()).data(QFileSystemModel::FileNameRole).toString()); + delete dlg; + dlg = new QNonNativeFileDialog(); + QCOMPARE(model->index(tempPath), model->index(dlg->directory().absolutePath())); + delete dlg; +} + +void tst_QFiledialog::completer_data() +{ + QTest::addColumn<QString>("startPath"); + QTest::addColumn<QString>("input"); + QTest::addColumn<int>("expected"); + + QTest::newRow("r, 10") << "" << "r" << 10; + QTest::newRow("x, 0") << "" << "x" << 0; + QTest::newRow("../, -1") << "" << "../" << -1; + + QTest::newRow("goto root") << QString() << QDir::rootPath() << -1; + QTest::newRow("start at root") << QDir::rootPath() << QString() << -1; + + QDir root = QDir::root(); + QStringList list = root.entryList(); + QString folder; + for (int i = 0; i < list.count(); ++i) { + if (list.at(0) == QChar('.')) + continue; + QFileInfo info(QDir::rootPath() + list[i]); + if (info.isDir()) { + folder = QDir::rootPath() + list[i]; + break; + } + } + + QTest::newRow("start at one below root r") << folder << "r" << -1; + QTest::newRow("start at one below root ../") << folder << "../" << -1; +} + +void tst_QFiledialog::completer() +{ + QFETCH(QString, input); + QFETCH(QString, startPath); + QFETCH(int, expected); + + QString tempPath = QDir::tempPath() + '/' + "QFileDialogTestDir"; + if (startPath.isEmpty()) + startPath = tempPath; + + startPath = QDir::cleanPath(startPath); + + // make temp dir and files + { + QDir cleanup(tempPath); + QStringList x = cleanup.entryList(); + for (int i = 0; i < x.count(); ++i) + QFile::remove(tempPath + '/' + x[i]); + cleanup.rmdir(tempPath); + } + QDir tmp(QDir::tempPath()); + if (!tmp.exists(tempPath)) + QVERIFY(tmp.mkdir("QFileDialogTestDir")); + QList<QTemporaryFile*> files; + QT_TRY { + for (int i = 0; i < 10; ++i) { + QScopedPointer<QTemporaryFile> file(new QTemporaryFile(tempPath + "/rXXXXXX")); + file->open(); + files.append(file.take()); + } + + // ### flesh this out more + QNonNativeFileDialog fd(0,QString("Test it"),startPath); + fd.setOptions(QFileDialog::DontUseNativeDialog); + fd.show(); + QVERIFY(fd.isVisible()); + QFileSystemModel *model = qFindChild<QFileSystemModel*>(&fd, "qt_filesystem_model"); + QVERIFY(model); + QLineEdit *lineEdit = qFindChild<QLineEdit*>(&fd, "fileNameEdit"); + QVERIFY(lineEdit); + QCompleter *completer = lineEdit->completer(); + QVERIFY(completer); + QAbstractItemModel *cModel = completer->completionModel(); + QVERIFY(cModel); + + //wait a bit + QTest::qWait(500); + + // path C:\depot\qt\examples\dialogs\standarddialogs + // files + // [debug] [release] [tmp] dialog dialog main makefile makefile.debug makefile.release standarddialgos + // + // d -> D:\ debug dialog.cpp dialog.h + // ..\ -> ..\classwizard ..\configdialog ..\dialogs.pro + // c -> C:\ control panel + // c: -> C:\ (nothing more) + // C:\ -> C:\, C:\_viminfo, ... + // \ -> \_viminfo + // c:\depot -> 'nothing' + // c:\depot\ -> C:\depot\devtools, C:\depot\dteske + QCOMPARE(model->index(fd.directory().path()), model->index(startPath)); + + if (input.isEmpty()) { + QModelIndex r = model->index(model->rootPath()); + QVERIFY(model->rowCount(r) > 0); + QModelIndex idx = model->index(0, 0, r); + input = idx.data().toString().at(0); + } + + // press 'keys' for the input + for (int i = 0; i < input.count(); ++i) + QTest::keyPress(lineEdit, input[i].toAscii()); + + QStringList expectedFiles; + if (expected == -1) { + QString fullPath = startPath.isEmpty() ? tempPath : startPath; + if (!fullPath.endsWith(QLatin1Char('/'))) + fullPath.append(QLatin1Char('/')); + fullPath.append(input); + bool inputStartsWithRootPath = false; + if (input.startsWith(QDir::rootPath())) { + fullPath = input; + input.clear(); + inputStartsWithRootPath = true; + } + + QFileInfo fi(fullPath); + QDir x(fi.absolutePath()); + expectedFiles = x.entryList(model->filter()); + expected = 0; + if (input.startsWith("..")) + input.clear(); + for (int ii = 0; ii < expectedFiles.count(); ++ii) { +#if defined(Q_OS_WIN) + if (expectedFiles.at(ii).startsWith(input,Qt::CaseInsensitive)) +#else + if (expectedFiles.at(ii).startsWith(input)) +#endif + ++expected; + } + } + + QTest::qWait(1000); + if (cModel->rowCount() != expected) { + for (int i = 0; i < cModel->rowCount(); ++i) { + QString file = cModel->index(i, 0).data().toString(); + expectedFiles.removeAll(file); + } + //qDebug() << expectedFiles; + } + + QTRY_COMPARE(cModel->rowCount(), expected); + } QT_CATCH(...) { + qDeleteAll(files); + QT_RETHROW; + } + qDeleteAll(files); +} + +void tst_QFiledialog::completer_up() +{ + QNonNativeFileDialog fd; + fd.setOptions(QFileDialog::DontUseNativeDialog); + QSignalSpy spyCurrentChanged(&fd, SIGNAL(currentChanged(const QString &))); + QSignalSpy spyDirectoryEntered(&fd, SIGNAL(directoryEntered(const QString &))); + QSignalSpy spyFilesSelected(&fd, SIGNAL(filesSelected(const QStringList &))); + QSignalSpy spyFilterSelected(&fd, SIGNAL(filterSelected(const QString &))); + + fd.show(); + QLineEdit *lineEdit = qFindChild<QLineEdit*>(&fd, "fileNameEdit"); + QVERIFY(lineEdit); + QCOMPARE(spyFilesSelected.count(), 0); + int depth = QDir::currentPath().split('/').count(); + for (int i = 0; i <= depth * 3 + 1; ++i) { + lineEdit->insert("../"); + qApp->processEvents(); + } + QCOMPARE(spyCurrentChanged.count(), 0); + QCOMPARE(spyDirectoryEntered.count(), 0); + QCOMPARE(spyFilesSelected.count(), 0); + QCOMPARE(spyFilterSelected.count(), 0); +} + +void tst_QFiledialog::acceptMode() +{ + QNonNativeFileDialog fd; + fd.show(); + + QToolButton* newButton = qFindChild<QToolButton*>(&fd, "newFolderButton"); + QVERIFY(newButton); + + // default + QCOMPARE(fd.acceptMode(), QFileDialog::AcceptOpen); + QCOMPARE(newButton && newButton->isVisible(), true); + + //fd.setDetailsExpanded(true); + fd.setAcceptMode(QFileDialog::AcceptSave); + QCOMPARE(fd.acceptMode(), QFileDialog::AcceptSave); + QCOMPARE(newButton->isVisible(), true); + + fd.setAcceptMode(QFileDialog::AcceptOpen); + QCOMPARE(fd.acceptMode(), QFileDialog::AcceptOpen); + QCOMPARE(newButton->isVisible(), true); +} + +void tst_QFiledialog::confirmOverwrite() +{ + QNonNativeFileDialog fd; + QCOMPARE(fd.confirmOverwrite(), true); + fd.setConfirmOverwrite(true); + QCOMPARE(fd.confirmOverwrite(), true); + fd.setConfirmOverwrite(false); + QCOMPARE(fd.confirmOverwrite(), false); + fd.setConfirmOverwrite(true); + QCOMPARE(fd.confirmOverwrite(), true); +} + +void tst_QFiledialog::defaultSuffix() +{ + QNonNativeFileDialog fd; + QCOMPARE(fd.defaultSuffix(), QString()); + fd.setDefaultSuffix("txt"); + QCOMPARE(fd.defaultSuffix(), QString("txt")); + fd.setDefaultSuffix(QString()); + QCOMPARE(fd.defaultSuffix(), QString()); +} + +void tst_QFiledialog::fileMode() +{ + QNonNativeFileDialog fd; + QCOMPARE(fd.fileMode(), QFileDialog::AnyFile); + fd.setFileMode(QFileDialog::ExistingFile); + QCOMPARE(fd.fileMode(), QFileDialog::ExistingFile); + fd.setFileMode(QFileDialog::Directory); + QCOMPARE(fd.fileMode(), QFileDialog::Directory); + fd.setFileMode(QFileDialog::DirectoryOnly); + QCOMPARE(fd.fileMode(), QFileDialog::DirectoryOnly); + fd.setFileMode(QFileDialog::ExistingFiles); + QCOMPARE(fd.fileMode(), QFileDialog::ExistingFiles); +} + +void tst_QFiledialog::caption() +{ + QNonNativeFileDialog fd; + fd.setWindowTitle("testing"); + fd.setFileMode(QFileDialog::Directory); + QCOMPARE(fd.windowTitle(), QString("testing")); +} + +void tst_QFiledialog::filters() +{ + QNonNativeFileDialog fd; + fd.setOptions(QFileDialog::DontUseNativeDialog); + QSignalSpy spyCurrentChanged(&fd, SIGNAL(currentChanged(const QString &))); + QSignalSpy spyDirectoryEntered(&fd, SIGNAL(directoryEntered(const QString &))); + QSignalSpy spyFilesSelected(&fd, SIGNAL(filesSelected(const QStringList &))); + QSignalSpy spyFilterSelected(&fd, SIGNAL(filterSelected(const QString &))); + QCOMPARE(fd.filters(), QStringList("All Files (*)")); + + // effects + QList<QComboBox*> views = qFindChildren<QComboBox*>(&fd, "fileTypeCombo"); + QVERIFY(views.count() == 1); + QCOMPARE(views.at(0)->isVisible(), false); + + QStringList filters; + filters << "Image files (*.png *.xpm *.jpg)" + << "Text files (*.txt)" + << "Any files (*.*)"; + fd.setFilters(filters); + QCOMPARE(views.at(0)->isVisible(), false); + fd.show(); + fd.setAcceptMode(QFileDialog::AcceptSave); + QCOMPARE(views.at(0)->isVisible(), true); + QCOMPARE(fd.filters(), filters); + fd.setFilter("Image files (*.png *.xpm *.jpg);;Text files (*.txt);;Any files (*.*)"); + QCOMPARE(fd.filters(), filters); + QCOMPARE(spyCurrentChanged.count(), 0); + QCOMPARE(spyDirectoryEntered.count(), 0); + QCOMPARE(spyFilesSelected.count(), 0); + QCOMPARE(spyFilterSelected.count(), 0); + + // setting shouldn't emit any signals + for (int i = views.at(0)->currentIndex(); i < views.at(0)->count(); ++i) + views.at(0)->setCurrentIndex(i); + QCOMPARE(spyFilterSelected.count(), 0); + + //Let check if filters with whitespaces + QNonNativeFileDialog fd2; + QStringList expected; + expected << "C++ Source Files(*.cpp)"; + expected << "Any(*.*)"; + fd2.setFilter("C++ Source Files(*.cpp);;Any(*.*)"); + QCOMPARE(expected, fd2.filters()); + fd2.setFilter("C++ Source Files(*.cpp) ;;Any(*.*)"); + QCOMPARE(expected, fd2.filters()); + fd2.setFilter("C++ Source Files(*.cpp);; Any(*.*)"); + QCOMPARE(expected, fd2.filters()); + fd2.setFilter(" C++ Source Files(*.cpp);; Any(*.*)"); + QCOMPARE(expected, fd2.filters()); + fd2.setFilter("C++ Source Files(*.cpp) ;; Any(*.*)"); + QCOMPARE(expected, fd2.filters()); +} + +void tst_QFiledialog::selectFilter() +{ + QNonNativeFileDialog fd; + QSignalSpy spyFilterSelected(&fd, SIGNAL(filterSelected(const QString &))); + QCOMPARE(fd.selectedFilter(), QString("All Files (*)")); + QStringList filters; + filters << "Image files (*.png *.xpm *.jpg)" + << "Text files (*.txt)" + << "Any files (*.*)"; + fd.setFilters(filters); + QCOMPARE(fd.selectedFilter(), filters.at(0)); + fd.selectFilter(filters.at(1)); + QCOMPARE(fd.selectedFilter(), filters.at(1)); + fd.selectFilter(filters.at(2)); + QCOMPARE(fd.selectedFilter(), filters.at(2)); + + fd.selectFilter("bob"); + QCOMPARE(fd.selectedFilter(), filters.at(2)); + fd.selectFilter(""); + QCOMPARE(fd.selectedFilter(), filters.at(2)); + QCOMPARE(spyFilterSelected.count(), 0); +} + +void tst_QFiledialog::history() +{ + QNonNativeFileDialog fd; + fd.setViewMode(QFileDialog::List); + QFileSystemModel *model = qFindChild<QFileSystemModel*>(&fd, "qt_filesystem_model"); + QVERIFY(model); + QSignalSpy spyCurrentChanged(&fd, SIGNAL(currentChanged(const QString &))); + QSignalSpy spyDirectoryEntered(&fd, SIGNAL(directoryEntered(const QString &))); + QSignalSpy spyFilesSelected(&fd, SIGNAL(filesSelected(const QStringList &))); + QSignalSpy spyFilterSelected(&fd, SIGNAL(filterSelected(const QString &))); + QCOMPARE(model->index(fd.history().first()), model->index(QDir::toNativeSeparators(fd.directory().absolutePath()))); + fd.setDirectory(QDir::current().absolutePath()); + QStringList history; + history << QDir::toNativeSeparators(QDir::current().absolutePath()) + << QDir::toNativeSeparators(QDir::home().absolutePath()) + << QDir::toNativeSeparators(QDir::temp().absolutePath()); + fd.setHistory(history); + if (fd.history() != history) { + qDebug() << fd.history() << history; + // quick and dirty output for windows failure. + QListView* list = qFindChild<QListView*>(&fd, "listView"); + QVERIFY(list); + QModelIndex root = list->rootIndex(); + while (root.isValid()) { + qDebug() << root.data(); + root = root.parent(); + } + } + QCOMPARE(fd.history(), history); + + QStringList badHistory; + badHistory << "junk"; + fd.setHistory(badHistory); + badHistory << QDir::toNativeSeparators(QDir::current().absolutePath()); + QCOMPARE(fd.history(), badHistory); + + QCOMPARE(spyCurrentChanged.count(), 0); + QCOMPARE(spyDirectoryEntered.count(), 0); + QCOMPARE(spyFilesSelected.count(), 0); + QCOMPARE(spyFilterSelected.count(), 0); +} + +void tst_QFiledialog::iconProvider() +{ + QNonNativeFileDialog *fd = new QNonNativeFileDialog(); + QVERIFY(fd->iconProvider() != 0); + QFileIconProvider *ip = new QFileIconProvider(); + fd->setIconProvider(ip); + QCOMPARE(fd->iconProvider(), ip); + delete fd; + delete ip; +} + +void tst_QFiledialog::isReadOnly() +{ + QNonNativeFileDialog fd; + + QPushButton* newButton = qFindChild<QPushButton*>(&fd, "newFolderButton"); + QAction* renameAction = qFindChild<QAction*>(&fd, "qt_rename_action"); + QAction* deleteAction = qFindChild<QAction*>(&fd, "qt_delete_action"); + + QCOMPARE(fd.isReadOnly(), false); + + // This is dependent upon the file/dir, find cross platform way to test + //fd.setDirectory(QDir::home()); + //QCOMPARE(newButton && newButton->isEnabled(), true); + //QCOMPARE(renameAction && renameAction->isEnabled(), true); + //QCOMPARE(deleteAction && deleteAction->isEnabled(), true); + + fd.setReadOnly(true); + QCOMPARE(fd.isReadOnly(), true); + + QCOMPARE(newButton && newButton->isEnabled(), false); + QCOMPARE(renameAction && renameAction->isEnabled(), false); + QCOMPARE(deleteAction && deleteAction->isEnabled(), false); +} + +void tst_QFiledialog::itemDelegate() +{ + QNonNativeFileDialog fd; + QVERIFY(fd.itemDelegate() != 0); + QItemDelegate *id = new QItemDelegate(&fd); + fd.setItemDelegate(id); + QCOMPARE(fd.itemDelegate(), (QAbstractItemDelegate *)id); +} + +void tst_QFiledialog::labelText() +{ + QNonNativeFileDialog fd; + QDialogButtonBox buttonBox; + QPushButton *cancelButton = buttonBox.addButton(QDialogButtonBox::Cancel); + QCOMPARE(fd.labelText(QFileDialog::LookIn), QString("Look in:")); + QCOMPARE(fd.labelText(QFileDialog::FileName), QString("File &name:")); + QCOMPARE(fd.labelText(QFileDialog::FileType), QString("Files of type:")); + QCOMPARE(fd.labelText(QFileDialog::Accept), QString("&Open")); ///### see task 241462 + QCOMPARE(fd.labelText(QFileDialog::Reject), cancelButton->text()); + + fd.setLabelText(QFileDialog::LookIn, "1"); + QCOMPARE(fd.labelText(QFileDialog::LookIn), QString("1")); + fd.setLabelText(QFileDialog::FileName, "2"); + QCOMPARE(fd.labelText(QFileDialog::FileName), QString("2")); + fd.setLabelText(QFileDialog::FileType, "3"); + QCOMPARE(fd.labelText(QFileDialog::FileType), QString("3")); + fd.setLabelText(QFileDialog::Accept, "4"); + QCOMPARE(fd.labelText(QFileDialog::Accept), QString("4")); + fd.setLabelText(QFileDialog::Reject, "5"); + QCOMPARE(fd.labelText(QFileDialog::Reject), QString("5")); +} + +void tst_QFiledialog::resolveSymlinks() +{ + QNonNativeFileDialog fd; + + // default + QCOMPARE(fd.resolveSymlinks(), true); + fd.setResolveSymlinks(false); + QCOMPARE(fd.resolveSymlinks(), false); + fd.setResolveSymlinks(true); + QCOMPARE(fd.resolveSymlinks(), true); + + // the file dialog doesn't do anything based upon this, just passes it to the model + // the model should fully test it, don't test it here +} + +void tst_QFiledialog::selectFile_data() +{ + QTest::addColumn<QString>("file"); + QTest::addColumn<int>("count"); + QTest::newRow("null") << QString() << 1; + QTest::newRow("file") << "foo" << 1; + QTest::newRow("tmp") << "temp" << 1; +} + +void tst_QFiledialog::selectFile() +{ + QFETCH(QString, file); + QFETCH(int, count); + QNonNativeFileDialog fd; + QFileSystemModel *model = qFindChild<QFileSystemModel*>(&fd, "qt_filesystem_model"); + QVERIFY(model); + fd.setDirectory(QDir::currentPath()); + // default value + QCOMPARE(fd.selectedFiles().count(), 1); + + QTemporaryFile tempFile(QDir::tempPath() + "/aXXXXXX"); + bool inTemp = (file == "temp"); + if (inTemp) { + tempFile.open(); + file = tempFile.fileName(); + } + + fd.selectFile(file); + QCOMPARE(fd.selectedFiles().count(), count); + if (inTemp) { + QCOMPARE(model->index(fd.directory().path()), model->index(QDir::tempPath())); + } else { + QCOMPARE(model->index(fd.directory().path()), model->index(QDir::currentPath())); + } +} + +void tst_QFiledialog::selectFiles() +{ + QNonNativeFileDialog fd; + fd.setViewMode(QFileDialog::List); + QString tempPath = QDir::tempPath() + '/' + "QFileDialogTestDir4SelectFiles"; + QDir dir; + QVERIFY(dir.mkpath(tempPath)); + fd.setDirectory(tempPath); + QSignalSpy spyCurrentChanged(&fd, SIGNAL(currentChanged(const QString &))); + QSignalSpy spyDirectoryEntered(&fd, SIGNAL(directoryEntered(const QString &))); + QSignalSpy spyFilesSelected(&fd, SIGNAL(filesSelected(const QStringList &))); + QSignalSpy spyFilterSelected(&fd, SIGNAL(filterSelected(const QString &))); + fd.show(); + fd.setFileMode(QFileDialog::ExistingFiles); + + QString filesPath = fd.directory().absolutePath(); + for (int i=0; i < 5; ++i) { + QFile file(filesPath + QString::fromLatin1("/qfiledialog_auto_test_not_pres_%1").arg(i)); + file.open(QIODevice::WriteOnly); + file.resize(1024); + file.flush(); + file.close(); + } + + // Get a list of files in the view and then get the corresponding index's + QStringList list = fd.directory().entryList(QDir::Files); + QModelIndexList toSelect; + QVERIFY(list.count() > 1); + QListView* listView = qFindChild<QListView*>(&fd, "listView"); + QVERIFY(listView); + for (int i = 0; i < list.count(); ++i) { + fd.selectFile(fd.directory().path() + "/" + list.at(i)); +#if defined(Q_WS_MAC) || defined(Q_WS_WIN) + QEXPECT_FAIL("", "This test does not work on Mac or Windows", Abort); +#endif + QTRY_VERIFY(!listView->selectionModel()->selectedRows().isEmpty()); + toSelect.append(listView->selectionModel()->selectedRows().last()); + } + QCOMPARE(spyFilesSelected.count(), 0); + + listView->selectionModel()->clear(); + QCOMPARE(spyFilesSelected.count(), 0); + + // select the indexes + for (int i = 0; i < toSelect.count(); ++i) { + listView->selectionModel()->select(toSelect.at(i), + QItemSelectionModel::Select | QItemSelectionModel::Rows); + } + QCOMPARE(fd.selectedFiles().count(), toSelect.count()); + QCOMPARE(spyCurrentChanged.count(), 0); + QCOMPARE(spyDirectoryEntered.count(), 0); + QCOMPARE(spyFilesSelected.count(), 0); + QCOMPARE(spyFilterSelected.count(), 0); + for (int i=0; i < 5; ++i) + QFile::remove(filesPath + QString::fromLatin1("/qfiledialog_auto_test_not_pres_%1").arg(i)); + + //If the selection is invalid then we fill the line edit but without the / + QNonNativeFileDialog * dialog = new QNonNativeFileDialog( 0, "Save" ); + dialog->setFileMode( QFileDialog::AnyFile ); + dialog->setAcceptMode( QFileDialog::AcceptSave ); + QString temporary = QDir::tempPath() + QLatin1String("/blah"); + dialog->selectFile(temporary); + dialog->show(); + QTest::qWait(500); + QLineEdit *lineEdit = qFindChild<QLineEdit*>(dialog, "fileNameEdit"); + QVERIFY(lineEdit); + QCOMPARE(lineEdit->text(),QLatin1String("blah")); + delete dialog; +} + +void tst_QFiledialog::viewMode() +{ + QNonNativeFileDialog fd; + fd.setViewMode(QFileDialog::List); + fd.show(); + + // find widgets + QList<QTreeView*> treeView = qFindChildren<QTreeView*>(&fd, "treeView"); + QCOMPARE(treeView.count(), 1); + QList<QListView*> listView = qFindChildren<QListView*>(&fd, "listView"); + QCOMPARE(listView.count(), 1); + QList<QToolButton*> listButton = qFindChildren<QToolButton*>(&fd, "listModeButton"); + QCOMPARE(listButton.count(), 1); + QList<QToolButton*> treeButton = qFindChildren<QToolButton*>(&fd, "detailModeButton"); + QCOMPARE(treeButton.count(), 1); + + // default value + QCOMPARE(fd.viewMode(), QFileDialog::List); + + // detail + fd.setViewMode(QFileDialog::ViewMode(QFileDialog::Detail)); + + QCOMPARE(QFileDialog::ViewMode(QFileDialog::Detail), fd.viewMode()); + QCOMPARE(listView.at(0)->isVisible(), false); + QCOMPARE(listButton.at(0)->isDown(), false); + QCOMPARE(treeView.at(0)->isVisible(), true); + QCOMPARE(treeButton.at(0)->isDown(), true); + + // list + fd.setViewMode(QFileDialog::ViewMode(QFileDialog::List)); + + QCOMPARE(QFileDialog::ViewMode(QFileDialog::List), fd.viewMode()); + QCOMPARE(treeView.at(0)->isVisible(), false); + QCOMPARE(treeButton.at(0)->isDown(), false); + QCOMPARE(listView.at(0)->isVisible(), true); + QCOMPARE(listButton.at(0)->isDown(), true); +} + +void tst_QFiledialog::proxymodel() +{ + QNonNativeFileDialog fd; + QCOMPARE(fd.proxyModel(), (QAbstractProxyModel*)0); + + fd.setProxyModel(0); + QCOMPARE(fd.proxyModel(), (QAbstractProxyModel*)0); + + QSortFilterProxyModel *proxyModel = new QSortFilterProxyModel(&fd); + fd.setProxyModel(proxyModel); + QCOMPARE(fd.proxyModel(), (QAbstractProxyModel *)proxyModel); + + fd.setProxyModel(0); + QCOMPARE(fd.proxyModel(), (QAbstractProxyModel*)0); +} + +void tst_QFiledialog::setNameFilter() +{ + QNonNativeFileDialog fd; + fd.setFilter(QString()); + fd.setFilters(QStringList()); +} + +void tst_QFiledialog::focus() +{ + QNonNativeFileDialog fd; + fd.setDirectory(QDir::currentPath()); + fd.show(); + QApplication::setActiveWindow(&fd); + QTest::qWaitForWindowShown(&fd); + QTRY_COMPARE(fd.isVisible(), true); + QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget*>(&fd)); + qApp->processEvents(); + + // make sure the tests work with focus follows mouse + QCursor::setPos(fd.geometry().center()); + QApplication::syncX(); + + QList<QWidget*> treeView = qFindChildren<QWidget*>(&fd, "fileNameEdit"); + QCOMPARE(treeView.count(), 1); + QVERIFY(treeView.at(0)); + QTRY_COMPARE(treeView.at(0)->hasFocus(), true); + QCOMPARE(treeView.at(0)->hasFocus(), true); +} + + +void tst_QFiledialog::historyBack() +{ + QNonNativeFileDialog fd; + QFileSystemModel *model = qFindChild<QFileSystemModel*>(&fd, "qt_filesystem_model"); + QVERIFY(model); + QToolButton *backButton = qFindChild<QToolButton*>(&fd, "backButton"); + QVERIFY(backButton); + QToolButton *forwardButton = qFindChild<QToolButton*>(&fd, "forwardButton"); + QVERIFY(forwardButton); + + QSignalSpy spy(model, SIGNAL(rootPathChanged(const QString &))); + + QString home = fd.directory().absolutePath(); + QString desktop = QDir::homePath(); + QString temp = QDir::tempPath(); + + QCOMPARE(backButton->isEnabled(), false); + QCOMPARE(forwardButton->isEnabled(), false); + fd.setDirectory(temp); + qApp->processEvents(); + QCOMPARE(backButton->isEnabled(), true); + QCOMPARE(forwardButton->isEnabled(), false); + fd.setDirectory(desktop); + QCOMPARE(spy.count(), 2); + + backButton->click(); + qApp->processEvents(); + QCOMPARE(backButton->isEnabled(), true); + QCOMPARE(forwardButton->isEnabled(), true); + QCOMPARE(spy.count(), 3); + QString currentPath = qVariantValue<QString>(spy.last().first()); + QCOMPARE(model->index(currentPath), model->index(temp)); + + backButton->click(); + currentPath = qVariantValue<QString>(spy.last().first()); + QCOMPARE(currentPath, home); + QCOMPARE(backButton->isEnabled(), false); + QCOMPARE(forwardButton->isEnabled(), true); + QCOMPARE(spy.count(), 4); + + // nothing should change at this point + backButton->click(); + QCOMPARE(spy.count(), 4); + QCOMPARE(backButton->isEnabled(), false); + QCOMPARE(forwardButton->isEnabled(), true); +} + +void tst_QFiledialog::historyForward() +{ + QNonNativeFileDialog fd; + fd.setDirectory(QDir::currentPath()); + QToolButton *backButton = qFindChild<QToolButton*>(&fd, "backButton"); + QVERIFY(backButton); + QToolButton *forwardButton = qFindChild<QToolButton*>(&fd, "forwardButton"); + QVERIFY(forwardButton); + + QFileSystemModel *model = qFindChild<QFileSystemModel*>(&fd, "qt_filesystem_model"); + QVERIFY(model); + QSignalSpy spy(model, SIGNAL(rootPathChanged(const QString &))); + + QString home = fd.directory().absolutePath(); + QString desktop = QDir::homePath(); + QString temp = QDir::tempPath(); + + fd.setDirectory(home); + fd.setDirectory(temp); + fd.setDirectory(desktop); + + backButton->click(); + QCOMPARE(forwardButton->isEnabled(), true); + QCOMPARE(model->index(qVariantValue<QString>(spy.last().first())), model->index(temp)); + + forwardButton->click(); + QCOMPARE(model->index(qVariantValue<QString>(spy.last().first())), model->index(desktop)); + QCOMPARE(backButton->isEnabled(), true); + QCOMPARE(forwardButton->isEnabled(), false); + QCOMPARE(spy.count(), 4); + + backButton->click(); + QCOMPARE(model->index(qVariantValue<QString>(spy.last().first())), model->index(temp)); + QCOMPARE(backButton->isEnabled(), true); + + backButton->click(); + QCOMPARE(model->index(qVariantValue<QString>(spy.last().first())), model->index(home)); + QCOMPARE(backButton->isEnabled(), false); + QCOMPARE(forwardButton->isEnabled(), true); + QCOMPARE(spy.count(), 6); + + forwardButton->click(); + QCOMPARE(model->index(qVariantValue<QString>(spy.last().first())), model->index(temp)); + backButton->click(); + QCOMPARE(model->index(qVariantValue<QString>(spy.last().first())), model->index(home)); + QCOMPARE(spy.count(), 8); + + forwardButton->click(); + QCOMPARE(model->index(qVariantValue<QString>(spy.last().first())), model->index(temp)); + forwardButton->click(); + QCOMPARE(model->index(qVariantValue<QString>(spy.last().first())), model->index(desktop)); + + backButton->click(); + QCOMPARE(model->index(qVariantValue<QString>(spy.last().first())), model->index(temp)); + backButton->click(); + QCOMPARE(model->index(qVariantValue<QString>(spy.last().first())), model->index(home)); + fd.setDirectory(desktop); + QCOMPARE(forwardButton->isEnabled(), false); +} + +void tst_QFiledialog::disableSaveButton_data() +{ + QTest::addColumn<QString>("path"); + QTest::addColumn<bool>("isEnabled"); + + QTest::newRow("valid path") << QDir::temp().absolutePath() + QDir::separator() + "qfiledialog.new_file" << true; + QTest::newRow("no path") << "" << false; +#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC) && !defined(Q_OS_OPENBSD) + QTest::newRow("too long path") << "iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii" << false; +#endif + QTest::newRow("file") << "foo.html" << true; +} + +void tst_QFiledialog::disableSaveButton() +{ + QFETCH(QString, path); + QFETCH(bool, isEnabled); + + QNonNativeFileDialog fd(0, "caption", path); + fd.setAcceptMode(QFileDialog::AcceptSave); + QDialogButtonBox *buttonBox = qFindChild<QDialogButtonBox*>(&fd, "buttonBox"); + QPushButton *button = buttonBox->button(QDialogButtonBox::Save); + QVERIFY(button); + QCOMPARE(button->isEnabled(), isEnabled); +} + +void tst_QFiledialog::saveButtonText_data() +{ + QTest::addColumn<QString>("path"); + QTest::addColumn<QString>("label"); + QTest::addColumn<QString>("caption"); + + QTest::newRow("empty path") << "" << QString() << QFileDialog::tr("&Save"); + QTest::newRow("file path") << "qfiledialog.new_file" << QString() << QFileDialog::tr("&Save"); + QTest::newRow("dir") << QDir::temp().absolutePath() << QString() << QFileDialog::tr("&Open"); + QTest::newRow("setTextLabel") << "qfiledialog.new_file" << "Mooo" << "Mooo"; + QTest::newRow("dir & label") << QDir::temp().absolutePath() << "Poo" << QFileDialog::tr("&Open"); +} + +void tst_QFiledialog::saveButtonText() +{ + QFETCH(QString, path); + QFETCH(QString, label); + QFETCH(QString, caption); + + QNonNativeFileDialog fd(0, "auto test", QDir::temp().absolutePath()); + fd.setAcceptMode(QFileDialog::AcceptSave); + if (!label.isNull()) + fd.setLabelText(QFileDialog::Accept, label); + fd.setDirectory(QDir::temp()); + fd.selectFile(path); + QDialogButtonBox *buttonBox = qFindChild<QDialogButtonBox*>(&fd, "buttonBox"); + QVERIFY(buttonBox); + QPushButton *button = buttonBox->button(QDialogButtonBox::Save); + QVERIFY(button); + QCOMPARE(button->text(), caption); +} + +void tst_QFiledialog::clearLineEdit() +{ + QNonNativeFileDialog fd(0, "caption", "foo"); + fd.setViewMode(QFileDialog::List); + fd.setFileMode(QFileDialog::AnyFile); + fd.setOptions(QFileDialog::DontUseNativeDialog); + fd.show(); + + //play it really safe by creating a directory + QDir::home().mkdir("_____aaaaaaaaaaaaaaaaaaaaaa"); + + QLineEdit *lineEdit = qFindChild<QLineEdit*>(&fd, "fileNameEdit"); + QVERIFY(lineEdit); + QVERIFY(lineEdit->text() == "foo"); + fd.setDirectory(QDir::home()); + + QListView* list = qFindChild<QListView*>(&fd, "listView"); + QVERIFY(list); + + // saving a file the text shouldn't be cleared + fd.setDirectory(QDir::home()); + + QTest::qWait(1000); +#ifdef QT_KEYPAD_NAVIGATION + list->setEditFocus(true); +#endif + QTest::keyClick(list, Qt::Key_Down); +#ifndef Q_WS_MAC + QTest::keyClick(list, Qt::Key_Return); +#else + QTest::keyClick(list, Qt::Key_O, Qt::ControlModifier); +#endif + + QTest::qWait(2000); + QVERIFY(fd.directory().absolutePath() != QDir::home().absolutePath()); + QVERIFY(!lineEdit->text().isEmpty()); + + // selecting a dir the text should be cleared so one can just hit ok + // and it selects that directory + fd.setFileMode(QNonNativeFileDialog::Directory); + fd.setDirectory(QDir::home()); + + QTest::qWait(1000); + QTest::keyClick(list, Qt::Key_Down); +#ifndef Q_WS_MAC + QTest::keyClick(list, Qt::Key_Return); +#else + QTest::keyClick(list, Qt::Key_O, Qt::ControlModifier); +#endif + + QTest::qWait(2000); + QVERIFY(fd.directory().absolutePath() != QDir::home().absolutePath()); + QVERIFY(lineEdit->text().isEmpty()); + + //remove the dir + QDir::home().rmdir("_____aaaaaaaaaaaaaaaaaaaaaa"); +} + +void tst_QFiledialog::enableChooseButton() +{ + QNonNativeFileDialog fd; + fd.setFileMode(QFileDialog::Directory); + fd.show(); + QDialogButtonBox *buttonBox = qFindChild<QDialogButtonBox*>(&fd, "buttonBox"); + QPushButton *button = buttonBox->button(QDialogButtonBox::Open); + QVERIFY(button); + QCOMPARE(button->isEnabled(), true); +} + +QT_BEGIN_NAMESPACE +typedef QString (*_qt_filedialog_existing_directory_hook)(QWidget *parent, const QString &caption, const QString &dir, QFileDialog::Options options); +extern Q_GUI_EXPORT _qt_filedialog_existing_directory_hook qt_filedialog_existing_directory_hook; +QT_END_NAMESPACE +QString existing(QWidget *, const QString &, const QString &, QFileDialog::Options) { + return "dir"; +} + +QT_BEGIN_NAMESPACE +typedef QString (*_qt_filedialog_open_filename_hook)(QWidget * parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options); +extern Q_GUI_EXPORT _qt_filedialog_open_filename_hook qt_filedialog_open_filename_hook; +QT_END_NAMESPACE +QString openName(QWidget *, const QString &, const QString &, const QString &, QString *, QFileDialog::Options) { + return "openName"; +} + +QT_BEGIN_NAMESPACE +typedef QStringList (*_qt_filedialog_open_filenames_hook)(QWidget * parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options); +extern Q_GUI_EXPORT _qt_filedialog_open_filenames_hook qt_filedialog_open_filenames_hook; +QT_END_NAMESPACE +QStringList openNames(QWidget *, const QString &, const QString &, const QString &, QString *, QFileDialog::Options) { + return QStringList("openNames"); +} + +QT_BEGIN_NAMESPACE +typedef QString (*_qt_filedialog_save_filename_hook)(QWidget * parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options); +extern Q_GUI_EXPORT _qt_filedialog_save_filename_hook qt_filedialog_save_filename_hook; +QT_END_NAMESPACE +QString saveName(QWidget *, const QString &, const QString &, const QString &, QString *, QFileDialog::Options) { + return "saveName"; +} + + +void tst_QFiledialog::hooks() +{ + qt_filedialog_existing_directory_hook = &existing; + qt_filedialog_save_filename_hook = &saveName; + qt_filedialog_open_filename_hook = &openName; + qt_filedialog_open_filenames_hook = &openNames; + + QCOMPARE(QFileDialog::getExistingDirectory(), QString("dir")); + QCOMPARE(QFileDialog::getOpenFileName(), QString("openName")); + QCOMPARE(QFileDialog::getOpenFileNames(), QStringList("openNames")); + QCOMPARE(QFileDialog::getSaveFileName(), QString("saveName")); +} + +// Test case relies on developer build (AUTOTEST_EXPORT). +#if defined(Q_OS_UNIX) && defined(QT_BUILD_INTERNAL) +void tst_QFiledialog::tildeExpansion_data() +{ + QTest::addColumn<QString>("tildePath"); + QTest::addColumn<QString>("expandedPath"); + + QTest::newRow("empty path") << QString() << QString(); + QTest::newRow("~") << QString::fromLatin1("~") << QDir::homePath(); + QTest::newRow("~/some/sub/dir/") << QString::fromLatin1("~/some/sub/dir") << QDir::homePath() + + QString::fromLatin1("/some/sub/dir"); + QString userHome = QString(qgetenv("USER")); + userHome.prepend('~'); + QTest::newRow("current user (~<user> syntax)") << userHome << QDir::homePath(); + QString invalid = QString::fromLatin1("~thisIsNotAValidUserName"); + QTest::newRow("invalid user name") << invalid << invalid; +} + +void tst_QFiledialog::tildeExpansion() +{ + QFETCH(QString, tildePath); + QFETCH(QString, expandedPath); + + QCOMPARE(qt_tildeExpansion(tildePath), expandedPath); +} +#endif + +QTEST_MAIN(tst_QFiledialog) +#include "tst_qfiledialog.moc" diff --git a/tests/auto/widgets/dialogs/qfiledialog2/qfiledialog2.pro b/tests/auto/widgets/dialogs/qfiledialog2/qfiledialog2.pro new file mode 100644 index 0000000000..a2149c8a39 --- /dev/null +++ b/tests/auto/widgets/dialogs/qfiledialog2/qfiledialog2.pro @@ -0,0 +1,20 @@ +load(qttest_p4) + +QT += widgets widgets-private +QT += core-private gui-private + +SOURCES += tst_qfiledialog2.cpp + +wince* { + addFiles.files = *.cpp + addFiles.path = . + filesInDir.files = *.pro + filesInDir.path = someDir + DEPLOYMENT += addFiles filesInDir +} + +wince* { + DEFINES += SRCDIR=\\\"./\\\" +} else { + DEFINES += SRCDIR=\\\"$$PWD/\\\" +} diff --git a/tests/auto/widgets/dialogs/qfiledialog2/tst_qfiledialog2.cpp b/tests/auto/widgets/dialogs/qfiledialog2/tst_qfiledialog2.cpp new file mode 100644 index 0000000000..a2d18eb847 --- /dev/null +++ b/tests/auto/widgets/dialogs/qfiledialog2/tst_qfiledialog2.cpp @@ -0,0 +1,1213 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtTest/QtTest> + +#include <qcoreapplication.h> +#include <qdebug.h> +#include <qfiledialog.h> +#include <qabstractitemdelegate.h> +#include <qdirmodel.h> +#include <qitemdelegate.h> +#include <qlistview.h> +#include <qcombobox.h> +#include <qpushbutton.h> +#include <qtoolbutton.h> +#include <qtreeview.h> +#include <qheaderview.h> +#include <qcompleter.h> +#include <qaction.h> +#include <qdialogbuttonbox.h> +#include <qsortfilterproxymodel.h> +#include <qlineedit.h> +#include <qlayout.h> +#include "../../../../../src/widgets/dialogs/qsidebar_p.h" +#include "../../../../../src/widgets/dialogs/qfilesystemmodel_p.h" +#include "../../../../../src/widgets/dialogs/qfiledialog_p.h" + +#include "../../../network-settings.h" + +//TESTED_CLASS= +//TESTED_FILES= + +#if defined QT_BUILD_INTERNAL +QT_BEGIN_NAMESPACE +Q_GUI_EXPORT bool qt_test_isFetchedRoot(); +Q_GUI_EXPORT void qt_test_resetFetchedRoot(); +QT_END_NAMESPACE +#endif + +class QNonNativeFileDialog : public QFileDialog +{ + Q_OBJECT +public: + QNonNativeFileDialog(QWidget *parent = 0, const QString &caption = QString(), const QString &directory = QString(), const QString &filter = QString()) + : QFileDialog(parent, caption, directory, filter) + { + setOption(QFileDialog::DontUseNativeDialog, true); + } +}; + +class tst_QFileDialog2 : public QObject +{ +Q_OBJECT + +public: + tst_QFileDialog2(); + virtual ~tst_QFileDialog2(); + +public slots: + void init(); + void cleanup(); + +private slots: + void listRoot(); + void heapCorruption(); + void deleteDirAndFiles(); + void filter(); + void showNameFilterDetails(); + void unc(); + void emptyUncPath(); + + void task178897_minimumSize(); + void task180459_lastDirectory_data(); + void task180459_lastDirectory(); + void task227304_proxyOnFileDialog(); + void task227930_correctNavigationKeyboardBehavior(); +#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) + void task226366_lowerCaseHardDriveWindows(); +#endif + void completionOnLevelAfterRoot(); + void task233037_selectingDirectory(); + void task235069_hideOnEscape(); + void task236402_dontWatchDeletedDir(); + void task203703_returnProperSeparator(); + void task228844_ensurePreviousSorting(); + void task239706_editableFilterCombo(); + void task218353_relativePaths(); + void task251321_sideBarHiddenEntries(); + void task251341_sideBarRemoveEntries(); + void task254490_selectFileMultipleTimes(); + void task257579_sideBarWithNonCleanUrls(); + void task259105_filtersCornerCases(); + + void QTBUG4419_lineEditSelectAll(); + void QTBUG6558_showDirsOnly(); + void QTBUG4842_selectFilterWithHideNameFilterDetails(); + void dontShowCompleterOnRoot(); + +private: + QByteArray userSettings; +}; + +tst_QFileDialog2::tst_QFileDialog2() +{ +#if defined(Q_OS_WINCE) + qApp->setAutoMaximizeThreshold(-1); +#endif +} + +tst_QFileDialog2::~tst_QFileDialog2() +{ +} + +void tst_QFileDialog2::init() +{ + // Save the developers settings so they don't get mad when their sidebar folders are gone. + QSettings settings(QSettings::UserScope, QLatin1String("Trolltech")); + settings.beginGroup(QLatin1String("Qt")); + userSettings = settings.value(QLatin1String("filedialog")).toByteArray(); + settings.remove(QLatin1String("filedialog")); + + // populate it with some default settings + QNonNativeFileDialog fd; +#if defined(Q_OS_WINCE) + QTest::qWait(1000); +#endif +} + +void tst_QFileDialog2::cleanup() +{ + QSettings settings(QSettings::UserScope, QLatin1String("Trolltech")); + settings.beginGroup(QLatin1String("Qt")); + settings.setValue(QLatin1String("filedialog"), userSettings); +} + +void tst_QFileDialog2::listRoot() +{ +#if defined QT_BUILD_INTERNAL + QFileInfoGatherer fileInfoGatherer; + fileInfoGatherer.start(); + QTest::qWait(1500); + qt_test_resetFetchedRoot(); + QString dir(QDir::currentPath()); + QNonNativeFileDialog fd(0, QString(), dir); + fd.show(); + QCOMPARE(qt_test_isFetchedRoot(),false); + fd.setDirectory(""); +#ifdef Q_OS_WINCE + QTest::qWait(1500); +#else + QTest::qWait(500); +#endif + QCOMPARE(qt_test_isFetchedRoot(),true); +#endif +} + +void tst_QFileDialog2::heapCorruption() +{ + QVector<QNonNativeFileDialog*> dialogs; + for (int i=0; i < 10; i++) { + QNonNativeFileDialog *f = new QNonNativeFileDialog(NULL); + dialogs << f; + } + qDeleteAll(dialogs); +} + +struct FriendlyQFileDialog : public QNonNativeFileDialog +{ + friend class tst_QFileDialog2; + Q_DECLARE_PRIVATE(QFileDialog) +}; + + +void tst_QFileDialog2::deleteDirAndFiles() +{ +#if defined QT_BUILD_INTERNAL + QString tempPath = QDir::tempPath() + '/' + "QFileDialogTestDir4FullDelete"; + QDir dir; + QVERIFY(dir.mkpath(tempPath + "/foo")); + QVERIFY(dir.mkpath(tempPath + "/foo/B")); + QVERIFY(dir.mkpath(tempPath + "/foo/B")); + QVERIFY(dir.mkpath(tempPath + "/foo/c")); + QVERIFY(dir.mkpath(tempPath + "/bar")); + QFile(tempPath + "/foo/a"); + QTemporaryFile *t; + t = new QTemporaryFile(tempPath + "/foo/aXXXXXX"); + t->setAutoRemove(false); + t->open(); + t->close(); + delete t; + + t = new QTemporaryFile(tempPath + "/foo/B/yXXXXXX"); + t->setAutoRemove(false); + t->open(); + t->close(); + delete t; + FriendlyQFileDialog fd; + fd.setOption(QFileDialog::DontUseNativeDialog); + fd.d_func()->removeDirectory(tempPath); + QFileInfo info(tempPath); + QTest::qWait(2000); + QVERIFY(!info.exists()); +#endif +} + +void tst_QFileDialog2::filter() +{ + QNonNativeFileDialog fd; + QAction *hiddenAction = qFindChild<QAction*>(&fd, "qt_show_hidden_action"); + QVERIFY(hiddenAction); + QVERIFY(hiddenAction->isEnabled()); + QVERIFY(!hiddenAction->isChecked()); + QDir::Filters filter = fd.filter(); + filter |= QDir::Hidden; + fd.setFilter(filter); + QVERIFY(hiddenAction->isChecked()); +} + +void tst_QFileDialog2::showNameFilterDetails() +{ + QNonNativeFileDialog fd; + QComboBox *filters = qFindChild<QComboBox*>(&fd, "fileTypeCombo"); + QVERIFY(filters); + QVERIFY(fd.isNameFilterDetailsVisible()); + + + QStringList filterChoices; + filterChoices << "Image files (*.png *.xpm *.jpg)" + << "Text files (*.txt)" + << "Any files (*.*)"; + fd.setFilters(filterChoices); + + fd.setNameFilterDetailsVisible(false); + QCOMPARE(filters->itemText(0), QString("Image files")); + QCOMPARE(filters->itemText(1), QString("Text files")); + QCOMPARE(filters->itemText(2), QString("Any files")); + + fd.setNameFilterDetailsVisible(true); + QCOMPARE(filters->itemText(0), filterChoices.at(0)); + QCOMPARE(filters->itemText(1), filterChoices.at(1)); + QCOMPARE(filters->itemText(2), filterChoices.at(2)); +} + +void tst_QFileDialog2::unc() +{ +#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) + // Only test UNC on Windows./ + QString dir("\\\\" + QtNetworkSettings::winServerName() + "\\testsharewritable"); +#else + QString dir(QDir::currentPath()); +#endif + QVERIFY(QFile::exists(dir)); + QNonNativeFileDialog fd(0, QString(), dir); + QFileSystemModel *model = qFindChild<QFileSystemModel*>(&fd, "qt_filesystem_model"); + QVERIFY(model); + QCOMPARE(model->index(fd.directory().absolutePath()), model->index(dir)); +} + +void tst_QFileDialog2::emptyUncPath() +{ + QNonNativeFileDialog fd; + fd.show(); + QLineEdit *lineEdit = qFindChild<QLineEdit*>(&fd, "fileNameEdit"); + QVERIFY(lineEdit); + // press 'keys' for the input + for (int i = 0; i < 3 ; ++i) + QTest::keyPress(lineEdit, Qt::Key_Backslash); + QFileSystemModel *model = qFindChild<QFileSystemModel*>(&fd, "qt_filesystem_model"); + QVERIFY(model); +} + +void tst_QFileDialog2::task178897_minimumSize() +{ + QNonNativeFileDialog fd; + QSize oldMs = fd.layout()->minimumSize(); + QStringList history = fd.history(); + history << QDir::toNativeSeparators("/verylongdirectory/" + "aaaaaaaaaabbbbbbbbcccccccccccddddddddddddddeeeeeeeeeeeeffffffffffgggtggggggggghhhhhhhhiiiiiijjjk"); + fd.setHistory(history); + fd.show(); + + QSize ms = fd.layout()->minimumSize(); + QVERIFY(ms.width() <= oldMs.width()); +} + +void tst_QFileDialog2::task180459_lastDirectory_data() +{ + QTest::addColumn<QString>("path"); + QTest::addColumn<QString>("directory"); + QTest::addColumn<bool>("isEnabled"); + QTest::addColumn<QString>("result"); + + QTest::newRow("path+file") << QDir::homePath() + QDir::separator() + "foo" + << QDir::homePath() << true + << QDir::homePath() + QDir::separator() + "foo" ; + QTest::newRow("no path") << "" + << QDir::tempPath() << false << QString(); + QTest::newRow("file") << "foo" + << QDir::currentPath() << true + << QDir::currentPath() + QDir::separator() + "foo" ; + QTest::newRow("path") << QDir::homePath() + << QDir::homePath() << false << QString(); + QTest::newRow("path not existing") << "/usr/bin/foo/bar/foo/foo.txt" + << QDir::tempPath() << true + << QDir::tempPath() + QDir::separator() + "foo.txt"; + +} + +void tst_QFileDialog2::task180459_lastDirectory() +{ + //first visit the temp directory and close the dialog + QNonNativeFileDialog *dlg = new QNonNativeFileDialog(0, "", QDir::tempPath()); + QFileSystemModel *model = qFindChild<QFileSystemModel*>(dlg, "qt_filesystem_model"); + QVERIFY(model); + QCOMPARE(model->index(QDir::tempPath()), model->index(dlg->directory().absolutePath())); + delete dlg; + + QFETCH(QString, path); + QFETCH(QString, directory); + QFETCH(bool, isEnabled); + QFETCH(QString, result); + + dlg = new QNonNativeFileDialog(0, "", path); + model = qFindChild<QFileSystemModel*>(dlg, "qt_filesystem_model"); + QVERIFY(model); + dlg->setAcceptMode(QFileDialog::AcceptSave); + QCOMPARE(model->index(dlg->directory().absolutePath()), model->index(directory)); + + QDialogButtonBox *buttonBox = qFindChild<QDialogButtonBox*>(dlg, "buttonBox"); + QPushButton *button = buttonBox->button(QDialogButtonBox::Save); + QVERIFY(button); + QCOMPARE(button->isEnabled(), isEnabled); + if (isEnabled) + QCOMPARE(model->index(result), model->index(dlg->selectedFiles().first())); + + delete dlg; +} + + + +class FilterDirModel : public QSortFilterProxyModel +{ + +public: + FilterDirModel(QString root, QObject* parent=0):QSortFilterProxyModel(parent), m_root(root) + {} + ~FilterDirModel() + {}; + +protected: + bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const + { + QModelIndex parentIndex; + parentIndex = source_parent; + + QString path; + path = parentIndex.child(source_row,0).data(Qt::DisplayRole).toString(); + + do { + path = parentIndex.data(Qt::DisplayRole).toString() + "/" + path; + parentIndex = parentIndex.parent(); + } while(parentIndex.isValid()); + + QFileInfo info(path); + if (info.isDir() && (QDir(path) != m_root)) + return false; + return true; + } + + +private: + QDir m_root; + + +}; + +class sortProxy : public QSortFilterProxyModel +{ +public: + sortProxy(QObject *parent) : QSortFilterProxyModel(parent) + { + } +protected: + virtual bool lessThan(const QModelIndex &left, const QModelIndex &right) const + { + QFileSystemModel * const model = qobject_cast<QFileSystemModel *>(sourceModel()); + const QFileInfo leftInfo(model->fileInfo(left)); + const QFileInfo rightInfo(model->fileInfo(right)); + + if (leftInfo.isDir() == rightInfo.isDir()) + return(leftInfo.filePath().compare(rightInfo.filePath(),Qt::CaseInsensitive) < 0); + else if (leftInfo.isDir()) + return(false); + else + return(true); + } +}; + +class CrashDialog : public QNonNativeFileDialog +{ + Q_OBJECT + +public: + CrashDialog(QWidget *parent, const QString &caption, const +QString &dir, const QString &filter) + : QNonNativeFileDialog(parent, caption, dir, filter) + { + sortProxy *proxyModel = new sortProxy(this); + setProxyModel(proxyModel); + } +}; + +void tst_QFileDialog2::task227304_proxyOnFileDialog() +{ +#if defined QT_BUILD_INTERNAL + QNonNativeFileDialog fd(0, "", QDir::currentPath(), 0); + fd.setProxyModel(new FilterDirModel(QDir::currentPath())); + fd.show(); + QLineEdit *edit = qFindChild<QLineEdit*>(&fd, "fileNameEdit"); + QTest::qWait(200); + QTest::keyClick(edit, Qt::Key_T); + QTest::keyClick(edit, Qt::Key_S); + QTest::qWait(200); + QTest::keyClick(edit->completer()->popup(), Qt::Key_Down); + + CrashDialog *dialog = new CrashDialog(0, QString("crash dialog test"), QDir::homePath(), QString("*") ); + dialog->setFileMode(QFileDialog::ExistingFile); + dialog->show(); + + QListView *list = qFindChild<QListView*>(dialog, "listView"); + QTest::qWait(200); + QTest::keyClick(list, Qt::Key_Down); + QTest::keyClick(list, Qt::Key_Return); + QTest::qWait(200); + + dialog->close(); + fd.close(); + + QNonNativeFileDialog fd2(0, "I should not crash with a proxy", QDir::tempPath(), 0); + QSortFilterProxyModel *pm = new QSortFilterProxyModel; + fd2.setProxyModel(pm); + fd2.show(); + QSidebar *sidebar = qFindChild<QSidebar*>(&fd2, "sidebar"); + sidebar->setFocus(); + sidebar->selectUrl(QUrl::fromLocalFile(QDir::homePath())); + QTest::mouseClick(sidebar->viewport(), Qt::LeftButton, 0, sidebar->visualRect(sidebar->model()->index(1, 0)).center()); + QTest::qWait(250); + //We shouldn't crash +#endif +} + +void tst_QFileDialog2::task227930_correctNavigationKeyboardBehavior() +{ + QDir current = QDir::currentPath(); + current.mkdir("test"); + current.cd("test"); + QFile file("test/out.txt"); + QFile file2("test/out2.txt"); + QVERIFY(file.open(QIODevice::WriteOnly | QIODevice::Text)); + QVERIFY(file2.open(QIODevice::WriteOnly | QIODevice::Text)); + current.cdUp(); + current.mkdir("test2"); + QNonNativeFileDialog fd; + fd.setViewMode(QFileDialog::List); + fd.setDirectory(current.absolutePath()); + fd.show(); + QListView *list = qFindChild<QListView*>(&fd, "listView"); + QTest::qWait(200); + QTest::keyClick(list, Qt::Key_Down); + QTest::keyClick(list, Qt::Key_Return); + QTest::qWait(200); + QTest::mouseClick(list->viewport(), Qt::LeftButton,0); + QTest::keyClick(list, Qt::Key_Down); + QTest::keyClick(list, Qt::Key_Backspace); + QTest::qWait(200); + QTest::keyClick(list, Qt::Key_Down); + QTest::keyClick(list, Qt::Key_Down); + QTest::keyClick(list, Qt::Key_Return); + QTest::qWait(200); + QCOMPARE(fd.isVisible(), true); + QTest::qWait(200); + file.close(); + file2.close(); + file.remove(); + file2.remove(); + current.rmdir("test"); + current.rmdir("test2"); +} + +#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) +void tst_QFileDialog2::task226366_lowerCaseHardDriveWindows() +{ + QNonNativeFileDialog fd; + fd.setDirectory(QDir::root().path()); + fd.show(); + QLineEdit *edit = qFindChild<QLineEdit*>(&fd, "fileNameEdit"); + QToolButton *buttonParent = qFindChild<QToolButton*>(&fd, "toParentButton"); + QTest::qWait(200); + QTest::mouseClick(buttonParent, Qt::LeftButton,0,QPoint(0,0)); + QTest::qWait(2000); + QTest::keyClick(edit, Qt::Key_C); + QTest::qWait(200); + QTest::keyClick(edit->completer()->popup(), Qt::Key_Down); + QTest::qWait(200); + QCOMPARE(edit->text(), QString("C:/")); + QTest::qWait(2000); + //i clear my previous selection in the completer + QTest::keyClick(edit->completer()->popup(), Qt::Key_Down); + edit->clear(); + QTest::keyClick(edit, (char)(Qt::Key_C | Qt::SHIFT)); + QTest::qWait(200); + QTest::keyClick(edit->completer()->popup(), Qt::Key_Down); + QCOMPARE(edit->text(), QString("C:/")); +} +#endif + +void tst_QFileDialog2::completionOnLevelAfterRoot() +{ + QNonNativeFileDialog fd; +#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) + fd.setDirectory("C:"); + QDir current = fd.directory(); + current.mkdir("completionOnLevelAfterRootTest"); +#else + fd.setFilter(QDir::Hidden | QDir::AllDirs | QDir::Files | QDir::System); + fd.setDirectory("/"); + QDir etc("/etc"); + if (!etc.exists()) + QSKIP("This test requires to have an etc directory under /", SkipAll); +#endif + fd.show(); + QLineEdit *edit = qFindChild<QLineEdit*>(&fd, "fileNameEdit"); + QTest::qWait(2000); +#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) + //I love testlib :D + QTest::keyClick(edit, Qt::Key_C); + QTest::keyClick(edit, Qt::Key_O); + QTest::keyClick(edit, Qt::Key_M); + QTest::keyClick(edit, Qt::Key_P); + QTest::keyClick(edit, Qt::Key_L); +#else + QTest::keyClick(edit, Qt::Key_E); + QTest::keyClick(edit, Qt::Key_T); +#endif + QTest::qWait(200); + QTest::keyClick(edit->completer()->popup(), Qt::Key_Down); + QTest::qWait(200); +#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) + QCOMPARE(edit->text(), QString("completionOnLevelAfterRootTest")); + current.rmdir("completionOnLevelAfterRootTest"); +#else + QTRY_COMPARE(edit->text(), QString("etc")); +#endif +} + +void tst_QFileDialog2::task233037_selectingDirectory() +{ + QDir current = QDir::currentPath(); + current.mkdir("test"); + QNonNativeFileDialog fd; + fd.setViewMode(QFileDialog::List); + fd.setDirectory(current.absolutePath()); + fd.setAcceptMode( QFileDialog::AcceptSave); + fd.show(); + QListView *list = qFindChild<QListView*>(&fd, "listView"); + QTest::qWait(3000); // Wait for sort to settle (I need a signal). +#ifdef QT_KEYPAD_NAVIGATION + list->setEditFocus(true); +#endif + QTest::keyClick(list, Qt::Key_Down); + QTest::qWait(100); + QDialogButtonBox *buttonBox = qFindChild<QDialogButtonBox*>(&fd, "buttonBox"); + QPushButton *button = buttonBox->button(QDialogButtonBox::Save); + QVERIFY(button); + QCOMPARE(button->isEnabled(), true); + current.rmdir("test"); +} + +void tst_QFileDialog2::task235069_hideOnEscape() +{ + QDir current = QDir::currentPath(); + QNonNativeFileDialog fd; + fd.setViewMode(QFileDialog::List); + fd.setDirectory(current.absolutePath()); + fd.setAcceptMode( QFileDialog::AcceptSave); + fd.show(); + QListView *list = qFindChild<QListView*>(&fd, "listView"); + list->setFocus(); + QTest::qWait(200); + QTest::keyClick(list, Qt::Key_Escape); + QCOMPARE(fd.isVisible(), false); + QNonNativeFileDialog fd2; + fd2.setDirectory(current.absolutePath()); + fd2.setAcceptMode( QFileDialog::AcceptSave); + fd2.show(); + QLineEdit *edit = qFindChild<QLineEdit*>(&fd2, "fileNameEdit"); + QTest::keyClick(edit, Qt::Key_Escape); + QCOMPARE(fd2.isVisible(), false); +} + +void tst_QFileDialog2::task236402_dontWatchDeletedDir() +{ +#if defined QT_BUILD_INTERNAL + //THIS TEST SHOULD NOT DISPLAY WARNINGS + QDir current = QDir::currentPath(); + //make sure it is the first on the list + current.mkdir("aaaaaaaaaa"); + FriendlyQFileDialog fd; + fd.setViewMode(QFileDialog::List); + fd.setDirectory(current.absolutePath()); + fd.setAcceptMode( QFileDialog::AcceptSave); + fd.show(); + QListView *list = qFindChild<QListView*>(&fd, "listView"); + list->setFocus(); + QTest::qWait(200); + QTest::keyClick(list, Qt::Key_Return); + QTest::qWait(200); + QTest::keyClick(list, Qt::Key_Backspace); + QTest::keyClick(list, Qt::Key_Down); + QTest::qWait(200); + fd.d_func()->removeDirectory(current.absolutePath() + "/aaaaaaaaaa/"); + QTest::qWait(1000); +#endif +} + +void tst_QFileDialog2::task203703_returnProperSeparator() +{ + QDir current = QDir::currentPath(); + current.mkdir("aaaaaaaaaaaaaaaaaa"); + QNonNativeFileDialog fd; + fd.setDirectory(current.absolutePath()); + fd.setViewMode(QFileDialog::List); + fd.setFileMode(QFileDialog::Directory); + fd.show(); + QTest::qWait(500); + QListView *list = qFindChild<QListView*>(&fd, "listView"); + list->setFocus(); + QTest::qWait(200); + QTest::keyClick(list, Qt::Key_Return); + QTest::qWait(1000); + QDialogButtonBox *buttonBox = qFindChild<QDialogButtonBox*>(&fd, "buttonBox"); + QPushButton *button = buttonBox->button(QDialogButtonBox::Cancel); + QTest::keyClick(button, Qt::Key_Return); + QTest::qWait(500); + QString result = fd.selectedFiles().first(); + QVERIFY(result.at(result.count() - 1) != '/'); + QVERIFY(!result.contains('\\')); + current.rmdir("aaaaaaaaaaaaaaaaaa"); +} + +void tst_QFileDialog2::task228844_ensurePreviousSorting() +{ + QDir current = QDir::currentPath(); + current.mkdir("aaaaaaaaaaaaaaaaaa"); + current.cd("aaaaaaaaaaaaaaaaaa"); + current.mkdir("a"); + current.mkdir("b"); + current.mkdir("c"); + current.mkdir("d"); + current.mkdir("e"); + current.mkdir("f"); + current.mkdir("g"); + QTemporaryFile *tempFile = new QTemporaryFile(current.absolutePath() + "/rXXXXXX"); + tempFile->open(); + current.cdUp(); + + QNonNativeFileDialog fd; + fd.setDirectory(current.absolutePath()); + fd.setViewMode(QFileDialog::Detail); + fd.show(); +#if defined(Q_OS_WINCE) + QTest::qWait(1500); +#else + QTest::qWait(500); +#endif + QTreeView *tree = qFindChild<QTreeView*>(&fd, "treeView"); + tree->header()->setSortIndicator(3,Qt::DescendingOrder); + QTest::qWait(200); + QDialogButtonBox *buttonBox = qFindChild<QDialogButtonBox*>(&fd, "buttonBox"); + QPushButton *button = buttonBox->button(QDialogButtonBox::Open); + QTest::mouseClick(button, Qt::LeftButton); +#if defined(Q_OS_WINCE) + QTest::qWait(1500); +#else + QTest::qWait(500); +#endif + QNonNativeFileDialog fd2; + fd2.setFileMode(QFileDialog::Directory); + fd2.restoreState(fd.saveState()); + current.cd("aaaaaaaaaaaaaaaaaa"); + fd2.setDirectory(current.absolutePath()); + fd2.show(); +#if defined(Q_OS_WINCE) + QTest::qWait(1500); +#else + QTest::qWait(500); +#endif + QTreeView *tree2 = qFindChild<QTreeView*>(&fd2, "treeView"); + tree2->setFocus(); + + QCOMPARE(tree2->rootIndex().data(QFileSystemModel::FilePathRole).toString(),current.absolutePath()); + + QDialogButtonBox *buttonBox2 = qFindChild<QDialogButtonBox*>(&fd2, "buttonBox"); + QPushButton *button2 = buttonBox2->button(QDialogButtonBox::Open); + fd2.selectFile("g"); + QTest::mouseClick(button2, Qt::LeftButton); +#if defined(Q_OS_WINCE) + QTest::qWait(1500); +#else + QTest::qWait(500); +#endif + QCOMPARE(fd2.selectedFiles().first(), current.absolutePath() + QChar('/') + QLatin1String("g")); + + QNonNativeFileDialog fd3(0, "This is a third file dialog", tempFile->fileName()); + fd3.restoreState(fd.saveState()); + fd3.setFileMode(QFileDialog::Directory); + fd3.show(); +#if defined(Q_OS_WINCE) + QTest::qWait(1500); +#else + QTest::qWait(500); +#endif + QTreeView *tree3 = qFindChild<QTreeView*>(&fd3, "treeView"); + tree3->setFocus(); + + QCOMPARE(tree3->rootIndex().data(QFileSystemModel::FilePathRole).toString(), current.absolutePath()); + + QDialogButtonBox *buttonBox3 = qFindChild<QDialogButtonBox*>(&fd3, "buttonBox"); + QPushButton *button3 = buttonBox3->button(QDialogButtonBox::Open); + QTest::mouseClick(button3, Qt::LeftButton); +#if defined(Q_OS_WINCE) + QTest::qWait(1500); +#else + QTest::qWait(500); +#endif + QCOMPARE(fd3.selectedFiles().first(), tempFile->fileName()); + + current.cd("aaaaaaaaaaaaaaaaaa"); + current.rmdir("a"); + current.rmdir("b"); + current.rmdir("c"); + current.rmdir("d"); + current.rmdir("e"); + current.rmdir("f"); + current.rmdir("g"); + tempFile->close(); + delete tempFile; + current.cdUp(); + current.rmdir("aaaaaaaaaaaaaaaaaa"); +} + + +void tst_QFileDialog2::task239706_editableFilterCombo() +{ + QNonNativeFileDialog d; + d.setNameFilter("*.cpp *.h"); + + d.show(); + QTest::qWait(500); + + QList<QComboBox *> comboList = d.findChildren<QComboBox *>(); + QComboBox *filterCombo = 0; + foreach (QComboBox *combo, comboList) { + if (combo->objectName() == QString("fileTypeCombo")) { + filterCombo = combo; + break; + } + } + QVERIFY(filterCombo); + filterCombo->setEditable(true); + QTest::mouseClick(filterCombo, Qt::LeftButton); + QTest::keyPress(filterCombo, Qt::Key_X); + QTest::keyPress(filterCombo, Qt::Key_Enter); // should not trigger assertion failure +} + +void tst_QFileDialog2::task218353_relativePaths() +{ + QDir appDir = QDir::current(); + QVERIFY(appDir.cdUp() != false); + QNonNativeFileDialog d(0, "TestDialog", ".."); + QCOMPARE(d.directory().absolutePath(), appDir.absolutePath()); + + d.setDirectory(appDir.absolutePath() + QLatin1String("/non-existing-directory/../another-non-existing-dir/../")); + QCOMPARE(d.directory().absolutePath(), appDir.absolutePath()); + + QDir::current().mkdir("test"); + appDir = QDir::current(); + d.setDirectory(appDir.absolutePath() + QLatin1String("/test/../test/../")); + QCOMPARE(d.directory().absolutePath(), appDir.absolutePath()); + appDir.rmdir("test"); +} + +void tst_QFileDialog2::task251321_sideBarHiddenEntries() +{ +#if defined QT_BUILD_INTERNAL + QNonNativeFileDialog fd; + + QDir current = QDir::currentPath(); + current.mkdir(".hidden"); + QDir hiddenDir = QDir(".hidden"); + hiddenDir.mkdir("subdir"); + QDir hiddenSubDir = QDir(".hidden/subdir"); + hiddenSubDir.mkdir("happy"); + hiddenSubDir.mkdir("happy2"); + + QList<QUrl> urls; + urls << QUrl::fromLocalFile(hiddenSubDir.absolutePath()); + fd.setSidebarUrls(urls); + fd.show(); + QTest::qWait(250); + + QSidebar *sidebar = qFindChild<QSidebar*>(&fd, "sidebar"); + sidebar->setFocus(); + sidebar->selectUrl(QUrl::fromLocalFile(hiddenSubDir.absolutePath())); + QTest::mouseClick(sidebar->viewport(), Qt::LeftButton, 0, sidebar->visualRect(sidebar->model()->index(0, 0)).center()); + // give the background processes more time on windows mobile +#ifdef Q_OS_WINCE + QTest::qWait(1000); +#else + QTest::qWait(250); +#endif + + QFileSystemModel *model = qFindChild<QFileSystemModel*>(&fd, "qt_filesystem_model"); + QCOMPARE(model->rowCount(model->index(hiddenSubDir.absolutePath())), 2); + + hiddenSubDir.rmdir("happy2"); + hiddenSubDir.rmdir("happy"); + hiddenDir.rmdir("subdir"); + current.rmdir(".hidden"); +#endif +} + +#if defined QT_BUILD_INTERNAL +class MyQSideBar : public QSidebar +{ +public : + MyQSideBar(QWidget *parent = 0) : QSidebar(parent) + {} + + void removeSelection() { + QList<QModelIndex> idxs = selectionModel()->selectedIndexes(); + QList<QPersistentModelIndex> indexes; + for (int i = 0; i < idxs.count(); i++) + indexes.append(idxs.at(i)); + + for (int i = 0; i < indexes.count(); ++i) + if (!indexes.at(i).data(Qt::UserRole + 1).toUrl().path().isEmpty()) + model()->removeRow(indexes.at(i).row()); + } +}; +#endif + +void tst_QFileDialog2::task251341_sideBarRemoveEntries() +{ +#if defined QT_BUILD_INTERNAL + QNonNativeFileDialog fd; + + QDir current = QDir::currentPath(); + current.mkdir("testDir"); + QDir testSubDir = QDir("testDir"); + + QList<QUrl> urls; + urls << QUrl::fromLocalFile(testSubDir.absolutePath()); + urls << QUrl::fromLocalFile("NotFound"); + fd.setSidebarUrls(urls); + fd.show(); + QTest::qWait(250); + + QSidebar *sidebar = qFindChild<QSidebar*>(&fd, "sidebar"); + sidebar->setFocus(); + //We enter in the first bookmark + sidebar->selectUrl(QUrl::fromLocalFile(testSubDir.absolutePath())); + QTest::mouseClick(sidebar->viewport(), Qt::LeftButton, 0, sidebar->visualRect(sidebar->model()->index(0, 0)).center()); + QTest::qWait(250); + + QFileSystemModel *model = qFindChild<QFileSystemModel*>(&fd, "qt_filesystem_model"); + //There is no file + QCOMPARE(model->rowCount(model->index(testSubDir.absolutePath())), 0); + //Icon is not enabled QUrlModel::EnabledRole + QVariant value = sidebar->model()->index(0, 0).data(Qt::UserRole + 2); + QCOMPARE(qvariant_cast<bool>(value), true); + + sidebar->setFocus(); + //We enter in the second bookmark which is invalid + sidebar->selectUrl(QUrl::fromLocalFile("NotFound")); + QTest::mouseClick(sidebar->viewport(), Qt::LeftButton, 0, sidebar->visualRect(sidebar->model()->index(1, 0)).center()); + QTest::qWait(250); + + //We fallback to root because the entry in the bookmark is invalid + QCOMPARE(model->rowCount(model->index("NotFound")), model->rowCount(model->index(model->rootPath()))); + //Icon is not enabled QUrlModel::EnabledRole + value = sidebar->model()->index(1, 0).data(Qt::UserRole + 2); + QCOMPARE(qvariant_cast<bool>(value), false); + + MyQSideBar mySideBar; + mySideBar.init(model, urls); + mySideBar.show(); + mySideBar.selectUrl(QUrl::fromLocalFile(testSubDir.absolutePath())); + QTest::qWait(1000); + mySideBar.removeSelection(); + + //We remove the first entry + QList<QUrl> expected; + expected << QUrl::fromLocalFile("NotFound"); + QCOMPARE(mySideBar.urls(), expected); + + mySideBar.selectUrl(QUrl::fromLocalFile("NotFound")); + mySideBar.removeSelection(); + + //We remove the second entry + expected.clear(); + QCOMPARE(mySideBar.urls(), expected); + + current.rmdir("testDir"); +#endif +} + +void tst_QFileDialog2::task254490_selectFileMultipleTimes() +{ + QString tempPath = QDir::tempPath(); + QTemporaryFile *t; + t = new QTemporaryFile; + t->open(); + QNonNativeFileDialog fd(0, "TestFileDialog"); + + fd.setDirectory(tempPath); + fd.setViewMode(QFileDialog::List); + fd.setAcceptMode(QFileDialog::AcceptSave); + fd.setFileMode(QFileDialog::AnyFile); + + //This should select the file in the QFileDialog + fd.selectFile(t->fileName()); + + //This should clear the selection and write it into the filename line edit + fd.selectFile("new_file.txt"); + + fd.show(); + QTest::qWait(250); + + QLineEdit *lineEdit = qFindChild<QLineEdit*>(&fd, "fileNameEdit"); + QVERIFY(lineEdit); + QCOMPARE(lineEdit->text(),QLatin1String("new_file.txt")); + QListView *list = qFindChild<QListView*>(&fd, "listView"); + QVERIFY(list); + QCOMPARE(list->selectionModel()->selectedRows(0).count(), 0); + + t->deleteLater(); +} + +void tst_QFileDialog2::task257579_sideBarWithNonCleanUrls() +{ +#if defined QT_BUILD_INTERNAL + QDir tempDir = QDir::temp(); + QLatin1String dirname("autotest_task257579"); + tempDir.rmdir(dirname); //makes sure it doesn't exist any more + QVERIFY(tempDir.mkdir(dirname)); + QString url = QString::fromLatin1("%1/%2/..").arg(tempDir.absolutePath()).arg(dirname); + QNonNativeFileDialog fd; + fd.setSidebarUrls(QList<QUrl>() << QUrl::fromLocalFile(url)); + QSidebar *sidebar = qFindChild<QSidebar*>(&fd, "sidebar"); + QCOMPARE(sidebar->urls().count(), 1); + QVERIFY(sidebar->urls().first().toLocalFile() != url); + QCOMPARE(sidebar->urls().first().toLocalFile(), QDir::cleanPath(url)); + +#ifdef Q_OS_WIN + QCOMPARE(sidebar->model()->index(0,0).data().toString().toLower(), tempDir.dirName().toLower()); +#else + QCOMPARE(sidebar->model()->index(0,0).data().toString(), tempDir.dirName()); +#endif + + //all tests are finished, we can remove the temporary dir + QVERIFY(tempDir.rmdir(dirname)); +#endif +} + +void tst_QFileDialog2::task259105_filtersCornerCases() +{ + QNonNativeFileDialog fd(0, "TestFileDialog"); + fd.setNameFilter(QLatin1String("All Files! (*);;Text Files (*.txt)")); + fd.setOption(QFileDialog::HideNameFilterDetails, true); + fd.show(); + QTest::qWait(250); + + //Extensions are hidden + QComboBox *filters = qFindChild<QComboBox*>(&fd, "fileTypeCombo"); + QVERIFY(filters); + QCOMPARE(filters->currentText(), QLatin1String("All Files!")); + filters->setCurrentIndex(1); + QCOMPARE(filters->currentText(), QLatin1String("Text Files")); + + //We should have the full names + fd.setOption(QFileDialog::HideNameFilterDetails, false); + QTest::qWait(250); + filters->setCurrentIndex(0); + QCOMPARE(filters->currentText(), QLatin1String("All Files! (*)")); + filters->setCurrentIndex(1); + QCOMPARE(filters->currentText(), QLatin1String("Text Files (*.txt)")); + + //Corner case undocumented of the task + fd.setNameFilter(QLatin1String("\352 (I like cheese) All Files! (*);;Text Files (*.txt)")); + QCOMPARE(filters->currentText(), QLatin1String("\352 (I like cheese) All Files! (*)")); + filters->setCurrentIndex(1); + QCOMPARE(filters->currentText(), QLatin1String("Text Files (*.txt)")); + + fd.setOption(QFileDialog::HideNameFilterDetails, true); + filters->setCurrentIndex(0); + QTest::qWait(500); + QCOMPARE(filters->currentText(), QLatin1String("\352 (I like cheese) All Files!")); + filters->setCurrentIndex(1); + QCOMPARE(filters->currentText(), QLatin1String("Text Files")); + + fd.setOption(QFileDialog::HideNameFilterDetails, true); + filters->setCurrentIndex(0); + QTest::qWait(500); + QCOMPARE(filters->currentText(), QLatin1String("\352 (I like cheese) All Files!")); + filters->setCurrentIndex(1); + QCOMPARE(filters->currentText(), QLatin1String("Text Files")); +} + +void tst_QFileDialog2::QTBUG4419_lineEditSelectAll() +{ + QString tempPath = QDir::tempPath(); + QTemporaryFile *t; + t = new QTemporaryFile; + t->open(); + QNonNativeFileDialog fd(0, "TestFileDialog", t->fileName()); + + fd.setDirectory(tempPath); + fd.setViewMode(QFileDialog::List); + fd.setAcceptMode(QFileDialog::AcceptSave); + fd.setFileMode(QFileDialog::AnyFile); + + fd.show(); + QApplication::setActiveWindow(&fd); + QTest::qWaitForWindowShown(&fd); + QTRY_COMPARE(fd.isVisible(), true); + QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget*>(&fd)); + + QTest::qWait(250); + QLineEdit *lineEdit = qFindChild<QLineEdit*>(&fd, "fileNameEdit"); + + QCOMPARE(tempPath + QChar('/') + lineEdit->text(), t->fileName()); + QCOMPARE(tempPath + QChar('/') + lineEdit->selectedText(), t->fileName()); +} + +void tst_QFileDialog2::QTBUG6558_showDirsOnly() +{ + const QString tempPath = QDir::tempPath(); + QDir dirTemp(tempPath); + const QString tempName = QLatin1String("showDirsOnly.") + QString::number(qrand()); + dirTemp.mkdir(tempName); + dirTemp.cd(tempName); + QTRY_VERIFY(dirTemp.exists()); + + const QString dirPath = dirTemp.absolutePath(); + QDir dir(dirPath); + + //We create two dirs + dir.mkdir("a"); + dir.mkdir("b"); + + //Create a file + QFile tempFile(dirPath + "/plop.txt"); + tempFile.open(QIODevice::WriteOnly | QIODevice::Text); + QTextStream out(&tempFile); + out << "The magic number is: " << 49 << "\n"; + tempFile.close(); + + QNonNativeFileDialog fd(0, "TestFileDialog"); + + fd.setDirectory(dir.absolutePath()); + fd.setViewMode(QFileDialog::List); + fd.setAcceptMode(QFileDialog::AcceptSave); + fd.setOption(QFileDialog::ShowDirsOnly, true); + fd.show(); + + QApplication::setActiveWindow(&fd); + QTest::qWaitForWindowShown(&fd); + QTRY_COMPARE(fd.isVisible(), true); + QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget*>(&fd)); + + QFileSystemModel *model = qFindChild<QFileSystemModel*>(&fd, "qt_filesystem_model"); + QTRY_COMPARE(model->rowCount(model->index(dir.absolutePath())), 2); + + fd.setOption(QFileDialog::ShowDirsOnly, false); + QTRY_COMPARE(model->rowCount(model->index(dir.absolutePath())), 3); + + fd.setOption(QFileDialog::ShowDirsOnly, true); + QTRY_COMPARE(model->rowCount(model->index(dir.absolutePath())), 2); + + fd.setFileMode(QFileDialog::DirectoryOnly); + QTRY_COMPARE(model->rowCount(model->index(dir.absolutePath())), 2); + QTRY_COMPARE(bool(fd.options() & QFileDialog::ShowDirsOnly), true); + + fd.setFileMode(QFileDialog::AnyFile); + QTRY_COMPARE(model->rowCount(model->index(dir.absolutePath())), 3); + QTRY_COMPARE(bool(fd.options() & QFileDialog::ShowDirsOnly), false); + + fd.setDirectory(QDir::homePath()); + + //We remove the dirs + dir.rmdir("a"); + dir.rmdir("b"); + + //we delete the file + tempFile.remove(); + + dirTemp.cdUp(); + dirTemp.rmdir(tempName); +} + +void tst_QFileDialog2::QTBUG4842_selectFilterWithHideNameFilterDetails() +{ + QStringList filtersStr; + filtersStr << "Images (*.png *.xpm *.jpg)" << "Text files (*.txt)" << "XML files (*.xml)"; + QString chosenFilterString("Text files (*.txt)"); + + QNonNativeFileDialog fd(0, "TestFileDialog"); + fd.setAcceptMode(QFileDialog::AcceptSave); + fd.setOption(QFileDialog::HideNameFilterDetails, true); + fd.setNameFilters(filtersStr); + fd.selectNameFilter(chosenFilterString); + fd.show(); + + QApplication::setActiveWindow(&fd); + QTest::qWaitForWindowShown(&fd); + QTRY_COMPARE(fd.isVisible(), true); + QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget*>(&fd)); + + QComboBox *filters = qFindChild<QComboBox*>(&fd, "fileTypeCombo"); + //We compare the current combobox text with the stripped version + QCOMPARE(filters->currentText(), QString("Text files")); + + QNonNativeFileDialog fd2(0, "TestFileDialog"); + fd2.setAcceptMode(QFileDialog::AcceptSave); + fd2.setOption(QFileDialog::HideNameFilterDetails, false); + fd2.setNameFilters(filtersStr); + fd2.selectNameFilter(chosenFilterString); + fd2.show(); + + QApplication::setActiveWindow(&fd2); + QTest::qWaitForWindowShown(&fd2); + QTRY_COMPARE(fd2.isVisible(), true); + QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget*>(&fd2)); + + QComboBox *filters2 = qFindChild<QComboBox*>(&fd2, "fileTypeCombo"); + //We compare the current combobox text with the non stripped version + QCOMPARE(filters2->currentText(), chosenFilterString); + +} + +void tst_QFileDialog2::dontShowCompleterOnRoot() +{ + QNonNativeFileDialog fd(0, "TestFileDialog"); + fd.setAcceptMode(QFileDialog::AcceptSave); + fd.show(); + + QApplication::setActiveWindow(&fd); + QTest::qWaitForWindowShown(&fd); + QTRY_COMPARE(fd.isVisible(), true); + QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget*>(&fd)); + + fd.setDirectory(""); + QLineEdit *lineEdit = qFindChild<QLineEdit*>(&fd, "fileNameEdit"); + QTRY_VERIFY(lineEdit->text().isEmpty()); + + //The gatherer thread will then return the result + QApplication::processEvents(); + + QTRY_VERIFY(lineEdit->completer()->popup()->isHidden()); +} + +QTEST_MAIN(tst_QFileDialog2) +#include "tst_qfiledialog2.moc" diff --git a/tests/auto/widgets/dialogs/qfilesystemmodel/.gitignore b/tests/auto/widgets/dialogs/qfilesystemmodel/.gitignore new file mode 100644 index 0000000000..9804e5a3d7 --- /dev/null +++ b/tests/auto/widgets/dialogs/qfilesystemmodel/.gitignore @@ -0,0 +1 @@ +tst_qfilesystemmodel diff --git a/tests/auto/widgets/dialogs/qfilesystemmodel/qfilesystemmodel.pro b/tests/auto/widgets/dialogs/qfilesystemmodel/qfilesystemmodel.pro new file mode 100644 index 0000000000..a7d042ce5e --- /dev/null +++ b/tests/auto/widgets/dialogs/qfilesystemmodel/qfilesystemmodel.pro @@ -0,0 +1,7 @@ +CONFIG += qttest_p4 + +QT += widgets widgets-private +QT += core-private gui + +SOURCES += tst_qfilesystemmodel.cpp +TARGET = tst_qfilesystemmodel diff --git a/tests/auto/widgets/dialogs/qfilesystemmodel/tst_qfilesystemmodel.cpp b/tests/auto/widgets/dialogs/qfilesystemmodel/tst_qfilesystemmodel.cpp new file mode 100644 index 0000000000..26fa58e649 --- /dev/null +++ b/tests/auto/widgets/dialogs/qfilesystemmodel/tst_qfilesystemmodel.cpp @@ -0,0 +1,1046 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtTest/QtTest> +#ifdef QT_BUILD_INTERNAL +#include "../../../src/widgets/dialogs/qfilesystemmodel_p.h" +#endif +#include <QFileSystemModel> +#include <QFileIconProvider> +#include <QTreeView> +#include <QHeaderView> +#include <QTime> +#include <QStyle> +#include <QtGlobal> + +//TESTED_CLASS= +//TESTED_FILES= + +#define WAITTIME 1000 + +// Will try to wait for the condition while allowing event processing +// for a maximum of 5 seconds. +#define TRY_WAIT(expr) \ + do { \ + const int step = 50; \ + for (int __i = 0; __i < 5000 && !(expr); __i+=step) { \ + QTest::qWait(step); \ + } \ + } while(0) + +class tst_QFileSystemModel : public QObject { + Q_OBJECT + +public: + tst_QFileSystemModel(); + virtual ~tst_QFileSystemModel(); + +public Q_SLOTS: + void init(); + void cleanup(); + +private slots: + void indexPath(); + + void rootPath(); + void naturalCompare_data(); + void naturalCompare(); + void readOnly(); + void iconProvider(); + + void rowCount(); + + void rowsInserted_data(); + void rowsInserted(); + + void rowsRemoved_data(); + void rowsRemoved(); + + void dataChanged_data(); + void dataChanged(); + + void filters_data(); + void filters(); + + void nameFilters(); + + void setData_data(); + void setData(); + + void sort_data(); + void sort(); + + void mkdir(); + + void caseSensitivity(); + +#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) + void Win32LongFileName(); +#endif + + void drives_data(); + void drives(); + void dirsBeforeFiles(); + + void roleNames_data(); + void roleNames(); + +protected: + bool createFiles(const QString &test_path, const QStringList &initial_files, int existingFileCount = 0, const QStringList &intial_dirs = QStringList(), const QString &baseDir = QDir::temp().absolutePath()); + +private: + QFileSystemModel *model; + QString flatDirTestPath; +}; + +tst_QFileSystemModel::tst_QFileSystemModel() : model(0) +{ + qRegisterMetaType<QModelIndex>("QModelIndex"); + + QTime midnight(0, 0, 0); + qsrand(midnight.secsTo(QTime::currentTime())); + // generating unique temporary directory name + flatDirTestPath = QDir::temp().path() + '/' + QString("flatdirtest.") + QString::number(qrand()); +} + +tst_QFileSystemModel::~tst_QFileSystemModel() +{ + QString tmp = flatDirTestPath; + QDir dir(tmp); + if (dir.exists() && !dir.rmdir(tmp)) + qWarning("failed to remove tmp dir %s", dir.dirName().toAscii().data()); +} + +void tst_QFileSystemModel::init() +{ + cleanup(); + QCOMPARE(model, (QFileSystemModel*)0); + model = new QFileSystemModel; +} + +void tst_QFileSystemModel::cleanup() +{ + delete model; + model = 0; + QString tmp = flatDirTestPath; + QDir dir(tmp); + if (dir.exists(tmp)) { + QStringList list = dir.entryList(QDir::AllEntries | QDir::System | QDir::Hidden | QDir::NoDotAndDotDot); + for (int i = 0; i < list.count(); ++i) { + QFileInfo fi(dir.path() + '/' + list.at(i)); + if (fi.exists() && fi.isFile()) { + QFile p(fi.absoluteFilePath()); + p.setPermissions(QFile::ReadUser | QFile::ReadOwner | QFile::ExeOwner | QFile::ExeUser | QFile::WriteUser | QFile::WriteOwner | QFile::WriteOther); + QFile dead(dir.path() + '/' + list.at(i)); + dead.remove(); + } + if (fi.exists() && fi.isDir()) + QVERIFY(dir.rmdir(list.at(i))); + } + list = dir.entryList(QDir::AllEntries | QDir::System | QDir::Hidden | QDir::NoDotAndDotDot); + QVERIFY(list.count() == 0); + } +} + +void tst_QFileSystemModel::indexPath() +{ +#if !defined(Q_OS_WIN) + int depth = QDir::currentPath().count('/'); + model->setRootPath(QDir::currentPath()); + QTest::qWait(WAITTIME); + QString backPath; + for (int i = 0; i <= depth * 2 + 1; ++i) { + backPath += "../"; + QModelIndex idx = model->index(backPath); + QVERIFY(i != depth - 1 ? idx.isValid() : !idx.isValid()); + } + QTest::qWait(WAITTIME * 3); + qApp->processEvents(); +#endif +} + +void tst_QFileSystemModel::rootPath() +{ + QCOMPARE(model->rootPath(), QString(QDir().path())); + + QSignalSpy rootChanged(model, SIGNAL(rootPathChanged(const QString &))); + QModelIndex root = model->setRootPath(model->rootPath()); + root = model->setRootPath("this directory shouldn't exist"); + QCOMPARE(rootChanged.count(), 0); + + QString oldRootPath = model->rootPath(); + root = model->setRootPath(QDir::homePath()); + + QTRY_VERIFY(model->rowCount(root) >= 0); + QCOMPARE(model->rootPath(), QString(QDir::homePath())); + QCOMPARE(rootChanged.count(), oldRootPath == model->rootPath() ? 0 : 1); + QCOMPARE(model->rootDirectory().absolutePath(), QDir::homePath()); + + model->setRootPath(QDir::rootPath()); + int oldCount = rootChanged.count(); + oldRootPath = model->rootPath(); + root = model->setRootPath(QDir::homePath() + QLatin1String("/.")); + QTRY_VERIFY(model->rowCount(root) >= 0); + QCOMPARE(model->rootPath(), QDir::homePath()); + QCOMPARE(rootChanged.count(), oldRootPath == model->rootPath() ? oldCount : oldCount + 1); + QCOMPARE(model->rootDirectory().absolutePath(), QDir::homePath()); + + QDir newdir = QDir::home(); + if (newdir.cdUp()) { + oldCount = rootChanged.count(); + oldRootPath = model->rootPath(); + root = model->setRootPath(QDir::homePath() + QLatin1String("/..")); + QTRY_VERIFY(model->rowCount(root) >= 0); + QCOMPARE(model->rootPath(), newdir.path()); + QCOMPARE(rootChanged.count(), oldCount + 1); + QCOMPARE(model->rootDirectory().absolutePath(), newdir.path()); + } +} + +void tst_QFileSystemModel::naturalCompare_data() +{ + QTest::addColumn<QString>("s1"); + QTest::addColumn<QString>("s2"); + QTest::addColumn<int>("caseSensitive"); + QTest::addColumn<int>("result"); + QTest::addColumn<int>("swap"); + for (int j = 0; j < 4; ++j) { // <- set a prefix and a postfix string (not numbers) + QString prefix = (j == 0 || j == 1) ? "b" : ""; + QString postfix = (j == 1 || j == 2) ? "y" : ""; + + for (int k = 0; k < 3; ++k) { // <- make 0 not a special case + QString num = QString("%1").arg(k); + QString nump = QString("%1").arg(k + 1); + for (int i = 10; i < 12; ++i) { // <- swap s1 and s2 and reverse the result + QTest::newRow("basic") << prefix + "0" + postfix << prefix + "0" + postfix << int(Qt::CaseInsensitive) << 0; + + // s1 should always be less then s2 + QTest::newRow("just text") << prefix + "fred" + postfix << prefix + "jane" + postfix << int(Qt::CaseInsensitive) << i; + QTest::newRow("just numbers") << prefix + num + postfix << prefix + "9" + postfix << int(Qt::CaseInsensitive) << i; + QTest::newRow("zero") << prefix + num + postfix << prefix + "0" + nump + postfix << int(Qt::CaseInsensitive) << i; + QTest::newRow("space b") << prefix + num + postfix << prefix + " " + nump + postfix << int(Qt::CaseInsensitive) << i; + QTest::newRow("space a") << prefix + num + postfix << prefix + nump + " " + postfix << int(Qt::CaseInsensitive) << i; + QTest::newRow("tab b") << prefix + num + postfix << prefix + " " + nump + postfix << int(Qt::CaseInsensitive) << i; + QTest::newRow("tab a") << prefix + num + postfix << prefix + nump + " " + postfix << int(Qt::CaseInsensitive) << i; + QTest::newRow("10 vs 2") << prefix + num + postfix << prefix + "10" + postfix << int(Qt::CaseInsensitive) << i; + QTest::newRow("diff len") << prefix + num + postfix << prefix + nump + postfix + "x" << int(Qt::CaseInsensitive) << i; + QTest::newRow("01 before 1") << prefix + "0" + num + postfix << prefix + nump + postfix << int(Qt::CaseInsensitive) << i; + QTest::newRow("mul nums 2nd") << prefix + "1-" + num + postfix << prefix + "1-" + nump + postfix << int(Qt::CaseInsensitive) << i; + QTest::newRow("mul nums 2nd") << prefix + "10-" + num + postfix<< prefix + "10-10" + postfix << int(Qt::CaseInsensitive) << i; + QTest::newRow("mul nums 2nd") << prefix + "10-0"+ num + postfix<< prefix + "10-10" + postfix << int(Qt::CaseInsensitive) << i; + QTest::newRow("mul nums 2nd") << prefix + "10-" + num + postfix<< prefix + "10-010" + postfix << int(Qt::CaseInsensitive) << i; + QTest::newRow("mul nums big") << prefix + "10-" + num + postfix<< prefix + "20-0" + postfix << int(Qt::CaseInsensitive) << i; + QTest::newRow("mul nums big") << prefix + "2-" + num + postfix << prefix + "10-0" + postfix << int(Qt::CaseInsensitive) << i; + QTest::newRow("mul alphabet") << prefix + num + "-a" + postfix << prefix + num + "-c" + postfix << int(Qt::CaseInsensitive) << i; + QTest::newRow("mul alphabet2")<< prefix + num + "-a9" + postfix<< prefix + num + "-c0" + postfix << int(Qt::CaseInsensitive) << i; + QTest::newRow("mul nums w\\0")<< prefix + num + "-"+ num + postfix<< prefix + num+"-0"+nump + postfix << int(Qt::CaseInsensitive) << i; + QTest::newRow("num first") << prefix + num + postfix << prefix + "a" + postfix << int(Qt::CaseInsensitive) << i; + } + } + } +} + +void tst_QFileSystemModel::naturalCompare() +{ +#ifdef QT_BUILD_INTERNAL + QFETCH(QString, s1); + QFETCH(QString, s2); + QFETCH(int, caseSensitive); + QFETCH(int, result); + + if (result == 10) + QCOMPARE(QFileSystemModelPrivate::naturalCompare(s1, s2, Qt::CaseSensitivity(caseSensitive)), -1); + else + if (result == 11) + QCOMPARE(QFileSystemModelPrivate::naturalCompare(s2, s1, Qt::CaseSensitivity(caseSensitive)), 1); + else + QCOMPARE(QFileSystemModelPrivate::naturalCompare(s2, s1, Qt::CaseSensitivity(caseSensitive)), result); +#if defined(Q_OS_WINCE) + // On Windows CE we need to wait after each test, otherwise no new threads can be + // created. The scheduler takes its time to recognize ended threads. + QTest::qWait(300); +#endif +#endif +} + +void tst_QFileSystemModel::readOnly() +{ + QCOMPARE(model->isReadOnly(), true); + QTemporaryFile file; + file.open(); + QModelIndex root = model->setRootPath(QDir::tempPath()); + + QTRY_VERIFY(model->rowCount(root) > 0); + QVERIFY(!(model->flags(model->index(file.fileName())) & Qt::ItemIsEditable)); + model->setReadOnly(false); + QCOMPARE(model->isReadOnly(), false); + QVERIFY(model->flags(model->index(file.fileName())) & Qt::ItemIsEditable); +} + +class CustomFileIconProvider : public QFileIconProvider +{ +public: + CustomFileIconProvider() : QFileIconProvider() { + mb = qApp->style()->standardIcon(QStyle::SP_MessageBoxCritical); + dvd = qApp->style()->standardIcon(QStyle::SP_DriveDVDIcon); + } + + virtual QIcon icon(const QFileInfo &info) const + { + if (info.isDir()) + return mb; + + return QFileIconProvider::icon(info); + } + virtual QIcon icon(IconType type) const + { + if (type == QFileIconProvider::Folder) + return dvd; + + return QFileIconProvider::icon(type); + } +private: + QIcon mb; + QIcon dvd; +}; + +void tst_QFileSystemModel::iconProvider() +{ + QVERIFY(model->iconProvider()); + QFileIconProvider *p = new QFileIconProvider(); + model->setIconProvider(p); + QCOMPARE(model->iconProvider(), p); + model->setIconProvider(0); + delete p; + + QFileSystemModel *myModel = new QFileSystemModel(); + myModel->setRootPath(QDir::homePath()); + //Let's wait to populate the model + QTest::qWait(250); + //We change the provider, icons must me updated + CustomFileIconProvider *custom = new CustomFileIconProvider(); + myModel->setIconProvider(custom); + + QPixmap mb = qApp->style()->standardIcon(QStyle::SP_MessageBoxCritical).pixmap(50, 50); + QCOMPARE(myModel->fileIcon(myModel->index(QDir::homePath())).pixmap(50, 50), mb); + delete myModel; + delete custom; +} + +bool tst_QFileSystemModel::createFiles(const QString &test_path, const QStringList &initial_files, int existingFileCount, const QStringList &initial_dirs, const QString &dir) +{ + QDir baseDir(dir); + if (!baseDir.exists(test_path)) { + if (!baseDir.mkdir(test_path) && false) { + qDebug() << "failed to create dir" << test_path; + return false; + } + } + //qDebug() << (model->rowCount(model->index(test_path))) << existingFileCount << initial_files; + TRY_WAIT((model->rowCount(model->index(test_path)) == existingFileCount)); + for (int i = 0; i < initial_dirs.count(); ++i) { + QDir dir(test_path); + if (!dir.exists()) { + qWarning() << "error" << test_path << "doesn't exists"; + return false; + } + if(!dir.mkdir(initial_dirs.at(i))) { + qWarning() << "error" << "failed to make" << initial_dirs.at(i); + return false; + } + //qDebug() << test_path + '/' + initial_dirs.at(i) << (QFile::exists(test_path + '/' + initial_dirs.at(i))); + } + for (int i = 0; i < initial_files.count(); ++i) { + QFile file(test_path + '/' + initial_files.at(i)); + if (!file.open(QIODevice::WriteOnly | QIODevice::Append)) { + qDebug() << "failed to open file" << initial_files.at(i); + return false; + } + if (!file.resize(1024 + file.size())) { + qDebug() << "failed to resize file" << initial_files.at(i); + return false; + } + if (!file.flush()) { + qDebug() << "failed to flush file" << initial_files.at(i); + return false; + } + file.close(); +#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) + if (initial_files.at(i)[0] == '.') + QProcess::execute(QString("attrib +h %1").arg(file.fileName())); +#endif + //qDebug() << test_path + '/' + initial_files.at(i) << (QFile::exists(test_path + '/' + initial_files.at(i))); + } + return true; +} + +void tst_QFileSystemModel::rowCount() +{ + QString tmp = flatDirTestPath; + QVERIFY(createFiles(tmp, QStringList())); + + QSignalSpy spy2(model, SIGNAL(rowsInserted(const QModelIndex &, int, int))); + QSignalSpy spy3(model, SIGNAL(rowsAboutToBeInserted(const QModelIndex &, int, int))); + +#if !defined(Q_OS_WINCE) + QStringList files = QStringList() << "b" << "d" << "f" << "h" << "j" << ".a" << ".c" << ".e" << ".g"; + QString l = "b,d,f,h,j,.a,.c,.e,.g"; +#else + // Cannot hide them on CE + QStringList files = QStringList() << "b" << "d" << "f" << "h" << "j"; + QString l = "b,d,f,h,j"; +#endif + QVERIFY(createFiles(tmp, files)); + + QModelIndex root = model->setRootPath(tmp); + QTRY_COMPARE(model->rowCount(root), 5); + QVERIFY(spy2.count() > 0); + QVERIFY(spy3.count() > 0); +} + +void tst_QFileSystemModel::rowsInserted_data() +{ + QTest::addColumn<int>("count"); + QTest::addColumn<int>("assending"); + for (int i = 0; i < 4; ++i) { + QTest::newRow(QString("Qt::AscendingOrder %1").arg(i).toLocal8Bit().constData()) << i << (int)Qt::AscendingOrder; + QTest::newRow(QString("Qt::DescendingOrder %1").arg(i).toLocal8Bit().constData()) << i << (int)Qt::DescendingOrder; + } +} + +void tst_QFileSystemModel::rowsInserted() +{ +#if defined(Q_OS_WINCE) + QSKIP("Watching directories does not work on CE(see #137910)", SkipAll); +#endif + QString tmp = flatDirTestPath; + rowCount(); + QModelIndex root = model->index(model->rootPath()); + + QFETCH(int, assending); + QFETCH(int, count); + model->sort(0, (Qt::SortOrder)assending); + + QSignalSpy spy0(model, SIGNAL(rowsInserted(const QModelIndex &, int, int))); + QSignalSpy spy1(model, SIGNAL(rowsAboutToBeInserted(const QModelIndex &, int, int))); + int oldCount = model->rowCount(root); + QStringList files; + for (int i = 0; i < count; ++i) + files.append(QString("c%1").arg(i)); + QVERIFY(createFiles(tmp, files, 5)); + TRY_WAIT(model->rowCount(root) == oldCount + count); + QTRY_COMPARE(model->rowCount(root), oldCount + count); + QTest::qWait(100); // Let the sort settle. + int totalRowsInserted = 0; + for (int i = 0; i < spy0.count(); ++i) { + int start = spy0[i].value(1).toInt(); + int end = spy0[i].value(2).toInt(); + totalRowsInserted += end - start + 1; + } + QCOMPARE(totalRowsInserted, count); + if (assending == (Qt::SortOrder)Qt::AscendingOrder) { + QString letter = model->index(model->rowCount(root) - 1, 0, root).data().toString(); + QCOMPARE(letter, QString("j")); + } else { + QCOMPARE(model->index(model->rowCount(root) - 1, 0, root).data().toString(), QString("b")); + } + if (spy0.count() > 0) { + if (count == 0) + QCOMPARE(spy0.count(), 0); + else + QVERIFY(spy0.count() >= 1); + } + if (count == 0) QCOMPARE(spy1.count(), 0); else QVERIFY(spy1.count() >= 1); + + QVERIFY(createFiles(tmp, QStringList(".hidden_file"), 5 + count)); + + if (count != 0) QTRY_VERIFY(spy0.count() >= 1); else QTRY_VERIFY(spy0.count() == 0); + if (count != 0) QTRY_VERIFY(spy1.count() >= 1); else QTRY_VERIFY(spy1.count() == 0); +} + +void tst_QFileSystemModel::rowsRemoved_data() +{ + rowsInserted_data(); +} + +void tst_QFileSystemModel::rowsRemoved() +{ +#if defined(Q_OS_WINCE) + QSKIP("Watching directories does not work on CE(see #137910)", SkipAll); +#endif + QString tmp = flatDirTestPath; + rowCount(); + QModelIndex root = model->index(model->rootPath()); + + QFETCH(int, count); + QFETCH(int, assending); + model->sort(0, (Qt::SortOrder)assending); + QTest::qWait(WAITTIME); + + QSignalSpy spy0(model, SIGNAL(rowsRemoved(const QModelIndex &, int, int))); + QSignalSpy spy1(model, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int))); + int oldCount = model->rowCount(root); + for (int i = count - 1; i >= 0; --i) { + //qDebug() << "removing" << model->index(i, 0, root).data().toString(); + QVERIFY(QFile::remove(tmp + '/' + model->index(i, 0, root).data().toString())); + } + for (int i = 0 ; i < 10; ++i) { + QTest::qWait(WAITTIME); + qApp->processEvents(); + if (count != 0) { + if (i == 10 || spy0.count() != 0) { + QVERIFY(spy0.count() >= 1); + QVERIFY(spy1.count() >= 1); + } + } else { + if (i == 10 || spy0.count() == 0) { + QVERIFY(spy0.count() == 0); + QVERIFY(spy1.count() == 0); + } + } + QStringList lst; + for (int i = 0; i < model->rowCount(root); ++i) + lst.append(model->index(i, 0, root).data().toString()); + if (model->rowCount(root) == oldCount - count) + break; + qDebug() << "still have:" << lst << QFile::exists(tmp + '/' + QString(".a")); + QDir tmpLister(tmp); + qDebug() << tmpLister.entryList(); + } + QTRY_COMPARE(model->rowCount(root), oldCount - count); + + QVERIFY(QFile::exists(tmp + '/' + QString(".a"))); + QVERIFY(QFile::remove(tmp + '/' + QString(".a"))); + QVERIFY(QFile::remove(tmp + '/' + QString(".c"))); + QTest::qWait(WAITTIME); + + if (count != 0) QVERIFY(spy0.count() >= 1); else QVERIFY(spy0.count() == 0); + if (count != 0) QVERIFY(spy1.count() >= 1); else QVERIFY(spy1.count() == 0); +} + +void tst_QFileSystemModel::dataChanged_data() +{ + rowsInserted_data(); +} + +void tst_QFileSystemModel::dataChanged() +{ + // This can't be tested right now sense we don't watch files, only directories + return; + + /* + QString tmp = flatDirTestPath; + rowCount(); + QModelIndex root = model->index(model->rootPath()); + + QFETCH(int, count); + QFETCH(int, assending); + model->sort(0, (Qt::SortOrder)assending); + + QSignalSpy spy(model, SIGNAL(dataChanged (const QModelIndex &, const QModelIndex &))); + QStringList files; + for (int i = 0; i < count; ++i) + files.append(model->index(i, 0, root).data().toString()); + createFiles(tmp, files); + + QTest::qWait(WAITTIME); + + if (count != 0) QVERIFY(spy.count() >= 1); else QVERIFY(spy.count() == 0); + */ +} + +void tst_QFileSystemModel::filters_data() +{ + QTest::addColumn<QStringList>("files"); + QTest::addColumn<QStringList>("dirs"); + QTest::addColumn<int>("dirFilters"); + QTest::addColumn<QStringList>("nameFilters"); + QTest::addColumn<int>("rowCount"); +#if !defined(Q_OS_WINCE) + QTest::newRow("no dirs") << (QStringList() << "a" << "b" << "c") << QStringList() << (int)(QDir::Dirs) << QStringList() << 2; + QTest::newRow("no dirs - dot") << (QStringList() << "a" << "b" << "c") << QStringList() << (int)(QDir::Dirs | QDir::NoDot) << QStringList() << 1; + QTest::newRow("no dirs - dotdot") << (QStringList() << "a" << "b" << "c") << QStringList() << (int)(QDir::Dirs | QDir::NoDotDot) << QStringList() << 1; + QTest::newRow("no dirs - dotanddotdot") << (QStringList() << "a" << "b" << "c") << QStringList() << (int)(QDir::Dirs | QDir::NoDotAndDotDot) << QStringList() << 0; + QTest::newRow("one dir - dot") << (QStringList() << "a" << "b" << "c") << (QStringList() << "Z") << (int)(QDir::Dirs | QDir::NoDot) << QStringList() << 2; + QTest::newRow("one dir - dotdot") << (QStringList() << "a" << "b" << "c") << (QStringList() << "Z") << (int)(QDir::Dirs | QDir::NoDotDot) << QStringList() << 2; + QTest::newRow("one dir - dotanddotdot") << (QStringList() << "a" << "b" << "c") << (QStringList() << "Z") << (int)(QDir::Dirs | QDir::NoDotAndDotDot) << QStringList() << 1; + QTest::newRow("one dir") << (QStringList() << "a" << "b" << "c") << (QStringList() << "Z") << (int)(QDir::Dirs) << QStringList() << 3; + QTest::newRow("no dir + hidden") << (QStringList() << "a" << "b" << "c") << QStringList() << (int)(QDir::Dirs | QDir::Hidden) << QStringList() << 2; + QTest::newRow("dir+hid+files") << (QStringList() << "a" << "b" << "c") << QStringList() << + (int)(QDir::Dirs | QDir::Files | QDir::Hidden) << QStringList() << 5; + QTest::newRow("dir+file+hid-dot .A") << (QStringList() << "a" << "b" << "c") << (QStringList() << ".A") << + (int)(QDir::Dirs | QDir::Files | QDir::Hidden | QDir::NoDotAndDotDot) << QStringList() << 4; + QTest::newRow("dir+files+hid+dot A") << (QStringList() << "a" << "b" << "c") << (QStringList() << "AFolder") << + (int)(QDir::Dirs | QDir::Files | QDir::Hidden | QDir::NoDotAndDotDot) << (QStringList() << "A*") << 2; + QTest::newRow("dir+files+hid+dot+cas1") << (QStringList() << "a" << "b" << "c") << (QStringList() << "Z") << + (int)(QDir::Dirs | QDir::Files | QDir::Hidden | QDir::NoDotAndDotDot | QDir::CaseSensitive) << (QStringList() << "Z") << 1; + QTest::newRow("dir+files+hid+dot+cas2") << (QStringList() << "a" << "b" << "c") << (QStringList() << "Z") << + (int)(QDir::Dirs | QDir::Files | QDir::Hidden | QDir::NoDotAndDotDot | QDir::CaseSensitive) << (QStringList() << "a") << 1; + QTest::newRow("dir+files+hid+dot+cas+alldir") << (QStringList() << "a" << "b" << "c") << (QStringList() << "Z") << + (int)(QDir::Dirs | QDir::Files | QDir::Hidden | QDir::NoDotAndDotDot | QDir::CaseSensitive | QDir::AllDirs) << (QStringList() << "Z") << 1; +#else + QTest::qWait(3000); // We need to calm down a bit... + QTest::newRow("no dirs") << (QStringList() << "a" << "b" << "c") << QStringList() << (int)(QDir::Dirs) << QStringList() << 0; + QTest::newRow("no dirs - dot") << (QStringList() << "a" << "b" << "c") << QStringList() << (int)(QDir::Dirs | QDir::NoDot) << QStringList() << 1; + QTest::newRow("no dirs - dotdot") << (QStringList() << "a" << "b" << "c") << QStringList() << (int)(QDir::Dirs | QDir::NoDotDot) << QStringList() << 1; + QTest::newRow("no dirs - dotanddotdot") << (QStringList() << "a" << "b" << "c") << QStringList() << (int)(QDir::Dirs | QDir::NoDotAndDotDot) << QStringList() << 0; + QTest::newRow("one dir - dot") << (QStringList() << "a" << "b" << "c") << (QStringList() << "Z") << (int)(QDir::Dirs | QDir::NoDot) << QStringList() << 2; + QTest::newRow("one dir - dotdot") << (QStringList() << "a" << "b" << "c") << (QStringList() << "Z") << (int)(QDir::Dirs | QDir::NoDotDot) << QStringList() << 2; + QTest::newRow("one dir - dotanddotdot") << (QStringList() << "a" << "b" << "c") << (QStringList() << "Z") << (int)(QDir::Dirs | QDir::NoDotAndDotDot) << QStringList() << 1; + QTest::newRow("one dir") << (QStringList() << "a" << "b" << "c") << (QStringList() << "Z") << (int)(QDir::Dirs) << QStringList() << 1; + QTest::newRow("no dir + hidden") << (QStringList() << "a" << "b" << "c") << QStringList() << (int)(QDir::Dirs | QDir::Hidden) << QStringList() << 0; + QTest::newRow("dir+hid+files") << (QStringList() << "a" << "b" << "c") << QStringList() << + (int)(QDir::Dirs | QDir::Files | QDir::Hidden) << QStringList() << 3; + QTest::newRow("dir+file+hid-dot .A") << (QStringList() << "a" << "b" << "c") << (QStringList() << ".A") << + (int)(QDir::Dirs | QDir::Files | QDir::Hidden | QDir::NoDotAndDotDot) << QStringList() << 4; + QTest::newRow("dir+files+hid+dot A") << (QStringList() << "a" << "b" << "c") << (QStringList() << "AFolder") << + (int)(QDir::Dirs | QDir::Files | QDir::Hidden | QDir::NoDotAndDotDot) << (QStringList() << "A*") << 2; + QTest::newRow("dir+files+hid+dot+cas1") << (QStringList() << "a" << "b" << "c") << (QStringList() << "Z") << + (int)(QDir::Dirs | QDir::Files | QDir::Hidden | QDir::NoDotAndDotDot | QDir::CaseSensitive) << (QStringList() << "Z") << 1; + QTest::newRow("dir+files+hid+dot+cas2") << (QStringList() << "a" << "b" << "c") << (QStringList() << "Z") << + (int)(QDir::Dirs | QDir::Files | QDir::Hidden | QDir::NoDotAndDotDot | QDir::CaseSensitive) << (QStringList() << "a") << 1; + QTest::newRow("dir+files+hid+dot+cas+alldir") << (QStringList() << "a" << "b" << "c") << (QStringList() << "Z") << + (int)(QDir::Dirs | QDir::Files | QDir::Hidden | QDir::NoDotAndDotDot | QDir::CaseSensitive | QDir::AllDirs) << (QStringList() << "Z") << 1; +#endif + + QTest::newRow("case sensitive") << (QStringList() << "Antiguagdb" << "Antiguamtd" + << "Antiguamtp" << "afghanistangdb" << "afghanistanmtd") + << QStringList() << (int)(QDir::Files) << QStringList() << 5; +} + +void tst_QFileSystemModel::filters() +{ + QString tmp = flatDirTestPath; + QVERIFY(createFiles(tmp, QStringList())); + QModelIndex root = model->setRootPath(tmp); + QFETCH(QStringList, files); + QFETCH(QStringList, dirs); + QFETCH(int, dirFilters); + QFETCH(QStringList, nameFilters); + QFETCH(int, rowCount); + + if (nameFilters.count() > 0) + model->setNameFilters(nameFilters); + model->setNameFilterDisables(false); + model->setFilter((QDir::Filters)dirFilters); + + QVERIFY(createFiles(tmp, files, 0, dirs)); + QTRY_COMPARE(model->rowCount(root), rowCount); + + // Make sure that we do what QDir does + QDir xFactor(tmp); + QDir::Filters filters = (QDir::Filters)dirFilters; + QStringList dirEntries; + + if (nameFilters.count() > 0) + dirEntries = xFactor.entryList(nameFilters, filters); + else + dirEntries = xFactor.entryList(filters); + + QCOMPARE(dirEntries.count(), rowCount); + + QStringList modelEntries; + + for (int i = 0; i < rowCount; ++i) + modelEntries.append(model->data(model->index(i, 0, root), QFileSystemModel::FileNameRole).toString()); + + qSort(dirEntries); + qSort(modelEntries); + QCOMPARE(dirEntries, modelEntries); + +#ifdef Q_OS_LINUX + if (files.count() >= 3 && rowCount >= 3 && rowCount != 5) { + QString fileName1 = (tmp + '/' + files.at(0)); + QString fileName2 = (tmp + '/' + files.at(1)); + QString fileName3 = (tmp + '/' + files.at(2)); + QFile::Permissions originalPermissions = QFile::permissions(fileName1); + QVERIFY(QFile::setPermissions(fileName1, QFile::WriteOwner)); + QVERIFY(QFile::setPermissions(fileName2, QFile::ReadOwner)); + QVERIFY(QFile::setPermissions(fileName3, QFile::ExeOwner)); + + model->setFilter((QDir::Files | QDir::Readable)); + QTRY_COMPARE(model->rowCount(root), 1); + + model->setFilter((QDir::Files | QDir::Writable)); + QTRY_COMPARE(model->rowCount(root), 1); + + model->setFilter((QDir::Files | QDir::Executable)); + QTRY_COMPARE(model->rowCount(root), 1); + + // reset permissions + QVERIFY(QFile::setPermissions(fileName1, originalPermissions)); + QVERIFY(QFile::setPermissions(fileName2, originalPermissions)); + QVERIFY(QFile::setPermissions(fileName3, originalPermissions)); + } +#endif +} + +void tst_QFileSystemModel::nameFilters() +{ + QStringList list; + list << "a" << "b" << "c"; + model->setNameFilters(list); + model->setNameFilterDisables(false); + QCOMPARE(model->nameFilters(), list); + + QString tmp = flatDirTestPath; + QVERIFY(createFiles(tmp, list)); + QModelIndex root = model->setRootPath(tmp); + QTRY_COMPARE(model->rowCount(root), 3); + + QStringList filters; + filters << "a" << "b"; + model->setNameFilters(filters); + QTRY_COMPARE(model->rowCount(root), 2); +} +void tst_QFileSystemModel::setData_data() +{ + QTest::addColumn<QStringList>("files"); + QTest::addColumn<QString>("oldFileName"); + QTest::addColumn<QString>("newFileName"); + QTest::addColumn<bool>("success"); + /*QTest::newRow("outside current dir") << (QStringList() << "a" << "b" << "c") + << flatDirTestPath + '/' + "a" + << QDir::temp().absolutePath() + '/' + "a" + << false; + */ + QTest::newRow("in current dir") << (QStringList() << "a" << "b" << "c") + << "a" + << "d" + << true; +} + +void tst_QFileSystemModel::setData() +{ + QSignalSpy spy(model, SIGNAL(fileRenamed(const QString&, const QString&, const QString&))); + QString tmp = flatDirTestPath; + QFETCH(QStringList, files); + QFETCH(QString, oldFileName); + QFETCH(QString, newFileName); + QFETCH(bool, success); + + QVERIFY(createFiles(tmp, files)); + QModelIndex root = model->setRootPath(tmp); + QTRY_COMPARE(model->rowCount(root), files.count()); + + QModelIndex idx = model->index(tmp + '/' + oldFileName); + QCOMPARE(idx.isValid(), true); + QCOMPARE(model->setData(idx, newFileName), false); + + model->setReadOnly(false); + QCOMPARE(model->setData(idx, newFileName), success); + if (success) { + QCOMPARE(spy.count(), 1); + QList<QVariant> arguments = spy.takeFirst(); + QCOMPARE(model->data(idx, QFileSystemModel::FileNameRole).toString(), newFileName); + QCOMPARE(model->index(arguments.at(0).toString()), model->index(tmp)); + QCOMPARE(arguments.at(1).toString(), oldFileName); + QCOMPARE(arguments.at(2).toString(), newFileName); + QCOMPARE(QFile::rename(tmp + '/' + newFileName, tmp + '/' + oldFileName), true); + } + QTRY_COMPARE(model->rowCount(root), files.count()); +} + +class MyFriendFileSystemModel : public QFileSystemModel +{ + friend class tst_QFileSystemModel; + Q_DECLARE_PRIVATE(QFileSystemModel) +}; + +void tst_QFileSystemModel::sort_data() +{ + QTest::addColumn<bool>("fileDialogMode"); + QTest::newRow("standard usage") << false; + QTest::newRow("QFileDialog usage") << true; +} + +void tst_QFileSystemModel::sort() +{ + QTemporaryFile file; + file.open(); + QModelIndex root = model->setRootPath(QDir::tempPath()); + QTRY_VERIFY(model->rowCount(root) > 0); + + QPersistentModelIndex idx = model->index(0, 1, root); + model->sort(0, Qt::AscendingOrder); + model->sort(0, Qt::DescendingOrder); + QVERIFY(idx.column() != 0); + + model->setRootPath(QDir::homePath()); + + QFETCH(bool, fileDialogMode); + + MyFriendFileSystemModel *myModel = new MyFriendFileSystemModel(); + QTreeView *tree = new QTreeView(); + +#ifdef QT_BUILD_INTERNAL + if (fileDialogMode) + myModel->d_func()->disableRecursiveSort = true; +#endif + + QDir dir(QDir::tempPath()); + //initialize the randomness + qsrand(QDateTime::currentDateTime().toTime_t()); + QString tempName = QLatin1String("sortTemp.") + QString::number(qrand()); + dir.mkdir(tempName); + dir.cd(tempName); + QTRY_VERIFY(dir.exists()); + + const QString dirPath = dir.absolutePath(); + QVERIFY(dir.exists()); + + //Create a file that will be at the end when sorting by name (For Mac, the default) + //but if we sort by size descending it will be the first + QFile tempFile(dirPath + "/plop2.txt"); + tempFile.open(QIODevice::WriteOnly | QIODevice::Text); + QTextStream out(&tempFile); + out << "The magic number is: " << 49 << "\n"; + tempFile.close(); + + QFile tempFile2(dirPath + "/plop.txt"); + tempFile2.open(QIODevice::WriteOnly | QIODevice::Text); + QTextStream out2(&tempFile2); + out2 << "The magic number is : " << 49 << " but i write some stuff in the file \n"; + tempFile2.close(); + + myModel->setRootPath(""); + myModel->setFilter(QDir::AllEntries | QDir::System | QDir::Hidden); + tree->setSortingEnabled(true); + tree->setModel(myModel); + tree->show(); + tree->resize(800, 800); + QTest::qWait(500); + tree->header()->setSortIndicator(1,Qt::DescendingOrder); + tree->header()->setResizeMode(0, QHeaderView::ResizeToContents); + QStringList dirsToOpen; + do + { + dirsToOpen<<dir.absolutePath(); + } while (dir.cdUp()); + + for (int i = dirsToOpen.size() -1 ; i > 0 ; --i) { + QString path = dirsToOpen[i]; + QTest::qWait(500); + tree->expand(myModel->index(path, 0)); + } + tree->expand(myModel->index(dirPath, 0)); + QTest::qWait(500); + QModelIndex parent = myModel->index(dirPath, 0); + QList<QString> expectedOrder; + expectedOrder << tempFile2.fileName() << tempFile.fileName() << dirPath + QChar('/') + "." << dirPath + QChar('/') + ".."; + //File dialog Mode means sub trees are not sorted, only the current root + if (fileDialogMode) { + // FIXME: we were only able to disableRecursiveSort in developer builds, so we can only + // stably perform this test for developer builds +#ifdef QT_BUILD_INTERNAL + QList<QString> actualRows; + for(int i = 0; i < myModel->rowCount(parent); ++i) + { + actualRows << dirPath + QChar('/') + myModel->index(i, 1, parent).data(QFileSystemModel::FileNameRole).toString(); + } + QVERIFY(actualRows != expectedOrder); +#endif + } else { + for(int i = 0; i < myModel->rowCount(parent); ++i) + { + QTRY_COMPARE(dirPath + QChar('/') + myModel->index(i, 1, parent).data(QFileSystemModel::FileNameRole).toString(), expectedOrder.at(i)); + } + } + + delete tree; + delete myModel; + + dir.setPath(QDir::tempPath()); + dir.cd(tempName); + tempFile.remove(); + tempFile2.remove(); + dir.cdUp(); + dir.rmdir(tempName); + +} + +void tst_QFileSystemModel::mkdir() +{ + QString tmp = QDir::tempPath(); + QString newFolderPath = QDir::toNativeSeparators(tmp + '/' + "NewFoldermkdirtest4"); + QModelIndex tmpDir = model->index(tmp); + QVERIFY(tmpDir.isValid()); + QDir bestatic(newFolderPath); + if (bestatic.exists()) { + if (!bestatic.rmdir(newFolderPath)) + qWarning() << "unable to remove" << newFolderPath; + QTest::qWait(WAITTIME); + } + model->mkdir(tmpDir, "NewFoldermkdirtest3"); + model->mkdir(tmpDir, "NewFoldermkdirtest5"); + QModelIndex idx = model->mkdir(tmpDir, "NewFoldermkdirtest4"); + QVERIFY(idx.isValid()); + int oldRow = idx.row(); + QTest::qWait(WAITTIME); + idx = model->index(newFolderPath); + QDir cleanup(tmp); + QVERIFY(cleanup.rmdir(QLatin1String("NewFoldermkdirtest3"))); + QVERIFY(cleanup.rmdir(QLatin1String("NewFoldermkdirtest5"))); + bestatic.rmdir(newFolderPath); + QVERIFY(0 != idx.row()); + QCOMPARE(oldRow, idx.row()); +} + +void tst_QFileSystemModel::caseSensitivity() +{ + QString tmp = flatDirTestPath; + QStringList files; + files << "a" << "c" << "C"; + QVERIFY(createFiles(tmp, files)); + QModelIndex root = model->index(tmp); + QCOMPARE(model->rowCount(root), 0); + for (int i = 0; i < files.count(); ++i) { + QVERIFY(model->index(tmp + '/' + files.at(i)).isValid()); + } +} + +#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) +void tst_QFileSystemModel::Win32LongFileName() +{ + QString tmp = flatDirTestPath; + QStringList files; + files << "aaaaaaaaaa" << "bbbbbbbbbb" << "cccccccccc"; + QVERIFY(createFiles(tmp, files)); + QModelIndex root = model->setRootPath(tmp); + QTRY_VERIFY(model->index(tmp + QLatin1String("/aaaaaa~1")).isValid()); + QTRY_VERIFY(model->index(tmp + QLatin1String("/bbbbbb~1")).isValid()); + QTRY_VERIFY(model->index(tmp + QLatin1String("/cccccc~1")).isValid()); +} +#endif + +void tst_QFileSystemModel::drives_data() +{ + QTest::addColumn<QString>("path"); + QTest::newRow("current") << QDir::currentPath(); + QTest::newRow("slash") << "/"; + QTest::newRow("My Computer") << "My Computer"; +} + +void tst_QFileSystemModel::drives() +{ + QFETCH(QString, path); + QFileSystemModel model; + model.setRootPath(path); + model.fetchMore(QModelIndex()); + QFileInfoList drives = QDir::drives(); + int driveCount = 0; + foreach(const QFileInfo& driveRoot, drives) + if (driveRoot.exists()) + driveCount++; + QTest::qWait(5000); + QTRY_COMPARE(model.rowCount(), driveCount); +} + +void tst_QFileSystemModel::dirsBeforeFiles() +{ + const QString dirPath = QString("%1/task221717_sortedOrder_test_dir").arg(QDir::tempPath()); + QDir dir(dirPath); + // clean up from last time + if (dir.exists()) { + for (int i = 0; i < 3; ++i) { + QLatin1Char c('a' + i); + dir.rmdir(QString("%1-dir").arg(c)); + QFile::remove(dirPath + QString("/%1-file").arg(c)); + } + dir.rmdir(dirPath); + } + QVERIFY(dir.mkpath(dirPath)); + QVERIFY(QDir(dirPath).exists()); + + for (int i = 0; i < 3; ++i) { + QLatin1Char c('a' + i); + dir.mkdir(QString("%1-dir").arg(c)); + QFile file(dirPath + QString("/%1-file").arg(c)); + file.open(QIODevice::ReadWrite); + file.close(); + } + + QModelIndex root = model->setRootPath(dirPath); + QTest::qWait(1000); // allow model to be notified by the file system watcher + + // ensure that no file occurs before a directory + for (int i = 0; i < model->rowCount(root); ++i) { +#ifndef Q_OS_MAC + QVERIFY(i == 0 || + !(model->fileInfo(model->index(i - 1, 0, root)).isFile() + && model->fileInfo(model->index(i, 0, root)).isDir())); +#else + QVERIFY(i == 0 || + model->fileInfo(model->index(i - 1, 0, root)).fileName() < + model->fileInfo(model->index(i, 0, root)).fileName()); +#endif + } +} + +void tst_QFileSystemModel::roleNames_data() +{ + QTest::addColumn<int>("role"); + QTest::addColumn<QByteArray>("roleName"); + QTest::newRow("decoration") << int(Qt::DecorationRole) << QByteArray("decoration"); + QTest::newRow("display") << int(Qt::DisplayRole) << QByteArray("display"); + QTest::newRow("fileIcon") << int(QFileSystemModel::FileIconRole) << QByteArray("fileIcon"); + QTest::newRow("filePath") << int(QFileSystemModel::FilePathRole) << QByteArray("filePath"); + QTest::newRow("fileName") << int(QFileSystemModel::FileNameRole) << QByteArray("fileName"); + QTest::newRow("filePermissions") << int(QFileSystemModel::FilePermissions) << QByteArray("filePermissions"); +} + +void tst_QFileSystemModel::roleNames() +{ + QFileSystemModel model; + QHash<int, QByteArray> roles = model.roleNames(); + + QFETCH(int, role); + QVERIFY(roles.contains(role)); + + QFETCH(QByteArray, roleName); + QList<QByteArray> values = roles.values(role); + QVERIFY(values.contains(roleName)); +} + +QTEST_MAIN(tst_QFileSystemModel) +#include "tst_qfilesystemmodel.moc" + diff --git a/tests/auto/widgets/dialogs/qfontdialog/.gitignore b/tests/auto/widgets/dialogs/qfontdialog/.gitignore new file mode 100644 index 0000000000..5bd48d4951 --- /dev/null +++ b/tests/auto/widgets/dialogs/qfontdialog/.gitignore @@ -0,0 +1 @@ +tst_qfontdialog diff --git a/tests/auto/widgets/dialogs/qfontdialog/qfontdialog.pro b/tests/auto/widgets/dialogs/qfontdialog/qfontdialog.pro new file mode 100644 index 0000000000..5a0c2b66bf --- /dev/null +++ b/tests/auto/widgets/dialogs/qfontdialog/qfontdialog.pro @@ -0,0 +1,12 @@ +load(qttest_p4) + +QT += widgets widgets-private +QT += core-private gui-private + +SOURCES += tst_qfontdialog.cpp + +mac:!qpa { + OBJECTIVE_SOURCES += tst_qfontdialog_mac_helpers.mm + LIBS += -framework Cocoa +} + diff --git a/tests/auto/widgets/dialogs/qfontdialog/tst_qfontdialog.cpp b/tests/auto/widgets/dialogs/qfontdialog/tst_qfontdialog.cpp new file mode 100644 index 0000000000..3f12b7501c --- /dev/null +++ b/tests/auto/widgets/dialogs/qfontdialog/tst_qfontdialog.cpp @@ -0,0 +1,186 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtTest/QtTest> + + +#include <qapplication.h> +#include <qfontinfo.h> +#include <qtimer.h> +#include <qmainwindow.h> +#include <qlistview.h> +#include "qfontdialog.h" +#include <private/qfontdialog_p.h> + +//TESTED_CLASS= +//TESTED_FILES= + +QT_FORWARD_DECLARE_CLASS(QtTestEventThread) + +class tst_QFontDialog : public QObject +{ + Q_OBJECT + +public: + tst_QFontDialog(); + virtual ~tst_QFontDialog(); + + +public slots: + void postKeyReturn(); + +public slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); +private slots: + void defaultOkButton(); + void setFont(); + void task256466_wrongStyle(); +}; + +tst_QFontDialog::tst_QFontDialog() +{ +} + +tst_QFontDialog::~tst_QFontDialog() +{ +} + +void tst_QFontDialog::initTestCase() +{ +} + +void tst_QFontDialog::cleanupTestCase() +{ +} + +void tst_QFontDialog::init() +{ +} + +void tst_QFontDialog::cleanup() +{ +} + + +void tst_QFontDialog::postKeyReturn() { +#ifndef Q_WS_MAC + QWidgetList list = QApplication::topLevelWidgets(); + for (int i=0; i<list.count(); ++i) { + QFontDialog *dialog = qobject_cast<QFontDialog*>(list[i]); + if (dialog) { + QTest::keyClick( list[i], Qt::Key_Return, Qt::NoModifier ); + return; + } + } +#else + extern void click_cocoa_button(); + click_cocoa_button(); +#endif +} + +void tst_QFontDialog::defaultOkButton() +{ + bool ok = FALSE; + QTimer::singleShot(2000, this, SLOT(postKeyReturn())); + QFontDialog::getFont(&ok); + QVERIFY(ok == TRUE); +} + + +void tst_QFontDialog::setFont() +{ + /* The font should be the same before as it is after if nothing changed + while the font dialog was open. + Task #27662 + */ + bool ok = FALSE; +#if defined Q_OS_HPUX + QString fontName = "Courier"; + int fontSize = 25; +#elif defined Q_OS_AIX + QString fontName = "Charter"; + int fontSize = 13; +#else + QString fontName = "Arial"; + int fontSize = 24; +#endif + QFont f1(fontName, fontSize); + f1.setPixelSize(QFontInfo(f1).pixelSize()); + QTimer::singleShot(2000, this, SLOT(postKeyReturn())); + QFont f2 = QFontDialog::getFont(&ok, f1); + QCOMPARE(QFontInfo(f2).pointSize(), QFontInfo(f1).pointSize()); +} + + +class FriendlyFontDialog : public QFontDialog +{ + friend class tst_QFontDialog; + Q_DECLARE_PRIVATE(QFontDialog); +}; + +void tst_QFontDialog::task256466_wrongStyle() +{ + QFontDatabase fdb; + FriendlyFontDialog dialog; + QListView *familyList = reinterpret_cast<QListView*>(dialog.d_func()->familyList); + QListView *styleList = reinterpret_cast<QListView*>(dialog.d_func()->styleList); + QListView *sizeList = reinterpret_cast<QListView*>(dialog.d_func()->sizeList); + for (int i = 0; i < familyList->model()->rowCount(); ++i) { + QModelIndex currentFamily = familyList->model()->index(i, 0); + familyList->setCurrentIndex(currentFamily); + const QFont current = dialog.currentFont(), + expected = fdb.font(currentFamily.data().toString(), + styleList->currentIndex().data().toString(), sizeList->currentIndex().data().toInt()); + QCOMPARE(current.family(), expected.family()); + QCOMPARE(current.style(), expected.style()); + QCOMPARE(current.pointSizeF(), expected.pointSizeF()); + } +} + + + + +QTEST_MAIN(tst_QFontDialog) +#include "tst_qfontdialog.moc" diff --git a/tests/auto/widgets/dialogs/qfontdialog/tst_qfontdialog_mac_helpers.mm b/tests/auto/widgets/dialogs/qfontdialog/tst_qfontdialog_mac_helpers.mm new file mode 100644 index 0000000000..d12f696f7e --- /dev/null +++ b/tests/auto/widgets/dialogs/qfontdialog/tst_qfontdialog_mac_helpers.mm @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <private/qt_mac_p.h> +#include <AppKit/AppKit.h> + +void click_cocoa_button() +{ + QMacCocoaAutoReleasePool pool; + NSArray *windows = [NSApp windows]; + for (NSWindow *window in windows) { + // This is NOT how one should do RTTI, but since I don't want to leak the class too much... + if ([[window delegate] respondsToSelector:@selector(qtFont)]) { + NSArray *subviews = [[window contentView] subviews]; + for (NSView *view in subviews) { + if ([view isKindOfClass:[NSButton class]] + && [[static_cast<NSButton *>(view) title] isEqualTo:@"OK"]) { + [static_cast<NSButton *>(view) performClick:view]; + [NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined location:NSZeroPoint + modifierFlags:0 timestamp:0. windowNumber:0 context:0 + subtype:SHRT_MAX data1:0 data2:0] atStart:NO]; + + break; + } + } + break; + } + } +} diff --git a/tests/auto/widgets/dialogs/qinputdialog/.gitignore b/tests/auto/widgets/dialogs/qinputdialog/.gitignore new file mode 100644 index 0000000000..b62797193c --- /dev/null +++ b/tests/auto/widgets/dialogs/qinputdialog/.gitignore @@ -0,0 +1 @@ +tst_qinputdialog diff --git a/tests/auto/widgets/dialogs/qinputdialog/qinputdialog.pro b/tests/auto/widgets/dialogs/qinputdialog/qinputdialog.pro new file mode 100644 index 0000000000..f7e56bd783 --- /dev/null +++ b/tests/auto/widgets/dialogs/qinputdialog/qinputdialog.pro @@ -0,0 +1,5 @@ +load(qttest_p4) +QT += widgets +SOURCES += tst_qinputdialog.cpp + + diff --git a/tests/auto/widgets/dialogs/qinputdialog/tst_qinputdialog.cpp b/tests/auto/widgets/dialogs/qinputdialog/tst_qinputdialog.cpp new file mode 100644 index 0000000000..5c74ab217b --- /dev/null +++ b/tests/auto/widgets/dialogs/qinputdialog/tst_qinputdialog.cpp @@ -0,0 +1,430 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtTest/QtTest> +#include <QString> +#include <QSpinBox> +#include <QPushButton> +#include <QLineEdit> +#include <QComboBox> +#include <QDialogButtonBox> +#include <qinputdialog.h> + +//TESTED_CLASS= +//TESTED_FILES= + +class tst_QInputDialog : public QObject +{ + Q_OBJECT + QWidget *parent; + QDialog::DialogCode doneCode; + void (*testFunc)(QInputDialog *); + static void testFuncGetInteger(QInputDialog *dialog); + static void testFuncGetDouble(QInputDialog *dialog); + static void testFuncGetText(QInputDialog *dialog); + static void testFuncGetItem(QInputDialog *dialog); + void timerEvent(QTimerEvent *event); +private slots: + void getInteger_data(); + void getInteger(); + void getDouble_data(); + void getDouble(); + void task255502getDouble(); + void getText_data(); + void getText(); + void getItem_data(); + void getItem(); + void task256299_getTextReturnNullStringOnRejected(); + void inputMethodHintsOfChildWidget(); +}; + +QString stripFraction(const QString &s) +{ + int period; + if (s.contains('.')) + period = s.indexOf('.'); + else if (s.contains(',')) + period = s.indexOf(','); + else + return s; + int end; + for (end = s.size() - 1; end > period && s[end] == '0'; --end) ; + return s.left(end + (end == period ? 0 : 1)); +} + +QString normalizeNumericString(const QString &s) +{ + return stripFraction(s); // assumed to be sufficient +} + +void _keyClick(QWidget *widget, char key) +{ + QTest::keyClick(widget, key); +} + +void _keyClick(QWidget *widget, Qt::Key key) +{ + QTest::keyClick(widget, key); +} + +template <typename SpinBoxType> +void testTypingValue( + SpinBoxType* sbox, QPushButton *okButton, const QString &value) +{ + sbox->selectAll(); + for (int i = 0; i < value.size(); ++i) { + const QChar valChar = value[i]; + _keyClick(static_cast<QWidget *>(sbox), valChar.toAscii()); // ### always guaranteed to work? + if (sbox->hasAcceptableInput()) + QVERIFY(okButton->isEnabled()); + else + QVERIFY(!okButton->isEnabled()); + } +} + +void testTypingValue(QLineEdit *ledit, QPushButton *okButton, const QString &value) +{ + ledit->selectAll(); + for (int i = 0; i < value.size(); ++i) { + const QChar valChar = value[i]; + _keyClick(ledit, valChar.toAscii()); // ### always guaranteed to work? + QVERIFY(ledit->hasAcceptableInput()); + QVERIFY(okButton->isEnabled()); + } +} + +template <typename SpinBoxType, typename ValueType> +void testInvalidateAndRestore( + SpinBoxType* sbox, QPushButton *okButton, QLineEdit *ledit, ValueType * = 0) +{ + const ValueType lastValidValue = sbox->value(); + + sbox->selectAll(); + _keyClick(ledit, Qt::Key_Delete); + QVERIFY(!sbox->hasAcceptableInput()); + QVERIFY(!okButton->isEnabled()); + + _keyClick(ledit, Qt::Key_Return); // should work with Qt::Key_Enter too + QVERIFY(sbox->hasAcceptableInput()); + QVERIFY(okButton->isEnabled()); + QCOMPARE(sbox->value(), lastValidValue); + QLocale loc; + QCOMPARE( + normalizeNumericString(ledit->text()), + normalizeNumericString(loc.toString(sbox->value()))); +} + +template <typename SpinBoxType, typename ValueType> +void testGetNumeric(QInputDialog *dialog, SpinBoxType * = 0, ValueType * = 0) +{ + SpinBoxType *sbox = qFindChild<SpinBoxType *>(dialog); + QVERIFY(sbox != 0); + + QLineEdit *ledit = qFindChild<QLineEdit *>(static_cast<QObject *>(sbox)); + QVERIFY(ledit != 0); + + QDialogButtonBox *bbox = qFindChild<QDialogButtonBox *>(dialog); + QVERIFY(bbox != 0); + QPushButton *okButton = bbox->button(QDialogButtonBox::Ok); + QVERIFY(okButton != 0); + + QVERIFY(sbox->value() >= sbox->minimum()); + QVERIFY(sbox->value() <= sbox->maximum()); + QVERIFY(sbox->hasAcceptableInput()); + QLocale loc; + QCOMPARE( + normalizeNumericString(ledit->selectedText()), + normalizeNumericString(loc.toString(sbox->value()))); + QVERIFY(okButton->isEnabled()); + + const ValueType origValue = sbox->value(); + + testInvalidateAndRestore<SpinBoxType, ValueType>(sbox, okButton, ledit); + testTypingValue<SpinBoxType>(sbox, okButton, QString("%1").arg(sbox->minimum())); + testTypingValue<SpinBoxType>(sbox, okButton, QString("%1").arg(sbox->maximum())); + testTypingValue<SpinBoxType>(sbox, okButton, QString("%1").arg(sbox->minimum() - 1)); + testTypingValue<SpinBoxType>(sbox, okButton, QString("%1").arg(sbox->maximum() + 1)); + testTypingValue<SpinBoxType>(sbox, okButton, "0"); + testTypingValue<SpinBoxType>(sbox, okButton, "0.0"); + testTypingValue<SpinBoxType>(sbox, okButton, "foobar"); + + testTypingValue<SpinBoxType>(sbox, okButton, loc.toString(origValue)); +} + +void testGetText(QInputDialog *dialog) +{ + QLineEdit *ledit = qFindChild<QLineEdit *>(dialog); + QVERIFY(ledit); + + QDialogButtonBox *bbox = qFindChild<QDialogButtonBox *>(dialog); + QVERIFY(bbox); + QPushButton *okButton = bbox->button(QDialogButtonBox::Ok); + QVERIFY(okButton); + + QVERIFY(ledit->hasAcceptableInput()); + QCOMPARE(ledit->selectedText(), ledit->text()); + QVERIFY(okButton->isEnabled()); + const QString origValue = ledit->text(); + + testTypingValue(ledit, okButton, origValue); +} + +void testGetItem(QInputDialog *dialog) +{ + QComboBox *cbox = qFindChild<QComboBox *>(dialog); + QVERIFY(cbox); + + QDialogButtonBox *bbox = qFindChild<QDialogButtonBox *>(dialog); + QVERIFY(bbox); + QPushButton *okButton = bbox->button(QDialogButtonBox::Ok); + QVERIFY(okButton); + + QVERIFY(okButton->isEnabled()); + const int origIndex = cbox->currentIndex(); + cbox->setCurrentIndex(origIndex - 1); + cbox->setCurrentIndex(origIndex); + QVERIFY(okButton->isEnabled()); +} + +void tst_QInputDialog::testFuncGetInteger(QInputDialog *dialog) +{ + testGetNumeric<QSpinBox, int>(dialog); +} + +void tst_QInputDialog::testFuncGetDouble(QInputDialog *dialog) +{ + testGetNumeric<QDoubleSpinBox, double>(dialog); +} + +void tst_QInputDialog::testFuncGetText(QInputDialog *dialog) +{ + ::testGetText(dialog); +} + +void tst_QInputDialog::testFuncGetItem(QInputDialog *dialog) +{ + ::testGetItem(dialog); +} + +void tst_QInputDialog::timerEvent(QTimerEvent *event) +{ + killTimer(event->timerId()); + QInputDialog *dialog = qFindChild<QInputDialog *>(parent); + QVERIFY(dialog); + if (testFunc) + testFunc(dialog); + dialog->done(doneCode); // cause static function call to return +} + +void tst_QInputDialog::getInteger_data() +{ + QTest::addColumn<int>("min"); + QTest::addColumn<int>("max"); + QTest::newRow("getInteger() - -") << -20 << -10; + QTest::newRow("getInteger() - 0") << -20 << 0; + QTest::newRow("getInteger() - +") << -20 << 20; + QTest::newRow("getInteger() 0 +") << 0 << 20; + QTest::newRow("getInteger() + +") << 10 << 20; +} + +void tst_QInputDialog::getInteger() +{ + QFETCH(int, min); + QFETCH(int, max); + QVERIFY(min < max); + parent = new QWidget; + doneCode = QDialog::Accepted; + testFunc = &tst_QInputDialog::testFuncGetInteger; + startTimer(0); + bool ok = false; + const int value = min + (max - min) / 2; + const int result = QInputDialog::getInteger(parent, "", "", value, min, max, 1, &ok); + QVERIFY(ok); + QCOMPARE(result, value); + delete parent; +} + +void tst_QInputDialog::getDouble_data() +{ + QTest::addColumn<double>("min"); + QTest::addColumn<double>("max"); + QTest::addColumn<int>("decimals"); + QTest::newRow("getDouble() - - d0") << -20.0 << -10.0 << 0; + QTest::newRow("getDouble() - 0 d0") << -20.0 << 0.0 << 0; + QTest::newRow("getDouble() - + d0") << -20.0 << 20.0 << 0; + QTest::newRow("getDouble() 0 + d0") << 0.0 << 20.0 << 0; + QTest::newRow("getDouble() + + d0") << 10.0 << 20.0 << 0; + QTest::newRow("getDouble() - - d1") << -20.5 << -10.5 << 1; + QTest::newRow("getDouble() - 0 d1") << -20.5 << 0.0 << 1; + QTest::newRow("getDouble() - + d1") << -20.5 << 20.5 << 1; + QTest::newRow("getDouble() 0 + d1") << 0.0 << 20.5 << 1; + QTest::newRow("getDouble() + + d1") << 10.5 << 20.5 << 1; + QTest::newRow("getDouble() - - d2") << -20.05 << -10.05 << 2; + QTest::newRow("getDouble() - 0 d2") << -20.05 << 0.0 << 2; + QTest::newRow("getDouble() - + d2") << -20.05 << 20.05 << 2; + QTest::newRow("getDouble() 0 + d2") << 0.0 << 20.05 << 2; + QTest::newRow("getDouble() + + d2") << 10.05 << 20.05 << 2; +} + +void tst_QInputDialog::getDouble() +{ + QFETCH(double, min); + QFETCH(double, max); + QFETCH(int, decimals); + QVERIFY(min < max && decimals >= 0 && decimals <= 13); + parent = new QWidget; + doneCode = QDialog::Accepted; + testFunc = &tst_QInputDialog::testFuncGetDouble; + startTimer(0); + bool ok = false; + // avoid decimals due to inconsistent roundoff behavior in QInputDialog::getDouble() + // (at one decimal, 10.25 is rounded off to 10.2, while at two decimals, 10.025 is + // rounded off to 10.03) + const double value = static_cast<int>(min + (max - min) / 2); + const double result = + QInputDialog::getDouble(parent, "", "", value, min, max, decimals, &ok); + QVERIFY(ok); + QCOMPARE(result, value); + delete parent; +} + +void tst_QInputDialog::task255502getDouble() +{ + parent = new QWidget; + doneCode = QDialog::Accepted; + testFunc = &tst_QInputDialog::testFuncGetDouble; + startTimer(0); + bool ok = false; + const double value = 0.001; + const double result = + QInputDialog::getDouble(parent, "", "", value, -1, 1, 4, &ok); + QVERIFY(ok); + QCOMPARE(result, value); + delete parent; +} + +void tst_QInputDialog::getText_data() +{ + QTest::addColumn<QString>("text"); + QTest::newRow("getText() 1") << ""; + QTest::newRow("getText() 2") << "foobar"; + QTest::newRow("getText() 3") << " foobar"; + QTest::newRow("getText() 4") << "foobar "; + QTest::newRow("getText() 5") << "aAzZ`1234567890-=~!@#$%^&*()_+[]{}\\|;:'\",.<>/?"; +} + +void tst_QInputDialog::getText() +{ + QFETCH(QString, text); + parent = new QWidget; + doneCode = QDialog::Accepted; + testFunc = &tst_QInputDialog::testFuncGetText; + startTimer(0); + bool ok = false; + const QString result = QInputDialog::getText(parent, "", "", QLineEdit::Normal, text, &ok); + QVERIFY(ok); + QCOMPARE(result, text); + delete parent; +} + +void tst_QInputDialog::task256299_getTextReturnNullStringOnRejected() +{ + parent = new QWidget; + doneCode = QDialog::Rejected; + testFunc = 0; + startTimer(0); + bool ok = true; + const QString result = QInputDialog::getText(parent, "", "", QLineEdit::Normal, "foobar", &ok); + QVERIFY(!ok); + QVERIFY(result.isNull()); + delete parent; +} + +void tst_QInputDialog::getItem_data() +{ + QTest::addColumn<QStringList>("items"); + QTest::addColumn<bool>("editable"); + QTest::newRow("getItem() 1 true") << (QStringList() << "") << true; + QTest::newRow("getItem() 2 true") << + (QStringList() << "spring" << "summer" << "fall" << "winter") << true; + QTest::newRow("getItem() 1 false") << (QStringList() << "") << false; + QTest::newRow("getItem() 2 false") << + (QStringList() << "spring" << "summer" << "fall" << "winter") << false; +} + +void tst_QInputDialog::getItem() +{ + QFETCH(QStringList, items); + QFETCH(bool, editable); + parent = new QWidget; + doneCode = QDialog::Accepted; + testFunc = &tst_QInputDialog::testFuncGetItem; + startTimer(0); + bool ok = false; + const int index = items.size() / 2; + const QString result = QInputDialog::getItem(parent, "", "", items, index, editable, &ok); + QVERIFY(ok); + QCOMPARE(result, items[index]); + delete parent; +} + +void tst_QInputDialog::inputMethodHintsOfChildWidget() +{ + QInputDialog dialog; + dialog.setInputMode(QInputDialog::TextInput); + QList<QObject *> children = dialog.children(); + QLineEdit *editWidget = 0; + for (int c = 0; c < children.size(); c++) { + editWidget = qobject_cast<QLineEdit *>(children.at(c)); + if (editWidget) + break; + } + QVERIFY(editWidget); + QCOMPARE(editWidget->inputMethodHints(), dialog.inputMethodHints()); + QCOMPARE(editWidget->inputMethodHints(), Qt::ImhNone); + dialog.setInputMethodHints(Qt::ImhDigitsOnly); + QCOMPARE(editWidget->inputMethodHints(), dialog.inputMethodHints()); + QCOMPARE(editWidget->inputMethodHints(), Qt::ImhDigitsOnly); +} + +QTEST_MAIN(tst_QInputDialog) +#include "tst_qinputdialog.moc" diff --git a/tests/auto/widgets/dialogs/qmessagebox/.gitignore b/tests/auto/widgets/dialogs/qmessagebox/.gitignore new file mode 100644 index 0000000000..19a0a35121 --- /dev/null +++ b/tests/auto/widgets/dialogs/qmessagebox/.gitignore @@ -0,0 +1 @@ +tst_qmessagebox diff --git a/tests/auto/widgets/dialogs/qmessagebox/qmessagebox.pro b/tests/auto/widgets/dialogs/qmessagebox/qmessagebox.pro new file mode 100644 index 0000000000..e3f6ddb1f6 --- /dev/null +++ b/tests/auto/widgets/dialogs/qmessagebox/qmessagebox.pro @@ -0,0 +1,9 @@ +load(qttest_p4) +TEMPLATE = app +TARGET = tst_qmessagebox +QT += widgets +DEPENDPATH += . +INCLUDEPATH += . + +# Input +SOURCES += tst_qmessagebox.cpp diff --git a/tests/auto/widgets/dialogs/qmessagebox/tst_qmessagebox.cpp b/tests/auto/widgets/dialogs/qmessagebox/tst_qmessagebox.cpp new file mode 100644 index 0000000000..a07d7cf7a9 --- /dev/null +++ b/tests/auto/widgets/dialogs/qmessagebox/tst_qmessagebox.cpp @@ -0,0 +1,674 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <QtTest/QtTest> +#include <QMessageBox> +#include <QDebug> +#include <QPair> +#include <QList> +#include <QPointer> +#include <QTimer> +#include <QApplication> +#include <QPushButton> +#include <QDialogButtonBox> +#if defined(Q_WS_MAC) && !defined(QT_NO_STYLE_MAC) +#include <QMacStyle> +#endif +#if !defined(QT_NO_STYLE_CLEANLOOKS) +#include <QCleanlooksStyle> +#endif + +//TESTED_CLASS= +//TESTED_FILES= + +#define CONVENIENCE_FUNC_SYMS(func) \ + { \ + int x1 = QMessageBox::func(0, "Foo", "Bar"); \ + int x3 = QMessageBox::func(0, "Foo", "Bar", "Save"); \ + int x6 = QMessageBox::func(0, "Foo", "Bar", "Save", "Save As"); \ + int x7 = QMessageBox::func(0, "Foo", "Bar", "Save", "Save As", "Dont Save"); \ + int x8 = QMessageBox::func(0, "Foo", "Bar", "Save", "Save As", "Dont Save", 1); \ + int x9 = QMessageBox::func(0, "Foo", "Bar", "Save", "Save As", "Dont Save", 1, 2); \ + int x10 = QMessageBox::func(0, "Foo", "Bar", QMessageBox::YesAll, QMessageBox::Yes); \ + int x11 = QMessageBox::func(0, "Foo", "Bar", QMessageBox::YesAll, QMessageBox::Yes, \ + QMessageBox::No); \ + qDebug("%d %d %d %d %d %d %d %d", x1, x3, x6, x7, x8, x9, x10, x11); \ + { \ + int x4 = QMessageBox::func(0, "Foo", "Bar", (int)QMessageBox::Yes, (int)QMessageBox::No); \ + int x5 = QMessageBox::func(0, "Foo", "Bar", QMessageBox::Yes, (int)QMessageBox::No); \ + int x6 = QMessageBox::func(0, "Foo", "Bar", QMessageBox::Yes | QMessageBox::Default, (int)QMessageBox::No); \ + int x7 = QMessageBox::func(0, "Foo", "Bar", (int)QMessageBox::Yes, QMessageBox::No); \ + int x8 = QMessageBox::func(0, "Foo", "Bar", QMessageBox::Yes, QMessageBox::No); \ + int x9 = QMessageBox::func(0, "Foo", "Bar", QMessageBox::Yes | QMessageBox::Default, QMessageBox::No); \ + int x10 = QMessageBox::func(0, "Foo", "Bar", (int)QMessageBox::Yes, (int)QMessageBox::No, (int)QMessageBox::Ok); \ + int x11 = QMessageBox::func(0, "Foo", "Bar", QMessageBox::Yes, (int)QMessageBox::No, (int)QMessageBox::Ok); \ + int x12 = QMessageBox::func(0, "Foo", "Bar", QMessageBox::Yes | QMessageBox::Default, (int)QMessageBox::No, (int)QMessageBox::Ok); \ + int x13 = QMessageBox::func(0, "Foo", "Bar", (int)QMessageBox::Yes, QMessageBox::No, (int)QMessageBox::Ok); \ + int x14 = QMessageBox::func(0, "Foo", "Bar", QMessageBox::Yes, QMessageBox::No, (int)QMessageBox::Ok); \ + int x15 = QMessageBox::func(0, "Foo", "Bar", QMessageBox::Yes | QMessageBox::Default, QMessageBox::No, (int)QMessageBox::Ok); \ + int x16 = QMessageBox::func(0, "Foo", "Bar", (int)QMessageBox::Yes, (int)QMessageBox::No, QMessageBox::Ok); \ + int x17 = QMessageBox::func(0, "Foo", "Bar", QMessageBox::Yes, (int)QMessageBox::No, QMessageBox::Ok); \ + int x18 = QMessageBox::func(0, "Foo", "Bar", QMessageBox::Yes | QMessageBox::Default, (int)QMessageBox::No, QMessageBox::Ok); \ + int x19 = QMessageBox::func(0, "Foo", "Bar", (int)QMessageBox::Yes, QMessageBox::No, QMessageBox::Ok); \ + int x20 = QMessageBox::func(0, "Foo", "Bar", QMessageBox::Yes, QMessageBox::No, QMessageBox::Ok); \ + int x21 = QMessageBox::func(0, "Foo", "Bar", QMessageBox::Yes | QMessageBox::Default, QMessageBox::No, QMessageBox::Ok); \ + qDebug("%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d", x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21); \ + } \ + } + +#define CONVENIENCE_FUNC_SYMS_EXTRA(func) \ + { \ + int x1 = QMessageBox::func(0, "Foo", "Bar", (int)QMessageBox::Yes); \ + int x2 = QMessageBox::func(0, "Foo", "Bar", QMessageBox::Yes); \ + int x3 = QMessageBox::func(0, "Foo", "Bar", QMessageBox::Yes | QMessageBox::Default); \ + qDebug("%d %d %d", x1, x2, x3); \ + } + +class tst_QMessageBox : public QObject +{ + Q_OBJECT +public: + tst_QMessageBox(); + int exec(QMessageBox *msgBox, int key = -1); + void sendKeySoon(); + +public slots: + void sendKey(); + +private slots: + void sanityTest(); + void defaultButton(); + void escapeButton(); + void button(); + void statics(); + void about(); + void detailsText(); + void detailsButtonText(); + +#ifndef Q_WS_MAC + void shortcut(); +#endif + + void staticSourceCompat(); + void instanceSourceCompat(); + + void testSymbols(); + void incorrectDefaultButton(); + void updateSize(); + + void setInformativeText(); + void iconPixmap(); + + void init(); + void initTestCase(); + +private: + int keyToSend; + QTimer keySendTimer; +}; + +tst_QMessageBox::tst_QMessageBox() : keyToSend(-1) +{ + int argc = qApp->argc(); + QT_REQUIRE_VERSION(argc, qApp->argv(), "4.6.2") +} + +int tst_QMessageBox::exec(QMessageBox *msgBox, int key) +{ + if (key == -1) { + QTimer::singleShot(1000, msgBox, SLOT(close())); + } else { + keyToSend = key; + sendKeySoon(); + } + return msgBox->exec(); +} + +void tst_QMessageBox::sendKey() +{ + if (keyToSend == -2) { + QApplication::activeModalWidget()->close(); + keyToSend = -1; + return; + } + if (keyToSend == -1) + return; + QKeyEvent *ke = new QKeyEvent(QEvent::KeyPress, keyToSend, Qt::NoModifier); + qApp->postEvent(QApplication::activeModalWidget(), ke); + keyToSend = -1; +} + +void tst_QMessageBox::sendKeySoon() +{ + keySendTimer.start(); +} + +void tst_QMessageBox::init() +{ + // if there is any pending key send from the last test, cancel it. + keySendTimer.stop(); +} + +void tst_QMessageBox::initTestCase() +{ + keySendTimer.setInterval(1000); + keySendTimer.setSingleShot(true); + QVERIFY(QObject::connect(&keySendTimer, SIGNAL(timeout()), this, SLOT(sendKey()))); +} + +void tst_QMessageBox::sanityTest() +{ + QMessageBox msgBox; + msgBox.setText("This is insane"); + for (int i = 0; i < 10; i++) + msgBox.setIcon(QMessageBox::Icon(i)); + msgBox.setIconPixmap(QPixmap()); + msgBox.setIconPixmap(QPixmap("whatever.png")); + msgBox.setTextFormat(Qt::RichText); + msgBox.setTextFormat(Qt::PlainText); + exec(&msgBox); +} + +void tst_QMessageBox::button() +{ + QMessageBox msgBox; + msgBox.addButton("retry", QMessageBox::DestructiveRole); + QVERIFY(msgBox.button(QMessageBox::Ok) == 0); // not added yet + QPushButton *b1 = msgBox.addButton(QMessageBox::Ok); + QCOMPARE(msgBox.button(QMessageBox::Ok), (QAbstractButton *)b1); // just added + QCOMPARE(msgBox.standardButton(b1), QMessageBox::Ok); + msgBox.addButton(QMessageBox::Cancel); + QCOMPARE(msgBox.standardButtons(), QMessageBox::Ok | QMessageBox::Cancel); + + // remove the cancel, should not exist anymore + msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); + QVERIFY(msgBox.button(QMessageBox::Cancel) == 0); + QVERIFY(msgBox.button(QMessageBox::Yes) != 0); + + // should not crash + QPushButton *b4 = new QPushButton; + msgBox.addButton(b4, QMessageBox::DestructiveRole); + msgBox.addButton(0, QMessageBox::ActionRole); +} + +void tst_QMessageBox::defaultButton() +{ + QMessageBox msgBox; + QVERIFY(msgBox.defaultButton() == 0); + msgBox.addButton(QMessageBox::Ok); + msgBox.addButton(QMessageBox::Cancel); + QVERIFY(msgBox.defaultButton() == 0); + QPushButton pushButton; + msgBox.setDefaultButton(&pushButton); + QVERIFY(msgBox.defaultButton() == 0); // we have not added it yet + QPushButton *retryButton = msgBox.addButton(QMessageBox::Retry); + msgBox.setDefaultButton(retryButton); + QCOMPARE(msgBox.defaultButton(), retryButton); + exec(&msgBox); + QCOMPARE(msgBox.clickedButton(), msgBox.button(QMessageBox::Cancel)); + + exec(&msgBox, Qt::Key_Enter); + QCOMPARE(msgBox.clickedButton(), (QAbstractButton *)retryButton); + + QAbstractButton *okButton = msgBox.button(QMessageBox::Ok); + msgBox.setDefaultButton(QMessageBox::Ok); + QCOMPARE(msgBox.defaultButton(), (QPushButton *)okButton); + exec(&msgBox, Qt::Key_Enter); + QCOMPARE(msgBox.clickedButton(), okButton); + msgBox.setDefaultButton(QMessageBox::Yes); // its not in there! + QVERIFY(msgBox.defaultButton() == okButton); + msgBox.removeButton(okButton); + delete okButton; + okButton = 0; + QVERIFY(msgBox.defaultButton() == 0); + msgBox.setDefaultButton(QMessageBox::Ok); + QVERIFY(msgBox.defaultButton() == 0); +} + +void tst_QMessageBox::escapeButton() +{ + QMessageBox msgBox; + QVERIFY(msgBox.escapeButton() == 0); + msgBox.addButton(QMessageBox::Ok); + exec(&msgBox); + QVERIFY(msgBox.clickedButton() == msgBox.button(QMessageBox::Ok)); // auto detected (one button only) + msgBox.addButton(QMessageBox::Cancel); + QVERIFY(msgBox.escapeButton() == 0); + QPushButton invalidButton; + msgBox.setEscapeButton(&invalidButton); + QVERIFY(msgBox.escapeButton() == 0); + QAbstractButton *retryButton = msgBox.addButton(QMessageBox::Retry); + + exec(&msgBox); + QVERIFY(msgBox.clickedButton() == msgBox.button(QMessageBox::Cancel)); // auto detected (cancel) + + msgBox.setEscapeButton(retryButton); + QCOMPARE(msgBox.escapeButton(), (QAbstractButton *)retryButton); + + // with escape + exec(&msgBox, Qt::Key_Escape); + QCOMPARE(msgBox.clickedButton(), retryButton); + + // with close + exec(&msgBox); + QCOMPARE(msgBox.clickedButton(), (QAbstractButton *)retryButton); + + QAbstractButton *okButton = msgBox.button(QMessageBox::Ok); + msgBox.setEscapeButton(QMessageBox::Ok); + QCOMPARE(msgBox.escapeButton(), okButton); + exec(&msgBox, Qt::Key_Escape); + QCOMPARE(msgBox.clickedButton(), okButton); + msgBox.setEscapeButton(QMessageBox::Yes); // its not in there! + QVERIFY(msgBox.escapeButton() == okButton); + msgBox.removeButton(okButton); + delete okButton; + okButton = 0; + QVERIFY(msgBox.escapeButton() == 0); + msgBox.setEscapeButton(QMessageBox::Ok); + QVERIFY(msgBox.escapeButton() == 0); + + QMessageBox msgBox2; + msgBox2.addButton(QMessageBox::Yes); + msgBox2.addButton(QMessageBox::No); + exec(&msgBox2); + QVERIFY(msgBox2.clickedButton() == msgBox2.button(QMessageBox::No)); // auto detected (one No button only) + + QPushButton *rejectButton = new QPushButton; + msgBox2.addButton(rejectButton, QMessageBox::RejectRole); + exec(&msgBox2); + QVERIFY(msgBox2.clickedButton() == rejectButton); // auto detected (one reject button only) + + msgBox2.addButton(new QPushButton, QMessageBox::RejectRole); + exec(&msgBox2); + QVERIFY(msgBox2.clickedButton() == msgBox2.button(QMessageBox::No)); // auto detected (one No button only) +} + +void tst_QMessageBox::statics() +{ + QMessageBox::StandardButton (*statics[4])(QWidget *, const QString &, + const QString&, QMessageBox::StandardButtons buttons, + QMessageBox::StandardButton); + + statics[0] = QMessageBox::information; + statics[1] = QMessageBox::critical; + statics[2] = QMessageBox::question; + statics[3] = QMessageBox::warning; + + for (int i = 0; i < 4; i++) { + keyToSend = Qt::Key_Escape; + sendKeySoon(); + QMessageBox::StandardButton sb = (*statics[i])(0, "caption", + "text", QMessageBox::Yes | QMessageBox::No | QMessageBox::Help | QMessageBox::Cancel, + QMessageBox::NoButton); + QCOMPARE(sb, QMessageBox::Cancel); + QCOMPARE(keyToSend, -1); + + keyToSend = -2; // close() + sendKeySoon(); + sb = (*statics[i])(0, "caption", + "text", QMessageBox::Yes | QMessageBox::No | QMessageBox::Help | QMessageBox::Cancel, + QMessageBox::NoButton); + QCOMPARE(sb, QMessageBox::Cancel); + QCOMPARE(keyToSend, -1); + + keyToSend = Qt::Key_Enter; + sendKeySoon(); + sb = (*statics[i])(0, "caption", + "text", QMessageBox::Yes | QMessageBox::No | QMessageBox::Help, + QMessageBox::Yes); + QCOMPARE(sb, QMessageBox::Yes); + QCOMPARE(keyToSend, -1); + + keyToSend = Qt::Key_Enter; + sendKeySoon(); + sb = (*statics[i])(0, "caption", + "text", QMessageBox::Yes | QMessageBox::No | QMessageBox::Help, + QMessageBox::No); + QCOMPARE(sb, QMessageBox::No); + QCOMPARE(keyToSend, -1); + } +} + +// Shortcuts are not used on Mac OS X. +#ifndef Q_WS_MAC +void tst_QMessageBox::shortcut() +{ + QMessageBox msgBox; + msgBox.addButton("O&k", QMessageBox::YesRole); + msgBox.addButton("&No", QMessageBox::YesRole); + msgBox.addButton("&Maybe", QMessageBox::YesRole); + QCOMPARE(exec(&msgBox, Qt::Key_M), 2); +} +#endif + +void tst_QMessageBox::about() +{ + keyToSend = Qt::Key_Escape; + sendKeySoon(); + QMessageBox::about(0, "Caption", "This is an auto test"); + // On Mac, about and aboutQt are not modal, so we need to + // explicitly run the event loop +#ifdef Q_WS_MAC + QTRY_COMPARE(keyToSend, -1); +#else + QCOMPARE(keyToSend, -1); +#endif + +#if !defined(Q_OS_WINCE) + keyToSend = Qt::Key_Enter; +#else + keyToSend = Qt::Key_Escape; +#endif + sendKeySoon(); + QMessageBox::aboutQt(0, "Caption"); +#ifdef Q_WS_MAC + QTRY_COMPARE(keyToSend, -1); +#else + QCOMPARE(keyToSend, -1); +#endif +} + +void tst_QMessageBox::staticSourceCompat() +{ + int ret; + + // source compat tests for < 4.2 + keyToSend = Qt::Key_Enter; + sendKeySoon(); + ret = QMessageBox::information(0, "title", "text", QMessageBox::Yes, QMessageBox::No); + int expectedButton = int(QMessageBox::Yes); +#if defined(Q_WS_MAC) && !defined(QT_NO_STYLE_MAC) + if (qobject_cast<QMacStyle *>(qApp->style())) + expectedButton = int(QMessageBox::No); +#elif !defined(QT_NO_STYLE_CLEANLOOKS) + if (qobject_cast<QCleanlooksStyle *>(qApp->style())) + expectedButton = int(QMessageBox::No); +#endif + QCOMPARE(ret, expectedButton); + QCOMPARE(keyToSend, -1); + + keyToSend = Qt::Key_Enter; + sendKeySoon(); + ret = QMessageBox::information(0, "title", "text", QMessageBox::Yes | QMessageBox::Default, QMessageBox::No); + QCOMPARE(ret, int(QMessageBox::Yes)); + QCOMPARE(keyToSend, -1); + + keyToSend = Qt::Key_Enter; + sendKeySoon(); + ret = QMessageBox::information(0, "title", "text", QMessageBox::Yes, QMessageBox::No | QMessageBox::Default); + QCOMPARE(ret, int(QMessageBox::No)); + QCOMPARE(keyToSend, -1); + + keyToSend = Qt::Key_Enter; + sendKeySoon(); + ret = QMessageBox::information(0, "title", "text", QMessageBox::Yes | QMessageBox::Default, QMessageBox::No | QMessageBox::Escape); + QCOMPARE(ret, int(QMessageBox::Yes)); + QCOMPARE(keyToSend, -1); + + keyToSend = Qt::Key_Enter; + sendKeySoon(); + ret = QMessageBox::information(0, "title", "text", QMessageBox::Yes | QMessageBox::Escape, QMessageBox::No | QMessageBox::Default); + QCOMPARE(ret, int(QMessageBox::No)); + QCOMPARE(keyToSend, -1); + + // the button text versions + keyToSend = Qt::Key_Enter; + sendKeySoon(); + ret = QMessageBox::information(0, "title", "text", "Yes", "No", QString(), 1); + QCOMPARE(ret, 1); + QCOMPARE(keyToSend, -1); + + if (0) { // dont run these tests since the dialog wont close! + keyToSend = Qt::Key_Escape; + sendKeySoon(); + ret = QMessageBox::information(0, "title", "text", "Yes", "No", QString(), 1); + QCOMPARE(ret, -1); + QCOMPARE(keyToSend, -1); + + keyToSend = Qt::Key_Escape; + sendKeySoon(); + ret = QMessageBox::information(0, "title", "text", "Yes", "No", QString(), 0, 1); + QCOMPARE(ret, 1); + QCOMPARE(keyToSend, -1); + } +} + +void tst_QMessageBox::instanceSourceCompat() +{ + QMessageBox mb("Application name here", + "Saving the file will overwrite the original file on the disk.\n" + "Do you really want to save?", + QMessageBox::Information, + QMessageBox::Yes | QMessageBox::Default, + QMessageBox::No, + QMessageBox::Cancel | QMessageBox::Escape); + mb.setButtonText(QMessageBox::Yes, "Save"); + mb.setButtonText(QMessageBox::No, "Discard"); + mb.addButton("&Revert", QMessageBox::RejectRole); + mb.addButton("&Zoo", QMessageBox::ActionRole); + + QCOMPARE(exec(&mb, Qt::Key_Enter), int(QMessageBox::Yes)); + QCOMPARE(exec(&mb, Qt::Key_Escape), int(QMessageBox::Cancel)); +#ifndef Q_WS_MAC + // mnemonics are not used on Mac OS X + QCOMPARE(exec(&mb, Qt::ALT + Qt::Key_R), 0); + QCOMPARE(exec(&mb, Qt::ALT + Qt::Key_Z), 1); +#endif +} + +void tst_QMessageBox::testSymbols() +{ + return; + + QMessageBox::Icon icon; + icon = QMessageBox::NoIcon; + icon = QMessageBox::Information; + icon = QMessageBox::Warning; + icon = QMessageBox::Critical; + icon = QMessageBox::Question; + + QMessageBox mb1; + QMessageBox mb2(0); + QMessageBox mb3(&mb1); + QMessageBox mb3b("title", "text", QMessageBox::Critical, int(QMessageBox::Yes), + int(QMessageBox::No), int(QMessageBox::Cancel), &mb1, Qt::Dialog); + + QMessageBox::Button button = QMessageBox::NoButton; + button = QMessageBox::Ok; + button = QMessageBox::Cancel; + button = QMessageBox::Yes; + button = QMessageBox::No; + button = QMessageBox::Abort; + button = QMessageBox::Retry; + button = QMessageBox::Ignore; + button = QMessageBox::YesAll; + button = QMessageBox::NoAll; + button = QMessageBox::ButtonMask; + button = QMessageBox::Default; + button = QMessageBox::Escape; + button = QMessageBox::FlagMask; + + const QString text = QStringLiteral("Foo"); + mb1.setText(text); + QCOMPARE(mb1.text(), text); + + icon = mb1.icon(); + QVERIFY(icon == QMessageBox::NoIcon); + mb1.setIcon(QMessageBox::Question); + QVERIFY(mb1.icon() == QMessageBox::Question); + + QPixmap iconPixmap = mb1.iconPixmap(); + mb1.setIconPixmap(iconPixmap); + QVERIFY(mb1.icon() == QMessageBox::NoIcon); + + QCOMPARE(mb1.buttonText(QMessageBox::Ok), QLatin1String("OK")); + QCOMPARE(mb1.buttonText(QMessageBox::Cancel), QString()); + QCOMPARE(mb1.buttonText(QMessageBox::Ok | QMessageBox::Default), QString()); + + const QString button1 = QStringLiteral("Bar"); + mb2.setButtonText(QMessageBox::Cancel, QStringLiteral("Foo")); + mb2.setButtonText(QMessageBox::Ok, button1); + mb2.setButtonText(QMessageBox::Ok | QMessageBox::Default, QStringLiteral("Baz")); + + QCOMPARE(mb2.buttonText(QMessageBox::Cancel), QString()); + QCOMPARE(mb2.buttonText(QMessageBox::Ok), button1); + + QVERIFY(mb3b.buttonText(QMessageBox::Yes).endsWith("Yes")); + QCOMPARE(mb3b.buttonText(QMessageBox::YesAll), QString()); + QCOMPARE(mb3b.buttonText(QMessageBox::Ok), QString()); + + const QString button2 = QStringLiteral("Blah"); + mb3b.setButtonText(QMessageBox::Yes, button2); + mb3b.setButtonText(QMessageBox::YesAll, QStringLiteral("Zoo")); + mb3b.setButtonText(QMessageBox::Ok, QStringLiteral("Zoo")); + + QCOMPARE(mb3b.buttonText(QMessageBox::Yes), button2); + QCOMPARE(mb3b.buttonText(QMessageBox::YesAll), QString()); + QCOMPARE(mb3b.buttonText(QMessageBox::Ok), QString()); + + QCOMPARE(mb1.textFormat(), Qt::AutoText); + mb1.setTextFormat(Qt::PlainText); + QCOMPARE(mb1.textFormat(), Qt::PlainText); + + CONVENIENCE_FUNC_SYMS(information); + CONVENIENCE_FUNC_SYMS_EXTRA(information); + CONVENIENCE_FUNC_SYMS(question); + CONVENIENCE_FUNC_SYMS_EXTRA(question); + CONVENIENCE_FUNC_SYMS(warning); + CONVENIENCE_FUNC_SYMS(critical); + + QSize sizeHint = mb1.sizeHint(); + QVERIFY(sizeHint.width() > 20 && sizeHint.height() > 20); + + QMessageBox::about(&mb1, "title", "text"); + QMessageBox::aboutQt(&mb1); + QMessageBox::aboutQt(&mb1, "title"); +} + +void tst_QMessageBox::detailsText() +{ + QMessageBox box; + QString text("This is the details text."); + box.setDetailedText(text); + QCOMPARE(box.detailedText(), text); +} + +void tst_QMessageBox::detailsButtonText() +{ + QMessageBox box; + box.setDetailedText("bla"); + box.open(); + QApplication::postEvent(&box, new QEvent(QEvent::LanguageChange)); + QApplication::processEvents(); + QDialogButtonBox* bb = box.findChild<QDialogButtonBox*>("qt_msgbox_buttonbox"); + QVERIFY(bb); //get the detail button + + QList<QAbstractButton *> list = bb->buttons(); + QAbstractButton* btn = NULL; + foreach(btn, list) { + if (btn && (btn->inherits("QPushButton"))) { + if (btn->text() != QMessageBox::tr("OK") && btn->text() != QMessageBox::tr("Show Details...")) { + QFAIL(qPrintable(QString("Unexpected messagebox button text: %1").arg(btn->text()))); + } + } + } +} + +void tst_QMessageBox::incorrectDefaultButton() +{ + keyToSend = Qt::Key_Escape; + sendKeySoon(); + //Do not crash here + QTest::ignoreMessage(QtWarningMsg, "QDialogButtonBox::createButton: Invalid ButtonRole, button not added"); + QMessageBox::question( 0, "", "I've been hit!",QMessageBox::Ok | QMessageBox::Cancel,QMessageBox::Save ); + QCOMPARE(keyToSend, -1); + + keyToSend = Qt::Key_Escape; + sendKeySoon(); + QTest::ignoreMessage(QtWarningMsg, "QDialogButtonBox::createButton: Invalid ButtonRole, button not added"); + QMessageBox::question( 0, "", "I've been hit!",QFlag(QMessageBox::Ok | QMessageBox::Cancel),QMessageBox::Save ); + QCOMPARE(keyToSend, -1); + + keyToSend = Qt::Key_Escape; + sendKeySoon(); + QTest::ignoreMessage(QtWarningMsg, "QDialogButtonBox::createButton: Invalid ButtonRole, button not added"); + QTest::ignoreMessage(QtWarningMsg, "QDialogButtonBox::createButton: Invalid ButtonRole, button not added"); + //do not crash here -> call old function of QMessageBox in this case + QMessageBox::question( 0, "", "I've been hit!",QMessageBox::Ok | QMessageBox::Cancel,QMessageBox::Save | QMessageBox::Cancel,QMessageBox::Ok); + QCOMPARE(keyToSend, -1); +} + +void tst_QMessageBox::updateSize() +{ + QMessageBox box; + box.setText("This is awesome"); + box.show(); + QSize oldSize = box.size(); + QString longText; + for (int i = 0; i < 20; i++) + longText += box.text(); + box.setText(longText); + QVERIFY(box.size() != oldSize); // should have grown + QVERIFY(box.width() > oldSize.width() || box.height() > oldSize.height()); + oldSize = box.size(); + box.setStandardButtons(QMessageBox::StandardButtons(0xFFFF)); + QVERIFY(box.size() != oldSize); // should have grown + QVERIFY(box.width() > oldSize.width() || box.height() > oldSize.height()); +} + +void tst_QMessageBox::setInformativeText() +{ + QMessageBox msgbox(QMessageBox::Warning, "", "", QMessageBox::Ok); + QString itext = "This is a very long message and it should make the dialog have enough width to fit this message in"; + msgbox.setInformativeText(itext); + msgbox.show(); + QCOMPARE(msgbox.informativeText(), itext); + QVERIFY2(msgbox.width() > 190, //verify it's big enough (task181688) + qPrintable(QString("%1 > 190").arg(msgbox.width()))); +} + +void tst_QMessageBox::iconPixmap() +{ + QMessageBox messageBox; + QCOMPARE(messageBox.iconPixmap(), QPixmap()); +} + +QTEST_MAIN(tst_QMessageBox) +#include "tst_qmessagebox.moc" diff --git a/tests/auto/widgets/dialogs/qprogressdialog/.gitignore b/tests/auto/widgets/dialogs/qprogressdialog/.gitignore new file mode 100644 index 0000000000..ce3cd0d7dd --- /dev/null +++ b/tests/auto/widgets/dialogs/qprogressdialog/.gitignore @@ -0,0 +1 @@ +tst_qprogressdialog diff --git a/tests/auto/widgets/dialogs/qprogressdialog/qprogressdialog.pro b/tests/auto/widgets/dialogs/qprogressdialog/qprogressdialog.pro new file mode 100644 index 0000000000..f3861e4cd3 --- /dev/null +++ b/tests/auto/widgets/dialogs/qprogressdialog/qprogressdialog.pro @@ -0,0 +1,9 @@ +############################################################ +# Project file for autotest for file qprogressdialog.h +############################################################ + +load(qttest_p4) +QT += widgets +SOURCES += tst_qprogressdialog.cpp + + diff --git a/tests/auto/widgets/dialogs/qprogressdialog/tst_qprogressdialog.cpp b/tests/auto/widgets/dialogs/qprogressdialog/tst_qprogressdialog.cpp new file mode 100644 index 0000000000..5fbd5d747a --- /dev/null +++ b/tests/auto/widgets/dialogs/qprogressdialog/tst_qprogressdialog.cpp @@ -0,0 +1,157 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtTest/QtTest> + +#include <qcoreapplication.h> +#include <qdebug.h> +#include <qprogressdialog.h> +#include <qlabel.h> + + +//TESTED_CLASS= +//TESTED_FILES= + +class tst_QProgressDialog : public QObject +{ +Q_OBJECT + +public: + tst_QProgressDialog(); + virtual ~tst_QProgressDialog(); + +private slots: + void getSetCheck(); + void task198202(); +}; + +tst_QProgressDialog::tst_QProgressDialog() +{ +} + +tst_QProgressDialog::~tst_QProgressDialog() +{ +} + +// Testing get/set functions +void tst_QProgressDialog::getSetCheck() +{ + QProgressDialog obj1; + // bool QProgressDialog::autoReset() + // void QProgressDialog::setAutoReset(bool) + obj1.setAutoReset(false); + QCOMPARE(false, obj1.autoReset()); + obj1.setAutoReset(true); + QCOMPARE(true, obj1.autoReset()); + + // bool QProgressDialog::autoClose() + // void QProgressDialog::setAutoClose(bool) + obj1.setAutoClose(false); + QCOMPARE(false, obj1.autoClose()); + obj1.setAutoClose(true); + QCOMPARE(true, obj1.autoClose()); + + // int QProgressDialog::maximum() + // void QProgressDialog::setMaximum(int) + obj1.setMaximum(0); + QCOMPARE(0, obj1.maximum()); + obj1.setMaximum(INT_MIN); + QCOMPARE(INT_MIN, obj1.maximum()); + obj1.setMaximum(INT_MAX); + QCOMPARE(INT_MAX, obj1.maximum()); + + // int QProgressDialog::minimum() + // void QProgressDialog::setMinimum(int) + obj1.setMinimum(0); + QCOMPARE(0, obj1.minimum()); + obj1.setMinimum(INT_MIN); + QCOMPARE(INT_MIN, obj1.minimum()); + obj1.setMinimum(INT_MAX); + QCOMPARE(INT_MAX, obj1.minimum()); + + // int QProgressDialog::value() + // void QProgressDialog::setValue(int) + obj1.setMaximum(INT_MAX); + obj1.setMinimum(INT_MIN); + obj1.setValue(0); + QCOMPARE(0, obj1.value()); + obj1.setValue(INT_MIN+1); + QCOMPARE(INT_MIN+1, obj1.value()); + obj1.setValue(INT_MIN); + QCOMPARE(INT_MIN, obj1.value()); + obj1.setValue(INT_MAX-1); + QCOMPARE(INT_MAX-1, obj1.value()); + + obj1.setValue(INT_MAX); + QCOMPARE(INT_MIN, obj1.value()); // We set autoReset, the thing is reset + + obj1.setAutoReset(false); + obj1.setValue(INT_MAX); + QCOMPARE(INT_MAX, obj1.value()); + obj1.setAutoReset(true); + + // int QProgressDialog::minimumDuration() + // void QProgressDialog::setMinimumDuration(int) + obj1.setMinimumDuration(0); + QCOMPARE(0, obj1.minimumDuration()); + obj1.setMinimumDuration(INT_MIN); + QCOMPARE(INT_MIN, obj1.minimumDuration()); + obj1.setMinimumDuration(INT_MAX); + QCOMPARE(INT_MAX, obj1.minimumDuration()); +} + +void tst_QProgressDialog::task198202() +{ + //should not crash + QProgressDialog dlg(QLatin1String("test"),QLatin1String("test"),1,10); + dlg.show(); + QTest::qWait(20); + int futureHeight = dlg.sizeHint().height() - qFindChild<QLabel*>(&dlg)->sizeHint().height(); + dlg.setLabel(0); + QTest::ignoreMessage(QtWarningMsg, "QProgressDialog::setBar: Cannot set a null progress bar"); + dlg.setBar(0); + QTest::qWait(20); + QCOMPARE(dlg.sizeHint().height(), futureHeight); +} + +QTEST_MAIN(tst_QProgressDialog) +#include "tst_qprogressdialog.moc" diff --git a/tests/auto/widgets/dialogs/qsidebar/.gitignore b/tests/auto/widgets/dialogs/qsidebar/.gitignore new file mode 100644 index 0000000000..194ca9f244 --- /dev/null +++ b/tests/auto/widgets/dialogs/qsidebar/.gitignore @@ -0,0 +1 @@ +tst_qsidebar diff --git a/tests/auto/widgets/dialogs/qsidebar/qsidebar.pro b/tests/auto/widgets/dialogs/qsidebar/qsidebar.pro new file mode 100644 index 0000000000..9f6e094784 --- /dev/null +++ b/tests/auto/widgets/dialogs/qsidebar/qsidebar.pro @@ -0,0 +1,6 @@ +CONFIG += qttest_p4 + +QT += core-private +QT += widgets widgets-private +SOURCES += tst_qsidebar.cpp +TARGET = tst_qsidebar diff --git a/tests/auto/widgets/dialogs/qsidebar/tst_qsidebar.cpp b/tests/auto/widgets/dialogs/qsidebar/tst_qsidebar.cpp new file mode 100644 index 0000000000..912719f76a --- /dev/null +++ b/tests/auto/widgets/dialogs/qsidebar/tst_qsidebar.cpp @@ -0,0 +1,231 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtTest/QtTest> +#include "../../../src/widgets/dialogs/qsidebar_p.h" +#include "../../../src/widgets/dialogs/qfilesystemmodel_p.h" + +//TESTED_CLASS= +//TESTED_FILES= + +class tst_QSidebar : public QObject { + Q_OBJECT + +public: + tst_QSidebar(); + virtual ~tst_QSidebar(); + +public Q_SLOTS: + void init(); + void cleanup(); + +private slots: + void setUrls(); + void selectUrls(); + void addUrls(); + + void goToUrl(); +}; + +tst_QSidebar::tst_QSidebar() +{ +} + +tst_QSidebar::~tst_QSidebar() +{ +} + +void tst_QSidebar::init() +{ +} + +void tst_QSidebar::cleanup() +{ +} + +void tst_QSidebar::setUrls() +{ + QList<QUrl> urls; + QFileSystemModel fsmodel; + QSidebar qsidebar; + qsidebar.init(&fsmodel, urls); + QAbstractItemModel *model = qsidebar.model(); + + urls << QUrl::fromLocalFile(QDir::rootPath()) + << QUrl::fromLocalFile(QDir::temp().absolutePath()); + + QCOMPARE(model->rowCount(), 0); + qsidebar.setUrls(urls); + QCOMPARE(qsidebar.urls(), urls); + QCOMPARE(model->rowCount(), urls.count()); + qsidebar.setUrls(urls); + QCOMPARE(model->rowCount(), urls.count()); +} + +void tst_QSidebar::selectUrls() +{ + QList<QUrl> urls; + urls << QUrl::fromLocalFile(QDir::rootPath()) + << QUrl::fromLocalFile(QDir::temp().absolutePath()); + QFileSystemModel fsmodel; + QSidebar qsidebar; + qsidebar.init(&fsmodel, urls); + + QSignalSpy spy(&qsidebar, SIGNAL(goToUrl(const QUrl &))); + qsidebar.selectUrl(urls.at(0)); + QCOMPARE(spy.count(), 0); +} + +void tst_QSidebar::addUrls() +{ + QList<QUrl> emptyUrls; + QFileSystemModel fsmodel; + QSidebar qsidebar; + qsidebar.init(&fsmodel, emptyUrls); + QAbstractItemModel *model = qsidebar.model(); + QDir testDir = QDir::home(); + + // default + QCOMPARE(model->rowCount(), 0); + + QList<QUrl> urls; + urls << QUrl::fromLocalFile(QDir::rootPath()) + << QUrl::fromLocalFile(QDir::temp().absolutePath()); + + // test < 0 + qsidebar.addUrls(urls, -1); + QCOMPARE(model->rowCount(), 2); + + // test = 0 + qsidebar.setUrls(emptyUrls); + qsidebar.addUrls(urls, 0); + QCOMPARE(model->rowCount(), 2); + + // test > 0 + qsidebar.setUrls(emptyUrls); + qsidebar.addUrls(urls, 100); + QCOMPARE(model->rowCount(), 2); + + // test inserting with already existing rows + QList<QUrl> moreUrls; + moreUrls << QUrl::fromLocalFile(testDir.absolutePath()); + qsidebar.addUrls(moreUrls, -1); + QCOMPARE(model->rowCount(), 3); + + // make sure invalid urls are still added + QList<QUrl> badUrls; + badUrls << QUrl::fromLocalFile(testDir.absolutePath() + "/I used to exist"); + qsidebar.addUrls(badUrls, 0); + QCOMPARE(model->rowCount(), 4); + + // check that every item has text and an icon including the above invalid one + for (int i = 0; i < model->rowCount(); ++i) { + QVERIFY(!model->index(i, 0).data().toString().isEmpty()); + QIcon icon = qvariant_cast<QIcon>(model->index(i, 0).data(Qt::DecorationRole)); + QVERIFY(!icon.isNull()); + } + + // test moving up the list + qsidebar.setUrls(emptyUrls); + qsidebar.addUrls(urls, 100); + qsidebar.addUrls(moreUrls, 100); + QCOMPARE(model->rowCount(), 3); + qsidebar.addUrls(moreUrls, 1); + QCOMPARE(qsidebar.urls()[1], moreUrls[0]); + + // test appending with -1 + qsidebar.setUrls(emptyUrls); + qsidebar.addUrls(urls, -1); + qsidebar.addUrls(moreUrls, -1); + QCOMPARE(qsidebar.urls()[0], urls[0]); + + QList<QUrl> doubleUrls; + //tow exact same paths, we have only one entry + doubleUrls << QUrl::fromLocalFile(testDir.absolutePath()); + doubleUrls << QUrl::fromLocalFile(testDir.absolutePath()); + qsidebar.setUrls(emptyUrls); + qsidebar.addUrls(doubleUrls, 1); + QCOMPARE(qsidebar.urls().size(), 1); + + // Two paths that are effectively pointing to the same location + doubleUrls << QUrl::fromLocalFile(testDir.absolutePath()); + doubleUrls << QUrl::fromLocalFile(testDir.absolutePath() + "/."); + qsidebar.setUrls(emptyUrls); + qsidebar.addUrls(doubleUrls, 1); + QCOMPARE(qsidebar.urls().size(), 1); + +#if defined(Q_OS_WIN) + //Windows is case insensitive so no duplicate entries in that case + doubleUrls << QUrl::fromLocalFile(testDir.absolutePath()); + doubleUrls << QUrl::fromLocalFile(testDir.absolutePath().toUpper()); + qsidebar.setUrls(emptyUrls); + qsidebar.addUrls(doubleUrls, 1); + QCOMPARE(qsidebar.urls().size(), 1); +#else + //Two different paths we should have two entries + doubleUrls << QUrl::fromLocalFile(testDir.absolutePath()); + doubleUrls << QUrl::fromLocalFile(testDir.absolutePath().toUpper()); + qsidebar.setUrls(emptyUrls); + qsidebar.addUrls(doubleUrls, 1); + QCOMPARE(qsidebar.urls().size(), 2); +#endif +} + +void tst_QSidebar::goToUrl() +{ + QList<QUrl> urls; + urls << QUrl::fromLocalFile(QDir::rootPath()) + << QUrl::fromLocalFile(QDir::temp().absolutePath()); + QFileSystemModel fsmodel; + QSidebar qsidebar; + qsidebar.init(&fsmodel, urls); + qsidebar.show(); + + QSignalSpy spy(&qsidebar, SIGNAL(goToUrl(const QUrl &))); + QTest::mousePress(qsidebar.viewport(), Qt::LeftButton, 0, qsidebar.visualRect(qsidebar.model()->index(0, 0)).center()); + QCOMPARE(spy.count(), 1); + QCOMPARE((spy.value(0)).at(0).toUrl(), urls.first()); +} + +QTEST_MAIN(tst_QSidebar) +#include "tst_qsidebar.moc" + diff --git a/tests/auto/widgets/dialogs/qwizard/.gitignore b/tests/auto/widgets/dialogs/qwizard/.gitignore new file mode 100644 index 0000000000..9494c96da2 --- /dev/null +++ b/tests/auto/widgets/dialogs/qwizard/.gitignore @@ -0,0 +1 @@ +tst_qwizard diff --git a/tests/auto/widgets/dialogs/qwizard/images/background.png b/tests/auto/widgets/dialogs/qwizard/images/background.png Binary files differnew file mode 100644 index 0000000000..db7d67dc3d --- /dev/null +++ b/tests/auto/widgets/dialogs/qwizard/images/background.png diff --git a/tests/auto/widgets/dialogs/qwizard/images/banner.png b/tests/auto/widgets/dialogs/qwizard/images/banner.png Binary files differnew file mode 100644 index 0000000000..be36202d99 --- /dev/null +++ b/tests/auto/widgets/dialogs/qwizard/images/banner.png diff --git a/tests/auto/widgets/dialogs/qwizard/images/logo.png b/tests/auto/widgets/dialogs/qwizard/images/logo.png Binary files differnew file mode 100644 index 0000000000..9cf3350c4b --- /dev/null +++ b/tests/auto/widgets/dialogs/qwizard/images/logo.png diff --git a/tests/auto/widgets/dialogs/qwizard/images/watermark.png b/tests/auto/widgets/dialogs/qwizard/images/watermark.png Binary files differnew file mode 100644 index 0000000000..9305675549 --- /dev/null +++ b/tests/auto/widgets/dialogs/qwizard/images/watermark.png diff --git a/tests/auto/widgets/dialogs/qwizard/qwizard.pro b/tests/auto/widgets/dialogs/qwizard/qwizard.pro new file mode 100644 index 0000000000..c27801b82f --- /dev/null +++ b/tests/auto/widgets/dialogs/qwizard/qwizard.pro @@ -0,0 +1,4 @@ +load(qttest_p4) +QT += widgets +SOURCES += tst_qwizard.cpp +RESOURCES = qwizard.qrc diff --git a/tests/auto/widgets/dialogs/qwizard/qwizard.qrc b/tests/auto/widgets/dialogs/qwizard/qwizard.qrc new file mode 100644 index 0000000000..471da9def6 --- /dev/null +++ b/tests/auto/widgets/dialogs/qwizard/qwizard.qrc @@ -0,0 +1,8 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource> + <file>images/background.png</file> + <file>images/banner.png</file> + <file>images/logo.png</file> + <file>images/watermark.png</file> +</qresource> +</RCC> diff --git a/tests/auto/widgets/dialogs/qwizard/tst_qwizard.cpp b/tests/auto/widgets/dialogs/qwizard/tst_qwizard.cpp new file mode 100644 index 0000000000..2e05eb5bab --- /dev/null +++ b/tests/auto/widgets/dialogs/qwizard/tst_qwizard.cpp @@ -0,0 +1,2645 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QFont> +#include <QtTest/QtTest> +#include <QCheckBox> +#include <QLabel> +#include <QLineEdit> +#include <QList> +#include <QPushButton> +#include <QToolButton> +#include <QVBoxLayout> +#include <QWizard> +#include <QStyle> +#include <QPlastiqueStyle> +#include <QTreeWidget> + +//TESTED_CLASS= +//TESTED_FILES= + +static QImage grabWidget(QWidget *window) +{ + return QPixmap::grabWidget(window).toImage(); +} + +class tst_QWizard : public QObject +{ + Q_OBJECT + +public: + tst_QWizard(); + +public slots: + void init(); + void cleanup(); + +private slots: + void buttonText(); + void setButtonLayout(); + void setButton(); + void setTitleFormatEtc(); + void setPixmap(); + void setDefaultProperty(); + void addPage(); + void setPage(); + void setStartId(); + void setOption_IndependentPages(); + void setOption_IgnoreSubTitles(); + void setOption_ExtendedWatermarkPixmap(); + void setOption_NoDefaultButton(); + void setOption_NoBackButtonOnStartPage(); + void setOption_NoBackButtonOnLastPage(); + void setOption_DisabledBackButtonOnLastPage(); + void setOption_HaveNextButtonOnLastPage(); + void setOption_HaveFinishButtonOnEarlyPages(); + void setOption_NoCancelButton(); + void setOption_CancelButtonOnLeft(); + void setOption_HaveHelpButton(); + void setOption_HelpButtonOnRight(); + void setOption_HaveCustomButtonX(); +#ifndef Q_OS_WINCE + void combinations_data(); + void combinations(); +#endif + void showCurrentPageOnly(); + void setButtonText(); + void setCommitPage(); + void setWizardStyle(); + void removePage(); + void sideWidget(); + + // task-specific tests below me: + void task161660_buttonSpacing(); + void task177716_disableCommitButton(); + void task183550_stretchFactor(); + void task161658_alignments(); + void task177022_setFixedSize(); + void task248107_backButton(); + void task255350_fieldObjectDestroyed(); + + /* + Things that could be added: + + 1. Test virtual functions that are called, signals that are + emitted, etc. + + 2. Test QWizardPage more thorougly. + + 3. Test the look and field a bit more (especially the + different wizard styles, and how they interact with + pixmaps, titles, subtitles, etc.). + + 4. Test minimum sizes, sizes, maximum sizes, resizing, etc. + + 5. Try setting various options and wizard styles in various + orders and check that the results are the same every time, + no matter the order in which the properties were set. + + -> Initial version done (tst_QWizard::combinations()) + + 6. Test done() and restart(). + + 7. Test default properties of built-in widgets. + + 8. Test mutual exclusiveness of Next and Commit buttons. + */ +}; + +tst_QWizard::tst_QWizard() +{ +} + +void tst_QWizard::init() +{ +#ifdef Q_OS_WINCE //disable magic for WindowsCE + qApp->setAutoMaximizeThreshold(-1); +#endif +} + +void tst_QWizard::cleanup() +{ +} + +void tst_QWizard::buttonText() +{ + QWizard wizard; + wizard.setWizardStyle(QWizard::ClassicStyle); + + // Check the buttons' original text in Classic and Modern styles. + for (int pass = 0; pass < 2; ++pass) { + QCOMPARE(wizard.buttonText(QWizard::BackButton), QString("< &Back")); + QVERIFY(wizard.buttonText(QWizard::NextButton).contains("Next")); + QVERIFY(wizard.buttonText(QWizard::FinishButton).endsWith("Finish")); + QVERIFY(wizard.buttonText(QWizard::CancelButton).endsWith("Cancel")); + QVERIFY(wizard.buttonText(QWizard::HelpButton).endsWith("Help")); + + QVERIFY(wizard.buttonText(QWizard::CustomButton1).isEmpty()); + QVERIFY(wizard.buttonText(QWizard::CustomButton2).isEmpty()); + QVERIFY(wizard.buttonText(QWizard::CustomButton3).isEmpty()); + + // robustness + QVERIFY(wizard.buttonText(QWizard::Stretch).isEmpty()); + QVERIFY(wizard.buttonText(QWizard::NoButton).isEmpty()); + QVERIFY(wizard.buttonText(QWizard::NStandardButtons).isEmpty()); + QVERIFY(wizard.buttonText(QWizard::NButtons).isEmpty()); + + wizard.setWizardStyle(QWizard::ModernStyle); + } + + // Check the buttons' original text in Mac style. + wizard.setWizardStyle(QWizard::MacStyle); + + QCOMPARE(wizard.buttonText(QWizard::BackButton), QString("Go Back")); + QCOMPARE(wizard.buttonText(QWizard::NextButton), QString("Continue")); + QCOMPARE(wizard.buttonText(QWizard::FinishButton), QString("Done")); + QCOMPARE(wizard.buttonText(QWizard::CancelButton), QString("Cancel")); + QCOMPARE(wizard.buttonText(QWizard::HelpButton), QString("Help")); + + QVERIFY(wizard.buttonText(QWizard::CustomButton1).isEmpty()); + QVERIFY(wizard.buttonText(QWizard::CustomButton2).isEmpty()); + QVERIFY(wizard.buttonText(QWizard::CustomButton3).isEmpty()); + + // robustness + QVERIFY(wizard.buttonText(QWizard::Stretch).isEmpty()); + QVERIFY(wizard.buttonText(QWizard::NoButton).isEmpty()); + QVERIFY(wizard.buttonText(QWizard::NStandardButtons).isEmpty()); + QVERIFY(wizard.buttonText(QWizard::NButtons).isEmpty()); + + // Modify the buttons' text and see what happens. + wizard.setButtonText(QWizard::NextButton, "N&este"); + wizard.setButtonText(QWizard::CustomButton2, "&Cucu"); + wizard.setButtonText(QWizard::Stretch, "Stretch"); + + QCOMPARE(wizard.buttonText(QWizard::BackButton), QString("Go Back")); + QCOMPARE(wizard.buttonText(QWizard::NextButton), QString("N&este")); + QCOMPARE(wizard.buttonText(QWizard::FinishButton), QString("Done")); + QCOMPARE(wizard.buttonText(QWizard::CancelButton), QString("Cancel")); + QCOMPARE(wizard.buttonText(QWizard::HelpButton), QString("Help")); + + QVERIFY(wizard.buttonText(QWizard::CustomButton1).isEmpty()); + QCOMPARE(wizard.buttonText(QWizard::CustomButton2), QString("&Cucu")); + QVERIFY(wizard.buttonText(QWizard::CustomButton3).isEmpty()); + + // robustness + QVERIFY(wizard.buttonText(QWizard::Stretch).isEmpty()); + QVERIFY(wizard.buttonText(QWizard::NoButton).isEmpty()); + QVERIFY(wizard.buttonText(QWizard::NStandardButtons).isEmpty()); + QVERIFY(wizard.buttonText(QWizard::NButtons).isEmpty()); + + // Switch back to Classic style and see what happens. + wizard.setWizardStyle(QWizard::ClassicStyle); + + for (int pass = 0; pass < 2; ++pass) { + QCOMPARE(wizard.buttonText(QWizard::BackButton), QString("< &Back")); + QCOMPARE(wizard.buttonText(QWizard::NextButton), QString("N&este")); + QVERIFY(wizard.buttonText(QWizard::FinishButton).endsWith("Finish")); + QVERIFY(wizard.buttonText(QWizard::CancelButton).endsWith("Cancel")); + QVERIFY(wizard.buttonText(QWizard::HelpButton).endsWith("Help")); + + QVERIFY(wizard.buttonText(QWizard::CustomButton1).isEmpty()); + QCOMPARE(wizard.buttonText(QWizard::CustomButton2), QString("&Cucu")); + QVERIFY(wizard.buttonText(QWizard::CustomButton3).isEmpty()); + + // robustness + QVERIFY(wizard.buttonText(QWizard::Stretch).isEmpty()); + QVERIFY(wizard.buttonText(QWizard::NoButton).isEmpty()); + QVERIFY(wizard.buttonText(QWizard::NStandardButtons).isEmpty()); + QVERIFY(wizard.buttonText(QWizard::NButtons).isEmpty()); + + wizard.setOptions(QWizard::NoDefaultButton + | QWizard::NoBackButtonOnStartPage + | QWizard::NoBackButtonOnLastPage + | QWizard::DisabledBackButtonOnLastPage + | QWizard::NoCancelButton + | QWizard::CancelButtonOnLeft + | QWizard::HaveHelpButton + | QWizard::HelpButtonOnRight + | QWizard::HaveCustomButton1 + | QWizard::HaveCustomButton2 + | QWizard::HaveCustomButton3); + } +} + +void tst_QWizard::setButtonLayout() +{ + QList<QWizard::WizardButton> layout; + + QWizard wizard; + wizard.setWizardStyle(QWizard::ClassicStyle); + wizard.setOptions(0); + wizard.setButtonLayout(layout); + wizard.show(); + qApp->processEvents(); + + // if these crash, this means there's a bug in QWizard + QVERIFY(wizard.button(QWizard::NextButton)->text().contains("Next")); + QVERIFY(wizard.button(QWizard::BackButton)->text().contains("Back")); + QVERIFY(wizard.button(QWizard::FinishButton)->text().contains("Finish")); + QVERIFY(wizard.button(QWizard::CancelButton)->text().contains("Cancel")); + QVERIFY(wizard.button(QWizard::HelpButton)->text().contains("Help")); + QVERIFY(wizard.button(QWizard::CustomButton1)->text().isEmpty()); + QVERIFY(wizard.button(QWizard::CustomButton2)->text().isEmpty()); + QVERIFY(wizard.button(QWizard::CustomButton3)->text().isEmpty()); + QVERIFY(!wizard.button(QWizard::Stretch)); + QVERIFY(!wizard.button(QWizard::NoButton)); + + QVERIFY(!wizard.button(QWizard::NextButton)->isVisible()); + QVERIFY(!wizard.button(QWizard::BackButton)->isVisible()); + QVERIFY(!wizard.button(QWizard::HelpButton)->isVisible()); + + layout << QWizard::NextButton << QWizard::HelpButton; + wizard.setButtonLayout(layout); + qApp->processEvents(); + + QVERIFY(!wizard.button(QWizard::NextButton)->isVisible()); + QVERIFY(!wizard.button(QWizard::BackButton)->isVisible()); + QVERIFY(wizard.button(QWizard::HelpButton)->isVisible()); + + wizard.addPage(new QWizardPage); + wizard.addPage(new QWizardPage); + qApp->processEvents(); + + QVERIFY(!wizard.button(QWizard::NextButton)->isVisible()); + QVERIFY(!wizard.button(QWizard::BackButton)->isVisible()); + QVERIFY(wizard.button(QWizard::HelpButton)->isVisible()); + + wizard.restart(); + qApp->processEvents(); + + QVERIFY(wizard.button(QWizard::NextButton)->isVisible()); + QVERIFY(!wizard.button(QWizard::BackButton)->isVisible()); + QVERIFY(wizard.button(QWizard::HelpButton)->isVisible()); + + layout.clear(); + layout << QWizard::NextButton << QWizard::HelpButton << QWizard::BackButton + << QWizard::FinishButton << QWizard::CancelButton << QWizard::Stretch + << QWizard::CustomButton2; + + // Turn on all the button-related wizard options. Some of these + // should have no impact on a custom layout; others should. + wizard.setButtonLayout(layout); + wizard.setOptions(QWizard::NoDefaultButton + | QWizard::NoBackButtonOnStartPage + | QWizard::NoBackButtonOnLastPage + | QWizard::DisabledBackButtonOnLastPage + | QWizard::HaveNextButtonOnLastPage + | QWizard::HaveFinishButtonOnEarlyPages + | QWizard::NoCancelButton + | QWizard::CancelButtonOnLeft + | QWizard::HaveHelpButton + | QWizard::HelpButtonOnRight + | QWizard::HaveCustomButton1 + | QWizard::HaveCustomButton2 + | QWizard::HaveCustomButton3); + qApp->processEvents(); + + // we're on first page + QVERIFY(!wizard.button(QWizard::BackButton)->isVisible()); + QVERIFY(wizard.button(QWizard::FinishButton)->isVisible()); + QVERIFY(wizard.button(QWizard::CancelButton)->isVisible()); // NoCancelButton overridden + QVERIFY(wizard.button(QWizard::HelpButton)->isVisible()); + QVERIFY(!wizard.button(QWizard::CustomButton1)->isVisible()); + QVERIFY(wizard.button(QWizard::CustomButton2)->isVisible()); // HaveCustomButton2 overridden + QVERIFY(!wizard.button(QWizard::CustomButton3)->isVisible()); + + wizard.next(); + qApp->processEvents(); + + // we're on last page + QVERIFY(!wizard.button(QWizard::BackButton)->isVisible()); + QVERIFY(wizard.button(QWizard::NextButton)->isVisible()); + QVERIFY(!wizard.button(QWizard::NextButton)->isEnabled()); + QVERIFY(wizard.button(QWizard::FinishButton)->isVisible()); + QVERIFY(wizard.button(QWizard::FinishButton)->isEnabled()); + QVERIFY(wizard.button(QWizard::CancelButton)->isVisible()); // NoCancelButton overridden + QVERIFY(wizard.button(QWizard::HelpButton)->isVisible()); + QVERIFY(!wizard.button(QWizard::CustomButton1)->isVisible()); + QVERIFY(wizard.button(QWizard::CustomButton2)->isVisible()); // HaveCustomButton2 overridden + QVERIFY(!wizard.button(QWizard::CustomButton3)->isVisible()); + + // Check that the buttons are in the right order on screen. + for (int pass = 0; pass < 2; ++pass) { + wizard.setLayoutDirection(pass == 0 ? Qt::LeftToRight : Qt::RightToLeft); + qApp->processEvents(); + + int sign = (pass == 0) ? +1 : -1; + + int p[5]; + p[0] = sign * wizard.button(QWizard::NextButton)->x(); + p[1] = sign * wizard.button(QWizard::HelpButton)->x(); + p[2] = sign * wizard.button(QWizard::FinishButton)->x(); + p[3] = sign * wizard.button(QWizard::CancelButton)->x(); + p[4] = sign * wizard.button(QWizard::CustomButton2)->x(); + + QVERIFY(p[0] < p[1]); + QVERIFY(p[1] < p[2]); + QVERIFY(p[2] < p[3]); + QVERIFY(p[3] < p[4]); + } + + layout.clear(); + wizard.setButtonLayout(layout); + qApp->processEvents(); + + for (int i = -1; i < 50; ++i) { + QAbstractButton *button = wizard.button(QWizard::WizardButton(i)); + QVERIFY(!button || !button->isVisible()); + } +} + +void tst_QWizard::setButton() +{ + QPointer<QToolButton> toolButton = new QToolButton; + + QWizard wizard; + wizard.setWizardStyle(QWizard::ClassicStyle); + wizard.setButton(QWizard::NextButton, toolButton); + wizard.setButton(QWizard::CustomButton2, new QCheckBox("Kustom 2")); + + QVERIFY(qobject_cast<QToolButton *>(wizard.button(QWizard::NextButton))); + QVERIFY(qobject_cast<QCheckBox *>(wizard.button(QWizard::CustomButton2))); + QVERIFY(qobject_cast<QPushButton *>(wizard.button(QWizard::CustomButton1))); + + QVERIFY(toolButton != 0); + + // resetting the same button does nothing + wizard.setButton(QWizard::NextButton, toolButton); + QVERIFY(toolButton != 0); + + // revert to default button + wizard.setButton(QWizard::NextButton, 0); + QVERIFY(toolButton == 0); + QVERIFY(qobject_cast<QPushButton *>(wizard.button(QWizard::NextButton))); + QVERIFY(wizard.button(QWizard::NextButton)->text().contains("Next")); +} + +void tst_QWizard::setTitleFormatEtc() +{ + QWizard wizard; + QVERIFY(wizard.titleFormat() == Qt::AutoText); + QVERIFY(wizard.subTitleFormat() == Qt::AutoText); + + wizard.setTitleFormat(Qt::RichText); + QVERIFY(wizard.titleFormat() == Qt::RichText); + QVERIFY(wizard.subTitleFormat() == Qt::AutoText); + + wizard.setSubTitleFormat(Qt::PlainText); + QVERIFY(wizard.titleFormat() == Qt::RichText); + QVERIFY(wizard.subTitleFormat() == Qt::PlainText); +} + +void tst_QWizard::setPixmap() +{ + QPixmap p1(1, 1); + QPixmap p2(2, 2); + QPixmap p3(3, 3); + QPixmap p4(4, 4); + QPixmap p5(5, 5); + + QWizard wizard; + QWizardPage *page = new QWizardPage; + QWizardPage *page2 = new QWizardPage; + + wizard.addPage(page); + wizard.addPage(page2); + + QVERIFY(wizard.pixmap(QWizard::BannerPixmap).isNull()); + QVERIFY(wizard.pixmap(QWizard::LogoPixmap).isNull()); + QVERIFY(wizard.pixmap(QWizard::WatermarkPixmap).isNull()); +#ifdef Q_WS_MAC + if (QSysInfo::MacintoshVersion > QSysInfo::MV_10_3) + QVERIFY(wizard.pixmap(QWizard::BackgroundPixmap).isNull() == false); + else // fall through since the image doesn't exist on a 10.3 system. + QVERIFY(page->pixmap(QWizard::BackgroundPixmap).isNull()); +#else + QVERIFY(wizard.pixmap(QWizard::BackgroundPixmap).isNull()); +#endif + + QVERIFY(page->pixmap(QWizard::BannerPixmap).isNull()); + QVERIFY(page->pixmap(QWizard::LogoPixmap).isNull()); + QVERIFY(page->pixmap(QWizard::WatermarkPixmap).isNull()); +#ifdef Q_WS_MAC + if (QSysInfo::MacintoshVersion > QSysInfo::MV_10_3) + QVERIFY(wizard.pixmap(QWizard::BackgroundPixmap).isNull() == false); + else // fall through since the image doesn't exist on a 10.3 system. + QVERIFY(page->pixmap(QWizard::BackgroundPixmap).isNull()); +#else + QVERIFY(page->pixmap(QWizard::BackgroundPixmap).isNull()); +#endif + wizard.setPixmap(QWizard::BannerPixmap, p1); + wizard.setPixmap(QWizard::LogoPixmap, p2); + wizard.setPixmap(QWizard::WatermarkPixmap, p3); + wizard.setPixmap(QWizard::BackgroundPixmap, p4); + + page->setPixmap(QWizard::LogoPixmap, p5); + + QVERIFY(wizard.pixmap(QWizard::BannerPixmap).size() == p1.size()); + QVERIFY(wizard.pixmap(QWizard::LogoPixmap).size() == p2.size()); + QVERIFY(wizard.pixmap(QWizard::WatermarkPixmap).size() == p3.size()); + QVERIFY(wizard.pixmap(QWizard::BackgroundPixmap).size() == p4.size()); + + QVERIFY(page->pixmap(QWizard::BannerPixmap).size() == p1.size()); + QVERIFY(page->pixmap(QWizard::LogoPixmap).size() == p5.size()); + QVERIFY(page->pixmap(QWizard::WatermarkPixmap).size() == p3.size()); + QVERIFY(page->pixmap(QWizard::BackgroundPixmap).size() == p4.size()); + + QVERIFY(page2->pixmap(QWizard::BannerPixmap).size() == p1.size()); + QVERIFY(page2->pixmap(QWizard::LogoPixmap).size() == p2.size()); + QVERIFY(page2->pixmap(QWizard::WatermarkPixmap).size() == p3.size()); + QVERIFY(page2->pixmap(QWizard::BackgroundPixmap).size() == p4.size()); +} + +class MyPage1 : public QWizardPage +{ +public: + MyPage1() { + edit1 = new QLineEdit("Bla 1", this); + + edit2 = new QLineEdit("Bla 2", this); + edit2->setInputMask("Mask"); + + edit3 = new QLineEdit("Bla 3", this); + edit3->setMaxLength(25); + + edit4 = new QLineEdit("Bla 4", this); + } + + void registerField(const QString &name, QWidget *widget, + const char *property = 0, + const char *changedSignal = 0) + { QWizardPage::registerField(name, widget, property, changedSignal); } + + QLineEdit *edit1; + QLineEdit *edit2; + QLineEdit *edit3; + QLineEdit *edit4; +}; + +void tst_QWizard::setDefaultProperty() +{ + QWizard wizard; + MyPage1 *page = new MyPage1; + wizard.addPage(page); + + page->registerField("edit1", page->edit1); + + wizard.setDefaultProperty("QLineEdit", "inputMask", 0); + page->registerField("edit2", page->edit2); + + wizard.setDefaultProperty("QLineEdit", "maxLength", 0); + page->registerField("edit3", page->edit3); + + wizard.setDefaultProperty("QLineEdit", "text", SIGNAL(textChanged(QString))); + page->registerField("edit3bis", page->edit3); + + wizard.setDefaultProperty("QWidget", "enabled", 0); // less specific, i.e. ignored + page->registerField("edit4", page->edit4); + QTest::ignoreMessage(QtWarningMsg,"QWizard::setField: Couldn't write to property 'customProperty'"); + wizard.setDefaultProperty("QLineEdit", "customProperty", 0); + page->registerField("edit4bis", page->edit4); + + QCOMPARE(wizard.field("edit1").toString(), QString("Bla 1")); + QCOMPARE(wizard.field("edit2").toString(), page->edit2->inputMask()); + QCOMPARE(wizard.field("edit3").toInt(), 25); + QCOMPARE(wizard.field("edit3bis").toString(), QString("Bla 3")); + QCOMPARE(wizard.field("edit4").toString(), QString("Bla 4")); + QCOMPARE(wizard.field("edit4bis").toString(), QString()); + + wizard.setField("edit1", "Alpha"); + wizard.setField("edit2", "Beta"); + wizard.setField("edit3", 50); + wizard.setField("edit3bis", "Gamma"); + wizard.setField("edit4", "Delta"); + wizard.setField("edit4bis", "Epsilon"); + + QCOMPARE(wizard.field("edit1").toString(), QString("Alpha")); + QVERIFY(wizard.field("edit2").toString().contains("Beta")); + QCOMPARE(wizard.field("edit3").toInt(), 50); + QCOMPARE(wizard.field("edit3bis").toString(), QString("Gamma")); + QCOMPARE(wizard.field("edit4").toString(), QString("Delta")); + QCOMPARE(wizard.field("edit4bis").toString(), QString("Epsilon")); + + // make sure the data structure is reasonable + for (int i = 0; i < 200000; ++i) { + wizard.setDefaultProperty("QLineEdit", "x" + QByteArray::number(i), 0); + wizard.setDefaultProperty("QLabel", "y" + QByteArray::number(i), 0); + } +} + +void tst_QWizard::addPage() +{ + QWidget *parent = new QWidget; + QWizard wizard; + const int N = 100; + QWizardPage *pages[N]; + QSignalSpy spy(&wizard, SIGNAL(pageAdded(int))); + + for (int i = 0; i < N; ++i) { + pages[i] = new QWizardPage(parent); + QCOMPARE(wizard.addPage(pages[i]), i); + QCOMPARE(pages[i]->window(), (QWidget *)&wizard); + QCOMPARE(wizard.startId(), 0); + QCOMPARE(spy.count(), 1); + QList<QVariant> arguments = spy.takeFirst(); + QCOMPARE(arguments.at(0).toInt(), i); + } + + for (int i = 0; i < N; ++i) { + QVERIFY(pages[i] == wizard.page(i)); + } + QVERIFY(!wizard.page(-1)); + QVERIFY(!wizard.page(N)); + QVERIFY(!wizard.page(N + 1)); + + wizard.setPage(N + 50, new QWizardPage); + QCOMPARE(spy.count(), 1); + QList<QVariant> arguments = spy.takeFirst(); + QCOMPARE(arguments.at(0).toInt(), N + 50); + wizard.setPage(-3000, new QWizardPage); + QCOMPARE(spy.count(), 1); + arguments = spy.takeFirst(); + QCOMPARE(arguments.at(0).toInt(), -3000); + + QWizardPage *pageX = new QWizardPage; + QCOMPARE(wizard.addPage(pageX), N + 51); + QCOMPARE(wizard.page(N + 51), pageX); + QCOMPARE(spy.count(), 1); + arguments = spy.takeFirst(); + QCOMPARE(arguments.at(0).toInt(), N + 51); + + QCOMPARE(wizard.addPage(new QWizardPage), N + 52); + QCOMPARE(spy.count(), 1); + arguments = spy.takeFirst(); + QCOMPARE(arguments.at(0).toInt(), N + 52); + + QTest::ignoreMessage(QtWarningMsg,"QWizard::setPage: Cannot insert null page"); + wizard.addPage(0); // generates a warning + QCOMPARE(spy.count(), 0); + delete parent; +} + +#define CHECK_VISITED(wizard, list) \ + do { \ + QList<int> myList = list; \ + QCOMPARE((wizard).visitedPages(), myList); \ + Q_FOREACH(int id, myList) \ + QVERIFY((wizard).hasVisitedPage(id)); \ + } while (0) + +void tst_QWizard::setPage() +{ + QWidget *parent = new QWidget; + QWizard wizard; + QWizardPage *page; + QSignalSpy spy(&wizard, SIGNAL(pageAdded(int))); + + QCOMPARE(wizard.startId(), -1); + QCOMPARE(wizard.currentId(), -1); + QVERIFY(!wizard.currentPage()); + QCOMPARE(wizard.nextId(), -1); + + page = new QWizardPage(parent); + QTest::ignoreMessage(QtWarningMsg,"QWizard::setPage: Cannot insert page with ID -1"); + wizard.setPage(-1, page); // gives a warning and does nothing + QCOMPARE(spy.count(), 0); + QVERIFY(!wizard.page(-2)); + QVERIFY(!wizard.page(-1)); + QVERIFY(!wizard.page(0)); + QCOMPARE(wizard.startId(), -1); + QCOMPARE(wizard.currentId(), -1); + QVERIFY(!wizard.currentPage()); + QCOMPARE(wizard.nextId(), -1); + CHECK_VISITED(wizard, QList<int>()); + + page = new QWizardPage(parent); + wizard.setPage(0, page); + QCOMPARE(spy.count(), 1); + QList<QVariant> arguments = spy.takeFirst(); + QCOMPARE(arguments.at(0).toInt(), 0); + QCOMPARE(page->window(), (QWidget *)&wizard); + QCOMPARE(wizard.page(0), page); + QCOMPARE(wizard.startId(), 0); + QCOMPARE(wizard.currentId(), -1); + QVERIFY(!wizard.currentPage()); + QCOMPARE(wizard.nextId(), -1); + CHECK_VISITED(wizard, QList<int>()); + + page = new QWizardPage(parent); + wizard.setPage(-2, page); + QCOMPARE(spy.count(), 1); + arguments = spy.takeFirst(); + QCOMPARE(arguments.at(0).toInt(), -2); + QCOMPARE(page->window(), (QWidget *)&wizard); + QCOMPARE(wizard.page(-2), page); + QCOMPARE(wizard.startId(), -2); + QCOMPARE(wizard.currentId(), -1); + QVERIFY(!wizard.currentPage()); + QCOMPARE(wizard.nextId(), -1); + CHECK_VISITED(wizard, QList<int>()); + + wizard.restart(); + QCOMPARE(wizard.page(-2), page); + QCOMPARE(wizard.startId(), -2); + QCOMPARE(wizard.currentId(), -2); + QVERIFY(wizard.currentPage() == page); + QCOMPARE(wizard.nextId(), 0); + CHECK_VISITED(wizard, QList<int>() << -2); + + page = new QWizardPage(parent); + wizard.setPage(2, page); + QCOMPARE(spy.count(), 1); + arguments = spy.takeFirst(); + QCOMPARE(arguments.at(0).toInt(), 2); + QCOMPARE(wizard.page(2), page); + QCOMPARE(wizard.startId(), -2); + QCOMPARE(wizard.currentId(), -2); + QVERIFY(wizard.currentPage() == wizard.page(-2)); + QCOMPARE(wizard.nextId(), 0); + CHECK_VISITED(wizard, QList<int>() << -2); + + wizard.restart(); + QCOMPARE(wizard.startId(), -2); + QCOMPARE(wizard.currentId(), -2); + QVERIFY(wizard.currentPage() == wizard.page(-2)); + QCOMPARE(wizard.nextId(), 0); + CHECK_VISITED(wizard, QList<int>() << -2); + + page = new QWizardPage(parent); + wizard.setPage(-3, page); + QCOMPARE(spy.count(), 1); + arguments = spy.takeFirst(); + QCOMPARE(arguments.at(0).toInt(), -3); + QCOMPARE(wizard.page(-3), page); + QCOMPARE(wizard.startId(), -3); + QCOMPARE(wizard.currentId(), -2); + QVERIFY(wizard.currentPage() == wizard.page(-2)); + QCOMPARE(wizard.nextId(), 0); + CHECK_VISITED(wizard, QList<int>() << -2); + + wizard.restart(); + QCOMPARE(wizard.startId(), -3); + QCOMPARE(wizard.currentId(), -3); + QVERIFY(wizard.currentPage() == wizard.page(-3)); + QCOMPARE(wizard.nextId(), -2); + CHECK_VISITED(wizard, QList<int>() << -3); + + wizard.next(); + QCOMPARE(wizard.startId(), -3); + QCOMPARE(wizard.currentId(), -2); + QVERIFY(wizard.currentPage() == wizard.page(-2)); + QCOMPARE(wizard.nextId(), 0); + CHECK_VISITED(wizard, QList<int>() << -3 << -2); + + wizard.next(); + QCOMPARE(wizard.startId(), -3); + QCOMPARE(wizard.currentId(), 0); + QVERIFY(wizard.currentPage() == wizard.page(0)); + QCOMPARE(wizard.nextId(), 2); + CHECK_VISITED(wizard, QList<int>() << -3 << -2 << 0); + + for (int i = 0; i < 100; ++i) { + wizard.next(); + QCOMPARE(wizard.startId(), -3); + QCOMPARE(wizard.currentId(), 2); + QVERIFY(wizard.currentPage() == wizard.page(2)); + QCOMPARE(wizard.nextId(), -1); + CHECK_VISITED(wizard, QList<int>() << -3 << -2 << 0 << 2); + } + + wizard.back(); + QCOMPARE(wizard.startId(), -3); + QCOMPARE(wizard.currentId(), 0); + QVERIFY(wizard.currentPage() == wizard.page(0)); + QCOMPARE(wizard.nextId(), 2); + CHECK_VISITED(wizard, QList<int>() << -3 << -2 << 0); + + wizard.back(); + QCOMPARE(wizard.startId(), -3); + QCOMPARE(wizard.currentId(), -2); + QVERIFY(wizard.currentPage() == wizard.page(-2)); + QCOMPARE(wizard.nextId(), 0); + CHECK_VISITED(wizard, QList<int>() << -3 << -2); + + for (int i = 0; i < 100; ++i) { + wizard.back(); + QCOMPARE(wizard.startId(), -3); + QCOMPARE(wizard.currentId(), -3); + QVERIFY(wizard.currentPage() == wizard.page(-3)); + QCOMPARE(wizard.nextId(), -2); + CHECK_VISITED(wizard, QList<int>() << -3); + } + + for (int i = 0; i < 100; ++i) { + wizard.restart(); + QCOMPARE(wizard.startId(), -3); + QCOMPARE(wizard.currentId(), -3); + QVERIFY(wizard.currentPage() == wizard.page(-3)); + QCOMPARE(wizard.nextId(), -2); + CHECK_VISITED(wizard, QList<int>() << -3); + } + QCOMPARE(spy.count(), 0); + delete parent; +} + +void tst_QWizard::setStartId() +{ + QWizard wizard; + QCOMPARE(wizard.startId(), -1); + + wizard.setPage(INT_MIN, new QWizardPage); + QCOMPARE(wizard.startId(), INT_MIN); + + wizard.setPage(-2, new QWizardPage); + QCOMPARE(wizard.startId(), INT_MIN); + + wizard.setPage(0, new QWizardPage); + QCOMPARE(wizard.startId(), INT_MIN); + + wizard.setPage(1, new QWizardPage); + QCOMPARE(wizard.startId(), INT_MIN); + + wizard.setPage(INT_MAX, new QWizardPage); + QCOMPARE(wizard.startId(), INT_MIN); + + QTest::ignoreMessage(QtWarningMsg,"QWizard::setStartId: Invalid page ID 123"); + wizard.setStartId(123); + QCOMPARE(wizard.startId(), INT_MIN); + + wizard.setStartId(-1); + QCOMPARE(wizard.startId(), INT_MIN); + + wizard.setStartId(-2); + QCOMPARE(wizard.startId(), -2); + QCOMPARE(wizard.nextId(), -1); + + wizard.setStartId(-1); + QCOMPARE(wizard.startId(), INT_MIN); + + wizard.setStartId(-2); + QCOMPARE(wizard.startId(), -2); + QCOMPARE(wizard.nextId(), -1); + + wizard.restart(); + QCOMPARE(wizard.startId(), -2); + QCOMPARE(wizard.currentId(), -2); + QVERIFY(wizard.currentPage() == wizard.page(-2)); + QCOMPARE(wizard.nextId(), 0); + + wizard.next(); + QCOMPARE(wizard.startId(), -2); + QCOMPARE(wizard.currentId(), 0); + QVERIFY(wizard.currentPage() == wizard.page(0)); + QCOMPARE(wizard.nextId(), 1); + + wizard.setStartId(INT_MIN); + QCOMPARE(wizard.startId(), INT_MIN); + QCOMPARE(wizard.currentId(), 0); + QVERIFY(wizard.currentPage() == wizard.page(0)); + QCOMPARE(wizard.nextId(), 1); + + wizard.next(); + QCOMPARE(wizard.startId(), INT_MIN); + QCOMPARE(wizard.currentId(), 1); + QVERIFY(wizard.currentPage() == wizard.page(1)); + QCOMPARE(wizard.nextId(), INT_MAX); + + wizard.next(); + QCOMPARE(wizard.startId(), INT_MIN); + QCOMPARE(wizard.currentId(), INT_MAX); + QVERIFY(wizard.currentPage() == wizard.page(INT_MAX)); + QCOMPARE(wizard.nextId(), -1); + CHECK_VISITED(wizard, QList<int>() << -2 << 0 << 1 << INT_MAX); +} + +struct MyPage2 : public QWizardPage +{ +public: + MyPage2() : init(0), cleanup(0), validate(0) {} + + void initializePage() { ++init; QWizardPage::initializePage(); } + void cleanupPage() { ++cleanup; QWizardPage::cleanupPage(); } + bool validatePage() { ++validate; return QWizardPage::validatePage(); } + + bool check(int init, int cleanup) + { + return init == this->init + && cleanup == this->cleanup + && (this->init == this->cleanup || this->init - 1 == this->cleanup); + } + + int init; + int cleanup; + int validate; +}; + +#define CHECK_PAGE_INIT(i0, c0, i1, c1, i2, c2) \ + QVERIFY(page0->check((i0), (c0))); \ + QVERIFY(page1->check((i1), (c1))); \ + QVERIFY(page2->check((i2), (c2))); + +void tst_QWizard::setOption_IndependentPages() +{ + MyPage2 *page0 = new MyPage2; + MyPage2 *page1 = new MyPage2; + MyPage2 *page2 = new MyPage2; + + QWizard wizard; + wizard.addPage(page0); + wizard.addPage(page1); + wizard.addPage(page2); + + QVERIFY(!wizard.testOption(QWizard::IndependentPages)); + + wizard.restart(); + + // Make sure initializePage() and cleanupPage() are called are + // they should when the + // wizard.testOption(QWizard::IndependentPages option is off. + for (int i = 0; i < 10; ++i) { + CHECK_PAGE_INIT(i + 1, i, i, i, i, i); + + wizard.next(); + CHECK_PAGE_INIT(i + 1, i, i + 1, i, i, i); + + wizard.next(); + CHECK_PAGE_INIT(i + 1, i, i + 1, i, i + 1, i); + + wizard.next(); + CHECK_PAGE_INIT(i + 1, i, i + 1, i, i + 1, i); + + wizard.back(); + CHECK_PAGE_INIT(i + 1, i, i + 1, i, i + 1, i + 1); + + wizard.back(); + CHECK_PAGE_INIT(i + 1, i, i + 1, i + 1, i + 1, i + 1); + + wizard.back(); + CHECK_PAGE_INIT(i + 1, i, i + 1, i + 1, i + 1, i + 1); + + wizard.restart(); + } + + CHECK_PAGE_INIT(11, 10, 10, 10, 10, 10); + + wizard.next(); + CHECK_PAGE_INIT(11, 10, 11, 10, 10, 10); + + // Now, turn on the option and check that they're called at the + // appropriate times (which aren't the same). + wizard.setOption(QWizard::IndependentPages, true); + CHECK_PAGE_INIT(11, 10, 11, 10, 10, 10); + + wizard.back(); + CHECK_PAGE_INIT(11, 10, 11, 10, 10, 10); + + wizard.next(); + CHECK_PAGE_INIT(11, 10, 11, 10, 10, 10); + + wizard.next(); + CHECK_PAGE_INIT(11, 10, 11, 10, 11, 10); + + wizard.next(); + CHECK_PAGE_INIT(11, 10, 11, 10, 11, 10); + + wizard.back(); + CHECK_PAGE_INIT(11, 10, 11, 10, 11, 10); + + wizard.setStartId(2); + + wizard.restart(); + CHECK_PAGE_INIT(11, 11, 11, 11, 12, 11); + + wizard.back(); + CHECK_PAGE_INIT(11, 11, 11, 11, 12, 11); + + wizard.next(); + CHECK_PAGE_INIT(11, 11, 11, 11, 12, 11); + + wizard.setStartId(0); + wizard.restart(); + CHECK_PAGE_INIT(12, 11, 11, 11, 12, 12); + + wizard.next(); + CHECK_PAGE_INIT(12, 11, 12, 11, 12, 12); + + wizard.next(); + CHECK_PAGE_INIT(12, 11, 12, 11, 13, 12); + + wizard.back(); + CHECK_PAGE_INIT(12, 11, 12, 11, 13, 12); + + // Fun stuff here. + + wizard.setOption(QWizard::IndependentPages, false); + CHECK_PAGE_INIT(12, 11, 12, 11, 13, 13); + + wizard.setOption(QWizard::IndependentPages, true); + CHECK_PAGE_INIT(12, 11, 12, 11, 13, 13); + + wizard.setOption(QWizard::IndependentPages, false); + CHECK_PAGE_INIT(12, 11, 12, 11, 13, 13); + + wizard.back(); + CHECK_PAGE_INIT(12, 11, 12, 12, 13, 13); + + wizard.back(); + CHECK_PAGE_INIT(12, 11, 12, 12, 13, 13); +} + +void tst_QWizard::setOption_IgnoreSubTitles() +{ +#if defined(Q_OS_WINCE) + QSKIP("Skipped because of limited resources and potential crash. (Task: 166824)", SkipAll); +#endif + QWizard wizard1; + wizard1.setButtonLayout(QList<QWizard::WizardButton>() << QWizard::CancelButton); + wizard1.resize(500, 500); + QVERIFY(!wizard1.testOption(QWizard::IgnoreSubTitles)); + QWizardPage *page11 = new QWizardPage; + page11->setTitle("Page X"); + page11->setSubTitle("Some subtitle"); + + QWizardPage *page12 = new QWizardPage; + page12->setTitle("Page X"); + + wizard1.addPage(page11); + wizard1.addPage(page12); + + QWizard wizard2; + wizard2.setButtonLayout(QList<QWizard::WizardButton>() << QWizard::CancelButton); + wizard2.resize(500, 500); + wizard2.setOption(QWizard::IgnoreSubTitles, true); + QWizardPage *page21 = new QWizardPage; + page21->setTitle("Page X"); + page21->setSubTitle("Some subtitle"); + + QWizardPage *page22 = new QWizardPage; + page22->setTitle("Page X"); + + wizard2.addPage(page21); + wizard2.addPage(page22); + + wizard1.show(); + wizard2.show(); + + // Check that subtitles are shown when they should (i.e., + // they're set and IgnoreSubTitles is off). + + qApp->setActiveWindow(0); // ensure no focus rectangle around cancel button + QImage i11 = grabWidget(&wizard1); + QImage i21 = grabWidget(&wizard2); + QVERIFY(i11 != i21); + + wizard1.next(); + wizard2.next(); + + QImage i12 = grabWidget(&wizard1); + QImage i22 = grabWidget(&wizard2); + QVERIFY(i12 == i22); + QVERIFY(i21 == i22); + + wizard1.back(); + wizard2.back(); + + QImage i13 = grabWidget(&wizard1); + QImage i23 = grabWidget(&wizard2); + QVERIFY(i13 == i11); + QVERIFY(i23 == i21); + + wizard1.setOption(QWizard::IgnoreSubTitles, true); + wizard2.setOption(QWizard::IgnoreSubTitles, false); + + QImage i14 = grabWidget(&wizard1); + QImage i24 = grabWidget(&wizard2); + QVERIFY(i14 == i21); + QVERIFY(i24 == i11); + + // Check the impact of subtitles on the rest of the layout, by + // using a subtitle that looks empty (but that isn't). In + // Classic and Modern styles, this should be enough to trigger a + // "header"; in Mac style, this only creates a QLabel, with no + // text, i.e. it doesn't affect the layout. + + page11->setSubTitle("<b></b>"); // not quite empty, but looks empty + + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 2; ++j) { + wizard1.setOption(QWizard::IgnoreSubTitles, j == 0); + + wizard1.setWizardStyle(i == 0 ? QWizard::ClassicStyle + : i == 1 ? QWizard::ModernStyle + : QWizard::MacStyle); + wizard1.restart(); + QImage i1 = grabWidget(&wizard1); + + wizard1.next(); + QImage i2 = grabWidget(&wizard1); + + if (j == 0 || wizard1.wizardStyle() == QWizard::MacStyle) { + QVERIFY(i1 == i2); + } else { + QVERIFY(i1 != i2); + } + } + } +} + +void tst_QWizard::setOption_ExtendedWatermarkPixmap() +{ +#if defined(Q_OS_WINCE) + QSKIP("Skipped because of limited resources and potential crash. (Task: 166824)", SkipAll); +#endif + QPixmap watermarkPixmap(200, 400); + watermarkPixmap.fill(Qt::black); + + QWizard wizard1; + wizard1.setButtonLayout(QList<QWizard::WizardButton>() << QWizard::CancelButton); + QVERIFY(!wizard1.testOption(QWizard::ExtendedWatermarkPixmap)); + QWizardPage *page11 = new QWizardPage; + page11->setTitle("Page X"); + page11->setPixmap(QWizard::WatermarkPixmap, watermarkPixmap); + + QWizardPage *page12 = new QWizardPage; + page12->setTitle("Page X"); + + wizard1.addPage(page11); + wizard1.addPage(page12); + + QWizard wizard2; + wizard2.setButtonLayout(QList<QWizard::WizardButton>() << QWizard::CancelButton); + wizard2.setOption(QWizard::ExtendedWatermarkPixmap, true); + QWizardPage *page21 = new QWizardPage; + page21->setTitle("Page X"); + page21->setPixmap(QWizard::WatermarkPixmap, watermarkPixmap); + + QWizardPage *page22 = new QWizardPage; + page22->setTitle("Page X"); + + wizard2.addPage(page21); + wizard2.addPage(page22); + + wizard1.show(); + wizard2.show(); + + // Check the impact of watermark pixmaps on the rest of the layout. + + for (int i = 0; i < 3; ++i) { + QImage i1[2]; + QImage i2[2]; + for (int j = 0; j < 2; ++j) { + wizard1.setOption(QWizard::ExtendedWatermarkPixmap, j == 0); + + wizard1.setWizardStyle(i == 0 ? QWizard::ClassicStyle + : i == 1 ? QWizard::ModernStyle + : QWizard::MacStyle); + wizard1.restart(); + wizard1.setMaximumSize(1000, 1000); + wizard1.resize(600, 600); + i1[j] = grabWidget(&wizard1); + + wizard1.next(); + wizard1.setMaximumSize(1000, 1000); + wizard1.resize(600, 600); + i2[j] = grabWidget(&wizard1); + } + + if (wizard1.wizardStyle() == QWizard::MacStyle) { + QVERIFY(i1[0] == i1[1]); + QVERIFY(i2[0] == i2[1]); + QVERIFY(i1[0] == i2[0]); + } else { + QVERIFY(i1[0] != i1[1]); + QVERIFY(i2[0] == i2[1]); + QVERIFY(i1[0] != i2[0]); + QVERIFY(i1[1] != i2[1]); + } + } +} + +void tst_QWizard::setOption_NoDefaultButton() +{ + QWizard wizard; + wizard.setOption(QWizard::NoDefaultButton, false); + wizard.setOption(QWizard::HaveFinishButtonOnEarlyPages, true); + wizard.addPage(new QWizardPage); + wizard.page(0)->setFinalPage(true); + wizard.addPage(new QWizardPage); + + if (QPushButton *pb = qobject_cast<QPushButton *>(wizard.button(QWizard::NextButton))) + pb->setAutoDefault(false); + if (QPushButton *pb = qobject_cast<QPushButton *>(wizard.button(QWizard::FinishButton))) + pb->setAutoDefault(false); + + wizard.show(); + qApp->processEvents(); + QVERIFY(qobject_cast<QPushButton *>(wizard.button(QWizard::NextButton))->isDefault()); + QVERIFY(wizard.button(QWizard::FinishButton)->isEnabled()); + + wizard.next(); + QVERIFY(qobject_cast<QPushButton *>(wizard.button(QWizard::FinishButton))->isDefault()); + + wizard.back(); + QVERIFY(qobject_cast<QPushButton *>(wizard.button(QWizard::NextButton))->isDefault()); + + wizard.setOption(QWizard::NoDefaultButton, true); + QVERIFY(!qobject_cast<QPushButton *>(wizard.button(QWizard::NextButton))->isDefault()); + QVERIFY(!qobject_cast<QPushButton *>(wizard.button(QWizard::FinishButton))->isDefault()); + + wizard.next(); + QVERIFY(!qobject_cast<QPushButton *>(wizard.button(QWizard::NextButton))->isDefault()); + QVERIFY(!qobject_cast<QPushButton *>(wizard.button(QWizard::FinishButton))->isDefault()); + + wizard.back(); + QVERIFY(!qobject_cast<QPushButton *>(wizard.button(QWizard::NextButton))->isDefault()); + QVERIFY(!qobject_cast<QPushButton *>(wizard.button(QWizard::FinishButton))->isDefault()); + + wizard.setOption(QWizard::NoDefaultButton, false); + QVERIFY(qobject_cast<QPushButton *>(wizard.button(QWizard::NextButton))->isDefault()); +} + +void tst_QWizard::setOption_NoBackButtonOnStartPage() +{ + QWizard wizard; + wizard.setOption(QWizard::NoBackButtonOnStartPage, true); + wizard.addPage(new QWizardPage); + wizard.addPage(new QWizardPage); + wizard.addPage(new QWizardPage); + + wizard.setStartId(1); + wizard.show(); + qApp->processEvents(); + + QVERIFY(!wizard.button(QWizard::BackButton)->isVisible()); + + wizard.setOption(QWizard::NoBackButtonOnStartPage, false); + qApp->processEvents(); + QVERIFY(wizard.button(QWizard::BackButton)->isVisible()); + + wizard.setOption(QWizard::NoBackButtonOnStartPage, true); + qApp->processEvents(); + QVERIFY(!wizard.button(QWizard::BackButton)->isVisible()); + + wizard.next(); + qApp->processEvents(); + QVERIFY(wizard.button(QWizard::BackButton)->isVisible()); + + wizard.setOption(QWizard::NoBackButtonOnStartPage, false); + QVERIFY(wizard.button(QWizard::BackButton)->isVisible()); + + wizard.setOption(QWizard::NoBackButtonOnStartPage, true); + QVERIFY(wizard.button(QWizard::BackButton)->isVisible()); + + wizard.back(); + qApp->processEvents(); + QVERIFY(!wizard.button(QWizard::BackButton)->isVisible()); +} + +void tst_QWizard::setOption_NoBackButtonOnLastPage() +{ + for (int i = 0; i < 2; ++i) { + QWizard wizard; + wizard.setOption(QWizard::NoBackButtonOnStartPage, false); + wizard.setOption(QWizard::NoBackButtonOnLastPage, true); + wizard.setOption(QWizard::DisabledBackButtonOnLastPage, i == 0); // changes nothing + wizard.addPage(new QWizardPage); + wizard.addPage(new QWizardPage); + wizard.page(1)->setFinalPage(true); // changes nothing (final != last in general) + wizard.addPage(new QWizardPage); + + wizard.setStartId(1); + wizard.show(); + qApp->processEvents(); + + QVERIFY(wizard.button(QWizard::BackButton)->isVisible()); + + wizard.next(); + qApp->processEvents(); + QVERIFY(!wizard.button(QWizard::BackButton)->isVisible()); + + wizard.next(); + qApp->processEvents(); + QVERIFY(!wizard.button(QWizard::BackButton)->isVisible()); + + wizard.back(); + qApp->processEvents(); + QVERIFY(wizard.button(QWizard::BackButton)->isVisible()); + + wizard.next(); + qApp->processEvents(); + QVERIFY(!wizard.button(QWizard::BackButton)->isVisible()); + + wizard.setOption(QWizard::NoBackButtonOnLastPage, false); + QVERIFY(wizard.button(QWizard::BackButton)->isVisible()); + + wizard.setOption(QWizard::NoBackButtonOnLastPage, true); + QVERIFY(!wizard.button(QWizard::BackButton)->isVisible()); + + wizard.addPage(new QWizardPage); + QVERIFY(!wizard.button(QWizard::BackButton)->isVisible()); // this is maybe wrong + } +} + +void tst_QWizard::setOption_DisabledBackButtonOnLastPage() +{ + QWizard wizard; + wizard.setOption(QWizard::NoBackButtonOnStartPage, false); + wizard.setOption(QWizard::DisabledBackButtonOnLastPage, true); + wizard.addPage(new QWizardPage); + wizard.addPage(new QWizardPage); + wizard.page(1)->setFinalPage(true); // changes nothing (final != last in general) + wizard.addPage(new QWizardPage); + + wizard.setStartId(1); + wizard.show(); + qApp->processEvents(); + + QVERIFY(wizard.button(QWizard::BackButton)->isVisible()); + QVERIFY(!wizard.button(QWizard::BackButton)->isEnabled()); + + wizard.next(); + qApp->processEvents(); + QVERIFY(!wizard.button(QWizard::BackButton)->isEnabled()); + + wizard.next(); + qApp->processEvents(); + QVERIFY(!wizard.button(QWizard::BackButton)->isEnabled()); + + wizard.back(); + qApp->processEvents(); + QVERIFY(wizard.button(QWizard::BackButton)->isVisible()); + QVERIFY(!wizard.button(QWizard::BackButton)->isEnabled()); + + wizard.next(); + qApp->processEvents(); + QVERIFY(!wizard.button(QWizard::BackButton)->isEnabled()); + + wizard.setOption(QWizard::DisabledBackButtonOnLastPage, false); + QVERIFY(wizard.button(QWizard::BackButton)->isEnabled()); + + wizard.setOption(QWizard::DisabledBackButtonOnLastPage, true); + QVERIFY(!wizard.button(QWizard::BackButton)->isEnabled()); + + wizard.addPage(new QWizardPage); + QVERIFY(!wizard.button(QWizard::BackButton)->isEnabled()); // this is maybe wrong +} + +void tst_QWizard::setOption_HaveNextButtonOnLastPage() +{ + QWizard wizard; + wizard.setOption(QWizard::HaveNextButtonOnLastPage, false); + wizard.addPage(new QWizardPage); + wizard.addPage(new QWizardPage); + wizard.page(1)->setFinalPage(true); // changes nothing (final != last in general) + wizard.addPage(new QWizardPage); + + wizard.setStartId(1); + wizard.show(); + qApp->processEvents(); + + QVERIFY(wizard.button(QWizard::NextButton)->isVisible()); + QVERIFY(wizard.button(QWizard::NextButton)->isEnabled()); + + wizard.next(); + QVERIFY(!wizard.button(QWizard::NextButton)->isVisible()); + + wizard.setOption(QWizard::HaveNextButtonOnLastPage, true); + QVERIFY(wizard.button(QWizard::NextButton)->isVisible()); + QVERIFY(!wizard.button(QWizard::NextButton)->isEnabled()); + + wizard.next(); + QVERIFY(wizard.button(QWizard::NextButton)->isVisible()); + QVERIFY(!wizard.button(QWizard::NextButton)->isEnabled()); + + wizard.back(); + QVERIFY(wizard.button(QWizard::NextButton)->isVisible()); + QVERIFY(wizard.button(QWizard::NextButton)->isEnabled()); + + wizard.setOption(QWizard::HaveNextButtonOnLastPage, false); + QVERIFY(wizard.button(QWizard::NextButton)->isVisible()); + QVERIFY(wizard.button(QWizard::NextButton)->isEnabled()); + + wizard.next(); + QVERIFY(!wizard.button(QWizard::NextButton)->isVisible()); + + wizard.setOption(QWizard::HaveNextButtonOnLastPage, true); + QVERIFY(wizard.button(QWizard::NextButton)->isVisible()); + QVERIFY(!wizard.button(QWizard::NextButton)->isEnabled()); +} + +void tst_QWizard::setOption_HaveFinishButtonOnEarlyPages() +{ + QWizard wizard; + QVERIFY(!wizard.testOption(QWizard::HaveFinishButtonOnEarlyPages)); + wizard.addPage(new QWizardPage); + wizard.addPage(new QWizardPage); + wizard.page(1)->setFinalPage(true); + wizard.addPage(new QWizardPage); + + wizard.show(); + qApp->processEvents(); + + QVERIFY(!wizard.button(QWizard::FinishButton)->isVisible()); + + wizard.next(); + QVERIFY(wizard.button(QWizard::FinishButton)->isVisible()); + + wizard.next(); + QVERIFY(wizard.button(QWizard::FinishButton)->isVisible()); + + wizard.back(); + QVERIFY(wizard.button(QWizard::FinishButton)->isVisible()); + + wizard.back(); + QVERIFY(!wizard.button(QWizard::FinishButton)->isVisible()); + + wizard.setOption(QWizard::HaveFinishButtonOnEarlyPages, true); + QVERIFY(wizard.button(QWizard::FinishButton)->isVisible()); + + wizard.next(); + QVERIFY(wizard.button(QWizard::FinishButton)->isVisible()); + + wizard.setOption(QWizard::HaveFinishButtonOnEarlyPages, false); + QVERIFY(wizard.button(QWizard::FinishButton)->isVisible()); + + wizard.back(); + QVERIFY(!wizard.button(QWizard::FinishButton)->isVisible()); +} + +void tst_QWizard::setOption_NoCancelButton() +{ + for (int i = 0; i < 2; ++i) { + QWizard wizard; + wizard.setOption(QWizard::NoCancelButton, true); + wizard.setOption(QWizard::CancelButtonOnLeft, i == 0); + wizard.addPage(new QWizardPage); + wizard.addPage(new QWizardPage); + wizard.show(); + qApp->processEvents(); + + QVERIFY(!wizard.button(QWizard::CancelButton)->isVisible()); + + wizard.next(); + QVERIFY(!wizard.button(QWizard::CancelButton)->isVisible()); + + wizard.setOption(QWizard::NoCancelButton, false); + QVERIFY(wizard.button(QWizard::CancelButton)->isVisible()); + + wizard.back(); + QVERIFY(wizard.button(QWizard::CancelButton)->isVisible()); + + wizard.setOption(QWizard::NoCancelButton, true); + QVERIFY(!wizard.button(QWizard::CancelButton)->isVisible()); + } +} + +void tst_QWizard::setOption_CancelButtonOnLeft() +{ + for (int i = 0; i < 2; ++i) { + int sign = (i == 0) ? +1 : -1; + + QWizard wizard; + wizard.setLayoutDirection(i == 0 ? Qt::LeftToRight : Qt::RightToLeft); + wizard.setOption(QWizard::NoCancelButton, false); + wizard.setOption(QWizard::CancelButtonOnLeft, true); + wizard.setOption(QWizard::NoBackButtonOnStartPage, false); + wizard.addPage(new QWizardPage); + wizard.addPage(new QWizardPage); + wizard.show(); + qApp->processEvents(); + + const QAbstractButton *refButton = wizard.button((wizard.wizardStyle() == QWizard::AeroStyle) + ? QWizard::NextButton : QWizard::BackButton); + const QAbstractButton *refButton2 = wizard.button((wizard.wizardStyle() == QWizard::AeroStyle) + ? QWizard::FinishButton : QWizard::BackButton); + + QVERIFY(sign * wizard.button(QWizard::CancelButton)->x() < sign * refButton->x()); + + wizard.next(); + qApp->processEvents(); + QVERIFY(sign * wizard.button(QWizard::CancelButton)->x() < sign * refButton->x()); + + wizard.setOption(QWizard::CancelButtonOnLeft, false); + qApp->processEvents(); + QVERIFY(sign * wizard.button(QWizard::CancelButton)->x() > sign * refButton2->x()); + + wizard.back(); + qApp->processEvents(); + QVERIFY(sign * wizard.button(QWizard::CancelButton)->x() > sign * refButton->x()); + } +} + +void tst_QWizard::setOption_HaveHelpButton() +{ + for (int i = 0; i < 2; ++i) { + QWizard wizard; + QVERIFY(!wizard.testOption(QWizard::HaveHelpButton)); + wizard.setOption(QWizard::HaveHelpButton, false); + wizard.setOption(QWizard::HelpButtonOnRight, i == 0); + wizard.addPage(new QWizardPage); + wizard.addPage(new QWizardPage); + wizard.show(); + qApp->processEvents(); + + QVERIFY(!wizard.button(QWizard::HelpButton)->isVisible()); + + wizard.next(); + QVERIFY(!wizard.button(QWizard::HelpButton)->isVisible()); + + wizard.setOption(QWizard::HaveHelpButton, true); + QVERIFY(wizard.button(QWizard::HelpButton)->isVisible()); + + wizard.back(); + QVERIFY(wizard.button(QWizard::HelpButton)->isVisible()); + + wizard.setOption(QWizard::HaveHelpButton, false); + QVERIFY(!wizard.button(QWizard::HelpButton)->isVisible()); + } +} + +void tst_QWizard::setOption_HelpButtonOnRight() +{ + for (int i = 0; i < 2; ++i) { + int sign = (i == 0) ? +1 : -1; + + QWizard wizard; + wizard.setLayoutDirection(i == 0 ? Qt::LeftToRight : Qt::RightToLeft); + wizard.setOption(QWizard::HaveHelpButton, true); + wizard.setOption(QWizard::HelpButtonOnRight, false); + wizard.setOption(QWizard::NoBackButtonOnStartPage, false); + wizard.addPage(new QWizardPage); + wizard.addPage(new QWizardPage); + wizard.show(); + qApp->processEvents(); + + const QAbstractButton *refButton = wizard.button((wizard.wizardStyle() == QWizard::AeroStyle) + ? QWizard::NextButton : QWizard::BackButton); + + QVERIFY(sign * wizard.button(QWizard::HelpButton)->x() < sign * refButton->x()); + + wizard.next(); + qApp->processEvents(); + QVERIFY(sign * wizard.button(QWizard::HelpButton)->x() < sign * refButton->x()); + + wizard.setOption(QWizard::HelpButtonOnRight, true); + qApp->processEvents(); + QVERIFY(sign * wizard.button(QWizard::HelpButton)->x() > sign * refButton->x()); + + wizard.back(); + qApp->processEvents(); + QVERIFY(sign * wizard.button(QWizard::HelpButton)->x() > sign * refButton->x()); + } +} + +void tst_QWizard::setOption_HaveCustomButtonX() +{ + for (int i = 0; i < 2; ++i) { + for (int j = 0; j < 2; ++j) { + for (int k = 0; k < 2; ++k) { + QWizard wizard; + wizard.setLayoutDirection(Qt::LeftToRight); + wizard.addPage(new QWizardPage); + wizard.addPage(new QWizardPage); + wizard.show(); + + wizard.setButtonText(QWizard::CustomButton1, "Foo"); + wizard.setButton(QWizard::CustomButton2, new QCheckBox("Bar")); + wizard.button(QWizard::CustomButton3)->setText("Baz"); + + wizard.setOption(QWizard::HaveCustomButton1, i == 0); + wizard.setOption(QWizard::HaveCustomButton2, j == 0); + wizard.setOption(QWizard::HaveCustomButton3, k == 0); + + QVERIFY(wizard.button(QWizard::CustomButton1)->isHidden() == (i != 0)); + QVERIFY(wizard.button(QWizard::CustomButton2)->isHidden() == (j != 0)); + QVERIFY(wizard.button(QWizard::CustomButton3)->isHidden() == (k != 0)); + + if (i + j + k == 0) { + qApp->processEvents(); + QVERIFY(wizard.button(QWizard::CustomButton1)->x() + < wizard.button(QWizard::CustomButton2)->x()); + QVERIFY(wizard.button(QWizard::CustomButton2)->x() + < wizard.button(QWizard::CustomButton3)->x()); + } + } + } + } +} + +class Operation +{ +public: + virtual void apply(QWizard *) const = 0; + virtual QString describe() const = 0; +protected: + virtual ~Operation() {} +}; + +class SetPage : public Operation +{ + void apply(QWizard *wizard) const + { + wizard->restart(); + for (int j = 0; j < page; ++j) + wizard->next(); + } + QString describe() const { return QString("set page %1").arg(page); } + const int page; +public: + SetPage(int page) : page(page) {} +}; + +class SetStyle : public Operation +{ + void apply(QWizard *wizard) const { wizard->setWizardStyle(style); } + QString describe() const { return QString("set style %1").arg(style); } + const QWizard::WizardStyle style; +public: + SetStyle(QWizard::WizardStyle style) : style(style) {} +}; + +class SetOption : public Operation +{ + void apply(QWizard *wizard) const { wizard->setOption(option, on); } + QString describe() const; + const QWizard::WizardOption option; + const bool on; +public: + SetOption(QWizard::WizardOption option, bool on) : option(option), on(on) {} +}; + +class OptionInfo +{ + OptionInfo() + { + tags[QWizard::IndependentPages] = "0/IPP"; + tags[QWizard::IgnoreSubTitles] = "1/IST"; + tags[QWizard::ExtendedWatermarkPixmap] = "2/EWP"; + tags[QWizard::NoDefaultButton] = "3/NDB"; + tags[QWizard::NoBackButtonOnStartPage] = "4/BSP"; + tags[QWizard::NoBackButtonOnLastPage] = "5/BLP"; + tags[QWizard::DisabledBackButtonOnLastPage] = "6/DLP"; + tags[QWizard::HaveNextButtonOnLastPage] = "7/NLP"; + tags[QWizard::HaveFinishButtonOnEarlyPages] = "8/FEP"; + tags[QWizard::NoCancelButton] = "9/NCB"; + tags[QWizard::CancelButtonOnLeft] = "10/CBL"; + tags[QWizard::HaveHelpButton] = "11/HHB"; + tags[QWizard::HelpButtonOnRight] = "12/HBR"; + tags[QWizard::HaveCustomButton1] = "13/CB1"; + tags[QWizard::HaveCustomButton2] = "14/CB2"; + tags[QWizard::HaveCustomButton3] = "15/CB3"; + + for (int i = 0; i < 2; ++i) { + QMap<QWizard::WizardOption, Operation *> operations_; + foreach (QWizard::WizardOption option, tags.keys()) + operations_[option] = new SetOption(option, i == 1); + operations << operations_; + } + } + OptionInfo(OptionInfo const&); + OptionInfo& operator=(OptionInfo const&); + QMap<QWizard::WizardOption, QString> tags; + QList<QMap<QWizard::WizardOption, Operation *> > operations; +public: + static OptionInfo &instance() + { + static OptionInfo optionInfo; + return optionInfo; + } + + QString tag(QWizard::WizardOption option) const { return tags.value(option); } + Operation * operation(QWizard::WizardOption option, bool on) const + { return operations.at(on).value(option); } + QList<QWizard::WizardOption> options() const { return tags.keys(); } +}; + +QString SetOption::describe() const +{ + return QString("set opt %1 %2").arg(OptionInfo::instance().tag(option)).arg(on); +} + +Q_DECLARE_METATYPE(Operation *) +Q_DECLARE_METATYPE(SetPage *) +Q_DECLARE_METATYPE(SetStyle *) +Q_DECLARE_METATYPE(SetOption *) +Q_DECLARE_METATYPE(QList<Operation *>) + +class TestGroup +{ +public: + enum Type {Equality, NonEquality}; + + TestGroup(const QString &name = QString("no name"), Type type = Equality) + : name(name), type(type), nRows_(0) {} + + void reset(const QString &name, Type type = Equality) + { + this->name = name; + this->type = type; + combinations.clear(); + } + + QList<Operation *> &add() + { combinations << new QList<Operation *>; return *(combinations.last()); } + + void createTestRows() + { + for (int i = 0; i < combinations.count(); ++i) { + QTest::newRow((name + QString(", row %1").arg(i)).toLatin1().data()) + << (i == 0) << (type == Equality) << *(combinations.at(i)); + ++nRows_; + } + } + + int nRows() const { return nRows_; } + +private: + QString name; + Type type; + int nRows_; + QList<QList<Operation *> *> combinations; +}; + +class IntroPage : public QWizardPage +{ + Q_OBJECT +public: + IntroPage() + { + setTitle(tr("Intro")); + setSubTitle(tr("Intro Subtitle")); + QVBoxLayout *layout = new QVBoxLayout; + layout->addWidget(new QLabel(tr("Intro Label"))); + setLayout(layout); + } +}; + +class MiddlePage : public QWizardPage +{ + Q_OBJECT +public: + MiddlePage() + { + setTitle(tr("Middle")); + setSubTitle(tr("Middle Subtitle")); + QVBoxLayout *layout = new QVBoxLayout; + layout->addWidget(new QLabel(tr("Middle Label"))); + setLayout(layout); + } +}; + +class ConclusionPage : public QWizardPage +{ + Q_OBJECT +public: + ConclusionPage() + { + setTitle(tr("Conclusion")); + setSubTitle(tr("Conclusion Subtitle")); + QVBoxLayout *layout = new QVBoxLayout; + layout->addWidget(new QLabel(tr("Conclusion Label"))); + setLayout(layout); + } +}; + +class TestWizard : public QWizard +{ + Q_OBJECT + QList<int> pageIds; + QString opsDescr; +public: + TestWizard() + { + setPixmap(QWizard::BannerPixmap, QPixmap(":/images/banner.png")); + setPixmap(QWizard::BackgroundPixmap, QPixmap(":/images/background.png")); + setPixmap(QWizard::WatermarkPixmap, QPixmap(":/images/watermark.png")); + setPixmap(QWizard::LogoPixmap, QPixmap(":/images/logo.png")); + setButtonText(QWizard::CustomButton1, "custom 1"); + setButtonText(QWizard::CustomButton2, "custom 2"); + setButtonText(QWizard::CustomButton3, "custom 3"); + pageIds << addPage(new IntroPage); + pageIds << addPage(new MiddlePage); + pageIds << addPage(new ConclusionPage); + + // Disable antialiased font rendering since this may sometimes result in tiny + // and (apparent) non-deterministic pixel variations between images expected to be + // identical. This may only be a problem on X11. + QFont f = font(); + f.setStyleStrategy(QFont::NoAntialias); + setFont(f); + + // ### Required to work with a deficiency(?) in QWizard: +// setFixedSize(800, 600); + } + + ~TestWizard() + { + foreach (int id, pageIds) { + QWizardPage *page_to_delete = page(id); + removePage(id); + delete page_to_delete; + } + } + + void applyOperations(const QList<Operation *> &operations) + { + foreach (Operation * op, operations) { + if (op) { + op->apply(this); + opsDescr += QString("(%1) ").arg(op->describe()); + } + } + } + + QImage createImage() const + { + return QPixmap::grabWidget(const_cast<TestWizard *>(this)) + .toImage().convertToFormat(QImage::Format_ARGB32); + } + + QString operationsDescription() const { return opsDescr; } +}; + +class CombinationsTestData +{ + TestGroup testGroup; + QList<Operation *> pageOps; + QList<Operation *> styleOps; + QMap<bool, QList<Operation *> *> setAllOptions; +public: + CombinationsTestData() + { + QTest::addColumn<bool>("ref"); + QTest::addColumn<bool>("testEquality"); + QTest::addColumn<QList<Operation *> >("operations"); + pageOps << new SetPage(0) << new SetPage(1) << new SetPage(2); + styleOps << new SetStyle(QWizard::ClassicStyle) << new SetStyle(QWizard::ModernStyle) + << new SetStyle(QWizard::MacStyle); +#define SETPAGE(page) pageOps.at(page) +#define SETSTYLE(style) styleOps.at(style) +#define OPT(option, on) OptionInfo::instance().operation(option, on) +#define CLROPT(option) OPT(option, false) +#define SETOPT(option) OPT(option, true) + setAllOptions[false] = new QList<Operation *>; + setAllOptions[true] = new QList<Operation *>; + foreach (QWizard::WizardOption option, OptionInfo::instance().options()) { + *setAllOptions.value(false) << CLROPT(option); + *setAllOptions.value(true) << SETOPT(option); + } +#define CLRALLOPTS *setAllOptions.value(false) +#define SETALLOPTS *setAllOptions.value(true) + } + + int nRows() const { return testGroup.nRows(); } + + // Creates "all" possible test rows. (WARNING: This typically makes the test take too long!) + void createAllTestRows() + { + testGroup.reset("testAll 1.1"); + testGroup.add(); // i.e. no operations applied! + testGroup.add() << SETPAGE(0); + testGroup.add() << SETSTYLE(0); + testGroup.add() << SETPAGE(0) << SETSTYLE(0); + testGroup.add() << SETSTYLE(0) << SETPAGE(0); + testGroup.createTestRows(); + + testGroup.reset("testAll 2.1"); + testGroup.add(); + testGroup.add() << CLRALLOPTS; + testGroup.createTestRows(); + + testGroup.reset("testAll 2.2"); + testGroup.add() << SETALLOPTS; + testGroup.add() << SETALLOPTS << SETALLOPTS; + testGroup.createTestRows(); + + testGroup.reset("testAll 2.3"); + testGroup.add() << CLRALLOPTS; + testGroup.add() << CLRALLOPTS << CLRALLOPTS; + testGroup.createTestRows(); + + testGroup.reset("testAll 2.4"); + testGroup.add() << CLRALLOPTS; + testGroup.add() << SETALLOPTS << CLRALLOPTS; + testGroup.createTestRows(); + + testGroup.reset("testAll 2.5"); + testGroup.add() << SETALLOPTS; + testGroup.add() << CLRALLOPTS << SETALLOPTS; + testGroup.createTestRows(); + + testGroup.reset("testAll 2.6"); + testGroup.add() << SETALLOPTS; + testGroup.add() << SETALLOPTS << CLRALLOPTS << SETALLOPTS; + testGroup.createTestRows(); + + testGroup.reset("testAll 2.7"); + testGroup.add() << CLRALLOPTS; + testGroup.add() << CLRALLOPTS << SETALLOPTS << CLRALLOPTS; + testGroup.createTestRows(); + + for (int i = 0; i < 2; ++i) { + QList<Operation *> setOptions = *setAllOptions.value(i == 1); + + testGroup.reset("testAll 3.1"); + testGroup.add() << setOptions; + testGroup.add() << SETPAGE(0) << setOptions; + testGroup.add() << setOptions << SETPAGE(0); + testGroup.add() << SETSTYLE(0) << setOptions; + testGroup.add() << setOptions << SETSTYLE(0); + testGroup.add() << setOptions << SETPAGE(0) << SETSTYLE(0); + testGroup.add() << SETPAGE(0) << setOptions << SETSTYLE(0); + testGroup.add() << SETPAGE(0) << SETSTYLE(0) << setOptions; + testGroup.add() << setOptions << SETSTYLE(0) << SETPAGE(0); + testGroup.add() << SETSTYLE(0) << setOptions << SETPAGE(0); + testGroup.add() << SETSTYLE(0) << SETPAGE(0) << setOptions; + testGroup.createTestRows(); + } + + foreach (Operation *pageOp, pageOps) { + testGroup.reset("testAll 4.1"); + testGroup.add() << pageOp; + testGroup.add() << pageOp << pageOp; + testGroup.createTestRows(); + + for (int i = 0; i < 2; ++i) { + QList<Operation *> optionOps = *setAllOptions.value(i == 1); + testGroup.reset("testAll 4.2"); + testGroup.add() << optionOps << pageOp; + testGroup.add() << pageOp << optionOps; + testGroup.createTestRows(); + + foreach (QWizard::WizardOption option, OptionInfo::instance().options()) { + Operation *optionOp = OPT(option, i == 1); + testGroup.reset("testAll 4.3"); + testGroup.add() << optionOp << pageOp; + testGroup.add() << pageOp << optionOp; + testGroup.createTestRows(); + } + } + } + + foreach (Operation *styleOp, styleOps) { + testGroup.reset("testAll 5.1"); + testGroup.add() << styleOp; + testGroup.add() << styleOp << styleOp; + testGroup.createTestRows(); + + for (int i = 0; i < 2; ++i) { + QList<Operation *> optionOps = *setAllOptions.value(i == 1); + testGroup.reset("testAll 5.2"); + testGroup.add() << optionOps << styleOp; + testGroup.add() << styleOp << optionOps; + testGroup.createTestRows(); + + foreach (QWizard::WizardOption option, OptionInfo::instance().options()) { + Operation *optionOp = OPT(option, i == 1); + testGroup.reset("testAll 5.3"); + testGroup.add() << optionOp << styleOp; + testGroup.add() << styleOp << optionOp; + testGroup.createTestRows(); + } + } + } + + foreach (Operation *pageOp, pageOps) { + foreach (Operation *styleOp, styleOps) { + + testGroup.reset("testAll 6.1"); + testGroup.add() << pageOp; + testGroup.add() << pageOp << pageOp; + testGroup.createTestRows(); + + testGroup.reset("testAll 6.2"); + testGroup.add() << styleOp; + testGroup.add() << styleOp << styleOp; + testGroup.createTestRows(); + + testGroup.reset("testAll 6.3"); + testGroup.add() << pageOp << styleOp; + testGroup.add() << styleOp << pageOp; + testGroup.createTestRows(); + + for (int i = 0; i < 2; ++i) { + QList<Operation *> optionOps = *setAllOptions.value(i == 1); + testGroup.reset("testAll 6.4"); + testGroup.add() << optionOps << pageOp << styleOp; + testGroup.add() << pageOp << optionOps << styleOp; + testGroup.add() << pageOp << styleOp << optionOps; + testGroup.add() << optionOps << styleOp << pageOp; + testGroup.add() << styleOp << optionOps << pageOp; + testGroup.add() << styleOp << pageOp << optionOps; + testGroup.createTestRows(); + + foreach (QWizard::WizardOption option, OptionInfo::instance().options()) { + Operation *optionOp = OPT(option, i == 1); + testGroup.reset("testAll 6.5"); + testGroup.add() << optionOp << pageOp << styleOp; + testGroup.add() << pageOp << optionOp << styleOp; + testGroup.add() << pageOp << styleOp << optionOp; + testGroup.add() << optionOp << styleOp << pageOp; + testGroup.add() << styleOp << optionOp << pageOp; + testGroup.add() << styleOp << pageOp << optionOp; + testGroup.createTestRows(); + } + } + } + } + + testGroup.reset("testAll 7.1", TestGroup::NonEquality); + testGroup.add() << SETPAGE(0); + testGroup.add() << SETPAGE(1); + testGroup.add() << SETPAGE(2); + testGroup.createTestRows(); + + testGroup.reset("testAll 7.2", TestGroup::NonEquality); + testGroup.add() << SETSTYLE(0); + testGroup.add() << SETSTYLE(1); + testGroup.add() << SETSTYLE(2); + testGroup.createTestRows(); + + // more to follow ... + } + + // Creates a "small" number of interesting test rows. + void createTestRows1() + { + testGroup.reset("test1 1"); + testGroup.add() << SETPAGE(0) << SETOPT(QWizard::HaveCustomButton3); + testGroup.add() << SETOPT(QWizard::HaveCustomButton3); + testGroup.createTestRows(); + + testGroup.reset("test1 2"); + testGroup.add() << SETOPT(QWizard::HaveFinishButtonOnEarlyPages) << SETPAGE(0); + testGroup.add() << SETPAGE(0) << SETOPT(QWizard::HaveFinishButtonOnEarlyPages); + testGroup.createTestRows(); + + testGroup.reset("test1 3"); + testGroup.add() << SETPAGE(2) << SETOPT(QWizard::HaveNextButtonOnLastPage); + testGroup.add() << SETOPT(QWizard::HaveNextButtonOnLastPage) << SETPAGE(2); + testGroup.createTestRows(); + } +}; + +// Too much memory usage for testing on CE emulator. +#ifndef Q_OS_WINCE +void tst_QWizard::combinations_data() +{ + CombinationsTestData combTestData; +// combTestData.createAllTestRows(); + combTestData.createTestRows1(); + +// qDebug() << "test rows:" << combTestData.nRows(); +} + +void tst_QWizard::combinations() +{ + QFETCH(bool, ref); + QFETCH(bool, testEquality); + QFETCH(QList<Operation *>, operations); + + TestWizard wizard; +#if !defined(QT_NO_STYLE_WINDOWSVISTA) + if (wizard.wizardStyle() == QWizard::AeroStyle) + return; // ### TODO: passes/fails in a unpredictable way, so disable for now +#endif + wizard.applyOperations(operations); + wizard.show(); // ### TODO: Required, but why? Should wizard.createImage() care? + + static QImage refImage; + static QSize refMinSize; + static QString refDescr; + + if (ref) { + refImage = wizard.createImage(); + refMinSize = wizard.minimumSizeHint(); + refDescr = wizard.operationsDescription(); + return; + } + + QImage image = wizard.createImage(); + + bool minSizeTest = wizard.minimumSizeHint() != refMinSize; + bool imageTest = image != refImage; + QLatin1String otor("!="); + QLatin1String reason("differ"); + + if (!testEquality) { + minSizeTest = false; // the image test is sufficient! + imageTest = !imageTest; + otor = QLatin1String("=="); + reason = QLatin1String("are equal"); + } + + if (minSizeTest) + qDebug() << "minimum sizes" << reason.latin1() << ";" << wizard.minimumSizeHint() + << otor.latin1() << refMinSize; + + if (imageTest) + qDebug() << "images" << reason.latin1(); + + if (minSizeTest || imageTest) { + qDebug() << "\t row 0 operations:" << refDescr.toLatin1(); + qDebug() << "\tcurrent row operations:" << wizard.operationsDescription().toLatin1(); + QVERIFY(false); + } +} +#endif + +class WizardPage : public QWizardPage +{ + Q_OBJECT + bool shown_; + void showEvent(QShowEvent *) { shown_ = true; } + void hideEvent(QHideEvent *) { shown_ = false; } +public: + WizardPage() : shown_(false) {} + bool shown() const { return shown_; } +}; + +class WizardPages +{ + QList<WizardPage *> pages; +public: + void add(WizardPage *page) { pages << page; } + QList<WizardPage *> all() const { return pages; } + QList<WizardPage *> shown() const + { + QList<WizardPage *> result; + foreach (WizardPage *page, pages) + if (page->shown()) + result << page; + return result; + } +}; + +void tst_QWizard::showCurrentPageOnly() +{ + QWizard wizard; + WizardPages pages; + for (int i = 0; i < 5; ++i) { + pages.add(new WizardPage); + wizard.addPage(pages.all().last()); + } + + wizard.show(); + + QCOMPARE(pages.shown().count(), 1); + QCOMPARE(pages.shown().first(), pages.all().first()); + + const int steps = 2; + for (int i = 0; i < steps; ++i) + wizard.next(); + + QCOMPARE(pages.shown().count(), 1); + QCOMPARE(pages.shown().first(), pages.all().at(steps)); + + wizard.restart(); + + QCOMPARE(pages.shown().count(), 1); + QCOMPARE(pages.shown().first(), pages.all().first()); +} + +void tst_QWizard::setButtonText() +{ + QWizard wizard; + wizard.setWizardStyle(QWizard::ClassicStyle); + QWizardPage* page1 = new QWizardPage; + QWizardPage* page2 = new QWizardPage; + wizard.addPage(page1); + wizard.addPage(page2); + + wizard.show(); + qApp->processEvents(); + QVERIFY(wizard.button(QWizard::NextButton)->text().contains("Next")); + QVERIFY(wizard.buttonText(QWizard::NextButton).contains("Next")); + QVERIFY(page1->buttonText(QWizard::NextButton).contains("Next")); + QVERIFY(page2->buttonText(QWizard::NextButton).contains("Next")); + + page2->setButtonText(QWizard::NextButton, "Page2"); + QVERIFY(wizard.button(QWizard::NextButton)->text().contains("Next")); + QVERIFY(wizard.buttonText(QWizard::NextButton).contains("Next")); + QVERIFY(page1->buttonText(QWizard::NextButton).contains("Next")); + QCOMPARE(page2->buttonText(QWizard::NextButton), QString("Page2")); + + wizard.next(); + qApp->processEvents(); + QCOMPARE(wizard.button(QWizard::NextButton)->text(), QString("Page2")); + QVERIFY(wizard.buttonText(QWizard::NextButton).contains("Next")); + QVERIFY(page1->buttonText(QWizard::NextButton).contains("Next")); + QCOMPARE(page2->buttonText(QWizard::NextButton), QString("Page2")); + + wizard.back(); + qApp->processEvents(); + QVERIFY(wizard.button(QWizard::NextButton)->text().contains("Next")); + QVERIFY(wizard.buttonText(QWizard::NextButton).contains("Next")); + QVERIFY(page1->buttonText(QWizard::NextButton).contains("Next")); + QCOMPARE(page2->buttonText(QWizard::NextButton), QString("Page2")); + + wizard.setButtonText(QWizard::NextButton, "Wizard"); + QVERIFY(wizard.button(QWizard::NextButton)->text().contains("Wizard")); + QCOMPARE(wizard.buttonText(QWizard::NextButton), QString("Wizard")); + QCOMPARE(page1->buttonText(QWizard::NextButton), QString("Wizard")); + QCOMPARE(page2->buttonText(QWizard::NextButton), QString("Page2")); + + wizard.next(); + qApp->processEvents(); + QCOMPARE(wizard.button(QWizard::NextButton)->text(), QString("Page2")); + QVERIFY(wizard.buttonText(QWizard::NextButton).contains("Wizard")); + QCOMPARE(page1->buttonText(QWizard::NextButton), QString("Wizard")); + QCOMPARE(page2->buttonText(QWizard::NextButton), QString("Page2")); +} + +void tst_QWizard::setCommitPage() +{ + QWizard wizard; + QWizardPage* page1 = new QWizardPage; + QWizardPage* page2 = new QWizardPage; + wizard.addPage(page1); + wizard.addPage(page2); + wizard.show(); + qApp->processEvents(); + + QVERIFY(!page1->isCommitPage()); + QVERIFY(!wizard.button(QWizard::BackButton)->isEnabled()); + + wizard.next(); + QVERIFY(wizard.button(QWizard::BackButton)->isEnabled()); + + page1->setCommitPage(true); + QVERIFY(page1->isCommitPage()); + + wizard.back(); + QVERIFY(!wizard.button(QWizard::BackButton)->isEnabled()); + + wizard.next(); + QVERIFY(!wizard.button(QWizard::BackButton)->isEnabled()); + + page1->setCommitPage(false); + QVERIFY(!page1->isCommitPage()); + + wizard.back(); + QVERIFY(!wizard.button(QWizard::BackButton)->isEnabled()); + + wizard.next(); + QVERIFY(wizard.button(QWizard::BackButton)->isEnabled()); + + // ### test relabeling of the Cancel button to "Close" once this is implemented +} + +void tst_QWizard::setWizardStyle() +{ + QWizard wizard; + wizard.addPage(new QWizardPage); + wizard.show(); + qApp->processEvents(); + + // defaults + const bool styleHintMatch = + wizard.wizardStyle() == + QWizard::WizardStyle(wizard.style()->styleHint(QStyle::SH_WizardStyle, 0, &wizard)); +#if !defined(QT_NO_STYLE_WINDOWSVISTA) + QVERIFY(styleHintMatch || wizard.wizardStyle() == QWizard::AeroStyle); +#else + QVERIFY(styleHintMatch); +#endif + + // set/get consistency + for (int wstyle = 0; wstyle < QWizard::NStyles; ++wstyle) { + wizard.setWizardStyle((QWizard::WizardStyle)wstyle); + QCOMPARE((int)wizard.wizardStyle(), wstyle); + } +} + +void tst_QWizard::removePage() +{ + QWizard wizard; + QWizardPage *page0 = new QWizardPage; + QWizardPage *page1 = new QWizardPage; + QWizardPage *page2 = new QWizardPage; + QWizardPage *page3 = new QWizardPage; + QSignalSpy spy(&wizard, SIGNAL(pageRemoved(int))); + + wizard.setPage(0, page0); + wizard.setPage(1, page1); + wizard.setPage(2, page2); + wizard.setPage(3, page3); + + wizard.restart(); + QCOMPARE(wizard.pageIds().size(), 4); + QCOMPARE(wizard.visitedPages().size(), 1); + QCOMPARE(spy.count(), 0); + + // Removing a non-existent page + wizard.removePage(4); + QCOMPARE(wizard.pageIds().size(), 4); + QCOMPARE(spy.count(), 0); + + // Removing and then reinserting a page + QCOMPARE(wizard.pageIds().size(), 4); + QVERIFY(wizard.pageIds().contains(2)); + wizard.removePage(2); + QCOMPARE(spy.count(), 1); + QList<QVariant> arguments = spy.takeFirst(); + QCOMPARE(arguments.at(0).toInt(), 2); + QCOMPARE(wizard.pageIds().size(), 3); + QVERIFY(!wizard.pageIds().contains(2)); + wizard.setPage(2, page2); + QCOMPARE(spy.count(), 0); + QCOMPARE(wizard.pageIds().size(), 4); + QVERIFY(wizard.pageIds().contains(2)); + + // Removing the same page twice + wizard.removePage(2); // restore + QCOMPARE(spy.count(), 1); + arguments = spy.takeFirst(); + QCOMPARE(arguments.at(0).toInt(), 2); + QCOMPARE(wizard.pageIds().size(), 3); + QVERIFY(!wizard.pageIds().contains(2)); + wizard.removePage(2); + QCOMPARE(spy.count(), 0); + QCOMPARE(wizard.pageIds().size(), 3); + QVERIFY(!wizard.pageIds().contains(2)); + + // Removing a page not in the history + wizard.setPage(2, page2); // restore + wizard.restart(); + wizard.next(); + QCOMPARE(wizard.visitedPages().size(), 2); + QCOMPARE(wizard.currentPage(), page1); + QCOMPARE(spy.count(), 0); + wizard.removePage(2); + QCOMPARE(spy.count(), 1); + arguments = spy.takeFirst(); + QCOMPARE(arguments.at(0).toInt(), 2); + QCOMPARE(wizard.visitedPages().size(), 2); + QVERIFY(!wizard.pageIds().contains(2)); + QCOMPARE(wizard.currentPage(), page1); + + // Removing a page in the history before the current page + wizard.setPage(2, page2); // restore + wizard.restart(); + wizard.next(); + QCOMPARE(spy.count(), 0); + QCOMPARE(wizard.visitedPages().size(), 2); + QCOMPARE(wizard.currentPage(), page1); + wizard.removePage(0); + QCOMPARE(spy.count(), 1); + arguments = spy.takeFirst(); + QCOMPARE(arguments.at(0).toInt(), 0); + QCOMPARE(wizard.visitedPages().size(), 1); + QVERIFY(!wizard.visitedPages().contains(0)); + QVERIFY(!wizard.pageIds().contains(0)); + QCOMPARE(wizard.currentPage(), page1); + + // Remove the current page which is not the first one in the history + wizard.setPage(0, page0); // restore + wizard.restart(); + wizard.next(); + QCOMPARE(spy.count(), 0); + QCOMPARE(wizard.visitedPages().size(), 2); + QCOMPARE(wizard.currentPage(), page1); + wizard.removePage(1); + QCOMPARE(spy.count(), 1); + arguments = spy.takeFirst(); + QCOMPARE(arguments.at(0).toInt(), 1); + QCOMPARE(wizard.visitedPages().size(), 1); + QVERIFY(!wizard.visitedPages().contains(1)); + QVERIFY(!wizard.pageIds().contains(1)); + QCOMPARE(wizard.currentPage(), page0); + + // Remove the current page which is the first (and only) one in the history + wizard.removePage(0); + QCOMPARE(spy.count(), 1); + arguments = spy.takeFirst(); + QCOMPARE(arguments.at(0).toInt(), 0); + QCOMPARE(wizard.visitedPages().size(), 1); + QVERIFY(!wizard.visitedPages().contains(0)); + QCOMPARE(wizard.pageIds().size(), 2); + QVERIFY(!wizard.pageIds().contains(0)); + QCOMPARE(wizard.currentPage(), page2); + // + wizard.removePage(2); + QCOMPARE(spy.count(), 1); + arguments = spy.takeFirst(); + QCOMPARE(arguments.at(0).toInt(), 2); + QCOMPARE(wizard.visitedPages().size(), 1); + QVERIFY(!wizard.visitedPages().contains(2)); + QCOMPARE(wizard.pageIds().size(), 1); + QVERIFY(!wizard.pageIds().contains(2)); + QCOMPARE(wizard.currentPage(), page3); + // + wizard.removePage(3); + QCOMPARE(spy.count(), 1); + arguments = spy.takeFirst(); + QCOMPARE(arguments.at(0).toInt(), 3); + QVERIFY(wizard.visitedPages().empty()); + QVERIFY(wizard.pageIds().empty()); + QCOMPARE(wizard.currentPage(), static_cast<QWizardPage *>(0)); +} + +void tst_QWizard::sideWidget() +{ + QWizard wizard; + + wizard.setSideWidget(0); + QVERIFY(wizard.sideWidget() == 0); + QWidget *w1 = new QWidget(&wizard); + wizard.setSideWidget(w1); + QVERIFY(wizard.sideWidget() == w1); + QWidget *w2 = new QWidget(&wizard); + wizard.setSideWidget(w2); + QVERIFY(wizard.sideWidget() == w2); + QVERIFY(w1->parent() != 0); + QCOMPARE(w1->window(), static_cast<QWidget *>(&wizard)); + QCOMPARE(w2->window(), static_cast<QWidget *>(&wizard)); + w1->setParent(0); + wizard.setSideWidget(0); + QVERIFY(wizard.sideWidget() == 0); +} + +void tst_QWizard::task161660_buttonSpacing() +{ +#ifndef QT_NO_STYLE_PLASTIQUE + QString origStyle = QApplication::style()->objectName(); + QApplication::setStyle(new QPlastiqueStyle); + QWizard wizard; + wizard.addPage(new QWizardPage); + wizard.show(); + const QAbstractButton *finishButton = wizard.button(QWizard::FinishButton); + const QAbstractButton *cancelButton = wizard.button(QWizard::CancelButton); + const int spacing = cancelButton->geometry().left() - finishButton->geometry().right() - 1; + QCOMPARE(spacing, wizard.style()->layoutSpacing( + QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal)); + QApplication::setStyle(origStyle); +#endif +} + +class task177716_CommitPage : public QWizardPage +{ + Q_OBJECT +public: + task177716_CommitPage() + { + setCommitPage(true); + QVBoxLayout *layout = new QVBoxLayout; + ledit = new QLineEdit(this); + registerField("foo*", ledit); + layout->addWidget(ledit); + setLayout(layout); + } + QLineEdit *ledit; +}; + +void tst_QWizard::task177716_disableCommitButton() +{ + QWizard wizard; + task177716_CommitPage *commitPage = new task177716_CommitPage; + wizard.addPage(commitPage); + // the following page must be there to prevent the first page from replacing the Commit button + // with the Finish button: + wizard.addPage(new QWizardPage); + wizard.show(); + QVERIFY(!wizard.button(QWizard::CommitButton)->isEnabled()); + commitPage->ledit->setText("some non-empty text"); + QVERIFY(wizard.button(QWizard::CommitButton)->isEnabled()); + commitPage->ledit->setText(""); + QVERIFY(!wizard.button(QWizard::CommitButton)->isEnabled()); +} + +class WizardPage_task183550 : public QWizardPage +{ +public: + WizardPage_task183550(QWidget *parent = 0) + : QWizardPage(parent) + , treeWidget(new QTreeWidget) + , verticalPolicy(QSizePolicy::MinimumExpanding) {} + void enableVerticalExpansion() { verticalPolicy = QSizePolicy::MinimumExpanding; } + void disableVerticalExpansion() { verticalPolicy = QSizePolicy::Preferred; } + int treeWidgetHeight() const { return treeWidget->height(); } + int treeWidgetSizeHintHeight() const { return treeWidget->sizeHint().height(); } + +private: + QTreeWidget *treeWidget; + QSizePolicy::Policy verticalPolicy; + + void initializePage() + { + if (layout()) + delete layout(); + if (treeWidget) + delete treeWidget; + + QLayout *layout_ = new QVBoxLayout(this); + layout_->addWidget(treeWidget = new QTreeWidget); + + QSizePolicy policy = sizePolicy(); + policy.setVerticalPolicy(verticalPolicy); + treeWidget->setSizePolicy(policy); + } +}; + +void tst_QWizard::task183550_stretchFactor() +{ + QWizard wizard; + WizardPage_task183550 *page1 = new WizardPage_task183550; + WizardPage_task183550 *page2 = new WizardPage_task183550; + wizard.addPage(page1); + wizard.addPage(page2); + wizard.resize(500, 2 * page2->treeWidgetSizeHintHeight()); + wizard.show(); + + QCOMPARE(wizard.currentPage(), static_cast<QWizardPage*>(page1)); + + // ---- + page2->disableVerticalExpansion(); + wizard.next(); + QCOMPARE(wizard.currentPage(), static_cast<QWizardPage*>(page2)); + QVERIFY(page2->treeWidgetHeight() == page2->treeWidgetSizeHintHeight()); + + wizard.back(); + QCOMPARE(wizard.currentPage(), static_cast<QWizardPage*>(page1)); + + // ---- + page2->enableVerticalExpansion(); + wizard.next(); + QCOMPARE(wizard.currentPage(), static_cast<QWizardPage*>(page2)); + QVERIFY(page2->treeWidgetHeight() > page2->treeWidgetSizeHintHeight()); + + wizard.back(); + QCOMPARE(wizard.currentPage(), static_cast<QWizardPage*>(page1)); + + // ---- + page2->disableVerticalExpansion(); + wizard.next(); + QCOMPARE(wizard.currentPage(), static_cast<QWizardPage*>(page2)); + QVERIFY(page2->treeWidgetHeight() == page2->treeWidgetSizeHintHeight()); +} + +void tst_QWizard::task161658_alignments() +{ + QWizard wizard; + wizard.setWizardStyle(QWizard::MacStyle); + + QWizardPage page; + page.setTitle("Title"); + page.setSubTitle("SUBTITLE#: The subtitle bust be alligned with the rest of the widget"); + + QLabel label1("Field:"); + QLineEdit lineEdit1; + QGridLayout *layout = new QGridLayout; + layout->addWidget(&label1, 0, 0); + layout->addWidget(&lineEdit1, 0, 1); + page.setLayout(layout); + + int idx = wizard.addPage(&page); + wizard.setStartId(idx); + wizard.show(); + QTest::qWait(100); + + foreach (QLabel *subtitleLabel, qFindChildren<QLabel *>(&wizard)) { + if (subtitleLabel->text().startsWith("SUBTITLE#")) { + QCOMPARE(lineEdit1.mapToGlobal(lineEdit1.contentsRect().bottomRight()).x(), + subtitleLabel->mapToGlobal(subtitleLabel->contentsRect().bottomRight()).x()); + return; + } + } + QFAIL("Subtitle label not found"); +} + +void tst_QWizard::task177022_setFixedSize() +{ + int width = 300; + int height = 200; + QWizard wiz; + QWizardPage page1; + QWizardPage page2; + int page1_id = wiz.addPage(&page1); + int page2_id = wiz.addPage(&page2); + wiz.setFixedSize(width, height); + if (wiz.wizardStyle() == QWizard::AeroStyle) + QEXPECT_FAIL("", "this probably relates to non-client area hack for AeroStyle titlebar " + "effect; not sure if it qualifies as a bug or not", Continue); + QCOMPARE(wiz.size(), QSize(width, height)); + QCOMPARE(wiz.minimumWidth(), width); + QCOMPARE(wiz.minimumHeight(), height); + QCOMPARE(wiz.maximumWidth(), width); + QCOMPARE(wiz.maximumHeight(), height); + + wiz.show(); + QTest::qWait(100); + QCOMPARE(wiz.size(), QSize(width, height)); + QCOMPARE(wiz.minimumWidth(), width); + QCOMPARE(wiz.minimumHeight(), height); + QCOMPARE(wiz.maximumWidth(), width); + QCOMPARE(wiz.maximumHeight(), height); + + wiz.next(); + QTest::qWait(100); + QCOMPARE(wiz.size(), QSize(width, height)); + QCOMPARE(wiz.minimumWidth(), width); + QCOMPARE(wiz.minimumHeight(), height); + QCOMPARE(wiz.maximumWidth(), width); + QCOMPARE(wiz.maximumHeight(), height); + + wiz.removePage(page1_id); + wiz.removePage(page2_id); +} + +void tst_QWizard::task248107_backButton() +{ + QWizard wizard; + QWizardPage page1; + QWizardPage page2; + QWizardPage page3; + QWizardPage page4; + wizard.addPage(&page1); + wizard.addPage(&page2); + wizard.addPage(&page3); + wizard.addPage(&page4); + + wizard.show(); + QTest::qWait(100); + QCOMPARE(wizard.currentPage(), &page1); + + QTest::mouseClick(wizard.button(QWizard::NextButton), Qt::LeftButton); + QCOMPARE(wizard.currentPage(), &page2); + + QTest::mouseClick(wizard.button(QWizard::NextButton), Qt::LeftButton); + QCOMPARE(wizard.currentPage(), &page3); + + QTest::mouseClick(wizard.button(QWizard::NextButton), Qt::LeftButton); + QCOMPARE(wizard.currentPage(), &page4); + + QTest::mouseClick(wizard.button(QWizard::BackButton), Qt::LeftButton); + QCOMPARE(wizard.currentPage(), &page3); + + QTest::mouseClick(wizard.button(QWizard::BackButton), Qt::LeftButton); + QCOMPARE(wizard.currentPage(), &page2); + + QTest::mouseClick(wizard.button(QWizard::BackButton), Qt::LeftButton); + QCOMPARE(wizard.currentPage(), &page1); +} + +class WizardPage_task255350 : public QWizardPage +{ +public: + QLineEdit *lineEdit; + WizardPage_task255350() + : lineEdit(new QLineEdit) + { + registerField("dummy*", lineEdit); + } +}; + +void tst_QWizard::task255350_fieldObjectDestroyed() +{ + QWizard wizard; + WizardPage_task255350 *page = new WizardPage_task255350; + int id = wizard.addPage(page); + delete page->lineEdit; + wizard.removePage(id); // don't crash! + delete page; +} + +QTEST_MAIN(tst_QWizard) +#include "tst_qwizard.moc" diff --git a/tests/auto/widgets/effects/effects.pro b/tests/auto/widgets/effects/effects.pro new file mode 100644 index 0000000000..fab24d6296 --- /dev/null +++ b/tests/auto/widgets/effects/effects.pro @@ -0,0 +1,4 @@ +TEMPLATE=subdirs +SUBDIRS=\ + qgraphicseffect \ + diff --git a/tests/auto/widgets/effects/qgraphicseffect/qgraphicseffect.pro b/tests/auto/widgets/effects/qgraphicseffect/qgraphicseffect.pro new file mode 100644 index 0000000000..171ab3ffda --- /dev/null +++ b/tests/auto/widgets/effects/qgraphicseffect/qgraphicseffect.pro @@ -0,0 +1,7 @@ +load(qttest_p4) + +QT += widgets widgets-private +QT += core-private gui-private + +SOURCES += tst_qgraphicseffect.cpp +CONFIG += parallel_test diff --git a/tests/auto/widgets/effects/qgraphicseffect/tst_qgraphicseffect.cpp b/tests/auto/widgets/effects/qgraphicseffect/tst_qgraphicseffect.cpp new file mode 100644 index 0000000000..2d9d87a2c5 --- /dev/null +++ b/tests/auto/widgets/effects/qgraphicseffect/tst_qgraphicseffect.cpp @@ -0,0 +1,760 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtTest/QtTest> +#include <QtWidgets/qdesktopwidget.h> +#include <QtWidgets/qgraphicseffect.h> +#include <QtWidgets/qgraphicsview.h> +#include <QtWidgets/qgraphicsscene.h> +#include <QtWidgets/qgraphicsitem.h> +#include <QtWidgets/qgraphicswidget.h> +#include <QtWidgets/qstyleoption.h> + +#include <private/qgraphicseffect_p.h> +#include "../../../platformquirks.h" + +//TESTED_CLASS= +//TESTED_FILES= + +class tst_QGraphicsEffect : public QObject +{ + Q_OBJECT +public slots: + void initTestCase(); + +private slots: + void setEnabled(); + void source(); + void boundingRectFor(); + void boundingRect(); + void boundingRect2(); + void draw(); + void opacity(); + void grayscale(); + void colorize(); + void drawPixmapItem(); + void deviceCoordinateTranslateCaching(); + void inheritOpacity(); + void dropShadowClipping(); + void childrenVisibilityShouldInvalidateCache(); + void prepareGeometryChangeInvalidateCache(); + void itemHasNoContents(); +}; + +void tst_QGraphicsEffect::initTestCase() +{} + +class CustomItem : public QGraphicsRectItem +{ +public: + CustomItem(qreal x, qreal y, qreal width, qreal height, QGraphicsItem *parent = 0) + : QGraphicsRectItem(x, y, width, height, parent), numRepaints(0), + m_painter(0), m_styleOption(0) + {} + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) + { + m_painter = painter; + m_styleOption = option; + ++numRepaints; + QGraphicsRectItem::paint(painter, option, widget); + } + + void reset() + { + numRepaints = 0; + m_painter = 0; + m_styleOption = 0; + } + + int numRepaints; + QPainter *m_painter; + const QStyleOption *m_styleOption; +}; + +class CustomEffect : public QGraphicsEffect +{ +public: + CustomEffect() + : QGraphicsEffect(), numRepaints(0), m_margin(10), + doNothingInDraw(false), m_painter(0), m_styleOption(0), m_source(0), m_opacity(1.0) + {} + + QRectF boundingRectFor(const QRectF &rect) const + { return rect.adjusted(-m_margin, -m_margin, m_margin, m_margin); } + + void reset() + { + numRepaints = 0; + m_sourceChangedFlags = QGraphicsEffect::ChangeFlags(); + m_painter = 0; + m_styleOption = 0; + m_source = 0; + m_opacity = 1.0; + } + + void setMargin(int margin) + { + m_margin = margin; + updateBoundingRect(); + } + + int margin() const + { return m_margin; } + + void draw(QPainter *painter) + { + ++numRepaints; + if (doNothingInDraw) + return; + m_source = source(); + m_painter = painter; + m_styleOption = source()->styleOption(); + m_opacity = painter->opacity(); + drawSource(painter); + } + + void sourceChanged(QGraphicsEffect::ChangeFlags flags) + { m_sourceChangedFlags |= flags; } + + int numRepaints; + int m_margin; + QGraphicsEffect::ChangeFlags m_sourceChangedFlags; + bool doNothingInDraw; + QPainter *m_painter; + const QStyleOption *m_styleOption; + QGraphicsEffectSource *m_source; + qreal m_opacity; +}; + +void tst_QGraphicsEffect::setEnabled() +{ + CustomEffect effect; + QVERIFY(effect.isEnabled()); + + effect.setEnabled(false); + QVERIFY(!effect.isEnabled()); +} + +void tst_QGraphicsEffect::source() +{ + QPointer<CustomEffect> effect = new CustomEffect; + QVERIFY(!effect->source()); + QVERIFY(!effect->m_sourceChangedFlags); + + // Install effect on QGraphicsItem. + QGraphicsItem *item = new QGraphicsRectItem(0, 0, 10, 10); + item->setGraphicsEffect(effect); + QVERIFY(effect->source()); + QCOMPARE(effect->source()->graphicsItem(), (const QGraphicsItem*)item); + QVERIFY(effect->m_sourceChangedFlags & QGraphicsEffect::SourceAttached); + effect->reset(); + + // Make sure disabling/enabling the effect doesn't change the source. + effect->setEnabled(false); + QVERIFY(effect->source()); + QCOMPARE(effect->source()->graphicsItem(), (const QGraphicsItem*)item); + QVERIFY(!effect->m_sourceChangedFlags); + effect->reset(); + + effect->setEnabled(true); + QVERIFY(effect->source()); + QCOMPARE(effect->source()->graphicsItem(), (const QGraphicsItem*)item); + QVERIFY(!effect->m_sourceChangedFlags); + effect->reset(); + + // Uninstall effect on QGraphicsItem. + effect->reset(); + item->setGraphicsEffect(0); + QVERIFY(!effect); + effect = new CustomEffect; + + // The item takes ownership and should delete the effect when destroyed. + item->setGraphicsEffect(effect); + QPointer<QGraphicsEffectSource> source = effect->source(); + QVERIFY(source); + QCOMPARE(source->graphicsItem(), (const QGraphicsItem*)item); + delete item; + QVERIFY(!effect); + QVERIFY(!source); +} + +void tst_QGraphicsEffect::boundingRectFor() +{ + CustomEffect effect; + int margin = effect.margin(); + const QRectF source(0, 0, 100, 100); + QCOMPARE(effect.boundingRectFor(source), source.adjusted(-margin, -margin, margin, margin)); + + effect.setMargin(margin = 20); + QCOMPARE(effect.boundingRectFor(source), source.adjusted(-margin, -margin, margin, margin)); +} + +void tst_QGraphicsEffect::boundingRect() +{ + // No source; empty bounding rect. + CustomEffect *effect = new CustomEffect; + QCOMPARE(effect->boundingRect(), QRectF()); + + // Install effect on QGraphicsItem. + QRectF itemRect(0, 0, 100, 100); + QGraphicsRectItem *item = new QGraphicsRectItem; + item->setRect(itemRect); + item->setGraphicsEffect(effect); + int margin = effect->margin(); + QCOMPARE(effect->boundingRect(), itemRect.adjusted(-margin, -margin, margin, margin)); + QCOMPARE(effect->boundingRect(), effect->boundingRectFor(itemRect)); + + // Make sure disabling/enabling the effect doesn't change the bounding rect. + effect->setEnabled(false); + QCOMPARE(effect->boundingRect(), itemRect.adjusted(-margin, -margin, margin, margin)); + QCOMPARE(effect->boundingRect(), effect->boundingRectFor(itemRect)); + effect->setEnabled(true); + QCOMPARE(effect->boundingRect(), itemRect.adjusted(-margin, -margin, margin, margin)); + QCOMPARE(effect->boundingRect(), effect->boundingRectFor(itemRect)); + + // Change effect margins. + effect->setMargin(margin = 20); + QCOMPARE(effect->boundingRect(), itemRect.adjusted(-margin, -margin, margin, margin)); + QCOMPARE(effect->boundingRect(), effect->boundingRectFor(itemRect)); + + // Uninstall effect on QGraphicsItem. + QPointer<CustomEffect> ptr = effect; + item->setGraphicsEffect(0); + QVERIFY(!ptr); + + delete item; +} + +void tst_QGraphicsEffect::boundingRect2() +{ + CustomEffect *effect = new CustomEffect; + QGraphicsRectItem *root = new QGraphicsRectItem; + root->setGraphicsEffect(effect); + + QGraphicsRectItem *child = new QGraphicsRectItem; + QRectF childRect(0, 0, 100, 100); + child->setFlag(QGraphicsItem::ItemClipsChildrenToShape); + child->setRect(childRect); + child->setParentItem(root); + + QGraphicsRectItem *grandChild = new QGraphicsRectItem; + QRectF grandChildRect(0, 0, 200, 200); + grandChild->setRect(grandChildRect); + grandChild->setParentItem(child); + + // Make sure the effect's bounding rect is clipped to the child's bounding rect. + QCOMPARE(effect->boundingRect(), effect->boundingRectFor(childRect)); + + // Disable ItemClipsChildrenToShape; effect's bounding rect is no longer clipped. + child->setFlag(QGraphicsItem::ItemClipsChildrenToShape, false); + QCOMPARE(effect->boundingRect(), effect->boundingRectFor(childRect | grandChildRect)); + + // Add root item to a scene, do the same tests as above. Results should be the same. + QGraphicsScene scene; + scene.addItem(root); + + child->setFlag(QGraphicsItem::ItemClipsChildrenToShape); + QCOMPARE(effect->boundingRect(), effect->boundingRectFor(childRect)); + + child->setFlag(QGraphicsItem::ItemClipsChildrenToShape, false); + QCOMPARE(effect->boundingRect(), effect->boundingRectFor(childRect | grandChildRect)); + + // Now add the scene to a view, results should be the same. + QGraphicsView view(&scene); + + child->setFlag(QGraphicsItem::ItemClipsChildrenToShape); + QCOMPARE(effect->boundingRect(), effect->boundingRectFor(childRect)); + + child->setFlag(QGraphicsItem::ItemClipsChildrenToShape, false); + QCOMPARE(effect->boundingRect(), effect->boundingRectFor(childRect | grandChildRect)); + + CustomEffect *childEffect = new CustomEffect; + child->setGraphicsEffect(childEffect); + QCOMPARE(effect->boundingRect(), effect->boundingRectFor(childEffect->boundingRectFor(childRect | grandChildRect))); + + child->setGraphicsEffect(0); + QCOMPARE(effect->boundingRect(), effect->boundingRectFor(childRect | grandChildRect)); +} + +void tst_QGraphicsEffect::draw() +{ + QGraphicsScene scene; + CustomItem *item = new CustomItem(0, 0, 100, 100); + scene.addItem(item); + + QGraphicsView view(&scene); + view.show(); + QTest::qWaitForWindowShown(&view); + QTRY_VERIFY(item->numRepaints > 0); + item->reset(); + + // Make sure installing the effect triggers a repaint. + CustomEffect *effect = new CustomEffect; + item->setGraphicsEffect(effect); + QTRY_COMPARE(effect->numRepaints, 1); + QTRY_COMPARE(item->numRepaints, 1); + + // Make sure QPainter* and QStyleOptionGraphicsItem* stays persistent + // during QGraphicsEffect::draw/QGraphicsItem::paint. + QVERIFY(effect->m_painter); + QCOMPARE(effect->m_painter, item->m_painter); + QCOMPARE(effect->m_styleOption, item->m_styleOption); + // Make sure QGraphicsEffect::source is persistent. + QCOMPARE(effect->m_source, effect->source()); + effect->reset(); + item->reset(); + + // Make sure updating the source triggers a repaint. + item->update(); + QTRY_COMPARE(effect->numRepaints, 1); + QTRY_COMPARE(item->numRepaints, 1); + QVERIFY(effect->m_sourceChangedFlags & QGraphicsEffect::SourceInvalidated); + effect->reset(); + item->reset(); + + // Make sure changing the effect's bounding rect triggers a repaint. + effect->setMargin(20); + QTRY_COMPARE(effect->numRepaints, 1); + QTRY_COMPARE(item->numRepaints, 1); + effect->reset(); + item->reset(); + + // Make sure change the item's bounding rect triggers a repaint. + item->setRect(0, 0, 50, 50); + QTRY_COMPARE(effect->numRepaints, 1); + QTRY_COMPARE(item->numRepaints, 1); + QVERIFY(effect->m_sourceChangedFlags & QGraphicsEffect::SourceBoundingRectChanged); + effect->reset(); + item->reset(); + + // Make sure the effect is the one to issue a repaint of the item. + effect->doNothingInDraw = true; + item->update(); + QTRY_COMPARE(effect->numRepaints, 1); + QCOMPARE(item->numRepaints, 0); + effect->doNothingInDraw = false; + effect->reset(); + item->reset(); + + // Make sure we update the source when disabling/enabling the effect. + effect->setEnabled(false); + QTest::qWait(50); + QCOMPARE(effect->numRepaints, 0); + QCOMPARE(item->numRepaints, 1); + effect->reset(); + item->reset(); + + effect->setEnabled(true); + QTRY_COMPARE(effect->numRepaints, 1); + QTRY_COMPARE(item->numRepaints, 1); + effect->reset(); + item->reset(); + + // Effect is already enabled; nothing should happen. + effect->setEnabled(true); + QTest::qWait(50); + QCOMPARE(effect->numRepaints, 0); + QCOMPARE(item->numRepaints, 0); + + // Make sure uninstalling an effect triggers a repaint. + QPointer<CustomEffect> ptr = effect; + item->setGraphicsEffect(0); + QVERIFY(!ptr); + QTRY_COMPARE(item->numRepaints, 1); +} + +void tst_QGraphicsEffect::opacity() +{ + // Make sure the painter's opacity is correct in QGraphicsEffect::draw. + QGraphicsScene scene; + CustomItem *item = new CustomItem(0, 0, 100, 100); + item->setOpacity(0.5); + CustomEffect *effect = new CustomEffect; + item->setGraphicsEffect(effect); + scene.addItem(item); + + QGraphicsView view(&scene); + view.show(); + QTest::qWaitForWindowShown(&view); + QTRY_VERIFY(effect->numRepaints > 0); + QCOMPARE(effect->m_opacity, qreal(0.5)); +} + +void tst_QGraphicsEffect::grayscale() +{ + if (qApp->desktop()->depth() < 24) + QSKIP("Test only works on 32 bit displays", SkipAll); + + QGraphicsScene scene(0, 0, 100, 100); + + QGraphicsRectItem *item = scene.addRect(0, 0, 50, 50); + item->setPen(Qt::NoPen); + item->setBrush(QColor(122, 193, 66)); // Qt light green + + QGraphicsColorizeEffect *effect = new QGraphicsColorizeEffect; + effect->setColor(Qt::black); + item->setGraphicsEffect(effect); + + QPainter painter; + QImage image(100, 100, QImage::Format_ARGB32_Premultiplied); + + image.fill(0); + painter.begin(&image); + painter.setRenderHint(QPainter::Antialiasing); + scene.render(&painter); + painter.end(); + + QCOMPARE(image.pixel(10, 10), qRgb(148, 148, 148)); + + effect->setStrength(0.5); + + image.fill(0); + painter.begin(&image); + painter.setRenderHint(QPainter::Antialiasing); + scene.render(&painter); + painter.end(); + + QCOMPARE(image.pixel(10, 10), qRgb(135, 171, 107)); + + effect->setStrength(0.0); + + image.fill(0); + painter.begin(&image); + painter.setRenderHint(QPainter::Antialiasing); + scene.render(&painter); + painter.end(); + + QCOMPARE(image.pixel(10, 10), qRgb(122, 193, 66)); +} + +void tst_QGraphicsEffect::colorize() +{ + if (qApp->desktop()->depth() < 24) + QSKIP("Test only works on 32 bit displays", SkipAll); + + QGraphicsScene scene(0, 0, 100, 100); + + QGraphicsRectItem *item = scene.addRect(0, 0, 50, 50); + item->setPen(Qt::NoPen); + item->setBrush(QColor(122, 193, 66)); // Qt light green + + QGraphicsColorizeEffect *effect = new QGraphicsColorizeEffect; + effect->setColor(QColor(102, 153, 51)); // Qt dark green + item->setGraphicsEffect(effect); + + QPainter painter; + QImage image(100, 100, QImage::Format_ARGB32_Premultiplied); + + image.fill(0); + painter.begin(&image); + painter.setRenderHint(QPainter::Antialiasing); + scene.render(&painter); + painter.end(); + + QCOMPARE(image.pixel(10, 10), qRgb(191, 212, 169)); + + effect->setStrength(0.5); + + image.fill(0); + painter.begin(&image); + painter.setRenderHint(QPainter::Antialiasing); + scene.render(&painter); + painter.end(); + + QCOMPARE(image.pixel(10, 10), qRgb(156, 203, 117)); + + effect->setStrength(0.0); + + image.fill(0); + painter.begin(&image); + painter.setRenderHint(QPainter::Antialiasing); + scene.render(&painter); + painter.end(); + + QCOMPARE(image.pixel(10, 10), qRgb(122, 193, 66)); +} + +class PixmapItemEffect : public QGraphicsEffect +{ +public: + PixmapItemEffect(const QPixmap &source) + : QGraphicsEffect() + , pixmap(source) + , repaints(0) + {} + + QRectF boundingRectFor(const QRectF &rect) const + { return rect; } + + void draw(QPainter *painter) + { + QVERIFY(sourcePixmap(Qt::LogicalCoordinates).handle() == pixmap.handle()); + QVERIFY((painter->worldTransform().type() <= QTransform::TxTranslate) == (sourcePixmap(Qt::DeviceCoordinates).handle() == pixmap.handle())); + + ++repaints; + } + QPixmap pixmap; + int repaints; +}; + +void tst_QGraphicsEffect::drawPixmapItem() +{ + QImage image(32, 32, QImage::Format_RGB32); + QPainter p(&image); + p.fillRect(0, 0, 32, 16, Qt::blue); + p.fillRect(0, 16, 32, 16, Qt::red); + p.end(); + + QGraphicsScene scene; + QGraphicsPixmapItem *item = new QGraphicsPixmapItem(QPixmap::fromImage(image)); + scene.addItem(item); + + PixmapItemEffect *effect = new PixmapItemEffect(item->pixmap()); + item->setGraphicsEffect(effect); + + QGraphicsView view(&scene); + view.show(); + QTest::qWaitForWindowShown(&view); + QTRY_VERIFY(effect->repaints >= 1); + + item->rotate(180); + + QTRY_VERIFY(effect->repaints >= 2); +} + +class DeviceEffect : public QGraphicsEffect +{ +public: + QRectF boundingRectFor(const QRectF &rect) const + { return rect; } + + void draw(QPainter *painter) + { + QPoint offset; + QPixmap pixmap = sourcePixmap(Qt::DeviceCoordinates, &offset, QGraphicsEffect::NoPad); + + if (pixmap.isNull()) + return; + + painter->save(); + painter->setWorldTransform(QTransform()); + painter->drawPixmap(offset, pixmap); + painter->restore(); + } +}; + +void tst_QGraphicsEffect::deviceCoordinateTranslateCaching() +{ + QGraphicsScene scene; + CustomItem *item = new CustomItem(0, 0, 10, 10); + scene.addItem(item); + scene.setSceneRect(0, 0, 50, 0); + + item->setGraphicsEffect(new DeviceEffect); + item->setPen(Qt::NoPen); + item->setBrush(Qt::red); + + QGraphicsView view(&scene); + view.show(); + QTest::qWaitForWindowShown(&view); + + QTRY_VERIFY(item->numRepaints >= 1); + int numRepaints = item->numRepaints; + + item->translate(10, 0); + + QTRY_VERIFY(item->numRepaints == numRepaints); +} + +void tst_QGraphicsEffect::inheritOpacity() +{ + QGraphicsScene scene; + QGraphicsRectItem *rectItem = new QGraphicsRectItem(0, 0, 10, 10); + CustomItem *item = new CustomItem(0, 0, 10, 10, rectItem); + + scene.addItem(rectItem); + + item->setGraphicsEffect(new DeviceEffect); + item->setPen(Qt::NoPen); + item->setBrush(Qt::red); + + rectItem->setOpacity(0.5); + + QGraphicsView view(&scene); + view.show(); + QTest::qWaitForWindowShown(&view); + + QTRY_VERIFY(item->numRepaints >= 1); + + int numRepaints = item->numRepaints; + + rectItem->setOpacity(1); + + // item should have been rerendered due to opacity changing + QTRY_VERIFY(item->numRepaints > numRepaints); +} + +void tst_QGraphicsEffect::dropShadowClipping() +{ + QImage img(128, 128, QImage::Format_ARGB32_Premultiplied); + img.fill(0xffffffff); + + QGraphicsScene scene; + QGraphicsRectItem *item = new QGraphicsRectItem(-5, -500, 10, 1000); + item->setGraphicsEffect(new QGraphicsDropShadowEffect); + item->setPen(Qt::NoPen); + item->setBrush(Qt::red); + + scene.addItem(item); + + QPainter p(&img); + scene.render(&p, img.rect(), QRect(-64, -64, 128, 128)); + p.end(); + + for (int y = 1; y < img.height(); ++y) + for (int x = 0; x < img.width(); ++x) + QCOMPARE(img.pixel(x, y), img.pixel(x, y-1)); +} + +class MyGraphicsItem : public QGraphicsWidget +{ +public: + MyGraphicsItem(QGraphicsItem *parent = 0) : + QGraphicsWidget(parent), nbPaint(0) + {} + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) + { + nbPaint++; + QGraphicsWidget::paint(painter, option, widget); + } + int nbPaint; +}; + +void tst_QGraphicsEffect::childrenVisibilityShouldInvalidateCache() +{ + QGraphicsScene scene; + MyGraphicsItem parent; + parent.resize(200, 200); + QGraphicsWidget child(&parent); + child.resize(200, 200); + child.setVisible(false); + scene.addItem(&parent); + QGraphicsView view(&scene); + view.show(); + QApplication::setActiveWindow(&view); + QTest::qWaitForWindowShown(&view); + QTRY_VERIFY(parent.nbPaint >= 1); + //we set an effect on the parent + parent.setGraphicsEffect(new QGraphicsDropShadowEffect(&parent)); + //flush the events + QApplication::processEvents(); + //new effect applied->repaint + QVERIFY(parent.nbPaint >= 2); + child.setVisible(true); + //flush the events + QApplication::processEvents(); + //a new child appears we need to redraw the effect. + QVERIFY(parent.nbPaint >= 3); +} + +void tst_QGraphicsEffect::prepareGeometryChangeInvalidateCache() +{ + MyGraphicsItem *item = new MyGraphicsItem; + item->resize(200, 200); + + QGraphicsScene scene; + scene.addItem(item); + + QGraphicsView view(&scene); + if(PlatformQuirks::isAutoMaximizing()) + view.showFullScreen(); + else + view.show(); + QTest::qWaitForWindowShown(&view); + QTRY_VERIFY(item->nbPaint >= 1); + + item->nbPaint = 0; + item->setGraphicsEffect(new QGraphicsDropShadowEffect); + QTRY_COMPARE(item->nbPaint, 1); + + item->nbPaint = 0; + item->resize(300, 300); + QTRY_COMPARE(item->nbPaint, 1); + + item->nbPaint = 0; + item->setPos(item->pos() + QPointF(10, 10)); + QTest::qWait(50); + QCOMPARE(item->nbPaint, 0); +} + +void tst_QGraphicsEffect::itemHasNoContents() +{ + QGraphicsRectItem *parent = new QGraphicsRectItem; + parent->setFlag(QGraphicsItem::ItemHasNoContents); + + MyGraphicsItem *child = new MyGraphicsItem; + child->setParentItem(parent); + child->resize(200, 200); + + QGraphicsScene scene; + scene.addItem(parent); + + QGraphicsView view(&scene); + view.show(); + QTest::qWaitForWindowShown(&view); + QTRY_VERIFY(child->nbPaint >= 1); + + CustomEffect *effect = new CustomEffect; + parent->setGraphicsEffect(effect); + QTRY_COMPARE(effect->numRepaints, 1); + + for (int i = 0; i < 3; ++i) { + effect->reset(); + effect->update(); + QTRY_COMPARE(effect->numRepaints, 1); + } +} + +QTEST_MAIN(tst_QGraphicsEffect) +#include "tst_qgraphicseffect.moc" + diff --git a/tests/auto/widgets/graphicsview/graphicsview.pro b/tests/auto/widgets/graphicsview/graphicsview.pro new file mode 100644 index 0000000000..9955e45b64 --- /dev/null +++ b/tests/auto/widgets/graphicsview/graphicsview.pro @@ -0,0 +1,32 @@ +TEMPLATE=subdirs +SUBDIRS=\ + qgraphicsanchorlayout \ + qgraphicsanchorlayout1 \ + qgraphicseffectsource \ + qgraphicsgridlayout \ + qgraphicsitem \ + qgraphicsitemanimation \ + qgraphicslayout \ + qgraphicslayoutitem \ + qgraphicslinearlayout \ + qgraphicsobject \ + qgraphicspixmapitem \ + qgraphicspolygonitem \ + qgraphicsproxywidget \ + qgraphicsscene \ + qgraphicssceneindex \ + qgraphicstransform \ + qgraphicsview \ + qgraphicswidget \ + +!contains(QT_CONFIG, private_tests): SUBDIRS -= \ + qgraphicsanchorlayout \ + qgraphicsanchorlayout1 \ + qgraphicsitem \ + qgraphicsscene \ + qgraphicssceneindex \ + +# These tests require the cleanlooks style +!contains(styles, cleanlooks):SUBDIRS -= \ + qgraphicsproxywidget \ + qgraphicswidget \ diff --git a/tests/auto/widgets/graphicsview/qgraphicsanchorlayout/qgraphicsanchorlayout.pro b/tests/auto/widgets/graphicsview/qgraphicsanchorlayout/qgraphicsanchorlayout.pro new file mode 100644 index 0000000000..5aa2936e3e --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsanchorlayout/qgraphicsanchorlayout.pro @@ -0,0 +1,5 @@ +load(qttest_p4) +QT += widgets widgets-private +QT += core-private gui-private +SOURCES += tst_qgraphicsanchorlayout.cpp +CONFIG += parallel_test diff --git a/tests/auto/widgets/graphicsview/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp b/tests/auto/widgets/graphicsview/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp new file mode 100644 index 0000000000..5dbe501ea8 --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp @@ -0,0 +1,2091 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> +#include <QtWidgets/qgraphicsanchorlayout.h> +#include <private/qgraphicsanchorlayout_p.h> +#include <QtWidgets/qgraphicswidget.h> +#include <QtWidgets/qgraphicsproxywidget.h> +#include <QtWidgets/qgraphicsview.h> +#include <QtWidgets/qwindowsstyle.h> + + +class tst_QGraphicsAnchorLayout : public QObject { + Q_OBJECT + +public: + tst_QGraphicsAnchorLayout() : QObject() { + hasSimplification = qgetenv("QT_ANCHORLAYOUT_NO_SIMPLIFICATION").isEmpty(); + } + +private: + bool hasSimplification; + +private slots: + void simple(); + void simple_center(); + void simple_semifloat(); + void layoutDirection(); + void diagonal(); + void parallel(); + void parallel2(); + void snake(); + void snakeOppositeDirections(); + void fairDistribution(); + void fairDistributionOppositeDirections(); + void proportionalPreferred(); + void example(); + void setSpacing(); + void styleDefaults(); + void hardComplexS60(); + void stability(); + void delete_anchor(); + void conflicts(); + void sizePolicy(); + void floatConflict(); + void infiniteMaxSizes(); + void simplifiableUnfeasible(); + void simplificationVsOrder(); + void parallelSimplificationOfCenter(); + void simplificationVsRedundance(); + void spacingPersistency(); + void snakeParallelWithLayout(); + void parallelToHalfLayout(); + void globalSpacing(); + void graphicsAnchorHandling(); + void invalidHierarchyCheck(); +}; + +class RectWidget : public QGraphicsWidget +{ +public: + RectWidget(QGraphicsItem *parent = 0) : QGraphicsWidget(parent){} + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) + { + Q_UNUSED(option); + Q_UNUSED(widget); + painter->drawRoundRect(rect()); + painter->drawLine(rect().topLeft(), rect().bottomRight()); + painter->drawLine(rect().bottomLeft(), rect().topRight()); + } +}; + +static QGraphicsWidget *createItem(const QSizeF &minimum = QSizeF(100.0, 100.0), + const QSizeF &preferred = QSize(150.0, 100.0), + const QSizeF &maximum = QSizeF(200.0, 100.0), + const QString &name = QString()) +{ + QGraphicsWidget *w = new RectWidget; + w->setMinimumSize(minimum); + w->setPreferredSize(preferred); + w->setMaximumSize(maximum); + w->setData(0, name); + return w; +} + +static void setAnchor(QGraphicsAnchorLayout *l, + QGraphicsLayoutItem *firstItem, + Qt::AnchorPoint firstEdge, + QGraphicsLayoutItem *secondItem, + Qt::AnchorPoint secondEdge, + qreal spacing = 0) +{ + QGraphicsAnchor *anchor = l->addAnchor(firstItem, firstEdge, secondItem, secondEdge); + anchor->setSpacing(spacing); +} + +static bool checkReverseDirection(QGraphicsWidget *widget) +{ + QGraphicsLayout *layout = widget->layout(); + qreal left, top, right, bottom; + layout->getContentsMargins(&left, &top, &right, &bottom); + widget->setLayoutDirection(Qt::LeftToRight); + QApplication::processEvents(); + const QRectF layoutGeometry = layout->geometry(); + QMap<QGraphicsLayoutItem *, QRectF> geometries; + for (int i = 0; i < layout->count(); ++i) { + QGraphicsLayoutItem *item = layout->itemAt(i); + geometries.insert(item, item->geometry()); + } + widget->setLayoutDirection(Qt::RightToLeft); + QApplication::processEvents(); + layoutGeometry.adjusted(+right, +top, -left, -bottom); + for (int i = 0; i < layout->count(); ++i) { + QGraphicsLayoutItem *item = layout->itemAt(i); + const QRectF rightToLeftGeometry = item->geometry(); + const QRectF leftToRightGeometry = geometries.value(item); + QRectF expectedGeometry = leftToRightGeometry; + expectedGeometry.moveRight(layoutGeometry.right() - leftToRightGeometry.left()); + if (expectedGeometry != rightToLeftGeometry) { + qDebug() << "layout->geometry():" << layoutGeometry + << "expected:" << expectedGeometry + << "actual:" << rightToLeftGeometry; + return false; + } + } + return true; +} + +static bool layoutHasConflict(QGraphicsAnchorLayout *l) +{ + return QGraphicsAnchorLayoutPrivate::get(l)->hasConflicts(); +} + +static bool usedSimplex(QGraphicsAnchorLayout *l, Qt::Orientation o) +{ + QGraphicsAnchorLayoutPrivate::Orientation oo = (o == Qt::Horizontal) ? + QGraphicsAnchorLayoutPrivate::Horizontal : + QGraphicsAnchorLayoutPrivate::Vertical; + + return QGraphicsAnchorLayoutPrivate::get(l)->lastCalculationUsedSimplex[oo]; +} + +void tst_QGraphicsAnchorLayout::simple() +{ + QGraphicsWidget *w1 = createItem(); + QGraphicsWidget *w2 = createItem(); + + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + l->setContentsMargins(0, 0, 0, 0); + + // Horizontal + l->addAnchor(l, Qt::AnchorLeft, w1, Qt::AnchorLeft); + l->addAnchor(w1, Qt::AnchorRight, w2, Qt::AnchorLeft); + l->addAnchor(w2, Qt::AnchorRight, l, Qt::AnchorRight); + + // Vertical + l->addAnchors(l, w1, Qt::Vertical); + l->addAnchors(l, w2, Qt::Vertical); + + QCOMPARE(l->count(), 2); + + QGraphicsWidget p; + p.setLayout(l); + p.adjustSize(); + + if (hasSimplification) { + QVERIFY(!usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); + } +} + +void tst_QGraphicsAnchorLayout::simple_center() +{ + QSizeF minSize(10, 10); + QSizeF pref(50, 10); + QSizeF maxSize(100, 10); + + QGraphicsWidget *a = createItem(minSize, pref, maxSize, "a"); + QGraphicsWidget *b = createItem(minSize, pref, maxSize, "b"); + QGraphicsWidget *c = createItem(minSize, pref, maxSize, "c"); + + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + l->setContentsMargins(0, 0, 0, 0); + // horizontal + setAnchor(l, l, Qt::AnchorLeft, a, Qt::AnchorLeft, 0); + setAnchor(l, a, Qt::AnchorRight, b, Qt::AnchorLeft, 0); + setAnchor(l, b, Qt::AnchorRight, l, Qt::AnchorRight, 0); + setAnchor(l, a, Qt::AnchorHorizontalCenter, c, Qt::AnchorLeft, 0); + setAnchor(l, c, Qt::AnchorRight, b, Qt::AnchorHorizontalCenter, 0); + + // vertical + setAnchor(l, l, Qt::AnchorTop, a, Qt::AnchorTop, 0); + setAnchor(l, l, Qt::AnchorTop, b, Qt::AnchorTop, 0); + setAnchor(l, a, Qt::AnchorBottom, c, Qt::AnchorTop, 0); + setAnchor(l, b, Qt::AnchorBottom, c, Qt::AnchorTop, 0); + setAnchor(l, c, Qt::AnchorBottom, l, Qt::AnchorBottom, 0); + + QCOMPARE(l->count(), 3); + + QGraphicsWidget *p = new QGraphicsWidget(0, Qt::Window); + p->setLayout(l); + + QSizeF layoutMinimumSize = l->effectiveSizeHint(Qt::MinimumSize); + QCOMPARE(layoutMinimumSize, QSizeF(20, 20)); + + QSizeF layoutMaximumSize = l->effectiveSizeHint(Qt::MaximumSize); + QCOMPARE(layoutMaximumSize, QSizeF(200, 20)); + + if (hasSimplification) { + QVERIFY(usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); + } + + delete p; +} + +void tst_QGraphicsAnchorLayout::simple_semifloat() +{ + // Useful for testing simplification between A_left and B_left. + // Unfortunately the only way to really test that now is to manually inspect the + // simplified graph. + QSizeF minSize(10, 10); + QSizeF pref(50, 10); + QSizeF maxSize(100, 10); + + QGraphicsWidget *A = createItem(minSize, pref, maxSize, "A"); + QGraphicsWidget *B = createItem(minSize, pref, maxSize, "B"); + QGraphicsWidget *a = createItem(minSize, pref, maxSize, "a"); + QGraphicsWidget *b = createItem(minSize, pref, maxSize, "b"); + + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + l->setContentsMargins(0, 0, 0, 0); + + // horizontal + setAnchor(l, l, Qt::AnchorLeft, A, Qt::AnchorLeft, 0); + setAnchor(l, A, Qt::AnchorRight, B, Qt::AnchorLeft, 0); + setAnchor(l, B, Qt::AnchorRight, l, Qt::AnchorRight, 0); + + setAnchor(l, A, Qt::AnchorLeft, a, Qt::AnchorLeft, 0); + setAnchor(l, B, Qt::AnchorLeft, b, Qt::AnchorLeft, 0); + + // vertical + setAnchor(l, l, Qt::AnchorTop, A, Qt::AnchorTop, 0); + setAnchor(l, l, Qt::AnchorTop, B, Qt::AnchorTop, 0); + setAnchor(l, A, Qt::AnchorBottom, a, Qt::AnchorTop, 0); + setAnchor(l, B, Qt::AnchorBottom, b, Qt::AnchorTop, 0); + setAnchor(l, a, Qt::AnchorBottom, l, Qt::AnchorBottom, 0); + setAnchor(l, b, Qt::AnchorBottom, l, Qt::AnchorBottom, 0); + + QCOMPARE(l->count(), 4); + + QGraphicsWidget *p = new QGraphicsWidget(0, Qt::Window); + p->setLayout(l); + + QSizeF layoutMinimumSize = l->effectiveSizeHint(Qt::MinimumSize); + QCOMPARE(layoutMinimumSize, QSizeF(20, 20)); + + QCOMPARE(l->effectiveSizeHint(Qt::PreferredSize), QSizeF(100, 20)); + + QSizeF layoutMaximumSize = l->effectiveSizeHint(Qt::MaximumSize); + QCOMPARE(layoutMaximumSize, QSizeF(200, 20)); + + delete p; +} + +void tst_QGraphicsAnchorLayout::layoutDirection() +{ + QSizeF minSize(10, 10); + QSizeF pref(50, 10); + QSizeF maxSize(100, 10); + + QGraphicsWidget *a = createItem(minSize, pref, maxSize, "a"); + QGraphicsWidget *b = createItem(minSize, pref, maxSize, "b"); + QGraphicsWidget *c = createItem(minSize, pref, QSizeF(100, 20), "c"); + + a->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + b->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + c->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + + + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + l->setContentsMargins(0, 5, 10, 15); + // horizontal + setAnchor(l, l, Qt::AnchorLeft, a, Qt::AnchorLeft, 0); + setAnchor(l, a, Qt::AnchorRight, b, Qt::AnchorLeft, 0); + setAnchor(l, b, Qt::AnchorRight, l, Qt::AnchorRight, 0); + setAnchor(l, a, Qt::AnchorHorizontalCenter, c, Qt::AnchorLeft, 0); + setAnchor(l, c, Qt::AnchorRight, b, Qt::AnchorHorizontalCenter, 0); + + // vertical + setAnchor(l, l, Qt::AnchorTop, a, Qt::AnchorTop, 0); + setAnchor(l, l, Qt::AnchorTop, b, Qt::AnchorTop, 0); + setAnchor(l, a, Qt::AnchorBottom, c, Qt::AnchorTop, 0); + setAnchor(l, b, Qt::AnchorBottom, c, Qt::AnchorTop, 0); + setAnchor(l, c, Qt::AnchorBottom, l, Qt::AnchorBottom, 0); + + QCOMPARE(l->count(), 3); + + QGraphicsWidget *p = new QGraphicsWidget(0, Qt::Window); + p->setLayoutDirection(Qt::LeftToRight); + p->setLayout(l); + + QGraphicsScene scene; + QGraphicsView *view = new QGraphicsView(&scene); + scene.addItem(p); + p->show(); + view->show(); + + QVERIFY(p->layout()); + QCOMPARE(checkReverseDirection(p), true); + + if (hasSimplification) { + QVERIFY(usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); + } + + delete p; + delete view; +} + +void tst_QGraphicsAnchorLayout::diagonal() +{ + QSizeF minSize(10, 100); + QSizeF pref(70, 100); + QSizeF maxSize(100, 100); + + QGraphicsWidget *a = createItem(minSize, pref, maxSize, "A"); + QGraphicsWidget *b = createItem(minSize, pref, maxSize, "B"); + QGraphicsWidget *c = createItem(minSize, pref, maxSize, "C"); + QGraphicsWidget *d = createItem(minSize, pref, maxSize, "D"); + QGraphicsWidget *e = createItem(minSize, pref, maxSize, "E"); + + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + l->setContentsMargins(0, 0, 0, 0); + l->setSpacing(0); + + // vertical + l->addAnchor(a, Qt::AnchorTop, l, Qt::AnchorTop); + l->addAnchor(b, Qt::AnchorTop, l, Qt::AnchorTop); + l->addAnchor(c, Qt::AnchorTop, a, Qt::AnchorBottom); + l->addAnchor(c, Qt::AnchorTop, b, Qt::AnchorBottom); + l->addAnchor(c, Qt::AnchorBottom, d, Qt::AnchorTop); + l->addAnchor(c, Qt::AnchorBottom, e, Qt::AnchorTop); + l->addAnchor(d, Qt::AnchorBottom, l, Qt::AnchorBottom); + l->addAnchor(e, Qt::AnchorBottom, l, Qt::AnchorBottom); + + // horizontal + l->addAnchor(l, Qt::AnchorLeft, a, Qt::AnchorLeft); + l->addAnchor(l, Qt::AnchorLeft, d, Qt::AnchorLeft); + l->addAnchor(a, Qt::AnchorRight, b, Qt::AnchorLeft); + + l->addAnchor(a, Qt::AnchorRight, c, Qt::AnchorLeft); + l->addAnchor(c, Qt::AnchorRight, e, Qt::AnchorLeft); + + l->addAnchor(b, Qt::AnchorRight, l, Qt::AnchorRight); + l->addAnchor(e, Qt::AnchorRight, l, Qt::AnchorRight); + l->addAnchor(d, Qt::AnchorRight, e, Qt::AnchorLeft); + + QCOMPARE(l->count(), 5); + + QGraphicsWidget p; + p.setLayout(l); + + QSizeF layoutMinimumSize = l->effectiveSizeHint(Qt::MinimumSize); + QSizeF layoutMaximumSize = l->effectiveSizeHint(Qt::MaximumSize); + QSizeF layoutPreferredSize = l->effectiveSizeHint(Qt::PreferredSize); + + QCOMPARE(layoutMinimumSize, QSizeF(30.0, 300.0)); + QCOMPARE(layoutPreferredSize, QSizeF(170.0, 300.0)); + QCOMPARE(layoutMaximumSize, QSizeF(190.0, 300.0)); + + p.resize(layoutMinimumSize); + QCOMPARE(a->geometry(), QRectF(0.0, 0.0, 10.0, 100.0)); + QCOMPARE(b->geometry(), QRectF(10.0, 0.0, 20.0, 100.0)); + QCOMPARE(c->geometry(), QRectF(10.0, 100.0, 10.0, 100.0)); + QCOMPARE(d->geometry(), QRectF(0.0, 200.0, 20.0, 100.0)); + QCOMPARE(e->geometry(), QRectF(20.0, 200.0, 10.0, 100.0)); + QCOMPARE(p.size(), layoutMinimumSize); + + p.resize(layoutPreferredSize); + QCOMPARE(a->geometry(), QRectF(0.0, 0.0, 70.0, 100.0)); + QCOMPARE(b->geometry(), QRectF(70.0, 0.0, 100.0, 100.0)); + QCOMPARE(c->geometry(), QRectF(70.0, 100.0, 30.0, 100.0)); + QCOMPARE(d->geometry(), QRectF(0.0, 200.0, 100.0, 100.0)); + QCOMPARE(e->geometry(), QRectF(100.0, 200.0, 70.0, 100.0)); + QCOMPARE(p.size(), layoutPreferredSize); + + p.resize(layoutMaximumSize); + QCOMPARE(a->geometry(), QRectF(0.0, 0.0, 90.0, 100.0)); + QCOMPARE(b->geometry(), QRectF(90.0, 0.0, 100.0, 100.0)); + QCOMPARE(c->geometry(), QRectF(90.0, 100.0, 10.0, 100.0)); + QCOMPARE(d->geometry(), QRectF(0.0, 200.0, 100.0, 100.0)); + QCOMPARE(e->geometry(), QRectF(100.0, 200.0, 90.0, 100.0)); + QCOMPARE(p.size(), layoutMaximumSize); + + QSizeF testA(175.0, 300.0); + p.resize(testA); + QCOMPARE(a->geometry(), QRectF(0.0, 0.0, 75.0, 100.0)); + QCOMPARE(b->geometry(), QRectF(75.0, 0.0, 100.0, 100.0)); + QCOMPARE(c->geometry(), QRectF(75.0, 100.0, 25.0, 100.0)); + QCOMPARE(d->geometry(), QRectF(0.0, 200.0, 100.0, 100.0)); + QCOMPARE(e->geometry(), QRectF(100.0, 200.0, 75.0, 100.0)); + QCOMPARE(p.size(), testA); + + if (hasSimplification) { + QVERIFY(usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); + } + + QVERIFY(p.layout()); + QCOMPARE(checkReverseDirection(&p), true); + + c->setMinimumWidth(300); + QVERIFY(layoutHasConflict(l)); +} + +void tst_QGraphicsAnchorLayout::parallel() +{ + QGraphicsWidget *a = createItem(QSizeF(100, 100), + QSizeF(150, 100), + QSizeF(200, 100), "A"); + + QGraphicsWidget *b = createItem(QSizeF(100, 100), + QSizeF(150, 100), + QSizeF(300, 100), "B"); + + QGraphicsWidget *c = createItem(QSizeF(100, 100), + QSizeF(200, 100), + QSizeF(350, 100), "C"); + + QGraphicsWidget *d = createItem(QSizeF(100, 100), + QSizeF(170, 100), + QSizeF(200, 100), "D"); + + QGraphicsWidget *e = createItem(QSizeF(150, 100), + QSizeF(150, 100), + QSizeF(200, 100), "E"); + + QGraphicsWidget *f = createItem(QSizeF(100, 100), + QSizeF(150, 100), + QSizeF(200, 100), "F"); + + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + l->setContentsMargins(0, 0, 0, 0); + l->setSpacing(0); + + l->addAnchor(l, Qt::AnchorTop, a, Qt::AnchorTop); + l->addAnchor(a, Qt::AnchorBottom, b, Qt::AnchorTop); + l->addAnchor(b, Qt::AnchorBottom, c, Qt::AnchorTop); + l->addAnchor(c, Qt::AnchorBottom, d, Qt::AnchorTop); + l->addAnchor(d, Qt::AnchorBottom, e, Qt::AnchorTop); + l->addAnchor(e, Qt::AnchorBottom, f, Qt::AnchorTop); + l->addAnchor(f, Qt::AnchorBottom, l, Qt::AnchorBottom); + + l->addAnchor(l, Qt::AnchorLeft, a, Qt::AnchorLeft); + l->addAnchor(a, Qt::AnchorRight, b, Qt::AnchorLeft); + l->addAnchor(a, Qt::AnchorRight, c, Qt::AnchorLeft); + l->addAnchor(b, Qt::AnchorRight, d, Qt::AnchorLeft); + l->addAnchor(b, Qt::AnchorRight, e, Qt::AnchorLeft); + l->addAnchor(c, Qt::AnchorRight, f, Qt::AnchorLeft); + l->addAnchor(d, Qt::AnchorRight, f, Qt::AnchorLeft); + l->addAnchor(e, Qt::AnchorRight, f, Qt::AnchorLeft); + l->addAnchor(f, Qt::AnchorRight, l, Qt::AnchorRight); + + QCOMPARE(l->count(), 6); + + QGraphicsWidget p; + p.setLayout(l); + + QSizeF layoutMinimumSize = l->effectiveSizeHint(Qt::MinimumSize); + QSizeF layoutPreferredSize = l->effectiveSizeHint(Qt::PreferredSize); + QSizeF layoutMaximumSize = l->effectiveSizeHint(Qt::MaximumSize); + + QCOMPARE(layoutMinimumSize, QSizeF(450, 600)); + QCOMPARE(layoutPreferredSize, QSizeF(620, 600)); + QCOMPARE(layoutMaximumSize, QSizeF(750, 600)); + + p.resize(layoutMinimumSize); + QCOMPARE(a->geometry(), QRectF(0, 0, 100, 100)); + QCOMPARE(b->geometry(), QRectF(100, 100, 100, 100)); + QCOMPARE(c->geometry(), QRectF(100, 200, 250, 100)); + QCOMPARE(d->geometry(), QRectF(200, 300, 150, 100)); + QCOMPARE(e->geometry(), QRectF(200, 400, 150, 100)); + QCOMPARE(f->geometry(), QRectF(350, 500, 100, 100)); + QCOMPARE(p.size(), layoutMinimumSize); + + if (!hasSimplification) + return; + + p.resize(layoutPreferredSize); + QCOMPARE(a->geometry(), QRectF(0, 0, 150, 100)); + QCOMPARE(b->geometry(), QRectF(150, 100, 150, 100)); + QCOMPARE(c->geometry(), QRectF(150, 200, 320, 100)); + QCOMPARE(d->geometry(), QRectF(300, 300, 170, 100)); + QCOMPARE(e->geometry(), QRectF(300, 400, 170, 100)); + QCOMPARE(f->geometry(), QRectF(470, 500, 150, 100)); + QCOMPARE(p.size(), layoutPreferredSize); + + // Maximum size depends on simplification / fair distribution + // Without that, test may or may not pass, depending on the + // solution found by the solver at runtime. + p.resize(layoutMaximumSize); + QCOMPARE(a->geometry(), QRectF(0, 0, 200, 100)); + QCOMPARE(b->geometry(), QRectF(200, 100, 175, 100)); + QCOMPARE(c->geometry(), QRectF(200, 200, 350, 100)); + QCOMPARE(d->geometry(), QRectF(375, 300, 175, 100)); + QCOMPARE(e->geometry(), QRectF(375, 400, 175, 100)); + QCOMPARE(f->geometry(), QRectF(550, 500, 200, 100)); + QCOMPARE(p.size(), layoutMaximumSize); + + QVERIFY(!usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); +} + +void tst_QGraphicsAnchorLayout::parallel2() +{ + QGraphicsWidget *a = createItem(QSizeF(70.0, 100.0), + QSizeF(100.0, 100.0), + QSizeF(200.0, 100.0), "A"); + + QGraphicsWidget *b = createItem(QSizeF(100.0, 100.0), + QSizeF(150.0, 100.0), + QSizeF(190.0, 100.0), "B"); + + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + l->setContentsMargins(0, 0, 0, 0); + l->setSpacing(0); + + l->addAnchor(l, Qt::AnchorTop, a, Qt::AnchorTop); + l->addAnchor(a, Qt::AnchorBottom, b, Qt::AnchorTop); + l->addAnchor(b, Qt::AnchorBottom, l, Qt::AnchorBottom); + + l->addAnchors(l, a, Qt::Horizontal); + l->addAnchor(l, Qt::AnchorLeft, b, Qt::AnchorLeft); + l->addAnchor(b, Qt::AnchorRight, a, Qt::AnchorRight); + + QCOMPARE(l->count(), 2); + + QGraphicsWidget p; + p.setLayout(l); + + QSizeF layoutMinimumSize = l->effectiveSizeHint(Qt::MinimumSize); + QSizeF layoutPreferredSize = l->effectiveSizeHint(Qt::PreferredSize); + QSizeF layoutMaximumSize = l->effectiveSizeHint(Qt::MaximumSize); + + QCOMPARE(layoutMinimumSize, QSizeF(100.0, 200.0)); + QCOMPARE(layoutPreferredSize, QSizeF(150.0, 200.0)); + QCOMPARE(layoutMaximumSize, QSizeF(190.0, 200.0)); + + p.resize(layoutMinimumSize); + QCOMPARE(p.size(), layoutMinimumSize); + + p.resize(layoutPreferredSize); + QCOMPARE(p.size(), layoutPreferredSize); + + p.resize(layoutMaximumSize); + QCOMPARE(p.size(), layoutMaximumSize); + + if (hasSimplification) { + QVERIFY(!usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); + } +} + +void tst_QGraphicsAnchorLayout::snake() +{ + QGraphicsWidget *a = createItem(QSizeF(50.0, 100.0), + QSizeF(70.0, 100.0), + QSizeF(100.0, 100.0), "A"); + + QGraphicsWidget *b = createItem(QSizeF(10.0, 100.0), + QSizeF(20.0, 100.0), + QSizeF(40.0, 100.0), "B"); + + QGraphicsWidget *c = createItem(QSizeF(50.0, 100.0), + QSizeF(70.0, 100.0), + QSizeF(100.0, 100.0), "C"); + + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + l->setContentsMargins(0, 0, 0, 0); + l->setSpacing(0); + + l->addAnchor(l, Qt::AnchorTop, a, Qt::AnchorTop); + l->addAnchor(a, Qt::AnchorBottom, b, Qt::AnchorTop); + l->addAnchor(b, Qt::AnchorBottom, c, Qt::AnchorTop); + l->addAnchor(c, Qt::AnchorBottom, l, Qt::AnchorBottom); + + l->addAnchor(l, Qt::AnchorLeft, a, Qt::AnchorLeft); + l->addAnchor(a, Qt::AnchorRight, b, Qt::AnchorRight); + l->addAnchor(b, Qt::AnchorLeft, c, Qt::AnchorLeft); + l->addAnchor(c, Qt::AnchorRight, l, Qt::AnchorRight); + + QCOMPARE(l->count(), 3); + + QGraphicsWidget p; + p.setLayout(l); + + QSizeF layoutMinimumSize = l->effectiveSizeHint(Qt::MinimumSize); + QSizeF layoutMaximumSize = l->effectiveSizeHint(Qt::MaximumSize); + QSizeF layoutPreferredSize = l->effectiveSizeHint(Qt::PreferredSize); + + QCOMPARE(layoutMinimumSize, QSizeF(60.0, 300.0)); + QCOMPARE(layoutPreferredSize, QSizeF(120.0, 300.0)); + QCOMPARE(layoutMaximumSize, QSizeF(190.0, 300.0)); + + p.resize(layoutMinimumSize); + QCOMPARE(a->geometry(), QRectF(0.0, 0.0, 50.0, 100.0)); + QCOMPARE(b->geometry(), QRectF(10.0, 100.0, 40.0, 100.0)); + QCOMPARE(c->geometry(), QRectF(10.0, 200.0, 50.0, 100.0)); + QCOMPARE(p.size(), layoutMinimumSize); + + p.resize(layoutPreferredSize); + QCOMPARE(a->geometry(), QRectF(0.0, 0.0, 70.0, 100.0)); + QCOMPARE(b->geometry(), QRectF(50.0, 100.0, 20.0, 100.0)); + QCOMPARE(c->geometry(), QRectF(50.0, 200.0, 70.0, 100.0)); + QCOMPARE(p.size(), layoutPreferredSize); + + p.resize(layoutMaximumSize); + QCOMPARE(a->geometry(), QRectF(0.0, 0.0, 100.0, 100.0)); + QCOMPARE(b->geometry(), QRectF(90.0, 100.0, 10.0, 100.0)); + QCOMPARE(c->geometry(), QRectF(90.0, 200.0, 100.0, 100.0)); + QCOMPARE(p.size(), layoutMaximumSize); + + QVERIFY(layoutHasConflict(l) == false); + + // Test QSizePolicy::ExpandFlag, it shouldn't change the extreme + // points of the layout... + b->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + + QSizeF newLayoutMinimumSize = l->effectiveSizeHint(Qt::MinimumSize); + QSizeF newLayoutMaximumSize = l->effectiveSizeHint(Qt::MaximumSize); + QSizeF newLayoutPreferredSize = l->effectiveSizeHint(Qt::PreferredSize); + + QCOMPARE(layoutMinimumSize, newLayoutMinimumSize); + QCOMPARE(layoutMaximumSize, newLayoutMaximumSize); + QCOMPARE(layoutPreferredSize, newLayoutPreferredSize); +} + +void tst_QGraphicsAnchorLayout::snakeOppositeDirections() +{ + QGraphicsWidget *a = createItem(QSizeF(50.0, 100.0), + QSizeF(70.0, 100.0), + QSizeF(100.0, 100.0), "A"); + + QGraphicsWidget *b = createItem(QSizeF(10.0, 100.0), + QSizeF(20.0, 100.0), + QSizeF(40.0, 100.0), "B"); + + QGraphicsWidget *c = createItem(QSizeF(50.0, 100.0), + QSizeF(70.0, 100.0), + QSizeF(100.0, 100.0), "C"); + + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + l->setContentsMargins(0, 0, 0, 0); + l->setSpacing(0); + + l->addAnchor(l, Qt::AnchorTop, a, Qt::AnchorTop); + l->addAnchor(a, Qt::AnchorBottom, b, Qt::AnchorTop); + l->addAnchor(b, Qt::AnchorBottom, c, Qt::AnchorTop); + l->addAnchor(c, Qt::AnchorBottom, l, Qt::AnchorBottom); + + l->addAnchor(l, Qt::AnchorLeft, a, Qt::AnchorLeft); + + // Both a and c are 'pointing' to b + l->addAnchor(a, Qt::AnchorRight, b, Qt::AnchorRight); + l->addAnchor(c, Qt::AnchorLeft, b, Qt::AnchorLeft); + + l->addAnchor(c, Qt::AnchorRight, l, Qt::AnchorRight); + + QCOMPARE(l->count(), 3); + + QGraphicsWidget p; + p.setLayout(l); + + QSizeF layoutMinimumSize = l->effectiveSizeHint(Qt::MinimumSize); + QSizeF layoutMaximumSize = l->effectiveSizeHint(Qt::MaximumSize); + QSizeF layoutPreferredSize = l->effectiveSizeHint(Qt::PreferredSize); + + QCOMPARE(layoutMinimumSize, QSizeF(60.0, 300.0)); + QCOMPARE(layoutPreferredSize, QSizeF(120.0, 300.0)); + QCOMPARE(layoutMaximumSize, QSizeF(190.0, 300.0)); + + p.resize(layoutMinimumSize); + QCOMPARE(a->geometry(), QRectF(0.0, 0.0, 50.0, 100.0)); + QCOMPARE(b->geometry(), QRectF(10.0, 100.0, 40.0, 100.0)); + QCOMPARE(c->geometry(), QRectF(10.0, 200.0, 50.0, 100.0)); + QCOMPARE(p.size(), layoutMinimumSize); + + p.resize(layoutPreferredSize); + QCOMPARE(a->geometry(), QRectF(0.0, 0.0, 70.0, 100.0)); + QCOMPARE(b->geometry(), QRectF(50.0, 100.0, 20.0, 100.0)); + QCOMPARE(c->geometry(), QRectF(50.0, 200.0, 70.0, 100.0)); + QCOMPARE(p.size(), layoutPreferredSize); + + p.resize(layoutMaximumSize); + QCOMPARE(a->geometry(), QRectF(0.0, 0.0, 100.0, 100.0)); + QCOMPARE(b->geometry(), QRectF(90.0, 100.0, 10.0, 100.0)); + QCOMPARE(c->geometry(), QRectF(90.0, 200.0, 100.0, 100.0)); + QCOMPARE(p.size(), layoutMaximumSize); + + QVERIFY(p.layout()); + QCOMPARE(checkReverseDirection(&p), true); +} + +void tst_QGraphicsAnchorLayout::fairDistribution() +{ + QGraphicsWidget *a = createItem(QSizeF(10.0, 100.0), + QSizeF(50.0, 100.0), + QSizeF(100.0, 100.0), "A"); + + QGraphicsWidget *b = createItem(QSizeF(10.0, 100.0), + QSizeF(50.0, 100.0), + QSizeF(100.0, 100.0), "B"); + + QGraphicsWidget *c = createItem(QSizeF(10.0, 100.0), + QSizeF(50.0, 100.0), + QSizeF(100.0, 100.0), "C"); + + QGraphicsWidget *d = createItem(QSizeF(60.0, 100.0), + QSizeF(165.0, 100.0), + QSizeF(600.0, 100.0), "D"); + + + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + l->setContentsMargins(0, 0, 0, 0); + l->setSpacing(0); + + l->addAnchor(l, Qt::AnchorTop, a, Qt::AnchorTop); + l->addAnchor(a, Qt::AnchorBottom, b, Qt::AnchorTop); + l->addAnchor(b, Qt::AnchorBottom, c, Qt::AnchorTop); + l->addAnchor(c, Qt::AnchorBottom, d, Qt::AnchorTop); + l->addAnchor(d, Qt::AnchorBottom, l, Qt::AnchorBottom); + + l->addAnchor(l, Qt::AnchorLeft, a, Qt::AnchorLeft); + l->addAnchor(a, Qt::AnchorRight, b, Qt::AnchorLeft); + l->addAnchor(b, Qt::AnchorRight, c, Qt::AnchorLeft); + l->addAnchor(c, Qt::AnchorRight, l, Qt::AnchorRight); + l->addAnchor(l, Qt::AnchorLeft, d, Qt::AnchorLeft); + l->addAnchor(d, Qt::AnchorRight, l, Qt::AnchorRight); + + QCOMPARE(l->count(), 4); + + QGraphicsWidget p; + p.setLayout(l); + + QSizeF layoutMinimumSize = l->effectiveSizeHint(Qt::MinimumSize); + QSizeF layoutMaximumSize = l->effectiveSizeHint(Qt::MaximumSize); + QSizeF layoutPreferredSize = l->effectiveSizeHint(Qt::PreferredSize); + + QCOMPARE(layoutMinimumSize, QSizeF(60.0, 400.0)); + QCOMPARE(layoutPreferredSize, QSizeF(165.0, 400.0)); + QCOMPARE(layoutMaximumSize, QSizeF(300.0, 400.0)); + + p.resize(layoutMinimumSize); + if (!hasSimplification) + QEXPECT_FAIL("", "Without simplification there is no fair distribution.", Abort); + QCOMPARE(a->geometry(), QRectF(0.0, 0.0, 20.0, 100.0)); + QCOMPARE(b->geometry(), QRectF(20.0, 100.0, 20.0, 100.0)); + QCOMPARE(c->geometry(), QRectF(40.0, 200.0, 20.0, 100.0)); + QCOMPARE(d->geometry(), QRectF(0.0, 300.0, 60.0, 100.0)); + QCOMPARE(p.size(), layoutMinimumSize); + + p.resize(layoutPreferredSize); + QCOMPARE(a->geometry(), QRectF(0.0, 0.0, 55.0, 100.0)); + QCOMPARE(b->geometry(), QRectF(55.0, 100.0, 55.0, 100.0)); + QCOMPARE(c->geometry(), QRectF(110.0, 200.0, 55.0, 100.0)); + QCOMPARE(d->geometry(), QRectF(0.0, 300.0, 165.0, 100.0)); + QCOMPARE(p.size(), layoutPreferredSize); + + p.resize(layoutMaximumSize); + QCOMPARE(a->geometry(), QRectF(0.0, 0.0, 100.0, 100.0)); + QCOMPARE(b->geometry(), QRectF(100.0, 100.0, 100.0, 100.0)); + QCOMPARE(c->geometry(), QRectF(200.0, 200.0, 100.0, 100.0)); + QCOMPARE(d->geometry(), QRectF(0.0, 300.0, 300.0, 100.0)); + QCOMPARE(p.size(), layoutMaximumSize); + + if (hasSimplification) { + QVERIFY(!usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); + } +} + +void tst_QGraphicsAnchorLayout::fairDistributionOppositeDirections() +{ + QGraphicsWidget *a = createItem(QSizeF(10.0, 100.0), + QSizeF(50.0, 100.0), + QSizeF(100.0, 100.0), "A"); + + QGraphicsWidget *b = createItem(QSizeF(10.0, 100.0), + QSizeF(50.0, 100.0), + QSizeF(100.0, 100.0), "B"); + + QGraphicsWidget *c = createItem(QSizeF(10.0, 100.0), + QSizeF(50.0, 100.0), + QSizeF(100.0, 100.0), "C"); + + QGraphicsWidget *d = createItem(QSizeF(10.0, 100.0), + QSizeF(50.0, 100.0), + QSizeF(100.0, 100.0), "D"); + + QGraphicsWidget *e = createItem(QSizeF(60.0, 100.0), + QSizeF(220.0, 100.0), + QSizeF(600.0, 100.0), "E"); + + + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + l->setContentsMargins(0, 0, 0, 0); + l->setSpacing(0); + + l->addAnchor(l, Qt::AnchorTop, a, Qt::AnchorTop); + l->addAnchor(a, Qt::AnchorBottom, b, Qt::AnchorTop); + l->addAnchor(b, Qt::AnchorBottom, c, Qt::AnchorTop); + l->addAnchor(c, Qt::AnchorBottom, d, Qt::AnchorTop); + l->addAnchor(d, Qt::AnchorBottom, e, Qt::AnchorTop); + l->addAnchor(e, Qt::AnchorBottom, l, Qt::AnchorBottom); + + l->addAnchor(a, Qt::AnchorLeft, l, Qt::AnchorLeft); + l->addAnchor(b, Qt::AnchorLeft, a, Qt::AnchorRight); + l->addAnchor(c, Qt::AnchorLeft, b, Qt::AnchorRight); + l->addAnchor(d, Qt::AnchorLeft, c, Qt::AnchorRight); + l->addAnchor(d, Qt::AnchorRight, l, Qt::AnchorRight); + l->addAnchors(l, e, Qt::Horizontal); + + QCOMPARE(l->count(), 5); + + QGraphicsWidget p; + p.setLayout(l); + + QSizeF layoutMinimumSize = l->effectiveSizeHint(Qt::MinimumSize); + QSizeF layoutMaximumSize = l->effectiveSizeHint(Qt::MaximumSize); + QSizeF layoutPreferredSize = l->effectiveSizeHint(Qt::PreferredSize); + + QCOMPARE(layoutMinimumSize, QSizeF(60.0, 500.0)); + QCOMPARE(layoutPreferredSize, QSizeF(220.0, 500.0)); + QCOMPARE(layoutMaximumSize, QSizeF(400.0, 500.0)); + + if (!hasSimplification) + return; + + p.resize(layoutMinimumSize); + QCOMPARE(a->size(), b->size()); + QCOMPARE(a->size(), c->size()); + QCOMPARE(a->size(), d->size()); + QCOMPARE(e->size().width(), 4 * a->size().width()); + QCOMPARE(p.size(), layoutMinimumSize); + + p.resize(layoutPreferredSize); + QCOMPARE(a->size(), b->size()); + QCOMPARE(a->size(), c->size()); + QCOMPARE(a->size(), d->size()); + QCOMPARE(e->size().width(), 4 * a->size().width()); + QCOMPARE(p.size(), layoutPreferredSize); + + p.resize(layoutMaximumSize); + QCOMPARE(a->size(), b->size()); + QCOMPARE(a->size(), c->size()); + QCOMPARE(a->size(), d->size()); + QCOMPARE(e->size().width(), 4 * a->size().width()); + QCOMPARE(p.size(), layoutMaximumSize); + + QVERIFY(!usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); +} + +void tst_QGraphicsAnchorLayout::proportionalPreferred() +{ + QSizeF minSize(0, 100); + + QGraphicsWidget *a = createItem(minSize, QSizeF(10, 100), QSizeF(20, 100), "A"); + QGraphicsWidget *b = createItem(minSize, QSizeF(20, 100), QSizeF(30, 100), "B"); + QGraphicsWidget *c = createItem(minSize, QSizeF(14, 100), QSizeF(20, 100), "C"); + QGraphicsWidget *d = createItem(minSize, QSizeF(10, 100), QSizeF(20, 100), "D"); + + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + l->setContentsMargins(0, 0, 0, 0); + l->setSpacing(0); + + l->addAnchor(l, Qt::AnchorTop, a, Qt::AnchorTop); + l->addAnchor(a, Qt::AnchorBottom, b, Qt::AnchorTop); + l->addAnchor(b, Qt::AnchorBottom, c, Qt::AnchorTop); + l->addAnchor(c, Qt::AnchorBottom, d, Qt::AnchorTop); + l->addAnchor(d, Qt::AnchorBottom, l, Qt::AnchorBottom); + + l->addAnchor(l, Qt::AnchorLeft, a, Qt::AnchorLeft); + l->addAnchor(l, Qt::AnchorLeft, b, Qt::AnchorLeft); + l->addAnchor(a, Qt::AnchorRight, c, Qt::AnchorLeft); + l->addAnchor(a, Qt::AnchorRight, d, Qt::AnchorLeft); + l->addAnchor(b, Qt::AnchorRight, l, Qt::AnchorRight); + l->addAnchor(c, Qt::AnchorRight, l, Qt::AnchorRight); + l->addAnchor(d, Qt::AnchorRight, l, Qt::AnchorRight); + + QCOMPARE(l->count(), 4); + + QGraphicsWidget p; + p.setLayout(l); + + QSizeF layoutMinimumSize = l->effectiveSizeHint(Qt::MinimumSize); + QSizeF layoutPreferredSize = l->effectiveSizeHint(Qt::PreferredSize); + QSizeF layoutMaximumSize = l->effectiveSizeHint(Qt::MaximumSize); + + QCOMPARE(layoutMinimumSize, QSizeF(0, 400)); + QCOMPARE(layoutPreferredSize, QSizeF(24, 400)); + QCOMPARE(layoutMaximumSize, QSizeF(30, 400)); + + p.resize(layoutMinimumSize); + QCOMPARE(p.size(), layoutMinimumSize); + + p.resize(layoutPreferredSize); + QCOMPARE(c->size().width(), d->size().width()); + QCOMPARE(p.size(), layoutPreferredSize); + + p.resize(layoutMaximumSize); + QCOMPARE(p.size(), layoutMaximumSize); + + p.resize(QSizeF(12, 400)); + + // Proportionality between size given and preferred size, this + // should be respected in this graph for (a) and (b)|(c). + qreal factor = 12.0 / 24.0; + + QCOMPARE(c->size().width(), d->size().width()); + QCOMPARE(a->size().width(), 10 * factor); + QCOMPARE(c->size().width(), 14 * factor); + QCOMPARE(p.size(), QSizeF(12, 400)); + + if (hasSimplification) { + QVERIFY(!usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); + } +} + +void tst_QGraphicsAnchorLayout::example() +{ + QSizeF minSize(30, 100); + QSizeF pref(210, 100); + QSizeF maxSize(300, 100); + + QGraphicsWidget *a = createItem(minSize, pref, maxSize, "A"); + QGraphicsWidget *b = createItem(minSize, pref, maxSize, "B"); + QGraphicsWidget *c = createItem(minSize, pref, maxSize, "C"); + QGraphicsWidget *d = createItem(minSize, pref, maxSize, "D"); + QGraphicsWidget *e = createItem(minSize, pref, maxSize, "E"); + QGraphicsWidget *f = createItem(QSizeF(30, 50), QSizeF(150, 50), maxSize, "F"); + QGraphicsWidget *g = createItem(QSizeF(30, 50), QSizeF(30, 100), maxSize, "G"); + + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + l->setContentsMargins(0, 0, 0, 0); + l->setSpacing(0); + + // vertical + l->addAnchor(a, Qt::AnchorTop, l, Qt::AnchorTop); + l->addAnchor(b, Qt::AnchorTop, l, Qt::AnchorTop); + + l->addAnchor(c, Qt::AnchorTop, a, Qt::AnchorBottom); + l->addAnchor(c, Qt::AnchorTop, b, Qt::AnchorBottom); + l->addAnchor(c, Qt::AnchorBottom, d, Qt::AnchorTop); + l->addAnchor(c, Qt::AnchorBottom, e, Qt::AnchorTop); + + l->addAnchor(d, Qt::AnchorBottom, l, Qt::AnchorBottom); + l->addAnchor(e, Qt::AnchorBottom, l, Qt::AnchorBottom); + + l->addAnchor(c, Qt::AnchorTop, f, Qt::AnchorTop); + l->addAnchor(c, Qt::AnchorVerticalCenter, f, Qt::AnchorBottom); + l->addAnchor(f, Qt::AnchorBottom, g, Qt::AnchorTop); + l->addAnchor(c, Qt::AnchorBottom, g, Qt::AnchorBottom); + + // horizontal + l->addAnchor(l, Qt::AnchorLeft, a, Qt::AnchorLeft); + l->addAnchor(l, Qt::AnchorLeft, d, Qt::AnchorLeft); + l->addAnchor(a, Qt::AnchorRight, b, Qt::AnchorLeft); + + l->addAnchor(a, Qt::AnchorRight, c, Qt::AnchorLeft); + l->addAnchor(c, Qt::AnchorRight, e, Qt::AnchorLeft); + + l->addAnchor(b, Qt::AnchorRight, l, Qt::AnchorRight); + l->addAnchor(e, Qt::AnchorRight, l, Qt::AnchorRight); + l->addAnchor(d, Qt::AnchorRight, e, Qt::AnchorLeft); + + l->addAnchor(l, Qt::AnchorLeft, f, Qt::AnchorLeft); + l->addAnchor(l, Qt::AnchorLeft, g, Qt::AnchorLeft); + l->addAnchor(f, Qt::AnchorRight, g, Qt::AnchorRight); + + QCOMPARE(l->count(), 7); + + QGraphicsWidget p; + p.setLayout(l); + + QSizeF layoutMinimumSize = l->effectiveSizeHint(Qt::MinimumSize); + QSizeF layoutMaximumSize = l->effectiveSizeHint(Qt::MaximumSize); + QSizeF layoutPreferredSize = l->effectiveSizeHint(Qt::PreferredSize); + + QCOMPARE(layoutMinimumSize, QSizeF(90.0, 300.0)); + QCOMPARE(layoutPreferredSize, QSizeF(510.0, 300.0)); + QCOMPARE(layoutMaximumSize, QSizeF(570.0, 300.0)); + + p.resize(layoutMinimumSize); + QCOMPARE(p.size(), layoutMinimumSize); + QCOMPARE(a->size(), e->size()); + QCOMPARE(b->size(), d->size()); + QCOMPARE(f->size(), g->size()); + + p.resize(layoutPreferredSize); + QCOMPARE(p.size(), layoutPreferredSize); + QCOMPARE(a->size(), e->size()); + QCOMPARE(b->size(), d->size()); + QCOMPARE(f->size(), g->size()); + + p.resize(layoutMaximumSize); + QCOMPARE(p.size(), layoutMaximumSize); + QCOMPARE(a->size(), e->size()); + QCOMPARE(b->size(), d->size()); + QCOMPARE(f->size(), g->size()); + + if (hasSimplification) { + QVERIFY(usedSimplex(l, Qt::Horizontal)); + QVERIFY(usedSimplex(l, Qt::Vertical)); + } +} + +void tst_QGraphicsAnchorLayout::setSpacing() +{ + QSizeF minSize(10, 10); + QSizeF pref(20, 20); + QSizeF maxSize(50, 50); + + QGraphicsWidget *a = createItem(minSize, pref, maxSize); + QGraphicsWidget *b = createItem(minSize, pref, maxSize); + QGraphicsWidget *c = createItem(minSize, pref, maxSize); + + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + l->addCornerAnchors(l, Qt::TopLeftCorner, a, Qt::TopLeftCorner); + l->addCornerAnchors(b, Qt::TopRightCorner, l, Qt::TopRightCorner); + l->addAnchor(a, Qt::AnchorRight, b, Qt::AnchorLeft); + + l->addAnchors(l, c, Qt::Horizontal); + + l->addAnchor(a, Qt::AnchorBottom, c, Qt::AnchorTop); + l->addAnchor(c, Qt::AnchorBottom, l, Qt::AnchorBottom); + + QGraphicsWidget *p = new QGraphicsWidget(0, Qt::Window); + + p->setLayout(l); + l->setSpacing(1); + + // don't let the style influence the test. + l->setContentsMargins(0, 0, 0, 0); + + QGraphicsScene scene; + scene.addItem(p); + QGraphicsView *view = new QGraphicsView(&scene); + view->show(); + p->show(); + + QApplication::processEvents(); +#ifdef Q_WS_MAC + QTest::qWait(200); +#endif + + // 21x21 + QCOMPARE(p->size(), QSizeF(41, 41)); + QCOMPARE(a->geometry(), QRectF(0, 0, 20, 20)); + QCOMPARE(b->geometry(), QRectF(21, 0, 20, 20)); + QCOMPARE(c->geometry(), QRectF(0, 21, 41, 20)); + + l->setHorizontalSpacing(4); + QApplication::processEvents(); + p->adjustSize(); + QCOMPARE(a->geometry(), QRectF(0, 0, 20, 20)); + QCOMPARE(b->geometry(), QRectF(24, 0, 20, 20)); + QCOMPARE(c->geometry(), QRectF(0, 21, 44, 20)); + + l->setVerticalSpacing(0); + QApplication::processEvents(); + p->adjustSize(); + QCOMPARE(a->geometry(), QRectF(0, 0, 20, 20)); + QCOMPARE(b->geometry(), QRectF(24, 0, 20, 20)); + QCOMPARE(c->geometry(), QRectF(0, 20, 44, 20)); + + delete p; + delete view; +} + +class CustomLayoutStyle : public QWindowsStyle +{ + Q_OBJECT +public: + CustomLayoutStyle() : QWindowsStyle() + { + hspacing = 5; + vspacing = 10; + } + + virtual int pixelMetric(PixelMetric metric, const QStyleOption * option = 0, + const QWidget * widget = 0 ) const; + + int hspacing; + int vspacing; + +protected slots: + int layoutSpacingImplementation(QSizePolicy::ControlType control1, + QSizePolicy::ControlType control2, + Qt::Orientation orientation, + const QStyleOption *option = 0, + const QWidget *widget = 0) const; + +}; + +#define CT1(c) CT2(c, c) +#define CT2(c1, c2) ((uint)c1 << 16) | (uint)c2 + +int CustomLayoutStyle::layoutSpacingImplementation(QSizePolicy::ControlType control1, + QSizePolicy::ControlType control2, + Qt::Orientation orientation, + const QStyleOption * /*option = 0*/, + const QWidget * /*widget = 0*/) const +{ + if (orientation == Qt::Horizontal) { + switch (CT2(control1, control2)) { + case CT1(QSizePolicy::PushButton): + return 2; + break; + } + return 5; + } else { + switch (CT2(control1, control2)) { + case CT1(QSizePolicy::RadioButton): + return 2; + break; + + } + return 10; + } +} + +int CustomLayoutStyle::pixelMetric(PixelMetric metric, const QStyleOption * option /*= 0*/, + const QWidget * widget /*= 0*/ ) const +{ + switch (metric) { + case PM_LayoutLeftMargin: + return 0; + break; + case PM_LayoutTopMargin: + return 3; + break; + case PM_LayoutRightMargin: + return 6; + break; + case PM_LayoutBottomMargin: + return 9; + break; + case PM_LayoutHorizontalSpacing: + return hspacing; + case PM_LayoutVerticalSpacing: + return vspacing; + break; + default: + break; + } + return QWindowsStyle::pixelMetric(metric, option, widget); +} + +void tst_QGraphicsAnchorLayout::styleDefaults() +{ + QSizeF minSize (10, 10); + QSizeF pref(20, 20); + QSizeF maxSize (50, 50); + + /* + create this layout, where a,b have controlType QSizePolicy::RadioButton + c,d have controlType QSizePolicy::PushButton: + +-------+ + |a | + | b | + | c | + | d| + +-------+ + */ + QGraphicsScene scene; + QGraphicsWidget *a = createItem(minSize, pref, maxSize); + QSizePolicy spRadioButton = a->sizePolicy(); + spRadioButton.setControlType(QSizePolicy::RadioButton); + a->setSizePolicy(spRadioButton); + + QGraphicsWidget *b = createItem(minSize, pref, maxSize); + b->setSizePolicy(spRadioButton); + + QGraphicsWidget *c = createItem(minSize, pref, maxSize); + QSizePolicy spPushButton = c->sizePolicy(); + spPushButton.setControlType(QSizePolicy::PushButton); + c->setSizePolicy(spPushButton); + + QGraphicsWidget *d = createItem(minSize, pref, maxSize); + d->setSizePolicy(spPushButton); + + QGraphicsWidget *window = new QGraphicsWidget(0, Qt::Window); + + // Test layoutSpacingImplementation + CustomLayoutStyle *style = new CustomLayoutStyle; + style->hspacing = -1; + style->vspacing = -1; + window->setStyle(style); + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + + l->addCornerAnchors(l, Qt::TopLeftCorner, a, Qt::TopLeftCorner); + l->addCornerAnchors(a, Qt::BottomRightCorner, b, Qt::TopLeftCorner); + l->addCornerAnchors(b, Qt::BottomRightCorner, c, Qt::TopLeftCorner); + l->addCornerAnchors(c, Qt::BottomRightCorner, d, Qt::TopLeftCorner); + l->addCornerAnchors(d, Qt::BottomRightCorner, l, Qt::BottomRightCorner); + + window->setLayout(l); + + scene.addItem(window); + + window->show(); + QGraphicsView view(&scene); + view.resize(200, 200); + view.show(); + + window->adjustSize(); + QCOMPARE(a->geometry(), QRectF(0, 3, 20, 20)); //radio + QCOMPARE(b->geometry(), QRectF(25, 25, 20, 20)); //radio + QCOMPARE(c->geometry(), QRectF(50, 55, 20, 20)); //push + QCOMPARE(d->geometry(), QRectF(72, 85, 20, 20)); //push + QCOMPARE(l->geometry(), QRectF(0, 0, 98, 114)); + + + // Test pixelMetric(PM_Layout{Horizontal|Vertical}Spacing + window->setStyle(0); + + style->hspacing = 1; + style->vspacing = 2; + + window->setStyle(style); + window->adjustSize(); + QCOMPARE(a->geometry(), QRectF(0, 3, 20, 20)); + QCOMPARE(b->geometry(), QRectF(21, 25, 20, 20)); + QCOMPARE(c->geometry(), QRectF(42, 47, 20, 20)); + QCOMPARE(d->geometry(), QRectF(63, 69, 20, 20)); + QCOMPARE(l->geometry(), QRectF(0, 0, 89, 98)); + + window->setStyle(0); + delete style; +} + + +/*! + Taken from "hard" complex case, found at + https://cwiki.nokia.com/S60QTUI/AnchorLayoutComplexCases + + This layout has a special property, since it has two possible solutions for its minimum size. + + For instance, when it is in its minimum size - the layout have two possible solutions: + 1. c.width == 10, e.width == 10 and g.width == 10 + (all others have width 0) + 2. d.width == 10 and g.width == 10 + (all others have width 0) + + It also has several solutions for preferred size. +*/ +static QGraphicsAnchorLayout *createAmbiguousS60Layout() +{ + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + l->setContentsMargins(0, 0, 0, 0); + l->setSpacing(0); + + QSizeF minSize(0, 10); + QSizeF pref(50, 10); + QSizeF maxSize(100, 10); + + QGraphicsWidget *a = createItem(minSize, pref, maxSize, "a"); + QGraphicsWidget *b = createItem(minSize, pref, maxSize, "b"); + QGraphicsWidget *c = createItem(minSize, pref, maxSize, "c"); + QGraphicsWidget *d = createItem(minSize, pref, maxSize, "d"); + QGraphicsWidget *e = createItem(minSize, pref, maxSize, "e"); + QGraphicsWidget *f = createItem(minSize, pref, maxSize, "f"); + QGraphicsWidget *g = createItem(minSize, pref, maxSize, "g"); + + //<!-- Trunk --> + setAnchor(l, l, Qt::AnchorLeft, a, Qt::AnchorLeft, 10); + setAnchor(l, a, Qt::AnchorRight, b, Qt::AnchorLeft, 10); + setAnchor(l, b, Qt::AnchorRight, c, Qt::AnchorLeft, 10); + setAnchor(l, c, Qt::AnchorRight, d, Qt::AnchorLeft, 10); + setAnchor(l, d, Qt::AnchorRight, l, Qt::AnchorRight, 10); + + //<!-- Above trunk --> + setAnchor(l, b, Qt::AnchorLeft, e, Qt::AnchorLeft, 10); + setAnchor(l, e, Qt::AnchorRight, d, Qt::AnchorLeft, 10); + + //<!-- Below trunk --> + setAnchor(l, a, Qt::AnchorHorizontalCenter, g, Qt::AnchorLeft, 10); + setAnchor(l, g, Qt::AnchorRight, f, Qt::AnchorHorizontalCenter, 10); + setAnchor(l, c, Qt::AnchorLeft, f, Qt::AnchorLeft, 10); + setAnchor(l, f, Qt::AnchorRight, d, Qt::AnchorRight, 10); + + //<!-- vertical is simpler --> + setAnchor(l, l, Qt::AnchorTop, e, Qt::AnchorTop, 0); + setAnchor(l, e, Qt::AnchorBottom, a, Qt::AnchorTop, 0); + setAnchor(l, e, Qt::AnchorBottom, b, Qt::AnchorTop, 0); + setAnchor(l, e, Qt::AnchorBottom, c, Qt::AnchorTop, 0); + setAnchor(l, e, Qt::AnchorBottom, d, Qt::AnchorTop, 0); + setAnchor(l, a, Qt::AnchorBottom, f, Qt::AnchorTop, 0); + setAnchor(l, a, Qt::AnchorBottom, b, Qt::AnchorBottom, 0); + setAnchor(l, a, Qt::AnchorBottom, c, Qt::AnchorBottom, 0); + setAnchor(l, a, Qt::AnchorBottom, d, Qt::AnchorBottom, 0); + setAnchor(l, f, Qt::AnchorBottom, g, Qt::AnchorTop, 0); + setAnchor(l, g, Qt::AnchorBottom, l, Qt::AnchorBottom, 0); + return l; +} + +void tst_QGraphicsAnchorLayout::hardComplexS60() +{ + QGraphicsAnchorLayout *l = createAmbiguousS60Layout(); + QCOMPARE(l->count(), 7); + + QGraphicsWidget *p = new QGraphicsWidget(0, Qt::Window); + p->setLayout(l); + + QSizeF layoutMinimumSize = l->effectiveSizeHint(Qt::MinimumSize); + QCOMPARE(layoutMinimumSize, QSizeF(60, 40)); + // expected preferred might be wrong, (haven't manually verified it) + QSizeF layoutPreferredSize = l->effectiveSizeHint(Qt::PreferredSize); + QCOMPARE(layoutPreferredSize, QSizeF(220, 40)); + QSizeF layoutMaximumSize = l->effectiveSizeHint(Qt::MaximumSize); + QCOMPARE(layoutMaximumSize, QSizeF(240, 40)); + + delete p; +} + +void tst_QGraphicsAnchorLayout::stability() +{ + QVector<QRectF> geometries; + geometries.resize(7); + QGraphicsWidget *p = new QGraphicsWidget(0, Qt::Window); + bool sameAsPreviousArrangement = true; + // it usually fails after 3-4 iterations + for (int pass = 0; pass < 20 && sameAsPreviousArrangement; ++pass) { + // In case we need to "scramble" the heap allocator to provoke this bug. + //static const int primes[] = {2, 3, 5, 13, 89, 233, 1597, 28657, 514229}; // fibo primes + //const int primeCount = sizeof(primes)/sizeof(int); + //int alloc = primes[pass % primeCount] + pass; + //void *mem = qMalloc(alloc); + //qFree(mem); + QGraphicsAnchorLayout *l = createAmbiguousS60Layout(); + p->setLayout(l); + QSizeF layoutMinimumSize = l->effectiveSizeHint(Qt::MinimumSize); + l->setGeometry(QRectF(QPointF(0,0), layoutMinimumSize)); + QApplication::processEvents(); + for (int i = l->count() - 1; i >=0 && sameAsPreviousArrangement; --i) { + QRectF geom = l->itemAt(i)->geometry(); + if (pass != 0) { + sameAsPreviousArrangement = (geometries[i] == geom); + } + geometries[i] = geom; + } + p->setLayout(0); // uninstalls and deletes the layout + QApplication::processEvents(); + } + delete p; + QEXPECT_FAIL("", "The layout have several solutions, but which solution it picks is not stable", Continue); + QCOMPARE(sameAsPreviousArrangement, true); +} + +void tst_QGraphicsAnchorLayout::delete_anchor() +{ + QGraphicsScene scene; + QSizeF minSize(0, 0); + QSizeF prefSize(50, 50); + QSizeF maxSize(100, 100); + QGraphicsWidget *w1 = createItem(minSize, prefSize, maxSize, "w1"); + QGraphicsWidget *w2 = createItem(minSize, prefSize, maxSize, "w2"); + QGraphicsWidget *w3 = createItem(minSize, prefSize, maxSize, "w3"); + + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + l->setSpacing(0); + l->setContentsMargins(0, 0, 0, 0); + + // Horizontal + l->addAnchor(l, Qt::AnchorLeft, w1, Qt::AnchorLeft); + l->addAnchor(w1, Qt::AnchorRight, w2, Qt::AnchorLeft); + l->addAnchor(w2, Qt::AnchorRight, l, Qt::AnchorRight); + l->addAnchor(w1, Qt::AnchorRight, w3, Qt::AnchorLeft); + l->addAnchor(w3, Qt::AnchorRight, l, Qt::AnchorRight); + + // Vertical + l->addAnchors(l, w1, Qt::Vertical); + l->addAnchors(l, w2, Qt::Vertical); + l->addAnchors(l, w3, Qt::Vertical); + + QGraphicsAnchor *anchor = l->anchor(w3, Qt::AnchorRight, l, Qt::AnchorRight); + anchor->setSpacing(10); + + QGraphicsWidget *p = new QGraphicsWidget; + p->setLayout(l); + + QCOMPARE(l->count(), 3); + + scene.addItem(p); + QGraphicsView *view = new QGraphicsView(&scene); + QApplication::processEvents(); + // Should now be simplified + QCOMPARE(l->effectiveSizeHint(Qt::PreferredSize).width(), qreal(110)); + QGraphicsAnchor *anchor1 = l->anchor(w3, Qt::AnchorRight, l, Qt::AnchorRight); + QVERIFY(anchor1); + QGraphicsAnchor *anchor2 = l->anchor(w3, Qt::AnchorRight, l, Qt::AnchorRight); + QVERIFY(anchor2); + QGraphicsAnchor *anchor3 = l->anchor(l, Qt::AnchorRight, w3, Qt::AnchorRight); + QVERIFY(anchor3); + QGraphicsAnchor *anchor4 = l->anchor(l, Qt::AnchorRight, w3, Qt::AnchorRight); + QVERIFY(anchor4); + + // should all be the same object + QCOMPARE(anchor1, anchor2); + QCOMPARE(anchor2, anchor3); + QCOMPARE(anchor3, anchor4); + + // check if removal works + delete anchor1; + + QApplication::processEvents(); + + // it should also change the preferred size of the layout + QCOMPARE(l->effectiveSizeHint(Qt::PreferredSize).width(), qreal(100)); + + delete p; + delete view; +} + +void tst_QGraphicsAnchorLayout::sizePolicy() +{ + QGraphicsScene scene; + QSizeF minSize(0, 0); + QSizeF prefSize(50, 50); + QSizeF maxSize(100, 100); + QGraphicsWidget *w1 = createItem(minSize, prefSize, maxSize, "w1"); + + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + l->setSpacing(0); + l->setContentsMargins(0, 0, 0, 0); + + // horizontal and vertical + l->addAnchors(l, w1); + + QGraphicsWidget *p = new QGraphicsWidget; + p->setLayout(l); + + scene.addItem(p); + QGraphicsView *view = new QGraphicsView(&scene); + + // QSizePolicy::Minimum + w1->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + QApplication::processEvents(); + w1->adjustSize(); + + QCOMPARE(l->effectiveSizeHint(Qt::MinimumSize), QSizeF(50, 50)); + QCOMPARE(l->effectiveSizeHint(Qt::PreferredSize), QSizeF(50, 50)); + QCOMPARE(l->effectiveSizeHint(Qt::MaximumSize), QSizeF(100, 100)); + + // QSizePolicy::Maximum + w1->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); + QApplication::processEvents(); + w1->adjustSize(); + + QCOMPARE(l->effectiveSizeHint(Qt::MinimumSize), QSizeF(0, 0)); + QCOMPARE(l->effectiveSizeHint(Qt::PreferredSize), QSizeF(50, 50)); + QCOMPARE(l->effectiveSizeHint(Qt::MaximumSize), QSizeF(50, 50)); + + // QSizePolicy::Fixed + w1->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + QApplication::processEvents(); + w1->adjustSize(); + + QCOMPARE(l->effectiveSizeHint(Qt::MinimumSize), QSizeF(50, 50)); + QCOMPARE(l->effectiveSizeHint(Qt::PreferredSize), QSizeF(50, 50)); + QCOMPARE(l->effectiveSizeHint(Qt::MaximumSize), QSizeF(50, 50)); + + // QSizePolicy::Preferred + w1->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + QApplication::processEvents(); + w1->adjustSize(); + + QCOMPARE(l->effectiveSizeHint(Qt::MinimumSize), QSizeF(0, 0)); + QCOMPARE(l->effectiveSizeHint(Qt::PreferredSize), QSizeF(50, 50)); + QCOMPARE(l->effectiveSizeHint(Qt::MaximumSize), QSizeF(100, 100)); + + // QSizePolicy::Ignored + w1->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored); + QApplication::processEvents(); + w1->adjustSize(); + + QCOMPARE(l->effectiveSizeHint(Qt::MinimumSize), QSizeF(0, 0)); + QCOMPARE(l->effectiveSizeHint(Qt::PreferredSize), QSizeF(0, 0)); + QCOMPARE(l->effectiveSizeHint(Qt::MaximumSize), QSizeF(100, 100)); + + // Anchor size policies + w1->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + QGraphicsAnchor *anchor = l->anchor(l, Qt::AnchorLeft, w1, Qt::AnchorLeft); + anchor->setSpacing(10); + + // QSizePolicy::Minimum + anchor->setSizePolicy(QSizePolicy::Minimum); + QApplication::processEvents(); + + QCOMPARE(l->effectiveSizeHint(Qt::MinimumSize), QSizeF(60, 50)); + QCOMPARE(l->effectiveSizeHint(Qt::PreferredSize), QSizeF(60, 50)); + // The layout has a maximum size of QWIDGETSIZE_MAX, so the result won't exceed that value. + QCOMPARE(l->effectiveSizeHint(Qt::MaximumSize), QSizeF(QWIDGETSIZE_MAX, 50)); + + // QSizePolicy::Preferred + anchor->setSizePolicy(QSizePolicy::Preferred); + QApplication::processEvents(); + + QCOMPARE(l->effectiveSizeHint(Qt::MinimumSize), QSizeF(50, 50)); + QCOMPARE(l->effectiveSizeHint(Qt::PreferredSize), QSizeF(60, 50)); + // The layout has a maximum size of QWIDGETSIZE_MAX, so the result won't exceed that value. + QCOMPARE(l->effectiveSizeHint(Qt::MaximumSize), QSizeF(QWIDGETSIZE_MAX, 50)); + + // QSizePolicy::Maximum + anchor->setSizePolicy(QSizePolicy::Maximum); + QApplication::processEvents(); + + QCOMPARE(l->effectiveSizeHint(Qt::MinimumSize), QSizeF(50, 50)); + QCOMPARE(l->effectiveSizeHint(Qt::PreferredSize), QSizeF(60, 50)); + QCOMPARE(l->effectiveSizeHint(Qt::MaximumSize), QSizeF(60, 50)); + + // QSizePolicy::Ignored + anchor->setSizePolicy(QSizePolicy::Ignored); + QApplication::processEvents(); + + QCOMPARE(l->effectiveSizeHint(Qt::MinimumSize), QSizeF(50, 50)); + QCOMPARE(l->effectiveSizeHint(Qt::PreferredSize), QSizeF(50, 50)); + QCOMPARE(l->effectiveSizeHint(Qt::MaximumSize), QSizeF(QWIDGETSIZE_MAX, 50)); + + if (hasSimplification) { + QVERIFY(!usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); + } + + delete p; + delete view; +} + +/*! + \internal + + Uses private API. (We have decided to pull hasConflicts() out of the API). However, it also + tests some tight conditions (almost-in-conflict) that we really want to test. +*/ +void tst_QGraphicsAnchorLayout::conflicts() +{ + QGraphicsWidget *a = createItem(QSizeF(80,10), QSizeF(90,10), QSizeF(100,10), "a"); + QGraphicsWidget *b = createItem(QSizeF(10,10), QSizeF(20,10), QSizeF(30,10), "b"); + QGraphicsWidget *c = createItem(QSizeF(10,10), QSizeF(20,10), QSizeF(30,10), "c"); + + QGraphicsAnchorLayout *l; + QGraphicsWidget *p = new QGraphicsWidget(0, Qt::Window); + + l = new QGraphicsAnchorLayout; + l->setContentsMargins(0, 0, 0, 0); + + // with the following setup, 'a' cannot be larger than 30 we will first have a Simplex conflict + + // horizontal + setAnchor(l, l, Qt::AnchorLeft, b, Qt::AnchorLeft); + setAnchor(l, b, Qt::AnchorRight, c, Qt::AnchorLeft); + setAnchor(l, c, Qt::AnchorRight, l, Qt::AnchorRight); + setAnchor(l, b, Qt::AnchorHorizontalCenter, a, Qt::AnchorLeft); + setAnchor(l, a, Qt::AnchorRight, c, Qt::AnchorHorizontalCenter); + + // vertical + setAnchor(l, l, Qt::AnchorTop, a, Qt::AnchorTop); + setAnchor(l, a, Qt::AnchorBottom, b, Qt::AnchorTop); + setAnchor(l, a, Qt::AnchorBottom, c, Qt::AnchorTop); + setAnchor(l, b, Qt::AnchorBottom, l, Qt::AnchorBottom); + setAnchor(l, c, Qt::AnchorBottom, l, Qt::AnchorBottom); + + p->setLayout(l); + + QCOMPARE(layoutHasConflict(l), true); + + a->setMinimumSize(QSizeF(29,10)); + QCOMPARE(layoutHasConflict(l), false); + + a->setMinimumSize(QSizeF(30,10)); + QCOMPARE(layoutHasConflict(l), false); + + delete p; +} + +void tst_QGraphicsAnchorLayout::floatConflict() +{ + QGraphicsWidget *a = createItem(QSizeF(80,10), QSizeF(90,10), QSizeF(100,10), "a"); + QGraphicsWidget *b = createItem(QSizeF(80,10), QSizeF(90,10), QSizeF(100,10), "b"); + + QGraphicsAnchorLayout *l; + QGraphicsWidget *p = new QGraphicsWidget(0, Qt::Window); + + l = new QGraphicsAnchorLayout; + l->setContentsMargins(0, 0, 0, 0); + + p->setLayout(l); + + // horizontal + // with this anchor we have two floating items + setAnchor(l, a, Qt::AnchorRight, b, Qt::AnchorLeft); + + // Just checking if the layout is handling well the removal of floating items + delete l->anchor(a, Qt::AnchorRight, b, Qt::AnchorLeft); + QCOMPARE(l->count(), 0); + QCOMPARE(layoutHasConflict(l), false); + + // setting back the same anchor + setAnchor(l, a, Qt::AnchorRight, b, Qt::AnchorLeft); + + // We don't support floating items but they should be counted as if they are in the layout + QCOMPARE(l->count(), 2); + // Although, we have an invalid situation + QCOMPARE(layoutHasConflict(l), true); + + // Semi-floats are supported + setAnchor(l, a, Qt::AnchorLeft, l, Qt::AnchorLeft); + QCOMPARE(l->count(), 2); + + // Vertically the layout has floating items. Therefore, we have a conflict + QCOMPARE(layoutHasConflict(l), true); + + // No more floating items + setAnchor(l, b, Qt::AnchorRight, l, Qt::AnchorRight); + setAnchor(l, a, Qt::AnchorTop, l, Qt::AnchorTop); + setAnchor(l, a, Qt::AnchorBottom, l, Qt::AnchorBottom); + setAnchor(l, b, Qt::AnchorTop, l, Qt::AnchorTop); + setAnchor(l, b, Qt::AnchorBottom, l, Qt::AnchorBottom); + QCOMPARE(layoutHasConflict(l), false); + + delete p; +} + +void tst_QGraphicsAnchorLayout::infiniteMaxSizes() +{ + if (sizeof(qreal) <= 4) { + QSKIP("qreal has too little precision, result will be wrong", SkipAll); + } + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + l->setContentsMargins(0, 0, 0, 0); + l->setSpacing(0); + + QSizeF minSize(10, 10); + QSizeF pref(50, 10); + QSizeF maxSize(QWIDGETSIZE_MAX, 10); + + QGraphicsWidget *a = createItem(minSize, pref, maxSize, "a"); + QGraphicsWidget *b = createItem(minSize, pref, maxSize, "b"); + QGraphicsWidget *c = createItem(minSize, pref, maxSize, "c"); + QGraphicsWidget *d = createItem(minSize, pref, maxSize, "d"); + QGraphicsWidget *e = createItem(minSize, pref, maxSize, "e"); + + //<!-- Trunk --> + setAnchor(l, l, Qt::AnchorLeft, a, Qt::AnchorLeft, 0); + setAnchor(l, a, Qt::AnchorRight, b, Qt::AnchorLeft, 0); + setAnchor(l, b, Qt::AnchorRight, c, Qt::AnchorLeft, 0); + setAnchor(l, c, Qt::AnchorRight, d, Qt::AnchorLeft, 0); + setAnchor(l, d, Qt::AnchorRight, l, Qt::AnchorRight, 0); + setAnchor(l, b, Qt::AnchorHorizontalCenter, e, Qt::AnchorLeft, 0); + setAnchor(l, e, Qt::AnchorRight, c, Qt::AnchorHorizontalCenter, 0); + + QGraphicsWidget p; + p.setLayout(l); + + QCOMPARE(int(p.effectiveSizeHint(Qt::MaximumSize).width()), + QWIDGETSIZE_MAX); + + p.resize(200, 10); + QCOMPARE(a->geometry(), QRectF(0, 0, 50, 10)); + QCOMPARE(b->geometry(), QRectF(50, 0, 50, 10)); + QCOMPARE(c->geometry(), QRectF(100, 0, 50, 10)); + QCOMPARE(d->geometry(), QRectF(150, 0, 50, 10)); + + p.resize(1000, 10); + QCOMPARE(a->geometry(), QRectF(0, 0, 250, 10)); + QCOMPARE(b->geometry(), QRectF(250, 0, 250, 10)); + QCOMPARE(c->geometry(), QRectF(500, 0, 250, 10)); + QCOMPARE(d->geometry(), QRectF(750, 0, 250, 10)); + + p.resize(40000, 10); + QCOMPARE(a->geometry(), QRectF(0, 0, 10000, 10)); + QCOMPARE(b->geometry(), QRectF(10000, 0, 10000, 10)); + QCOMPARE(c->geometry(), QRectF(20000, 0, 10000, 10)); + QCOMPARE(d->geometry(), QRectF(30000, 0, 10000, 10)); +} + +void tst_QGraphicsAnchorLayout::simplifiableUnfeasible() +{ + QGraphicsWidget *a = createItem(QSizeF(70.0, 100.0), + QSizeF(100.0, 100.0), + QSizeF(100.0, 100.0), "A"); + + QGraphicsWidget *b = createItem(QSizeF(110.0, 100.0), + QSizeF(150.0, 100.0), + QSizeF(190.0, 100.0), "B"); + + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + l->setContentsMargins(0, 0, 0, 0); + l->setSpacing(0); + + l->addAnchor(l, Qt::AnchorTop, a, Qt::AnchorTop); + l->addAnchor(a, Qt::AnchorBottom, b, Qt::AnchorTop); + l->addAnchor(b, Qt::AnchorBottom, l, Qt::AnchorBottom); + + l->addAnchors(l, a, Qt::Horizontal); + l->addAnchor(l, Qt::AnchorLeft, b, Qt::AnchorLeft); + l->addAnchor(b, Qt::AnchorRight, a, Qt::AnchorRight); + + QCOMPARE(l->count(), 2); + + QGraphicsWidget p; + p.setLayout(l); + + l->invalidate(); + QVERIFY(layoutHasConflict(l)); + if (hasSimplification) + QVERIFY(!usedSimplex(l, Qt::Horizontal)); + + // Now we make it valid + b->setMinimumWidth(100); + + l->invalidate(); + QVERIFY(!layoutHasConflict(l)); + if (hasSimplification) + QVERIFY(!usedSimplex(l, Qt::Horizontal)); + + // And make it invalid again + a->setPreferredWidth(70); + a->setMaximumWidth(70); + + l->invalidate(); + QVERIFY(layoutHasConflict(l)); + if (hasSimplification) + QVERIFY(!usedSimplex(l, Qt::Horizontal)); +} + +/* + Test whether the anchor direction can prevent it from + being simplificated +*/ +void tst_QGraphicsAnchorLayout::simplificationVsOrder() +{ + QSizeF minSize(10, 10); + QSizeF pref(20, 10); + QSizeF maxSize(50, 10); + + QGraphicsWidget *a = createItem(minSize, pref, maxSize, "A"); + QGraphicsWidget *b = createItem(minSize, pref, maxSize, "B"); + QGraphicsWidget *c = createItem(minSize, pref, maxSize, "C"); + + QGraphicsWidget frame; + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout(&frame); + + // Bulk anchors + l->addAnchor(l, Qt::AnchorLeft, a, Qt::AnchorLeft); + l->addAnchor(a, Qt::AnchorRight, b, Qt::AnchorLeft); + l->addAnchor(b, Qt::AnchorLeft, c, Qt::AnchorLeft); + l->addAnchor(c, Qt::AnchorRight, l, Qt::AnchorRight); + + // Problematic anchor, direction b->c + QGraphicsAnchor *anchor = l->addAnchor(b, Qt::AnchorRight, c, Qt::AnchorRight); + anchor->setSpacing(5); + + l->effectiveSizeHint(Qt::MinimumSize); + if (hasSimplification) { + QCOMPARE(usedSimplex(l, Qt::Horizontal), false); + QCOMPARE(usedSimplex(l, Qt::Vertical), false); + } + + // Problematic anchor, direction c->b + delete anchor; + anchor = l->addAnchor(c, Qt::AnchorRight, b, Qt::AnchorRight); + anchor->setSpacing(5); + + l->effectiveSizeHint(Qt::MinimumSize); + if (hasSimplification) { + QCOMPARE(usedSimplex(l, Qt::Horizontal), false); + QCOMPARE(usedSimplex(l, Qt::Vertical), false); + } +} + +void tst_QGraphicsAnchorLayout::parallelSimplificationOfCenter() +{ + QSizeF minSize(10, 10); + QSizeF pref(20, 10); + QSizeF maxSize(50, 10); + + QGraphicsWidget *a = createItem(minSize, pref, maxSize, "A"); + QGraphicsWidget *b = createItem(minSize, pref, maxSize, "B"); + + QGraphicsWidget parent; + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout(&parent); + l->setContentsMargins(0, 0, 0, 0); + + l->addAnchor(l, Qt::AnchorLeft, a, Qt::AnchorLeft); + l->addAnchor(l, Qt::AnchorRight, a, Qt::AnchorRight); + + l->addAnchor(a, Qt::AnchorHorizontalCenter, b, Qt::AnchorLeft); + l->addAnchor(b, Qt::AnchorRight, a, Qt::AnchorRight); + + parent.resize(l->effectiveSizeHint(Qt::PreferredSize)); + + QCOMPARE(a->geometry(), QRectF(0, 0, 40, 10)); + QCOMPARE(b->geometry(), QRectF(20, 0, 20, 10)); +} + +/* + Test whether redundance of anchors (in this case by using addCornerAnchors), will + prevent simplification to take place when it should. +*/ +void tst_QGraphicsAnchorLayout::simplificationVsRedundance() +{ + QSizeF minSize(10, 10); + QSizeF pref(20, 10); + QSizeF maxSize(50, 30); + + QGraphicsWidget *a = createItem(minSize, pref, maxSize, "A"); + QGraphicsWidget *b = createItem(minSize, pref, maxSize, "B"); + QGraphicsWidget *c = createItem(minSize, pref, maxSize, "C"); + + QGraphicsWidget frame; + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout(&frame); + + l->addCornerAnchors(a, Qt::TopLeftCorner, l, Qt::TopLeftCorner); + l->addCornerAnchors(a, Qt::BottomLeftCorner, l, Qt::BottomLeftCorner); + + l->addCornerAnchors(b, Qt::TopLeftCorner, a, Qt::TopRightCorner); + l->addCornerAnchors(b, Qt::TopRightCorner, l, Qt::TopRightCorner); + + l->addCornerAnchors(c, Qt::TopLeftCorner, b, Qt::BottomLeftCorner); + l->addCornerAnchors(c, Qt::BottomLeftCorner, a, Qt::BottomRightCorner); + l->addCornerAnchors(c, Qt::TopRightCorner, b, Qt::BottomRightCorner); + l->addCornerAnchors(c, Qt::BottomRightCorner, l, Qt::BottomRightCorner); + + l->effectiveSizeHint(Qt::MinimumSize); + + QCOMPARE(layoutHasConflict(l), false); + + if (!hasSimplification) + QEXPECT_FAIL("", "Test depends on simplification.", Abort); + + QCOMPARE(usedSimplex(l, Qt::Horizontal), false); + QCOMPARE(usedSimplex(l, Qt::Vertical), false); +} + +/* + Avoid regression where the saved prefSize would be lost. This was + solved by saving the original spacing in the QGraphicsAnchorPrivate class +*/ +void tst_QGraphicsAnchorLayout::spacingPersistency() +{ + QGraphicsWidget w; + QGraphicsWidget *a = createItem(); + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout(&w); + + l->addAnchors(l, a, Qt::Horizontal); + QGraphicsAnchor *anchor = l->anchor(l, Qt::AnchorLeft, a, Qt::AnchorLeft); + + anchor->setSpacing(-30); + QCOMPARE(anchor->spacing(), -30.0); + + anchor->setSpacing(30); + QCOMPARE(anchor->spacing(), 30.0); + + anchor->setSizePolicy(QSizePolicy::Ignored); + w.effectiveSizeHint(Qt::PreferredSize); + + QCOMPARE(anchor->spacing(), 30.0); +} + +/* + Test whether a correct preferred size is set when a "snake" sequence is in parallel with the + layout or half of the layout. The tricky thing here is that all items on the snake should + keep their preferred sizes. +*/ +void tst_QGraphicsAnchorLayout::snakeParallelWithLayout() +{ + QSizeF minSize(10, 20); + QSizeF pref(50, 20); + QSizeF maxSize(100, 20); + + QGraphicsWidget *a = createItem(maxSize, maxSize, maxSize, "A"); + QGraphicsWidget *b = createItem(minSize, pref, maxSize, "B"); + QGraphicsWidget *c = createItem(maxSize, maxSize, maxSize, "C"); + + QGraphicsWidget parent; + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout(&parent); + l->setContentsMargins(0, 0, 0, 0); + l->setSpacing(0); + + // First we'll do the case in parallel with the entire layout... + l->addAnchor(l, Qt::AnchorLeft, a, Qt::AnchorLeft); + l->addAnchor(a, Qt::AnchorRight, b, Qt::AnchorRight); + l->addAnchor(b, Qt::AnchorLeft, c, Qt::AnchorLeft); + l->addAnchor(c, Qt::AnchorRight, l, Qt::AnchorRight); + + l->addAnchor(l, Qt::AnchorTop, a, Qt::AnchorTop); + l->addAnchor(a, Qt::AnchorBottom, b, Qt::AnchorTop); + l->addAnchor(b, Qt::AnchorBottom, c, Qt::AnchorTop); + l->addAnchor(c, Qt::AnchorBottom, l, Qt::AnchorBottom); + + parent.resize(l->effectiveSizeHint(Qt::PreferredSize)); + + // Note that A and C are fixed in the maximum size + QCOMPARE(l->geometry(), QRectF(QPointF(0, 0), QSizeF(150, 60))); + QCOMPARE(a->geometry(), QRectF(QPointF(0, 0), maxSize)); + QCOMPARE(b->geometry(), QRectF(QPointF(50, 20), pref)); + QCOMPARE(c->geometry(), QRectF(QPointF(50, 40), maxSize)); + + // Then, we change the "snake" to be in parallel with half of the layout + delete l->anchor(c, Qt::AnchorRight, l, Qt::AnchorRight); + l->addAnchor(c, Qt::AnchorRight, l, Qt::AnchorHorizontalCenter); + + parent.resize(l->effectiveSizeHint(Qt::PreferredSize)); + + QCOMPARE(l->geometry(), QRectF(QPointF(0, 0), QSizeF(300, 60))); + QCOMPARE(a->geometry(), QRectF(QPointF(0, 0), maxSize)); + QCOMPARE(b->geometry(), QRectF(QPointF(50, 20), pref)); + QCOMPARE(c->geometry(), QRectF(QPointF(50, 40), maxSize)); +} + +/* + Avoid regression where the sizeHint constraints would not be + created for a parallel anchor that included the first layout half +*/ +void tst_QGraphicsAnchorLayout::parallelToHalfLayout() +{ + QGraphicsWidget *a = createItem(); + + QGraphicsWidget w; + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout(&w); + l->setContentsMargins(10, 10, 10, 10); + + l->addAnchors(l, a, Qt::Vertical); + + QGraphicsAnchor *anchor; + anchor = l->addAnchor(l, Qt::AnchorLeft, a, Qt::AnchorLeft); + anchor->setSpacing(5); + anchor = l->addAnchor(l, Qt::AnchorHorizontalCenter, a, Qt::AnchorRight); + anchor->setSpacing(-5); + + const QSizeF minimumSizeHint = w.effectiveSizeHint(Qt::MinimumSize); + const QSizeF preferredSizeHint = w.effectiveSizeHint(Qt::PreferredSize); + const QSizeF maximumSizeHint = w.effectiveSizeHint(Qt::MaximumSize); + + const QSizeF overhead = QSizeF(10 + 5 + 5, 10) * 2; + + QCOMPARE(minimumSizeHint, QSizeF(200, 100) + overhead); + QCOMPARE(preferredSizeHint, QSizeF(300, 100) + overhead); + QCOMPARE(maximumSizeHint, QSizeF(400, 100) + overhead); +} + +void tst_QGraphicsAnchorLayout::globalSpacing() +{ + QGraphicsWidget *a = createItem(); + QGraphicsWidget *b = createItem(); + + QGraphicsWidget w; + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout(&w); + + l->addCornerAnchors(l, Qt::TopLeftCorner, a, Qt::TopLeftCorner); + l->addCornerAnchors(a, Qt::BottomRightCorner, b, Qt::TopLeftCorner); + l->addCornerAnchors(b, Qt::BottomRightCorner, l, Qt::BottomRightCorner); + + w.resize(w.effectiveSizeHint(Qt::PreferredSize)); + qreal vSpacing = b->geometry().top() - a->geometry().bottom(); + qreal hSpacing = b->geometry().left() - a->geometry().right(); + + // Set spacings manually + l->setVerticalSpacing(vSpacing + 10); + l->setHorizontalSpacing(hSpacing + 5); + + w.resize(w.effectiveSizeHint(Qt::PreferredSize)); + qreal newVSpacing = b->geometry().top() - a->geometry().bottom(); + qreal newHSpacing = b->geometry().left() - a->geometry().right(); + + QCOMPARE(newVSpacing, vSpacing + 10); + QCOMPARE(newHSpacing, hSpacing + 5); + + // Set a negative spacing. This will unset the previous spacing and + // bring back the widget-defined spacing. + l->setSpacing(-1); + + w.resize(w.effectiveSizeHint(Qt::PreferredSize)); + newVSpacing = b->geometry().top() - a->geometry().bottom(); + newHSpacing = b->geometry().left() - a->geometry().right(); + + QCOMPARE(newVSpacing, vSpacing); + QCOMPARE(newHSpacing, hSpacing); +} + +void tst_QGraphicsAnchorLayout::graphicsAnchorHandling() +{ + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout(); + QGraphicsWidget *a = createItem(); + + l->addAnchors(l, a); + + QGraphicsAnchor *layoutAnchor = l->anchor(l, Qt::AnchorTop, l, Qt::AnchorBottom); + QGraphicsAnchor *itemAnchor = l->anchor(a, Qt::AnchorTop, a, Qt::AnchorBottom); + QGraphicsAnchor *invalidAnchor = l->anchor(a, Qt::AnchorTop, l, Qt::AnchorBottom); + + // Ensure none of these anchors are accessible. + QVERIFY(layoutAnchor == 0); + QVERIFY(itemAnchor == 0); + QVERIFY(invalidAnchor == 0); + + // Hook the anchors to a QObject + QObject object; + QGraphicsAnchor *userAnchor = l->anchor(l, Qt::AnchorTop, a, Qt::AnchorTop); + userAnchor->setParent(&object); + userAnchor = l->anchor(l, Qt::AnchorBottom, a, Qt::AnchorBottom); + userAnchor->setParent(&object); + userAnchor = l->anchor(l, Qt::AnchorRight, a, Qt::AnchorRight); + userAnchor->setParent(&object); + userAnchor = l->anchor(l, Qt::AnchorLeft, a, Qt::AnchorLeft); + userAnchor->setParent(&object); + + QCOMPARE(object.children().size(), 4); + + // Delete layout, this will cause all anchors to be deleted internally. + // We expect the public QGraphicsAnchor instances to be deleted too. + delete l; + QCOMPARE(object.children().size(), 0); + + delete a; +} + +void tst_QGraphicsAnchorLayout::invalidHierarchyCheck() +{ + QGraphicsWidget window(0, Qt::Window); + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + window.setLayout(l); + + QCOMPARE(l->count(), 0); + QTest::ignoreMessage(QtWarningMsg, "QGraphicsAnchorLayout::addAnchor(): " + "You cannot add the parent of the layout to the layout."); + QVERIFY(!l->addAnchor(l, Qt::AnchorLeft, &window, Qt::AnchorLeft)); + QTest::ignoreMessage(QtWarningMsg, "QGraphicsAnchorLayout::addAnchor(): " + "You cannot add the parent of the layout to the layout."); + l->addAnchors(l, &window); + QTest::ignoreMessage(QtWarningMsg, "QGraphicsAnchorLayout::addAnchor(): " + "You cannot add the parent of the layout to the layout."); + l->addCornerAnchors(l, Qt::TopLeftCorner, &window, Qt::TopLeftCorner); + QCOMPARE(l->count(), 0); +} + +QTEST_MAIN(tst_QGraphicsAnchorLayout) +#include "tst_qgraphicsanchorlayout.moc" diff --git a/tests/auto/widgets/graphicsview/qgraphicsanchorlayout1/qgraphicsanchorlayout1.pro b/tests/auto/widgets/graphicsview/qgraphicsanchorlayout1/qgraphicsanchorlayout1.pro new file mode 100644 index 0000000000..bcad43fc12 --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsanchorlayout1/qgraphicsanchorlayout1.pro @@ -0,0 +1,5 @@ +load(qttest_p4) +QT += widgets widgets-private +QT += core-private gui-private +SOURCES += tst_qgraphicsanchorlayout1.cpp +CONFIG += parallel_test diff --git a/tests/auto/widgets/graphicsview/qgraphicsanchorlayout1/tst_qgraphicsanchorlayout1.cpp b/tests/auto/widgets/graphicsview/qgraphicsanchorlayout1/tst_qgraphicsanchorlayout1.cpp new file mode 100644 index 0000000000..05f08e8719 --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsanchorlayout1/tst_qgraphicsanchorlayout1.cpp @@ -0,0 +1,3111 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtGui> +#include <QtTest/QtTest> +#include <QTest> +#include <QMetaType> +#include <QtWidgets/qgraphicsanchorlayout.h> +#include <private/qgraphicsanchorlayout_p.h> + +#define TEST_COMPLEX_CASES + + +//---------------------- AnchorLayout helper class ---------------------------- +class TheAnchorLayout : public QGraphicsAnchorLayout +{ +public: + TheAnchorLayout() : QGraphicsAnchorLayout() + { + setContentsMargins( 0,0,0,0 ); + setSpacing( 0 ); + } + + bool isValid() + { + return !QGraphicsAnchorLayoutPrivate::get(this)->hasConflicts(); + } + + void setAnchor( + QGraphicsLayoutItem *startItem, + Qt::AnchorPoint startEdge, + QGraphicsLayoutItem *endItem, + Qt::AnchorPoint endEdge, + qreal value) + { + QGraphicsAnchor *anchor = addAnchor( startItem, startEdge, endItem, endEdge); + if (anchor) + anchor->setSpacing(value); + } + + int indexOf(const QGraphicsLayoutItem* item) const + { + for ( int i=0; i< count(); i++) { + if ( itemAt(i) == item ) { + return i; + } + } + return -1; + } + + void removeItem(QGraphicsLayoutItem* item) + { + removeAt(indexOf(item)); + } + + void removeAnchor( + QGraphicsLayoutItem *startItem, + Qt::AnchorPoint startEdge, + QGraphicsLayoutItem *endItem, + Qt::AnchorPoint endEdge) + { + delete QGraphicsAnchorLayout::anchor(startItem, startEdge, endItem, endEdge); + } +}; +//----------------------------------------------------------------------------- + + +struct BasicLayoutTestData +{ + inline BasicLayoutTestData( + int index1, Qt::AnchorPoint edge1, + int index2, Qt::AnchorPoint edge2, + qreal distance) + : firstIndex(index1), firstEdge(edge1), + secondIndex(index2), secondEdge(edge2), + spacing(distance) + { + } + + int firstIndex; + Qt::AnchorPoint firstEdge; + int secondIndex; + Qt::AnchorPoint secondEdge; + qreal spacing; +}; + +struct AnchorItemSizeHint +{ + inline AnchorItemSizeHint( + qreal hmin, qreal hpref, qreal hmax, + qreal vmin, qreal vpref, qreal vmax ) + : hmin(hmin), hpref(hpref), hmax(hmax), vmin(vmin), vpref(vpref), vmax(vmax) + { + } + qreal hmin, hpref, hmax; + qreal vmin, vpref, vmax; +}; + +// some test results + +struct BasicLayoutTestResult +{ + inline BasicLayoutTestResult( + int resultIndex, const QRectF& resultRect ) + : index(resultIndex), rect(resultRect) + { + } + + int index; + QRectF rect; +}; + +typedef QList<BasicLayoutTestData> BasicLayoutTestDataList; +Q_DECLARE_METATYPE(BasicLayoutTestDataList) + +typedef QList<BasicLayoutTestResult> BasicLayoutTestResultList; +Q_DECLARE_METATYPE(BasicLayoutTestResultList) + +typedef QList<AnchorItemSizeHint> AnchorItemSizeHintList; +Q_DECLARE_METATYPE(AnchorItemSizeHintList) + + +//---------------------- Test Widget used on all tests ------------------------ +class TestWidget : public QGraphicsWidget +{ +public: + inline TestWidget(QGraphicsItem *parent = 0, const QString &name = QString()) + : QGraphicsWidget(parent) + { + setContentsMargins( 0,0,0,0 ); + if (name.isEmpty()) + setData(0, QString::fromAscii("w%1").arg(quintptr(this))); + else + setData(0, name); + } + ~TestWidget() + { + } + +protected: + QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const; +}; + +QSizeF TestWidget::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const +{ + Q_UNUSED( constraint ); + if (which == Qt::MinimumSize) { + return QSizeF(5,5); + } + + if (which == Qt::PreferredSize) { + return QSizeF(50,50); + } + + return QSizeF(500,500); +} +//----------------------------------------------------------------------------- + + + +//----------------------------- Test class ------------------------------------ +class tst_QGraphicsAnchorLayout1 : public QObject +{ + Q_OBJECT + +private slots: + void testCount(); + + void testRemoveAt(); + void testRemoveItem(); + + void testItemAt(); + void testIndexOf(); + + void testAddAndRemoveAnchor(); + void testIsValid(); + void testSpecialCases(); + + void testBasicLayout_data(); + void testBasicLayout(); + + void testNegativeSpacing_data(); + void testNegativeSpacing(); + + void testMixedSpacing_data(); + void testMixedSpacing(); + + void testMulti_data(); + void testMulti(); + + void testCenterAnchors_data(); + void testCenterAnchors(); + + void testRemoveCenterAnchor_data(); + void testRemoveCenterAnchor(); + + void testSingleSizePolicy_data(); + void testSingleSizePolicy(); + + void testDoubleSizePolicy_data(); + void testDoubleSizePolicy(); + + void testSizeDistribution_data(); + void testSizeDistribution(); + + void testSizeHint(); + +#ifdef TEST_COMPLEX_CASES + void testComplexCases_data(); + void testComplexCases(); +#endif +}; + + +void tst_QGraphicsAnchorLayout1::testCount() +{ + QGraphicsWidget *widget = new QGraphicsWidget; + + TheAnchorLayout *layout = new TheAnchorLayout(); + QVERIFY( layout->count() == 0 ); + + TestWidget *widget1 = new TestWidget(); + layout->setAnchor(layout, Qt::AnchorLeft, widget1, Qt::AnchorLeft, 1); + QCOMPARE( layout->count(), 1 ); + + // adding one more anchor for already added widget should not increase the count + layout->setAnchor(layout, Qt::AnchorRight, widget1, Qt::AnchorRight, 1); + QCOMPARE( layout->count(), 1 ); + + // create one more widget and attach with anchor layout + TestWidget *widget2 = new TestWidget(); + layout->setAnchor(layout, Qt::AnchorLeft, widget2, Qt::AnchorLeft, 1); + QCOMPARE( layout->count(), 2 ); + + widget->setLayout(layout); + delete widget; +} + +void tst_QGraphicsAnchorLayout1::testRemoveAt() +{ + TheAnchorLayout *layout = new TheAnchorLayout(); + QVERIFY( layout->count() == 0 ); + + TestWidget *widget1 = new TestWidget(); + layout->setAnchor(layout, Qt::AnchorLeft, widget1, Qt::AnchorLeft, 2); + QVERIFY( layout->count() == 1 ); + + TestWidget *widget2 = new TestWidget(); + layout->setAnchor(widget2, Qt::AnchorLeft, layout, Qt::AnchorLeft, 0.1); + QVERIFY( layout->count() == 2 ); + + layout->removeAt(0); + QVERIFY( layout->count() == 1 ); + + layout->removeAt(-55); + layout->removeAt(55); + QVERIFY( layout->count() == 1 ); + + layout->removeAt(0); + QVERIFY( layout->count() == 0 ); + + delete layout; + delete widget1; + delete widget2; +} + +void tst_QGraphicsAnchorLayout1::testRemoveItem() +{ + TheAnchorLayout *layout = new TheAnchorLayout(); + QCOMPARE( layout->count(), 0 ); + + TestWidget *widget1 = new TestWidget(); + layout->setAnchor(layout, Qt::AnchorLeft, widget1, Qt::AnchorLeft, 2); + QCOMPARE( layout->count(), 1 ); + + TestWidget *widget2 = new TestWidget(); + layout->setAnchor(layout, Qt::AnchorLeft, widget2, Qt::AnchorLeft, 0.1); + QCOMPARE( layout->count(), 2 ); + + layout->removeItem(0); + QCOMPARE( layout->count(), 2 ); + + layout->removeItem(widget1); + QCOMPARE( layout->count(), 1 ); + QCOMPARE( layout->indexOf(widget1), -1 ); + QCOMPARE( layout->indexOf(widget2), 0 ); + + layout->removeItem(widget1); + QCOMPARE( layout->count(), 1 ); + + layout->removeItem(widget2); + QVERIFY( layout->count() == 0 ); + + delete layout; + delete widget1; + delete widget2; +} + +void tst_QGraphicsAnchorLayout1::testItemAt() +{ + QGraphicsWidget *widget = new QGraphicsWidget; + + TheAnchorLayout *layout = new TheAnchorLayout(); + + TestWidget *widget1 = new TestWidget(); + TestWidget *widget2 = new TestWidget(); + TestWidget *widget3 = new TestWidget(); + TestWidget *widget4 = new TestWidget(); + + layout->setAnchor(layout, Qt::AnchorLeft, widget1, Qt::AnchorLeft, 0.1); + layout->setAnchor(layout, Qt::AnchorLeft, widget2, Qt::AnchorLeft, 0.1); + layout->setAnchor(layout, Qt::AnchorLeft, widget3, Qt::AnchorLeft, 0.1); + layout->setAnchor(layout, Qt::AnchorLeft, widget4, Qt::AnchorLeft, 0.1); + + QVERIFY( layout->itemAt(0) == widget1 ); + + layout->removeAt(0); + + QVERIFY( layout->itemAt(0) == widget2 ); + + delete widget1; + + widget->setLayout(layout); + delete widget; +} + +void tst_QGraphicsAnchorLayout1::testIndexOf() +{ + QGraphicsWidget *widget = new QGraphicsWidget; + + TheAnchorLayout *layout = new TheAnchorLayout(); + + TestWidget *widget1 = new TestWidget(); + TestWidget *widget2 = new TestWidget(); + TestWidget *widget3 = new TestWidget(); + TestWidget *widget4 = new TestWidget(); + + QCOMPARE( layout->indexOf(widget1), -1 ); + + layout->setAnchor(layout, Qt::AnchorLeft, widget1, Qt::AnchorLeft, 0.1); + layout->setAnchor(layout, Qt::AnchorLeft, widget2, Qt::AnchorLeft, 0.1); + layout->setAnchor(layout, Qt::AnchorLeft, widget3, Qt::AnchorLeft, 0.1); + + QCOMPARE( layout->indexOf(widget4), -1 ); + layout->setAnchor(layout, Qt::AnchorLeft, widget4, Qt::AnchorLeft, 0.1); + + QCOMPARE( layout->count(), 4 ); + for (int i = 0; i < layout->count(); ++i) { + QCOMPARE(layout->indexOf(layout->itemAt(i)), i); + } + + QCOMPARE( layout->indexOf(0), -1 ); + widget->setLayout(layout); + delete widget; +} + +void tst_QGraphicsAnchorLayout1::testAddAndRemoveAnchor() +{ + QGraphicsWidget *widget = new QGraphicsWidget; + + TheAnchorLayout *layout = new TheAnchorLayout(); + + TestWidget *widget1 = new TestWidget(); + TestWidget *widget2 = new TestWidget(); + TestWidget *widget3 = new TestWidget(); + TestWidget *widget4 = new TestWidget(); + TestWidget *widget5 = new TestWidget(); + + layout->setAnchor(layout, Qt::AnchorLeft, widget1, Qt::AnchorLeft, 0.1); + layout->setAnchor(layout, Qt::AnchorLeft, widget2, Qt::AnchorLeft, 0.5); + layout->setAnchor(layout, Qt::AnchorLeft, widget3, Qt::AnchorLeft, 10); + layout->setAnchor(layout, Qt::AnchorLeft, widget4, Qt::AnchorLeft, 0.1); + QCOMPARE( layout->count(), 4 ); + + // test setting invalid anchors + QTest::ignoreMessage(QtWarningMsg, "QGraphicsAnchorLayout::addAnchor(): Cannot anchor NULL items"); + layout->setAnchor(0, Qt::AnchorLeft, widget1, Qt::AnchorLeft, 1); + QTest::ignoreMessage(QtWarningMsg, "QGraphicsAnchorLayout::addAnchor(): Cannot anchor NULL items"); + layout->setAnchor(layout, Qt::AnchorLeft, 0, Qt::AnchorLeft, 1); + QCOMPARE( layout->count(), 4 ); + + // test removing invalid anchors + layout->removeAnchor(widget4, Qt::AnchorRight, widget1, Qt::AnchorRight); + + // anchor one horizontal edge with vertical edge. it should not add this widget as a child + QTest::ignoreMessage(QtWarningMsg, "QGraphicsAnchorLayout::addAnchor(): Cannot anchor edges of different orientations"); + layout->setAnchor(layout, Qt::AnchorLeft, widget5, Qt::AnchorTop, 10); + QCOMPARE( layout->count(), 4 ); + + // anchor two edges of a widget (to define width / height) + QTest::ignoreMessage(QtWarningMsg, "QGraphicsAnchorLayout::addAnchor(): Cannot anchor the item to itself"); + layout->setAnchor(widget5, Qt::AnchorLeft, widget5, Qt::AnchorRight, 10); + // QCOMPARE( layout->count(), 5 ); + QCOMPARE( layout->count(), 4 ); + + // anchor yet new widget properly + layout->setAnchor(layout, Qt::AnchorRight, widget5, Qt::AnchorRight, 20 ); + QCOMPARE( layout->count(), 5 ); + + // remove anchor for widget1. widget1 should be removed from layout since the + // last anchor was removed. + layout->removeAnchor(layout, Qt::AnchorLeft, widget1, Qt::AnchorLeft); + + QCOMPARE( layout->count(), 4 ); + QVERIFY( !widget1->parentLayoutItem() ); + + // test that item is not removed from layout if other anchors remain set + layout->setAnchor(widget2, Qt::AnchorLeft, widget3, Qt::AnchorRight, 10); + layout->removeAnchor(layout, Qt::AnchorLeft, widget2, Qt::AnchorLeft); + QCOMPARE( layout->count(), 4 ); + + // remove all the anchors + layout->removeAnchor(widget2, Qt::AnchorLeft, widget3, Qt::AnchorRight); + layout->removeAnchor(layout, Qt::AnchorLeft, widget3, Qt::AnchorLeft); + layout->removeAnchor(layout, Qt::AnchorLeft, widget4, Qt::AnchorLeft); + layout->removeAnchor(widget5, Qt::AnchorLeft, widget5, Qt::AnchorRight); + layout->removeAnchor(layout, Qt::AnchorRight, widget5, Qt::AnchorRight); + + QCOMPARE( layout->count(), 0 ); + + // set one anchor "another way round" to get full coverage for "removeAnchor" + layout->setAnchor(widget1, Qt::AnchorLeft, layout, Qt::AnchorLeft, 0.1); + layout->removeAnchor(widget1, Qt::AnchorLeft, layout, Qt::AnchorLeft); + + QCOMPARE( layout->count(), 0 ); + + delete widget1; + delete widget2; + delete widget3; + delete widget4; + delete widget5; + + widget->setLayout(layout); + delete widget; +} + +void tst_QGraphicsAnchorLayout1::testIsValid() +{ + // Empty, valid + { + QGraphicsWidget *widget = new QGraphicsWidget; + TheAnchorLayout *layout = new TheAnchorLayout(); + widget->setLayout(layout); + widget->setGeometry(QRectF(0,0,100,100)); + + QCOMPARE(layout->isValid(), true); + delete widget; + } + + // One widget, valid + { + QGraphicsWidget *widget = new QGraphicsWidget; + TheAnchorLayout *layout = new TheAnchorLayout(); + + TestWidget *widget1 = new TestWidget(); + + layout->setAnchor(layout, Qt::AnchorLeft, widget1, Qt::AnchorLeft, 0.1); + layout->setAnchor(layout, Qt::AnchorTop, widget1, Qt::AnchorTop, 0.1); + layout->setAnchor(widget1, Qt::AnchorRight, layout, Qt::AnchorRight, 0.1); + layout->setAnchor(widget1, Qt::AnchorBottom, layout, Qt::AnchorBottom, 0.1); + + widget->setLayout(layout); + + widget->setGeometry(QRectF(0,0,100,100)); + QCOMPARE(layout->isValid(), true); + delete widget; + } + + // Overconstrained one widget, invalid + // ### Our understanding is that this case is valid. What happens though, + // is that the layout minimum and maximum vertical size hints become + // the same, 10.1. That means its height is fixed. + // What will "fail" then is the "setGeometry(0, 0, 100, 100)" call, + // after which the layout geometry will be (0, 0, 100, 10.1). + + { + QGraphicsWidget *widget = new QGraphicsWidget; + TheAnchorLayout *layout = new TheAnchorLayout(); + + TestWidget *widget1 = new TestWidget(); + + layout->setAnchor(layout, Qt::AnchorLeft, widget1, Qt::AnchorLeft, 0.1); + layout->setAnchor(layout, Qt::AnchorTop, widget1, Qt::AnchorTop, 0.1); + layout->setAnchor(widget1, Qt::AnchorRight, layout, Qt::AnchorRight, 0.1); + layout->setAnchor(widget1, Qt::AnchorBottom, layout, Qt::AnchorBottom, 0.1); + + layout->setAnchor(widget1, Qt::AnchorTop, layout, Qt::AnchorBottom, 10); + + widget->setLayout(layout); + + widget->setGeometry(QRectF(0,0,100,100)); + QCOMPARE(layout->isValid(), true); + delete widget; + } + + // Underconstrained two widgets, valid + { + QGraphicsWidget *widget = new QGraphicsWidget; + TheAnchorLayout *layout = new TheAnchorLayout(); + + TestWidget *widget1 = new TestWidget(); + TestWidget *widget2 = new TestWidget(); + + // Vertically the layout has floating items. Therefore, we have a conflict + layout->setAnchor(layout, Qt::AnchorLeft, widget1, Qt::AnchorLeft, 0.1); + layout->setAnchor(layout, Qt::AnchorRight, widget1, Qt::AnchorRight, -0.1); + + // Horizontally the layout has floating items. Therefore, we have a conflict + layout->setAnchor(layout, Qt::AnchorTop, widget2, Qt::AnchorTop, 0.1); + layout->setAnchor(layout, Qt::AnchorBottom, widget2, Qt::AnchorBottom, -0.1); + + widget->setLayout(layout); + + widget->setGeometry(QRectF(0,0,100,100)); + QCOMPARE(layout->isValid(), false); + delete widget; + } +} + +void tst_QGraphicsAnchorLayout1::testSpecialCases() +{ + // One widget, setLayout before defining layouts + { +#ifdef QT_DEBUG + QTest::ignoreMessage(QtWarningMsg, "QGraphicsLayout::addChildLayoutItem: QGraphicsWidget \"\"" + " in wrong parent; moved to correct parent"); +#endif + QGraphicsWidget *widget = new QGraphicsWidget; + TheAnchorLayout *layout = new TheAnchorLayout(); + widget->setLayout(layout); + + TestWidget *widget1 = new TestWidget(); + + layout->setAnchor(layout, Qt::AnchorLeft, widget1, Qt::AnchorLeft, 1); + layout->setAnchor(layout, Qt::AnchorTop, widget1, Qt::AnchorTop, 1); + layout->setAnchor(widget1, Qt::AnchorRight, layout, Qt::AnchorRight, 1); + layout->setAnchor(widget1, Qt::AnchorBottom, layout, Qt::AnchorBottom, 1); + widget->setGeometry(QRectF(0,0,100,100)); + QCOMPARE(widget1->geometry(), QRectF(1,1,98,98)); + delete widget1; + delete widget; + } + + // One widget, layout inside layout, layout inside layout inside layout + { +#ifdef QT_DEBUG + QTest::ignoreMessage(QtWarningMsg, "QGraphicsLayout::addChildLayoutItem: QGraphicsWidget \"\"" + " in wrong parent; moved to correct parent"); +#endif + QGraphicsWidget *widget = new QGraphicsWidget; + TheAnchorLayout *layout = new TheAnchorLayout(); + widget->setLayout(layout); + + TheAnchorLayout *layout1 = new TheAnchorLayout(); + TestWidget *widget1 = new TestWidget(); + layout1->setAnchor(layout1, Qt::AnchorLeft, widget1, Qt::AnchorLeft, 1); + layout1->setAnchor(layout1, Qt::AnchorTop, widget1, Qt::AnchorTop, 1); + layout1->setAnchor(widget1, Qt::AnchorRight, layout1, Qt::AnchorRight, 1); + layout1->setAnchor(widget1, Qt::AnchorBottom, layout1, Qt::AnchorBottom, 1); + + TheAnchorLayout *layout2 = new TheAnchorLayout(); + TestWidget *widget2 = new TestWidget(); + layout2->setAnchor(layout2, Qt::AnchorLeft, widget2, Qt::AnchorLeft, 1); + layout2->setAnchor(layout2, Qt::AnchorTop, widget2, Qt::AnchorTop, 1); + layout2->setAnchor(widget2, Qt::AnchorRight, layout2, Qt::AnchorRight, 1); + layout2->setAnchor(widget2, Qt::AnchorBottom, layout2, Qt::AnchorBottom, 1); + + layout1->setAnchor(layout1, Qt::AnchorLeft, layout2, Qt::AnchorLeft, 1); + layout1->setAnchor(layout1, Qt::AnchorTop, layout2, Qt::AnchorTop, 1); + layout1->setAnchor(layout2, Qt::AnchorRight, layout1, Qt::AnchorRight, 1); + layout1->setAnchor(layout2, Qt::AnchorBottom, layout1, Qt::AnchorBottom, 1); + + layout->setAnchor(layout, Qt::AnchorLeft, layout1, Qt::AnchorLeft, 1); + layout->setAnchor(layout, Qt::AnchorTop, layout1, Qt::AnchorTop, 1); + layout->setAnchor(layout1, Qt::AnchorRight, layout, Qt::AnchorRight, 1); + layout->setAnchor(layout1, Qt::AnchorBottom, layout, Qt::AnchorBottom, 1); + + // remove and add again to improve test coverage. + layout->removeItem(layout1); + + layout->setAnchor(layout, Qt::AnchorLeft, layout1, Qt::AnchorLeft, 1); + layout->setAnchor(layout, Qt::AnchorTop, layout1, Qt::AnchorTop, 1); + layout->setAnchor(layout1, Qt::AnchorRight, layout, Qt::AnchorRight, 1); + layout->setAnchor(layout1, Qt::AnchorBottom, layout, Qt::AnchorBottom, 1); + + widget->setGeometry(QRectF(0,0,100,100)); + QCOMPARE(widget1->geometry(), QRectF(2,2,96,96)); + QCOMPARE(widget2->geometry(), QRectF(3,3,94,94)); + delete widget; + } + + // One widget, layout inside layout, setLayout after layout definition + { + QGraphicsWidget *widget = new QGraphicsWidget; + TheAnchorLayout *layout = new TheAnchorLayout(); + + TheAnchorLayout *layout1 = new TheAnchorLayout(); + + TestWidget *widget1 = new TestWidget(); + layout1->setAnchor(layout1, Qt::AnchorLeft, widget1, Qt::AnchorLeft, 1); + layout1->setAnchor(layout1, Qt::AnchorTop, widget1, Qt::AnchorTop, 1); + layout1->setAnchor(widget1, Qt::AnchorRight, layout1, Qt::AnchorRight, 1); + layout1->setAnchor(widget1, Qt::AnchorBottom, layout1, Qt::AnchorBottom, 1); + + layout->setAnchor(layout, Qt::AnchorLeft, layout1, Qt::AnchorLeft, 1); + layout->setAnchor(layout, Qt::AnchorTop, layout1, Qt::AnchorTop, 1); + layout->setAnchor(layout1, Qt::AnchorRight, layout, Qt::AnchorRight, 1); + layout->setAnchor(layout1, Qt::AnchorBottom, layout, Qt::AnchorBottom, 1); + + widget->setLayout(layout); + widget->setGeometry(QRectF(0,0,100,100)); + QCOMPARE(widget1->geometry(), QRectF(2,2,96,96)); + delete widget; + } + + // One widget, layout inside layout, setLayout after layout definition, widget transferred from + // one layout to another + { + QGraphicsWidget *widget = new QGraphicsWidget; + TheAnchorLayout *layout = new TheAnchorLayout(); + widget->setLayout(layout); + + TheAnchorLayout *layout1 = new TheAnchorLayout(); + TestWidget *widget1 = new TestWidget(); + + // Additional layout + widget to improve coverage. + TheAnchorLayout *layout0 = new TheAnchorLayout(); + TestWidget *widget0 = new TestWidget(); + + // widget0 to layout0 + layout0->setAnchor(layout0, Qt::AnchorLeft, widget0, Qt::AnchorLeft, 1); + layout0->setAnchor(layout0, Qt::AnchorTop, widget0, Qt::AnchorTop, 1); + layout0->setAnchor(widget0, Qt::AnchorRight, layout0, Qt::AnchorRight, 1); + layout0->setAnchor(widget0, Qt::AnchorBottom, layout0, Qt::AnchorBottom, 1); + + // layout0 to layout + layout->setAnchor(layout, Qt::AnchorLeft, layout0, Qt::AnchorLeft, 1); + layout->setAnchor(layout, Qt::AnchorTop, layout0, Qt::AnchorTop, 1); + layout->setAnchor(layout0, Qt::AnchorRight, layout, Qt::AnchorRight, 50); + layout->setAnchor(layout0, Qt::AnchorBottom, layout, Qt::AnchorBottom, 1); + + // widget1 to layout1 + layout1->setAnchor(layout1, Qt::AnchorLeft, widget1, Qt::AnchorLeft, 1); + layout1->setAnchor(layout1, Qt::AnchorTop, widget1, Qt::AnchorTop, 1); + layout1->setAnchor(widget1, Qt::AnchorRight, layout1, Qt::AnchorRight, 1); + layout1->setAnchor(widget1, Qt::AnchorBottom, layout1, Qt::AnchorBottom, 1); + + // layout1 to layout + layout->setAnchor(layout, Qt::AnchorLeft, layout1, Qt::AnchorLeft, 1); + layout->setAnchor(layout, Qt::AnchorTop, layout1, Qt::AnchorTop, 1); + layout->setAnchor(layout1, Qt::AnchorRight, layout, Qt::AnchorRight, 50); + layout->setAnchor(layout1, Qt::AnchorBottom, layout, Qt::AnchorBottom, 1); + + TheAnchorLayout *layout2 = new TheAnchorLayout(); + + // layout2 to layout + layout->setAnchor(layout, Qt::AnchorLeft, layout2, Qt::AnchorLeft, 50); + layout->setAnchor(layout, Qt::AnchorTop, layout2, Qt::AnchorTop, 1); + layout->setAnchor(layout2, Qt::AnchorRight, layout, Qt::AnchorRight, 1); + layout->setAnchor(layout2, Qt::AnchorBottom, layout, Qt::AnchorBottom, 1); + + // transfer widget1 to layout2 + layout2->setAnchor(layout2, Qt::AnchorLeft, widget1, Qt::AnchorLeft, 1); + layout2->setAnchor(layout2, Qt::AnchorTop, widget1, Qt::AnchorTop, 1); + layout2->setAnchor(widget1, Qt::AnchorRight, layout2, Qt::AnchorRight, 1); + layout2->setAnchor(widget1, Qt::AnchorBottom, layout2, Qt::AnchorBottom, 1); + + widget->setGeometry(QRectF(0,0,100,100)); + QCOMPARE(widget1->geometry(), QRectF(51,2,47,96)); + delete widget; + } + + // One widget, set first to one layout then to another. Child reparented. + // In addition widget as a direct child of another widget. Child reparented. + { + QGraphicsWidget *widget1 = new QGraphicsWidget; + TheAnchorLayout *layout1 = new TheAnchorLayout(); + widget1->setLayout(layout1); + + TestWidget *childWidget = new TestWidget(); + + // childWidget to layout1 + layout1->setAnchor(layout1, Qt::AnchorLeft, childWidget, Qt::AnchorLeft, 1); + layout1->setAnchor(layout1, Qt::AnchorTop, childWidget, Qt::AnchorTop, 1); + layout1->setAnchor(childWidget, Qt::AnchorRight, layout1, Qt::AnchorRight, 1); + layout1->setAnchor(childWidget, Qt::AnchorBottom, layout1, Qt::AnchorBottom, 1); + + widget1->setGeometry(QRectF(0,0,100,100)); + QCOMPARE(childWidget->geometry(), QRectF(1,1,98,98)); + QVERIFY(childWidget->parentLayoutItem() == layout1); + QGraphicsWidget *widget2 = new QGraphicsWidget; + TheAnchorLayout *layout2 = new TheAnchorLayout(); + widget2->setLayout(layout2); + + // childWidget to layout2 + layout2->setAnchor(layout2, Qt::AnchorLeft, childWidget, Qt::AnchorLeft, 1); + layout2->setAnchor(layout2, Qt::AnchorTop, childWidget, Qt::AnchorTop, 1); + layout2->setAnchor(childWidget, Qt::AnchorRight, layout2, Qt::AnchorRight, 1); + layout2->setAnchor(childWidget, Qt::AnchorBottom, layout2, Qt::AnchorBottom, 1); + + QGraphicsWidget *widget3 = new QGraphicsWidget; + QGraphicsWidget *widget4 = new QGraphicsWidget; + // widget4 is a direct child of widget3 (i.e. not in any layout) + widget4->setParentItem(widget3); + + // widget4 to layout2 + layout2->setAnchor(layout2, Qt::AnchorLeft, widget4, Qt::AnchorLeft, 1); + layout2->setAnchor(layout2, Qt::AnchorTop, widget4, Qt::AnchorTop, 1); + layout2->setAnchor(widget4, Qt::AnchorRight, layout2, Qt::AnchorRight, 1); + layout2->setAnchor(widget4, Qt::AnchorBottom, layout2, Qt::AnchorBottom, 1); + + widget2->setGeometry(QRectF(0,0,100,100)); + QCOMPARE(childWidget->geometry(), QRectF(1,1,98,98)); + QVERIFY(childWidget->parentLayoutItem() == layout2); |