1 /* ncmpc (Ncurses MPD Client)
2 * (c) 2004-2010 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.
21 #include "screen_browser.h"
22 #include "screen_file.h"
23 #include "screen_song.h"
24 #include "screen_lyrics.h"
25 #include "screen_status.h"
26 #include "screen_find.h"
32 #include "mpdclient.h"
36 #include "song_paint.h"
38 #include <mpd/client.h>
45 #define HIGHLIGHT (0x01)
50 /* sync highlight flags with playlist */
52 screen_browser_sync_highlights(struct filelist
*fl
,
53 const struct mpdclient_playlist
*playlist
)
57 for (i
= 0; i
< filelist_length(fl
); ++i
) {
58 struct filelist_entry
*entry
= filelist_get(fl
, i
);
59 const struct mpd_entity
*entity
= entry
->entity
;
61 if (entity
!= NULL
&& mpd_entity_get_type(entity
) == MPD_ENTITY_TYPE_SONG
) {
62 const struct mpd_song
*song
=
63 mpd_entity_get_song(entity
);
65 if (playlist_get_index_from_same_song(playlist
,
67 entry
->flags
|= HIGHLIGHT
;
69 entry
->flags
&= ~HIGHLIGHT
;
76 /* list_window callback */
78 browser_lw_callback(unsigned idx
, void *data
)
80 const struct filelist
*fl
= (const struct filelist
*) data
;
81 static char buf
[BUFSIZE
];
82 const struct filelist_entry
*entry
;
83 const struct mpd_entity
*entity
;
86 assert(idx
< filelist_length(fl
));
88 entry
= filelist_get(fl
, idx
);
89 assert(entry
!= NULL
);
91 entity
= entry
->entity
;
96 if (mpd_entity_get_type(entity
) == MPD_ENTITY_TYPE_DIRECTORY
) {
97 const struct mpd_directory
*dir
=
98 mpd_entity_get_directory(entity
);
99 char *directory
= utf8_to_locale(g_basename(mpd_directory_get_path(dir
)));
100 g_strlcpy(buf
, directory
, sizeof(buf
));
103 } else if (mpd_entity_get_type(entity
) == MPD_ENTITY_TYPE_SONG
) {
104 const struct mpd_song
*song
= mpd_entity_get_song(entity
);
106 strfsong(buf
, BUFSIZE
, options
.list_format
, song
);
108 } else if (mpd_entity_get_type(entity
) == MPD_ENTITY_TYPE_PLAYLIST
) {
109 const struct mpd_playlist
*playlist
=
110 mpd_entity_get_playlist(entity
);
111 char *filename
= utf8_to_locale(g_basename(mpd_playlist_get_path(playlist
)));
113 g_strlcpy(buf
, filename
, sizeof(buf
));
118 return "Error: Unknown entry!";
122 load_playlist(struct mpdclient
*c
, const struct mpd_playlist
*playlist
)
124 struct mpd_connection
*connection
= mpdclient_get_connection(c
);
126 if (connection
== NULL
)
129 if (mpd_run_load(connection
, mpd_playlist_get_path(playlist
))) {
130 char *filename
= utf8_to_locale(mpd_playlist_get_path(playlist
));
131 screen_status_printf(_("Loading playlist %s..."),
132 g_basename(filename
));
135 c
->events
|= MPD_IDLE_QUEUE
;
137 mpdclient_handle_error(c
);
143 enqueue_and_play(struct mpdclient
*c
, struct filelist_entry
*entry
)
145 struct mpd_connection
*connection
= mpdclient_get_connection(c
);
146 if (connection
== NULL
)
149 const struct mpd_song
*song
= mpd_entity_get_song(entry
->entity
);
153 if (!(entry
->flags
& HIGHLIGHT
))
157 id
= playlist_get_id_from_same_song(&c
->playlist
, song
);
162 id
= mpd_run_add_id(connection
, mpd_song_get_uri(song
));
164 mpdclient_handle_error(c
);
169 entry
->flags
|= HIGHLIGHT
;
171 strfsong(buf
, BUFSIZE
, options
.list_format
, song
);
172 screen_status_printf(_("Adding \'%s\' to queue"), buf
);
175 if (!mpd_run_play_id(connection
, id
)) {
176 mpdclient_handle_error(c
);
183 struct filelist_entry
*
184 browser_get_selected_entry(const struct screen_browser
*browser
)
186 struct list_window_range range
;
188 list_window_get_range(browser
->lw
, &range
);
190 if (browser
->filelist
== NULL
||
191 range
.end
<= range
.start
||
192 range
.end
> range
.start
+ 1 ||
193 range
.start
>= filelist_length(browser
->filelist
))
196 return filelist_get(browser
->filelist
, range
.start
);
199 static const struct mpd_entity
*
200 browser_get_selected_entity(const struct screen_browser
*browser
)
202 const struct filelist_entry
*entry
= browser_get_selected_entry(browser
);
209 static const struct mpd_song
*
210 browser_get_selected_song(const struct screen_browser
*browser
)
212 const struct mpd_entity
*entity
= browser_get_selected_entity(browser
);
214 return entity
!= NULL
&&
215 mpd_entity_get_type(entity
) == MPD_ENTITY_TYPE_SONG
216 ? mpd_entity_get_song(entity
)
220 static struct filelist_entry
*
221 browser_get_index(const struct screen_browser
*browser
, unsigned i
)
223 if (browser
->filelist
== NULL
||
224 i
>= filelist_length(browser
->filelist
))
227 return filelist_get(browser
->filelist
, i
);
231 browser_handle_enter(struct screen_browser
*browser
, struct mpdclient
*c
)
233 struct filelist_entry
*entry
= browser_get_selected_entry(browser
);
234 struct mpd_entity
*entity
;
239 entity
= entry
->entity
;
243 if (mpd_entity_get_type(entity
) == MPD_ENTITY_TYPE_PLAYLIST
)
244 return load_playlist(c
, mpd_entity_get_playlist(entity
));
245 else if (mpd_entity_get_type(entity
) == MPD_ENTITY_TYPE_SONG
)
246 return enqueue_and_play(c
, entry
);
251 browser_select_entry(struct mpdclient
*c
, struct filelist_entry
*entry
,
252 G_GNUC_UNUSED gboolean toggle
)
254 assert(entry
!= NULL
);
255 assert(entry
->entity
!= NULL
);
257 if (mpd_entity_get_type(entry
->entity
) == MPD_ENTITY_TYPE_PLAYLIST
)
258 return load_playlist(c
, mpd_entity_get_playlist(entry
->entity
));
260 if (mpd_entity_get_type(entry
->entity
) == MPD_ENTITY_TYPE_DIRECTORY
) {
261 const struct mpd_directory
*dir
=
262 mpd_entity_get_directory(entry
->entity
);
264 if (mpdclient_cmd_add_path(c
, mpd_directory_get_path(dir
))) {
265 char *tmp
= utf8_to_locale(mpd_directory_get_path(dir
));
267 screen_status_printf(_("Adding \'%s\' to queue"), tmp
);
274 if (mpd_entity_get_type(entry
->entity
) != MPD_ENTITY_TYPE_SONG
)
278 if (!toggle
|| (entry
->flags
& HIGHLIGHT
) == 0)
281 const struct mpd_song
*song
=
282 mpd_entity_get_song(entry
->entity
);
285 entry
->flags
|= HIGHLIGHT
;
288 if (mpdclient_cmd_add(c
, song
)) {
291 strfsong(buf
, BUFSIZE
, options
.list_format
, song
);
292 screen_status_printf(_("Adding \'%s\' to queue"), buf
);
296 /* remove song from playlist */
297 const struct mpd_song
*song
=
298 mpd_entity_get_song(entry
->entity
);
301 entry
->flags
&= ~HIGHLIGHT
;
303 while ((idx
= playlist_get_index_from_same_song(&c
->playlist
,
305 mpdclient_cmd_delete(c
, idx
);
313 browser_handle_select(struct screen_browser
*browser
, struct mpdclient
*c
)
315 struct list_window_range range
;
316 struct filelist_entry
*entry
;
317 bool success
= false;
319 list_window_get_range(browser
->lw
, &range
);
320 for (unsigned i
= range
.start
; i
< range
.end
; ++i
) {
321 entry
= browser_get_index(browser
, i
);
323 if (entry
!= NULL
&& entry
->entity
!= NULL
)
324 success
= browser_select_entry(c
, entry
, TRUE
);
327 return range
.end
== range
.start
+ 1 && success
;
331 browser_handle_add(struct screen_browser
*browser
, struct mpdclient
*c
)
333 struct list_window_range range
;
334 struct filelist_entry
*entry
;
335 bool success
= false;
337 list_window_get_range(browser
->lw
, &range
);
338 for (unsigned i
= range
.start
; i
< range
.end
; ++i
) {
339 entry
= browser_get_index(browser
, i
);
341 if (entry
!= NULL
&& entry
->entity
!= NULL
)
342 success
= browser_select_entry(c
, entry
, FALSE
) ||
346 return range
.end
== range
.start
+ 1 && success
;
350 browser_handle_select_all(struct screen_browser
*browser
, struct mpdclient
*c
)
354 if (browser
->filelist
== NULL
)
357 for (i
= 0; i
< filelist_length(browser
->filelist
); ++i
) {
358 struct filelist_entry
*entry
= filelist_get(browser
->filelist
, i
);
360 if (entry
!= NULL
&& entry
->entity
!= NULL
)
361 browser_select_entry(c
, entry
, FALSE
);
367 browser_handle_mouse_event(struct screen_browser
*browser
, struct mpdclient
*c
)
370 unsigned prev_selected
= browser
->lw
->selected
;
371 unsigned long bstate
;
373 if (screen_get_mouse_event(c
, &bstate
, &row
) ||
374 list_window_mouse(browser
->lw
, bstate
, row
))
377 list_window_set_cursor(browser
->lw
, browser
->lw
->start
+ row
);
379 if( bstate
& BUTTON1_CLICKED
) {
380 if (prev_selected
== browser
->lw
->selected
)
381 browser_handle_enter(browser
, c
);
382 } else if (bstate
& BUTTON3_CLICKED
) {
383 if (prev_selected
== browser
->lw
->selected
)
384 browser_handle_select(browser
, c
);
392 screen_browser_paint_callback(WINDOW
*w
, unsigned i
, unsigned y
,
393 unsigned width
, bool selected
, void *data
);
396 browser_cmd(struct screen_browser
*browser
,
397 struct mpdclient
*c
, command_t cmd
)
399 const struct mpd_song
*song
;
401 if (browser
->filelist
== NULL
)
404 if (list_window_cmd(browser
->lw
, cmd
))
410 case CMD_LIST_FIND_NEXT
:
411 case CMD_LIST_RFIND_NEXT
:
412 screen_find(browser
->lw
, cmd
, browser_lw_callback
,
416 screen_jump(browser
->lw
, browser_lw_callback
,
417 screen_browser_paint_callback
, browser
->filelist
);
421 case CMD_MOUSE_EVENT
:
422 browser_handle_mouse_event(browser
, c
);
426 #ifdef ENABLE_SONG_SCREEN
427 case CMD_SCREEN_SONG
:
428 song
= browser_get_selected_song(browser
);
432 screen_song_switch(c
, song
);
436 #ifdef ENABLE_LYRICS_SCREEN
437 case CMD_SCREEN_LYRICS
:
438 song
= browser_get_selected_song(browser
);
442 screen_lyrics_switch(c
, song
, false);
445 case CMD_SCREEN_SWAP
:
446 screen_swap(c
, browser_get_selected_song(browser
));
453 if (!mpdclient_is_connected(c
))
458 browser_handle_enter(browser
, c
);
462 if (browser_handle_select(browser
, c
))
463 list_window_cmd(browser
->lw
, CMD_LIST_NEXT
);
467 if (browser_handle_add(browser
, c
))
468 list_window_cmd(browser
->lw
, CMD_LIST_NEXT
);
472 browser_handle_select_all(browser
, c
);
476 song
= browser_get_selected_song(browser
);
480 screen_file_goto_song(c
, song
);
491 screen_browser_paint_directory(WINDOW
*w
, unsigned width
,
492 bool selected
, const char *name
)
494 row_color(w
, COLOR_DIRECTORY
, selected
);
500 /* erase the unused space after the text */
501 row_clear_to_eol(w
, width
, selected
);
505 screen_browser_paint_playlist(WINDOW
*w
, unsigned width
,
506 bool selected
, const char *name
)
508 row_paint_text(w
, width
, COLOR_PLAYLIST
, selected
, name
);
512 screen_browser_paint_callback(WINDOW
*w
, unsigned i
,
513 unsigned y
, unsigned width
,
514 bool selected
, void *data
)
516 const struct filelist
*fl
= (const struct filelist
*) data
;
517 const struct filelist_entry
*entry
;
518 const struct mpd_entity
*entity
;
520 const struct mpd_directory
*directory
;
521 const struct mpd_playlist
*playlist
;
525 assert(i
< filelist_length(fl
));
527 entry
= filelist_get(fl
, i
);
528 assert(entry
!= NULL
);
530 entity
= entry
->entity
;
531 if (entity
== NULL
) {
532 screen_browser_paint_directory(w
, width
, selected
, "..");
537 highlight
= (entry
->flags
& HIGHLIGHT
) != 0;
542 switch (mpd_entity_get_type(entity
)) {
543 case MPD_ENTITY_TYPE_DIRECTORY
:
544 directory
= mpd_entity_get_directory(entity
);
545 p
= utf8_to_locale(g_basename(mpd_directory_get_path(directory
)));
546 screen_browser_paint_directory(w
, width
, selected
, p
);
550 case MPD_ENTITY_TYPE_SONG
:
551 paint_song_row(w
, y
, width
, selected
, highlight
,
552 mpd_entity_get_song(entity
), NULL
);
555 case MPD_ENTITY_TYPE_PLAYLIST
:
556 playlist
= mpd_entity_get_playlist(entity
);
557 p
= utf8_to_locale(g_basename(mpd_playlist_get_path(playlist
)));
558 screen_browser_paint_playlist(w
, width
, selected
, p
);
563 row_paint_text(w
, width
, highlight
? COLOR_LIST_BOLD
: COLOR_LIST
,
564 selected
, "<unknown>");
569 screen_browser_paint(const struct screen_browser
*browser
)
571 list_window_paint2(browser
->lw
, screen_browser_paint_callback
,