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
41 #include "backlight.h"
45 #include "playlist_viewer.h"
48 #include "statusbar.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].nb_lines
59 /* Default playlist name for saving */
60 #define DEFAULT_VIEWER_PLAYLIST_NAME "/viewer.m3u"
63 /* Information about a specific track */
64 struct playlist_entry
{
65 char *name
; /* Formatted track name */
66 int index
; /* Playlist index */
67 int display_index
; /* Display index */
68 bool queued
; /* Is track queued? */
69 bool skipped
; /* Is track marked as bad? */
78 struct playlist_buffer
80 char *name_buffer
; /* Buffer used to store track names */
81 int buffer_size
; /* Size of name buffer */
83 int first_index
; /* Real index of first track loaded inside
86 enum direction direction
; /* Direction of the buffer (if the buffer
87 was loaded BACKWARD, the last track in
88 the buffer has a real index < to the
89 real index of the the first track)*/
91 struct playlist_entry tracks
[MAX_PLAYLIST_ENTRIES
];
92 int num_loaded
; /* Number of track entries loaded in buffer */
95 /* Global playlist viewer settings */
96 struct playlist_viewer
{
97 struct playlist_info
* playlist
; /* playlist being viewed */
98 int num_tracks
; /* Number of tracks in playlist */
99 int current_playing_track
; /* Index of current playing track */
100 int selected_track
; /* The selected track, relative (first is 0)*/
101 int move_track
; /* Playlist index of track to move or -1 */
102 struct playlist_buffer buffer
;
105 static struct playlist_viewer viewer
;
107 /* Used when viewing playlists on disk */
108 static struct playlist_info temp_playlist
;
110 void playlist_buffer_init(struct playlist_buffer
* pb
, char * names_buffer
,
111 int names_buffer_size
);
112 void playlist_buffer_load_entries(struct playlist_buffer
* pb
, int index
,
113 enum direction direction
);
114 int playlist_entry_load(struct playlist_entry
*entry
, int index
,
115 char* name_buffer
, int remaining_size
);
117 struct playlist_entry
* playlist_buffer_get_track(struct playlist_buffer
* pb
,
120 static bool playlist_viewer_init(struct playlist_viewer
* viewer
,
121 char* filename
, bool reload
);
123 static void format_name(char* dest
, const char* src
);
124 static void format_line(const struct playlist_entry
* track
, char* str
,
127 static bool update_playlist(bool force
);
128 static int onplay_menu(int index
);
129 static bool viewer_menu(void);
130 static bool show_icons(void);
131 static bool show_indices(void);
132 static bool track_display(void);
133 static bool save_playlist(void);
135 void playlist_buffer_init(struct playlist_buffer
* pb
, char * names_buffer
,
136 int names_buffer_size
)
138 pb
->name_buffer
=names_buffer
;
139 pb
->buffer_size
=names_buffer_size
;
145 * Loads the entries following 'index' in the playlist buffer
147 void playlist_buffer_load_entries(struct playlist_buffer
* pb
, int index
,
148 enum direction direction
)
150 int num_entries
= viewer
.num_tracks
;
151 char* p
= pb
->name_buffer
;
152 int remaining
= pb
->buffer_size
;
155 pb
->first_index
= index
;
156 if (num_entries
> MAX_PLAYLIST_ENTRIES
)
157 num_entries
= MAX_PLAYLIST_ENTRIES
;
159 for(i
=0; i
<num_entries
; i
++)
161 int len
= playlist_entry_load(&(pb
->tracks
[i
]), index
, p
, remaining
);
164 /* Out of name buffer space */
172 if(direction
==FORWARD
)
176 index
+=viewer
.num_tracks
;
177 index
%=viewer
.num_tracks
;
179 pb
->direction
=direction
;
183 void playlist_buffer_load_entries_screen(struct playlist_buffer
* pb
,
184 enum direction direction
)
186 if(direction
==FORWARD
)
188 int min_start
=viewer
.selected_track
-2*screens
[0].nb_lines
;
190 min_start
+=viewer
.num_tracks
;
191 min_start
%= viewer
.num_tracks
;
192 playlist_buffer_load_entries(pb
, min_start
, FORWARD
);
196 int max_start
=viewer
.selected_track
+2*screens
[0].nb_lines
;
197 max_start
%=viewer
.num_tracks
;
198 playlist_buffer_load_entries(pb
, max_start
, BACKWARD
);
202 int playlist_entry_load(struct playlist_entry
*entry
, int index
,
203 char* name_buffer
, int remaining_size
)
205 struct playlist_track_info info
;
208 /* Playlist viewer orders songs based on display index. We need to
209 convert to real playlist index to access track */
210 index
= (index
+ playlist_get_first_index(viewer
.playlist
)) %
212 if (playlist_get_track_info(viewer
.playlist
, index
, &info
) < 0)
215 len
= strlen(info
.filename
) + 1;
217 if (len
<= remaining_size
)
219 strcpy(name_buffer
, info
.filename
);
221 entry
->name
= name_buffer
;
222 entry
->index
= info
.index
;
223 entry
->display_index
= info
.display_index
;
224 entry
->queued
= info
.attr
& PLAYLIST_ATTR_QUEUED
;
225 entry
->skipped
= info
.attr
& PLAYLIST_ATTR_SKIPPED
;
231 int playlist_buffer_get_index(struct playlist_buffer
* pb
, int index
)
234 if(pb
->direction
==FORWARD
)
236 if(index
>=pb
->first_index
)
237 buffer_index
=index
-pb
->first_index
;
238 else /* rotation : track0 in buffer + requested track */
239 buffer_index
=(viewer
.num_tracks
-pb
->first_index
)+(index
);
243 if(index
<=pb
->first_index
)
244 buffer_index
=pb
->first_index
-index
;
245 else /* rotation : track0 in buffer + dist from the last track
246 to the requested track (num_tracks-requested track) */
247 buffer_index
=(pb
->first_index
)+(viewer
.num_tracks
-index
);
249 return(buffer_index
);
252 #define distance(a, b) \
253 a>b? (a) - (b) : (b) - (a)
254 bool playlist_buffer_needs_reload(struct playlist_buffer
* pb
, int track_index
)
256 if(pb
->num_loaded
==viewer
.num_tracks
)
258 int selected_index
=playlist_buffer_get_index(pb
, track_index
);
259 int first_buffer_index
=playlist_buffer_get_index(pb
, pb
->first_index
);
260 int distance_beginning
=distance(selected_index
, first_buffer_index
);
261 if(distance_beginning
<MIN_BUFFER_MARGIN
)
264 if(pb
->num_loaded
- distance_beginning
< MIN_BUFFER_MARGIN
)
269 struct playlist_entry
* playlist_buffer_get_track(struct playlist_buffer
* pb
,
272 int buffer_index
=playlist_buffer_get_index(pb
, index
);
273 return(&(pb
->tracks
[buffer_index
]));
276 /* Initialize the playlist viewer. */
277 static bool playlist_viewer_init(struct playlist_viewer
* viewer
,
278 char* filename
, bool reload
)
282 bool is_playing
= audio_status() & AUDIO_STATUS_PLAY
;
284 if (!filename
&& !is_playing
)
285 /* Nothing is playing, exit */
288 buffer
= plugin_get_buffer(&buffer_size
);
293 viewer
->playlist
= NULL
;
296 /* Viewing playlist on disk */
297 char *dir
, *file
, *temp_ptr
;
298 char *index_buffer
= NULL
;
299 int index_buffer_size
= 0;
301 viewer
->playlist
= &temp_playlist
;
303 /* Separate directory from filename */
304 temp_ptr
= strrchr(filename
+1,'/');
319 /* Something is playing, use half the plugin buffer for playlist
321 index_buffer_size
= buffer_size
/ 2;
322 index_buffer
= buffer
;
325 playlist_create_ex(viewer
->playlist
, dir
, file
, index_buffer
,
326 index_buffer_size
, buffer
+index_buffer_size
,
327 buffer_size
-index_buffer_size
);
332 buffer
+= index_buffer_size
;
333 buffer_size
-= index_buffer_size
;
335 playlist_buffer_init(&viewer
->buffer
, buffer
, buffer_size
);
337 viewer
->move_track
= -1;
341 if (viewer
->playlist
)
342 viewer
->selected_track
= 0;
344 viewer
->selected_track
= playlist_get_display_index() - 1;
347 if (!update_playlist(true))
352 /* Format trackname for display purposes */
353 static void format_name(char* dest
, const char* src
)
355 switch (global_settings
.playlist_viewer_track_display
)
360 /* Only display the mp3 filename */
361 char* p
= strrchr(src
, '/');
367 /* Remove the extension */
368 if (!strcasecmp(&dest
[len
-4], ".mp3") ||
369 !strcasecmp(&dest
[len
-4], ".mp2") ||
370 !strcasecmp(&dest
[len
-4], ".mpa"))
382 /* Format display line */
383 static void format_line(const struct playlist_entry
* track
, char* str
,
389 format_name(name
, track
->name
);
394 if (global_settings
.playlist_viewer_indices
)
395 /* Display playlist index */
396 snprintf(str
, len
, "%d. %s%s", track
->display_index
, skipped
, name
);
398 snprintf(str
, len
, "%s%s", skipped
, name
);
402 /* Update playlist in case something has changed or forced */
403 static bool update_playlist(bool force
)
405 if (!viewer
.playlist
)
406 playlist_get_resume_info(&viewer
.current_playing_track
);
408 viewer
.current_playing_track
= -1;
409 int nb_tracks
=playlist_amount_ex(viewer
.playlist
);
410 force
=force
|| nb_tracks
!= viewer
.num_tracks
;
414 viewer
.num_tracks
= nb_tracks
;
415 if (viewer
.num_tracks
< 0)
417 playlist_buffer_load_entries_screen(&viewer
.buffer
, FORWARD
);
418 if (viewer
.buffer
.num_loaded
<= 0)
424 /* Menu of playlist commands. Invoked via ON+PLAY on main viewer screen.
425 Returns -1 if USB attached, 0 if no playlist change, and 1 if playlist
427 static int onplay_menu(int index
)
429 struct menu_item items
[3]; /* increase this if you add entries! */
430 int m
, i
=0, result
, ret
= 0;
431 struct playlist_entry
* current_track
=
432 playlist_buffer_get_track(&viewer
.buffer
, index
);
433 bool current
= (current_track
->index
== viewer
.current_playing_track
);
435 items
[i
].desc
= ID2P(LANG_REMOVE
);
438 items
[i
].desc
= ID2P(LANG_MOVE
);
441 items
[i
].desc
= ID2P(LANG_FILE_OPTIONS
);
444 m
= menu_init(items
, i
, NULL
, NULL
, NULL
, NULL
);
445 result
= menu_show(m
);
446 if (result
== MENU_ATTACHED_USB
)
448 else if (result
>= 0)
450 /* Abort current move */
451 viewer
.move_track
= -1;
457 playlist_delete(viewer
.playlist
, current_track
->index
);
460 /* Start playing new track except if it's the last track
461 in the playlist and repeat mode is disabled */
463 playlist_buffer_get_track(&viewer
.buffer
, index
);
464 if (current_track
->display_index
!= viewer
.num_tracks
||
465 global_settings
.repeat_mode
== REPEAT_ALL
)
467 talk_buffer_steal(); /* will use the mp3 buffer */
469 viewer
.current_playing_track
= -1;
476 viewer
.move_track
= current_track
->index
;
481 onplay(current_track
->name
, TREE_ATTR_MPA
, CONTEXT_TREE
);
483 if (!viewer
.playlist
)
495 /* Menu of viewer options. Invoked via F1(r) or Menu(p). */
496 static bool viewer_menu(void)
501 static const struct menu_item items
[] = {
502 { ID2P(LANG_SHOW_ICONS
), show_icons
},
503 { ID2P(LANG_SHOW_INDICES
), show_indices
},
504 { ID2P(LANG_TRACK_DISPLAY
), track_display
},
505 { ID2P(LANG_SAVE_DYNAMIC_PLAYLIST
), save_playlist
},
508 m
=menu_init( items
, sizeof(items
) / sizeof(*items
), NULL
,
510 result
= menu_run(m
);
518 /* Show icons in viewer? */
519 static bool show_icons(void)
521 return set_bool((char *)str(LANG_SHOW_ICONS
),
522 &global_settings
.playlist_viewer_icons
);
525 /* Show indices of tracks? */
526 static bool show_indices(void)
528 return set_bool((char *)str(LANG_SHOW_INDICES
),
529 &global_settings
.playlist_viewer_indices
);
532 /* How to display a track */
533 static bool track_display(void)
535 static const struct opt_items names
[] = {
536 { STR(LANG_DISPLAY_TRACK_NAME_ONLY
) },
537 { STR(LANG_DISPLAY_FULL_PATH
) }
540 return set_option((char *)str(LANG_TRACK_DISPLAY
),
541 &global_settings
.playlist_viewer_track_display
, INT
, names
, 2,
545 /* Save playlist to disk */
546 static bool save_playlist(void)
548 save_playlist_screen(viewer
.playlist
);
552 /* View current playlist */
553 bool playlist_viewer(void)
555 return playlist_viewer_ex(NULL
);
558 char * playlist_callback_name(int selected_item
, void * data
, char *buffer
)
560 struct playlist_viewer
* local_viewer
= (struct playlist_viewer
*)data
;
561 struct playlist_entry
*track
=
562 playlist_buffer_get_track(&(local_viewer
->buffer
), selected_item
);
563 format_line(track
, buffer
, MAX_PATH
);
568 void playlist_callback_icons(int selected_item
, void * data
, ICON
* icon
)
570 struct playlist_viewer
* local_viewer
=(struct playlist_viewer
*)data
;
571 struct playlist_entry
*track
=
572 playlist_buffer_get_track(&(local_viewer
->buffer
), selected_item
);
573 if (track
->index
== local_viewer
->current_playing_track
)
575 /* Current playing track */
576 #ifdef HAVE_LCD_BITMAP
577 *icon
=bitmap_icons_6x8
[Icon_Audio
];
582 else if (track
->index
== local_viewer
->move_track
)
584 /* Track we are moving */
585 #ifdef HAVE_LCD_BITMAP
586 *icon
=bitmap_icons_6x8
[Icon_Moving
];
591 else if (track
->queued
)
594 #ifdef HAVE_LCD_BITMAP
595 *icon
=bitmap_icons_6x8
[Icon_Queued
];
601 #ifdef HAVE_LCD_BITMAP
608 /* Main viewer function. Filename identifies playlist to be viewed. If NULL,
609 view current playlist. */
610 bool playlist_viewer_ex(char* filename
)
612 bool ret
= false; /* return value */
613 bool exit
=false; /* exit viewer */
614 int button
, lastbutton
= BUTTON_NONE
;
615 struct gui_synclist playlist_lists
;
616 if (!playlist_viewer_init(&viewer
, filename
, false))
619 gui_synclist_init(&playlist_lists
, playlist_callback_name
, &viewer
);
620 gui_synclist_set_icon_callback(&playlist_lists
,
621 global_settings
.playlist_viewer_icons
?
622 &playlist_callback_icons
:NULL
);
623 gui_synclist_set_nb_items(&playlist_lists
, viewer
.num_tracks
);
624 gui_synclist_select_item(&playlist_lists
, viewer
.selected_track
);
625 gui_synclist_draw(&playlist_lists
);
629 if (!viewer
.playlist
&& !(audio_status() & AUDIO_STATUS_PLAY
))
631 /* Play has stopped */
632 #ifdef HAVE_LCD_CHARCELLS
633 gui_syncsplash(HZ
, true, str(LANG_END_PLAYLIST_PLAYER
));
635 gui_syncsplash(HZ
, true, str(LANG_END_PLAYLIST_RECORDER
));
640 if (viewer
.move_track
!= -1)
641 gui_synclist_flash(&playlist_lists
);
643 if (!viewer
.playlist
)
644 playlist_get_resume_info(&track
);
648 if (track
!= viewer
.current_playing_track
||
649 playlist_amount_ex(viewer
.playlist
) != viewer
.num_tracks
)
651 /* Playlist has changed (new track started?) */
652 if (!update_playlist(false))
654 gui_synclist_set_nb_items(&playlist_lists
, viewer
.num_tracks
);
655 /* Abort move on playlist change */
656 viewer
.move_track
= -1;
659 /* Timeout so we can determine if play status has changed */
660 button
= button_get_w_tmo(HZ
/2);
662 if( (list_action
=gui_synclist_do_button(&playlist_lists
, button
))!=0 )
664 viewer
.selected_track
=gui_synclist_get_sel_pos(&playlist_lists
);
665 if(playlist_buffer_needs_reload(&viewer
.buffer
,
666 viewer
.selected_track
))
667 playlist_buffer_load_entries_screen(&viewer
.buffer
,
668 list_action
==LIST_NEXT
?
688 case TREE_ENTER
| BUTTON_REPEAT
:
695 if (((button
== TREE_RUN
)
696 #ifdef TREE_RC_RUN_PRE
697 || (button
== TREE_RC_RUN
))
698 && ((lastbutton
!= TREE_RC_RUN_PRE
)
700 && (lastbutton
!= TREE_RUN_PRE
)))
703 struct playlist_entry
* current_track
=
704 playlist_buffer_get_track(&viewer
.buffer
,
705 viewer
.selected_track
);
706 if (viewer
.move_track
>= 0)
711 ret
= playlist_move(viewer
.playlist
, viewer
.move_track
,
712 current_track
->index
);
714 gui_syncsplash(HZ
, true, str(LANG_MOVE_FAILED
));
716 update_playlist(true);
717 viewer
.move_track
= -1;
719 else if (!viewer
.playlist
)
722 playlist_start(current_track
->index
, 0);
723 update_playlist(false);
728 if (playlist_set_current(viewer
.playlist
) < 0)
731 playlist_start(current_track
->index
, 0);
733 /* Our playlist is now the current list */
734 if (!playlist_viewer_init(&viewer
, NULL
, true))
737 gui_synclist_draw(&playlist_lists
);
745 #ifdef TREE_RC_CONTEXT
746 case TREE_RC_CONTEXT
:
752 ret
= onplay_menu(viewer
.selected_track
);
761 /* Playlist changed */
762 gui_synclist_del_item(&playlist_lists
);
763 update_playlist(true);
764 if (viewer
.num_tracks
<= 0)
767 gui_synclist_draw(&playlist_lists
);
783 gui_synclist_set_icon_callback(
785 global_settings
.playlist_viewer_icons
?
786 &playlist_callback_icons
:NULL
788 gui_synclist_draw(&playlist_lists
);
792 gui_syncstatusbar_draw(&statusbars
, false);
796 if(default_event_handler(button
) == SYS_USB_CONNECTED
)
808 playlist_close(viewer
.playlist
);
811 char * playlist_search_callback_name(int selected_item
, void * data
, char *buffer
)
813 int *found_indicies
= (int*)data
;
814 static struct playlist_track_info track
;
815 playlist_get_track_info(viewer
.playlist
,found_indicies
[selected_item
],&track
);
816 format_name(buffer
,track
.filename
);
821 void playlist_search_callback_icons(int selected_item
, void * data
, ICON
* icon
)
825 #ifdef HAVE_LCD_BITMAP
831 bool search_playlist(void)
833 char search_str
[32] = "";
834 bool ret
= false, exit
= false;
835 int i
, playlist_count
;
836 int found_indicies
[MAX_PLAYLIST_ENTRIES
],found_indicies_count
= 0;
838 struct gui_synclist playlist_lists
;
839 struct playlist_track_info track
;
841 if (!playlist_viewer_init(&viewer
, 0, false))
843 if (kbd_input(search_str
,sizeof(search_str
)) == -1)
846 playlist_count
= playlist_amount_ex(viewer
.playlist
);
847 for (i
=0;(i
<playlist_count
)&&(found_indicies_count
<MAX_PLAYLIST_ENTRIES
);i
++)
849 gui_syncsplash(0, true, str(LANG_PLAYLIST_SEARCH_MSG
),found_indicies_count
,
850 #if CONFIG_KEYPAD == PLAYER_PAD
856 if (SETTINGS_CANCEL
== button_get(false))
858 playlist_get_track_info(viewer
.playlist
,i
,&track
);
859 if (strcasestr(track
.filename
,search_str
))
861 found_indicies
[found_indicies_count
++] = track
.index
;
864 if (!found_indicies_count
)
869 gui_synclist_init(&playlist_lists
, playlist_search_callback_name
,
871 gui_synclist_set_icon_callback(&playlist_lists
,
872 global_settings
.playlist_viewer_icons
?
873 &playlist_search_callback_icons
:NULL
);
874 gui_synclist_set_nb_items(&playlist_lists
, found_indicies_count
);
875 gui_synclist_select_item(&playlist_lists
, 0);
876 gui_synclist_draw(&playlist_lists
);
879 button
= button_get(true);
880 if (gui_synclist_do_button(&playlist_lists
, button
))
896 case TREE_ENTER
| BUTTON_REPEAT
:
903 found_indicies
[gui_synclist_get_sel_pos(&playlist_lists
)]
910 if(default_event_handler(button
) == SYS_USB_CONNECTED
)