d8894534042b1d01bf81101ab4d992bbc3ac5fcb
1 /*
2 * (c) 2005 by Kalle Wallin <kaw@linux.se>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 *
17 */
19 #include "ncmpc.h"
20 #include "options.h"
21 #include "support.h"
22 #include "mpdclient.h"
23 #include "utils.h"
24 #include "strfsong.h"
25 #include "command.h"
26 #include "screen.h"
27 #include "screen_utils.h"
28 #include "screen_browser.h"
29 #include "gcc.h"
31 #include <ctype.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <glib.h>
35 #include <ncurses.h>
37 #define BUFSIZE 1024
39 typedef enum { LIST_ARTISTS, LIST_ALBUMS, LIST_SONGS } artist_mode_t;
41 static artist_mode_t mode = LIST_ARTISTS;
42 static char *artist = NULL;
43 static char *album = NULL;
44 static GPtrArray *metalist = NULL;
46 static struct screen_browser browser;
48 static gint
49 compare_utf8(gconstpointer s1, gconstpointer s2)
50 {
51 char *key1, *key2;
52 int n;
54 key1 = g_utf8_collate_key(s1,-1);
55 key2 = g_utf8_collate_key(s2,-1);
56 n = strcmp(key1,key2);
57 g_free(key1);
58 g_free(key2);
59 return n;
60 }
62 /* list_window callback */
63 static const char *
64 artist_lw_callback(unsigned idx, mpd_unused int *highlight, mpd_unused void *data)
65 {
66 static char buf[BUFSIZE];
67 char *str, *str_utf8;
69 if (mode == LIST_ALBUMS) {
70 if (idx == 0)
71 return "[..]";
72 else if (idx == metalist->len + 1) {
73 str = utf8_to_locale(_("All tracks"));
74 g_snprintf(buf, BUFSIZE, "[%s]", str);
75 g_free(str);
76 return buf;
77 }
79 --idx;
80 }
82 if (idx >= metalist->len)
83 return NULL;
85 str_utf8 = g_ptr_array_index(metalist, idx);
86 assert(str_utf8 != NULL);
88 str = utf8_to_locale(str_utf8);
89 g_snprintf(buf, BUFSIZE, "[%s]", str);
90 g_free(str);
92 return buf;
93 }
95 static void
96 paint(mpdclient_t *c);
98 static void
99 artist_repaint(void)
100 {
101 paint(NULL);
102 wrefresh(browser.lw->w);
103 }
105 static void
106 artist_repaint_if_active(void)
107 {
108 if (screen_is_visible(&screen_artist))
109 artist_repaint();
110 }
112 /* the playlist have been updated -> fix highlights */
113 static void
114 playlist_changed_callback(mpdclient_t *c, int event, gpointer data)
115 {
116 browser_playlist_changed(&browser, c, event, data);
118 artist_repaint_if_active();
119 }
121 static GPtrArray *
122 g_list_to_ptr_array(GList *in)
123 {
124 GPtrArray *out = g_ptr_array_sized_new(g_list_length(in));
125 GList *head = in;
127 while (in != NULL) {
128 g_ptr_array_add(out, in->data);
129 in = g_list_next(in);
130 }
132 g_list_free(head);
133 return out;
134 }
136 static void
137 string_array_free(GPtrArray *array)
138 {
139 unsigned i;
141 for (i = 0; i < array->len; ++i) {
142 char *value = g_ptr_array_index(array, i);
143 free(value);
144 }
146 g_ptr_array_free(array, TRUE);
147 }
149 /* fetch artists/albums/songs from mpd */
150 static void
151 update_metalist(mpdclient_t *c, char *m_artist, char *m_album)
152 {
153 g_free(artist);
154 g_free(album);
155 artist = NULL;
156 album = NULL;
158 if (metalist != NULL) {
159 string_array_free(metalist);
160 metalist = NULL;
161 }
163 if (browser.filelist) {
164 mpdclient_remove_playlist_callback(c, playlist_changed_callback);
165 filelist_free(browser.filelist);
166 browser.filelist = NULL;
167 }
169 if (m_album) {
170 /* retreive songs... */
171 artist = m_artist;
172 album = m_album;
173 if (album[0] == 0) {
174 album = g_strdup(_("All tracks"));
175 browser.filelist =
176 mpdclient_filelist_search_utf8(c, TRUE,
177 MPD_TABLE_ARTIST,
178 artist);
179 } else
180 browser.filelist =
181 mpdclient_filelist_search_utf8(c, TRUE,
182 MPD_TABLE_ALBUM,
183 album);
184 if (browser.filelist == NULL)
185 browser.filelist = filelist_new(NULL);
187 /* add a dummy entry for ".." */
188 filelist_prepend(browser.filelist, NULL);
190 /* install playlist callback and fix highlights */
191 sync_highlights(c, browser.filelist);
192 mpdclient_install_playlist_callback(c, playlist_changed_callback);
193 mode = LIST_SONGS;
194 } else if (m_artist) {
195 /* retreive albums... */
196 GList *list;
198 artist = m_artist;
199 list = mpdclient_get_albums_utf8(c, m_artist);
200 /* sort list */
201 list = g_list_sort(list, compare_utf8);
203 metalist = g_list_to_ptr_array(list);
204 mode = LIST_ALBUMS;
205 } else {
206 /* retreive artists... */
207 GList *list;
209 list = mpdclient_get_artists_utf8(c);
210 /* sort list */
211 list = g_list_sort(list, compare_utf8);
213 metalist = g_list_to_ptr_array(list);
214 mode = LIST_ARTISTS;
215 }
216 }
218 /* db updated */
219 static void
220 browse_callback(mpdclient_t *c, int event, mpd_unused gpointer data)
221 {
222 switch(event) {
223 case BROWSE_DB_UPDATED:
224 D("screen_artist.c> browse_callback() [BROWSE_DB_UPDATED]\n");
225 update_metalist(c, g_strdup(artist), g_strdup(album));
226 break;
227 default:
228 break;
229 }
231 artist_repaint_if_active();
232 }
234 static void
235 init(WINDOW *w, int cols, int rows)
236 {
237 browser.lw = list_window_init(w, cols, rows);
238 browser.lw_state = list_window_init_state();
239 artist = NULL;
240 album = NULL;
241 }
243 static void
244 quit(void)
245 {
246 if (browser.filelist)
247 filelist_free(browser.filelist);
248 if (metalist)
249 string_array_free(metalist);
250 g_free(artist);
251 g_free(album);
252 artist = NULL;
253 album = NULL;
254 list_window_free(browser.lw);
255 list_window_free_state(browser.lw_state);
256 }
258 static void
259 open(mpd_unused screen_t *screen, mpdclient_t *c)
260 {
261 static gboolean callback_installed = FALSE;
263 if (metalist == NULL && browser.filelist == NULL)
264 update_metalist(c, NULL, NULL);
265 if (!callback_installed) {
266 mpdclient_install_browse_callback(c, browse_callback);
267 callback_installed = TRUE;
268 }
269 }
271 static void
272 resize(int cols, int rows)
273 {
274 browser.lw->cols = cols;
275 browser.lw->rows = rows;
276 }
278 static void
279 paint(mpd_unused mpdclient_t *c)
280 {
281 if (browser.filelist) {
282 list_window_paint(browser.lw, browser_lw_callback,
283 browser.filelist);
284 } else if (metalist) {
285 list_window_paint(browser.lw, artist_lw_callback, metalist);
286 } else {
287 wmove(browser.lw->w, 0, 0);
288 wclrtobot(browser.lw->w);
289 }
290 }
292 static const char *
293 get_title(char *str, size_t size)
294 {
295 char *s1 = artist ? utf8_to_locale(artist) : NULL;
296 char *s2 = album ? utf8_to_locale(album) : NULL;
298 switch(mode) {
299 case LIST_ARTISTS:
300 g_snprintf(str, size, _("Artist: [db browser - EXPERIMENTAL]"));
301 break;
302 case LIST_ALBUMS:
303 g_snprintf(str, size, _("Artist: %s"), s1);
304 break;
305 case LIST_SONGS:
306 g_snprintf(str, size, _("Artist: %s - %s"), s1, s2);
307 break;
308 }
309 g_free(s1);
310 g_free(s2);
311 return str;
312 }
314 static void
315 add_query(mpdclient_t *c, int table, char *_filter)
316 {
317 char *str;
318 mpdclient_filelist_t *addlist;
320 assert(filter != NULL);
322 str = utf8_to_locale(_filter);
323 if (table== MPD_TABLE_ALBUM)
324 screen_status_printf("Adding album %s...", str);
325 else
326 screen_status_printf("Adding %s...", str);
327 g_free(str);
329 addlist = mpdclient_filelist_search_utf8(c, TRUE, table, _filter);
330 if (addlist) {
331 mpdclient_filelist_add_all(c, addlist);
332 filelist_free(addlist);
333 }
334 }
336 static unsigned
337 metalist_length(void)
338 {
339 assert(metalist != NULL);
341 return mode == LIST_ALBUMS
342 ? metalist->len + 2
343 : metalist->len;
344 }
346 static int
347 artist_lw_cmd(command_t cmd)
348 {
349 switch (mode) {
350 case LIST_ARTISTS:
351 case LIST_ALBUMS:
352 return list_window_cmd(browser.lw, metalist_length(), cmd);
354 case LIST_SONGS:
355 return list_window_cmd(browser.lw,
356 filelist_length(browser.filelist),
357 cmd);
358 }
360 assert(0);
361 return 0;
362 }
364 static int
365 artist_cmd(screen_t *screen, mpdclient_t *c, command_t cmd)
366 {
367 char *selected;
368 int ret;
370 if (artist_lw_cmd(cmd)) {
371 artist_repaint();
372 wrefresh(browser.lw->w);
373 return 1;
374 }
376 switch(cmd) {
377 case CMD_PLAY:
378 switch (mode) {
379 case LIST_ARTISTS:
380 selected = g_ptr_array_index(metalist,
381 browser.lw->selected);
382 update_metalist(c, g_strdup(selected), NULL);
383 list_window_push_state(browser.lw_state, browser.lw);
385 list_window_paint(browser.lw, artist_lw_callback, metalist);
386 wrefresh(browser.lw->w);
387 break;
389 case LIST_ALBUMS:
390 if (browser.lw->selected == 0) {
391 /* handle ".." */
393 update_metalist(c, NULL, NULL);
394 list_window_reset(browser.lw);
395 /* restore previous list window state */
396 list_window_pop_state(browser.lw_state, browser.lw);
397 } else if (browser.lw->selected == metalist->len + 1) {
398 /* handle "show all" */
399 update_metalist(c, g_strdup(artist), g_strdup("\0"));
400 list_window_push_state(browser.lw_state, browser.lw);
401 } else {
402 /* select album */
403 selected = g_ptr_array_index(metalist,
404 browser.lw->selected - 1);
405 update_metalist(c, g_strdup(artist), g_strdup(selected));
406 list_window_push_state(browser.lw_state, browser.lw);
407 }
409 artist_repaint();
410 break;
412 case LIST_SONGS:
413 if (browser.lw->selected == 0) {
414 /* handle ".." */
416 update_metalist(c, g_strdup(artist), NULL);
417 list_window_reset(browser.lw);
418 /* restore previous list window state */
419 list_window_pop_state(browser.lw_state,
420 browser.lw);
422 list_window_paint(browser.lw, artist_lw_callback, metalist);
423 wrefresh(browser.lw->w);
424 } else
425 browser_handle_enter(&browser, c);
426 break;
427 }
428 return 1;
431 /* FIXME? CMD_GO_* handling duplicates code from CMD_PLAY */
433 case CMD_GO_PARENT_DIRECTORY:
434 switch (mode) {
435 case LIST_ARTISTS:
436 break;
438 case LIST_ALBUMS:
439 update_metalist(c, NULL, NULL);
440 list_window_reset(browser.lw);
441 /* restore previous list window state */
442 list_window_pop_state(browser.lw_state, browser.lw);
443 break;
445 case LIST_SONGS:
446 update_metalist(c, g_strdup(artist), NULL);
447 list_window_reset(browser.lw);
448 /* restore previous list window state */
449 list_window_pop_state(browser.lw_state, browser.lw);
450 break;
451 }
453 artist_repaint();
454 break;
456 case CMD_GO_ROOT_DIRECTORY:
457 switch (mode) {
458 case LIST_ARTISTS:
459 break;
461 case LIST_ALBUMS:
462 case LIST_SONGS:
463 update_metalist(c, NULL, NULL);
464 list_window_reset(browser.lw);
465 /* restore first list window state (pop while returning true) */
466 while(list_window_pop_state(browser.lw_state, browser.lw));
467 break;
468 }
470 artist_repaint();
471 break;
473 case CMD_SELECT:
474 case CMD_ADD:
475 switch(mode) {
476 case LIST_ARTISTS:
477 selected = g_ptr_array_index(metalist,
478 browser.lw->selected);
479 add_query(c, MPD_TABLE_ARTIST, selected);
480 cmd = CMD_LIST_NEXT; /* continue and select next item... */
481 break;
483 case LIST_ALBUMS:
484 if (browser.lw->selected == metalist->len + 1)
485 add_query(c, MPD_TABLE_ARTIST, artist);
486 else if (browser.lw->selected > 0) {
487 selected = g_ptr_array_index(metalist,
488 browser.lw->selected - 1);
489 add_query(c, MPD_TABLE_ALBUM, selected);
490 cmd = CMD_LIST_NEXT; /* continue and select next item... */
491 }
492 break;
494 case LIST_SONGS:
495 ret = cmd == CMD_SELECT
496 ? browser_handle_select(&browser, c)
497 : browser_handle_add(&browser, c);
498 if (ret == 0)
499 /* continue and select next item... */
500 cmd = CMD_LIST_NEXT;
501 break;
502 }
503 break;
505 /* continue and update... */
506 case CMD_SCREEN_UPDATE:
507 update_metalist(c, g_strdup(artist), g_strdup(album));
508 screen_status_printf(_("Screen updated!"));
509 return 0;
511 case CMD_LIST_FIND:
512 case CMD_LIST_RFIND:
513 case CMD_LIST_FIND_NEXT:
514 case CMD_LIST_RFIND_NEXT:
515 if (browser.filelist)
516 screen_find(screen,
517 browser.lw, filelist_length(browser.filelist),
518 cmd, browser_lw_callback,
519 browser.filelist);
520 else if (metalist)
521 screen_find(screen,
522 browser.lw, metalist_length(),
523 cmd, artist_lw_callback, metalist);
524 else
525 return 1;
527 artist_repaint();
528 return 1;
530 case CMD_MOUSE_EVENT:
531 return browser_handle_mouse_event(&browser, c);
533 default:
534 break;
535 }
537 return 0;
538 }
540 const struct screen_functions screen_artist = {
541 .init = init,
542 .exit = quit,
543 .open = open,
544 .resize = resize,
545 .paint = paint,
546 .cmd = artist_cmd,
547 .get_title = get_title,
548 };