From 42b7a7c6097825e9fa4a11abac3ad61db051162d Mon Sep 17 00:00:00 2001 From: Joerg Bornemann Date: Wed, 27 May 2015 12:27:19 +0200 Subject: [PATCH] Fix crash due to QTreeView accessing deleted model indexes. QTreeViewPrivate::updateScrollBars depends on a correctly set up viewItems vector. If a delayed layout is pending, we must call QTreeViewPrivate::executePostedLayout before accessing any stored model indices. Task-number: QTBUG-45697 Change-Id: I55fcbaf81f225b26181c2cf739283740b85dd16a Reviewed-by: Friedemann Kleint Reviewed-by: Olivier Goffart (Woboq GmbH) --- src/widgets/itemviews/qtreeview.cpp | 1 + .../itemviews/qtreeview/tst_qtreeview.cpp | 78 +++++++++++++++++++ 2 files changed, 79 insertions(+) diff --git a/src/widgets/itemviews/qtreeview.cpp b/src/widgets/itemviews/qtreeview.cpp index 43db43fcd4..9b3e270fdd 100644 --- a/src/widgets/itemviews/qtreeview.cpp +++ b/src/widgets/itemviews/qtreeview.cpp @@ -3658,6 +3658,7 @@ void QTreeViewPrivate::updateScrollBars() if (!viewportSize.isValid()) viewportSize = QSize(0, 0); + executePostedLayout(); if (viewItems.isEmpty()) { q->doItemsLayout(); } diff --git a/tests/auto/widgets/itemviews/qtreeview/tst_qtreeview.cpp b/tests/auto/widgets/itemviews/qtreeview/tst_qtreeview.cpp index 3ead172d82..1324027af6 100644 --- a/tests/auto/widgets/itemviews/qtreeview/tst_qtreeview.cpp +++ b/tests/auto/widgets/itemviews/qtreeview/tst_qtreeview.cpp @@ -255,6 +255,7 @@ private slots: void taskQTBUG_8176_emitOnExpandAll(); void taskQTBUG_34717_collapseAtBottom(); void taskQTBUG_37813_crash(); + void taskQTBUG_45697_crash(); void testInitialFocus(); }; @@ -4385,5 +4386,82 @@ void tst_QTreeView::taskQTBUG_37813_crash() #endif // QT_BUILD_INTERNAL } +// QTBUG-45697: Using a QTreeView with a multi-column model filtered by QSortFilterProxyModel, +// when sorting the source model while the widget is not yet visible and showing the widget +// later on, corruption occurs in QTreeView. +class Qtbug45697TestWidget : public QWidget +{ + Q_OBJECT +public: + static const int columnCount = 3; + + explicit Qtbug45697TestWidget(); + int timerTick() const { return m_timerTick; } + +public slots: + void slotTimer(); + +private: + QTreeView *m_treeView; + QStandardItemModel *m_model; + QSortFilterProxyModel *m_sortFilterProxyModel; + int m_timerTick; +}; + +Qtbug45697TestWidget::Qtbug45697TestWidget() + : m_treeView(new QTreeView(this)) + , m_model(new QStandardItemModel(0, Qtbug45697TestWidget::columnCount, this)) + , m_sortFilterProxyModel(new QSortFilterProxyModel(this)) + , m_timerTick(0) + { + QVBoxLayout *vBoxLayout = new QVBoxLayout(this); + vBoxLayout->addWidget(m_treeView); + + for (char sortChar = 'z'; sortChar >= 'a' ; --sortChar) { + QList items; + for (int column = 0; column < Qtbug45697TestWidget::columnCount; ++column) { + const QString text = QLatin1Char(sortChar) + QLatin1String(" ") + QString::number(column); + items.append(new QStandardItem(text)); + } + m_model->appendRow(items); + } + + m_sortFilterProxyModel->setSourceModel(m_model); + m_treeView->setModel(m_sortFilterProxyModel); + + QHeaderView *headerView = m_treeView->header(); + for (int s = 1, lastSection = headerView->count() - 1; s < lastSection; ++s ) + headerView->setSectionResizeMode(s, QHeaderView::ResizeToContents); + + QTimer *timer = new QTimer(this); + timer->setInterval(50); + connect(timer, &QTimer::timeout, this, &Qtbug45697TestWidget::slotTimer); + timer->start(); +} + +void Qtbug45697TestWidget::slotTimer() +{ + switch (m_timerTick++) { + case 0: + m_model->sort(0); + break; + case 1: + show(); + break; + default: + close(); + break; + } +} + +void tst_QTreeView::taskQTBUG_45697_crash() +{ + Qtbug45697TestWidget testWidget; + testWidget.setWindowTitle(QTest::currentTestFunction()); + testWidget.resize(400, 400); + testWidget.move(QGuiApplication::primaryScreen()->availableGeometry().topLeft() + QPoint(100, 100)); + QTRY_VERIFY(testWidget.timerTick() >= 2); +} + QTEST_MAIN(tst_QTreeView) #include "tst_qtreeview.moc"