Merge remote-tracking branch 'origin/Applications/18.04'

remotes/offline-stg/wilder
Albert Astals Cid 8 years ago
commit 37a46fe17f
  1. BIN
      autotests/data/checkbox_ro.pdf
  2. 118
      autotests/parttest.cpp
  3. 4
      core/form.cpp
  4. 7
      core/form.h
  5. 58
      core/script/kjs_field.cpp
  6. 10
      generators/poppler/CMakeLists.txt
  7. 3
      generators/poppler/config-okular-poppler.h.cmake
  8. 27
      generators/poppler/formfields.cpp
  9. 3
      generators/poppler/formfields.h
  10. 95
      ui/formwidgets.cpp
  11. 19
      ui/formwidgets.h
  12. 5
      ui/pageviewutils.cpp

Binary file not shown.

@ -19,6 +19,8 @@
#include "../ui/toc.h"
#include "../ui/pageview.h"
#include "../generators/poppler/config-okular-poppler.h"
#include <KConfigDialog>
#include <KParts/OpenUrlArguments>
@ -105,6 +107,7 @@ class PartTest
void testRClickOnSelectionModeShoulShowFollowTheLinkMenu();
void testClickAnywhereAfterSelectionShouldUnselect();
void testeRectSelectionStartingOnLinks();
void testCheckBoxReadOnly();
private:
void simulateMouseSelection(double startX, double startY, double endX, double endY, QWidget *target);
@ -1281,6 +1284,121 @@ void PartTest::test388288()
QTRY_COMPARE(part.m_pageView->cursor().shape(), Qt::OpenHandCursor);
}
void PartTest::testCheckBoxReadOnly()
{
#ifndef HAVE_POPPLER_0_64
return;
#endif
const QString testFile = QStringLiteral( KDESRCDIR "data/checkbox_ro.pdf" );
Okular::Part part( nullptr, nullptr, QVariantList() );
part.openDocument( testFile );
// The test document uses the activation action of checkboxes
// to update the read only state. For this we need the part so that
// undo / redo activates the activation action.
QVERIFY( part.m_document->isOpened() );
const Okular::Page* page = part.m_document->page( 0 );
QMap<QString, Okular::FormField *> fields;
// Field names in test document are:
// CBMakeRW, CBMakeRO, TargetDefaultRO, TargetDefaultRW
for ( Okular::FormField *ff: page->formFields() )
{
fields.insert( ff->name(), static_cast< Okular::FormField* >( ff ) );
}
// First grab all fields and check that the setup is as expected.
auto cbMakeRW = dynamic_cast< Okular::FormFieldButton* > ( fields[QStringLiteral( "CBMakeRW" )] );
auto cbMakeRO = dynamic_cast< Okular::FormFieldButton* > ( fields[QStringLiteral( "CBMakeRO" )] );
auto targetDefaultRW = dynamic_cast< Okular::FormFieldText* > ( fields[QStringLiteral( "TargetDefaultRw" )] );
auto targetDefaultRO = dynamic_cast< Okular::FormFieldText* > ( fields[QStringLiteral( "TargetDefaultRo" )] );
QVERIFY( cbMakeRW );
QVERIFY( cbMakeRO );
QVERIFY( targetDefaultRW );
QVERIFY( targetDefaultRO );
QVERIFY( !cbMakeRW->state() );
QVERIFY( !cbMakeRO->state() );
QVERIFY( !targetDefaultRW->isReadOnly() );
QVERIFY( targetDefaultRO->isReadOnly() );
QList< Okular::FormFieldButton* > btns;
btns << cbMakeRW << cbMakeRO;
// Now check both boxes
QList< bool > btnStates;
btnStates << true << true;
part.m_document->editFormButtons( 0, btns, btnStates );
// Read only should be inverted
QVERIFY( targetDefaultRW->isReadOnly() );
QVERIFY( !targetDefaultRO->isReadOnly() );
// Test that undo / redo works
QVERIFY( part.m_document->canUndo() );
part.m_document->undo();
QVERIFY( !targetDefaultRW->isReadOnly() );
QVERIFY( targetDefaultRO->isReadOnly() );
part.m_document->redo();
QVERIFY( targetDefaultRW->isReadOnly() );
QVERIFY( !targetDefaultRO->isReadOnly() );
btnStates.clear();
btnStates << false << true;
part.m_document->editFormButtons( 0, btns, btnStates );
QVERIFY( targetDefaultRW->isReadOnly() );
QVERIFY( targetDefaultRO->isReadOnly() );
// Now set both to checked again and confirm that
// save / load works.
btnStates.clear();
btnStates << true << true;
part.m_document->editFormButtons( 0, btns, btnStates );
QTemporaryFile saveFile( QString( "%1/okrXXXXXX.pdf" ).arg( QDir::tempPath() ) );
QVERIFY( saveFile.open() );
saveFile.close();
// Save
QVERIFY( part.saveAs( QUrl::fromLocalFile( saveFile.fileName() ), Part::NoSaveAsFlags ) );
part.closeUrl();
// Load
part.openDocument( saveFile.fileName() );
QVERIFY( part.m_document->isOpened() );
page = part.m_document->page( 0 );
fields.clear();
for ( Okular::FormField *ff: page->formFields() )
{
fields.insert( ff->name(), static_cast< Okular::FormField* >( ff ) );
}
cbMakeRW = dynamic_cast< Okular::FormFieldButton* > ( fields[QStringLiteral( "CBMakeRW" )] );
cbMakeRO = dynamic_cast< Okular::FormFieldButton* > ( fields[QStringLiteral( "CBMakeRO" )] );
targetDefaultRW = dynamic_cast< Okular::FormFieldText* > ( fields[QStringLiteral( "TargetDefaultRw" )] );
targetDefaultRO = dynamic_cast< Okular::FormFieldText* > ( fields[QStringLiteral( "TargetDefaultRo" )] );
QVERIFY( cbMakeRW->state() );
QVERIFY( cbMakeRO->state() );
QVERIFY( targetDefaultRW->isReadOnly() );
QVERIFY( !targetDefaultRO->isReadOnly() );
}
}
int main(int argc, char *argv[])

