Fix crash when invalidating a QSortFilterProxyModel

Task-number: QTBUG-27122

Change-Id: Ibca46b88442f4f92422d9b3182e4bbf25716a07f
Reviewed-by: Olivier Goffart <ogoffart@woboq.com>
bb10
Stephen Kelly 2012-09-13 15:23:15 +02:00 committed by Qt by Nokia
parent 5c412fd74c
commit d7a15fbfd9
2 changed files with 78 additions and 9 deletions

View File

@ -1052,21 +1052,31 @@ void QSortFilterProxyModelPrivate::filter_changed(const QModelIndex &source_pare
// 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;
// The m->mapped_children vector can be appended to with indexes which are no longer filtered
// out (in create_mapping) when this function recurses for child indexes.
const QVector<QModelIndex> mappedChildren = m->mapped_children;
QVector<int> indexesToRemove;
for (int i = 0; i < mappedChildren.size(); ++i) {
const QModelIndex source_child_index = mappedChildren.at(i);
if (rows_removed.contains(source_child_index.row()) || columns_removed.contains(source_child_index.column())) {
it2 = mappedChildren.erase(it2);
indexesToRemove.push_back(i);
remove_from_mapping(source_child_index);
} else {
filter_changed(source_child_index);
}
}
QVector<int>::const_iterator removeIt = indexesToRemove.constEnd();
const QVector<int>::const_iterator removeBegin = indexesToRemove.constBegin();
// We can't just remove these items from mappedChildren while iterating above and then
// do something like m->mapped_children = mappedChildren, because mapped_children might
// be appended to in create_mapping, and we would lose those new items.
// Because they are always appended in create_mapping, we can still remove them by
// position here.
while (removeIt != removeBegin) {
--removeIt;
m->mapped_children.remove(*removeIt);
}
}
/*!

View File

@ -146,6 +146,7 @@ private slots:
void moveSourceRows();
void hierarchyFilterInvalidation();
void simpleFilterInvalidation();
protected:
void buildHierarchy(const QStringList &data, QAbstractItemModel *model);
@ -3582,6 +3583,64 @@ void tst_QSortFilterProxyModel::hierarchyFilterInvalidation()
proxy.setMode(true);
}
class FilterProxy2 : public QSortFilterProxyModel
{
Q_OBJECT
public:
FilterProxy2(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 (source_parent.isValid()) {
return true;
} else {
if (0 == source_row) {
return true;
} else {
return !mode;
}
}
}
private:
bool mode;
};
void tst_QSortFilterProxyModel::simpleFilterInvalidation()
{
QStandardItemModel model;
for (int i = 0; i < 2; ++i) {
QStandardItem *child = new QStandardItem(QString("Row %1").arg(i));
child->appendRow(new QStandardItem("child"));
model.appendRow(child);
}
FilterProxy2 proxy;
proxy.setSourceModel(&model);
QTreeView view;
view.setModel(&proxy);
view.show();
QTest::qWaitForWindowExposed(&view);
proxy.setMode(true);
model.insertRow(0, new QStandardItem("extra"));
}
QTEST_MAIN(tst_QSortFilterProxyModel)
#include "tst_qsortfilterproxymodel.moc"