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

Add undo/redo support for annotations

REVIEW: 107442
parent add8fdef
This diff is collapsed.
......@@ -92,6 +92,7 @@ class OKULAR_EXPORT Annotation
/// @cond PRIVATE
friend class AnnotationObjectRect;
friend class Document;
friend class DocumentPrivate;
friend class Page;
friend class PagePrivate;
/// @endcond
......@@ -657,6 +658,16 @@ class OKULAR_EXPORT Annotation
*/
virtual void store( QDomNode & node, QDomDocument & document ) const;
/**
* Retrieve the QDomNode representing this annotation's properties
*/
QDomNode getAnnotationPropertiesDomNode();
/**
* Sets annotations internal properties according to the contents of @p node
*/
void setAnnotationProperties(const QDomNode & node);
protected:
/// @cond PRIVATE
Annotation( AnnotationPrivate &dd );
......@@ -706,7 +717,7 @@ class OKULAR_EXPORT AnnotationProxy
/**
* Called after an existing @p annotation at a given @p page is modified.
*
*
* Generator can call @p annotation getters to get the new values.
* @p appearanceChanged tells if a non-visible property was modifed
*
......
......@@ -43,6 +43,8 @@ class AnnotationPrivate
virtual void resetTransformation();
virtual void translate( const NormalizedPoint &coord );
virtual bool openDialogAfterCreation() const;
virtual void setAnnotationProperties( const QDomNode& node );
virtual AnnotationPrivate* getNewAnnotationPrivate() = 0;
PagePrivate * m_page;
......
This diff is collapsed.
......@@ -20,6 +20,7 @@
#include <QtCore/QStringList>
#include <QtCore/QVector>
#include <QtGui/QPrinter>
#include <QUndoCommand>
#include <QtXml/QDomDocument>
#include <kmimetype.h>
......@@ -382,23 +383,46 @@ class OKULAR_EXPORT Document : public QObject
bool canModifyPageAnnotation( const Annotation * annotation ) const;
/**
* Modifies the given @p annotation on the given @p page.
* Prepares to modify the properties of the given @p annotation.
* Must be called before the annotation's properties are modified
*
* Same as calling modifyPageAnnotation(int,Annotation*,bool) with
* appearanceChanged = true
* @since 0.17 (KDE 4.11)
*/
void modifyPageAnnotation( int page, Annotation *annotation );
void prepareToModifyAnnotationProperties( Annotation * annotation );
/**
* Modifies the given @p annotation on the given @p page.
* Must be preceded by a call to prepareToModifyAnnotationProperties before
* the annotation's properties are modified
*
* The caller can set @p appearanceChanged to false if it didn't change
* the annotation appearance (because it only changed non-visible data
* such as timestamps or author name).
* @since 0.17 (KDE 4.11)
*/
void modifyPageAnnotationProperties( int page, Annotation * annotation );
/**
* Translates the position of the given @p annotation on the given @p page by a distance @p delta in normalized coordinates.
*
* @since 0.15 (KDE 4.9)
* Consecutive translations applied to the same @p annotation are merged together on the undo stack if the
* BeingMoved flag is set on the @P annotation
*
* @since 0.17 (KDE 4.11)
*/
void translatePageAnnotation( int page, Annotation *annotation, const Okular::NormalizedPoint & delta );
/**
* Edits the plain text contents of the given @p annotation on the given @p page.
*
* The contents are set to @p newContents with cursor position @p newCursorPos.
* The previous cursor position @p prevCursorPos and previous anchor position @p prevAnchorPos
* must also be supplied so that they can be restored if the edit action is undone.
*
* The Annotation's internal contents should not be modified prior to calling this method.
*
* @since 0.17 (KDE 4.11)
*/
void modifyPageAnnotation( int page, Annotation *annotation, bool appearanceChanged );
void editPageAnnotationContents( int page, Annotation* annotation, const QString & newContents,
int newCursorPos, int prevCursorPos, int prevAnchorPos );
/**
* Tests if the @p annotation can be removed
......@@ -425,6 +449,18 @@ class OKULAR_EXPORT Document : public QObject
*/
void setPageTextSelection( int page, RegularAreaRect * rect, const QColor & color );
/**
* Returns true if there is an undo command available; otherwise returns false.
* @since 0.17 (KDE 4.11)
*/
bool canUndo() const;
/**
* Returns true if there is a redo command available; otherwise returns false.
* @since 0.17 (KDE 4.11)
*/
bool canRedo() const;
/**
* Describes the possible search types.
*/
......@@ -534,7 +570,7 @@ class OKULAR_EXPORT Document : public QObject
* Prints the document to the given @p printer.
*/
bool print( QPrinter &printer );
/**
* Returns the last print error in case print() failed
* @since 0.11 (KDE 4.5)
......@@ -681,7 +717,6 @@ class OKULAR_EXPORT Document : public QObject
*/
void setAnnotationEditingEnabled( bool enable );
public Q_SLOTS:
/**
* This slot is called whenever the user changes the @p rotation of
......@@ -700,6 +735,17 @@ class OKULAR_EXPORT Document : public QObject
*/
void cancelSearch();
/**
* Undo last edit command
* @since 0.17 (KDE 4.11)
*/
void undo();
/**
* Redo last undone edit command
* @since 0.17 (KDE 4.11)
*/
void redo();
Q_SIGNALS:
/**
......@@ -808,6 +854,18 @@ class OKULAR_EXPORT Document : public QObject
*/
void processMovieAction( const Okular::MovieAction *action );
/**
* This signal is emmitted whenever the availability of the undo function changes
* @since 0.17 (KDE 4.11)
*/
void canUndoChanged( bool undoAvailable );
/**
* This signal is emmitted whenever the availability of the redo function changes
* @since 0.17 (KDE 4.11)
*/
void canRedoChanged( bool redoAvailable );
/**
* This signal is emitted whenever an rendition action is triggered and the UI should process it.
*
......@@ -815,11 +873,22 @@ class OKULAR_EXPORT Document : public QObject
*/
void processRenditionAction( const Okular::RenditionAction *action );
/**
* This signal is emmitted whenever the contents of the given @p annotation are changed by an undo
* or redo action.
*
* The new contents (@p contents), cursor position (@p cursorPos), and anchor position (@p anchorPos) are
* included
* @since 0.17 (KDE 4.11)
*/
void annotationContentsChangedByUndoRedo( Okular::Annotation* annotation, const QString & contents, int cursorPos, int anchorPos );
private:
/// @cond PRIVATE
friend class DocumentPrivate;
friend class Part;
friend class ::DocumentItem;
friend class EditAnnotationContentsCommand;
/// @endcond
DocumentPrivate *const d;
......
......@@ -27,6 +27,7 @@
#include "fontinfo.h"
#include "generator.h"
class QUndoStack;
class QEventLoop;
class QTimer;
class KTemporaryFile;
......@@ -141,6 +142,12 @@ class DocumentPrivate
bool canRemoveExternalAnnotations() const;
void warnLimitedAnnotSupport();
// Methods that implement functionality needed by undo commands
void performAddPageAnnotation( int page, Annotation *annotation );
void performRemovePageAnnotation( int page, Annotation * annotation );
void performModifyPageAnnotation( int page, Annotation * annotation, bool appearanceChanged );
void performSetAnnotationContents( const QString & newContents, Annotation *annot, int pageNumber );
// private slots
void saveDocumentInfo() const;
void slotTimedMemoryCheck();
......@@ -261,6 +268,146 @@ class DocumentPrivate
bool m_annotationsNeedSaveAs;
bool m_annotationBeingMoved; // is an annotation currently being moved?
bool m_showWarningLimitedAnnotSupport;
QUndoStack *m_undoStack;
QDomNode m_prevPropsOfAnnotBeingModified;
};
class AddAnnotationCommand : public QUndoCommand
{
public:
AddAnnotationCommand(Okular::DocumentPrivate * docPriv, Okular::Annotation* annotation, int pageNumber);
virtual ~AddAnnotationCommand();
virtual void undo();
virtual void redo();
private:
Okular::DocumentPrivate * m_docPriv;
Okular::Annotation* m_annotation;
int m_pageNumber;
bool m_done;
};
class RemoveAnnotationCommand : public QUndoCommand
{
public:
RemoveAnnotationCommand(Okular::DocumentPrivate * doc, Okular::Annotation* annotation, int pageNumber);
virtual ~RemoveAnnotationCommand();
virtual void undo();
virtual void redo();
private:
Okular::DocumentPrivate * m_doc;
Okular::Annotation* m_annotation;
int m_pageNumber;
bool m_done;
};
class ModifyAnnotationPropertiesCommand : public QUndoCommand
{
public:
ModifyAnnotationPropertiesCommand( Okular::DocumentPrivate* docPriv, Okular::Annotation* annotation,
int pageNumber,
QDomNode oldProperties,
QDomNode newProperties );
virtual void undo();
virtual void redo();
private:
Okular::DocumentPrivate * m_docPriv;
Okular::Annotation* m_annotation;
int m_pageNumber;
QDomNode m_prevProperties;
QDomNode m_newProperties;
};
class TranslateAnnotationCommand : public QUndoCommand
{
public:
TranslateAnnotationCommand(Okular::DocumentPrivate* docPriv,
Okular::Annotation* annotation,
int pageNumber,
const Okular::NormalizedPoint & delta,
bool completeDrag
);
virtual void undo();
virtual void redo();
virtual int id() const;
virtual bool mergeWith(const QUndoCommand *uc);
Okular::NormalizedPoint minusDelta();
private:
Okular::DocumentPrivate * m_docPriv;
Okular::Annotation* m_annotation;
int m_pageNumber;
Okular::NormalizedPoint m_delta;
bool m_completeDrag;
};
class EditTextCommand : public QUndoCommand
{
public:
EditTextCommand( const QString & newContents,
int newCursorPos,
const QString & prevContents,
int prevCursorPos,
int prevAnchorPos
);
virtual void undo() = 0;
virtual void redo() = 0;
virtual int id() const = 0;
virtual bool mergeWith(const QUndoCommand *uc);
private:
enum EditType {
CharBackspace, ///< Edit made up of one or more single character backspace operations
CharDelete, ///< Edit made up of one or more single character delete operations
CharInsert, ///< Edit made up of one or more single character insertion operations
OtherEdit ///< All other edit operations (these will not be merged together)
};
QString oldContentsLeftOfCursor();
QString newContentsLeftOfCursor();
QString oldContentsRightOfCursor();
QString newContentsRightOfCursor();
protected:
QString m_newContents;
int m_newCursorPos;
QString m_prevContents;
int m_prevCursorPos;
int m_prevAnchorPos;
EditType m_editType;
};
class EditAnnotationContentsCommand : public EditTextCommand
{
public:
EditAnnotationContentsCommand(Okular::DocumentPrivate* docPriv,
Okular::Annotation* annotation,
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::DocumentPrivate * m_docPriv;
Okular::Annotation* m_annotation;
int m_pageNumber;
};
}
......
......@@ -249,7 +249,7 @@ RegularAreaRect * Page::wordAt( const NormalizedPoint &p, QString *word ) const
if ( d->m_text )
return d->m_text->wordAt( p, word );
return 0;
return 0;
}
RegularAreaRect * Page::textArea ( TextSelection * selection ) const
......@@ -667,7 +667,7 @@ bool Page::removeAnnotation( Annotation * annotation )
rectfound = true;
}
kDebug(OkularDebug) << "removed annotation:" << annotation->uniqueName();
delete *aIt;
annotation->d_ptr->m_page = 0;
m_annotations.erase( aIt );
break;
}
......@@ -816,7 +816,7 @@ void PagePrivate::restoreLocalContents( const QDomNode & pageNode )
// append annotation to the list or show warning
if ( annotation )
{
m_doc->m_parent->addPageAnnotation(m_number, annotation);
m_doc->performAddPageAnnotation(m_number, annotation);
kDebug(OkularDebug) << "restored annot:" << annotation->uniqueName();
}
else
......
<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
<kpartgui name="okular_part" version="33">
<kpartgui name="okular_part" version="34">
<MenuBar>
<Menu name="file"><text>&amp;File</text>
<Action name="get_new_stuff" group="file_open"/>
......@@ -14,6 +14,9 @@
<Action name="file_export_as" group="file_print"/>
</Menu>
<Menu name="edit"><text>&amp;Edit</text>
<Action name="edit_undo"/>
<Action name="edit_redo"/>
<Separator/>
<Action name="edit_copy"/>
<Separator/>
<Action name="edit_select_all"/>
......
......@@ -85,7 +85,7 @@ AnnotsPropertiesDialog::AnnotsPropertiesDialog( QWidget *parent, Okular::Documen
gridlayout->addItem( new QSpacerItem( 5, 5, QSizePolicy::Fixed, QSizePolicy::MinimumExpanding ), 3, 0 );
//END tab1
//BEGIN tab 2
page = new QFrame( this );
addPage( page, i18n( "&General" ) );
......@@ -97,12 +97,12 @@ AnnotsPropertiesDialog::AnnotsPropertiesDialog( QWidget *parent, Okular::Documen
tmplabel->setBuddy( AuthorEdit );
gridlayout->addWidget( tmplabel, 0, 0, Qt::AlignRight );
gridlayout->addWidget( AuthorEdit, 0, 1 );
tmplabel = new QLabel( page );
tmplabel->setText( i18n( "Created: %1", KGlobal::locale()->formatDateTime( ann->creationDate(), KLocale::LongDate, true ) ) );
tmplabel->setTextInteractionFlags( Qt::TextSelectableByMouse );
gridlayout->addWidget( tmplabel, 1, 0, 1, 2 );
m_modifyDateLabel = new QLabel( page );
m_modifyDateLabel->setText( i18n( "Modified: %1", KGlobal::locale()->formatDateTime( ann->modificationDate(), KLocale::LongDate, true ) ) );
m_modifyDateLabel->setTextInteractionFlags( Qt::TextSelectableByMouse );
......@@ -198,6 +198,7 @@ void AnnotsPropertiesDialog::slotapply()
if ( !modified )
return;
m_document->prepareToModifyAnnotationProperties( m_annot );
m_annot->setAuthor( AuthorEdit->text() );
m_annot->setModificationDate( QDateTime::currentDateTime() );
m_annot->style().setColor( colorBn->color() );
......@@ -206,7 +207,7 @@ void AnnotsPropertiesDialog::slotapply()
if ( m_annotWidget )
m_annotWidget->applyChanges();
m_document->modifyPageAnnotation( m_page, m_annot );
m_document->modifyPageAnnotationProperties( m_page, m_annot );
m_modifyDateLabel->setText( i18n( "Modified: %1", KGlobal::locale()->formatDateTime( m_annot->modificationDate(), KLocale::LongDate, true ) ) );
......@@ -215,4 +216,4 @@ void AnnotsPropertiesDialog::slotapply()
}
#include "annotationpropertiesdialog.moc"
......@@ -28,6 +28,9 @@
#include <klocale.h>
#include <ktextedit.h>
#include <kdebug.h>
#include <kaction.h>
#include <kstandardaction.h>
#include <qmenu.h>
// local includes
#include "core/annotations.h"
......@@ -194,10 +197,21 @@ AnnotWindow::AnnotWindow( QWidget * parent, Okular::Annotation * annot, Okular::
textEdit = new KTextEdit( this );
textEdit->setAcceptRichText( false );
textEdit->setPlainText( GuiUtils::contents( m_annot ) );
textEdit->setPlainText( m_annot->contents() );
textEdit->installEventFilter( this );
connect(textEdit,SIGNAL(textChanged()),
textEdit->setUndoRedoEnabled( false );
m_prevCursorPos = textEdit->textCursor().position();
m_prevAnchorPos = textEdit->textCursor().anchor();
connect(textEdit, SIGNAL(textChanged()),
this,SLOT(slotsaveWindowText()));
connect(textEdit, SIGNAL(cursorPositionChanged()),
this,SLOT(slotsaveWindowText()));
connect(textEdit, SIGNAL(aboutToShowContextMenu(QMenu*)),
this,SLOT(slotUpdateUndoAndRedoInContextMenu(QMenu*)));
connect(m_document, SIGNAL(annotationContentsChangedByUndoRedo(Okular::Annotation*,QString,int,int)),
this, SLOT(slotHandleContentsChangedByUndoRedo(Okular::Annotation*,QString,int,int)));
if (!canEditAnnotation)
textEdit->setReadOnly(true);
......@@ -215,7 +229,7 @@ AnnotWindow::AnnotWindow( QWidget * parent, Okular::Annotation * annot, Okular::
lowerlay->addWidget( sb );
m_latexRenderer = new GuiUtils::LatexRenderer();
emit containsLatex( GuiUtils::LatexRenderer::mightContainLatex( GuiUtils::contents( m_annot ) ) );
emit containsLatex( GuiUtils::LatexRenderer::mightContainLatex( m_annot->contents() ) );
m_title->setTitle( m_annot->window().summary() );
m_title->connectOptionButton( this, SLOT(slotOptionBtn()) );
......@@ -245,11 +259,6 @@ void AnnotWindow::reloadInfo()
m_title->setDate( m_annot->modificationDate() );
}
Okular::Annotation* AnnotWindow::annotation() const
{
return m_annot;
}
void AnnotWindow::showEvent( QShowEvent * event )
{
QFrame::showEvent( event );
......@@ -269,9 +278,48 @@ bool AnnotWindow::eventFilter(QObject *, QEvent *e)
return true;
}
}
else if (e->type() == QEvent::KeyPress)
{
QKeyEvent *keyEvent = static_cast<QKeyEvent*>(e);
if (keyEvent == QKeySequence::Undo)
{
m_document->undo();
return true;
}
else if (keyEvent == QKeySequence::Redo)
{
m_document->redo();
return true;
}
}
return false;
}
void AnnotWindow::slotUpdateUndoAndRedoInContextMenu(QMenu* menu)
{
if (!menu) return;
QList<QAction *> actionList = menu->actions();
enum { UndoAct, RedoAct, CutAct, CopyAct, PasteAct, ClearAct, SelectAllAct, NCountActs };
KAction *kundo = KStandardAction::create( KStandardAction::Undo, m_document, SLOT(undo()), menu);
KAction *kredo = KStandardAction::create( KStandardAction::Redo, m_document, SLOT(redo()), menu);
connect(m_document, SIGNAL(canUndoChanged(bool)), kundo, SLOT(setEnabled(bool)));
connect(m_document, SIGNAL(canRedoChanged(bool)), kredo, SLOT(setEnabled(bool)));
kundo->setEnabled(m_document->canUndo());
kredo->setEnabled(m_document->canRedo());
QAction *oldUndo, *oldRedo;
oldUndo = actionList[UndoAct];
oldRedo = actionList[RedoAct];
menu->insertAction(oldUndo, kundo);
menu->insertAction(oldRedo, kredo);
menu->removeAction(oldUndo);
menu->removeAction(oldRedo);
}
void AnnotWindow::slotOptionBtn()
{
//TODO: call context menu in pageview
......@@ -280,49 +328,15 @@ void AnnotWindow::slotOptionBtn()
void AnnotWindow::slotsaveWindowText()
{
const QString newText = textEdit->toPlainText();
bool appearanceChanged = false;
// Set window text
if ( !m_annot->window().text().isEmpty() )
QString contents = textEdit->toPlainText();
int cursorPos = textEdit->textCursor().position();
if (contents != m_annot->contents())
{
m_annot->window().setText( newText );
return;
m_document->editPageAnnotationContents( m_page, m_annot, contents, cursorPos, m_prevCursorPos, m_prevAnchorPos);
emit containsLatex( GuiUtils::LatexRenderer::mightContainLatex( textEdit->toPlainText() ) );
}
// Handle special cases
switch ( m_annot->subType() )
{
// If it's an in-place TextAnnotation, set the inplace text
case Okular::Annotation::AText:
{
Okular::TextAnnotation * txtann = static_cast< Okular::TextAnnotation * >( m_annot );
if ( txtann->textType() == Okular::TextAnnotation::InPlace )
{
txtann->setInplaceText( newText );
appearanceChanged = true;
}
break;
}
// If it's a LineAnnotation, check if caption text is visible
case Okular::Annotation::ALine:
{
Okular::LineAnnotation * lineann = static_cast< Okular::LineAnnotation * >( m_annot );
if ( lineann->showCaption() )
appearanceChanged = true;
break;
}
default:
break;
}
// Set contents
m_annot->setContents( newText );
// Tell the document
m_document->modifyPageAnnotation( m_page, m_annot, appearanceChanged );
emit containsLatex( GuiUtils::LatexRenderer::mightContainLatex( newText ) );
m_prevCursorPos = cursorPos;
m_prevAnchorPos = textEdit->textCursor().anchor();
}
void AnnotWindow::renderLatex( bool render )
......@@ -332,7 +346,7 @@ void AnnotWindow::renderLatex( bool render )
textEdit->setReadOnly( true );
disconnect(textEdit, SIGNAL(textChanged()), this,SLOT(slotsaveWindowText()));
textEdit->setAcceptRichText( true );
QString contents = GuiUtils::contents( m_annot );
QString contents = m_annot->contents();
contents = Qt::convertFromPlainText( contents );
QColor fontColor = textEdit->textColor();
int fontSize = textEdit->fontPointSize();
......@@ -369,10 +383,28 @@ void AnnotWindow::renderLatex( bool render )
else
{
textEdit->setAcceptRichText( false );
textEdit->setPlainText( GuiUtils::contents( m_annot ) );
textEdit->setPlainText( m_annot->contents() );
connect(textEdit, SIGNAL(textChanged()), this,SLOT(slotsaveWindowText()));
textEdit->setReadOnly( false );
}
}
void AnnotWindow::slotHandleContentsChangedByUndoRedo(Okular::Annotation* annot, QString contents, int cursorPos, int anchorPos)
{
if ( annot != m_annot )
{
return;
}
textEdit->setPlainText(contents);
QTextCursor c = textEdit->textCursor();
c.setPosition(anchorPos);
c.setPosition(cursorPos,QTextCursor::KeepAnchor);
m_prevCursorPos = cursorPos;
m_prevAnchorPos = anchorPos;
textEdit->setTextCursor(c);
textEdit->setFocus();
emit containsLatex( GuiUtils::LatexRenderer::mightContainLatex( m_annot->contents() ) );
}
#include "annotwindow.moc"
......@@ -23,8 +23,9 @@ namespace GuiUtils {
class LatexRenderer;
}
class QTextEdit;
class KTextEdit;
class MovableTitle;
class QMenu;
class AnnotWindow : public QFrame
{
......@@ -34,25 +35,28 @@ class AnnotWindow : public QFrame
~AnnotWindow();
void reloadInfo();
Okular::Annotation* annotation() const;
private:
MovableTitle * m_title;
QTextEdit *textEdit;
KTextEdit *textEdit;
QColor m_color;
GuiUtils::LatexRenderer *m_latexRenderer;
Okular::Annotation* m_annot;
Okular::Document* m_document;
int m_page;
int m_prevCursorPos;
int m_prevAnchorPos;
protected:
virtual void showEvent( QShowEvent * event );
virtual bool eventFilter( QObject * obj, QEvent * event );
private slots:
void slotUpdateUndoAndRedoInContextMenu(QMenu *menu);
void slotOptionBtn();
void slotsaveWindowText();
void renderLatex( bool render );
void slotHandleContentsChangedByUndoRedo( Okular::Annotation* annot, QString contents, int cursorPos, int anchorPos);
signals:
void containsLatex( bool );
......
......@@ -119,35 +119,9 @@ QString authorForAnnotation( const Okular::Annotation * ann )
return !ann->author().isEmpty() ? ann->author() : i18nc( "Unknown author", "Unknown" );
}
QString contents( const Okular::Annotation * ann )
{
Q_ASSERT( ann );
// 1. window text
QString ret = ann->window().text();
if ( !ret.isEmpty() )
return ret;
// 2. if Text and InPlace, the inplace text
if ( ann->subType() == Okular::Annotation::AText )
{
const Okular::TextAnnotation * txtann = static_cast< const Okular::TextAnnotation * >( ann );
if ( txtann->textType() == Okular::TextAnnotation::InPlace )
{
ret = txtann->inplaceText();
if ( !ret.isEmpty() )
return ret;
}
}
// 3. contents
ret = ann->contents();
return ret;
}
QString contentsHtml( const Okular::Annotation * ann )
{
QString text = Qt::escape( contents( ann ) );
QString text = Qt::escape( ann->contents() );
text.replace( '\n', "<br>" );
return text;
}
......
......@@ -32,7 +32,6 @@ namespace GuiUtils
QString captionForAnnotation( const Okular::Annotation * annotation );
QString authorForAnnotation( const Okular::Annotation * annotation );
QString contents( const Okular::Annotation * annotation );
QString contentsHtml( const Okular::Annotation * annotation );