Support additional widget actions in PDF Forms

Summary:
This adds support for actions associated with form fields
through corresponding annotation widgets.

Test Plan:
Still needs a unit test, only tested manually with
the document attached in the task.

Reviewers: #okular, aacid

Reviewed By: #okular, aacid

Subscribers: okular-devel, aacid

Tags: #okular

Maniphest Tasks: T8627

Differential Revision: https://phabricator.kde.org/D12665
remotes/origin/Applications/18.08
Andre Heinecke 8 years ago committed by Albert Astals Cid
parent 3ae2ae06e8
commit 18d404c3ee
  1. 10
      core/annotations.h
  2. 15
      core/form.cpp
  3. 10
      core/form.h
  4. 1
      core/form_p.h
  5. 10
      generators/poppler/CMakeLists.txt
  6. 3
      generators/poppler/config-okular-poppler.h.cmake
  7. 15
      generators/poppler/formfields.cpp
  8. 97
      ui/formwidgets.cpp
  9. 20
      ui/formwidgets.h

@ -191,8 +191,14 @@ class OKULARCORE_EXPORT Annotation
*/
enum AdditionalActionType
{
PageOpening, ///< Performed when the page containing the annotation is opened.
PageClosing ///< Performed when the page containing the annotation is closed.
PageOpening, ///< Performed when the page containing the annotation is opened.
PageClosing, ///< Performed when the page containing the annotation is closed.
CursorEntering, ///< Performed when the cursor enters the annotation's active area @since 1.5
CursorLeaving, ///< Performed when the cursor exists the annotation's active area @since 1.5
MousePressed, ///< Performed when the mouse button is pressed inside the annotation's active area @since 1.5
MouseReleased, ///< Performed when the mouse button is released inside the annotation's active area @since 1.5
FocusIn, ///< Performed when the annotation receives the input focus @since 1.5
FocusOut, ///< Performed when the annotation loses the input focus @since 1.5
};
/**

@ -25,6 +25,8 @@ FormFieldPrivate::FormFieldPrivate( FormField::FieldType type )
FormFieldPrivate::~FormFieldPrivate()
{
delete m_activateAction;
qDeleteAll( m_additionalActions.values() );
qDeleteAll( m_additionalAnnotActions.values() );
}
void FormFieldPrivate::setDefault()
@ -94,6 +96,19 @@ void FormField::setAdditionalAction( AdditionalActionType type, Action *action )
d->m_additionalActions[type] = action;
}
Action* FormField::additionalAction( Annotation::AdditionalActionType type ) const
{
Q_D( const FormField );
return d->m_additionalAnnotActions.value(type);
}
void FormField::setAdditionalAction( Annotation::AdditionalActionType type, Action *action )
{
Q_D( FormField );
delete d->m_additionalAnnotActions.value(type);
d->m_additionalAnnotActions[type] = action;
}
class Okular::FormFieldButtonPrivate : public Okular::FormFieldPrivate
{

@ -12,6 +12,7 @@
#include "okularcore_export.h"
#include "area.h"
#include "annotations.h"
#include <QtCore/QStringList>
@ -127,6 +128,14 @@ class OKULARCORE_EXPORT FormField
*/
Action* additionalAction( AdditionalActionType type ) const;
/* Returns the additional action of the given @p type or @c nullptr if no action has been defined.
*
* This is for actions of annotation widgets associated with the FormField
*
* @since 1.5
*/
Action* additionalAction( Annotation::AdditionalActionType type ) const;
protected:
/// @cond PRIVATE
FormField( FormFieldPrivate &dd );
@ -136,6 +145,7 @@ class OKULARCORE_EXPORT FormField
void setActivationAction( Action *action );
void setAdditionalAction( AdditionalActionType type, Action *action );
void setAdditionalAction( Annotation::AdditionalActionType type, Action *action );
private:
Q_DISABLE_COPY( FormField )

@ -34,6 +34,7 @@ class FormFieldPrivate
QString m_default;
Action *m_activateAction;
QHash<int, Action*> m_additionalActions;
QHash<int, Action*> m_additionalAnnotActions;
Q_DECLARE_PUBLIC( FormField )
FormField *q_ptr;

