~ubuntu-branches/ubuntu/precise/gwenview/precise-proposed

« back to all changes in this revision

Viewing changes to lib/thumbnailloadjob.cpp

  • Committer: Package Import Robot
  • Author(s): Jonathan Riddell
  • Date: 2011-12-15 14:17:54 UTC
  • mto: This revision was merged to the branch mainline in revision 12.
  • Revision ID: package-import@ubuntu.com-20111215141754-z043hyx69dulbggf
Tags: upstream-4.7.90
ImportĀ upstreamĀ versionĀ 4.7.90

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.
 
1
// vim: set tabstop=4 shiftwidth=4 expandtab:
 
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
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.
 
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
23
*/
24
24
#include "thumbnailloadjob.moc"
25
25
 
54
54
#include "imageutils.h"
55
55
#include "urlutils.h"
56
56
 
57
 
namespace Gwenview {
 
57
namespace Gwenview
 
58
{
58
59
 
59
60
#undef ENABLE_LOG
60
61
#undef LOG
65
66
#define LOG(x) ;
66
67
#endif
67
68
 
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();
 
69
static QString generateOriginalUri(const KUrl& url_)
 
70
{
 
71
    KUrl url = url_;
 
72
    // Don't include the password if any
 
73
    url.setPass(QString::null); //krazy:exclude=nullstrassign for old broken gcc
 
74
    return url.url();
74
75
}
75
76
 
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";
 
77
static QString generateThumbnailPath(const QString& uri, ThumbnailGroup::Enum group)
 
78
{
 
79
    KMD5 md5(QFile::encodeName(uri));
 
80
    QString baseDir = ThumbnailLoadJob::thumbnailBaseDir(group);
 
81
    return baseDir + QString(QFile::encodeName(md5.hexDigest())) + ".png";
81
82
}
82
83
 
83
84
//------------------------------------------------------------------------
87
88
//------------------------------------------------------------------------
88
89
K_GLOBAL_STATIC(ThumbnailCache, sThumbnailCache)
89
90
 
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
 
 
 
91
static void storeThumbnailToDiskCache(const QString& path, const QImage& image)
 
92
{
 
93
    LOG(path);
 
94
    KTemporaryFile tmp;
 
95
    tmp.setPrefix(path + ".gwenview.tmp");
 
96
    tmp.setSuffix(".png");
 
97
    if (!tmp.open()) {
 
98
        kWarning() << "Could not create a temporary file.";
 
99
        return;
 
100
    }
 
101
 
 
102
    if (!image.save(tmp.fileName(), "png")) {
 
103
        kWarning() << "Could not save thumbnail";
 
104
        return;
 
105
    }
 
106
 
 
107
    KDE_rename(QFile::encodeName(tmp.fileName()), QFile::encodeName(path));
 
108
}
 
109
 
 
110
void ThumbnailCache::queueThumbnail(const QString& path, const QImage& image)
 
111
{
 
112
    LOG(path);
 
113
    QMutexLocker locker(&mMutex);
 
114
    mCache.insert(path, image);
 
115
}
 
116
 
 
117
void ThumbnailCache::run()
 
118
{
 
119
    QMutexLocker locker(&mMutex);
 
120
    while (!mCache.isEmpty()) {
 
121
        Cache::ConstIterator it = mCache.constBegin();
 
122
        const QString path = it.key();
 
123
        const QImage image = it.value();
 
124
 
 
125
        // This part of the thread is the most time consuming but it does not
 
126
        // depend on mCache so we can unlock here. This way other thumbnails
 
127
        // can be added or queried
 
128
        locker.unlock();
 
129
        storeThumbnailToDiskCache(path, image);
 
130
        locker.relock();
 
131
 
 
132
        mCache.remove(path);
 
133
    }
 
134
}
 
135
 
 
136
QImage ThumbnailCache::value(const QString& path) const
 
137
{
 
138
    QMutexLocker locker(&mMutex);
 
139
    return mCache.value(path);
 
140
}
 
141
 
 
142
bool ThumbnailCache::isEmpty() const
 
143
{
 
144
    QMutexLocker locker(&mMutex);
 
145
    return mCache.isEmpty();
 
146
}
144
147
 
145
148
//------------------------------------------------------------------------
146
149
//
151
154
: mCancel(false) {}
152
155
 
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)
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
 
 
 
157
    const QString& originalUri, time_t originalTime, int originalSize, const QString& originalMimeType,
 
158
    const QString& pixPath,
 
159
    const QString& thumbnailPath,
 
160
    ThumbnailGroup::Enum group)
 
