thumbnaillist.cpp 8.43 KB
Newer Older
Albert Astals Cid's avatar
Albert Astals Cid committed
1
/***************************************************************************
2
 *   Copyright (C) 2004 by Albert Astals Cid <tsdgeos@terra.es>            *
Albert Astals Cid's avatar
Albert Astals Cid committed
3 4 5 6 7 8 9
 *                                                                         *
 *   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.                                   *
 ***************************************************************************/

10
#include <qtimer.h>
11
#include <klocale.h>
12 13
#include <kurl.h>
#include <kurldrag.h>
14 15
#include <kaction.h>
#include <kactioncollection.h>
16

Albert Astals Cid's avatar
Albert Astals Cid committed
17
#include "thumbnaillist.h"
18
#include "pixmapwidget.h"
19 20
#include "page.h"

21 22 23
ThumbnailList::ThumbnailList( QWidget *parent, KPDFDocument *document )
	: QScrollView( parent, "KPDF::Thumbnails", WNoAutoErase | WStaticContents ),
	m_document( document ), m_selected( 0 ), m_delayTimer( 0 )
Albert Astals Cid's avatar
Albert Astals Cid committed
24
{
25 26 27 28 29 30 31
	// set scrollbars
	setHScrollBarMode( QScrollView::AlwaysOff );
	setVScrollBarMode( QScrollView::AlwaysOn );

	// dealing with large areas so enable clipper
	enableClipper( true );

32 33
	// widget setup: can be focused by tab and mouse click (not wheel)
	viewport()->setFocusProxy( this );
34
	viewport()->setFocusPolicy( StrongFocus );
35 36 37
	viewport()->setPaletteBackgroundColor( Qt::gray );
	setResizePolicy( Manual );
	setAcceptDrops( true );
38
	setDragAutoScroll( false );
39

40 41 42
	// set contents background to the 'base' color
	viewport()->setPaletteBackgroundColor( palette().active().base() );

43
	setFrameStyle( StyledPanel | Raised );
44
	connect( this, SIGNAL(contentsMoving(int, int)), this, SLOT(slotRequestPixmaps(int, int)) );
Albert Astals Cid's avatar
Albert Astals Cid committed
45 46
}

47

48
//BEGIN KPDFDocumentObserver inherited methods 
49
void ThumbnailList::pageSetup( const QValueVector<KPDFPage*> & pages, bool /*documentChanged*/ )
50
{
51
	// delete all the Thumbnails
52 53
	QValueVector<ThumbnailWidget *>::iterator thumbIt = m_thumbnails.begin();
	QValueVector<ThumbnailWidget *>::iterator thumbEnd = m_thumbnails.end();
54 55
	for ( ; thumbIt != thumbEnd; ++thumbIt )
		delete *thumbIt;
56
	m_thumbnails.clear();
57 58 59
	m_selected = 0;

	if ( pages.count() < 1 )
60 61
	{
		resizeContents( 0, 0 );
62
		return;
63
	}
64

65 66 67 68 69 70
	//FIXME change this quick fix (lines that follows). Check if filtering:
	bool skipCheck = true;
	for ( uint i = 0; i < pages.count(); i++ )
		if ( pages[i]->isHilighted() )
			skipCheck = false;

71
	// generate Thumbnails for the given set of pages
72
	ThumbnailWidget *t;
73 74
	int width = clipper()->width(),
	    totalHeight = 0;
75 76
	QValueVector<KPDFPage*>::const_iterator pageIt = pages.begin();
	QValueVector<KPDFPage*>::const_iterator pageEnd = pages.end();
77
	for (; pageIt != pageEnd ; ++pageIt)
78
		if ( skipCheck || (*pageIt)->isHilighted() ) {
79
			t = new ThumbnailWidget( viewport(), *pageIt );
80
			t->setFocusProxy( this );
81 82 83 84 85
			// add to the scrollview
			addChild( t, 0, totalHeight );
			// add to the internal queue
			m_thumbnails.push_back( t );
			// update total height (asking widget its own height)
86 87
			t->setZoomFitWidth( width );
			t->resize( t->widthHint(), t->heightHint() );
88
			totalHeight += t->heightHint() + 4;
89 90
			t->show();
		}
91

92 93 94
	// update scrollview's contents size (sets scrollbars limits)
	resizeContents( width, totalHeight );

95
	// request for thumbnail generation
96
	requestPixmaps( 200 );
Albert Astals Cid's avatar
Albert Astals Cid committed
97 98
}

