~ubuntu-branches/ubuntu/trusty/gwenview/trusty

« back to all changes in this revision

Viewing changes to lib/thumbnailloadjob.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Rohan Garg
  • Date: 2011-07-20 13:46:34 UTC
  • Revision ID: james.westby@ubuntu.com-20110720134634-92930fdjeed4gdc9
Tags: upstream-4.6.90+repack
ImportĀ upstreamĀ versionĀ 4.6.90+repack

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// vim: set tabstop=4 shiftwidth=4 noexpandtab:
 
2
/*      Gwenview - A simple image viewer for KDE
 
3
        Copyright 2000-2007 AurĆ©lien GĆ¢teau <agateau@kde.org>
 
4
        This class is based on the ImagePreviewJob class from Konqueror.
 
5
*/
 
6
/*      This file is part of the KDE project
 
7
        Copyright (C) 2000 David Faure <faure@kde.org>
 
8
                                  2000 Carsten Pfeiffer <pfeiffer@kde.org>
 
9
 
 
10
        This program is free software; you can redistribute it and/or modify
 
11
        it under the terms of the GNU General Public License as published by
 
12
        the Free Software Foundation; either version 2 of the License, or
 
13
        (at your option) any later version.
 
14
 
 
15
        This program is distributed in the hope that it will be useful,
 
16
        but WITHOUT ANY WARRANTY; without even the implied warranty of
 
17
        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
18
        GNU General Public License for more details.
 
19
 
 
20
        You should have received a copy of the GNU General Public License
 
21
        along with this program; if not, write to the Free Software
 
22
        Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 
23
*/
 
24
#include "thumbnailloadjob.moc"
 
25
 
 
26
#include <sys/types.h>
 
27
#include <sys/stat.h>
 
28
#include <unistd.h>
 
29
#include <assert.h>
 
30
 
 
31
// Qt
 
32
#include <QDir>
 
33
#include <QFile>
 
34
#include <QImage>
 
35
#include <QImageReader>
 
36
#include <QMatrix>
 
37
#include <QPixmap>
 
38
#include <QTimer>
 
39
 
 
40
// KDE
 
41
#include <kapplication.h>
 
42
#include <kcodecs.h>
 
43
#include <kdebug.h>
 
44
#include <kde_file.h>
 
45
#include <kfileitem.h>
 
46
#include <kio/jobuidelegate.h>
 
47
#include <kio/previewjob.h>
 
48
#include <kstandarddirs.h>
 
49
#include <ktemporaryfile.h>
 
50
 
 
51
// Local
 
52
#include "mimetypeutils.h"
 
53
#include "jpegcontent.h"
 
54
#include "imageutils.h"
 
55
#include "urlutils.h"
 