161
{
 
162
    QMutexLocker lock(&mMutex);
 
163
    assert(mPixPath.isNull());
 
164
 
 
165
    mOriginalUri = originalUri;
 
166
    mOriginalTime = originalTime;
 
167
    mOriginalSize = originalSize;
 
168
    mOriginalMimeType = originalMimeType;
 
169
    mPixPath = pixPath;
 
170
    mThumbnailPath = thumbnailPath;
 
171
    mThumbnailGroup = group;
 
172
    if (!isRunning()) start();
 
173
    mCond.wakeOne();
 
174
}
 
175
 
 
176
bool ThumbnailThread::testCancel()
 
177
{
 
178
    QMutexLocker lock(&mMutex);
 
179
    return mCancel;
 
180
}
 
181
 
 
182
void ThumbnailThread::cancel()
 
183
{
 
184
    QMutexLocker lock(&mMutex);
 
185
    mCancel = true;
 
186
    mCond.wakeOne();
 
187
}
 
188
 
 
189
void ThumbnailThread::run()
 
190
{
 
191
    LOG("");
 
192
    while (!testCancel()) {
 
193
        {
 
194
            QMutexLocker lock(&mMutex);
 
195
            // empty mPixPath means nothing to do
 
196
            LOG("Waiting for mPixPath");
 
197
            if (mPixPath.isNull()) {
 
198
                LOG("mPixPath.isNull");
 
199
                mCond.wait(&mMutex);
 
200
            }
 
201
        }
 
202
        if (testCancel()) {
 
203
            return;
 
204
        }
 
205
        {
 
206
            QMutexLocker lock(&mMutex);
 
207
            Q_ASSERT(!mPixPath.isNull());
 
208
            LOG("Loading" << mPixPath);
 
209
            bool needCaching;
 
210
            bool ok = loadThumbnail(&needCaching);
 
211
            if (ok && needCaching) {
 
212
                cacheThumbnail();
 
213
            }
 
214
            mPixPath.clear(); // done, ready for next
 
215
        }
 
216
        if (testCancel()) {
 
217
            return;
 
218
        }
 
219
        {
 
220
            QSize size(mOriginalWidth, mOriginalHeight);
 
221
            LOG("emitting done signal, size=" << size);
 
222
            QMutexLocker lock(&mMutex);
 
223
            done(mImage, size);
 
224
            LOG("Done");
 
225
        }
 
226
    }
 
227
    LOG("Ending thread");
 
228
}
 
229
 
 
230
bool ThumbnailThread::loadThumbnail(bool* needCaching)
 
231
{
 
232
    mImage = QImage();
 
233
    *needCaching = true;
 
234
    int pixelSize = ThumbnailGroup::pixelSize(mThumbnailGroup);
 
235
    Orientation orientation = NORMAL;
 
236
 
 
237
    QImageReader reader(mPixPath);
 
238
    // If it's a Jpeg, try to load an embedded thumbnail, if available
 
239
    if (reader.format() == "jpeg") {
 
240
        JpegContent content;
 
241
        content.load(mPixPath);
 
242
        QImage thumbnail = content.thumbnail();
 
243
        orientation = content.orientation();
 
244
 
 
245
        if (qMax(thumbnail.width(), thumbnail.height()) >= pixelSize) {
 
246
            mImage = thumbnail;
 
247
            if (orientation != NORMAL && orientation != NOT_AVAILABLE) {
 
248
                QMatrix matrix = ImageUtils::transformMatrix(orientation);
 
249
                mImage = mImage.transformed(matrix);
 
250
            }
 
251
            mOriginalWidth = content.size().width();
 
252
            mOriginalHeight = content.size().height();
 
253
            return true;
 
254
        }
 
255
    }
 
256
 
 
257
    // Generate thumbnail from full image
 
258
    QSize originalSize = reader.size();
 
259
    if (originalSize.isValid() && reader.supportsOption(QImageIOHandler::ScaledSize)) {
 
260
        int scale;
 
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);
 
266
        }
 
