bf6dec803057e9a59929df8bfde072c1eb3b2a7b
[ncmpc-debian.git] / src / ArtistListPage.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 "ArtistListPage.hxx"
21 #include "screen_interface.hxx"
22 #include "screen_status.hxx"
23 #include "screen_find.hxx"
24 #include "screen_browser.hxx"
25 #include "i18n.h"
26 #include "charset.hxx"
27 #include "mpdclient.hxx"
28 #include "filelist.hxx"
29
30 #include <algorithm>
31
32 #include <glib.h>
33
34 #include <assert.h>
35 #include <string.h>
36
37 #define BUFSIZE 1024
38
39 gcc_pure
40 static bool
41 CompareUTF8(const std::string &a, const std::string &b)
42 {
43         char *key1 = g_utf8_collate_key(a.c_str(), -1);
44         char *key2 = g_utf8_collate_key(b.c_str(), -1);
45         int n = strcmp(key1,key2);
46         g_free(key1);
47         g_free(key2);
48         return n < 0;
49 }
50
51 /* list_window callback */
52 static const char *
53 screen_artist_lw_callback(unsigned idx, void *data)
54 {
55         const auto &list = *(const std::vector<std::string> *)data;
56
57         assert(idx < list.size());
58
59         const char *str_utf8 = list[idx].c_str();
60         char *str = utf8_to_locale(str_utf8);
61
62         static char buf[BUFSIZE];
63         g_strlcpy(buf, str, sizeof(buf));
64         g_free(str);
65
66         return buf;
67 }
68
69 static void
70 recv_tag_values(struct mpd_connection *connection, enum mpd_tag_type tag,
71                 std::vector<std::string> &list)
72 {
73         struct mpd_pair *pair;
74
75         while ((pair = mpd_recv_pair_tag(connection, tag)) != nullptr) {
76                 list.emplace_back(pair->value);
77                 mpd_return_pair(connection, pair);
78         }
79 }
80
81 void
82 ArtistListPage::LoadArtistList(struct mpdclient &c)
83 {
84         struct mpd_connection *connection = mpdclient_get_connection(&c);
85
86         artist_list.clear();
87
88         if (connection != nullptr) {
89                 mpd_search_db_tags(connection, MPD_TAG_ARTIST);
90                 mpd_search_commit(connection);
91                 recv_tag_values(connection, MPD_TAG_ARTIST, artist_list);
92
93                 mpdclient_finish_command(&c);
94         }
95
96         /* sort list */
97         std::sort(artist_list.begin(), artist_list.end(), CompareUTF8);
98         lw.SetLength(artist_list.size());
99 }
100
101 void
102 ArtistListPage::Reload(struct mpdclient &c)
103 {
104         LoadArtistList(c);
105 }
106
107 /**
108  * Paint one item in the artist list.
109  */
110 static void
111 paint_artist_callback(WINDOW *w, unsigned i,
112                       gcc_unused unsigned y, unsigned width,
113                       bool selected, const void *data)
114 {
115         const auto &list = *(const std::vector<std::string> *)data;
116         char *p = utf8_to_locale(list[i].c_str());
117
118         screen_browser_paint_directory(w, width, selected, p);
119         g_free(p);
120 }
121
122 void
123 ArtistListPage::Paint() const
124 {
125         lw.Paint(paint_artist_callback, &artist_list);
126 }
127
128 const char *
129 ArtistListPage::GetTitle(char *, size_t) const
130 {
131         return _("All artists");
132 }
133
134 void
135 ArtistListPage::Update(struct mpdclient &c, unsigned events)
136 {
137         if (events & MPD_IDLE_DATABASE) {
138                 /* the db has changed -> update the list */
139                 Reload(c);
140                 SetDirty();
141         }
142 }
143
144 /* add_query - Add all songs satisfying specified criteria.
145    _artist is actually only used in the ALBUM case to distinguish albums with
146    the same name from different artists. */
147 static void
148 add_query(struct mpdclient *c, enum mpd_tag_type table, const char *_filter,
149           const char *_artist)
150 {
151         struct mpd_connection *connection = mpdclient_get_connection(c);
152
153         assert(_filter != nullptr);
154
155         if (connection == nullptr)
156                 return;
157
158         char *str = utf8_to_locale(_filter);
159         if (table == MPD_TAG_ALBUM)
160                 screen_status_printf(_("Adding album %s..."), str);
161         else
162                 screen_status_printf(_("Adding %s..."), str);
163         g_free(str);
164
165         mpd_search_db_songs(connection, true);
166         mpd_search_add_tag_constraint(connection, MPD_OPERATOR_DEFAULT,
167                                       table, _filter);
168         if (table == MPD_TAG_ALBUM)
169                 mpd_search_add_tag_constraint(connection, MPD_OPERATOR_DEFAULT,
170                                               MPD_TAG_ARTIST, _artist);
171         mpd_search_commit(connection);
172
173         auto *addlist = filelist_new_recv(connection);
174
175         if (mpdclient_finish_command(c))
176                 mpdclient_filelist_add_all(c, addlist);
177
178         delete addlist;
179 }
180
181 inline bool
182 ArtistListPage::OnListCommand(command_t cmd)
183 {
184         if (lw.HandleCommand(cmd)) {
185                 SetDirty();
186                 return true;
187         }
188
189         return false;
190 }
191
192 bool
193 ArtistListPage::OnCommand(struct mpdclient &c, command_t cmd)
194 {
195         switch(cmd) {
196                 const char *selected;
197
198         case CMD_SELECT:
199         case CMD_ADD:
200                 if (lw.selected >= artist_list.size())
201                         return true;
202
203                 for (const unsigned i : lw.GetRange()) {
204                         selected = artist_list[i].c_str();
205                         add_query(&c, MPD_TAG_ARTIST, selected, nullptr);
206                         cmd = CMD_LIST_NEXT; /* continue and select next item... */
207                 }
208
209                 break;
210
211                 /* continue and update... */
212         case CMD_SCREEN_UPDATE:
213                 Reload(c);
214                 return false;
215
216         case CMD_LIST_FIND:
217         case CMD_LIST_RFIND:
218         case CMD_LIST_FIND_NEXT:
219         case CMD_LIST_RFIND_NEXT:
220                 screen_find(screen, &lw, cmd,
221                             screen_artist_lw_callback, &artist_list);
222                 SetDirty();
223                 return true;
224
225         case CMD_LIST_JUMP:
226                 screen_jump(screen, &lw,
227                             screen_artist_lw_callback, &artist_list,
228                             paint_artist_callback, &artist_list);
229                 SetDirty();
230                 return true;
231
232         default:
233                 break;
234         }
235
236         if (OnListCommand(cmd))
237                 return true;
238
239         return false;
240 }