diff options
Diffstat (limited to 'apps/tree.c')
-rw-r--r-- | apps/tree.c | 423 |
1 files changed, 229 insertions, 194 deletions
diff --git a/apps/tree.c b/apps/tree.c index 63363422ba..ea2ef23e71 100644 --- a/apps/tree.c +++ b/apps/tree.c @@ -72,14 +72,12 @@ #include "list.h" #include "splash.h" #include "quickscreen.h" +#include "shortcuts.h" #include "appevents.h" #include "root_menu.h" -static const struct filetype *filetypes; -static int filetypes_count; - -struct gui_synclist tree_lists; +static struct gui_synclist tree_lists; /* I put it here because other files doesn't use it yet, * but should be elsewhere since it will be used mostly everywhere */ @@ -88,7 +86,7 @@ static struct tree_context tc; char lastfile[MAX_PATH]; static char lastdir[MAX_PATH]; #ifdef HAVE_TAGCACHE -static int lasttable, lastextra, lastfirstpos; +static int lasttable, lastextra; #endif static bool reload_dir = false; @@ -247,6 +245,7 @@ static int tree_voice_cb(int selected_item, void * data) did_clip = false; } } + bool spell_name = false; if(!did_clip) { /* say the number or spell if required or as a fallback */ @@ -256,9 +255,6 @@ static int tree_voice_cb(int selected_item, void * data) talk_id(is_dir ? VOICE_DIR : VOICE_FILE, false); talk_number(selected_item+1 - (is_dir ? 0 : local_tc->dirsindir), true); - if(global_settings.talk_filetype - && !is_dir && *local_tc->dirfilter < NUM_FILTER_MODES) - say_filetype(attr); break; case 2: /* spelled */ talk_shutup(); @@ -266,13 +262,22 @@ static int tree_voice_cb(int selected_item, void * data) { if(is_dir) talk_id(VOICE_DIR, true); - else if(*local_tc->dirfilter < NUM_FILTER_MODES) - say_filetype(attr); } - talk_spell(name, true); + spell_name = true; break; } } + + if(global_settings.talk_filetype && !is_dir + && *local_tc->dirfilter < NUM_FILTER_MODES) + { + say_filetype(attr); + } + + /* spell name AFTER voicing filetype */ + if (spell_name) + talk_spell(name, true); + return 0; } @@ -293,19 +298,10 @@ bool check_rockboxdir(void) } /* do this really late in the init sequence */ -void tree_gui_init(void) +void tree_init(void) { check_rockboxdir(); - strcpy(tc.currdir, "/"); - - gui_synclist_init(&tree_lists, &tree_get_filename, &tc, false, 1, NULL); - gui_synclist_set_voice_callback(&tree_lists, tree_voice_cb); - gui_synclist_set_icon_callback(&tree_lists, - global_settings.show_icons?&tree_get_fileicon:NULL); -#ifdef HAVE_LCD_COLOR - gui_synclist_set_color_callback(&tree_lists, &tree_get_filecolor); -#endif } @@ -314,6 +310,18 @@ struct tree_context* tree_get_context(void) return &tc; } +void tree_lock_cache(struct tree_context *t) +{ + core_pin(t->cache.name_buffer_handle); + core_pin(t->cache.entries_handle); +} + +void tree_unlock_cache(struct tree_context *t) +{ + core_unpin(t->cache.name_buffer_handle); + core_unpin(t->cache.entries_handle); +} + /* * Returns the position of a given file in the current directory * returns -1 if not found @@ -344,14 +352,24 @@ static int tree_get_file_position(char * filename) */ static int update_dir(void) { + struct gui_synclist * const list = &tree_lists; + int show_path_in_browser = global_settings.show_path_in_browser; bool changed = false; + + const char* title = NULL;/* Must clear the title as the list is reused */ + int icon = NOICON; + #ifdef HAVE_TAGCACHE bool id3db = *tc.dirfilter == SHOW_ID3DB; +#else + const bool id3db = false; +#endif + +#ifdef HAVE_TAGCACHE /* Checks for changes */ if (id3db) { if (tc.currtable != lasttable || tc.currextra != lastextra || - tc.firstpos != lastfirstpos || reload_dir) { if (tagtree_load(&tc) < 0) @@ -359,7 +377,6 @@ static int update_dir(void) lasttable = tc.currtable; lastextra = tc.currextra; - lastfirstpos = tc.firstpos; changed = true; } } @@ -372,15 +389,16 @@ static int update_dir(void) { if (ft_load(&tc, NULL) < 0) return -1; - strlcpy(lastdir, tc.currdir, MAX_PATH); + strmemccpy(lastdir, tc.currdir, MAX_PATH); changed = true; } } /* if selected item is undefined */ if (tc.selected_item == -1) { - /* use lastfile to determine the selected item */ - tc.selected_item = tree_get_file_position(lastfile); + if (!id3db) + /* use lastfile to determine the selected item */ + tc.selected_item = tree_get_file_position(lastfile); /* If the file doesn't exists, select the first one (default) */ if(tc.selected_item < 0) @@ -389,28 +407,22 @@ static int update_dir(void) } if (changed) { - if( -#ifdef HAVE_TAGCACHE - !id3db && -#endif - tc.dirfull ) + if( !id3db && tc.dirfull ) { splash(HZ, ID2P(LANG_SHOWDIR_BUFFER_FULL)); } } + + gui_synclist_init(list, &tree_get_filename, &tc, false, 1, NULL); + #ifdef HAVE_TAGCACHE if (id3db) { - if (global_settings.show_path_in_browser == SHOW_PATH_FULL - || global_settings.show_path_in_browser == SHOW_PATH_CURRENT) - { - gui_synclist_set_title(&tree_lists, tagtree_get_title(&tc), - filetype_get_icon(ATTR_DIRECTORY)); - } - else + if (show_path_in_browser == SHOW_PATH_FULL + || show_path_in_browser == SHOW_PATH_CURRENT) { - /* Must clear the title as the list is reused */ - gui_synclist_set_title(&tree_lists, NULL, NOICON); + title = tagtree_get_title(&tc); + icon = filetype_get_icon(ATTR_DIRECTORY); } } else @@ -418,45 +430,52 @@ static int update_dir(void) { if (tc.browse && tc.browse->title) { - int icon = tc.browse->icon; + title = tc.browse->title; + icon = tc.browse->icon; if (icon == NOICON) icon = filetype_get_icon(ATTR_DIRECTORY); - gui_synclist_set_title(&tree_lists, tc.browse->title, icon); - } - else if (global_settings.show_path_in_browser == SHOW_PATH_FULL) - { - gui_synclist_set_title(&tree_lists, tc.currdir, - filetype_get_icon(ATTR_DIRECTORY)); } - else if (global_settings.show_path_in_browser == SHOW_PATH_CURRENT) + else { - char *title = strrchr(tc.currdir, '/') + 1; - if (*title == '\0') + if (show_path_in_browser == SHOW_PATH_FULL) { - /* Display "Files" for the root dir */ - gui_synclist_set_title(&tree_lists, str(LANG_DIR_BROWSER), - filetype_get_icon(ATTR_DIRECTORY)); + title = tc.currdir; + icon = filetype_get_icon(ATTR_DIRECTORY); } - else - gui_synclist_set_title(&tree_lists, title, - filetype_get_icon(ATTR_DIRECTORY)); - } - else - { - /* Must clear the title as the list is reused */ - gui_synclist_set_title(&tree_lists, NULL, NOICON); + else if (show_path_in_browser == SHOW_PATH_CURRENT) + { + title = strrchr(tc.currdir, '/'); + if (title != NULL) + { + title++; /* step past the separator */ + if (*title == '\0') + { + /* Display "Files" for the root dir */ + title = str(LANG_DIR_BROWSER); + } + icon = filetype_get_icon(ATTR_DIRECTORY); + } + } } } - gui_synclist_set_nb_items(&tree_lists, tc.filesindir); - gui_synclist_set_icon_callback(&tree_lists, - global_settings.show_icons?tree_get_fileicon:NULL); + /* set title and icon, if nothing is set, clear the title + * with NULL and icon as NOICON as the list is reused */ + gui_synclist_set_title(list, title, icon); + + gui_synclist_set_nb_items(list, tc.filesindir); + gui_synclist_set_icon_callback(list, + global_settings.show_icons?tree_get_fileicon:NULL); + gui_synclist_set_voice_callback(list, &tree_voice_cb); +#ifdef HAVE_LCD_COLOR + gui_synclist_set_color_callback(list, &tree_get_filecolor); +#endif if( tc.selected_item >= tc.filesindir) tc.selected_item=tc.filesindir-1; - gui_synclist_select_item(&tree_lists, tc.selected_item); - gui_synclist_draw(&tree_lists); - gui_synclist_speak_item(&tree_lists); + gui_synclist_select_item(list, tc.selected_item); + gui_synclist_draw(list); + gui_synclist_speak_item(list); return tc.filesindir; } @@ -467,13 +486,13 @@ void resume_directory(const char *dir) int ret; #ifdef HAVE_TAGCACHE bool id3db = *tc.dirfilter == SHOW_ID3DB; +#else + const bool id3db = false; #endif /* make sure the dirfilter is sane. The only time it should be possible * thats its not is when resume playlist is called from a plugin */ -#ifdef HAVE_TAGCACHE if (!id3db) -#endif *tc.dirfilter = global_settings.dirfilter; ret = ft_load(&tc, dir); *tc.dirfilter = dirfilter; @@ -497,10 +516,10 @@ char *getcwd(char *buf, getcwd_size_t size) return tc.currdir; else if (size) { - if ((getcwd_size_t)strlcpy(buf, tc.currdir, size) < size) + if (strmemccpy(buf, tc.currdir, size) != NULL) return buf; } - /* size == 0, or truncation in strlcpy */ + /* size == 0, or truncation in strmemccpy */ return NULL; } @@ -541,10 +560,9 @@ void set_dirfilter(int l_dirfilter) *tc.dirfilter = l_dirfilter; } -/* Selects a file and update tree context properly */ -void set_current_file(const char *path) +/* Selects a path + file and update tree context properly */ +static void set_current_file_ex(const char *path, const char *filename) { - const char *name; int i; #ifdef HAVE_TAGCACHE @@ -554,21 +572,37 @@ void set_current_file(const char *path) return; #endif - /* separate directory from filename */ - /* gets the directory's name and put it into tc.currdir */ - name = strrchr(path+1,'/'); - if (name) + if (!filename) /* path and filename supplied combined */ { - strlcpy(tc.currdir, path, name - path + 1); - name++; + /* separate directory from filename */ + /* gets the directory's name and put it into tc.currdir */ + filename = strrchr(path+1,'/'); + size_t endpos = filename - path; + if (endpos < MAX_PATH - 1) + { + strmemccpy(tc.currdir, path, endpos + 1); + filename++; + } + else + { + strcpy(tc.currdir, "/"); + filename = path+1; + } } - else + else /* path and filename came in separate ensure an ending '/' */ { - strcpy(tc.currdir, "/"); - name = path+1; + char *end_p = strmemccpy(tc.currdir, path, MAX_PATH); + size_t endpos = end_p - tc.currdir; + if (endpos < MAX_PATH) + { + if (tc.currdir[endpos - 2] != '/') + { + tc.currdir[endpos - 1] = '/'; + tc.currdir[endpos] = '\0'; + } + } } - - strlcpy(lastfile, name, MAX_PATH); + strmemccpy(lastfile, filename, MAX_PATH); /* If we changed dir we must recalculate the dirlevel @@ -591,16 +625,27 @@ void set_current_file(const char *path) if (ft_load(&tc, NULL) >= 0) { tc.selected_item = tree_get_file_position(lastfile); + if (!tc.is_browsing && tc.out_of_tree == 0) + { + /* the browser is closed */ + /* don't allow the previous items to overwrite what we just loaded */ + tc.out_of_tree = tc.selected_item + 1; + } } } +/* Selects a file and update tree context properly */ +void set_current_file(const char *path) +{ + set_current_file_ex(path, NULL); +} + /* main loop, handles key events */ static int dirbrowse(void) { int numentries=0; char buf[MAX_PATH]; - int len; int button; int oldbutton; bool reload_root = false; @@ -620,10 +665,8 @@ static int dirbrowse(void) if (tc.selected_item < 0) tc.selected_item = 0; #ifdef HAVE_TAGCACHE - tc.firstpos = 0; lasttable = -1; lastextra = -1; - lastfirstpos = 0; #endif start_wps = false; @@ -638,9 +681,7 @@ static int dirbrowse(void) return GO_TO_PREVIOUS; /* No files found for rockbox_browse() */ } - send_event(GUI_EVENT_ACTIONUPDATE, (void*)1); /* force a redraw */ - gui_synclist_draw(&tree_lists); - while(1) { + while(tc.browse && tc.is_browsing) { bool restore = false; if (tc.dirlevel < 0) tc.dirlevel = 0; /* shouldnt be needed.. this code needs work! */ @@ -649,7 +690,7 @@ static int dirbrowse(void) button = get_action(CONTEXT_TREE|ALLOW_SOFTLOCK, list_do_action_timeout(&tree_lists, HZ/2)); oldbutton = button; - gui_synclist_do_button(&tree_lists, &button,LIST_WRAP_UNLESS_HELD); + gui_synclist_do_button(&tree_lists, &button); tc.selected_item = gui_synclist_get_sel_pos(&tree_lists); switch ( button ) { case ACTION_STD_OK: @@ -671,7 +712,7 @@ static int dirbrowse(void) } } #ifdef HAVE_TAGCACHE - switch (id3db?tagtree_enter(&tc):ft_enter(&tc)) + switch (id3db ? tagtree_enter(&tc, true) : ft_enter(&tc)) #else switch (ft_enter(&tc)) #endif @@ -686,7 +727,8 @@ static int dirbrowse(void) return GO_TO_FM; #endif case GO_TO_ROOT: exit_func = true; break; - default: break; + default: + break; } restore = true; break; @@ -707,7 +749,7 @@ static int dirbrowse(void) #ifdef HAVE_TAGCACHE if (id3db) - tagtree_exit(&tc); + tagtree_exit(&tc, true); else #endif if (ft_exit(&tc) == 3) @@ -735,19 +777,40 @@ static int dirbrowse(void) break; #ifdef HAVE_QUICKSCREEN case ACTION_STD_QUICKSCREEN: - if (global_settings.shortcuts_replaces_qs) - { - if (*tc.dirfilter < NUM_FILTER_MODES) - { - global_status.last_screen = GO_TO_SHORTCUTMENU; - return quick_screen_quick(button); - } + { + bool enter_shortcuts_menu = global_settings.shortcuts_replaces_qs; + if (enter_shortcuts_menu && *tc.dirfilter >= NUM_FILTER_MODES) break; + else if (!enter_shortcuts_menu) + { + int ret = quick_screen_quick(button); + if (ret == QUICKSCREEN_IN_USB) + reload_dir = true; + else if (ret == QUICKSCREEN_GOTO_SHORTCUTS_MENU) + enter_shortcuts_menu = true; } - else if (quick_screen_quick(button)) - reload_dir = true; + + if (enter_shortcuts_menu && *tc.dirfilter < NUM_FILTER_MODES) + { + int last_screen = global_status.last_screen; + global_status.last_screen = GO_TO_SHORTCUTMENU; + int shortcut_ret = do_shortcut_menu(NULL); + if (shortcut_ret == GO_TO_PREVIOUS) + global_status.last_screen = last_screen; + else + return shortcut_ret; + } + else if (enter_shortcuts_menu) /* currently disabled */ + { + /* QuickScreen defers skin updates, popping its activity, when + switching to Shortcuts Menu, so make up for that here: */ + FOR_NB_SCREENS(i) + skin_update(CUSTOM_STATUSBAR, i, SKIN_REFRESH_ALL); + } + restore = true; break; + } #endif #ifdef HAVE_HOTKEY @@ -777,7 +840,12 @@ static int dirbrowse(void) tagtree_get_filename(&tc, buf, sizeof(buf)); } else + { attr = ATTR_DIRECTORY; + tagtree_get_entry_name(&tc, tc.selected_item, + buf, sizeof(buf)); + fix_path_part(buf, 0, sizeof(buf)); + } } else #endif @@ -788,16 +856,8 @@ static int dirbrowse(void) attr = entry->attr; - if (currdir[1]) /* Not in / */ - { - len = snprintf(buf, sizeof buf, "%s/%s", - currdir, entry->name); + ft_assemble_path(buf, sizeof(buf), currdir, entry->name); - if ((unsigned) len > sizeof(buf)) - splash(HZ, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR)); - } - else /* In / */ - snprintf(buf, sizeof buf, "/%s", entry->name); } onplay_result = onplay(buf, attr, curr_context, hotkey); } @@ -922,35 +982,19 @@ int create_playlist(void) #endif trigger_cpu_boost(); - ret = catalog_add_to_a_playlist(tc.currdir, ATTR_DIRECTORY, true, NULL); + ret = catalog_add_to_a_playlist(tc.currdir, ATTR_DIRECTORY, true, NULL, NULL); cancel_cpu_boost(); return (ret) ? 1 : 0; } -void browse_context_init(struct browse_context *browse, - int dirfilter, unsigned flags, - char *title, enum themable_icons icon, - const char *root, const char *selected) -{ - browse->dirfilter = dirfilter; - browse->flags = flags; - browse->callback_show_item = NULL; - browse->title = title; - browse->icon = icon; - browse->root = root; - browse->selected = selected; - browse->buf = NULL; - browse->bufsize = 0; -} - #define NUM_TC_BACKUP 3 static struct tree_context backups[NUM_TC_BACKUP]; /* do not make backup if it is not recursive call */ static int backup_count = -1; int rockbox_browse(struct browse_context *browse) { - static char current[MAX_PATH]; + tc.is_browsing = (browse != NULL); int ret_val = 0; int dirfilter = browse->dirfilter; @@ -964,59 +1008,68 @@ int rockbox_browse(struct browse_context *browse) tc.sort_dir = global_settings.sort_dir; reload_dir = true; - if (*tc.dirfilter >= NUM_FILTER_MODES) + + if (tc.out_of_tree > 0) + { + /* an item has already been loaded out_of_tree holds the selected index + * what happens with the item is dependent on the browse context */ + tc.selected_item = tc.out_of_tree - 1; + tc.out_of_tree = 0; + ret_val = ft_enter(&tc); + } + else { - int last_context; - /* don't reset if its the same browse already loaded */ - if (tc.browse != browse || - !(tc.currdir[1] && strcmp(tc.currdir, browse->root) == 0)) + if (*tc.dirfilter >= NUM_FILTER_MODES) { - tc.browse = browse; - tc.selected_item = 0; - tc.dirlevel = 0; + int last_context; + /* don't reset if its the same browse already loaded */ + if (tc.browse != browse || + !(tc.currdir[1] && strcmp(tc.currdir, browse->root) == 0)) + { + tc.browse = browse; + tc.selected_item = 0; + tc.dirlevel = 0; - strlcpy(tc.currdir, browse->root, sizeof(tc.currdir)); - } + strmemccpy(tc.currdir, browse->root, sizeof(tc.currdir)); + } - start_wps = false; - last_context = curr_context; + start_wps = false; + last_context = curr_context; - if (browse->selected) - { - snprintf(current, sizeof(current), "%s/%s", - browse->root, browse->selected); - set_current_file(current); - /* set_current_file changes dirlevel, change it back */ - tc.dirlevel = 0; - } + if (browse->selected) + { + set_current_file_ex(browse->root, browse->selected); + /* set_current_file changes dirlevel, change it back */ + tc.dirlevel = 0; + } - ret_val = dirbrowse(); - curr_context = last_context; - } - else - { - if (dirfilter != SHOW_ID3DB) - tc.dirfilter = &global_settings.dirfilter; - tc.browse = browse; - strlcpy(current, browse->root, MAX_PATH); - set_current_file(current); - if (browse->flags&BROWSE_RUNFILE) - ret_val = ft_enter(&tc); - else ret_val = dirbrowse(); + curr_context = last_context; + } + else + { + if (dirfilter != SHOW_ID3DB && (browse->flags & BROWSE_DIRFILTER) == 0) + tc.dirfilter = &global_settings.dirfilter; + tc.browse = browse; + set_current_file(browse->root); + if (browse->flags&BROWSE_RUNFILE) + ret_val = ft_enter(&tc); + else + ret_val = dirbrowse(); + } } backup_count--; if (backup_count >= 0) tc = backups[backup_count]; + + tc.is_browsing = false; + return ret_val; } static int move_callback(int handle, void* current, void* new) { struct tree_cache* cache = &tc.cache; - if (cache->lock_count > 0) - return BUFLIB_CB_CANNOT_MOVE; - ptrdiff_t diff = new - current; /* FIX_PTR makes sure to not accidentally update static allocations */ #define FIX_PTR(x) \ @@ -1048,15 +1101,11 @@ void tree_mem_init(void) cache->name_buffer_size = AVERAGE_FILENAME_LENGTH * global_settings.max_files_in_dir; - cache->name_buffer_handle = core_alloc_ex("tree names", - cache->name_buffer_size, - &ops); + cache->name_buffer_handle = core_alloc_ex(cache->name_buffer_size, &ops); cache->max_entries = global_settings.max_files_in_dir; - cache->entries_handle = core_alloc_ex("tree entries", - cache->max_entries*(sizeof(struct entry)), - &ops); - tree_get_filetypes(&filetypes, &filetypes_count); + cache->entries_handle = + core_alloc_ex(cache->max_entries*(sizeof(struct entry)), &ops); } bool bookmark_play(char *resume_file, int index, unsigned long elapsed, @@ -1089,6 +1138,7 @@ bool bookmark_play(char *resume_file, int index, unsigned long elapsed, { if (global_settings.playlist_shuffle) playlist_shuffle(seed, -1); + playlist_start(index, elapsed, offset); started = true; } @@ -1140,6 +1190,7 @@ bool bookmark_play(char *resume_file, int index, unsigned long elapsed, else return false; } + playlist_start(index, elapsed, offset); started = true; } @@ -1150,25 +1201,9 @@ bool bookmark_play(char *resume_file, int index, unsigned long elapsed, return started; } -static long filetype_voiceclip(int attr) -{ - int j; - if (global_settings.talk_filetype) - { - /* try to find a voice ID for the extension, if known */ - attr &= FILE_ATTR_MASK; /* file type */ - for (j=0; j<filetypes_count; j++) - if (attr == filetypes[j].tree_attr) - { - return filetypes[j].voiceclip; - } - } - return -1; -} - static void say_filetype(int attr) { - talk_id(filetype_voiceclip(attr), true); + talk_id(tree_get_filetype_voiceclip(attr), true); } static int ft_play_dirname(char* name) @@ -1186,7 +1221,7 @@ static int ft_play_filename(char *dir, char *file, int attr) file_thumbnail_ext)) /* file has no .talk extension */ return talk_file(dir, NULL, file, file_thumbnail_ext, - TALK_IDARRAY(filetype_voiceclip(attr)), false); + TALK_IDARRAY(tree_get_filetype_voiceclip(attr)), false); /* it already is a .talk file, play this directly, but prefix it. */ return talk_file(dir, NULL, file, NULL, @@ -1196,7 +1231,7 @@ static int ft_play_filename(char *dir, char *file, int attr) /* These two functions are called by the USB and shutdown handlers */ void tree_flush(void) { - tc.browse = NULL; /* clear browse to prevent reentry to a possibly missing file */ + tc.is_browsing = false;/* clear browse to prevent reentry to a possibly missing file */ #ifdef HAVE_TAGCACHE tagcache_shutdown(); #endif @@ -1245,7 +1280,7 @@ void tree_restore(void) #endif #ifdef HAVE_TC_RAMCACHE - remove(TAGCACHE_STATEFILE); + tagcache_remove_statefile(); #endif #ifdef HAVE_DIRCACHE |