153
157
typedef QSet<QPersistentModelIndex> PersistentModelIndexSet;
155
159
struct ThumbnailViewPrivate {
158
AbstractDocumentInfoProvider* mDocumentInfoProvider;
159
AbstractThumbnailViewHelper* mThumbnailViewHelper;
160
ThumbnailForUrl mThumbnailForUrl;
161
QTimer mScheduledThumbnailGenerationTimer;
163
UrlQueue mSmoothThumbnailQueue;
164
QTimer mSmoothThumbnailTimer;
166
QPixmap mWaitingThumbnail;
167
QPointer<ThumbnailLoadJob> mThumbnailLoadJob;
169
PersistentModelIndexSet mBusyIndexSet;
170
KPixmapSequence mBusySequence;
171
QTimeLine* mBusyAnimationTimeLine;
173
void setupBusyAnimation() {
174
mBusySequence = KPixmapSequence("process-working", 22);
175
mBusyAnimationTimeLine = new QTimeLine(100 * mBusySequence.frameCount(), that);
176
mBusyAnimationTimeLine->setCurveShape(QTimeLine::LinearCurve);
177
mBusyAnimationTimeLine->setEndFrame(mBusySequence.frameCount() - 1);
178
mBusyAnimationTimeLine->setLoopCount(0);
179
QObject::connect(mBusyAnimationTimeLine, SIGNAL(frameChanged(int)),
180
that, SLOT(updateBusyIndexes()));
183
void scheduleThumbnailGenerationForVisibleItems() {
184
if (mThumbnailLoadJob) {
185
mThumbnailLoadJob->removeItems(mThumbnailLoadJob->pendingItems());
187
mSmoothThumbnailQueue.clear();
188
mScheduledThumbnailGenerationTimer.start();
191
void updateThumbnailForModifiedDocument(const QModelIndex& index) {
192
Q_ASSERT(mDocumentInfoProvider);
193
KFileItem item = fileItemForIndex(index);
194
KUrl url = item.url();
195
ThumbnailGroup::Enum group = ThumbnailGroup::fromPixelSize(mThumbnailSize);
198
mDocumentInfoProvider->thumbnailForDocument(url, group, &pix, &fullSize);
199
mThumbnailForUrl[url] = Thumbnail(QPersistentModelIndex(index), KDateTime::currentLocalDateTime());
200
that->setThumbnail(item, pix, fullSize);
203
void generateThumbnailsForItems(const KFileItemList& list) {
204
ThumbnailGroup::Enum group = ThumbnailGroup::fromPixelSize(mThumbnailSize);
205
if (!mThumbnailLoadJob) {
206
mThumbnailLoadJob = new ThumbnailLoadJob(list, group);
207
QObject::connect(mThumbnailLoadJob, SIGNAL(thumbnailLoaded(const KFileItem&, const QPixmap&, const QSize&)),
208
that, SLOT(setThumbnail(const KFileItem&, const QPixmap&, const QSize&)));
209
QObject::connect(mThumbnailLoadJob, SIGNAL(thumbnailLoadingFailed(const KFileItem&)),
210
that, SLOT(setBrokenThumbnail(const KFileItem&)));
211
mThumbnailLoadJob->start();
213
mThumbnailLoadJob->setThumbnailGroup(group);
214
Q_FOREACH(const KFileItem& item, list) {
215
mThumbnailLoadJob->appendItem(item);
220
void roughAdjustThumbnail(Thumbnail* thumbnail) {
221
const QPixmap& mGroupPix = thumbnail->mGroupPix;
222
const int groupSize = qMax(mGroupPix.width(), mGroupPix.height());
223
const int fullSize = qMax(thumbnail->mFullSize.width(), thumbnail->mFullSize.height());
224
if (fullSize == groupSize && groupSize <= mThumbnailSize) {
225
thumbnail->mAdjustedPix = mGroupPix;
226
thumbnail->mRough = false;
228
thumbnail->mAdjustedPix = mGroupPix.scaled(mThumbnailSize, mThumbnailSize, Qt::KeepAspectRatio);
229
thumbnail->mRough = true;
233
QPixmap dragPixmapForIndex(const QModelIndex& index) const {
234
KUrl url = urlForIndex(index);
235
QPixmap pix = mThumbnailForUrl.value(url).mAdjustedPix;
236
if (qMax(pix.width(), pix.height()) > DRAG_THUMB_SIZE) {
237
return pix.scaled(DRAG_THUMB_SIZE, DRAG_THUMB_SIZE, Qt::KeepAspectRatio, Qt::SmoothTransformation);
243
QPixmap createDragPixmap(const QModelIndexList& indexes) {
244
const int MAX_THUMBS = 3;
248
if (indexes.count() > MAX_THUMBS) {
249
thumbCount = MAX_THUMBS;
252
thumbCount = indexes.count();
255
QList<QPixmap> thumbs;
258
for (int row=0; row<thumbCount; ++row) {
260
if (row == thumbCount-1 && more) {
261
QString text = "(...)";
262
QPixmap pix(that->fontMetrics().boundingRect(text).size());
263
pix.fill(Qt::transparent);
265
QPainter painter(&pix);
266
painter.drawText(pix.rect(), Qt::AlignHCenter | Qt::AlignBottom, text);
268
index = indexes.last();
269
width += pix.width();
272
index = indexes[row];
274
QPixmap thumb = dragPixmapForIndex(index);
275
height = qMax(height, thumb.height());
276
width += thumb.width();
281
width + (thumbs.count() + 1) * DRAG_THUMB_SPACING,
282
height + 2 * DRAG_THUMB_SPACING
284
pix.fill(QToolTip::palette().color(QPalette::Inactive, QPalette::ToolTipBase));
285
QPainter painter(&pix);
287
int x = DRAG_THUMB_SPACING;
288
Q_FOREACH(const QPixmap& thumb, thumbs) {
289
painter.drawPixmap(x, (pix.height() - thumb.height()) / 2, thumb);
290
x += thumb.width() + DRAG_THUMB_SPACING;
162
AbstractDocumentInfoProvider* mDocumentInfoProvider;
163
AbstractThumbnailViewHelper* mThumbnailViewHelper;
164
ThumbnailForUrl mThumbnailForUrl;
165
QTimer mScheduledThumbnailGenerationTimer;
167
UrlQueue mSmoothThumbnailQueue;
168
QTimer mSmoothThumbnailTimer;
170
QPixmap mWaitingThumbnail;
171
QPointer<ThumbnailLoadJob> mThumbnailLoadJob;
173
PersistentModelIndexSet mBusyIndexSet;
174
KPixmapSequence mBusySequence;
175
QTimeLine* mBusyAnimationTimeLine;
177
void setupBusyAnimation()
179
mBusySequence = KPixmapSequence("process-working", 22);
180
mBusyAnimationTimeLine = new QTimeLine(100 * mBusySequence.frameCount(), q);
181
mBusyAnimationTimeLine->setCurveShape(QTimeLine::LinearCurve);
182
mBusyAnimationTimeLine->setEndFrame(mBusySequence.frameCount() - 1);
183
mBusyAnimationTimeLine->setLoopCount(0);
184
QObject::connect(mBusyAnimationTimeLine, SIGNAL(frameChanged(int)),
185
q, SLOT(updateBusyIndexes()));
188
void scheduleThumbnailGenerationForVisibleItems()
190
if (mThumbnailLoadJob) {
191
mThumbnailLoadJob->removeItems(mThumbnailLoadJob->pendingItems());
193
mSmoothThumbnailQueue.clear();
194
mScheduledThumbnailGenerationTimer.start();
197
void updateThumbnailForModifiedDocument(const QModelIndex& index)
199
Q_ASSERT(mDocumentInfoProvider);
200
KFileItem item = fileItemForIndex(index);
201
KUrl url = item.url();
202
ThumbnailGroup::Enum group = ThumbnailGroup::fromPixelSize(mThumbnailSize);
205
mDocumentInfoProvider->thumbnailForDocument(url, group, &pix, &fullSize);
206
mThumbnailForUrl[url] = Thumbnail(QPersistentModelIndex(index), KDateTime::currentLocalDateTime());
207
q->setThumbnail(item, pix, fullSize);
210
void generateThumbnailsForItems(const KFileItemList& list)
212
ThumbnailGroup::Enum group = ThumbnailGroup::fromPixelSize(mThumbnailSize);
213
if (!mThumbnailLoadJob) {
214
mThumbnailLoadJob = new ThumbnailLoadJob(list, group);
215
QObject::connect(mThumbnailLoadJob, SIGNAL(thumbnailLoaded(KFileItem, QPixmap, QSize)),
216
q, SLOT(setThumbnail(KFileItem, QPixmap, QSize)));
217
QObject::connect(mThumbnailLoadJob, SIGNAL(thumbnailLoadingFailed(KFileItem)),
218
q, SLOT(setBrokenThumbnail(KFileItem)));
219
mThumbnailLoadJob->start();
221
mThumbnailLoadJob->setThumbnailGroup(group);
222
Q_FOREACH(const KFileItem & item, list) {
223
mThumbnailLoadJob->appendItem(item);
228
void roughAdjustThumbnail(Thumbnail* thumbnail)
230
const QPixmap& mGroupPix = thumbnail->mGroupPix;
231
const int groupSize = qMax(mGroupPix.width(), mGroupPix.height());
232
const int fullSize = qMax(thumbnail->mFullSize.width(), thumbnail->mFullSize.height());
233
if (fullSize == groupSize && groupSize <= mThumbnailSize) {
234
thumbnail->mAdjustedPix = mGroupPix;
235
thumbnail->mRough = false;
237
thumbnail->mAdjustedPix = mGroupPix.scaled(mThumbnailSize, mThumbnailSize, Qt::KeepAspectRatio);
238
thumbnail->mRough = true;
242
QPixmap dragPixmapForIndex(const QModelIndex& index) const {
243
KUrl url = urlForIndex(index);
244
QPixmap pix = mThumbnailForUrl.value(url).mAdjustedPix;
245
if (qMax(pix.width(), pix.height()) > DRAG_THUMB_SIZE) {
246
return pix.scaled(DRAG_THUMB_SIZE, DRAG_THUMB_SIZE, Qt::KeepAspectRatio, Qt::SmoothTransformation);
252
QPixmap createDragPixmap(const QModelIndexList& indexes)
254
const int MAX_THUMBS = 3;
258
if (indexes.count() > MAX_THUMBS) {
259
thumbCount = MAX_THUMBS;
262
thumbCount = indexes.count();
265
QList<QPixmap> thumbs;
268
for (int row = 0; row < thumbCount; ++row) {
270
if (row == thumbCount - 1 && more) {
271
QString text = "(...)";
272
QPixmap pix(q->fontMetrics().boundingRect(text).size());
273
pix.fill(Qt::transparent);
275
QPainter painter(&pix);
276
painter.drawText(pix.rect(), Qt::AlignHCenter | Qt::AlignBottom, text);
278
index = indexes.last();
279
width += pix.width();
282
index = indexes[row];
284
QPixmap thumb = dragPixmapForIndex(index);
285
height = qMax(height, thumb.height());
286
width += thumb.width();
291
width + (thumbs.count() + 1) * DRAG_THUMB_SPACING,
292
height + 2 * DRAG_THUMB_SPACING
294
pix.fill(QToolTip::palette().color(QPalette::Inactive, QPalette::ToolTipBase));
295
QPainter painter(&pix);
297
int x = DRAG_THUMB_SPACING;
298
Q_FOREACH(const QPixmap & thumb, thumbs) {
299
painter.drawPixmap(x, (pix.height() - thumb.height()) / 2, thumb);
300
x += thumb.width() + DRAG_THUMB_SPACING;
297
306
ThumbnailView::ThumbnailView(QWidget* parent)
298
307
: QListView(parent)
299
, d(new ThumbnailViewPrivate) {
301
d->mThumbnailViewHelper = 0;
302
d->mDocumentInfoProvider = 0;
303
// Init to some stupid value so that the first call to setThumbnailSize()
304
// is not ignored (do not use 0 in case someone try to divide by
305
// mThumbnailSize...)
306
d->mThumbnailSize = 1;
308
setFrameShape(QFrame::NoFrame);
309
setViewMode(QListView::IconMode);
310
setResizeMode(QListView::Adjust);
311
setDragEnabled(true);
312
setAcceptDrops(true);
313
setDropIndicatorShown(true);
314
setUniformItemSizes(true);
315
setEditTriggers(QAbstractItemView::EditKeyPressed);
317
d->setupBusyAnimation();
319
viewport()->setMouseTracking(true);
320
// Set this attribute, otherwise the item delegate won't get the
321
// State_MouseOver state
322
viewport()->setAttribute(Qt::WA_Hover);
324
setVerticalScrollMode(ScrollPerPixel);
325
setHorizontalScrollMode(ScrollPerPixel);
327
d->mScheduledThumbnailGenerationTimer.setSingleShot(true);
328
d->mScheduledThumbnailGenerationTimer.setInterval(500);
329
connect(&d->mScheduledThumbnailGenerationTimer, SIGNAL(timeout()),
330
SLOT(generateThumbnailsForVisibleItems()) );
332
d->mSmoothThumbnailTimer.setSingleShot(true);
333
connect(&d->mSmoothThumbnailTimer, SIGNAL(timeout()),
334
SLOT(smoothNextThumbnail()) );
336
setContextMenuPolicy(Qt::CustomContextMenu);
337
connect(this, SIGNAL(customContextMenuRequested(const QPoint&)),
338
SLOT(showContextMenu()) );
340
if (KGlobalSettings::singleClick()) {
341
connect(this, SIGNAL(clicked(const QModelIndex&)),
342
SLOT(emitIndexActivatedIfNoModifiers(const QModelIndex&)) );
344
connect(this, SIGNAL(doubleClicked(const QModelIndex&)),
345
SLOT(emitIndexActivatedIfNoModifiers(const QModelIndex&)) );
350
ThumbnailView::~ThumbnailView() {
355
void ThumbnailView::setModel(QAbstractItemModel* newModel) {
357
disconnect(model(), 0, this, 0);
359
QListView::setModel(newModel);
360
connect(model(), SIGNAL(rowsRemoved(const QModelIndex&, int, int)),
361
SIGNAL(rowsRemovedSignal(const QModelIndex&, int, int)));
365
void ThumbnailView::setThumbnailSize(int value) {
366
if (d->mThumbnailSize == value) {
369
d->mThumbnailSize = value;
372
int waitingThumbnailSize;
374
waitingThumbnailSize = 48;
376
waitingThumbnailSize = 32;
378
if (d->mWaitingThumbnail.width() != waitingThumbnailSize) {
379
QPixmap icon = DesktopIcon("chronometer", waitingThumbnailSize);
380
QPixmap pix(icon.size());
381
pix.fill(Qt::transparent);
382
QPainter painter(&pix);
383
painter.setOpacity(0.5);
384
painter.drawPixmap(0, 0, icon);
386
d->mWaitingThumbnail = pix;
390
d->mSmoothThumbnailTimer.stop();
391
d->mSmoothThumbnailQueue.clear();
393
// Clear adjustedPixes
394
ThumbnailForUrl::iterator
395
it = d->mThumbnailForUrl.begin(),
396
end = d->mThumbnailForUrl.end();
397
for (; it!=end; ++it) {
398
it.value().mAdjustedPix = QPixmap();
401
thumbnailSizeChanged(value);
402
d->scheduleThumbnailGenerationForVisibleItems();
406
int ThumbnailView::thumbnailSize() const {
407
return d->mThumbnailSize;
411
void ThumbnailView::setThumbnailViewHelper(AbstractThumbnailViewHelper* helper) {
412
d->mThumbnailViewHelper = helper;
415
AbstractThumbnailViewHelper* ThumbnailView::thumbnailViewHelper() const {
416
return d->mThumbnailViewHelper;
420
void ThumbnailView::setDocumentInfoProvider(AbstractDocumentInfoProvider* provider) {
421
d->mDocumentInfoProvider = provider;
423
connect(provider, SIGNAL(busyStateChanged(const QModelIndex&, bool)),
424
SLOT(updateThumbnailBusyState(const QModelIndex&, bool)));
425
connect(provider, SIGNAL(documentChanged(const QModelIndex&)),
426
SLOT(updateThumbnail(const QModelIndex&)));
430
AbstractDocumentInfoProvider* ThumbnailView::documentInfoProvider() const {
431
return d->mDocumentInfoProvider;
435
void ThumbnailView::rowsAboutToBeRemoved(const QModelIndex& parent, int start, int end) {
436
QListView::rowsAboutToBeRemoved(parent, start, end);
438
// Remove references to removed items
439
KFileItemList itemList;
440
for (int pos=start; pos<=end; ++pos) {
441
QModelIndex index = model()->index(pos, 0, parent);
442
KFileItem item = fileItemForIndex(index);
444
kDebug() << "Skipping invalid item!" << index.data().toString();
448
QUrl url = item.url();
449
d->mThumbnailForUrl.remove(url);
450
d->mSmoothThumbnailQueue.removeAll(url);
452
itemList.append(item);
455
if (d->mThumbnailLoadJob) {
456
d->mThumbnailLoadJob->removeItems(itemList);
459
// Update current index if it is among the deleted rows
460
const int row = currentIndex().row();
461
if (start <= row && row <= end) {
463
if (end < model()->rowCount() - 1) {
464
index = model()->index(end + 1, 0);
465
} else if (start > 0) {
466
index = model()->index(start - 1, 0);
468
setCurrentIndex(index);
471
// Removing rows might make new images visible, make sure their thumbnail
473
d->mScheduledThumbnailGenerationTimer.start();
477
void ThumbnailView::rowsInserted(const QModelIndex& parent, int start, int end) {
478
QListView::rowsInserted(parent, start, end);
479
d->mScheduledThumbnailGenerationTimer.start();
480
rowsInsertedSignal(parent, start, end);
484
void ThumbnailView::dataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight) {
485
QListView::dataChanged(topLeft, bottomRight);
486
bool thumbnailsNeedRefresh = false;
487
for (int row = topLeft.row(); row <= bottomRight.row(); ++row) {
488
QModelIndex index = model()->index(row, 0);
489
KFileItem item = fileItemForIndex(index);
491
kWarning() << "Invalid item for index" << index << ". This should not happen!";
495
ThumbnailForUrl::Iterator it = d->mThumbnailForUrl.find(item.url());
496
if (it != d->mThumbnailForUrl.end()) {
497
// All thumbnail views are connected to the model, so
498
// ThumbnailView::dataChanged() is called for all of them. As a
499
// result this method will also be called for views which are not
500
// currently visible, and do not yet have a thumbnail for the
502
KDateTime mtime = item.time(KFileItem::ModificationTime);
503
if (it->mModificationTime != mtime) {
504
// dataChanged() is called when the file changes but also when
505
// the model fetched additional data such as semantic info. To
506
// avoid needless refreshes, we only trigger a refresh if the
507
// modification time changes.
508
thumbnailsNeedRefresh = true;
509
it->prepareForRefresh(mtime);
513
if (thumbnailsNeedRefresh) {
514
d->mScheduledThumbnailGenerationTimer.start();
519
void ThumbnailView::showContextMenu() {
520
d->mThumbnailViewHelper->showContextMenu(this);
524
void ThumbnailView::emitIndexActivatedIfNoModifiers(const QModelIndex& index) {
525
if (QApplication::keyboardModifiers() == Qt::NoModifier) {
526
emit indexActivated(index);
531
void ThumbnailView::setThumbnail(const KFileItem& item, const QPixmap& pixmap, const QSize& size) {
532
ThumbnailForUrl::iterator it = d->mThumbnailForUrl.find(item.url());
533
if (it == d->mThumbnailForUrl.end()) {
536
Thumbnail& thumbnail = it.value();
537
thumbnail.mGroupPix = pixmap;
538
thumbnail.mAdjustedPix = QPixmap();
539
int largeGroupSize = ThumbnailGroup::pixelSize(ThumbnailGroup::Large);
540
thumbnail.mFullSize = size.isValid() ? size : QSize(largeGroupSize, largeGroupSize);
541
thumbnail.mRealFullSize = size;
542
thumbnail.mWaitingForThumbnail = false;
544
update(thumbnail.mIndex);
548
void ThumbnailView::setBrokenThumbnail(const KFileItem& item) {
549
ThumbnailForUrl::iterator it = d->mThumbnailForUrl.find(item.url());
550
if (it == d->mThumbnailForUrl.end()) {
553
Thumbnail& thumbnail = it.value();
554
MimeTypeUtils::Kind kind = MimeTypeUtils::fileItemKind(item);
555
if (kind == MimeTypeUtils::KIND_VIDEO) {
556
// Special case for videos because our kde install may come without
557
// support for video thumbnails so we show the mimetype icon instead of
558
// a broken image icon
559
ThumbnailGroup::Enum group = ThumbnailGroup::fromPixelSize(d->mThumbnailSize);
560
QPixmap pix = item.pixmap(ThumbnailGroup::pixelSize(group));
561
thumbnail.initAsIcon(pix);
562
} else if (kind == MimeTypeUtils::KIND_DIR) {
563
// Special case for folders because ThumbnailLoadJob does not return a
564
// thumbnail if there is no images
565
thumbnail.mWaitingForThumbnail = false;
568
thumbnail.initAsIcon(DesktopIcon("image-missing", 48));
569
thumbnail.mFullSize = thumbnail.mGroupPix.size();
571
update(thumbnail.mIndex);
575
QPixmap ThumbnailView::thumbnailForIndex(const QModelIndex& index, QSize* fullSize) {
576
KFileItem item = fileItemForIndex(index);
584
KUrl url = item.url();
586
// Find or create Thumbnail instance
587
ThumbnailForUrl::Iterator it = d->mThumbnailForUrl.find(url);
588
if (it == d->mThumbnailForUrl.end()) {
589
Thumbnail thumbnail = Thumbnail(QPersistentModelIndex(index), item.time(KFileItem::ModificationTime));
590
it = d->mThumbnailForUrl.insert(url, thumbnail);
592
Thumbnail& thumbnail = it.value();
594
// If dir or archive, generate a thumbnail from fileitem pixmap
595
MimeTypeUtils::Kind kind = MimeTypeUtils::fileItemKind(item);
596
if (kind == MimeTypeUtils::KIND_ARCHIVE || kind == MimeTypeUtils::KIND_DIR) {
597
int groupSize = ThumbnailGroup::pixelSize(ThumbnailGroup::fromPixelSize(d->mThumbnailSize));
598
if (thumbnail.mGroupPix.isNull() || thumbnail.mGroupPix.width() < groupSize) {
599
QPixmap pix = item.pixmap(groupSize);
600
thumbnail.initAsIcon(pix);
601
if (kind == MimeTypeUtils::KIND_ARCHIVE) {
602
// No thumbnails for archives
603
thumbnail.mWaitingForThumbnail = false;
605
// set mWaitingForThumbnail to true (necessary in the case
606
// 'thumbnail' already existed before, but with a too small
608
thumbnail.mWaitingForThumbnail = true;
613
if (thumbnail.mGroupPix.isNull()) {
617
return d->mWaitingThumbnail;
621
if (thumbnail.mAdjustedPix.isNull()) {
622
d->roughAdjustThumbnail(&thumbnail);
624
if (thumbnail.mRough && !d->mSmoothThumbnailQueue.contains(url)) {
625
d->mSmoothThumbnailQueue.enqueue(url);
626
if (!d->mSmoothThumbnailTimer.isActive()) {
627
d->mSmoothThumbnailTimer.start(SMOOTH_DELAY);
631
*fullSize = thumbnail.mRealFullSize;
633
return thumbnail.mAdjustedPix;
637
bool ThumbnailView::isModified(const QModelIndex& index) const {
638
if (!d->mDocumentInfoProvider) {
641
KUrl url = urlForIndex(index);
642
return d->mDocumentInfoProvider->isModified(url);
646
bool ThumbnailView::isBusy(const QModelIndex& index) const {
647
if (!d->mDocumentInfoProvider) {
650
KUrl url = urlForIndex(index);
651
return d->mDocumentInfoProvider->isBusy(url);
655
void ThumbnailView::startDrag(Qt::DropActions supportedActions) {
656
QModelIndexList indexes = selectionModel()->selectedIndexes();
657
if (indexes.isEmpty()) {
660
QDrag* drag = new QDrag(this);
661
drag->setMimeData(model()->mimeData(indexes));
662
QPixmap pix = d->createDragPixmap(indexes);
663
drag->setPixmap(pix);
664
drag->exec(supportedActions, Qt::CopyAction);
668
void ThumbnailView::dragEnterEvent(QDragEnterEvent* event) {
669
QAbstractItemView::dragEnterEvent(event);
670
if (event->mimeData()->hasUrls()) {
671
event->acceptProposedAction();
676
void ThumbnailView::dragMoveEvent(QDragMoveEvent* event) {
677
// Necessary, otherwise we don't reach dropEvent()
678
QAbstractItemView::dragMoveEvent(event);
679
event->acceptProposedAction();
683
void ThumbnailView::dropEvent(QDropEvent* event) {
684
const KUrl::List urlList = KUrl::List::fromMimeData(event->mimeData());
685
if (urlList.isEmpty()) {
689
QModelIndex destIndex = indexAt(event->pos());
690
if (destIndex.isValid()) {
691
KFileItem item = fileItemForIndex(destIndex);
693
KUrl destUrl = item.url();
694
d->mThumbnailViewHelper->showMenuForUrlDroppedOnDir(this, urlList, destUrl);
699
d->mThumbnailViewHelper->showMenuForUrlDroppedOnViewport(this, urlList);
701
event->acceptProposedAction();
705
void ThumbnailView::keyPressEvent(QKeyEvent* event) {
706
QListView::keyPressEvent(event);
707
if (event->key() == Qt::Key_Return) {
708
const QModelIndex index = selectionModel()->currentIndex();
709
if (index.isValid() && selectionModel()->selectedIndexes().count() == 1) {
710
emit indexActivated(index);
716
void ThumbnailView::resizeEvent(QResizeEvent* event) {
717
QListView::resizeEvent(event);
718
d->scheduleThumbnailGenerationForVisibleItems();
722
void ThumbnailView::showEvent(QShowEvent* event) {
723
QListView::showEvent(event);
724
d->scheduleThumbnailGenerationForVisibleItems();
725
QTimer::singleShot(0, this, SLOT(scrollToSelectedIndex()));
729
void ThumbnailView::wheelEvent(QWheelEvent* event) {
730
// If we don't adjust the single step, the wheel scroll exactly one item up
731
// and down, giving the impression that the items do not move but only
732
// their label changes.
733
// For some reason it is necessary to set the step here: setting it in
734
// setThumbnailSize() does not work
735
//verticalScrollBar()->setSingleStep(d->mThumbnailSize / 5);
736
if (event->modifiers() == Qt::ControlModifier) {
737
int size = d->mThumbnailSize + (event->delta() > 0 ? 1 : -1) * WHEEL_ZOOM_MULTIPLIER;
738
size = qMax(int(MinThumbnailSize), qMin(size, int(MaxThumbnailSize)));
739
setThumbnailSize(size);
741
QListView::wheelEvent(event);
746
void ThumbnailView::scrollToSelectedIndex() {
747
QModelIndexList list = selectedIndexes();
748
if (list.count() >= 1) {
749
scrollTo(list.first(), PositionAtCenter);
754
void ThumbnailView::selectionChanged(const QItemSelection& selected, const QItemSelection& deselected) {
755
QListView::selectionChanged(selected, deselected);
756
emit selectionChangedSignal(selected, deselected);
760
void ThumbnailView::scrollContentsBy(int dx, int dy) {
761
QListView::scrollContentsBy(dx, dy);
762
d->scheduleThumbnailGenerationForVisibleItems();
766
void ThumbnailView::generateThumbnailsForVisibleItems() {
767
if (!isVisible() || !model()) {
771
QRect visibleRect = viewport()->rect();
772
// Adjust visibleRect so that next invisible rows of thumbnails
775
visibleRect.adjust(0, 0, 0, d->mThumbnailSize * 2);
777
visibleRect.adjust(0, 0, visibleRect.width() / 2, 0);
780
for (int row=0; row < model()->rowCount(); ++row) {
781
QModelIndex index = model()->index(row, 0);
782
KFileItem item = fileItemForIndex(index);
783
QUrl url = item.url();
785
// Filter out invisible items
786
QRect rect = visualRect(index);
787
if (!visibleRect.intersects(rect)) {
791
// Filter out archives
792
MimeTypeUtils::Kind kind = MimeTypeUtils::fileItemKind(item);
793
if (kind == MimeTypeUtils::KIND_ARCHIVE) {
797
// Immediately update modified items
798
if (d->mDocumentInfoProvider && d->mDocumentInfoProvider->isModified(url)) {
799
d->updateThumbnailForModifiedDocument(index);
803
// Filter out items which already have a thumbnail
804
ThumbnailForUrl::ConstIterator it = d->mThumbnailForUrl.constFind(url);
805
if (it != d->mThumbnailForUrl.constEnd() && it.value().isGroupPixAdaptedForSize(d->mThumbnailSize)) {
809
// Add the item to our list
812
// Insert the thumbnail in mThumbnailForUrl, so that
813
// setThumbnail() can find the item to update
814
if (it == d->mThumbnailForUrl.constEnd()) {
815
Thumbnail thumbnail = Thumbnail(QPersistentModelIndex(index), item.time(KFileItem::ModificationTime));
816
d->mThumbnailForUrl.insert(url, thumbnail);
821
d->generateThumbnailsForItems(list);
826
void ThumbnailView::updateThumbnail(const QModelIndex& index) {
827
KFileItem item = fileItemForIndex(index);
828
KUrl url = item.url();
829
if (d->mDocumentInfoProvider && d->mDocumentInfoProvider->isModified(url)) {
830
d->updateThumbnailForModifiedDocument(index);
834
d->generateThumbnailsForItems(list);
839
void ThumbnailView::updateThumbnailBusyState(const QModelIndex& _index, bool busy) {
840
QPersistentModelIndex index(_index);
841
if (busy && !d->mBusyIndexSet.contains(index)) {
842
d->mBusyIndexSet << index;
844
if (d->mBusyAnimationTimeLine->state() != QTimeLine::Running) {
845
d->mBusyAnimationTimeLine->start();
847
} else if (!busy && d->mBusyIndexSet.remove(index)) {
849
if (d->mBusyIndexSet.isEmpty()) {
850
d->mBusyAnimationTimeLine->stop();
856
void ThumbnailView::updateBusyIndexes() {
857
Q_FOREACH(const QPersistentModelIndex& index, d->mBusyIndexSet) {
863
QPixmap ThumbnailView::busySequenceCurrentPixmap() const {
864
return d->mBusySequence.frameAt(d->mBusyAnimationTimeLine->currentFrame());
868
void ThumbnailView::smoothNextThumbnail() {
869
if (d->mSmoothThumbnailQueue.isEmpty()) {
873
if (d->mThumbnailLoadJob) {
874
// give mThumbnailLoadJob priority over smoothing
875
d->mSmoothThumbnailTimer.start(SMOOTH_DELAY);
879
KUrl url = d->mSmoothThumbnailQueue.dequeue();
880
ThumbnailForUrl::Iterator it = d->mThumbnailForUrl.find(url);
881
if (it == d->mThumbnailForUrl.end()) {
882
kWarning() << url << " not in mThumbnailForUrl. This should not happen!";
886
Thumbnail& thumbnail = it.value();
887
thumbnail.mAdjustedPix = thumbnail.mGroupPix.scaled(d->mThumbnailSize, d->mThumbnailSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
888
thumbnail.mRough = false;
890
if (thumbnail.mIndex.isValid()) {
891
update(thumbnail.mIndex);
893
kWarning() << "index for" << url << "is invalid. This should not happen!";
896
if (!d->mSmoothThumbnailQueue.isEmpty()) {
897
d->mSmoothThumbnailTimer.start(0);
902
void ThumbnailView::reloadThumbnail(const QModelIndex& index) {
903
KUrl url = urlForIndex(index);
904
if (!url.isValid()) {
905
kWarning() << "Invalid url for index" << index;
908
ThumbnailLoadJob::deleteImageThumbnail(url);
909
ThumbnailForUrl::Iterator it = d->mThumbnailForUrl.find(url);
910
if (it == d->mThumbnailForUrl.end()) {
913
d->mThumbnailForUrl.erase(it);
914
generateThumbnailsForVisibleItems();
308
, d(new ThumbnailViewPrivate)
311
d->mThumbnailViewHelper = 0;
312
d->mDocumentInfoProvider = 0;
313
// Init to some stupid value so that the first call to setThumbnailSize()
314
// is not ignored (do not use 0 in case someone try to divide by
315
// mThumbnailSize...)
316
d->mThumbnailSize = 1;
318
setFrameShape(QFrame::NoFrame);
319
setViewMode(QListView::IconMode);
320
setResizeMode(QListView::Adjust);
321
setDragEnabled(true);
322
setAcceptDrops(true);
323
setDropIndicatorShown(true);
324
setUniformItemSizes(true);
325
setEditTriggers(QAbstractItemView::EditKeyPressed);
327
d->setupBusyAnimation();
329
viewport()->setMouseTracking(true);
330
// Set this attribute, otherwise the item delegate won't get the
331
// State_MouseOver state
332
viewport()->setAttribute(Qt::WA_Hover);
334
setVerticalScrollMode(ScrollPerPixel);
335
setHorizontalScrollMode(ScrollPerPixel);
337
d->mScheduledThumbnailGenerationTimer.setSingleShot(true);
338
d->mScheduledThumbnailGenerationTimer.setInterval(500);
339
connect(&d->mScheduledThumbnailGenerationTimer, SIGNAL(timeout()),
340
SLOT(generateThumbnailsForVisibleItems()));
342
d->mSmoothThumbnailTimer.setSingleShot(true);
343
connect(&d->mSmoothThumbnailTimer, SIGNAL(timeout()),
344
SLOT(smoothNextThumbnail()));
346
setContextMenuPolicy(Qt::CustomContextMenu);
347
connect(this, SIGNAL(customContextMenuRequested(QPoint)),
348
SLOT(showContextMenu()));
350
if (KGlobalSettings::singleClick()) {
351
connect(this, SIGNAL(clicked(QModelIndex)),
352
SLOT(emitIndexActivatedIfNoModifiers(QModelIndex)));
354
connect(this, SIGNAL(doubleClicked(QModelIndex)),
355
SLOT(emitIndexActivatedIfNoModifiers(QModelIndex)));
359
ThumbnailView::~ThumbnailView()
364
void ThumbnailView::setModel(QAbstractItemModel* newModel)
367
disconnect(model(), 0, this, 0);
369
QListView::setModel(newModel);
370
connect(model(), SIGNAL(rowsRemoved(QModelIndex, int, int)),
371
SIGNAL(rowsRemovedSignal(QModelIndex, int, int)));
374
void ThumbnailView::setThumbnailSize(int value)
376
if (d->mThumbnailSize == value) {
379
d->mThumbnailSize = value;
382
int waitingThumbnailSize;
384
waitingThumbnailSize = 48;
386
waitingThumbnailSize = 32;
388
if (d->mWaitingThumbnail.width() != waitingThumbnailSize) {
389
QPixmap icon = DesktopIcon("chronometer", waitingThumbnailSize);
390
QPixmap pix(icon.size());
391
pix.fill(Qt::transparent);
392
QPainter painter(&pix);
393
painter.setOpacity(0.5);
394
painter.drawPixmap(0, 0, icon);
396
d->mWaitingThumbnail = pix;
400
d->mSmoothThumbnailTimer.stop();
401
d->mSmoothThumbnailQueue.clear();
403
// Clear adjustedPixes
404
ThumbnailForUrl::iterator
405
it = d->mThumbnailForUrl.begin(),
406
end = d->mThumbnailForUrl.end();
407
for (; it != end; ++it) {
408
it.value().mAdjustedPix = QPixmap();
411
thumbnailSizeChanged(value);
412
d->scheduleThumbnailGenerationForVisibleItems();
415
int ThumbnailView::thumbnailSize() const
417
return d->mThumbnailSize;
420
void ThumbnailView::setThumbnailViewHelper(AbstractThumbnailViewHelper* helper)
422
d->mThumbnailViewHelper = helper;
425
AbstractThumbnailViewHelper* ThumbnailView::thumbnailViewHelper() const
427
return d->mThumbnailViewHelper;
430
void ThumbnailView::setDocumentInfoProvider(AbstractDocumentInfoProvider* provider)
432
d->mDocumentInfoProvider = provider;
434
connect(provider, SIGNAL(busyStateChanged(QModelIndex, bool)),
435
SLOT(updateThumbnailBusyState(QModelIndex, bool)));
436
connect(provider, SIGNAL(documentChanged(QModelIndex)),
437
SLOT(updateThumbnail(QModelIndex)));
441
AbstractDocumentInfoProvider* ThumbnailView::documentInfoProvider() const
443
return d->mDocumentInfoProvider;
446
void ThumbnailView::rowsAboutToBeRemoved(const QModelIndex& parent, int start, int end)
448
QListView::rowsAboutToBeRemoved(parent, start, end);
450
// Remove references to removed items
451
KFileItemList itemList;
452
for (int pos = start; pos <= end; ++pos) {
453
QModelIndex index = model()->index(pos, 0, parent);
454
KFileItem item = fileItemForIndex(index);
456
kDebug() << "Skipping invalid item!" << index.data().toString();
460
QUrl url = item.url();
461
d->mThumbnailForUrl.remove(url);
462
d->mSmoothThumbnailQueue.removeAll(url);
464
itemList.append(item);
467
if (d->mThumbnailLoadJob) {
468
d->mThumbnailLoadJob->removeItems(itemList);
471
// Update current index if it is among the deleted rows
472
const int row = currentIndex().row();
473
if (start <= row && row <= end) {
475
if (end < model()->rowCount() - 1) {
476
index = model()->index(end + 1, 0);
477
} else if (start > 0) {
478
index = model()->index(start - 1, 0);
480
setCurrentIndex(index);
483
// Removing rows might make new images visible, make sure their thumbnail
485
d->mScheduledThumbnailGenerationTimer.start();
488
void ThumbnailView::rowsInserted(const QModelIndex& parent, int start, int end)
490
QListView::rowsInserted(parent, start, end);
491
d->mScheduledThumbnailGenerationTimer.start();
492
rowsInsertedSignal(parent, start, end);
495
void ThumbnailView::dataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight)
497
QListView::dataChanged(topLeft, bottomRight);
498
bool thumbnailsNeedRefresh = false;
499
for (int row = topLeft.row(); row <= bottomRight.row(); ++row) {
500
QModelIndex index = model()->index(row, 0);
501
KFileItem item = fileItemForIndex(index);
503
kWarning() << "Invalid item for index" << index << ". This should not happen!";
507
ThumbnailForUrl::Iterator it = d->mThumbnailForUrl.find(item.url());
508
if (it != d->mThumbnailForUrl.end()) {
509
// All thumbnail views are connected to the model, so
510
// ThumbnailView::dataChanged() is called for all of them. As a
511
// result this method will also be called for views which are not
512
// currently visible, and do not yet have a thumbnail for the
514
KDateTime mtime = item.time(KFileItem::ModificationTime);
515
if (it->mModificationTime != mtime) {
516
// dataChanged() is called when the file changes but also when
517
// the model fetched additional data such as semantic info. To
518
// avoid needless refreshes, we only trigger a refresh if the
519
// modification time changes.
520
thumbnailsNeedRefresh = true;
521
it->prepareForRefresh(mtime);
525
if (thumbnailsNeedRefresh) {
526
d->mScheduledThumbnailGenerationTimer.start();
530
void ThumbnailView::showContextMenu()
532
d->mThumbnailViewHelper->showContextMenu(this);
535
void ThumbnailView::emitIndexActivatedIfNoModifiers(const QModelIndex& index)
537
if (QApplication::keyboardModifiers() == Qt::NoModifier) {
538
emit indexActivated(index);
542
void ThumbnailView::setThumbnail(const KFileItem& item, const QPixmap& pixmap, const QSize& size)
544
ThumbnailForUrl::iterator it = d->mThumbnailForUrl.find(item.url());
545
if (it == d->mThumbnailForUrl.end()) {
548
Thumbnail& thumbnail = it.value();
549
thumbnail.mGroupPix = pixmap;
550
thumbnail.mAdjustedPix = QPixmap();
551
int largeGroupSize = ThumbnailGroup::pixelSize(ThumbnailGroup::Large);
552
thumbnail.mFullSize = size.isValid() ? size : QSize(largeGroupSize, largeGroupSize);
553
thumbnail.mRealFullSize = size;
554
thumbnail.mWaitingForThumbnail = false;
556
update(thumbnail.mIndex);
559
void ThumbnailView::setBrokenThumbnail(const KFileItem& item)
561
ThumbnailForUrl::iterator it = d->mThumbnailForUrl.find(item.url());
562
if (it == d->mThumbnailForUrl.end()) {
565
Thumbnail& thumbnail = it.value();
566
MimeTypeUtils::Kind kind = MimeTypeUtils::fileItemKind(item);
567
if (kind == MimeTypeUtils::KIND_VIDEO) {
568
// Special case for videos because our kde install may come without
569
// support for video thumbnails so we show the mimetype icon instead of
570
// a broken image icon
571
ThumbnailGroup::Enum group = ThumbnailGroup::fromPixelSize(d->mThumbnailSize);
572
QPixmap pix = item.pixmap(ThumbnailGroup::pixelSize(group));
573
thumbnail.initAsIcon(pix);
574
} else if (kind == MimeTypeUtils::KIND_DIR) {
575
// Special case for folders because ThumbnailLoadJob does not return a
576
// thumbnail if there is no images
577
thumbnail.mWaitingForThumbnail = false;
580
thumbnail.initAsIcon(DesktopIcon("image-missing", 48));
581
thumbnail.mFullSize = thumbnail.mGroupPix.size();
583
update(thumbnail.mIndex);
586
QPixmap ThumbnailView::thumbnailForIndex(const QModelIndex& index, QSize* fullSize)
588
KFileItem item = fileItemForIndex(index);
596
KUrl url = item.url();
598
// Find or create Thumbnail instance
599
ThumbnailForUrl::Iterator it = d->mThumbnailForUrl.find(url);
600
if (it == d->mThumbnailForUrl.end()) {
601
Thumbnail thumbnail = Thumbnail(QPersistentModelIndex(index), item.time(KFileItem::ModificationTime));
602
it = d->mThumbnailForUrl.insert(url, thumbnail);
604
Thumbnail& thumbnail = it.value();
606
// If dir or archive, generate a thumbnail from fileitem pixmap
607
MimeTypeUtils::Kind kind = MimeTypeUtils::fileItemKind(item);
608
if (kind == MimeTypeUtils::KIND_ARCHIVE || kind == MimeTypeUtils::KIND_DIR) {
609
int groupSize = ThumbnailGroup::pixelSize(ThumbnailGroup::fromPixelSize(d->mThumbnailSize));
610
if (thumbnail.mGroupPix.isNull() || thumbnail.mGroupPix.width() < groupSize) {
611
QPixmap pix = item.pixmap(groupSize);
612
thumbnail.initAsIcon(pix);
613
if (kind == MimeTypeUtils::KIND_ARCHIVE) {
614
// No thumbnails for archives
615
thumbnail.mWaitingForThumbnail = false;
617
// set mWaitingForThumbnail to true (necessary in the case
618
// 'thumbnail' already existed before, but with a too small
620
thumbnail.mWaitingForThumbnail = true;
625
if (thumbnail.mGroupPix.isNull()) {
629
return d->mWaitingThumbnail;
633
if (thumbnail.mAdjustedPix.isNull()) {
634
d->roughAdjustThumbnail(&thumbnail);
636
if (thumbnail.mRough && !d->mSmoothThumbnailQueue.contains(url)) {
637
d->mSmoothThumbnailQueue.enqueue(url);
638
if (!d->mSmoothThumbnailTimer.isActive()) {
639
d->mSmoothThumbnailTimer.start(SMOOTH_DELAY);
643
*fullSize = thumbnail.mRealFullSize;
645
return thumbnail.mAdjustedPix;
648
bool ThumbnailView::isModified(const QModelIndex& index) const
650
if (!d->mDocumentInfoProvider) {
653
KUrl url = urlForIndex(index);
654
return d->mDocumentInfoProvider->isModified(url);
657
bool ThumbnailView::isBusy(const QModelIndex& index) const
659
if (!d->mDocumentInfoProvider) {
662
KUrl url = urlForIndex(index);
663
return d->mDocumentInfoProvider->isBusy(url);
666
void ThumbnailView::startDrag(Qt::DropActions supportedActions)
668
QModelIndexList indexes = selectionModel()->selectedIndexes();
669
if (indexes.isEmpty()) {
672
QDrag* drag = new QDrag(this);
673
drag->setMimeData(model()->mimeData(indexes));
674
QPixmap pix = d->createDragPixmap(indexes);
675
drag->setPixmap(pix);
676
drag->exec(supportedActions, Qt::CopyAction);
679
void ThumbnailView::dragEnterEvent(QDragEnterEvent* event)
681
QAbstractItemView::dragEnterEvent(event);
682
if (event->mimeData()->hasUrls()) {
683
event->acceptProposedAction();
687
void ThumbnailView::dragMoveEvent(QDragMoveEvent* event)
689
// Necessary, otherwise we don't reach dropEvent()
690
QAbstractItemView::dragMoveEvent(event);
691
event->acceptProposedAction();
694
void ThumbnailView::dropEvent(QDropEvent* event)
696
const KUrl::List urlList = KUrl::List::fromMimeData(event->mimeData());
697
if (urlList.isEmpty()) {
701
QModelIndex destIndex = indexAt(event->pos());
702
if (destIndex.isValid()) {
703
KFileItem item = fileItemForIndex(destIndex);
705
KUrl destUrl = item.url();
706
d->mThumbnailViewHelper->showMenuForUrlDroppedOnDir(this, urlList, destUrl);
711
d->mThumbnailViewHelper->showMenuForUrlDroppedOnViewport(this, urlList);
713
event->acceptProposedAction();
716
void ThumbnailView::keyPressEvent(QKeyEvent* event)
718
QListView::keyPressEvent(event);
719
if (event->key() == Qt::Key_Return) {
720
const QModelIndex index = selectionModel()->currentIndex();
721
if (index.isValid() && selectionModel()->selectedIndexes().count() == 1) {
722
emit indexActivated(index);
727
void ThumbnailView::resizeEvent(QResizeEvent* event)
729
QListView::resizeEvent(event);
730
d->scheduleThumbnailGenerationForVisibleItems();
733
void ThumbnailView::showEvent(QShowEvent* event)
735
QListView::showEvent(event);
736
d->scheduleThumbnailGenerationForVisibleItems();
737
QTimer::singleShot(0, this, SLOT(scrollToSelectedIndex()));
740
void ThumbnailView::wheelEvent(QWheelEvent* event)
742
// If we don't adjust the single step, the wheel scroll exactly one item up
743
// and down, giving the impression that the items do not move but only
744
// their label changes.
745
// For some reason it is necessary to set the step here: setting it in
746
// setThumbnailSize() does not work
747
//verticalScrollBar()->setSingleStep(d->mThumbnailSize / 5);
748
if (event->modifiers() == Qt::ControlModifier) {
749
int size = d->mThumbnailSize + (event->delta() > 0 ? 1 : -1) * WHEEL_ZOOM_MULTIPLIER;
750
size = qMax(int(MinThumbnailSize), qMin(size, int(MaxThumbnailSize)));
751
setThumbnailSize(size);
753
QListView::wheelEvent(event);
757
void ThumbnailView::scrollToSelectedIndex()
759
QModelIndexList list = selectedIndexes();
760
if (list.count() >= 1) {
761
scrollTo(list.first(), PositionAtCenter);
765
void ThumbnailView::selectionChanged(const QItemSelection& selected, const QItemSelection& deselected)
767
QListView::selectionChanged(selected, deselected);
768
emit selectionChangedSignal(selected, deselected);
771
void ThumbnailView::scrollContentsBy(int dx, int dy)
773
QListView::scrollContentsBy(dx, dy);
774
d->scheduleThumbnailGenerationForVisibleItems();
777
void ThumbnailView::generateThumbnailsForVisibleItems()
779
if (!isVisible() || !model()) {
783
QRect visibleRect = viewport()->rect();
784
// Adjust visibleRect so that next thumbnail page|row
787
visibleRect.setHeight(visibleRect.height() * 2);
789
visibleRect.setWidth(visibleRect.width() * 2);
792
for (int row = 0; row < model()->rowCount(); ++row) {
793
QModelIndex index = model()->index(row, 0);
794
KFileItem item = fileItemForIndex(index);
795
QUrl url = item.url();
797
// Filter out invisible items
798
QRect rect = visualRect(index);
799
if (!visibleRect.intersects(rect)) {
803
// Filter out archives
804
MimeTypeUtils::Kind kind = MimeTypeUtils::fileItemKind(item);
805
if (kind == MimeTypeUtils::KIND_ARCHIVE) {
809
// Immediately update modified items
810
if (d->mDocumentInfoProvider && d->mDocumentInfoProvider->isModified(url)) {
811
d->updateThumbnailForModifiedDocument(index);
815
// Filter out items which already have a thumbnail
816
ThumbnailForUrl::ConstIterator it = d->mThumbnailForUrl.constFind(url);
817
if (it != d->mThumbnailForUrl.constEnd() && it.value().isGroupPixAdaptedForSize(d->mThumbnailSize)) {
821
// Add the item to our list
824
// Insert the thumbnail in mThumbnailForUrl, so that
825
// setThumbnail() can find the item to update
826
if (it == d->mThumbnailForUrl.constEnd()) {
827
Thumbnail thumbnail = Thumbnail(QPersistentModelIndex(index), item.time(KFileItem::ModificationTime));
828
d->mThumbnailForUrl.insert(url, thumbnail);
833
d->generateThumbnailsForItems(list);
837
void ThumbnailView::updateThumbnail(const QModelIndex& index)
839
KFileItem item = fileItemForIndex(index);
840
KUrl url = item.url();
841
if (d->mDocumentInfoProvider && d->mDocumentInfoProvider->isModified(url)) {
842
d->updateThumbnailForModifiedDocument(index);
846
d->generateThumbnailsForItems(list);
850
void ThumbnailView::updateThumbnailBusyState(const QModelIndex& _index, bool busy)
852
QPersistentModelIndex index(_index);
853
if (busy && !d->mBusyIndexSet.contains(index)) {
854
d->mBusyIndexSet << index;
856
if (d->mBusyAnimationTimeLine->state() != QTimeLine::Running) {
857
d->mBusyAnimationTimeLine->start();
859
} else if (!busy && d->mBusyIndexSet.remove(index)) {
861
if (d->mBusyIndexSet.isEmpty()) {
862
d->mBusyAnimationTimeLine->stop();
867
void ThumbnailView::updateBusyIndexes()
869
Q_FOREACH(const QPersistentModelIndex & index, d->mBusyIndexSet) {
874
QPixmap ThumbnailView::busySequenceCurrentPixmap() const
876
return d->mBusySequence.frameAt(d->mBusyAnimationTimeLine->currentFrame());
879
void ThumbnailView::smoothNextThumbnail()
881
if (d->mSmoothThumbnailQueue.isEmpty()) {
885
if (d->mThumbnailLoadJob) {
886
// give mThumbnailLoadJob priority over smoothing
887
d->mSmoothThumbnailTimer.start(SMOOTH_DELAY);
891
KUrl url = d->mSmoothThumbnailQueue.dequeue();
892
ThumbnailForUrl::Iterator it = d->mThumbnailForUrl.find(url);
893
if (it == d->mThumbnailForUrl.end()) {
894
kWarning() << url << " not in mThumbnailForUrl. This should not happen!";
898
Thumbnail& thumbnail = it.value();
899
thumbnail.mAdjustedPix = thumbnail.mGroupPix.scaled(d->mThumbnailSize, d->mThumbnailSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
900
thumbnail.mRough = false;
902
if (thumbnail.mIndex.isValid()) {
903
update(thumbnail.mIndex);
905
kWarning() << "index for" << url << "is invalid. This should not happen!";
908
if (!d->mSmoothThumbnailQueue.isEmpty()) {
909
d->mSmoothThumbnailTimer.start(0);
913
void ThumbnailView::reloadThumbnail(const QModelIndex& index)
915
KUrl url = urlForIndex(index);
916
if (!url.isValid()) {
917
kWarning() << "Invalid url for index" << index;
920
ThumbnailLoadJob::deleteImageThumbnail(url);
921
ThumbnailForUrl::Iterator it = d->mThumbnailForUrl.find(url);
922
if (it == d->mThumbnailForUrl.end()) {
925
d->mThumbnailForUrl.erase(it);
926
generateThumbnailsForVisibleItems();