diff --git a/CMakeLists.txt b/CMakeLists.txt index 352a3b4e6..80ea76cb4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -101,6 +101,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) diff --git a/core/annotations.cpp b/core/annotations.cpp index 4e58288ed..79d9b180f 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" @@ -100,6 +101,9 @@ QRect AnnotationUtils::annotationGeometry( const Annotation * ann, } //END AnnotationUtils implementation +AnnotationProxy::~AnnotationProxy() +{ +} //BEGIN Annotation implementation @@ -732,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 @@ -764,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 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 cf6428604..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) }; /** @@ -655,6 +656,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 0ebc34edb..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 ); @@ -2331,6 +2356,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,42 +2371,160 @@ 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 ); + } + + d->warnLimitedAnnotSupport(); } -void Document::modifyPageAnnotation( int page, Annotation * newannotation ) +bool Document::canModifyPageAnnotation( const Annotation * annotation ) const { - //TODO: modify annotations + 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 ); +} + +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; - kp->d->modifyAnnotation( newannotation ); + // 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) ) + { + /* 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 ); + } + + // 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 +{ + if ( !annotation || ( annotation->flags() & Annotation::DenyDelete ) ) + return false; + + if ( ( annotation->flags() & Annotation::External ) && !d->canRemoveExternalAnnotations() ) + 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 ); + } } + + d->warnLimitedAnnotSupport(); } 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 ) @@ -2387,9 +2533,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; } } @@ -2397,7 +2556,48 @@ 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 ); + } } + + 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 +{ + Okular::SaveInterface * iface = qobject_cast< Okular::SaveInterface * >( m_generator ); + + if ( iface && iface->supportsOption(Okular::SaveInterface::SaveChanges) && + iface->annotationProxy() && iface->annotationProxy()->supports(AnnotationProxy::Modification) ) + return true; + + return false; +} + +bool DocumentPrivate::canRemoveExternalAnnotations() const +{ + Okular::SaveInterface * iface = qobject_cast< Okular::SaveInterface * >( m_generator ); + + if ( iface && iface->supportsOption(Okular::SaveInterface::SaveChanges) && + iface->annotationProxy() && iface->annotationProxy()->supports(AnnotationProxy::Removal) ) + return true; + + return false; } void Document::setPageTextSelection( int page, RegularAreaRect * rect, const QColor & color ) @@ -3454,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.h b/core/document.h index b05e30d00..cd36ae7f1 100644 --- a/core/document.h +++ b/core/document.h @@ -367,11 +367,39 @@ 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. + * + * 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/document_p.h b/core/document_p.h index 4f78d9a14..ac24cf9d7 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(); } @@ -114,6 +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; @@ -227,6 +232,10 @@ class DocumentPrivate FontInfo::List m_fontsCache; 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; }; } diff --git a/core/page.cpp b/core/page.cpp index 9f7400da7..641e8cfe3 100644 --- a/core/page.cpp +++ b/core/page.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -26,6 +27,8 @@ #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" #include "pagecontroller_p.h" @@ -59,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; @@ -596,37 +595,9 @@ 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 ) ) + if ( !d->m_doc->m_parent->canRemovePageAnnotation(annotation) ) return false; QLinkedList< Annotation * >::iterator aIt = m_annotations.begin(), aEnd = m_annotations.end(); @@ -780,17 +751,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 ) ); - 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; - } - + m_doc->m_parent->addPageAnnotation(m_number, annotation); kDebug(OkularDebug) << "restored annot:" << annotation->uniqueName(); } else diff --git a/core/page_p.h b/core/page_p.h index 72e330e53..4cbfb193a 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. */ @@ -121,7 +114,6 @@ class PagePrivate DocumentPrivate *m_doc; NormalizedRect m_boundingBox; Rotation m_rotation; - int m_maxuniqueNum; TextPage * m_text; PageTransition * m_transition; diff --git a/generators/poppler/annots.cpp b/generators/poppler/annots.cpp index f99a316f8..ad8189873 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,243 @@ 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) ); +} + +// 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, QMutex *userMutex ) + : ppl_doc ( doc ), mutex ( userMutex ) +{ +} + +PopplerAnnotationProxy::~PopplerAnnotationProxy() +{ +} + +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; + } +} + +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" ); + Okular::AnnotationUtils::storeAnnotation( okl_ann, dom_ann, doc ); + + QMutexLocker ml(mutex); + + // 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(); +#endif +} + +void PopplerAnnotationProxy::notifyModification( const Okular::Annotation *okl_ann, int page, bool appearanceChanged ) +{ +#ifdef HAVE_POPPLER_0_20 + Q_UNUSED( page ); + Q_UNUSED( appearanceChanged ); + + Poppler::Annotation *ppl_ann = qvariant_cast( okl_ann->nativeId() ); + + 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 + 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; + 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(); +#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 + return; + + QMutexLocker ml(mutex); + + 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(); +#endif +} +//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: @@ -86,6 +321,21 @@ Okular::Annotation* createAnnotationFromPopplerAnnotation( Poppler::Annotation * break; } + 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 */ + } #endif default: { @@ -95,7 +345,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 +357,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..84dd80204 --- /dev/null +++ b/generators/poppler/annots.h @@ -0,0 +1,38 @@ +/*************************************************************************** + * 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 + +#include "core/annotations.h" +#include "config-okular-poppler.h" + +extern Okular::Annotation* createAnnotationFromPopplerAnnotation( Poppler::Annotation *ann, bool * doDelete ); + +class PopplerAnnotationProxy : public Okular::AnnotationProxy +{ + public: + PopplerAnnotationProxy( Poppler::Document *doc, QMutex *userMutex ); + ~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; + QMutex *mutex; +}; + +#endif diff --git a/generators/poppler/generator_pdf.cpp b/generators/poppler/generator_pdf.cpp index e370b93bf..86a69d044 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" @@ -61,7 +62,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; @@ -72,11 +72,30 @@ 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); + +#ifndef HAVE_POPPLER_0_20 + m_printAnnots->setVisible( false ); +#endif + setPrintAnnots( true ); // Default value + } + + bool printAnnots() + { + return m_printAnnots->isChecked(); + } + + void setPrintAnnots( bool printAnnots ) + { + m_printAnnots->setChecked( printAnnots ); } bool printForceRaster() @@ -90,6 +109,7 @@ class PDFOptionsPage : public QWidget } private: + QCheckBox *m_printAnnots; QCheckBox *m_forceRaster; }; @@ -317,8 +337,6 @@ static QLinkedList generateLinks( const QList & pagesVector, const QString &wal // update the configuration reparseConfig(); + // create annotation proxy + annotProxy = new PopplerAnnotationProxy( pdfdoc, userMutex() ); + // the file has been loaded correctly return true; } @@ -521,6 +542,8 @@ bool PDFGenerator::doCloseDocument() { // remove internal objects userMutex()->lock(); + delete annotProxy; + annotProxy = 0; delete pdfdoc; pdfdoc = 0; userMutex()->unlock(); @@ -1026,9 +1049,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 +1072,11 @@ 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()) { @@ -1724,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; @@ -1764,6 +1798,11 @@ bool PDFGenerator::save( const QString &fileName, SaveOptions options, QString * return success; } +Okular::AnnotationProxy* PDFGenerator::annotationProxy() const +{ + return annotProxy; +} + #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..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. @@ -56,6 +57,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 ); @@ -93,6 +96,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(); @@ -142,6 +146,7 @@ class PDFGenerator : public Okular::Generator, public Okular::ConfigInterface, p int nextFontPage; double dpiX; double dpiY; + PopplerAnnotationProxy *annotProxy; QHash annotationsHash; QBitArray rectsGenerated; 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/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 3f8cf2e3c..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 ) ) ); @@ -276,30 +281,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 ) ); } diff --git a/ui/pagepainter.cpp b/ui/pagepainter.cpp index ca55e8171..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 ) { @@ -169,9 +170,20 @@ 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 ) + { + // 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 ) { @@ -703,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 ) { 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; }