screen_artist: simplify the GO_PARENT_DIRECTORY handler
[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 "TagListPage.hxx"
22 #include "PageMeta.hxx"
23 #include "screen_status.hxx"
24 #include "screen_find.hxx"
25 #include "FileListPage.hxx"
26 #include "Command.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
35 #include <vector>
36 #include <string>
37 #include <algorithm>
38
39 #include <assert.h>
40 #include <string.h>
41
42 class SongListPage final : public FileListPage {
43         Page *const parent;
44
45         const enum mpd_tag_type artist_tag, album_tag;
46
47         TagFilter filter;
48
49 public:
50         SongListPage(ScreenManager &_screen, Page *_parent,
51                      enum mpd_tag_type _artist_tag,
52                      enum mpd_tag_type _album_tag,
53                      WINDOW *_w, Size size) noexcept
54                 :FileListPage(_screen, _w, size,
55                               options.list_format.c_str()),
56                  parent(_parent),
57                  artist_tag(_artist_tag), album_tag(_album_tag) {}
58
59         const auto &GetFilter() const noexcept {
60                 return filter;
61         }
62
63         template<typename F>
64         void SetFilter(F &&_filter) noexcept {
65                 filter = std::forward<F>(_filter);
66                 AddPendingEvents(~0u);
67         }
68
69         void LoadSongList(struct mpdclient &c);
70
71         /* virtual methods from class Page */
72         void Update(struct mpdclient &c, unsigned events) noexcept override;
73         bool OnCommand(struct mpdclient &c, Command cmd) override;
74         const char *GetTitle(char *s, size_t size) const noexcept override;
75 };
76
77 void
78 SongListPage::Update(struct mpdclient &c, unsigned events) noexcept
79 {
80         if (events & MPD_IDLE_DATABASE) {
81                 LoadSongList(c);
82         }
83 }
84
85 class ArtistBrowserPage final : public ProxyPage {
86         TagListPage artist_list_page;
87         TagListPage album_list_page;
88         SongListPage song_list_page;
89
90 public:
91         ArtistBrowserPage(ScreenManager &_screen, WINDOW *_w,
92                           Size size)
93                 :ProxyPage(_w),
94                  artist_list_page(_screen, nullptr,
95                                   MPD_TAG_ARTIST,
96                                   nullptr,
97                                   _w, size),
98                  album_list_page(_screen, this,
99                                  MPD_TAG_ALBUM,
100                                  _("All tracks"),
101                                  _w, size),
102                  song_list_page(_screen, this,
103                                 artist_list_page.GetTag(),
104                                 album_list_page.GetTag(),
105                                 _w, size) {}
106
107 private:
108         void OpenArtistList(struct mpdclient &c);
109         void OpenAlbumList(struct mpdclient &c, std::string _artist);
110         void OpenSongList(struct mpdclient &c, TagFilter &&filter);
111
112 public:
113         /* virtual methods from class Page */
114         void OnOpen(struct mpdclient &c) noexcept override;
115         void Update(struct mpdclient &c, unsigned events) noexcept override;
116         bool OnCommand(struct mpdclient &c, Command cmd) override;
117 };
118
119 void
120 SongListPage::LoadSongList(struct mpdclient &c)
121 {
122         auto *connection = c.GetConnection();
123
124         delete filelist;
125
126         filelist = new FileList();
127         /* add a dummy entry for ".." */
128         filelist->emplace_back(nullptr);
129
130         if (connection != nullptr) {
131                 mpd_search_db_songs(connection, true);
132                 AddConstraints(connection, filter);
133                 mpd_search_commit(connection);
134
135                 filelist->Receive(*connection);
136
137                 c.FinishCommand();
138         }
139
140         /* fix highlights */
141         screen_browser_sync_highlights(filelist, &c.playlist);
142         lw.SetLength(filelist->size());
143 }
144
145 void
146 ArtistBrowserPage::OpenArtistList(struct mpdclient &c)
147 {
148         artist_list_page.SetFilter(TagFilter{}, _("Artists"));
149
150         SetCurrentPage(c, &artist_list_page);
151 }
152
153 void
154 ArtistBrowserPage::OpenAlbumList(struct mpdclient &c, std::string _artist)
155 {
156         const char *title = _("Albums");
157
158         char buffer[64];
159         if (!_artist.empty()) {
160                 snprintf(buffer, sizeof(buffer), "%s: %s", title,
161                          Utf8ToLocale(_artist.c_str()).c_str());
162                 title = buffer;
163         }
164
165         album_list_page.SetFilter(TagFilter{{artist_list_page.GetTag(), std::move(_artist)}},
166                                   title);
167
168         SetCurrentPage(c, &album_list_page);
169 }
170
171 void
172 ArtistBrowserPage::OpenSongList(struct mpdclient &c, TagFilter &&filter)
173 {
174         song_list_page.SetFilter(std::move(filter));
175         SetCurrentPage(c, &song_list_page);
176 }
177
178 static std::unique_ptr<Page>
179 screen_artist_init(ScreenManager &_screen, WINDOW *w, Size size)
180 {
181         return std::make_unique<ArtistBrowserPage>(_screen, w, size);
182 }
183
184 const char *
185 SongListPage::GetTitle(char *str, size_t size) const noexcept
186 {
187         const char *artist = FindTag(filter, artist_tag);
188         if (artist == nullptr)
189                 artist = "?";
190
191         const char *const album = FindTag(filter, album_tag);
192
193         if (album == nullptr)
194                 snprintf(str, size, "%s: %s",
195                          _("Artist"),
196                          Utf8ToLocale(artist).c_str());
197         else if (*album != '\0')
198                 snprintf(str, size, "%s: %s - %s",
199                          _("Album"),
200                          Utf8ToLocale(artist).c_str(),
201                          Utf8ToLocale(album).c_str());
202         else
203                 snprintf(str, size, "%s: %s",
204                          _("Artist"),
205                          Utf8ToLocale(artist).c_str());
206
207         return str;
208 }
209
210 bool
211 SongListPage::OnCommand(struct mpdclient &c, Command cmd)
212 {
213         switch(cmd) {
214         case Command::PLAY:
215                 if (lw.selected == 0 && parent != nullptr)
216                         /* handle ".." */
217                         return parent->OnCommand(c, Command::GO_PARENT_DIRECTORY);
218
219                 break;
220
221                 /* continue and update... */
222         case Command::SCREEN_UPDATE:
223                 LoadSongList(c);
224                 return false;
225
226         default:
227                 break;
228         }
229
230         return FileListPage::OnCommand(c, cmd);
231 }
232
233 void
234 ArtistBrowserPage::OnOpen(struct mpdclient &c) noexcept
235 {
236         ProxyPage::OnOpen(c);
237
238         if (GetCurrentPage() == nullptr)
239                 OpenArtistList(c);
240 }
241
242 void
243 ArtistBrowserPage::Update(struct mpdclient &c, unsigned events) noexcept
244 {
245         artist_list_page.AddPendingEvents(events);
246         album_list_page.AddPendingEvents(events);
247         song_list_page.AddPendingEvents(events);
248
249         ProxyPage::Update(c, events);
250 }
251
252 bool
253 ArtistBrowserPage::OnCommand(struct mpdclient &c, Command cmd)
254 {
255         if (ProxyPage::OnCommand(c, cmd))
256                 return true;
257
258         switch (cmd) {
259         case Command::PLAY:
260                 if (GetCurrentPage() == &artist_list_page) {
261                         const char *artist = artist_list_page.GetSelectedValue();
262                         if (artist != nullptr) {
263                                 OpenAlbumList(c, artist);
264                                 return true;
265                         }
266                 } else if (GetCurrentPage() == &album_list_page) {
267                         auto filter = album_list_page.MakeCursorFilter();
268                         if (!filter.empty())
269                                 OpenSongList(c, std::move(filter));
270                 }
271
272                 break;
273
274         case Command::GO_ROOT_DIRECTORY:
275                 if (GetCurrentPage() != &artist_list_page) {
276                         SetCurrentPage(c, &artist_list_page);
277                         return true;
278                 }
279
280                 break;
281
282         case Command::GO_PARENT_DIRECTORY:
283                 if (GetCurrentPage() == &album_list_page) {
284                         SetCurrentPage(c, &artist_list_page);
285                         return true;
286                 } else if (GetCurrentPage() == &song_list_page) {
287                         SetCurrentPage(c, &album_list_page);
288                         return true;
289                 }
290
291                 break;
292
293         default:
294                 break;
295         }
296
297         return false;
298 }
299
300 const PageMeta screen_artist = {
301         "artist",
302         N_("Artist"),
303         Command::SCREEN_ARTIST,
304         screen_artist_init,
305 };