Code

2caf2519723a6953185a60810a5d553af2d17d60
[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
40 #define BUFSIZE 1024
42 #define HIGHLIGHT  (0x01)
45 static list_window_t *lw = NULL;
46 static GList *lw_state_list = NULL;
47 static mpdclient_filelist_t *filelist = NULL;
51 /* clear the highlight flag for all items in the filelist */
52 static void
53 clear_highlights(mpdclient_filelist_t *filelist)
54 {
55   GList *list = g_list_first(filelist->list);
56   
57   while( list )
58     {
59       filelist_entry_t *entry = list->data;
61       entry->flags &= ~HIGHLIGHT;
62       list = list->next;
63     }
64 }
66 /* change the highlight flag for a song */
67 static void
68 set_highlight(mpdclient_filelist_t *filelist, mpd_Song *song, int highlight)
69 {
70   GList *list = g_list_first(filelist->list);
72   if( !song )
73     return;
75   while( list )
76     {
77       filelist_entry_t *entry = list->data;
78       mpd_InfoEntity *entity  = entry->entity;
80       if( entity && entity->type==MPD_INFO_ENTITY_TYPE_SONG )
81         {
82           mpd_Song *song2 = entity->info.song;
84           if( strcmp(song->file, song2->file) == 0 )
85             {
86               if(highlight)
87                 entry->flags |= HIGHLIGHT;
88               else
89                 entry->flags &= ~HIGHLIGHT;
90             }
91         }
92       list = list->next;
93     }
94 }
96 /* sync highlight flags with playlist */
97 static void
98 sync_highlights(mpdclient_t *c, mpdclient_filelist_t *filelist)
99 {
100   GList *list = g_list_first(filelist->list);
102   while(list)
103     {
104       filelist_entry_t *entry = list->data;
105       mpd_InfoEntity *entity = entry->entity;
107       if( entity && entity->type==MPD_INFO_ENTITY_TYPE_SONG )
108         {
109           mpd_Song *song = entity->info.song;
110           
111           if( playlist_get_index_from_file(c, song->file) >= 0 )
112             entry->flags |= HIGHLIGHT;
113           else
114             entry->flags &= ~HIGHLIGHT;
115         }
116       list=list->next;
117     }
120 /* the db have changed -> update the filelist */
121 static void 
122 file_changed_callback(mpdclient_t *c, int event, gpointer data)
124   D("screen_file.c> filelist_callback() [%d]\n", event);
125   filelist = mpdclient_filelist_update(c, filelist);
126   sync_highlights(c, filelist);
127   list_window_check_selected(lw, filelist->length);
130 /* the playlist have been updated -> fix highlights */
131 static void 
132 playlist_changed_callback(mpdclient_t *c, int event, gpointer data)
134   D("screen_file.c> playlist_callback() [%d]\n", event);
135   switch(event)
136     {
137     case PLAYLIST_EVENT_CLEAR:
138       clear_highlights(filelist);
139       break;
140     case PLAYLIST_EVENT_ADD:
141       set_highlight(filelist, (mpd_Song *) data, 1); 
142       break;
143     case PLAYLIST_EVENT_DELETE:
144       set_highlight(filelist, (mpd_Song *) data, 0); 
145       break;
146     case PLAYLIST_EVENT_MOVE:
147       break;
148     default:
149       sync_highlights(c, filelist);
150       break;
151     }
154 /* store current state when entering a subdirectory */
155 static void
156 push_lw_state(void)
158   list_window_t *tmp = g_malloc(sizeof(list_window_t));
160   memcpy(tmp, lw, sizeof(list_window_t));
161   lw_state_list = g_list_prepend(lw_state_list, (gpointer) tmp);
164 /* get previous state when leaving a directory */
165 static void
166 pop_lw_state(void)
168   if( lw_state_list )
169     {
170       list_window_t *tmp = lw_state_list->data;
172       memcpy(lw, tmp, sizeof(list_window_t));
173       g_free(tmp);
174       lw_state_list->data = NULL;
175       lw_state_list = g_list_delete_link(lw_state_list, lw_state_list);
176     }
179 /* list_window callback */
180 static char *
181 list_callback(int index, int *highlight, void *data)
183   static char buf[BUFSIZE];
184   //mpdclient_t *c = (mpdclient_t *) data;
185   filelist_entry_t *entry;
186   mpd_InfoEntity *entity;
188   *highlight = 0;
189   if( (entry=(filelist_entry_t *)g_list_nth_data(filelist->list,index))==NULL )
190     return NULL;
192   entity = entry->entity;
193   *highlight = (entry->flags & HIGHLIGHT);
195   if( entity == NULL )
196     {
197       return "[..]";
198     }
199   if( entity->type==MPD_INFO_ENTITY_TYPE_DIRECTORY ) 
200     {
201       mpd_Directory *dir = entity->info.directory;
202       char *dirname = utf8_to_locale(basename(dir->path));
204       snprintf(buf, BUFSIZE, "[%s]", dirname);
205       g_free(dirname);
206       return buf;
207     }
208   else if( entity->type==MPD_INFO_ENTITY_TYPE_SONG )
209     {
210       mpd_Song *song = entity->info.song;
212       strfsong(buf, BUFSIZE, LIST_FORMAT, song);
213       return buf;
214     }
215   else if( entity->type==MPD_INFO_ENTITY_TYPE_PLAYLISTFILE )
216     {
217       mpd_PlaylistFile *plf = entity->info.playlistFile;
218       char *filename = utf8_to_locale(basename(plf->path));
220 #ifdef USE_OLD_LAYOUT      
221       snprintf(buf, BUFSIZE, "*%s*", filename);
222 #else 
223       snprintf(buf, BUFSIZE, "<Playlist> %s", filename);
224 #endif
225       g_free(filename);
226       return buf;
227     }
228   return "Error: Unknow entry!";
231 /* chdir */
232 static int
233 change_directory(screen_t *screen, mpdclient_t *c, filelist_entry_t *entry)
235   mpd_InfoEntity *entity = entry->entity;
236   gchar *path = NULL;
238   if( entity==NULL )
239     {
240       /* return to parent */
241       char *parent = g_path_get_dirname(filelist->path);
242       if( strcmp(parent, ".") == 0 )
243         {
244           parent[0] = '\0';
245         }
246       path = g_strdup(parent);
247       list_window_reset(lw);
248       /* restore previous list window state */
249       pop_lw_state(); 
250     }
251   else
252     if( entity->type==MPD_INFO_ENTITY_TYPE_DIRECTORY)
253       {
254         /* enter sub */
255         mpd_Directory *dir = entity->info.directory;
256         path = utf8_to_locale(dir->path);      
257         /* save current list window state */
258         push_lw_state(); 
259         list_window_reset(lw);
260       }
261     else
262       return -1;
264   filelist = mpdclient_filelist_free(filelist);
265   filelist = mpdclient_filelist_get(c, path);
266   sync_highlights(c, filelist);
267   list_window_check_selected(lw, filelist->length);
268   g_free(path);
269   return 0;
272 static int
273 load_playlist(screen_t *screen, mpdclient_t *c, filelist_entry_t *entry)
275   mpd_InfoEntity *entity = entry->entity;
276   mpd_PlaylistFile *plf = entity->info.playlistFile;
277   char *filename = utf8_to_locale(plf->path);
279   if( mpdclient_cmd_load_playlist(c, plf->path) == 0 )
280     screen_status_printf(_("Loading playlist %s..."), basename(filename));
281   g_free(filename);
282   return 0;
285 static int 
286 handle_delete(screen_t *screen, mpdclient_t *c)
288   filelist_entry_t *entry;
289   mpd_InfoEntity *entity;
290   mpd_PlaylistFile *plf;
291   char *str, buf[BUFSIZE];
292   int key;
294   entry=( filelist_entry_t *) g_list_nth_data(filelist->list,lw->selected);
295   if( entry==NULL || entry->entity==NULL )
296     return -1;
298   entity = entry->entity;
300   if( entity->type!=MPD_INFO_ENTITY_TYPE_PLAYLISTFILE )
301     {
302       screen_status_printf(_("You can only delete playlists!"));
303       beep();
304       return -1;
305     }
307   plf = entity->info.playlistFile;
308   str = utf8_to_locale(basename(plf->path));
309   snprintf(buf, BUFSIZE, _("Delete playlist %s [%s/%s] ? "), str, YES, NO);
310   g_free(str);  
311   key = tolower(screen_getch(screen->status_window.w, buf));
312   if( key==KEY_RESIZE )
313     screen_resize();
314   if( key != YES[0] )
315     {
316       screen_status_printf(_("Aborted!"));
317       return 0;
318     }
320   if( mpdclient_cmd_delete_playlist(c, plf->path) )
321     {
322       return -1;
323     }
324   screen_status_printf(_("Playlist deleted!"));
325   return 0;
329 static int
330 handle_enter(screen_t *screen, mpdclient_t *c)
332   filelist_entry_t *entry;
333   mpd_InfoEntity *entity;
334   
335   entry = ( filelist_entry_t *) g_list_nth_data(filelist->list, lw->selected);
336   if( entry==NULL )
337     return -1;
339   entity = entry->entity;
340   if( entity==NULL || entity->type==MPD_INFO_ENTITY_TYPE_DIRECTORY )
341     return change_directory(screen, c, entry);
342   else if( entity->type==MPD_INFO_ENTITY_TYPE_PLAYLISTFILE )
343     return load_playlist(screen, c, entry);
344   return -1;
348 /* NOTE - The add_directory functions should move to mpdclient.c */
349 extern gint mpdclient_finish_command(mpdclient_t *c);
351 static int
352 add_directory(mpdclient_t *c, char *dir)
354   mpd_InfoEntity *entity;
355   GList *subdir_list = NULL;
356   GList *list = NULL;
357   char *dirname;
359   dirname = utf8_to_locale(dir);
360   screen_status_printf(_("Adding directory %s...\n"), dirname);
361   doupdate(); 
362   g_free(dirname);
363   dirname = NULL;
365   mpd_sendLsInfoCommand(c->connection, dir);
366   mpd_sendCommandListBegin(c->connection);
367   while( (entity=mpd_getNextInfoEntity(c->connection)) )
368     {
369       if( entity->type==MPD_INFO_ENTITY_TYPE_SONG )
370         {
371           mpd_Song *song = entity->info.song;
372           mpd_sendAddCommand(c->connection, song->file);
373           mpd_freeInfoEntity(entity);
374         }
375       else if( entity->type==MPD_INFO_ENTITY_TYPE_DIRECTORY )
376         {
377           subdir_list = g_list_append(subdir_list, (gpointer) entity); 
378         }
379       else
380         mpd_freeInfoEntity(entity);
381     }
382   mpd_sendCommandListEnd(c->connection);
383   mpdclient_finish_command(c);
384   c->need_update = TRUE;
385   
386   list = g_list_first(subdir_list);
387   while( list!=NULL )
388     {
389       mpd_Directory *dir;
391       entity = list->data;
392       dir = entity->info.directory;
393       add_directory(c, dir->path);
394       mpd_freeInfoEntity(entity);
395       list->data=NULL;
396       list=list->next;
397     }
398   g_list_free(subdir_list);
399   return 0;
402 static int
403 handle_select(screen_t *screen, mpdclient_t *c)
405   filelist_entry_t *entry;
407   entry=( filelist_entry_t *) g_list_nth_data(filelist->list, lw->selected);
408   if( entry==NULL || entry->entity==NULL)
409     return -1;
411   if( entry->entity->type==MPD_INFO_ENTITY_TYPE_PLAYLISTFILE )
412     return load_playlist(screen, c, entry);
414   if( entry->entity->type==MPD_INFO_ENTITY_TYPE_DIRECTORY )
415     {
416       mpd_Directory *dir = entry->entity->info.directory;
417       add_directory(c, dir->path);
418       return 0;
419     }
421   if( entry->entity->type!=MPD_INFO_ENTITY_TYPE_SONG )
422     return -1; 
424   if( entry->flags & HIGHLIGHT )
425     entry->flags &= ~HIGHLIGHT;
426   else
427     entry->flags |= HIGHLIGHT;
429   if( entry->flags & HIGHLIGHT )
430     {
431       if( entry->entity->type==MPD_INFO_ENTITY_TYPE_SONG )
432         {
433           mpd_Song *song = entry->entity->info.song;
435           if( mpdclient_cmd_add(c, song) == 0 )
436             {
437               char buf[BUFSIZE];
438               
439               strfsong(buf, BUFSIZE, LIST_FORMAT, song);
440               screen_status_printf(_("Adding \'%s\' to playlist\n"), buf);
441             }
442         }
443     }
444   else
445     {
446       /* remove song from playlist */
447       if( entry->entity->type==MPD_INFO_ENTITY_TYPE_SONG )
448         {
449           mpd_Song *song = entry->entity->info.song;
451           if( song )
452             {
453               int index = playlist_get_index_from_file(c, song->file);
454               
455               while( (index=playlist_get_index_from_file(c, song->file))>=0 )
456                 mpdclient_cmd_delete(c, index);
457             }
458         }
459     }
460   return 0;
463 static void
464 browse_init(WINDOW *w, int cols, int rows)
466   lw = list_window_init(w, cols, rows);
469 static void
470 browse_resize(int cols, int rows)
472   lw->cols = cols;
473   lw->rows = rows;
476 static void
477 browse_exit(void)
479   if( lw_state_list )
480     {
481       GList *list = lw_state_list;
482       while( list )
483         {
484           g_free(list->data);
485           list->data = NULL;
486           list = list->next;
487         }
488       g_list_free(lw_state_list);
489       lw_state_list = NULL;
491     }
492   if( filelist )
493     filelist = mpdclient_filelist_free(filelist);
494   list_window_free(lw);
497 static void 
498 browse_open(screen_t *screen, mpdclient_t *c)
500   if( filelist == NULL )
501     {
502       filelist = mpdclient_filelist_get(c, "");
503       mpdclient_install_playlist_callback(c, playlist_changed_callback);
504       mpdclient_install_browse_callback(c, file_changed_callback);
505     }
508 static void
509 browse_close(void)
513 static char *
514 browse_title(char *str, size_t size)
516   snprintf(str, size, _("Browse: %s"), basename(filelist->path));
517   return str;
520 static void 
521 browse_paint(screen_t *screen, mpdclient_t *c)
523   lw->clear = 1;
524   
525   list_window_paint(lw, list_callback, (void *) c);
526   wnoutrefresh(lw->w);
529 static void 
530 browse_update(screen_t *screen, mpdclient_t *c)
532   if( filelist->updated )
533     {
534       browse_paint(screen, c);
535       filelist->updated = FALSE;
536       return;
537     }
538   list_window_paint(lw, list_callback, (void *) c);
539   wnoutrefresh(lw->w);
543 static int 
544 browse_cmd(screen_t *screen, mpdclient_t *c, command_t cmd)
546   switch(cmd)
547     {
548     case CMD_PLAY:
549       handle_enter(screen, c);
550       return 1;
551     case CMD_SELECT:
552       if( handle_select(screen, c) == 0 )
553         {
554           /* continue and select next item... */
555           cmd = CMD_LIST_NEXT;
556         }
557       break;
558     case CMD_DELETE:
559       handle_delete(screen, c);
560       break;
561     case CMD_SCREEN_UPDATE:
562       filelist = mpdclient_filelist_update(c, filelist);
563       list_window_check_selected(lw, filelist->length);
564       screen_status_printf(_("Screen updated!"));
565       return 1;
566     case CMD_DB_UPDATE:
567       if( !c->status->updatingDb )
568         {
569           if( mpdclient_cmd_db_update(c,filelist->path)==0 )
570             {
571               screen_status_printf(_("Database update of %s started!"),
572                                    filelist->path);
573               /* set updatingDb to make shure the browse callback gets called
574                * even if the updated has finished before status is updated */
575               c->status->updatingDb = 1; 
576             }
577         }
578       else
579         screen_status_printf(_("Database update running..."));
580       return 1;
581     case CMD_LIST_FIND:
582     case CMD_LIST_RFIND:
583     case CMD_LIST_FIND_NEXT:
584     case CMD_LIST_RFIND_NEXT:
585       return screen_find(screen, c, 
586                          lw, filelist->length,
587                          cmd, list_callback);
588     default:
589       break;
590     }
591   return list_window_cmd(lw, filelist->length, cmd);
595 list_window_t *
596 get_filelist_window()
598   return lw;
604 screen_functions_t *
605 get_screen_browse(void)
607   static screen_functions_t functions;
609   memset(&functions, 0, sizeof(screen_functions_t));
610   functions.init   = browse_init;
611   functions.exit   = browse_exit;
612   functions.open   = browse_open;
613   functions.close  = browse_close;
614   functions.resize = browse_resize;
615   functions.paint  = browse_paint;
616   functions.update = browse_update;
617   functions.cmd    = browse_cmd;
618   functions.get_lw = get_filelist_window;
619   functions.get_title = browse_title;
621   return &functions;