diff --git a/autotests/data/formattest.pdf b/autotests/data/formattest.pdf index a51d55213..c3138bebb 100644 Binary files a/autotests/data/formattest.pdf and b/autotests/data/formattest.pdf differ diff --git a/autotests/formattest.cpp b/autotests/formattest.cpp index 63f9a9191..650043945 100644 --- a/autotests/formattest.cpp +++ b/autotests/formattest.cpp @@ -33,6 +33,8 @@ private slots: void testSpecialFormat_data(); void testFocusAction(); void testFocusAction_data(); + void testValidateAction(); + void testValidateAction_data(); private: Okular::Document *m_document; @@ -154,6 +156,28 @@ void FormatTest::testFocusAction_data() QTest::newRow( "when focuses" ) << QStringLiteral( "No" ); } +void FormatTest::testValidateAction() +{ + QFETCH( QString, text ); + QFETCH( QString, result ); + Okular::FormFieldText *fft = reinterpret_cast< Okular::FormFieldText * >( m_fields[ "Validate/Focus" ] ); + + fft->setText( text ); + bool ok = false; + m_document->processValidateAction( fft->additionalAction( Okular::Annotation::FocusOut ), fft, ok ); + QCOMPARE( fft->text(), result ); + QVERIFY( ok ); +} + +void FormatTest::testValidateAction_data() +{ + QTest::addColumn< QString >( "text" ); + QTest::addColumn< QString >( "result" ); + + QTest::newRow( "valid text was set" ) << QStringLiteral( "123" ) << QStringLiteral( "valid" ); + QTest::newRow( "invalid text was set" ) << QStringLiteral( "abc" ) << QStringLiteral( "invalid" ); +} + void FormatTest::cleanupTestCase() { m_document->closeDocument(); diff --git a/core/document.cpp b/core/document.cpp index 21af1f3d5..c152d18a5 100644 --- a/core/document.cpp +++ b/core/document.cpp @@ -4442,6 +4442,44 @@ void Document::processFocusAction( const Action * action, Okular::FormField *fie d->m_scripter->setEvent( nullptr ); } +void Document::processValidateAction( const Action * action, Okular::FormFieldText *fft, bool &returnCode ) +{ + if ( !action || action->actionType() != Action::Script ) + return; + + // Lookup the page of the FormFieldText + int foundPage = -1; + for ( uint pageIdx = 0, nPages = pages(); pageIdx < nPages; pageIdx++ ) + { + const Page *p = page( pageIdx ); + if ( p && p->formFields().contains( fft ) ) + { + foundPage = static_cast< int >( pageIdx ); + break; + } + } + + if ( foundPage == -1 ) + { + qCDebug( OkularCoreDebug ) << "Could not find page for formfield!"; + return; + } + + std::shared_ptr< Event > event = Event::createFormValidateEvent( fft, d->m_pagesVector[foundPage] ); + + const ScriptAction * linkscript = static_cast< const ScriptAction * >( action ); + if ( !d->m_scripter ) + { + d->m_scripter = new Scripter( d ); + } + d->m_scripter->setEvent( event.get() ); + d->m_scripter->execute( linkscript->scriptType(), linkscript->script() ); + + // Clear out the event after execution + d->m_scripter->setEvent( nullptr ); + returnCode = event->returnCode(); +} + void Document::processSourceReference( const SourceReference * ref ) { if ( !ref ) diff --git a/core/document.h b/core/document.h index 1bc4331ea..064e2661b 100644 --- a/core/document.h +++ b/core/document.h @@ -688,7 +688,14 @@ class OKULARCORE_EXPORT Document : public QObject * @since 1.9 */ void processFocusAction( const Action *action, Okular::FormField *field ); - + + /** + * Processes the given keystroke @p action on @p field. + * + * @since 1.9 + */ + void processValidateAction( const Action *action, Okular::FormFieldText *field, bool &returnCode ); + /** * Returns a list of the bookmarked.pages */ diff --git a/core/script/event.cpp b/core/script/event.cpp index 8a3abe611..f4c55eab7 100644 --- a/core/script/event.cpp +++ b/core/script/event.cpp @@ -64,6 +64,8 @@ QString Event::name() const return QStringLiteral( "Keystroke" ); case ( FieldFocus ): return QStringLiteral( "Focus" ); + case ( FieldValidate ): + return QStringLiteral( "Validate" ); case ( UnknownEvent ): default: return QStringLiteral( "Unknown" ); @@ -78,6 +80,7 @@ QString Event::type() const case ( FieldFormat ): case ( FieldKeystroke ): case ( FieldFocus ): + case ( FieldValidate ): return QStringLiteral( "Field" ); case ( UnknownEvent ): default: @@ -242,4 +245,23 @@ std::shared_ptr Event::createFormFocusEvent( FormField *target, ret->setValue( QVariant( fft->text() ) ); } return ret; +} + +std::shared_ptr Event::createFormValidateEvent( FormField *target, + Page *targetPage, + const QString &targetName ) +{ + std::shared_ptr ret( new Event( Event::FieldValidate ) ); + ret->setTarget( target ); + ret->setTargetPage( targetPage ); + ret->setTargetName( targetName ); + ret->setShiftModifier( QApplication::keyboardModifiers() & Qt::ShiftModifier ); + + FormFieldText *fft = dynamic_cast< FormFieldText * >(target); + if ( fft ) + { + ret->setValue( QVariant( fft->text() ) ); + ret->setReturnCode( true ); + } + return ret; } \ No newline at end of file diff --git a/core/script/event_p.h b/core/script/event_p.h index a13be4aff..5e59766c4 100644 --- a/core/script/event_p.h +++ b/core/script/event_p.h @@ -63,7 +63,11 @@ class Event FieldMouseEnter, /// < Not implemented. FieldMouseExit, /// < Not implemented. FieldMouseUp, /// < Not implemented. - FieldValidate, /// < Not implemented. + /* Validates the field after every change is commited + * (clicked outside or tabbed to another field). + * The enter event is not handled + */ + FieldValidate, LinkMouseUp, /// < Not implemented. MenuExec, /// < Not implemented. PageOpen, /// < Not implemented. @@ -115,6 +119,9 @@ class Event static std::shared_ptr createFormFocusEvent( FormField *target, Page *targetPage, const QString &targetName = QString() ); + static std::shared_ptr createFormValidateEvent( FormField *target, + Page *targetPage, + const QString &targetName = QString() ); private: class Private; std::shared_ptr d; diff --git a/core/script/kjs_event.cpp b/core/script/kjs_event.cpp index f4e6224ac..c39bb9f94 100644 --- a/core/script/kjs_event.cpp +++ b/core/script/kjs_event.cpp @@ -79,6 +79,7 @@ static KJSObject eventGetTarget( KJSContext *ctx, void *object ) case Event::FieldFormat: case Event::FieldKeystroke: case Event::FieldFocus: + case Event::FieldValidate: { FormField *target = static_cast< FormField * >( event->target() ); if ( target ) diff --git a/ui/formwidgets.cpp b/ui/formwidgets.cpp index 63af3ea62..e7440e6ca 100644 --- a/ui/formwidgets.cpp +++ b/ui/formwidgets.cpp @@ -90,6 +90,30 @@ void FormWidgetsController::signalAction( Okular::Action *a ) emit action( a ); } +void FormWidgetsController::processScriptAction( Okular::Action *a, Okular::FormField * field, Okular::Annotation::AdditionalActionType type ) +{ + // If it's not a Action Script or if the field is not a FormText, handle it normally + if( a->actionType() != Okular::Action::Script || field->type() != Okular::FormField::FormText ) + { + emit action( a ); + return; + } + switch( type ) + { + // These cases are to be handled by the FormField text, so we let it happen. + case Okular::Annotation::FocusIn: + case Okular::Annotation::FocusOut: + return; + case Okular::Annotation::PageOpening: + case Okular::Annotation::PageClosing: + case Okular::Annotation::CursorEntering: + case Okular::Annotation::CursorLeaving: + case Okular::Annotation::MousePressed: + case Okular::Annotation::MouseReleased: + emit action( a ); + } +} + void FormWidgetsController::registerRadioButton( FormWidgetIface *fwButton, Okular::FormFieldButton *formButton ) { if ( !fwButton ) @@ -510,10 +534,21 @@ bool FormLineEdit::event( QEvent* e ) m_editing = true; if( const Okular::Action *action = m_ff->additionalAction( Okular::Annotation::FocusIn ) ) emit m_controller->focusAction( action, fft ); + setFocus(); } else if ( e->type() == QEvent::FocusOut ) { + // Don't worry about focus events from other sources than the user FocusEvent to edit the field + QFocusEvent *focusEvent = static_cast< QFocusEvent* >( e ); + if( focusEvent->reason() == Qt::OtherFocusReason ) + return true; m_editing = false; + + if( const Okular::Action *action = m_ff->additionalAction( Okular::Annotation::FocusOut ) ) + { + bool ok = false; + emit m_controller->validateAction( action, static_cast< Okular::FormFieldText * > ( m_ff ), ok ); + } if ( const Okular::Action *action = m_ff->additionalAction( Okular::FormField::FormatField ) ) { emit m_controller->formatAction( action, static_cast< Okular::FormFieldText * > ( m_ff ) ); @@ -1299,7 +1334,7 @@ void SignatureEdit::slotViewProperties() Okular::Action *act = m_ff->additionalAction( Okular::Annotation::FocusIn ); \ if ( act ) \ { \ - m_controller->signalAction( act ); \ + m_controller->processScriptAction( act, m_ff, Okular::Annotation::FocusIn ); \ } \ BaseClass::focusInEvent( event ); \ } \ @@ -1308,7 +1343,7 @@ void SignatureEdit::slotViewProperties() Okular::Action *act = m_ff->additionalAction( Okular::Annotation::FocusOut ); \ if ( act ) \ { \ - m_controller->signalAction( act ); \ + m_controller->processScriptAction( act, m_ff, Okular::Annotation::FocusOut ); \ } \ BaseClass::focusOutEvent( event ); \ } \ diff --git a/ui/formwidgets.h b/ui/formwidgets.h index fc226f89c..41e64213d 100644 --- a/ui/formwidgets.h +++ b/ui/formwidgets.h @@ -61,6 +61,8 @@ class FormWidgetsController : public QObject void signalAction( Okular::Action *action ); + void processScriptAction( Okular::Action *a, Okular::FormField * field, Okular::Annotation::AdditionalActionType type ); + void registerRadioButton( FormWidgetIface *fwButton, Okular::FormFieldButton *formButton ); void dropRadioButtons(); bool canUndo(); @@ -123,6 +125,8 @@ class FormWidgetsController : public QObject void keystrokeAction( const Okular::Action *action, Okular::FormFieldText *ff, bool &ok ); + void validateAction( const Okular::Action *action, Okular::FormFieldText *ff, bool &ok ); + void refreshFormWidget( Okular::FormField * form ); private Q_SLOTS: diff --git a/ui/pageview.cpp b/ui/pageview.cpp index e3135a943..862060019 100644 --- a/ui/pageview.cpp +++ b/ui/pageview.cpp @@ -276,6 +276,10 @@ FormWidgetsController* PageViewPrivate::formWidgetsController() q, [this] (const Okular::Action *action, Okular::FormFieldText *fft ) { document->processFocusAction( action, fft ); } ); + QObject::connect( formsWidgetController, &FormWidgetsController::validateAction, + q, [this] (const Okular::Action *action, Okular::FormFieldText *fft, bool &ok ) { + document->processValidateAction( action, fft, ok ); + } ); } return formsWidgetController;