From ec9f068d770b536f85d8322b672de2e97fe81835 Mon Sep 17 00:00:00 2001 From: Fabio D'Urso Date: Mon, 14 May 2012 00:50:41 +0200 Subject: [PATCH] Added AnnotationProxy to SaveInterface Based on Pino Toscano's earlier work --- core/annotations.cpp | 3 + core/annotations.h | 53 ++++++++++++++ core/document.cpp | 101 +++++++++++++++++++++++++-- core/document.h | 21 ++++++ core/page.cpp | 3 +- generators/poppler/generator_pdf.cpp | 5 ++ generators/poppler/generator_pdf.h | 1 + interfaces/saveinterface.h | 12 +++- ui/annotwindow.cpp | 39 ++++++++--- 9 files changed, 220 insertions(+), 18 deletions(-) diff --git a/core/annotations.cpp b/core/annotations.cpp index 4e58288ed..adb634d27 100644 --- a/core/annotations.cpp +++ b/core/annotations.cpp @@ -100,6 +100,9 @@ QRect AnnotationUtils::annotationGeometry( const Annotation * ann, } //END AnnotationUtils implementation +AnnotationProxy::~AnnotationProxy() +{ +} //BEGIN Annotation implementation diff --git a/core/annotations.h b/core/annotations.h index cf6428604..4a84d8c96 100644 --- a/core/annotations.h +++ b/core/annotations.h @@ -655,6 +655,59 @@ class OKULAR_EXPORT Annotation Q_DISABLE_COPY( Annotation ) }; +/** + * @short Native annotation interface + * + * Generators can subclass it to provide native annotation support. + * Generators can use Annotation::setNativeId to store per-annotation data. + * + * @since 0.15 (KDE 4.9) + */ +class OKULAR_EXPORT AnnotationProxy +{ + public: + enum Capability + { + Addition, ///< Generator can create native annotations + Modification, ///< Generator can edit native annotations + Removal ///< Generator can remove native annotations + }; + + /** + * Destroys the annotation proxy. + */ + virtual ~AnnotationProxy(); + + /** + * Query for the supported capabilities. + */ + virtual bool supports( Capability capability ) const = 0; + + /** + * Called when a new @p annotation is added to a @p page. + * + * @note Only called if supports(Addition) == true + */ + virtual void notifyAddition( Annotation *annotation, int page ) = 0; + + /** + * 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 + * + * @note Only called if supports(Modification) == true + */ + virtual void notifyModification( const Annotation *annotation, int page, bool appearanceChanged ) = 0; + + /** + * Called when an existing @p annotation at a given @p page is removed. + * + * @note Only called if supports(Removal) == true + */ + virtual void notifyRemoval( Annotation *annotation, int page ) = 0; +}; + class OKULAR_EXPORT TextAnnotation : public Annotation { public: diff --git a/core/document.cpp b/core/document.cpp index 7c511541c..fe80cffaf 100644 --- a/core/document.cpp +++ b/core/document.cpp @@ -2331,6 +2331,9 @@ void Document::requestTextPage( uint page ) void Document::addPageAnnotation( int page, Annotation * annotation ) { + Okular::SaveInterface * iface = qobject_cast< Okular::SaveInterface * >( d->m_generator ); + AnnotationProxy *proxy = iface ? iface->annotationProxy() : 0; + // find out the page to attach annotation Page * kp = d->m_pagesVector[ page ]; if ( !d->m_generator || !kp ) @@ -2343,41 +2346,110 @@ void Document::addPageAnnotation( int page, Annotation * annotation ) // add annotation to the page kp->addAnnotation( annotation ); + // tell the annotation proxy + if ( proxy && proxy->supports(AnnotationProxy::Addition) ) + proxy->notifyAddition( annotation, page ); + // notify observers about the change foreachObserver( notifyPageChanged( page, DocumentObserver::Annotations ) ); + + if ( annotation->flags() & Annotation::ExternallyDrawn ) + { + // Redraw everything, including ExternallyDrawn annotations + d->refreshPixmaps( page ); + } } -void Document::modifyPageAnnotation( int page, Annotation * newannotation ) +void Document::modifyPageAnnotation( int page, Annotation * annotation ) { - //TODO: modify annotations + modifyPageAnnotation( page, annotation, true ); +} + +void Document::modifyPageAnnotation( int page, Annotation * annotation, bool appearanceChanged ) +{ + Okular::SaveInterface * iface = qobject_cast< Okular::SaveInterface * >( d->m_generator ); + AnnotationProxy *proxy = iface ? iface->annotationProxy() : 0; // find out the page Page * kp = d->m_pagesVector[ page ]; if ( !d->m_generator || !kp ) return; + // tell the annotation proxy + if ( proxy && proxy->supports(AnnotationProxy::Modification) ) + proxy->notifyModification( annotation, page, appearanceChanged ); + // notify observers about the change foreachObserver( notifyPageChanged( page, DocumentObserver::Annotations ) ); + + if ( appearanceChanged && (annotation->flags() & Annotation::ExternallyDrawn) ) + { + // Redraw everything, including ExternallyDrawn annotations + d->refreshPixmaps( page ); + } } +bool Document::canRemovePageAnnotation( const Annotation * annotation ) const +{ + if ( !annotation || ( annotation->flags() & Annotation::DenyDelete ) ) + return false; + + switch ( annotation->subType() ) + { + case Annotation::AText: + case Annotation::ALine: + case Annotation::AGeom: + case Annotation::AHighlight: + case Annotation::AStamp: + case Annotation::AInk: + return true; + default: + return false; + } +} void Document::removePageAnnotation( int page, Annotation * annotation ) { + Okular::SaveInterface * iface = qobject_cast< Okular::SaveInterface * >( d->m_generator ); + AnnotationProxy *proxy = iface ? iface->annotationProxy() : 0; + bool isExternallyDrawn; + // find out the page Page * kp = d->m_pagesVector[ page ]; if ( !d->m_generator || !kp ) return; + if ( annotation->flags() & Annotation::ExternallyDrawn ) + isExternallyDrawn = true; + else + isExternallyDrawn = false; + // try to remove the annotation - if ( kp->removeAnnotation( annotation ) ) + if ( canRemovePageAnnotation( annotation ) ) { + // tell the annotation proxy + if ( proxy && proxy->supports(AnnotationProxy::Removal) ) + proxy->notifyRemoval( annotation, page ); + + kp->removeAnnotation( annotation ); // Also destroys the object + // in case of success, notify observers about the change foreachObserver( notifyPageChanged( page, DocumentObserver::Annotations ) ); + + if ( isExternallyDrawn ) + { + // Redraw everything, including ExternallyDrawn annotations + d->refreshPixmaps( page ); + } } } void Document::removePageAnnotations( int page, const QList< Annotation * > &annotations ) { + Okular::SaveInterface * iface = qobject_cast< Okular::SaveInterface * >( d->m_generator ); + AnnotationProxy *proxy = iface ? iface->annotationProxy() : 0; + bool refreshNeeded = false; + // find out the page Page * kp = d->m_pagesVector[ page ]; if ( !d->m_generator || !kp ) @@ -2386,9 +2458,22 @@ void Document::removePageAnnotations( int page, const QList< Annotation * > &ann bool changed = false; foreach ( Annotation * annotation, annotations ) { - // try to remove the annotation - if ( kp->removeAnnotation( annotation ) ) + bool isExternallyDrawn; + if ( annotation->flags() & Annotation::ExternallyDrawn ) + isExternallyDrawn = true; + else + isExternallyDrawn = false; + + if ( canRemovePageAnnotation( annotation ) ) { + if ( isExternallyDrawn ) + refreshNeeded = true; + + // tell the annotation proxy + if ( proxy && proxy->supports(AnnotationProxy::Removal) ) + proxy->notifyRemoval( annotation, page ); + + kp->removeAnnotation( annotation ); // Also destroys the object changed = true; } } @@ -2396,6 +2481,12 @@ void Document::removePageAnnotations( int page, const QList< Annotation * > &ann { // in case we removed even only one annotation, notify observers about the change foreachObserver( notifyPageChanged( page, DocumentObserver::Annotations ) ); + + if ( refreshNeeded ) + { + // Redraw everything, including ExternallyDrawn annotations + d->refreshPixmaps( page ); + } } } diff --git a/core/document.h b/core/document.h index b05e30d00..9ff32b641 100644 --- a/core/document.h +++ b/core/document.h @@ -369,9 +369,30 @@ class OKULAR_EXPORT Document : public QObject /** * Modifies the given @p annotation on the given @p page. + * + * Same as calling modifyPageAnnotation(int,Annotation*,bool) with + * appearanceChanged = true */ void modifyPageAnnotation( int page, Annotation *annotation ); + /** + * Modifies the given @p annotation on the given @p page. + * + * 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.15 (KDE 4.9) + */ + void modifyPageAnnotation( int page, Annotation *annotation, bool appearanceChanged ); + + /** + * Tests if the @p annotation can be removed + * + * @since 0.15 (KDE 4.9) + */ + bool canRemovePageAnnotation( const Annotation * annotation ) const; + /** * Removes the given @p annotation from the given @p page. */ diff --git a/core/page.cpp b/core/page.cpp index 3e4c07cc7..e645b64a1 100644 --- a/core/page.cpp +++ b/core/page.cpp @@ -26,6 +26,7 @@ #include "annotations_p.h" #include "area.h" #include "debug_p.h" +#include "document_p.h" #include "form.h" #include "form_p.h" #include "pagecontroller_p.h" @@ -598,7 +599,7 @@ void Page::addAnnotation( Annotation * annotation ) bool Page::removeAnnotation( Annotation * annotation ) { - if ( !annotation || ( annotation->flags() & Annotation::DenyDelete ) ) + if ( !d->m_doc->m_parent->canRemovePageAnnotation(annotation) ) return false; QLinkedList< Annotation * >::iterator aIt = m_annotations.begin(), aEnd = m_annotations.end(); diff --git a/generators/poppler/generator_pdf.cpp b/generators/poppler/generator_pdf.cpp index e370b93bf..a27f9ffa7 100644 --- a/generators/poppler/generator_pdf.cpp +++ b/generators/poppler/generator_pdf.cpp @@ -1764,6 +1764,11 @@ bool PDFGenerator::save( const QString &fileName, SaveOptions options, QString * return success; } +Okular::AnnotationProxy* PDFGenerator::annotationProxy() const +{ + return 0; // Not supported +} + #include "generator_pdf.moc" /* kate: replace-tabs on; indent-width 4; */ diff --git a/generators/poppler/generator_pdf.h b/generators/poppler/generator_pdf.h index d9efbdbdb..0e6ebf038 100644 --- a/generators/poppler/generator_pdf.h +++ b/generators/poppler/generator_pdf.h @@ -93,6 +93,7 @@ class PDFGenerator : public Okular::Generator, public Okular::ConfigInterface, p // [INHERITED] save interface bool supportsOption( SaveOption ) const; bool save( const QString &fileName, SaveOptions options, QString *errorText ); + Okular::AnnotationProxy* annotationProxy() const; protected: bool doCloseDocument(); diff --git a/interfaces/saveinterface.h b/interfaces/saveinterface.h index f55e209bd..21dc3de29 100644 --- a/interfaces/saveinterface.h +++ b/interfaces/saveinterface.h @@ -65,11 +65,21 @@ class OKULAR_EXPORT SaveInterface * Save to the specified @p fileName with the specified @p options. */ virtual bool save( const QString &fileName, SaveOptions options, QString *errorText ) = 0; + + /** + * Returns the annotation proxy. Generators can return NULL if native + * annotations are not supported. + * + * @note Returning NULL is equivalent to returning an AnnotationProxy + * that doesn't support any capability. + * @since 0.15 (KDE 4.9) + */ + virtual AnnotationProxy* annotationProxy() const = 0; }; } -Q_DECLARE_INTERFACE( Okular::SaveInterface, "org.kde.okular.SaveInterface/0.2" ) +Q_DECLARE_INTERFACE( Okular::SaveInterface, "org.kde.okular.SaveInterface/0.3" ) Q_DECLARE_OPERATORS_FOR_FLAGS( Okular::SaveInterface::SaveOptions ) #endif diff --git a/ui/annotwindow.cpp b/ui/annotwindow.cpp index 3f8cf2e3c..fce1667be 100644 --- a/ui/annotwindow.cpp +++ b/ui/annotwindow.cpp @@ -276,30 +276,47 @@ void AnnotWindow::slotOptionBtn() void AnnotWindow::slotsaveWindowText() { const QString newText = textEdit->toPlainText(); - - // 0. tell the document - m_document->modifyPageAnnotation( m_page, m_annot ); + bool appearanceChanged = false; - // 1. window text + // Set window text if ( !m_annot->window().text().isEmpty() ) { m_annot->window().setText( newText ); return; } - // 2. if Text and InPlace, the inplace text - if ( m_annot->subType() == Okular::Annotation::AText ) + + // Handle special cases + switch ( m_annot->subType() ) { - Okular::TextAnnotation * txtann = static_cast< Okular::TextAnnotation * >( m_annot ); - if ( txtann->textType() == Okular::TextAnnotation::InPlace ) + // 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: { - txtann->setInplaceText( newText ); - return; + Okular::LineAnnotation * lineann = static_cast< Okular::LineAnnotation * >( m_annot ); + if ( lineann->showCaption() ) + appearanceChanged = true; + break; } + default: + break; } - // 3. contents + // Set contents m_annot->setContents( newText ); + // Tell the document + m_document->modifyPageAnnotation( m_page, m_annot, appearanceChanged ); + emit containsLatex( GuiUtils::LatexRenderer::mightContainLatex( newText ) ); }