267
    }
 
268
 
 
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;
 
274
        return false;
 
275
    }
 
276
    if (!originalSize.isValid()) {
 
277
        originalSize = originalImage.size();
 
278
    }
 
279
    mOriginalWidth = originalSize.width();
 
280
    mOriginalHeight = originalSize.height();
 
281
 
 
282
    if (qMax(mOriginalWidth, mOriginalHeight) <= pixelSize) {
 
283
        mImage = originalImage;
 
284
        *needCaching = format != "png";
 
285
    } else {
 
286
        mImage = originalImage.scaled(pixelSize, pixelSize, Qt::KeepAspectRatio);
 
287
    }
 
288
 
 
289
    // Rotate if necessary
 
290
    if (orientation != NORMAL && orientation != NOT_AVAILABLE) {
 
291
        QMatrix matrix = ImageUtils::transformMatrix(orientation);
 
292
        mImage = mImage.transformed(matrix);
 
293
 
 
294
        switch (orientation) {
 
295
        case TRANSPOSE:
 
296
        case ROT_90:
 
297
        case TRANSVERSE:
 
298
        case ROT_270:
 
299
            qSwap(mOriginalWidth, mOriginalHeight);
 
300
            break;
 
301
        default:
 
302
            break;
 
303
        }
 
304
    }
 
305
    return true;
 
306
}
 
307
 
 
308
void ThumbnailThread::cacheThumbnail()
 
309
{
 
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");
 
317
 
 
318
    emit thumbnailReadyToBeCached(mThumbnailPath, mImage);
 
319
}
314
320
 
315
321
//------------------------------------------------------------------------
316
322
//
318
324
//
319
325
//------------------------------------------------------------------------
320
326
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
 
 
 
327
QString ThumbnailLoadJob::thumbnailBaseDir()
 
328
{
 
329
    if (sThumbnailBaseDir.isEmpty()) {
 
330
        sThumbnailBaseDir = QDir::homePath() + "/.thumbnails/";
 
331
    }
 
332
    return sThumbnailBaseDir;
 
333
}
 
334
 
 
335
void ThumbnailLoadJob::setThumbnailBaseDir(const QString& dir)
 
336
{
 
337
    sThumbnailBaseDir = dir;
 
338
}
 
339
 
 
340
QString ThumbnailLoadJob::thumbnailBaseDir(ThumbnailGroup::Enum group)
 
341
{
 
342
    QString dir = thumbnailBaseDir();
 
343
    switch (group) {
 
344
    case ThumbnailGroup::Normal:
 
345
        dir += "normal/";
 
346
        break;
 
347
    case ThumbnailGroup::Large:
 
348
        dir += "large/";
 
349
        break;
 
350
    }
 
351
    return dir;
 
352
}
 
353
 
 
354
void ThumbnailLoadJob::deleteImageThumbnail(const KUrl& url)
 
355
{
 
356
    QString uri = generateOriginalUri(url);
 
357
    QFile::remove(generateThumbnailPath(uri, ThumbnailGroup::Normal));
 
358
    QFile::remove(generateThumbnailPath(uri, ThumbnailGroup::Large));
 
359
}
 
360
 
 
361
static void moveThumbnailHelper(const QString& oldUri, const QString& newUri, ThumbnailGroup::Enum group)
 
362
{
 
363
    QString oldPath = generateThumbnailPath(oldUri, group);
 
364
    QString newPath = generateThumbnailPath(newUri, group);
 
365
    QImage thumb;
 
366
    if (!thumb.load(oldPath)) {
 
367
        return;
 
368
    }
 
369
    thumb.setText("Thumb::Uri", 0, newUri);
 
370
    thumb.save(newPath, "png");
 
371
    QFile::remove(QFile::encodeName(oldPath));
 
372
}
 
373
 
 
374
void ThumbnailLoadJob::moveThumbnail(const KUrl& oldUrl, const KUrl& newUrl)
 
