1 /* ============================================================
2  *
3  * This file is a part of digiKam project
4  * https://www.digikam.org
5  *
6  * Date        : 2009-03-23
7  * Description : Qt Model for Albums
8  *
9  * Copyright (C) 2008-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
10  * Copyright (C) 2010      by Andi Clemens <andi dot clemens at gmail dot com>
11  * Copyright (C) 2012-2021 by Gilles Caulier <caulier dot gilles at gmail dot com>
12  *
13  * This program is free software; you can redistribute it
14  * and/or modify it under the terms of the GNU General
15  * Public License as published by the Free Software Foundation;
16  * either version 2, or (at your option)
17  * any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  *
24  * ============================================================ */
25 
26 #ifndef DIGIKAM_ABSTRACT_ALBUM_MODEL_H
27 #define DIGIKAM_ABSTRACT_ALBUM_MODEL_H
28 
29 // Qt includes
30 
31 #include <QAbstractItemModel>
32 #include <QHash>
33 #include <QPixmap>
34 #include <QSet>
35 
36 // Local includes
37 
38 #include "album.h"
39 #include "digikam_export.h"
40 
41 namespace Digikam
42 {
43 
44 class Album;
45 class AlbumManager;
46 class AlbumModelDragDropHandler;
47 
48 class DIGIKAM_GUI_EXPORT AbstractAlbumModel : public QAbstractItemModel
49 {
50     Q_OBJECT
51 
52 public:
53 
54     /**
55      *  AbstractAlbumModel is the abstract base class for all models that
56      *  present Album objects as managed by AlbumManager.
57      *  You will want to create an instance of the base classes.
58      */
59     enum RootAlbumBehavior
60     {
61         /**
62          * The root album will be included as a single parent item
63          * with all top-level album as children
64          */
65         IncludeRootAlbum,
66         /**
67          * The root album will not be included, but all top-level album
68          * are represented as top-level items in this view
69          */
70         IgnoreRootAlbum
71     };
72 
73     enum AlbumDataRole
74     {
75         /// Returns the album title. Principally the same as display role, but without any additions.
76         AlbumTitleRole    = Qt::UserRole,
77         /// Returns the Album::Type of the associated album
78         AlbumTypeRole     = Qt::UserRole + 1,
79         /// Returns a pointer to the associated Album object
80         AlbumPointerRole  = Qt::UserRole + 2,
81         /// Returns the id of the associated Album object
82         AlbumIdRole       = Qt::UserRole + 3,
83         /// Returns the global id (unique across all album types)
84         AlbumGlobalIdRole = Qt::UserRole + 4,
85         /// Returns the data to sort on
86         AlbumSortRole     = Qt::UserRole + 5
87     };
88 
89 public:
90 
91     /**
92      * Create an AbstractAlbumModel object for albums with the given type.
93      * Pass the root album if it is already available.
94      * Do not use this class directly, but one of the subclasses.
95      */
96     explicit AbstractAlbumModel(Album::Type albumType,
97                                 Album* const rootAlbum,
98                                 RootAlbumBehavior rootBehavior = IncludeRootAlbum,
99                                 QObject* const parent = nullptr);
100     ~AbstractAlbumModel() override;
101 
102     /**
103      * Set a drag drop handler
104      */
105     void setDragDropHandler(AlbumModelDragDropHandler* handler);
106 
107     /**
108      * Returns the drag drop handler, or 0 if none is installed
109      */
110     AlbumModelDragDropHandler* dragDropHandler()    const;
111 
112     /**
113      * Returns the album object associated with the given model index
114      */
115     Album* albumForIndex(const QModelIndex& index)  const;
116 
117     /**
118      * Return the QModelIndex for the given album, or an invalid index if
119      * the album is not contained in this model.
120      */
121     QModelIndex indexForAlbum(Album* album)         const;
122 
123     /**
124      * Returns the album represented by the index. In contrast to albumForIndex(),
125      * the index can be from any proxy model, as long as an AbstractAlbumModel is at the end.
126      */
127     static Album* retrieveAlbum(const QModelIndex& index);
128 
129     Album* rootAlbum()                              const;
130 
131     /**
132      * Return the index corresponding to the root album. If the policy is IgnoreRootAlbum, this is an invalid index.
133      */
134     QModelIndex rootAlbumIndex()                    const;
135 
136     /**
137      * Returns the root album behavior set for this model
138      */
139     RootAlbumBehavior rootAlbumBehavior()           const;
140 
141     /**
142      * Returns the Album::Type of the contained albums
143      */
144     Album::Type albumType()                         const;
145 
146     /**
147      * Returns true if the album model a face tag model
148      */
149     bool isFaceTagModel()                           const;
150 
151     QVariant data(const QModelIndex& index, int role = Qt::DisplayRole)                                             const override;
152     QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole)                       const override;
153     int rowCount(const QModelIndex& parent = QModelIndex())                                                         const override;
154     int columnCount(const QModelIndex& parent = QModelIndex())                                                      const override;
155     Qt::ItemFlags flags(const QModelIndex& index)                                                                   const override;
156     bool hasChildren(const QModelIndex& parent = QModelIndex())                                                     const override;
157     QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex())                               const override;
158     QModelIndex parent(const QModelIndex& index)                                                                    const override;
159 
160     Qt::DropActions supportedDropActions()                                                                          const override;
161     QStringList mimeTypes()                                                                                         const override;
162     bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent)       override;
163     QMimeData* mimeData(const QModelIndexList& indexes)                                                             const override;
164 
165 Q_SIGNALS:
166 
167     /**
168      * This is initialized once after creation, if the root album becomes available,
169      * if it was not already available at time of construction.
170      * This is emitted regardless of root album policy.
171      */
172     void rootAlbumAvailable();
173 
174 protected:
175 
176     /**
177      * Switch on drag and drop globally for all items. Default is true.
178      * For per-item cases reimplement itemFlags().
179      */
180     void setEnableDrag(bool enable);
181     void setEnableDrop(bool enable);
182     void setFaceTagModel(bool enable);
183 
184     /**
185      * NOTE: these can be reimplemented in a subclass
186      */
187 
188     /// For subclassing convenience: A part of the implementation of data()
189     virtual QVariant albumData(Album* a, int role)  const;
190 
191     /// For subclassing convenience: A part of the implementation of data()
192     virtual QVariant decorationRoleData(Album* a)   const;
193 
194     /// For subclassing convenience: A part of the implementation of data()
195     virtual QVariant fontRoleData(Album* a)         const;
196 
197     /// For subclassing convenience: A part of the implementation of data()
198     virtual QVariant sortRoleData(Album* a)         const;
199 
200     /// For subclassing convenience: A part of the implementation of headerData()
201     virtual QString columnHeader()                  const;
202 
203     /// For subclassing convenience: A part of the implementation of itemFlags()
204     virtual Qt::ItemFlags itemFlags(Album* album)   const;
205 
206     /**
207      * Returns true for those and only those albums that shall be contained in this model.
208      * They must have a common root album, which is set in the constructor.
209      */
210     virtual bool filterAlbum(Album* album)          const;
211 
212     /// Notification when an entry is removed
albumCleared(Album *)213     virtual void albumCleared(Album* /*album*/) {};
214 
215     /// Notification when all entries are removed
allAlbumsCleared()216     virtual void allAlbumsCleared()             {};
217 
218 protected Q_SLOTS:
219 
220     void slotAlbumAboutToBeAdded(Album* album, Album* parent, Album* prev);
221     void slotAlbumAdded(Album*);
222     void slotAlbumAboutToBeDeleted(Album* album);
223     void slotAlbumHasBeenDeleted(quintptr);
224     void slotAlbumsCleared();
225     void slotAlbumIconChanged(Album* album);
226     void slotAlbumRenamed(Album* album);
227 
228 private:
229 
230     class Private;
231     Private* const d;
232 };
233 
234 // ------------------------------------------------------------------
235 
236 class DIGIKAM_GUI_EXPORT AbstractSpecificAlbumModel : public AbstractAlbumModel
237 {
238     Q_OBJECT
239 
240 public:
241 
242     /// Abstract base class, do not instantiate.
243     explicit AbstractSpecificAlbumModel(Album::Type albumType,
244                                         Album* const rootAlbum,
245                                         RootAlbumBehavior rootBehavior = IncludeRootAlbum,
246                                         QObject* const parent = nullptr);
247 
248 protected:
249 
250     QString  columnHeader() const override;
251     void setColumnHeader(const QString& header);
252 
253     /// You need to call this from your constructor if you intend to load the thumbnail facilities of this class
254     void setupThumbnailLoading();
255     void emitDataChangedForChildren(Album* album);
256 
257 protected Q_SLOTS:
258 
259     void slotGotThumbnailFromIcon(Album* album, const QPixmap& thumbnail);
260     void slotThumbnailLost(Album* album);
261     void slotReloadThumbnails();
262 
263 protected:
264 
265     QString m_columnHeader;
266 };
267 
268 // ------------------------------------------------------------------
269 
270 class DIGIKAM_GUI_EXPORT AbstractCountingAlbumModel : public AbstractSpecificAlbumModel
271 {
272     Q_OBJECT
273 
274 public:
275 
276     /// Supports displaying a count alongside the album name in DisplayRole
277 
278     explicit AbstractCountingAlbumModel(Album::Type albumType,
279                                         Album* const rootAlbum,
280                                         RootAlbumBehavior rootBehavior = IncludeRootAlbum,
281                                         QObject* const parent = nullptr);
282     ~AbstractCountingAlbumModel() override;
283 
284     bool showCount()                        const;
285 
286     /**
287      * Returns the number of included items for this album.
288      *
289      * @return positive value or -1 if unknown
290      */
291     virtual int albumCount(Album* album)    const;
292 
293 protected:
294 
295     /**
296      * Call this method in children class contructors to init signal/slots connections.
297      */
298     void setup();
299 
300 public Q_SLOTS:
301 
302     /// Call to enable or disable showing the count. Default is false.
303     void setShowCount(bool show);
304 
305     /**
306      * Enable displaying the count. Set a map of album id -> count (excluding children).
307      * If an album is not contained, no count is displayed. To display a count of 0,
308      * there must be an entry album id -> 0.
309      */
310     void setCountMap(const QMap<int, int>& idCountMap);
311 
312     /**
313      * Displays only the count of the album, without adding child albums' counts.
314      * This is the default.
315      * Can connect to QTreeView's expanded() signal.
316      */
317     void excludeChildrenCount(const QModelIndex& index);
318 
319     /**
320      * Displays sum of the count of the album and child albums' counts.
321      * Can connect to QTreeView's collapsed() signal.
322      */
323     void includeChildrenCount(const QModelIndex& index);
324 
325 protected:
326 
327     /// If you do not use setCountMap, excludeChildrenCount and includeChildrenCount, you can set a count here.
328     void setCount(Album* album, int count);
329 
330     /// need to implement in subclass
331     virtual Album* albumForId(int id)       const = 0;
332 
333     /// Can reimplement in subclass
334     virtual QString albumName(Album* a)     const;
335 
336     /// Reimplemented from parent classes
337     QVariant albumData(Album* a, int role)  const override;
338     void albumCleared(Album* album) override;
339     void allAlbumsCleared() override;
340 
341 protected Q_SLOTS:
342 
343     void slotAlbumMoved(Album* album);
344 
345 private:
346 
347     void updateCount(Album* album);
348 
349 private:
350 
351     class Private;
352     Private* const d;
353 };
354 
355 // ------------------------------------------------------------------
356 
357 class DIGIKAM_GUI_EXPORT AbstractCheckableAlbumModel : public AbstractCountingAlbumModel
358 {
359     Q_OBJECT
360 
361 public:
362 
363     /**
364      * Abstract base class that manages the check state of Albums.
365      * Call setCheckable(true) to enable checkable albums.
366      */
367 
368     explicit AbstractCheckableAlbumModel(Album::Type albumType,
369                                          Album* const rootAlbum,
370                                          RootAlbumBehavior rootBehavior = IncludeRootAlbum,
371                                          QObject* const parent = nullptr);
372     ~AbstractCheckableAlbumModel() override;
373 
374     /// Triggers if the albums in this model are checkable
375     void setCheckable(bool isCheckable);
376     bool isCheckable()                      const;
377 
378     /**
379      * Triggers if the root album is checkable.
380      * Only applicable if the root album is contained at all, and if isCheckable() is true.
381      */
382     void setRootCheckable(bool rootIsCheckable);
383     bool rootIsCheckable()                  const;
384 
385     /**
386      * Triggers if the albums in this model are tristate.
387      * Used to allow the user to actively set a third state,
388      * don't use if you only want to display a third state.
389      * Note that you want to set setCheckable(true) before.
390      */
391     void setTristate(bool isTristate);
392     bool isTristate()                       const;
393 
394     /**
395      * Sets a special tristate mode, which offers the
396      * three modes "unchecked", "added" and "excluded",
397      * where "excluded" corresponds to partially checked internally,
398      * but is reflected in the treeview through the decoration only.
399      */
400     void setAddExcludeTristate(bool b);
401     bool isAddExcludeTristate()             const;
402 
403     /// Returns if the given album has the check state Checked
404     bool isChecked(Album* album)            const;
405 
406     /// Returns the check state of the album
407     Qt::CheckState checkState(Album* album) const;
408 
409     /// Returns a list of album with check state Checked
410     QList<Album*> checkedAlbums()           const;
411 
412     /// Returns a list of album with partially check state Checked
413     QList<Album*> partiallyCheckedAlbums()  const;
414 
415 public Q_SLOTS:
416 
417     /// Sets the check state of album to Checked or Unchecked
418     void setChecked(Album* album, bool isChecked);
419 
420     /// Sets the check state of the album
421     void setCheckState(Album* album, Qt::CheckState state);
422 
423     /// Toggles the check state of album between Checked or Unchecked
424     void toggleChecked(Album* album);
425 
426     /// Resets the checked state of all albums to Qt::Unchecked
427     void resetAllCheckedAlbums();
428 
429     /// Resets the checked state of all albums under the given parent
430     void resetCheckedAlbums(const QModelIndex& parent = QModelIndex());
431 
432     /// Resets the checked state of all parents of the child including it.
433     void resetCheckedParentAlbums(const QModelIndex& child);
434 
435     /// Checks all albums beneath the given parent
436     void checkAllAlbums(const QModelIndex& parent = QModelIndex());
437 
438     /// Checks all parent albums starting at the child, including it.
439     void checkAllParentAlbums(const QModelIndex& child);
440 
441     /// Inverts the checked state of all albums under the given parent.
442     void invertCheckedAlbums(const QModelIndex& parent = QModelIndex());
443 
444     /// Sets the checked state recursively for all children of and the given album.
445     void setCheckStateForChildren(Album* album, Qt::CheckState state);
446 
447     /// Sets the checked state recursively for all parents of and the given album.
448     void setCheckStateForParents(Album* album, Qt::CheckState state);
449 
450 Q_SIGNALS:
451 
452     /**
453      * Emitted when the check state of an album changes.
454      * checkState contains the new Qt::CheckState of album
455      */
456     void checkStateChanged(Album* album, Qt::CheckState checkState);
457 
458 protected:
459 
460     /**
461      * If in AddExcludeTristate mode, changes the icon as to indicate the state.
462      */
463     void prepareAddExcludeDecoration(Album* a, QPixmap& icon)                              const;
464 
465     QVariant albumData(Album* a, int role)                                                 const override;
466     Qt::ItemFlags flags(const QModelIndex& index)                                          const override;
467     bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole)       override;
468 
469     void albumCleared(Album* album)                                                              override;
470     void allAlbumsCleared()                                                                      override;
471 
472 private:
473 
474     void setDataForParents(const QModelIndex& child, const QVariant& value, int role = Qt::EditRole);
475     void setDataForChildren(const QModelIndex& parent, const QVariant& value, int role = Qt::EditRole);
476 
477 private:
478 
479     class Private;
480     Private* const d;
481 };
482 
483 } // namespace Digikam
484 
485 #endif // DIGIKAM_ABSTRACT_ALBUM_MODEL_H
486