Improved ff/rw max step calculation (patch #882931 by Craigh Sather)
[kugel-rb.git] / apps / bookmark.c
blob0c12026816d4bbf3b315a65b56b03f7bc952ba78
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
9 * Copyright (C) 2003 by Benjamin Metzler
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
17 ****************************************************************************/
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <stdbool.h>
25 #include "applimits.h"
26 #include "lcd.h"
27 #include "button.h"
28 #include "usb.h"
29 #include "mpeg.h"
30 #include "wps.h"
31 #include "settings.h"
32 #include "bookmark.h"
33 #include "dir.h"
34 #include "status.h"
35 #include "system.h"
36 #include "errno.h"
37 #include "icons.h"
38 #include "atoi.h"
39 #include "string.h"
40 #include "menu.h"
41 #include "lang.h"
42 #include "screens.h"
43 #include "status.h"
44 #include "debug.h"
45 #include "kernel.h"
46 #include "sprintf.h"
47 #include "talk.h"
49 #define MAX_BOOKMARKS 10
50 #define MAX_BOOKMARK_SIZE 350
51 #define RECENT_BOOKMARK_FILE ROCKBOX_DIR "/most-recent.bmark"
53 static bool add_bookmark(char* bookmark_file_name, char* bookmark);
54 static bool bookmark_load_menu(void);
55 static bool check_bookmark(char* bookmark);
56 static char* create_bookmark(void);
57 static bool delete_bookmark(char* bookmark_file_name, int bookmark_id);
58 static void display_bookmark(char* bookmark,
59 int bookmark_id,
60 int bookmark_count);
61 static void say_bookmark(char* bookmark,
62 int bookmark_id);
63 static bool generate_bookmark_file_name(char *in,
64 char *out,
65 unsigned int max_length);
66 static char* get_bookmark(char* bookmark_file, int bookmark_count);
67 static bool parse_bookmark(char *bookmark,
68 int *resume_index,
69 int *resume_offset,
70 int *resume_seed,
71 int *resume_first_index,
72 char* resume_file,
73 unsigned int resume_file_size,
74 int* ms,
75 int * repeat_mode,
76 bool *shuffle,
77 char* file_name,
78 unsigned int max_file_name_size);
79 static char* select_bookmark(char* bookmark_file_name);
80 static bool system_check(void);
81 static bool write_bookmark(bool create_bookmark_file);
82 static int get_bookmark_count(char* bookmark_file_name);
84 static char global_temp_buffer[MAX_PATH+1];
85 static char global_bookmark_file_name[MAX_PATH];
86 static char global_read_buffer[MAX_BOOKMARK_SIZE];
87 static char global_bookmark[MAX_BOOKMARK_SIZE];
89 /* ----------------------------------------------------------------------- */
90 /* Displays the bookmark menu options for the user to decide. This is an */
91 /* interface function. */
92 /* ----------------------------------------------------------------------- */
93 bool bookmark_menu(void)
95 int m;
96 bool result;
98 struct menu_item items[] = {
99 { STR(LANG_BOOKMARK_MENU_CREATE), bookmark_create_menu},
100 { STR(LANG_BOOKMARK_MENU_LIST), bookmark_load_menu},
101 { STR(LANG_BOOKMARK_MENU_RECENT_BOOKMARKS), bookmark_mrb_load},
104 m=menu_init( items, sizeof items / sizeof(struct menu_item), NULL,
105 NULL, NULL, NULL);
107 #ifdef HAVE_LCD_CHARCELLS
108 status_set_param(true);
109 #endif
110 result = menu_run(m);
111 #ifdef HAVE_LCD_CHARCELLS
112 status_set_param(false);
113 #endif
114 menu_exit(m);
116 settings_save();
118 return result;
121 /* ----------------------------------------------------------------------- */
122 /* This is the interface function from the main menu. */
123 /* ----------------------------------------------------------------------- */
124 bool bookmark_create_menu(void)
126 write_bookmark(true);
127 return false;
130 /* ----------------------------------------------------------------------- */
131 /* This function acts as the load interface from the main menu */
132 /* This function determines the bookmark file name and then loads that file*/
133 /* for the user. The user can then select a bookmark to load. */
134 /* If no file/directory is currently playing, the menu item does not work. */
135 /* ----------------------------------------------------------------------- */
136 static bool bookmark_load_menu(void)
138 bool success = true;
139 int offset;
140 int seed;
141 int index;
142 char* bookmark;
144 if(!system_check())
145 return false;
146 else
148 char* name = playlist_get_name(NULL, global_temp_buffer,
149 sizeof(global_temp_buffer));
150 if (generate_bookmark_file_name(name,
151 global_bookmark_file_name,
152 sizeof(global_bookmark_file_name)))
154 bookmark = select_bookmark(global_bookmark_file_name);
155 if (!bookmark)
156 return false; /* User exited without selecting a bookmark */
158 success = parse_bookmark(bookmark,
159 &index,
160 &offset,
161 &seed,
162 NULL,
163 global_temp_buffer,
164 sizeof(global_temp_buffer),
165 NULL,
166 &global_settings.repeat_mode,
167 &global_settings.playlist_shuffle,
168 NULL, 0);
170 else
172 /* something bad happened while creating bookmark name*/
173 success = false;
176 if (success)
177 bookmark_play(global_temp_buffer, index, offset, seed);
180 return success;
183 /* ----------------------------------------------------------------------- */
184 /* Gives the user a list of the Most Recent Bookmarks. This is an */
185 /* interface function */
186 /* ----------------------------------------------------------------------- */
187 bool bookmark_mrb_load()
189 bool success = true;
190 int offset;
191 int seed;
192 int index;
193 char* bookmark;
195 bookmark = select_bookmark(RECENT_BOOKMARK_FILE);
196 if (!bookmark)
197 return false; /* User exited without selecting a bookmark */
199 success = parse_bookmark(bookmark,
200 &index,
201 &offset,
202 &seed,
203 NULL,
204 global_temp_buffer,
205 sizeof(global_temp_buffer),
206 NULL,
207 &global_settings.repeat_mode,
208 &global_settings.playlist_shuffle,
209 NULL, 0);
211 if (success)
212 bookmark_play(global_temp_buffer, index, offset, seed);
214 return success;
218 /* ----------------------------------------------------------------------- */
219 /* This function handles an autobookmark creation. This is an interface */
220 /* function. */
221 /* ----------------------------------------------------------------------- */
222 bool bookmark_autobookmark(void)
224 /* prompts the user as to create a bookmark */
225 bool done = false;
226 int key = 0;
228 if (!system_check())
229 return false;
231 mpeg_pause(); /* first pause playback */
232 switch (global_settings.autocreatebookmark)
234 case BOOKMARK_YES:
235 return write_bookmark(true);
237 case BOOKMARK_NO:
238 return false;
240 case BOOKMARK_RECENT_ONLY_YES:
241 return write_bookmark(false);
244 /* Prompting user to confirm bookmark creation */
245 lcd_clear_display();
246 #ifdef HAVE_LCD_BITMAP
247 lcd_puts(0,0, str(LANG_AUTO_BOOKMARK_QUERY));
248 lcd_puts(0,1, str(LANG_CONFIRM_WITH_PLAY_RECORDER));
249 lcd_puts(0,2, str(LANG_CANCEL_WITH_ANY_RECORDER));
250 #else
251 status_draw(false);
252 lcd_puts(0,0, str(LANG_AUTO_BOOKMARK_QUERY));
253 lcd_puts(0,1,str(LANG_RESUME_CONFIRM_PLAYER));
254 #endif
255 lcd_update();
257 while (!done)
259 /* Wait for a key to be pushed */
260 key = button_get(true);
261 switch (key)
263 case BUTTON_DOWN | BUTTON_REL:
264 case BUTTON_ON | BUTTON_REL:
265 #ifdef HAVE_RECORDER_KEYPAD
266 case BUTTON_OFF | BUTTON_REL:
267 case BUTTON_RIGHT | BUTTON_REL:
268 case BUTTON_UP | BUTTON_REL:
269 #endif
270 case BUTTON_LEFT | BUTTON_REL:
271 done = true;
272 break;
274 case BUTTON_PLAY | BUTTON_REL:
275 if (global_settings.autocreatebookmark ==
276 BOOKMARK_RECENT_ONLY_ASK)
277 write_bookmark(false);
278 else
279 write_bookmark(true);
280 done = true;
281 break;
283 case SYS_USB_CONNECTED:
284 usb_screen();
285 #ifdef HAVE_LCD_CHARCELLS
286 status_set_param(true);
287 #endif
288 return false;
291 return true;
294 /* ----------------------------------------------------------------------- */
295 /* This function takes the current current resume information and writes */
296 /* that to the beginning of the bookmark file. */
297 /* This file will contain N number of bookmarks in the following format: */
298 /* resume_index*resume_offset*resume_seed*resume_first_index* */
299 /* resume_file*milliseconds*MP3 Title* */
300 /* ------------------------------------------------------------------------*/
301 static bool write_bookmark(bool create_bookmark_file)
303 bool success=false;
304 char* bookmark;
306 if (!system_check())
307 return false; /* something didn't happen correctly, do nothing */
309 bookmark = create_bookmark();
310 if (!bookmark)
311 return false; /* something didn't happen correctly, do nothing */
313 if (global_settings.usemrb)
314 success = add_bookmark(RECENT_BOOKMARK_FILE, bookmark);
317 /* writing the bookmark */
318 if (create_bookmark_file)
320 char* name = playlist_get_name(NULL, global_temp_buffer,
321 sizeof(global_temp_buffer));
322 if (generate_bookmark_file_name(name,
323 global_bookmark_file_name,
324 sizeof(global_bookmark_file_name)))
326 success = add_bookmark(global_bookmark_file_name, bookmark);
330 if (success)
331 splash(HZ, true, str(LANG_BOOKMARK_CREATE_SUCCESS));
332 else
333 splash(HZ, true, str(LANG_BOOKMARK_CREATE_FAILURE));
335 return true;
338 /* ----------------------------------------------------------------------- */
339 /* This function adds a bookmark to a file. */
340 /* ------------------------------------------------------------------------*/
341 static bool add_bookmark(char* bookmark_file_name, char* bookmark)
343 int temp_bookmark_file = 0;
344 int bookmark_file = 0;
345 int bookmark_count = 0;
346 char* playlist = NULL;
347 char* cp;
348 char* tmp;
349 int len = 0;
350 bool unique = false;
352 /* Opening up a temp bookmark file */
353 snprintf(global_temp_buffer, sizeof(global_temp_buffer),
354 "%s.tmp", bookmark_file_name);
355 temp_bookmark_file = open(global_temp_buffer,
356 O_WRONLY | O_CREAT | O_TRUNC);
357 if (temp_bookmark_file < 0)
358 return false; /* can't open the temp file */
360 if (!strcmp(bookmark_file_name,RECENT_BOOKMARK_FILE) &&
361 (global_settings.usemrb == BOOKMARK_UNIQUE_ONLY))
363 playlist = strchr(bookmark,'/');
364 cp = strrchr(bookmark,';');
365 len = cp - playlist;
366 unique = true;
369 /* Writing the new bookmark to the begining of the temp file */
370 write(temp_bookmark_file, bookmark, strlen(bookmark));
371 write(temp_bookmark_file, "\n", 1);
372 bookmark_count++;
374 /* Reading in the previous bookmarks and writing them to the temp file */
375 bookmark_file = open(bookmark_file_name, O_RDONLY);
376 if (bookmark_file >= 0)
378 while (read_line(bookmark_file, global_read_buffer,
379 sizeof(global_read_buffer)))
381 /* The MRB has a max of MAX_BOOKMARKS in it */
382 /* This keeps it from getting too large */
383 if ((strcmp(bookmark_file_name,RECENT_BOOKMARK_FILE)==0))
385 if(bookmark_count >= MAX_BOOKMARKS)
386 break;
389 cp = strchr(global_read_buffer,'/');
390 tmp = strrchr(global_read_buffer,';');
391 if (check_bookmark(global_read_buffer) &&
392 (!unique || len != tmp -cp || strncmp(playlist,cp,len)))
394 bookmark_count++;
395 write(temp_bookmark_file, global_read_buffer,
396 strlen(global_read_buffer));
397 write(temp_bookmark_file, "\n", 1);
400 close(bookmark_file);
402 close(temp_bookmark_file);
404 remove(bookmark_file_name);
405 rename(global_temp_buffer, bookmark_file_name);
407 return true;
411 /* ----------------------------------------------------------------------- */
412 /* This function takes the system resume data and formats it into a valid */
413 /* bookmark. */
414 /* ----------------------------------------------------------------------- */
415 static char* create_bookmark()
417 int resume_index = 0;
418 char *file;
420 /* grab the currently playing track */
421 struct mp3entry *id3 = mpeg_current_track();
422 if(!id3)
423 return NULL;
425 /* Get some basic resume information */
426 /* queue_resume and queue_resume_index are not used and can be ignored.*/
427 playlist_get_resume_info(&resume_index);
429 /* Get the currently playing file minus the path */
430 /* This is used when displaying the available bookmarks */
431 file = strrchr(id3->path,'/');
432 if(NULL == file)
433 return NULL;
435 /* create the bookmark */
436 snprintf(global_bookmark, sizeof(global_bookmark),
437 "%d;%d;%d;%d;%d;%d;%d;%s;%s",
438 resume_index,
439 id3->offset,
440 playlist_get_seed(NULL),
442 id3->elapsed,
443 global_settings.repeat_mode,
444 global_settings.playlist_shuffle,
445 playlist_get_name(NULL, global_temp_buffer,
446 sizeof(global_temp_buffer)),
447 file+1);
449 /* checking to see if the bookmark is valid */
450 if (check_bookmark(global_bookmark))
451 return global_bookmark;
452 else
453 return NULL;
456 static bool check_bookmark(char* bookmark)
458 return parse_bookmark(bookmark,
459 NULL,NULL,NULL, NULL,
460 NULL,0,NULL,NULL,
461 NULL, NULL, 0);
464 /* ----------------------------------------------------------------------- */
465 /* This function will determine if an autoload is necessary. This is an */
466 /* interface function. */
467 /* ------------------------------------------------------------------------*/
468 bool bookmark_autoload(char* file)
470 int key;
471 int fd;
472 bool done = false;
474 if(global_settings.autoloadbookmark == BOOKMARK_NO)
475 return false;
477 /*Checking to see if a bookmark file exists.*/
478 if(!generate_bookmark_file_name(file,
479 global_bookmark_file_name,
480 sizeof(global_bookmark_file_name)))
482 return false;
485 fd = open(global_bookmark_file_name, O_RDONLY);
486 if(fd<0)
487 return false;
488 if(-1 == lseek(fd, 0, SEEK_END))
490 close(fd);
491 return false;
493 close(fd);
495 if(global_settings.autoloadbookmark == BOOKMARK_YES)
497 return bookmark_load(global_bookmark_file_name, true);
499 else
501 while (button_get(false)); /* clear button queue */
502 /* Prompting user to confirm bookmark load */
503 lcd_clear_display();
504 #ifdef HAVE_LCD_BITMAP
505 lcd_puts_scroll(0,0, str(LANG_BOOKMARK_AUTOLOAD_QUERY));
506 lcd_puts(0,1, str(LANG_CONFIRM_WITH_PLAY_RECORDER));
507 lcd_puts(0,2, str(LANG_BOOKMARK_SELECT_LIST_BOOKMARKS));
508 lcd_puts(0,3, str(LANG_CANCEL_WITH_ANY_RECORDER));
509 #else
510 status_draw(false);
511 lcd_puts_scroll(0,0, str(LANG_BOOKMARK_AUTOLOAD_QUERY));
512 lcd_puts(0,1,str(LANG_RESUME_CONFIRM_PLAYER));
513 #endif
514 lcd_update();
516 sleep(100);
518 while(!done)
520 /* Wait for a key to be pushed */
521 while (button_get(false)); /* clear button queue */
522 key = button_get(true);
523 switch(key)
525 default:
526 return false;
527 #ifdef HAVE_LCD_BITMAP
528 case BUTTON_DOWN:
529 return bookmark_load(global_bookmark_file_name, false);
530 #endif
531 case BUTTON_PLAY:
532 return bookmark_load(global_bookmark_file_name, true);
533 case SYS_USB_CONNECTED:
534 status_set_playmode(STATUS_STOP);
535 usb_screen();
536 #ifdef HAVE_LCD_CHARCELLS
537 status_set_param(true);
538 #endif
539 return true;
542 return true;
546 /* ----------------------------------------------------------------------- */
547 /* This function loads the bookmark information into the resume memory. */
548 /* This is an interface function. */
549 /* ------------------------------------------------------------------------*/
550 bool bookmark_load(char* file, bool autoload)
552 int fd;
553 bool success = true;
554 int offset;
555 int seed;
556 int index;
557 char* bookmark = NULL;;
559 if(autoload)
561 fd = open(file, O_RDONLY);
562 if(fd >= 0)
564 if(read_line(fd, global_read_buffer, sizeof(global_read_buffer)))
565 bookmark=global_read_buffer;
566 close(fd);
569 else
571 /* This is not an auto-load, so list the bookmarks */
572 bookmark=select_bookmark(file);
573 if(!bookmark)
574 return true; /* User exited without selecting a bookmark */
577 if(bookmark)
579 success = parse_bookmark(bookmark,
580 &index,
581 &offset,
582 &seed,
583 NULL,
584 global_temp_buffer,
585 sizeof(global_temp_buffer),
586 NULL,
587 &global_settings.repeat_mode,
588 &global_settings.playlist_shuffle,
589 NULL, 0);
593 if(success)
594 bookmark_play(global_temp_buffer,index,offset,seed);
596 return success;
600 static int get_bookmark_count(char* bookmark_file_name)
602 int read_count = 0;
603 int file = open(bookmark_file_name, O_RDONLY);
605 if(file < 0)
606 return -1;
608 /* Get the requested bookmark */
609 while(read_line(file, global_read_buffer, sizeof(global_read_buffer)))
611 if(check_bookmark(global_read_buffer))
612 read_count++;
615 close(file);
616 return read_count;
622 /* ----------------------------------------------------------------------- */
623 /* This displays a the bookmarks in a file and allows the user to */
624 /* select one to play. */
625 /* ------------------------------------------------------------------------*/
626 static char* select_bookmark(char* bookmark_file_name)
628 int bookmark_id = 0;
629 int bookmark_id_prev = -1;
630 int key = 0;
631 char* bookmark = NULL;
632 int bookmark_count = 0;
634 #ifdef HAVE_LCD_BITMAP
635 lcd_setmargins(0, 0);
636 #endif
638 while (button_get(false)); /* clear button queue */
639 bookmark_count = get_bookmark_count(bookmark_file_name);
641 while(true)
643 if(bookmark_id < 0)
644 bookmark_id = bookmark_count -1;
645 if(bookmark_id == bookmark_count)
646 bookmark_id = 0;
648 if (bookmark_id != bookmark_id_prev)
650 bookmark = get_bookmark(bookmark_file_name, bookmark_id);
651 bookmark_id_prev = bookmark_id;
654 if (!bookmark)
656 /* if there were no bookmarks in the file, delete the file and exit. */
657 if(bookmark_id == 0)
659 splash(HZ, true, str(LANG_BOOKMARK_LOAD_EMPTY));
660 remove(bookmark_file_name);
661 while (button_get(false)); /* clear button queue */
662 return NULL;
664 else
666 bookmark_id_prev = bookmark_id;
667 bookmark_id--;
670 else
672 display_bookmark(bookmark, bookmark_id, bookmark_count);
673 if (global_settings.talk_menu) /* for voice UI */
674 say_bookmark(bookmark, bookmark_id);
677 /* waiting for the user to click a button */
678 key = button_get(true);
679 switch(key)
681 case BUTTON_PLAY:
682 /* User wants to use this bookmark */
683 #ifdef HAVE_LCD_BITMAP
684 if (global_settings.statusbar)
685 lcd_setmargins(0, STATUSBAR_HEIGHT);
686 else
687 lcd_setmargins(0, 0);
688 #endif
689 return bookmark;
691 case BUTTON_ON | BUTTON_PLAY:
692 /* User wants to delete this bookmark */
693 delete_bookmark(bookmark_file_name, bookmark_id);
694 bookmark_id_prev=-1;
695 bookmark_id--;
696 bookmark_count--;
697 while (button_get(false)); /* clear button queue */
698 break;
700 case SYS_USB_CONNECTED:
701 usb_screen();
702 #ifdef HAVE_LCD_CHARCELLS
703 status_set_param(true);
704 #endif
705 return NULL;
706 #ifdef HAVE_RECORDER_KEYPAD
707 case BUTTON_UP:
708 bookmark_id--;
709 break;
711 case BUTTON_DOWN:
712 bookmark_id++;
713 break;
715 case BUTTON_LEFT:
716 case BUTTON_OFF:
717 #ifdef HAVE_LCD_BITMAP
718 if (global_settings.statusbar)
719 lcd_setmargins(0, STATUSBAR_HEIGHT);
720 else
721 lcd_setmargins(0, 0);
722 #endif
723 return NULL;
724 #else
725 case BUTTON_LEFT:
726 bookmark_id--;
727 break;
729 case BUTTON_RIGHT:
730 bookmark_id++;
731 break;
733 case BUTTON_STOP:
734 return NULL;
735 #endif
739 return NULL;
743 /* ----------------------------------------------------------------------- */
744 /* This function takes a location in a bookmark file and deletes that */
745 /* bookmark. */
746 /* ------------------------------------------------------------------------*/
747 static bool delete_bookmark(char* bookmark_file_name, int bookmark_id)
749 int temp_bookmark_file = 0;
750 int bookmark_file = 0;
751 int bookmark_count = 0;
753 /* Opening up a temp bookmark file */
754 snprintf(global_temp_buffer, sizeof(global_temp_buffer),
755 "%s.tmp", bookmark_file_name);
756 temp_bookmark_file = open(global_temp_buffer,
757 O_WRONLY | O_CREAT | O_TRUNC);
758 bookmark_file = open(bookmark_file_name, O_RDONLY);
760 if (temp_bookmark_file < 0 || bookmark_file < 0)
761 return false; /* can't open one of the files */
763 /* Reading in the previous bookmarks and writing them to the temp file */
764 while (read_line(bookmark_file, global_read_buffer,
765 sizeof(global_read_buffer)))
767 /* The MRB has a max of MAX_BOOKMARKS in it */
768 /* This keeps it from getting too large */
769 if ((strcmp(bookmark_file_name,RECENT_BOOKMARK_FILE)==0))
771 if(bookmark_count >= MAX_BOOKMARKS)
772 break;
775 if (check_bookmark(global_read_buffer))
777 if (bookmark_id != bookmark_count)
779 write(temp_bookmark_file, global_read_buffer,
780 strlen(global_read_buffer));
781 write(temp_bookmark_file, "\n", 1);
783 bookmark_count++;
787 close(bookmark_file);
788 close(temp_bookmark_file);
790 remove(bookmark_file_name);
791 rename(global_temp_buffer, bookmark_file_name);
793 return true;
796 /* ----------------------------------------------------------------------- */
797 /* This function parses a bookmark and displays it for the user. */
798 /* ------------------------------------------------------------------------*/
799 static void display_bookmark(char* bookmark,
800 int bookmark_id,
801 int bookmark_count)
803 int resume_index = 0;
804 int ms = 0;
805 int repeat_mode = 0;
806 bool playlist_shuffle = false;
807 char MP3_file_name[45];
808 int len;
809 char *dot;
811 /* getting the index and the time into the file */
812 parse_bookmark(bookmark,
813 &resume_index, NULL, NULL, NULL, NULL, 0,
814 &ms, &repeat_mode, &playlist_shuffle,
815 MP3_file_name, sizeof(MP3_file_name));
817 lcd_clear_display();
818 lcd_stop_scroll();
820 #ifdef HAVE_LCD_BITMAP
821 /* bookmark shuffle and repeat states*/
822 switch (repeat_mode)
824 case REPEAT_ONE:
825 statusbar_icon_play_mode(Icon_RepeatOne);
826 break;
828 case REPEAT_ALL:
829 statusbar_icon_play_mode(Icon_Repeat);
830 break;
832 if(playlist_shuffle)
833 statusbar_icon_shuffle();
835 /* File Name */
836 len=strlen(MP3_file_name);
837 if (len>3)
838 dot=strrchr(MP3_file_name + len - 4, '.');
839 else
840 dot=NULL;
841 if (dot)
842 *dot='\0';
843 lcd_puts_scroll(0, 0, MP3_file_name);
844 if (dot)
845 *dot='.';
847 /* bookmark number */
848 snprintf(global_temp_buffer, sizeof(global_temp_buffer), "%s: %2d/%2d",
849 str(LANG_BOOKMARK_SELECT_BOOKMARK_TEXT),
850 bookmark_id + 1, bookmark_count);
851 lcd_puts_scroll(0, 1, global_temp_buffer);
853 /* bookmark resume index */
854 snprintf(global_temp_buffer, sizeof(global_temp_buffer), "%s: %2d",
855 str(LANG_BOOKMARK_SELECT_INDEX_TEXT), resume_index+1);
856 lcd_puts_scroll(0, 2, global_temp_buffer);
858 /* elapsed time*/
859 snprintf(global_temp_buffer, sizeof(global_temp_buffer), "%s: %d:%02d",
860 str(LANG_BOOKMARK_SELECT_TIME_TEXT),
861 ms / 60000,
862 ms % 60000 / 1000);
863 lcd_puts_scroll(0, 3, global_temp_buffer);
865 /* commands */
866 lcd_puts_scroll(0, 4, str(LANG_BOOKMARK_SELECT_PLAY));
867 lcd_puts_scroll(0, 5, str(LANG_BOOKMARK_SELECT_EXIT));
868 lcd_puts_scroll(0, 6, str(LANG_BOOKMARK_SELECT_DELETE));
869 #else
870 (void)bookmark_id;
871 len=strlen(MP3_file_name);
872 if (len>3)
873 dot=strrchr(MP3_file_name+len-4,'.');
874 else
875 dot=NULL;
876 if (dot)
877 *dot='\0';
878 snprintf(global_temp_buffer, sizeof(global_temp_buffer),
879 "%2d, %d:%02d, %s,",
880 (bookmark_count+1),
881 ms / 60000,
882 ms % 60000 / 1000,
883 MP3_file_name);
884 status_draw(false);
885 lcd_puts_scroll(0,0,global_temp_buffer);
886 lcd_puts(0,1,str(LANG_RESUME_CONFIRM_PLAYER));
887 if (dot)
888 *dot='.';
889 #endif
890 lcd_update();
894 /* ----------------------------------------------------------------------- */
895 /* This function parses a bookmark, says the voice UI part of it. */
896 /* ------------------------------------------------------------------------*/
897 static void say_bookmark(char* bookmark,
898 int bookmark_id)
900 int resume_index;
901 int ms;
902 char dir[MAX_PATH];
903 bool enqueue = false; /* only the first voice is not queued */
905 parse_bookmark(bookmark,
906 &resume_index,
907 NULL, NULL, NULL,
908 dir, sizeof(dir),
909 &ms, NULL, NULL,
910 NULL, 0);
911 /* disabled, because transition between talkbox and voice UI clip is not nice */
912 #if 0
913 if (global_settings.talk_dir >= 3)
914 { /* "talkbox" enabled */
915 char* last = strrchr(dir, '/');
916 if (last)
917 { /* compose filename for talkbox */
918 strncpy(last + 1, dir_thumbnail_name, sizeof(dir)-(last-dir)-1);
919 talk_file(dir, enqueue);
920 enqueue = true;
923 #endif
924 talk_id(VOICE_EXT_BMARK, enqueue);
925 talk_number(bookmark_id + 1, true);
926 talk_id(LANG_BOOKMARK_SELECT_INDEX_TEXT, true);
927 talk_number(resume_index + 1, true);
928 talk_id(LANG_BOOKMARK_SELECT_TIME_TEXT, true);
929 if (ms / 60000)
930 talk_value(ms / 60000, UNIT_MIN, true);
931 talk_value((ms % 60000) / 1000, UNIT_SEC, true);
935 /* ----------------------------------------------------------------------- */
936 /* This function retrieves a given bookmark from a file. */
937 /* If the bookmark requested is beyond the number of bookmarks available */
938 /* in the file, it will return the last one. */
939 /* It also returns the index number of the bookmark in the file */
940 /* ------------------------------------------------------------------------*/
941 static char* get_bookmark(char* bookmark_file, int bookmark_count)
943 int read_count = -1;
944 int result = 0;
945 int file = open(bookmark_file, O_RDONLY);
947 if (file < 0)
948 return NULL;
950 /* Get the requested bookmark */
951 while (read_count < bookmark_count)
953 /*Reading in a single bookmark */
954 result = read_line(file,
955 global_read_buffer,
956 sizeof(global_read_buffer));
958 /* Reading past the last bookmark in the file
959 causes the loop to stop */
960 if (result <= 0)
961 break;
963 read_count++;
966 close(file);
967 if (read_count == bookmark_count)
968 return global_read_buffer;
969 else
970 return NULL;
973 /* ----------------------------------------------------------------------- */
974 /* This function takes a bookmark and parses it. This function also */
975 /* validates the bookmark. Passing in NULL for an output variable */
976 /* indicates that value is not requested. */
977 /* ----------------------------------------------------------------------- */
978 static bool parse_bookmark(char *bookmark,
979 int *resume_index,
980 int *resume_offset,
981 int *resume_seed,
982 int *resume_first_index,
983 char* resume_file,
984 unsigned int resume_file_size,
985 int* ms,
986 int * repeat_mode, bool *shuffle,
987 char* file_name,
988 unsigned int max_file_name_size)
990 /* First check to see if a valid line was passed in. */
991 int bookmark_len = strlen(bookmark);
992 int local_resume_index = 0;
993 int local_resume_offset = 0;
994 int local_resume_seed = 0;
995 int local_resume_first_index = 0;
996 int local_mS = 0;
997 int local_shuffle = 0;
998 int local_repeat_mode = 0;
999 char* local_resume_file = NULL;
1000 char* local_file_name = NULL;
1001 char* field;
1002 char* end;
1003 static char bookmarkcopy[MAX_BOOKMARK_SIZE];
1005 /* Don't do anything if the bookmark length is 0 */
1006 if (bookmark_len <= 0)
1007 return false;
1009 /* Making a dup of the bookmark to use with strtok_r */
1010 strncpy(bookmarkcopy, bookmark, sizeof(bookmarkcopy));
1011 bookmarkcopy[sizeof(bookmarkcopy) - 1] = 0;
1013 /* resume_index */
1014 if ((field = strtok_r(bookmarkcopy, ";", &end)))
1015 local_resume_index = atoi(field);
1016 else
1017 return false;
1019 /* resume_offset */
1020 if ((field = strtok_r(NULL, ";", &end)))
1021 local_resume_offset = atoi(field);
1022 else
1023 return false;
1025 /* resume_seed */
1026 if ((field = strtok_r(NULL, ";", &end)))
1027 local_resume_seed = atoi(field);
1028 else
1029 return false;
1031 /* resume_first_index */
1032 if ((field = strtok_r(NULL, ";", &end)))
1033 local_resume_first_index = atoi(field);
1034 else
1035 return false;
1037 /* Milliseconds into MP3. Used for the bookmark select menu */
1038 if ((field = strtok_r(NULL, ";", &end)))
1039 local_mS = atoi(field);
1040 else
1041 return false;
1043 /* repeat_mode */
1044 if ((field = strtok_r(NULL, ";", &end)))
1045 local_repeat_mode = atoi(field);
1046 else
1047 return false;
1049 /* shuffle mode */
1050 if ((field = strtok_r(NULL, ";", &end)))
1051 local_shuffle = atoi(field);
1052 else
1053 return false;
1055 /* resume_file & file_name (for the bookmark select menu)*/
1056 if (end)
1058 local_resume_file = strtok_r(NULL, ";", &end);
1060 if (end)
1061 local_file_name = strtok_r(NULL, ";", &end);
1063 else
1064 return false;
1066 /* Only return the values the calling function wants */
1067 if (resume_index)
1068 *resume_index = local_resume_index;
1070 if (resume_offset)
1071 *resume_offset = local_resume_offset;
1073 if (resume_seed)
1074 *resume_seed = local_resume_seed;
1076 if (resume_first_index)
1077 *resume_first_index = local_resume_first_index;
1079 if (resume_file && local_resume_file)
1081 strncpy(resume_file, local_resume_file,
1082 MIN(strlen(local_resume_file), resume_file_size-1));
1083 resume_file[MIN(strlen(local_resume_file), resume_file_size-1)]=0;
1086 if (ms)
1087 *ms = local_mS;
1089 if (shuffle)
1090 *shuffle = local_shuffle;
1092 if (repeat_mode)
1093 *repeat_mode = local_repeat_mode;
1095 if (file_name && local_file_name)
1097 strncpy(file_name, local_file_name,
1098 MIN(strlen(local_file_name),max_file_name_size-1));
1099 file_name[MIN(strlen(local_file_name),max_file_name_size-1)]=0;
1102 return true;
1105 /* ----------------------------------------------------------------------- */
1106 /* This function is used by multiple functions and is used to generate a */
1107 /* bookmark named based off of the input. */
1108 /* Changing this function could result in how the bookmarks are stored. */
1109 /* it would be here that the centralized/decentralized bookmark code */
1110 /* could be placed. */
1111 /* ----------------------------------------------------------------------- */
1112 static bool generate_bookmark_file_name(char *in, char *out,
1113 unsigned int max_length)
1115 char* cp;
1117 if (!in || !out || max_length <= 0)
1118 return false;
1120 if (max_length < strlen(in)+6)
1121 return false;
1123 /* if this is a root dir MP3, rename the boomark file root_dir.bmark */
1124 /* otherwise, name it based on the in variable */
1125 cp = in;
1127 cp = in + strlen(in) - 1;
1128 if (*cp == '/')
1129 *cp = 0;
1131 cp = in;
1132 if (*cp == '/')
1133 cp++;
1135 if (strlen(in) > 0)
1136 snprintf(out, max_length, "/%s.%s", cp, "bmark");
1137 else
1138 snprintf(out, max_length, "/root_dir.%s", "bmark");
1140 return true;
1143 /* ----------------------------------------------------------------------- */
1144 /* Checks the current state of the system and returns if it is in a */
1145 /* bookmarkable state. */
1146 /* ----------------------------------------------------------------------- */
1147 /* Inputs: */
1148 /* ----------------------------------------------------------------------- */
1149 /* Outputs: */
1150 /* return bool: Indicates if the system was in a bookmarkable state */
1151 /* ----------------------------------------------------------------------- */
1152 static bool system_check(void)
1154 int resume_index = 0;
1155 struct mp3entry *id3 = mpeg_current_track();
1157 if (!id3)
1159 /* no track playing */
1160 return false;
1163 /* Checking to see if playing a queued track */
1164 if (playlist_get_resume_info(&resume_index) == -1)
1166 /* something bad happened while getting the queue information */
1167 return false;
1169 else if (playlist_modified(NULL))
1171 /* can't bookmark while in the queue */
1172 return false;
1175 return true;