diff --git a/src/qtquick/ComicCoverImageProvider.cpp b/src/qtquick/ComicCoverImageProvider.cpp index a6b6b61..fe5041b 100644 --- a/src/qtquick/ComicCoverImageProvider.cpp +++ b/src/qtquick/ComicCoverImageProvider.cpp @@ -1,118 +1,182 @@ /* * Copyright (C) 2015 Dan Leinir Turthra Jensen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * */ #include "ComicCoverImageProvider.h" #include #include #include +#include #include #include +#include #include class ComicCoverImageProvider::Private { public: Private() {} + QThreadPool pool; +}; + +ComicCoverImageProvider::ComicCoverImageProvider() + : QQuickAsyncImageProvider() + , d(new Private) +{ +} + +ComicCoverImageProvider::~ComicCoverImageProvider() +{ + delete d; +} + +class ComicCoverResponse : public QQuickImageResponse +{ + public: + ComicCoverResponse(const QString &id, const QSize &requestedSize, QThreadPool *pool) + { + m_runnable = new ComicCoverRunnable(id, requestedSize); + connect(m_runnable, &ComicCoverRunnable::done, this, &ComicCoverResponse::handleDone); + pool->start(m_runnable); + } + + void handleDone(QImage image) { + m_image = image; + emit finished(); + } + + QQuickTextureFactory *textureFactory() const override + { + return QQuickTextureFactory::textureFactoryForImage(m_image); + } + + void cancel() override + { + m_runnable->abort(); + } + + ComicCoverRunnable* m_runnable{nullptr}; + QImage m_image; +}; + +QQuickImageResponse * ComicCoverImageProvider::requestImageResponse(const QString& id, const QSize& requestedSize) +{ + ComicCoverResponse* response = new ComicCoverResponse(id, requestedSize, &d->pool); + return response; +} + + +class ComicCoverRunnable::Private { +public: + Private() {} + QString id; + QSize requestedSize; + + bool abort{false}; QStringList entries; void filterImages(QStringList& entries) { /// Sort case-insensitive, then remove non-image entries. QMap entryMap; Q_FOREACH(const QString& entry, entries) { if (entry.endsWith(QLatin1String(".gif"), Qt::CaseInsensitive) || entry.endsWith(QLatin1String(".jpg"), Qt::CaseInsensitive) || entry.endsWith(QLatin1String(".jpeg"), Qt::CaseInsensitive) || entry.endsWith(QLatin1String(".png"), Qt::CaseInsensitive)) { entryMap.insert(entry.toLower(), entry); } } entries = entryMap.values(); } void getArchiveFileList(QStringList& entries, const QString& prefix, const KArchiveDirectory *dir) { /// Recursively list all files in the ZIP archive into 'entries'. Q_FOREACH (const QString& entry, dir->entries()) { const KArchiveEntry *e = dir->entry(entry); if (e->isDirectory()) { getArchiveFileList(entries, prefix + entry + '/', static_cast(e)); } else if (e->isFile()) { entries.append(prefix + entry); } } } }; -ComicCoverImageProvider::ComicCoverImageProvider() - : QQuickImageProvider(QQuickImageProvider::Image) - , d(new Private) +ComicCoverRunnable::ComicCoverRunnable(const QString& id, const QSize& requestedSize) + : d(new Private) { + d->id = id; + d->requestedSize = requestedSize; } -ComicCoverImageProvider::~ComicCoverImageProvider() +void ComicCoverRunnable::abort() { - delete d; + d->abort = true; } -QImage ComicCoverImageProvider::requestImage(const QString& id, QSize* size, const QSize& requestedSize) +void ComicCoverRunnable::run() { - Q_UNUSED(size) - Q_UNUSED(requestedSize) QImage img; + QSize ourSize(KIconLoader::SizeEnormous, KIconLoader::SizeEnormous); + if(d->requestedSize.width() > 0 && d->requestedSize.height() > 0) + { + ourSize = d->requestedSize; + } + KArchive* archive = nullptr; QMimeDatabase db; - db.mimeTypeForFile(id, QMimeDatabase::MatchContent); - const QMimeType mime = db.mimeTypeForFile(id, QMimeDatabase::MatchContent); - if(mime.inherits("application/x-cbr") || mime.inherits("application/x-rar")) { - archive = new KRar(id); + db.mimeTypeForFile(d->id, QMimeDatabase::MatchContent); + const QMimeType mime = db.mimeTypeForFile(d->id, QMimeDatabase::MatchContent); + if(!d->abort && (mime.inherits("application/x-cbr") || mime.inherits("application/x-rar"))) { + archive = new KRar(d->id); } // FIXME: This goes elsewhere - see below // If this code seems familiar, it is adapted from kio-extras/thumbnail/comiccreator.cpp // The reason being that this code should be removed once our karchive-rar functionality is merged into // karchive proper. - if(archive && archive->open(QIODevice::ReadOnly)) { + if(!d->abort && archive && archive->open(QIODevice::ReadOnly)) { // Get the archive's directory. const KArchiveDirectory* cArchiveDir = archive->directory(); - if (cArchiveDir) { + if (!d->abort && cArchiveDir) { QStringList entries; // Get and filter the entries from the archive. d->getArchiveFileList(entries, QString(), cArchiveDir); d->filterImages(entries); - if (!entries.isEmpty()) { + if (!d->abort && !entries.isEmpty()) { // Extract the cover file. const KArchiveFile *coverFile = static_cast(cArchiveDir->entry(entries[0])); - if (coverFile) { + if (!d->abort && coverFile) { bool success = img.loadFromData(coverFile->data()); - if(!success) { + if(!d->abort && !success) { QIcon oops = QIcon::fromTheme("unknown"); img = oops.pixmap(oops.availableSizes().last()).toImage(); - qCDebug(QTQUICK_LOG) << "Failed to load image with id:" << id; + qCDebug(QTQUICK_LOG) << "Failed to load image with id:" << d->id; } } } } } - return img; + Q_EMIT done(img.scaled(ourSize)); } diff --git a/src/qtquick/ComicCoverImageProvider.h b/src/qtquick/ComicCoverImageProvider.h index 3912bcc..dae6844 100644 --- a/src/qtquick/ComicCoverImageProvider.h +++ b/src/qtquick/ComicCoverImageProvider.h @@ -1,55 +1,81 @@ /* * Copyright (C) 2015 Dan Leinir Turthra Jensen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * */ #ifndef COMICCOVERIMAGEPROVIDER_H #define COMICCOVERIMAGEPROVIDER_H -#include +#include +#include /** * \brief Get file previews of Comic Book Archives * * TODO This should go into a thumbnailer later, once karchive-rar is merged into KArchive * * NOTE: As this task is potentially heavy, make sure to mark any Image using this provider asynchronous */ -class ComicCoverImageProvider : public QQuickImageProvider +class ComicCoverImageProvider : public QQuickAsyncImageProvider { public: explicit ComicCoverImageProvider(); ~ComicCoverImageProvider() override; /** * \brief Get an image. * * @param id The source of the image. * @param size The size of the original image, unused. * @param requestedSize The required size of the final image, unused. * - * @return a QImage. + * @return an asynchronous image response */ - QImage requestImage(const QString& id, QSize* size, const QSize& requestedSize) override; + QQuickImageResponse *requestImageResponse(const QString &id, const QSize &requestedSize) override; +private: + class Private; + Private* d; +}; + +/** + * \brief A worker class which does the bulk of the work for PreviewImageProvider + */ +class ComicCoverRunnable : public QObject, public QRunnable { + Q_OBJECT; +public: + ComicCoverRunnable(const QString &id, const QSize &requestedSize); + + void run() override; + + /** + * Request that the preview worker abort what it's doing + */ + Q_SLOT void abort(); + + /** + * \brief Emitted once the preview has been retrieved (successfully or not) + * @param image The preview image in the requested size (possibly a placeholder) + */ + Q_SIGNAL void done(QImage image); private: class Private; Private* d; }; #endif//COMICCOVERIMAGEPROVIDER_H