375
{
 
376
    QString oldUri = generateOriginalUri(oldUrl);
 
377
    QString newUri = generateOriginalUri(newUrl);
 
378
    moveThumbnailHelper(oldUri, newUri, ThumbnailGroup::Normal);
 
379
    moveThumbnailHelper(oldUri, newUri, ThumbnailGroup::Large);
 
380
}
374
381
 
375
382
//------------------------------------------------------------------------
376
383
//
379
386
//------------------------------------------------------------------------
380
387
ThumbnailLoadJob::ThumbnailLoadJob(const KFileItemList& items, ThumbnailGroup::Enum group)
381
388
: KIO::Job()
382
 
, mState( STATE_NEXTTHUMB )
 
389
, mState(STATE_NEXTTHUMB)
383
390
, mThumbnailGroup(group)
384
391
{
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
 
 
 
392
    LOG(this);
 
393
 
 
394
    // Make sure we have a place to store our thumbnails
 
395
    QString thumbnailDir = ThumbnailLoadJob::thumbnailBaseDir(mThumbnailGroup);
 
396
    KStandardDirs::makeDir(thumbnailDir, 0700);
 
397
 
 
398
    // Look for images and store the items in our todo list
 
399
    Q_ASSERT(!items.empty());
 
400
    mItems = items;
 
401
    mCurrentItem = KFileItem();
 
402
 
 
403
    connect(&mThumbnailThread, SIGNAL(done(QImage, QSize)),
 
404
            SLOT(thumbnailReady(QImage, QSize)),
 
405
            Qt::QueuedConnection);
 
406
 
 
407
    connect(&mThumbnailThread, SIGNAL(thumbnailReadyToBeCached(QString, QImage)),
 
408
            sThumbnailCache, SLOT(queueThumbnail(QString, QImage)),
 
409
            Qt::QueuedConnection);
 
410
}
 
411
 
 
412
ThumbnailLoadJob::~ThumbnailLoadJob()
 
413
{
 
414
    LOG(this);
 
415
    if (hasSubjobs()) {
 
416
        LOG("Killing subjob");
 
417
        KJob* job = subjobs().first();
 
418
        job->kill();
 
419
        removeSubjob(job);
 
420
    }
 
421
    mThumbnailThread.cancel();
 
422
    mThumbnailThread.wait();
 
423
    if (!sThumbnailCache->isRunning()) {
 
424
        sThumbnailCache->start();
 
425
    }
 
426
}
 
427
 
 
428
void ThumbnailLoadJob::start()
 
429
{
 
430
    if (mItems.isEmpty()) {
 
431
        LOG("Nothing to do");
 
432
        emitResult();
 
433
        delete this;
 
434
        return;
 
435
    }
 
436
 
 
437
    determineNextIcon();
 
438
}
 
439
 
 
440
const KFileItemList& ThumbnailLoadJob::pendingItems() const
 
441
{
 
442
    return mItems;
 
443
}
 
444
 
 
445
void ThumbnailLoadJob::setThumbnailGroup(ThumbnailGroup::Enum group)
 
446
{
 
447
    mThumbnailGroup = group;
 
448
}
443
449
 
444
450
//-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
 
 
 
451
void ThumbnailLoadJob::appendItem(const KFileItem& item)
 
452
{
 
453
    if (!mItems.contains(item)) {
 
454
        mItems.append(item);
 
455
    }
 
456
}
 
457
 
 
458
void ThumbnailLoadJob::removeItems(const KFileItemList& itemList)
 
459
{
 
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);
 
464
 
 
465
        if (item == mCurrentItem) {
 
466
            // Abort current item
 
467
            mCurrentItem = KFileItem();
 
468
            if (hasSubjobs()) {
 
469
                KJob* job = subjobs().first();
 
470
                job->kill();
 
471
                removeSubjob(job);
 
472
            }
 
473
        }
 
474
    }
 
475
 
 
476
    // No more current item, carry on to the next remaining item
 
477
    if (mCurrentItem.isNull()) {
 
478
        determineNextIcon();
 
479
    }
 
480
}
 
481
 
 
482
void ThumbnailLoadJob::determineNextIcon()
 
