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,11 +1151,19 @@ void DocumentPrivate::recalculateForms()
if ( newVal != oldVal )
{
fft->setText( newVal );
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;
}
}
}
}
else
{
qWarning() << "Form that is part of calculate order doesn't have a calculate 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 )
{
if ( !ref )
......
......@@ -668,6 +668,13 @@ class OKULARCORE_EXPORT Document : public QObject
*/
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
*/
......
......@@ -506,6 +506,7 @@ void EditFormTextCommand::undo()
{
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 );
}
......@@ -514,6 +515,7 @@ 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 );
}
......
......@@ -182,6 +182,19 @@ class Okular::FormFieldTextPrivate : public Okular::FormFieldPrivate
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 @@ bool FormFieldText::canBeSpellChecked() const
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
{
......
......@@ -317,6 +317,31 @@ class OKULARCORE_EXPORT FormFieldText : public FormField
*/
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();
......
......@@ -53,6 +53,8 @@ QString Event::name() const
{
case ( FieldCalculate ):
return QStringLiteral( "Calculate" );
case ( FieldFormat ):
return QStringLiteral( "Format" );
case ( UnknownEvent ):
default:
return QStringLiteral( "Unknown" );
......@@ -64,6 +66,7 @@ QString Event::type() const
switch ( d->m_eventType )
{
case ( FieldCalculate ):
case ( FieldFormat ):
return QStringLiteral( "Field" );
case ( UnknownEvent ):
default:
......@@ -153,7 +156,25 @@ std::shared_ptr<Event> Event::createFormCalculateEvent( FormField *target,
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> 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;
}
......@@ -57,7 +57,7 @@ class Event
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 @@ class Event
FormField *source = nullptr,
Page *sourcePage = nullptr,
const QString &targetName = QString() );
static std::shared_ptr<Event> createFormatEvent( FormField *target, Page *targetPage,
const QString &targetName = QString() );
private:
class Private;
std::shared_ptr<Private> d;
......
......@@ -148,7 +148,7 @@ static KJSObject fieldGetValue( KJSContext */*context*/, void *object )
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 @@ static void fieldSetValue( KJSContext *context, void *object, KJSObject value )
{
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;
......
......@@ -501,6 +501,18 @@ bool FormLineEdit::event( QEvent* e )
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 @@ bool TextAreaEdit::event( QEvent* e )
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 );
}
......
......@@ -116,6 +116,8 @@ class FormWidgetsController : public QObject
void action( Okular::Action *action );
void formatAction( const Okular::Action *action, Okular::FormFieldText *ff );
void refreshFormWidget( Okular::FormField * form );
private Q_SLOTS:
......
......@@ -264,6 +264,10 @@ FormWidgetsController* PageViewPrivate::formWidgetsController()
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;
......
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