Implement swapBackingFile for the PDF backend

How does it work:
 * What it does is really closing and opening the file again through poppler
 * This means that things that are generated in "open" time like Page, Rects, Annotations, Forms need to be updated
	* For Page what we do is swap the PagePrivate so that other classes that hold Page* don't break
		* Since some parts of the PagePrivate can be reused, we move them in PagePrivate::adoptGeneratedContents
	* For all the commands in the undo stack we need to update the annotations/forms it refers to, added a new function to do that
	* The annotationmodel needs updating it's pointers
	* The widgets for the forms are reused and their form* updated
	* the widgets for the videos are recreased since videos don't really hold much content (you lose the playing status on save but i think that's acceptable)

TODO: Make this work for .okular files
TODO: For files with password we will need to reload the file, asking for the password again and thus losing the undo stack, warn the user
TODO: autotests
This commit is contained in:
Albert Astals Cid 2017-10-26 09:47:18 +02:00 committed by Albert Astals Cid
parent 423dd010e0
commit ca5422d0e9
21 changed files with 467 additions and 60 deletions

View file

@ -4359,13 +4359,80 @@ bool Document::swapBackingFile( const QString &newFileName, const QUrl & url )
d->saveDocumentInfo();
qCDebug(OkularCoreDebug) << "Swapping backing file to" << newFileName;
if (genIt->generator->swapBackingFile( newFileName ))
QVector< Page * > newPagesVector;
Generator::SwapBackingFileResult result = genIt->generator->swapBackingFile( newFileName, newPagesVector );
if (result != Generator::SwapBackingFileError)
{
QLinkedList< ObjectRect* > rectsToDelete;
QLinkedList< Annotation* > annotationsToDelete;
QSet< PagePrivate* > pagePrivatesToDelete;
if (result == Generator::SwapBackingFileReloadInternalData)
{
// Here we need to replace everything that the old generator
// had created with what the new one has without making it look like
// we have actually closed and opened the file again
// Simple sanity check
if (newPagesVector.count() != d->m_pagesVector.count())
return false;
// Update the undo stack contents
for (int i = 0; i < d->m_undoStack->count(); ++i)
{
// Trust me on the const_cast ^_^
QUndoCommand *uc = const_cast<QUndoCommand *>( d->m_undoStack->command( i ) );
if (OkularUndoCommand *ouc = dynamic_cast<OkularUndoCommand*>( uc )) ouc->refreshInternalPageReferences( newPagesVector );
else
{
qWarning() << "Unhandled undo command" << uc;
}
}
for (int i = 0; i < d->m_pagesVector.count(); ++i)
{
// switch the PagePrivate* from newPage to oldPage
// this way everyone still holding Page* doesn't get
// disturbed by it
Page *oldPage = d->m_pagesVector[i];
Page *newPage = newPagesVector[i];
newPage->d->adoptGeneratedContents(oldPage->d);
pagePrivatesToDelete << oldPage->d;
oldPage->d = newPage->d;
oldPage->d->m_page = oldPage;
oldPage->d->m_doc = d;
newPage->d = nullptr;
annotationsToDelete << oldPage->m_annotations;
rectsToDelete << oldPage->m_rects;
oldPage->m_annotations = newPage->m_annotations;
oldPage->m_rects = newPage->m_rects;
}
qDeleteAll( newPagesVector );
}
d->m_url = url;
d->m_docFileName = newFileName;
d->updateMetadataXmlNameAndDocSize();
d->m_bookmarkManager->setUrl( d->m_url );
if ( d->m_synctex_scanner )
{
synctex_scanner_free( d->m_synctex_scanner );
d->m_synctex_scanner = synctex_scanner_new_with_output_file( QFile::encodeName( newFileName ).constData(), nullptr, 1);
if ( !d->m_synctex_scanner && QFile::exists(newFileName + QLatin1String( "sync" ) ) )
{
d->loadSyncFile(newFileName);
}
}
foreachObserver( notifySetup( d->m_pagesVector, DocumentObserver::UrlChanged ) );
qDeleteAll( annotationsToDelete );
qDeleteAll( rectsToDelete );
qDeleteAll( pagePrivatesToDelete );
return true;
}
else
@ -4398,8 +4465,11 @@ bool Document::swapBackingFileArchive( const QString &newFileName, const QUrl &
const QString tempFileName = newArchive->document.fileName();
qCDebug(OkularCoreDebug) << "Swapping backing file to" << tempFileName;
if (genIt->generator->swapBackingFile( tempFileName ))
QVector< Page * > newPagesVector;
if (genIt->generator->swapBackingFile( newFileName, newPagesVector ))
{
// TODO Do the same we do in the other swapBackingFile call
delete d->m_archiveData;
d->m_archiveData = newArchive;
d->m_url = url;

View file

@ -15,6 +15,7 @@
#include "form.h"
#include "utils_p.h"
#include "page.h"
#include "page_p.h"
#include <KLocalizedString>
@ -87,6 +88,19 @@ void AddAnnotationCommand::redo()
m_done = true;
}
void AddAnnotationCommand::refreshInternalPageReferences( const QVector< Okular::Page * > &newPagesVector )
{
if ( m_done )
{
// We don't always update m_annotation because even if the annotation has been added to the document
// it can have been removed later so the annotation pointer is stored inside a following RemoveAnnotationCommand
// and thus doesn't need updating because it didn't change
// because of the document reload
auto a = newPagesVector[m_pageNumber]->annotation( m_annotation->uniqueName() );
if (a) m_annotation = a;
}
}
RemoveAnnotationCommand::RemoveAnnotationCommand(Okular::DocumentPrivate * doc, Okular::Annotation* annotation, int pageNumber)
: m_docPriv( doc ),
@ -112,12 +126,25 @@ void RemoveAnnotationCommand::undo()
m_done = false;
}
void RemoveAnnotationCommand::redo(){
void RemoveAnnotationCommand::redo()
{
moveViewportIfBoundingRectNotFullyVisible( m_annotation->boundingRectangle(), m_docPriv, m_pageNumber );
m_docPriv->performRemovePageAnnotation( m_pageNumber, m_annotation );
m_done = true;
}
void RemoveAnnotationCommand::refreshInternalPageReferences( const QVector< Okular::Page * > &newPagesVector )
{
if ( !m_done )
{
// We don't always update m_annotation because it can happen that the annotation remove has been undo
// and that annotation addition has also been undone so the the annotation pointer is stored inside
// a previous AddAnnotationCommand and thus doesn't need updating because it didn't change
// because of the document reload
auto a = newPagesVector[m_pageNumber]->annotation( m_annotation->uniqueName() );
if (a) m_annotation = a;
}
}
ModifyAnnotationPropertiesCommand::ModifyAnnotationPropertiesCommand( DocumentPrivate* docPriv,
Annotation* annotation,
@ -147,6 +174,14 @@ void ModifyAnnotationPropertiesCommand::redo()
m_docPriv->performModifyPageAnnotation( m_pageNumber, m_annotation, true );
}
void ModifyAnnotationPropertiesCommand::refreshInternalPageReferences( const QVector< Okular::Page * > &newPagesVector )
{
// Same reason for not unconditionally updating m_annotation, the annotation pointer can be stored in an add/Remove command
auto a = newPagesVector[m_pageNumber]->annotation( m_annotation->uniqueName() );
if (a) m_annotation = a;
}
TranslateAnnotationCommand::TranslateAnnotationCommand( DocumentPrivate* docPriv,
Annotation* annotation,
int pageNumber,
@ -212,6 +247,14 @@ Okular::NormalizedRect TranslateAnnotationCommand::translateBoundingRectangle( c
return boundingRect;
}
void TranslateAnnotationCommand::refreshInternalPageReferences( const QVector< Page * > &newPagesVector )
{
// Same reason for not unconditionally updating m_annotation, the annotation pointer can be stored in an add/Remove command
auto a = newPagesVector[m_pageNumber]->annotation( m_annotation->uniqueName() );
if (a) m_annotation = a;
}
AdjustAnnotationCommand::AdjustAnnotationCommand(Okular::DocumentPrivate * docPriv,
Okular::Annotation * annotation,
int pageNumber,
@ -277,6 +320,14 @@ Okular::NormalizedRect AdjustAnnotationCommand::adjustBoundingRectangle(
return Okular::NormalizedRect( left, top, right, bottom );
}
void AdjustAnnotationCommand::refreshInternalPageReferences( const QVector< Page * > &newPagesVector )
{
// Same reason for not unconditionally updating m_annotation, the annotation pointer can be stored in an add/Remove command
auto a = newPagesVector[m_pageNumber]->annotation( m_annotation->uniqueName() );
if (a) m_annotation = a;
}
EditTextCommand::EditTextCommand( const QString & newContents,
int newCursorPos,
const QString & prevContents,
@ -363,6 +414,7 @@ QString EditTextCommand::newContentsRightOfCursor()
return m_newContents.right(m_newContents.length() - m_newCursorPos);
}
EditAnnotationContentsCommand::EditAnnotationContentsCommand( DocumentPrivate* docPriv,
Annotation* annotation,
int pageNumber,
@ -412,6 +464,13 @@ bool EditAnnotationContentsCommand::mergeWith(const QUndoCommand* uc)
}
}
void EditAnnotationContentsCommand::refreshInternalPageReferences( const QVector< Page * > &newPagesVector )
{
auto a = newPagesVector[m_pageNumber]->annotation( m_annotation->uniqueName() );
if (a) m_annotation = a;
}
EditFormTextCommand::EditFormTextCommand( Okular::DocumentPrivate* docPriv,
Okular::FormFieldText* form,
int pageNumber,
@ -463,6 +522,12 @@ bool EditFormTextCommand::mergeWith(const QUndoCommand* uc)
}
}
void EditFormTextCommand::refreshInternalPageReferences( const QVector< Page * > &newPagesVector )
{
m_form = dynamic_cast<FormFieldText *>(Okular::PagePrivate::findEquivalentForm( newPagesVector[m_pageNumber], m_form ));
}
EditFormListCommand::EditFormListCommand( Okular::DocumentPrivate* docPriv,
FormFieldChoice* form,
int pageNumber,
@ -493,6 +558,12 @@ void EditFormListCommand::redo()
m_docPriv->notifyFormChanges( m_pageNumber );
}
void EditFormListCommand::refreshInternalPageReferences( const QVector< Page * > &newPagesVector )
{
m_form = dynamic_cast<FormFieldChoice *>(Okular::PagePrivate::findEquivalentForm( newPagesVector[m_pageNumber], m_form ));
}
EditFormComboCommand::EditFormComboCommand( Okular::DocumentPrivate* docPriv,
FormFieldChoice* form,
int pageNumber,
@ -579,6 +650,12 @@ bool EditFormComboCommand::mergeWith( const QUndoCommand *uc )
}
}
void EditFormComboCommand::refreshInternalPageReferences( const QVector< Page * > &newPagesVector )
{
m_form = dynamic_cast<FormFieldChoice *>(Okular::PagePrivate::findEquivalentForm( newPagesVector[m_pageNumber], m_form ));
}
EditFormButtonsCommand::EditFormButtonsCommand( Okular::DocumentPrivate* docPriv,
int pageNumber,
const QList< FormFieldButton* > & formButtons,
@ -628,6 +705,17 @@ void EditFormButtonsCommand::redo()
m_docPriv->notifyFormChanges( m_pageNumber );
}
void EditFormButtonsCommand::refreshInternalPageReferences( const QVector< Okular::Page * > &newPagesVector )
{
const QList< FormFieldButton* > oldFormButtons = m_formButtons;
m_formButtons.clear();
foreach( FormFieldButton* oldFormButton, oldFormButtons )
{
FormFieldButton *button = dynamic_cast<FormFieldButton *>(Okular::PagePrivate::findEquivalentForm( newPagesVector[m_pageNumber], oldFormButton ));
m_formButtons << button;
}
}
void EditFormButtonsCommand::clearFormButtonStates()
{
foreach( FormFieldButton* formButton, m_formButtons )

View file

@ -23,8 +23,15 @@ class DocumentPrivate;
class FormFieldText;
class FormFieldButton;
class FormFieldChoice;
class Page;
class AddAnnotationCommand : public QUndoCommand
class OkularUndoCommand : public QUndoCommand
{
public:
virtual void refreshInternalPageReferences( const QVector< Okular::Page * > &newPagesVector ) = 0;
};
class AddAnnotationCommand : public OkularUndoCommand
{
public:
AddAnnotationCommand(Okular::DocumentPrivate * docPriv, Okular::Annotation* annotation, int pageNumber);
@ -35,6 +42,8 @@ class AddAnnotationCommand : public QUndoCommand
void redo() override;
void refreshInternalPageReferences( const QVector< Okular::Page * > &newPagesVector ) override;
private:
Okular::DocumentPrivate * m_docPriv;
Okular::Annotation* m_annotation;
@ -42,7 +51,7 @@ class AddAnnotationCommand : public QUndoCommand
bool m_done;
};
class RemoveAnnotationCommand : public QUndoCommand
class RemoveAnnotationCommand : public OkularUndoCommand
{
public:
RemoveAnnotationCommand(Okular::DocumentPrivate * doc, Okular::Annotation* annotation, int pageNumber);
@ -50,6 +59,8 @@ class RemoveAnnotationCommand : public QUndoCommand
void undo() override;
void redo() override;
void refreshInternalPageReferences( const QVector< Okular::Page * > &newPagesVector ) override;
private:
Okular::DocumentPrivate * m_docPriv;
Okular::Annotation* m_annotation;
@ -57,7 +68,7 @@ class RemoveAnnotationCommand : public QUndoCommand
bool m_done;
};
class ModifyAnnotationPropertiesCommand : public QUndoCommand
class ModifyAnnotationPropertiesCommand : public OkularUndoCommand
{
public:
ModifyAnnotationPropertiesCommand( Okular::DocumentPrivate* docPriv, Okular::Annotation* annotation,
@ -68,6 +79,8 @@ class ModifyAnnotationPropertiesCommand : public QUndoCommand
void undo() override;
void redo() override;
void refreshInternalPageReferences( const QVector< Okular::Page * > &newPagesVector ) override;
private:
Okular::DocumentPrivate * m_docPriv;
Okular::Annotation* m_annotation;
@ -76,7 +89,7 @@ class ModifyAnnotationPropertiesCommand : public QUndoCommand
QDomNode m_newProperties;
};
class TranslateAnnotationCommand : public QUndoCommand
class TranslateAnnotationCommand : public OkularUndoCommand
{
public:
TranslateAnnotationCommand(Okular::DocumentPrivate* docPriv,
@ -92,6 +105,8 @@ class TranslateAnnotationCommand : public QUndoCommand
Okular::NormalizedPoint minusDelta();
Okular::NormalizedRect translateBoundingRectangle( const Okular::NormalizedPoint & delta );
void refreshInternalPageReferences( const QVector< Okular::Page * > &newPagesVector ) override;
private:
Okular::DocumentPrivate * m_docPriv;
Okular::Annotation* m_annotation;
@ -100,7 +115,7 @@ class TranslateAnnotationCommand : public QUndoCommand
bool m_completeDrag;
};
class AdjustAnnotationCommand : public QUndoCommand
class AdjustAnnotationCommand : public OkularUndoCommand
{
public:
AdjustAnnotationCommand(Okular::DocumentPrivate * docPriv,
@ -117,6 +132,8 @@ class AdjustAnnotationCommand : public QUndoCommand
Okular::NormalizedRect adjustBoundingRectangle(
const Okular::NormalizedPoint & delta1, const Okular::NormalizedPoint & delta2 );
void refreshInternalPageReferences( const QVector< Okular::Page * > &newPagesVector ) override;
private:
Okular::DocumentPrivate * m_docPriv;
Okular::Annotation* m_annotation;
@ -126,7 +143,7 @@ class AdjustAnnotationCommand : public QUndoCommand
bool m_completeDrag;
};
class EditTextCommand : public QUndoCommand
class EditTextCommand : public OkularUndoCommand
{
public:
EditTextCommand( const QString & newContents,
@ -182,6 +199,8 @@ class EditAnnotationContentsCommand : public EditTextCommand
int id() const override;
bool mergeWith(const QUndoCommand *uc) override;
void refreshInternalPageReferences( const QVector< Okular::Page * > &newPagesVector ) override;
private:
Okular::DocumentPrivate * m_docPriv;
Okular::Annotation* m_annotation;
@ -203,13 +222,16 @@ class EditFormTextCommand : public EditTextCommand
void redo() override;
int id() const override;
bool mergeWith( const QUndoCommand *uc ) override;
void refreshInternalPageReferences( const QVector< Okular::Page * > &newPagesVector ) override;
private:
Okular::DocumentPrivate* m_docPriv;
Okular::FormFieldText* m_form;
int m_pageNumber;
};
class EditFormListCommand : public QUndoCommand
class EditFormListCommand : public OkularUndoCommand
{
public:
EditFormListCommand( Okular::DocumentPrivate* docPriv,
@ -222,6 +244,8 @@ class EditFormListCommand : public QUndoCommand
void undo() override;
void redo() override;
void refreshInternalPageReferences( const QVector< Okular::Page * > &newPagesVector ) override;
private:
Okular::DocumentPrivate* m_docPriv;
FormFieldChoice* m_form;
@ -248,6 +272,8 @@ class EditFormComboCommand : public EditTextCommand
int id() const override;
bool mergeWith( const QUndoCommand *uc ) override;
void refreshInternalPageReferences( const QVector< Okular::Page * > &newPagesVector ) override;
private:
Okular::DocumentPrivate* m_docPriv;
FormFieldChoice* m_form;
@ -256,7 +282,7 @@ class EditFormComboCommand : public EditTextCommand
int m_prevIndex;
};
class EditFormButtonsCommand : public QUndoCommand
class EditFormButtonsCommand : public OkularUndoCommand
{
public:
EditFormButtonsCommand( Okular::DocumentPrivate* docPriv,
@ -268,6 +294,8 @@ class EditFormButtonsCommand : public QUndoCommand
void undo() override;
void redo() override;
void refreshInternalPageReferences( const QVector< Okular::Page * > &newPagesVector ) override;
private:
void clearFormButtonStates();

View file

@ -199,9 +199,9 @@ Document::OpenResult Generator::loadDocumentFromDataWithPassword( const QByteArr
return loadDocumentFromData( fileData, pagesVector ) ? Document::OpenSuccess : Document::OpenError;
}
bool Generator::swapBackingFile( QString const &/*newFileName */)
Generator::SwapBackingFileResult Generator::swapBackingFile( QString const &/*newFileName */, QVector<Okular::Page*> & /*newPagesVector*/ )
{
return false;
return SwapBackingFileError;
}
bool Generator::closeDocument()

View file

@ -270,17 +270,27 @@ class OKULARCORE_EXPORT Generator : public QObject
*/
virtual Document::OpenResult loadDocumentFromDataWithPassword( const QByteArray & fileData, QVector< Page * > & pagesVector, const QString &password );
/**
* Describes the result of an swap file operation.
*
* @since 1.3
*/
enum SwapBackingFileResult
{
SwapBackingFileError, //< The document could not be swapped
SwapBackingFileNoOp, //< The document was swapped and nothing needs to be done
SwapBackingFileReloadInternalData //< The document was swapped and internal data (forms, annotations, etc) needs to be reloaded
};
/**
* Changes the path of the file we are reading from. The new path must
* point to a copy of the same document.
*
* @note the Generator has to have the feature @ref SwapBackingFile enabled
*
* @since 0.20 (KDE 4.14)
*
* @returns true on success, false otherwise.
* @since 1.3
*/
virtual bool swapBackingFile( const QString &newFileName );
virtual SwapBackingFileResult swapBackingFile( const QString &newFileName, QVector<Okular::Page*> & newPagesVector );
/**
* This method is called when the document is closed and not used

View file

@ -135,14 +135,17 @@ Page::Page( uint page, double w, double h, Rotation o )
Page::~Page()
{
deletePixmaps();
deleteRects();
d->deleteHighlights();
deleteAnnotations();
d->deleteTextSelections();
deleteSourceReferences();
if (d)
{
deletePixmaps();
deleteRects();
d->deleteHighlights();
deleteAnnotations();
d->deleteTextSelections();
deleteSourceReferences();
delete d;
delete d;
}
}
int Page::number() const
@ -1050,3 +1053,59 @@ void PagePrivate::setTilesManager( const DocumentObserver *observer, TilesManage
m_tilesManagers.insert(observer, tm);
}
void PagePrivate::adoptGeneratedContents( PagePrivate *oldPage )
{
rotateAt( oldPage->m_rotation );
m_pixmaps = oldPage->m_pixmaps;
oldPage->m_pixmaps.clear();
m_tilesManagers = oldPage->m_tilesManagers;
oldPage->m_tilesManagers.clear();
m_boundingBox = oldPage->m_boundingBox;
m_isBoundingBoxKnown = oldPage->m_isBoundingBoxKnown;
m_text = oldPage->m_text;
oldPage->m_text = nullptr;
m_textSelections = oldPage->m_textSelections;
oldPage->m_textSelections = nullptr;
restoredLocalAnnotationList = oldPage->restoredLocalAnnotationList;
restoredFormFieldList = oldPage->restoredFormFieldList;
}
FormField *PagePrivate::findEquivalentForm( const Page *p, FormField *oldField )
{
// given how id is not very good of id (at least for pdf) we do a few passes
// same rect, type and id
foreach(FormField *f, p->d->formfields)
{
if (f->rect() == oldField->rect() && f->type() == oldField->type() && f->id() == oldField->id())
return f;
}
// same rect and type
foreach(FormField *f, p->d->formfields)
{
if (f->rect() == oldField->rect() && f->type() == oldField->type())
return f;
}
// fuzzy rect, same type and id
foreach(FormField *f, p->d->formfields)
{
if (f->type() == oldField->type() && f->id() == oldField->id() && qFuzzyCompare(f->rect().left, oldField->rect().left) && qFuzzyCompare(f->rect().top, oldField->rect().top) && qFuzzyCompare(f->rect().right, oldField->rect().right) && qFuzzyCompare(f->rect().bottom, oldField->rect().bottom))
{
return f;
}
}
// fuzzy rect and same type
foreach(FormField *f, p->d->formfields)
{
if (f->type() == oldField->type() && qFuzzyCompare(f->rect().left, oldField->rect().left) && qFuzzyCompare(f->rect().top, oldField->rect().top) && qFuzzyCompare(f->rect().right, oldField->rect().right) && qFuzzyCompare(f->rect().bottom, oldField->rect().bottom))
{
return f;
}
}
return nullptr;
}

View file

@ -392,7 +392,7 @@ class OKULARCORE_EXPORT Page
QList<Tile> tilesAt( const DocumentObserver *observer, const NormalizedRect &rect ) const;
private:
PagePrivate* const d;
PagePrivate* d;
/// @cond PRIVATE
friend class PagePrivate;
friend class Document;

View file

@ -120,6 +120,17 @@ class PagePrivate
*/
void setTilesManager( const DocumentObserver *observer, TilesManager *tm );
/**
* Moves contents that are generated from oldPage to this. And clears them from page
* so it can be deleted fine.
*/
void adoptGeneratedContents( PagePrivate *oldPage );
/*
* Tries to find an equivalent form field to oldField by looking into the rect, type and name
*/
OKULARCORE_EXPORT static FormField *findEquivalentForm( const Page *p, FormField *oldField );
class PixmapObject
{
public:

View file

@ -91,11 +91,11 @@ bool KIMGIOGenerator::loadDocumentInternal(const QByteArray & fileData, const QS
return true;
}
bool KIMGIOGenerator::swapBackingFile( QString const &/*newFileName*/ )
KIMGIOGenerator::SwapBackingFileResult KIMGIOGenerator::swapBackingFile( QString const &/*newFileName*/, QVector<Okular::Page*> & /*newPagesVector*/ )
{
// NOP: We don't actually need to do anything because all data has already
// been loaded in RAM
return true;
return SwapBackingFileNoOp;
}
bool KIMGIOGenerator::doCloseDocument()

View file

@ -27,7 +27,7 @@ class KIMGIOGenerator : public Okular::Generator
// [INHERITED] load a document and fill up the pagesVector
bool loadDocument( const QString & fileName, QVector<Okular::Page*> & pagesVector ) override;
bool loadDocumentFromData( const QByteArray & fileData, QVector<Okular::Page*> & pagesVector ) override;
bool swapBackingFile( QString const &newFileName ) override;
SwapBackingFileResult swapBackingFile( QString const &newFileName, QVector<Okular::Page*> & newPagesVector ) override;
// [INHERITED] print document using already configured kprinter
bool print( QPrinter& printer ) override;

View file

@ -512,6 +512,7 @@ PDFGenerator::PDFGenerator( QObject *parent, const QVariantList &args )
setFeature( PrintToFile );
setFeature( ReadRawData );
setFeature( TiledRendering );
setFeature( SwapBackingFile );
// You only need to do it once not for each of the documents but it is cheap enough
// so doing it all the time won't hurt either
@ -592,6 +593,19 @@ Okular::Document::OpenResult PDFGenerator::init(QVector<Okular::Page*> & pagesVe
return Okular::Document::OpenSuccess;
}
PDFGenerator::SwapBackingFileResult PDFGenerator::swapBackingFile( QString const &newFileName, QVector<Okular::Page*> & newPagesVector )
{
doCloseDocument();
// TODO For files with password we need to figure out a way to return false but that doesn't
// end in error but that ends up in a reload.
// Probably hijacking at the canSwapBackingFile level
auto openResult = loadDocumentWithPassword(newFileName, newPagesVector, QString());
if (openResult != Okular::Document::OpenSuccess)
return SwapBackingFileError;
return SwapBackingFileReloadInternalData;
}
bool PDFGenerator::doCloseDocument()
{
// remove internal objects

View file

@ -99,6 +99,7 @@ class PDFGenerator : public Okular::Generator, public Okular::ConfigInterface, p
Okular::AnnotationProxy* annotationProxy() const override;
protected:
SwapBackingFileResult swapBackingFile( QString const &newFileName, QVector<Okular::Page*> & newPagesVector ) override;
bool doCloseDocument() override;
Okular::TextPage* textPage( Okular::Page *page ) override;

View file

@ -105,10 +105,32 @@ AnnotationModelPrivate::~AnnotationModelPrivate()
delete root;
}
static void updateAnnotationPointer( AnnItem *item, const QVector< Okular::Page * > &pages )
{
if ( item->annotation ) {
item->annotation = pages[ item->page ]->annotation( item->annotation->uniqueName() );
if ( !item->annotation )
qWarning() << "Lost annotation on document save, something went wrong";
}
foreach ( AnnItem *child, item->children )
updateAnnotationPointer( child, pages );
}
void AnnotationModelPrivate::notifySetup( const QVector< Okular::Page * > &pages, int setupFlags )
{
if ( !( setupFlags & Okular::DocumentObserver::DocumentChanged ) )
{
if ( setupFlags & Okular::DocumentObserver::UrlChanged )
{
// Here with UrlChanged and no document changed it means we
// need to update all the Annotation* otherwise
// they still point to the old document ones, luckily the old ones are still
// around so we can look for the new ones using unique ids, etc
updateAnnotationPointer( root, pages );
}
return;
}
q->beginResetModel();
qDeleteAll( root->children );

View file

@ -246,6 +246,11 @@ Okular::Annotation * AnnotWindow::annotation() const
return m_annot;
}
void AnnotWindow::updateAnnotation( Okular::Annotation * a )
{
m_annot = a;
}
void AnnotWindow::reloadInfo()
{
const QColor newcolor = m_annot->style().color().isValid() ? m_annot->style().color() : Qt::yellow;
@ -261,6 +266,11 @@ void AnnotWindow::reloadInfo()
m_title->setDate( m_annot->modificationDate() );
}
int AnnotWindow::pageNumber() const
{
return m_page;
}
void AnnotWindow::showEvent( QShowEvent * event )
{
QFrame::showEvent( event );

View file

@ -37,6 +37,9 @@ class AnnotWindow : public QFrame
void reloadInfo();
Okular::Annotation * annotation() const;
int pageNumber() const;
void updateAnnotation( Okular::Annotation * a );
private:
MovableTitle * m_title;

View file

@ -309,6 +309,11 @@ void FormWidgetIface::setPageItem( PageViewItem *pageItem )
m_pageItem = pageItem;
}
void FormWidgetIface::setFormField( Okular::FormField *field )
{
m_ff = field;
}
Okular::FormField* FormWidgetIface::formField() const
{
return m_ff;

View file

@ -148,8 +148,9 @@ class FormWidgetIface
void setCanBeFilled( bool fill );
void setPageItem( PageViewItem *pageItem );
Okular::FormField* formField() const;
PageViewItem* pageItem() const;
void setFormField( Okular::FormField *field );
Okular::FormField* formField() const;
virtual void setFormWidgetsController( FormWidgetsController *controller );

View file

@ -81,6 +81,7 @@
#include "core/document_p.h"
#include "core/form.h"
#include "core/page.h"
#include "core/page_p.h"
#include "core/misc.h"
#include "core/generator.h"
#include "core/movie.h"
@ -937,6 +938,43 @@ void PageView::selectAll()
}
}
void PageView::createAnnotationsVideoWidgets(PageViewItem *item, const QLinkedList< Okular::Annotation * > &annotations)
{
qDeleteAll( item->videoWidgets() );
item->videoWidgets().clear();
QLinkedList< Okular::Annotation * >::const_iterator aIt = annotations.constBegin(), aEnd = annotations.constEnd();
for ( ; aIt != aEnd; ++aIt )
{
Okular::Annotation * a = *aIt;
if ( a->subType() == Okular::Annotation::AMovie )
{
Okular::MovieAnnotation * movieAnn = static_cast< Okular::MovieAnnotation * >( a );
VideoWidget * vw = new VideoWidget( movieAnn, movieAnn->movie(), d->document, viewport() );
item->videoWidgets().insert( movieAnn->movie(), vw );
vw->pageInitialized();
}
else if ( a->subType() == Okular::Annotation::ARichMedia )
{
Okular::RichMediaAnnotation * richMediaAnn = static_cast< Okular::RichMediaAnnotation * >( a );
VideoWidget * vw = new VideoWidget( richMediaAnn, richMediaAnn->movie(), d->document, viewport() );
item->videoWidgets().insert( richMediaAnn->movie(), vw );
vw->pageInitialized();
}
else if ( a->subType() == Okular::Annotation::AScreen )
{
const Okular::ScreenAnnotation * screenAnn = static_cast< Okular::ScreenAnnotation * >( a );
Okular::Movie *movie = GuiUtils::renditionMovieFromScreenAnnotation( screenAnn );
if ( movie )
{
VideoWidget * vw = new VideoWidget( screenAnn, movie, d->document, viewport() );
item->videoWidgets().insert( movie, vw );
vw->pageInitialized();
}
}
}
}
//BEGIN DocumentObserver inherited methods
void PageView::notifySetup( const QVector< Okular::Page * > & pageSet, int setupFlags )
{
@ -966,8 +1004,66 @@ void PageView::notifySetup( const QVector< Okular::Page * > & pageSet, int setup
w->setCanBeFilled( allowfillforms );
}
}
if ( !documentChanged )
{
if ( setupFlags & Okular::DocumentObserver::UrlChanged )
{
// Here with UrlChanged and no document changed it means we
// need to update all the Annotation* and Form* otherwise
// they still point to the old document ones, luckily the old ones are still
// around so we can look for the new ones using unique ids, etc
d->mouseAnnotation->updateAnnotationPointers();
foreach(AnnotWindow *aw, d->m_annowindows)
{
Okular::Annotation *newA = d->document->page( aw->pageNumber() )->annotation( aw->annotation()->uniqueName() );
aw->updateAnnotation( newA );
}
const QRect viewportRect( horizontalScrollBar()->value(), verticalScrollBar()->value(),
viewport()->width(), viewport()->height() );
for ( int i = 0; i < count; i++ )
{
PageViewItem *item = d->items[i];
const QSet<FormWidgetIface*> fws = item->formWidgets();
foreach ( FormWidgetIface * w, fws )
{
Okular::FormField *f = Okular::PagePrivate::findEquivalentForm( d->document->page( i ), w->formField() );
if (f)
{
w->setFormField( f );
}
else
{
qWarning() << "Lost form field on document save, something is wrong";
item->formWidgets().remove(w);
delete w;
}
}
// For the video widgets we don't really care about reusing them since they don't contain much info so just
// create them again
createAnnotationsVideoWidgets( item, pageSet[i]->annotations() );
Q_FOREACH ( VideoWidget *vw, item->videoWidgets() )
{
const Okular::NormalizedRect r = vw->normGeometry();
vw->setGeometry(
qRound( item->uncroppedGeometry().left() + item->uncroppedWidth() * r.left ) + 1 - viewportRect.left(),
qRound( item->uncroppedGeometry().top() + item->uncroppedHeight() * r.top ) + 1 - viewportRect.top(),
qRound( fabs( r.right - r.left ) * item->uncroppedGeometry().width() ),
qRound( fabs( r.bottom - r.top ) * item->uncroppedGeometry().height() ) );
// Workaround, otherwise the size somehow gets lost
vw->show();
vw->hide();
}
}
}
return;
}
}
// mouseAnnotation must not access our PageViewItem widgets any longer
@ -1011,37 +1107,8 @@ void PageView::notifySetup( const QVector< Okular::Page * > & pageSet, int setup
hasformwidgets = true;
}
}
const QLinkedList< Okular::Annotation * > annotations = (*setIt)->annotations();
QLinkedList< Okular::Annotation * >::const_iterator aIt = annotations.constBegin(), aEnd = annotations.constEnd();
for ( ; aIt != aEnd; ++aIt )
{
Okular::Annotation * a = *aIt;
if ( a->subType() == Okular::Annotation::AMovie )
{
Okular::MovieAnnotation * movieAnn = static_cast< Okular::MovieAnnotation * >( a );
VideoWidget * vw = new VideoWidget( movieAnn, movieAnn->movie(), d->document, viewport() );
item->videoWidgets().insert( movieAnn->movie(), vw );
vw->pageInitialized();
}
else if ( a->subType() == Okular::Annotation::ARichMedia )
{
Okular::RichMediaAnnotation * richMediaAnn = static_cast< Okular::RichMediaAnnotation * >( a );
VideoWidget * vw = new VideoWidget( richMediaAnn, richMediaAnn->movie(), d->document, viewport() );
item->videoWidgets().insert( richMediaAnn->movie(), vw );
vw->pageInitialized();
}
else if ( a->subType() == Okular::Annotation::AScreen )
{
const Okular::ScreenAnnotation * screenAnn = static_cast< Okular::ScreenAnnotation * >( a );
Okular::Movie *movie = GuiUtils::renditionMovieFromScreenAnnotation( screenAnn );
if ( movie )
{
VideoWidget * vw = new VideoWidget( screenAnn, movie, d->document, viewport() );
item->videoWidgets().insert( movie, vw );
vw->pageInitialized();
}
}
}
createAnnotationsVideoWidgets( item, (*setIt)->annotations() );
}
// invalidate layout so relayout/repaint will happen on next viewport change

View file

@ -199,6 +199,8 @@ Q_OBJECT
// handle link clicked
bool mouseReleaseOverLink( const Okular::ObjectRect * rect ) const;
void createAnnotationsVideoWidgets(PageViewItem *item, const QLinkedList< Okular::Annotation * > &annotations);
// don't want to expose classes in here
class PageViewPrivate * d;

View file

@ -400,6 +400,19 @@ Qt::CursorShape MouseAnnotation::cursor() const
return Qt::ArrowCursor;
}
void MouseAnnotation::updateAnnotationPointers()
{
if (m_focusedAnnotation.annotation)
{
m_focusedAnnotation.annotation = m_document->page( m_focusedAnnotation.pageNumber )->annotation( m_focusedAnnotation.annotation->uniqueName() );
}
if (m_mouseOverAnnotation.annotation)
{
m_mouseOverAnnotation.annotation = m_document->page( m_mouseOverAnnotation.pageNumber )->annotation( m_mouseOverAnnotation.annotation->uniqueName() );
}
}
void MouseAnnotation::cancel()
{
if ( isActive() )

View file

@ -113,6 +113,9 @@ public:
Qt::CursorShape cursor() const;
// needs to be called after document save
void updateAnnotationPointers();
enum MouseAnnotationState {
StateInactive,
StateFocused,