Commit da487f3a authored by Andre Heinecke's avatar Andre Heinecke Committed by Albert Astals Cid

Add support for form text formatting

Summary:
With formatting there is an internal value, which represents
the true value of a field additionaly to the normal,
visible, text.

For fields which have formatting rules these might differ
and for calculations the internal value is used. The behavior
to format on focus in / focus out events is similar to
that of Acrobat reader.

Test Plan: Needs unit test

Reviewers: aacid

Subscribers: okular-devel

Tags: #okular

Maniphest Tasks: T8886

Differential Revision: https://phabricator.kde.org/D13171
parent 96b9fa1e
...@@ -1151,8 +1151,16 @@ void DocumentPrivate::recalculateForms() ...@@ -1151,8 +1151,16 @@ void DocumentPrivate::recalculateForms()
if ( newVal != oldVal ) if ( newVal != oldVal )
{ {
fft->setText( newVal ); fft->setText( newVal );
emit m_parent->refreshFormWidget( fft ); if ( const Okular::Action *action = fft->additionalAction( Okular::FormField::FormatField ) )
pageNeedsRefresh = true; {
// 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::processAction( const Action * action ) ...@@ -4292,6 +4300,68 @@ void Document::processAction( const Action * action )
} }
} }
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 ) void Document::processSourceReference( const SourceReference * ref )
{ {
if ( !ref ) if ( !ref )
......
...@@ -668,6 +668,13 @@ class OKULARCORE_EXPORT Document : public QObject ...@@ -668,6 +668,13 @@ class OKULARCORE_EXPORT Document : public QObject
*/ */
void processAction( const Action *action ); 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 * Returns a list of the bookmarked.pages
*/ */
......
...@@ -506,6 +506,7 @@ void EditFormTextCommand::undo() ...@@ -506,6 +506,7 @@ void EditFormTextCommand::undo()
{ {
moveViewportIfBoundingRectNotFullyVisible( m_form->rect(), m_docPriv, m_pageNumber ); moveViewportIfBoundingRectNotFullyVisible( m_form->rect(), m_docPriv, m_pageNumber );
m_form->setText( m_prevContents ); 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 ); emit m_docPriv->m_parent->formTextChangedByUndoRedo( m_pageNumber, m_form, m_prevContents, m_prevCursorPos, m_prevAnchorPos );
m_docPriv->notifyFormChanges( m_pageNumber ); m_docPriv->notifyFormChanges( m_pageNumber );
} }
...@@ -514,6 +515,7 @@ void EditFormTextCommand::redo() ...@@ -514,6 +515,7 @@ void EditFormTextCommand::redo()
{ {
moveViewportIfBoundingRectNotFullyVisible( m_form->rect(), m_docPriv, m_pageNumber ); moveViewportIfBoundingRectNotFullyVisible( m_form->rect(), m_docPriv, m_pageNumber );
m_form->setText( m_newContents ); 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 ); emit m_docPriv->m_parent->formTextChangedByUndoRedo( m_pageNumber, m_form, m_newContents, m_newCursorPos, m_newCursorPos );
m_docPriv->notifyFormChanges( m_pageNumber ); m_docPriv->notifyFormChanges( m_pageNumber );
} }
......
...@@ -182,6 +182,19 @@ class Okular::FormFieldTextPrivate : public Okular::FormFieldPrivate ...@@ -182,6 +182,19 @@ class Okular::FormFieldTextPrivate : public Okular::FormFieldPrivate
Q_Q( const FormFieldText ); Q_Q( const FormFieldText );
return q->text(); 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 @@ bool FormFieldText::canBeSpellChecked() const ...@@ -223,6 +236,23 @@ bool FormFieldText::canBeSpellChecked() const
return false; 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 class Okular::FormFieldChoicePrivate : public Okular::FormFieldPrivate
{ {
......
...@@ -317,6 +317,31 @@ class OKULARCORE_EXPORT FormFieldText : public FormField ...@@ -317,6 +317,31 @@ class OKULARCORE_EXPORT FormFieldText : public FormField
*/ */
virtual bool canBeSpellChecked() const; 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: protected:
FormFieldText(); FormFieldText();
......
...@@ -53,6 +53,8 @@ QString Event::name() const ...@@ -53,6 +53,8 @@ QString Event::name() const
{ {
case ( FieldCalculate ): case ( FieldCalculate ):
return QStringLiteral( "Calculate" ); return QStringLiteral( "Calculate" );
case ( FieldFormat ):
return QStringLiteral( "Format" );
case ( UnknownEvent ): case ( UnknownEvent ):
default: default:
return QStringLiteral( "Unknown" ); return QStringLiteral( "Unknown" );
...@@ -64,6 +66,7 @@ QString Event::type() const ...@@ -64,6 +66,7 @@ QString Event::type() const
switch ( d->m_eventType ) switch ( d->m_eventType )
{ {
case ( FieldCalculate ): case ( FieldCalculate ):
case ( FieldFormat ):
return QStringLiteral( "Field" ); return QStringLiteral( "Field" );
case ( UnknownEvent ): case ( UnknownEvent ):
default: default:
...@@ -153,7 +156,25 @@ std::shared_ptr<Event> Event::createFormCalculateEvent( FormField *target, ...@@ -153,7 +156,25 @@ std::shared_ptr<Event> Event::createFormCalculateEvent( FormField *target,
FormFieldText *fft = dynamic_cast< FormFieldText * >(target); FormFieldText *fft = dynamic_cast< FormFieldText * >(target);
if ( fft ) if ( fft )
{ {
ret->setValue( QVariant( fft->text() ) ); ret->setValue( QVariant( fft->internalText() ) );
}
return ret;
}
// static
std::shared_ptr<Event> Event::createFormatEvent( FormField *target,
Page *targetPage,
const QString &targetName )
{
std::shared_ptr<Event> 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; return ret;
} }
...@@ -57,7 +57,7 @@ class Event ...@@ -57,7 +57,7 @@ class Event
FieldBlur, /// < Not implemented. FieldBlur, /// < Not implemented.
FieldCalculate, /// < This event is defined in a field re-calculation. FieldCalculate, /// < This event is defined in a field re-calculation.
FieldFocus, /// < Not implemented. FieldFocus, /// < Not implemented.
FieldFormat, /// < Not implemented. FieldFormat, /// < When a format action is executed
FieldKeystroke, /// < Not implemented. FieldKeystroke, /// < Not implemented.
FieldMouseDown, /// < Not implemented. FieldMouseDown, /// < Not implemented.
FieldMouseEnter, /// < Not implemented. FieldMouseEnter, /// < Not implemented.
...@@ -102,6 +102,8 @@ class Event ...@@ -102,6 +102,8 @@ class Event
FormField *source = nullptr, FormField *source = nullptr,
Page *sourcePage = nullptr, Page *sourcePage = nullptr,
const QString &targetName = QString() ); const QString &targetName = QString() );
static std::shared_ptr<Event> createFormatEvent( FormField *target, Page *targetPage,
const QString &targetName = QString() );
private: private:
class Private; class Private;
std::shared_ptr<Private> d; std::shared_ptr<Private> d;
......
...@@ -148,7 +148,7 @@ static KJSObject fieldGetValue( KJSContext */*context*/, void *object ) ...@@ -148,7 +148,7 @@ static KJSObject fieldGetValue( KJSContext */*context*/, void *object )
case FormField::FormText: case FormField::FormText:
{ {
const FormFieldText *text = static_cast< const FormFieldText * >( field ); const FormFieldText *text = static_cast< const FormFieldText * >( field );
return KJSString( text->text() ); return KJSString( text->internalText() );
} }
case FormField::FormChoice: case FormField::FormChoice:
{ {
...@@ -192,9 +192,10 @@ static void fieldSetValue( KJSContext *context, void *object, KJSObject value ) ...@@ -192,9 +192,10 @@ static void fieldSetValue( KJSContext *context, void *object, KJSObject value )
{ {
FormFieldText *textField = static_cast< FormFieldText * >( field ); FormFieldText *textField = static_cast< FormFieldText * >( field );
const QString text = value.toString( context ); const QString text = value.toString( context );
if ( text != textField->text() ) if ( text != textField->internalText() )
{ {
textField->setText( text ); textField->setText( text );
textField->setInternalText( text );
updateField( field ); updateField( field );
} }
break; break;
......
...@@ -501,6 +501,18 @@ bool FormLineEdit::event( QEvent* e ) ...@@ -501,6 +501,18 @@ bool FormLineEdit::event( QEvent* e )
return true; 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 ); return QLineEdit::event( e );
} }
...@@ -633,6 +645,18 @@ bool TextAreaEdit::event( QEvent* e ) ...@@ -633,6 +645,18 @@ bool TextAreaEdit::event( QEvent* e )
return true; 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 ); return KTextEdit::event( e );
} }
......
...@@ -116,6 +116,8 @@ class FormWidgetsController : public QObject ...@@ -116,6 +116,8 @@ class FormWidgetsController : public QObject
void action( Okular::Action *action ); void action( Okular::Action *action );
void formatAction( const Okular::Action *action, Okular::FormFieldText *ff );
void refreshFormWidget( Okular::FormField * form ); void refreshFormWidget( Okular::FormField * form );
private Q_SLOTS: private Q_SLOTS:
......
...@@ -264,6 +264,10 @@ FormWidgetsController* PageViewPrivate::formWidgetsController() ...@@ -264,6 +264,10 @@ FormWidgetsController* PageViewPrivate::formWidgetsController()
q, SLOT( slotFormChanged( int ) ) ); q, SLOT( slotFormChanged( int ) ) );
QObject::connect( formsWidgetController, SIGNAL( action( Okular::Action* ) ), QObject::connect( formsWidgetController, SIGNAL( action( Okular::Action* ) ),
q, SLOT( slotAction( 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; return formsWidgetController;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment