File indexing completed on 2025-01-05 03:54:13

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2009-03-05
0007  * Description : Qt item model for database entries with support for thumbnail loading
0008  *
0009  * SPDX-FileCopyrightText: 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
0010  * SPDX-FileCopyrightText: 2011-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0011  *
0012  * SPDX-License-Identifier: GPL-2.0-or-later
0013  *
0014  * ============================================================ */
0015 
0016 #include "itemthumbnailmodel.h"
0017 
0018 // Qt includes
0019 
0020 #include <QHash>
0021 
0022 // Local includes
0023 
0024 #include "digikam_debug.h"
0025 #include "digikam_globals.h"
0026 #include "thumbnailloadthread.h"
0027 
0028 namespace Digikam
0029 {
0030 
0031 class Q_DECL_HIDDEN ItemThumbnailModel::Private
0032 {
0033 public:
0034 
0035     explicit Private()
0036       : displayWidget      (nullptr),
0037         loadingThread      (nullptr),
0038         storageThread      (nullptr),
0039         preloadThread      (nullptr),
0040         thumbSize          (0),
0041         lastGlobalThumbSize(0),
0042         preloadThumbSize   (0),
0043         emitDataChanged    (true)
0044     {
0045         staticListContainingThumbnailRole << ItemModel::ThumbnailRole;
0046     }
0047 
0048     QWidget*               displayWidget;
0049 
0050     ThumbnailLoadThread*   loadingThread;
0051     ThumbnailLoadThread*   storageThread;
0052     ThumbnailLoadThread*   preloadThread;
0053     ThumbnailSize          thumbSize;
0054     ThumbnailSize          lastGlobalThumbSize;
0055     ThumbnailSize          preloadThumbSize;
0056     QRect                  detailRect;
0057     QVector<int>           staticListContainingThumbnailRole;
0058 
0059     bool                   emitDataChanged;
0060 
0061 public:
0062 
0063     int preloadThumbnailSize() const
0064     {
0065         if (preloadThumbSize.size())
0066         {
0067             return preloadThumbSize.size();
0068         }
0069 
0070         return thumbSize.size();
0071     }
0072 };
0073 
0074 ItemThumbnailModel::ItemThumbnailModel(QWidget* const parent)
0075     : ItemModel(parent),
0076       d        (new Private)
0077 {
0078     d->displayWidget = parent;
0079     d->storageThread = new ThumbnailLoadThread;
0080     d->storageThread->setSendSurrogatePixmap(false);
0081 
0082     connect(d->storageThread, SIGNAL(signalThumbnailLoaded(LoadingDescription,QPixmap)),
0083             this, SLOT(slotThumbnailLoadedFromStorage(LoadingDescription,QPixmap)));
0084 
0085     setKeepsFilePathCache(true);
0086 }
0087 
0088 ItemThumbnailModel::~ItemThumbnailModel()
0089 {
0090     d->storageThread->stopAllTasks();
0091     d->storageThread->wait();
0092 
0093     if (d->preloadThread)
0094     {
0095         d->preloadThread->stopAllTasks();
0096         d->preloadThread->wait();
0097     }
0098 
0099     delete d->storageThread;
0100     delete d->preloadThread;
0101     delete d;
0102 }
0103 
0104 void ItemThumbnailModel::setThumbnailLoadThread(ThumbnailLoadThread* const thread)
0105 {
0106     d->loadingThread = thread;
0107 
0108     connect(d->loadingThread, SIGNAL(signalThumbnailLoaded(LoadingDescription,QPixmap)),
0109             this, SLOT(slotThumbnailLoaded(LoadingDescription,QPixmap)));
0110 }
0111 
0112 ThumbnailLoadThread* ItemThumbnailModel::thumbnailLoadThread() const
0113 {
0114     return d->loadingThread;
0115 }
0116 
0117 ThumbnailSize ItemThumbnailModel::thumbnailSize() const
0118 {
0119     return d->thumbSize;
0120 }
0121 
0122 void ItemThumbnailModel::setThumbnailSize(const ThumbnailSize& size)
0123 {
0124     d->lastGlobalThumbSize = size;
0125     d->thumbSize           = size;
0126 }
0127 
0128 void ItemThumbnailModel::setPreloadThumbnailSize(const ThumbnailSize& size)
0129 {
0130     d->preloadThumbSize = size;
0131 }
0132 
0133 void ItemThumbnailModel::setEmitDataChanged(bool emitSignal)
0134 {
0135     d->emitDataChanged = emitSignal;
0136 }
0137 
0138 void ItemThumbnailModel::setPreloadThumbnails(bool preload)
0139 {
0140     if (preload)
0141     {
0142         if (!d->preloadThread)
0143         {
0144             d->preloadThread = new ThumbnailLoadThread;
0145             d->preloadThread->setPixmapRequested(false);
0146             d->preloadThread->setPriority(QThread::LowestPriority);
0147         }
0148 
0149         connect(this, SIGNAL(allRefreshingFinished()),
0150                 this, SLOT(preloadAllThumbnails()));
0151     }
0152     else
0153     {
0154         delete d->preloadThread;
0155         d->preloadThread = nullptr;
0156 
0157         disconnect(this, SIGNAL(allRefreshingFinished()),
0158                    this, SLOT(preloadAllThumbnails()));
0159     }
0160 }
0161 
0162 void ItemThumbnailModel::preloadThumbnails(const QList<ItemInfo>& infos)
0163 {
0164     if (!d->preloadThread)
0165     {
0166         return;
0167     }
0168 
0169     QList<ThumbnailIdentifier> ids;
0170 
0171     Q_FOREACH (const ItemInfo& info, infos)
0172     {
0173         ids << info.thumbnailIdentifier();
0174     }
0175 
0176     d->preloadThread->stopAllTasks();
0177     d->preloadThread->pregenerateGroup(ids, d->preloadThumbnailSize());
0178 }
0179 
0180 void ItemThumbnailModel::preloadThumbnails(const QList<QModelIndex>& indexesToPreload)
0181 {
0182     if (!d->preloadThread)
0183     {
0184         return;
0185     }
0186 
0187     QList<ThumbnailIdentifier> ids;
0188 
0189     Q_FOREACH (const QModelIndex& index, indexesToPreload)
0190     {
0191         ids << imageInfoRef(index).thumbnailIdentifier();
0192     }
0193 
0194     d->preloadThread->stopAllTasks();
0195     d->preloadThread->pregenerateGroup(ids, d->preloadThumbnailSize());
0196 }
0197 
0198 void ItemThumbnailModel::preloadAllThumbnails()
0199 {
0200     preloadThumbnails(imageInfos());
0201 }
0202 
0203 void ItemThumbnailModel::imageInfosCleared()
0204 {
0205     if (d->preloadThread)
0206     {
0207         d->preloadThread->stopAllTasks();
0208     }
0209 }
0210 
0211 QVariant ItemThumbnailModel::data(const QModelIndex& index, int role) const
0212 {
0213     if ((role == ThumbnailRole) && (d->loadingThread && index.isValid()))
0214     {
0215         QPixmap thumbnail;
0216         ItemInfo info = imageInfo(index);
0217 
0218         if (info.isNull())
0219         {
0220 
0221 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0222 
0223             QVariant var = QPixmap();
0224             return var;
0225 
0226 #else
0227 
0228             return QVariant(QVariant::Pixmap);
0229 
0230 #endif
0231 
0232         }
0233 
0234         double dpr    = d->displayWidget->devicePixelRatio();
0235         int thumbSize = qRound((double)d->thumbSize.size() * dpr);
0236 
0237         if (!d->detailRect.isNull())
0238         {
0239             if (d->storageThread->find(info.thumbnailIdentifier(),
0240                                        d->detailRect, thumbnail, thumbSize, true))
0241             {
0242                 return thumbnail;
0243             }
0244         }
0245         else
0246         {
0247             if (d->storageThread->find(info.thumbnailIdentifier(),
0248                                        thumbnail, thumbSize, true))
0249             {
0250                 return thumbnail;
0251             }
0252         }
0253 
0254 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0255 
0256         QVariant var = QPixmap();
0257         return var;
0258 
0259 #else
0260 
0261         return QVariant(QVariant::Pixmap);
0262 
0263 #endif
0264 
0265     }
0266 
0267     return ItemModel::data(index, role);
0268 }
0269 
0270 bool ItemThumbnailModel::setData(const QModelIndex& index, const QVariant& value, int role)
0271 {
0272     if (role == ThumbnailRole)
0273     {
0274 
0275 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0276 
0277         switch (value.typeId())
0278 
0279 #else
0280 
0281         switch (value.type())
0282 
0283 #endif
0284 
0285         {
0286             case QVariant::Invalid:
0287             {
0288                 d->thumbSize  = d->lastGlobalThumbSize;
0289                 d->detailRect = QRect();
0290 
0291                 break;
0292             }
0293 
0294             case QVariant::Int:
0295             {
0296                 if (value.isNull())
0297                 {
0298                     d->thumbSize = d->lastGlobalThumbSize;
0299                 }
0300                 else
0301                 {
0302                     d->thumbSize = ThumbnailSize(value.toInt());
0303                 }
0304 
0305                 break;
0306             }
0307 
0308             case QVariant::Rect:
0309             {
0310                 if (value.isNull())
0311                 {
0312                     d->detailRect = QRect();
0313                 }
0314                 else
0315                 {
0316                     d->detailRect = value.toRect();
0317                 }
0318 
0319                 break;
0320             }
0321 
0322             default:
0323             {
0324                 break;
0325             }
0326         }
0327     }
0328 
0329     return ItemModel::setData(index, value, role);
0330 }
0331 
0332 void ItemThumbnailModel::slotThumbnailLoadedFromStorage(const LoadingDescription& loadingDescription, const QPixmap& thumb)
0333 {
0334     if (thumb.isNull())
0335     {
0336         LoadingDescription description = loadingDescription;
0337         description.previewParameters.flags &= ~LoadingDescription::PreviewParameters::OnlyFromStorage;
0338         d->loadingThread->load(description);
0339     }
0340     else
0341     {
0342         slotThumbnailLoaded(loadingDescription, thumb);
0343     }
0344 }
0345 
0346 void ItemThumbnailModel::slotThumbnailLoaded(const LoadingDescription& loadingDescription, const QPixmap& thumb)
0347 {
0348     if (thumb.isNull())
0349     {
0350         return;
0351     }
0352 
0353     // In case of multiple occurrence, we currently do not know which thumbnail is this. Signal change on all.
0354 
0355     QModelIndexList indexes;
0356     ThumbnailIdentifier thumbId = loadingDescription.thumbnailIdentifier();
0357 
0358     if (thumbId.filePath.isEmpty())
0359     {
0360         indexes = indexesForImageId(thumbId.id);
0361     }
0362     else
0363     {
0364         indexes = indexesForPath(thumbId.filePath);
0365     }
0366 
0367     Q_FOREACH (const QModelIndex& index, indexes)
0368     {
0369         if (thumb.isNull())
0370         {
0371             Q_EMIT thumbnailFailed(index, loadingDescription.previewParameters.size);
0372         }
0373         else
0374         {
0375             Q_EMIT thumbnailAvailable(index, loadingDescription.previewParameters.size);
0376 
0377             if (d->emitDataChanged)
0378             {
0379                 Q_EMIT dataChanged(index, index, d->staticListContainingThumbnailRole);
0380             }
0381         }
0382     }
0383 }
0384 
0385 } // namespace Digikam
0386 
0387 #include "moc_itemthumbnailmodel.cpp"