1 /***************************************************************************
4 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
5 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
6 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
7 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
11 * Copyright (C) 2003 Hardeep Sidhu
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ****************************************************************************/
23 * Kevin Ferrare 2005/10/16
24 * multi-screen support, rewrote a lot of code
35 #include "filetypes.h"
41 #include "backlight.h"
45 #include "playlist_viewer.h"
46 #include "playlist_catalog.h"
50 #include "playlist_menu.h"
52 /* Maximum number of tracks we can have loaded at one time */
53 #define MAX_PLAYLIST_ENTRIES 200
55 /* The number of items between the selected one and the end/start of
56 * the buffer under which the buffer must reload */
57 #define MIN_BUFFER_MARGIN (screens[0].getnblines()+1)
59 /* Information about a specific track */
60 struct playlist_entry
{
61 char *name
; /* Formatted track name */
62 int index
; /* Playlist index */
63 int display_index
; /* Display index */
64 bool queued
; /* Is track queued? */
65 bool skipped
; /* Is track marked as bad? */
74 struct playlist_buffer
76 char *name_buffer
; /* Buffer used to store track names */
77 int buffer_size
; /* Size of name buffer */
79 int first_index
; /* Real index of first track loaded inside
82 enum direction direction
; /* Direction of the buffer (if the buffer
83 was loaded BACKWARD, the last track in
84 the buffer has a real index < to the
85 real index of the the first track)*/
87 struct playlist_entry tracks
[MAX_PLAYLIST_ENTRIES
];
88 int num_loaded
; /* Number of track entries loaded in buffer */
91 /* Global playlist viewer settings */
92 struct playlist_viewer
{
93 struct playlist_info
* playlist
; /* playlist being viewed */
94 int num_tracks
; /* Number of tracks in playlist */
95 int current_playing_track
; /* Index of current playing track */
96 int selected_track
; /* The selected track, relative (first is 0) */
97 int moving_track
; /* The track to move, relative (first is 0)
98 or -1 if nothing is currently being moved */
99 int moving_playlist_index
; /* Playlist-relative index (as opposed to
100 viewer-relative index) of moving track */
101 struct playlist_buffer buffer
;
104 static struct playlist_viewer viewer
;
106 /* Used when viewing playlists on disk */
107 static struct playlist_info temp_playlist
;
109 static void playlist_buffer_init(struct playlist_buffer
*pb
, char *names_buffer
,
110 int names_buffer_size
);
111 static void playlist_buffer_load_entries(struct playlist_buffer
* pb
, int index
,
112 enum direction direction
);
113 static int playlist_entry_load(struct playlist_entry
*entry
, int index
,
114 char* name_buffer
, int remaining_size
);
116 static struct playlist_entry
* playlist_buffer_get_track(struct playlist_buffer
*pb
,
119 static bool playlist_viewer_init(struct playlist_viewer
* viewer
,
120 const char* filename
, bool reload
);
122 static void format_name(char* dest
, const char* src
);
123 static void format_line(const struct playlist_entry
* track
, char* str
,
126 static bool update_playlist(bool force
);
127 static int onplay_menu(int index
);
128 static bool viewer_menu(void);
129 static int save_playlist_func(void);
131 static void playlist_buffer_init(struct playlist_buffer
*pb
, char *names_buffer
,
132 int names_buffer_size
)
134 pb
->name_buffer
=names_buffer
;
135 pb
->buffer_size
=names_buffer_size
;
141 * Loads the entries following 'index' in the playlist buffer
143 static void playlist_buffer_load_entries(struct playlist_buffer
*pb
, int index
,
144 enum direction direction
)
146 int num_entries
= viewer
.num_tracks
;
147 char* p
= pb
->name_buffer
;
148 int remaining
= pb
->buffer_size
;
151 pb
->first_index
= index
;
152 if (num_entries
> MAX_PLAYLIST_ENTRIES
)
153 num_entries
= MAX_PLAYLIST_ENTRIES
;
155 for(i
=0; i
<num_entries
; i
++)
157 int len
= playlist_entry_load(&(pb
->tracks
[i
]), index
, p
, remaining
);
160 /* Out of name buffer space */
168 if(direction
==FORWARD
)
172 index
+=viewer
.num_tracks
;
173 index
%=viewer
.num_tracks
;
175 pb
->direction
=direction
;
179 static void playlist_buffer_load_entries_screen(struct playlist_buffer
* pb
,
180 enum direction direction
)
182 if(direction
==FORWARD
)
184 int min_start
=viewer
.selected_track
-2*screens
[0].getnblines();
186 min_start
+=viewer
.num_tracks
;
187 min_start
%= viewer
.num_tracks
;
188 playlist_buffer_load_entries(pb
, min_start
, FORWARD
);
192 int max_start
=viewer
.selected_track
+2*screens
[0].getnblines();
193 max_start
%=viewer
.num_tracks
;
194 playlist_buffer_load_entries(pb
, max_start
, BACKWARD
);
198 static int playlist_entry_load(struct playlist_entry
*entry
, int index
,
199 char* name_buffer
, int remaining_size
)
201 struct playlist_track_info info
;
204 /* Playlist viewer orders songs based on display index. We need to
205 convert to real playlist index to access track */
206 index
= (index
+ playlist_get_first_index(viewer
.playlist
)) %
208 if (playlist_get_track_info(viewer
.playlist
, index
, &info
) < 0)
211 len
= strlen(info
.filename
) + 1;
213 if (len
<= remaining_size
)
215 strcpy(name_buffer
, info
.filename
);
217 entry
->name
= name_buffer
;
218 entry
->index
= info
.index
;
219 entry
->display_index
= info
.display_index
;
220 entry
->queued
= info
.attr
& PLAYLIST_ATTR_QUEUED
;
221 entry
->skipped
= info
.attr
& PLAYLIST_ATTR_SKIPPED
;
227 static int playlist_buffer_get_index(struct playlist_buffer
*pb
, int index
)
230 if(pb
->direction
==FORWARD
)
232 if(index
>=pb
->first_index
)
233 buffer_index
=index
-pb
->first_index
;
234 else /* rotation : track0 in buffer + requested track */
235 buffer_index
=(viewer
.num_tracks
-pb
->first_index
)+(index
);
239 if(index
<=pb
->first_index
)
240 buffer_index
=pb
->first_index
-index
;
241 else /* rotation : track0 in buffer + dist from the last track
242 to the requested track (num_tracks-requested track) */
243 buffer_index
=(pb
->first_index
)+(viewer
.num_tracks
-index
);
245 return(buffer_index
);
248 #define distance(a, b) \
249 a>b? (a) - (b) : (b) - (a)
250 static bool playlist_buffer_needs_reload(struct playlist_buffer
* pb
,
253 if(pb
->num_loaded
==viewer
.num_tracks
)
255 int selected_index
=playlist_buffer_get_index(pb
, track_index
);
256 int first_buffer_index
=playlist_buffer_get_index(pb
, pb
->first_index
);
257 int distance_beginning
=distance(selected_index
, first_buffer_index
);
258 if(distance_beginning
<MIN_BUFFER_MARGIN
)
261 if(pb
->num_loaded
- distance_beginning
< MIN_BUFFER_MARGIN
)
266 static struct playlist_entry
* playlist_buffer_get_track(struct playlist_buffer
*pb
,
269 int buffer_index
=playlist_buffer_get_index(pb
, index
);
270 return(&(pb
->tracks
[buffer_index
]));
273 /* Initialize the playlist viewer. */
274 static bool playlist_viewer_init(struct playlist_viewer
* viewer
,
275 const char* filename
, bool reload
)
279 bool is_playing
= audio_status() & (AUDIO_STATUS_PLAY
| AUDIO_STATUS_PAUSE
);
280 bool have_list
= filename
|| is_playing
;
281 if (!have_list
&& (global_status
.resume_index
!= -1))
283 /* Try to restore the list from control file */
284 have_list
= (playlist_resume() != -1);
286 if (!have_list
&& (playlist_amount() > 0))
288 /*If dynamic playlist still exists, view it anyway even
289 if playback has reached the end of the playlist */
294 /* Nothing to view, exit */
295 splash(HZ
, str(LANG_CATALOG_NO_PLAYLISTS
));
299 buffer
= plugin_get_buffer(&buffer_size
);
304 viewer
->playlist
= NULL
;
307 /* Viewing playlist on disk */
308 const char *dir
, *file
;
310 char *index_buffer
= NULL
;
311 ssize_t index_buffer_size
= 0;
313 viewer
->playlist
= &temp_playlist
;
315 /* Separate directory from filename */
316 temp_ptr
= strrchr(filename
+1,'/');
331 /* Something is playing, use half the plugin buffer for playlist
333 index_buffer_size
= buffer_size
/ 2;
334 index_buffer
= buffer
;
337 playlist_create_ex(viewer
->playlist
, dir
, file
, index_buffer
,
338 index_buffer_size
, buffer
+index_buffer_size
,
339 buffer_size
-index_buffer_size
);
344 buffer
+= index_buffer_size
;
345 buffer_size
-= index_buffer_size
;
347 playlist_buffer_init(&viewer
->buffer
, buffer
, buffer_size
);
349 viewer
->moving_track
= -1;
350 viewer
->moving_playlist_index
= -1;
354 if (viewer
->playlist
)
355 viewer
->selected_track
= 0;
357 viewer
->selected_track
= playlist_get_display_index() - 1;
360 if (!update_playlist(true))
365 /* Format trackname for display purposes */
366 static void format_name(char* dest
, const char* src
)
368 switch (global_settings
.playlist_viewer_track_display
)
373 /* Only display the filename */
374 char* p
= strrchr(src
, '/');
378 /* Remove the extension */
390 /* Format display line */
391 static void format_line(const struct playlist_entry
* track
, char* str
,
397 format_name(name
, track
->name
);
402 if (global_settings
.playlist_viewer_indices
)
403 /* Display playlist index */
404 snprintf(str
, len
, "%d. %s%s", track
->display_index
, skipped
, name
);
406 snprintf(str
, len
, "%s%s", skipped
, name
);
410 /* Update playlist in case something has changed or forced */
411 static bool update_playlist(bool force
)
413 if (!viewer
.playlist
)
414 playlist_get_resume_info(&viewer
.current_playing_track
);
416 viewer
.current_playing_track
= -1;
417 int nb_tracks
=playlist_amount_ex(viewer
.playlist
);
418 force
=force
|| nb_tracks
!= viewer
.num_tracks
;
422 viewer
.num_tracks
= nb_tracks
;
423 if (viewer
.num_tracks
<= 0)
425 global_status
.resume_index
= -1;
426 global_status
.resume_offset
= -1;
429 playlist_buffer_load_entries_screen(&viewer
.buffer
, FORWARD
);
430 if (viewer
.buffer
.num_loaded
<= 0)
432 global_status
.resume_index
= -1;
433 global_status
.resume_offset
= -1;
440 /* Menu of playlist commands. Invoked via ON+PLAY on main viewer screen.
441 Returns -1 if USB attached, 0 if no playlist change, and 1 if playlist
443 static int onplay_menu(int index
)
446 struct playlist_entry
* current_track
=
447 playlist_buffer_get_track(&viewer
.buffer
, index
);
448 MENUITEM_STRINGLIST(menu_items
, ID2P(LANG_PLAYLIST
), NULL
,
449 ID2P(LANG_REMOVE
), ID2P(LANG_MOVE
),
450 ID2P(LANG_CATALOG_ADD_TO
), ID2P(LANG_CATALOG_ADD_TO_NEW
),
451 ID2P(LANG_PLAYLISTVIEWER_SETTINGS
));
452 bool current
= (current_track
->index
== viewer
.current_playing_track
);
454 result
= do_menu(&menu_items
, NULL
, NULL
, false);
455 if (result
== MENU_ATTACHED_USB
)
459 else if (result
>= 0)
461 /* Abort current move */
462 viewer
.moving_track
= -1;
463 viewer
.moving_playlist_index
= -1;
469 playlist_delete(viewer
.playlist
, current_track
->index
);
472 if (playlist_amount_ex(viewer
.playlist
) <= 0)
476 /* Start playing new track except if it's the lasttrack
477 track in the playlist and repeat mode is disabled */
479 playlist_buffer_get_track(&viewer
.buffer
, index
);
480 if (current_track
->display_index
!=viewer
.num_tracks
||
481 global_settings
.repeat_mode
== REPEAT_ALL
)
483 #if CONFIG_CODEC != SWCODEC
484 talk_buffer_steal(); /* will use the mp3 buffer */
487 viewer
.current_playing_track
= -1;
495 viewer
.moving_track
= index
;
496 viewer
.moving_playlist_index
= current_track
->index
;
499 case 2: /* add to catalog */
500 case 3: /* add to a new one */
501 catalog_add_to_a_playlist(current_track
->name
,
506 case 4: /* playlist viewer settings */
507 /* true on usb connect */
508 ret
= viewer_menu() ? -1 : 0;
515 /* Menu of viewer options. Invoked via F1(r) or Menu(p). */
516 MENUITEM_SETTING(show_icons
, &global_settings
.playlist_viewer_icons
, NULL
);
517 MENUITEM_SETTING(show_indices
, &global_settings
.playlist_viewer_indices
, NULL
);
518 MENUITEM_SETTING(track_display
,
519 &global_settings
.playlist_viewer_track_display
, NULL
);
520 MENUITEM_FUNCTION(save_playlist_item
, 0, ID2P(LANG_SAVE_DYNAMIC_PLAYLIST
),
521 save_playlist_func
, 0, NULL
, Icon_NOICON
);
522 MAKE_MENU(viewer_settings_menu
, ID2P(LANG_PLAYLISTVIEWER_SETTINGS
),
524 &show_icons
, &show_indices
, &track_display
, &save_playlist_item
);
525 static bool viewer_menu(void)
527 return do_menu(&viewer_settings_menu
, NULL
, NULL
, false) == MENU_ATTACHED_USB
;
530 /* Save playlist to disk */
531 static int save_playlist_func(void)
533 save_playlist_screen(viewer
.playlist
);
537 /* View current playlist */
538 enum playlist_viewer_result
playlist_viewer(void)
540 return playlist_viewer_ex(NULL
);
543 static int get_track_num( struct playlist_viewer
* local_viewer
,
546 if( local_viewer
->moving_track
>= 0 )
548 if( local_viewer
->selected_track
== selected_item
)
550 return local_viewer
->moving_track
;
552 else if( local_viewer
->selected_track
> selected_item
553 && selected_item
>= local_viewer
->moving_track
)
555 return selected_item
+1; /* move down */
557 else if( local_viewer
->selected_track
< selected_item
558 && selected_item
<= local_viewer
->moving_track
)
560 return selected_item
-1; /* move up */
563 return selected_item
;
566 static const char* playlist_callback_name(int selected_item
,
571 struct playlist_viewer
* local_viewer
= (struct playlist_viewer
*)data
;
573 int track_num
= get_track_num(local_viewer
, selected_item
);
574 struct playlist_entry
*track
=
575 playlist_buffer_get_track(&(local_viewer
->buffer
), track_num
);
577 format_line(track
, buffer
, buffer_len
);
583 static enum themable_icons
playlist_callback_icons(int selected_item
,
586 struct playlist_viewer
* local_viewer
=(struct playlist_viewer
*)data
;
588 int track_num
= get_track_num(local_viewer
, selected_item
);
589 struct playlist_entry
*track
=
590 playlist_buffer_get_track(&(local_viewer
->buffer
), track_num
);
592 if (track
->index
== local_viewer
->current_playing_track
)
594 /* Current playing track */
597 else if (track
->index
== local_viewer
->moving_playlist_index
)
599 /* Track we are moving */
602 else if (track
->queued
)
611 /* Main viewer function. Filename identifies playlist to be viewed. If NULL,
612 view current playlist. */
613 enum playlist_viewer_result
playlist_viewer_ex(const char* filename
)
615 enum playlist_viewer_result ret
= PLAYLIST_VIEWER_OK
;
616 bool exit
=false; /* exit viewer */
618 struct gui_synclist playlist_lists
;
619 if (!playlist_viewer_init(&viewer
, filename
, false))
622 gui_synclist_init(&playlist_lists
, playlist_callback_name
,
623 &viewer
, false, 1, NULL
);
624 gui_synclist_set_icon_callback(&playlist_lists
,
625 global_settings
.playlist_viewer_icons
?
626 &playlist_callback_icons
:NULL
);
627 gui_synclist_set_nb_items(&playlist_lists
, viewer
.num_tracks
);
628 gui_synclist_set_title(&playlist_lists
, str(LANG_PLAYLIST
), Icon_Playlist
);
629 gui_synclist_select_item(&playlist_lists
, viewer
.selected_track
);
630 gui_synclist_draw(&playlist_lists
);
635 if (global_status
.resume_index
!= -1 && !viewer
.playlist
)
636 playlist_get_resume_info(&track
);
640 if (track
!= viewer
.current_playing_track
||
641 playlist_amount_ex(viewer
.playlist
) != viewer
.num_tracks
)
643 /* Playlist has changed (new track started?) */
644 if (!update_playlist(false))
646 /*Needed because update_playlist gives wrong value when
648 viewer
.current_playing_track
= track
;
649 gui_synclist_set_nb_items(&playlist_lists
, viewer
.num_tracks
);
651 gui_synclist_draw(&playlist_lists
);
654 /* Timeout so we can determine if play status has changed */
655 bool res
= list_do_action(CONTEXT_TREE
, HZ
/2,
656 &playlist_lists
, &button
, LIST_WRAP_UNLESS_HELD
);
657 /* during moving, another redraw is going to be needed,
658 * since viewer.selected_track is updated too late (after the first draw)
659 * drawing the moving item needs it */
660 viewer
.selected_track
=gui_synclist_get_sel_pos(&playlist_lists
);
663 bool reload
= playlist_buffer_needs_reload(&viewer
.buffer
,
664 viewer
.selected_track
);
666 playlist_buffer_load_entries_screen(&viewer
.buffer
,
667 button
== ACTION_STD_NEXT
? FORWARD
: BACKWARD
);
668 if (reload
|| viewer
.moving_track
>= 0)
669 gui_synclist_draw(&playlist_lists
);
673 case ACTION_TREE_WPS
:
674 case ACTION_STD_CANCEL
:
676 if (viewer
.moving_track
>= 0)
678 viewer
.selected_track
= viewer
.moving_track
;
679 gui_synclist_select_item(&playlist_lists
, viewer
.moving_track
);
680 viewer
.moving_track
= -1;
681 viewer
.moving_playlist_index
= -1;
682 gui_synclist_draw(&playlist_lists
);
690 struct playlist_entry
* current_track
=
691 playlist_buffer_get_track(&viewer
.buffer
,
692 viewer
.selected_track
);
694 if (viewer
.moving_track
>= 0)
699 ret_val
= playlist_move(viewer
.playlist
,
700 viewer
.moving_playlist_index
,
701 current_track
->index
);
703 splashf(HZ
, (unsigned char *)"%s %s", str(LANG_MOVE
),
705 update_playlist(true);
706 viewer
.moving_track
= -1;
707 viewer
.moving_playlist_index
= -1;
709 else if (!viewer
.playlist
)
712 playlist_start(current_track
->index
, 0);
713 update_playlist(false);
718 if (playlist_set_current(viewer
.playlist
) < 0)
721 playlist_start(current_track
->index
, 0);
723 /* Our playlist is now the current list */
724 if (!playlist_viewer_init(&viewer
, NULL
, true))
727 gui_synclist_draw(&playlist_lists
);
731 case ACTION_STD_CONTEXT
:
736 ret_val
= onplay_menu(viewer
.selected_track
);
740 ret
= PLAYLIST_VIEWER_USB
;
743 else if (ret_val
> 0)
745 /* Playlist changed */
746 gui_synclist_del_item(&playlist_lists
);
747 update_playlist(true);
748 if (viewer
.num_tracks
<= 0)
750 if(viewer
.selected_track
>= viewer
.num_tracks
)
751 viewer
.selected_track
= viewer
.num_tracks
-1;
753 gui_synclist_draw(&playlist_lists
);
756 case ACTION_STD_MENU
:
757 ret
= PLAYLIST_VIEWER_MAINMENU
;
760 if(default_event_handler(button
) == SYS_USB_CONNECTED
)
762 ret
= PLAYLIST_VIEWER_USB
;
771 playlist_close(viewer
.playlist
);
775 static const char* playlist_search_callback_name(int selected_item
, void * data
,
776 char *buffer
, size_t buffer_len
)
778 (void)buffer_len
; /* this should probably be used */
779 int *found_indicies
= (int*)data
;
780 static struct playlist_track_info track
;
781 playlist_get_track_info(viewer
.playlist
, found_indicies
[selected_item
], &track
);
782 format_name(buffer
, track
.filename
);
786 bool search_playlist(void)
788 char search_str
[32] = "";
789 bool ret
= false, exit
= false;
790 int i
, playlist_count
;
791 int found_indicies
[MAX_PLAYLIST_ENTRIES
];
792 int found_indicies_count
= 0, last_found_count
= -1;
794 struct gui_synclist playlist_lists
;
795 struct playlist_track_info track
;
797 if (!playlist_viewer_init(&viewer
, 0, false))
799 if (kbd_input(search_str
, sizeof(search_str
)) < 0)
802 playlist_count
= playlist_amount_ex(viewer
.playlist
);
806 for (i
=0; (i
<playlist_count
)&&(found_indicies_count
<MAX_PLAYLIST_ENTRIES
); i
++)
808 if (found_indicies_count
!= last_found_count
)
810 splashf(0, str(LANG_PLAYLIST_SEARCH_MSG
), found_indicies_count
,
811 str(LANG_OFF_ABORT
));
812 last_found_count
= found_indicies_count
;
815 if (action_userabort(TIMEOUT_NOBLOCK
))
818 playlist_get_track_info(viewer
.playlist
, i
, &track
);
820 if (strcasestr(track
.filename
,search_str
))
821 found_indicies
[found_indicies_count
++] = track
.index
;
828 if (!found_indicies_count
)
834 gui_synclist_init(&playlist_lists
, playlist_search_callback_name
,
835 found_indicies
, false, 1, NULL
);
836 gui_synclist_set_title(&playlist_lists
, str(LANG_SEARCH_RESULTS
), NOICON
);
837 gui_synclist_set_icon_callback(&playlist_lists
, NULL
);
838 gui_synclist_set_nb_items(&playlist_lists
, found_indicies_count
);
839 gui_synclist_select_item(&playlist_lists
, 0);
840 gui_synclist_draw(&playlist_lists
);
843 if (list_do_action(CONTEXT_LIST
, HZ
/4,
844 &playlist_lists
, &button
, LIST_WRAP_UNLESS_HELD
))
848 case ACTION_STD_CANCEL
:
854 int sel
= gui_synclist_get_sel_pos(&playlist_lists
);
855 playlist_start(found_indicies
[sel
] ,0);
861 if(default_event_handler(button
) == SYS_USB_CONNECTED
)