From baad82d242a4d8c1af6c87faaa7f25584183fd53 Mon Sep 17 00:00:00 2001
From: Stephen Kelly <steveire@gmail.com>
Date: Tue, 20 Dec 2016 00:44:12 +0000
Subject: [PATCH] QIPM: Persist model indexes after emitting layoutChange, not
 before

Callers can persist a QModelIndex which was not persisted before in a
slot connected to the signal, and such a persisted index must be updated
in the course of the layoutChange.

Store the indexes to persist after emitting the signal.

Task-number: QTBUG-32981
Change-Id: Ibee4c0d84817d72603a03fe5b22fdeefeac0695e
Reviewed-by: David Faure <david.faure@kdab.com>
---
 src/corelib/itemmodels/qidentityproxymodel.cpp     | 18 ++---
 .../tst_qidentityproxymodel.cpp                    | 76 ++++++++++++++++++++++
 2 files changed, 85 insertions(+), 9 deletions(-)

diff --git a/src/corelib/itemmodels/qidentityproxymodel.cpp b/src/corelib/itemmodels/qidentityproxymodel.cpp
index e537793..7c30679 100644
--- a/src/corelib/itemmodels/qidentityproxymodel.cpp
+++ b/src/corelib/itemmodels/qidentityproxymodel.cpp
@@ -496,15 +496,6 @@ void QIdentityProxyModelPrivate::_q_sourceLayoutAboutToBeChanged(const QList<QPe
 {
     Q_Q(QIdentityProxyModel);
 
-    const auto proxyPersistentIndexes = q->persistentIndexList();
-    for (const QPersistentModelIndex &proxyPersistentIndex : proxyPersistentIndexes) {
-        proxyIndexes << proxyPersistentIndex;
-        Q_ASSERT(proxyPersistentIndex.isValid());
-        const QPersistentModelIndex srcPersistentIndex = q->mapToSource(proxyPersistentIndex);
-        Q_ASSERT(srcPersistentIndex.isValid());
-        layoutChangePersistentIndexes << srcPersistentIndex;
-    }
-
     QList<QPersistentModelIndex> parents;
     parents.reserve(sourceParents.size());
     for (const QPersistentModelIndex &parent : sourceParents) {
@@ -518,6 +509,15 @@ void QIdentityProxyModelPrivate::_q_sourceLayoutAboutToBeChanged(const QList<QPe
     }
 
     q->layoutAboutToBeChanged(parents, hint);
+
+    const auto proxyPersistentIndexes = q->persistentIndexList();
+    for (const QPersistentModelIndex &proxyPersistentIndex : proxyPersistentIndexes) {
+        proxyIndexes << proxyPersistentIndex;
+        Q_ASSERT(proxyPersistentIndex.isValid());
+        const QPersistentModelIndex srcPersistentIndex = q->mapToSource(proxyPersistentIndex);
+        Q_ASSERT(srcPersistentIndex.isValid());
+        layoutChangePersistentIndexes << srcPersistentIndex;
+    }
 }
 
 void QIdentityProxyModelPrivate::_q_sourceLayoutChanged(const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint)
diff --git a/tests/auto/corelib/itemmodels/qidentityproxymodel/tst_qidentityproxymodel.cpp b/tests/auto/corelib/itemmodels/qidentityproxymodel/tst_qidentityproxymodel.cpp
index e946f31..564b854 100644
--- a/tests/auto/corelib/itemmodels/qidentityproxymodel/tst_qidentityproxymodel.cpp
+++ b/tests/auto/corelib/itemmodels/qidentityproxymodel/tst_qidentityproxymodel.cpp
@@ -68,6 +68,8 @@ private slots:
 
     void itemData();
 
+    void persistIndexOnLayoutChange();
+
 protected:
     void verifyIdentity(QAbstractItemModel *model, const QModelIndex &parent = QModelIndex());
 
@@ -377,5 +379,79 @@ void tst_QIdentityProxyModel::itemData()
     QCOMPARE(proxy.itemData(topIndex).value(Qt::DisplayRole).toString(), QStringLiteral("Monday_appended"));
 }
 
+void dump(QAbstractItemModel* model, QString const& indent = " - ", QModelIndex const& parent = {})
+{
+    for (auto row = 0; row < model->rowCount(parent); ++row)
+    {
+        auto idx = model->index(row, 0, parent);
+        qDebug() << (indent + idx.data().toString());
+        dump(model, indent + "- ", idx);
+    }
+}
+
+void tst_QIdentityProxyModel::persistIndexOnLayoutChange()
+{
+    DynamicTreeModel model;
+
+    QList<int> ancestors;
+    for (auto i = 0; i < 3; ++i)
+    {
+        Q_UNUSED(i);
+        ModelInsertCommand insertCommand(&model);
+        insertCommand.setAncestorRowNumbers(ancestors);
+        insertCommand.setStartRow(0);
+        insertCommand.setEndRow(0);
+        insertCommand.doCommand();
+        ancestors.push_back(0);
+    }
+    ModelInsertCommand insertCommand(&model);
+    insertCommand.setAncestorRowNumbers(ancestors);
+    insertCommand.setStartRow(0);
+    insertCommand.setEndRow(1);
+    insertCommand.doCommand();
+
+    // dump(&model);
+    // " - 1"
+    // " - - 2"
+    // " - - - 3"
+    // " - - - - 4"
+    // " - - - - 5"
+
+    QIdentityProxyModel proxy;
+    proxy.setSourceModel(&model);
+
+    QPersistentModelIndex persistentIndex;
+
+    QPersistentModelIndex sourcePersistentIndex = model.match(model.index(0, 0), Qt::DisplayRole, "5", 1, Qt::MatchRecursive).first();
+
+    QCOMPARE(sourcePersistentIndex.data().toString(), QStringLiteral("5"));
+
+    bool gotLayoutAboutToBeChanged = false;
+    bool gotLayoutChanged = false;
+
+    QObject::connect(&proxy, &QAbstractItemModel::layoutAboutToBeChanged, &proxy, [&proxy, &persistentIndex, &gotLayoutAboutToBeChanged]
+    {
+        gotLayoutAboutToBeChanged = true;
+        persistentIndex = proxy.match(proxy.index(0, 0), Qt::DisplayRole, "5", 1, Qt::MatchRecursive).first();
+    });
+
+    QObject::connect(&proxy, &QAbstractItemModel::layoutChanged, &proxy, [&proxy, &persistentIndex, &sourcePersistentIndex, &gotLayoutChanged]
+    {
+        gotLayoutChanged = true;
+        QCOMPARE(QModelIndex(persistentIndex), proxy.mapFromSource(sourcePersistentIndex));
+    });
+
+    ModelChangeChildrenLayoutsCommand layoutChangeCommand(&model, 0);
+
+    layoutChangeCommand.setAncestorRowNumbers(QList<int>{0, 0, 0});
+    layoutChangeCommand.setSecondAncestorRowNumbers(QList<int>{0, 0});
+
+    layoutChangeCommand.doCommand();
+
+    QVERIFY(gotLayoutAboutToBeChanged);
+    QVERIFY(gotLayoutChanged);
+    QVERIFY(persistentIndex.isValid());
+}
+
 QTEST_MAIN(tst_QIdentityProxyModel)
 #include "tst_qidentityproxymodel.moc"
