2021-05-24 07:25:56 +00:00
/*
SPDX - FileCopyrightText : 2004 - 2005 Enrico Ros < eros . kde @ email . it >
SPDX - FileCopyrightText : 2004 - 2008 Albert Astals Cid < aacid @ kde . org >
2021-06-03 18:33:18 +00:00
Work sponsored by the LiMux project of the city of Munich :
2021-05-24 17:21:33 +00:00
SPDX - FileCopyrightText : 2017 , 2018 Klarälvdalens Datakonsult AB a KDAB Group company < info @ kdab . com >
2021-05-24 07:25:56 +00:00
SPDX - License - Identifier : GPL - 2.0 - or - later
*/
2004-09-08 12:41:14 +00:00
2007-04-19 18:30:20 +00:00
# include "document.h"
2007-09-14 15:17:36 +00:00
# include "document_p.h"
2013-04-05 22:39:35 +00:00
# include "documentcommands_p.h"
2007-04-19 18:30:20 +00:00
2012-07-02 06:22:51 +00:00
# include <limits.h>
2018-02-21 22:56:43 +00:00
# include <memory>
2007-10-31 19:44:35 +00:00
# ifdef Q_OS_WIN
# define _WIN32_WINNT 0x0500
# include <windows.h>
2010-12-18 01:07:33 +00:00
# elif defined(Q_OS_FREEBSD)
2020-07-11 08:11:31 +00:00
// clang-format off
// FreeBSD really wants this include order
2010-12-18 01:07:33 +00:00
# include <sys/types.h>
2020-07-11 08:11:31 +00:00
# include <sys/sysctl.h>
// clang-format on
2010-12-18 01:07:33 +00:00
# include <vm/vm_param.h>
2007-10-31 19:44:35 +00:00
# endif
2004-10-06 00:05:49 +00:00
// qt/kde/system includes
2018-08-31 09:23:45 +00:00
# include <QApplication>
# include <QDesktopServices>
# include <QDir>
# include <QFile>
# include <QFileInfo>
# include <QLabel>
2013-04-05 22:22:48 +00:00
# include <QMap>
2014-10-08 11:48:01 +00:00
# include <QMimeDatabase>
2015-08-12 14:17:20 +00:00
# include <QPageSize>
2018-08-31 09:23:45 +00:00
# include <QPrintDialog>
2020-01-26 11:40:33 +00:00
# include <QRegularExpression>
2020-03-01 09:32:27 +00:00
# include <QScreen>
2014-10-08 22:17:53 +00:00
# include <QStack>
2015-08-12 14:17:20 +00:00
# include <QStandardPaths>
2020-07-08 11:54:37 +00:00
# include <QTemporaryFile>
2018-08-31 09:23:45 +00:00
# include <QTextStream>
# include <QTimer>
2013-04-05 22:39:35 +00:00
# include <QUndoCommand>
2020-03-01 09:32:27 +00:00
# include <QWindow>
2015-08-12 14:17:20 +00:00
# include <QtAlgorithms>
2006-09-21 08:45:36 +00:00
2022-03-09 23:14:02 +00:00
# include <KApplicationTrader>
2020-07-08 11:54:37 +00:00
# include <KAuthorized>
# include <KConfigDialog>
2015-08-12 14:17:20 +00:00
# include <KFormat>
2015-01-29 19:55:57 +00:00
# include <KIO/Global>
2023-03-21 21:07:53 +00:00
# include <KIO/JobUiDelegateFactory>
# include <KIO/OpenUrlJob>
2015-11-28 19:24:41 +00:00
# include <KLocalizedString>
2020-07-08 11:54:37 +00:00
# include <KMacroExpander>
2015-11-28 19:24:41 +00:00
# include <KPluginMetaData>
2020-07-08 11:54:37 +00:00
# include <KProcess>
2016-08-03 14:01:30 +00:00
# include <KRun>
2020-07-08 11:54:37 +00:00
# include <KShell>
2016-01-30 16:31:01 +00:00
# include <Kdelibs4Migration>
2023-03-21 21:07:53 +00:00
# include <kio_version.h>
2008-11-15 14:15:31 +00:00
# include <kzip.h>
2004-09-08 12:41:14 +00:00
// local includes
2007-04-20 11:26:05 +00:00
# include "action.h"
2007-07-17 18:10:25 +00:00
# include "annotations.h"
# include "annotations_p.h"
2007-02-05 00:49:40 +00:00
# include "audioplayer.h"
2006-12-27 16:04:49 +00:00
# include "bookmarkmanager.h"
2007-07-10 22:52:25 +00:00
# include "chooseenginedialog_p.h"
2007-07-31 10:19:48 +00:00
# include "debug_p.h"
2013-06-03 20:46:41 +00:00
# include "form.h"
2007-04-20 17:26:04 +00:00
# include "generator_p.h"
2007-01-03 14:30:48 +00:00
# include "interfaces/configinterface.h"
# include "interfaces/guiinterface.h"
# include "interfaces/printinterface.h"
2008-03-11 23:40:59 +00:00
# include "interfaces/saveinterface.h"
2011-10-17 19:56:45 +00:00
# include "misc.h"
2005-01-02 14:55:14 +00:00
# include "observer.h"
2004-09-08 12:41:14 +00:00
# include "page.h"
2007-04-14 19:58:17 +00:00
# include "page_p.h"
2007-03-24 10:47:22 +00:00
# include "pagecontroller_p.h"
2018-02-21 22:56:43 +00:00
# include "script/event_p.h"
2008-04-13 22:31:59 +00:00
# include "scripter.h"
2012-10-15 22:27:42 +00:00
# include "settings_core.h"
2007-01-02 17:45:32 +00:00
# include "sourcereference.h"
2008-09-22 13:41:28 +00:00
# include "sourcereference_p.h"
2008-10-19 18:01:16 +00:00
# include "texteditors_p.h"
2012-11-08 20:29:09 +00:00
# include "tile.h"
2012-08-17 18:23:58 +00:00
# include "tilesmanager_p.h"
2014-01-13 00:15:55 +00:00
# include "utils.h"
2008-11-15 14:15:31 +00:00
# include "utils_p.h"
2008-04-27 11:05:59 +00:00
# include "view.h"
# include "view_p.h"
2004-09-09 13:25:40 +00:00
2007-01-29 14:28:58 +00:00
# include <config-okular.h>
2018-05-30 08:55:17 +00:00
# if HAVE_MALLOC_TRIM
# include "malloc.h"
# endif
2006-09-21 08:45:36 +00:00
using namespace Okular ;
2005-01-20 17:33:05 +00:00
struct AllocatedPixmap {
// owner of the page
2013-02-24 21:58:53 +00:00
DocumentObserver * observer ;
2005-01-20 17:33:05 +00:00
int page ;
2007-07-15 16:10:48 +00:00
qulonglong memory ;
2004-12-21 12:38:52 +00:00
// public constructor: initialize data
2013-02-24 21:58:53 +00:00
AllocatedPixmap ( DocumentObserver * o , int p , qulonglong m )
: observer ( o )
, page ( p )
, memory ( m )
{
}
2004-09-08 12:41:14 +00:00
} ;
2008-11-15 14:15:31 +00:00
struct ArchiveData {
ArchiveData ( )
{
}
2014-05-10 10:32:12 +00:00
QString originalFileName ;
2014-09-17 22:30:39 +00:00
QTemporaryFile document ;
QTemporaryFile metadataFile ;
2008-11-15 14:15:31 +00:00
} ;
2005-02-01 18:26:56 +00:00
struct RunningSearch {
2005-02-02 18:18:26 +00:00
// store search properties
int continueOnPage ;
2005-07-15 18:20:57 +00:00
RegularAreaRect continueOnMatch ;
2007-08-13 22:25:27 +00:00
QSet < int > highlightedPages ;
2005-02-02 18:18:26 +00:00
// fields related to previous searches (used for 'continueSearch')
QString cachedString ;
2006-09-21 08:45:36 +00:00
Document : : SearchType cachedType ;
2006-11-23 16:54:28 +00:00
Qt : : CaseSensitivity cachedCaseSensitivity ;
2007-09-07 13:13:50 +00:00
bool cachedViewportMove : 1 ;
2009-08-10 22:28:54 +00:00
bool isCurrentlySearching : 1 ;
2005-02-02 18:18:26 +00:00
QColor cachedColor ;
2014-05-10 09:33:21 +00:00
int pagesDone ;
2005-02-01 18:26:56 +00:00
} ;
2004-09-08 12:41:14 +00:00
# define foreachObserver(cmd) \
{ \
2013-02-24 21:58:53 +00:00
QSet < DocumentObserver * > : : const_iterator it = d - > m_observers . constBegin ( ) , end = d - > m_observers . constEnd ( ) ; \
2005-01-20 17:33:05 +00:00
for ( ; it ! = end ; + + it ) { \
( * it ) - > cmd ; \
} \
}
2004-09-08 12:41:14 +00:00
2007-07-12 20:04:56 +00:00
# define foreachObserverD(cmd) \
{ \
2013-02-24 21:58:53 +00:00
QSet < DocumentObserver * > : : const_iterator it = m_observers . constBegin ( ) , end = m_observers . constEnd ( ) ; \
2007-07-12 20:04:56 +00:00
for ( ; it ! = end ; + + it ) { \
( * it ) - > cmd ; \
} \
}
2007-07-23 17:12:20 +00:00
# define OKULAR_HISTORY_MAXSTEPS 100
# define OKULAR_HISTORY_SAVEDSTEPS 10
2020-03-03 00:15:19 +00:00
// how often to run slotTimedMemoryCheck
2023-02-10 14:50:22 +00:00
constexpr int kMemCheckTime = 2000 ; // in msec
// getFreeMemory is called every two seconds when checking to see if the system is low on memory. If this timeout was left at kMemCheckTime, half of these checks are useless (when okular is idle) since the cache is used when the cache is
// <=2 seconds old. This means that after the system is out of memory, up to 4 seconds (instead of 2) could go by before okular starts to free memory.
constexpr int kFreeMemCacheTimeout = kMemCheckTime - 100 ;
2020-03-03 00:15:19 +00:00
2007-01-02 19:05:49 +00:00
/***** Document ******/
2005-01-26 10:42:07 +00:00
2007-04-14 19:44:07 +00:00
QString DocumentPrivate : : pagesSizeString ( ) const
2007-01-02 19:05:49 +00:00
{
if ( m_generator ) {
if ( m_generator - > pagesSizeMetric ( ) ! = Generator : : None ) {
QSizeF size = m_parent - > allPagesSize ( ) ;
2020-06-02 23:24:26 +00:00
// Single page size
2007-01-02 19:05:49 +00:00
if ( size . isValid ( ) ) {
return localizedSize ( size ) ;
2022-03-08 10:10:43 +00:00
}
2020-06-02 23:24:26 +00:00
// Multiple page sizes
QString sizeString ;
QHash < QString , int > pageSizeFrequencies ;
// Compute frequencies of each page size
for ( int i = 0 ; i < m_pagesVector . count ( ) ; + + i ) {
const Page * p = m_pagesVector . at ( i ) ;
sizeString = localizedSize ( QSizeF ( p - > width ( ) , p - > height ( ) ) ) ;
pageSizeFrequencies [ sizeString ] = pageSizeFrequencies . value ( sizeString , 0 ) + 1 ;
}
// Figure out which page size is most frequent
int largestFrequencySeen = 0 ;
QString mostCommonPageSize = QString ( ) ;
QHash < QString , int > : : const_iterator i = pageSizeFrequencies . constBegin ( ) ;
while ( i ! = pageSizeFrequencies . constEnd ( ) ) {
if ( i . value ( ) > largestFrequencySeen ) {
largestFrequencySeen = i . value ( ) ;
mostCommonPageSize = i . key ( ) ;
}
+ + i ;
}
QString finalText = i18nc ( " @info %1 is a page size " , " Most pages are %1. " , mostCommonPageSize ) ;
return finalText ;
2007-01-02 19:05:49 +00:00
} else {
return QString ( ) ;
2022-03-08 10:10:43 +00:00
}
2007-01-02 19:05:49 +00:00
} else {
return QString ( ) ;
2022-03-08 10:10:43 +00:00
}
2007-01-02 19:05:49 +00:00
}
2005-01-03 00:45:47 +00:00
2012-12-31 10:14:54 +00:00
QString DocumentPrivate : : namePaperSize ( double inchesWidth , double inchesHeight ) const
{
2023-07-24 00:33:00 +00:00
const QPageLayout : : Orientation orientation = inchesWidth > inchesHeight ? QPageLayout : : Landscape : QPageLayout : : Portrait ;
2012-12-31 10:14:54 +00:00
2016-10-29 10:39:19 +00:00
const QSize pointsSize ( inchesWidth * 72.0 , inchesHeight * 72.0 ) ;
const QPageSize : : PageSizeId paperSize = QPageSize : : id ( pointsSize , QPageSize : : FuzzyOrientationMatch ) ;
2012-12-31 10:14:54 +00:00
2016-10-29 10:39:19 +00:00
const QString paperName = QPageSize : : name ( paperSize ) ;
2012-12-31 10:14:54 +00:00
2023-07-24 00:33:00 +00:00
if ( orientation = = QPageLayout : : Portrait ) {
2018-01-29 12:19:37 +00:00
return i18nc ( " paper type and orientation (eg: Portrait A4) " , " Portrait % 1 " , paperName) ;
2016-10-29 10:39:19 +00:00
} else {
2018-01-29 12:19:37 +00:00
return i18nc ( " paper type and orientation (eg: Portrait A4) " , " Landscape % 1 " , paperName) ;
2012-12-31 10:14:54 +00:00
}
}
2020-02-20 17:45:46 +00:00
QString DocumentPrivate : : localizedSize ( const QSizeF size ) const
2007-01-02 19:05:49 +00:00
{
double inchesWidth = 0 , inchesHeight = 0 ;
switch ( m_generator - > pagesSizeMetric ( ) ) {
case Generator : : Points :
inchesWidth = size . width ( ) / 72.0 ;
inchesHeight = size . height ( ) / 72.0 ;
break ;
2005-01-03 15:51:05 +00:00
2014-01-13 00:15:55 +00:00
case Generator : : Pixels : {
const QSizeF dpi = m_generator - > dpi ( ) ;
inchesWidth = size . width ( ) / dpi . width ( ) ;
inchesHeight = size . height ( ) / dpi . height ( ) ;
} break ;
2007-01-02 19:05:49 +00:00
case Generator : : None :
break ;
2005-01-21 20:05:36 +00:00
}
2015-01-29 20:48:47 +00:00
if ( QLocale : : system ( ) . measurementSystem ( ) = = QLocale : : ImperialSystem ) {
2012-12-31 10:14:54 +00:00
return i18nc ( " %1 is width, %2 is height, %3 is paper size name " , " %1 x %2 in (%3) " , inchesWidth, inchesHeight, namePaperSize(inchesWidth, inchesHeight)) ;
2007-01-02 19:05:49 +00:00
} else {
2012-12-31 10:14:54 +00:00
return i18nc ( " %1 is width, %2 is height, %3 is paper size name " , " %1 x %2 mm (%3) " , QString::number(inchesWidth * 25.4, 'd', 0), QString::number(inchesHeight * 25.4, 'd', 0), namePaperSize(inchesWidth, inchesHeight)) ;
2005-02-04 22:35:44 +00:00
}
2004-09-08 12:41:14 +00:00
}
2012-07-01 23:19:32 +00:00
qulonglong DocumentPrivate : : calculateMemoryToFree ( )
- Page/Link: tooltips for links backported
- Page: rotation does not switch height and width
- Document/Part/Generator:
1. Add API for attaching stuff to the interface: ActionCollection and the Navigation Panel
also add possibility to merge an XML .rc file with menu layout. Relevant functions are:
QString Generator::getXMLFile(), returns a QString with your .rc file name.
void Generator::setupGUI (KActionCollection* , QToolbox* ), add your components to the user interface
2. Supporting backend settings:
If during startup, backends which provide a configuration ([X-KDE-oKularHasInternalSettings]
set to true) are found, a menu item: configure backends is created, clicking on it results in
loading all the generators that have settings, but not those that dont. the Generator::addPages(KConfigDialog *dlg)
function should be overloaded by a generator and dlg->addPage should be used to add pages.
If a user opens a file that needs an already loaded generator, the already loaded one is used instead of loading another.
3. Error/Warning/Notice sending support, to send a notice/error/warning, add a relevant notice/error/warning(QString& txt ,int duration)
to the generator class, and sending a message to the user is as simple as emitting a signal!
4. Intercepting of events generated by the PageView is done by Generator::handleEvent(QEvent*), subclass it, do a switch on QEvent::type(), handle your
event and return true if pageview is to proceed with its handling or false if not.
5. Support configuring the KPrinter on the generator side, use Generator::canConfigurePrinter(), return true there, and you get a nonconfigured KPrinter in your
Generator::print()
6. PixmapRequest handling update:
a.) Generator::canGeneratePixmap is now Generator::canGeneratePixmap(bool async)
b.) Document::sendGeneratorRequests is a slot now
c.) Old way of sending pixmaps (Document::requestPixmaps(QValueList<PixmapRequest*> checking if we can generate pixmap if not, waiting for receiving)
is replaced with: requestPixmaps only queues the pixmap all checking if w can generate is done in sendGeneratorReqest, the sendGeneratorRequest is
run in three places:
1. in requestPixmaps when we receive a request
2. in requestDone if pixmapStack is not empty
3. sendGeneratorRequest, apart from removing invalid requests, takes the current request and if generator canGeratePixmap(request->async)
it removes the pixmap from stack and sends to generator if not, QTimer::singleshots to itself after 20ms, it ends when stack has no valid pixmap request
7. Added a commented out zoom field to PixmapGenerator, mightcome in handy sometime
- TextPage: add instructions that handle simplyfing the RegularAreaRect, no more double painted borders in selection rectangles, this rocks.
svn path=/trunk/playground/graphics/oKular/kpdf/; revision=445196
2005-08-10 16:14:39 +00:00
{
2007-01-02 19:05:49 +00:00
// [MEM] choose memory parameters based on configuration profile
2008-04-27 18:08:14 +00:00
qulonglong clipValue = 0 ;
qulonglong memoryToFree = 0 ;
2012-06-24 18:35:37 +00:00
2012-10-15 22:27:42 +00:00
switch ( SettingsCore : : memoryLevel ( ) ) {
case SettingsCore : : EnumMemoryLevel : : Low :
2008-04-27 18:08:14 +00:00
memoryToFree = m_allocatedPixmapsTotalMemory ;
break ;
- Page/Link: tooltips for links backported
- Page: rotation does not switch height and width
- Document/Part/Generator:
1. Add API for attaching stuff to the interface: ActionCollection and the Navigation Panel
also add possibility to merge an XML .rc file with menu layout. Relevant functions are:
QString Generator::getXMLFile(), returns a QString with your .rc file name.
void Generator::setupGUI (KActionCollection* , QToolbox* ), add your components to the user interface
2. Supporting backend settings:
If during startup, backends which provide a configuration ([X-KDE-oKularHasInternalSettings]
set to true) are found, a menu item: configure backends is created, clicking on it results in
loading all the generators that have settings, but not those that dont. the Generator::addPages(KConfigDialog *dlg)
function should be overloaded by a generator and dlg->addPage should be used to add pages.
If a user opens a file that needs an already loaded generator, the already loaded one is used instead of loading another.
3. Error/Warning/Notice sending support, to send a notice/error/warning, add a relevant notice/error/warning(QString& txt ,int duration)
to the generator class, and sending a message to the user is as simple as emitting a signal!
4. Intercepting of events generated by the PageView is done by Generator::handleEvent(QEvent*), subclass it, do a switch on QEvent::type(), handle your
event and return true if pageview is to proceed with its handling or false if not.
5. Support configuring the KPrinter on the generator side, use Generator::canConfigurePrinter(), return true there, and you get a nonconfigured KPrinter in your
Generator::print()
6. PixmapRequest handling update:
a.) Generator::canGeneratePixmap is now Generator::canGeneratePixmap(bool async)
b.) Document::sendGeneratorRequests is a slot now
c.) Old way of sending pixmaps (Document::requestPixmaps(QValueList<PixmapRequest*> checking if we can generate pixmap if not, waiting for receiving)
is replaced with: requestPixmaps only queues the pixmap all checking if w can generate is done in sendGeneratorReqest, the sendGeneratorRequest is
run in three places:
1. in requestPixmaps when we receive a request
2. in requestDone if pixmapStack is not empty
3. sendGeneratorRequest, apart from removing invalid requests, takes the current request and if generator canGeratePixmap(request->async)
it removes the pixmap from stack and sends to generator if not, QTimer::singleshots to itself after 20ms, it ends when stack has no valid pixmap request
7. Added a commented out zoom field to PixmapGenerator, mightcome in handy sometime
- TextPage: add instructions that handle simplyfing the RegularAreaRect, no more double painted borders in selection rectangles, this rocks.
svn path=/trunk/playground/graphics/oKular/kpdf/; revision=445196
2005-08-10 16:14:39 +00:00
2007-01-02 19:05:49 +00:00
case SettingsCore : : EnumMemoryLevel : : Normal : {
2008-04-27 18:08:14 +00:00
qulonglong thirdTotalMemory = getTotalMemory ( ) / 3 ;
qulonglong freeMemory = getFreeMemory ( ) ;
if ( m_allocatedPixmapsTotalMemory > thirdTotalMemory ) {
memoryToFree = m_allocatedPixmapsTotalMemory - thirdTotalMemory ;
2022-03-08 10:10:43 +00:00
}
2008-04-27 18:08:14 +00:00
if ( m_allocatedPixmapsTotalMemory > freeMemory ) {
clipValue = ( m_allocatedPixmapsTotalMemory - freeMemory ) / 2 ;
2022-03-08 10:10:43 +00:00
}
2020-07-10 22:15:05 +00:00
} break ;
2012-10-15 22:27:42 +00:00
case SettingsCore : : EnumMemoryLevel : : Aggressive : {
2008-04-27 18:08:14 +00:00
qulonglong freeMemory = getFreeMemory ( ) ;
if ( m_allocatedPixmapsTotalMemory > freeMemory ) {
clipValue = ( m_allocatedPixmapsTotalMemory - freeMemory ) / 2 ;
2022-03-08 10:10:43 +00:00
}
2020-07-10 22:15:05 +00:00
} break ;
2012-10-15 22:27:42 +00:00
case SettingsCore : : EnumMemoryLevel : : Greedy : {
2012-06-24 21:53:01 +00:00
qulonglong freeSwap ;
2012-06-24 18:35:37 +00:00
qulonglong freeMemory = getFreeMemory ( & freeSwap ) ;
const qulonglong memoryLimit = qMin ( qMax ( freeMemory , getTotalMemory ( ) / 2 ) , freeMemory + freeSwap ) ;
2012-03-08 23:12:20 +00:00
if ( m_allocatedPixmapsTotalMemory > memoryLimit ) {
clipValue = ( m_allocatedPixmapsTotalMemory - memoryLimit ) / 2 ;
2022-03-08 10:10:43 +00:00
}
2020-07-10 22:15:05 +00:00
} break ;
}
2007-01-02 19:05:49 +00:00
if ( clipValue > memoryToFree ) {
memoryToFree = clipValue ;
2022-03-08 10:10:43 +00:00
}
2007-01-02 19:05:49 +00:00
2012-07-01 23:19:32 +00:00
return memoryToFree ;
}
void DocumentPrivate : : cleanupPixmapMemory ( )
{
cleanupPixmapMemory ( calculateMemoryToFree ( ) ) ;
}
void DocumentPrivate : : cleanupPixmapMemory ( qulonglong memoryToFree )
{
2014-02-19 22:40:43 +00:00
if ( memoryToFree < 1 ) {
return ;
2022-03-08 10:10:43 +00:00
}
2012-11-08 18:21:09 +00:00
2014-02-19 22:40:43 +00:00
const int currentViewportPage = ( * m_viewportIterator ) . pageNumber ;
2012-11-08 13:58:08 +00:00
2014-02-19 22:40:43 +00:00
// Create a QMap of visible rects, indexed by page number
QMap < int , VisiblePageRect * > visibleRects ;
QVector < Okular : : VisiblePageRect * > : : const_iterator vIt = m_pageRects . constBegin ( ) , vEnd = m_pageRects . constEnd ( ) ;
for ( ; vIt ! = vEnd ; + + vIt ) {
visibleRects . insert ( ( * vIt ) - > pageNumber , ( * vIt ) ) ;
2022-03-08 10:10:43 +00:00
}
2012-07-01 23:05:02 +00:00
2014-02-19 22:40:43 +00:00
// Free memory starting from pages that are farthest from the current one
int pagesFreed = 0 ;
while ( memoryToFree > 0 ) {
AllocatedPixmap * p = searchLowestPriorityPixmap ( true , true ) ;
if ( ! p ) { // No pixmap to remove
break ;
2022-03-08 10:10:43 +00:00
}
2012-07-01 23:05:02 +00:00
2014-09-11 17:36:01 +00:00
qCDebug ( OkularCoreDebug ) . nospace ( ) < < " Evicting cache pixmap observer= " < < p - > observer < < " page= " < < p - > page ;
2014-02-19 22:40:43 +00:00
// m_allocatedPixmapsTotalMemory can't underflow because we always add or remove
// the memory used by the AllocatedPixmap so at most it can reach zero
m_allocatedPixmapsTotalMemory - = p - > memory ;
// Make sure memoryToFree does not underflow
if ( p - > memory > memoryToFree ) {
memoryToFree = 0 ;
} else {
memoryToFree - = p - > memory ;
2022-03-08 10:10:43 +00:00
}
2014-02-19 22:40:43 +00:00
pagesFreed + + ;
// delete pixmap
m_pagesVector . at ( p - > page ) - > deletePixmap ( p - > observer ) ;
// delete allocation descriptor
delete p ;
}
2012-08-13 04:09:05 +00:00
2014-02-19 22:40:43 +00:00
// If we're still on low memory, try to free individual tiles
2012-11-08 13:58:08 +00:00
2014-02-19 22:40:43 +00:00
// Store pages that weren't completely removed
2022-03-31 15:05:15 +00:00
std : : list < AllocatedPixmap * > pixmapsToKeep ;
2014-02-19 22:40:43 +00:00
while ( memoryToFree > 0 ) {
int clean_hits = 0 ;
2019-12-09 13:16:55 +00:00
for ( DocumentObserver * observer : qAsConst ( m_observers ) ) {
2014-02-19 22:40:43 +00:00
AllocatedPixmap * p = searchLowestPriorityPixmap ( false , true , observer ) ;
2012-11-08 13:58:08 +00:00
if ( ! p ) { // No pixmap to remove
2014-02-19 22:40:43 +00:00
continue ;
2022-03-08 10:10:43 +00:00
}
2012-08-13 04:09:05 +00:00
2014-02-19 22:40:43 +00:00
clean_hits + + ;
TilesManager * tilesManager = m_pagesVector . at ( p - > page ) - > d - > tilesManager ( observer ) ;
2012-08-20 01:43:59 +00:00
if ( tilesManager & & tilesManager - > totalMemory ( ) > 0 ) {
2012-11-08 18:21:09 +00:00
qulonglong memoryDiff = p - > memory ;
NormalizedRect visibleRect ;
2012-11-08 13:58:08 +00:00
if ( visibleRects . contains ( p - > page ) ) {
2012-11-08 18:21:09 +00:00
visibleRect = visibleRects [ p - > page ] - > rect ;
2022-03-08 10:10:43 +00:00
}
2012-11-08 13:58:08 +00:00
2012-11-08 18:21:09 +00:00
// Free non visible tiles
tilesManager - > cleanupPixmapMemory ( memoryToFree , visibleRect , currentViewportPage ) ;
2012-11-08 13:58:08 +00:00
2012-08-13 04:09:05 +00:00
p - > memory = tilesManager - > totalMemory ( ) ;
2012-11-08 13:58:08 +00:00
memoryDiff - = p - > memory ;
2012-11-08 18:21:09 +00:00
memoryToFree = ( memoryDiff < memoryToFree ) ? ( memoryToFree - memoryDiff ) : 0 ;
2012-11-08 13:58:08 +00:00
m_allocatedPixmapsTotalMemory - = memoryDiff ;
if ( p - > memory > 0 ) {
2022-03-31 15:05:15 +00:00
pixmapsToKeep . push_back ( p ) ;
2012-11-08 13:58:08 +00:00
} else {
delete p ;
2022-03-08 10:10:43 +00:00
}
2012-11-08 13:58:08 +00:00
} else {
2022-03-31 15:05:15 +00:00
pixmapsToKeep . push_back ( p ) ;
2022-03-08 10:10:43 +00:00
}
2012-08-13 04:09:05 +00:00
}
2012-11-08 13:58:08 +00:00
2014-02-19 22:40:43 +00:00
if ( clean_hits = = 0 ) {
break ;
2022-03-08 10:10:43 +00:00
}
2012-07-01 23:05:02 +00:00
}
2014-02-19 22:40:43 +00:00
2022-03-31 15:05:15 +00:00
m_allocatedPixmaps . splice ( m_allocatedPixmaps . end ( ) , pixmapsToKeep ) ;
2014-02-19 22:40:43 +00:00
// p--rintf("freeMemory A:[%d -%d = %d] \n", m_allocatedPixmaps.count() + pagesFreed, pagesFreed, m_allocatedPixmaps.count() );
2012-07-01 23:05:02 +00:00
}
2012-08-13 04:09:05 +00:00
2012-07-01 23:05:02 +00:00
/* Returns the next pixmap to evict from cache, or NULL if no suitable pixmap
2012-11-08 13:58:08 +00:00
* if found . If unloadableOnly is set , only unloadable pixmaps are returned . If
* thenRemoveIt is set , the pixmap is removed from m_allocatedPixmaps before
* returning it
*/
2013-02-24 21:58:53 +00:00
AllocatedPixmap * DocumentPrivate : : searchLowestPriorityPixmap ( bool unloadableOnly , bool thenRemoveIt , DocumentObserver * observer )
2012-07-01 23:05:02 +00:00
{
2022-03-31 15:05:15 +00:00
std : : list < AllocatedPixmap * > : : iterator pIt = m_allocatedPixmaps . begin ( ) ;
std : : list < AllocatedPixmap * > : : iterator pEnd = m_allocatedPixmaps . end ( ) ;
std : : list < AllocatedPixmap * > : : iterator farthestPixmap = pEnd ;
2012-07-01 23:05:02 +00:00
const int currentViewportPage = ( * m_viewportIterator ) . pageNumber ;
/* Find the pixmap that is farthest from the current viewport */
int maxDistance = - 1 ;
while ( pIt ! = pEnd ) {
const AllocatedPixmap * p = * pIt ;
2012-11-08 19:46:47 +00:00
// Filter by observer
2017-09-05 21:27:18 +00:00
if ( observer = = nullptr | | p - > observer = = observer ) {
2012-11-08 19:46:47 +00:00
const int distance = qAbs ( p - > page - currentViewportPage ) ;
2013-02-24 21:58:53 +00:00
if ( maxDistance < distance & & ( ! unloadableOnly | | p - > observer - > canUnloadPixmap ( p - > page ) ) ) {
2012-11-08 19:46:47 +00:00
maxDistance = distance ;
farthestPixmap = pIt ;
}
2007-01-02 19:05:49 +00:00
}
2012-07-01 23:05:02 +00:00
+ + pIt ;
2007-01-02 16:40:22 +00:00
}
2012-07-01 23:05:02 +00:00
/* No pixmap to remove */
if ( farthestPixmap = = pEnd ) {
2017-09-05 21:27:18 +00:00
return nullptr ;
2022-03-08 10:10:43 +00:00
}
2012-07-01 23:05:02 +00:00
AllocatedPixmap * selectedPixmap = * farthestPixmap ;
if ( thenRemoveIt ) {
m_allocatedPixmaps . erase ( farthestPixmap ) ;
2022-03-08 10:10:43 +00:00
}
2012-07-01 23:05:02 +00:00
return selectedPixmap ;
- Page/Link: tooltips for links backported
- Page: rotation does not switch height and width
- Document/Part/Generator:
1. Add API for attaching stuff to the interface: ActionCollection and the Navigation Panel
also add possibility to merge an XML .rc file with menu layout. Relevant functions are:
QString Generator::getXMLFile(), returns a QString with your .rc file name.
void Generator::setupGUI (KActionCollection* , QToolbox* ), add your components to the user interface
2. Supporting backend settings:
If during startup, backends which provide a configuration ([X-KDE-oKularHasInternalSettings]
set to true) are found, a menu item: configure backends is created, clicking on it results in
loading all the generators that have settings, but not those that dont. the Generator::addPages(KConfigDialog *dlg)
function should be overloaded by a generator and dlg->addPage should be used to add pages.
If a user opens a file that needs an already loaded generator, the already loaded one is used instead of loading another.
3. Error/Warning/Notice sending support, to send a notice/error/warning, add a relevant notice/error/warning(QString& txt ,int duration)
to the generator class, and sending a message to the user is as simple as emitting a signal!
4. Intercepting of events generated by the PageView is done by Generator::handleEvent(QEvent*), subclass it, do a switch on QEvent::type(), handle your
event and return true if pageview is to proceed with its handling or false if not.
5. Support configuring the KPrinter on the generator side, use Generator::canConfigurePrinter(), return true there, and you get a nonconfigured KPrinter in your
Generator::print()
6. PixmapRequest handling update:
a.) Generator::canGeneratePixmap is now Generator::canGeneratePixmap(bool async)
b.) Document::sendGeneratorRequests is a slot now
c.) Old way of sending pixmaps (Document::requestPixmaps(QValueList<PixmapRequest*> checking if we can generate pixmap if not, waiting for receiving)
is replaced with: requestPixmaps only queues the pixmap all checking if w can generate is done in sendGeneratorReqest, the sendGeneratorRequest is
run in three places:
1. in requestPixmaps when we receive a request
2. in requestDone if pixmapStack is not empty
3. sendGeneratorRequest, apart from removing invalid requests, takes the current request and if generator canGeratePixmap(request->async)
it removes the pixmap from stack and sends to generator if not, QTimer::singleshots to itself after 20ms, it ends when stack has no valid pixmap request
7. Added a commented out zoom field to PixmapGenerator, mightcome in handy sometime
- TextPage: add instructions that handle simplyfing the RegularAreaRect, no more double painted borders in selection rectangles, this rocks.
svn path=/trunk/playground/graphics/oKular/kpdf/; revision=445196
2005-08-10 16:14:39 +00:00
}
2007-07-15 16:10:48 +00:00
qulonglong DocumentPrivate : : getTotalMemory ( )
2004-09-08 12:41:14 +00:00
{
2007-07-15 16:10:48 +00:00
static qulonglong cachedValue = 0 ;
2007-01-02 19:05:49 +00:00
if ( cachedValue ) {
return cachedValue ;
2022-03-08 10:10:43 +00:00
}
2005-01-03 00:28:46 +00:00
2007-07-15 16:51:23 +00:00
# if defined(Q_OS_LINUX)
2007-01-02 19:05:49 +00:00
// if /proc/meminfo doesn't exist, return 128MB
2015-10-29 12:37:11 +00:00
QFile memFile ( QStringLiteral ( " /proc/meminfo " ) ) ;
2007-01-02 19:05:49 +00:00
if ( ! memFile . open ( QIODevice : : ReadOnly ) ) {
return ( cachedValue = 134217728 ) ;
2022-03-08 10:10:43 +00:00
}
2004-12-24 10:24:10 +00:00
2007-01-02 19:05:49 +00:00
QTextStream readStream ( & memFile ) ;
2012-06-19 21:38:54 +00:00
while ( true ) {
2007-01-02 19:05:49 +00:00
QString entry = readStream . readLine ( ) ;
2007-03-10 05:38:23 +00:00
if ( entry . isNull ( ) ) {
break ;
2022-03-08 10:10:43 +00:00
}
2015-10-29 12:37:11 +00:00
if ( entry . startsWith ( QLatin1String ( " MemTotal: " ) ) ) {
2016-07-11 20:07:57 +00:00
return ( cachedValue = ( Q_UINT64_C ( 1024 ) * entry . section ( QLatin1Char ( ' ' ) , - 2 , - 2 ) . toULongLong ( ) ) ) ;
2022-03-08 10:10:43 +00:00
}
2005-08-31 18:13:59 +00:00
}
2010-12-18 01:07:33 +00:00
# elif defined(Q_OS_FREEBSD)
qulonglong physmem ;
int mib [ ] = { CTL_HW , HW_PHYSMEM } ;
size_t len = sizeof ( physmem ) ;
if ( sysctl ( mib , 2 , & physmem , & len , NULL , 0 ) = = 0 )
return ( cachedValue = physmem ) ;
2007-07-15 16:43:24 +00:00
# elif defined(Q_OS_WIN)
MEMORYSTATUSEX stat ;
2009-02-08 02:36:04 +00:00
stat . dwLength = sizeof ( stat ) ;
2007-07-15 16:43:24 +00:00
GlobalMemoryStatusEx ( & stat ) ;
2007-09-15 14:05:08 +00:00
return ( cachedValue = stat . ullTotalPhys ) ;
2007-01-02 19:05:49 +00:00
# endif
return ( cachedValue = 134217728 ) ;
}
2005-01-18 16:43:36 +00:00
2012-06-24 18:35:37 +00:00
qulonglong DocumentPrivate : : getFreeMemory ( qulonglong * freeSwap )
2007-01-02 19:05:49 +00:00
{
2023-02-10 14:50:22 +00:00
static QDeadlineTimer cacheTimer ( 0 ) ;
2007-07-15 16:10:48 +00:00
static qulonglong cachedValue = 0 ;
2012-06-24 18:35:37 +00:00
static qulonglong cachedFreeSwap = 0 ;
2005-01-03 00:24:59 +00:00
2023-02-10 14:50:22 +00:00
if ( ! cacheTimer . hasExpired ( ) ) {
2012-06-24 21:53:01 +00:00
if ( freeSwap ) {
* freeSwap = cachedFreeSwap ;
2022-03-08 10:10:43 +00:00
}
2007-01-02 19:05:49 +00:00
return cachedValue ;
2012-06-24 18:35:37 +00:00
}
/* Initialize the returned free swap value to 0. It is overwritten if the
* actual value is available */
2012-06-24 21:53:01 +00:00
if ( freeSwap ) {
* freeSwap = 0 ;
2022-03-08 10:10:43 +00:00
}
2004-12-10 16:04:45 +00:00
2007-07-15 16:51:23 +00:00
# if defined(Q_OS_LINUX)
2007-01-02 19:05:49 +00:00
// if /proc/meminfo doesn't exist, return MEMORY FULL
2015-10-29 12:37:11 +00:00
QFile memFile ( QStringLiteral ( " /proc/meminfo " ) ) ;
2007-01-02 19:05:49 +00:00
if ( ! memFile . open ( QIODevice : : ReadOnly ) ) {
return 0 ;
2022-03-08 10:10:43 +00:00
}
2005-02-01 18:26:56 +00:00
2007-01-02 19:05:49 +00:00
// read /proc/meminfo and sum up the contents of 'MemFree', 'Buffers'
// and 'Cached' fields. consider swapped memory as used memory.
2007-07-15 16:10:48 +00:00
qulonglong memoryFree = 0 ;
2007-01-02 19:05:49 +00:00
QString entry ;
QTextStream readStream ( & memFile ) ;
2012-06-19 22:33:43 +00:00
static const int nElems = 5 ;
2015-10-29 12:37:11 +00:00
QString names [ nElems ] = { QStringLiteral ( " MemFree: " ) , QStringLiteral ( " Buffers: " ) , QStringLiteral ( " Cached: " ) , QStringLiteral ( " SwapFree: " ) , QStringLiteral ( " SwapTotal: " ) } ;
2012-06-19 22:33:43 +00:00
qulonglong values [ nElems ] = { 0 , 0 , 0 , 0 , 0 } ;
bool foundValues [ nElems ] = { false , false , false , false , false } ;
2007-01-02 19:05:49 +00:00
while ( true ) {
entry = readStream . readLine ( ) ;
2007-03-10 05:38:23 +00:00
if ( entry . isNull ( ) ) {
break ;
2022-03-08 10:10:43 +00:00
}
2012-06-19 22:33:43 +00:00
for ( int i = 0 ; i < nElems ; + + i ) {
if ( entry . startsWith ( names [ i ] ) ) {
2016-07-11 20:07:57 +00:00
values [ i ] = entry . section ( QLatin1Char ( ' ' ) , - 2 , - 2 ) . toULongLong ( & foundValues [ i ] ) ;
2012-06-19 22:33:43 +00:00
}
}
2007-01-02 19:05:49 +00:00
}
memFile . close ( ) ;
2012-06-19 22:33:43 +00:00
bool found = true ;
for ( int i = 0 ; found & & i < nElems ; + + i ) {
found = found & & foundValues [ i ] ;
2022-03-08 10:10:43 +00:00
}
2012-06-19 22:33:43 +00:00
if ( found ) {
2012-06-24 18:35:37 +00:00
/* MemFree + Buffers + Cached - SwapUsed =
* = MemFree + Buffers + Cached - ( SwapTotal - SwapFree ) =
* = MemFree + Buffers + Cached + SwapFree - SwapTotal */
2012-06-19 22:33:43 +00:00
memoryFree = values [ 0 ] + values [ 1 ] + values [ 2 ] + values [ 3 ] ;
if ( values [ 4 ] > memoryFree ) {
memoryFree = 0 ;
} else {
memoryFree - = values [ 4 ] ;
2022-03-08 10:10:43 +00:00
}
2012-06-24 18:08:18 +00:00
} else {
return 0 ;
}
2006-06-24 10:03:12 +00:00
2023-02-10 14:50:22 +00:00
cacheTimer . setRemainingTime ( kFreeMemCacheTimeout ) ;
2005-07-15 18:20:57 +00:00
2012-06-24 21:53:01 +00:00
if ( freeSwap ) {
* freeSwap = ( cachedFreeSwap = ( Q_UINT64_C ( 1024 ) * values [ 3 ] ) ) ;
2022-03-08 10:10:43 +00:00
}
2008-10-04 19:31:36 +00:00
return ( cachedValue = ( Q_UINT64_C ( 1024 ) * memoryFree ) ) ;
2010-12-18 01:07:33 +00:00
# elif defined(Q_OS_FREEBSD)
qulonglong cache , inact , free , psize ;
size_t cachelen , inactlen , freelen , psizelen ;
cachelen = sizeof ( cache ) ;
inactlen = sizeof ( inact ) ;
freelen = sizeof ( free ) ;
psizelen = sizeof ( psize ) ;
// sum up inactive, cached and free memory
if ( sysctlbyname ( " vm.stats.vm.v_cache_count " , & cache , & cachelen , NULL , 0 ) = = 0 & & sysctlbyname ( " vm.stats.vm.v_inactive_count " , & inact , & inactlen , NULL , 0 ) = = 0 & &
sysctlbyname ( " vm.stats.vm.v_free_count " , & free , & freelen , NULL , 0 ) = = 0 & & sysctlbyname ( " vm.stats.vm.v_page_size " , & psize , & psizelen , NULL , 0 ) = = 0 ) {
2023-02-10 14:50:22 +00:00
cacheTimer . setRemainingTime ( kFreeMemCacheTimeout ) ;
2010-12-18 01:07:33 +00:00
return ( cachedValue = ( cache + inact + free ) * psize ) ;
} else {
return 0 ;
}
2007-07-15 16:43:24 +00:00
# elif defined(Q_OS_WIN)
2007-07-15 16:47:55 +00:00
MEMORYSTATUSEX stat ;
2009-02-08 02:36:04 +00:00
stat . dwLength = sizeof ( stat ) ;
2007-07-15 16:47:55 +00:00
GlobalMemoryStatusEx ( & stat ) ;
2007-07-15 16:43:24 +00:00
2023-02-10 14:50:22 +00:00
cacheTimer . setRemainingTime ( kFreeMemCacheTimeout ) ;
2007-12-11 13:52:55 +00:00
2012-06-24 21:53:01 +00:00
if ( freeSwap )
* freeSwap = ( cachedFreeSwap = stat . ullAvailPageFile ) ;
2007-08-14 13:03:38 +00:00
return ( cachedValue = stat . ullAvailPhys ) ;
2007-01-02 19:05:49 +00:00
# else
// tell the memory is full.. will act as in LOW profile
return 0 ;
# endif
2004-09-08 12:41:14 +00:00
}
2014-09-09 14:08:32 +00:00
bool DocumentPrivate : : loadDocumentInfo ( LoadDocumentInfoFlags loadWhat )
2007-01-02 19:05:49 +00:00
// note: load data and stores it internally (document or pages). observers
// are still uninitialized at this point so don't access them
2004-09-08 12:41:14 +00:00
{
2014-09-11 17:36:01 +00:00
// qCDebug(OkularCoreDebug).nospace() << "Using '" << d->m_xmlFileName << "' as document info file.";
2007-01-12 22:49:14 +00:00
if ( m_xmlFileName . isEmpty ( ) ) {
2014-09-09 14:08:32 +00:00
return false ;
2022-03-08 10:10:43 +00:00
}
2007-01-12 22:49:14 +00:00
2014-05-10 09:35:33 +00:00
QFile infoFile ( m_xmlFileName ) ;
2014-09-09 14:08:32 +00:00
return loadDocumentInfo ( infoFile , loadWhat ) ;
2008-11-15 00:34:03 +00:00
}
2014-09-09 14:08:32 +00:00
bool DocumentPrivate : : loadDocumentInfo ( QFile & infoFile , LoadDocumentInfoFlags loadWhat )
2008-11-15 00:34:03 +00:00
{
2007-01-02 19:05:49 +00:00
if ( ! infoFile . exists ( ) | | ! infoFile . open ( QIODevice : : ReadOnly ) ) {
2014-09-09 14:08:32 +00:00
return false ;
2022-03-08 10:10:43 +00:00
}
2004-12-17 17:14:46 +00:00
2007-01-02 19:05:49 +00:00
// Load DOM from XML file
2015-10-29 12:37:11 +00:00
QDomDocument doc ( QStringLiteral ( " documentInfo " ) ) ;
2007-01-02 19:05:49 +00:00
if ( ! doc . setContent ( & infoFile ) ) {
2014-09-11 17:36:01 +00:00
qCDebug ( OkularCoreDebug ) < < " Can't load XML pair! Check for broken xml. " ;
2007-01-02 19:05:49 +00:00
infoFile . close ( ) ;
2014-09-09 14:08:32 +00:00
return false ;
2005-02-12 17:42:54 +00:00
}
2007-01-02 19:05:49 +00:00
infoFile . close ( ) ;
2004-12-17 17:14:46 +00:00
2007-01-02 19:05:49 +00:00
QDomElement root = doc . documentElement ( ) ;
2017-09-10 09:51:56 +00:00
2015-10-29 12:37:11 +00:00
if ( root . tagName ( ) ! = QLatin1String ( " documentInfo " ) ) {
2014-09-09 14:08:32 +00:00
return false ;
2022-03-08 10:10:43 +00:00
}
2007-01-02 19:05:49 +00:00
2014-09-09 14:08:32 +00:00
bool loadedAnything = false ; // set if something gets actually loaded
2007-01-02 19:05:49 +00:00
// Parse the DOM tree
QDomNode topLevelNode = root . firstChild ( ) ;
while ( topLevelNode . isElement ( ) ) {
QString catName = topLevelNode . toElement ( ) . tagName ( ) ;
2004-12-24 10:24:10 +00:00
2007-01-02 19:05:49 +00:00
// Restore page attributes (bookmark, annotations, ...) from the DOM
2017-09-10 09:51:56 +00:00
if ( catName = = QLatin1String ( " pageList " ) & & ( loadWhat & LoadPageInfo ) ) {
2007-01-02 19:05:49 +00:00
QDomNode pageNode = topLevelNode . firstChild ( ) ;
while ( pageNode . isElement ( ) ) {
QDomElement pageElement = pageNode . toElement ( ) ;
2015-10-29 12:37:11 +00:00
if ( pageElement . hasAttribute ( QStringLiteral ( " number " ) ) ) {
2007-01-02 19:05:49 +00:00
// get page number (node's attribute)
bool ok ;
2015-10-29 12:37:11 +00:00
int pageNumber = pageElement . attribute ( QStringLiteral ( " number " ) ) . toInt ( & ok ) ;
2007-01-02 19:05:49 +00:00
// pass the domElement to the right page, to read config data from
if ( ok & & pageNumber > = 0 & & pageNumber < ( int ) m_pagesVector . count ( ) ) {
2014-09-09 14:08:32 +00:00
if ( m_pagesVector [ pageNumber ] - > d - > restoreLocalContents ( pageElement ) ) {
loadedAnything = true ;
2022-03-08 10:10:43 +00:00
}
2014-09-09 14:08:32 +00:00
}
2007-01-02 19:05:49 +00:00
}
pageNode = pageNode . nextSibling ( ) ;
2005-01-28 18:18:48 +00:00
}
}
2007-01-02 19:05:49 +00:00
// Restore 'general info' from the DOM
2017-09-10 09:51:56 +00:00
else if ( catName = = QLatin1String ( " generalInfo " ) & & ( loadWhat & LoadGeneralInfo ) ) {
2007-01-02 19:05:49 +00:00
QDomNode infoNode = topLevelNode . firstChild ( ) ;
while ( infoNode . isElement ( ) ) {
QDomElement infoElement = infoNode . toElement ( ) ;
2005-01-21 20:05:36 +00:00
2007-01-02 19:05:49 +00:00
// restore viewports history
2015-10-29 12:37:11 +00:00
if ( infoElement . tagName ( ) = = QLatin1String ( " history " ) ) {
2007-01-02 19:05:49 +00:00
// clear history
m_viewportHistory . clear ( ) ;
// append old viewports
QDomNode historyNode = infoNode . firstChild ( ) ;
while ( historyNode . isElement ( ) ) {
QDomElement historyElement = historyNode . toElement ( ) ;
2015-10-29 12:37:11 +00:00
if ( historyElement . hasAttribute ( QStringLiteral ( " viewport " ) ) ) {
QString vpString = historyElement . attribute ( QStringLiteral ( " viewport " ) ) ;
2007-01-02 19:05:49 +00:00
m_viewportIterator = m_viewportHistory . insert ( m_viewportHistory . end ( ) , DocumentViewport ( vpString ) ) ;
2014-09-09 14:08:32 +00:00
loadedAnything = true ;
2007-01-02 19:05:49 +00:00
}
historyNode = historyNode . nextSibling ( ) ;
}
2019-01-11 07:09:34 +00:00
// consistency check
2022-03-31 15:05:15 +00:00
if ( m_viewportHistory . empty ( ) ) {
2007-01-02 19:05:49 +00:00
m_viewportIterator = m_viewportHistory . insert ( m_viewportHistory . end ( ) , DocumentViewport ( ) ) ;
2022-03-08 10:10:43 +00:00
}
2015-10-29 12:37:11 +00:00
} else if ( infoElement . tagName ( ) = = QLatin1String ( " rotation " ) ) {
2007-11-01 16:15:34 +00:00
QString str = infoElement . text ( ) ;
bool ok = true ;
int newrotation = ! str . isEmpty ( ) ? ( str . toInt ( & ok ) % 4 ) : 0 ;
if ( ok & & newrotation ! = 0 ) {
setRotationInternal ( newrotation , false ) ;
2014-09-09 14:08:32 +00:00
loadedAnything = true ;
2007-11-01 16:15:34 +00:00
}
2015-10-29 12:37:11 +00:00
} else if ( infoElement . tagName ( ) = = QLatin1String ( " views " ) ) {
2008-05-31 21:13:15 +00:00
QDomNode viewNode = infoNode . firstChild ( ) ;
while ( viewNode . isElement ( ) ) {
QDomElement viewElement = viewNode . toElement ( ) ;
2015-10-29 12:37:11 +00:00
if ( viewElement . tagName ( ) = = QLatin1String ( " view " ) ) {
const QString viewName = viewElement . attribute ( QStringLiteral ( " name " ) ) ;
2019-12-09 13:16:55 +00:00
for ( View * view : qAsConst ( m_views ) ) {
2008-05-31 21:13:15 +00:00
if ( view - > name ( ) = = viewName ) {
loadViewsInfo ( view , viewElement ) ;
2014-09-09 14:08:32 +00:00
loadedAnything = true ;
2008-05-31 21:13:15 +00:00
break ;
}
}
}
viewNode = viewNode . nextSibling ( ) ;
}
}
2007-01-02 19:05:49 +00:00
infoNode = infoNode . nextSibling ( ) ;
}
}
2005-01-21 20:05:36 +00:00
2007-01-02 19:05:49 +00:00
topLevelNode = topLevelNode . nextSibling ( ) ;
} // </documentInfo>
2014-09-09 14:08:32 +00:00
return loadedAnything ;
2004-09-08 12:41:14 +00:00
}
2008-05-31 21:13:15 +00:00
void DocumentPrivate : : loadViewsInfo ( View * view , const QDomElement & e )
{
QDomNode viewNode = e . firstChild ( ) ;
while ( viewNode . isElement ( ) ) {
QDomElement viewElement = viewNode . toElement ( ) ;
2015-10-29 12:37:11 +00:00
if ( viewElement . tagName ( ) = = QLatin1String ( " zoom " ) ) {
const QString valueString = viewElement . attribute ( QStringLiteral ( " value " ) ) ;
2008-05-31 21:13:15 +00:00
bool newzoom_ok = true ;
const double newzoom = ! valueString . isEmpty ( ) ? valueString . toDouble ( & newzoom_ok ) : 1.0 ;
2008-07-22 18:14:44 +00:00
if ( newzoom_ok & & newzoom ! = 0 & & view - > supportsCapability ( View : : Zoom ) & & ( view - > capabilityFlags ( View : : Zoom ) & ( View : : CapabilityRead | View : : CapabilitySerializable ) ) ) {
2008-05-31 21:13:15 +00:00
view - > setCapability ( View : : Zoom , newzoom ) ;
}
2015-10-29 12:37:11 +00:00
const QString modeString = viewElement . attribute ( QStringLiteral ( " mode " ) ) ;
2008-05-31 21:13:15 +00:00
bool newmode_ok = true ;
const int newmode = ! modeString . isEmpty ( ) ? modeString . toInt ( & newmode_ok ) : 2 ;
2008-07-22 18:14:44 +00:00
if ( newmode_ok & & view - > supportsCapability ( View : : ZoomModality ) & & ( view - > capabilityFlags ( View : : ZoomModality ) & ( View : : CapabilityRead | View : : CapabilitySerializable ) ) ) {
2008-05-31 21:13:15 +00:00
view - > setCapability ( View : : ZoomModality , newmode ) ;
}
2020-02-21 16:45:03 +00:00
} else if ( viewElement . tagName ( ) = = QLatin1String ( " viewMode " ) ) {
const QString modeString = viewElement . attribute ( QStringLiteral ( " mode " ) ) ;
2019-09-18 07:40:06 +00:00
bool newmode_ok = true ;
const int newmode = ! modeString . isEmpty ( ) ? modeString . toInt ( & newmode_ok ) : 2 ;
if ( newmode_ok & & view - > supportsCapability ( View : : ViewModeModality ) & & ( view - > capabilityFlags ( View : : ViewModeModality ) & ( View : : CapabilityRead | View : : CapabilitySerializable ) ) ) {
view - > setCapability ( View : : ViewModeModality , newmode ) ;
}
2020-02-21 16:45:03 +00:00
} else if ( viewElement . tagName ( ) = = QLatin1String ( " continuous " ) ) {
const QString modeString = viewElement . attribute ( QStringLiteral ( " mode " ) ) ;
2019-09-18 07:40:06 +00:00
bool newmode_ok = true ;
const int newmode = ! modeString . isEmpty ( ) ? modeString . toInt ( & newmode_ok ) : 2 ;
if ( newmode_ok & & view - > supportsCapability ( View : : Continuous ) & & ( view - > capabilityFlags ( View : : Continuous ) & ( View : : CapabilityRead | View : : CapabilitySerializable ) ) ) {
view - > setCapability ( View : : Continuous , newmode ) ;
}
2020-02-21 16:45:03 +00:00
} else if ( viewElement . tagName ( ) = = QLatin1String ( " trimMargins " ) ) {
const QString valueString = viewElement . attribute ( QStringLiteral ( " value " ) ) ;
2019-09-18 07:40:06 +00:00
bool newmode_ok = true ;
const int newmode = ! valueString . isEmpty ( ) ? valueString . toInt ( & newmode_ok ) : 2 ;
if ( newmode_ok & & view - > supportsCapability ( View : : TrimMargins ) & & ( view - > capabilityFlags ( View : : TrimMargins ) & ( View : : CapabilityRead | View : : CapabilitySerializable ) ) ) {
view - > setCapability ( View : : TrimMargins , newmode ) ;
}
}
2008-05-31 21:13:15 +00:00
viewNode = viewNode . nextSibling ( ) ;
}
}
void DocumentPrivate : : saveViewsInfo ( View * view , QDomElement & e ) const
{
2008-07-22 18:14:44 +00:00
if ( view - > supportsCapability ( View : : Zoom ) & & ( view - > capabilityFlags ( View : : Zoom ) & ( View : : CapabilityRead | View : : CapabilitySerializable ) ) & & view - > supportsCapability ( View : : ZoomModality ) & &
2008-05-31 21:13:15 +00:00
( view - > capabilityFlags ( View : : ZoomModality ) & ( View : : CapabilityRead | View : : CapabilitySerializable ) ) ) {
2015-10-29 12:37:11 +00:00
QDomElement zoomEl = e . ownerDocument ( ) . createElement ( QStringLiteral ( " zoom " ) ) ;
2008-05-31 21:13:15 +00:00
e . appendChild ( zoomEl ) ;
bool ok = true ;
const double zoom = view - > capability ( View : : Zoom ) . toDouble ( & ok ) ;
if ( ok & & zoom ! = 0 ) {
2015-10-29 12:37:11 +00:00
zoomEl . setAttribute ( QStringLiteral ( " value " ) , QString : : number ( zoom ) ) ;
2008-05-31 21:13:15 +00:00
}
const int mode = view - > capability ( View : : ZoomModality ) . toInt ( & ok ) ;
if ( ok ) {
2015-10-29 12:37:11 +00:00
zoomEl . setAttribute ( QStringLiteral ( " mode " ) , mode ) ;
2008-05-31 21:13:15 +00:00
}
}
2019-09-18 07:40:06 +00:00
if ( view - > supportsCapability ( View : : Continuous ) & & ( view - > capabilityFlags ( View : : Continuous ) & ( View : : CapabilityRead | View : : CapabilitySerializable ) ) ) {
2020-02-21 16:45:03 +00:00
QDomElement contEl = e . ownerDocument ( ) . createElement ( QStringLiteral ( " continuous " ) ) ;
2019-09-18 07:40:06 +00:00
e . appendChild ( contEl ) ;
const bool mode = view - > capability ( View : : Continuous ) . toBool ( ) ;
2020-02-21 16:45:03 +00:00
contEl . setAttribute ( QStringLiteral ( " mode " ) , mode ) ;
2019-09-18 07:40:06 +00:00
}
if ( view - > supportsCapability ( View : : ViewModeModality ) & & ( view - > capabilityFlags ( View : : ViewModeModality ) & ( View : : CapabilityRead | View : : CapabilitySerializable ) ) ) {
2020-02-21 16:45:03 +00:00
QDomElement viewEl = e . ownerDocument ( ) . createElement ( QStringLiteral ( " viewMode " ) ) ;
2019-09-18 07:40:06 +00:00
e . appendChild ( viewEl ) ;
bool ok = true ;
const int mode = view - > capability ( View : : ViewModeModality ) . toInt ( & ok ) ;
if ( ok ) {
2020-02-21 16:45:03 +00:00
viewEl . setAttribute ( QStringLiteral ( " mode " ) , mode ) ;
2019-09-18 07:40:06 +00:00
}
}
if ( view - > supportsCapability ( View : : TrimMargins ) & & ( view - > capabilityFlags ( View : : TrimMargins ) & ( View : : CapabilityRead | View : : CapabilitySerializable ) ) ) {
2020-02-21 16:45:03 +00:00
QDomElement contEl = e . ownerDocument ( ) . createElement ( QStringLiteral ( " trimMargins " ) ) ;
2019-09-18 07:40:06 +00:00
e . appendChild ( contEl ) ;
const bool value = view - > capability ( View : : TrimMargins ) . toBool ( ) ;
2020-02-21 16:45:03 +00:00
contEl . setAttribute ( QStringLiteral ( " value " ) , value ) ;
2019-09-18 07:40:06 +00:00
}
2008-05-31 21:13:15 +00:00
}
2016-01-30 19:00:25 +00:00
QUrl DocumentPrivate : : giveAbsoluteUrl ( const QString & fileName ) const
2005-01-15 01:08:35 +00:00
{
2007-05-21 19:51:30 +00:00
if ( ! QDir : : isRelativePath ( fileName ) ) {
2016-01-30 19:00:25 +00:00
return QUrl : : fromLocalFile ( fileName ) ;
2022-03-08 10:10:43 +00:00
}
2007-05-21 19:51:30 +00:00
2008-05-22 10:23:41 +00:00
if ( ! m_url . isValid ( ) ) {
2016-01-30 19:00:25 +00:00
return QUrl ( ) ;
2022-03-08 10:10:43 +00:00
}
2008-05-22 10:23:41 +00:00
2016-01-30 19:00:25 +00:00
return QUrl ( KIO : : upUrl ( m_url ) . toString ( ) + fileName ) ;
- Page/Link: tooltips for links backported
- Page: rotation does not switch height and width
- Document/Part/Generator:
1. Add API for attaching stuff to the interface: ActionCollection and the Navigation Panel
also add possibility to merge an XML .rc file with menu layout. Relevant functions are:
QString Generator::getXMLFile(), returns a QString with your .rc file name.
void Generator::setupGUI (KActionCollection* , QToolbox* ), add your components to the user interface
2. Supporting backend settings:
If during startup, backends which provide a configuration ([X-KDE-oKularHasInternalSettings]
set to true) are found, a menu item: configure backends is created, clicking on it results in
loading all the generators that have settings, but not those that dont. the Generator::addPages(KConfigDialog *dlg)
function should be overloaded by a generator and dlg->addPage should be used to add pages.
If a user opens a file that needs an already loaded generator, the already loaded one is used instead of loading another.
3. Error/Warning/Notice sending support, to send a notice/error/warning, add a relevant notice/error/warning(QString& txt ,int duration)
to the generator class, and sending a message to the user is as simple as emitting a signal!
4. Intercepting of events generated by the PageView is done by Generator::handleEvent(QEvent*), subclass it, do a switch on QEvent::type(), handle your
event and return true if pageview is to proceed with its handling or false if not.
5. Support configuring the KPrinter on the generator side, use Generator::canConfigurePrinter(), return true there, and you get a nonconfigured KPrinter in your
Generator::print()
6. PixmapRequest handling update:
a.) Generator::canGeneratePixmap is now Generator::canGeneratePixmap(bool async)
b.) Document::sendGeneratorRequests is a slot now
c.) Old way of sending pixmaps (Document::requestPixmaps(QValueList<PixmapRequest*> checking if we can generate pixmap if not, waiting for receiving)
is replaced with: requestPixmaps only queues the pixmap all checking if w can generate is done in sendGeneratorReqest, the sendGeneratorRequest is
run in three places:
1. in requestPixmaps when we receive a request
2. in requestDone if pixmapStack is not empty
3. sendGeneratorRequest, apart from removing invalid requests, takes the current request and if generator canGeratePixmap(request->async)
it removes the pixmap from stack and sends to generator if not, QTimer::singleshots to itself after 20ms, it ends when stack has no valid pixmap request
7. Added a commented out zoom field to PixmapGenerator, mightcome in handy sometime
- TextPage: add instructions that handle simplyfing the RegularAreaRect, no more double painted borders in selection rectangles, this rocks.
svn path=/trunk/playground/graphics/oKular/kpdf/; revision=445196
2005-08-10 16:14:39 +00:00
}
2007-04-14 19:44:07 +00:00
bool DocumentPrivate : : openRelativeFile ( const QString & fileName )
2004-12-05 22:47:32 +00:00
{
2020-12-02 14:27:49 +00:00
const QUrl newUrl = giveAbsoluteUrl ( fileName ) ;
if ( newUrl . isEmpty ( ) ) {
2007-01-02 19:05:49 +00:00
return false ;
2022-03-08 10:10:43 +00:00
}
2004-12-11 17:25:03 +00:00
2020-12-02 14:27:49 +00:00
qCDebug ( OkularCoreDebug ) . nospace ( ) < < " openRelativeFile: ' " < < newUrl < < " ' " ;
2004-12-05 22:47:32 +00:00
2022-03-18 21:35:45 +00:00
Q_EMIT m_parent - > openUrl ( newUrl ) ;
2020-12-02 14:27:49 +00:00
return m_url = = newUrl ;
2005-06-13 15:46:23 +00:00
}
2015-11-28 19:24:41 +00:00
Generator * DocumentPrivate : : loadGeneratorLibrary ( const KPluginMetaData & service )
2007-01-28 15:46:10 +00:00
{
2015-11-28 19:24:41 +00:00
KPluginLoader loader ( service . fileName ( ) ) ;
2017-01-14 18:15:26 +00:00
qCDebug ( OkularCoreDebug ) < < service . fileName ( ) ;
2015-07-31 10:15:09 +00:00
KPluginFactory * factory = loader . factory ( ) ;
2014-08-13 09:16:28 +00:00
if ( ! factory ) {
2015-11-28 19:24:41 +00:00
qCWarning ( OkularCoreDebug ) . nospace ( ) < < " Invalid plugin factory for " < < service . fileName ( ) < < " : " < < loader . errorString ( ) ;
2017-09-05 21:27:18 +00:00
return nullptr ;
2014-08-13 09:16:28 +00:00
}
2015-11-28 19:24:41 +00:00
Generator * plugin = factory - > create < Okular : : Generator > ( ) ;
2014-08-13 09:16:28 +00:00
2015-11-28 19:24:41 +00:00
GeneratorInfo info ( plugin , service ) ;
m_loadedGenerators . insert ( service . pluginId ( ) , info ) ;
2015-04-22 13:08:32 +00:00
return plugin ;
2007-01-28 15:46:10 +00:00
}
2007-04-14 19:44:07 +00:00
void DocumentPrivate : : loadAllGeneratorLibraries ( )
2007-01-28 15:46:10 +00:00
{
if ( m_generatorsLoaded ) {
return ;
2022-03-08 10:10:43 +00:00
}
2007-01-28 15:46:10 +00:00
2015-11-28 19:24:41 +00:00
loadServiceList ( availableGenerators ( ) ) ;
2007-01-28 15:46:10 +00:00
2015-11-28 19:24:41 +00:00
m_generatorsLoaded = true ;
2007-01-28 16:10:12 +00:00
}
2015-11-28 19:24:41 +00:00
void DocumentPrivate : : loadServiceList ( const QVector < KPluginMetaData > & offers )
2007-01-28 16:10:12 +00:00
{
2007-01-28 15:46:10 +00:00
int count = offers . count ( ) ;
if ( count < = 0 ) {
return ;
2022-03-08 10:10:43 +00:00
}
2007-01-28 15:46:10 +00:00
for ( int i = 0 ; i < count ; + + i ) {
2015-11-28 19:24:41 +00:00
QString id = offers . at ( i ) . pluginId ( ) ;
2007-01-28 15:46:10 +00:00
// don't load already loaded generators
2015-11-28 19:24:41 +00:00
QHash < QString , GeneratorInfo > : : const_iterator genIt = m_loadedGenerators . constFind ( id ) ;
2008-11-11 18:48:40 +00:00
if ( ! m_loadedGenerators . isEmpty ( ) & & genIt ! = m_loadedGenerators . constEnd ( ) ) {
2007-01-28 15:46:10 +00:00
continue ;
2022-03-08 10:10:43 +00:00
}
2007-01-28 15:46:10 +00:00
2007-12-02 20:57:24 +00:00
Generator * g = loadGeneratorLibrary ( offers . at ( i ) ) ;
2007-01-28 15:46:10 +00:00
( void ) g ;
}
}
2007-04-14 19:44:07 +00:00
void DocumentPrivate : : unloadGenerator ( const GeneratorInfo & info )
2007-01-30 12:43:43 +00:00
{
delete info . generator ;
}
2007-04-14 19:44:07 +00:00
void DocumentPrivate : : cacheExportFormats ( )
2007-03-10 20:51:50 +00:00
{
if ( m_exportCached ) {
return ;
2022-03-08 10:10:43 +00:00
}
2007-03-10 20:51:50 +00:00
const ExportFormat : : List formats = m_generator - > exportFormats ( ) ;
for ( int i = 0 ; i < formats . count ( ) ; + + i ) {
2014-09-11 13:18:05 +00:00
if ( formats . at ( i ) . mimeType ( ) . name ( ) = = QLatin1String ( " text/plain " ) ) {
2007-03-10 20:51:50 +00:00
m_exportToText = formats . at ( i ) ;
} else {
m_exportFormats . append ( formats . at ( i ) ) ;
2022-03-08 10:10:43 +00:00
}
2007-03-10 20:51:50 +00:00
}
m_exportCached = true ;
}
2007-11-25 12:49:30 +00:00
ConfigInterface * DocumentPrivate : : generatorConfig ( GeneratorInfo & info )
{
if ( info . configChecked ) {
return info . config ;
2022-03-08 10:10:43 +00:00
}
2007-11-25 12:49:30 +00:00
info . config = qobject_cast < Okular : : ConfigInterface * > ( info . generator ) ;
info . configChecked = true ;
return info . config ;
}
2008-03-11 23:40:59 +00:00
SaveInterface * DocumentPrivate : : generatorSave ( GeneratorInfo & info )
{
if ( info . saveChecked ) {
return info . save ;
2022-03-08 10:10:43 +00:00
}
2008-03-11 23:40:59 +00:00
info . save = qobject_cast < Okular : : SaveInterface * > ( info . generator ) ;
info . saveChecked = true ;
return info . save ;
}
2015-11-28 19:24:41 +00:00
Document : : OpenResult DocumentPrivate : : openDocumentInternal ( const KPluginMetaData & offer , bool isstdin , const QString & docFile , const QByteArray & filedata , const QString & password )
2008-11-09 14:21:20 +00:00
{
2015-11-28 19:24:41 +00:00
QString propName = offer . pluginId ( ) ;
2008-11-09 14:21:20 +00:00
QHash < QString , GeneratorInfo > : : const_iterator genIt = m_loadedGenerators . constFind ( propName ) ;
2017-09-05 21:27:18 +00:00
m_walletGenerator = nullptr ;
2008-11-09 14:21:20 +00:00
if ( genIt ! = m_loadedGenerators . constEnd ( ) ) {
m_generator = genIt . value ( ) . generator ;
} else {
m_generator = loadGeneratorLibrary ( offer ) ;
if ( ! m_generator ) {
2014-05-09 17:56:16 +00:00
return Document : : OpenError ;
2022-03-08 10:10:43 +00:00
}
2008-11-09 14:21:20 +00:00
genIt = m_loadedGenerators . constFind ( propName ) ;
Q_ASSERT ( genIt ! = m_loadedGenerators . constEnd ( ) ) ;
}
Q_ASSERT_X ( m_generator , " Document::load() " , " null generator?! " ) ;
m_generator - > d_func ( ) - > m_document = this ;
// connect error reporting signals
2020-02-10 23:27:46 +00:00
m_openError . clear ( ) ;
2020-02-10 23:17:05 +00:00
QMetaObject : : Connection errorToOpenErrorConnection = QObject : : connect ( m_generator , & Generator : : error , m_parent , [ this ] ( const QString & message ) { m_openError = message ; } ) ;
2015-10-29 12:37:11 +00:00
QObject : : connect ( m_generator , & Generator : : warning , m_parent , & Document : : warning ) ;
QObject : : connect ( m_generator , & Generator : : notice , m_parent , & Document : : notice ) ;
2008-11-09 14:21:20 +00:00
QApplication : : setOverrideCursor ( Qt : : WaitCursor ) ;
2014-01-13 00:15:55 +00:00
2022-01-27 18:24:52 +00:00
const QWindow * window = m_widget & & m_widget - > window ( ) ? m_widget - > window ( ) - > windowHandle ( ) : nullptr ;
const QSizeF dpi = Utils : : realDpi ( window ) ;
2014-09-11 17:36:01 +00:00
qCDebug ( OkularCoreDebug ) < < " Output DPI: " < < dpi ;
2014-05-15 18:18:22 +00:00
m_generator - > setDPI ( dpi ) ;
2014-01-13 00:15:55 +00:00
2014-05-09 17:56:16 +00:00
Document : : OpenResult openResult = Document : : OpenError ;
2008-11-09 14:21:20 +00:00
if ( ! isstdin ) {
2014-05-09 17:56:16 +00:00
openResult = m_generator - > loadDocumentWithPassword ( docFile , m_pagesVector , password ) ;
2008-11-09 14:21:20 +00:00
} else if ( ! filedata . isEmpty ( ) ) {
if ( m_generator - > hasFeature ( Generator : : ReadRawData ) ) {
2014-05-09 17:56:16 +00:00
openResult = m_generator - > loadDocumentFromDataWithPassword ( filedata , m_pagesVector , password ) ;
2008-11-09 14:21:20 +00:00
} else {
2014-09-17 22:30:39 +00:00
m_tempFile = new QTemporaryFile ( ) ;
2008-11-09 14:21:20 +00:00
if ( ! m_tempFile - > open ( ) ) {
delete m_tempFile ;
2017-09-05 21:27:18 +00:00
m_tempFile = nullptr ;
2008-11-09 14:21:20 +00:00
} else {
m_tempFile - > write ( filedata ) ;
QString tmpFileName = m_tempFile - > fileName ( ) ;
m_tempFile - > close ( ) ;
2014-05-09 17:56:16 +00:00
openResult = m_generator - > loadDocumentWithPassword ( tmpFileName , m_pagesVector , password ) ;
2008-11-09 14:21:20 +00:00
}
}
}
QApplication : : restoreOverrideCursor ( ) ;
2014-05-09 17:56:16 +00:00
if ( openResult ! = Document : : OpenSuccess | | m_pagesVector . size ( ) < = 0 ) {
2017-09-05 21:27:18 +00:00
m_generator - > d_func ( ) - > m_document = nullptr ;
QObject : : disconnect ( m_generator , nullptr , m_parent , nullptr ) ;
2020-02-10 23:17:05 +00:00
2015-02-09 21:24:46 +00:00
// TODO this is a bit of a hack, since basically means that
// you can only call walletDataForFile after calling openDocument
// but since in reality it's what happens I've decided not to refactor/break API
// One solution is just kill walletDataForFile and make OpenResult be an object
// where the wallet data is also returned when OpenNeedsPassword
2015-02-09 21:03:44 +00:00
m_walletGenerator = m_generator ;
2017-09-05 21:27:18 +00:00
m_generator = nullptr ;
2008-11-09 14:21:20 +00:00
qDeleteAll ( m_pagesVector ) ;
m_pagesVector . clear ( ) ;
delete m_tempFile ;
2017-09-05 21:27:18 +00:00
m_tempFile = nullptr ;
2009-01-03 10:17:15 +00:00
2022-03-18 21:35:45 +00:00
// TODO: Q_EMIT a message telling the document is empty
2014-05-09 17:56:16 +00:00
if ( openResult = = Document : : OpenSuccess ) {
openResult = Document : : OpenError ;
2022-03-08 10:10:43 +00:00
}
2020-02-10 23:22:06 +00:00
} else {
/*
* Now that the documen is opened , the tab ( if using tabs ) is visible , which mean that
* we can now connect the error reporting signal directly to the parent
*/
2020-02-10 23:17:05 +00:00
QObject : : disconnect ( errorToOpenErrorConnection ) ;
2020-02-10 23:22:06 +00:00
QObject : : connect ( m_generator , & Generator : : error , m_parent , & Document : : error ) ;
2008-11-09 14:21:20 +00:00
}
2014-05-09 17:56:16 +00:00
return openResult ;
2008-11-09 14:21:20 +00:00
}
2014-09-17 22:30:39 +00:00
bool DocumentPrivate : : savePageDocumentInfo ( QTemporaryFile * infoFile , int what ) const
2008-11-15 14:15:31 +00:00
{
if ( infoFile - > open ( ) ) {
// 1. Create DOM
2015-10-29 12:37:11 +00:00
QDomDocument doc ( QStringLiteral ( " documentInfo " ) ) ;
QDomProcessingInstruction xmlPi = doc . createProcessingInstruction ( QStringLiteral ( " xml " ) , QStringLiteral ( " version= \" 1.0 \" encoding= \" utf-8 \" " ) ) ;
2008-11-15 14:15:31 +00:00
doc . appendChild ( xmlPi ) ;
2015-10-29 12:37:11 +00:00
QDomElement root = doc . createElement ( QStringLiteral ( " documentInfo " ) ) ;
2008-11-15 14:15:31 +00:00
doc . appendChild ( root ) ;
// 2.1. Save page attributes (bookmark state, annotations, ... ) to DOM
2015-10-29 12:37:11 +00:00
QDomElement pageList = doc . createElement ( QStringLiteral ( " pageList " ) ) ;
2008-11-15 14:15:31 +00:00
root . appendChild ( pageList ) ;
// <page list><page number='x'>.... </page> 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 , PageItems ( what ) ) ;
2022-03-08 10:10:43 +00:00
}
2008-11-15 14:15:31 +00:00
// 3. Save DOM to XML file
QString xml = doc . toString ( ) ;
QTextStream os ( infoFile ) ;
os . setCodec ( " UTF-8 " ) ;
os < < xml ;
return true ;
}
return false ;
}
2009-05-13 14:24:30 +00:00
DocumentViewport DocumentPrivate : : nextDocumentViewport ( ) const
{
DocumentViewport ret = m_nextDocumentViewport ;
if ( ! m_nextDocumentDestination . isEmpty ( ) & & m_generator ) {
2015-10-29 12:37:11 +00:00
DocumentViewport vp ( m_parent - > metaData ( QStringLiteral ( " NamedViewport " ) , m_nextDocumentDestination ) . toString ( ) ) ;
2009-05-13 14:24:30 +00:00
if ( vp . isValid ( ) ) {
ret = vp ;
}
}
return ret ;
}
2013-04-05 22:22:48 +00:00
void DocumentPrivate : : performAddPageAnnotation ( int page , Annotation * annotation )
{
Okular : : SaveInterface * iface = qobject_cast < Okular : : SaveInterface * > ( m_generator ) ;
2017-09-05 21:27:18 +00:00
AnnotationProxy * proxy = iface ? iface - > annotationProxy ( ) : nullptr ;
2013-04-05 22:22:48 +00:00
// find out the page to attach annotation
Page * kp = m_pagesVector [ page ] ;
if ( ! m_generator | | ! kp ) {
return ;
2022-03-08 10:10:43 +00:00
}
2013-04-05 22:22:48 +00:00
// the annotation belongs already to a page
if ( annotation - > d_ptr - > m_page ) {
return ;
2022-03-08 10:10:43 +00:00
}
2013-04-05 22:22:48 +00:00
// add annotation to the page
kp - > addAnnotation ( annotation ) ;
// tell the annotation proxy
if ( proxy & & proxy - > supports ( AnnotationProxy : : Addition ) ) {
proxy - > notifyAddition ( annotation , page ) ;
2022-03-08 10:10:43 +00:00
}
2013-04-05 22:22:48 +00:00
// notify observers about the change
notifyAnnotationChanges ( page ) ;
if ( annotation - > flags ( ) & Annotation : : ExternallyDrawn ) {
// Redraw everything, including ExternallyDrawn annotations
refreshPixmaps ( page ) ;
}
}
void DocumentPrivate : : performRemovePageAnnotation ( int page , Annotation * annotation )
{
Okular : : SaveInterface * iface = qobject_cast < Okular : : SaveInterface * > ( m_generator ) ;
2017-09-05 21:27:18 +00:00
AnnotationProxy * proxy = iface ? iface - > annotationProxy ( ) : nullptr ;
2013-04-05 22:22:48 +00:00
bool isExternallyDrawn ;
// find out the page
Page * kp = m_pagesVector [ page ] ;
if ( ! m_generator | | ! kp ) {
return ;
2022-03-08 10:10:43 +00:00
}
2013-04-05 22:22:48 +00:00
if ( annotation - > flags ( ) & Annotation : : ExternallyDrawn ) {
isExternallyDrawn = true ;
} else {
isExternallyDrawn = false ;
2022-03-08 10:10:43 +00:00
}
2013-04-05 22:22:48 +00:00
// try to remove the annotation
if ( m_parent - > canRemovePageAnnotation ( annotation ) ) {
// tell the annotation proxy
if ( proxy & & proxy - > supports ( AnnotationProxy : : Removal ) ) {
proxy - > notifyRemoval ( annotation , page ) ;
2022-03-08 10:10:43 +00:00
}
2013-04-05 22:22:48 +00:00
kp - > removeAnnotation ( annotation ) ; // Also destroys the object
// in case of success, notify observers about the change
notifyAnnotationChanges ( page ) ;
if ( isExternallyDrawn ) {
// Redraw everything, including ExternallyDrawn annotations
refreshPixmaps ( page ) ;
}
}
}
void DocumentPrivate : : performModifyPageAnnotation ( int page , Annotation * annotation , bool appearanceChanged )
{
Okular : : SaveInterface * iface = qobject_cast < Okular : : SaveInterface * > ( m_generator ) ;
2017-09-05 21:27:18 +00:00
AnnotationProxy * proxy = iface ? iface - > annotationProxy ( ) : nullptr ;
2013-04-05 22:22:48 +00:00
// find out the page
Page * kp = m_pagesVector [ page ] ;
if ( ! m_generator | | ! kp ) {
return ;
2022-03-08 10:10:43 +00:00
}
2013-04-05 22:22:48 +00:00
// tell the annotation proxy
if ( proxy & & proxy - > supports ( AnnotationProxy : : Modification ) ) {
proxy - > notifyModification ( annotation , page , appearanceChanged ) ;
}
// notify observers about the change
notifyAnnotationChanges ( page ) ;
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 */
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
if ( annotation - > flags ( ) & ( Annotation : : BeingMoved | Annotation : : BeingResized ) ) {
if ( m_annotationBeingModified ) {
2013-04-05 22:22:48 +00:00
return ;
} else { // First time: take note
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 = true ;
2022-03-08 10:10:43 +00:00
}
2013-04-05 22:22:48 +00:00
} else {
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 ;
2013-04-05 22:22:48 +00:00
}
// Redraw everything, including ExternallyDrawn annotations
2014-09-11 17:36:01 +00:00
qCDebug ( OkularCoreDebug ) < < " Refreshing Pixmaps " ;
2013-04-05 22:22:48 +00:00
refreshPixmaps ( page ) ;
}
}
void DocumentPrivate : : performSetAnnotationContents ( const QString & newContents , Annotation * annot , int pageNumber )
{
bool appearanceChanged = false ;
2013-05-14 07:37:00 +00:00
// Check if appearanceChanged should be true
2013-04-05 22:22:48 +00:00
switch ( annot - > subType ( ) ) {
// If it's an in-place TextAnnotation, set the inplace text
case Okular : : Annotation : : AText : {
Okular : : TextAnnotation * txtann = static_cast < Okular : : TextAnnotation * > ( annot ) ;
if ( txtann - > textType ( ) = = Okular : : TextAnnotation : : InPlace ) {
appearanceChanged = true ;
}
2020-07-10 22:15:05 +00:00
break ;
}
2013-04-05 22:22:48 +00:00
// If it's a LineAnnotation, check if caption text is visible
case Okular : : Annotation : : ALine : {
Okular : : LineAnnotation * lineann = static_cast < Okular : : LineAnnotation * > ( annot ) ;
if ( lineann - > showCaption ( ) ) {
appearanceChanged = true ;
2022-03-08 10:10:43 +00:00
}
2013-04-05 22:22:48 +00:00
break ;
}
default :
break ;
}
// Set contents
annot - > setContents ( newContents ) ;
// Tell the document the annotation has been modified
performModifyPageAnnotation ( pageNumber , annot , appearanceChanged ) ;
}
2017-03-03 00:08:44 +00:00
void DocumentPrivate : : recalculateForms ( )
{
2019-04-25 21:10:48 +00:00
const QVariant fco = m_parent - > metaData ( QStringLiteral ( " FormCalculateOrder " ) ) ;
2017-03-03 00:08:44 +00:00
const QVector < int > formCalculateOrder = fco . value < QVector < int > > ( ) ;
2022-03-18 21:57:13 +00:00
for ( int formId : formCalculateOrder ) {
2017-03-03 00:08:44 +00:00
for ( uint pageIdx = 0 ; pageIdx < m_parent - > pages ( ) ; pageIdx + + ) {
const Page * p = m_parent - > page ( pageIdx ) ;
if ( p ) {
2018-02-21 22:56:43 +00:00
bool pageNeedsRefresh = false ;
2022-03-21 23:59:42 +00:00
const QList < Okular : : FormField * > forms = p - > formFields ( ) ;
2022-03-18 21:57:13 +00:00
for ( FormField * form : forms ) {
2017-03-03 00:08:44 +00:00
if ( form - > id ( ) = = formId ) {
Action * action = form - > additionalAction ( FormField : : CalculateField ) ;
if ( action ) {
2018-02-21 22:56:43 +00:00
FormFieldText * fft = dynamic_cast < FormFieldText * > ( form ) ;
std : : shared_ptr < Event > event ;
QString oldVal ;
if ( fft ) {
2018-11-14 19:12:15 +00:00
// Prepare text calculate event
2018-02-21 22:56:43 +00:00
event = Event : : createFormCalculateEvent ( fft , m_pagesVector [ pageIdx ] ) ;
if ( ! m_scripter ) {
m_scripter = new Scripter ( this ) ;
2022-03-08 10:10:43 +00:00
}
2018-02-21 22:56:43 +00:00
m_scripter - > setEvent ( event . get ( ) ) ;
// The value maybe changed in javascript so save it first.
oldVal = fft - > text ( ) ;
}
2017-03-03 00:08:44 +00:00
m_parent - > processAction ( action ) ;
2018-02-21 22:56:43 +00:00
if ( event & & fft ) {
// Update text field from calculate
m_scripter - > setEvent ( nullptr ) ;
const QString newVal = event - > value ( ) . toString ( ) ;
if ( newVal ! = oldVal ) {
fft - > setText ( newVal ) ;
2020-03-21 23:37:00 +00:00
fft - > setAppearanceText ( newVal ) ;
2019-06-26 00:58:28 +00:00
if ( const Okular : : Action * action = fft - > additionalAction ( Okular : : FormField : : FormatField ) ) {
// The format action handles the refresh.
m_parent - > processFormatAction ( action , fft ) ;
} else {
2022-03-18 21:35:45 +00:00
Q_EMIT m_parent - > refreshFormWidget ( fft ) ;
2019-06-26 00:58:28 +00:00
pageNeedsRefresh = true ;
}
2018-02-21 22:56:43 +00:00
}
}
2017-03-03 00:08:44 +00:00
} else {
qWarning ( ) < < " Form that is part of calculate order doesn't have a calculate action " ;
}
}
}
2018-02-21 22:56:43 +00:00
if ( pageNeedsRefresh ) {
refreshPixmaps ( p - > number ( ) ) ;
}
2017-03-03 00:08:44 +00:00
}
}
}
}
2007-04-14 19:44:07 +00:00
void DocumentPrivate : : saveDocumentInfo ( ) const
2006-05-28 16:54:54 +00:00
{
2008-01-01 16:22:29 +00:00
if ( m_xmlFileName . isEmpty ( ) ) {
2007-01-02 19:05:49 +00:00
return ;
2022-03-08 10:10:43 +00:00
}
2006-05-28 16:54:54 +00:00
2007-01-02 19:05:49 +00:00
QFile infoFile ( m_xmlFileName ) ;
2016-01-30 16:31:01 +00:00
qCDebug ( OkularCoreDebug ) < < " About to save document info to " < < m_xmlFileName ;
if ( ! infoFile . open ( QIODevice : : WriteOnly | QIODevice : : Truncate ) ) {
qCWarning ( OkularCoreDebug ) < < " Failed to open docdata file " < < m_xmlFileName ;
return ;
}
// 1. Create DOM
QDomDocument doc ( QStringLiteral ( " documentInfo " ) ) ;
QDomProcessingInstruction xmlPi = doc . createProcessingInstruction ( QStringLiteral ( " xml " ) , QStringLiteral ( " version= \" 1.0 \" encoding= \" utf-8 \" " ) ) ;
doc . appendChild ( xmlPi ) ;
QDomElement root = doc . createElement ( QStringLiteral ( " documentInfo " ) ) ;
root . setAttribute ( QStringLiteral ( " url " ) , m_url . toDisplayString ( QUrl : : PreferLocalFile ) ) ;
doc . appendChild ( root ) ;
// 2.1. Save page attributes (bookmark state, annotations, ... ) to DOM
2017-11-15 10:20:25 +00:00
// -> do this if there are not-yet-migrated annots or forms in docdata/
2017-09-10 09:51:56 +00:00
if ( m_docdataMigrationNeeded ) {
2019-04-25 21:10:48 +00:00
QDomElement pageList = doc . createElement ( QStringLiteral ( " pageList " ) ) ;
2017-09-10 09:51:56 +00:00
root . appendChild ( pageList ) ;
// OriginalAnnotationPageItems and OriginalFormFieldPageItems tell to
// store the same unmodified annotation list and form contents that we
// read when we opened the file and ignore any change made by the user.
2017-11-15 10:20:25 +00:00
// Since we don't store annotations and forms in docdata/ any more, this is
2017-09-10 09:51:56 +00:00
// necessary to preserve annotations/forms that previous Okular version
// had stored there.
const PageItems saveWhat = AllPageItems | OriginalAnnotationPageItems | OriginalFormFieldPageItems ;
// <page list><page number='x'>.... </page> 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 , saveWhat ) ;
2022-03-08 10:10:43 +00:00
}
2017-09-10 09:51:56 +00:00
}
2016-01-30 16:31:01 +00:00
// 2.2. Save document info (current viewport, history, ... ) to DOM
QDomElement generalInfo = doc . createElement ( QStringLiteral ( " generalInfo " ) ) ;
root . appendChild ( generalInfo ) ;
// create rotation node
if ( m_rotation ! = Rotation0 ) {
QDomElement rotationNode = doc . createElement ( QStringLiteral ( " rotation " ) ) ;
generalInfo . appendChild ( rotationNode ) ;
rotationNode . appendChild ( doc . createTextNode ( QString : : number ( ( int ) m_rotation ) ) ) ;
}
// <general info><history> ... </history> save history up to OKULAR_HISTORY_SAVEDSTEPS viewports
2022-03-31 15:05:15 +00:00
const auto currentViewportIterator = std : : list < DocumentViewport > : : const_iterator ( m_viewportIterator ) ;
std : : list < DocumentViewport > : : const_iterator backIterator = currentViewportIterator ;
if ( backIterator ! = m_viewportHistory . end ( ) ) {
2016-01-30 16:31:01 +00:00
// go back up to OKULAR_HISTORY_SAVEDSTEPS steps from the current viewportIterator
int backSteps = OKULAR_HISTORY_SAVEDSTEPS ;
2022-03-31 15:05:15 +00:00
while ( backSteps - - & & backIterator ! = m_viewportHistory . begin ( ) ) {
2016-01-30 16:31:01 +00:00
- - backIterator ;
2022-03-08 10:10:43 +00:00
}
2016-01-30 16:31:01 +00:00
// create history root node
QDomElement historyNode = doc . createElement ( QStringLiteral ( " history " ) ) ;
generalInfo . appendChild ( historyNode ) ;
// add old[backIterator] and present[viewportIterator] items
2022-03-31 15:05:15 +00:00
std : : list < DocumentViewport > : : const_iterator endIt = currentViewportIterator ;
2016-01-30 16:31:01 +00:00
+ + endIt ;
while ( backIterator ! = endIt ) {
2020-02-20 09:15:24 +00:00
QString name = ( backIterator = = currentViewportIterator ) ? QStringLiteral ( " current " ) : QStringLiteral ( " oldPage " ) ;
2016-01-30 16:31:01 +00:00
QDomElement historyEntry = doc . createElement ( name ) ;
historyEntry . setAttribute ( QStringLiteral ( " viewport " ) , ( * backIterator ) . toString ( ) ) ;
historyNode . appendChild ( historyEntry ) ;
+ + backIterator ;
2008-05-31 21:13:15 +00:00
}
2007-01-02 19:05:49 +00:00
}
2016-01-30 16:31:01 +00:00
// create views root node
QDomElement viewsNode = doc . createElement ( QStringLiteral ( " views " ) ) ;
generalInfo . appendChild ( viewsNode ) ;
2019-12-09 13:16:55 +00:00
for ( View * view : qAsConst ( m_views ) ) {
2016-01-30 16:31:01 +00:00
QDomElement viewEntry = doc . createElement ( QStringLiteral ( " view " ) ) ;
viewEntry . setAttribute ( QStringLiteral ( " name " ) , view - > name ( ) ) ;
viewsNode . appendChild ( viewEntry ) ;
saveViewsInfo ( view , viewEntry ) ;
}
// 3. Save DOM to XML file
QString xml = doc . toString ( ) ;
QTextStream os ( & infoFile ) ;
os . setCodec ( " UTF-8 " ) ;
os < < xml ;
2007-01-02 19:05:49 +00:00
infoFile . close ( ) ;
2004-12-05 22:47:32 +00:00
}
2007-04-14 19:44:07 +00:00
void DocumentPrivate : : slotTimedMemoryCheck ( )
2005-06-13 17:39:58 +00:00
{
2018-11-14 19:12:15 +00:00
// [MEM] clean memory (for 'free mem dependent' profiles only)
2012-10-15 22:27:42 +00:00
if ( SettingsCore : : memoryLevel ( ) ! = SettingsCore : : EnumMemoryLevel : : Low & & m_allocatedPixmapsTotalMemory > 1024 * 1024 ) {
2007-01-02 19:05:49 +00:00
cleanupPixmapMemory ( ) ;
2022-03-08 10:10:43 +00:00
}
2005-06-13 17:39:58 +00:00
}
2012-09-25 09:09:34 +00:00
void DocumentPrivate : : sendGeneratorPixmapRequest ( )
2004-09-09 13:25:40 +00:00
{
2012-07-02 06:22:51 +00:00
/* If the pixmap cache will have to be cleaned in order to make room for the
* next request , get the distance from the current viewport of the page
* whose pixmap will be removed . We will ignore preload requests for pages
* that are at the same distance or farther */
const qulonglong memoryToFree = calculateMemoryToFree ( ) ;
const int currentViewportPage = ( * m_viewportIterator ) . pageNumber ;
int maxDistance = INT_MAX ; // Default: No maximum
if ( memoryToFree ) {
2012-11-08 13:58:08 +00:00
AllocatedPixmap * pixmapToReplace = searchLowestPriorityPixmap ( true ) ;
2012-07-02 06:22:51 +00:00
if ( pixmapToReplace ) {
maxDistance = qAbs ( pixmapToReplace - > page - currentViewportPage ) ;
2022-03-08 10:10:43 +00:00
}
2012-07-02 06:22:51 +00:00
}
2007-01-02 19:05:49 +00:00
// find a request
2017-09-05 21:27:18 +00:00
PixmapRequest * request = nullptr ;
2007-05-12 21:40:38 +00:00
m_pixmapRequestsMutex . lock ( ) ;
2022-03-31 15:05:15 +00:00
while ( ! m_pixmapRequestsStack . empty ( ) & & ! request ) {
PixmapRequest * r = m_pixmapRequestsStack . back ( ) ;
2007-01-02 19:05:49 +00:00
if ( ! r ) {
m_pixmapRequestsStack . pop_back ( ) ;
2012-08-19 19:00:15 +00:00
continue ;
}
2012-08-24 16:32:39 +00:00
QRect requestRect = r - > isTile ( ) ? r - > normalizedRect ( ) . geometry ( r - > width ( ) , r - > height ( ) ) : QRect ( 0 , 0 , r - > width ( ) , r - > height ( ) ) ;
2014-02-19 22:40:43 +00:00
TilesManager * tilesManager = r - > d - > tilesManager ( ) ;
2019-06-22 14:17:23 +00:00
const double normalizedArea = r - > normalizedRect ( ) . width ( ) * r - > normalizedRect ( ) . height ( ) ;
2020-03-01 09:32:27 +00:00
const QScreen * screen = nullptr ;
if ( m_widget ) {
const QWindow * window = m_widget - > window ( ) - > windowHandle ( ) ;
if ( window ) {
screen = window - > screen ( ) ;
2022-03-08 10:10:43 +00:00
}
2020-03-01 09:32:27 +00:00
}
if ( ! screen ) {
screen = QGuiApplication : : primaryScreen ( ) ;
2022-03-08 10:10:43 +00:00
}
2020-03-01 09:32:27 +00:00
const long screenSize = screen - > devicePixelRatio ( ) * screen - > size ( ) . width ( ) * screen - > devicePixelRatio ( ) * screen - > size ( ) . height ( ) ;
2005-06-24 16:45:25 +00:00
2022-11-24 00:52:53 +00:00
// Make sure the page is the right size to receive the pixmap
r - > page ( ) - > setPageSize ( r - > observer ( ) , r - > width ( ) , r - > height ( ) ) ;
2013-06-23 16:42:56 +00:00
// If it's a preload but the generator is not threaded no point in trying to preload
2013-06-23 16:45:09 +00:00
if ( r - > preload ( ) & & ! m_generator - > hasFeature ( Generator : : Threaded ) ) {
2013-06-23 16:42:56 +00:00
m_pixmapRequestsStack . pop_back ( ) ;
delete r ;
}
// request only if page isn't already present and request has valid id
2013-06-23 16:45:09 +00:00
else if ( ( ! r - > d - > mForce & & r - > page ( ) - > hasPixmap ( r - > observer ( ) , r - > width ( ) , r - > height ( ) , r - > normalizedRect ( ) ) ) | | ! m_observers . contains ( r - > observer ( ) ) ) {
2007-01-02 19:05:49 +00:00
m_pixmapRequestsStack . pop_back ( ) ;
delete r ;
2013-02-24 21:58:53 +00:00
} else if ( ! r - > d - > mForce & & r - > preload ( ) & & qAbs ( r - > pageNumber ( ) - currentViewportPage ) > = maxDistance ) {
2007-01-02 19:05:49 +00:00
m_pixmapRequestsStack . pop_back ( ) ;
2014-09-11 17:36:01 +00:00
// qCDebug(OkularCoreDebug) << "Ignoring request that doesn't fit in cache";
2007-01-02 19:05:49 +00:00
delete r ;
}
2012-11-08 12:02:33 +00:00
// Ignore requests for pixmaps that are already being generated
2012-09-25 01:01:47 +00:00
else if ( tilesManager & & tilesManager - > isRequesting ( r - > normalizedRect ( ) , r - > width ( ) , r - > height ( ) ) ) {
m_pixmapRequestsStack . pop_back ( ) ;
delete r ;
}
2020-03-01 09:32:27 +00:00
// If the requested area is above 4*screenSize pixels, and we're not rendering most of the page, switch on the tile manager
2023-02-09 17:24:57 +00:00
else if ( ! tilesManager & & m_generator - > hasFeature ( Generator : : TiledRendering ) & & ( long ) r - > width ( ) * ( long ) r - > height ( ) > 4L * screenSize & & normalizedArea < 0.75 ) {
2012-07-12 05:20:03 +00:00
// if the image is too big. start using tiles
2014-09-11 17:36:01 +00:00
qCDebug ( OkularCoreDebug ) . nospace ( ) < < " Start using tiles on page " < < r - > pageNumber ( ) < < " ( " < < r - > width ( ) < < " x " < < r - > height ( ) < < " px); " ;
2012-07-12 05:20:03 +00:00
// fill the tiles manager with the last rendered pixmap
2013-02-24 21:58:53 +00:00
const QPixmap * pixmap = r - > page ( ) - > _o_nearestPixmap ( r - > observer ( ) , r - > width ( ) , r - > height ( ) ) ;
2012-07-12 05:20:03 +00:00
if ( pixmap ) {
2012-11-08 13:58:08 +00:00
tilesManager = new TilesManager ( r - > pageNumber ( ) , pixmap - > width ( ) , pixmap - > height ( ) , r - > page ( ) - > rotation ( ) ) ;
2018-02-01 18:42:37 +00:00
tilesManager - > setPixmap ( pixmap , NormalizedRect ( 0 , 0 , 1 , 1 ) , true /*isPartialPixmap*/ ) ;
2012-11-11 19:56:39 +00:00
tilesManager - > setSize ( r - > width ( ) , r - > height ( ) ) ;
2012-07-12 05:20:03 +00:00
} else {
// create new tiles manager
2012-11-08 13:58:08 +00:00
tilesManager = new TilesManager ( r - > pageNumber ( ) , r - > width ( ) , r - > height ( ) , r - > page ( ) - > rotation ( ) ) ;
2012-07-12 05:20:03 +00:00
}
2012-10-18 04:24:06 +00:00
tilesManager - > setRequest ( r - > normalizedRect ( ) , r - > width ( ) , r - > height ( ) ) ;
2013-02-24 21:58:53 +00:00
r - > page ( ) - > deletePixmap ( r - > observer ( ) ) ;
2014-02-19 22:40:43 +00:00
r - > page ( ) - > d - > setTilesManager ( r - > observer ( ) , tilesManager ) ;
2012-08-24 16:32:39 +00:00
r - > setTile ( true ) ;
2012-07-12 05:20:03 +00:00
2012-08-26 19:01:07 +00:00
// Change normalizedRect to the smallest rect that contains all
// visible tiles.
if ( ! r - > normalizedRect ( ) . isNull ( ) ) {
NormalizedRect tilesRect ;
2012-11-12 14:55:13 +00:00
const QList < Tile > tiles = tilesManager - > tilesAt ( r - > normalizedRect ( ) , TilesManager : : TerminalTile ) ;
2012-08-26 19:01:07 +00:00
QList < Tile > : : const_iterator tIt = tiles . constBegin ( ) , tEnd = tiles . constEnd ( ) ;
while ( tIt ! = tEnd ) {
Tile tile = * tIt ;
if ( tilesRect . isNull ( ) ) {
2012-11-08 15:41:00 +00:00
tilesRect = tile . rect ( ) ;
2012-08-26 19:01:07 +00:00
} else {
2012-11-08 15:41:00 +00:00
tilesRect | = tile . rect ( ) ;
2022-03-08 10:10:43 +00:00
}
2012-08-26 19:01:07 +00:00
+ + tIt ;
}
r - > setNormalizedRect ( tilesRect ) ;
2012-12-08 19:02:01 +00:00
request = r ;
} else {
2012-12-12 22:27:51 +00:00
// Discard request if normalizedRect is null. This happens in
// preload requests issued by PageView if the requested page is
// not visible and the user has just switched from a non-tiled
// zoom level to a tiled one
2012-12-08 19:02:01 +00:00
m_pixmapRequestsStack . pop_back ( ) ;
delete r ;
2012-08-26 19:01:07 +00:00
}
2012-07-12 05:20:03 +00:00
}
2020-03-01 09:32:27 +00:00
// If the requested area is below 3*screenSize pixels, switch off the tile manager
else if ( tilesManager & & ( long ) r - > width ( ) * ( long ) r - > height ( ) < 3L * screenSize ) {
2014-09-11 17:36:01 +00:00
qCDebug ( OkularCoreDebug ) . nospace ( ) < < " Stop using tiles on page " < < r - > pageNumber ( ) < < " ( " < < r - > width ( ) < < " x " < < r - > height ( ) < < " px); " ;
2012-07-12 05:20:03 +00:00
// page is too small. stop using tiles.
2013-02-24 21:58:53 +00:00
r - > page ( ) - > deletePixmap ( r - > observer ( ) ) ;
2012-08-24 16:32:39 +00:00
r - > setTile ( false ) ;
2012-07-12 05:20:03 +00:00
request = r ;
2020-03-01 09:32:27 +00:00
} else if ( ( long ) requestRect . width ( ) * ( long ) requestRect . height ( ) > 100L * screenSize & & ( SettingsCore : : memoryLevel ( ) ! = SettingsCore : : EnumMemoryLevel : : Greedy ) ) {
2007-01-02 19:05:49 +00:00
m_pixmapRequestsStack . pop_back ( ) ;
if ( ! m_warnedOutOfMemory ) {
2014-09-11 19:12:27 +00:00
qCWarning ( OkularCoreDebug ) . nospace ( ) < < " Running out of memory on page " < < r - > pageNumber ( ) < < " ( " < < r - > width ( ) < < " x " < < r - > height ( ) < < " px); " ;
qCWarning ( OkularCoreDebug ) < < " this message will be reported only once. " ;
2007-01-02 19:05:49 +00:00
m_warnedOutOfMemory = true ;
}
delete r ;
} else {
request = r ;
2012-07-02 06:22:51 +00:00
}
2007-01-02 19:05:49 +00:00
}
2005-07-20 22:28:23 +00:00
2007-01-02 19:05:49 +00:00
// if no request found (or already generated), return
if ( ! request ) {
2007-05-12 21:40:38 +00:00
m_pixmapRequestsMutex . unlock ( ) ;
2007-01-02 19:05:49 +00:00
return ;
2007-05-12 21:40:38 +00:00
}
- GIGANTIC 2700 line diff with LOTS OF FEATURES!
- 1. editor-like text selection, and I do mean it, its not pseudo-editor
(like the ones acroread and kviewshell have) it doesnt intersect the
selection area with words under it, no, it does a lot more, including
work on cursors and searching for the text area closest to the given
cursor
- 2. rotation support, change the orientation of the documents if
you need too :)
- 3. the kfaxview backend works beautifully, porting kviewshell backends
is damn easy ! djvu and dvi will be next!
- 4. Hardware Blending of selection rectangles! We now use XRender
instead of KImageEffect, makes a damn faster blend!
- 5. Overview mode - as seen in Kviewshell, but quite a bit extended,
the kviewshell is only one state, while we support it in both
continous and non-continous form
- BTW. I coded all those features myself, (apart from kfaxview backend library)
it is an impressive bit right? but oKular cant be run by only one person,
join in on the fun! i can introduce you into the code just mail niedakh@gmail.com
svn path=/trunk/playground/graphics/oKular/kpdf/; revision=509871
2006-02-15 18:54:49 +00:00
2007-01-02 19:05:49 +00:00
// [MEM] preventive memory freeing
2012-07-16 15:57:51 +00:00
qulonglong pixmapBytes = 0 ;
2014-02-19 22:40:43 +00:00
TilesManager * tm = request - > d - > tilesManager ( ) ;
2012-07-16 15:57:51 +00:00
if ( tm ) {
pixmapBytes = tm - > totalMemory ( ) ;
} else {
pixmapBytes = 4 * request - > width ( ) * request - > height ( ) ;
2022-03-08 10:10:43 +00:00
}
2012-07-16 15:57:51 +00:00
2007-01-02 19:05:49 +00:00
if ( pixmapBytes > ( 1024 * 1024 ) ) {
2012-07-02 06:22:51 +00:00
cleanupPixmapMemory ( memoryToFree /* previously calculated value */ ) ;
2022-03-08 10:10:43 +00:00
}
- GIGANTIC 2700 line diff with LOTS OF FEATURES!
- 1. editor-like text selection, and I do mean it, its not pseudo-editor
(like the ones acroread and kviewshell have) it doesnt intersect the
selection area with words under it, no, it does a lot more, including
work on cursors and searching for the text area closest to the given
cursor
- 2. rotation support, change the orientation of the documents if
you need too :)
- 3. the kfaxview backend works beautifully, porting kviewshell backends
is damn easy ! djvu and dvi will be next!
- 4. Hardware Blending of selection rectangles! We now use XRender
instead of KImageEffect, makes a damn faster blend!
- 5. Overview mode - as seen in Kviewshell, but quite a bit extended,
the kviewshell is only one state, while we support it in both
continous and non-continous form
- BTW. I coded all those features myself, (apart from kfaxview backend library)
it is an impressive bit right? but oKular cant be run by only one person,
join in on the fun! i can introduce you into the code just mail niedakh@gmail.com
svn path=/trunk/playground/graphics/oKular/kpdf/; revision=509871
2006-02-15 18:54:49 +00:00
2007-01-02 19:05:49 +00:00
// submit the request to the generator
2007-01-31 18:31:19 +00:00
if ( m_generator - > canGeneratePixmap ( ) ) {
2012-08-24 16:32:39 +00:00
QRect requestRect = ! request - > isTile ( ) ? QRect ( 0 , 0 , request - > width ( ) , request - > height ( ) ) : request - > normalizedRect ( ) . geometry ( request - > width ( ) , request - > height ( ) ) ;
2014-09-11 17:36:01 +00:00
qCDebug ( OkularCoreDebug ) . nospace ( ) < < " sending request observer= " < < request - > observer ( ) < < " " < < requestRect . width ( ) < < " x " < < requestRect . height ( ) < < " @ " < < request - > pageNumber ( ) < < " async == " < < request - > asynchronous ( )
< < " isTile == " < < request - > isTile ( ) ;
2022-03-31 15:05:15 +00:00
m_pixmapRequestsStack . remove ( request ) ;
2006-10-25 15:35:53 +00:00
2012-09-25 01:01:47 +00:00
if ( tm ) {
tm - > setRequest ( request - > normalizedRect ( ) , request - > width ( ) , request - > height ( ) ) ;
2022-03-08 10:10:43 +00:00
}
2012-09-25 01:01:47 +00:00
2007-01-05 17:09:47 +00:00
if ( ( int ) m_rotation % 2 ) {
2007-12-04 21:36:32 +00:00
request - > d - > swap ( ) ;
2022-03-08 10:10:43 +00:00
}
2006-10-25 15:35:53 +00:00
2012-08-17 17:25:58 +00:00
if ( m_rotation ! = Rotation0 & & ! request - > normalizedRect ( ) . isNull ( ) ) {
request - > setNormalizedRect ( TilesManager : : fromRotatedRect ( request - > normalizedRect ( ) , m_rotation ) ) ;
2022-03-08 10:10:43 +00:00
}
2012-08-17 17:25:58 +00:00
2018-02-01 18:42:37 +00:00
// If set elsewhere we already know we want it to be partial
if ( ! request - > partialUpdatesWanted ( ) ) {
request - > setPartialUpdatesWanted ( request - > asynchronous ( ) & & ! request - > page ( ) - > hasPixmap ( request - > observer ( ) ) ) ;
}
2017-10-03 07:29:18 +00:00
2007-05-12 21:40:38 +00:00
// we always have to unlock _before_ the generatePixmap() because
// a sync generation would end with requestDone() -> deadlock, and
// we can not really know if the generator can do async requests
2008-02-01 00:43:45 +00:00
m_executingPixmapRequests . push_back ( request ) ;
2007-05-12 21:40:38 +00:00
m_pixmapRequestsMutex . unlock ( ) ;
2007-01-31 18:31:19 +00:00
m_generator - > generatePixmap ( request ) ;
2007-01-02 19:05:49 +00:00
} else {
2007-05-12 21:40:38 +00:00
m_pixmapRequestsMutex . unlock ( ) ;
2007-01-02 19:05:49 +00:00
// pino (7/4/2006): set the polling interval from 10 to 30
2019-12-19 22:18:31 +00:00
QTimer : : singleShot ( 30 , m_parent , [ this ] { sendGeneratorPixmapRequest ( ) ; } ) ;
2007-05-12 21:40:38 +00:00
}
2006-07-01 22:17:53 +00:00
}
2009-06-30 17:21:55 +00:00
void DocumentPrivate : : rotationFinished ( int page , Okular : : Page * okularPage )
2006-07-01 22:17:53 +00:00
{
2020-09-13 09:20:09 +00:00
Okular : : Page * wantedPage = m_pagesVector . value ( page , nullptr ) ;
2009-06-30 17:21:55 +00:00
if ( ! wantedPage | | wantedPage ! = okularPage ) {
return ;
2022-03-08 10:10:43 +00:00
}
2009-06-30 17:21:55 +00:00
2022-03-18 21:57:13 +00:00
for ( DocumentObserver * o : qAsConst ( m_observers ) ) {
2013-02-24 21:58:53 +00:00
o - > notifyPageChanged ( page , DocumentObserver : : Pixmap | DocumentObserver : : Annotations ) ;
2022-03-18 21:57:13 +00:00
}
2006-07-01 22:17:53 +00:00
}
2017-01-31 21:50:54 +00:00
void DocumentPrivate : : slotFontReadingProgress ( int page )
2007-07-07 20:35:01 +00:00
{
2022-03-18 21:35:45 +00:00
Q_EMIT m_parent - > fontReadingProgress ( page ) ;
2007-07-07 20:35:01 +00:00
2007-12-28 17:06:43 +00:00
if ( page > = ( int ) m_parent - > pages ( ) - 1 ) {
2022-03-18 21:35:45 +00:00
Q_EMIT m_parent - > fontReadingEnded ( ) ;
2017-09-05 21:27:18 +00:00
m_fontThread = nullptr ;
2007-07-07 20:35:01 +00:00
m_fontsCached = true ;
}
}
void DocumentPrivate : : fontReadingGotFont ( const Okular : : FontInfo & font )
{
2016-07-13 21:46:43 +00:00
// Try to avoid duplicate fonts
if ( m_fontsCache . indexOf ( font ) = = - 1 ) {
m_fontsCache . append ( font ) ;
2007-07-07 20:35:01 +00:00
2022-03-18 21:35:45 +00:00
Q_EMIT m_parent - > gotFont ( font ) ;
2016-07-13 21:46:43 +00:00
}
2007-07-07 20:35:01 +00:00
}
2019-12-19 22:18:31 +00:00
void DocumentPrivate : : slotGeneratorConfigChanged ( )
2007-07-12 20:04:56 +00:00
{
if ( ! m_generator ) {
return ;
2022-03-08 10:10:43 +00:00
}
2007-07-12 20:04:56 +00:00
// reparse generator config and if something changed clear Pages
bool configchanged = false ;
2007-11-25 12:49:30 +00:00
QHash < QString , GeneratorInfo > : : iterator it = m_loadedGenerators . begin ( ) , itEnd = m_loadedGenerators . end ( ) ;
2007-07-12 20:04:56 +00:00
for ( ; it ! = itEnd ; + + it ) {
2007-11-25 12:49:30 +00:00
Okular : : ConfigInterface * iface = generatorConfig ( it . value ( ) ) ;
2007-07-12 20:04:56 +00:00
if ( iface ) {
bool it_changed = iface - > reparseConfig ( ) ;
if ( it_changed & & ( m_generator = = it . value ( ) . generator ) ) {
configchanged = true ;
2022-03-08 10:10:43 +00:00
}
2007-07-12 20:04:56 +00:00
}
}
if ( configchanged ) {
// invalidate pixmaps
2008-11-11 18:48:40 +00:00
QVector < Page * > : : const_iterator it = m_pagesVector . constBegin ( ) , end = m_pagesVector . constEnd ( ) ;
2007-07-12 20:04:56 +00:00
for ( ; it ! = end ; + + it ) {
( * it ) - > deletePixmaps ( ) ;
}
// [MEM] remove allocation descriptors
2012-07-01 23:05:02 +00:00
qDeleteAll ( m_allocatedPixmaps ) ;
m_allocatedPixmaps . clear ( ) ;
2007-07-12 20:04:56 +00:00
m_allocatedPixmapsTotalMemory = 0 ;
// send reload signals to observers
foreachObserverD ( notifyContentsCleared ( DocumentObserver : : Pixmap ) ) ;
}
// free memory if in 'low' profile
2022-03-31 15:05:15 +00:00
if ( SettingsCore : : memoryLevel ( ) = = SettingsCore : : EnumMemoryLevel : : Low & & ! m_allocatedPixmaps . empty ( ) & & ! m_pagesVector . isEmpty ( ) ) {
2007-07-12 20:04:56 +00:00
cleanupPixmapMemory ( ) ;
2022-03-08 10:10:43 +00:00
}
2007-07-12 20:04:56 +00:00
}
2008-03-07 15:50:48 +00:00
void DocumentPrivate : : refreshPixmaps ( int pageNumber )
{
2020-09-13 09:20:09 +00:00
Page * page = m_pagesVector . value ( pageNumber , nullptr ) ;
2008-03-07 15:50:48 +00:00
if ( ! page ) {
return ;
2022-03-08 10:10:43 +00:00
}
2008-03-07 15:50:48 +00:00
2013-02-24 21:58:53 +00:00
QMap < DocumentObserver * , PagePrivate : : PixmapObject > : : ConstIterator it = page - > d - > m_pixmaps . constBegin ( ) , itEnd = page - > d - > m_pixmaps . constEnd ( ) ;
2018-02-01 18:42:37 +00:00
QVector < Okular : : PixmapRequest * > pixmapsToRequest ;
2008-03-07 15:50:48 +00:00
for ( ; it ! = itEnd ; + + it ) {
2018-02-01 18:42:37 +00:00
const QSize size = ( * it ) . m_pixmap - > size ( ) ;
2021-02-19 20:28:32 +00:00
PixmapRequest * p = new PixmapRequest ( it . key ( ) , pageNumber , size . width ( ) , size . height ( ) , 1 /* dpr */ , 1 , PixmapRequest : : Asynchronous ) ;
2008-03-07 15:50:48 +00:00
p - > d - > mForce = true ;
2018-02-01 18:42:37 +00:00
pixmapsToRequest < < p ;
}
// Need to do this ↑↓ in two steps since requestPixmaps can end up calling cancelRenderingBecauseOf
// which changes m_pixmaps and thus breaks the loop above
for ( PixmapRequest * pr : qAsConst ( pixmapsToRequest ) ) {
2022-03-19 00:00:01 +00:00
QList < Okular : : PixmapRequest * > requestedPixmaps ;
2018-02-09 23:37:19 +00:00
requestedPixmaps . push_back ( pr ) ;
2018-02-01 18:42:37 +00:00
m_parent - > requestPixmaps ( requestedPixmaps , Okular : : Document : : NoOption ) ;
2008-03-07 15:50:48 +00:00
}
2012-10-25 12:16:17 +00:00
2019-12-09 13:16:55 +00:00
for ( DocumentObserver * observer : qAsConst ( m_observers ) ) {
2022-03-19 00:00:01 +00:00
QList < Okular : : PixmapRequest * > requestedPixmaps ;
2018-02-01 18:42:37 +00:00
2014-02-19 22:40:43 +00:00
TilesManager * tilesManager = page - > d - > tilesManager ( observer ) ;
if ( tilesManager ) {
tilesManager - > markDirty ( ) ;
2012-10-25 12:16:17 +00:00
2021-02-19 20:28:32 +00:00
PixmapRequest * p = new PixmapRequest ( observer , pageNumber , tilesManager - > width ( ) , tilesManager - > height ( ) , 1 /* dpr */ , 1 , PixmapRequest : : Asynchronous ) ;
2012-10-25 12:16:17 +00:00
2014-02-19 22:40:43 +00:00
// Get the visible page rect
NormalizedRect visibleRect ;
QVector < Okular : : VisiblePageRect * > : : const_iterator vIt = m_pageRects . constBegin ( ) , vEnd = m_pageRects . constEnd ( ) ;
for ( ; vIt ! = vEnd ; + + vIt ) {
if ( ( * vIt ) - > pageNumber = = pageNumber ) {
visibleRect = ( * vIt ) - > rect ;
break ;
}
2012-11-08 13:58:08 +00:00
}
2012-10-25 12:16:17 +00:00
2014-02-19 22:40:43 +00:00
if ( ! visibleRect . isNull ( ) ) {
p - > setNormalizedRect ( visibleRect ) ;
p - > setTile ( true ) ;
p - > d - > mForce = true ;
requestedPixmaps . push_back ( p ) ;
} else {
delete p ;
}
2012-10-25 12:16:17 +00:00
}
2014-02-19 22:40:43 +00:00
2008-03-07 15:50:48 +00:00
m_parent - > requestPixmaps ( requestedPixmaps , Okular : : Document : : NoOption ) ;
2018-02-01 18:42:37 +00:00
}
2008-03-07 15:50:48 +00:00
}
2008-05-04 15:02:35 +00:00
void DocumentPrivate : : _o_configChanged ( )
{
2008-05-04 15:10:32 +00:00
// free text pages if needed
calculateMaxTextPages ( ) ;
while ( m_allocatedTextPagesFifo . count ( ) > m_maxAllocatedTextPages ) {
int pageToKick = m_allocatedTextPagesFifo . takeFirst ( ) ;
2017-09-05 21:27:18 +00:00
m_pagesVector . at ( pageToKick ) - > setTextPage ( nullptr ) ; // deletes the textpage
2008-05-04 15:10:32 +00:00
}
2008-05-04 15:02:35 +00:00
}
2012-09-24 22:14:10 +00:00
void DocumentPrivate : : doContinueDirectionMatchSearch ( void * doContinueDirectionMatchSearchStruct )
2007-08-13 22:25:27 +00:00
{
2012-09-24 22:14:10 +00:00
DoContinueDirectionMatchSearchStruct * searchStruct = static_cast < DoContinueDirectionMatchSearchStruct * > ( doContinueDirectionMatchSearchStruct ) ;
RunningSearch * search = m_searches . value ( searchStruct - > searchID ) ;
2007-08-13 22:25:27 +00:00
2012-09-24 22:14:10 +00:00
if ( ( m_searchCancelled & & ! searchStruct - > match ) | | ! search ) {
2007-08-13 22:25:27 +00:00
// if the user cancelled but he just got a match, give him the match!
QApplication : : restoreOverrideCursor ( ) ;
2009-08-10 22:28:54 +00:00
2009-11-06 00:19:10 +00:00
if ( search ) {
search - > isCurrentlySearching = false ;
2022-03-08 10:10:43 +00:00
}
2009-08-10 22:28:54 +00:00
2022-03-18 21:35:45 +00:00
Q_EMIT m_parent - > searchFinished ( searchStruct - > searchID , Document : : SearchCancelled ) ;
2012-09-24 22:14:10 +00:00
delete searchStruct - > pagesToNotify ;
delete searchStruct ;
2007-08-13 22:25:27 +00:00
return ;
}
2014-05-09 22:49:01 +00:00
const bool forward = search - > cachedType = = Document : : NextMatch ;
2012-09-25 10:53:35 +00:00
bool doContinue = false ;
2007-08-13 22:25:27 +00:00
// if no match found, loop through the whole doc, starting from currentPage
2012-09-24 22:14:10 +00:00
if ( ! searchStruct - > match ) {
const int pageCount = m_pagesVector . count ( ) ;
2014-05-10 09:33:21 +00:00
if ( search - > pagesDone < pageCount ) {
2012-09-25 10:53:35 +00:00
doContinue = true ;
2017-09-26 21:26:05 +00:00
if ( searchStruct - > currentPage > = pageCount ) {
searchStruct - > currentPage = 0 ;
2022-03-18 21:35:45 +00:00
Q_EMIT m_parent - > notice ( i18n ( " Continuing search from beginning " ) , 3000 ) ;
2017-09-26 21:26:05 +00:00
} else if ( searchStruct - > currentPage < 0 ) {
searchStruct - > currentPage = pageCount - 1 ;
2022-03-18 21:35:45 +00:00
Q_EMIT m_parent - > notice ( i18n ( " Continuing search from bottom " ) , 3000 ) ;
2007-08-13 22:25:27 +00:00
}
}
}
2012-09-25 10:53:35 +00:00
if ( doContinue ) {
// get page
Page * page = m_pagesVector [ searchStruct - > currentPage ] ;
// request search page if needed
if ( ! page - > hasTextPage ( ) ) {
m_parent - > requestTextPage ( page - > number ( ) ) ;
2022-03-08 10:10:43 +00:00
}
2012-09-26 21:28:58 +00:00
// if found a match on the current page, end the loop
2014-05-09 22:49:01 +00:00
searchStruct - > match = page - > findText ( searchStruct - > searchID , search - > cachedString , forward ? FromTop : FromBottom , search - > cachedCaseSensitivity ) ;
2012-09-26 21:28:58 +00:00
if ( ! searchStruct - > match ) {
2014-05-09 22:49:01 +00:00
if ( forward ) {
searchStruct - > currentPage + + ;
2012-09-26 21:28:58 +00:00
} else {
searchStruct - > currentPage - - ;
2022-03-08 10:10:43 +00:00
}
2014-05-10 09:33:21 +00:00
search - > pagesDone + + ;
2012-09-25 10:53:35 +00:00
} else {
2014-05-10 09:33:21 +00:00
search - > pagesDone = 1 ;
2007-08-13 22:25:27 +00:00
}
2013-04-05 22:22:48 +00:00
2012-09-26 21:28:58 +00:00
// Both of the previous if branches need to call doContinueDirectionMatchSearch
2019-12-19 22:18:31 +00:00
QTimer : : singleShot ( 0 , m_parent , [ this , searchStruct ] { doContinueDirectionMatchSearch ( searchStruct ) ; } ) ;
2012-09-25 10:53:35 +00:00
} else {
2014-05-09 22:49:01 +00:00
doProcessSearchMatch ( searchStruct - > match , search , searchStruct - > pagesToNotify , searchStruct - > currentPage , searchStruct - > searchID , search - > cachedViewportMove , search - > cachedColor ) ;
2012-09-25 10:53:35 +00:00
delete searchStruct ;
}
2010-04-15 22:26:58 +00:00
}
void DocumentPrivate : : doProcessSearchMatch ( RegularAreaRect * match , RunningSearch * search , QSet < int > * pagesToNotify , int currentPage , int searchID , bool moveViewport , const QColor & color )
{
2007-08-13 22:25:27 +00:00
// reset cursor to previous shape
QApplication : : restoreOverrideCursor ( ) ;
bool foundAMatch = false ;
2009-11-06 00:19:10 +00:00
search - > isCurrentlySearching = false ;
2009-08-10 22:28:54 +00:00
2007-08-13 22:25:27 +00:00
// if a match has been found..
if ( match ) {
// update the RunningSearch structure adding this match..
foundAMatch = true ;
2009-11-06 00:19:10 +00:00
search - > continueOnPage = currentPage ;
search - > continueOnMatch = * match ;
search - > highlightedPages . insert ( currentPage ) ;
2007-08-13 22:25:27 +00:00
// ..add highlight to the page..
m_pagesVector [ currentPage ] - > d - > setHighlight ( searchID , match , color ) ;
// ..queue page for notifying changes..
pagesToNotify - > insert ( currentPage ) ;
2013-12-29 22:27:30 +00:00
// Create a normalized rectangle around the search match that includes a 5% buffer on all sides.
const Okular : : NormalizedRect matchRectWithBuffer = Okular : : NormalizedRect ( match - > first ( ) . left - 0.05 , match - > first ( ) . top - 0.05 , match - > first ( ) . right + 0.05 , match - > first ( ) . bottom + 0.05 ) ;
const bool matchRectFullyVisible = isNormalizedRectangleFullyVisible ( matchRectWithBuffer , currentPage ) ;
2007-08-13 22:25:27 +00:00
// ..move the viewport to show the first of the searched word sequence centered
2013-12-29 22:27:30 +00:00
if ( moveViewport & & ! matchRectFullyVisible ) {
2007-08-13 22:25:27 +00:00
DocumentViewport searchViewport ( currentPage ) ;
searchViewport . rePos . enabled = true ;
searchViewport . rePos . normalizedX = ( match - > first ( ) . left + match - > first ( ) . right ) / 2.0 ;
searchViewport . rePos . normalizedY = ( match - > first ( ) . top + match - > first ( ) . bottom ) / 2.0 ;
2017-09-05 21:27:18 +00:00
m_parent - > setViewport ( searchViewport , nullptr , true ) ;
2007-08-13 22:25:27 +00:00
}
delete match ;
}
// notify observers about highlights changes
2022-03-18 21:57:13 +00:00
for ( int pageNumber : qAsConst ( * pagesToNotify ) ) {
for ( DocumentObserver * observer : qAsConst ( m_observers ) ) {
2007-08-13 22:25:27 +00:00
observer - > notifyPageChanged ( pageNumber , DocumentObserver : : Highlights ) ;
2022-03-18 21:57:13 +00:00
}
}
2007-08-13 22:25:27 +00:00
2007-08-23 22:16:37 +00:00
if ( foundAMatch ) {
2022-03-18 21:35:45 +00:00
Q_EMIT m_parent - > searchFinished ( searchID , Document : : MatchFound ) ;
2007-08-23 22:16:37 +00:00
} else {
2022-03-18 21:35:45 +00:00
Q_EMIT m_parent - > searchFinished ( searchID , Document : : NoMatchFound ) ;
2022-03-08 10:10:43 +00:00
}
2007-08-13 22:25:27 +00:00
delete pagesToNotify ;
}
2014-05-09 22:49:01 +00:00
void DocumentPrivate : : doContinueAllDocumentSearch ( void * pagesToNotifySet , void * pageMatchesMap , int currentPage , int searchID )
2007-08-13 22:25:27 +00:00
{
QMap < Page * , QVector < RegularAreaRect * > > * pageMatches = static_cast < QMap < Page * , QVector < RegularAreaRect * > > * > ( pageMatchesMap ) ;
QSet < int > * pagesToNotify = static_cast < QSet < int > * > ( pagesToNotifySet ) ;
2009-11-06 00:19:10 +00:00
RunningSearch * search = m_searches . value ( searchID ) ;
2007-08-13 22:25:27 +00:00
2009-11-06 00:19:10 +00:00
if ( m_searchCancelled | | ! search ) {
2007-08-13 22:25:27 +00:00
typedef QVector < RegularAreaRect * > MatchesVector ;
QApplication : : restoreOverrideCursor ( ) ;
2009-08-10 22:28:54 +00:00
2009-11-06 00:19:10 +00:00
if ( search ) {
search - > isCurrentlySearching = false ;
2022-03-08 10:10:43 +00:00
}
2009-08-10 22:28:54 +00:00
2022-03-18 21:35:45 +00:00
Q_EMIT m_parent - > searchFinished ( searchID , Document : : SearchCancelled ) ;
2022-03-18 21:57:13 +00:00
for ( const MatchesVector & mv : qAsConst ( * pageMatches ) ) {
2007-08-13 22:25:27 +00:00
qDeleteAll ( mv ) ;
2022-03-18 21:57:13 +00:00
}
2007-08-13 22:25:27 +00:00
delete pageMatches ;
delete pagesToNotify ;
return ;
}
if ( currentPage < m_pagesVector . count ( ) ) {
// get page (from the first to the last)
Page * page = m_pagesVector . at ( currentPage ) ;
int pageNumber = page - > number ( ) ; // redundant? is it == currentPage ?
// request search page if needed
if ( ! page - > hasTextPage ( ) ) {
m_parent - > requestTextPage ( pageNumber ) ;
2022-03-08 10:10:43 +00:00
}
2007-08-13 22:25:27 +00:00
// loop on a page adding highlights for all found items
2017-09-05 21:27:18 +00:00
RegularAreaRect * lastMatch = nullptr ;
2019-12-18 09:13:43 +00:00
while ( true ) {
2007-08-13 22:25:27 +00:00
if ( lastMatch ) {
2014-05-09 22:49:01 +00:00
lastMatch = page - > findText ( searchID , search - > cachedString , NextResult , search - > cachedCaseSensitivity , lastMatch ) ;
2007-08-13 22:25:27 +00:00
} else {
2014-05-09 22:49:01 +00:00
lastMatch = page - > findText ( searchID , search - > cachedString , FromTop , search - > cachedCaseSensitivity ) ;
2022-03-08 10:10:43 +00:00
}
2007-08-13 22:25:27 +00:00
if ( ! lastMatch ) {
break ;
2022-03-08 10:10:43 +00:00
}
2007-08-13 22:25:27 +00:00
2019-01-11 07:09:34 +00:00
// add highlight rect to the matches map
2007-08-13 22:25:27 +00:00
( * pageMatches ) [ page ] . append ( lastMatch ) ;
}
delete lastMatch ;
2019-12-19 22:18:31 +00:00
QTimer : : singleShot ( 0 , m_parent , [ this , pagesToNotifySet , pageMatches , currentPage , searchID ] { doContinueAllDocumentSearch ( pagesToNotifySet , pageMatches , currentPage + 1 , searchID ) ; } ) ;
2007-08-13 22:25:27 +00:00
} else {
// reset cursor to previous shape
QApplication : : restoreOverrideCursor ( ) ;
2009-11-06 00:19:10 +00:00
search - > isCurrentlySearching = false ;
2007-08-13 22:25:27 +00:00
bool foundAMatch = pageMatches - > count ( ) ! = 0 ;
QMap < Page * , QVector < RegularAreaRect * > > : : const_iterator it , itEnd ;
2008-11-11 18:48:40 +00:00
it = pageMatches - > constBegin ( ) ;
itEnd = pageMatches - > constEnd ( ) ;
2007-08-13 22:25:27 +00:00
for ( ; it ! = itEnd ; + + it ) {
2022-03-18 21:57:13 +00:00
for ( RegularAreaRect * match : it . value ( ) ) {
2014-05-09 22:49:01 +00:00
it . key ( ) - > d - > setHighlight ( searchID , match , search - > cachedColor ) ;
2007-08-13 22:25:27 +00:00
delete match ;
}
2009-11-06 00:19:10 +00:00
search - > highlightedPages . insert ( it . key ( ) - > number ( ) ) ;
2007-08-13 22:25:27 +00:00
pagesToNotify - > insert ( it . key ( ) - > number ( ) ) ;
}
2022-03-18 21:57:13 +00:00
for ( DocumentObserver * observer : qAsConst ( m_observers ) ) {
2007-09-09 10:50:36 +00:00
observer - > notifySetup ( m_pagesVector , 0 ) ;
2022-03-18 21:57:13 +00:00
}
2007-08-13 22:25:27 +00:00
// notify observers about highlights changes
2022-03-18 21:57:13 +00:00
for ( int pageNumber : qAsConst ( * pagesToNotify ) ) {
for ( DocumentObserver * observer : qAsConst ( m_observers ) ) {
2007-08-13 22:25:27 +00:00
observer - > notifyPageChanged ( pageNumber , DocumentObserver : : Highlights ) ;
2022-03-18 21:57:13 +00:00
}
}
2007-08-13 22:25:27 +00:00
2007-08-23 22:16:37 +00:00
if ( foundAMatch ) {
2022-03-18 21:35:45 +00:00
Q_EMIT m_parent - > searchFinished ( searchID , Document : : MatchFound ) ;
2007-08-23 22:16:37 +00:00
} else {
2022-03-18 21:35:45 +00:00
Q_EMIT m_parent - > searchFinished ( searchID , Document : : NoMatchFound ) ;
2022-03-08 10:10:43 +00:00
}
2007-08-13 22:25:27 +00:00
delete pageMatches ;
delete pagesToNotify ;
}
}
2014-05-09 22:49:01 +00:00
void DocumentPrivate : : doContinueGooglesDocumentSearch ( void * pagesToNotifySet , void * pageMatchesMap , int currentPage , int searchID , const QStringList & words )
2007-08-13 22:25:27 +00:00
{
typedef QPair < RegularAreaRect * , QColor > MatchColor ;
QMap < Page * , QVector < MatchColor > > * pageMatches = static_cast < QMap < Page * , QVector < MatchColor > > * > ( pageMatchesMap ) ;
QSet < int > * pagesToNotify = static_cast < QSet < int > * > ( pagesToNotifySet ) ;
2009-11-06 00:19:10 +00:00
RunningSearch * search = m_searches . value ( searchID ) ;
2007-08-13 22:25:27 +00:00
2009-11-06 00:19:10 +00:00
if ( m_searchCancelled | | ! search ) {
2007-08-13 22:25:27 +00:00
typedef QVector < MatchColor > MatchesVector ;
QApplication : : restoreOverrideCursor ( ) ;
2009-08-10 22:28:54 +00:00
2009-11-06 00:19:10 +00:00
if ( search ) {
search - > isCurrentlySearching = false ;
2022-03-08 10:10:43 +00:00
}
2009-08-10 22:28:54 +00:00
2022-03-18 21:35:45 +00:00
Q_EMIT m_parent - > searchFinished ( searchID , Document : : SearchCancelled ) ;
2007-08-13 22:25:27 +00:00
2022-03-18 21:57:13 +00:00
for ( const MatchesVector & mv : qAsConst ( * pageMatches ) ) {
for ( const MatchColor & mc : mv ) {
2007-08-13 22:25:27 +00:00
delete mc . first ;
2022-03-18 21:57:13 +00:00
}
2007-08-13 22:25:27 +00:00
}
delete pageMatches ;
delete pagesToNotify ;
return ;
}
const int wordCount = words . count ( ) ;
const int hueStep = ( wordCount > 1 ) ? ( 60 / ( wordCount - 1 ) ) : 60 ;
int baseHue , baseSat , baseVal ;
2014-05-09 22:49:01 +00:00
search - > cachedColor . getHsv ( & baseHue , & baseSat , & baseVal ) ;
2007-08-13 22:25:27 +00:00
if ( currentPage < m_pagesVector . count ( ) ) {
// get page (from the first to the last)
Page * page = m_pagesVector . at ( currentPage ) ;
int pageNumber = page - > number ( ) ; // redundant? is it == currentPage ?
// request search page if needed
if ( ! page - > hasTextPage ( ) ) {
m_parent - > requestTextPage ( pageNumber ) ;
2022-03-08 10:10:43 +00:00
}
2007-08-13 22:25:27 +00:00
// loop on a page adding highlights for all found items
bool allMatched = wordCount > 0 , anyMatched = false ;
for ( int w = 0 ; w < wordCount ; w + + ) {
const QString & word = words [ w ] ;
int newHue = baseHue - w * hueStep ;
if ( newHue < 0 ) {
newHue + = 360 ;
2022-03-08 10:10:43 +00:00
}
2007-08-13 22:25:27 +00:00
QColor wordColor = QColor : : fromHsv ( newHue , baseSat , baseVal ) ;
2017-09-05 21:27:18 +00:00
RegularAreaRect * lastMatch = nullptr ;
2007-08-13 22:25:27 +00:00
// add all highlights for current word
bool wordMatched = false ;
2019-12-18 09:13:43 +00:00
while ( true ) {
2007-08-13 22:25:27 +00:00
if ( lastMatch ) {
2014-05-09 22:49:01 +00:00
lastMatch = page - > findText ( searchID , word , NextResult , search - > cachedCaseSensitivity , lastMatch ) ;
2007-08-13 22:25:27 +00:00
} else {
2014-05-09 22:49:01 +00:00
lastMatch = page - > findText ( searchID , word , FromTop , search - > cachedCaseSensitivity ) ;
2022-03-08 10:10:43 +00:00
}
2007-08-13 22:25:27 +00:00
if ( ! lastMatch ) {
break ;
2022-03-08 10:10:43 +00:00
}
2007-08-13 22:25:27 +00:00
// add highligh rect to the matches map
( * pageMatches ) [ page ] . append ( MatchColor ( lastMatch , wordColor ) ) ;
wordMatched = true ;
}
allMatched = allMatched & & wordMatched ;
anyMatched = anyMatched | | wordMatched ;
}
// if not all words are present in page, remove partial highlights
2014-05-09 22:49:01 +00:00
const bool matchAll = search - > cachedType = = Document : : GoogleAll ;
2007-08-13 22:25:27 +00:00
if ( ! allMatched & & matchAll ) {
2022-03-18 21:57:13 +00:00
const QVector < MatchColor > & matches = ( * pageMatches ) [ page ] ;
for ( const MatchColor & mc : matches ) {
2007-08-13 22:25:27 +00:00
delete mc . first ;
2022-03-18 21:57:13 +00:00
}
2007-08-13 22:25:27 +00:00
pageMatches - > remove ( page ) ;
}
2019-12-19 22:18:31 +00:00
QTimer : : singleShot ( 0 , m_parent , [ this , pagesToNotifySet , pageMatches , currentPage , searchID , words ] { doContinueGooglesDocumentSearch ( pagesToNotifySet , pageMatches , currentPage + 1 , searchID , words ) ; } ) ;
2007-08-13 22:25:27 +00:00
} else {
// reset cursor to previous shape
QApplication : : restoreOverrideCursor ( ) ;
2009-11-06 00:19:10 +00:00
search - > isCurrentlySearching = false ;
2007-08-13 22:25:27 +00:00
bool foundAMatch = pageMatches - > count ( ) ! = 0 ;
QMap < Page * , QVector < MatchColor > > : : const_iterator it , itEnd ;
2008-11-11 18:48:40 +00:00
it = pageMatches - > constBegin ( ) ;
itEnd = pageMatches - > constEnd ( ) ;
2007-08-13 22:25:27 +00:00
for ( ; it ! = itEnd ; + + it ) {
2022-03-18 21:57:13 +00:00
for ( const MatchColor & mc : it . value ( ) ) {
2007-08-13 22:25:27 +00:00
it . key ( ) - > d - > setHighlight ( searchID , mc . first , mc . second ) ;
delete mc . first ;
}
2009-11-06 00:19:10 +00:00
search - > highlightedPages . insert ( it . key ( ) - > number ( ) ) ;
2007-08-13 22:25:27 +00:00
pagesToNotify - > insert ( it . key ( ) - > number ( ) ) ;
}
// send page lists to update observers (since some filter on bookmarks)
2022-03-18 21:57:13 +00:00
for ( DocumentObserver * observer : qAsConst ( m_observers ) ) {
2007-09-09 10:50:36 +00:00
observer - > notifySetup ( m_pagesVector , 0 ) ;
2022-03-18 21:57:13 +00:00
}
2007-08-13 22:25:27 +00:00
// notify observers about highlights changes
2022-03-18 21:57:13 +00:00
for ( int pageNumber : qAsConst ( * pagesToNotify ) ) {
for ( DocumentObserver * observer : qAsConst ( m_observers ) ) {
2007-08-13 22:25:27 +00:00
observer - > notifyPageChanged ( pageNumber , DocumentObserver : : Highlights ) ;
2022-03-18 21:57:13 +00:00
}
}
2007-08-13 22:25:27 +00:00
2007-08-23 22:16:37 +00:00
if ( foundAMatch ) {
2022-03-18 21:35:45 +00:00
Q_EMIT m_parent - > searchFinished ( searchID , Document : : MatchFound ) ;
2007-08-23 22:16:37 +00:00
} else {
2022-03-18 21:35:45 +00:00
Q_EMIT m_parent - > searchFinished ( searchID , Document : : NoMatchFound ) ;
2022-03-08 10:10:43 +00:00
}
2007-08-13 22:25:27 +00:00
delete pageMatches ;
delete pagesToNotify ;
}
}
2017-03-02 21:45:45 +00:00
QVariant DocumentPrivate : : documentMetaData ( const Generator : : DocumentMetaDataKey key , const QVariant & option ) const
2007-09-14 22:16:00 +00:00
{
2017-03-02 20:04:59 +00:00
switch ( key ) {
case Generator : : PaperColorMetaData : {
bool giveDefault = option . toBool ( ) ;
QColor color ;
if ( ( SettingsCore : : renderMode ( ) = = SettingsCore : : EnumRenderMode : : Paper ) & & SettingsCore : : changeColors ( ) ) {
color = SettingsCore : : paperColor ( ) ;
} else if ( giveDefault ) {
color = Qt : : white ;
}
return color ;
} break ;
case Generator : : TextAntialiasMetaData :
switch ( SettingsCore : : textAntialias ( ) ) {
case SettingsCore : : EnumTextAntialias : : Enabled :
return true ;
break ;
case SettingsCore : : EnumTextAntialias : : Disabled :
return false ;
break ;
}
break ;
case Generator : : GraphicsAntialiasMetaData :
switch ( SettingsCore : : graphicsAntialias ( ) ) {
case SettingsCore : : EnumGraphicsAntialias : : Enabled :
return true ;
break ;
case SettingsCore : : EnumGraphicsAntialias : : Disabled :
return false ;
break ;
}
break ;
case Generator : : TextHintingMetaData :
switch ( SettingsCore : : textHinting ( ) ) {
case SettingsCore : : EnumTextHinting : : Enabled :
return true ;
break ;
case SettingsCore : : EnumTextHinting : : Disabled :
return false ;
break ;
}
break ;
2009-10-08 21:55:51 +00:00
}
2007-09-14 22:16:00 +00:00
return QVariant ( ) ;
2014-03-02 12:59:49 +00:00
}
2013-04-05 22:22:48 +00:00
2013-12-29 22:27:30 +00:00
bool DocumentPrivate : : isNormalizedRectangleFullyVisible ( const Okular : : NormalizedRect & rectOfInterest , int rectPage )
{
bool rectFullyVisible = false ;
const QVector < Okular : : VisiblePageRect * > & visibleRects = m_parent - > visiblePageRects ( ) ;
QVector < Okular : : VisiblePageRect * > : : const_iterator vEnd = visibleRects . end ( ) ;
QVector < Okular : : VisiblePageRect * > : : const_iterator vIt = visibleRects . begin ( ) ;
for ( ; ( vIt ! = vEnd ) & & ! rectFullyVisible ; + + vIt ) {
if ( ( * vIt ) - > pageNumber = = rectPage & & ( * vIt ) - > rect . contains ( rectOfInterest . left , rectOfInterest . top ) & & ( * vIt ) - > rect . contains ( rectOfInterest . right , rectOfInterest . bottom ) ) {
rectFullyVisible = true ;
}
}
return rectFullyVisible ;
}
2014-10-08 22:17:53 +00:00
struct pdfsyncpoint {
QString file ;
qlonglong x ;
qlonglong y ;
int row ;
int column ;
int page ;
} ;
void DocumentPrivate : : loadSyncFile ( const QString & filePath )
{
QFile f ( filePath + QLatin1String ( " sync " ) ) ;
if ( ! f . open ( QIODevice : : ReadOnly ) ) {
return ;
2022-03-08 10:10:43 +00:00
}
2014-10-08 22:17:53 +00:00
QTextStream ts ( & f ) ;
// first row: core name of the pdf output
const QString coreName = ts . readLine ( ) ;
// second row: version string, in the form 'Version %u'
2020-01-26 11:40:33 +00:00
const QString versionstr = ts . readLine ( ) ;
// anchor the pattern with \A and \z to match the entire subject string
// TODO: with Qt 5.12 QRegularExpression::anchoredPattern() can be used instead
QRegularExpression versionre ( QStringLiteral ( " \\ AVersion \\ d+ \\ z " ) , QRegularExpression : : CaseInsensitiveOption ) ;
QRegularExpressionMatch match = versionre . match ( versionstr ) ;
if ( ! match . hasMatch ( ) ) {
2014-10-08 22:17:53 +00:00
return ;
2020-01-26 11:40:33 +00:00
}
2014-10-08 22:17:53 +00:00
QHash < int , pdfsyncpoint > points ;
QStack < QString > fileStack ;
int currentpage = - 1 ;
const QLatin1String texStr ( " .tex " ) ;
const QChar spaceChar = QChar : : fromLatin1 ( ' ' ) ;
fileStack . push ( coreName + texStr ) ;
const QSizeF dpi = m_generator - > dpi ( ) ;
QString line ;
while ( ! ts . atEnd ( ) ) {
line = ts . readLine ( ) ;
2023-05-11 11:16:55 +00:00
const QStringList tokens = line . split ( spaceChar , Qt : : SkipEmptyParts ) ;
2014-10-08 22:17:53 +00:00
const int tokenSize = tokens . count ( ) ;
if ( tokenSize < 1 ) {
continue ;
2022-03-08 10:10:43 +00:00
}
2014-10-08 22:17:53 +00:00
if ( tokens . first ( ) = = QLatin1String ( " l " ) & & tokenSize > = 3 ) {
int id = tokens . at ( 1 ) . toInt ( ) ;
QHash < int , pdfsyncpoint > : : const_iterator it = points . constFind ( id ) ;
if ( it = = points . constEnd ( ) ) {
pdfsyncpoint pt ;
pt . x = 0 ;
pt . y = 0 ;
pt . row = tokens . at ( 2 ) . toInt ( ) ;
pt . column = 0 ; // TODO
pt . page = - 1 ;
pt . file = fileStack . top ( ) ;
points [ id ] = pt ;
}
} else if ( tokens . first ( ) = = QLatin1String ( " s " ) & & tokenSize > = 2 ) {
currentpage = tokens . at ( 1 ) . toInt ( ) - 1 ;
} else if ( tokens . first ( ) = = QLatin1String ( " p* " ) & & tokenSize > = 4 ) {
// TODO
2014-10-09 05:28:59 +00:00
qCDebug ( OkularCoreDebug ) < < " PdfSync: 'p*' line ignored " ;
2014-10-08 22:17:53 +00:00
} else if ( tokens . first ( ) = = QLatin1String ( " p " ) & & tokenSize > = 4 ) {
int id = tokens . at ( 1 ) . toInt ( ) ;
QHash < int , pdfsyncpoint > : : iterator it = points . find ( id ) ;
if ( it ! = points . end ( ) ) {
it - > x = tokens . at ( 2 ) . toInt ( ) ;
it - > y = tokens . at ( 3 ) . toInt ( ) ;
it - > page = currentpage ;
}
} else if ( line . startsWith ( QLatin1Char ( ' ( ' ) ) & & tokenSize = = 1 ) {
QString newfile = line ;
// chop the leading '('
newfile . remove ( 0 , 1 ) ;
if ( ! newfile . endsWith ( texStr ) ) {
newfile + = texStr ;
}
fileStack . push ( newfile ) ;
} else if ( line = = QLatin1String ( " ) " )) {
if ( ! fileStack . isEmpty ( ) ) {
fileStack . pop ( ) ;
} else {
2014-10-09 05:28:59 +00:00
qCDebug ( OkularCoreDebug ) < < " PdfSync: going one level down too much " ;
2022-03-08 10:10:43 +00:00
}
2014-10-08 22:17:53 +00:00
} else {
2014-10-09 05:28:59 +00:00
qCDebug ( OkularCoreDebug ) . nospace ( ) < < " PdfSync: unknown line format: ' " < < line < < " ' " ;
2022-03-08 10:10:43 +00:00
}
2014-10-08 22:17:53 +00:00
}
2022-03-21 23:59:42 +00:00
QVector < QList < Okular : : SourceRefObjectRect * > > refRects ( m_pagesVector . size ( ) ) ;
2019-12-09 13:16:55 +00:00
for ( const pdfsyncpoint & pt : qAsConst ( points ) ) {
2014-10-08 22:17:53 +00:00
// drop pdfsync points not completely valid
if ( pt . page < 0 | | pt . page > = m_pagesVector . size ( ) ) {
continue ;
2022-03-08 10:10:43 +00:00
}
2014-10-08 22:17:53 +00:00
// magic numbers for TeX's RSU's (Ridiculously Small Units) conversion to pixels
Okular : : NormalizedPoint p ( ( pt . x * dpi . width ( ) ) / ( 72.27 * 65536.0 * m_pagesVector [ pt . page ] - > width ( ) ) , ( pt . y * dpi . height ( ) ) / ( 72.27 * 65536.0 * m_pagesVector [ pt . page ] - > height ( ) ) ) ;
QString file = pt . file ;
Okular : : SourceReference * sourceRef = new Okular : : SourceReference ( file , pt . row , pt . column ) ;
refRects [ pt . page ] . append ( new Okular : : SourceRefObjectRect ( p , sourceRef ) ) ;
}
for ( int i = 0 ; i < refRects . size ( ) ; + + i ) {
if ( ! refRects . at ( i ) . isEmpty ( ) ) {
m_pagesVector [ i ] - > setSourceReferences ( refRects . at ( i ) ) ;
2022-03-08 10:10:43 +00:00
}
}
2014-10-08 22:17:53 +00:00
}
2018-02-14 16:48:10 +00:00
void DocumentPrivate : : clearAndWaitForRequests ( )
{
m_pixmapRequestsMutex . lock ( ) ;
2022-03-31 15:05:15 +00:00
std : : list < PixmapRequest * > : : const_iterator sIt = m_pixmapRequestsStack . begin ( ) ;
std : : list < PixmapRequest * > : : const_iterator sEnd = m_pixmapRequestsStack . end ( ) ;
2018-02-14 16:48:10 +00:00
for ( ; sIt ! = sEnd ; + + sIt ) {
delete * sIt ;
2022-03-08 10:10:43 +00:00
}
2018-02-14 16:48:10 +00:00
m_pixmapRequestsStack . clear ( ) ;
m_pixmapRequestsMutex . unlock ( ) ;
QEventLoop loop ;
bool startEventLoop = false ;
do {
m_pixmapRequestsMutex . lock ( ) ;
2022-03-31 15:05:15 +00:00
startEventLoop = ! m_executingPixmapRequests . empty ( ) ;
2018-02-15 09:14:45 +00:00
if ( m_generator - > hasFeature ( Generator : : SupportsCancelling ) ) {
for ( PixmapRequest * executingRequest : qAsConst ( m_executingPixmapRequests ) ) {
executingRequest - > d - > mShouldAbortRender = 1 ;
2022-03-08 10:10:43 +00:00
}
2018-02-15 09:14:45 +00:00
if ( m_generator - > d_ptr - > mTextPageGenerationThread ) {
m_generator - > d_ptr - > mTextPageGenerationThread - > abortExtraction ( ) ;
2022-03-08 10:10:43 +00:00
}
2018-02-15 09:14:45 +00:00
}
2018-02-14 16:48:10 +00:00
m_pixmapRequestsMutex . unlock ( ) ;
if ( startEventLoop ) {
m_closingLoop = & loop ;
loop . exec ( ) ;
m_closingLoop = nullptr ;
}
} while ( startEventLoop ) ;
}
2019-08-22 22:16:56 +00:00
int DocumentPrivate : : findFieldPageNumber ( Okular : : FormField * field )
{
// Lookup the page of the FormField
int foundPage = - 1 ;
for ( uint pageIdx = 0 , nPages = m_parent - > pages ( ) ; pageIdx < nPages ; pageIdx + + ) {
const Page * p = m_parent - > page ( pageIdx ) ;
if ( p & & p - > formFields ( ) . contains ( field ) ) {
foundPage = static_cast < int > ( pageIdx ) ;
break ;
}
}
return foundPage ;
}
2019-10-05 18:18:28 +00:00
void DocumentPrivate : : executeScriptEvent ( const std : : shared_ptr < Event > & event , const Okular : : ScriptAction * linkscript )
2019-08-22 22:16:56 +00:00
{
if ( ! m_scripter ) {
m_scripter = new Scripter ( this ) ;
}
m_scripter - > setEvent ( event . get ( ) ) ;
m_scripter - > execute ( linkscript - > scriptType ( ) , linkscript - > script ( ) ) ;
// Clear out the event after execution
m_scripter - > setEvent ( nullptr ) ;
}
2007-03-30 17:46:50 +00:00
Document : : Document ( QWidget * widget )
2017-09-05 21:27:18 +00:00
: QObject ( nullptr )
, d ( new DocumentPrivate ( this ) )
2006-07-01 22:17:53 +00:00
{
2008-09-21 22:44:42 +00:00
d - > m_widget = widget ;
2007-10-28 18:31:33 +00:00
d - > m_bookmarkManager = new BookmarkManager ( d ) ;
2007-10-04 21:41:19 +00:00
d - > m_viewportIterator = d - > m_viewportHistory . insert ( d - > m_viewportHistory . end ( ) , DocumentViewport ( ) ) ;
2013-04-05 22:22:48 +00:00
d - > m_undoStack = new QUndoStack ( this ) ;
2007-03-24 10:47:22 +00:00
2019-12-19 22:18:31 +00:00
connect ( SettingsCore : : self ( ) , & SettingsCore : : configChanged , this , [ this ] { d - > _o_configChanged ( ) ; } ) ;
2014-10-01 12:02:01 +00:00
connect ( d - > m_undoStack , & QUndoStack : : canUndoChanged , this , & Document : : canUndoChanged ) ;
connect ( d - > m_undoStack , & QUndoStack : : canRedoChanged , this , & Document : : canRedoChanged ) ;
2017-11-14 13:52:02 +00:00
connect ( d - > m_undoStack , & QUndoStack : : cleanChanged , this , & Document : : undoHistoryCleanChanged ) ;
2007-07-07 20:35:01 +00:00
qRegisterMetaType < Okular : : FontInfo > ( ) ;
2006-07-01 22:17:53 +00:00
}
2007-01-02 19:05:49 +00:00
Document : : ~ Document ( )
2005-01-27 17:31:07 +00:00
{
2007-01-02 19:05:49 +00:00
// delete generator, pages, and related stuff
closeDocument ( ) ;
2005-01-27 17:31:07 +00:00
2008-11-11 18:48:40 +00:00
QSet < View * > : : const_iterator viewIt = d - > m_views . constBegin ( ) , viewEnd = d - > m_views . constEnd ( ) ;
2008-04-27 11:05:59 +00:00
for ( ; viewIt ! = viewEnd ; + + viewIt ) {
View * v = * viewIt ;
2017-09-05 21:27:18 +00:00
v - > d_func ( ) - > document = nullptr ;
2008-04-27 11:05:59 +00:00
}
2007-02-05 15:54:19 +00:00
// delete the bookmark manager
delete d - > m_bookmarkManager ;
2007-01-28 15:46:10 +00:00
// delete the loaded generators
2007-02-03 23:09:40 +00:00
QHash < QString , GeneratorInfo > : : const_iterator it = d - > m_loadedGenerators . constBegin ( ) , itEnd = d - > m_loadedGenerators . constEnd ( ) ;
2007-01-28 15:46:10 +00:00
for ( ; it ! = itEnd ; + + it ) {
2007-01-30 12:43:43 +00:00
d - > unloadGenerator ( it . value ( ) ) ;
2022-03-08 10:10:43 +00:00
}
2007-01-28 15:46:10 +00:00
d - > m_loadedGenerators . clear ( ) ;
2007-01-02 19:05:49 +00:00
// delete the private structure
delete d ;
2005-01-27 17:31:07 +00:00
}
2015-01-29 19:55:57 +00:00
QString DocumentPrivate : : docDataFileName ( const QUrl & url , qint64 document_size )
2014-08-08 20:35:53 +00:00
{
QString fn = url . fileName ( ) ;
2016-07-11 20:07:57 +00:00
fn = QString : : number ( document_size ) + QLatin1Char ( ' . ' ) + fn + QStringLiteral ( " .xml " ) ;
2016-01-30 16:31:01 +00:00
QString docdataDir = QStandardPaths : : writableLocation ( QStandardPaths : : GenericDataLocation ) + QStringLiteral ( " /okular/docdata " ) ;
// make sure that the okular/docdata/ directory exists (probably this used to be handled by KStandardDirs)
if ( ! QFileInfo : : exists ( docdataDir ) ) {
qCDebug ( OkularCoreDebug ) < < " creating docdata folder " < < docdataDir ;
QDir ( ) . mkpath ( docdataDir ) ;
}
QString newokularfile = docdataDir + QLatin1Char ( ' / ' ) + fn ;
// we don't want to accidentally migrate old files when running unit tests
if ( ! QFile : : exists ( newokularfile ) & & ! QStandardPaths : : isTestModeEnabled ( ) ) {
// see if an KDE4 file still exists
static Kdelibs4Migration k4migration ;
QString oldfile = k4migration . locateLocal ( " data " , QStringLiteral ( " okular/docdata/ " ) + fn ) ;
if ( oldfile . isEmpty ( ) ) {
oldfile = k4migration . locateLocal ( " data " , QStringLiteral ( " kpdf/ " ) + fn ) ;
}
if ( ! oldfile . isEmpty ( ) & & QFile : : exists ( oldfile ) ) {
2014-08-08 20:35:53 +00:00
// ### copy or move?
2016-01-30 16:31:01 +00:00
if ( ! QFile : : copy ( oldfile , newokularfile ) ) {
2014-08-08 20:35:53 +00:00
return QString ( ) ;
2022-03-08 10:10:43 +00:00
}
2014-08-08 20:35:53 +00:00
}
}
return newokularfile ;
}
2015-11-28 19:24:41 +00:00
QVector < KPluginMetaData > DocumentPrivate : : availableGenerators ( )
{
static QVector < KPluginMetaData > result ;
if ( result . isEmpty ( ) ) {
2019-04-25 21:10:48 +00:00
result = KPluginLoader : : findPlugins ( QStringLiteral ( " okular/generators " ) ) ;
2015-11-28 19:24:41 +00:00
}
return result ;
}
2014-09-11 13:18:05 +00:00
2016-12-08 22:29:13 +00:00
KPluginMetaData DocumentPrivate : : generatorForMimeType ( const QMimeType & type , QWidget * widget , const QVector < KPluginMetaData > & triedOffers )
2015-11-28 19:24:41 +00:00
{
2016-07-13 01:23:09 +00:00
// First try to find an exact match, and then look for more general ones (e. g. the plain text one)
// Ideally we would rank these by "closeness", but that might be overdoing it
2015-11-28 19:24:41 +00:00
const QVector < KPluginMetaData > available = availableGenerators ( ) ;
QVector < KPluginMetaData > offers ;
2016-07-13 01:23:09 +00:00
QVector < KPluginMetaData > exactMatches ;
QMimeDatabase mimeDatabase ;
2015-11-28 19:24:41 +00:00
for ( const KPluginMetaData & md : available ) {
2016-12-08 22:29:13 +00:00
if ( triedOffers . contains ( md ) ) {
continue ;
2022-03-08 10:10:43 +00:00
}
2016-12-08 22:29:13 +00:00
2019-12-09 13:16:55 +00:00
const QStringList mimetypes = md . mimeTypes ( ) ;
for ( const QString & supported : mimetypes ) {
2016-07-13 01:23:09 +00:00
QMimeType mimeType = mimeDatabase . mimeTypeForName ( supported ) ;
2017-01-08 18:38:42 +00:00
if ( mimeType = = type & & ! exactMatches . contains ( md ) ) {
2016-07-13 01:23:09 +00:00
exactMatches < < md ;
}
2017-01-08 18:38:42 +00:00
if ( type . inherits ( supported ) & & ! offers . contains ( md ) ) {
2015-11-28 19:24:41 +00:00
offers < < md ;
}
}
}
2016-07-13 01:23:09 +00:00
if ( ! exactMatches . isEmpty ( ) ) {
offers = exactMatches ;
}
2015-11-28 19:24:41 +00:00
if ( offers . isEmpty ( ) ) {
return KPluginMetaData ( ) ;
}
int hRank = 0 ;
// best ranked offer search
int offercount = offers . size ( ) ;
if ( offercount > 1 ) {
// sort the offers: the offers with an higher priority come before
auto cmp = [ ] ( const KPluginMetaData & s1 , const KPluginMetaData & s2 ) {
const QString property = QStringLiteral ( " X-KDE-Priority " ) ;
return s1 . rawData ( ) [ property ] . toInt ( ) > s2 . rawData ( ) [ property ] . toInt ( ) ;
} ;
std : : stable_sort ( offers . begin ( ) , offers . end ( ) , cmp ) ;
if ( SettingsCore : : chooseGenerators ( ) ) {
QStringList list ;
for ( int i = 0 ; i < offercount ; + + i ) {
list < < offers . at ( i ) . pluginId ( ) ;
}
ChooseEngineDialog choose ( list , type , widget ) ;
if ( choose . exec ( ) = = QDialog : : Rejected ) {
return KPluginMetaData ( ) ;
2022-03-08 10:10:43 +00:00
}
2015-11-28 19:24:41 +00:00
hRank = choose . selectedGenerator ( ) ;
}
}
Q_ASSERT ( hRank < offers . size ( ) ) ;
return offers . at ( hRank ) ;
}
2014-09-11 13:18:05 +00:00
2015-01-29 19:55:57 +00:00
Document : : OpenResult Document : : openDocument ( const QString & docFile , const QUrl & url , const QMimeType & _mime , const QString & password )
2006-06-02 20:42:57 +00:00
{
2014-09-11 13:18:05 +00:00
QMimeDatabase db ;
QMimeType mime = _mime ;
2007-01-12 22:49:14 +00:00
QByteArray filedata ;
2018-05-09 00:45:12 +00:00
int fd = - 1 ;
if ( url . scheme ( ) = = QLatin1String ( " fd " ) ) {
bool ok ;
2019-04-25 21:10:48 +00:00
fd = url . path ( ) . midRef ( 1 ) . toInt ( & ok ) ;
2018-05-09 00:45:12 +00:00
if ( ! ok ) {
return OpenError ;
}
} else if ( url . fileName ( ) = = QLatin1String ( " - " ) ) {
fd = 0 ;
}
2013-04-17 20:13:02 +00:00
bool triedMimeFromFileContent = false ;
2018-05-09 00:45:12 +00:00
if ( fd < 0 ) {
2014-09-11 13:18:05 +00:00
if ( ! mime . isValid ( ) ) {
2014-05-09 17:56:16 +00:00
return OpenError ;
2022-03-08 10:10:43 +00:00
}
2007-01-12 22:49:14 +00:00
d - > m_url = url ;
d - > m_docFileName = docFile ;
2014-05-11 14:36:28 +00:00
if ( ! d - > updateMetadataXmlNameAndDocSize ( ) ) {
return OpenError ;
2022-03-08 10:10:43 +00:00
}
2007-01-12 22:49:14 +00:00
} else {
QFile qstdin ;
2018-05-09 00:45:12 +00:00
const bool ret = qstdin . open ( fd , QIODevice : : ReadOnly , QFileDevice : : AutoCloseHandle ) ;
if ( ! ret ) {
qWarning ( ) < < " failed to read " < < url < < filedata ;
return OpenError ;
}
2007-01-12 22:49:14 +00:00
filedata = qstdin . readAll ( ) ;
2014-09-11 13:18:05 +00:00
mime = db . mimeTypeForData ( filedata ) ;
if ( ! mime . isValid ( ) | | mime . isDefault ( ) ) {
2014-05-09 17:56:16 +00:00
return OpenError ;
2022-03-08 10:10:43 +00:00
}
2014-05-11 14:36:28 +00:00
d - > m_docSize = filedata . size ( ) ;
2013-04-17 20:13:02 +00:00
triedMimeFromFileContent = true ;
2007-01-02 19:05:49 +00:00
}
2007-01-02 22:37:55 +00:00
2018-05-09 00:45:12 +00:00
const bool fromFileDescriptor = fd > = 0 ;
2007-01-02 19:05:49 +00:00
// 0. load Generator
// request only valid non-disabled plugins suitable for the mimetype
2015-11-28 19:24:41 +00:00
KPluginMetaData offer = DocumentPrivate : : generatorForMimeType ( mime , d - > m_widget ) ;
if ( ! offer . isValid ( ) & & ! triedMimeFromFileContent ) {
2018-10-01 22:33:58 +00:00
QMimeType newmime = db . mimeTypeForFile ( docFile , QMimeDatabase : : MatchContent ) ;
2013-04-17 20:13:02 +00:00
triedMimeFromFileContent = true ;
2015-11-28 19:24:41 +00:00
if ( newmime ! = mime ) {
2008-11-09 14:21:20 +00:00
mime = newmime ;
2015-11-28 19:24:41 +00:00
offer = DocumentPrivate : : generatorForMimeType ( mime , d - > m_widget ) ;
2008-11-09 14:21:20 +00:00
}
2015-11-28 19:24:41 +00:00
if ( ! offer . isValid ( ) ) {
2013-04-17 20:00:02 +00:00
// There's still no offers, do a final mime search based on the filename
2013-06-24 10:46:16 +00:00
// We need this because sometimes (e.g. when downloading from a webserver) the mimetype we
// use is the one fed by the server, that may be wrong
2015-11-28 19:24:41 +00:00
newmime = db . mimeTypeForUrl ( url ) ;
if ( ! newmime . isDefault ( ) & & newmime ! = mime ) {
2013-04-17 20:00:02 +00:00
mime = newmime ;
2015-11-28 19:24:41 +00:00
offer = DocumentPrivate : : generatorForMimeType ( mime , d - > m_widget ) ;
2013-04-17 20:00:02 +00:00
}
}
2008-11-09 14:21:20 +00:00
}
2015-11-28 19:24:41 +00:00
if ( ! offer . isValid ( ) ) {
2020-02-10 23:22:06 +00:00
d - > m_openError = i18n ( " Can not find a plugin which is able to handle the document being passed. " ) ;
2022-03-18 21:35:45 +00:00
Q_EMIT error ( d - > m_openError , - 1 ) ;
2014-09-11 19:12:27 +00:00
qCWarning ( OkularCoreDebug ) . nospace ( ) < < " No plugin for mimetype ' " < < mime . name ( ) < < " '. " ;
2014-05-09 17:56:16 +00:00
return OpenError ;
2006-10-15 19:37:14 +00:00
}
2004-09-15 20:45:00 +00:00
2008-11-09 14:21:20 +00:00
// 1. load Document
2018-05-09 00:45:12 +00:00
OpenResult openResult = d - > openDocumentInternal ( offer , fromFileDescriptor , docFile , filedata , password ) ;
2016-12-08 22:29:13 +00:00
if ( openResult = = OpenError ) {
QVector < KPluginMetaData > triedOffers ;
triedOffers < < offer ;
offer = DocumentPrivate : : generatorForMimeType ( mime , d - > m_widget , triedOffers ) ;
while ( offer . isValid ( ) ) {
2018-05-09 00:45:12 +00:00
openResult = d - > openDocumentInternal ( offer , fromFileDescriptor , docFile , filedata , password ) ;
2016-12-08 22:29:13 +00:00
if ( openResult = = OpenError ) {
triedOffers < < offer ;
offer = DocumentPrivate : : generatorForMimeType ( mime , d - > m_widget , triedOffers ) ;
} else {
break ;
2022-03-08 10:10:43 +00:00
}
2016-12-08 22:29:13 +00:00
}
if ( openResult = = OpenError & & ! triedMimeFromFileContent ) {
2018-10-01 22:33:58 +00:00
QMimeType newmime = db . mimeTypeForFile ( docFile , QMimeDatabase : : MatchContent ) ;
2016-12-08 22:29:13 +00:00
triedMimeFromFileContent = true ;
if ( newmime ! = mime ) {
mime = newmime ;
offer = DocumentPrivate : : generatorForMimeType ( mime , d - > m_widget , triedOffers ) ;
while ( offer . isValid ( ) ) {
2018-05-09 00:45:12 +00:00
openResult = d - > openDocumentInternal ( offer , fromFileDescriptor , docFile , filedata , password ) ;
2016-12-08 22:29:13 +00:00
if ( openResult = = OpenError ) {
triedOffers < < offer ;
offer = DocumentPrivate : : generatorForMimeType ( mime , d - > m_widget , triedOffers ) ;
} else {
break ;
2022-03-08 10:10:43 +00:00
}
2016-12-08 22:29:13 +00:00
}
2007-01-12 22:49:14 +00:00
}
}
2016-12-08 22:29:13 +00:00
if ( openResult = = OpenSuccess ) {
// Clear errors, since we're trying various generators, maybe one of them errored out
// but we finally succeeded
// TODO one can still see the error message animating out but since this is a very rare
// condition we can leave this for future work
2022-03-18 21:35:45 +00:00
Q_EMIT error ( QString ( ) , - 1 ) ;
2007-01-12 22:49:14 +00:00
}
}
2014-05-09 17:56:16 +00:00
if ( openResult ! = OpenSuccess ) {
return openResult ;
2004-12-21 12:38:52 +00:00
}
2005-01-18 16:43:36 +00:00
2014-10-08 22:17:53 +00:00
// no need to check for the existence of a synctex file, no parser will be
// created if none exists
2017-09-05 21:27:18 +00:00
d - > m_synctex_scanner = synctex_scanner_new_with_output_file ( QFile : : encodeName ( docFile ) . constData ( ) , nullptr , 1 ) ;
2014-10-08 22:53:54 +00:00
if ( ! d - > m_synctex_scanner & & QFile : : exists ( docFile + QLatin1String ( " sync " ) ) ) {
2014-10-08 22:17:53 +00:00
d - > loadSyncFile ( docFile ) ;
}
2015-11-28 19:24:41 +00:00
d - > m_generatorName = offer . pluginId ( ) ;
2013-07-30 15:00:32 +00:00
d - > m_pageController = new PageController ( ) ;
2019-12-19 22:18:31 +00:00
connect ( d - > m_pageController , & PageController : : rotationFinished , this , [ this ] ( int p , Okular : : Page * op ) { d - > rotationFinished ( p , op ) ; } ) ;
2012-05-30 16:38:51 +00:00
2019-12-09 13:16:55 +00:00
for ( Page * p : qAsConst ( d - > m_pagesVector ) ) {
2011-01-29 15:38:18 +00:00
p - > d - > m_doc = d ;
2022-03-08 10:10:43 +00:00
}
2007-03-11 22:35:14 +00:00
2014-05-11 11:37:22 +00:00
d - > m_metadataLoadingCompleted = false ;
2014-09-09 14:08:32 +00:00
d - > m_docdataMigrationNeeded = false ;
2012-05-30 16:38:51 +00:00
2012-05-14 13:51:58 +00:00
// 2. load Additional Data (bookmarks, local annotations and metadata) about the document
2008-11-15 14:15:31 +00:00
if ( d - > m_archiveData ) {
2018-01-31 10:17:16 +00:00
// QTemporaryFile is weird and will return false in exists if fileName wasn't called before
d - > m_archiveData - > metadataFile . fileName ( ) ;
2014-05-11 13:55:46 +00:00
d - > loadDocumentInfo ( d - > m_archiveData - > metadataFile , LoadPageInfo ) ;
d - > loadDocumentInfo ( LoadGeneralInfo ) ;
2008-11-15 14:15:31 +00:00
} else {
2014-09-09 14:08:32 +00:00
if ( d - > loadDocumentInfo ( LoadPageInfo ) ) {
d - > m_docdataMigrationNeeded = true ;
2022-03-08 10:10:43 +00:00
}
2014-09-09 14:08:32 +00:00
d - > loadDocumentInfo ( LoadGeneralInfo ) ;
2008-11-15 14:15:31 +00:00
}
2012-05-30 16:38:51 +00:00
2014-05-11 11:37:22 +00:00
d - > m_metadataLoadingCompleted = true ;
2007-01-02 19:05:49 +00:00
d - > m_bookmarkManager - > setUrl ( d - > m_url ) ;
2004-09-15 20:45:00 +00:00
2018-11-14 19:12:15 +00:00
// 3. setup observers internal lists and data
2014-05-11 15:38:30 +00:00
foreachObserver ( notifySetup ( d - > m_pagesVector , DocumentObserver : : DocumentChanged | DocumentObserver : : UrlChanged ) ) ;
2004-12-10 16:04:45 +00:00
2007-01-02 19:05:49 +00:00
// 4. set initial page (restoring the page saved in xml if loaded)
DocumentViewport loadedViewport = ( * d - > m_viewportIterator ) ;
if ( loadedViewport . isValid ( ) ) {
( * d - > m_viewportIterator ) = DocumentViewport ( ) ;
if ( loadedViewport . pageNumber > = ( int ) d - > m_pagesVector . size ( ) ) {
loadedViewport . pageNumber = d - > m_pagesVector . size ( ) - 1 ;
2022-03-08 10:10:43 +00:00
}
2007-01-02 19:05:49 +00:00
} else {
loadedViewport . pageNumber = 0 ;
2022-03-08 10:10:43 +00:00
}
2007-01-02 19:05:49 +00:00
setViewport ( loadedViewport ) ;
2004-12-21 12:38:52 +00:00
2007-01-02 19:05:49 +00:00
// start bookmark saver timer
if ( ! d - > m_saveBookmarksTimer ) {
d - > m_saveBookmarksTimer = new QTimer ( this ) ;
2019-12-19 22:18:31 +00:00
connect ( d - > m_saveBookmarksTimer , & QTimer : : timeout , this , [ this ] { d - > saveDocumentInfo ( ) ; } ) ;
2007-01-02 19:05:49 +00:00
}
d - > m_saveBookmarksTimer - > start ( 5 * 60 * 1000 ) ;
2005-02-18 18:24:45 +00:00
2007-01-02 19:05:49 +00:00
// start memory check timer
if ( ! d - > m_memCheckTimer ) {
d - > m_memCheckTimer = new QTimer ( this ) ;
2019-12-19 22:18:31 +00:00
connect ( d - > m_memCheckTimer , & QTimer : : timeout , this , [ this ] { d - > slotTimedMemoryCheck ( ) ; } ) ;
2007-01-02 19:05:49 +00:00
}
2020-03-03 00:15:19 +00:00
d - > m_memCheckTimer - > start ( kMemCheckTime ) ;
2005-02-18 18:24:45 +00:00
2009-05-13 14:24:30 +00:00
const DocumentViewport nextViewport = d - > nextDocumentViewport ( ) ;
if ( nextViewport . isValid ( ) ) {
setViewport ( nextViewport ) ;
2007-01-02 19:05:49 +00:00
d - > m_nextDocumentViewport = DocumentViewport ( ) ;
2009-05-13 14:24:30 +00:00
d - > m_nextDocumentDestination = QString ( ) ;
2007-01-02 19:05:49 +00:00
}
2005-02-18 18:24:45 +00:00
2023-06-21 15:50:58 +00:00
AudioPlayer : : instance ( ) - > setDocument ( fromFileDescriptor ? QUrl ( ) : d - > m_url , this ) ;
2007-07-10 18:24:18 +00:00
2016-07-11 20:07:57 +00:00
const QStringList docScripts = d - > m_generator - > metaData ( QStringLiteral ( " DocumentScripts " ) , QStringLiteral ( " JavaScript " ) ) . toStringList ( ) ;
2008-04-13 22:31:59 +00:00
if ( ! docScripts . isEmpty ( ) ) {
d - > m_scripter = new Scripter ( d ) ;
2019-12-09 13:16:55 +00:00
for ( const QString & docscript : docScripts ) {
2008-04-13 22:31:59 +00:00
d - > m_scripter - > execute ( JavaScript , docscript ) ;
}
}
2014-05-09 17:56:16 +00:00
return OpenSuccess ;
2005-02-18 18:24:45 +00:00
}
2006-08-08 15:31:13 +00:00
2014-05-11 14:36:28 +00:00
bool DocumentPrivate : : updateMetadataXmlNameAndDocSize ( )
{
// m_docFileName is always local so we can use QFileInfo on it
QFileInfo fileReadTest ( m_docFileName ) ;
if ( ! fileReadTest . isFile ( ) & & ! fileReadTest . isReadable ( ) ) {
return false ;
2022-03-08 10:10:43 +00:00
}
2014-05-11 14:36:28 +00:00
m_docSize = fileReadTest . size ( ) ;
// determine the related "xml document-info" filename
if ( m_url . isLocalFile ( ) ) {
2014-09-08 12:58:55 +00:00
const QString filePath = docDataFileName ( m_url , m_docSize ) ;
2017-09-10 09:51:56 +00:00
qCDebug ( OkularCoreDebug ) < < " Metadata file is now: " < < filePath ;
2014-09-08 12:58:55 +00:00
m_xmlFileName = filePath ;
2014-05-11 14:36:28 +00:00
} else {
2017-09-10 09:51:56 +00:00
qCDebug ( OkularCoreDebug ) < < " Metadata file: disabled " ;
2014-05-11 14:36:28 +00:00
m_xmlFileName = QString ( ) ;
}
return true ;
}
2007-08-28 23:17:00 +00:00
KXMLGUIClient * Document : : guiClient ( )
2006-08-08 15:31:13 +00:00
{
2007-01-02 19:05:49 +00:00
if ( d - > m_generator ) {
Okular : : GuiInterface * iface = qobject_cast < Okular : : GuiInterface * > ( d - > m_generator ) ;
if ( iface ) {
2007-08-28 23:17:00 +00:00
return iface - > guiClient ( ) ;
2022-03-08 10:10:43 +00:00
}
2006-09-14 18:42:28 +00:00
}
2017-09-05 21:27:18 +00:00
return nullptr ;
2006-08-08 15:31:13 +00:00
}
2006-11-01 15:17:22 +00:00
2007-01-02 19:05:49 +00:00
void Document : : closeDocument ( )
2006-11-01 15:17:22 +00:00
{
2007-10-03 15:03:49 +00:00
// check if there's anything to close...
if ( ! d - > m_generator ) {
return ;
2022-03-08 10:10:43 +00:00
}
2007-10-03 15:03:49 +00:00
2022-03-18 21:35:45 +00:00
Q_EMIT aboutToClose ( ) ;
2018-10-21 20:25:47 +00:00
2013-07-30 15:00:32 +00:00
delete d - > m_pageController ;
2017-09-05 21:27:18 +00:00
d - > m_pageController = nullptr ;
2013-07-30 15:00:32 +00:00
2008-04-13 22:31:59 +00:00
delete d - > m_scripter ;
2017-09-05 21:27:18 +00:00
d - > m_scripter = nullptr ;
2008-04-13 22:31:59 +00:00
2009-11-11 19:36:58 +00:00
// remove requests left in queue
2018-02-14 16:48:10 +00:00
d - > clearAndWaitForRequests ( ) ;
2008-02-01 00:43:45 +00:00
2007-07-08 21:22:37 +00:00
if ( d - > m_fontThread ) {
2017-09-05 21:27:18 +00:00
disconnect ( d - > m_fontThread , nullptr , this , nullptr ) ;
2007-07-08 21:22:37 +00:00
d - > m_fontThread - > stopExtraction ( ) ;
d - > m_fontThread - > wait ( ) ;
2017-09-05 21:27:18 +00:00
d - > m_fontThread = nullptr ;
2007-07-08 21:22:37 +00:00
}
2008-04-14 21:58:14 +00:00
// stop any audio playback
AudioPlayer : : instance ( ) - > stopPlaybacks ( ) ;
2007-01-02 19:05:49 +00:00
// close the current document and save document info if a document is still opened
if ( d - > m_generator & & d - > m_pagesVector . size ( ) > 0 ) {
d - > saveDocumentInfo ( ) ;
2021-12-29 08:55:56 +00:00
// free the content of the opaque backend actions (if any)
// this is a bit awkward since backends can store "random stuff" in the
// BackendOpaqueAction nativeId qvariant so we need to tell them to free it
// ideally we would just do that in the BackendOpaqueAction destructor
// but that's too late in the cleanup process, i.e. the generator has already closed its document
// and the document generator is nullptr
for ( Page * p : qAsConst ( d - > m_pagesVector ) ) {
2022-03-31 14:16:40 +00:00
const QList < ObjectRect * > & oRects = p - > objectRects ( ) ;
2021-12-29 08:55:56 +00:00
for ( ObjectRect * oRect : oRects ) {
if ( oRect - > objectType ( ) = = ObjectRect : : Action ) {
const Action * a = static_cast < const Action * > ( oRect - > object ( ) ) ;
const BackendOpaqueAction * backendAction = dynamic_cast < const BackendOpaqueAction * > ( a ) ;
if ( backendAction ) {
d - > m_generator - > freeOpaqueActionContents ( * backendAction ) ;
}
}
}
2022-03-21 23:59:42 +00:00
const QList < FormField * > forms = p - > formFields ( ) ;
2021-12-29 08:55:56 +00:00
for ( const FormField * form : forms ) {
const QList < Action * > additionalActions = form - > additionalActions ( ) ;
for ( const Action * a : additionalActions ) {
const BackendOpaqueAction * backendAction = dynamic_cast < const BackendOpaqueAction * > ( a ) ;
if ( backendAction ) {
d - > m_generator - > freeOpaqueActionContents ( * backendAction ) ;
}
}
}
}
2007-07-04 09:28:31 +00:00
d - > m_generator - > closeDocument ( ) ;
2007-01-02 19:05:49 +00:00
}
2006-11-01 15:17:22 +00:00
2014-10-08 22:53:54 +00:00
if ( d - > m_synctex_scanner ) {
synctex_scanner_free ( d - > m_synctex_scanner ) ;
2017-09-05 21:27:18 +00:00
d - > m_synctex_scanner = nullptr ;
2014-10-08 22:17:53 +00:00
}
2007-01-02 19:05:49 +00:00
// stop timers
if ( d - > m_memCheckTimer ) {
d - > m_memCheckTimer - > stop ( ) ;
2022-03-08 10:10:43 +00:00
}
2007-01-02 19:05:49 +00:00
if ( d - > m_saveBookmarksTimer ) {
d - > m_saveBookmarksTimer - > stop ( ) ;
2022-03-08 10:10:43 +00:00
}
2007-01-02 19:05:49 +00:00
if ( d - > m_generator ) {
2007-01-28 15:46:10 +00:00
// disconnect the generator from this document ...
2017-09-05 21:27:18 +00:00
d - > m_generator - > d_func ( ) - > m_document = nullptr ;
2007-01-28 15:46:10 +00:00
// .. and this document from the generator signals
2017-09-05 21:27:18 +00:00
disconnect ( d - > m_generator , nullptr , this , nullptr ) ;
2007-03-11 22:35:14 +00:00
QHash < QString , GeneratorInfo > : : const_iterator genIt = d - > m_loadedGenerators . constFind ( d - > m_generatorName ) ;
Q_ASSERT ( genIt ! = d - > m_loadedGenerators . constEnd ( ) ) ;
2006-11-01 15:17:22 +00:00
}
2017-09-05 21:27:18 +00:00
d - > m_generator = nullptr ;
2007-03-11 22:35:14 +00:00
d - > m_generatorName = QString ( ) ;
2015-01-29 19:55:57 +00:00
d - > m_url = QUrl ( ) ;
2017-09-05 21:27:18 +00:00
d - > m_walletGenerator = nullptr ;
2007-01-12 22:49:14 +00:00
d - > m_docFileName = QString ( ) ;
d - > m_xmlFileName = QString ( ) ;
delete d - > m_tempFile ;
2017-09-05 21:27:18 +00:00
d - > m_tempFile = nullptr ;
2008-11-15 14:15:31 +00:00
delete d - > m_archiveData ;
2017-09-05 21:27:18 +00:00
d - > m_archiveData = nullptr ;
2007-07-10 23:49:37 +00:00
d - > m_docSize = - 1 ;
2007-03-10 20:51:50 +00:00
d - > m_exportCached = false ;
d - > m_exportFormats . clear ( ) ;
d - > m_exportToText = ExportFormat ( ) ;
2007-07-07 20:35:01 +00:00
d - > m_fontsCached = false ;
d - > m_fontsCache . clear ( ) ;
2007-05-24 22:52:29 +00:00
d - > m_rotation = Rotation0 ;
2006-09-26 22:22:01 +00:00
2007-01-02 19:05:49 +00:00
// send an empty list to observers (to free their data)
2014-05-11 15:38:30 +00:00
foreachObserver ( notifySetup ( QVector < Page * > ( ) , DocumentObserver : : DocumentChanged | DocumentObserver : : UrlChanged ) ) ;
2006-09-26 22:22:01 +00:00
2007-01-02 19:05:49 +00:00
// delete pages and clear 'd->m_pagesVector' container
2008-11-11 18:48:40 +00:00
QVector < Page * > : : const_iterator pIt = d - > m_pagesVector . constBegin ( ) ;
QVector < Page * > : : const_iterator pEnd = d - > m_pagesVector . constEnd ( ) ;
2007-01-02 19:05:49 +00:00
for ( ; pIt ! = pEnd ; + + pIt ) {
delete * pIt ;
2022-03-08 10:10:43 +00:00
}
2007-01-02 19:05:49 +00:00
d - > m_pagesVector . clear ( ) ;
2006-09-26 22:22:01 +00:00
2007-01-02 19:05:49 +00:00
// clear 'memory allocation' descriptors
2012-07-01 23:05:02 +00:00
qDeleteAll ( d - > m_allocatedPixmaps ) ;
d - > m_allocatedPixmaps . clear ( ) ;
2006-09-26 22:22:01 +00:00
2007-01-02 19:05:49 +00:00
// clear 'running searches' descriptors
2008-11-11 18:48:40 +00:00
QMap < int , RunningSearch * > : : const_iterator rIt = d - > m_searches . constBegin ( ) ;
QMap < int , RunningSearch * > : : const_iterator rEnd = d - > m_searches . constEnd ( ) ;
2007-01-02 19:05:49 +00:00
for ( ; rIt ! = rEnd ; + + rIt ) {
delete * rIt ;
2022-03-08 10:10:43 +00:00
}
2007-01-02 19:05:49 +00:00
d - > m_searches . clear ( ) ;
2004-10-09 08:10:56 +00:00
2007-01-02 19:05:49 +00:00
// clear the visible areas and notify the observers
2008-11-11 18:48:40 +00:00
QVector < VisiblePageRect * > : : const_iterator vIt = d - > m_pageRects . constBegin ( ) ;
QVector < VisiblePageRect * > : : const_iterator vEnd = d - > m_pageRects . constEnd ( ) ;
2007-01-02 19:05:49 +00:00
for ( ; vIt ! = vEnd ; + + vIt ) {
delete * vIt ;
2022-03-08 10:10:43 +00:00
}
2007-01-02 19:05:49 +00:00
d - > m_pageRects . clear ( ) ;
foreachObserver ( notifyVisibleRectsChanged ( ) ) ;
2005-01-09 23:37:07 +00:00
2007-01-02 19:05:49 +00:00
// reset internal variables
d - > m_viewportHistory . clear ( ) ;
2022-03-31 15:05:15 +00:00
d - > m_viewportHistory . emplace_back ( DocumentViewport ( ) ) ;
2007-01-02 19:05:49 +00:00
d - > m_viewportIterator = d - > m_viewportHistory . begin ( ) ;
d - > m_allocatedPixmapsTotalMemory = 0 ;
2009-02-08 01:32:49 +00:00
d - > m_allocatedTextPagesFifo . clear ( ) ;
2007-01-05 23:12:06 +00:00
d - > m_pageSize = PageSize ( ) ;
d - > m_pageSizes . clear ( ) ;
2013-04-05 22:22:48 +00:00
2014-05-11 09:17:49 +00:00
d - > m_documentInfo = DocumentInfo ( ) ;
d - > m_documentInfoAskedKeys . clear ( ) ;
2009-11-16 00:46:33 +00:00
2023-06-21 15:50:58 +00:00
AudioPlayer : : instance ( ) - > resetDocument ( ) ;
2013-04-13 20:54:47 +00:00
d - > m_undoStack - > clear ( ) ;
2014-09-09 14:08:32 +00:00
d - > m_docdataMigrationNeeded = false ;
2018-05-30 08:55:17 +00:00
# if HAVE_MALLOC_TRIM
// trim unused memory, glibc should do this but it seems it does not
// this can greatly decrease the [perceived] memory consumption of okular
// see: https://sourceware.org/bugzilla/show_bug.cgi?id=14827
malloc_trim ( 0 ) ;
# endif
2005-01-09 23:37:07 +00:00
}
2007-01-02 19:05:49 +00:00
void Document : : addObserver ( DocumentObserver * pObserver )
2005-01-09 23:37:07 +00:00
{
2013-02-24 21:58:53 +00:00
Q_ASSERT ( ! d - > m_observers . contains ( pObserver ) ) ;
d - > m_observers < < pObserver ;
2005-01-09 23:37:07 +00:00
2007-01-02 19:05:49 +00:00
// if the observer is added while a document is already opened, tell it
if ( ! d - > m_pagesVector . isEmpty ( ) ) {
2014-05-11 15:38:30 +00:00
pObserver - > notifySetup ( d - > m_pagesVector , DocumentObserver : : DocumentChanged | DocumentObserver : : UrlChanged ) ;
2007-01-02 19:05:49 +00:00
pObserver - > notifyViewportChanged ( false /*disables smoothMove*/ ) ;
2005-01-27 17:31:07 +00:00
}
2007-01-02 19:05:49 +00:00
}
2005-01-27 17:31:07 +00:00
2007-01-02 19:05:49 +00:00
void Document : : removeObserver ( DocumentObserver * pObserver )
{
2018-04-06 09:18:05 +00:00
// remove observer from the set. it won't receive notifications anymore
2013-02-24 21:58:53 +00:00
if ( d - > m_observers . contains ( pObserver ) ) {
2007-01-02 19:05:49 +00:00
// free observer's pixmap data
2008-11-11 18:48:40 +00:00
QVector < Page * > : : const_iterator it = d - > m_pagesVector . constBegin ( ) , end = d - > m_pagesVector . constEnd ( ) ;
2007-01-02 19:05:49 +00:00
for ( ; it ! = end ; + + it ) {
2013-02-24 21:58:53 +00:00
( * it ) - > deletePixmap ( pObserver ) ;
2022-03-08 10:10:43 +00:00
}
2007-01-02 19:05:49 +00:00
// [MEM] free observer's allocation descriptors
2022-03-31 15:05:15 +00:00
std : : list < AllocatedPixmap * > : : iterator aIt = d - > m_allocatedPixmaps . begin ( ) ;
std : : list < AllocatedPixmap * > : : iterator aEnd = d - > m_allocatedPixmaps . end ( ) ;
2005-01-26 10:42:07 +00:00
while ( aIt ! = aEnd ) {
2007-01-02 19:05:49 +00:00
AllocatedPixmap * p = * aIt ;
2013-02-24 21:58:53 +00:00
if ( p - > observer = = pObserver ) {
2012-07-01 23:05:02 +00:00
aIt = d - > m_allocatedPixmaps . erase ( aIt ) ;
2007-01-02 19:05:49 +00:00
delete p ;
} else {
+ + aIt ;
2022-03-08 10:10:43 +00:00
}
2005-01-20 17:33:05 +00:00
}
2007-01-02 19:05:49 +00:00
2018-04-06 09:18:05 +00:00
for ( PixmapRequest * executingRequest : qAsConst ( d - > m_executingPixmapRequests ) ) {
if ( executingRequest - > observer ( ) = = pObserver ) {
d - > cancelRenderingBecauseOf ( executingRequest , nullptr ) ;
}
}
// remove observer entry from the set
2013-02-24 21:58:53 +00:00
d - > m_observers . remove ( pObserver ) ;
2005-01-26 10:42:07 +00:00
}
2004-09-08 12:41:14 +00:00
}
2007-01-02 19:05:49 +00:00
void Document : : reparseConfig ( )
2005-01-27 17:31:07 +00:00
{
2007-01-02 19:05:49 +00:00
// reparse generator config and if something changed clear Pages
bool configchanged = false ;
if ( d - > m_generator ) {
Okular : : ConfigInterface * iface = qobject_cast < Okular : : ConfigInterface * > ( d - > m_generator ) ;
if ( iface ) {
configchanged = iface - > reparseConfig ( ) ;
2022-03-08 10:10:43 +00:00
}
2005-01-27 17:31:07 +00:00
}
2007-01-02 19:05:49 +00:00
if ( configchanged ) {
// invalidate pixmaps
2008-11-11 18:48:40 +00:00
QVector < Page * > : : const_iterator it = d - > m_pagesVector . constBegin ( ) , end = d - > m_pagesVector . constEnd ( ) ;
2007-01-02 19:05:49 +00:00
for ( ; it ! = end ; + + it ) {
( * it ) - > deletePixmaps ( ) ;
}
// [MEM] remove allocation descriptors
2012-07-01 23:05:02 +00:00
qDeleteAll ( d - > m_allocatedPixmaps ) ;
d - > m_allocatedPixmaps . clear ( ) ;
2007-01-02 19:05:49 +00:00
d - > m_allocatedPixmapsTotalMemory = 0 ;
// send reload signals to observers
foreachObserver ( notifyContentsCleared ( DocumentObserver : : Pixmap ) ) ;
2005-01-27 17:31:07 +00:00
}
2007-01-02 19:05:49 +00:00
// free memory if in 'low' profile
2022-03-31 15:05:15 +00:00
if ( SettingsCore : : memoryLevel ( ) = = SettingsCore : : EnumMemoryLevel : : Low & & ! d - > m_allocatedPixmaps . empty ( ) & & ! d - > m_pagesVector . isEmpty ( ) ) {
2007-01-02 19:05:49 +00:00
d - > cleanupPixmapMemory ( ) ;
2022-03-08 10:10:43 +00:00
}
2005-01-27 17:31:07 +00:00
}
2007-01-02 19:05:49 +00:00
bool Document : : isOpened ( ) const
2006-09-15 21:08:48 +00:00
{
2007-01-02 19:05:49 +00:00
return d - > m_generator ;
2006-09-15 21:08:48 +00:00
}
2005-02-01 18:26:56 +00:00
2007-01-02 19:05:49 +00:00
bool Document : : canConfigurePrinter ( ) const
2004-09-16 21:04:49 +00:00
{
2007-01-02 19:05:49 +00:00
if ( d - > m_generator ) {
Okular : : PrintInterface * iface = qobject_cast < Okular : : PrintInterface * > ( d - > m_generator ) ;
return iface ? true : false ;
} else {
2019-12-18 09:13:43 +00:00
return false ;
2022-03-08 10:10:43 +00:00
}
2007-01-02 19:05:49 +00:00
}
2020-11-26 14:02:28 +00:00
bool Document : : sign ( const NewSignatureData & data , const QString & newPath )
2019-08-16 10:11:02 +00:00
{
if ( d - > m_generator - > canSign ( ) ) {
2020-11-26 14:02:28 +00:00
return d - > m_generator - > sign ( data , newPath ) ;
} else {
return false ;
2019-09-30 23:34:12 +00:00
}
2019-08-16 10:11:02 +00:00
}
2020-11-26 12:54:57 +00:00
Okular : : CertificateStore * Document : : certificateStore ( ) const
2020-01-17 00:42:22 +00:00
{
2020-11-26 12:54:57 +00:00
return d - > m_generator ? d - > m_generator - > certificateStore ( ) : nullptr ;
2020-01-17 00:42:22 +00:00
}
2022-02-18 13:33:28 +00:00
void Document : : setEditorCommandOverride ( const QString & editCmd )
{
d - > editorCommandOverride = editCmd ;
}
QString Document : : editorCommandOverride ( ) const
{
return d - > editorCommandOverride ;
}
2014-05-11 09:17:49 +00:00
DocumentInfo Document : : documentInfo ( ) const
2007-01-02 19:05:49 +00:00
{
2014-05-11 09:17:49 +00:00
QSet < DocumentInfo : : Key > keys ;
for ( Okular : : DocumentInfo : : Key ks = Okular : : DocumentInfo : : Title ; ks < Okular : : DocumentInfo : : Invalid ; ks = Okular : : DocumentInfo : : Key ( ks + 1 ) ) {
keys < < ks ;
}
2009-11-16 00:46:33 +00:00
2014-05-11 09:17:49 +00:00
return documentInfo ( keys ) ;
}
DocumentInfo Document : : documentInfo ( const QSet < DocumentInfo : : Key > & keys ) const
2007-01-02 19:05:49 +00:00
{
2014-05-11 09:17:49 +00:00
DocumentInfo result = d - > m_documentInfo ;
const QSet < DocumentInfo : : Key > missingKeys = keys - d - > m_documentInfoAskedKeys ;
2009-11-16 00:46:33 +00:00
2014-05-11 09:17:49 +00:00
if ( d - > m_generator & & ! missingKeys . isEmpty ( ) ) {
DocumentInfo info = d - > m_generator - > generateDocumentInfo ( missingKeys ) ;
2007-07-10 23:49:37 +00:00
2014-05-11 09:17:49 +00:00
if ( missingKeys . contains ( DocumentInfo : : FilePath ) ) {
2015-01-29 19:55:57 +00:00
info . set ( DocumentInfo : : FilePath , currentDocument ( ) . toDisplayString ( ) ) ;
2014-05-11 09:17:49 +00:00
}
2007-07-10 23:49:37 +00:00
2014-05-11 09:17:49 +00:00
if ( d - > m_docSize ! = - 1 & & missingKeys . contains ( DocumentInfo : : DocumentSize ) ) {
2014-10-01 05:27:09 +00:00
const QString sizeString = KFormat ( ) . formatByteSize ( d - > m_docSize ) ;
2014-05-11 09:17:49 +00:00
info . set ( DocumentInfo : : DocumentSize , sizeString ) ;
2007-07-10 23:49:37 +00:00
}
2014-05-11 09:17:49 +00:00
if ( missingKeys . contains ( DocumentInfo : : PagesSize ) ) {
const QString pagesSize = d - > pagesSizeString ( ) ;
if ( ! pagesSize . isEmpty ( ) ) {
info . set ( DocumentInfo : : PagesSize , pagesSize ) ;
}
2009-11-16 00:46:33 +00:00
}
2014-05-11 09:17:49 +00:00
if ( missingKeys . contains ( DocumentInfo : : Pages ) & & info . get ( DocumentInfo : : Pages ) . isEmpty ( ) ) {
info . set ( DocumentInfo : : Pages , QString : : number ( this - > pages ( ) ) ) ;
2007-01-02 19:05:49 +00:00
}
2009-11-16 00:46:33 +00:00
2014-05-11 09:17:49 +00:00
d - > m_documentInfo . d - > values . unite ( info . d - > values ) ;
d - > m_documentInfo . d - > titles . unite ( info . d - > titles ) ;
result . d - > values . unite ( info . d - > values ) ;
result . d - > titles . unite ( info . d - > titles ) ;
2006-07-11 19:57:17 +00:00
}
2014-05-11 09:17:49 +00:00
d - > m_documentInfoAskedKeys + = keys ;
return result ;
2007-01-02 19:05:49 +00:00
}
2005-02-01 18:26:56 +00:00
2007-01-02 19:05:49 +00:00
const DocumentSynopsis * Document : : documentSynopsis ( ) const
{
2017-09-05 21:27:18 +00:00
return d - > m_generator ? d - > m_generator - > generateDocumentSynopsis ( ) : nullptr ;
2007-01-02 19:05:49 +00:00
}
2005-02-01 18:26:56 +00:00
2007-07-07 20:35:01 +00:00
void Document : : startFontReading ( )
2007-01-02 19:05:49 +00:00
{
2007-07-07 20:35:01 +00:00
if ( ! d - > m_generator | | ! d - > m_generator - > hasFeature ( Generator : : FontInfo ) | | d - > m_fontThread ) {
return ;
2022-03-08 10:10:43 +00:00
}
2007-07-07 20:35:01 +00:00
if ( d - > m_fontsCached ) {
// in case we have cached fonts, simulate a reading
// this way the API is the same, and users no need to care about the
// internal caching
for ( int i = 0 ; i < d - > m_fontsCache . count ( ) ; + + i ) {
2022-03-18 21:35:45 +00:00
Q_EMIT gotFont ( d - > m_fontsCache . at ( i ) ) ;
Q_EMIT fontReadingProgress ( i / pages ( ) ) ;
2007-07-07 20:35:01 +00:00
}
2022-03-18 21:35:45 +00:00
Q_EMIT fontReadingEnded ( ) ;
2007-07-07 20:35:01 +00:00
return ;
}
d - > m_fontThread = new FontExtractionThread ( d - > m_generator , pages ( ) ) ;
2019-12-19 22:18:31 +00:00
connect ( d - > m_fontThread , & FontExtractionThread : : gotFont , this , [ this ] ( const Okular : : FontInfo & f ) { d - > fontReadingGotFont ( f ) ; } ) ;
connect ( d - > m_fontThread . data ( ) , & FontExtractionThread : : progress , this , [ this ] ( int p ) { d - > slotFontReadingProgress ( p ) ; } ) ;
2007-07-07 20:35:01 +00:00
d - > m_fontThread - > startExtraction ( /*d->m_generator->hasFeature( Generator::Threaded )*/ true ) ;
}
2007-07-08 21:22:37 +00:00
void Document : : stopFontReading ( )
{
if ( ! d - > m_fontThread ) {
return ;
2022-03-08 10:10:43 +00:00
}
2007-07-08 21:22:37 +00:00
2017-09-05 21:27:18 +00:00
disconnect ( d - > m_fontThread , nullptr , this , nullptr ) ;
2007-07-08 21:22:37 +00:00
d - > m_fontThread - > stopExtraction ( ) ;
2017-09-05 21:27:18 +00:00
d - > m_fontThread = nullptr ;
2007-07-08 21:22:37 +00:00
d - > m_fontsCache . clear ( ) ;
}
2007-07-07 20:35:01 +00:00
bool Document : : canProvideFontInformation ( ) const
{
return d - > m_generator ? d - > m_generator - > hasFeature ( Generator : : FontInfo ) : false ;
2007-01-02 19:05:49 +00:00
}
2005-02-02 18:18:26 +00:00
2019-08-25 00:04:19 +00:00
bool Document : : canSign ( ) const
{
return d - > m_generator ? d - > m_generator - > canSign ( ) : false ;
}
2007-01-02 19:05:49 +00:00
const QList < EmbeddedFile * > * Document : : embeddedFiles ( ) const
{
2017-09-05 21:27:18 +00:00
return d - > m_generator ? d - > m_generator - > embeddedFiles ( ) : nullptr ;
2007-01-02 19:05:49 +00:00
}
2005-02-01 18:26:56 +00:00
2007-01-02 19:05:49 +00:00
const Page * Document : : page ( int n ) const
{
2018-07-09 22:49:08 +00:00
return ( n > = 0 & & n < d - > m_pagesVector . count ( ) ) ? d - > m_pagesVector . at ( n ) : nullptr ;
2007-01-02 19:05:49 +00:00
}
2005-02-01 18:26:56 +00:00
2007-01-02 19:05:49 +00:00
const DocumentViewport & Document : : viewport ( ) const
{
return ( * d - > m_viewportIterator ) ;
}
2005-02-02 18:18:26 +00:00
2007-01-02 19:05:49 +00:00
const QVector < VisiblePageRect * > & Document : : visiblePageRects ( ) const
{
return d - > m_pageRects ;
}
2004-09-08 12:41:14 +00:00
2013-02-24 21:58:53 +00:00
void Document : : setVisiblePageRects ( const QVector < VisiblePageRect * > & visiblePageRects , DocumentObserver * excludeObserver )
2007-01-02 19:05:49 +00:00
{
2008-11-11 18:48:40 +00:00
QVector < VisiblePageRect * > : : const_iterator vIt = d - > m_pageRects . constBegin ( ) ;
QVector < VisiblePageRect * > : : const_iterator vEnd = d - > m_pageRects . constEnd ( ) ;
2007-01-02 19:05:49 +00:00
for ( ; vIt ! = vEnd ; + + vIt ) {
delete * vIt ;
2022-03-08 10:10:43 +00:00
}
2007-01-02 19:05:49 +00:00
d - > m_pageRects = visiblePageRects ;
// notify change to all other (different from id) observers
2022-03-18 21:57:13 +00:00
for ( DocumentObserver * o : qAsConst ( d - > m_observers ) ) {
2013-02-24 21:58:53 +00:00
if ( o ! = excludeObserver ) {
o - > notifyVisibleRectsChanged ( ) ;
2022-03-08 10:10:43 +00:00
}
2022-03-18 21:57:13 +00:00
}
2007-01-02 19:05:49 +00:00
}
2005-02-01 18:26:56 +00:00
2007-01-02 19:05:49 +00:00
uint Document : : currentPage ( ) const
{
return ( * d - > m_viewportIterator ) . pageNumber ;
}
2005-02-01 18:26:56 +00:00
2007-01-02 19:05:49 +00:00
uint Document : : pages ( ) const
{
return d - > m_pagesVector . size ( ) ;
}
2005-02-02 18:18:26 +00:00
2015-01-29 19:55:57 +00:00
QUrl Document : : currentDocument ( ) const
2007-01-02 19:05:49 +00:00
{
return d - > m_url ;
}
2005-02-02 18:18:26 +00:00
2007-03-10 23:59:11 +00:00
bool Document : : isAllowed ( Permission action ) const
2007-01-02 19:05:49 +00:00
{
2014-09-09 14:08:32 +00:00
if ( action = = Okular : : AllowNotes & & ( d - > m_docdataMigrationNeeded | | ! d - > m_annotationEditingEnabled ) ) {
return false ;
2022-03-08 10:10:43 +00:00
}
2014-09-09 14:08:32 +00:00
if ( action = = Okular : : AllowFillForms & & d - > m_docdataMigrationNeeded ) {
2012-05-23 21:43:44 +00:00
return false ;
2022-03-08 10:10:43 +00:00
}
2012-05-23 21:43:44 +00:00
2007-01-29 14:28:58 +00:00
# if !OKULAR_FORCE_DRM
2015-10-29 12:37:11 +00:00
if ( KAuthorized : : authorize ( QStringLiteral ( " skip_drm " ) ) & & ! SettingsCore : : obeyDRM ( ) ) {
2007-01-29 14:28:58 +00:00
return true ;
2022-03-08 10:10:43 +00:00
}
2007-01-29 14:28:58 +00:00
# endif
2007-03-10 23:59:11 +00:00
return d - > m_generator ? d - > m_generator - > isAllowed ( action ) : false ;
2007-01-02 19:05:49 +00:00
}
2005-02-01 18:26:56 +00:00
2007-01-02 19:05:49 +00:00
bool Document : : supportsSearching ( ) const
{
2007-01-17 11:58:20 +00:00
return d - > m_generator ? d - > m_generator - > hasFeature ( Generator : : TextExtraction ) : false ;
2007-01-02 19:05:49 +00:00
}
2004-09-13 08:51:36 +00:00
2007-01-05 23:12:06 +00:00
bool Document : : supportsPageSizes ( ) const
2007-01-02 19:05:49 +00:00
{
2007-01-17 18:02:53 +00:00
return d - > m_generator ? d - > m_generator - > hasFeature ( Generator : : PageSizes ) : false ;
2007-01-02 19:05:49 +00:00
}
2005-02-02 18:18:26 +00:00
2012-11-15 22:18:59 +00:00
bool Document : : supportsTiles ( ) const
{
return d - > m_generator ? d - > m_generator - > hasFeature ( Generator : : TiledRendering ) : false ;
}
2007-01-05 23:12:06 +00:00
PageSize : : List Document : : pageSizes ( ) const
2007-01-02 19:05:49 +00:00
{
2007-01-05 23:12:06 +00:00
if ( d - > m_generator ) {
if ( d - > m_pageSizes . isEmpty ( ) ) {
d - > m_pageSizes = d - > m_generator - > pageSizes ( ) ;
2022-03-08 10:10:43 +00:00
}
2007-01-05 23:12:06 +00:00
return d - > m_pageSizes ;
}
return PageSize : : List ( ) ;
2007-01-02 19:05:49 +00:00
}
2005-02-01 18:26:56 +00:00
2007-01-02 19:05:49 +00:00
bool Document : : canExportToText ( ) const
{
if ( ! d - > m_generator ) {
return false ;
2022-03-08 10:10:43 +00:00
}
2005-02-01 18:26:56 +00:00
2007-03-10 20:51:50 +00:00
d - > cacheExportFormats ( ) ;
return ! d - > m_exportToText . isNull ( ) ;
2007-01-02 19:05:49 +00:00
}
2005-02-20 16:04:40 +00:00
2007-01-02 19:05:49 +00:00
bool Document : : exportToText ( const QString & fileName ) const
{
if ( ! d - > m_generator ) {
return false ;
2022-03-08 10:10:43 +00:00
}
2005-02-20 16:04:40 +00:00
2007-03-10 20:51:50 +00:00
d - > cacheExportFormats ( ) ;
if ( d - > m_exportToText . isNull ( ) ) {
return false ;
2022-03-08 10:10:43 +00:00
}
2005-02-01 18:26:56 +00:00
2007-03-10 20:51:50 +00:00
return d - > m_generator - > exportTo ( fileName , d - > m_exportToText ) ;
2004-09-08 12:41:14 +00:00
}
2007-01-02 19:05:49 +00:00
ExportFormat : : List Document : : exportFormats ( ) const
2004-11-09 17:20:19 +00:00
{
2007-03-10 20:51:50 +00:00
if ( ! d - > m_generator ) {
return ExportFormat : : List ( ) ;
2022-03-08 10:10:43 +00:00
}
2007-03-10 20:51:50 +00:00
d - > cacheExportFormats ( ) ;
return d - > m_exportFormats ;
2004-11-09 17:20:19 +00:00
}
2007-01-02 19:05:49 +00:00
bool Document : : exportTo ( const QString & fileName , const ExportFormat & format ) const
2005-02-01 18:26:56 +00:00
{
2007-01-02 19:05:49 +00:00
return d - > m_generator ? d - > m_generator - > exportTo ( fileName , format ) : false ;
2005-02-01 18:26:56 +00:00
}
2007-01-02 19:05:49 +00:00
bool Document : : historyAtBegin ( ) const
2006-07-11 19:57:17 +00:00
{
2007-01-02 19:05:49 +00:00
return d - > m_viewportIterator = = d - > m_viewportHistory . begin ( ) ;
2006-07-11 19:57:17 +00:00
}
2005-02-01 18:26:56 +00:00
2007-01-02 19:05:49 +00:00
bool Document : : historyAtEnd ( ) const
2004-11-09 17:20:19 +00:00
{
2007-01-02 19:05:49 +00:00
return d - > m_viewportIterator = = - - ( d - > m_viewportHistory . end ( ) ) ;
2004-11-09 17:20:19 +00:00
}
2007-01-02 22:37:55 +00:00
QVariant Document : : metaData ( const QString & key , const QVariant & option ) const
2006-12-27 16:04:49 +00:00
{
2014-10-08 22:53:54 +00:00
// if option starts with "src:" assume that we are handling a
// source reference
2015-10-29 12:37:11 +00:00
if ( key = = QLatin1String ( " NamedViewport " ) & & option . toString ( ) . startsWith ( QLatin1String ( " src: " ) , Qt : : CaseInsensitive ) & & d - > m_synctex_scanner ) {
2014-10-08 22:53:54 +00:00
const QString reference = option . toString ( ) ;
// The reference is of form "src:1111Filename", where "1111"
// points to line number 1111 in the file "Filename".
// Extract the file name and the numeral part from the reference string.
// This will fail if Filename starts with a digit.
QString name , lineString ;
// Remove "src:". Presence of substring has been checked before this
// function is called.
name = reference . mid ( 4 ) ;
// split
int nameLength = name . length ( ) ;
int i = 0 ;
for ( i = 0 ; i < nameLength ; + + i ) {
if ( ! name [ i ] . isDigit ( ) ) {
break ;
2022-03-08 10:10:43 +00:00
}
2014-10-08 22:53:54 +00:00
}
lineString = name . left ( i ) ;
name = name . mid ( i ) ;
// Remove spaces.
name = name . trimmed ( ) ;
lineString = lineString . trimmed ( ) ;
// Convert line to integer.
bool ok ;
int line = lineString . toInt ( & ok ) ;
if ( ! ok ) {
line = - 1 ;
2022-03-08 10:10:43 +00:00
}
2014-10-08 22:53:54 +00:00
// Use column == -1 for now.
2017-08-28 22:29:46 +00:00
if ( synctex_display_query ( d - > m_synctex_scanner , QFile : : encodeName ( name ) . constData ( ) , line , - 1 , 0 ) > 0 ) {
synctex_node_p node ;
2014-10-08 22:53:54 +00:00
// For now use the first hit. Could possibly be made smarter
// in case there are multiple hits.
2017-08-28 22:29:46 +00:00
while ( ( node = synctex_scanner_next_result ( d - > m_synctex_scanner ) ) ) {
2014-10-08 22:53:54 +00:00
Okular : : DocumentViewport viewport ;
// TeX pages start at 1.
viewport . pageNumber = synctex_node_page ( node ) - 1 ;
if ( viewport . pageNumber > = 0 ) {
const QSizeF dpi = d - > m_generator - > dpi ( ) ;
// TeX small points ...
double px = ( synctex_node_visible_h ( node ) * dpi . width ( ) ) / 72.27 ;
double py = ( synctex_node_visible_v ( node ) * dpi . height ( ) ) / 72.27 ;
viewport . rePos . normalizedX = px / page ( viewport . pageNumber ) - > width ( ) ;
viewport . rePos . normalizedY = ( py + 0.5 ) / page ( viewport . pageNumber ) - > height ( ) ;
viewport . rePos . enabled = true ;
viewport . rePos . pos = Okular : : DocumentViewport : : Center ;
return viewport . toString ( ) ;
}
}
}
}
2007-01-02 19:05:49 +00:00
return d - > m_generator ? d - > m_generator - > metaData ( key , option ) : QVariant ( ) ;
2006-12-27 16:04:49 +00:00
}
2007-01-05 17:09:47 +00:00
Rotation Document : : rotation ( ) const
2006-12-27 16:04:49 +00:00
{
2007-01-02 19:05:49 +00:00
return d - > m_rotation ;
2006-12-27 16:04:49 +00:00
}
2007-01-02 19:05:49 +00:00
QSizeF Document : : allPagesSize ( ) const
2006-12-27 16:04:49 +00:00
{
2007-01-02 19:05:49 +00:00
bool allPagesSameSize = true ;
QSizeF size ;
for ( int i = 0 ; allPagesSameSize & & i < d - > m_pagesVector . count ( ) ; + + i ) {
2007-02-12 14:15:51 +00:00
const Page * p = d - > m_pagesVector . at ( i ) ;
2007-01-02 19:05:49 +00:00
if ( i = = 0 ) {
size = QSizeF ( p - > width ( ) , p - > height ( ) ) ;
} else {
allPagesSameSize = ( size = = QSizeF ( p - > width ( ) , p - > height ( ) ) ) ;
}
}
if ( allPagesSameSize ) {
return size ;
} else {
return QSizeF ( ) ;
2022-03-08 10:10:43 +00:00
}
2006-12-27 16:04:49 +00:00
}
2007-01-02 19:05:49 +00:00
QString Document : : pageSizeString ( int page ) const
2006-12-27 16:04:49 +00:00
{
2007-01-02 19:05:49 +00:00
if ( d - > m_generator ) {
if ( d - > m_generator - > pagesSizeMetric ( ) ! = Generator : : None ) {
2007-02-12 14:15:51 +00:00
const Page * p = d - > m_pagesVector . at ( page ) ;
2007-01-02 19:05:49 +00:00
return d - > localizedSize ( QSizeF ( p - > width ( ) , p - > height ( ) ) ) ;
}
}
return QString ( ) ;
2006-12-27 16:04:49 +00:00
}
2018-02-01 18:42:37 +00:00
static bool shouldCancelRenderingBecauseOf ( const PixmapRequest & executingRequest , const PixmapRequest & otherRequest )
{
// New request has higher priority -> cancel
if ( executingRequest . priority ( ) > otherRequest . priority ( ) ) {
return true ;
2022-03-08 10:10:43 +00:00
}
2018-02-01 18:42:37 +00:00
// New request has lower priority -> don't cancel
if ( executingRequest . priority ( ) < otherRequest . priority ( ) ) {
return false ;
2022-03-08 10:10:43 +00:00
}
2018-02-01 18:42:37 +00:00
// New request has same priority and is from a different observer -> don't cancel
// AFAIK this never happens since all observers have different priorities
if ( executingRequest . observer ( ) ! = otherRequest . observer ( ) ) {
return false ;
2022-03-08 10:10:43 +00:00
}
2018-02-01 18:42:37 +00:00
// Same priority and observer, different page number -> don't cancel
// may still end up cancelled later in the parent caller if none of the requests
// is of the executingRequest page and RemoveAllPrevious is specified
if ( executingRequest . pageNumber ( ) ! = otherRequest . pageNumber ( ) ) {
return false ;
2022-03-08 10:10:43 +00:00
}
2018-02-01 18:42:37 +00:00
// Same priority, observer, page, different size -> cancel
if ( executingRequest . width ( ) ! = otherRequest . width ( ) ) {
return true ;
2022-03-08 10:10:43 +00:00
}
2018-02-01 18:42:37 +00:00
// Same priority, observer, page, different size -> cancel
if ( executingRequest . height ( ) ! = otherRequest . height ( ) ) {
return true ;
2022-03-08 10:10:43 +00:00
}
2018-02-01 18:42:37 +00:00
// Same priority, observer, page, different tiling -> cancel
if ( executingRequest . isTile ( ) ! = otherRequest . isTile ( ) ) {
return true ;
2022-03-08 10:10:43 +00:00
}
2018-02-01 18:42:37 +00:00
// Same priority, observer, page, different tiling -> cancel
if ( executingRequest . isTile ( ) ) {
const NormalizedRect bothRequestsRect = executingRequest . normalizedRect ( ) | otherRequest . normalizedRect ( ) ;
if ( ! ( bothRequestsRect = = executingRequest . normalizedRect ( ) ) ) {
return true ;
2022-03-08 10:10:43 +00:00
}
2018-02-01 18:42:37 +00:00
}
return false ;
}
bool DocumentPrivate : : cancelRenderingBecauseOf ( PixmapRequest * executingRequest , PixmapRequest * newRequest )
{
// No point in aborting the rendering already finished, let it go through
if ( ! executingRequest - > d - > mResultImage . isNull ( ) ) {
return false ;
2022-03-08 10:10:43 +00:00
}
2018-02-01 18:42:37 +00:00
2018-04-02 19:04:01 +00:00
if ( newRequest & & newRequest - > asynchronous ( ) & & executingRequest - > partialUpdatesWanted ( ) ) {
2018-02-01 18:42:37 +00:00
newRequest - > setPartialUpdatesWanted ( true ) ;
}
TilesManager * tm = executingRequest - > d - > tilesManager ( ) ;
if ( tm ) {
tm - > setPixmap ( nullptr , executingRequest - > normalizedRect ( ) , true /*isPartialPixmap*/ ) ;
tm - > setRequest ( NormalizedRect ( ) , 0 , 0 ) ;
}
PagePrivate : : PixmapObject object = executingRequest - > page ( ) - > d - > m_pixmaps . take ( executingRequest - > observer ( ) ) ;
delete object . m_pixmap ;
if ( executingRequest - > d - > mShouldAbortRender ! = 0 ) {
return false ;
2022-03-08 10:10:43 +00:00
}
2018-02-01 18:42:37 +00:00
executingRequest - > d - > mShouldAbortRender = 1 ;
if ( m_generator - > d_ptr - > mTextPageGenerationThread & & m_generator - > d_ptr - > mTextPageGenerationThread - > page ( ) = = executingRequest - > page ( ) ) {
m_generator - > d_ptr - > mTextPageGenerationThread - > abortExtraction ( ) ;
}
return true ;
}
2022-03-19 00:00:01 +00:00
void Document : : requestPixmaps ( const QList < PixmapRequest * > & requests )
2008-03-07 15:50:48 +00:00
{
requestPixmaps ( requests , RemoveAllPrevious ) ;
}
2022-03-19 00:00:01 +00:00
void Document : : requestPixmaps ( const QList < PixmapRequest * > & requests , PixmapRequestFlags reqOptions )
2004-09-08 12:41:14 +00:00
{
2007-05-24 22:52:29 +00:00
if ( requests . isEmpty ( ) ) {
return ;
2022-03-08 10:10:43 +00:00
}
2007-05-24 22:52:29 +00:00
2014-07-31 23:27:27 +00:00
if ( ! d - > m_pageController ) {
2007-01-02 19:05:49 +00:00
// delete requests..
2022-03-19 00:00:01 +00:00
qDeleteAll ( requests ) ;
2007-01-02 19:05:49 +00:00
// ..and return
2004-10-06 00:05:49 +00:00
return ;
2007-01-02 19:05:49 +00:00
}
2004-10-06 00:05:49 +00:00
2018-02-01 18:42:37 +00:00
QSet < DocumentObserver * > observersPixmapCleared ;
2007-01-02 19:05:49 +00:00
// 1. [CLEAN STACK] remove previous requests of requesterID
2013-02-24 21:58:53 +00:00
DocumentObserver * requesterObserver = requests . first ( ) - > observer ( ) ;
2008-03-07 15:50:48 +00:00
QSet < int > requestedPages ;
{
2022-03-19 00:00:01 +00:00
for ( PixmapRequest * request : requests ) {
Q_ASSERT ( request - > observer ( ) = = requesterObserver ) ;
requestedPages . insert ( request - > pageNumber ( ) ) ;
2018-02-01 18:42:37 +00:00
}
2008-03-07 15:50:48 +00:00
}
const bool removeAllPrevious = reqOptions & RemoveAllPrevious ;
2007-05-12 21:40:38 +00:00
d - > m_pixmapRequestsMutex . lock ( ) ;
2022-03-31 15:05:15 +00:00
std : : list < PixmapRequest * > : : iterator sIt = d - > m_pixmapRequestsStack . begin ( ) , sEnd = d - > m_pixmapRequestsStack . end ( ) ;
2007-01-02 19:05:49 +00:00
while ( sIt ! = sEnd ) {
2008-03-07 15:50:48 +00:00
if ( ( * sIt ) - > observer ( ) = = requesterObserver & & ( removeAllPrevious | | requestedPages . contains ( ( * sIt ) - > pageNumber ( ) ) ) ) {
2007-01-02 19:05:49 +00:00
// delete request and remove it from stack
delete * sIt ;
sIt = d - > m_pixmapRequestsStack . erase ( sIt ) ;
} else {
+ + sIt ;
2022-03-08 10:10:43 +00:00
}
2007-01-02 19:05:49 +00:00
}
2005-02-04 22:35:44 +00:00
2018-02-01 18:42:37 +00:00
// 1.B [PREPROCESS REQUESTS] tweak some values of the requests
for ( PixmapRequest * request : requests ) {
2007-01-02 19:05:49 +00:00
// set the 'page field' (see PixmapRequest) and check if it is valid
2014-09-11 17:36:01 +00:00
qCDebug ( OkularCoreDebug ) . nospace ( ) < < " request observer= " < < request - > observer ( ) < < " " < < request - > width ( ) < < " x " < < request - > height ( ) < < " @ " < < request - > pageNumber ( ) ;
2020-09-13 09:20:09 +00:00
if ( d - > m_pagesVector . value ( request - > pageNumber ( ) ) = = nullptr ) {
2007-01-02 19:05:49 +00:00
// skip requests referencing an invalid page (must not happen)
delete request ;
continue ;
}
2004-12-10 16:04:45 +00:00
2007-09-30 21:44:31 +00:00
request - > d - > mPage = d - > m_pagesVector . value ( request - > pageNumber ( ) ) ;
2005-06-13 17:44:33 +00:00
2012-11-12 14:55:13 +00:00
if ( request - > isTile ( ) ) {
// Change the current request rect so that only invalid tiles are
// requested. Also make sure the rect is tile-aligned.
NormalizedRect tilesRect ;
2014-02-19 22:40:43 +00:00
const QList < Tile > tiles = request - > d - > tilesManager ( ) - > tilesAt ( request - > normalizedRect ( ) , TilesManager : : TerminalTile ) ;
2012-11-12 14:55:13 +00:00
QList < Tile > : : const_iterator tIt = tiles . constBegin ( ) , tEnd = tiles . constEnd ( ) ;
while ( tIt ! = tEnd ) {
const Tile & tile = * tIt ;
if ( ! tile . isValid ( ) ) {
if ( tilesRect . isNull ( ) ) {
tilesRect = tile . rect ( ) ;
} else {
tilesRect | = tile . rect ( ) ;
2022-03-08 10:10:43 +00:00
}
2012-11-12 14:55:13 +00:00
}
tIt + + ;
}
request - > setNormalizedRect ( tilesRect ) ;
}
2007-01-02 19:05:49 +00:00
if ( ! request - > asynchronous ( ) ) {
2007-09-30 21:44:31 +00:00
request - > d - > mPriority = 0 ;
2022-03-08 10:10:43 +00:00
}
2018-02-01 18:42:37 +00:00
}
// 1.C [CANCEL REQUESTS] cancel those requests that are running and should be cancelled because of the new requests coming in
if ( d - > m_generator - > hasFeature ( Generator : : SupportsCancelling ) ) {
for ( PixmapRequest * executingRequest : qAsConst ( d - > m_executingPixmapRequests ) ) {
bool newRequestsContainExecutingRequestPage = false ;
bool requestCancelled = false ;
for ( PixmapRequest * newRequest : requests ) {
if ( newRequest - > pageNumber ( ) = = executingRequest - > pageNumber ( ) & & requesterObserver = = executingRequest - > observer ( ) ) {
newRequestsContainExecutingRequestPage = true ;
}
if ( shouldCancelRenderingBecauseOf ( * executingRequest , * newRequest ) ) {
requestCancelled = d - > cancelRenderingBecauseOf ( executingRequest , newRequest ) ;
}
}
// If we were told to remove all the previous requests and the executing request page is not part of the new requests, cancel it
if ( ! requestCancelled & & removeAllPrevious & & requesterObserver = = executingRequest - > observer ( ) & & ! newRequestsContainExecutingRequestPage ) {
requestCancelled = d - > cancelRenderingBecauseOf ( executingRequest , nullptr ) ;
}
if ( requestCancelled ) {
observersPixmapCleared < < executingRequest - > observer ( ) ;
}
}
}
2004-10-06 00:05:49 +00:00
2018-02-01 18:42:37 +00:00
// 2. [ADD TO STACK] add requests to stack
for ( PixmapRequest * request : requests ) {
2007-01-02 19:05:49 +00:00
// add request to the 'stack' at the right place
if ( ! request - > priority ( ) ) {
// add priority zero requests to the top of the stack
2022-03-31 15:05:15 +00:00
d - > m_pixmapRequestsStack . push_back ( request ) ;
2007-01-02 19:05:49 +00:00
} else {
// insert in stack sorted by priority
sIt = d - > m_pixmapRequestsStack . begin ( ) ;
sEnd = d - > m_pixmapRequestsStack . end ( ) ;
2007-01-04 14:28:54 +00:00
while ( sIt ! = sEnd & & ( * sIt ) - > priority ( ) > request - > priority ( ) ) {
2007-01-02 19:05:49 +00:00
+ + sIt ;
2022-03-08 10:10:43 +00:00
}
2007-01-02 19:05:49 +00:00
d - > m_pixmapRequestsStack . insert ( sIt , request ) ;
}
}
2007-05-12 21:40:38 +00:00
d - > m_pixmapRequestsMutex . unlock ( ) ;
2004-12-10 16:04:45 +00:00
2007-01-02 19:05:49 +00:00
// 3. [START FIRST GENERATION] if <NO>generator is ready, start a new generation,
// or else (if gen is running) it will be started when the new contents will
// come from generator (in requestDone())</NO>
2012-09-25 09:09:34 +00:00
// all handling of requests put into sendGeneratorPixmapRequest
2007-01-24 15:06:45 +00:00
// if ( generator->canRequestPixmap() )
2012-09-25 09:09:34 +00:00
d - > sendGeneratorPixmapRequest ( ) ;
2018-02-01 18:42:37 +00:00
for ( DocumentObserver * o : qAsConst ( observersPixmapCleared ) ) {
o - > notifyContentsCleared ( Okular : : DocumentObserver : : Pixmap ) ;
2022-03-08 10:10:43 +00:00
}
2007-01-02 19:05:49 +00:00
}
2004-12-10 16:04:45 +00:00
2019-12-20 15:40:59 +00:00
void Document : : requestTextPage ( uint pageNumber )
2007-01-02 19:05:49 +00:00
{
2019-12-20 15:40:59 +00:00
Page * kp = d - > m_pagesVector [ pageNumber ] ;
2007-01-02 19:05:49 +00:00
if ( ! d - > m_generator | | ! kp ) {
return ;
2022-03-08 10:10:43 +00:00
}
2004-12-10 16:04:45 +00:00
2007-01-02 19:05:49 +00:00
// Memory management for TextPages
2004-12-10 16:04:45 +00:00
2007-08-13 22:25:27 +00:00
d - > m_generator - > generateTextPage ( kp ) ;
2007-01-02 19:05:49 +00:00
}
2005-03-05 15:59:15 +00:00
2012-05-23 21:43:44 +00:00
void DocumentPrivate : : notifyAnnotationChanges ( int page )
{
2017-11-14 13:52:02 +00:00
foreachObserverD ( notifyPageChanged ( page , DocumentObserver : : Annotations ) ) ;
2012-05-23 21:43:44 +00:00
}
2017-11-14 13:52:02 +00:00
void DocumentPrivate : : notifyFormChanges ( int /*page*/ )
2014-05-11 13:06:27 +00:00
{
2018-02-21 22:57:01 +00:00
recalculateForms ( ) ;
2012-05-23 21:43:44 +00:00
}
2007-01-02 19:05:49 +00:00
void Document : : addPageAnnotation ( int page , Annotation * annotation )
{
2013-04-13 20:54:47 +00:00
// Transform annotation's base boundary rectangle into unrotated coordinates
Page * p = d - > m_pagesVector [ page ] ;
QTransform t = p - > d - > rotationMatrix ( ) ;
annotation - > d_ptr - > baseTransform ( t . inverted ( ) ) ;
2013-04-05 22:22:48 +00:00
QUndoCommand * uc = new AddAnnotationCommand ( this - > d , annotation , page ) ;
d - > m_undoStack - > push ( uc ) ;
2004-09-09 13:25:40 +00:00
}
2004-11-09 17:20:19 +00:00
2012-03-09 13:59:24 +00:00
bool Document : : canModifyPageAnnotation ( const Annotation * annotation ) const
{
if ( ! annotation | | ( annotation - > flags ( ) & Annotation : : DenyWrite ) ) {
return false ;
2022-03-08 10:10:43 +00:00
}
2012-03-09 13:59:24 +00:00
if ( ! isAllowed ( Okular : : AllowNotes ) ) {
return false ;
2022-03-08 10:10:43 +00:00
}
2012-03-09 13:59:24 +00:00
if ( ( annotation - > flags ( ) & Annotation : : External ) & & ! d - > canModifyExternalAnnotations ( ) ) {
return false ;
2022-03-08 10:10:43 +00:00
}
2012-03-09 13:59:24 +00:00
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 ;
}
}
2013-04-05 22:22:48 +00:00
void Document : : prepareToModifyAnnotationProperties ( Annotation * annotation )
2006-11-17 22:15:15 +00:00
{
2013-04-05 22:28:39 +00:00
Q_ASSERT ( d - > m_prevPropsOfAnnotBeingModified . isNull ( ) ) ;
2013-04-05 22:22:48 +00:00
if ( ! d - > m_prevPropsOfAnnotBeingModified . isNull ( ) ) {
2014-09-11 19:12:27 +00:00
qCCritical ( OkularCoreDebug ) < < " Error: Document::prepareToModifyAnnotationProperties has already been called since last call to Document::modifyPageAnnotationProperties " ;
2013-04-05 22:22:48 +00:00
return ;
}
d - > m_prevPropsOfAnnotBeingModified = annotation - > getAnnotationPropertiesDomNode ( ) ;
2012-05-13 22:50:41 +00:00
}
2013-04-05 22:22:48 +00:00
void Document : : modifyPageAnnotationProperties ( int page , Annotation * annotation )
2012-05-13 22:50:41 +00:00
{
2013-04-05 22:28:39 +00:00
Q_ASSERT ( ! d - > m_prevPropsOfAnnotBeingModified . isNull ( ) ) ;
2013-04-05 22:22:48 +00:00
if ( d - > m_prevPropsOfAnnotBeingModified . isNull ( ) ) {
2014-09-11 19:12:27 +00:00
qCCritical ( OkularCoreDebug ) < < " Error: Document::prepareToModifyAnnotationProperties must be called before Annotation is modified " ;
2013-04-05 22:22:48 +00:00
return ;
2012-05-13 22:50:41 +00:00
}
2013-04-05 22:22:48 +00:00
QDomNode prevProps = d - > m_prevPropsOfAnnotBeingModified ;
QUndoCommand * uc = new Okular : : ModifyAnnotationPropertiesCommand ( d , annotation , page , prevProps , annotation - > getAnnotationPropertiesDomNode ( ) ) ;
d - > m_undoStack - > push ( uc ) ;
d - > m_prevPropsOfAnnotBeingModified . clear ( ) ;
}
2012-05-14 13:51:58 +00:00
2013-04-05 22:22:48 +00:00
void Document : : translatePageAnnotation ( int page , Annotation * annotation , const NormalizedPoint & delta )
{
int complete = ( annotation - > flags ( ) & Okular : : Annotation : : BeingMoved ) = = 0 ;
QUndoCommand * uc = new Okular : : TranslateAnnotationCommand ( d , annotation , page , delta , complete ) ;
d - > m_undoStack - > push ( uc ) ;
}
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
void Document : : adjustPageAnnotation ( int page , Annotation * annotation , const Okular : : NormalizedPoint & delta1 , const Okular : : NormalizedPoint & delta2 )
{
const bool complete = ( annotation - > flags ( ) & Okular : : Annotation : : BeingResized ) = = 0 ;
QUndoCommand * uc = new Okular : : AdjustAnnotationCommand ( d , annotation , page , delta1 , delta2 , complete ) ;
d - > m_undoStack - > push ( uc ) ;
}
2013-04-05 22:22:48 +00:00
void Document : : editPageAnnotationContents ( int page , Annotation * annotation , const QString & newContents , int newCursorPos , int prevCursorPos , int prevAnchorPos )
{
QString prevContents = annotation - > contents ( ) ;
QUndoCommand * uc = new EditAnnotationContentsCommand ( d , annotation , page , newContents , newCursorPos , prevContents , prevCursorPos , prevAnchorPos ) ;
d - > m_undoStack - > push ( uc ) ;
2007-01-02 19:05:49 +00:00
}
2006-11-17 22:15:15 +00:00
2012-05-13 22:50:41 +00:00
bool Document : : canRemovePageAnnotation ( const Annotation * annotation ) const
{
if ( ! annotation | | ( annotation - > flags ( ) & Annotation : : DenyDelete ) ) {
return false ;
2022-03-08 10:10:43 +00:00
}
2012-05-13 22:50:41 +00:00
2012-03-09 13:59:24 +00:00
if ( ( annotation - > flags ( ) & Annotation : : External ) & & ! d - > canRemoveExternalAnnotations ( ) ) {
return false ;
2022-03-08 10:10:43 +00:00
}
2012-03-09 13:59:24 +00:00
2012-05-13 22:50:41 +00:00
switch ( annotation - > subType ( ) ) {
case Annotation : : AText :
case Annotation : : ALine :
case Annotation : : AGeom :
case Annotation : : AHighlight :
case Annotation : : AStamp :
case Annotation : : AInk :
2015-07-30 08:49:00 +00:00
case Annotation : : ACaret :
2012-05-13 22:50:41 +00:00
return true ;
default :
return false ;
}
}
2007-01-02 19:05:49 +00:00
void Document : : removePageAnnotation ( int page , Annotation * annotation )
{
2013-04-05 22:22:48 +00:00
QUndoCommand * uc = new RemoveAnnotationCommand ( this - > d , annotation , page ) ;
d - > m_undoStack - > push ( uc ) ;
2007-01-02 19:05:49 +00:00
}
2006-11-17 22:15:15 +00:00
2013-04-05 22:22:48 +00:00
void Document : : removePageAnnotations ( int page , const QList < Annotation * > & annotations )
2007-01-02 19:05:49 +00:00
{
2013-04-05 22:22:48 +00:00
d - > m_undoStack - > beginMacro ( i18nc ( " remove a collection of annotations from the page " , " remove annotations " ) ) ;
2022-03-18 21:57:13 +00:00
for ( Annotation * annotation : annotations ) {
2013-04-05 22:22:48 +00:00
QUndoCommand * uc = new RemoveAnnotationCommand ( this - > d , annotation , page ) ;
d - > m_undoStack - > push ( uc ) ;
2007-01-02 19:05:49 +00:00
}
2013-04-05 22:22:48 +00:00
d - > m_undoStack - > endMacro ( ) ;
2012-05-14 13:51:58 +00:00
}
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 ;
2022-03-08 10:10:43 +00:00
}
2012-05-14 13:51:58 +00:00
return false ;
2007-01-02 19:05:49 +00:00
}
2006-11-17 22:15:15 +00:00
2012-03-09 13:59:24 +00:00
bool DocumentPrivate : : canModifyExternalAnnotations ( ) const
{
2012-05-17 18:49:21 +00:00
Okular : : SaveInterface * iface = qobject_cast < Okular : : SaveInterface * > ( m_generator ) ;
2012-03-09 13:59:24 +00:00
2012-05-17 18:49:21 +00:00
if ( iface & & iface - > supportsOption ( Okular : : SaveInterface : : SaveChanges ) & & iface - > annotationProxy ( ) & & iface - > annotationProxy ( ) - > supports ( AnnotationProxy : : Modification ) ) {
2012-03-09 13:59:24 +00:00
return true ;
2022-03-08 10:10:43 +00:00
}
2012-05-17 18:49:21 +00:00
2012-03-09 13:59:24 +00:00
return false ;
}
bool DocumentPrivate : : canRemoveExternalAnnotations ( ) const
{
2012-05-17 18:49:21 +00:00
Okular : : SaveInterface * iface = qobject_cast < Okular : : SaveInterface * > ( m_generator ) ;
2012-03-09 13:59:24 +00:00
2012-05-17 18:49:21 +00:00
if ( iface & & iface - > supportsOption ( Okular : : SaveInterface : : SaveChanges ) & & iface - > annotationProxy ( ) & & iface - > annotationProxy ( ) - > supports ( AnnotationProxy : : Removal ) ) {
2012-03-09 13:59:24 +00:00
return true ;
2022-03-08 10:10:43 +00:00
}
2012-05-17 18:49:21 +00:00
2012-03-09 13:59:24 +00:00
return false ;
}
2007-01-02 19:05:49 +00:00
void Document : : setPageTextSelection ( int page , RegularAreaRect * rect , const QColor & color )
{
Page * kp = d - > m_pagesVector [ page ] ;
if ( ! d - > m_generator | | ! kp ) {
2006-11-17 22:15:15 +00:00
return ;
2022-03-08 10:10:43 +00:00
}
2006-11-17 22:15:15 +00:00
2007-01-02 19:05:49 +00:00
// add or remove the selection basing whether rect is null or not
if ( rect ) {
2007-05-05 18:07:34 +00:00
kp - > d - > setTextSelections ( rect , color ) ;
2007-01-02 19:05:49 +00:00
} else {
2007-09-03 23:47:12 +00:00
kp - > d - > deleteTextSelections ( ) ;
2022-03-08 10:10:43 +00:00
}
2007-01-02 19:05:49 +00:00
// notify observers about the change
foreachObserver ( notifyPageChanged ( page , DocumentObserver : : TextSelection ) ) ;
2006-11-17 22:15:15 +00:00
}
2013-04-05 22:22:48 +00:00
bool Document : : canUndo ( ) const
{
return d - > m_undoStack - > canUndo ( ) ;
}
bool Document : : canRedo ( ) const
{
return d - > m_undoStack - > canRedo ( ) ;
}
2007-01-02 19:05:49 +00:00
/* REFERENCE IMPLEMENTATION: better calling setViewport from other code
void Document : : setNextPage ( )
2004-11-09 17:20:19 +00:00
{
2007-01-02 19:05:49 +00:00
// advance page and set viewport on observers
if ( ( * d - > m_viewportIterator ) . pageNumber < ( int ) d - > m_pagesVector . count ( ) - 1 )
setViewport ( DocumentViewport ( ( * d - > m_viewportIterator ) . pageNumber + 1 ) ) ;
2004-11-09 17:20:19 +00:00
}
2004-12-10 16:04:45 +00:00
2007-01-02 19:05:49 +00:00
void Document : : setPrevPage ( )
2007-01-02 16:11:40 +00:00
{
2007-01-02 19:05:49 +00:00
// go to previous page and set viewport on observers
if ( ( * d - > m_viewportIterator ) . pageNumber > 0 )
setViewport ( DocumentViewport ( ( * d - > m_viewportIterator ) . pageNumber - 1 ) ) ;
}
*/
2023-05-12 10:39:41 +00:00
void Document : : setViewport ( const DocumentViewport & viewport , DocumentObserver * excludeObserver , bool smoothMove , bool updateHistory )
2007-01-02 19:05:49 +00:00
{
2008-11-30 11:28:08 +00:00
if ( ! viewport . isValid ( ) ) {
2014-09-11 17:36:01 +00:00
qCDebug ( OkularCoreDebug ) < < " invalid viewport: " < < viewport . toString ( ) ;
2008-11-30 11:28:08 +00:00
return ;
}
2011-02-12 14:49:32 +00:00
if ( viewport . pageNumber > = int ( d - > m_pagesVector . count ( ) ) ) {
2014-09-11 17:36:01 +00:00
// qCDebug(OkularCoreDebug) << "viewport out of document:" << viewport.toString();
2011-02-12 14:49:32 +00:00
return ;
}
2008-11-30 11:28:08 +00:00
2007-01-02 19:05:49 +00:00
// if already broadcasted, don't redo it
DocumentViewport & oldViewport = * d - > m_viewportIterator ;
// disabled by enrico on 2005-03-18 (less debug output)
// if ( viewport == oldViewport )
2014-09-11 17:36:01 +00:00
// qCDebug(OkularCoreDebug) << "setViewport with the same viewport.";
2007-01-02 19:05:49 +00:00
2012-08-16 15:37:39 +00:00
const int oldPageNumber = oldViewport . pageNumber ;
2007-01-02 19:05:49 +00:00
// set internal viewport taking care of history
2020-08-16 14:21:35 +00:00
if ( oldViewport . pageNumber = = viewport . pageNumber | | ! oldViewport . isValid ( ) | | ! updateHistory ) {
2007-01-02 19:05:49 +00:00
// if page is unchanged save the viewport at current position in queue
oldViewport = viewport ;
2007-01-02 16:11:40 +00:00
} else {
2007-01-02 19:05:49 +00:00
// remove elements after viewportIterator in queue
d - > m_viewportHistory . erase ( + + d - > m_viewportIterator , d - > m_viewportHistory . end ( ) ) ;
// keep the list to a reasonable size by removing head when needed
2022-03-31 15:05:15 +00:00
if ( d - > m_viewportHistory . size ( ) > = OKULAR_HISTORY_MAXSTEPS ) {
2007-01-02 19:05:49 +00:00
d - > m_viewportHistory . pop_front ( ) ;
2022-03-08 10:10:43 +00:00
}
2007-01-02 19:05:49 +00:00
// add the item at the end of the queue
d - > m_viewportIterator = d - > m_viewportHistory . insert ( d - > m_viewportHistory . end ( ) , viewport ) ;
}
2012-08-16 15:37:39 +00:00
const int currentViewportPage = ( * d - > m_viewportIterator ) . pageNumber ;
const bool currentPageChanged = ( oldPageNumber ! = currentViewportPage ) ;
2007-01-02 19:05:49 +00:00
// notify change to all other (different from id) observers
2019-12-09 13:16:55 +00:00
for ( DocumentObserver * o : qAsConst ( d - > m_observers ) ) {
2013-02-24 21:58:53 +00:00
if ( o ! = excludeObserver ) {
o - > notifyViewportChanged ( smoothMove ) ;
2022-03-08 10:10:43 +00:00
}
2012-08-16 15:37:39 +00:00
if ( currentPageChanged ) {
2013-02-24 21:58:53 +00:00
o - > notifyCurrentPageChanged ( oldPageNumber , currentViewportPage ) ;
2022-03-08 10:10:43 +00:00
}
2012-08-16 15:37:39 +00:00
}
2007-01-02 16:11:40 +00:00
}
2020-08-16 14:21:35 +00:00
void Document : : setViewportPage ( int page , DocumentObserver * excludeObserver , bool smoothMove )
{
// clamp page in range [0 ... numPages-1]
if ( page < 0 ) {
page = 0 ;
} else if ( page > ( int ) d - > m_pagesVector . count ( ) ) {
page = d - > m_pagesVector . count ( ) - 1 ;
2022-03-08 10:10:43 +00:00
}
2020-08-16 14:21:35 +00:00
// make a viewport from the page and broadcast it
setViewport ( DocumentViewport ( page ) , excludeObserver , smoothMove ) ;
}
2013-02-24 21:58:53 +00:00
void Document : : setZoom ( int factor , DocumentObserver * excludeObserver )
2007-07-08 14:25:08 +00:00
{
// notify change to all other (different from id) observers
2019-12-09 13:16:55 +00:00
for ( DocumentObserver * o : qAsConst ( d - > m_observers ) ) {
2013-02-24 21:58:53 +00:00
if ( o ! = excludeObserver ) {
o - > notifyZoom ( factor ) ;
2022-03-08 10:10:43 +00:00
}
}
2007-07-08 14:25:08 +00:00
}
2007-01-02 19:05:49 +00:00
void Document : : setPrevViewport ( )
// restore viewport from the history
2005-01-18 16:43:36 +00:00
{
2007-01-02 19:05:49 +00:00
if ( d - > m_viewportIterator ! = d - > m_viewportHistory . begin ( ) ) {
2013-11-27 23:05:24 +00:00
const int oldViewportPage = ( * d - > m_viewportIterator ) . pageNumber ;
2007-01-02 19:05:49 +00:00
// restore previous viewport and notify it to observers
- - d - > m_viewportIterator ;
foreachObserver ( notifyViewportChanged ( true ) ) ;
2013-11-27 23:05:24 +00:00
const int currentViewportPage = ( * d - > m_viewportIterator ) . pageNumber ;
if ( oldViewportPage ! = currentViewportPage )
foreachObserver ( notifyCurrentPageChanged ( oldViewportPage , currentViewportPage ) ) ;
2007-01-02 19:05:49 +00:00
}
}
2005-01-20 17:33:05 +00:00
2007-01-02 19:05:49 +00:00
void Document : : setNextViewport ( )
// restore next viewport from the history
{
2022-03-31 15:05:15 +00:00
auto nextIterator = std : : list < DocumentViewport > : : const_iterator ( d - > m_viewportIterator ) ;
2007-01-02 19:05:49 +00:00
+ + nextIterator ;
2022-03-31 15:05:15 +00:00
if ( nextIterator ! = d - > m_viewportHistory . end ( ) ) {
2013-11-27 23:05:24 +00:00
const int oldViewportPage = ( * d - > m_viewportIterator ) . pageNumber ;
2007-01-02 19:05:49 +00:00
// restore next viewport and notify it to observers
+ + d - > m_viewportIterator ;
foreachObserver ( notifyViewportChanged ( true ) ) ;
2013-11-27 23:05:24 +00:00
const int currentViewportPage = ( * d - > m_viewportIterator ) . pageNumber ;
if ( oldViewportPage ! = currentViewportPage )
foreachObserver ( notifyCurrentPageChanged ( oldViewportPage , currentViewportPage ) ) ;
2007-01-02 19:05:49 +00:00
}
}
void Document : : setNextDocumentViewport ( const DocumentViewport & viewport )
{
d - > m_nextDocumentViewport = viewport ;
}
2009-05-13 14:24:30 +00:00
void Document : : setNextDocumentDestination ( const QString & namedDestination )
{
d - > m_nextDocumentDestination = namedDestination ;
}
2007-08-13 22:25:27 +00:00
void Document : : searchText ( int searchID , const QString & text , bool fromStart , Qt : : CaseSensitivity caseSensitivity , SearchType type , bool moveViewport , const QColor & color )
2007-01-02 19:05:49 +00:00
{
2007-08-13 22:25:27 +00:00
d - > m_searchCancelled = false ;
2020-07-10 22:15:05 +00:00
2007-01-02 19:05:49 +00:00
// safety checks: don't perform searches on empty or unsearchable docs
2007-01-17 11:58:20 +00:00
if ( ! d - > m_generator | | ! d - > m_generator - > hasFeature ( Generator : : TextExtraction ) | | d - > m_pagesVector . isEmpty ( ) ) {
2022-03-18 21:35:45 +00:00
Q_EMIT searchFinished ( searchID , NoMatchFound ) ;
2007-08-13 22:25:27 +00:00
return ;
}
2007-01-02 19:05:49 +00:00
// if searchID search not recorded, create new descriptor and init params
2007-02-12 14:15:51 +00:00
QMap < int , RunningSearch * > : : iterator searchIt = d - > m_searches . find ( searchID ) ;
if ( searchIt = = d - > m_searches . end ( ) ) {
2007-01-02 19:05:49 +00:00
RunningSearch * search = new RunningSearch ( ) ;
search - > continueOnPage = - 1 ;
2007-02-12 14:15:51 +00:00
searchIt = d - > m_searches . insert ( searchID , search ) ;
2007-01-02 19:05:49 +00:00
}
2007-02-12 14:15:51 +00:00
RunningSearch * s = * searchIt ;
2007-01-02 19:05:49 +00:00
2010-04-02 18:49:13 +00:00
// update search structure
2007-01-02 19:05:49 +00:00
bool newText = text ! = s - > cachedString ;
s - > cachedString = text ;
s - > cachedType = type ;
s - > cachedCaseSensitivity = caseSensitivity ;
s - > cachedViewportMove = moveViewport ;
s - > cachedColor = color ;
2009-08-10 22:28:54 +00:00
s - > isCurrentlySearching = true ;
2007-01-02 19:05:49 +00:00
// global data for search
2007-08-13 22:25:27 +00:00
QSet < int > * pagesToNotify = new QSet < int > ;
2007-01-02 19:05:49 +00:00
// remove highlights from pages and queue them for notifying changes
2007-08-13 22:25:27 +00:00
* pagesToNotify + = s - > highlightedPages ;
2019-12-09 13:16:55 +00:00
for ( const int pageNumber : qAsConst ( s - > highlightedPages ) ) {
2007-08-13 22:25:27 +00:00
d - > m_pagesVector . at ( pageNumber ) - > d - > deleteHighlights ( searchID ) ;
2019-12-09 13:16:55 +00:00
}
2007-01-02 19:05:49 +00:00
s - > highlightedPages . clear ( ) ;
// set hourglass cursor
QApplication : : setOverrideCursor ( Qt : : WaitCursor ) ;
2018-11-14 19:12:15 +00:00
// 1. ALLDOC - process all document marking pages
2007-01-02 22:37:55 +00:00
if ( type = = AllDocument ) {
2007-08-13 22:25:27 +00:00
QMap < Page * , QVector < RegularAreaRect * > > * pageMatches = new QMap < Page * , QVector < RegularAreaRect * > > ;
2007-01-02 19:05:49 +00:00
2007-08-13 22:25:27 +00:00
// search and highlight 'text' (as a solid phrase) on all pages
2019-12-19 22:18:31 +00:00
QTimer : : singleShot ( 0 , this , [ this , pagesToNotify , pageMatches , searchID ] { d - > doContinueAllDocumentSearch ( pagesToNotify , pageMatches , 0 , searchID ) ; } ) ;
2007-01-02 19:05:49 +00:00
}
// 2. NEXTMATCH - find next matching item (or start from top)
2008-03-14 14:14:06 +00:00
// 3. PREVMATCH - find previous matching item (or start from bottom)
2012-09-24 22:14:10 +00:00
else if ( type = = NextMatch | | type = = PreviousMatch ) {
2008-03-14 14:14:06 +00:00
// find out from where to start/resume search from
2012-09-24 22:14:10 +00:00
const bool forward = type = = NextMatch ;
const int viewportPage = ( * d - > m_viewportIterator ) . pageNumber ;
const int fromStartSearchPage = forward ? 0 : d - > m_pagesVector . count ( ) - 1 ;
int currentPage = fromStart ? fromStartSearchPage : ( ( s - > continueOnPage ! = - 1 ) ? s - > continueOnPage : viewportPage ) ;
2020-09-13 09:20:09 +00:00
Page * lastPage = fromStart ? nullptr : d - > m_pagesVector [ currentPage ] ;
2008-11-30 11:02:02 +00:00
int pagesDone = 0 ;
2008-03-14 14:14:06 +00:00
// continue checking last TextPage first (if it is the current page)
2017-09-05 21:27:18 +00:00
RegularAreaRect * match = nullptr ;
2008-03-14 14:14:06 +00:00
if ( lastPage & & lastPage - > number ( ) = = s - > continueOnPage ) {
if ( newText ) {
2012-09-24 22:14:10 +00:00
match = lastPage - > findText ( searchID , text , forward ? FromTop : FromBottom , caseSensitivity ) ;
2008-03-14 14:14:06 +00:00
} else {
2012-09-24 22:14:10 +00:00
match = lastPage - > findText ( searchID , text , forward ? NextResult : PreviousResult , caseSensitivity , & s - > continueOnMatch ) ;
2022-03-08 10:10:43 +00:00
}
2008-03-14 14:14:06 +00:00
if ( ! match ) {
2012-09-24 22:14:10 +00:00
if ( forward ) {
currentPage + + ;
} else {
currentPage - - ;
2022-03-08 10:10:43 +00:00
}
2008-11-30 11:02:02 +00:00
pagesDone + + ;
}
2008-03-14 14:14:06 +00:00
}
2013-04-05 22:22:48 +00:00
2014-05-10 09:33:21 +00:00
s - > pagesDone = pagesDone ;
2012-09-24 22:14:10 +00:00
DoContinueDirectionMatchSearchStruct * searchStruct = new DoContinueDirectionMatchSearchStruct ( ) ;
searchStruct - > pagesToNotify = pagesToNotify ;
searchStruct - > match = match ;
searchStruct - > currentPage = currentPage ;
searchStruct - > searchID = searchID ;
2019-12-19 22:18:31 +00:00
QTimer : : singleShot ( 0 , this , [ this , searchStruct ] { d - > doContinueDirectionMatchSearch ( searchStruct ) ; } ) ;
2007-01-02 19:05:49 +00:00
}
// 4. GOOGLE* - process all document marking pages
else if ( type = = GoogleAll | | type = = GoogleAny ) {
2007-08-13 22:25:27 +00:00
QMap < Page * , QVector < QPair < RegularAreaRect * , QColor > > > * pageMatches = new QMap < Page * , QVector < QPair < RegularAreaRect * , QColor > > > ;
2023-05-11 11:16:55 +00:00
const QStringList words = text . split ( QLatin1Char ( ' ' ) , Qt : : SkipEmptyParts ) ;
2007-01-02 19:05:49 +00:00
2007-08-13 22:25:27 +00:00
// search and highlight every word in 'text' on all pages
2019-12-19 22:18:31 +00:00
QTimer : : singleShot ( 0 , this , [ this , pagesToNotify , pageMatches , searchID , words ] { d - > doContinueGooglesDocumentSearch ( pagesToNotify , pageMatches , 0 , searchID , words ) ; } ) ;
2007-01-02 19:05:49 +00:00
}
}
2007-08-13 22:25:27 +00:00
void Document : : continueSearch ( int searchID )
2007-01-02 19:05:49 +00:00
{
// check if searchID is present in runningSearches
2007-02-12 14:15:51 +00:00
QMap < int , RunningSearch * > : : const_iterator it = d - > m_searches . constFind ( searchID ) ;
if ( it = = d - > m_searches . constEnd ( ) ) {
2022-03-18 21:35:45 +00:00
Q_EMIT searchFinished ( searchID , NoMatchFound ) ;
2007-08-13 22:25:27 +00:00
return ;
}
2007-01-02 19:05:49 +00:00
// start search with cached parameters from last search by searchID
2007-02-12 14:15:51 +00:00
RunningSearch * p = * it ;
2009-08-10 22:28:54 +00:00
if ( ! p - > isCurrentlySearching ) {
searchText ( searchID , p - > cachedString , false , p - > cachedCaseSensitivity , p - > cachedType , p - > cachedViewportMove , p - > cachedColor ) ;
2022-03-08 10:10:43 +00:00
}
2005-01-18 16:43:36 +00:00
}
2008-03-14 13:57:01 +00:00
void Document : : continueSearch ( int searchID , SearchType type )
{
// check if searchID is present in runningSearches
QMap < int , RunningSearch * > : : const_iterator it = d - > m_searches . constFind ( searchID ) ;
if ( it = = d - > m_searches . constEnd ( ) ) {
2022-03-18 21:35:45 +00:00
Q_EMIT searchFinished ( searchID , NoMatchFound ) ;
2008-03-14 13:57:01 +00:00
return ;
}
// start search with cached parameters from last search by searchID
RunningSearch * p = * it ;
2009-08-10 22:28:54 +00:00
if ( ! p - > isCurrentlySearching ) {
searchText ( searchID , p - > cachedString , false , p - > cachedCaseSensitivity , type , p - > cachedViewportMove , p - > cachedColor ) ;
2022-03-08 10:10:43 +00:00
}
2008-03-14 13:57:01 +00:00
}
2007-01-02 19:05:49 +00:00
void Document : : resetSearch ( int searchID )
2005-01-18 16:43:36 +00:00
{
2014-01-05 00:26:41 +00:00
// if we are closing down, don't bother doing anything
if ( ! d - > m_generator ) {
return ;
2022-03-08 10:10:43 +00:00
}
2014-01-05 00:26:41 +00:00
2007-01-02 19:05:49 +00:00
// check if searchID is present in runningSearches
2007-02-12 14:15:51 +00:00
QMap < int , RunningSearch * > : : iterator searchIt = d - > m_searches . find ( searchID ) ;
if ( searchIt = = d - > m_searches . end ( ) ) {
2005-01-18 16:43:36 +00:00
return ;
2022-03-08 10:10:43 +00:00
}
2005-01-18 16:43:36 +00:00
2007-01-02 19:05:49 +00:00
// get previous parameters for search
2007-02-12 14:15:51 +00:00
RunningSearch * s = * searchIt ;
2005-01-18 16:43:36 +00:00
2007-01-02 19:05:49 +00:00
// unhighlight pages and inform observers about that
2019-12-09 13:16:55 +00:00
for ( const int pageNumber : qAsConst ( s - > highlightedPages ) ) {
2007-08-13 22:25:27 +00:00
d - > m_pagesVector . at ( pageNumber ) - > d - > deleteHighlights ( searchID ) ;
2007-01-02 19:05:49 +00:00
foreachObserver ( notifyPageChanged ( pageNumber , DocumentObserver : : Highlights ) ) ;
}
2006-11-20 07:53:32 +00:00
2007-01-02 19:05:49 +00:00
// send the setup signal too (to update views that filter on matches)
2007-09-09 10:50:36 +00:00
foreachObserver ( notifySetup ( d - > m_pagesVector , 0 ) ) ;
2006-11-20 07:53:32 +00:00
2018-11-14 19:12:15 +00:00
// remove search from the runningSearches list and delete it
2007-02-12 14:15:51 +00:00
d - > m_searches . erase ( searchIt ) ;
2007-01-02 19:05:49 +00:00
delete s ;
}
2007-08-13 22:25:27 +00:00
void Document : : cancelSearch ( )
{
d - > m_searchCancelled = true ;
}
2007-01-02 19:05:49 +00:00
2013-04-05 22:22:48 +00:00
void Document : : undo ( )
{
d - > m_undoStack - > undo ( ) ;
}
void Document : : redo ( )
{
d - > m_undoStack - > redo ( ) ;
}
2013-06-03 20:46:41 +00:00
void Document : : editFormText ( int pageNumber , Okular : : FormFieldText * form , const QString & newContents , int newCursorPos , int prevCursorPos , int prevAnchorPos )
{
2013-12-29 22:27:30 +00:00
QUndoCommand * uc = new EditFormTextCommand ( this - > d , form , pageNumber , newContents , newCursorPos , form - > text ( ) , prevCursorPos , prevAnchorPos ) ;
2013-06-03 20:46:41 +00:00
d - > m_undoStack - > push ( uc ) ;
}
void Document : : editFormList ( int pageNumber , FormFieldChoice * form , const QList < int > & newChoices )
{
const QList < int > prevChoices = form - > currentChoices ( ) ;
2013-12-29 22:27:30 +00:00
QUndoCommand * uc = new EditFormListCommand ( this - > d , form , pageNumber , newChoices , prevChoices ) ;
2013-06-03 20:46:41 +00:00
d - > m_undoStack - > push ( uc ) ;
}
void Document : : editFormCombo ( int pageNumber , FormFieldChoice * form , const QString & newText , int newCursorPos , int prevCursorPos , int prevAnchorPos )
{
QString prevText ;
if ( form - > currentChoices ( ) . isEmpty ( ) ) {
prevText = form - > editChoice ( ) ;
} else {
2020-02-19 16:13:21 +00:00
prevText = form - > choices ( ) . at ( form - > currentChoices ( ) . constFirst ( ) ) ;
2013-06-03 20:46:41 +00:00
}
2013-12-29 22:27:30 +00:00
QUndoCommand * uc = new EditFormComboCommand ( this - > d , form , pageNumber , newText , newCursorPos , prevText , prevCursorPos , prevAnchorPos ) ;
2013-06-03 20:46:41 +00:00
d - > m_undoStack - > push ( uc ) ;
}
void Document : : editFormButtons ( int pageNumber , const QList < FormFieldButton * > & formButtons , const QList < bool > & newButtonStates )
{
2013-12-29 22:27:30 +00:00
QUndoCommand * uc = new EditFormButtonsCommand ( this - > d , pageNumber , formButtons , newButtonStates ) ;
2013-06-03 20:46:41 +00:00
d - > m_undoStack - > push ( uc ) ;
}
2015-05-27 13:56:56 +00:00
void Document : : reloadDocument ( ) const
{
const int numOfPages = pages ( ) ;
2015-07-12 15:52:33 +00:00
for ( int i = currentPage ( ) ; i > = 0 ; i - - ) {
d - > refreshPixmaps ( i ) ;
2022-03-08 10:10:43 +00:00
}
2015-07-12 15:52:33 +00:00
for ( int i = currentPage ( ) + 1 ; i < numOfPages ; i + + ) {
2015-05-27 13:56:56 +00:00
d - > refreshPixmaps ( i ) ;
2022-03-08 10:10:43 +00:00
}
2015-05-27 13:56:56 +00:00
}
2007-10-28 18:31:33 +00:00
BookmarkManager * Document : : bookmarkManager ( ) const
2007-01-02 19:05:49 +00:00
{
return d - > m_bookmarkManager ;
}
2004-12-22 18:21:36 +00:00
2007-11-26 21:43:54 +00:00
QList < int > Document : : bookmarkedPageList ( ) const
{
QList < int > list ;
uint docPages = pages ( ) ;
// pages are 0-indexed internally, but 1-indexed externally
for ( uint i = 0 ; i < docPages ; i + + ) {
if ( bookmarkManager ( ) - > isBookmarked ( i ) ) {
list < < i + 1 ;
}
}
return list ;
}
QString Document : : bookmarkedPageRange ( ) const
{
// Code formerly in Part::slotPrint()
// range detecting
QString range ;
uint docPages = pages ( ) ;
int startId = - 1 ;
int endId = - 1 ;
for ( uint i = 0 ; i < docPages ; + + i ) {
if ( bookmarkManager ( ) - > isBookmarked ( i ) ) {
if ( startId < 0 ) {
startId = i ;
2022-03-08 10:10:43 +00:00
}
2007-11-26 21:43:54 +00:00
if ( endId < 0 ) {
endId = startId ;
} else {
+ + endId ;
2022-03-08 10:10:43 +00:00
}
2007-11-26 21:43:54 +00:00
} else if ( startId > = 0 & & endId > = 0 ) {
if ( ! range . isEmpty ( ) ) {
2016-07-11 20:07:57 +00:00
range + = QLatin1Char ( ' , ' ) ;
2022-03-08 10:10:43 +00:00
}
2007-11-26 21:43:54 +00:00
if ( endId - startId > 0 ) {
2015-10-29 12:37:11 +00:00
range + = QStringLiteral ( " %1-%2 " ) . arg ( startId + 1 ) . arg ( endId + 1 ) ;
2007-11-26 21:43:54 +00:00
} else {
range + = QString : : number ( startId + 1 ) ;
2022-03-08 10:10:43 +00:00
}
2007-11-26 21:43:54 +00:00
startId = - 1 ;
endId = - 1 ;
}
}
if ( startId > = 0 & & endId > = 0 ) {
if ( ! range . isEmpty ( ) ) {
2016-07-11 20:07:57 +00:00
range + = QLatin1Char ( ' , ' ) ;
2022-03-08 10:10:43 +00:00
}
2007-11-26 21:43:54 +00:00
if ( endId - startId > 0 ) {
2015-10-29 12:37:11 +00:00
range + = QStringLiteral ( " %1-%2 " ) . arg ( startId + 1 ) . arg ( endId + 1 ) ;
2007-11-26 21:43:54 +00:00
} else {
range + = QString : : number ( startId + 1 ) ;
2022-03-08 10:10:43 +00:00
}
2007-11-26 21:43:54 +00:00
}
return range ;
}
2023-03-29 21:16:33 +00:00
struct ExecuteNextActionsHelper : public QObject , private DocumentObserver {
2021-08-26 22:31:14 +00:00
Q_OBJECT
public :
2023-03-29 21:16:33 +00:00
explicit ExecuteNextActionsHelper ( Document * doc )
: m_doc ( doc )
{
doc - > addObserver ( this ) ;
connect ( doc , & Document : : aboutToClose , this , [ this ] { b = false ; } ) ;
}
~ ExecuteNextActionsHelper ( ) override
{
m_doc - > removeObserver ( this ) ;
}
void notifySetup ( const QVector < Okular : : Page * > & /*pages*/ , int setupFlags ) override
{
if ( setupFlags = = DocumentChanged | | setupFlags = = UrlChanged ) {
b = false ;
}
}
bool shouldExecuteNextAction ( ) const
{
return b ;
}
private :
Document * const m_doc ;
2021-08-26 22:31:14 +00:00
bool b = true ;
} ;
2007-04-20 12:49:17 +00:00
void Document : : processAction ( const Action * action )
2007-01-02 19:05:49 +00:00
{
2007-04-20 12:49:17 +00:00
if ( ! action ) {
2007-01-02 19:05:49 +00:00
return ;
2022-03-08 10:10:43 +00:00
}
2004-12-24 10:24:10 +00:00
2018-10-21 20:25:47 +00:00
// Don't execute next actions if the action itself caused the closing of the document
2023-03-29 21:16:33 +00:00
const ExecuteNextActionsHelper executeNextActionsHelper ( this ) ;
2020-07-10 22:15:05 +00:00
2007-04-20 12:49:17 +00:00
switch ( action - > actionType ( ) ) {
2007-04-20 12:37:12 +00:00
case Action : : Goto : {
2007-05-02 22:50:27 +00:00
const GotoAction * go = static_cast < const GotoAction * > ( action ) ;
2007-01-02 19:05:49 +00:00
d - > m_nextDocumentViewport = go - > destViewport ( ) ;
2009-05-13 14:24:30 +00:00
d - > m_nextDocumentDestination = go - > destinationName ( ) ;
2020-07-10 22:15:05 +00:00
2007-01-02 19:05:49 +00:00
// Explanation of why d->m_nextDocumentViewport is needed:
// all openRelativeFile does is launch a signal telling we
2007-03-10 05:38:23 +00:00
// want to open another URL, the problem is that when the file is
2018-11-14 19:12:15 +00:00
// non local, the loading is done asynchronously so you can't
2007-01-02 19:05:49 +00:00
// do a setViewport after the if as it was because you are doing the setViewport
// on the old file and when the new arrives there is no setViewport for it and
// it does not show anything
2020-07-10 22:15:05 +00:00
2007-01-02 19:05:49 +00:00
// first open filename if link is pointing outside this document
2021-08-26 22:31:14 +00:00
const QString filename = go - > fileName ( ) ;
if ( go - > isExternal ( ) & & ! d - > openRelativeFile ( filename ) ) {
qCWarning ( OkularCoreDebug ) . nospace ( ) < < " Action: Error opening ' " < < filename < < " '. " ;
2020-07-10 22:15:05 +00:00
break ;
} else {
2009-05-13 14:24:30 +00:00
const DocumentViewport nextViewport = d - > nextDocumentViewport ( ) ;
2007-01-02 19:05:49 +00:00
// skip local links that point to nowhere (broken ones)
2009-05-13 14:24:30 +00:00
if ( ! nextViewport . isValid ( ) ) {
2018-04-30 11:41:55 +00:00
break ;
2022-03-08 10:10:43 +00:00
}
2004-12-22 18:21:36 +00:00
2017-09-05 21:27:18 +00:00
setViewport ( nextViewport , nullptr , true ) ;
2007-01-02 19:05:49 +00:00
d - > m_nextDocumentViewport = DocumentViewport ( ) ;
2009-05-13 14:24:30 +00:00
d - > m_nextDocumentDestination = QString ( ) ;
2007-01-02 19:05:49 +00:00
}
2004-12-22 18:21:36 +00:00
2007-01-02 19:05:49 +00:00
} break ;
2004-12-21 12:38:52 +00:00
2007-04-20 12:37:12 +00:00
case Action : : Execute : {
2007-05-02 22:50:27 +00:00
const ExecuteAction * exe = static_cast < const ExecuteAction * > ( action ) ;
2016-01-30 19:00:25 +00:00
const QString fileName = exe - > fileName ( ) ;
if ( fileName . endsWith ( QLatin1String ( " .pdf " ) , Qt : : CaseInsensitive ) ) {
2007-01-02 19:05:49 +00:00
d - > openRelativeFile ( fileName ) ;
2018-04-30 11:41:55 +00:00
break ;
2007-01-02 19:05:49 +00:00
}
2004-12-21 12:38:52 +00:00
2007-01-02 19:05:49 +00:00
// Albert: the only pdf i have that has that kind of link don't define
// an application and use the fileName as the file to open
2016-01-30 19:00:25 +00:00
QUrl url = d - > giveAbsoluteUrl ( fileName ) ;
2014-09-11 13:18:05 +00:00
QMimeDatabase db ;
2016-01-30 19:00:25 +00:00
QMimeType mime = db . mimeTypeForUrl ( url ) ;
2007-01-02 19:05:49 +00:00
// Check executables
2016-01-30 19:00:25 +00:00
if ( KRun : : isExecutableFile ( url , mime . name ( ) ) ) {
2007-01-02 19:05:49 +00:00
// Don't have any pdf that uses this code path, just a guess on how it should work
if ( ! exe - > parameters ( ) . isEmpty ( ) ) {
2016-01-30 19:00:25 +00:00
url = d - > giveAbsoluteUrl ( exe - > parameters ( ) ) ;
mime = db . mimeTypeForUrl ( url ) ;
2020-07-10 22:15:05 +00:00
2016-01-30 19:00:25 +00:00
if ( KRun : : isExecutableFile ( url , mime . name ( ) ) ) {
2007-01-02 19:05:49 +00:00
// this case is a link pointing to an executable with a parameter
// that also is an executable, possibly a hand-crafted pdf
2022-03-18 21:35:45 +00:00
Q_EMIT error ( i18n ( " The document is trying to execute an external application and, for your safety, Okular does not allow that. " ) , - 1 ) ;
2018-04-30 11:41:55 +00:00
break ;
2007-01-02 19:05:49 +00:00
}
2020-07-10 22:15:05 +00:00
} else {
2007-01-02 19:05:49 +00:00
// this case is a link pointing to an executable with no parameters
// core developers find unacceptable executing it even after asking the user
2022-03-18 21:35:45 +00:00
Q_EMIT error ( i18n ( " The document is trying to execute an external application and, for your safety, Okular does not allow that. " ) , - 1 ) ;
2020-07-10 22:15:05 +00:00
break ;
2007-01-02 19:05:49 +00:00
}
2020-07-10 22:15:05 +00:00
}
2006-11-15 19:39:39 +00:00
2023-03-21 21:07:53 +00:00
# if KIO_VERSION >= QT_VERSION_CHECK(5, 98, 0)
KIO : : OpenUrlJob * job = new KIO : : OpenUrlJob ( url , mime . name ( ) ) ;
job - > setUiDelegate ( KIO : : createDefaultJobUiDelegate ( KJobUiDelegate : : AutoHandlingEnabled , d - > m_widget ) ) ;
job - > start ( ) ;
connect ( job , & KIO : : OpenUrlJob : : result , this , [ this , mime ] ( KJob * job ) {
if ( job - > error ( ) ) {
Q_EMIT error ( i18n ( " No application found for opening file of mimetype %1. " , mime . name ( ) ) , - 1 ) ;
}
} ) ;
# else
2020-08-12 12:28:17 +00:00
KService : : Ptr ptr = KApplicationTrader : : preferredService ( mime . name ( ) ) ;
2007-01-02 19:05:49 +00:00
if ( ptr ) {
2014-08-13 11:07:44 +00:00
QList < QUrl > lst ;
2016-01-30 19:00:25 +00:00
lst . append ( url ) ;
2017-09-05 21:27:18 +00:00
KRun : : runService ( * ptr , lst , nullptr ) ;
2007-01-02 19:05:49 +00:00
} else {
2022-03-18 21:35:45 +00:00
Q_EMIT error ( i18n ( " No application found for opening file of mimetype %1. " , mime . name ( ) ) , - 1 ) ;
2022-03-08 10:10:43 +00:00
}
2023-03-21 21:07:53 +00:00
# endif
2007-01-02 19:05:49 +00:00
} break ;
2020-07-10 22:15:05 +00:00
2007-05-02 22:50:27 +00:00
case Action : : DocAction : {
const DocumentAction * docaction = static_cast < const DocumentAction * > ( action ) ;
switch ( docaction - > documentActionType ( ) ) {
case DocumentAction : : PageFirst :
2007-01-02 19:05:49 +00:00
setViewportPage ( 0 ) ;
2020-07-10 22:15:05 +00:00
break ;
2007-05-02 22:50:27 +00:00
case DocumentAction : : PagePrev :
if ( ( * d - > m_viewportIterator ) . pageNumber > 0 ) {
2007-01-02 19:05:49 +00:00
setViewportPage ( ( * d - > m_viewportIterator ) . pageNumber - 1 ) ;
2022-03-08 10:10:43 +00:00
}
2007-01-02 19:05:49 +00:00
break ;
2007-05-02 22:50:27 +00:00
case DocumentAction : : PageNext :
2008-09-22 13:41:28 +00:00
if ( ( * d - > m_viewportIterator ) . pageNumber < ( int ) d - > m_pagesVector . count ( ) - 1 ) {
2007-01-02 19:05:49 +00:00
setViewportPage ( ( * d - > m_viewportIterator ) . pageNumber + 1 ) ;
2022-03-08 10:10:43 +00:00
}
2018-04-30 11:41:55 +00:00
break ;
2016-08-03 14:01:30 +00:00
case DocumentAction : : PageLast :
setViewportPage ( d - > m_pagesVector . count ( ) - 1 ) ;
break ;
case DocumentAction : : HistoryBack :
2020-03-10 22:07:24 +00:00
setPrevViewport ( ) ;
2007-01-02 19:05:49 +00:00
break ;
2007-04-20 12:37:12 +00:00
case DocumentAction : : HistoryForward :
2007-02-05 00:49:40 +00:00
setNextViewport ( ) ;
break ;
2008-04-13 22:31:59 +00:00
case DocumentAction : : Quit :
2022-03-18 21:35:45 +00:00
Q_EMIT quit ( ) ;
2007-01-02 19:05:49 +00:00
break ;
2014-09-11 19:12:27 +00:00
case DocumentAction : : Presentation :
2022-03-18 21:35:45 +00:00
Q_EMIT linkPresentation ( ) ;
2020-07-10 22:15:05 +00:00
break ;
2012-09-27 12:15:04 +00:00
case DocumentAction : : EndPresentation :
2022-03-18 21:35:45 +00:00
Q_EMIT linkEndPresentation ( ) ;
2012-09-27 12:15:04 +00:00
break ;
case DocumentAction : : Find :
2022-03-18 21:35:45 +00:00
Q_EMIT linkFind ( ) ;
2012-09-27 12:15:04 +00:00
break ;
2007-05-02 22:50:27 +00:00
case DocumentAction : : GoToPage :
2022-03-18 21:35:45 +00:00
Q_EMIT linkGoToPage ( ) ;
2020-07-10 22:15:05 +00:00
break ;
2007-05-02 22:50:27 +00:00
case DocumentAction : : Close :
2022-03-18 21:35:45 +00:00
Q_EMIT close ( ) ;
2020-07-10 22:15:05 +00:00
break ;
2022-01-19 21:44:23 +00:00
case DocumentAction : : Print :
2022-03-18 21:35:45 +00:00
Q_EMIT requestPrint ( ) ;
2022-01-19 21:44:23 +00:00
break ;
2022-03-01 23:03:34 +00:00
case DocumentAction : : SaveAs :
2022-03-18 21:35:45 +00:00
Q_EMIT requestSaveAs ( ) ;
2022-03-01 23:03:34 +00:00
break ;
2020-07-10 22:15:05 +00:00
}
} break ;
2012-09-27 12:15:04 +00:00
case Action : : Browse : {
const BrowseAction * browse = static_cast < const BrowseAction * > ( action ) ;
2008-09-22 13:41:28 +00:00
QString lilySource ;
int lilyRow = 0 , lilyCol = 0 ;
2007-01-02 19:05:49 +00:00
// if the url is a mailto one, invoke mailer
2016-08-03 14:01:30 +00:00
if ( browse - > url ( ) . scheme ( ) = = QLatin1String ( " mailto " ) ) {
2015-01-29 20:48:47 +00:00
QDesktopServices : : openUrl ( browse - > url ( ) ) ;
2012-09-27 12:15:04 +00:00
} else if ( extractLilyPondSourceReference ( browse - > url ( ) , & lilySource , & lilyRow , & lilyCol ) ) {
2008-09-22 13:41:28 +00:00
const SourceReference ref ( lilySource , lilyRow , lilyCol ) ;
processSourceReference ( & ref ) ;
2020-07-10 22:15:05 +00:00
} else {
2016-08-03 14:01:30 +00:00
const QUrl url = browse - > url ( ) ;
2020-07-10 22:15:05 +00:00
2012-09-27 12:15:04 +00:00
// fix for #100366, documents with relative links that are the form of http:foo.pdf
if ( ( url . scheme ( ) = = QLatin1String ( " http " ) ) & & url . host ( ) . isEmpty ( ) & & url . fileName ( ) . endsWith ( QLatin1String ( " pdf " ) ) ) {
d - > openRelativeFile ( url . fileName ( ) ) ;
break ;
}
2015-08-12 20:42:00 +00:00
2007-01-02 19:05:49 +00:00
// handle documents with relative path
2023-07-23 16:28:03 +00:00
QUrl realUrl ;
2015-08-12 20:42:00 +00:00
if ( d - > m_url . isValid ( ) ) {
2023-07-23 16:28:03 +00:00
realUrl = KIO : : upUrl ( d - > m_url ) . resolved ( url ) ;
} else if ( ! url . isRelative ( ) ) {
realUrl = url ;
}
if ( realUrl . isValid ( ) ) {
2015-08-12 20:42:00 +00:00
// KRun autodeletes
2020-03-10 22:07:24 +00:00
KRun * r = new KRun ( realUrl , d - > m_widget ) ;
r - > setRunExecutables ( false ) ;
2020-07-10 22:15:05 +00:00
}
}
} break ;
2007-05-02 22:50:27 +00:00
case Action : : Sound : {
const SoundAction * linksound = static_cast < const SoundAction * > ( action ) ;
2007-02-05 00:49:40 +00:00
AudioPlayer : : instance ( ) - > playSound ( linksound - > sound ( ) , linksound ) ;
2020-07-10 22:15:05 +00:00
} break ;
2007-05-02 22:50:27 +00:00
case Action : : Script : {
2008-04-13 22:31:59 +00:00
const ScriptAction * linkscript = static_cast < const ScriptAction * > ( action ) ;
if ( ! d - > m_scripter ) {
d - > m_scripter = new Scripter ( d ) ;
2022-03-08 10:10:43 +00:00
}
2008-04-13 22:31:59 +00:00
d - > m_scripter - > execute ( linkscript - > scriptType ( ) , linkscript - > script ( ) ) ;
2020-07-10 22:15:05 +00:00
} break ;
2007-04-20 12:37:12 +00:00
case Action : : Movie :
2022-03-18 21:35:45 +00:00
Q_EMIT processMovieAction ( static_cast < const MovieAction * > ( action ) ) ;
2020-07-10 22:15:05 +00:00
break ;
2007-05-02 22:50:27 +00:00
case Action : : Rendition : {
2015-08-12 20:42:00 +00:00
const RenditionAction * linkrendition = static_cast < const RenditionAction * > ( action ) ;
2016-11-26 15:00:02 +00:00
if ( ! linkrendition - > script ( ) . isEmpty ( ) ) {
if ( ! d - > m_scripter ) {
d - > m_scripter = new Scripter ( d ) ;
2022-03-08 10:10:43 +00:00
}
2016-11-26 15:00:02 +00:00
d - > m_scripter - > execute ( linkrendition - > scriptType ( ) , linkrendition - > script ( ) ) ;
}
2022-03-18 21:35:45 +00:00
Q_EMIT processRenditionAction ( static_cast < const RenditionAction * > ( action ) ) ;
2020-07-10 22:15:05 +00:00
} break ;
2016-11-26 15:00:02 +00:00
case Action : : BackendOpaque : {
d - > m_generator - > opaqueAction ( static_cast < const BackendOpaqueAction * > ( action ) ) ;
2020-07-10 22:15:05 +00:00
} break ;
2007-01-02 19:05:49 +00:00
}
2018-04-30 11:41:55 +00:00
2023-03-29 21:16:33 +00:00
if ( executeNextActionsHelper . shouldExecuteNextAction ( ) ) {
2020-02-19 16:28:08 +00:00
const QVector < Action * > nextActions = action - > nextActions ( ) ;
for ( const Action * a : nextActions ) {
2018-10-21 20:25:47 +00:00
processAction ( a ) ;
}
2018-04-30 11:41:55 +00:00
}
2004-12-21 12:38:52 +00:00
}
2019-06-26 00:58:28 +00:00
void Document : : processFormatAction ( const Action * action , Okular : : FormFieldText * fft )
{
if ( action - > actionType ( ) ! = Action : : Script ) {
qCDebug ( OkularCoreDebug ) < < " Unsupported action type " < < action - > actionType ( ) < < " for formatting. " ;
return ;
}
// Lookup the page of the FormFieldText
2019-08-22 22:16:56 +00:00
int foundPage = d - > findFieldPageNumber ( fft ) ;
2019-06-26 00:58:28 +00:00
if ( foundPage = = - 1 ) {
qCDebug ( OkularCoreDebug ) < < " Could not find page for formfield! " ;
return ;
}
2019-08-10 21:21:38 +00:00
const QString unformattedText = fft - > text ( ) ;
2019-06-26 00:58:28 +00:00
std : : shared_ptr < Event > event = Event : : createFormatEvent ( fft , d - > m_pagesVector [ foundPage ] ) ;
const ScriptAction * linkscript = static_cast < const ScriptAction * > ( action ) ;
2019-08-22 22:16:56 +00:00
d - > executeScriptEvent ( event , linkscript ) ;
2019-06-26 00:58:28 +00:00
2019-08-10 21:21:38 +00:00
const QString formattedText = event - > value ( ) . toString ( ) ;
if ( formattedText ! = unformattedText ) {
// We set the formattedText, because when we call refreshFormWidget
// It will set the QLineEdit to this formattedText
fft - > setText ( formattedText ) ;
fft - > setAppearanceText ( formattedText ) ;
2022-03-18 21:35:45 +00:00
Q_EMIT refreshFormWidget ( fft ) ;
2019-06-26 00:58:28 +00:00
d - > refreshPixmaps ( foundPage ) ;
2019-08-09 17:49:57 +00:00
// Then we make the form have the unformatted text, to use
// in calculations and other things.
2019-08-10 21:21:38 +00:00
fft - > setText ( unformattedText ) ;
2019-06-26 00:58:28 +00:00
} else if ( fft - > additionalAction ( FormField : : CalculateField ) ) {
// When the field was calculated we need to refresh even
// if the format script changed nothing. e.g. on error.
// This is because the recalculateForms function delegated
// the responsiblity for the refresh to us.
2022-03-18 21:35:45 +00:00
Q_EMIT refreshFormWidget ( fft ) ;
2019-06-26 00:58:28 +00:00
d - > refreshPixmaps ( foundPage ) ;
}
}
2022-04-08 18:05:37 +00:00
QString DocumentPrivate : : diff ( const QString & oldVal , const QString & newVal )
{
2023-08-10 23:06:18 +00:00
// We need to consider unicode surrogate pairs and others so working
// with QString directly, even with the private QStringIterator is
// not that simple to get right
// so let's just convert to ucs4
// also, given that toUcs4 is either a QList or a QVector depending on
// qt version, let's try keep it very auto-typed to ease Qt6 porting
2022-04-08 18:05:37 +00:00
2023-08-10 23:06:18 +00:00
auto oldUcs4 = oldVal . toUcs4 ( ) ;
auto newUcs4 = newVal . toUcs4 ( ) ;
2022-04-08 18:05:37 +00:00
2023-08-10 23:06:18 +00:00
for ( int i = 0 ; i < std : : min ( oldUcs4 . size ( ) , newUcs4 . size ( ) ) ; i + + ) {
if ( oldUcs4 . at ( i ) ! = newUcs4 . at ( i ) ) {
return QString : : fromUcs4 ( newUcs4 . mid ( i ) . constData ( ) , newUcs4 . size ( ) - i ) ;
2022-04-08 18:05:37 +00:00
}
}
2023-08-10 23:06:18 +00:00
if ( oldUcs4 . size ( ) < newUcs4 . size ( ) ) {
return QString : : fromUcs4 ( newUcs4 . mid ( oldUcs4 . size ( ) ) . constData ( ) , newUcs4 . size ( ) - oldUcs4 . size ( ) ) ;
2022-04-08 18:05:37 +00:00
}
2023-08-10 23:06:18 +00:00
return { } ;
2022-04-08 18:05:37 +00:00
}
2022-02-04 15:52:29 +00:00
void Document : : processKeystrokeAction ( const Action * action , Okular : : FormFieldText * fft , const QVariant & newValue )
2019-07-27 17:53:31 +00:00
{
if ( action - > actionType ( ) ! = Action : : Script ) {
qCDebug ( OkularCoreDebug ) < < " Unsupported action type " < < action - > actionType ( ) < < " for keystroke. " ;
return ;
}
// Lookup the page of the FormFieldText
2019-08-22 22:16:56 +00:00
int foundPage = d - > findFieldPageNumber ( fft ) ;
2019-07-27 17:53:31 +00:00
if ( foundPage = = - 1 ) {
qCDebug ( OkularCoreDebug ) < < " Could not find page for formfield! " ;
return ;
}
std : : shared_ptr < Event > event = Event : : createKeystrokeEvent ( fft , d - > m_pagesVector [ foundPage ] ) ;
2022-04-08 18:05:37 +00:00
event - > setChange ( DocumentPrivate : : diff ( fft - > text ( ) , newValue . toString ( ) ) ) ;
2020-07-10 22:15:05 +00:00
2019-07-27 17:53:31 +00:00
const ScriptAction * linkscript = static_cast < const ScriptAction * > ( action ) ;
2019-08-22 22:16:56 +00:00
d - > executeScriptEvent ( event , linkscript ) ;
2022-02-04 15:52:29 +00:00
if ( event - > returnCode ( ) ) {
2022-03-02 18:00:52 +00:00
fft - > setText ( newValue . toString ( ) ) ;
2022-02-04 15:52:29 +00:00
} else {
2022-03-18 21:35:45 +00:00
Q_EMIT refreshFormWidget ( fft ) ;
2022-02-04 15:52:29 +00:00
}
2019-07-27 17:53:31 +00:00
}
2022-01-21 17:59:20 +00:00
void Document : : processKeystrokeCommitAction ( const Action * action , Okular : : FormFieldText * fft )
{
if ( action - > actionType ( ) ! = Action : : Script ) {
qCDebug ( OkularCoreDebug ) < < " Unsupported action type " < < action - > actionType ( ) < < " for keystroke. " ;
return ;
}
// Lookup the page of the FormFieldText
int foundPage = d - > findFieldPageNumber ( fft ) ;
if ( foundPage = = - 1 ) {
qCDebug ( OkularCoreDebug ) < < " Could not find page for formfield! " ;
return ;
}
std : : shared_ptr < Event > event = Event : : createKeystrokeEvent ( fft , d - > m_pagesVector [ foundPage ] ) ;
event - > setWillCommit ( true ) ;
const ScriptAction * linkscript = static_cast < const ScriptAction * > ( action ) ;
d - > executeScriptEvent ( event , linkscript ) ;
if ( event - > returnCode ( ) ) {
fft - > setText ( event - > value ( ) . toString ( ) ) ;
// TODO commit value
} else {
// TODO reset to committed value
}
}
2019-07-30 20:25:35 +00:00
void Document : : processFocusAction ( const Action * action , Okular : : FormField * field )
{
if ( ! action | | action - > actionType ( ) ! = Action : : Script ) {
return ;
2022-03-08 10:10:43 +00:00
}
2019-07-30 20:25:35 +00:00
2019-08-22 22:16:56 +00:00
// Lookup the page of the FormFieldText
int foundPage = d - > findFieldPageNumber ( field ) ;
2019-07-30 20:25:35 +00:00
if ( foundPage = = - 1 ) {
qCDebug ( OkularCoreDebug ) < < " Could not find page for formfield! " ;
return ;
}
std : : shared_ptr < Event > event = Event : : createFormFocusEvent ( field , d - > m_pagesVector [ foundPage ] ) ;
const ScriptAction * linkscript = static_cast < const ScriptAction * > ( action ) ;
2019-08-22 22:16:56 +00:00
d - > executeScriptEvent ( event , linkscript ) ;
2019-07-30 20:25:35 +00:00
}
2019-08-16 23:15:47 +00:00
void Document : : processValidateAction ( const Action * action , Okular : : FormFieldText * fft , bool & returnCode )
{
if ( ! action | | action - > actionType ( ) ! = Action : : Script ) {
return ;
2022-03-08 10:10:43 +00:00
}
2019-08-16 23:15:47 +00:00
// Lookup the page of the FormFieldText
2019-08-22 22:16:56 +00:00
int foundPage = d - > findFieldPageNumber ( fft ) ;
2019-08-16 23:15:47 +00:00
if ( foundPage = = - 1 ) {
qCDebug ( OkularCoreDebug ) < < " Could not find page for formfield! " ;
return ;
}
std : : shared_ptr < Event > event = Event : : createFormValidateEvent ( fft , d - > m_pagesVector [ foundPage ] ) ;
2020-07-10 22:15:05 +00:00
2019-08-16 23:15:47 +00:00
const ScriptAction * linkscript = static_cast < const ScriptAction * > ( action ) ;
2019-08-22 22:16:56 +00:00
d - > executeScriptEvent ( event , linkscript ) ;
2019-08-16 23:15:47 +00:00
returnCode = event - > returnCode ( ) ;
}
2023-08-01 23:25:25 +00:00
void Document : : processFormMouseUpScripAction ( const Action * action , Okular : : FormField * ff )
{
if ( ! action | | action - > actionType ( ) ! = Action : : Script ) {
return ;
}
// Lookup the page of the FormFieldText
int foundPage = d - > findFieldPageNumber ( ff ) ;
if ( foundPage = = - 1 ) {
qCDebug ( OkularCoreDebug ) < < " Could not find page for formfield! " ;
return ;
}
std : : shared_ptr < Event > event = Event : : createFieldMouseUpEvent ( ff , d - > m_pagesVector [ foundPage ] ) ;
const ScriptAction * linkscript = static_cast < const ScriptAction * > ( action ) ;
d - > executeScriptEvent ( event , linkscript ) ;
}
2007-01-02 19:05:49 +00:00
void Document : : processSourceReference ( const SourceReference * ref )
2005-01-03 00:28:46 +00:00
{
2007-01-02 19:05:49 +00:00
if ( ! ref ) {
2005-01-28 17:21:51 +00:00
return ;
2022-03-08 10:10:43 +00:00
}
2005-01-28 17:21:51 +00:00
2016-01-30 19:00:25 +00:00
const QUrl url = d - > giveAbsoluteUrl ( ref - > fileName ( ) ) ;
2009-03-23 01:22:17 +00:00
if ( ! url . isLocalFile ( ) ) {
2014-09-11 17:36:01 +00:00
qCDebug ( OkularCoreDebug ) < < url . url ( ) < < " is not a local file. " ;
2009-03-23 01:22:17 +00:00
return ;
}
const QString absFileName = url . toLocalFile ( ) ;
if ( ! QFile : : exists ( absFileName ) ) {
2014-09-11 17:36:01 +00:00
qCDebug ( OkularCoreDebug ) < < " No such file: " < < absFileName ;
2005-01-28 17:21:51 +00:00
return ;
}
2005-01-03 00:28:46 +00:00
2011-09-10 17:15:52 +00:00
bool handled = false ;
2022-03-18 21:35:45 +00:00
Q_EMIT sourceReferenceActivated ( absFileName , ref - > row ( ) , ref - > column ( ) , & handled ) ;
2011-09-10 17:15:52 +00:00
if ( handled ) {
return ;
}
2007-01-02 19:05:49 +00:00
static QHash < int , QString > editors ;
// init the editors table if empty (on first run, usually)
if ( editors . isEmpty ( ) ) {
2008-10-19 18:01:16 +00:00
editors = buildEditorsMap ( ) ;
2007-01-02 19:05:49 +00:00
}
2005-03-13 13:14:44 +00:00
2022-02-18 13:33:28 +00:00
// prefer the editor from the command line
QString p = d - > editorCommandOverride ;
if ( p . isEmpty ( ) ) {
QHash < int , QString > : : const_iterator it = editors . constFind ( SettingsCore : : externalEditor ( ) ) ;
if ( it ! = editors . constEnd ( ) ) {
p = * it ;
} else {
p = SettingsCore : : externalEditorCommand ( ) ;
2022-03-08 10:10:43 +00:00
}
2022-02-18 13:33:28 +00:00
}
2007-01-02 19:05:49 +00:00
// custom editor not yet configured
if ( p . isEmpty ( ) ) {
return ;
2022-03-08 10:10:43 +00:00
}
2005-01-28 17:21:51 +00:00
2008-10-19 19:40:17 +00:00
// manually append the %f placeholder if not specified
if ( p . indexOf ( QLatin1String ( " %f " ) ) = = - 1 ) {
p . append ( QLatin1String ( " %f " ) ) ;
2022-03-08 10:10:43 +00:00
}
2005-01-28 17:21:51 +00:00
2008-10-19 19:40:17 +00:00
// replacing the placeholders
QHash < QChar , QString > map ;
2016-07-11 20:07:57 +00:00
map . insert ( QLatin1Char ( ' f ' ) , absFileName ) ;
map . insert ( QLatin1Char ( ' c ' ) , QString : : number ( ref - > column ( ) ) ) ;
map . insert ( QLatin1Char ( ' l ' ) , QString : : number ( ref - > row ( ) ) ) ;
2008-10-19 19:40:17 +00:00
const QString cmd = KMacroExpander : : expandMacrosShellQuote ( p , map ) ;
if ( cmd . isEmpty ( ) ) {
return ;
2022-03-08 10:10:43 +00:00
}
2022-01-23 18:36:31 +00:00
QStringList args = KShell : : splitArgs ( cmd ) ;
2008-10-19 19:40:17 +00:00
if ( args . isEmpty ( ) ) {
2007-01-02 19:05:49 +00:00
return ;
2022-03-08 10:10:43 +00:00
}
2005-03-13 13:14:44 +00:00
2022-01-23 18:36:31 +00:00
const QString prog = args . takeFirst ( ) ;
// Make sure prog is in PATH and not just in the CWD
const QString progFullPath = QStandardPaths : : findExecutable ( prog ) ;
if ( progFullPath . isEmpty ( ) ) {
return ;
}
KProcess : : startDetached ( progFullPath , args ) ;
2005-01-03 00:28:46 +00:00
}
2009-07-27 14:15:33 +00:00
const SourceReference * Document : : dynamicSourceReference ( int pageNr , double absX , double absY )
{
2014-10-08 22:53:54 +00:00
if ( ! d - > m_synctex_scanner ) {
2017-09-05 21:27:18 +00:00
return nullptr ;
2022-03-08 10:10:43 +00:00
}
2014-10-08 22:17:53 +00:00
const QSizeF dpi = d - > m_generator - > dpi ( ) ;
2014-10-08 22:53:54 +00:00
if ( synctex_edit_query ( d - > m_synctex_scanner , pageNr + 1 , absX * 72. / dpi . width ( ) , absY * 72. / dpi . height ( ) ) > 0 ) {
2017-08-28 22:29:46 +00:00
synctex_node_p node ;
2014-10-08 22:17:53 +00:00
// TODO what should we do if there is really more than one node?
2017-08-28 22:29:46 +00:00
while ( ( node = synctex_scanner_next_result ( d - > m_synctex_scanner ) ) ) {
2014-10-08 22:17:53 +00:00
int line = synctex_node_line ( node ) ;
int col = synctex_node_column ( node ) ;
// column extraction does not seem to be implemented in synctex so far. set the SourceReference default value.
if ( col = = - 1 ) {
col = 0 ;
}
2014-10-08 22:53:54 +00:00
const char * name = synctex_scanner_get_name ( d - > m_synctex_scanner , synctex_node_tag ( node ) ) ;
2014-10-08 22:17:53 +00:00
return new Okular : : SourceReference ( QFile : : decodeName ( name ) , line , col ) ;
}
2009-07-27 14:15:33 +00:00
}
2017-09-05 21:27:18 +00:00
return nullptr ;
2009-07-27 14:15:33 +00:00
}
2007-11-26 21:43:54 +00:00
Document : : PrintingType Document : : printingSupport ( ) const
{
if ( d - > m_generator ) {
if ( d - > m_generator - > hasFeature ( Generator : : PrintNative ) ) {
return NativePrinting ;
}
# ifndef Q_OS_WIN
if ( d - > m_generator - > hasFeature ( Generator : : PrintPostscript ) ) {
return PostscriptPrinting ;
}
# endif
}
return NoPrinting ;
}
bool Document : : supportsPrintToFile ( ) const
{
return d - > m_generator ? d - > m_generator - > hasFeature ( Generator : : PrintToFile ) : false ;
}
2021-12-14 22:52:30 +00:00
Document : : PrintError Document : : print ( QPrinter & printer )
2004-10-17 18:40:02 +00:00
{
2021-12-14 22:52:30 +00:00
return d - > m_generator ? d - > m_generator - > print ( printer ) : Document : : UnknownPrintError ;
2004-10-17 18:40:02 +00:00
}
2021-12-14 22:52:30 +00:00
QString Document : : printErrorString ( PrintError error )
2010-04-14 23:07:27 +00:00
{
2021-12-14 22:52:30 +00:00
switch ( error ) {
case TemporaryFileOpenPrintError :
2010-04-14 23:07:27 +00:00
return i18n ( " Could not open a temporary file " ) ;
2021-12-14 22:52:30 +00:00
case FileConversionPrintError :
2010-04-14 23:07:27 +00:00
return i18n ( " Print conversion failed " ) ;
2021-12-14 22:52:30 +00:00
case PrintingProcessCrashPrintError :
2010-04-14 23:07:27 +00:00
return i18n ( " Printing process crashed " ) ;
2021-12-14 22:52:30 +00:00
case PrintingProcessStartPrintError :
2010-04-14 23:07:27 +00:00
return i18n ( " Printing process could not start " ) ;
2021-12-14 22:52:30 +00:00
case PrintToFilePrintError :
2010-04-14 23:07:27 +00:00
return i18n ( " Printing to file failed " ) ;
2021-12-14 22:52:30 +00:00
case InvalidPrinterStatePrintError :
2010-04-14 23:07:27 +00:00
return i18n ( " Printer was in invalid state " ) ;
2021-12-14 22:52:30 +00:00
case UnableToFindFilePrintError :
2010-04-14 23:07:27 +00:00
return i18n ( " Unable to find file to print " ) ;
2021-12-14 22:52:30 +00:00
case NoFileToPrintError :
2010-04-14 23:07:27 +00:00
return i18n ( " There was no file to print " ) ;
2021-12-14 22:52:30 +00:00
case NoBinaryToPrintError :
2010-07-11 15:32:43 +00:00
return i18n ( " Could not find a suitable binary for printing. Make sure CUPS lpr binary is available " ) ;
2021-12-14 22:52:30 +00:00
case InvalidPageSizePrintError :
2014-01-12 18:20:54 +00:00
return i18n ( " The page print size is invalid " ) ;
2021-12-14 22:52:30 +00:00
case NoPrintError :
2010-04-14 23:07:27 +00:00
return QString ( ) ;
2021-12-14 22:52:30 +00:00
case UnknownPrintError :
2010-04-14 23:07:27 +00:00
return QString ( ) ;
}
2013-04-05 22:22:48 +00:00
2010-04-14 23:07:27 +00:00
return QString ( ) ;
}
2007-10-15 23:01:27 +00:00
QWidget * Document : : printConfigurationWidget ( ) const
2004-10-06 00:05:49 +00:00
{
2007-01-02 19:05:49 +00:00
if ( d - > m_generator ) {
PrintInterface * iface = qobject_cast < Okular : : PrintInterface * > ( d - > m_generator ) ;
2017-09-05 21:27:18 +00:00
return iface ? iface - > printConfigurationWidget ( ) : nullptr ;
2007-01-02 19:05:49 +00:00
} else {
2017-09-05 21:27:18 +00:00
return nullptr ;
2022-03-08 10:10:43 +00:00
}
2004-10-06 00:05:49 +00:00
}
2007-01-28 15:46:10 +00:00
void Document : : fillConfigDialog ( KConfigDialog * dialog )
{
if ( ! dialog ) {
return ;
2022-03-08 10:10:43 +00:00
}
2007-01-28 15:46:10 +00:00
2018-10-08 20:53:51 +00:00
// We know it's a BackendConfigDialog, but check anyway
BackendConfigDialog * bcd = dynamic_cast < BackendConfigDialog * > ( dialog ) ;
if ( ! bcd ) {
return ;
2022-03-08 10:10:43 +00:00
}
2018-10-08 20:53:51 +00:00
2007-01-28 16:10:12 +00:00
// ensure that we have all the generators with settings loaded
2015-11-28 19:24:41 +00:00
QVector < KPluginMetaData > offers = DocumentPrivate : : configurableGenerators ( ) ;
2007-01-28 16:10:12 +00:00
d - > loadServiceList ( offers ) ;
2007-01-28 15:46:10 +00:00
2018-08-14 08:37:30 +00:00
// We want the generators to be sorted by name so let's fill in a QMap
// this sorts by internal id which is not awesome, but at least the sorting
// is stable between runs that before it wasn't
QMap < QString , GeneratorInfo > sortedGenerators ;
2007-01-28 15:46:10 +00:00
QHash < QString , GeneratorInfo > : : iterator it = d - > m_loadedGenerators . begin ( ) ;
QHash < QString , GeneratorInfo > : : iterator itEnd = d - > m_loadedGenerators . end ( ) ;
for ( ; it ! = itEnd ; + + it ) {
2018-08-14 08:37:30 +00:00
sortedGenerators . insert ( it . key ( ) , it . value ( ) ) ;
}
bool pagesAdded = false ;
QMap < QString , GeneratorInfo > : : iterator sit = sortedGenerators . begin ( ) ;
QMap < QString , GeneratorInfo > : : iterator sitEnd = sortedGenerators . end ( ) ;
for ( ; sit ! = sitEnd ; + + sit ) {
Okular : : ConfigInterface * iface = d - > generatorConfig ( sit . value ( ) ) ;
2007-01-28 15:46:10 +00:00
if ( iface ) {
iface - > addPages ( dialog ) ;
2007-07-12 20:04:56 +00:00
pagesAdded = true ;
2018-10-08 20:53:51 +00:00
if ( sit . value ( ) . generator = = d - > m_generator ) {
const int rowCount = bcd - > thePageWidget ( ) - > model ( ) - > rowCount ( ) ;
KPageView * view = bcd - > thePageWidget ( ) ;
view - > setCurrentPage ( view - > model ( ) - > index ( rowCount - 1 , 0 ) ) ;
}
2007-07-12 17:52:14 +00:00
}
2007-01-28 15:46:10 +00:00
}
2007-07-12 20:04:56 +00:00
if ( pagesAdded ) {
2019-12-19 22:18:31 +00:00
connect ( dialog , & KConfigDialog : : settingsChanged , this , [ this ] { d - > slotGeneratorConfigChanged ( ) ; } ) ;
2007-07-12 20:04:56 +00:00
}
2007-01-28 15:46:10 +00:00
}
2015-11-28 19:24:41 +00:00
QVector < KPluginMetaData > DocumentPrivate : : configurableGenerators ( )
2007-01-28 15:46:10 +00:00
{
2015-11-28 19:24:41 +00:00
const QVector < KPluginMetaData > available = availableGenerators ( ) ;
QVector < KPluginMetaData > result ;
for ( const KPluginMetaData & md : available ) {
if ( md . rawData ( ) [ QStringLiteral ( " X-KDE-okularHasInternalSettings " ) ] . toBool ( ) ) {
result < < md ;
}
2007-01-28 15:46:10 +00:00
}
2015-11-28 19:24:41 +00:00
return result ;
2007-01-28 15:46:10 +00:00
}
2015-11-28 19:24:41 +00:00
KPluginMetaData Document : : generatorInfo ( ) const
2007-03-07 18:15:00 +00:00
{
2015-11-28 19:24:41 +00:00
if ( ! d - > m_generator ) {
return KPluginMetaData ( ) ;
2022-03-08 10:10:43 +00:00
}
2007-11-24 18:07:59 +00:00
2015-11-28 19:24:41 +00:00
auto genIt = d - > m_loadedGenerators . constFind ( d - > m_generatorName ) ;
Q_ASSERT ( genIt ! = d - > m_loadedGenerators . constEnd ( ) ) ;
return genIt . value ( ) . metadata ;
}
2007-11-24 18:07:59 +00:00
2015-11-28 19:24:41 +00:00
int Document : : configurableGenerators ( ) const
{
return DocumentPrivate : : configurableGenerators ( ) . size ( ) ;
}
2007-11-24 18:07:59 +00:00
2015-11-28 19:24:41 +00:00
QStringList Document : : supportedMimeTypes ( ) const
{
// TODO: make it a static member of DocumentPrivate?
QStringList result = d - > m_supportedMimeTypes ;
if ( result . isEmpty ( ) ) {
const QVector < KPluginMetaData > available = DocumentPrivate : : availableGenerators ( ) ;
for ( const KPluginMetaData & md : available ) {
result < < md . mimeTypes ( ) ;
}
2016-07-12 23:57:26 +00:00
// Remove duplicate mimetypes represented by different names
QMimeDatabase mimeDatabase ;
QSet < QMimeType > uniqueMimetypes ;
2020-02-19 16:28:08 +00:00
for ( const QString & mimeName : qAsConst ( result ) ) {
2016-07-12 23:57:26 +00:00
uniqueMimetypes . insert ( mimeDatabase . mimeTypeForName ( mimeName ) ) ;
}
result . clear ( ) ;
for ( const QMimeType & mimeType : uniqueMimetypes ) {
result . append ( mimeType . name ( ) ) ;
}
2017-11-13 16:30:10 +00:00
// Add the Okular archive mimetype
result < < QStringLiteral ( " application/vnd.kde.okular-archive " ) ;
2016-07-12 23:57:26 +00:00
// Sorting by mimetype name doesn't make a ton of sense,
// but ensures that the list is ordered the same way every time
2019-03-12 12:13:53 +00:00
std : : sort ( result . begin ( ) , result . end ( ) ) ;
2016-07-12 23:57:26 +00:00
2015-11-28 19:24:41 +00:00
d - > m_supportedMimeTypes = result ;
}
return result ;
2007-03-07 18:15:00 +00:00
}
2014-05-09 22:53:39 +00:00
bool Document : : canSwapBackingFile ( ) const
{
if ( ! d - > m_generator ) {
return false ;
2022-03-08 10:10:43 +00:00
}
2014-05-09 22:53:39 +00:00
2017-12-14 13:33:18 +00:00
return d - > m_generator - > hasFeature ( Generator : : SwapBackingFile ) ;
2014-05-09 22:53:39 +00:00
}
2017-11-06 05:12:03 +00:00
bool Document : : swapBackingFile ( const QString & newFileName , const QUrl & url )
2014-05-09 22:53:39 +00:00
{
if ( ! d - > m_generator ) {
return false ;
2022-03-08 10:10:43 +00:00
}
2014-05-09 22:53:39 +00:00
2017-12-14 13:33:18 +00:00
if ( ! d - > m_generator - > hasFeature ( Generator : : SwapBackingFile ) ) {
2014-05-09 22:53:39 +00:00
return false ;
2022-03-08 10:10:43 +00:00
}
2014-05-09 22:53:39 +00:00
2014-05-11 14:36:28 +00:00
// Save metadata about the file we're about to close
d - > saveDocumentInfo ( ) ;
2018-02-14 16:48:10 +00:00
d - > clearAndWaitForRequests ( ) ;
2017-09-10 09:51:56 +00:00
qCDebug ( OkularCoreDebug ) < < " Swapping backing file to " < < newFileName ;
2017-10-26 07:47:18 +00:00
QVector < Page * > newPagesVector ;
2017-12-14 13:33:18 +00:00
Generator : : SwapBackingFileResult result = d - > m_generator - > swapBackingFile ( newFileName , newPagesVector ) ;
2017-10-26 07:47:18 +00:00
if ( result ! = Generator : : SwapBackingFileError ) {
2022-03-31 14:16:40 +00:00
QList < ObjectRect * > rectsToDelete ;
QList < Annotation * > annotationsToDelete ;
2017-10-26 07:47:18 +00:00
QSet < PagePrivate * > pagePrivatesToDelete ;
2020-07-10 22:15:05 +00:00
2017-10-26 07:47:18 +00:00
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 ;
2022-03-08 10:10:43 +00:00
}
2017-10-26 07:47:18 +00:00
// 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 ) ) ;
2017-11-14 08:57:38 +00:00
if ( OkularUndoCommand * ouc = dynamic_cast < OkularUndoCommand * > ( uc ) ) {
const bool success = ouc - > refreshInternalPageReferences ( newPagesVector ) ;
if ( ! success ) {
qWarning ( ) < < " Document::swapBackingFile: refreshInternalPageReferences failed " < < ouc ;
return false ;
}
2017-10-26 07:47:18 +00:00
} else {
2017-11-14 08:57:38 +00:00
qWarning ( ) < < " Document::swapBackingFile: Unhandled undo command " < < uc ;
return false ;
2017-10-26 07:47:18 +00:00
}
}
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 ) ;
}
2014-05-09 22:53:39 +00:00
d - > m_url = url ;
2014-05-11 14:36:28 +00:00
d - > m_docFileName = newFileName ;
d - > updateMetadataXmlNameAndDocSize ( ) ;
d - > m_bookmarkManager - > setUrl ( d - > m_url ) ;
2018-08-08 22:27:42 +00:00
d - > m_documentInfo = DocumentInfo ( ) ;
d - > m_documentInfoAskedKeys . clear ( ) ;
2017-10-26 07:47:18 +00:00
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 ) ;
}
}
2014-05-11 15:38:30 +00:00
foreachObserver ( notifySetup ( d - > m_pagesVector , DocumentObserver : : UrlChanged ) ) ;
2017-10-26 07:47:18 +00:00
qDeleteAll ( annotationsToDelete ) ;
qDeleteAll ( rectsToDelete ) ;
qDeleteAll ( pagePrivatesToDelete ) ;
2014-05-09 22:53:39 +00:00
return true ;
} else {
return false ;
}
}
2017-11-06 05:12:03 +00:00
bool Document : : swapBackingFileArchive ( const QString & newFileName , const QUrl & url )
2014-05-09 22:53:39 +00:00
{
2017-09-10 09:51:56 +00:00
qCDebug ( OkularCoreDebug ) < < " Swapping backing archive to " < < newFileName ;
2014-05-09 22:53:39 +00:00
ArchiveData * newArchive = DocumentPrivate : : unpackDocumentArchive ( newFileName ) ;
if ( ! newArchive ) {
return false ;
2022-03-08 10:10:43 +00:00
}
2014-05-09 22:53:39 +00:00
const QString tempFileName = newArchive - > document . fileName ( ) ;
2017-11-14 08:49:32 +00:00
const bool success = swapBackingFile ( tempFileName , url ) ;
2017-10-26 07:47:18 +00:00
2017-11-14 08:49:32 +00:00
if ( success ) {
2014-05-09 22:53:39 +00:00
delete d - > m_archiveData ;
d - > m_archiveData = newArchive ;
}
2017-11-14 08:49:32 +00:00
return success ;
2014-05-09 22:53:39 +00:00
}
2017-11-14 13:52:02 +00:00
void Document : : setHistoryClean ( bool clean )
{
if ( clean ) {
d - > m_undoStack - > setClean ( ) ;
} else {
d - > m_undoStack - > resetClean ( ) ;
2022-03-08 10:10:43 +00:00
}
2017-11-14 13:52:02 +00:00
}
2020-11-26 12:19:52 +00:00
bool Document : : isHistoryClean ( ) const
{
return d - > m_undoStack - > isClean ( ) ;
}
2008-03-11 23:40:59 +00:00
bool Document : : canSaveChanges ( ) const
{
if ( ! d - > m_generator ) {
return false ;
2022-03-08 10:10:43 +00:00
}
2008-03-11 23:40:59 +00:00
Q_ASSERT ( ! d - > m_generatorName . isEmpty ( ) ) ;
QHash < QString , GeneratorInfo > : : iterator genIt = d - > m_loadedGenerators . find ( d - > m_generatorName ) ;
Q_ASSERT ( genIt ! = d - > m_loadedGenerators . end ( ) ) ;
SaveInterface * saveIface = d - > generatorSave ( genIt . value ( ) ) ;
if ( ! saveIface ) {
return false ;
2022-03-08 10:10:43 +00:00
}
2008-03-11 23:40:59 +00:00
return saveIface - > supportsOption ( SaveInterface : : SaveChanges ) ;
}
2012-06-13 13:33:49 +00:00
bool Document : : canSaveChanges ( SaveCapability cap ) const
{
switch ( cap ) {
2012-06-13 22:27:22 +00:00
case SaveFormsCapability :
2012-06-13 13:33:49 +00:00
/* Assume that if the generator supports saving, forms can be saved.
* We have no means to actually query the generator at the moment
* TODO : Add some method to query the generator in SaveInterface */
return canSaveChanges ( ) ;
2012-06-13 22:27:22 +00:00
case SaveAnnotationsCapability :
2012-06-13 13:33:49 +00:00
return d - > canAddAnnotationsNatively ( ) ;
}
return false ;
}
2008-03-11 23:40:59 +00:00
bool Document : : saveChanges ( const QString & fileName )
2009-10-19 23:00:52 +00:00
{
QString errorText ;
return saveChanges ( fileName , & errorText ) ;
}
bool Document : : saveChanges ( const QString & fileName , QString * errorText )
2008-03-11 23:40:59 +00:00
{
if ( ! d - > m_generator | | fileName . isEmpty ( ) ) {
return false ;
2022-03-08 10:10:43 +00:00
}
2008-03-11 23:40:59 +00:00
Q_ASSERT ( ! d - > m_generatorName . isEmpty ( ) ) ;
QHash < QString , GeneratorInfo > : : iterator genIt = d - > m_loadedGenerators . find ( d - > m_generatorName ) ;
Q_ASSERT ( genIt ! = d - > m_loadedGenerators . end ( ) ) ;
SaveInterface * saveIface = d - > generatorSave ( genIt . value ( ) ) ;
if ( ! saveIface | | ! saveIface - > supportsOption ( SaveInterface : : SaveChanges ) ) {
return false ;
2022-03-08 10:10:43 +00:00
}
2008-03-11 23:40:59 +00:00
2009-10-19 23:00:52 +00:00
return saveIface - > save ( fileName , SaveInterface : : SaveChanges , errorText ) ;
2008-03-11 23:40:59 +00:00
}
2008-04-27 11:05:59 +00:00
void Document : : registerView ( View * view )
{
if ( ! view ) {
return ;
2022-03-08 10:10:43 +00:00
}
2008-04-27 11:05:59 +00:00
Document * viewDoc = view - > viewDocument ( ) ;
if ( viewDoc ) {
// check if already registered for this document
if ( viewDoc = = this ) {
return ;
2022-03-08 10:10:43 +00:00
}
2008-04-27 11:05:59 +00:00
viewDoc - > unregisterView ( view ) ;
}
d - > m_views . insert ( view ) ;
view - > d_func ( ) - > document = d ;
}
void Document : : unregisterView ( View * view )
{
if ( ! view ) {
return ;
2022-03-08 10:10:43 +00:00
}
2008-04-27 11:05:59 +00:00
Document * viewDoc = view - > viewDocument ( ) ;
if ( ! viewDoc | | viewDoc ! = this ) {
return ;
2022-03-08 10:10:43 +00:00
}
2008-04-27 11:05:59 +00:00
2017-09-05 21:27:18 +00:00
view - > d_func ( ) - > document = nullptr ;
2008-04-27 11:05:59 +00:00
d - > m_views . remove ( view ) ;
}
2008-08-01 20:26:22 +00:00
QByteArray Document : : fontData ( const FontInfo & font ) const
{
if ( d - > m_generator ) {
2021-12-14 22:52:30 +00:00
return d - > m_generator - > requestFontData ( font ) ;
2008-08-01 20:26:22 +00:00
}
2013-04-05 22:22:48 +00:00
2021-12-14 22:52:30 +00:00
return { } ;
2008-08-01 20:26:22 +00:00
}
2014-05-10 10:32:12 +00:00
ArchiveData * DocumentPrivate : : unpackDocumentArchive ( const QString & archivePath )
2008-11-15 14:15:31 +00:00
{
2014-09-11 13:18:05 +00:00
QMimeDatabase db ;
2017-09-10 09:51:56 +00:00
const QMimeType mime = db . mimeTypeForFile ( archivePath , QMimeDatabase : : MatchExtension ) ;
2015-10-29 12:37:11 +00:00
if ( ! mime . inherits ( QStringLiteral ( " application/vnd.kde.okular-archive " ) ) ) {
2017-09-10 09:51:56 +00:00
return nullptr ;
2022-03-08 10:10:43 +00:00
}
2008-11-15 14:15:31 +00:00
2014-05-10 10:32:12 +00:00
KZip okularArchive ( archivePath ) ;
2008-11-15 14:15:31 +00:00
if ( ! okularArchive . open ( QIODevice : : ReadOnly ) ) {
2017-09-10 09:51:56 +00:00
return nullptr ;
2022-03-08 10:10:43 +00:00
}
2008-11-15 14:15:31 +00:00
const KArchiveDirectory * mainDir = okularArchive . directory ( ) ;
2018-09-03 19:14:30 +00:00
// Check the archive doesn't have folders, we don't create them when saving the archive
// and folders mean paths and paths mean path traversal issues
2020-02-19 16:28:08 +00:00
const QStringList mainDirEntries = mainDir - > entries ( ) ;
for ( const QString & entry : mainDirEntries ) {
2018-09-03 19:14:30 +00:00
if ( mainDir - > entry ( entry ) - > isDirectory ( ) ) {
qWarning ( ) < < " Warning: Found a directory inside " < < archivePath < < " - Okular does not create files like that so it is most probably forged. " ;
return nullptr ;
}
}
2015-10-29 12:37:11 +00:00
const KArchiveEntry * mainEntry = mainDir - > entry ( QStringLiteral ( " content.xml " ) ) ;
2008-11-15 14:15:31 +00:00
if ( ! mainEntry | | ! mainEntry - > isFile ( ) ) {
2017-09-10 09:51:56 +00:00
return nullptr ;
2022-03-08 10:10:43 +00:00
}
2008-11-15 14:15:31 +00:00
2015-04-09 01:37:24 +00:00
std : : unique_ptr < QIODevice > mainEntryDevice ( static_cast < const KZipFileEntry * > ( mainEntry ) - > createDevice ( ) ) ;
2008-11-15 14:15:31 +00:00
QDomDocument doc ;
if ( ! doc . setContent ( mainEntryDevice . get ( ) ) ) {
2017-09-10 09:51:56 +00:00
return nullptr ;
2022-03-08 10:10:43 +00:00
}
2008-11-15 14:15:31 +00:00
mainEntryDevice . reset ( ) ;
QDomElement root = doc . documentElement ( ) ;
2017-09-10 09:51:56 +00:00
if ( root . tagName ( ) ! = QLatin1String ( " OkularArchive " ) ) {
return nullptr ;
2022-03-08 10:10:43 +00:00
}
2008-11-15 14:15:31 +00:00
QString documentFileName ;
QString metadataFileName ;
QDomElement el = root . firstChild ( ) . toElement ( ) ;
for ( ; ! el . isNull ( ) ; el = el . nextSibling ( ) . toElement ( ) ) {
2015-10-29 12:37:11 +00:00
if ( el . tagName ( ) = = QLatin1String ( " Files " ) ) {
2008-11-15 14:15:31 +00:00
QDomElement fileEl = el . firstChild ( ) . toElement ( ) ;
for ( ; ! fileEl . isNull ( ) ; fileEl = fileEl . nextSibling ( ) . toElement ( ) ) {
2015-10-29 12:37:11 +00:00
if ( fileEl . tagName ( ) = = QLatin1String ( " DocumentFileName " ) ) {
2008-11-15 14:15:31 +00:00
documentFileName = fileEl . text ( ) ;
2015-10-29 12:37:11 +00:00
} else if ( fileEl . tagName ( ) = = QLatin1String ( " MetadataFileName " ) ) {
2008-11-15 14:15:31 +00:00
metadataFileName = fileEl . text ( ) ;
2022-03-08 10:10:43 +00:00
}
2008-11-15 14:15:31 +00:00
}
}
}
if ( documentFileName . isEmpty ( ) ) {
2017-09-10 09:51:56 +00:00
return nullptr ;
2022-03-08 10:10:43 +00:00
}
2008-11-15 14:15:31 +00:00
const KArchiveEntry * docEntry = mainDir - > entry ( documentFileName ) ;
if ( ! docEntry | | ! docEntry - > isFile ( ) ) {
2017-09-10 09:51:56 +00:00
return nullptr ;
2022-03-08 10:10:43 +00:00
}
2008-11-15 14:15:31 +00:00
2015-04-09 01:37:24 +00:00
std : : unique_ptr < ArchiveData > archiveData ( new ArchiveData ( ) ) ;
2016-07-11 20:07:57 +00:00
const int dotPos = documentFileName . indexOf ( QLatin1Char ( ' . ' ) ) ;
2008-11-15 14:15:31 +00:00
if ( dotPos ! = - 1 ) {
2014-09-17 22:30:39 +00:00
archiveData - > document . setFileTemplate ( QDir : : tempPath ( ) + QLatin1String ( " /okular_XXXXXX " ) + documentFileName . mid ( dotPos ) ) ;
2022-03-08 10:10:43 +00:00
}
2008-11-15 14:15:31 +00:00
if ( ! archiveData - > document . open ( ) ) {
2017-09-10 09:51:56 +00:00
return nullptr ;
2022-03-08 10:10:43 +00:00
}
2014-05-10 10:32:12 +00:00
archiveData - > originalFileName = documentFileName ;
2008-11-15 14:15:31 +00:00
{
2015-04-09 01:37:24 +00:00
std : : unique_ptr < QIODevice > docEntryDevice ( static_cast < const KZipFileEntry * > ( docEntry ) - > createDevice ( ) ) ;
2008-11-15 14:15:31 +00:00
copyQIODevice ( docEntryDevice . get ( ) , & archiveData - > document ) ;
archiveData - > document . close ( ) ;
}
const KArchiveEntry * metadataEntry = mainDir - > entry ( metadataFileName ) ;
if ( metadataEntry & & metadataEntry - > isFile ( ) ) {
2015-04-09 01:37:24 +00:00
std : : unique_ptr < QIODevice > metadataEntryDevice ( static_cast < const KZipFileEntry * > ( metadataEntry ) - > createDevice ( ) ) ;
2014-09-17 22:30:39 +00:00
archiveData - > metadataFile . setFileTemplate ( QDir : : tempPath ( ) + QLatin1String ( " /okular_XXXXXX.xml " ) ) ;
2014-05-10 09:35:33 +00:00
if ( archiveData - > metadataFile . open ( ) ) {
copyQIODevice ( metadataEntryDevice . get ( ) , & archiveData - > metadataFile ) ;
archiveData - > metadataFile . close ( ) ;
2008-11-15 14:15:31 +00:00
}
}
2014-05-10 10:32:12 +00:00
return archiveData . release ( ) ;
}
2017-09-10 09:51:56 +00:00
Document : : OpenResult Document : : openDocumentArchive ( const QString & docFile , const QUrl & url , const QString & password )
2014-05-10 10:32:12 +00:00
{
d - > m_archiveData = DocumentPrivate : : unpackDocumentArchive ( docFile ) ;
if ( ! d - > m_archiveData ) {
return OpenError ;
2022-03-08 10:10:43 +00:00
}
2014-05-10 10:32:12 +00:00
const QString tempFileName = d - > m_archiveData - > document . fileName ( ) ;
2017-09-10 09:51:56 +00:00
QMimeDatabase db ;
2018-10-02 19:29:48 +00:00
const QMimeType docMime = db . mimeTypeForFile ( tempFileName , QMimeDatabase : : MatchExtension ) ;
2014-05-09 17:56:16 +00:00
const OpenResult ret = openDocument ( tempFileName , url , docMime , password ) ;
2008-11-15 14:15:31 +00:00
2014-05-10 10:32:12 +00:00
if ( ret ! = OpenSuccess ) {
delete d - > m_archiveData ;
2017-09-05 21:27:18 +00:00
d - > m_archiveData = nullptr ;
2008-11-15 14:15:31 +00:00
}
return ret ;
}
bool Document : : saveDocumentArchive ( const QString & fileName )
{
if ( ! d - > m_generator ) {
return false ;
2022-03-08 10:10:43 +00:00
}
2008-11-15 14:15:31 +00:00
2012-06-07 15:05:00 +00:00
/* If we opened an archive, use the name of original file (eg foo.pdf)
* instead of the archive ' s one ( eg foo . okular ) */
2014-05-10 10:32:12 +00:00
QString docFileName = d - > m_archiveData ? d - > m_archiveData - > originalFileName : d - > m_url . fileName ( ) ;
2008-11-15 14:15:31 +00:00
if ( docFileName = = QLatin1String ( " - " ) ) {
return false ;
2022-03-08 10:10:43 +00:00
}
2008-11-15 14:15:31 +00:00
2010-08-10 23:49:30 +00:00
QString docPath = d - > m_docFileName ;
const QFileInfo fi ( docPath ) ;
if ( fi . isSymLink ( ) ) {
docPath = fi . symLinkTarget ( ) ;
2022-03-08 10:10:43 +00:00
}
2010-08-10 23:49:30 +00:00
2008-11-15 14:15:31 +00:00
KZip okularArchive ( fileName ) ;
if ( ! okularArchive . open ( QIODevice : : WriteOnly ) ) {
return false ;
2022-03-08 10:10:43 +00:00
}
2008-11-15 14:15:31 +00:00
const KUser user ;
2008-11-17 22:16:28 +00:00
# ifndef Q_OS_WIN
2015-01-29 20:48:47 +00:00
const KUserGroup userGroup ( user . groupId ( ) ) ;
2008-11-17 22:16:28 +00:00
# else
2022-04-08 08:52:26 +00:00
const KUserGroup userGroup ( QStringLiteral ( " " ) ) ;
2008-11-17 22:16:28 +00:00
# endif
2008-11-15 14:15:31 +00:00
2015-10-29 12:37:11 +00:00
QDomDocument contentDoc ( QStringLiteral ( " OkularArchive " ) ) ;
2008-11-15 14:15:31 +00:00
QDomProcessingInstruction xmlPi = contentDoc . createProcessingInstruction ( QStringLiteral ( " xml " ) , QStringLiteral ( " version= \" 1.0 \" encoding= \" utf-8 \" " ) ) ;
contentDoc . appendChild ( xmlPi ) ;
2015-10-29 12:37:11 +00:00
QDomElement root = contentDoc . createElement ( QStringLiteral ( " OkularArchive " ) ) ;
2008-11-15 14:15:31 +00:00
contentDoc . appendChild ( root ) ;
2015-10-29 12:37:11 +00:00
QDomElement filesNode = contentDoc . createElement ( QStringLiteral ( " Files " ) ) ;
2008-11-15 14:15:31 +00:00
root . appendChild ( filesNode ) ;
2015-10-29 12:37:11 +00:00
QDomElement fileNameNode = contentDoc . createElement ( QStringLiteral ( " DocumentFileName " ) ) ;
2008-11-15 14:15:31 +00:00
filesNode . appendChild ( fileNameNode ) ;
fileNameNode . appendChild ( contentDoc . createTextNode ( docFileName ) ) ;
2015-10-29 12:37:11 +00:00
QDomElement metadataFileNameNode = contentDoc . createElement ( QStringLiteral ( " MetadataFileName " ) ) ;
2008-11-15 14:15:31 +00:00
filesNode . appendChild ( metadataFileNameNode ) ;
2015-10-29 12:37:11 +00:00
metadataFileNameNode . appendChild ( contentDoc . createTextNode ( QStringLiteral ( " metadata.xml " ) ) ) ;
2008-11-15 14:15:31 +00:00
2012-05-14 13:51:58 +00:00
// If the generator can save annotations natively, do it
2014-09-17 22:30:39 +00:00
QTemporaryFile modifiedFile ;
2012-05-14 13:51:58 +00:00
bool annotationsSavedNatively = false ;
2014-05-11 13:22:41 +00:00
bool formsSavedNatively = false ;
if ( d - > canAddAnnotationsNatively ( ) | | canSaveChanges ( SaveFormsCapability ) ) {
2012-05-14 13:51:58 +00:00
if ( ! modifiedFile . open ( ) ) {
return false ;
2022-03-08 10:10:43 +00:00
}
2012-05-14 13:51:58 +00:00
2018-01-31 10:17:16 +00:00
const QString modifiedFileName = modifiedFile . fileName ( ) ;
2012-05-14 13:51:58 +00:00
modifiedFile . close ( ) ; // We're only interested in the file name
QString errorText ;
2018-01-31 10:17:16 +00:00
if ( saveChanges ( modifiedFileName , & errorText ) ) {
docPath = modifiedFileName ; // Save this instead of the original file
2014-05-11 13:22:41 +00:00
annotationsSavedNatively = d - > canAddAnnotationsNatively ( ) ;
formsSavedNatively = canSaveChanges ( SaveFormsCapability ) ;
2012-05-14 13:51:58 +00:00
} else {
2014-09-11 19:12:27 +00:00
qCWarning ( OkularCoreDebug ) < < " saveChanges failed: " < < errorText ;
2014-09-11 17:36:01 +00:00
qCDebug ( OkularCoreDebug ) < < " Falling back to saving a copy of the original file " ;
2012-05-14 13:51:58 +00:00
}
}
2014-05-11 13:22:41 +00:00
PageItems saveWhat = None ;
if ( ! annotationsSavedNatively ) {
saveWhat | = AnnotationPageItems ;
2022-03-08 10:10:43 +00:00
}
2014-05-11 13:22:41 +00:00
if ( ! formsSavedNatively ) {
saveWhat | = FormFieldPageItems ;
2022-03-08 10:10:43 +00:00
}
2014-05-11 13:22:41 +00:00
2014-09-17 22:30:39 +00:00
QTemporaryFile metadataFile ;
2012-05-14 13:51:58 +00:00
if ( ! d - > savePageDocumentInfo ( & metadataFile , saveWhat ) ) {
2008-11-15 14:15:31 +00:00
return false ;
2022-03-08 10:10:43 +00:00
}
2008-11-15 14:15:31 +00:00
const QByteArray contentDocXml = contentDoc . toByteArray ( ) ;
2016-07-13 18:58:30 +00:00
const mode_t perm = 0100644 ;
okularArchive . writeFile ( QStringLiteral ( " content.xml " ) , contentDocXml , perm , user . loginName ( ) , userGroup . name ( ) ) ;
2010-08-10 23:49:30 +00:00
okularArchive . addLocalFile ( docPath , docFileName ) ;
2015-10-29 12:37:11 +00:00
okularArchive . addLocalFile ( metadataFile . fileName ( ) , QStringLiteral ( " metadata.xml " ) ) ;
2008-11-15 14:15:31 +00:00
if ( ! okularArchive . close ( ) ) {
return false ;
2022-03-08 10:10:43 +00:00
}
2008-11-15 14:15:31 +00:00
return true ;
}
2014-05-10 14:34:53 +00:00
bool Document : : extractArchivedFile ( const QString & destFileName )
{
if ( ! d - > m_archiveData ) {
return false ;
2022-03-08 10:10:43 +00:00
}
2014-05-10 14:34:53 +00:00
// Remove existing file, if present (QFile::copy doesn't overwrite by itself)
QFile : : remove ( destFileName ) ;
return d - > m_archiveData - > document . copy ( destFileName ) ;
}
2023-07-24 00:33:00 +00:00
QPageLayout : : Orientation Document : : orientation ( ) const
2011-06-05 22:40:22 +00:00
{
double width , height ;
int landscape , portrait ;
const Okular : : Page * currentPage ;
// if some pages are landscape and others are not, the most common wins, as
// QPrinter does not accept a per-page setting
landscape = 0 ;
portrait = 0 ;
for ( uint i = 0 ; i < pages ( ) ; i + + ) {
currentPage = page ( i ) ;
width = currentPage - > width ( ) ;
height = currentPage - > height ( ) ;
if ( currentPage - > orientation ( ) = = Okular : : Rotation90 | | currentPage - > orientation ( ) = = Okular : : Rotation270 ) {
2023-01-26 13:53:23 +00:00
std : : swap ( width , height ) ;
2022-03-08 10:10:43 +00:00
}
2011-06-05 22:40:22 +00:00
if ( width > height ) {
landscape + + ;
} else {
portrait + + ;
2022-03-08 10:10:43 +00:00
}
2011-06-05 22:40:22 +00:00
}
2023-07-24 00:33:00 +00:00
return ( landscape > portrait ) ? QPageLayout : : Landscape : QPageLayout : : Portrait ;
2011-06-05 22:40:22 +00:00
}
2012-05-23 21:43:44 +00:00
void Document : : setAnnotationEditingEnabled ( bool enable )
{
d - > m_annotationEditingEnabled = enable ;
foreachObserver ( notifySetup ( d - > m_pagesVector , 0 ) ) ;
}
2014-05-10 13:31:59 +00:00
void Document : : walletDataForFile ( const QString & fileName , QString * walletName , QString * walletFolder , QString * walletKey ) const
{
if ( d - > m_generator ) {
d - > m_generator - > walletDataForFile ( fileName , walletName , walletFolder , walletKey ) ;
2015-02-09 21:03:44 +00:00
} else if ( d - > m_walletGenerator ) {
d - > m_walletGenerator - > walletDataForFile ( fileName , walletName , walletFolder , walletKey ) ;
2014-05-10 13:31:59 +00:00
}
}
2014-09-09 14:08:32 +00:00
bool Document : : isDocdataMigrationNeeded ( ) const
{
return d - > m_docdataMigrationNeeded ;
}
void Document : : docdataMigrationDone ( )
{
if ( d - > m_docdataMigrationNeeded ) {
d - > m_docdataMigrationNeeded = false ;
foreachObserver ( notifySetup ( d - > m_pagesVector , 0 ) ) ;
}
}
2015-05-27 13:56:56 +00:00
QAbstractItemModel * Document : : layersModel ( ) const
{
2017-09-05 21:27:18 +00:00
return d - > m_generator ? d - > m_generator - > layersModel ( ) : nullptr ;
2015-05-27 13:56:56 +00:00
}
2020-02-10 23:22:06 +00:00
QString Document : : openError ( ) const
{
return d - > m_openError ;
}
2019-01-05 22:30:25 +00:00
QByteArray Document : : requestSignedRevisionData ( const Okular : : SignatureInfo & info )
{
QFile f ( d - > m_docFileName ) ;
if ( ! f . open ( QIODevice : : ReadOnly ) ) {
2022-03-18 21:35:45 +00:00
Q_EMIT error ( i18n ( " Could not open '%1'. File does not exist " , d - > m_docFileName ) , - 1 ) ;
2019-01-05 22:30:25 +00:00
return { } ;
}
const QList < qint64 > byteRange = info . signedRangeBounds ( ) ;
f . seek ( byteRange . first ( ) ) ;
2021-05-06 21:07:57 +00:00
QByteArray data = f . read ( byteRange . last ( ) - byteRange . first ( ) ) ;
2019-01-05 22:30:25 +00:00
f . close ( ) ;
return data ;
}
2019-12-19 22:18:31 +00:00
void Document : : refreshPixmaps ( int pageNumber )
{
d - > refreshPixmaps ( pageNumber ) ;
}
2019-06-28 12:39:02 +00:00
void DocumentPrivate : : executeScript ( const QString & function )
2019-05-23 13:28:53 +00:00
{
2019-06-28 12:39:02 +00:00
if ( ! m_scripter ) {
m_scripter = new Scripter ( this ) ;
2022-03-08 10:10:43 +00:00
}
2019-06-28 12:39:02 +00:00
m_scripter - > execute ( JavaScript , function ) ;
2019-05-23 13:28:53 +00:00
}
2007-09-14 15:29:16 +00:00
void DocumentPrivate : : requestDone ( PixmapRequest * req )
2005-01-03 00:28:46 +00:00
{
2008-02-01 00:43:45 +00:00
if ( ! req ) {
return ;
2022-03-08 10:10:43 +00:00
}
2008-02-01 00:43:45 +00:00
if ( ! m_generator | | m_closingLoop ) {
m_pixmapRequestsMutex . lock ( ) ;
2022-03-31 15:05:15 +00:00
m_executingPixmapRequests . remove ( req ) ;
2008-02-01 00:43:45 +00:00
m_pixmapRequestsMutex . unlock ( ) ;
delete req ;
if ( m_closingLoop ) {
m_closingLoop - > exit ( ) ;
2022-03-08 10:10:43 +00:00
}
2007-05-24 22:52:29 +00:00
return ;
2008-02-01 00:43:45 +00:00
}
2007-05-24 22:52:29 +00:00
2007-01-02 19:05:49 +00:00
# ifndef NDEBUG
2007-09-14 15:29:16 +00:00
if ( ! m_generator - > canGeneratePixmap ( ) ) {
2014-09-11 17:36:01 +00:00
qCDebug ( OkularCoreDebug ) < < " requestDone with generator not in READY state. " ;
2022-03-08 10:10:43 +00:00
}
2007-01-02 19:05:49 +00:00
# endif
2005-01-03 00:28:46 +00:00
2018-02-01 18:42:37 +00:00
if ( ! req - > shouldAbortRender ( ) ) {
// [MEM] 1.1 find and remove a previous entry for the same page and id
2022-03-31 15:05:15 +00:00
std : : list < AllocatedPixmap * > : : iterator aIt = m_allocatedPixmaps . begin ( ) ;
std : : list < AllocatedPixmap * > : : iterator aEnd = m_allocatedPixmaps . end ( ) ;
2018-02-01 18:42:37 +00:00
for ( ; aIt ! = aEnd ; + + aIt ) {
if ( ( * aIt ) - > page = = req - > pageNumber ( ) & & ( * aIt ) - > observer = = req - > observer ( ) ) {
AllocatedPixmap * p = * aIt ;
m_allocatedPixmaps . erase ( aIt ) ;
m_allocatedPixmapsTotalMemory - = p - > memory ;
delete p ;
break ;
}
2022-03-08 10:10:43 +00:00
}
2018-02-01 18:42:37 +00:00
DocumentObserver * observer = req - > observer ( ) ;
if ( m_observers . contains ( observer ) ) {
// [MEM] 1.2 append memory allocation descriptor to the FIFO
qulonglong memoryBytes = 0 ;
const TilesManager * tm = req - > d - > tilesManager ( ) ;
if ( tm ) {
memoryBytes = tm - > totalMemory ( ) ;
} else {
memoryBytes = 4 * req - > width ( ) * req - > height ( ) ;
2022-03-08 10:10:43 +00:00
}
2012-07-16 15:57:51 +00:00
2018-02-01 18:42:37 +00:00
AllocatedPixmap * memoryPage = new AllocatedPixmap ( req - > observer ( ) , req - > pageNumber ( ) , memoryBytes ) ;
2022-03-31 15:05:15 +00:00
m_allocatedPixmaps . push_back ( memoryPage ) ;
2018-02-01 18:42:37 +00:00
m_allocatedPixmapsTotalMemory + = memoryBytes ;
2007-05-17 16:52:44 +00:00
2018-02-01 18:42:37 +00:00
// 2. notify an observer that its pixmap changed
observer - > notifyPageChanged ( req - > pageNumber ( ) , DocumentObserver : : Pixmap ) ;
}
2007-05-17 16:52:44 +00:00
# ifndef NDEBUG
2018-02-01 18:42:37 +00:00
else {
qCWarning ( OkularCoreDebug ) < < " Receiving a done request for the defunct observer " < < observer ;
2022-03-08 10:10:43 +00:00
}
2007-05-17 16:52:44 +00:00
# endif
2018-02-01 18:42:37 +00:00
}
2005-01-28 17:21:51 +00:00
2007-01-02 19:05:49 +00:00
// 3. delete request
2008-02-01 00:43:45 +00:00
m_pixmapRequestsMutex . lock ( ) ;
2022-03-31 15:05:15 +00:00
m_executingPixmapRequests . remove ( req ) ;
2008-02-01 00:43:45 +00:00
m_pixmapRequestsMutex . unlock ( ) ;
2007-01-02 19:05:49 +00:00
delete req ;
2005-01-03 00:28:46 +00:00
2007-01-02 19:05:49 +00:00
// 4. start a new generation if some is pending
2007-09-14 15:29:16 +00:00
m_pixmapRequestsMutex . lock ( ) ;
2022-03-31 15:05:15 +00:00
bool hasPixmaps = ! m_pixmapRequestsStack . empty ( ) ;
2007-09-14 15:29:16 +00:00
m_pixmapRequestsMutex . unlock ( ) ;
2007-05-12 21:40:38 +00:00
if ( hasPixmaps ) {
2012-09-25 09:09:34 +00:00
sendGeneratorPixmapRequest ( ) ;
2022-03-08 10:10:43 +00:00
}
2004-12-24 10:24:10 +00:00
}
2008-05-18 23:06:21 +00:00
void DocumentPrivate : : setPageBoundingBox ( int page , const NormalizedRect & boundingBox )
{
Page * kp = m_pagesVector [ page ] ;
if ( ! m_generator | | ! kp ) {
return ;
2022-03-08 10:10:43 +00:00
}
2008-05-18 23:06:21 +00:00
if ( kp - > boundingBox ( ) = = boundingBox ) {
return ;
2022-03-08 10:10:43 +00:00
}
2008-05-18 23:06:21 +00:00
kp - > setBoundingBox ( boundingBox ) ;
// notify observers about the change
foreachObserverD ( notifyPageChanged ( page , DocumentObserver : : BoundingBox ) ) ;
// TODO: For generators that generate the bbox by pixmap scanning, if the first generated pixmap is very small, the bounding box will forever be inaccurate.
// TODO: Crop computation should also consider annotations, actions, etc. to make sure they're not cropped away.
// TODO: Help compute bounding box for generators that create a QPixmap without a QImage, like text and plucker.
// TODO: Don't compute the bounding box if no one needs it (e.g., Trim Borders is off).
}
2008-05-04 15:10:32 +00:00
void DocumentPrivate : : calculateMaxTextPages ( )
{
int multipliers = qMax ( 1 , qRound ( getTotalMemory ( ) / 536870912.0 ) ) ; // 512 MB
2012-10-15 22:27:42 +00:00
switch ( SettingsCore : : memoryLevel ( ) ) {
case SettingsCore : : EnumMemoryLevel : : Low :
2008-05-04 15:10:32 +00:00
m_maxAllocatedTextPages = multipliers * 2 ;
break ;
2012-10-15 22:27:42 +00:00
case SettingsCore : : EnumMemoryLevel : : Normal :
2008-05-04 15:10:32 +00:00
m_maxAllocatedTextPages = multipliers * 50 ;
break ;
2012-10-15 22:27:42 +00:00
case SettingsCore : : EnumMemoryLevel : : Aggressive :
2008-05-04 15:10:32 +00:00
m_maxAllocatedTextPages = multipliers * 250 ;
break ;
2012-03-08 23:12:20 +00:00
2012-10-15 22:27:42 +00:00
case SettingsCore : : EnumMemoryLevel : : Greedy :
2012-03-08 23:12:20 +00:00
m_maxAllocatedTextPages = multipliers * 1250 ;
break ;
2008-05-04 15:10:32 +00:00
}
}
void DocumentPrivate : : textGenerationDone ( Page * page )
{
2014-07-31 23:27:27 +00:00
if ( ! m_pageController ) {
return ;
2022-03-08 10:10:43 +00:00
}
2008-05-04 15:10:32 +00:00
// 1. If we reached the cache limit, delete the first text page from the fifo
if ( m_allocatedTextPagesFifo . size ( ) = = m_maxAllocatedTextPages ) {
int pageToKick = m_allocatedTextPagesFifo . takeFirst ( ) ;
if ( pageToKick ! = page - > number ( ) ) // this should never happen but better be safe than sorry
{
2017-09-05 21:27:18 +00:00
m_pagesVector . at ( pageToKick ) - > setTextPage ( nullptr ) ; // deletes the textpage
2008-05-04 15:10:32 +00:00
}
}
// 2. Add the page to the fifo of generated text pages
m_allocatedTextPagesFifo . append ( page - > number ( ) ) ;
}
2007-05-01 23:09:45 +00:00
void Document : : setRotation ( int r )
2007-11-01 16:15:34 +00:00
{
d - > setRotationInternal ( r , true ) ;
}
void DocumentPrivate : : setRotationInternal ( int r , bool notify )
2005-11-04 11:59:51 +00:00
{
2007-01-05 17:09:47 +00:00
Rotation rotation = ( Rotation ) r ;
2007-11-01 16:15:34 +00:00
if ( ! m_generator | | ( m_rotation = = rotation ) ) {
2019-07-30 20:25:35 +00:00
return ;
2022-03-08 10:10:43 +00:00
}
2007-07-17 18:13:50 +00:00
2006-10-25 15:35:53 +00:00
// tell the pages to rotate
2008-11-11 18:48:40 +00:00
QVector < Okular : : Page * > : : const_iterator pIt = m_pagesVector . constBegin ( ) ;
QVector < Okular : : Page * > : : const_iterator pEnd = m_pagesVector . constEnd ( ) ;
2006-10-25 15:35:53 +00:00
for ( ; pIt ! = pEnd ; + + pIt ) {
2007-05-01 23:36:57 +00:00
( * pIt ) - > d - > rotateAt ( rotation ) ;
2022-03-08 10:10:43 +00:00
}
2007-11-01 16:15:34 +00:00
if ( notify ) {
// notify the generator that the current rotation has changed
m_generator - > rotationChanged ( rotation , m_rotation ) ;
}
2006-10-25 15:35:53 +00:00
// set the new rotation
2007-11-01 16:15:34 +00:00
m_rotation = rotation ;
2006-10-25 15:35:53 +00:00
2007-11-01 16:15:34 +00:00
if ( notify ) {
foreachObserverD ( notifySetup ( m_pagesVector , DocumentObserver : : NewLayoutForPages ) ) ;
foreachObserverD ( notifyContentsCleared ( DocumentObserver : : Pixmap | DocumentObserver : : Highlights | DocumentObserver : : Annotations ) ) ;
}
2014-09-11 17:36:01 +00:00
qCDebug ( OkularCoreDebug ) < < " Rotated: " < < r ;
2005-11-04 11:59:51 +00:00
}
2005-01-09 23:37:07 +00:00
2007-05-01 23:21:29 +00:00
void Document : : setPageSize ( const PageSize & size )
- GIGANTIC 2700 line diff with LOTS OF FEATURES!
- 1. editor-like text selection, and I do mean it, its not pseudo-editor
(like the ones acroread and kviewshell have) it doesnt intersect the
selection area with words under it, no, it does a lot more, including
work on cursors and searching for the text area closest to the given
cursor
- 2. rotation support, change the orientation of the documents if
you need too :)
- 3. the kfaxview backend works beautifully, porting kviewshell backends
is damn easy ! djvu and dvi will be next!
- 4. Hardware Blending of selection rectangles! We now use XRender
instead of KImageEffect, makes a damn faster blend!
- 5. Overview mode - as seen in Kviewshell, but quite a bit extended,
the kviewshell is only one state, while we support it in both
continous and non-continous form
- BTW. I coded all those features myself, (apart from kfaxview backend library)
it is an impressive bit right? but oKular cant be run by only one person,
join in on the fun! i can introduce you into the code just mail niedakh@gmail.com
svn path=/trunk/playground/graphics/oKular/kpdf/; revision=509871
2006-02-15 18:54:49 +00:00
{
2007-05-24 22:52:29 +00:00
if ( ! d - > m_generator | | ! d - > m_generator - > hasFeature ( Generator : : PageSizes ) ) {
return ;
2022-03-08 10:10:43 +00:00
}
2007-05-24 22:52:29 +00:00
if ( d - > m_pageSizes . isEmpty ( ) ) {
d - > m_pageSizes = d - > m_generator - > pageSizes ( ) ;
2022-03-08 10:10:43 +00:00
}
2007-05-01 23:21:29 +00:00
int sizeid = d - > m_pageSizes . indexOf ( size ) ;
2007-05-24 22:52:29 +00:00
if ( sizeid = = - 1 ) {
2007-01-05 23:12:06 +00:00
return ;
2022-03-08 10:10:43 +00:00
}
2007-01-05 23:12:06 +00:00
// tell the pages to change size
2008-11-11 18:48:40 +00:00
QVector < Okular : : Page * > : : const_iterator pIt = d - > m_pagesVector . constBegin ( ) ;
QVector < Okular : : Page * > : : const_iterator pEnd = d - > m_pagesVector . constEnd ( ) ;
2007-01-05 23:12:06 +00:00
for ( ; pIt ! = pEnd ; + + pIt ) {
2007-05-01 23:36:57 +00:00
( * pIt ) - > d - > changeSize ( size ) ;
2022-03-08 10:10:43 +00:00
}
2007-01-05 23:12:06 +00:00
// clear 'memory allocation' descriptors
2012-07-01 23:05:02 +00:00
qDeleteAll ( d - > m_allocatedPixmaps ) ;
d - > m_allocatedPixmaps . clear ( ) ;
2007-01-05 23:12:06 +00:00
d - > m_allocatedPixmapsTotalMemory = 0 ;
// notify the generator that the current page size has changed
2007-05-01 23:21:29 +00:00
d - > m_generator - > pageSizeChanged ( size , d - > m_pageSize ) ;
2007-01-05 23:12:06 +00:00
// set the new page size
2007-05-01 23:21:29 +00:00
d - > m_pageSize = size ;
2007-01-05 23:12:06 +00:00
2007-09-09 10:50:36 +00:00
foreachObserver ( notifySetup ( d - > m_pagesVector , DocumentObserver : : NewLayoutForPages ) ) ;
2007-01-17 23:06:40 +00:00
foreachObserver ( notifyContentsCleared ( DocumentObserver : : Pixmap | DocumentObserver : : Highlights ) ) ;
2014-09-11 17:36:01 +00:00
qCDebug ( OkularCoreDebug ) < < " New PageSize id: " < < sizeid ;
- GIGANTIC 2700 line diff with LOTS OF FEATURES!
- 1. editor-like text selection, and I do mean it, its not pseudo-editor
(like the ones acroread and kviewshell have) it doesnt intersect the
selection area with words under it, no, it does a lot more, including
work on cursors and searching for the text area closest to the given
cursor
- 2. rotation support, change the orientation of the documents if
you need too :)
- 3. the kfaxview backend works beautifully, porting kviewshell backends
is damn easy ! djvu and dvi will be next!
- 4. Hardware Blending of selection rectangles! We now use XRender
instead of KImageEffect, makes a damn faster blend!
- 5. Overview mode - as seen in Kviewshell, but quite a bit extended,
the kviewshell is only one state, while we support it in both
continous and non-continous form
- BTW. I coded all those features myself, (apart from kfaxview backend library)
it is an impressive bit right? but oKular cant be run by only one person,
join in on the fun! i can introduce you into the code just mail niedakh@gmail.com
svn path=/trunk/playground/graphics/oKular/kpdf/; revision=509871
2006-02-15 18:54:49 +00:00
}
2005-01-09 23:37:07 +00:00
/** DocumentViewport **/
DocumentViewport : : DocumentViewport ( int n )
: pageNumber ( n )
{
// default settings
2005-06-13 11:53:47 +00:00
rePos . enabled = false ;
rePos . normalizedX = 0.5 ;
rePos . normalizedY = 0.0 ;
rePos . pos = Center ;
2005-01-09 23:37:07 +00:00
autoFit . enabled = false ;
autoFit . width = false ;
autoFit . height = false ;
}
2011-11-01 21:13:20 +00:00
DocumentViewport : : DocumentViewport ( const QString & xmlDesc )
: pageNumber ( - 1 )
2005-01-09 23:37:07 +00:00
{
// default settings (maybe overridden below)
2005-06-13 11:53:47 +00:00
rePos . enabled = false ;
rePos . normalizedX = 0.5 ;
rePos . normalizedY = 0.0 ;
rePos . pos = Center ;
2005-01-09 23:37:07 +00:00
autoFit . enabled = false ;
autoFit . width = false ;
autoFit . height = false ;
// check for string presence
if ( xmlDesc . isEmpty ( ) ) {
return ;
2022-03-08 10:10:43 +00:00
}
2005-01-09 23:37:07 +00:00
// decode the string
bool ok ;
int field = 0 ;
2016-07-11 20:07:57 +00:00
QString token = xmlDesc . section ( QLatin1Char ( ' ; ' ) , field , field ) ;
2005-01-09 23:37:07 +00:00
while ( ! token . isEmpty ( ) ) {
// decode the current token
if ( field = = 0 ) {
pageNumber = token . toInt ( & ok ) ;
if ( ! ok ) {
return ;
2022-03-08 10:10:43 +00:00
}
2015-10-29 12:37:11 +00:00
} else if ( token . startsWith ( QLatin1String ( " C1 " ) ) ) {
2005-06-13 11:53:47 +00:00
rePos . enabled = true ;
2016-07-11 20:07:57 +00:00
rePos . normalizedX = token . section ( QLatin1Char ( ' : ' ) , 1 , 1 ) . toDouble ( ) ;
rePos . normalizedY = token . section ( QLatin1Char ( ' : ' ) , 2 , 2 ) . toDouble ( ) ;
2005-06-13 11:53:47 +00:00
rePos . pos = Center ;
2015-10-29 12:37:11 +00:00
} else if ( token . startsWith ( QLatin1String ( " C2 " ) ) ) {
2005-06-13 11:53:47 +00:00
rePos . enabled = true ;
2016-07-11 20:07:57 +00:00
rePos . normalizedX = token . section ( QLatin1Char ( ' : ' ) , 1 , 1 ) . toDouble ( ) ;
rePos . normalizedY = token . section ( QLatin1Char ( ' : ' ) , 2 , 2 ) . toDouble ( ) ;
if ( token . section ( QLatin1Char ( ' : ' ) , 3 , 3 ) . toInt ( ) = = 1 ) {
rePos . pos = Center ;
2005-06-13 11:53:47 +00:00
} else {
rePos . pos = TopLeft ;
2022-03-08 10:10:43 +00:00
}
2015-10-29 12:37:11 +00:00
} else if ( token . startsWith ( QLatin1String ( " AF1 " ) ) ) {
2005-01-09 23:37:07 +00:00
autoFit . enabled = true ;
2016-07-11 20:07:57 +00:00
autoFit . width = token . section ( QLatin1Char ( ' : ' ) , 1 , 1 ) = = QLatin1String ( " T " ) ;
autoFit . height = token . section ( QLatin1Char ( ' : ' ) , 2 , 2 ) = = QLatin1String ( " T " ) ;
2005-01-09 23:37:07 +00:00
}
// proceed tokenizing string
field + + ;
2016-07-11 20:07:57 +00:00
token = xmlDesc . section ( QLatin1Char ( ' ; ' ) , field , field ) ;
2005-01-09 23:37:07 +00:00
}
2004-12-22 18:21:36 +00:00
}
2005-01-09 23:37:07 +00:00
QString DocumentViewport : : toString ( ) const
{
// start string with page number
QString s = QString : : number ( pageNumber ) ;
// if has center coordinates, save them on string
2005-06-13 11:53:47 +00:00
if ( rePos . enabled ) {
2015-10-29 12:37:11 +00:00
s + = QStringLiteral ( " ;C2: " ) + QString : : number ( rePos . normalizedX ) + QLatin1Char ( ' : ' ) + QString : : number ( rePos . normalizedY ) + QLatin1Char ( ' : ' ) + QString : : number ( rePos . pos ) ;
2022-03-08 10:10:43 +00:00
}
2005-01-09 23:37:07 +00:00
// if has autofit enabled, save its state on string
if ( autoFit . enabled ) {
2016-07-11 20:07:57 +00:00
s + = QStringLiteral ( " ;AF1: " ) + ( autoFit . width ? QLatin1Char ( ' T ' ) : QLatin1Char ( ' F ' ) ) + QLatin1Char ( ' : ' ) + ( autoFit . height ? QLatin1Char ( ' T ' ) : QLatin1Char ( ' F ' ) ) ;
2022-03-08 10:10:43 +00:00
}
2005-01-09 23:37:07 +00:00
return s ;
}
2006-12-26 12:26:49 +00:00
bool DocumentViewport : : isValid ( ) const
{
2007-05-02 18:21:15 +00:00
return pageNumber > = 0 ;
2006-12-26 12:26:49 +00:00
}
2019-12-20 15:40:59 +00:00
bool DocumentViewport : : operator = = ( const DocumentViewport & other ) const
2005-01-09 23:37:07 +00:00
{
2019-12-20 15:40:59 +00:00
bool equal = ( pageNumber = = other . pageNumber ) & & ( rePos . enabled = = other . rePos . enabled ) & & ( autoFit . enabled = = other . autoFit . enabled ) ;
2005-01-09 23:37:07 +00:00
if ( ! equal ) {
return false ;
2022-03-08 10:10:43 +00:00
}
2019-12-20 15:40:59 +00:00
if ( rePos . enabled & & ( ( rePos . normalizedX ! = other . rePos . normalizedX ) | | ( rePos . normalizedY ! = other . rePos . normalizedY ) | | rePos . pos ! = other . rePos . pos ) ) {
2005-01-09 23:37:07 +00:00
return false ;
2022-03-08 10:10:43 +00:00
}
2019-12-20 15:40:59 +00:00
if ( autoFit . enabled & & ( ( autoFit . width ! = other . autoFit . width ) | | ( autoFit . height ! = other . autoFit . height ) ) ) {
2005-01-09 23:37:07 +00:00
return false ;
2022-03-08 10:10:43 +00:00
}
2005-01-09 23:37:07 +00:00
return true ;
}
2019-12-20 15:40:59 +00:00
bool DocumentViewport : : operator < ( const DocumentViewport & other ) const
2012-06-04 14:10:33 +00:00
{
// TODO: Check autoFit and Position
2019-12-20 15:40:59 +00:00
if ( pageNumber ! = other . pageNumber ) {
return pageNumber < other . pageNumber ;
2022-03-08 10:10:43 +00:00
}
2012-06-04 14:10:33 +00:00
2019-12-20 15:40:59 +00:00
if ( ! rePos . enabled & & other . rePos . enabled ) {
2012-06-04 14:10:33 +00:00
return true ;
2022-03-08 10:10:43 +00:00
}
2012-06-04 14:10:33 +00:00
2019-12-20 15:40:59 +00:00
if ( ! other . rePos . enabled ) {
2012-06-04 14:10:33 +00:00
return false ;
2022-03-08 10:10:43 +00:00
}
2012-06-04 14:10:33 +00:00
2019-12-20 15:40:59 +00:00
if ( rePos . normalizedY ! = other . rePos . normalizedY ) {
return rePos . normalizedY < other . rePos . normalizedY ;
2022-03-08 10:10:43 +00:00
}
2012-06-04 14:10:33 +00:00
2019-12-20 15:40:59 +00:00
return rePos . normalizedX < other . rePos . normalizedX ;
2012-06-04 14:10:33 +00:00
}
2005-01-02 22:37:52 +00:00
/** DocumentInfo **/
2014-05-11 09:17:49 +00:00
DocumentInfo : : DocumentInfo ( )
: d ( new DocumentInfoPrivate ( ) )
2005-01-02 22:37:52 +00:00
{
}
2014-05-11 09:17:49 +00:00
DocumentInfo : : DocumentInfo ( const DocumentInfo & info )
: d ( new DocumentInfoPrivate ( ) )
2005-01-02 22:37:52 +00:00
{
2014-05-11 09:17:49 +00:00
* this = info ;
}
2005-01-02 22:37:52 +00:00
2014-05-11 09:17:49 +00:00
DocumentInfo & DocumentInfo : : operator = ( const DocumentInfo & info )
{
2019-12-23 13:57:50 +00:00
if ( this ! = & info ) {
d - > values = info . d - > values ;
d - > titles = info . d - > titles ;
}
2014-05-11 09:17:49 +00:00
return * this ;
}
2005-01-02 22:37:52 +00:00
2014-05-11 09:17:49 +00:00
DocumentInfo : : ~ DocumentInfo ( )
{
delete d ;
}
2005-01-02 22:37:52 +00:00
2014-05-11 09:17:49 +00:00
void DocumentInfo : : set ( const QString & key , const QString & value , const QString & title )
{
d - > values [ key ] = value ;
d - > titles [ key ] = title ;
2005-01-02 22:37:52 +00:00
}
2009-11-16 00:46:33 +00:00
void DocumentInfo : : set ( Key key , const QString & value )
{
2014-05-11 09:17:49 +00:00
d - > values [ getKeyString ( key ) ] = value ;
2009-11-16 00:46:33 +00:00
}
2014-05-11 09:17:49 +00:00
QStringList DocumentInfo : : keys ( ) const
2009-11-16 00:46:33 +00:00
{
2014-05-11 09:17:49 +00:00
return d - > values . keys ( ) ;
}
2009-11-16 00:46:33 +00:00
2014-05-11 09:17:49 +00:00
QString DocumentInfo : : get ( Key key ) const
{
return get ( getKeyString ( key ) ) ;
}
QString DocumentInfo : : get ( const QString & key ) const
{
return d - > values [ key ] ;
2009-11-16 00:46:33 +00:00
}
QString DocumentInfo : : getKeyString ( Key key ) // const
2007-05-13 12:54:46 +00:00
{
switch ( key ) {
case Title :
2015-10-29 12:37:11 +00:00
return QStringLiteral ( " title " ) ;
2007-05-13 12:54:46 +00:00
break ;
case Subject :
2015-10-29 12:37:11 +00:00
return QStringLiteral ( " subject " ) ;
2007-05-13 12:54:46 +00:00
break ;
case Description :
2015-10-29 12:37:11 +00:00
return QStringLiteral ( " description " ) ;
2007-05-13 12:54:46 +00:00
break ;
case Author :
2015-10-29 12:37:11 +00:00
return QStringLiteral ( " author " ) ;
2007-05-13 12:54:46 +00:00
break ;
case Creator :
2015-10-29 12:37:11 +00:00
return QStringLiteral ( " creator " ) ;
2007-05-13 12:54:46 +00:00
break ;
case Producer :
2015-10-29 12:37:11 +00:00
return QStringLiteral ( " producer " ) ;
2007-05-13 12:54:46 +00:00
break ;
case Copyright :
2015-10-29 12:37:11 +00:00
return QStringLiteral ( " copyright " ) ;
2007-05-13 12:54:46 +00:00
break ;
case Pages :
2015-10-29 12:37:11 +00:00
return QStringLiteral ( " pages " ) ;
2007-05-13 12:54:46 +00:00
break ;
case CreationDate :
2015-10-29 12:37:11 +00:00
return QStringLiteral ( " creationDate " ) ;
2007-05-13 12:54:46 +00:00
break ;
case ModificationDate :
2015-10-29 12:37:11 +00:00
return QStringLiteral ( " modificationDate " ) ;
2007-05-13 12:54:46 +00:00
break ;
case MimeType :
2015-10-29 12:37:11 +00:00
return QStringLiteral ( " mimeType " ) ;
2007-05-13 12:54:46 +00:00
break ;
case Category :
2015-10-29 12:37:11 +00:00
return QStringLiteral ( " category " ) ;
2007-05-13 12:54:46 +00:00
break ;
case Keywords :
2015-10-29 12:37:11 +00:00
return QStringLiteral ( " keywords " ) ;
2009-11-16 00:46:33 +00:00
break ;
case FilePath :
2015-10-29 12:37:11 +00:00
return QStringLiteral ( " filePath " ) ;
2009-11-16 00:46:33 +00:00
break ;
case DocumentSize :
2015-10-29 12:37:11 +00:00
return QStringLiteral ( " documentSize " ) ;
2009-11-16 00:46:33 +00:00
break ;
case PagesSize :
2015-10-29 12:37:11 +00:00
return QStringLiteral ( " pageSize " ) ;
2007-05-13 12:54:46 +00:00
break ;
default :
2014-09-11 19:12:27 +00:00
qCWarning ( OkularCoreDebug ) < < " Unknown " < < key ;
2009-11-16 00:46:33 +00:00
return QString ( ) ;
2007-05-13 12:54:46 +00:00
break ;
}
}
2014-05-11 09:17:49 +00:00
DocumentInfo : : Key DocumentInfo : : getKeyFromString ( const QString & key ) // const
{
2015-10-29 12:37:11 +00:00
if ( key = = QLatin1String ( " title " ) ) {
return Title ;
} else if ( key = = QLatin1String ( " subject " ) ) {
return Subject ;
} else if ( key = = QLatin1String ( " description " ) ) {
return Description ;
} else if ( key = = QLatin1String ( " author " ) ) {
return Author ;
} else if ( key = = QLatin1String ( " creator " ) ) {
return Creator ;
} else if ( key = = QLatin1String ( " producer " ) ) {
return Producer ;
} else if ( key = = QLatin1String ( " copyright " ) ) {
return Copyright ;
} else if ( key = = QLatin1String ( " pages " ) ) {
return Pages ;
} else if ( key = = QLatin1String ( " creationDate " ) ) {
return CreationDate ;
} else if ( key = = QLatin1String ( " modificationDate " ) ) {
return ModificationDate ;
} else if ( key = = QLatin1String ( " mimeType " ) ) {
return MimeType ;
} else if ( key = = QLatin1String ( " category " ) ) {
return Category ;
} else if ( key = = QLatin1String ( " keywords " ) ) {
return Keywords ;
} else if ( key = = QLatin1String ( " filePath " ) ) {
return FilePath ;
} else if ( key = = QLatin1String ( " documentSize " ) ) {
return DocumentSize ;
} else if ( key = = QLatin1String ( " pageSize " ) ) {
return PagesSize ;
2014-05-11 09:17:49 +00:00
} else {
return Invalid ;
2022-03-08 10:10:43 +00:00
}
2014-05-11 09:17:49 +00:00
}
2009-11-16 00:46:33 +00:00
QString DocumentInfo : : getKeyTitle ( Key key ) // const
2005-01-02 22:37:52 +00:00
{
2009-11-16 00:46:33 +00:00
switch ( key ) {
case Title :
return i18n ( " Title " ) ;
break ;
case Subject :
return i18n ( " Subject " ) ;
break ;
case Description :
return i18n ( " Description " ) ;
break ;
case Author :
return i18n ( " Author " ) ;
break ;
case Creator :
return i18n ( " Creator " ) ;
break ;
case Producer :
return i18n ( " Producer " ) ;
break ;
case Copyright :
return i18n ( " Copyright " ) ;
break ;
case Pages :
return i18n ( " Pages " ) ;
break ;
case CreationDate :
return i18n ( " Created " ) ;
break ;
case ModificationDate :
return i18n ( " Modified " ) ;
break ;
case MimeType :
2021-07-02 17:27:53 +00:00
return i18n ( " MIME Type " ) ;
2009-11-16 00:46:33 +00:00
break ;
case Category :
return i18n ( " Category " ) ;
break ;
case Keywords :
return i18n ( " Keywords " ) ;
break ;
case FilePath :
return i18n ( " File Path " ) ;
break ;
case DocumentSize :
return i18n ( " File Size " ) ;
break ;
case PagesSize :
return i18n ( " Page Size " ) ;
break ;
default :
return QString ( ) ;
break ;
}
2005-01-02 22:37:52 +00:00
}
2014-05-11 09:17:49 +00:00
QString DocumentInfo : : getKeyTitle ( const QString & key ) const
{
QString title = getKeyTitle ( getKeyFromString ( key ) ) ;
if ( title . isEmpty ( ) ) {
title = d - > titles [ key ] ;
2022-03-08 10:10:43 +00:00
}
2014-05-11 09:17:49 +00:00
return title ;
}
2005-01-03 15:51:05 +00:00
/** DocumentSynopsis **/
DocumentSynopsis : : DocumentSynopsis ( )
2015-10-29 12:37:11 +00:00
: QDomDocument ( QStringLiteral ( " DocumentSynopsis " ) )
2005-01-03 15:51:05 +00:00
{
// void implementation, only subclassed for naming
}
2006-09-20 11:44:58 +00:00
DocumentSynopsis : : DocumentSynopsis ( const QDomDocument & document )
: QDomDocument ( document )
{
}
2005-06-13 15:46:23 +00:00
2006-05-28 16:54:54 +00:00
/** EmbeddedFile **/
EmbeddedFile : : EmbeddedFile ( )
{
}
EmbeddedFile : : ~ EmbeddedFile ( )
{
}
2007-01-02 19:05:49 +00:00
VisiblePageRect : : VisiblePageRect ( int page , const NormalizedRect & rectangle )
: pageNumber ( page )
, rect ( rectangle )
{
}
2020-10-29 16:14:05 +00:00
/** NewSignatureData **/
struct Okular : : NewSignatureDataPrivate {
NewSignatureDataPrivate ( ) = default ;
QString certNickname ;
QString certSubjectCommonName ;
QString password ;
2021-12-29 18:28:34 +00:00
QString documentPassword ;
2023-04-20 08:16:04 +00:00
QString location ;
QString reason ;
2023-04-20 14:17:41 +00:00
QString backgroundImagePath ;
2023-06-16 14:26:27 +00:00
int page = - 1 ;
2020-10-29 16:14:05 +00:00
NormalizedRect boundingRectangle ;
} ;
NewSignatureData : : NewSignatureData ( )
: d ( new NewSignatureDataPrivate ( ) )
{
}
NewSignatureData : : ~ NewSignatureData ( )
{
delete d ;
}
QString NewSignatureData : : certNickname ( ) const
{
return d - > certNickname ;
}
void NewSignatureData : : setCertNickname ( const QString & certNickname )
{
d - > certNickname = certNickname ;
}
QString NewSignatureData : : certSubjectCommonName ( ) const
{
return d - > certSubjectCommonName ;
}
void NewSignatureData : : setCertSubjectCommonName ( const QString & certSubjectCommonName )
{
d - > certSubjectCommonName = certSubjectCommonName ;
}
QString NewSignatureData : : password ( ) const
{
return d - > password ;
}
void NewSignatureData : : setPassword ( const QString & password )
{
d - > password = password ;
}
int NewSignatureData : : page ( ) const
{
return d - > page ;
}
void NewSignatureData : : setPage ( int page )
{
d - > page = page ;
}
NormalizedRect NewSignatureData : : boundingRectangle ( ) const
{
return d - > boundingRectangle ;
}
void NewSignatureData : : setBoundingRectangle ( const NormalizedRect & rect )
{
d - > boundingRectangle = rect ;
}
2021-12-29 18:28:34 +00:00
QString NewSignatureData : : documentPassword ( ) const
{
return d - > documentPassword ;
}
void NewSignatureData : : setDocumentPassword ( const QString & password )
{
d - > documentPassword = password ;
}
2023-04-20 08:16:04 +00:00
QString NewSignatureData : : location ( ) const
{
return d - > location ;
}
void NewSignatureData : : setLocation ( const QString & location )
{
d - > location = location ;
}
QString NewSignatureData : : reason ( ) const
{
return d - > reason ;
}
void NewSignatureData : : setReason ( const QString & reason )
{
d - > reason = reason ;
}
2023-04-20 14:17:41 +00:00
QString Okular : : NewSignatureData : : backgroundImagePath ( ) const
{
return d - > backgroundImagePath ;
}
void Okular : : NewSignatureData : : setBackgroundImagePath ( const QString & path )
{
d - > backgroundImagePath = path ;
}
2009-01-10 11:32:54 +00:00
# undef foreachObserver
# undef foreachObserverD
2021-08-26 22:31:14 +00:00
# include "document.moc"
2008-05-04 15:10:32 +00:00
/* kate: replace-tabs on; indent-width 4; */