ListWindow: convert list_window_callback_fn_t to an abstract class
[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 const char *
51 ArtistListPage::GetListItemText(unsigned idx) const
52 {
53         assert(idx < artist_list.size());
54
55         const char *str_utf8 = artist_list[idx].c_str();
56
57         static char buf[BUFSIZE];
58         g_strlcpy(buf, Utf8ToLocale(str_utf8).c_str(), sizeof(buf));
59         return buf;
60 }
61
62 static void
63 recv_tag_values(struct mpd_connection *connection, enum mpd_tag_type tag,
64                 std::vector<std::string> &list)
65 {
66         struct mpd_pair *pair;
67
68         while ((pair = mpd_recv_pair_tag(connection, tag)) != nullptr) {
69                 list.emplace_back(pair->value);
70                 mpd_return_pair(connection, pair);
71         }
72 }
73
74 void
75 ArtistListPage::LoadArtistList(struct mpdclient &c)
76 {
77         struct mpd_connection *connection = mpdclient_get_connection(&c);
78
79         artist_list.clear();
80
81         if (connection != nullptr) {
82                 mpd_search_db_tags(connection, MPD_TAG_ARTIST);
83                 mpd_search_commit(connection);
84                 recv_tag_values(connection, MPD_TAG_ARTIST, artist_list);
85
86                 mpdclient_finish_command(&c);
87         }
88
89         /* sort list */
90         std::sort(artist_list.begin(), artist_list.end(), CompareUTF8);
91         lw.SetLength(artist_list.size());
92 }
93
94 void
95 ArtistListPage::Reload(struct mpdclient &c)
96 {
97         LoadArtistList(c);
98 }
99
100 void
101 ArtistListPage::PaintListItem(WINDOW *w, unsigned i,
102                               gcc_unused unsigned y, unsigned width,
103                               bool selected) const
104 {
105         screen_browser_paint_directory(w, width, selected,
106                                        Utf8ToLocale(artist_list[i].c_str()).c_str());
107 }
108
109 void
110 ArtistListPage::Paint() const
111 {
112         lw.Paint(*this);
113 }
114
115 const char *
116 ArtistListPage::GetTitle(char *, size_t) const
117 {
118         return _("All artists");
119 }
120
121 void
122 ArtistListPage::Update(struct mpdclient &c, unsigned events)
123 {
124         if (events & MPD_IDLE_DATABASE) {
125                 /* the db has changed -> update the list */
126                 Reload(c);
127                 SetDirty();
128         }
129 }
130
131 /* add_query - Add all songs satisfying specified criteria */
132 static void
133 add_query(struct mpdclient *c, enum mpd_tag_type table, const char *_filter)
134 {
135         struct mpd_connection *connection = mpdclient_get_connection(c);
136
137         assert(_filter != nullptr);
138
139         if (connection == nullptr)
140                 return;
141
142         screen_status_printf(_("Adding \'%s\' to queue"),
143                              Utf8ToLocale(_filter).c_str());
144
145         mpd_search_add_db_songs(connection, true);
146         mpd_search_add_tag_constraint(connection, MPD_OPERATOR_DEFAULT,
147                                       table, _filter);
148         mpd_search_commit(connection);
149         mpdclient_finish_command(c);
150 }
151
152 inline bool
153 ArtistListPage::OnListCommand(command_t cmd)
154 {
155         if (lw.HandleCommand(cmd)) {
156                 SetDirty();
157                 return true;
158         }
159
160         return false;
161 }
162
163 bool
164 ArtistListPage::OnCommand(struct mpdclient &c, command_t cmd)
165 {
166         switch(cmd) {
167                 const char *selected;
168
169         case CMD_SELECT:
170         case CMD_ADD:
171                 if (lw.selected >= artist_list.size())
172                         return true;
173
174                 for (const unsigned i : lw.GetRange()) {
175                         selected = artist_list[i].c_str();
176                         add_query(&c, MPD_TAG_ARTIST, selected);
177                         cmd = CMD_LIST_NEXT; /* continue and select next item... */
178                 }
179
180                 break;
181
182                 /* continue and update... */
183         case CMD_SCREEN_UPDATE:
184                 Reload(c);
185                 return false;
186
187         case CMD_LIST_FIND:
188         case CMD_LIST_RFIND:
189         case CMD_LIST_FIND_NEXT:
190         case CMD_LIST_RFIND_NEXT:
191                 screen_find(screen, &lw, cmd, *this);
192                 SetDirty();
193                 return true;
194
195         case CMD_LIST_JUMP:
196                 screen_jump(screen, &lw, *this, *this);
197                 SetDirty();
198                 return true;
199
200         default:
201                 break;
202         }
203
204         if (OnListCommand(cmd))
205                 return true;
206
207         return false;
208 }