File indexing completed on 2025-03-09 03:52:41

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2007-03-21
0007  * Description : Collection scanning to database - private containers.
0008  *
0009  * SPDX-FileCopyrightText: 2005-2006 by Tom Albers <tomalbers at kde dot nl>
0010  * SPDX-FileCopyrightText: 2007-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
0011  * SPDX-FileCopyrightText: 2009-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0012  *
0013  * SPDX-License-Identifier: GPL-2.0-or-later
0014  *
0015  * ============================================================ */
0016 
0017 #include "collectionscanner_p.h"
0018 
0019 namespace Digikam
0020 {
0021 
0022 bool s_modificationDateEquals(const QDateTime& a, const QDateTime& b)
0023 {
0024     if (!a.isValid() || !b.isValid())
0025     {
0026         return false;
0027     }
0028 
0029     if (a != b)
0030     {
0031         // allow a "modify window" of one second.
0032         // FAT filesystems store the modify date in 2-second resolution.
0033 
0034         int diff = a.secsTo(b);
0035 
0036         if (abs(diff) > 1)
0037         {
0038             return false;
0039         }
0040     }
0041 
0042     return true;
0043 }
0044 
0045 // --------------------------------------------------------------------
0046 
0047 NewlyAppearedFile::NewlyAppearedFile()
0048     : albumId(0)
0049 {
0050 }
0051 
0052 NewlyAppearedFile::NewlyAppearedFile(int albumId, const QString& fileName)
0053     : albumId (albumId),
0054       fileName(fileName)
0055 {
0056 }
0057 
0058 bool NewlyAppearedFile::operator==(const NewlyAppearedFile& other) const
0059 {
0060     return (
0061             (albumId  == other.albumId) &&
0062             (fileName == other.fileName)
0063            );
0064 }
0065 
0066 // --------------------------------------------------------------------
0067 
0068 bool CollectionScannerHintContainerImplementation::hasAnyNormalHint(qlonglong id)
0069 {
0070     QReadLocker locker(&lock);
0071 
0072     return (
0073             modifiedItemHints.contains(id)          ||
0074             rescanItemHints.contains(id)            ||
0075             metadataAboutToAdjustHints.contains(id) ||
0076             metadataAdjustedHints.contains(id)
0077            );
0078 }
0079 
0080 bool CollectionScannerHintContainerImplementation::hasAlbumHints()
0081 {
0082     QReadLocker locker(&lock);
0083 
0084     return !albumHints.isEmpty();
0085 }
0086 
0087 bool CollectionScannerHintContainerImplementation::hasModificationHint(qlonglong id)
0088 {
0089     QReadLocker locker(&lock);
0090 
0091     return modifiedItemHints.contains(id);
0092 }
0093 
0094 bool CollectionScannerHintContainerImplementation::hasRescanHint(qlonglong id)
0095 {
0096     QReadLocker locker(&lock);
0097 
0098     return rescanItemHints.contains(id);
0099 }
0100 
0101 bool CollectionScannerHintContainerImplementation::hasMetadataAboutToAdjustHint(qlonglong id)
0102 {
0103     QReadLocker locker(&lock);
0104 
0105     return metadataAboutToAdjustHints.contains(id);
0106 }
0107 
0108 bool CollectionScannerHintContainerImplementation::hasMetadataAdjustedHint(qlonglong id)
0109 {
0110     QReadLocker locker(&lock);
0111 
0112     return metadataAdjustedHints.contains(id);
0113 }
0114 
0115 void CollectionScannerHintContainerImplementation::recordHints(const QList<AlbumCopyMoveHint>& hints)
0116 {
0117     QWriteLocker locker(&lock);
0118 
0119     Q_FOREACH (const AlbumCopyMoveHint& hint, hints)
0120     {
0121         // automagic casting to src and dst
0122 
0123         albumHints[hint] = hint;
0124     }
0125 }
0126 
0127 void CollectionScannerHintContainerImplementation::recordHints(const QList<ItemCopyMoveHint>& hints)
0128 {
0129     QWriteLocker locker(&lock);
0130 
0131     Q_FOREACH (const ItemCopyMoveHint& hint, hints)
0132     {
0133         QList<qlonglong> ids = hint.srcIds();
0134         QStringList dstNames = hint.dstNames();
0135 
0136         for (int i = 0 ; i < ids.size() ; ++i)
0137         {
0138             itemHints[NewlyAppearedFile(hint.albumIdDst(), dstNames.at(i))] = ids.at(i);
0139         }
0140     }
0141 }
0142 
0143 void CollectionScannerHintContainerImplementation::recordHints(const QList<ItemChangeHint>& hints)
0144 {
0145     QWriteLocker locker(&lock);
0146 
0147     Q_FOREACH (const ItemChangeHint& hint, hints)
0148     {
0149         const QList<qlonglong>& ids = hint.ids();
0150 
0151         for (int i = 0 ; i < ids.size() ; ++i)
0152         {
0153             if (hint.isModified())
0154             {
0155                 modifiedItemHints << ids.at(i);
0156             }
0157             else
0158             {
0159                 rescanItemHints << ids.at(i);
0160             }
0161         }
0162     }
0163 }
0164 
0165 void CollectionScannerHintContainerImplementation::recordHint(const ItemMetadataAdjustmentHint& hint)
0166 {
0167     if (hint.isAboutToEdit())
0168     {
0169         ItemInfo info(hint.id());
0170 
0171         if (!
0172             (s_modificationDateEquals(hint.modificationDate(), info.modDateTime()) &&
0173              (hint.fileSize() == info.fileSize()))
0174            )
0175         {
0176             // refuse to create a hint as a rescan is required already before any metadata edit
0177             // or, in case of multiple edits, there is already a hint with an older date, then all is fine.
0178 
0179             return;
0180         }
0181 
0182         QWriteLocker locker(&lock);
0183         metadataAboutToAdjustHints[hint.id()] = hint.modificationDate();
0184     }
0185     else if (hint.isEditingFinished())
0186     {
0187         QWriteLocker locker(&lock);
0188         QHash<qlonglong, QDateTime>::iterator it = metadataAboutToAdjustHints.find(hint.id());
0189 
0190         if (it == metadataAboutToAdjustHints.end())
0191         {
0192             return;
0193         }
0194 
0195         QDateTime date                   = it.value();
0196         metadataAboutToAdjustHints.erase(it);
0197 
0198         metadataAdjustedHints[hint.id()] = hint.modificationDate();
0199     }
0200     else // Aborted
0201     {
0202          QWriteLocker locker(&lock);
0203          QDateTime formerDate = metadataAboutToAdjustHints.take(hint.id());
0204     }
0205 }
0206 
0207 void CollectionScannerHintContainerImplementation::clear()
0208 {
0209     QWriteLocker locker(&lock);
0210 
0211     albumHints.clear();
0212     itemHints.clear();
0213     modifiedItemHints.clear();
0214     rescanItemHints.clear();
0215     metadataAboutToAdjustHints.clear();
0216     metadataAdjustedHints.clear();
0217 }
0218 
0219 // --------------------------------------------------------------------
0220 
0221 CollectionScanner::Private::Private()
0222     : wantSignals         (false),
0223       needTotalFiles      (false),
0224       performFastScan     (true),
0225       hints               (nullptr),
0226       updatingHashHint    (false),
0227       recordHistoryIds    (false),
0228       deferredFileScanning(false),
0229       observer            (nullptr)
0230 {
0231 }
0232 
0233 void CollectionScanner::Private::resetRemovedItemsTime()
0234 {
0235     removedItemsTime = QDateTime();
0236 }
0237 
0238 void CollectionScanner::Private::removedItems()
0239 {
0240     removedItemsTime = QDateTime::currentDateTime();
0241 }
0242 
0243 bool CollectionScanner::Private::checkObserver()
0244 {
0245     if (observer)
0246     {
0247         return observer->continueQuery();
0248     }
0249 
0250     return true;
0251 }
0252 
0253 bool CollectionScanner::Private::checkDeferred(const QFileInfo& info)
0254 {
0255     if (deferredFileScanning)
0256     {
0257         deferredAlbumPaths << info.path();
0258 
0259         return true;
0260     }
0261 
0262     return false;
0263 }
0264 
0265 void CollectionScanner::Private::finishScanner(ItemScanner& scanner)
0266 {
0267     /**
0268      * Perform the actual write operation to the database
0269      */
0270     {
0271         CoreDbOperationGroup group;
0272         scanner.commit();
0273     }
0274 
0275     if (recordHistoryIds && scanner.hasHistoryToResolve())
0276     {
0277         needResolveHistorySet << scanner.id();
0278     }
0279 }
0280 
0281 } // namespace Digikam