ncmpc version 0.15
[ncmpc.git] / src / screen_browser.c
blob501c875ea5352c4225123e10b598e29d7f4d7312
1 /* ncmpc (Ncurses MPD Client)
2 * (c) 2004-2009 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_browser.h"
21 #include "i18n.h"
22 #include "options.h"
23 #include "charset.h"
24 #include "strfsong.h"
25 #include "screen_utils.h"
27 #include <string.h>
29 #define BUFSIZE 1024
31 #ifndef NCMPC_MINI
32 #define HIGHLIGHT (0x01)
33 #endif
35 static const char playlist_format[] = "*%s*";
37 #ifndef NCMPC_MINI
39 /* clear the highlight flag for all items in the filelist */
40 static void
41 clear_highlights(mpdclient_filelist_t *fl)
43 guint i;
45 for (i = 0; i < filelist_length(fl); ++i) {
46 struct filelist_entry *entry = filelist_get(fl, i);
48 entry->flags &= ~HIGHLIGHT;
52 /* change the highlight flag for a song */
53 static void
54 set_highlight(mpdclient_filelist_t *fl, mpd_Song *song, int highlight)
56 int i = filelist_find_song(fl, song);
57 struct filelist_entry *entry;
59 if (i < 0)
60 return;
62 entry = filelist_get(fl, i);
63 if (highlight)
64 entry->flags |= HIGHLIGHT;
65 else
66 entry->flags &= ~HIGHLIGHT;
69 /* sync highlight flags with playlist */
70 void
71 sync_highlights(mpdclient_t *c, mpdclient_filelist_t *fl)
73 guint i;
75 for (i = 0; i < filelist_length(fl); ++i) {
76 struct filelist_entry *entry = filelist_get(fl, i);
77 mpd_InfoEntity *entity = entry->entity;
79 if ( entity && entity->type==MPD_INFO_ENTITY_TYPE_SONG ) {
80 mpd_Song *song = entity->info.song;
82 if (playlist_get_index_from_same_song(c, song) >= 0)
83 entry->flags |= HIGHLIGHT;
84 else
85 entry->flags &= ~HIGHLIGHT;
90 /* the playlist has been updated -> fix highlights */
91 void
92 browser_playlist_changed(struct screen_browser *browser, mpdclient_t *c,
93 int event, gpointer data)
95 if (browser->filelist == NULL)
96 return;
98 switch(event) {
99 case PLAYLIST_EVENT_CLEAR:
100 clear_highlights(browser->filelist);
101 break;
102 case PLAYLIST_EVENT_ADD:
103 set_highlight(browser->filelist, (mpd_Song *) data, 1);
104 break;
105 case PLAYLIST_EVENT_DELETE:
106 set_highlight(browser->filelist, (mpd_Song *) data, 0);
107 break;
108 case PLAYLIST_EVENT_MOVE:
109 break;
110 default:
111 sync_highlights(c, browser->filelist);
112 break;
116 #endif
118 /* list_window callback */
119 const char *
120 browser_lw_callback(unsigned idx, bool *highlight, G_GNUC_UNUSED char **second_column, void *data)
122 static char buf[BUFSIZE];
123 mpdclient_filelist_t *fl = (mpdclient_filelist_t *) data;
124 filelist_entry_t *entry;
125 mpd_InfoEntity *entity;
127 if (fl == NULL || idx >= filelist_length(fl))
128 return NULL;
130 entry = filelist_get(fl, idx);
131 assert(entry != NULL);
133 entity = entry->entity;
134 #ifndef NCMPC_MINI
135 *highlight = (entry->flags & HIGHLIGHT) != 0;
136 #else
137 *highlight = false;
138 #endif
140 if( entity == NULL )
141 return "[..]";
143 if( entity->type==MPD_INFO_ENTITY_TYPE_DIRECTORY ) {
144 mpd_Directory *dir = entity->info.directory;
145 char *directory = utf8_to_locale(g_basename(dir->path));
147 g_snprintf(buf, BUFSIZE, "[%s]", directory);
148 g_free(directory);
149 return buf;
150 } else if( entity->type==MPD_INFO_ENTITY_TYPE_SONG ) {
151 const mpd_Song *song = entity->info.song;
153 strfsong(buf, BUFSIZE, options.list_format, song);
154 return buf;
155 } else if( entity->type==MPD_INFO_ENTITY_TYPE_PLAYLISTFILE ) {
156 mpd_PlaylistFile *plf = entity->info.playlistFile;
157 char *filename = utf8_to_locale(g_basename(plf->path));
159 g_snprintf(buf, BUFSIZE, playlist_format, filename);
160 g_free(filename);
161 return buf;
164 return "Error: Unknown entry!";
167 static bool
168 load_playlist(mpdclient_t *c, const mpd_PlaylistFile *plf)
170 char *filename = utf8_to_locale(plf->path);
172 if (mpdclient_cmd_load_playlist(c, plf->path) == 0)
173 screen_status_printf(_("Loading playlist %s..."),
174 g_basename(filename));
175 g_free(filename);
176 return true;
179 static bool
180 enqueue_and_play(mpdclient_t *c, filelist_entry_t *entry)
182 int idx;
183 mpd_InfoEntity *entity = entry->entity;
184 mpd_Song *song = entity->info.song;
186 #ifndef NCMPC_MINI
187 if (!(entry->flags & HIGHLIGHT)) {
188 #endif
189 if (mpdclient_cmd_add(c, song) == 0) {
190 char buf[BUFSIZE];
192 #ifndef NCMPC_MINI
193 entry->flags |= HIGHLIGHT;
194 #endif
195 strfsong(buf, BUFSIZE, options.list_format, song);
196 screen_status_printf(_("Adding \'%s\' to playlist"), buf);
197 mpdclient_update(c); /* get song id */
198 } else
199 return false;
200 #ifndef NCMPC_MINI
202 #endif
204 idx = playlist_get_index_from_same_song(c, song);
205 mpdclient_cmd_play(c, idx);
206 return true;
209 struct filelist_entry *
210 browser_get_selected_entry(const struct screen_browser *browser)
212 if (browser->filelist == NULL ||
213 browser->lw->selected_start < browser->lw->selected_end ||
214 browser->lw->selected >= filelist_length(browser->filelist))
215 return NULL;
217 return filelist_get(browser->filelist, browser->lw->selected);
220 static const struct mpd_InfoEntity *
221 browser_get_selected_entity(const struct screen_browser *browser)
223 const struct filelist_entry *entry = browser_get_selected_entry(browser);
225 return entry != NULL
226 ? entry->entity
227 : NULL;
230 static const struct mpd_song *
231 browser_get_selected_song(const struct screen_browser *browser)
233 const struct mpd_InfoEntity *entity = browser_get_selected_entity(browser);
235 return entity != NULL && entity->type == MPD_INFO_ENTITY_TYPE_SONG
236 ? entity->info.song
237 : NULL;
240 static struct filelist_entry *
241 browser_get_index(const struct screen_browser *browser, unsigned i)
243 if (browser->filelist == NULL ||
244 i >= filelist_length(browser->filelist))
245 return NULL;
247 return filelist_get(browser->filelist, i);
250 static bool
251 browser_handle_enter(struct screen_browser *browser, mpdclient_t *c)
253 struct filelist_entry *entry = browser_get_selected_entry(browser);
254 mpd_InfoEntity *entity;
256 if (entry == NULL)
257 return false;
259 entity = entry->entity;
260 if (entity == NULL)
261 return false;
263 if (entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE)
264 return load_playlist(c, entity->info.playlistFile);
265 else if (entity->type == MPD_INFO_ENTITY_TYPE_SONG)
266 return enqueue_and_play(c, entry);
267 return false;
270 static bool
271 browser_select_entry(mpdclient_t *c, filelist_entry_t *entry,
272 G_GNUC_UNUSED gboolean toggle)
274 assert(entry != NULL);
275 assert(entry->entity != NULL);
277 if (entry->entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE)
278 return load_playlist(c, entry->entity->info.playlistFile);
280 if (entry->entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
281 mpd_Directory *dir = entry->entity->info.directory;
283 if (mpdclient_cmd_add_path(c, dir->path) == 0) {
284 char *tmp = utf8_to_locale(dir->path);
286 screen_status_printf(_("Adding \'%s\' to playlist"), tmp);
287 g_free(tmp);
290 return true;
293 if (entry->entity->type != MPD_INFO_ENTITY_TYPE_SONG)
294 return false;
296 assert(entry->entity->info.song != NULL);
298 #ifndef NCMPC_MINI
299 if (!toggle || (entry->flags & HIGHLIGHT) == 0)
300 #endif
302 mpd_Song *song = entry->entity->info.song;
304 #ifndef NCMPC_MINI
305 entry->flags |= HIGHLIGHT;
306 #endif
308 if (mpdclient_cmd_add(c, song) == 0) {
309 char buf[BUFSIZE];
311 strfsong(buf, BUFSIZE, options.list_format, song);
312 screen_status_printf(_("Adding \'%s\' to playlist"), buf);
314 #ifndef NCMPC_MINI
315 } else {
316 /* remove song from playlist */
317 mpd_Song *song = entry->entity->info.song;
318 int idx;
320 entry->flags &= ~HIGHLIGHT;
322 while ((idx = playlist_get_index_from_same_song(c, song)) >= 0)
323 mpdclient_cmd_delete(c, idx);
324 #endif
327 return true;
330 static bool
331 browser_handle_select(struct screen_browser *browser, mpdclient_t *c)
333 struct filelist_entry *entry;
335 if (browser->lw->range_selection) {
336 for (unsigned i = browser->lw->selected_start;
337 i <= browser->lw->selected_end; i++) {
338 entry = browser_get_index(browser, i);
340 if (entry != NULL && entry->entity != NULL)
341 browser_select_entry(c, entry, TRUE);
343 return false;
344 } else {
345 entry = browser_get_selected_entry(browser);
347 if (entry == NULL || entry->entity == NULL)
348 return false;
350 return browser_select_entry(c, entry, TRUE);
354 static bool
355 browser_handle_add(struct screen_browser *browser, mpdclient_t *c)
357 struct filelist_entry *entry;
359 if (browser->lw->range_selection) {
360 for (unsigned i = browser->lw->selected_start;
361 i <= browser->lw->selected_end; i++) {
362 entry = browser_get_index(browser, i);
364 if (entry != NULL && entry->entity != NULL)
365 browser_select_entry(c, entry, FALSE);
367 return false;
368 } else {
369 entry = browser_get_selected_entry(browser);
371 if (entry == NULL || entry->entity == NULL)
372 return false;
374 return browser_select_entry(c, entry, FALSE);
378 static void
379 browser_handle_select_all(struct screen_browser *browser, mpdclient_t *c)
381 guint i;
383 if (browser->filelist == NULL)
384 return;
386 for (i = 0; i < filelist_length(browser->filelist); ++i) {
387 struct filelist_entry *entry = filelist_get(browser->filelist, i);
389 if (entry != NULL && entry->entity != NULL)
390 browser_select_entry(c, entry, FALSE);
394 #ifdef HAVE_GETMOUSE
395 static int
396 browser_handle_mouse_event(struct screen_browser *browser, mpdclient_t *c)
398 int row;
399 unsigned prev_selected = browser->lw->selected;
400 unsigned long bstate;
401 int length;
403 if (browser->filelist)
404 length = filelist_length(browser->filelist);
405 else
406 length = 0;
408 if (screen_get_mouse_event(c, &bstate, &row) ||
409 list_window_mouse(browser->lw, length, bstate, row))
410 return 1;
412 browser->lw->selected = browser->lw->start + row;
413 list_window_check_selected(browser->lw, length);
415 if( bstate & BUTTON1_CLICKED ) {
416 if (prev_selected == browser->lw->selected)
417 browser_handle_enter(browser, c);
418 } else if (bstate & BUTTON3_CLICKED) {
419 if (prev_selected == browser->lw->selected)
420 browser_handle_select(browser, c);
423 return 1;
425 #endif
427 bool
428 browser_cmd(struct screen_browser *browser,
429 struct mpdclient *c, command_t cmd)
431 const struct mpd_song *song;
433 if (browser->filelist == NULL)
434 return false;
436 switch (cmd) {
437 case CMD_PLAY:
438 browser_handle_enter(browser, c);
439 return true;
441 case CMD_SELECT:
442 if (browser_handle_select(browser, c))
443 /* continue and select next item... */
444 cmd = CMD_LIST_NEXT;
446 /* call list_window_cmd to go to the next item */
447 break;
449 case CMD_ADD:
450 if (browser_handle_add(browser, c))
451 /* continue and select next item... */
452 cmd = CMD_LIST_NEXT;
454 /* call list_window_cmd to go to the next item */
455 break;
457 case CMD_SELECT_ALL:
458 browser_handle_select_all(browser, c);
459 return true;
461 case CMD_LIST_FIND:
462 case CMD_LIST_RFIND:
463 case CMD_LIST_FIND_NEXT:
464 case CMD_LIST_RFIND_NEXT:
465 screen_find(browser->lw, filelist_length(browser->filelist),
466 cmd, browser_lw_callback,
467 browser->filelist);
468 return true;
469 case CMD_LIST_JUMP:
470 screen_jump(browser->lw, browser_lw_callback, browser->filelist);
471 return true;
473 #ifdef HAVE_GETMOUSE
474 case CMD_MOUSE_EVENT:
475 browser_handle_mouse_event(browser, c);
476 return true;
477 #endif
479 #ifdef ENABLE_SONG_SCREEN
480 case CMD_SCREEN_SONG:
481 song = browser_get_selected_song(browser);
482 if (song == NULL)
483 return false;
485 screen_song_switch(c, song);
486 return true;
487 #endif
489 case CMD_LOCATE:
490 song = browser_get_selected_song(browser);
491 if (song == NULL)
492 return false;
494 screen_file_goto_song(c, song);
495 return true;
497 #ifdef ENABLE_LYRICS_SCREEN
498 case CMD_SCREEN_LYRICS:
499 song = browser_get_selected_song(browser);
500 if (song == NULL)
501 return false;
503 screen_lyrics_switch(c, song, false);
504 return true;
505 #endif
506 case CMD_SCREEN_SWAP:
507 screen_swap(c, browser_get_selected_song(browser));
508 return true;
510 default:
511 break;
514 if (list_window_cmd(browser->lw, filelist_length(browser->filelist),
515 cmd))
516 return true;
518 return false;