Experimenting with USER
[kugel-rb.git] / apps / playlist_viewer.c
blob71059aa5939de6427f8c21d39bc088df71467e0c
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"
36 #ifdef HAVE_LCD_BITMAP
37 #include "widgets.h"
38 #endif
40 #include "lang.h"
42 #include "playlist_viewer.h"
44 /* Defines for LCD display purposes. Taken from tree.c */
45 #ifdef HAVE_LCD_BITMAP
46 #define CURSOR_X (global_settings.scrollbar && \
47 viewer.num_tracks>viewer.num_display_lines?1:0)
48 #define CURSOR_Y 0
49 #define CURSOR_WIDTH (global_settings.invert_cursor ? 0 : 4)
51 #define ICON_WIDTH ((viewer.char_width > 6) ? viewer.char_width : 6)
53 #define MARGIN_X ((global_settings.scrollbar && \
54 viewer.num_tracks > viewer.num_display_lines ? \
55 SCROLLBAR_WIDTH : 0) + CURSOR_WIDTH + \
56 (global_settings.playlist_viewer_icons ? \
57 ICON_WIDTH : 0))
58 #define MARGIN_Y (global_settings.statusbar ? STATUSBAR_HEIGHT : 0)
60 #define LINE_X 0
61 #define LINE_Y (global_settings.statusbar ? 1 : 0)
63 #define SCROLLBAR_X 0
64 #define SCROLLBAR_Y lcd_getymargin()
65 #define SCROLLBAR_WIDTH 6
66 #else
67 #define MARGIN_X 0
68 #define MARGIN_Y 0
69 #define LINE_X 2
70 #define LINE_Y 0
71 #define CURSOR_X 0
72 #define CURSOR_Y 0
73 #endif
75 /* Maximum number of tracks we can have loaded at one time */
76 #define MAX_PLAYLIST_ENTRIES 200
78 /* Default playlist name for saving */
79 #define DEFAULT_PLAYLIST_NAME "/viewer.m3u"
81 /* Index of track on display line _pos */
82 #define INDEX(_pos) (viewer.first_display_index - viewer.first_index + (_pos))
84 /* Global playlist viewer settings */
85 struct playlist_viewer_info {
86 struct playlist_info* playlist; /* playlist being viewed */
87 char *name_buffer; /* Buffer used to store track names */
88 int buffer_size; /* Size of name buffer */
90 int num_display_lines; /* Number of lines on lcd */
91 int line_height; /* Height (in pixels) of display line */
92 int char_width; /* Width (in pixels) of a character */
94 int num_tracks; /* Number of tracks in playlist */
95 int current_playing_track; /* Index of current playing track */
97 int num_loaded; /* Number of track entries loaded in viewer */
98 int first_index; /* Index of first loaded track */
99 int last_index; /* Index of last loaded track */
100 int first_display_index; /* Index of first track on display */
101 int last_display_index; /* Index of last track on display */
102 int cursor_pos; /* Line number of cursor */
104 int move_track; /* Playlist index of track to move or -1 */
107 /* Information about a specific track */
108 struct playlist_entry {
109 char *name; /* Formatted track name */
110 int index; /* Playlist index */
111 int display_index; /* Display index */
112 bool queued; /* Is track queued? */
115 static struct playlist_viewer_info viewer;
116 static struct playlist_entry tracks[MAX_PLAYLIST_ENTRIES];
118 /* Used when viewing playlists on disk */
119 static struct playlist_info temp_playlist;
121 #ifdef HAVE_LCD_BITMAP
122 extern unsigned char bitmap_icons_6x8[LastIcon][6];
123 #endif
125 static bool initialize(char* filename, bool reload);
126 static void load_playlist_entries(int start_index);
127 static void load_playlist_entries_r(int end_index);
128 static int load_entry(int index, int pos, char* p, int size);
129 static void format_name(char* dest, char* src);
130 static void format_line(struct playlist_entry* track, char* str, int len);
131 static void display_playlist(void);
132 static void update_display_line(int line, bool scroll);
133 static void scroll_display(int lines);
134 static void update_first_index(void);
135 static bool update_playlist(bool force);
136 #ifdef BUTTON_ON
137 static int onplay_menu(int index);
138 #endif
139 static bool viewer_menu(void);
140 static bool show_icons(void);
141 static bool show_indices(void);
142 static bool track_display(void);
143 static bool save_playlist(void);
145 /* Initialize the playlist viewer. */
146 static bool initialize(char* filename, bool reload)
148 char* buffer;
149 int buffer_size;
150 bool is_playing = mpeg_status() & MPEG_STATUS_PLAY;
152 if (!filename && !is_playing)
153 /* Nothing is playing, exit */
154 return false;
156 buffer = plugin_get_buffer(&buffer_size);
157 if (!buffer)
158 return false;
160 if (!filename)
161 viewer.playlist = NULL;
162 else
164 /* Viewing playlist on disk */
165 char *dir, *file, *temp_ptr;
166 char *index_buffer = NULL;
167 int index_buffer_size = 0;
169 viewer.playlist = &temp_playlist;
171 /* Separate directory from filename */
172 temp_ptr = strrchr(filename+1,'/');
173 if (temp_ptr)
175 *temp_ptr = 0;
176 dir = filename;
177 file = temp_ptr + 1;
179 else
181 dir = "/";
182 file = filename+1;
185 if (is_playing)
187 /* Something is playing, use half the plugin buffer for playlist
188 indices */
189 index_buffer_size = buffer_size / 2;
190 index_buffer = buffer;
193 playlist_create_ex(viewer.playlist, dir, file, index_buffer,
194 index_buffer_size, buffer+index_buffer_size,
195 buffer_size-index_buffer_size);
197 if (temp_ptr)
198 *temp_ptr = '/';
200 buffer += index_buffer_size;
201 buffer_size -= index_buffer_size;
204 viewer.name_buffer = buffer;
205 viewer.buffer_size = buffer_size;
207 #ifdef HAVE_LCD_BITMAP
209 char icon_chars[] = "MQ"; /* characters used as icons */
210 unsigned int i;
212 viewer.char_width = 0;
213 viewer.line_height = 0;
215 /* Use icon characters to calculate largest possible width/height so
216 that we set proper margins */
217 for (i=0; i<sizeof(icon_chars); i++)
219 char str[2];
220 int w, h;
222 snprintf(str, sizeof(str), "%c", icon_chars[i]);
223 lcd_getstringsize(str, &w, &h);
225 if (w > viewer.char_width)
226 viewer.char_width = w;
228 if (h > viewer.line_height)
230 viewer.line_height = h;
231 viewer.num_display_lines = (LCD_HEIGHT - MARGIN_Y)/h;
235 #else
236 viewer.num_display_lines = 2;
237 viewer.char_width = 1;
238 viewer.line_height = 1;
239 #endif
241 viewer.move_track = -1;
243 if (!reload)
245 viewer.cursor_pos = 0;
247 if (!viewer.playlist)
248 /* Start displaying at current playing track */
249 viewer.first_display_index = playlist_get_display_index() - 1;
250 else
251 viewer.first_display_index = 0;
253 update_first_index();
256 if (!update_playlist(true))
257 return false;
259 return true;
262 /* Load tracks starting at start_index */
263 static void load_playlist_entries(int start_index)
265 int num_entries = viewer.num_tracks - start_index;
266 char* p = viewer.name_buffer;
267 int remaining = viewer.buffer_size;
268 int i;
270 viewer.first_index = start_index;
272 if (num_entries > MAX_PLAYLIST_ENTRIES)
273 num_entries = MAX_PLAYLIST_ENTRIES;
275 for(i=0; i<num_entries; i++, start_index++)
277 int len = load_entry(start_index, i, p, remaining);
278 if (len < 0)
280 /* Out of name buffer space */
281 num_entries = i;
282 break;
285 p += len;
286 remaining -= len;
289 viewer.num_loaded = num_entries;
290 viewer.last_index = viewer.first_index + (viewer.num_loaded - 1);
293 /* Load tracks in reverse, ending at end_index */
294 static void load_playlist_entries_r(int end_index)
296 int num_entries = end_index;
297 char* p = viewer.name_buffer;
298 int remaining = viewer.buffer_size;
299 int i;
301 viewer.last_index = end_index;
303 if (num_entries >= MAX_PLAYLIST_ENTRIES)
304 num_entries = MAX_PLAYLIST_ENTRIES-1;
306 for(i=num_entries; i>=0; i--, end_index--)
308 int len = load_entry(end_index, i, p, remaining);
309 if (len < 0)
311 int j;
313 /* Out of name buffer space */
314 num_entries -= i;
316 /* Shift loaded tracks up such that first track is index 0 */
317 for (j=0; j<num_entries; j++, i++)
319 tracks[j].name = tracks[i].name;
320 tracks[j].index = tracks[i].index;
321 tracks[j].display_index = tracks[i].display_index;
322 tracks[j].queued = tracks[i].queued;
325 break;
328 p += len;
329 remaining -= len;
332 viewer.first_index = viewer.last_index - num_entries;
334 num_entries++;
335 if (!viewer.first_index &&
336 num_entries < viewer.num_tracks &&
337 num_entries < MAX_PLAYLIST_ENTRIES)
339 /* Lets see if we can load more data at the end of the list */
340 int max = viewer.num_tracks;
341 if (max > MAX_PLAYLIST_ENTRIES)
342 max = MAX_PLAYLIST_ENTRIES;
344 for (i = num_entries; i<max; i++)
346 int len = load_entry(num_entries, num_entries, p, remaining);
347 if (len < 0)
348 /* Out of name buffer space */
349 break;
351 p += len;
352 remaining -= len;
354 num_entries++;
355 viewer.last_index++;
359 viewer.num_loaded = num_entries;
362 /* Load track at playlist index. pos is the position in the tracks array and
363 p is a pointer to the name buffer (max size), Returns -1 if buffer is
364 full. */
365 static int load_entry(int index, int pos, char* p, int size)
367 struct playlist_track_info info;
368 int len;
369 int result = 0;
371 /* Playlist viewer orders songs based on display index. We need to
372 convert to real playlist index to access track */
373 index = (index + playlist_get_first_index(viewer.playlist)) %
374 viewer.num_tracks;
375 if (playlist_get_track_info(viewer.playlist, index, &info) < 0)
376 return -1;
378 len = strlen(info.filename) + 1;
380 if (len <= size)
382 strcpy(p, info.filename);
384 tracks[pos].name = p;
385 tracks[pos].index = info.index;
386 tracks[pos].display_index = info.display_index;
387 tracks[pos].queued = info.attr & PLAYLIST_ATTR_QUEUED;
389 result = len;
391 else
392 result = -1;
394 return result;
397 /* Format trackname for display purposes */
398 static void format_name(char* dest, char* src)
400 switch (global_settings.playlist_viewer_track_display)
402 case 0:
403 default:
405 /* Only display the mp3 filename */
406 char* p = strrchr(src, '/');
407 int len;
409 strcpy(dest, p+1);
410 len = strlen(dest);
412 /* Remove the extension */
413 if (!strcasecmp(&dest[len-4], ".mp3") ||
414 !strcasecmp(&dest[len-4], ".mp2") ||
415 !strcasecmp(&dest[len-4], ".mpa"))
416 dest[len-4] = '\0';
418 break;
420 case 1:
421 /* Full path */
422 strcpy(dest, src);
423 break;
427 /* Format display line */
428 static void format_line(struct playlist_entry* track, char* str, int len)
430 char name[MAX_PATH];
432 format_name(name, track->name);
434 if (global_settings.playlist_viewer_indices)
435 /* Display playlist index */
436 snprintf(str, len, "%d. %s", track->display_index, name);
437 else
438 snprintf(str, len, "%s", name);
442 /* Display tracks on screen */
443 static void display_playlist(void)
445 int i;
446 int num_display_tracks =
447 viewer.last_display_index - viewer.first_display_index;
449 lcd_clear_display();
451 #ifdef HAVE_LCD_BITMAP
452 lcd_setmargins(MARGIN_X, MARGIN_Y);
453 lcd_setfont(FONT_UI);
454 #endif
456 for (i=0; i<=num_display_tracks; i++)
458 if (global_settings.playlist_viewer_icons)
460 /* Icons */
461 if (tracks[INDEX(i)].index == viewer.current_playing_track)
463 /* Current playing track */
464 #ifdef HAVE_LCD_BITMAP
465 int offset=0;
466 if ( viewer.line_height > 8 )
467 offset = (viewer.line_height - 8) / 2;
468 lcd_bitmap(bitmap_icons_6x8[File],
469 CURSOR_X * 6 + CURSOR_WIDTH,
470 MARGIN_Y+(i*viewer.line_height) + offset,
471 6, 8, true);
472 #else
473 lcd_putc(LINE_X-1, i, File);
474 #endif
476 else if (tracks[INDEX(i)].index == viewer.move_track)
478 /* Track we are moving */
479 #ifdef HAVE_LCD_BITMAP
480 lcd_putsxy(CURSOR_X * 6 + CURSOR_WIDTH,
481 MARGIN_Y+(i*viewer.line_height), "M");
482 #else
483 lcd_putc(LINE_X-1, i, 'M');
484 #endif
486 else if (tracks[INDEX(i)].queued)
488 /* Queued track */
489 #ifdef HAVE_LCD_BITMAP
490 lcd_putsxy(CURSOR_X * 6 + CURSOR_WIDTH,
491 MARGIN_Y+(i*viewer.line_height), "Q");
492 #else
493 lcd_putc(LINE_X-1, i, 'Q');
494 #endif
498 update_display_line(i, false);
501 #ifdef HAVE_LCD_BITMAP
502 if (global_settings.scrollbar &&
503 (viewer.num_tracks > viewer.num_display_lines))
504 scrollbar(SCROLLBAR_X, SCROLLBAR_Y, SCROLLBAR_WIDTH - 1,
505 LCD_HEIGHT - SCROLLBAR_Y, viewer.num_tracks-1,
506 viewer.first_display_index, viewer.last_display_index,
507 VERTICAL);
508 #endif
510 put_cursorxy(CURSOR_X, CURSOR_Y + viewer.cursor_pos, true);
511 status_draw(true);
514 /* Scroll cursor or display by num lines */
515 static void scroll_display(int lines)
517 int new_index = viewer.first_display_index + viewer.cursor_pos + lines;
518 bool pagescroll = false;
519 bool wrap = false;
521 put_cursorxy(CURSOR_X, CURSOR_Y + viewer.cursor_pos, false);
523 if (lines > 1 || lines < -1)
524 pagescroll = true;
526 if (new_index < 0)
528 /* Wrap around if not pageup */
529 if (pagescroll)
530 new_index = 0;
531 else
533 new_index += viewer.num_tracks;
534 viewer.cursor_pos = viewer.num_display_lines-1;
535 wrap = true;
538 else if (new_index >= viewer.num_tracks)
540 /* Wrap around if not pagedown */
541 if (pagescroll)
542 new_index = viewer.num_tracks - 1;
543 else
545 new_index -= viewer.num_tracks;
546 viewer.cursor_pos = 0;
547 wrap = true;
551 if (new_index >= viewer.first_display_index &&
552 new_index <= viewer.last_display_index)
554 /* Just update the cursor */
555 viewer.cursor_pos = new_index - viewer.first_display_index;
557 else
559 /* New track is outside of display */
560 if (wrap)
561 viewer.first_display_index = new_index;
562 else
563 viewer.first_display_index = viewer.first_display_index + lines;
565 if (viewer.first_display_index < 0)
566 viewer.first_display_index = 0;
568 viewer.last_display_index =
569 viewer.first_display_index + (viewer.num_display_lines - 1);
570 if (viewer.last_display_index >= viewer.num_tracks)
572 /* display as many tracks as possible on screen */
573 if (viewer.first_display_index > 0)
575 viewer.first_display_index -=
576 (viewer.last_display_index - viewer.num_tracks + 1);
577 if (viewer.first_display_index < 0)
578 viewer.first_display_index = 0;
581 viewer.last_display_index = viewer.num_tracks - 1;
584 if (viewer.cursor_pos >
585 (viewer.last_display_index - viewer.first_display_index))
586 viewer.cursor_pos =
587 viewer.last_display_index - viewer.first_display_index;
589 /* Load more data if needed */
590 if (viewer.first_display_index < viewer.first_index)
591 load_playlist_entries_r(viewer.last_display_index);
592 else if (viewer.last_display_index > viewer.last_index)
593 load_playlist_entries(viewer.first_display_index);
595 display_playlist();
598 put_cursorxy(CURSOR_X, CURSOR_Y + viewer.cursor_pos, true);
601 /* Update lcd line. Scroll line if requested */
602 static void update_display_line(int line, bool scroll)
604 char str[MAX_PATH + 16];
606 format_line(&tracks[INDEX(line)], str, sizeof(str));
608 if (scroll)
610 #ifdef HAVE_LCD_BITMAP
611 if (global_settings.invert_cursor)
612 lcd_puts_scroll_style(LINE_X, line, str, STYLE_INVERT);
613 else
614 #endif
615 lcd_puts_scroll(LINE_X, line, str);
617 else
618 lcd_puts(LINE_X, line, str);
621 /* Update first index, if necessary, to put as much as possible on the
622 screen */
623 static void update_first_index(void)
625 /* viewer.num_tracks may be invalid at this point */
626 int num_tracks = playlist_amount_ex(viewer.playlist);
628 if ((num_tracks - viewer.first_display_index) < viewer.num_display_lines)
630 /* Try to display as much as possible */
631 int old_index = viewer.first_display_index;
633 viewer.first_display_index = num_tracks - viewer.num_display_lines;
634 if (viewer.first_display_index < 0)
635 viewer.first_display_index = 0;
637 /* Cursor should still point at current track */
638 viewer.cursor_pos += old_index - viewer.first_display_index;
642 /* Update playlist in case something has changed or forced */
643 static bool update_playlist(bool force)
645 if (!viewer.playlist)
646 playlist_get_resume_info(&viewer.current_playing_track);
647 else
648 viewer.current_playing_track = -1;
650 if (force || playlist_amount_ex(viewer.playlist) != viewer.num_tracks)
652 int index;
654 /* Reload tracks */
655 viewer.num_tracks = playlist_amount_ex(viewer.playlist);
656 if (viewer.num_tracks < 0)
657 return false;
659 index = viewer.first_display_index;
661 load_playlist_entries(index);
663 if (viewer.num_loaded <= 0)
664 return false;
666 viewer.first_display_index = viewer.first_index;
667 viewer.last_display_index =
668 viewer.first_index + viewer.num_display_lines - 1;
669 if (viewer.last_display_index >= viewer.num_tracks)
670 viewer.last_display_index = viewer.num_tracks - 1;
673 display_playlist();
675 return true;
678 #ifdef BUTTON_ON
679 /* Menu of playlist commands. Invoked via ON+PLAY on main viewer screen.
680 Returns -1 if USB attached, 0 if no playlist change, and 1 if playlist
681 changed. */
682 static int onplay_menu(int index)
684 struct menu_item items[3]; /* increase this if you add entries! */
685 int m, i=0, result, ret = 0;
686 bool current = (tracks[index].index == viewer.current_playing_track);
688 items[i].desc = str(LANG_REMOVE);
689 items[i].voice_id = LANG_REMOVE;
690 i++;
692 items[i].desc = str(LANG_MOVE);
693 items[i].voice_id = LANG_MOVE;
694 i++;
696 items[i].desc = str(LANG_FILE_OPTIONS);
697 items[i].voice_id = LANG_FILE_OPTIONS;
698 i++;
700 m = menu_init(items, i, NULL, NULL, NULL, NULL);
701 result = menu_show(m);
702 if (result == MENU_ATTACHED_USB)
703 ret = -1;
704 else if (result >= 0)
706 /* Abort current move */
707 viewer.move_track = -1;
709 switch (result)
711 case 0:
712 /* delete track */
713 if (current)
714 mpeg_stop();
716 playlist_delete(viewer.playlist, tracks[index].index);
718 if (current)
720 /* Start playing new track except if it's the last track
721 in the playlist and repeat mode is disabled */
722 if (tracks[index].display_index != viewer.num_tracks ||
723 global_settings.repeat_mode == REPEAT_ALL)
725 talk_buffer_steal(); /* will use the mp3 buffer */
726 mpeg_play(0);
727 viewer.current_playing_track = -1;
731 ret = 1;
732 break;
733 case 1:
734 /* move track */
735 viewer.move_track = tracks[index].index;
736 ret = 0;
737 break;
738 case 2:
740 onplay(tracks[index].name, TREE_ATTR_MPA);
742 if (!viewer.playlist)
743 ret = 1;
744 else
745 ret = 0;
747 break;
752 menu_exit(m);
754 return ret;
756 #endif
758 /* Menu of viewer options. Invoked via F1(r) or Menu(p). */
759 static bool viewer_menu(void)
761 int m;
762 bool result;
764 struct menu_item items[] = {
765 { STR(LANG_SHOW_ICONS), show_icons },
766 { STR(LANG_SHOW_INDICES), show_indices },
767 { STR(LANG_TRACK_DISPLAY), track_display },
768 { STR(LANG_SAVE_DYNAMIC_PLAYLIST), save_playlist },
771 m=menu_init( items, sizeof(items) / sizeof(*items), NULL,
772 NULL, NULL, NULL );
773 result = menu_run(m);
774 menu_exit(m);
776 settings_save();
778 return result;
781 /* Show icons in viewer? */
782 static bool show_icons(void)
784 return set_bool(str(LANG_SHOW_ICONS),
785 &global_settings.playlist_viewer_icons);
788 /* Show indices of tracks? */
789 static bool show_indices(void)
791 return set_bool(str(LANG_SHOW_INDICES),
792 &global_settings.playlist_viewer_indices);
795 /* How to display a track */
796 static bool track_display(void)
798 struct opt_items names[] = {
799 { STR(LANG_DISPLAY_TRACK_NAME_ONLY) },
800 { STR(LANG_DISPLAY_FULL_PATH) }
803 return set_option(str(LANG_TRACK_DISPLAY),
804 &global_settings.playlist_viewer_track_display, INT, names, 2, NULL);
807 /* Save playlist to disk */
808 static bool save_playlist(void)
810 char filename[MAX_PATH+1];
812 strncpy(filename, DEFAULT_PLAYLIST_NAME, sizeof(filename));
814 if (!kbd_input(filename, sizeof(filename)))
816 playlist_save(viewer.playlist, filename);
818 /* reload in case playlist was saved to cwd */
819 reload_directory();
822 return false;
825 /* View current playlist */
826 bool playlist_viewer(void)
828 return playlist_viewer_ex(NULL);
831 /* Main viewer function. Filename identifies playlist to be viewed. If NULL,
832 view current playlist. */
833 bool playlist_viewer_ex(char* filename)
835 bool ret = false; /* return value */
836 bool exit=false; /* exit viewer */
837 bool update=true; /* update display */
838 bool cursor_on=true; /* used for flashing cursor */
839 int old_cursor_pos; /* last cursor position */
840 int button;
842 if (!initialize(filename, false))
843 goto exit;
845 old_cursor_pos = viewer.cursor_pos;
847 while (!exit)
849 int track;
851 if (!viewer.playlist && !(mpeg_status() & MPEG_STATUS_PLAY))
853 /* Play has stopped */
854 #ifdef HAVE_LCD_CHARCELLS
855 splash(HZ, true, str(LANG_END_PLAYLIST_PLAYER));
856 #else
857 splash(HZ, true, str(LANG_END_PLAYLIST_RECORDER));
858 #endif
859 status_set_playmode(STATUS_STOP);
860 goto exit;
863 if (viewer.move_track != -1 || !cursor_on)
865 /* Flash cursor to identify that we are moving a track */
866 cursor_on = !cursor_on;
867 #ifdef HAVE_LCD_BITMAP
868 if (global_settings.invert_cursor)
870 lcd_invertrect(
871 MARGIN_X, MARGIN_Y+(viewer.cursor_pos*viewer.line_height),
872 LCD_WIDTH, viewer.line_height);
873 lcd_invertscroll(LINE_X, viewer.cursor_pos);
875 else
876 put_cursorxy(CURSOR_X, CURSOR_Y + viewer.cursor_pos,
877 cursor_on);
879 lcd_update_rect(
880 0, MARGIN_Y + (viewer.cursor_pos * viewer.line_height),
881 LCD_WIDTH, viewer.line_height);
882 #else
883 put_cursorxy(CURSOR_X, CURSOR_Y + viewer.cursor_pos, cursor_on);
884 lcd_update();
885 #endif
888 if (!viewer.playlist)
889 playlist_get_resume_info(&track);
890 else
891 track = -1;
893 if (track != viewer.current_playing_track ||
894 playlist_amount_ex(viewer.playlist) != viewer.num_tracks)
896 /* Playlist has changed (new track started?) */
897 update_first_index();
898 if (!update_playlist(false))
899 goto exit;
900 else
901 update = true;
903 /* Abort move on playlist change */
904 viewer.move_track = -1;
907 /* Timeout so we can determine if play status has changed */
908 button = button_get_w_tmo(HZ/2);
910 switch (button)
912 #ifdef HAVE_RECORDER_KEYPAD
913 case BUTTON_OFF:
914 case BUTTON_LEFT:
915 #else
916 case BUTTON_STOP:
917 #endif
918 exit = true;
919 break;
921 #ifdef HAVE_RECORDER_KEYPAD
922 case BUTTON_UP:
923 case BUTTON_UP | BUTTON_REPEAT:
924 #else
925 case BUTTON_LEFT:
926 case BUTTON_LEFT | BUTTON_REPEAT:
927 #endif
928 scroll_display(-1);
929 update = true;
930 break;
932 #ifdef HAVE_RECORDER_KEYPAD
933 case BUTTON_DOWN:
934 case BUTTON_DOWN | BUTTON_REPEAT:
935 #else
936 case BUTTON_RIGHT:
937 case BUTTON_RIGHT | BUTTON_REPEAT:
938 #endif
939 scroll_display(1);
940 update = true;
941 break;
943 #ifdef BUTTON_ON
944 #ifdef HAVE_RECORDER_KEYPAD
945 case BUTTON_ON | BUTTON_UP:
946 case BUTTON_ON | BUTTON_UP | BUTTON_REPEAT:
947 #else
948 case BUTTON_ON | BUTTON_LEFT:
949 case BUTTON_ON | BUTTON_LEFT | BUTTON_REPEAT:
950 #endif
951 /* Pageup */
952 scroll_display(-viewer.num_display_lines);
953 update = true;
954 break;
956 #ifdef HAVE_RECORDER_KEYPAD
957 case BUTTON_ON | BUTTON_DOWN:
958 case BUTTON_ON | BUTTON_DOWN | BUTTON_REPEAT:
959 #else
960 case BUTTON_ON | BUTTON_RIGHT:
961 case BUTTON_ON | BUTTON_RIGHT | BUTTON_REPEAT:
962 #endif
963 /* Pagedown */
964 scroll_display(viewer.num_display_lines);
965 update = true;
966 break;
967 #endif /* BUTTON_ON */
969 #ifdef HAVE_RECORDER_KEYPAD
970 case BUTTON_RIGHT:
971 #endif
972 case BUTTON_PLAY:
973 if (viewer.move_track >= 0)
975 /* Move track */
976 int ret;
978 ret = playlist_move(viewer.playlist, viewer.move_track,
979 tracks[INDEX(viewer.cursor_pos)].index);
980 if (ret < 0)
981 splash(HZ, true, str(LANG_MOVE_FAILED));
983 update_playlist(true);
984 viewer.move_track = -1;
986 else if (!viewer.playlist)
988 /* Stop current track and play new track */
989 mpeg_stop();
990 playlist_start(tracks[INDEX(viewer.cursor_pos)].index, 0);
991 status_set_playmode(STATUS_PLAY);
992 update_playlist(false);
994 else
996 /* Play track from playlist on disk */
997 mpeg_stop();
999 /* New playlist */
1000 if (playlist_set_current(viewer.playlist) < 0)
1001 goto exit;
1003 playlist_start(tracks[INDEX(viewer.cursor_pos)].index, 0);
1004 status_set_playmode(STATUS_PLAY);
1006 /* Our playlist is now the current list */
1007 if (!initialize(NULL, true))
1008 goto exit;
1011 display_playlist();
1012 update = true;
1013 break;
1015 #ifdef BUTTON_ON
1016 case BUTTON_ON | BUTTON_PLAY:
1018 /* ON+PLAY menu */
1019 int ret;
1021 ret = onplay_menu(INDEX(viewer.cursor_pos));
1023 if (ret < 0)
1025 ret = true;
1026 goto exit;
1028 else if (ret > 0)
1030 /* Playlist changed */
1031 update_first_index();
1032 update_playlist(false);
1033 if (viewer.num_tracks <= 0)
1034 exit = true;
1036 else
1037 display_playlist();
1039 update = true;
1040 break;
1042 #endif /* BUTTON_ON */
1043 #ifdef HAVE_RECORDER_KEYPAD
1044 case BUTTON_F1:
1045 #else
1046 case BUTTON_MENU:
1047 #endif
1048 if (viewer_menu())
1050 ret = true;
1051 goto exit;
1054 display_playlist();
1055 update = true;
1056 break;
1058 case SYS_USB_CONNECTED:
1059 usb_screen();
1060 ret = true;
1061 goto exit;
1062 break;
1064 case BUTTON_NONE:
1065 status_draw(false);
1066 break;
1069 if (update && !exit)
1071 lcd_stop_scroll();
1073 if (viewer.cursor_pos >
1074 (viewer.last_display_index - viewer.first_display_index))
1076 /* Cursor position is invalid */
1077 put_cursorxy(CURSOR_X, CURSOR_Y + viewer.cursor_pos, false);
1078 viewer.cursor_pos =
1079 viewer.last_display_index - viewer.first_display_index;
1080 put_cursorxy(CURSOR_X, CURSOR_Y + viewer.cursor_pos, true);
1083 if (viewer.cursor_pos != old_cursor_pos &&
1084 old_cursor_pos <=
1085 (viewer.last_display_index - viewer.first_display_index))
1086 /* Stop scrolling previous line */
1087 update_display_line(old_cursor_pos, false);
1089 /* Scroll line at new cursor position */
1090 update_display_line(viewer.cursor_pos, true);
1092 lcd_update();
1094 old_cursor_pos = viewer.cursor_pos;
1095 cursor_on = true;
1096 update = false;
1100 exit:
1101 if (viewer.playlist)
1102 playlist_close(viewer.playlist);
1103 return ret;