screen_queue: use mpdclient_settings_name() for the title
[ncmpc.git] / src / screen_browser.c
blobc38ae9ea81490f539723a4e7e6dd2e8da0fc76d8
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 "config.h"
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"
27 #include "screen.h"
28 #include "i18n.h"
29 #include "options.h"
30 #include "charset.h"
31 #include "strfsong.h"
32 #include "mpdclient.h"
33 #include "filelist.h"
34 #include "colors.h"
35 #include "paint.h"
36 #include "song_paint.h"
38 #include <mpd/client.h>
40 #include <string.h>
42 #define BUFSIZE 1024
44 #ifndef NCMPC_MINI
45 #define HIGHLIGHT (0x01)
46 #endif
48 #ifndef NCMPC_MINI
50 /* sync highlight flags with playlist */
51 void
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,
64 song) >= 0)
65 entry->flags |= HIGHLIGHT;
66 else
67 entry->flags &= ~HIGHLIGHT;
72 #endif
74 /* list_window callback */
75 static const char *
76 browser_lw_callback(unsigned idx, void *data)
78 const struct filelist *fl = (const struct filelist *) data;
79 static char buf[BUFSIZE];
81 assert(fl != NULL);
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;
89 if( entity == NULL )
90 return "..";
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));
97 g_free(directory);
98 return 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);
103 return buf;
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));
110 g_free(filename);
111 return buf;
114 return "Error: Unknown entry!";
117 static bool
118 load_playlist(struct mpdclient *c, const struct mpd_playlist *playlist)
120 struct mpd_connection *connection = mpdclient_get_connection(c);
122 if (connection == NULL)
123 return false;
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));
129 g_free(filename);
131 c->events |= MPD_IDLE_QUEUE;
132 } else
133 mpdclient_handle_error(c);
135 return true;
138 static bool
139 enqueue_and_play(struct mpdclient *c, struct filelist_entry *entry)
141 struct mpd_connection *connection = mpdclient_get_connection(c);
142 if (connection == NULL)
143 return false;
145 const struct mpd_song *song = mpd_entity_get_song(entry->entity);
146 int id;
148 #ifndef NCMPC_MINI
149 if (!(entry->flags & HIGHLIGHT))
150 id = -1;
151 else
152 #endif
153 id = playlist_get_id_from_same_song(&c->playlist, song);
155 if (id < 0) {
156 char buf[BUFSIZE];
158 id = mpd_run_add_id(connection, mpd_song_get_uri(song));
159 if (id < 0) {
160 mpdclient_handle_error(c);
161 return false;
164 #ifndef NCMPC_MINI
165 entry->flags |= HIGHLIGHT;
166 #endif
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);
173 return false;
176 return true;
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))
190 return NULL;
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);
200 return entry != NULL
201 ? entry->entity
202 : NULL;
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)
213 : NULL;
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))
221 return NULL;
223 return filelist_get(browser->filelist, i);
226 static bool
227 browser_handle_enter(struct screen_browser *browser, struct mpdclient *c)
229 struct filelist_entry *entry = browser_get_selected_entry(browser);
230 if (entry == NULL)
231 return false;
233 struct mpd_entity *entity = entry->entity;
234 if (entity == NULL)
235 return false;
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);
241 return false;
244 static bool
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);
262 g_free(tmp);
265 return true;
268 if (mpd_entity_get_type(entry->entity) != MPD_ENTITY_TYPE_SONG)
269 return false;
271 #ifndef NCMPC_MINI
272 if (!toggle || (entry->flags & HIGHLIGHT) == 0)
273 #endif
275 const struct mpd_song *song =
276 mpd_entity_get_song(entry->entity);
278 #ifndef NCMPC_MINI
279 entry->flags |= HIGHLIGHT;
280 #endif
282 if (mpdclient_cmd_add(c, song)) {
283 char buf[BUFSIZE];
285 strfsong(buf, BUFSIZE, options.list_format, song);
286 screen_status_printf(_("Adding \'%s\' to queue"), buf);
288 #ifndef NCMPC_MINI
289 } else {
290 /* remove song from playlist */
291 const struct mpd_song *song =
292 mpd_entity_get_song(entry->entity);
293 int idx;
295 entry->flags &= ~HIGHLIGHT;
297 while ((idx = playlist_get_index_from_same_song(&c->playlist,
298 song)) >= 0)
299 mpdclient_cmd_delete(c, idx);
300 #endif
303 return true;
306 static bool
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;
322 static bool
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) ||
333 success;
336 return range.end == range.start + 1 && success;
339 static void
340 browser_handle_select_all(struct screen_browser *browser, struct mpdclient *c)
342 if (browser->filelist == NULL)
343 return;
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);
353 #ifdef HAVE_GETMOUSE
354 static int
355 browser_handle_mouse_event(struct screen_browser *browser, struct mpdclient *c)
357 int row;
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))
363 return 1;
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);
375 return 1;
377 #endif
379 static void
380 screen_browser_paint_callback(WINDOW *w, unsigned i, unsigned y,
381 unsigned width, bool selected, const void *data);
383 bool
384 browser_cmd(struct screen_browser *browser,
385 struct mpdclient *c, command_t cmd)
387 if (browser->filelist == NULL)
388 return false;
390 if (list_window_cmd(browser->lw, cmd))
391 return true;
393 switch (cmd) {
394 #if defined(ENABLE_SONG_SCREEN) || defined(ENABLE_LYRICS_SCREEN)
395 const struct mpd_song *song;
396 #endif
398 case CMD_LIST_FIND:
399 case CMD_LIST_RFIND:
400 case CMD_LIST_FIND_NEXT:
401 case CMD_LIST_RFIND_NEXT:
402 screen_find(browser->lw, cmd, browser_lw_callback,
403 browser->filelist);
404 return true;
405 case CMD_LIST_JUMP:
406 screen_jump(browser->lw,
407 browser_lw_callback, browser->filelist,
408 screen_browser_paint_callback, browser);
409 return true;
411 #ifdef HAVE_GETMOUSE
412 case CMD_MOUSE_EVENT:
413 browser_handle_mouse_event(browser, c);
414 return true;
415 #endif
417 #ifdef ENABLE_SONG_SCREEN
418 case CMD_SCREEN_SONG:
419 song = browser_get_selected_song(browser);
420 if (song == NULL)
421 return false;
423 screen_song_switch(c, song);
424 return true;
425 #endif
427 #ifdef ENABLE_LYRICS_SCREEN
428 case CMD_SCREEN_LYRICS:
429 song = browser_get_selected_song(browser);
430 if (song == NULL)
431 return false;
433 screen_lyrics_switch(c, song, false);
434 return true;
435 #endif
436 case CMD_SCREEN_SWAP:
437 screen_swap(c, browser_get_selected_song(browser));
438 return true;
440 default:
441 break;
444 if (!mpdclient_is_connected(c))
445 return false;
447 switch (cmd) {
448 const struct mpd_song *song;
450 case CMD_PLAY:
451 browser_handle_enter(browser, c);
452 return true;
454 case CMD_SELECT:
455 if (browser_handle_select(browser, c))
456 list_window_cmd(browser->lw, CMD_LIST_NEXT);
457 return true;
459 case CMD_ADD:
460 if (browser_handle_add(browser, c))
461 list_window_cmd(browser->lw, CMD_LIST_NEXT);
462 return true;
464 case CMD_SELECT_ALL:
465 browser_handle_select_all(browser, c);
466 return true;
468 case CMD_LOCATE:
469 song = browser_get_selected_song(browser);
470 if (song == NULL)
471 return false;
473 screen_file_goto_song(c, song);
474 return true;
476 default:
477 break;
480 return false;
483 void
484 screen_browser_paint_directory(WINDOW *w, unsigned width,
485 bool selected, const char *name)
487 row_color(w, COLOR_DIRECTORY, selected);
489 waddch(w, '[');
490 waddstr(w, name);
491 waddch(w, ']');
493 /* erase the unused space after the text */
494 row_clear_to_eol(w, width, selected);
497 static void
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);
504 static void
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, "..");
521 return;
524 #ifndef NCMPC_MINI
525 const bool highlight = (entry->flags & HIGHLIGHT) != 0;
526 #else
527 const bool highlight = false;
528 #endif
530 switch (mpd_entity_get_type(entity)) {
531 const struct mpd_directory *directory;
532 const struct mpd_playlist *playlist;
533 char *p;
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);
539 g_free(p);
540 break;
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);
545 break;
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);
551 g_free(p);
552 break;
554 default:
555 row_paint_text(w, width, highlight ? COLOR_LIST_BOLD : COLOR_LIST,
556 selected, "<unknown>");
560 void
561 screen_browser_paint(const struct screen_browser *browser)
563 list_window_paint2(browser->lw, screen_browser_paint_callback,
564 browser);