Form editors (first part)

This commit is contained in:
Jakub Melka 2020-04-26 19:43:45 +02:00
parent ba74169eaa
commit c5027e9eeb
6 changed files with 515 additions and 8 deletions

View File

@ -985,7 +985,8 @@ void PDFAnnotationManager::drawWidgetAnnotationHighlight(QRectF annotationRectan
{
// Is it a form field?
const PDFFormManager::FormAppearanceFlags flags = m_formManager->getAppearanceFlags();
if (flags.testFlag(PDFFormManager::HighlightFields) || flags.testFlag(PDFFormManager::HighlightRequiredFields))
const bool isFocused = m_formManager->isFocused(annotation->getSelfReference());
if (isFocused || flags.testFlag(PDFFormManager::HighlightFields) || flags.testFlag(PDFFormManager::HighlightRequiredFields))
{
const PDFFormField* formField = m_formManager->getFormFieldForWidget(annotation->getSelfReference());
if (!formField)
@ -1002,6 +1003,10 @@ void PDFAnnotationManager::drawWidgetAnnotationHighlight(QRectF annotationRectan
{
color = Qt::red;
}
if (isFocused)
{
color = Qt::yellow;
}
if (color.isValid())
{
@ -1453,7 +1458,14 @@ void PDFWidgetAnnotationManager::updateFromMouseEvent(QMouseEvent* event)
}
if (annotationType == AnnotationType::Widget)
{
m_cursor = QCursor(Qt::ArrowCursor);
if (m_formManager && m_formManager->hasFormFieldWidgetText(pageAnnotation.annotation->getSelfReference()))
{
m_cursor = QCursor(Qt::IBeamCursor);
}
else
{
m_cursor = QCursor(Qt::ArrowCursor);
}
}
// Generate popup window

View File

@ -20,6 +20,7 @@
#include "pdfcompiler.h"
#include "pdfwidgettool.h"
#include "pdfannotation.h"
#include "pdfform.h"
#include <QPainter>
#include <QGridLayout>
@ -34,6 +35,8 @@ PDFWidget::PDFWidget(const PDFCMSManager* cmsManager, RendererEngine engine, int
QWidget(parent),
m_cmsManager(cmsManager),
m_toolManager(nullptr),
m_annotationManager(nullptr),
m_formManager(nullptr),
m_drawWidget(nullptr),
m_horizontalScrollBar(nullptr),
m_verticalScrollBar(nullptr),
@ -66,6 +69,16 @@ PDFWidget::~PDFWidget()
}
bool PDFWidget::focusNextPrevChild(bool next)
{
if (m_formManager && m_formManager->focusNextPrevFormField(next))
{
return true;
}
return QWidget::focusNextPrevChild(next);
}
void PDFWidget::setDocument(const PDFModifiedDocument& document)
{
m_proxy->setDocument(document);
@ -191,6 +204,18 @@ void PDFWidget::addInputInterface(IDrawWidgetInputInterface* inputInterface)
}
}
PDFFormManager* PDFWidget::getFormManager() const
{
return m_formManager;
}
void PDFWidget::setFormManager(PDFFormManager* formManager)
{
removeInputInterface(m_formManager);
m_formManager = formManager;
addInputInterface(m_formManager);
}
void PDFWidget::setToolManager(PDFToolManager* toolManager)
{
removeInputInterface(m_toolManager);

View File

@ -31,6 +31,7 @@ class PDFDocument;
class PDFCMSManager;
class PDFToolManager;
class PDFDrawWidget;
class PDFFormManager;
class PDFDrawWidgetProxy;
class PDFModifiedDocument;
class PDFWidgetAnnotationManager;
@ -59,6 +60,8 @@ public:
explicit PDFWidget(const PDFCMSManager* cmsManager, RendererEngine engine, int samplesCount, QWidget* parent);
virtual ~PDFWidget() override;
virtual bool focusNextPrevChild(bool next) override;
using PageRenderingErrors = std::map<PDFInteger, QList<PDFRenderError>>;
/// Sets the document to be viewed in this widget. Document can be nullptr,
@ -93,6 +96,9 @@ public:
void setToolManager(PDFToolManager* toolManager);
void setAnnotationManager(PDFWidgetAnnotationManager* annotationManager);
PDFFormManager* getFormManager() const;
void setFormManager(PDFFormManager* formManager);
signals:
void pageRenderingErrorsChanged(PDFInteger pageIndex, int errorsCount);
@ -109,6 +115,7 @@ private:
const PDFCMSManager* m_cmsManager;
PDFToolManager* m_toolManager;
PDFWidgetAnnotationManager* m_annotationManager;
PDFFormManager* m_formManager;
IDrawWidget* m_drawWidget;
QScrollBar* m_horizontalScrollBar;
QScrollBar* m_verticalScrollBar;

View File

@ -74,7 +74,8 @@ PDFForm PDFForm::parse(const PDFDocument* document, PDFObject object)
continue;
}
if (loader.readNameFromDictionary(annotationDictionary, "Subtype") == "Widget")
if (loader.readNameFromDictionary(annotationDictionary, "Subtype") == "Widget" &&
!annotationDictionary->hasKey("Kids"))
{
rogueFieldFound = true;
form.m_formFields.emplace_back(PDFFormField::parse(&document->getStorage(), annotationReference, nullptr));
@ -146,6 +147,16 @@ void PDFFormField::reloadValue(const PDFObjectStorage* storage, PDFObject parent
}
}
void PDFFormField::apply(const std::function<void (const PDFFormField*)>& functor) const
{
functor(this);
for (const PDFFormFieldPointer& childField : m_childFields)
{
childField->apply(functor);
}
}
PDFFormFieldPointer PDFFormField::parse(const PDFObjectStorage* storage, PDFObjectReference reference, PDFFormField* parentField)
{
PDFFormFieldPointer result;
@ -361,7 +372,8 @@ PDFFormManager::PDFFormManager(PDFDrawWidgetProxy* proxy, QObject* parent) :
m_proxy(proxy),
m_annotationManager(nullptr),
m_document(nullptr),
m_flags(getDefaultApperanceFlags())
m_flags(getDefaultApperanceFlags()),
m_focusedEditor(nullptr)
{
Q_ASSERT(proxy);
}
@ -403,6 +415,8 @@ void PDFFormManager::setDocument(const PDFModifiedDocument& document)
// Clean the form
m_form = PDFForm();
}
updateFormWidgetEditors();
}
else if (document.hasFlag(PDFModifiedDocument::FormField))
{
@ -422,6 +436,245 @@ void PDFFormManager::setAppearanceFlags(FormAppearanceFlags flags)
m_flags = flags;
}
bool PDFFormManager::hasFormFieldWidgetText(PDFObjectReference widgetAnnotation) const
{
if (const PDFFormField* formField = getFormFieldForWidget(widgetAnnotation))
{
switch (formField->getFieldType())
{
case PDFFormField::FieldType::Text:
return true;
case PDFFormField::FieldType::Choice:
{
PDFFormField::FieldFlags flags = formField->getFlags();
return flags.testFlag(PDFFormField::Combo) && flags.testFlag(PDFFormField::Edit);
}
default:
break;
}
}
return false;
}
PDFFormWidgets PDFFormManager::getWidgets() const
{
PDFFormWidgets result;
auto functor = [&result](const PDFFormField* formField)
{
const PDFFormWidgets& widgets = formField->getWidgets();
result.insert(result.cend(), widgets.cbegin(), widgets.cend());
};
apply(functor);
return result;
}
void PDFFormManager::apply(const std::function<void (const PDFFormField*)>& functor) const
{
for (const PDFFormFieldPointer& childField : m_form.getFormFields())
{
childField->apply(functor);
}
}
void PDFFormManager::setFocusToEditor(PDFFormFieldWidgetEditor* editor)
{
if (m_focusedEditor != editor)
{
if (m_focusedEditor)
{
m_focusedEditor->setFocus(false);
}
m_focusedEditor = editor;
if (m_focusedEditor)
{
m_focusedEditor->setFocus(true);
}
// Request repaint, because focus changed
Q_ASSERT(m_proxy);
m_proxy->repaintNeeded();
}
}
bool PDFFormManager::focusNextPrevFormField(bool next)
{
if (m_widgetEditors.empty())
{
return false;
}
std::vector<PDFFormFieldWidgetEditor*>::const_iterator newFocusIterator = m_widgetEditors.cend();
if (!m_focusedEditor)
{
// We are setting a new focus
if (next)
{
newFocusIterator = m_widgetEditors.cbegin();
}
else
{
newFocusIterator = std::prev(m_widgetEditors.cend());
}
}
else
{
std::vector<PDFFormFieldWidgetEditor*>::const_iterator it = std::find(m_widgetEditors.cbegin(), m_widgetEditors.cend(), m_focusedEditor);
Q_ASSERT(it != m_widgetEditors.cend());
if (next)
{
newFocusIterator = std::next(it);
}
else if (it != m_widgetEditors.cbegin())
{
newFocusIterator = std::prev(it);
}
}
if (newFocusIterator != m_widgetEditors.cend())
{
setFocusToEditor(*newFocusIterator);
return true;
}
else
{
// Jakub Melka: We must remove focus out of editor, because
setFocusToEditor(nullptr);
}
return false;
}
bool PDFFormManager::isFocused(PDFObjectReference widget) const
{
if (m_focusedEditor)
{
return m_focusedEditor->getWidgetAnnotation() == widget;
}
return false;
}
void PDFFormManager::keyPressEvent(QWidget* widget, QKeyEvent* event)
{
Q_UNUSED(widget);
Q_UNUSED(event);
}
void PDFFormManager::mousePressEvent(QWidget* widget, QMouseEvent* event)
{
Q_UNUSED(widget);
Q_UNUSED(event);
}
void PDFFormManager::mouseReleaseEvent(QWidget* widget, QMouseEvent* event)
{
Q_UNUSED(widget);
Q_UNUSED(event);
}
void PDFFormManager::mouseMoveEvent(QWidget* widget, QMouseEvent* event)
{
Q_UNUSED(widget);
Q_UNUSED(event);
}
void PDFFormManager::wheelEvent(QWidget* widget, QWheelEvent* event)
{
Q_UNUSED(widget);
Q_UNUSED(event);
}
const std::optional<QCursor>& PDFFormManager::getCursor() const
{
static const std::optional<QCursor> dummy;
return dummy;
}
void PDFFormManager::updateFormWidgetEditors()
{
setFocusToEditor(nullptr);
qDeleteAll(m_widgetEditors);
m_widgetEditors.clear();
for (PDFFormWidget widget : getWidgets())
{
const PDFFormField* formField = widget.getParent();
switch (formField->getFieldType())
{
case PDFFormField::FieldType::Button:
{
Q_ASSERT(dynamic_cast<const PDFFormFieldButton*>(formField));
const PDFFormFieldButton* formFieldButton = static_cast<const PDFFormFieldButton*>(formField);
switch (formFieldButton->getButtonType())
{
case PDFFormFieldButton::ButtonType::PushButton:
{
m_widgetEditors.push_back(new PDFFormFieldPushButtonEditor(this, widget, this));
break;
}
case PDFFormFieldButton::ButtonType::RadioButton:
case PDFFormFieldButton::ButtonType::CheckBox:
{
m_widgetEditors.push_back(new PDFFormFieldCheckableButtonEditor(this, widget, this));
break;
}
default:
Q_ASSERT(false);
break;
}
break;
}
case PDFFormField::FieldType::Text:
{
m_widgetEditors.push_back(new PDFFormFieldTextBoxEditor(this, widget, this));
break;
}
case PDFFormField::FieldType::Choice:
{
Q_ASSERT(dynamic_cast<const PDFFormFieldChoice*>(formField));
const PDFFormFieldChoice* formFieldChoice = static_cast<const PDFFormFieldChoice*>(formField);
if (formFieldChoice->isComboBox())
{
m_widgetEditors.push_back(new PDFFormFieldComboBoxEditor(this, widget, this));
}
else if (formFieldChoice->isListBox())
{
m_widgetEditors.push_back(new PDFFormFieldListBoxEditor(this, widget, this));
}
else
{
// Uknown field choice
Q_ASSERT(false);
}
break;
}
case PDFFormField::FieldType::Signature:
// Signature fields doesn't have editor
break;
default:
Q_ASSERT(false);
break;
}
}
}
void PDFFormManager::updateFieldValues()
{
if (m_document)
@ -433,4 +686,48 @@ void PDFFormManager::updateFieldValues()
}
}
PDFFormFieldWidgetEditor::PDFFormFieldWidgetEditor(PDFFormManager* formManager, PDFFormWidget formWidget, QObject* parent) :
BaseClass(parent),
m_formManager(formManager),
m_formWidget(formWidget),
m_hasFocus(false)
{
Q_ASSERT(m_formManager);
}
void PDFFormFieldWidgetEditor::setFocus(bool hasFocus)
{
m_hasFocus = hasFocus;
}
PDFFormFieldPushButtonEditor::PDFFormFieldPushButtonEditor(PDFFormManager* formManager, PDFFormWidget formWidget, QObject* parent) :
BaseClass(formManager, formWidget, parent)
{
}
PDFFormFieldCheckableButtonEditor::PDFFormFieldCheckableButtonEditor(PDFFormManager* formManager, PDFFormWidget formWidget, QObject* parent) :
BaseClass(formManager, formWidget, parent)
{
}
PDFFormFieldComboBoxEditor::PDFFormFieldComboBoxEditor(PDFFormManager* formManager, PDFFormWidget formWidget, QObject* parent) :
BaseClass(formManager, formWidget, parent)
{
}
PDFFormFieldListBoxEditor::PDFFormFieldListBoxEditor(PDFFormManager* formManager, PDFFormWidget formWidget, QObject* parent) :
BaseClass(formManager, formWidget, parent)
{
}
PDFFormFieldTextBoxEditor::PDFFormFieldTextBoxEditor(PDFFormManager* formManager, PDFFormWidget formWidget, QObject* parent) :
BaseClass(formManager, formWidget, parent)
{
}
} // namespace pdf

