Code

let mpd add directories (just send the path)
[ncmpc.git] / src / screen_file.c
1 /* 
2  * $Id$
3  *
4  * (c) 2004 by Kalle Wallin <kaw@linux.se>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  *
19  */
21 #include <ctype.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <glib.h>
25 #include <ncurses.h>
27 #include "config.h"
28 #include "ncmpc.h"
29 #include "options.h"
30 #include "support.h"
31 #include "mpdclient.h"
32 #include "strfsong.h"
33 #include "command.h"
34 #include "screen.h"
35 #include "screen_utils.h"
38 #define USE_OLD_LAYOUT
39 #undef  USE_OLD_ADD
41 #define BUFSIZE 1024
43 #define HIGHLIGHT  (0x01)
46 static list_window_t *lw = NULL;
47 static GList *lw_state_list = NULL;
48 static mpdclient_filelist_t *filelist = NULL;
52 /* clear the highlight flag for all items in the filelist */
53 static void
54 clear_highlights(mpdclient_filelist_t *filelist)
55 {
56   GList *list = g_list_first(filelist->list);
57   
58   while( list )
59     {
60       filelist_entry_t *entry = list->data;
62       entry->flags &= ~HIGHLIGHT;
63       list = list->next;
64     }
65 }
67 /* change the highlight flag for a song */
68 static void
69 set_highlight(mpdclient_filelist_t *filelist, mpd_Song *song, int highlight)
70 {
71   GList *list = g_list_first(filelist->list);
73   if( !song )
74     return;
76   while( list )
77     {
78       filelist_entry_t *entry = list->data;
79       mpd_InfoEntity *entity  = entry->entity;
81       if( entity && entity->type==MPD_INFO_ENTITY_TYPE_SONG )
82         {
83           mpd_Song *song2 = entity->info.song;
85           if( strcmp(song->file, song2->file) == 0 )
86             {
87               if(highlight)
88                 entry->flags |= HIGHLIGHT;
89               else
90                 entry->flags &= ~HIGHLIGHT;
91             }
92         }
93       list = list->next;
94     }
95 }
97 /* sync highlight flags with playlist */
98 static void
99 sync_highlights(mpdclient_t *c, mpdclient_filelist_t *filelist)
101   GList *list = g_list_first(filelist->list);
103   while(list)
104     {
105       filelist_entry_t *entry = list->data;
106       mpd_InfoEntity *entity = entry->entity;
108       if( entity && entity->type==MPD_INFO_ENTITY_TYPE_SONG )
109         {
110           mpd_Song *song = entity->info.song;
111           
112           if( playlist_get_index_from_file(c, song->file) >= 0 )
113             entry->flags |= HIGHLIGHT;
114           else
115             entry->flags &= ~HIGHLIGHT;
116         }
117       list=list->next;
118     }
121 /* the db have changed -> update the filelist */
122 static void 
123 file_changed_callback(mpdclient_t *c, int event, gpointer data)
125   D("screen_file.c> filelist_callback() [%d]\n", event);
126   filelist = mpdclient_filelist_update(c, filelist);
127   sync_highlights(c, filelist);
128   list_window_check_selected(lw, filelist->length);
131 /* the playlist have been updated -> fix highlights */
132 static void 
133 playlist_changed_callback(mpdclient_t *c, int event, gpointer data)
135   D("screen_file.c> playlist_callback() [%d]\n", event);
136   switch(event)
137     {
138     case PLAYLIST_EVENT_CLEAR:
139       clear_highlights(filelist);
140       break;
141     case PLAYLIST_EVENT_ADD:
142       set_highlight(filelist, (mpd_Song *) data, 1); 
143       break;
144     case PLAYLIST_EVENT_DELETE:
145       set_highlight(filelist, (mpd_Song *) data, 0); 
146       break;
147     case PLAYLIST_EVENT_MOVE:
148       break;
149     default:
150       sync_highlights(c, filelist);
151       break;
152     }
155 /* store current state when entering a subdirectory */
156 static void
157 push_lw_state(void)
159   list_window_t *tmp = g_malloc(sizeof(list_window_t));
161   memcpy(tmp, lw, sizeof(list_window_t));
162   lw_state_list = g_list_prepend(lw_state_list, (gpointer) tmp);
165 /* get previous state when leaving a directory */
166 static void
167 pop_lw_state(void)
169   if( lw_state_list )
170     {
171       list_window_t *tmp = lw_state_list->data;
173       memcpy(lw, tmp, sizeof(list_window_t));
174       g_free(tmp);
175       lw_state_list->data = NULL;
176       lw_state_list = g_list_delete_link(lw_state_list, lw_state_list);
177     }
180 /* list_window callback */
181 static char *
182 list_callback(int index, int *highlight, void *data)
184   static char buf[BUFSIZE];
185   //mpdclient_t *c = (mpdclient_t *) data;
186   filelist_entry_t *entry;
187   mpd_InfoEntity *entity;
189   *highlight = 0;
190   if( (entry=(filelist_entry_t *)g_list_nth_data(filelist->list,index))==NULL )
191     return NULL;
193   entity = entry->entity;
194   *highlight = (entry->flags & HIGHLIGHT);
196   if( entity == NULL )
197     {
198       return "[..]";
199     }
200   if( entity->type==MPD_INFO_ENTITY_TYPE_DIRECTORY ) 
201     {
202       mpd_Directory *dir = entity->info.directory;
203       char *dirname = utf8_to_locale(basename(dir->path));
205       snprintf(buf, BUFSIZE, "[%s]", dirname);
206       g_free(dirname);
207       return buf;
208     }
209   else if( entity->type==MPD_INFO_ENTITY_TYPE_SONG )
210     {
211       mpd_Song *song = entity->info.song;
213       strfsong(buf, BUFSIZE, LIST_FORMAT, song);
214       return buf;
215     }
216   else if( entity->type==MPD_INFO_ENTITY_TYPE_PLAYLISTFILE )
217     {
218       mpd_PlaylistFile *plf = entity->info.playlistFile;
219       char *filename = utf8_to_locale(basename(plf->path));
221 #ifdef USE_OLD_LAYOUT      
222       snprintf(buf, BUFSIZE, "*%s*", filename);
223 #else 
224       snprintf(buf, BUFSIZE, "<Playlist> %s", filename);
225 #endif
226       g_free(filename);
227       return buf;
228     }
229   return "Error: Unknow entry!";
232 /* chdir */
233 static int
234 change_directory(screen_t *screen, mpdclient_t *c, filelist_entry_t *entry)
236   mpd_InfoEntity *entity = entry->entity;
237   gchar *path = NULL;
239   if( entity==NULL )
240     {
241       /* return to parent */
242       char *parent = g_path_get_dirname(filelist->path);
243       if( strcmp(parent, ".") == 0 )
244         {
245           parent[0] = '\0';
246         }
247       path = g_strdup(parent);
248       list_window_reset(lw);
249       /* restore previous list window state */
250       pop_lw_state(); 
251     }
252   else
253     if( entity->type==MPD_INFO_ENTITY_TYPE_DIRECTORY)
254       {
255         /* enter sub */
256         mpd_Directory *dir = entity->info.directory;
257         path = utf8_to_locale(dir->path);      
258         /* save current list window state */
259         push_lw_state(); 
260         list_window_reset(lw);
261       }
262     else
263       return -1;
265   filelist = mpdclient_filelist_free(filelist);
266   filelist = mpdclient_filelist_get(c, path);
267   sync_highlights(c, filelist);
268   list_window_check_selected(lw, filelist->length);
269   g_free(path);
270   return 0;
273 static int
274 load_playlist(screen_t *screen, mpdclient_t *c, filelist_entry_t *entry)
276   mpd_InfoEntity *entity = entry->entity;
277   mpd_PlaylistFile *plf = entity->info.playlistFile;
278   char *filename = utf8_to_locale(plf->path);
280   if( mpdclient_cmd_load_playlist(c, plf->path) == 0 )
281     screen_status_printf(_("Loading playlist %s..."), basename(filename));
282   g_free(filename);
283   return 0;
286 static int 
287 handle_delete(screen_t *screen, mpdclient_t *c)
289   filelist_entry_t *entry;
290   mpd_InfoEntity *entity;
291   mpd_PlaylistFile *plf;
292   char *str, buf[BUFSIZE];
293   int key;
295   entry=( filelist_entry_t *) g_list_nth_data(filelist->list,lw->selected);
296   if( entry==NULL || entry->entity==NULL )
297     return -1;
299   entity = entry->entity;
301   if( entity->type!=MPD_INFO_ENTITY_TYPE_PLAYLISTFILE )
302     {
303       screen_status_printf(_("You can only delete playlists!"));
304       beep();
305       return -1;
306     }
308   plf = entity->info.playlistFile;
309   str = utf8_to_locale(basename(plf->path));
310   snprintf(buf, BUFSIZE, _("Delete playlist %s [%s/%s] ? "), str, YES, NO);
311   g_free(str);  
312   key = tolower(screen_getch(screen->status_window.w, buf));
313   if( key==KEY_RESIZE )
314     screen_resize();
315   if( key != YES[0] )
316     {
317       screen_status_printf(_("Aborted!"));
318       return 0;
319     }
321   if( mpdclient_cmd_delete_playlist(c, plf->path) )
322     {
323       return -1;
324     }
325   screen_status_printf(_("Playlist deleted!"));
326   return 0;
330 static int
331 handle_enter(screen_t *screen, mpdclient_t *c)
333   filelist_entry_t *entry;
334   mpd_InfoEntity *entity;
335   
336   entry = ( filelist_entry_t *) g_list_nth_data(filelist->list, lw->selected);
337   if( entry==NULL )
338     return -1;
340   entity = entry->entity;
341   if( entity==NULL || entity->type==MPD_INFO_ENTITY_TYPE_DIRECTORY )
342     return change_directory(screen, c, entry);
343   else if( entity->type==MPD_INFO_ENTITY_TYPE_PLAYLISTFILE )
344     return load_playlist(screen, c, entry);
345   return -1;
349 #ifdef USE_OLD_ADD
350 /* NOTE - The add_directory functions should move to mpdclient.c */
351 extern gint mpdclient_finish_command(mpdclient_t *c);
353 static int
354 add_directory(mpdclient_t *c, char *dir)
356   mpd_InfoEntity *entity;
357   GList *subdir_list = NULL;
358   GList *list = NULL;
359   char *dirname;
361   dirname = utf8_to_locale(dir);
362   screen_status_printf(_("Adding directory %s...\n"), dirname);
363   doupdate(); 
364   g_free(dirname);
365   dirname = NULL;
367   mpd_sendLsInfoCommand(c->connection, dir);
368   mpd_sendCommandListBegin(c->connection);
369   while( (entity=mpd_getNextInfoEntity(c->connection)) )
370     {
371       if( entity->type==MPD_INFO_ENTITY_TYPE_SONG )
372         {
373           mpd_Song *song = entity->info.song;
374           mpd_sendAddCommand(c->connection, song->file);
375           mpd_freeInfoEntity(entity);
376         }
377       else if( entity->type==MPD_INFO_ENTITY_TYPE_DIRECTORY )
378         {
379           subdir_list = g_list_append(subdir_list, (gpointer) entity); 
380         }
381       else
382         mpd_freeInfoEntity(entity);
383     }
384   mpd_sendCommandListEnd(c->connection);
385   mpdclient_finish_command(c);
386   c->need_update = TRUE;
387   
388   list = g_list_first(subdir_list);
389   while( list!=NULL )
390     {
391       mpd_Directory *dir;
393       entity = list->data;
394       dir = entity->info.directory;
395       add_directory(c, dir->path);
396       mpd_freeInfoEntity(entity);
397       list->data=NULL;
398       list=list->next;
399     }
400   g_list_free(subdir_list);
401   return 0;
403 #endif
405 static int
406 handle_select(screen_t *screen, mpdclient_t *c)
408   filelist_entry_t *entry;
410   entry=( filelist_entry_t *) g_list_nth_data(filelist->list, lw->selected);
411   if( entry==NULL || entry->entity==NULL)
412     return -1;
414   if( entry->entity->type==MPD_INFO_ENTITY_TYPE_PLAYLISTFILE )
415     return load_playlist(screen, c, entry);
417   if( entry->entity->type==MPD_INFO_ENTITY_TYPE_DIRECTORY )
418     {
419       mpd_Directory *dir = entry->entity->info.directory;
420 #ifdef USE_OLD_ADD
421       add_directory(c, dir->path);
422 #else
423       if( mpdclient_cmd_add_path(c, dir->path) == 0 )
424         {
425           char *tmp = utf8_to_locale(dir->path);
427           screen_status_printf(_("Adding \'%s\' to playlist\n"), tmp);
428           g_free(tmp);
429         }
430 #endif
431       return 0;
432     }
434   if( entry->entity->type!=MPD_INFO_ENTITY_TYPE_SONG )
435     return -1; 
437   if( entry->flags & HIGHLIGHT )
438     entry->flags &= ~HIGHLIGHT;
439   else
440     entry->flags |= HIGHLIGHT;
442   if( entry->flags & HIGHLIGHT )
443     {
444       if( entry->entity->type==MPD_INFO_ENTITY_TYPE_SONG )
445         {
446           mpd_Song *song = entry->entity->info.song;
448           if( mpdclient_cmd_add(c, song) == 0 )
449             {
450               char buf[BUFSIZE];
451               
452               strfsong(buf, BUFSIZE, LIST_FORMAT, song);
453               screen_status_printf(_("Adding \'%s\' to playlist\n"), buf);
454             }
455         }
456     }
457   else
458     {
459       /* remove song from playlist */
460       if( entry->entity->type==MPD_INFO_ENTITY_TYPE_SONG )
461         {
462           mpd_Song *song = entry->entity->info.song;
464           if( song )
465             {
466               int index = playlist_get_index_from_file(c, song->file);
467               
468               while( (index=playlist_get_index_from_file(c, song->file))>=0 )
469                 mpdclient_cmd_delete(c, index);
470             }
471         }
472     }
473   return 0;
476 static void
477 browse_init(WINDOW *w, int cols, int rows)
479   lw = list_window_init(w, cols, rows);
482 static void
483 browse_resize(int cols, int rows)
485   lw->cols = cols;
486   lw->rows = rows;
489 static void
490 browse_exit(void)
492   if( lw_state_list )
493     {
494       GList *list = lw_state_list;
495       while( list )
496         {
497           g_free(list->data);
498           list->data = NULL;
499           list = list->next;
500         }
501       g_list_free(lw_state_list);
502       lw_state_list = NULL;
504     }
505   if( filelist )
506     filelist = mpdclient_filelist_free(filelist);
507   list_window_free(lw);
510 static void 
511 browse_open(screen_t *screen, mpdclient_t *c)
513   if( filelist == NULL )
514     {
515       filelist = mpdclient_filelist_get(c, "");
516       mpdclient_install_playlist_callback(c, playlist_changed_callback);
517       mpdclient_install_browse_callback(c, file_changed_callback);
518     }
521 static void
522 browse_close(void)
526 static char *
527 browse_title(char *str, size_t size)
529   snprintf(str, size, _("Browse: %s"), basename(filelist->path));
530   return str;
533 static void 
534 browse_paint(screen_t *screen, mpdclient_t *c)
536   lw->clear = 1;
537   
538   list_window_paint(lw, list_callback, (void *) c);
539   wnoutrefresh(lw->w);
542 static void 
543 browse_update(screen_t *screen, mpdclient_t *c)
545   if( filelist->updated )
546     {
547       browse_paint(screen, c);
548       filelist->updated = FALSE;
549       return;
550     }
551   list_window_paint(lw, list_callback, (void *) c);
552   wnoutrefresh(lw->w);
556 static int 
557 browse_cmd(screen_t *screen, mpdclient_t *c, command_t cmd)
559   switch(cmd)
560     {
561     case CMD_PLAY:
562       handle_enter(screen, c);
563       return 1;
564     case CMD_SELECT:
565       if( handle_select(screen, c) == 0 )
566         {
567           /* continue and select next item... */
568           cmd = CMD_LIST_NEXT;
569         }
570       break;
571     case CMD_DELETE:
572       handle_delete(screen, c);
573       break;
574     case CMD_SCREEN_UPDATE:
575       filelist = mpdclient_filelist_update(c, filelist);
576       list_window_check_selected(lw, filelist->length);
577       screen_status_printf(_("Screen updated!"));
578       return 1;
579     case CMD_DB_UPDATE:
580       if( !c->status->updatingDb )
581         {
582           if( mpdclient_cmd_db_update(c,filelist->path)==0 )
583             {
584               screen_status_printf(_("Database update of %s started!"),
585                                    filelist->path);
586               /* set updatingDb to make shure the browse callback gets called
587                * even if the updated has finished before status is updated */
588               c->status->updatingDb = 1; 
589             }
590         }
591       else
592         screen_status_printf(_("Database update running..."));
593       return 1;
594     case CMD_LIST_FIND:
595     case CMD_LIST_RFIND:
596     case CMD_LIST_FIND_NEXT:
597     case CMD_LIST_RFIND_NEXT:
598       return screen_find(screen, c, 
599                          lw, filelist->length,
600                          cmd, list_callback);
601     default:
602       break;
603     }
604   return list_window_cmd(lw, filelist->length, cmd);
608 list_window_t *
609 get_filelist_window()
611   return lw;
617 screen_functions_t *
618 get_screen_browse(void)
620   static screen_functions_t functions;
622   memset(&functions, 0, sizeof(screen_functions_t));
623   functions.init   = browse_init;
624   functions.exit   = browse_exit;
625   functions.open   = browse_open;
626   functions.close  = browse_close;
627   functions.resize = browse_resize;
628   functions.paint  = browse_paint;
629   functions.update = browse_update;
630   functions.cmd    = browse_cmd;
631   functions.get_lw = get_filelist_window;
632   functions.get_title = browse_title;
634   return &functions;