Enrico Ros's avatar
Enrico Ros committed
99
void ThumbnailList::pageSetCurrent( int pageNumber, const QRect & /*viewport*/ )
Albert Astals Cid's avatar
Albert Astals Cid committed
100
{
101
	// deselect previous thumbnail
102 103 104 105 106
	if ( m_selected )
		m_selected->setSelected( false );
	m_selected = 0;

	// select next page
107
	m_vectorIndex = 0;
108 109
	QValueVector<ThumbnailWidget *>::iterator thumbIt = m_thumbnails.begin();
	QValueVector<ThumbnailWidget *>::iterator thumbEnd = m_thumbnails.end();
110
	for (; thumbIt != thumbEnd; ++thumbIt)
111
	{
112 113 114 115
		if ( (*thumbIt)->pageNumber() == pageNumber )
		{
			m_selected = *thumbIt;
			m_selected->setSelected( true );
116 117
			ensureVisible( 0, childY( m_selected ) + m_selected->height()/2, 0, visibleHeight()/2 );
			//non-centered version: ensureVisible( 0, itemTop + itemHeight/2, 0, itemHeight/2 );
118 119
			break;
		}
120
		m_vectorIndex++;
121
	}
122 123
}

124
void ThumbnailList::notifyPixmapChanged( int pageNumber )
125
{
126
	QValueVector<ThumbnailWidget *>::iterator thumbIt = m_thumbnails.begin(), thumbEnd = m_thumbnails.end();
127 128 129 130 131 132 133
	for (; thumbIt != thumbEnd; ++thumbIt)
		if ( (*thumbIt)->pageNumber() == pageNumber )
		{
			(*thumbIt)->update();
			break;
		}
}
134 135 136 137 138 139 140 141 142 143 144 145

void ThumbnailList::dragEnterEvent( QDragEnterEvent * ev )
{
	ev->accept();
}

void ThumbnailList::dropEvent( QDropEvent * ev )
{
	KURL::List lst;
	if (  KURLDrag::decode(  ev, lst ) )
		emit urlDropped( lst.first() );
}
146
//END KPDFDocumentObserver inherited methods 
147

148 149
//BEGIN widget events 
void ThumbnailList::keyPressEvent( QKeyEvent * keyEvent )
150
{
151
	if ( m_thumbnails.count() < 1 )
152
		return keyEvent->ignore();
153 154

	int nextPage = -1;
155 156 157
	if ( keyEvent->key() == Key_Up )
	{
		if ( !m_selected )
158
			nextPage = 0;
159 160
		else if ( m_vectorIndex > 0 )
			nextPage = m_thumbnails[ m_vectorIndex - 1 ]->pageNumber();
161 162 163 164
	}
	else if ( keyEvent->key() == Key_Down )
	{
		if ( !m_selected )
165
			nextPage = 0;
166 167
		else if ( m_vectorIndex < (int)m_thumbnails.count() - 1 )
			nextPage = m_thumbnails[ m_vectorIndex + 1 ]->pageNumber();
168
	}
169
	else if ( keyEvent->key() == Key_Home )
170
		nextPage = m_thumbnails[ 0 ]->pageNumber();
171
	else if ( keyEvent->key() == Key_End )
172
		nextPage = m_thumbnails[ m_thumbnails.count() - 1 ]->pageNumber();
173 174

	if ( nextPage == -1 )
175
		return keyEvent->ignore();
176

177
	keyEvent->accept();
178 179 180 181
	if ( m_selected )
		m_selected->setSelected( false );
	m_selected = 0;
	m_document->slotSetCurrentPage( nextPage );
182
}
183

