1 /* 2 * Strawberry Music Player 3 * This file was part of Clementine. 4 * Copyright 2010, David Sansome <me@davidsansome.com> 5 * Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net> 6 * 7 * Strawberry is free software: you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation, either version 3 of the License, or 10 * (at your option) any later version. 11 * 12 * Strawberry is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with Strawberry. If not, see <http://www.gnu.org/licenses/>. 19 * 20 */ 21 22 #ifndef COLLECTIONMODEL_H 23 #define COLLECTIONMODEL_H 24 25 #include "config.h" 26 27 #include <QtGlobal> 28 #include <QObject> 29 #include <QAbstractItemModel> 30 #include <QFuture> 31 #include <QDataStream> 32 #include <QMetaType> 33 #include <QPair> 34 #include <QSet> 35 #include <QList> 36 #include <QMap> 37 #include <QVariant> 38 #include <QString> 39 #include <QStringList> 40 #include <QUrl> 41 #include <QImage> 42 #include <QIcon> 43 #include <QPixmap> 44 #include <QNetworkDiskCache> 45 46 #include "core/simpletreemodel.h" 47 #include "core/song.h" 48 #include "covermanager/albumcoverloader.h" 49 #include "collectionquery.h" 50 #include "collectionitem.h" 51 #include "sqlrow.h" 52 #include "covermanager/albumcoverloaderoptions.h" 53 54 class QSettings; 55 56 class Application; 57 class CollectionBackend; 58 class CollectionDirectoryModel; 59 60 class CollectionModel : public SimpleTreeModel<CollectionItem> { 61 Q_OBJECT 62 63 public: 64 explicit CollectionModel(CollectionBackend *backend, Application *app, QObject *parent = nullptr); 65 ~CollectionModel() override; 66 67 static const char *kSavedGroupingsSettingsGroup; 68 69 static const int kPrettyCoverSize; 70 static const char *kPixmapDiskCacheDir; 71 72 enum Role { 73 Role_Type = Qt::UserRole + 1, 74 Role_ContainerType, 75 Role_SortText, 76 Role_Key, 77 Role_Artist, 78 Role_IsDivider, 79 Role_Editable, 80 LastRole 81 }; 82 83 // These values get saved in QSettings - don't change them 84 enum GroupBy { 85 GroupBy_None = 0, 86 GroupBy_AlbumArtist = 1, 87 GroupBy_Artist = 2, 88 GroupBy_Album = 3, 89 GroupBy_AlbumDisc = 4, 90 GroupBy_YearAlbum = 5, 91 GroupBy_YearAlbumDisc = 6, 92 GroupBy_OriginalYearAlbum = 7, 93 GroupBy_OriginalYearAlbumDisc = 8, 94 GroupBy_Disc = 9, 95 GroupBy_Year = 10, 96 GroupBy_OriginalYear = 11, 97 GroupBy_Genre = 12, 98 GroupBy_Composer = 13, 99 GroupBy_Performer = 14, 100 GroupBy_Grouping = 15, 101 GroupBy_FileType = 16, 102 GroupBy_Format = 17, 103 GroupBy_Samplerate = 18, 104 GroupBy_Bitdepth = 19, 105 GroupBy_Bitrate = 20, 106 GroupByCount = 21, 107 }; 108 Q_ENUM(GroupBy) 109 110 struct Grouping { 111 explicit Grouping(GroupBy f = GroupBy_None, GroupBy s = GroupBy_None, GroupBy t = GroupBy_None) firstGrouping112 : first(f), second(s), third(t) {} 113 114 GroupBy first; 115 GroupBy second; 116 GroupBy third; 117 118 const GroupBy &operator[](const int i) const; 119 GroupBy &operator[](const int i); 120 bool operator==(const Grouping other) const { 121 return first == other.first && second == other.second && third == other.third; 122 } 123 bool operator!=(const Grouping other) const { return !(*this == other); } 124 }; 125 126 struct QueryResult { QueryResultQueryResult127 QueryResult() : create_va(false) {} 128 129 SqlRowList rows; 130 bool create_va; 131 }; 132 backend()133 CollectionBackend *backend() const { return backend_; } directory_model()134 CollectionDirectoryModel *directory_model() const { return dir_model_; } 135 136 // Call before Init() set_show_various_artists(const bool show_various_artists)137 void set_show_various_artists(const bool show_various_artists) { show_various_artists_ = show_various_artists; } 138 139 // Get information about the collection 140 void GetChildSongs(CollectionItem *item, QList<QUrl> *urls, SongList *songs, QSet<int> *song_ids) const; 141 SongList GetChildSongs(const QModelIndex &idx) const; 142 SongList GetChildSongs(const QModelIndexList &indexes) const; 143 144 // Might be accurate total_song_count()145 int total_song_count() const { return total_song_count_; } total_artist_count()146 int total_artist_count() const { return total_artist_count_; } total_album_count()147 int total_album_count() const { return total_album_count_; } 148 149 // QAbstractItemModel 150 QVariant data(const QModelIndex &idx, const int role = Qt::DisplayRole) const override; 151 Qt::ItemFlags flags(const QModelIndex &idx) const override; 152 QStringList mimeTypes() const override; 153 QMimeData *mimeData(const QModelIndexList &indexes) const override; 154 bool canFetchMore(const QModelIndex &parent) const override; 155 156 // Whether or not to use album cover art, if it exists, in the collection view 157 void set_pretty_covers(const bool use_pretty_covers); use_pretty_covers()158 bool use_pretty_covers() const { return use_pretty_covers_; } 159 160 // Whether or not to show letters heading in the collection view 161 void set_show_dividers(const bool show_dividers); 162 163 // Save the current grouping 164 void SaveGrouping(const QString &name); 165 166 // Reload settings. 167 void ReloadSettings(); 168 169 // Utility functions for manipulating text 170 static QString TextOrUnknown(const QString &text); 171 static QString PrettyYearAlbum(const int year, const QString &album); 172 static QString PrettyAlbumDisc(const QString &album, const int disc); 173 static QString PrettyYearAlbumDisc(const int year, const QString &album, const int disc); 174 static QString PrettyDisc(const int disc); 175 static QString SortText(QString text); 176 static QString SortTextForNumber(const int number); 177 static QString SortTextForArtist(QString artist); 178 static QString SortTextForSong(const Song &song); 179 static QString SortTextForYear(const int year); 180 static QString SortTextForBitrate(const int bitrate); 181 icon_cache_disk_size()182 quint64 icon_cache_disk_size() { return sIconCache->cacheSize(); } 183 IsArtistGroupBy(const GroupBy group_by)184 static bool IsArtistGroupBy(const GroupBy group_by) { 185 return group_by == CollectionModel::GroupBy_Artist || group_by == CollectionModel::GroupBy_AlbumArtist; 186 } IsAlbumGroupBy(const GroupBy group_by)187 static bool IsAlbumGroupBy(const GroupBy group_by) { return group_by == GroupBy_Album || group_by == GroupBy_YearAlbum || group_by == GroupBy_AlbumDisc || group_by == GroupBy_YearAlbumDisc || group_by == GroupBy_OriginalYearAlbum || group_by == GroupBy_OriginalYearAlbumDisc; } 188 set_use_lazy_loading(const bool value)189 void set_use_lazy_loading(const bool value) { use_lazy_loading_ = value; } 190 container_nodes(const int i)191 QMap<QString, CollectionItem*> container_nodes(const int i) { return container_nodes_[i]; } song_nodes()192 QList<CollectionItem*> song_nodes() const { return song_nodes_.values(); } divider_nodes_count()193 int divider_nodes_count() const { return divider_nodes_.count(); } 194 195 void ExpandAll(CollectionItem *item = nullptr) const; 196 GetGroupBy()197 const CollectionModel::Grouping GetGroupBy() const { return group_by_; } 198 void SetGroupBy(const CollectionModel::Grouping g); 199 200 static QString ContainerKey(const GroupBy type, const Song &song); 201 202 signals: 203 void TotalSongCountUpdated(int count); 204 void TotalArtistCountUpdated(int count); 205 void TotalAlbumCountUpdated(int count); 206 void GroupingChanged(CollectionModel::Grouping g); 207 208 public slots: 209 void SetFilterAge(const int age); 210 void SetFilterText(const QString &text); 211 void SetFilterQueryMode(QueryOptions::QueryMode query_mode); 212 213 void Init(const bool async = true); 214 void Reset(); 215 void ResetAsync(); 216 217 void SongsDiscovered(const SongList &songs); 218 219 protected: LazyPopulate(CollectionItem * item)220 void LazyPopulate(CollectionItem *item) override { LazyPopulate(item, true); } 221 void LazyPopulate(CollectionItem *parent, const bool signal); 222 223 private slots: 224 // From CollectionBackend 225 void SongsDeleted(const SongList &songs); 226 void SongsSlightlyChanged(const SongList &songs); 227 void TotalSongCountUpdatedSlot(const int count); 228 void TotalArtistCountUpdatedSlot(const int count); 229 void TotalAlbumCountUpdatedSlot(const int count); 230 static void ClearDiskCache(); 231 232 // Called after ResetAsync 233 void ResetAsyncQueryFinished(); 234 235 void AlbumCoverLoaded(const quint64 id, const AlbumCoverLoaderResult &result); 236 237 private: 238 // Provides some optimisations for loading the list of items in the root. 239 // This gets called a lot when filtering the playlist, so it's nice to be able to do it in a background thread. 240 QueryResult RunQuery(CollectionItem *parent); 241 void PostQuery(CollectionItem *parent, const QueryResult &result, const bool signal); 242 243 bool HasCompilations(const QSqlDatabase &db, const CollectionQuery &query); 244 245 void BeginReset(); 246 247 // Functions for working with queries and creating items. 248 // When the model is reset or when a node is lazy-loaded the Collection constructs a database query to populate the items. 249 // Filters are added for each parent item, restricting the songs returned to a particular album or artist for example. 250 static void InitQuery(const GroupBy type, CollectionQuery *q); 251 static void FilterQuery(const GroupBy type, CollectionItem *item, CollectionQuery *q); 252 253 // Items can be created either from a query that's been run to populate a node, or by a spontaneous SongsDiscovered emission from the backend. 254 CollectionItem *ItemFromQuery(const GroupBy type, const bool signal, const bool create_divider, CollectionItem *parent, const SqlRow &row, const int container_level); 255 CollectionItem *ItemFromSong(const GroupBy type, const bool signal, const bool create_divider, CollectionItem *parent, const Song &s, const int container_level); 256 257 // The "Various Artists" node is an annoying special case. 258 CollectionItem *CreateCompilationArtistNode(const bool signal, CollectionItem *parent); 259 260 // Helpers for ItemFromQuery and ItemFromSong 261 CollectionItem *InitItem(const GroupBy type, const bool signal, CollectionItem *parent, const int container_level); 262 void FinishItem(const GroupBy type, const bool signal, const bool create_divider, CollectionItem *parent, CollectionItem *item); 263 264 static QString DividerKey(const GroupBy type, CollectionItem *item); 265 static QString DividerDisplayText(const GroupBy type, const QString &key); 266 267 // Helpers IsCompilationArtistNode(const CollectionItem * node)268 static bool IsCompilationArtistNode(const CollectionItem *node) { return node == node->parent->compilation_artist_node_; } 269 QString AlbumIconPixmapCacheKey(const QModelIndex &idx) const; 270 QVariant AlbumIcon(const QModelIndex &idx); 271 QVariant data(const CollectionItem *item, const int role) const; 272 bool CompareItems(const CollectionItem *a, const CollectionItem *b) const; 273 static qint64 MaximumCacheSize(QSettings *s, const char *size_id, const char *size_unit_id, const qint64 cache_size_default); 274 275 private: 276 CollectionBackend *backend_; 277 Application *app_; 278 CollectionDirectoryModel *dir_model_; 279 bool show_various_artists_; 280 281 int total_song_count_; 282 int total_artist_count_; 283 int total_album_count_; 284 285 QueryOptions query_options_; 286 Grouping group_by_; 287 288 // Keyed on database ID 289 QMap<int, CollectionItem*> song_nodes_; 290 291 // Keyed on whatever the key is for that level - artist, album, year, etc. 292 QMap<QString, CollectionItem*> container_nodes_[3]; 293 294 // Keyed on a letter, a year, a century, etc. 295 QMap<QString, CollectionItem*> divider_nodes_; 296 297 QIcon artist_icon_; 298 QIcon album_icon_; 299 // Used as a generic icon to show when no cover art is found, fixed to the same size as the artwork (32x32) 300 QPixmap no_cover_icon_; 301 302 static QNetworkDiskCache *sIconCache; 303 304 int init_task_id_; 305 306 bool use_pretty_covers_; 307 bool show_dividers_; 308 bool use_disk_cache_; 309 bool use_lazy_loading_; 310 311 AlbumCoverLoaderOptions cover_loader_options_; 312 313 typedef QPair<CollectionItem*, QString> ItemAndCacheKey; 314 QMap<quint64, ItemAndCacheKey> pending_art_; 315 QSet<QString> pending_cache_keys_; 316 }; 317 318 Q_DECLARE_METATYPE(CollectionModel::Grouping) 319 320 QDataStream &operator<<(QDataStream &s, const CollectionModel::Grouping g); 321 QDataStream &operator>>(QDataStream &s, CollectionModel::Grouping &g); 322 323 #endif // COLLECTIONMODEL_H 324