56
 
 
57
namespace Gwenview {
 
58
 
 
59
#undef ENABLE_LOG
 
60
#undef LOG
 
61
//#define ENABLE_LOG
 
62
#ifdef ENABLE_LOG
 
63
#define LOG(x) kDebug() << x
 
64
#else
 
65
#define LOG(x) ;
 
66
#endif
 
67
 
 
68
 
 
69
static QString generateOriginalUri(const KUrl& url_) {
 
70
        KUrl url = url_;
 
71
        // Don't include the password if any
 
72
        url.setPass(QString::null);     //krazy:exclude=nullstrassign for old broken gcc
 
73
        return url.url();
 
74
}
 
75
 
 
76
 
 
77
static QString generateThumbnailPath(const QString& uri, ThumbnailGroup::Enum group) {
 
78
        KMD5 md5( QFile::encodeName(uri) );
 
79
        QString baseDir=ThumbnailLoadJob::thumbnailBaseDir(group);
 
80
        return baseDir + QString(QFile::encodeName( md5.hexDigest())) + ".png";
 
81
}
 
82
 
 
83
//------------------------------------------------------------------------
 
84
//
 
85
// ThumbnailCache
 
86
//
 
87
//------------------------------------------------------------------------
 
88
K_GLOBAL_STATIC(ThumbnailCache, sThumbnailCache)
 
89
 
 
90
 
 
91
static void storeThumbnailToDiskCache(const QString& path, const QImage& image) {
 
92
        LOG(path);
 
93
        KTemporaryFile tmp;
 
94
        tmp.setPrefix(path + ".gwenview.tmp");
 
95
        tmp.setSuffix(".png");
 
96
        if (!tmp.open()) {
 
97
                kWarning() << "Could not create a temporary file.";
 
98
                return;
 
99
        }
 
100
 
 
101
        if (!image.save(tmp.fileName(), "png")) {
 
102
                kWarning() << "Could not save thumbnail";
 
103
                return;
 
104
        }
 
105
 
 
106
        KDE_rename(QFile::encodeName(tmp.fileName()), QFile::encodeName(path));
 
107
}
 
108
 
 
109
 
 
110
void ThumbnailCache::queueThumbnail(const QString& path, const QImage& image) {
 
111
        LOG(path);
 
112
        QMutexLocker locker(&mMutex);
 
113
        mCache.insert(path, image);
 
114
}
 
115
 
 
116
void ThumbnailCache::run() {
 
117
        QMutexLocker locker(&mMutex);
 
118
        while (!mCache.isEmpty()) {
 
119
                Cache::ConstIterator it = mCache.constBegin();
 
120
                const QString path = it.key();
 
121
                const QImage image = it.value();
 
122
 
 
123
                // This part of the thread is the most time consuming but it does not
 
124
                // depend on mCache so we can unlock here. This way other thumbnails
 
125
                // can be added or queried
 
126
                locker.unlock();
 
127
                storeThumbnailToDiskCache(path, image);
 
128
                locker.relock();
 
129
 
 
130
                mCache.remove(path);
 
131
        }
 
132
}
 
133
 
 
134
QImage ThumbnailCache::value(const QString& path) const {
 
135
        QMutexLocker locker(&mMutex);
 
136
        return mCache.value(path);
 
137
}
 
138
 
 
139
bool ThumbnailCache::isEmpty() const {
 
140
        QMutexLocker locker(&mMutex);
 
141
        return mCache.isEmpty();
 
142
}
 
143
 
 
144
 
 
145
//------------------------------------------------------------------------
 
146
//
 
147
// ThumbnailThread
 
148
//
 
149
//------------------------------------------------------------------------
 
150
ThumbnailThread::ThumbnailThread()
 
151
: mCancel(false) {}
 
152
 
 
153
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)
 
158
{
 
159
        QMutexLocker lock( &mMutex );
 
160
        assert( mPixPath.isNull());
 
161
 
 
162
        mOriginalUri = originalUri;
 
163
        mOriginalTime = originalTime;
 
164
        mOriginalSize = originalSize;
 
165
        mOriginalMimeType = originalMimeType;
 
166
        mPixPath = pixPath;
 
167
        mThumbnailPath = thumbnailPath;
 
168
        mThumbnailGroup = group;
 
169
        if(!isRunning()) start();
 
170
        mCond.wakeOne();
 
171
}
 
172
 
 
173
bool ThumbnailThread::testCancel() {
 
174
        QMutexLocker lock( &mMutex );
 
175
        return mCancel;
 
176
}
 
177
 
 
178
void ThumbnailThread::cancel() {
 
179
        QMutexLocker lock( &mMutex );
 
180
        mCancel = true;
 
181
        mCond.wakeOne();
 
182
}
 
183
 
 
184
void ThumbnailThread::run() {
 
185
        LOG("");
 
186
        while( !testCancel()) {
 
187
                {
 
188
                        QMutexLocker lock(&mMutex);
 
189
                        // empty mPixPath means nothing to do
 
190
                        LOG("Waiting for mPixPath");
 
191
                        if (mPixPath.isNull()) {
 
192
                                LOG("mPixPath.isNull");
 
193
                                mCond.wait(&mMutex);
 
194
                        }
 
195
                }
 
196
                if(testCancel()) {
 
197
                        return;
 
198
                }
 
199
                {
 
200
                        QMutexLocker lock(&mMutex);
 
201
                        Q_ASSERT(!mPixPath.isNull());
 
202
                        LOG("Loading" << mPixPath);
 
203
                        bool needCaching;
 
204
                        bool ok = loadThumbnail(&needCaching);
 
205
                        if (ok && needCaching) {
 
206
                                cacheThumbnail();
 
207
                        }
 
208
                        mPixPath.clear(); // done, ready for next
 
209
                }
 
210
                if(testCancel()) {
 
211
                        return;
 
212
                }
 
213
                {
 
214
                        QSize size(mOriginalWidth, mOriginalHeight);
 
215
                        LOG("emitting done signal, size=" << size);
 
216
                        QMutexLocker lock(&mMutex);
 
217
                        done(mImage, size);
 
218
                        LOG("Done");
 
219
                }
 
220
        }
 
221
        LOG("Ending thread");
 
222
}
 
