1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the test suite of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include <QtTest/QtTest>
43 #include "dynamictreemodel.h"
44 #include "modeltest.h"
46 #include <QtCore/QCoreApplication>
47 #include <QtGui/QStandardItem>
48 #include <QtWidgets/QTreeView>
49 #include <QtWidgets/QTableView>
53 typedef QList<int> IntList;
54 typedef QPair<int, int> IntPair;
55 typedef QList<IntPair> IntPairList;
57 Q_DECLARE_METATYPE(IntList)
58 Q_DECLARE_METATYPE(IntPair)
59 Q_DECLARE_METATYPE(IntPairList)
60 Q_DECLARE_METATYPE(QModelIndex)
62 class tst_QSortFilterProxyModel : public QObject
66 tst_QSortFilterProxyModel();
70 void cleanupTestCase();
77 void sortHierarchy_data();
80 void insertRows_data();
83 void removeRows_data();
85 void removeColumns_data();
87 void insertAfterSelect();
88 void removeAfterSelect();
91 void filterHierarchy_data();
92 void filterHierarchy();
93 void filterColumns_data();
99 void changeSourceLayout();
100 void removeSourceRows_data();
101 void removeSourceRows();
102 void insertSourceRows_data();
103 void insertSourceRows();
104 void changeFilter_data();
106 void changeSourceData_data();
107 void changeSourceData();
108 void sortFilterRole();
109 void selectionFilteredOut();
112 void insertIntoChildrenlessItem();
113 void invalidateMappedChildren();
114 void insertRowIntoFilteredParent();
115 void filterOutParentAndFilterInChild();
117 void sourceInsertRows();
118 void sourceModelDeletion();
120 void sortColumnTracking1();
121 void sortColumnTracking2();
125 void hiddenColumns();
126 void insertRowsSort();
127 void staticSorting();
128 void dynamicSorting();
130 void hiddenChildren();
131 void mapFromToSource();
132 void removeRowsRecursive();
133 void doubleProxySelectionSetSourceModel();
134 void appearsAndSort();
135 void unnecessaryDynamicSorting();
136 void unnecessaryMapCreation();
137 void resetInvalidate_data();
138 void resetInvalidate();
140 void testMultipleProxiesWithSelection();
141 void mapSelectionFromSource();
142 void filteredColumns();
143 void headerDataChanged();
145 void testParentLayoutChanged();
146 void moveSourceRows();
148 void hierarchyFilterInvalidation();
149 void simpleFilterInvalidation();
152 void buildHierarchy(const QStringList &data, QAbstractItemModel *model);
153 void checkHierarchy(const QStringList &data, const QAbstractItemModel *model);
156 QStandardItemModel *m_model;
157 QSortFilterProxyModel *m_proxy;
160 // Testing get/set functions
161 void tst_QSortFilterProxyModel::getSetCheck()
163 QSortFilterProxyModel obj1;
164 QCOMPARE(obj1.sourceModel(), (QAbstractItemModel *)0);
165 // int QSortFilterProxyModel::filterKeyColumn()
166 // void QSortFilterProxyModel::setFilterKeyColumn(int)
167 obj1.setFilterKeyColumn(0);
168 QCOMPARE(0, obj1.filterKeyColumn());
169 obj1.setFilterKeyColumn(INT_MIN);
170 QCOMPARE(INT_MIN, obj1.filterKeyColumn());
171 obj1.setFilterKeyColumn(INT_MAX);
172 QCOMPARE(INT_MAX, obj1.filterKeyColumn());
175 tst_QSortFilterProxyModel::tst_QSortFilterProxyModel()
176 : m_model(0), m_proxy(0)
180 void tst_QSortFilterProxyModel::initTestCase()
182 qRegisterMetaType<QModelIndex>("QModelIndex");
183 qRegisterMetaType<IntList>("IntList");
184 qRegisterMetaType<IntPair>("IntPair");
185 qRegisterMetaType<IntPairList>("IntPairList");
186 m_model = new QStandardItemModel(0, 1);
187 m_proxy = new QSortFilterProxyModel();
188 m_proxy->setSourceModel(m_model);
191 void tst_QSortFilterProxyModel::cleanupTestCase()
197 void tst_QSortFilterProxyModel::cleanup()
199 m_proxy->setFilterRegExp(QRegExp());
200 m_proxy->sort(-1, Qt::AscendingOrder);
202 m_model->insertColumns(0, 1);
209 void tst_QSortFilterProxyModel::sort_data()
211 QTest::addColumn<int>("sortOrder");
212 QTest::addColumn<int>("sortCaseSensitivity");
213 QTest::addColumn<QStringList>("initial");
214 QTest::addColumn<QStringList>("expected");
216 QTest::newRow("flat descending") << static_cast<int>(Qt::DescendingOrder)
217 << int(Qt::CaseSensitive)
272 QTest::newRow("flat ascending") << static_cast<int>(Qt::AscendingOrder)
273 << int(Qt::CaseSensitive)
328 QTest::newRow("case insensitive") << static_cast<int>(Qt::AscendingOrder)
329 << int(Qt::CaseInsensitive)
331 << "alpha" << "BETA" << "Gamma" << "delta")
333 << "alpha" << "BETA" << "delta" << "Gamma");
334 QTest::newRow("case sensitive") << static_cast<int>(Qt::AscendingOrder)
335 << int(Qt::CaseSensitive)
337 << "alpha" << "BETA" << "Gamma" << "delta")
339 << "BETA" << "Gamma" << "alpha" << "delta");
342 for (int i = 10000; i < 20000; ++i)
343 list.append(QString("Number: %1").arg(i));
344 QTest::newRow("large set ascending") << static_cast<int>(Qt::AscendingOrder) << int(Qt::CaseSensitive) << list << list;
347 void tst_QSortFilterProxyModel::sort()
349 QFETCH(int, sortOrder);
350 QFETCH(int, sortCaseSensitivity);
351 QFETCH(QStringList, initial);
352 QFETCH(QStringList, expected);
355 QStandardItem *root = m_model->invisibleRootItem ();
356 QList<QStandardItem *> items;
357 for (int i = 0; i < initial.count(); ++i) {
358 items.append(new QStandardItem(initial.at(i)));
360 root->insertRows(0, items);
361 QCOMPARE(m_model->rowCount(QModelIndex()), initial.count());
362 QCOMPARE(m_model->columnCount(QModelIndex()), 1);
364 // make sure the proxy is unsorted
365 QCOMPARE(m_proxy->columnCount(QModelIndex()), 1);
366 QCOMPARE(m_proxy->rowCount(QModelIndex()), initial.count());
367 for (int row = 0; row < m_proxy->rowCount(QModelIndex()); ++row) {
368 QModelIndex index = m_proxy->index(row, 0, QModelIndex());
369 QCOMPARE(m_proxy->data(index, Qt::DisplayRole).toString(), initial.at(row));
373 m_proxy->sort(0, static_cast<Qt::SortOrder>(sortOrder));
374 m_proxy->setSortCaseSensitivity(static_cast<Qt::CaseSensitivity>(sortCaseSensitivity));
376 // make sure the model is unchanged
377 for (int row = 0; row < m_model->rowCount(QModelIndex()); ++row) {
378 QModelIndex index = m_model->index(row, 0, QModelIndex());
379 QCOMPARE(m_model->data(index, Qt::DisplayRole).toString(), initial.at(row));
381 // make sure the proxy is sorted
382 for (int row = 0; row < m_proxy->rowCount(QModelIndex()); ++row) {
383 QModelIndex index = m_proxy->index(row, 0, QModelIndex());
384 QCOMPARE(m_proxy->data(index, Qt::DisplayRole).toString(), expected.at(row));
387 // restore the unsorted order
390 // make sure the proxy is unsorted again
391 for (int row = 0; row < m_proxy->rowCount(QModelIndex()); ++row) {
392 QModelIndex index = m_proxy->index(row, 0, QModelIndex());
393 QCOMPARE(m_proxy->data(index, Qt::DisplayRole).toString(), initial.at(row));
397 void tst_QSortFilterProxyModel::sortHierarchy_data()
399 QTest::addColumn<int>("sortOrder");
400 QTest::addColumn<QStringList>("initial");
401 QTest::addColumn<QStringList>("expected");
403 QTest::newRow("flat ascending")
404 << static_cast<int>(Qt::AscendingOrder)
406 << "c" << "f" << "d" << "e" << "a" << "b")
408 << "a" << "b" << "c" << "d" << "e" << "f");
410 QTest::newRow("simple hierarchy")
411 << static_cast<int>(Qt::AscendingOrder)
412 << (QStringList() << "a" << "<" << "b" << "<" << "c" << ">" << ">")
413 << (QStringList() << "a" << "<" << "b" << "<" << "c" << ">" << ">");
415 QTest::newRow("hierarchical ascending")
416 << static_cast<int>(Qt::AscendingOrder)
477 void tst_QSortFilterProxyModel::sortHierarchy()
479 QFETCH(int, sortOrder);
480 QFETCH(QStringList, initial);
481 QFETCH(QStringList, expected);
483 buildHierarchy(initial, m_model);
484 checkHierarchy(initial, m_model);
485 checkHierarchy(initial, m_proxy);
486 m_proxy->sort(0, static_cast<Qt::SortOrder>(sortOrder));
487 checkHierarchy(initial, m_model);
488 checkHierarchy(expected, m_proxy);
491 void tst_QSortFilterProxyModel::insertRows_data()
493 QTest::addColumn<QStringList>("initial");
494 QTest::addColumn<QStringList>("expected");
495 QTest::addColumn<QStringList>("insert");
496 QTest::addColumn<int>("position");
498 QTest::newRow("insert one row in the middle")
514 QTest::newRow("insert one row in the beginning")
530 QTest::newRow("insert one row in the end")
547 void tst_QSortFilterProxyModel::insertRows()
549 QFETCH(QStringList, initial);
550 QFETCH(QStringList, expected);
551 QFETCH(QStringList, insert);
552 QFETCH(int, position);
554 m_model->insertRows(0, initial.count(), QModelIndex());
555 //m_model->insertColumns(0, 1, QModelIndex());
556 QCOMPARE(m_model->columnCount(QModelIndex()), 1);
557 QCOMPARE(m_model->rowCount(QModelIndex()), initial.count());
558 for (int row = 0; row < m_model->rowCount(QModelIndex()); ++row) {
559 QModelIndex index = m_model->index(row, 0, QModelIndex());
560 m_model->setData(index, initial.at(row), Qt::DisplayRole);
562 // make sure the model correct before insert
563 for (int row = 0; row < m_model->rowCount(QModelIndex()); ++row) {
564 QModelIndex index = m_model->index(row, 0, QModelIndex());
565 QCOMPARE(m_model->data(index, Qt::DisplayRole).toString(), initial.at(row));
567 // make sure the proxy is correct before insert
568 for (int row = 0; row < m_proxy->rowCount(QModelIndex()); ++row) {
569 QModelIndex index = m_proxy->index(row, 0, QModelIndex());
570 QCOMPARE(m_proxy->data(index, Qt::DisplayRole).toString(), initial.at(row));
574 m_proxy->insertRows(position, insert.count(), QModelIndex());
575 QCOMPARE(m_model->rowCount(QModelIndex()), expected.count());
576 QCOMPARE(m_proxy->rowCount(QModelIndex()), expected.count());
578 // set the data for the inserted row
579 for (int i = 0; i < insert.count(); ++i) {
580 QModelIndex index = m_proxy->index(position + i, 0, QModelIndex());
581 m_proxy->setData(index, insert.at(i), Qt::DisplayRole);
584 // make sure the model correct after insert
585 for (int row = 0; row < m_model->rowCount(QModelIndex()); ++row) {
586 QModelIndex index = m_model->index(row, 0, QModelIndex());
587 QCOMPARE(m_model->data(index, Qt::DisplayRole).toString(), expected.at(row));
590 // make sure the proxy is correct after insert
591 for (int row = 0; row < m_proxy->rowCount(QModelIndex()); ++row) {
592 QModelIndex index = m_proxy->index(row, 0, QModelIndex());
593 QCOMPARE(m_proxy->data(index, Qt::DisplayRole).toString(), expected.at(row));
597 void tst_QSortFilterProxyModel::prependRow()
599 //this tests that data is correctly handled by the sort filter when prepending a row
600 QStandardItemModel model;
601 QSortFilterProxyModel proxy;
602 proxy.setSourceModel(&model);
604 QStandardItem item("root");
605 model.appendRow(&item);
607 QStandardItem sub("sub");
608 item.appendRow(&sub);
610 sub.appendRow(new QStandardItem("test1"));
611 sub.appendRow(new QStandardItem("test2"));
613 QStandardItem sub2("sub2");
614 sub2.appendRow(new QStandardItem("sub3"));
615 item.insertRow(0, &sub2);
617 QModelIndex index_sub2 = proxy.mapFromSource(model.indexFromItem(&sub2));
619 QCOMPARE(sub2.rowCount(), proxy.rowCount(index_sub2));
620 QCOMPARE(proxy.rowCount(QModelIndex()), 1); //only the "root" item is there
623 void tst_QSortFilterProxyModel::removeRows_data()
625 QTest::addColumn<QStringList>("initial");
626 QTest::addColumn<int>("sortOrder");
627 QTest::addColumn<QString>("filter");
628 QTest::addColumn<int>("position");
629 QTest::addColumn<int>("count");
630 QTest::addColumn<bool>("success");
631 QTest::addColumn<QStringList>("expectedProxy");
632 QTest::addColumn<QStringList>("expectedSource");
634 QTest::newRow("remove one row in the middle [no sorting/filter]")
642 << QString() // no filter
646 << (QStringList() // expectedProxy
651 << (QStringList() // expectedSource
657 QTest::newRow("remove one row in the beginning [no sorting/filter]")
665 << QString() // no filter
669 << (QStringList() // expectedProxy
674 << (QStringList() // expectedSource
680 QTest::newRow("remove one row in the end [no sorting/filter]")
688 << QString() // no filter
692 << (QStringList() // expectedProxy
697 << (QStringList() // expectedSource
703 QTest::newRow("remove all [no sorting/filter]")
711 << QString() // no filter
715 << QStringList() // expectedProxy
716 << QStringList(); // expectedSource
718 QTest::newRow("remove one row past the end [no sorting/filter]")
726 << QString() // no filter
730 << (QStringList() // expectedProxy
736 << (QStringList() // expectedSource
743 QTest::newRow("remove row -1 [no sorting/filter]")
751 << QString() // no filter
755 << (QStringList() // expectedProxy
761 << (QStringList() // expectedSource
768 QTest::newRow("remove three rows in the middle [no sorting/filter]")
776 << QString() // no filter
780 << (QStringList() // expectedProxy
783 << (QStringList() // expectedSource
787 QTest::newRow("remove one row in the middle [ascending sorting, no filter]")
794 << static_cast<int>(Qt::AscendingOrder)
795 << QString() // no filter
799 << (QStringList() // expectedProxy
804 << (QStringList() // expectedSource
810 QTest::newRow("remove two rows in the middle [ascending sorting, no filter]")
817 << static_cast<int>(Qt::AscendingOrder)
818 << QString() // no filter
822 << (QStringList() // expectedProxy
826 << (QStringList() // expectedSource
831 QTest::newRow("remove two rows in the middle [descending sorting, no filter]")
838 << static_cast<int>(Qt::DescendingOrder)
839 << QString() // no filter
843 << (QStringList() // expectedProxy
847 << (QStringList() // expectedSource
852 QTest::newRow("remove one row in the middle [no sorting, filter=5|2|3]")
864 << (QStringList() // expectedProxy
867 << (QStringList() // expectedSource
873 QTest::newRow("remove all [ascending sorting, no filter]")
880 << static_cast<int>(Qt::AscendingOrder)
881 << QString() // no filter
885 << QStringList() // expectedProxy
886 << QStringList(); // expectedSource
889 void tst_QSortFilterProxyModel::removeRows()
891 QFETCH(QStringList, initial);
892 QFETCH(int, sortOrder);
893 QFETCH(QString, filter);
894 QFETCH(int, position);
896 QFETCH(bool, success);
897 QFETCH(QStringList, expectedProxy);
898 QFETCH(QStringList, expectedSource);
900 QStandardItemModel model;
901 QSortFilterProxyModel proxy;
902 proxy.setSourceModel(&model);
905 foreach (QString s, initial)
906 model.appendRow(new QStandardItem(s));
909 proxy.sort(0, static_cast<Qt::SortOrder>(sortOrder));
910 if (!filter.isEmpty())
911 proxy.setFilterRegExp(QRegExp(filter));
914 QCOMPARE(proxy.removeRows(position, count, QModelIndex()), success);
915 QCOMPARE(model.rowCount(QModelIndex()), expectedSource.count());
916 QCOMPARE(proxy.rowCount(QModelIndex()), expectedProxy.count());
918 // make sure the model is correct after remove
919 for (int row = 0; row < model.rowCount(QModelIndex()); ++row)
920 QCOMPARE(model.item(row)->text(), expectedSource.at(row));
922 // make sure the proxy is correct after remove
923 for (int row = 0; row < proxy.rowCount(QModelIndex()); ++row) {
924 QModelIndex index = proxy.index(row, 0, QModelIndex());
925 QCOMPARE(proxy.data(index, Qt::DisplayRole).toString(), expectedProxy.at(row));
929 class MyFilteredColumnProxyModel : public QSortFilterProxyModel
933 MyFilteredColumnProxyModel(QObject *parent = 0)
934 : QSortFilterProxyModel(parent) { }
936 bool filterAcceptsColumn(int sourceColumn, const QModelIndex &) const
938 QString key = sourceModel()->headerData(sourceColumn, Qt::Horizontal).toString();
939 return key.contains(filterRegExp());
943 void tst_QSortFilterProxyModel::removeColumns_data()
945 QTest::addColumn<QStringList>("initial");
946 QTest::addColumn<QString>("filter");
947 QTest::addColumn<int>("position");
948 QTest::addColumn<int>("count");
949 QTest::addColumn<bool>("success");
950 QTest::addColumn<QStringList>("expectedProxy");
951 QTest::addColumn<QStringList>("expectedSource");
953 QTest::newRow("remove one column in the middle [no filter]")
960 << QString() // no filter
964 << (QStringList() // expectedProxy
969 << (QStringList() // expectedSource
975 QTest::newRow("remove one column in the end [no filter]")
982 << QString() // no filter
986 << (QStringList() // expectedProxy
991 << (QStringList() // expectedSource
997 QTest::newRow("remove one column past the end [no filter]")
1004 << QString() // no filter
1008 << (QStringList() // expectedProxy
1014 << (QStringList() // expectedSource
1021 QTest::newRow("remove column -1 [no filter]")
1028 << QString() // no filter
1032 << (QStringList() // expectedProxy
1038 << (QStringList() // expectedSource
1045 QTest::newRow("remove all columns [no filter]")
1052 << QString() // no filter
1056 << QStringList() // expectedProxy
1057 << QStringList(); // expectedSource
1059 QTest::newRow("remove one column in the middle [filter=1|3|5]")
1070 << (QStringList() // expectedProxy
1073 << (QStringList() // expectedSource
1079 QTest::newRow("remove one column in the end [filter=1|3|5]")
1090 << (QStringList() // expectedProxy
1093 << (QStringList() // expectedSource
1099 QTest::newRow("remove one column past the end [filter=1|3|5]")
1110 << (QStringList() // expectedProxy
1114 << (QStringList() // expectedSource
1121 QTest::newRow("remove all columns [filter=1|3|5]")
1132 << QStringList() // expectedProxy
1133 << (QStringList() // expectedSource
1138 void tst_QSortFilterProxyModel::removeColumns()
1140 QFETCH(QStringList, initial);
1141 QFETCH(QString, filter);
1142 QFETCH(int, position);
1144 QFETCH(bool, success);
1145 QFETCH(QStringList, expectedProxy);
1146 QFETCH(QStringList, expectedSource);
1148 QStandardItemModel model;
1149 MyFilteredColumnProxyModel proxy;
1150 proxy.setSourceModel(&model);
1151 if (!filter.isEmpty())
1152 proxy.setFilterRegExp(QRegExp(filter));
1155 model.setHorizontalHeaderLabels(initial);
1157 // remove the columns
1158 QCOMPARE(proxy.removeColumns(position, count, QModelIndex()), success);
1159 QCOMPARE(model.columnCount(QModelIndex()), expectedSource.count());
1160 QCOMPARE(proxy.columnCount(QModelIndex()), expectedProxy.count());
1162 // make sure the model is correct after remove
1163 for (int col = 0; col < model.columnCount(QModelIndex()); ++col)
1164 QCOMPARE(model.horizontalHeaderItem(col)->text(), expectedSource.at(col));
1166 // make sure the proxy is correct after remove
1167 for (int col = 0; col < proxy.columnCount(QModelIndex()); ++col) {
1168 QCOMPARE(proxy.headerData(col, Qt::Horizontal, Qt::DisplayRole).toString(),
1169 expectedProxy.at(col));
1173 void tst_QSortFilterProxyModel::filterColumns_data()
1175 QTest::addColumn<QString>("pattern");
1176 QTest::addColumn<QStringList>("initial");
1177 QTest::addColumn<bool>("data");
1179 QTest::newRow("all") << "a"
1187 QTest::newRow("some") << "lie"
1195 QTest::newRow("nothing") << "zoo"
1204 void tst_QSortFilterProxyModel::filterColumns()
1206 QFETCH(QString, pattern);
1207 QFETCH(QStringList, initial);
1210 m_model->setColumnCount(initial.count());
1211 m_model->setRowCount(1);
1212 QCOMPARE(m_model->columnCount(QModelIndex()), initial.count());
1213 QCOMPARE(m_model->rowCount(QModelIndex()), 1);
1215 QCOMPARE(m_model->rowCount(QModelIndex()), 1);
1216 for (int col = 0; col < m_model->columnCount(QModelIndex()); ++col) {
1217 QModelIndex index = m_model->index(0, col, QModelIndex());
1218 m_model->setData(index, initial.at(col), Qt::DisplayRole);
1220 m_proxy->setFilterRegExp(pattern);
1221 m_proxy->setFilterKeyColumn(-1);
1222 // make sure the model is unchanged
1223 for (int col = 0; col < m_model->columnCount(QModelIndex()); ++col) {
1224 QModelIndex index = m_model->index(0, col, QModelIndex());
1225 QCOMPARE(m_model->data(index, Qt::DisplayRole).toString(), initial.at(col));
1227 // make sure the proxy is filtered
1228 QModelIndex index = m_proxy->index(0, 0, QModelIndex());
1229 QCOMPARE(index.isValid(), data);
1232 void tst_QSortFilterProxyModel::filter_data()
1234 QTest::addColumn<QString>("pattern");
1235 QTest::addColumn<QStringList>("initial");
1236 QTest::addColumn<QStringList>("expected");
1238 QTest::newRow("flat") << "e"
1281 void tst_QSortFilterProxyModel::filter()
1283 QFETCH(QString, pattern);
1284 QFETCH(QStringList, initial);
1285 QFETCH(QStringList, expected);
1287 QVERIFY(m_model->insertRows(0, initial.count(), QModelIndex()));
1288 QCOMPARE(m_model->rowCount(QModelIndex()), initial.count());
1290 QCOMPARE(m_model->columnCount(QModelIndex()), 1);
1291 for (int row = 0; row < m_model->rowCount(QModelIndex()); ++row) {
1292 QModelIndex index = m_model->index(row, 0, QModelIndex());
1293 m_model->setData(index, initial.at(row), Qt::DisplayRole);
1295 m_proxy->setFilterRegExp(pattern);
1296 // make sure the proxy is unfiltered
1297 QCOMPARE(m_proxy->columnCount(QModelIndex()), 1);
1298 QCOMPARE(m_proxy->rowCount(QModelIndex()), expected.count());
1299 // make sure the model is unchanged
1300 for (int row = 0; row < m_model->rowCount(QModelIndex()); ++row) {
1301 QModelIndex index = m_model->index(row, 0, QModelIndex());
1302 QCOMPARE(m_model->data(index, Qt::DisplayRole).toString(), initial.at(row));
1304 // make sure the proxy is filtered
1305 for (int row = 0; row < m_proxy->rowCount(QModelIndex()); ++row) {
1306 QModelIndex index = m_proxy->index(row, 0, QModelIndex());
1307 QCOMPARE(m_proxy->data(index, Qt::DisplayRole).toString(), expected.at(row));
1311 void tst_QSortFilterProxyModel::filterHierarchy_data()
1313 QTest::addColumn<QString>("pattern");
1314 QTest::addColumn<QStringList>("initial");
1315 QTest::addColumn<QStringList>("expected");
1317 QTest::newRow("flat") << ".*oo"
1319 << "foo" << "boo" << "baz" << "moo" << "laa" << "haa")
1321 << "foo" << "boo" << "moo");
1323 QTest::newRow("simple hierarchy") << "b.*z"
1324 << (QStringList() << "baz" << "<" << "boz" << "<" << "moo" << ">" << ">")
1325 << (QStringList() << "baz" << "<" << "boz" << ">");
1328 void tst_QSortFilterProxyModel::filterHierarchy()
1330 QFETCH(QString, pattern);
1331 QFETCH(QStringList, initial);
1332 QFETCH(QStringList, expected);
1333 buildHierarchy(initial, m_model);
1334 m_proxy->setFilterRegExp(pattern);
1335 checkHierarchy(initial, m_model);
1336 checkHierarchy(expected, m_proxy);
1339 void tst_QSortFilterProxyModel::buildHierarchy(const QStringList &l, QAbstractItemModel *m)
1343 QStack<int> row_stack;
1345 QStack<QModelIndex> parent_stack;
1346 for (int i = 0; i < l.count(); ++i) {
1347 QString token = l.at(i);
1348 if (token == "<") { // start table
1350 parent_stack.push(parent);
1351 row_stack.push(row);
1352 parent = m->index(row - 1, 0, parent);
1354 QVERIFY(m->insertColumns(0, 1, parent)); // add column
1355 } else if (token == ">") { // end table
1357 parent = parent_stack.pop();
1358 row = row_stack.pop();
1359 } else { // append row
1360 QVERIFY(m->insertRows(row, 1, parent));
1361 QModelIndex index = m->index(row, 0, parent);
1362 QVERIFY(index.isValid());
1363 m->setData(index, token, Qt::DisplayRole);
1369 void tst_QSortFilterProxyModel::checkHierarchy(const QStringList &l, const QAbstractItemModel *m)
1373 QStack<int> row_stack;
1375 QStack<QModelIndex> parent_stack;
1376 for (int i = 0; i < l.count(); ++i) {
1377 QString token = l.at(i);
1378 if (token == "<") { // start table
1380 parent_stack.push(parent);
1381 row_stack.push(row);
1382 parent = m->index(row - 1, 0, parent);
1383 QVERIFY(parent.isValid());
1385 } else if (token == ">") { // end table
1387 parent = parent_stack.pop();
1388 row = row_stack.pop();
1389 } else { // compare row
1390 QModelIndex index = m->index(row, 0, parent);
1391 QVERIFY(index.isValid());
1392 QString str = m->data(index, Qt::DisplayRole).toString();
1393 QCOMPARE(str, token);
1399 class TestModel: public QAbstractTableModel
1402 int rowCount(const QModelIndex &) const { return 10000; }
1403 int columnCount(const QModelIndex &) const { return 1; }
1404 QVariant data(const QModelIndex &index, int role) const
1406 if (role != Qt::DisplayRole)
1408 return QString::number(index.row());
1412 void tst_QSortFilterProxyModel::filterTable()
1415 QSortFilterProxyModel filter;
1416 filter.setSourceModel(&model);
1417 filter.setFilterRegExp("9");
1419 for (int i = 0; i < filter.rowCount(); ++i)
1420 QVERIFY(filter.data(filter.index(i, 0)).toString().contains("9"));
1423 void tst_QSortFilterProxyModel::insertAfterSelect()
1425 QStandardItemModel model(10, 2);
1426 for (int i = 0; i<10;i++)
1427 model.setData(model.index(i, 0), QVariant(i));
1428 QSortFilterProxyModel filter;
1429 filter.setSourceModel(&model);
1431 view.setModel(&filter);
1433 QModelIndex firstIndex = filter.mapFromSource(model.index(0, 0, QModelIndex()));
1434 QCOMPARE(firstIndex.model(), (const QAbstractItemModel *)view.model());
1435 QVERIFY(firstIndex.isValid());
1436 int itemOffset = view.visualRect(firstIndex).width() / 2;
1437 QPoint p(itemOffset, 1);
1438 QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, p);
1439 QVERIFY(view.selectionModel()->selectedIndexes().size() > 0);
1440 model.insertRows(5, 1, QModelIndex());
1441 QVERIFY(view.selectionModel()->selectedIndexes().size() > 0); // Should still have a selection
1444 void tst_QSortFilterProxyModel::removeAfterSelect()
1446 QStandardItemModel model(10, 2);
1447 for (int i = 0; i<10;i++)
1448 model.setData(model.index(i, 0), QVariant(i));
1449 QSortFilterProxyModel filter;
1450 filter.setSourceModel(&model);
1452 view.setModel(&filter);
1454 QModelIndex firstIndex = filter.mapFromSource(model.index(0, 0, QModelIndex()));
1455 QCOMPARE(firstIndex.model(), (const QAbstractItemModel *)view.model());
1456 QVERIFY(firstIndex.isValid());
1457 int itemOffset = view.visualRect(firstIndex).width() / 2;
1458 QPoint p(itemOffset, 1);
1459 QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, p);
1460 QVERIFY(view.selectionModel()->selectedIndexes().size() > 0);
1461 model.removeRows(5, 1, QModelIndex());
1462 QVERIFY(view.selectionModel()->selectedIndexes().size() > 0); // Should still have a selection
1465 void tst_QSortFilterProxyModel::filterCurrent()
1467 QStandardItemModel model(2, 1);
1468 model.setData(model.index(0, 0), QString("AAA"));
1469 model.setData(model.index(1, 0), QString("BBB"));
1470 QSortFilterProxyModel proxy;
1471 proxy.setSourceModel(&model);
1475 view.setModel(&proxy);
1476 QSignalSpy spy(view.selectionModel(), SIGNAL(currentChanged(QModelIndex, QModelIndex)));
1477 QVERIFY(spy.isValid());
1479 view.setCurrentIndex(proxy.index(0, 0));
1480 QCOMPARE(spy.count(), 1);
1481 proxy.setFilterRegExp(QRegExp("^B"));
1482 QCOMPARE(spy.count(), 2);
1485 void tst_QSortFilterProxyModel::changeSourceLayout()
1487 QStandardItemModel model(2, 1);
1488 model.setData(model.index(0, 0), QString("b"));
1489 model.setData(model.index(1, 0), QString("a"));
1490 QSortFilterProxyModel proxy;
1491 proxy.setSourceModel(&model);
1493 QList<QPersistentModelIndex> persistentSourceIndexes;
1494 QList<QPersistentModelIndex> persistentProxyIndexes;
1495 for (int row = 0; row < model.rowCount(); ++row) {
1496 persistentSourceIndexes.append(model.index(row, 0));
1497 persistentProxyIndexes.append(proxy.index(row, 0));
1500 // change layout of source model
1501 model.sort(0, Qt::AscendingOrder);
1503 for (int row = 0; row < model.rowCount(); ++row) {
1504 QCOMPARE(persistentProxyIndexes.at(row).row(),
1505 persistentSourceIndexes.at(row).row());
1509 void tst_QSortFilterProxyModel::removeSourceRows_data()
1511 QTest::addColumn<QStringList>("sourceItems");
1512 QTest::addColumn<int>("start");
1513 QTest::addColumn<int>("count");
1514 QTest::addColumn<int>("sortOrder");
1515 QTest::addColumn<IntPairList>("expectedRemovedProxyIntervals");
1516 QTest::addColumn<QStringList>("expectedProxyItems");
1518 QTest::newRow("remove one, no sorting")
1519 << (QStringList() << "a" << "b") // sourceItems
1522 << -1 // sortOrder (no sorting)
1523 << (IntPairList() << IntPair(0, 0)) // expectedRemovedIntervals
1524 << (QStringList() << "b") // expectedProxyItems
1526 QTest::newRow("remove one, ascending sort (same order)")
1527 << (QStringList() << "a" << "b") // sourceItems
1530 << static_cast<int>(Qt::AscendingOrder) // sortOrder
1531 << (IntPairList() << IntPair(0, 0)) // expectedRemovedIntervals
1532 << (QStringList() << "b") // expectedProxyItems
1534 QTest::newRow("remove one, ascending sort (reverse order)")
1535 << (QStringList() << "b" << "a") // sourceItems
1538 << static_cast<int>(Qt::AscendingOrder) // sortOrder
1539 << (IntPairList() << IntPair(1, 1)) // expectedRemovedIntervals
1540 << (QStringList() << "a") // expectedProxyItems
1542 QTest::newRow("remove two, multiple proxy intervals")
1543 << (QStringList() << "c" << "d" << "a" << "b") // sourceItems
1546 << static_cast<int>(Qt::AscendingOrder) // sortOrder
1547 << (IntPairList() << IntPair(3, 3) << IntPair(0, 0)) // expectedRemovedIntervals
1548 << (QStringList() << "b" << "c") // expectedProxyItems
1550 QTest::newRow("remove three, multiple proxy intervals")
1551 << (QStringList() << "b" << "d" << "f" << "a" << "c" << "e") // sourceItems
1554 << static_cast<int>(Qt::AscendingOrder) // sortOrder
1555 << (IntPairList() << IntPair(4, 4) << IntPair(2, 2) << IntPair(0, 0)) // expectedRemovedIntervals
1556 << (QStringList() << "b" << "d" << "f") // expectedProxyItems
1558 QTest::newRow("remove all, single proxy intervals")
1559 << (QStringList() << "a" << "b" << "c" << "d" << "e" << "f") // sourceItems
1562 << static_cast<int>(Qt::DescendingOrder) // sortOrder
1563 << (IntPairList() << IntPair(0, 5)) // expectedRemovedIntervals
1564 << QStringList() // expectedProxyItems
1568 // Check that correct proxy model rows are removed when rows are removed
1569 // from the source model
1570 void tst_QSortFilterProxyModel::removeSourceRows()
1572 QFETCH(QStringList, sourceItems);
1575 QFETCH(int, sortOrder);
1576 QFETCH(IntPairList, expectedRemovedProxyIntervals);
1577 QFETCH(QStringList, expectedProxyItems);
1579 QStandardItemModel model;
1580 QSortFilterProxyModel proxy;
1582 proxy.setSourceModel(&model);
1583 model.insertColumns(0, 1);
1584 model.insertRows(0, sourceItems.count());
1586 for (int i = 0; i < sourceItems.count(); ++i) {
1587 QModelIndex sindex = model.index(i, 0, QModelIndex());
1588 model.setData(sindex, sourceItems.at(i), Qt::DisplayRole);
1589 QModelIndex pindex = proxy.index(i, 0, QModelIndex());
1590 QCOMPARE(proxy.data(pindex, Qt::DisplayRole), model.data(sindex, Qt::DisplayRole));
1593 if (sortOrder != -1)
1594 proxy.sort(0, static_cast<Qt::SortOrder>(sortOrder));
1595 (void)proxy.rowCount(QModelIndex()); // force mapping
1597 QSignalSpy removeSpy(&proxy, SIGNAL(rowsRemoved(QModelIndex, int, int)));
1598 QSignalSpy insertSpy(&proxy, SIGNAL(rowsInserted(QModelIndex, int, int)));
1599 QSignalSpy aboutToRemoveSpy(&proxy, SIGNAL(rowsAboutToBeRemoved(QModelIndex, int, int)));
1600 QSignalSpy aboutToInsertSpy(&proxy, SIGNAL(rowsAboutToBeInserted(QModelIndex, int, int)));
1602 QVERIFY(removeSpy.isValid());
1603 QVERIFY(insertSpy.isValid());
1604 QVERIFY(aboutToRemoveSpy.isValid());
1605 QVERIFY(aboutToInsertSpy.isValid());
1607 model.removeRows(start, count, QModelIndex());
1609 QCOMPARE(aboutToRemoveSpy.count(), expectedRemovedProxyIntervals.count());
1610 for (int i = 0; i < aboutToRemoveSpy.count(); ++i) {
1611 QList<QVariant> args = aboutToRemoveSpy.at(i);
1612 QVERIFY(args.at(1).type() == QVariant::Int);
1613 QVERIFY(args.at(2).type() == QVariant::Int);
1614 QCOMPARE(args.at(1).toInt(), expectedRemovedProxyIntervals.at(i).first);
1615 QCOMPARE(args.at(2).toInt(), expectedRemovedProxyIntervals.at(i).second);
1617 QCOMPARE(removeSpy.count(), expectedRemovedProxyIntervals.count());
1618 for (int i = 0; i < removeSpy.count(); ++i) {
1619 QList<QVariant> args = removeSpy.at(i);
1620 QVERIFY(args.at(1).type() == QVariant::Int);
1621 QVERIFY(args.at(2).type() == QVariant::Int);
1622 QCOMPARE(args.at(1).toInt(), expectedRemovedProxyIntervals.at(i).first);
1623 QCOMPARE(args.at(2).toInt(), expectedRemovedProxyIntervals.at(i).second);
1626 QCOMPARE(insertSpy.count(), 0);
1627 QCOMPARE(aboutToInsertSpy.count(), 0);
1629 QCOMPARE(proxy.rowCount(QModelIndex()), expectedProxyItems.count());
1630 for (int i = 0; i < expectedProxyItems.count(); ++i) {
1631 QModelIndex pindex = proxy.index(i, 0, QModelIndex());
1632 QCOMPARE(proxy.data(pindex, Qt::DisplayRole).toString(), expectedProxyItems.at(i));
1636 void tst_QSortFilterProxyModel::insertSourceRows_data()
1638 QTest::addColumn<QStringList>("sourceItems");
1639 QTest::addColumn<int>("start");
1640 QTest::addColumn<QStringList>("newItems");
1641 QTest::addColumn<int>("sortOrder");
1642 QTest::addColumn<QStringList>("proxyItems");
1644 QTest::newRow("insert (1)")
1645 << (QStringList() << "c" << "b") // sourceItems
1647 << (QStringList() << "a") // newItems
1648 << static_cast<int>(Qt::AscendingOrder) // sortOrder
1649 << (QStringList() << "a" << "b" << "c") // proxyItems
1652 QTest::newRow("insert (2)")
1653 << (QStringList() << "d" << "b" << "c") // sourceItems
1655 << (QStringList() << "a") // newItems
1656 << static_cast<int>(Qt::DescendingOrder) // sortOrder
1657 << (QStringList() << "d" << "c" << "b" << "a") // proxyItems
1661 // Check that rows are inserted at correct position in proxy model when
1662 // rows are inserted into the source model
1663 void tst_QSortFilterProxyModel::insertSourceRows()
1665 QFETCH(QStringList, sourceItems);
1667 QFETCH(QStringList, newItems);
1668 QFETCH(int, sortOrder);
1669 QFETCH(QStringList, proxyItems);
1671 QStandardItemModel model;
1672 QSortFilterProxyModel proxy;
1673 proxy.setDynamicSortFilter(true);
1675 proxy.setSourceModel(&model);
1676 model.insertColumns(0, 1);
1677 model.insertRows(0, sourceItems.count());
1679 for (int i = 0; i < sourceItems.count(); ++i) {
1680 QModelIndex index = model.index(i, 0, QModelIndex());
1681 model.setData(index, sourceItems.at(i), Qt::DisplayRole);
1684 proxy.sort(0, static_cast<Qt::SortOrder>(sortOrder));
1685 (void)proxy.rowCount(QModelIndex()); // force mapping
1687 model.insertRows(start, newItems.size(), QModelIndex());
1689 QCOMPARE(proxy.rowCount(QModelIndex()), proxyItems.count());
1690 for (int i = 0; i < newItems.count(); ++i) {
1691 QModelIndex index = model.index(start + i, 0, QModelIndex());
1692 model.setData(index, newItems.at(i), Qt::DisplayRole);
1695 for (int i = 0; i < proxyItems.count(); ++i) {
1696 QModelIndex index = proxy.index(i, 0, QModelIndex());
1697 QCOMPARE(proxy.data(index, Qt::DisplayRole).toString(), proxyItems.at(i));
1701 void tst_QSortFilterProxyModel::changeFilter_data()
1703 QTest::addColumn<QStringList>("sourceItems");
1704 QTest::addColumn<int>("sortOrder");
1705 QTest::addColumn<QString>("initialFilter");
1706 QTest::addColumn<IntPairList>("initialRemoveIntervals");
1707 QTest::addColumn<QStringList>("initialProxyItems");
1708 QTest::addColumn<QString>("finalFilter");
1709 QTest::addColumn<IntPairList>("finalRemoveIntervals");
1710 QTest::addColumn<IntPairList>("insertIntervals");
1711 QTest::addColumn<QStringList>("finalProxyItems");
1713 QTest::newRow("filter (1)")
1714 << (QStringList() << "a" << "b" << "c" << "d" << "e" << "f") // sourceItems
1715 << static_cast<int>(Qt::AscendingOrder) // sortOrder
1716 << "a|b|c" // initialFilter
1717 << (IntPairList() << IntPair(3, 5)) // initialRemoveIntervals
1718 << (QStringList() << "a" << "b" << "c") // initialProxyItems
1719 << "b|d|f" // finalFilter
1720 << (IntPairList() << IntPair(2, 2) << IntPair(0, 0)) // finalRemoveIntervals
1721 << (IntPairList() << IntPair(1, 2)) // insertIntervals
1722 << (QStringList() << "b" << "d" << "f") // finalProxyItems
1725 QTest::newRow("filter (2)")
1726 << (QStringList() << "a" << "b" << "c" << "d" << "e" << "f") // sourceItems
1727 << static_cast<int>(Qt::AscendingOrder) // sortOrder
1728 << "a|c|e" // initialFilter
1729 << (IntPairList() << IntPair(5, 5) << IntPair(3, 3) << IntPair(1, 1)) // initialRemoveIntervals
1730 << (QStringList() << "a" << "c" << "e") // initialProxyItems
1731 << "" // finalFilter
1732 << IntPairList() // finalRemoveIntervals
1733 << (IntPairList() << IntPair(3, 3) << IntPair(2, 2) << IntPair(1, 1)) // insertIntervals
1734 << (QStringList() << "a" << "b" << "c" << "d" << "e" << "f") // finalProxyItems
1737 QTest::newRow("filter (3)")
1738 << (QStringList() << "a" << "b" << "c") // sourceItems
1739 << static_cast<int>(Qt::AscendingOrder) // sortOrder
1740 << "a" // initialFilter
1741 << (IntPairList() << IntPair(1, 2)) // initialRemoveIntervals
1742 << (QStringList() << "a") // initialProxyItems
1743 << "a" // finalFilter
1744 << IntPairList() // finalRemoveIntervals
1745 << IntPairList() // insertIntervals
1746 << (QStringList() << "a") // finalProxyItems
1750 // Check that rows are added/removed when filter changes
1751 void tst_QSortFilterProxyModel::changeFilter()
1753 QFETCH(QStringList, sourceItems);
1754 QFETCH(int, sortOrder);
1755 QFETCH(QString, initialFilter);
1756 QFETCH(IntPairList, initialRemoveIntervals);
1757 QFETCH(QStringList, initialProxyItems);
1758 QFETCH(QString, finalFilter);
1759 QFETCH(IntPairList, finalRemoveIntervals);
1760 QFETCH(IntPairList, insertIntervals);
1761 QFETCH(QStringList, finalProxyItems);
1763 QStandardItemModel model;
1764 QSortFilterProxyModel proxy;
1766 proxy.setSourceModel(&model);
1767 model.insertColumns(0, 1);
1768 model.insertRows(0, sourceItems.count());
1770 for (int i = 0; i < sourceItems.count(); ++i) {
1771 QModelIndex index = model.index(i, 0, QModelIndex());
1772 model.setData(index, sourceItems.at(i), Qt::DisplayRole);
1775 proxy.sort(0, static_cast<Qt::SortOrder>(sortOrder));
1776 (void)proxy.rowCount(QModelIndex()); // force mapping
1778 QSignalSpy initialRemoveSpy(&proxy, SIGNAL(rowsRemoved(QModelIndex, int, int)));
1779 QSignalSpy initialInsertSpy(&proxy, SIGNAL(rowsInserted(QModelIndex, int, int)));
1781 QVERIFY(initialRemoveSpy.isValid());
1782 QVERIFY(initialInsertSpy.isValid());
1784 proxy.setFilterRegExp(initialFilter);
1786 QCOMPARE(initialRemoveSpy.count(), initialRemoveIntervals.count());
1787 QCOMPARE(initialInsertSpy.count(), 0);
1788 for (int i = 0; i < initialRemoveSpy.count(); ++i) {
1789 QList<QVariant> args = initialRemoveSpy.at(i);
1790 QVERIFY(args.at(1).type() == QVariant::Int);
1791 QVERIFY(args.at(2).type() == QVariant::Int);
1792 QCOMPARE(args.at(1).toInt(), initialRemoveIntervals.at(i).first);
1793 QCOMPARE(args.at(2).toInt(), initialRemoveIntervals.at(i).second);
1796 QCOMPARE(proxy.rowCount(QModelIndex()), initialProxyItems.count());
1797 for (int i = 0; i < initialProxyItems.count(); ++i) {
1798 QModelIndex index = proxy.index(i, 0, QModelIndex());
1799 QCOMPARE(proxy.data(index, Qt::DisplayRole).toString(), initialProxyItems.at(i));
1802 QSignalSpy finalRemoveSpy(&proxy, SIGNAL(rowsRemoved(QModelIndex, int, int)));
1803 QSignalSpy finalInsertSpy(&proxy, SIGNAL(rowsInserted(QModelIndex, int, int)));
1805 QVERIFY(finalRemoveSpy.isValid());
1806 QVERIFY(finalInsertSpy.isValid());
1808 proxy.setFilterRegExp(finalFilter);
1810 QCOMPARE(finalRemoveSpy.count(), finalRemoveIntervals.count());
1811 for (int i = 0; i < finalRemoveSpy.count(); ++i) {
1812 QList<QVariant> args = finalRemoveSpy.at(i);
1813 QVERIFY(args.at(1).type() == QVariant::Int);
1814 QVERIFY(args.at(2).type() == QVariant::Int);
1815 QCOMPARE(args.at(1).toInt(), finalRemoveIntervals.at(i).first);
1816 QCOMPARE(args.at(2).toInt(), finalRemoveIntervals.at(i).second);
1820 QEXPECT_FAIL("filter (2)", "Not reliable on IRIX", Abort);
1822 QCOMPARE(finalInsertSpy.count(), insertIntervals.count());
1823 for (int i = 0; i < finalInsertSpy.count(); ++i) {
1824 QList<QVariant> args = finalInsertSpy.at(i);
1825 QVERIFY(args.at(1).type() == QVariant::Int);
1826 QVERIFY(args.at(2).type() == QVariant::Int);
1827 QCOMPARE(args.at(1).toInt(), insertIntervals.at(i).first);
1828 QCOMPARE(args.at(2).toInt(), insertIntervals.at(i).second);
1831 QCOMPARE(proxy.rowCount(QModelIndex()), finalProxyItems.count());
1832 for (int i = 0; i < finalProxyItems.count(); ++i) {
1833 QModelIndex index = proxy.index(i, 0, QModelIndex());
1834 QCOMPARE(proxy.data(index, Qt::DisplayRole).toString(), finalProxyItems.at(i));
1838 void tst_QSortFilterProxyModel::changeSourceData_data()
1840 QTest::addColumn<QStringList>("sourceItems");
1841 QTest::addColumn<int>("sortOrder");
1842 QTest::addColumn<QString>("filter");
1843 QTest::addColumn<bool>("dynamic");
1844 QTest::addColumn<int>("row");
1845 QTest::addColumn<QString>("newValue");
1846 QTest::addColumn<IntPairList>("removeIntervals");
1847 QTest::addColumn<IntPairList>("insertIntervals");
1848 QTest::addColumn<QStringList>("proxyItems");
1850 QTest::newRow("changeSourceData (1)")
1851 << (QStringList() << "c" << "b" << "a") // sourceItems
1852 << static_cast<int>(Qt::AscendingOrder) // sortOrder
1857 << IntPairList() // removeIntervals
1858 << IntPairList() // insertIntervals
1859 << (QStringList() << "b" << "c" << "z") // proxyItems
1862 QTest::newRow("changeSourceData (2)")
1863 << (QStringList() << "b" << "c" << "z") // sourceItems
1864 << static_cast<int>(Qt::DescendingOrder) // sortOrder
1869 << IntPairList() // removeIntervals
1870 << IntPairList() // insertIntervals
1871 << (QStringList() << "z" << "b" << "a") // proxyItems
1874 QTest::newRow("changeSourceData (3)")
1875 << (QStringList() << "a" << "b") // sourceItems
1876 << static_cast<int>(Qt::DescendingOrder) // sortOrder
1881 << IntPairList() // removeIntervals
1882 << IntPairList() // insertIntervals
1883 << (QStringList() << "b" << "a") // proxyItems
1886 QTest::newRow("changeSourceData (4)")
1887 << (QStringList() << "a" << "b" << "c" << "d") // sourceItems
1888 << static_cast<int>(Qt::AscendingOrder) // sortOrder
1893 << IntPairList() // removeIntervals
1894 << IntPairList() // insertIntervals
1895 << (QStringList() << "a" << "c") // proxyItems
1898 QTest::newRow("changeSourceData (5)")
1899 << (QStringList() << "a" << "b" << "c" << "d") // sourceItems
1900 << static_cast<int>(Qt::AscendingOrder) // sortOrder
1901 << "a|c|x" // filter
1905 << IntPairList() // removeIntervals
1906 << (IntPairList() << IntPair(2, 2)) // insertIntervals
1907 << (QStringList() << "a" << "c" << "x") // proxyItems
1910 QTest::newRow("changeSourceData (6)")
1911 << (QStringList() << "c" << "b" << "a") // sourceItems
1912 << static_cast<int>(Qt::AscendingOrder) // sortOrder
1917 << IntPairList() // removeIntervals
1918 << IntPairList() // insertIntervals
1919 << (QStringList() << "x" << "b" << "c") // proxyItems
1923 void tst_QSortFilterProxyModel::changeSourceData()
1925 QFETCH(QStringList, sourceItems);
1926 QFETCH(int, sortOrder);
1927 QFETCH(QString, filter);
1928 QFETCH(bool, dynamic);
1930 QFETCH(QString, newValue);
1931 QFETCH(IntPairList, removeIntervals);
1932 QFETCH(IntPairList, insertIntervals);
1933 QFETCH(QStringList, proxyItems);
1935 QStandardItemModel model;
1936 QSortFilterProxyModel proxy;
1938 proxy.setDynamicSortFilter(dynamic);
1939 proxy.setSourceModel(&model);
1940 model.insertColumns(0, 1);
1941 model.insertRows(0, sourceItems.count());
1943 for (int i = 0; i < sourceItems.count(); ++i) {
1944 QModelIndex index = model.index(i, 0, QModelIndex());
1945 model.setData(index, sourceItems.at(i), Qt::DisplayRole);
1948 proxy.sort(0, static_cast<Qt::SortOrder>(sortOrder));
1949 (void)proxy.rowCount(QModelIndex()); // force mapping
1951 proxy.setFilterRegExp(filter);
1953 QSignalSpy removeSpy(&proxy, SIGNAL(rowsRemoved(QModelIndex, int, int)));
1954 QSignalSpy insertSpy(&proxy, SIGNAL(rowsInserted(QModelIndex, int, int)));
1956 QVERIFY(removeSpy.isValid());
1957 QVERIFY(insertSpy.isValid());
1960 QModelIndex index = model.index(row, 0, QModelIndex());
1961 model.setData(index, newValue, Qt::DisplayRole);
1964 QCOMPARE(removeSpy.count(), removeIntervals.count());
1965 for (int i = 0; i < removeSpy.count(); ++i) {
1966 QList<QVariant> args = removeSpy.at(i);
1967 QVERIFY(args.at(1).type() == QVariant::Int);
1968 QVERIFY(args.at(2).type() == QVariant::Int);
1969 QCOMPARE(args.at(1).toInt(), removeIntervals.at(i).first);
1970 QCOMPARE(args.at(2).toInt(), removeIntervals.at(i).second);
1973 QCOMPARE(insertSpy.count(), insertIntervals.count());
1974 for (int i = 0; i < insertSpy.count(); ++i) {
1975 QList<QVariant> args = insertSpy.at(i);
1976 QVERIFY(args.at(1).type() == QVariant::Int);
1977 QVERIFY(args.at(2).type() == QVariant::Int);
1978 QCOMPARE(args.at(1).toInt(), insertIntervals.at(i).first);
1979 QCOMPARE(args.at(2).toInt(), insertIntervals.at(i).second);
1982 QCOMPARE(proxy.rowCount(QModelIndex()), proxyItems.count());
1983 for (int i = 0; i < proxyItems.count(); ++i) {
1984 QModelIndex index = proxy.index(i, 0, QModelIndex());
1985 QCOMPARE(proxy.data(index, Qt::DisplayRole).toString(), proxyItems.at(i));
1989 void tst_QSortFilterProxyModel::sortFilterRole()
1991 QStandardItemModel model;
1992 QSortFilterProxyModel proxy;
1993 proxy.setSourceModel(&model);
1994 model.insertColumns(0, 1);
1996 QList<QPair<QVariant, QVariant> > sourceItems;
1997 sourceItems = QList<QPair<QVariant, QVariant> >()
1998 << QPair<QVariant, QVariant>("b", 3)
1999 << QPair<QVariant, QVariant>("c", 2)
2000 << QPair<QVariant, QVariant>("a", 1);
2002 QList<int> orderedItems;
2003 orderedItems = QList<int>()
2006 model.insertRows(0, sourceItems.count());
2007 for (int i = 0; i < sourceItems.count(); ++i) {
2008 QModelIndex index = model.index(i, 0, QModelIndex());
2009 model.setData(index, sourceItems.at(i).first, Qt::DisplayRole);
2010 model.setData(index, sourceItems.at(i).second, Qt::UserRole);
2013 proxy.setFilterRegExp("2");
2014 QCOMPARE(proxy.rowCount(), 0); // Qt::DisplayRole is default role
2016 proxy.setFilterRole(Qt::UserRole);
2017 QCOMPARE(proxy.rowCount(), 1);
2019 proxy.setFilterRole(Qt::DisplayRole);
2020 QCOMPARE(proxy.rowCount(), 0);
2022 proxy.setFilterRegExp("1|2|3");
2023 QCOMPARE(proxy.rowCount(), 0);
2025 proxy.setFilterRole(Qt::UserRole);
2026 QCOMPARE(proxy.rowCount(), 3);
2028 proxy.sort(0, Qt::AscendingOrder);
2029 QCOMPARE(proxy.rowCount(), 3);
2031 proxy.setSortRole(Qt::UserRole);
2032 proxy.setFilterRole(Qt::DisplayRole);
2033 proxy.setFilterRegExp("a|c");
2034 QCOMPARE(proxy.rowCount(), orderedItems.count());
2035 for (int i = 0; i < proxy.rowCount(); ++i) {
2036 QModelIndex index = proxy.index(i, 0, QModelIndex());
2037 QCOMPARE(proxy.data(index, Qt::DisplayRole), sourceItems.at(orderedItems.at(i)).first);
2041 void tst_QSortFilterProxyModel::selectionFilteredOut()
2043 QStandardItemModel model(2, 1);
2044 model.setData(model.index(0, 0), QString("AAA"));
2045 model.setData(model.index(1, 0), QString("BBB"));
2046 QSortFilterProxyModel proxy;
2047 proxy.setSourceModel(&model);
2051 view.setModel(&proxy);
2052 QSignalSpy spy(view.selectionModel(), SIGNAL(currentChanged(QModelIndex, QModelIndex)));
2053 QVERIFY(spy.isValid());
2055 view.setCurrentIndex(proxy.index(0, 0));
2056 QCOMPARE(spy.count(), 1);
2057 proxy.setFilterRegExp(QRegExp("^B"));
2058 QCOMPARE(spy.count(), 2);
2061 void tst_QSortFilterProxyModel::match_data()
2063 QTest::addColumn<QStringList>("sourceItems");
2064 QTest::addColumn<int>("sortOrder");
2065 QTest::addColumn<QString>("filter");
2066 QTest::addColumn<int>("proxyStartRow");
2067 QTest::addColumn<QString>("what");
2068 QTest::addColumn<int>("matchFlags");
2069 QTest::addColumn<IntList>("expectedProxyItems");
2071 << (QStringList() << "a") // sourceItems
2072 << static_cast<int>(Qt::AscendingOrder) // sortOrder
2074 << 0 // proxyStartRow
2076 << static_cast<int>(Qt::MatchExactly) // matchFlags
2077 << (IntList() << 0); // expectedProxyItems
2079 << (QStringList() << "a" << "b") // sourceItems
2080 << static_cast<int>(Qt::AscendingOrder) // sortOrder
2082 << 0 // proxyStartRow
2084 << static_cast<int>(Qt::MatchExactly) // matchFlags
2085 << (IntList() << 1); // expectedProxyItems
2087 << (QStringList() << "a" << "b") // sourceItems
2088 << static_cast<int>(Qt::DescendingOrder) // sortOrder
2090 << 0 // proxyStartRow
2092 << static_cast<int>(Qt::MatchExactly) // matchFlags
2093 << (IntList() << 1); // expectedProxyItems
2095 << (QStringList() << "b" << "d" << "a" << "c") // sourceItems
2096 << static_cast<int>(Qt::AscendingOrder) // sortOrder
2098 << 1 // proxyStartRow
2100 << static_cast<int>(Qt::MatchExactly) // matchFlags
2101 << IntList(); // expectedProxyItems
2103 << (QStringList() << "b" << "d" << "a" << "c") // sourceItems
2104 << static_cast<int>(Qt::AscendingOrder) // sortOrder
2106 << 0 // proxyStartRow
2108 << static_cast<int>(Qt::MatchExactly) // matchFlags
2109 << IntList(); // expectedProxyItems
2111 << (QStringList() << "b" << "d" << "a" << "c") // sourceItems
2112 << static_cast<int>(Qt::DescendingOrder) // sortOrder
2114 << 0 // proxyStartRow
2116 << static_cast<int>(Qt::MatchExactly) // matchFlags
2117 << (IntList() << 0); // expectedProxyItems
2120 void tst_QSortFilterProxyModel::match()
2122 QFETCH(QStringList, sourceItems);
2123 QFETCH(int, sortOrder);
2124 QFETCH(QString, filter);
2125 QFETCH(int, proxyStartRow);
2126 QFETCH(QString, what);
2127 QFETCH(int, matchFlags);
2128 QFETCH(IntList, expectedProxyItems);
2130 QStandardItemModel model;
2131 QSortFilterProxyModel proxy;
2133 proxy.setSourceModel(&model);
2134 model.insertColumns(0, 1);
2135 model.insertRows(0, sourceItems.count());
2137 for (int i = 0; i < sourceItems.count(); ++i) {
2138 QModelIndex index = model.index(i, 0, QModelIndex());
2139 model.setData(index, sourceItems.at(i), Qt::DisplayRole);
2142 proxy.sort(0, static_cast<Qt::SortOrder>(sortOrder));
2143 proxy.setFilterRegExp(filter);
2145 QModelIndex startIndex = proxy.index(proxyStartRow, 0);
2146 QModelIndexList indexes = proxy.match(startIndex, Qt::DisplayRole, what,
2147 expectedProxyItems.count(),
2148 Qt::MatchFlags(matchFlags));
2149 QCOMPARE(indexes.count(), expectedProxyItems.count());
2150 for (int i = 0; i < indexes.count(); ++i)
2151 QCOMPARE(indexes.at(i).row(), expectedProxyItems.at(i));
2154 void tst_QSortFilterProxyModel::insertIntoChildrenlessItem()
2156 QStandardItemModel model;
2157 QStandardItem *itemA = new QStandardItem("a");
2158 model.appendRow(itemA);
2159 QStandardItem *itemB = new QStandardItem("b");
2160 model.appendRow(itemB);
2161 QStandardItem *itemC = new QStandardItem("c");
2162 model.appendRow(itemC);
2164 QSortFilterProxyModel proxy;
2165 proxy.setSourceModel(&model);
2167 QSignalSpy colsInsertedSpy(&proxy, SIGNAL(columnsInserted(const QModelIndex&, int, int)));
2168 QSignalSpy rowsInsertedSpy(&proxy, SIGNAL(rowsInserted(const QModelIndex&, int, int)));
2170 QVERIFY(colsInsertedSpy.isValid());
2171 QVERIFY(rowsInsertedSpy.isValid());
2173 (void)proxy.rowCount(QModelIndex()); // force mapping of "a", "b", "c"
2174 QCOMPARE(colsInsertedSpy.count(), 0);
2175 QCOMPARE(rowsInsertedSpy.count(), 0);
2177 // now add a child to itemB ==> should get insert notification from the proxy
2178 itemB->appendRow(new QStandardItem("a.0"));
2179 QCOMPARE(colsInsertedSpy.count(), 1);
2180 QCOMPARE(rowsInsertedSpy.count(), 1);
2182 QVariantList args = colsInsertedSpy.takeFirst();
2183 QCOMPARE(qvariant_cast<QModelIndex>(args.at(0)), proxy.mapFromSource(itemB->index()));
2184 QCOMPARE(qvariant_cast<int>(args.at(1)), 0);
2185 QCOMPARE(qvariant_cast<int>(args.at(2)), 0);
2187 args = rowsInsertedSpy.takeFirst();
2188 QCOMPARE(qvariant_cast<QModelIndex>(args.at(0)), proxy.mapFromSource(itemB->index()));
2189 QCOMPARE(qvariant_cast<int>(args.at(1)), 0);
2190 QCOMPARE(qvariant_cast<int>(args.at(2)), 0);
2193 void tst_QSortFilterProxyModel::invalidateMappedChildren()
2195 QStandardItemModel model;
2197 QSortFilterProxyModel proxy;
2198 proxy.setSourceModel(&model);
2200 QStandardItem *itemA = new QStandardItem("a");
2201 model.appendRow(itemA);
2202 QStandardItem *itemB = new QStandardItem("b");
2203 itemA->appendRow(itemB);
2205 QStandardItem *itemC = new QStandardItem("c");
2206 itemB->appendRow(itemC);
2207 itemC->appendRow(new QStandardItem("d"));
2210 (void)proxy.hasChildren(QModelIndex());
2211 (void)proxy.hasChildren(proxy.mapFromSource(itemA->index()));
2212 (void)proxy.hasChildren(proxy.mapFromSource(itemB->index()));
2213 (void)proxy.hasChildren(proxy.mapFromSource(itemC->index()));
2215 itemB->removeRow(0); // should invalidate mapping of itemC
2216 itemC = new QStandardItem("c");
2217 itemA->appendRow(itemC);
2218 itemC->appendRow(new QStandardItem("d"));
2220 itemA->removeRow(1); // should invalidate mapping of itemC
2221 itemC = new QStandardItem("c");
2222 itemB->appendRow(itemC);
2223 itemC->appendRow(new QStandardItem("d"));
2225 QCOMPARE(proxy.rowCount(proxy.mapFromSource(itemA->index())), 1);
2226 QCOMPARE(proxy.rowCount(proxy.mapFromSource(itemB->index())), 1);
2227 QCOMPARE(proxy.rowCount(proxy.mapFromSource(itemC->index())), 1);
2230 class EvenOddFilterModel : public QSortFilterProxyModel
2233 virtual bool filterAcceptsRow(int srcRow, const QModelIndex& srcParent) const
2235 if (srcParent.isValid())
2236 return (srcParent.row() % 2) ^ !(srcRow % 2);
2237 return (srcRow % 2);
2241 void tst_QSortFilterProxyModel::insertRowIntoFilteredParent()
2243 QStandardItemModel model;
2244 EvenOddFilterModel proxy;
2245 proxy.setSourceModel(&model);
2247 QSignalSpy spy(&proxy, SIGNAL(rowsInserted(const QModelIndex&, int, int)));
2248 QVERIFY(spy.isValid());
2250 QStandardItem *itemA = new QStandardItem();
2251 model.appendRow(itemA); // A will be filtered
2252 QStandardItem *itemB = new QStandardItem();
2253 itemA->appendRow(itemB);
2255 QCOMPARE(spy.count(), 0);
2257 itemA->removeRow(0);
2259 QCOMPARE(spy.count(), 0);
2262 void tst_QSortFilterProxyModel::filterOutParentAndFilterInChild()
2264 QStandardItemModel model;
2265 QSortFilterProxyModel proxy;
2266 proxy.setSourceModel(&model);
2268 proxy.setFilterRegExp("A|B");
2269 QStandardItem *itemA = new QStandardItem("A");
2270 model.appendRow(itemA); // not filtered
2271 QStandardItem *itemB = new QStandardItem("B");
2272 itemA->appendRow(itemB); // not filtered
2273 QStandardItem *itemC = new QStandardItem("C");
2274 itemA->appendRow(itemC); // filtered
2276 QSignalSpy removedSpy(&proxy, SIGNAL(rowsRemoved(const QModelIndex&, int, int)));
2277 QSignalSpy insertedSpy(&proxy, SIGNAL(rowsInserted(const QModelIndex&, int, int)));
2279 QVERIFY(removedSpy.isValid());
2280 QVERIFY(insertedSpy.isValid());
2282 proxy.setFilterRegExp("C"); // A and B will be filtered out, C filtered in
2284 // we should now have been notified that the subtree represented by itemA has been removed
2285 QCOMPARE(removedSpy.count(), 1);
2286 // we should NOT get any inserts; itemC should be filtered because its parent (itemA) is
2287 QCOMPARE(insertedSpy.count(), 0);
2290 void tst_QSortFilterProxyModel::sourceInsertRows()
2292 QStandardItemModel model;
2293 QSortFilterProxyModel proxyModel;
2294 proxyModel.setSourceModel(&model);
2296 model.insertColumns(0, 1, QModelIndex());
2297 model.insertRows(0, 2, QModelIndex());
2300 QModelIndex parent = model.index(0, 0, QModelIndex());
2301 model.insertColumns(0, 1, parent);
2302 model.insertRows(0, 1, parent);
2306 QModelIndex parent = model.index(1, 0, QModelIndex());
2307 model.insertColumns(0, 1, parent);
2308 model.insertRows(0, 1, parent);
2311 model.insertRows(0, 1, QModelIndex());
2312 model.insertRows(0, 1, QModelIndex());
2314 QVERIFY(true); // if you got here without asserting, it's all good
2317 void tst_QSortFilterProxyModel::sourceModelDeletion()
2319 QSortFilterProxyModel proxyModel;
2321 QStandardItemModel model;
2322 proxyModel.setSourceModel(&model);
2323 QCOMPARE(proxyModel.sourceModel(), static_cast<QAbstractItemModel*>(&model));
2325 QCOMPARE(proxyModel.sourceModel(), static_cast<QAbstractItemModel*>(0));
2328 void tst_QSortFilterProxyModel::sortColumnTracking1()
2330 QStandardItemModel model;
2331 QSortFilterProxyModel proxyModel;
2332 proxyModel.setSourceModel(&model);
2334 model.insertColumns(0, 10);
2335 model.insertRows(0, 10);
2338 QCOMPARE(proxyModel.sortColumn(), 1);
2340 model.insertColumn(8);
2341 QCOMPARE(proxyModel.sortColumn(), 1);
2343 model.removeColumn(8);
2344 QCOMPARE(proxyModel.sortColumn(), 1);
2346 model.insertColumn(2);
2347 QCOMPARE(proxyModel.sortColumn(), 1);
2349 model.removeColumn(2);
2350 QCOMPARE(proxyModel.sortColumn(), 1);
2352 model.insertColumn(1);
2353 QCOMPARE(proxyModel.sortColumn(), 2);
2355 model.removeColumn(1);
2356 QCOMPARE(proxyModel.sortColumn(), 1);
2358 model.removeColumn(1);
2359 QCOMPARE(proxyModel.sortColumn(), -1);
2362 void tst_QSortFilterProxyModel::sortColumnTracking2()
2364 QStandardItemModel model;
2365 QSortFilterProxyModel proxyModel;
2366 proxyModel.setDynamicSortFilter(true);
2367 proxyModel.setSourceModel(&model);
2370 QCOMPARE(proxyModel.sortColumn(), 0);
2372 QList<QStandardItem *> items;
2373 QStringList strings;
2374 strings << "foo" << "bar" << "some" << "others" << "item" << "aa" << "zz";
2375 foreach (QString s, strings)
2376 items << new QStandardItem(s);
2378 model.insertColumn(0,items);
2379 QCOMPARE(proxyModel.sortColumn(), 0);
2380 QCOMPARE(proxyModel.data(proxyModel.index(0,0)).toString(),QString::fromLatin1("aa"));
2381 QCOMPARE(proxyModel.data(proxyModel.index(strings.count()-1,0)).toString(),QString::fromLatin1("zz"));
2384 void tst_QSortFilterProxyModel::sortStable()
2386 QStandardItemModel* model = new QStandardItemModel(5, 2);
2387 for (int r = 0; r < 5; r++) {
2388 for (int c = 0; c < 2; c++) {
2389 QStandardItem* item = new QStandardItem(
2390 QString("Row:%0, Column:%1").arg(r).arg(c) );
2391 for (int i = 0; i < 3; i++) {
2392 QStandardItem* child = new QStandardItem(
2393 QString("Item %0").arg(i) );
2394 item->appendRow( child );
2396 model->setItem(r, c, item);
2399 model->setHorizontalHeaderItem( 0, new QStandardItem( "Name" ));
2400 model->setHorizontalHeaderItem( 1, new QStandardItem( "Value" ));
2402 QSortFilterProxyModel *filterModel = new QSortFilterProxyModel(model);
2403 filterModel->setSourceModel(model);
2405 QTreeView *view = new QTreeView;
2406 view->setModel(filterModel);
2407 QModelIndex firstRoot = filterModel->index(0,0);
2408 view->expand(firstRoot);
2409 view->setSortingEnabled(true);
2411 view->model()->sort(1, Qt::DescendingOrder);
2412 QVariant lastItemData =filterModel->index(2,0, firstRoot).data();
2413 view->model()->sort(1, Qt::DescendingOrder);
2414 QCOMPARE(lastItemData, filterModel->index(2,0, firstRoot).data());
2417 void tst_QSortFilterProxyModel::hiddenColumns()
2419 class MyStandardItemModel : public QStandardItemModel
2422 MyStandardItemModel() : QStandardItemModel(0,5) {}
2424 { QStandardItemModel::reset(); }
2425 friend class tst_QSortFilterProxyModel;
2427 QSortFilterProxyModel proxy;
2428 proxy.setSourceModel(&model);
2431 view.setModel(&proxy);
2435 QVERIFY(view.isColumnHidden(0));
2436 model.blockSignals(true);
2437 model.setRowCount(1);
2438 model.blockSignals(false);
2441 // In the initial bug report that spawned this test, this would be false
2442 // because resetting model would also reset the hidden columns.
2443 QVERIFY(view.isColumnHidden(0));
2446 void tst_QSortFilterProxyModel::insertRowsSort()
2448 QStandardItemModel model(2,2);
2449 QSortFilterProxyModel proxyModel;
2450 proxyModel.setSourceModel(&model);
2453 QCOMPARE(proxyModel.sortColumn(), 0);
2455 model.insertColumns(0, 3, model.index(0,0));
2456 QCOMPARE(proxyModel.sortColumn(), 0);
2458 model.removeColumns(0, 3, model.index(0,0));
2459 QCOMPARE(proxyModel.sortColumn(), 0);
2462 void tst_QSortFilterProxyModel::staticSorting()
2464 QStandardItemModel model(0, 1);
2465 QSortFilterProxyModel proxy;
2466 proxy.setSourceModel(&model);
2467 proxy.setDynamicSortFilter(false);
2468 QStringList initial = QString("bateau avion dragon hirondelle flamme camion elephant").split(" ");
2471 QStandardItem *root = model.invisibleRootItem ();
2472 QList<QStandardItem *> items;
2473 for (int i = 0; i < initial.count(); ++i) {
2474 items.append(new QStandardItem(initial.at(i)));
2476 root->insertRows(0, items);
2477 QCOMPARE(model.rowCount(QModelIndex()), initial.count());
2478 QCOMPARE(model.columnCount(QModelIndex()), 1);
2480 // make sure the proxy is unsorted
2481 QCOMPARE(proxy.columnCount(QModelIndex()), 1);
2482 QCOMPARE(proxy.rowCount(QModelIndex()), initial.count());
2483 for (int row = 0; row < proxy.rowCount(QModelIndex()); ++row) {
2484 QModelIndex index = proxy.index(row, 0, QModelIndex());
2485 QCOMPARE(proxy.data(index, Qt::DisplayRole).toString(), initial.at(row));
2491 QStringList expected = initial;
2493 // make sure the proxy is sorted
2494 for (int row = 0; row < proxy.rowCount(QModelIndex()); ++row) {
2495 QModelIndex index = proxy.index(row, 0, QModelIndex());
2496 QCOMPARE(proxy.data(index, Qt::DisplayRole).toString(), expected.at(row));
2500 items.first()->setData("girafe", Qt::DisplayRole);
2502 // make sure the proxy is updated but not sorted
2503 expected.replaceInStrings("bateau", "girafe");
2504 for (int row = 0; row < proxy.rowCount(QModelIndex()); ++row) {
2505 QModelIndex index = proxy.index(row, 0, QModelIndex());
2506 QCOMPARE(proxy.data(index, Qt::DisplayRole).toString(), expected.at(row));
2513 // make sure the proxy is sorted
2514 for (int row = 0; row < proxy.rowCount(QModelIndex()); ++row) {
2515 QModelIndex index = proxy.index(row, 0, QModelIndex());
2516 QCOMPARE(proxy.data(index, Qt::DisplayRole).toString(), expected.at(row));
2520 void tst_QSortFilterProxyModel::dynamicSorting()
2522 QStringListModel model1;
2523 const QStringList initial = QString("bateau avion dragon hirondelle flamme camion elephant").split(" ");
2524 model1.setStringList(initial);
2525 QSortFilterProxyModel proxy1;
2526 proxy1.setDynamicSortFilter(false);
2528 proxy1.setSourceModel(&model1);
2530 QCOMPARE(proxy1.columnCount(QModelIndex()), 1);
2531 //the model should not be sorted because sorting has not been set to dynamic yet.
2532 QCOMPARE(proxy1.rowCount(QModelIndex()), initial.count());
2533 for (int row = 0; row < proxy1.rowCount(QModelIndex()); ++row) {
2534 QModelIndex index = proxy1.index(row, 0, QModelIndex());
2535 QCOMPARE(proxy1.data(index, Qt::DisplayRole).toString(), initial.at(row));
2538 proxy1.setDynamicSortFilter(true);
2540 //now the model should be sorted.
2541 QStringList expected = initial;
2543 for (int row = 0; row < proxy1.rowCount(QModelIndex()); ++row) {
2544 QModelIndex index = proxy1.index(row, 0, QModelIndex());
2545 QCOMPARE(proxy1.data(index, Qt::DisplayRole).toString(), expected.at(row));
2548 QStringList initial2 = initial;
2549 initial2.replaceInStrings("bateau", "girafe");
2550 model1.setStringList(initial2); //this will cause a reset
2552 QStringList expected2 = initial2;
2555 //now the model should still be sorted.
2556 for (int row = 0; row < proxy1.rowCount(QModelIndex()); ++row) {
2557 QModelIndex index = proxy1.index(row, 0, QModelIndex());
2558 QCOMPARE(proxy1.data(index, Qt::DisplayRole).toString(), expected2.at(row));
2561 QStringListModel model2(initial);
2562 proxy1.setSourceModel(&model2);
2564 //the model should again be sorted
2565 for (int row = 0; row < proxy1.rowCount(QModelIndex()); ++row) {
2566 QModelIndex index = proxy1.index(row, 0, QModelIndex());
2567 QCOMPARE(proxy1.data(index, Qt::DisplayRole).toString(), expected.at(row));
2570 //set up the sorting before seting the model up
2571 QSortFilterProxyModel proxy2;
2573 proxy2.setSourceModel(&model2);
2574 for (int row = 0; row < proxy2.rowCount(QModelIndex()); ++row) {
2575 QModelIndex index = proxy2.index(row, 0, QModelIndex());
2576 QCOMPARE(proxy2.data(index, Qt::DisplayRole).toString(), expected.at(row));
2580 class QtTestModel: public QAbstractItemModel
2583 QtTestModel(int _rows, int _cols, QObject *parent = 0)
2584 : QAbstractItemModel(parent)
2591 bool canFetchMore(const QModelIndex &idx) const
2593 return !fetched.contains(idx);
2596 void fetchMore(const QModelIndex &idx)
2598 if (fetched.contains(idx))
2600 beginInsertRows(idx, 0, rows-1);
2601 fetched.insert(idx);
2605 bool hasChildren(const QModelIndex & = QModelIndex()) const
2610 int rowCount(const QModelIndex& parent = QModelIndex()) const
2612 return fetched.contains(parent) ? rows : 0;
2615 int columnCount(const QModelIndex& parent = QModelIndex()) const
2621 QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const
2623 if (row < 0 || column < 0 || column >= cols || row >= rows) {
2624 return QModelIndex();
2626 QModelIndex i = createIndex(row, column, int(parent.internalId() + 1));
2627 parentHash[i] = parent;
2631 QModelIndex parent(const QModelIndex &index) const
2633 if (!parentHash.contains(index))
2634 return QModelIndex();
2635 return parentHash[index];
2638 QVariant data(const QModelIndex &idx, int role) const
2643 if (role == Qt::DisplayRole) {
2644 if (idx.row() < 0 || idx.column() < 0 || idx.column() >= cols || idx.row() >= rows) {
2646 qWarning("Invalid modelIndex [%d,%d,%p]", idx.row(), idx.column(),
2647 idx.internalPointer());
2649 return QString("[%1,%2]").arg(idx.row()).arg(idx.column());
2654 QSet<QModelIndex> fetched;
2656 mutable bool wrongIndex;
2657 mutable QMap<QModelIndex,QModelIndex> parentHash;
2660 void tst_QSortFilterProxyModel::fetchMore()
2662 QtTestModel model(10,10);
2663 QSortFilterProxyModel proxy;
2664 proxy.setSourceModel(&model);
2665 QVERIFY(proxy.canFetchMore(QModelIndex()));
2666 QVERIFY(proxy.hasChildren());
2667 while (proxy.canFetchMore(QModelIndex()))
2668 proxy.fetchMore(QModelIndex());
2669 QCOMPARE(proxy.rowCount(), 10);
2670 QCOMPARE(proxy.columnCount(), 10);
2672 QModelIndex idx = proxy.index(1,1);
2673 QVERIFY(idx.isValid());
2674 QVERIFY(proxy.canFetchMore(idx));
2675 QVERIFY(proxy.hasChildren(idx));
2676 while (proxy.canFetchMore(idx))
2677 proxy.fetchMore(idx);
2678 QCOMPARE(proxy.rowCount(idx), 10);
2679 QCOMPARE(proxy.columnCount(idx), 10);
2682 void tst_QSortFilterProxyModel::hiddenChildren()
2684 QStandardItemModel model;
2685 QSortFilterProxyModel proxy;
2686 proxy.setSourceModel(&model);
2687 proxy.setDynamicSortFilter(true);
2689 QStandardItem *itemA = new QStandardItem("A VISIBLE");
2690 model.appendRow(itemA);
2691 QStandardItem *itemB = new QStandardItem("B VISIBLE");
2692 itemA->appendRow(itemB);
2693 QStandardItem *itemC = new QStandardItem("C");
2694 itemA->appendRow(itemC);
2695 proxy.setFilterRegExp("VISIBLE");
2697 QCOMPARE(proxy.rowCount(QModelIndex()) , 1);
2698 QPersistentModelIndex indexA = proxy.index(0,0);
2699 QCOMPARE(proxy.data(indexA).toString(), QString::fromLatin1("A VISIBLE"));
2701 QCOMPARE(proxy.rowCount(indexA) , 1);
2702 QPersistentModelIndex indexB = proxy.index(0, 0, indexA);
2703 QCOMPARE(proxy.data(indexB).toString(), QString::fromLatin1("B VISIBLE"));
2705 itemA->setText("A");
2706 QCOMPARE(proxy.rowCount(QModelIndex()), 0);
2707 QVERIFY(!indexA.isValid());
2708 QVERIFY(!indexB.isValid());
2710 itemB->setText("B");
2711 itemA->setText("A VISIBLE");
2712 itemC->setText("C VISIBLE");
2714 QCOMPARE(proxy.rowCount(QModelIndex()), 1);
2715 indexA = proxy.index(0,0);
2716 QCOMPARE(proxy.data(indexA).toString(), QString::fromLatin1("A VISIBLE"));
2718 QCOMPARE(proxy.rowCount(indexA) , 1);
2719 QModelIndex indexC = proxy.index(0, 0, indexA);
2720 QCOMPARE(proxy.data(indexC).toString(), QString::fromLatin1("C VISIBLE"));
2722 proxy.setFilterRegExp("C");
2723 QCOMPARE(proxy.rowCount(QModelIndex()), 0);
2724 itemC->setText("invisible");
2725 itemA->setText("AC");
2727 QCOMPARE(proxy.rowCount(QModelIndex()), 1);
2728 indexA = proxy.index(0,0);
2729 QCOMPARE(proxy.data(indexA).toString(), QString::fromLatin1("AC"));
2730 QCOMPARE(proxy.rowCount(indexA) , 0);
2733 void tst_QSortFilterProxyModel::mapFromToSource()
2735 QtTestModel source(10,10);
2736 source.fetchMore(QModelIndex());
2737 QSortFilterProxyModel proxy;
2738 proxy.setSourceModel(&source);
2739 QCOMPARE(proxy.mapFromSource(source.index(5, 4)), proxy.index(5, 4));
2740 QCOMPARE(proxy.mapToSource(proxy.index(3, 2)), source.index(3, 2));
2741 QCOMPARE(proxy.mapFromSource(QModelIndex()), QModelIndex());
2742 QCOMPARE(proxy.mapToSource(QModelIndex()), QModelIndex());
2744 #ifdef QT_NO_DEBUG //if Qt is compiled in debug mode, this will assert
2745 QTest::ignoreMessage(QtWarningMsg, "QSortFilterProxyModel: index from wrong model passed to mapToSource ");
2746 QCOMPARE(proxy.mapToSource(source.index(2, 3)), QModelIndex());
2747 QTest::ignoreMessage(QtWarningMsg, "QSortFilterProxyModel: index from wrong model passed to mapFromSource ");
2748 QCOMPARE(proxy.mapFromSource(proxy.index(6, 2)), QModelIndex());
2752 static QStandardItem *addEntry(QStandardItem* pParent, const QString &description)
2754 QStandardItem* pItem = new QStandardItem(description);
2755 pParent->appendRow(pItem);
2759 void tst_QSortFilterProxyModel::removeRowsRecursive()
2761 QStandardItemModel pModel;
2762 QStandardItem *pItem1 = new QStandardItem("root");
2763 pModel.appendRow(pItem1);
2764 QList<QStandardItem *> items;
2766 QStandardItem *pItem11 = addEntry(pItem1,"Sub-heading");
2768 QStandardItem *pItem111 = addEntry(pItem11,"A");
2770 items << addEntry(pItem111,"A1");
2771 items << addEntry(pItem111,"A2");
2772 QStandardItem *pItem112 = addEntry(pItem11,"B");
2774 items << addEntry(pItem112,"B1");
2775 items << addEntry(pItem112,"B2");
2776 QStandardItem *pItem1123 = addEntry(pItem112,"B3");
2778 items << addEntry(pItem1123,"B3-");
2780 QSortFilterProxyModel proxy;
2781 proxy.setSourceModel(&pModel);
2783 QList<QPersistentModelIndex> sourceIndexes;
2784 QList<QPersistentModelIndex> proxyIndexes;
2785 foreach (QStandardItem *item, items) {
2786 QModelIndex idx = item->index();
2787 sourceIndexes << idx;
2788 proxyIndexes << proxy.mapFromSource(idx);
2791 foreach (const QPersistentModelIndex &pidx, sourceIndexes)
2792 QVERIFY(pidx.isValid());
2793 foreach (const QPersistentModelIndex &pidx, proxyIndexes)
2794 QVERIFY(pidx.isValid());
2796 QList<QStandardItem*> itemRow = pItem1->takeRow(0);
2798 QCOMPARE(itemRow.count(), 1);
2799 QCOMPARE(itemRow.first(), pItem11);
2801 foreach (const QPersistentModelIndex &pidx, sourceIndexes)
2802 QVERIFY(!pidx.isValid());
2803 foreach (const QPersistentModelIndex &pidx, proxyIndexes)
2804 QVERIFY(!pidx.isValid());
2809 void tst_QSortFilterProxyModel::doubleProxySelectionSetSourceModel()
2811 QStandardItemModel *model1 = new QStandardItemModel;
2812 QStandardItem *parentItem = model1->invisibleRootItem();
2813 for (int i = 0; i < 4; ++i) {
2814 QStandardItem *item = new QStandardItem(QString("model1 item %0").arg(i));
2815 parentItem->appendRow(item);
2819 QStandardItemModel *model2 = new QStandardItemModel;
2820 QStandardItem *parentItem2 = model2->invisibleRootItem();
2821 for (int i = 0; i < 4; ++i) {
2822 QStandardItem *item = new QStandardItem(QString("model2 item %0").arg(i));
2823 parentItem2->appendRow(item);
2827 QSortFilterProxyModel *toggleProxy = new QSortFilterProxyModel;
2828 toggleProxy->setSourceModel(model1);
2830 QSortFilterProxyModel *proxyModel = new QSortFilterProxyModel;
2831 proxyModel->setSourceModel(toggleProxy);
2833 QModelIndex mi = proxyModel->index(0, 0, proxyModel->index(0, 0, proxyModel->index(0, 0)));
2834 QItemSelectionModel ism(proxyModel);
2835 ism.select(mi, QItemSelectionModel::Select);
2836 QModelIndexList mil = ism.selectedIndexes();
2837 QCOMPARE(mil.count(), 1);
2838 QCOMPARE(mil.first(), mi);
2840 toggleProxy->setSourceModel(model2);
2841 // No crash, it's good news!
2842 QVERIFY(ism.selection().isEmpty());
2845 void tst_QSortFilterProxyModel::appearsAndSort()
2847 class PModel : public QSortFilterProxyModel
2850 PModel() : mVisible(false) {};
2852 bool filterAcceptsRow(int, const QModelIndex &) const
2867 QStringListModel sourceModel;
2869 list << "b" << "a" << "c";
2870 sourceModel.setStringList(list);
2872 proxyModel.setSourceModel(&sourceModel);
2873 proxyModel.setDynamicSortFilter(true);
2874 proxyModel.sort(0, Qt::AscendingOrder);
2876 QApplication::processEvents();
2877 QCOMPARE(sourceModel.rowCount(), 3);
2878 QCOMPARE(proxyModel.rowCount(), 0); //all rows are hidden at first;
2880 QSignalSpy spyAbout1(&proxyModel, SIGNAL(layoutAboutToBeChanged()));
2881 QSignalSpy spyChanged1(&proxyModel, SIGNAL(layoutChanged()));
2883 QVERIFY(spyAbout1.isValid());
2884 QVERIFY(spyChanged1.isValid());
2886 //introducing secondProxyModel to test the layoutChange when many items appears at once
2887 QSortFilterProxyModel secondProxyModel;
2888 secondProxyModel.setSourceModel(&proxyModel);
2889 secondProxyModel.setDynamicSortFilter(true);
2890 secondProxyModel.sort(0, Qt::DescendingOrder);
2891 QCOMPARE(secondProxyModel.rowCount(), 0); //all rows are hidden at first;
2892 QSignalSpy spyAbout2(&secondProxyModel, SIGNAL(layoutAboutToBeChanged()));
2893 QSignalSpy spyChanged2(&secondProxyModel, SIGNAL(layoutChanged()));
2895 QVERIFY(spyAbout2.isValid());
2896 QVERIFY(spyChanged2.isValid());
2898 proxyModel.updateXX();
2899 QApplication::processEvents();
2900 //now rows should be visible, and sorted
2901 QCOMPARE(proxyModel.rowCount(), 3);
2902 QCOMPARE(proxyModel.data(proxyModel.index(0,0), Qt::DisplayRole).toString(), QString::fromLatin1("a"));
2903 QCOMPARE(proxyModel.data(proxyModel.index(1,0), Qt::DisplayRole).toString(), QString::fromLatin1("b"));
2904 QCOMPARE(proxyModel.data(proxyModel.index(2,0), Qt::DisplayRole).toString(), QString::fromLatin1("c"));
2906 //now rows should be visible, and sorted
2907 QCOMPARE(secondProxyModel.rowCount(), 3);
2908 QCOMPARE(secondProxyModel.data(secondProxyModel.index(0,0), Qt::DisplayRole).toString(), QString::fromLatin1("c"));
2909 QCOMPARE(secondProxyModel.data(secondProxyModel.index(1,0), Qt::DisplayRole).toString(), QString::fromLatin1("b"));
2910 QCOMPARE(secondProxyModel.data(secondProxyModel.index(2,0), Qt::DisplayRole).toString(), QString::fromLatin1("a"));
2912 QCOMPARE(spyAbout1.count(), 1);
2913 QCOMPARE(spyChanged1.count(), 1);
2914 QCOMPARE(spyAbout2.count(), 1);
2915 QCOMPARE(spyChanged2.count(), 1);
2918 void tst_QSortFilterProxyModel::unnecessaryDynamicSorting()
2920 QStringListModel model;
2921 const QStringList initial = QString("bravo charlie delta echo").split(" ");
2922 model.setStringList(initial);
2923 QSortFilterProxyModel proxy;
2924 proxy.setDynamicSortFilter(false);
2925 proxy.setSourceModel(&model);
2926 proxy.sort(Qt::AscendingOrder);
2929 int maxrows = proxy.rowCount(QModelIndex());
2930 model.insertRows(maxrows, 2);
2931 model.setData(model.index(maxrows, 0), QString("alpha"));
2932 model.setData(model.index(maxrows + 1, 0), QString("fondue"));
2934 //append new items to the initial string list and compare with model
2935 QStringList expected = initial;
2936 expected << QString("alpha") << QString("fondue");
2938 //if bug 7716 is present, new rows were prepended, when they should have been appended
2939 for (int row = 0; row < proxy.rowCount(QModelIndex()); ++row) {
2940 QModelIndex index = proxy.index(row, 0, QModelIndex());
2941 QCOMPARE(proxy.data(index, Qt::DisplayRole).toString(), expected.at(row));
2945 class SelectionProxyModel : QAbstractProxyModel
2949 SelectionProxyModel()
2950 : QAbstractProxyModel(), selectionModel(0)
2954 QModelIndex mapFromSource(QModelIndex const&) const
2955 { return QModelIndex(); }
2957 QModelIndex mapToSource(QModelIndex const&) const
2958 { return QModelIndex(); }
2960 QModelIndex index(int, int, const QModelIndex&) const
2961 { return QModelIndex(); }
2963 QModelIndex parent(const QModelIndex&) const
2964 { return QModelIndex(); }
2966 int rowCount(const QModelIndex&) const
2969 int columnCount(const QModelIndex&) const
2972 void setSourceModel( QAbstractItemModel *sourceModel )
2975 disconnect( sourceModel, SIGNAL(modelAboutToBeReset()), this, SLOT(sourceModelAboutToBeReset()) );
2976 QAbstractProxyModel::setSourceModel( sourceModel );
2977 connect( sourceModel, SIGNAL(modelAboutToBeReset()), this, SLOT(sourceModelAboutToBeReset()) );
2981 void setSelectionModel( QItemSelectionModel *_selectionModel )
2983 selectionModel = _selectionModel;
2987 void sourceModelAboutToBeReset()
2989 QVERIFY( selectionModel->selectedIndexes().size() == 1 );
2993 void sourceModelReset()
2999 QItemSelectionModel *selectionModel;
3002 void tst_QSortFilterProxyModel::testMultipleProxiesWithSelection()
3004 QStringListModel model;
3005 const QStringList initial = QString("bravo charlie delta echo").split(" ");
3006 model.setStringList(initial);
3008 QSortFilterProxyModel proxy;
3009 proxy.setSourceModel( &model );
3011 SelectionProxyModel proxy1;
3012 QSortFilterProxyModel proxy2;
3014 // Note that the order here matters. The order of the sourceAboutToBeReset
3015 // exposes the bug in QSortFilterProxyModel.
3016 proxy2.setSourceModel( &proxy );
3017 proxy1.setSourceModel( &proxy );
3019 QItemSelectionModel selectionModel(&proxy2);
3020 proxy1.setSelectionModel( &selectionModel );
3022 selectionModel.select( proxy2.index( 0, 0 ), QItemSelectionModel::Select );
3024 // trick the proxy into emitting begin/end reset signals.
3025 proxy.setSourceModel(0);
3028 static bool isValid(const QItemSelection &selection)
3030 foreach (const QItemSelectionRange &range, selection)
3031 if (!range.isValid())
3036 void tst_QSortFilterProxyModel::mapSelectionFromSource()
3038 QStringListModel model;
3039 const QStringList initial = QString("bravo charlie delta echo").split(" ");
3040 model.setStringList(initial);
3042 QSortFilterProxyModel proxy;
3043 proxy.setDynamicSortFilter(true);
3044 proxy.setFilterRegExp("d.*");
3045 proxy.setSourceModel(&model);
3047 // Only "delta" remains.
3048 QVERIFY(proxy.rowCount() == 1);
3050 QItemSelection selection;
3051 QModelIndex charlie = model.index(1, 0);
3052 selection.append(QItemSelectionRange(charlie, charlie));
3053 QModelIndex delta = model.index(2, 0);
3054 selection.append(QItemSelectionRange(delta, delta));
3055 QModelIndex echo = model.index(3, 0);
3056 selection.append(QItemSelectionRange(echo, echo));
3058 QVERIFY(isValid(selection));
3060 QItemSelection proxiedSelection = proxy.mapSelectionFromSource(selection);
3062 // Only "delta" is in the mapped result.
3063 QVERIFY(proxiedSelection.size() == 1);
3064 QVERIFY(isValid(proxiedSelection));
3067 class Model10287 : public QStandardItemModel
3072 Model10287(QObject *parent = 0)
3073 : QStandardItemModel(0, 1, parent)
3075 parentItem = new QStandardItem("parent");
3076 parentItem->setData(false, Qt::UserRole);
3077 appendRow(parentItem);
3079 childItem = new QStandardItem("child");
3080 childItem->setData(true, Qt::UserRole);
3081 parentItem->appendRow(childItem);
3083 childItem2 = new QStandardItem("child2");
3084 childItem2->setData(true, Qt::UserRole);
3085 parentItem->appendRow(childItem2);
3090 childItem2->setData(false, Qt::UserRole);
3091 parentItem->removeRow(0);
3095 QStandardItem *parentItem, *childItem, *childItem2;
3098 class Proxy10287 : public QSortFilterProxyModel
3103 Proxy10287(QAbstractItemModel *model, QObject *parent = 0)
3104 : QSortFilterProxyModel(parent)
3106 setSourceModel(model);
3107 setDynamicSortFilter(true);
3111 virtual bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
3113 // Filter based on UserRole in model
3114 QModelIndex i = sourceModel()->index(source_row, 0, source_parent);
3115 return i.data(Qt::UserRole).toBool();
3119 void tst_QSortFilterProxyModel::unnecessaryMapCreation()
3124 // No assert failure, it passes.
3127 class FilteredColumnProxyModel : public QSortFilterProxyModel
3131 FilteredColumnProxyModel(QObject *parent = 0)
3132 : QSortFilterProxyModel(parent)
3137 bool filterAcceptsColumn(int column, const QModelIndex & /* source_parent */) const
3139 return column % 2 != 0;
3143 void tst_QSortFilterProxyModel::filteredColumns()
3145 DynamicTreeModel *model = new DynamicTreeModel(this);
3147 FilteredColumnProxyModel *proxy = new FilteredColumnProxyModel(this);
3148 proxy->setSourceModel(model);
3150 new ModelTest(proxy, this);
3152 ModelInsertCommand *insertCommand = new ModelInsertCommand(model, this);
3153 insertCommand->setNumCols(2);
3154 insertCommand->setStartRow(0);
3155 insertCommand->setEndRow(0);
3156 // Parent is QModelIndex()
3157 insertCommand->doCommand();
3160 class ChangableHeaderData : public QStringListModel
3164 explicit ChangableHeaderData(QObject *parent = 0)
3165 : QStringListModel(parent)
3170 void emitHeaderDataChanged()
3172 headerDataChanged(Qt::Vertical, 0, rowCount() - 1);
3177 void tst_QSortFilterProxyModel::headerDataChanged()
3179 ChangableHeaderData *model = new ChangableHeaderData(this);
3181 QStringList numbers;
3182 for (int i = 0; i < 10; ++i)
3183 numbers.append(QString::number(i));
3184 model->setStringList(numbers);
3186 QSortFilterProxyModel *proxy = new QSortFilterProxyModel(this);
3187 proxy->setSourceModel(model);
3189 new ModelTest(proxy, this);
3191 model->emitHeaderDataChanged();
3194 void tst_QSortFilterProxyModel::resetInvalidate_data()
3196 QTest::addColumn<int>("test");
3197 QTest::addColumn<bool>("works");
3199 QTest::newRow("nothing") << 0 << false;
3200 QTest::newRow("reset") << 1 << true;
3201 QTest::newRow("invalidate") << 2 << true;
3202 QTest::newRow("invalidate_filter") << 3 << true;
3205 void tst_QSortFilterProxyModel::resetInvalidate()
3208 QFETCH(bool, works);
3210 struct Proxy : QSortFilterProxyModel {
3212 virtual bool filterAcceptsRow(int source_row, const QModelIndex&) const
3214 return sourceModel()->data(sourceModel()->index(source_row, 0)).toString().contains(pattern);
3216 void notifyChange(int test)
3220 case 1: reset(); break;
3221 case 2: invalidate(); break;
3222 case 3: invalidateFilter(); break;
3227 QStringListModel sourceModel(QStringList() << "Poisson" << "Vache" << "Brebis"
3228 << "Elephant" << "Cochon" << "Serpent"
3229 << "Mouton" << "Ecureuil" << "Mouche");
3231 proxy.pattern = QString::fromLatin1("n");
3232 proxy.setSourceModel(&sourceModel);
3234 QCOMPARE(proxy.rowCount(), 5);
3235 for (int i = 0; i < proxy.rowCount(); i++) {
3236 QVERIFY(proxy.data(proxy.index(i,0)).toString().contains('n'));
3239 proxy.pattern = QString::fromLatin1("o");
3240 proxy.notifyChange(test);
3242 QCOMPARE(proxy.rowCount(), works ? 4 : 5);
3244 for (int i = 0; i < proxy.rowCount(); i++) {
3245 if (!proxy.data(proxy.index(i,0)).toString().contains('o'))
3248 QCOMPARE(ok, works);
3251 Q_DECLARE_METATYPE(QList<QPersistentModelIndex>)
3253 void tst_QSortFilterProxyModel::testParentLayoutChanged()
3255 QStandardItemModel model;
3256 QStandardItem *parentItem = model.invisibleRootItem();
3257 for (int i = 0; i < 4; ++i) {
3259 QStandardItem *item = new QStandardItem(QString("item %0").arg(i));
3260 parentItem->appendRow(item);
3263 QStandardItem *item = new QStandardItem(QString("item 1%0").arg(i));
3264 parentItem->appendRow(item);
3269 QSortFilterProxyModel proxy;
3270 proxy.sort(0, Qt::AscendingOrder);
3271 proxy.setDynamicSortFilter(true);
3273 proxy.setSourceModel(&model);
3274 proxy.setObjectName("proxy");
3276 // When Proxy1 emits layoutChanged(QList<QPersistentModelIndex>) this
3277 // one will too, with mapped indexes.
3278 QSortFilterProxyModel proxy2;
3279 proxy2.sort(0, Qt::AscendingOrder);
3280 proxy2.setDynamicSortFilter(true);
3282 proxy2.setSourceModel(&proxy);
3283 proxy2.setObjectName("proxy2");
3285 qRegisterMetaType<QList<QPersistentModelIndex> >();
3287 QSignalSpy dataChangedSpy(&model, SIGNAL(dataChanged(QModelIndex,QModelIndex)));
3289 QVERIFY(dataChangedSpy.isValid());
3291 // Verify that the no-arg signal is still emitted.
3292 QSignalSpy layoutAboutToBeChangedSpy(&proxy, SIGNAL(layoutAboutToBeChanged()));
3293 QSignalSpy layoutChangedSpy(&proxy, SIGNAL(layoutChanged()));
3295 QVERIFY(layoutAboutToBeChangedSpy.isValid());
3296 QVERIFY(layoutChangedSpy.isValid());
3298 QSignalSpy parentsAboutToBeChangedSpy(&proxy, SIGNAL(layoutAboutToBeChanged(QList<QPersistentModelIndex>)));
3299 QSignalSpy parentsChangedSpy(&proxy, SIGNAL(layoutChanged(QList<QPersistentModelIndex>)));
3301 QVERIFY(parentsAboutToBeChangedSpy.isValid());
3302 QVERIFY(parentsChangedSpy.isValid());
3304 QSignalSpy proxy2ParentsAboutToBeChangedSpy(&proxy2, SIGNAL(layoutAboutToBeChanged(QList<QPersistentModelIndex>)));
3305 QSignalSpy proxy2ParentsChangedSpy(&proxy2, SIGNAL(layoutChanged(QList<QPersistentModelIndex>)));
3307 QVERIFY(proxy2ParentsAboutToBeChangedSpy.isValid());
3308 QVERIFY(proxy2ParentsChangedSpy.isValid());
3310 QStandardItem *item = model.invisibleRootItem()->child(1)->child(1);
3313 proxy.mapFromSource(model.indexFromItem(item));
3315 item->setData("Changed");
3317 QCOMPARE(dataChangedSpy.size(), 1);
3318 QCOMPARE(layoutAboutToBeChangedSpy.size(), 1);
3319 QCOMPARE(layoutChangedSpy.size(), 1);
3320 QCOMPARE(parentsAboutToBeChangedSpy.size(), 1);
3321 QCOMPARE(parentsChangedSpy.size(), 1);
3322 QCOMPARE(proxy2ParentsAboutToBeChangedSpy.size(), 1);
3323 QCOMPARE(proxy2ParentsChangedSpy.size(), 1);
3325 QVariantList beforeSignal = parentsAboutToBeChangedSpy.first();
3326 QVariantList afterSignal = parentsChangedSpy.first();
3328 QCOMPARE(beforeSignal.size(), 1);
3329 QCOMPARE(afterSignal.size(), 1);
3331 QList<QPersistentModelIndex> beforeParents = beforeSignal.first().value<QList<QPersistentModelIndex> >();
3332 QList<QPersistentModelIndex> afterParents = afterSignal.first().value<QList<QPersistentModelIndex> >();
3334 QCOMPARE(beforeParents.size(), 1);
3335 QCOMPARE(afterParents.size(), 1);
3337 QVERIFY(beforeParents.first().isValid());
3338 QVERIFY(beforeParents.first() == afterParents.first());
3340 QVERIFY(beforeParents.first() == proxy.mapFromSource(model.indexFromItem(model.invisibleRootItem()->child(1))));
3342 QList<QPersistentModelIndex> proxy2BeforeList = proxy2ParentsAboutToBeChangedSpy.first().first().value<QList<QPersistentModelIndex> >();
3343 QList<QPersistentModelIndex> proxy2AfterList = proxy2ParentsChangedSpy.first().first().value<QList<QPersistentModelIndex> >();
3345 QCOMPARE(proxy2BeforeList.size(), beforeParents.size());
3346 QCOMPARE(proxy2AfterList.size(), afterParents.size());
3347 foreach (const QPersistentModelIndex &idx, proxy2BeforeList)
3348 QVERIFY(beforeParents.contains(proxy2.mapToSource(idx)));
3349 foreach (const QPersistentModelIndex &idx, proxy2AfterList)
3350 QVERIFY(afterParents.contains(proxy2.mapToSource(idx)));
3353 class SignalArgumentChecker : public QObject
3357 SignalArgumentChecker(QAbstractItemModel *model, QAbstractProxyModel *proxy, QObject *parent = 0)
3358 : QObject(parent), m_model(model), m_proxy(proxy)
3360 connect(model, SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)), SLOT(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)));
3361 connect(model, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), SLOT(rowsMoved(QModelIndex,int,int,QModelIndex,int)));
3362 connect(proxy, SIGNAL(layoutAboutToBeChanged(QList<QPersistentModelIndex>)), SLOT(layoutAboutToBeChanged(QList<QPersistentModelIndex>)));
3363 connect(proxy, SIGNAL(layoutChanged(QList<QPersistentModelIndex>)), SLOT(layoutChanged(QList<QPersistentModelIndex>)));
3367 void rowsAboutToBeMoved(const QModelIndex &source, int, int, const QModelIndex &destination, int)
3369 m_p1PersistentBefore = source;
3370 m_p2PersistentBefore = destination;
3371 m_p2FirstProxyChild = m_proxy->index(0, 0, m_proxy->mapFromSource(destination));
3374 void rowsMoved(const QModelIndex &source, int, int, const QModelIndex &destination, int)
3376 m_p1PersistentAfter = source;
3377 m_p2PersistentAfter = destination;
3380 void layoutAboutToBeChanged(const QList<QPersistentModelIndex> &parents)
3382 QVERIFY(m_p1PersistentBefore.isValid());
3383 QVERIFY(m_p2PersistentBefore.isValid());
3384 QCOMPARE(parents.size(), 2);
3385 QVERIFY(parents.first() != parents.at(1));
3386 QVERIFY(parents.contains(m_proxy->mapFromSource(m_p1PersistentBefore)));
3387 QVERIFY(parents.contains(m_proxy->mapFromSource(m_p2PersistentBefore)));
3390 void layoutChanged(const QList<QPersistentModelIndex> &parents)
3392 QVERIFY(m_p1PersistentAfter.isValid());
3393 QVERIFY(m_p2PersistentAfter.isValid());
3394 QCOMPARE(parents.size(), 2);
3395 QVERIFY(parents.first() != parents.at(1));
3396 QVERIFY(parents.contains(m_proxy->mapFromSource(m_p1PersistentAfter)));
3397 QVERIFY(parents.contains(m_proxy->mapFromSource(m_p2PersistentAfter)));
3399 // In the source model, the rows were moved to row 1 in the parent.
3400 // m_p2FirstProxyChild was created with row 0 in the proxy.
3401 // The moved rows in the proxy do not appear at row 1 because of sorting.
3402 // Sorting causes them to appear at row 0 instead, pushing what used to
3403 // be row 0 in the proxy down by two rows.
3404 QCOMPARE(m_p2FirstProxyChild.row(), 2);
3408 QAbstractItemModel *m_model;
3409 QAbstractProxyModel *m_proxy;
3410 QPersistentModelIndex m_p1PersistentBefore;
3411 QPersistentModelIndex m_p2PersistentBefore;
3412 QPersistentModelIndex m_p1PersistentAfter;
3413 QPersistentModelIndex m_p2PersistentAfter;
3415 QPersistentModelIndex m_p2FirstProxyChild;
3418 void tst_QSortFilterProxyModel::moveSourceRows()
3420 qRegisterMetaType<QList<QPersistentModelIndex> >();
3422 DynamicTreeModel model;
3425 ModelInsertCommand insertCommand(&model);
3426 insertCommand.setStartRow(0);
3427 insertCommand.setEndRow(9);
3428 insertCommand.doCommand();
3431 ModelInsertCommand insertCommand(&model);
3432 insertCommand.setAncestorRowNumbers(QList<int>() << 2);
3433 insertCommand.setStartRow(0);
3434 insertCommand.setEndRow(9);
3435 insertCommand.doCommand();
3438 ModelInsertCommand insertCommand(&model);
3439 insertCommand.setAncestorRowNumbers(QList<int>() << 5);
3440 insertCommand.setStartRow(0);
3441 insertCommand.setEndRow(9);
3442 insertCommand.doCommand();
3445 QSortFilterProxyModel proxy;
3446 proxy.setDynamicSortFilter(true);
3447 proxy.sort(0, Qt::AscendingOrder);
3449 // We need to check the arguments at emission time
3450 SignalArgumentChecker checker(&model, &proxy);
3452 proxy.setSourceModel(&model);
3454 QSortFilterProxyModel filterProxy;
3455 filterProxy.setDynamicSortFilter(true);
3456 filterProxy.sort(0, Qt::AscendingOrder);
3457 filterProxy.setSourceModel(&proxy);
3458 filterProxy.setFilterRegExp("6"); // One of the parents
3460 QSortFilterProxyModel filterBothProxy;
3461 filterBothProxy.setDynamicSortFilter(true);
3462 filterBothProxy.sort(0, Qt::AscendingOrder);
3463 filterBothProxy.setSourceModel(&proxy);
3464 filterBothProxy.setFilterRegExp("5"); // The parents are 6 and 3. This filters both out.
3466 QSignalSpy modelBeforeSpy(&model, SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)));
3467 QSignalSpy modelAfterSpy(&model, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)));
3468 QSignalSpy proxyBeforeMoveSpy(m_proxy, SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)));
3469 QSignalSpy proxyAfterMoveSpy(m_proxy, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)));
3470 QSignalSpy proxyBeforeParentLayoutSpy(&proxy, SIGNAL(layoutAboutToBeChanged(QList<QPersistentModelIndex>)));
3471 QSignalSpy proxyAfterParentLayoutSpy(&proxy, SIGNAL(layoutChanged(QList<QPersistentModelIndex>)));
3472 QSignalSpy filterBeforeParentLayoutSpy(&filterProxy, SIGNAL(layoutAboutToBeChanged(QList<QPersistentModelIndex>)));
3473 QSignalSpy filterAfterParentLayoutSpy(&filterProxy, SIGNAL(layoutChanged(QList<QPersistentModelIndex>)));
3474 QSignalSpy filterBothBeforeParentLayoutSpy(&filterBothProxy, SIGNAL(layoutAboutToBeChanged(QList<QPersistentModelIndex>)));
3475 QSignalSpy filterBothAfterParentLayoutSpy(&filterBothProxy, SIGNAL(layoutChanged(QList<QPersistentModelIndex>)));
3477 QVERIFY(modelBeforeSpy.isValid());
3478 QVERIFY(modelAfterSpy.isValid());
3479 QVERIFY(proxyBeforeMoveSpy.isValid());
3480 QVERIFY(proxyAfterMoveSpy.isValid());
3481 QVERIFY(proxyBeforeParentLayoutSpy.isValid());
3482 QVERIFY(proxyAfterParentLayoutSpy.isValid());
3483 QVERIFY(filterBeforeParentLayoutSpy.isValid());
3484 QVERIFY(filterAfterParentLayoutSpy.isValid());
3485 QVERIFY(filterBothBeforeParentLayoutSpy.isValid());
3486 QVERIFY(filterBothAfterParentLayoutSpy.isValid());
3489 ModelMoveCommand moveCommand(&model, 0);
3490 moveCommand.setAncestorRowNumbers(QList<int>() << 2);
3491 moveCommand.setDestAncestors(QList<int>() << 5);
3492 moveCommand.setStartRow(3);
3493 moveCommand.setEndRow(4);
3494 moveCommand.setDestRow(1);
3495 moveCommand.doCommand();
3498 // Proxy notifies layout change
3499 QCOMPARE(modelBeforeSpy.size(), 1);
3500 QCOMPARE(proxyBeforeParentLayoutSpy.size(), 1);
3501 QCOMPARE(modelAfterSpy.size(), 1);
3502 QCOMPARE(proxyAfterParentLayoutSpy.size(), 1);
3504 // But it doesn't notify a move.
3505 QCOMPARE(proxyBeforeMoveSpy.size(), 0);
3506 QCOMPARE(proxyAfterMoveSpy.size(), 0);
3508 QCOMPARE(filterBeforeParentLayoutSpy.size(), 1);
3509 QCOMPARE(filterAfterParentLayoutSpy.size(), 1);
3511 QList<QPersistentModelIndex> filterBeforeParents = filterBeforeParentLayoutSpy.first().first().value<QList<QPersistentModelIndex> >();
3512 QList<QPersistentModelIndex> filterAfterParents = filterAfterParentLayoutSpy.first().first().value<QList<QPersistentModelIndex> >();
3514 QCOMPARE(filterBeforeParents.size(), 1);
3515 QCOMPARE(filterAfterParents.size(), 1);
3517 QCOMPARE(filterBothBeforeParentLayoutSpy.size(), 0);
3518 QCOMPARE(filterBothAfterParentLayoutSpy.size(), 0);
3521 class FilterProxy : public QSortFilterProxyModel
3525 FilterProxy(QObject *parent = 0)
3526 : QSortFilterProxyModel(parent),
3533 void setMode(bool on)
3540 virtual bool filterAcceptsRow ( int source_row, const QModelIndex & source_parent ) const
3543 if (!source_parent.isValid()) {
3546 return (source_row % 2) != 0;
3549 if (!source_parent.isValid()) {
3550 return source_row >= 2 && source_row < 10;
3561 void tst_QSortFilterProxyModel::hierarchyFilterInvalidation()
3563 QStandardItemModel model;
3564 for (int i = 0; i < 10; ++i) {
3565 QStandardItem *child = new QStandardItem(QString("Row %1").arg(i));
3566 for (int j = 0; j < 1; ++j) {
3567 child->appendRow(new QStandardItem(QString("Row %1/%2").arg(i).arg(j)));
3569 model.appendRow(child);
3573 proxy.setSourceModel(&model);
3576 view.setModel(&proxy);
3578 view.setCurrentIndex(proxy.index(2, 0).child(0, 0));
3581 QTest::qWaitForWindowExposed(&view);
3583 proxy.setMode(true);
3586 class FilterProxy2 : public QSortFilterProxyModel
3590 FilterProxy2(QObject *parent = 0)
3591 : QSortFilterProxyModel(parent),
3598 void setMode(bool on)
3605 virtual bool filterAcceptsRow ( int source_row, const QModelIndex & source_parent ) const
3607 if (source_parent.isValid()) {
3610 if (0 == source_row) {
3622 void tst_QSortFilterProxyModel::simpleFilterInvalidation()
3624 QStandardItemModel model;
3625 for (int i = 0; i < 2; ++i) {
3626 QStandardItem *child = new QStandardItem(QString("Row %1").arg(i));
3627 child->appendRow(new QStandardItem("child"));
3628 model.appendRow(child);
3632 proxy.setSourceModel(&model);
3635 view.setModel(&proxy);
3638 QTest::qWaitForWindowExposed(&view);
3640 proxy.setMode(true);
3641 model.insertRow(0, new QStandardItem("extra"));
3645 QTEST_MAIN(tst_QSortFilterProxyModel)
3646 #include "tst_qsortfilterproxymodel.moc"