From 07c57bb2ab2b82a04b0f89090cfff4d47aaa4e77 Mon Sep 17 00:00:00 2001 From: Fabio D'Urso Date: Fri, 13 Apr 2012 18:41:29 +0200 Subject: [PATCH 01/18] Dead code removed (PagePrivate::modifyAnnotation) The first if ("modified already") is always taken --- core/document.cpp | 1 - core/page.cpp | 28 ---------------------------- core/page_p.h | 7 ------- 3 files changed, 36 deletions(-) diff --git a/core/document.cpp b/core/document.cpp index 0ebc34edb..7c511541c 100644 --- a/core/document.cpp +++ b/core/document.cpp @@ -2356,7 +2356,6 @@ void Document::modifyPageAnnotation( int page, Annotation * newannotation ) if ( !d->m_generator || !kp ) return; - kp->d->modifyAnnotation( newannotation ); // notify observers about the change foreachObserver( notifyPageChanged( page, DocumentObserver::Annotations ) ); } diff --git a/core/page.cpp b/core/page.cpp index 9f7400da7..3e4c07cc7 100644 --- a/core/page.cpp +++ b/core/page.cpp @@ -596,34 +596,6 @@ void Page::addAnnotation( Annotation * annotation ) m_rects.append( rect ); } -void PagePrivate::modifyAnnotation(Annotation * newannotation ) -{ - if(!newannotation) - return; - - QLinkedList< Annotation * >::iterator aIt = m_page->m_annotations.begin(), aEnd = m_page->m_annotations.end(); - for ( ; aIt != aEnd; ++aIt ) - { - if((*aIt)==newannotation) - return; //modified already - if((*aIt) && (*aIt)->uniqueName()==newannotation->uniqueName()) - { - int rectfound = false; - QLinkedList< ObjectRect * >::iterator it = m_page->m_rects.begin(), end = m_page->m_rects.end(); - for ( ; it != end && !rectfound; ++it ) - if ( ( (*it)->objectType() == ObjectRect::OAnnotation ) && ( (*it)->object() == (*aIt) ) ) - { - delete *it; - *it = new AnnotationObjectRect( newannotation ); - rectfound = true; - } - delete *aIt; - *aIt = newannotation; - break; - } - } -} - bool Page::removeAnnotation( Annotation * annotation ) { if ( !annotation || ( annotation->flags() & Annotation::DenyDelete ) ) diff --git a/core/page_p.h b/core/page_p.h index 72e330e53..38caeabe4 100644 --- a/core/page_p.h +++ b/core/page_p.h @@ -66,13 +66,6 @@ class PagePrivate */ void saveLocalContents( QDomNode & parentNode, QDomDocument & document, PageItems what = AllPageItems ) const; - /** - * Modifies an existing annotation by replacing it with a new @p annotation. - * - * The unique name is used to find the old annotation. - */ - void modifyAnnotation( Annotation * annotation ); - /** * Rotates the image and object rects of the page to the given @p orientation. */ From ec9f068d770b536f85d8322b672de2e97fe81835 Mon Sep 17 00:00:00 2001 From: Fabio D'Urso Date: Mon, 14 May 2012 00:50:41 +0200 Subject: [PATCH 02/18] 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: { - txtann->setInplaceText( newText ); - return; + 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; } - // 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 ) ); } From f6fa2a5614db0cc542e466f2289c92623f808e56 Mon Sep 17 00:00:00 2001 From: Fabio D'Urso Date: Fri, 9 Mar 2012 14:59:24 +0100 Subject: [PATCH 03/18] Disable GUI operations on certain types of annotations Modification and removal of *external* annotations are disabled by this patch. Note that this change doesn't remove any functionality, because they have never been implemented (AnnotationProxy is defined by the previous patch). The #if0'd blocks will be enabled by a future patch that provides fallback behavior for generators that don't support saving changes. --- core/annotations.cpp | 7 ++-- core/document.cpp | 55 +++++++++++++++++++++++++++++++ core/document.h | 7 ++++ core/document_p.h | 2 ++ ui/annotationpopup.cpp | 7 ++-- ui/annotationpropertiesdialog.cpp | 2 +- ui/annotwindow.cpp | 5 +++ 7 files changed, 79 insertions(+), 6 deletions(-) diff --git a/core/annotations.cpp b/core/annotations.cpp index adb634d27..6842d9878 100644 --- a/core/annotations.cpp +++ b/core/annotations.cpp @@ -16,6 +16,7 @@ // local includes #include "document.h" +#include "document_p.h" #include "movie.h" #include "page_p.h" #include "sound.h" @@ -735,9 +736,9 @@ void Annotation::setDisposeDataFunction( DisposeDataFunction func ) bool Annotation::canBeMoved() const { Q_D( const Annotation ); - // for now, it is pointless moving external annotations - // as we cannot change them anyway - if ( d->m_flags & External ) + + // Don't move annotations if they cannot be modified + if ( !d->m_page || !d->m_page->m_doc->m_parent->canModifyPageAnnotation(this) ) return false; // highlight "requires" to be "bounded" to text, and that's tricky for now diff --git a/core/document.cpp b/core/document.cpp index fe80cffaf..b2ecdf9e1 100644 --- a/core/document.cpp +++ b/core/document.cpp @@ -2360,6 +2360,31 @@ void Document::addPageAnnotation( int page, Annotation * annotation ) } } +bool Document::canModifyPageAnnotation( const Annotation * annotation ) const +{ + if ( !annotation || ( annotation->flags() & Annotation::DenyWrite ) ) + return false; + + if ( !isAllowed(Okular::AllowNotes) ) + return false; + + if ( ( annotation->flags() & Annotation::External ) && !d->canModifyExternalAnnotations() ) + 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::modifyPageAnnotation( int page, Annotation * annotation ) { modifyPageAnnotation( page, annotation, true ); @@ -2394,6 +2419,9 @@ bool Document::canRemovePageAnnotation( const Annotation * annotation ) const if ( !annotation || ( annotation->flags() & Annotation::DenyDelete ) ) return false; + if ( ( annotation->flags() & Annotation::External ) && !d->canRemoveExternalAnnotations() ) + return false; + switch ( annotation->subType() ) { case Annotation::AText: @@ -2490,6 +2518,33 @@ void Document::removePageAnnotations( int page, const QList< Annotation * > &ann } } +bool DocumentPrivate::canModifyExternalAnnotations() const +{ + /* To be enabled once external annotations are implemented independently + * of save/restoreLocalContents. At the moment, we support editing internal + * annotations only. */ +#if 0 + Okular::SaveInterface * iface = qobject_cast< Okular::SaveInterface * >( d->m_generator ); + + if ( iface && iface->annotationProxy() && + iface->annotationProxy()->supports(AnnotationProxy::Modification) ) + return true; +#endif + return false; +} + +bool DocumentPrivate::canRemoveExternalAnnotations() const +{ +#if 0 /* See canModifyExternalAnnotations */ + Okular::SaveInterface * iface = qobject_cast< Okular::SaveInterface * >( d->m_generator ); + + if ( iface && iface->annotationProxy() && + iface->annotationProxy()->supports(AnnotationProxy::Removal) ) + return true; +#endif + return false; +} + void Document::setPageTextSelection( int page, RegularAreaRect * rect, const QColor & color ) { Page * kp = d->m_pagesVector[ page ]; diff --git a/core/document.h b/core/document.h index 9ff32b641..cd36ae7f1 100644 --- a/core/document.h +++ b/core/document.h @@ -367,6 +367,13 @@ class OKULAR_EXPORT Document : public QObject */ void addPageAnnotation( int page, Annotation *annotation ); + /** + * Tests if the @p annotation can be modified + * + * @since 0.15 (KDE 4.9) + */ + bool canModifyPageAnnotation( const Annotation * annotation ) const; + /** * Modifies the given @p annotation on the given @p page. * diff --git a/core/document_p.h b/core/document_p.h index 4f78d9a14..8ead67f18 100644 --- a/core/document_p.h +++ b/core/document_p.h @@ -114,6 +114,8 @@ class DocumentPrivate bool openDocumentInternal( const KService::Ptr& offer, bool isstdin, const QString& docFile, const QByteArray& filedata ); bool savePageDocumentInfo( KTemporaryFile *infoFile, int what ) const; DocumentViewport nextDocumentViewport() const; + bool canModifyExternalAnnotations() const; + bool canRemoveExternalAnnotations() const; // private slots void saveDocumentInfo() const; diff --git a/ui/annotationpopup.cpp b/ui/annotationpopup.cpp index e665bdcd7..1c3a71714 100644 --- a/ui/annotationpopup.cpp +++ b/ui/annotationpopup.cpp @@ -53,8 +53,11 @@ void AnnotationPopup::exec( const QPoint &point ) deleteNote->setEnabled( mDocument->isAllowed( Okular::AllowNotes ) ); const AnnotPagePair &firstAnnotPagePair = mAnnotations.at(0); - if ( onlyOne && firstAnnotPagePair.annotation->flags() & Okular::Annotation::DenyDelete ) - deleteNote->setEnabled( false ); + foreach ( const AnnotPagePair& pair, mAnnotations ) + { + if ( !mDocument->canRemovePageAnnotation(pair.annotation) ) + deleteNote->setEnabled( false ); + } showProperties = menu.addAction( KIcon( "configure" ), i18n( "&Properties" ) ); showProperties->setEnabled( onlyOne ); diff --git a/ui/annotationpropertiesdialog.cpp b/ui/annotationpropertiesdialog.cpp index 06f92769d..4b0225803 100644 --- a/ui/annotationpropertiesdialog.cpp +++ b/ui/annotationpropertiesdialog.cpp @@ -34,7 +34,7 @@ AnnotsPropertiesDialog::AnnotsPropertiesDialog( QWidget *parent, Okular::Documen { setFaceType( Tabbed ); m_annot=ann; - bool canEditAnnotations = !(ann->flags() & Okular::Annotation::External) && m_document->isAllowed( Okular::AllowNotes ); + const bool canEditAnnotations = m_document->canModifyPageAnnotation( ann ); setCaptionTextbyAnnotType(); if ( canEditAnnotations ) { diff --git a/ui/annotwindow.cpp b/ui/annotwindow.cpp index fce1667be..dc84b6f7e 100644 --- a/ui/annotwindow.cpp +++ b/ui/annotwindow.cpp @@ -190,6 +190,8 @@ AnnotWindow::AnnotWindow( QWidget * parent, Okular::Annotation * annot, Okular:: setFrameStyle( Panel | Raised ); setAttribute( Qt::WA_DeleteOnClose ); + const bool canEditAnnotation = m_document->canModifyPageAnnotation( annot ); + textEdit = new KTextEdit( this ); textEdit->setAcceptRichText( false ); textEdit->setPlainText( GuiUtils::contents( m_annot ) ); @@ -197,6 +199,9 @@ AnnotWindow::AnnotWindow( QWidget * parent, Okular::Annotation * annot, Okular:: connect(textEdit,SIGNAL(textChanged()), this,SLOT(slotsaveWindowText())); + if (!canEditAnnotation) + textEdit->setReadOnly(true); + m_latexRenderer = new GuiUtils::LatexRenderer(); emit containsLatex( GuiUtils::LatexRenderer::mightContainLatex( GuiUtils::contents( m_annot ) ) ); From 249bea5985021454a6cd5244575150aa78ad79a3 Mon Sep 17 00:00:00 2001 From: Fabio D'Urso Date: Sun, 6 May 2012 19:54:42 +0200 Subject: [PATCH 04/18] Re-add restored annotations via Document so that AnnotationProxy gets notified Previously, restored annotations followed a shorter path that bypassed AnnotationProxy --- core/page.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/core/page.cpp b/core/page.cpp index e645b64a1..82deb69d4 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.h" #include "document_p.h" #include "form.h" #include "form_p.h" @@ -753,9 +754,7 @@ void PagePrivate::restoreLocalContents( const QDomNode & pageNode ) // append annotation to the list or show warning if ( annotation ) { - annotation->d_ptr->m_page = this; - m_page->m_annotations.append( annotation ); - m_page->m_rects.append( new AnnotationObjectRect( annotation ) ); + m_doc->m_parent->addPageAnnotation(m_number, annotation); int pos = annotation->uniqueName().lastIndexOf("-"); if(pos != -1) { From 68127e00ea6baa6ba1fba98bfee81f17d062f04e Mon Sep 17 00:00:00 2001 From: Fabio D'Urso Date: Mon, 7 May 2012 11:47:42 +0200 Subject: [PATCH 05/18] Do not store flag Annotation::ExternallyDrawn when exporting to DOM It's an implementation detail --- core/annotations.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/annotations.cpp b/core/annotations.cpp index 6842d9878..8649f6662 100644 --- a/core/annotations.cpp +++ b/core/annotations.cpp @@ -768,8 +768,8 @@ void Annotation::store( QDomNode & annNode, QDomDocument & document ) const e.setAttribute( "creationDate", d->m_creationDate.toString(Qt::ISODate) ); // store -other- attributes - if ( d->m_flags ) - e.setAttribute( "flags", d->m_flags ); + if ( d->m_flags ) // Strip ExternallyDrawn flag because it's an implementation detail + e.setAttribute( "flags", d->m_flags & ~Annotation::ExternallyDrawn ); if ( d->m_style.color().isValid() ) e.setAttribute( "color", d->m_style.color().name() ); if ( d->m_style.opacity() != 1.0 ) From 6c296b916b506b4d72be0df5112469a33e5912e0 Mon Sep 17 00:00:00 2001 From: Fabio D'Urso Date: Fri, 13 Apr 2012 20:02:34 +0200 Subject: [PATCH 06/18] Use UUIDs intead of a (broken) counter to generate annotations' unique names The previous counter-based approach didn't take into account existing names used by external annotations and names used in other pages. Instead of creating a document-global table of used names, I used random UUIDs as a source of unique names. --- core/page.cpp | 19 ++++--------------- core/page_p.h | 1 - 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/core/page.cpp b/core/page.cpp index 82deb69d4..641e8cfe3 100644 --- a/core/page.cpp +++ b/core/page.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -61,7 +62,7 @@ static void deleteObjectRects( QLinkedList< ObjectRect * >& rects, const QSetuniqueName().isEmpty()) { - QString uniqueName = "okular-"; - uniqueName += ( QString::number(d->m_number) + '-' + QString::number(++(d->m_maxuniqueNum)) ); - - kDebug(OkularDebug).nospace() << "inc m_maxuniqueNum=" << d->m_maxuniqueNum; - + QString uniqueName = "okular-" + QUuid::createUuid().toString(); annotation->setUniqueName( uniqueName ); } annotation->d_ptr->m_page = d; @@ -755,14 +752,6 @@ void PagePrivate::restoreLocalContents( const QDomNode & pageNode ) if ( annotation ) { m_doc->m_parent->addPageAnnotation(m_number, annotation); - int pos = annotation->uniqueName().lastIndexOf("-"); - if(pos != -1) - { - int uniqID=annotation->uniqueName().right(annotation->uniqueName().length()-pos-1).toInt(); - if ( m_maxuniqueNum < uniqID ) - m_maxuniqueNum = uniqID; - } - kDebug(OkularDebug) << "restored annot:" << annotation->uniqueName(); } else diff --git a/core/page_p.h b/core/page_p.h index 38caeabe4..4cbfb193a 100644 --- a/core/page_p.h +++ b/core/page_p.h @@ -114,7 +114,6 @@ class PagePrivate DocumentPrivate *m_doc; NormalizedRect m_boundingBox; Rotation m_rotation; - int m_maxuniqueNum; TextPage * m_text; PageTransition * m_transition; From a76e328dd65b29fbdb83416c390440409e2ee0c0 Mon Sep 17 00:00:00 2001 From: Fabio D'Urso Date: Sat, 14 Apr 2012 00:29:51 +0200 Subject: [PATCH 07/18] poppler gen: Added checkbox to print/hide annotations in PDFOptionsPage --- generators/poppler/generator_pdf.cpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/generators/poppler/generator_pdf.cpp b/generators/poppler/generator_pdf.cpp index a27f9ffa7..acd6f7da2 100644 --- a/generators/poppler/generator_pdf.cpp +++ b/generators/poppler/generator_pdf.cpp @@ -72,11 +72,27 @@ class PDFOptionsPage : public QWidget { setWindowTitle( i18n( "PDF Options" ) ); QVBoxLayout *layout = new QVBoxLayout(this); + m_printAnnots = new QCheckBox(i18n("Print annotations"), this); + m_printAnnots->setToolTip(i18n("Include annotations in the printed document")); + m_printAnnots->setWhatsThis(i18n("Includes annotations in the printed document. You can disable this if you want to print the original unannotated document.")); + layout->addWidget(m_printAnnots); m_forceRaster = new QCheckBox(i18n("Force rasterization"), this); m_forceRaster->setToolTip(i18n("Rasterize into an image before printing")); m_forceRaster->setWhatsThis(i18n("Forces the rasterization of each page into an image before printing it. This usually gives somewhat worse results, but is useful when printing documents that appear to print incorrectly.")); layout->addWidget(m_forceRaster); layout->addStretch(1); + + setPrintAnnots( true ); // Default value + } + + bool printAnnots() + { + return m_printAnnots->isChecked(); + } + + void setPrintAnnots( bool printAnnots ) + { + m_printAnnots->setChecked( printAnnots ); } bool printForceRaster() @@ -90,6 +106,7 @@ class PDFOptionsPage : public QWidget } private: + QCheckBox *m_printAnnots; QCheckBox *m_forceRaster; }; @@ -1026,9 +1043,11 @@ bool PDFGenerator::print( QPrinter& printer ) pstitle = document()->currentDocument().fileName(); } + bool printAnnots = true; bool forceRasterize = false; if ( pdfOptionsPage ) { + printAnnots = pdfOptionsPage->printAnnots(); forceRasterize = pdfOptionsPage->printForceRaster(); } @@ -1047,6 +1066,9 @@ bool PDFGenerator::print( QPrinter& printer ) psConverter->setForceRasterize(forceRasterize); psConverter->setTitle(pstitle); + if (!printAnnots) + psConverter->setPSOptions(psConverter->psOptions() | Poppler::PSConverter::HideAnnotations ); + userMutex()->lock(); if (psConverter->convert()) { From d06c17d45124628a33738bc74a08833c431bd7a4 Mon Sep 17 00:00:00 2001 From: Fabio D'Urso Date: Fri, 13 Apr 2012 21:37:30 +0200 Subject: [PATCH 08/18] poppler gen: Moved PDFDebug constant into PDFGenerator class --- generators/poppler/generator_pdf.cpp | 1 - generators/poppler/generator_pdf.h | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/generators/poppler/generator_pdf.cpp b/generators/poppler/generator_pdf.cpp index acd6f7da2..84855edbd 100644 --- a/generators/poppler/generator_pdf.cpp +++ b/generators/poppler/generator_pdf.cpp @@ -61,7 +61,6 @@ Q_DECLARE_METATYPE(Poppler::FontInfo) Q_DECLARE_METATYPE(const Poppler::LinkMovie*) #endif -static const int PDFDebug = 4710; static const int defaultPageWidth = 595; static const int defaultPageHeight = 842; diff --git a/generators/poppler/generator_pdf.h b/generators/poppler/generator_pdf.h index 0e6ebf038..b75be74e6 100644 --- a/generators/poppler/generator_pdf.h +++ b/generators/poppler/generator_pdf.h @@ -56,6 +56,8 @@ class PDFGenerator : public Okular::Generator, public Okular::ConfigInterface, p PDFGenerator( QObject *parent, const QVariantList &args ); virtual ~PDFGenerator(); + static const int PDFDebug = 4710; + // [INHERITED] load a document and fill up the pagesVector bool loadDocument( const QString & fileName, QVector & pagesVector ); bool loadDocumentFromData( const QByteArray & fileData, QVector & pagesVector ); From 65d59f2a3e47aa61467adb54d2edb5c8d41a1e92 Mon Sep 17 00:00:00 2001 From: Fabio D'Urso Date: Fri, 13 Apr 2012 21:33:25 +0200 Subject: [PATCH 09/18] poppler gen: AnnotationProxy implementation --- generators/poppler/annots.cpp | 239 ++++++++++++++++++++++++++- generators/poppler/annots.h | 34 ++++ generators/poppler/generator_pdf.cpp | 12 +- generators/poppler/generator_pdf.h | 2 + 4 files changed, 282 insertions(+), 5 deletions(-) create mode 100644 generators/poppler/annots.h diff --git a/generators/poppler/annots.cpp b/generators/poppler/annots.cpp index f99a316f8..9be6242b5 100644 --- a/generators/poppler/annots.cpp +++ b/generators/poppler/annots.cpp @@ -14,7 +14,10 @@ #include #include +#include +#include "annots.h" +#include "generator_pdf.h" #include "popplerembeddedfile.h" #include "config-okular-poppler.h" @@ -32,11 +35,209 @@ static void disposeAnnotation( const Okular::Annotation *ann ) delete popplerAnn; } +static QPointF normPointToPointF( const Okular::NormalizedPoint& pt ) +{ + return QPointF(pt.x, pt.y); +} + +static QRectF normRectToRectF( const Okular::NormalizedRect& rect ) +{ + return QRectF( QPointF(rect.left, rect.top), QPointF(rect.right, rect.bottom) ); +} + +//BEGIN PopplerAnnotationProxy implementation +PopplerAnnotationProxy::PopplerAnnotationProxy( Poppler::Document *doc ) + : ppl_doc ( doc ) +{ +} + +PopplerAnnotationProxy::~PopplerAnnotationProxy() +{ +} + +bool PopplerAnnotationProxy::supports( Capability cap ) const +{ + switch ( cap ) + { + case Addition: + case Modification: + case Removal: + return true; + default: + return false; + } +} + +void PopplerAnnotationProxy::notifyAddition( Okular::Annotation *okl_ann, int page ) +{ + // Export annotation to DOM + QDomDocument doc; + QDomElement dom_ann = doc.createElement( "root" ); + Okular::AnnotationUtils::storeAnnotation( okl_ann, dom_ann, doc ); + + // Create poppler annotation + Poppler::Annotation *ppl_ann = Poppler::AnnotationUtils::createAnnotation( dom_ann ); + + // Poppler doesn't render StampAnnotations yet + if ( ppl_ann->subType() != Poppler::Annotation::AStamp ) + okl_ann->setFlags( okl_ann->flags() | Okular::Annotation::ExternallyDrawn ); + + // Poppler stores highlight points in swapped order + if ( ppl_ann->subType() == Poppler::Annotation::AHighlight ) + { + Poppler::HighlightAnnotation * hlann = static_cast( ppl_ann ); + QList quads = hlann->highlightQuads(); + QMutableListIterator it( quads ); + while ( it.hasNext() ) + { + Poppler::HighlightAnnotation::Quad &q = it.next(); + QPointF t; + t = q.points[3]; + q.points[3] = q.points[0]; + q.points[0] = t; + t = q.points[2]; + q.points[2] = q.points[1]; + q.points[1] = t; + } + hlann->setHighlightQuads( quads ); + } + + // Bind poppler object to page + Poppler::Page *ppl_page = ppl_doc->page( page ); + ppl_page->addAnnotation( ppl_ann ); + delete ppl_page; + + // Set pointer to poppler annotation as native Id + okl_ann->setNativeId( qVariantFromValue( ppl_ann ) ); + okl_ann->setDisposeDataFunction( disposeAnnotation ); + + kDebug(PDFGenerator::PDFDebug) << okl_ann->uniqueName(); +} + +void PopplerAnnotationProxy::notifyModification( const Okular::Annotation *okl_ann, int page, bool appearanceChanged ) +{ + Q_UNUSED( page ); + Q_UNUSED( appearanceChanged ); + + Poppler::Annotation *ppl_ann = qvariant_cast( okl_ann->nativeId() ); + + if ( !ppl_ann ) // Ignore non-native annotations + return; + + // Set basic properties + ppl_ann->setBoundary(normRectToRectF( okl_ann->boundingRectangle() )); + ppl_ann->setAuthor( okl_ann->author() ); + ppl_ann->setContents( okl_ann->contents() ); + + // Set style + Poppler::Annotation::Style s; + s.setColor( okl_ann->style().color() ); + s.setWidth( okl_ann->style().width() ); + s.setOpacity( okl_ann->style().opacity() ); + ppl_ann->setStyle( s ); + + // Set type-specific properties (if any) + switch ( ppl_ann->subType() ) + { + case Poppler::Annotation::AText: + { + const Okular::TextAnnotation * okl_txtann = static_cast(okl_ann); + Poppler::TextAnnotation * ppl_txtann = static_cast(ppl_ann); + ppl_txtann->setTextIcon( okl_txtann->textIcon() ); + ppl_txtann->setTextFont( okl_txtann->textFont() ); + ppl_txtann->setInplaceAlign( okl_txtann->inplaceAlignment() ); + if ( okl_txtann->textType() == Okular::TextAnnotation::InPlace ) + ppl_txtann->setContents( okl_txtann->inplaceText() ); // overrides contents + ppl_txtann->setCalloutPoints( QVector() ); + ppl_txtann->setInplaceIntent( (Poppler::TextAnnotation::InplaceIntent)okl_txtann->inplaceIntent() ); + break; + } + case Poppler::Annotation::ALine: + { + const Okular::LineAnnotation * okl_lineann = static_cast(okl_ann); + Poppler::LineAnnotation * ppl_lineann = static_cast(ppl_ann); + QLinkedList points; + foreach ( const Okular::NormalizedPoint &p, okl_lineann->linePoints() ) + points.append(normPointToPointF( p )); + ppl_lineann->setLinePoints( points ); + ppl_lineann->setLineStartStyle( (Poppler::LineAnnotation::TermStyle)okl_lineann->lineStartStyle() ); + ppl_lineann->setLineEndStyle( (Poppler::LineAnnotation::TermStyle)okl_lineann->lineEndStyle() ); + ppl_lineann->setLineClosed( okl_lineann->lineClosed() ); + ppl_lineann->setLineInnerColor( okl_lineann->lineInnerColor() ); + ppl_lineann->setLineLeadingForwardPoint( okl_lineann->lineLeadingForwardPoint() ); + ppl_lineann->setLineLeadingBackPoint( okl_lineann->lineLeadingBackwardPoint() ); + ppl_lineann->setLineShowCaption( okl_lineann->showCaption() ); + ppl_lineann->setLineIntent( (Poppler::LineAnnotation::LineIntent)okl_lineann->lineIntent() ); + break; + } + case Poppler::Annotation::AGeom: + { + const Okular::GeomAnnotation * okl_geomann = static_cast(okl_ann); + Poppler::GeomAnnotation * ppl_geomann = static_cast(ppl_ann); + ppl_geomann->setGeomType( (Poppler::GeomAnnotation::GeomType)okl_geomann->geometricalType() ); + ppl_geomann->setGeomInnerColor( okl_geomann->geometricalInnerColor() ); + break; + } + case Poppler::Annotation::AHighlight: + { + const Okular::HighlightAnnotation * okl_hlann = static_cast(okl_ann); + Poppler::HighlightAnnotation * ppl_hlann = static_cast(ppl_ann); + ppl_hlann->setHighlightType( (Poppler::HighlightAnnotation::HighlightType)okl_hlann->highlightType() ); + break; + } + case Poppler::Annotation::AStamp: + { + const Okular::StampAnnotation * okl_stampann = static_cast(okl_ann); + Poppler::StampAnnotation * ppl_stampann = static_cast(ppl_ann); + ppl_stampann->setStampIconName( okl_stampann->stampIconName() ); + break; + } + case Poppler::Annotation::AInk: + { + const Okular::InkAnnotation * okl_inkann = static_cast(okl_ann); + Poppler::InkAnnotation * ppl_inkann = static_cast(ppl_ann); + QList< QLinkedList > paths; + foreach ( const QLinkedList &path, okl_inkann->inkPaths() ) + { + QLinkedList points; + foreach ( const Okular::NormalizedPoint &p, path ) + points.append(normPointToPointF( p )); + paths.append( points ); + } + ppl_inkann->setInkPaths( paths ); + break; + } + default: + kDebug() << "Type-specific property modification is not implemented for this annotation type"; + break; + } + + kDebug(PDFGenerator::PDFDebug) << okl_ann->uniqueName(); +} + +void PopplerAnnotationProxy::notifyRemoval( Okular::Annotation *okl_ann, int page ) +{ + Poppler::Annotation *ppl_ann = qvariant_cast( okl_ann->nativeId() ); + + if ( !ppl_ann ) // Ignore non-native annotations + return; + + Poppler::Page *ppl_page = ppl_doc->page( page ); + ppl_page->removeAnnotation( ppl_ann ); // Also destroys ppl_ann + delete ppl_page; + + okl_ann->setNativeId( qVariantFromValue(0) ); // So that we don't double-free in disposeAnnotation + + kDebug(PDFGenerator::PDFDebug) << okl_ann->uniqueName(); +} +//END PopplerAnnotationProxy implementation + Okular::Annotation* createAnnotationFromPopplerAnnotation( Poppler::Annotation *ann, bool *doDelete ) { Okular::Annotation *annotation = 0; *doDelete = true; bool tieToOkularAnn = false; + bool externallyDrawn = false; switch ( ann->subType() ) { case Poppler::Annotation::AFileAttachment: @@ -87,6 +288,21 @@ Okular::Annotation* createAnnotationFromPopplerAnnotation( Poppler::Annotation * break; } #endif + case Poppler::Annotation::AText: + case Poppler::Annotation::ALine: + case Poppler::Annotation::AGeom: + case Poppler::Annotation::AHighlight: + case Poppler::Annotation::AInk: + { + externallyDrawn = true; + /* fallback */ + } + case Poppler::Annotation::AStamp: + { + tieToOkularAnn = true; + *doDelete = false; + /* fallback */ + } default: { // this is uber ugly but i don't know a better way to do it without introducing a poppler::annotation dependency on core @@ -95,7 +311,7 @@ Okular::Annotation* createAnnotationFromPopplerAnnotation( Poppler::Annotation * doc.appendChild( root ); Poppler::AnnotationUtils::storeAnnotation( ann, root, doc ); annotation = Okular::AnnotationUtils::createAnnotation( root ); - return annotation; + break; } } if ( annotation ) @@ -107,6 +323,27 @@ Okular::Annotation* createAnnotationFromPopplerAnnotation( Poppler::Annotation * annotation->setCreationDate( ann->creationDate() ); annotation->setFlags( ann->flags() ); annotation->setBoundingRectangle( Okular::NormalizedRect::fromQRectF( ann->boundary() ) ); + + if (externallyDrawn) + annotation->setFlags( annotation->flags() | Okular::Annotation::ExternallyDrawn ); + + // Poppler stores highlight points in swapped order + if ( annotation->subType() == Okular::Annotation::AHighlight ) + { + Okular::HighlightAnnotation * hlann = static_cast( annotation ); + QList &quads = hlann->highlightQuads(); + for (QList::iterator it = quads.begin(); it != quads.end(); ++it) + { + Okular::NormalizedPoint t; + t = it->point( 3 ); + it->setPoint( it->point(0), 3 ); + it->setPoint( t, 0 ); + t = it->point( 2 ); + it->setPoint( it->point(1), 2 ); + it->setPoint( t, 1 ); + } + } + // TODO clone style // TODO clone window // TODO clone revisions diff --git a/generators/poppler/annots.h b/generators/poppler/annots.h new file mode 100644 index 000000000..d71e7e537 --- /dev/null +++ b/generators/poppler/annots.h @@ -0,0 +1,34 @@ +/*************************************************************************** + * Copyright (C) 2012 by Fabio D'Urso * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + ***************************************************************************/ + +#ifndef _OKULAR_GENERATOR_PDF_ANNOTS_H_ +#define _OKULAR_GENERATOR_PDF_ANNOTS_H_ + +#include +#include + +#include "core/annotations.h" + +extern Okular::Annotation* createAnnotationFromPopplerAnnotation( Poppler::Annotation *ann, bool * doDelete ); + +class PopplerAnnotationProxy : public Okular::AnnotationProxy +{ + public: + PopplerAnnotationProxy( Poppler::Document *doc ); + ~PopplerAnnotationProxy(); + + bool supports( Capability capability ) const; + void notifyAddition( Okular::Annotation *annotation, int page ); + void notifyModification( const Okular::Annotation *annotation, int page, bool appearanceChanged ); + void notifyRemoval( Okular::Annotation *annotation, int page ); + private: + Poppler::Document *ppl_doc; +}; + +#endif diff --git a/generators/poppler/generator_pdf.cpp b/generators/poppler/generator_pdf.cpp index 84855edbd..b89bfb34d 100644 --- a/generators/poppler/generator_pdf.cpp +++ b/generators/poppler/generator_pdf.cpp @@ -52,6 +52,7 @@ # include #endif +#include "annots.h" #include "formfields.h" #include "popplerembeddedfile.h" @@ -333,8 +334,6 @@ static QLinkedList generateLinks( const QList & pagesVector, const QString &wal // update the configuration reparseConfig(); + // create annotation proxy + annotProxy = new PopplerAnnotationProxy( pdfdoc ); + // the file has been loaded correctly return true; } @@ -537,6 +539,8 @@ bool PDFGenerator::doCloseDocument() { // remove internal objects userMutex()->lock(); + delete annotProxy; + annotProxy = 0; delete pdfdoc; pdfdoc = 0; userMutex()->unlock(); @@ -1787,7 +1791,7 @@ bool PDFGenerator::save( const QString &fileName, SaveOptions options, QString * Okular::AnnotationProxy* PDFGenerator::annotationProxy() const { - return 0; // Not supported + return annotProxy; } #include "generator_pdf.moc" diff --git a/generators/poppler/generator_pdf.h b/generators/poppler/generator_pdf.h index b75be74e6..bea65d9ee 100644 --- a/generators/poppler/generator_pdf.h +++ b/generators/poppler/generator_pdf.h @@ -32,6 +32,7 @@ class SourceReference; } class PDFOptionsPage; +class PopplerAnnotationProxy; /** * @short A generator that builds contents from a PDF document. @@ -145,6 +146,7 @@ class PDFGenerator : public Okular::Generator, public Okular::ConfigInterface, p int nextFontPage; double dpiX; double dpiY; + PopplerAnnotationProxy *annotProxy; QHash annotationsHash; QBitArray rectsGenerated; From 80c26f5bb923682cb4b68b7d40f83d754e2a1396 Mon Sep 17 00:00:00 2001 From: Fabio D'Urso Date: Sun, 13 May 2012 17:06:59 +0200 Subject: [PATCH 10/18] poppler gen: Put new annotation features between #ifdef HAVE_POPPLER_0_20 --- generators/poppler/annots.cpp | 10 +++++++++- generators/poppler/annots.h | 1 + generators/poppler/generator_pdf.cpp | 5 +++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/generators/poppler/annots.cpp b/generators/poppler/annots.cpp index 9be6242b5..1880af500 100644 --- a/generators/poppler/annots.cpp +++ b/generators/poppler/annots.cpp @@ -59,10 +59,12 @@ bool PopplerAnnotationProxy::supports( Capability cap ) const { switch ( cap ) { +#ifdef HAVE_POPPLER_0_20 case Addition: case Modification: case Removal: return true; +#endif default: return false; } @@ -70,6 +72,7 @@ bool PopplerAnnotationProxy::supports( Capability cap ) const void PopplerAnnotationProxy::notifyAddition( Okular::Annotation *okl_ann, int page ) { +#ifdef HAVE_POPPLER_0_20 // Export annotation to DOM QDomDocument doc; QDomElement dom_ann = doc.createElement( "root" ); @@ -112,10 +115,12 @@ void PopplerAnnotationProxy::notifyAddition( Okular::Annotation *okl_ann, int pa okl_ann->setDisposeDataFunction( disposeAnnotation ); kDebug(PDFGenerator::PDFDebug) << okl_ann->uniqueName(); +#endif } void PopplerAnnotationProxy::notifyModification( const Okular::Annotation *okl_ann, int page, bool appearanceChanged ) { +#ifdef HAVE_POPPLER_0_20 Q_UNUSED( page ); Q_UNUSED( appearanceChanged ); @@ -213,10 +218,12 @@ void PopplerAnnotationProxy::notifyModification( const Okular::Annotation *okl_a } kDebug(PDFGenerator::PDFDebug) << okl_ann->uniqueName(); +#endif } void PopplerAnnotationProxy::notifyRemoval( Okular::Annotation *okl_ann, int page ) { +#ifdef HAVE_POPPLER_0_20 Poppler::Annotation *ppl_ann = qvariant_cast( okl_ann->nativeId() ); if ( !ppl_ann ) // Ignore non-native annotations @@ -229,6 +236,7 @@ void PopplerAnnotationProxy::notifyRemoval( Okular::Annotation *okl_ann, int pag okl_ann->setNativeId( qVariantFromValue(0) ); // So that we don't double-free in disposeAnnotation kDebug(PDFGenerator::PDFDebug) << okl_ann->uniqueName(); +#endif } //END PopplerAnnotationProxy implementation @@ -287,7 +295,6 @@ Okular::Annotation* createAnnotationFromPopplerAnnotation( Poppler::Annotation * break; } -#endif case Poppler::Annotation::AText: case Poppler::Annotation::ALine: case Poppler::Annotation::AGeom: @@ -303,6 +310,7 @@ Okular::Annotation* createAnnotationFromPopplerAnnotation( Poppler::Annotation * *doDelete = false; /* fallback */ } +#endif default: { // this is uber ugly but i don't know a better way to do it without introducing a poppler::annotation dependency on core diff --git a/generators/poppler/annots.h b/generators/poppler/annots.h index d71e7e537..ac0630232 100644 --- a/generators/poppler/annots.h +++ b/generators/poppler/annots.h @@ -14,6 +14,7 @@ #include #include "core/annotations.h" +#include "config-okular-poppler.h" extern Okular::Annotation* createAnnotationFromPopplerAnnotation( Poppler::Annotation *ann, bool * doDelete ); diff --git a/generators/poppler/generator_pdf.cpp b/generators/poppler/generator_pdf.cpp index b89bfb34d..e7e678065 100644 --- a/generators/poppler/generator_pdf.cpp +++ b/generators/poppler/generator_pdf.cpp @@ -82,6 +82,9 @@ class PDFOptionsPage : public QWidget layout->addWidget(m_forceRaster); layout->addStretch(1); +#ifndef HAVE_POPPLER_0_20 + m_printAnnots->setVisible( false ); +#endif setPrintAnnots( true ); // Default value } @@ -1069,8 +1072,10 @@ bool PDFGenerator::print( QPrinter& printer ) psConverter->setForceRasterize(forceRasterize); psConverter->setTitle(pstitle); +#ifdef HAVE_POPPLER_0_20 if (!printAnnots) psConverter->setPSOptions(psConverter->psOptions() | Poppler::PSConverter::HideAnnotations ); +#endif userMutex()->lock(); if (psConverter->convert()) From 157638f2f989ee091413be5694061b42c2f3042d Mon Sep 17 00:00:00 2001 From: Fabio D'Urso Date: Sun, 13 May 2012 20:10:55 +0200 Subject: [PATCH 11/18] Added Annotation::BeingMoved flag to avoid refreshing pixmaps while moving annotations --- core/annotations.cpp | 4 ++-- core/annotations.h | 3 ++- core/document.cpp | 14 ++++++++++++++ core/document_p.h | 5 ++++- generators/poppler/annots.cpp | 20 ++++++++++++++++++++ ui/pageview.cpp | 20 +++++++++++++++----- 6 files changed, 57 insertions(+), 9 deletions(-) diff --git a/core/annotations.cpp b/core/annotations.cpp index 8649f6662..79d9b180f 100644 --- a/core/annotations.cpp +++ b/core/annotations.cpp @@ -768,8 +768,8 @@ void Annotation::store( QDomNode & annNode, QDomDocument & document ) const e.setAttribute( "creationDate", d->m_creationDate.toString(Qt::ISODate) ); // store -other- attributes - if ( d->m_flags ) // Strip ExternallyDrawn flag because it's an implementation detail - e.setAttribute( "flags", d->m_flags & ~Annotation::ExternallyDrawn ); + if ( d->m_flags ) // Strip internal flags + e.setAttribute( "flags", d->m_flags & ~(External | ExternallyDrawn | BeingMoved) ); if ( d->m_style.color().isValid() ) e.setAttribute( "color", d->m_style.color().name() ); if ( d->m_style.opacity() != 1.0 ) diff --git a/core/annotations.h b/core/annotations.h index 4a84d8c96..23deaf3a9 100644 --- a/core/annotations.h +++ b/core/annotations.h @@ -128,7 +128,8 @@ class OKULAR_EXPORT Annotation DenyDelete = 32, ///< Cannot be deleted ToggleHidingOnMouse = 64, ///< Can be hidden/shown by mouse click External = 128, ///< Is stored external - ExternallyDrawn = 256 ///< Is drawn externally (eg the generator which povided it) @since 0.10 (KDE 4.4) + ExternallyDrawn = 256, ///< Is drawn externally (by the generator which provided it) @since 0.10 (KDE 4.4) + BeingMoved = 512 ///< Is being moved (mouse drag and drop). If ExternallyDrawn, the generator must not draw it @since 0.15 (KDE 4.9) }; /** diff --git a/core/document.cpp b/core/document.cpp index b2ecdf9e1..2fed9ae8b 100644 --- a/core/document.cpp +++ b/core/document.cpp @@ -2409,6 +2409,20 @@ void Document::modifyPageAnnotation( int page, Annotation * annotation, bool app if ( appearanceChanged && (annotation->flags() & Annotation::ExternallyDrawn) ) { + /* When an annotation is being moved, the generator will not render it. + * Therefore there's no need to refresh pixmaps after the first time */ + if ( annotation->flags() & Annotation::BeingMoved ) + { + if ( d->m_annotationBeingMoved ) + return; + else // First time: take note + d->m_annotationBeingMoved = true; + } + else + { + d->m_annotationBeingMoved = false; + } + // Redraw everything, including ExternallyDrawn annotations d->refreshPixmaps( page ); } diff --git a/core/document_p.h b/core/document_p.h index 8ead67f18..5c6dcf7ee 100644 --- a/core/document_p.h +++ b/core/document_p.h @@ -85,7 +85,8 @@ class DocumentPrivate m_scripter( 0 ), m_archiveData( 0 ), m_fontsCached( false ), - m_documentInfo( 0 ) + m_documentInfo( 0 ), + m_annotationBeingMoved( false ) { calculateMaxTextPages(); } @@ -229,6 +230,8 @@ class DocumentPrivate FontInfo::List m_fontsCache; QSet< View * > m_views; + + bool m_annotationBeingMoved; // is an annotation currently being moved? }; } diff --git a/generators/poppler/annots.cpp b/generators/poppler/annots.cpp index 1880af500..0489bcb22 100644 --- a/generators/poppler/annots.cpp +++ b/generators/poppler/annots.cpp @@ -45,6 +45,18 @@ static QRectF normRectToRectF( const Okular::NormalizedRect& rect ) return QRectF( QPointF(rect.left, rect.top), QPointF(rect.right, rect.bottom) ); } +// Poppler and Okular share the same flag values, but we don't want to export internal flags +static int maskExportedFlags(int flags) +{ + return flags & ( Okular::Annotation::Hidden | + Okular::Annotation::FixedSize | + Okular::Annotation::FixedRotation | + Okular::Annotation::DenyPrint | + Okular::Annotation::DenyWrite | + Okular::Annotation::DenyDelete | + Okular::Annotation::ToggleHidingOnMouse ); +} + //BEGIN PopplerAnnotationProxy implementation PopplerAnnotationProxy::PopplerAnnotationProxy( Poppler::Document *doc ) : ppl_doc ( doc ) @@ -129,10 +141,18 @@ void PopplerAnnotationProxy::notifyModification( const Okular::Annotation *okl_a if ( !ppl_ann ) // Ignore non-native annotations return; + if ( okl_ann->flags() & Okular::Annotation::BeingMoved ) + { + // Okular ui already renders the annotation on its own + ppl_ann->setFlags( Poppler::Annotation::Hidden ); + return; + } + // Set basic properties ppl_ann->setBoundary(normRectToRectF( okl_ann->boundingRectangle() )); ppl_ann->setAuthor( okl_ann->author() ); ppl_ann->setContents( okl_ann->contents() ); + ppl_ann->setFlags(maskExportedFlags( okl_ann->flags() )); // Set style Poppler::Annotation::Style s; diff --git a/ui/pageview.cpp b/ui/pageview.cpp index 4be5e6bcd..3f59d3087 100644 --- a/ui/pageview.cpp +++ b/ui/pageview.cpp @@ -132,6 +132,7 @@ public: bool mouseOnRect; Okular::Annotation * mouseAnn; QPoint mouseAnnPos; + int mouseAnnPageNum; // table selection QList tableSelectionCols; @@ -1793,7 +1794,7 @@ void PageView::mouseMoveEvent( QMouseEvent * e ) } d->mouseAnn->translate( Okular::NormalizedPoint( pf.x(), pf.y() ) ); d->mouseAnnPos = newpos; - d->document->modifyPageAnnotation( pageItem->pageNumber(), d->mouseAnn ); + d->document->modifyPageAnnotation( d->mouseAnnPageNum, d->mouseAnn ); } } // drag page @@ -1965,11 +1966,17 @@ void PageView::mousePressEvent( QMouseEvent * e ) if ( d->mouseAnn && !d->mouseAnn->canBeMoved() ) d->mouseAnn = 0; } - if ( !d->mouseAnn ) + if ( d->mouseAnn ) { - d->mouseGrabPos = d->mouseOnRect ? QPoint() : d->mousePressPos; - if ( !d->mouseOnRect ) - d->leftClickTimer.start( QApplication::doubleClickInterval() + 10 ); + d->mouseAnn->setFlags( d->mouseAnn->flags() | Okular::Annotation::BeingMoved ); + d->mouseAnnPageNum = pageItem->pageNumber(); + d->document->modifyPageAnnotation( d->mouseAnnPageNum, d->mouseAnn ); + } + else + { + d->mouseGrabPos = d->mouseOnRect ? QPoint() : d->mousePressPos; + if ( !d->mouseOnRect ) + d->leftClickTimer.start( QApplication::doubleClickInterval() + 10 ); } } else if ( rightButton ) @@ -2145,6 +2152,9 @@ void PageView::mouseReleaseEvent( QMouseEvent * e ) if ( d->mouseAnn ) { + // Just finished to move the annotation + d->mouseAnn->setFlags( d->mouseAnn->flags() & ~Okular::Annotation::BeingMoved ); + d->document->modifyPageAnnotation( d->mouseAnnPageNum, d->mouseAnn ); setCursor( Qt::ArrowCursor ); d->mouseAnn = 0; } From 29db8bafbc4b20e39d5cb0e9200fa1e9be135af1 Mon Sep 17 00:00:00 2001 From: Fabio D'Urso Date: Sun, 13 May 2012 20:25:31 +0200 Subject: [PATCH 12/18] Render ExternallyDrawn annotations in PagePainter if they are BeingMoved --- ui/pagepainter.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ui/pagepainter.cpp b/ui/pagepainter.cpp index ca55e8171..438d4abf7 100644 --- a/ui/pagepainter.cpp +++ b/ui/pagepainter.cpp @@ -169,7 +169,10 @@ void PagePainter::paintCroppedPageOnPainter( QPainter * destPainter, const Okula for ( ; aIt != aEnd; ++aIt ) { Okular::Annotation * ann = *aIt; - if ( ann->flags() & ( Okular::Annotation::Hidden | Okular::Annotation::ExternallyDrawn ) ) + int flags = ann->flags(); + if ( flags & Okular::Annotation::Hidden ) + continue; + if ( ( flags & Okular::Annotation::ExternallyDrawn) && !(flags & Okular::Annotation::BeingMoved) ) continue; bool intersects = ann->transformedBoundingRectangle().intersects( nXMin, nYMin, nXMax, nYMax ); From 1fdc4cbc7270116ac7cba2d8139d4d9f9bdec829 Mon Sep 17 00:00:00 2001 From: Fabio D'Urso Date: Sun, 13 May 2012 20:44:22 +0200 Subject: [PATCH 13/18] Only draw the bounding box of (ExternallyDrawn & BeingMoved) annotations --- ui/pagepainter.cpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/ui/pagepainter.cpp b/ui/pagepainter.cpp index 438d4abf7..537e5c501 100644 --- a/ui/pagepainter.cpp +++ b/ui/pagepainter.cpp @@ -117,6 +117,7 @@ void PagePainter::paintCroppedPageOnPainter( QPainter * destPainter, const Okula QList< QPair > * bufferedHighlights = 0; QList< Okular::Annotation * > * bufferedAnnotations = 0; QList< Okular::Annotation * > * unbufferedAnnotations = 0; + Okular::Annotation *boundingRectOnlyAnn = 0; // Paint the bounding rect of this annotation // fill up lists with visible annotation/highlight objects/text selections if ( canDrawHighlights || canDrawTextSelection || canDrawAnnotations ) { @@ -170,10 +171,18 @@ void PagePainter::paintCroppedPageOnPainter( QPainter * destPainter, const Okula { Okular::Annotation * ann = *aIt; int flags = ann->flags(); + if ( flags & Okular::Annotation::Hidden ) continue; - if ( ( flags & Okular::Annotation::ExternallyDrawn) && !(flags & Okular::Annotation::BeingMoved) ) + + if ( flags & Okular::Annotation::ExternallyDrawn ) + { + // ExternallyDrawn annots are never rendered by PagePainter. + // Just paint the boundingRect if the annot is BeingMoved + if ( flags & Okular::Annotation::BeingMoved ) + boundingRectOnlyAnn = ann; continue; + } bool intersects = ann->transformedBoundingRectangle().intersects( nXMin, nYMin, nXMax, nYMax ); if ( ann->subType() == Okular::Annotation::AText ) @@ -706,6 +715,13 @@ void PagePainter::paintCroppedPageOnPainter( QPainter * destPainter, const Okula } } + if ( boundingRectOnlyAnn ) + { + QRect annotBoundary = boundingRectOnlyAnn->transformedBoundingRectangle().geometry( scaledWidth, scaledHeight ).translated( -scaledCrop.topLeft() ); + mixedPainter->setPen( Qt::DashLine ); + mixedPainter->drawRect( annotBoundary ); + } + /** 6 -- MIXED FLOW. Draw LINKS+IMAGES BORDER on ACTIVE PAINTER **/ if ( enhanceLinks || enhanceImages ) { From cb1968383fe1c5b9686b127b74ad9cb4182fec4a Mon Sep 17 00:00:00 2001 From: Fabio D'Urso Date: Mon, 14 May 2012 13:47:06 +0200 Subject: [PATCH 14/18] poppler gen: don't lie ;) We can't save changes if the document has /Encrypt --- generators/poppler/generator_pdf.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/generators/poppler/generator_pdf.cpp b/generators/poppler/generator_pdf.cpp index e7e678065..b51a0b960 100644 --- a/generators/poppler/generator_pdf.cpp +++ b/generators/poppler/generator_pdf.cpp @@ -1754,7 +1754,11 @@ bool PDFGenerator::supportsOption( SaveOption option ) const switch ( option ) { case SaveChanges: - return true; + { + QMutexLocker locker( userMutex() ); + // Saving files with /Encrypt is not supported + return pdfdoc->isEncrypted() ? false : true; + } default: ; } return false; From d916c1e6cd4c0188eef1ece03a55ccae9e50976b Mon Sep 17 00:00:00 2001 From: Fabio D'Urso Date: Mon, 14 May 2012 17:56:58 +0200 Subject: [PATCH 15/18] poppler gen: Added mutex protection to PopplerAnnotationProxy --- generators/poppler/annots.cpp | 10 ++++++++-- generators/poppler/annots.h | 5 ++++- generators/poppler/generator_pdf.cpp | 2 +- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/generators/poppler/annots.cpp b/generators/poppler/annots.cpp index 0489bcb22..ad8189873 100644 --- a/generators/poppler/annots.cpp +++ b/generators/poppler/annots.cpp @@ -58,8 +58,8 @@ static int maskExportedFlags(int flags) } //BEGIN PopplerAnnotationProxy implementation -PopplerAnnotationProxy::PopplerAnnotationProxy( Poppler::Document *doc ) - : ppl_doc ( doc ) +PopplerAnnotationProxy::PopplerAnnotationProxy( Poppler::Document *doc, QMutex *userMutex ) + : ppl_doc ( doc ), mutex ( userMutex ) { } @@ -90,6 +90,8 @@ void PopplerAnnotationProxy::notifyAddition( Okular::Annotation *okl_ann, int pa QDomElement dom_ann = doc.createElement( "root" ); Okular::AnnotationUtils::storeAnnotation( okl_ann, dom_ann, doc ); + QMutexLocker ml(mutex); + // Create poppler annotation Poppler::Annotation *ppl_ann = Poppler::AnnotationUtils::createAnnotation( dom_ann ); @@ -141,6 +143,8 @@ void PopplerAnnotationProxy::notifyModification( const Okular::Annotation *okl_a if ( !ppl_ann ) // Ignore non-native annotations return; + QMutexLocker ml(mutex); + if ( okl_ann->flags() & Okular::Annotation::BeingMoved ) { // Okular ui already renders the annotation on its own @@ -249,6 +253,8 @@ void PopplerAnnotationProxy::notifyRemoval( Okular::Annotation *okl_ann, int pag if ( !ppl_ann ) // Ignore non-native annotations return; + QMutexLocker ml(mutex); + Poppler::Page *ppl_page = ppl_doc->page( page ); ppl_page->removeAnnotation( ppl_ann ); // Also destroys ppl_ann delete ppl_page; diff --git a/generators/poppler/annots.h b/generators/poppler/annots.h index ac0630232..84dd80204 100644 --- a/generators/poppler/annots.h +++ b/generators/poppler/annots.h @@ -13,6 +13,8 @@ #include #include +#include + #include "core/annotations.h" #include "config-okular-poppler.h" @@ -21,7 +23,7 @@ extern Okular::Annotation* createAnnotationFromPopplerAnnotation( Poppler::Annot class PopplerAnnotationProxy : public Okular::AnnotationProxy { public: - PopplerAnnotationProxy( Poppler::Document *doc ); + PopplerAnnotationProxy( Poppler::Document *doc, QMutex *userMutex ); ~PopplerAnnotationProxy(); bool supports( Capability capability ) const; @@ -30,6 +32,7 @@ class PopplerAnnotationProxy : public Okular::AnnotationProxy void notifyRemoval( Okular::Annotation *annotation, int page ); private: Poppler::Document *ppl_doc; + QMutex *mutex; }; #endif diff --git a/generators/poppler/generator_pdf.cpp b/generators/poppler/generator_pdf.cpp index b51a0b960..86a69d044 100644 --- a/generators/poppler/generator_pdf.cpp +++ b/generators/poppler/generator_pdf.cpp @@ -532,7 +532,7 @@ bool PDFGenerator::init(QVector & pagesVector, const QString &wal reparseConfig(); // create annotation proxy - annotProxy = new PopplerAnnotationProxy( pdfdoc ); + annotProxy = new PopplerAnnotationProxy( pdfdoc, userMutex() ); // the file has been loaded correctly return true; From b33d71ef81df7e97405894f7a02f89b865c25467 Mon Sep 17 00:00:00 2001 From: Fabio D'Urso Date: Thu, 17 May 2012 20:49:21 +0200 Subject: [PATCH 16/18] Enable edit/removal of external annotations if the generator supports it --- core/document.cpp | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/core/document.cpp b/core/document.cpp index 2fed9ae8b..1665891d2 100644 --- a/core/document.cpp +++ b/core/document.cpp @@ -2534,28 +2534,23 @@ void Document::removePageAnnotations( int page, const QList< Annotation * > &ann bool DocumentPrivate::canModifyExternalAnnotations() const { - /* To be enabled once external annotations are implemented independently - * of save/restoreLocalContents. At the moment, we support editing internal - * annotations only. */ -#if 0 - Okular::SaveInterface * iface = qobject_cast< Okular::SaveInterface * >( d->m_generator ); + Okular::SaveInterface * iface = qobject_cast< Okular::SaveInterface * >( m_generator ); - if ( iface && iface->annotationProxy() && - iface->annotationProxy()->supports(AnnotationProxy::Modification) ) + if ( iface && iface->supportsOption(Okular::SaveInterface::SaveChanges) && + iface->annotationProxy() && iface->annotationProxy()->supports(AnnotationProxy::Modification) ) return true; -#endif + return false; } bool DocumentPrivate::canRemoveExternalAnnotations() const { -#if 0 /* See canModifyExternalAnnotations */ - Okular::SaveInterface * iface = qobject_cast< Okular::SaveInterface * >( d->m_generator ); + Okular::SaveInterface * iface = qobject_cast< Okular::SaveInterface * >( m_generator ); - if ( iface && iface->annotationProxy() && - iface->annotationProxy()->supports(AnnotationProxy::Removal) ) + if ( iface && iface->supportsOption(Okular::SaveInterface::SaveChanges) && + iface->annotationProxy() && iface->annotationProxy()->supports(AnnotationProxy::Removal) ) return true; -#endif + return false; } From b3782d82a11f248f06ce0e9a88e86601041ce428 Mon Sep 17 00:00:00 2001 From: Fabio D'Urso Date: Mon, 14 May 2012 15:51:58 +0200 Subject: [PATCH 17/18] Fallback behavior for documents whose generator provides native annotation editing support without saveAs support The only affected generator is poppler. Note that: Document has /Encrypt <=iff=> SaveInterface supportsOption(SaveChanges) is false This patch enforces the following behavior (and warns the user the first time he edits an annotation). - If the document has /Encrypt, warn that "Save as" is not available, but it's possible to export as okular archive. New annotations will be automatically saved to XML as usual. Note that the previous patch already made all existing annotations uneditable, because there's no way to save them. - If the document has no /Encrypt and there are existing external annotations, warn that changes won't be saved automatically. The user needs to "Save as" or changes will be lost. - If the document has no /Encrypt and there aren't existing external annotations, don't show any warning. New annotations will be automatically saved to XML as usual and to file if "Save as" is pressed. --- core/document.cpp | 76 +++++++++++++++++++++++++++++++++++++++++++++-- core/document_p.h | 4 +++ 2 files changed, 77 insertions(+), 3 deletions(-) diff --git a/core/document.cpp b/core/document.cpp index 1665891d2..b06fb3693 100644 --- a/core/document.cpp +++ b/core/document.cpp @@ -771,6 +771,24 @@ DocumentViewport DocumentPrivate::nextDocumentViewport() const return ret; } +void DocumentPrivate::warnLimitedAnnotSupport() +{ + if ( !m_showWarningLimitedAnnotSupport ) + return; + m_showWarningLimitedAnnotSupport = false; // Show the warning once + + if ( canAddAnnotationsNatively() ) + { + // Show only if there are external annotations (we follow the usual XML path otherwise) + if ( m_containsExternalAnnotations ) + KMessageBox::sorry( m_parent->widget(), i18n("Your changes will not be saved automatically. Use File -> Save As... or your changes will be lost") ); + } + else + { + KMessageBox::information( m_parent->widget(), i18n("You can save the annotated document using File -> Export As -> Document Archive"), QString(), "annotExportAsArchive" ); + } +} + void DocumentPrivate::saveDocumentInfo() const { if ( m_xmlFileName.isEmpty() ) @@ -791,10 +809,13 @@ void DocumentPrivate::saveDocumentInfo() const // 2.1. Save page attributes (bookmark state, annotations, ... ) to DOM QDomElement pageList = doc.createElement( "pageList" ); root.appendChild( pageList ); + PageItems saveWhat = AllPageItems; + if ( canAddAnnotationsNatively() && m_containsExternalAnnotations ) + saveWhat &= ~AnnotationPageItems; // Don't save local annotations in this case // .... save pages that hold data QVector< Page * >::const_iterator pIt = m_pagesVector.constBegin(), pEnd = m_pagesVector.constEnd(); for ( ; pIt != pEnd; ++pIt ) - (*pIt)->d->saveLocalContents( pageList, doc ); + (*pIt)->d->saveLocalContents( pageList, doc, saveWhat ); // 2.2. Save document info (current viewport, history, ... ) to DOM QDomElement generalInfo = doc.createElement( "generalInfo" ); @@ -1657,12 +1678,16 @@ bool Document::openDocument( const QString & docFile, const KUrl& url, const KMi } d->m_generatorName = offer->name(); + d->m_containsExternalAnnotations = false; + d->m_showWarningLimitedAnnotSupport = true; foreach ( Page * p, d->m_pagesVector ) { p->d->m_doc = d; + if ( !p->annotations().empty() ) + d->m_containsExternalAnnotations = true; } - // 2. load Additional Data (our bookmarks and metadata) about the document + // 2. load Additional Data (bookmarks, local annotations and metadata) about the document if ( d->m_archiveData ) { d->loadDocumentInfo( d->m_archiveData->metadataFileName ); @@ -2358,6 +2383,8 @@ void Document::addPageAnnotation( int page, Annotation * annotation ) // Redraw everything, including ExternallyDrawn annotations d->refreshPixmaps( page ); } + + d->warnLimitedAnnotSupport(); } bool Document::canModifyPageAnnotation( const Annotation * annotation ) const @@ -2426,6 +2453,10 @@ void Document::modifyPageAnnotation( int page, Annotation * annotation, bool app // Redraw everything, including ExternallyDrawn annotations d->refreshPixmaps( page ); } + + // If the user is moving the annotation, don't steal the focus + if ( (annotation->flags() & Annotation::BeingMoved) == 0 ) + d->warnLimitedAnnotSupport(); } bool Document::canRemovePageAnnotation( const Annotation * annotation ) const @@ -2484,6 +2515,8 @@ void Document::removePageAnnotation( int page, Annotation * annotation ) d->refreshPixmaps( page ); } } + + d->warnLimitedAnnotSupport(); } void Document::removePageAnnotations( int page, const QList< Annotation * > &annotations ) @@ -2530,6 +2563,19 @@ void Document::removePageAnnotations( int page, const QList< Annotation * > &ann d->refreshPixmaps( page ); } } + + d->warnLimitedAnnotSupport(); +} + +bool DocumentPrivate::canAddAnnotationsNatively() const +{ + Okular::SaveInterface * iface = qobject_cast< Okular::SaveInterface * >( m_generator ); + + if ( iface && iface->supportsOption(Okular::SaveInterface::SaveChanges) && + iface->annotationProxy() && iface->annotationProxy()->supports(AnnotationProxy::Addition) ) + return true; + + return false; } bool DocumentPrivate::canModifyExternalAnnotations() const @@ -3608,8 +3654,32 @@ bool Document::saveDocumentArchive( const QString &fileName ) filesNode.appendChild( metadataFileNameNode ); metadataFileNameNode.appendChild( contentDoc.createTextNode( "metadata.xml" ) ); + // If the generator can save annotations natively, do it + KTemporaryFile modifiedFile; + bool annotationsSavedNatively = false; + if ( d->canAddAnnotationsNatively() ) + { + if ( !modifiedFile.open() ) + return false; + + modifiedFile.close(); // We're only interested in the file name + + QString errorText; + if ( saveChanges( modifiedFile.fileName(), &errorText ) ) + { + docPath = modifiedFile.fileName(); // Save this instead of the original file + annotationsSavedNatively = true; + } + else + { + kWarning(OkularDebug) << "saveChanges failed: " << errorText; + kDebug(OkularDebug) << "Falling back to saving a copy of the original file"; + } + } + KTemporaryFile metadataFile; - if ( !d->savePageDocumentInfo( &metadataFile, AnnotationPageItems ) ) + PageItems saveWhat = annotationsSavedNatively ? None : AnnotationPageItems; + if ( !d->savePageDocumentInfo( &metadataFile, saveWhat ) ) return false; const QByteArray contentDocXml = contentDoc.toByteArray(); diff --git a/core/document_p.h b/core/document_p.h index 5c6dcf7ee..ac24cf9d7 100644 --- a/core/document_p.h +++ b/core/document_p.h @@ -115,8 +115,10 @@ class DocumentPrivate bool openDocumentInternal( const KService::Ptr& offer, bool isstdin, const QString& docFile, const QByteArray& filedata ); bool savePageDocumentInfo( KTemporaryFile *infoFile, int what ) const; DocumentViewport nextDocumentViewport() const; + bool canAddAnnotationsNatively() const; bool canModifyExternalAnnotations() const; bool canRemoveExternalAnnotations() const; + void warnLimitedAnnotSupport(); // private slots void saveDocumentInfo() const; @@ -232,6 +234,8 @@ class DocumentPrivate QSet< View * > m_views; bool m_annotationBeingMoved; // is an annotation currently being moved? + bool m_containsExternalAnnotations; // set on opening and never changed + bool m_showWarningLimitedAnnotSupport; }; } From 6f629e3237aca4584e6eb423e0c25a184aa51e22 Mon Sep 17 00:00:00 2001 From: Fabio D'Urso Date: Mon, 21 May 2012 23:54:48 +0200 Subject: [PATCH 18/18] CMakeLists: Install saveinterface.h --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0034cf30a..744d10a5e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -100,6 +100,7 @@ install( FILES interfaces/configinterface.h interfaces/guiinterface.h interfaces/printinterface.h + interfaces/saveinterface.h interfaces/viewerinterface.h DESTINATION ${INCLUDE_INSTALL_DIR}/okular/interfaces COMPONENT Devel)