diff --git a/kpdf/QOutputDev.cpp b/kpdf/QOutputDev.cpp index fcec81ca7..7ab46d9ac 100644 --- a/kpdf/QOutputDev.cpp +++ b/kpdf/QOutputDev.cpp @@ -35,7 +35,7 @@ //NOTE: XPDF/Splash *implementation dependant* code is marked with '###' //BEGIN KPDFOutputDev -KPDFOutputDev::KPDFOutputDev( GeneratorPDF * parent, SplashColor paperColor ) +KPDFOutputDev::KPDFOutputDev( PDFGenerator * parent, SplashColor paperColor ) : SplashOutputDev( splashModeRGB8, false, paperColor ), m_pixmap( 0 ), m_generator( parent ), m_text( 0 ) { diff --git a/kpdf/QOutputDev.h b/kpdf/QOutputDev.h index 49c8f9822..5590d1eff 100644 --- a/kpdf/QOutputDev.h +++ b/kpdf/QOutputDev.h @@ -25,7 +25,7 @@ class QPixmap; class TextPage; -class GeneratorPDF; +class PDFGenerator; class KPDFLink; class KPDFPageRect; @@ -42,7 +42,7 @@ class KPDFPageRect; class KPDFOutputDev : public SplashOutputDev { public: - KPDFOutputDev( GeneratorPDF * parent, SplashColor paperColor ); + KPDFOutputDev( PDFGenerator * parent, SplashColor paperColor ); virtual ~KPDFOutputDev(); // to be called before PDFDoc->displayPage( thisclass, .. ) @@ -84,7 +84,7 @@ class KPDFOutputDev : public SplashOutputDev int m_pixmapWidth; int m_pixmapHeight; QPixmap * m_pixmap; - GeneratorPDF * m_generator; + PDFGenerator * m_generator; // text page generated on demand TextPage * m_text; diff --git a/kpdf/TODO b/kpdf/TODO index b16a9ec3d..8e8dbf715 100644 --- a/kpdf/TODO +++ b/kpdf/TODO @@ -7,16 +7,17 @@ Legend: (*) - Some parts of this item are already done In progress on the branch (first item comes first): +-> create a memory manager in Document with different profiles [80%] -> FIX: viewport changes the right way when clicking links and TOC items (also suggested by Mikolaj Machowski). Create a great viewport definition and merge it inside the synopsis too. [70% done] --> memory manager with different profiles (mem/cpu tradeoff: {memory saving, normal, memory aggressive}) [20%] Things to do in order to merge in HEAD (first item has highest priority): -> take care of naming on merge, too differences (remove some kpdf_* prefixes and rename internals too document->kpdfdocument, page->kpdfpage, etc..) Higher priority after merge: +-> move toolbar view actions in the PageView instead of the part.. maybe.. or not... -> link thumbnails view with document [first, the 'Viewport' must be defined] -> usability: layout 2PPV [1 2,3 4,5 6] -> [1,2 3,4 5]. add option for 'ebook' style alignment. (by Mikolaj) -> usability: trigger redraw on 'filter text' on current page (by Mikolaj) diff --git a/kpdf/document.cpp b/kpdf/document.cpp index e5ec165cb..205470a19 100644 --- a/kpdf/document.cpp +++ b/kpdf/document.cpp @@ -31,7 +31,7 @@ #include "generator_pdf.h" // PDF generator //#include "generator_ps.H" // PS generator -// structure used internally by KPDFDocument for local variables storage +// structures used internally by KPDFDocument for local variables storage class KPDFDocumentPrivate { public: @@ -47,13 +47,23 @@ class KPDFDocumentPrivate int currentPage; // observers related (note: won't delete oservers) - QMap< int, KPDFDocumentObserver* > observers; + QMap< int, class ObserverData* > observers; +}; + +struct ObserverData +{ + // public data fields + KPDFDocumentObserver * observer; + QMap< int, int > pageMemory; + int totalMemory; + // public constructor: initialize data + ObserverData( KPDFDocumentObserver * obs ) : observer( obs ), totalMemory( 0 ) {}; }; #define foreachObserver( cmd ) {\ - QMap::iterator it = d->observers.begin();\ - QMap::iterator end = d->observers.end();\ - for ( ; it != end ; ++ it ) { (*it)-> cmd ; } } + QMap< int, ObserverData * >::iterator it = d->observers.begin(), end = d->observers.end();\ + for ( ; it != end ; ++ it ) { (*it)->observer-> cmd ; } } + KPDFDocument::KPDFDocument() : generator( 0 ), d( new KPDFDocumentPrivate ) @@ -80,9 +90,19 @@ bool KPDFDocument::openDocument( const QString & docFile ) // reset internal status and frees memory closeDocument(); - // create the generator - // TODO: switch on mimetype for generator selection - generator = new GeneratorPDF(); + // create the generator based on the file's mimetype + KMimeType::Ptr mime = KMimeType::findByPath( docFile ); + QString mimeName = mime->name(); + if ( mimeName == "application/pdf" ) + generator = new PDFGenerator(); +// else if ( mimeName == "application/postscript" ) +// generator = new PSGenerator(); + else + { + kdWarning() << "Unknown mimetype '" << mimeName << "'." << endl; + return false; + } + documentFileName = docFile; bool openOk = generator->loadDocument( docFile, pages_vector ); if ( !openOk ) @@ -127,7 +147,7 @@ void KPDFDocument::closeDocument() void KPDFDocument::addObserver( KPDFDocumentObserver * pObserver ) { // keep the pointer to the observer in a map - d->observers[ pObserver->observerId() ] = pObserver; + d->observers[ pObserver->observerId() ] = new ObserverData( pObserver ); // if the observer is added while a document is already opened, tell it if ( !pages_vector.isEmpty() ) @@ -137,7 +157,17 @@ void KPDFDocument::addObserver( KPDFDocumentObserver * pObserver ) void KPDFDocument::removeObserver( KPDFDocumentObserver * pObserver ) { // remove observer from the map. it won't receive notifications anymore - d->observers.remove( pObserver->observerId() ); + if ( d->observers.contains( pObserver->observerId() ) ) + { + // free observer data + int observerId = pObserver->observerId(); + QValueVector::iterator it = pages_vector.begin(), end = pages_vector.end(); + for ( ; it != end; ++it ) + (*it)->deletePixmap( observerId ); + // delete observer + delete d->observers[ observerId ]; + d->observers.remove( observerId ); + } } void KPDFDocument::reparseConfig() @@ -185,15 +215,65 @@ bool KPDFDocument::okToPrint() const } -void KPDFDocument::requestPixmap( int id, uint page, int width, int height, bool syn ) +void KPDFDocument::requestPixmap( int id, int page, int width, int height, bool syn ) { KPDFPage * kp = pages_vector[ page ]; if ( !generator || !kp || kp->width() < 1 || kp->height() < 1 ) return; + // 1. Update statistics (pageMemory / totalMemory) adding this pixmap + ObserverData * obs = d->observers[ id ]; + if ( obs->pageMemory.contains( page ) ) + obs->totalMemory -= obs->pageMemory[ page ]; + int pixmapMemory = 4 * width * height / 1024; + obs->pageMemory[ page ] = pixmapMemory; + obs->totalMemory += pixmapMemory; + + // + int memoryToFree; + switch ( Settings::memoryLevel() ) + { + case Settings::EnumMemoryLevel::Low: + memoryToFree = obs->totalMemory; + break; + + case Settings::EnumMemoryLevel::Normal: + memoryToFree = obs->totalMemory - mTotalMemory()/4; + printf("%d\n",memoryToFree); + break; + + case Settings::EnumMemoryLevel::Aggressive: + memoryToFree = 0; + break; + } + + // 2. FREE Memory Loop. remove older data first. + int freed = 0; + if ( memoryToFree > 0 ) + { + QMap< int, int >::iterator it = obs->pageMemory.begin(), end = obs->pageMemory.end(); + while ( (it != end) && (memoryToFree > 0) ) + { + int freeNumber = it.key(); + if ( page != freeNumber && obs->observer->canUnloadPixmap( freeNumber ) ) + { + // update mem stats + memoryToFree -= it.data(); + obs->totalMemory -= it.data(); + obs->pageMemory.remove( it ); + // delete pixmap + pages_vector[ freeNumber ]->deletePixmap( id ); + freed++; + } + ++it; + } + } + kdWarning() << "[" << obs->totalMemory << "] Removed " << freed << " pages. " << obs->pageMemory.count() << " pages kept in memory." << endl; + + // 3. Request next pixmap to generator bool pixChanged = generator->requestPixmap( id, kp, width, height, syn ); if ( pixChanged ) - d->observers[id]->notifyPixmapChanged( page ); + d->observers[id]->observer->notifyPixmapChanged( page ); } void KPDFDocument::requestTextPage( uint page ) @@ -202,6 +282,8 @@ void KPDFDocument::requestTextPage( uint page ) if ( !generator || !kp ) return; + // Memory management for TextPages + generator->requestTextPage( kp ); } @@ -424,6 +506,58 @@ bool KPDFDocument::print( KPrinter &printer ) } +int KPDFDocument::mTotalMemory() +{ +#ifdef __linux__ + // if /proc/meminfo doesn't exist, return 128MB + QFile memFile( "/proc/meminfo" ); + if ( !memFile.open( IO_ReadOnly ) ) + return 131072; + + // read /proc/meminfo and sum up the contents of 'MemFree', 'Buffers' + // and 'Cached' fields. consider swapped memory as used memory. + QTextStream readStream( &memFile ); + while ( !readStream.atEnd() ) + { + QString entry = readStream.readLine(); + if ( entry.startsWith( "MemTotal:" ) ) + return entry.section( ' ', -2, -2 ).toInt(); + } +#endif + return 131072; +} + +int KPDFDocument::mFreeMemory() +{ +#ifdef __linux__ + // if /proc/meminfo doesn't exist, return 128MB + QFile memFile( "/proc/meminfo" ); + if ( !memFile.open( IO_ReadOnly ) ) + return 131072; + + // read /proc/meminfo and sum up the contents of 'MemFree', 'Buffers' + // and 'Cached' fields. consider swapped memory as used memory. + int memoryFree = 0; + QString entry; + QTextStream readStream( &memFile ); + while ( !readStream.atEnd() ) + { + entry = readStream.readLine(); + if ( entry.startsWith( "MemFree:" ) || + entry.startsWith( "Buffers:" ) || + entry.startsWith( "Cached:" ) || + entry.startsWith( "SwapFree:" ) ) + memoryFree += entry.section( ' ', -2, -2 ).toInt(); + if ( entry.startsWith( "SwapTotal:" ) ) + memoryFree -= entry.section( ' ', -2, -2 ).toInt(); + } + memFile.close(); + return memoryFree; +#else + return 131072; +#endif +} + QString KPDFDocument::giveAbsolutePath( const QString & fileName ) { if ( documentFileName.isEmpty() ) diff --git a/kpdf/document.h b/kpdf/document.h index b88ccf77e..abd00b983 100644 --- a/kpdf/document.h +++ b/kpdf/document.h @@ -34,13 +34,16 @@ class KPDFDocumentObserver // you must give each observer a unique ID (used for notifications) virtual uint observerId() const = 0; - // monitor changes in pixmaps (generation thread complete) - virtual void notifyPixmapChanged( int /*pageNumber*/ ) {}; - virtual void notifyPixmapsCleared() {}; - // commands from the Document to all observers virtual void pageSetup( const QValueVector & /*pages*/, bool /*documentChanged*/ ) {}; virtual void pageSetCurrent( int /*pageNumber*/, const QRect & /*viewport*/ = QRect() ) {}; + + // queries to observers + virtual bool canUnloadPixmap( int /*pageNum*/ ) { return true; } + + // monitor changes in pixmaps (generation thread complete) + virtual void notifyPixmapChanged( int /*pageNumber*/ ) {}; + virtual void notifyPixmapsCleared() {}; }; #define PRESENTATION_ID 1 @@ -90,7 +93,8 @@ class KPDFDocument bool okToPrint() const; // perform actions on document / pages - void requestPixmap( int id, uint page, int width, int height, bool syncronous = false ); + //void requestPixmaps( int id, const QValueList & pages, int width, int height, bool syncronous = false ); + void requestPixmap( int id, int pageNum, int width, int height, bool syncronous = false ); void requestTextPage( uint page ); void setCurrentPage( int page, const QRect & viewport = QRect() ); void findText( const QString & text = "", bool caseSensitive = false ); @@ -100,6 +104,10 @@ class KPDFDocument bool print( KPrinter &printer ); private: + // memory management related functions + int mTotalMemory(); + int mFreeMemory(); + // more private functions QString giveAbsolutePath( const QString & fileName ); bool openRelativeFile( const QString & fileName ); void processPageList( bool documentChanged ); @@ -129,6 +137,7 @@ struct DocumentInfo producer, subject, title, + mimeType, format, formatVersion, encryption, diff --git a/kpdf/generator.h b/kpdf/generator.h index 22f8bbebd..86c961b69 100644 --- a/kpdf/generator.h +++ b/kpdf/generator.h @@ -10,6 +10,7 @@ #ifndef _KPDF_GENERATOR_H_ #define _KPDF_GENERATOR_H_ +#include #include #include class KPrinter; @@ -19,6 +20,17 @@ class KPDFDocument; class DocumentSynopsis; class DocumentInfo; +/* Note: on contents generation and asyncronous queries. + * Many observers may want to request data syncronously or asyncronously. + * - Sync requests. These should be done in-place. Syncronous events in the + * queue have precedence on all the asyncronous ones. + * - Async request must be done in real background. That usually means a + * thread, such as QThread derived classes. + * Once contents are available, they must be immediately stored in the + * KPDFPage they refer to, and a signal is emitted as soon as storing + * (even for sync or async queries) has been done. + */ + /** * @short [Abstract Class] The information generator. * @@ -29,8 +41,9 @@ class DocumentInfo; * class stores the resulting data into 'KPDFPage's. The data will then be * displayed by the GUI components (pageView, thumbnailList, etc..). */ -class Generator +class Generator : public QObject { + Q_OBJECT public: // load a document and fill up the pagesVector virtual bool loadDocument( const QString & fileName, QValueVector< KPDFPage* > & pagesVector ) = 0; @@ -43,13 +56,16 @@ class Generator enum Permissions { Modify = 1, Copy = 2, Print = 4, AddNotes = 8 }; virtual bool allowed( int /*permisisons*/ ) { return true; } - // perform actions (/request content generation) - virtual bool print( KPrinter& printer ) = 0; + // generator core + virtual bool print( KPrinter& printer ) { return false; } virtual bool requestPixmap( int id, KPDFPage * page, int width, int height, bool syncronous = false ) = 0; virtual void requestTextPage( KPDFPage * page ) = 0; - // check configuration and return if something changed + // check configuration and return true if something changed virtual bool reparseConfig() { return false; } + + signals: + void contentsChanged( const KPDFPage * page ); }; #endif diff --git a/kpdf/generator_pdf.cpp b/kpdf/generator_pdf.cpp index 0829a78f1..c0cc5ab25 100644 --- a/kpdf/generator_pdf.cpp +++ b/kpdf/generator_pdf.cpp @@ -35,7 +35,7 @@ #include "QOutputDev.h" -GeneratorPDF::GeneratorPDF() +PDFGenerator::PDFGenerator() : pdfdoc( 0 ), kpdfOutputDev( 0 ), docInfoDirty( true ), docSynopsisDirty( true ) { @@ -43,7 +43,7 @@ GeneratorPDF::GeneratorPDF() reparseConfig(); } -GeneratorPDF::~GeneratorPDF() +PDFGenerator::~PDFGenerator() { docLock.lock(); delete kpdfOutputDev; @@ -52,7 +52,7 @@ GeneratorPDF::~GeneratorPDF() } -bool GeneratorPDF::loadDocument( const QString & fileName, QValueVector & pagesVector ) +bool PDFGenerator::loadDocument( const QString & fileName, QValueVector & pagesVector ) { // create PDFDoc for the given file GString *filename = new GString( QFile::encodeName( fileName ) ); @@ -122,7 +122,7 @@ bool GeneratorPDF::loadDocument( const QString & fileName, QValueVectorgetLength(); for ( int i = 0; i < numItems; ++i ) @@ -220,7 +221,7 @@ void GeneratorPDF::addSynopsisChildren( QDomNode * parent, GList * items ) } -bool GeneratorPDF::print( KPrinter& printer ) +bool PDFGenerator::print( KPrinter& printer ) { KTempFile tf( QString::null, ".ps" ); PSOutputDev *psOut = new PSOutputDev(tf.name().latin1(), pdfdoc->getXRef(), pdfdoc->getCatalog(), 1, pdfdoc->getNumPages(), psModePS); @@ -258,7 +259,7 @@ bool GeneratorPDF::print( KPrinter& printer ) } } -bool GeneratorPDF::requestPixmap( int id, KPDFPage * page, int width, int height, bool syncronous ) +bool PDFGenerator::requestPixmap( int id, KPDFPage * page, int width, int height, bool syncronous ) { //kdDebug() << "id: " << id << " is requesting pixmap for page " << page->number() << " [" << width << " x " << height << "]." << endl; if ( syncronous ) @@ -301,7 +302,7 @@ bool GeneratorPDF::requestPixmap( int id, KPDFPage * page, int width, int height return false; } -void GeneratorPDF::requestTextPage( KPDFPage * page ) +void PDFGenerator::requestTextPage( KPDFPage * page ) { // build a TextPage using the lightweight KPDFTextDev generator.. KPDFTextDev td; @@ -312,7 +313,7 @@ void GeneratorPDF::requestTextPage( KPDFPage * page ) page->setSearchPage( td.takeTextPage() ); } -bool GeneratorPDF::reparseConfig() +bool PDFGenerator::reparseConfig() { // load paper color from Settings or use the white default color QColor color = ( (Settings::renderMode() == Settings::EnumRenderMode::Paper ) && @@ -337,7 +338,7 @@ bool GeneratorPDF::reparseConfig() return false; } -KPDFLinkGoto::Viewport GeneratorPDF::decodeLinkViewport( GString * namedDest, LinkDest * dest ) +KPDFLinkGoto::Viewport PDFGenerator::decodeLinkViewport( GString * namedDest, LinkDest * dest ) // note: this function is called when processing a page, when the MUTEX is already LOCKED { KPDFLinkGoto::Viewport vp; @@ -398,7 +399,7 @@ KPDFLinkGoto::Viewport GeneratorPDF::decodeLinkViewport( GString * namedDest, Li } -QString GeneratorPDF::getDocumentInfo( const QString & data ) const +QString PDFGenerator::getDocumentInfo( const QString & data ) const { // [Albert] Code adapted from pdfinfo.cc on xpdf Object info; @@ -457,7 +458,7 @@ QString GeneratorPDF::getDocumentInfo( const QString & data ) const return i18n( "Unknown" ); } -QString GeneratorPDF::getDocumentDate( const QString & data ) const +QString PDFGenerator::getDocumentDate( const QString & data ) const { // [Albert] Code adapted from pdfinfo.cc on xpdf Object info; diff --git a/kpdf/generator_pdf.h b/kpdf/generator_pdf.h index af85ef746..2aaabd084 100644 --- a/kpdf/generator_pdf.h +++ b/kpdf/generator_pdf.h @@ -14,6 +14,7 @@ #include #include #include +#include #include "generator.h" #include "document.h" #include "link.h" @@ -25,13 +26,22 @@ class KPDFOutputDev; /** * @short A generator that builds contents from a PDF document. * - * ... + * All Generator features are supported and implented by this one. + * Internally this holds a reference to xpdf's core objects and provides + * contents generation using the PDFDoc object and a couple of OutputDevices + * called KPDFOutputDev and KPDFTextDev (both defined in QOutputDev.h). + * + * For generating page contents we tell PDFDoc to render a page and grab + * contents from out OutputDevs when rendering finishes. + * + * Background asyncronous contents providing is done via a QThread inherited + * class defined at the bottom of the file. */ -class GeneratorPDF : public Generator +class PDFGenerator : public Generator { public: - GeneratorPDF(); - virtual ~GeneratorPDF(); + PDFGenerator(); + virtual ~PDFGenerator(); // [INHERITED] load a document and fill up the pagesVector bool loadDocument( const QString & fileName, QValueVector & pagesVector ); @@ -69,33 +79,29 @@ class GeneratorPDF : public Generator DocumentSynopsis docSyn; }; -/* -#ifndef THUMBNAILGENERATOR_H -#define THUMBNAILGENERATOR_H -#include - -class QMutex; - -class ThumbnailGenerator : public QThread +/** + * @short A thread that builds contents for PDFGenerator in the background. + * + * + */ +class PDFGeneratorThread : public QThread { +/* public: - ThumbnailGenerator(PDFDoc *doc, QMutex *docMutex, int page, double ppp, QObject *o); - + PDFGeneratorThread(PDFDoc *doc, QMutex *docMutex, int page, double ppp, QObject *o); int getPage() const; protected: void run(); - + private: PDFDoc *m_doc; QMutex *m_docMutex; int m_page; QObject *m_o; double m_ppp; +*/ }; #endif -*/ - -#endif diff --git a/kpdf/page.cpp b/kpdf/page.cpp index 755e1ec36..6d0796087 100644 --- a/kpdf/page.cpp +++ b/kpdf/page.cpp @@ -53,6 +53,8 @@ bool KPDFPage::hasPixmap( int id, int width, int height ) const { if ( !m_pixmaps.contains( id ) ) return false; + if ( width == -1 || height == -1 ) + return true; QPixmap * p = m_pixmaps[ id ]; return p ? ( p->width() == width && p->height() == height ) : false; } @@ -136,6 +138,15 @@ void KPDFPage::setRects( const QValueList< KPDFPageRect * > rects ) m_rects = rects; } +void KPDFPage::deletePixmap( int id ) +{ + if ( m_pixmaps.contains( id ) ) + { + delete m_pixmaps[ id ]; + m_pixmaps.remove( id ); + } +} + void KPDFPage::deletePixmapsAndRects() { // delete all stored pixmaps diff --git a/kpdf/page.h b/kpdf/page.h index 292f92559..411887cf7 100644 --- a/kpdf/page.h +++ b/kpdf/page.h @@ -45,7 +45,7 @@ class KPDFPage inline float height() const { return m_height; } inline float ratio() const { return m_height / m_width; } - bool hasPixmap( int id, int width, int height ) const; + bool hasPixmap( int id, int width = -1, int height = -1 ) const; bool hasSearchPage() const; bool hasRect( int mouseX, int mouseY ) const; const KPDFPageRect * getRect( int mouseX, int mouseY ) const; @@ -57,10 +57,11 @@ class KPDFPage inline void toggleAttribute( int att ) { m_attributes ^= att; } bool hasText( const QString & text, bool strictCase, bool fromTop ); - // set contents (by KPDFDocument) + // set/delete contents (by KPDFDocument) void setPixmap( int id, QPixmap * pixmap ); void setSearchPage( TextPage * text ); void setRects( const QValueList< KPDFPageRect * > rects ); + void deletePixmap( int id ); void deletePixmapsAndRects(); private: diff --git a/kpdf/pageview.cpp b/kpdf/pageview.cpp index 689fa43e3..522987192 100644 --- a/kpdf/pageview.cpp +++ b/kpdf/pageview.cpp @@ -53,6 +53,7 @@ public: PageViewItem * activeItem; //equal to items[vectorIndex] QValueVector< PageViewItem * > items; int vectorIndex; + QValueList< PageViewItem * > visibleItems; // view layout (columns and continous in Settings), zoom and mouse PageView::ZoomMode zoomMode; @@ -210,25 +211,6 @@ void PageView::setZoomFitWidth() //BEGIN KPDFDocumentObserver inherited methods -void PageView::notifyPixmapChanged( int pageNumber ) -{ - QValueVector< PageViewItem * >::iterator iIt = d->items.begin(), iEnd = d->items.end(); - for ( ; iIt != iEnd; ++iIt ) - if ( (*iIt)->pageNumber() == pageNumber ) - { - // update item's rectangle plus the little outline - QRect expandedRect = (*iIt)->geometry(); - expandedRect.addCoords( -1, -1, 3, 3 ); - updateContents( expandedRect ); - break; - } -} - -void PageView::notifyPixmapsCleared() -{ - slotRequestVisiblePixmaps(); -} - void PageView::pageSetup( const QValueVector & pageSet, bool documentChanged ) { // reuse current pages if nothing new @@ -300,6 +282,36 @@ void PageView::pageSetCurrent( int pageNumber, const QRect & viewport ) if ( d->zoomMode != ZoomFixed ) updateZoomText(); } + +bool PageView::canUnloadPixmap( int pageNumber ) +{ + // if the item is visible, forbid unloading + QValueList< PageViewItem * >::iterator vIt = d->visibleItems.begin(), vEnd = d->visibleItems.end(); + for ( ; vIt != vEnd; ++vIt ) + if ( (*vIt)->pageNumber() == pageNumber ) + return false; + // if hidden premit unloading + return true; +} + +void PageView::notifyPixmapChanged( int pageNumber ) +{ + QValueVector< PageViewItem * >::iterator iIt = d->items.begin(), iEnd = d->items.end(); + for ( ; iIt != iEnd; ++iIt ) + if ( (*iIt)->pageNumber() == pageNumber ) + { + // update item's rectangle plus the little outline + QRect expandedRect = (*iIt)->geometry(); + expandedRect.addCoords( -1, -1, 3, 3 ); + updateContents( expandedRect ); + break; + } +} + +void PageView::notifyPixmapsCleared() +{ + slotRequestVisiblePixmaps(); +} //END KPDFDocumentObserver inherited methods //BEGIN widget events @@ -1297,17 +1309,23 @@ void PageView::slotRequestVisiblePixmaps( int newLeft, int newTop ) newTop == -1 ? contentsY() : newTop, visibleWidth(), visibleHeight() ); - // scroll from the top to the last visible thumbnail + // for each item, check if it intersects the viewport + d->visibleItems.clear(); QValueVector< PageViewItem * >::iterator iIt = d->items.begin(), iEnd = d->items.end(); for ( ; iIt != iEnd; ++iIt ) { PageViewItem * item = *iIt; - const QRect & itemRect = item->geometry(); - if ( viewportRect.intersects( itemRect ) ) - { - d->document->requestPixmap( PAGEVIEW_ID, item->pageNumber(), - itemRect.width(), itemRect.height(), true ); - } + if ( viewportRect.intersects( item->geometry() ) ) + d->visibleItems.push_back( item ); + } + + // actually request pixmaps + QValueList< PageViewItem * >::iterator vIt = d->visibleItems.begin(), vEnd = d->visibleItems.end(); + for ( ; vIt != vEnd; ++vIt ) + { + PageViewItem * item = *vIt; + if ( !item->page()->hasPixmap( PAGEVIEW_ID, item->width(), item->height() ) ) + d->document->requestPixmap( PAGEVIEW_ID, item->pageNumber(), item->width(), item->height(), true ); } } diff --git a/kpdf/pageview.h b/kpdf/pageview.h index fbe500064..2bf908b99 100644 --- a/kpdf/pageview.h +++ b/kpdf/pageview.h @@ -56,10 +56,11 @@ class PageView : public QScrollView, public KPDFDocumentObserver // inherited from KPDFDocumentObserver uint observerId() const { return PAGEVIEW_ID; } - void notifyPixmapChanged( int pageNumber ); - void notifyPixmapsCleared(); void pageSetup( const QValueVector & pages, bool documentChanged ); void pageSetCurrent( int pageNumber, const QRect & viewport ); + bool canUnloadPixmap( int pageNum ); + void notifyPixmapChanged( int pageNumber ); + void notifyPixmapsCleared(); public slots: void slotSetMouseDraw(); diff --git a/kpdf/propertiesdialog.cpp b/kpdf/propertiesdialog.cpp index 9ddf296e7..84d6f6b64 100644 --- a/kpdf/propertiesdialog.cpp +++ b/kpdf/propertiesdialog.cpp @@ -15,16 +15,22 @@ #include "properties.h" #include "propertiesdialog.h" -propertiesDialog::propertiesDialog(QWidget *parent, KPDFDocument *doc) : KDialogBase(parent, 0, true, i18n("PDF properties"), Ok) +propertiesDialog::propertiesDialog(QWidget *parent, KPDFDocument *doc) : KDialogBase(parent, 0, true, i18n( "Unknown file." ), Ok) { + // embed the properties widget (TODO switch to a dynamic generated one) properties *p = new properties(this); setMainWidget(p); + // get document info, if not present display blank data and a warning const DocumentInfo * info = doc->documentInfo(); if ( !info ) { p->titleValue->setText( i18n( "No document opened!" ) ); return; } + // mime name based on mimetype id + QString mimeName = info->mimeType.section( '/', -1 ).upper(); + setCaption( i18n("%1 properties").arg( mimeName ) ); + // fill in document property values p->pagesValue->setText( QString::number( doc->pages() ) ); p->authorValue->setText( info->author ); p->titleValue->setText( info->title ); diff --git a/kpdf/thumbnaillist.cpp b/kpdf/thumbnaillist.cpp index 62a2cd93a..06ff6bffc 100644 --- a/kpdf/thumbnaillist.cpp +++ b/kpdf/thumbnaillist.cpp @@ -34,6 +34,7 @@ class ThumbnailWidget : public QWidget int pixmapWidth() const { return m_pixmapWidth; } int pixmapHeight() const { return m_pixmapHeight; } int pageNumber() const { return m_page->number(); } + const KPDFPage * page() const { return m_page; } protected: void paintEvent(QPaintEvent *); @@ -310,8 +311,9 @@ void ThumbnailList::slotRequestPixmaps( int /*newContentsX*/, int newContentsY ) int top = childY( t ) - vOffset; if ( top > vHeight ) break; - else if ( top + t->height() > 0 ) - m_document->requestPixmap( THUMBNAILS_ID, t->pageNumber(), t->pixmapWidth(), t->pixmapHeight(), true ); + else if ( top + t->height() > 0 && + !t->page()->hasPixmap( THUMBNAILS_ID, t->pixmapWidth(), t->pixmapHeight() ) ) + m_document->requestPixmap( THUMBNAILS_ID, t->pageNumber(), t->pixmapWidth(), t->pixmapHeight(), true ); } } //END internal SLOTS