diff --git a/sources/elementspanel.cpp b/sources/elementspanel.cpp index 349f1e9b6..2256de362 100644 --- a/sources/elementspanel.cpp +++ b/sources/elementspanel.cpp @@ -422,10 +422,20 @@ void ElementsPanel::projectWasOpened(QETProject *project) { @param project Projet a enlever du panel */ void ElementsPanel::projectWasClosed(QETProject *project) { - if (QTreeWidgetItem *item_to_remove = itemForProject(project)) { - GenericPanel::deleteItem(item_to_remove); - projects_to_display_.remove(project); + //Stop listening to the project and its sheets: the project still + //emits while it is torn down (setModified() -> + //projectInformationsChanged()), which put its item back, with items + //for sheets about to be freed + if (project) { + disconnect(project, nullptr, this, nullptr); + disconnect(project->embeddedTitleBlockTemplatesCollection(), nullptr, this, nullptr); } + if (QTreeWidgetItem *item_to_remove = itemForProject(project)) { + for (QTreeWidgetItem *sheet : childItems(item_to_remove, QET::Diagram)) + disconnect(valueForItem(sheet), nullptr, this, nullptr); + GenericPanel::deleteItem(item_to_remove); + } + projects_to_display_.remove(project); emit(panelContentChanged()); } diff --git a/tests/qttest/CMakeLists.txt b/tests/qttest/CMakeLists.txt index 25dedbbbe..c2566723f 100644 --- a/tests/qttest/CMakeLists.txt +++ b/tests/qttest/CMakeLists.txt @@ -1445,3 +1445,7 @@ if(QET_HAS_SCRIPTING) "QET_TEST_BINARY_PATH=\"$\"" "QET_EXAMPLES_DIR=\"${QET_DIR}/examples\"") endif() + +# Closing a modified project with Discard left it in the project tree, with +# items for its freed sheets; a click on one crashed (forum topic 3215). +qet_add_app_test(tst_elementspanelclosedproject tst_elementspanelclosedproject.cpp) diff --git a/tests/qttest/tst_elementspanelclosedproject.cpp b/tests/qttest/tst_elementspanelclosedproject.cpp new file mode 100644 index 000000000..b31a1697a --- /dev/null +++ b/tests/qttest/tst_elementspanelclosedproject.cpp @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include "qetapptest.h" + +#include "elementspanel.h" +#include "elementspanelwidget.h" +#include "genericpanel.h" +#include "projectview.h" +#include "qetproject.h" + +#include +#include +#include +#include + +// Closing a modified project and choosing Discard left the project in the +// project tree: the project still emitted projectInformationsChanged() while +// it was torn down, and the panel added it back, with items for its sheets. +// A click on one of those sheets then crashed in diagram->showMe() on the +// freed sheet (forum topic 3215, issue #1548). +class tst_elementspanelclosedproject : public QObject +{ + Q_OBJECT + + private slots: + void discardedProjectLeavesTheTree() + { + QETDiagramEditor *editor = QetAppTest::editor(); + QVERIFY(editor); + auto *panel_widget = editor->findChild(); + QVERIFY(panel_widget); + ElementsPanel &panel = panel_widget->elementsPanel(); + // What the app does 250 ms after the panel is first shown. + panel.reload(); + + QVERIFY(editor->newProject()); + QPointer project = editor->currentProjectView()->project(); + project->addNewDiagram(); + QVERIFY(project->projectWasModified()); + QTreeWidgetItem *project_item = panel.itemForProject(project); + QVERIFY(project_item); + + // Answer "Discard" to "Do you want to save changes?". + bool answered = false; + QTimer answer; + connect(&answer, &QTimer::timeout, this, [&answered]() { + if (auto *box = qobject_cast(QApplication::activeModalWidget())) { + if (QAbstractButton *discard = box->button(QMessageBox::Discard)) { + answered = true; + discard->click(); + } + } + }); + answer.start(20); + + // Close it from the project tree's context menu. + panel.setCurrentItem(project_item); + panel_widget->closeProject(); + answer.stop(); + QVERIFY(answered); + QTRY_VERIFY(project.isNull()); + + for (int i = 0; i < panel.topLevelItemCount(); ++i) { + QTreeWidgetItem *item = panel.topLevelItem(i); + QVERIFY2(item->type() != QET::Project, + qPrintable(QStringLiteral("the closed project is still in the tree: %1").arg(item->text(0)))); + } + } +}; + +QET_APP_TEST_MAIN(tst_elementspanelclosedproject) + +#include "tst_elementspanelclosedproject.moc"