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"