File indexing completed on 2024-04-28 15:39:57

0001 // SPDX-FileCopyrightText: 2003-2014 Jesper K. Pedersen <jesper.pedersen@kdab.com>
0002 // SPDX-FileCopyrightText: 2006-2010 Tuomas Suutari <tuomas@nepnep.net>
0003 // SPDX-FileCopyrightText: 2007 Dirk Mueller <mueller@kde.org>
0004 // SPDX-FileCopyrightText: 2007-2008 Jan Kundrát <jkt@flaska.net>
0005 // SPDX-FileCopyrightText: 2008-2009 Henner Zeller <h.zeller@acm.org>
0006 // SPDX-FileCopyrightText: 2012-2015 Miika Turkia <miika.turkia@gmail.com>
0007 // SPDX-FileCopyrightText: 2012-2023 Johannes Zarl-Zierl <johannes@zarl-zierl.at>
0008 // SPDX-FileCopyrightText: 2015-2022 Tobias Leupold <tl@stonemx.de>
0009 // SPDX-FileCopyrightText: 2018 Robert Krawitz <rlk@alum.mit.edu>
0010 //
0011 // SPDX-License-Identifier: GPL-2.0-or-later
0012 
0013 #ifndef IMAGEDB_H
0014 #define IMAGEDB_H
0015 
0016 #include "Category.h"
0017 #include "ImageInfoList.h"
0018 #include "ImageInfoPtr.h"
0019 #include "MediaCount.h"
0020 
0021 #include <DB/CategoryCollection.h>
0022 #include <DB/MD5Map.h>
0023 #include <DB/MemberMap.h>
0024 #include <DB/XML/FileReader.h>
0025 #include <DB/search/ImageSearchInfo.h>
0026 #include <kpabase/FileNameList.h>
0027 
0028 #include <QObject>
0029 #include <QPointer>
0030 #include <memory>
0031 
0032 class QProgressBar;
0033 
0034 namespace DB
0035 {
0036 class FileWriter;
0037 }
0038 namespace DB
0039 {
0040 Q_NAMESPACE
0041 
0042 class CategoryCollection;
0043 class Category;
0044 class MD5Map;
0045 class MemberMap;
0046 class FileName;
0047 class TagInfo;
0048 class UIDelegate;
0049 
0050 /**
0051  * @brief The SearchOption enum influences the way a search is conducted.
0052  */
0053 enum class SearchOption {
0054     NoOption = 0b0000, ///< No special query options
0055     RequireOnDisk = 0b0001, ///< Check for each file, if it is currently available on disk and discard any image that is not available.
0056     AllowRangeMatch = 0b0010 ///< In addition to the standard date match (returning images within the given range), allow matching on image dates that overlap with the search range ("range match").
0057 };
0058 Q_DECLARE_FLAGS(SearchOptions, SearchOption);
0059 Q_FLAG_NS(SearchOption);
0060 
0061 /**
0062  * @brief The ClassificationMode enum can be used to short-circuit classification in the classify() method.
0063  * This allows you to only check whether a given category has more than one sub-category (including the "No other" category).
0064  * In other words, you can use a partial count when all you want to know is whether further search refinement is possible
0065  * in a category.
0066  * @see ImageDB::classify()
0067  * @see Browser::OverviewPage::updateImageCount()
0068  */
0069 enum class ClassificationMode {
0070     FullCount ///< @brief run a full classification. This is normally what you want.
0071     ,
0072     PartialCount ///< @brief Count until at least 2 categories are found
0073 };
0074 
0075 class ImageDB : public QObject
0076 {
0077     Q_OBJECT
0078 
0079 public:
0080     static ImageDB *instance();
0081     static void setupXMLDB(const QString &configFile, UIDelegate &delegate);
0082     static void deleteInstance();
0083     static QString NONE();
0084 
0085     UIDelegate &uiDelegate() const;
0086     DB::FileNameList currentScope(bool requireOnDisk) const;
0087 
0088     DB::FileName findFirstItemInRange(const FileNameList &files, const ImageDate &range, bool includeRanges) const;
0089 
0090     bool untaggedCategoryFeatureConfigured() const;
0091 
0092     int totalCount() const;
0093     DB::ImageInfoList search(const ImageSearchInfo &, bool requireOnDisk) const;
0094     DB::ImageInfoList search(const DB::ImageSearchInfo &searchInfo, DB::SearchOptions options = DB::SearchOption::NoOption) const;
0095 
0096     /**
0097      * @brief Rename category in media items stored in database.
0098      */
0099     void renameCategory(const QString &oldName, const QString newName);
0100 
0101     /**
0102      * @brief classify computes a histogram of tags within a category.
0103      * I.e. for each sub-category within a given category it counts all images matching the current context, and
0104      * computes the date range for those images.
0105      *
0106      * @param info ImageSearchInfo describing the current search context
0107      * @param category the category for which images should be classified
0108      * @param typemask images/videos/both
0109      * @param mode whether accurate counts are required or not
0110      * @return a mapping of sub-category (tags/tag-groups) to the number of images (and the associated date range)
0111      */
0112     QMap<QString, CountWithRange> classify(const ImageSearchInfo &info, const QString &category, MediaType typemask, ClassificationMode mode = ClassificationMode::FullCount);
0113     FileNameList files(MediaType type = anyMediaType) const;
0114     ImageInfoList images() const;
0115     /**
0116      * @brief addImages to the database.
0117      * The parameter \p doUpdate decides whether all bookkeeping should be done right away
0118      * (\c true; the "normal" use-case), or if it should be deferred until later(\c false).
0119      * If doUpdate is deferred, either commitDelayedImages() or clearDelayedImages() needs to be called afterwards.
0120      * @param images
0121      * @param doUpdate
0122      */
0123     void addImages(const ImageInfoList &images, bool doUpdate = true);
0124     void commitDelayedImages();
0125     void clearDelayedImages();
0126     /** @short Update file name stored in the DB */
0127     void renameImage(const ImageInfoPtr info, const DB::FileName &newName);
0128 
0129     void addToBlockList(const DB::FileNameList &list);
0130     bool isBlocking(const DB::FileName &fileName);
0131     void deleteList(const DB::FileNameList &list);
0132     ImageInfoPtr info(const DB::FileName &fileName) const;
0133     MemberMap &memberMap();
0134     void save(const QString &fileName, bool isAutoSave);
0135     MD5Map *md5Map();
0136     void sortAndMergeBackIn(const DB::FileNameList &fileNameList);
0137 
0138     CategoryCollection *categoryCollection();
0139     const CategoryCollection *categoryCollection() const;
0140 
0141     /**
0142      * Reorder the items in the database by placing all the items given in
0143      * selection directly before or after the given item.
0144      * If the parameter "after" determines where to place it.
0145      */
0146     void reorder(const DB::FileName &item, const DB::FileNameList &selection, bool after);
0147 
0148     /** @short Create a stack of images/videos/whatever
0149      *
0150      * If the specified images already belong to different stacks, then no
0151      * change happens and the function returns false.
0152      *
0153      * If some of them are in one stack and others aren't stacked at all, then
0154      * the unstacked will be added to the existing stack and we return true.
0155      *
0156      * If none of them are stacked, then a new stack is created and we return
0157      * true.
0158      *
0159      * All images which previously weren't in the stack are added in order they
0160      * are present in the provided list and after all items that are already in
0161      * the stack. The order of images which were already in the stack is not
0162      * changed.
0163      * */
0164     bool stack(const DB::FileNameList &items);
0165 
0166     /** @short Remove all images from whichever stacks they might be in
0167      *
0168      * We might destroy some stacks in the process if they become empty or just
0169      * containing one image.
0170      *
0171      * This function doesn't touch the order of images at all.
0172      * */
0173     void unstack(const DB::FileNameList &items);
0174 
0175     /** @short Return a list of images which are in the same stack as the one specified.
0176      *
0177      * Returns an empty list when the image is not stacked.
0178      *
0179      * They are returned sorted according to their stackOrder.
0180      * */
0181     DB::FileNameList getStackFor(const DB::FileName &referenceImage) const;
0182 
0183     void copyData(const DB::FileName &from, const DB::FileName &to);
0184 
0185     Exif::Database *exifDB() const;
0186 
0187     /**
0188      * @brief untaggedTag
0189      * @return the untaggedTag, or \c nullptr if the feature is not configured
0190      */
0191     const DB::TagInfo *untaggedTag() const;
0192 
0193     static int fileVersion();
0194     static DB::ImageInfoPtr createImageInfo(const DB::FileName &fileName, DB::ReaderPtr, ImageDB *db = nullptr, const QMap<QString, QString> *newToOldCategory = nullptr);
0195     static void possibleLoadCompressedCategories(DB::ReaderPtr reader, DB::ImageInfoPtr info, ImageDB *db, const QMap<QString, QString> *newToOldCategory = nullptr);
0196 public Q_SLOTS:
0197     void setDateRange(const ImageDate &, bool includeFuzzyCounts);
0198     void clearDateRange();
0199     virtual MediaCount count(const ImageSearchInfo &info);
0200     virtual void slotReread(const DB::FileNameList &list, DB::ExifMode mode);
0201     void setCurrentScope(const DB::ImageSearchInfo &info);
0202 
0203 Q_SIGNALS:
0204     void totalChanged(int);
0205     void dirty();
0206     void imagesDeleted(const DB::FileNameList &);
0207 
0208 protected:
0209     ImageDB(const QString &configFile, UIDelegate &delegate);
0210 
0211     bool rangeInclude(DB::ImageInfoPtr info) const;
0212 
0213     DB::ImageInfoList takeImagesFromSelection(const DB::FileNameList &selection);
0214     void insertList(const DB::FileName &fileName, const DB::ImageInfoList &list, bool after);
0215     static void readOptions(DB::ImageInfoPtr info, DB::ReaderPtr reader, const QMap<QString, QString> *newToOldCategory = nullptr);
0216 
0217     ImageInfoList m_clipboard;
0218     UIDelegate &m_UI;
0219     std::unique_ptr<Exif::Database> m_exifDB;
0220     ImageDate m_selectionRange;
0221     bool m_includeFuzzyCounts;
0222 
0223 protected Q_SLOTS:
0224     void renameItem(DB::Category *category, const QString &oldName, const QString &newName);
0225     void deleteItem(DB::Category *category, const QString &value);
0226     void lockDB(bool lock, bool exclude);
0227     void markDirty();
0228     /**
0229      * @brief setUntaggedTag sets the untaggedTag for the database and also updates the corresponding settings value.
0230      * If tag is null, the untaggedTag is cleared.
0231      * @param tag
0232      * @see Settings::SettingsData::untaggedTag()
0233      * @see Settings::SettingsData::untaggedCategory()
0234      */
0235     void setUntaggedTag(DB::TagInfo *tag);
0236     void setUntaggedTag(const QString &category, const QString &tag);
0237 
0238 private:
0239     friend class DB::FileReader;
0240     friend class DB::FileWriter;
0241 
0242     static void connectSlots();
0243     static ImageDB *s_instance;
0244     DB::ImageSearchInfo m_currentScope;
0245     QPointer<DB::TagInfo> m_untaggedTag;
0246 
0247     void forceUpdate(const DB::ImageInfoList &images);
0248 
0249     QString m_fileName;
0250     DB::ImageInfoList m_images;
0251     QSet<DB::FileName> m_blockList;
0252     DB::ImageInfoList m_missingTimes;
0253     DB::CategoryCollection m_categoryCollection;
0254     DB::MemberMap m_members;
0255     DB::MD5Map m_md5map;
0256     // QMap<QString, QString> m_settings;
0257 
0258     DB::StackID m_nextStackId;
0259     typedef QMap<DB::StackID, DB::FileNameList> StackMap;
0260     mutable StackMap m_stackMap;
0261     DB::ImageInfoList m_delayedUpdate;
0262     mutable QHash<const QString, DB::ImageInfoPtr> m_imageCache;
0263     mutable QHash<const QString, DB::ImageInfoPtr> m_delayedCache;
0264 
0265     // used for checking if any images are without image attribute from the database.
0266     static bool s_anyImageWithEmptySize;
0267 };
0268 }
0269 #endif /* IMAGEDB_H */
0270 
0271 // vi:expandtab:tabstop=4 shiftwidth=4: