From a108ec2ebd237835a688ae5c82c90e07607219ae Mon Sep 17 00:00:00 2001 From: Björn Stenberg Date: Wed, 14 Jan 2004 00:13:04 +0000 Subject: Added Benjamin Metzlers bookmarking feature (patch #669440) git-svn-id: svn://svn.rockbox.org/rockbox/trunk@4227 a1c6a512-1295-4272-9138-f99709370657 --- apps/bookmark.c | 1105 ++++++++++++++++++++++++++++++++++++++++++++++++ apps/bookmark.h | 33 ++ apps/lang/english.lang | 122 ++++++ apps/main_menu.c | 6 +- apps/playlist.c | 61 ++- apps/playlist.h | 16 +- apps/playlist_viewer.c | 4 +- apps/recorder/icons.c | 1 + apps/recorder/icons.h | 1 + apps/settings.c | 48 ++- apps/settings.h | 16 +- apps/settings_menu.c | 52 +++ apps/tree.c | 107 ++++- apps/tree.h | 1 + apps/wps.c | 4 +- 15 files changed, 1537 insertions(+), 40 deletions(-) create mode 100644 apps/bookmark.c create mode 100644 apps/bookmark.h (limited to 'apps') 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 +#include +#include +#include + +#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 + +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 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 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); -- cgit v1.2.3