charset: add class Utf8ToLocale
[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 class SongListPage final : public FileListPage {
45         std::string artist;
46
47         /**
48          * The current album filter.  If IsNulled() is true, then the
49          * album filter is not used (i.e. all songs from all albums
50          * are displayed).
51          */
52         std::string album;
53
54 public:
55         SongListPage(ScreenManager &_screen, WINDOW *_w, Size size)
56                 :FileListPage(_screen, _w, size, options.list_format) {}
57
58         template<typename A>
59         void SetArtist(A &&_artist) {
60                 artist = std::forward<A>(_artist);
61                 AddPendingEvents(~0u);
62         }
63
64         const std::string &GetArtist() {
65                 return artist;
66         }
67
68         template<typename A>
69         void SetAlbum(A &&_album) {
70                 album = std::forward<A>(_album);
71                 AddPendingEvents(~0u);
72         }
73
74         const std::string &GetAlbum() {
75                 return album;
76         }
77
78         void LoadSongList(struct mpdclient &c);
79
80         /* virtual methods from class Page */
81         void Update(struct mpdclient &c, unsigned events) override;
82         bool OnCommand(struct mpdclient &c, command_t cmd) override;
83         const char *GetTitle(char *s, size_t size) const override;
84 };
85
86 void
87 SongListPage::Update(struct mpdclient &c, unsigned events)
88 {
89         if (events & MPD_IDLE_DATABASE) {
90                 LoadSongList(c);
91         }
92 }
93
94 class ArtistBrowserPage final : public ProxyPage {
95         ArtistListPage artist_list_page;
96         AlbumListPage album_list_page;
97         SongListPage song_list_page;
98
99 public:
100         ArtistBrowserPage(ScreenManager &_screen, WINDOW *_w,
101                           Size size)
102                 :ProxyPage(_w),
103                  artist_list_page(_screen, _w, size),
104                  album_list_page(_screen, _w, size),
105                  song_list_page(_screen, _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, std::string _artist,
111                           std::string _album);
112
113 public:
114         /* virtual methods from class Page */
115         void OnOpen(struct mpdclient &c) override;
116         void Update(struct mpdclient &c, unsigned events) override;
117         bool OnCommand(struct mpdclient &c, command_t cmd) override;
118 };
119
120 void
121 SongListPage::LoadSongList(struct mpdclient &c)
122 {
123         struct mpd_connection *connection = mpdclient_get_connection(&c);
124
125         delete filelist;
126
127         filelist = new FileList();
128         /* add a dummy entry for ".." */
129         filelist->emplace_back(nullptr);
130
131         if (connection != nullptr) {
132                 mpd_search_db_songs(connection, true);
133                 mpd_search_add_tag_constraint(connection, MPD_OPERATOR_DEFAULT,
134                                               MPD_TAG_ARTIST, artist.c_str());
135                 if (!IsNulled(album))
136                         mpd_search_add_tag_constraint(connection, MPD_OPERATOR_DEFAULT,
137                                                       MPD_TAG_ALBUM, album.c_str());
138                 mpd_search_commit(connection);
139
140                 filelist->Receive(*connection);
141
142                 mpdclient_finish_command(&c);
143         }
144
145         /* fix highlights */
146         screen_browser_sync_highlights(filelist, &c.playlist);
147         lw.SetLength(filelist->size());
148 }
149
150 void
151 ArtistBrowserPage::OpenArtistList(struct mpdclient &c)
152 {
153         SetCurrentPage(c, &artist_list_page);
154 }
155
156 void
157 ArtistBrowserPage::OpenAlbumList(struct mpdclient &c, std::string _artist)
158 {
159         album_list_page.SetArtist(std::move(_artist));
160         SetCurrentPage(c, &album_list_page);
161 }
162
163 void
164 ArtistBrowserPage::OpenSongList(struct mpdclient &c, std::string _artist,
165                                 std::string _album)
166 {
167         song_list_page.SetArtist(std::move(_artist));
168         song_list_page.SetAlbum(std::move(_album));
169         SetCurrentPage(c, &song_list_page);
170 }
171
172 static Page *
173 screen_artist_init(ScreenManager &_screen, WINDOW *w, Size size)
174 {
175         return new ArtistBrowserPage(_screen, w, size);
176 }
177
178 const char *
179 SongListPage::GetTitle(char *str, size_t size) const
180 {
181         const Utf8ToLocale artist_locale(artist.c_str());
182
183         if (IsNulled(album))
184                 g_snprintf(str, size,
185                            _("All tracks of artist: %s"),
186                            artist_locale.c_str());
187         else if (!album.empty()) {
188                 const Utf8ToLocale album_locale(album.c_str());
189                 g_snprintf(str, size, _("Album: %s - %s"),
190                            artist_locale.c_str(), album_locale.c_str());
191         } else
192                 g_snprintf(str, size,
193                            _("Tracks of no album of artist: %s"),
194                            artist_locale.c_str());
195
196         return str;
197 }
198
199 bool
200 SongListPage::OnCommand(struct mpdclient &c, command_t cmd)
201 {
202         switch(cmd) {
203         case CMD_PLAY:
204                 if (lw.selected == 0) {
205                         /* handle ".." */
206                         screen.OnCommand(c, CMD_GO_PARENT_DIRECTORY);
207                         return true;
208                 }
209
210                 break;
211
212                 /* continue and update... */
213         case CMD_SCREEN_UPDATE:
214                 LoadSongList(c);
215                 return false;
216
217         default:
218                 break;
219         }
220
221         return FileListPage::OnCommand(c, cmd);
222 }
223
224 void
225 ArtistBrowserPage::OnOpen(struct mpdclient &c)
226 {
227         ProxyPage::OnOpen(c);
228
229         if (GetCurrentPage() == nullptr)
230                 SetCurrentPage(c, &artist_list_page);
231 }
232
233 void
234 ArtistBrowserPage::Update(struct mpdclient &c, unsigned events)
235 {
236         artist_list_page.AddPendingEvents(events);
237         album_list_page.AddPendingEvents(events);
238         song_list_page.AddPendingEvents(events);
239
240         ProxyPage::Update(c, events);
241 }
242
243 bool
244 ArtistBrowserPage::OnCommand(struct mpdclient &c, command_t cmd)
245 {
246         if (ProxyPage::OnCommand(c, cmd))
247                 return true;
248
249         switch (cmd) {
250         case CMD_PLAY:
251                 if (GetCurrentPage() == &artist_list_page) {
252                         const char *artist = artist_list_page.GetSelectedValue();
253                         if (artist != nullptr) {
254                                 OpenAlbumList(c, artist);
255                                 return true;
256                         }
257                 } else if (GetCurrentPage() == &album_list_page) {
258                         const char *album = album_list_page.GetSelectedValue();
259                         if (album != nullptr)
260                                 OpenSongList(c, album_list_page.GetArtist(),
261                                              album);
262                         else if (album_list_page.IsShowAll())
263                                 OpenSongList(c, album_list_page.GetArtist(),
264                                              MakeNulledString());
265                 }
266
267                 break;
268
269         case CMD_GO_ROOT_DIRECTORY:
270                 if (GetCurrentPage() != &artist_list_page) {
271                         OpenArtistList(c);
272                         return true;
273                 }
274
275                 break;
276
277         case CMD_GO_PARENT_DIRECTORY:
278                 if (GetCurrentPage() == &album_list_page) {
279                         OpenArtistList(c);
280                         return true;
281                 } else if (GetCurrentPage() == &song_list_page) {
282                         OpenAlbumList(c, song_list_page.GetArtist());
283                         return true;
284                 }
285
286                 break;
287
288         default:
289                 break;
290         }
291
292         return false;
293 }
294
295 const struct screen_functions screen_artist = {
296         "artist",
297         screen_artist_init,
298 };