223
 
 
224
bool ThumbnailThread::loadThumbnail(bool* needCaching) {
 
225
        mImage = QImage();
 
226
        *needCaching = true;
 
227
        int pixelSize = ThumbnailGroup::pixelSize(mThumbnailGroup);
 
228
        Orientation orientation = NORMAL;
 
229
 
 
230
        QImageReader reader(mPixPath);
 
231
        // If it's a Jpeg, try to load an embedded thumbnail, if available
 
232
        if (reader.format() == "jpeg") {
 
233
                JpegContent content;
 
234
                content.load(mPixPath);
 
235
                QImage thumbnail = content.thumbnail();
 
236
                orientation = content.orientation();
 
237
 
 
238
                if (qMax(thumbnail.width(), thumbnail.height()) >= pixelSize) {
 
239
                        mImage = thumbnail;
 
240
                        if (orientation != NORMAL && orientation != NOT_AVAILABLE) {
 
241
                                QMatrix matrix = ImageUtils::transformMatrix(orientation);
 
242
                                mImage = mImage.transformed(matrix);
 
243
                        }
 
244
                        mOriginalWidth = content.size().width();
 
245
                        mOriginalHeight = content.size().height();
 
246
                        return true;
 
247
                }
 
248
        }
 
249
 
 
250
        // Generate thumbnail from full image
 
251
        QSize originalSize = reader.size();
 
252
        if (originalSize.isValid() && reader.supportsOption(QImageIOHandler::ScaledSize)) {
 
253
                int scale;
 
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);
 
259
                }
 
260
        }
 
261
 
 
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;
 
267
                return false;
 
268
        }
 
269
        if (!originalSize.isValid()) {
 
270
                originalSize = originalImage.size();
 
271
        }
 
272
        mOriginalWidth = originalSize.width();
 
273
        mOriginalHeight = originalSize.height();
 
274
 
 
275
        if (qMax(mOriginalWidth, mOriginalHeight)<=pixelSize) {
 
276
                mImage = originalImage;
 
277
                *needCaching = format != "png";
 
278
        } else {
 
279
                mImage = originalImage.scaled(pixelSize, pixelSize, Qt::KeepAspectRatio);
 
280
        }
 
281
 
 
282
        // Rotate if necessary
 
283
        if (orientation != NORMAL && orientation != NOT_AVAILABLE) {
 
284
                QMatrix matrix = ImageUtils::transformMatrix(orientation);
 
285
                mImage = mImage.transformed(matrix);
 
286
 
 
287
                switch (orientation) {
 
288
                case TRANSPOSE:
 
289
                case ROT_90:
 
290
                case TRANSVERSE:
 
291
                case ROT_270:
 
292
                        qSwap(mOriginalWidth, mOriginalHeight);
 
293
                        break;
 
294
                default:
 
295
                        break;
 
296
                }
 
297
        }
 
298
        return true;
 
299
}
 
300
 
 
301
 
 
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");
 
310
 
 
311
        emit thumbnailReadyToBeCached(mThumbnailPath, mImage);
 
312
}
 
313
 
 
314
 
 
315
//------------------------------------------------------------------------
 
316
//
 
317
// ThumbnailLoadJob static methods
 
318
//
 
319
//------------------------------------------------------------------------
 
320
static QString sThumbnailBaseDir;
 
321
QString ThumbnailLoadJob::thumbnailBaseDir() {
 
322
        if (sThumbnailBaseDir.isEmpty()) {
 
323
                sThumbnailBaseDir = QDir::homePath() + "/.thumbnails/";
 
324
        }
 
325
        return sThumbnailBaseDir;
 
326
}
 
327
 
 
328
 
 
329
void ThumbnailLoadJob::setThumbnailBaseDir(const QString& dir) {
 
330
        sThumbnailBaseDir = dir;
 
331
}
 
