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