From 211e434a05c666e172b2f1e2f72c7695adac52a1 Mon Sep 17 00:00:00 2001 From: Stephen Kelly Date: Sat, 10 Dec 2011 19:13:51 +0100 Subject: Move proxy and selection models to QtCore. Change-Id: I71097855cb9e28105238e496778f29f99f7fc84e Reviewed-by: Thiago Macieira Reviewed-by: Lars Knoll --- tests/auto/corelib/itemmodels/itemmodels.pro | 7 +- .../qabstractitemmodel/tst_qabstractitemmodel.cpp | 4 +- .../itemmodels/qabstractproxymodel/.gitignore | 1 + .../qabstractproxymodel/qabstractproxymodel.pro | 4 + .../tst_qabstractproxymodel.cpp | 449 +++ .../qidentityproxymodel/qidentityproxymodel.pro | 8 + .../tst_qidentityproxymodel.cpp | 330 ++ .../itemmodels/qitemselectionmodel/.gitignore | 1 + .../qitemselectionmodel/qitemselectionmodel.pro | 6 + .../tst_qitemselectionmodel.cpp | 2752 ++++++++++++++++ .../itemmodels/qsortfilterproxymodel/.gitignore | 1 + .../qsortfilterproxymodel.pro | 9 + .../tst_qsortfilterproxymodel.cpp | 3463 ++++++++++++++++++++ .../corelib/itemmodels/qstringlistmodel/.gitignore | 1 + .../itemmodels/qstringlistmodel/qmodellistener.h | 75 + .../qstringlistmodel/qstringlistmodel.pro | 9 + .../qstringlistmodel/tst_qstringlistmodel.cpp | 283 ++ 17 files changed, 7400 insertions(+), 3 deletions(-) create mode 100644 tests/auto/corelib/itemmodels/qabstractproxymodel/.gitignore create mode 100644 tests/auto/corelib/itemmodels/qabstractproxymodel/qabstractproxymodel.pro create mode 100644 tests/auto/corelib/itemmodels/qabstractproxymodel/tst_qabstractproxymodel.cpp create mode 100644 tests/auto/corelib/itemmodels/qidentityproxymodel/qidentityproxymodel.pro create mode 100644 tests/auto/corelib/itemmodels/qidentityproxymodel/tst_qidentityproxymodel.cpp create mode 100644 tests/auto/corelib/itemmodels/qitemselectionmodel/.gitignore create mode 100644 tests/auto/corelib/itemmodels/qitemselectionmodel/qitemselectionmodel.pro create mode 100644 tests/auto/corelib/itemmodels/qitemselectionmodel/tst_qitemselectionmodel.cpp create mode 100644 tests/auto/corelib/itemmodels/qsortfilterproxymodel/.gitignore create mode 100644 tests/auto/corelib/itemmodels/qsortfilterproxymodel/qsortfilterproxymodel.pro create mode 100644 tests/auto/corelib/itemmodels/qsortfilterproxymodel/tst_qsortfilterproxymodel.cpp create mode 100644 tests/auto/corelib/itemmodels/qstringlistmodel/.gitignore create mode 100644 tests/auto/corelib/itemmodels/qstringlistmodel/qmodellistener.h create mode 100644 tests/auto/corelib/itemmodels/qstringlistmodel/qstringlistmodel.pro create mode 100644 tests/auto/corelib/itemmodels/qstringlistmodel/tst_qstringlistmodel.cpp (limited to 'tests/auto/corelib/itemmodels') diff --git a/tests/auto/corelib/itemmodels/itemmodels.pro b/tests/auto/corelib/itemmodels/itemmodels.pro index c11a1f120b..c5124c3241 100644 --- a/tests/auto/corelib/itemmodels/itemmodels.pro +++ b/tests/auto/corelib/itemmodels/itemmodels.pro @@ -1,5 +1,10 @@ TEMPLATE=subdirs -SUBDIRS = qabstractitemmodel +SUBDIRS = qabstractitemmodel \ + qabstractproxymodel \ + qidentityproxymodel \ + qitemselectionmodel \ + qsortfilterproxymodel \ + qstringlistmodel mac: qabstractitemmodel.CONFIG = no_check_target # QTBUG-22748 diff --git a/tests/auto/corelib/itemmodels/qabstractitemmodel/tst_qabstractitemmodel.cpp b/tests/auto/corelib/itemmodels/qabstractitemmodel/tst_qabstractitemmodel.cpp index 2340c86207..199d96c0d1 100644 --- a/tests/auto/corelib/itemmodels/qabstractitemmodel/tst_qabstractitemmodel.cpp +++ b/tests/auto/corelib/itemmodels/qabstractitemmodel/tst_qabstractitemmodel.cpp @@ -42,8 +42,8 @@ #include #include -#include -#include +#include +#include #include "dynamictreemodel.h" diff --git a/tests/auto/corelib/itemmodels/qabstractproxymodel/.gitignore b/tests/auto/corelib/itemmodels/qabstractproxymodel/.gitignore new file mode 100644 index 0000000000..bffc04d632 --- /dev/null +++ b/tests/auto/corelib/itemmodels/qabstractproxymodel/.gitignore @@ -0,0 +1 @@ +tst_qabstractproxymodel diff --git a/tests/auto/corelib/itemmodels/qabstractproxymodel/qabstractproxymodel.pro b/tests/auto/corelib/itemmodels/qabstractproxymodel/qabstractproxymodel.pro new file mode 100644 index 0000000000..5ded15ad5c --- /dev/null +++ b/tests/auto/corelib/itemmodels/qabstractproxymodel/qabstractproxymodel.pro @@ -0,0 +1,4 @@ +CONFIG += testcase +TARGET = tst_qabstractproxymodel +QT += widgets testlib +SOURCES += tst_qabstractproxymodel.cpp diff --git a/tests/auto/corelib/itemmodels/qabstractproxymodel/tst_qabstractproxymodel.cpp b/tests/auto/corelib/itemmodels/qabstractproxymodel/tst_qabstractproxymodel.cpp new file mode 100644 index 0000000000..b6557c45ec --- /dev/null +++ b/tests/auto/corelib/itemmodels/qabstractproxymodel/tst_qabstractproxymodel.cpp @@ -0,0 +1,449 @@ +/**************************************************************************** +** +** 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 +#include +#include +#include + +class tst_QAbstractProxyModel : public QObject +{ + Q_OBJECT + +public slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + +private slots: + void qabstractproxymodel_data(); + void qabstractproxymodel(); + void data_data(); + void data(); + void flags_data(); + void flags(); + void headerData_data(); + void headerData(); + void itemData_data(); + void itemData(); + void mapFromSource_data(); + void mapFromSource(); + void mapSelectionFromSource_data(); + void mapSelectionFromSource(); + void mapSelectionToSource_data(); + void mapSelectionToSource(); + void mapToSource_data(); + void mapToSource(); + void revert_data(); + void revert(); + void setSourceModel_data(); + void setSourceModel(); + void submit_data(); + void submit(); + void testRoleNames(); +}; + +// Subclass that exposes the protected functions. +class SubQAbstractProxyModel : public QAbstractProxyModel +{ +public: + // QAbstractProxyModel::mapFromSource is a pure virtual function. + QModelIndex mapFromSource(QModelIndex const& sourceIndex) const + { Q_UNUSED(sourceIndex); return QModelIndex(); } + + // QAbstractProxyModel::mapToSource is a pure virtual function. + QModelIndex mapToSource(QModelIndex const& proxyIndex) const + { Q_UNUSED(proxyIndex); return QModelIndex(); } + + QModelIndex index(int, int, const QModelIndex&) const + { + return QModelIndex(); + } + + QModelIndex parent(const QModelIndex&) const + { + return QModelIndex(); + } + + int rowCount(const QModelIndex&) const + { + return 0; + } + + int columnCount(const QModelIndex&) const + { + return 0; + } +}; + +// This will be called before the first test function is executed. +// It is only called once. +void tst_QAbstractProxyModel::initTestCase() +{ +} + +// This will be called after the last test function is executed. +// It is only called once. +void tst_QAbstractProxyModel::cleanupTestCase() +{ +} + +// This will be called before each test function is executed. +void tst_QAbstractProxyModel::init() +{ +} + +// This will be called after every test function. +void tst_QAbstractProxyModel::cleanup() +{ +} + +void tst_QAbstractProxyModel::qabstractproxymodel_data() +{ +} + +void tst_QAbstractProxyModel::qabstractproxymodel() +{ + SubQAbstractProxyModel model; + model.data(QModelIndex()); + model.flags(QModelIndex()); + model.headerData(0, Qt::Vertical, 0); + model.itemData(QModelIndex()); + model.mapFromSource(QModelIndex()); + model.mapSelectionFromSource(QItemSelection()); + model.mapSelectionToSource(QItemSelection()); + model.mapToSource(QModelIndex()); + model.revert(); + model.setSourceModel(0); + QCOMPARE(model.sourceModel(), (QAbstractItemModel*)0); + model.submit(); +} + +Q_DECLARE_METATYPE(QVariant) +Q_DECLARE_METATYPE(QModelIndex) +void tst_QAbstractProxyModel::data_data() +{ + QTest::addColumn("proxyIndex"); + QTest::addColumn("role"); + QTest::addColumn("data"); + QTest::newRow("null") << QModelIndex() << 0 << QVariant(); +} + +// public QVariant data(QModelIndex const& proxyIndex, int role = Qt::DisplayRole) const +void tst_QAbstractProxyModel::data() +{ + QFETCH(QModelIndex, proxyIndex); + QFETCH(int, role); + QFETCH(QVariant, data); + + SubQAbstractProxyModel model; + QCOMPARE(model.data(proxyIndex, role), data); +} + +Q_DECLARE_METATYPE(Qt::ItemFlags) +void tst_QAbstractProxyModel::flags_data() +{ + QTest::addColumn("index"); + QTest::addColumn("flags"); + QTest::newRow("null") << QModelIndex() << (Qt::ItemFlags)0; +} + +// public Qt::ItemFlags flags(QModelIndex const& index) const +void tst_QAbstractProxyModel::flags() +{ + QFETCH(QModelIndex, index); + QFETCH(Qt::ItemFlags, flags); + + SubQAbstractProxyModel model; + QCOMPARE(model.flags(index), flags); +} + +Q_DECLARE_METATYPE(Qt::Orientation) +Q_DECLARE_METATYPE(Qt::ItemDataRole) +void tst_QAbstractProxyModel::headerData_data() +{ + QTest::addColumn("section"); + QTest::addColumn("orientation"); + QTest::addColumn("role"); + QTest::addColumn("headerData"); + QTest::newRow("null") << 0 << Qt::Vertical << Qt::UserRole << QVariant(); +} + +// public QVariant headerData(int section, Qt::Orientation orientation, int role) const +void tst_QAbstractProxyModel::headerData() +{ + QFETCH(int, section); + QFETCH(Qt::Orientation, orientation); + QFETCH(Qt::ItemDataRole, role); + QFETCH(QVariant, headerData); + + SubQAbstractProxyModel model; + QCOMPARE(model.headerData(section, orientation, role), headerData); +} + +void tst_QAbstractProxyModel::itemData_data() +{ + QTest::addColumn("index"); + QTest::addColumn("count"); + + QTest::newRow("null") << QModelIndex() << 0; +} + +// public QMap itemData(QModelIndex const& index) const +void tst_QAbstractProxyModel::itemData() +{ + QFETCH(QModelIndex, index); + QFETCH(int, count); + SubQAbstractProxyModel model; + QCOMPARE(model.itemData(index).count(), count); +} + +void tst_QAbstractProxyModel::mapFromSource_data() +{ + QTest::addColumn("sourceIndex"); + QTest::addColumn("mapFromSource"); + QTest::newRow("null") << QModelIndex() << QModelIndex(); +} + +// public QModelIndex mapFromSource(QModelIndex const& sourceIndex) const +void tst_QAbstractProxyModel::mapFromSource() +{ + QFETCH(QModelIndex, sourceIndex); + QFETCH(QModelIndex, mapFromSource); + + SubQAbstractProxyModel model; + QCOMPARE(model.mapFromSource(sourceIndex), mapFromSource); +} + +Q_DECLARE_METATYPE(QItemSelection) +void tst_QAbstractProxyModel::mapSelectionFromSource_data() +{ + QTest::addColumn("selection"); + QTest::addColumn("mapSelectionFromSource"); + QTest::newRow("null") << QItemSelection() << QItemSelection(); + QTest::newRow("empty") << QItemSelection(QModelIndex(), QModelIndex()) << QItemSelection(QModelIndex(), QModelIndex()); +} + +// public QItemSelection mapSelectionFromSource(QItemSelection const& selection) const +void tst_QAbstractProxyModel::mapSelectionFromSource() +{ + QFETCH(QItemSelection, selection); + QFETCH(QItemSelection, mapSelectionFromSource); + + SubQAbstractProxyModel model; + QCOMPARE(model.mapSelectionFromSource(selection), mapSelectionFromSource); +} + +void tst_QAbstractProxyModel::mapSelectionToSource_data() +{ + QTest::addColumn("selection"); + QTest::addColumn("mapSelectionToSource"); + QTest::newRow("null") << QItemSelection() << QItemSelection(); + QTest::newRow("empty") << QItemSelection(QModelIndex(), QModelIndex()) << QItemSelection(QModelIndex(), QModelIndex()); +} + +// public QItemSelection mapSelectionToSource(QItemSelection const& selection) const +void tst_QAbstractProxyModel::mapSelectionToSource() +{ + QFETCH(QItemSelection, selection); + QFETCH(QItemSelection, mapSelectionToSource); + + SubQAbstractProxyModel model; + QCOMPARE(model.mapSelectionToSource(selection), mapSelectionToSource); +} + +void tst_QAbstractProxyModel::mapToSource_data() +{ + QTest::addColumn("proxyIndex"); + QTest::addColumn("mapToSource"); + QTest::newRow("null") << QModelIndex() << QModelIndex(); +} + +// public QModelIndex mapToSource(QModelIndex const& proxyIndex) const +void tst_QAbstractProxyModel::mapToSource() +{ + QFETCH(QModelIndex, proxyIndex); + QFETCH(QModelIndex, mapToSource); + + SubQAbstractProxyModel model; + QCOMPARE(model.mapToSource(proxyIndex), mapToSource); +} + +void tst_QAbstractProxyModel::revert_data() +{ + //QTest::addColumn("foo"); + //QTest::newRow("null") << 0; +} + +// public void revert() +void tst_QAbstractProxyModel::revert() +{ + //QFETCH(int, foo); + + SubQAbstractProxyModel model; + model.revert(); +} + +void tst_QAbstractProxyModel::setSourceModel_data() +{ + //QTest::addColumn("sourceModelCount"); + //QTest::newRow("null") << 0; +} + +// public void setSourceModel(QAbstractItemModel* sourceModel) +void tst_QAbstractProxyModel::setSourceModel() +{ + //QFETCH(int, sourceModelCount); + + SubQAbstractProxyModel model; + QStandardItemModel *sourceModel = new QStandardItemModel(&model); + model.setSourceModel(sourceModel); + QCOMPARE(model.sourceModel(), static_cast(sourceModel)); + + QStandardItemModel *sourceModel2 = new QStandardItemModel(&model); + model.setSourceModel(sourceModel2); + QCOMPARE(model.sourceModel(), static_cast(sourceModel2)); + + delete sourceModel2; + QCOMPARE(model.sourceModel(), static_cast(0)); +} + +void tst_QAbstractProxyModel::submit_data() +{ + QTest::addColumn("submit"); + QTest::newRow("null") << true; +} + +// public bool submit() +void tst_QAbstractProxyModel::submit() +{ + QFETCH(bool, submit); + + SubQAbstractProxyModel model; + QCOMPARE(model.submit(), submit); +} + +class StandardItemModelWithCustomRoleNames : public QStandardItemModel +{ +public: + enum CustomRole { + CustomRole1 = Qt::UserRole, + CustomRole2 + }; + + StandardItemModelWithCustomRoleNames() { + QHash _roleNames = roleNames(); + _roleNames.insert(CustomRole1, "custom1"); + _roleNames.insert(CustomRole2, "custom2"); + setRoleNames(_roleNames); + } +}; + +class AnotherStandardItemModelWithCustomRoleNames : public QStandardItemModel +{ + public: + enum CustomRole { + AnotherCustomRole1 = Qt::UserRole + 10, // Different to StandardItemModelWithCustomRoleNames::CustomRole1 + AnotherCustomRole2 + }; + + AnotherStandardItemModelWithCustomRoleNames() { + QHash _roleNames = roleNames(); + _roleNames.insert(AnotherCustomRole1, "another_custom1"); + _roleNames.insert(AnotherCustomRole2, "another_custom2"); + setRoleNames(_roleNames); + } +}; + +/** + Verifies that @p subSet is a subset of @p superSet. That is, all keys in @p subSet exist in @p superSet and have the same values. +*/ +static void verifySubSetOf(const QHash &superSet, const QHash &subSet) +{ + QHash::const_iterator it = subSet.constBegin(); + const QHash::const_iterator end = subSet.constEnd(); + for ( ; it != end; ++it ) { + QVERIFY(superSet.contains(it.key())); + QVERIFY(it.value() == superSet.value(it.key())); + } +} + +void tst_QAbstractProxyModel::testRoleNames() +{ + QStandardItemModel defaultModel; + StandardItemModelWithCustomRoleNames model; + QHash rootModelRoleNames = model.roleNames(); + QHash defaultModelRoleNames = defaultModel.roleNames(); + + verifySubSetOf( rootModelRoleNames, defaultModelRoleNames); + QVERIFY( rootModelRoleNames.size() == defaultModelRoleNames.size() + 2 ); + QVERIFY( rootModelRoleNames.contains(StandardItemModelWithCustomRoleNames::CustomRole1)); + QVERIFY( rootModelRoleNames.contains(StandardItemModelWithCustomRoleNames::CustomRole2)); + QVERIFY( rootModelRoleNames.value(StandardItemModelWithCustomRoleNames::CustomRole1) == "custom1" ); + QVERIFY( rootModelRoleNames.value(StandardItemModelWithCustomRoleNames::CustomRole2) == "custom2" ); + + SubQAbstractProxyModel proxy1; + proxy1.setSourceModel(&model); + QHash proxy1RoleNames = proxy1.roleNames(); + verifySubSetOf( proxy1RoleNames, defaultModelRoleNames ); + QVERIFY( proxy1RoleNames.size() == defaultModelRoleNames.size() + 2 ); + QVERIFY( proxy1RoleNames.contains(StandardItemModelWithCustomRoleNames::CustomRole1)); + QVERIFY( proxy1RoleNames.contains(StandardItemModelWithCustomRoleNames::CustomRole2)); + QVERIFY( proxy1RoleNames.value(StandardItemModelWithCustomRoleNames::CustomRole1) == "custom1" ); + QVERIFY( proxy1RoleNames.value(StandardItemModelWithCustomRoleNames::CustomRole2) == "custom2" ); + + SubQAbstractProxyModel proxy2; + proxy2.setSourceModel(&proxy1); + QHash proxy2RoleNames = proxy2.roleNames(); + verifySubSetOf( proxy2RoleNames, defaultModelRoleNames ); + QVERIFY( proxy2RoleNames.size() == defaultModelRoleNames.size() + 2 ); + QVERIFY( proxy2RoleNames.contains(StandardItemModelWithCustomRoleNames::CustomRole1)); + QVERIFY( proxy2RoleNames.contains(StandardItemModelWithCustomRoleNames::CustomRole2)); + QVERIFY( proxy2RoleNames.value(StandardItemModelWithCustomRoleNames::CustomRole1) == "custom1" ); + QVERIFY( proxy2RoleNames.value(StandardItemModelWithCustomRoleNames::CustomRole2) == "custom2" ); + +} + +QTEST_MAIN(tst_QAbstractProxyModel) +#include "tst_qabstractproxymodel.moc" + diff --git a/tests/auto/corelib/itemmodels/qidentityproxymodel/qidentityproxymodel.pro b/tests/auto/corelib/itemmodels/qidentityproxymodel/qidentityproxymodel.pro new file mode 100644 index 0000000000..4fb8c98fe7 --- /dev/null +++ b/tests/auto/corelib/itemmodels/qidentityproxymodel/qidentityproxymodel.pro @@ -0,0 +1,8 @@ +CONFIG += testcase +TARGET = tst_qidentityproxymodel + +mtdir = ../../../other/modeltest +INCLUDEPATH += $$PWD/$${mtdir} +QT += widgets testlib +SOURCES += tst_qidentityproxymodel.cpp $${mtdir}/dynamictreemodel.cpp $${mtdir}/modeltest.cpp +HEADERS += $${mtdir}/dynamictreemodel.h $${mtdir}/modeltest.h diff --git a/tests/auto/corelib/itemmodels/qidentityproxymodel/tst_qidentityproxymodel.cpp b/tests/auto/corelib/itemmodels/qidentityproxymodel/tst_qidentityproxymodel.cpp new file mode 100644 index 0000000000..86ff00f9d4 --- /dev/null +++ b/tests/auto/corelib/itemmodels/qidentityproxymodel/tst_qidentityproxymodel.cpp @@ -0,0 +1,330 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Stephen Kelly +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module 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 +#include +#include +#include + +#include "dynamictreemodel.h" +#include "qidentityproxymodel.h" + +Q_DECLARE_METATYPE(QModelIndex) + +class tst_QIdentityProxyModel : public QObject +{ + Q_OBJECT + +public: + + tst_QIdentityProxyModel(); + virtual ~tst_QIdentityProxyModel(); + +public slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + +private slots: + void insertRows(); + void removeRows(); + void moveRows(); + void reset(); + +protected: + void verifyIdentity(QAbstractItemModel *model, const QModelIndex &parent = QModelIndex()); + +private: + QStandardItemModel *m_model; + QIdentityProxyModel *m_proxy; +}; + +tst_QIdentityProxyModel::tst_QIdentityProxyModel() + : m_model(0), m_proxy(0) +{ + +} + +tst_QIdentityProxyModel::~tst_QIdentityProxyModel() +{ + +} + +void tst_QIdentityProxyModel::initTestCase() +{ + qRegisterMetaType("QModelIndex"); + + m_model = new QStandardItemModel(0, 1); + m_proxy = new QIdentityProxyModel(); +} + +void tst_QIdentityProxyModel::cleanupTestCase() +{ + delete m_proxy; + delete m_model; +} + +void tst_QIdentityProxyModel::init() +{ +} + +void tst_QIdentityProxyModel::cleanup() +{ + m_model->clear(); + m_model->insertColumns(0, 1); +} + +void tst_QIdentityProxyModel::verifyIdentity(QAbstractItemModel *model, const QModelIndex &parent) +{ + const int rows = model->rowCount(parent); + const int columns = model->columnCount(parent); + const QModelIndex proxyParent = m_proxy->mapFromSource(parent); + + QVERIFY(m_proxy->mapToSource(proxyParent) == parent); + QVERIFY(rows == m_proxy->rowCount(proxyParent)); + QVERIFY(columns == m_proxy->columnCount(proxyParent)); + + for (int row = 0; row < rows; ++row) { + for (int column = 0; column < columns; ++column) { + const QModelIndex idx = model->index(row, column, parent); + const QModelIndex proxyIdx = m_proxy->mapFromSource(idx); + QVERIFY(proxyIdx.model() == m_proxy); + QVERIFY(m_proxy->mapToSource(proxyIdx) == idx); + QVERIFY(proxyIdx.isValid()); + QVERIFY(proxyIdx.row() == row); + QVERIFY(proxyIdx.column() == column); + QVERIFY(proxyIdx.parent() == proxyParent); + QVERIFY(proxyIdx.data() == idx.data()); + QVERIFY(proxyIdx.flags() == idx.flags()); + const int childCount = m_proxy->rowCount(proxyIdx); + const bool hasChildren = m_proxy->hasChildren(proxyIdx); + QVERIFY(model->hasChildren(idx) == hasChildren); + QVERIFY((childCount > 0) == hasChildren); + + if (hasChildren) + verifyIdentity(model, idx); + } + } +} + +/* + tests +*/ + +void tst_QIdentityProxyModel::insertRows() +{ + QStandardItem *parentItem = m_model->invisibleRootItem(); + for (int i = 0; i < 4; ++i) { + QStandardItem *item = new QStandardItem(QString("item %0").arg(i)); + parentItem->appendRow(item); + parentItem = item; + } + + m_proxy->setSourceModel(m_model); + + verifyIdentity(m_model); + + QSignalSpy modelBeforeSpy(m_model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int))); + QSignalSpy modelAfterSpy(m_model, SIGNAL(rowsInserted(QModelIndex,int,int))); + QSignalSpy proxyBeforeSpy(m_proxy, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int))); + QSignalSpy proxyAfterSpy(m_proxy, SIGNAL(rowsInserted(QModelIndex,int,int))); + + QStandardItem *item = new QStandardItem(QString("new item")); + parentItem->appendRow(item); + + QVERIFY(modelBeforeSpy.size() == 1 && 1 == proxyBeforeSpy.size()); + QVERIFY(modelAfterSpy.size() == 1 && 1 == proxyAfterSpy.size()); + + QVERIFY(modelBeforeSpy.first().first().value() == m_proxy->mapToSource(proxyBeforeSpy.first().first().value())); + QVERIFY(modelBeforeSpy.first().at(1) == proxyBeforeSpy.first().at(1)); + QVERIFY(modelBeforeSpy.first().at(2) == proxyBeforeSpy.first().at(2)); + + QVERIFY(modelAfterSpy.first().first().value() == m_proxy->mapToSource(proxyAfterSpy.first().first().value())); + QVERIFY(modelAfterSpy.first().at(1) == proxyAfterSpy.first().at(1)); + QVERIFY(modelAfterSpy.first().at(2) == proxyAfterSpy.first().at(2)); + + verifyIdentity(m_model); + +} + +void tst_QIdentityProxyModel::removeRows() +{ + QStandardItem *parentItem = m_model->invisibleRootItem(); + for (int i = 0; i < 4; ++i) { + QStandardItem *item = new QStandardItem(QString("item %0").arg(i)); + parentItem->appendRow(item); + parentItem = item; + } + + m_proxy->setSourceModel(m_model); + + verifyIdentity(m_model); + + QSignalSpy modelBeforeSpy(m_model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int))); + QSignalSpy modelAfterSpy(m_model, SIGNAL(rowsRemoved(QModelIndex,int,int))); + QSignalSpy proxyBeforeSpy(m_proxy, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int))); + QSignalSpy proxyAfterSpy(m_proxy, SIGNAL(rowsRemoved(QModelIndex,int,int))); + + const QModelIndex topLevel = m_model->index(0, 0, QModelIndex()); + const QModelIndex secondLevel = m_model->index(0, 0, topLevel); + const QModelIndex thirdLevel = m_model->index(0, 0, secondLevel); + + QVERIFY(thirdLevel.isValid()); + + m_model->removeRow(0, secondLevel); + + QVERIFY(modelBeforeSpy.size() == 1 && 1 == proxyBeforeSpy.size()); + QVERIFY(modelAfterSpy.size() == 1 && 1 == proxyAfterSpy.size()); + + QVERIFY(modelBeforeSpy.first().first().value() == m_proxy->mapToSource(proxyBeforeSpy.first().first().value())); + QVERIFY(modelBeforeSpy.first().at(1) == proxyBeforeSpy.first().at(1)); + QVERIFY(modelBeforeSpy.first().at(2) == proxyBeforeSpy.first().at(2)); + + QVERIFY(modelAfterSpy.first().first().value() == m_proxy->mapToSource(proxyAfterSpy.first().first().value())); + QVERIFY(modelAfterSpy.first().at(1) == proxyAfterSpy.first().at(1)); + QVERIFY(modelAfterSpy.first().at(2) == proxyAfterSpy.first().at(2)); + + verifyIdentity(m_model); +} + +void tst_QIdentityProxyModel::moveRows() +{ + DynamicTreeModel model; + + { + ModelInsertCommand insertCommand(&model); + insertCommand.setStartRow(0); + insertCommand.setEndRow(9); + insertCommand.doCommand(); + } + { + ModelInsertCommand insertCommand(&model); + insertCommand.setAncestorRowNumbers(QList() << 5); + insertCommand.setStartRow(0); + insertCommand.setEndRow(9); + insertCommand.doCommand(); + } + + m_proxy->setSourceModel(&model); + + verifyIdentity(&model); + + QSignalSpy modelBeforeSpy(&model, SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int))); + QSignalSpy modelAfterSpy(&model, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int))); + QSignalSpy proxyBeforeSpy(m_proxy, SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int))); + QSignalSpy proxyAfterSpy(m_proxy, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int))); + + { + ModelMoveCommand moveCommand(&model, 0); + moveCommand.setAncestorRowNumbers(QList() << 5); + moveCommand.setStartRow(3); + moveCommand.setEndRow(4); + moveCommand.setDestRow(1); + moveCommand.doCommand(); + } + + QVERIFY(modelBeforeSpy.size() == 1 && 1 == proxyBeforeSpy.size()); + QVERIFY(modelAfterSpy.size() == 1 && 1 == proxyAfterSpy.size()); + + QVERIFY(modelBeforeSpy.first().first().value() == m_proxy->mapToSource(proxyBeforeSpy.first().first().value())); + QVERIFY(modelBeforeSpy.first().at(1) == proxyBeforeSpy.first().at(1)); + QVERIFY(modelBeforeSpy.first().at(2) == proxyBeforeSpy.first().at(2)); + QVERIFY(modelBeforeSpy.first().at(3).value() == m_proxy->mapToSource(proxyBeforeSpy.first().at(3).value())); + QVERIFY(modelBeforeSpy.first().at(4) == proxyBeforeSpy.first().at(4)); + + QVERIFY(modelAfterSpy.first().first().value() == m_proxy->mapToSource(proxyAfterSpy.first().first().value())); + QVERIFY(modelAfterSpy.first().at(1) == proxyAfterSpy.first().at(1)); + QVERIFY(modelAfterSpy.first().at(2) == proxyAfterSpy.first().at(2)); + QVERIFY(modelAfterSpy.first().at(3).value() == m_proxy->mapToSource(proxyAfterSpy.first().at(3).value())); + QVERIFY(modelAfterSpy.first().at(4) == proxyAfterSpy.first().at(4)); + + verifyIdentity(&model); + + m_proxy->setSourceModel(0); +} + +void tst_QIdentityProxyModel::reset() +{ + DynamicTreeModel model; + + { + ModelInsertCommand insertCommand(&model); + insertCommand.setStartRow(0); + insertCommand.setEndRow(9); + insertCommand.doCommand(); + } + { + ModelInsertCommand insertCommand(&model); + insertCommand.setAncestorRowNumbers(QList() << 5); + insertCommand.setStartRow(0); + insertCommand.setEndRow(9); + insertCommand.doCommand(); + } + + m_proxy->setSourceModel(&model); + + verifyIdentity(&model); + + QSignalSpy modelBeforeSpy(&model, SIGNAL(modelAboutToBeReset())); + QSignalSpy modelAfterSpy(&model, SIGNAL(modelReset())); + QSignalSpy proxyBeforeSpy(m_proxy, SIGNAL(modelAboutToBeReset())); + QSignalSpy proxyAfterSpy(m_proxy, SIGNAL(modelReset())); + + { + ModelResetCommandFixed resetCommand(&model, 0); + resetCommand.setAncestorRowNumbers(QList() << 5); + resetCommand.setStartRow(3); + resetCommand.setEndRow(4); + resetCommand.setDestRow(1); + resetCommand.doCommand(); + } + + QVERIFY(modelBeforeSpy.size() == 1 && 1 == proxyBeforeSpy.size()); + QVERIFY(modelAfterSpy.size() == 1 && 1 == proxyAfterSpy.size()); + + verifyIdentity(&model); + m_proxy->setSourceModel(0); +} + +QTEST_MAIN(tst_QIdentityProxyModel) +#include "tst_qidentityproxymodel.moc" diff --git a/tests/auto/corelib/itemmodels/qitemselectionmodel/.gitignore b/tests/auto/corelib/itemmodels/qitemselectionmodel/.gitignore new file mode 100644 index 0000000000..aa543a200a --- /dev/null +++ b/tests/auto/corelib/itemmodels/qitemselectionmodel/.gitignore @@ -0,0 +1 @@ +tst_qitemselectionmodel diff --git a/tests/auto/corelib/itemmodels/qitemselectionmodel/qitemselectionmodel.pro b/tests/auto/corelib/itemmodels/qitemselectionmodel/qitemselectionmodel.pro new file mode 100644 index 0000000000..a4c7ba3786 --- /dev/null +++ b/tests/auto/corelib/itemmodels/qitemselectionmodel/qitemselectionmodel.pro @@ -0,0 +1,6 @@ +CONFIG += testcase +TARGET = tst_qitemselectionmodel +QT += widgets testlib +SOURCES += tst_qitemselectionmodel.cpp + + diff --git a/tests/auto/corelib/itemmodels/qitemselectionmodel/tst_qitemselectionmodel.cpp b/tests/auto/corelib/itemmodels/qitemselectionmodel/tst_qitemselectionmodel.cpp new file mode 100644 index 0000000000..2097cb31ee --- /dev/null +++ b/tests/auto/corelib/itemmodels/qitemselectionmodel/tst_qitemselectionmodel.cpp @@ -0,0 +1,2752 @@ +/**************************************************************************** +** +** 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 + +#include +#include + +class tst_QItemSelectionModel : public QObject +{ + Q_OBJECT + +public: + tst_QItemSelectionModel(); + virtual ~tst_QItemSelectionModel(); + + +public slots: + void initTestCase(); + void cleanupTestCase(); + void init(); +private slots: + void clear_data(); + void clear(); + void clearAndSelect(); + void toggleSelection(); + void select_data(); + void select(); + void persistentselections_data(); + void persistentselections(); + void resetModel(); + void removeRows_data(); + void removeRows(); + void removeColumns_data(); + void removeColumns(); + void modelLayoutChanged_data(); + void modelLayoutChanged(); + void selectedRows_data(); + void selectedRows(); + void selectedColumns_data(); + void selectedColumns(); + void setCurrentIndex(); + void splitOnInsert(); + void task196285_rowIntersectsSelection(); + void unselectable(); + void task220420_selectedIndexes(); + void task240734_layoutChanged(); + void merge_data(); + void merge(); + void task119433_isRowSelected(); + void task252069_rowIntersectsSelection(); + void task232634_childrenDeselectionSignal(); + void task260134_layoutChangedWithAllSelected(); + void QTBUG5671_layoutChangedWithAllSelected(); + void QTBUG2804_layoutChangedTreeSelection(); + void deselectRemovedMiddleRange(); + void rangeOperatorLessThan_data(); + void rangeOperatorLessThan(); + + void testDifferentModels(); + + void testValidRangesInSelectionsAfterReset(); + void testChainedSelectionClear(); + void testClearCurrentIndex(); + +private: + QAbstractItemModel *model; + QItemSelectionModel *selection; +}; + +QDataStream &operator<<(QDataStream &, const QModelIndex &); +QDataStream &operator>>(QDataStream &, QModelIndex &); +QDataStream &operator<<(QDataStream &, const QModelIndexList &); +QDataStream &operator>>(QDataStream &, QModelIndexList &); + +typedef QList IntList; +typedef QPair IntPair; +typedef QList PairList; + + +Q_DECLARE_METATYPE(PairList) +Q_DECLARE_METATYPE(QModelIndex) +Q_DECLARE_METATYPE(QModelIndexList) +Q_DECLARE_METATYPE(IntList) +Q_DECLARE_METATYPE(QItemSelection) + +class QStreamHelper: public QAbstractItemModel +{ +public: + QStreamHelper() {} + static QModelIndex create(int row = -1, int column = -1, void *data = 0) + { + QStreamHelper helper; + return helper.QAbstractItemModel::createIndex(row, column, data); + } + + QModelIndex index(int, int, const QModelIndex&) const + { return QModelIndex(); } + QModelIndex parent(const QModelIndex&) const + { return QModelIndex(); } + int rowCount(const QModelIndex & = QModelIndex()) const + { return 0; } + int columnCount(const QModelIndex & = QModelIndex()) const + { return 0; } + QVariant data(const QModelIndex &, int = Qt::DisplayRole) const + { return QVariant(); } + bool hasChildren(const QModelIndex &) const + { return false; } +}; + +QDataStream &operator<<(QDataStream &s, const QModelIndex &input) +{ + s << input.row() + << input.column() + << reinterpret_cast(input.internalPointer()); + return s; +} + +QDataStream &operator>>(QDataStream &s, QModelIndex &output) +{ + int r, c; + qlonglong ptr; + s >> r; + s >> c; + s >> ptr; + output = QStreamHelper::create(r, c, reinterpret_cast(ptr)); + return s; +} + +QDataStream &operator<<(QDataStream &s, const QModelIndexList &input) +{ + s << input.count(); + for (int i=0; i>(QDataStream &s, QModelIndexList &output) +{ + QModelIndex tmpIndex; + int count; + s >> count; + for (int i=0; i> tmpIndex; + output << tmpIndex; + } + return s; +} + +tst_QItemSelectionModel::tst_QItemSelectionModel() : model(0), selection(0) +{ +} + +tst_QItemSelectionModel::~tst_QItemSelectionModel() +{ +} + +/* + This test usually uses a model with a 5x5 table + ------------------------------------------- + | 0,0 | 0,1 | 0,2 | 0,3 | 0,4 | + ------------------------------------------- + | 1,0 | 1,1 | 1,2 | 1,3 | 1,4 | + ------------------------------------------- + | 2,0 | 2,1 | 2,2 | 2,3 | 2,4 | + ------------------------------------------- + | 3,0 | 3,1 | 3,2 | 3,3 | 3,4 | + ------------------------------------------- + | 4,0 | 4,1 | 4,2 | 4,3 | 4,4 | + ------------------------------------------- + + ...that for each row has a children in a new 5x5 table ad infinitum. + +*/ +void tst_QItemSelectionModel::initTestCase() +{ + qRegisterMetaType("QItemSelection"); + + model = new QStandardItemModel(5, 5); + QModelIndex parent = model->index(0, 0, QModelIndex()); + model->insertRows(0, 5, parent); + model->insertColumns(0, 5, parent); + selection = new QItemSelectionModel(model); +} + +void tst_QItemSelectionModel::cleanupTestCase() +{ + delete selection; + delete model; +} + +void tst_QItemSelectionModel::init() +{ + selection->clear(); + while (model->rowCount(QModelIndex()) > 5) + model->removeRow(0, QModelIndex()); + while (model->rowCount(QModelIndex()) < 5) + model->insertRow(0, QModelIndex()); +} + +void tst_QItemSelectionModel::clear_data() +{ + QTest::addColumn("indexList"); + QTest::addColumn("commandList"); + { + QModelIndexList index; + IntList command; + index << model->index(0, 0, QModelIndex()); + command << (QItemSelectionModel::Select | QItemSelectionModel::Rows); + index << model->index(1, 0, QModelIndex()); + command << (QItemSelectionModel::Select | QItemSelectionModel::Rows); + QTest::newRow("(0, 0) and (1, 0): Select|Rows") + << index + << command; + } + { + QModelIndexList index; + IntList command; + index << model->index(0, 0, QModelIndex()); + command << (QItemSelectionModel::Select | QItemSelectionModel::Columns); + index << model->index(0, 1, QModelIndex()); + command << (QItemSelectionModel::Select | QItemSelectionModel::Columns); + QTest::newRow("(0, 0) and (1, 0): Select|Columns") + << index + << command; + } + { + QModelIndexList index; + IntList command; + index << model->index(0, 0, QModelIndex()); + command << QItemSelectionModel::Select; + index << model->index(1, 1, QModelIndex()); + command << QItemSelectionModel::Select; + index << model->index(2, 2, QModelIndex()); + command << QItemSelectionModel::SelectCurrent; + QTest::newRow("(0, 0), (1, 1) and (2, 2): Select, Select, SelectCurrent") + << index + << command; + } + { + QModelIndexList index; + IntList command; + index << model->index(0, 0, QModelIndex()); + command << QItemSelectionModel::Select; + index << model->index(1, 1, QModelIndex()); + command << QItemSelectionModel::Select; + index << model->index(1, 1, QModelIndex()); + command << QItemSelectionModel::Toggle; + QTest::newRow("(0, 0), (1, 1) and (1, 1): Select, Select, Toggle") + << index + << command; + } + { + QModelIndexList index; + IntList command; + index << model->index(0, 0, model->index(0, 0, QModelIndex())); + command << (QItemSelectionModel::Select | QItemSelectionModel::Rows); + QTest::newRow("child (0, 0) of (0, 0): Select|Rows") + << index + << command; + } +} + +void tst_QItemSelectionModel::clear() +{ + QFETCH(QModelIndexList, indexList); + QFETCH(IntList, commandList); + + // do selections + for (int i=0; iselect(indexList.at(i), (QItemSelectionModel::SelectionFlags)commandList.at(i)); + } + // test that we have selected items + QVERIFY(!selection->selectedIndexes().isEmpty()); + selection->clear(); + // test that they were all cleared + QVERIFY(selection->selectedIndexes().isEmpty()); +} + +void tst_QItemSelectionModel::clearAndSelect() +{ + // populate selectionmodel + selection->select(model->index(1, 1, QModelIndex()), QItemSelectionModel::Select); + QCOMPARE(selection->selectedIndexes().count(), 1); + QVERIFY(selection->hasSelection()); + + // ClearAndSelect with empty selection + QItemSelection emptySelection; + selection->select(emptySelection, QItemSelectionModel::ClearAndSelect); + + // verify the selectionmodel is empty + QVERIFY(selection->selectedIndexes().isEmpty()); + QVERIFY(selection->hasSelection()==false); +} + +void tst_QItemSelectionModel::toggleSelection() +{ + //test the toggle selection and checks whether selectedIndex + //and hasSelection returns the correct value + + selection->clearSelection(); + QCOMPARE(selection->selectedIndexes().count(), 0); + QVERIFY(selection->hasSelection()==false); + + QModelIndex index=model->index(1, 1, QModelIndex()); + // populate selectionmodel + selection->select(index, QItemSelectionModel::Toggle); + QCOMPARE(selection->selectedIndexes().count(), 1); + QVERIFY(selection->hasSelection()==true); + + selection->select(index, QItemSelectionModel::Toggle); + QCOMPARE(selection->selectedIndexes().count(), 0); + QVERIFY(selection->hasSelection()==false); + + // populate selectionmodel with rows + selection->select(index, QItemSelectionModel::Toggle | QItemSelectionModel::Rows); + QCOMPARE(selection->selectedIndexes().count(), model->columnCount()); + QVERIFY(selection->hasSelection()==true); + + selection->select(index, QItemSelectionModel::Toggle | QItemSelectionModel::Rows); + QCOMPARE(selection->selectedIndexes().count(), 0); + QVERIFY(selection->hasSelection()==false); + +} + + +void tst_QItemSelectionModel::select_data() +{ + QTest::addColumn("indexList"); + QTest::addColumn("useRanges"); + QTest::addColumn("commandList"); + QTest::addColumn("expectedList"); + + { + QModelIndexList index; + QModelIndexList expected; + IntList command; + index << model->index(0, 0, QModelIndex()); + command << QItemSelectionModel::Select; + expected << model->index(0, 0, QModelIndex()); + QTest::newRow("(0, 0): Select") + << index + << false + << command + << expected; + } + { + QModelIndexList index; + QModelIndexList expected; + IntList command; + index << model->index(0, 0, model->index(0, 0, QModelIndex())); + command << QItemSelectionModel::Select; + expected << model->index(0, 0, model->index(0, 0, QModelIndex())); + QTest::newRow("child (0, 0) of (0, 0): Select") + << index + << false + << command + << expected; + } + { + QModelIndexList index; + QModelIndexList expected; + IntList command; + index << model->index(0, 0, QModelIndex()); + command << QItemSelectionModel::Deselect; + QTest::newRow("(0, 0): Deselect") + << index + << false + << command + << expected; + } + { + QModelIndexList index; + QModelIndexList expected; + IntList command; + index << model->index(0, 0, QModelIndex()); + command << QItemSelectionModel::Toggle; + expected << model->index(0, 0, QModelIndex()); + QTest::newRow("(0, 0): Toggle") + << index + << false + << command + << expected; + } + { + QModelIndexList index; + QModelIndexList expected; + IntList command; + index << model->index(0, 0, QModelIndex()); + command << QItemSelectionModel::Select; + index << model->index(0, 0, QModelIndex()); + command << QItemSelectionModel::Toggle; + QTest::newRow("(0, 0) and (0, 0): Select and Toggle") + << index + << false + << command + << expected; + } + { + QModelIndexList index; + QModelIndexList expected; + IntList command; + index << model->index(0, 0, QModelIndex()); + command << QItemSelectionModel::Select; + index << model->index(0, 0, QModelIndex()); + command << QItemSelectionModel::Deselect; + QTest::newRow("(0, 0) and (0, 0): Select and Deselect") + << index + << false + << command + << expected; + } + { + QModelIndexList index; + QModelIndexList expected; + IntList command; + index << model->index(0, 0, QModelIndex()); + command << QItemSelectionModel::Select; + index << model->index(0, 0, model->index(0, 0, QModelIndex())); + command << QItemSelectionModel::ClearAndSelect; + expected << model->index(0, 0, model->index(0, 0, QModelIndex())); + QTest::newRow("(0, 0) and child (0, 0) of (0, 0): Select and ClearAndSelect") + << index + << false + << command + << expected; + } + { + QModelIndexList index; + QModelIndexList expected; + IntList command; + index << model->index(0, 0, QModelIndex()); + index << model->index(4, 0, QModelIndex()); + command << QItemSelectionModel::Select; + index << model->index(0, 1, QModelIndex()); + index << model->index(4, 1, QModelIndex()); + command << QItemSelectionModel::Select; + index << model->index(0, 0, QModelIndex()); + index << model->index(4, 1, QModelIndex()); + command << QItemSelectionModel::Deselect; + QTest::newRow("(0, 0 to 4, 0) and (0, 1 to 4, 1) and (0, 0 to 4, 1): Select and Select and Deselect") + << index + << true + << command + << expected; + } + { + QModelIndexList index; + QModelIndexList expected; + IntList command; + index << model->index(0, 0, QModelIndex()); + command << QItemSelectionModel::Select; + index << model->index(4, 4, QModelIndex()); + command << QItemSelectionModel::Select; + expected << model->index(0, 0, QModelIndex()) << model->index(4, 4, QModelIndex()); + QTest::newRow("(0, 0) and (4, 4): Select") + << index + << false + << command + << expected; + } + { + QModelIndexList index; + QModelIndexList expected; + IntList command; + index << model->index(0, 0, QModelIndex()); + command << QItemSelectionModel::Select; + index << model->index(4, 4, QModelIndex()); + command << QItemSelectionModel::ClearAndSelect; + expected << model->index(4, 4, QModelIndex()); + QTest::newRow("(0, 0) and (4, 4): Select and ClearAndSelect") + << index + << false + << command + << expected; + } + { + QModelIndexList index; + QModelIndexList expected; + IntList command; + index << model->index(0, 0, QModelIndex()); + command << (QItemSelectionModel::Select | QItemSelectionModel::Rows); + index << model->index(4, 4, QModelIndex()); + command << (QItemSelectionModel::Select | QItemSelectionModel::Rows); + expected << model->index(0, 0, QModelIndex()) + << model->index(0, 1, QModelIndex()) + << model->index(0, 2, QModelIndex()) + << model->index(0, 3, QModelIndex()) + << model->index(0, 4, QModelIndex()) + << model->index(4, 0, QModelIndex()) + << model->index(4, 1, QModelIndex()) + << model->index(4, 2, QModelIndex()) + << model->index(4, 3, QModelIndex()) + << model->index(4, 4, QModelIndex()); + QTest::newRow("(0, 0) and (4, 4): Select|Rows") + << index + << false + << command + << expected; + } + { + QModelIndexList index; + QModelIndexList expected; + IntList command; + index << model->index(0, 0, model->index(0, 0, QModelIndex())); + command << (QItemSelectionModel::Select | QItemSelectionModel::Rows); + index << model->index(4, 4, model->index(0, 0, QModelIndex())); + command << (QItemSelectionModel::Select | QItemSelectionModel::Rows); + QModelIndex parent = model->index(0, 0, QModelIndex()); + expected << model->index(0, 0, parent) + << model->index(0, 1, parent) + << model->index(0, 2, parent) + << model->index(0, 3, parent) + << model->index(0, 4, parent) + << model->index(4, 0, parent) + << model->index(4, 1, parent) + << model->index(4, 2, parent) + << model->index(4, 3, parent) + << model->index(4, 4, parent); + QTest::newRow("child (0, 0) and (4, 4) of (0, 0): Select|Rows") + << index + << false + << command + << expected; + } + { + QModelIndexList index; + QModelIndexList expected; + IntList command; + index << model->index(0, 0, QModelIndex()); + command << (QItemSelectionModel::Select | QItemSelectionModel::Columns); + index << model->index(4, 4, QModelIndex()); + command << (QItemSelectionModel::Select | QItemSelectionModel::Columns); + expected << model->index(0, 0, QModelIndex()) + << model->index(1, 0, QModelIndex()) + << model->index(2, 0, QModelIndex()) + << model->index(3, 0, QModelIndex()) + << model->index(4, 0, QModelIndex()) + << model->index(0, 4, QModelIndex()) + << model->index(1, 4, QModelIndex()) + << model->index(2, 4, QModelIndex()) + << model->index(3, 4, QModelIndex()) + << model->index(4, 4, QModelIndex()); + QTest::newRow("(0, 0) and (4, 4): Select|Columns") + << index + << false + << command + << expected; + } + { + QModelIndexList index; + QModelIndexList expected; + IntList command; + index << model->index(0, 0, model->index(0, 0, QModelIndex())); + command << (QItemSelectionModel::Select | QItemSelectionModel::Columns); + index << model->index(4, 4, model->index(0, 0, QModelIndex())); + command << (QItemSelectionModel::Select | QItemSelectionModel::Columns); + expected << model->index(0, 0, model->index(0, 0, QModelIndex())) + << model->index(1, 0, model->index(0, 0, QModelIndex())) + << model->index(2, 0, model->index(0, 0, QModelIndex())) + << model->index(3, 0, model->index(0, 0, QModelIndex())) + << model->index(4, 0, model->index(0, 0, QModelIndex())) + << model->index(0, 4, model->index(0, 0, QModelIndex())) + << model->index(1, 4, model->index(0, 0, QModelIndex())) + << model->index(2, 4, model->index(0, 0, QModelIndex())) + << model->index(3, 4, model->index(0, 0, QModelIndex())) + << model->index(4, 4, model->index(0, 0, QModelIndex())); + QTest::newRow("child (0, 0) and (4, 4) of (0, 0): Select|Columns") + << index + << false + << command + << expected; + } + { + QModelIndexList index; + QModelIndexList expected; + IntList command; + index << model->index(0, 0, QModelIndex()); + index << model->index(4, 0, QModelIndex()); + command << QItemSelectionModel::Select; + expected << model->index(0, 0, QModelIndex()) + << model->index(1, 0, QModelIndex()) + << model->index(2, 0, QModelIndex()) + << model->index(3, 0, QModelIndex()) + << model->index(4, 0, QModelIndex()); + QTest::newRow("(0, 0 to 4, 0): Select") + << index + << true + << command + << expected; + } + /* ### FAILS + { + QModelIndexList index; + QModelIndexList expected; + IntList command; + index << model->index(0, 0, QModelIndex()); + index << model->index(0, 0, model->index(0, 0, QModelIndex())); + command << QItemSelectionModel::Select; + QTest::newRow("(0, 0 to child 0, 0): Select") + << index + << true + << command + << expected; + } + */ + { + QModelIndexList index; + QModelIndexList expected; + IntList command; + index << model->index(0, 0, model->index(0, 0, QModelIndex())); + index << model->index(0, 0, model->index(1, 0, QModelIndex())); + command << QItemSelectionModel::Select; + QTest::newRow("child (0, 0) of (0, 0) to child (0, 0) of (1, 0): Select") + << index + << true + << command + << expected; + } + { + QModelIndexList index; + QModelIndexList expected; + IntList command; + index << model->index(0, 0, QModelIndex()); + index << model->index(4, 4, QModelIndex()); + command << QItemSelectionModel::Select; + expected << model->index(0, 0, QModelIndex()) + << model->index(0, 1, QModelIndex()) + << model->index(0, 2, QModelIndex()) + << model->index(0, 3, QModelIndex()) + << model->index(0, 4, QModelIndex()) + << model->index(1, 0, QModelIndex()) + << model->index(1, 1, QModelIndex()) + << model->index(1, 2, QModelIndex()) + << model->index(1, 3, QModelIndex()) + << model->index(1, 4, QModelIndex()) + << model->index(2, 0, QModelIndex()) + << model->index(2, 1, QModelIndex()) + << model->index(2, 2, QModelIndex()) + << model->index(2, 3, QModelIndex()) + << model->index(2, 4, QModelIndex()) + << model->index(3, 0, QModelIndex()) + << model->index(3, 1, QModelIndex()) + << model->index(3, 2, QModelIndex()) + << model->index(3, 3, QModelIndex()) + << model->index(3, 4, QModelIndex()) + << model->index(4, 0, QModelIndex()) + << model->index(4, 1, QModelIndex()) + << model->index(4, 2, QModelIndex()) + << model->index(4, 3, QModelIndex()) + << model->index(4, 4, QModelIndex()); + QTest::newRow("(0, 0 to 4, 4): Select") + << index + << true + << command + << expected; + } + { + QModelIndexList index; + QModelIndexList expected; + IntList command; + index << model->index(0, 0, QModelIndex()); + index << model->index(4, 0, QModelIndex()); + command << (QItemSelectionModel::Select | QItemSelectionModel::Rows); + expected << model->index(0, 0, QModelIndex()) + << model->index(0, 1, QModelIndex()) + << model->index(0, 2, QModelIndex()) + << model->index(0, 3, QModelIndex()) + << model->index(0, 4, QModelIndex()) + << model->index(1, 0, QModelIndex()) + << model->index(1, 1, QModelIndex()) + << model->index(1, 2, QModelIndex()) + << model->index(1, 3, QModelIndex()) + << model->index(1, 4, QModelIndex()) + << model->index(2, 0, QModelIndex()) + << model->index(2, 1, QModelIndex()) + << model->index(2, 2, QModelIndex()) + << model->index(2, 3, QModelIndex()) + << model->index(2, 4, QModelIndex()) + << model->index(3, 0, QModelIndex()) + << model->index(3, 1, QModelIndex()) + << model->index(3, 2, QModelIndex()) + << model->index(3, 3, QModelIndex()) + << model->index(3, 4, QModelIndex()) + << model->index(4, 0, QModelIndex()) + << model->index(4, 1, QModelIndex()) + << model->index(4, 2, QModelIndex()) + << model->index(4, 3, QModelIndex()) + << model->index(4, 4, QModelIndex()); + QTest::newRow("(0, 0 to 4, 0): Select|Rows") + << index + << true + << command + << expected; + } + { + QModelIndexList index; + QModelIndexList expected; + IntList command; + index << model->index(0, 0, QModelIndex()); + index << model->index(0, 4, QModelIndex()); + command << (QItemSelectionModel::Select | QItemSelectionModel::Columns); + expected << model->index(0, 0, QModelIndex()) + << model->index(0, 1, QModelIndex()) + << model->index(0, 2, QModelIndex()) + << model->index(0, 3, QModelIndex()) + << model->index(0, 4, QModelIndex()) + << model->index(1, 0, QModelIndex()) + << model->index(1, 1, QModelIndex()) + << model->index(1, 2, QModelIndex()) + << model->index(1, 3, QModelIndex()) + << model->index(1, 4, QModelIndex()) + << model->index(2, 0, QModelIndex()) + << model->index(2, 1, QModelIndex()) + << model->index(2, 2, QModelIndex()) + << model->index(2, 3, QModelIndex()) + << model->index(2, 4, QModelIndex()) + << model->index(3, 0, QModelIndex()) + << model->index(3, 1, QModelIndex()) + << model->index(3, 2, QModelIndex()) + << model->index(3, 3, QModelIndex()) + << model->index(3, 4, QModelIndex()) + << model->index(4, 0, QModelIndex()) + << model->index(4, 1, QModelIndex()) + << model->index(4, 2, QModelIndex()) + << model->index(4, 3, QModelIndex()) + << model->index(4, 4, QModelIndex()); + QTest::newRow("(0, 0 to 0, 4): Select|Columns") + << index + << true + << command + << expected; + } + { + QModelIndexList index; + QModelIndexList expected; + IntList command; + index << model->index(0, 0, QModelIndex()); + index << model->index(4, 4, QModelIndex()); + command << (QItemSelectionModel::Select | QItemSelectionModel::Rows); + expected << model->index(0, 0, QModelIndex()) + << model->index(0, 1, QModelIndex()) + << model->index(0, 2, QModelIndex()) + << model->index(0, 3, QModelIndex()) + << model->index(0, 4, QModelIndex()) + << model->index(1, 0, QModelIndex()) + << model->index(1, 1, QModelIndex()) + << model->index(1, 2, QModelIndex()) + << model->index(1, 3, QModelIndex()) + << model->index(1, 4, QModelIndex()) + << model->index(2, 0, QModelIndex()) + << model->index(2, 1, QModelIndex()) + << model->index(2, 2, QModelIndex()) + << model->index(2, 3, QModelIndex()) + << model->index(2, 4, QModelIndex()) + << model->index(3, 0, QModelIndex()) + << model->index(3, 1, QModelIndex()) + << model->index(3, 2, QModelIndex()) + << model->index(3, 3, QModelIndex()) + << model->index(3, 4, QModelIndex()) + << model->index(4, 0, QModelIndex()) + << model->index(4, 1, QModelIndex()) + << model->index(4, 2, QModelIndex()) + << model->index(4, 3, QModelIndex()) + << model->index(4, 4, QModelIndex()); + QTest::newRow("(0, 0 to 4, 4): Select|Rows") + << index + << true + << command + << expected; + } + { + QModelIndexList index; + QModelIndexList expected; + IntList command; + index << model->index(0, 0, QModelIndex()); + index << model->index(4, 4, QModelIndex()); + command << (QItemSelectionModel::Select | QItemSelectionModel::Columns); + expected << model->index(0, 0, QModelIndex()) + << model->index(0, 1, QModelIndex()) + << model->index(0, 2, QModelIndex()) + << model->index(0, 3, QModelIndex()) + << model->index(0, 4, QModelIndex()) + << model->index(1, 0, QModelIndex()) + << model->index(1, 1, QModelIndex()) + << model->index(1, 2, QModelIndex()) + << model->index(1, 3, QModelIndex()) + << model->index(1, 4, QModelIndex()) + << model->index(2, 0, QModelIndex()) + << model->index(2, 1, QModelIndex()) + << model->index(2, 2, QModelIndex()) + << model->index(2, 3, QModelIndex()) + << model->index(2, 4, QModelIndex()) + << model->index(3, 0, QModelIndex()) + << model->index(3, 1, QModelIndex()) + << model->index(3, 2, QModelIndex()) + << model->index(3, 3, QModelIndex()) + << model->index(3, 4, QModelIndex()) + << model->index(4, 0, QModelIndex()) + << model->index(4, 1, QModelIndex()) + << model->index(4, 2, QModelIndex()) + << model->index(4, 3, QModelIndex()) + << model->index(4, 4, QModelIndex()); + QTest::newRow("(0, 0 to 4, 4): Select|Columns") + << index + << true + << command + << expected; + } + { + QModelIndexList index; + QModelIndexList expected; + IntList command; + index << model->index(0, 2, QModelIndex()); + index << model->index(4, 2, QModelIndex()); + command << QItemSelectionModel::Select; + index << model->index(2, 0, QModelIndex()); + index << model->index(2, 4, QModelIndex()); + command << QItemSelectionModel::Select; + expected << model->index(0, 2, QModelIndex()) + << model->index(1, 2, QModelIndex()) + << model->index(2, 2, QModelIndex()) + << model->index(3, 2, QModelIndex()) + << model->index(4, 2, QModelIndex()) + << model->index(2, 0, QModelIndex()) + << model->index(2, 1, QModelIndex()) + << model->index(2, 3, QModelIndex()) + << model->index(2, 4, QModelIndex()); + QTest::newRow("(0, 2 to 4, 2) and (2, 0 to 2, 4): Select") + << index + << true + << command + << expected; + } + { + QModelIndexList index; + QModelIndexList expected; + IntList command; + index << model->index(0, 2, QModelIndex()); + index << model->index(4, 2, QModelIndex()); + command << QItemSelectionModel::Select; + index << model->index(2, 0, QModelIndex()); + index << model->index(2, 4, QModelIndex()); + command << QItemSelectionModel::SelectCurrent; + expected << model->index(2, 0, QModelIndex()) + << model->index(2, 1, QModelIndex()) + << model->index(2, 2, QModelIndex()) + << model->index(2, 3, QModelIndex()) + << model->index(2, 4, QModelIndex()); + QTest::newRow("(0, 2 to 4, 2) and (2, 0 to 2, 4): Select and SelectCurrent") + << index + << true + << command + << expected; + } + { + QModelIndexList index; + QModelIndexList expected; + IntList command; + index << model->index(0, 2, QModelIndex()); + index << model->index(4, 2, QModelIndex()); + command << QItemSelectionModel::Select; + index << model->index(2, 0, QModelIndex()); + index << model->index(2, 4, QModelIndex()); + command << QItemSelectionModel::Toggle; + expected << model->index(0, 2, QModelIndex()) + << model->index(1, 2, QModelIndex()) + << model->index(3, 2, QModelIndex()) + << model->index(4, 2, QModelIndex()) + << model->index(2, 0, QModelIndex()) + << model->index(2, 1, QModelIndex()) + << model->index(2, 3, QModelIndex()) + << model->index(2, 4, QModelIndex()); + QTest::newRow("(0, 2 to 4, 2) and (2, 0 to 2, 4): Select and Toggle") + << index + << true + << command + << expected; + } + { + QModelIndexList index; + QModelIndexList expected; + IntList command; + index << model->index(0, 2, QModelIndex()); + index << model->index(4, 2, QModelIndex()); + command << QItemSelectionModel::Select; + index << model->index(2, 0, QModelIndex()); + index << model->index(2, 4, QModelIndex()); + command << QItemSelectionModel::Deselect; + expected << model->index(0, 2, QModelIndex()) + << model->index(1, 2, QModelIndex()) + << model->index(3, 2, QModelIndex()) + << model->index(4, 2, QModelIndex()); + QTest::newRow("(0, 2 to 4, 2) and (2, 0 to 2, 4): Select and Deselect") + << index + << true + << command + << expected; + } + + { + QModelIndexList index; + QModelIndexList expected; + IntList command; + index << model->index(0, 0, QModelIndex()); + index << model->index(2, 2, QModelIndex()); + command << QItemSelectionModel::Select; + + index << model->index(0, 0, QModelIndex()); + index << model->index(0, 0, QModelIndex()); + command << QItemSelectionModel::Toggle; + + expected << model->index(0, 1, QModelIndex()) + << model->index(0, 2, QModelIndex()) + << model->index(1, 0, QModelIndex()) + << model->index(1, 1, QModelIndex()) + << model->index(1, 2, QModelIndex()) + << model->index(2, 0, QModelIndex()) + << model->index(2, 1, QModelIndex()) + << model->index(2, 2, QModelIndex()); + + QTest::newRow("(0, 0 to 2, 2) and (0, 0 to 0, 0): Select and Toggle at selection boundary") + << index + << true + << command + << expected; + } + + { + QModelIndexList index; + QModelIndexList expected; + IntList command; + index << model->index(0, 0, QModelIndex()); + index << model->index(2, 2, QModelIndex()); + command << QItemSelectionModel::Select; + + index << model->index(0, 1, QModelIndex()); + index << model->index(0, 1, QModelIndex()); + command << QItemSelectionModel::Toggle; + + expected << model->index(0, 0, QModelIndex()) + << model->index(0, 2, QModelIndex()) + << model->index(1, 0, QModelIndex()) + << model->index(1, 1, QModelIndex()) + << model->index(1, 2, QModelIndex()) + << model->index(2, 0, QModelIndex()) + << model->index(2, 1, QModelIndex()) + << model->index(2, 2, QModelIndex()); + + QTest::newRow("(0, 0 to 2, 2) and (0, 1 to 0, 1): Select and Toggle at selection boundary") + << index + << true + << command + << expected; + } + + { + QModelIndexList index; + QModelIndexList expected; + IntList command; + index << model->index(0, 0, QModelIndex()); + index << model->index(2, 2, QModelIndex()); + command << QItemSelectionModel::Select; + + index << model->index(0, 2, QModelIndex()); + index << model->index(0, 2, QModelIndex()); + command << QItemSelectionModel::Toggle; + + expected << model->index(0, 0, QModelIndex()) + << model->index(0, 1, QModelIndex()) + << model->index(1, 0, QModelIndex()) + << model->index(1, 1, QModelIndex()) + << model->index(1, 2, QModelIndex()) + << model->index(2, 0, QModelIndex()) + << model->index(2, 1, QModelIndex()) + << model->index(2, 2, QModelIndex()); + + QTest::newRow("(0, 0 to 2, 2) and (0, 2 to 0, 2): Select and Toggle at selection boundary") + << index + << true + << command + << expected; + } + + { + QModelIndexList index; + QModelIndexList expected; + IntList command; + index << model->index(0, 0, QModelIndex()); + index << model->index(2, 2, QModelIndex()); + command << QItemSelectionModel::Select; + + index << model->index(1, 0, QModelIndex()); + index << model->index(1, 0, QModelIndex()); + command << QItemSelectionModel::Toggle; + + expected << model->index(0, 0, QModelIndex()) + << model->index(0, 1, QModelIndex()) + << model->index(0, 2, QModelIndex()) + << model->index(1, 1, QModelIndex()) + << model->index(1, 2, QModelIndex()) + << model->index(2, 0, QModelIndex()) + << model->index(2, 1, QModelIndex()) + << model->index(2, 2, QModelIndex()); + + QTest::newRow("(0, 0 to 2, 2) and (1, 0 to 1, 0): Select and Toggle at selection boundary") + << index + << true + << command + << expected; + } + + { + QModelIndexList index; + QModelIndexList expected; + IntList command; + index << model->index(0, 0, QModelIndex()); + index << model->index(2, 2, QModelIndex()); + command << QItemSelectionModel::Select; + + index << model->index(1, 1, QModelIndex()); + index << model->index(1, 1, QModelIndex()); + command << QItemSelectionModel::Toggle; + + expected << model->index(0, 0, QModelIndex()) + << model->index(0, 1, QModelIndex()) + << model->index(0, 2, QModelIndex()) + << model->index(1, 0, QModelIndex()) + << model->index(1, 2, QModelIndex()) + << model->index(2, 0, QModelIndex()) + << model->index(2, 1, QModelIndex()) + << model->index(2, 2, QModelIndex()); + + QTest::newRow("(0, 0 to 2, 2) and (1, 1 to 1, 1): Select and Toggle at selection boundary") + << index + << true + << command + << expected; + } + { + QModelIndexList index; + QModelIndexList expected; + IntList command; + index << model->index(0, 0, QModelIndex()); + index << model->index(2, 2, QModelIndex()); + command << QItemSelectionModel::Select; + + index << model->index(1, 2, QModelIndex()); + index << model->index(1, 2, QModelIndex()); + command << QItemSelectionModel::Toggle; + + expected << model->index(0, 0, QModelIndex()) + << model->index(0, 1, QModelIndex()) + << model->index(0, 2, QModelIndex()) + << model->index(1, 0, QModelIndex()) + << model->index(1, 1, QModelIndex()) + << model->index(2, 0, QModelIndex()) + << model->index(2, 1, QModelIndex()) + << model->index(2, 2, QModelIndex()); + + QTest::newRow("(0, 0 to 2, 2) and (1, 2 to 1, 2): Select and Toggle at selection boundary") + << index + << true + << command + << expected; + } + { + QModelIndexList index; + QModelIndexList expected; + IntList command; + index << model->index(0, 0, QModelIndex()); + index << model->index(2, 2, QModelIndex()); + command << QItemSelectionModel::Select; + + index << model->index(2, 0, QModelIndex()); + index << model->index(2, 0, QModelIndex()); + command << QItemSelectionModel::Toggle; + + expected << model->index(0, 0, QModelIndex()) + << model->index(0, 1, QModelIndex()) + << model->index(0, 2, QModelIndex()) + << model->index(1, 0, QModelIndex()) + << model->index(1, 1, QModelIndex()) + << model->index(1, 2, QModelIndex()) + << model->index(2, 1, QModelIndex()) + << model->index(2, 2, QModelIndex()); + + QTest::newRow("(0, 0 to 2, 2) and (2, 0 to 2, 0): Select and Toggle at selection boundary") + << index + << true + << command + << expected; + } + { + QModelIndexList index; + QModelIndexList expected; + IntList command; + index << model->index(0, 0, QModelIndex()); + index << model->index(2, 2, QModelIndex()); + command << QItemSelectionModel::Select; + + index << model->index(2, 1, QModelIndex()); + index << model->index(2, 1, QModelIndex()); + command << QItemSelectionModel::Toggle; + + expected << model->index(0, 0, QModelIndex()) + << model->index(0, 1, QModelIndex()) + << model->index(0, 2, QModelIndex()) + << model->index(1, 0, QModelIndex()) + << model->index(1, 1, QModelIndex()) + << model->index(1, 2, QModelIndex()) + << model->index(2, 0, QModelIndex()) + << model->index(2, 2, QModelIndex()); + + QTest::newRow("(0, 0 to 2, 2) and (2, 1 to 2, 1): Select and Toggle at selection boundary") + << index + << true + << command + << expected; + } + { + QModelIndexList index; + QModelIndexList expected; + IntList command; + + index << model->index(0, 0, QModelIndex()); + index << model->index(2, 2, QModelIndex()); + command << QItemSelectionModel::Select; + + index << model->index(2, 2, QModelIndex()); + index << model->index(2, 2, QModelIndex()); + command << QItemSelectionModel::Toggle; + + expected << model->index(0, 0, QModelIndex()) + << model->index(0, 1, QModelIndex()) + << model->index(0, 2, QModelIndex()) + << model->index(1, 0, QModelIndex()) + << model->index(1, 1, QModelIndex()) + << model->index(1, 2, QModelIndex()) + << model->index(2, 0, QModelIndex()) + << model->index(2, 1, QModelIndex()); + + QTest::newRow("(0, 0 to 2, 2) and (2, 2 to 2, 2): Select and Toggle at selection boundary") + << index + << true + << command + << expected; + } + { + QModelIndexList indexes; + IntList commands; + QModelIndexList expected; + + indexes << model->index(0, 0, QModelIndex()) << model->index(0, 0, QModelIndex()) // press 0 + << model->index(0, 0, QModelIndex()) << model->index(0, 0, QModelIndex()) // release 0 + << model->index(1, 0, QModelIndex()) << model->index(1, 0, QModelIndex()) // press 1 + << model->index(1, 0, QModelIndex()) << model->index(1, 0, QModelIndex()) // release 1 + << model->index(2, 0, QModelIndex()) << model->index(2, 0, QModelIndex()) // press 2 + << model->index(2, 0, QModelIndex()) << model->index(2, 0, QModelIndex()) // release 2 + << model->index(3, 0, QModelIndex()) << model->index(3, 0, QModelIndex()) // press 3 + << model->index(3, 0, QModelIndex()) << model->index(3, 0, QModelIndex()) // release 3 + << model->index(2, 0, QModelIndex()) << model->index(2, 0, QModelIndex()) // press 2 again + << model->index(2, 0, QModelIndex()) << model->index(2, 0, QModelIndex());// move 2 + + commands << (QItemSelectionModel::NoUpdate) // press 0 + << (QItemSelectionModel::Toggle|QItemSelectionModel::Rows) // release 0 + << (QItemSelectionModel::NoUpdate) // press 1 + << (QItemSelectionModel::Toggle|QItemSelectionModel::Rows) // release 1 + << (QItemSelectionModel::NoUpdate) // press 2 + << (QItemSelectionModel::Toggle|QItemSelectionModel::Rows) // release 2 + << (QItemSelectionModel::NoUpdate) // press 3 + << (QItemSelectionModel::Toggle|QItemSelectionModel::Rows) // release 3 + << (QItemSelectionModel::NoUpdate) // press 2 again + << (QItemSelectionModel::Toggle/*Current*/|QItemSelectionModel::Rows);// move 2 + + expected << model->index(0, 0, QModelIndex()) + << model->index(0, 1, QModelIndex()) + << model->index(0, 2, QModelIndex()) + << model->index(0, 3, QModelIndex()) + << model->index(0, 4, QModelIndex()) + + << model->index(1, 0, QModelIndex()) + << model->index(1, 1, QModelIndex()) + << model->index(1, 2, QModelIndex()) + << model->index(1, 3, QModelIndex()) + << model->index(1, 4, QModelIndex()) + /* + << model->index(2, 0, QModelIndex()) + << model->index(2, 1, QModelIndex()) + << model->index(2, 2, QModelIndex()) + << model->index(2, 3, QModelIndex()) + << model->index(2, 4, QModelIndex()) + */ + << model->index(3, 0, QModelIndex()) + << model->index(3, 1, QModelIndex()) + << model->index(3, 2, QModelIndex()) + << model->index(3, 3, QModelIndex()) + << model->index(3, 4, QModelIndex()); + + QTest::newRow("simulated treeview multiselection behavior") + << indexes + << true + << commands + << expected; + } +} + +void tst_QItemSelectionModel::select() +{ + QFETCH(QModelIndexList, indexList); + QFETCH(bool, useRanges); + QFETCH(IntList, commandList); + QFETCH(QModelIndexList, expectedList); + + int lastCommand = 0; + // do selections + for (int i = 0; iselect(QItemSelection(indexList.at(2*i), indexList.at(2*i+1)), + (QItemSelectionModel::SelectionFlags)commandList.at(i)); + } else { + selection->select(indexList.at(i), + (QItemSelectionModel::SelectionFlags)commandList.at(i)); + } + lastCommand = commandList.at(i); + } + + + QModelIndexList selectedList = selection->selectedIndexes(); + + QVERIFY(selection->hasSelection()!=selectedList.isEmpty()); + + // debug output +// for (int i=0; iisSelected(idx) == selectedList.contains(idx), + QString("isSelected(index: %1, %2) does not match selectedIndexes()") + .arg(idx.row()) + .arg(idx.column()).toLatin1()); + } + + //for now we assume Rows/Columns flag is the same for all commands, therefore we just check lastCommand + // test that isRowSelected agrees + if (lastCommand & QItemSelectionModel::Rows) { + for (int i=0; iisRowSelected(selectedList.at(i).row(), + model->parent(selectedList.at(i))), + QString("isRowSelected(row: %1) does not match selectedIndexes()") + .arg(selectedList.at(i).row()).toLatin1()); + } + + // test that isColumnSelected agrees + if (lastCommand & QItemSelectionModel::Columns) { + for (int i=0; iisColumnSelected(selectedList.at(i).column(), + model->parent(selectedList.at(i))), + QString("isColumnSelected(column: %1) does not match selectedIndexes()") + .arg(selectedList.at(i).column()).toLatin1()); + } +} + +void tst_QItemSelectionModel::persistentselections_data() +{ + QTest::addColumn("indexList"); + QTest::addColumn("commandList"); + QTest::addColumn("insertRows"); // start, count + QTest::addColumn("insertColumns"); // start, count + QTest::addColumn("deleteRows"); // start, count + QTest::addColumn("deleteColumns"); // start, count + QTest::addColumn("expectedList"); + + PairList index, expected; + IntList command, insertRows, insertColumns, deleteRows, deleteColumns; + + + index.clear(); expected.clear(); command.clear(); + insertRows.clear(); insertColumns.clear(); deleteRows.clear(); deleteColumns.clear(); + index << IntPair(0, 0); + command << QItemSelectionModel::ClearAndSelect; + deleteRows << 4 << 1; + expected << IntPair(0, 0); + QTest::newRow("ClearAndSelect (0, 0). Delete last row.") + << index << command + << insertRows << insertColumns << deleteRows << deleteColumns + << expected; + + index.clear(); expected.clear(); command.clear(); + insertRows.clear(); insertColumns.clear(); deleteRows.clear(); deleteColumns.clear(); + index << IntPair(0, 0); + command << QItemSelectionModel::ClearAndSelect; + deleteRows << 0 << 1; + QTest::newRow("ClearAndSelect (0, 0). Delete first row.") + << index << command + << insertRows << insertColumns << deleteRows << deleteColumns + << expected; + + index.clear(); expected.clear(); command.clear(); + insertRows.clear(); insertColumns.clear(); deleteRows.clear(); deleteColumns.clear(); + index << IntPair(1, 0); + command << QItemSelectionModel::ClearAndSelect; + deleteRows << 0 << 1; + expected << IntPair(0, 0); + QTest::newRow("ClearAndSelect (1, 0). Delete first row.") + << index << command + << insertRows << insertColumns << deleteRows << deleteColumns + << expected; + + index.clear(); expected.clear(); command.clear(); + insertRows.clear(); insertColumns.clear(); deleteRows.clear(); deleteColumns.clear(); + index << IntPair(0, 0); + command << QItemSelectionModel::ClearAndSelect; + insertRows << 5 << 1; + expected << IntPair(0, 0); + QTest::newRow("ClearAndSelect (0, 0). Append row.") + << index << command + << insertRows << insertColumns << deleteRows << deleteColumns + << expected; + + index.clear(); expected.clear(); command.clear(); + insertRows.clear(); insertColumns.clear(); deleteRows.clear(); deleteColumns.clear(); + index << IntPair(0, 0); + command << QItemSelectionModel::ClearAndSelect; + insertRows << 0 << 1; + expected << IntPair(1, 0); + QTest::newRow("ClearAndSelect (0, 0). Insert before first row.") + << index << command + << insertRows << insertColumns << deleteRows << deleteColumns + << expected; + + index.clear(); expected.clear(); command.clear(); + insertRows.clear(); insertColumns.clear(); deleteRows.clear(); deleteColumns.clear(); + index << IntPair(0, 0) + << IntPair(4, 0); + command << QItemSelectionModel::ClearAndSelect; + insertRows << 5 << 1; + expected << IntPair(0, 0) + << IntPair(1, 0) + << IntPair(2, 0) + << IntPair(3, 0) + << IntPair(4, 0); + QTest::newRow("ClearAndSelect (0, 0) to (4, 0). Append row.") + << index << command + << insertRows << insertColumns << deleteRows << deleteColumns + << expected; + + index.clear(); expected.clear(); command.clear(); + insertRows.clear(); insertColumns.clear(); deleteRows.clear(); deleteColumns.clear(); + index << IntPair(0, 0) + << IntPair(4, 0); + command << QItemSelectionModel::ClearAndSelect; + insertRows << 0 << 1; + expected << IntPair(1, 0) + << IntPair(2, 0) + << IntPair(3, 0) + << IntPair(4, 0) + << IntPair(5, 0); + QTest::newRow("ClearAndSelect (0, 0) to (4, 0). Insert before first row.") + << index << command + << insertRows << insertColumns << deleteRows << deleteColumns + << expected; + + index.clear(); expected.clear(); command.clear(); + insertRows.clear(); insertColumns.clear(); deleteRows.clear(); deleteColumns.clear(); + index << IntPair(0, 0) + << IntPair(4, 0); + command << QItemSelectionModel::ClearAndSelect; + deleteRows << 0 << 1; + expected << IntPair(0, 0) + << IntPair(1, 0) + << IntPair(2, 0) + << IntPair(3, 0); + QTest::newRow("ClearAndSelect (0, 0) to (4, 0). Delete first row.") + << index << command + << insertRows << insertColumns << deleteRows << deleteColumns + << expected; + + index.clear(); expected.clear(); command.clear(); + insertRows.clear(); insertColumns.clear(); deleteRows.clear(); deleteColumns.clear(); + index << IntPair(0, 0) + << IntPair(4, 0); + command << QItemSelectionModel::ClearAndSelect; + deleteRows << 4 << 1; + expected << IntPair(0, 0) + << IntPair(1, 0) + << IntPair(2, 0) + << IntPair(3, 0); + QTest::newRow("ClearAndSelect (0, 0) to (4, 0). Delete last row.") + << index << command + << insertRows << insertColumns << deleteRows << deleteColumns + << expected; + + index.clear(); expected.clear(); command.clear(); + insertRows.clear(); insertColumns.clear(); deleteRows.clear(); deleteColumns.clear(); + index << IntPair(0, 0) + << IntPair(4, 0); + command << QItemSelectionModel::ClearAndSelect; + deleteRows << 1 << 3; + expected << IntPair(0, 0) + << IntPair(1, 0); + QTest::newRow("ClearAndSelect (0, 0) to (4, 0). Deleting all but first and last row.") + << index << command + << insertRows << insertColumns << deleteRows << deleteColumns + << expected; + + index.clear(); expected.clear(); command.clear(); + insertRows.clear(); insertColumns.clear(); deleteRows.clear(); deleteColumns.clear(); + index << IntPair(0, 0) + << IntPair(4, 0); + command << QItemSelectionModel::ClearAndSelect; + insertRows << 1 << 1; + expected << IntPair(0, 0) + // the inserted row should not be selected + << IntPair(2, 0) + << IntPair(3, 0) + << IntPair(4, 0) + << IntPair(5, 0); + QTest::newRow("ClearAndSelect (0, 0) to (4, 0). Insert after first row.") + << index << command + << insertRows << insertColumns << deleteRows << deleteColumns + << expected; +} + +void tst_QItemSelectionModel::persistentselections() +{ + QFETCH(PairList, indexList); + QFETCH(IntList, commandList); + QFETCH(IntList, insertRows); + QFETCH(IntList, insertColumns); + QFETCH(IntList, deleteRows); + QFETCH(IntList, deleteColumns); + QFETCH(PairList, expectedList); + + // make sure the model is sane (5x5) + QCOMPARE(model->rowCount(QModelIndex()), 5); + QCOMPARE(model->columnCount(QModelIndex()), 5); + + // do selections + for (int i=0; iindex(indexList.at(i).first, + indexList.at(i).second, + QModelIndex()); + selection->select(index, (QItemSelectionModel::SelectionFlags)commandList.at(i)); + } else { + QModelIndex tl = model->index(indexList.at(2*i).first, + indexList.at(2*i).second, + QModelIndex()); + QModelIndex br = model->index(indexList.at(2*i+1).first, + indexList.at(2*i+1).second, + QModelIndex()); + selection->select(QItemSelection(tl, br), + (QItemSelectionModel::SelectionFlags)commandList.at(i)); + } + } + // test that we have selected items + QVERIFY(!selection->selectedIndexes().isEmpty()); + QVERIFY(selection->hasSelection()); + + // insert/delete row and/or columns + if (insertRows.count() > 1) + model->insertRows(insertRows.at(0), insertRows.at(1), QModelIndex()); + if (insertColumns.count() > 1) + model->insertColumns(insertColumns.at(0), insertColumns.at(1), QModelIndex()); + if (deleteRows.count() > 1) + model->removeRows(deleteRows.at(0), deleteRows.at(1), QModelIndex()); + if (deleteColumns.count() > 1) + model->removeColumns(deleteColumns.at(0), deleteColumns.at(1), QModelIndex()); + + // check that the selected items are the correct number and indexes + QModelIndexList selectedList = selection->selectedIndexes(); + QCOMPARE(selectedList.count(), expectedList.count()); + foreach(IntPair pair, expectedList) { + QModelIndex index = model->index(pair.first, pair.second, QModelIndex()); + QVERIFY(selectedList.contains(index)); + } +} + +// "make reset public"-model +class MyStandardItemModel: public QStandardItemModel +{ + Q_OBJECT +public: + inline MyStandardItemModel(int i1, int i2): QStandardItemModel(i1, i2) {} + inline void reset() { QStandardItemModel::reset(); } +}; + +void tst_QItemSelectionModel::resetModel() +{ + MyStandardItemModel model(20, 20); + QTreeView view; + view.setModel(&model); + + QSignalSpy spy(view.selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection))); + + view.selectionModel()->select(QItemSelection(model.index(0, 0), model.index(5, 5)), QItemSelectionModel::Select); + + QCOMPARE(spy.count(), 1); + + model.reset(); + + QVERIFY(view.selectionModel()->selection().isEmpty()); + QVERIFY(view.selectionModel()->hasSelection() == false); + + view.selectionModel()->select(QItemSelection(model.index(0, 0), model.index(5, 5)), QItemSelectionModel::Select); + + QCOMPARE(spy.count(), 2); + QCOMPARE(spy.at(1).count(), 2); + // make sure we don't get an "old selection" + QCOMPARE(spy.at(1).at(1).userType(), qMetaTypeId()); + QVERIFY(qvariant_cast(spy.at(1).at(1)).isEmpty()); +} + +void tst_QItemSelectionModel::removeRows_data() +{ + QTest::addColumn("rowCount"); + QTest::addColumn("columnCount"); + + QTest::addColumn("selectTop"); + QTest::addColumn("selectLeft"); + QTest::addColumn("selectBottom"); + QTest::addColumn("selectRight"); + + QTest::addColumn("removeTop"); + QTest::addColumn("removeBottom"); + + QTest::addColumn("expectedTop"); + QTest::addColumn("expectedLeft"); + QTest::addColumn("expectedBottom"); + QTest::addColumn("expectedRight"); + + QTest::newRow("4x4 <0,1><1,1>") + << 4 << 4 + << 0 << 1 << 1 << 1 + << 0 << 0 + << 0 << 1 << 0 << 1; +} + +void tst_QItemSelectionModel::removeRows() +{ + QFETCH(int, rowCount); + QFETCH(int, columnCount); + QFETCH(int, selectTop); + QFETCH(int, selectLeft); + QFETCH(int, selectBottom); + QFETCH(int, selectRight); + QFETCH(int, removeTop); + QFETCH(int, removeBottom); + QFETCH(int, expectedTop); + QFETCH(int, expectedLeft); + QFETCH(int, expectedBottom); + QFETCH(int, expectedRight); + + MyStandardItemModel model(rowCount, columnCount); + QItemSelectionModel selections(&model); + QSignalSpy spy(&selections, SIGNAL(selectionChanged(QItemSelection,QItemSelection))); + + QModelIndex tl = model.index(selectTop, selectLeft); + QModelIndex br = model.index(selectBottom, selectRight); + selections.select(QItemSelection(tl, br), QItemSelectionModel::ClearAndSelect); + + QCOMPARE(spy.count(), 1); + QVERIFY(selections.isSelected(tl)); + QVERIFY(selections.isSelected(br)); + QVERIFY(selections.hasSelection()); + + model.removeRows(removeTop, removeBottom - removeTop + 1); + + QCOMPARE(spy.count(), 2); + tl = model.index(expectedTop, expectedLeft); + br = model.index(expectedBottom, expectedRight); + QVERIFY(selections.isSelected(tl)); + QVERIFY(selections.isSelected(br)); +} + +void tst_QItemSelectionModel::removeColumns_data() +{ + QTest::addColumn("rowCount"); + QTest::addColumn("columnCount"); + + QTest::addColumn("selectTop"); + QTest::addColumn("selectLeft"); + QTest::addColumn("selectBottom"); + QTest::addColumn("selectRight"); + + QTest::addColumn("removeLeft"); + QTest::addColumn("removeRight"); + + QTest::addColumn("expectedTop"); + QTest::addColumn("expectedLeft"); + QTest::addColumn("expectedBottom"); + QTest::addColumn("expectedRight"); + + QTest::newRow("4x4 <0,1><1,1>") + << 4 << 4 + << 1 << 0 << 1 << 1 + << 0 << 0 + << 1 << 0 << 1 << 0; +} + +void tst_QItemSelectionModel::removeColumns() +{ + QFETCH(int, rowCount); + QFETCH(int, columnCount); + QFETCH(int, selectTop); + QFETCH(int, selectLeft); + QFETCH(int, selectBottom); + QFETCH(int, selectRight); + QFETCH(int, removeLeft); + QFETCH(int, removeRight); + QFETCH(int, expectedTop); + QFETCH(int, expectedLeft); + QFETCH(int, expectedBottom); + QFETCH(int, expectedRight); + + MyStandardItemModel model(rowCount, columnCount); + QItemSelectionModel selections(&model); + QSignalSpy spy(&selections, SIGNAL(selectionChanged(QItemSelection,QItemSelection))); + + QModelIndex tl = model.index(selectTop, selectLeft); + QModelIndex br = model.index(selectBottom, selectRight); + selections.select(QItemSelection(tl, br), QItemSelectionModel::ClearAndSelect); + + QCOMPARE(spy.count(), 1); + QVERIFY(selections.isSelected(tl)); + QVERIFY(selections.isSelected(br)); + QVERIFY(selections.hasSelection()); + + model.removeColumns(removeLeft, removeRight - removeLeft + 1); + + QCOMPARE(spy.count(), 2); + tl = model.index(expectedTop, expectedLeft); + br = model.index(expectedBottom, expectedRight); + QVERIFY(selections.isSelected(tl)); + QVERIFY(selections.isSelected(br)); +} + +typedef QList IntListList; +typedef QPair IntPairPair; +typedef QList IntPairPairList; +Q_DECLARE_METATYPE(IntListList) +Q_DECLARE_METATYPE(IntPairPair) +Q_DECLARE_METATYPE(IntPairPairList) + +void tst_QItemSelectionModel::modelLayoutChanged_data() +{ + QTest::addColumn("items"); + QTest::addColumn("initialSelectedRanges"); + QTest::addColumn("sortOrder"); + QTest::addColumn("sortColumn"); + QTest::addColumn("expectedSelectedRanges"); + + QTest::newRow("everything selected, then row order reversed") + << (IntListList() + << (IntList() << 0 << 1 << 2 << 3) + << (IntList() << 3 << 2 << 1 << 0)) + << (IntPairPairList() + << IntPairPair(IntPair(0, 0), IntPair(3, 1))) + << int(Qt::DescendingOrder) + << 0 + << (IntPairPairList() + << IntPairPair(IntPair(0, 0), IntPair(3, 1))); + QTest::newRow("first two rows selected, then row order reversed") + << (IntListList() + << (IntList() << 0 << 1 << 2 << 3) + << (IntList() << 3 << 2 << 1 << 0)) + << (IntPairPairList() + << IntPairPair(IntPair(0, 0), IntPair(1, 1))) + << int(Qt::DescendingOrder) + << 0 + << (IntPairPairList() + << IntPairPair(IntPair(2, 0), IntPair(3, 1))); + QTest::newRow("middle two rows selected, then row order reversed") + << (IntListList() + << (IntList() << 0 << 1 << 2 << 3) + << (IntList() << 3 << 2 << 1 << 0)) + << (IntPairPairList() + << IntPairPair(IntPair(1, 0), IntPair(2, 1))) + << int(Qt::DescendingOrder) + << 0 + << (IntPairPairList() + << IntPairPair(IntPair(1, 0), IntPair(2, 1))); + QTest::newRow("two ranges") + << (IntListList() + << (IntList() << 2 << 0 << 3 << 1) + << (IntList() << 2 << 0 << 3 << 1)) + << (IntPairPairList() + << IntPairPair(IntPair(1, 0), IntPair(1, 1)) + << IntPairPair(IntPair(3, 0), IntPair(3, 1))) + << int(Qt::AscendingOrder) + << 0 + << (IntPairPairList() + << IntPairPair(IntPair(0, 0), IntPair(0, 1)) + << IntPairPair(IntPair(1, 0), IntPair(1, 1))); +} + +void tst_QItemSelectionModel::modelLayoutChanged() +{ + QFETCH(IntListList, items); + QFETCH(IntPairPairList, initialSelectedRanges); + QFETCH(int, sortOrder); + QFETCH(int, sortColumn); + QFETCH(IntPairPairList, expectedSelectedRanges); + + MyStandardItemModel model(items.at(0).count(), items.count()); + // initialize model data + for (int i = 0; i < model.rowCount(); ++i) { + for (int j = 0; j < model.columnCount(); ++j) { + QModelIndex index = model.index(i, j); + model.setData(index, items.at(j).at(i), Qt::DisplayRole); + } + } + + // select initial ranges + QItemSelectionModel selectionModel(&model); + foreach (IntPairPair range, initialSelectedRanges) { + IntPair tl = range.first; + IntPair br = range.second; + QItemSelection selection( + model.index(tl.first, tl.second), + model.index(br.first, br.second)); + selectionModel.select(selection, QItemSelectionModel::Select); + } + + // sort the model + model.sort(sortColumn, Qt::SortOrder(sortOrder)); + + // verify that selection is as expected + QItemSelection selection = selectionModel.selection(); + QCOMPARE(selection.count(), expectedSelectedRanges.count()); + QVERIFY(selectionModel.hasSelection() == !expectedSelectedRanges.isEmpty()); + + for (int i = 0; i < expectedSelectedRanges.count(); ++i) { + IntPairPair expectedRange = expectedSelectedRanges.at(i); + IntPair expectedTl = expectedRange.first; + IntPair expectedBr = expectedRange.second; + QItemSelectionRange actualRange = selection.at(i); + QModelIndex actualTl = actualRange.topLeft(); + QModelIndex actualBr = actualRange.bottomRight(); + QCOMPARE(actualTl.row(), expectedTl.first); + QCOMPARE(actualTl.column(), expectedTl.second); + QCOMPARE(actualBr.row(), expectedBr.first); + QCOMPARE(actualBr.column(), expectedBr.second); + } +} + +void tst_QItemSelectionModel::selectedRows_data() +{ + QTest::addColumn("rowCount"); + QTest::addColumn("columnCount"); + QTest::addColumn("column"); + QTest::addColumn("selectRows"); + QTest::addColumn("expectedRows"); + QTest::addColumn("unexpectedRows"); + + QTest::newRow("10x10, first row") + << 10 << 10 << 0 + << (IntList() << 0) + << (IntList() << 0) + << (IntList() << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9); + + QTest::newRow("10x10, first 4 rows") + << 10 << 10 << 0 + << (IntList() << 0 << 1 << 2 << 3) + << (IntList() << 0 << 1 << 2 << 3) + << (IntList() << 4 << 5 << 6 << 7 << 8 << 9); + + QTest::newRow("10x10, last 4 rows") + << 10 << 10 << 0 + << (IntList() << 6 << 7 << 8 << 9) + << (IntList() << 6 << 7 << 8 << 9) + << (IntList() << 0 << 1 << 2 << 3 << 4 << 6); +} + +void tst_QItemSelectionModel::selectedRows() +{ + QFETCH(int, rowCount); + QFETCH(int, columnCount); + QFETCH(int, column); + QFETCH(IntList, selectRows); + QFETCH(IntList, expectedRows); + QFETCH(IntList, unexpectedRows); + + MyStandardItemModel model(rowCount, columnCount); + QItemSelectionModel selectionModel(&model); + + for (int i = 0; i < selectRows.count(); ++i) + selectionModel.select(model.index(selectRows.at(i), 0), + QItemSelectionModel::Select + |QItemSelectionModel::Rows); + + for (int j = 0; j < selectRows.count(); ++j) + QVERIFY(selectionModel.isRowSelected(expectedRows.at(j), QModelIndex())); + + for (int k = 0; k < selectRows.count(); ++k) + QVERIFY(!selectionModel.isRowSelected(unexpectedRows.at(k), QModelIndex())); + + QModelIndexList selectedRowIndexes = selectionModel.selectedRows(column); + QCOMPARE(selectedRowIndexes.count(), expectedRows.count()); + qSort(selectedRowIndexes); + for (int l = 0; l < selectedRowIndexes.count(); ++l) { + QCOMPARE(selectedRowIndexes.at(l).row(), expectedRows.at(l)); + QCOMPARE(selectedRowIndexes.at(l).column(), column); + } +} + +void tst_QItemSelectionModel::selectedColumns_data() +{ + QTest::addColumn("rowCount"); + QTest::addColumn("columnCount"); + QTest::addColumn("row"); + QTest::addColumn("selectColumns"); + QTest::addColumn("expectedColumns"); + QTest::addColumn("unexpectedColumns"); + + QTest::newRow("10x10, first columns") + << 10 << 10 << 0 + << (IntList() << 0) + << (IntList() << 0) + << (IntList() << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9); + + QTest::newRow("10x10, first 4 columns") + << 10 << 10 << 0 + << (IntList() << 0 << 1 << 2 << 3) + << (IntList() << 0 << 1 << 2 << 3) + << (IntList() << 4 << 5 << 6 << 7 << 8 << 9); + + QTest::newRow("10x10, last 4 columns") + << 10 << 10 << 0 + << (IntList() << 6 << 7 << 8 << 9) + << (IntList() << 6 << 7 << 8 << 9) + << (IntList() << 0 << 1 << 2 << 3 << 4 << 6); +} + +void tst_QItemSelectionModel::selectedColumns() +{ + QFETCH(int, rowCount); + QFETCH(int, columnCount); + QFETCH(int, row); + QFETCH(IntList, selectColumns); + QFETCH(IntList, expectedColumns); + QFETCH(IntList, unexpectedColumns); + + MyStandardItemModel model(rowCount, columnCount); + QItemSelectionModel selectionModel(&model); + + for (int i = 0; i < selectColumns.count(); ++i) + selectionModel.select(model.index(0, selectColumns.at(i)), + QItemSelectionModel::Select + |QItemSelectionModel::Columns); + + for (int j = 0; j < selectColumns.count(); ++j) + QVERIFY(selectionModel.isColumnSelected(expectedColumns.at(j), QModelIndex())); + + for (int k = 0; k < selectColumns.count(); ++k) + QVERIFY(!selectionModel.isColumnSelected(unexpectedColumns.at(k), QModelIndex())); + + QModelIndexList selectedColumnIndexes = selectionModel.selectedColumns(row); + QCOMPARE(selectedColumnIndexes.count(), expectedColumns.count()); + qSort(selectedColumnIndexes); + for (int l = 0; l < selectedColumnIndexes.count(); ++l) { + QCOMPARE(selectedColumnIndexes.at(l).column(), expectedColumns.at(l)); + QCOMPARE(selectedColumnIndexes.at(l).row(), row); + } +} + +void tst_QItemSelectionModel::setCurrentIndex() +{ + // Build up a simple tree + QStandardItemModel *treemodel = new QStandardItemModel(0, 1); + treemodel->insertRow(0, new QStandardItem(1)); + treemodel->insertRow(1, new QStandardItem(2)); + + QTreeView treeView; + treeView.setModel(treemodel); + QItemSelectionModel *selectionModel = treeView.selectionModel(); + selectionModel->setCurrentIndex( + treemodel->index(0, 0, treemodel->index(0, 0)), + QItemSelectionModel::SelectCurrent); + + QSignalSpy currentSpy(selectionModel, + SIGNAL(currentChanged(QModelIndex,QModelIndex))); + QSignalSpy rowSpy(selectionModel, + SIGNAL(currentRowChanged(QModelIndex,QModelIndex))); + QSignalSpy columnSpy(selectionModel, + SIGNAL(currentColumnChanged(QModelIndex,QModelIndex))); + + // Select the same row and column indexes, but with a different parent + selectionModel->setCurrentIndex( + treemodel->index(0, 0, treemodel->index(1, 0)), + QItemSelectionModel::SelectCurrent); + + QCOMPARE(currentSpy.count(), 1); + QCOMPARE(rowSpy.count(), 1); + QCOMPARE(columnSpy.count(), 1); + + // Select another row in the same parent + selectionModel->setCurrentIndex( + treemodel->index(1, 0, treemodel->index(1, 0)), + QItemSelectionModel::SelectCurrent); + + QCOMPARE(currentSpy.count(), 2); + QCOMPARE(rowSpy.count(), 2); + QCOMPARE(columnSpy.count(), 1); + + delete treemodel; +} + +void tst_QItemSelectionModel::splitOnInsert() +{ + QStandardItemModel model(4, 1); + QItemSelectionModel selectionModel(&model); + selectionModel.select(model.index(2, 0), QItemSelectionModel::Select); + model.insertRow(2); + model.removeRow(3); + QVERIFY(!selectionModel.isSelected(model.index(1, 0))); +} + +void tst_QItemSelectionModel::task196285_rowIntersectsSelection() +{ + QTableWidget table; + table.setColumnCount(1); + table.setRowCount(1); + table.setItem(0, 0, new QTableWidgetItem("foo")); + QAbstractItemModel *model = table.model(); + QItemSelectionModel *selectionModel = table.selectionModel(); + QModelIndex index = model->index(0, 0, QModelIndex()); + + selectionModel->select(index, QItemSelectionModel::Select); + QVERIFY(selectionModel->rowIntersectsSelection(0, QModelIndex())); + QVERIFY(selectionModel->columnIntersectsSelection(0, QModelIndex())); + + selectionModel->select(index, QItemSelectionModel::Deselect); + QVERIFY(!selectionModel->rowIntersectsSelection(0, QModelIndex())); + QVERIFY(!selectionModel->columnIntersectsSelection(0, QModelIndex())); + + selectionModel->select(index, QItemSelectionModel::Toggle); + QVERIFY(selectionModel->rowIntersectsSelection(0, QModelIndex())); + QVERIFY(selectionModel->columnIntersectsSelection(0, QModelIndex())); + + selectionModel->select(index, QItemSelectionModel::Toggle); + QVERIFY(!selectionModel->rowIntersectsSelection(0, QModelIndex())); + QVERIFY(!selectionModel->columnIntersectsSelection(0, QModelIndex())); +} + +void tst_QItemSelectionModel::unselectable() +{ + QTreeWidget w; + for (int i = 0; i < 10; ++i) + w.setItemSelected(new QTreeWidgetItem(&w), true); + QCOMPARE(w.topLevelItemCount(), 10); + QCOMPARE(w.selectionModel()->selectedIndexes().count(), 10); + QCOMPARE(w.selectionModel()->selectedRows().count(), 10); + for (int j = 0; j < 10; ++j) + w.topLevelItem(j)->setFlags(0); + QCOMPARE(w.selectionModel()->selectedIndexes().count(), 0); + QCOMPARE(w.selectionModel()->selectedRows().count(), 0); +} + +void tst_QItemSelectionModel::task220420_selectedIndexes() +{ + QStandardItemModel model(2, 2); + QItemSelectionModel selectionModel(&model); + QItemSelection selection; + selection.append(QItemSelectionRange(model.index(0,0))); + selection.append(QItemSelectionRange(model.index(0,1))); + + //we select the 1st row + selectionModel.select(selection, QItemSelectionModel::Rows | QItemSelectionModel::Select); + + QCOMPARE(selectionModel.selectedRows().count(), 1); + QCOMPARE(selectionModel.selectedIndexes().count(), model.columnCount()); +} + + +class QtTestTableModel: public QAbstractTableModel +{ + Q_OBJECT + + public: + QtTestTableModel(int rows = 0, int columns = 0, QObject *parent = 0) + : QAbstractTableModel(parent), + row_count(rows), + column_count(columns) {} + + int rowCount(const QModelIndex& = QModelIndex()) const { return row_count; } + int columnCount(const QModelIndex& = QModelIndex()) const { return column_count; } + bool isEditable(const QModelIndex &) const { return true; } + + QVariant data(const QModelIndex &idx, int role) const + { + if (role == Qt::DisplayRole || role == Qt::EditRole) + return QString("[%1,%2]").arg(idx.row()).arg(idx.column()); + return QVariant(); + } + + int row_count; + int column_count; + friend class tst_QItemSelectionModel; +}; + + +void tst_QItemSelectionModel::task240734_layoutChanged() +{ + QtTestTableModel model(1,1); + QItemSelectionModel selectionModel(&model); + selectionModel.select(model.index(0,0), QItemSelectionModel::Select); + QCOMPARE(selectionModel.selectedIndexes().count() , 1); + + emit model.layoutAboutToBeChanged(); + model.row_count = 5; + emit model.layoutChanged(); + + //The selection should not change. + QCOMPARE(selectionModel.selectedIndexes().count() , 1); + QCOMPARE(selectionModel.selectedIndexes().first() , model.index(0,0)); +} + +void tst_QItemSelectionModel::merge_data() +{ + QTest::addColumn("init"); + QTest::addColumn("other"); + QTest::addColumn("command"); + QTest::addColumn("result"); + + QTest::newRow("Simple select") + << QItemSelection() + << QItemSelection(model->index(2, 1) , model->index(3, 4)) + << int(QItemSelectionModel::Select) + << QItemSelection(model->index(2, 1) , model->index(3, 4)); + + QTest::newRow("Simple deselect") + << QItemSelection(model->index(2, 1) , model->index(3, 4)) + << QItemSelection(model->index(2, 1) , model->index(3, 4)) + << int(QItemSelectionModel::Deselect) + << QItemSelection(); + + QTest::newRow("Simple Toggle deselect") + << QItemSelection(model->index(2, 1) , model->index(3, 4)) + << QItemSelection(model->index(2, 1) , model->index(3, 4)) + << int(QItemSelectionModel::Toggle) + << QItemSelection(); + + QTest::newRow("Simple Toggle select") + << QItemSelection() + << QItemSelection(model->index(2, 1) , model->index(3, 4)) + << int(QItemSelectionModel::Toggle) + << QItemSelection(model->index(2, 1) , model->index(3, 4)); + + QTest::newRow("Add select") + << QItemSelection(model->index(2, 1) , model->index(3, 3)) + << QItemSelection(model->index(2, 2) , model->index(3, 4)) + << int(QItemSelectionModel::Select) + << QItemSelection(model->index(2, 1) , model->index(3, 4)); + + QTest::newRow("Deselect") + << QItemSelection(model->index(2, 1) , model->index(3, 4)) + << QItemSelection(model->index(2, 2) , model->index(3, 4)) + << int(QItemSelectionModel::Deselect) + << QItemSelection(model->index(2, 1) , model->index(3, 1)); + + QItemSelection r1(model->index(2, 1) , model->index(3, 1)); + r1.select(model->index(2, 4) , model->index(3, 4)); + QTest::newRow("Toggle") + << QItemSelection(model->index(2, 1) , model->index(3, 3)) + << QItemSelection(model->index(2, 2) , model->index(3, 4)) + << int(QItemSelectionModel::Toggle) + << r1; +} + + +void tst_QItemSelectionModel::merge() +{ + QFETCH(QItemSelection, init); + QFETCH(QItemSelection, other); + QFETCH(int, command); + QFETCH(QItemSelection, result); + + init.merge(other, QItemSelectionModel::SelectionFlags(command)); + + foreach(const QModelIndex &idx, init.indexes()) + QVERIFY(result.contains(idx)); + foreach(const QModelIndex &idx, result.indexes()) + QVERIFY(init.contains(idx)); +} + +void tst_QItemSelectionModel::task119433_isRowSelected() +{ + QStandardItemModel model(2,2); + model.setData(model.index(0,0), 0, Qt::UserRole - 1); + QItemSelectionModel sel(&model); + sel.select( QItemSelection(model.index(0,0), model.index(0, 1)), QItemSelectionModel::Select); + QCOMPARE(sel.selectedIndexes().count(), 1); + QVERIFY(sel.isRowSelected(0, QModelIndex())); +} + +void tst_QItemSelectionModel::task252069_rowIntersectsSelection() +{ + QStandardItemModel m; + for (int i=0; i<8; ++i) { + for (int j=0; j<8; ++j) { + QStandardItem *item = new QStandardItem(QString("Item number %1").arg(i)); + if ((i % 2 == 0 && j == 0) || + (j % 2 == 0 && i == 0) || + j == 5 || i == 5 ) { + item->setEnabled(false); + //item->setSelectable(false); + } + m.setItem(i, j, item); + } + } + + QItemSelectionModel selected(&m); + //nothing is selected + QVERIFY(!selected.rowIntersectsSelection(0, QModelIndex())); + QVERIFY(!selected.rowIntersectsSelection(2, QModelIndex())); + QVERIFY(!selected.rowIntersectsSelection(3, QModelIndex())); + QVERIFY(!selected.rowIntersectsSelection(5, QModelIndex())); + QVERIFY(!selected.columnIntersectsSelection(0, QModelIndex())); + QVERIFY(!selected.columnIntersectsSelection(2, QModelIndex())); + QVERIFY(!selected.columnIntersectsSelection(3, QModelIndex())); + QVERIFY(!selected.columnIntersectsSelection(5, QModelIndex())); + selected.select(m.index(2, 0), QItemSelectionModel::Select | QItemSelectionModel::Rows); + QVERIFY(!selected.rowIntersectsSelection(0, QModelIndex())); + QVERIFY( selected.rowIntersectsSelection(2, QModelIndex())); + QVERIFY(!selected.rowIntersectsSelection(3, QModelIndex())); + QVERIFY(!selected.rowIntersectsSelection(5, QModelIndex())); + QVERIFY(!selected.columnIntersectsSelection(0, QModelIndex())); + QVERIFY( selected.columnIntersectsSelection(2, QModelIndex())); + QVERIFY( selected.columnIntersectsSelection(3, QModelIndex())); + QVERIFY(!selected.columnIntersectsSelection(5, QModelIndex())); + selected.select(m.index(0, 5), QItemSelectionModel::Select | QItemSelectionModel::Columns); + QVERIFY(!selected.rowIntersectsSelection(0, QModelIndex())); + QVERIFY( selected.rowIntersectsSelection(2, QModelIndex())); + QVERIFY(!selected.rowIntersectsSelection(3, QModelIndex())); + QVERIFY(!selected.rowIntersectsSelection(5, QModelIndex())); + QVERIFY(!selected.columnIntersectsSelection(0, QModelIndex())); + QVERIFY( selected.columnIntersectsSelection(2, QModelIndex())); + QVERIFY( selected.columnIntersectsSelection(3, QModelIndex())); + QVERIFY(!selected.columnIntersectsSelection(5, QModelIndex())); +} + +void tst_QItemSelectionModel::task232634_childrenDeselectionSignal() +{ + QStandardItemModel model; + + QStandardItem *parentItem = model.invisibleRootItem(); + for (int i = 0; i < 4; ++i) { + QStandardItem *item = new QStandardItem(QString("item %0").arg(i)); + parentItem->appendRow(item); + parentItem = item; + } + + QModelIndex root = model.index(0,0); + QModelIndex par = root.child(0,0); + QModelIndex sel = par.child(0,0); + + QItemSelectionModel selectionModel(&model); + selectionModel.select(sel, QItemSelectionModel::SelectCurrent); + + QSignalSpy deselectSpy(&selectionModel, SIGNAL(selectionChanged(const QItemSelection& , const QItemSelection&))); + model.removeRows(0, 1, root); + QVERIFY(deselectSpy.count() == 1); + + // More testing stress for the patch. + model.clear(); + selectionModel.clear(); + + parentItem = model.invisibleRootItem(); + for (int i = 0; i < 2; ++i) { + QStandardItem *item = new QStandardItem(QString("item %0").arg(i)); + parentItem->appendRow(item); + } + for (int i = 0; i < 2; ++i) { + parentItem = model.invisibleRootItem()->child(i, 0); + for (int j = 0; j < 2; ++j) { + QStandardItem *item = new QStandardItem(QString("item %0.%1").arg(i).arg(j)); + parentItem->appendRow(item); + } + } + + sel = model.index(0, 0).child(0, 0); + selectionModel.select(sel, QItemSelectionModel::Select); + QModelIndex sel2 = model.index(1, 0).child(0, 0); + selectionModel.select(sel2, QItemSelectionModel::Select); + + QVERIFY(selectionModel.selection().contains(sel)); + QVERIFY(selectionModel.selection().contains(sel2)); + deselectSpy.clear(); + model.removeRow(0, model.index(0, 0)); + QVERIFY(deselectSpy.count() == 1); + QVERIFY(!selectionModel.selection().contains(sel)); + QVERIFY(selectionModel.selection().contains(sel2)); +} + +void tst_QItemSelectionModel::task260134_layoutChangedWithAllSelected() +{ + QStringListModel model( QStringList() << "foo" << "bar" << "foo2"); + QSortFilterProxyModel proxy; + proxy.setSourceModel(&model); + QItemSelectionModel selection(&proxy); + + + QCOMPARE(model.rowCount(), 3); + QCOMPARE(proxy.rowCount(), 3); + proxy.setFilterRegExp( QRegExp("f")); + QCOMPARE(proxy.rowCount(), 2); + + QList indexList; + indexList << proxy.index(0,0) << proxy.index(1,0); + selection.select( QItemSelection(indexList.first(), indexList.last()), QItemSelectionModel::Select); + + //let's check the selection hasn't changed + QCOMPARE(selection.selectedIndexes().count(), indexList.count()); + foreach(QPersistentModelIndex index, indexList) + QVERIFY(selection.isSelected(index)); + + proxy.setFilterRegExp(QRegExp()); + QCOMPARE(proxy.rowCount(), 3); + + //let's check the selection hasn't changed + QCOMPARE(selection.selectedIndexes().count(), indexList.count()); + foreach(QPersistentModelIndex index, indexList) + QVERIFY(selection.isSelected(index)); +} + + +void tst_QItemSelectionModel::QTBUG5671_layoutChangedWithAllSelected() +{ + struct MyFilterModel : public QSortFilterProxyModel + { // Override sort filter proxy to remove even numbered rows. + bool filtering; + virtual bool filterAcceptsRow( int source_row, const QModelIndex& /* source_parent */) const + { + return !filtering || !( source_row & 1 ); + } + }; + + //same as task260134_layoutChangedWithAllSelected but with a sightly bigger model + + enum { cNumRows=30, cNumCols=20 }; + + QStandardItemModel model(cNumRows, cNumCols); + MyFilterModel proxy; + proxy.filtering = true; + proxy.setSourceModel(&model); + QItemSelectionModel selection(&proxy); + + // Populate the tree view. + for (unsigned int i = 0; i < cNumCols; i++) + model.setHeaderData( i, Qt::Horizontal, QString::fromLatin1("Column %1").arg(i)); + + for (unsigned int r = 0; r < cNumRows; r++) { + for (unsigned int c = 0; c < cNumCols; c++) { + model.setData(model.index(r, c, QModelIndex()), + QString::fromLatin1("r:%1/c:%2").arg(r, c)); + } + } + + + QCOMPARE(model.rowCount(), int(cNumRows)); + QCOMPARE(proxy.rowCount(), int(cNumRows/2)); + + selection.select( QItemSelection(proxy.index(0,0), proxy.index(proxy.rowCount() - 1, proxy.columnCount() - 1)), QItemSelectionModel::Select); + + QList indexList; + foreach(const QModelIndex &id, selection.selectedIndexes()) + indexList << id; + + proxy.filtering = false; + proxy.invalidate(); + QCOMPARE(proxy.rowCount(), int(cNumRows)); + + //let's check the selection hasn't changed + QCOMPARE(selection.selectedIndexes().count(), indexList.count()); + foreach(QPersistentModelIndex index, indexList) + QVERIFY(selection.isSelected(index)); +} + +void tst_QItemSelectionModel::QTBUG2804_layoutChangedTreeSelection() +{ + QStandardItemModel model; + QStandardItem top1("Child1"), top2("Child2"), top3("Child3"); + QStandardItem sub11("Alpha"), sub12("Beta"), sub13("Gamma"), sub14("Delta"), + sub21("Alpha"), sub22("Beta"), sub23("Gamma"), sub24("Delta"); + top1.appendColumn(QList() << &sub11 << &sub12 << &sub13 << &sub14); + top2.appendColumn(QList() << &sub21 << &sub22 << &sub23 << &sub24); + model.appendColumn(QList() << &top1 << &top2 << &top3); + + QItemSelectionModel selModel(&model); + + selModel.select(sub11.index(), QItemSelectionModel::Select); + selModel.select(sub12.index(), QItemSelectionModel::Select); + selModel.select(sub21.index(), QItemSelectionModel::Select); + selModel.select(sub23.index(), QItemSelectionModel::Select); + + QModelIndexList list = selModel.selectedIndexes(); + QCOMPARE(list.count(), 4); + + model.sort(0); //this will provoke a relayout + + QCOMPARE(selModel.selectedIndexes().count(), 4); +} + +class RemovalObserver : public QObject +{ + Q_OBJECT + QItemSelectionModel *m_itemSelectionModel; +public: + RemovalObserver(QItemSelectionModel *selectionModel) + : m_itemSelectionModel(selectionModel) + { + connect(m_itemSelectionModel, SIGNAL(selectionChanged(QItemSelection, QItemSelection)), SLOT(selectionChanged(QItemSelection, QItemSelection))); + } + +public slots: + void selectionChanged(const QItemSelection & /* selected */, const QItemSelection &deselected) + { + foreach(const QModelIndex &index, deselected.indexes()) { + QVERIFY(!m_itemSelectionModel->selection().contains(index)); + } + QVERIFY(m_itemSelectionModel->selection().size() == 2); + } + +}; + +void tst_QItemSelectionModel::deselectRemovedMiddleRange() +{ + QStandardItemModel model(8, 0); + + for (int row = 0; row < 8; ++row) { + static const int column = 0; + QStandardItem *item = new QStandardItem(QString::number(row)); + model.setItem(row, column, item); + } + + QItemSelectionModel selModel(&model); + + selModel.select(QItemSelection(model.index(3, 0), model.index(6, 0)), QItemSelectionModel::Select); + + QVERIFY(selModel.selection().size() == 1); + + RemovalObserver ro(&selModel); + + QSignalSpy spy(&selModel, SIGNAL(selectionChanged(QItemSelection, QItemSelection))); + bool ok = model.removeRows(4, 2); + + QVERIFY(ok); + QVERIFY(spy.size() == 1); +} + +static QStandardItemModel* getModel(QObject *parent) +{ + QStandardItemModel *model = new QStandardItemModel(parent); + + for (int i = 0; i < 4; ++i) { + QStandardItem *parentItem = model->invisibleRootItem(); + QList list; + for (int j = 0; j < 4; ++j) { + list.append(new QStandardItem(QString("item %1, %2").arg(i).arg(j))); + } + parentItem->appendRow(list); + parentItem = list.first(); + for (int j = 0; j < 4; ++j) { + QList list; + for (int k = 0; k < 4; ++k) { + list.append(new QStandardItem(QString("item %1, %2").arg(i).arg(j))); + } + parentItem->appendRow(list); + } + } + return model; +} + +enum Result { + LessThan, + NotLessThan, + NotEqual +}; + +Q_DECLARE_METATYPE(Result); + +void tst_QItemSelectionModel::rangeOperatorLessThan_data() +{ + QTest::addColumn("parent1"); + QTest::addColumn("top1"); + QTest::addColumn("left1"); + QTest::addColumn("bottom1"); + QTest::addColumn("right1"); + QTest::addColumn("parent2"); + QTest::addColumn("top2"); + QTest::addColumn("left2"); + QTest::addColumn("bottom2"); + QTest::addColumn("right2"); + QTest::addColumn("result"); + + QTest::newRow("lt01") << -1 << 0 << 0 << 3 << 3 + << -1 << 0 << 0 << 3 << 3 << NotLessThan; + + QTest::newRow("lt02") << -1 << 0 << 0 << 2 << 3 + << -1 << 0 << 0 << 3 << 3 << LessThan; + QTest::newRow("lt03") << -1 << 0 << 0 << 3 << 2 + << -1 << 0 << 0 << 3 << 3 << LessThan; + QTest::newRow("lt04") << -1 << 0 << 0 << 2 << 2 + << -1 << 0 << 0 << 3 << 3 << LessThan; + + QTest::newRow("lt05") << -1 << 0 << 0 << 3 << 3 + << -1 << 0 << 0 << 2 << 3 << NotLessThan; + QTest::newRow("lt06") << -1 << 0 << 0 << 3 << 3 + << -1 << 0 << 0 << 3 << 2 << NotLessThan; + QTest::newRow("lt07") << -1 << 0 << 0 << 3 << 3 + << -1 << 0 << 0 << 2 << 2 << NotLessThan; + + QTest::newRow("lt08") << -1 << 0 << 0 << 3 << 3 + << 0 << 0 << 0 << 3 << 3 << NotEqual; + QTest::newRow("lt09") << 1 << 0 << 0 << 3 << 3 + << 0 << 0 << 0 << 3 << 3 << NotEqual; + QTest::newRow("lt10") << 1 << 0 << 0 << 1 << 1 + << 0 << 2 << 2 << 3 << 3 << NotEqual; + QTest::newRow("lt11") << 1 << 2 << 2 << 3 << 3 + << 0 << 0 << 0 << 1 << 1 << NotEqual; + + QTest::newRow("lt12") << -1 << 0 << 0 << 1 << 1 + << -1 << 2 << 2 << 3 << 3 << LessThan; + QTest::newRow("lt13") << -1 << 2 << 2 << 3 << 3 + << -1 << 0 << 0 << 1 << 1 << NotLessThan; + QTest::newRow("lt14") << 1 << 0 << 0 << 1 << 1 + << 1 << 2 << 2 << 3 << 3 << LessThan; + QTest::newRow("lt15") << 1 << 2 << 2 << 3 << 3 + << 1 << 0 << 0 << 1 << 1 << NotLessThan; + + QTest::newRow("lt16") << -1 << 0 << 0 << 2 << 2 + << -1 << 1 << 1 << 3 << 3 << LessThan; + QTest::newRow("lt17") << -1 << 1 << 1 << 3 << 3 + << -1 << 0 << 0 << 2 << 2 << NotLessThan; + QTest::newRow("lt18") << 1 << 0 << 0 << 2 << 2 + << 1 << 1 << 1 << 3 << 3 << LessThan; + QTest::newRow("lt19") << 1 << 1 << 1 << 3 << 3 + << 1 << 0 << 0 << 2 << 2 << NotLessThan; +} + +void tst_QItemSelectionModel::rangeOperatorLessThan() +{ + QStandardItemModel *model1 = getModel(this); + QStandardItemModel *model2 = getModel(this); + + QFETCH(int, parent1); + QFETCH(int, top1); + QFETCH(int, left1); + QFETCH(int, bottom1); + QFETCH(int, right1); + QFETCH(int, parent2); + QFETCH(int, top2); + QFETCH(int, left2); + QFETCH(int, bottom2); + QFETCH(int, right2); + QFETCH(Result, result); + + QModelIndex p1 = model1->index(parent1, 0); + + QModelIndex tl1 = model1->index(top1, left1, p1); + QModelIndex br1 = model1->index(bottom1, right1, p1); + + QItemSelectionRange r1(tl1, br1); + + QModelIndex p2 = model1->index(parent2, 0); + + QModelIndex tl2 = model1->index(top2, left2, p2); + QModelIndex br2 = model1->index(bottom2, right2, p2); + + QItemSelectionRange r2(tl2, br2); + + if (result == LessThan) + QVERIFY(r1 < r2); + else if (result == NotLessThan) + QVERIFY(!(r1 < r2)); + else if (result == NotEqual) + if (!(r1 < r2)) + QVERIFY(r2 < r1); + + // Ranges in different models are always non-equal + + QModelIndex p3 = model2->index(parent1, 0); + + QModelIndex tl3 = model2->index(top1, left1, p3); + QModelIndex br3 = model2->index(bottom1, right1, p3); + + QItemSelectionRange r3(tl3, br3); + + if (!(r1 < r3)) + QVERIFY(r3 < r1); + + if (!(r2 < r3)) + QVERIFY(r3 < r2); + + QModelIndex p4 = model2->index(parent2, 0); + + QModelIndex tl4 = model2->index(top2, left2, p4); + QModelIndex br4 = model2->index(bottom2, right2, p4); + + QItemSelectionRange r4(tl4, br4); + + if (!(r1 < r4)) + QVERIFY(r4 < r1); + + if (!(r2 < r4)) + QVERIFY(r4 < r2); +} + +void tst_QItemSelectionModel::testDifferentModels() +{ + QStandardItemModel model1; + QStandardItemModel model2; + QStandardItem top11("Child1"), top12("Child2"), top13("Child3"); + QStandardItem top21("Child1"), top22("Child2"), top23("Child3"); + + model1.appendColumn(QList() << &top11 << &top12 << &top13); + model2.appendColumn(QList() << &top21 << &top22 << &top23); + + + QModelIndex topIndex1 = model1.index(0, 0); + QModelIndex bottomIndex1 = model1.index(2, 0); + QModelIndex topIndex2 = model2.index(0, 0); + + QItemSelectionRange range(topIndex1, bottomIndex1); + + QVERIFY(range.intersects(QItemSelectionRange(topIndex1, topIndex1))); + QVERIFY(!range.intersects(QItemSelectionRange(topIndex2, topIndex2))); + + QItemSelection newSelection; + QItemSelection::split(range, QItemSelectionRange(topIndex2, topIndex2), &newSelection); + + QVERIFY(newSelection.isEmpty()); +} + +class SelectionObserver : public QObject +{ + Q_OBJECT +public: + SelectionObserver(QAbstractItemModel *model, QObject *parent = 0) + : QObject(parent), m_model(model), m_selectionModel(0) + { + connect(model, SIGNAL(modelReset()), SLOT(modelReset())); + } + + void setSelectionModel(QItemSelectionModel *selectionModel) + { + m_selectionModel = selectionModel; + connect(m_selectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)), SLOT(selectionChanged(QItemSelection,QItemSelection))); + } + + private slots: + void modelReset() + { + const QModelIndex idx = m_model->index(2, 0); + QVERIFY(idx.isValid()); + m_selectionModel->select(QItemSelection(idx, idx), QItemSelectionModel::Clear); + } + + void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) + { + foreach(const QItemSelectionRange &range, selected) + QVERIFY(range.isValid()); + foreach(const QItemSelectionRange &range, deselected) + QVERIFY(range.isValid()); + } + +private: + QAbstractItemModel *m_model; + QItemSelectionModel *m_selectionModel; +}; + +void tst_QItemSelectionModel::testValidRangesInSelectionsAfterReset() +{ + QStringListModel model; + + QStringList strings; + strings << "one" + << "two" + << "three" + << "four" + << "five"; + + model.setStringList(strings); + + SelectionObserver observer(&model); + + QItemSelectionModel selectionModel(&model); + + selectionModel.select(QItemSelection(model.index(1, 0), model.index(3, 0)), QItemSelectionModel::Select); + + // Cause d->ranges to contain something. + model.insertRows(2, 1); + + observer.setSelectionModel(&selectionModel); + + model.setStringList(strings); +} + +class DuplicateItemSelectionModel : public QItemSelectionModel +{ + Q_OBJECT +public: + DuplicateItemSelectionModel(QItemSelectionModel *target, QAbstractItemModel *model, QObject *parent = 0) + : QItemSelectionModel(model, parent), m_target(target) + { + + } + + void select(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command) + { + QItemSelectionModel::select(selection, command); + m_target->select(selection, command); + } + + using QItemSelectionModel::select; + + void setCurrentIndex(const QModelIndex &index, QItemSelectionModel::SelectionFlags command) + { + QItemSelectionModel::setCurrentIndex(index, command); + m_target->setCurrentIndex(index, command); + } + + void clearCurrentIndex() + { + QItemSelectionModel::clearCurrentIndex(); + m_target->clearCurrentIndex(); + } + +private: + QItemSelectionModel *m_target; + +}; + +void tst_QItemSelectionModel::testChainedSelectionClear() +{ + QStringListModel model(QStringList() << "Apples" << "Pears"); + + QItemSelectionModel selectionModel(&model, 0); + DuplicateItemSelectionModel duplicate(&selectionModel, &model, 0); + + duplicate.select(model.index(0, 0), QItemSelectionModel::Select); + + { + QModelIndexList selectedIndexes = selectionModel.selection().indexes(); + QModelIndexList duplicatedIndexes = duplicate.selection().indexes(); + + QVERIFY(selectedIndexes.size() == duplicatedIndexes.size()); + QVERIFY(selectedIndexes.size() == 1); + QVERIFY(selectedIndexes.first() == model.index(0, 0)); + } + + duplicate.clearSelection(); + + { + QModelIndexList selectedIndexes = selectionModel.selection().indexes(); + QModelIndexList duplicatedIndexes = duplicate.selection().indexes(); + + QVERIFY(selectedIndexes.size() == duplicatedIndexes.size()); + QVERIFY(selectedIndexes.size() == 0); + } + + duplicate.setCurrentIndex(model.index(0, 0), QItemSelectionModel::NoUpdate); + + QVERIFY(selectionModel.currentIndex() == duplicate.currentIndex()); + + duplicate.clearCurrentIndex(); + + QVERIFY(!duplicate.currentIndex().isValid()); + QVERIFY(selectionModel.currentIndex() == duplicate.currentIndex()); +} + +void tst_QItemSelectionModel::testClearCurrentIndex() +{ + QStringListModel model(QStringList() << "Apples" << "Pears"); + + QItemSelectionModel selectionModel(&model, 0); + + QSignalSpy currentIndexSpy(&selectionModel, SIGNAL(currentChanged(QModelIndex,QModelIndex))); + + QModelIndex firstIndex = model.index(0, 0); + QVERIFY(firstIndex.isValid()); + selectionModel.setCurrentIndex(firstIndex, QItemSelectionModel::NoUpdate); + QVERIFY(selectionModel.currentIndex() == firstIndex); + QVERIFY(currentIndexSpy.size() == 1); + + selectionModel.clearCurrentIndex(); + + QVERIFY(selectionModel.currentIndex() == QModelIndex()); + QVERIFY(currentIndexSpy.size() == 2); +} + +QTEST_MAIN(tst_QItemSelectionModel) +#include "tst_qitemselectionmodel.moc" diff --git a/tests/auto/corelib/itemmodels/qsortfilterproxymodel/.gitignore b/tests/auto/corelib/itemmodels/qsortfilterproxymodel/.gitignore new file mode 100644 index 0000000000..d3672fe4ae --- /dev/null +++ b/tests/auto/corelib/itemmodels/qsortfilterproxymodel/.gitignore @@ -0,0 +1 @@ +tst_qsortfilterproxymodel diff --git a/tests/auto/corelib/itemmodels/qsortfilterproxymodel/qsortfilterproxymodel.pro b/tests/auto/corelib/itemmodels/qsortfilterproxymodel/qsortfilterproxymodel.pro new file mode 100644 index 0000000000..d6e949f73d --- /dev/null +++ b/tests/auto/corelib/itemmodels/qsortfilterproxymodel/qsortfilterproxymodel.pro @@ -0,0 +1,9 @@ +CONFIG += testcase +TARGET = tst_qsortfilterproxymodel + +QT += gui widgets testlib +mtdir = ../../../other/modeltest + +INCLUDEPATH += $$PWD/$${mtdir} +SOURCES += tst_qsortfilterproxymodel.cpp $${mtdir}/dynamictreemodel.cpp $${mtdir}/modeltest.cpp +HEADERS += $${mtdir}/dynamictreemodel.h $${mtdir}/modeltest.h diff --git a/tests/auto/corelib/itemmodels/qsortfilterproxymodel/tst_qsortfilterproxymodel.cpp b/tests/auto/corelib/itemmodels/qsortfilterproxymodel/tst_qsortfilterproxymodel.cpp new file mode 100644 index 0000000000..cc8299e28f --- /dev/null +++ b/tests/auto/corelib/itemmodels/qsortfilterproxymodel/tst_qsortfilterproxymodel.cpp @@ -0,0 +1,3463 @@ +/**************************************************************************** +** +** 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 +#include "dynamictreemodel.h" +#include "modeltest.h" + +#include +#include +#include + +#include + +typedef QList IntList; +typedef QPair IntPair; +typedef QList IntPairList; + +Q_DECLARE_METATYPE(IntList) +Q_DECLARE_METATYPE(IntPair) +Q_DECLARE_METATYPE(IntPairList) +Q_DECLARE_METATYPE(QModelIndex) + +class tst_QSortFilterProxyModel : public QObject +{ + Q_OBJECT + +public: + + tst_QSortFilterProxyModel(); + virtual ~tst_QSortFilterProxyModel(); + +public slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + +private slots: + void getSetCheck(); + void sort_data(); + void sort(); + void sortHierarchy_data(); + void sortHierarchy(); + + void insertRows_data(); + void insertRows(); + void prependRow(); +// void insertColumns_data(); +// void insertColumns(); + void removeRows_data(); + void removeRows(); + void removeColumns_data(); + void removeColumns(); + void insertAfterSelect(); + void removeAfterSelect(); + void filter_data(); + void filter(); + void filterHierarchy_data(); + void filterHierarchy(); + void filterColumns_data(); + void filterColumns(); + + void filterTable(); + void filterCurrent(); + + void changeSourceLayout(); + void removeSourceRows_data(); + void removeSourceRows(); + void insertSourceRows_data(); + void insertSourceRows(); + void changeFilter_data(); + void changeFilter(); + void changeSourceData_data(); + void changeSourceData(); + void sortFilterRole(); + void selectionFilteredOut(); + void match_data(); + void match(); + void insertIntoChildrenlessItem(); + void invalidateMappedChildren(); + void insertRowIntoFilteredParent(); + void filterOutParentAndFilterInChild(); + + void sourceInsertRows(); + void sourceModelDeletion(); + + void sortColumnTracking1(); + void sortColumnTracking2(); + + void sortStable(); + + void task236755_hiddenColumns(); + void task247867_insertRowsSort(); + void task248868_staticSorting(); + void task248868_dynamicSorting(); + void task250023_fetchMore(); + void task251296_hiddenChildren(); + void task252507_mapFromToSource(); + void task255652_removeRowsRecursive(); + void taskQTBUG_6205_doubleProxySelectionSetSourceModel(); + void taskQTBUG_7537_appearsAndSort(); + void taskQTBUG_7716_unnecessaryDynamicSorting(); + void taskQTBUG_10287_unnecessaryMapCreation(); + void taskQTBUG_17812_resetInvalidate_data(); + void taskQTBUG_17812_resetInvalidate(); + + void testMultipleProxiesWithSelection(); + void mapSelectionFromSource(); + void filteredColumns(); + + void testParentLayoutChanged(); + void moveSourceRows(); + +protected: + void buildHierarchy(const QStringList &data, QAbstractItemModel *model); + void checkHierarchy(const QStringList &data, const QAbstractItemModel *model); + +private: + QStandardItemModel *m_model; + QSortFilterProxyModel *m_proxy; +}; + +// Testing get/set functions +void tst_QSortFilterProxyModel::getSetCheck() +{ + QSortFilterProxyModel obj1; + QCOMPARE(obj1.sourceModel(), (QAbstractItemModel *)0); + // int QSortFilterProxyModel::filterKeyColumn() + // void QSortFilterProxyModel::setFilterKeyColumn(int) + obj1.setFilterKeyColumn(0); + QCOMPARE(0, obj1.filterKeyColumn()); + obj1.setFilterKeyColumn(INT_MIN); + QCOMPARE(INT_MIN, obj1.filterKeyColumn()); + obj1.setFilterKeyColumn(INT_MAX); + QCOMPARE(INT_MAX, obj1.filterKeyColumn()); +} + +tst_QSortFilterProxyModel::tst_QSortFilterProxyModel() + : m_model(0), m_proxy(0) +{ + +} + +tst_QSortFilterProxyModel::~tst_QSortFilterProxyModel() +{ + +} + +void tst_QSortFilterProxyModel::initTestCase() +{ + qRegisterMetaType("QModelIndex"); + qRegisterMetaType("IntList"); + qRegisterMetaType("IntPair"); + qRegisterMetaType("IntPairList"); + m_model = new QStandardItemModel(0, 1); + m_proxy = new QSortFilterProxyModel(); + m_proxy->setSourceModel(m_model); +} + +void tst_QSortFilterProxyModel::cleanupTestCase() +{ + delete m_proxy; + delete m_model; +} + +void tst_QSortFilterProxyModel::init() +{ +} + +void tst_QSortFilterProxyModel::cleanup() +{ + m_proxy->setFilterRegExp(QRegExp()); + m_proxy->sort(-1, Qt::AscendingOrder); + m_model->clear(); + m_model->insertColumns(0, 1); +} + +/* + tests +*/ + +void tst_QSortFilterProxyModel::sort_data() +{ + QTest::addColumn("sortOrder"); + QTest::addColumn("sortCaseSensitivity"); + QTest::addColumn("initial"); + QTest::addColumn("expected"); + + QTest::newRow("flat descending") << static_cast(Qt::DescendingOrder) + << int(Qt::CaseSensitive) + << (QStringList() + << "delta" + << "yankee" + << "bravo" + << "lima" + << "charlie" + << "juliet" + << "tango" + << "hotel" + << "uniform" + << "alpha" + << "echo" + << "golf" + << "quebec" + << "foxtrot" + << "india" + << "romeo" + << "november" + << "oskar" + << "zulu" + << "kilo" + << "whiskey" + << "mike" + << "papa" + << "sierra" + << "xray" + << "viktor") + << (QStringList() + << "zulu" + << "yankee" + << "xray" + << "whiskey" + << "viktor" + << "uniform" + << "tango" + << "sierra" + << "romeo" + << "quebec" + << "papa" + << "oskar" + << "november" + << "mike" + << "lima" + << "kilo" + << "juliet" + << "india" + << "hotel" + << "golf" + << "foxtrot" + << "echo" + << "delta" + << "charlie" + << "bravo" + << "alpha"); + QTest::newRow("flat ascending") << static_cast(Qt::AscendingOrder) + << int(Qt::CaseSensitive) + << (QStringList() + << "delta" + << "yankee" + << "bravo" + << "lima" + << "charlie" + << "juliet" + << "tango" + << "hotel" + << "uniform" + << "alpha" + << "echo" + << "golf" + << "quebec" + << "foxtrot" + << "india" + << "romeo" + << "november" + << "oskar" + << "zulu" + << "kilo" + << "whiskey" + << "mike" + << "papa" + << "sierra" + << "xray" + << "viktor") + << (QStringList() + << "alpha" + << "bravo" + << "charlie" + << "delta" + << "echo" + << "foxtrot" + << "golf" + << "hotel" + << "india" + << "juliet" + << "kilo" + << "lima" + << "mike" + << "november" + << "oskar" + << "papa" + << "quebec" + << "romeo" + << "sierra" + << "tango" + << "uniform" + << "viktor" + << "whiskey" + << "xray" + << "yankee" + << "zulu"); + QTest::newRow("case insensitive") << static_cast(Qt::AscendingOrder) + << int(Qt::CaseInsensitive) + << (QStringList() + << "alpha" << "BETA" << "Gamma" << "delta") + << (QStringList() + << "alpha" << "BETA" << "delta" << "Gamma"); + QTest::newRow("case sensitive") << static_cast(Qt::AscendingOrder) + << int(Qt::CaseSensitive) + << (QStringList() + << "alpha" << "BETA" << "Gamma" << "delta") + << (QStringList() + << "BETA" << "Gamma" << "alpha" << "delta"); + + + QStringList list; + for (int i = 10000; i < 20000; ++i) + list.append(QString("Number: %1").arg(i)); + QTest::newRow("large set ascending") << static_cast(Qt::AscendingOrder) << int(Qt::CaseSensitive) << list << list; +} + +void tst_QSortFilterProxyModel::sort() +{ + QFETCH(int, sortOrder); + QFETCH(int, sortCaseSensitivity); + QFETCH(QStringList, initial); + QFETCH(QStringList, expected); + + // prepare model + QStandardItem *root = m_model->invisibleRootItem (); + QList items; + for (int i = 0; i < initial.count(); ++i) { + items.append(new QStandardItem(initial.at(i))); + } + root->insertRows(0, items); + QCOMPARE(m_model->rowCount(QModelIndex()), initial.count()); + QCOMPARE(m_model->columnCount(QModelIndex()), 1); + + // make sure the proxy is unsorted + QCOMPARE(m_proxy->columnCount(QModelIndex()), 1); + QCOMPARE(m_proxy->rowCount(QModelIndex()), initial.count()); + for (int row = 0; row < m_proxy->rowCount(QModelIndex()); ++row) { + QModelIndex index = m_proxy->index(row, 0, QModelIndex()); + QCOMPARE(m_proxy->data(index, Qt::DisplayRole).toString(), initial.at(row)); + } + + // sort + m_proxy->sort(0, static_cast(sortOrder)); + m_proxy->setSortCaseSensitivity(static_cast(sortCaseSensitivity)); + + // make sure the model is unchanged + for (int row = 0; row < m_model->rowCount(QModelIndex()); ++row) { + QModelIndex index = m_model->index(row, 0, QModelIndex()); + QCOMPARE(m_model->data(index, Qt::DisplayRole).toString(), initial.at(row)); + } + // make sure the proxy is sorted + for (int row = 0; row < m_proxy->rowCount(QModelIndex()); ++row) { + QModelIndex index = m_proxy->index(row, 0, QModelIndex()); + QCOMPARE(m_proxy->data(index, Qt::DisplayRole).toString(), expected.at(row)); + } + + // restore the unsorted order + m_proxy->sort(-1); + + // make sure the proxy is unsorted again + for (int row = 0; row < m_proxy->rowCount(QModelIndex()); ++row) { + QModelIndex index = m_proxy->index(row, 0, QModelIndex()); + QCOMPARE(m_proxy->data(index, Qt::DisplayRole).toString(), initial.at(row)); + } + +} + +void tst_QSortFilterProxyModel::sortHierarchy_data() +{ + QTest::addColumn("sortOrder"); + QTest::addColumn("initial"); + QTest::addColumn("expected"); + + QTest::newRow("flat ascending") + << static_cast(Qt::AscendingOrder) + << (QStringList() + << "c" << "f" << "d" << "e" << "a" << "b") + << (QStringList() + << "a" << "b" << "c" << "d" << "e" << "f"); + + QTest::newRow("simple hierarchy") + << static_cast(Qt::AscendingOrder) + << (QStringList() << "a" << "<" << "b" << "<" << "c" << ">" << ">") + << (QStringList() << "a" << "<" << "b" << "<" << "c" << ">" << ">"); + + QTest::newRow("hierarchical ascending") + << static_cast(Qt::AscendingOrder) + << (QStringList() + << "c" + << "<" + << "h" + << "<" + << "2" + << "0" + << "1" + << ">" + << "g" + << "i" + << ">" + << "b" + << "<" + << "l" + << "k" + << "<" + << "8" + << "7" + << "9" + << ">" + << "m" + << ">" + << "a" + << "<" + << "z" + << "y" + << "x" + << ">") + << (QStringList() + << "a" + << "<" + << "x" + << "y" + << "z" + << ">" + << "b" + << "<" + << "k" + << "<" + << "7" + << "8" + << "9" + << ">" + << "l" + << "m" + << ">" + << "c" + << "<" + << "g" + << "h" + << "<" + << "0" + << "1" + << "2" + << ">" + << "i" + << ">"); +} + +void tst_QSortFilterProxyModel::sortHierarchy() +{ + QFETCH(int, sortOrder); + QFETCH(QStringList, initial); + QFETCH(QStringList, expected); + + buildHierarchy(initial, m_model); + checkHierarchy(initial, m_model); + checkHierarchy(initial, m_proxy); + m_proxy->sort(0, static_cast(sortOrder)); + checkHierarchy(initial, m_model); + checkHierarchy(expected, m_proxy); +} + +void tst_QSortFilterProxyModel::insertRows_data() +{ + QTest::addColumn("initial"); + QTest::addColumn("expected"); + QTest::addColumn("insert"); + QTest::addColumn("position"); + + QTest::newRow("insert one row in the middle") + << (QStringList() + << "One" + << "Two" + << "Four" + << "Five") + << (QStringList() + << "One" + << "Two" + << "Three" + << "Four" + << "Five") + << (QStringList() + << "Three") + << 2; + + QTest::newRow("insert one row in the beginning") + << (QStringList() + << "Two" + << "Three" + << "Four" + << "Five") + << (QStringList() + << "One" + << "Two" + << "Three" + << "Four" + << "Five") + << (QStringList() + << "One") + << 0; + + QTest::newRow("insert one row in the end") + << (QStringList() + << "One" + << "Two" + << "Three" + << "Four") + << (QStringList() + << "One" + << "Two" + << "Three" + << "Four" + << "Five") + << (QStringList() + <<"Five") + << 4; +} + +void tst_QSortFilterProxyModel::insertRows() +{ + QFETCH(QStringList, initial); + QFETCH(QStringList, expected); + QFETCH(QStringList, insert); + QFETCH(int, position); + // prepare model + m_model->insertRows(0, initial.count(), QModelIndex()); + //m_model->insertColumns(0, 1, QModelIndex()); + QCOMPARE(m_model->columnCount(QModelIndex()), 1); + QCOMPARE(m_model->rowCount(QModelIndex()), initial.count()); + for (int row = 0; row < m_model->rowCount(QModelIndex()); ++row) { + QModelIndex index = m_model->index(row, 0, QModelIndex()); + m_model->setData(index, initial.at(row), Qt::DisplayRole); + } + // make sure the model correct before insert + for (int row = 0; row < m_model->rowCount(QModelIndex()); ++row) { + QModelIndex index = m_model->index(row, 0, QModelIndex()); + QCOMPARE(m_model->data(index, Qt::DisplayRole).toString(), initial.at(row)); + } + // make sure the proxy is correct before insert + for (int row = 0; row < m_proxy->rowCount(QModelIndex()); ++row) { + QModelIndex index = m_proxy->index(row, 0, QModelIndex()); + QCOMPARE(m_proxy->data(index, Qt::DisplayRole).toString(), initial.at(row)); + } + + // insert the row + m_proxy->insertRows(position, insert.count(), QModelIndex()); + QCOMPARE(m_model->rowCount(QModelIndex()), expected.count()); + QCOMPARE(m_proxy->rowCount(QModelIndex()), expected.count()); + + // set the data for the inserted row + for (int i = 0; i < insert.count(); ++i) { + QModelIndex index = m_proxy->index(position + i, 0, QModelIndex()); + m_proxy->setData(index, insert.at(i), Qt::DisplayRole); + } + + // make sure the model correct after insert + for (int row = 0; row < m_model->rowCount(QModelIndex()); ++row) { + QModelIndex index = m_model->index(row, 0, QModelIndex()); + QCOMPARE(m_model->data(index, Qt::DisplayRole).toString(), expected.at(row)); + } + + // make sure the proxy is correct after insert + for (int row = 0; row < m_proxy->rowCount(QModelIndex()); ++row) { + QModelIndex index = m_proxy->index(row, 0, QModelIndex()); + QCOMPARE(m_proxy->data(index, Qt::DisplayRole).toString(), expected.at(row)); + } +} + +void tst_QSortFilterProxyModel::prependRow() +{ + //this tests that data is correctly handled by the sort filter when prepending a row + QStandardItemModel model; + QSortFilterProxyModel proxy; + proxy.setSourceModel(&model); + + QStandardItem item("root"); + model.appendRow(&item); + + QStandardItem sub("sub"); + item.appendRow(&sub); + + sub.appendRow(new QStandardItem("test1")); + sub.appendRow(new QStandardItem("test2")); + + QStandardItem sub2("sub2"); + sub2.appendRow(new QStandardItem("sub3")); + item.insertRow(0, &sub2); + + QModelIndex index_sub2 = proxy.mapFromSource(model.indexFromItem(&sub2)); + + QCOMPARE(sub2.rowCount(), proxy.rowCount(index_sub2)); + QCOMPARE(proxy.rowCount(QModelIndex()), 1); //only the "root" item is there +} + + +/* +void tst_QSortFilterProxyModel::insertColumns_data() +{ + +} + +void tst_QSortFilterProxyModel::insertColumns() +{ + +} +*/ + +void tst_QSortFilterProxyModel::removeRows_data() +{ + QTest::addColumn("initial"); + QTest::addColumn("sortOrder"); + QTest::addColumn("filter"); + QTest::addColumn("position"); + QTest::addColumn("count"); + QTest::addColumn("success"); + QTest::addColumn("expectedProxy"); + QTest::addColumn("expectedSource"); + + QTest::newRow("remove one row in the middle [no sorting/filter]") + << (QStringList() + << "One" + << "Two" + << "Three" + << "Four" + << "Five") + << -1 // no sorting + << QString() // no filter + << 2 // position + << 1 // count + << true // success + << (QStringList() // expectedProxy + << "One" + << "Two" + << "Four" + << "Five") + << (QStringList() // expectedSource + << "One" + << "Two" + << "Four" + << "Five"); + + QTest::newRow("remove one row in the beginning [no sorting/filter]") + << (QStringList() + << "One" + << "Two" + << "Three" + << "Four" + << "Five") + << -1 // no sorting + << QString() // no filter + << 0 // position + << 1 // count + << true // success + << (QStringList() // expectedProxy + << "Two" + << "Three" + << "Four" + << "Five") + << (QStringList() // expectedSource + << "Two" + << "Three" + << "Four" + << "Five"); + + QTest::newRow("remove one row in the end [no sorting/filter]") + << (QStringList() + << "One" + << "Two" + << "Three" + << "Four" + << "Five") + << -1 // no sorting + << QString() // no filter + << 4 // position + << 1 // count + << true // success + << (QStringList() // expectedProxy + << "One" + << "Two" + << "Three" + << "Four") + << (QStringList() // expectedSource + << "One" + << "Two" + << "Three" + << "Four"); + + QTest::newRow("remove all [no sorting/filter]") + << (QStringList() + << "One" + << "Two" + << "Three" + << "Four" + << "Five") + << -1 // no sorting + << QString() // no filter + << 0 // position + << 5 // count + << true // success + << QStringList() // expectedProxy + << QStringList(); // expectedSource + + QTest::newRow("remove one row past the end [no sorting/filter]") + << (QStringList() + << "One" + << "Two" + << "Three" + << "Four" + << "Five") + << -1 // no sorting + << QString() // no filter + << 5 // position + << 1 // count + << false // success + << (QStringList() // expectedProxy + << "One" + << "Two" + << "Three" + << "Four" + << "Five") + << (QStringList() // expectedSource + << "One" + << "Two" + << "Three" + << "Four" + << "Five"); + + QTest::newRow("remove row -1 [no sorting/filter]") + << (QStringList() + << "One" + << "Two" + << "Three" + << "Four" + << "Five") + << -1 // no sorting + << QString() // no filter + << -1 // position + << 1 // count + << false // success + << (QStringList() // expectedProxy + << "One" + << "Two" + << "Three" + << "Four" + << "Five") + << (QStringList() // expectedSource + << "One" + << "Two" + << "Three" + << "Four" + << "Five"); + + QTest::newRow("remove three rows in the middle [no sorting/filter]") + << (QStringList() + << "One" + << "Two" + << "Three" + << "Four" + << "Five") + << -1 // no sorting + << QString() // no filter + << 1 // position + << 3 // count + << true // success + << (QStringList() // expectedProxy + << "One" + << "Five") + << (QStringList() // expectedSource + << "One" + << "Five"); + + QTest::newRow("remove one row in the middle [ascending sorting, no filter]") + << (QStringList() + << "1" + << "5" + << "2" + << "4" + << "3") + << static_cast(Qt::AscendingOrder) + << QString() // no filter + << 2 // position + << 1 // count + << true // success + << (QStringList() // expectedProxy + << "1" + << "2" + << "4" + << "5") + << (QStringList() // expectedSource + << "1" + << "5" + << "2" + << "4"); + + QTest::newRow("remove two rows in the middle [ascending sorting, no filter]") + << (QStringList() + << "1" + << "5" + << "2" + << "4" + << "3") + << static_cast(Qt::AscendingOrder) + << QString() // no filter + << 2 // position + << 2 // count + << true // success + << (QStringList() // expectedProxy + << "1" + << "2" + << "5") + << (QStringList() // expectedSource + << "1" + << "5" + << "2"); + + QTest::newRow("remove two rows in the middle [descending sorting, no filter]") + << (QStringList() + << "1" + << "5" + << "2" + << "4" + << "3") + << static_cast(Qt::DescendingOrder) + << QString() // no filter + << 2 // position + << 2 // count + << true // success + << (QStringList() // expectedProxy + << "5" + << "4" + << "1") + << (QStringList() // expectedSource + << "1" + << "5" + << "4"); + + QTest::newRow("remove one row in the middle [no sorting, filter=5|2|3]") + << (QStringList() + << "1" + << "5" + << "2" + << "4" + << "3") + << -1 // no sorting + << QString("5|2|3") + << 1 // position + << 1 // count + << true // success + << (QStringList() // expectedProxy + << "5" + << "3") + << (QStringList() // expectedSource + << "1" + << "5" + << "4" + << "3"); + + QTest::newRow("remove all [ascending sorting, no filter]") + << (QStringList() + << "1" + << "5" + << "2" + << "4" + << "3") + << static_cast(Qt::AscendingOrder) + << QString() // no filter + << 0 // position + << 5 // count + << true // success + << QStringList() // expectedProxy + << QStringList(); // expectedSource +} + +void tst_QSortFilterProxyModel::removeRows() +{ + QFETCH(QStringList, initial); + QFETCH(int, sortOrder); + QFETCH(QString, filter); + QFETCH(int, position); + QFETCH(int, count); + QFETCH(bool, success); + QFETCH(QStringList, expectedProxy); + QFETCH(QStringList, expectedSource); + + QStandardItemModel model; + QSortFilterProxyModel proxy; + proxy.setSourceModel(&model); + + // prepare model + foreach (QString s, initial) + model.appendRow(new QStandardItem(s)); + + if (sortOrder != -1) + proxy.sort(0, static_cast(sortOrder)); + if (!filter.isEmpty()) + proxy.setFilterRegExp(QRegExp(filter)); + + // remove the rows + QCOMPARE(proxy.removeRows(position, count, QModelIndex()), success); + QCOMPARE(model.rowCount(QModelIndex()), expectedSource.count()); + QCOMPARE(proxy.rowCount(QModelIndex()), expectedProxy.count()); + + // make sure the model is correct after remove + for (int row = 0; row < model.rowCount(QModelIndex()); ++row) + QCOMPARE(model.item(row)->text(), expectedSource.at(row)); + + // make sure the proxy is correct after remove + for (int row = 0; row < proxy.rowCount(QModelIndex()); ++row) { + QModelIndex index = proxy.index(row, 0, QModelIndex()); + QCOMPARE(proxy.data(index, Qt::DisplayRole).toString(), expectedProxy.at(row)); + } +} + +class MyFilteredColumnProxyModel : public QSortFilterProxyModel +{ + Q_OBJECT +public: + MyFilteredColumnProxyModel(QObject *parent = 0) + : QSortFilterProxyModel(parent) { } +protected: + bool filterAcceptsColumn(int sourceColumn, const QModelIndex &) const + { + QString key = sourceModel()->headerData(sourceColumn, Qt::Horizontal).toString(); + return key.contains(filterRegExp()); + } +}; + +void tst_QSortFilterProxyModel::removeColumns_data() +{ + QTest::addColumn("initial"); + QTest::addColumn("filter"); + QTest::addColumn("position"); + QTest::addColumn("count"); + QTest::addColumn("success"); + QTest::addColumn("expectedProxy"); + QTest::addColumn("expectedSource"); + + QTest::newRow("remove one column in the middle [no filter]") + << (QStringList() + << "1" + << "2" + << "3" + << "4" + << "5") + << QString() // no filter + << 2 // position + << 1 // count + << true // success + << (QStringList() // expectedProxy + << "1" + << "2" + << "4" + << "5") + << (QStringList() // expectedSource + << "1" + << "2" + << "4" + << "5"); + + QTest::newRow("remove one column in the end [no filter]") + << (QStringList() + << "1" + << "2" + << "3" + << "4" + << "5") + << QString() // no filter + << 4 // position + << 1 // count + << true // success + << (QStringList() // expectedProxy + << "1" + << "2" + << "3" + << "4") + << (QStringList() // expectedSource + << "1" + << "2" + << "3" + << "4"); + + QTest::newRow("remove one column past the end [no filter]") + << (QStringList() + << "1" + << "2" + << "3" + << "4" + << "5") + << QString() // no filter + << 5 // position + << 1 // count + << false // success + << (QStringList() // expectedProxy + << "1" + << "2" + << "3" + << "4" + << "5") + << (QStringList() // expectedSource + << "1" + << "2" + << "3" + << "4" + << "5"); + + QTest::newRow("remove column -1 [no filter]") + << (QStringList() + << "1" + << "2" + << "3" + << "4" + << "5") + << QString() // no filter + << -1 // position + << 1 // count + << false // success + << (QStringList() // expectedProxy + << "1" + << "2" + << "3" + << "4" + << "5") + << (QStringList() // expectedSource + << "1" + << "2" + << "3" + << "4" + << "5"); + + QTest::newRow("remove all columns [no filter]") + << (QStringList() + << "1" + << "2" + << "3" + << "4" + << "5") + << QString() // no filter + << 0 // position + << 5 // count + << true // success + << QStringList() // expectedProxy + << QStringList(); // expectedSource + + QTest::newRow("remove one column in the middle [filter=1|3|5]") + << (QStringList() + << "1" + << "2" + << "3" + << "4" + << "5") + << QString("1|3|5") + << 1 // position + << 1 // count + << true // success + << (QStringList() // expectedProxy + << "1" + << "5") + << (QStringList() // expectedSource + << "1" + << "2" + << "4" + << "5"); + + QTest::newRow("remove one column in the end [filter=1|3|5]") + << (QStringList() + << "1" + << "2" + << "3" + << "4" + << "5") + << QString("1|3|5") + << 2 // position + << 1 // count + << true // success + << (QStringList() // expectedProxy + << "1" + << "3") + << (QStringList() // expectedSource + << "1" + << "2" + << "3" + << "4"); + + QTest::newRow("remove one column past the end [filter=1|3|5]") + << (QStringList() + << "1" + << "2" + << "3" + << "4" + << "5") + << QString("1|3|5") + << 3 // position + << 1 // count + << false // success + << (QStringList() // expectedProxy + << "1" + << "3" + << "5") + << (QStringList() // expectedSource + << "1" + << "2" + << "3" + << "4" + << "5"); + + QTest::newRow("remove all columns [filter=1|3|5]") + << (QStringList() + << "1" + << "2" + << "3" + << "4" + << "5") + << QString("1|3|5") + << 0 // position + << 3 // count + << true // success + << QStringList() // expectedProxy + << (QStringList() // expectedSource + << "2" + << "4"); +} + +void tst_QSortFilterProxyModel::removeColumns() +{ + QFETCH(QStringList, initial); + QFETCH(QString, filter); + QFETCH(int, position); + QFETCH(int, count); + QFETCH(bool, success); + QFETCH(QStringList, expectedProxy); + QFETCH(QStringList, expectedSource); + + QStandardItemModel model; + MyFilteredColumnProxyModel proxy; + proxy.setSourceModel(&model); + if (!filter.isEmpty()) + proxy.setFilterRegExp(QRegExp(filter)); + + // prepare model + model.setHorizontalHeaderLabels(initial); + + // remove the columns + QCOMPARE(proxy.removeColumns(position, count, QModelIndex()), success); + QCOMPARE(model.columnCount(QModelIndex()), expectedSource.count()); + QCOMPARE(proxy.columnCount(QModelIndex()), expectedProxy.count()); + + // make sure the model is correct after remove + for (int col = 0; col < model.columnCount(QModelIndex()); ++col) + QCOMPARE(model.horizontalHeaderItem(col)->text(), expectedSource.at(col)); + + // make sure the proxy is correct after remove + for (int col = 0; col < proxy.columnCount(QModelIndex()); ++col) { + QCOMPARE(proxy.headerData(col, Qt::Horizontal, Qt::DisplayRole).toString(), + expectedProxy.at(col)); + } +} + + +void tst_QSortFilterProxyModel::filterColumns_data() +{ + QTest::addColumn("pattern"); + QTest::addColumn("initial"); + QTest::addColumn("data"); + + QTest::newRow("all") << "a" + << (QStringList() + << "delta" + << "yankee" + << "bravo" + << "lima") + << true; + + QTest::newRow("some") << "lie" + << (QStringList() + << "charlie" + << "juliet" + << "tango" + << "hotel") + << true; + + QTest::newRow("nothing") << "zoo" + << (QStringList() + << "foxtrot" + << "uniform" + << "alpha" + << "golf") + << false; +} + +void tst_QSortFilterProxyModel::filterColumns() +{ + QFETCH(QString, pattern); + QFETCH(QStringList, initial); + QFETCH(bool, data); + // prepare model + m_model->setColumnCount(initial.count()); + m_model->setRowCount(1); + QCOMPARE(m_model->columnCount(QModelIndex()), initial.count()); + QCOMPARE(m_model->rowCount(QModelIndex()), 1); + // set data + QCOMPARE(m_model->rowCount(QModelIndex()), 1); + for (int col = 0; col < m_model->columnCount(QModelIndex()); ++col) { + QModelIndex index = m_model->index(0, col, QModelIndex()); + m_model->setData(index, initial.at(col), Qt::DisplayRole); + } + m_proxy->setFilterRegExp(pattern); + m_proxy->setFilterKeyColumn(-1); + // make sure the model is unchanged + for (int col = 0; col < m_model->columnCount(QModelIndex()); ++col) { + QModelIndex index = m_model->index(0, col, QModelIndex()); + QCOMPARE(m_model->data(index, Qt::DisplayRole).toString(), initial.at(col)); + } + // make sure the proxy is filtered + QModelIndex index = m_proxy->index(0, 0, QModelIndex()); + QCOMPARE(index.isValid(), data); +} + +void tst_QSortFilterProxyModel::filter_data() +{ + QTest::addColumn("pattern"); + QTest::addColumn("initial"); + QTest::addColumn("expected"); + + QTest::newRow("flat") << "e" + << (QStringList() + << "delta" + << "yankee" + << "bravo" + << "lima" + << "charlie" + << "juliet" + << "tango" + << "hotel" + << "uniform" + << "alpha" + << "echo" + << "golf" + << "quebec" + << "foxtrot" + << "india" + << "romeo" + << "november" + << "oskar" + << "zulu" + << "kilo" + << "whiskey" + << "mike" + << "papa" + << "sierra" + << "xray" + << "viktor") + << (QStringList() + << "delta" + << "yankee" + << "charlie" + << "juliet" + << "hotel" + << "echo" + << "quebec" + << "romeo" + << "november" + << "whiskey" + << "mike" + << "sierra"); +} + +void tst_QSortFilterProxyModel::filter() +{ + QFETCH(QString, pattern); + QFETCH(QStringList, initial); + QFETCH(QStringList, expected); + // prepare model + QVERIFY(m_model->insertRows(0, initial.count(), QModelIndex())); + QCOMPARE(m_model->rowCount(QModelIndex()), initial.count()); + // set data + QCOMPARE(m_model->columnCount(QModelIndex()), 1); + for (int row = 0; row < m_model->rowCount(QModelIndex()); ++row) { + QModelIndex index = m_model->index(row, 0, QModelIndex()); + m_model->setData(index, initial.at(row), Qt::DisplayRole); + } + m_proxy->setFilterRegExp(pattern); + // make sure the proxy is unfiltered + QCOMPARE(m_proxy->columnCount(QModelIndex()), 1); + QCOMPARE(m_proxy->rowCount(QModelIndex()), expected.count()); + // make sure the model is unchanged + for (int row = 0; row < m_model->rowCount(QModelIndex()); ++row) { + QModelIndex index = m_model->index(row, 0, QModelIndex()); + QCOMPARE(m_model->data(index, Qt::DisplayRole).toString(), initial.at(row)); + } + // make sure the proxy is filtered + for (int row = 0; row < m_proxy->rowCount(QModelIndex()); ++row) { + QModelIndex index = m_proxy->index(row, 0, QModelIndex()); + QCOMPARE(m_proxy->data(index, Qt::DisplayRole).toString(), expected.at(row)); + } +} + +void tst_QSortFilterProxyModel::filterHierarchy_data() +{ + QTest::addColumn("pattern"); + QTest::addColumn("initial"); + QTest::addColumn("expected"); + + QTest::newRow("flat") << ".*oo" + << (QStringList() + << "foo" << "boo" << "baz" << "moo" << "laa" << "haa") + << (QStringList() + << "foo" << "boo" << "moo"); + + QTest::newRow("simple hierarchy") << "b.*z" + << (QStringList() << "baz" << "<" << "boz" << "<" << "moo" << ">" << ">") + << (QStringList() << "baz" << "<" << "boz" << ">"); +} + +void tst_QSortFilterProxyModel::filterHierarchy() +{ + QFETCH(QString, pattern); + QFETCH(QStringList, initial); + QFETCH(QStringList, expected); + buildHierarchy(initial, m_model); + m_proxy->setFilterRegExp(pattern); + checkHierarchy(initial, m_model); + checkHierarchy(expected, m_proxy); +} + +void tst_QSortFilterProxyModel::buildHierarchy(const QStringList &l, QAbstractItemModel *m) +{ + int ind = 0; + int row = 0; + QStack row_stack; + QModelIndex parent; + QStack parent_stack; + for (int i = 0; i < l.count(); ++i) { + QString token = l.at(i); + if (token == "<") { // start table + ++ind; + parent_stack.push(parent); + row_stack.push(row); + parent = m->index(row - 1, 0, parent); + row = 0; + QVERIFY(m->insertColumns(0, 1, parent)); // add column + } else if (token == ">") { // end table + --ind; + parent = parent_stack.pop(); + row = row_stack.pop(); + } else { // append row + QVERIFY(m->insertRows(row, 1, parent)); + QModelIndex index = m->index(row, 0, parent); + QVERIFY(index.isValid()); + m->setData(index, token, Qt::DisplayRole); + ++row; + } + } +} + +void tst_QSortFilterProxyModel::checkHierarchy(const QStringList &l, const QAbstractItemModel *m) +{ + int row = 0; + int indent = 0; + QStack row_stack; + QModelIndex parent; + QStack parent_stack; + for (int i = 0; i < l.count(); ++i) { + QString token = l.at(i); + if (token == "<") { // start table + ++indent; + parent_stack.push(parent); + row_stack.push(row); + parent = m->index(row - 1, 0, parent); + QVERIFY(parent.isValid()); + row = 0; + } else if (token == ">") { // end table + --indent; + parent = parent_stack.pop(); + row = row_stack.pop(); + } else { // compare row + QModelIndex index = m->index(row, 0, parent); + QVERIFY(index.isValid()); + QString str = m->data(index, Qt::DisplayRole).toString(); + QCOMPARE(str, token); + ++row; + } + } +} + + + +class TestModel: public QAbstractTableModel +{ +public: + int rowCount(const QModelIndex &) const { return 10000; } + int columnCount(const QModelIndex &) const { return 1; } + QVariant data(const QModelIndex &index, int role) const + { + if (role != Qt::DisplayRole) + return QVariant(); + return QString::number(index.row()); + } +}; + +void tst_QSortFilterProxyModel::filterTable() +{ + TestModel model; + QSortFilterProxyModel filter; + filter.setSourceModel(&model); + filter.setFilterRegExp("9"); + + for (int i = 0; i < filter.rowCount(); ++i) + QVERIFY(filter.data(filter.index(i, 0)).toString().contains("9")); +} + +void tst_QSortFilterProxyModel::insertAfterSelect() +{ + QStandardItemModel model(10, 2); + for (int i = 0; i<10;i++) + model.setData(model.index(i, 0), QVariant(i)); + QSortFilterProxyModel filter; + filter.setSourceModel(&model); + QTreeView view; + view.setModel(&filter); + view.show(); + QModelIndex firstIndex = filter.mapFromSource(model.index(0, 0, QModelIndex())); + QCOMPARE(firstIndex.model(), (const QAbstractItemModel *)view.model()); + QVERIFY(firstIndex.isValid()); + int itemOffset = view.visualRect(firstIndex).width() / 2; + QPoint p(itemOffset, 1); + QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, p); + QVERIFY(view.selectionModel()->selectedIndexes().size() > 0); + model.insertRows(5, 1, QModelIndex()); + QVERIFY(view.selectionModel()->selectedIndexes().size() > 0); // Should still have a selection +} + +void tst_QSortFilterProxyModel::removeAfterSelect() +{ + QStandardItemModel model(10, 2); + for (int i = 0; i<10;i++) + model.setData(model.index(i, 0), QVariant(i)); + QSortFilterProxyModel filter; + filter.setSourceModel(&model); + QTreeView view; + view.setModel(&filter); + view.show(); + QModelIndex firstIndex = filter.mapFromSource(model.index(0, 0, QModelIndex())); + QCOMPARE(firstIndex.model(), (const QAbstractItemModel *)view.model()); + QVERIFY(firstIndex.isValid()); + int itemOffset = view.visualRect(firstIndex).width() / 2; + QPoint p(itemOffset, 1); + QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, p); + QVERIFY(view.selectionModel()->selectedIndexes().size() > 0); + model.removeRows(5, 1, QModelIndex()); + QVERIFY(view.selectionModel()->selectedIndexes().size() > 0); // Should still have a selection +} + +void tst_QSortFilterProxyModel::filterCurrent() +{ + QStandardItemModel model(2, 1); + model.setData(model.index(0, 0), QString("AAA")); + model.setData(model.index(1, 0), QString("BBB")); + QSortFilterProxyModel proxy; + proxy.setSourceModel(&model); + QTreeView view; + + view.show(); + view.setModel(&proxy); + QSignalSpy spy(view.selectionModel(), SIGNAL(currentChanged(QModelIndex, QModelIndex))); + + view.setCurrentIndex(proxy.index(0, 0)); + QCOMPARE(spy.count(), 1); + proxy.setFilterRegExp(QRegExp("^B")); + QCOMPARE(spy.count(), 2); +} + +void tst_QSortFilterProxyModel::changeSourceLayout() +{ + QStandardItemModel model(2, 1); + model.setData(model.index(0, 0), QString("b")); + model.setData(model.index(1, 0), QString("a")); + QSortFilterProxyModel proxy; + proxy.setSourceModel(&model); + + QList persistentSourceIndexes; + QList persistentProxyIndexes; + for (int row = 0; row < model.rowCount(); ++row) { + persistentSourceIndexes.append(model.index(row, 0)); + persistentProxyIndexes.append(proxy.index(row, 0)); + } + + // change layout of source model + model.sort(0, Qt::AscendingOrder); + + for (int row = 0; row < model.rowCount(); ++row) { + QCOMPARE(persistentProxyIndexes.at(row).row(), + persistentSourceIndexes.at(row).row()); + } +} + +void tst_QSortFilterProxyModel::removeSourceRows_data() +{ + QTest::addColumn("sourceItems"); + QTest::addColumn("start"); + QTest::addColumn("count"); + QTest::addColumn("sortOrder"); + QTest::addColumn("expectedRemovedProxyIntervals"); + QTest::addColumn("expectedProxyItems"); + + QTest::newRow("remove one, no sorting") + << (QStringList() << "a" << "b") // sourceItems + << 0 // start + << 1 // count + << -1 // sortOrder (no sorting) + << (IntPairList() << IntPair(0, 0)) // expectedRemovedIntervals + << (QStringList() << "b") // expectedProxyItems + ; + QTest::newRow("remove one, ascending sort (same order)") + << (QStringList() << "a" << "b") // sourceItems + << 0 // start + << 1 // count + << static_cast(Qt::AscendingOrder) // sortOrder + << (IntPairList() << IntPair(0, 0)) // expectedRemovedIntervals + << (QStringList() << "b") // expectedProxyItems + ; + QTest::newRow("remove one, ascending sort (reverse order)") + << (QStringList() << "b" << "a") // sourceItems + << 0 // start + << 1 // count + << static_cast(Qt::AscendingOrder) // sortOrder + << (IntPairList() << IntPair(1, 1)) // expectedRemovedIntervals + << (QStringList() << "a") // expectedProxyItems + ; + QTest::newRow("remove two, multiple proxy intervals") + << (QStringList() << "c" << "d" << "a" << "b") // sourceItems + << 1 // start + << 2 // count + << static_cast(Qt::AscendingOrder) // sortOrder + << (IntPairList() << IntPair(3, 3) << IntPair(0, 0)) // expectedRemovedIntervals + << (QStringList() << "b" << "c") // expectedProxyItems + ; + QTest::newRow("remove three, multiple proxy intervals") + << (QStringList() << "b" << "d" << "f" << "a" << "c" << "e") // sourceItems + << 3 // start + << 3 // count + << static_cast(Qt::AscendingOrder) // sortOrder + << (IntPairList() << IntPair(4, 4) << IntPair(2, 2) << IntPair(0, 0)) // expectedRemovedIntervals + << (QStringList() << "b" << "d" << "f") // expectedProxyItems + ; + QTest::newRow("remove all, single proxy intervals") + << (QStringList() << "a" << "b" << "c" << "d" << "e" << "f") // sourceItems + << 0 // start + << 6 // count + << static_cast(Qt::DescendingOrder) // sortOrder + << (IntPairList() << IntPair(0, 5)) // expectedRemovedIntervals + << QStringList() // expectedProxyItems + ; +} + +// Check that correct proxy model rows are removed when rows are removed +// from the source model +void tst_QSortFilterProxyModel::removeSourceRows() +{ + QFETCH(QStringList, sourceItems); + QFETCH(int, start); + QFETCH(int, count); + QFETCH(int, sortOrder); + QFETCH(IntPairList, expectedRemovedProxyIntervals); + QFETCH(QStringList, expectedProxyItems); + + QStandardItemModel model; + QSortFilterProxyModel proxy; + + proxy.setSourceModel(&model); + model.insertColumns(0, 1); + model.insertRows(0, sourceItems.count()); + + for (int i = 0; i < sourceItems.count(); ++i) { + QModelIndex sindex = model.index(i, 0, QModelIndex()); + model.setData(sindex, sourceItems.at(i), Qt::DisplayRole); + QModelIndex pindex = proxy.index(i, 0, QModelIndex()); + QCOMPARE(proxy.data(pindex, Qt::DisplayRole), model.data(sindex, Qt::DisplayRole)); + } + + if (sortOrder != -1) + proxy.sort(0, static_cast(sortOrder)); + (void)proxy.rowCount(QModelIndex()); // force mapping + + QSignalSpy removeSpy(&proxy, SIGNAL(rowsRemoved(QModelIndex, int, int))); + QSignalSpy insertSpy(&proxy, SIGNAL(rowsInserted(QModelIndex, int, int))); + QSignalSpy aboutToRemoveSpy(&proxy, SIGNAL(rowsAboutToBeRemoved(QModelIndex, int, int))); + QSignalSpy aboutToInsertSpy(&proxy, SIGNAL(rowsAboutToBeInserted(QModelIndex, int, int))); + + model.removeRows(start, count, QModelIndex()); + + QCOMPARE(aboutToRemoveSpy.count(), expectedRemovedProxyIntervals.count()); + for (int i = 0; i < aboutToRemoveSpy.count(); ++i) { + QList args = aboutToRemoveSpy.at(i); + QVERIFY(args.at(1).type() == QVariant::Int); + QVERIFY(args.at(2).type() == QVariant::Int); + QCOMPARE(args.at(1).toInt(), expectedRemovedProxyIntervals.at(i).first); + QCOMPARE(args.at(2).toInt(), expectedRemovedProxyIntervals.at(i).second); + } + QCOMPARE(removeSpy.count(), expectedRemovedProxyIntervals.count()); + for (int i = 0; i < removeSpy.count(); ++i) { + QList args = removeSpy.at(i); + QVERIFY(args.at(1).type() == QVariant::Int); + QVERIFY(args.at(2).type() == QVariant::Int); + QCOMPARE(args.at(1).toInt(), expectedRemovedProxyIntervals.at(i).first); + QCOMPARE(args.at(2).toInt(), expectedRemovedProxyIntervals.at(i).second); + } + + QCOMPARE(insertSpy.count(), 0); + QCOMPARE(aboutToInsertSpy.count(), 0); + + QCOMPARE(proxy.rowCount(QModelIndex()), expectedProxyItems.count()); + for (int i = 0; i < expectedProxyItems.count(); ++i) { + QModelIndex pindex = proxy.index(i, 0, QModelIndex()); + QCOMPARE(proxy.data(pindex, Qt::DisplayRole).toString(), expectedProxyItems.at(i)); + } +} + +void tst_QSortFilterProxyModel::insertSourceRows_data() +{ + QTest::addColumn("sourceItems"); + QTest::addColumn("start"); + QTest::addColumn("newItems"); + QTest::addColumn("sortOrder"); + QTest::addColumn("proxyItems"); + + QTest::newRow("insert (1)") + << (QStringList() << "c" << "b") // sourceItems + << 1 // start + << (QStringList() << "a") // newItems + << static_cast(Qt::AscendingOrder) // sortOrder + << (QStringList() << "a" << "b" << "c") // proxyItems + ; + + QTest::newRow("insert (2)") + << (QStringList() << "d" << "b" << "c") // sourceItems + << 3 // start + << (QStringList() << "a") // newItems + << static_cast(Qt::DescendingOrder) // sortOrder + << (QStringList() << "d" << "c" << "b" << "a") // proxyItems + ; +} + +// Check that rows are inserted at correct position in proxy model when +// rows are inserted into the source model +void tst_QSortFilterProxyModel::insertSourceRows() +{ + QFETCH(QStringList, sourceItems); + QFETCH(int, start); + QFETCH(QStringList, newItems); + QFETCH(int, sortOrder); + QFETCH(QStringList, proxyItems); + + QStandardItemModel model; + QSortFilterProxyModel proxy; + proxy.setDynamicSortFilter(true); + + proxy.setSourceModel(&model); + model.insertColumns(0, 1); + model.insertRows(0, sourceItems.count()); + + for (int i = 0; i < sourceItems.count(); ++i) { + QModelIndex index = model.index(i, 0, QModelIndex()); + model.setData(index, sourceItems.at(i), Qt::DisplayRole); + } + + proxy.sort(0, static_cast(sortOrder)); + (void)proxy.rowCount(QModelIndex()); // force mapping + + model.insertRows(start, newItems.size(), QModelIndex()); + + QCOMPARE(proxy.rowCount(QModelIndex()), proxyItems.count()); + for (int i = 0; i < newItems.count(); ++i) { + QModelIndex index = model.index(start + i, 0, QModelIndex()); + model.setData(index, newItems.at(i), Qt::DisplayRole); + } + + for (int i = 0; i < proxyItems.count(); ++i) { + QModelIndex index = proxy.index(i, 0, QModelIndex()); + QCOMPARE(proxy.data(index, Qt::DisplayRole).toString(), proxyItems.at(i)); + } +} + +void tst_QSortFilterProxyModel::changeFilter_data() +{ + QTest::addColumn("sourceItems"); + QTest::addColumn("sortOrder"); + QTest::addColumn("initialFilter"); + QTest::addColumn("initialRemoveIntervals"); + QTest::addColumn("initialProxyItems"); + QTest::addColumn("finalFilter"); + QTest::addColumn("finalRemoveIntervals"); + QTest::addColumn("insertIntervals"); + QTest::addColumn("finalProxyItems"); + + QTest::newRow("filter (1)") + << (QStringList() << "a" << "b" << "c" << "d" << "e" << "f") // sourceItems + << static_cast(Qt::AscendingOrder) // sortOrder + << "a|b|c" // initialFilter + << (IntPairList() << IntPair(3, 5)) // initialRemoveIntervals + << (QStringList() << "a" << "b" << "c") // initialProxyItems + << "b|d|f" // finalFilter + << (IntPairList() << IntPair(2, 2) << IntPair(0, 0)) // finalRemoveIntervals + << (IntPairList() << IntPair(1, 2)) // insertIntervals + << (QStringList() << "b" << "d" << "f") // finalProxyItems + ; + + QTest::newRow("filter (2)") + << (QStringList() << "a" << "b" << "c" << "d" << "e" << "f") // sourceItems + << static_cast(Qt::AscendingOrder) // sortOrder + << "a|c|e" // initialFilter + << (IntPairList() << IntPair(5, 5) << IntPair(3, 3) << IntPair(1, 1)) // initialRemoveIntervals + << (QStringList() << "a" << "c" << "e") // initialProxyItems + << "" // finalFilter + << IntPairList() // finalRemoveIntervals + << (IntPairList() << IntPair(3, 3) << IntPair(2, 2) << IntPair(1, 1)) // insertIntervals + << (QStringList() << "a" << "b" << "c" << "d" << "e" << "f") // finalProxyItems + ; + + QTest::newRow("filter (3)") + << (QStringList() << "a" << "b" << "c") // sourceItems + << static_cast(Qt::AscendingOrder) // sortOrder + << "a" // initialFilter + << (IntPairList() << IntPair(1, 2)) // initialRemoveIntervals + << (QStringList() << "a") // initialProxyItems + << "a" // finalFilter + << IntPairList() // finalRemoveIntervals + << IntPairList() // insertIntervals + << (QStringList() << "a") // finalProxyItems + ; +} + +// Check that rows are added/removed when filter changes +void tst_QSortFilterProxyModel::changeFilter() +{ + QFETCH(QStringList, sourceItems); + QFETCH(int, sortOrder); + QFETCH(QString, initialFilter); + QFETCH(IntPairList, initialRemoveIntervals); + QFETCH(QStringList, initialProxyItems); + QFETCH(QString, finalFilter); + QFETCH(IntPairList, finalRemoveIntervals); + QFETCH(IntPairList, insertIntervals); + QFETCH(QStringList, finalProxyItems); + + QStandardItemModel model; + QSortFilterProxyModel proxy; + + proxy.setSourceModel(&model); + model.insertColumns(0, 1); + model.insertRows(0, sourceItems.count()); + + for (int i = 0; i < sourceItems.count(); ++i) { + QModelIndex index = model.index(i, 0, QModelIndex()); + model.setData(index, sourceItems.at(i), Qt::DisplayRole); + } + + proxy.sort(0, static_cast(sortOrder)); + (void)proxy.rowCount(QModelIndex()); // force mapping + + QSignalSpy initialRemoveSpy(&proxy, SIGNAL(rowsRemoved(QModelIndex, int, int))); + QSignalSpy initialInsertSpy(&proxy, SIGNAL(rowsInserted(QModelIndex, int, int))); + + proxy.setFilterRegExp(initialFilter); + + QCOMPARE(initialRemoveSpy.count(), initialRemoveIntervals.count()); + QCOMPARE(initialInsertSpy.count(), 0); + for (int i = 0; i < initialRemoveSpy.count(); ++i) { + QList args = initialRemoveSpy.at(i); + QVERIFY(args.at(1).type() == QVariant::Int); + QVERIFY(args.at(2).type() == QVariant::Int); + QCOMPARE(args.at(1).toInt(), initialRemoveIntervals.at(i).first); + QCOMPARE(args.at(2).toInt(), initialRemoveIntervals.at(i).second); + } + + QCOMPARE(proxy.rowCount(QModelIndex()), initialProxyItems.count()); + for (int i = 0; i < initialProxyItems.count(); ++i) { + QModelIndex index = proxy.index(i, 0, QModelIndex()); + QCOMPARE(proxy.data(index, Qt::DisplayRole).toString(), initialProxyItems.at(i)); + } + + QSignalSpy finalRemoveSpy(&proxy, SIGNAL(rowsRemoved(QModelIndex, int, int))); + QSignalSpy finalInsertSpy(&proxy, SIGNAL(rowsInserted(QModelIndex, int, int))); + + proxy.setFilterRegExp(finalFilter); + + QCOMPARE(finalRemoveSpy.count(), finalRemoveIntervals.count()); + for (int i = 0; i < finalRemoveSpy.count(); ++i) { + QList args = finalRemoveSpy.at(i); + QVERIFY(args.at(1).type() == QVariant::Int); + QVERIFY(args.at(2).type() == QVariant::Int); + QCOMPARE(args.at(1).toInt(), finalRemoveIntervals.at(i).first); + QCOMPARE(args.at(2).toInt(), finalRemoveIntervals.at(i).second); + } + +#ifdef Q_OS_IRIX + QEXPECT_FAIL("filter (2)", "Not reliable on IRIX", Abort); +#endif + QCOMPARE(finalInsertSpy.count(), insertIntervals.count()); + for (int i = 0; i < finalInsertSpy.count(); ++i) { + QList args = finalInsertSpy.at(i); + QVERIFY(args.at(1).type() == QVariant::Int); + QVERIFY(args.at(2).type() == QVariant::Int); + QCOMPARE(args.at(1).toInt(), insertIntervals.at(i).first); + QCOMPARE(args.at(2).toInt(), insertIntervals.at(i).second); + } + + QCOMPARE(proxy.rowCount(QModelIndex()), finalProxyItems.count()); + for (int i = 0; i < finalProxyItems.count(); ++i) { + QModelIndex index = proxy.index(i, 0, QModelIndex()); + QCOMPARE(proxy.data(index, Qt::DisplayRole).toString(), finalProxyItems.at(i)); + } +} + +void tst_QSortFilterProxyModel::changeSourceData_data() +{ + QTest::addColumn("sourceItems"); + QTest::addColumn("sortOrder"); + QTest::addColumn("filter"); + QTest::addColumn("dynamic"); + QTest::addColumn("row"); + QTest::addColumn("newValue"); + QTest::addColumn("removeIntervals"); + QTest::addColumn("insertIntervals"); + QTest::addColumn("proxyItems"); + + QTest::newRow("changeSourceData (1)") + << (QStringList() << "c" << "b" << "a") // sourceItems + << static_cast(Qt::AscendingOrder) // sortOrder + << "" // filter + << true // dynamic + << 2 // row + << "z" // newValue + << IntPairList() // removeIntervals + << IntPairList() // insertIntervals + << (QStringList() << "b" << "c" << "z") // proxyItems + ; + + QTest::newRow("changeSourceData (2)") + << (QStringList() << "b" << "c" << "z") // sourceItems + << static_cast(Qt::DescendingOrder) // sortOrder + << "" // filter + << true // dynamic + << 1 // row + << "a" // newValue + << IntPairList() // removeIntervals + << IntPairList() // insertIntervals + << (QStringList() << "z" << "b" << "a") // proxyItems + ; + + QTest::newRow("changeSourceData (3)") + << (QStringList() << "a" << "b") // sourceItems + << static_cast(Qt::DescendingOrder) // sortOrder + << "" // filter + << true // dynamic + << 0 // row + << "a" // newValue + << IntPairList() // removeIntervals + << IntPairList() // insertIntervals + << (QStringList() << "b" << "a") // proxyItems + ; + + QTest::newRow("changeSourceData (4)") + << (QStringList() << "a" << "b" << "c" << "d") // sourceItems + << static_cast(Qt::AscendingOrder) // sortOrder + << "a|c" // filter + << true // dynamic + << 1 // row + << "x" // newValue + << IntPairList() // removeIntervals + << IntPairList() // insertIntervals + << (QStringList() << "a" << "c") // proxyItems + ; + + QTest::newRow("changeSourceData (5)") + << (QStringList() << "a" << "b" << "c" << "d") // sourceItems + << static_cast(Qt::AscendingOrder) // sortOrder + << "a|c|x" // filter + << true // dynamic + << 1 // row + << "x" // newValue + << IntPairList() // removeIntervals + << (IntPairList() << IntPair(2, 2)) // insertIntervals + << (QStringList() << "a" << "c" << "x") // proxyItems + ; + + QTest::newRow("changeSourceData (6)") + << (QStringList() << "c" << "b" << "a") // sourceItems + << static_cast(Qt::AscendingOrder) // sortOrder + << "" // filter + << false // dynamic + << 2 // row + << "x" // newValue + << IntPairList() // removeIntervals + << IntPairList() // insertIntervals + << (QStringList() << "x" << "b" << "c") // proxyItems + ; +} + +void tst_QSortFilterProxyModel::changeSourceData() +{ + QFETCH(QStringList, sourceItems); + QFETCH(int, sortOrder); + QFETCH(QString, filter); + QFETCH(bool, dynamic); + QFETCH(int, row); + QFETCH(QString, newValue); + QFETCH(IntPairList, removeIntervals); + QFETCH(IntPairList, insertIntervals); + QFETCH(QStringList, proxyItems); + + QStandardItemModel model; + QSortFilterProxyModel proxy; + + proxy.setDynamicSortFilter(dynamic); + proxy.setSourceModel(&model); + model.insertColumns(0, 1); + model.insertRows(0, sourceItems.count()); + + for (int i = 0; i < sourceItems.count(); ++i) { + QModelIndex index = model.index(i, 0, QModelIndex()); + model.setData(index, sourceItems.at(i), Qt::DisplayRole); + } + + proxy.sort(0, static_cast(sortOrder)); + (void)proxy.rowCount(QModelIndex()); // force mapping + + proxy.setFilterRegExp(filter); + + QSignalSpy removeSpy(&proxy, SIGNAL(rowsRemoved(QModelIndex, int, int))); + QSignalSpy insertSpy(&proxy, SIGNAL(rowsInserted(QModelIndex, int, int))); + + { + QModelIndex index = model.index(row, 0, QModelIndex()); + model.setData(index, newValue, Qt::DisplayRole); + } + + QCOMPARE(removeSpy.count(), removeIntervals.count()); + for (int i = 0; i < removeSpy.count(); ++i) { + QList args = removeSpy.at(i); + QVERIFY(args.at(1).type() == QVariant::Int); + QVERIFY(args.at(2).type() == QVariant::Int); + QCOMPARE(args.at(1).toInt(), removeIntervals.at(i).first); + QCOMPARE(args.at(2).toInt(), removeIntervals.at(i).second); + } + + QCOMPARE(insertSpy.count(), insertIntervals.count()); + for (int i = 0; i < insertSpy.count(); ++i) { + QList args = insertSpy.at(i); + QVERIFY(args.at(1).type() == QVariant::Int); + QVERIFY(args.at(2).type() == QVariant::Int); + QCOMPARE(args.at(1).toInt(), insertIntervals.at(i).first); + QCOMPARE(args.at(2).toInt(), insertIntervals.at(i).second); + } + + QCOMPARE(proxy.rowCount(QModelIndex()), proxyItems.count()); + for (int i = 0; i < proxyItems.count(); ++i) { + QModelIndex index = proxy.index(i, 0, QModelIndex()); + QCOMPARE(proxy.data(index, Qt::DisplayRole).toString(), proxyItems.at(i)); + } +} + +void tst_QSortFilterProxyModel::sortFilterRole() +{ + QStandardItemModel model; + QSortFilterProxyModel proxy; + proxy.setSourceModel(&model); + model.insertColumns(0, 1); + + QList > sourceItems; + sourceItems = QList >() + << QPair("b", 3) + << QPair("c", 2) + << QPair("a", 1); + + QList orderedItems; + orderedItems = QList() + << 2 << 1; + + model.insertRows(0, sourceItems.count()); + for (int i = 0; i < sourceItems.count(); ++i) { + QModelIndex index = model.index(i, 0, QModelIndex()); + model.setData(index, sourceItems.at(i).first, Qt::DisplayRole); + model.setData(index, sourceItems.at(i).second, Qt::UserRole); + } + + proxy.setFilterRegExp("2"); + QCOMPARE(proxy.rowCount(), 0); // Qt::DisplayRole is default role + + proxy.setFilterRole(Qt::UserRole); + QCOMPARE(proxy.rowCount(), 1); + + proxy.setFilterRole(Qt::DisplayRole); + QCOMPARE(proxy.rowCount(), 0); + + proxy.setFilterRegExp("1|2|3"); + QCOMPARE(proxy.rowCount(), 0); + + proxy.setFilterRole(Qt::UserRole); + QCOMPARE(proxy.rowCount(), 3); + + proxy.sort(0, Qt::AscendingOrder); + QCOMPARE(proxy.rowCount(), 3); + + proxy.setSortRole(Qt::UserRole); + proxy.setFilterRole(Qt::DisplayRole); + proxy.setFilterRegExp("a|c"); + QCOMPARE(proxy.rowCount(), orderedItems.count()); + for (int i = 0; i < proxy.rowCount(); ++i) { + QModelIndex index = proxy.index(i, 0, QModelIndex()); + QCOMPARE(proxy.data(index, Qt::DisplayRole), sourceItems.at(orderedItems.at(i)).first); + } +} + +void tst_QSortFilterProxyModel::selectionFilteredOut() +{ + QStandardItemModel model(2, 1); + model.setData(model.index(0, 0), QString("AAA")); + model.setData(model.index(1, 0), QString("BBB")); + QSortFilterProxyModel proxy; + proxy.setSourceModel(&model); + QTreeView view; + + view.show(); + view.setModel(&proxy); + QSignalSpy spy(view.selectionModel(), SIGNAL(currentChanged(QModelIndex, QModelIndex))); + + view.setCurrentIndex(proxy.index(0, 0)); + QCOMPARE(spy.count(), 1); + proxy.setFilterRegExp(QRegExp("^B")); + QCOMPARE(spy.count(), 2); +} + +void tst_QSortFilterProxyModel::match_data() +{ + QTest::addColumn("sourceItems"); + QTest::addColumn("sortOrder"); + QTest::addColumn("filter"); + QTest::addColumn("proxyStartRow"); + QTest::addColumn("what"); + QTest::addColumn("matchFlags"); + QTest::addColumn("expectedProxyItems"); + QTest::newRow("1") + << (QStringList() << "a") // sourceItems + << static_cast(Qt::AscendingOrder) // sortOrder + << "" // filter + << 0 // proxyStartRow + << "a" // what + << static_cast(Qt::MatchExactly) // matchFlags + << (IntList() << 0); // expectedProxyItems + QTest::newRow("2") + << (QStringList() << "a" << "b") // sourceItems + << static_cast(Qt::AscendingOrder) // sortOrder + << "" // filter + << 0 // proxyStartRow + << "b" // what + << static_cast(Qt::MatchExactly) // matchFlags + << (IntList() << 1); // expectedProxyItems + QTest::newRow("3") + << (QStringList() << "a" << "b") // sourceItems + << static_cast(Qt::DescendingOrder) // sortOrder + << "" // filter + << 0 // proxyStartRow + << "a" // what + << static_cast(Qt::MatchExactly) // matchFlags + << (IntList() << 1); // expectedProxyItems + QTest::newRow("4") + << (QStringList() << "b" << "d" << "a" << "c") // sourceItems + << static_cast(Qt::AscendingOrder) // sortOrder + << "" // filter + << 1 // proxyStartRow + << "a" // what + << static_cast(Qt::MatchExactly) // matchFlags + << IntList(); // expectedProxyItems + QTest::newRow("5") + << (QStringList() << "b" << "d" << "a" << "c") // sourceItems + << static_cast(Qt::AscendingOrder) // sortOrder + << "a|b" // filter + << 0 // proxyStartRow + << "c" // what + << static_cast(Qt::MatchExactly) // matchFlags + << IntList(); // expectedProxyItems + QTest::newRow("6") + << (QStringList() << "b" << "d" << "a" << "c") // sourceItems + << static_cast(Qt::DescendingOrder) // sortOrder + << "a|b" // filter + << 0 // proxyStartRow + << "b" // what + << static_cast(Qt::MatchExactly) // matchFlags + << (IntList() << 0); // expectedProxyItems +} + +void tst_QSortFilterProxyModel::match() +{ + QFETCH(QStringList, sourceItems); + QFETCH(int, sortOrder); + QFETCH(QString, filter); + QFETCH(int, proxyStartRow); + QFETCH(QString, what); + QFETCH(int, matchFlags); + QFETCH(IntList, expectedProxyItems); + + QStandardItemModel model; + QSortFilterProxyModel proxy; + + proxy.setSourceModel(&model); + model.insertColumns(0, 1); + model.insertRows(0, sourceItems.count()); + + for (int i = 0; i < sourceItems.count(); ++i) { + QModelIndex index = model.index(i, 0, QModelIndex()); + model.setData(index, sourceItems.at(i), Qt::DisplayRole); + } + + proxy.sort(0, static_cast(sortOrder)); + proxy.setFilterRegExp(filter); + + QModelIndex startIndex = proxy.index(proxyStartRow, 0); + QModelIndexList indexes = proxy.match(startIndex, Qt::DisplayRole, what, + expectedProxyItems.count(), + Qt::MatchFlags(matchFlags)); + QCOMPARE(indexes.count(), expectedProxyItems.count()); + for (int i = 0; i < indexes.count(); ++i) + QCOMPARE(indexes.at(i).row(), expectedProxyItems.at(i)); +} + +void tst_QSortFilterProxyModel::insertIntoChildrenlessItem() +{ + QStandardItemModel model; + QStandardItem *itemA = new QStandardItem("a"); + model.appendRow(itemA); + QStandardItem *itemB = new QStandardItem("b"); + model.appendRow(itemB); + QStandardItem *itemC = new QStandardItem("c"); + model.appendRow(itemC); + + QSortFilterProxyModel proxy; + proxy.setSourceModel(&model); + + QSignalSpy colsInsertedSpy(&proxy, SIGNAL(columnsInserted(const QModelIndex&, int, int))); + QSignalSpy rowsInsertedSpy(&proxy, SIGNAL(rowsInserted(const QModelIndex&, int, int))); + + (void)proxy.rowCount(QModelIndex()); // force mapping of "a", "b", "c" + QCOMPARE(colsInsertedSpy.count(), 0); + QCOMPARE(rowsInsertedSpy.count(), 0); + + // now add a child to itemB ==> should get insert notification from the proxy + itemB->appendRow(new QStandardItem("a.0")); + QCOMPARE(colsInsertedSpy.count(), 1); + QCOMPARE(rowsInsertedSpy.count(), 1); + + QVariantList args = colsInsertedSpy.takeFirst(); + QCOMPARE(qvariant_cast(args.at(0)), proxy.mapFromSource(itemB->index())); + QCOMPARE(qvariant_cast(args.at(1)), 0); + QCOMPARE(qvariant_cast(args.at(2)), 0); + + args = rowsInsertedSpy.takeFirst(); + QCOMPARE(qvariant_cast(args.at(0)), proxy.mapFromSource(itemB->index())); + QCOMPARE(qvariant_cast(args.at(1)), 0); + QCOMPARE(qvariant_cast(args.at(2)), 0); +} + +void tst_QSortFilterProxyModel::invalidateMappedChildren() +{ + QStandardItemModel model; + + QSortFilterProxyModel proxy; + proxy.setSourceModel(&model); + + QStandardItem *itemA = new QStandardItem("a"); + model.appendRow(itemA); + QStandardItem *itemB = new QStandardItem("b"); + itemA->appendRow(itemB); + + QStandardItem *itemC = new QStandardItem("c"); + itemB->appendRow(itemC); + itemC->appendRow(new QStandardItem("d")); + + // force mappings + (void)proxy.hasChildren(QModelIndex()); + (void)proxy.hasChildren(proxy.mapFromSource(itemA->index())); + (void)proxy.hasChildren(proxy.mapFromSource(itemB->index())); + (void)proxy.hasChildren(proxy.mapFromSource(itemC->index())); + + itemB->removeRow(0); // should invalidate mapping of itemC + itemC = new QStandardItem("c"); + itemA->appendRow(itemC); + itemC->appendRow(new QStandardItem("d")); + + itemA->removeRow(1); // should invalidate mapping of itemC + itemC = new QStandardItem("c"); + itemB->appendRow(itemC); + itemC->appendRow(new QStandardItem("d")); + + QCOMPARE(proxy.rowCount(proxy.mapFromSource(itemA->index())), 1); + QCOMPARE(proxy.rowCount(proxy.mapFromSource(itemB->index())), 1); + QCOMPARE(proxy.rowCount(proxy.mapFromSource(itemC->index())), 1); +} + +class EvenOddFilterModel : public QSortFilterProxyModel +{ +public: + virtual bool filterAcceptsRow(int srcRow, const QModelIndex& srcParent) const + { + if (srcParent.isValid()) + return (srcParent.row() % 2) ^ !(srcRow % 2); + return (srcRow % 2); + } +}; + +void tst_QSortFilterProxyModel::insertRowIntoFilteredParent() +{ + QStandardItemModel model; + EvenOddFilterModel proxy; + proxy.setSourceModel(&model); + + QSignalSpy spy(&proxy, SIGNAL(rowsInserted(const QModelIndex&, int, int))); + + QStandardItem *itemA = new QStandardItem(); + model.appendRow(itemA); // A will be filtered + QStandardItem *itemB = new QStandardItem(); + itemA->appendRow(itemB); + + QCOMPARE(spy.count(), 0); + + itemA->removeRow(0); + + QCOMPARE(spy.count(), 0); +} + +void tst_QSortFilterProxyModel::filterOutParentAndFilterInChild() +{ + QStandardItemModel model; + QSortFilterProxyModel proxy; + proxy.setSourceModel(&model); + + proxy.setFilterRegExp("A|B"); + QStandardItem *itemA = new QStandardItem("A"); + model.appendRow(itemA); // not filtered + QStandardItem *itemB = new QStandardItem("B"); + itemA->appendRow(itemB); // not filtered + QStandardItem *itemC = new QStandardItem("C"); + itemA->appendRow(itemC); // filtered + + QSignalSpy removedSpy(&proxy, SIGNAL(rowsRemoved(const QModelIndex&, int, int))); + QSignalSpy insertedSpy(&proxy, SIGNAL(rowsInserted(const QModelIndex&, int, int))); + + proxy.setFilterRegExp("C"); // A and B will be filtered out, C filtered in + + // we should now have been notified that the subtree represented by itemA has been removed + QCOMPARE(removedSpy.count(), 1); + // we should NOT get any inserts; itemC should be filtered because its parent (itemA) is + QCOMPARE(insertedSpy.count(), 0); +} + +void tst_QSortFilterProxyModel::sourceInsertRows() +{ + QStandardItemModel model; + QSortFilterProxyModel proxyModel; + proxyModel.setSourceModel(&model); + + model.insertColumns(0, 1, QModelIndex()); + model.insertRows(0, 2, QModelIndex()); + + { + QModelIndex parent = model.index(0, 0, QModelIndex()); + model.insertColumns(0, 1, parent); + model.insertRows(0, 1, parent); + } + + { + QModelIndex parent = model.index(1, 0, QModelIndex()); + model.insertColumns(0, 1, parent); + model.insertRows(0, 1, parent); + } + + model.insertRows(0, 1, QModelIndex()); + model.insertRows(0, 1, QModelIndex()); + + QVERIFY(true); // if you got here without asserting, it's all good +} + +void tst_QSortFilterProxyModel::sourceModelDeletion() +{ + + QSortFilterProxyModel proxyModel; + { + QStandardItemModel model; + proxyModel.setSourceModel(&model); + QCOMPARE(proxyModel.sourceModel(), static_cast(&model)); + } + QCOMPARE(proxyModel.sourceModel(), static_cast(0)); + +} + +void tst_QSortFilterProxyModel::sortColumnTracking1() +{ + QStandardItemModel model; + QSortFilterProxyModel proxyModel; + proxyModel.setSourceModel(&model); + + model.insertColumns(0, 10); + model.insertRows(0, 10); + + proxyModel.sort(1); + QCOMPARE(proxyModel.sortColumn(), 1); + + model.insertColumn(8); + QCOMPARE(proxyModel.sortColumn(), 1); + + model.removeColumn(8); + QCOMPARE(proxyModel.sortColumn(), 1); + + model.insertColumn(2); + QCOMPARE(proxyModel.sortColumn(), 1); + + model.removeColumn(2); + QCOMPARE(proxyModel.sortColumn(), 1); + + model.insertColumn(1); + QCOMPARE(proxyModel.sortColumn(), 2); + + model.removeColumn(1); + QCOMPARE(proxyModel.sortColumn(), 1); + + model.removeColumn(1); + QCOMPARE(proxyModel.sortColumn(), -1); +} + +void tst_QSortFilterProxyModel::sortColumnTracking2() +{ + QStandardItemModel model; + QSortFilterProxyModel proxyModel; + proxyModel.setDynamicSortFilter(true); + proxyModel.setSourceModel(&model); + + proxyModel.sort(0); + QCOMPARE(proxyModel.sortColumn(), 0); + + QList items; + QStringList strings; + strings << "foo" << "bar" << "some" << "others" << "item" << "aa" << "zz"; + foreach (QString s, strings) + items << new QStandardItem(s); + + model.insertColumn(0,items); + QCOMPARE(proxyModel.sortColumn(), 0); + QCOMPARE(proxyModel.data(proxyModel.index(0,0)).toString(),QString::fromLatin1("aa")); + QCOMPARE(proxyModel.data(proxyModel.index(strings.count()-1,0)).toString(),QString::fromLatin1("zz")); +} + +void tst_QSortFilterProxyModel::sortStable() +{ + QStandardItemModel* model = new QStandardItemModel(5, 2); + for (int r=0; r<5; r++) { + for (int c=0; c<2; c++) { + QStandardItem* item = new QStandardItem( + QString("Row:%0, Column:%1").arg(r).arg(c) ); + for( int i=0; i<3; i++ ) { + QStandardItem* child = new QStandardItem( + QString("Item %0").arg(i) ); + item->appendRow( child ); + } + model->setItem(r, c, item); + } + } + model->setHorizontalHeaderItem( 0, new QStandardItem( "Name" )); + model->setHorizontalHeaderItem( 1, new QStandardItem( "Value" ) ); + + + QSortFilterProxyModel *filterModel = new QSortFilterProxyModel(model); + filterModel->setSourceModel(model); + + QTreeView *view = new QTreeView; + view->setModel(filterModel); + QModelIndex firstRoot = filterModel->index(0,0); + view->expand(firstRoot); + view->setSortingEnabled(true); + + view->model()->sort(1, Qt::DescendingOrder); + QVariant lastItemData =filterModel->index(2,0, firstRoot).data(); + view->model()->sort(1, Qt::DescendingOrder); + QCOMPARE(lastItemData, filterModel->index(2,0, firstRoot).data()); +} + +void tst_QSortFilterProxyModel::task236755_hiddenColumns() +{ + class MyStandardItemModel : public QStandardItemModel + { + public: + MyStandardItemModel() : QStandardItemModel(0,5) {} + void reset() + { QStandardItemModel::reset(); } + friend class tst_QSortFilterProxyModel; + } model; + QSortFilterProxyModel proxy; + proxy.setSourceModel(&model); + + QTableView view; + view.setModel(&proxy); + + view.hideColumn(0); + + QVERIFY(view.isColumnHidden(0)); + model.blockSignals(true); + model.setRowCount(1); + model.blockSignals(false); + model.reset(); + + //in the initial task this would be false because resetting + //model would also reset the hidden columns + QVERIFY(view.isColumnHidden(0)); +} + +void tst_QSortFilterProxyModel::task247867_insertRowsSort() +{ + QStandardItemModel model(2,2); + QSortFilterProxyModel proxyModel; + proxyModel.setSourceModel(&model); + + proxyModel.sort(0); + QCOMPARE(proxyModel.sortColumn(), 0); + + model.insertColumns(0, 3, model.index(0,0)); + QCOMPARE(proxyModel.sortColumn(), 0); + + model.removeColumns(0, 3, model.index(0,0)); + QCOMPARE(proxyModel.sortColumn(), 0); +} + +void tst_QSortFilterProxyModel::task248868_staticSorting() +{ + QStandardItemModel model(0, 1); + QSortFilterProxyModel proxy; + proxy.setSourceModel(&model); + proxy.setDynamicSortFilter(false); + QStringList initial = QString("bateau avion dragon hirondelle flamme camion elephant").split(" "); + + // prepare model + QStandardItem *root = model.invisibleRootItem (); + QList items; + for (int i = 0; i < initial.count(); ++i) { + items.append(new QStandardItem(initial.at(i))); + } + root->insertRows(0, items); + QCOMPARE(model.rowCount(QModelIndex()), initial.count()); + QCOMPARE(model.columnCount(QModelIndex()), 1); + + // make sure the proxy is unsorted + QCOMPARE(proxy.columnCount(QModelIndex()), 1); + QCOMPARE(proxy.rowCount(QModelIndex()), initial.count()); + for (int row = 0; row < proxy.rowCount(QModelIndex()); ++row) { + QModelIndex index = proxy.index(row, 0, QModelIndex()); + QCOMPARE(proxy.data(index, Qt::DisplayRole).toString(), initial.at(row)); + } + + // sort + proxy.sort(0); + + QStringList expected = initial; + expected.sort(); + // make sure the proxy is sorted + for (int row = 0; row < proxy.rowCount(QModelIndex()); ++row) { + QModelIndex index = proxy.index(row, 0, QModelIndex()); + QCOMPARE(proxy.data(index, Qt::DisplayRole).toString(), expected.at(row)); + } + + //update one item. + model.setItem(0, 0, new QStandardItem("girafe")); + + // make sure the proxy is updated but not sorted + expected.replaceInStrings("bateau", "girafe"); + for (int row = 0; row < proxy.rowCount(QModelIndex()); ++row) { + QModelIndex index = proxy.index(row, 0, QModelIndex()); + QCOMPARE(proxy.data(index, Qt::DisplayRole).toString(), expected.at(row)); + } + + // sort again + proxy.sort(0); + expected.sort(); + + // make sure the proxy is sorted + for (int row = 0; row < proxy.rowCount(QModelIndex()); ++row) { + QModelIndex index = proxy.index(row, 0, QModelIndex()); + QCOMPARE(proxy.data(index, Qt::DisplayRole).toString(), expected.at(row)); + } + +} + +void tst_QSortFilterProxyModel::task248868_dynamicSorting() +{ + QStringListModel model1; + const QStringList initial = QString("bateau avion dragon hirondelle flamme camion elephant").split(" "); + model1.setStringList(initial); + QSortFilterProxyModel proxy1; + proxy1.sort(0); + proxy1.setSourceModel(&model1); + + QCOMPARE(proxy1.columnCount(QModelIndex()), 1); + //the model should not be sorted because sorting has not been set to dynamic yet. + QCOMPARE(proxy1.rowCount(QModelIndex()), initial.count()); + for (int row = 0; row < proxy1.rowCount(QModelIndex()); ++row) { + QModelIndex index = proxy1.index(row, 0, QModelIndex()); + QCOMPARE(proxy1.data(index, Qt::DisplayRole).toString(), initial.at(row)); + } + + proxy1.setDynamicSortFilter(true); + + //now the model should be sorted. + QStringList expected = initial; + expected.sort(); + for (int row = 0; row < proxy1.rowCount(QModelIndex()); ++row) { + QModelIndex index = proxy1.index(row, 0, QModelIndex()); + QCOMPARE(proxy1.data(index, Qt::DisplayRole).toString(), expected.at(row)); + } + + QStringList initial2 = initial; + initial2.replaceInStrings("bateau", "girafe"); + model1.setStringList(initial2); //this will cause a reset + + QStringList expected2 = initial2; + expected2.sort(); + + //now the model should still be sorted. + for (int row = 0; row < proxy1.rowCount(QModelIndex()); ++row) { + QModelIndex index = proxy1.index(row, 0, QModelIndex()); + QCOMPARE(proxy1.data(index, Qt::DisplayRole).toString(), expected2.at(row)); + } + + QStringListModel model2(initial); + proxy1.setSourceModel(&model2); + + //the model should again be sorted + for (int row = 0; row < proxy1.rowCount(QModelIndex()); ++row) { + QModelIndex index = proxy1.index(row, 0, QModelIndex()); + QCOMPARE(proxy1.data(index, Qt::DisplayRole).toString(), expected.at(row)); + } + + //set up the sorting before seting the model up + QSortFilterProxyModel proxy2; + proxy2.setDynamicSortFilter(true); + proxy2.sort(0); + proxy2.setSourceModel(&model2); + for (int row = 0; row < proxy2.rowCount(QModelIndex()); ++row) { + QModelIndex index = proxy2.index(row, 0, QModelIndex()); + QCOMPARE(proxy2.data(index, Qt::DisplayRole).toString(), expected.at(row)); + } +} + +class QtTestModel: public QAbstractItemModel +{ + public: + QtTestModel(int _rows, int _cols, QObject *parent = 0): QAbstractItemModel(parent), + rows(_rows), cols(_cols), wrongIndex(false) { } + + bool canFetchMore(const QModelIndex &idx) const { + return !fetched.contains(idx); + } + + void fetchMore(const QModelIndex &idx) { + if (fetched.contains(idx)) + return; + beginInsertRows(idx, 0, rows-1); + fetched.insert(idx); + endInsertRows(); + } + + bool hasChildren(const QModelIndex & = QModelIndex()) const { + return true; + } + + int rowCount(const QModelIndex& parent = QModelIndex()) const { + return fetched.contains(parent) ? rows : 0; + } + int columnCount(const QModelIndex& parent = QModelIndex()) const { + Q_UNUSED(parent); + return cols; + } + + QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const + { + if (row < 0 || column < 0 || column >= cols || row >= rows) { + return QModelIndex(); + } + QModelIndex i = createIndex(row, column, int(parent.internalId() + 1)); + parentHash[i] = parent; + return i; + } + + QModelIndex parent(const QModelIndex &index) const + { + if (!parentHash.contains(index)) + return QModelIndex(); + return parentHash[index]; + } + + QVariant data(const QModelIndex &idx, int role) const + { + if (!idx.isValid()) + return QVariant(); + + if (role == Qt::DisplayRole) { + if (idx.row() < 0 || idx.column() < 0 || idx.column() >= cols || idx.row() >= rows) { + wrongIndex = true; + qWarning("Invalid modelIndex [%d,%d,%p]", idx.row(), idx.column(), + idx.internalPointer()); + } + return QString("[%1,%2]").arg(idx.row()).arg(idx.column()); + } + return QVariant(); + } + + QSet fetched; + int rows, cols; + mutable bool wrongIndex; + mutable QMap parentHash; +}; + +void tst_QSortFilterProxyModel::task250023_fetchMore() +{ + QtTestModel model(10,10); + QSortFilterProxyModel proxy; + proxy.setSourceModel(&model); + QVERIFY(proxy.canFetchMore(QModelIndex())); + QVERIFY(proxy.hasChildren()); + while (proxy.canFetchMore(QModelIndex())) + proxy.fetchMore(QModelIndex()); + QCOMPARE(proxy.rowCount(), 10); + QCOMPARE(proxy.columnCount(), 10); + + QModelIndex idx = proxy.index(1,1); + QVERIFY(idx.isValid()); + QVERIFY(proxy.canFetchMore(idx)); + QVERIFY(proxy.hasChildren(idx)); + while (proxy.canFetchMore(idx)) + proxy.fetchMore(idx); + QCOMPARE(proxy.rowCount(idx), 10); + QCOMPARE(proxy.columnCount(idx), 10); +} + +void tst_QSortFilterProxyModel::task251296_hiddenChildren() +{ + QStandardItemModel model; + QSortFilterProxyModel proxy; + proxy.setSourceModel(&model); + proxy.setDynamicSortFilter(true); + + QStandardItem *itemA = new QStandardItem("A VISIBLE"); + model.appendRow(itemA); + QStandardItem *itemB = new QStandardItem("B VISIBLE"); + itemA->appendRow(itemB); + QStandardItem *itemC = new QStandardItem("C"); + itemA->appendRow(itemC); + proxy.setFilterRegExp("VISIBLE"); + + QCOMPARE(proxy.rowCount(QModelIndex()) , 1); + QPersistentModelIndex indexA = proxy.index(0,0); + QCOMPARE(proxy.data(indexA).toString(), QString::fromLatin1("A VISIBLE")); + + QCOMPARE(proxy.rowCount(indexA) , 1); + QPersistentModelIndex indexB = proxy.index(0, 0, indexA); + QCOMPARE(proxy.data(indexB).toString(), QString::fromLatin1("B VISIBLE")); + + itemA->setText("A"); + QCOMPARE(proxy.rowCount(QModelIndex()), 0); + QVERIFY(!indexA.isValid()); + QVERIFY(!indexB.isValid()); + + itemB->setText("B"); + itemA->setText("A VISIBLE"); + itemC->setText("C VISIBLE"); + + QCOMPARE(proxy.rowCount(QModelIndex()), 1); + indexA = proxy.index(0,0); + QCOMPARE(proxy.data(indexA).toString(), QString::fromLatin1("A VISIBLE")); + + QCOMPARE(proxy.rowCount(indexA) , 1); + QModelIndex indexC = proxy.index(0, 0, indexA); + QCOMPARE(proxy.data(indexC).toString(), QString::fromLatin1("C VISIBLE")); + + proxy.setFilterRegExp("C"); + QCOMPARE(proxy.rowCount(QModelIndex()), 0); + itemC->setText("invisible"); + itemA->setText("AC"); + + QCOMPARE(proxy.rowCount(QModelIndex()), 1); + indexA = proxy.index(0,0); + QCOMPARE(proxy.data(indexA).toString(), QString::fromLatin1("AC")); + QCOMPARE(proxy.rowCount(indexA) , 0); +} + +void tst_QSortFilterProxyModel::task252507_mapFromToSource() +{ + QtTestModel source(10,10); + source.fetchMore(QModelIndex()); + QSortFilterProxyModel proxy; + proxy.setSourceModel(&source); + QCOMPARE(proxy.mapFromSource(source.index(5, 4)), proxy.index(5, 4)); + QCOMPARE(proxy.mapToSource(proxy.index(3, 2)), source.index(3, 2)); + QCOMPARE(proxy.mapFromSource(QModelIndex()), QModelIndex()); + QCOMPARE(proxy.mapToSource(QModelIndex()), QModelIndex()); + +#ifdef QT_NO_DEBUG //if Qt is compiled in debug mode, this will assert + QTest::ignoreMessage(QtWarningMsg, "QSortFilterProxyModel: index from wrong model passed to mapToSource "); + QCOMPARE(proxy.mapToSource(source.index(2, 3)), QModelIndex()); + QTest::ignoreMessage(QtWarningMsg, "QSortFilterProxyModel: index from wrong model passed to mapFromSource "); + QCOMPARE(proxy.mapFromSource(proxy.index(6, 2)), QModelIndex()); +#endif +} + +static QStandardItem *addEntry(QStandardItem* pParent, const QString &description) +{ + QStandardItem* pItem = new QStandardItem(description); + pParent->appendRow(pItem); + return pItem; +} + + +void tst_QSortFilterProxyModel::task255652_removeRowsRecursive() +{ + QStandardItemModel pModel; + QStandardItem *pItem1 = new QStandardItem("root"); + pModel.appendRow(pItem1); + QList items; + + QStandardItem *pItem11 = addEntry(pItem1,"Sub-heading"); + items << pItem11; + QStandardItem *pItem111 = addEntry(pItem11,"A"); + items << pItem111; + items << addEntry(pItem111,"A1"); + items << addEntry(pItem111,"A2"); + QStandardItem *pItem112 = addEntry(pItem11,"B"); + items << pItem112; + items << addEntry(pItem112,"B1"); + items << addEntry(pItem112,"B2"); + QStandardItem *pItem1123 = addEntry(pItem112,"B3"); + items << pItem1123; + items << addEntry(pItem1123,"B3-"); + + QSortFilterProxyModel proxy; + proxy.setSourceModel(&pModel); + + QList sourceIndexes; + QList proxyIndexes; + foreach (QStandardItem *item, items) { + QModelIndex idx = item->index(); + sourceIndexes << idx; + proxyIndexes << proxy.mapFromSource(idx); + } + + foreach (const QPersistentModelIndex &pidx, sourceIndexes) + QVERIFY(pidx.isValid()); + foreach (const QPersistentModelIndex &pidx, proxyIndexes) + QVERIFY(pidx.isValid()); + + QList itemRow = pItem1->takeRow(0); + + QCOMPARE(itemRow.count(), 1); + QCOMPARE(itemRow.first(), pItem11); + + foreach (const QPersistentModelIndex &pidx, sourceIndexes) + QVERIFY(!pidx.isValid()); + foreach (const QPersistentModelIndex &pidx, proxyIndexes) + QVERIFY(!pidx.isValid()); + + delete pItem11; +} + +void tst_QSortFilterProxyModel::taskQTBUG_6205_doubleProxySelectionSetSourceModel() +{ + QStandardItemModel *model1 = new QStandardItemModel; + QStandardItem *parentItem = model1->invisibleRootItem(); + for (int i = 0; i < 4; ++i) { + QStandardItem *item = new QStandardItem(QString("model1 item %0").arg(i)); + parentItem->appendRow(item); + parentItem = item; + } + + QStandardItemModel *model2 = new QStandardItemModel; + QStandardItem *parentItem2 = model2->invisibleRootItem(); + for (int i = 0; i < 4; ++i) { + QStandardItem *item = new QStandardItem(QString("model2 item %0").arg(i)); + parentItem2->appendRow(item); + parentItem2 = item; + } + + QSortFilterProxyModel *toggleProxy = new QSortFilterProxyModel; + toggleProxy->setSourceModel(model1); + + QSortFilterProxyModel *proxyModel = new QSortFilterProxyModel; + proxyModel->setSourceModel(toggleProxy); + + QModelIndex mi = proxyModel->index(0, 0, proxyModel->index(0, 0, proxyModel->index(0, 0))); + QItemSelectionModel ism(proxyModel); + ism.select(mi, QItemSelectionModel::Select); + QModelIndexList mil = ism.selectedIndexes(); + QCOMPARE(mil.count(), 1); + QCOMPARE(mil.first(), mi); + + toggleProxy->setSourceModel(model2); + // No crash, it's good news! + QVERIFY(ism.selection().isEmpty()); +} + +void tst_QSortFilterProxyModel::taskQTBUG_7537_appearsAndSort() +{ + class PModel : public QSortFilterProxyModel + { + public: + PModel() : mVisible(false) {}; + protected: + bool filterAcceptsRow(int, const QModelIndex &) const + { + return mVisible; + } + + public: + void updateXX() + { + mVisible = true; + invalidate(); + } + private: + bool mVisible; + } proxyModel; + + + QStringListModel sourceModel; + QStringList list; + list << "b" << "a" << "c"; + sourceModel.setStringList(list); + + proxyModel.setSourceModel(&sourceModel); + proxyModel.setDynamicSortFilter(true); + proxyModel.sort(0, Qt::AscendingOrder); + + QApplication::processEvents(); + QCOMPARE(sourceModel.rowCount(), 3); + QCOMPARE(proxyModel.rowCount(), 0); //all rows are hidden at first; + + QSignalSpy spyAbout1(&proxyModel, SIGNAL(layoutAboutToBeChanged())); + QSignalSpy spyChanged1(&proxyModel, SIGNAL(layoutChanged())); + + //introducing secondProxyModel to test the layoutChange when many items appears at once + QSortFilterProxyModel secondProxyModel; + secondProxyModel.setSourceModel(&proxyModel); + secondProxyModel.setDynamicSortFilter(true); + secondProxyModel.sort(0, Qt::DescendingOrder); + QCOMPARE(secondProxyModel.rowCount(), 0); //all rows are hidden at first; + QSignalSpy spyAbout2(&secondProxyModel, SIGNAL(layoutAboutToBeChanged())); + QSignalSpy spyChanged2(&secondProxyModel, SIGNAL(layoutChanged())); + + proxyModel.updateXX(); + QApplication::processEvents(); + //now rows should be visible, and sorted + QCOMPARE(proxyModel.rowCount(), 3); + QCOMPARE(proxyModel.data(proxyModel.index(0,0), Qt::DisplayRole).toString(), QString::fromLatin1("a")); + QCOMPARE(proxyModel.data(proxyModel.index(1,0), Qt::DisplayRole).toString(), QString::fromLatin1("b")); + QCOMPARE(proxyModel.data(proxyModel.index(2,0), Qt::DisplayRole).toString(), QString::fromLatin1("c")); + + //now rows should be visible, and sorted + QCOMPARE(secondProxyModel.rowCount(), 3); + QCOMPARE(secondProxyModel.data(secondProxyModel.index(0,0), Qt::DisplayRole).toString(), QString::fromLatin1("c")); + QCOMPARE(secondProxyModel.data(secondProxyModel.index(1,0), Qt::DisplayRole).toString(), QString::fromLatin1("b")); + QCOMPARE(secondProxyModel.data(secondProxyModel.index(2,0), Qt::DisplayRole).toString(), QString::fromLatin1("a")); + + QCOMPARE(spyAbout1.count(), 1); + QCOMPARE(spyChanged1.count(), 1); + QCOMPARE(spyAbout2.count(), 1); + QCOMPARE(spyChanged2.count(), 1); +} + +void tst_QSortFilterProxyModel::taskQTBUG_7716_unnecessaryDynamicSorting() +{ + QStringListModel model; + const QStringList initial = QString("bravo charlie delta echo").split(" "); + model.setStringList(initial); + QSortFilterProxyModel proxy; + proxy.setDynamicSortFilter(false); + proxy.setSourceModel(&model); + proxy.sort(Qt::AscendingOrder); + + //append two rows + int maxrows = proxy.rowCount(QModelIndex()); + model.insertRows(maxrows, 2); + model.setData(model.index(maxrows, 0), QString("alpha")); + model.setData(model.index(maxrows + 1, 0), QString("fondue")); + + //append new items to the initial string list and compare with model + QStringList expected = initial; + expected << QString("alpha") << QString("fondue"); + + //if bug 7716 is present, new rows were prepended, when they should have been appended + for (int row = 0; row < proxy.rowCount(QModelIndex()); ++row) { + QModelIndex index = proxy.index(row, 0, QModelIndex()); + QCOMPARE(proxy.data(index, Qt::DisplayRole).toString(), expected.at(row)); + } +} + +class SelectionProxyModel : QAbstractProxyModel +{ + Q_OBJECT +public: + SelectionProxyModel() + : QAbstractProxyModel(), selectionModel(0) + { + } + + QModelIndex mapFromSource(QModelIndex const&) const + { return QModelIndex(); } + + QModelIndex mapToSource(QModelIndex const&) const + { return QModelIndex(); } + + QModelIndex index(int, int, const QModelIndex&) const + { return QModelIndex(); } + + QModelIndex parent(const QModelIndex&) const + { return QModelIndex(); } + + int rowCount(const QModelIndex&) const + { return 0; } + + int columnCount(const QModelIndex&) const + { return 0; } + + void setSourceModel( QAbstractItemModel *sourceModel ) + { + beginResetModel(); + disconnect( sourceModel, SIGNAL(modelAboutToBeReset()), this, SLOT(sourceModelAboutToBeReset()) ); + QAbstractProxyModel::setSourceModel( sourceModel ); + connect( sourceModel, SIGNAL(modelAboutToBeReset()), this, SLOT(sourceModelAboutToBeReset()) ); + endResetModel(); + } + + void setSelectionModel( QItemSelectionModel *_selectionModel ) + { + selectionModel = _selectionModel; + } + +private slots: + void sourceModelAboutToBeReset() + { + QVERIFY( selectionModel->selectedIndexes().size() == 1 ); + beginResetModel(); + } + + void sourceModelReset() + { + endResetModel(); + } + +private: + QItemSelectionModel *selectionModel; + +}; + +void tst_QSortFilterProxyModel::testMultipleProxiesWithSelection() +{ + QStringListModel model; + const QStringList initial = QString("bravo charlie delta echo").split(" "); + model.setStringList(initial); + + QSortFilterProxyModel proxy; + proxy.setSourceModel( &model ); + + SelectionProxyModel proxy1; + QSortFilterProxyModel proxy2; + + // Note that the order here matters. The order of the sourceAboutToBeReset + // exposes the bug in QSortFilterProxyModel. + proxy2.setSourceModel( &proxy ); + proxy1.setSourceModel( &proxy ); + + QItemSelectionModel selectionModel(&proxy2); + proxy1.setSelectionModel( &selectionModel ); + + selectionModel.select( proxy2.index( 0, 0 ), QItemSelectionModel::Select ); + + // trick the proxy into emitting begin/end reset signals. + proxy.setSourceModel(0); + +} + +static bool isValid(const QItemSelection &selection) { + foreach(const QItemSelectionRange &range, selection) + if (!range.isValid()) + return false; + return true; +} + +void tst_QSortFilterProxyModel::mapSelectionFromSource() +{ + QStringListModel model; + const QStringList initial = QString("bravo charlie delta echo").split(" "); + model.setStringList(initial); + + QSortFilterProxyModel proxy; + proxy.setDynamicSortFilter(true); + proxy.setFilterRegExp("d.*"); + proxy.setSourceModel(&model); + + // Only "delta" remains. + QVERIFY(proxy.rowCount() == 1); + + QItemSelection selection; + QModelIndex charlie = model.index(1, 0); + selection.append(QItemSelectionRange(charlie, charlie)); + QModelIndex delta = model.index(2, 0); + selection.append(QItemSelectionRange(delta, delta)); + QModelIndex echo = model.index(3, 0); + selection.append(QItemSelectionRange(echo, echo)); + + QVERIFY(isValid(selection)); + + QItemSelection proxiedSelection = proxy.mapSelectionFromSource(selection); + + // Only "delta" is in the mapped result. + QVERIFY(proxiedSelection.size() == 1); + QVERIFY(isValid(proxiedSelection)); +} + +class Model10287 : public QStandardItemModel +{ + Q_OBJECT + +public: + Model10287(QObject *parent = 0) + : QStandardItemModel(0, 1, parent) + { + parentItem = new QStandardItem("parent"); + parentItem->setData(false, Qt::UserRole); + appendRow(parentItem); + + childItem = new QStandardItem("child"); + childItem->setData(true, Qt::UserRole); + parentItem->appendRow(childItem); + + childItem2 = new QStandardItem("child2"); + childItem2->setData(true, Qt::UserRole); + parentItem->appendRow(childItem2); + } + + void removeChild() + { + childItem2->setData(false, Qt::UserRole); + parentItem->removeRow(0); + } + +private: + QStandardItem *parentItem, *childItem, *childItem2; +}; + +class Proxy10287 : public QSortFilterProxyModel +{ + Q_OBJECT + +public: + Proxy10287(QAbstractItemModel *model, QObject *parent = 0) + : QSortFilterProxyModel(parent) + { + setSourceModel(model); + setDynamicSortFilter(true); + } + +protected: + virtual bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const + { + // Filter based on UserRole in model + QModelIndex i = sourceModel()->index(source_row, 0, source_parent); + return i.data(Qt::UserRole).toBool(); + } +}; + +void tst_QSortFilterProxyModel::taskQTBUG_10287_unnecessaryMapCreation() +{ + Model10287 m; + Proxy10287 p(&m); + m.removeChild(); + // No assert failure, it passes. +} + +class FilteredColumnProxyModel : public QSortFilterProxyModel +{ + Q_OBJECT +public: + FilteredColumnProxyModel(QObject *parent = 0) + : QSortFilterProxyModel(parent) + { + + } + +protected: + bool filterAcceptsColumn(int column, const QModelIndex & /* source_parent */) const + { + return column % 2 != 0; + } +}; + +void tst_QSortFilterProxyModel::filteredColumns() +{ + DynamicTreeModel *model = new DynamicTreeModel(this); + + FilteredColumnProxyModel *proxy = new FilteredColumnProxyModel(this); + proxy->setSourceModel(model); + + new ModelTest(proxy, this); + + ModelInsertCommand *insertCommand = new ModelInsertCommand(model, this); + insertCommand->setNumCols(2); + insertCommand->setStartRow(0); + insertCommand->setEndRow(0); + // Parent is QModelIndex() + insertCommand->doCommand(); +} + +void tst_QSortFilterProxyModel::taskQTBUG_17812_resetInvalidate_data() +{ + QTest::addColumn("test"); + QTest::addColumn("works"); + + QTest::newRow("nothing") << 0 << false; + QTest::newRow("reset") << 1 << true; + QTest::newRow("invalidate") << 2 << true; + QTest::newRow("invalidate_filter") << 3 << true; +} + +void tst_QSortFilterProxyModel::taskQTBUG_17812_resetInvalidate() +{ + QFETCH(int, test); + QFETCH(bool, works); + + struct Proxy : QSortFilterProxyModel { + QString pattern; + virtual bool filterAcceptsRow(int source_row, const QModelIndex&) const { + return sourceModel()->data(sourceModel()->index(source_row, 0)).toString().contains(pattern); + } + void notifyChange(int test) { + switch (test) { + case 0: break; + case 1: reset(); break; + case 2: invalidate(); break; + case 3: invalidateFilter(); break; + } + } + }; + + QStringListModel sourceModel(QStringList() << "Poisson" << "Vache" << "Brebis" + << "Elephant" << "Cochon" << "Serpent" + << "Mouton" << "Ecureuil" << "Mouche"); + Proxy proxy; + proxy.pattern = QString::fromLatin1("n"); + proxy.setSourceModel(&sourceModel); + + QCOMPARE(proxy.rowCount(), 5); + for (int i = 0; i < proxy.rowCount(); i++) { + QVERIFY(proxy.data(proxy.index(i,0)).toString().contains('n')); + } + + proxy.pattern = QString::fromLatin1("o"); + proxy.notifyChange(test); + + QCOMPARE(proxy.rowCount(), works ? 4 : 5); + bool ok = true; + for (int i = 0; i < proxy.rowCount(); i++) { + if (!proxy.data(proxy.index(i,0)).toString().contains('o')) + ok = false; + } + QCOMPARE(ok, works); +} + +Q_DECLARE_METATYPE(QList) + +void tst_QSortFilterProxyModel::testParentLayoutChanged() +{ + QStandardItemModel model; + QStandardItem *parentItem = model.invisibleRootItem(); + for (int i = 0; i < 4; ++i) { + { + QStandardItem *item = new QStandardItem(QString("item %0").arg(i)); + parentItem->appendRow(item); + } + { + QStandardItem *item = new QStandardItem(QString("item 1%0").arg(i)); + parentItem->appendRow(item); + parentItem = item; + } + } + + QSortFilterProxyModel proxy; + proxy.sort(0, Qt::AscendingOrder); + proxy.setDynamicSortFilter(true); + + proxy.setSourceModel(&model); + proxy.setObjectName("proxy"); + + // When Proxy1 emits layoutChanged(QList) this + // one will too, with mapped indexes. + QSortFilterProxyModel proxy2; + proxy2.sort(0, Qt::AscendingOrder); + proxy2.setDynamicSortFilter(true); + + proxy2.setSourceModel(&proxy); + proxy2.setObjectName("proxy2"); + + qRegisterMetaType >(); + + QSignalSpy dataChangedSpy(&model, SIGNAL(dataChanged(QModelIndex,QModelIndex))); + + // Verify that the no-arg signal is still emitted. + QSignalSpy layoutAboutToBeChangedSpy(&proxy, SIGNAL(layoutAboutToBeChanged())); + QSignalSpy layoutChangedSpy(&proxy, SIGNAL(layoutChanged())); + + QSignalSpy parentsAboutToBeChangedSpy(&proxy, SIGNAL(layoutAboutToBeChanged(QList))); + QSignalSpy parentsChangedSpy(&proxy, SIGNAL(layoutChanged(QList))); + + QSignalSpy proxy2ParentsAboutToBeChangedSpy(&proxy2, SIGNAL(layoutAboutToBeChanged(QList))); + QSignalSpy proxy2ParentsChangedSpy(&proxy2, SIGNAL(layoutChanged(QList))); + + QStandardItem *item = model.invisibleRootItem()->child(1)->child(1); + + // Ensure mapped: + proxy.mapFromSource(model.indexFromItem(item)); + + item->setData("Changed"); + + QCOMPARE(dataChangedSpy.size(), 1); + QCOMPARE(layoutAboutToBeChangedSpy.size(), 1); + QCOMPARE(layoutChangedSpy.size(), 1); + QCOMPARE(parentsAboutToBeChangedSpy.size(), 1); + QCOMPARE(parentsChangedSpy.size(), 1); + QCOMPARE(proxy2ParentsAboutToBeChangedSpy.size(), 1); + QCOMPARE(proxy2ParentsChangedSpy.size(), 1); + + QVariantList beforeSignal = parentsAboutToBeChangedSpy.first(); + QVariantList afterSignal = parentsChangedSpy.first(); + + QCOMPARE(beforeSignal.size(), 1); + QCOMPARE(afterSignal.size(), 1); + + QList beforeParents = beforeSignal.first().value >(); + QList afterParents = afterSignal.first().value >(); + + QCOMPARE(beforeParents.size(), 1); + QCOMPARE(afterParents.size(), 1); + + QVERIFY(beforeParents.first().isValid()); + QVERIFY(beforeParents.first() == afterParents.first()); + + QVERIFY(beforeParents.first() == proxy.mapFromSource(model.indexFromItem(model.invisibleRootItem()->child(1)))); + + QList proxy2BeforeList = proxy2ParentsAboutToBeChangedSpy.first().first().value >(); + QList proxy2AfterList = proxy2ParentsChangedSpy.first().first().value >(); + + QCOMPARE(proxy2BeforeList.size(), beforeParents.size()); + QCOMPARE(proxy2AfterList.size(), afterParents.size()); + foreach (const QPersistentModelIndex &idx, proxy2BeforeList) + QVERIFY(beforeParents.contains(proxy2.mapToSource(idx))); + foreach (const QPersistentModelIndex &idx, proxy2AfterList) + QVERIFY(afterParents.contains(proxy2.mapToSource(idx))); + +} + +class SignalArgumentChecker : public QObject +{ + Q_OBJECT +public: + SignalArgumentChecker(QAbstractItemModel *model, QAbstractProxyModel *proxy, QObject *parent = 0) + : QObject(parent), m_model(model), m_proxy(proxy) + { + connect(model, SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)), SLOT(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int))); + connect(model, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), SLOT(rowsMoved(QModelIndex,int,int,QModelIndex,int))); + connect(proxy, SIGNAL(layoutAboutToBeChanged(QList)), SLOT(layoutAboutToBeChanged(QList))); + connect(proxy, SIGNAL(layoutChanged(QList)), SLOT(layoutChanged(QList))); + } + +private slots: + void rowsAboutToBeMoved(const QModelIndex &source, int, int, const QModelIndex &destination, int) + { + m_p1PersistentBefore = source; + m_p2PersistentBefore = destination; + m_p2FirstProxyChild = m_proxy->index(0, 0, m_proxy->mapFromSource(destination)); + } + + void rowsMoved(const QModelIndex &source, int, int, const QModelIndex &destination, int) + { + m_p1PersistentAfter = source; + m_p2PersistentAfter = destination; + } + + void layoutAboutToBeChanged(const QList &parents) + { + QVERIFY(m_p1PersistentBefore.isValid()); + QVERIFY(m_p2PersistentBefore.isValid()); + QCOMPARE(parents.size(), 2); + QVERIFY(parents.first() != parents.at(1)); + QVERIFY(parents.contains(m_proxy->mapFromSource(m_p1PersistentBefore))); + QVERIFY(parents.contains(m_proxy->mapFromSource(m_p2PersistentBefore))); + } + + void layoutChanged(const QList &parents) + { + QVERIFY(m_p1PersistentAfter.isValid()); + QVERIFY(m_p2PersistentAfter.isValid()); + QCOMPARE(parents.size(), 2); + QVERIFY(parents.first() != parents.at(1)); + QVERIFY(parents.contains(m_proxy->mapFromSource(m_p1PersistentAfter))); + QVERIFY(parents.contains(m_proxy->mapFromSource(m_p2PersistentAfter))); + + // In the source model, the rows were moved to row 1 in the parent. + // m_p2FirstProxyChild was created with row 0 in the proxy. + // The moved rows in the proxy do not appear at row 1 because of sorting. + // Sorting causes them to appear at row 0 instead, pushing what used to + // be row 0 in the proxy down by two rows. + QCOMPARE(m_p2FirstProxyChild.row(), 2); + } + +private: + QAbstractItemModel *m_model; + QAbstractProxyModel *m_proxy; + QPersistentModelIndex m_p1PersistentBefore; + QPersistentModelIndex m_p2PersistentBefore; + QPersistentModelIndex m_p1PersistentAfter; + QPersistentModelIndex m_p2PersistentAfter; + + QPersistentModelIndex m_p2FirstProxyChild; +}; + +void tst_QSortFilterProxyModel::moveSourceRows() +{ + qRegisterMetaType >(); + + DynamicTreeModel model; + + { + ModelInsertCommand insertCommand(&model); + insertCommand.setStartRow(0); + insertCommand.setEndRow(9); + insertCommand.doCommand(); + } + { + ModelInsertCommand insertCommand(&model); + insertCommand.setAncestorRowNumbers(QList() << 2); + insertCommand.setStartRow(0); + insertCommand.setEndRow(9); + insertCommand.doCommand(); + } + { + ModelInsertCommand insertCommand(&model); + insertCommand.setAncestorRowNumbers(QList() << 5); + insertCommand.setStartRow(0); + insertCommand.setEndRow(9); + insertCommand.doCommand(); + } + + QSortFilterProxyModel proxy; + proxy.setDynamicSortFilter(true); + proxy.sort(0, Qt::AscendingOrder); + + // We need to check the arguments at emission time + SignalArgumentChecker checker(&model, &proxy); + + proxy.setSourceModel(&model); + + QSortFilterProxyModel filterProxy; + filterProxy.setDynamicSortFilter(true); + filterProxy.sort(0, Qt::AscendingOrder); + filterProxy.setSourceModel(&proxy); + filterProxy.setFilterRegExp("6"); // One of the parents + + QSortFilterProxyModel filterBothProxy; + filterBothProxy.setDynamicSortFilter(true); + filterBothProxy.sort(0, Qt::AscendingOrder); + filterBothProxy.setSourceModel(&proxy); + filterBothProxy.setFilterRegExp("5"); // The parents are 6 and 3. This filters both out. + + QSignalSpy modelBeforeSpy(&model, SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int))); + QSignalSpy modelAfterSpy(&model, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int))); + QSignalSpy proxyBeforeMoveSpy(m_proxy, SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int))); + QSignalSpy proxyAfterMoveSpy(m_proxy, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int))); + QSignalSpy proxyBeforeParentLayoutSpy(&proxy, SIGNAL(layoutAboutToBeChanged(QList))); + QSignalSpy proxyAfterParentLayoutSpy(&proxy, SIGNAL(layoutChanged(QList))); + QSignalSpy filterBeforeParentLayoutSpy(&filterProxy, SIGNAL(layoutAboutToBeChanged(QList))); + QSignalSpy filterAfterParentLayoutSpy(&filterProxy, SIGNAL(layoutChanged(QList))); + QSignalSpy filterBothBeforeParentLayoutSpy(&filterBothProxy, SIGNAL(layoutAboutToBeChanged(QList))); + QSignalSpy filterBothAfterParentLayoutSpy(&filterBothProxy, SIGNAL(layoutChanged(QList))); + + { + ModelMoveCommand moveCommand(&model, 0); + moveCommand.setAncestorRowNumbers(QList() << 2); + moveCommand.setDestAncestors(QList() << 5); + moveCommand.setStartRow(3); + moveCommand.setEndRow(4); + moveCommand.setDestRow(1); + moveCommand.doCommand(); + } + + // Proxy notifies layout change + QCOMPARE(modelBeforeSpy.size(), 1); + QCOMPARE(proxyBeforeParentLayoutSpy.size(), 1); + QCOMPARE(modelAfterSpy.size(), 1); + QCOMPARE(proxyAfterParentLayoutSpy.size(), 1); + + // But it doesn't notify a move. + QCOMPARE(proxyBeforeMoveSpy.size(), 0); + QCOMPARE(proxyAfterMoveSpy.size(), 0); + + QCOMPARE(filterBeforeParentLayoutSpy.size(), 1); + QCOMPARE(filterAfterParentLayoutSpy.size(), 1); + + QList filterBeforeParents = filterBeforeParentLayoutSpy.first().first().value >(); + QList filterAfterParents = filterAfterParentLayoutSpy.first().first().value >(); + + QCOMPARE(filterBeforeParents.size(), 1); + QCOMPARE(filterAfterParents.size(), 1); + + QCOMPARE(filterBothBeforeParentLayoutSpy.size(), 0); + QCOMPARE(filterBothAfterParentLayoutSpy.size(), 0); +} + +QTEST_MAIN(tst_QSortFilterProxyModel) +#include "tst_qsortfilterproxymodel.moc" diff --git a/tests/auto/corelib/itemmodels/qstringlistmodel/.gitignore b/tests/auto/corelib/itemmodels/qstringlistmodel/.gitignore new file mode 100644 index 0000000000..9c14561e3c --- /dev/null +++ b/tests/auto/corelib/itemmodels/qstringlistmodel/.gitignore @@ -0,0 +1 @@ +tst_qstringlistmodel diff --git a/tests/auto/corelib/itemmodels/qstringlistmodel/qmodellistener.h b/tests/auto/corelib/itemmodels/qstringlistmodel/qmodellistener.h new file mode 100644 index 0000000000..f3ed3c1793 --- /dev/null +++ b/tests/auto/corelib/itemmodels/qstringlistmodel/qmodellistener.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** 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 +#include +#include + + +QT_FORWARD_DECLARE_CLASS(QStringListModel) + +class QModelListener : public QObject +{ + Q_OBJECT +public: + QModelListener(QStringList *pAboutToStringlist, QStringList *pExpectedStringlist, QStringListModel *pModel) + { + setTestData(pAboutToStringlist, pExpectedStringlist, pModel); + } + virtual ~QModelListener() { } + + void setTestData(QStringList *pAboutToStringlist, QStringList *pExpectedStringlist, QStringListModel *pModel) + { + m_pAboutToStringlist = pAboutToStringlist; + m_pExpectedStringlist = pExpectedStringlist; + m_pModel = pModel; + } + +private: + QStringList *m_pAboutToStringlist; + QStringList *m_pExpectedStringlist; + QStringListModel *m_pModel; + +public slots: + void rowsAboutToBeRemovedOrInserted(const QModelIndex & parent, int start, int end ); + void rowsRemovedOrInserted(const QModelIndex & parent, int start, int end ); + +}; + diff --git a/tests/auto/corelib/itemmodels/qstringlistmodel/qstringlistmodel.pro b/tests/auto/corelib/itemmodels/qstringlistmodel/qstringlistmodel.pro new file mode 100644 index 0000000000..ecdd30cae2 --- /dev/null +++ b/tests/auto/corelib/itemmodels/qstringlistmodel/qstringlistmodel.pro @@ -0,0 +1,9 @@ +CONFIG += testcase +TARGET = tst_qstringlistmodel +QT += widgets testlib +HEADERS += qmodellistener.h + +SOURCES += tst_qstringlistmodel.cpp + + + diff --git a/tests/auto/corelib/itemmodels/qstringlistmodel/tst_qstringlistmodel.cpp b/tests/auto/corelib/itemmodels/qstringlistmodel/tst_qstringlistmodel.cpp new file mode 100644 index 0000000000..c8afe5dd75 --- /dev/null +++ b/tests/auto/corelib/itemmodels/qstringlistmodel/tst_qstringlistmodel.cpp @@ -0,0 +1,283 @@ +/**************************************************************************** +** +** 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 +#include +#include +#include +#include +#include +#include +#include "qmodellistener.h" +#include + +void QModelListener::rowsAboutToBeRemovedOrInserted(const QModelIndex & parent, int start, int end ) +{ + int i; + for (i = 0; start + i <= end; i++) + { + QModelIndex mIndex = m_pModel->index(start + i, 0, parent); + QVariant var = m_pModel->data(mIndex, Qt::DisplayRole); + QString str = var.toString(); + + QCOMPARE(str, m_pAboutToStringlist->at(i)); + } +} + +void QModelListener::rowsRemovedOrInserted(const QModelIndex & parent, int , int) +{ + int i; + // Can the rows that *are* removed be iterated now ? + + // What about rowsAboutToBeInserted - what will the indices be? + // will insertRow() overwrite existing, or insert (and conseq. grow the model?) + // What will the item then contain? empty data? + + // RemoveColumn. Does that also fire the rowsRemoved-family signals? + + for (i = 0; i < m_pExpectedStringlist->size(); i++) + { + QModelIndex mIndex = m_pModel->index(i, 0, parent); + QVariant var = m_pModel->data(mIndex, Qt::DisplayRole); + QString str = var.toString(); + + //qDebug() << "index: " << i << " start: " << start << "end: " << end; + QCOMPARE(str, m_pExpectedStringlist->at(i)); + } +} + + +class tst_QStringListModel : public QObject +{ + Q_OBJECT + +public: + + tst_QStringListModel(); + virtual ~tst_QStringListModel(); + + +public slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); +private slots: + + void rowsAboutToBeRemoved_rowsRemoved(); + void rowsAboutToBeRemoved_rowsRemoved_data(); + + void rowsAboutToBeInserted_rowsInserted(); + void rowsAboutToBeInserted_rowsInserted_data(); +}; + + +tst_QStringListModel::tst_QStringListModel() + +{ +} + +tst_QStringListModel::~tst_QStringListModel() +{ +} + +void tst_QStringListModel::initTestCase() +{ +} + +void tst_QStringListModel::cleanupTestCase() +{ +} + +void tst_QStringListModel::init() +{ +} + +void tst_QStringListModel::cleanup() +{ +} + +/* + tests +*/ + + +void tst_QStringListModel::rowsAboutToBeRemoved_rowsRemoved_data() +{ + QTest::addColumn("input"); + QTest::addColumn("row"); + QTest::addColumn("count"); + QTest::addColumn("aboutto"); + QTest::addColumn("res"); + + QStringList strings0; strings0 << "One" << "Two" << "Three" << "Four" << "Five"; + QStringList aboutto0; aboutto0 << "Two" << "Three"; + QStringList res0; res0 << "One" << "Four" << "Five"; + QTest::newRow( "data0" ) << strings0 << 1 << 2 << aboutto0 << res0; + + QStringList strings1; strings1 << "One" << "Two" << "Three" << "Four" << "Five"; + QStringList aboutto1; aboutto1 << "One" << "Two"; + QStringList res1; res1 << "Three" << "Four" << "Five"; + QTest::newRow( "data1" ) << strings1 << 0 << 2 << aboutto1 << res1; + + QStringList strings2; strings2 << "One" << "Two" << "Three" << "Four" << "Five"; + QStringList aboutto2; aboutto2 << "Four" << "Five"; + QStringList res2; res2 << "One" << "Two" << "Three"; + QTest::newRow( "data2" ) << strings2 << 3 << 2 << aboutto2 << res2; + + QStringList strings3; strings3 << "One" << "Two" << "Three" << "Four" << "Five"; + QStringList aboutto3; aboutto3 << "One" << "Two" << "Three" << "Four" << "Five"; + QStringList res3; + QTest::newRow( "data3" ) << strings3 << 0 << 5 << aboutto3 << res3; + + /* Not sure if this is a valid test */ + QStringList strings4; strings4 << "One" << "Two" << "Three" << "Four" << "Five"; + QStringList aboutto4; aboutto4 << "Five" << ""; + QStringList res4; res4 << "One" << "Two" << "Three" << "Four"; + QTest::newRow( "data4" ) << strings4 << 4 << 2 << aboutto4 << res4; + + /* + * Keep this, template to add more data + QStringList strings2; strings2 << "One" << "Two" << "Three" << "Four" << "Five"; + QStringList aboutto2; aboutto2 << "One" << "Two" << "Three" << "Four" << "Five"; + QStringList res2; res2 << "One" << "Two" << "Three" << "Four" << "Five"; + QTest::newRow( "data2" ) << strings2 << 0 << 5 << aboutto2 << res2; +*/ + +} + +void tst_QStringListModel::rowsAboutToBeRemoved_rowsRemoved() +{ + QFETCH(QStringList, input); + QFETCH(int, row); + QFETCH(int, count); + QFETCH(QStringList, aboutto); + QFETCH(QStringList, res); + + QStringListModel *model = new QStringListModel(input); + QModelListener *pListener = new QModelListener(&aboutto, &res, model); + pListener->connect(model, SIGNAL( rowsAboutToBeRemoved(const QModelIndex & , int , int )), + pListener, SLOT( rowsAboutToBeRemovedOrInserted(const QModelIndex & , int , int )) ); + + pListener->connect(model, SIGNAL( rowsRemoved(const QModelIndex & , int , int )), + pListener, SLOT( rowsRemovedOrInserted(const QModelIndex & , int , int )) ); + + model->removeRows(row,count); + // At this point, control goes to our connected slots inn this order: + // 1. rowsAboutToBeRemovedOrInserted + // 2. rowsRemovedOrInserted + // Control returns here + + delete pListener; + delete model; + +} + +void tst_QStringListModel::rowsAboutToBeInserted_rowsInserted_data() +{ + QTest::addColumn("input"); + QTest::addColumn("row"); + QTest::addColumn("count"); + QTest::addColumn("aboutto"); + QTest::addColumn("res"); + + QStringList strings0; strings0 << "One" << "Two" << "Three" << "Four" << "Five"; + QStringList aboutto0; aboutto0 << "Two" << "Three"; + QStringList res0; res0 << "One" << "" << "" << "Two" << "Three" << "Four" << "Five"; + QTest::newRow( "data0" ) << strings0 << 1 << 2 << aboutto0 << res0; + + QStringList strings1; strings1 << "One" << "Two" << "Three" << "Four" << "Five"; + QStringList aboutto1; aboutto1 << "One" << "Two"; + QStringList res1; res1 << "" << "" << "One" << "Two" << "Three" << "Four" << "Five"; + QTest::newRow( "data1" ) << strings1 << 0 << 2 << aboutto1 << res1; + + QStringList strings2; strings2 << "One" << "Two" << "Three" << "Four" << "Five"; + QStringList aboutto2; aboutto2 << "Four" << "Five"; + QStringList res2; res2 << "One" << "Two" << "Three" << "" << "" << "Four" << "Five"; + QTest::newRow( "data2" ) << strings2 << 3 << 2 << aboutto2 << res2; + + QStringList strings3; strings3 << "One" << "Two" << "Three" << "Four" << "Five"; + QStringList aboutto3; aboutto3 << "One" << "Two" << "Three" << "Four" << "Five"; + QStringList res3; res3 << "" << "" << "" << "" << "" << "One" << "Two" << "Three" << "Four" << "Five"; + QTest::newRow( "data3" ) << strings3 << 0 << 5 << aboutto3 << res3; + + /* + * Keep this, template to add more data + QStringList strings2; strings2 << "One" << "Two" << "Three" << "Four" << "Five"; + QStringList aboutto2; aboutto2 << "One" << "Two" << "Three" << "Four" << "Five"; + QStringList res2; res2 << "One" << "Two" << "Three" << "Four" << "Five"; + QTest::newRow( "data2" ) << strings2 << 0 << 5 << aboutto2 << res2; +*/ + +} + +void tst_QStringListModel::rowsAboutToBeInserted_rowsInserted() +{ + QFETCH(QStringList, input); + QFETCH(int, row); + QFETCH(int, count); + QFETCH(QStringList, aboutto); + QFETCH(QStringList, res); + + QStringListModel *model = new QStringListModel(input); + QModelListener *pListener = new QModelListener(&aboutto, &res, model); + connect(model, SIGNAL( rowsAboutToBeInserted(const QModelIndex & , int , int )), + pListener, SLOT( rowsAboutToBeRemovedOrInserted(const QModelIndex & , int , int )) ); + + connect(model, SIGNAL( rowsInserted(const QModelIndex & , int , int )), + pListener, SLOT( rowsRemovedOrInserted(const QModelIndex & , int , int )) ); + + model->insertRows(row,count); + // At this point, control goes to our connected slots inn this order: + // 1. rowsAboutToBeRemovedOrInserted + // 2. rowsRemovedOrInserted + // Control returns here + + delete pListener; + delete model; + +} + + +QTEST_MAIN(tst_QStringListModel) +#include "tst_qstringlistmodel.moc" + -- cgit v1.2.3