184 185
void ThumbnailList::contentsMousePressEvent( QMouseEvent * e )
{
186 187
	if ( e->button() != Qt::LeftButton )
		return;
188
	int clickY = e->y();
189 190
	QValueVector<ThumbnailWidget *>::iterator thumbIt = m_thumbnails.begin();
	QValueVector<ThumbnailWidget *>::iterator thumbEnd = m_thumbnails.end();
191
	for ( ; thumbIt != thumbEnd; ++thumbIt )
192
	{
193
		ThumbnailWidget * t = *thumbIt;
194 195 196 197
		int childTop = childY(t);
		if ( clickY > childTop && clickY < (childTop + t->height()) )
		{
			m_document->slotSetCurrentPage( t->pageNumber() );
198
			break;
199
		}
200 201 202
	}
}

203
void ThumbnailList::viewportResizeEvent( QResizeEvent * e )
204
{
205
	if ( m_thumbnails.count() < 1 || width() < 1 )
206 207 208 209
		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() )
210
	{
211
		// runs the timer avoiding a thumbnail regeneration by 'contentsMoving'
212
		requestPixmaps( 2000 );
213

214
		// resize and reposition items
215 216
		int totalHeight = 0,
		    newWidth = e->size().width();
217 218
		QValueVector<ThumbnailWidget *>::iterator thumbIt = m_thumbnails.begin();
		QValueVector<ThumbnailWidget *>::iterator thumbEnd = m_thumbnails.end();
219
		for ( ; thumbIt != thumbEnd; ++thumbIt )
220
		{
221
			ThumbnailWidget *t = *thumbIt;
222
			moveChild( t, 0, totalHeight );
223 224 225 226
			t->setZoomFitWidth( newWidth );
			t->resize( t->widthHint(), t->heightHint() );
			totalHeight += t->heightHint() + 4;
		}
227 228 229 230

		// update scrollview's contents size (sets scrollbars limits)
		resizeContents( newWidth, totalHeight );

231 232 233
		// ensure selected item remains visible
		if ( m_selected )
			ensureVisible( 0, childY( m_selected ) + m_selected->height()/2, 0, visibleHeight()/2 );
234
	}
235
	else if ( e->size().height() <= e->oldSize().height() )
236
		return;
237
	// update Thumbnails since width has changed or height has increased
238
	requestPixmaps( 500 );
239
}
240
//END widget events
241

242
//BEGIN internal SLOTS 
243
void ThumbnailList::slotRequestPixmaps( int /*newContentsX*/, int newContentsY )
244
{
245 246
	// if an update is already scheduled or the widget is hidden, don't proceed
	if ( (m_delayTimer && m_delayTimer->isActive()) || !isShown() )
247 248
		return;

249 250
	int vHeight = visibleHeight(),
	    vOffset = newContentsY == -1 ? contentsY() : newContentsY;
251

252
	// scroll from the top to the last visible thumbnail
253 254
	QValueVector<ThumbnailWidget *>::iterator thumbIt = m_thumbnails.begin();
	QValueVector<ThumbnailWidget *>::iterator thumbEnd = m_thumbnails.end();
255
	for ( ; thumbIt != thumbEnd; ++thumbIt )
256
	{
257
		ThumbnailWidget * t = *thumbIt;
258 259
		int top = childY( t ) - vOffset;
		if ( top > vHeight )
260
			break;
261
		else if ( top + t->height() > 0 )
262
			m_document->requestPixmap( THUMBNAILS_ID, t->pageNumber(), t->pixmapWidth(), t->pixmapHeight(), true );
263
	}
264
}
265
//END internal SLOTS
266

267
void ThumbnailList::requestPixmaps( int delayMs )
268
{
269
	if ( !m_delayTimer )
270
	{
271
		m_delayTimer = new QTimer( this );
272
		connect( m_delayTimer, SIGNAL( timeout() ), this, SLOT( slotRequestPixmaps() ) );
273
	}
274
	m_delayTimer->start( delayMs, true );
275 276
}

Albert Astals Cid's avatar
Albert Astals Cid committed
277
#include "thumbnaillist.moc"