diff --git a/PdfForQtLib/sources/pdfannotation.cpp b/PdfForQtLib/sources/pdfannotation.cpp index 0cdac8c..d23c967 100644 --- a/PdfForQtLib/sources/pdfannotation.cpp +++ b/PdfForQtLib/sources/pdfannotation.cpp @@ -114,7 +114,7 @@ PDFAppeareanceStreams PDFAppeareanceStreams::parse(const PDFObjectStorage* stora { PDFAppeareanceStreams result; - auto processSubdicitonary = [&result, storage](Appearance appearance, PDFObject subdictionaryObject) + auto processSubdictionary = [&result, storage](Appearance appearance, PDFObject subdictionaryObject) { subdictionaryObject = storage->getObject(subdictionaryObject); if (subdictionaryObject.isDictionary()) @@ -133,9 +133,9 @@ PDFAppeareanceStreams PDFAppeareanceStreams::parse(const PDFObjectStorage* stora if (const PDFDictionary* dictionary = storage->getDictionaryFromObject(object)) { - processSubdicitonary(Appearance::Normal, dictionary->get("N")); - processSubdicitonary(Appearance::Rollover, dictionary->get("R")); - processSubdicitonary(Appearance::Down, dictionary->get("D")); + processSubdictionary(Appearance::Normal, dictionary->get("N")); + processSubdictionary(Appearance::Rollover, dictionary->get("R")); + processSubdictionary(Appearance::Down, dictionary->get("D")); } return result; @@ -507,7 +507,7 @@ PDFAnnotationPtr PDFAnnotation::parse(const PDFObjectStorage* storage, PDFObject annotation->m_screenTitle = loader.readTextStringFromDictionary(dictionary, "T", QString()); annotation->m_appearanceCharacteristics = PDFAnnotationAppearanceCharacteristics::parse(storage, dictionary->get("MK")); annotation->m_action = PDFAction::parse(storage, dictionary->get("A")); - annotation->m_additionalActions = PDFAnnotationAdditionalActions::parse(storage, dictionary->get("AA")); + annotation->m_additionalActions = PDFAnnotationAdditionalActions::parse(storage, dictionary->get("AA"), dictionary->get("A")); } else if (subtype == "Widget") { @@ -525,7 +525,7 @@ PDFAnnotationPtr PDFAnnotation::parse(const PDFObjectStorage* storage, PDFObject annotation->m_highlightMode = loader.readEnumByName(dictionary->get("H"), highlightModes.begin(), highlightModes.end(), PDFWidgetAnnotation::HighlightMode::Invert); annotation->m_appearanceCharacteristics = PDFAnnotationAppearanceCharacteristics::parse(storage, dictionary->get("MK")); annotation->m_action = PDFAction::parse(storage, dictionary->get("A")); - annotation->m_additionalActions = PDFAnnotationAdditionalActions::parse(storage, dictionary->get("AA")); + annotation->m_additionalActions = PDFAnnotationAdditionalActions::parse(storage, dictionary->get("AA"), dictionary->get("A")); } else if (subtype == "PrinterMark") { @@ -866,7 +866,7 @@ PDFAnnotationIconFitInfo PDFAnnotationIconFitInfo::parse(const PDFObjectStorage* return info; } -PDFAnnotationAdditionalActions PDFAnnotationAdditionalActions::parse(const PDFObjectStorage* storage, PDFObject object) +PDFAnnotationAdditionalActions PDFAnnotationAdditionalActions::parse(const PDFObjectStorage* storage, PDFObject object, PDFObject defaultAction) { PDFAnnotationAdditionalActions result; @@ -884,6 +884,7 @@ PDFAnnotationAdditionalActions PDFAnnotationAdditionalActions::parse(const PDFOb result.m_actions[PageHide] = PDFAction::parse(storage, dictionary->get("PI")); } + result.m_actions[Default] = PDFAction::parse(storage, defaultAction); return result; } @@ -1342,6 +1343,12 @@ void PDFWidgetAnnotationManager::keyPressEvent(QWidget* widget, QKeyEvent* event Q_UNUSED(event); } +void PDFWidgetAnnotationManager::keyReleaseEvent(QWidget* widget, QKeyEvent* event) +{ + Q_UNUSED(widget); + Q_UNUSED(event); +} + void PDFWidgetAnnotationManager::mousePressEvent(QWidget* widget, QMouseEvent* event) { Q_UNUSED(widget); diff --git a/PdfForQtLib/sources/pdfannotation.h b/PdfForQtLib/sources/pdfannotation.h index dab9a39..de59fda 100644 --- a/PdfForQtLib/sources/pdfannotation.h +++ b/PdfForQtLib/sources/pdfannotation.h @@ -383,6 +383,7 @@ public: PageClosed, PageShow, PageHide, + Default, End }; @@ -397,7 +398,8 @@ public: /// empty additional actions is constructed. /// \param storage Object storage /// \param object Additional actions object - static PDFAnnotationAdditionalActions parse(const PDFObjectStorage* storage, PDFObject object); + /// \param defaultAction Default action of object (defined in "A" entry of annotation dictionary) + static PDFAnnotationAdditionalActions parse(const PDFObjectStorage* storage, PDFObject object, PDFObject defaultAction); private: std::array m_actions; @@ -1409,6 +1411,11 @@ public: /// \param event Event virtual void keyPressEvent(QWidget* widget, QKeyEvent* event) override; + /// Handles key release event from widget + /// \param widget Widget + /// \param event Event + virtual void keyReleaseEvent(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 @@ -1438,7 +1445,7 @@ public: virtual int getInputPriority() const override { return AnnotationPriority; } signals: - void actionTriggered(const pdf::PDFAction* action); + void actionTriggered(const PDFAction* action); private: void updateFromMouseEvent(QMouseEvent* event); diff --git a/PdfForQtLib/sources/pdfdocumentdrawinterface.h b/PdfForQtLib/sources/pdfdocumentdrawinterface.h index 7108083..b7c8541 100644 --- a/PdfForQtLib/sources/pdfdocumentdrawinterface.h +++ b/PdfForQtLib/sources/pdfdocumentdrawinterface.h @@ -79,6 +79,11 @@ public: /// \param event Event virtual void keyPressEvent(QWidget* widget, QKeyEvent* event) = 0; + /// Handles key release event from widget + /// \param widget Widget + /// \param event Event + virtual void keyReleaseEvent(QWidget* widget, QKeyEvent* event) = 0; + /// Handles mouse press event from widget /// \param widget Widget /// \param event Event diff --git a/PdfForQtLib/sources/pdfdrawwidget.cpp b/PdfForQtLib/sources/pdfdrawwidget.cpp index 0bee4b8..432b4d5 100644 --- a/PdfForQtLib/sources/pdfdrawwidget.cpp +++ b/PdfForQtLib/sources/pdfdrawwidget.cpp @@ -339,6 +339,19 @@ void PDFDrawWidgetBase::keyPressEvent(QKeyEvent* event) updateCursor(); } +template +void PDFDrawWidgetBase::keyReleaseEvent(QKeyEvent* event) +{ + event->ignore(); + + if (processEvent(event)) + { + return; + } + + event->accept(); +} + template void PDFDrawWidgetBase::mousePressEvent(QMouseEvent* event) { diff --git a/PdfForQtLib/sources/pdfdrawwidget.h b/PdfForQtLib/sources/pdfdrawwidget.h index 54e6017..8ff1030 100644 --- a/PdfForQtLib/sources/pdfdrawwidget.h +++ b/PdfForQtLib/sources/pdfdrawwidget.h @@ -139,6 +139,7 @@ public: protected: virtual void keyPressEvent(QKeyEvent* event) override; + virtual void keyReleaseEvent(QKeyEvent* event) override; virtual void mousePressEvent(QMouseEvent* event) override; virtual void mouseReleaseEvent(QMouseEvent* event) override; virtual void mouseMoveEvent(QMouseEvent* event) override; diff --git a/PdfForQtLib/sources/pdfform.cpp b/PdfForQtLib/sources/pdfform.cpp index 5b91ecd..704d806 100644 --- a/PdfForQtLib/sources/pdfform.cpp +++ b/PdfForQtLib/sources/pdfform.cpp @@ -19,6 +19,10 @@ #include "pdfdocument.h" #include "pdfdrawspacecontroller.h" +#include +#include +#include + namespace pdf { @@ -228,7 +232,7 @@ PDFFormFieldPointer PDFFormField::parse(const PDFObjectStorage* storage, PDFObje formField->m_fieldFlags = fieldDictionary->hasKey("Ff") ? static_cast(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")); + formField->m_additionalActions = PDFAnnotationAdditionalActions::parse(storage, fieldDictionary->get("AA"), fieldDictionary->get("A")); // 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). @@ -340,17 +344,23 @@ PDFFormFieldPointer PDFFormField::parse(const PDFObjectStorage* storage, PDFObje return result; } -PDFFormWidget::PDFFormWidget(PDFObjectReference widget, PDFFormField* parentField) : +PDFFormWidget::PDFFormWidget(PDFObjectReference widget, PDFFormField* parentField, PDFAnnotationAdditionalActions actions) : m_widget(widget), - m_parentField(parentField) + m_parentField(parentField), + m_actions(qMove(actions)) { } PDFFormWidget PDFFormWidget::parse(const PDFObjectStorage* storage, PDFObjectReference reference, PDFFormField* parentField) { - Q_UNUSED(storage); - return PDFFormWidget(reference, parentField); + PDFAnnotationAdditionalActions actions; + if (const PDFDictionary* annotationDictionary = storage->getDictionaryFromObject(storage->getObjectByReference(reference))) + { + actions = PDFAnnotationAdditionalActions::parse(storage, annotationDictionary->get("AA"), annotationDictionary->get("A")); + } + + return PDFFormWidget(reference, parentField, qMove(actions)); } PDFFormFieldButton::ButtonType PDFFormFieldButton::getButtonType() const @@ -563,10 +573,38 @@ bool PDFFormManager::isFocused(PDFObjectReference widget) const return false; } +const PDFAction* PDFFormManager::getAction(PDFAnnotationAdditionalActions::Action actionType, const PDFFormWidget* widget) +{ + if (const PDFAction* action = widget->getAction(actionType)) + { + return action; + } + + for (const PDFFormField* formField = widget->getParent(); formField; formField = formField->getParentField()) + { + if (const PDFAction* action = formField->getAction(actionType)) + { + return action; + } + } + + return nullptr; +} + void PDFFormManager::keyPressEvent(QWidget* widget, QKeyEvent* event) { - Q_UNUSED(widget); - Q_UNUSED(event); + if (m_focusedEditor) + { + m_focusedEditor->keyPressEvent(widget, event); + } +} + +void PDFFormManager::keyReleaseEvent(QWidget* widget, QKeyEvent* event) +{ + if (m_focusedEditor) + { + m_focusedEditor->keyReleaseEvent(widget, event); + } } void PDFFormManager::mousePressEvent(QWidget* widget, QMouseEvent* event) @@ -695,17 +733,170 @@ PDFFormFieldWidgetEditor::PDFFormFieldWidgetEditor(PDFFormManager* formManager, Q_ASSERT(m_formManager); } +void PDFFormFieldWidgetEditor::keyPressEvent(QWidget* widget, QKeyEvent* event) +{ + Q_UNUSED(widget); + Q_UNUSED(event); +} + +void PDFFormFieldWidgetEditor::keyReleaseEvent(QWidget* widget, QKeyEvent* event) +{ + Q_UNUSED(widget); + Q_UNUSED(event); +} + +void PDFFormFieldWidgetEditor::mousePressEvent(QWidget* widget, QMouseEvent* event) +{ + Q_UNUSED(widget); + Q_UNUSED(event); +} + +void PDFFormFieldWidgetEditor::mouseReleaseEvent(QWidget* widget, QMouseEvent* event) +{ + Q_UNUSED(widget); + Q_UNUSED(event); +} + +void PDFFormFieldWidgetEditor::mouseMoveEvent(QWidget* widget, QMouseEvent* event) +{ + Q_UNUSED(widget); + Q_UNUSED(event); +} + void PDFFormFieldWidgetEditor::setFocus(bool hasFocus) { m_hasFocus = hasFocus; } +void PDFFormFieldWidgetEditor::performKeypadNavigation(QWidget* widget, QKeyEvent* event) +{ + int key = event->key(); + + const bool isLeft = key == Qt::Key_Left; + const bool isRight = key == Qt::Key_Right; + const bool isUp = key == Qt::Key_Up; + const bool isDown = key == Qt::Key_Down; + const bool isHorizontal = isLeft || isRight; + + Qt::NavigationMode navigationMode = Qt::NavigationModeKeypadDirectional; +#ifdef QT_KEYPAD_NAVIGATION + navigationMode = QApplication::navigationMode(); +#endif + + switch (navigationMode) + { + case Qt::NavigationModeKeypadTabOrder: + { + // According the Qt's documentation, Up/Down arrows are used + // to change focus. So, if user pressed Left/Right, we must + // ignore this event. + if (isHorizontal) + { + return; + } + break; + } + + case Qt::NavigationModeKeypadDirectional: + // Default behaviour + break; + + default: + // Nothing happens + return; + } + + bool next = false; + if (isHorizontal) + { + switch (widget->layoutDirection()) + { + case Qt::LeftToRight: + case Qt::LayoutDirectionAuto: + next = isRight; + break; + + case Qt::RightToLeft: + next = isLeft; + break; + + default: + Q_ASSERT(false); + break; + } + } + else + { + // Vertical + next = isDown; + } + + m_formManager->focusNextPrevFormField(next); +} + PDFFormFieldPushButtonEditor::PDFFormFieldPushButtonEditor(PDFFormManager* formManager, PDFFormWidget formWidget, QObject* parent) : BaseClass(formManager, formWidget, parent) { } +void PDFFormFieldPushButtonEditor::keyPressEvent(QWidget* widget, QKeyEvent* event) +{ + switch (event->key()) + { + case Qt::Key_Enter: + case Qt::Key_Return: + { + click(); + event->accept(); + break; + } + + case Qt::Key_Left: + case Qt::Key_Right: + case Qt::Key_Up: + case Qt::Key_Down: + { + performKeypadNavigation(widget, event); + break; + } + + default: + break; + } +} + +void PDFFormFieldPushButtonEditor::keyReleaseEvent(QWidget* widget, QKeyEvent* event) +{ + Q_UNUSED(widget); + + switch (event->key()) + { + case Qt::Key_Select: + case Qt::Key_Space: + { + click(); + event->accept(); + break; + } + + default: + break; + } +} + +void PDFFormFieldPushButtonEditor::click() +{ + if (const PDFAction* action = m_formManager->getAction(PDFAnnotationAdditionalActions::MousePressed, getFormWidget())) + { + emit m_formManager->actionTriggered(action); + } + else if (const PDFAction* action = m_formManager->getAction(PDFAnnotationAdditionalActions::Default, getFormWidget())) + { + emit m_formManager->actionTriggered(action); + } +} + PDFFormFieldCheckableButtonEditor::PDFFormFieldCheckableButtonEditor(PDFFormManager* formManager, PDFFormWidget formWidget, QObject* parent) : BaseClass(formManager, formWidget, parent) { diff --git a/PdfForQtLib/sources/pdfform.h b/PdfForQtLib/sources/pdfform.h index e1f0937..ae4daef 100644 --- a/PdfForQtLib/sources/pdfform.h +++ b/PdfForQtLib/sources/pdfform.h @@ -40,10 +40,11 @@ class PDFFormWidget { public: explicit inline PDFFormWidget() = default; - explicit inline PDFFormWidget(PDFObjectReference widget, PDFFormField* parentField); + explicit inline PDFFormWidget(PDFObjectReference widget, PDFFormField* parentField, PDFAnnotationAdditionalActions actions); PDFObjectReference getWidget() const { return m_widget; } PDFFormField* getParent() const { return m_parentField; } + const PDFAction* getAction(PDFAnnotationAdditionalActions::Action action) const { return m_actions.getAction(action); } /// Parses form widget from the object reference. If some error occurs /// then empty object is returned, no exception is thrown. @@ -55,6 +56,7 @@ public: private: PDFObjectReference m_widget; PDFFormField* m_parentField; + PDFAnnotationAdditionalActions m_actions; }; using PDFFormWidgets = std::vector; @@ -198,6 +200,10 @@ public: /// \param functor Functor to apply void apply(const std::function& functor) const; + /// Returns action by type. If action is not found, nullptr is returned + /// \param action Action type + const PDFAction* getAction(PDFAnnotationAdditionalActions::Action action) const { return m_additionalActions.getAction(action); } + /// Parses form field from the object reference. If some error occurs /// then null pointer is returned, no exception is thrown. /// \param storage Storage @@ -379,12 +385,21 @@ public: explicit PDFFormFieldWidgetEditor(PDFFormManager* formManager, PDFFormWidget formWidget, QObject* parent); virtual ~PDFFormFieldWidgetEditor() = default; + virtual void keyPressEvent(QWidget* widget, QKeyEvent* event); + virtual void keyReleaseEvent(QWidget* widget, QKeyEvent* event); + virtual void mousePressEvent(QWidget* widget, QMouseEvent* event); + virtual void mouseReleaseEvent(QWidget* widget, QMouseEvent* event); + virtual void mouseMoveEvent(QWidget* widget, QMouseEvent* event); + + const PDFFormWidget* getFormWidget() const { return &m_formWidget; } PDFFormField* getFormField() const { return m_formWidget.getParent(); } PDFObjectReference getWidgetAnnotation() const { return m_formWidget.getWidget(); } void setFocus(bool hasFocus); -private: +protected: + void performKeypadNavigation(QWidget* widget, QKeyEvent* event); + PDFFormManager* m_formManager; PDFFormWidget m_formWidget; bool m_hasFocus; @@ -401,6 +416,12 @@ private: public: explicit PDFFormFieldPushButtonEditor(PDFFormManager* formManager, PDFFormWidget formWidget, QObject* parent); virtual ~PDFFormFieldPushButtonEditor() = default; + + virtual void keyPressEvent(QWidget* widget, QKeyEvent* event); + virtual void keyReleaseEvent(QWidget* widget, QKeyEvent* event); + +private: + void click(); }; /// Editor for check boxes or radio buttons @@ -523,33 +544,44 @@ public: /// \param widget Widget annotation reference bool isFocused(PDFObjectReference widget) const; + /// Tries to find appropriate action and returns it. If action is not found, + /// then nullptr is returned. + /// \param actionType Action to be performed + /// \param widget Form field widget + const PDFAction* getAction(PDFAnnotationAdditionalActions::Action actionType, const PDFFormWidget* widget); + /// 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 + /// Handles key press event from widget + /// \param widget Widget /// \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 + /// Handles key release event from widget + /// \param widget Widget + /// \param event Event + virtual void keyReleaseEvent(QWidget* widget, QKeyEvent* event) override; + + /// Handles mouse press event from widget + /// \param widget Widget /// \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 + /// Handles mouse release event from widget + /// \param widget Widget /// \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 + /// Handles mouse move event from widge + /// \param widget Widget /// \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 + /// Handles mouse wheel event from widget + /// \param widget Widget /// \param event Event virtual void wheelEvent(QWidget* widget, QWheelEvent* event) override; @@ -561,6 +593,9 @@ public: virtual int getInputPriority() const override { return FormPriority; } +signals: + void actionTriggered(const PDFAction* action); + private: void updateFormWidgetEditors(); void updateFieldValues(); diff --git a/PdfForQtLib/sources/pdfwidgettool.cpp b/PdfForQtLib/sources/pdfwidgettool.cpp index 2d34fe3..0280b67 100644 --- a/PdfForQtLib/sources/pdfwidgettool.cpp +++ b/PdfForQtLib/sources/pdfwidgettool.cpp @@ -113,6 +113,14 @@ void PDFWidgetTool::keyPressEvent(QWidget* widget, QKeyEvent* event) } } +void PDFWidgetTool::keyReleaseEvent(QWidget* widget, QKeyEvent* event) +{ + if (PDFWidgetTool* tool = getTopToolstackTool()) + { + tool->keyReleaseEvent(widget, event); + } +} + void PDFWidgetTool::mousePressEvent(QWidget* widget, QMouseEvent* event) { if (PDFWidgetTool* tool = getTopToolstackTool()) @@ -799,6 +807,16 @@ void PDFToolManager::keyPressEvent(QWidget* widget, QKeyEvent* event) } } +void PDFToolManager::keyReleaseEvent(QWidget* widget, QKeyEvent* event) +{ + event->ignore(); + + if (PDFWidgetTool* activeTool = getActiveTool()) + { + activeTool->keyReleaseEvent(widget, event); + } +} + void PDFToolManager::mousePressEvent(QWidget* widget, QMouseEvent* event) { event->ignore(); diff --git a/PdfForQtLib/sources/pdfwidgettool.h b/PdfForQtLib/sources/pdfwidgettool.h index 1ed7c7c..4a5c996 100644 --- a/PdfForQtLib/sources/pdfwidgettool.h +++ b/PdfForQtLib/sources/pdfwidgettool.h @@ -66,6 +66,11 @@ public: /// \param event Event virtual void keyPressEvent(QWidget* widget, QKeyEvent* event); + /// Handles key release event from widget + /// \param widget Widget + /// \param event Event + virtual void keyReleaseEvent(QWidget* widget, QKeyEvent* event); + /// Handles mouse press event from widget, over which tool operates /// \param widget Widget, over which tool operates /// \param event Event @@ -446,6 +451,11 @@ public: /// \param event Event virtual void keyPressEvent(QWidget* widget, QKeyEvent* event) override; + /// Handles key release event from widget + /// \param widget Widget + /// \param event Event + virtual void keyReleaseEvent(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 diff --git a/PdfForQtViewer/pdfviewermainwindow.cpp b/PdfForQtViewer/pdfviewermainwindow.cpp index 7e6016b..1da85a5 100644 --- a/PdfForQtViewer/pdfviewermainwindow.cpp +++ b/PdfForQtViewer/pdfviewermainwindow.cpp @@ -276,6 +276,7 @@ PDFViewerMainWindow::PDFViewerMainWindow(QWidget* parent) : m_formManager->setAppearanceFlags(m_settings->getSettings().m_formAppearanceFlags); m_annotationManager->setFormManager(m_formManager); m_pdfWidget->setFormManager(m_formManager); + connect(m_formManager, &pdf::PDFFormManager::actionTriggered, this, &PDFViewerMainWindow::onActionTriggered); connect(m_pdfWidget->getDrawWidgetProxy(), &pdf::PDFDrawWidgetProxy::drawSpaceChanged, this, &PDFViewerMainWindow::onDrawSpaceChanged); connect(m_pdfWidget->getDrawWidgetProxy(), &pdf::PDFDrawWidgetProxy::pageLayoutChanged, this, &PDFViewerMainWindow::onPageLayoutChanged);