From 0bb526f8fab33a42df56c8a60c272e2cca4cc792 Mon Sep 17 00:00:00 2001
From: Alexis Menard <alexis.menard@trolltech.com>
Date: Tue, 14 Apr 2009 18:05:26 +0200
Subject: QGraphicsItem: When an item is deleted and eventfilters installed The
 problem here is that we are filling the sceneEventFilters map when we install
 evenfilter but we never remove the references of an item if it has been
 removed from the scene or deleted. The deletion can keep stale pointers into
 the map and a crash can happen.

BT:yes
Task-number:250272
Reviewed-by: bnilsen
Reviewed-by: andreas
---
 src/gui/graphicsview/qgraphicsscene.cpp        | 19 +++++++++++++++++++
 tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp | 19 +++++++++++++++++++
 2 files changed, 38 insertions(+)

diff --git a/src/gui/graphicsview/qgraphicsscene.cpp b/src/gui/graphicsview/qgraphicsscene.cpp
index a5fec69..9881960 100644
--- a/src/gui/graphicsview/qgraphicsscene.cpp
+++ b/src/gui/graphicsview/qgraphicsscene.cpp
@@ -746,6 +746,15 @@ void QGraphicsScenePrivate::_q_removeItemLater(QGraphicsItem *item)
     unpolishedItems.removeAll(item);
     dirtyItems.removeAll(item);
 
+    //We remove all references of item from the sceneEventFilter arrays
+    QMultiMap<QGraphicsItem*, QGraphicsItem*>::iterator iterator = sceneEventFilters.begin();
+    while (iterator != sceneEventFilters.end()) {
+        if (iterator.value() == item || iterator.key() == item)
+            iterator = sceneEventFilters.erase(iterator);
+        else
+            ++iterator;
+    }
+
     // Remove from scene transform cache
     int transformIndex = item->d_func()->sceneTransformIndex;
     if (transformIndex != -1) {
@@ -3293,6 +3302,16 @@ void QGraphicsScene::removeItem(QGraphicsItem *item)
     d->unpolishedItems.removeAll(item);
     d->dirtyItems.removeAll(item);
 
+    //We remove all references of item from the sceneEventFilter arrays
+    QMultiMap<QGraphicsItem*, QGraphicsItem*>::iterator iterator = d->sceneEventFilters.begin();
+    while (iterator != d->sceneEventFilters.end()) {
+        if (iterator.value() == item || iterator.key() == item)
+            iterator = d->sceneEventFilters.erase(iterator);
+        else
+            ++iterator;
+    }
+
+
     //Ensure dirty flag have the correct default value so the next time it will be added it will receive updates
     item->d_func()->dirty = 0;
     item->d_func()->dirtyChildren = 0;
diff --git a/tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp b/tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp
index ad0dc97..5dd7e1e 100644
--- a/tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp
+++ b/tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp
@@ -4037,6 +4037,25 @@ void tst_QGraphicsItem::sceneEventFilter()
     QCOMPARE(tester->filteredEventReceivers.at(6), static_cast<QGraphicsItem *>(text2));
 
     QVERIFY(text2->hasFocus());
+
+    //Let check if the items are correctly removed from the sceneEventFilters array
+    //to avoid stale pointers.
+    QGraphicsView gv;
+    QGraphicsScene *anotherScene = new QGraphicsScene;
+    QGraphicsTextItem *ti = anotherScene->addText("This is a test #1");
+    ti->moveBy(50, 50);
+    QGraphicsTextItem *ti2 = anotherScene->addText("This is a test #2");
+    QGraphicsTextItem *ti3 = anotherScene->addText("This is a test #3");
+    gv.setScene(anotherScene);
+    gv.show();
+    QTest::qWait(250);
+    ti->installSceneEventFilter(ti2);
+    ti3->installSceneEventFilter(ti);
+    delete ti2;
+    //we souldn't crash
+    QTest::mouseMove(gv.viewport(), gv.mapFromScene(ti->scenePos()));
+    QTest::qWait(250);
+    delete ti;
 }
 
 class GeometryChanger : public QGraphicsItem
-- 
cgit v0.12