screen_artist: split into three Page implementations
[ncmpc-debian.git] / src / screen_artist.cxx
1 /* ncmpc (Ncurses MPD Client)
2  * (c) 2004-2018 The Music Player Daemon Project
3  * Project homepage: http://musicpd.org
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19
20 #include "screen_artist.hxx"
21 #include "ArtistListPage.hxx"
22 #include "AlbumListPage.hxx"
23 #include "screen_interface.hxx"
24 #include "screen_status.hxx"
25 #include "screen_find.hxx"
26 #include "screen_browser.hxx"
27 #include "screen.hxx"
28 #include "ProxyPage.hxx"
29 #include "i18n.h"
30 #include "charset.hxx"
31 #include "mpdclient.hxx"
32 #include "filelist.hxx"
33 #include "options.hxx"
34 #include "util/NulledString.hxx"
35
36 #include <vector>
37 #include <string>
38 #include <algorithm>
39
40 #include <assert.h>
41 #include <string.h>
42 #include <glib.h>
43
44 #define BUFSIZE 1024
45
46 class SongListPage final : public FileListPage {
47         std::string artist;
48
49         /**
50          * The current album filter.  If IsNulled() is true, then the
51          * album filter is not used (i.e. all songs from all albums
52          * are displayed).
53          */
54         std::string album;
55
56 public:
57         SongListPage(ScreenManager &_screen, WINDOW *_w, Size size)
58                 :FileListPage(_screen, _w, size, options.list_format) {}
59
60         template<typename A>
61         void SetArtist(A &&_artist) {
62                 artist = std::forward<A>(_artist);
63                 AddPendingEvents(~0u);
64         }
65
66         const std::string &GetArtist() {
67                 return artist;
68         }
69
70         template<typename A>
71         void SetAlbum(A &&_album) {
72                 album = std::forward<A>(_album);
73                 AddPendingEvents(~0u);
74         }
75
76         const std::string &GetAlbum() {
77                 return album;
78         }
79
80         void LoadSongList(struct mpdclient &c);
81
82         /* virtual methods from class Page */
83         void Update(struct mpdclient &c, unsigned events) override;
84         bool OnCommand(struct mpdclient &c, command_t cmd) override;
85         const char *GetTitle(char *s, size_t size) const override;
86 };
87
88 void
89 SongListPage::Update(struct mpdclient &c, unsigned events)
90 {
91         if (events & MPD_IDLE_DATABASE) {
92                 LoadSongList(c);
93         }
94 }
95
96 class ArtistBrowserPage final : public ProxyPage {
97         enum class Mode {
98                 ARTISTS,
99                 ALBUMS,
100                 SONGS,
101         } mode = Mode::ARTISTS;
102
103         ArtistListPage artist_list_page;
104         AlbumListPage album_list_page;
105         SongListPage song_list_page;
106
107 public:
108         ArtistBrowserPage(ScreenManager &_screen, WINDOW *_w,
109                           Size size)
110                 :ProxyPage(_w),
111                  artist_list_page(_screen, _w, size),
112                  album_list_page(_screen, _w, size),
113                  song_list_page(_screen, _w, size) {}
114
115 private:
116         void OpenArtistList(struct mpdclient &c);
117         void OpenArtistList(struct mpdclient &c, const char *selected_value);
118         void OpenAlbumList(struct mpdclient &c, std::string _artist);
119         void OpenAlbumList(struct mpdclient &c, std::string _artist,
120                            const char *selected_value);
121         void OpenSongList(struct mpdclient &c, std::string _artist,
122                           std::string _album);
123
124 public:
125         /* virtual methods from class Page */
126         void OnOpen(struct mpdclient &c) override;
127         void Update(struct mpdclient &c, unsigned events) override;
128         bool OnCommand(struct mpdclient &c, command_t cmd) override;
129 };
130
131 void
132 SongListPage::LoadSongList(struct mpdclient &c)
133 {
134         struct mpd_connection *connection = mpdclient_get_connection(&c);
135
136         delete filelist;
137
138         filelist = new FileList();
139         /* add a dummy entry for ".." */
140         filelist->emplace_back(nullptr);
141
142         if (connection != nullptr) {
143                 mpd_search_db_songs(connection, true);
144                 mpd_search_add_tag_constraint(connection, MPD_OPERATOR_DEFAULT,
145                                               MPD_TAG_ARTIST, artist.c_str());
146                 if (!IsNulled(album))
147                         mpd_search_add_tag_constraint(connection, MPD_OPERATOR_DEFAULT,
148                                                       MPD_TAG_ALBUM, album.c_str());
149                 mpd_search_commit(connection);
150
151                 filelist->Receive(*connection);
152
153                 mpdclient_finish_command(&c);
154         }
155
156         /* fix highlights */
157         screen_browser_sync_highlights(filelist, &c.playlist);
158         lw.SetLength(filelist->size());
159 }
160
161 void
162 ArtistBrowserPage::OpenArtistList(struct mpdclient &c)
163 {
164         mode = Mode::ARTISTS;
165         SetCurrentPage(c, &artist_list_page);
166 }
167
168 void
169 ArtistBrowserPage::OpenArtistList(struct mpdclient &c,
170                                   const char *selected_value)
171 {
172         OpenArtistList(c);
173
174         // TODO: move cursor to selected_value
175         (void)selected_value;
176 }
177
178 void
179 ArtistBrowserPage::OpenAlbumList(struct mpdclient &c, std::string _artist)
180 {
181         mode = Mode::ALBUMS;
182         album_list_page.SetArtist(std::move(_artist));
183         SetCurrentPage(c, &album_list_page);
184 }
185
186 void
187 ArtistBrowserPage::OpenAlbumList(struct mpdclient &c, std::string _artist,
188                                  const char *selected_value)
189 {
190         OpenAlbumList(c, std::move(_artist));
191
192         // TODO: move cursor to selected_value
193         (void)selected_value;
194 }
195
196 void
197 ArtistBrowserPage::OpenSongList(struct mpdclient &c, std::string _artist,
198                                 std::string _album)
199 {
200         mode = Mode::SONGS;
201         song_list_page.SetArtist(std::move(_artist));
202         song_list_page.SetAlbum(std::move(_album));
203         SetCurrentPage(c, &song_list_page);
204 }
205
206 static Page *
207 screen_artist_init(ScreenManager &_screen, WINDOW *w, Size size)
208 {
209         return new ArtistBrowserPage(_screen, w, size);
210 }
211
212 const char *
213 SongListPage::GetTitle(char *str, size_t size) const
214 {
215         char *s1 = utf8_to_locale(artist.c_str());
216
217         if (IsNulled(album))
218                 g_snprintf(str, size,
219                            _("All tracks of artist: %s"), s1);
220         else if (!album.empty()) {
221                 char *s2 = utf8_to_locale(album.c_str());
222                 g_snprintf(str, size, _("Album: %s - %s"), s1, s2);
223                 g_free(s2);
224         } else
225                 g_snprintf(str, size,
226                            _("Tracks of no album of artist: %s"), s1);
227         g_free(s1);
228
229         return str;
230 }
231
232 bool
233 SongListPage::OnCommand(struct mpdclient &c, command_t cmd)
234 {
235         switch(cmd) {
236         case CMD_PLAY:
237                 if (lw.selected == 0) {
238                         /* handle ".." */
239                         screen.OnCommand(c, CMD_GO_PARENT_DIRECTORY);
240                         return true;
241                 }
242
243                 break;
244
245                 /* continue and update... */
246         case CMD_SCREEN_UPDATE:
247                 LoadSongList(c);
248                 return false;
249
250         default:
251                 break;
252         }
253
254         return FileListPage::OnCommand(c, cmd);
255 }
256
257 void
258 ArtistBrowserPage::OnOpen(struct mpdclient &c)
259 {
260         ProxyPage::OnOpen(c);
261
262         if (GetCurrentPage() == nullptr)
263                 SetCurrentPage(c, &artist_list_page);
264 }
265
266 void
267 ArtistBrowserPage::Update(struct mpdclient &c, unsigned events)
268 {
269         artist_list_page.AddPendingEvents(events);
270         album_list_page.AddPendingEvents(events);
271         song_list_page.AddPendingEvents(events);
272
273         ProxyPage::Update(c, events);
274 }
275
276 bool
277 ArtistBrowserPage::OnCommand(struct mpdclient &c, command_t cmd)
278 {
279         if (ProxyPage::OnCommand(c, cmd))
280                 return true;
281
282         switch (cmd) {
283         case CMD_PLAY:
284                 if (GetCurrentPage() == &artist_list_page) {
285                         const char *artist = artist_list_page.GetSelectedValue();
286                         if (artist != nullptr) {
287                                 OpenAlbumList(c, artist);
288                                 return true;
289                         }
290                 } else if (GetCurrentPage() == &album_list_page) {
291                         const char *album = album_list_page.GetSelectedValue();
292                         if (album != nullptr)
293                                 OpenSongList(c, album_list_page.GetArtist(),
294                                              album);
295                         else if (album_list_page.IsShowAll())
296                                 OpenSongList(c, album_list_page.GetArtist(),
297                                              MakeNulledString());
298                 }
299
300                 break;
301
302         case CMD_GO_ROOT_DIRECTORY:
303                 if (GetCurrentPage() != &artist_list_page) {
304                         OpenArtistList(c, album_list_page.GetArtist().c_str());
305                         return true;
306                 }
307
308                 break;
309
310         case CMD_GO_PARENT_DIRECTORY:
311                 if (GetCurrentPage() == &album_list_page) {
312                         OpenArtistList(c, album_list_page.GetArtist().c_str());
313                         return true;
314                 } else if (GetCurrentPage() == &song_list_page) {
315                         const auto &album = song_list_page.GetAlbum();
316                         OpenAlbumList(c, song_list_page.GetArtist(),
317                                       IsNulled(album) ? nullptr : album.c_str());
318                         return true;
319                 }
320
321                 break;
322
323         default:
324                 break;
325         }
326
327         return false;
328 }
329
330 const struct screen_functions screen_artist = {
331         "artist",
332         screen_artist_init,
333 };