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"