@ -55,6 +55,10 @@ bool FormField::isReadOnly() const
return false;
}
void FormField::setReadOnly( bool )
{
}
bool FormField::isVisible() const
{
return true;

@ -86,6 +86,13 @@ class OKULARCORE_EXPORT FormField
*/
virtual bool isReadOnly() const;
/**
* Whether the field is read-only.
*
* @since 1.4
*/
virtual void setReadOnly( bool value );
/**
* Whether this form field is visible.
*/

@ -31,6 +31,23 @@ static KJSPrototype *g_fieldProto;
typedef QHash< FormField *, Page * > FormCache;
Q_GLOBAL_STATIC( FormCache, g_fieldCache )
// Helper for modified fields
static void updateField( FormField *field )
{
Page *page = g_fieldCache->value( field );
if (page)
{
Document *doc = PagePrivate::get( page )->m_doc->m_parent;
QMetaObject::invokeMethod( doc, "refreshPixmaps", Qt::QueuedConnection, Q_ARG( int, page->number() ) );
emit doc->refreshFormWidget( field );
}
else
{
qWarning() << "Could not get page of field" << field;
}
}
// Field.doc
static KJSObject fieldGetDoc( KJSContext *context, void * )
{
@ -54,16 +71,11 @@ static KJSObject fieldGetReadOnly( KJSContext *, void *object )
// Field.readonly (setter)
static void fieldSetReadOnly( KJSContext *context, void *object, KJSObject value )
{
#if 0
FormField *field = reinterpret_cast< FormField * >( object );
bool b = value.toBoolean( context );
field->setReadOnly( b );
#else
Q_UNUSED( context );
Q_UNUSED( object );
Q_UNUSED( value );
qCDebug(OkularCoreDebug) << "Not implemented: setting readonly property";
#endif
updateField( field );
}
static QString fieldGetTypeHelper( const FormField *field )
@ -122,8 +134,11 @@ static KJSObject fieldGetValue( KJSContext */*context*/, void *object )
case FormField::FormButton:
{
const FormFieldButton *button = static_cast< const FormFieldButton * >( field );
Q_UNUSED( button ); // ###
break;
if ( button->state() )
{
return KJSString( QStringLiteral( "Yes" ) );
}
return KJSString( QStringLiteral( "Off" ) );
}
case FormField::FormText:
{
@ -155,7 +170,17 @@ static void fieldSetValue( KJSContext *context, void *object, KJSObject value )
case FormField::FormButton:
{
FormFieldButton *button = static_cast< FormFieldButton * >( field );
Q_UNUSED( button ); // ###
const QString text = value.toString( context );
if ( text == QStringLiteral( "Yes" ) )
{
button->setState( true );
updateField( field );
}
else if ( text == QStringLiteral( "Off" ) )
{
button->setState( false );
updateField( field );
}
break;
}
case FormField::FormText:
@ -165,18 +190,7 @@ static void fieldSetValue( KJSContext *context, void *object, KJSObject value )
if ( text != textField->text() )
{
textField->setText( text );
Page *page = g_fieldCache->value( field );
if (page)
{
Document *doc = PagePrivate::get( page )->m_doc->m_parent;
QMetaObject::invokeMethod( doc, "refreshPixmaps", Qt::QueuedConnection, Q_ARG( int, page->number() ) );
emit doc->refreshFormWidget( field );
}
else
{
qWarning() << "Could not get page of field" << field;
}
updateField( field );
}
break;
}

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

@ -24,3 +24,6 @@
/* Defined if we have the 0.63 version of the Poppler library */
#cmakedefine HAVE_POPPLER_0_63 1
/* Defined if we have the 0.64 version of the Poppler library */
#cmakedefine HAVE_POPPLER_0_64 1

@ -67,6 +67,15 @@ bool PopplerFormFieldButton::isReadOnly() const
return m_field->isReadOnly();
}
void PopplerFormFieldButton::setReadOnly( bool value )
{
#ifdef HAVE_POPPLER_0_64
m_field->setReadOnly( value );
#else
Q_UNUSED( value );
#endif
}
bool PopplerFormFieldButton::isVisible() const
{
return m_field->isVisible();
@ -145,6 +154,15 @@ bool PopplerFormFieldText::isReadOnly() const
return m_field->isReadOnly();
}
void PopplerFormFieldText::setReadOnly( bool value )
{
#ifdef HAVE_POPPLER_0_64
m_field->setReadOnly( value );
#else
Q_UNUSED( value );
#endif
}
bool PopplerFormFieldText::isVisible() const
{
return m_field->isVisible();
@ -238,6 +256,15 @@ bool PopplerFormFieldChoice::isReadOnly() const
return m_field->isReadOnly();
}
void PopplerFormFieldChoice::setReadOnly( bool value )
{
#ifdef HAVE_POPPLER_0_64
m_field->setReadOnly( value );
#else
Q_UNUSED( value );
#endif
}
bool PopplerFormFieldChoice::isVisible() const
{
return m_field->isVisible();

@ -25,6 +25,7 @@ class PopplerFormFieldButton : public Okular::FormFieldButton
QString name() const override;
QString uiName() const override;
bool isReadOnly() const override;
void setReadOnly( bool value ) override;
bool isVisible() const override;
// inherited from Okular::FormFieldButton
@ -53,6 +54,7 @@ class PopplerFormFieldText : public Okular::FormFieldText
QString name() const override;
QString uiName() const override;
bool isReadOnly() const override;
void setReadOnly( bool value ) override;
bool isVisible() const override;
// inherited from Okular::FormFieldText
@ -84,6 +86,7 @@ class PopplerFormFieldChoice : public Okular::FormFieldChoice
QString name() const override;
QString uiName() const override;
bool isReadOnly() const override;
void setReadOnly( bool value ) override;
bool isVisible() const override;
// inherited from Okular::FormFieldChoice

@ -154,7 +154,8 @@ bool FormWidgetsController::canRedo()
void FormWidgetsController::slotButtonClicked( QAbstractButton *button )
{
int pageNumber = -1;
if ( CheckBoxEdit *check = qobject_cast< CheckBoxEdit * >( button ) )
CheckBoxEdit *check = qobject_cast< CheckBoxEdit * >( button );
if ( check )
{
// Checkboxes need to be uncheckable so if clicking a checked one
// disable the exclusive status temporarily and uncheck it
@ -186,6 +187,13 @@ void FormWidgetsController::slotButtonClicked( QAbstractButton *button )
}
if (checked != prevChecked)
emit formButtonsChangedByWidget( pageNumber, formButtons, checked );
if ( check )
{
// The formButtonsChangedByWidget signal changes the value of the underlying
// Okular::FormField of the checkbox. We need to execute the activiation
// action after this.
check->doActivateAction();
}
}
void FormWidgetsController::slotFormButtonsChangedByUndoRedo( int pageNumber, const QList< Okular::FormFieldButton* > & formButtons)
@ -194,6 +202,11 @@ void FormWidgetsController::slotFormButtonsChangedByUndoRedo( int pageNumber, co
{
int id = formButton->id();
QAbstractButton* button = m_buttons[id];
CheckBoxEdit *check = qobject_cast< CheckBoxEdit * >( button );
if ( check )
{
emit refreshFormWidget( check->formField() );
}
// temporarily disable exclusiveness of the button group
// since it breaks doing/redoing steps into which all the checkboxes
// are unchecked
@ -211,9 +224,6 @@ FormWidgetIface * FormWidgetFactory::createWidget( Okular::FormField * ff, QWidg
{
FormWidgetIface * widget = nullptr;
if (ff->isReadOnly())
return nullptr;
switch ( ff->type() )
{
case Okular::FormField::FormButton:
@ -267,15 +277,17 @@ FormWidgetIface * FormWidgetFactory::createWidget( Okular::FormField * ff, QWidg
}
default: ;
}
if ( ff->isReadOnly() )
widget->setVisibility( false );
return widget;
}
FormWidgetIface::FormWidgetIface( QWidget * w, Okular::FormField * ff, bool canBeEnabled )
: m_controller( nullptr ), m_ff( ff ), m_widget( w ), m_pageItem( nullptr ),
m_canBeEnabled( canBeEnabled )
FormWidgetIface::FormWidgetIface( QWidget * w, Okular::FormField * ff )
: m_controller( nullptr ), m_ff( ff ), m_widget( w ), m_pageItem( nullptr )
{
m_widget->setEnabled( m_canBeEnabled );
}
FormWidgetIface::~FormWidgetIface()
@ -308,7 +320,7 @@ bool FormWidgetIface::setVisibility( bool visible )
void FormWidgetIface::setCanBeFilled( bool fill )
{
m_widget->setEnabled( fill && m_canBeEnabled );
m_widget->setEnabled( fill );
}
void FormWidgetIface::setPageItem( PageViewItem *pageItem )
@ -334,11 +346,27 @@ PageViewItem* FormWidgetIface::pageItem() const
void FormWidgetIface::setFormWidgetsController( FormWidgetsController *controller )
{
m_controller = controller;
QObject *obj = dynamic_cast< QObject * > ( this );
QObject::connect( m_controller, &FormWidgetsController::refreshFormWidget, obj,
[this] ( Okular::FormField *form ) {
slotRefresh ( form );
});
}
void FormWidgetIface::slotRefresh( Okular::FormField * form )
{
if ( m_ff != form )
{
return;
}
setVisibility( form->isVisible() && !form->isReadOnly() );
m_widget->setEnabled( !form->isReadOnly() );
}
PushButtonEdit::PushButtonEdit( Okular::FormFieldButton * button, QWidget * parent )
: QPushButton( parent ), FormWidgetIface( this, button, !button->isReadOnly() )
: QPushButton( parent ), FormWidgetIface( this, button )
{
setText( button->caption() );
setVisible( button->isVisible() );
@ -355,7 +383,7 @@ void PushButtonEdit::slotClicked()
CheckBoxEdit::CheckBoxEdit( Okular::FormFieldButton * button, QWidget * parent )
: QCheckBox( parent ), FormWidgetIface( this, button, !button->isReadOnly() )
: QCheckBox( parent ), FormWidgetIface( this, button )
{
setText( button->caption() );
@ -369,19 +397,36 @@ void CheckBoxEdit::setFormWidgetsController( FormWidgetsController *controller )
FormWidgetIface::setFormWidgetsController( controller );
m_controller->registerRadioButton( this, form );
setChecked( form->state() );
connect( this, &QCheckBox::stateChanged, this, &CheckBoxEdit::slotStateChanged );
}
void CheckBoxEdit::slotStateChanged( int state )
void CheckBoxEdit::doActivateAction()
{
Okular::FormFieldButton *form = static_cast<Okular::FormFieldButton *>(m_ff);
if ( state == Qt::Checked && form->activationAction() )
if ( form->activationAction() )
m_controller->signalAction( form->activationAction() );
}
void CheckBoxEdit::slotRefresh( Okular::FormField * form )
{
if ( form != m_ff )
{
return;
}
FormWidgetIface::slotRefresh( form );
Okular::FormFieldButton *button = static_cast<Okular::FormFieldButton *>(m_ff);
bool oldState = isChecked();
bool newState = button->state();
if ( oldState != newState )
{
setChecked( button->state() );
doActivateAction();
}
}
RadioButtonEdit::RadioButtonEdit( Okular::FormFieldButton * button, QWidget * parent )
: QRadioButton( parent ), FormWidgetIface( this, button, !button->isReadOnly() )
: QRadioButton( parent ), FormWidgetIface( this, button )
{
setText( button->caption() );
@ -399,7 +444,7 @@ void RadioButtonEdit::setFormWidgetsController( FormWidgetsController *controlle
FormLineEdit::FormLineEdit( Okular::FormFieldText * text, QWidget * parent )
: QLineEdit( parent ), FormWidgetIface( this, text, true )
: QLineEdit( parent ), FormWidgetIface( this, text )
{
int maxlen = text->maximumLength();
if ( maxlen >= 0 )
@ -423,8 +468,6 @@ void FormLineEdit::setFormWidgetsController(FormWidgetsController* controller)
FormWidgetIface::setFormWidgetsController(controller);
connect( m_controller, &FormWidgetsController::formTextChangedByUndoRedo,
this, &FormLineEdit::slotHandleTextChangedByUndoRedo );
connect( m_controller, &FormWidgetsController::refreshFormWidget,
this, &FormLineEdit::slotRefresh );
}
bool FormLineEdit::event( QEvent* e )
@ -527,13 +570,14 @@ void FormLineEdit::slotRefresh( Okular::FormField *form )
{
return;
}
Okular::FormFieldText *text = static_cast<Okular::FormFieldText *> ( form );
FormWidgetIface::slotRefresh( form );
Okular::FormFieldText *text = static_cast<Okular::FormFieldText *> ( form );
setText( text->text() );
}
TextAreaEdit::TextAreaEdit( Okular::FormFieldText * text, QWidget * parent )
: KTextEdit( parent ), FormWidgetIface( this, text, true )
: KTextEdit( parent ), FormWidgetIface( this, text )
{
setAcceptRichText( text->isRichText() );
setCheckSpellingEnabled( text->canBeSpellChecked() );
@ -599,8 +643,6 @@ void TextAreaEdit::setFormWidgetsController( FormWidgetsController* controller )
FormWidgetIface::setFormWidgetsController( controller );
connect( m_controller, &FormWidgetsController::formTextChangedByUndoRedo,
this, &TextAreaEdit::slotHandleTextChangedByUndoRedo );
connect( m_controller, &FormWidgetsController::refreshFormWidget,
this, &TextAreaEdit::slotRefresh );
}
void TextAreaEdit::slotHandleTextChangedByUndoRedo( int pageNumber,
@ -648,13 +690,14 @@ void TextAreaEdit::slotRefresh( Okular::FormField *form )
{
return;
}
Okular::FormFieldText *text = static_cast<Okular::FormFieldText *> ( form );
FormWidgetIface::slotRefresh( form );
Okular::FormFieldText *text = static_cast<Okular::FormFieldText *> ( form );
setPlainText( text->text() );
}
FileEdit::FileEdit( Okular::FormFieldText * text, QWidget * parent )
: KUrlRequester( parent ), FormWidgetIface( this, text, !text->isReadOnly() )
: KUrlRequester( parent ), FormWidgetIface( this, text )
{
setMode( KFile::File | KFile::ExistingOnly | KFile::LocalOnly );
setFilter( i18n( "*|All Files" ) );
@ -780,7 +823,7 @@ void FileEdit::slotHandleFileChangedByUndoRedo( int pageNumber,
}
ListEdit::ListEdit( Okular::FormFieldChoice * choice, QWidget * parent )
: QListWidget( parent ), FormWidgetIface( this, choice, !choice->isReadOnly() )
: QListWidget( parent ), FormWidgetIface( this, choice )
{
addItems( choice->choices() );
setSelectionMode( choice->multiSelect() ? QAbstractItemView::ExtendedSelection : QAbstractItemView::SingleSelection );
@ -850,7 +893,7 @@ void ListEdit::slotHandleFormListChangedByUndoRedo( int pageNumber,
}
ComboEdit::ComboEdit( Okular::FormFieldChoice * choice, QWidget * parent )
: QComboBox( parent ), FormWidgetIface( this, choice, !choice->isReadOnly() )
: QComboBox( parent ), FormWidgetIface( this, choice )
{
addItems( choice->choices() );
setEditable( true );

@ -143,7 +143,7 @@ class FormWidgetFactory
class FormWidgetIface
{
public:
FormWidgetIface( QWidget * w, Okular::FormField * ff, bool canBeEnabled );
FormWidgetIface( QWidget * w, Okular::FormField * ff );
virtual ~FormWidgetIface();
Okular::NormalizedRect rect() const;
@ -160,13 +160,14 @@ class FormWidgetIface
virtual void setFormWidgetsController( FormWidgetsController *controller );
protected:
virtual void slotRefresh( Okular::FormField *form );
FormWidgetsController * m_controller;
Okular::FormField * m_ff;
private:
QWidget * m_widget;
PageViewItem * m_pageItem;
bool m_canBeEnabled;
};
@ -191,8 +192,10 @@ class CheckBoxEdit : public QCheckBox, public FormWidgetIface
// reimplemented from FormWidgetIface
void setFormWidgetsController( FormWidgetsController *controller ) override;
private Q_SLOTS:
void slotStateChanged( int state );
void doActivateAction();
protected:
void slotRefresh( Okular::FormField *form ) override;
};
class RadioButtonEdit : public QRadioButton, public FormWidgetIface
@ -225,7 +228,9 @@ class FormLineEdit : public QLineEdit, public FormWidgetIface
int anchorPos );
private Q_SLOTS:
void slotChanged();
void slotRefresh(Okular::FormField* form);
protected:
void slotRefresh( Okular::FormField* form ) override;
private:
int m_prevCursorPos;
@ -252,7 +257,9 @@ class TextAreaEdit : public KTextEdit, public FormWidgetIface
private Q_SLOTS:
void slotChanged();
void slotRefresh(Okular::FormField* form);
protected:
void slotRefresh( Okular::FormField* form ) override;
private:
int m_prevCursorPos;

@ -204,7 +204,8 @@ bool PageViewItem::setFormWidgetsVisible( bool visible )
QSet<FormWidgetIface*>::iterator it = m_formWidgets.begin(), itEnd = m_formWidgets.end();
for ( ; it != itEnd; ++it )
{
bool hadfocus = (*it)->setVisibility( visible && (*it)->formField()->isVisible() );
bool hadfocus = (*it)->setVisibility( visible && (*it)->formField()->isVisible() &&
!(*it)->formField()->isReadOnly() );
somehadfocus = somehadfocus || hadfocus;
}
return somehadfocus;
@ -214,7 +215,7 @@ void PageViewItem::reloadFormWidgetsState()
{
foreach(FormWidgetIface *fwi, m_formWidgets)
{
fwi->setVisibility( fwi->formField()->isVisible() );
fwi->setVisibility( fwi->formField()->isVisible() && !fwi->formField()->isReadOnly() );
}
}

Loading…
Cancel
Save