diff --git a/Pdf4QtLib/sources/pdfdocument.h b/Pdf4QtLib/sources/pdfdocument.h index cbdb874..6a0d5f8 100644 --- a/Pdf4QtLib/sources/pdfdocument.h +++ b/Pdf4QtLib/sources/pdfdocument.h @@ -499,13 +499,14 @@ public: enum ModificationFlag { - None = 0x0000, ///< No flag - Reset = 0x0001, ///< Whole document content is changed (for example, new document is being set) - PageContents = 0x0002, ///< Page contents changed (page graphics, not annotations) - Annotation = 0x0004, ///< Annotations changed - FormField = 0x0008, ///< Form field content changed - Authorization = 0x0010, ///< Authorization has changed (for example, old document is granted user access, but for new, owner access) - XFA_Pagination = 0x0020, ///< XFA pagination has been performed (this flag can be set only when Reset flag has been set and not any other flag) + None = 0x0000, ///< No flag + Reset = 0x0001, ///< Whole document content is changed (for example, new document is being set) + PageContents = 0x0002, ///< Page contents changed (page graphics, not annotations) + Annotation = 0x0004, ///< Annotations changed + FormField = 0x0008, ///< Form field content changed + Authorization = 0x0010, ///< Authorization has changed (for example, old document is granted user access, but for new, owner access) + XFA_Pagination = 0x0020, ///< XFA pagination has been performed (this flag can be set only when Reset flag has been set and not any other flag) + PreserveUndoRedo = 0x0040, ///< Preserve undo/red even when Reset flag is being set }; Q_DECLARE_FLAGS(ModificationFlags, ModificationFlag) diff --git a/Pdf4QtViewer/pdfprogramcontroller.cpp b/Pdf4QtViewer/pdfprogramcontroller.cpp index ef0ef07..837aac0 100644 --- a/Pdf4QtViewer/pdfprogramcontroller.cpp +++ b/Pdf4QtViewer/pdfprogramcontroller.cpp @@ -1057,6 +1057,11 @@ void PDFProgramController::saveDocument(const QString& fileName) pdf::PDFOperationResult result = writer.write(fileName, m_pdfDocument.data(), true); if (result) { + if (m_undoRedoManager) + { + m_undoRedoManager->setIsCurrentSaved(true); + } + updateFileInfo(fileName); updateTitle(); @@ -1091,6 +1096,41 @@ bool PDFProgramController::canClose() const return !(m_futureWatcher && m_futureWatcher->isRunning()) || !m_isBusy; } +bool PDFProgramController::askForSaveDocumentBeforeClose() +{ + if (!m_pdfDocument) + { + // Nothing to be done + return true; + } + + if (m_undoRedoManager && !m_undoRedoManager->isCurrentSaved()) + { + QString title = tr("Save Document"); + QString message = tr("Do you wish to save modified document before it is closed?"); + switch (QMessageBox::question(m_mainWindow, title, message, QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Cancel)) + { + case QMessageBox::Yes: + { + performSave(); + return m_undoRedoManager->isCurrentSaved(); + } + + case QMessageBox::No: + return true; + + case QMessageBox::Cancel: + return false; + + default: + Q_ASSERT(false); + return true; + } + } + + return true; +} + QString PDFProgramController::getOriginalFileName() const { return m_fileInfo.originalFileName; @@ -1174,7 +1214,7 @@ void PDFProgramController::onActionOptimizeTriggered() if (dialog.exec() == QDialog::Accepted) { pdf::PDFDocumentPointer pointer(new pdf::PDFDocument(dialog.takeOptimizedDocument())); - pdf::PDFModifiedDocument document(qMove(pointer), m_optionalContentActivity, pdf::PDFModifiedDocument::Reset); + pdf::PDFModifiedDocument document(qMove(pointer), m_optionalContentActivity, pdf::PDFModifiedDocument::ModificationFlags(pdf::PDFModifiedDocument::Reset | pdf::PDFModifiedDocument::PreserveUndoRedo)); onDocumentModified(qMove(document)); } } @@ -1186,7 +1226,7 @@ void PDFProgramController::onActionSanitizeTriggered() if (dialog.exec() == QDialog::Accepted) { pdf::PDFDocumentPointer pointer(new pdf::PDFDocument(dialog.takeSanitizedDocument())); - pdf::PDFModifiedDocument document(qMove(pointer), m_optionalContentActivity, pdf::PDFModifiedDocument::Reset); + pdf::PDFModifiedDocument document(qMove(pointer), m_optionalContentActivity, pdf::PDFModifiedDocument::ModificationFlags(pdf::PDFModifiedDocument::Reset | pdf::PDFModifiedDocument::PreserveUndoRedo)); onDocumentModified(qMove(document)); } } @@ -1626,7 +1666,7 @@ void PDFProgramController::onDocumentReadingFinished() m_pdfDocument = qMove(result.document); m_signatures = qMove(result.signatures); pdf::PDFModifiedDocument document(m_pdfDocument.data(), m_optionalContentActivity); - setDocument(document); + setDocument(document, true); if (m_formManager) { @@ -1687,17 +1727,17 @@ void PDFProgramController::onDocumentModified(pdf::PDFModifiedDocument document) m_pdfDocument = document; document.setOptionalContentActivity(m_optionalContentActivity); - setDocument(document); + setDocument(document, false); } void PDFProgramController::onDocumentUndoRedo(pdf::PDFModifiedDocument document) { m_pdfDocument = document; document.setOptionalContentActivity(m_optionalContentActivity); - setDocument(document); + setDocument(document, false); } -void PDFProgramController::setDocument(pdf::PDFModifiedDocument document) +void PDFProgramController::setDocument(pdf::PDFModifiedDocument document, bool isCurrentSaved) { if (document.hasReset()) { @@ -1714,7 +1754,7 @@ void PDFProgramController::setDocument(pdf::PDFModifiedDocument document) m_optionalContentActivity = new pdf::PDFOptionalContentActivity(document, pdf::OCUsage::View, this); } - if (m_undoRedoManager) + if (m_undoRedoManager && !document.hasFlag(pdf::PDFModifiedDocument::PreserveUndoRedo)) { m_undoRedoManager->clear(); } @@ -1747,6 +1787,11 @@ void PDFProgramController::setDocument(pdf::PDFModifiedDocument document) m_textToSpeech->setDocument(document); } + if (m_undoRedoManager) + { + m_undoRedoManager->setIsCurrentSaved(isCurrentSaved); + } + m_pdfWidget->setDocument(document); m_mainWindowInterface->setDocument(document); m_CMSManager->setDocument(document); @@ -1777,7 +1822,7 @@ void PDFProgramController::setDocument(pdf::PDFModifiedDocument document) void PDFProgramController::closeDocument() { m_signatures.clear(); - setDocument(pdf::PDFModifiedDocument()); + setDocument(pdf::PDFModifiedDocument(), true); m_pdfDocument.reset(); updateActionsAvailability(); updateTitle(); @@ -1797,10 +1842,17 @@ void PDFProgramController::updateTitle() if (m_pdfDocument) { QString title = m_pdfDocument->getInfo()->title; + if (title.isEmpty()) { title = m_fileInfo.fileName; } + + if (m_undoRedoManager && !m_undoRedoManager->isCurrentSaved()) + { + title += "*"; + } + m_mainWindow->setWindowTitle(tr("%1 - %2").arg(title, QApplication::applicationDisplayName())); } else @@ -2046,7 +2098,10 @@ void PDFProgramController::onActionOpenTriggered() void PDFProgramController::onActionCloseTriggered() { - closeDocument(); + if (askForSaveDocumentBeforeClose()) + { + closeDocument(); + } } void PDFProgramController::onActionDeveloperCreateInstaller() diff --git a/Pdf4QtViewer/pdfprogramcontroller.h b/Pdf4QtViewer/pdfprogramcontroller.h index b99ff3b..a664877 100644 --- a/Pdf4QtViewer/pdfprogramcontroller.h +++ b/Pdf4QtViewer/pdfprogramcontroller.h @@ -255,7 +255,7 @@ public: Q_DECLARE_FLAGS(Features, Feature) void openDocument(const QString& fileName); - void setDocument(pdf::PDFModifiedDocument document); + void setDocument(pdf::PDFModifiedDocument document, bool isCurrentSaved); void closeDocument(); pdf::PDFWidget* getPdfWidget() const { return m_pdfWidget; } @@ -287,6 +287,7 @@ public: void setIsBusy(bool isBusy); bool canClose() const; + bool askForSaveDocumentBeforeClose(); virtual QString getOriginalFileName() const override; virtual pdf::PDFTextSelection getSelectedText() const override; diff --git a/Pdf4QtViewer/pdfundoredomanager.cpp b/Pdf4QtViewer/pdfundoredomanager.cpp index 6c6a51e..faca692 100644 --- a/Pdf4QtViewer/pdfundoredomanager.cpp +++ b/Pdf4QtViewer/pdfundoredomanager.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2022 Jakub Melka +// Copyright (C) 2020-2023 Jakub Melka // // This file is part of PDF4QT. // @@ -42,6 +42,7 @@ void PDFUndoRedoManager::doUndo() UndoRedoItem item = m_undoSteps.back(); m_undoSteps.pop_back(); + m_isCurrentSaved = false; m_redoSteps.insert(m_redoSteps.begin(), item); clampUndoRedoSteps(); @@ -59,6 +60,7 @@ void PDFUndoRedoManager::doRedo() UndoRedoItem item = m_redoSteps.front(); m_redoSteps.erase(m_redoSteps.begin()); + m_isCurrentSaved = false; m_undoSteps.push_back(item); clampUndoRedoSteps(); @@ -80,6 +82,7 @@ void PDFUndoRedoManager::createUndo(pdf::PDFModifiedDocument document, pdf::PDFD { m_undoSteps.emplace_back(oldDocument, document, document.getFlags()); m_redoSteps.clear(); + m_isCurrentSaved = false; clampUndoRedoSteps(); Q_EMIT undoRedoStateChanged(); } @@ -109,4 +112,14 @@ void PDFUndoRedoManager::clampUndoRedoSteps() } } +bool PDFUndoRedoManager::isCurrentSaved() const +{ + return m_isCurrentSaved; +} + +void PDFUndoRedoManager::setIsCurrentSaved(bool newIsCurrentSaved) +{ + m_isCurrentSaved = newIsCurrentSaved; +} + } // namespace pdfviewer diff --git a/Pdf4QtViewer/pdfundoredomanager.h b/Pdf4QtViewer/pdfundoredomanager.h index 583d09c..45614f5 100644 --- a/Pdf4QtViewer/pdfundoredomanager.h +++ b/Pdf4QtViewer/pdfundoredomanager.h @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2021 Jakub Melka +// Copyright (C) 2020-2023 Jakub Melka // // This file is part of PDF4QT. // @@ -62,6 +62,12 @@ public: /// \param redoLimit Maximum redo steps void setMaximumSteps(size_t undoLimit, size_t redoLimit); + /// Returns true, if document was saved + bool isCurrentSaved() const; + + /// Sets flag, if document was saved + void setIsCurrentSaved(bool newIsCurrentSaved = true); + signals: /// This signals are emitted, when undo/redo action availability has /// been changed (for example, user pressed undo/redo action) @@ -97,6 +103,7 @@ private: size_t m_redoLimit = 0; std::vector m_undoSteps; std::vector m_redoSteps; + bool m_isCurrentSaved = true; }; } // namespace pdfviewer diff --git a/Pdf4QtViewer/pdfviewermainwindow.cpp b/Pdf4QtViewer/pdfviewermainwindow.cpp index 760ebe7..21432d0 100644 --- a/Pdf4QtViewer/pdfviewermainwindow.cpp +++ b/Pdf4QtViewer/pdfviewermainwindow.cpp @@ -502,6 +502,13 @@ void PDFViewerMainWindow::closeEvent(QCloseEvent* event) } else { + if (!m_programController->askForSaveDocumentBeforeClose()) + { + // User cancelled close operation + event->ignore(); + return; + } + if (!m_programController->isFactorySettingsBeingRestored()) { m_programController->writeSettings(); diff --git a/RELEASES.txt b/RELEASES.txt index 0d8026e..727f433 100644 --- a/RELEASES.txt +++ b/RELEASES.txt @@ -1,4 +1,5 @@ CURRENT: + - Issue #43: Save question - Issue #40: Sanitization of documents V: 1.3.2 1.2.2023