diff --git a/core/document.h b/core/document.h --- a/core/document.h +++ b/core/document.h @@ -668,6 +668,13 @@ */ void processAction( const Action *action ); + /** + * Processes the given format @p action on @p field. + * + * @since 1.5 + */ + void processFormatAction( const Action *action, Okular::FormFieldText *field ); + /** * Returns a list of the bookmarked.pages */ diff --git a/core/document.cpp b/core/document.cpp --- a/core/document.cpp +++ b/core/document.cpp @@ -1151,8 +1151,16 @@ if ( newVal != oldVal ) { fft->setText( newVal ); - emit m_parent->refreshFormWidget( fft ); - pageNeedsRefresh = true; + if ( const Okular::Action *action = fft->additionalAction( Okular::FormField::FormatField ) ) + { + // The format action handles the refresh. + m_parent->processFormatAction( action, fft ); + } + else + { + emit m_parent->refreshFormWidget( fft ); + pageNeedsRefresh = true; + } } } } @@ -4292,6 +4300,68 @@ } } +void Document::processFormatAction( const Action * action, Okular::FormFieldText *fft ) +{ + if ( action->actionType() != Action::Script ) + { + qCDebug( OkularCoreDebug ) << "Unsupported action type" << action->actionType() << "for formatting."; + 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; + } + + const QString oldVal = fft->text(); + + // We are before formatting. So the unformatted text is currently in fft->text() + // Internally we want to use the current value for calculations and formatting. + fft->setInternalText( oldVal ); + std::shared_ptr< Event > event = Event::createFormatEvent( 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 ); + + const QString newVal = event->value().toString(); + if ( newVal != oldVal ) + { + fft->setText( newVal ); + emit refreshFormWidget( fft ); + d->refreshPixmaps( foundPage ); + } + else if ( fft->additionalAction( FormField::CalculateField ) ) + { + // When the field was calculated we need to refresh even + // if the format script changed nothing. e.g. on error. + // This is because the recalculateForms function delegated + // the responsiblity for the refresh to us. + emit refreshFormWidget( fft ); + d->refreshPixmaps( foundPage ); + } +} + void Document::processSourceReference( const SourceReference * ref ) { if ( !ref ) diff --git a/core/documentcommands.cpp b/core/documentcommands.cpp --- a/core/documentcommands.cpp +++ b/core/documentcommands.cpp @@ -506,14 +506,16 @@ { moveViewportIfBoundingRectNotFullyVisible( m_form->rect(), m_docPriv, m_pageNumber ); m_form->setText( m_prevContents ); + m_form->setInternalText( m_prevContents ); emit m_docPriv->m_parent->formTextChangedByUndoRedo( m_pageNumber, m_form, m_prevContents, m_prevCursorPos, m_prevAnchorPos ); m_docPriv->notifyFormChanges( m_pageNumber ); } void EditFormTextCommand::redo() { moveViewportIfBoundingRectNotFullyVisible( m_form->rect(), m_docPriv, m_pageNumber ); m_form->setText( m_newContents ); + m_form->setInternalText( m_newContents ); emit m_docPriv->m_parent->formTextChangedByUndoRedo( m_pageNumber, m_form, m_newContents, m_newCursorPos, m_newCursorPos ); m_docPriv->notifyFormChanges( m_pageNumber ); } diff --git a/core/form.h b/core/form.h --- a/core/form.h +++ b/core/form.h @@ -317,6 +317,31 @@ */ virtual bool canBeSpellChecked() const; + /** + * Optionally different internal text. + * + * Internal text is the value of the field before formatting + * and should be used for editing and calculations. + * + * The default implementation returns the value of + * @ref text if no internal text was set. + * + * @since 1.5 + */ + virtual QString internalText() const; + + /** + * Set internalText to a value before formatting. + * + * If the text value was changed for display purposes use + * setRawText to store the internal value @p text before + * formatting. The internal text is used for calculations + * and editing. + * + * @since 1.5 + */ + virtual void setInternalText( const QString &text ); + protected: FormFieldText(); diff --git a/core/form.cpp b/core/form.cpp --- a/core/form.cpp +++ b/core/form.cpp @@ -182,6 +182,19 @@ Q_Q( const FormFieldText ); return q->text(); } + + void setInternalText( const QString& v ) + { + m_internalText = v; + } + + QString internalText() const + { + return m_internalText; + } + + private: + QString m_internalText; }; @@ -223,6 +236,23 @@ return false; } +QString FormFieldText::internalText() const +{ + Q_D( const FormFieldText ); + const QString val = d->internalText(); + if ( val.isNull() ) + { + return text(); + } + return val; +} + +void FormFieldText::setInternalText( const QString &text ) +{ + Q_D( FormFieldText ); + d->setInternalText( text ); +} + class Okular::FormFieldChoicePrivate : public Okular::FormFieldPrivate { diff --git a/core/script/event.cpp b/core/script/event.cpp --- a/core/script/event.cpp +++ b/core/script/event.cpp @@ -53,6 +53,8 @@ { case ( FieldCalculate ): return QStringLiteral( "Calculate" ); + case ( FieldFormat ): + return QStringLiteral( "Format" ); case ( UnknownEvent ): default: return QStringLiteral( "Unknown" ); @@ -64,6 +66,7 @@ switch ( d->m_eventType ) { case ( FieldCalculate ): + case ( FieldFormat ): return QStringLiteral( "Field" ); case ( UnknownEvent ): default: @@ -153,7 +156,25 @@ FormFieldText *fft = dynamic_cast< FormFieldText * >(target); if ( fft ) { - ret->setValue( QVariant( fft->text() ) ); + ret->setValue( QVariant( fft->internalText() ) ); + } + return ret; +} + +// static +std::shared_ptr Event::createFormatEvent( FormField *target, + Page *targetPage, + const QString &targetName ) +{ + std::shared_ptr ret( new Event( Event::FieldFormat ) ); + ret->setTarget( target ); + ret->setTargetPage( targetPage ); + ret->setTargetName( targetName ); + + FormFieldText *fft = dynamic_cast< FormFieldText * >(target); + if ( fft ) + { + ret->setValue( QVariant( fft->internalText() ) ); } return ret; } diff --git a/core/script/event_p.h b/core/script/event_p.h --- a/core/script/event_p.h +++ b/core/script/event_p.h @@ -57,7 +57,7 @@ FieldBlur, /// < Not implemented. FieldCalculate, /// < This event is defined in a field re-calculation. FieldFocus, /// < Not implemented. - FieldFormat, /// < Not implemented. + FieldFormat, /// < When a format action is executed FieldKeystroke, /// < Not implemented. FieldMouseDown, /// < Not implemented. FieldMouseEnter, /// < Not implemented. @@ -102,6 +102,8 @@ FormField *source = nullptr, Page *sourcePage = nullptr, const QString &targetName = QString() ); + static std::shared_ptr createFormatEvent( FormField *target, Page *targetPage, + const QString &targetName = QString() ); private: class Private; std::shared_ptr d; diff --git a/core/script/kjs_field.cpp b/core/script/kjs_field.cpp --- a/core/script/kjs_field.cpp +++ b/core/script/kjs_field.cpp @@ -148,7 +148,7 @@ case FormField::FormText: { const FormFieldText *text = static_cast< const FormFieldText * >( field ); - return KJSString( text->text() ); + return KJSString( text->internalText() ); } case FormField::FormChoice: { @@ -192,9 +192,10 @@ { FormFieldText *textField = static_cast< FormFieldText * >( field ); const QString text = value.toString( context ); - if ( text != textField->text() ) + if ( text != textField->internalText() ) { textField->setText( text ); + textField->setInternalText( text ); updateField( field ); } break; diff --git a/ui/formwidgets.h b/ui/formwidgets.h --- a/ui/formwidgets.h +++ b/ui/formwidgets.h @@ -116,6 +116,8 @@ void action( Okular::Action *action ); + void formatAction( const Okular::Action *action, Okular::FormFieldText *ff ); + void refreshFormWidget( Okular::FormField * form ); private Q_SLOTS: diff --git a/ui/formwidgets.cpp b/ui/formwidgets.cpp --- a/ui/formwidgets.cpp +++ b/ui/formwidgets.cpp @@ -501,6 +501,18 @@ return true; } } + else if ( e->type() == QEvent::FocusIn ) + { + const auto fft = static_cast< Okular::FormFieldText * > ( m_ff ); + setText( fft->internalText() ); + } + else if ( e->type() == QEvent::FocusOut ) + { + if ( const Okular::Action *action = m_ff->additionalAction( Okular::FormField::FormatField ) ) + { + emit m_controller->formatAction( action, static_cast< Okular::FormFieldText * > ( m_ff ) ); + } + } return QLineEdit::event( e ); } @@ -633,6 +645,18 @@ return true; } } + else if ( e->type() == QEvent::FocusIn ) + { + const auto fft = static_cast< Okular::FormFieldText * > ( m_ff ); + setText( fft->internalText() ); + } + else if ( e->type() == QEvent::FocusOut ) + { + if ( const Okular::Action *action = m_ff->additionalAction( Okular::FormField::FormatField ) ) + { + emit m_controller->formatAction( action, static_cast< Okular::FormFieldText * > ( m_ff ) ); + } + } return KTextEdit::event( e ); } diff --git a/ui/pageview.cpp b/ui/pageview.cpp --- a/ui/pageview.cpp +++ b/ui/pageview.cpp @@ -264,6 +264,10 @@ q, SLOT( slotFormChanged( int ) ) ); QObject::connect( formsWidgetController, SIGNAL( action( Okular::Action* ) ), q, SLOT( slotAction( Okular::Action* ) ) ); + QObject::connect( formsWidgetController, &FormWidgetsController::formatAction, + q, [this] (const Okular::Action *action, Okular::FormFieldText *fft ) { + document->processFormatAction( action, fft ); + } ); } return formsWidgetController;