1 /* ncmpc (Ncurses MPD Client)
2 * (c) 2004-2017 The Music Player Daemon Project
3 * Project homepage: http://musicpd.org
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.
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.
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.
20 #include "screen_artist.h"
21 #include "screen_interface.h"
22 #include "screen_status.h"
23 #include "screen_find.h"
24 #include "screen_browser.h"
28 #include "mpdclient.h"
29 #include "screen_browser.h"
39 typedef enum { LIST_ARTISTS
, LIST_ALBUMS
, LIST_SONGS
} artist_mode_t
;
41 static artist_mode_t mode
= LIST_ARTISTS
;
42 static GPtrArray
*artist_list
, *album_list
;
43 static char *artist
= NULL
;
44 static char *album
= NULL
;
45 static char ALL_TRACKS
[] = "";
47 static struct screen_browser browser
;
50 compare_utf8(gconstpointer s1
, gconstpointer s2
)
52 const char *const*t1
= s1
, *const*t2
= s2
;
54 char *key1
= g_utf8_collate_key(*t1
,-1);
55 char *key2
= g_utf8_collate_key(*t2
,-1);
56 int n
= strcmp(key1
,key2
);
62 /* list_window callback */
64 screen_artist_lw_callback(unsigned idx
, void *data
)
66 GPtrArray
*list
= data
;
68 if (mode
== LIST_ALBUMS
) {
71 else if (idx
== list
->len
+ 1)
72 return _("All tracks");
77 assert(idx
< list
->len
);
79 char *str_utf8
= g_ptr_array_index(list
, idx
);
80 assert(str_utf8
!= NULL
);
82 char *str
= utf8_to_locale(str_utf8
);
84 static char buf
[BUFSIZE
];
85 g_strlcpy(buf
, str
, sizeof(buf
));
92 string_array_free(GPtrArray
*array
)
94 for (unsigned i
= 0; i
< array
->len
; ++i
) {
95 char *value
= g_ptr_array_index(array
, i
);
99 g_ptr_array_free(array
, TRUE
);
105 if (artist_list
!= NULL
) {
106 string_array_free(artist_list
);
110 if (album_list
!= NULL
) {
111 string_array_free(album_list
);
115 if (browser
.filelist
) {
116 filelist_free(browser
.filelist
);
117 browser
.filelist
= NULL
;
122 recv_tag_values(struct mpd_connection
*connection
, enum mpd_tag_type tag
,
125 struct mpd_pair
*pair
;
127 while ((pair
= mpd_recv_pair_tag(connection
, tag
)) != NULL
) {
128 g_ptr_array_add(list
, g_strdup(pair
->value
));
129 mpd_return_pair(connection
, pair
);
134 load_artist_list(struct mpdclient
*c
)
136 struct mpd_connection
*connection
= mpdclient_get_connection(c
);
138 assert(mode
== LIST_ARTISTS
);
139 assert(artist
== NULL
);
140 assert(album
== NULL
);
141 assert(artist_list
== NULL
);
142 assert(album_list
== NULL
);
143 assert(browser
.filelist
== NULL
);
145 artist_list
= g_ptr_array_new();
147 if (connection
!= NULL
) {
148 mpd_search_db_tags(connection
, MPD_TAG_ARTIST
);
149 mpd_search_commit(connection
);
150 recv_tag_values(connection
, MPD_TAG_ARTIST
, artist_list
);
152 mpdclient_finish_command(c
);
156 g_ptr_array_sort(artist_list
, compare_utf8
);
157 list_window_set_length(browser
.lw
, artist_list
->len
);
161 load_album_list(struct mpdclient
*c
)
163 struct mpd_connection
*connection
= mpdclient_get_connection(c
);
165 assert(mode
== LIST_ALBUMS
);
166 assert(artist
!= NULL
);
167 assert(album
== NULL
);
168 assert(album_list
== NULL
);
169 assert(browser
.filelist
== NULL
);
171 album_list
= g_ptr_array_new();
173 if (connection
!= NULL
) {
174 mpd_search_db_tags(connection
, MPD_TAG_ALBUM
);
175 mpd_search_add_tag_constraint(connection
,
176 MPD_OPERATOR_DEFAULT
,
177 MPD_TAG_ARTIST
, artist
);
178 mpd_search_commit(connection
);
180 recv_tag_values(connection
, MPD_TAG_ALBUM
, album_list
);
182 mpdclient_finish_command(c
);
186 g_ptr_array_sort(album_list
, compare_utf8
);
188 list_window_set_length(browser
.lw
, album_list
->len
+ 2);
192 load_song_list(struct mpdclient
*c
)
194 struct mpd_connection
*connection
= mpdclient_get_connection(c
);
196 assert(mode
== LIST_SONGS
);
197 assert(artist
!= NULL
);
198 assert(album
!= NULL
);
199 assert(browser
.filelist
== NULL
);
201 browser
.filelist
= filelist_new();
202 /* add a dummy entry for ".." */
203 filelist_append(browser
.filelist
, NULL
);
205 if (connection
!= NULL
) {
206 mpd_search_db_songs(connection
, true);
207 mpd_search_add_tag_constraint(connection
, MPD_OPERATOR_DEFAULT
,
208 MPD_TAG_ARTIST
, artist
);
209 if (album
!= ALL_TRACKS
)
210 mpd_search_add_tag_constraint(connection
, MPD_OPERATOR_DEFAULT
,
211 MPD_TAG_ALBUM
, album
);
212 mpd_search_commit(connection
);
214 filelist_recv(browser
.filelist
, connection
);
216 mpdclient_finish_command(c
);
220 screen_browser_sync_highlights(browser
.filelist
, &c
->playlist
);
221 list_window_set_length(browser
.lw
, filelist_length(browser
.filelist
));
228 if (album
!= ALL_TRACKS
)
237 open_artist_list(struct mpdclient
*c
)
246 open_album_list(struct mpdclient
*c
, char *_artist
)
248 assert(_artist
!= NULL
);
258 open_song_list(struct mpdclient
*c
, char *_artist
, char *_album
)
260 assert(_artist
!= NULL
);
261 assert(_album
!= NULL
);
272 reload_lists(struct mpdclient
*c
)
292 screen_artist_init(WINDOW
*w
, unsigned cols
, unsigned rows
)
294 browser
.lw
= list_window_init(w
, cols
, rows
);
295 browser
.song_format
= options
.list_format
;
301 screen_artist_quit(void)
304 list_window_free(browser
.lw
);
308 screen_artist_open(struct mpdclient
*c
)
310 if (artist_list
== NULL
&& album_list
== NULL
&&
311 browser
.filelist
== NULL
)
316 screen_artist_resize(unsigned cols
, unsigned rows
)
318 list_window_resize(browser
.lw
, cols
, rows
);
322 * Paint one item in the artist list.
325 paint_artist_callback(WINDOW
*w
, unsigned i
,
326 gcc_unused
unsigned y
, unsigned width
,
327 bool selected
, const void *data
)
329 const GPtrArray
*list
= data
;
330 char *p
= utf8_to_locale(g_ptr_array_index(list
, i
));
332 screen_browser_paint_directory(w
, width
, selected
, p
);
337 * Paint one item in the album list. There are two virtual items
338 * inserted: at the beginning, there's the special item ".." to go to
339 * the parent directory, and at the end, there's the item "All tracks"
340 * to view the tracks of all albums.
343 paint_album_callback(WINDOW
*w
, unsigned i
,
344 gcc_unused
unsigned y
, unsigned width
,
345 bool selected
, const void *data
)
347 const GPtrArray
*list
= data
;
353 else if (i
== list
->len
+ 1)
356 p
= q
= utf8_to_locale(g_ptr_array_index(list
, i
- 1));
358 screen_browser_paint_directory(w
, width
, selected
, p
);
363 screen_artist_paint(void)
365 if (browser
.filelist
) {
366 screen_browser_paint(&browser
);
367 } else if (album_list
!= NULL
)
368 list_window_paint2(browser
.lw
,
369 paint_album_callback
, album_list
);
370 else if (artist_list
!= NULL
)
371 list_window_paint2(browser
.lw
,
372 paint_artist_callback
, artist_list
);
374 wmove(browser
.lw
->w
, 0, 0);
375 wclrtobot(browser
.lw
->w
);
380 screen_artist_get_title(char *str
, size_t size
)
386 g_snprintf(str
, size
, _("All artists"));
390 s1
= utf8_to_locale(artist
);
391 g_snprintf(str
, size
, _("Albums of artist: %s"), s1
);
396 s1
= utf8_to_locale(artist
);
398 if (album
== ALL_TRACKS
)
399 g_snprintf(str
, size
,
400 _("All tracks of artist: %s"), s1
);
401 else if (*album
!= 0) {
402 s2
= utf8_to_locale(album
);
403 g_snprintf(str
, size
, _("Album: %s - %s"), s1
, s2
);
406 g_snprintf(str
, size
,
407 _("Tracks of no album of artist: %s"), s1
);
416 screen_artist_update(struct mpdclient
*c
)
418 if (browser
.filelist
== NULL
)
421 if (c
->events
& MPD_IDLE_DATABASE
)
422 /* the db has changed -> update the list */
425 if (c
->events
& (MPD_IDLE_DATABASE
| MPD_IDLE_QUEUE
))
426 screen_browser_sync_highlights(browser
.filelist
, &c
->playlist
);
428 if (c
->events
& (MPD_IDLE_DATABASE
433 screen_artist_paint();
436 /* add_query - Add all songs satisfying specified criteria.
437 _artist is actually only used in the ALBUM case to distinguish albums with
438 the same name from different artists. */
440 add_query(struct mpdclient
*c
, enum mpd_tag_type table
, const char *_filter
,
443 struct mpd_connection
*connection
= mpdclient_get_connection(c
);
445 assert(_filter
!= NULL
);
447 if (connection
== NULL
)
450 char *str
= utf8_to_locale(_filter
);
451 if (table
== MPD_TAG_ALBUM
)
452 screen_status_printf(_("Adding album %s..."), str
);
454 screen_status_printf(_("Adding %s..."), str
);
457 mpd_search_db_songs(connection
, true);
458 mpd_search_add_tag_constraint(connection
, MPD_OPERATOR_DEFAULT
,
460 if (table
== MPD_TAG_ALBUM
)
461 mpd_search_add_tag_constraint(connection
, MPD_OPERATOR_DEFAULT
,
462 MPD_TAG_ARTIST
, _artist
);
463 mpd_search_commit(connection
);
465 struct filelist
*addlist
= filelist_new_recv(connection
);
467 if (mpdclient_finish_command(c
))
468 mpdclient_filelist_add_all(c
, addlist
);
470 filelist_free(addlist
);
474 screen_artist_lw_cmd(struct mpdclient
*c
, command_t cmd
)
479 return list_window_cmd(browser
.lw
, cmd
);
482 return browser_cmd(&browser
, c
, cmd
);
490 string_array_find(GPtrArray
*array
, const char *value
)
494 for (i
= 0; i
< array
->len
; ++i
)
495 if (strcmp((const char*)g_ptr_array_index(array
, i
),
503 screen_artist_cmd(struct mpdclient
*c
, command_t cmd
)
506 struct list_window_range range
;
515 if (browser
.lw
->selected
>= artist_list
->len
)
518 selected
= g_ptr_array_index(artist_list
,
519 browser
.lw
->selected
);
520 open_album_list(c
, g_strdup(selected
));
521 list_window_reset(browser
.lw
);
523 screen_artist_paint();
527 if (browser
.lw
->selected
== 0) {
529 old
= g_strdup(artist
);
532 list_window_reset(browser
.lw
);
533 /* restore previous list window state */
534 idx
= string_array_find(artist_list
, old
);
538 list_window_set_cursor(browser
.lw
, idx
);
539 list_window_center(browser
.lw
, idx
);
541 } else if (browser
.lw
->selected
== album_list
->len
+ 1) {
542 /* handle "show all" */
543 open_song_list(c
, g_strdup(artist
), ALL_TRACKS
);
544 list_window_reset(browser
.lw
);
547 selected
= g_ptr_array_index(album_list
,
548 browser
.lw
->selected
- 1);
549 open_song_list(c
, g_strdup(artist
), g_strdup(selected
));
550 list_window_reset(browser
.lw
);
553 screen_artist_paint();
557 if (browser
.lw
->selected
== 0) {
559 old
= g_strdup(album
);
562 open_album_list(c
, g_strdup(artist
));
563 list_window_reset(browser
.lw
);
564 /* restore previous list window state */
565 idx
= old_ptr
== ALL_TRACKS
566 ? (int)album_list
->len
567 : string_array_find(album_list
, old
);
572 list_window_set_cursor(browser
.lw
, idx
);
573 list_window_center(browser
.lw
, idx
);
576 screen_artist_paint();
583 /* FIXME? CMD_GO_* handling duplicates code from CMD_PLAY */
585 case CMD_GO_PARENT_DIRECTORY
:
591 old
= g_strdup(artist
);
594 list_window_reset(browser
.lw
);
595 /* restore previous list window state */
596 idx
= string_array_find(artist_list
, old
);
600 list_window_set_cursor(browser
.lw
, idx
);
601 list_window_center(browser
.lw
, idx
);
606 old
= g_strdup(album
);
609 open_album_list(c
, g_strdup(artist
));
610 list_window_reset(browser
.lw
);
611 /* restore previous list window state */
612 idx
= old_ptr
== ALL_TRACKS
613 ? (int)album_list
->len
614 : string_array_find(album_list
, old
);
619 list_window_set_cursor(browser
.lw
, idx
);
620 list_window_center(browser
.lw
, idx
);
625 screen_artist_paint();
628 case CMD_GO_ROOT_DIRECTORY
:
636 list_window_reset(browser
.lw
);
637 /* restore first list window state (pop while returning true) */
642 screen_artist_paint();
649 if (browser
.lw
->selected
>= artist_list
->len
)
652 list_window_get_range(browser
.lw
, &range
);
653 for (unsigned i
= range
.start
; i
< range
.end
; ++i
) {
654 selected
= g_ptr_array_index(artist_list
, i
);
655 add_query(c
, MPD_TAG_ARTIST
, selected
, NULL
);
656 cmd
= CMD_LIST_NEXT
; /* continue and select next item... */
661 list_window_get_range(browser
.lw
, &range
);
662 for (unsigned i
= range
.start
; i
< range
.end
; ++i
) {
663 if(i
== album_list
->len
+ 1)
664 add_query(c
, MPD_TAG_ARTIST
, artist
, NULL
);
667 selected
= g_ptr_array_index(album_list
,
668 browser
.lw
->selected
- 1);
669 add_query(c
, MPD_TAG_ALBUM
, selected
, artist
);
670 cmd
= CMD_LIST_NEXT
; /* continue and select next item... */
676 /* handled by browser_cmd() */
681 /* continue and update... */
682 case CMD_SCREEN_UPDATE
:
688 case CMD_LIST_FIND_NEXT
:
689 case CMD_LIST_RFIND_NEXT
:
692 screen_find(browser
.lw
, cmd
,
693 screen_artist_lw_callback
, artist_list
);
694 screen_artist_paint();
698 screen_find(browser
.lw
, cmd
,
699 screen_artist_lw_callback
, album_list
);
700 screen_artist_paint();
704 /* handled by browser_cmd() */
710 screen_jump(browser
.lw
,
711 screen_artist_lw_callback
, artist_list
,
712 paint_artist_callback
, artist_list
);
713 screen_artist_paint();
717 screen_jump(browser
.lw
,
718 screen_artist_lw_callback
, album_list
,
719 paint_album_callback
, album_list
);
720 screen_artist_paint();
724 /* handled by browser_cmd() */
734 if (screen_artist_lw_cmd(c
, cmd
)) {
735 if (screen_is_visible(&screen_artist
))
736 screen_artist_paint();
745 screen_artist_mouse(struct mpdclient
*c
, int x
, int y
, mmask_t bstate
)
747 if (browser_mouse(&browser
, c
, x
, y
, bstate
)) {
748 if (screen_is_visible(&screen_artist
))
749 screen_artist_paint();
758 const struct screen_functions screen_artist
= {
759 .init
= screen_artist_init
,
760 .exit
= screen_artist_quit
,
761 .open
= screen_artist_open
,
762 .resize
= screen_artist_resize
,
763 .paint
= screen_artist_paint
,
764 .update
= screen_artist_update
,
765 .cmd
= screen_artist_cmd
,
767 .mouse
= screen_artist_mouse
,
769 .get_title
= screen_artist_get_title
,