332
 
 
333
 
 
334
QString ThumbnailLoadJob::thumbnailBaseDir(ThumbnailGroup::Enum group) {
 
335
        QString dir = thumbnailBaseDir();
 
336
        switch (group) {
 
337
        case ThumbnailGroup::Normal:
 
338
                dir += "normal/";
 
339
                break;
 
340
        case ThumbnailGroup::Large:
 
341
                dir += "large/";
 
342
                break;
 
343
        }
 
344
        return dir;
 
345
}
 
346
 
 
347
 
 
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));
 
352
}
 
353
 
 
354
 
 
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);
 
358
        QImage thumb;
 
359
        if (!thumb.load(oldPath)) {
 
360
                return;
 
361
        }
 
362
        thumb.setText("Thumb::Uri", 0, newUri);
 
363
        thumb.save(newPath, "png");
 
364
        QFile::remove(QFile::encodeName(oldPath));
 
365
}
 
366
 
 
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);
 
372
}
 
373
 
 
374
 
 
375
//------------------------------------------------------------------------
 
376
//
 
377
// ThumbnailLoadJob implementation
 
378
//
 
379
//------------------------------------------------------------------------
 
380
ThumbnailLoadJob::ThumbnailLoadJob(const KFileItemList& items, ThumbnailGroup::Enum group)
 
381
: KIO::Job()
 
382
, mState( STATE_NEXTTHUMB )
 
383
, mThumbnailGroup(group)
 
384
{
 
385
        LOG(this);
 
386
 
 
387
        // Make sure we have a place to store our thumbnails
 
388
        QString thumbnailDir = ThumbnailLoadJob::thumbnailBaseDir(mThumbnailGroup);
 
389
        KStandardDirs::makeDir(thumbnailDir, 0700);
 
390
 
 
391
        // Look for images and store the items in our todo list
 
392
        Q_ASSERT(!items.empty());
 
393
        mItems = items;
 
394
        mCurrentItem = KFileItem();
 
395
 
 
396
        connect(&mThumbnailThread, SIGNAL(done(const QImage&, const QSize&)),
 
397
                SLOT(thumbnailReady(const QImage&, const QSize&)),
 
398
                Qt::QueuedConnection);
 
399
 
 
400
        connect(&mThumbnailThread, SIGNAL(thumbnailReadyToBeCached(const QString&, const QImage&)),
 
401
                sThumbnailCache, SLOT(queueThumbnail(const QString&, const QImage&)),
 
402
                Qt::QueuedConnection);
 
403
}
 
404
 
 
405
 
 
406
ThumbnailLoadJob::~ThumbnailLoadJob() {
 
407
        LOG(this);
 
408
        if (hasSubjobs()) {
 
409
                LOG("Killing subjob");
 
410
                KJob* job = subjobs().first();
 
411
                job->kill();
 
412
                removeSubjob(job);
 
413
        }
 
414
        mThumbnailThread.cancel();
 
415
        mThumbnailThread.wait();
 
416
        if (!sThumbnailCache->isRunning()) {
 
417
                sThumbnailCache->start();
 
418
        }
 
419
}
 
420
 
 
421
 
 
422
void ThumbnailLoadJob::start() {
 
423
        if (mItems.isEmpty()) {
 
424
                LOG("Nothing to do");
 
425
                emitResult();
 
426
                delete this;
 
427
                return;
 
428
        }
 
429
 
 
430
        determineNextIcon();
 
431
}
 
432
 
 
433
 
 
434
const KFileItemList& ThumbnailLoadJob::pendingItems() const {
 
435
        return mItems;
 
436
}
 
437
 
 
438
 
 
439
void ThumbnailLoadJob::setThumbnailGroup(ThumbnailGroup::Enum group) {
 
440
        mThumbnailGroup = group;
 
441
}
 
442
 
 
443
 
 
444
//-Internal--------------------------------------------------------------
 
445
void ThumbnailLoadJob::appendItem(const KFileItem& item) {
 
446
        if (!mItems.contains(item)) {
 
447
                mItems.append(item);
 
448
        }
 
449
}
 
450
 
 
451
 
 
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 );
 
457
 
 
458
                if (item == mCurrentItem) {
 
459
                        // Abort current item
 
460
                        mCurrentItem = KFileItem();
 
461
                        if (hasSubjobs()) {
 
462
                                KJob* job = subjobs().first();
 
463
                                job->kill();
 
464
                                removeSubjob(job);
 
465
                        }
 
466
                }
 
