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 PLAYLISTMANAGER_H
23 #define PLAYLISTMANAGER_H
24 
25 #include "config.h"
26 
27 #include <QtGlobal>
28 #include <QObject>
29 #include <QItemSelectionModel>
30 #include <QFuture>
31 #include <QList>
32 #include <QMap>
33 #include <QString>
34 #include <QUrl>
35 
36 #include "core/song.h"
37 #include "playlist.h"
38 #include "smartplaylists/playlistgenerator.h"
39 
40 class QModelIndex;
41 
42 class Application;
43 class CollectionBackend;
44 class PlaylistBackend;
45 class PlaylistContainer;
46 class PlaylistParser;
47 class PlaylistSequence;
48 
49 class PlaylistManagerInterface : public QObject {
50   Q_OBJECT
51 
52  public:
53   explicit PlaylistManagerInterface(Application *app, QObject *parent) : QObject(parent) { Q_UNUSED(app); }
54 
55   virtual int current_id() const = 0;
56   virtual int active_id() const = 0;
57 
58   virtual Playlist *playlist(const int id) const = 0;
59   virtual Playlist *current() const = 0;
60   virtual Playlist *active() const = 0;
61 
62   // Returns the collection of playlists managed by this PlaylistManager.
63   virtual QList<Playlist*> GetAllPlaylists() const = 0;
64   // Grays out and reloads all deleted songs in all playlists.
65   virtual void InvalidateDeletedSongs() = 0;
66   // Removes all deleted songs from all playlists.
67   virtual void RemoveDeletedSongs() = 0;
68 
69   virtual QItemSelection selection(const int id) const = 0;
70   virtual QItemSelection current_selection() const = 0;
71   virtual QItemSelection active_selection() const = 0;
72 
73   virtual QString GetPlaylistName(const int index) const = 0;
74 
75   virtual CollectionBackend *collection_backend() const = 0;
76   virtual PlaylistBackend *playlist_backend() const = 0;
77   virtual PlaylistSequence *sequence() const = 0;
78   virtual PlaylistParser *parser() const = 0;
79   virtual PlaylistContainer *playlist_container() const = 0;
80 
81   virtual void PlaySmartPlaylist(PlaylistGeneratorPtr generator, const bool as_new, const bool clear) = 0;
82 
83  public slots:
84   virtual void New(const QString &name, const SongList &songs = SongList(), const QString &special_type = QString()) = 0;
85   virtual void Load(const QString &filename) = 0;
86   virtual void Save(const int id, const QString &filename, const Playlist::Path path_type) = 0;
87   virtual void Rename(const int id, const QString &new_name) = 0;
88   virtual void Delete(const int id) = 0;
89   virtual bool Close(const int id) = 0;
90   virtual void Open(const int id) = 0;
91   virtual void ChangePlaylistOrder(const QList<int> &ids) = 0;
92 
93   virtual void SongChangeRequestProcessed(const QUrl &url, const bool valid) = 0;
94 
95   virtual void SetCurrentPlaylist(const int id) = 0;
96   virtual void SetActivePlaylist(const int id) = 0;
97   virtual void SetActiveToCurrent() = 0;
98 
99   virtual void SelectionChanged(const QItemSelection &selection) = 0;
100 
101   // Convenience slots that defer to either current() or active()
102   virtual void ClearCurrent() = 0;
103   virtual void ShuffleCurrent() = 0;
104   virtual void RemoveDuplicatesCurrent() = 0;
105   virtual void RemoveUnavailableCurrent() = 0;
106   virtual void SetActivePlaying() = 0;
107   virtual void SetActivePaused() = 0;
108   virtual void SetActiveStopped() = 0;
109 
110   // Rate current song using 0.0 - 1.0 scale.
111   virtual void RateCurrentSong(const double rating) = 0;
112   // Rate current song using 0 - 5 scale.
113   virtual void RateCurrentSong(const int rating) = 0;
114 
115  signals:
116   void PlaylistManagerInitialized();
117   void AllPlaylistsLoaded();
118 
119   void PlaylistAdded(int id, QString name, bool favorite);
120   void PlaylistDeleted(int id);
121   void PlaylistClosed(int id);
122   void PlaylistRenamed(int id, QString new_name);
123   void PlaylistFavorited(int id, bool favorite);
124   void CurrentChanged(Playlist *new_playlist, int scroll_position = 0);
125   void ActiveChanged(Playlist *new_playlist);
126 
127   void Error(QString message);
128   void SummaryTextChanged(QString summary);
129 
130   // Forwarded from individual playlists
131   void CurrentSongChanged(Song song);
132   void SongMetadataChanged(Song song);
133 
134   // Signals that one of manager's playlists has changed (new items, new ordering etc.) - the argument shows which.
135   void PlaylistChanged(Playlist *playlist);
136   void EditingFinished(int playlist_id, QModelIndex idx);
137   void PlayRequested(QModelIndex idx, Playlist::AutoScroll autoscroll);
138 };
139 
140 class PlaylistManager : public PlaylistManagerInterface {
141   Q_OBJECT
142 
143  public:
144   explicit PlaylistManager(Application *app, QObject *parent = nullptr);
145   ~PlaylistManager() override;
146 
147   int current_id() const override { return current_; }
148   int active_id() const override { return active_; }
149 
150   Playlist *playlist(const int id) const override { return playlists_[id].p; }
151   Playlist *current() const override { return playlist(current_id()); }
152   Playlist *active() const override { return playlist(active_id()); }
153 
154   // Returns the collection of playlists managed by this PlaylistManager.
155   QList<Playlist*> GetAllPlaylists() const override;
156   // Grays out and reloads all deleted songs in all playlists.
157   void InvalidateDeletedSongs() override;
158   // Removes all deleted songs from all playlists.
159   void RemoveDeletedSongs() override;
160   // Returns true if the playlist is open
161   bool IsPlaylistOpen(const int id);
162 
163   // Returns a pretty automatic name for playlist created from the given list of songs.
164   static QString GetNameForNewPlaylist(const SongList &songs);
165 
166   QItemSelection selection(const int id) const override;
167   QItemSelection current_selection() const override { return selection(current_id()); }
168   QItemSelection active_selection() const override { return selection(active_id()); }
169 
170   QString GetPlaylistName(const int index) const override { return playlists_[index].name; }
171   bool IsPlaylistFavorite(const int index) const { return playlists_[index].p->is_favorite(); }
172 
173   void Init(CollectionBackend *collection_backend, PlaylistBackend *playlist_backend, PlaylistSequence *sequence, PlaylistContainer *playlist_container);
174 
175   CollectionBackend *collection_backend() const override { return collection_backend_; }
176   PlaylistBackend *playlist_backend() const override { return playlist_backend_; }
177   PlaylistSequence *sequence() const override { return sequence_; }
178   PlaylistParser *parser() const override { return parser_; }
179   PlaylistContainer *playlist_container() const override { return playlist_container_; }
180 
181  public slots:
182   void New(const QString &name, const SongList &songs = SongList(), const QString &special_type = QString()) override;
183   void Load(const QString &filename) override;
184   void Save(const int id, const QString &filename, const Playlist::Path path_type) override;
185   // Display a file dialog to let user choose a file before saving the file
186   void SaveWithUI(const int id, const QString &playlist_name);
187   void Rename(const int id, const QString &new_name) override;
188   void Favorite(const int id, bool favorite);
189   void Delete(const int id) override;
190   bool Close(const int id) override;
191   void Open(const int id) override;
192   void ChangePlaylistOrder(const QList<int> &ids) override;
193 
194   void SetCurrentPlaylist(const int id) override;
195   void SetActivePlaylist(const int id) override;
196   void SetActiveToCurrent() override;
197 
198   void SelectionChanged(const QItemSelection &selection) override;
199 
200   // Makes a playlist current if it's open already, or opens it and makes it current if it is hidden.
201   void SetCurrentOrOpen(const int id);
202 
203   // Convenience slots that defer to either current() or active()
204   void ClearCurrent() override;
205   void ShuffleCurrent() override;
206   void RemoveDuplicatesCurrent() override;
207   void RemoveUnavailableCurrent() override;
208 
209   void SongChangeRequestProcessed(const QUrl &url, const bool valid) override;
210 
211   void InsertUrls(const int id, const QList<QUrl> &urls, const int pos = -1, const bool play_now = false, const bool enqueue = false);
212   void InsertSongs(const int id, const SongList &songs, const int pos = -1, const bool play_now = false, const bool enqueue = false);
213   // Removes items with given indices from the playlist. This operation is not undoable.
214   void RemoveItemsWithoutUndo(const int id, const QList<int> &indices);
215   // Remove the current playing song
216   void RemoveCurrentSong() const;
217 
218   void PlaySmartPlaylist(PlaylistGeneratorPtr generator, const bool as_new, const bool clear) override;
219 
220   // Rate current song using 0.0 - 1.0 scale.
221   void RateCurrentSong(const double rating) override;
222   // Rate current song using 0 - 5 scale.
223   void RateCurrentSong(const int rating) override;
224 
225  private slots:
226   void SetActivePlaying() override;
227   void SetActivePaused() override;
228   void SetActiveStopped() override;
229 
230   void OneOfPlaylistsChanged();
231   void UpdateSummaryText();
232   void SongsDiscovered(const SongList &songs);
233   void ItemsLoadedForSavePlaylist(const SongList &songs, const QString &filename, const Playlist::Path path_type);
234   void PlaylistLoaded();
235 
236  private:
237   Playlist *AddPlaylist(const int id, const QString &name, const QString &special_type, const QString &ui_path, const bool favorite);
238 
239  private:
240   struct Data {
241     explicit Data(Playlist *_p = nullptr, const QString &_name = QString()) : p(_p), name(_name), scroll_position(0) {}
242     Playlist *p;
243     QString name;
244     QItemSelection selection;
245     int scroll_position;
246   };
247 
248   Application *app_;
249   PlaylistBackend *playlist_backend_;
250   CollectionBackend *collection_backend_;
251   PlaylistSequence *sequence_;
252   PlaylistParser *parser_;
253   PlaylistContainer *playlist_container_;
254 
255   // key = id
256   QMap<int, Data> playlists_;
257 
258   int current_;
259   int active_;
260   int playlists_loading_;
261 };
262 
263 #endif  // PLAYLISTMANAGER_H
264