151
154
: mCancel(false) {}
153
156
void ThumbnailThread::load(
154
const QString& originalUri, time_t originalTime, int originalSize, const QString& originalMimeType,
155
const QString& pixPath,
156
const QString& thumbnailPath,
157
ThumbnailGroup::Enum group)
159
QMutexLocker lock( &mMutex );
160
assert( mPixPath.isNull());
162
mOriginalUri = originalUri;
163
mOriginalTime = originalTime;
164
mOriginalSize = originalSize;
165
mOriginalMimeType = originalMimeType;
167
mThumbnailPath = thumbnailPath;
168
mThumbnailGroup = group;
169
if(!isRunning()) start();
173
bool ThumbnailThread::testCancel() {
174
QMutexLocker lock( &mMutex );
178
void ThumbnailThread::cancel() {
179
QMutexLocker lock( &mMutex );
184
void ThumbnailThread::run() {
186
while( !testCancel()) {
188
QMutexLocker lock(&mMutex);
189
// empty mPixPath means nothing to do
190
LOG("Waiting for mPixPath");
191
if (mPixPath.isNull()) {
192
LOG("mPixPath.isNull");
200
QMutexLocker lock(&mMutex);
201
Q_ASSERT(!mPixPath.isNull());
202
LOG("Loading" << mPixPath);
204
bool ok = loadThumbnail(&needCaching);
205
if (ok && needCaching) {
208
mPixPath.clear(); // done, ready for next
214
QSize size(mOriginalWidth, mOriginalHeight);
215
LOG("emitting done signal, size=" << size);
216
QMutexLocker lock(&mMutex);
221
LOG("Ending thread");
224
bool ThumbnailThread::loadThumbnail(bool* needCaching) {
227
int pixelSize = ThumbnailGroup::pixelSize(mThumbnailGroup);
228
Orientation orientation = NORMAL;
230
QImageReader reader(mPixPath);
231
// If it's a Jpeg, try to load an embedded thumbnail, if available
232
if (reader.format() == "jpeg") {
234
content.load(mPixPath);
235
QImage thumbnail = content.thumbnail();
236
orientation = content.orientation();
238
if (qMax(thumbnail.width(), thumbnail.height()) >= pixelSize) {
240
if (orientation != NORMAL && orientation != NOT_AVAILABLE) {
241
QMatrix matrix = ImageUtils::transformMatrix(orientation);
242
mImage = mImage.transformed(matrix);
244
mOriginalWidth = content.size().width();
245
mOriginalHeight = content.size().height();
250
// Generate thumbnail from full image
251
QSize originalSize = reader.size();
252
if (originalSize.isValid() && reader.supportsOption(QImageIOHandler::ScaledSize)) {
254
const int maxSize = qMax(originalSize.width(), originalSize.height());
255
for (scale=1; pixelSize*scale*2 <= maxSize && scale <= 8; scale *= 2) {}
256
const QSize scaledSize = originalSize / scale;
257
if (!scaledSize.isEmpty()) {
258
reader.setScaledSize(scaledSize);
262
QImage originalImage;
263
// format() is empty after QImageReader::read() is called
264
QByteArray format = reader.format();
265
if (!reader.read(&originalImage)) {
266
kWarning() << "Could not generate thumbnail for file" << mOriginalUri;
269
if (!originalSize.isValid()) {
270
originalSize = originalImage.size();
272
mOriginalWidth = originalSize.width();
273
mOriginalHeight = originalSize.height();
275
if (qMax(mOriginalWidth, mOriginalHeight)<=pixelSize) {
276
mImage = originalImage;
277
*needCaching = format != "png";
279
mImage = originalImage.scaled(pixelSize, pixelSize, Qt::KeepAspectRatio);
282
// Rotate if necessary
283
if (orientation != NORMAL && orientation != NOT_AVAILABLE) {
284
QMatrix matrix = ImageUtils::transformMatrix(orientation);
285
mImage = mImage.transformed(matrix);
287
switch (orientation) {
292
qSwap(mOriginalWidth, mOriginalHeight);
302
void ThumbnailThread::cacheThumbnail() {
303
mImage.setText("Thumb::Uri" , 0, mOriginalUri);
304
mImage.setText("Thumb::MTime" , 0, QString::number(mOriginalTime));
305
mImage.setText("Thumb::Size" , 0, QString::number(mOriginalSize));
306
mImage.setText("Thumb::Mimetype" , 0, mOriginalMimeType);
307
mImage.setText("Thumb::Image::Width" , 0, QString::number(mOriginalWidth));
308
mImage.setText("Thumb::Image::Height", 0, QString::number(mOriginalHeight));
309
mImage.setText("Software" , 0, "Gwenview");
311
emit thumbnailReadyToBeCached(mThumbnailPath, mImage);
157
const QString& originalUri, time_t originalTime, int originalSize, const QString& originalMimeType,
158
const QString& pixPath,
159
const QString& thumbnailPath,
160
ThumbnailGroup::Enum group)
162
QMutexLocker lock(&mMutex);
163
assert(mPixPath.isNull());
165
mOriginalUri = originalUri;
166
mOriginalTime = originalTime;
167
mOriginalSize = originalSize;
168
mOriginalMimeType = originalMimeType;
170
mThumbnailPath = thumbnailPath;
171
mThumbnailGroup = group;
172
if (!isRunning()) start();
176
bool ThumbnailThread::testCancel()
178
QMutexLocker lock(&mMutex);
182
void ThumbnailThread::cancel()
184
QMutexLocker lock(&mMutex);
189
void ThumbnailThread::run()
192
while (!testCancel()) {
194
QMutexLocker lock(&mMutex);
195
// empty mPixPath means nothing to do
196
LOG("Waiting for mPixPath");
197
if (mPixPath.isNull()) {
198
LOG("mPixPath.isNull");
206
QMutexLocker lock(&mMutex);
207
Q_ASSERT(!mPixPath.isNull());
208
LOG("Loading" << mPixPath);
210
bool ok = loadThumbnail(&needCaching);
211
if (ok && needCaching) {
214
mPixPath.clear(); // done, ready for next
220
QSize size(mOriginalWidth, mOriginalHeight);
221
LOG("emitting done signal, size=" << size);
222
QMutexLocker lock(&mMutex);
227
LOG("Ending thread");
230
bool ThumbnailThread::loadThumbnail(bool* needCaching)
234
int pixelSize = ThumbnailGroup::pixelSize(mThumbnailGroup);
235
Orientation orientation = NORMAL;
237
QImageReader reader(mPixPath);
238
// If it's a Jpeg, try to load an embedded thumbnail, if available
239
if (reader.format() == "jpeg") {
241
content.load(mPixPath);
242
QImage thumbnail = content.thumbnail();
243
orientation = content.orientation();
245
if (qMax(thumbnail.width(), thumbnail.height()) >= pixelSize) {
247
if (orientation != NORMAL && orientation != NOT_AVAILABLE) {
248
QMatrix matrix = ImageUtils::transformMatrix(orientation);
249
mImage = mImage.transformed(matrix);
251
mOriginalWidth = content.size().width();
252
mOriginalHeight = content.size().height();
257
// Generate thumbnail from full image
258
QSize originalSize = reader.size();
259
if (originalSize.isValid() && reader.supportsOption(QImageIOHandler::ScaledSize)) {
261
const int maxSize = qMax(originalSize.width(), originalSize.height());
262
for (scale = 1; pixelSize*scale * 2 <= maxSize && scale <= 8; scale *= 2) {}
263
const QSize scaledSize = originalSize / scale;
264
if (!scaledSize.isEmpty()) {
265
reader.setScaledSize(scaledSize);
269
QImage originalImage;
270
// format() is empty after QImageReader::read() is called
271
QByteArray format = reader.format();
272
if (!reader.read(&originalImage)) {
273
kWarning() << "Could not generate thumbnail for file" << mOriginalUri;
276
if (!originalSize.isValid()) {
277
originalSize = originalImage.size();
279
mOriginalWidth = originalSize.width();
280
mOriginalHeight = originalSize.height();
282
if (qMax(mOriginalWidth, mOriginalHeight) <= pixelSize) {
283
mImage = originalImage;
284
*needCaching = format != "png";
286
mImage = originalImage.scaled(pixelSize, pixelSize, Qt::KeepAspectRatio);
289
// Rotate if necessary
290
if (orientation != NORMAL && orientation != NOT_AVAILABLE) {
291
QMatrix matrix = ImageUtils::transformMatrix(orientation);
292
mImage = mImage.transformed(matrix);
294
switch (orientation) {
299
qSwap(mOriginalWidth, mOriginalHeight);
308
void ThumbnailThread::cacheThumbnail()
310
mImage.setText("Thumb::Uri" , 0, mOriginalUri);
311
mImage.setText("Thumb::MTime" , 0, QString::number(mOriginalTime));
312
mImage.setText("Thumb::Size" , 0, QString::number(mOriginalSize));
313
mImage.setText("Thumb::Mimetype" , 0, mOriginalMimeType);
314
mImage.setText("Thumb::Image::Width" , 0, QString::number(mOriginalWidth));
315
mImage.setText("Thumb::Image::Height", 0, QString::number(mOriginalHeight));
316
mImage.setText("Software" , 0, "Gwenview");
318
emit thumbnailReadyToBeCached(mThumbnailPath, mImage);
315
321
//------------------------------------------------------------------------
319
325
//------------------------------------------------------------------------
320
326
static QString sThumbnailBaseDir;
321
QString ThumbnailLoadJob::thumbnailBaseDir() {
322
if (sThumbnailBaseDir.isEmpty()) {
323
sThumbnailBaseDir = QDir::homePath() + "/.thumbnails/";
325
return sThumbnailBaseDir;
329
void ThumbnailLoadJob::setThumbnailBaseDir(const QString& dir) {
330
sThumbnailBaseDir = dir;
334
QString ThumbnailLoadJob::thumbnailBaseDir(ThumbnailGroup::Enum group) {
335
QString dir = thumbnailBaseDir();
337
case ThumbnailGroup::Normal:
340
case ThumbnailGroup::Large:
348
void ThumbnailLoadJob::deleteImageThumbnail(const KUrl& url) {
349
QString uri=generateOriginalUri(url);
350
QFile::remove(generateThumbnailPath(uri, ThumbnailGroup::Normal));
351
QFile::remove(generateThumbnailPath(uri, ThumbnailGroup::Large));
355
static void moveThumbnailHelper(const QString& oldUri, const QString& newUri, ThumbnailGroup::Enum group) {
356
QString oldPath = generateThumbnailPath(oldUri, group);
357
QString newPath = generateThumbnailPath(newUri, group);
359
if (!thumb.load(oldPath)) {
362
thumb.setText("Thumb::Uri", 0, newUri);
363
thumb.save(newPath, "png");
364
QFile::remove(QFile::encodeName(oldPath));
367
void ThumbnailLoadJob::moveThumbnail(const KUrl& oldUrl, const KUrl& newUrl) {
368
QString oldUri = generateOriginalUri(oldUrl);
369
QString newUri = generateOriginalUri(newUrl);
370
moveThumbnailHelper(oldUri, newUri, ThumbnailGroup::Normal);
371
moveThumbnailHelper(oldUri, newUri, ThumbnailGroup::Large);
327
QString ThumbnailLoadJob::thumbnailBaseDir()
329
if (sThumbnailBaseDir.isEmpty()) {
330
sThumbnailBaseDir = QDir::homePath() + "/.thumbnails/";
332
return sThumbnailBaseDir;
335
void ThumbnailLoadJob::setThumbnailBaseDir(const QString& dir)
337
sThumbnailBaseDir = dir;
340
QString ThumbnailLoadJob::thumbnailBaseDir(ThumbnailGroup::Enum group)
342
QString dir = thumbnailBaseDir();
344
case ThumbnailGroup::Normal:
347
case ThumbnailGroup::Large:
354
void ThumbnailLoadJob::deleteImageThumbnail(const KUrl& url)
356
QString uri = generateOriginalUri(url);
357
QFile::remove(generateThumbnailPath(uri, ThumbnailGroup::Normal));
358
QFile::remove(generateThumbnailPath(uri, ThumbnailGroup::Large));
361
static void moveThumbnailHelper(const QString& oldUri, const QString& newUri, ThumbnailGroup::Enum group)
363
QString oldPath = generateThumbnailPath(oldUri, group);
364
QString newPath = generateThumbnailPath(newUri, group);
366
if (!thumb.load(oldPath)) {
369
thumb.setText("Thumb::Uri", 0, newUri);
370
thumb.save(newPath, "png");
371
QFile::remove(QFile::encodeName(oldPath));
374
void ThumbnailLoadJob::moveThumbnail(const KUrl& oldUrl, const KUrl& newUrl)
376
QString oldUri = generateOriginalUri(oldUrl);
377
QString newUri = generateOriginalUri(newUrl);
378
moveThumbnailHelper(oldUri, newUri, ThumbnailGroup::Normal);
379
moveThumbnailHelper(oldUri, newUri, ThumbnailGroup::Large);
375
382
//------------------------------------------------------------------------
379
386
//------------------------------------------------------------------------
380
387
ThumbnailLoadJob::ThumbnailLoadJob(const KFileItemList& items, ThumbnailGroup::Enum group)
382
, mState( STATE_NEXTTHUMB )
389
, mState(STATE_NEXTTHUMB)
383
390
, mThumbnailGroup(group)
387
// Make sure we have a place to store our thumbnails
388
QString thumbnailDir = ThumbnailLoadJob::thumbnailBaseDir(mThumbnailGroup);
389
KStandardDirs::makeDir(thumbnailDir, 0700);
391
// Look for images and store the items in our todo list
392
Q_ASSERT(!items.empty());
394
mCurrentItem = KFileItem();
396
connect(&mThumbnailThread, SIGNAL(done(const QImage&, const QSize&)),
397
SLOT(thumbnailReady(const QImage&, const QSize&)),
398
Qt::QueuedConnection);
400
connect(&mThumbnailThread, SIGNAL(thumbnailReadyToBeCached(const QString&, const QImage&)),
401
sThumbnailCache, SLOT(queueThumbnail(const QString&, const QImage&)),
402
Qt::QueuedConnection);
406
ThumbnailLoadJob::~ThumbnailLoadJob() {
409
LOG("Killing subjob");
410
KJob* job = subjobs().first();
414
mThumbnailThread.cancel();
415
mThumbnailThread.wait();
416
if (!sThumbnailCache->isRunning()) {
417
sThumbnailCache->start();
422
void ThumbnailLoadJob::start() {
423
if (mItems.isEmpty()) {
424
LOG("Nothing to do");
434
const KFileItemList& ThumbnailLoadJob::pendingItems() const {
439
void ThumbnailLoadJob::setThumbnailGroup(ThumbnailGroup::Enum group) {
440
mThumbnailGroup = group;
394
// Make sure we have a place to store our thumbnails
395
QString thumbnailDir = ThumbnailLoadJob::thumbnailBaseDir(mThumbnailGroup);
396
KStandardDirs::makeDir(thumbnailDir, 0700);
398
// Look for images and store the items in our todo list
399
Q_ASSERT(!items.empty());
401
mCurrentItem = KFileItem();
403
connect(&mThumbnailThread, SIGNAL(done(QImage, QSize)),
404
SLOT(thumbnailReady(QImage, QSize)),
405
Qt::QueuedConnection);
407
connect(&mThumbnailThread, SIGNAL(thumbnailReadyToBeCached(QString, QImage)),
408
sThumbnailCache, SLOT(queueThumbnail(QString, QImage)),
409
Qt::QueuedConnection);
412
ThumbnailLoadJob::~ThumbnailLoadJob()
416
LOG("Killing subjob");
417
KJob* job = subjobs().first();
421
mThumbnailThread.cancel();
422
mThumbnailThread.wait();
423
if (!sThumbnailCache->isRunning()) {
424
sThumbnailCache->start();
428
void ThumbnailLoadJob::start()
430
if (mItems.isEmpty()) {
431
LOG("Nothing to do");
440
const KFileItemList& ThumbnailLoadJob::pendingItems() const
445
void ThumbnailLoadJob::setThumbnailGroup(ThumbnailGroup::Enum group)
447
mThumbnailGroup = group;
444
450
//-Internal--------------------------------------------------------------
445
void ThumbnailLoadJob::appendItem(const KFileItem& item) {
446
if (!mItems.contains(item)) {
452
void ThumbnailLoadJob::removeItems(const KFileItemList& itemList) {
453
Q_FOREACH(const KFileItem& item, itemList) {
454
// If we are removing the next item, update to be the item after or the
455
// first if we removed the last item
456
mItems.removeAll( item );
458
if (item == mCurrentItem) {
459
// Abort current item
460
mCurrentItem = KFileItem();
462
KJob* job = subjobs().first();
469
// No more current item, carry on to the next remaining item
470
if (mCurrentItem.isNull()) {
476
void ThumbnailLoadJob::determineNextIcon() {
478
mState = STATE_NEXTTHUMB;
481
if (mItems.isEmpty()) {
483
LOG("emitting result");
489
mCurrentItem = mItems.first();
491
LOG("mCurrentItem.url=" << mCurrentItem.url());
493
// First, stat the orig file
494
mState = STATE_STATORIG;
495
mCurrentUrl = mCurrentItem.url();
496
mCurrentUrl.cleanPath();
498
// Do direct stat instead of using KIO if the file is local (faster)
499
bool directStatOk = false;
500
if (UrlUtils::urlIsFastLocalFile(mCurrentUrl)) {
501
KDE_struct_stat buff;
502
if ( KDE::stat( mCurrentUrl.toLocalFile(), &buff ) == 0 ) {
504
mOriginalTime = buff.st_mtime;
505
QTimer::singleShot( 0, this, SLOT( checkThumbnail()));
509
KIO::Job* job = KIO::stat(mCurrentUrl, KIO::HideProgressInfo);
510
job->ui()->setWindow(KApplication::kApplication()->activeWindow());
511
LOG( "KIO::stat orig" << mCurrentUrl.url() );
514
LOG("/determineNextIcon" << this);
518
void ThumbnailLoadJob::slotResult(KJob * job) {
521
Q_ASSERT(subjobs().isEmpty()); // We should have only one job at a time
524
case STATE_NEXTTHUMB:
529
case STATE_STATORIG: {
530
// Could not stat original, drop this one and move on to the next one
532
emitThumbnailLoadingFailed();
537
// Get modification time of the original file
538
KIO::UDSEntry entry = static_cast<KIO::StatJob*>(job)->statResult();
539
mOriginalTime = entry.numberValue(KIO::UDSEntry::UDS_MODIFICATION_TIME, -1);
544
case STATE_DOWNLOADORIG:
546
emitThumbnailLoadingFailed();
547
LOG("Delete temp file" << mTempPath);
548
QFile::remove(mTempPath);
552
startCreatingThumbnail(mTempPath);
556
case STATE_PREVIEWJOB:
563
void ThumbnailLoadJob::thumbnailReady( const QImage& _img, const QSize& _size) {
566
if ( !img.isNull()) {
567
emitThumbnailLoaded(img, size);
569
emitThumbnailLoadingFailed();
571
if( !mTempPath.isEmpty()) {
572
LOG("Delete temp file" << mTempPath);
573
QFile::remove(mTempPath);
579
QImage ThumbnailLoadJob::loadThumbnailFromCache() const {
580
QImage image = sThumbnailCache->value(mThumbnailPath);
581
if (!image.isNull()) {
584
return QImage(mThumbnailPath);
587
void ThumbnailLoadJob::checkThumbnail() {
588
// If we are in the thumbnail dir, just load the file
589
if (mCurrentUrl.isLocalFile()
590
&& mCurrentUrl.directory().startsWith(thumbnailBaseDir()) )
592
QImage image(mCurrentUrl.toLocalFile());
593
emitThumbnailLoaded(image, image.size());
599
mOriginalUri=generateOriginalUri(mCurrentUrl);
600
mThumbnailPath=generateThumbnailPath(mOriginalUri, mThumbnailGroup);
602
LOG("Stat thumb" << mThumbnailPath);
604
QImage thumb = loadThumbnailFromCache();
605
if (!thumb.isNull()) {
606
if (thumb.text("Thumb::Uri", 0) == mOriginalUri &&
607
thumb.text("Thumb::MTime", 0).toInt() == mOriginalTime )
609
int width=0, height=0;
613
width=thumb.text("Thumb::Image::Width", 0).toInt(&ok);
614
if (ok) height=thumb.text("Thumb::Image::Height", 0).toInt(&ok);
616
size=QSize(width, height);
618
LOG("Thumbnail for" << mOriginalUri << "does not contain correct image size information");
619
KFileMetaInfo fmi(mCurrentUrl);
621
KFileMetaInfoItem item=fmi.item("Dimensions");
622
if (item.isValid()) {
623
size=item.value().toSize();
625
LOG("KFileMetaInfoItem for" << mOriginalUri << "did not get image size information");
628
LOG("Could not get a valid KFileMetaInfo instance for" << mOriginalUri);
631
emitThumbnailLoaded(thumb, size);
637
// Thumbnail not found or not valid
638
if (MimeTypeUtils::fileItemKind(mCurrentItem) == MimeTypeUtils::KIND_RASTER_IMAGE) {
639
if (mCurrentUrl.isLocalFile()) {
640
// Original is a local file, create the thumbnail
641
startCreatingThumbnail(mCurrentUrl.toLocalFile());
643
// Original is remote, download it
644
mState=STATE_DOWNLOADORIG;
646
KTemporaryFile tempFile;
647
tempFile.setAutoRemove(false);
648
if (!tempFile.open()) {
649
kWarning() << "Couldn't create temp file to download " << mCurrentUrl.prettyUrl();
650
emitThumbnailLoadingFailed();
654
mTempPath = tempFile.fileName();
657
url.setPath(mTempPath);
658
KIO::Job* job=KIO::file_copy(mCurrentUrl, url,-1, KIO::Overwrite | KIO::HideProgressInfo);
659
job->ui()->setWindow(KApplication::kApplication()->activeWindow());
660
LOG("Download remote file" << mCurrentUrl.prettyUrl() << "to" << url.pathOrUrl());
664
// Not a raster image, use a KPreviewJob
665
LOG("Starting a KPreviewJob for" << mCurrentItem.url());
666
mState=STATE_PREVIEWJOB;
668
list.append(mCurrentItem);
669
const int pixelSize = ThumbnailGroup::pixelSize(mThumbnailGroup);
670
KIO::Job* job = KIO::filePreview(list, QSize(pixelSize, pixelSize));
671
//job->ui()->setWindow(KApplication::kApplication()->activeWindow());
672
connect(job, SIGNAL(gotPreview(const KFileItem&, const QPixmap&)),
673
this, SLOT(slotGotPreview(const KFileItem&, const QPixmap&)) );
674
connect(job, SIGNAL(failed(const KFileItem&)),
675
this, SLOT(emitThumbnailLoadingFailed()) );
680
void ThumbnailLoadJob::startCreatingThumbnail(const QString& pixPath) {
681
LOG("Creating thumbnail from" << pixPath);
682
mThumbnailThread.load( mOriginalUri, mOriginalTime, mCurrentItem.size(),
683
mCurrentItem.mimetype(), pixPath, mThumbnailPath, mThumbnailGroup);
687
void ThumbnailLoadJob::slotGotPreview(const KFileItem& item, const QPixmap& pixmap) {
688
LOG(mCurrentItem.url());
690
emit thumbnailLoaded(item, pixmap, size);
694
void ThumbnailLoadJob::emitThumbnailLoaded(const QImage& img, const QSize& size) {
695
LOG(mCurrentItem.url());
696
QPixmap thumb = QPixmap::fromImage(img);
697
emit thumbnailLoaded(mCurrentItem, thumb, size);
701
void ThumbnailLoadJob::emitThumbnailLoadingFailed() {
702
LOG(mCurrentItem.url());
703
emit thumbnailLoadingFailed(mCurrentItem);
707
bool ThumbnailLoadJob::isPendingThumbnailCacheEmpty() {
708
return sThumbnailCache->isEmpty();
451
void ThumbnailLoadJob::appendItem(const KFileItem& item)
453
if (!mItems.contains(item)) {
458
void ThumbnailLoadJob::removeItems(const KFileItemList& itemList)
460
Q_FOREACH(const KFileItem & item, itemList) {
461
// If we are removing the next item, update to be the item after or the
462
// first if we removed the last item
463
mItems.removeAll(item);
465
if (item == mCurrentItem) {
466
// Abort current item
467
mCurrentItem = KFileItem();
469
KJob* job = subjobs().first();
476
// No more current item, carry on to the next remaining item
477
if (mCurrentItem.isNull()) {
482
void ThumbnailLoadJob::determineNextIcon()
485
mState = STATE_NEXTTHUMB;
488
if (mItems.isEmpty()) {
490
LOG("emitting result");
496
mCurrentItem = mItems.first();
498
LOG("mCurrentItem.url=" << mCurrentItem.url());
500
// First, stat the orig file
501
mState = STATE_STATORIG;
502
mCurrentUrl = mCurrentItem.url();
503
mCurrentUrl.cleanPath();
505
// Do direct stat instead of using KIO if the file is local (faster)
506
bool directStatOk = false;
507
if (UrlUtils::urlIsFastLocalFile(mCurrentUrl)) {
508
KDE_struct_stat buff;
509
if (KDE::stat(mCurrentUrl.toLocalFile(), &buff) == 0) {
511
mOriginalTime = buff.st_mtime;
512
QTimer::singleShot(0, this, SLOT(checkThumbnail()));
516
KIO::Job* job = KIO::stat(mCurrentUrl, KIO::HideProgressInfo);
517
job->ui()->setWindow(KApplication::kApplication()->activeWindow());
518
LOG("KIO::stat orig" << mCurrentUrl.url());
521
LOG("/determineNextIcon" << this);
524
void ThumbnailLoadJob::slotResult(KJob * job)
528
Q_ASSERT(subjobs().isEmpty()); // We should have only one job at a time
531
case STATE_NEXTTHUMB:
536
case STATE_STATORIG: {
537
// Could not stat original, drop this one and move on to the next one
539
emitThumbnailLoadingFailed();
544
// Get modification time of the original file
545
KIO::UDSEntry entry = static_cast<KIO::StatJob*>(job)->statResult();
546
mOriginalTime = entry.numberValue(KIO::UDSEntry::UDS_MODIFICATION_TIME, -1);
551
case STATE_DOWNLOADORIG:
553
emitThumbnailLoadingFailed();
554
LOG("Delete temp file" << mTempPath);
555
QFile::remove(mTempPath);
559
startCreatingThumbnail(mTempPath);
563
case STATE_PREVIEWJOB:
569
void ThumbnailLoadJob::thumbnailReady(const QImage& _img, const QSize& _size)
574
emitThumbnailLoaded(img, size);
576
emitThumbnailLoadingFailed();
578
if (!mTempPath.isEmpty()) {
579
LOG("Delete temp file" << mTempPath);
580
QFile::remove(mTempPath);
586
QImage ThumbnailLoadJob::loadThumbnailFromCache() const
588
QImage image = sThumbnailCache->value(mThumbnailPath);
589
if (!image.isNull()) {
592
return QImage(mThumbnailPath);
595
void ThumbnailLoadJob::checkThumbnail()
597
// If we are in the thumbnail dir, just load the file
598
if (mCurrentUrl.isLocalFile()
599
&& mCurrentUrl.directory().startsWith(thumbnailBaseDir())) {
600
QImage image(mCurrentUrl.toLocalFile());
601
emitThumbnailLoaded(image, image.size());
607
mOriginalUri = generateOriginalUri(mCurrentUrl);
608
mThumbnailPath = generateThumbnailPath(mOriginalUri, mThumbnailGroup);
610
LOG("Stat thumb" << mThumbnailPath);
612
QImage thumb = loadThumbnailFromCache();
613
if (!thumb.isNull()) {
614
if (thumb.text("Thumb::Uri", 0) == mOriginalUri &&
615
thumb.text("Thumb::MTime", 0).toInt() == mOriginalTime) {
616
int width = 0, height = 0;
620
width = thumb.text("Thumb::Image::Width", 0).toInt(&ok);
621
if (ok) height = thumb.text("Thumb::Image::Height", 0).toInt(&ok);
623
size = QSize(width, height);
625
LOG("Thumbnail for" << mOriginalUri << "does not contain correct image size information");
626
KFileMetaInfo fmi(mCurrentUrl);
628
KFileMetaInfoItem item = fmi.item("Dimensions");
629
if (item.isValid()) {
630
size = item.value().toSize();
632
LOG("KFileMetaInfoItem for" << mOriginalUri << "did not get image size information");
635
LOG("Could not get a valid KFileMetaInfo instance for" << mOriginalUri);
638
emitThumbnailLoaded(thumb, size);
644
// Thumbnail not found or not valid
645
if (MimeTypeUtils::fileItemKind(mCurrentItem) == MimeTypeUtils::KIND_RASTER_IMAGE) {
646
if (mCurrentUrl.isLocalFile()) {
647
// Original is a local file, create the thumbnail
648
startCreatingThumbnail(mCurrentUrl.toLocalFile());
650
// Original is remote, download it
651
mState = STATE_DOWNLOADORIG;
653
KTemporaryFile tempFile;
654
tempFile.setAutoRemove(false);
655
if (!tempFile.open()) {
656
kWarning() << "Couldn't create temp file to download " << mCurrentUrl.prettyUrl();
657
emitThumbnailLoadingFailed();
661
mTempPath = tempFile.fileName();
664
url.setPath(mTempPath);
665
KIO::Job* job = KIO::file_copy(mCurrentUrl, url, -1, KIO::Overwrite | KIO::HideProgressInfo);
666
job->ui()->setWindow(KApplication::kApplication()->activeWindow());
667
LOG("Download remote file" << mCurrentUrl.prettyUrl() << "to" << url.pathOrUrl());
671
// Not a raster image, use a KPreviewJob
672
LOG("Starting a KPreviewJob for" << mCurrentItem.url());
673
mState = STATE_PREVIEWJOB;
675
list.append(mCurrentItem);
676
const int pixelSize = ThumbnailGroup::pixelSize(mThumbnailGroup);
677
if (mPreviewPlugins.isEmpty()) {
678
mPreviewPlugins = KIO::PreviewJob::availablePlugins();
680
KIO::Job* job = KIO::filePreview(list, QSize(pixelSize, pixelSize), &mPreviewPlugins);
681
//job->ui()->setWindow(KApplication::kApplication()->activeWindow());
682
connect(job, SIGNAL(gotPreview(KFileItem, QPixmap)),
683
this, SLOT(slotGotPreview(KFileItem, QPixmap)));
684
connect(job, SIGNAL(failed(KFileItem)),
685
this, SLOT(emitThumbnailLoadingFailed()));
690
void ThumbnailLoadJob::startCreatingThumbnail(const QString& pixPath)
692
LOG("Creating thumbnail from" << pixPath);
693
mThumbnailThread.load(mOriginalUri, mOriginalTime, mCurrentItem.size(),
694
mCurrentItem.mimetype(), pixPath, mThumbnailPath, mThumbnailGroup);
697
void ThumbnailLoadJob::slotGotPreview(const KFileItem& item, const QPixmap& pixmap)
699
LOG(mCurrentItem.url());
701
emit thumbnailLoaded(item, pixmap, size);
704
void ThumbnailLoadJob::emitThumbnailLoaded(const QImage& img, const QSize& size)
706
LOG(mCurrentItem.url());
707
QPixmap thumb = QPixmap::fromImage(img);
708
emit thumbnailLoaded(mCurrentItem, thumb, size);
711
void ThumbnailLoadJob::emitThumbnailLoadingFailed()
713
LOG(mCurrentItem.url());
714
emit thumbnailLoadingFailed(mCurrentItem);
717
bool ThumbnailLoadJob::isPendingThumbnailCacheEmpty()
719
return sThumbnailCache->isEmpty();