File indexing completed on 2024-04-28 05:49:32

0001 /* This file is part of the KDE project
0002    SPDX-FileCopyrightText: 2004 Joseph Wenninger <jowenn@kde.org>
0003 
0004    SPDX-License-Identifier: LGPL-2.0-only
0005 */
0006 
0007 #include "katesavemodifieddialog.h"
0008 
0009 #include <KGuiItem>
0010 #include <KLocalizedString>
0011 #include <KMessageBox>
0012 #include <KMessageDialog>
0013 #include <KStandardGuiItem>
0014 
0015 #include <QApplication>
0016 #include <QFileDialog>
0017 #include <QGridLayout>
0018 #include <QLabel>
0019 #include <QPushButton>
0020 #include <QTreeWidget>
0021 #include <QVBoxLayout>
0022 
0023 class AbstractKateSaveModifiedDialogCheckListItem : public QTreeWidgetItem
0024 {
0025 public:
0026     AbstractKateSaveModifiedDialogCheckListItem(const QString &title, const QString &url)
0027     {
0028         setFlags(flags() | Qt::ItemIsUserCheckable);
0029         setText(0, title);
0030         setText(1, url);
0031         setCheckState(0, Qt::Checked);
0032         setState(InitialState);
0033     }
0034     ~AbstractKateSaveModifiedDialogCheckListItem() override
0035     {
0036     }
0037     virtual bool synchronousSave(QWidget *dialogParent) = 0;
0038     enum STATE { InitialState, SaveOKState, SaveFailedState };
0039     STATE state() const
0040     {
0041         return m_state;
0042     }
0043     void setState(enum STATE state)
0044     {
0045         m_state = state;
0046         switch (state) {
0047         case InitialState:
0048             setIcon(0, QIcon());
0049             break;
0050         case SaveOKState:
0051             setIcon(0, QIcon::fromTheme(QStringLiteral("dialog-ok")));
0052             // QStringLiteral("ok") icon should probably be QStringLiteral("dialog-success"), but we don't have that icon in KDE 4.0
0053             break;
0054         case SaveFailedState:
0055             setIcon(0, QIcon::fromTheme(QStringLiteral("dialog-error")));
0056             break;
0057         }
0058     }
0059 
0060 private:
0061     STATE m_state = InitialState;
0062 };
0063 
0064 class KateSaveModifiedDocumentCheckListItem : public AbstractKateSaveModifiedDialogCheckListItem
0065 {
0066 public:
0067     explicit KateSaveModifiedDocumentCheckListItem(KTextEditor::Document *document)
0068         : AbstractKateSaveModifiedDialogCheckListItem(document->documentName(), document->url().toString())
0069     {
0070         m_document = document;
0071     }
0072     ~KateSaveModifiedDocumentCheckListItem() override
0073     {
0074     }
0075     bool synchronousSave(QWidget *dialogParent) override
0076     {
0077         if (m_document->url().isEmpty()) {
0078             const QUrl url = QFileDialog::getSaveFileUrl(dialogParent, i18n("Save As (%1)", m_document->documentName()));
0079             if (!url.isEmpty()) {
0080                 if (!m_document->saveAs(url)) {
0081                     setState(SaveFailedState);
0082                     setText(1, m_document->url().toString());
0083                     return false;
0084                 } else {
0085                     bool sc = m_document->waitSaveComplete();
0086                     setText(1, m_document->url().toString());
0087                     if (!sc) {
0088                         setState(SaveFailedState);
0089                         return false;
0090                     } else {
0091                         setState(SaveOKState);
0092                         return true;
0093                     }
0094                 }
0095             } else {
0096                 // setState(SaveFailedState);
0097                 return false;
0098             }
0099         } else {
0100             // document has an existing location
0101             if (!m_document->save()) {
0102                 setState(SaveFailedState);
0103                 setText(1, m_document->url().toString());
0104                 return false;
0105             } else {
0106                 bool sc = m_document->waitSaveComplete();
0107                 setText(1, m_document->url().toString());
0108                 if (!sc) {
0109                     setState(SaveFailedState);
0110                     return false;
0111                 } else {
0112                     setState(SaveOKState);
0113                     return true;
0114                 }
0115             }
0116         }
0117 
0118         return false;
0119     }
0120 
0121 private:
0122     KTextEditor::Document *m_document;
0123 };
0124 
0125 KateSaveModifiedDialog::KateSaveModifiedDialog(QWidget *parent, const std::vector<KTextEditor::Document *> &documents)
0126     : QDialog(parent)
0127 {
0128     const bool multipleDocuments = documents.size() != 1;
0129 
0130     setWindowTitle(multipleDocuments ? i18n("Save Documents") : i18n("Close Document"));
0131     setObjectName(QStringLiteral("KateSaveModifiedDialog"));
0132     setModal(true);
0133 
0134     auto *wrapperLayout = new QGridLayout;
0135     setLayout(wrapperLayout);
0136 
0137     auto *mainLayout = new QVBoxLayout;
0138     wrapperLayout->addLayout(mainLayout, 0, 1);
0139 
0140     // label
0141 
0142     m_label = new QLabel;
0143 
0144     if (!multipleDocuments) {
0145         // Display a simpler dialog, similar to a QMessageBox::warning one
0146 
0147         // Display a "warning" label as QMessageBox does
0148         const auto icon = QApplication::style()->standardIcon(QStyle::SP_MessageBoxWarning);
0149         const auto size = icon.actualSize(QSize(64, 64));
0150         auto *iconLabel = new QLabel;
0151         iconLabel->setPixmap(icon.pixmap(size));
0152         wrapperLayout->addWidget(iconLabel, 0, 0);
0153 
0154         m_label->setText(i18n("The document \"%1\" has been modified. Do you want to save your changes or discard them?", documents.front()->documentName()));
0155         m_label->setWordWrap(true);
0156 
0157     } else {
0158         m_label->setText(i18n("<qt>The following documents have been modified. Do you want to save them before closing?</qt>"));
0159     }
0160 
0161     mainLayout->addWidget(m_label);
0162 
0163     // main view
0164     m_list = new QTreeWidget(this);
0165     mainLayout->addWidget(m_list);
0166     m_list->setColumnCount(2);
0167     m_list->setHeaderLabels(QStringList() << i18n("Documents") << i18n("Location"));
0168     m_list->setRootIsDecorated(true);
0169 
0170     for (KTextEditor::Document *doc : documents) {
0171         m_list->addTopLevelItem(new KateSaveModifiedDocumentCheckListItem(doc));
0172     }
0173     m_list->resizeColumnToContents(0);
0174 
0175     connect(m_list, &QTreeWidget::itemChanged, this, &KateSaveModifiedDialog::slotItemActivated);
0176 
0177     QPushButton *selectAllButton = new QPushButton(i18n("Se&lect All"), this);
0178     mainLayout->addWidget(selectAllButton);
0179     connect(selectAllButton, &QPushButton::clicked, this, &KateSaveModifiedDialog::slotSelectAll);
0180 
0181     if (!multipleDocuments) {
0182         m_list->hide();
0183         selectAllButton->hide();
0184     }
0185 
0186     // dialog buttons
0187     QDialogButtonBox *buttons = new QDialogButtonBox(this);
0188     wrapperLayout->addWidget(buttons, 1, 1);
0189 
0190     m_saveButton = new QPushButton;
0191     KGuiItem::assign(m_saveButton, KStandardGuiItem::save());
0192     buttons->addButton(m_saveButton, QDialogButtonBox::YesRole);
0193     connect(m_saveButton, &QPushButton::clicked, this, &KateSaveModifiedDialog::slotSaveSelected);
0194 
0195     QPushButton *discardButton = new QPushButton;
0196     KGuiItem::assign(discardButton, KStandardGuiItem::discard());
0197     buttons->addButton(discardButton, QDialogButtonBox::NoRole);
0198     connect(discardButton, &QPushButton::clicked, this, &KateSaveModifiedDialog::slotDoNotSave);
0199 
0200     QPushButton *cancelButton = new QPushButton;
0201     KGuiItem::assign(cancelButton, KStandardGuiItem::cancel());
0202     cancelButton->setDefault(true);
0203     buttons->addButton(cancelButton, QDialogButtonBox::RejectRole);
0204     connect(cancelButton, &QPushButton::clicked, this, &KateSaveModifiedDialog::reject);
0205     cancelButton->setFocus();
0206 }
0207 
0208 KateSaveModifiedDialog::~KateSaveModifiedDialog()
0209 {
0210 }
0211 
0212 void KateSaveModifiedDialog::slotItemActivated(QTreeWidgetItem *, int)
0213 {
0214     bool enableSaveButton = false;
0215 
0216     for (int i = 0; i < m_list->topLevelItemCount(); ++i) {
0217         if (m_list->topLevelItem(i)->checkState(0) == Qt::Checked) {
0218             enableSaveButton = true;
0219             break;
0220         }
0221     }
0222 
0223     m_saveButton->setEnabled(enableSaveButton);
0224 }
0225 
0226 void KateSaveModifiedDialog::slotSelectAll()
0227 {
0228     for (int i = 0; i < m_list->topLevelItemCount(); ++i) {
0229         m_list->topLevelItem(i)->setCheckState(0, Qt::Checked);
0230     }
0231 
0232     m_saveButton->setEnabled(true);
0233 }
0234 
0235 void KateSaveModifiedDialog::slotSaveSelected()
0236 {
0237     if (doSave()) {
0238         done(QDialog::Accepted);
0239     }
0240 }
0241 
0242 void KateSaveModifiedDialog::slotDoNotSave()
0243 {
0244     done(QDialog::Accepted);
0245 }
0246 
0247 bool KateSaveModifiedDialog::doSave()
0248 {
0249     for (int i = 0; i < m_list->topLevelItemCount(); ++i) {
0250         AbstractKateSaveModifiedDialogCheckListItem *cit = static_cast<AbstractKateSaveModifiedDialogCheckListItem *>(m_list->topLevelItem(i));
0251 
0252         if (cit->checkState(0) == Qt::Checked && (cit->state() != AbstractKateSaveModifiedDialogCheckListItem::SaveOKState)) {
0253             if (!cit->synchronousSave(this /*perhaps that should be the kate mainwindow*/)) {
0254                 if (cit->state() == AbstractKateSaveModifiedDialogCheckListItem::SaveFailedState) {
0255                     KMessageBox::error(this, i18n("Data you requested to be saved could not be written. Please choose how you want to proceed."));
0256                 }
0257                 return false;
0258             }
0259         } else if ((cit->checkState(0) != Qt::Checked) && (cit->state() == AbstractKateSaveModifiedDialogCheckListItem::SaveFailedState)) {
0260             cit->setState(AbstractKateSaveModifiedDialogCheckListItem::InitialState);
0261         }
0262     }
0263 
0264     return true;
0265 }
0266 
0267 void KateSaveModifiedDialog::showEvent(QShowEvent *event)
0268 {
0269     Q_UNUSED(event);
0270 #if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 249, 0)
0271     KMessageDialog::beep(KMessageDialog::WarningTwoActionsCancel, m_label->text(), this);
0272 #endif
0273 }
0274 
0275 bool KateSaveModifiedDialog::queryClose(QWidget *parent, const std::vector<KTextEditor::Document *> &documents)
0276 {
0277     KateSaveModifiedDialog d(parent, documents);
0278     return (d.exec() != QDialog::Rejected);
0279 }
0280 
0281 #include "moc_katesavemodifieddialog.cpp"