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.
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
)
55 for (unsigned i
= 0; i
< filelist_length(fl
); ++i
) {
56 struct filelist_entry
*entry
= filelist_get(fl
, i
);
57 const struct mpd_entity
*entity
= entry
->entity
;
59 if (entity
!= NULL
&& mpd_entity_get_type(entity
) == MPD_ENTITY_TYPE_SONG
) {
60 const struct mpd_song
*song
=
61 mpd_entity_get_song(entity
);
63 if (playlist_get_index_from_same_song(playlist
,
65 entry
->flags
|= HIGHLIGHT
;
67 entry
->flags
&= ~HIGHLIGHT
;
74 /* list_window callback */
76 browser_lw_callback(unsigned idx
, void *data
)
78 const struct filelist
*fl
= (const struct filelist
*) data
;
79 static char buf
[BUFSIZE
];
82 assert(idx
< filelist_length(fl
));
84 const struct filelist_entry
*entry
= filelist_get(fl
, idx
);
85 assert(entry
!= NULL
);
87 const struct mpd_entity
*entity
= entry
->entity
;
92 if (mpd_entity_get_type(entity
) == MPD_ENTITY_TYPE_DIRECTORY
) {
93 const struct mpd_directory
*dir
=
94 mpd_entity_get_directory(entity
);
95 char *directory
= utf8_to_locale(g_basename(mpd_directory_get_path(dir
)));
96 g_strlcpy(buf
, directory
, sizeof(buf
));
99 } else if (mpd_entity_get_type(entity
) == MPD_ENTITY_TYPE_SONG
) {
100 const struct mpd_song
*song
= mpd_entity_get_song(entity
);
102 strfsong(buf
, BUFSIZE
, options
.list_format
, song
);
104 } else if (mpd_entity_get_type(entity
) == MPD_ENTITY_TYPE_PLAYLIST
) {
105 const struct mpd_playlist
*playlist
=
106 mpd_entity_get_playlist(entity
);
107 char *filename
= utf8_to_locale(g_basename(mpd_playlist_get_path(playlist
)));
109 g_strlcpy(buf
, filename
, sizeof(buf
));
114 return "Error: Unknown entry!";
118 load_playlist(struct mpdclient
*c
, const struct mpd_playlist
*playlist
)
120 struct mpd_connection
*connection
= mpdclient_get_connection(c
);
122 if (connection
== NULL
)
125 if (mpd_run_load(connection
, mpd_playlist_get_path(playlist
))) {
126 char *filename
= utf8_to_locale(mpd_playlist_get_path(playlist
));
127 screen_status_printf(_("Loading playlist %s..."),
128 g_basename(filename
));
131 c
->events
|= MPD_IDLE_QUEUE
;
133 mpdclient_handle_error(c
);
139 enqueue_and_play(struct mpdclient
*c
, struct filelist_entry
*entry
)
141 struct mpd_connection
*connection
= mpdclient_get_connection(c
);
142 if (connection
== NULL
)
145 const struct mpd_song
*song
= mpd_entity_get_song(entry
->entity
);
149 if (!(entry
->flags
& HIGHLIGHT
))
153 id
= playlist_get_id_from_same_song(&c
->playlist
, song
);
158 id
= mpd_run_add_id(connection
, mpd_song_get_uri(song
));
160 mpdclient_handle_error(c
);
165 entry
->flags
|= HIGHLIGHT
;
167 strfsong(buf
, BUFSIZE
, options
.list_format
, song
);
168 screen_status_printf(_("Adding \'%s\' to queue"), buf
);
171 if (!mpd_run_play_id(connection
, id
)) {
172 mpdclient_handle_error(c
);
179 struct filelist_entry
*
180 browser_get_selected_entry(const struct screen_browser
*browser
)
182 struct list_window_range range
;
184 list_window_get_range(browser
->lw
, &range
);
186 if (browser
->filelist
== NULL
||
187 range
.end
<= range
.start
||
188 range
.end
> range
.start
+ 1 ||
189 range
.start
>= filelist_length(browser
->filelist
))
192 return filelist_get(browser
->filelist
, range
.start
);
195 static const struct mpd_entity
*
196 browser_get_selected_entity(const struct screen_browser
*browser
)
198 const struct filelist_entry
*entry
= browser_get_selected_entry(browser
);
205 static const struct mpd_song
*
206 browser_get_selected_song(const struct screen_browser
*browser
)
208 const struct mpd_entity
*entity
= browser_get_selected_entity(browser
);
210 return entity
!= NULL
&&
211 mpd_entity_get_type(entity
) == MPD_ENTITY_TYPE_SONG
212 ? mpd_entity_get_song(entity
)
216 static struct filelist_entry
*
217 browser_get_index(const struct screen_browser
*browser
, unsigned i
)
219 if (browser
->filelist
== NULL
||
220 i
>= filelist_length(browser
->filelist
))
223 return filelist_get(browser
->filelist
, i
);
227 browser_handle_enter(struct screen_browser
*browser
, struct mpdclient
*c
)
229 struct filelist_entry
*entry
= browser_get_selected_entry(browser
);
233 struct mpd_entity
*entity
= entry
->entity
;
237 if (mpd_entity_get_type(entity
) == MPD_ENTITY_TYPE_PLAYLIST
)
238 return load_playlist(c
, mpd_entity_get_playlist(entity
));
239 else if (mpd_entity_get_type(entity
) == MPD_ENTITY_TYPE_SONG
)
240 return enqueue_and_play(c
, entry
);
245 browser_select_entry(struct mpdclient
*c
, struct filelist_entry
*entry
,
246 gcc_unused gboolean toggle
)
248 assert(entry
!= NULL
);
249 assert(entry
->entity
!= NULL
);
251 if (mpd_entity_get_type(entry
->entity
) == MPD_ENTITY_TYPE_PLAYLIST
)
252 return load_playlist(c
, mpd_entity_get_playlist(entry
->entity
));
254 if (mpd_entity_get_type(entry
->entity
) == MPD_ENTITY_TYPE_DIRECTORY
) {
255 const struct mpd_directory
*dir
=
256 mpd_entity_get_directory(entry
->entity
);
258 if (mpdclient_cmd_add_path(c
, mpd_directory_get_path(dir
))) {
259 char *tmp
= utf8_to_locale(mpd_directory_get_path(dir
));
261 screen_status_printf(_("Adding \'%s\' to queue"), tmp
);
268 if (mpd_entity_get_type(entry
->entity
) != MPD_ENTITY_TYPE_SONG
)
272 if (!toggle
|| (entry
->flags
& HIGHLIGHT
) == 0)
275 const struct mpd_song
*song
=
276 mpd_entity_get_song(entry
->entity
);
279 entry
->flags
|= HIGHLIGHT
;
282 if (mpdclient_cmd_add(c
, song
)) {
285 strfsong(buf
, BUFSIZE
, options
.list_format
, song
);
286 screen_status_printf(_("Adding \'%s\' to queue"), buf
);
290 /* remove song from playlist */
291 const struct mpd_song
*song
=
292 mpd_entity_get_song(entry
->entity
);
295 entry
->flags
&= ~HIGHLIGHT
;
297 while ((idx
= playlist_get_index_from_same_song(&c
->playlist
,
299 mpdclient_cmd_delete(c
, idx
);
307 browser_handle_select(struct screen_browser
*browser
, struct mpdclient
*c
)
309 struct list_window_range range
;
310 bool success
= false;
312 list_window_get_range(browser
->lw
, &range
);
313 for (unsigned i
= range
.start
; i
< range
.end
; ++i
) {
314 struct filelist_entry
*entry
= browser_get_index(browser
, i
);
315 if (entry
!= NULL
&& entry
->entity
!= NULL
)
316 success
= browser_select_entry(c
, entry
, TRUE
);
319 return range
.end
== range
.start
+ 1 && success
;
323 browser_handle_add(struct screen_browser
*browser
, struct mpdclient
*c
)
325 struct list_window_range range
;
326 bool success
= false;
328 list_window_get_range(browser
->lw
, &range
);
329 for (unsigned i
= range
.start
; i
< range
.end
; ++i
) {
330 struct filelist_entry
*entry
= browser_get_index(browser
, i
);
331 if (entry
!= NULL
&& entry
->entity
!= NULL
)
332 success
= browser_select_entry(c
, entry
, FALSE
) ||
336 return range
.end
== range
.start
+ 1 && success
;
340 browser_handle_select_all(struct screen_browser
*browser
, struct mpdclient
*c
)
342 if (browser
->filelist
== NULL
)
345 for (unsigned i
= 0; i
< filelist_length(browser
->filelist
); ++i
) {
346 struct filelist_entry
*entry
= filelist_get(browser
->filelist
, i
);
348 if (entry
!= NULL
&& entry
->entity
!= NULL
)
349 browser_select_entry(c
, entry
, FALSE
);
355 browser_handle_mouse_event(struct screen_browser
*browser
, struct mpdclient
*c
)
358 unsigned prev_selected
= browser
->lw
->selected
;
359 unsigned long bstate
;
361 if (screen_get_mouse_event(c
, &bstate
, &row
) ||
362 list_window_mouse(browser
->lw
, bstate
, row
))
365 list_window_set_cursor(browser
->lw
, browser
->lw
->start
+ row
);
367 if( bstate
& BUTTON1_CLICKED
) {
368 if (prev_selected
== browser
->lw
->selected
)
369 browser_handle_enter(browser
, c
);
370 } else if (bstate
& BUTTON3_CLICKED
) {
371 if (prev_selected
== browser
->lw
->selected
)
372 browser_handle_select(browser
, c
);
380 screen_browser_paint_callback(WINDOW
*w
, unsigned i
, unsigned y
,
381 unsigned width
, bool selected
, const void *data
);
384 browser_cmd(struct screen_browser
*browser
,
385 struct mpdclient
*c
, command_t cmd
)
387 if (browser
->filelist
== NULL
)
390 if (list_window_cmd(browser
->lw
, cmd
))
394 #if defined(ENABLE_SONG_SCREEN) || defined(ENABLE_LYRICS_SCREEN)
395 const struct mpd_song
*song
;
400 case CMD_LIST_FIND_NEXT
:
401 case CMD_LIST_RFIND_NEXT
:
402 screen_find(browser
->lw
, cmd
, browser_lw_callback
,
406 screen_jump(browser
->lw
,
407 browser_lw_callback
, browser
->filelist
,
408 screen_browser_paint_callback
, browser
);
412 case CMD_MOUSE_EVENT
:
413 browser_handle_mouse_event(browser
, c
);
417 #ifdef ENABLE_SONG_SCREEN
418 case CMD_SCREEN_SONG
:
419 song
= browser_get_selected_song(browser
);
423 screen_song_switch(c
, song
);
427 #ifdef ENABLE_LYRICS_SCREEN
428 case CMD_SCREEN_LYRICS
:
429 song
= browser_get_selected_song(browser
);
433 screen_lyrics_switch(c
, song
, false);
436 case CMD_SCREEN_SWAP
:
437 screen_swap(c
, browser_get_selected_song(browser
));
444 if (!mpdclient_is_connected(c
))
448 const struct mpd_song
*song
;
451 browser_handle_enter(browser
, c
);
455 if (browser_handle_select(browser
, c
))
456 list_window_cmd(browser
->lw
, CMD_LIST_NEXT
);
460 if (browser_handle_add(browser
, c
))
461 list_window_cmd(browser
->lw
, CMD_LIST_NEXT
);
465 browser_handle_select_all(browser
, c
);
469 song
= browser_get_selected_song(browser
);
473 screen_file_goto_song(c
, song
);
484 screen_browser_paint_directory(WINDOW
*w
, unsigned width
,
485 bool selected
, const char *name
)
487 row_color(w
, COLOR_DIRECTORY
, selected
);
493 /* erase the unused space after the text */
494 row_clear_to_eol(w
, width
, selected
);
498 screen_browser_paint_playlist(WINDOW
*w
, unsigned width
,
499 bool selected
, const char *name
)
501 row_paint_text(w
, width
, COLOR_PLAYLIST
, selected
, name
);
505 screen_browser_paint_callback(WINDOW
*w
, unsigned i
,
506 unsigned y
, unsigned width
,
507 bool selected
, const void *data
)
509 const struct screen_browser
*browser
= (const struct screen_browser
*) data
;
511 assert(browser
!= NULL
);
512 assert(browser
->filelist
!= NULL
);
513 assert(i
< filelist_length(browser
->filelist
));
515 const struct filelist_entry
*entry
= filelist_get(browser
->filelist
, i
);
516 assert(entry
!= NULL
);
518 const struct mpd_entity
*entity
= entry
->entity
;
519 if (entity
== NULL
) {
520 screen_browser_paint_directory(w
, width
, selected
, "..");
525 const bool highlight
= (entry
->flags
& HIGHLIGHT
) != 0;
527 const bool highlight
= false;
530 switch (mpd_entity_get_type(entity
)) {
531 const struct mpd_directory
*directory
;
532 const struct mpd_playlist
*playlist
;
535 case MPD_ENTITY_TYPE_DIRECTORY
:
536 directory
= mpd_entity_get_directory(entity
);
537 p
= utf8_to_locale(g_basename(mpd_directory_get_path(directory
)));
538 screen_browser_paint_directory(w
, width
, selected
, p
);
542 case MPD_ENTITY_TYPE_SONG
:
543 paint_song_row(w
, y
, width
, selected
, highlight
,
544 mpd_entity_get_song(entity
), NULL
, browser
->song_format
);
547 case MPD_ENTITY_TYPE_PLAYLIST
:
548 playlist
= mpd_entity_get_playlist(entity
);
549 p
= utf8_to_locale(g_basename(mpd_playlist_get_path(playlist
)));
550 screen_browser_paint_playlist(w
, width
, selected
, p
);
555 row_paint_text(w
, width
, highlight
? COLOR_LIST_BOLD
: COLOR_LIST
,
556 selected
, "<unknown>");
561 screen_browser_paint(const struct screen_browser
*browser
)
563 list_window_paint2(browser
->lw
, screen_browser_paint_callback
,