@ -86,6 +86,16 @@ int main()
}
" HAVE_POPPLER_0_64)
check_cxx_source_compiles("
#include <poppler-qt5.h>
#include <poppler-form.h>
int main()
{
Poppler::FormField *f;
f->additionalAction(Poppler::Annotation::CursorEnteringAction);
}
" HAVE_POPPLER_0_65)
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/config-okular-poppler.h.cmake
${CMAKE_CURRENT_BINARY_DIR}/config-okular-poppler.h

@ -27,3 +27,6 @@
/* Defined if we have the 0.64 version of the Poppler library */
#cmakedefine HAVE_POPPLER_0_64 1
/* Defined if we have the 0.65 version of the Poppler library */
#cmakedefine HAVE_POPPLER_0_65 1

@ -1,5 +1,6 @@
/***************************************************************************
* Copyright (C) 2007 by Pino Toscano <pino@kde.org> *
* Copyright (C) 2018 by Intevation GmbH <intevation@intevation.de> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
@ -16,6 +17,17 @@
#include <config-okular-poppler.h>
extern Okular::Action* createLinkFromPopplerLink(const Poppler::Link *popplerLink, bool deletePopplerLink = true);
#ifdef HAVE_POPPLER_0_65
# define SET_ANNOT_ACTIONS \
setAdditionalAction( Okular::Annotation::CursorEntering, createLinkFromPopplerLink( field->additionalAction( Poppler::Annotation::CursorEnteringAction ) ) ); \
setAdditionalAction( Okular::Annotation::CursorLeaving, createLinkFromPopplerLink( field->additionalAction( Poppler::Annotation::CursorLeavingAction ) ) ); \
setAdditionalAction( Okular::Annotation::MousePressed, createLinkFromPopplerLink( field->additionalAction( Poppler::Annotation::MousePressedAction ) ) ); \
setAdditionalAction( Okular::Annotation::MouseReleased, createLinkFromPopplerLink( field->additionalAction( Poppler::Annotation::MouseReleasedAction ) ) ); \
setAdditionalAction( Okular::Annotation::FocusIn, createLinkFromPopplerLink( field->additionalAction( Poppler::Annotation::FocusInAction ) ) ); \
setAdditionalAction( Okular::Annotation::FocusOut, createLinkFromPopplerLink( field->additionalAction( Poppler::Annotation::FocusOutAction ) ) );
#else
# define SET_ANNOT_ACTIONS
#endif
#ifdef HAVE_POPPLER_0_53
#define SET_ACTIONS \
@ -23,7 +35,8 @@ extern Okular::Action* createLinkFromPopplerLink(const Poppler::Link *popplerLin
setAdditionalAction( Okular::FormField::FieldModified, createLinkFromPopplerLink( field->additionalAction( Poppler::FormField::FieldModified ) ) ); \
setAdditionalAction( Okular::FormField::FormatField, createLinkFromPopplerLink( field->additionalAction( Poppler::FormField::FormatField ) ) ); \
setAdditionalAction( Okular::FormField::ValidateField, createLinkFromPopplerLink( field->additionalAction( Poppler::FormField::ValidateField ) ) ); \
setAdditionalAction( Okular::FormField::CalculateField, createLinkFromPopplerLink( field->additionalAction( Poppler::FormField::CalculateField ) ) );
setAdditionalAction( Okular::FormField::CalculateField, createLinkFromPopplerLink( field->additionalAction( Poppler::FormField::CalculateField ) ) ); \
SET_ANNOT_ACTIONS
#else
#define SET_ACTIONS \
setActivationAction( createLinkFromPopplerLink( field->activationAction() ) );

@ -3,6 +3,7 @@
* Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group *
* company, info@kdab.com. Work sponsored by the *
* LiMux project of the city of Munich *
* Copyright (C) 2018 Intevation GmbH <intevation@intevation.de> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
@ -372,14 +373,6 @@ PushButtonEdit::PushButtonEdit( Okular::FormFieldButton * button, QWidget * pare
setText( button->caption() );
setVisible( button->isVisible() );
setCursor( Qt::ArrowCursor );
connect( this, &QAbstractButton::clicked, this, &PushButtonEdit::slotClicked );
}
void PushButtonEdit::slotClicked()
{
if ( m_ff->activationAction() )
m_controller->signalAction( m_ff->activationAction() );
}
@ -1056,4 +1049,92 @@ bool ComboEdit::event( QEvent* e )
return QComboBox::event( e );
}
// Code for additional action handling.
// Challenge: Change preprocessor magic to C++ magic!
//
// The mouseRelease event is special because the PDF spec
// says that the activation action takes precedence over this.
// So the mouse release action is only signaled if no activation
// action exists.
//
// For checkboxes the activation action is not triggered as
// they are still triggered from the clicked signal and additionally
// when the checked state changes.
#define DEFINE_ADDITIONAL_ACTIONS(FormClass, BaseClass) \
void FormClass::mousePressEvent( QMouseEvent *event ) \
{ \
Okular::Action *act = m_ff->additionalAction( Okular::Annotation::MousePressed ); \
if ( act ) \
{ \
m_controller->signalAction( act ); \
} \
BaseClass::mousePressEvent( event ); \
} \
void FormClass::mouseReleaseEvent( QMouseEvent *event ) \
{ \
if ( !QWidget::rect().contains( event->localPos().toPoint() ) ) \
{ \
BaseClass::mouseReleaseEvent( event ); \
return; \
} \
Okular::Action *act = m_ff->activationAction(); \
if ( act && !qobject_cast< CheckBoxEdit* > ( this ) ) \
{ \
m_controller->signalAction( act ); \
} \
else if ( ( act = m_ff->additionalAction( Okular::Annotation::MouseReleased ) ) ) \
{ \
m_controller->signalAction( act ); \
} \
BaseClass::mouseReleaseEvent( event ); \
} \
void FormClass::focusInEvent( QFocusEvent *event ) \
{ \
Okular::Action *act = m_ff->additionalAction( Okular::Annotation::FocusIn ); \
if ( act ) \
{ \
m_controller->signalAction( act ); \
} \
BaseClass::focusInEvent( event ); \
} \
void FormClass::focusOutEvent( QFocusEvent *event ) \
{ \
Okular::Action *act = m_ff->additionalAction( Okular::Annotation::FocusOut ); \
if ( act ) \
{ \
m_controller->signalAction( act ); \
} \
BaseClass::focusOutEvent( event ); \
} \
void FormClass::leaveEvent( QEvent *event ) \
{ \
Okular::Action *act = m_ff->additionalAction( Okular::Annotation::CursorLeaving ); \
if ( act ) \
{ \
m_controller->signalAction( act ); \
} \
BaseClass::leaveEvent( event ); \
} \
void FormClass::enterEvent( QEvent *event ) \
{ \
Okular::Action *act = m_ff->additionalAction( Okular::Annotation::CursorEntering ); \
if ( act ) \
{ \
m_controller->signalAction( act ); \
} \
BaseClass::enterEvent( event ); \
}
DEFINE_ADDITIONAL_ACTIONS( PushButtonEdit, QPushButton )
DEFINE_ADDITIONAL_ACTIONS( CheckBoxEdit, QCheckBox )
DEFINE_ADDITIONAL_ACTIONS( RadioButtonEdit, QRadioButton )
DEFINE_ADDITIONAL_ACTIONS( FormLineEdit, QLineEdit )
DEFINE_ADDITIONAL_ACTIONS( TextAreaEdit, KTextEdit )
DEFINE_ADDITIONAL_ACTIONS( FileEdit, KUrlRequester )
DEFINE_ADDITIONAL_ACTIONS( ListEdit, QListWidget )
DEFINE_ADDITIONAL_ACTIONS( ComboEdit, QComboBox )
#undef DEFINE_ADDITIONAL_ACTIONS
#include "moc_formwidgets.cpp"

@ -170,6 +170,14 @@ class FormWidgetIface
PageViewItem * m_pageItem;
};
#define DECLARE_ADDITIONAL_ACTIONS \
protected: \
virtual void mousePressEvent( QMouseEvent *event ) override; \
virtual void mouseReleaseEvent( QMouseEvent *event ) override; \
virtual void focusInEvent( QFocusEvent *event ) override; \
virtual void focusOutEvent( QFocusEvent *event ) override; \
virtual void leaveEvent( QEvent *event ) override; \
virtual void enterEvent( QEvent *event ) override;
class PushButtonEdit : public QPushButton, public FormWidgetIface
{
@ -178,8 +186,7 @@ class PushButtonEdit : public QPushButton, public FormWidgetIface
public:
explicit PushButtonEdit( Okular::FormFieldButton * button, QWidget * parent = nullptr );
private Q_SLOTS:
void slotClicked();
DECLARE_ADDITIONAL_ACTIONS
};
class CheckBoxEdit : public QCheckBox, public FormWidgetIface
@ -196,6 +203,7 @@ class CheckBoxEdit : public QCheckBox, public FormWidgetIface
protected:
void slotRefresh( Okular::FormField *form ) override;
DECLARE_ADDITIONAL_ACTIONS
};
class RadioButtonEdit : public QRadioButton, public FormWidgetIface
@ -207,6 +215,7 @@ class RadioButtonEdit : public QRadioButton, public FormWidgetIface
// reimplemented from FormWidgetIface
void setFormWidgetsController( FormWidgetsController *controller ) override;
DECLARE_ADDITIONAL_ACTIONS
};
class FormLineEdit : public QLineEdit, public FormWidgetIface
@ -235,6 +244,7 @@ class FormLineEdit : public QLineEdit, public FormWidgetIface
private:
int m_prevCursorPos;
int m_prevAnchorPos;
DECLARE_ADDITIONAL_ACTIONS
};
class TextAreaEdit : public KTextEdit, public FormWidgetIface
@ -264,6 +274,7 @@ class TextAreaEdit : public KTextEdit, public FormWidgetIface
private:
int m_prevCursorPos;
int m_prevAnchorPos;
DECLARE_ADDITIONAL_ACTIONS
};
@ -289,6 +300,7 @@ class FileEdit : public KUrlRequester, public FormWidgetIface
private:
int m_prevCursorPos;
int m_prevAnchorPos;
DECLARE_ADDITIONAL_ACTIONS
};
@ -305,6 +317,7 @@ class ListEdit : public QListWidget, public FormWidgetIface
void slotHandleFormListChangedByUndoRedo( int pageNumber,
Okular::FormFieldChoice * listForm,
const QList< int > & choices );
DECLARE_ADDITIONAL_ACTIONS
};
@ -330,6 +343,9 @@ class ComboEdit : public QComboBox, public FormWidgetIface
private:
int m_prevCursorPos;
int m_prevAnchorPos;
DECLARE_ADDITIONAL_ACTIONS
};
#undef DECLARE_ADDITIONAL_ACTIONS
#endif

Loading…
Cancel
Save