Fake power off in clean_shutdown() on Ondio players, to make the user releases the...
[kugel-rb.git] / apps / playlist_viewer.c
blob98f04b8eafe845a12d5d538471c0ad9d09149e38
1 /***************************************************************************
3 * __________ __ ___.
4 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
5 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
6 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
7 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
8 * \/ \/ \/ \/ \/
9 * $Id$
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 #include <string.h>
22 #include <sprintf.h>
23 #include "playlist.h"
24 #include "mpeg.h"
25 #include "screens.h"
26 #include "status.h"
27 #include "settings.h"
28 #include "icons.h"
29 #include "menu.h"
30 #include "plugin.h"
31 #include "keyboard.h"
32 #include "tree.h"
33 #include "onplay.h"
34 #include "talk.h"
35 #include "misc.h"
37 #ifdef HAVE_LCD_BITMAP
38 #include "widgets.h"
39 #endif
41 #include "lang.h"
43 #include "playlist_viewer.h"
45 /* Defines for LCD display purposes. Taken from tree.c */
46 #ifdef HAVE_LCD_BITMAP
47 #define CURSOR_X (global_settings.scrollbar && \
48 viewer.num_tracks>viewer.num_display_lines?1:0)
49 #define CURSOR_Y 0
50 #define CURSOR_WIDTH (global_settings.invert_cursor ? 0 : 4)
52 #define ICON_WIDTH ((viewer.char_width > 6) ? viewer.char_width : 6)
54 #define MARGIN_X ((global_settings.scrollbar && \
55 viewer.num_tracks > viewer.num_display_lines ? \
56 SCROLLBAR_WIDTH : 0) + CURSOR_WIDTH + \
57 (global_settings.playlist_viewer_icons ? \
58 ICON_WIDTH : 0))
59 #define MARGIN_Y (global_settings.statusbar ? STATUSBAR_HEIGHT : 0)
61 #define LINE_X 0
62 #define LINE_Y (global_settings.statusbar ? 1 : 0)
64 #define SCROLLBAR_X 0
65 #define SCROLLBAR_Y lcd_getymargin()
66 #define SCROLLBAR_WIDTH 6
67 #else
68 #define MARGIN_X 0
69 #define MARGIN_Y 0
70 #define LINE_X 2
71 #define LINE_Y 0
72 #define CURSOR_X 0
73 #define CURSOR_Y 0
74 #endif
76 /* Maximum number of tracks we can have loaded at one time */
77 #define MAX_PLAYLIST_ENTRIES 200
79 /* Default playlist name for saving */
80 #define DEFAULT_PLAYLIST_NAME "/viewer.m3u"
82 /* Index of track on display line _pos */
83 #define INDEX(_pos) (viewer.first_display_index - viewer.first_index + (_pos))
85 /* Global playlist viewer settings */
86 struct playlist_viewer_info {
87 struct playlist_info* playlist; /* playlist being viewed */
88 char *name_buffer; /* Buffer used to store track names */
89 int buffer_size; /* Size of name buffer */
91 int num_display_lines; /* Number of lines on lcd */
92 int line_height; /* Height (in pixels) of display line */
93 int char_width; /* Width (in pixels) of a character */
95 int num_tracks; /* Number of tracks in playlist */
96 int current_playing_track; /* Index of current playing track */
98 int num_loaded; /* Number of track entries loaded in viewer */
99 int first_index; /* Index of first loaded track */
100 int last_index; /* Index of last loaded track */
101 int first_display_index; /* Index of first track on display */
102 int last_display_index; /* Index of last track on display */
103 int cursor_pos; /* Line number of cursor */
105 int move_track; /* Playlist index of track to move or -1 */
108 /* Information about a specific track */
109 struct playlist_entry {
110 char *name; /* Formatted track name */
111 int index; /* Playlist index */
112 int display_index; /* Display index */
113 bool queued; /* Is track queued? */
116 static struct playlist_viewer_info viewer;
117 static struct playlist_entry tracks[MAX_PLAYLIST_ENTRIES];
119 /* Used when viewing playlists on disk */
120 static struct playlist_info temp_playlist;
122 static bool initialize(char* filename, bool reload);
123 static void load_playlist_entries(int start_index);
124 static void load_playlist_entries_r(int end_index);
125 static int load_entry(int index, int pos, char* p, int size);
126 static void format_name(char* dest, const char* src);
127 static void format_line(const struct playlist_entry* track, char* str, int len);
128 static void display_playlist(void);
129 static void update_display_line(int line, bool scroll);
130 static void scroll_display(int lines);
131 static void update_first_index(void);
132 static bool update_playlist(bool force);
133 static int onplay_menu(int index);
134 static bool viewer_menu(void);
135 static bool show_icons(void);
136 static bool show_indices(void);
137 static bool track_display(void);
138 static bool save_playlist(void);
140 /* Initialize the playlist viewer. */
141 static bool initialize(char* filename, bool reload)
143 char* buffer;
144 int buffer_size;
145 bool is_playing = mpeg_status() & MPEG_STATUS_PLAY;
147 if (!filename && !is_playing)
148 /* Nothing is playing, exit */
149 return false;
151 buffer = plugin_get_buffer(&buffer_size);
152 if (!buffer)
153 return false;
155 if (!filename)
156 viewer.playlist = NULL;
157 else
159 /* Viewing playlist on disk */
160 char *dir, *file, *temp_ptr;
161 char *index_buffer = NULL;
162 int index_buffer_size = 0;
164 viewer.playlist = &temp_playlist;
166 /* Separate directory from filename */
167 temp_ptr = strrchr(filename+1,'/');
168 if (temp_ptr)
170 *temp_ptr = 0;
171 dir = filename;
172 file = temp_ptr + 1;
174 else
176 dir = "/";
177 file = filename+1;
180 if (is_playing)
182 /* Something is playing, use half the plugin buffer for playlist
183 indices */
184 index_buffer_size = buffer_size / 2;
185 index_buffer = buffer;
188 playlist_create_ex(viewer.playlist, dir, file, index_buffer,
189 index_buffer_size, buffer+index_buffer_size,
190 buffer_size-index_buffer_size);
192 if (temp_ptr)
193 *temp_ptr = '/';
195 buffer += index_buffer_size;
196 buffer_size -= index_buffer_size;
199 viewer.name_buffer = buffer;
200 viewer.buffer_size = buffer_size;
202 #ifdef HAVE_LCD_BITMAP
204 char icon_chars[] = "MQ"; /* characters used as icons */
205 unsigned int i;
207 viewer.char_width = 0;
208 viewer.line_height = 0;
210 /* Use icon characters to calculate largest possible width/height so
211 that we set proper margins */
212 for (i=0; i<sizeof(icon_chars); i++)
214 char str[2];
215 int w, h;
217 snprintf(str, sizeof(str), "%c", icon_chars[i]);
218 lcd_getstringsize(str, &w, &h);
220 if (w > viewer.char_width)
221 viewer.char_width = w;
223 if (h > viewer.line_height)
225 viewer.line_height = h;
226 viewer.num_display_lines = (LCD_HEIGHT - MARGIN_Y)/h;
230 #else
231 viewer.num_display_lines = 2;
232 viewer.char_width = 1;
233 viewer.line_height = 1;
234 #endif
236 viewer.move_track = -1;
238 if (!reload)
240 viewer.cursor_pos = 0;
242 if (!viewer.playlist)
243 /* Start displaying at current playing track */
244 viewer.first_display_index = playlist_get_display_index() - 1;
245 else
246 viewer.first_display_index = 0;
248 update_first_index();
251 if (!update_playlist(true))
252 return false;
254 return true;
257 /* Load tracks starting at start_index */
258 static void load_playlist_entries(int start_index)
260 int num_entries = viewer.num_tracks - start_index;
261 char* p = viewer.name_buffer;
262 int remaining = viewer.buffer_size;
263 int i;
265 viewer.first_index = start_index;
267 if (num_entries > MAX_PLAYLIST_ENTRIES)
268 num_entries = MAX_PLAYLIST_ENTRIES;
270 for(i=0; i<num_entries; i++, start_index++)
272 int len = load_entry(start_index, i, p, remaining);
273 if (len < 0)
275 /* Out of name buffer space */
276 num_entries = i;
277 break;
280 p += len;
281 remaining -= len;
284 viewer.num_loaded = num_entries;
285 viewer.last_index = viewer.first_index + (viewer.num_loaded - 1);
288 /* Load tracks in reverse, ending at end_index */
289 static void load_playlist_entries_r(int end_index)
291 int num_entries = end_index;
292 char* p = viewer.name_buffer;
293 int remaining = viewer.buffer_size;
294 int i;
296 viewer.last_index = end_index;
298 if (num_entries >= MAX_PLAYLIST_ENTRIES)
299 num_entries = MAX_PLAYLIST_ENTRIES-1;
301 for(i=num_entries; i>=0; i--, end_index--)
303 int len = load_entry(end_index, i, p, remaining);
304 if (len < 0)
306 int j;
308 /* Out of name buffer space */
309 num_entries -= i;
311 /* Shift loaded tracks up such that first track is index 0 */
312 for (j=0; j<num_entries; j++, i++)
314 tracks[j].name = tracks[i].name;
315 tracks[j].index = tracks[i].index;
316 tracks[j].display_index = tracks[i].display_index;
317 tracks[j].queued = tracks[i].queued;
320 break;
323 p += len;
324 remaining -= len;
327 viewer.first_index = viewer.last_index - num_entries;
329 num_entries++;
330 if (!viewer.first_index &&
331 num_entries < viewer.num_tracks &&
332 num_entries < MAX_PLAYLIST_ENTRIES)
334 /* Lets see if we can load more data at the end of the list */
335 int max = viewer.num_tracks;
336 if (max > MAX_PLAYLIST_ENTRIES)
337 max = MAX_PLAYLIST_ENTRIES;
339 for (i = num_entries; i<max; i++)
341 int len = load_entry(num_entries, num_entries, p, remaining);
342 if (len < 0)
343 /* Out of name buffer space */
344 break;
346 p += len;
347 remaining -= len;
349 num_entries++;
350 viewer.last_index++;
354 viewer.num_loaded = num_entries;
357 /* Load track at playlist index. pos is the position in the tracks array and
358 p is a pointer to the name buffer (max size), Returns -1 if buffer is
359 full. */
360 static int load_entry(int index, int pos, char* p, int size)
362 struct playlist_track_info info;
363 int len;
364 int result = 0;
366 /* Playlist viewer orders songs based on display index. We need to
367 convert to real playlist index to access track */
368 index = (index + playlist_get_first_index(viewer.playlist)) %
369 viewer.num_tracks;
370 if (playlist_get_track_info(viewer.playlist, index, &info) < 0)
371 return -1;
373 len = strlen(info.filename) + 1;
375 if (len <= size)
377 strcpy(p, info.filename);
379 tracks[pos].name = p;
380 tracks[pos].index = info.index;
381 tracks[pos].display_index = info.display_index;
382 tracks[pos].queued = info.attr & PLAYLIST_ATTR_QUEUED;
384 result = len;
386 else
387 result = -1;
389 return result;
392 /* Format trackname for display purposes */
393 static void format_name(char* dest, const char* src)
395 switch (global_settings.playlist_viewer_track_display)
397 case 0:
398 default:
400 /* Only display the mp3 filename */
401 char* p = strrchr(src, '/');
402 int len;
404 strcpy(dest, p+1);
405 len = strlen(dest);
407 /* Remove the extension */
408 if (!strcasecmp(&dest[len-4], ".mp3") ||
409 !strcasecmp(&dest[len-4], ".mp2") ||
410 !strcasecmp(&dest[len-4], ".mpa"))
411 dest[len-4] = '\0';
413 break;
415 case 1:
416 /* Full path */
417 strcpy(dest, src);
418 break;
422 /* Format display line */
423 static void format_line(const struct playlist_entry* track, char* str, int len)
425 char name[MAX_PATH];
427 format_name(name, track->name);
429 if (global_settings.playlist_viewer_indices)
430 /* Display playlist index */
431 snprintf(str, len, "%d. %s", track->display_index, name);
432 else
433 snprintf(str, len, "%s", name);
437 /* Display tracks on screen */
438 static void display_playlist(void)
440 int i;
441 int num_display_tracks =
442 viewer.last_display_index - viewer.first_display_index;
444 lcd_clear_display();
446 #ifdef HAVE_LCD_BITMAP
447 lcd_setmargins(MARGIN_X, MARGIN_Y);
448 lcd_setfont(FONT_UI);
449 #endif
451 for (i=0; i<=num_display_tracks; i++)
453 if (global_settings.playlist_viewer_icons)
455 /* Icons */
456 if (tracks[INDEX(i)].index == viewer.current_playing_track)
458 /* Current playing track */
459 #ifdef HAVE_LCD_BITMAP
460 int offset=0;
461 if ( viewer.line_height > 8 )
462 offset = (viewer.line_height - 8) / 2;
463 lcd_bitmap(bitmap_icons_6x8[File],
464 CURSOR_X * 6 + CURSOR_WIDTH,
465 MARGIN_Y+(i*viewer.line_height) + offset,
466 6, 8, true);
467 #else
468 lcd_putc(LINE_X-1, i, File);
469 #endif
471 else if (tracks[INDEX(i)].index == viewer.move_track)
473 /* Track we are moving */
474 #ifdef HAVE_LCD_BITMAP
475 lcd_putsxy(CURSOR_X * 6 + CURSOR_WIDTH,
476 MARGIN_Y+(i*viewer.line_height), "M");
477 #else
478 lcd_putc(LINE_X-1, i, 'M');
479 #endif
481 else if (tracks[INDEX(i)].queued)
483 /* Queued track */
484 #ifdef HAVE_LCD_BITMAP
485 lcd_putsxy(CURSOR_X * 6 + CURSOR_WIDTH,
486 MARGIN_Y+(i*viewer.line_height), "Q");
487 #else
488 lcd_putc(LINE_X-1, i, 'Q');
489 #endif
493 update_display_line(i, false);
496 #ifdef HAVE_LCD_BITMAP
497 if (global_settings.scrollbar &&
498 (viewer.num_tracks > viewer.num_display_lines))
499 scrollbar(SCROLLBAR_X, SCROLLBAR_Y, SCROLLBAR_WIDTH - 1,
500 LCD_HEIGHT - SCROLLBAR_Y, viewer.num_tracks-1,
501 viewer.first_display_index, viewer.last_display_index,
502 VERTICAL);
503 #endif
505 put_cursorxy(CURSOR_X, CURSOR_Y + viewer.cursor_pos, true);
506 status_draw(true);
509 /* Scroll cursor or display by num lines */
510 static void scroll_display(int lines)
512 int new_index = viewer.first_display_index + viewer.cursor_pos + lines;
513 bool pagescroll = false;
514 bool wrap = false;
516 put_cursorxy(CURSOR_X, CURSOR_Y + viewer.cursor_pos, false);
518 if (lines > 1 || lines < -1)
519 pagescroll = true;
521 if (new_index < 0)
523 /* Wrap around if not pageup */
524 if (pagescroll)
525 new_index = 0;
526 else
528 new_index += viewer.num_tracks;
529 viewer.cursor_pos = viewer.num_display_lines-1;
530 wrap = true;
533 else if (new_index >= viewer.num_tracks)
535 /* Wrap around if not pagedown */
536 if (pagescroll)
537 new_index = viewer.num_tracks - 1;
538 else
540 new_index -= viewer.num_tracks;
541 viewer.cursor_pos = 0;
542 wrap = true;
546 if (new_index >= viewer.first_display_index &&
547 new_index <= viewer.last_display_index)
549 /* Just update the cursor */
550 viewer.cursor_pos = new_index - viewer.first_display_index;
552 else
554 /* New track is outside of display */
555 if (wrap)
556 viewer.first_display_index = new_index;
557 else
558 viewer.first_display_index = viewer.first_display_index + lines;
560 if (viewer.first_display_index < 0)
561 viewer.first_display_index = 0;
563 viewer.last_display_index =
564 viewer.first_display_index + (viewer.num_display_lines - 1);
565 if (viewer.last_display_index >= viewer.num_tracks)
567 /* display as many tracks as possible on screen */
568 if (viewer.first_display_index > 0)
570 viewer.first_display_index -=
571 (viewer.last_display_index - viewer.num_tracks + 1);
572 if (viewer.first_display_index < 0)
573 viewer.first_display_index = 0;
576 viewer.last_display_index = viewer.num_tracks - 1;
579 if (viewer.cursor_pos >
580 (viewer.last_display_index - viewer.first_display_index))
581 viewer.cursor_pos =
582 viewer.last_display_index - viewer.first_display_index;
584 /* Load more data if needed */
585 if (viewer.first_display_index < viewer.first_index)
586 load_playlist_entries_r(viewer.last_display_index);
587 else if (viewer.last_display_index > viewer.last_index)
588 load_playlist_entries(viewer.first_display_index);
590 display_playlist();
593 put_cursorxy(CURSOR_X, CURSOR_Y + viewer.cursor_pos, true);
596 /* Update lcd line. Scroll line if requested */
597 static void update_display_line(int line, bool scroll)
599 char str[MAX_PATH + 16];
601 format_line(&tracks[INDEX(line)], str, sizeof(str));
603 if (scroll)
605 #ifdef HAVE_LCD_BITMAP
606 if (global_settings.invert_cursor)
607 lcd_puts_scroll_style(LINE_X, line, str, STYLE_INVERT);
608 else
609 #endif
610 lcd_puts_scroll(LINE_X, line, str);
612 else
613 lcd_puts(LINE_X, line, str);
616 /* Update first index, if necessary, to put as much as possible on the
617 screen */
618 static void update_first_index(void)
620 /* viewer.num_tracks may be invalid at this point */
621 int num_tracks = playlist_amount_ex(viewer.playlist);
623 if ((num_tracks - viewer.first_display_index) < viewer.num_display_lines)
625 /* Try to display as much as possible */
626 int old_index = viewer.first_display_index;
628 viewer.first_display_index = num_tracks - viewer.num_display_lines;
629 if (viewer.first_display_index < 0)
630 viewer.first_display_index = 0;
632 /* Cursor should still point at current track */
633 viewer.cursor_pos += old_index - viewer.first_display_index;
637 /* Update playlist in case something has changed or forced */
638 static bool update_playlist(bool force)
640 if (!viewer.playlist)
641 playlist_get_resume_info(&viewer.current_playing_track);
642 else
643 viewer.current_playing_track = -1;
645 if (force || playlist_amount_ex(viewer.playlist) != viewer.num_tracks)
647 int index;
649 /* Reload tracks */
650 viewer.num_tracks = playlist_amount_ex(viewer.playlist);
651 if (viewer.num_tracks < 0)
652 return false;
654 index = viewer.first_display_index;
656 load_playlist_entries(index);
658 if (viewer.num_loaded <= 0)
659 return false;
661 viewer.first_display_index = viewer.first_index;
662 viewer.last_display_index =
663 viewer.first_index + viewer.num_display_lines - 1;
664 if (viewer.last_display_index >= viewer.num_tracks)
665 viewer.last_display_index = viewer.num_tracks - 1;
668 display_playlist();
670 return true;
673 /* Menu of playlist commands. Invoked via ON+PLAY on main viewer screen.
674 Returns -1 if USB attached, 0 if no playlist change, and 1 if playlist
675 changed. */
676 static int onplay_menu(int index)
678 struct menu_item items[3]; /* increase this if you add entries! */
679 int m, i=0, result, ret = 0;
680 bool current = (tracks[index].index == viewer.current_playing_track);
682 items[i].desc = ID2P(LANG_REMOVE);
683 i++;
685 items[i].desc = ID2P(LANG_MOVE);
686 i++;
688 items[i].desc = ID2P(LANG_FILE_OPTIONS);
689 i++;
691 m = menu_init(items, i, NULL, NULL, NULL, NULL);
692 result = menu_show(m);
693 if (result == MENU_ATTACHED_USB)
694 ret = -1;
695 else if (result >= 0)
697 /* Abort current move */
698 viewer.move_track = -1;
700 switch (result)
702 case 0:
703 /* delete track */
704 if (current)
705 mpeg_stop();
707 playlist_delete(viewer.playlist, tracks[index].index);
709 if (current)
711 /* Start playing new track except if it's the last track
712 in the playlist and repeat mode is disabled */
713 if (tracks[index].display_index != viewer.num_tracks ||
714 global_settings.repeat_mode == REPEAT_ALL)
716 talk_buffer_steal(); /* will use the mp3 buffer */
717 mpeg_play(0);
718 viewer.current_playing_track = -1;
722 ret = 1;
723 break;
724 case 1:
725 /* move track */
726 viewer.move_track = tracks[index].index;
727 ret = 0;
728 break;
729 case 2:
731 onplay(tracks[index].name, TREE_ATTR_MPA);
733 if (!viewer.playlist)
734 ret = 1;
735 else
736 ret = 0;
738 break;
743 menu_exit(m);
745 return ret;
748 /* Menu of viewer options. Invoked via F1(r) or Menu(p). */
749 static bool viewer_menu(void)
751 int m;
752 bool result;
754 static const struct menu_item items[] = {
755 { ID2P(LANG_SHOW_ICONS), show_icons },
756 { ID2P(LANG_SHOW_INDICES), show_indices },
757 { ID2P(LANG_TRACK_DISPLAY), track_display },
758 { ID2P(LANG_SAVE_DYNAMIC_PLAYLIST), save_playlist },
761 m=menu_init( items, sizeof(items) / sizeof(*items), NULL,
762 NULL, NULL, NULL );
763 result = menu_run(m);
764 menu_exit(m);
766 settings_save();
768 return result;
771 /* Show icons in viewer? */
772 static bool show_icons(void)
774 return set_bool(str(LANG_SHOW_ICONS),
775 &global_settings.playlist_viewer_icons);
778 /* Show indices of tracks? */
779 static bool show_indices(void)
781 return set_bool(str(LANG_SHOW_INDICES),
782 &global_settings.playlist_viewer_indices);
785 /* How to display a track */
786 static bool track_display(void)
788 static const struct opt_items names[] = {
789 { STR(LANG_DISPLAY_TRACK_NAME_ONLY) },
790 { STR(LANG_DISPLAY_FULL_PATH) }
793 return set_option(str(LANG_TRACK_DISPLAY),
794 &global_settings.playlist_viewer_track_display, INT, names, 2, NULL);
797 /* Save playlist to disk */
798 static bool save_playlist(void)
800 char filename[MAX_PATH+1];
802 strncpy(filename, DEFAULT_PLAYLIST_NAME, sizeof(filename));
804 if (!kbd_input(filename, sizeof(filename)))
806 playlist_save(viewer.playlist, filename);
808 /* reload in case playlist was saved to cwd */
809 reload_directory();
812 return false;
815 /* View current playlist */
816 bool playlist_viewer(void)
818 return playlist_viewer_ex(NULL);
821 /* Main viewer function. Filename identifies playlist to be viewed. If NULL,
822 view current playlist. */
823 bool playlist_viewer_ex(char* filename)
825 bool ret = false; /* return value */
826 bool exit=false; /* exit viewer */
827 bool update=true; /* update display */
828 bool cursor_on=true; /* used for flashing cursor */
829 int old_cursor_pos; /* last cursor position */
830 int button;
832 if (!initialize(filename, false))
833 goto exit;
835 old_cursor_pos = viewer.cursor_pos;
837 while (!exit)
839 int track;
841 if (!viewer.playlist && !(mpeg_status() & MPEG_STATUS_PLAY))
843 /* Play has stopped */
844 #ifdef HAVE_LCD_CHARCELLS
845 splash(HZ, true, str(LANG_END_PLAYLIST_PLAYER));
846 #else
847 splash(HZ, true, str(LANG_END_PLAYLIST_RECORDER));
848 #endif
849 goto exit;
852 if (viewer.move_track != -1 || !cursor_on)
854 /* Flash cursor to identify that we are moving a track */
855 cursor_on = !cursor_on;
856 #ifdef HAVE_LCD_BITMAP
857 if (global_settings.invert_cursor)
859 lcd_invertrect(
860 MARGIN_X, MARGIN_Y+(viewer.cursor_pos*viewer.line_height),
861 LCD_WIDTH, viewer.line_height);
862 lcd_invertscroll(LINE_X, viewer.cursor_pos);
864 else
865 put_cursorxy(CURSOR_X, CURSOR_Y + viewer.cursor_pos,
866 cursor_on);
868 lcd_update_rect(
869 0, MARGIN_Y + (viewer.cursor_pos * viewer.line_height),
870 LCD_WIDTH, viewer.line_height);
871 #else
872 put_cursorxy(CURSOR_X, CURSOR_Y + viewer.cursor_pos, cursor_on);
873 lcd_update();
874 #endif
877 if (!viewer.playlist)
878 playlist_get_resume_info(&track);
879 else
880 track = -1;
882 if (track != viewer.current_playing_track ||
883 playlist_amount_ex(viewer.playlist) != viewer.num_tracks)
885 /* Playlist has changed (new track started?) */
886 update_first_index();
887 if (!update_playlist(false))
888 goto exit;
889 else
890 update = true;
892 /* Abort move on playlist change */
893 viewer.move_track = -1;
896 /* Timeout so we can determine if play status has changed */
897 button = button_get_w_tmo(HZ/2);
899 switch (button)
901 case TREE_EXIT | BUTTON_REL:
902 exit = true;
903 break;
905 case TREE_PREV:
906 case TREE_PREV | BUTTON_REPEAT:
907 scroll_display(-1);
908 update = true;
909 break;
911 case TREE_NEXT:
912 case TREE_NEXT | BUTTON_REPEAT:
913 scroll_display(1);
914 update = true;
915 break;
917 #ifdef TREE_PGUP
918 case TREE_PGUP:
919 case TREE_PGUP | BUTTON_REPEAT:
920 /* Pageup */
921 scroll_display(-viewer.num_display_lines);
922 update = true;
923 break;
925 case TREE_PGDN:
926 case TREE_PGDN | BUTTON_REPEAT:
927 /* Pagedown */
928 scroll_display(viewer.num_display_lines);
929 update = true;
930 break;
931 #endif
933 case TREE_RUN:
934 if (viewer.move_track >= 0)
936 /* Move track */
937 int ret;
939 ret = playlist_move(viewer.playlist, viewer.move_track,
940 tracks[INDEX(viewer.cursor_pos)].index);
941 if (ret < 0)
942 splash(HZ, true, str(LANG_MOVE_FAILED));
944 update_playlist(true);
945 viewer.move_track = -1;
947 else if (!viewer.playlist)
949 /* Stop current track and play new track */
950 mpeg_stop();
951 playlist_start(tracks[INDEX(viewer.cursor_pos)].index, 0);
952 update_playlist(false);
954 else
956 /* Play track from playlist on disk */
957 mpeg_stop();
959 /* New playlist */
960 if (playlist_set_current(viewer.playlist) < 0)
961 goto exit;
963 playlist_start(tracks[INDEX(viewer.cursor_pos)].index, 0);
965 /* Our playlist is now the current list */
966 if (!initialize(NULL, true))
967 goto exit;
970 display_playlist();
971 update = true;
972 break;
974 case TREE_CONTEXT:
975 #ifdef TREE_CONTEXT2
976 case TREE_CONTEXT2:
977 #endif
979 /* ON+PLAY menu */
980 int ret;
982 /* Wait for the user to release the TREE_RUN key */
983 while((button_get(true)) != (TREE_RUN | BUTTON_REL)) {};
985 ret = onplay_menu(INDEX(viewer.cursor_pos));
987 if (ret < 0)
989 ret = true;
990 goto exit;
992 else if (ret > 0)
994 /* Playlist changed */
995 update_first_index();
996 update_playlist(false);
997 if (viewer.num_tracks <= 0)
998 exit = true;
1000 else
1001 display_playlist();
1003 update = true;
1004 break;
1007 case TREE_MENU:
1008 if (viewer_menu())
1010 ret = true;
1011 goto exit;
1014 display_playlist();
1015 update = true;
1016 break;
1018 case BUTTON_NONE:
1019 status_draw(false);
1020 break;
1022 default:
1023 if(default_event_handler(button) == SYS_USB_CONNECTED)
1025 ret = true;
1026 goto exit;
1028 break;
1031 if (update && !exit)
1033 lcd_stop_scroll();
1035 if (viewer.cursor_pos >
1036 (viewer.last_display_index - viewer.first_display_index))
1038 /* Cursor position is invalid */
1039 put_cursorxy(CURSOR_X, CURSOR_Y + viewer.cursor_pos, false);
1040 viewer.cursor_pos =
1041 viewer.last_display_index - viewer.first_display_index;
1042 put_cursorxy(CURSOR_X, CURSOR_Y + viewer.cursor_pos, true);
1045 if (viewer.cursor_pos != old_cursor_pos &&
1046 old_cursor_pos <=
1047 (viewer.last_display_index - viewer.first_display_index))
1048 /* Stop scrolling previous line */
1049 update_display_line(old_cursor_pos, false);
1051 /* Scroll line at new cursor position */
1052 update_display_line(viewer.cursor_pos, true);
1054 lcd_update();
1056 old_cursor_pos = viewer.cursor_pos;
1057 cursor_on = true;
1058 update = false;
1062 exit:
1063 if (viewer.playlist)
1064 playlist_close(viewer.playlist);
1065 return ret;