Code

screen_browser: pass playlist object to sync_highlights()
[ncmpc.git] / src / screen_browser.c
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.
18 */
20 #include "screen_browser.h"
21 #include "screen_file.h"
22 #include "i18n.h"
23 #include "options.h"
24 #include "charset.h"
25 #include "strfsong.h"
26 #include "screen_utils.h"
27 #include "mpdclient.h"
28 #include "filelist.h"
30 #include <mpd/client.h>
32 #include <string.h>
34 #define BUFSIZE 1024
36 #ifndef NCMPC_MINI
37 #define HIGHLIGHT  (0x01)
38 #endif
40 static const char playlist_format[] = "*%s*";
42 #ifndef NCMPC_MINI
44 /* sync highlight flags with playlist */
45 void
46 screen_browser_sync_highlights(struct filelist *fl,
47                                const struct mpdclient_playlist *playlist)
48 {
49         guint i;
51         for (i = 0; i < filelist_length(fl); ++i) {
52                 struct filelist_entry *entry = filelist_get(fl, i);
53                 struct mpd_entity *entity = entry->entity;
55                 if (entity != NULL && mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_SONG) {
56                         const struct mpd_song *song =
57                                 mpd_entity_get_song(entity);
59                         if (playlist_get_index_from_same_song(playlist,
60                                                               song) >= 0)
61                                 entry->flags |= HIGHLIGHT;
62                         else
63                                 entry->flags &= ~HIGHLIGHT;
64                 }
65         }
66 }
68 #endif
70 /* list_window callback */
71 const char *
72 browser_lw_callback(unsigned idx, bool *highlight, G_GNUC_UNUSED char **second_column, void *data)
73 {
74         struct filelist *fl = (struct filelist *) data;
75         static char buf[BUFSIZE];
76         struct filelist_entry *entry;
77         struct mpd_entity *entity;
79         if (fl == NULL || idx >= filelist_length(fl))
80                 return NULL;
82         entry = filelist_get(fl, idx);
83         assert(entry != NULL);
85         entity = entry->entity;
86 #ifndef NCMPC_MINI
87         *highlight = (entry->flags & HIGHLIGHT) != 0;
88 #else
89         *highlight = false;
90 #endif
92         if( entity == NULL )
93                 return "[..]";
95         if (mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_DIRECTORY) {
96                 const struct mpd_directory *dir =
97                         mpd_entity_get_directory(entity);
98                 char *directory = utf8_to_locale(g_basename(mpd_directory_get_path(dir)));
100                 g_snprintf(buf, BUFSIZE, "[%s]", directory);
101                 g_free(directory);
102                 return 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);
107                 return buf;
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_snprintf(buf, BUFSIZE, playlist_format, filename);
114                 g_free(filename);
115                 return buf;
116         }
118         return "Error: Unknown entry!";
121 static bool
122 load_playlist(struct mpdclient *c, const struct mpd_playlist *playlist)
124         char *filename = utf8_to_locale(mpd_playlist_get_path(playlist));
126         if (mpdclient_cmd_load_playlist(c, mpd_playlist_get_path(playlist)) == 0)
127                 screen_status_printf(_("Loading playlist %s..."),
128                                      g_basename(filename));
129         g_free(filename);
130         return true;
133 static bool
134 enqueue_and_play(struct mpdclient *c, struct filelist_entry *entry)
136         const struct mpd_song *song = mpd_entity_get_song(entry->entity);
137         int id;
139 #ifndef NCMPC_MINI
140         if (!(entry->flags & HIGHLIGHT))
141                 id = -1;
142         else
143 #endif
144                 id = playlist_get_id_from_same_song(&c->playlist, song);
146         if (id < 0) {
147                 char buf[BUFSIZE];
149                 id = mpd_run_add_id(c->connection, mpd_song_get_uri(song));
150                 if (id < 0) {
151                         mpdclient_handle_error(c);
152                         return false;
153                 }
155 #ifndef NCMPC_MINI
156                 entry->flags |= HIGHLIGHT;
157 #endif
158                 strfsong(buf, BUFSIZE, options.list_format, song);
159                 screen_status_printf(_("Adding \'%s\' to playlist"), buf);
160         }
162         if (!mpd_run_play_id(c->connection, id)) {
163                 mpdclient_handle_error(c);
164                 return false;
165         }
167         return true;
170 struct filelist_entry *
171 browser_get_selected_entry(const struct screen_browser *browser)
173         if (browser->filelist == NULL ||
174             browser->lw->selected_start < browser->lw->selected_end ||
175             browser->lw->selected >= filelist_length(browser->filelist))
176                 return NULL;
178         return filelist_get(browser->filelist, browser->lw->selected);
181 static const struct mpd_entity *
182 browser_get_selected_entity(const struct screen_browser *browser)
184         const struct filelist_entry *entry = browser_get_selected_entry(browser);
186         return entry != NULL
187                 ? entry->entity
188                 : NULL;
191 static const struct mpd_song *
192 browser_get_selected_song(const struct screen_browser *browser)
194         const struct mpd_entity *entity = browser_get_selected_entity(browser);
196         return entity != NULL &&
197                 mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_SONG
198                 ? mpd_entity_get_song(entity)
199                 : NULL;
202 static struct filelist_entry *
203 browser_get_index(const struct screen_browser *browser, unsigned i)
205         if (browser->filelist == NULL ||
206             i >= filelist_length(browser->filelist))
207                 return NULL;
209         return filelist_get(browser->filelist, i);
212 static bool
213 browser_handle_enter(struct screen_browser *browser, struct mpdclient *c)
215         struct filelist_entry *entry = browser_get_selected_entry(browser);
216         struct mpd_entity *entity;
218         if (entry == NULL)
219                 return false;
221         entity = entry->entity;
222         if (entity == NULL)
223                 return false;
225         if (mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_PLAYLIST)
226                 return load_playlist(c, mpd_entity_get_playlist(entity));
227         else if (mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_SONG)
228                 return enqueue_and_play(c, entry);
229         return false;
232 static bool
233 browser_select_entry(struct mpdclient *c, struct filelist_entry *entry,
234                      G_GNUC_UNUSED gboolean toggle)
236         assert(entry != NULL);
237         assert(entry->entity != NULL);
239         if (mpd_entity_get_type(entry->entity) == MPD_ENTITY_TYPE_PLAYLIST)
240                 return load_playlist(c, mpd_entity_get_playlist(entry->entity));
242         if (mpd_entity_get_type(entry->entity) == MPD_ENTITY_TYPE_DIRECTORY) {
243                 const struct mpd_directory *dir =
244                         mpd_entity_get_directory(entry->entity);
246                 if (mpdclient_cmd_add_path(c, mpd_directory_get_path(dir)) == 0) {
247                         char *tmp = utf8_to_locale(mpd_directory_get_path(dir));
249                         screen_status_printf(_("Adding \'%s\' to playlist"), tmp);
250                         g_free(tmp);
251                 }
253                 return true;
254         }
256         if (mpd_entity_get_type(entry->entity) != MPD_ENTITY_TYPE_SONG)
257                 return false;
259 #ifndef NCMPC_MINI
260         if (!toggle || (entry->flags & HIGHLIGHT) == 0)
261 #endif
262         {
263                 const struct mpd_song *song =
264                         mpd_entity_get_song(entry->entity);
266 #ifndef NCMPC_MINI
267                 entry->flags |= HIGHLIGHT;
268 #endif
270                 if (mpdclient_cmd_add(c, song) == 0) {
271                         char buf[BUFSIZE];
273                         strfsong(buf, BUFSIZE, options.list_format, song);
274                         screen_status_printf(_("Adding \'%s\' to playlist"), buf);
275                 }
276 #ifndef NCMPC_MINI
277         } else {
278                 /* remove song from playlist */
279                 const struct mpd_song *song =
280                         mpd_entity_get_song(entry->entity);
281                 int idx;
283                 entry->flags &= ~HIGHLIGHT;
285                 while ((idx = playlist_get_index_from_same_song(&c->playlist,
286                                                                 song)) >= 0)
287                         mpdclient_cmd_delete(c, idx);
288 #endif
289         }
291         return true;
294 static bool
295 browser_handle_select(struct screen_browser *browser, struct mpdclient *c)
297         struct filelist_entry *entry;
299         if (browser->lw->range_selection) {
300                 for (unsigned i = browser->lw->selected_start;
301                          i <= browser->lw->selected_end; i++) {
302                         entry = browser_get_index(browser, i);
304                         if (entry != NULL && entry->entity != NULL)
305                                 browser_select_entry(c, entry, TRUE);
306                 }
307                 return false;
308         } else {
309                 entry = browser_get_selected_entry(browser);
311                 if (entry == NULL || entry->entity == NULL)
312                         return false;
314                 return browser_select_entry(c, entry, TRUE);
315         }
318 static bool
319 browser_handle_add(struct screen_browser *browser, struct mpdclient *c)
321         struct filelist_entry *entry;
323         if (browser->lw->range_selection) {
324                 for (unsigned i = browser->lw->selected_start;
325                          i <= browser->lw->selected_end; i++) {
326                         entry = browser_get_index(browser, i);
328                         if (entry != NULL && entry->entity != NULL)
329                                 browser_select_entry(c, entry, FALSE);
330                 }
331                 return false;
332         } else {
333                 entry = browser_get_selected_entry(browser);
335                 if (entry == NULL || entry->entity == NULL)
336                         return false;
338                 return browser_select_entry(c, entry, FALSE);
339         }
342 static void
343 browser_handle_select_all(struct screen_browser *browser, struct mpdclient *c)
345         guint i;
347         if (browser->filelist == NULL)
348                 return;
350         for (i = 0; i < filelist_length(browser->filelist); ++i) {
351                 struct filelist_entry *entry = filelist_get(browser->filelist, i);
353                 if (entry != NULL && entry->entity != NULL)
354                         browser_select_entry(c, entry, FALSE);
355         }
358 #ifdef HAVE_GETMOUSE
359 static int
360 browser_handle_mouse_event(struct screen_browser *browser, struct mpdclient *c)
362         int row;
363         unsigned prev_selected = browser->lw->selected;
364         unsigned long bstate;
365         int length;
367         if (browser->filelist)
368                 length = filelist_length(browser->filelist);
369         else
370                 length = 0;
372         if (screen_get_mouse_event(c, &bstate, &row) ||
373             list_window_mouse(browser->lw, length, bstate, row))
374                 return 1;
376         browser->lw->selected = browser->lw->start + row;
377         list_window_check_selected(browser->lw, length);
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);
385         }
387         return 1;
389 #endif
391 bool
392 browser_cmd(struct screen_browser *browser,
393             struct mpdclient *c, command_t cmd)
395         const struct mpd_song *song;
397         if (browser->filelist == NULL)
398                 return false;
400         switch (cmd) {
401         case CMD_PLAY:
402                 browser_handle_enter(browser, c);
403                 return true;
405         case CMD_SELECT:
406                 if (browser_handle_select(browser, c))
407                         /* continue and select next item... */
408                         cmd = CMD_LIST_NEXT;
410                 /* call list_window_cmd to go to the next item */
411                 break;
413         case CMD_ADD:
414                 if (browser_handle_add(browser, c))
415                         /* continue and select next item... */
416                         cmd = CMD_LIST_NEXT;
418                 /* call list_window_cmd to go to the next item */
419                 break;
421         case CMD_SELECT_ALL:
422                 browser_handle_select_all(browser, c);
423                 return true;
425         case CMD_LIST_FIND:
426         case CMD_LIST_RFIND:
427         case CMD_LIST_FIND_NEXT:
428         case CMD_LIST_RFIND_NEXT:
429                 screen_find(browser->lw, filelist_length(browser->filelist),
430                             cmd, browser_lw_callback,
431                             browser->filelist);
432                 return true;
433         case CMD_LIST_JUMP:
434                 screen_jump(browser->lw, browser_lw_callback, browser->filelist);
435                 return true;
437 #ifdef HAVE_GETMOUSE
438         case CMD_MOUSE_EVENT:
439                 browser_handle_mouse_event(browser, c);
440                 return true;
441 #endif
443 #ifdef ENABLE_SONG_SCREEN
444         case CMD_SCREEN_SONG:
445                 song = browser_get_selected_song(browser);
446                 if (song == NULL)
447                         return false;
449                 screen_song_switch(c, song);
450                 return true;
451 #endif
453         case CMD_LOCATE:
454                 song = browser_get_selected_song(browser);
455                 if (song == NULL)
456                         return false;
458                 screen_file_goto_song(c, song);
459                 return true;
461 #ifdef ENABLE_LYRICS_SCREEN
462         case CMD_SCREEN_LYRICS:
463                 song = browser_get_selected_song(browser);
464                 if (song == NULL)
465                         return false;
467                 screen_lyrics_switch(c, song, false);
468                 return true;
469 #endif
470         case CMD_SCREEN_SWAP:
471                 screen_swap(c, browser_get_selected_song(browser));
472                 return true;
474         default:
475                 break;
476         }
478         if (list_window_cmd(browser->lw, filelist_length(browser->filelist),
479                             cmd))
480                 return true;
482         return false;