Code

screen_lyrics: add screen_lyrics_switch()
[ncmpc.git] / src / screen_browser.c
1 /*
2  * (c) 2004 by Kalle Wallin <kaw@linux.se>
3  * Copyright (C) 2008 Max Kellermann <max@duempel.org>
4  *
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.
9  *
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.
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  *
18  */
20 #include "screen_browser.h"
21 #include "ncmpc.h"
22 #include "options.h"
23 #include "support.h"
24 #include "strfsong.h"
25 #include "screen_utils.h"
26 #include "gcc.h"
28 #include <string.h>
30 #define USE_OLD_LAYOUT
31 #undef  USE_OLD_ADD
33 #define BUFSIZE 1024
35 #define HIGHLIGHT  (0x01)
37 /* clear the highlight flag for all items in the filelist */
38 static void
39 clear_highlights(mpdclient_filelist_t *fl)
40 {
41         guint i;
43         for (i = 0; i < filelist_length(fl); ++i) {
44                 struct filelist_entry *entry = filelist_get(fl, i);
46                 entry->flags &= ~HIGHLIGHT;
47         }
48 }
50 /* change the highlight flag for a song */
51 static void
52 set_highlight(mpdclient_filelist_t *fl, mpd_Song *song, int highlight)
53 {
54         struct filelist_entry *entry = filelist_find_song(fl, song);
55         mpd_InfoEntity *entity;
57         if (entry == NULL)
58                 return;
60         entity = entry->entity;
61         if (highlight)
62                 entry->flags |= HIGHLIGHT;
63         else
64                 entry->flags &= ~HIGHLIGHT;
65 }
67 /* sync highlight flags with playlist */
68 void
69 sync_highlights(mpdclient_t *c, mpdclient_filelist_t *fl)
70 {
71         guint i;
73         for (i = 0; i < filelist_length(fl); ++i) {
74                 struct filelist_entry *entry = filelist_get(fl, i);
75                 mpd_InfoEntity *entity = entry->entity;
77                 if ( entity && entity->type==MPD_INFO_ENTITY_TYPE_SONG ) {
78                         mpd_Song *song = entity->info.song;
80                         if( playlist_get_index_from_file(c, song->file) >= 0 )
81                                 entry->flags |= HIGHLIGHT;
82                         else
83                                 entry->flags &= ~HIGHLIGHT;
84                 }
85         }
86 }
88 /* the playlist have been updated -> fix highlights */
89 void
90 browser_playlist_changed(struct screen_browser *browser, mpdclient_t *c,
91                          int event, gpointer data)
92 {
93         if (browser->filelist == NULL)
94                 return;
96         D("screen_file.c> playlist_callback() [%d]\n", event);
97         switch(event) {
98         case PLAYLIST_EVENT_CLEAR:
99                 clear_highlights(browser->filelist);
100                 break;
101         case PLAYLIST_EVENT_ADD:
102                 set_highlight(browser->filelist, (mpd_Song *) data, 1);
103                 break;
104         case PLAYLIST_EVENT_DELETE:
105                 set_highlight(browser->filelist, (mpd_Song *) data, 0);
106                 break;
107         case PLAYLIST_EVENT_MOVE:
108                 break;
109         default:
110                 sync_highlights(c, browser->filelist);
111                 break;
112         }
115 /* list_window callback */
116 const char *
117 browser_lw_callback(unsigned idx, int *highlight, void *data)
119         static char buf[BUFSIZE];
120         mpdclient_filelist_t *fl = (mpdclient_filelist_t *) data;
121         filelist_entry_t *entry;
122         mpd_InfoEntity *entity;
124         if (idx >= filelist_length(fl))
125                 return NULL;
127         entry = filelist_get(fl, idx);
128         assert(entry != NULL);
130         entity = entry->entity;
131         *highlight = (entry->flags & HIGHLIGHT);
133         if( entity == NULL )
134                 return "[..]";
136         if( entity->type==MPD_INFO_ENTITY_TYPE_DIRECTORY ) {
137                 mpd_Directory *dir = entity->info.directory;
138                 char *directory = utf8_to_locale(basename(dir->path));
140                 g_snprintf(buf, BUFSIZE, "[%s]", directory);
141                 g_free(directory);
142                 return buf;
143         } else if( entity->type==MPD_INFO_ENTITY_TYPE_SONG ) {
144                 mpd_Song *song = entity->info.song;
146                 strfsong(buf, BUFSIZE, LIST_FORMAT, song);
147                 return buf;
148         } else if( entity->type==MPD_INFO_ENTITY_TYPE_PLAYLISTFILE ) {
149                 mpd_PlaylistFile *plf = entity->info.playlistFile;
150                 char *filename = utf8_to_locale(basename(plf->path));
152 #ifdef USE_OLD_LAYOUT
153                 g_snprintf(buf, BUFSIZE, "*%s*", filename);
154 #else
155                 g_snprintf(buf, BUFSIZE, "<Playlist> %s", filename);
156 #endif
157                 g_free(filename);
158                 return buf;
159         }
161         return "Error: Unknown entry!";
164 /* chdir */
165 int
166 browser_change_directory(struct screen_browser *browser, mpdclient_t *c,
167                          filelist_entry_t *entry, const char *new_path)
169         mpd_InfoEntity *entity = NULL;
170         gchar *path = NULL;
172         if( entry!=NULL )
173                 entity = entry->entity;
174         else if( new_path==NULL )
175                 return -1;
177         if( entity==NULL ) {
178                 if( entry || 0==strcmp(new_path, "..") ) {
179                         /* return to parent */
180                         char *parent = g_path_get_dirname(browser->filelist->path);
181                         if( strcmp(parent, ".") == 0 )
182                                 parent[0] = '\0';
183                         path = g_strdup(parent);
184                         list_window_reset(browser->lw);
185                         /* restore previous list window state */
186                         list_window_pop_state(browser->lw_state, browser->lw);
187                 } else {
188                         /* entry==NULL, then new_path ("" is root) */
189                         path = g_strdup(new_path);
190                         list_window_reset(browser->lw);
191                         /* restore first list window state (pop while returning true) */
192                         while(list_window_pop_state(browser->lw_state, browser->lw));
193                 }
194         } else if( entity->type==MPD_INFO_ENTITY_TYPE_DIRECTORY) {
195                 /* enter sub */
196                 mpd_Directory *dir = entity->info.directory;
197                 path = utf8_to_locale(dir->path);
198                 /* save current list window state */
199                 list_window_push_state(browser->lw_state, browser->lw);
200         } else
201                 return -1;
203         filelist_free(browser->filelist);
204         browser->filelist = mpdclient_filelist_get(c, path);
205         sync_highlights(c, browser->filelist);
206         list_window_check_selected(browser->lw, filelist_length(browser->filelist));
207         g_free(path);
208         return 0;
211 static int
212 load_playlist(mpdclient_t *c, filelist_entry_t *entry)
214         mpd_InfoEntity *entity = entry->entity;
215         mpd_PlaylistFile *plf = entity->info.playlistFile;
216         char *filename = utf8_to_locale(plf->path);
218         if (mpdclient_cmd_load_playlist_utf8(c, plf->path) == 0)
219                 screen_status_printf(_("Loading playlist %s..."), basename(filename));
220         g_free(filename);
221         return 0;
224 static int
225 enqueue_and_play(mpdclient_t *c, filelist_entry_t *entry)
227         int idx;
228         mpd_InfoEntity *entity = entry->entity;
229         mpd_Song *song = entity->info.song;
231         if (!(entry->flags & HIGHLIGHT)) {
232                 if (mpdclient_cmd_add(c, song) == 0) {
233                         char buf[BUFSIZE];
235                         entry->flags |= HIGHLIGHT;
236                         strfsong(buf, BUFSIZE, LIST_FORMAT, song);
237                         screen_status_printf(_("Adding \'%s\' to playlist\n"), buf);
238                         mpdclient_update(c); /* get song id */
239                 } else
240                         return -1;
241         }
243         idx = playlist_get_index_from_file(c, song->file);
244         mpdclient_cmd_play(c, idx);
245         return 0;
248 static struct filelist_entry *
249 browser_get_selected(const struct screen_browser *browser)
251         if (browser->filelist == NULL ||
252             browser->lw->selected >= filelist_length(browser->filelist))
253                 return NULL;
255         return filelist_get(browser->filelist, browser->lw->selected);
258 int
259 browser_handle_enter(struct screen_browser *browser, mpdclient_t *c)
261         struct filelist_entry *entry = browser_get_selected(browser);
262         mpd_InfoEntity *entity;
264         if( entry==NULL )
265                 return -1;
267         entity = entry->entity;
268         if (entity == NULL || entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY)
269                 return browser_change_directory(browser, c, entry, NULL);
270         else if (entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE)
271                 return load_playlist(c, entry);
272         else if (entity->type == MPD_INFO_ENTITY_TYPE_SONG)
273                 return enqueue_and_play(c, entry);
274         return -1;
278 #ifdef USE_OLD_ADD
279 /* NOTE - The add_directory functions should move to mpdclient.c */
280 extern gint mpdclient_finish_command(mpdclient_t *c);
282 static int
283 add_directory(mpdclient_t *c, char *dir)
285         mpd_InfoEntity *entity;
286         GList *subdir_list = NULL;
287         GList *list = NULL;
288         char *dirname;
290         dirname = utf8_to_locale(dir);
291         screen_status_printf(_("Adding directory %s...\n"), dirname);
292         doupdate();
293         g_free(dirname);
294         dirname = NULL;
296         mpd_sendLsInfoCommand(c->connection, dir);
297         mpd_sendCommandListBegin(c->connection);
298         while( (entity=mpd_getNextInfoEntity(c->connection)) ) {
299                 if( entity->type==MPD_INFO_ENTITY_TYPE_SONG ) {
300                         mpd_Song *song = entity->info.song;
301                         mpd_sendAddCommand(c->connection, song->file);
302                         mpd_freeInfoEntity(entity);
303                 } else if( entity->type==MPD_INFO_ENTITY_TYPE_DIRECTORY ) {
304                         subdir_list = g_list_append(subdir_list, (gpointer) entity);
305                 } else
306                         mpd_freeInfoEntity(entity);
307         }
308         mpd_sendCommandListEnd(c->connection);
309         mpdclient_finish_command(c);
310         c->need_update = TRUE;
312         list = g_list_first(subdir_list);
313         while( list!=NULL ) {
314                 mpd_Directory *dir;
316                 entity = list->data;
317                 dir = entity->info.directory;
318                 add_directory(c, dir->path);
319                 mpd_freeInfoEntity(entity);
320                 list->data=NULL;
321                 list=list->next;
322         }
323         g_list_free(subdir_list);
324         return 0;
326 #endif
328 static int
329 browser_select_entry(mpdclient_t *c, filelist_entry_t *entry,
330                      gboolean toggle)
332         assert(entry != NULL);
333         assert(entry->entity != NULL);
335         if (entry->entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE)
336                 return load_playlist(c, entry);
338         if (entry->entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
339                 mpd_Directory *dir = entry->entity->info.directory;
340 #ifdef USE_OLD_ADD
341                 add_directory(c, tmp);
342 #else
343                 if (mpdclient_cmd_add_path_utf8(c, dir->path) == 0) {
344                         char *tmp = utf8_to_locale(dir->path);
346                         screen_status_printf(_("Adding \'%s\' to playlist\n"), tmp);
347                         g_free(tmp);
348                 }
349 #endif
350                 return 0;
351         }
353         if (entry->entity->type != MPD_INFO_ENTITY_TYPE_SONG)
354                 return -1;
356         assert(entry->entity->info.song != NULL);
358         if (!toggle || (entry->flags & HIGHLIGHT) == 0) {
359                 mpd_Song *song = entry->entity->info.song;
361                 entry->flags |= HIGHLIGHT;
363                 if (mpdclient_cmd_add(c, song) == 0) {
364                         char buf[BUFSIZE];
366                         strfsong(buf, BUFSIZE, LIST_FORMAT, song);
367                         screen_status_printf(_("Adding \'%s\' to playlist\n"), buf);
368                 }
369         } else {
370                 /* remove song from playlist */
371                 mpd_Song *song = entry->entity->info.song;
372                 int idx;
374                 entry->flags &= ~HIGHLIGHT;
376                 while ((idx = playlist_get_index_from_file(c, song->file)) >=0)
377                         mpdclient_cmd_delete(c, idx);
378         }
380         return 0;
383 int
384 browser_handle_select(struct screen_browser *browser, mpdclient_t *c)
386         struct filelist_entry *entry = browser_get_selected(browser);
388         if (entry == NULL || entry->entity == NULL)
389                 return -1;
391         return browser_select_entry(c, entry, TRUE);
394 int
395 browser_handle_add(struct screen_browser *browser, mpdclient_t *c)
397         struct filelist_entry *entry = browser_get_selected(browser);
399         if (entry == NULL || entry->entity == NULL)
400                 return -1;
402         return browser_select_entry(c, entry, FALSE);
405 void
406 browser_handle_select_all(struct screen_browser *browser, mpdclient_t *c)
408         guint i;
410         if (browser->filelist == NULL)
411                 return;
413         for (i = 0; i < filelist_length(browser->filelist); ++i) {
414                 struct filelist_entry *entry = filelist_get(browser->filelist, i);
416                 if (entry != NULL && entry->entity != NULL)
417                         browser_select_entry(c, entry, FALSE);
418         }
421 #ifdef HAVE_GETMOUSE
422 int
423 browser_handle_mouse_event(struct screen_browser *browser, mpdclient_t *c)
425         int row;
426         unsigned prev_selected = browser->lw->selected;
427         unsigned long bstate;
428         int length;
430         if (browser->filelist)
431                 length = filelist_length(browser->filelist);
432         else
433                 length = 0;
435         if (screen_get_mouse_event(c, &bstate, &row) ||
436             list_window_mouse(browser->lw, length, bstate, row))
437                 return 1;
439         browser->lw->selected = browser->lw->start + row;
440         list_window_check_selected(browser->lw, length);
442         if( bstate & BUTTON1_CLICKED ) {
443                 if (prev_selected == browser->lw->selected)
444                         browser_handle_enter(browser, c);
445         } else if (bstate & BUTTON3_CLICKED) {
446                 if (prev_selected == browser->lw->selected)
447                         browser_handle_select(browser, c);
448         }
450         return 1;
452 #endif
454 bool
455 browser_cmd(struct screen_browser *browser, struct screen *screen,
456             struct mpdclient *c, command_t cmd)
458         struct filelist_entry *entry;
460         switch (cmd) {
461         case CMD_PLAY:
462                 browser_handle_enter(browser, c);
463                 return true;
465         case CMD_SELECT:
466                 if (browser_handle_select(browser, c) == 0)
467                         /* continue and select next item... */
468                         cmd = CMD_LIST_NEXT;
470                 /* call list_window_cmd to go to the next item */
471                 break;
473         case CMD_ADD:
474                 if (browser_handle_add(browser, c) == 0)
475                         /* continue and select next item... */
476                         cmd = CMD_LIST_NEXT;
478                 /* call list_window_cmd to go to the next item */
479                 break;
481         case CMD_SELECT_ALL:
482                 browser_handle_select_all(browser, c);
483                 return true;
485         case CMD_LIST_FIND:
486         case CMD_LIST_RFIND:
487         case CMD_LIST_FIND_NEXT:
488         case CMD_LIST_RFIND_NEXT:
489                 screen_find(screen,
490                             browser->lw, filelist_length(browser->filelist),
491                             cmd, browser_lw_callback,
492                             browser->filelist);
493                 return true;
495         case CMD_MOUSE_EVENT:
496                 browser_handle_mouse_event(browser, c);
497                 return true;
499 #ifdef ENABLE_LYRICS_SCREEN
500         case CMD_SCREEN_LYRICS:
501                 entry = browser_get_selected(browser);
502                 if (entry == NULL)
503                         return false;
505                 if (entry->entity == NULL ||
506                     entry->entity->type != MPD_INFO_ENTITY_TYPE_SONG)
507                         return true;
509                 screen_lyrics_switch(c, entry->entity->info.song);
510                 return true;
511 #endif
513         default:
514                 break;
515         }
517         if (list_window_cmd(browser->lw, filelist_length(browser->filelist),
518                             cmd))
519                 return true;
521         return false;