483
{
 
484
    LOG(this);
 
485
    mState = STATE_NEXTTHUMB;
 
486
 
 
487
    // No more items ?
 
488
    if (mItems.isEmpty()) {
 
489
        // Done
 
490
        LOG("emitting result");
 
491
        emitResult();
 
492
        delete this;
 
493
        return;
 
494
    }
 
495
 
 
496
    mCurrentItem = mItems.first();
 
497
    mItems.pop_front();
 
498
    LOG("mCurrentItem.url=" << mCurrentItem.url());
 
499
 
 
500
    // First, stat the orig file
 
501
    mState = STATE_STATORIG;
 
502
    mCurrentUrl = mCurrentItem.url();
 
503
    mCurrentUrl.cleanPath();
 
504
 
 
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)  {
 
510
            directStatOk = true;
 
511
            mOriginalTime = buff.st_mtime;
 
512
            QTimer::singleShot(0, this, SLOT(checkThumbnail()));
 
513
        }
 
514
    }
 
515
    if (!directStatOk) {
 
516
        KIO::Job* job = KIO::stat(mCurrentUrl, KIO::HideProgressInfo);
 
517
        job->ui()->setWindow(KApplication::kApplication()->activeWindow());
 
518
        LOG("KIO::stat orig" << mCurrentUrl.url());
 
519
        addSubjob(job);
 
520
    }
 
521
    LOG("/determineNextIcon" << this);
 
522
}
 
523
 
 
524
void ThumbnailLoadJob::slotResult(KJob * job)
 
525
{
 
526
    LOG(mState);
 
527
    removeSubjob(job);
 
528
    Q_ASSERT(subjobs().isEmpty()); // We should have only one job at a time
 
529
 
 
530
    switch (mState) {
 
531
    case STATE_NEXTTHUMB:
 
532
        Q_ASSERT(false);
 
533
        determineNextIcon();
 
534
        return;
 
535
 
 
536
    case STATE_STATORIG: {
 
537
        // Could not stat original, drop this one and move on to the next one
 
538
        if (job->error()) {
 
539
            emitThumbnailLoadingFailed();
 
540
            determineNextIcon();
 
541
            return;
 
542
        }
 
543
 
 
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);
 
547
        checkThumbnail();
 
548
        return;
 
549
    }
 
550
 
 
551
    case STATE_DOWNLOADORIG:
 
552
        if (job->error()) {
 
553
            emitThumbnailLoadingFailed();
 
554
            LOG("Delete temp file" << mTempPath);
 
555
            QFile::remove(mTempPath);
 
556
            mTempPath.clear();
 
557
            determineNextIcon();
 
558
        } else {
 
559
            startCreatingThumbnail(mTempPath);
 
560
        }
 
561
        return;
 
562
 
 
563
    case STATE_PREVIEWJOB:
 
564
        determineNextIcon();
 
565
        return;
 
566
    }
 
567
}
 
568
 
 
569
void ThumbnailLoadJob::thumbnailReady(const QImage& _img, const QSize& _size)
 
570
{
 
571
    QImage img = _img;
 
572
    QSize size = _size;
 
573
    if (!img.isNull()) {
 
574
        emitThumbnailLoaded(img, size);
 
575
    } else {
 
576
        emitThumbnailLoadingFailed();
 
577
    }
 
578
    if (!mTempPath.isEmpty()) {
 
579
        LOG("Delete temp file" << mTempPath);
 
580
        QFile::remove(mTempPath);
 
581
        mTempPath.clear();
 
582
    }
 
583
    determineNextIcon();
 
584
}
 
585
 
 
586
QImage ThumbnailLoadJob::loadThumbnailFromCache() const
 
587
{
 
588
    QImage image = sThumbnailCache->value(mThumbnailPath);
 
589
    if (!image.isNull()) {
 
590
        return image;
 
591
    }
 
592
    return QImage(mThumbnailPath);
 
593
}
 
594
 
 
595
void ThumbnailLoadJob::checkThumbnail()
 
596
{
 
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());
 
602
        determineNextIcon();
 
603
        return;
 
604
    }
 
605
    QSize imagesize;
 
606
 
 
607
    mOriginalUri = generateOriginalUri(mCurrentUrl);
 
608
    mThumbnailPath = generateThumbnailPath(mOriginalUri, mThumbnailGroup);
 
