Commit ad449a1e authored by Hartmut Riesenbeck's avatar Hartmut Riesenbeck Committed by Albert Astals Cid
Browse files

Fix segfault when leaving statistics main window

Reproducible segfault when leaving statistics main window with unsaved
changes and clicking on save in the query dialog.
The segfault is thrown by KEduVocDocument pointer access which was
deleted before during destruction of StatisticsMainWindow. Both document
and widget are destructed by deleteLater. With the conditions mentioned
above the order of destruction becomes to the document deleted first
which leads to the segfault.
The fix is done by changing the pointer for KEduVocDocument to a shared
pointer which was not deleted until the last using class is deleted.

BUG: 420302
(cherry picked from commit a7cff050)
parent 149cc565
......@@ -35,7 +35,6 @@ ContainerModel::ContainerModel(KEduVocContainer::EnumContainerType type, QObject
: ReadonlyContainerModel(type, parent)
{
m_type = type;
m_doc = 0;
}
QModelIndex ContainerModel::appendContainer(const QModelIndex& parent, const QString & containerName)
......
......@@ -36,11 +36,10 @@
ReadonlyContainerModel::ReadonlyContainerModel(KEduVocContainer::EnumContainerType type, QObject * parent)
: QAbstractItemModel(parent)
, m_type(type)
, m_doc(0)
{
}
void ReadonlyContainerModel::setDocument(KEduVocDocument * doc)
void ReadonlyContainerModel::setDocument(const std::shared_ptr<KEduVocDocument> &doc)
{
beginResetModel();
m_doc = doc;
......
......@@ -16,6 +16,8 @@
#ifndef READONLYCONTAINERMODEL_H
#define READONLYCONTAINERMODEL_H
#include <memory>
#include <QAbstractItemModel>
#include <QModelIndex>
......@@ -54,12 +56,12 @@ public:
public slots:
/** Set the new source kvtml file
* @param doc the new file */
virtual void setDocument(KEduVocDocument *doc);
virtual void setDocument(const std::shared_ptr<KEduVocDocument> &doc);
protected:
virtual KEduVocContainer *rootContainer() const = 0;
KEduVocContainer::EnumContainerType m_type;
KEduVocDocument *m_doc;
std::shared_ptr<KEduVocDocument> m_doc;
};
......
......@@ -102,21 +102,21 @@ EditorWindow::~EditorWindow()
saveMainWindowSettings(cfg);
}
void EditorWindow::updateDocument(KEduVocDocument *doc)
void EditorWindow::updateDocument(const std::shared_ptr<KEduVocDocument> &doc)
{
m_vocabularyView->setDocument(doc);
m_vocabularyModel->setDocument(doc);
m_vocabularyModel->setDocument(doc.get());
m_lessonModel->setDocument(doc);
m_wordTypeModel->setDocument(doc);
m_summaryWordWidget->slotDocumentChanged(doc);
m_inflectionWidget->setDocument(doc);
m_comparisonWidget->setDocument(doc);
m_synonymWidget->setDocument(doc);
m_antonymWidget->setDocument(doc);
m_falseFriendWidget->setDocument(doc);
m_comparisonWidget->setDocument(doc.get());
m_synonymWidget->setDocument(doc.get());
m_antonymWidget->setDocument(doc.get());
m_falseFriendWidget->setDocument(doc.get());
if (!m_mainWindow->parleyDocument()->document()) {
return;
......@@ -368,7 +368,7 @@ void EditorWindow::initDockWidgets()
// LaTeX
QDockWidget *latexDock = new QDockWidget(i18n("LaTeX"), this);
latexDock->setObjectName(QStringLiteral("LatexDock"));
m_latexWidget = new LatexWidget(m_vocabularyFilter, m_mainWindow->parleyDocument()->document(), this);
m_latexWidget = new LatexWidget(m_vocabularyFilter, m_mainWindow->parleyDocument()->document().get(), this);
QScrollArea *latexScrollArea = new QScrollArea(this);
latexScrollArea->setWidgetResizable(true);
latexScrollArea->setWidget(m_latexWidget);
......
......@@ -102,7 +102,7 @@ public slots:
/**
* Set the current doc (after creating a new one or opening a file)
*/
void updateDocument(KEduVocDocument *doc);
void updateDocument(const std::shared_ptr<KEduVocDocument> &doc);
/**
* DBus method for adding words by external apps
......
......@@ -66,11 +66,11 @@ InflectionWidget::InflectionWidget(QWidget* parent): QStackedWidget(parent)
addWidget(m_declensionWidget);
}
void InflectionWidget::setDocument(KEduVocDocument* doc)
void InflectionWidget::setDocument(const std::shared_ptr<KEduVocDocument> &doc)
{
m_doc = doc;
m_conjugationWidget->setDocument(doc);
m_declensionWidget->setDocument(doc);
m_conjugationWidget->setDocument(doc.get());
m_declensionWidget->setDocument(doc.get());
}
void InflectionWidget::setTranslation(KEduVocExpression* entry, int translation)
......
......@@ -15,6 +15,8 @@
#ifndef INFLECTIONWIDGET_H
#define INFLECTIONWIDGET_H
#include <memory>
#include <QStackedWidget>
#include <KEduVocDeclension>
......@@ -35,7 +37,7 @@ public:
explicit InflectionWidget(QWidget *parent = 0);
public slots:
void setDocument(KEduVocDocument* doc);
void setDocument(const std::shared_ptr<KEduVocDocument> &doc);
void setTranslation(KEduVocExpression* entry, int translation);
private slots:
......@@ -46,7 +48,7 @@ private:
DeclensionWidget *m_declensionWidget;
ConjugationWidget *m_conjugationWidget;
KEduVocDocument *m_doc;
std::shared_ptr<KEduVocDocument> m_doc;
KEduVocExpression *m_entry;
int m_translation;
};
......
......@@ -39,7 +39,7 @@
using namespace Editor;
SummaryWordWidget::SummaryWordWidget(VocabularyFilter *model, KEduVocDocument *doc, QWidget *parent)
SummaryWordWidget::SummaryWordWidget(VocabularyFilter *model, const std::shared_ptr<KEduVocDocument> &doc, QWidget *parent)
: QWidget(parent)
, m_doc(doc)
, m_wordTypeModel(0)
......@@ -93,7 +93,7 @@ void SummaryWordWidget::setTranslation(KEduVocExpression *entry, int translation
m_translationId = translation;
}
void SummaryWordWidget::slotDocumentChanged(KEduVocDocument *doc)
void SummaryWordWidget::slotDocumentChanged(const std::shared_ptr<KEduVocDocument> &doc)
{
m_doc = doc;
if (!m_doc) {
......
......@@ -17,6 +17,8 @@
#include "ui_summarywordwidget.h"
#include <memory>
#include <QItemDelegate>
#include <QWidget>
......@@ -48,7 +50,7 @@ class SummaryWordWidget : public QWidget, public Ui::SummaryWordWidget
Q_OBJECT
public:
SummaryWordWidget(VocabularyFilter *model, KEduVocDocument *doc, QWidget *parent = 0);
SummaryWordWidget(VocabularyFilter *model, const std::shared_ptr<KEduVocDocument> &, QWidget *parent = 0);
public slots:
/**
......@@ -59,7 +61,7 @@ public slots:
/**
* Called when a KEduVocDocument change happened
*/
void slotDocumentChanged(KEduVocDocument *doc);
void slotDocumentChanged(const std::shared_ptr<KEduVocDocument> &doc);
/**
* Called when the selection changed in the vocabulary view
......@@ -86,7 +88,7 @@ private:
private:
VocabularyFilter *m_model;
KEduVocDocument *m_doc;
std::shared_ptr<KEduVocDocument> m_doc;
QDataWidgetMapper *m_mapper;
WordClassModel *m_wordTypeModel;
QTreeView *m_wordTypeView;
......
......@@ -39,7 +39,7 @@
using namespace Editor;
VocabularyDelegate::VocabularyDelegate(QObject *parent)
: QItemDelegate(parent), m_doc(0), m_translator(0)
: QItemDelegate(parent), m_translator(0)
{
}
......@@ -327,7 +327,7 @@ void VocabularyDelegate::setModelData(QWidget * editor, QAbstractItemModel * mod
}
}
void VocabularyDelegate::setDocument(KEduVocDocument * doc)
void VocabularyDelegate::setDocument(const std::shared_ptr<KEduVocDocument> &doc)
{
m_doc = doc;
}
......
......@@ -65,11 +65,11 @@ public:
QPolygon imagePolygon(const QStyleOptionViewItem &option) const;
public slots:
void setDocument(KEduVocDocument *doc);
void setDocument(const std::shared_ptr<KEduVocDocument> &doc);
bool helpEvent(QHelpEvent *event, QAbstractItemView *view, const QStyleOptionViewItem &option, const QModelIndex &index ) Q_DECL_OVERRIDE;
private:
KEduVocDocument *m_doc;
std::shared_ptr<KEduVocDocument> m_doc;
Translator * m_translator;
/** Returns the translations of the word of the given index */
......
......@@ -52,7 +52,7 @@
using namespace Editor;
VocabularyView::VocabularyView(EditorWindow * parent)
: QTableView(parent), m_model(0), m_doc(0),
: QTableView(parent), m_model(0),
m_spellChecker(0), m_spellDialog(0)
{
installEventFilter(this);
......@@ -350,7 +350,7 @@ void VocabularyView::slotSelectionChanged(const QItemSelection &, const QItemSel
m_cutAction->setEnabled(hasSelection);
}
void VocabularyView::setDocument(KEduVocDocument * doc)
void VocabularyView::setDocument(const std::shared_ptr<KEduVocDocument> &doc)
{
m_doc = doc;
m_vocabularyDelegate->setDocument(doc);
......@@ -368,7 +368,7 @@ void VocabularyView::setTranslator(Translator* translator)
void VocabularyView::slotShowVocabularyColumnsDialog()
{
VocabularyColumnsDialog *dialog = new VocabularyColumnsDialog(m_doc, this);
VocabularyColumnsDialog *dialog = new VocabularyColumnsDialog(m_doc.get(), this);
if (dialog->exec() == QDialog::Accepted) {
reset();
......
......@@ -17,6 +17,7 @@
#ifndef VOCABULARYVIEW_H
#define VOCABULARYVIEW_H
#include <memory>
#include <sonnet/dialog.h>
// Qt
......@@ -80,7 +81,7 @@ public slots:
*/
void appendChar(const QChar & c);
void setDocument(KEduVocDocument * doc);
void setDocument(const std::shared_ptr<KEduVocDocument> &doc);
void setTranslator(Translator* translator);
......@@ -122,7 +123,7 @@ private:
VocabularyFilter* m_model;
VocabularyDelegate* m_vocabularyDelegate;
KEduVocDocument *m_doc;
std::shared_ptr<KEduVocDocument> m_doc;
int m_spellColumn;
int m_spellRow;
......
......@@ -80,7 +80,7 @@ ParleyDocument::ParleyDocument(ParleyMainWindow* parleyMainWindow)
: QObject(parleyMainWindow)
, m_parleyApp(parleyMainWindow)
, m_backupTimer(0)
, m_doc(new KEduVocDocument(this))
, m_doc(new KEduVocDocument, [](KEduVocDocument *p){p->deleteLater();})
{
}
......@@ -90,11 +90,11 @@ ParleyDocument::~ParleyDocument()
}
KEduVocDocument *ParleyDocument::document()
std::shared_ptr<KEduVocDocument> ParleyDocument::document()
{
// If there is no present vocabulary document, create an empty one.
if (!m_doc) {
m_doc = new KEduVocDocument(this);
m_doc.reset(new KEduVocDocument, [](KEduVocDocument *p){p->deleteLater();});
}
return m_doc;
......@@ -114,14 +114,14 @@ void ParleyDocument::slotFileNew()
void ParleyDocument::newDocument(bool wizard)
{
KEduVocDocument *newDoc = new KEduVocDocument(this);
std::shared_ptr<KEduVocDocument> newDoc(new KEduVocDocument, [](KEduVocDocument *p){p->deleteLater();});
initializeDefaultGrammar(newDoc);
setDefaultDocumentProperties(newDoc);
initializeDefaultGrammar(newDoc.get());
setDefaultDocumentProperties(newDoc.get());
bool showGrammarDialog = false;
bool fetchGrammarOnline = false;
if (wizard) {
DocumentProperties* titleAuthorWidget = new DocumentProperties(newDoc, true, m_parleyApp);
DocumentProperties* titleAuthorWidget = new DocumentProperties(newDoc.get(), true, m_parleyApp);
QDialogButtonBox * button_dialog = new QDialogButtonBox;
button_dialog->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
......@@ -144,22 +144,18 @@ void ParleyDocument::newDocument(bool wizard)
delete titleAuthorDialog;
} else {
delete titleAuthorDialog;
delete newDoc;
return;
}
}
close();
if (m_doc) {
m_doc->deleteLater();
}
m_doc = newDoc;
m_doc = std::move(newDoc);
emit documentChanged(m_doc);
enableAutoBackup(Prefs::autoBackup());
if (fetchGrammarOnline) {
DocumentHelper::fetchGrammar(m_doc, 0);
DocumentHelper::fetchGrammar(m_doc, 1);
DocumentHelper::fetchGrammar(m_doc.get(), 0);
DocumentHelper::fetchGrammar(m_doc.get(), 1);
}
if (showGrammarDialog) {
languageProperties();
......@@ -205,10 +201,7 @@ bool ParleyDocument::open(const QUrl & url)
close();
if (m_doc) {
m_doc->deleteLater();
}
m_doc = new KEduVocDocument(this);
m_doc.reset(new KEduVocDocument, [](KEduVocDocument *p){p->deleteLater();});
emit documentChanged(m_doc);
m_doc->setCsvDelimiter(Prefs::separator());
......@@ -254,9 +247,8 @@ bool ParleyDocument::open(const QUrl & url)
m_parleyApp, i18n("Opening collection \"%1\" resulted in an error: %2", m_doc->url().url(),
m_doc->errorDescription(ret)), i18nc("@title:window", "Open Collection"));
}
m_doc->deleteLater();
emit documentChanged(0);
m_doc = 0;
m_doc.reset();
emit documentChanged(m_doc);
}
return isSuccess;
......@@ -266,15 +258,14 @@ void ParleyDocument::close()
{
enableAutoBackup(false);
if (m_doc) {
emit documentChanged(0);
m_doc->deleteLater();
m_doc = 0;
m_doc.reset();
emit documentChanged(m_doc);
}
}
bool ParleyDocument::queryClose()
{
if ( (m_doc == NULL ) || !m_doc->isModified()) {
if ( !m_doc || !m_doc->isModified()) {
return true;
}
......@@ -580,7 +571,7 @@ void ParleyDocument::slotGHNS()
void ParleyDocument::documentProperties()
{
DocumentProperties* titleAuthorWidget = new DocumentProperties(m_doc, false, m_parleyApp);
DocumentProperties* titleAuthorWidget = new DocumentProperties(m_doc.get(), false, m_parleyApp);
QDialogButtonBox * button_dialog = new QDialogButtonBox;
button_dialog->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
......@@ -607,7 +598,7 @@ void ParleyDocument::documentProperties()
void ParleyDocument::languageProperties()
{
LanguageProperties properties(m_doc, m_parleyApp);
LanguageProperties properties(m_doc.get(), m_parleyApp);
if (properties.exec() == QDialog::Accepted) {
emit languagesChanged();
}
......
......@@ -15,6 +15,7 @@
#ifndef PARLEYDOCUMENT_H
#define PARLEYDOCUMENT_H
#include <memory>
#include <KEduVocDocument>
#include <QObject>
......@@ -34,7 +35,7 @@ public:
~ParleyDocument();
KEduVocDocument *document();
std::shared_ptr<KEduVocDocument> document();
void setTitle(const QString& title);
......@@ -86,7 +87,7 @@ public slots:
signals:
/** Emitted when the document pointer is changed.
@todo Stop using documentChanged(0) as a replacement for destroyed in editor classes.**/
void documentChanged(KEduVocDocument *newDocument);
void documentChanged(const std::shared_ptr<KEduVocDocument> &newDocument);
void languagesChanged();
void statesNeedSaving();
......@@ -99,7 +100,7 @@ private:
QTimer *m_backupTimer; // Timer for next autosave
// The contents of the document
KEduVocDocument *m_doc;
std::shared_ptr<KEduVocDocument> m_doc;
};
#endif
......@@ -126,12 +126,12 @@ void ParleyMainWindow::removeRecentFile(const QUrl &url)
m_recentFilesAction->saveEntries(KSharedConfig::openConfig()->group("Recent Files"));
}
void ParleyMainWindow::documentUpdated(KEduVocDocument *doc)
void ParleyMainWindow::documentUpdated(const std::shared_ptr<KEduVocDocument> &doc)
{
if (doc != nullptr) {
connect(doc, &KEduVocDocument::docModified,
if (doc) {
connect(doc.get(), &KEduVocDocument::docModified,
this, &ParleyMainWindow::slotUpdateWindowCaption);
connect(doc, &KEduVocDocument::destroyed,
connect(doc.get(), &KEduVocDocument::destroyed,
this, &ParleyMainWindow::slotUpdateWindowCaption);
slotUpdateWindowCaption();
}
......@@ -164,7 +164,7 @@ void ParleyMainWindow::slotUpdateWindowCaption()
void ParleyMainWindow::slotGeneralOptions()
{
ParleyPrefs* dialog = new ParleyPrefs(m_document->document(), this, QStringLiteral("settings"), Prefs::self());
ParleyPrefs* dialog = new ParleyPrefs(m_document->document().get(), this, QStringLiteral("settings"), Prefs::self());
connect(dialog, &ParleyPrefs::settingsChanged, this, &ParleyMainWindow::preferencesChanged);
dialog->show();
}
......@@ -185,7 +185,7 @@ void ParleyMainWindow::slotCloseDocument()
void ParleyMainWindow::configurePractice()
{
ConfigurePracticeDialog configurePracticeDialog(m_document->document(), this, QStringLiteral("practice settings"), Prefs::self());
ConfigurePracticeDialog configurePracticeDialog(m_document->document().get(), this, QStringLiteral("practice settings"), Prefs::self());
configurePracticeDialog.exec();
}
......@@ -299,7 +299,7 @@ void ParleyMainWindow::switchComponent(Component component)
// Don't start a practice when there are no words to practice.
// This has to be checked before deleting the old component.
m_sessionManager.setDocument(m_document->document());
m_sessionManager.setDocument(m_document->document().get());
if (!m_sessionManager.allEntryCount()) {
return;
}
......
......@@ -88,7 +88,7 @@ public:
public slots:
/** Updates connections when the ParleyDocument pointer is changed to @p doc **/
void documentUpdated(KEduVocDocument *doc);
void documentUpdated(const std::shared_ptr<KEduVocDocument> &doc);
/** Opens a dialog for a new collection. **/
void slotFileNew();
......
......@@ -67,29 +67,29 @@ void PracticeStateMachine::createPracticeMode()
case Prefs::EnumPracticeMode::MixedLettersPractice:
qDebug() << "Create Mixed Letters Practice backend";
m_frontend->setMode(AbstractFrontend::MixedLetters);
m_mode = new WrittenBackendMode(m_frontend, this, m_sessionManager, m_document->document());
m_mode = new WrittenBackendMode(m_frontend, this, m_sessionManager, m_document->document().get());
break;
case Prefs::EnumPracticeMode::WrittenPractice:
qDebug() << "Create Written Practice backend";
m_frontend->setMode(AbstractFrontend::Written);
m_mode = new WrittenBackendMode(m_frontend, this, m_sessionManager, m_document->document());
m_mode = new WrittenBackendMode(m_frontend, this, m_sessionManager, m_document->document().get());
break;
case Prefs::EnumPracticeMode::ExampleSentencesPractice:
qDebug() << "Create Written Practice backend";
m_frontend->setMode(AbstractFrontend::ExampleSentence);
m_mode = new ExampleSentenceBackendMode(m_frontend, this, m_sessionManager, m_document->document());
m_mode = new ExampleSentenceBackendMode(m_frontend, this, m_sessionManager, m_document->document().get());
break;
case Prefs::EnumPracticeMode::GenderPractice:
m_frontend->setMode(AbstractFrontend::MultipleChoice);
m_mode = new GenderBackendMode(m_frontend, this, m_sessionManager, m_document->document());
m_mode = new GenderBackendMode(m_frontend, this, m_sessionManager, m_document->document().get());
break;
case Prefs::EnumPracticeMode::ConjugationPractice:
m_frontend->setMode(AbstractFrontend::Conjugation);
m_mode = new ConjugationBackendMode(m_frontend, this, m_sessionManager, m_document->document());
m_mode = new ConjugationBackendMode(m_frontend, this, m_sessionManager, m_document->document().get());
break;
case Prefs::EnumPracticeMode::ComparisonPractice:
m_frontend->setMode(AbstractFrontend::Comparison);
m_mode = new ComparisonBackendMode(m_frontend, this, m_sessionManager, m_document->document());
m_mode = new ComparisonBackendMode(m_frontend, this, m_sessionManager, m_document->document().get());
break;
default:
......
......@@ -40,7 +40,7 @@ namespace Scripting
Parley::Parley(EditorWindow * editor) : QObject(), m_editor(editor)
{
m_translator = new Translator(this); //parameter has to be <this> cause it's used by Translator to access callTranslateWord
m_doc = new Document(m_editor->m_mainWindow->parleyDocument()->document());
m_doc = new Document(m_editor->m_mainWindow->parleyDocument()->document().get());
}
Parley::~Parley()
......
......@@ -188,7 +188,7 @@ void LessonStatisticsView::saveExpandedStatus() const
QStringList collapsedItems;
getCollapsedItems(collapsedItems, statisticsModel->index(0, 0, QModelIndex()), QString());
const KEduVocDocument *doc = statisticsModel->document();
const KEduVocDocument *doc = statisticsModel->document().get();
if (doc != nullptr) {
DocumentSettings documentSettings(doc->url().url());
documentSettings.setCollapsedStatisticsViewItems(collapsedItems);
......@@ -221,7 +221,7 @@ void LessonStatisticsView::restoreExpandedStatus()
auto statisticsModel = qobject_cast<StatisticsModel *>(model());
Q_ASSERT(statisticsModel != nullptr);
const KEduVocDocument *doc = statisticsModel->document();
const KEduVocDocument *doc = statisticsModel->document().get();
if (doc != nullptr) {
DocumentSettings documentSettings(doc->url().url());
documentSettings.load();
......
......@@ -37,7 +37,8 @@
#include "conjugationoptions.h"
StatisticsMainWindow::StatisticsMainWindow(KEduVocDocument *doc, ParleyMainWindow *parent)
StatisticsMainWindow::StatisticsMainWindow(const std::shared_ptr<KEduVocDocument> &doc,
ParleyMainWindow *parent)
: KXmlGuiWindow(parent)
, m_mainWindow(parent)
, m_doc(doc)
......@@ -83,7 +84,7 @@ void StatisticsMainWindow::syncConfig()
}
}
void StatisticsMainWindow::setDocument(KEduVocDocument* doc)
void StatisticsMainWindow::setDocument(const std::shared_ptr<KEduVocDocument> &doc)
{
m_doc = doc;
m_statisticsModel->setDocument(doc);
......@@ -287,7 +288,7 @@ void StatisticsMainWindow::showConjugationOptions(bool visible)
}
if (!m_conjugationOptions) {
m_conjugationOptions = new ConjugationOptions(m_doc, m_ui->modeSpecificOptions);
m_conjugationOptions = new ConjugationOptions(m_doc.get(), m_ui->modeSpecificOptions);
QHBoxLayout* layout = new QHBoxLayout(m_ui->modeSpecificOptions);
layout->setContentsMargins(0, 0, 0, 0);
layout->addWidget(m_conjugationOptions);
......@@ -302,7 +303,7 @@ void StatisticsMainWindow::showConjugationOptions(bool visible)
void StatisticsMainWindow::configurePractice()
{
ConfigurePracticeDialog dialog(m_doc, this, QStringLiteral("practice settings"), Prefs::self());
ConfigurePracticeDialog dialog(m_doc.get(), this, QStringLiteral("practice settings"), Prefs::self());
dialog.exec();
}
......
......@@ -16,6 +16,8 @@
#ifndef STATISTICSMAINWINDOW_H
#define STATISTICSMAINWINDOW_H
#include <memory>
#include <KXmlGuiWindow>
class ConjugationOptions;
......@@ -32,10 +34,10 @@ class StatisticsMainWindow : public KXmlGuiWindow
Q_OBJECT
public: