okular/core/document_p.h

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

367 lines
12 KiB
C
Raw Normal View History

2021-05-24 07:25:56 +00:00
/*
SPDX-FileCopyrightText: 2004-2005 Enrico Ros <eros.kde@email.it>
SPDX-FileCopyrightText: 2004-2007 Albert Astals Cid <aacid@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef _OKULAR_DOCUMENT_P_H_
#define _OKULAR_DOCUMENT_P_H_
#include "document.h"
#include "script/event_p.h"
#include "synctex/synctex_parser.h"
#include <memory>
// qt/kde/system includes
#include <KConfigDialog>
#include <KPluginMetaData>
2018-08-31 09:23:45 +00:00
#include <QHash>
#include <QMap>
#include <QMutex>
#include <QPointer>
2015-01-29 19:55:57 +00:00
#include <QUrl>
// local includes
#include "fontinfo.h"
#include "generator.h"
class QUndoStack;
class QEventLoop;
class QFile;
class QTimer;
2014-09-17 22:30:39 +00:00
class QTemporaryFile;
class KPluginMetaData;
struct AllocatedPixmap;
struct ArchiveData;
struct RunningSearch;
namespace Okular
{
class ScriptAction;
class ConfigInterface;
class PageController;
class SaveInterface;
class Scripter;
class View;
}
struct GeneratorInfo {
explicit GeneratorInfo(Okular::Generator *g, const KPluginMetaData &data)
: generator(g)
, metadata(data)
, config(nullptr)
, save(nullptr)
, configChecked(false)
, saveChecked(false)
{
}
Okular::Generator *generator;
KPluginMetaData metadata;
Okular::ConfigInterface *config;
Okular::SaveInterface *save;
bool configChecked : 1;
bool saveChecked : 1;
};
namespace Okular
{
2020-02-21 15:25:29 +00:00
class OKULARCORE_EXPORT BackendConfigDialog : public KConfigDialog
{
2020-02-21 15:25:29 +00:00
Q_OBJECT
public:
BackendConfigDialog(QWidget *parent, const QString &name, KCoreConfigSkeleton *config)
: KConfigDialog(parent, name, config)
{
}
KPageWidget *thePageWidget()
{
return pageWidget();
}
};
class FontExtractionThread;
struct DoContinueDirectionMatchSearchStruct {
QSet<int> *pagesToNotify;
RegularAreaRect *match;
int currentPage;
int searchID;
};
enum LoadDocumentInfoFlag {
LoadNone = 0,
LoadPageInfo = 1, // Load annotations and forms
LoadGeneralInfo = 2, // History, rotation, ...
LoadAllInfo = 0xff
};
Q_DECLARE_FLAGS(LoadDocumentInfoFlags, LoadDocumentInfoFlag)
class DocumentPrivate
{
public:
2018-09-01 08:25:57 +00:00
explicit DocumentPrivate(Document *parent)
: m_parent(parent)
, m_searchCancelled(false)
, m_tempFile(nullptr)
, m_docSize(-1)
, m_allocatedPixmapsTotalMemory(0)
, m_maxAllocatedTextPages(0)
, m_warnedOutOfMemory(false)
, m_rotation(Rotation0)
, m_exportCached(false)
, m_bookmarkManager(nullptr)
, m_memCheckTimer(nullptr)
, m_saveBookmarksTimer(nullptr)
, m_generator(nullptr)
, m_walletGenerator(nullptr)
, m_generatorsLoaded(false)
, m_pageController(nullptr)
, m_closingLoop(nullptr)
, m_scripter(nullptr)
, m_archiveData(nullptr)
, m_fontsCached(false)
, m_annotationEditingEnabled(true)
Add annotation resize functionality Usage: If you left-click an annotation, it gets selected. Resize handles appear on the selection rectangle. When cursor is moved over one of the 8 resize handles on the corners/edges, the cursor shape changes to indicate resize mode. Everywhere else on the annotation means "move", just as it was before resize feature was added. Pressing ESC or clicking an area outside the annotation cancels a selection. Pressing Del deletes a selected annotation. Feature is only applicable for annotation types AText, AStamp and AGeom. Implementation: It works by eventually changing AnnotationPrivate::m_boundary and notifying generator (i.e. poppler) about that change. Annotation state handling is shifted out of PageView into a new class MouseAnnotation (ui/pageviewmouseannotation.cpp). Some functionality not related to resizing but to annotation interaction in general is also shifted to class MouseAnnotation, to build a single place of responsiblity. Other changes: Add method Document::adjustPageAnnotation, backed by a QUndoCommand. class Okular::AdjustAnnotationCommand. Add Annotation::adjust and Annotation::canBeResized methods. Draw resize handles in PagePainter::paintCroppedPageOnPainter. Resize and move work -for types AText, AStamp and AGeom -on all pages of document -when viewport position changes -when zoom level changes -for all page rotations (0°, 90°, 180°, 270°) Selection is canceled -when currently selected annotation is deleted -on mouse click outside of currently selected annotation -ESC is pressed Viewport is shifted when mouse cursor during move/resize comes close to viewport border. Resize to negative is prevented. Tiny annotations are still selectable. If mouse is moved over an annotation type that we can focus, and the annotation is not yet focused, mouse cursor shape changes to arrow. If mouse cursor rests over an annotation A, while annotation B is focused, a tooltip for annotation A is shown. Selected Annotation is deleted when Del is pressed. Test for regressions: -Annotation interaction (focus, move, resize, start playback, ...) are only done in mode EnumMouseMode::Browse. -If mouse is moved over an annotation type where we can start an action, mouse cursor shape changes to pointing hand. -If mouse is moved over an annotation type that we can't interact with, mouse cursor shape stays a open hand. -If mouse cursor rests over an annotation of any type, a tooltip for that annotation is shown. -Grab/move scroll area (on left click + mouse move) is prevented, if mouse is over focused annotation, or over AMovie/AScreen/AFileAttachment annotation. -A double click on a annotation starts the "annotator". REVIEW: 127366 BUG: 177778 BUG: 314843 BUG: 358060
2017-03-19 22:16:06 +00:00
, m_annotationBeingModified(false)
2023-06-16 14:26:27 +00:00
, m_undoStack(nullptr)
, m_docdataMigrationNeeded(false)
, m_synctex_scanner(nullptr)
{
calculateMaxTextPages();
}
// private methods
bool updateMetadataXmlNameAndDocSize();
QString pagesSizeString() const;
QString namePaperSize(double inchesWidth, double inchesHeight) const;
2020-02-20 17:45:46 +00:00
QString localizedSize(const QSizeF size) const;
qulonglong calculateMemoryToFree();
void cleanupPixmapMemory();
void cleanupPixmapMemory(qulonglong memoryToFree);
AllocatedPixmap *searchLowestPriorityPixmap(bool unloadableOnly = false, bool thenRemoveIt = false, DocumentObserver *observer = nullptr /* any */);
void calculateMaxTextPages();
qulonglong getTotalMemory();
qulonglong getFreeMemory(qulonglong *freeSwap = nullptr);
bool loadDocumentInfo(LoadDocumentInfoFlags loadWhat);
bool loadDocumentInfo(QFile &infoFile, LoadDocumentInfoFlags loadWhat);
void loadViewsInfo(View *view, const QDomElement &e);
void saveViewsInfo(View *view, QDomElement &e) const;
QUrl giveAbsoluteUrl(const QString &fileName) const;
bool openRelativeFile(const QString &fileName);
Generator *loadGeneratorLibrary(const KPluginMetaData &service);
void loadAllGeneratorLibraries();
void loadServiceList(const QVector<KPluginMetaData> &offers);
void unloadGenerator(const GeneratorInfo &info);
void cacheExportFormats();
void setRotationInternal(int r, bool notify);
ConfigInterface *generatorConfig(GeneratorInfo &info);
SaveInterface *generatorSave(GeneratorInfo &info);
Document::OpenResult openDocumentInternal(const KPluginMetaData &offer, bool isstdin, const QString &docFile, const QByteArray &filedata, const QString &password);
static ArchiveData *unpackDocumentArchive(const QString &archivePath);
2014-09-17 22:30:39 +00:00
bool savePageDocumentInfo(QTemporaryFile *infoFile, int what) const;
DocumentViewport nextDocumentViewport() const;
void notifyAnnotationChanges(int page);
void notifyFormChanges(int page);
bool canAddAnnotationsNatively() const;
bool canModifyExternalAnnotations() const;
bool canRemoveExternalAnnotations() const;
2015-01-29 19:55:57 +00:00
OKULARCORE_EXPORT static QString docDataFileName(const QUrl &url, qint64 document_size);
bool cancelRenderingBecauseOf(PixmapRequest *executingRequest, PixmapRequest *newRequest);
// Methods that implement functionality needed by undo commands
void performAddPageAnnotation(int page, Annotation *annotation);
void performRemovePageAnnotation(int page, Annotation *annotation);
void performModifyPageAnnotation(int page, Annotation *annotation, bool appearanceChanged);
void performSetAnnotationContents(const QString &newContents, Annotation *annot, int pageNumber);
void recalculateForms();
// private slots
void saveDocumentInfo() const;
void slotTimedMemoryCheck();
void sendGeneratorPixmapRequest();
void rotationFinished(int page, Okular::Page *okularPage);
void slotFontReadingProgress(int page);
void fontReadingGotFont(const Okular::FontInfo &font);
void slotGeneratorConfigChanged();
void refreshPixmaps(int);
void _o_configChanged();
void doContinueDirectionMatchSearch(void *doContinueDirectionMatchSearchStruct);
2014-05-09 22:49:01 +00:00
void doContinueAllDocumentSearch(void *pagesToNotifySet, void *pageMatchesMap, int currentPage, int searchID);
void doContinueGooglesDocumentSearch(void *pagesToNotifySet, void *pageMatchesMap, int currentPage, int searchID, const QStringList &words);
void doProcessSearchMatch(RegularAreaRect *match, RunningSearch *search, QSet<int> *pagesToNotify, int currentPage, int searchID, bool moveViewport, const QColor &color);
/**
* Executes a JavaScript script from the setInterval function.
*
* @since 1.9
*/
void executeScript(const QString &function);
// generators stuff
/**
* This method is used by the generators to signal the finish of
* the pixmap generation @p request.
*/
void requestDone(PixmapRequest *request);
void textGenerationDone(Page *page);
/**
* Sets the bounding box of the given @p page (in terms of upright orientation, i.e., Rotation0).
*/
void setPageBoundingBox(int page, const NormalizedRect &boundingBox);
/**
* Request a particular metadata of the Document itself (ie, not something
* depending on the document type/backend).
*/
2017-03-02 21:45:45 +00:00
QVariant documentMetaData(const Generator::DocumentMetaDataKey key, const QVariant &option) const;
/**
* Return whether the normalized rectangle @p rectOfInterest on page number @p rectPage
* is fully visible.
*/
bool isNormalizedRectangleFullyVisible(const Okular::NormalizedRect &rectOfInterest, int rectPage);
// For sync files
void loadSyncFile(const QString &filePath);
void clearAndWaitForRequests();
OKULARCORE_EXPORT static QString evaluateKeystrokeEventChange(const QString &oldVal, const QString &newVal, int selStart, int selEnd);
/*
* Executes a ScriptAction with the event passed as parameter.
*/
2019-10-05 18:18:28 +00:00
void executeScriptEvent(const std::shared_ptr<Event> &event, const Okular::ScriptAction *linkscript);
/*
* Find the corresponding page number for the form field passed as parameter.
*/
int findFieldPageNumber(Okular::FormField *field);
// member variables
Document *m_parent;
QPointer<QWidget> m_widget;
// find descriptors, mapped by ID (we handle multiple searches)
QMap<int, RunningSearch *> m_searches;
bool m_searchCancelled;
// needed because for remote documents docFileName is a local file and
// we want the remote url when the document refers to relativeNames
2015-01-29 19:55:57 +00:00
QUrl m_url;
// cached stuff
QString m_docFileName;
QString m_xmlFileName;
2014-09-17 22:30:39 +00:00
QTemporaryFile *m_tempFile;
qint64 m_docSize;
// viewport stuff
std::list<DocumentViewport> m_viewportHistory;
std::list<DocumentViewport>::iterator m_viewportIterator;
DocumentViewport m_nextDocumentViewport; // see Link::Goto for an explanation
QString m_nextDocumentDestination;
// observers / requests / allocator stuff
QSet<DocumentObserver *> m_observers;
std::list<PixmapRequest *> m_pixmapRequestsStack;
std::list<PixmapRequest *> m_executingPixmapRequests;
QMutex m_pixmapRequestsMutex;
std::list<AllocatedPixmap *> m_allocatedPixmaps;
qulonglong m_allocatedPixmapsTotalMemory;
QList<int> m_allocatedTextPagesFifo;
int m_maxAllocatedTextPages;
bool m_warnedOutOfMemory;
// the rotation applied to the document
Rotation m_rotation;
// the current size of the pages (if available), and the cache of the
// available page sizes
PageSize m_pageSize;
PageSize::List m_pageSizes;
// cache of the export formats
bool m_exportCached;
ExportFormat::List m_exportFormats;
ExportFormat m_exportToText;
// our bookmark manager
BookmarkManager *m_bookmarkManager;
// timers (memory checking / info saver)
QTimer *m_memCheckTimer;
QTimer *m_saveBookmarksTimer;
QHash<QString, GeneratorInfo> m_loadedGenerators;
Generator *m_generator;
QString m_generatorName;
Generator *m_walletGenerator;
bool m_generatorsLoaded;
QVector<Page *> m_pagesVector;
QVector<VisiblePageRect *> m_pageRects;
// cache of the mimetype we support
QStringList m_supportedMimeTypes;
PageController *m_pageController;
QEventLoop *m_closingLoop;
Scripter *m_scripter;
ArchiveData *m_archiveData;
QString m_archivedFileName;
QPointer<FontExtractionThread> m_fontThread;
bool m_fontsCached;
QSet<DocumentInfo::Key> m_documentInfoAskedKeys;
DocumentInfo m_documentInfo;
FontInfo::List m_fontsCache;
QSet<View *> m_views;
bool m_annotationEditingEnabled;
Add annotation resize functionality Usage: If you left-click an annotation, it gets selected. Resize handles appear on the selection rectangle. When cursor is moved over one of the 8 resize handles on the corners/edges, the cursor shape changes to indicate resize mode. Everywhere else on the annotation means "move", just as it was before resize feature was added. Pressing ESC or clicking an area outside the annotation cancels a selection. Pressing Del deletes a selected annotation. Feature is only applicable for annotation types AText, AStamp and AGeom. Implementation: It works by eventually changing AnnotationPrivate::m_boundary and notifying generator (i.e. poppler) about that change. Annotation state handling is shifted out of PageView into a new class MouseAnnotation (ui/pageviewmouseannotation.cpp). Some functionality not related to resizing but to annotation interaction in general is also shifted to class MouseAnnotation, to build a single place of responsiblity. Other changes: Add method Document::adjustPageAnnotation, backed by a QUndoCommand. class Okular::AdjustAnnotationCommand. Add Annotation::adjust and Annotation::canBeResized methods. Draw resize handles in PagePainter::paintCroppedPageOnPainter. Resize and move work -for types AText, AStamp and AGeom -on all pages of document -when viewport position changes -when zoom level changes -for all page rotations (0°, 90°, 180°, 270°) Selection is canceled -when currently selected annotation is deleted -on mouse click outside of currently selected annotation -ESC is pressed Viewport is shifted when mouse cursor during move/resize comes close to viewport border. Resize to negative is prevented. Tiny annotations are still selectable. If mouse is moved over an annotation type that we can focus, and the annotation is not yet focused, mouse cursor shape changes to arrow. If mouse cursor rests over an annotation A, while annotation B is focused, a tooltip for annotation A is shown. Selected Annotation is deleted when Del is pressed. Test for regressions: -Annotation interaction (focus, move, resize, start playback, ...) are only done in mode EnumMouseMode::Browse. -If mouse is moved over an annotation type where we can start an action, mouse cursor shape changes to pointing hand. -If mouse is moved over an annotation type that we can't interact with, mouse cursor shape stays a open hand. -If mouse cursor rests over an annotation of any type, a tooltip for that annotation is shown. -Grab/move scroll area (on left click + mouse move) is prevented, if mouse is over focused annotation, or over AMovie/AScreen/AFileAttachment annotation. -A double click on a annotation starts the "annotator". REVIEW: 127366 BUG: 177778 BUG: 314843 BUG: 358060
2017-03-19 22:16:06 +00:00
bool m_annotationBeingModified; // is an annotation currently being moved or resized?
QUndoStack *m_undoStack;
QDomNode m_prevPropsOfAnnotBeingModified;
// Since 0.21, we no longer support saving annotations and form data in
// the docdata/ directory and we ask the user to migrate them to an
// external file as soon as possible, otherwise the document will be
// shown in read-only mode. This flag is set if the docdata/ XML file
// for the current document contains any annotation or form.
bool m_docdataMigrationNeeded;
Update to synctex 1.19 Summary: This should prevent crashes when reloading some synctex-enabled pdf files created with newer versions of TeXLive. We also gain bugfixes, features and improved accuracy from the last 6 years of synctex development. Procedure followed: - svn co svn://tug.org/texlive/trunk/Build/source/texk/web2c/synctexdir - Check out revision 45150 - Update files present in core/synctex/* - Adapt Okular code to changes - Review and drop or update/apply old patches using quilt - Create missing patches for local synctex changes - New patch: Omit warning message when opening non-synctex pdf - Two new patches to fix more compiler warnings - New patch: Plug multiple leaks and prevent a segfault TODO for later: - Move sync file detection code to Okular to never call into synctex C code for non-synctex files - Evaluate feasibility of upstreaming all patches for TeXLive 2018 and using synctex as a library BUG: 383915 FIXED-IN: 17.12.0 Test Plan: - No crash in synctex on reloading empty.pdf from bugreport anymore. - Shift-clicking on a word in a simple pdf opens Kate with the corresponding tex line. - Forward and backward search in Kile seems to work. - Works with synctex files from both TeXLive 2015 and 2017. - PartTest::testForwardPDF still passes. - No additional memory leaks in autotests and with basic synctex and non-synctex usage of Okular. Reviewers: #okular, sander, #kile, aacid Reviewed By: #okular, aacid Subscribers: mludwig, aacid Tags: #okular Differential Revision: https://phabricator.kde.org/D7594
2017-08-28 22:29:46 +00:00
synctex_scanner_p m_synctex_scanner;
QString m_openError;
// generator selection
static QVector<KPluginMetaData> availableGenerators();
static QVector<KPluginMetaData> configurableGenerators();
static KPluginMetaData generatorForMimeType(const QMimeType &type, QWidget *widget, const QVector<KPluginMetaData> &triedOffers = QVector<KPluginMetaData>());
// overrides the editor command (for example with a command from the command line)
QString editorCommandOverride;
};
class DocumentInfoPrivate
{
public:
QMap<QString, QString> values; // key -> value
QMap<QString, QString> titles; // key -> title For the custom keys
};
}
#endif
/* kate: replace-tabs on; indent-width 4; */