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