You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
878 lines
30 KiB
878 lines
30 KiB
/*************************************************************************** |
|
* Copyright (C) 2004-2006 by Albert Astals Cid <tsdgeos@terra.es> * |
|
* * |
|
* This program is free software; you can redistribute it and/or modify * |
|
* it under the terms of the GNU General Public License as published by * |
|
* the Free Software Foundation; either version 2 of the License, or * |
|
* (at your option) any later version. * |
|
***************************************************************************/ |
|
|
|
#include "thumbnaillist.h" |
|
|
|
// qt/kde includes |
|
#include <qevent.h> |
|
#include <qtimer.h> |
|
#include <qpainter.h> |
|
#include <qscrollbar.h> |
|
#include <qsizepolicy.h> |
|
#include <klocale.h> |
|
#include <kurl.h> |
|
#include <kaction.h> |
|
#include <kdialog.h> |
|
#include <kiconloader.h> |
|
#include <kactioncollection.h> |
|
#include <kicon.h> |
|
|
|
// local includes |
|
#include "pagepainter.h" |
|
#include "core/area.h" |
|
#include "core/bookmarkmanager.h" |
|
#include "core/document.h" |
|
#include "core/generator.h" |
|
#include "core/page.h" |
|
#include "settings.h" |
|
|
|
class ThumbnailWidget; |
|
|
|
class ThumbnailListPrivate : public QWidget |
|
{ |
|
public: |
|
ThumbnailListPrivate( ThumbnailList *qq, Okular::Document *document ); |
|
~ThumbnailListPrivate(); |
|
|
|
ThumbnailList *q; |
|
Okular::Document *m_document; |
|
ThumbnailWidget *m_selected; |
|
QTimer *m_delayTimer; |
|
QPixmap *m_bookmarkOverlay; |
|
QVector<ThumbnailWidget *> m_thumbnails; |
|
QList<ThumbnailWidget *> m_visibleThumbnails; |
|
int m_vectorIndex; |
|
QPoint mouseGrabPos; |
|
// this is a (temporary) HACK to prevent jumping of the selected area |
|
// when dragging the mouse between pages |
|
ThumbnailWidget *mouseGrabItem; |
|
|
|
// resize thumbnails to fit the width |
|
void viewportResizeEvent( QResizeEvent * ); |
|
// called by ThumbnailWidgets to get the overlay bookmark pixmap |
|
const QPixmap * getBookmarkOverlay() const; |
|
// called by ThumbnailWidgets to send (forward) the mouse move signals |
|
void forwardTrack( const Okular::Page *, const QPoint &, const QSize & ); |
|
|
|
ThumbnailWidget* itemFor( const QPoint & p ) const; |
|
void delayedRequestVisiblePixmaps( int delayMs = 0 ); |
|
|
|
// SLOTS: |
|
// make requests for generating pixmaps for visible thumbnails |
|
void slotRequestVisiblePixmaps( int newContentsY = -1 ); |
|
// delay timeout: resize overlays and requests pixmaps |
|
void slotDelayTimeout(); |
|
|
|
protected: |
|
void mousePressEvent( QMouseEvent * e ); |
|
void mouseReleaseEvent( QMouseEvent * e ); |
|
void mouseMoveEvent( QMouseEvent * e ); |
|
void wheelEvent( QWheelEvent * e ); |
|
void contextMenuEvent( QContextMenuEvent * e ); |
|
void paintEvent( QPaintEvent * e ); |
|
}; |
|
|
|
|
|
// ThumbnailWidget represents a single thumbnail in the ThumbnailList |
|
class ThumbnailWidget |
|
{ |
|
public: |
|
ThumbnailWidget( ThumbnailListPrivate * parent, const Okular::Page * page ); |
|
|
|
// set internal parameters to fit the page in the given width |
|
void resizeFitWidth( int width ); |
|
// set thumbnail's selected state |
|
void setSelected( bool selected ); |
|
// set the visible rect of the current page |
|
void setVisibleRect( const Okular::NormalizedRect & rect ); |
|
|
|
// query methods |
|
int heightHint() const { return m_pixmapHeight + m_labelHeight + m_margin; } |
|
int pixmapWidth() const { return m_pixmapWidth; } |
|
int pixmapHeight() const { return m_pixmapHeight; } |
|
int pageNumber() const { return m_page->number(); } |
|
const Okular::Page * page() const { return m_page; } |
|
QRect visibleRect() const { return m_visibleRect.geometry( m_pixmapWidth, m_pixmapHeight ); } |
|
|
|
void paint( QPainter &p, const QRect &clipRect ); |
|
|
|
static int margin() { return m_margin; } |
|
|
|
// simulating QWidget |
|
QRect rect() const { return m_rect; } |
|
int height() const { return m_rect.height(); } |
|
int width() const { return m_rect.width(); } |
|
QPoint pos() const { return m_rect.topLeft(); } |
|
void move( int x, int y ) { m_rect.setTopLeft( QPoint( x, y ) ); } |
|
void update() { m_parent->update( m_rect ); } |
|
void update( const QRect & rect ) { m_parent->update( rect.translated( m_rect.topLeft() ) ); } |
|
|
|
private: |
|
// the margin around the widget |
|
static int const m_margin = 16; |
|
|
|
ThumbnailListPrivate * m_parent; |
|
const Okular::Page * m_page; |
|
bool m_selected; |
|
int m_pixmapWidth, m_pixmapHeight; |
|
int m_labelHeight, m_labelNumber; |
|
Okular::NormalizedRect m_visibleRect; |
|
QRect m_rect; |
|
}; |
|
|
|
|
|
ThumbnailListPrivate::ThumbnailListPrivate( ThumbnailList *qq, Okular::Document *document ) |
|
: QWidget(), q( qq ), m_document( document ), m_selected( 0 ), |
|
m_delayTimer( 0 ), m_bookmarkOverlay( 0 ) |
|
{ |
|
setMouseTracking( true ); |
|
mouseGrabItem = 0; |
|
} |
|
|
|
ThumbnailListPrivate::~ThumbnailListPrivate() |
|
{ |
|
} |
|
|
|
ThumbnailWidget* ThumbnailListPrivate::itemFor( const QPoint & p ) const |
|
{ |
|
QVector< ThumbnailWidget * >::const_iterator tIt = m_thumbnails.constBegin(), tEnd = m_thumbnails.constEnd(); |
|
for ( ; tIt != tEnd; ++tIt ) |
|
{ |
|
if ( (*tIt)->rect().contains( p ) ) |
|
return (*tIt); |
|
} |
|
return 0; |
|
} |
|
|
|
void ThumbnailListPrivate::paintEvent( QPaintEvent * e ) |
|
{ |
|
QPainter painter( this ); |
|
QVector<ThumbnailWidget *>::const_iterator tIt = m_thumbnails.constBegin(), tEnd = m_thumbnails.constEnd(); |
|
for ( ; tIt != tEnd; ++tIt ) |
|
{ |
|
QRect rect = e->rect().intersected( (*tIt)->rect() ); |
|
if ( !rect.isNull() ) |
|
{ |
|
rect.translate( -(*tIt)->pos() ); |
|
painter.save(); |
|
painter.translate( (*tIt)->pos() ); |
|
(*tIt)->paint( painter, rect ); |
|
painter.restore(); |
|
} |
|
} |
|
} |
|
|
|
|
|
/** ThumbnailList implementation **/ |
|
|
|
ThumbnailList::ThumbnailList( QWidget *parent, Okular::Document *document ) |
|
: QScrollArea( parent ), d( new ThumbnailListPrivate( this, document ) ) |
|
{ |
|
setObjectName( "okular::Thumbnails" ); |
|
// set scrollbars |
|
setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); |
|
setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOn ); |
|
verticalScrollBar()->setEnabled( false ); |
|
|
|
setAttribute( Qt::WA_StaticContents ); |
|
|
|
setAcceptDrops( true ); |
|
|
|
QPalette pal = palette(); |
|
// set contents background to the 'base' color |
|
QPalette viewportPal = viewport()->palette(); |
|
viewportPal.setColor( viewport()->backgroundRole(), pal.color( QPalette::Base ) ); |
|
viewport()->setPalette( viewportPal ); |
|
|
|
setWidget( d ); |
|
// widget setup: can be focused by tab and mouse click (not wheel) |
|
widget()->setFocusPolicy( Qt::StrongFocus ); |
|
widget()->show(); |
|
QPalette widgetPal = widget()->palette(); |
|
widgetPal.setColor( widget()->backgroundRole(), pal.color( QPalette::Base ) ); |
|
widget()->setPalette( widgetPal ); |
|
|
|
connect( verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(slotRequestVisiblePixmaps(int)) ); |
|
} |
|
|
|
ThumbnailList::~ThumbnailList() |
|
{ |
|
d->m_document->removeObserver( this ); |
|
delete d->m_bookmarkOverlay; |
|
} |
|
|
|
//BEGIN DocumentObserver inherited methods |
|
void ThumbnailList::notifySetup( const QVector< Okular::Page * > & pages, int setupFlags ) |
|
{ |
|
// if there was a widget selected, save its pagenumber to restore |
|
// its selection (if available in the new set of pages) |
|
int prevPage = -1; |
|
if ( !( setupFlags & Okular::DocumentObserver::DocumentChanged ) && d->m_selected ) |
|
{ |
|
prevPage = d->m_selected->page()->number(); |
|
} else |
|
prevPage = d->m_document->viewport().pageNumber; |
|
|
|
// delete all the Thumbnails |
|
QVector<ThumbnailWidget *>::const_iterator tIt = d->m_thumbnails.constBegin(), tEnd = d->m_thumbnails.constEnd(); |
|
for ( ; tIt != tEnd; ++tIt ) |
|
delete *tIt; |
|
d->m_thumbnails.clear(); |
|
d->m_visibleThumbnails.clear(); |
|
d->m_selected = 0; |
|
d->mouseGrabItem = 0; |
|
|
|
if ( pages.count() < 1 ) |
|
{ |
|
widget()->resize( 0, 0 ); |
|
return; |
|
} |
|
|
|
// show pages containing hilighted text or bookmarked ones |
|
//RESTORE THIS int flags = Okular::Settings::filterBookmarks() ? Okular::Page::Bookmark : Okular::Page::Highlight; |
|
|
|
// if no page matches filter rule, then display all pages |
|
QVector< Okular::Page * >::const_iterator pIt = pages.constBegin(), pEnd = pages.constEnd(); |
|
bool skipCheck = true; |
|
for ( ; pIt != pEnd ; ++pIt ) |
|
//if ( (*pIt)->attributes() & flags ) |
|
if ( (*pIt)->hasHighlights( SW_SEARCH_ID ) ) |
|
skipCheck = false; |
|
|
|
// generate Thumbnails for the given set of pages |
|
int width = viewport()->width(); |
|
int height = 0; |
|
int centerHeight = 0; |
|
for ( pIt = pages.constBegin(); pIt != pEnd ; ++pIt ) |
|
//if ( skipCheck || (*pIt)->attributes() & flags ) |
|
if ( skipCheck || (*pIt)->hasHighlights( SW_SEARCH_ID ) ) |
|
{ |
|
ThumbnailWidget * t = new ThumbnailWidget( d, *pIt ); |
|
t->move(0, height); |
|
// add to the internal queue |
|
d->m_thumbnails.push_back( t ); |
|
// update total height (asking widget its own height) |
|
t->resizeFitWidth( width ); |
|
// restoring the previous selected page, if any |
|
if ( (*pIt)->number() < prevPage ) |
|
{ |
|
centerHeight = height + t->height() + KDialog::spacingHint()/2; |
|
} |
|
if ( (*pIt)->number() == prevPage ) |
|
{ |
|
d->m_selected = t; |
|
d->m_selected->setSelected( true ); |
|
centerHeight = height + t->height() / 2; |
|
} |
|
height += t->height() + KDialog::spacingHint(); |
|
} |
|
|
|
// update scrollview's contents size (sets scrollbars limits) |
|
height -= KDialog::spacingHint(); |
|
widget()->resize( width, height ); |
|
|
|
// enable scrollbar when there's something to scroll |
|
verticalScrollBar()->setEnabled( viewport()->height() < height ); |
|
verticalScrollBar()->setValue(centerHeight - viewport()->height() / 2); |
|
|
|
// request for thumbnail generation |
|
d->delayedRequestVisiblePixmaps( 200 ); |
|
} |
|
|
|
void ThumbnailList::notifyViewportChanged( bool /*smoothMove*/ ) |
|
{ |
|
// skip notifies for the current page (already selected) |
|
int newPage = d->m_document->viewport().pageNumber; |
|
if ( d->m_selected && d->m_selected->pageNumber() == newPage ) |
|
return; |
|
|
|
// deselect previous thumbnail |
|
if ( d->m_selected ) |
|
d->m_selected->setSelected( false ); |
|
d->m_selected = 0; |
|
|
|
// select the page with viewport and ensure it's centered in the view |
|
d->m_vectorIndex = 0; |
|
QVector<ThumbnailWidget *>::const_iterator tIt = d->m_thumbnails.constBegin(), tEnd = d->m_thumbnails.constEnd(); |
|
for ( ; tIt != tEnd; ++tIt ) |
|
{ |
|
if ( (*tIt)->pageNumber() == newPage ) |
|
{ |
|
d->m_selected = *tIt; |
|
d->m_selected->setSelected( true ); |
|
if ( Okular::Settings::syncThumbnailsViewport() ) |
|
{ |
|
int yOffset = qMax( viewport()->height() / 4, d->m_selected->height() / 2 ); |
|
ensureVisible( 0, d->m_selected->pos().y() + d->m_selected->height()/2, 0, yOffset ); |
|
} |
|
break; |
|
} |
|
d->m_vectorIndex++; |
|
} |
|
} |
|
|
|
void ThumbnailList::notifyPageChanged( int pageNumber, int changedFlags ) |
|
{ |
|
static int interestingFlags = DocumentObserver::Pixmap | DocumentObserver::Bookmark | DocumentObserver::Highlights | DocumentObserver::Annotations; |
|
// only handle change notifications we are interested in |
|
if ( !( changedFlags & interestingFlags ) ) |
|
return; |
|
|
|
// iterate over visible items: if page(pageNumber) is one of them, repaint it |
|
QList<ThumbnailWidget *>::const_iterator vIt = d->m_visibleThumbnails.constBegin(), vEnd = d->m_visibleThumbnails.constEnd(); |
|
for ( ; vIt != vEnd; ++vIt ) |
|
if ( (*vIt)->pageNumber() == pageNumber ) |
|
{ |
|
(*vIt)->update(); |
|
break; |
|
} |
|
} |
|
|
|
void ThumbnailList::notifyContentsCleared( int changedFlags ) |
|
{ |
|
// if pixmaps were cleared, re-ask them |
|
if ( changedFlags & DocumentObserver::Pixmap ) |
|
d->slotRequestVisiblePixmaps(); |
|
} |
|
|
|
void ThumbnailList::notifyVisibleRectsChanged() |
|
{ |
|
bool found = false; |
|
const QVector<Okular::VisiblePageRect *> & visibleRects = d->m_document->visiblePageRects(); |
|
QVector<ThumbnailWidget *>::const_iterator tIt = d->m_thumbnails.constBegin(), tEnd = d->m_thumbnails.constEnd(); |
|
QVector<Okular::VisiblePageRect *>::const_iterator vEnd = visibleRects.end(); |
|
for ( ; tIt != tEnd; ++tIt ) |
|
{ |
|
found = false; |
|
QVector<Okular::VisiblePageRect *>::const_iterator vIt = visibleRects.begin(); |
|
for ( ; ( vIt != vEnd ) && !found; ++vIt ) |
|
{ |
|
if ( (*tIt)->pageNumber() == (*vIt)->pageNumber ) |
|
{ |
|
(*tIt)->setVisibleRect( (*vIt)->rect ); |
|
found = true; |
|
} |
|
} |
|
if ( !found ) |
|
{ |
|
(*tIt)->setVisibleRect( Okular::NormalizedRect() ); |
|
} |
|
} |
|
} |
|
|
|
bool ThumbnailList::canUnloadPixmap( int pageNumber ) const |
|
{ |
|
// if the thubnail 'pageNumber' is one of the visible ones, forbid unloading |
|
QList<ThumbnailWidget *>::const_iterator vIt = d->m_visibleThumbnails.constBegin(), vEnd = d->m_visibleThumbnails.constEnd(); |
|
for ( ; vIt != vEnd; ++vIt ) |
|
if ( (*vIt)->pageNumber() == pageNumber ) |
|
return false; |
|
// if hidden permit unloading |
|
return true; |
|
} |
|
//END DocumentObserver inherited methods |
|
|
|
|
|
void ThumbnailList::updateWidgets() |
|
{ |
|
// find all widgets that intersects the viewport and update them |
|
QRect viewportRect = viewport()->rect().translated( viewport()->pos() ); |
|
QList<ThumbnailWidget *>::const_iterator vIt = d->m_visibleThumbnails.constBegin(), vEnd = d->m_visibleThumbnails.constEnd(); |
|
for ( ; vIt != vEnd; ++vIt ) |
|
{ |
|
ThumbnailWidget * t = *vIt; |
|
QRect thumbRect = t->rect().translated( widget()->mapToParent( t->pos() ) ); |
|
// update only the exposed area of the widget (saves pixels..) |
|
QRect relativeRect = thumbRect.intersect( viewport()->rect() ); |
|
if ( !relativeRect.isValid() ) |
|
continue; |
|
t->update( relativeRect ); |
|
} |
|
} |
|
|
|
void ThumbnailListPrivate::forwardTrack( const Okular::Page * p, const QPoint &point, const QSize &s ) |
|
{ |
|
Okular::DocumentViewport vp=m_document->viewport(); |
|
|
|
QVector< Okular::VisiblePageRect * > vVpr = m_document->visiblePageRects(); |
|
|
|
QVector< Okular::VisiblePageRect * >::const_iterator vIt = vVpr.constBegin(); |
|
QVector< Okular::VisiblePageRect * >::const_iterator vEnd = vVpr.constEnd(); |
|
for ( ; vIt != vEnd; ++vIt ) |
|
{ |
|
Okular::VisiblePageRect *vpr = ( *vIt ); |
|
if( vpr->pageNumber == p->number() ) |
|
{ |
|
double w = vpr->rect.right - vpr->rect.left, |
|
h = vpr->rect.bottom - vpr->rect.top, |
|
deltaX = (double)point.x() / s.width(), |
|
deltaY = (double)point.y() / s.height(); |
|
|
|
vp.rePos.normalizedX -= deltaX; |
|
vp.rePos.normalizedY -= deltaY; |
|
|
|
if( !vp.rePos.enabled ) |
|
{ |
|
vp.rePos.enabled = true; |
|
vp.rePos.normalizedY += h/2; |
|
} |
|
m_document->setViewport( vp ); |
|
break; |
|
} |
|
} |
|
} |
|
|
|
const QPixmap * ThumbnailListPrivate::getBookmarkOverlay() const |
|
{ |
|
return m_bookmarkOverlay; |
|
} |
|
|
|
void ThumbnailList::slotFilterBookmarks( bool filterOn ) |
|
{ |
|
// save state |
|
Okular::Settings::setFilterBookmarks( filterOn ); |
|
Okular::Settings::self()->writeConfig(); |
|
// ask for the 'notifySetup' with a little trick (on reinsertion the |
|
// document sends the list again) |
|
d->m_document->removeObserver( this ); |
|
d->m_document->addObserver( this ); |
|
} |
|
|
|
|
|
//BEGIN widget events |
|
void ThumbnailList::keyPressEvent( QKeyEvent * keyEvent ) |
|
{ |
|
if ( d->m_thumbnails.count() < 1 ) |
|
return keyEvent->ignore(); |
|
|
|
int nextPage = -1; |
|
if ( keyEvent->key() == Qt::Key_Up ) |
|
{ |
|
if ( !d->m_selected ) |
|
nextPage = 0; |
|
else if ( d->m_vectorIndex > 0 ) |
|
nextPage = d->m_thumbnails[ d->m_vectorIndex - 1 ]->pageNumber(); |
|
} |
|
else if ( keyEvent->key() == Qt::Key_Down ) |
|
{ |
|
if ( !d->m_selected ) |
|
nextPage = 0; |
|
else if ( d->m_vectorIndex < (int)d->m_thumbnails.count() - 1 ) |
|
nextPage = d->m_thumbnails[ d->m_vectorIndex + 1 ]->pageNumber(); |
|
} |
|
else if ( keyEvent->key() == Qt::Key_PageUp ) |
|
verticalScrollBar()->triggerAction( QScrollBar::SliderPageStepSub ); |
|
else if ( keyEvent->key() == Qt::Key_PageDown ) |
|
verticalScrollBar()->triggerAction( QScrollBar::SliderPageStepAdd ); |
|
else if ( keyEvent->key() == Qt::Key_Home ) |
|
nextPage = d->m_thumbnails[ 0 ]->pageNumber(); |
|
else if ( keyEvent->key() == Qt::Key_End ) |
|
nextPage = d->m_thumbnails[ d->m_thumbnails.count() - 1 ]->pageNumber(); |
|
|
|
if ( nextPage == -1 ) |
|
return keyEvent->ignore(); |
|
|
|
keyEvent->accept(); |
|
if ( d->m_selected ) |
|
d->m_selected->setSelected( false ); |
|
d->m_selected = 0; |
|
d->m_document->setViewportPage( nextPage ); |
|
} |
|
|
|
bool ThumbnailList::viewportEvent( QEvent * e ) |
|
{ |
|
switch ( e->type() ) |
|
{ |
|
case QEvent::Resize: |
|
{ |
|
d->viewportResizeEvent( (QResizeEvent*)e ); |
|
break; |
|
} |
|
default: |
|
; |
|
} |
|
return QScrollArea::viewportEvent( e ); |
|
} |
|
|
|
void ThumbnailListPrivate::viewportResizeEvent( QResizeEvent * e ) |
|
{ |
|
if ( m_thumbnails.count() < 1 || width() < 1 ) |
|
return; |
|
|
|
// if width changed resize all the Thumbnails, reposition them to the |
|
// right place and recalculate the contents area |
|
if ( e->size().width() != e->oldSize().width() ) |
|
{ |
|
// runs the timer avoiding a thumbnail regeneration by 'contentsMoving' |
|
delayedRequestVisiblePixmaps( 2000 ); |
|
|
|
// resize and reposition items |
|
int newWidth = q->viewport()->width(); |
|
int newHeight = 0; |
|
QVector<ThumbnailWidget *>::const_iterator tIt = m_thumbnails.constBegin(), tEnd = m_thumbnails.constEnd(); |
|
for ( ; tIt != tEnd; ++tIt ) |
|
{ |
|
ThumbnailWidget *t = *tIt; |
|
t->move(0, newHeight); |
|
t->resizeFitWidth( newWidth ); |
|
newHeight += t->height() + KDialog::spacingHint(); |
|
} |
|
|
|
// update scrollview's contents size (sets scrollbars limits) |
|
newHeight -= KDialog::spacingHint(); |
|
const int oldHeight = q->widget()->height(); |
|
const int oldYCenter = q->verticalScrollBar()->value() + q->viewport()->height() / 2; |
|
q->widget()->resize( newWidth, newHeight ); |
|
|
|
// enable scrollbar when there's something to scroll |
|
q->verticalScrollBar()->setEnabled( q->viewport()->height() < newHeight ); |
|
|
|
// ensure that what was visibile before remains visible now |
|
q->ensureVisible( 0, int( (qreal)oldYCenter * q->widget()->height() / oldHeight ), 0, q->viewport()->height() / 2 ); |
|
} |
|
else if ( e->size().height() <= e->oldSize().height() ) |
|
return; |
|
|
|
// invalidate the bookmark overlay |
|
if ( m_bookmarkOverlay ) |
|
{ |
|
delete m_bookmarkOverlay; |
|
m_bookmarkOverlay = 0; |
|
} |
|
|
|
// update Thumbnails since width has changed or height has increased |
|
delayedRequestVisiblePixmaps( 500 ); |
|
} |
|
|
|
void ThumbnailList::dragEnterEvent( QDragEnterEvent * ev ) |
|
{ |
|
ev->accept(); |
|
} |
|
|
|
void ThumbnailList::dropEvent( QDropEvent * ev ) |
|
{ |
|
if ( KUrl::List::canDecode( ev->mimeData() ) ) |
|
emit urlDropped( KUrl::List::fromMimeData( ev->mimeData() ).first() ); |
|
} |
|
//END widget events |
|
|
|
//BEGIN internal SLOTS |
|
void ThumbnailListPrivate::slotRequestVisiblePixmaps( int /*newContentsY*/ ) |
|
{ |
|
// if an update is already scheduled or the widget is hidden, don't proceed |
|
if ( ( m_delayTimer && m_delayTimer->isActive() ) || q->isHidden() ) |
|
return; |
|
|
|
// scroll from the top to the last visible thumbnail |
|
m_visibleThumbnails.clear(); |
|
QLinkedList< Okular::PixmapRequest * > requestedPixmaps; |
|
QVector<ThumbnailWidget *>::const_iterator tIt = m_thumbnails.constBegin(), tEnd = m_thumbnails.constEnd(); |
|
QRect viewportRect = q->viewport()->rect().translated( q->horizontalScrollBar()->value(), q->verticalScrollBar()->value() ); |
|
for ( ; tIt != tEnd; ++tIt ) |
|
{ |
|
ThumbnailWidget * t = *tIt; |
|
QRect thumbRect = t->rect(); |
|
if ( !thumbRect.intersects( viewportRect ) ) |
|
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() ) ) |
|
{ |
|
Okular::PixmapRequest * p = new Okular::PixmapRequest( |
|
THUMBNAILS_ID, t->pageNumber(), t->pixmapWidth(), t->pixmapHeight(), THUMBNAILS_PRIO, true ); |
|
requestedPixmaps.push_back( p ); |
|
} |
|
} |
|
|
|
// actually request pixmaps |
|
if ( !requestedPixmaps.isEmpty() ) |
|
m_document->requestPixmaps( requestedPixmaps ); |
|
} |
|
|
|
void ThumbnailListPrivate::slotDelayTimeout() |
|
{ |
|
// resize the bookmark overlay |
|
delete m_bookmarkOverlay; |
|
int expectedWidth = q->viewport()->width() / 4; |
|
if ( expectedWidth > 10 ) |
|
m_bookmarkOverlay = new QPixmap( DesktopIcon( "bookmarks", expectedWidth ) ); |
|
else |
|
m_bookmarkOverlay = 0; |
|
|
|
// request pixmaps |
|
slotRequestVisiblePixmaps(); |
|
} |
|
//END internal SLOTS |
|
|
|
void ThumbnailListPrivate::delayedRequestVisiblePixmaps( int delayMs ) |
|
{ |
|
if ( !m_delayTimer ) |
|
{ |
|
m_delayTimer = new QTimer( q ); |
|
m_delayTimer->setSingleShot( true ); |
|
connect( m_delayTimer, SIGNAL( timeout() ), q, SLOT( slotDelayTimeout() ) ); |
|
} |
|
m_delayTimer->start( delayMs ); |
|
} |
|
|
|
|
|
/** ThumbnailWidget implementation **/ |
|
|
|
ThumbnailWidget::ThumbnailWidget( ThumbnailListPrivate * parent, const Okular::Page * kp ) |
|
: m_parent( parent ), m_page( kp ), |
|
m_selected( false ), m_pixmapWidth( 10 ), m_pixmapHeight( 10 ) |
|
{ |
|
m_labelNumber = m_page->number() + 1; |
|
m_labelHeight = QFontMetrics( m_parent->font() ).height(); |
|
|
|
} |
|
|
|
void ThumbnailWidget::resizeFitWidth( int width ) |
|
{ |
|
m_pixmapWidth = width - m_margin; |
|
m_pixmapHeight = qRound( m_page->ratio() * (double)m_pixmapWidth ); |
|
m_rect.setSize( QSize( width, heightHint() ) ); |
|
} |
|
|
|
void ThumbnailWidget::setSelected( bool selected ) |
|
{ |
|
// update selected state |
|
if ( m_selected != selected ) |
|
{ |
|
m_selected = selected; |
|
update(); |
|
} |
|
} |
|
|
|
void ThumbnailWidget::setVisibleRect( const Okular::NormalizedRect & rect ) |
|
{ |
|
if ( rect == m_visibleRect ) |
|
return; |
|
|
|
m_visibleRect = rect; |
|
update(); |
|
} |
|
|
|
void ThumbnailListPrivate::mousePressEvent( QMouseEvent * e ) |
|
{ |
|
ThumbnailWidget* item = itemFor( e->pos() ); |
|
if ( !item ) // mouse on the spacing between items |
|
return e->ignore(); |
|
|
|
QRect r = item->visibleRect(); |
|
int margin = ThumbnailWidget::margin(); |
|
QPoint p = e->pos() - item->pos(); |
|
|
|
if ( e->button() != Qt::RightButton && r.contains( p - QPoint( margin / 2, margin / 2 ) ) ) |
|
{ |
|
mouseGrabPos = e->pos(); |
|
mouseGrabItem = item; |
|
} |
|
else |
|
{ |
|
mouseGrabPos.setX( 0 ); |
|
mouseGrabPos.setY( 0 ); |
|
mouseGrabItem = 0; |
|
} |
|
} |
|
|
|
void ThumbnailListPrivate::mouseReleaseEvent( QMouseEvent * e ) |
|
{ |
|
ThumbnailWidget* item = itemFor( e->pos() ); |
|
mouseGrabItem = item; |
|
if ( !item ) // mouse on the spacing between items |
|
return e->ignore(); |
|
|
|
QRect r = item->visibleRect(); |
|
int margin = ThumbnailWidget::margin(); |
|
QPoint p = e->pos() - item->pos(); |
|
|
|
if ( r.contains( p - QPoint( margin / 2, margin / 2 ) ) ) |
|
{ |
|
setCursor( Qt::OpenHandCursor ); |
|
} |
|
else |
|
{ |
|
setCursor( Qt::ArrowCursor ); |
|
if ( mouseGrabPos.isNull() ) |
|
{ |
|
if ( m_document->viewport().pageNumber != item->pageNumber() ) |
|
m_document->setViewportPage( item->pageNumber() ); |
|
} |
|
} |
|
mouseGrabPos.setX( 0 ); |
|
mouseGrabPos.setY( 0 ); |
|
} |
|
|
|
void ThumbnailListPrivate::mouseMoveEvent( QMouseEvent * e ) |
|
{ |
|
ThumbnailWidget* item = itemFor( e->pos() ); |
|
if ( e->buttons() == Qt::NoButton ) |
|
return e->ignore(); |
|
|
|
ThumbnailWidget* theItem = item ? item : mouseGrabItem; |
|
// no item under the mouse or previously selected |
|
if ( !theItem ) |
|
return e->ignore(); |
|
QRect r = theItem->rect(); |
|
int margin = ThumbnailWidget::margin(); |
|
QPoint p = e->pos() - theItem->pos(); |
|
|
|
if ( true /*r.contains( p - QPoint( margin / 2, margin / 2 ) )*/ ) |
|
{ |
|
if ( !mouseGrabPos.isNull() ) |
|
{ |
|
setCursor( Qt::ClosedHandCursor ); |
|
QPoint mousePos = e->pos(); |
|
QPoint delta = mouseGrabPos - mousePos; |
|
mouseGrabPos = e->pos(); |
|
if ( item ) |
|
mouseGrabItem = item; |
|
// don't handle the mouse move, forward it to the thumbnail list |
|
forwardTrack( mouseGrabItem->page(), delta, r.size() ); |
|
} |
|
else |
|
{ |
|
mouseGrabPos = QPoint(); |
|
mouseGrabItem = 0; |
|
setCursor( Qt::OpenHandCursor ); |
|
} |
|
} |
|
else |
|
{ |
|
setCursor( Qt::ArrowCursor ); |
|
} |
|
} |
|
|
|
void ThumbnailListPrivate::wheelEvent( QWheelEvent * e ) |
|
{ |
|
ThumbnailWidget* item = itemFor( e->pos() ); |
|
if ( !item ) // wheeling on the spacing between items |
|
return e->ignore(); |
|
|
|
QRect r = item->visibleRect(); |
|
int margin = ThumbnailWidget::margin(); |
|
|
|
if ( r.contains( e->pos() - QPoint( margin / 2, margin / 2 ) ) && e->orientation() == Qt::Vertical && e->modifiers() == Qt::ControlModifier ) |
|
{ |
|
m_document->setZoom( e->delta() ); |
|
} |
|
else |
|
{ |
|
e->ignore(); |
|
} |
|
} |
|
|
|
void ThumbnailListPrivate::contextMenuEvent( QContextMenuEvent * e ) |
|
{ |
|
ThumbnailWidget* item = itemFor( e->pos() ); |
|
if ( item ) |
|
{ |
|
emit q->rightClick( item->page(), e->globalPos() ); |
|
} |
|
} |
|
|
|
void ThumbnailWidget::paint( QPainter &p, const QRect &_clipRect ) |
|
{ |
|
int width = m_pixmapWidth + m_margin; |
|
QRect clipRect = _clipRect; |
|
QPalette pal = m_parent->palette(); |
|
|
|
// draw the bottom label + highlight mark |
|
QColor fillColor = m_selected ? pal.color( QPalette::Active, QPalette::Highlight ) : pal.color( QPalette::Active, QPalette::Base ); |
|
p.fillRect( clipRect, fillColor ); |
|
p.setPen( m_selected ? pal.color( QPalette::Active, QPalette::HighlightedText ) : pal.color( QPalette::Active, QPalette::Text ) ); |
|
p.drawText( 0, m_pixmapHeight + m_margin, width, m_labelHeight, Qt::AlignCenter, QString::number( m_labelNumber ) ); |
|
|
|
// draw page outline and pixmap |
|
if ( clipRect.top() < m_pixmapHeight + m_margin ) |
|
{ |
|
// if page is bookmarked draw a colored border |
|
bool isBookmarked = m_parent->m_document->bookmarkManager()->isBookmarked( pageNumber() ); |
|
// draw the inner rect |
|
p.setPen( isBookmarked ? QColor( 0xFF8000 ) : Qt::black ); |
|
p.drawRect( m_margin/2 - 1, m_margin/2 - 1, m_pixmapWidth + 1, m_pixmapHeight + 1 ); |
|
// draw the clear rect |
|
p.setPen( isBookmarked ? QColor( 0x804000 ) : pal.color( QPalette::Active, QPalette::Base ) ); |
|
// draw the bottom and right shadow edges |
|
if ( !isBookmarked ) |
|
{ |
|
int left, right, bottom, top; |
|
left = m_margin/2 + 1; |
|
right = m_margin/2 + m_pixmapWidth + 1; |
|
bottom = m_pixmapHeight + m_margin/2 + 1; |
|
top = m_margin/2 + 1; |
|
p.setPen( Qt::gray ); |
|
p.drawLine( left, bottom, right, bottom ); |
|
p.drawLine( right, top, right, bottom ); |
|
} |
|
|
|
// draw the page using the shared PagePainter class |
|
p.translate( m_margin/2, m_margin/2 ); |
|
clipRect.translate( -m_margin/2, -m_margin/2 ); |
|
clipRect = clipRect.intersect( QRect( 0, 0, m_pixmapWidth, m_pixmapHeight ) ); |
|
if ( clipRect.isValid() ) |
|
{ |
|
int flags = PagePainter::Accessibility | PagePainter::Highlights | |
|
PagePainter::Annotations; |
|
PagePainter::paintPageOnPainter( &p, m_page, THUMBNAILS_ID, flags, |
|
m_pixmapWidth, m_pixmapHeight, clipRect ); |
|
} |
|
|
|
if ( !m_visibleRect.isNull() ) |
|
{ |
|
p.save(); |
|
p.setPen( QColor( 255, 255, 0, 200 ) ); |
|
p.setBrush( QColor( 0, 0, 0, 100 ) ); |
|
p.drawRect( m_visibleRect.geometry( m_pixmapWidth, m_pixmapHeight ).adjusted( 0, 0, -1, -1 ) ); |
|
p.restore(); |
|
} |
|
|
|
// draw the bookmark overlay on the top-right corner |
|
const QPixmap * bookmarkPixmap = m_parent->getBookmarkOverlay(); |
|
if ( isBookmarked && bookmarkPixmap ) |
|
{ |
|
int pixW = bookmarkPixmap->width(), |
|
pixH = bookmarkPixmap->height(); |
|
clipRect = clipRect.intersect( QRect( m_pixmapWidth - pixW, 0, pixW, pixH ) ); |
|
if ( clipRect.isValid() ) |
|
p.drawPixmap( m_pixmapWidth - pixW, -pixH/8, *bookmarkPixmap ); |
|
} |
|
} |
|
} |
|
|
|
|
|
/** ThumbnailsController implementation **/ |
|
|
|
#define FILTERB_ID 1 |
|
|
|
ThumbnailController::ThumbnailController( QWidget * parent, ThumbnailList * list ) |
|
: QToolBar( parent ) |
|
{ |
|
setObjectName( "ThumbsControlBar" ); |
|
// change toolbar appearance |
|
setIconSize( QSize( 16, 16 ) ); |
|
setMovable( false ); |
|
QSizePolicy sp = sizePolicy(); |
|
sp.setVerticalPolicy( QSizePolicy::Minimum ); |
|
setSizePolicy( sp ); |
|
|
|
// insert a togglebutton [show only bookmarked pages] |
|
//insertSeparator(); |
|
QAction * showBoomarkOnlyAction = addAction( |
|
KIcon( "bookmarks" ), i18n( "Show bookmarked pages only" ) ); |
|
showBoomarkOnlyAction->setCheckable( true ); |
|
connect( showBoomarkOnlyAction, SIGNAL( toggled( bool ) ), list, SLOT( slotFilterBookmarks( bool ) ) ); |
|
showBoomarkOnlyAction->setChecked( Okular::Settings::filterBookmarks() ); |
|
//insertLineSeparator(); |
|
} |
|
|
|
|
|
#include "thumbnaillist.moc"
|
|
|