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

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2005-10-28
0007  * Description : scan item controller.
0008  *
0009  * SPDX-FileCopyrightText: 2005-2006 by Tom Albers <tomalbers at kde dot nl>
0010  * SPDX-FileCopyrightText: 2006-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0011  * SPDX-FileCopyrightText: 2007-2013 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
0012  *
0013  * SPDX-License-Identifier: GPL-2.0-or-later
0014  *
0015  * ============================================================ */
0016 
0017 #ifndef DIGIKAM_SCAN_CONTROLLER_H
0018 #define DIGIKAM_SCAN_CONTROLLER_H
0019 
0020 // Qt includes
0021 
0022 #include <QThread>
0023 #include <QString>
0024 #include <QList>
0025 
0026 // Local includes
0027 
0028 #include "digikam_export.h"
0029 #include "collectionscannerobserver.h"
0030 #include "collectionscannerhints.h"
0031 #include "collectionscanner.h"
0032 #include "iteminfo.h"
0033 #include "loadingcache.h"
0034 #include "coredbchangesets.h"
0035 
0036 namespace Digikam
0037 {
0038 
0039 class CollectionScanner;
0040 class PAlbum;
0041 
0042 class DIGIKAM_GUI_EXPORT ScanController : public QThread,
0043                                           public InitializationObserver
0044 {
0045     Q_OBJECT
0046 
0047 public:
0048 
0049     enum Advice
0050     {
0051         Success,
0052         ContinueWithoutDatabase,
0053         AbortImmediately
0054     };
0055 
0056 public:
0057 
0058     /**
0059      * When writing metadata to the file, the file content on disk changes,
0060      * but the information is taken from the database; therefore,
0061      * the resulting scanning process can be optimized.
0062      *
0063      * Thus, if you write metadata of an ItemInfo from the database to disk,
0064      * do this in the scope of a FileMetadataWrite object.
0065      */
0066     class FileMetadataWrite
0067     {
0068     public:
0069 
0070         explicit FileMetadataWrite(const ItemInfo& info);
0071         ~FileMetadataWrite();
0072 
0073         void changed(bool wasChanged);
0074 
0075     protected:
0076 
0077         ItemInfo m_info;
0078         bool     m_changed;
0079     };
0080 
0081 public:
0082 
0083     static ScanController* instance();
0084 
0085     /**
0086      * Carries out a complete collection scan, at the same time updating
0087      * the unique hash in the database and thumbnail database.
0088      * Synchronous, returns when ready.
0089      * The database will be locked while the scan is running.
0090      */
0091     void updateUniqueHash();
0092 
0093     void allowToScanDeferredFiles();
0094 
0095     /**
0096      * If necessary (modified or newly created, scans the file directly
0097      * Returns the up-to-date ItemInfo.
0098      */
0099     ItemInfo scannedInfo(const QString& filePath,
0100                          CollectionScanner::FileScanMode mode = CollectionScanner::NormalScan);
0101 
0102     /**
0103      * Returns item ids from new detected items
0104      */
0105     QList<qlonglong> getNewIdsList() const;
0106 
0107 protected:
0108 
0109     void run() override;
0110 
0111 private:
0112 
0113     explicit ScanController();
0114     ~ScanController() override;
0115 
0116     void setInitializationMessage();
0117     void createProgressDialog();
0118 
0119     bool continueQuery()                                            override;
0120     void connectCollectionScanner(CollectionScanner* const scanner) override;
0121 
0122     // -----------------------------------------------------------------------------
0123 
0124     /** @name Start Operations
0125      */
0126 
0127     //@{
0128 
0129 public:
0130 
0131     /**
0132      * Restart thread after shutdown.
0133      */
0134     void restart();
0135 
0136     /**
0137      * Calls CoreDbAccess::checkReadyForUse(), providing progress
0138      * feedback if schema updating occurs.
0139      * Synchronous, returns when ready.
0140      */
0141     Advice databaseInitialization();
0142 
0143     /**
0144      * Carries out a complete collection scan, providing progress feedback.
0145      * Synchronous, returns when ready.
0146      * The database will be locked while the scan is running.
0147      * With the DeferFiles variant, deep files scanning (new files), the part
0148      * which can take long, will be done during the time after the method returns,
0149      * shortening the synchronous wait. After completeCollectionScanDeferFiles, you
0150      * need to call allowToScanDeferredFiles() once to enable scanning the deferred files.
0151      */
0152     void completeCollectionScan(bool defer = false);
0153     void completeCollectionScanDeferFiles();
0154 
0155     /**
0156      * Scan Whole collection without to display a progress dialog
0157      * or to manage splashscreen, as for NewItemsFinder tool.
0158      */
0159     void completeCollectionScanInBackground(bool defer, bool fastScan = true);
0160 
0161     /**
0162      * Schedules a scan of the specified part of the collection.
0163      * Asynchronous, returns immediately.
0164      */
0165     void scheduleCollectionScan(const QString& path);
0166 
0167     /**
0168      * Schedules a scan of the specified part of the collection.
0169      * Asynchronous, returns immediately.
0170      * A small delay may be introduced before the actual scanning starts,
0171      * so that you can call this often without checking for duplicates.
0172      * This method must only be used from the main thread.
0173      */
0174     void scheduleCollectionScanRelaxed(const QString& path);
0175 
0176     /**
0177      * Schedules a scan of the specified part of the collection.
0178      * Asynchronous, returns immediately.
0179      * A very long delay with timer restart may be introduced
0180      * before the actual scanning starts, so that you can call
0181      * this often without checking for duplicates.
0182      * This method is only for the QFileSystemWatcher.
0183      */
0184     void scheduleCollectionScanExternal(const QString& path);
0185 
0186     /**
0187      * Implementation of FileMetadataWrite, see there. Calling these methods is equivalent.
0188      */
0189     void beginFileMetadataWrite(const ItemInfo& info);
0190 
0191     /**
0192      * Resume a suspended collection scanning.
0193      * All scheduled scanning tasks are queued
0194      * and will be done when resumeCollectionScan()
0195      * has been called.
0196      * Calling these methods is recursive, you must resume
0197      * as often as you called suspend.
0198      */
0199     void resumeCollectionScan();
0200 
0201     /**
0202      * Restart a suspended collection scanning.
0203      * All scheduled scanning tasks are queued
0204      * and will be done when restartCollectionScan()
0205      * has been called.
0206      */
0207     void restartCollectionScan();
0208 
0209 Q_SIGNALS:
0210 
0211     void databaseInitialized(bool success);
0212     void collectionScanStarted(const QString& message);
0213 
0214 private Q_SLOTS:
0215 
0216     void slotStartCompleteScan();
0217     void slotStartScanningAlbum(const QString& albumRoot, const QString& album);
0218     void slotStartScanningAlbumRoot(const QString& albumRoot);
0219     void slotStartScanningForStaleAlbums();
0220     void slotStartScanningAlbumRoots();
0221 
0222 private:
0223 
0224     /**
0225      * The file pointed to by file path will be scanned.
0226      * The scan is finished when returning from the method.
0227      */
0228     void scanFileDirectly(const QString& filePath);
0229     void scanFileDirectlyNormal(const ItemInfo& info);
0230     void completeCollectionScanCore(bool needTotalFiles, bool defer, bool fastScan);
0231 
0232     //@}
0233 
0234     // -----------------------------------------------------------------------------
0235 
0236     /** @name Progress Operations
0237      */
0238 
0239     //@{
0240 
0241 public:
0242 
0243     /**
0244      * Hint at the imminent copy, move or rename of an album, so that the
0245      * collection scanner is informed about this.
0246      * If the album is renamed, give the new name in newAlbumName.
0247      * DstAlbum is the new parent album /
0248      * dstPath is the new parent directory of the album, so
0249      * do not include the album name to dstPath.
0250      */
0251     void hintAtMoveOrCopyOfAlbum(const PAlbum* const album,
0252                                  const PAlbum* const dstAlbum,
0253                                  const QString& newAlbumName = QString());
0254     void hintAtMoveOrCopyOfAlbum(const PAlbum* const album,
0255                                  const QString& dstPath,
0256                                  const QString& newAlbumName = QString());
0257 
0258     /**
0259      * Hint at the imminent copy, move or rename of items, so that the
0260      * collection scanner is informed about this.
0261      * Give the list of existing items, specify the destination with dstAlbum,
0262      * and give the names at destination in itemNames (Unless for rename, names wont usually change.
0263      * Give them nevertheless.)
0264      */
0265     void hintAtMoveOrCopyOfItems(const QList<qlonglong>& ids,
0266                                  const PAlbum* const dstAlbum,
0267                                  const QStringList& itemNames);
0268     void hintAtMoveOrCopyOfItem(qlonglong id,
0269                                 const PAlbum* const dstAlbum,
0270                                 const QString& itemName);
0271 
0272     /**
0273      * Hint at the fact that an item may have changed, although its modification date may not have changed.
0274      * Note that a scan of the containing directory will need to be triggered nonetheless for the hints to take effect.
0275      */
0276     void hintAtModificationOfItems(const QList<qlonglong>& ids);
0277     void hintAtModificationOfItem(qlonglong id);
0278 
0279 Q_SIGNALS:
0280 
0281     void totalFilesToScan(int);
0282     void filesScanned(int);
0283     void scanningProgress(float progress);
0284     void triggerShowProgressDialog();
0285     void incrementProgressDialog(int);
0286     void progressFromInitialization(const QString&, int);
0287 
0288 private Q_SLOTS:
0289 
0290     void slotTotalFilesToScan(int count);
0291     void slotScannedFiles(int scanned);
0292     void slotShowProgressDialog();
0293     void slotTriggerShowProgressDialog();
0294     void slotProgressFromInitialization(const QString& message, int numberOfSteps);
0295     void slotErrorFromInitialization(const QString& errorMessage);
0296 
0297 private:
0298 
0299     void moreSchemaUpdateSteps(int numberOfSteps)                        override;
0300     void schemaUpdateProgress(const QString& message, int numberOfSteps) override;
0301     void error(const QString& errorMessage) override;
0302 
0303     AlbumCopyMoveHint hintForAlbum(const PAlbum* const album,
0304                                    int dstAlbumRootId,
0305                                    const QString& relativeDstPath,
0306                                    const QString& albumName);
0307 
0308     QList<AlbumCopyMoveHint> hintsForAlbum(const PAlbum* const album,
0309                                            int dstAlbumRootId,
0310                                            const QString& relativeDstPath,
0311                                            const QString& albumName);
0312     //@}
0313 
0314     // -----------------------------------------------------------------------------
0315 
0316     /** @name Stop Operations
0317      */
0318 
0319     //@{
0320 
0321 public:
0322 
0323     /**
0324      * Wait for the thread to finish. Returns after all tasks are done.
0325      */
0326     void shutDown();
0327 
0328     /**
0329      * If the controller is currently processing a database update
0330      * (typically after first run),
0331      * cancel this hard and as soon as possible. Any progress may be lost.
0332      */
0333     void abortInitialization();
0334 
0335     /**
0336      * If the controller is currently doing a complete scan
0337      * (typically at startup), stop this operation.
0338      * It can be resumed later.
0339      */
0340     void cancelCompleteScan();
0341 
0342     /**
0343      * Cancels all running or scheduled operations and suspends scanning.
0344      * This method returns when all scanning has stopped.
0345      * This includes a call to suspendCollectionScan().
0346      * Restart with resumeCollectionScan.
0347      */
0348     void cancelAllAndSuspendCollectionScan();
0349 
0350     /**
0351      * Temporarily suspend collection scanning.
0352      * All scheduled scanning tasks are queued
0353      * and will be done when resumeCollectionScan()
0354      * has been called.
0355      * Calling these methods is recursive, you must resume
0356      * as often as you called suspend.
0357      */
0358     void suspendCollectionScan();
0359 
0360     /**
0361      * Implementation of FileMetadataWrite, see there. Calling these methods is equivalent.
0362      */
0363     void finishFileMetadataWrite(const ItemInfo& info, bool changed);
0364 
0365 Q_SIGNALS:
0366 
0367     void collectionScanFinished();
0368     void newImages(const ItemInfoList&);
0369     void partialScanDone(const QString& path);
0370     void completeScanDone();
0371     void completeScanCanceled();
0372     void errorFromInitialization(const QString&);
0373 
0374 private Q_SLOTS:
0375 
0376     void slotCancelPressed();
0377     void slotRelaxedScanning();
0378 
0379 private:
0380 
0381     void finishedSchemaUpdate(UpdateResult result) override;
0382 
0383     //@}
0384 
0385 private:
0386 
0387     // Disable
0388     ScanController(QObject*)                         = delete;
0389     ScanController(const ScanController&)            = delete;
0390     ScanController& operator=(const ScanController&) = delete;
0391 
0392 private:
0393 
0394     friend class ScanControllerCreator;
0395 
0396     class Private;
0397     Private* const d;
0398 };
0399 
0400 } // namespace Digikam
0401 
0402 #endif // DIGIKAM_SCAN_CONTROLLER_H