View File

@ -20,12 +20,14 @@
#include "pdfobject.h"
#include "pdfannotation.h"
#include "pdfdocumentdrawinterface.h"
#include <optional>
namespace pdf
{
class PDFFormField;
class PDFFormManager;
class PDFObjectStorage;
class PDFModifiedDocument;
@ -190,6 +192,12 @@ public:
/// Reloads value from object storage. Actually stored value is lost.
void reloadValue(const PDFObjectStorage* storage, PDFObject parentValue);
/// Applies function to this form field and all its descendants,
/// in pre-order (first application is to the parent, following
/// calls to apply for children).
/// \param functor Functor to apply
void apply(const std::function<void(const PDFFormField*)>& functor) const;
/// Parses form field from the object reference. If some error occurs
/// then null pointer is returned, no exception is thrown.
/// \param storage Storage
@ -358,10 +366,99 @@ private:
PDFWidgetToFormFieldMapping m_widgetToFormField;
};
/// Base class for editors of form fields. It enables editation
/// of form fields, such as entering text, clicking on check box etc.
class PDFFormFieldWidgetEditor : public QObject
{
Q_OBJECT
private:
using BaseClass = QObject;
public:
explicit PDFFormFieldWidgetEditor(PDFFormManager* formManager, PDFFormWidget formWidget, QObject* parent);
virtual ~PDFFormFieldWidgetEditor() = default;
PDFFormField* getFormField() const { return m_formWidget.getParent(); }
PDFObjectReference getWidgetAnnotation() const { return m_formWidget.getWidget(); }
void setFocus(bool hasFocus);
private:
PDFFormManager* m_formManager;
PDFFormWidget m_formWidget;
bool m_hasFocus;
};
/// Editor for push buttons
class PDFFormFieldPushButtonEditor : public PDFFormFieldWidgetEditor
{
Q_OBJECT
private:
using BaseClass = PDFFormFieldWidgetEditor;
public:
explicit PDFFormFieldPushButtonEditor(PDFFormManager* formManager, PDFFormWidget formWidget, QObject* parent);
virtual ~PDFFormFieldPushButtonEditor() = default;
};
/// Editor for check boxes or radio buttons
class PDFFormFieldCheckableButtonEditor : public PDFFormFieldWidgetEditor
{
Q_OBJECT
private:
using BaseClass = PDFFormFieldWidgetEditor;
public:
explicit PDFFormFieldCheckableButtonEditor(PDFFormManager* formManager, PDFFormWidget formWidget, QObject* parent);
virtual ~PDFFormFieldCheckableButtonEditor() = default;
};
/// Editor for text fields
class PDFFormFieldTextBoxEditor : public PDFFormFieldWidgetEditor
{
Q_OBJECT
private:
using BaseClass = PDFFormFieldWidgetEditor;
public:
explicit PDFFormFieldTextBoxEditor(PDFFormManager* formManager, PDFFormWidget formWidget, QObject* parent);
virtual ~PDFFormFieldTextBoxEditor() = default;
};
/// Editor for combo boxes
class PDFFormFieldComboBoxEditor : public PDFFormFieldWidgetEditor
{
Q_OBJECT
private:
using BaseClass = PDFFormFieldWidgetEditor;
public:
explicit PDFFormFieldComboBoxEditor(PDFFormManager* formManager, PDFFormWidget formWidget, QObject* parent);
virtual ~PDFFormFieldComboBoxEditor() = default;
};
/// Editor for list boxes
class PDFFormFieldListBoxEditor : public PDFFormFieldWidgetEditor
{
Q_OBJECT
private:
using BaseClass = PDFFormFieldWidgetEditor;
public:
explicit PDFFormFieldListBoxEditor(PDFFormManager* formManager, PDFFormWidget formWidget, QObject* parent);
virtual ~PDFFormFieldListBoxEditor() = default;
};
/// Form manager. Manages all form widgets functionality - triggers actions,
/// edits fields, updates annotation appearances, etc. Valid pointer to annotation
/// manager is requirement.
class PDFFORQTLIBSHARED_EXPORT PDFFormManager : public QObject
class PDFFORQTLIBSHARED_EXPORT PDFFormManager : public QObject, public IDrawWidgetInputInterface
{
Q_OBJECT
@ -394,13 +491,78 @@ public:
const PDFDocument* getDocument() const;
void setDocument(const PDFModifiedDocument& document);
/// Returns default form apperance flags
static constexpr FormAppearanceFlags getDefaultApperanceFlags() { return HighlightFields | HighlightRequiredFields; }
FormAppearanceFlags getAppearanceFlags() const;
void setAppearanceFlags(FormAppearanceFlags flags);
/// Returns true, if form field has text (for example, it is a text box,
/// or editable combo box)
/// \param widgetAnnotation Widget annotation
bool hasFormFieldWidgetText(PDFObjectReference widgetAnnotation) const;
/// Returns all form field widgets. This function is not simple getter,
/// call can be expensive, because all form fields are accessed.
PDFFormWidgets getWidgets() const;
/// Applies function to all form fields present in the form,
/// in pre-order (first application is to the parent, following
/// calls to apply for children).
/// \param functor Functor to apply
void apply(const std::function<void(const PDFFormField*)>& functor) const;
/// Sets focus to the editor. Is is allowed to pass nullptr to this
/// function, it means that no editor is focused.
/// \param editor Editor to be focused
void setFocusToEditor(PDFFormFieldWidgetEditor* editor);
/// Tries to set focus on next/previous form field in the focus chain.
/// Function returns true, if focus has been successfully set.
/// \param next Focus next (true) or previous (false) widget
bool focusNextPrevFormField(bool next);
/// Returns true, if widget is focused.
/// \param widget Widget annotation reference
bool isFocused(PDFObjectReference widget) const;
/// Returns default form apperance flags
static constexpr FormAppearanceFlags getDefaultApperanceFlags() { return HighlightFields | HighlightRequiredFields; }
// interface IDrawWidgetInputInterface
/// Handles key press event from widget, over which tool operates
/// \param widget Widget, over which tool operates
/// \param event Event
virtual void keyPressEvent(QWidget* widget, QKeyEvent* event) override;
/// Handles mouse press event from widget, over which tool operates
/// \param widget Widget, over which tool operates
/// \param event Event
virtual void mousePressEvent(QWidget* widget, QMouseEvent* event) override;
/// Handles mouse release event from widget, over which tool operates
/// \param widget Widget, over which tool operates
/// \param event Event
virtual void mouseReleaseEvent(QWidget* widget, QMouseEvent* event) override;
/// Handles mouse move event from widget, over which tool operates
/// \param widget Widget, over which tool operates
/// \param event Event
virtual void mouseMoveEvent(QWidget* widget, QMouseEvent* event) override;
/// Handles mouse wheel event from widget, over which tool operates
/// \param widget Widget, over which tool operates
/// \param event Event
virtual void wheelEvent(QWidget* widget, QWheelEvent* event) override;
/// Returns tooltip generated from annotation
virtual QString getTooltip() const override { return QString(); }
/// Returns current cursor
virtual const std::optional<QCursor>& getCursor() const override;
virtual int getInputPriority() const override { return FormPriority; }
private:
void updateFormWidgetEditors();
void updateFieldValues();
PDFDrawWidgetProxy* m_proxy;
@ -408,6 +570,9 @@ private:
const PDFDocument* m_document;
FormAppearanceFlags m_flags;
PDFForm m_form;
std::vector<PDFFormFieldWidgetEditor*> m_widgetEditors;
PDFFormFieldWidgetEditor* m_focusedEditor;
};
} // namespace pdf

View File

@ -275,6 +275,7 @@ PDFViewerMainWindow::PDFViewerMainWindow(QWidget* parent) :
m_formManager->setAnnotationManager(m_annotationManager);
m_formManager->setAppearanceFlags(m_settings->getSettings().m_formAppearanceFlags);
m_annotationManager->setFormManager(m_formManager);
m_pdfWidget->setFormManager(m_formManager);
connect(m_pdfWidget->getDrawWidgetProxy(), &pdf::PDFDrawWidgetProxy::drawSpaceChanged, this, &PDFViewerMainWindow::onDrawSpaceChanged);
connect(m_pdfWidget->getDrawWidgetProxy(), &pdf::PDFDrawWidgetProxy::pageLayoutChanged, this, &PDFViewerMainWindow::onPageLayoutChanged);