summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBjörn Stenberg <bjorn@haxx.se>2004-01-14 00:13:04 +0000
committerBjörn Stenberg <bjorn@haxx.se>2004-01-14 00:13:04 +0000
commita108ec2ebd237835a688ae5c82c90e07607219ae (patch)
tree17c0af92368ee76d16cfdc2162aadbb7f103d926
parent50b6358272eaf1f255bcb430766e6fc9e26810d3 (diff)
downloadrockbox-a108ec2ebd237835a688ae5c82c90e07607219ae.tar.gz
rockbox-a108ec2ebd237835a688ae5c82c90e07607219ae.zip
Added Benjamin Metzlers bookmarking feature (patch #669440)
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@4227 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--apps/bookmark.c1105
-rw-r--r--apps/bookmark.h33
-rw-r--r--apps/lang/english.lang122
-rw-r--r--apps/main_menu.c6
-rw-r--r--apps/playlist.c61
-rw-r--r--apps/playlist.h16
-rw-r--r--apps/playlist_viewer.c4
-rw-r--r--apps/recorder/icons.c1
-rw-r--r--apps/recorder/icons.h1
-rw-r--r--apps/settings.c48
-rw-r--r--apps/settings.h16
-rw-r--r--apps/settings_menu.c52
-rw-r--r--apps/tree.c107
-rw-r--r--apps/tree.h1
-rw-r--r--apps/wps.c4
-rw-r--r--uisimulator/win32/Makefile5
-rw-r--r--uisimulator/x11/Makefile5
17 files changed, 1545 insertions, 42 deletions
diff --git a/apps/bookmark.c b/apps/bookmark.c
new file mode 100644
index 0000000000..b557e88fb2
--- /dev/null
+++ b/apps/bookmark.c
@@ -0,0 +1,1105 @@
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2003 by Benjamin Metzler
10 *
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.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 ****************************************************************************/
18
19
20#include <stdio.h>
21#include <stdlib.h>
22#include <string.h>
23#include <stdbool.h>
24
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
47#define MAX_BOOKMARK_SIZE 350
48#define RECENT_BOOKMARK_FILE ROCKBOX_DIR "/most-recent.bmark"
49
50static bool add_bookmark(char* bookmark_file_name, char* bookmark);
51static bool bookmark_load_menu(void);
52static bool check_bookmark(char* bookmark);
53static char* create_bookmark(void);
54static bool delete_bookmark(char* bookmark_file_name, int bookmark_id);
55static void display_bookmark(char* bookmark,
56 int bookmark_id,
57 int bookmark_count);
58static bool generate_bookmark_file_name(char *in,
59 char *out,
60 unsigned int max_length);
61static char* get_bookmark(char* bookmark_file, int bookmark_count);
62static bool parse_bookmark(char *bookmark,
63 int *resume_index,
64 int *resume_offset,
65 int *resume_seed,
66 int *resume_first_index,
67 char* resume_file,
68 unsigned int resume_file_size,
69 int* ms,
70 int * repeat_mode,
71 bool *shuffle,
72 char* file_name,
73 unsigned int max_file_name_size);
74static char* select_bookmark(char* bookmark_file_name);
75static bool system_check(void);
76static bool write_bookmark(bool create_bookmark_file);
77static int get_bookmark_count(char* bookmark_file_name);
78
79static char global_temp_buffer[MAX_PATH+1];
80static char global_bookmark_file_name[MAX_PATH];
81static char global_read_buffer[MAX_BOOKMARK_SIZE];
82static char global_bookmark[MAX_BOOKMARK_SIZE];
83
84/* ----------------------------------------------------------------------- */
85/* Displays the bookmark menu options for the user to decide. This is an */
86/* interface function. */
87/* ----------------------------------------------------------------------- */
88bool bookmark_menu(void)
89{
90 int m;
91 bool result;
92
93 struct menu_items items[] = {
94 { str(LANG_BOOKMARK_MENU_CREATE), bookmark_create_menu},
95 { str(LANG_BOOKMARK_MENU_LIST), bookmark_load_menu},
96 { str(LANG_BOOKMARK_MENU_RECENT_BOOKMARKS), bookmark_mrb_load},
97 };
98
99 m=menu_init( items, sizeof items / sizeof(struct menu_items) );
100
101#ifdef HAVE_LCD_CHARCELLS
102 status_set_param(true);
103#endif
104 result = menu_run(m);
105#ifdef HAVE_LCD_CHARCELLS
106 status_set_param(false);
107#endif
108 menu_exit(m);
109
110 settings_save();
111
112 return result;
113}
114
115/* ----------------------------------------------------------------------- */
116/* This is the interface function from the main menu. */
117/* ----------------------------------------------------------------------- */
118bool bookmark_create_menu(void)
119{
120 write_bookmark(true);
121 return false;
122}
123
124/* ----------------------------------------------------------------------- */
125/* This function acts as the load interface from the main menu */
126/* This function determines the bookmark file name and then loads that file*/
127/* for the user. The user can then select a bookmark to load. */
128/* If no file/directory is currently playing, the menu item does not work. */
129/* ----------------------------------------------------------------------- */
130static bool bookmark_load_menu(void)
131{
132 bool success = true;
133 int offset;
134 int seed;
135 int index;
136 char* bookmark;
137
138 if(!system_check())
139 return false;
140 else
141 {
142 char* name = playlist_get_name(global_temp_buffer,
143 sizeof(global_temp_buffer));
144 if (generate_bookmark_file_name(name,
145 global_bookmark_file_name,
146 sizeof(global_bookmark_file_name)))
147 {
148 bookmark = select_bookmark(global_bookmark_file_name);
149 if (!bookmark)
150 return false; /* User exited without selecting a bookmark */
151
152 success = parse_bookmark(bookmark,
153 &index,
154 &offset,
155 &seed,
156 NULL,
157 global_temp_buffer,
158 sizeof(global_temp_buffer),
159 NULL,
160 &global_settings.repeat_mode,
161 &global_settings.playlist_shuffle,
162 NULL, 0);
163 }
164 else
165 {
166 /* something bad happened while creating bookmark name*/
167 success = false;
168 }
169
170 if (success)
171 bookmark_play(global_temp_buffer, index, offset, seed);
172 }
173
174 return success;
175}
176
177/* ----------------------------------------------------------------------- */
178/* Gives the user a list of the Most Recent Bookmarks. This is an */
179/* interface function */
180/* ----------------------------------------------------------------------- */
181bool bookmark_mrb_load()
182{
183 bool success = true;
184 int offset;
185 int seed;
186 int index;
187 char* bookmark;
188
189 bookmark = select_bookmark(RECENT_BOOKMARK_FILE);
190 if (!bookmark)
191 return false; /* User exited without selecting a bookmark */
192
193 success = parse_bookmark(bookmark,
194 &index,
195 &offset,
196 &seed,
197 NULL,
198 global_temp_buffer,
199 sizeof(global_temp_buffer),
200 NULL,
201 &global_settings.repeat_mode,
202 &global_settings.playlist_shuffle,
203 NULL, 0);
204
205 if (success)
206 bookmark_play(global_temp_buffer, index, offset, seed);
207
208 return success;
209}
210
211
212/* ----------------------------------------------------------------------- */
213/* This function handles an autobookmark creation. This is an interface */
214/* function. */
215/* ----------------------------------------------------------------------- */
216bool bookmark_autobookmark(void)
217{
218 /* prompts the user as to create a bookmark */
219 bool done = false;
220 int key = 0;
221
222 if (!system_check())
223 return false;
224
225 mpeg_pause(); /* first pause playback */
226 switch (global_settings.autocreatebookmark)
227 {
228 case BOOKMARK_YES:
229 return write_bookmark(true);
230
231 case BOOKMARK_NO:
232 return false;
233
234 case BOOKMARK_RECENT_ONLY_YES:
235 return write_bookmark(false);
236 }
237
238 /* Prompting user to confirm bookmark creation */
239 lcd_clear_display();
240#ifdef HAVE_LCD_BITMAP
241 lcd_puts(0,0, str(LANG_AUTO_BOOKMARK_QUERY));
242 lcd_puts(0,1, str(LANG_CONFIRM_WITH_PLAY_RECORDER));
243 lcd_puts(0,2, str(LANG_CANCEL_WITH_ANY_RECORDER));
244#else
245 status_draw(false);
246 lcd_puts(0,0, str(LANG_AUTO_BOOKMARK_QUERY));
247 lcd_puts(0,1,str(LANG_RESUME_CONFIRM_PLAYER));
248#endif
249 lcd_update();
250
251 while (!done)
252 {
253 /* Wait for a key to be pushed */
254 key = button_get(true);
255 switch (key)
256 {
257 case BUTTON_DOWN | BUTTON_REL:
258 case BUTTON_ON | BUTTON_REL:
259#ifdef HAVE_RECORDER_KEYPAD
260 case BUTTON_OFF | BUTTON_REL:
261 case BUTTON_RIGHT | BUTTON_REL:
262 case BUTTON_UP | BUTTON_REL:
263#endif
264 case BUTTON_LEFT | BUTTON_REL:
265 done = true;
266 break;
267
268 case BUTTON_PLAY | BUTTON_REL:
269 if (global_settings.autocreatebookmark ==
270 BOOKMARK_RECENT_ONLY_ASK)
271 write_bookmark(false);
272 else
273 write_bookmark(true);
274 done = true;
275 break;
276
277 case SYS_USB_CONNECTED:
278 usb_screen();
279#ifdef HAVE_LCD_CHARCELLS
280 status_set_param(true);
281#endif
282 return false;
283 }
284 }
285 return true;
286}
287
288/* ----------------------------------------------------------------------- */
289/* This function takes the current current resume information and writes */
290/* that to the beginning of the bookmark file. */
291/* This file will contain N number of bookmarks in the following format: */
292/* resume_index*resume_offset*resume_seed*resume_first_index* */
293/* resume_file*milliseconds*MP3 Title* */
294/* ------------------------------------------------------------------------*/
295static bool write_bookmark(bool create_bookmark_file)
296{
297 bool success=false;
298 char* bookmark;
299
300 if (!system_check())
301 return false; /* something didn't happen correctly, do nothing */
302
303 bookmark = create_bookmark();
304 if (!bookmark)
305 return false; /* something didn't happen correctly, do nothing */
306
307 if (global_settings.usemrb)
308 success = add_bookmark(RECENT_BOOKMARK_FILE, bookmark);
309
310
311 /* writing the bookmark */
312 if (create_bookmark_file)
313 {
314 char* name = playlist_get_name(global_temp_buffer,
315 sizeof(global_temp_buffer));
316 if (generate_bookmark_file_name(name,
317 global_bookmark_file_name,
318 sizeof(global_bookmark_file_name)))
319 {
320 success = add_bookmark(global_bookmark_file_name, bookmark);
321 }
322 }
323
324 if (success)
325 splash(HZ, true, str(LANG_BOOKMARK_CREATE_SUCCESS));
326 else
327 splash(HZ, true, str(LANG_BOOKMARK_CREATE_FAILURE));
328
329 return true;
330}
331
332/* ----------------------------------------------------------------------- */
333/* This function adds a bookmark to a file. */
334/* ------------------------------------------------------------------------*/
335static bool add_bookmark(char* bookmark_file_name, char* bookmark)
336{
337 int temp_bookmark_file = 0;
338 int bookmark_file = 0;
339 int bookmark_count = 0;
340 char* playlist = NULL;
341 char* cp;
342 int len = 0;
343 bool unique = false;
344
345 /* Opening up a temp bookmark file */
346 snprintf(global_temp_buffer, sizeof(global_temp_buffer),
347 "%s.tmp", bookmark_file_name);
348 temp_bookmark_file = open(global_temp_buffer,
349 O_WRONLY | O_CREAT | O_TRUNC);
350 if (temp_bookmark_file < 0)
351 return false; /* can't open the temp file */
352
353 if (!strcmp(bookmark_file_name,RECENT_BOOKMARK_FILE) &&
354 (global_settings.usemrb == BOOKMARK_UNIQUE_ONLY))
355 {
356 playlist = strchr(bookmark,'/');
357 cp = strrchr(bookmark,';');
358 len = cp - playlist;
359 unique = true;
360 }
361
362 /* Writing the new bookmark to the begining of the temp file */
363 write(temp_bookmark_file, bookmark, strlen(bookmark));
364 write(temp_bookmark_file, "\n", 1);
365
366 /* Reading in the previous bookmarks and writing them to the temp file */
367 bookmark_file = open(bookmark_file_name, O_RDONLY);
368 if (bookmark_file >= 0)
369 {
370 while (read_line(bookmark_file, global_read_buffer,
371 sizeof(global_read_buffer)))
372 {
373 if (unique)
374 {
375 cp=strchr(global_read_buffer,'/');
376 if (check_bookmark(global_read_buffer) &&
377 strncmp(playlist,cp,len))
378 {
379 bookmark_count++;
380 write(temp_bookmark_file, global_read_buffer,
381 strlen(global_read_buffer));
382 write(temp_bookmark_file, "\n", 1);
383 }
384 }
385 else
386 {
387 if (check_bookmark(global_read_buffer))
388 {
389 bookmark_count++;
390 write(temp_bookmark_file, global_read_buffer,
391 strlen(global_read_buffer));
392 write(temp_bookmark_file, "\n", 1);
393 }
394 }
395 }
396 close(bookmark_file);
397 }
398 close(temp_bookmark_file);
399
400 remove(bookmark_file_name);
401 rename(global_temp_buffer, bookmark_file_name);
402
403 return true;
404}
405
406
407/* ----------------------------------------------------------------------- */
408/* This function takes the system resume data and formats it into a valid */
409/* bookmark. */
410/* ----------------------------------------------------------------------- */
411static char* create_bookmark()
412{
413 int resume_index = 0;
414 char *file;
415
416 /* grab the currently playing track */
417 struct mp3entry *id3 = mpeg_current_track();
418 if(!id3)
419 return NULL;
420
421 /* Get some basic resume information */
422 /* queue_resume and queue_resume_index are not used and can be ignored.*/
423 playlist_get_resume_info(&resume_index);
424
425 /* Get the currently playing file minus the path */
426 /* This is used when displaying the available bookmarks */
427 file = strrchr(id3->path,'/');
428 if(NULL == file)
429 return NULL;
430
431 /* create the bookmark */
432 snprintf(global_bookmark, sizeof(global_bookmark),
433 "%d;%d;%d;%d;%d;%d;%d;%s;%s",
434 resume_index,
435 id3->offset,
436 playlist_get_seed(),
437 0,
438 id3->elapsed,
439 global_settings.repeat_mode,
440 global_settings.playlist_shuffle,
441 playlist_get_name(global_temp_buffer,sizeof(global_temp_buffer)),
442 file+1);
443
444 /* checking to see if the bookmark is valid */
445 if (check_bookmark(global_bookmark))
446 return global_bookmark;
447 else
448 return NULL;
449}
450
451static bool check_bookmark(char* bookmark)
452{
453 return parse_bookmark(bookmark,
454 NULL,NULL,NULL, NULL,
455 NULL,0,NULL,NULL,
456 NULL, NULL, 0);
457}
458
459/* ----------------------------------------------------------------------- */
460/* This function will determine if an autoload is necessary. This is an */
461/* interface function. */
462/* ------------------------------------------------------------------------*/
463bool bookmark_autoload(char* file)
464{
465 int key;
466 int fd;
467 bool done = false;
468
469 if(global_settings.autoloadbookmark == BOOKMARK_NO)
470 return false;
471
472 /*Checking to see if a bookmark file exists.*/
473 if(!generate_bookmark_file_name(file,
474 global_bookmark_file_name,
475 sizeof(global_bookmark_file_name)))
476 {
477 return false;
478 }
479
480 fd = open(global_bookmark_file_name, O_RDONLY);
481 if(fd<0)
482 return false;
483 if(-1 == lseek(fd, 0, SEEK_END))
484 {
485 close(fd);
486 return false;
487 }
488 close(fd);
489
490 if(global_settings.autoloadbookmark == BOOKMARK_YES)
491 {
492 return bookmark_load(global_bookmark_file_name, true);
493 }
494 else
495 {
496 while (button_get(false)); /* clear button queue */
497 /* Prompting user to confirm bookmark load */
498 lcd_clear_display();
499#ifdef HAVE_LCD_BITMAP
500 lcd_puts_scroll(0,0, str(LANG_BOOKMARK_AUTOLOAD_QUERY));
501 lcd_puts(0,1, str(LANG_CONFIRM_WITH_PLAY_RECORDER));
502 lcd_puts(0,2, str(LANG_BOOKMARK_SELECT_LIST_BOOKMARKS));
503 lcd_puts(0,3, str(LANG_CANCEL_WITH_ANY_RECORDER));
504#else
505 status_draw(false);
506 lcd_puts_scroll(0,0, str(LANG_BOOKMARK_AUTOLOAD_QUERY));
507 lcd_puts(0,1,str(LANG_RESUME_CONFIRM_PLAYER));
508#endif
509 lcd_update();
510
511 sleep(100);
512
513 while(!done)
514 {
515 /* Wait for a key to be pushed */
516 while (button_get(false)); /* clear button queue */
517 key = button_get(true);
518 switch(key)
519 {
520 default:
521 return false;
522#ifdef HAVE_LCD_BITMAP
523 case BUTTON_DOWN:
524 return bookmark_load(global_bookmark_file_name, false);
525#endif
526 case BUTTON_PLAY:
527 return bookmark_load(global_bookmark_file_name, true);
528 case SYS_USB_CONNECTED:
529 status_set_playmode(STATUS_STOP);
530 usb_screen();
531#ifdef HAVE_LCD_CHARCELLS
532 status_set_param(true);
533#endif
534 return true;
535 }
536 }
537 return true;
538 }
539}
540
541/* ----------------------------------------------------------------------- */
542/* This function loads the bookmark information into the resume memory. */
543/* This is an interface function. */
544/* ------------------------------------------------------------------------*/
545bool bookmark_load(char* file, bool autoload)
546{
547 int fd;
548 bool success = true;
549 int offset;
550 int seed;
551 int index;
552 char* bookmark = NULL;;
553
554 if(autoload)
555 {
556 fd = open(file, O_RDONLY);
557 if(fd >= 0)
558 {
559 if(read_line(fd, global_read_buffer, sizeof(global_read_buffer)))
560 bookmark=global_read_buffer;
561 close(fd);
562 }
563 }
564 else
565 {
566 /* This is not an auto-load, so list the bookmarks */
567 bookmark=select_bookmark(file);
568 if(!bookmark)
569 return true; /* User exited without selecting a bookmark */
570 }
571
572 if(bookmark)
573 {
574 success = parse_bookmark(bookmark,
575 &index,
576 &offset,
577 &seed,
578 NULL,
579 global_temp_buffer,
580 sizeof(global_temp_buffer),
581 NULL,
582 &global_settings.repeat_mode,
583 &global_settings.playlist_shuffle,
584 NULL, 0);
585
586 }
587
588 if(success)
589 bookmark_play(global_temp_buffer,index,offset,seed);
590
591 return success;
592}
593
594
595static int get_bookmark_count(char* bookmark_file_name)
596{
597 int read_count = 0;
598 int file = open(bookmark_file_name, O_RDONLY);
599
600 if(file < 0)
601 return -1;
602
603 /* Get the requested bookmark */
604 while(read_line(file, global_read_buffer, sizeof(global_read_buffer)))
605 {
606 if(check_bookmark(global_read_buffer))
607 read_count++;
608 }
609
610 close(file);
611 return read_count;
612
613
614}
615
616
617/* ----------------------------------------------------------------------- */
618/* This displays a the bookmarks in a file and allows the user to */
619/* select one to play. */
620/* ------------------------------------------------------------------------*/
621static char* select_bookmark(char* bookmark_file_name)
622{
623 int bookmark_id = 0;
624 bool delete_this_bookmark = true;
625 int key = 0;
626 char* bookmark;
627 int bookmark_count = 0;
628
629 while(true)
630 {
631 /* Handles the case where the user wants to go below the 0th bookmark */
632 if(bookmark_id < 0)
633 bookmark_id = 0;
634
635 if(delete_this_bookmark)
636 {
637 bookmark_count = get_bookmark_count(bookmark_file_name);
638 delete_this_bookmark = false;
639 }
640
641 bookmark = get_bookmark(bookmark_file_name, bookmark_id);
642
643 if (!bookmark)
644 {
645 /* if there were no bookmarks in the file, delete the file and exit. */
646 if(bookmark_id == 0)
647 {
648 splash(HZ, true, str(LANG_BOOKMARK_LOAD_EMPTY));
649 remove(bookmark_file_name);
650 while (button_get(false)); /* clear button queue */
651 return NULL;
652 }
653 else
654 {
655 bookmark_id--;
656 }
657 }
658 else
659 {
660 display_bookmark(bookmark, bookmark_id, bookmark_count);
661 }
662
663 /* waiting for the user to click a button */
664 while (button_get(false)); /* clear button queue */
665 key = button_get(true);
666 switch(key)
667 {
668 case BUTTON_PLAY:
669 /* User wants to use this bookmark */
670 return bookmark;
671
672 case BUTTON_ON | BUTTON_PLAY:
673 /* User wants to delete this bookmark */
674 delete_this_bookmark = true;
675 break;
676
677 case SYS_USB_CONNECTED:
678 usb_screen();
679#ifdef HAVE_LCD_CHARCELLS
680 status_set_param(true);
681#endif
682 return NULL;
683#ifdef HAVE_RECORDER_KEYPAD
684 case BUTTON_UP:
685 bookmark_id--;
686 break;
687
688 case BUTTON_DOWN:
689 bookmark_id++;
690 break;
691
692 case BUTTON_LEFT:
693 case BUTTON_OFF:
694 return NULL;
695#else
696 case BUTTON_LEFT:
697 bookmark_id--;
698 break;
699
700 case BUTTON_RIGHT:
701 bookmark_id++;
702 break;
703
704 case BUTTON_STOP:
705 return NULL;
706#endif
707 }
708
709 if (delete_this_bookmark)
710 {
711 delete_bookmark(bookmark_file_name, bookmark_id);
712 bookmark_id--;
713 }
714 }
715
716 return NULL;
717}
718
719
720/* ----------------------------------------------------------------------- */
721/* This function takes a location in a bookmark file and deletes that */
722/* bookmark. */
723/* ------------------------------------------------------------------------*/
724static bool delete_bookmark(char* bookmark_file_name, int bookmark_id)
725{
726 int temp_bookmark_file = 0;
727 int bookmark_file = 0;
728 int bookmark_count = 0;
729
730 /* Opening up a temp bookmark file */
731 snprintf(global_temp_buffer, sizeof(global_temp_buffer),
732 "%s.tmp", bookmark_file_name);
733 temp_bookmark_file = open(global_temp_buffer,
734 O_WRONLY | O_CREAT | O_TRUNC);
735 bookmark_file = open(bookmark_file_name, O_RDONLY);
736
737 if (temp_bookmark_file < 0 || bookmark_file < 0)
738 return false; /* can't open one of the files */
739
740 /* Reading in the previous bookmarks and writing them to the temp file */
741 while (read_line(bookmark_file, global_read_buffer,
742 sizeof(global_read_buffer)))
743 {
744 if (check_bookmark(global_read_buffer))
745 {
746 if (bookmark_id != bookmark_count)
747 {
748 write(temp_bookmark_file, global_read_buffer,
749 strlen(global_read_buffer));
750 write(temp_bookmark_file, "\n", 1);
751 }
752 bookmark_count++;
753 }
754 }
755
756 close(bookmark_file);
757 close(temp_bookmark_file);
758
759 remove(bookmark_file_name);
760 rename(global_temp_buffer, bookmark_file_name);
761
762 return true;
763}
764
765/* ----------------------------------------------------------------------- */
766/* This function parses a bookmark and displays it for the user. */
767/* ------------------------------------------------------------------------*/
768static void display_bookmark(char* bookmark,
769 int bookmark_id,
770 int bookmark_count)
771{
772 int resume_index = 0;
773 int ms = 0;
774 int repeat_mode = 0;
775 bool playlist_shuffle = false;
776 char MP3_file_name[45];
777 int len;
778 char *dot;
779
780 /* getting the index and the time into the file */
781 parse_bookmark(bookmark,
782 &resume_index, NULL, NULL, NULL, NULL, 0,
783 &ms, &repeat_mode, &playlist_shuffle,
784 MP3_file_name, sizeof(MP3_file_name));
785
786 lcd_clear_display();
787 lcd_stop_scroll();
788
789#ifdef HAVE_LCD_BITMAP
790 /* bookmark shuffle and repeat states*/
791 switch (repeat_mode)
792 {
793 case REPEAT_ONE:
794 statusbar_icon_play_mode(Icon_RepeatOne);
795 break;
796
797 case REPEAT_ALL:
798 statusbar_icon_play_mode(Icon_Repeat);
799 break;
800 }
801 if(playlist_shuffle)
802 statusbar_icon_shuffle();
803
804 /* File Name */
805 len=strlen(MP3_file_name);
806 if (len>3)
807 dot=strrchr(MP3_file_name + len - 4, '.');
808 else
809 dot=NULL;
810 if (dot)
811 *dot='\0';
812 lcd_puts_scroll(0, 0, MP3_file_name);
813 if (dot)
814 *dot='.';
815
816 /* bookmark number */
817 snprintf(global_temp_buffer, sizeof(global_temp_buffer), "%s: %2d/%2d",
818 str(LANG_BOOKMARK_SELECT_BOOKMARK_TEXT),
819 bookmark_id + 1, bookmark_count);
820 lcd_puts_scroll(0, 1, global_temp_buffer);
821
822 /* bookmark resume index */
823 snprintf(global_temp_buffer, sizeof(global_temp_buffer), "%s: %2d",
824 str(LANG_BOOKMARK_SELECT_INDEX_TEXT), resume_index+1);
825 lcd_puts_scroll(0, 2, global_temp_buffer);
826
827 /* elapsed time*/
828 snprintf(global_temp_buffer, sizeof(global_temp_buffer), "%s: %d:%02d",
829 str(LANG_BOOKMARK_SELECT_TIME_TEXT),
830 ms / 60000,
831 ms % 60000 / 1000);
832 lcd_puts_scroll(0, 3, global_temp_buffer);
833
834 /* commands */
835 lcd_puts_scroll(0, 4, str(LANG_BOOKMARK_SELECT_PLAY));
836 lcd_puts_scroll(0, 5, str(LANG_BOOKMARK_SELECT_EXIT));
837 lcd_puts_scroll(0, 6, str(LANG_BOOKMARK_SELECT_DELETE));
838#else
839 len=strlen(MP3_file_name);
840 if (len>3)
841 dot=strrchr(MP3_file_name+len-4,'.');
842 else
843 dot=NULL;
844 if (dot)
845 *dot='\0';
846 snprintf(global_temp_buffer, sizeof(global_temp_buffer),
847 "%2d, %d:%02d, %s,",
848 (bookmark_count+1),
849 ms / 60000,
850 ms % 60000 / 1000,
851 MP3_file_name);
852 status_draw(false);
853 lcd_puts_scroll(0,0,global_temp_buffer);
854 lcd_puts(0,1,str(LANG_RESUME_CONFIRM_PLAYER));
855 if (dot)
856 *dot='.';
857#endif
858 lcd_update();
859}
860
861/* ----------------------------------------------------------------------- */
862/* This function retrieves a given bookmark from a file. */
863/* If the bookmark requested is beyond the number of bookmarks available */
864/* in the file, it will return the last one. */
865/* It also returns the index number of the bookmark in the file */
866/* ------------------------------------------------------------------------*/
867static char* get_bookmark(char* bookmark_file, int bookmark_count)
868{
869 int read_count = -1;
870 int result = 0;
871 int file = open(bookmark_file, O_RDONLY);
872
873 if (file < 0)
874 return NULL;
875
876 /* Get the requested bookmark */
877 while (read_count < bookmark_count)
878 {
879 /*Reading in a single bookmark */
880 result = read_line(file,
881 global_read_buffer,
882 sizeof(global_read_buffer));
883
884 /* Reading past the last bookmark in the file
885 causes the loop to stop */
886 if (result <= 0)
887 break;
888
889 read_count++;
890 }
891
892 close(file);
893 if (read_count == bookmark_count)
894 return global_read_buffer;
895 else
896 return NULL;
897}
898
899/* ----------------------------------------------------------------------- */
900/* This function takes a bookmark and parses it. This function also */
901/* validates the bookmark. Passing in NULL for an output variable */
902/* indicates that value is not requested. */
903/* ----------------------------------------------------------------------- */
904static bool parse_bookmark(char *bookmark,
905 int *resume_index,
906 int *resume_offset,
907 int *resume_seed,
908 int *resume_first_index,
909 char* resume_file,
910 unsigned int resume_file_size,
911 int* ms,
912 int * repeat_mode, bool *shuffle,
913 char* file_name,
914 unsigned int max_file_name_size)
915{
916 /* First check to see if a valid line was passed in. */
917 int bookmark_len = strlen(bookmark);
918 int local_resume_index = 0;
919 int local_resume_offset = 0;
920 int local_resume_seed = 0;
921 int local_resume_first_index = 0;
922 int local_mS = 0;
923 int local_shuffle = 0;
924 int local_repeat_mode = 0;
925 char* local_resume_file = NULL;
926 char* local_file_name = NULL;
927 char* field;
928 char* end;
929 static char bookmarkcopy[MAX_BOOKMARK_SIZE];
930
931 /* Don't do anything if the bookmark length is 0 */
932 if (bookmark_len <= 0)
933 return false;
934
935 /* Making a dup of the bookmark to use with strtok_r */
936 strncpy(bookmarkcopy, bookmark, sizeof(bookmarkcopy));
937 bookmarkcopy[sizeof(bookmarkcopy) - 1] = 0;
938
939 /* resume_index */
940 if ((field = strtok_r(bookmarkcopy, ";", &end)))
941 local_resume_index = atoi(field);
942 else
943 return false;
944
945 /* resume_offset */
946 if ((field = strtok_r(NULL, ";", &end)))
947 local_resume_offset = atoi(field);
948 else
949 return false;
950
951 /* resume_seed */
952 if ((field = strtok_r(NULL, ";", &end)))
953 local_resume_seed = atoi(field);
954 else
955 return false;
956
957 /* resume_first_index */
958 if ((field = strtok_r(NULL, ";", &end)))
959 local_resume_first_index = atoi(field);
960 else
961 return false;
962
963 /* Milliseconds into MP3. Used for the bookmark select menu */
964 if ((field = strtok_r(NULL, ";", &end)))
965 local_mS = atoi(field);
966 else
967 return false;
968
969 /* repeat_mode */
970 if ((field = strtok_r(NULL, ";", &end)))
971 local_repeat_mode = atoi(field);
972 else
973 return false;
974
975 /* shuffle mode */
976 if ((field = strtok_r(NULL, ";", &end)))
977 local_shuffle = atoi(field);
978 else
979 return false;
980
981 /* resume_file & file_name (for the bookmark select menu)*/
982 if (end)
983 {
984 local_resume_file = strtok_r(NULL, ";", &end);
985 if (local_resume_file[strlen(local_resume_file) - 1] == '/')
986 local_resume_file[strlen(local_resume_file) - 1] = '\0';
987
988 if (end)
989 local_file_name = strtok_r(NULL, ";", &end);
990 }
991 else
992 return false;
993
994 /* Only return the values the calling function wants */
995 if (resume_index)
996 *resume_index = local_resume_index;
997
998 if (resume_offset)
999 *resume_offset = local_resume_offset;
1000
1001 if (resume_seed)
1002 *resume_seed = local_resume_seed;
1003
1004 if (resume_first_index)
1005 *resume_first_index = local_resume_first_index;
1006
1007 if (resume_file && local_resume_file)
1008 {
1009 strncpy(resume_file, local_resume_file,
1010 MIN(strlen(local_resume_file), resume_file_size-1));
1011 resume_file[MIN(strlen(local_resume_file), resume_file_size-1)]=0;
1012 }
1013
1014 if (ms)
1015 *ms = local_mS;
1016
1017 if (shuffle)
1018 *shuffle = local_shuffle;
1019
1020 if (repeat_mode)
1021 *repeat_mode = local_repeat_mode;
1022
1023 if (file_name && local_file_name)
1024 {
1025 strncpy(file_name, local_file_name,
1026 MIN(strlen(local_file_name),max_file_name_size-1));
1027 file_name[MIN(strlen(local_file_name),max_file_name_size-1)]=0;
1028 }
1029
1030 return true;
1031}
1032
1033/* ----------------------------------------------------------------------- */
1034/* This function is used by multiple functions and is used to generate a */
1035/* bookmark named based off of the input. */
1036/* Changing this function could result in how the bookmarks are stored. */
1037/* it would be here that the centralized/decentralized bookmark code */
1038/* could be placed. */
1039/* ----------------------------------------------------------------------- */
1040static bool generate_bookmark_file_name(char *in, char *out,
1041 unsigned int max_length)
1042{
1043 char* cp;
1044
1045 if (!in || !out || max_length <= 0)
1046 return false;
1047
1048 if (max_length < strlen(in)+6)
1049 return false;
1050
1051 /* if this is a root dir MP3, rename the boomark file root_dir.bmark */
1052 /* otherwise, name it based on the in variable */
1053 cp = in;
1054
1055 cp = in + strlen(in) - 1;
1056 if (*cp == '/')
1057 *cp = 0;
1058
1059 cp = in;
1060 if (*cp == '/')
1061 cp++;
1062
1063 if (strlen(in) > 0)
1064 snprintf(out, max_length, "/%s.%s", cp, "bmark");
1065 else
1066 snprintf(out, max_length, "/root_dir.%s", "bmark");
1067
1068 return true;
1069}
1070
1071/* ----------------------------------------------------------------------- */
1072/* Checks the current state of the system and returns if it is in a */
1073/* bookmarkable state. */
1074/* ----------------------------------------------------------------------- */
1075/* Inputs: */
1076/* ----------------------------------------------------------------------- */
1077/* Outputs: */
1078/* return bool: Indicates if the system was in a bookmarkable state */
1079/* ----------------------------------------------------------------------- */
1080static bool system_check(void)
1081{
1082 int resume_index = 0;
1083 struct mp3entry *id3 = mpeg_current_track();
1084
1085 if (!id3)
1086 {
1087 /* no track playing */
1088 return false;
1089 }
1090
1091 /* Checking to see if playing a queued track */
1092 if (playlist_get_resume_info(&resume_index) == -1)
1093 {
1094 /* something bad happened while getting the queue information */
1095 return false;
1096 }
1097 else if (playlist_modified())
1098 {
1099 /* can't bookmark while in the queue */
1100 return false;
1101 }
1102
1103 return true;
1104}
1105
diff --git a/apps/bookmark.h b/apps/bookmark.h
new file mode 100644
index 0000000000..bff58f67a8
--- /dev/null
+++ b/apps/bookmark.h
@@ -0,0 +1,33 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2003 by Benjamin Metzler
10 *
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.
13 *
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
16 *
17 ****************************************************************************/
18
19#ifndef __BOOKMARK_H__
20#define __BOOKMARK_H__
21
22#include <stdbool.h>
23
24bool bookmark_menu(void);
25bool bookmark_autobookmark(void);
26bool bookmark_create_menu(void);
27bool bookmark_mrb_load(void);
28bool bookmark_autoload(char* file);
29bool bookmark_load(char* file, bool autoload);
30void bookmark_play(char* resume_file, int index, int offset, int seed);
31
32#endif /* __BOOKMARK_H__ */
33
diff --git a/apps/lang/english.lang b/apps/lang/english.lang
index 5cad562a7f..24ac940411 100644
--- a/apps/lang/english.lang
+++ b/apps/lang/english.lang
@@ -1848,6 +1848,128 @@ desc: in settings_menu, option to turn display+buttos by 180 degreed
1848eng: "Upside Down" 1848eng: "Upside Down"
1849new: 1849new:
1850 1850
1851#Auto bookmark prompts
1852id: LANG_BOOKMARK_AUTOLOAD_QUERY
1853desc: prompt for user to decide to create a bookmark
1854eng: "Load Last Bookmark?"
1855new:
1856
1857id: LANG_AUTO_BOOKMARK_QUERY
1858desc: prompt for user to decide to create an bookmark
1859eng: "Create a Bookmark?"
1860new:
1861
1862# Bookmark Select Menu Text
1863id: LANG_BOOKMARK_SELECT_LIST_BOOKMARKS
1864desc: From the auto-load screen, allows user to list all bookmarks
1865eng: "Down = List"
1866new:
1867
1868id: LANG_BOOKMARK_SELECT_EXIT
1869desc: From the bookmark list screen, allows user to exit
1870eng: "OFF = Exit"
1871new:
1872
1873id: LANG_BOOKMARK_SELECT_BOOKMARK_TEXT
1874desc: Used on the bookmark select window to label bookmark number
1875eng: "Bookmark"
1876new:
1877
1878id: LANG_BOOKMARK_SELECT_INDEX_TEXT
1879desc: Used on the bookmark select window to label index number
1880eng: "Index"
1881new:
1882
1883id: LANG_BOOKMARK_SELECT_TIME_TEXT
1884desc: Used on the bookmark select window to label elapsed time
1885eng: "Time"
1886new:
1887
1888id: LANG_BOOKMARK_SELECT_PLAY
1889desc: Used on the bookmark select window to indicated the play option
1890eng: "PLAY = Select"
1891new:
1892
1893id: LANG_BOOKMARK_SELECT_DELETE
1894desc: Used on the bookmark select window to indicated the bookmark delete option
1895eng: "ON+Play = Delete"
1896new:
1897
1898# Bookmark creation/failure text
1899id: LANG_BOOKMARK_CREATE_SUCCESS
1900desc: Indicates bookmark was successfully created
1901eng: "Bookmark Created"
1902new:
1903
1904id: LANG_BOOKMARK_CREATE_FAILURE
1905desc: Indicates bookmark was not created
1906eng: "Bookmark Failed!"
1907new:
1908
1909# Bookmark creation/failure text
1910id: LANG_BOOKMARK_LOAD_EMPTY
1911desc: Indicates bookmark was empty
1912eng: "Bookmark Empty"
1913new:
1914
1915# Bookmark Settings Text
1916id: LANG_BOOKMARK_SETTINGS
1917desc: in general settings
1918eng: "Bookmarking"
1919new:
1920
1921id: LANG_BOOKMARK_SETTINGS_AUTOLOAD
1922desc: prompt for user to decide to create a bookmark
1923eng: "Load Last Bookmark"
1924new:
1925
1926id: LANG_BOOKMARK_SETTINGS_AUTOCREATE
1927desc: prompt for user to decide to create an bookmark
1928eng: "Bookmark on Stop"
1929new:
1930
1931id: LANG_BOOKMARK_SETTINGS_MAINTAIN_RECENT_BOOKMARKS
1932desc: Configuration option to maintain a list of recent bookmarks
1933eng: "Maintain a List of Recent Bookmarks?"
1934new:
1935
1936id: LANG_BOOKMARK_SETTINGS_RECENT_ONLY_YES
1937desc: Save in recent bookmarks only
1938eng: "Yes - Recent only"
1939new:
1940
1941id: LANG_BOOKMARK_SETTINGS_RECENT_ONLY_ASK
1942desc: Save in recent bookmarks only
1943eng: "Ask - Recent only"
1944new:
1945
1946
1947id: LANG_BOOKMARK_SETTINGS_UNIQUE_ONLY
1948desc: Save only on bookmark for each playlist in recent bookmarks
1949eng: "Unique only"
1950new:
1951
1952# Main Bookmarks Menu
1953id: LANG_BOOKMARK_MENU
1954desc: Text on main menu to get to bookmark commands
1955eng: "Bookmarks"
1956new:
1957
1958id: LANG_BOOKMARK_MENU_CREATE
1959desc: Used off of the bookmark menu to create a bookmark
1960eng: "Create Bookmark"
1961new:
1962
1963id: LANG_BOOKMARK_MENU_LIST
1964desc: Used off of the bookmark menu to list available bookmarks for the currently playing directory or M3U
1965eng: "List Bookmarks"
1966new:
1967
1968id: LANG_BOOKMARK_MENU_RECENT_BOOKMARKS
1969desc: Text for the menu text to access the most recent bookmarks list
1970eng: "Recent Bookmarks"
1971new:
1972
1851id: LANG_RECORD_PRERECORD 1973id: LANG_RECORD_PRERECORD
1852desc: in recording and radio screen 1974desc: in recording and radio screen
1853eng: "Prerecording" 1975eng: "Prerecording"
diff --git a/apps/main_menu.c b/apps/main_menu.c
index b4d676332b..53954d2260 100644
--- a/apps/main_menu.c
+++ b/apps/main_menu.c
@@ -40,6 +40,7 @@
40#include "status.h" 40#include "status.h"
41#include "fat.h" 41#include "fat.h"
42#include "sleeptimer.h" 42#include "sleeptimer.h"
43#include "bookmark.h"
43#include "wps.h" 44#include "wps.h"
44#include "buffer.h" 45#include "buffer.h"
45#include "screens.h" 46#include "screens.h"
@@ -269,7 +270,10 @@ bool main_menu(void)
269 int i = 0; 270 int i = 0;
270 271
271 /* main menu */ 272 /* main menu */
272 struct menu_items items[14]; 273 struct menu_items items[15];
274
275 items[i].desc = str(LANG_BOOKMARK_MENU);
276 items[i++].function = bookmark_menu;
273 277
274 items[i].desc = str(LANG_SOUND_SETTINGS); 278 items[i].desc = str(LANG_SOUND_SETTINGS);
275 items[i++].function = sound_menu; 279 items[i++].function = sound_menu;
diff --git a/apps/playlist.c b/apps/playlist.c
index 1b6c65245d..13919b2f02 100644
--- a/apps/playlist.c
+++ b/apps/playlist.c
@@ -172,6 +172,10 @@ static void empty_playlist(bool resume)
172 playlist.first_index = 0; 172 playlist.first_index = 0;
173 playlist.amount = 0; 173 playlist.amount = 0;
174 playlist.last_insert_pos = -1; 174 playlist.last_insert_pos = -1;
175 playlist.seed = 0;
176 playlist.shuffle_modified = false;
177 playlist.deleted = false;
178 playlist.num_inserted_tracks = 0;
175 playlist.shuffle_flush = false; 179 playlist.shuffle_flush = false;
176 180
177 if (!resume) 181 if (!resume)
@@ -412,6 +416,7 @@ static int add_track_to_playlist(char *filename, int position, bool queue,
412 playlist.indices[insert_position] = flags | seek_pos; 416 playlist.indices[insert_position] = flags | seek_pos;
413 417
414 playlist.amount++; 418 playlist.amount++;
419 playlist.num_inserted_tracks++;
415 420
416 return insert_position; 421 return insert_position;
417} 422}
@@ -528,16 +533,24 @@ static int add_directory_to_playlist(char *dirname, int *position, bool queue,
528static int remove_track_from_playlist(int position, bool write) 533static int remove_track_from_playlist(int position, bool write)
529{ 534{
530 int i; 535 int i;
536 bool inserted;
531 537
532 if (playlist.amount <= 0) 538 if (playlist.amount <= 0)
533 return -1; 539 return -1;
534 540
541 inserted = playlist.indices[position] & PLAYLIST_INSERT_TYPE_MASK;
542
535 /* shift indices now that track has been removed */ 543 /* shift indices now that track has been removed */
536 for (i=position; i<playlist.amount; i++) 544 for (i=position; i<playlist.amount; i++)
537 playlist.indices[i] = playlist.indices[i+1]; 545 playlist.indices[i] = playlist.indices[i+1];
538 546
539 playlist.amount--; 547 playlist.amount--;
540 548
549 if (inserted)
550 playlist.num_inserted_tracks--;
551 else
552 playlist.deleted = true;
553
541 /* update stored indices if needed */ 554 /* update stored indices if needed */
542 if (position < playlist.index) 555 if (position < playlist.index)
543 playlist.index--; 556 playlist.index--;
@@ -622,6 +635,10 @@ static int randomise_playlist(unsigned int seed, bool start_current, bool write)
622 /* indices have been moved so last insert position is no longer valid */ 635 /* indices have been moved so last insert position is no longer valid */
623 playlist.last_insert_pos = -1; 636 playlist.last_insert_pos = -1;
624 637
638 playlist.seed = seed;
639 if (playlist.num_inserted_tracks > 0 || playlist.deleted)
640 playlist.shuffle_modified = true;
641
625 if (write) 642 if (write)
626 { 643 {
627 /* Don't write to disk immediately. Instead, save in settings and 644 /* Don't write to disk immediately. Instead, save in settings and
@@ -652,6 +669,8 @@ static int sort_playlist(bool start_current, bool write)
652 /* indices have been moved so last insert position is no longer valid */ 669 /* indices have been moved so last insert position is no longer valid */
653 playlist.last_insert_pos = -1; 670 playlist.last_insert_pos = -1;
654 671
672 if (!playlist.num_inserted_tracks && !playlist.deleted)
673 playlist.shuffle_modified = false;
655 if (write && playlist.control_fd >= 0) 674 if (write && playlist.control_fd >= 0)
656 { 675 {
657 /* Don't write to disk immediately. Instead, save in settings and 676 /* Don't write to disk immediately. Instead, save in settings and
@@ -1898,9 +1917,26 @@ int playlist_next(int steps)
1898 return index; 1917 return index;
1899} 1918}
1900 1919
1920bool playlist_modified(void)
1921{
1922 if ((mpeg_status() & MPEG_STATUS_PLAY))
1923 {
1924 if (playlist.shuffle_modified ||
1925 playlist.deleted ||
1926 playlist.num_inserted_tracks > 0)
1927 return true;
1928 }
1929 return false;
1930}
1931
1932int playlist_get_seed(void)
1933{
1934 return playlist.seed;
1935}
1936
1901/* Get resume info for current playing song. If return value is -1 then 1937/* Get resume info for current playing song. If return value is -1 then
1902 settings shouldn't be saved. */ 1938 settings shouldn't be saved. */
1903int playlist_get_resume_info(short *resume_index) 1939int playlist_get_resume_info(int *resume_index)
1904{ 1940{
1905 *resume_index = playlist.index; 1941 *resume_index = playlist.index;
1906 1942
@@ -1924,6 +1960,16 @@ int playlist_get_first_index(void)
1924 return playlist.first_index; 1960 return playlist.first_index;
1925} 1961}
1926 1962
1963char *playlist_get_name(char *buf, int buf_size)
1964{
1965 snprintf(buf, buf_size, "%s", playlist.filename);
1966
1967 if (!buf[0])
1968 return NULL;
1969
1970 return buf;
1971}
1972
1927/* returns number of tracks in playlist (includes queued/inserted tracks) */ 1973/* returns number of tracks in playlist (includes queued/inserted tracks) */
1928int playlist_amount(void) 1974int playlist_amount(void)
1929{ 1975{
@@ -1937,14 +1983,14 @@ char *playlist_name(char *buf, int buf_size)
1937 1983
1938 snprintf(buf, buf_size, "%s", playlist.filename+playlist.dirlen); 1984 snprintf(buf, buf_size, "%s", playlist.filename+playlist.dirlen);
1939 1985
1940 if (0 == buf[0]) 1986 if (!buf[0])
1941 return NULL; 1987 return NULL;
1942 1988
1943 /* Remove extension */ 1989 /* Remove extension */
1944 sep = strrchr(buf, '.'); 1990 sep = strrchr(buf, '.');
1945 if (NULL != sep) 1991 if (sep)
1946 *sep = 0; 1992 *sep = 0;
1947 1993
1948 return buf; 1994 return buf;
1949} 1995}
1950 1996
@@ -2001,7 +2047,7 @@ int playlist_save(char *filename)
2001 2047
2002 /* use current working directory as base for pathname */ 2048 /* use current working directory as base for pathname */
2003 if (format_track_path(tmp_buf, filename, sizeof(tmp_buf), 2049 if (format_track_path(tmp_buf, filename, sizeof(tmp_buf),
2004 strlen(filename)+1, getcwd(NULL, -1)) < 0) 2050 strlen(filename)+1, getcwd(NULL, -1)) < 0)
2005 return -1; 2051 return -1;
2006 2052
2007 fd = open(tmp_buf, O_CREAT|O_WRONLY|O_TRUNC); 2053 fd = open(tmp_buf, O_CREAT|O_WRONLY|O_TRUNC);
@@ -2043,15 +2089,14 @@ int playlist_save(char *filename)
2043 2089
2044 if (fprintf(fd, "%s\n", tmp_buf) < 0) 2090 if (fprintf(fd, "%s\n", tmp_buf) < 0)
2045 { 2091 {
2046 splash(HZ*2, true, 2092 splash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR));
2047 str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR));
2048 result = -1; 2093 result = -1;
2049 break; 2094 break;
2050 } 2095 }
2051 2096
2052 count++; 2097 count++;
2053 2098
2054 if ((count%PLAYLIST_DISPLAY_COUNT) == 0) 2099 if ((count % PLAYLIST_DISPLAY_COUNT) == 0)
2055 display_playlist_count(count, str(LANG_PLAYLIST_SAVE_COUNT)); 2100 display_playlist_count(count, str(LANG_PLAYLIST_SAVE_COUNT));
2056 2101
2057 yield(); 2102 yield();
diff --git a/apps/playlist.h b/apps/playlist.h
index 82d67bf0bb..020d3332cb 100644
--- a/apps/playlist.h
+++ b/apps/playlist.h
@@ -39,11 +39,16 @@ struct playlist_info
39 char *buffer; /* buffer for in-ram playlists */ 39 char *buffer; /* buffer for in-ram playlists */
40 int buffer_size; /* size of buffer */ 40 int buffer_size; /* size of buffer */
41 int buffer_end_pos; /* last position where buffer was written */ 41 int buffer_end_pos; /* last position where buffer was written */
42 short index; /* index of current playing track */ 42 int index; /* index of current playing track */
43 short first_index; /* index of first song in playlist */ 43 int first_index; /* index of first song in playlist */
44 int amount; /* number of tracks in the index */ 44 int amount; /* number of tracks in the index */
45 int last_insert_pos; /* last position we inserted a track */ 45 int last_insert_pos; /* last position we inserted a track */
46 bool shuffle_flush; /* Does shuffle value need to be flushed? */ 46 int seed; /* shuffle seed */
47 bool shuffle_modified; /* has playlist been shuffled with
48 inserted tracks? */
49 bool deleted; /* have any tracks been deleted? */
50 int num_inserted_tracks; /* number of tracks inserted */
51 bool shuffle_flush; /* does shuffle value need to be flushed? */
47 struct mutex control_mutex; /* mutex for control file access */ 52 struct mutex control_mutex; /* mutex for control file access */
48}; 53};
49 54
@@ -75,13 +80,16 @@ int playlist_start(int start_index, int offset);
75bool playlist_check(int steps); 80bool playlist_check(int steps);
76char *playlist_peek(int steps); 81char *playlist_peek(int steps);
77int playlist_next(int steps); 82int playlist_next(int steps);
78int playlist_get_resume_info(short *resume_index); 83int playlist_get_resume_info(int *resume_index);
79int playlist_get_display_index(void); 84int playlist_get_display_index(void);
80int playlist_get_first_index(void); 85int playlist_get_first_index(void);
81int playlist_amount(void); 86int playlist_amount(void);
82char *playlist_name(char *buf, int buf_size); 87char *playlist_name(char *buf, int buf_size);
83int playlist_get_track_info(int index, struct playlist_track_info* info); 88int playlist_get_track_info(int index, struct playlist_track_info* info);
84int playlist_save(char *filename); 89int playlist_save(char *filename);
90int playlist_get_seed(void);
91char *playlist_get_name(char *buf, int buf_size);
92bool playlist_modified(void);
85 93
86enum { 94enum {
87 PLAYLIST_PREPEND = -1, 95 PLAYLIST_PREPEND = -1,
diff --git a/apps/playlist_viewer.c b/apps/playlist_viewer.c
index fe792e9bbb..00da973732 100644
--- a/apps/playlist_viewer.c
+++ b/apps/playlist_viewer.c
@@ -80,7 +80,7 @@ struct playlist_viewer_info {
80 int char_width; /* Width (in pixels) of a character */ 80 int char_width; /* Width (in pixels) of a character */
81 81
82 int num_tracks; /* Number of tracks in playlist */ 82 int num_tracks; /* Number of tracks in playlist */
83 short current_playing_track;/* Index of current playing track */ 83 int current_playing_track; /* Index of current playing track */
84 84
85 int num_loaded; /* Number of track entries loaded in viewer */ 85 int num_loaded; /* Number of track entries loaded in viewer */
86 int first_index; /* Index of first loaded track */ 86 int first_index; /* Index of first loaded track */
@@ -643,7 +643,7 @@ bool playlist_viewer(void)
643 643
644 while (!exit) 644 while (!exit)
645 { 645 {
646 short track; 646 int track;
647 647
648 /* Timeout so we can determine if play status has changed */ 648 /* Timeout so we can determine if play status has changed */
649 button = button_get_w_tmo(HZ/2); 649 button = button_get_w_tmo(HZ/2);
diff --git a/apps/recorder/icons.c b/apps/recorder/icons.c
index a68167d9ac..88aa943202 100644
--- a/apps/recorder/icons.c
+++ b/apps/recorder/icons.c
@@ -68,6 +68,7 @@ unsigned char bitmap_icons_6x8[LastIcon][6] =
68 { 0x2a, 0x7f, 0x41, 0x41, 0x7f, 0x2a }, /* UCL flash file: chip */ 68 { 0x2a, 0x7f, 0x41, 0x41, 0x7f, 0x2a }, /* UCL flash file: chip */
69 { 0x70, 0x70, 0x7f, 0x7f, 0x70, 0x70 }, /* Chip8 game: joystick */ 69 { 0x70, 0x70, 0x7f, 0x7f, 0x70, 0x70 }, /* Chip8 game: joystick */
70 { 0x5d, 0x7f, 0x5d, 0x7f, 0x5d, 0x7f }, /* Video file: film strip */ 70 { 0x5d, 0x7f, 0x5d, 0x7f, 0x5d, 0x7f }, /* Video file: film strip */
71 { 0xff, 0x81, 0xaf, 0xaa, 0x8c, 0xf8 }, /* Bookmark file */
71}; 72};
72 73
73unsigned char bitmap_icons_7x8[][7] = 74unsigned char bitmap_icons_7x8[][7] =
diff --git a/apps/recorder/icons.h b/apps/recorder/icons.h
index 7a18cc2473..d362e05fee 100644
--- a/apps/recorder/icons.h
+++ b/apps/recorder/icons.h
@@ -30,6 +30,7 @@ enum icons_6x8 {
30 Selected, Cursor, Wps, Mod_Ajz, 30 Selected, Cursor, Wps, Mod_Ajz,
31 Font, Language, Text, Config, 31 Font, Language, Text, Config,
32 Plugin, Flashfile, Chip8, Video, 32 Plugin, Flashfile, Chip8, Video,
33 Bookmark,
33 LastIcon 34 LastIcon
34}; 35};
35 36
diff --git a/apps/settings.c b/apps/settings.c
index 1cde6e02e7..a342acc746 100644
--- a/apps/settings.c
+++ b/apps/settings.c
@@ -56,6 +56,7 @@
56#include "language.h" 56#include "language.h"
57#include "wps-display.h" 57#include "wps-display.h"
58#include "powermgmt.h" 58#include "powermgmt.h"
59#include "bookmark.h"
59#include "sprintf.h" 60#include "sprintf.h"
60#include "keyboard.h" 61#include "keyboard.h"
61#include "version.h" 62#include "version.h"
@@ -143,7 +144,7 @@ Rest of config block, only saved to disk:
143 caption backlight (bit 1) 144 caption backlight (bit 1)
144 car adapter mode (bit 2) 145 car adapter mode (bit 2)
145 line_in (Player only) (bit 3) 146 line_in (Player only) (bit 3)
1460xAF [available/unused] 1470xAF <most-recent-bookmarks, auto-bookmark, autoload>
1470xB0 peak meter clip hold timeout (bit 0-4), peak meter performance (bit 7) 1480xB0 peak meter clip hold timeout (bit 0-4), peak meter performance (bit 7)
1480xB1 peak meter release step size, peak_meter_dbfs (bit 7) 1490xB1 peak meter release step size, peak_meter_dbfs (bit 7)
1490xB2 peak meter min either in -db or in percent 1500xB2 peak meter min either in -db or in percent
@@ -419,6 +420,9 @@ int settings_save( void )
419 ((global_settings.caption_backlight & 1) << 1) | 420 ((global_settings.caption_backlight & 1) << 1) |
420 ((global_settings.car_adapter_mode & 1) << 2) | 421 ((global_settings.car_adapter_mode & 1) << 2) |
421 ((global_settings.line_in & 1) << 3)); 422 ((global_settings.line_in & 1) << 3));
423 config_block[0xaf] = ((global_settings.usemrb << 5) |
424 (global_settings.autocreatebookmark << 2) |
425 (global_settings.autoloadbookmark));
422 config_block[0xb0] = (unsigned char)global_settings.peak_meter_clip_hold | 426 config_block[0xb0] = (unsigned char)global_settings.peak_meter_clip_hold |
423 (global_settings.peak_meter_performance ? 0x80 : 0); 427 (global_settings.peak_meter_performance ? 0x80 : 0);
424 config_block[0xb1] = global_settings.peak_meter_release | 428 config_block[0xb1] = global_settings.peak_meter_release |
@@ -771,6 +775,9 @@ void settings_load(void)
771 if (config_block[0xa9] != 0xff) 775 if (config_block[0xa9] != 0xff)
772 global_settings.jump_scroll_delay = config_block[0xa9]; 776 global_settings.jump_scroll_delay = config_block[0xa9];
773#endif 777#endif
778 global_settings.usemrb = (config_block[0xaf] >> 5) & 3;
779 global_settings.autocreatebookmark = (config_block[0xaf] >> 2) & 7;
780 global_settings.autoloadbookmark = (config_block[0xaf]) & 3;
774 } 781 }
775 782
776 settings_apply(); 783 settings_apply();
@@ -1128,6 +1135,21 @@ bool settings_load_config(char* file)
1128 set_cfg_option(&global_settings.recursive_dir_insert, value, 1135 set_cfg_option(&global_settings.recursive_dir_insert, value,
1129 options, 3); 1136 options, 3);
1130 } 1137 }
1138 else if (!strcasecmp(name, "autoload bookmarks"))
1139 {
1140 static char* options[] = {"off", "on", "ask"};
1141 set_cfg_option(&global_settings.autoloadbookmark, value, options, 3);
1142 }
1143 else if (!strcasecmp(name, "autocreate bookmarks"))
1144 {
1145 static char* options[] = {"off", "on", "ask","recent only - yes","recent only - ask"};
1146 set_cfg_option(&global_settings.autocreatebookmark, value, options, 5);
1147 }
1148 else if (!strcasecmp(name, "use most-recent-bookmarks"))
1149 {
1150 static char* options[] = {"off", "on", "unique only"};
1151 set_cfg_option(&global_settings.usemrb, value, options, 3);
1152 }
1131 } 1153 }
1132 1154
1133 close(fd); 1155 close(fd);
@@ -1143,6 +1165,7 @@ bool settings_save_config(void)
1143 int fd, i, value; 1165 int fd, i, value;
1144 char filename[MAX_PATH]; 1166 char filename[MAX_PATH];
1145 char* boolopt[] = {"off","on"}; 1167 char* boolopt[] = {"off","on"};
1168 char* triopt[] = {"off","on","ask"};
1146 1169
1147 /* find unused filename */ 1170 /* find unused filename */
1148 for (i=0; ; i++) { 1171 for (i=0; ; i++) {
@@ -1431,11 +1454,27 @@ bool settings_save_config(void)
1431 1454
1432#endif 1455#endif
1433 1456
1457 fprintf(fd, "#\r\n# Bookmarking\r\n#\r\n");
1458 {
1459 fprintf(fd, "autoload bookmarks: %s\r\n",
1460 triopt[global_settings.autoloadbookmark]);
1461 }
1462
1463 {
1464 static char* options[] = {"off", "on", "ask","recent only - on", "recent only - ask"};
1465 fprintf(fd, "autocreate bookmarks: %s\r\n",
1466 options[global_settings.autocreatebookmark]);
1467 }
1468
1469 {
1470 static char* options[] = {"off", "on", "unique only"};
1471 fprintf(fd, "UseMRB: %s\r\n", options[global_settings.usemrb]);
1472 }
1473
1434 fprintf(fd, "#\r\n# Playlists\r\n#\r\n"); 1474 fprintf(fd, "#\r\n# Playlists\r\n#\r\n");
1435 { 1475 {
1436 static char* options[] = {"off", "on", "ask"};
1437 fprintf(fd, "recursive directory insert: %s\r\n", 1476 fprintf(fd, "recursive directory insert: %s\r\n",
1438 options[global_settings.recursive_dir_insert]); 1477 triopt[global_settings.recursive_dir_insert]);
1439 } 1478 }
1440 1479
1441 close(fd); 1480 close(fd);
@@ -1528,6 +1567,9 @@ void settings_reset(void) {
1528 global_settings.lang_file[0] = 0; 1567 global_settings.lang_file[0] = 0;
1529 global_settings.runtime = 0; 1568 global_settings.runtime = 0;
1530 global_settings.topruntime = 0; 1569 global_settings.topruntime = 0;
1570 global_settings.autocreatebookmark = BOOKMARK_NO;
1571 global_settings.autoloadbookmark = BOOKMARK_NO;
1572 global_settings.usemrb = BOOKMARK_NO;
1531 global_settings.fade_on_stop = true; 1573 global_settings.fade_on_stop = true;
1532 global_settings.caption_backlight = false; 1574 global_settings.caption_backlight = false;
1533 global_settings.car_adapter_mode = false; 1575 global_settings.car_adapter_mode = false;
diff --git a/apps/settings.h b/apps/settings.h
index 904bcd6a91..49fa83b359 100644
--- a/apps/settings.h
+++ b/apps/settings.h
@@ -38,6 +38,12 @@
38#define RESUME_ASK_ONCE 2 38#define RESUME_ASK_ONCE 2
39#define RESUME_ON 3 39#define RESUME_ON 3
40 40
41#define BOOKMARK_NO 0
42#define BOOKMARK_YES 1
43#define BOOKMARK_ASK 2
44#define BOOKMARK_UNIQUE_ONLY 2
45#define BOOKMARK_RECENT_ONLY_YES 3
46#define BOOKMARK_RECENT_ONLY_ASK 4
41#define FF_REWIND_1000 0 47#define FF_REWIND_1000 0
42#define FF_REWIND_2000 1 48#define FF_REWIND_2000 1
43#define FF_REWIND_3000 2 49#define FF_REWIND_3000 2
@@ -109,8 +115,8 @@ struct user_settings
109 /* resume settings */ 115 /* resume settings */
110 116
111 int resume; /* resume option: 0=off, 1=ask, 2=on */ 117 int resume; /* resume option: 0=off, 1=ask, 2=on */
112 short resume_index; /* index in playlist (-1 for no active resume) */ 118 int resume_index; /* index in playlist (-1 for no active resume) */
113 short resume_first_index; /* index of first track in playlist */ 119 int resume_first_index; /* index of first track in playlist */
114 int resume_offset; /* byte offset in mp3 file */ 120 int resume_offset; /* byte offset in mp3 file */
115 int resume_seed; /* shuffle seed (-1=no resume shuffle 0=sorted 121 int resume_seed; /* shuffle seed (-1=no resume shuffle 0=sorted
116 >0=shuffled) */ 122 >0=shuffled) */
@@ -161,6 +167,11 @@ struct user_settings
161 int bidir_limit; /* bidir scroll length limit */ 167 int bidir_limit; /* bidir scroll length limit */
162 int scroll_delay; /* delay (in 1/10s) before starting scroll */ 168 int scroll_delay; /* delay (in 1/10s) before starting scroll */
163 int scroll_step; /* pixels to advance per update */ 169 int scroll_step; /* pixels to advance per update */
170
171 /* auto bookmark settings */
172 int autoloadbookmark; /* auto load option: 0=off, 1=ask, 2=on */
173 int autocreatebookmark; /* auto create option: 0=off, 1=ask, 2=on */
174 int usemrb; /* use MRB list: 0=No, 1=Yes*/
164#ifdef HAVE_LCD_CHARCELLS 175#ifdef HAVE_LCD_CHARCELLS
165 int jump_scroll; /* Fast jump when scrolling */ 176 int jump_scroll; /* Fast jump when scrolling */
166 int jump_scroll_delay; /* Delay between jump scroll screens */ 177 int jump_scroll_delay; /* Delay between jump scroll screens */
@@ -205,6 +216,7 @@ bool set_option(char* string, void* variable, enum optiontype type,
205bool set_int(char* string, char* unit, int* variable, 216bool set_int(char* string, char* unit, int* variable,
206 void (*function)(int), int step, int min, int max ); 217 void (*function)(int), int step, int min, int max );
207bool set_time(char* string, int timedate[]); 218bool set_time(char* string, int timedate[]);
219int read_line(int fd, char* buffer, int buffer_size);
208void set_file(char* filename, char* setting, int maxlen); 220void set_file(char* filename, char* setting, int maxlen);
209 221
210#ifdef HAVE_MAS3587F 222#ifdef HAVE_MAS3587F
diff --git a/apps/settings_menu.c b/apps/settings_menu.c
index 65261e348c..a96c88c681 100644
--- a/apps/settings_menu.c
+++ b/apps/settings_menu.c
@@ -438,6 +438,40 @@ static bool resume(void)
438 names, 4, NULL ); 438 names, 4, NULL );
439} 439}
440 440
441static bool autocreatebookmark(void)
442{
443 char* names[] = { str(LANG_SET_BOOL_NO),
444 str(LANG_SET_BOOL_YES),
445 str(LANG_RESUME_SETTING_ASK),
446 str(LANG_BOOKMARK_SETTINGS_RECENT_ONLY_YES),
447 str(LANG_BOOKMARK_SETTINGS_RECENT_ONLY_ASK) };
448
449 return set_option( str(LANG_BOOKMARK_SETTINGS_AUTOCREATE),
450 &global_settings.autocreatebookmark, INT,
451 names, 5, NULL );
452}
453
454static bool autoloadbookmark(void)
455{
456 char* names[] = { str(LANG_SET_BOOL_NO),
457 str(LANG_SET_BOOL_YES),
458 str(LANG_RESUME_SETTING_ASK) };
459
460 return set_option( str(LANG_BOOKMARK_SETTINGS_AUTOLOAD),
461 &global_settings.autoloadbookmark, INT,
462 names, 3, NULL );
463}
464
465static bool useMRB(void)
466{
467 char* names[] = { str(LANG_SET_BOOL_NO),
468 str(LANG_SET_BOOL_YES),
469 str(LANG_BOOKMARK_SETTINGS_UNIQUE_ONLY)};
470
471 return set_option( str(LANG_BOOKMARK_SETTINGS_MAINTAIN_RECENT_BOOKMARKS),
472 &global_settings.usemrb, INT,
473 names, 3, NULL );
474}
441static bool backlight_on_when_charging(void) 475static bool backlight_on_when_charging(void)
442{ 476{
443 bool result = set_bool(str(LANG_BACKLIGHT_ON_WHEN_CHARGING), 477 bool result = set_bool(str(LANG_BACKLIGHT_ON_WHEN_CHARGING),
@@ -789,6 +823,23 @@ static bool playback_settings_menu(void)
789 return result; 823 return result;
790} 824}
791 825
826static bool bookmark_settings_menu(void)
827{
828 int m;
829 bool result;
830
831 struct menu_items items[] = {
832 { str(LANG_BOOKMARK_SETTINGS_AUTOCREATE), autocreatebookmark},
833 { str(LANG_BOOKMARK_SETTINGS_AUTOLOAD), autoloadbookmark},
834 { str(LANG_BOOKMARK_SETTINGS_MAINTAIN_RECENT_BOOKMARKS), useMRB},
835 };
836
837 m=menu_init( items, sizeof items / sizeof(struct menu_items) );
838 result = menu_run(m);
839 menu_exit(m);
840
841 return result;
842}
792static bool reset_settings(void) 843static bool reset_settings(void)
793{ 844{
794 bool done=false; 845 bool done=false;
@@ -966,6 +1017,7 @@ bool settings_menu(void)
966 { str(LANG_CUSTOM_FONT), font_browse }, 1017 { str(LANG_CUSTOM_FONT), font_browse },
967#endif 1018#endif
968 { str(LANG_SYSTEM), system_settings_menu }, 1019 { str(LANG_SYSTEM), system_settings_menu },
1020 { str(LANG_BOOKMARK_SETTINGS),bookmark_settings_menu },
969 { str(LANG_SAVE_SETTINGS), settings_save_config }, 1021 { str(LANG_SAVE_SETTINGS), settings_save_config },
970 }; 1022 };
971 1023
diff --git a/apps/tree.c b/apps/tree.c
index 3c9827d685..12472fa79b 100644
--- a/apps/tree.c
+++ b/apps/tree.c
@@ -49,6 +49,7 @@
49#include "language.h" 49#include "language.h"
50#include "screens.h" 50#include "screens.h"
51#include "keyboard.h" 51#include "keyboard.h"
52#include "bookmark.h"
52#include "onplay.h" 53#include "onplay.h"
53#include "buffer.h" 54#include "buffer.h"
54#include "plugin.h" 55#include "plugin.h"
@@ -83,6 +84,9 @@ static struct
83 { ".fnt", TREE_ATTR_FONT,Font }, 84 { ".fnt", TREE_ATTR_FONT,Font },
84 { ".ch8", TREE_ATTR_CH8, Chip8 }, 85 { ".ch8", TREE_ATTR_CH8, Chip8 },
85 { ".rvf", TREE_ATTR_RVF, Video }, 86 { ".rvf", TREE_ATTR_RVF, Video },
87 { ".bmark",TREE_ATTR_BMARK,Bookmark },
88#else
89 { ".bmark", TREE_ATTR_BMARK, -1 },
86#endif 90#endif
87#ifndef SIMULATOR 91#ifndef SIMULATOR
88#ifdef HAVE_LCD_BITMAP 92#ifdef HAVE_LCD_BITMAP
@@ -118,6 +122,7 @@ static int boot_size = 0;
118static int boot_cluster; 122static int boot_cluster;
119static bool boot_changed = false; 123static bool boot_changed = false;
120 124
125static bool start_wps = false;
121static bool dirbrowse(char *root, int *dirfilter); 126static bool dirbrowse(char *root, int *dirfilter);
122 127
123void browse_root(void) 128void browse_root(void)
@@ -646,9 +651,7 @@ static void start_resume(bool ask_once)
646 playlist_start(global_settings.resume_index, 651 playlist_start(global_settings.resume_index,
647 global_settings.resume_offset); 652 global_settings.resume_offset);
648 653
649 status_set_playmode(STATUS_PLAY); 654 start_wps = true;
650 status_draw(true);
651 wps_show();
652 } 655 }
653 else 656 else
654 return; 657 return;
@@ -930,6 +933,7 @@ static bool dirbrowse(char *root, int *dirfilter)
930 933
931#ifdef HAVE_RECORDER_KEYPAD 934#ifdef HAVE_RECORDER_KEYPAD
932 case BUTTON_OFF: 935 case BUTTON_OFF:
936 bookmark_autobookmark();
933 mpeg_stop(); 937 mpeg_stop();
934 status_set_playmode(STATUS_STOP); 938 status_set_playmode(STATUS_STOP);
935 status_draw(false); 939 status_draw(false);
@@ -988,6 +992,12 @@ static bool dirbrowse(char *root, int *dirfilter)
988 lcd_stop_scroll(); 992 lcd_stop_scroll();
989 switch ( file->attr & TREE_ATTR_MASK ) { 993 switch ( file->attr & TREE_ATTR_MASK ) {
990 case TREE_ATTR_M3U: 994 case TREE_ATTR_M3U:
995 if (bookmark_autoload(buf))
996 {
997 restore = true;
998 break;
999 }
1000
991 if (playlist_create(currdir, file->name) != -1) 1001 if (playlist_create(currdir, file->name) != -1)
992 { 1002 {
993 if (global_settings.playlist_shuffle) 1003 if (global_settings.playlist_shuffle)
@@ -999,6 +1009,12 @@ static bool dirbrowse(char *root, int *dirfilter)
999 break; 1009 break;
1000 1010
1001 case TREE_ATTR_MPA: 1011 case TREE_ATTR_MPA:
1012 if (bookmark_autoload(currdir))
1013 {
1014 restore = true;
1015 break;
1016 }
1017
1002 if (playlist_create(currdir, NULL) != -1) 1018 if (playlist_create(currdir, NULL) != -1)
1003 { 1019 {
1004 start_index = 1020 start_index =
@@ -1051,6 +1067,12 @@ static bool dirbrowse(char *root, int *dirfilter)
1051 restore = true; 1067 restore = true;
1052 break; 1068 break;
1053 1069
1070 case TREE_ATTR_BMARK:
1071 bookmark_load(buf, false);
1072 restore = true;
1073 reload_dir = true;
1074 break;
1075
1054 case TREE_ATTR_TXT: 1076 case TREE_ATTR_TXT:
1055 plugin_load("/.rockbox/rocks/viewer.rock",buf); 1077 plugin_load("/.rockbox/rocks/viewer.rock",buf);
1056 restore = true; 1078 restore = true;
@@ -1122,15 +1144,7 @@ static bool dirbrowse(char *root, int *dirfilter)
1122 settings_save(); 1144 settings_save();
1123 } 1145 }
1124 1146
1125 status_set_playmode(STATUS_PLAY); 1147 start_wps = true;
1126 status_draw(false);
1127 lcd_stop_scroll();
1128 if ( wps_show() == SYS_USB_CONNECTED ) {
1129 reload_root = true;
1130 }
1131#ifdef HAVE_LCD_BITMAP
1132 tree_max_on_screen = (LCD_HEIGHT - MARGIN_Y) / fh;
1133#endif
1134 } 1148 }
1135 else if (*dirfilter > NUM_FILTER_MODES) 1149 else if (*dirfilter > NUM_FILTER_MODES)
1136 exit_func = true; 1150 exit_func = true;
@@ -1238,13 +1252,7 @@ static bool dirbrowse(char *root, int *dirfilter)
1238 { 1252 {
1239 if (mpeg_status() & MPEG_STATUS_PLAY) 1253 if (mpeg_status() & MPEG_STATUS_PLAY)
1240 { 1254 {
1241 lcd_stop_scroll(); 1255 start_wps=true;
1242 if (wps_show() == SYS_USB_CONNECTED)
1243 reload_root = true;
1244#ifdef HAVE_LCD_BITMAP
1245 tree_max_on_screen = (LCD_HEIGHT - MARGIN_Y) / fh;
1246#endif
1247 restore = true;
1248 } 1256 }
1249 else 1257 else
1250 { 1258 {
@@ -1293,6 +1301,18 @@ static bool dirbrowse(char *root, int *dirfilter)
1293 if ( button ) 1301 if ( button )
1294 ata_spin(); 1302 ata_spin();
1295 1303
1304 if (start_wps)
1305 {
1306 lcd_stop_scroll();
1307 if (wps_show() == SYS_USB_CONNECTED)
1308 reload_root = true;
1309#ifdef HAVE_LCD_BITMAP
1310 tree_max_on_screen = (LCD_HEIGHT - MARGIN_Y) / fh;
1311#endif
1312 restore = true;
1313 start_wps=false;
1314 }
1315
1296 /* do we need to rescan dir? */ 1316 /* do we need to rescan dir? */
1297 if (reload_dir || reload_root || 1317 if (reload_dir || reload_root ||
1298 lastfilter != *dirfilter || 1318 lastfilter != *dirfilter ||
@@ -1313,6 +1333,7 @@ static bool dirbrowse(char *root, int *dirfilter)
1313 lastfilter = *dirfilter; 1333 lastfilter = *dirfilter;
1314 lastsortcase = global_settings.sort_case; 1334 lastsortcase = global_settings.sort_case;
1315 restore = true; 1335 restore = true;
1336 while (button_get(false)); /* clear button queue */
1316 } 1337 }
1317 1338
1318 if (exit_func) 1339 if (exit_func)
@@ -1507,3 +1528,51 @@ void tree_init(void)
1507 name_buffer = buffer_alloc(name_buffer_size); 1528 name_buffer = buffer_alloc(name_buffer_size);
1508 dircache = buffer_alloc(max_files_in_dir * sizeof(struct entry)); 1529 dircache = buffer_alloc(max_files_in_dir * sizeof(struct entry));
1509} 1530}
1531
1532void bookmark_play(char *resume_file, int index, int offset, int seed)
1533{
1534 int len=strlen(resume_file);
1535
1536 if (!strcasecmp(&resume_file[len-4], ".m3u"))
1537 {
1538 char* slash;
1539 // check that the file exists
1540 int fd = open(resume_file, O_RDONLY);
1541 if(fd<0)
1542 return;
1543 close(fd);
1544
1545 slash = strrchr(resume_file,'/');
1546 if (slash)
1547 {
1548 char* cp;
1549 *slash=0;
1550
1551 cp=resume_file;
1552 if (!cp[0])
1553 cp="/";
1554
1555 if (playlist_create(cp, slash+1) != -1)
1556 {
1557 if (global_settings.playlist_shuffle)
1558 playlist_shuffle(seed, -1);
1559 playlist_start(index,offset);
1560 }
1561 *slash='/';
1562 }
1563 }
1564 else
1565 {
1566 lastdir[0]='\0';
1567 if (playlist_create(resume_file, NULL) != -1)
1568 {
1569 resume_directory(resume_file);
1570 if (global_settings.playlist_shuffle)
1571 playlist_shuffle(seed, -1);
1572 playlist_start(index,offset);
1573 }
1574 }
1575
1576 status_set_playmode(STATUS_PLAY);
1577 start_wps=true;
1578}
diff --git a/apps/tree.h b/apps/tree.h
index 87cd469148..c7b678eb82 100644
--- a/apps/tree.h
+++ b/apps/tree.h
@@ -39,6 +39,7 @@ struct entry {
39#define TREE_ATTR_UCL 0x0A00 /* rockbox flash image */ 39#define TREE_ATTR_UCL 0x0A00 /* rockbox flash image */
40#define TREE_ATTR_CH8 0x0B00 /* chip-8 game */ 40#define TREE_ATTR_CH8 0x0B00 /* chip-8 game */
41#define TREE_ATTR_RVF 0x0C00 /* rockbox video file */ 41#define TREE_ATTR_RVF 0x0C00 /* rockbox video file */
42#define TREE_ATTR_BMARK 0x0D00 /* book mark file */
42#define TREE_ATTR_MASK 0xFFC0 /* which bits tree.c uses (above) */ 43#define TREE_ATTR_MASK 0xFFC0 /* which bits tree.c uses (above) */
43 44
44void tree_init(void); 45void tree_init(void);
diff --git a/apps/wps.c b/apps/wps.c
index 777894d535..09eeef4361 100644
--- a/apps/wps.c
+++ b/apps/wps.c
@@ -45,6 +45,7 @@
45#include "peakmeter.h" 45#include "peakmeter.h"
46#endif 46#endif
47#include "lang.h" 47#include "lang.h"
48#include "bookmark.h"
48#define FF_REWIND_MAX_PERCENT 3 /* cap ff/rewind step size at max % of file */ 49#define FF_REWIND_MAX_PERCENT 3 /* cap ff/rewind step size at max % of file */
49 /* 3% of 30min file == 54s step size */ 50 /* 3% of 30min file == 54s step size */
50 51
@@ -943,7 +944,7 @@ int wps_show(void)
943 case BUTTON_RC_STOP: 944 case BUTTON_RC_STOP:
944#endif 945#endif
945#ifdef BUTTON_OFF 946#ifdef BUTTON_OFF
946 case BUTTON_OFF: 947 case BUTTON_OFF | BUTTON_REL:
947#else 948#else
948 case BUTTON_STOP | BUTTON_REL: 949 case BUTTON_STOP | BUTTON_REL:
949 if ( lastbutton != BUTTON_STOP ) 950 if ( lastbutton != BUTTON_STOP )
@@ -985,6 +986,7 @@ int wps_show(void)
985 fade(0); 986 fade(0);
986 987
987 lcd_stop_scroll(); 988 lcd_stop_scroll();
989 bookmark_autobookmark();
988 mpeg_stop(); 990 mpeg_stop();
989 status_set_playmode(STATUS_STOP); 991 status_set_playmode(STATUS_STOP);
990 992
diff --git a/uisimulator/win32/Makefile b/uisimulator/win32/Makefile
index 69e6e0b734..fe7086391b 100644
--- a/uisimulator/win32/Makefile
+++ b/uisimulator/win32/Makefile
@@ -102,7 +102,7 @@ FIRMSRCS = $(LCDSRSC) id3.c mp3data.c usb.c mpeg.c mp3_playback.c \
102APPS = main.c tree.c menu.c credits.c main_menu.c icons.c language.c \ 102APPS = main.c tree.c menu.c credits.c main_menu.c icons.c language.c \
103 playlist.c wps.c wps-display.c settings.c status.c \ 103 playlist.c wps.c wps-display.c settings.c status.c \
104 screens.c peakmeter.c sleeptimer.c keyboard.c onplay.c\ 104 screens.c peakmeter.c sleeptimer.c keyboard.c onplay.c\
105 misc.c plugin.c playlist_viewer.c 105 misc.c plugin.c playlist_viewer.c bookmark.c
106 106
107MENUS = settings_menu.c sound_menu.c playlist_menu.c 107MENUS = settings_menu.c sound_menu.c playlist_menu.c
108 108
@@ -204,6 +204,9 @@ $(OBJDIR)/playlist.o: $(APPDIR)/playlist.c
204$(OBJDIR)/playlist_viewer.o: $(APPDIR)/playlist_viewer.c 204$(OBJDIR)/playlist_viewer.o: $(APPDIR)/playlist_viewer.c
205 $(CC) $(APPCFLAGS) -c $< -o $@ 205 $(CC) $(APPCFLAGS) -c $< -o $@
206 206
207$(OBJDIR)/bookmark.o: $(APPDIR)/bookmark.c
208 $(CC) $(APPCFLAGS) -c $< -o $@
209
207$(OBJDIR)/plugin.o: $(APPDIR)/plugin.c plugin-win32.h 210$(OBJDIR)/plugin.o: $(APPDIR)/plugin.c plugin-win32.h
208 $(CC) $(APPCFLAGS) -c $< -o $@ 211 $(CC) $(APPCFLAGS) -c $< -o $@
209 212
diff --git a/uisimulator/x11/Makefile b/uisimulator/x11/Makefile
index a66d86dfd4..fb609ea2c3 100644
--- a/uisimulator/x11/Makefile
+++ b/uisimulator/x11/Makefile
@@ -102,7 +102,7 @@ FIRMSRCS = $(LCDSRSC) id3.c debug.c usb.c mpeg.c mp3_playback.c power.c\
102APPS = main.c tree.c menu.c credits.c main_menu.c language.c\ 102APPS = main.c tree.c menu.c credits.c main_menu.c language.c\
103 playlist.c wps.c wps-display.c settings.c status.c icons.c\ 103 playlist.c wps.c wps-display.c settings.c status.c icons.c\
104 screens.c peakmeter.c sleeptimer.c keyboard.c onplay.c\ 104 screens.c peakmeter.c sleeptimer.c keyboard.c onplay.c\
105 misc.c plugin.c playlist_viewer.c 105 misc.c plugin.c playlist_viewer.c bookmark.c
106 106
107MENUS = settings_menu.c sound_menu.c playlist_menu.c 107MENUS = settings_menu.c sound_menu.c playlist_menu.c
108 108
@@ -202,6 +202,9 @@ $(OBJDIR)/playlist.o: $(APPDIR)/playlist.c
202$(OBJDIR)/playlist_viewer.o: $(APPDIR)/playlist_viewer.c 202$(OBJDIR)/playlist_viewer.o: $(APPDIR)/playlist_viewer.c
203 $(CC) $(APPCFLAGS) -c $< -o $@ 203 $(CC) $(APPCFLAGS) -c $< -o $@
204 204
205$(OBJDIR)/bookmark.o: $(APPDIR)/bookmark.c
206 $(CC) $(APPCFLAGS) -c $< -o $@
207
205$(OBJDIR)/build.lang: $(APPDIR)/lang/$(LANGUAGE).lang 208$(OBJDIR)/build.lang: $(APPDIR)/lang/$(LANGUAGE).lang
206 perl $(TOOLSDIR)/uplang $(APPDIR)/lang/english.lang $< > $@ 209 perl $(TOOLSDIR)/uplang $(APPDIR)/lang/english.lang $< > $@
207 210