1 /* ============================================================ 2 * 3 * This file is a part of digiKam project 4 * https://www.digikam.org 5 * 6 * Date : 2009-03-05 7 * Description : Qt item model for database entries 8 * 9 * Copyright (C) 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de> 10 * Copyright (C) 2012-2021 by Gilles Caulier <caulier dot gilles at gmail dot com> 11 * 12 * This program is free software; you can redistribute it 13 * and/or modify it under the terms of the GNU General 14 * Public License as published by the Free Software Foundation; 15 * either version 2, or (at your option) 16 * any later version. 17 * 18 * This program is distributed in the hope that it will be useful, 19 * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 * GNU General Public License for more details. 22 * 23 * ============================================================ */ 24 25 #ifndef DIGIKAM_ITEM_MODEL_H 26 #define DIGIKAM_ITEM_MODEL_H 27 28 // Qt includes 29 30 #include <QAbstractListModel> 31 32 // Local includes 33 34 #include "dragdropimplementations.h" 35 #include "iteminfo.h" 36 #include "digikam_export.h" 37 38 class QItemSelection; 39 40 namespace Digikam 41 { 42 43 class ImageChangeset; 44 class ImageTagChangeset; 45 46 namespace DatabaseFields 47 { 48 class Set; 49 } 50 51 class DIGIKAM_DATABASE_EXPORT ItemModel : public QAbstractListModel, 52 public DragDropModelImplementation 53 { 54 Q_OBJECT 55 56 public: 57 58 enum ItemModelRoles 59 { 60 /** 61 * An ItemModel* pointer to this model 62 */ 63 ItemModelPointerRole = Qt::UserRole, 64 ItemModelInternalId = Qt::UserRole + 1, 65 66 /** 67 * Returns a thumbnail pixmap. May be implemented by subclasses. 68 * Returns either a valid pixmap or a null QVariant. 69 */ 70 ThumbnailRole = Qt::UserRole + 2, 71 72 /** 73 * Returns a QDateTime with the creation date 74 */ 75 CreationDateRole = Qt::UserRole + 3, 76 77 /** 78 * Return (optional) extraData field 79 */ 80 ExtraDataRole = Qt::UserRole + 5, 81 82 /** 83 * Returns the number of duplicate indexes for the same image id 84 */ 85 ExtraDataDuplicateCount = Qt::UserRole + 6, 86 87 /** 88 * Roles which are defined here but not implemented by ItemModel 89 * Returns position of item in Left Light Table preview. 90 */ 91 LTLeftPanelRole = Qt::UserRole + 50, 92 93 /** 94 * Returns position of item in Right Light Table preview. 95 */ 96 LTRightPanelRole = Qt::UserRole + 51, 97 98 /** 99 * For use by subclasses 100 */ 101 SubclassRoles = Qt::UserRole + 100, 102 103 /** 104 * For use by filter models 105 */ 106 FilterModelRoles = Qt::UserRole + 500 107 }; 108 109 public: 110 111 explicit ItemModel(QObject* const parent = nullptr); 112 ~ItemModel() override; 113 114 /** 115 * If a cache is kept, lookup by file path is fast, 116 * without a cache it is O(n). Default is false. 117 */ 118 void setKeepsFilePathCache(bool keepCache); 119 bool keepsFilePathCache() const; 120 121 /** 122 * Set a set of database fields to watch. 123 * If either of these is changed, dataChanged() will be emitted. 124 * Default is no flag (no signal will be emitted). 125 */ 126 void setWatchFlags(const DatabaseFields::Set& set); 127 128 /** 129 * Returns the ItemInfo object, reference or image id from the underlying data 130 * pointed to by the index. 131 * If the index is not valid, imageInfo will return a null ItemInfo, imageId will 132 * return 0, imageInfoRef must not be called with an invalid index. 133 */ 134 ItemInfo imageInfo(const QModelIndex& index) const; 135 ItemInfo& imageInfoRef(const QModelIndex& index) const; 136 qlonglong imageId(const QModelIndex& index) const; 137 QList<ItemInfo> imageInfos(const QList<QModelIndex>& indexes) const; 138 QList<qlonglong> imageIds(const QList<QModelIndex>& indexes) const; 139 140 /** 141 * Returns the ItemInfo object, reference or image id from the underlying data 142 * of the given row (parent is the invalid QModelIndex, column is 0). 143 * Note that imageInfoRef will crash if index is invalid. 144 */ 145 ItemInfo imageInfo(int row) const; 146 ItemInfo& imageInfoRef(int row) const; 147 qlonglong imageId(int row) const; 148 149 /** 150 * Return the index for the given ItemInfo or id, if contained in this model. 151 */ 152 QModelIndex indexForItemInfo(const ItemInfo& info) const; 153 QModelIndex indexForItemInfo(const ItemInfo& info, const QVariant& extraValue) const; 154 QModelIndex indexForImageId(qlonglong id) const; 155 QModelIndex indexForImageId(qlonglong id, const QVariant& extraValue) const; 156 QList<QModelIndex> indexesForItemInfo(const ItemInfo& info) const; 157 QList<QModelIndex> indexesForImageId(qlonglong id) const; 158 159 int numberOfIndexesForItemInfo(const ItemInfo& info) const; 160 int numberOfIndexesForImageId(qlonglong id) const; 161 162 /** 163 * Returns the index or ItemInfo object from the underlying data 164 * for the given file path. This is fast if keepsFilePathCache is enabled. 165 * The file path is as returned by ItemInfo.filePath(). 166 * In case of multiple occurrences of the same file, the simpler variants return 167 * any one found first, use the QList methods to retrieve all occurrences. 168 */ 169 QModelIndex indexForPath(const QString& filePath) const; 170 ItemInfo imageInfo(const QString& filePath) const; 171 QList<QModelIndex> indexesForPath(const QString& filePath) const; 172 QList<ItemInfo> imageInfos(const QString& filePath) const; 173 174 /** 175 * Main entry point for subclasses adding image infos to the model. 176 * If you list entries not unique per image id, you must add an extraValue 177 * so that every entry is unique by imageId and extraValues. 178 * Please note that these methods do not prevent addition of duplicate entries. 179 */ 180 void addItemInfo(const ItemInfo& info); 181 void addItemInfos(const QList<ItemInfo>& infos); 182 void addItemInfos(const QList<ItemInfo>& infos, const QList<QVariant>& extraValues); 183 184 /** 185 * Clears image infos and resets model. 186 */ 187 void clearItemInfos(); 188 189 /** 190 * Clears and adds the infos. 191 */ 192 void setItemInfos(const QList<ItemInfo>& infos); 193 194 /** 195 * Directly remove the given indexes or infos from the model. 196 */ 197 void removeIndex(const QModelIndex& indexes); 198 void removeIndexes(const QList<QModelIndex>& indexes); 199 void removeItemInfo(const ItemInfo& info); 200 void removeItemInfos(const QList<ItemInfo>& infos); 201 void removeItemInfos(const QList<ItemInfo>& infos, const QList<QVariant>& extraValues); 202 203 /** 204 * addItemInfo() is asynchronous if a prepocessor is set. 205 * This method first adds the info, synchronously. 206 * Only afterwards, the preprocessor will have the opportunity to process it. 207 * This method also bypasses any incremental updates. 208 * Please note that these methods do not prevent addition of duplicate entries. 209 */ 210 void addItemInfoSynchronously(const ItemInfo& info); 211 void addItemInfosSynchronously(const QList<ItemInfo>& infos); 212 void addItemInfosSynchronously(const QList<ItemInfo>& infos, const QList<QVariant>& extraValues); 213 214 /** 215 * Add the given entries. Method returns immediately, the 216 * addition may happen later asynchronously. 217 * These methods prevent the addition of duplicate entries. 218 */ 219 void ensureHasItemInfo(const ItemInfo& info); 220 void ensureHasItemInfos(const QList<ItemInfo>& infos); 221 void ensureHasItemInfos(const QList<ItemInfo>& infos, const QList<QVariant>& extraValues); 222 223 /** 224 * Ensure that all images grouped on the given leader are contained in the model. 225 */ 226 void ensureHasGroupedImages(const ItemInfo& groupLeader); 227 228 QList<ItemInfo> imageInfos() const; 229 QList<qlonglong> imageIds() const; 230 QList<ItemInfo> uniqueItemInfos() const; 231 232 bool hasImage(qlonglong id) const; 233 bool hasImage(const ItemInfo& info) const; 234 bool hasImage(const ItemInfo& info, const QVariant& extraValue) const; 235 bool hasImage(qlonglong id, const QVariant& extraValue) const; 236 237 bool isEmpty() const; 238 int itemCount() const; 239 240 // Drag and Drop 241 DECLARE_MODEL_DRAG_DROP_METHODS 242 243 /** 244 * Install an object as a preprocessor for ItemInfos added to this model. 245 * For every QList of ItemInfos added to addItemInfo, the signal preprocess() 246 * will be emitted. The preprocessor may process the items and shall then readd 247 * them by calling reAddItemInfos(). It may take some time to process. 248 * It shall discard any held infos when the modelReset() signal is sent. 249 * It shall call readdFinished() when no reset occurred and all infos on the way have been readded. 250 * This means that only after calling this method, you shall make three connections 251 * (preprocess -> your slot, your signal -> reAddItemInfos, your signal -> reAddingFinished) 252 * and make or already hold a connection modelReset() -> your slot. 253 * There is only one preprocessor at a time, a previously set object will be disconnected. 254 */ 255 void setPreprocessor(QObject* const processor); 256 void unsetPreprocessor(QObject* const processor); 257 258 /** 259 * Returns true if this model is currently refreshing. 260 * For a preprocessor this means that, although the preprocessor may currently have 261 * processed all it got, more batches are to be expected. 262 */ 263 bool isRefreshing() const; 264 265 /** 266 * Enable sending of imageInfosAboutToBeRemoved and imageInfosRemoved signals. 267 * Default: false 268 */ 269 void setSendRemovalSignals(bool send); 270 271 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; 272 QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; 273 int rowCount(const QModelIndex& parent = QModelIndex()) const override; 274 Qt::ItemFlags flags(const QModelIndex& index) const override; 275 QModelIndex index(int row, int column = 0, const QModelIndex& parent = QModelIndex()) const override; 276 277 /** 278 * Retrieves the imageInfo object from the data() method of the given index. 279 * The index may be from a QSortFilterProxyModel as long as an ItemModel is at the end. 280 */ 281 static ItemInfo retrieveItemInfo(const QModelIndex& index); 282 static qlonglong retrieveImageId(const QModelIndex& index); 283 284 Q_SIGNALS: 285 286 /** 287 * Informs that ItemInfos will be added to the model. 288 * This signal is sent before the model data is changed and views are informed. 289 */ 290 void imageInfosAboutToBeAdded(const QList<ItemInfo>& infos); 291 292 /** 293 * Informs that ItemInfos have been added to the model. 294 * This signal is sent after the model data is changed and views are informed. 295 */ 296 void imageInfosAdded(const QList<ItemInfo>& infos); 297 298 /** 299 * Informs that ItemInfos will be removed from the model. 300 * This signal is sent before the model data is changed and views are informed. 301 * Note: You need to explicitly enable sending of this signal. It is not sent 302 * in clearItemInfos(). 303 */ 304 void imageInfosAboutToBeRemoved(const QList<ItemInfo>& infos); 305 306 /** 307 * Informs that ItemInfos have been removed from the model. 308 * This signal is sent after the model data is changed and views are informed. * 309 * Note: You need to explicitly enable sending of this signal. It is not sent 310 * in clearItemInfos(). 311 */ 312 void imageInfosRemoved(const QList<ItemInfo>& infos); 313 314 /** 315 * Connect to this signal only if you are the current preprocessor. 316 */ 317 void preprocess(const QList<ItemInfo>& infos, const QList<QVariant>&); 318 void processAdded(const QList<ItemInfo>& infos, const QList<QVariant>&); 319 320 /** 321 * If an ImageChangeset affected indexes of this model with changes as set in watchFlags(), 322 * this signal contains the changeset and the affected indexes. 323 */ 324 void imageChange(const ImageChangeset&, const QItemSelection&); 325 326 /** 327 * If an ImageTagChangeset affected indexes of this model, 328 * this signal contains the changeset and the affected indexes. 329 */ 330 void imageTagChange(const ImageTagChangeset&, const QItemSelection&); 331 332 /** 333 * Signals that the model is right now ready to start an incremental refresh. 334 * This is guaranteed only for the scope of emitting this signal. 335 */ 336 void readyForIncrementalRefresh(); 337 338 /** 339 * Signals that the model has finished currently with all scheduled 340 * refreshing, full or incremental, and all preprocessing. 341 * The model is in polished, clean situation right now. 342 */ 343 void allRefreshingFinished(); 344 345 public Q_SLOTS: 346 347 void reAddItemInfos(const QList<ItemInfo>& infos, const QList<QVariant>& extraValues); 348 void reAddingFinished(); 349 350 protected: 351 352 /** 353 * Subclasses that add ItemInfos in batches shall call startRefresh() 354 * when they start sending batches and finishRefresh() when they have finished. 355 * No incremental refreshes will be started while listing. 356 * A clearItemInfos() always stops listing, calling finishRefresh() is then not necessary. 357 */ 358 void startRefresh(); 359 void finishRefresh(); 360 361 /** 362 * As soon as the model is ready to start an incremental refresh, the signal 363 * readyForIncrementalRefresh() will be emitted. The signal will be emitted inline 364 * if the model is ready right now. 365 */ 366 void requestIncrementalRefresh(); 367 bool hasIncrementalRefreshPending() const; 368 369 /** 370 * Starts an incremental refresh operation. You shall only call this method from a slot 371 * connected to readyForIncrementalRefresh(). To initiate an incremental refresh, 372 * call requestIncrementalRefresh(). 373 */ 374 void startIncrementalRefresh(); 375 void finishIncrementalRefresh(); 376 377 void emitDataChangedForAll(); 378 void emitDataChangedForSelection(const QItemSelection& selection); 379 380 /** 381 * Called when the internal storage is cleared 382 */ imageInfosCleared()383 virtual void imageInfosCleared() {}; 384 385 /** 386 * Called before rowsAboutToBeRemoved 387 */ imageInfosAboutToBeRemoved(int,int)388 virtual void imageInfosAboutToBeRemoved(int /*begin*/, int /*end*/) {}; 389 390 protected Q_SLOTS: 391 392 virtual void slotImageChange(const ImageChangeset& changeset); 393 virtual void slotImageTagChange(const ImageTagChangeset& changeset); 394 395 private: 396 397 void appendInfos(const QList<ItemInfo>& infos, const QList<QVariant>& extraValues); 398 void appendInfosChecked(const QList<ItemInfo>& infos, const QList<QVariant>& extraValues); 399 void publiciseInfos(const QList<ItemInfo>& infos, const QList<QVariant>& extraValues); 400 void cleanSituationChecks(); 401 void removeRowPairsWithCheck(const QList<QPair<int, int> >& toRemove); 402 void removeRowPairs(const QList<QPair<int, int> >& toRemove); 403 404 public: 405 406 // Declared public because it's used in ItemModelIncrementalUpdater class 407 class Private; 408 409 private: 410 411 // Disable 412 ItemModel(const ItemModel&) = delete; 413 ItemModel& operator=(const ItemModel&) = delete; 414 415 Private* const d; 416 }; 417 418 } // namespace Digikam 419 420 Q_DECLARE_METATYPE(Digikam::ItemModel*) 421 422 #endif // DIGIKAM_ITEM_MODEL_H 423