Undo/redo fix

This commit is contained in:
Jakub Melka 2020-05-03 18:52:22 +02:00
parent f604dd77b2
commit f70ff229ac
11 changed files with 189 additions and 36 deletions

View File

@ -517,7 +517,16 @@ public:
m_optionalContentActivity(optionalContentActivity),
m_flags(Reset)
{
Q_ASSERT(document);
Q_ASSERT(m_document);
}
explicit inline PDFModifiedDocument(PDFDocumentPointer document, PDFOptionalContentActivity* optionalContentActivity) :
m_documentPointer(qMove(document)),
m_document(m_documentPointer.data()),
m_optionalContentActivity(optionalContentActivity),
m_flags(Reset)
{
Q_ASSERT(m_document);
}
explicit inline PDFModifiedDocument(PDFDocument* document, PDFOptionalContentActivity* optionalContentActivity, ModificationFlags flags) :
@ -525,19 +534,31 @@ public:
m_optionalContentActivity(optionalContentActivity),
m_flags(flags)
{
Q_ASSERT(document);
Q_ASSERT(m_document);
}
explicit inline PDFModifiedDocument(PDFDocumentPointer document, PDFOptionalContentActivity* optionalContentActivity, ModificationFlags flags) :
m_documentPointer(qMove(document)),
m_document(m_documentPointer.data()),
m_optionalContentActivity(optionalContentActivity),
m_flags(flags)
{
Q_ASSERT(m_document);
}
PDFDocument* getDocument() const { return m_document; }
PDFOptionalContentActivity* getOptionalContentActivity() const { return m_optionalContentActivity; }
void setOptionalContentActivity(PDFOptionalContentActivity* optionalContentActivity) { m_optionalContentActivity = optionalContentActivity; }
ModificationFlags getFlags() const { return m_flags; }
bool hasReset() const { return m_flags.testFlag(Reset); }
bool hasFlag(ModificationFlag flag) const { return m_flags.testFlag(flag); }
operator PDFDocument*() const { return m_document; }
operator PDFDocumentPointer() const { return m_documentPointer; }
private:
PDFDocumentPointer m_documentPointer;
PDFDocument* m_document = nullptr;
PDFOptionalContentActivity* m_optionalContentActivity = nullptr;
ModificationFlags m_flags = Reset;

View File

@ -770,7 +770,7 @@ void PDFFormManager::setFormFieldValue(PDFFormField::SetValueParameters paramete
if (modifier.finalize())
{
emit documentModified(modifier.getDocument(), modifier.getFlags());
emit documentModified(PDFModifiedDocument(modifier.getDocument(), nullptr, modifier.getFlags()));
}
}
}

View File

@ -705,7 +705,7 @@ public:
signals:
void actionTriggered(const PDFAction* action);
void documentModified(PDFDocumentPointer document, PDFModifiedDocument::ModificationFlags flags);
void documentModified(PDFModifiedDocument document);
private:
void updateFormWidgetEditors();

View File