467
        }
 
468
 
 
469
        // No more current item, carry on to the next remaining item
 
470
        if (mCurrentItem.isNull()) {
 
471
                determineNextIcon();
 
472
        }
 
473
}
 
474
 
 
475
 
 
476
void ThumbnailLoadJob::determineNextIcon() {
 
477
        LOG(this);
 
478
        mState = STATE_NEXTTHUMB;
 
479
 
 
480
        // No more items ?
 
481
        if (mItems.isEmpty()) {
 
482
                // Done
 
483
                LOG("emitting result");
 
484
                emitResult();
 
485
                delete this;
 
486
                return;
 
487
        }
 
488
 
 
489
        mCurrentItem = mItems.first();
 
490
        mItems.pop_front();
 
491
        LOG("mCurrentItem.url=" << mCurrentItem.url());
 
492
 
 
493
        // First, stat the orig file
 
494
        mState = STATE_STATORIG;
 
495
        mCurrentUrl = mCurrentItem.url();
 
496
        mCurrentUrl.cleanPath();
 
497
 
 
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 )  {
 
503
                        directStatOk = true;
 
504
                        mOriginalTime = buff.st_mtime;
 
505
                        QTimer::singleShot( 0, this, SLOT( checkThumbnail()));
 
506
                }
 
507
        }
 
508
        if (!directStatOk) {
 
509
                KIO::Job* job = KIO::stat(mCurrentUrl, KIO::HideProgressInfo);
 
510
                job->ui()->setWindow(KApplication::kApplication()->activeWindow());
 
511
                LOG( "KIO::stat orig" << mCurrentUrl.url() );
 
512
                addSubjob(job);
 
513
        }
 
514
        LOG("/determineNextIcon" << this);
 
515
}
 
516
 
 
517
 
 
518
void ThumbnailLoadJob::slotResult(KJob * job) {
 
519
        LOG(mState);
 
520
        removeSubjob(job);
 
521
        Q_ASSERT(subjobs().isEmpty()); // We should have only one job at a time
 
522
 
 
523
        switch (mState) {
 
524
        case STATE_NEXTTHUMB:
 
525
                Q_ASSERT(false);
 
526
                determineNextIcon();
 
527
                return;
 
528
 
 
529
        case STATE_STATORIG: {
 
530
                // Could not stat original, drop this one and move on to the next one
 
531
                if (job->error()) {
 
532
                        emitThumbnailLoadingFailed();
 
533
                        determineNextIcon();
 
534
                        return;
 
535
                }
 
536
 
 
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);
 
540
                checkThumbnail();
 
541
                return;
 
542
        }
 
543
 
 
544
        case STATE_DOWNLOADORIG:
 
545
                if (job->error()) {
 
546
                        emitThumbnailLoadingFailed();
 
547
                        LOG("Delete temp file" << mTempPath);
 
548
                        QFile::remove(mTempPath);
 
549
                        mTempPath.clear();
 
550
                        determineNextIcon();
 
551
                } else {
 
552
                        startCreatingThumbnail(mTempPath);
 
553
                }
 
554
                return;
 
555
 
 
556
        case STATE_PREVIEWJOB:
 
557
                determineNextIcon();
 
558
                return;
 
559
        }
 
560
}
 
561
 
 
562
 
 
563
void ThumbnailLoadJob::thumbnailReady( const QImage& _img, const QSize& _size) {
 
564
        QImage img = _img;
 
565
        QSize size = _size;
 
566
        if ( !img.isNull()) {
 
567
                emitThumbnailLoaded(img, size);
 
568
        } else {
 
569
                emitThumbnailLoadingFailed();
 
570
        }
 
571
        if( !mTempPath.isEmpty()) {
 
572
                LOG("Delete temp file" << mTempPath);
 
573
                QFile::remove(mTempPath);
 
574
                mTempPath.clear();
 
575
        }
 
576
        determineNextIcon();
 
577
}
 
578
 
 
579
QImage ThumbnailLoadJob::loadThumbnailFromCache() const {
 
580
        QImage image = sThumbnailCache->value(mThumbnailPath);
 
581
        if (!image.isNull()) {
 
582
                return image;
 
583
        }
 
584
        return QImage(mThumbnailPath);
 
585
}
 
