File indexing completed on 2025-04-27 03:58:09

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2007-06-05
0007  * Description : Thumbnail loading
0008  *
0009  * SPDX-FileCopyrightText: 2006-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
0010  * SPDX-FileCopyrightText: 2005-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0011  * SPDX-FileCopyrightText: 2015      by Mohamed_Anwer <m_dot_anwer at gmx dot com>
0012  *
0013  * SPDX-License-Identifier: GPL-2.0-or-later
0014  *
0015  * ============================================================ */
0016 
0017 #include "thumbnailloadthread_p.h"
0018 
0019 namespace Digikam
0020 {
0021 
0022 Q_GLOBAL_STATIC(ThumbnailLoadThreadStaticPriv, static_d)
0023 Q_GLOBAL_STATIC(ThumbnailLoadThread,           defaultObject)
0024 Q_GLOBAL_STATIC(ThumbnailLoadThread,           defaultIconViewObject)
0025 
0026 // --- Creating loading descriptions ---
0027 
0028 LoadingDescription ThumbnailLoadThread::Private::createLoadingDescription(const ThumbnailIdentifier& identifier,
0029                                                                           int size,
0030                                                                           bool setLastDescription)
0031 {
0032     size = thumbnailSizeForPixmapSize(size);
0033 
0034     LoadingDescription description(identifier.filePath, PreviewSettings(), size,
0035                                    LoadingDescription::NoColorConversion,
0036                                    LoadingDescription::PreviewParameters::Thumbnail);
0037     description.previewParameters.storageReference = identifier.id;
0038 
0039     if (IccSettings::instance()->useManagedPreviews())
0040     {
0041         description.postProcessingParameters.colorManagement = LoadingDescription::ConvertForDisplay;
0042         description.postProcessingParameters.setProfile(static_d->profile);
0043     }
0044 
0045     if (setLastDescription)
0046     {
0047         lastDescriptions.clear();
0048         lastDescriptions << description;
0049     }
0050 
0051     return description;
0052 }
0053 
0054 LoadingDescription ThumbnailLoadThread::Private::createLoadingDescription(const ThumbnailIdentifier& identifier,
0055                                                                           int size,
0056                                                                           const QRect& detailRect,
0057                                                                           bool setLastDescription)
0058 {
0059     size                                          = thumbnailSizeForPixmapSize(size);
0060 
0061     LoadingDescription description(identifier.filePath, PreviewSettings(), size,
0062                                    LoadingDescription::NoColorConversion,
0063                                    LoadingDescription::PreviewParameters::DetailThumbnail);
0064 
0065     description.previewParameters.storageReference = identifier.id;
0066     description.previewParameters.extraParameter   = detailRect;
0067 
0068     if (IccSettings::instance()->useManagedPreviews())
0069     {
0070         description.postProcessingParameters.colorManagement = LoadingDescription::ConvertForDisplay;
0071         description.postProcessingParameters.setProfile(static_d->profile);
0072     }
0073 
0074     if (setLastDescription)
0075     {
0076         lastDescriptions.clear();
0077         lastDescriptions << description;
0078     }
0079 
0080     return description;
0081 }
0082 
0083 // ---------------------------------------------------------------------------
0084 
0085 ThumbnailLoadThread::ThumbnailLoadThread(QObject* const parent)
0086     : ManagedLoadSaveThread(parent),
0087       d                    (new Private)
0088 {
0089     static_d->firstThreadCreated = true;
0090     d->creator                   = new ThumbnailCreator(static_d->storageMethod);
0091 
0092     if (static_d->provider)
0093     {
0094         d->creator->setThumbnailInfoProvider(static_d->provider);
0095     }
0096 
0097     d->creator->setOnlyLargeThumbnails(true);
0098     d->creator->setRemoveAlphaChannel(true);
0099 
0100     connect(this, SIGNAL(thumbnailsAvailable()),
0101             this, SLOT(slotThumbnailsAvailable()));
0102 }
0103 
0104 ThumbnailLoadThread::~ThumbnailLoadThread()
0105 {
0106     shutDown();
0107 
0108     delete d->creator;
0109     delete d;
0110 }
0111 
0112 ThumbnailLoadThread* ThumbnailLoadThread::defaultIconViewThread()
0113 {
0114     return defaultIconViewObject;
0115 }
0116 
0117 ThumbnailLoadThread* ThumbnailLoadThread::defaultThread()
0118 {
0119     return defaultObject;
0120 }
0121 
0122 void ThumbnailLoadThread::cleanUp()
0123 {
0124     // NOTE: Nothing to do with Qt5 and Q_GLOBAL_STATIC. Qt clean up all automatically at end of application instance.
0125     // But stopping all running tasks to prevent a crash at end.
0126 
0127     defaultIconViewThread()->stopAllTasks();
0128     defaultThread()->stopAllTasks();
0129 
0130     defaultIconViewThread()->wait();
0131     defaultThread()->wait();
0132 }
0133 
0134 void ThumbnailLoadThread::initializeThumbnailDatabase(const DbEngineParameters& params, ThumbnailInfoProvider* const provider)
0135 {
0136     if (static_d->firstThreadCreated)
0137     {
0138         qCDebug(DIGIKAM_GENERAL_LOG) << "Call initializeThumbnailDatabase at application start. "
0139                                         "There are already thumbnail loading threads created, "
0140                                         "and these will not be switched to use the database. ";
0141     }
0142 
0143     ThumbsDbAccess::setParameters(params);
0144 
0145     if (ThumbsDbAccess::checkReadyForUse(nullptr))
0146     {
0147         qCDebug(DIGIKAM_GENERAL_LOG) << "Thumbnails database ready for use";
0148         static_d->storageMethod = ThumbnailCreator::ThumbnailDatabase;
0149         static_d->provider      = provider;
0150     }
0151     else
0152     {
0153         QMessageBox::information(qApp->activeWindow(), i18nc("@title:window", "Failed to Initialize Thumbnails Database"),
0154                                  i18n("Error message: %1", ThumbsDbAccess().lastError()));
0155     }
0156 }
0157 
0158 void ThumbnailLoadThread::setDisplayingWidget(QWidget* const widget)
0159 {
0160     static_d->profile = IccManager::displayProfile(widget);
0161 }
0162 
0163 void ThumbnailLoadThread::setThumbnailSize(int size, bool forFace)
0164 {
0165     d->size = size;
0166 
0167     if (forFace)
0168     {
0169         d->creator->setThumbnailSize(size);
0170     }
0171 }
0172 
0173 int ThumbnailLoadThread::maximumThumbnailSize()
0174 {
0175     return ThumbnailSize::maxThumbsSize();
0176 }
0177 
0178 int ThumbnailLoadThread::maximumThumbnailPixmapSize(bool highlight)
0179 {
0180     if (highlight)
0181     {
0182         return ThumbnailSize::maxThumbsSize();
0183     }
0184     else
0185     {
0186         return ThumbnailSize::maxThumbsSize() + 2;    // see slotThumbnailLoaded
0187     }
0188 }
0189 
0190 void ThumbnailLoadThread::setSendSurrogatePixmap(bool send)
0191 {
0192     d->sendSurrogate = send;
0193 }
0194 
0195 void ThumbnailLoadThread::setPixmapRequested(bool wantPixmap)
0196 {
0197     d->wantPixmap = wantPixmap;
0198 }
0199 
0200 void ThumbnailLoadThread::setHighlightPixmap(bool highlight)
0201 {
0202     d->highlight = highlight;
0203 }
0204 
0205 ThumbnailCreator* ThumbnailLoadThread::thumbnailCreator() const
0206 {
0207     return d->creator;
0208 }
0209 
0210 int ThumbnailLoadThread::thumbnailToPixmapSize(int size) const
0211 {
0212     return d->pixmapSizeForThumbnailSize(size);
0213 }
0214 
0215 int ThumbnailLoadThread::thumbnailToPixmapSize(bool withHighlight, int size)
0216 {
0217     if (withHighlight && (size >= 10))
0218     {
0219         return (size + 2);
0220     }
0221 
0222     return size;
0223 }
0224 
0225 int ThumbnailLoadThread::pixmapToThumbnailSize(int size) const
0226 {
0227     return d->thumbnailSizeForPixmapSize(size);
0228 }
0229 
0230 bool ThumbnailLoadThread::find(const ThumbnailIdentifier& identifier,
0231                                int size,
0232                                QPixmap* retPixmap,
0233                                bool emitSignal,
0234                                const QRect& detailRect,
0235                                bool onlyStorage)
0236 {
0237     const QPixmap* pix = nullptr;
0238     LoadingDescription description;
0239 
0240     if (detailRect.isNull())
0241     {
0242         description = d->createLoadingDescription(identifier, size);
0243     }
0244     else
0245     {
0246         description = d->createLoadingDescription(identifier, size, detailRect);
0247     }
0248 
0249     if (onlyStorage)
0250     {
0251         description.previewParameters.flags |= LoadingDescription::PreviewParameters::OnlyFromStorage;
0252     }
0253 
0254     QString cacheKey = description.cacheKey();
0255 
0256     {
0257         LoadingCache* const cache = LoadingCache::cache();
0258         LoadingCache::CacheLock lock(cache);
0259         pix                       = cache->retrieveThumbnailPixmap(cacheKey);
0260     }
0261 
0262     if (pix)
0263     {
0264         if (retPixmap)
0265         {
0266             *retPixmap = *pix;
0267         }
0268 
0269         if (emitSignal)
0270         {
0271             load(description);
0272             Q_EMIT signalThumbnailLoaded(description, QPixmap(*pix));
0273         }
0274 
0275         return true;
0276     }
0277 
0278     {
0279         // If there is a result waiting for conversion to pixmap, return false - pixmap will come shortly
0280 
0281         QMutexLocker lock(&d->resultsMutex);
0282 
0283         if (d->collectedResults.contains(cacheKey))
0284         {
0285             return false;
0286         }
0287     }
0288 
0289     load(description);
0290 
0291     return false;
0292 }
0293 
0294 // --- Normal thumbnails ---
0295 
0296 bool ThumbnailLoadThread::find(const ThumbnailIdentifier& identifier,
0297                                QPixmap& retPixmap, int size, bool onlyStorage)
0298 {
0299     return find(identifier, size, &retPixmap, false, QRect(), onlyStorage);
0300 }
0301 
0302 bool ThumbnailLoadThread::find(const ThumbnailIdentifier& identifier, QPixmap& retPixmap)
0303 {
0304     return find(identifier, retPixmap, d->size);
0305 }
0306 
0307 void ThumbnailLoadThread::find(const ThumbnailIdentifier& identifier)
0308 {
0309     find(identifier, d->size);
0310 }
0311 
0312 void ThumbnailLoadThread::find(const ThumbnailIdentifier& identifier, int size)
0313 {
0314     find(identifier, size, nullptr, true, QRect());
0315 }
0316 
0317 void ThumbnailLoadThread::findGroup(QList<ThumbnailIdentifier>& identifiers)
0318 {
0319     findGroup(identifiers, d->size);
0320 }
0321 
0322 void ThumbnailLoadThread::findGroup(QList<ThumbnailIdentifier>& identifiers, int size)
0323 {
0324     if (!checkSize(size))
0325     {
0326         return;
0327     }
0328 
0329     QList<LoadingDescription> descriptions = d->makeDescriptions(identifiers, size);
0330     ManagedLoadSaveThread::prependThumbnailGroup(descriptions);
0331 }
0332 
0333 // --- Detail thumbnails ---
0334 
0335 bool ThumbnailLoadThread::find(const ThumbnailIdentifier& identifier, const QRect& rect, QPixmap& pixmap)
0336 {
0337     return find(identifier, rect, pixmap, d->size);
0338 }
0339 
0340 bool ThumbnailLoadThread::find(const ThumbnailIdentifier& identifier,
0341                                const QRect& rect, QPixmap& pixmap, int size, bool onlyStorage)
0342 {
0343     return find(identifier, size, &pixmap, false, rect, onlyStorage);
0344 }
0345 
0346 void ThumbnailLoadThread::find(const ThumbnailIdentifier& identifier, const QRect& rect)
0347 {
0348     find(identifier, rect, d->size);
0349 }
0350 
0351 void ThumbnailLoadThread::find(const ThumbnailIdentifier& identifier, const QRect& rect, int size)
0352 {
0353     find(identifier, size, nullptr, true, rect);
0354 }
0355 
0356 void ThumbnailLoadThread::findGroup(const QList<QPair<ThumbnailIdentifier, QRect> >& idsAndRects)
0357 {
0358     findGroup(idsAndRects, d->size);
0359 }
0360 
0361 void ThumbnailLoadThread::findGroup(const QList<QPair<ThumbnailIdentifier, QRect> >& idsAndRects, int size)
0362 {
0363     if (!checkSize(size))
0364     {
0365         return;
0366     }
0367 
0368     QList<LoadingDescription> descriptions = d->makeDescriptions(idsAndRects, size);
0369     ManagedLoadSaveThread::prependThumbnailGroup(descriptions);
0370 }
0371 
0372 // --- Preloading ---
0373 
0374 void ThumbnailLoadThread::preload(const ThumbnailIdentifier& identifier)
0375 {
0376     preload(identifier, d->size);
0377 }
0378 
0379 void ThumbnailLoadThread::preload(const ThumbnailIdentifier& identifier, int size)
0380 {
0381     LoadingDescription description = d->createLoadingDescription(identifier, size);
0382 
0383     if (d->checkDescription(description))
0384     {
0385         load(description, true);
0386     }
0387 }
0388 
0389 void ThumbnailLoadThread::preloadGroup(QList<ThumbnailIdentifier>& identifiers)
0390 {
0391     preloadGroup(identifiers, d->size);
0392 }
0393 
0394 void ThumbnailLoadThread::preloadGroup(QList<ThumbnailIdentifier>& identifiers, int size)
0395 {
0396     if (!checkSize(size))
0397     {
0398         return;
0399     }
0400 
0401     QList<LoadingDescription> descriptions = d->makeDescriptions(identifiers, size);
0402     ManagedLoadSaveThread::preloadThumbnailGroup(descriptions);
0403 }
0404 
0405 void ThumbnailLoadThread::pregenerateGroup(const QList<ThumbnailIdentifier>& identifiers)
0406 {
0407     pregenerateGroup(identifiers, d->size);
0408 }
0409 
0410 void ThumbnailLoadThread::pregenerateGroup(const QList<ThumbnailIdentifier>& identifiers, int size)
0411 {
0412     if (!checkSize(size))
0413     {
0414         return;
0415     }
0416 
0417     QList<LoadingDescription> descriptions = d->makeDescriptions(identifiers, size);
0418 
0419     for (int i = 0 ; i < descriptions.size() ; ++i)
0420     {
0421         descriptions[i].previewParameters.flags |= LoadingDescription::PreviewParameters::OnlyPregenerate;
0422     }
0423 
0424     ManagedLoadSaveThread::preloadThumbnailGroup(descriptions);
0425 }
0426 
0427 // --- Basic load() ---
0428 
0429 void ThumbnailLoadThread::load(const LoadingDescription& desc)
0430 {
0431     load(desc, false);
0432 }
0433 
0434 void ThumbnailLoadThread::load(const LoadingDescription& description, bool preload)
0435 {
0436     if (!checkSize(description.previewParameters.size))
0437     {
0438         return;
0439     }
0440 
0441     if (preload)
0442     {
0443         ManagedLoadSaveThread::preloadThumbnail(description);
0444     }
0445     else
0446     {
0447         ManagedLoadSaveThread::loadThumbnail(description);
0448     }
0449 }
0450 
0451 QList<LoadingDescription> ThumbnailLoadThread::lastDescriptions() const
0452 {
0453     return d->lastDescriptions;
0454 }
0455 
0456 bool ThumbnailLoadThread::checkSize(int size)
0457 {
0458     size             = d->thumbnailSizeForPixmapSize(size);
0459     double dpr       = qApp->devicePixelRatio();
0460     int maxThumbSize = (dpr > 1.0) ? ThumbnailSize::MAX
0461                                    : ThumbnailSize::maxThumbsSize();
0462 
0463     if      (size <= 0)
0464     {
0465         qCDebug(DIGIKAM_GENERAL_LOG) << "ThumbnailLoadThread::load: No thumbnail size specified. Refusing to load thumbnail.";
0466         return false;
0467     }
0468     else if (size > maxThumbSize)
0469     {
0470         qCDebug(DIGIKAM_GENERAL_LOG) << "ThumbnailLoadThread::load: Thumbnail size " << size
0471                                      << " is larger than " << maxThumbSize << ". Refusing to load.";
0472         return false;
0473     }
0474 
0475     return true;
0476 }
0477 
0478 // --- Receiving ---
0479 
0480 /**
0481  * virtual method overridden from LoadSaveNotifier, implemented first by LoadSaveThread
0482  * called by ThumbnailTask from working thread
0483  */
0484 void ThumbnailLoadThread::thumbnailLoaded(const LoadingDescription& loadingDescription, const QImage& img)
0485 {
0486     // call parent to send signalThumbnailLoaded(LoadingDescription, QImage) - signal is part of public API
0487 
0488     ManagedLoadSaveThread::thumbnailLoaded(loadingDescription, img);
0489 
0490     if (!d->wantPixmap)
0491     {
0492         return;
0493     }
0494 
0495     // Store result in our list and fire one signal
0496     // This means there can be several results per pixmap,
0497     // to speed up cases where inter-thread communication is the limiting factor
0498 
0499     QMutexLocker lock(&d->resultsMutex);
0500     d->collectedResults.insert(loadingDescription.cacheKey(), ThumbnailResult(loadingDescription, img));
0501 
0502     // only sent signal when flag indicates there is no signal on the way currently
0503 
0504     if (!d->notifiedForResults)
0505     {
0506         d->notifiedForResults = true;
0507         Q_EMIT thumbnailsAvailable();
0508     }
0509 }
0510 
0511 void ThumbnailLoadThread::slotThumbnailsAvailable()
0512 {
0513     // harvest collected results safely into our thread
0514 
0515     QList<ThumbnailResult> results;
0516     {
0517         QMutexLocker lock(&d->resultsMutex);
0518         results               = d->collectedResults.values();
0519         d->collectedResults.clear();
0520 
0521         // reset flag so that for next result, the signal is sent again
0522 
0523         d->notifiedForResults = false;
0524     }
0525 
0526     Q_FOREACH (const ThumbnailResult& result, results)
0527     {
0528         slotThumbnailLoaded(result.loadingDescription, result.image);
0529     }
0530 }
0531 
0532 void ThumbnailLoadThread::slotThumbnailLoaded(const LoadingDescription& description, const QImage& thumb)
0533 {
0534     QPixmap pix;
0535 
0536     if (thumb.isNull())
0537     {
0538         pix = surrogatePixmap(description);
0539     }
0540     else
0541     {
0542         int w = thumb.width();
0543         int h = thumb.height();
0544 
0545         // highlight only when requested and when thumbnail
0546         // width and height are greater than 10
0547 
0548         if (d->highlight && ((w >= 10) && (h >= 10)))
0549         {
0550             pix = QPixmap(w + 2, h + 2);
0551             QPainter p(&pix);
0552             p.setPen(QPen(Qt::black, 1));
0553             p.drawRect(0, 0, w + 1, h + 1);
0554             p.drawImage(1, 1, thumb);
0555         }
0556         else
0557         {
0558             pix = QPixmap::fromImage(thumb);
0559         }
0560     }
0561 
0562     // put into cache
0563 
0564     if (!pix.isNull())
0565     {
0566         LoadingCache* const cache = LoadingCache::cache();
0567         LoadingCache::CacheLock lock(cache);
0568         cache->putThumbnail(description.cacheKey(), pix, description.filePath);
0569     }
0570 
0571     Q_EMIT signalThumbnailLoaded(description, pix);
0572 }
0573 
0574 QPixmap ThumbnailLoadThread::surrogatePixmap(const LoadingDescription& description)
0575 {
0576     if (!d->sendSurrogate)
0577     {
0578         return QPixmap();
0579     }
0580 
0581     QPixmap pix;
0582 
0583     QMimeType mimeType = QMimeDatabase().mimeTypeForFile(description.filePath);
0584 
0585     if (mimeType.isValid())
0586     {
0587         pix = QIcon::fromTheme(mimeType.genericIconName()).pixmap(128);
0588     }
0589 
0590     if (pix.isNull())
0591     {
0592         pix = QIcon::fromTheme(QLatin1String("application-x-zerosize")).pixmap(128);
0593     }
0594 
0595     if (pix.isNull())
0596     {
0597         // give up
0598         return QPixmap();
0599     }
0600 
0601     // Resize icon to the right size depending of current settings.
0602 
0603     QSize size(pix.size());
0604     size.scale(description.previewParameters.size, description.previewParameters.size, Qt::KeepAspectRatio);
0605 
0606     if (!pix.isNull() && (size.width() < pix.width()) && (size.height() < pix.height()))
0607     {
0608         // only scale down
0609         // do not scale up, looks bad
0610 
0611         pix = pix.scaled(size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
0612     }
0613 
0614     return pix;
0615 }
0616 
0617 // --- Utilities ---
0618 
0619 void ThumbnailLoadThread::storeDetailThumbnail(const QString& filePath, const QRect& detailRect, const QImage& image, bool isFace)
0620 {
0621     Q_UNUSED(isFace);
0622     d->creator->storeDetailThumbnail(filePath, detailRect, image);
0623 }
0624 
0625 int ThumbnailLoadThread::storedSize() const
0626 {
0627     return d->creator->storedSize();
0628 }
0629 
0630 void ThumbnailLoadThread::deleteThumbnail(const QString& filePath)
0631 {
0632     {
0633         LoadingCache* const cache = LoadingCache::cache();
0634         LoadingCache::CacheLock lock(cache);
0635         QStringList possibleKeys  = LoadingDescription::possibleThumbnailCacheKeys(filePath);
0636 
0637         Q_FOREACH (const QString& cacheKey, possibleKeys)
0638         {
0639             cache->removeThumbnail(cacheKey);
0640         }
0641     }
0642 
0643     ThumbnailCreator creator(static_d->storageMethod);
0644 
0645     if (static_d->provider)
0646     {
0647         creator.setThumbnailInfoProvider(static_d->provider);
0648     }
0649 
0650     creator.deleteThumbnailsFromDisk(filePath);
0651 }
0652 
0653 ThumbnailImageCatcher::ThumbnailImageCatcher(QObject* const parent)
0654     : QObject(parent),
0655       d      (new Private)
0656 {
0657 }
0658 
0659 ThumbnailImageCatcher::ThumbnailImageCatcher(ThumbnailLoadThread* const thread, QObject* const parent)
0660     : QObject(parent),
0661       d      (new Private)
0662 {
0663     setThumbnailLoadThread(thread);
0664 }
0665 
0666 ThumbnailImageCatcher::~ThumbnailImageCatcher()
0667 {
0668     delete d;
0669 }
0670 
0671 ThumbnailLoadThread* ThumbnailImageCatcher::thread() const
0672 {
0673     return d->thread;
0674 }
0675 
0676 void ThumbnailImageCatcher::setThumbnailLoadThread(ThumbnailLoadThread* const thread)
0677 {
0678     if (d->thread == thread)
0679     {
0680         return;
0681     }
0682 
0683     d->state = Private::Inactive;
0684 
0685     if (d->thread)
0686     {
0687         disconnect(d->thread, SIGNAL(signalThumbnailLoaded(LoadingDescription,QImage)),
0688                    this, SLOT(slotThumbnailLoaded(LoadingDescription,QImage)));
0689     }
0690 
0691     d->thread = thread;
0692 
0693     {
0694         QMutexLocker lock(&d->mutex);
0695         d->reset();
0696     }
0697 
0698     if (d->thread)
0699     {
0700         connect(d->thread, SIGNAL(signalThumbnailLoaded(LoadingDescription,QImage)),
0701                 this, SLOT(slotThumbnailLoaded(LoadingDescription,QImage)),
0702                 Qt::DirectConnection);
0703     }
0704 }
0705 
0706 void ThumbnailImageCatcher::setActive(bool active)
0707 {
0708     if (d->active == active)
0709     {
0710         return;
0711     }
0712 
0713     if (!active)
0714     {
0715         cancel();
0716     }
0717 
0718     QMutexLocker lock(&d->mutex);
0719     d->active = active;
0720     d->reset();
0721 }
0722 
0723 void ThumbnailImageCatcher::cancel()
0724 {
0725     QMutexLocker lock(&d->mutex);
0726 
0727     if (d->state == Private::Waiting)
0728     {
0729         d->state = Private::Quitting;
0730         d->condVar.wakeOne();
0731     }
0732 }
0733 
0734 void ThumbnailImageCatcher::slotThumbnailLoaded(const LoadingDescription& description, const QImage& image)
0735 {
0736     // We are in the thumbnail thread here, DirectConnection!
0737 
0738     QMutexLocker lock(&d->mutex);
0739 
0740     switch (d->state)
0741     {
0742         case Private::Inactive:
0743         {
0744             break;
0745         }
0746 
0747         case Private::Accepting:
0748         {
0749             d->intermediate << Private::CatcherResult(description, image);
0750             break;
0751         }
0752 
0753         case Private::Waiting:
0754         {
0755             d->harvest(description, image);
0756             break;
0757         }
0758 
0759         case Private::Quitting:
0760         {
0761             break;
0762         }
0763     }
0764 }
0765 
0766 int ThumbnailImageCatcher::enqueue()
0767 {
0768     QList<LoadingDescription> descriptions = d->thread->lastDescriptions();
0769 
0770     QMutexLocker lock(&d->mutex);
0771 
0772     Q_FOREACH (const LoadingDescription& description, descriptions)
0773     {
0774         d->tasks << Private::CatcherResult(description);
0775     }
0776 
0777     return descriptions.size();
0778 }
0779 
0780 QList<QImage> ThumbnailImageCatcher::waitForThumbnails()
0781 {
0782     if (!d->thread || d->tasks.isEmpty() || !d->active)
0783     {
0784         return QList<QImage>();
0785     }
0786 
0787     QMutexLocker lock(&d->mutex);
0788     d->state = Private::Waiting;
0789 
0790     // first, handle results received between request and calling this method
0791 
0792     Q_FOREACH (const Private::CatcherResult& result, d->intermediate)
0793     {
0794         d->harvest(result.description, result.image);
0795     }
0796 
0797     d->intermediate.clear();
0798 
0799     // Now wait for the rest to arrive. If already finished, state will be Quitting
0800 
0801     while (d->state == Private::Waiting)
0802     {
0803         d->condVar.wait(&d->mutex);
0804     }
0805 
0806     QList<QImage> result;
0807 
0808     Q_FOREACH (const Private::CatcherResult& task, d->tasks)
0809     {
0810         result << task.image;
0811     }
0812 
0813     d->reset();
0814 
0815     return result;
0816 }
0817 
0818 } // namespace Digikam
0819 
0820 #include "moc_thumbnailloadthread.cpp"