@ -45,7 +45,7 @@ void PDFUndoRedoManager::doUndo()
clampUndoRedoSteps();
emit undoRedoStateChanged();
emit documentChangeRequest(item.document, item.flags);
emit documentChangeRequest(pdf::PDFModifiedDocument(item.oldDocument, nullptr, item.flags));
}
void PDFUndoRedoManager::doRedo()
@ -62,7 +62,7 @@ void PDFUndoRedoManager::doRedo()
clampUndoRedoSteps();
emit undoRedoStateChanged();
emit documentChangeRequest(item.document, item.flags);
emit documentChangeRequest(pdf::PDFModifiedDocument(item.newDocument, nullptr, item.flags));
}
void PDFUndoRedoManager::clear()
@ -75,6 +75,25 @@ void PDFUndoRedoManager::clear()
}
}
void PDFUndoRedoManager::createUndo(pdf::PDFModifiedDocument document, pdf::PDFDocumentPointer oldDocument)
{
m_undoSteps.emplace_back(oldDocument, document, document.getFlags());
m_redoSteps.clear();
clampUndoRedoSteps();
emit undoRedoStateChanged();
}
void PDFUndoRedoManager::setMaximumSteps(size_t undoLimit, size_t redoLimit)
{
if (m_undoLimit != undoLimit || m_redoLimit != redoLimit)
{
m_undoLimit = undoLimit;
m_redoLimit = redoLimit;
clampUndoRedoSteps();
emit undoRedoStateChanged();
}
}
void PDFUndoRedoManager::clampUndoRedoSteps()
{
if (m_undoSteps.size() > m_undoLimit)

View File

@ -52,6 +52,16 @@ public:
/// Clears all undo/redo steps
void clear();
/// Create undo action (after document modification)
/// \param document Document created by modification
/// \param oldDocument Old document
void createUndo(pdf::PDFModifiedDocument document, pdf::PDFDocumentPointer oldDocument);
/// Sets maximum steps for undo/redo
/// \param undoLimit Maximum undo steps
/// \param redoLimit Maximum redo steps
void setMaximumSteps(size_t undoLimit, size_t redoLimit);
signals:
/// This signals are emitted, when undo/redo action availability has
/// been changed (for example, user pressed undo/redo action)
@ -60,9 +70,8 @@ signals:
/// This signal is being emitted, when user performs undo/redo action.
/// Before signal is emitted, this object is in corrected state, as action
/// is performed.
/// \param document Active document
/// \param flags Change flags
void documentChangeRequest(pdf::PDFDocumentPointer document, pdf::PDFModifiedDocument::ModificationFlags flags);
/// \param document Document
void documentChangeRequest(pdf::PDFModifiedDocument document);
private:
/// Clamps undo/redo steps so they fit the limits
@ -70,7 +79,17 @@ private:
struct UndoRedoItem
{
pdf::PDFDocumentPointer document;
explicit inline UndoRedoItem() = default;
explicit inline UndoRedoItem(pdf::PDFDocumentPointer oldDocument, pdf::PDFDocumentPointer newDocument, pdf::PDFModifiedDocument::ModificationFlags flags) :
oldDocument(qMove(oldDocument)),
newDocument(qMove(newDocument)),
flags(flags)
{
}
pdf::PDFDocumentPointer oldDocument;
pdf::PDFDocumentPointer newDocument;
pdf::PDFModifiedDocument::ModificationFlags flags = pdf::PDFModifiedDocument::None;
};

View File

@ -91,7 +91,8 @@ PDFViewerMainWindow::PDFViewerMainWindow(QWidget* parent) :
m_toolManager(nullptr),
m_annotationManager(nullptr),
m_formManager(nullptr),
m_textToSpeech(new PDFTextToSpeech(this))
m_textToSpeech(new PDFTextToSpeech(this)),
m_undoRedoManager(new PDFUndoRedoManager(this))
{
ui->setupUi(this);
@ -290,6 +291,13 @@ PDFViewerMainWindow::PDFViewerMainWindow(QWidget* parent) :
connect(this, &PDFViewerMainWindow::queryPasswordRequest, this, &PDFViewerMainWindow::onQueryPasswordRequest, Qt::BlockingQueuedConnection);
connect(ui->actionFind, &QAction::triggered, this, [this] { m_toolManager->setActiveTool(m_toolManager->getFindTextTool()); });
// Connect undo/redo manager
connect(m_undoRedoManager, &PDFUndoRedoManager::undoRedoStateChanged, this, &PDFViewerMainWindow::updateUndoRedoActions);
connect(m_undoRedoManager, &PDFUndoRedoManager::documentChangeRequest, this, &PDFViewerMainWindow::onDocumentUndoRedo);
connect(ui->actionUndo, &QAction::triggered, m_undoRedoManager, &PDFUndoRedoManager::doUndo);
connect(ui->actionRedo, &QAction::triggered, m_undoRedoManager, &PDFUndoRedoManager::doRedo);
updateUndoRedoSettings();
readActionSettings();
updatePageLayoutActions();
updateUI(true);
@ -873,6 +881,7 @@ void PDFViewerMainWindow::updateActionsAvailability()
ui->actionPrint->setEnabled(hasValidDocument && canPrint);
ui->actionRender_to_Images->setEnabled(hasValidDocument && canPrint);
setEnabled(!isBusy);
updateUndoRedoActions();
}
void PDFViewerMainWindow::onViewerSettingsChanged()
@ -983,11 +992,24 @@ void PDFViewerMainWindow::onDocumentReadingFinished()
updateActionsAvailability();
}
void PDFViewerMainWindow::onDocumentModified(pdf::PDFDocumentPointer document, pdf::PDFModifiedDocument::ModificationFlags flags)
void PDFViewerMainWindow::onDocumentModified(pdf::PDFModifiedDocument document)
{
// We will create undo/redo step from old document, with flags from the new,
// because new document is modification of old document with flags.
Q_ASSERT(m_pdfDocument);
m_undoRedoManager->createUndo(document, m_pdfDocument);
m_pdfDocument = document;
document.setOptionalContentActivity(m_optionalContentActivity);
setDocument(document);
}
void PDFViewerMainWindow::onDocumentUndoRedo(pdf::PDFModifiedDocument document)
{
m_pdfDocument = document;
pdf::PDFModifiedDocument modifiedDocument(m_pdfDocument.data(), m_optionalContentActivity, flags);
setDocument(modifiedDocument);
document.setOptionalContentActivity(m_optionalContentActivity);
setDocument(document);
}
void PDFViewerMainWindow::setDocument(pdf::PDFModifiedDocument document)
@ -1006,6 +1028,8 @@ void PDFViewerMainWindow::setDocument(pdf::PDFModifiedDocument document)
{
m_optionalContentActivity = new pdf::PDFOptionalContentActivity(document, pdf::OCUsage::View, this);
}
m_undoRedoManager->clear();
}
else if (m_optionalContentActivity)
{
@ -1039,7 +1063,7 @@ void PDFViewerMainWindow::setDocument(pdf::PDFModifiedDocument document)
updateTitle();
updateUI(true);
if (m_pdfDocument)
if (m_pdfDocument && document.hasReset())
{
const pdf::PDFCatalog* catalog = m_pdfDocument->getCatalog();
setPageLayout(catalog->getPageLayout());
@ -1188,6 +1212,22 @@ void PDFViewerMainWindow::updateMagnifierToolSettings()
magnifierTool->setMagnifierZoom(m_settings->getSettings().m_magnifierZoom);
}
void PDFViewerMainWindow::updateUndoRedoSettings()
{
const PDFViewerSettings::Settings& settings = m_settings->getSettings();
m_undoRedoManager->setMaximumSteps(settings.m_maximumUndoSteps, settings.m_maximumRedoSteps);
}
void PDFViewerMainWindow::updateUndoRedoActions()
{
const bool isBusy = m_futureWatcher.isRunning() || m_isBusy;
const bool canUndo = !isBusy && m_undoRedoManager->canUndo();
const bool canRedo = !isBusy && m_undoRedoManager->canRedo();
ui->actionUndo->setEnabled(canUndo);
ui->actionRedo->setEnabled(canRedo);
}
void PDFViewerMainWindow::on_actionOptions_triggered()
{
PDFViewerSettingsDialog::OtherSettings otherSettings;
@ -1203,6 +1243,7 @@ void PDFViewerMainWindow::on_actionOptions_triggered()
m_textToSpeech->setSettings(m_settings);
m_formManager->setAppearanceFlags(m_settings->getSettings().m_formAppearanceFlags);
updateMagnifierToolSettings();
updateUndoRedoSettings();
}
}