586
 
 
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()) )
 
591
        {
 
592
                QImage image(mCurrentUrl.toLocalFile());
 
593
                emitThumbnailLoaded(image, image.size());
 
594
                determineNextIcon();
 
595
                return;
 
596
        }
 
597
        QSize imagesize;
 
598
 
 
599
        mOriginalUri=generateOriginalUri(mCurrentUrl);
 
600
        mThumbnailPath=generateThumbnailPath(mOriginalUri, mThumbnailGroup);
 
601
 
 
602
        LOG("Stat thumb" << mThumbnailPath);
 
603
 
 
604
        QImage thumb = loadThumbnailFromCache();
 
605
        if (!thumb.isNull()) {
 
606
                if (thumb.text("Thumb::Uri", 0) == mOriginalUri &&
 
607
                        thumb.text("Thumb::MTime", 0).toInt() == mOriginalTime )
 
608
                {
 
609
                        int width=0, height=0;
 
610
                        QSize size;
 
611
                        bool ok;
 
612
 
 
613
                        width=thumb.text("Thumb::Image::Width", 0).toInt(&ok);
 
614
                        if (ok) height=thumb.text("Thumb::Image::Height", 0).toInt(&ok);
 
615
                        if (ok) {
 
616
                                size=QSize(width, height);
 
617
                        } else {
 
618
                                LOG("Thumbnail for" << mOriginalUri << "does not contain correct image size information");
 
619
                                KFileMetaInfo fmi(mCurrentUrl);
 
620
                                if (fmi.isValid()) {
 
621
                                        KFileMetaInfoItem item=fmi.item("Dimensions");
 
622
                                        if (item.isValid()) {
 
623
                                                size=item.value().toSize();
 
624
                                        } else {
 
625
                                                LOG("KFileMetaInfoItem for" << mOriginalUri << "did not get image size information");
 
626
                                        }
 
627
                                } else {
 
628
                                        LOG("Could not get a valid KFileMetaInfo instance for" << mOriginalUri);
 
629
                                }
 
630
                        }
 
631
                        emitThumbnailLoaded(thumb, size);
 
632
                        determineNextIcon();
 
633
                        return;
 
634
                }
 
635
        }
 
636
 
 
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());
 
642
                } else {
 
643
                        // Original is remote, download it
 
644
                        mState=STATE_DOWNLOADORIG;
 
645
                        
 
646
                        KTemporaryFile tempFile;
 
647
                        tempFile.setAutoRemove(false);
 
648
                        if (!tempFile.open()) {
 
649
                                kWarning() << "Couldn't create temp file to download " << mCurrentUrl.prettyUrl();
 
650
                                emitThumbnailLoadingFailed();
 
651
                                determineNextIcon();
 
652
                                return;
 
653
                        }
 
654
                        mTempPath = tempFile.fileName();
 
655
 
 
656
                        KUrl url;
 
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());
 
661
                        addSubjob(job);
 
662
                }
 
663
        } else {
 
664
                // Not a raster image, use a KPreviewJob
 
665
                LOG("Starting a KPreviewJob for" << mCurrentItem.url());
 
666
                mState=STATE_PREVIEWJOB;
 
667
                KFileItemList list;
 
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()) );
 
676
                addSubjob(job);
 
677
        }
 
678
}
 
679
 
 
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);
 
684
}
 
685
 
 
686
 
 
687
void ThumbnailLoadJob::slotGotPreview(const KFileItem& item, const QPixmap& pixmap) {
 
688
        LOG(mCurrentItem.url());
 
689
        QSize size;
 
690
        emit thumbnailLoaded(item, pixmap, size);
 
691
}
 
692
 
 
693
 
 
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);
 
698
}
 
699
 
 
700
 
 
701
void ThumbnailLoadJob::emitThumbnailLoadingFailed() {
 
702
        LOG(mCurrentItem.url());
 
703
        emit thumbnailLoadingFailed(mCurrentItem);
 
704
}
 
705
 
 
706
 
 
707
bool ThumbnailLoadJob::isPendingThumbnailCacheEmpty() {
 
708
        return sThumbnailCache->isEmpty();
 
709
}
 
710
 
 
711
 
 
712
} // namespace