Members of the KDE Community are recommended to subscribe to the kde-community mailing list at https://mail.kde.org/mailman/listinfo/kde-community to allow them to participate in important discussions and receive other important announcements

Commit b7ce9114 authored by Jon Mease's avatar Jon Mease Committed by Albert Astals Cid

Undo support for PDF forms

Together with the already commited support for undo in annotations fixes 177501
BUGS: 177501
REVIEW: 110589
FIXED-IN: 4.11.0
parent 8d988191
......@@ -83,6 +83,7 @@
#include "utils_p.h"
#include "view.h"
#include "view_p.h"
#include "form.h"
#include <memory>
......@@ -3408,6 +3409,54 @@ void Document::redo()
d->m_undoStack->redo();
}
void Document::editFormText( int pageNumber,
Okular::FormFieldText* form,
const QString & newContents,
int newCursorPos,
int prevCursorPos,
int prevAnchorPos )
{
QUndoCommand *uc = new EditFormTextCommand( this, form, pageNumber, newContents, newCursorPos, form->text(), prevCursorPos, prevAnchorPos );
d->m_undoStack->push( uc );
}
void Document::editFormList( int pageNumber,
FormFieldChoice* form,
const QList< int > & newChoices )
{
const QList< int > prevChoices = form->currentChoices();
QUndoCommand *uc = new EditFormListCommand( this, form, pageNumber, newChoices, prevChoices );
d->m_undoStack->push( uc );
}
void Document::editFormCombo( int pageNumber,
FormFieldChoice* form,
const QString & newText,
int newCursorPos,
int prevCursorPos,
int prevAnchorPos )
{
QString prevText;
if ( form->currentChoices().isEmpty() )
{
prevText = form->editChoice();
}
else
{
prevText = form->choices()[form->currentChoices()[0]];
}
QUndoCommand *uc = new EditFormComboCommand( this, form, pageNumber, newText, newCursorPos, prevText, prevCursorPos, prevAnchorPos );
d->m_undoStack->push( uc );
}
void Document::editFormButtons( int pageNumber, const QList< FormFieldButton* >& formButtons, const QList< bool >& newButtonStates )
{
QUndoCommand *uc = new EditFormButtonsCommand( this, pageNumber, formButtons, newButtonStates );
d->m_undoStack->push( uc );
}
BookmarkManager * Document::bookmarkManager() const
{
return d->m_bookmarkManager;
......
......@@ -44,6 +44,9 @@ class DocumentViewport;
class EmbeddedFile;
class ExportFormat;
class FontInfo;
class FormFieldText;
class FormFieldButton;
class FormFieldChoice;
class Generator;
class Action;
class MovieAction;
......@@ -746,6 +749,52 @@ class OKULAR_EXPORT Document : public QObject
*/
void redo();
/**
* Edit the text contents of the specified @p form on page @p page to be @p newContents.
* The new text cursor position (@p newCursorPos), previous text cursor position (@p prevCursorPos),
* and previous cursor anchor position will be restored by the undo / redo commands.
* @since 0.17 (KDE 4.11)
*/
void editFormText( int pageNumber,
Okular::FormFieldText* form,
const QString & newContents,
int newCursorPos,
int prevCursorPos,
int prevAnchorPos );
/**
* Edit the selected list entries in @p form on page @p page to be @p newChoices.
* @since 0.17 (KDE 4.11)
*/
void editFormList( int pageNumber,
Okular::FormFieldChoice* form,
const QList<int> & newChoices );
/**
* Set the active choice in the combo box @p form on page @p page to @p newText
* The new cursor position (@p newCursorPos), previous cursor position
* (@p prevCursorPos), and previous anchor position (@p prevAnchorPos)
* will be restored by the undo / redo commands.
*
* @since 0.17 (KDE 4.11)
*/
void editFormCombo( int pageNumber,
Okular::FormFieldChoice *form,
const QString & newText,
int newCursorPos,
int prevCursorPos,
int prevAnchorPos );
/**
* Set the states of the group of form buttons @p formButtons on page @p page to @p newButtonStates.
* The lists @p formButtons and @p newButtonStates should be the same length and true values
* in @p newButtonStates indicate that the corresponding entry in @p formButtons should be enabled.
*/
void editFormButtons( int pageNumber,
const QList< Okular::FormFieldButton* > & formButtons,
const QList< bool > & newButtonStates );
Q_SIGNALS:
/**
* This signal is emitted whenever an action requests a
......@@ -882,12 +931,46 @@ class OKULAR_EXPORT Document : public QObject
*/
void annotationContentsChangedByUndoRedo( Okular::Annotation* annotation, const QString & contents, int cursorPos, int anchorPos );
/**
* This signal is emmitted whenever the text contents of the given text @p form on the given @p page
* are changed by an undo or redo action.
*
* The new text contents (@p contents), cursor position (@p cursorPos), and anchor position (@p anchorPos) are
* included
* @since 0.17 (KDE 4.11)
*/
void formTextChangedByUndoRedo( int page, Okular::FormFieldText* form, const QString & contents, int cursorPos, int anchorPos );
/**
* This signal is emmitted whenever the selected @p choices for the given list @p form on the
* given @p page are changed by an undo or redo action.
* @since 0.17 (KDE 4.11)
*/
void formListChangedByUndoRedo( int page, Okular::FormFieldChoice* form, const QList< int > & choices );
/**
* This signal is emmitted whenever the active @p text for the given combo @p form on the
* given @p page is changed by an undo or redo action.
* @since 0.17 (KDE 4.11)
*/
void formComboChangedByUndoRedo( int page, Okular::FormFieldChoice* form, const QString & text, int cursorPos, int anchorPos );
/**
* This signal is emmitted whenever the state of the specified group of form buttons (@p formButtons) on the
* given @p page is changed by an undo or redo action.
* @since 0.17 (KDE 4.11)
*/
void formButtonsChangedByUndoRedo( int page, const QList< Okular::FormFieldButton* > & formButtons );
private:
/// @cond PRIVATE
friend class DocumentPrivate;
friend class Part;
friend class ::DocumentItem;
friend class EditAnnotationContentsCommand;
friend class EditFormTextCommand;
friend class EditFormListCommand;
friend class EditFormComboCommand;
friend class EditFormButtonsCommand;
/// @endcond
DocumentPrivate *const d;
......
......@@ -12,6 +12,7 @@
#include "annotations.h"
#include "debug_p.h"
#include "document_p.h"
#include "form.h"
#include <KLocalizedString>
......@@ -288,5 +289,209 @@ bool EditAnnotationContentsCommand::mergeWith(const QUndoCommand* uc)
}
}
EditFormTextCommand::EditFormTextCommand( Okular::Document* doc,
Okular::FormFieldText* form,
int pageNumber,
const QString & newContents,
int newCursorPos,
const QString & prevContents,
int prevCursorPos,
int prevAnchorPos )
: EditTextCommand( newContents, newCursorPos, prevContents, prevCursorPos, prevAnchorPos ),
m_doc ( doc ),
m_form( form ),
m_pageNumber( pageNumber )
{
setText( i18nc( "Edit an form's text contents", "edit form contents" ) );
}
void EditFormTextCommand::undo()
{
m_form->setText( m_prevContents );
m_doc->formTextChangedByUndoRedo( m_pageNumber, m_form, m_prevContents, m_prevCursorPos, m_prevAnchorPos );
}
void EditFormTextCommand::redo()
{
m_form->setText( m_newContents );
m_doc->formTextChangedByUndoRedo( m_pageNumber, m_form, m_newContents, m_newCursorPos, m_newCursorPos );
}
int EditFormTextCommand::id() const
{
return 3;
}
bool EditFormTextCommand::mergeWith(const QUndoCommand* uc)
{
EditFormTextCommand *euc = (EditFormTextCommand*)uc;
// Only attempt merge of euc into this if they modify the same form
if ( m_form == euc->m_form )
{
return EditTextCommand::mergeWith( uc );
}
else
{
return false;
}
}
EditFormListCommand::EditFormListCommand( Okular::Document* doc,
FormFieldChoice* form,
int pageNumber,
const QList< int > & newChoices,
const QList< int > & prevChoices )
: m_doc( doc ),
m_form( form ),
m_pageNumber( pageNumber ),
m_newChoices( newChoices ),
m_prevChoices( prevChoices )
{
setText( i18nc( "Edit a list form's choices", "edit list form choices" ) );
}
void EditFormListCommand::undo()
{
m_form->setCurrentChoices( m_prevChoices );
m_doc->formListChangedByUndoRedo( m_pageNumber, m_form, m_prevChoices );
}
void EditFormListCommand::redo()
{
m_form->setCurrentChoices( m_newChoices );
m_doc->formListChangedByUndoRedo( m_pageNumber, m_form, m_newChoices );
}
EditFormComboCommand::EditFormComboCommand( Okular::Document* doc,
FormFieldChoice* form,
int pageNumber,
const QString & newContents,
int newCursorPos,
const QString & prevContents,
int prevCursorPos,
int prevAnchorPos )
: EditTextCommand( newContents, newCursorPos, prevContents, prevCursorPos, prevAnchorPos ),
m_doc( doc ),
m_form( form ),
m_pageNumber( pageNumber ),
m_newIndex( -1 ),
m_prevIndex( -1 )
{
setText( i18nc( "Edit a combo form's selection", "edit combo form selection" ) );
// Determine new and previous choice indices (if any)
for ( int i = 0; i < m_form->choices().size(); i++ )
{
if ( m_form->choices()[i] == m_prevContents )
{
m_prevIndex = i;
}
if ( m_form->choices()[i] == m_newContents )
{
m_newIndex = i;
}
}
}
void EditFormComboCommand::undo()
{
if ( m_prevIndex != -1 )
{
m_form->setCurrentChoices( QList<int>() << m_prevIndex );
}
else
{
m_form->setEditChoice( m_prevContents );
}
m_doc->formComboChangedByUndoRedo( m_pageNumber, m_form, m_prevContents, m_prevCursorPos, m_prevAnchorPos );
}
void EditFormComboCommand::redo()
{
if ( m_newIndex != -1 )
{
m_form->setCurrentChoices( QList<int>() << m_newIndex );
}
else
{
m_form->setEditChoice( m_newContents );
}
m_doc->formComboChangedByUndoRedo( m_pageNumber, m_form, m_newContents, m_newCursorPos, m_newCursorPos );
}
int EditFormComboCommand::id() const
{
return 4;
}
bool EditFormComboCommand::mergeWith( const QUndoCommand *uc )
{
EditFormComboCommand *euc = (EditFormComboCommand*)uc;
// Only attempt merge of euc into this if they modify the same form
if ( m_form == euc->m_form )
{
bool shouldMerge = EditTextCommand::mergeWith( uc );
if( shouldMerge )
{
m_newIndex = euc->m_newIndex;
}
return shouldMerge;
}
else
{
return false;
}
}
EditFormButtonsCommand::EditFormButtonsCommand( Okular::Document* doc,
int pageNumber,
const QList< FormFieldButton* > & formButtons,
const QList< bool > & newButtonStates )
: m_doc( doc ),
m_pageNumber( pageNumber ),
m_formButtons( formButtons ),
m_newButtonStates( newButtonStates ),
m_prevButtonStates( QList< bool >() )
{
setText( i18nc( "Edit the state of a group of form buttons", "edit form button states" ) );
foreach( FormFieldButton* formButton, m_formButtons )
{
m_prevButtonStates.append( formButton->state() );
}
}
void EditFormButtonsCommand::undo()
{
clearFormButtonStates();
for( int i = 0; i < m_formButtons.size(); i++ )
{
bool checked = m_prevButtonStates.at( i );
if ( checked )
m_formButtons.at( i )->setState( checked );
}
m_doc->formButtonsChangedByUndoRedo( m_pageNumber, m_formButtons );
}
void EditFormButtonsCommand::redo()
{
clearFormButtonStates();
for( int i = 0; i < m_formButtons.size(); i++ )
{
bool checked = m_newButtonStates.at( i );
if ( checked )
m_formButtons.at( i )->setState( checked );
}
m_doc->formButtonsChangedByUndoRedo( m_pageNumber, m_formButtons );
}
void EditFormButtonsCommand::clearFormButtonStates()
{
foreach( FormFieldButton* formButton, m_formButtons )
{
formButton->setState( false );
}
}
}
......@@ -17,8 +17,12 @@
namespace Okular {
class Document;
class Annotation;
class DocumentPrivate;
class FormFieldText;
class FormFieldButton;
class FormFieldChoice;
class AddAnnotationCommand : public QUndoCommand
{
......@@ -157,8 +161,98 @@ class EditAnnotationContentsCommand : public EditTextCommand
int m_pageNumber;
};
}
class EditFormTextCommand : public EditTextCommand
{
public:
EditFormTextCommand( Okular::Document* doc,
Okular::FormFieldText* form,
int pageNumber,
const QString & newContents,
int newCursorPos,
const QString & prevContents,
int prevCursorPos,
int prevAnchorPos );
virtual void undo();
virtual void redo();
virtual int id() const;
virtual bool mergeWith( const QUndoCommand *uc );
private:
Okular::Document* m_doc;
Okular::FormFieldText* m_form;
int m_pageNumber;
};
class EditFormListCommand : public QUndoCommand
{
public:
EditFormListCommand( Okular::Document* doc,
FormFieldChoice* form,
int pageNumber,
const QList< int > & newChoices,
const QList< int > & prevChoices
);
virtual void undo();
virtual void redo();
private:
Okular::Document* m_doc;
FormFieldChoice* m_form;
int m_pageNumber;
QList< int > m_newChoices;
QList< int > m_prevChoices;
};
class EditFormComboCommand : public EditTextCommand
{
public:
EditFormComboCommand( Okular::Document* doc,
FormFieldChoice* form,
int pageNumber,
const QString & newText,
int newCursorPos,
const QString & prevText,
int prevCursorPos,
int prevAnchorPos
);
virtual void undo();
virtual void redo();
virtual int id() const;
virtual bool mergeWith( const QUndoCommand *uc );
private:
Okular::Document* m_doc;
FormFieldChoice* m_form;
int m_pageNumber;
int m_newIndex;
int m_prevIndex;
};
class EditFormButtonsCommand : public QUndoCommand
{
public:
EditFormButtonsCommand( Okular::Document* doc,
int pageNumber,
const QList< FormFieldButton* > & formButtons,
const QList< bool > & newButtonStates
);
virtual void undo();
virtual void redo();
private:
void clearFormButtonStates();
private:
Okular::Document* m_doc;
int m_pageNumber;
QList< FormFieldButton* > m_formButtons;
QList< bool > m_newButtonStates;
QList< bool > m_prevButtonStates;
};
}
#endif
/* kate: replace-tabs on; indent-width 4; */
This diff is collapsed.
......@@ -21,10 +21,13 @@
#include <ktextedit.h>
#include <kurlrequester.h>
class ComboEdit;
class QMenu;
class QButtonGroup;
class FormWidgetIface;
class PageViewItem;
class RadioButtonEdit;
class QEvent;
namespace Okular {
class Action;
......@@ -32,6 +35,7 @@ class FormField;
class FormFieldButton;
class FormFieldChoice;
class FormFieldText;
class Document;
}
struct RadioData
......@@ -47,24 +51,81 @@ class FormWidgetsController : public QObject
Q_OBJECT
public:
FormWidgetsController( QObject *parent = 0 );
FormWidgetsController( Okular::Document *doc );
virtual ~FormWidgetsController();
void signalChanged( FormWidgetIface *w );
void signalAction( Okular::Action *action );
QButtonGroup* registerRadioButton( FormWidgetIface* widget, const QList< int >& siblings );
QButtonGroup* registerRadioButton( QAbstractButton *button, Okular::FormFieldButton *formButton );
void dropRadioButtons();
bool canUndo();
bool canRedo();
signals:
void changed( FormWidgetIface *w );
void changed( int pageNumber );
void requestUndo();
void requestRedo();
void canUndoChanged( bool undoAvailable );
void canRedoChanged( bool redoAvailable);
void formTextChangedByWidget( int pageNumber,
Okular::FormFieldText *form,
const QString & newContents,
int newCursorPos,
int prevCursorPos,
int prevAnchorPos );
void formTextChangedByUndoRedo( int pageNumber,
Okular::FormFieldText *form,
const QString & contents,
int cursorPos,
int anchorPos );
void formListChangedByWidget( int pageNumber,
Okular::FormFieldChoice *form,
const QList< int > & newChoices );
void formListChangedByUndoRedo( int pageNumber,
Okular::FormFieldChoice *form,
const QList< int > & choices );
void formComboChangedByWidget( int pageNumber,
Okular::FormFieldChoice *form,
const QString & newText,
int newCursorPos,
int prevCursorPos,
int prevAnchorPos
);
void formComboChangedByUndoRedo( int pageNumber,
Okular::FormFieldChoice *form,
const QString & text,
int cursorPos,
int anchorPos
);
void formButtonsChangedByWidget( int pageNumber,
const QList< Okular::FormFieldButton* > & formButtons,
const QList< bool > & newButtonStates );
void action( Okular::Action *action );
private slots:
void slotButtonClicked( QAbstractButton *button );
void slotFormButtonsChangedByUndoRedo( int pageNumber,
const QList< Okular::FormFieldButton* > & formButtons );
private:
friend class TextAreaEdit;
friend class FormLineEdit;
friend class FileEdit;
friend class ListEdit;
friend class ComboEdit;
QList< RadioData > m_radios;
QHash< int, Okular::FormFieldButton* > m_formButtons;
QHash< int, QAbstractButton* > m_buttons;
Okular::Document* m_doc;
};
......@@ -147,9 +208,6 @@ class RadioButtonEdit : public QRadioButton, public FormWidgetIface
void setFormWidgetsController( FormWidgetsController *controller );
QAbstractButton* button();
private slots:
void slotToggled( bool checked );
private:
Okular::FormFieldButton * m_form;
};
......@@ -160,12 +218,24 @@ class FormLineEdit : public QLineEdit, public FormWidgetIface
public:
explicit FormLineEdit( Okular::FormFieldText * text, QWidget * parent = 0 );
void setFormWidgetsController( FormWidgetsController *controller );
virtual bool event ( QEvent * e );
virtual void contextMenuEvent( QContextMenuEvent* event );
public slots:
void slotHandleTextChangedByUndoRedo( int pageNumber,
Okular::FormFieldText* textForm,
const QString & contents,
int cursorPos,
int anchorPos );
private slots:
void textEdited( const QString& );
void slotChanged();
private:
Okular::FormFieldText * m_form;
int m_prevCursorPos;
int m_prevAnchorPos;
};
class TextAreaEdit : public KTextEdit, public FormWidgetIface
......@@ -174,12 +244,25 @@ class TextAreaEdit : public KTextEdit, public FormWidgetIface
public:
explicit TextAreaEdit( Okular::FormFieldText * text, QWidget * parent = 0 );
void setFormWidgetsController( FormWidgetsController *controller );
virtual bool event ( QEvent * e );
public slots:
void slotHandleTextChangedByUndoRedo( int pageNumber,
Okular::FormFieldText * textForm,
const QString & contents,
int cursorPos,
int anchorPos );
void slotUpdateUndoAndRedoInContextMenu( QMenu* menu );
private slots:
void slotChanged();
private:
Okular::FormFieldText * m_form;
int m_prevCursorPos;
int m_prevAnchorPos;
};
......@@ -189,12 +272,23 @@ class FileEdit : public KUrlRequester, public FormWidgetIface
public:
explicit FileEdit( Okular::FormFieldText * text, QWidget * parent = 0 );
void setFormWidgetsController( FormWidgetsController *controller );
protected:
bool eventFilter( QObject *obj, QEvent *event );
private slots:
void slotChanged( const QString& );
private slots:
void slotChanged();
void slotHandleFileChangedByUndoRedo( int pageNumber,
Okular::FormFieldText * form,
const QString & contents,
int cursorPos,
int anchorPos );
private: