diff --git a/autotests/CMakeLists.txt b/autotests/CMakeLists.txt --- a/autotests/CMakeLists.txt +++ b/autotests/CMakeLists.txt @@ -62,6 +62,11 @@ LINK_LIBRARIES Qt5::Widgets Qt5::Test okularcore ) +ecm_add_test(resetformstest.cpp + TEST_NAME "resetformstest" + LINK_LIBRARIES Qt5::Widgets Qt5::Test okularcore +) + if(NOT WIN32) ecm_add_test(mainshelltest.cpp ../shell/okular_main.cpp ../shell/shellutils.cpp ../shell/shell.cpp TEST_NAME "mainshelltest" diff --git a/autotests/resetformstest.cpp b/autotests/resetformstest.cpp new file mode 100644 --- /dev/null +++ b/autotests/resetformstest.cpp @@ -0,0 +1,288 @@ +#include + +#include +#include +#include "../settings_core.h" +#include "core/document.h" +#include +#include + + +class ResetFormsTest : public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + + void testResetRadioButtonForm(); + void testResetCheckBoxForm(); + void testResetTextLineForm(); + void testResetTextAreaForm(); + void testResetFileEditForm(); + void testResetComboEditForm(); + void testResetListSingleEdit(); + void testResetListMultiEdit(); + + // helper methods + void triggerResetAction(); + void verifyRadioButtonStates(bool state1, bool state2, bool state3); + void setRadioButtonStates(bool state1, bool state2, bool state3); + void verifyTextForm( Okular::FormFieldText *form ); + +private: + Okular::Document *m_document; + QList m_radioButtonForms; + QList m_checkBoxForms; + Okular::FormFieldText* m_textLineForm; + Okular::FormFieldText* m_textAreaForm; + Okular::FormFieldText* m_fileEditForm; + Okular::FormFieldChoice* m_comboEdit; + Okular::FormFieldChoice* m_listSingleEdit; + Okular::FormFieldChoice* m_listMultiEdit; +}; + +void ResetFormsTest::initTestCase() +{ + Okular::SettingsCore::instance( QStringLiteral("editformstest") ); + m_document = new Okular::Document( nullptr ); +} + +void ResetFormsTest::cleanupTestCase() +{ + delete m_document; +} + +void ResetFormsTest::cleanup() +{ + m_document->closeDocument(); +} + +void ResetFormsTest::init() +{ + const QString testFile = QStringLiteral(KDESRCDIR "data/formSamples.pdf"); + QMimeDatabase db; + const QMimeType mime = db.mimeTypeForFile( testFile ); + QCOMPARE( m_document->openDocument(testFile, QUrl(), mime), Okular::Document::OpenSuccess ); + + const Okular::Page* page = m_document->page( 0 ); + QLinkedList pageFields = page->formFields(); + + // Clear lists + m_checkBoxForms.clear(); + m_radioButtonForms.clear(); + + // Collect forms of the various types + QLinkedList< Okular::FormField * >::const_iterator ffIt = pageFields.constBegin(), ffEnd = pageFields.constEnd(); + for ( ; ffIt != ffEnd; ++ffIt ) + { + Okular::FormField * ff = *ffIt; + ff->type(); + + switch ( ff->type() ) + { + case Okular::FormField::FormButton: + { + Okular::FormFieldButton * ffb = static_cast< Okular::FormFieldButton * >( ff ); + switch ( ffb->buttonType() ) + { + case Okular::FormFieldButton::Push: + break; + case Okular::FormFieldButton::CheckBox: + m_checkBoxForms.append(ffb); + break; + case Okular::FormFieldButton::Radio: + m_radioButtonForms.append(ffb); + break; + default: ; + } + break; + } + case Okular::FormField::FormText: + { + Okular::FormFieldText * fft = static_cast< Okular::FormFieldText * >( ff ); + switch ( fft->textType() ) + { + case Okular::FormFieldText::Multiline: + m_textAreaForm = fft; + break; + case Okular::FormFieldText::Normal: + m_textLineForm = fft; + break; + case Okular::FormFieldText::FileSelect: + m_fileEditForm = fft; + break; + } + break; + } + case Okular::FormField::FormChoice: + { + Okular::FormFieldChoice * ffc = static_cast< Okular::FormFieldChoice * >( ff ); + switch ( ffc->choiceType() ) + { + case Okular::FormFieldChoice::ListBox: + if ( ffc->multiSelect() ) + { + m_listMultiEdit = ffc; + } + else + { + m_listSingleEdit = ffc; + } + break; + case Okular::FormFieldChoice::ComboBox: + m_comboEdit = ffc; + break; + } + break; + } + default: ; + } + } +} + + +void ResetFormsTest::testResetRadioButtonForm() +{ + // select second radio button + setRadioButtonStates(false, true, false); + // trigger reset action, first radion button should be selected + triggerResetAction(); + QVERIFY( m_document->canUndo() ); + verifyRadioButtonStates(true, false, false); + + m_document->undo(); + verifyRadioButtonStates(false, true, false); + + m_document->redo(); + verifyRadioButtonStates(true, false, false); +} + +void ResetFormsTest::testResetCheckBoxForm() +{ + // set all check boxes to true + m_document->editFormButtons( 0, m_checkBoxForms, QList() << true << true << true); + triggerResetAction(); + QVERIFY( m_document->canUndo() ); + + QVERIFY ( m_checkBoxForms[0]->state() == false ); + QVERIFY ( m_checkBoxForms[1]->state() == false ); + QVERIFY ( m_checkBoxForms[2]->state() == false ); + + m_document->undo(); + QVERIFY ( m_checkBoxForms[0]->state() == true ); + QVERIFY ( m_checkBoxForms[1]->state() == true ); + QVERIFY ( m_checkBoxForms[2]->state() == true ); + + m_document->redo(); + QVERIFY ( m_checkBoxForms[0]->state() == false ); + QVERIFY ( m_checkBoxForms[1]->state() == false ); + QVERIFY ( m_checkBoxForms[2]->state() == false ); +} + +void ResetFormsTest::testResetTextLineForm() +{ + verifyTextForm( m_textLineForm ); +} + +void ResetFormsTest::testResetTextAreaForm() +{ + verifyTextForm( m_textAreaForm ); +} + +void ResetFormsTest::testResetFileEditForm() +{ + verifyTextForm( m_fileEditForm ); +} + +void ResetFormsTest::testResetComboEditForm() +{ + // Select first choice + m_document->editFormCombo( 0, m_comboEdit, QStringLiteral("combo1"), 0, 0, 0); + QCOMPARE( m_comboEdit->currentChoices().length(), 1 ); + triggerResetAction(); + QVERIFY( m_document->canUndo() ); + QCOMPARE( m_comboEdit->currentChoices().length(), 0 ); + QCOMPARE( m_comboEdit->editChoice(), QStringLiteral( "" ) ); + + m_document->undo(); + QCOMPARE( m_comboEdit->currentChoices().length(), 1 ); + + m_document->redo(); + QCOMPARE( m_comboEdit->currentChoices().length(), 0 ); +} + +void ResetFormsTest::testResetListSingleEdit() +{ + // Select first item + m_document->editFormList( 0, m_listSingleEdit, QList() << 0); + QCOMPARE( m_listSingleEdit->currentChoices().length(), 1 ); + QCOMPARE( m_listSingleEdit->currentChoices()[0], 0 ); + triggerResetAction(); + QVERIFY( m_document->canUndo() ); + QCOMPARE( m_listSingleEdit->currentChoices().length(), 0 ); + + m_document->undo(); + QCOMPARE( m_listSingleEdit->currentChoices().length(), 1 ); + QCOMPARE( m_listSingleEdit->currentChoices()[0], 0 ); + + m_document->redo(); + QCOMPARE( m_listSingleEdit->currentChoices().length(), 0 ); +} + +void ResetFormsTest::testResetListMultiEdit() +{ + // Select first and third choices + m_document->editFormList( 0, m_listMultiEdit, QList() << 0 << 2); + QCOMPARE( m_listMultiEdit->currentChoices(), QList() << 0 << 2); + triggerResetAction(); + QVERIFY( m_document->canUndo() ); + QCOMPARE( m_listMultiEdit->currentChoices(), QList()); + + m_document->undo(); + QCOMPARE( m_listMultiEdit->currentChoices(), QList() << 0 << 2); + + m_document->redo(); + QCOMPARE( m_listMultiEdit->currentChoices(), QList()); +} +// helper methods + +void ResetFormsTest::triggerResetAction() +{ + m_document->resetFormWidgets(); +} +void ResetFormsTest::verifyRadioButtonStates( bool state1, bool state2, bool state3 ) +{ + QVERIFY ( m_radioButtonForms[0]->state() == state1 ); + QVERIFY ( m_radioButtonForms[1]->state() == state2 ); + QVERIFY ( m_radioButtonForms[2]->state() == state3 ); +} + +void ResetFormsTest::setRadioButtonStates( bool state1, bool state2, bool state3 ) +{ + QList newButtonStates; + newButtonStates.append(state1); + newButtonStates.append(state2); + newButtonStates.append(state3); + m_document->editFormButtons( 0, m_radioButtonForms, newButtonStates); +} + +void ResetFormsTest::verifyTextForm( Okular::FormFieldText *form ) +{ + m_document->editFormText(0, form, QStringLiteral("Hello_World"), 11, 0, 0); + triggerResetAction(); + QVERIFY( m_document->canUndo() ); + QCOMPARE( form->text(), QStringLiteral("") ); + + m_document->undo(); + QCOMPARE( form->text(), QStringLiteral("Hello_World") ); + + m_document->redo(); + QCOMPARE( form->text(), QStringLiteral("") ); +} + +QTEST_MAIN( ResetFormsTest ) +#include "resetformstest.moc" \ No newline at end of file diff --git a/core/document.h b/core/document.h --- a/core/document.h +++ b/core/document.h @@ -1014,6 +1014,7 @@ const QList< Okular::FormFieldButton* > & formButtons, const QList< bool > & newButtonStates ); + void resetFormWidgets(); /** * Reloads the pixmaps for whole document * diff --git a/core/document.cpp b/core/document.cpp --- a/core/document.cpp +++ b/core/document.cpp @@ -3967,6 +3967,110 @@ d->m_undoStack->push( uc ); } +void Document::resetFormWidgets() +{ + QVector widgetsCommands; + + for( uint pageIdx = 0; pageIdx < pages(); pageIdx++ ) + { + const Page *currentPage = page( pageIdx ); + QLinkedList pageFields = currentPage->formFields(); + + QList m_checkBoxForms, m_radioButtonForms; + QList newCheckButtonStates, newRadioButtonStates; + + QLinkedList ::const_iterator ffIt = pageFields.constBegin(), ffEnd = pageFields.constEnd(); + QUndoCommand *command; + + for ( ; ffIt != ffEnd; ++ffIt ) + { + FormField * ff = *ffIt; + ff->type(); + + switch ( ff->type() ) + { + case FormField::FormButton: + { + FormFieldButton * ffb = static_cast< FormFieldButton * >( ff ); + switch ( ffb->buttonType() ) + { + case FormFieldButton::Push: + break; + case FormFieldButton::CheckBox: // checkbox + m_checkBoxForms.append(ffb); + newCheckButtonStates.append( false ); + break; + case FormFieldButton::Radio: // radio button + m_radioButtonForms.append(ffb); + newRadioButtonStates.append( false ); + break; + default: ; + } + break; + } + case FormField::FormText: // Single Line, multiline, fileEdit + { + FormFieldText * fft = static_cast< FormFieldText * >( ff ); + //resetFormFieldText( i, document, fft ); + int prevCursorPos = fft->text().size(); + command = new EditFormTextCommand( this->d, fft, pageIdx, QStringLiteral(""), 0, fft->text(), prevCursorPos, prevCursorPos ); + widgetsCommands.push_back( command ); + break; + } + case FormField::FormChoice: + { + FormFieldChoice * ffc = static_cast< FormFieldChoice * >( ff ); + switch ( ffc->choiceType() ) + { + case FormFieldChoice::ListBox: + { //resetListEdit(i, document, ffc); + const QList< int > prevChoices = ffc->currentChoices(); + command = new EditFormListCommand( this->d, ffc, pageIdx, QList(), prevChoices ); + widgetsCommands.push_back( command ); + break; + } + case FormFieldChoice::ComboBox: + { //resetComboForms(i, document, ffc); + QString prevText; + if ( ffc->currentChoices().isEmpty() ) + { + prevText = ffc->editChoice(); + } + else + { + prevText = ffc->choices()[ffc->currentChoices().constFirst()]; + } + + command = new EditFormComboCommand( this->d, ffc, pageIdx, QStringLiteral(""), 0, prevText, 0, 0 ); + widgetsCommands.push_back( command ); + break; + } + } + break; + } + default: ; + } + + if(m_checkBoxForms.size()) + { + //resetCheckBoxForms( i, document, m_checkBoxForms, newCheckButtonStates); + command = new EditFormButtonsCommand( this->d, pageIdx, m_checkBoxForms, newCheckButtonStates ); + widgetsCommands.push_back( command ); + } + if(m_radioButtonForms.size()) + { + newRadioButtonStates[0] = true; + //resetRadioButtonForms( i, document, m_radioButtonForms, newRadioButtonStates); + command = new EditFormButtonsCommand( this->d, pageIdx, m_radioButtonForms, newRadioButtonStates ); + widgetsCommands.push_back( command ); + } + } + } + + QUndoCommand *uc = new EditWidgetsCommand( widgetsCommands ); + d->m_undoStack->push( uc ); +} + void Document::reloadDocument() const { const int numOfPages = pages(); diff --git a/core/documentcommands.cpp b/core/documentcommands.cpp --- a/core/documentcommands.cpp +++ b/core/documentcommands.cpp @@ -749,5 +749,39 @@ } } +EditWidgetsCommand::EditWidgetsCommand(const QVector &widgetsUndoCommands) + :widgetsUndoCommands( widgetsUndoCommands ) +{ + +} + +void EditWidgetsCommand::undo() +{ + foreach( QUndoCommand* widgetCommand, widgetsUndoCommands) + { + widgetCommand->undo(); + } +} + +void EditWidgetsCommand::redo() +{ + foreach( QUndoCommand* widgetCommand, widgetsUndoCommands) + { + widgetCommand->redo(); + } +} + +bool EditWidgetsCommand::refreshInternalPageReferences( const QVector< Okular::Page * > &newPagesVector ) +{ + bool ok = false; + foreach( QUndoCommand* widgetCommand, widgetsUndoCommands) + { + OkularUndoCommand *command = static_cast< OkularUndoCommand * >( widgetCommand ); + bool temp = command->refreshInternalPageReferences( newPagesVector ); + ok = ok || temp; + } + return ok; +} + } diff --git a/core/documentcommands_p.h b/core/documentcommands_p.h --- a/core/documentcommands_p.h +++ b/core/documentcommands_p.h @@ -310,6 +310,21 @@ QList< bool > m_prevButtonStates; }; +// class to undo all widgets + +class EditWidgetsCommand : public OkularUndoCommand +{ + public: + EditWidgetsCommand(const QVector &widgetsUndoCommands); + + void undo() override; + void redo() override; + bool refreshInternalPageReferences( const QVector< Okular::Page * > &newPagesVector ) override; + + private: + QVector widgetsUndoCommands; +}; + } #endif diff --git a/part.h b/part.h --- a/part.h +++ b/part.h @@ -115,7 +115,7 @@ Q_INTERFACES(Okular::ViewerInterface) friend class PartTest; - + public: // Default constructor /** diff --git a/part.cpp b/part.cpp --- a/part.cpp +++ b/part.cpp @@ -3356,7 +3356,6 @@ // attach the actions of the children widgets too m_formsMessage->addAction( m_pageView->toggleFormsAction() ); - // ensure history actions are in the correct state updateViewActions(); } diff --git a/part.rc b/part.rc --- a/part.rc +++ b/part.rc @@ -1,5 +1,5 @@ - + &File @@ -47,6 +47,7 @@ + &Go diff --git a/ui/pageview.h b/ui/pageview.h --- a/ui/pageview.h +++ b/ui/pageview.h @@ -56,7 +56,7 @@ */ class PageView : public QAbstractScrollArea, public Okular::DocumentObserver, public Okular::View { -Q_OBJECT +Q_OBJECT public: PageView( QWidget *parent, Okular::Document *document ); @@ -102,6 +102,7 @@ KActionCollection *actionCollection() const; QAction *toggleFormsAction() const; + QAction *resetFormsAction() const; int contentAreaWidth() const; int contentAreaHeight() const; @@ -187,6 +188,7 @@ void scrollTo( int x, int y ); void toggleFormWidgets( bool on ); + void resetFormWidgets( bool on ); void resizeContentArea( const QSize & newSize ); void updatePageStep(); @@ -257,6 +259,7 @@ void slotTrimMarginsToggled( bool ); void slotTrimToSelectionToggled( bool ); void slotToggleForms(); + void slotResetForms(); void slotFormChanged( int pageNumber ); void slotRefreshPage(); #ifdef HAVE_SPEECH diff --git a/ui/pageview.cpp b/ui/pageview.cpp --- a/ui/pageview.cpp +++ b/ui/pageview.cpp @@ -229,6 +229,7 @@ KToggleAction * aViewContinuous; QAction * aPrevAction; QAction * aToggleForms; + QAction * aResetForms; QAction * aSpeakDoc; QAction * aSpeakPage; QAction * aSpeakStop; @@ -351,6 +352,7 @@ d->aViewContinuous = nullptr; d->aPrevAction = nullptr; d->aToggleForms = nullptr; + d->aResetForms = nullptr; d->aSpeakDoc = nullptr; d->aSpeakPage = nullptr; d->aSpeakStop = nullptr; @@ -722,6 +724,12 @@ d->aToggleForms->setEnabled( false ); toggleFormWidgets( false ); + d->aResetForms = new QAction( this ); + ac->addAction( QStringLiteral("view_reset_forms"), d->aResetForms ); + connect( d->aResetForms, &QAction::triggered, this, &PageView::slotResetForms ); + d->aResetForms->setEnabled( false ); + d->aResetForms->setText( i18n( "Reset Forms" ) ); + // Setup undo and redo actions QAction *kundo = KStandardAction::create( KStandardAction::Undo, d->document, SLOT(undo()), ac ); QAction *kredo = KStandardAction::create( KStandardAction::Redo, d->document, SLOT(redo()), ac ); @@ -865,6 +873,11 @@ return d->aToggleForms; } +QAction *PageView::resetFormsAction() const +{ + return d->aResetForms; +} + int PageView::contentAreaWidth() const { return horizontalScrollBar()->maximum() + viewport()->width(); @@ -1215,6 +1228,11 @@ { // may be null if dummy mode is on d->aToggleForms->setEnabled( haspages && hasformwidgets ); } + if ( d->aResetForms ) + { + d->aResetForms->setEnabled( haspages && hasformwidgets ); + } + bool allowAnnotations = d->document->isAllowed( Okular::AllowNotes ); if ( d->annotator ) { @@ -4309,6 +4327,13 @@ } } +void PageView::resetFormWidgets( bool on) +{ + if( !on ) + return; + d->document->resetFormWidgets(); +} + void PageView::resizeContentArea( const QSize & newSize ) { const QSize vs = viewport()->size(); @@ -5338,6 +5363,11 @@ toggleFormWidgets( !d->m_formsVisible ); } +void PageView::slotResetForms() +{ + resetFormWidgets( d->m_formsVisible ); +} + void PageView::slotFormChanged( int pageNumber ) { if ( !d->refreshTimer )