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

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
0008  *
0009  * SPDX-FileCopyrightText: 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
0010  * SPDX-FileCopyrightText: 2012-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 #ifndef DIGIKAM_ITEM_MODEL_H
0017 #define DIGIKAM_ITEM_MODEL_H
0018 
0019 // Qt includes
0020 
0021 #include <QAbstractListModel>
0022 
0023 // Local includes
0024 
0025 #include "dragdropimplementations.h"
0026 #include "iteminfo.h"
0027 #include "digikam_export.h"
0028 
0029 class QItemSelection;
0030 
0031 namespace Digikam
0032 {
0033 
0034 class AlbumChangeset;
0035 class ImageChangeset;
0036 class ImageTagChangeset;
0037 
0038 namespace DatabaseFields
0039 {
0040 class Set;
0041 }
0042 
0043 class DIGIKAM_DATABASE_EXPORT ItemModel : public QAbstractListModel,
0044                                           public DragDropModelImplementation
0045 {
0046     Q_OBJECT
0047 
0048 public:
0049 
0050     enum ItemModelRoles
0051     {
0052         /**
0053          * An ItemModel* pointer to this model
0054          */
0055         ItemModelPointerRole    = Qt::UserRole,
0056         ItemModelInternalId     = Qt::UserRole + 1,
0057 
0058         /**
0059          * Returns a thumbnail pixmap. May be implemented by subclasses.
0060          * Returns either a valid pixmap or a null QVariant.
0061          */
0062         ThumbnailRole           = Qt::UserRole + 2,
0063 
0064         /**
0065          * Returns a QDateTime with the creation date
0066          */
0067         CreationDateRole        = Qt::UserRole + 3,
0068 
0069         /**
0070          * Return (optional) extraData field
0071          */
0072         ExtraDataRole           = Qt::UserRole + 5,
0073 
0074         /**
0075          * Returns the number of duplicate indexes for the same image id
0076          */
0077         ExtraDataDuplicateCount = Qt::UserRole + 6,
0078 
0079         /**
0080          * Roles which are defined here but not implemented by ItemModel
0081          * Returns position of item in Left Light Table preview.
0082          */
0083         LTLeftPanelRole         = Qt::UserRole + 50,
0084 
0085         /**
0086          * Returns position of item in Right Light Table preview.
0087          */
0088         LTRightPanelRole        = Qt::UserRole + 51,
0089 
0090         /**
0091          * For use by subclasses
0092          */
0093         SubclassRoles           = Qt::UserRole + 100,
0094 
0095         /**
0096          * For use by filter models
0097          */
0098         FilterModelRoles        = Qt::UserRole + 500
0099     };
0100 
0101 public:
0102 
0103     explicit ItemModel(QObject* const parent = nullptr);
0104     ~ItemModel() override;
0105 
0106     /**
0107      * If a cache is kept, lookup by file path is fast,
0108      * without a cache it is O(n). Default is false.
0109      */
0110     void setKeepsFilePathCache(bool keepCache);
0111     bool keepsFilePathCache()                                                                               const;
0112 
0113     /**
0114      * Set a set of database fields to watch.
0115      * If either of these is changed, dataChanged() will be emitted.
0116      * Default is no flag (no signal will be emitted).
0117      */
0118     void setWatchFlags(const DatabaseFields::Set& set);
0119 
0120     /**
0121      * Returns the ItemInfo object, reference or image id from the underlying data
0122      * pointed to by the index.
0123      * If the index is not valid, imageInfo will return a null ItemInfo, imageId will
0124      * return 0, imageInfoRef must not be called with an invalid index.
0125      */
0126     ItemInfo         imageInfo(const QModelIndex& index)                                                    const;
0127     ItemInfo&        imageInfoRef(const QModelIndex& index)                                                 const;
0128     qlonglong        imageId(const QModelIndex& index)                                                      const;
0129     QList<ItemInfo>  imageInfos(const QList<QModelIndex>& indexes)                                          const;
0130     QList<qlonglong> imageIds(const QList<QModelIndex>& indexes)                                            const;
0131 
0132     /**
0133      * Returns the ItemInfo object, reference or image id from the underlying data
0134      * of the given row (parent is the invalid QModelIndex, column is 0).
0135      * Note that imageInfoRef will crash if index is invalid.
0136      */
0137     ItemInfo   imageInfo(int row)                                                                           const;
0138     ItemInfo&  imageInfoRef(int row)                                                                        const;
0139     qlonglong  imageId(int row)                                                                             const;
0140 
0141     /**
0142      * Return the index for the given ItemInfo or id, if contained in this model.
0143      */
0144     QModelIndex        indexForItemInfo(const ItemInfo& info)                                               const;
0145     QModelIndex        indexForItemInfo(const ItemInfo& info, const QVariant& extraValue)                   const;
0146     QModelIndex        indexForImageId(qlonglong id)                                                        const;
0147     QModelIndex        indexForImageId(qlonglong id, const QVariant& extraValue)                            const;
0148     QList<QModelIndex> indexesForItemInfo(const ItemInfo& info)                                             const;
0149     QList<QModelIndex> indexesForImageId(qlonglong id)                                                      const;
0150 
0151     int numberOfIndexesForItemInfo(const ItemInfo& info)                                                    const;
0152     int numberOfIndexesForImageId(qlonglong id)                                                             const;
0153 
0154     /**
0155      * Returns the index or ItemInfo object from the underlying data
0156      * for the given file path. This is fast if keepsFilePathCache is enabled.
0157      * The file path is as returned by ItemInfo.filePath().
0158      * In case of multiple occurrences of the same file, the simpler variants return
0159      * any one found first, use the QList methods to retrieve all occurrences.
0160      */
0161     QModelIndex        indexForPath(const QString& filePath)                                                const;
0162     ItemInfo           imageInfo(const QString& filePath)                                                   const;
0163     QList<QModelIndex> indexesForPath(const QString& filePath)                                              const;
0164     QList<ItemInfo>    imageInfos(const QString& filePath)                                                  const;
0165 
0166     /**
0167      * Main entry point for subclasses adding image infos to the model.
0168      * If you list entries not unique per image id, you must add an extraValue
0169      * so that every entry is unique by imageId and extraValues.
0170      * Please note that these methods do not prevent addition of duplicate entries.
0171      */
0172     void addItemInfo(const ItemInfo& info);
0173     void addItemInfos(const QList<ItemInfo>& infos);
0174     void addItemInfos(const QList<ItemInfo>& infos, const QList<QVariant>& extraValues);
0175 
0176     /**
0177      * Clears image infos and resets model.
0178      */
0179     void clearItemInfos();
0180 
0181     /**
0182      * Clears and adds the infos.
0183      */
0184     void setItemInfos(const QList<ItemInfo>& infos);
0185 
0186     /**
0187      * Directly remove the given indexes or infos from the model.
0188      */
0189     void removeIndex(const QModelIndex& indexes);
0190     void removeIndexes(const QList<QModelIndex>& indexes);
0191     void removeItemInfo(const ItemInfo& info);
0192     void removeItemInfos(const QList<ItemInfo>& infos);
0193     void removeItemInfos(const QList<ItemInfo>& infos, const QList<QVariant>& extraValues);
0194 
0195     /**
0196      * addItemInfo() is asynchronous if a prepocessor is set.
0197      * This method first adds the info, synchronously.
0198      * Only afterwards, the preprocessor will have the opportunity to process it.
0199      * This method also bypasses any incremental updates.
0200      * Please note that these methods do not prevent addition of duplicate entries.
0201      */
0202     void addItemInfoSynchronously(const ItemInfo& info);
0203     void addItemInfosSynchronously(const QList<ItemInfo>& infos);
0204     void addItemInfosSynchronously(const QList<ItemInfo>& infos, const QList<QVariant>& extraValues);
0205 
0206     /**
0207      * Add the given entries. Method returns immediately, the
0208      * addition may happen later asynchronously.
0209      * These methods prevent the addition of duplicate entries.
0210      */
0211     void ensureHasItemInfo(const ItemInfo& info);
0212     void ensureHasItemInfos(const QList<ItemInfo>& infos);
0213     void ensureHasItemInfos(const QList<ItemInfo>& infos, const QList<QVariant>& extraValues);
0214 
0215     /**
0216      * Ensure that all images grouped on the given leader are contained in the model.
0217      */
0218     void ensureHasGroupedImages(const ItemInfo& groupLeader);
0219 
0220     QList<ItemInfo>  imageInfos()                                                                           const;
0221     QList<qlonglong> imageIds()                                                                             const;
0222     QList<ItemInfo>  uniqueItemInfos()                                                                      const;
0223 
0224     bool hasImage(qlonglong id)                                                                             const;
0225     bool hasImage(const ItemInfo& info)                                                                     const;
0226     bool hasImage(const ItemInfo& info, const QVariant& extraValue)                                         const;
0227     bool hasImage(qlonglong id, const QVariant& extraValue)                                                 const;
0228 
0229     bool isEmpty()                                                                                          const;
0230     int  itemCount()                                                                                        const;
0231 
0232     // Drag and Drop
0233     DECLARE_MODEL_DRAG_DROP_METHODS
0234 
0235     /**
0236      * Install an object as a preprocessor for ItemInfos added to this model.
0237      * For every QList of ItemInfos added to addItemInfo, the signal preprocess()
0238      * will be emitted. The preprocessor may process the items and shall then readd
0239      * them by calling reAddItemInfos(). It may take some time to process.
0240      * It shall discard any held infos when the modelReset() signal is sent.
0241      * It shall call readdFinished() when no reset occurred and all infos on the way have been readded.
0242      * This means that only after calling this method, you shall make three connections
0243      * (preprocess -> your slot, your signal -> reAddItemInfos, your signal -> reAddingFinished)
0244      * and make or already hold a connection modelReset() -> your slot.
0245      * There is only one preprocessor at a time, a previously set object will be disconnected.
0246      */
0247     void setPreprocessor(QObject* const processor);
0248     void unsetPreprocessor(QObject* const processor);
0249 
0250     /**
0251      * Returns true if this model is currently refreshing.
0252      * For a preprocessor this means that, although the preprocessor may currently have
0253      * processed all it got, more batches are to be expected.
0254      */
0255     bool isRefreshing()                                                                                     const;
0256 
0257     /**
0258      * Enable sending of imageInfosAboutToBeRemoved and imageInfosRemoved signals.
0259      * Default: false
0260      */
0261     void setSendRemovalSignals(bool send);
0262 
0263     QVariant      data(const QModelIndex& index, int role = Qt::DisplayRole)                        const override;
0264     QVariant      headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole)  const override;
0265     int           rowCount(const QModelIndex& parent = QModelIndex())                               const override;
0266     Qt::ItemFlags flags(const QModelIndex& index)                                                   const override;
0267     QModelIndex   index(int row, int column = 0, const QModelIndex& parent = QModelIndex())         const override;
0268 
0269     /**
0270      * Retrieves the imageInfo object from the data() method of the given index.
0271      * The index may be from a QSortFilterProxyModel as long as an ItemModel is at the end.
0272      */
0273     static ItemInfo retrieveItemInfo(const QModelIndex& index);
0274     static qlonglong retrieveImageId(const QModelIndex& index);
0275 
0276 Q_SIGNALS:
0277 
0278     /**
0279      * Informs that ItemInfos will be added to the model.
0280      * This signal is sent before the model data is changed and views are informed.
0281      */
0282     void imageInfosAboutToBeAdded(const QList<ItemInfo>& infos);
0283 
0284     /**
0285      * Informs that ItemInfos have been added to the model.
0286      * This signal is sent after the model data is changed and views are informed.
0287      */
0288     void imageInfosAdded(const QList<ItemInfo>& infos);
0289 
0290     /**
0291      * Informs that ItemInfos will be removed from the model.
0292      * This signal is sent before the model data is changed and views are informed.
0293      * Note: You need to explicitly enable sending of this signal. It is not sent
0294      * in clearItemInfos().
0295      */
0296     void imageInfosAboutToBeRemoved(const QList<ItemInfo>& infos);
0297 
0298     /**
0299      * Informs that ItemInfos have been removed from the model.
0300      * This signal is sent after the model data is changed and views are informed. *
0301      * Note: You need to explicitly enable sending of this signal. It is not sent
0302      * in clearItemInfos().
0303      */
0304     void imageInfosRemoved(const QList<ItemInfo>& infos);
0305 
0306     /**
0307      * Connect to this signal only if you are the current preprocessor.
0308      */
0309     void preprocess(const QList<ItemInfo>& infos, const QList<QVariant>&);
0310     void processAdded(const QList<ItemInfo>& infos, const QList<QVariant>&);
0311 
0312     /**
0313      * If an ImageChangeset affected indexes of this model with changes as set in watchFlags(),
0314      * this signal contains the changeset and the affected indexes.
0315      */
0316     void imageChange(const ImageChangeset&, const QItemSelection&);
0317 
0318     /**
0319      * If an ImageTagChangeset affected indexes of this model,
0320      * this signal contains the changeset and the affected indexes.
0321      */
0322     void imageTagChange(const ImageTagChangeset&, const QItemSelection&);
0323 
0324     /**
0325      * Signals that the model is right now ready to start an incremental refresh.
0326      * This is guaranteed only for the scope of emitting this signal.
0327      */
0328     void readyForIncrementalRefresh();
0329 
0330     /**
0331      * Signals that the model has finished currently with all scheduled
0332      * refreshing, full or incremental, and all preprocessing.
0333      * The model is in polished, clean situation right now.
0334      */
0335     void allRefreshingFinished();
0336 
0337 public Q_SLOTS:
0338 
0339     void reAddItemInfos(const QList<ItemInfo>& infos, const QList<QVariant>& extraValues);
0340     void reAddingFinished();
0341 
0342 protected:
0343 
0344     /**
0345      * Subclasses that add ItemInfos in batches shall call startRefresh()
0346      * when they start sending batches and finishRefresh() when they have finished.
0347      * No incremental refreshes will be started while listing.
0348      * A clearItemInfos() always stops listing, calling finishRefresh() is then not necessary.
0349      */
0350     void startRefresh();
0351     void finishRefresh();
0352 
0353     /**
0354      * As soon as the model is ready to start an incremental refresh, the signal
0355      * readyForIncrementalRefresh() will be emitted. The signal will be emitted inline
0356      * if the model is ready right now.
0357      */
0358     void requestIncrementalRefresh();
0359     bool hasIncrementalRefreshPending()                                                                     const;
0360 
0361     /**
0362      * Starts an incremental refresh operation. You shall only call this method from a slot
0363      * connected to readyForIncrementalRefresh(). To initiate an incremental refresh,
0364      * call requestIncrementalRefresh().
0365      */
0366     void startIncrementalRefresh();
0367     void finishIncrementalRefresh();
0368 
0369     void emitDataChangedForAll();
0370     void emitDataChangedForSelection(const QItemSelection& selection);
0371 
0372     /**
0373      * Called when the internal storage is cleared
0374      */
0375     virtual void imageInfosCleared() {};
0376 
0377     /**
0378      * Called before rowsAboutToBeRemoved
0379      */
0380     virtual void imageInfosAboutToBeRemoved(int /*begin*/, int /*end*/) {};
0381 
0382 protected Q_SLOTS:
0383 
0384     virtual void slotAlbumChange(const AlbumChangeset& changeset);
0385     virtual void slotImageChange(const ImageChangeset& changeset);
0386     virtual void slotImageTagChange(const ImageTagChangeset& changeset);
0387 
0388 private:
0389 
0390     void appendInfos(const QList<ItemInfo>& infos, const QList<QVariant>& extraValues);
0391     void appendInfosChecked(const QList<ItemInfo>& infos, const QList<QVariant>& extraValues);
0392     void publiciseInfos(const QList<ItemInfo>& infos, const QList<QVariant>& extraValues);
0393     void cleanSituationChecks();
0394     void removeRowPairsWithCheck(const QList<QPair<int, int> >& toRemove);
0395     void removeRowPairs(const QList<QPair<int, int> >& toRemove);
0396 
0397 public:
0398 
0399     // Declared public because it's used in ItemModelIncrementalUpdater class
0400     class Private;
0401 
0402 private:
0403 
0404     // Disable
0405     ItemModel(const ItemModel&)            = delete;
0406     ItemModel& operator=(const ItemModel&) = delete;
0407 
0408     Private* const d;
0409 };
0410 
0411 } // namespace Digikam
0412 
0413 Q_DECLARE_METATYPE(Digikam::ItemModel*)
0414 
0415 #endif // DIGIKAM_ITEM_MODEL_H