mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-10-19 20:44:16 +02:00
Fix a crash on clicking a sheet of a project closed without saving
Closing a modified project and choosing Discard left the project in the project tree. The panel removed its item, but the project still emits projectInformationsChanged() while it is torn down (setModified()), and GenericPanel answered by adding the project back, with items for its sheets. Once the project was freed, a click on one of those sheets called diagram->showMe() on a freed Diagram: SIGSEGV in QObjectPrivate::maybeSignalConnected(), the backtrace reported on the forum (topic 3215). The panel now stops listening to a project when it is closed, to its sheets and to its embedded title block collection, before removing its item. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -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<Diagram *>(sheet), nullptr, this, nullptr);
|
||||
GenericPanel::deleteItem(item_to_remove);
|
||||
}
|
||||
projects_to_display_.remove(project);
|
||||
emit(panelContentChanged());
|
||||
}
|
||||
|
||||
|
||||
@@ -1445,3 +1445,7 @@ if(QET_HAS_SCRIPTING)
|
||||
"QET_TEST_BINARY_PATH=\"$<TARGET_FILE:qelectrotech>\""
|
||||
"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)
|
||||
|
||||
@@ -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 <QAbstractButton>
|
||||
#include <QMessageBox>
|
||||
#include <QPointer>
|
||||
#include <QTimer>
|
||||
|
||||
// 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<ElementsPanelWidget *>();
|
||||
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<QETProject> 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<QMessageBox *>(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"
|
||||
Reference in New Issue
Block a user