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);
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;
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 }
117 }
119 /* the db have changed -> update the filelist */
120 static void
121 file_changed_callback(mpdclient_t *c, int event, gpointer data)
122 {
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);
127 }
129 /* the playlist have been updated -> fix highlights */
130 static void
131 playlist_changed_callback(mpdclient_t *c, int event, gpointer data)
132 {
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 }
151 }
153 /* store current state when entering a subdirectory */
154 static void
155 push_lw_state(void)
156 {
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);
161 }
163 /* get previous state when leaving a directory */
164 static void
165 pop_lw_state(void)
166 {
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 }
176 }
178 /* list_window callback */
179 static char *
180 list_callback(int index, int *highlight, void *data)
181 {
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!";
228 }
230 /* chdir */
231 static int
232 change_directory(screen_t *screen, mpdclient_t *c, filelist_entry_t *entry)
233 {
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;
269 }
271 static int
272 load_playlist(screen_t *screen, mpdclient_t *c, filelist_entry_t *entry)
273 {
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;
282 }
284 static int
285 handle_delete(screen_t *screen, mpdclient_t *c)
286 {
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;
326 }
329 static int
330 handle_enter(screen_t *screen, mpdclient_t *c)
331 {
332 filelist_entry_t *entry;
333 mpd_InfoEntity *entity;
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;
345 }
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)
353 {
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;
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;
400 }
402 static int
403 handle_select(screen_t *screen, mpdclient_t *c)
404 {
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];
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);
455 while( (index=playlist_get_index_from_file(c, song->file))>=0 )
456 mpdclient_cmd_delete(c, index);
457 }
458 }
459 }
460 return 0;
461 }
463 static void
464 browse_init(WINDOW *w, int cols, int rows)
465 {
466 lw = list_window_init(w, cols, rows);
467 }
469 static void
470 browse_resize(int cols, int rows)
471 {
472 lw->cols = cols;
473 lw->rows = rows;
474 }
476 static void
477 browse_exit(void)
478 {
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);
495 }
497 static void
498 browse_open(screen_t *screen, mpdclient_t *c)
499 {
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 }
506 }
508 static void
509 browse_close(void)
510 {
511 }
513 static char *
514 browse_title(char *str, size_t size)
515 {
516 snprintf(str, size, _("Browse: %s"), basename(filelist->path));
517 return str;
518 }
520 static void
521 browse_paint(screen_t *screen, mpdclient_t *c)
522 {
523 lw->clear = 1;
525 list_window_paint(lw, list_callback, (void *) c);
526 wnoutrefresh(lw->w);
527 }
529 static void
530 browse_update(screen_t *screen, mpdclient_t *c)
531 {
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);
540 }
543 static int
544 browse_cmd(screen_t *screen, mpdclient_t *c, command_t cmd)
545 {
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);
577 }
580 list_window_t *
581 get_filelist_window()
582 {
583 return lw;
584 }
589 screen_functions_t *
590 get_screen_browse(void)
591 {
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;
607 }