b2ca1b6cd9c6c5acd0bf72f9935f116ce9e55a82
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 list_window_state_t *lw_state = 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 /* list_window callback */
157 char *
158 browse_lw_callback(int index, int *highlight, void *data)
159 {
160 static char buf[BUFSIZE];
161 mpdclient_filelist_t *filelist = (mpdclient_filelist_t *) data;
162 filelist_entry_t *entry;
163 mpd_InfoEntity *entity;
165 *highlight = 0;
166 if( (entry=(filelist_entry_t *)g_list_nth_data(filelist->list,index))==NULL )
167 return NULL;
169 entity = entry->entity;
170 *highlight = (entry->flags & HIGHLIGHT);
172 if( entity == NULL )
173 {
174 return "[..]";
175 }
176 if( entity->type==MPD_INFO_ENTITY_TYPE_DIRECTORY )
177 {
178 mpd_Directory *dir = entity->info.directory;
179 char *dirname = utf8_to_locale(basename(dir->path));
181 g_snprintf(buf, BUFSIZE, "[%s]", dirname);
182 g_free(dirname);
183 return buf;
184 }
185 else if( entity->type==MPD_INFO_ENTITY_TYPE_SONG )
186 {
187 mpd_Song *song = entity->info.song;
189 strfsong(buf, BUFSIZE, LIST_FORMAT, song);
190 return buf;
191 }
192 else if( entity->type==MPD_INFO_ENTITY_TYPE_PLAYLISTFILE )
193 {
194 mpd_PlaylistFile *plf = entity->info.playlistFile;
195 char *filename = utf8_to_locale(basename(plf->path));
197 #ifdef USE_OLD_LAYOUT
198 g_snprintf(buf, BUFSIZE, "*%s*", filename);
199 #else
200 g_snprintf(buf, BUFSIZE, "<Playlist> %s", filename);
201 #endif
202 g_free(filename);
203 return buf;
204 }
205 return "Error: Unknow entry!";
206 }
208 /* chdir */
209 static int
210 change_directory(screen_t *screen, mpdclient_t *c, filelist_entry_t *entry)
211 {
212 mpd_InfoEntity *entity = entry->entity;
213 gchar *path = NULL;
215 if( entity==NULL )
216 {
217 /* return to parent */
218 char *parent = g_path_get_dirname(filelist->path);
219 if( strcmp(parent, ".") == 0 )
220 {
221 parent[0] = '\0';
222 }
223 path = g_strdup(parent);
224 list_window_reset(lw);
225 /* restore previous list window state */
226 list_window_pop_state(lw_state,lw);
227 }
228 else
229 if( entity->type==MPD_INFO_ENTITY_TYPE_DIRECTORY)
230 {
231 /* enter sub */
232 mpd_Directory *dir = entity->info.directory;
233 path = utf8_to_locale(dir->path);
234 /* save current list window state */
235 list_window_push_state(lw_state,lw);
236 }
237 else
238 return -1;
240 filelist = mpdclient_filelist_free(filelist);
241 filelist = mpdclient_filelist_get(c, path);
242 sync_highlights(c, filelist);
243 list_window_check_selected(lw, filelist->length);
244 g_free(path);
245 return 0;
246 }
248 static int
249 load_playlist(screen_t *screen, mpdclient_t *c, filelist_entry_t *entry)
250 {
251 mpd_InfoEntity *entity = entry->entity;
252 mpd_PlaylistFile *plf = entity->info.playlistFile;
253 char *filename = utf8_to_locale(plf->path);
255 if( mpdclient_cmd_load_playlist_utf8(c, plf->path) == 0 )
256 screen_status_printf(_("Loading playlist %s..."), basename(filename));
257 g_free(filename);
258 return 0;
259 }
261 static int
262 handle_save(screen_t *screen, mpdclient_t *c)
263 {
264 filelist_entry_t *entry;
265 char *defaultname = NULL;
268 entry=( filelist_entry_t *) g_list_nth_data(filelist->list,lw->selected);
269 if( entry && entry->entity )
270 {
271 mpd_InfoEntity *entity = entry->entity;
272 if( entity->type==MPD_INFO_ENTITY_TYPE_PLAYLISTFILE )
273 {
274 mpd_PlaylistFile *plf = entity->info.playlistFile;
275 defaultname = plf->path;
276 }
277 }
278 return playlist_save(screen, c, NULL, defaultname);
279 }
281 static int
282 handle_delete(screen_t *screen, mpdclient_t *c)
283 {
284 filelist_entry_t *entry;
285 mpd_InfoEntity *entity;
286 mpd_PlaylistFile *plf;
287 char *str, *buf;
288 int key;
290 entry=( filelist_entry_t *) g_list_nth_data(filelist->list,lw->selected);
291 if( entry==NULL || entry->entity==NULL )
292 return -1;
294 entity = entry->entity;
296 if( entity->type!=MPD_INFO_ENTITY_TYPE_PLAYLISTFILE )
297 {
298 screen_status_printf(_("You can only delete playlists!"));
299 screen_bell();
300 return -1;
301 }
303 plf = entity->info.playlistFile;
304 str = utf8_to_locale(basename(plf->path));
305 buf = g_strdup_printf(_("Delete playlist %s [%s/%s] ? "), str, YES, NO);
306 g_free(str);
307 key = tolower(screen_getch(screen->status_window.w, buf));
308 g_free(buf);
309 if( key==KEY_RESIZE )
310 screen_resize();
311 if( key != YES[0] )
312 {
313 screen_status_printf(_("Aborted!"));
314 return 0;
315 }
317 if( mpdclient_cmd_delete_playlist_utf8(c, plf->path) )
318 {
319 return -1;
320 }
321 screen_status_printf(_("Playlist deleted!"));
322 return 0;
323 }
325 static int
326 enqueue_and_play(screen_t *screen, mpdclient_t *c, filelist_entry_t *entry)
327 {
328 int index;
329 mpd_InfoEntity *entity = entry->entity;
330 mpd_Song *song = entity->info.song;
332 if(!( entry->flags & HIGHLIGHT ))
333 {
334 if( mpdclient_cmd_add(c, song) == 0 )
335 {
336 char buf[BUFSIZE];
338 entry->flags |= HIGHLIGHT;
339 strfsong(buf, BUFSIZE, LIST_FORMAT, song);
340 screen_status_printf(_("Adding \'%s\' to playlist\n"), buf);
341 mpdclient_update(c); /* get song id */
342 }
343 else
344 return -1;
345 }
347 index = playlist_get_index_from_file(c, song->file);
348 mpdclient_cmd_play(c, index);
349 return 0;
350 }
352 int
353 browse_handle_enter(screen_t *screen,
354 mpdclient_t *c,
355 list_window_t *lw,
356 mpdclient_filelist_t *filelist)
357 {
358 filelist_entry_t *entry;
359 mpd_InfoEntity *entity;
361 if ( filelist==NULL )
362 return -1;
363 entry = ( filelist_entry_t *) g_list_nth_data(filelist->list, lw->selected);
364 if( entry==NULL )
365 return -1;
367 entity = entry->entity;
368 if( entity==NULL || entity->type==MPD_INFO_ENTITY_TYPE_DIRECTORY )
369 return change_directory(screen, c, entry);
370 else if( entity->type==MPD_INFO_ENTITY_TYPE_PLAYLISTFILE )
371 return load_playlist(screen, c, entry);
372 else if( entity->type==MPD_INFO_ENTITY_TYPE_SONG )
373 return enqueue_and_play(screen, c, entry);
374 return -1;
375 }
378 #ifdef USE_OLD_ADD
379 /* NOTE - The add_directory functions should move to mpdclient.c */
380 extern gint mpdclient_finish_command(mpdclient_t *c);
382 static int
383 add_directory(mpdclient_t *c, char *dir)
384 {
385 mpd_InfoEntity *entity;
386 GList *subdir_list = NULL;
387 GList *list = NULL;
388 char *dirname;
390 dirname = utf8_to_locale(dir);
391 screen_status_printf(_("Adding directory %s...\n"), dirname);
392 doupdate();
393 g_free(dirname);
394 dirname = NULL;
396 mpd_sendLsInfoCommand(c->connection, dir);
397 mpd_sendCommandListBegin(c->connection);
398 while( (entity=mpd_getNextInfoEntity(c->connection)) )
399 {
400 if( entity->type==MPD_INFO_ENTITY_TYPE_SONG )
401 {
402 mpd_Song *song = entity->info.song;
403 mpd_sendAddCommand(c->connection, song->file);
404 mpd_freeInfoEntity(entity);
405 }
406 else if( entity->type==MPD_INFO_ENTITY_TYPE_DIRECTORY )
407 {
408 subdir_list = g_list_append(subdir_list, (gpointer) entity);
409 }
410 else
411 mpd_freeInfoEntity(entity);
412 }
413 mpd_sendCommandListEnd(c->connection);
414 mpdclient_finish_command(c);
415 c->need_update = TRUE;
417 list = g_list_first(subdir_list);
418 while( list!=NULL )
419 {
420 mpd_Directory *dir;
422 entity = list->data;
423 dir = entity->info.directory;
424 add_directory(c, dir->path);
425 mpd_freeInfoEntity(entity);
426 list->data=NULL;
427 list=list->next;
428 }
429 g_list_free(subdir_list);
430 return 0;
431 }
432 #endif
434 int
435 browse_handle_select(screen_t *screen,
436 mpdclient_t *c,
437 list_window_t *lw,
438 mpdclient_filelist_t *filelist)
439 {
440 filelist_entry_t *entry;
442 if ( filelist==NULL )
443 return -1;
444 entry=( filelist_entry_t *) g_list_nth_data(filelist->list, lw->selected);
445 if( entry==NULL || entry->entity==NULL)
446 return -1;
448 if( entry->entity->type==MPD_INFO_ENTITY_TYPE_PLAYLISTFILE )
449 return load_playlist(screen, c, entry);
451 if( entry->entity->type==MPD_INFO_ENTITY_TYPE_DIRECTORY )
452 {
453 mpd_Directory *dir = entry->entity->info.directory;
454 #ifdef USE_OLD_ADD
455 add_directory(c, tmp);
456 #else
457 if( mpdclient_cmd_add_path_utf8(c, dir->path) == 0 )
458 {
459 char *tmp = utf8_to_locale(dir->path);
461 screen_status_printf(_("Adding \'%s\' to playlist\n"), tmp);
462 g_free(tmp);
463 }
464 #endif
465 return 0;
466 }
468 if( entry->entity->type!=MPD_INFO_ENTITY_TYPE_SONG )
469 return -1;
471 if( entry->flags & HIGHLIGHT )
472 entry->flags &= ~HIGHLIGHT;
473 else
474 entry->flags |= HIGHLIGHT;
476 if( entry->flags & HIGHLIGHT )
477 {
478 if( entry->entity->type==MPD_INFO_ENTITY_TYPE_SONG )
479 {
480 mpd_Song *song = entry->entity->info.song;
482 if( mpdclient_cmd_add(c, song) == 0 )
483 {
484 char buf[BUFSIZE];
486 strfsong(buf, BUFSIZE, LIST_FORMAT, song);
487 screen_status_printf(_("Adding \'%s\' to playlist\n"), buf);
488 }
489 }
490 }
491 else
492 {
493 /* remove song from playlist */
494 if( entry->entity->type==MPD_INFO_ENTITY_TYPE_SONG )
495 {
496 mpd_Song *song = entry->entity->info.song;
498 if( song )
499 {
500 int index = playlist_get_index_from_file(c, song->file);
502 while( (index=playlist_get_index_from_file(c, song->file))>=0 )
503 mpdclient_cmd_delete(c, index);
504 }
505 }
506 }
507 return 0;
508 }
510 static void
511 browse_init(WINDOW *w, int cols, int rows)
512 {
513 lw = list_window_init(w, cols, rows);
514 lw_state = list_window_init_state();
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( filelist )
528 filelist = mpdclient_filelist_free(filelist);
529 lw = list_window_free(lw);
530 lw_state = list_window_free_state(lw_state);
531 }
533 static void
534 browse_open(screen_t *screen, mpdclient_t *c)
535 {
536 if( filelist == NULL )
537 {
538 filelist = mpdclient_filelist_get(c, "");
539 mpdclient_install_playlist_callback(c, playlist_changed_callback);
540 mpdclient_install_browse_callback(c, file_changed_callback);
541 }
542 }
544 static void
545 browse_close(void)
546 {
547 }
549 static char *
550 browse_title(char *str, size_t size)
551 {
552 g_snprintf(str, size, _("Browse: %s"), basename(filelist->path));
553 return str;
554 }
556 static void
557 browse_paint(screen_t *screen, mpdclient_t *c)
558 {
559 lw->clear = 1;
561 list_window_paint(lw, browse_lw_callback, (void *) filelist);
562 wnoutrefresh(lw->w);
563 }
565 static void
566 browse_update(screen_t *screen, mpdclient_t *c)
567 {
568 if( filelist->updated )
569 {
570 browse_paint(screen, c);
571 filelist->updated = FALSE;
572 return;
573 }
574 list_window_paint(lw, browse_lw_callback, (void *) filelist);
575 wnoutrefresh(lw->w);
576 }
579 #ifdef HAVE_GETMOUSE
580 int
581 browse_handle_mouse_event(screen_t *screen,
582 mpdclient_t *c,
583 list_window_t *lw,
584 mpdclient_filelist_t *filelist)
585 {
586 int row;
587 int prev_selected = lw->selected;
588 unsigned long bstate;
589 int length;
591 if ( filelist )
592 length = filelist->length;
593 else
594 length = 0;
596 if( screen_get_mouse_event(c, lw, length, &bstate, &row) )
597 return 1;
599 lw->selected = lw->start+row;
600 list_window_check_selected(lw, length);
602 if( bstate & BUTTON1_CLICKED )
603 {
604 if( prev_selected == lw->selected )
605 browse_handle_enter(screen, c, lw, filelist);
606 }
607 else if( bstate & BUTTON3_CLICKED )
608 {
609 if( prev_selected == lw->selected )
610 browse_handle_select(screen, c, lw, filelist);
611 }
613 return 1;
614 }
615 #endif
617 static int
618 browse_cmd(screen_t *screen, mpdclient_t *c, command_t cmd)
619 {
620 switch(cmd)
621 {
622 case CMD_PLAY:
623 browse_handle_enter(screen, c, lw, filelist);
624 return 1;
625 case CMD_SELECT:
626 if( browse_handle_select(screen, c, lw, filelist) == 0 )
627 {
628 /* continue and select next item... */
629 cmd = CMD_LIST_NEXT;
630 }
631 break;
632 case CMD_DELETE:
633 handle_delete(screen, c);
634 break;
635 case CMD_SAVE_PLAYLIST:
636 handle_save(screen, c);
637 break;
638 case CMD_SCREEN_UPDATE:
639 screen->painted = 0;
640 lw->clear = 1;
641 lw->repaint = 1;
642 filelist = mpdclient_filelist_update(c, filelist);
643 list_window_check_selected(lw, filelist->length);
644 screen_status_printf(_("Screen updated!"));
645 return 1;
646 case CMD_DB_UPDATE:
647 if( !c->status->updatingDb )
648 {
649 if( mpdclient_cmd_db_update_utf8(c,filelist->path)==0 )
650 {
651 screen_status_printf(_("Database update of %s started!"),
652 filelist->path);
653 /* set updatingDb to make shure the browse callback gets called
654 * even if the updated has finished before status is updated */
655 c->status->updatingDb = 1;
656 }
657 }
658 else
659 screen_status_printf(_("Database update running..."));
660 return 1;
661 case CMD_LIST_FIND:
662 case CMD_LIST_RFIND:
663 case CMD_LIST_FIND_NEXT:
664 case CMD_LIST_RFIND_NEXT:
665 return screen_find(screen, c,
666 lw, filelist->length,
667 cmd, browse_lw_callback, (void *) filelist);
668 case CMD_MOUSE_EVENT:
669 return browse_handle_mouse_event(screen,c,lw,filelist);
670 default:
671 break;
672 }
673 return list_window_cmd(lw, filelist->length, cmd);
674 }
677 list_window_t *
678 get_filelist_window()
679 {
680 return lw;
681 }
686 screen_functions_t *
687 get_screen_browse(void)
688 {
689 static screen_functions_t functions;
691 memset(&functions, 0, sizeof(screen_functions_t));
692 functions.init = browse_init;
693 functions.exit = browse_exit;
694 functions.open = browse_open;
695 functions.close = browse_close;
696 functions.resize = browse_resize;
697 functions.paint = browse_paint;
698 functions.update = browse_update;
699 functions.cmd = browse_cmd;
700 functions.get_lw = get_filelist_window;
701 functions.get_title = browse_title;
703 return &functions;
704 }