609
 
 
610
    LOG("Stat thumb" << mThumbnailPath);
 
611
 
 
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;
 
617
            QSize size;
 
618
            bool ok;
 
619
 
 
620
            width = thumb.text("Thumb::Image::Width", 0).toInt(&ok);
 
621
            if (ok) height = thumb.text("Thumb::Image::Height", 0).toInt(&ok);
 
622
            if (ok) {
 
623
                size = QSize(width, height);
 
624
            } else {
 
625
                LOG("Thumbnail for" << mOriginalUri << "does not contain correct image size information");
 
626
                KFileMetaInfo fmi(mCurrentUrl);
 
627
                if (fmi.isValid()) {
 
628
                    KFileMetaInfoItem item = fmi.item("Dimensions");
 
629
                    if (item.isValid()) {
 
630
                        size = item.value().toSize();
 
631
                    } else {
 
632
                        LOG("KFileMetaInfoItem for" << mOriginalUri << "did not get image size information");
 
633
                    }
 
634
                } else {
 
635
                    LOG("Could not get a valid KFileMetaInfo instance for" << mOriginalUri);
 
636
                }
 
637
            }
 
638
            emitThumbnailLoaded(thumb, size);
 
639
            determineNextIcon();
 
640
            return;
 
641
        }
 
642
    }
 
643
 
 
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());
 
649
        } else {
 
650
            // Original is remote, download it
 
651
            mState = STATE_DOWNLOADORIG;
 
652
 
 
653
            KTemporaryFile tempFile;
 
654
            tempFile.setAutoRemove(false);
 
655
            if (!tempFile.open()) {
 
656
                kWarning() << "Couldn't create temp file to download " << mCurrentUrl.prettyUrl();
 
657
                emitThumbnailLoadingFailed();
 
658
                determineNextIcon();
 
659
                return;
 
660
            }
 
661
            mTempPath = tempFile.fileName();
 
662
 
 
663
            KUrl url;
 
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());
 
668
            addSubjob(job);
 
669
        }
 
670
    } else {
 
671
        // Not a raster image, use a KPreviewJob
 
672
        LOG("Starting a KPreviewJob for" << mCurrentItem.url());
 
673
        mState = STATE_PREVIEWJOB;
 
674
        KFileItemList list;
 
675
        list.append(mCurrentItem);
 
676
        const int pixelSize = ThumbnailGroup::pixelSize(mThumbnailGroup);
 
677
        if (mPreviewPlugins.isEmpty()) {
 
678
            mPreviewPlugins = KIO::PreviewJob::availablePlugins();
 
679
        }
 
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()));
 
686
        addSubjob(job);
 
687
    }
 
688
}
 
689
 
 
690
void ThumbnailLoadJob::startCreatingThumbnail(const QString& pixPath)
 
691
{
 
692
    LOG("Creating thumbnail from" << pixPath);
 
693
    mThumbnailThread.load(mOriginalUri, mOriginalTime, mCurrentItem.size(),
 
694
                          mCurrentItem.mimetype(), pixPath, mThumbnailPath, mThumbnailGroup);
 
695
}
 
696
 
 
697
void ThumbnailLoadJob::slotGotPreview(const KFileItem& item, const QPixmap& pixmap)
 
698
{
 
699
    LOG(mCurrentItem.url());
 
700
    QSize size;
 
701
    emit thumbnailLoaded(item, pixmap, size);
 
702
}
 
703
 
 
704
void ThumbnailLoadJob::emitThumbnailLoaded(const QImage& img, const QSize& size)
 
705
{
 
706
    LOG(mCurrentItem.url());
 
707
    QPixmap thumb = QPixmap::fromImage(img);
 
708
    emit thumbnailLoaded(mCurrentItem, thumb, size);
 
709
}
 
710
 
 
711
void ThumbnailLoadJob::emitThumbnailLoadingFailed()
 
712
{
 
713
    LOG(mCurrentItem.url());
 
714
    emit thumbnailLoadingFailed(mCurrentItem);
 
715
}
 
716
 
 
717
bool ThumbnailLoadJob::isPendingThumbnailCacheEmpty()
 
718
{
 
719
    return sThumbnailCache->isEmpty();
 
720
}
711
721
 
712
722
} // namespace