View File

@ -30,6 +30,7 @@
#include "pdftexttospeech.h"
#include "pdfannotation.h"
#include "pdfform.h"
#include "pdfundoredomanager.h"
#include <QFuture>
#include <QTreeView>
@ -114,7 +115,8 @@ private:
void onProgressFinished();
void onDocumentReadingFinished();
void onDocumentModified(pdf::PDFDocumentPointer document, pdf::PDFModifiedDocument::ModificationFlags flags);
void onDocumentModified(pdf::PDFModifiedDocument document);
void onDocumentUndoRedo(pdf::PDFModifiedDocument document);
void readSettings();
void readActionSettings();
@ -126,6 +128,8 @@ private:
void updateUI(bool fullUpdate);
void updateActionsAvailability();
void updateMagnifierToolSettings();
void updateUndoRedoSettings();
void updateUndoRedoActions();
void onViewerSettingsChanged();
void onRenderingOptionTriggered(bool checked);
@ -180,6 +184,7 @@ private:
pdf::PDFWidgetAnnotationManager* m_annotationManager;
pdf::PDFFormManager* m_formManager;
PDFTextToSpeech* m_textToSpeech;
PDFUndoRedoManager* m_undoRedoManager;
};
} // namespace pdfviewer

View File

@ -53,9 +53,11 @@ void PDFViewerSettings::readSettings(QSettings& settings, const pdf::PDFCMSSetti
m_settings.m_instancedFontCacheLimit = settings.value("instancedFontCacheLimit", defaultSettings.m_instancedFontCacheLimit).toInt();
m_settings.m_allowLaunchApplications = settings.value("allowLaunchApplications", defaultSettings.m_allowLaunchApplications).toBool();
m_settings.m_allowLaunchURI = settings.value("allowLaunchURI", defaultSettings.m_allowLaunchURI).toBool();
m_settings.m_multithreadingStrategy = static_cast<pdf::PDFExecutionPolicy::Strategy>(settings.value("mutlithreadingStrategy", static_cast<int>(defaultSettings.m_multithreadingStrategy)).toInt());
m_settings.m_multithreadingStrategy = static_cast<pdf::PDFExecutionPolicy::Strategy>(settings.value("multithreadingStrategy", static_cast<int>(defaultSettings.m_multithreadingStrategy)).toInt());
m_settings.m_magnifierSize = settings.value("magnifierSize", defaultSettings.m_magnifierSize).toInt();
m_settings.m_magnifierZoom = settings.value("magnifierZoom", defaultSettings.m_magnifierZoom).toDouble();
m_settings.m_maximumUndoSteps = settings.value("maximumUndoSteps", defaultSettings.m_maximumUndoSteps).toInt();
m_settings.m_maximumRedoSteps = settings.value("maximumRedoSteps", defaultSettings.m_maximumRedoSteps).toInt();
settings.endGroup();
settings.beginGroup("ColorManagementSystemSettings");
@ -105,9 +107,11 @@ void PDFViewerSettings::writeSettings(QSettings& settings)
settings.setValue("instancedFontCacheLimit", m_settings.m_instancedFontCacheLimit);
settings.setValue("allowLaunchApplications", m_settings.m_allowLaunchApplications);
settings.setValue("allowLaunchURI", m_settings.m_allowLaunchURI);
settings.setValue("mutlithreadingStrategy", static_cast<int>(m_settings.m_multithreadingStrategy));
settings.setValue("multithreadingStrategy", static_cast<int>(m_settings.m_multithreadingStrategy));
settings.setValue("magnifierSize", m_settings.m_magnifierSize);
settings.setValue("magnifierZoom", m_settings.m_magnifierZoom);
settings.setValue("maximumUndoSteps", m_settings.m_maximumUndoSteps);
settings.setValue("maximumRedoSteps", m_settings.m_maximumRedoSteps);
settings.endGroup();
settings.beginGroup("ColorManagementSystemSettings");
@ -241,6 +245,8 @@ PDFViewerSettings::Settings::Settings() :
m_speechVolume(1.0),
m_magnifierSize(100),
m_magnifierZoom(2.0),
m_maximumUndoSteps(5),
m_maximumRedoSteps(5),
m_formAppearanceFlags(pdf::PDFFormManager::getDefaultApperanceFlags())
{

View File

@ -77,6 +77,10 @@ public:
int m_magnifierSize;
double m_magnifierZoom;
// Undo/redo steps settings
int m_maximumUndoSteps;
int m_maximumRedoSteps;
// Form settings
pdf::PDFFormManager::FormAppearanceFlags m_formAppearanceFlags;
};

View File

@ -264,6 +264,8 @@ void PDFViewerSettingsDialog::loadData()
ui->maximumRecentFileCountEdit->setValue(m_otherSettings.maximumRecentFileCount);
ui->magnifierSizeEdit->setValue(m_settings.m_magnifierSize);
ui->magnifierZoomEdit->setValue(m_settings.m_magnifierZoom);
ui->maximumUndoStepsEdit->setValue(m_settings.m_maximumUndoSteps);
ui->maximumRedoStepsEdit->setValue(m_settings.m_maximumRedoSteps);
// CMS
ui->cmsTypeComboBox->setCurrentIndex(ui->cmsTypeComboBox->findData(int(m_cmsSettings.system)));
@ -504,6 +506,14 @@ void PDFViewerSettingsDialog::saveData()
{
m_settings.m_formAppearanceFlags.setFlag(pdf::PDFFormManager::HighlightRequiredFields, ui->formHighlightRequiredFieldsCheckBox->isChecked());
}
else if (sender == ui->maximumUndoStepsEdit)
{
m_settings.m_maximumUndoSteps = ui->maximumUndoStepsEdit->value();
}
else if (sender == ui->maximumRedoStepsEdit)
{
m_settings.m_maximumRedoSteps = ui->maximumRedoStepsEdit->value();
}
const bool loadData = !qobject_cast<const QDoubleSpinBox*>(sender) && !qobject_cast<const QSpinBox*>(sender);
if (loadData)

View File

@ -861,13 +861,6 @@
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<layout class="QGridLayout" name="uiGroupBoxLayout">
<item row="1" column="0">
<widget class="QLabel" name="magnifierSizeLabel">
<property name="text">
<string>Magnifier size</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="maximumRecentFileCountLabel">
<property name="text">
@ -875,13 +868,20 @@
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSpinBox" name="maximumRecentFileCountEdit"/>
<item row="2" column="1">
<widget class="QDoubleSpinBox" name="magnifierZoomEdit">
<property name="minimum">
<double>1.000000000000000</double>
</property>
<property name="maximum">
<double>10.000000000000000</double>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="magnifierZoomLabel">
<item row="3" column="0">
<widget class="QLabel" name="maximumUndoStepsLabel">
<property name="text">
<string>Magnifier zoom</string>
<string>Maximum undo steps</string>
</property>
</widget>
</item>
@ -898,13 +898,41 @@
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QDoubleSpinBox" name="magnifierZoomEdit">
<property name="minimum">
<double>1.000000000000000</double>
<item row="0" column="1">
<widget class="QSpinBox" name="maximumRecentFileCountEdit"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="magnifierZoomLabel">
<property name="text">
<string>Magnifier zoom</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="magnifierSizeLabel">
<property name="text">
<string>Magnifier size</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="maximumRedoStepsLabel">
<property name="text">
<string>Maximum redo steps</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QSpinBox" name="maximumUndoStepsEdit">
<property name="maximum">
<double>10.000000000000000</double>
<number>20</number>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QSpinBox" name="maximumRedoStepsEdit">
<property name="maximum">
<number>20</number>
</property>
</widget>
</item>
@ -913,7 +941,7 @@
<item>
<widget class="QLabel" name="uiInfoLabel">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Maximum count of recent files controls, how much of recent files will be displayed in menu. When document is opened, then document is addet to the top of recent file list, and recent file list is truncated from bottom, if number of recent files is greater, than maximum.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Magnifier tool settings &lt;/span&gt;set the appearance of the magnifier. Magnifier tool magnifies area under the mouse cursor. You can specify how big the magnifier will be (in pixels) and how much it will zoom.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Maximum count of recent files controls, how much of recent files will be displayed in menu. When document is opened, then document is addet to the top of recent file list, and recent file list is truncated from bottom, if number of recent files is greater, than maximum.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Magnifier tool settings &lt;/span&gt;set the appearance of the magnifier. Magnifier tool magnifies area under the mouse cursor. You can specify how big the magnifier will be (in &lt;span style=&quot; font-weight:600;&quot;&gt;logical&lt;/span&gt; pixels) and how much it will zoom.&lt;/p&gt;&lt;p&gt;By specifying &lt;span style=&quot; font-weight:600;&quot;&gt;undo/redo&lt;/span&gt; step count, you can control how many undo/redo steps is available when editing document. Set maximum undo step count to zero to disable undo/redo. You can also set undo step count to nonzero and redo step count to zero. In this case, redo actions will not be available, only undo actions. Changes are optimized for memory usage, so each undo/redo steps shares common unmodified objects between them (so, by guessestimate, 10 modifications of 50 MB document can take 51 MB of memory). Memory usage depends on how big changes are, but usually only small changes are done (for example, editing form field, modifying annotation affects small number of objects).&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="wordWrap">
<bool>true</bool>