Show UnsavedDocumentsDialog when closing window or quitting application
so user can save and or discard docs more efficiently.
This commit is contained in:
parent
b97adced4c
commit
784ad68758
16 changed files with 544 additions and 164 deletions
|
|
@ -21,12 +21,16 @@
|
|||
#include "util.h"
|
||||
#include "UserConfiguration.h"
|
||||
#include "IDatabaseWindow.h"
|
||||
#include "UnsavedDocumentsDialog.h"
|
||||
#include <QPlainTextDocumentLayout>
|
||||
#include <QTextDocument>
|
||||
|
||||
|
||||
QueryTool::QueryTool(IDatabaseWindow *context, QWidget *parent)
|
||||
QueryTool::QueryTool(IDatabaseWindow *context, std::shared_ptr<SqlDocument> document, QWidget *parent)
|
||||
: ManagedPage(parent)
|
||||
, m_context(context)
|
||||
, ui(new Ui::QueryTab)
|
||||
, m_document(std::move(document))
|
||||
, m_dbConnection()
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
|
@ -40,13 +44,15 @@ QueryTool::QueryTool(IDatabaseWindow *context, QWidget *parent)
|
|||
|
||||
ui->queryEdit->setFont(UserConfiguration::instance()->codeFont());
|
||||
|
||||
highlighter = new SqlSyntaxHighlighter(ui->queryEdit->document());
|
||||
ui->queryEdit->setDocument(m_document->textDocument());
|
||||
m_document->addEditor(this);
|
||||
auto types = m_catalog->types();
|
||||
if (types) {
|
||||
highlighter->setTypes(*types);
|
||||
m_document->highlighter()->setTypes(*types);
|
||||
}
|
||||
|
||||
connect(ui->queryEdit, &QPlainTextEdit::textChanged, this, &QueryTool::queryTextChanged);
|
||||
connect(m_document.get(), &SqlDocument::modifiedChanged, this, &QueryTool::updateTitle);
|
||||
connect(m_document.get(), &SqlDocument::fileNameChanged, this, &QueryTool::updateTitle);
|
||||
ui->queryEdit->setAcceptDrops(false);
|
||||
|
||||
m_queryParamListController = new QueryParamListController(ui->paramTableView, m_context->openDatabase(), this);
|
||||
|
|
@ -60,87 +66,42 @@ QueryTool::QueryTool(IDatabaseWindow *context, QWidget *parent)
|
|||
|
||||
QueryTool::~QueryTool()
|
||||
{
|
||||
// Detach the editor from the shared document, it may be destroyed together with this tool
|
||||
auto detached = new QTextDocument(ui->queryEdit);
|
||||
detached->setDocumentLayout(new QPlainTextDocumentLayout(detached));
|
||||
ui->queryEdit->setDocument(detached);
|
||||
m_document->removeEditor(this);
|
||||
delete ui;
|
||||
}
|
||||
|
||||
bool QueryTool::CanClose(bool prompt_user)
|
||||
{
|
||||
bool can_close;
|
||||
if (m_queryTextChanged) {
|
||||
if (prompt_user)
|
||||
can_close = continueWithoutSavingWarning();
|
||||
else
|
||||
can_close = false;
|
||||
}
|
||||
else {
|
||||
can_close = true;
|
||||
}
|
||||
return can_close;
|
||||
}
|
||||
if (!m_document->isModified())
|
||||
return true;
|
||||
|
||||
void QueryTool::newdoc()
|
||||
{
|
||||
ui->queryEdit->clear();
|
||||
setFileName(tr("new"));
|
||||
setQueryTextChanged(false);
|
||||
m_new = true;
|
||||
}
|
||||
// The document stays open in another editor, so it is not closed with this tool
|
||||
if (m_document->editors().size() > 1)
|
||||
return true;
|
||||
|
||||
bool QueryTool::load(const QString &filename)
|
||||
{
|
||||
bool result = false;
|
||||
QFile file(filename);
|
||||
if (file.open(QIODevice::ReadOnly)) {
|
||||
QByteArray ba = file.readAll();
|
||||
auto toUtf16 = QStringDecoder(QStringDecoder::Utf8);
|
||||
QString text = toUtf16(ba);
|
||||
if (toUtf16.hasError()) {
|
||||
file.reset();
|
||||
QTextStream stream(&file);
|
||||
text = stream.readAll();
|
||||
}
|
||||
if (!prompt_user)
|
||||
return false;
|
||||
|
||||
ui->queryEdit->setPlainText(text);
|
||||
setQueryTextChanged(false);
|
||||
setFileName(filename);
|
||||
m_new = false;
|
||||
result = true;
|
||||
}
|
||||
return result;
|
||||
return confirmClose(this, {m_document});
|
||||
}
|
||||
|
||||
bool QueryTool::save()
|
||||
{
|
||||
bool result;
|
||||
if (m_fileName.isEmpty() || m_new) {
|
||||
result = saveAs();
|
||||
}
|
||||
else {
|
||||
result = saveSqlTo(m_fileName);
|
||||
}
|
||||
return result;
|
||||
return m_document->save(this);
|
||||
}
|
||||
|
||||
bool QueryTool::saveAs()
|
||||
{
|
||||
bool result = false;
|
||||
QString filename = promptUserForSaveSqlFilename();
|
||||
if (!filename.isEmpty()) {
|
||||
result = saveSqlTo(filename);
|
||||
if (result) {
|
||||
setFileName(filename);
|
||||
m_new = false;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
return m_document->saveAs(this);
|
||||
}
|
||||
|
||||
void QueryTool::saveCopyAs()
|
||||
{
|
||||
QString filename = promptUserForSaveSqlFilename();
|
||||
if (!filename.isEmpty()) {
|
||||
saveSqlTo(filename);
|
||||
}
|
||||
m_document->saveCopyAs(this);
|
||||
}
|
||||
|
||||
void QueryTool::execute()
|
||||
|
|
@ -245,81 +206,21 @@ void QueryTool::cancel()
|
|||
|
||||
QString QueryTool::title() const
|
||||
{
|
||||
QFileInfo fileInfo(m_fileName);
|
||||
QFileInfo fileInfo(m_document->fileName());
|
||||
QString fn(fileInfo.fileName());
|
||||
return fn;
|
||||
}
|
||||
|
||||
void QueryTool::setFileName(const QString &filename)
|
||||
{
|
||||
m_fileName = filename;
|
||||
updateTitle();
|
||||
}
|
||||
|
||||
void QueryTool::updateTitle()
|
||||
{
|
||||
QFileInfo fileInfo(m_fileName);
|
||||
QFileInfo fileInfo(m_document->fileName());
|
||||
QString title = fileInfo.fileName();
|
||||
|
||||
if (m_queryTextChanged) {
|
||||
if (m_document->isModified()) {
|
||||
title = "*" + title;
|
||||
}
|
||||
|
||||
m_context->setTitleForWidget(this, title, m_fileName);
|
||||
}
|
||||
|
||||
bool QueryTool::continueWithoutSavingWarning()
|
||||
{
|
||||
QMessageBox msgBox;
|
||||
msgBox.setIcon(QMessageBox::Warning);
|
||||
msgBox.setText(QString("Save changes in document \"%1\" before closing?").arg(m_fileName));
|
||||
msgBox.setInformativeText("The changes will be lost when you choose Discard.");
|
||||
msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
|
||||
msgBox.setDefaultButton(QMessageBox::Cancel);
|
||||
int ret = msgBox.exec();
|
||||
if (ret == QMessageBox::Save) {
|
||||
if (!save()) {
|
||||
// save failed or was a saveAs and was cancelled, don't close!
|
||||
ret = QMessageBox::Cancel;
|
||||
}
|
||||
}
|
||||
|
||||
return ret != QMessageBox::Cancel;
|
||||
}
|
||||
|
||||
bool QueryTool::saveSqlTo(const QString &filename)
|
||||
{
|
||||
bool result = false;
|
||||
QFileInfo fileinfo(filename);
|
||||
|
||||
|
||||
QFile file(filename);
|
||||
if (file.open(QIODevice::WriteOnly)) {
|
||||
QTextStream stream(&file);
|
||||
//stream.setCodec("utf-8");
|
||||
stream.setEncoding(QStringConverter::Utf8);
|
||||
QString text = ui->queryEdit->toPlainText();
|
||||
stream << text;
|
||||
|
||||
stream.flush();
|
||||
if (stream.status() == QTextStream::Ok) {
|
||||
setQueryTextChanged(false);
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
QString QueryTool::promptUserForSaveSqlFilename()
|
||||
{
|
||||
QString home_dir = QStandardPaths::locate(QStandardPaths::HomeLocation, "", QStandardPaths::LocateDirectory);
|
||||
return QFileDialog::getSaveFileName(this, tr("Save query"), home_dir, tr("SQL file (*.sql)"));
|
||||
}
|
||||
|
||||
void QueryTool::queryTextChanged()
|
||||
{
|
||||
setQueryTextChanged(true);
|
||||
m_context->setTitleForWidget(this, title, m_document->fileName());
|
||||
}
|
||||
|
||||
void QueryTool::connectionStateChanged()
|
||||
|
|
@ -618,10 +519,3 @@ void QueryTool::exportData()
|
|||
exportDataToFilename(file_name);
|
||||
}
|
||||
|
||||
void QueryTool::setQueryTextChanged(bool queryTextChanged)
|
||||
{
|
||||
if (m_queryTextChanged != queryTextChanged) {
|
||||
m_queryTextChanged = queryTextChanged;
|
||||
updateTitle();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#include "QueryExplainModel.h"
|
||||
#include "stopwatch.h"
|
||||
#include "tuplesresultwidget.h"
|
||||
#include "SqlDocument.h"
|
||||
|
||||
#include <QWidget>
|
||||
#include <memory>
|
||||
|
|
@ -34,12 +35,9 @@ class PgDatabaseCatalog;
|
|||
class QueryTool : public ManagedPage {
|
||||
Q_OBJECT
|
||||
public:
|
||||
QueryTool(IDatabaseWindow *context, QWidget *parent = nullptr);
|
||||
QueryTool(IDatabaseWindow *context, std::shared_ptr<SqlDocument> document, QWidget *parent = nullptr);
|
||||
~QueryTool() override;
|
||||
|
||||
void newdoc();
|
||||
bool load(const QString &filename);
|
||||
|
||||
void explain(bool analyze);
|
||||
|
||||
bool CanClose(bool prompt_user);
|
||||
|
|
@ -47,12 +45,12 @@ public:
|
|||
void generateCode();
|
||||
void exportDataToFilename(const QString &filename);
|
||||
|
||||
QString fileName() const { return m_fileName; }
|
||||
bool isChanged() const { return m_queryTextChanged; }
|
||||
bool isNew() const { return m_new; }
|
||||
QString fileName() const { return m_document->fileName(); }
|
||||
std::shared_ptr<SqlDocument> document() const { return m_document; }
|
||||
void focusEditor();
|
||||
|
||||
QString title() const;
|
||||
void updateTitle();
|
||||
|
||||
|
||||
public slots:
|
||||
|
|
@ -74,15 +72,12 @@ private:
|
|||
using ResultTabContainer = std::vector<TuplesResultWidget*>;
|
||||
IDatabaseWindow *m_context;
|
||||
Ui::QueryTab *ui;
|
||||
SqlSyntaxHighlighter* highlighter;
|
||||
std::shared_ptr<SqlDocument> m_document;
|
||||
ConnectionConfig m_config;
|
||||
StopWatch m_stopwatch;
|
||||
|
||||
QueryParamListController *m_queryParamListController = nullptr;
|
||||
|
||||
bool m_new = true;
|
||||
QString m_fileName; ///< use setFileName function to set
|
||||
bool m_queryTextChanged = false;
|
||||
|
||||
std::shared_ptr<PgDatabaseCatalog> m_catalog;
|
||||
ASyncDBConnection m_dbConnection;
|
||||
|
|
@ -90,11 +85,6 @@ private:
|
|||
std::unique_ptr<QueryExplainModel> explainModel;
|
||||
ResultTabContainer resultList;
|
||||
|
||||
void setFileName(const QString &filename);
|
||||
bool continueWithoutSavingWarning();
|
||||
bool saveSqlTo(const QString &filename);
|
||||
QString promptUserForSaveSqlFilename();
|
||||
|
||||
void addLog(QString s);
|
||||
|
||||
QString getCommand() const;
|
||||
|
|
@ -102,15 +92,12 @@ private:
|
|||
|
||||
void clearResult();
|
||||
void markError(const Pgsql::ErrorDetails &details);
|
||||
void updateTitle();
|
||||
void setQueryTextChanged(bool queryTextChanged);
|
||||
|
||||
private slots:
|
||||
|
||||
void explain_ready(ExplainRoot::SPtr explain);
|
||||
void query_ready(std::shared_ptr<Pgsql::Result>, qint64 elapsedms);
|
||||
|
||||
void queryTextChanged();
|
||||
void connectionStateChanged();
|
||||
void receiveNotice(Pgsql::ErrorDetails notice);
|
||||
|
||||
|
|
|
|||
143
pglab/querytool/SqlDocument.cpp
Normal file
143
pglab/querytool/SqlDocument.cpp
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
#include "SqlDocument.h"
|
||||
#include "util/SqlSyntaxHighlighter.h"
|
||||
#include <QFile>
|
||||
#include <QFileDialog>
|
||||
#include <QFileInfo>
|
||||
#include <QStandardPaths>
|
||||
#include <QStringConverter>
|
||||
#include <QPlainTextDocumentLayout>
|
||||
#include <QTextDocument>
|
||||
#include <QTextStream>
|
||||
|
||||
SqlDocument::SqlDocument(QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_document(new QTextDocument(this))
|
||||
, m_highlighter(new SqlSyntaxHighlighter(m_document))
|
||||
{
|
||||
// QPlainTextEdit only accepts documents that use a QPlainTextDocumentLayout
|
||||
m_document->setDocumentLayout(new QPlainTextDocumentLayout(m_document));
|
||||
connect(m_document, &QTextDocument::modificationChanged, this, &SqlDocument::modifiedChanged);
|
||||
setFileName(tr("new"));
|
||||
}
|
||||
|
||||
SqlDocument::~SqlDocument() = default;
|
||||
|
||||
bool SqlDocument::isModified() const
|
||||
{
|
||||
return m_document->isModified();
|
||||
}
|
||||
|
||||
void SqlDocument::setModified(bool modified)
|
||||
{
|
||||
m_document->setModified(modified);
|
||||
}
|
||||
|
||||
void SqlDocument::clear()
|
||||
{
|
||||
m_document->clear();
|
||||
setFileName(tr("new"));
|
||||
m_new = true;
|
||||
setModified(false);
|
||||
}
|
||||
|
||||
bool SqlDocument::load(const QString &filename)
|
||||
{
|
||||
bool result = false;
|
||||
QFile file(filename);
|
||||
if (file.open(QIODevice::ReadOnly)) {
|
||||
QByteArray ba = file.readAll();
|
||||
auto toUtf16 = QStringDecoder(QStringDecoder::Utf8);
|
||||
QString text = toUtf16(ba);
|
||||
if (toUtf16.hasError()) {
|
||||
file.reset();
|
||||
QTextStream stream(&file);
|
||||
text = stream.readAll();
|
||||
}
|
||||
|
||||
m_document->setPlainText(text);
|
||||
setModified(false);
|
||||
setFileName(filename);
|
||||
m_new = false;
|
||||
result = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool SqlDocument::save(QWidget *parent)
|
||||
{
|
||||
bool result;
|
||||
if (m_fileName.isEmpty() || m_new) {
|
||||
result = saveAs(parent);
|
||||
}
|
||||
else {
|
||||
result = saveSqlTo(m_fileName);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool SqlDocument::saveAs(QWidget *parent)
|
||||
{
|
||||
bool result = false;
|
||||
QString filename = promptUserForSaveSqlFilename(parent);
|
||||
if (!filename.isEmpty()) {
|
||||
result = saveSqlTo(filename);
|
||||
if (result) {
|
||||
setFileName(filename);
|
||||
m_new = false;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool SqlDocument::saveCopyAs(QWidget *parent)
|
||||
{
|
||||
bool result = false;
|
||||
QString filename = promptUserForSaveSqlFilename(parent);
|
||||
if (!filename.isEmpty()) {
|
||||
result = saveSqlTo(filename);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void SqlDocument::addEditor(QWidget *editor)
|
||||
{
|
||||
if (!m_editors.contains(editor))
|
||||
m_editors.append(editor);
|
||||
}
|
||||
|
||||
void SqlDocument::removeEditor(QWidget *editor)
|
||||
{
|
||||
m_editors.removeAll(editor);
|
||||
}
|
||||
|
||||
void SqlDocument::setFileName(const QString &filename)
|
||||
{
|
||||
m_fileName = filename;
|
||||
emit fileNameChanged();
|
||||
}
|
||||
|
||||
bool SqlDocument::saveSqlTo(const QString &filename)
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
QFile file(filename);
|
||||
if (file.open(QIODevice::WriteOnly)) {
|
||||
QTextStream stream(&file);
|
||||
stream.setEncoding(QStringConverter::Utf8);
|
||||
QString text = m_document->toPlainText();
|
||||
stream << text;
|
||||
|
||||
stream.flush();
|
||||
if (stream.status() == QTextStream::Ok) {
|
||||
setModified(false);
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QString SqlDocument::promptUserForSaveSqlFilename(QWidget *parent)
|
||||
{
|
||||
QString home_dir = QStandardPaths::locate(QStandardPaths::HomeLocation, "", QStandardPaths::LocateDirectory);
|
||||
return QFileDialog::getSaveFileName(parent, tr("Save query"), home_dir, tr("SQL file (*.sql)"));
|
||||
}
|
||||
60
pglab/querytool/SqlDocument.h
Normal file
60
pglab/querytool/SqlDocument.h
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
#ifndef SQLDOCUMENT_H
|
||||
#define SQLDOCUMENT_H
|
||||
|
||||
#include <QList>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
|
||||
class QTextDocument;
|
||||
class QWidget;
|
||||
class SqlSyntaxHighlighter;
|
||||
|
||||
/** Owns the text of a SQL script together with its file state.
|
||||
* Several query tools (editors) can show the same document, they all
|
||||
* use the same QTextDocument.
|
||||
*/
|
||||
class SqlDocument : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SqlDocument(QObject *parent = nullptr);
|
||||
~SqlDocument() override;
|
||||
|
||||
QTextDocument *textDocument() const { return m_document; }
|
||||
SqlSyntaxHighlighter *highlighter() const { return m_highlighter; }
|
||||
|
||||
QString fileName() const { return m_fileName; }
|
||||
bool isNew() const { return m_new; }
|
||||
bool isModified() const;
|
||||
void setModified(bool modified);
|
||||
|
||||
/// Empties the document and makes it a new unnamed document.
|
||||
void clear();
|
||||
bool load(const QString &filename);
|
||||
/// Save the document under its current name, a file save dialog will be shown if this is a new document
|
||||
bool save(QWidget *parent);
|
||||
/// Saves the document under a new name and continue editing the document under this new name.
|
||||
bool saveAs(QWidget *parent);
|
||||
/// Save the document under a new name but continue editing the document under its old name.
|
||||
bool saveCopyAs(QWidget *parent);
|
||||
|
||||
void addEditor(QWidget *editor);
|
||||
void removeEditor(QWidget *editor);
|
||||
const QList<QWidget*> &editors() const { return m_editors; }
|
||||
|
||||
signals:
|
||||
void modifiedChanged(bool modified);
|
||||
void fileNameChanged();
|
||||
|
||||
private:
|
||||
QTextDocument *m_document;
|
||||
SqlSyntaxHighlighter *m_highlighter;
|
||||
QString m_fileName;
|
||||
bool m_new = true;
|
||||
QList<QWidget*> m_editors;
|
||||
|
||||
void setFileName(const QString &filename);
|
||||
bool saveSqlTo(const QString &filename);
|
||||
QString promptUserForSaveSqlFilename(QWidget *parent);
|
||||
};
|
||||
|
||||
#endif // SQLDOCUMENT_H
|
||||
27
pglab/querytool/SqlDocumentRegistry.cpp
Normal file
27
pglab/querytool/SqlDocumentRegistry.cpp
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
#include "SqlDocumentRegistry.h"
|
||||
#include "SqlDocument.h"
|
||||
|
||||
void SqlDocumentRegistry::add(const std::shared_ptr<SqlDocument> &document)
|
||||
{
|
||||
m_documents.push_back(document);
|
||||
}
|
||||
|
||||
QList<std::shared_ptr<SqlDocument>> SqlDocumentRegistry::documents() const
|
||||
{
|
||||
QList<std::shared_ptr<SqlDocument>> result;
|
||||
for (const auto &weak : m_documents) {
|
||||
if (auto doc = weak.lock())
|
||||
result.append(doc);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QList<std::shared_ptr<SqlDocument>> SqlDocumentRegistry::modifiedDocuments() const
|
||||
{
|
||||
QList<std::shared_ptr<SqlDocument>> result;
|
||||
for (auto &doc : documents()) {
|
||||
if (doc->isModified())
|
||||
result.append(doc);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
27
pglab/querytool/SqlDocumentRegistry.h
Normal file
27
pglab/querytool/SqlDocumentRegistry.h
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
#ifndef SQLDOCUMENTREGISTRY_H
|
||||
#define SQLDOCUMENTREGISTRY_H
|
||||
|
||||
#include <QList>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
class SqlDocument;
|
||||
|
||||
/** Keeps track of all SQL documents that are still in use, without
|
||||
* extending their lifetime. A document lives as long as at least one
|
||||
* query tool holds a shared pointer to it.
|
||||
*/
|
||||
class SqlDocumentRegistry {
|
||||
public:
|
||||
void add(const std::shared_ptr<SqlDocument> &document);
|
||||
|
||||
/// All documents that are still alive.
|
||||
QList<std::shared_ptr<SqlDocument>> documents() const;
|
||||
/// All documents that are alive and have unsaved changes.
|
||||
QList<std::shared_ptr<SqlDocument>> modifiedDocuments() const;
|
||||
|
||||
private:
|
||||
std::vector<std::weak_ptr<SqlDocument>> m_documents;
|
||||
};
|
||||
|
||||
#endif // SQLDOCUMENTREGISTRY_H
|
||||
112
pglab/querytool/UnsavedDocumentsDialog.cpp
Normal file
112
pglab/querytool/UnsavedDocumentsDialog.cpp
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
#include "UnsavedDocumentsDialog.h"
|
||||
#include <QDialogButtonBox>
|
||||
#include <QListWidget>
|
||||
#include <QPushButton>
|
||||
#include <QVBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <algorithm>
|
||||
|
||||
UnsavedDocumentsDialog::UnsavedDocumentsDialog(const QList<std::shared_ptr<SqlDocument>> &docs, QWidget *parent)
|
||||
: QDialog(parent)
|
||||
, m_docs(docs)
|
||||
, m_list(new QListWidget(this))
|
||||
{
|
||||
setWindowTitle(tr("Unsaved changes"));
|
||||
|
||||
auto layout = new QVBoxLayout(this);
|
||||
layout->addWidget(new QLabel(tr("The following documents have unsaved changes. Select the documents to save or discard:"), this));
|
||||
|
||||
for (const auto &doc : m_docs) {
|
||||
QStringList windows;
|
||||
for (const QWidget *editor : doc->editors()) {
|
||||
QString title = editor->window()->windowTitle();
|
||||
if (!windows.contains(title))
|
||||
windows.append(title);
|
||||
}
|
||||
|
||||
auto item = new QListWidgetItem(QString("%1 (%2)").arg(doc->fileName(), windows.join(", ")), m_list);
|
||||
item->setToolTip(doc->fileName());
|
||||
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
|
||||
item->setCheckState(Qt::Checked);
|
||||
}
|
||||
layout->addWidget(m_list);
|
||||
|
||||
auto buttons = new QDialogButtonBox(this);
|
||||
auto save_button = buttons->addButton(tr("Save selected"), QDialogButtonBox::AcceptRole);
|
||||
auto discard_button = buttons->addButton(tr("Discard selected"), QDialogButtonBox::DestructiveRole);
|
||||
buttons->addButton(QDialogButtonBox::Cancel);
|
||||
layout->addWidget(buttons);
|
||||
|
||||
connect(save_button, &QPushButton::clicked, this, [this]() {
|
||||
m_choice = Choice::Save;
|
||||
accept();
|
||||
});
|
||||
connect(discard_button, &QPushButton::clicked, this, [this]() {
|
||||
m_choice = Choice::Discard;
|
||||
accept();
|
||||
});
|
||||
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
}
|
||||
|
||||
QList<std::shared_ptr<SqlDocument>> UnsavedDocumentsDialog::checkedDocuments() const
|
||||
{
|
||||
QList<std::shared_ptr<SqlDocument>> result;
|
||||
for (int i = 0; i < m_docs.size(); ++i) {
|
||||
if (m_list->item(i)->checkState() == Qt::Checked)
|
||||
result.append(m_docs[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QList<std::shared_ptr<SqlDocument>> UnsavedDocumentsDialog::uncheckedDocuments() const
|
||||
{
|
||||
QList<std::shared_ptr<SqlDocument>> result;
|
||||
for (int i = 0; i < m_docs.size(); ++i) {
|
||||
if (m_list->item(i)->checkState() != Qt::Checked)
|
||||
result.append(m_docs[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QList<std::shared_ptr<SqlDocument>> documentsClosedWith(const QList<std::shared_ptr<SqlDocument>> &docs,
|
||||
const std::function<bool(const QWidget*)> &isClosing)
|
||||
{
|
||||
QList<std::shared_ptr<SqlDocument>> result;
|
||||
for (const auto &doc : docs) {
|
||||
const auto &editors = doc->editors();
|
||||
if (!doc->isModified() || editors.isEmpty())
|
||||
continue;
|
||||
|
||||
bool all_closing = std::all_of(editors.begin(), editors.end(),
|
||||
[&isClosing](const QWidget *editor) { return isClosing(editor); });
|
||||
if (all_closing)
|
||||
result.append(doc);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool confirmClose(QWidget *parent, const QList<std::shared_ptr<SqlDocument>> &docs)
|
||||
{
|
||||
auto remaining = docs;
|
||||
while (!remaining.isEmpty()) {
|
||||
UnsavedDocumentsDialog dlg(remaining, parent);
|
||||
if (dlg.exec() != QDialog::Accepted || dlg.choice() == UnsavedDocumentsDialog::Choice::Cancel)
|
||||
return false;
|
||||
|
||||
bool save = dlg.choice() == UnsavedDocumentsDialog::Choice::Save;
|
||||
for (const auto &doc : dlg.checkedDocuments()) {
|
||||
if (save) {
|
||||
if (!doc->save(parent))
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
doc->setModified(false);
|
||||
}
|
||||
}
|
||||
|
||||
// Show the dialog again for the documents the user did not select.
|
||||
remaining = dlg.uncheckedDocuments();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
48
pglab/querytool/UnsavedDocumentsDialog.h
Normal file
48
pglab/querytool/UnsavedDocumentsDialog.h
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
#ifndef UNSAVEDDOCUMENTSDIALOG_H
|
||||
#define UNSAVEDDOCUMENTSDIALOG_H
|
||||
|
||||
#include "SqlDocument.h"
|
||||
#include <QDialog>
|
||||
#include <QList>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
class QListWidget;
|
||||
|
||||
/** Lists modified documents with a checkbox per document. The user picks
|
||||
* whether the checked documents should be saved or discarded.
|
||||
*/
|
||||
class UnsavedDocumentsDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum class Choice {
|
||||
Cancel,
|
||||
Save,
|
||||
Discard
|
||||
};
|
||||
|
||||
UnsavedDocumentsDialog(const QList<std::shared_ptr<SqlDocument>> &docs, QWidget *parent = nullptr);
|
||||
|
||||
Choice choice() const { return m_choice; }
|
||||
QList<std::shared_ptr<SqlDocument>> checkedDocuments() const;
|
||||
QList<std::shared_ptr<SqlDocument>> uncheckedDocuments() const;
|
||||
|
||||
private:
|
||||
QList<std::shared_ptr<SqlDocument>> m_docs;
|
||||
QListWidget *m_list;
|
||||
Choice m_choice = Choice::Cancel;
|
||||
};
|
||||
|
||||
/** Selects the documents that are closed when every editor for which
|
||||
* isClosing returns true is closed. Only modified documents are returned.
|
||||
*/
|
||||
QList<std::shared_ptr<SqlDocument>> documentsClosedWith(const QList<std::shared_ptr<SqlDocument>> &docs,
|
||||
const std::function<bool(const QWidget*)> &isClosing);
|
||||
|
||||
/** Asks the user what to do with the modified documents. Returns true when
|
||||
* the close may continue, i.e. nothing was cancelled, no save failed and
|
||||
* every listed document was checked.
|
||||
*/
|
||||
bool confirmClose(QWidget *parent, const QList<std::shared_ptr<SqlDocument>> &docs);
|
||||
|
||||
#endif // UNSAVEDDOCUMENTSDIALOG_H
|
||||
Loading…
Add table
Add a link
Reference in a new issue