Code

screen_browser: browser_change_directory() to screen_file.c
[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 "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)
42 {
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;
49         }
50 }
52 /* change the highlight flag for a song */
53 static void
54 set_highlight(mpdclient_filelist_t *fl, mpd_Song *song, int highlight)
55 {
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;
67 }
69 /* sync highlight flags with playlist */
70 void
71 sync_highlights(mpdclient_t *c, mpdclient_filelist_t *fl)
72 {
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;
86                 }
87         }
88 }
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)
94 {
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;
113         }
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 (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                 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;
162         }
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
201         }
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 struct mpd_InfoEntity *
221 browser_get_selected_entity(const struct screen_browser *browser)
223         struct filelist_entry *entry = browser_get_selected_entry(browser);
225         return entry != NULL
226                 ? entry->entity
227                 : NULL;
230 static struct mpd_song *
231 browser_get_selected_song(const struct screen_browser *browser)
233         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);
288                 }
290                 return true;
291         }
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
301         {
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);
313                 }
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
325         }
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);
342                 }
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);
351         }
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);
366                 }
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);
375         }
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);
391         }
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);
421         }
423         return 1;
425 #endif
427 bool
428 browser_cmd(struct screen_browser *browser,
429             struct mpdclient *c, command_t cmd)
431         struct mpd_song *song;
433         switch (cmd) {
434         case CMD_PLAY:
435                 browser_handle_enter(browser, c);
436                 return true;
438         case CMD_SELECT:
439                 if (browser_handle_select(browser, c))
440                         /* continue and select next item... */
441                         cmd = CMD_LIST_NEXT;
443                 /* call list_window_cmd to go to the next item */
444                 break;
446         case CMD_ADD:
447                 if (browser_handle_add(browser, c))
448                         /* continue and select next item... */
449                         cmd = CMD_LIST_NEXT;
451                 /* call list_window_cmd to go to the next item */
452                 break;
454         case CMD_SELECT_ALL:
455                 browser_handle_select_all(browser, c);
456                 return true;
458         case CMD_LIST_FIND:
459         case CMD_LIST_RFIND:
460         case CMD_LIST_FIND_NEXT:
461         case CMD_LIST_RFIND_NEXT:
462                 screen_find(browser->lw, filelist_length(browser->filelist),
463                             cmd, browser_lw_callback,
464                             browser->filelist);
465                 return true;
466         case CMD_LIST_JUMP:
467                 screen_jump(browser->lw, browser_lw_callback, browser->filelist);
468                 return true;
470 #ifdef HAVE_GETMOUSE
471         case CMD_MOUSE_EVENT:
472                 browser_handle_mouse_event(browser, c);
473                 return true;
474 #endif
476 #ifdef ENABLE_SONG_SCREEN
477         case CMD_SCREEN_SONG:
478                 song = browser_get_selected_song(browser);
479                 if (song == NULL)
480                         return false;
482                 screen_song_switch(c, song);
483                 return true;
484 #endif
486         case CMD_LOCATE:
487                 song = browser_get_selected_song(browser);
488                 if (song == NULL)
489                         return false;
491                 screen_file_goto_song(c, song);
492                 return true;
494 #ifdef ENABLE_LYRICS_SCREEN
495         case CMD_SCREEN_LYRICS:
496                 song = browser_get_selected_song(browser);
497                 if (song == NULL)
498                         return false;
500                 screen_lyrics_switch(c, song, false);
501                 return true;
502 #endif
503         case CMD_SCREEN_SWAP:
504                 screen_swap(c, browser_get_selected_song(browser));
505                 return true;
507         default:
508                 break;
509         }
511         if (list_window_cmd(browser->lw, filelist_length(browser->filelist),
512                             cmd))
513                 return true;
515         return false;