summaryrefslogtreecommitdiffstats
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 @@
+ /***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ *
+ * Copyright (C) 2003 by Benjamin Metzler
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+
+#include "applimits.h"
+#include "lcd.h"
+#include "button.h"
+#include "usb.h"
+#include "mpeg.h"
+#include "wps.h"
+#include "settings.h"
+#include "bookmark.h"
+#include "dir.h"
+#include "status.h"
+#include "system.h"
+#include "errno.h"
+#include "icons.h"
+#include "atoi.h"
+#include "string.h"
+#include "menu.h"
+#include "lang.h"
+#include "screens.h"
+#include "status.h"
+#include "debug.h"
+#include "kernel.h"
+
+#define MAX_BOOKMARK_SIZE 350
+#define RECENT_BOOKMARK_FILE ROCKBOX_DIR "/most-recent.bmark"
+
+static bool add_bookmark(char* bookmark_file_name, char* bookmark);
+static bool bookmark_load_menu(void);
+static bool check_bookmark(char* bookmark);
+static char* create_bookmark(void);
+static bool delete_bookmark(char* bookmark_file_name, int bookmark_id);
+static void display_bookmark(char* bookmark,
+ int bookmark_id,
+ int bookmark_count);
+static bool generate_bookmark_file_name(char *in,
+ char *out,
+ unsigned int max_length);
+static char* get_bookmark(char* bookmark_file, int bookmark_count);
+static bool parse_bookmark(char *bookmark,
+ int *resume_index,
+ int *resume_offset,
+ int *resume_seed,
+ int *resume_first_index,
+ char* resume_file,
+ unsigned int resume_file_size,
+ int* ms,
+ int * repeat_mode,
+ bool *shuffle,
+ char* file_name,
+ unsigned int max_file_name_size);
+static char* select_bookmark(char* bookmark_file_name);
+static bool system_check(void);
+static bool write_bookmark(bool create_bookmark_file);
+static int get_bookmark_count(char* bookmark_file_name);
+
+static char global_temp_buffer[MAX_PATH+1];
+static char global_bookmark_file_name[MAX_PATH];
+static char global_read_buffer[MAX_BOOKMARK_SIZE];
+static char global_bookmark[MAX_BOOKMARK_SIZE];
+
+/* ----------------------------------------------------------------------- */
+/* Displays the bookmark menu options for the user to decide. This is an */
+/* interface function. */
+/* ----------------------------------------------------------------------- */
+bool bookmark_menu(void)
+{
+ int m;
+ bool result;
+
+ struct menu_items items[] = {
+ { str(LANG_BOOKMARK_MENU_CREATE), bookmark_create_menu},
+ { str(LANG_BOOKMARK_MENU_LIST), bookmark_load_menu},
+ { str(LANG_BOOKMARK_MENU_RECENT_BOOKMARKS), bookmark_mrb_load},
+ };
+
+ m=menu_init( items, sizeof items / sizeof(struct menu_items) );
+
+#ifdef HAVE_LCD_CHARCELLS
+ status_set_param(true);
+#endif
+ result = menu_run(m);
+#ifdef HAVE_LCD_CHARCELLS
+ status_set_param(false);
+#endif
+ menu_exit(m);
+
+ settings_save();
+
+ return result;
+}
+
+/* ----------------------------------------------------------------------- */
+/* This is the interface function from the main menu. */
+/* ----------------------------------------------------------------------- */
+bool bookmark_create_menu(void)
+{
+ write_bookmark(true);
+ return false;
+}
+
+/* ----------------------------------------------------------------------- */
+/* This function acts as the load interface from the main menu */
+/* This function determines the bookmark file name and then loads that file*/
+/* for the user. The user can then select a bookmark to load. */
+/* If no file/directory is currently playing, the menu item does not work. */
+/* ----------------------------------------------------------------------- */
+static bool bookmark_load_menu(void)
+{
+ bool success = true;
+ int offset;
+ int seed;
+ int index;
+ char* bookmark;
+
+ if(!system_check())
+ return false;
+ else
+ {
+ char* name = playlist_get_name(global_temp_buffer,
+ sizeof(global_temp_buffer));
+ if (generate_bookmark_file_name(name,
+ global_bookmark_file_name,
+ sizeof(global_bookmark_file_name)))
+ {
+ bookmark = select_bookmark(global_bookmark_file_name);
+ if (!bookmark)
+ return false; /* User exited without selecting a bookmark */
+
+ success = parse_bookmark(bookmark,
+ &index,
+ &offset,
+ &seed,
+ NULL,
+ global_temp_buffer,
+ sizeof(global_temp_buffer),
+ NULL,
+ &global_settings.repeat_mode,
+ &global_settings.playlist_shuffle,
+ NULL, 0);
+ }
+ else
+ {
+ /* something bad happened while creating bookmark name*/
+ success = false;
+ }
+
+ if (success)
+ bookmark_play(global_temp_buffer, index, offset, seed);
+ }
+
+ return success;
+}
+
+/* ----------------------------------------------------------------------- */
+/* Gives the user a list of the Most Recent Bookmarks. This is an */
+/* interface function */
+/* ----------------------------------------------------------------------- */
+bool bookmark_mrb_load()
+{
+ bool success = true;
+ int offset;
+ int seed;
+ int index;
+ char* bookmark;
+
+ bookmark = select_bookmark(RECENT_BOOKMARK_FILE);
+ if (!bookmark)
+ return false; /* User exited without selecting a bookmark */
+
+ success = parse_bookmark(bookmark,
+ &index,
+ &offset,
+ &seed,
+ NULL,
+ global_temp_buffer,
+ sizeof(global_temp_buffer),
+ NULL,
+ &global_settings.repeat_mode,
+ &global_settings.playlist_shuffle,
+ NULL, 0);
+
+ if (success)
+ bookmark_play(global_temp_buffer, index, offset, seed);
+
+ return success;
+}
+
+
+/* ----------------------------------------------------------------------- */
+/* This function handles an autobookmark creation. This is an interface */
+/* function. */
+/* ----------------------------------------------------------------------- */
+bool bookmark_autobookmark(void)
+{
+ /* prompts the user as to create a bookmark */
+ bool done = false;
+ int key = 0;
+
+ if (!system_check())
+ return false;
+
+ mpeg_pause(); /* first pause playback */
+ switch (global_settings.autocreatebookmark)
+ {
+ case BOOKMARK_YES:
+ return write_bookmark(true);
+
+ case BOOKMARK_NO:
+ return false;
+
+ case BOOKMARK_RECENT_ONLY_YES:
+ return write_bookmark(false);
+ }
+
+ /* Prompting user to confirm bookmark creation */
+ lcd_clear_display();
+#ifdef HAVE_LCD_BITMAP
+ lcd_puts(0,0, str(LANG_AUTO_BOOKMARK_QUERY));
+ lcd_puts(0,1, str(LANG_CONFIRM_WITH_PLAY_RECORDER));
+ lcd_puts(0,2, str(LANG_CANCEL_WITH_ANY_RECORDER));
+#else
+ status_draw(false);
+ lcd_puts(0,0, str(LANG_AUTO_BOOKMARK_QUERY));
+ lcd_puts(0,1,str(LANG_RESUME_CONFIRM_PLAYER));
+#endif
+ lcd_update();
+
+ while (!done)
+ {
+ /* Wait for a key to be pushed */
+ key = button_get(true);
+ switch (key)
+ {
+ case BUTTON_DOWN | BUTTON_REL:
+ case BUTTON_ON | BUTTON_REL:
+#ifdef HAVE_RECORDER_KEYPAD
+ case BUTTON_OFF | BUTTON_REL:
+ case BUTTON_RIGHT | BUTTON_REL:
+ case BUTTON_UP | BUTTON_REL:
+#endif
+ case BUTTON_LEFT | BUTTON_REL:
+ done = true;
+ break;
+
+ case BUTTON_PLAY | BUTTON_REL:
+ if (global_settings.autocreatebookmark ==
+ BOOKMARK_RECENT_ONLY_ASK)
+ write_bookmark(false);
+ else
+ write_bookmark(true);
+ done = true;
+ break;
+
+ case SYS_USB_CONNECTED:
+ usb_screen();
+#ifdef HAVE_LCD_CHARCELLS
+ status_set_param(true);
+#endif
+ return false;
+ }
+ }
+ return true;
+}
+
+/* ----------------------------------------------------------------------- */
+/* This function takes the current current resume information and writes */
+/* that to the beginning of the bookmark file. */
+/* This file will contain N number of bookmarks in the following format: */
+/* resume_index*resume_offset*resume_seed*resume_first_index* */
+/* resume_file*milliseconds*MP3 Title* */
+/* ------------------------------------------------------------------------*/
+static bool write_bookmark(bool create_bookmark_file)
+{
+ bool success=false;
+ char* bookmark;
+
+ if (!system_check())
+ return false; /* something didn't happen correctly, do nothing */
+
+ bookmark = create_bookmark();
+ if (!bookmark)
+ return false; /* something didn't happen correctly, do nothing */
+
+ if (global_settings.usemrb)
+ success = add_bookmark(RECENT_BOOKMARK_FILE, bookmark);
+
+
+ /* writing the bookmark */
+ if (create_bookmark_file)
+ {
+ char* name = playlist_get_name(global_temp_buffer,
+ sizeof(global_temp_buffer));
+ if (generate_bookmark_file_name(name,
+ global_bookmark_file_name,
+ sizeof(global_bookmark_file_name)))
+ {
+ success = add_bookmark(global_bookmark_file_name, bookmark);
+ }
+ }
+
+ if (success)
+ splash(HZ, true, str(LANG_BOOKMARK_CREATE_SUCCESS));
+ else
+ splash(HZ, true, str(LANG_BOOKMARK_CREATE_FAILURE));
+
+ return true;
+}
+
+/* ----------------------------------------------------------------------- */
+/* This function adds a bookmark to a file. */
+/* ------------------------------------------------------------------------*/
+static bool add_bookmark(char* bookmark_file_name, char* bookmark)
+{
+ int temp_bookmark_file = 0;
+ int bookmark_file = 0;
+ int bookmark_count = 0;
+ char* playlist = NULL;
+ char* cp;
+ int len = 0;
+ bool unique = false;
+
+ /* Opening up a temp bookmark file */
+ snprintf(global_temp_buffer, sizeof(global_temp_buffer),
+ "%s.tmp", bookmark_file_name);
+ temp_bookmark_file = open(global_temp_buffer,
+ O_WRONLY | O_CREAT | O_TRUNC);
+ if (temp_bookmark_file < 0)
+ return false; /* can't open the temp file */
+
+ if (!strcmp(bookmark_file_name,RECENT_BOOKMARK_FILE) &&
+ (global_settings.usemrb == BOOKMARK_UNIQUE_ONLY))
+ {
+ playlist = strchr(bookmark,'/');
+ cp = strrchr(bookmark,';');
+ len = cp - playlist;
+ unique = true;
+ }
+
+ /* Writing the new bookmark to the begining of the temp file */
+ write(temp_bookmark_file, bookmark, strlen(bookmark));
+ write(temp_bookmark_file, "\n", 1);
+
+ /* Reading in the previous bookmarks and writing them to the temp file */
+ bookmark_file = open(bookmark_file_name, O_RDONLY);
+ if (bookmark_file >= 0)
+ {
+ while (read_line(bookmark_file, global_read_buffer,
+ sizeof(global_read_buffer)))
+ {
+ if (unique)
+ {
+ cp=strchr(global_read_buffer,'/');
+ if (check_bookmark(global_read_buffer) &&
+ strncmp(playlist,cp,len))
+ {
+ bookmark_count++;
+ write(temp_bookmark_file, global_read_buffer,
+ strlen(global_read_buffer));
+ write(temp_bookmark_file, "\n", 1);
+ }
+ }
+ else
+ {
+ if (check_bookmark(global_read_buffer))
+ {
+ bookmark_count++;
+ write(temp_bookmark_file, global_read_buffer,
+ strlen(global_read_buffer));
+ write(temp_bookmark_file, "\n", 1);
+ }
+ }
+ }
+ close(bookmark_file);
+ }
+ close(temp_bookmark_file);
+
+ remove(bookmark_file_name);
+ rename(global_temp_buffer, bookmark_file_name);
+
+ return true;
+}
+
+
+/* ----------------------------------------------------------------------- */
+/* This function takes the system resume data and formats it into a valid */
+/* bookmark. */
+/* ----------------------------------------------------------------------- */
+static char* create_bookmark()
+{
+ int resume_index = 0;
+ char *file;
+
+ /* grab the currently playing track */
+ struct mp3entry *id3 = mpeg_current_track();
+ if(!id3)
+ return NULL;
+
+ /* Get some basic resume information */
+ /* queue_resume and queue_resume_index are not used and can be ignored.*/
+ playlist_get_resume_info(&resume_index);
+
+ /* Get the currently playing file minus the path */
+ /* This is used when displaying the available bookmarks */
+ file = strrchr(id3->path,'/');
+ if(NULL == file)
+ return NULL;
+
+ /* create the bookmark */
+ snprintf(global_bookmark, sizeof(global_bookmark),
+ "%d;%d;%d;%d;%d;%d;%d;%s;%s",
+ resume_index,
+ id3->offset,
+ playlist_get_seed(),
+ 0,
+ id3->elapsed,
+ global_settings.repeat_mode,
+ global_settings.playlist_shuffle,
+ playlist_get_name(global_temp_buffer,sizeof(global_temp_buffer)),
+ file+1);
+
+ /* checking to see if the bookmark is valid */
+ if (check_bookmark(global_bookmark))
+ return global_bookmark;
+ else
+ return NULL;
+}
+
+static bool check_bookmark(char* bookmark)
+{
+ return parse_bookmark(bookmark,
+ NULL,NULL,NULL, NULL,
+ NULL,0,NULL,NULL,
+ NULL, NULL, 0);
+}
+
+/* ----------------------------------------------------------------------- */
+/* This function will determine if an autoload is necessary. This is an */
+/* interface function. */
+/* ------------------------------------------------------------------------*/
+bool bookmark_autoload(char* file)
+{
+ int key;
+ int fd;
+ bool done = false;
+
+ if(global_settings.autoloadbookmark == BOOKMARK_NO)
+ return false;
+
+ /*Checking to see if a bookmark file exists.*/
+ if(!generate_bookmark_file_name(file,
+ global_bookmark_file_name,
+ sizeof(global_bookmark_file_name)))
+ {
+ return false;
+ }
+
+ fd = open(global_bookmark_file_name, O_RDONLY);
+ if(fd<0)
+ return false;
+ if(-1 == lseek(fd, 0, SEEK_END))
+ {
+ close(fd);
+ return false;
+ }
+ close(fd);
+
+ if(global_settings.autoloadbookmark == BOOKMARK_YES)
+ {
+ return bookmark_load(global_bookmark_file_name, true);
+ }
+ else
+ {
+ while (button_get(false)); /* clear button queue */
+ /* Prompting user to confirm bookmark load */
+ lcd_clear_display();
+#ifdef HAVE_LCD_BITMAP
+ lcd_puts_scroll(0,0, str(LANG_BOOKMARK_AUTOLOAD_QUERY));
+ lcd_puts(0,1, str(LANG_CONFIRM_WITH_PLAY_RECORDER));
+ lcd_puts(0,2, str(LANG_BOOKMARK_SELECT_LIST_BOOKMARKS));
+ lcd_puts(0,3, str(LANG_CANCEL_WITH_ANY_RECORDER));
+#else
+ status_draw(false);
+ lcd_puts_scroll(0,0, str(LANG_BOOKMARK_AUTOLOAD_QUERY));
+ lcd_puts(0,1,str(LANG_RESUME_CONFIRM_PLAYER));
+#endif
+ lcd_update();
+
+ sleep(100);
+
+ while(!done)
+ {
+ /* Wait for a key to be pushed */
+ while (button_get(false)); /* clear button queue */
+ key = button_get(true);
+ switch(key)
+ {
+ default:
+ return false;
+#ifdef HAVE_LCD_BITMAP
+ case BUTTON_DOWN:
+ return bookmark_load(global_bookmark_file_name, false);
+#endif
+ case BUTTON_PLAY:
+ return bookmark_load(global_bookmark_file_name, true);
+ case SYS_USB_CONNECTED:
+ status_set_playmode(STATUS_STOP);
+ usb_screen();
+#ifdef HAVE_LCD_CHARCELLS
+ status_set_param(true);
+#endif
+ return true;
+ }
+ }
+ return true;
+ }
+}
+
+/* ----------------------------------------------------------------------- */
+/* This function loads the bookmark information into the resume memory. */
+/* This is an interface function. */
+/* ------------------------------------------------------------------------*/
+bool bookmark_load(char* file, bool autoload)
+{
+ int fd;
+ bool success = true;
+ int offset;
+ int seed;
+ int index;
+ char* bookmark = NULL;;
+
+ if(autoload)
+ {
+ fd = open(file, O_RDONLY);
+ if(fd >= 0)
+ {
+ if(read_line(fd, global_read_buffer, sizeof(global_read_buffer)))
+ bookmark=global_read_buffer;
+ close(fd);
+ }
+ }
+ else
+ {
+ /* This is not an auto-load, so list the bookmarks */
+ bookmark=select_bookmark(file);
+ if(!bookmark)
+ return true; /* User exited without selecting a bookmark */
+ }
+
+ if(bookmark)
+ {
+ success = parse_bookmark(bookmark,
+ &index,
+ &offset,
+ &seed,
+ NULL,
+ global_temp_buffer,
+ sizeof(global_temp_buffer),
+ NULL,
+ &global_settings.repeat_mode,
+ &global_settings.playlist_shuffle,
+ NULL, 0);
+
+ }
+
+ if(success)
+ bookmark_play(global_temp_buffer,index,offset,seed);
+
+ return success;
+}
+
+
+static int get_bookmark_count(char* bookmark_file_name)
+{
+ int read_count = 0;
+ int file = open(bookmark_file_name, O_RDONLY);
+
+ if(file < 0)
+ return -1;
+
+ /* Get the requested bookmark */
+ while(read_line(file, global_read_buffer, sizeof(global_read_buffer)))
+ {
+ if(check_bookmark(global_read_buffer))
+ read_count++;
+ }
+
+ close(file);
+ return read_count;
+
+
+}
+
+
+/* ----------------------------------------------------------------------- */
+/* This displays a the bookmarks in a file and allows the user to */
+/* select one to play. */
+/* ------------------------------------------------------------------------*/
+static char* select_bookmark(char* bookmark_file_name)
+{
+ int bookmark_id = 0;
+ bool delete_this_bookmark = true;
+ int key = 0;
+ char* bookmark;
+ int bookmark_count = 0;
+
+ while(true)
+ {
+ /* Handles the case where the user wants to go below the 0th bookmark */
+ if(bookmark_id < 0)
+ bookmark_id = 0;
+
+ if(delete_this_bookmark)
+ {
+ bookmark_count = get_bookmark_count(bookmark_file_name);
+ delete_this_bookmark = false;
+ }
+
+ bookmark = get_bookmark(bookmark_file_name, bookmark_id);
+
+ if (!bookmark)
+ {
+ /* if there were no bookmarks in the file, delete the file and exit. */
+ if(bookmark_id == 0)
+ {
+ splash(HZ, true, str(LANG_BOOKMARK_LOAD_EMPTY));
+ remove(bookmark_file_name);
+ while (button_get(false)); /* clear button queue */
+ return NULL;
+ }
+ else
+ {
+ bookmark_id--;
+ }
+ }
+ else
+ {
+ display_bookmark(bookmark, bookmark_id, bookmark_count);
+ }
+
+ /* waiting for the user to click a button */
+ while (button_get(false)); /* clear button queue */
+ key = button_get(true);
+ switch(key)
+ {
+ case BUTTON_PLAY:
+ /* User wants to use this bookmark */
+ return bookmark;
+
+ case BUTTON_ON | BUTTON_PLAY:
+ /* User wants to delete this bookmark */
+ delete_this_bookmark = true;
+ break;
+
+ case SYS_USB_CONNECTED:
+ usb_screen();
+#ifdef HAVE_LCD_CHARCELLS
+ status_set_param(true);
+#endif
+ return NULL;
+#ifdef HAVE_RECORDER_KEYPAD
+ case BUTTON_UP:
+ bookmark_id--;
+ break;
+
+ case BUTTON_DOWN:
+ bookmark_id++;
+ break;
+
+ case BUTTON_LEFT:
+ case BUTTON_OFF:
+ return NULL;
+#else
+ case BUTTON_LEFT:
+ bookmark_id--;
+ break;
+
+ case BUTTON_RIGHT:
+ bookmark_id++;
+ break;
+
+ case BUTTON_STOP:
+ return NULL;
+#endif
+ }
+
+ if (delete_this_bookmark)
+ {
+ delete_bookmark(bookmark_file_name, bookmark_id);
+ bookmark_id--;
+ }
+ }
+
+ return NULL;
+}
+
+
+/* ----------------------------------------------------------------------- */
+/* This function takes a location in a bookmark file and deletes that */
+/* bookmark. */
+/* ------------------------------------------------------------------------*/
+static bool delete_bookmark(char* bookmark_file_name, int bookmark_id)
+{
+ int temp_bookmark_file = 0;
+ int bookmark_file = 0;
+ int bookmark_count = 0;
+
+ /* Opening up a temp bookmark file */
+ snprintf(global_temp_buffer, sizeof(global_temp_buffer),
+ "%s.tmp", bookmark_file_name);
+ temp_bookmark_file = open(global_temp_buffer,
+ O_WRONLY | O_CREAT | O_TRUNC);
+ bookmark_file = open(bookmark_file_name, O_RDONLY);
+
+ if (temp_bookmark_file < 0 || bookmark_file < 0)
+ return false; /* can't open one of the files */
+
+ /* Reading in the previous bookmarks and writing them to the temp file */
+ while (read_line(bookmark_file, global_read_buffer,
+ sizeof(global_read_buffer)))
+ {
+ if (check_bookmark(global_read_buffer))
+ {
+ if (bookmark_id != bookmark_count)
+ {
+ write(temp_bookmark_file, global_read_buffer,
+ strlen(global_read_buffer));
+ write(temp_bookmark_file, "\n", 1);
+ }
+ bookmark_count++;
+ }
+ }
+
+ close(bookmark_file);
+ close(temp_bookmark_file);
+
+ remove(bookmark_file_name);
+ rename(global_temp_buffer, bookmark_file_name);
+
+ return true;
+}
+
+/* ----------------------------------------------------------------------- */
+/* This function parses a bookmark and displays it for the user. */
+/* ------------------------------------------------------------------------*/
+static void display_bookmark(char* bookmark,
+ int bookmark_id,
+ int bookmark_count)
+{
+ int resume_index = 0;
+ int ms = 0;
+ int repeat_mode = 0;
+ bool playlist_shuffle = false;
+ char MP3_file_name[45];
+ int len;
+ char *dot;
+
+ /* getting the index and the time into the file */
+ parse_bookmark(bookmark,
+ &resume_index, NULL, NULL, NULL, NULL, 0,
+ &ms, &repeat_mode, &playlist_shuffle,
+ MP3_file_name, sizeof(MP3_file_name));
+
+ lcd_clear_display();
+ lcd_stop_scroll();
+
+#ifdef HAVE_LCD_BITMAP
+ /* bookmark shuffle and repeat states*/
+ switch (repeat_mode)
+ {
+ case REPEAT_ONE:
+ statusbar_icon_play_mode(Icon_RepeatOne);
+ break;
+
+ case REPEAT_ALL:
+ statusbar_icon_play_mode(Icon_Repeat);
+ break;
+ }
+ if(playlist_shuffle)
+ statusbar_icon_shuffle();
+
+ /* File Name */
+ len=strlen(MP3_file_name);
+ if (len>3)
+ dot=strrchr(MP3_file_name + len - 4, '.');
+ else
+ dot=NULL;
+ if (dot)
+ *dot='\0';
+ lcd_puts_scroll(0, 0, MP3_file_name);
+ if (dot)
+ *dot='.';
+
+ /* bookmark number */
+ snprintf(global_temp_buffer, sizeof(global_temp_buffer), "%s: %2d/%2d",
+ str(LANG_BOOKMARK_SELECT_BOOKMARK_TEXT),
+ bookmark_id + 1, bookmark_count);
+ lcd_puts_scroll(0, 1, global_temp_buffer);
+
+ /* bookmark resume index */
+ snprintf(global_temp_buffer, sizeof(global_temp_buffer), "%s: %2d",
+ str(LANG_BOOKMARK_SELECT_INDEX_TEXT), resume_index+1);
+ lcd_puts_scroll(0, 2, global_temp_buffer);
+
+ /* elapsed time*/
+ snprintf(global_temp_buffer, sizeof(global_temp_buffer), "%s: %d:%02d",
+ str(LANG_BOOKMARK_SELECT_TIME_TEXT),
+ ms / 60000,
+ ms % 60000 / 1000);
+ lcd_puts_scroll(0, 3, global_temp_buffer);
+
+ /* commands */
+ lcd_puts_scroll(0, 4, str(LANG_BOOKMARK_SELECT_PLAY));
+ lcd_puts_scroll(0, 5, str(LANG_BOOKMARK_SELECT_EXIT));
+ lcd_puts_scroll(0, 6, str(LANG_BOOKMARK_SELECT_DELETE));
+#else
+ len=strlen(MP3_file_name);
+ if (len>3)
+ dot=strrchr(MP3_file_name+len-4,'.');
+ else
+ dot=NULL;
+ if (dot)
+ *dot='\0';
+ snprintf(global_temp_buffer, sizeof(global_temp_buffer),
+ "%2d, %d:%02d, %s,",
+ (bookmark_count+1),
+ ms / 60000,
+ ms % 60000 / 1000,
+ MP3_file_name);
+ status_draw(false);
+ lcd_puts_scroll(0,0,global_temp_buffer);
+ lcd_puts(0,1,str(LANG_RESUME_CONFIRM_PLAYER));
+ if (dot)
+ *dot='.';
+#endif
+ lcd_update();
+}
+
+/* ----------------------------------------------------------------------- */
+/* This function retrieves a given bookmark from a file. */
+/* If the bookmark requested is beyond the number of bookmarks available */
+/* in the file, it will return the last one. */
+/* It also returns the index number of the bookmark in the file */
+/* ------------------------------------------------------------------------*/
+static char* get_bookmark(char* bookmark_file, int bookmark_count)
+{
+ int read_count = -1;
+ int result = 0;
+ int file = open(bookmark_file, O_RDONLY);
+
+ if (file < 0)
+ return NULL;
+
+ /* Get the requested bookmark */
+ while (read_count < bookmark_count)
+ {
+ /*Reading in a single bookmark */
+ result = read_line(file,
+ global_read_buffer,
+ sizeof(global_read_buffer));
+
+ /* Reading past the last bookmark in the file
+ causes the loop to stop */
+ if (result <= 0)
+ break;
+
+ read_count++;
+ }
+
+ close(file);
+ if (read_count == bookmark_count)
+ return global_read_buffer;
+ else
+ return NULL;
+}
+
+/* ----------------------------------------------------------------------- */
+/* This function takes a bookmark and parses it. This function also */
+/* validates the bookmark. Passing in NULL for an output variable */
+/* indicates that value is not requested. */
+/* ----------------------------------------------------------------------- */
+static bool parse_bookmark(char *bookmark,
+ int *resume_index,
+ int *resume_offset,
+ int *resume_seed,
+ int *resume_first_index,
+ char* resume_file,
+ unsigned int resume_file_size,
+ int* ms,
+ int * repeat_mode, bool *shuffle,
+ char* file_name,
+ unsigned int max_file_name_size)
+{
+ /* First check to see if a valid line was passed in. */
+ int bookmark_len = strlen(bookmark);
+ int local_resume_index = 0;
+ int local_resume_offset = 0;
+ int local_resume_seed = 0;
+ int local_resume_first_index = 0;
+ int local_mS = 0;
+ int local_shuffle = 0;
+ int local_repeat_mode = 0;
+ char* local_resume_file = NULL;
+ char* local_file_name = NULL;
+ char* field;
+ char* end;
+ static char bookmarkcopy[MAX_BOOKMARK_SIZE];
+
+ /* Don't do anything if the bookmark length is 0 */
+ if (bookmark_len <= 0)
+ return false;
+
+ /* Making a dup of the bookmark to use with strtok_r */
+ strncpy(bookmarkcopy, bookmark, sizeof(bookmarkcopy));
+ bookmarkcopy[sizeof(bookmarkcopy) - 1] = 0;
+
+ /* resume_index */
+ if ((field = strtok_r(bookmarkcopy, ";", &end)))
+ local_resume_index = atoi(field);
+ else
+ return false;
+
+ /* resume_offset */
+ if ((field = strtok_r(NULL, ";", &end)))
+ local_resume_offset = atoi(field);
+ else
+ return false;
+
+ /* resume_seed */
+ if ((field = strtok_r(NULL, ";", &end)))
+ local_resume_seed = atoi(field);
+ else
+ return false;
+
+ /* resume_first_index */
+ if ((field = strtok_r(NULL, ";", &end)))
+ local_resume_first_index = atoi(field);
+ else
+ return false;
+
+ /* Milliseconds into MP3. Used for the bookmark select menu */
+ if ((field = strtok_r(NULL, ";", &end)))
+ local_mS = atoi(field);
+ else
+ return false;
+
+ /* repeat_mode */
+ if ((field = strtok_r(NULL, ";", &end)))
+ local_repeat_mode = atoi(field);
+ else
+ return false;
+
+ /* shuffle mode */
+ if ((field = strtok_r(NULL, ";", &end)))
+ local_shuffle = atoi(field);
+ else
+ return false;
+
+ /* resume_file & file_name (for the bookmark select menu)*/
+ if (end)
+ {
+ local_resume_file = strtok_r(NULL, ";", &end);
+ if (local_resume_file[strlen(local_resume_file) - 1] == '/')
+ local_resume_file[strlen(local_resume_file) - 1] = '\0';
+
+ if (end)
+ local_file_name = strtok_r(NULL, ";", &end);
+ }
+ else
+ return false;
+
+ /* Only return the values the calling function wants */
+ if (resume_index)
+ *resume_index = local_resume_index;
+
+ if (resume_offset)
+ *resume_offset = local_resume_offset;
+
+ if (resume_seed)
+ *resume_seed = local_resume_seed;
+
+ if (resume_first_index)
+ *resume_first_index = local_resume_first_index;
+
+ if (resume_file && local_resume_file)
+ {
+ strncpy(resume_file, local_resume_file,
+ MIN(strlen(local_resume_file), resume_file_size-1));
+ resume_file[MIN(strlen(local_resume_file), resume_file_size-1)]=0;
+ }
+
+ if (ms)
+ *ms = local_mS;
+
+ if (shuffle)
+ *shuffle = local_shuffle;
+
+ if (repeat_mode)
+ *repeat_mode = local_repeat_mode;
+
+ if (file_name && local_file_name)
+ {
+ strncpy(file_name, local_file_name,
+ MIN(strlen(local_file_name),max_file_name_size-1));
+ file_name[MIN(strlen(local_file_name),max_file_name_size-1)]=0;
+ }
+
+ return true;
+}
+
+/* ----------------------------------------------------------------------- */
+/* This function is used by multiple functions and is used to generate a */
+/* bookmark named based off of the input. */
+/* Changing this function could result in how the bookmarks are stored. */
+/* it would be here that the centralized/decentralized bookmark code */
+/* could be placed. */
+/* ----------------------------------------------------------------------- */
+static bool generate_bookmark_file_name(char *in, char *out,
+ unsigned int max_length)
+{
+ char* cp;
+
+ if (!in || !out || max_length <= 0)
+ return false;
+
+ if (max_length < strlen(in)+6)
+ return false;
+
+ /* if this is a root dir MP3, rename the boomark file root_dir.bmark */
+ /* otherwise, name it based on the in variable */
+ cp = in;
+
+ cp = in + strlen(in) - 1;
+ if (*cp == '/')
+ *cp = 0;
+
+ cp = in;
+ if (*cp == '/')
+ cp++;
+
+ if (strlen(in) > 0)
+ snprintf(out, max_length, "/%s.%s", cp, "bmark");
+ else
+ snprintf(out, max_length, "/root_dir.%s", "bmark");
+
+ return true;
+}
+
+/* ----------------------------------------------------------------------- */
+/* Checks the current state of the system and returns if it is in a */
+/* bookmarkable state. */
+/* ----------------------------------------------------------------------- */
+/* Inputs: */
+/* ----------------------------------------------------------------------- */
+/* Outputs: */
+/* return bool: Indicates if the system was in a bookmarkable state */
+/* ----------------------------------------------------------------------- */
+static bool system_check(void)
+{
+ int resume_index = 0;
+ struct mp3entry *id3 = mpeg_current_track();
+
+ if (!id3)
+ {
+ /* no track playing */
+ return false;
+ }
+
+ /* Checking to see if playing a queued track */
+ if (playlist_get_resume_info(&resume_index) == -1)
+ {
+ /* something bad happened while getting the queue information */
+ return false;
+ }
+ else if (playlist_modified())
+ {
+ /* can't bookmark while in the queue */
+ return false;
+ }
+
+ return true;
+}
+
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 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ *
+ * Copyright (C) 2003 by Benjamin Metzler
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef __BOOKMARK_H__
+#define __BOOKMARK_H__
+
+#include <stdbool.h>
+
+bool bookmark_menu(void);
+bool bookmark_autobookmark(void);
+bool bookmark_create_menu(void);
+bool bookmark_mrb_load(void);
+bool bookmark_autoload(char* file);
+bool bookmark_load(char* file, bool autoload);
+void bookmark_play(char* resume_file, int index, int offset, int seed);
+
+#endif /* __BOOKMARK_H__ */
+
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
eng: "Upside Down"
new:
+#Auto bookmark prompts
+id: LANG_BOOKMARK_AUTOLOAD_QUERY
+desc: prompt for user to decide to create a bookmark
+eng: "Load Last Bookmark?"
+new:
+
+id: LANG_AUTO_BOOKMARK_QUERY
+desc: prompt for user to decide to create an bookmark
+eng: "Create a Bookmark?"
+new:
+
+# Bookmark Select Menu Text
+id: LANG_BOOKMARK_SELECT_LIST_BOOKMARKS
+desc: From the auto-load screen, allows user to list all bookmarks
+eng: "Down = List"
+new:
+
+id: LANG_BOOKMARK_SELECT_EXIT
+desc: From the bookmark list screen, allows user to exit
+eng: "OFF = Exit"
+new:
+
+id: LANG_BOOKMARK_SELECT_BOOKMARK_TEXT
+desc: Used on the bookmark select window to label bookmark number
+eng: "Bookmark"
+new:
+
+id: LANG_BOOKMARK_SELECT_INDEX_TEXT
+desc: Used on the bookmark select window to label index number
+eng: "Index"
+new:
+
+id: LANG_BOOKMARK_SELECT_TIME_TEXT
+desc: Used on the bookmark select window to label elapsed time
+eng: "Time"
+new:
+
+id: LANG_BOOKMARK_SELECT_PLAY
+desc: Used on the bookmark select window to indicated the play option
+eng: "PLAY = Select"
+new:
+
+id: LANG_BOOKMARK_SELECT_DELETE
+desc: Used on the bookmark select window to indicated the bookmark delete option
+eng: "ON+Play = Delete"
+new:
+
+# Bookmark creation/failure text
+id: LANG_BOOKMARK_CREATE_SUCCESS
+desc: Indicates bookmark was successfully created
+eng: "Bookmark Created"
+new:
+
+id: LANG_BOOKMARK_CREATE_FAILURE
+desc: Indicates bookmark was not created
+eng: "Bookmark Failed!"
+new:
+
+# Bookmark creation/failure text
+id: LANG_BOOKMARK_LOAD_EMPTY
+desc: Indicates bookmark was empty
+eng: "Bookmark Empty"
+new:
+
+# Bookmark Settings Text
+id: LANG_BOOKMARK_SETTINGS
+desc: in general settings
+eng: "Bookmarking"
+new:
+
+id: LANG_BOOKMARK_SETTINGS_AUTOLOAD
+desc: prompt for user to decide to create a bookmark
+eng: "Load Last Bookmark"
+new:
+
+id: LANG_BOOKMARK_SETTINGS_AUTOCREATE
+desc: prompt for user to decide to create an bookmark
+eng: "Bookmark on Stop"
+new:
+
+id: LANG_BOOKMARK_SETTINGS_MAINTAIN_RECENT_BOOKMARKS
+desc: Configuration option to maintain a list of recent bookmarks
+eng: "Maintain a List of Recent Bookmarks?"
+new:
+
+id: LANG_BOOKMARK_SETTINGS_RECENT_ONLY_YES
+desc: Save in recent bookmarks only
+eng: "Yes - Recent only"
+new:
+
+id: LANG_BOOKMARK_SETTINGS_RECENT_ONLY_ASK
+desc: Save in recent bookmarks only
+eng: "Ask - Recent only"
+new:
+
+
+id: LANG_BOOKMARK_SETTINGS_UNIQUE_ONLY
+desc: Save only on bookmark for each playlist in recent bookmarks
+eng: "Unique only"
+new:
+
+# Main Bookmarks Menu
+id: LANG_BOOKMARK_MENU
+desc: Text on main menu to get to bookmark commands
+eng: "Bookmarks"
+new:
+
+id: LANG_BOOKMARK_MENU_CREATE
+desc: Used off of the bookmark menu to create a bookmark
+eng: "Create Bookmark"
+new:
+
+id: LANG_BOOKMARK_MENU_LIST
+desc: Used off of the bookmark menu to list available bookmarks for the currently playing directory or M3U
+eng: "List Bookmarks"
+new:
+
+id: LANG_BOOKMARK_MENU_RECENT_BOOKMARKS
+desc: Text for the menu text to access the most recent bookmarks list
+eng: "Recent Bookmarks"
+new:
+
id: LANG_RECORD_PRERECORD
desc: in recording and radio screen
eng: "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 @@
#include "status.h"
#include "fat.h"
#include "sleeptimer.h"
+#include "bookmark.h"
#include "wps.h"
#include "buffer.h"
#include "screens.h"
@@ -269,7 +270,10 @@ bool main_menu(void)
int i = 0;
/* main menu */
- struct menu_items items[14];
+ struct menu_items items[15];
+
+ items[i].desc = str(LANG_BOOKMARK_MENU);
+ items[i++].function = bookmark_menu;
items[i].desc = str(LANG_SOUND_SETTINGS);
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)
playlist.first_index = 0;
playlist.amount = 0;
playlist.last_insert_pos = -1;
+ playlist.seed = 0;
+ playlist.shuffle_modified = false;
+ playlist.deleted = false;
+ playlist.num_inserted_tracks = 0;
playlist.shuffle_flush = false;
if (!resume)
@@ -412,6 +416,7 @@ static int add_track_to_playlist(char *filename, int position, bool queue,
playlist.indices[insert_position] = flags | seek_pos;
playlist.amount++;
+ playlist.num_inserted_tracks++;
return insert_position;
}
@@ -528,16 +533,24 @@ static int add_directory_to_playlist(char *dirname, int *position, bool queue,
static int remove_track_from_playlist(int position, bool write)
{
int i;
+ bool inserted;
if (playlist.amount <= 0)
return -1;
+ inserted = playlist.indices[position] & PLAYLIST_INSERT_TYPE_MASK;
+
/* shift indices now that track has been removed */
for (i=position; i<playlist.amount; i++)
playlist.indices[i] = playlist.indices[i+1];
playlist.amount--;
+ if (inserted)
+ playlist.num_inserted_tracks--;
+ else
+ playlist.deleted = true;
+
/* update stored indices if needed */
if (position < playlist.index)
playlist.index--;
@@ -622,6 +635,10 @@ static int randomise_playlist(unsigned int seed, bool start_current, bool write)
/* indices have been moved so last insert position is no longer valid */
playlist.last_insert_pos = -1;
+ playlist.seed = seed;
+ if (playlist.num_inserted_tracks > 0 || playlist.deleted)
+ playlist.shuffle_modified = true;
+
if (write)
{
/* Don't write to disk immediately. Instead, save in settings and
@@ -652,6 +669,8 @@ static int sort_playlist(bool start_current, bool write)
/* indices have been moved so last insert position is no longer valid */
playlist.last_insert_pos = -1;
+ if (!playlist.num_inserted_tracks && !playlist.deleted)
+ playlist.shuffle_modified = false;
if (write && playlist.control_fd >= 0)
{
/* Don't write to disk immediately. Instead, save in settings and
@@ -1898,9 +1917,26 @@ int playlist_next(int steps)
return index;
}
+bool playlist_modified(void)
+{
+ if ((mpeg_status() & MPEG_STATUS_PLAY))
+ {
+ if (playlist.shuffle_modified ||
+ playlist.deleted ||
+ playlist.num_inserted_tracks > 0)
+ return true;
+ }
+ return false;
+}
+
+int playlist_get_seed(void)
+{
+ return playlist.seed;
+}
+
/* Get resume info for current playing song. If return value is -1 then
settings shouldn't be saved. */
-int playlist_get_resume_info(short *resume_index)
+int playlist_get_resume_info(int *resume_index)
{
*resume_index = playlist.index;
@@ -1924,6 +1960,16 @@ int playlist_get_first_index(void)
return playlist.first_index;
}
+char *playlist_get_name(char *buf, int buf_size)
+{
+ snprintf(buf, buf_size, "%s", playlist.filename);
+
+ if (!buf[0])
+ return NULL;
+
+ return buf;
+}
+
/* returns number of tracks in playlist (includes queued/inserted tracks) */
int playlist_amount(void)
{
@@ -1937,14 +1983,14 @@ char *playlist_name(char *buf, int buf_size)
snprintf(buf, buf_size, "%s", playlist.filename+playlist.dirlen);
- if (0 == buf[0])
+ if (!buf[0])
return NULL;
/* Remove extension */
sep = strrchr(buf, '.');
- if (NULL != sep)
+ if (sep)
*sep = 0;
-
+
return buf;
}
@@ -2001,7 +2047,7 @@ int playlist_save(char *filename)
/* use current working directory as base for pathname */
if (format_track_path(tmp_buf, filename, sizeof(tmp_buf),
- strlen(filename)+1, getcwd(NULL, -1)) < 0)
+ strlen(filename)+1, getcwd(NULL, -1)) < 0)
return -1;
fd = open(tmp_buf, O_CREAT|O_WRONLY|O_TRUNC);
@@ -2043,15 +2089,14 @@ int playlist_save(char *filename)
if (fprintf(fd, "%s\n", tmp_buf) < 0)
{
- splash(HZ*2, true,
- str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR));
+ splash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR));
result = -1;
break;
}
count++;
- if ((count%PLAYLIST_DISPLAY_COUNT) == 0)
+ if ((count % PLAYLIST_DISPLAY_COUNT) == 0)
display_playlist_count(count, str(LANG_PLAYLIST_SAVE_COUNT));
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
char *buffer; /* buffer for in-ram playlists */
int buffer_size; /* size of buffer */
int buffer_end_pos; /* last position where buffer was written */
- short index; /* index of current playing track */
- short first_index; /* index of first song in playlist */
+ int index; /* index of current playing track */
+ int first_index; /* index of first song in playlist */
int amount; /* number of tracks in the index */
int last_insert_pos; /* last position we inserted a track */
- bool shuffle_flush; /* Does shuffle value need to be flushed? */
+ int seed; /* shuffle seed */
+ bool shuffle_modified; /* has playlist been shuffled with
+ inserted tracks? */
+ bool deleted; /* have any tracks been deleted? */
+ int num_inserted_tracks; /* number of tracks inserted */
+ bool shuffle_flush; /* does shuffle value need to be flushed? */
struct mutex control_mutex; /* mutex for control file access */
};
@@ -75,13 +80,16 @@ int playlist_start(int start_index, int offset);
bool playlist_check(int steps);
char *playlist_peek(int steps);
int playlist_next(int steps);
-int playlist_get_resume_info(short *resume_index);
+int playlist_get_resume_info(int *resume_index);
int playlist_get_display_index(void);
int playlist_get_first_index(void);
int playlist_amount(void);
char *playlist_name(char *buf, int buf_size);
int playlist_get_track_info(int index, struct playlist_track_info* info);
int playlist_save(char *filename);
+int playlist_get_seed(void);
+char *playlist_get_name(char *buf, int buf_size);
+bool playlist_modified(void);
enum {
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 {
int char_width; /* Width (in pixels) of a character */
int num_tracks; /* Number of tracks in playlist */
- short current_playing_track;/* Index of current playing track */
+ int current_playing_track; /* Index of current playing track */
int num_loaded; /* Number of track entries loaded in viewer */
int first_index; /* Index of first loaded track */
@@ -643,7 +643,7 @@ bool playlist_viewer(void)
while (!exit)
{
- short track;
+ int track;
/* Timeout so we can determine if play status has changed */
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] =
{ 0x2a, 0x7f, 0x41, 0x41, 0x7f, 0x2a }, /* UCL flash file: chip */
{ 0x70, 0x70, 0x7f, 0x7f, 0x70, 0x70 }, /* Chip8 game: joystick */
{ 0x5d, 0x7f, 0x5d, 0x7f, 0x5d, 0x7f }, /* Video file: film strip */
+ { 0xff, 0x81, 0xaf, 0xaa, 0x8c, 0xf8 }, /* Bookmark file */
};
unsigned 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 {
Selected, Cursor, Wps, Mod_Ajz,
Font, Language, Text, Config,
Plugin, Flashfile, Chip8, Video,
+ Bookmark,
LastIcon
};
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 @@
#include "language.h"
#include "wps-display.h"
#include "powermgmt.h"
+#include "bookmark.h"
#include "sprintf.h"
#include "keyboard.h"
#include "version.h"
@@ -143,7 +144,7 @@ Rest of config block, only saved to disk:
caption backlight (bit 1)
car adapter mode (bit 2)
line_in (Player only) (bit 3)
-0xAF [available/unused]
+0xAF <most-recent-bookmarks, auto-bookmark, autoload>
0xB0 peak meter clip hold timeout (bit 0-4), peak meter performance (bit 7)
0xB1 peak meter release step size, peak_meter_dbfs (bit 7)
0xB2 peak meter min either in -db or in percent
@@ -419,6 +420,9 @@ int settings_save( void )
((global_settings.caption_backlight & 1) << 1) |
((global_settings.car_adapter_mode & 1) << 2) |
((global_settings.line_in & 1) << 3));
+ config_block[0xaf] = ((global_settings.usemrb << 5) |
+ (global_settings.autocreatebookmark << 2) |
+ (global_settings.autoloadbookmark));
config_block[0xb0] = (unsigned char)global_settings.peak_meter_clip_hold |
(global_settings.peak_meter_performance ? 0x80 : 0);
config_block[0xb1] = global_settings.peak_meter_release |
@@ -771,6 +775,9 @@ void settings_load(void)
if (config_block[0xa9] != 0xff)
global_settings.jump_scroll_delay = config_block[0xa9];
#endif
+ global_settings.usemrb = (config_block[0xaf] >> 5) & 3;
+ global_settings.autocreatebookmark = (config_block[0xaf] >> 2) & 7;
+ global_settings.autoloadbookmark = (config_block[0xaf]) & 3;
}
settings_apply();
@@ -1128,6 +1135,21 @@ bool settings_load_config(char* file)
set_cfg_option(&global_settings.recursive_dir_insert, value,
options, 3);
}
+ else if (!strcasecmp(name, "autoload bookmarks"))
+ {
+ static char* options[] = {"off", "on", "ask"};
+ set_cfg_option(&global_settings.autoloadbookmark, value, options, 3);
+ }
+ else if (!strcasecmp(name, "autocreate bookmarks"))
+ {
+ static char* options[] = {"off", "on", "ask","recent only - yes","recent only - ask"};
+ set_cfg_option(&global_settings.autocreatebookmark, value, options, 5);
+ }
+ else if (!strcasecmp(name, "use most-recent-bookmarks"))
+ {
+ static char* options[] = {"off", "on", "unique only"};
+ set_cfg_option(&global_settings.usemrb, value, options, 3);
+ }
}
close(fd);
@@ -1143,6 +1165,7 @@ bool settings_save_config(void)
int fd, i, value;
char filename[MAX_PATH];
char* boolopt[] = {"off","on"};
+ char* triopt[] = {"off","on","ask"};
/* find unused filename */
for (i=0; ; i++) {
@@ -1431,11 +1454,27 @@ bool settings_save_config(void)
#endif
+ fprintf(fd, "#\r\n# Bookmarking\r\n#\r\n");
+ {
+ fprintf(fd, "autoload bookmarks: %s\r\n",
+ triopt[global_settings.autoloadbookmark]);
+ }
+
+ {
+ static char* options[] = {"off", "on", "ask","recent only - on", "recent only - ask"};
+ fprintf(fd, "autocreate bookmarks: %s\r\n",
+ options[global_settings.autocreatebookmark]);
+ }
+
+ {
+ static char* options[] = {"off", "on", "unique only"};
+ fprintf(fd, "UseMRB: %s\r\n", options[global_settings.usemrb]);
+ }
+
fprintf(fd, "#\r\n# Playlists\r\n#\r\n");
{
- static char* options[] = {"off", "on", "ask"};
fprintf(fd, "recursive directory insert: %s\r\n",
- options[global_settings.recursive_dir_insert]);
+ triopt[global_settings.recursive_dir_insert]);
}
close(fd);
@@ -1528,6 +1567,9 @@ void settings_reset(void) {
global_settings.lang_file[0] = 0;
global_settings.runtime = 0;
global_settings.topruntime = 0;
+ global_settings.autocreatebookmark = BOOKMARK_NO;
+ global_settings.autoloadbookmark = BOOKMARK_NO;
+ global_settings.usemrb = BOOKMARK_NO;
global_settings.fade_on_stop = true;
global_settings.caption_backlight = false;
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 @@
#define RESUME_ASK_ONCE 2
#define RESUME_ON 3
+#define BOOKMARK_NO 0
+#define BOOKMARK_YES 1
+#define BOOKMARK_ASK 2
+#define BOOKMARK_UNIQUE_ONLY 2
+#define BOOKMARK_RECENT_ONLY_YES 3
+#define BOOKMARK_RECENT_ONLY_ASK 4
#define FF_REWIND_1000 0
#define FF_REWIND_2000 1
#define FF_REWIND_3000 2
@@ -109,8 +115,8 @@ struct user_settings
/* resume settings */
int resume; /* resume option: 0=off, 1=ask, 2=on */
- short resume_index; /* index in playlist (-1 for no active resume) */
- short resume_first_index; /* index of first track in playlist */
+ int resume_index; /* index in playlist (-1 for no active resume) */
+ int resume_first_index; /* index of first track in playlist */
int resume_offset; /* byte offset in mp3 file */
int resume_seed; /* shuffle seed (-1=no resume shuffle 0=sorted
>0=shuffled) */
@@ -161,6 +167,11 @@ struct user_settings
int bidir_limit; /* bidir scroll length limit */
int scroll_delay; /* delay (in 1/10s) before starting scroll */
int scroll_step; /* pixels to advance per update */
+
+ /* auto bookmark settings */
+ int autoloadbookmark; /* auto load option: 0=off, 1=ask, 2=on */
+ int autocreatebookmark; /* auto create option: 0=off, 1=ask, 2=on */
+ int usemrb; /* use MRB list: 0=No, 1=Yes*/
#ifdef HAVE_LCD_CHARCELLS
int jump_scroll; /* Fast jump when scrolling */
int jump_scroll_delay; /* Delay between jump scroll screens */
@@ -205,6 +216,7 @@ bool set_option(char* string, void* variable, enum optiontype type,
bool set_int(char* string, char* unit, int* variable,
void (*function)(int), int step, int min, int max );
bool set_time(char* string, int timedate[]);
+int read_line(int fd, char* buffer, int buffer_size);
void set_file(char* filename, char* setting, int maxlen);
#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)
names, 4, NULL );
}
+static bool autocreatebookmark(void)
+{
+ char* names[] = { str(LANG_SET_BOOL_NO),
+ str(LANG_SET_BOOL_YES),
+ str(LANG_RESUME_SETTING_ASK),
+ str(LANG_BOOKMARK_SETTINGS_RECENT_ONLY_YES),
+ str(LANG_BOOKMARK_SETTINGS_RECENT_ONLY_ASK) };
+
+ return set_option( str(LANG_BOOKMARK_SETTINGS_AUTOCREATE),
+ &global_settings.autocreatebookmark, INT,
+ names, 5, NULL );
+}
+
+static bool autoloadbookmark(void)
+{
+ char* names[] = { str(LANG_SET_BOOL_NO),
+ str(LANG_SET_BOOL_YES),
+ str(LANG_RESUME_SETTING_ASK) };
+
+ return set_option( str(LANG_BOOKMARK_SETTINGS_AUTOLOAD),
+ &global_settings.autoloadbookmark, INT,
+ names, 3, NULL );
+}
+
+static bool useMRB(void)
+{
+ char* names[] = { str(LANG_SET_BOOL_NO),
+ str(LANG_SET_BOOL_YES),
+ str(LANG_BOOKMARK_SETTINGS_UNIQUE_ONLY)};
+
+ return set_option( str(LANG_BOOKMARK_SETTINGS_MAINTAIN_RECENT_BOOKMARKS),
+ &global_settings.usemrb, INT,
+ names, 3, NULL );
+}
static bool backlight_on_when_charging(void)
{
bool result = set_bool(str(LANG_BACKLIGHT_ON_WHEN_CHARGING),
@@ -789,6 +823,23 @@ static bool playback_settings_menu(void)
return result;
}
+static bool bookmark_settings_menu(void)
+{
+ int m;
+ bool result;
+
+ struct menu_items items[] = {
+ { str(LANG_BOOKMARK_SETTINGS_AUTOCREATE), autocreatebookmark},
+ { str(LANG_BOOKMARK_SETTINGS_AUTOLOAD), autoloadbookmark},
+ { str(LANG_BOOKMARK_SETTINGS_MAINTAIN_RECENT_BOOKMARKS), useMRB},
+ };
+
+ m=menu_init( items, sizeof items / sizeof(struct menu_items) );
+ result = menu_run(m);
+ menu_exit(m);
+
+ return result;
+}
static bool reset_settings(void)
{
bool done=false;
@@ -966,6 +1017,7 @@ bool settings_menu(void)
{ str(LANG_CUSTOM_FONT), font_browse },
#endif
{ str(LANG_SYSTEM), system_settings_menu },
+ { str(LANG_BOOKMARK_SETTINGS),bookmark_settings_menu },
{ str(LANG_SAVE_SETTINGS), settings_save_config },
};
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 @@
#include "language.h"
#include "screens.h"
#include "keyboard.h"
+#include "bookmark.h"
#include "onplay.h"
#include "buffer.h"
#include "plugin.h"
@@ -83,6 +84,9 @@ static struct
{ ".fnt", TREE_ATTR_FONT,Font },
{ ".ch8", TREE_ATTR_CH8, Chip8 },
{ ".rvf", TREE_ATTR_RVF, Video },
+ { ".bmark",TREE_ATTR_BMARK,Bookmark },
+#else
+ { ".bmark", TREE_ATTR_BMARK, -1 },
#endif
#ifndef SIMULATOR
#ifdef HAVE_LCD_BITMAP
@@ -118,6 +122,7 @@ static int boot_size = 0;
static int boot_cluster;
static bool boot_changed = false;
+static bool start_wps = false;
static bool dirbrowse(char *root, int *dirfilter);
void browse_root(void)
@@ -646,9 +651,7 @@ static void start_resume(bool ask_once)
playlist_start(global_settings.resume_index,
global_settings.resume_offset);
- status_set_playmode(STATUS_PLAY);
- status_draw(true);
- wps_show();
+ start_wps = true;
}
else
return;
@@ -930,6 +933,7 @@ static bool dirbrowse(char *root, int *dirfilter)
#ifdef HAVE_RECORDER_KEYPAD
case BUTTON_OFF:
+ bookmark_autobookmark();
mpeg_stop();
status_set_playmode(STATUS_STOP);
status_draw(false);
@@ -988,6 +992,12 @@ static bool dirbrowse(char *root, int *dirfilter)
lcd_stop_scroll();
switch ( file->attr & TREE_ATTR_MASK ) {
case TREE_ATTR_M3U:
+ if (bookmark_autoload(buf))
+ {
+ restore = true;
+ break;
+ }
+
if (playlist_create(currdir, file->name) != -1)
{
if (global_settings.playlist_shuffle)
@@ -999,6 +1009,12 @@ static bool dirbrowse(char *root, int *dirfilter)
break;
case TREE_ATTR_MPA:
+ if (bookmark_autoload(currdir))
+ {
+ restore = true;
+ break;
+ }
+
if (playlist_create(currdir, NULL) != -1)
{
start_index =
@@ -1051,6 +1067,12 @@ static bool dirbrowse(char *root, int *dirfilter)
restore = true;
break;
+ case TREE_ATTR_BMARK:
+ bookmark_load(buf, false);
+ restore = true;
+ reload_dir = true;
+ break;
+
case TREE_ATTR_TXT:
plugin_load("/.rockbox/rocks/viewer.rock",buf);
restore = true;
@@ -1122,15 +1144,7 @@ static bool dirbrowse(char *root, int *dirfilter)
settings_save();
}
- status_set_playmode(STATUS_PLAY);
- status_draw(false);
- lcd_stop_scroll();
- if ( wps_show() == SYS_USB_CONNECTED ) {
- reload_root = true;
- }
-#ifdef HAVE_LCD_BITMAP
- tree_max_on_screen = (LCD_HEIGHT - MARGIN_Y) / fh;
-#endif
+ start_wps = true;
}
else if (*dirfilter > NUM_FILTER_MODES)
exit_func = true;
@@ -1238,13 +1252,7 @@ static bool dirbrowse(char *root, int *dirfilter)
{
if (mpeg_status() & MPEG_STATUS_PLAY)
{
- lcd_stop_scroll();
- if (wps_show() == SYS_USB_CONNECTED)
- reload_root = true;
-#ifdef HAVE_LCD_BITMAP
- tree_max_on_screen = (LCD_HEIGHT - MARGIN_Y) / fh;
-#endif
- restore = true;
+ start_wps=true;
}
else
{
@@ -1293,6 +1301,18 @@ static bool dirbrowse(char *root, int *dirfilter)
if ( button )
ata_spin();
+ if (start_wps)
+ {
+ lcd_stop_scroll();
+ if (wps_show() == SYS_USB_CONNECTED)
+ reload_root = true;
+#ifdef HAVE_LCD_BITMAP
+ tree_max_on_screen = (LCD_HEIGHT - MARGIN_Y) / fh;
+#endif
+ restore = true;
+ start_wps=false;
+ }
+
/* do we need to rescan dir? */
if (reload_dir || reload_root ||
lastfilter != *dirfilter ||
@@ -1313,6 +1333,7 @@ static bool dirbrowse(char *root, int *dirfilter)
lastfilter = *dirfilter;
lastsortcase = global_settings.sort_case;
restore = true;
+ while (button_get(false)); /* clear button queue */
}
if (exit_func)
@@ -1507,3 +1528,51 @@ void tree_init(void)
name_buffer = buffer_alloc(name_buffer_size);
dircache = buffer_alloc(max_files_in_dir * sizeof(struct entry));
}
+
+void bookmark_play(char *resume_file, int index, int offset, int seed)
+{
+ int len=strlen(resume_file);
+
+ if (!strcasecmp(&resume_file[len-4], ".m3u"))
+ {
+ char* slash;
+ // check that the file exists
+ int fd = open(resume_file, O_RDONLY);
+ if(fd<0)
+ return;
+ close(fd);
+
+ slash = strrchr(resume_file,'/');
+ if (slash)
+ {
+ char* cp;
+ *slash=0;
+
+ cp=resume_file;
+ if (!cp[0])
+ cp="/";
+
+ if (playlist_create(cp, slash+1) != -1)
+ {
+ if (global_settings.playlist_shuffle)
+ playlist_shuffle(seed, -1);
+ playlist_start(index,offset);
+ }
+ *slash='/';
+ }
+ }
+ else
+ {
+ lastdir[0]='\0';
+ if (playlist_create(resume_file, NULL) != -1)
+ {
+ resume_directory(resume_file);
+ if (global_settings.playlist_shuffle)
+ playlist_shuffle(seed, -1);
+ playlist_start(index,offset);
+ }
+ }
+
+ status_set_playmode(STATUS_PLAY);
+ start_wps=true;
+}
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 {
#define TREE_ATTR_UCL 0x0A00 /* rockbox flash image */
#define TREE_ATTR_CH8 0x0B00 /* chip-8 game */
#define TREE_ATTR_RVF 0x0C00 /* rockbox video file */
+#define TREE_ATTR_BMARK 0x0D00 /* book mark file */
#define TREE_ATTR_MASK 0xFFC0 /* which bits tree.c uses (above) */
void 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 @@
#include "peakmeter.h"
#endif
#include "lang.h"
+#include "bookmark.h"
#define FF_REWIND_MAX_PERCENT 3 /* cap ff/rewind step size at max % of file */
/* 3% of 30min file == 54s step size */
@@ -943,7 +944,7 @@ int wps_show(void)
case BUTTON_RC_STOP:
#endif
#ifdef BUTTON_OFF
- case BUTTON_OFF:
+ case BUTTON_OFF | BUTTON_REL:
#else
case BUTTON_STOP | BUTTON_REL:
if ( lastbutton != BUTTON_STOP )
@@ -985,6 +986,7 @@ int wps_show(void)
fade(0);
lcd_stop_scroll();
+ bookmark_autobookmark();
mpeg_stop();
status_set_playmode(STATUS_STOP);
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 \
APPS = main.c tree.c menu.c credits.c main_menu.c icons.c language.c \
playlist.c wps.c wps-display.c settings.c status.c \
screens.c peakmeter.c sleeptimer.c keyboard.c onplay.c\
- misc.c plugin.c playlist_viewer.c
+ misc.c plugin.c playlist_viewer.c bookmark.c
MENUS = settings_menu.c sound_menu.c playlist_menu.c
@@ -204,6 +204,9 @@ $(OBJDIR)/playlist.o: $(APPDIR)/playlist.c
$(OBJDIR)/playlist_viewer.o: $(APPDIR)/playlist_viewer.c
$(CC) $(APPCFLAGS) -c $< -o $@
+$(OBJDIR)/bookmark.o: $(APPDIR)/bookmark.c
+ $(CC) $(APPCFLAGS) -c $< -o $@
+
$(OBJDIR)/plugin.o: $(APPDIR)/plugin.c plugin-win32.h
$(CC) $(APPCFLAGS) -c $< -o $@
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\
APPS = main.c tree.c menu.c credits.c main_menu.c language.c\
playlist.c wps.c wps-display.c settings.c status.c icons.c\
screens.c peakmeter.c sleeptimer.c keyboard.c onplay.c\
- misc.c plugin.c playlist_viewer.c
+ misc.c plugin.c playlist_viewer.c bookmark.c
MENUS = settings_menu.c sound_menu.c playlist_menu.c
@@ -202,6 +202,9 @@ $(OBJDIR)/playlist.o: $(APPDIR)/playlist.c
$(OBJDIR)/playlist_viewer.o: $(APPDIR)/playlist_viewer.c
$(CC) $(APPCFLAGS) -c $< -o $@
+$(OBJDIR)/bookmark.o: $(APPDIR)/bookmark.c
+ $(CC) $(APPCFLAGS) -c $< -o $@
+
$(OBJDIR)/build.lang: $(APPDIR)/lang/$(LANGUAGE).lang
perl $(TOOLSDIR)/uplang $(APPDIR)/lang/english.lang $< > $@