Code

Major cleanup of the mpd client code (mpc->mpdclient)
[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 "support.h"
30 #include "mpdclient.h"
31 #include "strfsong.h"
32 #include "command.h"
33 #include "screen.h"
34 #include "screen_utils.h"
37 #define USE_OLD_LAYOUT
39 #define BUFSIZE 1024
41 #define HIGHLIGHT  (0x01)
44 static list_window_t *lw = NULL;
45 static GList *lw_state_list = NULL;
46 static mpdclient_filelist_t *filelist = NULL;
50 /* clear the highlight flag for all items in the filelist */
51 static void
52 clear_highlights(mpdclient_filelist_t *filelist)
53 {
54   GList *list = g_list_first(filelist->list);
55   
56   while( list )
57     {
58       filelist_entry_t *entry = list->data;
60       entry->flags &= ~HIGHLIGHT;
61       list = list->next;
62     }
63 }
65 /* change the highlight flag for a song */
66 static void
67 set_highlight(mpdclient_filelist_t *filelist, mpd_Song *song, int highlight)
68 {
69   GList *list = g_list_first(filelist->list);
71   if( !song )
72     return;
74   while( list )
75     {
76       filelist_entry_t *entry = list->data;
77       mpd_InfoEntity *entity  = entry->entity;
79       if( entity && entity->type==MPD_INFO_ENTITY_TYPE_SONG )
80         {
81           mpd_Song *song2 = entity->info.song;
83           if( strcmp(song->file, song2->file) == 0 )
84             {
85               if(highlight)
86                 entry->flags |= HIGHLIGHT;
87               else
88                 entry->flags &= ~HIGHLIGHT;
89             }
90         }
91       list = list->next;
92     }
93 }
95 /* sync highlight flags with playlist */
96 static void
97 sync_highlights(mpdclient_t *c, mpdclient_filelist_t *filelist)
98 {
99   GList *list = g_list_first(filelist->list);
101   while(list)
102     {
103       filelist_entry_t *entry = list->data;
104       mpd_InfoEntity *entity = entry->entity;
106       if( entity && entity->type==MPD_INFO_ENTITY_TYPE_SONG )
107         {
108           mpd_Song *song = entity->info.song;
109           
110           if( playlist_get_index_from_file(c, song->file) >= 0 )
111             entry->flags |= HIGHLIGHT;
112           else
113             entry->flags &= ~HIGHLIGHT;
114         }
115       list=list->next;
116     }
119 /* the db have changed -> update the filelist */
120 static void 
121 file_changed_callback(mpdclient_t *c, int event, gpointer data)
123   D("screen_file.c> filelist_callback() [%d]\n", event);
124   filelist = mpdclient_filelist_update(c, filelist);
125   sync_highlights(c, filelist);
126   list_window_check_selected(lw, filelist->length);
129 /* the playlist have been updated -> fix highlights */
130 static void 
131 playlist_changed_callback(mpdclient_t *c, int event, gpointer data)
133   D("screen_file.c> playlist_callback() [%d]\n", event);
134   switch(event)
135     {
136     case PLAYLIST_EVENT_CLEAR:
137       clear_highlights(filelist);
138       break;
139     case PLAYLIST_EVENT_ADD:
140       set_highlight(filelist, (mpd_Song *) data, 1); 
141       break;
142     case PLAYLIST_EVENT_DELETE:
143       set_highlight(filelist, (mpd_Song *) data, 0); 
144       break;
145     case PLAYLIST_EVENT_MOVE:
146       break;
147     default:
148       sync_highlights(c, filelist);
149       break;
150     }
153 /* store current state when entering a subdirectory */
154 static void
155 push_lw_state(void)
157   list_window_t *tmp = g_malloc(sizeof(list_window_t));
159   memcpy(tmp, lw, sizeof(list_window_t));
160   lw_state_list = g_list_prepend(lw_state_list, (gpointer) tmp);
163 /* get previous state when leaving a directory */
164 static void
165 pop_lw_state(void)
167   if( lw_state_list )
168     {
169       list_window_t *tmp = lw_state_list->data;
171       memcpy(lw, tmp, sizeof(list_window_t));
172       g_free(tmp);
173       lw_state_list->data = NULL;
174       lw_state_list = g_list_delete_link(lw_state_list, lw_state_list);
175     }
178 /* list_window callback */
179 static char *
180 list_callback(int index, int *highlight, void *data)
182   static char buf[BUFSIZE];
183   //mpdclient_t *c = (mpdclient_t *) data;
184   filelist_entry_t *entry;
185   mpd_InfoEntity *entity;
187   *highlight = 0;
188   if( (entry=(filelist_entry_t *)g_list_nth_data(filelist->list,index))==NULL )
189     return NULL;
191   entity = entry->entity;
192   *highlight = (entry->flags & HIGHLIGHT);
194   if( entity == NULL )
195     {
196       return "[..]";
197     }
198   if( entity->type==MPD_INFO_ENTITY_TYPE_DIRECTORY ) 
199     {
200       mpd_Directory *dir = entity->info.directory;
201       char *dirname = utf8_to_locale(basename(dir->path));
203       snprintf(buf, BUFSIZE, "[%s]", dirname);
204       g_free(dirname);
205       return buf;
206     }
207   else if( entity->type==MPD_INFO_ENTITY_TYPE_SONG )
208     {
209       mpd_Song *song = entity->info.song;
211       strfsong(buf, BUFSIZE, LIST_FORMAT, song);
212       return buf;
213     }
214   else if( entity->type==MPD_INFO_ENTITY_TYPE_PLAYLISTFILE )
215     {
216       mpd_PlaylistFile *plf = entity->info.playlistFile;
217       char *filename = utf8_to_locale(basename(plf->path));
219 #ifdef USE_OLD_LAYOUT      
220       snprintf(buf, BUFSIZE, "*%s*", filename);
221 #else 
222       snprintf(buf, BUFSIZE, "<Playlist> %s", filename);
223 #endif
224       g_free(filename);
225       return buf;
226     }
227   return "Error: Unknow entry!";
230 /* chdir */
231 static int
232 change_directory(screen_t *screen, mpdclient_t *c, filelist_entry_t *entry)
234   mpd_InfoEntity *entity = entry->entity;
235   gchar *path = NULL;
237   if( entity==NULL )
238     {
239       /* return to parent */
240       char *parent = g_path_get_dirname(filelist->path);
241       if( strcmp(parent, ".") == 0 )
242         {
243           parent[0] = '\0';
244         }
245       path = g_strdup(parent);
246       list_window_reset(lw);
247       /* restore previous list window state */
248       pop_lw_state(); 
249     }
250   else
251     if( entity->type==MPD_INFO_ENTITY_TYPE_DIRECTORY)
252       {
253         /* enter sub */
254         mpd_Directory *dir = entity->info.directory;
255         path = utf8_to_locale(dir->path);      
256         /* save current list window state */
257         push_lw_state(); 
258         list_window_reset(lw);
259       }
260     else
261       return -1;
263   filelist = mpdclient_filelist_free(filelist);
264   filelist = mpdclient_filelist_get(c, path);
265   sync_highlights(c, filelist);
266   list_window_check_selected(lw, filelist->length);
267   g_free(path);
268   return 0;
271 static int
272 load_playlist(screen_t *screen, mpdclient_t *c, filelist_entry_t *entry)
274   mpd_InfoEntity *entity = entry->entity;
275   mpd_PlaylistFile *plf = entity->info.playlistFile;
276   char *filename = utf8_to_locale(plf->path);
278   if( mpdclient_cmd_load_playlist(c, plf->path) == 0 )
279     screen_status_printf(_("Loading playlist %s..."), basename(filename));
280   g_free(filename);
281   return 0;
284 static int 
285 handle_delete(screen_t *screen, mpdclient_t *c)
287   filelist_entry_t *entry;
288   mpd_InfoEntity *entity;
289   mpd_PlaylistFile *plf;
290   char *str, buf[BUFSIZE];
291   int key;
293   entry=( filelist_entry_t *) g_list_nth_data(filelist->list,lw->selected);
294   if( entry==NULL || entry->entity==NULL )
295     return -1;
297   entity = entry->entity;
299   if( entity->type!=MPD_INFO_ENTITY_TYPE_PLAYLISTFILE )
300     {
301       screen_status_printf(_("You can only delete playlists!"));
302       beep();
303       return -1;
304     }
306   plf = entity->info.playlistFile;
307   str = utf8_to_locale(basename(plf->path));
308   snprintf(buf, BUFSIZE, _("Delete playlist %s [y/n] ? "), str);
309   g_free(str);  
310   key = tolower(screen_getch(screen->status_window.w, buf));
311   if( key==KEY_RESIZE )
312     screen_resize();
313   if( key!='y' )
314     {
315       screen_status_printf(_("Aborted!"));
316       return 0;
317     }
319   if( mpdclient_cmd_delete_playlist(c, plf->path) )
320     {
321       beep();
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_LIST_FIND:
567     case CMD_LIST_RFIND:
568     case CMD_LIST_FIND_NEXT:
569     case CMD_LIST_RFIND_NEXT:
570       return screen_find(screen, c, 
571                          lw, filelist->length,
572                          cmd, list_callback);
573     default:
574       break;
575     }
576   return list_window_cmd(lw, filelist->length, cmd);
580 list_window_t *
581 get_filelist_window()
583   return lw;
589 screen_functions_t *
590 get_screen_browse(void)
592   static screen_functions_t functions;
594   memset(&functions, 0, sizeof(screen_functions_t));
595   functions.init   = browse_init;
596   functions.exit   = browse_exit;
597   functions.open   = browse_open;
598   functions.close  = browse_close;
599   functions.resize = browse_resize;
600   functions.paint  = browse_paint;
601   functions.update = browse_update;
602   functions.cmd    = browse_cmd;
603   functions.get_lw = get_filelist_window;
604   functions.get_title = browse_title;
606   return &functions;