1 /***************************************************************************
4 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
5 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
6 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
7 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
11 * Copyright (C) 2003 Hardeep Sidhu
13 * All files in this archive are subject to the GNU General Public License.
14 * See the file COPYING in the source tree root for full license agreement.
16 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17 * KIND, either express or implied.
19 ****************************************************************************/
21 * Kevin Ferrare 2005/10/16
22 * 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"
49 #include "statusbar.h"
51 #include "playlist_menu.h"
54 /* Maximum number of tracks we can have loaded at one time */
55 #define MAX_PLAYLIST_ENTRIES 200
57 /* The number of items between the selected one and the end/start of
58 * the buffer under which the buffer must reload */
59 #define MIN_BUFFER_MARGIN (screens[0].nb_lines+1)
61 /* Information about a specific track */
62 struct playlist_entry
{
63 char *name
; /* Formatted track name */
64 int index
; /* Playlist index */
65 int display_index
; /* Display index */
66 bool queued
; /* Is track queued? */
67 bool skipped
; /* Is track marked as bad? */
76 struct playlist_buffer
78 char *name_buffer
; /* Buffer used to store track names */
79 int buffer_size
; /* Size of name buffer */
81 int first_index
; /* Real index of first track loaded inside
84 enum direction direction
; /* Direction of the buffer (if the buffer
85 was loaded BACKWARD, the last track in
86 the buffer has a real index < to the
87 real index of the the first track)*/
89 struct playlist_entry tracks
[MAX_PLAYLIST_ENTRIES
];
90 int num_loaded
; /* Number of track entries loaded in buffer */
93 /* Global playlist viewer settings */
94 struct playlist_viewer
{
95 struct playlist_info
* playlist
; /* playlist being viewed */
96 int num_tracks
; /* Number of tracks in playlist */
97 int current_playing_track
; /* Index of current playing track */
98 int selected_track
; /* The selected track, relative (first is 0)*/
99 int move_track
; /* Playlist index of track to move or -1 */
100 struct playlist_buffer buffer
;
103 static struct playlist_viewer viewer
;
105 /* Used when viewing playlists on disk */
106 static struct playlist_info temp_playlist
;
108 static void playlist_buffer_init(struct playlist_buffer
*pb
, char *names_buffer
,
109 int names_buffer_size
);
110 static void playlist_buffer_load_entries(struct playlist_buffer
* pb
, int index
,
111 enum direction direction
);
112 static int playlist_entry_load(struct playlist_entry
*entry
, int index
,
113 char* name_buffer
, int remaining_size
);
115 static struct playlist_entry
* playlist_buffer_get_track(struct playlist_buffer
*pb
,
118 static bool playlist_viewer_init(struct playlist_viewer
* viewer
,
119 char* filename
, bool reload
);
121 static void format_name(char* dest
, const char* src
);
122 static void format_line(const struct playlist_entry
* track
, char* str
,
125 static bool update_playlist(bool force
);
126 static int onplay_menu(int index
);
127 static bool viewer_menu(void);
128 static int save_playlist_func(void);
130 static void playlist_buffer_init(struct playlist_buffer
*pb
, char *names_buffer
,
131 int names_buffer_size
)
133 pb
->name_buffer
=names_buffer
;
134 pb
->buffer_size
=names_buffer_size
;
140 * Loads the entries following 'index' in the playlist buffer
142 static void playlist_buffer_load_entries(struct playlist_buffer
*pb
, int index
,
143 enum direction direction
)
145 int num_entries
= viewer
.num_tracks
;
146 char* p
= pb
->name_buffer
;
147 int remaining
= pb
->buffer_size
;
150 pb
->first_index
= index
;
151 if (num_entries
> MAX_PLAYLIST_ENTRIES
)
152 num_entries
= MAX_PLAYLIST_ENTRIES
;
154 for(i
=0; i
<num_entries
; i
++)
156 int len
= playlist_entry_load(&(pb
->tracks
[i
]), index
, p
, remaining
);
159 /* Out of name buffer space */
167 if(direction
==FORWARD
)
171 index
+=viewer
.num_tracks
;
172 index
%=viewer
.num_tracks
;
174 pb
->direction
=direction
;
178 static void playlist_buffer_load_entries_screen(struct playlist_buffer
* pb
,
179 enum direction direction
)
181 if(direction
==FORWARD
)
183 int min_start
=viewer
.selected_track
-2*screens
[0].nb_lines
;
185 min_start
+=viewer
.num_tracks
;
186 min_start
%= viewer
.num_tracks
;
187 playlist_buffer_load_entries(pb
, min_start
, FORWARD
);
191 int max_start
=viewer
.selected_track
+2*screens
[0].nb_lines
;
192 max_start
%=viewer
.num_tracks
;
193 playlist_buffer_load_entries(pb
, max_start
, BACKWARD
);
197 static int playlist_entry_load(struct playlist_entry
*entry
, int index
,
198 char* name_buffer
, int remaining_size
)
200 struct playlist_track_info info
;
203 /* Playlist viewer orders songs based on display index. We need to
204 convert to real playlist index to access track */
205 index
= (index
+ playlist_get_first_index(viewer
.playlist
)) %
207 if (playlist_get_track_info(viewer
.playlist
, index
, &info
) < 0)
210 len
= strlen(info
.filename
) + 1;
212 if (len
<= remaining_size
)
214 strcpy(name_buffer
, info
.filename
);
216 entry
->name
= name_buffer
;
217 entry
->index
= info
.index
;
218 entry
->display_index
= info
.display_index
;
219 entry
->queued
= info
.attr
& PLAYLIST_ATTR_QUEUED
;
220 entry
->skipped
= info
.attr
& PLAYLIST_ATTR_SKIPPED
;
226 static int playlist_buffer_get_index(struct playlist_buffer
*pb
, int index
)
229 if(pb
->direction
==FORWARD
)
231 if(index
>=pb
->first_index
)
232 buffer_index
=index
-pb
->first_index
;
233 else /* rotation : track0 in buffer + requested track */
234 buffer_index
=(viewer
.num_tracks
-pb
->first_index
)+(index
);
238 if(index
<=pb
->first_index
)
239 buffer_index
=pb
->first_index
-index
;
240 else /* rotation : track0 in buffer + dist from the last track
241 to the requested track (num_tracks-requested track) */
242 buffer_index
=(pb
->first_index
)+(viewer
.num_tracks
-index
);
244 return(buffer_index
);
247 #define distance(a, b) \
248 a>b? (a) - (b) : (b) - (a)
249 static bool playlist_buffer_needs_reload(struct playlist_buffer
* pb
, int track_index
)
251 if(pb
->num_loaded
==viewer
.num_tracks
)
253 int selected_index
=playlist_buffer_get_index(pb
, track_index
);
254 int first_buffer_index
=playlist_buffer_get_index(pb
, pb
->first_index
);
255 int distance_beginning
=distance(selected_index
, first_buffer_index
);
256 if(distance_beginning
<MIN_BUFFER_MARGIN
)
259 if(pb
->num_loaded
- distance_beginning
< MIN_BUFFER_MARGIN
)
264 static struct playlist_entry
* playlist_buffer_get_track(struct playlist_buffer
*pb
,
267 int buffer_index
=playlist_buffer_get_index(pb
, index
);
268 return(&(pb
->tracks
[buffer_index
]));
271 /* Initialize the playlist viewer. */
272 static bool playlist_viewer_init(struct playlist_viewer
* viewer
,
273 char* filename
, bool reload
)
277 bool is_playing
= audio_status() & (AUDIO_STATUS_PLAY
| AUDIO_STATUS_PAUSE
);
278 bool have_list
= filename
|| is_playing
;
279 if (!have_list
&& (global_status
.resume_index
!= -1))
281 /* Try to restore the list from control file */
282 have_list
= (playlist_resume() != -1);
286 /* Nothing to view, exit */
287 gui_syncsplash(HZ
, str(LANG_CATALOG_NO_PLAYLISTS
));
291 buffer
= plugin_get_buffer(&buffer_size
);
296 viewer
->playlist
= NULL
;
299 /* Viewing playlist on disk */
300 char *dir
, *file
, *temp_ptr
;
301 char *index_buffer
= NULL
;
302 ssize_t index_buffer_size
= 0;
304 viewer
->playlist
= &temp_playlist
;
306 /* Separate directory from filename */
307 temp_ptr
= strrchr(filename
+1,'/');
322 /* Something is playing, use half the plugin buffer for playlist
324 index_buffer_size
= buffer_size
/ 2;
325 index_buffer
= buffer
;
328 playlist_create_ex(viewer
->playlist
, dir
, file
, index_buffer
,
329 index_buffer_size
, buffer
+index_buffer_size
,
330 buffer_size
-index_buffer_size
);
335 buffer
+= index_buffer_size
;
336 buffer_size
-= index_buffer_size
;
338 playlist_buffer_init(&viewer
->buffer
, buffer
, buffer_size
);
340 viewer
->move_track
= -1;
344 if (viewer
->playlist
)
345 viewer
->selected_track
= 0;
347 viewer
->selected_track
= playlist_get_display_index() - 1;
350 if (!update_playlist(true))
355 /* Format trackname for display purposes */
356 static void format_name(char* dest
, const char* src
)
358 switch (global_settings
.playlist_viewer_track_display
)
363 /* Only display the filename */
364 char* p
= strrchr(src
, '/');
368 /* Remove the extension */
369 char* q
= strrchr(dest
, '.');
383 /* Format display line */
384 static void format_line(const struct playlist_entry
* track
, char* str
,
390 format_name(name
, track
->name
);
395 if (global_settings
.playlist_viewer_indices
)
396 /* Display playlist index */
397 snprintf(str
, len
, "%d. %s%s", track
->display_index
, skipped
, name
);
399 snprintf(str
, len
, "%s%s", skipped
, name
);
403 /* Update playlist in case something has changed or forced */
404 static bool update_playlist(bool force
)
406 if (!viewer
.playlist
)
407 playlist_get_resume_info(&viewer
.current_playing_track
);
409 viewer
.current_playing_track
= -1;
410 int nb_tracks
=playlist_amount_ex(viewer
.playlist
);
411 force
=force
|| nb_tracks
!= viewer
.num_tracks
;
415 viewer
.num_tracks
= nb_tracks
;
416 if (viewer
.num_tracks
<= 0)
418 playlist_buffer_load_entries_screen(&viewer
.buffer
, FORWARD
);
419 if (viewer
.buffer
.num_loaded
<= 0)
425 /* Menu of playlist commands. Invoked via ON+PLAY on main viewer screen.
426 Returns -1 if USB attached, 0 if no playlist change, and 1 if playlist
428 static int onplay_menu(int index
)
431 struct playlist_entry
* current_track
=
432 playlist_buffer_get_track(&viewer
.buffer
, index
);
433 MENUITEM_STRINGLIST(menu_items
, ID2P(LANG_PLAYLIST_MENU
), NULL
,
434 ID2P(LANG_REMOVE
), ID2P(LANG_MOVE
),
435 ID2P(LANG_CATALOG_ADD_TO
), ID2P(LANG_CATALOG_ADD_TO_NEW
));
436 bool current
= (current_track
->index
== viewer
.current_playing_track
);
438 result
= do_menu(&menu_items
, NULL
);
439 if (result
== MENU_ATTACHED_USB
)
443 else if (result
>= 0)
445 /* Abort current move */
446 viewer
.move_track
= -1;
452 playlist_delete(viewer
.playlist
, current_track
->index
);
455 if (playlist_amount_ex(viewer
.playlist
) <= 0)
459 /* Start playing new track except if it's the lasttrack
460 track in the playlist and repeat mode is disabled */
462 playlist_buffer_get_track(&viewer
.buffer
, index
);
463 if (current_track
->display_index
!=viewer
.num_tracks
||
464 global_settings
.repeat_mode
== REPEAT_ALL
)
466 #if CONFIG_CODEC != SWCODEC
467 talk_buffer_steal(); /* will use the mp3 buffer */
470 viewer
.current_playing_track
= -1;
478 viewer
.move_track
= current_track
->index
;
481 case 2: /* add to catalog */
482 case 3: /* add to a new one */
483 catalog_add_to_a_playlist(current_track
->name
,
493 /* Menu of viewer options. Invoked via F1(r) or Menu(p). */
494 MENUITEM_SETTING(show_icons
, &global_settings
.playlist_viewer_icons
, NULL
);
495 MENUITEM_SETTING(show_indices
, &global_settings
.playlist_viewer_indices
, NULL
);
496 MENUITEM_SETTING(track_display
,
497 &global_settings
.playlist_viewer_track_display
, NULL
);
498 MENUITEM_FUNCTION(save_playlist_item
, 0, ID2P(LANG_SAVE_DYNAMIC_PLAYLIST
),
499 save_playlist_func
, 0, NULL
, Icon_NOICON
);
500 MAKE_MENU(viewer_settings_menu
, ID2P(LANG_PLAYLISTVIEWER_SETTINGS
),
502 &show_icons
, &show_indices
, &track_display
, &save_playlist_item
);
503 static bool viewer_menu(void)
505 return do_menu(&viewer_settings_menu
, NULL
) == MENU_ATTACHED_USB
;
508 /* Save playlist to disk */
509 static int save_playlist_func(void)
511 save_playlist_screen(viewer
.playlist
);
515 /* View current playlist */
516 bool playlist_viewer(void)
518 return playlist_viewer_ex(NULL
);
521 static char *playlist_callback_name(int selected_item
, void *data
, char *buffer
)
523 struct playlist_viewer
* local_viewer
= (struct playlist_viewer
*)data
;
524 struct playlist_entry
*track
=
525 playlist_buffer_get_track(&(local_viewer
->buffer
), selected_item
);
526 format_line(track
, buffer
, MAX_PATH
);
531 static int playlist_callback_icons(int selected_item
, void *data
)
533 struct playlist_viewer
* local_viewer
=(struct playlist_viewer
*)data
;
534 struct playlist_entry
*track
=
535 playlist_buffer_get_track(&(local_viewer
->buffer
), selected_item
);
536 if (track
->index
== local_viewer
->current_playing_track
)
538 /* Current playing track */
541 else if (track
->index
== local_viewer
->move_track
)
543 /* Track we are moving */
546 else if (track
->queued
)
555 /* Main viewer function. Filename identifies playlist to be viewed. If NULL,
556 view current playlist. */
557 bool playlist_viewer_ex(char* filename
)
559 bool ret
= false; /* return value */
560 bool exit
=false; /* exit viewer */
562 struct gui_synclist playlist_lists
;
563 if (!playlist_viewer_init(&viewer
, filename
, false))
566 gui_synclist_init(&playlist_lists
, playlist_callback_name
, &viewer
, false, 1);
567 gui_synclist_set_icon_callback(&playlist_lists
,
568 global_settings
.playlist_viewer_icons
?
569 &playlist_callback_icons
:NULL
);
570 gui_synclist_set_nb_items(&playlist_lists
, viewer
.num_tracks
);
571 gui_synclist_select_item(&playlist_lists
, viewer
.selected_track
);
572 gui_synclist_set_title(&playlist_lists
, str(LANG_PLAYLIST_MENU
), Icon_Playlist
);
573 gui_synclist_draw(&playlist_lists
);
577 if (global_status
.resume_index
== -1)
579 /* Play has stopped */
580 gui_syncsplash(HZ
, str(LANG_END_PLAYLIST_RECORDER
));
584 if (viewer
.move_track
!= -1)
585 gui_synclist_flash(&playlist_lists
);
587 if (!viewer
.playlist
)
588 playlist_get_resume_info(&track
);
592 if (track
!= viewer
.current_playing_track
||
593 playlist_amount_ex(viewer
.playlist
) != viewer
.num_tracks
)
595 /* Playlist has changed (new track started?) */
596 if (!update_playlist(false))
598 gui_synclist_set_nb_items(&playlist_lists
, viewer
.num_tracks
);
599 /* Abort move on playlist change */
600 viewer
.move_track
= -1;
601 gui_synclist_draw(&playlist_lists
);
604 /* Timeout so we can determine if play status has changed */
605 button
= get_action(CONTEXT_TREE
,HZ
/2);
607 if( (list_action
=gui_synclist_do_button(&playlist_lists
, button
,LIST_WRAP_UNLESS_HELD
))!=0 )
609 viewer
.selected_track
=gui_synclist_get_sel_pos(&playlist_lists
);
610 if(playlist_buffer_needs_reload(&viewer
.buffer
,
611 viewer
.selected_track
))
612 playlist_buffer_load_entries_screen(&viewer
.buffer
,
613 list_action
==ACTION_STD_NEXT
?
621 case ACTION_TREE_WPS
:
622 case ACTION_STD_CANCEL
:
627 struct playlist_entry
* current_track
=
628 playlist_buffer_get_track(&viewer
.buffer
,
629 viewer
.selected_track
);
630 if (viewer
.move_track
>= 0)
635 ret_val
= playlist_move(viewer
.playlist
, viewer
.move_track
,
636 current_track
->index
);
638 gui_syncsplash(HZ
, str(LANG_MOVE_FAILED
));
640 update_playlist(true);
641 viewer
.move_track
= -1;
643 else if (!viewer
.playlist
)
646 playlist_start(current_track
->index
, 0);
647 update_playlist(false);
652 if (playlist_set_current(viewer
.playlist
) < 0)
655 playlist_start(current_track
->index
, 0);
657 /* Our playlist is now the current list */
658 if (!playlist_viewer_init(&viewer
, NULL
, true))
661 gui_synclist_draw(&playlist_lists
);
665 case ACTION_STD_CONTEXT
:
670 ret_val
= onplay_menu(viewer
.selected_track
);
679 /* Playlist changed */
680 gui_synclist_del_item(&playlist_lists
);
681 update_playlist(true);
682 if (viewer
.num_tracks
<= 0)
684 if(viewer
.selected_track
>= viewer
.num_tracks
)
685 viewer
.selected_track
= viewer
.num_tracks
-1;
687 gui_synclist_draw(&playlist_lists
);
690 case ACTION_STD_MENU
:
696 gui_synclist_set_icon_callback(
698 global_settings
.playlist_viewer_icons
?
699 &playlist_callback_icons
:NULL
701 gui_synclist_draw(&playlist_lists
);
705 gui_syncstatusbar_draw(&statusbars
, false);
709 if(default_event_handler(button
) == SYS_USB_CONNECTED
)
720 playlist_close(viewer
.playlist
);
724 static char *playlist_search_callback_name(int selected_item
, void * data
, char *buffer
)
726 int *found_indicies
= (int*)data
;
727 static struct playlist_track_info track
;
728 playlist_get_track_info(viewer
.playlist
,found_indicies
[selected_item
],&track
);
729 format_name(buffer
,track
.filename
);
733 bool search_playlist(void)
735 char search_str
[32] = "";
736 bool ret
= false, exit
= false;
737 int i
, playlist_count
;
738 int found_indicies
[MAX_PLAYLIST_ENTRIES
],found_indicies_count
= 0;
740 struct gui_synclist playlist_lists
;
741 struct playlist_track_info track
;
743 if (!playlist_viewer_init(&viewer
, 0, false))
745 if (kbd_input(search_str
,sizeof(search_str
)) == -1)
748 playlist_count
= playlist_amount_ex(viewer
.playlist
);
749 for (i
=0;(i
<playlist_count
)&&(found_indicies_count
<MAX_PLAYLIST_ENTRIES
);i
++)
751 gui_syncsplash(0, str(LANG_PLAYLIST_SEARCH_MSG
),found_indicies_count
,
752 #if CONFIG_KEYPAD == PLAYER_PAD
758 if (action_userabort(TIMEOUT_NOBLOCK
))
760 playlist_get_track_info(viewer
.playlist
,i
,&track
);
761 if (strcasestr(track
.filename
,search_str
))
763 found_indicies
[found_indicies_count
++] = track
.index
;
766 if (!found_indicies_count
)
771 gui_synclist_init(&playlist_lists
, playlist_search_callback_name
,
772 found_indicies
, false, 1);
773 gui_synclist_set_icon_callback(&playlist_lists
, NULL
);
774 gui_synclist_set_nb_items(&playlist_lists
, found_indicies_count
);
775 gui_synclist_select_item(&playlist_lists
, 0);
776 gui_synclist_draw(&playlist_lists
);
779 button
= get_action(CONTEXT_LIST
,TIMEOUT_BLOCK
);
780 if (gui_synclist_do_button(&playlist_lists
, button
,LIST_WRAP_UNLESS_HELD
))
784 case ACTION_STD_CANCEL
:
790 found_indicies
[gui_synclist_get_sel_pos(&playlist_lists
)]
797 if(default_event_handler(button
) == SYS_USB_CONNECTED
)