summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/corelib/itemmodels/qsortfilterproxymodel.cpp13
-rw-r--r--tests/auto/corelib/itemmodels/qsortfilterproxymodel/tst_qsortfilterproxymodel.cpp68
2 files changed, 78 insertions, 3 deletions
diff --git a/src/corelib/itemmodels/qsortfilterproxymodel.cpp b/src/corelib/itemmodels/qsortfilterproxymodel.cpp
index 875bb81967..fb84dce861 100644
--- a/src/corelib/itemmodels/qsortfilterproxymodel.cpp
+++ b/src/corelib/itemmodels/qsortfilterproxymodel.cpp
@@ -1049,12 +1049,19 @@ void QSortFilterProxyModelPrivate::filter_changed(const QModelIndex &source_pare
Mapping *m = it.value();
QSet<int> rows_removed = handle_filter_changed(m->proxy_rows, m->source_rows, source_parent, Qt::Vertical);
QSet<int> columns_removed = handle_filter_changed(m->proxy_columns, m->source_columns, source_parent, Qt::Horizontal);
- QVector<QModelIndex>::iterator it2 = m->mapped_children.end();
- while (it2 != m->mapped_children.begin()) {
+
+ // We need to iterate over a copy of m->mapped_children because otherwise it may be changed by other code, invalidating
+ // the iterator it2.
+ // The m->mapped_children vector can be appended to when this function recurses for child indexes.
+ // The handle_filter_changed implementation can cause source_parent.parent() to be called, which will create
+ // a mapping (and do appending) while we are invalidating the filter.
+ QVector<QModelIndex> mappedChildren = m->mapped_children;
+ QVector<QModelIndex>::iterator it2 = mappedChildren.end();
+ while (it2 != mappedChildren.begin()) {
--it2;
const QModelIndex source_child_index = *it2;
if (rows_removed.contains(source_child_index.row()) || columns_removed.contains(source_child_index.column())) {
- it2 = m->mapped_children.erase(it2);
+ it2 = mappedChildren.erase(it2);
remove_from_mapping(source_child_index);
} else {
filter_changed(source_child_index);
diff --git a/tests/auto/corelib/itemmodels/qsortfilterproxymodel/tst_qsortfilterproxymodel.cpp b/tests/auto/corelib/itemmodels/qsortfilterproxymodel/tst_qsortfilterproxymodel.cpp
index bb3132b94f..3770abb849 100644
--- a/tests/auto/corelib/itemmodels/qsortfilterproxymodel/tst_qsortfilterproxymodel.cpp
+++ b/tests/auto/corelib/itemmodels/qsortfilterproxymodel/tst_qsortfilterproxymodel.cpp
@@ -144,6 +144,8 @@ private slots:
void testParentLayoutChanged();
void moveSourceRows();
+ void hierarchyFilterInvalidation();
+
protected:
void buildHierarchy(const QStringList &data, QAbstractItemModel *model);
void checkHierarchy(const QStringList &data, const QAbstractItemModel *model);
@@ -3480,5 +3482,71 @@ void tst_QSortFilterProxyModel::moveSourceRows()
QCOMPARE(filterBothAfterParentLayoutSpy.size(), 0);
}
+class FilterProxy : public QSortFilterProxyModel
+{
+ Q_OBJECT
+public:
+ FilterProxy(QObject *parent = 0)
+ : QSortFilterProxyModel(parent),
+ mode(false)
+ {
+
+ }
+
+public slots:
+ void setMode(bool on)
+ {
+ mode = on;
+ invalidateFilter();
+ }
+
+protected:
+ virtual bool filterAcceptsRow ( int source_row, const QModelIndex & source_parent ) const
+ {
+ if (mode) {
+ if (!source_parent.isValid()) {
+ return true;
+ } else {
+ return (source_row % 2) != 0;
+ }
+ } else {
+ if (!source_parent.isValid()) {
+ return source_row >= 2 && source_row < 10;
+ } else {
+ return true;
+ }
+ }
+ }
+
+private:
+ bool mode;
+};
+
+void tst_QSortFilterProxyModel::hierarchyFilterInvalidation()
+{
+ QStandardItemModel model;
+ for (int i = 0; i < 10; ++i) {
+ QStandardItem *child = new QStandardItem(QString("Row %1").arg(i));
+ for (int j = 0; j < 1; ++j) {
+ child->appendRow(new QStandardItem(QString("Row %1/%2").arg(i).arg(j)));
+ }
+ model.appendRow(child);
+ }
+
+ FilterProxy proxy;
+ proxy.setSourceModel(&model);
+
+ QTreeView view;
+ view.setModel(&proxy);
+
+ view.setCurrentIndex(proxy.index(2, 0).child(0, 0));
+
+ view.show();
+ QTest::qWaitForWindowExposed(&view);
+
+ proxy.setMode(true);
+}
+
+
QTEST_MAIN(tst_QSortFilterProxyModel)
#include "tst_qsortfilterproxymodel.moc"