00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011 #include "document.h"
00012 #include "document_p.h"
00013
00014 #ifdef Q_OS_WIN
00015 #define _WIN32_WINNT 0x0500
00016 #include <windows.h>
00017 #endif
00018
00019
00020 #include <QtCore/QtAlgorithms>
00021 #include <QtCore/QDir>
00022 #include <QtCore/QFile>
00023 #include <QtCore/QFileInfo>
00024 #include <QtCore/QMap>
00025 #include <QtCore/QProcess>
00026 #include <QtCore/QTextStream>
00027 #include <QtCore/QTimer>
00028 #include <QtGui/QApplication>
00029 #include <QtGui/QLabel>
00030 #include <QtGui/QPrinter>
00031 #include <QtGui/QPrintDialog>
00032
00033 #include <kaboutdata.h>
00034 #include <kauthorized.h>
00035 #include <kcomponentdata.h>
00036 #include <kconfigdialog.h>
00037 #include <kdebug.h>
00038 #include <klibloader.h>
00039 #include <klocale.h>
00040 #include <kmessagebox.h>
00041 #include <kmimetypetrader.h>
00042 #include <krun.h>
00043 #include <kstandarddirs.h>
00044 #include <ktemporaryfile.h>
00045 #include <ktoolinvocation.h>
00046
00047
00048 #include "action.h"
00049 #include "annotations.h"
00050 #include "annotations_p.h"
00051 #include "audioplayer.h"
00052 #include "audioplayer_p.h"
00053 #include "bookmarkmanager.h"
00054 #include "chooseenginedialog_p.h"
00055 #include "debug_p.h"
00056 #include "generator_p.h"
00057 #include "interfaces/configinterface.h"
00058 #include "interfaces/guiinterface.h"
00059 #include "interfaces/printinterface.h"
00060 #include "observer.h"
00061 #include "page.h"
00062 #include "page_p.h"
00063 #include "pagecontroller_p.h"
00064 #include "settings.h"
00065 #include "sourcereference.h"
00066
00067 #include <config-okular.h>
00068
00069 using namespace Okular;
00070
00071 struct AllocatedPixmap
00072 {
00073
00074 int id;
00075 int page;
00076 qulonglong memory;
00077
00078 AllocatedPixmap( int i, int p, qulonglong m ) : id( i ), page( p ), memory( m ) {}
00079 };
00080
00081 struct RunningSearch
00082 {
00083
00084 int continueOnPage;
00085 RegularAreaRect continueOnMatch;
00086 QSet< int > highlightedPages;
00087
00088
00089 QString cachedString;
00090 Document::SearchType cachedType;
00091 Qt::CaseSensitivity cachedCaseSensitivity;
00092 bool cachedViewportMove : 1;
00093 bool cachedNoDialogs : 1;
00094 QColor cachedColor;
00095 };
00096
00097 #define foreachObserver( cmd ) {\
00098 QMap< int, DocumentObserver * >::const_iterator it=d->m_observers.begin(), end=d->m_observers.end();\
00099 for ( ; it != end ; ++ it ) { (*it)-> cmd ; } }
00100
00101 #define foreachObserverD( cmd ) {\
00102 QMap< int, DocumentObserver * >::const_iterator it = m_observers.begin(), end = m_observers.end();\
00103 for ( ; it != end ; ++ it ) { (*it)-> cmd ; } }
00104
00105 #define OKULAR_HISTORY_MAXSTEPS 100
00106 #define OKULAR_HISTORY_SAVEDSTEPS 10
00107
00108
00109
00110 QString DocumentPrivate::pagesSizeString() const
00111 {
00112 if (m_generator)
00113 {
00114 if (m_generator->pagesSizeMetric() != Generator::None)
00115 {
00116 QSizeF size = m_parent->allPagesSize();
00117 if (size.isValid()) return localizedSize(size);
00118 else return QString();
00119 }
00120 else return QString();
00121 }
00122 else return QString();
00123 }
00124
00125 QString DocumentPrivate::localizedSize(const QSizeF &size) const
00126 {
00127 double inchesWidth = 0, inchesHeight = 0;
00128 switch (m_generator->pagesSizeMetric())
00129 {
00130 case Generator::Points:
00131 inchesWidth = size.width() / 72.0;
00132 inchesHeight = size.height() / 72.0;
00133 break;
00134
00135 case Generator::None:
00136 break;
00137 }
00138 if (KGlobal::locale()->measureSystem() == KLocale::Imperial)
00139 {
00140 return i18n("%1 x %2 in", inchesWidth, inchesHeight);
00141 }
00142 else
00143 {
00144 return i18n("%1 x %2 mm", inchesWidth * 25.4, inchesHeight * 25.4);
00145 }
00146 }
00147
00148 void DocumentPrivate::cleanupPixmapMemory( qulonglong )
00149 {
00150
00151 qulonglong clipValue = ~0U;
00152 qulonglong memoryToFree = ~0U;
00153 switch ( Settings::memoryLevel() )
00154 {
00155 case Settings::EnumMemoryLevel::Low:
00156 memoryToFree = m_allocatedPixmapsTotalMemory;
00157 break;
00158
00159 case Settings::EnumMemoryLevel::Normal:
00160 memoryToFree = m_allocatedPixmapsTotalMemory - getTotalMemory() / 3;
00161 clipValue = (m_allocatedPixmapsTotalMemory - getFreeMemory()) / 2;
00162 break;
00163
00164 case Settings::EnumMemoryLevel::Aggressive:
00165 clipValue = (m_allocatedPixmapsTotalMemory - getFreeMemory()) / 2;
00166 break;
00167 }
00168
00169 if ( clipValue > memoryToFree )
00170 memoryToFree = clipValue;
00171
00172 if ( memoryToFree > 0 )
00173 {
00174
00175 int pagesFreed = 0;
00176 QLinkedList< AllocatedPixmap * >::iterator pIt = m_allocatedPixmapsFifo.begin();
00177 QLinkedList< AllocatedPixmap * >::iterator pEnd = m_allocatedPixmapsFifo.end();
00178 while ( (pIt != pEnd) && (memoryToFree > 0) )
00179 {
00180 AllocatedPixmap * p = *pIt;
00181 if ( m_observers.value( p->id )->canUnloadPixmap( p->page ) )
00182 {
00183
00184 pIt = m_allocatedPixmapsFifo.erase( pIt );
00185 m_allocatedPixmapsTotalMemory -= p->memory;
00186 memoryToFree -= p->memory;
00187 pagesFreed++;
00188
00189 m_pagesVector.at( p->page )->deletePixmap( p->id );
00190
00191 delete p;
00192 } else
00193 ++pIt;
00194 }
00195
00196 }
00197 }
00198
00199 qulonglong DocumentPrivate::getTotalMemory()
00200 {
00201 static qulonglong cachedValue = 0;
00202 if ( cachedValue )
00203 return cachedValue;
00204
00205 #if defined(Q_OS_LINUX)
00206
00207 QFile memFile( "/proc/meminfo" );
00208 if ( !memFile.open( QIODevice::ReadOnly ) )
00209 return (cachedValue = 134217728);
00210
00211
00212
00213 QTextStream readStream( &memFile );
00214 while ( true )
00215 {
00216 QString entry = readStream.readLine();
00217 if ( entry.isNull() ) break;
00218 if ( entry.startsWith( "MemTotal:" ) )
00219 return (cachedValue = (1024 * entry.section( ' ', -2, -2 ).toInt()));
00220 }
00221 #elif defined(Q_OS_WIN)
00222 MEMORYSTATUSEX stat;
00223
00224 GlobalMemoryStatusEx (&stat);
00225
00226 return ( cachedValue = stat.ullTotalPhys );
00227 #endif
00228 return (cachedValue = 134217728);
00229 }
00230
00231 qulonglong DocumentPrivate::getFreeMemory()
00232 {
00233 static QTime lastUpdate = QTime::currentTime();
00234 static qulonglong cachedValue = 0;
00235
00236 if ( lastUpdate.secsTo( QTime::currentTime() ) <= 2 )
00237 return cachedValue;
00238
00239 #if defined(Q_OS_LINUX)
00240
00241 QFile memFile( "/proc/meminfo" );
00242 if ( !memFile.open( QIODevice::ReadOnly ) )
00243 return 0;
00244
00245
00246
00247 qulonglong memoryFree = 0;
00248 QString entry;
00249 QTextStream readStream( &memFile );
00250 while ( true )
00251 {
00252 entry = readStream.readLine();
00253 if ( entry.isNull() ) break;
00254 if ( entry.startsWith( "MemFree:" ) ||
00255 entry.startsWith( "Buffers:" ) ||
00256 entry.startsWith( "Cached:" ) ||
00257 entry.startsWith( "SwapFree:" ) )
00258 memoryFree += entry.section( ' ', -2, -2 ).toInt();
00259 if ( entry.startsWith( "SwapTotal:" ) )
00260 memoryFree -= entry.section( ' ', -2, -2 ).toInt();
00261 }
00262 memFile.close();
00263
00264 lastUpdate = QTime::currentTime();
00265
00266 return ( cachedValue = (1024 * memoryFree) );
00267 #elif defined(Q_OS_WIN)
00268 MEMORYSTATUSEX stat;
00269
00270 GlobalMemoryStatusEx (&stat);
00271
00272 lastUpdate = QTime::currentTime();
00273
00274 return ( cachedValue = stat.ullAvailPhys );
00275 #else
00276
00277 return 0;
00278 #endif
00279 }
00280
00281 void DocumentPrivate::loadDocumentInfo()
00282
00283
00284 {
00285
00286 if ( m_xmlFileName.isEmpty() )
00287 return;
00288
00289 QFile infoFile( m_xmlFileName );
00290 if ( !infoFile.exists() || !infoFile.open( QIODevice::ReadOnly ) )
00291 return;
00292
00293
00294 QDomDocument doc( "documentInfo" );
00295 if ( !doc.setContent( &infoFile ) )
00296 {
00297 kDebug(OkularDebug) << "Can't load XML pair! Check for broken xml.";
00298 infoFile.close();
00299 return;
00300 }
00301 infoFile.close();
00302
00303 QDomElement root = doc.documentElement();
00304 if ( root.tagName() != "documentInfo" )
00305 return;
00306
00307 KUrl documentUrl( root.attribute( "url" ) );
00308
00309
00310 QDomNode topLevelNode = root.firstChild();
00311 while ( topLevelNode.isElement() )
00312 {
00313 QString catName = topLevelNode.toElement().tagName();
00314
00315
00316 if ( catName == "pageList" )
00317 {
00318 QDomNode pageNode = topLevelNode.firstChild();
00319 while ( pageNode.isElement() )
00320 {
00321 QDomElement pageElement = pageNode.toElement();
00322 if ( pageElement.hasAttribute( "number" ) )
00323 {
00324
00325 bool ok;
00326 int pageNumber = pageElement.attribute( "number" ).toInt( &ok );
00327
00328
00329 if ( ok && pageNumber >= 0 && pageNumber < (int)m_pagesVector.count() )
00330 m_pagesVector[ pageNumber ]->d->restoreLocalContents( pageElement );
00331 }
00332 pageNode = pageNode.nextSibling();
00333 }
00334 }
00335
00336
00337 else if ( catName == "generalInfo" )
00338 {
00339 QDomNode infoNode = topLevelNode.firstChild();
00340 while ( infoNode.isElement() )
00341 {
00342 QDomElement infoElement = infoNode.toElement();
00343
00344
00345 if ( infoElement.tagName() == "history" )
00346 {
00347
00348 m_viewportHistory.clear();
00349
00350 QDomNode historyNode = infoNode.firstChild();
00351 while ( historyNode.isElement() )
00352 {
00353 QDomElement historyElement = historyNode.toElement();
00354 if ( historyElement.hasAttribute( "viewport" ) )
00355 {
00356 QString vpString = historyElement.attribute( "viewport" );
00357 m_viewportIterator = m_viewportHistory.insert( m_viewportHistory.end(),
00358 DocumentViewport( vpString ) );
00359 }
00360 historyNode = historyNode.nextSibling();
00361 }
00362
00363 if ( m_viewportHistory.isEmpty() )
00364 m_viewportIterator = m_viewportHistory.insert( m_viewportHistory.end(), DocumentViewport() );
00365 }
00366 else if ( infoElement.tagName() == "rotation" )
00367 {
00368 QString str = infoElement.text();
00369 bool ok = true;
00370 int newrotation = !str.isEmpty() ? ( str.toInt( &ok ) % 4 ) : 0;
00371 if ( ok && newrotation != 0 )
00372 {
00373 setRotationInternal( newrotation, false );
00374 }
00375 }
00376 infoNode = infoNode.nextSibling();
00377 }
00378 }
00379
00380 topLevelNode = topLevelNode.nextSibling();
00381 }
00382 }
00383
00384 QString DocumentPrivate::giveAbsolutePath( const QString & fileName ) const
00385 {
00386 if ( !m_url.isValid() )
00387 return QString();
00388
00389 if ( !QDir::isRelativePath( fileName ) )
00390 return fileName;
00391
00392 return m_url.upUrl().url() + fileName;
00393 }
00394
00395 bool DocumentPrivate::openRelativeFile( const QString & fileName )
00396 {
00397 QString absFileName = giveAbsolutePath( fileName );
00398 if ( absFileName.isEmpty() )
00399 return false;
00400
00401 kDebug(OkularDebug).nospace() << "openDocument: '" << absFileName << "'";
00402
00403 emit m_parent->openUrl( absFileName );
00404 return true;
00405 }
00406
00407 Generator * DocumentPrivate::loadGeneratorLibrary( const KService::Ptr &service )
00408 {
00409 KPluginFactory *factory = KPluginLoader( service->library() ).factory();
00410 if ( !factory )
00411 {
00412 kWarning(OkularDebug).nospace() << "Invalid plugin factory for " << service->library() << "!";
00413 return 0;
00414 }
00415 Generator * generator = factory->create< Okular::Generator >( 0 );
00416 GeneratorInfo info( factory->componentData() );
00417 info.generator = generator;
00418 if ( info.data.isValid() && info.data.aboutData() )
00419 info.catalogName = info.data.aboutData()->catalogName();
00420 m_loadedGenerators.insert( service->name(), info );
00421 return generator;
00422 }
00423
00424 void DocumentPrivate::loadAllGeneratorLibraries()
00425 {
00426 if ( m_generatorsLoaded )
00427 return;
00428
00429 m_generatorsLoaded = true;
00430
00431 QString constraint("([X-KDE-Priority] > 0) and (exist Library)") ;
00432 KService::List offers = KServiceTypeTrader::self()->query( "okular/Generator", constraint );
00433 loadServiceList( offers );
00434 }
00435
00436 void DocumentPrivate::loadServiceList( const KService::List& offers )
00437 {
00438 int count = offers.count();
00439 if ( count <= 0 )
00440 return;
00441
00442 for ( int i = 0; i < count; ++i )
00443 {
00444 QString propName = offers.at(i)->name();
00445
00446 QHash< QString, GeneratorInfo >::const_iterator genIt = m_loadedGenerators.constFind( propName );
00447 if ( !m_loadedGenerators.isEmpty() && genIt != m_loadedGenerators.end() )
00448 continue;
00449
00450 Generator * g = loadGeneratorLibrary( offers.at(i) );
00451 (void)g;
00452 }
00453 }
00454
00455 void DocumentPrivate::unloadGenerator( const GeneratorInfo& info )
00456 {
00457 delete info.generator;
00458 }
00459
00460 void DocumentPrivate::cacheExportFormats()
00461 {
00462 if ( m_exportCached )
00463 return;
00464
00465 const ExportFormat::List formats = m_generator->exportFormats();
00466 for ( int i = 0; i < formats.count(); ++i )
00467 {
00468 if ( formats.at( i ).mimeType()->name() == QLatin1String( "text/plain" ) )
00469 m_exportToText = formats.at( i );
00470 else
00471 m_exportFormats.append( formats.at( i ) );
00472 }
00473
00474 m_exportCached = true;
00475 }
00476
00477 ConfigInterface* DocumentPrivate::generatorConfig( GeneratorInfo& info )
00478 {
00479 if ( info.configChecked )
00480 return info.config;
00481
00482 info.config = qobject_cast< Okular::ConfigInterface * >( info.generator );
00483 info.configChecked = true;
00484 return info.config;
00485 }
00486
00487 void DocumentPrivate::saveDocumentInfo() const
00488 {
00489 if ( m_xmlFileName.isEmpty() )
00490 return;
00491
00492 QFile infoFile( m_xmlFileName );
00493 if (infoFile.open( QIODevice::WriteOnly | QIODevice::Truncate) )
00494 {
00495
00496 QDomDocument doc( "documentInfo" );
00497 QDomElement root = doc.createElement( "documentInfo" );
00498 root.setAttribute( "url", m_url.pathOrUrl() );
00499 doc.appendChild( root );
00500
00501
00502 QDomElement pageList = doc.createElement( "pageList" );
00503 root.appendChild( pageList );
00504
00505 QVector< Page * >::const_iterator pIt = m_pagesVector.begin(), pEnd = m_pagesVector.end();
00506 for ( ; pIt != pEnd; ++pIt )
00507 (*pIt)->d->saveLocalContents( pageList, doc );
00508
00509
00510 QDomElement generalInfo = doc.createElement( "generalInfo" );
00511 root.appendChild( generalInfo );
00512
00513 if ( m_rotation != Rotation0 )
00514 {
00515 QDomElement rotationNode = doc.createElement( "rotation" );
00516 generalInfo.appendChild( rotationNode );
00517 rotationNode.appendChild( doc.createTextNode( QString::number( (int)m_rotation ) ) );
00518 }
00519
00520 QLinkedList< DocumentViewport >::const_iterator backIterator = m_viewportIterator;
00521 if ( backIterator != m_viewportHistory.end() )
00522 {
00523
00524 int backSteps = OKULAR_HISTORY_SAVEDSTEPS;
00525 while ( backSteps-- && backIterator != m_viewportHistory.begin() )
00526 --backIterator;
00527
00528
00529 QDomElement historyNode = doc.createElement( "history" );
00530 generalInfo.appendChild( historyNode );
00531
00532
00533 QLinkedList< DocumentViewport >::const_iterator endIt = m_viewportIterator;
00534 ++endIt;
00535 while ( backIterator != endIt )
00536 {
00537 QString name = (backIterator == m_viewportIterator) ? "current" : "oldPage";
00538 QDomElement historyEntry = doc.createElement( name );
00539 historyEntry.setAttribute( "viewport", (*backIterator).toString() );
00540 historyNode.appendChild( historyEntry );
00541 ++backIterator;
00542 }
00543 }
00544
00545
00546 QString xml = doc.toString();
00547 QTextStream os( &infoFile );
00548 os << xml;
00549 }
00550 infoFile.close();
00551 }
00552
00553 void DocumentPrivate::slotTimedMemoryCheck()
00554 {
00555
00556 if ( Settings::memoryLevel() != Settings::EnumMemoryLevel::Low &&
00557 m_allocatedPixmapsTotalMemory > 1024*1024 )
00558 cleanupPixmapMemory();
00559 }
00560
00561 void DocumentPrivate::sendGeneratorRequest()
00562 {
00563
00564 PixmapRequest * request = 0;
00565 m_pixmapRequestsMutex.lock();
00566 while ( !m_pixmapRequestsStack.isEmpty() && !request )
00567 {
00568 PixmapRequest * r = m_pixmapRequestsStack.last();
00569 if (!r)
00570 m_pixmapRequestsStack.pop_back();
00571
00572
00573 else if ( r->page()->hasPixmap( r->id(), r->width(), r->height() ) || r->id() <= 0 || r->id() >= MAX_OBSERVER_ID)
00574 {
00575 m_pixmapRequestsStack.pop_back();
00576 delete r;
00577 }
00578 else if ( (long)r->width() * (long)r->height() > 20000000L )
00579 {
00580 m_pixmapRequestsStack.pop_back();
00581 if ( !m_warnedOutOfMemory )
00582 {
00583 kWarning(OkularDebug).nospace() << "Running out of memory on page " << r->pageNumber()
00584 << " (" << r->width() << "x" << r->height() << " px);";
00585 kWarning(OkularDebug) << "this message will be reported only once.";
00586 m_warnedOutOfMemory = true;
00587 }
00588 delete r;
00589 }
00590 else
00591 request = r;
00592 }
00593
00594
00595 if ( !request )
00596 {
00597 m_pixmapRequestsMutex.unlock();
00598 return;
00599 }
00600
00601
00602 qulonglong pixmapBytes = 4 * request->width() * request->height();
00603 if ( pixmapBytes > (1024 * 1024) )
00604 cleanupPixmapMemory( pixmapBytes );
00605
00606
00607 if ( m_generator->canGeneratePixmap() )
00608 {
00609 kDebug(OkularDebug).nospace() << "sending request id=" << request->id() << " " <<request->width() << "x" << request->height() << "@" << request->pageNumber() << " async == " << request->asynchronous();
00610 m_pixmapRequestsStack.removeAll ( request );
00611
00612 if ( (int)m_rotation % 2 )
00613 request->d->swap();
00614
00615
00616
00617
00618 m_executingPixmapRequests.push_back( request );
00619 m_pixmapRequestsMutex.unlock();
00620 m_generator->generatePixmap( request );
00621 }
00622 else
00623 {
00624 m_pixmapRequestsMutex.unlock();
00625
00626 QTimer::singleShot( 30, m_parent, SLOT(sendGeneratorRequest()) );
00627 }
00628 }
00629
00630 void DocumentPrivate::rotationFinished( int page )
00631 {
00632 QMap< int, DocumentObserver * >::const_iterator it = m_observers.begin(), end = m_observers.end();
00633 for ( ; it != end ; ++ it ) {
00634 (*it)->notifyPageChanged( page, DocumentObserver::Pixmap | DocumentObserver::Annotations );
00635 }
00636 }
00637
00638 void DocumentPrivate::fontReadingProgress( int page )
00639 {
00640 emit m_parent->fontReadingProgress( page );
00641
00642 if ( page >= (int)m_parent->pages() - 1 )
00643 {
00644 emit m_parent->fontReadingEnded();
00645 m_fontThread = 0;
00646 m_fontsCached = true;
00647 }
00648 }
00649
00650 void DocumentPrivate::fontReadingGotFont( const Okular::FontInfo& font )
00651 {
00652
00653 m_fontsCache.append( font );
00654
00655 emit m_parent->gotFont( font );
00656 }
00657
00658 void DocumentPrivate::slotGeneratorConfigChanged( const QString& )
00659 {
00660 if ( !m_generator )
00661 return;
00662
00663
00664 bool configchanged = false;
00665 QHash< QString, GeneratorInfo >::iterator it = m_loadedGenerators.begin(), itEnd = m_loadedGenerators.end();
00666 for ( ; it != itEnd; ++it )
00667 {
00668 Okular::ConfigInterface * iface = generatorConfig( it.value() );
00669 if ( iface )
00670 {
00671 bool it_changed = iface->reparseConfig();
00672 if ( it_changed && ( m_generator == it.value().generator ) )
00673 configchanged = true;
00674 }
00675 }
00676 if ( configchanged )
00677 {
00678
00679 QVector<Page*>::const_iterator it = m_pagesVector.begin(), end = m_pagesVector.end();
00680 for ( ; it != end; ++it ) {
00681 (*it)->deletePixmaps();
00682 }
00683
00684
00685 QLinkedList< AllocatedPixmap * >::const_iterator aIt = m_allocatedPixmapsFifo.begin();
00686 QLinkedList< AllocatedPixmap * >::const_iterator aEnd = m_allocatedPixmapsFifo.end();
00687 for ( ; aIt != aEnd; ++aIt )
00688 delete *aIt;
00689 m_allocatedPixmapsFifo.clear();
00690 m_allocatedPixmapsTotalMemory = 0;
00691
00692
00693 foreachObserverD( notifyContentsCleared( DocumentObserver::Pixmap ) );
00694 }
00695
00696
00697 if ( Settings::memoryLevel() == Settings::EnumMemoryLevel::Low &&
00698 !m_allocatedPixmapsFifo.isEmpty() && !m_pagesVector.isEmpty() )
00699 cleanupPixmapMemory();
00700 }
00701
00702 void DocumentPrivate::doContinueNextMatchSearch(void *pagesToNotifySet, void * theMatch, int currentPage, int searchID, const QString & text, int theCaseSensitivity, bool moveViewport, const QColor & color, bool noDialogs, int donePages)
00703 {
00704 RegularAreaRect * match = static_cast<RegularAreaRect *>(theMatch);
00705 Qt::CaseSensitivity caseSensitivity = static_cast<Qt::CaseSensitivity>(theCaseSensitivity);
00706 QSet< int > *pagesToNotify = static_cast< QSet< int > * >( pagesToNotifySet );
00707
00708 if (m_searchCancelled && !match)
00709 {
00710
00711 QApplication::restoreOverrideCursor();
00712 emit m_parent->searchFinished( searchID, Document::SearchCancelled );
00713 delete pagesToNotify;
00714 return;
00715 }
00716
00717
00718 if ( !match )
00719 {
00720 int pageCount = m_pagesVector.count();
00721 if (donePages < pageCount)
00722 {
00723 bool doContinue = true;
00724 if ( currentPage >= pageCount )
00725 {
00726 if ( noDialogs || KMessageBox::questionYesNo(m_parent->widget(), i18n("End of document reached.\nContinue from the beginning?"), QString(), KStandardGuiItem::cont(), KStandardGuiItem::cancel()) == KMessageBox::Yes )
00727 currentPage = 0;
00728 else
00729 doContinue = false;
00730 }
00731 if (doContinue)
00732 {
00733
00734 Page * page = m_pagesVector[ currentPage ];
00735
00736 if ( !page->hasTextPage() )
00737 m_parent->requestTextPage( page->number() );
00738
00739 match = page->findText( searchID, text, FromTop, caseSensitivity );
00740
00741 if ( !match ) currentPage++;
00742
00743 QMetaObject::invokeMethod(m_parent, "doContinueNextMatchSearch", Qt::QueuedConnection, Q_ARG(void *, pagesToNotifySet), Q_ARG(void *, match), Q_ARG(int, currentPage), Q_ARG(int, searchID), Q_ARG(QString, text), Q_ARG(int, caseSensitivity), Q_ARG(bool, moveViewport), Q_ARG(QColor, color), Q_ARG(bool, noDialogs), Q_ARG(int, donePages +1));
00744 return;
00745 }
00746 }
00747 }
00748
00749
00750 QApplication::restoreOverrideCursor();
00751
00752 bool foundAMatch = false;
00753
00754
00755 if ( match )
00756 {
00757
00758 RunningSearch * s = m_searches[searchID];
00759 foundAMatch = true;
00760 s->continueOnPage = currentPage;
00761 s->continueOnMatch = *match;
00762 s->highlightedPages.insert( currentPage );
00763
00764 m_pagesVector[ currentPage ]->d->setHighlight( searchID, match, color );
00765
00766
00767 pagesToNotify->insert( currentPage );
00768
00769
00770 if ( moveViewport )
00771 {
00772 DocumentViewport searchViewport( currentPage );
00773 searchViewport.rePos.enabled = true;
00774 searchViewport.rePos.normalizedX = (match->first().left + match->first().right) / 2.0;
00775 searchViewport.rePos.normalizedY = (match->first().top + match->first().bottom) / 2.0;
00776 m_parent->setViewport( searchViewport, -1, true );
00777 }
00778 delete match;
00779 }
00780 else if ( !noDialogs )
00781 KMessageBox::information( m_parent->widget(), i18n( "No matches found for '%1'.", text ) );
00782
00783
00784 foreach(int pageNumber, *pagesToNotify)
00785 foreach(DocumentObserver *observer, m_observers)
00786 observer->notifyPageChanged( pageNumber, DocumentObserver::Highlights );
00787
00788 if (foundAMatch) emit m_parent->searchFinished( searchID, Document::MatchFound );
00789 else emit m_parent->searchFinished( searchID, Document::NoMatchFound );
00790
00791 delete pagesToNotify;
00792 }
00793
00794 void DocumentPrivate::doContinueAllDocumentSearch(void *pagesToNotifySet, void *pageMatchesMap, int currentPage, int searchID, const QString & text, int theCaseSensitivity, const QColor & color)
00795 {
00796 QMap< Page *, QVector<RegularAreaRect *> > *pageMatches = static_cast< QMap< Page *, QVector<RegularAreaRect *> > * >(pageMatchesMap);
00797 Qt::CaseSensitivity caseSensitivity = static_cast<Qt::CaseSensitivity>(theCaseSensitivity);
00798 QSet< int > *pagesToNotify = static_cast< QSet< int > * >( pagesToNotifySet );
00799
00800 if (m_searchCancelled)
00801 {
00802 typedef QVector<RegularAreaRect *> MatchesVector;
00803
00804 QApplication::restoreOverrideCursor();
00805 emit m_parent->searchFinished( searchID, Document::SearchCancelled );
00806 foreach(const MatchesVector &mv, *pageMatches) qDeleteAll(mv);
00807 delete pageMatches;
00808 delete pagesToNotify;
00809 return;
00810 }
00811
00812 if (currentPage < m_pagesVector.count())
00813 {
00814
00815 Page *page = m_pagesVector.at(currentPage);
00816 int pageNumber = page->number();
00817
00818
00819 if ( !page->hasTextPage() )
00820 m_parent->requestTextPage( pageNumber );
00821
00822
00823 RegularAreaRect * lastMatch = 0;
00824 while ( 1 )
00825 {
00826 if ( lastMatch )
00827 lastMatch = page->findText( searchID, text, NextResult, caseSensitivity, lastMatch );
00828 else
00829 lastMatch = page->findText( searchID, text, FromTop, caseSensitivity );
00830
00831 if ( !lastMatch )
00832 break;
00833
00834
00835 (*pageMatches)[page].append(lastMatch);
00836 }
00837 delete lastMatch;
00838
00839 QMetaObject::invokeMethod(m_parent, "doContinueAllDocumentSearch", Qt::QueuedConnection, Q_ARG(void *, pagesToNotifySet), Q_ARG(void *, pageMatches), Q_ARG(int, currentPage + 1), Q_ARG(int, searchID), Q_ARG(QString, text), Q_ARG(int, caseSensitivity), Q_ARG(QColor, color));
00840 }
00841 else
00842 {
00843
00844 QApplication::restoreOverrideCursor();
00845
00846 RunningSearch * s = m_searches[searchID];
00847 bool foundAMatch = pageMatches->count() != 0;
00848 QMap< Page *, QVector<RegularAreaRect *> >::const_iterator it, itEnd;
00849 it = pageMatches->begin();
00850 itEnd = pageMatches->end();
00851 for ( ; it != itEnd; ++it)
00852 {
00853 foreach(RegularAreaRect *match, it.value())
00854 {
00855 it.key()->d->setHighlight( searchID, match, color );
00856 delete match;
00857 }
00858 s->highlightedPages.insert( it.key()->number() );
00859 pagesToNotify->insert( it.key()->number() );
00860 }
00861
00862 foreach(DocumentObserver *observer, m_observers)
00863 observer->notifySetup( m_pagesVector, 0 );
00864
00865
00866 foreach(int pageNumber, *pagesToNotify)
00867 foreach(DocumentObserver *observer, m_observers)
00868 observer->notifyPageChanged( pageNumber, DocumentObserver::Highlights );
00869
00870 if (foundAMatch) emit m_parent->searchFinished(searchID, Document::MatchFound );
00871 else emit m_parent->searchFinished( searchID, Document::NoMatchFound );
00872
00873 delete pageMatches;
00874 delete pagesToNotify;
00875 }
00876 }
00877
00878 void DocumentPrivate::doContinueGooglesDocumentSearch(void *pagesToNotifySet, void *pageMatchesMap, int currentPage, int searchID, const QString & text, int theCaseSensitivity, const QColor & color, bool matchAll)
00879 {
00880 typedef QPair<RegularAreaRect *, QColor> MatchColor;
00881 QMap< Page *, QVector<MatchColor> > *pageMatches = static_cast< QMap< Page *, QVector<MatchColor> > * >(pageMatchesMap);
00882 Qt::CaseSensitivity caseSensitivity = static_cast<Qt::CaseSensitivity>(theCaseSensitivity);
00883 QSet< int > *pagesToNotify = static_cast< QSet< int > * >( pagesToNotifySet );
00884
00885 if (m_searchCancelled)
00886 {
00887 typedef QVector<MatchColor> MatchesVector;
00888
00889 QApplication::restoreOverrideCursor();
00890 emit m_parent->searchFinished( searchID, Document::SearchCancelled );
00891
00892 foreach(const MatchesVector &mv, *pageMatches)
00893 {
00894 foreach(const MatchColor &mc, mv) delete mc.first;
00895 }
00896 delete pageMatches;
00897 delete pagesToNotify;
00898 return;
00899 }
00900
00901 QStringList words = text.split( " ", QString::SkipEmptyParts );
00902 const int wordCount = words.count();
00903 const int hueStep = (wordCount > 1) ? (60 / (wordCount - 1)) : 60;
00904 int baseHue, baseSat, baseVal;
00905 color.getHsv( &baseHue, &baseSat, &baseVal );
00906
00907 if (currentPage < m_pagesVector.count())
00908 {
00909
00910 Page *page = m_pagesVector.at(currentPage);
00911 int pageNumber = page->number();
00912
00913
00914 if ( !page->hasTextPage() )
00915 m_parent->requestTextPage( pageNumber );
00916
00917
00918 bool allMatched = wordCount > 0,
00919 anyMatched = false;
00920 for ( int w = 0; w < wordCount; w++ )
00921 {
00922 const QString &word = words[ w ];
00923 int newHue = baseHue - w * hueStep;
00924 if ( newHue < 0 )
00925 newHue += 360;
00926 QColor wordColor = QColor::fromHsv( newHue, baseSat, baseVal );
00927 RegularAreaRect * lastMatch = 0;
00928
00929 bool wordMatched = false;
00930 while ( 1 )
00931 {
00932 if ( lastMatch )
00933 lastMatch = page->findText( searchID, word, NextResult, caseSensitivity, lastMatch );
00934 else
00935 lastMatch = page->findText( searchID, word, FromTop, caseSensitivity);
00936
00937 if ( !lastMatch )
00938 break;
00939
00940
00941 (*pageMatches)[page].append(MatchColor(lastMatch, wordColor));
00942 wordMatched = true;
00943 }
00944 allMatched = allMatched && wordMatched;
00945 anyMatched = anyMatched || wordMatched;
00946 }
00947
00948
00949 if ( !allMatched && matchAll )
00950 {
00951 QVector<MatchColor> &matches = (*pageMatches)[page];
00952 foreach(const MatchColor &mc, matches) delete mc.first;
00953 pageMatches->remove(page);
00954 }
00955
00956 QMetaObject::invokeMethod(m_parent, "doContinueGooglesDocumentSearch", Qt::QueuedConnection, Q_ARG(void *, pagesToNotifySet), Q_ARG(void *, pageMatches), Q_ARG(int, currentPage + 1), Q_ARG(int, searchID), Q_ARG(QString, text), Q_ARG(int, caseSensitivity), Q_ARG(QColor, color), Q_ARG(bool, matchAll));
00957 }
00958 else
00959 {
00960
00961 QApplication::restoreOverrideCursor();
00962
00963 RunningSearch * s = m_searches[searchID];
00964 bool foundAMatch = pageMatches->count() != 0;
00965 QMap< Page *, QVector<MatchColor> >::const_iterator it, itEnd;
00966 it = pageMatches->begin();
00967 itEnd = pageMatches->end();
00968 for ( ; it != itEnd; ++it)
00969 {
00970 foreach(const MatchColor &mc, it.value())
00971 {
00972 it.key()->d->setHighlight( searchID, mc.first, mc.second );
00973 delete mc.first;
00974 }
00975 s->highlightedPages.insert( it.key()->number() );
00976 pagesToNotify->insert( it.key()->number() );
00977 }
00978
00979
00980 foreach(DocumentObserver *observer, m_observers)
00981 observer->notifySetup( m_pagesVector, 0 );
00982
00983
00984 foreach(int pageNumber, *pagesToNotify)
00985 foreach(DocumentObserver *observer, m_observers)
00986 observer->notifyPageChanged( pageNumber, DocumentObserver::Highlights );
00987
00988 if (foundAMatch) emit m_parent->searchFinished( searchID, Document::MatchFound );
00989 else emit m_parent->searchFinished( searchID, Document::NoMatchFound );
00990
00991 delete pageMatches;
00992 delete pagesToNotify;
00993 }
00994 }
00995
00996 QVariant DocumentPrivate::documentMetaData( const QString &key, const QVariant &option ) const
00997 {
00998 if ( key == QLatin1String( "PaperColor" ) )
00999 {
01000 bool giveDefault = option.toBool();
01001
01002
01003 QColor color;
01004 if ( ( Settings::renderMode() == Settings::EnumRenderMode::Paper )
01005 && Settings::changeColors() )
01006 {
01007 color = Settings::paperColor();
01008 }
01009 else if ( giveDefault )
01010 {
01011 color = Qt::white;
01012 }
01013 return color;
01014 }
01015 else if ( key == QLatin1String( "ZoomFactor" ) )
01016 {
01017 return Settings::zoomFactor();
01018 }
01019 else if ( key == QLatin1String( "TextAntialias" ) )
01020 {
01021
01022
01023 return true;
01024 }
01025 else if ( key == QLatin1String( "GraphicsAntialias" ) )
01026 {
01027
01028 return true;
01029 }
01030 return QVariant();
01031 }
01032
01033
01034 Document::Document( QWidget *widget )
01035 : QObject( widget ), d( new DocumentPrivate( this ) )
01036 {
01037 d->m_bookmarkManager = new BookmarkManager( d );
01038 d->m_viewportIterator = d->m_viewportHistory.insert( d->m_viewportHistory.end(), DocumentViewport() );
01039
01040 connect( PageController::self(), SIGNAL( rotationFinished( int ) ),
01041 this, SLOT( rotationFinished( int ) ) );
01042
01043 qRegisterMetaType<Okular::FontInfo>();
01044 }
01045
01046 Document::~Document()
01047 {
01048
01049 AudioPlayer::instance()->stopPlaybacks();
01050
01051
01052 closeDocument();
01053
01054
01055 delete d->m_bookmarkManager;
01056
01057
01058 QHash< QString, GeneratorInfo >::const_iterator it = d->m_loadedGenerators.constBegin(), itEnd = d->m_loadedGenerators.constEnd();
01059 for ( ; it != itEnd; ++it )
01060 d->unloadGenerator( it.value() );
01061 d->m_loadedGenerators.clear();
01062
01063
01064 delete d;
01065 }
01066
01067 static bool kserviceMoreThan( const KService::Ptr &s1, const KService::Ptr &s2 )
01068 {
01069 return s1->property( "X-KDE-Priority" ).toInt() > s2->property( "X-KDE-Priority" ).toInt();
01070 }
01071
01072 bool Document::openDocument( const QString & docFile, const KUrl& url, const KMimeType::Ptr &_mime )
01073 {
01074 KMimeType::Ptr mime = _mime;
01075 QByteArray filedata;
01076 qint64 document_size = -1;
01077 bool isstdin = url.fileName( KUrl::ObeyTrailingSlash ) == QLatin1String( "-" );
01078 if ( !isstdin )
01079 {
01080 if ( mime.count() <= 0 )
01081 return false;
01082
01083
01084 QFile fileReadTest( docFile );
01085 if ( !fileReadTest.open( QIODevice::ReadOnly ) )
01086 {
01087 d->m_docFileName.clear();
01088 return false;
01089 }
01090
01091 d->m_url = url;
01092 d->m_docFileName = docFile;
01093 if ( url.isLocalFile() )
01094 {
01095 QString fn = docFile.contains('/') ? docFile.section('/', -1, -1) : docFile;
01096 document_size = fileReadTest.size();
01097 fn = QString::number( document_size ) + '.' + fn + ".xml";
01098 fileReadTest.close();
01099 QString newokular = "okular/docdata/" + fn;
01100 QString newokularfile = KStandardDirs::locateLocal( "data", newokular );
01101 if ( !QFile::exists( newokularfile ) )
01102 {
01103 QString oldkpdf = "kpdf/" + fn;
01104 QString oldkpdffile = KStandardDirs::locateLocal( "data", oldkpdf );
01105 if ( QFile::exists( oldkpdffile ) )
01106 {
01107
01108 if ( !QFile::copy( oldkpdffile, newokularfile ) )
01109 return false;
01110 }
01111 }
01112 d->m_xmlFileName = newokularfile;
01113 }
01114 }
01115 else
01116 {
01117 QFile qstdin;
01118 qstdin.open( stdin, QIODevice::ReadOnly );
01119 filedata = qstdin.readAll();
01120 mime = KMimeType::findByContent( filedata );
01121 if ( !mime || mime->name() == QLatin1String( "application/octet-stream" ) )
01122 return false;
01123 document_size = filedata.size();
01124 }
01125
01126
01127
01128 QString constraint("([X-KDE-Priority] > 0) and (exist Library)") ;
01129 KService::List offers = KMimeTypeTrader::self()->query(mime->name(),"okular/Generator",constraint);
01130 if (offers.isEmpty())
01131 {
01132 emit error( i18n( "Can not find a plugin which is able to handle the passed document." ), -1 );
01133 kWarning(OkularDebug).nospace() << "No plugin for mimetype '" << mime->name() << "'.";
01134 return false;
01135 }
01136 int hRank=0;
01137
01138 int offercount = offers.count();
01139 if ( offercount > 1 )
01140 {
01141
01142 qStableSort( offers.begin(), offers.end(), kserviceMoreThan );
01143
01144 if ( Settings::chooseGenerators() )
01145 {
01146 QStringList list;
01147 for ( int i = 0; i < offercount; ++i )
01148 {
01149 list << offers.at(i)->name();
01150 }
01151
01152 ChooseEngineDialog choose( list, mime, widget() );
01153
01154 if ( choose.exec() == QDialog::Rejected )
01155 return false;
01156
01157 hRank = choose.selectedGenerator();
01158 }
01159 }
01160
01161 QString propName = offers.at(hRank)->name();
01162 QHash< QString, GeneratorInfo >::const_iterator genIt = d->m_loadedGenerators.constFind( propName );
01163 QString catalogName;
01164 if ( genIt != d->m_loadedGenerators.constEnd() )
01165 {
01166 d->m_generator = genIt.value().generator;
01167 catalogName = genIt.value().catalogName;
01168 }
01169 else
01170 {
01171 d->m_generator = d->loadGeneratorLibrary( offers.at(hRank) );
01172 if ( !d->m_generator )
01173 return false;
01174 genIt = d->m_loadedGenerators.constFind( propName );
01175 Q_ASSERT( genIt != d->m_loadedGenerators.constEnd() );
01176 catalogName = genIt.value().catalogName;
01177 }
01178 Q_ASSERT_X( d->m_generator, "Document::load()", "null generator?!" );
01179
01180 if ( !catalogName.isEmpty() )
01181 KGlobal::locale()->insertCatalog( catalogName );
01182
01183 d->m_generator->d_func()->m_document = d;
01184
01185
01186 connect( d->m_generator, SIGNAL( error( const QString&, int ) ), this, SIGNAL( error( const QString&, int ) ) );
01187 connect( d->m_generator, SIGNAL( warning( const QString&, int ) ), this, SIGNAL( warning( const QString&, int ) ) );
01188 connect( d->m_generator, SIGNAL( notice( const QString&, int ) ), this, SIGNAL( notice( const QString&, int ) ) );
01189
01190
01191 QApplication::setOverrideCursor( Qt::WaitCursor );
01192 bool openOk = false;
01193 if ( !isstdin )
01194 {
01195 openOk = d->m_generator->loadDocument( docFile, d->m_pagesVector );
01196 }
01197 else if ( !filedata.isEmpty() )
01198 {
01199 if ( d->m_generator->hasFeature( Generator::ReadRawData ) )
01200 {
01201 openOk = d->m_generator->loadDocumentFromData( filedata, d->m_pagesVector );
01202 }
01203 else
01204 {
01205 d->m_tempFile = new KTemporaryFile();
01206 if ( !d->m_tempFile->open() )
01207 {
01208 delete d->m_tempFile;
01209 d->m_tempFile = 0;
01210 }
01211 else
01212 {
01213 d->m_tempFile->write( filedata );
01214 QString tmpFileName = d->m_tempFile->fileName();
01215 d->m_tempFile->close();
01216 openOk = d->m_generator->loadDocument( tmpFileName, d->m_pagesVector );
01217 }
01218 }
01219 }
01220
01221 QApplication::restoreOverrideCursor();
01222 if ( !openOk || d->m_pagesVector.size() <= 0 )
01223 {
01224 if ( !catalogName.isEmpty() )
01225 KGlobal::locale()->removeCatalog( catalogName );
01226
01227 d->m_generator = 0;
01228 return openOk;
01229 }
01230
01231 d->m_generatorName = propName;
01232
01233
01234 d->loadDocumentInfo();
01235 d->m_bookmarkManager->setUrl( d->m_url );
01236
01237
01238 foreachObserver( notifySetup( d->m_pagesVector, DocumentObserver::DocumentChanged ) );
01239
01240
01241 DocumentViewport loadedViewport = (*d->m_viewportIterator);
01242 if ( loadedViewport.isValid() )
01243 {
01244 (*d->m_viewportIterator) = DocumentViewport();
01245 if ( loadedViewport.pageNumber >= (int)d->m_pagesVector.size() )
01246 loadedViewport.pageNumber = d->m_pagesVector.size() - 1;
01247 }
01248 else
01249 loadedViewport.pageNumber = 0;
01250 setViewport( loadedViewport );
01251
01252
01253 if ( !d->m_saveBookmarksTimer )
01254 {
01255 d->m_saveBookmarksTimer = new QTimer( this );
01256 connect( d->m_saveBookmarksTimer, SIGNAL( timeout() ), this, SLOT( saveDocumentInfo() ) );
01257 }
01258 d->m_saveBookmarksTimer->start( 5 * 60 * 1000 );
01259
01260
01261 if ( !d->m_memCheckTimer )
01262 {
01263 d->m_memCheckTimer = new QTimer( this );
01264 connect( d->m_memCheckTimer, SIGNAL( timeout() ), this, SLOT( slotTimedMemoryCheck() ) );
01265 }
01266 d->m_memCheckTimer->start( 2000 );
01267
01268 if (d->m_nextDocumentViewport.isValid())
01269 {
01270 setViewport(d->m_nextDocumentViewport);
01271 d->m_nextDocumentViewport = DocumentViewport();
01272 }
01273
01274 AudioPlayer::instance()->d->m_currentDocument = isstdin ? KUrl() : d->m_url;
01275 d->m_docSize = document_size;
01276
01277 return true;
01278 }
01279
01280
01281 KXMLGUIClient* Document::guiClient()
01282 {
01283 if ( d->m_generator )
01284 {
01285 Okular::GuiInterface * iface = qobject_cast< Okular::GuiInterface * >( d->m_generator );
01286 if ( iface )
01287 return iface->guiClient();
01288 }
01289 return 0;
01290 }
01291
01292 void Document::closeDocument()
01293 {
01294
01295 if ( !d->m_generator )
01296 return;
01297
01298 QEventLoop loop;
01299 bool startEventLoop = false;
01300 do
01301 {
01302 d->m_pixmapRequestsMutex.lock();
01303 startEventLoop = !d->m_executingPixmapRequests.isEmpty();
01304 d->m_pixmapRequestsMutex.unlock();
01305 if ( startEventLoop )
01306 {
01307 d->m_closingLoop = &loop;
01308 loop.exec();
01309 d->m_closingLoop = 0;
01310 }
01311 }
01312 while ( startEventLoop );
01313
01314 if ( d->m_fontThread )
01315 {
01316 disconnect( d->m_fontThread, 0, this, 0 );
01317 d->m_fontThread->stopExtraction();
01318 d->m_fontThread->wait();
01319 d->m_fontThread = 0;
01320 }
01321
01322
01323 if ( d->m_generator && d->m_pagesVector.size() > 0 )
01324 {
01325 d->saveDocumentInfo();
01326 d->m_generator->closeDocument();
01327 }
01328
01329
01330 if ( d->m_memCheckTimer )
01331 d->m_memCheckTimer->stop();
01332 if ( d->m_saveBookmarksTimer )
01333 d->m_saveBookmarksTimer->stop();
01334
01335 if ( d->m_generator )
01336 {
01337
01338 d->m_generator->d_func()->m_document = 0;
01339
01340 disconnect( d->m_generator, 0, this, 0 );
01341
01342 QHash< QString, GeneratorInfo >::const_iterator genIt = d->m_loadedGenerators.constFind( d->m_generatorName );
01343 Q_ASSERT( genIt != d->m_loadedGenerators.constEnd() );
01344 if ( !genIt.value().catalogName.isEmpty() && !genIt.value().config )
01345 KGlobal::locale()->removeCatalog( genIt.value().catalogName );
01346 }
01347 d->m_generator = 0;
01348 d->m_generatorName = QString();
01349 d->m_url = KUrl();
01350 d->m_docFileName = QString();
01351 d->m_xmlFileName = QString();
01352 delete d->m_tempFile;
01353 d->m_tempFile = 0;
01354 d->m_docSize = -1;
01355 d->m_exportCached = false;
01356 d->m_exportFormats.clear();
01357 d->m_exportToText = ExportFormat();
01358 d->m_fontsCached = false;
01359 d->m_fontsCache.clear();
01360 d->m_rotation = Rotation0;
01361
01362 d->m_pixmapRequestsMutex.lock();
01363 QLinkedList< PixmapRequest * >::const_iterator sIt = d->m_pixmapRequestsStack.begin();
01364 QLinkedList< PixmapRequest * >::const_iterator sEnd = d->m_pixmapRequestsStack.end();
01365 for ( ; sIt != sEnd; ++sIt )
01366 delete *sIt;
01367 d->m_pixmapRequestsStack.clear();
01368 d->m_pixmapRequestsMutex.unlock();
01369
01370
01371 foreachObserver( notifySetup( QVector< Page * >(), DocumentObserver::DocumentChanged ) );
01372
01373
01374 QVector< Page * >::const_iterator pIt = d->m_pagesVector.begin();
01375 QVector< Page * >::const_iterator pEnd = d->m_pagesVector.end();
01376 for ( ; pIt != pEnd; ++pIt )
01377 delete *pIt;
01378 d->m_pagesVector.clear();
01379
01380
01381 QLinkedList< AllocatedPixmap * >::const_iterator aIt = d->m_allocatedPixmapsFifo.begin();
01382 QLinkedList< AllocatedPixmap * >::const_iterator aEnd = d->m_allocatedPixmapsFifo.end();
01383 for ( ; aIt != aEnd; ++aIt )
01384 delete *aIt;
01385 d->m_allocatedPixmapsFifo.clear();
01386
01387
01388 QMap< int, RunningSearch * >::const_iterator rIt = d->m_searches.begin();
01389 QMap< int, RunningSearch * >::const_iterator rEnd = d->m_searches.end();
01390 for ( ; rIt != rEnd; ++rIt )
01391 delete *rIt;
01392 d->m_searches.clear();
01393
01394
01395 QVector< VisiblePageRect * >::const_iterator vIt = d->m_pageRects.begin();
01396 QVector< VisiblePageRect * >::const_iterator vEnd = d->m_pageRects.end();
01397 for ( ; vIt != vEnd; ++vIt )
01398 delete *vIt;
01399 d->m_pageRects.clear();
01400 foreachObserver( notifyVisibleRectsChanged() );
01401
01402
01403
01404 d->m_viewportHistory.clear();
01405 d->m_viewportHistory.append( DocumentViewport() );
01406 d->m_viewportIterator = d->m_viewportHistory.begin();
01407 d->m_allocatedPixmapsTotalMemory = 0;
01408 d->m_pageSize = PageSize();
01409 d->m_pageSizes.clear();
01410 AudioPlayer::instance()->d->m_currentDocument = KUrl();
01411 }
01412
01413 void Document::addObserver( DocumentObserver * pObserver )
01414 {
01415
01416 d->m_observers.insert( pObserver->observerId(), pObserver );
01417
01418
01419 if ( !d->m_pagesVector.isEmpty() )
01420 {
01421 pObserver->notifySetup( d->m_pagesVector, DocumentObserver::DocumentChanged );
01422 pObserver->notifyViewportChanged( false );
01423 }
01424 }
01425
01426 void Document::removeObserver( DocumentObserver * pObserver )
01427 {
01428
01429 if ( d->m_observers.contains( pObserver->observerId() ) )
01430 {
01431
01432 int observerId = pObserver->observerId();
01433 QVector<Page*>::const_iterator it = d->m_pagesVector.begin(), end = d->m_pagesVector.end();
01434 for ( ; it != end; ++it )
01435 (*it)->deletePixmap( observerId );
01436
01437
01438 QLinkedList< AllocatedPixmap * >::iterator aIt = d->m_allocatedPixmapsFifo.begin();
01439 QLinkedList< AllocatedPixmap * >::iterator aEnd = d->m_allocatedPixmapsFifo.end();
01440 while ( aIt != aEnd )
01441 {
01442 AllocatedPixmap * p = *aIt;
01443 if ( p->id == observerId )
01444 {
01445 aIt = d->m_allocatedPixmapsFifo.erase( aIt );
01446 delete p;
01447 }
01448 else
01449 ++aIt;
01450 }
01451
01452
01453 d->m_observers.remove( observerId );
01454 }
01455 }
01456
01457 void Document::reparseConfig()
01458 {
01459
01460 bool configchanged = false;
01461 if ( d->m_generator )
01462 {
01463 Okular::ConfigInterface * iface = qobject_cast< Okular::ConfigInterface * >( d->m_generator );
01464 if ( iface )
01465 configchanged = iface->reparseConfig();
01466 }
01467 if ( configchanged )
01468 {
01469
01470 QVector<Page*>::const_iterator it = d->m_pagesVector.begin(), end = d->m_pagesVector.end();
01471 for ( ; it != end; ++it ) {
01472 (*it)->deletePixmaps();
01473 }
01474
01475
01476 QLinkedList< AllocatedPixmap * >::const_iterator aIt = d->m_allocatedPixmapsFifo.begin();
01477 QLinkedList< AllocatedPixmap * >::const_iterator aEnd = d->m_allocatedPixmapsFifo.end();
01478 for ( ; aIt != aEnd; ++aIt )
01479 delete *aIt;
01480 d->m_allocatedPixmapsFifo.clear();
01481 d->m_allocatedPixmapsTotalMemory = 0;
01482
01483
01484 foreachObserver( notifyContentsCleared( DocumentObserver::Pixmap ) );
01485 }
01486
01487
01488 if ( Settings::memoryLevel() == Settings::EnumMemoryLevel::Low &&
01489 !d->m_allocatedPixmapsFifo.isEmpty() && !d->m_pagesVector.isEmpty() )
01490 d->cleanupPixmapMemory();
01491 }
01492
01493
01494 QWidget *Document::widget() const
01495 {
01496 return parent() ? static_cast< QWidget * >( parent() ) : 0;
01497 }
01498
01499 bool Document::isOpened() const
01500 {
01501 return d->m_generator;
01502 }
01503
01504 bool Document::canConfigurePrinter( ) const
01505 {
01506 if ( d->m_generator )
01507 {
01508 Okular::PrintInterface * iface = qobject_cast< Okular::PrintInterface * >( d->m_generator );
01509 return iface ? true : false;
01510 }
01511 else
01512 return 0;
01513 }
01514
01515 const DocumentInfo * Document::documentInfo() const
01516 {
01517 if ( d->m_generator )
01518 {
01519 const DocumentInfo *infoConst = d->m_generator->generateDocumentInfo();
01520 if ( !infoConst )
01521 return 0;
01522
01523 DocumentInfo *info = const_cast< DocumentInfo * >( infoConst );
01524 QString pagesSize = d->pagesSizeString();
01525 if ( d->m_docSize != -1 )
01526 {
01527 QString sizeString = KGlobal::locale()->formatByteSize( d->m_docSize );
01528 info->set( "documentSize", sizeString, i18n( "File Size" ) );
01529 }
01530 if (!pagesSize.isEmpty())
01531 {
01532 info->set( "pagesSize", pagesSize, i18n("Pages Size") );
01533 }
01534 return info;
01535 }
01536 else return NULL;
01537 }
01538
01539 const DocumentSynopsis * Document::documentSynopsis() const
01540 {
01541 return d->m_generator ? d->m_generator->generateDocumentSynopsis() : NULL;
01542 }
01543
01544 void Document::startFontReading()
01545 {
01546 if ( !d->m_generator || !d->m_generator->hasFeature( Generator::FontInfo ) || d->m_fontThread )
01547 return;
01548
01549 if ( d->m_fontsCached )
01550 {
01551
01552
01553
01554 for ( int i = 0; i < d->m_fontsCache.count(); ++i )
01555 {
01556 emit gotFont( d->m_fontsCache.at( i ) );
01557 emit fontReadingProgress( i / pages() );
01558 }
01559 emit fontReadingEnded();
01560 return;
01561 }
01562
01563 d->m_fontThread = new FontExtractionThread( d->m_generator, pages() );
01564 connect( d->m_fontThread, SIGNAL( gotFont( const Okular::FontInfo& ) ), this, SLOT( fontReadingGotFont( const Okular::FontInfo& ) ) );
01565 connect( d->m_fontThread, SIGNAL( progress( int ) ), this, SLOT( fontReadingProgress( int ) ) );
01566
01567 d->m_fontThread->startExtraction( true );
01568 }
01569
01570 void Document::stopFontReading()
01571 {
01572 if ( !d->m_fontThread )
01573 return;
01574
01575 disconnect( d->m_fontThread, 0, this, 0 );
01576 d->m_fontThread->stopExtraction();
01577 d->m_fontThread = 0;
01578 d->m_fontsCache.clear();
01579 }
01580
01581 bool Document::canProvideFontInformation() const
01582 {
01583 return d->m_generator ? d->m_generator->hasFeature( Generator::FontInfo ) : false;
01584 }
01585
01586 const QList<EmbeddedFile*> *Document::embeddedFiles() const
01587 {
01588 return d->m_generator ? d->m_generator->embeddedFiles() : NULL;
01589 }
01590
01591 const Page * Document::page( int n ) const
01592 {
01593 return ( n < d->m_pagesVector.count() ) ? d->m_pagesVector.at(n) : 0;
01594 }
01595
01596 const DocumentViewport & Document::viewport() const
01597 {
01598 return (*d->m_viewportIterator);
01599 }
01600
01601 const QVector< VisiblePageRect * > & Document::visiblePageRects() const
01602 {
01603 return d->m_pageRects;
01604 }
01605
01606 void Document::setVisiblePageRects( const QVector< VisiblePageRect * > & visiblePageRects, int excludeId )
01607 {
01608 QVector< VisiblePageRect * >::const_iterator vIt = d->m_pageRects.begin();
01609 QVector< VisiblePageRect * >::const_iterator vEnd = d->m_pageRects.end();
01610 for ( ; vIt != vEnd; ++vIt )
01611 delete *vIt;
01612 d->m_pageRects = visiblePageRects;
01613
01614 QMap< int, DocumentObserver * >::const_iterator it = d->m_observers.begin(), end = d->m_observers.end();
01615 for ( ; it != end ; ++ it )
01616 if ( it.key() != excludeId )
01617 (*it)->notifyVisibleRectsChanged();
01618 }
01619
01620 uint Document::currentPage() const
01621 {
01622 return (*d->m_viewportIterator).pageNumber;
01623 }
01624
01625 uint Document::pages() const
01626 {
01627 return d->m_pagesVector.size();
01628 }
01629
01630 KUrl Document::currentDocument() const
01631 {
01632 return d->m_url;
01633 }
01634
01635 bool Document::isAllowed( Permission action ) const
01636 {
01637 #if !OKULAR_FORCE_DRM
01638 if ( KAuthorized::authorize( "skip_drm" ) && !Okular::Settings::obeyDRM() )
01639 return true;
01640 #endif
01641
01642 return d->m_generator ? d->m_generator->isAllowed( action ) : false;
01643 }
01644
01645 bool Document::supportsSearching() const
01646 {
01647 return d->m_generator ? d->m_generator->hasFeature( Generator::TextExtraction ) : false;
01648 }
01649
01650 bool Document::supportsPageSizes() const
01651 {
01652 return d->m_generator ? d->m_generator->hasFeature( Generator::PageSizes ) : false;
01653 }
01654
01655 PageSize::List Document::pageSizes() const
01656 {
01657 if ( d->m_generator )
01658 {
01659 if ( d->m_pageSizes.isEmpty() )
01660 d->m_pageSizes = d->m_generator->pageSizes();
01661 return d->m_pageSizes;
01662 }
01663 return PageSize::List();
01664 }
01665
01666 bool Document::canExportToText() const
01667 {
01668 if ( !d->m_generator )
01669 return false;
01670
01671 d->cacheExportFormats();
01672 return !d->m_exportToText.isNull();
01673 }
01674
01675 bool Document::exportToText( const QString& fileName ) const
01676 {
01677 if ( !d->m_generator )
01678 return false;
01679
01680 d->cacheExportFormats();
01681 if ( d->m_exportToText.isNull() )
01682 return false;
01683
01684 return d->m_generator->exportTo( fileName, d->m_exportToText );
01685 }
01686
01687 ExportFormat::List Document::exportFormats() const
01688 {
01689 if ( !d->m_generator )
01690 return ExportFormat::List();
01691
01692 d->cacheExportFormats();
01693 return d->m_exportFormats;
01694 }
01695
01696 bool Document::exportTo( const QString& fileName, const ExportFormat& format ) const
01697 {
01698 return d->m_generator ? d->m_generator->exportTo( fileName, format ) : false;
01699 }
01700
01701 bool Document::historyAtBegin() const
01702 {
01703 return d->m_viewportIterator == d->m_viewportHistory.begin();
01704 }
01705
01706 bool Document::historyAtEnd() const
01707 {
01708 return d->m_viewportIterator == --(d->m_viewportHistory.end());
01709 }
01710
01711 QVariant Document::metaData( const QString & key, const QVariant & option ) const
01712 {
01713 return d->m_generator ? d->m_generator->metaData( key, option ) : QVariant();
01714 }
01715
01716 Rotation Document::rotation() const
01717 {
01718 return d->m_rotation;
01719 }
01720
01721 QSizeF Document::allPagesSize() const
01722 {
01723 bool allPagesSameSize = true;
01724 QSizeF size;
01725 for (int i = 0; allPagesSameSize && i < d->m_pagesVector.count(); ++i)
01726 {
01727 const Page *p = d->m_pagesVector.at(i);
01728 if (i == 0) size = QSizeF(p->width(), p->height());
01729 else
01730 {
01731 allPagesSameSize = (size == QSizeF(p->width(), p->height()));
01732 }
01733 }
01734 if (allPagesSameSize) return size;
01735 else return QSizeF();
01736 }
01737
01738 QString Document::pageSizeString(int page) const
01739 {
01740 if (d->m_generator)
01741 {
01742 if (d->m_generator->pagesSizeMetric() != Generator::None)
01743 {
01744 const Page *p = d->m_pagesVector.at( page );
01745 return d->localizedSize(QSizeF(p->width(), p->height()));
01746 }
01747 }
01748 return QString();
01749 }
01750
01751 void Document::requestPixmaps( const QLinkedList< PixmapRequest * > & requests )
01752 {
01753 if ( requests.isEmpty() )
01754 return;
01755
01756 if ( !d->m_generator )
01757 {
01758
01759 QLinkedList< PixmapRequest * >::const_iterator rIt = requests.begin(), rEnd = requests.end();
01760 for ( ; rIt != rEnd; ++rIt )
01761 delete *rIt;
01762
01763 return;
01764 }
01765
01766
01767 int requesterID = requests.first()->id();
01768 d->m_pixmapRequestsMutex.lock();
01769 QLinkedList< PixmapRequest * >::iterator sIt = d->m_pixmapRequestsStack.begin(), sEnd = d->m_pixmapRequestsStack.end();
01770 while ( sIt != sEnd )
01771 {
01772 if ( (*sIt)->id() == requesterID )
01773 {
01774
01775 delete *sIt;
01776 sIt = d->m_pixmapRequestsStack.erase( sIt );
01777 }
01778 else
01779 ++sIt;
01780 }
01781
01782
01783 bool threadingDisabled = !Settings::enableThreading();
01784 QLinkedList< PixmapRequest * >::const_iterator rIt = requests.begin(), rEnd = requests.end();
01785 for ( ; rIt != rEnd; ++rIt )
01786 {
01787
01788 PixmapRequest * request = *rIt;
01789 kDebug(OkularDebug).nospace() << "request id=" << request->id() << " " <<request->width() << "x" << request->height() << "@" << request->pageNumber();
01790 if ( d->m_pagesVector.value( request->pageNumber() ) == 0 )
01791 {
01792
01793 delete request;
01794 continue;
01795 }
01796
01797 request->d->mPage = d->m_pagesVector.value( request->pageNumber() );
01798
01799 if ( !request->asynchronous() )
01800 request->d->mPriority = 0;
01801
01802 if ( request->asynchronous() && threadingDisabled )
01803 request->d->mAsynchronous = false;
01804
01805
01806 if ( !request->priority() )
01807
01808 d->m_pixmapRequestsStack.append( request );
01809 else
01810 {
01811
01812 sIt = d->m_pixmapRequestsStack.begin();
01813 sEnd = d->m_pixmapRequestsStack.end();
01814 while ( sIt != sEnd && (*sIt)->priority() > request->priority() )
01815 ++sIt;
01816 d->m_pixmapRequestsStack.insert( sIt, request );
01817 }
01818 }
01819 d->m_pixmapRequestsMutex.unlock();
01820
01821
01822
01823
01824
01825
01826 d->sendGeneratorRequest();
01827 }
01828
01829 void Document::requestTextPage( uint page )
01830 {
01831 Page * kp = d->m_pagesVector[ page ];
01832 if ( !d->m_generator || !kp )
01833 return;
01834
01835
01836
01837 d->m_generator->generateTextPage( kp );
01838 }
01839
01840 void Document::addPageAnnotation( int page, Annotation * annotation )
01841 {
01842
01843 Page * kp = d->m_pagesVector[ page ];
01844 if ( !d->m_generator || !kp )
01845 return;
01846
01847
01848 if ( annotation->d_ptr->m_page )
01849 return;
01850
01851
01852 kp->addAnnotation( annotation );
01853
01854
01855 foreachObserver( notifyPageChanged( page, DocumentObserver::Annotations ) );
01856 }
01857
01858 void Document::modifyPageAnnotation( int page, Annotation * newannotation )
01859 {
01860
01861
01862
01863 Page * kp = d->m_pagesVector[ page ];
01864 if ( !d->m_generator || !kp )
01865 return;
01866
01867 kp->d->modifyAnnotation( newannotation );
01868
01869 foreachObserver( notifyPageChanged( page, DocumentObserver::Annotations ) );
01870 }
01871
01872
01873 void Document::removePageAnnotation( int page, Annotation * annotation )
01874 {
01875
01876 Page * kp = d->m_pagesVector[ page ];
01877 if ( !d->m_generator || !kp )
01878 return;
01879
01880
01881 if ( kp->removeAnnotation( annotation ) )
01882 {
01883
01884 foreachObserver( notifyPageChanged( page, DocumentObserver::Annotations ) );
01885 }
01886 }
01887
01888 void Document::removePageAnnotations( int page, const QList< Annotation * > &annotations )
01889 {
01890
01891 Page * kp = d->m_pagesVector[ page ];
01892 if ( !d->m_generator || !kp )
01893 return;
01894
01895 bool changed = false;
01896 foreach ( Annotation * annotation, annotations )
01897 {
01898
01899 if ( kp->removeAnnotation( annotation ) )
01900 {
01901 changed = true;
01902 }
01903 }
01904 if ( changed )
01905 {
01906
01907 foreachObserver( notifyPageChanged( page, DocumentObserver::Annotations ) );
01908 }
01909 }
01910
01911 void Document::setPageTextSelection( int page, RegularAreaRect * rect, const QColor & color )
01912 {
01913 Page * kp = d->m_pagesVector[ page ];
01914 if ( !d->m_generator || !kp )
01915 return;
01916
01917
01918 if ( rect )
01919 kp->d->setTextSelections( rect, color );
01920 else
01921 kp->d->deleteTextSelections();
01922
01923
01924 foreachObserver( notifyPageChanged( page, DocumentObserver::TextSelection ) );
01925 }
01926
01927
01928
01929
01930
01931
01932
01933
01934
01935
01936
01937
01938
01939
01940
01941
01942 void Document::setViewportPage( int page, int excludeId, bool smoothMove )
01943 {
01944
01945 if ( page < 0 )
01946 page = 0;
01947 else if ( page > (int)d->m_pagesVector.count() )
01948 page = d->m_pagesVector.count() - 1;
01949
01950
01951 setViewport( DocumentViewport( page ), excludeId, smoothMove );
01952 }
01953
01954 void Document::setViewport( const DocumentViewport & viewport, int excludeId, bool smoothMove )
01955 {
01956
01957 DocumentViewport & oldViewport = *d->m_viewportIterator;
01958
01959
01960
01961
01962
01963 if ( oldViewport.pageNumber == viewport.pageNumber || !oldViewport.isValid() )
01964 {
01965
01966 oldViewport = viewport;
01967 }
01968 else
01969 {
01970
01971 d->m_viewportHistory.erase( ++d->m_viewportIterator, d->m_viewportHistory.end() );
01972
01973
01974 if ( d->m_viewportHistory.count() >= OKULAR_HISTORY_MAXSTEPS )
01975 d->m_viewportHistory.pop_front();
01976
01977
01978 d->m_viewportIterator = d->m_viewportHistory.insert( d->m_viewportHistory.end(), viewport );
01979 }
01980
01981
01982 QMap< int, DocumentObserver * >::const_iterator it = d->m_observers.begin(), end = d->m_observers.end();
01983 for ( ; it != end ; ++ it )
01984 if ( it.key() != excludeId )
01985 (*it)->notifyViewportChanged( smoothMove );
01986
01987
01988 if ( d->m_allocatedPixmapsFifo.count() > 1 )
01989 {
01990 const int page = viewport.pageNumber;
01991 QLinkedList< AllocatedPixmap * > viewportPixmaps;
01992 QLinkedList< AllocatedPixmap * >::iterator aIt = d->m_allocatedPixmapsFifo.begin();
01993 QLinkedList< AllocatedPixmap * >::iterator aEnd = d->m_allocatedPixmapsFifo.end();
01994 while ( aIt != aEnd )
01995 {
01996 if ( (*aIt)->page == page )
01997 {
01998 viewportPixmaps.append( *aIt );
01999 aIt = d->m_allocatedPixmapsFifo.erase( aIt );
02000 continue;
02001 }
02002 ++aIt;
02003 }
02004 if ( !viewportPixmaps.isEmpty() )
02005 d->m_allocatedPixmapsFifo += viewportPixmaps;
02006 }
02007 }
02008
02009 void Document::setZoom(int factor, int excludeId)
02010 {
02011
02012 QMap< int, DocumentObserver * >::const_iterator it = d->m_observers.begin(), end = d->m_observers.end();
02013 for ( ; it != end ; ++ it )
02014 if ( it.key() != excludeId )
02015 (*it)->notifyZoom( factor );
02016 }
02017
02018 void Document::setPrevViewport()
02019
02020 {
02021 if ( d->m_viewportIterator != d->m_viewportHistory.begin() )
02022 {
02023
02024 --d->m_viewportIterator;
02025 foreachObserver( notifyViewportChanged( true ) );
02026 }
02027 }
02028
02029 void Document::setNextViewport()
02030
02031 {
02032 QLinkedList< DocumentViewport >::const_iterator nextIterator = d->m_viewportIterator;
02033 ++nextIterator;
02034 if ( nextIterator != d->m_viewportHistory.end() )
02035 {
02036
02037 ++d->m_viewportIterator;
02038 foreachObserver( notifyViewportChanged( true ) );
02039 }
02040 }
02041
02042 void Document::setNextDocumentViewport( const DocumentViewport & viewport )
02043 {
02044 d->m_nextDocumentViewport = viewport;
02045 }
02046
02047 void Document::searchText( int searchID, const QString & text, bool fromStart, Qt::CaseSensitivity caseSensitivity,
02048 SearchType type, bool moveViewport, const QColor & color, bool noDialogs )
02049 {
02050 d->m_searchCancelled = false;
02051
02052
02053 if ( !d->m_generator || !d->m_generator->hasFeature( Generator::TextExtraction ) || d->m_pagesVector.isEmpty() )
02054 {
02055 emit searchFinished( searchID, NoMatchFound );
02056 return;
02057 }
02058
02059 if ( !noDialogs )
02060 {
02061 KDialog *searchDialog = new KDialog(widget());
02062 searchDialog->setCaption( i18n("Search in progress...") );
02063 searchDialog->setButtons( KDialog::Cancel );
02064 QLabel *searchLabel = new QLabel(i18n("Searching for %1", text), searchDialog);
02065 searchDialog->setMainWidget( searchLabel );
02066
02067 QTimer::singleShot(500, searchDialog, SLOT(show()));
02068 connect(this, SIGNAL( searchFinished(int, Okular::Document::SearchStatus) ), searchDialog, SLOT(deleteLater()));
02069 connect(searchDialog, SIGNAL( finished() ), this, SLOT(cancelSearch()));
02070 }
02071
02072
02073 QMap< int, RunningSearch * >::iterator searchIt = d->m_searches.find( searchID );
02074 if ( searchIt == d->m_searches.end() )
02075 {
02076 RunningSearch * search = new RunningSearch();
02077 search->continueOnPage = -1;
02078 searchIt = d->m_searches.insert( searchID, search );
02079 }
02080 if (d->m_lastSearchID != searchID)
02081 {
02082 resetSearch(d->m_lastSearchID);
02083 }
02084 d->m_lastSearchID = searchID;
02085 RunningSearch * s = *searchIt;
02086
02087
02088 bool newText = text != s->cachedString;
02089 s->cachedString = text;
02090 s->cachedType = type;
02091 s->cachedCaseSensitivity = caseSensitivity;
02092 s->cachedViewportMove = moveViewport;
02093 s->cachedNoDialogs = noDialogs;
02094 s->cachedColor = color;
02095
02096
02097 QSet< int > *pagesToNotify = new QSet< int >;
02098
02099
02100 *pagesToNotify += s->highlightedPages;
02101 foreach(int pageNumber, s->highlightedPages)
02102 d->m_pagesVector.at(pageNumber)->d->deleteHighlights( searchID );
02103 s->highlightedPages.clear();
02104
02105
02106 QApplication::setOverrideCursor( Qt::WaitCursor );
02107
02108
02109 if ( type == AllDocument )
02110 {
02111 QMap< Page *, QVector<RegularAreaRect *> > *pageMatches = new QMap< Page *, QVector<RegularAreaRect *> >;
02112
02113
02114 QMetaObject::invokeMethod(this, "doContinueAllDocumentSearch", Qt::QueuedConnection, Q_ARG(void *, pagesToNotify), Q_ARG(void *, pageMatches), Q_ARG(int, 0), Q_ARG(int, searchID), Q_ARG(QString, text), Q_ARG(int, caseSensitivity), Q_ARG(QColor, color));
02115 }
02116
02117 else if ( type == NextMatch )
02118 {
02119
02120 int viewportPage = (*d->m_viewportIterator).pageNumber;
02121 int currentPage = fromStart ? 0 : ((s->continueOnPage != -1) ? s->continueOnPage : viewportPage);
02122 Page * lastPage = fromStart ? 0 : d->m_pagesVector[ currentPage ];
02123
02124
02125 RegularAreaRect * match = 0;
02126 if ( lastPage && lastPage->number() == s->continueOnPage )
02127 {
02128 if ( newText )
02129 match = lastPage->findText( searchID, text, FromTop, caseSensitivity );
02130 else
02131 match = lastPage->findText( searchID, text, NextResult, caseSensitivity, &s->continueOnMatch );
02132 if ( !match )
02133 currentPage++;
02134 }
02135
02136 QMetaObject::invokeMethod(this, "doContinueNextMatchSearch", Qt::QueuedConnection, Q_ARG(void *, pagesToNotify), Q_ARG(void *, match), Q_ARG(int, currentPage), Q_ARG(int, searchID), Q_ARG(QString, text), Q_ARG(int, caseSensitivity), Q_ARG(bool, moveViewport), Q_ARG(QColor, color), Q_ARG(bool, noDialogs), Q_ARG(int, 1));
02137 }
02138
02139 else if ( type == PreviousMatch )
02140 {
02141 }
02142
02143 else if ( type == GoogleAll || type == GoogleAny )
02144 {
02145 bool matchAll = type == GoogleAll;
02146
02147 QMap< Page *, QVector< QPair<RegularAreaRect *, QColor> > > *pageMatches = new QMap< Page *, QVector<QPair<RegularAreaRect *, QColor> > >;
02148
02149
02150 QMetaObject::invokeMethod(this, "doContinueGooglesDocumentSearch", Qt::QueuedConnection, Q_ARG(void *, pagesToNotify), Q_ARG(void *, pageMatches), Q_ARG(int, 0), Q_ARG(int, searchID), Q_ARG(QString, text), Q_ARG(int, caseSensitivity), Q_ARG(QColor, color), Q_ARG(bool, matchAll));
02151 }
02152 }
02153
02154 void Document::continueSearch( int searchID )
02155 {
02156
02157 QMap< int, RunningSearch * >::const_iterator it = d->m_searches.constFind( searchID );
02158 if ( it == d->m_searches.constEnd() )
02159 {
02160 emit searchFinished( searchID, NoMatchFound );
02161 return;
02162 }
02163
02164
02165 RunningSearch * p = *it;
02166 searchText( searchID, p->cachedString, false, p->cachedCaseSensitivity,
02167 p->cachedType, p->cachedViewportMove, p->cachedColor,
02168 p->cachedNoDialogs );
02169 }
02170
02171 void Document::resetSearch( int searchID )
02172 {
02173
02174 QMap< int, RunningSearch * >::iterator searchIt = d->m_searches.find( searchID );
02175 if ( searchIt == d->m_searches.end() )
02176 return;
02177
02178
02179 RunningSearch * s = *searchIt;
02180
02181
02182 foreach(int pageNumber, s->highlightedPages)
02183 {
02184 d->m_pagesVector.at(pageNumber)->d->deleteHighlights( searchID );
02185 foreachObserver( notifyPageChanged( pageNumber, DocumentObserver::Highlights ) );
02186 }
02187
02188
02189 foreachObserver( notifySetup( d->m_pagesVector, 0 ) );
02190
02191
02192 d->m_searches.erase( searchIt );
02193 delete s;
02194 }
02195
02196 void Document::cancelSearch()
02197 {
02198 d->m_searchCancelled = true;
02199 }
02200
02201 BookmarkManager * Document::bookmarkManager() const
02202 {
02203 return d->m_bookmarkManager;
02204 }
02205
02206 QList<int> Document::bookmarkedPageList() const
02207 {
02208 QList<int> list;
02209 uint docPages = pages();
02210
02211
02212 for ( uint i = 0; i < docPages; i++ )
02213 {
02214 if ( bookmarkManager()->isBookmarked( i ) )
02215 {
02216 list << i + 1;
02217 }
02218 }
02219 return list;
02220 }
02221
02222 QString Document::bookmarkedPageRange() const
02223 {
02224
02225
02226 QString range;
02227 uint docPages = pages();
02228 int startId = -1;
02229 int endId = -1;
02230
02231 for ( uint i = 0; i < docPages; ++i )
02232 {
02233 if ( bookmarkManager()->isBookmarked( i ) )
02234 {
02235 if ( startId < 0 )
02236 startId = i;
02237 if ( endId < 0 )
02238 endId = startId;
02239 else
02240 ++endId;
02241 }
02242 else if ( startId >= 0 && endId >= 0 )
02243 {
02244 if ( !range.isEmpty() )
02245 range += ',';
02246
02247 if ( endId - startId > 0 )
02248 range += QString( "%1-%2" ).arg( startId + 1 ).arg( endId + 1 );
02249 else
02250 range += QString::number( startId + 1 );
02251 startId = -1;
02252 endId = -1;
02253 }
02254 }
02255 if ( startId >= 0 && endId >= 0 )
02256 {
02257 if ( !range.isEmpty() )
02258 range += ',';
02259
02260 if ( endId - startId > 0 )
02261 range += QString( "%1-%2" ).arg( startId + 1 ).arg( endId + 1 );
02262 else
02263 range += QString::number( startId + 1 );
02264 }
02265 return range;
02266 }
02267
02268 void Document::processAction( const Action * action )
02269 {
02270 if ( !action )
02271 return;
02272
02273 switch( action->actionType() )
02274 {
02275 case Action::Goto: {
02276 const GotoAction * go = static_cast< const GotoAction * >( action );
02277 d->m_nextDocumentViewport = go->destViewport();
02278
02279
02280
02281
02282
02283
02284
02285
02286
02287
02288 if ( go->isExternal() && !d->openRelativeFile( go->fileName() ) )
02289 {
02290 kWarning(OkularDebug).nospace() << "Action: Error opening '" << go->fileName() << "'.";
02291 return;
02292 }
02293 else
02294 {
02295
02296 if (!d->m_nextDocumentViewport.isValid())
02297 return;
02298
02299 setViewport( d->m_nextDocumentViewport, -1, true );
02300 d->m_nextDocumentViewport = DocumentViewport();
02301 }
02302
02303 } break;
02304
02305 case Action::Execute: {
02306 const ExecuteAction * exe = static_cast< const ExecuteAction * >( action );
02307 QString fileName = exe->fileName();
02308 if ( fileName.endsWith( ".pdf" ) || fileName.endsWith( ".PDF" ) )
02309 {
02310 d->openRelativeFile( fileName );
02311 return;
02312 }
02313
02314
02315
02316 fileName = d->giveAbsolutePath( fileName );
02317 KMimeType::Ptr mime = KMimeType::findByPath( fileName );
02318
02319 if ( KRun::isExecutableFile( fileName, mime->name() ) )
02320 {
02321
02322 if ( !exe->parameters().isEmpty() )
02323 {
02324 fileName = d->giveAbsolutePath( exe->parameters() );
02325 mime = KMimeType::findByPath( fileName );
02326 if ( KRun::isExecutableFile( fileName, mime->name() ) )
02327 {
02328
02329
02330 KMessageBox::information( widget(), i18n("The document is trying to execute an external application and for your safety okular does not allow that.") );
02331 return;
02332 }
02333 }
02334 else
02335 {
02336
02337
02338 KMessageBox::information( widget(), i18n("The document is trying to execute an external application and for your safety okular does not allow that.") );
02339 return;
02340 }
02341 }
02342
02343 KService::Ptr ptr = KMimeTypeTrader::self()->preferredService( mime->name(), "Application" );
02344 if ( ptr )
02345 {
02346 KUrl::List lst;
02347 lst.append( fileName );
02348 KRun::run( *ptr, lst, 0 );
02349 }
02350 else
02351 KMessageBox::information( widget(), i18n( "No application found for opening file of mimetype %1.", mime->name() ) );
02352 } break;
02353
02354 case Action::DocAction: {
02355 const DocumentAction * docaction = static_cast< const DocumentAction * >( action );
02356 switch( docaction->documentActionType() )
02357 {
02358 case DocumentAction::PageFirst:
02359 setViewportPage( 0 );
02360 break;
02361 case DocumentAction::PagePrev:
02362 if ( (*d->m_viewportIterator).pageNumber > 0 )
02363 setViewportPage( (*d->m_viewportIterator).pageNumber - 1 );
02364 break;
02365 case DocumentAction::PageNext:
02366 if ( (*d->m_viewportIterator).pageNumber < (int)d->m_pagesVector.count() - 1 )
02367 setViewportPage( (*d->m_viewportIterator).pageNumber + 1 );
02368 break;
02369 case DocumentAction::PageLast:
02370 setViewportPage( d->m_pagesVector.count() - 1 );
02371 break;
02372 case DocumentAction::HistoryBack:
02373 setPrevViewport();
02374 break;
02375 case DocumentAction::HistoryForward:
02376 setNextViewport();
02377 break;
02378 case DocumentAction::Quit:
02379 emit quit();
02380 break;
02381 case DocumentAction::Presentation:
02382 emit linkPresentation();
02383 break;
02384 case DocumentAction::EndPresentation:
02385 emit linkEndPresentation();
02386 break;
02387 case DocumentAction::Find:
02388 emit linkFind();
02389 break;
02390 case DocumentAction::GoToPage:
02391 emit linkGoToPage();
02392 break;
02393 case DocumentAction::Close:
02394 emit close();
02395 break;
02396 }
02397 } break;
02398
02399 case Action::Browse: {
02400 const BrowseAction * browse = static_cast< const BrowseAction * >( action );
02401
02402 if ( browse->url().startsWith( "mailto:", Qt::CaseInsensitive ) )
02403 KToolInvocation::invokeMailer( browse->url() );
02404 else
02405 {
02406 QString url = browse->url();
02407
02408
02409 if (url.indexOf("http:") == 0 && url.indexOf("http://") == -1 && url.right(4) == ".pdf")
02410 {
02411 d->openRelativeFile(url.mid(5));
02412 return;
02413 }
02414
02415
02416 new KRun( KUrl( url ), widget() );
02417 }
02418 } break;
02419
02420 case Action::Sound: {
02421 const SoundAction * linksound = static_cast< const SoundAction * >( action );
02422 AudioPlayer::instance()->playSound( linksound->sound(), linksound );
02423 } break;
02424
02425 case Action::Movie:
02426
02427
02428 break;
02429 }
02430 }
02431
02432 void Document::processSourceReference( const SourceReference * ref )
02433 {
02434 if ( !ref )
02435 return;
02436
02437 if ( !QFile::exists( ref->fileName() ) )
02438 {
02439 kDebug(OkularDebug).nospace() << "No such file: '" << ref->fileName() << "'";
02440 return;
02441 }
02442
02443 static QHash< int, QString > editors;
02444
02445 if ( editors.isEmpty() )
02446 {
02447 editors[ Settings::EnumExternalEditor::Kate ] =
02448 QLatin1String( "kate --use --line %l --column %c" );
02449 editors[ Settings::EnumExternalEditor::Scite ] =
02450 QLatin1String( "scite %f \"-goto:%l,%c\"" );
02451 }
02452
02453 QHash< int, QString >::const_iterator it = editors.constFind( Settings::externalEditor() );
02454 QString p;
02455 if ( it != editors.end() )
02456 p = *it;
02457 else
02458 p = Settings::externalEditorCommand();
02459
02460 if ( p.isEmpty() )
02461 return;
02462
02463
02464 p.replace( QLatin1String( "%l" ), QString::number( ref->row() ) );
02465 p.replace( QLatin1String( "%c" ), QString::number( ref->column() ) );
02466 if ( p.indexOf( QLatin1String( "%f" ) ) > -1 )
02467 p.replace( QLatin1String( "%f" ), ref->fileName() );
02468 else
02469 p.append( QLatin1String( " " ) + ref->fileName() );
02470
02471
02472 if ( p.isEmpty() || p.trimmed() == ref->fileName() )
02473 return;
02474
02475 QProcess::startDetached( p );
02476 }
02477
02478 Document::PrintingType Document::printingSupport() const
02479 {
02480 if ( d->m_generator )
02481 {
02482
02483 if ( d->m_generator->hasFeature( Generator::PrintNative ) )
02484 {
02485 return NativePrinting;
02486 }
02487
02488 #ifndef Q_OS_WIN
02489 if ( d->m_generator->hasFeature( Generator::PrintPostscript ) )
02490 {
02491 return PostscriptPrinting;
02492 }
02493 #endif
02494
02495 }
02496
02497 return NoPrinting;
02498 }
02499
02500 bool Document::supportsPrintToFile() const
02501 {
02502 return d->m_generator ? d->m_generator->hasFeature( Generator::PrintToFile ) : false;
02503 }
02504
02505 bool Document::print( QPrinter &printer )
02506 {
02507 return d->m_generator ? d->m_generator->print( printer ) : false;
02508 }
02509
02510 QWidget* Document::printConfigurationWidget() const
02511 {
02512 if ( d->m_generator )
02513 {
02514 PrintInterface * iface = qobject_cast< Okular::PrintInterface * >( d->m_generator );
02515 return iface ? iface->printConfigurationWidget() : 0;
02516 }
02517 else
02518 return 0;
02519 }
02520
02521 void Document::fillConfigDialog( KConfigDialog * dialog )
02522 {
02523 if ( !dialog )
02524 return;
02525
02526
02527 QString constraint( "([X-KDE-Priority] > 0) and (exist Library) and ([X-KDE-okularHasInternalSettings])" );
02528 KService::List offers = KServiceTypeTrader::self()->query( "okular/Generator", constraint );
02529 d->loadServiceList( offers );
02530
02531 bool pagesAdded = false;
02532 QHash< QString, GeneratorInfo >::iterator it = d->m_loadedGenerators.begin();
02533 QHash< QString, GeneratorInfo >::iterator itEnd = d->m_loadedGenerators.end();
02534 for ( ; it != itEnd; ++it )
02535 {
02536 Okular::ConfigInterface * iface = d->generatorConfig( it.value() );
02537 if ( iface )
02538 {
02539 iface->addPages( dialog );
02540 pagesAdded = true;
02541 if ( !it.value().catalogName.isEmpty() )
02542 KGlobal::locale()->insertCatalog( it.value().catalogName );
02543 }
02544 }
02545 if ( pagesAdded )
02546 {
02547 connect( dialog, SIGNAL( settingsChanged( const QString& ) ),
02548 this, SLOT( slotGeneratorConfigChanged( const QString& ) ) );
02549 }
02550 }
02551
02552 int Document::configurableGenerators() const
02553 {
02554 QString constraint( "([X-KDE-Priority] > 0) and (exist Library) and ([X-KDE-okularHasInternalSettings])" );
02555 KService::List offers = KServiceTypeTrader::self()->query( "okular/Generator", constraint );
02556 return offers.count();
02557 }
02558
02559 QStringList Document::supportedMimeTypes() const
02560 {
02561 if ( !d->m_supportedMimeTypes.isEmpty() )
02562 return d->m_supportedMimeTypes;
02563
02564 QString constraint( "(Library == 'okularpart')" );
02565 QLatin1String basePartService( "KParts/ReadOnlyPart" );
02566 KService::List offers = KServiceTypeTrader::self()->query( basePartService, constraint );
02567 KService::List::ConstIterator it = offers.begin(), itEnd = offers.end();
02568 for ( ; it != itEnd; ++it )
02569 {
02570 KService::Ptr service = *it;
02571 QStringList mimeTypes = service->serviceTypes();
02572 foreach ( const QString& mimeType, mimeTypes )
02573 if ( mimeType != basePartService )
02574 d->m_supportedMimeTypes.append( mimeType );
02575 }
02576
02577 return d->m_supportedMimeTypes;
02578 }
02579
02580 const KComponentData* Document::componentData() const
02581 {
02582 if ( !d->m_generator )
02583 return 0;
02584
02585 QHash< QString, GeneratorInfo >::const_iterator genIt = d->m_loadedGenerators.constFind( d->m_generatorName );
02586 Q_ASSERT( genIt != d->m_loadedGenerators.constEnd() );
02587 const KComponentData* kcd = &genIt.value().data;
02588
02589
02590 if ( kcd->isValid() && kcd->aboutData() && kcd->aboutData()->programName().isEmpty() )
02591 return 0;
02592
02593 return kcd;
02594 }
02595
02596 void DocumentPrivate::requestDone( PixmapRequest * req )
02597 {
02598 if ( !req )
02599 return;
02600
02601 if ( !m_generator || m_closingLoop )
02602 {
02603 m_pixmapRequestsMutex.lock();
02604 m_executingPixmapRequests.removeAll( req );
02605 m_pixmapRequestsMutex.unlock();
02606 delete req;
02607 if ( m_closingLoop )
02608 m_closingLoop->exit();
02609 return;
02610 }
02611
02612 #ifndef NDEBUG
02613 if ( !m_generator->canGeneratePixmap() )
02614 kDebug(OkularDebug) << "requestDone with generator not in READY state.";
02615 #endif
02616
02617
02618 QLinkedList< AllocatedPixmap * >::iterator aIt = m_allocatedPixmapsFifo.begin();
02619 QLinkedList< AllocatedPixmap * >::iterator aEnd = m_allocatedPixmapsFifo.end();
02620 for ( ; aIt != aEnd; ++aIt )
02621 if ( (*aIt)->page == req->pageNumber() && (*aIt)->id == req->id() )
02622 {
02623 AllocatedPixmap * p = *aIt;
02624 m_allocatedPixmapsFifo.erase( aIt );
02625 m_allocatedPixmapsTotalMemory -= p->memory;
02626 delete p;
02627 break;
02628 }
02629
02630 QMap< int, DocumentObserver * >::const_iterator itObserver = m_observers.constFind( req->id() );
02631 if ( itObserver != m_observers.constEnd() )
02632 {
02633
02634 qulonglong memoryBytes = 4 * req->width() * req->height();
02635 AllocatedPixmap * memoryPage = new AllocatedPixmap( req->id(), req->pageNumber(), memoryBytes );
02636 m_allocatedPixmapsFifo.append( memoryPage );
02637 m_allocatedPixmapsTotalMemory += memoryBytes;
02638
02639
02640 itObserver.value()->notifyPageChanged( req->pageNumber(), DocumentObserver::Pixmap );
02641 }
02642 #ifndef NDEBUG
02643 else
02644 kWarning(OkularDebug) << "Receiving a done request for the defunct observer" << req->id();
02645 #endif
02646
02647
02648 m_pixmapRequestsMutex.lock();
02649 m_executingPixmapRequests.removeAll( req );
02650 m_pixmapRequestsMutex.unlock();
02651 delete req;
02652
02653
02654 m_pixmapRequestsMutex.lock();
02655 bool hasPixmaps = !m_pixmapRequestsStack.isEmpty();
02656 m_pixmapRequestsMutex.unlock();
02657 if ( hasPixmaps )
02658 sendGeneratorRequest();
02659 }
02660
02661 void Document::setRotation( int r )
02662 {
02663 d->setRotationInternal( r, true );
02664 }
02665
02666 void DocumentPrivate::setRotationInternal( int r, bool notify )
02667 {
02668 Rotation rotation = (Rotation)r;
02669 if ( !m_generator || ( m_rotation == rotation ) )
02670 return;
02671
02672
02673 QVector< Okular::Page * >::const_iterator pIt = m_pagesVector.begin();
02674 QVector< Okular::Page * >::const_iterator pEnd = m_pagesVector.end();
02675 for ( ; pIt != pEnd; ++pIt )
02676 (*pIt)->d->rotateAt( rotation );
02677 if ( notify )
02678 {
02679
02680 m_generator->rotationChanged( rotation, m_rotation );
02681 }
02682
02683 m_rotation = rotation;
02684
02685 if ( notify )
02686 {
02687 foreachObserverD( notifySetup( m_pagesVector, DocumentObserver::NewLayoutForPages ) );
02688 foreachObserverD( notifyContentsCleared( DocumentObserver::Pixmap | DocumentObserver::Highlights | DocumentObserver::Annotations ) );
02689 }
02690 kDebug(OkularDebug) << "Rotated:" << r;
02691 }
02692
02693 void Document::setPageSize( const PageSize &size )
02694 {
02695 if ( !d->m_generator || !d->m_generator->hasFeature( Generator::PageSizes ) )
02696 return;
02697
02698 if ( d->m_pageSizes.isEmpty() )
02699 d->m_pageSizes = d->m_generator->pageSizes();
02700 int sizeid = d->m_pageSizes.indexOf( size );
02701 if ( sizeid == -1 )
02702 return;
02703
02704
02705 QVector< Okular::Page * >::const_iterator pIt = d->m_pagesVector.begin();
02706 QVector< Okular::Page * >::const_iterator pEnd = d->m_pagesVector.end();
02707 for ( ; pIt != pEnd; ++pIt )
02708 (*pIt)->d->changeSize( size );
02709
02710 QLinkedList< AllocatedPixmap * >::const_iterator aIt = d->m_allocatedPixmapsFifo.begin();
02711 QLinkedList< AllocatedPixmap * >::const_iterator aEnd = d->m_allocatedPixmapsFifo.end();
02712 for ( ; aIt != aEnd; ++aIt )
02713 delete *aIt;
02714 d->m_allocatedPixmapsFifo.clear();
02715 d->m_allocatedPixmapsTotalMemory = 0;
02716
02717 d->m_generator->pageSizeChanged( size, d->m_pageSize );
02718
02719 d->m_pageSize = size;
02720
02721 foreachObserver( notifySetup( d->m_pagesVector, DocumentObserver::NewLayoutForPages ) );
02722 foreachObserver( notifyContentsCleared( DocumentObserver::Pixmap | DocumentObserver::Highlights ) );
02723 kDebug(OkularDebug) << "New PageSize id:" << sizeid;
02724 }
02725
02726
02729 DocumentViewport::DocumentViewport( int n )
02730 : pageNumber( n )
02731 {
02732
02733 rePos.enabled = false;
02734 rePos.normalizedX = 0.5;
02735 rePos.normalizedY = 0.0;
02736 rePos.pos = Center;
02737 autoFit.enabled = false;
02738 autoFit.width = false;
02739 autoFit.height = false;
02740 }
02741
02742 DocumentViewport::DocumentViewport( const QString & xmlDesc )
02743 : pageNumber( -1 )
02744 {
02745
02746 rePos.enabled = false;
02747 rePos.normalizedX = 0.5;
02748 rePos.normalizedY = 0.0;
02749 rePos.pos = Center;
02750 autoFit.enabled = false;
02751 autoFit.width = false;
02752 autoFit.height = false;
02753
02754
02755 if ( xmlDesc.isEmpty() )
02756 return;
02757
02758
02759 bool ok;
02760 int field = 0;
02761 QString token = xmlDesc.section( ';', field, field );
02762 while ( !token.isEmpty() )
02763 {
02764
02765 if ( field == 0 )
02766 {
02767 pageNumber = token.toInt( &ok );
02768 if ( !ok )
02769 return;
02770 }
02771 else if ( token.startsWith( "C1" ) )
02772 {
02773 rePos.enabled = true;
02774 rePos.normalizedX = token.section( ':', 1, 1 ).toDouble();
02775 rePos.normalizedY = token.section( ':', 2, 2 ).toDouble();
02776 rePos.pos = Center;
02777 }
02778 else if ( token.startsWith( "C2" ) )
02779 {
02780 rePos.enabled = true;
02781 rePos.normalizedX = token.section( ':', 1, 1 ).toDouble();
02782 rePos.normalizedY = token.section( ':', 2, 2 ).toDouble();
02783 if (token.section( ':', 3, 3 ).toInt() == 1) rePos.pos = Center;
02784 else rePos.pos = TopLeft;
02785 }
02786 else if ( token.startsWith( "AF1" ) )
02787 {
02788 autoFit.enabled = true;
02789 autoFit.width = token.section( ':', 1, 1 ) == "T";
02790 autoFit.height = token.section( ':', 2, 2 ) == "T";
02791 }
02792
02793 field++;
02794 token = xmlDesc.section( ';', field, field );
02795 }
02796 }
02797
02798 QString DocumentViewport::toString() const
02799 {
02800
02801 QString s = QString::number( pageNumber );
02802
02803 if ( rePos.enabled )
02804 s += QString( ";C2:" ) + QString::number( rePos.normalizedX ) +
02805 ':' + QString::number( rePos.normalizedY ) +
02806 ':' + QString::number( rePos.pos );
02807
02808 if ( autoFit.enabled )
02809 s += QString( ";AF1:" ) + (autoFit.width ? "T" : "F") +
02810 ':' + (autoFit.height ? "T" : "F");
02811 return s;
02812 }
02813
02814 bool DocumentViewport::isValid() const
02815 {
02816 return pageNumber >= 0;
02817 }
02818
02819 bool DocumentViewport::operator==( const DocumentViewport & vp ) const
02820 {
02821 bool equal = ( pageNumber == vp.pageNumber ) &&
02822 ( rePos.enabled == vp.rePos.enabled ) &&
02823 ( autoFit.enabled == vp.autoFit.enabled );
02824 if ( !equal )
02825 return false;
02826 if ( rePos.enabled &&
02827 (( rePos.normalizedX != vp.rePos.normalizedX) ||
02828 ( rePos.normalizedY != vp.rePos.normalizedY ) || rePos.pos != vp.rePos.pos) )
02829 return false;
02830 if ( autoFit.enabled &&
02831 (( autoFit.width != vp.autoFit.width ) ||
02832 ( autoFit.height != vp.autoFit.height )) )
02833 return false;
02834 return true;
02835 }
02836
02837
02840 DocumentInfo::DocumentInfo()
02841 : QDomDocument( "DocumentInformation" )
02842 {
02843 QDomElement docElement = createElement( "DocumentInfo" );
02844 appendChild( docElement );
02845 }
02846
02847 void DocumentInfo::set( const QString &key, const QString &value,
02848 const QString &title )
02849 {
02850 QDomElement docElement = documentElement();
02851 QDomElement element;
02852
02853
02854 QDomNodeList list = docElement.elementsByTagName( key );
02855 if ( list.count() > 0 )
02856 element = list.item( 0 ).toElement();
02857 else
02858 element = createElement( key );
02859
02860 element.setAttribute( "value", value );
02861 element.setAttribute( "title", title );
02862
02863 if ( list.count() == 0 )
02864 docElement.appendChild( element );
02865 }
02866
02867 void DocumentInfo::set( enum Key key, const QString &value )
02868 {
02869 switch ( key ) {
02870 case Title:
02871 set( "title", value, i18n( "Title" ) );
02872 break;
02873 case Subject:
02874 set( "subject", value, i18n( "Subject" ) );
02875 break;
02876 case Description:
02877 set( "description", value, i18n( "Description" ) );
02878 break;
02879 case Author:
02880 set( "author", value, i18n( "Author" ) );
02881 break;
02882 case Creator:
02883 set( "creator", value, i18n( "Creator" ) );
02884 break;
02885 case Producer:
02886 set( "producer", value, i18n( "Producer" ) );
02887 break;
02888 case Copyright:
02889 set( "copyright", value, i18n( "Copyright" ) );
02890 break;
02891 case Pages:
02892 set( "pages", value, i18n( "Pages" ) );
02893 break;
02894 case CreationDate:
02895 set( "creationDate", value, i18n( "Created" ) );
02896 break;
02897 case ModificationDate:
02898 set( "modificationDate", value, i18n( "Modified" ) );
02899 break;
02900 case MimeType:
02901 set( "mimeType", value, i18n( "Mime Type" ) );
02902 break;
02903 case Category:
02904 set( "category", value, i18n( "Category" ) );
02905 break;
02906 case Keywords:
02907 set( "keywords", value, i18n( "Keywords" ) );
02908 break;
02909 default:
02910 kWarning(OkularDebug) << "Invalid key passed";
02911 break;
02912 }
02913 }
02914
02915 QString DocumentInfo::get( const QString &key ) const
02916 {
02917 QDomElement docElement = documentElement();
02918 QDomElement element;
02919
02920
02921 QDomNodeList list = docElement.elementsByTagName( key );
02922 if ( list.count() > 0 )
02923 return list.item( 0 ).toElement().attribute( "value" );
02924 else
02925 return QString();
02926 }
02927
02928
02931 DocumentSynopsis::DocumentSynopsis()
02932 : QDomDocument( "DocumentSynopsis" )
02933 {
02934
02935 }
02936
02937 DocumentSynopsis::DocumentSynopsis( const QDomDocument &document )
02938 : QDomDocument( document )
02939 {
02940 }
02941
02944 EmbeddedFile::EmbeddedFile()
02945 {
02946 }
02947
02948 EmbeddedFile::~EmbeddedFile()
02949 {
02950 }
02951
02952 VisiblePageRect::VisiblePageRect( int page, const NormalizedRect &rectangle )
02953 : pageNumber( page ), rect( rectangle )
02954 {
02955 }
02956
02957 #include "document.moc"