mirror of
https://invent.kde.org/graphics/okular
synced 2024-10-05 15:39:47 +00:00
Changed pixmap requesting methods. Now each request is packed into a
PixmapRequest class. When requesting pixmaps, one or multiple requests are sent to the Document that (frees memory as in current policy) and send each PixmapRequest to the current Generator. Added a signal in generators to notify the Document when a pixmap generation has finished. PageView, ThumbnailsList, PreviewWidget have been unbroken after the memory management commit. (mem management seems in pretty good shape..it's smart.) Added 'visible widgets' list to those classes to speed up searching and processing on visible widgets only. Note: asyncronous pixmap requests can now be queued and we're getting very close to the threaded generator. Note2: Leakfixes and memory improvements. Final NOTE: head merging is possible now, as all remaining work can be considered bugfixes.. API is getting final. It will only change in xpdf dep stuff, the already undefined Viewport object and some bits in Generators. svn path=/branches/kpdf_experiments/kdegraphics/kpdf/; revision=372787
This commit is contained in:
parent
c9dc4c5bc9
commit
16908b2f0c
|
@ -27,6 +27,7 @@ Higher priority after merge:
|
|||
|
||||
More items (first items will enter 'In progress list' first):
|
||||
*THIS ITEMS ARE CURRENTLY FROZEN SINCE "HEAD-MERGE" LIST IS CLEARED*
|
||||
-> fix: iterate from older pages to newest ones when freeing memory (not randomly)
|
||||
-> fix: On continous view mode, if you click to a link that moves you to another
|
||||
page, then scroll up and click again on the same link it does not work. (by Albert)
|
||||
-> JJ: convert DocumentInfo to a DomTree containing a few common fields (see the current
|
||||
|
|
|
@ -95,14 +95,18 @@ bool KPDFDocument::openDocument( const QString & docFile )
|
|||
QString mimeName = mime->name();
|
||||
if ( mimeName == "application/pdf" )
|
||||
generator = new PDFGenerator();
|
||||
// else if ( mimeName == "application/postscript" )
|
||||
// generator = new PSGenerator();
|
||||
else if ( mimeName == "application/postscript" )
|
||||
kdError() << "PS generator not available" << endl;
|
||||
else
|
||||
{
|
||||
kdWarning() << "Unknown mimetype '" << mimeName << "'." << endl;
|
||||
return false;
|
||||
}
|
||||
// get notification of completed jobs
|
||||
connect( generator, SIGNAL( contentsChanged( int, int ) ),
|
||||
this, SLOT( slotGeneratedContents( int, int ) ) );
|
||||
|
||||
// ask generator to open the document and return if unsuccessfull
|
||||
documentFileName = docFile;
|
||||
bool openOk = generator->loadDocument( docFile, pages_vector );
|
||||
if ( !openOk )
|
||||
|
@ -134,6 +138,15 @@ void KPDFDocument::closeDocument()
|
|||
// send an empty list to observers (to free their data)
|
||||
foreachObserver( pageSetup( pages_vector, true ) );
|
||||
|
||||
// clear memory management data
|
||||
QMap< int, ObserverData * >::iterator oIt = d->observers.begin(), oEnd = d->observers.end();
|
||||
for ( ; oIt != oEnd ; ++oIt )
|
||||
{
|
||||
ObserverData * observerData = *oIt;
|
||||
observerData->pageMemory.clear();
|
||||
observerData->totalMemory = 0;
|
||||
}
|
||||
|
||||
// delete contents generator
|
||||
delete generator;
|
||||
generator = 0;
|
||||
|
@ -215,65 +228,35 @@ bool KPDFDocument::okToPrint() const
|
|||
}
|
||||
|
||||
|
||||
void KPDFDocument::requestPixmap( int id, int page, int width, int height, bool syn )
|
||||
void KPDFDocument::requestPixmaps( const QValueList< PixmapRequest * > & requests, bool syn )
|
||||
{
|
||||
KPDFPage * kp = pages_vector[ page ];
|
||||
if ( !generator || !kp || kp->width() < 1 || kp->height() < 1 )
|
||||
if ( !generator )
|
||||
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() )
|
||||
QValueList< PixmapRequest * >::const_iterator rIt = requests.begin(), rEnd = requests.end();
|
||||
for ( ; rIt != rEnd; ++rIt )
|
||||
{
|
||||
case Settings::EnumMemoryLevel::Low:
|
||||
memoryToFree = obs->totalMemory;
|
||||
break;
|
||||
// set the 'page field' (see PixmapRequest) and check if request is valid
|
||||
PixmapRequest * request = *rIt;
|
||||
request->page = pages_vector[ request->pageNumber ];
|
||||
if ( !request->page || request->page->width() < 1 || request->page->height() < 1 )
|
||||
continue;
|
||||
|
||||
case Settings::EnumMemoryLevel::Normal:
|
||||
memoryToFree = obs->totalMemory - mTotalMemory()/4;
|
||||
printf("%d\n",memoryToFree);
|
||||
break;
|
||||
// 1. Update statistics (pageMemory / totalMemory) adding this pixmap
|
||||
int pageNumber = request->pageNumber;
|
||||
ObserverData * obs = d->observers[ request->id ];
|
||||
if ( obs->pageMemory.contains( pageNumber ) )
|
||||
obs->totalMemory -= obs->pageMemory[ pageNumber ];
|
||||
int pixmapMemory = 4 * request->width * request->height / 1024;
|
||||
obs->pageMemory[ pageNumber ] = pixmapMemory;
|
||||
obs->totalMemory += pixmapMemory;
|
||||
|
||||
case Settings::EnumMemoryLevel::Aggressive:
|
||||
memoryToFree = 0;
|
||||
break;
|
||||
// 2. Perform pre-cleaning if needed
|
||||
mCleanupMemory( request->id );
|
||||
|
||||
// 3. Enqueue to Generator (that takes ownership of request)
|
||||
generator->requestPixmap( request, syn );
|
||||
}
|
||||
|
||||
// 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]->observer->notifyPixmapChanged( page );
|
||||
}
|
||||
|
||||
void KPDFDocument::requestTextPage( uint page )
|
||||
|
@ -506,13 +489,63 @@ bool KPDFDocument::print( KPrinter &printer )
|
|||
}
|
||||
|
||||
|
||||
void KPDFDocument::mCleanupMemory( int observerId )
|
||||
{
|
||||
// get observer data for given id
|
||||
ObserverData * obs = d->observers[ observerId ];
|
||||
|
||||
// choose memory parameters based on configuration profile
|
||||
int memoryToFree = 0;
|
||||
switch ( Settings::memoryLevel() )
|
||||
{
|
||||
case Settings::EnumMemoryLevel::Low:
|
||||
memoryToFree = obs->totalMemory;
|
||||
break;
|
||||
|
||||
case Settings::EnumMemoryLevel::Normal:
|
||||
memoryToFree = obs->totalMemory - mTotalMemory()/4;
|
||||
break;
|
||||
|
||||
case Settings::EnumMemoryLevel::Aggressive:
|
||||
memoryToFree = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
// free memory. remove older data until we free enough memory
|
||||
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 ( obs->observer->canUnloadPixmap( freeNumber ) )
|
||||
{
|
||||
// update mem stats
|
||||
memoryToFree -= it.data();
|
||||
obs->totalMemory -= it.data();
|
||||
obs->pageMemory.remove( it );
|
||||
// delete pixmap
|
||||
pages_vector[ freeNumber ]->deletePixmap( observerId );
|
||||
freed++;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
}
|
||||
kdWarning() << "[" << obs->totalMemory << "kB] Removed " << freed << " pages. " << obs->pageMemory.count() << " pages kept in memory." << endl;
|
||||
}
|
||||
|
||||
int KPDFDocument::mTotalMemory()
|
||||
{
|
||||
static int cachedValue = 0;
|
||||
if ( cachedValue )
|
||||
return cachedValue;
|
||||
|
||||
#ifdef __linux__
|
||||
// if /proc/meminfo doesn't exist, return 128MB
|
||||
QFile memFile( "/proc/meminfo" );
|
||||
if ( !memFile.open( IO_ReadOnly ) )
|
||||
return 131072;
|
||||
return (cachedValue = 131072);
|
||||
|
||||
// read /proc/meminfo and sum up the contents of 'MemFree', 'Buffers'
|
||||
// and 'Cached' fields. consider swapped memory as used memory.
|
||||
|
@ -521,10 +554,10 @@ int KPDFDocument::mTotalMemory()
|
|||
{
|
||||
QString entry = readStream.readLine();
|
||||
if ( entry.startsWith( "MemTotal:" ) )
|
||||
return entry.section( ' ', -2, -2 ).toInt();
|
||||
return (cachedValue = entry.section( ' ', -2, -2 ).toInt());
|
||||
}
|
||||
#endif
|
||||
return 131072;
|
||||
return (cachedValue = 131072);
|
||||
}
|
||||
|
||||
int KPDFDocument::mFreeMemory()
|
||||
|
@ -622,3 +655,11 @@ void KPDFDocument::unHilightPages()
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KPDFDocument::slotGeneratedContents( int id, int pageNumber )
|
||||
{
|
||||
if ( d->observers.contains( id ) )
|
||||
d->observers[ id ]->observer->notifyPixmapChanged( pageNumber );
|
||||
}
|
||||
|
||||
#include "document.moc"
|
||||
|
|
|
@ -21,6 +21,7 @@ class KPDFLink;
|
|||
class Generator;
|
||||
class DocumentInfo;
|
||||
class DocumentSynopsis;
|
||||
class PixmapRequest;
|
||||
|
||||
/**
|
||||
* @short Base class for objects being notified when something changes.
|
||||
|
@ -69,8 +70,9 @@ class KPDFDocumentObserver
|
|||
* For a better understanding of hieracies @see README.internals.png
|
||||
* @see KPDFDocumentObserver, KPDFPage
|
||||
*/
|
||||
class KPDFDocument
|
||||
class KPDFDocument : public QObject // only for a private slot..
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
KPDFDocument();
|
||||
~KPDFDocument();
|
||||
|
@ -93,8 +95,7 @@ class KPDFDocument
|
|||
bool okToPrint() const;
|
||||
|
||||
// perform actions on document / pages
|
||||
//void requestPixmaps( int id, const QValueList<int> & pages, int width, int height, bool syncronous = false );
|
||||
void requestPixmap( int id, int pageNum, int width, int height, bool syncronous = false );
|
||||
void requestPixmaps( const QValueList< PixmapRequest * > & requests, bool syncronous = false );
|
||||
void requestTextPage( uint page );
|
||||
void setCurrentPage( int page, const QRect & viewport = QRect() );
|
||||
void findText( const QString & text = "", bool caseSensitive = false );
|
||||
|
@ -105,6 +106,7 @@ class KPDFDocument
|
|||
|
||||
private:
|
||||
// memory management related functions
|
||||
void mCleanupMemory( int observerId );
|
||||
int mTotalMemory();
|
||||
int mFreeMemory();
|
||||
// more private functions
|
||||
|
@ -117,6 +119,9 @@ class KPDFDocument
|
|||
QString documentFileName;
|
||||
QValueVector< KPDFPage * > pages_vector;
|
||||
class KPDFDocumentPrivate * d;
|
||||
|
||||
private slots:
|
||||
void slotGeneratedContents( int id, int pageNumber );
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -19,6 +19,7 @@ class KPDFLink;
|
|||
class KPDFDocument;
|
||||
class DocumentSynopsis;
|
||||
class DocumentInfo;
|
||||
class PixmapRequest;
|
||||
|
||||
/* Note: on contents generation and asyncronous queries.
|
||||
* Many observers may want to request data syncronously or asyncronously.
|
||||
|
@ -57,15 +58,34 @@ class Generator : public QObject
|
|||
virtual bool allowed( int /*permisisons*/ ) { return true; }
|
||||
|
||||
// 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 bool print( KPrinter& /*printer*/ ) { return false; }
|
||||
virtual void requestPixmap( PixmapRequest * request, bool syncronous = false ) = 0;
|
||||
virtual void requestTextPage( KPDFPage * page ) = 0;
|
||||
|
||||
// check configuration and return true if something changed
|
||||
virtual bool reparseConfig() { return false; }
|
||||
|
||||
signals:
|
||||
void contentsChanged( const KPDFPage * page );
|
||||
void contentsChanged( int id, int pageNumber );
|
||||
};
|
||||
|
||||
/**
|
||||
* @short Describes a pixmap type request.
|
||||
*/
|
||||
struct PixmapRequest
|
||||
{
|
||||
// public data fields
|
||||
int id;
|
||||
int pageNumber;
|
||||
int width;
|
||||
int height;
|
||||
// this field is set by the document before passing the
|
||||
// request to the generator
|
||||
KPDFPage * page;
|
||||
|
||||
// public constructor: initialize data
|
||||
PixmapRequest( int rId, int n, int w, int h )
|
||||
: id( rId ), pageNumber( n ), width( w ), height( h ), page( 0 ) {};
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -259,47 +259,45 @@ bool PDFGenerator::print( KPrinter& printer )
|
|||
}
|
||||
}
|
||||
|
||||
bool PDFGenerator::requestPixmap( int id, KPDFPage * page, int width, int height, bool syncronous )
|
||||
void PDFGenerator::requestPixmap( PixmapRequest * request, bool syncronous )
|
||||
{
|
||||
//kdDebug() << "id: " << id << " is requesting pixmap for page " << page->number() << " [" << width << " x " << height << "]." << endl;
|
||||
if ( syncronous )
|
||||
{
|
||||
// in-place Pixmap generation for syncronous requests
|
||||
if ( !page->hasPixmap( id, width, height ) )
|
||||
{
|
||||
// compute dpi used to get an image with desired width and height
|
||||
double fakeDpiX = width * 72.0 / page->width(),
|
||||
fakeDpiY = height * 72.0 / page->height();
|
||||
KPDFPage * page = request->page;
|
||||
if ( page->hasPixmap( request->id, request->width, request->height ) )
|
||||
return;
|
||||
|
||||
// setup kpdf output device: text page is generated only if we are at 72dpi.
|
||||
// since we can pre-generate the TextPage at the right res.. why not?
|
||||
bool genTextPage = !page->hasSearchPage() && (width == page->width()) && (height == page->height());
|
||||
// generate links and image rects if rendering pages on pageview
|
||||
bool genRects = id == PAGEVIEW_ID;
|
||||
kpdfOutputDev->setParams( width, height, genTextPage, genRects, genRects );
|
||||
// compute dpi used to get an image with desired width and height
|
||||
double fakeDpiX = request->width * 72.0 / page->width(),
|
||||
fakeDpiY = request->height * 72.0 / page->height();
|
||||
|
||||
docLock.lock();
|
||||
pdfdoc->displayPage( kpdfOutputDev, page->number() + 1, fakeDpiX, fakeDpiY, 0, true, genRects );
|
||||
docLock.unlock();
|
||||
// setup kpdf output device: text page is generated only if we are at 72dpi.
|
||||
// since we can pre-generate the TextPage at the right res.. why not?
|
||||
bool genTextPage = !page->hasSearchPage() && (request->width == page->width()) &&
|
||||
(request->height == page->height());
|
||||
// generate links and image rects if rendering pages on pageview
|
||||
bool genRects = request->id == PAGEVIEW_ID;
|
||||
kpdfOutputDev->setParams( request->width, request->height, genTextPage, genRects, genRects );
|
||||
|
||||
page->setPixmap( id, kpdfOutputDev->takePixmap() );
|
||||
if ( genTextPage )
|
||||
page->setSearchPage( kpdfOutputDev->takeTextPage() );
|
||||
if ( genRects )
|
||||
page->setRects( kpdfOutputDev->takeRects() );
|
||||
docLock.lock();
|
||||
pdfdoc->displayPage( kpdfOutputDev, page->number() + 1, fakeDpiX, fakeDpiY, 0, true, genRects );
|
||||
docLock.unlock();
|
||||
|
||||
// pixmap generated
|
||||
return true;
|
||||
}
|
||||
page->setPixmap( request->id, kpdfOutputDev->takePixmap() );
|
||||
if ( genTextPage )
|
||||
page->setSearchPage( kpdfOutputDev->takeTextPage() );
|
||||
if ( genRects )
|
||||
page->setRects( kpdfOutputDev->takeRects() );
|
||||
|
||||
// pixmap generated
|
||||
contentsChanged( request->id, request->pageNumber );
|
||||
}
|
||||
else
|
||||
{
|
||||
//TODO asyncronous events queuing
|
||||
return false;
|
||||
// TODO add the pixmaprequest in a queue ...
|
||||
}
|
||||
|
||||
// no pixmap generated
|
||||
return false;
|
||||
}
|
||||
|
||||
void PDFGenerator::requestTextPage( KPDFPage * page )
|
||||
|
|
|
@ -52,7 +52,7 @@ class PDFGenerator : public Generator
|
|||
|
||||
// [INHERITED] perform actions on document / pages
|
||||
bool print( KPrinter& printer );
|
||||
bool requestPixmap( int id, KPDFPage * page, int width, int height, bool syncronous = false );
|
||||
void requestPixmap( PixmapRequest * request, bool syncronous = false );
|
||||
void requestTextPage( KPDFPage * page );
|
||||
|
||||
// [INHERITED] reparse configuration
|
||||
|
@ -73,6 +73,8 @@ class PDFGenerator : public Generator
|
|||
PDFDoc * pdfdoc;
|
||||
KPDFOutputDev * kpdfOutputDev;
|
||||
QColor paperColor;
|
||||
//PixmapRequest * currentRequest;
|
||||
//QValueList< PixmapRequest * > requests;
|
||||
bool docInfoDirty;
|
||||
DocumentInfo docInfo;
|
||||
bool docSynopsisDirty;
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
#include "pageviewutils.h"
|
||||
#include "page.h"
|
||||
#include "link.h"
|
||||
#include "generator.h"
|
||||
#include "settings.h"
|
||||
|
||||
#define ROUND(x) (int(x + 0.5))
|
||||
|
@ -1311,22 +1312,22 @@ void PageView::slotRequestVisiblePixmaps( int newLeft, int newTop )
|
|||
|
||||
// for each item, check if it intersects the viewport
|
||||
d->visibleItems.clear();
|
||||
QValueList< PixmapRequest * > requestedPixmaps;
|
||||
QValueVector< PageViewItem * >::iterator iIt = d->items.begin(), iEnd = d->items.end();
|
||||
for ( ; iIt != iEnd; ++iIt )
|
||||
{
|
||||
PageViewItem * item = *iIt;
|
||||
if ( viewportRect.intersects( item->geometry() ) )
|
||||
d->visibleItems.push_back( item );
|
||||
PageViewItem * i = *iIt;
|
||||
if ( !viewportRect.intersects( i->geometry() ) )
|
||||
continue;
|
||||
|
||||
d->visibleItems.push_back( i );
|
||||
if ( !i->page()->hasPixmap( PAGEVIEW_ID, i->width(), i->height() ) )
|
||||
requestedPixmaps.push_back( new PixmapRequest( PAGEVIEW_ID, i->pageNumber(), i->width(), i->height() ) );
|
||||
}
|
||||
|
||||
// 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 );
|
||||
}
|
||||
if ( !requestedPixmaps.isEmpty() )
|
||||
d->document->requestPixmaps( requestedPixmaps, true );
|
||||
}
|
||||
|
||||
void PageView::slotAutoScoll()
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
// local includes
|
||||
#include "presentationwidget.h"
|
||||
#include "document.h" // for PRESENTATION_ID
|
||||
#include "generator.h"
|
||||
#include "page.h"
|
||||
|
||||
|
||||
|
@ -113,7 +114,7 @@ void PresentationWidget::pageSetup( const QValueVector<KPDFPage*> & pageSet, boo
|
|||
{
|
||||
PresentationFrame * frame = new PresentationFrame();
|
||||
frame->page = *setIt;
|
||||
frame->transType = Glitter; //TODO get transition from the document
|
||||
frame->transType = NoTrans; //TODO get transition from the document
|
||||
frame->transDir = Left;
|
||||
// calculate frame geometry keeping constant aspect ratio
|
||||
float pageRatio = frame->page->ratio();
|
||||
|
@ -144,6 +145,12 @@ void PresentationWidget::pageSetup( const QValueVector<KPDFPage*> & pageSet, boo
|
|||
m_metaStrings += i18n( "Click to begin" );
|
||||
}
|
||||
|
||||
bool PresentationWidget::canUnloadPixmap( int pageNumber )
|
||||
{
|
||||
// can unload all pixmaps except for the currently visible one
|
||||
return pageNumber != m_frameIndex;
|
||||
}
|
||||
|
||||
void PresentationWidget::notifyPixmapChanged( int pageNumber )
|
||||
{
|
||||
// check if it's the last requested pixmap. if so update the widget.
|
||||
|
@ -462,7 +469,11 @@ void PresentationWidget::slotNextPage()
|
|||
// if pixmap not inside the KPDFPage we request it and wait for
|
||||
// notifyPixmapChanged call or else we proceed to pixmap generation
|
||||
if ( !frame->page->hasPixmap( PRESENTATION_ID, pixW, pixH ) )
|
||||
m_document->requestPixmap( PRESENTATION_ID, m_frameIndex, pixW, pixH, true );
|
||||
{
|
||||
QValueList< PixmapRequest * > request;
|
||||
request.push_back( new PixmapRequest( PRESENTATION_ID, m_frameIndex, pixW, pixH ) );
|
||||
m_document->requestPixmaps( request, true );
|
||||
}
|
||||
else
|
||||
generatePage();
|
||||
}
|
||||
|
@ -487,7 +498,11 @@ void PresentationWidget::slotPrevPage()
|
|||
// if pixmap not inside the KPDFPage we request it and wait for
|
||||
// notifyPixmapChanged call or else we can proceed to pixmap generation
|
||||
if ( !frame->page->hasPixmap( PRESENTATION_ID, pixW, pixH ) )
|
||||
m_document->requestPixmap( PRESENTATION_ID, m_frameIndex, pixW, pixH, true );
|
||||
{
|
||||
QValueList< PixmapRequest * > request;
|
||||
request.push_back( new PixmapRequest( PRESENTATION_ID, m_frameIndex, pixW, pixH ) );
|
||||
m_document->requestPixmaps( request, true );
|
||||
}
|
||||
else
|
||||
generatePage();
|
||||
}
|
||||
|
|
|
@ -40,6 +40,7 @@ class PresentationWidget : public QWidget, public KPDFDocumentObserver
|
|||
// inherited from KPDFDocumentObserver
|
||||
uint observerId() const { return PRESENTATION_ID; }
|
||||
void pageSetup( const QValueVector<KPDFPage*> & pages, bool documentChanged );
|
||||
bool canUnloadPixmap( int pageNumber );
|
||||
void notifyPixmapChanged( int pageNumber );
|
||||
|
||||
protected:
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <kactioncollection.h>
|
||||
|
||||
#include "thumbnaillist.h"
|
||||
#include "generator.h"
|
||||
#include "page.h"
|
||||
|
||||
// ThumbnailWidget represents a single thumbnail in the ThumbnailList
|
||||
|
@ -76,29 +77,12 @@ ThumbnailList::ThumbnailList( QWidget *parent, KPDFDocument *document )
|
|||
|
||||
|
||||
//BEGIN KPDFDocumentObserver inherited methods
|
||||
void ThumbnailList::notifyPixmapChanged( int pageNumber )
|
||||
{
|
||||
QValueVector<ThumbnailWidget *>::iterator thumbIt = m_thumbnails.begin(), thumbEnd = m_thumbnails.end();
|
||||
for (; thumbIt != thumbEnd; ++thumbIt)
|
||||
if ( (*thumbIt)->pageNumber() == pageNumber )
|
||||
{
|
||||
(*thumbIt)->update();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ThumbnailList::notifyPixmapsCleared()
|
||||
{
|
||||
slotRequestPixmaps();
|
||||
}
|
||||
|
||||
void ThumbnailList::pageSetup( const QValueVector<KPDFPage*> & pages, bool /*documentChanged*/ )
|
||||
{
|
||||
// delete all the Thumbnails
|
||||
QValueVector<ThumbnailWidget *>::iterator thumbIt = m_thumbnails.begin();
|
||||
QValueVector<ThumbnailWidget *>::iterator thumbEnd = m_thumbnails.end();
|
||||
for ( ; thumbIt != thumbEnd; ++thumbIt )
|
||||
delete *thumbIt;
|
||||
QValueVector<ThumbnailWidget *>::iterator tIt = m_thumbnails.begin(), tEnd = m_thumbnails.end();
|
||||
for ( ; tIt != tEnd; ++tIt )
|
||||
delete *tIt;
|
||||
m_thumbnails.clear();
|
||||
m_selected = 0;
|
||||
|
||||
|
@ -118,10 +102,10 @@ void ThumbnailList::pageSetup( const QValueVector<KPDFPage*> & pages, bool /*doc
|
|||
ThumbnailWidget *t;
|
||||
int width = clipper()->width(),
|
||||
totalHeight = 0;
|
||||
QValueVector<KPDFPage*>::const_iterator pageIt = pages.begin();
|
||||
QValueVector<KPDFPage*>::const_iterator pageEnd = pages.end();
|
||||
QValueVector<KPDFPage*>::const_iterator pageIt = pages.begin(), pageEnd = pages.end();
|
||||
for (; pageIt != pageEnd ; ++pageIt)
|
||||
if ( skipCheck || ( (*pageIt)->attributes() & KPDFPage::Highlight ) ) {
|
||||
if ( skipCheck || ( (*pageIt)->attributes() & KPDFPage::Highlight ) )
|
||||
{
|
||||
t = new ThumbnailWidget( viewport(), *pageIt );
|
||||
t->setFocusProxy( this );
|
||||
// add to the scrollview
|
||||
|
@ -150,13 +134,12 @@ void ThumbnailList::pageSetCurrent( int pageNumber, const QRect & /*viewport*/ )
|
|||
|
||||
// select next page
|
||||
m_vectorIndex = 0;
|
||||
QValueVector<ThumbnailWidget *>::iterator thumbIt = m_thumbnails.begin();
|
||||
QValueVector<ThumbnailWidget *>::iterator thumbEnd = m_thumbnails.end();
|
||||
for (; thumbIt != thumbEnd; ++thumbIt)
|
||||
QValueVector<ThumbnailWidget *>::iterator tIt = m_thumbnails.begin(), tEnd = m_thumbnails.end();
|
||||
for ( ; tIt != tEnd; ++tIt )
|
||||
{
|
||||
if ( (*thumbIt)->pageNumber() == pageNumber )
|
||||
if ( (*tIt)->pageNumber() == pageNumber )
|
||||
{
|
||||
m_selected = *thumbIt;
|
||||
m_selected = *tIt;
|
||||
m_selected->setSelected( true );
|
||||
ensureVisible( 0, childY( m_selected ) + m_selected->height()/2, 0, visibleHeight()/2 );
|
||||
//non-centered version: ensureVisible( 0, itemTop + itemHeight/2, 0, itemHeight/2 );
|
||||
|
@ -166,23 +149,49 @@ void ThumbnailList::pageSetCurrent( int pageNumber, const QRect & /*viewport*/ )
|
|||
}
|
||||
}
|
||||
|
||||
bool ThumbnailList::canUnloadPixmap( int pageNumber )
|
||||
{
|
||||
// if the thubnail 'pageNumber' is one of the visible ones, forbid unloading
|
||||
QValueList<ThumbnailWidget *>::iterator vIt = m_visibleThumbnails.begin(), vEnd = m_visibleThumbnails.end();
|
||||
for ( ; vIt != vEnd; ++vIt )
|
||||
if ( (*vIt)->pageNumber() == pageNumber )
|
||||
return false;
|
||||
// if hidden permit unloading
|
||||
return true;
|
||||
}
|
||||
|
||||
void ThumbnailList::notifyPixmapChanged( int pageNumber )
|
||||
{
|
||||
QValueList<ThumbnailWidget *>::iterator vIt = m_visibleThumbnails.begin(), vEnd = m_visibleThumbnails.end();
|
||||
for ( ; vIt != vEnd; ++vIt )
|
||||
if ( (*vIt)->pageNumber() == pageNumber )
|
||||
{
|
||||
(*vIt)->update();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ThumbnailList::notifyPixmapsCleared()
|
||||
{
|
||||
slotRequestPixmaps();
|
||||
}
|
||||
|
||||
|
||||
void ThumbnailList::updateWidgets()
|
||||
{
|
||||
// find all widgets that intersects the viewport and update them
|
||||
QRect viewportRect( contentsX(), contentsY(), visibleWidth(), visibleHeight() );
|
||||
QValueVector<ThumbnailWidget *>::iterator tIt = m_thumbnails.begin(), tEnd = m_thumbnails.end();
|
||||
for ( ; tIt != tEnd; ++tIt )
|
||||
QValueList<ThumbnailWidget *>::iterator vIt = m_visibleThumbnails.begin(), vEnd = m_visibleThumbnails.end();
|
||||
for ( ; vIt != vEnd; ++vIt )
|
||||
{
|
||||
ThumbnailWidget * t = *tIt;
|
||||
ThumbnailWidget * t = *vIt;
|
||||
QRect widgetRect( childX( t ), childY( t ), t->width(), t->height() );
|
||||
if ( viewportRect.intersects( widgetRect ) )
|
||||
{
|
||||
// update only the exposed area of the widget (saves pixels..)
|
||||
QRect relativeRect = viewportRect.intersect( widgetRect );
|
||||
relativeRect.moveBy( -widgetRect.left(), -widgetRect.top() );
|
||||
t->update( relativeRect );
|
||||
}
|
||||
// update only the exposed area of the widget (saves pixels..)
|
||||
QRect relativeRect = viewportRect.intersect( widgetRect );
|
||||
if ( !relativeRect.isValid() )
|
||||
continue;
|
||||
relativeRect.moveBy( -widgetRect.left(), -widgetRect.top() );
|
||||
t->update( relativeRect );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -240,11 +249,10 @@ void ThumbnailList::contentsMousePressEvent( QMouseEvent * e )
|
|||
if ( e->button() != Qt::LeftButton )
|
||||
return;
|
||||
int clickY = e->y();
|
||||
QValueVector<ThumbnailWidget *>::iterator thumbIt = m_thumbnails.begin();
|
||||
QValueVector<ThumbnailWidget *>::iterator thumbEnd = m_thumbnails.end();
|
||||
for ( ; thumbIt != thumbEnd; ++thumbIt )
|
||||
QValueList<ThumbnailWidget *>::iterator vIt = m_visibleThumbnails.begin(), vEnd = m_visibleThumbnails.end();
|
||||
for ( ; vIt != vEnd; ++vIt )
|
||||
{
|
||||
ThumbnailWidget * t = *thumbIt;
|
||||
ThumbnailWidget * t = *vIt;
|
||||
int childTop = childY(t);
|
||||
if ( clickY > childTop && clickY < (childTop + t->height()) )
|
||||
{
|
||||
|
@ -268,11 +276,10 @@ void ThumbnailList::viewportResizeEvent( QResizeEvent * e )
|
|||
// resize and reposition items
|
||||
int totalHeight = 0,
|
||||
newWidth = e->size().width();
|
||||
QValueVector<ThumbnailWidget *>::iterator thumbIt = m_thumbnails.begin();
|
||||
QValueVector<ThumbnailWidget *>::iterator thumbEnd = m_thumbnails.end();
|
||||
for ( ; thumbIt != thumbEnd; ++thumbIt )
|
||||
QValueVector<ThumbnailWidget *>::iterator tIt = m_thumbnails.begin(), tEnd = m_thumbnails.end();
|
||||
for ( ; tIt != tEnd; ++tIt )
|
||||
{
|
||||
ThumbnailWidget *t = *thumbIt;
|
||||
ThumbnailWidget *t = *tIt;
|
||||
moveChild( t, 0, totalHeight );
|
||||
t->resizeFitWidth( newWidth );
|
||||
totalHeight += t->heightHint() + 4;
|
||||
|
@ -302,19 +309,28 @@ void ThumbnailList::slotRequestPixmaps( int /*newContentsX*/, int newContentsY )
|
|||
int vHeight = visibleHeight(),
|
||||
vOffset = newContentsY == -1 ? contentsY() : newContentsY;
|
||||
|
||||
// scroll from the top to the last visible thumbnail
|
||||
QValueVector<ThumbnailWidget *>::iterator thumbIt = m_thumbnails.begin();
|
||||
QValueVector<ThumbnailWidget *>::iterator thumbEnd = m_thumbnails.end();
|
||||
for ( ; thumbIt != thumbEnd; ++thumbIt )
|
||||
{
|
||||
ThumbnailWidget * t = *thumbIt;
|
||||
int top = childY( t ) - vOffset;
|
||||
if ( top > vHeight )
|
||||
break;
|
||||
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 );
|
||||
}
|
||||
// scroll from the top to the last visible thumbnail
|
||||
m_visibleThumbnails.clear();
|
||||
QValueList< PixmapRequest * > requestedPixmaps;
|
||||
QValueVector<ThumbnailWidget *>::iterator tIt = m_thumbnails.begin(), tEnd = m_thumbnails.end();
|
||||
for ( ; tIt != tEnd; ++tIt )
|
||||
{
|
||||
ThumbnailWidget * t = *tIt;
|
||||
int top = childY( t ) - vOffset;
|
||||
if ( top > vHeight )
|
||||
break;
|
||||
if ( top + t->height() < 0 )
|
||||
continue;
|
||||
// add ThumbnailWidget to visible list
|
||||
m_visibleThumbnails.push_back( t );
|
||||
// if pixmap not present add it to requests
|
||||
if ( !t->page()->hasPixmap( THUMBNAILS_ID, t->pixmapWidth(), t->pixmapHeight() ) )
|
||||
requestedPixmaps.push_back( new PixmapRequest( THUMBNAILS_ID, t->pageNumber(), t->pixmapWidth(), t->pixmapHeight() ) );
|
||||
}
|
||||
|
||||
// actually request pixmaps
|
||||
if ( !requestedPixmaps.isEmpty() )
|
||||
m_document->requestPixmaps( requestedPixmaps, true );
|
||||
}
|
||||
//END internal SLOTS
|
||||
|
||||
|
|
|
@ -30,21 +30,19 @@ Q_OBJECT
|
|||
public:
|
||||
ThumbnailList(QWidget *parent, KPDFDocument *document);
|
||||
|
||||
// return thumbnails observer id
|
||||
uint observerId() const { return THUMBNAILS_ID; }
|
||||
|
||||
// redraw thumbnail ( inherited as DocumentObserver )
|
||||
// inherited: return thumbnails observer id
|
||||
uint observerId() const { return THUMBNAILS_ID; }
|
||||
// inherited: create thumbnails ( inherited as a DocumentObserver )
|
||||
void pageSetup( const QValueVector<KPDFPage*> & pages, bool documentChanged );
|
||||
// inherited: hilihght current thumbnail ( inherited as DocumentObserver )
|
||||
void pageSetCurrent( int pageNumber, const QRect & viewport );
|
||||
// inherited: tell if pixmap is hidden and can be unloaded
|
||||
bool canUnloadPixmap( int pageNumber );
|
||||
// inherited: redraw thumbnail ( inherited as DocumentObserver )
|
||||
void notifyPixmapChanged( int pageNumber );
|
||||
|
||||
// request all visible pixmap (due to a global shange or so..)
|
||||
// inherited: request all visible pixmap (due to a global shange or so..)
|
||||
void notifyPixmapsCleared();
|
||||
|
||||
// create thumbnails ( inherited as a DocumentObserver )
|
||||
void pageSetup( const QValueVector<KPDFPage*> & pages, bool documentChanged );
|
||||
|
||||
// hilihght current thumbnail ( inherited as DocumentObserver )
|
||||
void pageSetCurrent( int pageNumber, const QRect & viewport );
|
||||
|
||||
// redraw visible widgets (useful for refreshing contents...)
|
||||
void updateWidgets();
|
||||
|
||||
|
@ -75,6 +73,7 @@ Q_OBJECT
|
|||
ThumbnailWidget *m_selected;
|
||||
QTimer *m_delayTimer;
|
||||
QValueVector<ThumbnailWidget *> m_thumbnails;
|
||||
QValueList<ThumbnailWidget *> m_visibleThumbnails;
|
||||
int m_vectorIndex;
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue