75095db9a1bfdbe2e5e9fb9253aba700f7b52a3b
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"
36 #include "screen_browse.h"
39 #define USE_OLD_LAYOUT
40 #undef USE_OLD_ADD
42 #define BUFSIZE 1024
44 #define HIGHLIGHT (0x01)
47 static list_window_t *lw = NULL;
48 static GList *lw_state_list = NULL;
49 static mpdclient_filelist_t *filelist = NULL;
53 /* clear the highlight flag for all items in the filelist */
54 void
55 clear_highlights(mpdclient_filelist_t *filelist)
56 {
57 GList *list = g_list_first(filelist->list);
59 while( list )
60 {
61 filelist_entry_t *entry = list->data;
63 entry->flags &= ~HIGHLIGHT;
64 list = list->next;
65 }
66 }
68 /* change the highlight flag for a song */
69 void
70 set_highlight(mpdclient_filelist_t *filelist, mpd_Song *song, int highlight)
71 {
72 GList *list = g_list_first(filelist->list);
74 if( !song )
75 return;
77 while( list )
78 {
79 filelist_entry_t *entry = list->data;
80 mpd_InfoEntity *entity = entry->entity;
82 if( entity && entity->type==MPD_INFO_ENTITY_TYPE_SONG )
83 {
84 mpd_Song *song2 = entity->info.song;
86 if( strcmp(song->file, song2->file) == 0 )
87 {
88 if(highlight)
89 entry->flags |= HIGHLIGHT;
90 else
91 entry->flags &= ~HIGHLIGHT;
92 }
93 }
94 list = list->next;
95 }
96 }
98 /* sync highlight flags with playlist */
99 void
100 sync_highlights(mpdclient_t *c, mpdclient_filelist_t *filelist)
101 {
102 GList *list = g_list_first(filelist->list);
104 while(list)
105 {
106 filelist_entry_t *entry = list->data;
107 mpd_InfoEntity *entity = entry->entity;
109 if( entity && entity->type==MPD_INFO_ENTITY_TYPE_SONG )
110 {
111 mpd_Song *song = entity->info.song;
113 if( playlist_get_index_from_file(c, song->file) >= 0 )
114 entry->flags |= HIGHLIGHT;
115 else
116 entry->flags &= ~HIGHLIGHT;
117 }
118 list=list->next;
119 }
120 }
122 /* the db have changed -> update the filelist */
123 static void
124 file_changed_callback(mpdclient_t *c, int event, gpointer data)
125 {
126 D("screen_file.c> filelist_callback() [%d]\n", event);
127 filelist = mpdclient_filelist_update(c, filelist);
128 sync_highlights(c, filelist);
129 list_window_check_selected(lw, filelist->length);
130 }
132 /* the playlist have been updated -> fix highlights */
133 static void
134 playlist_changed_callback(mpdclient_t *c, int event, gpointer data)
135 {
136 D("screen_file.c> playlist_callback() [%d]\n", event);
137 switch(event)
138 {
139 case PLAYLIST_EVENT_CLEAR:
140 clear_highlights(filelist);
141 break;
142 case PLAYLIST_EVENT_ADD:
143 set_highlight(filelist, (mpd_Song *) data, 1);
144 break;
145 case PLAYLIST_EVENT_DELETE:
146 set_highlight(filelist, (mpd_Song *) data, 0);
147 break;
148 case PLAYLIST_EVENT_MOVE:
149 break;
150 default:
151 sync_highlights(c, filelist);
152 break;
153 }
154 }
156 /* store current state when entering a subdirectory */
157 static void
158 push_lw_state(void)
159 {
160 list_window_t *tmp = g_malloc(sizeof(list_window_t));
162 memcpy(tmp, lw, sizeof(list_window_t));
163 lw_state_list = g_list_prepend(lw_state_list, (gpointer) tmp);
164 }
166 /* get previous state when leaving a directory */
167 static void
168 pop_lw_state(void)
169 {
170 if( lw_state_list )
171 {
172 list_window_t *tmp = lw_state_list->data;
174 memcpy(lw, tmp, sizeof(list_window_t));
175 g_free(tmp);
176 lw_state_list->data = NULL;
177 lw_state_list = g_list_delete_link(lw_state_list, lw_state_list);
178 }
179 }
181 /* list_window callback */
182 char *
183 browse_lw_callback(int index, int *highlight, void *data)
184 {
185 static char buf[BUFSIZE];
186 mpdclient_filelist_t *filelist = (mpdclient_filelist_t *) data;
187 filelist_entry_t *entry;
188 mpd_InfoEntity *entity;
190 *highlight = 0;
191 if( (entry=(filelist_entry_t *)g_list_nth_data(filelist->list,index))==NULL )
192 return NULL;
194 entity = entry->entity;
195 *highlight = (entry->flags & HIGHLIGHT);
197 if( entity == NULL )
198 {
199 return "[..]";
200 }
201 if( entity->type==MPD_INFO_ENTITY_TYPE_DIRECTORY )
202 {
203 mpd_Directory *dir = entity->info.directory;
204 char *dirname = utf8_to_locale(basename(dir->path));
206 g_snprintf(buf, BUFSIZE, "[%s]", dirname);
207 g_free(dirname);
208 return buf;
209 }
210 else if( entity->type==MPD_INFO_ENTITY_TYPE_SONG )
211 {
212 mpd_Song *song = entity->info.song;
214 strfsong(buf, BUFSIZE, LIST_FORMAT, song);
215 return buf;
216 }
217 else if( entity->type==MPD_INFO_ENTITY_TYPE_PLAYLISTFILE )
218 {
219 mpd_PlaylistFile *plf = entity->info.playlistFile;
220 char *filename = utf8_to_locale(basename(plf->path));
222 #ifdef USE_OLD_LAYOUT
223 g_snprintf(buf, BUFSIZE, "*%s*", filename);
224 #else
225 g_snprintf(buf, BUFSIZE, "<Playlist> %s", filename);
226 #endif
227 g_free(filename);
228 return buf;
229 }
230 return "Error: Unknow entry!";
231 }
233 /* chdir */
234 static int
235 change_directory(screen_t *screen, mpdclient_t *c, filelist_entry_t *entry)
236 {
237 mpd_InfoEntity *entity = entry->entity;
238 gchar *path = NULL;
240 if( entity==NULL )
241 {
242 /* return to parent */
243 char *parent = g_path_get_dirname(filelist->path);
244 if( strcmp(parent, ".") == 0 )
245 {
246 parent[0] = '\0';
247 }
248 path = g_strdup(parent);
249 list_window_reset(lw);
250 /* restore previous list window state */
251 pop_lw_state();
252 }
253 else
254 if( entity->type==MPD_INFO_ENTITY_TYPE_DIRECTORY)
255 {
256 /* enter sub */
257 mpd_Directory *dir = entity->info.directory;
258 path = utf8_to_locale(dir->path);
259 /* save current list window state */
260 push_lw_state();
261 list_window_reset(lw);
262 }
263 else
264 return -1;
266 filelist = mpdclient_filelist_free(filelist);
267 filelist = mpdclient_filelist_get(c, path);
268 sync_highlights(c, filelist);
269 list_window_check_selected(lw, filelist->length);
270 g_free(path);
271 return 0;
272 }
274 static int
275 load_playlist(screen_t *screen, mpdclient_t *c, filelist_entry_t *entry)
276 {
277 mpd_InfoEntity *entity = entry->entity;
278 mpd_PlaylistFile *plf = entity->info.playlistFile;
279 char *filename = utf8_to_locale(plf->path);
281 if( mpdclient_cmd_load_playlist_utf8(c, plf->path) == 0 )
282 screen_status_printf(_("Loading playlist %s..."), basename(filename));
283 g_free(filename);
284 return 0;
285 }
287 static int
288 handle_delete(screen_t *screen, mpdclient_t *c)
289 {
290 filelist_entry_t *entry;
291 mpd_InfoEntity *entity;
292 mpd_PlaylistFile *plf;
293 char *str, *buf;
294 int key;
296 entry=( filelist_entry_t *) g_list_nth_data(filelist->list,lw->selected);
297 if( entry==NULL || entry->entity==NULL )
298 return -1;
300 entity = entry->entity;
302 if( entity->type!=MPD_INFO_ENTITY_TYPE_PLAYLISTFILE )
303 {
304 screen_status_printf(_("You can only delete playlists!"));
305 screen_bell();
306 return -1;
307 }
309 plf = entity->info.playlistFile;
310 str = utf8_to_locale(basename(plf->path));
311 buf = g_strdup_printf(_("Delete playlist %s [%s/%s] ? "), str, YES, NO);
312 g_free(str);
313 key = tolower(screen_getch(screen->status_window.w, buf));
314 g_free(buf);
315 if( key==KEY_RESIZE )
316 screen_resize();
317 if( key != YES[0] )
318 {
319 screen_status_printf(_("Aborted!"));
320 return 0;
321 }
323 if( mpdclient_cmd_delete_playlist_utf8(c, plf->path) )
324 {
325 return -1;
326 }
327 screen_status_printf(_("Playlist deleted!"));
328 return 0;
329 }
331 static int
332 enqueue_and_play(screen_t *screen, mpdclient_t *c, filelist_entry_t *entry)
333 {
334 mpd_InfoEntity *entity = entry->entity;
335 mpd_Song *song = entity->info.song;
337 if(!( entry->flags & HIGHLIGHT ))
338 {
339 if( mpdclient_cmd_add(c, song) == 0 )
340 {
341 char buf[BUFSIZE];
343 entry->flags |= HIGHLIGHT;
344 strfsong(buf, BUFSIZE, LIST_FORMAT, song);
345 screen_status_printf(_("Adding \'%s\' to playlist\n"), buf);
346 mpdclient_update(c); /* get song id */
347 }
348 else
349 return -1;
350 }
352 int index = playlist_get_index_from_file(c, song->file);
353 mpdclient_cmd_play(c, index);
354 return 0;
355 }
357 int
358 browse_handle_enter(screen_t *screen,
359 mpdclient_t *c,
360 list_window_t *lw,
361 mpdclient_filelist_t *filelist)
362 {
363 filelist_entry_t *entry;
364 mpd_InfoEntity *entity;
366 entry = ( filelist_entry_t *) g_list_nth_data(filelist->list, lw->selected);
367 if( entry==NULL )
368 return -1;
370 entity = entry->entity;
371 if( entity==NULL || entity->type==MPD_INFO_ENTITY_TYPE_DIRECTORY )
372 return change_directory(screen, c, entry);
373 else if( entity->type==MPD_INFO_ENTITY_TYPE_PLAYLISTFILE )
374 return load_playlist(screen, c, entry);
375 else if( entity->type==MPD_INFO_ENTITY_TYPE_SONG )
376 return enqueue_and_play(screen, c, entry);
377 return -1;
378 }
381 #ifdef USE_OLD_ADD
382 /* NOTE - The add_directory functions should move to mpdclient.c */
383 extern gint mpdclient_finish_command(mpdclient_t *c);
385 static int
386 add_directory(mpdclient_t *c, char *dir)
387 {
388 mpd_InfoEntity *entity;
389 GList *subdir_list = NULL;
390 GList *list = NULL;
391 char *dirname;
393 dirname = utf8_to_locale(dir);
394 screen_status_printf(_("Adding directory %s...\n"), dirname);
395 doupdate();
396 g_free(dirname);
397 dirname = NULL;
399 mpd_sendLsInfoCommand(c->connection, dir);
400 mpd_sendCommandListBegin(c->connection);
401 while( (entity=mpd_getNextInfoEntity(c->connection)) )
402 {
403 if( entity->type==MPD_INFO_ENTITY_TYPE_SONG )
404 {
405 mpd_Song *song = entity->info.song;
406 mpd_sendAddCommand(c->connection, song->file);
407 mpd_freeInfoEntity(entity);
408 }
409 else if( entity->type==MPD_INFO_ENTITY_TYPE_DIRECTORY )
410 {
411 subdir_list = g_list_append(subdir_list, (gpointer) entity);
412 }
413 else
414 mpd_freeInfoEntity(entity);
415 }
416 mpd_sendCommandListEnd(c->connection);
417 mpdclient_finish_command(c);
418 c->need_update = TRUE;
420 list = g_list_first(subdir_list);
421 while( list!=NULL )
422 {
423 mpd_Directory *dir;
425 entity = list->data;
426 dir = entity->info.directory;
427 add_directory(c, dir->path);
428 mpd_freeInfoEntity(entity);
429 list->data=NULL;
430 list=list->next;
431 }
432 g_list_free(subdir_list);
433 return 0;
434 }
435 #endif
437 int
438 browse_handle_select(screen_t *screen,
439 mpdclient_t *c,
440 list_window_t *lw,
441 mpdclient_filelist_t *filelist)
442 {
443 filelist_entry_t *entry;
445 entry=( filelist_entry_t *) g_list_nth_data(filelist->list, lw->selected);
446 if( entry==NULL || entry->entity==NULL)
447 return -1;
449 if( entry->entity->type==MPD_INFO_ENTITY_TYPE_PLAYLISTFILE )
450 return load_playlist(screen, c, entry);
452 if( entry->entity->type==MPD_INFO_ENTITY_TYPE_DIRECTORY )
453 {
454 mpd_Directory *dir = entry->entity->info.directory;
455 #ifdef USE_OLD_ADD
456 add_directory(c, tmp);
457 #else
458 if( mpdclient_cmd_add_path_utf8(c, dir->path) == 0 )
459 {
460 char *tmp = utf8_to_locale(dir->path);
462 screen_status_printf(_("Adding \'%s\' to playlist\n"), tmp);
463 g_free(tmp);
464 }
465 #endif
466 return 0;
467 }
469 if( entry->entity->type!=MPD_INFO_ENTITY_TYPE_SONG )
470 return -1;
472 if( entry->flags & HIGHLIGHT )
473 entry->flags &= ~HIGHLIGHT;
474 else
475 entry->flags |= HIGHLIGHT;
477 if( entry->flags & HIGHLIGHT )
478 {
479 if( entry->entity->type==MPD_INFO_ENTITY_TYPE_SONG )
480 {
481 mpd_Song *song = entry->entity->info.song;
483 if( mpdclient_cmd_add(c, song) == 0 )
484 {
485 char buf[BUFSIZE];
487 strfsong(buf, BUFSIZE, LIST_FORMAT, song);
488 screen_status_printf(_("Adding \'%s\' to playlist\n"), buf);
489 }
490 }
491 }
492 else
493 {
494 /* remove song from playlist */
495 if( entry->entity->type==MPD_INFO_ENTITY_TYPE_SONG )
496 {
497 mpd_Song *song = entry->entity->info.song;
499 if( song )
500 {
501 int index = playlist_get_index_from_file(c, song->file);
503 while( (index=playlist_get_index_from_file(c, song->file))>=0 )
504 mpdclient_cmd_delete(c, index);
505 }
506 }
507 }
508 return 0;
509 }
511 static void
512 browse_init(WINDOW *w, int cols, int rows)
513 {
514 lw = list_window_init(w, cols, rows);
515 }
517 static void
518 browse_resize(int cols, int rows)
519 {
520 lw->cols = cols;
521 lw->rows = rows;
522 }
524 static void
525 browse_exit(void)
526 {
527 if( lw_state_list )
528 {
529 GList *list = lw_state_list;
530 while( list )
531 {
532 g_free(list->data);
533 list->data = NULL;
534 list = list->next;
535 }
536 g_list_free(lw_state_list);
537 lw_state_list = NULL;
539 }
540 if( filelist )
541 filelist = mpdclient_filelist_free(filelist);
542 list_window_free(lw);
543 }
545 static void
546 browse_open(screen_t *screen, mpdclient_t *c)
547 {
548 if( filelist == NULL )
549 {
550 filelist = mpdclient_filelist_get(c, "");
551 mpdclient_install_playlist_callback(c, playlist_changed_callback);
552 mpdclient_install_browse_callback(c, file_changed_callback);
553 }
554 }
556 static void
557 browse_close(void)
558 {
559 }
561 static char *
562 browse_title(char *str, size_t size)
563 {
564 g_snprintf(str, size, _("Browse: %s"), basename(filelist->path));
565 return str;
566 }
568 static void
569 browse_paint(screen_t *screen, mpdclient_t *c)
570 {
571 lw->clear = 1;
573 list_window_paint(lw, browse_lw_callback, (void *) filelist);
574 wnoutrefresh(lw->w);
575 }
577 static void
578 browse_update(screen_t *screen, mpdclient_t *c)
579 {
580 if( filelist->updated )
581 {
582 browse_paint(screen, c);
583 filelist->updated = FALSE;
584 return;
585 }
586 list_window_paint(lw, browse_lw_callback, (void *) filelist);
587 wnoutrefresh(lw->w);
588 }
591 #ifdef HAVE_GETMOUSE
592 int
593 browse_handle_mouse_event(screen_t *screen,
594 mpdclient_t *c,
595 list_window_t *lw,
596 mpdclient_filelist_t *filelist)
597 {
598 int row;
599 int prev_selected = lw->selected;
600 unsigned long bstate;
602 if( screen_get_mouse_event(c, lw, filelist->length, &bstate, &row) )
603 return 1;
605 lw->selected = lw->start+row;
606 list_window_check_selected(lw, filelist->length);
608 if( bstate & BUTTON1_CLICKED )
609 {
610 if( prev_selected == lw->selected )
611 browse_handle_enter(screen, c, lw, filelist);
612 }
613 else if( bstate & BUTTON3_CLICKED )
614 {
615 if( prev_selected == lw->selected )
616 browse_handle_select(screen, c, lw, filelist);
617 }
619 return 1;
620 }
621 #endif
623 static int
624 browse_cmd(screen_t *screen, mpdclient_t *c, command_t cmd)
625 {
626 switch(cmd)
627 {
628 case CMD_PLAY:
629 browse_handle_enter(screen, c, lw, filelist);
630 return 1;
631 case CMD_SELECT:
632 if( browse_handle_select(screen, c, lw, filelist) == 0 )
633 {
634 /* continue and select next item... */
635 cmd = CMD_LIST_NEXT;
636 }
637 break;
638 case CMD_DELETE:
639 handle_delete(screen, c);
640 break;
641 case CMD_SCREEN_UPDATE:
642 screen->painted = 0;
643 lw->clear = 1;
644 lw->repaint = 1;
645 filelist = mpdclient_filelist_update(c, filelist);
646 list_window_check_selected(lw, filelist->length);
647 screen_status_printf(_("Screen updated!"));
648 return 1;
649 case CMD_DB_UPDATE:
650 if( !c->status->updatingDb )
651 {
652 if( mpdclient_cmd_db_update_utf8(c,filelist->path)==0 )
653 {
654 screen_status_printf(_("Database update of %s started!"),
655 filelist->path);
656 /* set updatingDb to make shure the browse callback gets called
657 * even if the updated has finished before status is updated */
658 c->status->updatingDb = 1;
659 }
660 }
661 else
662 screen_status_printf(_("Database update running..."));
663 return 1;
664 case CMD_LIST_FIND:
665 case CMD_LIST_RFIND:
666 case CMD_LIST_FIND_NEXT:
667 case CMD_LIST_RFIND_NEXT:
668 return screen_find(screen, c,
669 lw, filelist->length,
670 cmd, browse_lw_callback, (void *) filelist);
671 case CMD_MOUSE_EVENT:
672 return browse_handle_mouse_event(screen,c,lw,filelist);
673 default:
674 break;
675 }
676 return list_window_cmd(lw, filelist->length, cmd);
677 }
680 list_window_t *
681 get_filelist_window()
682 {
683 return lw;
684 }
689 screen_functions_t *
690 get_screen_browse(void)
691 {
692 static screen_functions_t functions;
694 memset(&functions, 0, sizeof(screen_functions_t));
695 functions.init = browse_init;
696 functions.exit = browse_exit;
697 functions.open = browse_open;
698 functions.close = browse_close;
699 functions.resize = browse_resize;
700 functions.paint = browse_paint;
701 functions.update = browse_update;
702 functions.cmd = browse_cmd;
703 functions.get_lw = get_filelist_window;
704 functions.get_title = browse_title;
706 return &functions;
707 }