mirror of https://github.com/JakubMelka/PDF4QT.git
Undo/redo manager, fixing form bugs
This commit is contained in:
parent
57b3711210
commit
ba74169eaa
|
@ -479,6 +479,8 @@ private:
|
|||
PDFCatalog m_catalog;
|
||||
};
|
||||
|
||||
using PDFDocumentPointer = QSharedPointer<PDFDocument>;
|
||||
|
||||
/// Helper class for document updates (for example, add/delete annotations,
|
||||
/// fill form fields, do some other minor operations) and also for major
|
||||
/// updates (document reset). It also contains modification flags, which are
|
||||
|
|
|
@ -22,19 +22,19 @@
|
|||
namespace pdf
|
||||
{
|
||||
|
||||
PDFForm PDFForm::parse(const PDFObjectStorage* storage, PDFObject object)
|
||||
PDFForm PDFForm::parse(const PDFDocument* document, PDFObject object)
|
||||
{
|
||||
PDFForm form;
|
||||
|
||||
if (const PDFDictionary* formDictionary = storage->getDictionaryFromObject(object))
|
||||
if (const PDFDictionary* formDictionary = document->getDictionaryFromObject(object))
|
||||
{
|
||||
PDFDocumentDataLoaderDecorator loader(storage);
|
||||
PDFDocumentDataLoaderDecorator loader(document);
|
||||
|
||||
std::vector<PDFObjectReference> fieldRoots = loader.readReferenceArrayFromDictionary(formDictionary, "Fields");
|
||||
form.m_formFields.reserve(fieldRoots.size());
|
||||
for (const PDFObjectReference& fieldRootReference : fieldRoots)
|
||||
{
|
||||
form.m_formFields.emplace_back(PDFFormField::parse(storage, fieldRootReference, nullptr));
|
||||
form.m_formFields.emplace_back(PDFFormField::parse(&document->getStorage(), fieldRootReference, nullptr));
|
||||
}
|
||||
|
||||
form.m_formType = FormType::AcroForm;
|
||||
|
@ -51,11 +51,73 @@ PDFForm PDFForm::parse(const PDFObjectStorage* storage, PDFObject object)
|
|||
// Jakub Melka: handle XFA form
|
||||
form.m_formType = FormType::XFAForm;
|
||||
}
|
||||
|
||||
// As post-processing, delete all form fields, which are nullptr (are incorrectly defined)
|
||||
form.m_formFields.erase(std::remove_if(form.m_formFields.begin(), form.m_formFields.end(), [](const auto& field){ return !field; }), form.m_formFields.end());
|
||||
form.updateWidgetToFormFieldMapping();
|
||||
|
||||
// If we have form, then we must also look for 'rogue' form fields, which are
|
||||
// incorrectly not in the 'Fields' entry of this form. We do this by iterating
|
||||
// all pages, and their annotations and try to find these 'rogue' fields.
|
||||
bool rogueFieldFound = false;
|
||||
const size_t pageCount = document->getCatalog()->getPageCount();
|
||||
for (size_t i = 0; i < pageCount; ++i)
|
||||
{
|
||||
const PDFPage* page = document->getCatalog()->getPage(i);
|
||||
for (PDFObjectReference annotationReference : page->getAnnotations())
|
||||
{
|
||||
const PDFDictionary* annotationDictionary = document->getDictionaryFromObject(document->getObjectByReference(annotationReference));
|
||||
|
||||
if (form.m_widgetToFormField.count(annotationReference))
|
||||
{
|
||||
// This widget/form field is already present
|
||||
continue;
|
||||
}
|
||||
|
||||
if (loader.readNameFromDictionary(annotationDictionary, "Subtype") == "Widget")
|
||||
{
|
||||
rogueFieldFound = true;
|
||||
form.m_formFields.emplace_back(PDFFormField::parse(&document->getStorage(), annotationReference, nullptr));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// As post-processing, delete all form fields, which are nullptr (are incorrectly defined)
|
||||
form.m_formFields.erase(std::remove_if(form.m_formFields.begin(), form.m_formFields.end(), [](const auto& field){ return !field; }), form.m_formFields.end());
|
||||
|
||||
if (rogueFieldFound)
|
||||
{
|
||||
form.updateWidgetToFormFieldMapping();
|
||||
}
|
||||
}
|
||||
|
||||
return form;
|
||||
}
|
||||
|
||||
void PDFForm::updateWidgetToFormFieldMapping()
|
||||
{
|
||||
m_widgetToFormField.clear();
|
||||
|
||||
if (isAcroForm() || isXFAForm())
|
||||
{
|
||||
for (const PDFFormFieldPointer& formFieldPtr : getFormFields())
|
||||
{
|
||||
formFieldPtr->fillWidgetToFormFieldMapping(m_widgetToFormField);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const PDFFormField* PDFForm::getFormFieldForWidget(PDFObjectReference widget) const
|
||||
{
|
||||
auto it = m_widgetToFormField.find(widget);
|
||||
if (it != m_widgetToFormField.cend())
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void PDFFormField::fillWidgetToFormFieldMapping(PDFWidgetToFormFieldMapping& mapping)
|
||||
{
|
||||
for (const auto& childField : m_childFields)
|
||||
|
@ -142,6 +204,34 @@ PDFFormFieldPointer PDFFormField::parse(const PDFObjectStorage* storage, PDFObje
|
|||
}
|
||||
result.reset(formField);
|
||||
|
||||
PDFObject parentV = parentField ? parentField->getValue() : PDFObject();
|
||||
PDFObject parentDV = parentField ? parentField->getDefaultValue() : PDFObject();
|
||||
FieldFlags parentFlags = parentField ? parentField->getFlags() : None;
|
||||
|
||||
formField->m_selfReference = reference;
|
||||
formField->m_fieldType = formFieldType;
|
||||
formField->m_parentField = parentField;
|
||||
formField->m_fieldNames[Partial] = loader.readTextStringFromDictionary(fieldDictionary, "T", QString());
|
||||
formField->m_fieldNames[UserCaption] = loader.readTextStringFromDictionary(fieldDictionary, "TU", QString());
|
||||
formField->m_fieldNames[Export] = loader.readTextStringFromDictionary(fieldDictionary, "TM", QString());
|
||||
formField->m_fieldFlags = fieldDictionary->hasKey("Ff") ? static_cast<FieldFlags>(loader.readIntegerFromDictionary(fieldDictionary, "Ff", 0)) : parentFlags;
|
||||
formField->m_value = fieldDictionary->hasKey("V") ? fieldDictionary->get("V") : parentV;
|
||||
formField->m_defaultValue = fieldDictionary->hasKey("DV") ? fieldDictionary->get("DV") : parentDV;
|
||||
formField->m_additionalActions = PDFAnnotationAdditionalActions::parse(storage, fieldDictionary->get("AA"));
|
||||
|
||||
// Generate fully qualified name. If partial name is empty, then fully qualified name
|
||||
// is generated from parent fully qualified name (i.e. it is same as parent's name).
|
||||
// This is according the PDF specification 1.7.
|
||||
QStringList names;
|
||||
if (parentField)
|
||||
{
|
||||
names << parentField->getName(FullyQualified);
|
||||
}
|
||||
names << formField->m_fieldNames[Partial];
|
||||
names.removeAll(QString());
|
||||
|
||||
formField->m_fieldNames[FullyQualified] = names.join(".");
|
||||
|
||||
std::vector<PDFObjectReference> kids = loader.readReferenceArrayFromDictionary(fieldDictionary, "Kids");
|
||||
if (kids.empty())
|
||||
{
|
||||
|
@ -182,22 +272,6 @@ PDFFormFieldPointer PDFFormField::parse(const PDFObjectStorage* storage, PDFObje
|
|||
|
||||
}
|
||||
|
||||
PDFObject parentV = parentField ? parentField->getValue() : PDFObject();
|
||||
PDFObject parentDV = parentField ? parentField->getDefaultValue() : PDFObject();
|
||||
FieldFlags parentFlags = parentField ? parentField->getFlags() : None;
|
||||
|
||||
formField->m_selfReference = reference;
|
||||
formField->m_fieldType = formFieldType;
|
||||
formField->m_parentField = parentField;
|
||||
formField->m_fieldNames[Partial] = loader.readTextStringFromDictionary(fieldDictionary, "T", QString());
|
||||
formField->m_fieldNames[UserCaption] = loader.readTextStringFromDictionary(fieldDictionary, "TU", QString());
|
||||
formField->m_fieldNames[Export] = loader.readTextStringFromDictionary(fieldDictionary, "TM", QString());
|
||||
formField->m_fieldNames[FullyQualified] = parentField ? QString("%1.%2").arg(parentField->getName(FullyQualified), formField->m_fieldNames[Partial]) : formField->m_fieldNames[Partial];
|
||||
formField->m_fieldFlags = fieldDictionary->hasKey("Ff") ? static_cast<FieldFlags>(loader.readIntegerFromDictionary(fieldDictionary, "Ff", 0)) : parentFlags;
|
||||
formField->m_value = fieldDictionary->hasKey("V") ? fieldDictionary->get("V") : parentV;
|
||||
formField->m_defaultValue = fieldDictionary->hasKey("DV") ? fieldDictionary->get("DV") : parentDV;
|
||||
formField->m_additionalActions = PDFAnnotationAdditionalActions::parse(storage, fieldDictionary->get("AA"));
|
||||
|
||||
if (formFieldButton)
|
||||
{
|
||||
formFieldButton->m_options = loader.readTextStringList(fieldDictionary->get("Opt"));
|
||||
|
@ -297,17 +371,6 @@ PDFFormManager::~PDFFormManager()
|
|||
|
||||
}
|
||||
|
||||
const PDFFormField* PDFFormManager::getFormFieldForWidget(PDFObjectReference widget) const
|
||||
{
|
||||
auto it = m_widgetToFormField.find(widget);
|
||||
if (it != m_widgetToFormField.cend())
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PDFAnnotationManager* PDFFormManager::getAnnotationManager() const
|
||||
{
|
||||
return m_annotationManager;
|
||||
|
@ -333,15 +396,13 @@ void PDFFormManager::setDocument(const PDFModifiedDocument& document)
|
|||
{
|
||||
if (m_document)
|
||||
{
|
||||
m_form = PDFForm::parse(&m_document->getStorage(), m_document->getCatalog()->getFormObject());
|
||||
m_form = PDFForm::parse(m_document, m_document->getCatalog()->getFormObject());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Clean the form
|
||||
m_form = PDFForm();
|
||||
}
|
||||
|
||||
updateWidgetToFormFieldMapping();
|
||||
}
|
||||
else if (document.hasFlag(PDFModifiedDocument::FormField))
|
||||
{
|
||||
|
@ -372,17 +433,4 @@ void PDFFormManager::updateFieldValues()
|
|||
}
|
||||
}
|
||||
|
||||
void PDFFormManager::updateWidgetToFormFieldMapping()
|
||||
{
|
||||
m_widgetToFormField.clear();
|
||||
|
||||
if (hasAcroForm() || hasXFAForm())
|
||||
{
|
||||
for (const PDFFormFieldPointer& formFieldPtr : m_form.getFormFields())
|
||||
{
|
||||
formFieldPtr->fillWidgetToFormFieldMapping(m_widgetToFormField);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace pdf
|
||||
|
|
|
@ -329,13 +329,23 @@ public:
|
|||
const std::optional<PDFInteger>& getQuadding() const { return m_quadding; }
|
||||
const PDFObject& getXFA() const { return m_xfa; }
|
||||
|
||||
bool isAcroForm() const { return getFormType() == PDFForm::FormType::AcroForm; }
|
||||
bool isXFAForm() const { return getFormType() == PDFForm::FormType::XFAForm; }
|
||||
|
||||
/// Returns form field for widget. If widget doesn't have attached form field,
|
||||
/// then nullptr is returned.
|
||||
/// \param widget Widget annotation
|
||||
const PDFFormField* getFormFieldForWidget(PDFObjectReference widget) const;
|
||||
|
||||
/// Parses form from the object. If some error occurs
|
||||
/// then empty form is returned, no exception is thrown.
|
||||
/// \param storage Storage
|
||||
/// \param document Document
|
||||
/// \param reference Field reference
|
||||
static PDFForm parse(const PDFObjectStorage* storage, PDFObject object);
|
||||
static PDFForm parse(const PDFDocument* document, PDFObject object);
|
||||
|
||||
private:
|
||||
void updateWidgetToFormFieldMapping();
|
||||
|
||||
FormType m_formType = FormType::None;
|
||||
PDFFormFields m_formFields;
|
||||
bool m_needAppearances = false;
|
||||
|
@ -345,6 +355,7 @@ private:
|
|||
std::optional<QByteArray> m_defaultAppearance;
|
||||
std::optional<PDFInteger> m_quadding;
|
||||
PDFObject m_xfa;
|
||||
PDFWidgetToFormFieldMapping m_widgetToFormField;
|
||||
};
|
||||
|
||||
/// Form manager. Manages all form widgets functionality - triggers actions,
|
||||
|
@ -375,7 +386,7 @@ public:
|
|||
/// Returns form field for widget. If widget doesn't have attached form field,
|
||||
/// then nullptr is returned.
|
||||
/// \param widget Widget annotation
|
||||
const PDFFormField* getFormFieldForWidget(PDFObjectReference widget) const;
|
||||
const PDFFormField* getFormFieldForWidget(PDFObjectReference widget) const { return m_form.getFormFieldForWidget(widget); }
|
||||
|
||||
PDFAnnotationManager* getAnnotationManager() const;
|
||||
void setAnnotationManager(PDFAnnotationManager* annotationManager);
|
||||
|
@ -391,14 +402,12 @@ public:
|
|||
|
||||
private:
|
||||
void updateFieldValues();
|
||||
void updateWidgetToFormFieldMapping();
|
||||
|
||||
PDFDrawWidgetProxy* m_proxy;
|
||||
PDFAnnotationManager* m_annotationManager;
|
||||
const PDFDocument* m_document;
|
||||
FormAppearanceFlags m_flags;
|
||||
PDFForm m_form;
|
||||
PDFWidgetToFormFieldMapping m_widgetToFormField;
|
||||
};
|
||||
|
||||
} // namespace pdf
|
||||
|
|
|
@ -42,6 +42,7 @@ SOURCES += \
|
|||
pdfsendmail.cpp \
|
||||
pdfsidebarwidget.cpp \
|
||||
pdftexttospeech.cpp \
|
||||
pdfundoredomanager.cpp \
|
||||
pdfviewermainwindow.cpp \
|
||||
pdfviewersettings.cpp \
|
||||
pdfviewersettingsdialog.cpp
|
||||
|
@ -55,6 +56,7 @@ HEADERS += \
|
|||
pdfsendmail.h \
|
||||
pdfsidebarwidget.h \
|
||||
pdftexttospeech.h \
|
||||
pdfundoredomanager.h \
|
||||
pdfviewermainwindow.h \
|
||||
pdfviewersettings.h \
|
||||
pdfviewersettingsdialog.h
|
||||
|
|
|
@ -44,5 +44,7 @@
|
|||
<file>resources/screenshot-tool.svg</file>
|
||||
<file>resources/extract-image.svg</file>
|
||||
<file>resources/form-settings.svg</file>
|
||||
<file>resources/undo.svg</file>
|
||||
<file>resources/redo.svg</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
// Copyright (C) 2020 Jakub Melka
|
||||
//
|
||||
// This file is part of PdfForQt.
|
||||
//
|
||||
// PdfForQt is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// PdfForQt is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with PDFForQt. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "pdfundoredomanager.h"
|
||||
|
||||
namespace pdfviewer
|
||||
{
|
||||
|
||||
PDFUndoRedoManager::PDFUndoRedoManager(QObject* parent) :
|
||||
BaseClass(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
PDFUndoRedoManager::~PDFUndoRedoManager()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void PDFUndoRedoManager::doUndo()
|
||||
{
|
||||
if (!canUndo())
|
||||
{
|
||||
// Undo operation can't be performed
|
||||
return;
|
||||
}
|
||||
|
||||
UndoRedoItem item = m_undoSteps.back();
|
||||
m_undoSteps.pop_back();
|
||||
m_redoSteps.insert(m_redoSteps.begin(), item);
|
||||
clampUndoRedoSteps();
|
||||
|
||||
emit undoRedoStateChanged();
|
||||
emit documentChangeRequest(item.document, item.flags);
|
||||
}
|
||||
|
||||
void PDFUndoRedoManager::doRedo()
|
||||
{
|
||||
if (!canRedo())
|
||||
{
|
||||
// Redo operation can't be performed
|
||||
return;
|
||||
}
|
||||
|
||||
UndoRedoItem item = m_redoSteps.front();
|
||||
m_redoSteps.erase(m_redoSteps.begin());
|
||||
m_undoSteps.push_back(item);
|
||||
clampUndoRedoSteps();
|
||||
|
||||
emit undoRedoStateChanged();
|
||||
emit documentChangeRequest(item.document, item.flags);
|
||||
}
|
||||
|
||||
void PDFUndoRedoManager::clear()
|
||||
{
|
||||
if (canUndo() || canRedo())
|
||||
{
|
||||
m_undoSteps.clear();
|
||||
m_redoSteps.clear();
|
||||
emit undoRedoStateChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void PDFUndoRedoManager::clampUndoRedoSteps()
|
||||
{
|
||||
if (m_undoSteps.size() > m_undoLimit)
|
||||
{
|
||||
// We erase from oldest steps to newest
|
||||
m_undoSteps.erase(m_undoSteps.begin(), std::next(m_undoSteps.begin(), m_undoSteps.size() - m_undoLimit));
|
||||
}
|
||||
if (m_redoSteps.size() > m_redoLimit)
|
||||
{
|
||||
// Newest steps are erased
|
||||
m_redoSteps.resize(m_redoLimit);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace pdfviewer
|
|
@ -0,0 +1,85 @@
|
|||
// Copyright (C) 2020 Jakub Melka
|
||||
//
|
||||
// This file is part of PdfForQt.
|
||||
//
|
||||
// PdfForQt is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// PdfForQt is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with PDFForQt. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef PDFUNDOREDOMANAGER_H
|
||||
#define PDFUNDOREDOMANAGER_H
|
||||
|
||||
#include "pdfdocument.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
namespace pdfviewer
|
||||
{
|
||||
|
||||
/// Undo/Redo document manager, it is managing undo and redo steps,
|
||||
/// when document is modified.
|
||||
class PDFUndoRedoManager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
using BaseClass = QObject;
|
||||
|
||||
public:
|
||||
explicit PDFUndoRedoManager(QObject* parent);
|
||||
virtual ~PDFUndoRedoManager() override;
|
||||
|
||||
bool canUndo() const { return !m_undoSteps.empty(); }
|
||||
bool canRedo() const { return !m_redoSteps.empty(); }
|
||||
|
||||
/// Performs single undo step. If Undo action can't be performed,
|
||||
/// then nothing happens.
|
||||
void doUndo();
|
||||
|
||||
/// Performs single redo step. If Redo action can't be performed,
|
||||
/// then nothing happens.
|
||||
void doRedo();
|
||||
|
||||
/// Clears all undo/redo steps
|
||||
void clear();
|
||||
|
||||
signals:
|
||||
/// This signals are emitted, when undo/redo action availability has
|
||||
/// been changed (for example, user pressed undo/redo action)
|
||||
void undoRedoStateChanged();
|
||||
|
||||
/// 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);
|
||||
|
||||
private:
|
||||
/// Clamps undo/redo steps so they fit the limits
|
||||
void clampUndoRedoSteps();
|
||||
|
||||
struct UndoRedoItem
|
||||
{
|
||||
pdf::PDFDocumentPointer document;
|
||||
pdf::PDFModifiedDocument::ModificationFlags flags = pdf::PDFModifiedDocument::None;
|
||||
};
|
||||
|
||||
size_t m_undoLimit = 0;
|
||||
size_t m_redoLimit = 0;
|
||||
std::vector<UndoRedoItem> m_undoSteps;
|
||||
std::vector<UndoRedoItem> m_redoSteps;
|
||||
};
|
||||
|
||||
} // namespace pdfviewer
|
||||
|
||||
#endif // PDFUNDOREDOMANAGER_H
|
|
@ -117,6 +117,8 @@ PDFViewerMainWindow::PDFViewerMainWindow(QWidget* parent) :
|
|||
ui->actionRotateRight->setShortcut(QKeySequence("Ctrl+Shift++"));
|
||||
ui->actionRotateLeft->setShortcut(QKeySequence("Ctrl+Shift+-"));
|
||||
ui->actionPrint->setShortcut(QKeySequence::Print);
|
||||
ui->actionUndo->setShortcut(QKeySequence::Undo);
|
||||
ui->actionRedo->setShortcut(QKeySequence::Redo);
|
||||
|
||||
for (QAction* action : m_recentFileManager->getActions())
|
||||
{
|
||||
|
|
|
@ -34,7 +34,6 @@
|
|||
#include <QFuture>
|
||||
#include <QTreeView>
|
||||
#include <QMainWindow>
|
||||
#include <QSharedPointer>
|
||||
#include <QWinTaskbarButton>
|
||||
#include <QWinTaskbarProgress>
|
||||
#include <QFutureWatcher>
|
||||
|
@ -147,7 +146,7 @@ private:
|
|||
|
||||
struct AsyncReadingResult
|
||||
{
|
||||
QSharedPointer<pdf::PDFDocument> document;
|
||||
pdf::PDFDocumentPointer document;
|
||||
QString errorMessage;
|
||||
pdf::PDFDocumentReader::Result result = pdf::PDFDocumentReader::Result::Cancelled;
|
||||
};
|
||||
|
@ -157,7 +156,7 @@ private:
|
|||
PDFRecentFileManager* m_recentFileManager;
|
||||
PDFViewerSettings* m_settings;
|
||||
pdf::PDFWidget* m_pdfWidget;
|
||||
QSharedPointer<pdf::PDFDocument> m_pdfDocument;
|
||||
pdf::PDFDocumentPointer m_pdfDocument;
|
||||
QString m_currentFile;
|
||||
PDFSidebarWidget* m_sidebarWidget;
|
||||
QDockWidget* m_sidebarDockWidget;
|
||||
|
|
|
@ -112,6 +112,9 @@
|
|||
<property name="title">
|
||||
<string>Edit</string>
|
||||
</property>
|
||||
<addaction name="actionUndo"/>
|
||||
<addaction name="actionRedo"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionFind"/>
|
||||
<addaction name="actionFindPrevious"/>
|
||||
<addaction name="actionFindNext"/>
|
||||
|
@ -508,6 +511,24 @@
|
|||
<string>Display Annotations</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionUndo">
|
||||
<property name="icon">
|
||||
<iconset resource="pdfforqtviewer.qrc">
|
||||
<normaloff>:/resources/undo.svg</normaloff>:/resources/undo.svg</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Undo</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionRedo">
|
||||
<property name="icon">
|
||||
<iconset resource="pdfforqtviewer.qrc">
|
||||
<normaloff>:/resources/redo.svg</normaloff>:/resources/redo.svg</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Redo</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
<resources>
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="30mm"
|
||||
height="30mm"
|
||||
viewBox="0 0 30 30"
|
||||
version="1.1"
|
||||
id="svg5291"
|
||||
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
|
||||
sodipodi:docname="redo.svg">
|
||||
<defs
|
||||
id="defs5285">
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 15 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="30 : 15 : 1"
|
||||
inkscape:persp3d-origin="15 : 10 : 1"
|
||||
id="perspective5921" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="11.313708"
|
||||
inkscape:cx="82.017664"
|
||||
inkscape:cy="53.86011"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="3840"
|
||||
inkscape:window-height="2035"
|
||||
inkscape:window-x="-13"
|
||||
inkscape:window-y="-13"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata5288">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>Jakub Melka</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
<cc:license
|
||||
rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
|
||||
</cc:Work>
|
||||
<cc:License
|
||||
rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Reproduction" />
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Distribution" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#Notice" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#Attribution" />
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
|
||||
</cc:License>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Vrstva 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-267)">
|
||||
<flowRoot
|
||||
xml:space="preserve"
|
||||
id="flowRoot5913"
|
||||
style="font-style:normal;font-weight:normal;font-size:40px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><flowRegion
|
||||
id="flowRegion5915"><rect
|
||||
id="rect5917"
|
||||
width="129.22377"
|
||||
height="91.747108"
|
||||
x="-13.788582"
|
||||
y="-33.515606" /></flowRegion><flowPara
|
||||
id="flowPara5919" /></flowRoot> <g
|
||||
aria-label="⎌"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:38.09999847px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.39687496"
|
||||
id="text847"
|
||||
transform="matrix(-1,0,0,1,29.656982,0)">
|
||||
<path
|
||||
d="m 11.014771,277.24364 -4.7252931,3.75791 -1.1348144,-5.93452 2.0835937,0.76274 q 1.1534179,-1.54409 2.8463378,-2.49287 2.195215,-1.24643 4.743896,-1.24643 2.548682,0 4.725293,1.22783 2.195215,1.20923 3.423047,3.36724 1.246435,2.158 1.246435,4.85551 v 0.11162 q 0.706934,0.14883 1.339453,0.5023 1.097608,0.61391 1.711524,1.69292 0.632519,1.079 0.632519,2.41846 0,1.33945 -0.613916,2.41845 -0.613916,1.0604 -1.711523,1.69292 -1.079004,0.61392 -2.38125,0.61392 -1.265039,0 -2.38125,-0.61392 -1.116211,-0.61391 -1.730127,-1.69292 -0.595312,-1.079 -0.595312,-2.41845 0,-1.30225 0.613916,-2.38125 0.632519,-1.09761 1.730127,-1.71153 0.799951,-0.46509 1.674316,-0.57671 v -0.0558 q 0,-2.26963 -1.023193,-4.05556 -1.00459,-1.78594 -2.771924,-2.73472 -1.767334,-0.96738 -3.888135,-0.96738 -2.046386,0 -3.81372,0.96738 -1.2092287,0.65112 -2.0649904,1.71152 z m -4.5578614,4.29741 q 1.2836425,0 2.3626464,0.61392 1.0976073,0.61391 1.711523,1.69292 0.63252,1.079 0.63252,2.41846 0,1.33945 -0.613916,2.41845 -0.6139161,1.0604 -1.7115235,1.69292 -1.0790039,0.61392 -2.3812499,0.61392 -1.265039,0 -2.3812499,-0.61392 -1.1162109,-0.61391 -1.7301269,-1.69292 -0.5953125,-1.079 -0.5953125,-2.41845 0,-1.30225 0.613916,-2.38125 0.6325195,-1.09761 1.7301269,-1.71153 1.0976074,-0.63252 2.3626464,-0.63252 z m 0,1.39527 q -0.8557617,0 -1.6185058,0.40927 -0.7627441,0.40928 -1.2092285,1.19063 -0.4278808,0.76274 -0.4278808,1.73013 0,0.93017 0.4092773,1.73012 0.4278808,0.79995 1.2092285,1.20923 0.7813476,0.40928 1.6371093,0.40928 0.9301757,0 1.6743163,-0.40928 0.7441406,-0.40928 1.190625,-1.20923 0.4464843,-0.79995 0.4464843,-1.73012 0,-0.96739 -0.4464843,-1.73013 -0.4464844,-0.78135 -1.2092285,-1.19063 -0.7441406,-0.40927 -1.6557128,-0.40927 z m 16.7431634,0 q -0.855762,0 -1.618506,0.40927 -0.762744,0.40928 -1.209228,1.19063 -0.427881,0.76274 -0.427881,1.73013 0,0.93017 0.409277,1.73012 0.427881,0.79995 1.209229,1.20923 0.781347,0.40928 1.637109,0.40928 0.930176,0 1.674316,-0.40928 0.744141,-0.40928 1.190625,-1.20923 0.446485,-0.79995 0.446485,-1.73012 0,-0.96739 -0.446485,-1.73013 -0.446484,-0.78135 -1.209228,-1.19063 -0.744141,-0.40927 -1.655713,-0.40927 z"
|
||||
style="stroke-width:0.39687496"
|
||||
id="path4552"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 5.8 KiB |
|
@ -0,0 +1,107 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="30mm"
|
||||
height="30mm"
|
||||
viewBox="0 0 30 30"
|
||||
version="1.1"
|
||||
id="svg5291"
|
||||
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
|
||||
sodipodi:docname="undo.svg">
|
||||
<defs
|
||||
id="defs5285">
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 15 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="30 : 15 : 1"
|
||||
inkscape:persp3d-origin="15 : 10 : 1"
|
||||
id="perspective5921" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="11.313708"
|
||||
inkscape:cx="82.017664"
|
||||
inkscape:cy="53.86011"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="3840"
|
||||
inkscape:window-height="2035"
|
||||
inkscape:window-x="-13"
|
||||
inkscape:window-y="-13"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata5288">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>Jakub Melka</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
<cc:license
|
||||
rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
|
||||
</cc:Work>
|
||||
<cc:License
|
||||
rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Reproduction" />
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Distribution" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#Notice" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#Attribution" />
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
|
||||
</cc:License>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Vrstva 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-267)">
|
||||
<flowRoot
|
||||
xml:space="preserve"
|
||||
id="flowRoot5913"
|
||||
style="font-style:normal;font-weight:normal;font-size:40px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><flowRegion
|
||||
id="flowRegion5915"><rect
|
||||
id="rect5917"
|
||||
width="129.22377"
|
||||
height="91.747108"
|
||||
x="-13.788582"
|
||||
y="-33.515606" /></flowRegion><flowPara
|
||||
id="flowPara5919" /></flowRoot> <g
|
||||
aria-label="⎌"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:38.09999847px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.39687496"
|
||||
id="text847">
|
||||
<path
|
||||
d="m 11.014771,277.24364 -4.7252931,3.75791 -1.1348144,-5.93452 2.0835937,0.76274 q 1.1534179,-1.54409 2.8463378,-2.49287 2.195215,-1.24643 4.743896,-1.24643 2.548682,0 4.725293,1.22783 2.195215,1.20923 3.423047,3.36724 1.246435,2.158 1.246435,4.85551 v 0.11162 q 0.706934,0.14883 1.339453,0.5023 1.097608,0.61391 1.711524,1.69292 0.632519,1.079 0.632519,2.41846 0,1.33945 -0.613916,2.41845 -0.613916,1.0604 -1.711523,1.69292 -1.079004,0.61392 -2.38125,0.61392 -1.265039,0 -2.38125,-0.61392 -1.116211,-0.61391 -1.730127,-1.69292 -0.595312,-1.079 -0.595312,-2.41845 0,-1.30225 0.613916,-2.38125 0.632519,-1.09761 1.730127,-1.71153 0.799951,-0.46509 1.674316,-0.57671 v -0.0558 q 0,-2.26963 -1.023193,-4.05556 -1.00459,-1.78594 -2.771924,-2.73472 -1.767334,-0.96738 -3.888135,-0.96738 -2.046386,0 -3.81372,0.96738 -1.2092287,0.65112 -2.0649904,1.71152 z m -4.5578614,4.29741 q 1.2836425,0 2.3626464,0.61392 1.0976073,0.61391 1.711523,1.69292 0.63252,1.079 0.63252,2.41846 0,1.33945 -0.613916,2.41845 -0.6139161,1.0604 -1.7115235,1.69292 -1.0790039,0.61392 -2.3812499,0.61392 -1.265039,0 -2.3812499,-0.61392 -1.1162109,-0.61391 -1.7301269,-1.69292 -0.5953125,-1.079 -0.5953125,-2.41845 0,-1.30225 0.613916,-2.38125 0.6325195,-1.09761 1.7301269,-1.71153 1.0976074,-0.63252 2.3626464,-0.63252 z m 0,1.39527 q -0.8557617,0 -1.6185058,0.40927 -0.7627441,0.40928 -1.2092285,1.19063 -0.4278808,0.76274 -0.4278808,1.73013 0,0.93017 0.4092773,1.73012 0.4278808,0.79995 1.2092285,1.20923 0.7813476,0.40928 1.6371093,0.40928 0.9301757,0 1.6743163,-0.40928 0.7441406,-0.40928 1.190625,-1.20923 0.4464843,-0.79995 0.4464843,-1.73012 0,-0.96739 -0.4464843,-1.73013 -0.4464844,-0.78135 -1.2092285,-1.19063 -0.7441406,-0.40927 -1.6557128,-0.40927 z m 16.7431634,0 q -0.855762,0 -1.618506,0.40927 -0.762744,0.40928 -1.209228,1.19063 -0.427881,0.76274 -0.427881,1.73013 0,0.93017 0.409277,1.73012 0.427881,0.79995 1.209229,1.20923 0.781347,0.40928 1.637109,0.40928 0.930176,0 1.674316,-0.40928 0.744141,-0.40928 1.190625,-1.20923 0.446485,-0.79995 0.446485,-1.73012 0,-0.96739 -0.446485,-1.73013 -0.446484,-0.78135 -1.209228,-1.19063 -0.744141,-0.40927 -1.655713,-0.40927 z"
|
||||
style="stroke-width:0.39687496"
|
||||
id="path4552"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 5.8 KiB |
Loading…
Reference in New Issue