File indexing completed on 2025-01-19 03:53:38
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2007-05-01 0007 * Description : ItemInfo common data 0008 * 0009 * SPDX-FileCopyrightText: 2007-2013 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de> 0010 * SPDX-FileCopyrightText: 2014-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0011 * SPDX-FileCopyrightText: 2013 by Michael G. Hansen <mike at mghansen dot de> 0012 * 0013 * SPDX-License-Identifier: GPL-2.0-or-later 0014 * 0015 * ============================================================ */ 0016 0017 #include "iteminfocache.h" 0018 0019 // Local includes 0020 0021 #include "coredb.h" 0022 #include "coredbalbuminfo.h" 0023 #include "iteminfo.h" 0024 #include "iteminfolist.h" 0025 #include "iteminfodata.h" 0026 #include "digikam_debug.h" 0027 0028 namespace Digikam 0029 { 0030 0031 ItemInfoCache::ItemInfoCache() 0032 : m_needUpdateAlbums(true), 0033 m_needUpdateGrouped(true) 0034 { 0035 qRegisterMetaType<ItemInfo>("ItemInfo"); 0036 qRegisterMetaType<ItemInfoList>("ItemInfoList"); 0037 qRegisterMetaType<QList<ItemInfo> >("QList<ItemInfo>"); 0038 0039 CoreDbWatch* const dbwatch = CoreDbAccess::databaseWatch(); 0040 0041 connect(dbwatch, SIGNAL(imageChange(ImageChangeset)), 0042 this, SLOT(slotImageChanged(ImageChangeset)), 0043 Qt::DirectConnection); 0044 0045 connect(dbwatch, SIGNAL(imageTagChange(ImageTagChangeset)), 0046 this, SLOT(slotImageTagChanged(ImageTagChangeset)), 0047 Qt::DirectConnection); 0048 0049 connect(dbwatch, SIGNAL(albumChange(AlbumChangeset)), 0050 this, SLOT(slotAlbumChange(AlbumChangeset)), 0051 Qt::DirectConnection); 0052 } 0053 0054 ItemInfoCache::~ItemInfoCache() 0055 { 0056 } 0057 0058 static bool lessThanForAlbumShortInfo(const AlbumShortInfo& first, const AlbumShortInfo& second) 0059 { 0060 return first.id < second.id; 0061 } 0062 0063 void ItemInfoCache::checkAlbums() 0064 { 0065 if (m_needUpdateAlbums) 0066 { 0067 // list comes sorted from db 0068 0069 QList<AlbumShortInfo> infos = CoreDbAccess().db()->getAlbumShortInfos(); 0070 0071 ItemInfoWriteLocker lock; 0072 m_albums = infos; 0073 m_needUpdateAlbums = false; 0074 } 0075 } 0076 0077 int ItemInfoCache::getImageGroupedCount(qlonglong id) 0078 { 0079 if (m_needUpdateGrouped) 0080 { 0081 QList<qlonglong> ids = CoreDbAccess().db()->getRelatedImagesToByType(DatabaseRelation::Grouped); 0082 0083 ItemInfoWriteLocker lock; 0084 m_grouped = ids; 0085 m_needUpdateGrouped = false; 0086 } 0087 0088 ItemInfoReadLocker lock; 0089 return m_grouped.count(id); 0090 } 0091 0092 QExplicitlySharedDataPointer<ItemInfoData> ItemInfoCache::infoForId(qlonglong id) 0093 { 0094 { 0095 ItemInfoReadLocker lock; 0096 QExplicitlySharedDataPointer<ItemInfoData> ptr(m_infoHash.value(id)); 0097 0098 if (ptr) 0099 { 0100 return ptr; 0101 } 0102 } 0103 0104 ItemInfoWriteLocker lock; 0105 ItemInfoData* const data = new ItemInfoData(); 0106 data->id = id; 0107 m_infoHash[id] = data; 0108 0109 return QExplicitlySharedDataPointer<ItemInfoData>(data); 0110 } 0111 0112 void ItemInfoCache::cacheByName(const QExplicitlySharedDataPointer<ItemInfoData>& infoPtr) 0113 { 0114 // Called with Write lock 0115 0116 if (!infoPtr || (infoPtr->id == -1) || infoPtr->name.isEmpty()) 0117 { 0118 return; 0119 } 0120 0121 // Called in a context where we can assume that the entry is not yet cached by name (newly created data) 0122 0123 m_nameHash.remove(m_dataHash.value(infoPtr->id), infoPtr); 0124 m_dataHash.insert(infoPtr->id, infoPtr->name); 0125 m_nameHash.insert(infoPtr->name, infoPtr); 0126 } 0127 0128 QExplicitlySharedDataPointer<ItemInfoData> ItemInfoCache::infoForPath(int albumRootId, 0129 const QString& relativePath, const QString& name) 0130 { 0131 ItemInfoReadLocker lock; 0132 0133 // We check all entries in the multi hash with matching file name 0134 0135 QMultiHash<QString, QExplicitlySharedDataPointer<ItemInfoData> >::const_iterator it; 0136 0137 for (it = m_nameHash.constFind(name) ; (it != m_nameHash.constEnd()) && (it.key() == name) ; ++it) 0138 { 0139 // first check that album root matches 0140 0141 if (it.value()->albumRootId != albumRootId) 0142 { 0143 continue; 0144 } 0145 0146 // check that relativePath matches. We get relativePath from entry's id and compare to given name. 0147 0148 QList<AlbumShortInfo>::const_iterator albumIt = findAlbum(it.value()->albumId); 0149 0150 if ((albumIt == m_albums.constEnd()) || (albumIt->relativePath != relativePath)) 0151 { 0152 continue; 0153 } 0154 0155 // we have now a match by name, albumRootId and relativePath 0156 0157 return it.value(); 0158 } 0159 0160 return QExplicitlySharedDataPointer<ItemInfoData>(); 0161 } 0162 0163 void ItemInfoCache::dropInfo(const QExplicitlySharedDataPointer<ItemInfoData>& infoPtr) 0164 { 0165 if (!infoPtr) 0166 { 0167 return; 0168 } 0169 0170 ItemInfoWriteLocker lock; 0171 0172 // When we have the last ItemInfoData, the reference counter is at 3. 0173 // Because 2 QExplicitlySharedDataPointers are in cache and 1 is held by m_data. 0174 0175 if (infoPtr.data()->ref > 3) 0176 { 0177 return; 0178 } 0179 0180 m_nameHash.remove(m_dataHash.value(infoPtr->id), infoPtr); 0181 m_nameHash.remove(infoPtr->name, infoPtr); 0182 m_infoHash.remove(infoPtr->id); 0183 m_dataHash.remove(infoPtr->id); 0184 } 0185 0186 QList<AlbumShortInfo>::const_iterator ItemInfoCache::findAlbum(int id) 0187 { 0188 // Called with read lock 0189 0190 AlbumShortInfo info; 0191 info.id = id; 0192 0193 // we use the fact that d->infos is sorted by id 0194 0195 QList<AlbumShortInfo>::const_iterator it; 0196 it = std::lower_bound(m_albums.constBegin(), 0197 m_albums.constEnd(), info, 0198 lessThanForAlbumShortInfo); 0199 0200 if ((it == m_albums.constEnd()) || (info.id < (*it).id)) 0201 { 0202 return m_albums.constEnd(); 0203 } 0204 0205 return it; 0206 } 0207 0208 QString ItemInfoCache::albumRelativePath(int albumId) 0209 { 0210 checkAlbums(); 0211 ItemInfoReadLocker lock; 0212 QList<AlbumShortInfo>::const_iterator it = findAlbum(albumId); 0213 0214 if (it != m_albums.constEnd()) 0215 { 0216 return it->relativePath; 0217 } 0218 0219 return QString(); 0220 } 0221 0222 void ItemInfoCache::invalidate() 0223 { 0224 ItemInfoWriteLocker lock; 0225 QHash<qlonglong, QExplicitlySharedDataPointer<ItemInfoData> >::iterator it; 0226 0227 for (it = m_infoHash.begin() ; it != m_infoHash.end() ; ++it) 0228 { 0229 (*it)->invalid = true; 0230 (*it)->id = -1; 0231 } 0232 0233 m_albums.clear(); 0234 m_grouped.clear(); 0235 m_nameHash.clear(); 0236 m_infoHash.clear(); 0237 m_dataHash.clear(); 0238 m_needUpdateAlbums = true; 0239 m_needUpdateGrouped = true; 0240 } 0241 0242 void ItemInfoCache::slotImageChanged(const ImageChangeset& changeset) 0243 { 0244 ItemInfoWriteLocker lock; 0245 0246 Q_FOREACH (const qlonglong& imageId, changeset.ids()) 0247 { 0248 QHash<qlonglong, QExplicitlySharedDataPointer<ItemInfoData> >::iterator it = m_infoHash.find(imageId); 0249 0250 if (it != m_infoHash.end()) 0251 { 0252 // invalidate the relevant field. It will be lazy-loaded at first access. 0253 0254 DatabaseFields::Set changes = changeset.changes(); 0255 0256 if (changes & DatabaseFields::ItemCommentsAll) 0257 { 0258 (*it)->defaultCommentCached = false; 0259 (*it)->defaultTitleCached = false; 0260 } 0261 0262 if (changes & DatabaseFields::Category) 0263 { 0264 (*it)->categoryCached = false; 0265 } 0266 0267 if (changes & DatabaseFields::Format) 0268 { 0269 (*it)->formatCached = false; 0270 } 0271 0272 if (changes & DatabaseFields::PickLabel) 0273 { 0274 (*it)->pickLabelCached = false; 0275 } 0276 0277 if (changes & DatabaseFields::ColorLabel) 0278 { 0279 (*it)->colorLabelCached = false; 0280 } 0281 0282 if (changes & DatabaseFields::Rating) 0283 { 0284 (*it)->ratingCached = false; 0285 } 0286 0287 if (changes & DatabaseFields::CreationDate) 0288 { 0289 (*it)->creationDateCached = false; 0290 } 0291 0292 if (changes & DatabaseFields::ModificationDate) 0293 { 0294 (*it)->modificationDateCached = false; 0295 } 0296 0297 if (changes & DatabaseFields::Orientation) 0298 { 0299 (*it)->orientationCached = false; 0300 } 0301 0302 if (changes & DatabaseFields::FileSize) 0303 { 0304 (*it)->fileSizeCached = false; 0305 } 0306 0307 if (changes & DatabaseFields::UniqueHash) 0308 { 0309 (*it)->uniqueHashCached = false; 0310 } 0311 0312 if (changes & DatabaseFields::ManualOrder) 0313 { 0314 (*it)->manualOrderCached = false; 0315 } 0316 0317 if ((changes & DatabaseFields::Width) || (changes & DatabaseFields::Height)) 0318 { 0319 (*it)->imageSizeCached = false; 0320 } 0321 0322 if (changes & DatabaseFields::LatitudeNumber || 0323 changes & DatabaseFields::LongitudeNumber || 0324 changes & DatabaseFields::Altitude) 0325 { 0326 (*it)->positionsCached = false; 0327 } 0328 0329 if (changes & DatabaseFields::ImageRelations) 0330 { 0331 (*it)->groupImageCached = false; 0332 m_needUpdateGrouped = true; 0333 } 0334 0335 if (changes.hasFieldsFromVideoMetadata()) 0336 { 0337 const DatabaseFields::VideoMetadata changedVideoMetadata = changes.getVideoMetadata(); 0338 (*it)->videoMetadataCached &= ~changedVideoMetadata; 0339 (*it)->hasVideoMetadata = true; 0340 0341 (*it)->databaseFieldsHashRaw.removeAllFields(changedVideoMetadata); 0342 } 0343 0344 if (changes.hasFieldsFromImageMetadata()) 0345 { 0346 const DatabaseFields::ImageMetadata changedImageMetadata = changes.getImageMetadata(); 0347 (*it)->imageMetadataCached &= ~changedImageMetadata; 0348 (*it)->hasImageMetadata = true; 0349 0350 (*it)->databaseFieldsHashRaw.removeAllFields(changedImageMetadata); 0351 } 0352 } 0353 else 0354 { 0355 m_needUpdateGrouped = true; 0356 } 0357 } 0358 } 0359 0360 void ItemInfoCache::slotImageTagChanged(const ImageTagChangeset& changeset) 0361 { 0362 if (changeset.propertiesWereChanged()) 0363 { 0364 ItemInfoWriteLocker lock; 0365 0366 Q_FOREACH (const qlonglong& imageId, changeset.ids()) 0367 { 0368 QHash<qlonglong, QExplicitlySharedDataPointer<ItemInfoData> >::iterator it = m_infoHash.find(imageId); 0369 0370 if (it != m_infoHash.end()) 0371 { 0372 (*it)->faceCountCached = false; 0373 (*it)->faceSuggestionsCached = false; 0374 (*it)->unconfirmedFaceCountCached = false; 0375 } 0376 } 0377 0378 return; 0379 } 0380 0381 ItemInfoWriteLocker lock; 0382 0383 Q_FOREACH (const qlonglong& imageId, changeset.ids()) 0384 { 0385 QHash<qlonglong, QExplicitlySharedDataPointer<ItemInfoData> >::iterator it = m_infoHash.find(imageId); 0386 0387 if (it != m_infoHash.end()) 0388 { 0389 (*it)->tagIdsCached = false; 0390 (*it)->colorLabelCached = false; 0391 (*it)->pickLabelCached = false; 0392 } 0393 } 0394 } 0395 0396 void ItemInfoCache::slotAlbumChange(const AlbumChangeset& changeset) 0397 { 0398 switch (changeset.operation()) 0399 { 0400 case AlbumChangeset::Added: 0401 case AlbumChangeset::Deleted: 0402 case AlbumChangeset::Renamed: 0403 case AlbumChangeset::PropertiesChanged: 0404 m_needUpdateAlbums = true; 0405 break; 0406 0407 case AlbumChangeset::Unknown: 0408 break; 0409 } 0410 } 0411 0412 } // namespace Digikam 0413 0414 #include "moc_iteminfocache.cpp"