mirror of
https://invent.kde.org/graphics/okular
synced 2024-08-27 11:40:07 +00:00
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:
parent
423dd010e0
commit
ca5422d0e9
|
@ -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;
|
||||
|
|
|
@ -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 )
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 );
|
||||
|
||||
|
|
129
ui/pageview.cpp
129
ui/pageview.cpp
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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() )
|
||||
|
|
|
@ -113,6 +113,9 @@ public:
|
|||
|
||||
Qt::CursorShape cursor() const;
|
||||
|
||||
// needs to be called after document save
|
||||
void updateAnnotationPointers();
|
||||
|
||||
enum MouseAnnotationState {
|
||||
StateInactive,
|
||||
StateFocused,
|
||||
|
|
Loading…
Reference in a new issue