2caf2519723a6953185a60810a5d553af2d17d60
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);
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;
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 }
118 }
120 /* the db have changed -> update the filelist */
121 static void
122 file_changed_callback(mpdclient_t *c, int event, gpointer data)
123 {
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);
128 }
130 /* the playlist have been updated -> fix highlights */
131 static void
132 playlist_changed_callback(mpdclient_t *c, int event, gpointer data)
133 {
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 }
152 }
154 /* store current state when entering a subdirectory */
155 static void
156 push_lw_state(void)
157 {
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);
162 }
164 /* get previous state when leaving a directory */
165 static void
166 pop_lw_state(void)
167 {
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 }
177 }
179 /* list_window callback */
180 static char *
181 list_callback(int index, int *highlight, void *data)
182 {
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!";
229 }
231 /* chdir */
232 static int
233 change_directory(screen_t *screen, mpdclient_t *c, filelist_entry_t *entry)
234 {
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;
270 }
272 static int
273 load_playlist(screen_t *screen, mpdclient_t *c, filelist_entry_t *entry)
274 {
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;
283 }
285 static int
286 handle_delete(screen_t *screen, mpdclient_t *c)
287 {
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;
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_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);
592 }
595 list_window_t *
596 get_filelist_window()
597 {
598 return lw;
599 }
604 screen_functions_t *
605 get_screen_browse(void)
606 {
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;
622 }