static bool dirbrowse(void) { int numentries=0; char buf[MAX_PATH]; int lasti = -1; unsigned button, returned_button; bool reload_root = false; int lastfilter = *tc.dirfilter; bool lastsortcase = global_settings.sort_case; bool need_update = true; bool exit_func = false; long thumbnail_time = -1; /* for delaying a thumbnail */ char* currdir = tc.currdir; /* just a shortcut */ #ifdef HAVE_TAGCACHE bool id3db = *tc.dirfilter == SHOW_ID3DB; if (id3db) curr_context=CONTEXT_ID3DB; else #endif curr_context=CONTEXT_TREE; tc.selected_item = 0; tc.dirlevel=0; tc.firstpos=0; lasttable = -1; lastextra = -1; lastfirstpos = 0; if (*tc.dirfilter < NUM_FILTER_MODES) { #ifdef HAVE_RECORDING #ifndef SIMULATOR if (global_settings.rec_startup) { /* We fake being in the menu structure by calling the appropriate parent when we drop out of each screen */ #if CONFIG_CODEC == SWCODEC /* Put in a 1 sec pause to slow bootup or the recording codecs won't initialize */ sleep(HZ); #endif recording_screen(false); rec_menu(); main_menu(); } else #endif #endif start_resume(true); } /* If we don't need to show the wps, draw the dir */ if (!start_wps) { numentries = update_dir(); if (numentries == -1) return false; /* currdir is not a directory */ if (*tc.dirfilter > NUM_FILTER_MODES && numentries==0) { gui_syncsplash(HZ*2, true, str(LANG_NO_FILES)); return false; /* No files found for rockbox_browser() */ } } while(1) { struct entry *dircache = tc.dircache; bool restore = false; #ifdef BOOTFILE if (boot_changed) { char *lines[]={str(LANG_BOOT_CHANGED), str(LANG_REBOOT_NOW)}; struct text_message message={lines, 2}; if(gui_syncyesno_run(&message, NULL, NULL)==YESNO_YES) rolo_load("/" BOOTFILE); restore = true; boot_changed = false; } #endif button = get_action(CONTEXT_TREE,HZ/5); returned_button = gui_synclist_do_button(&tree_lists, button); if (returned_button) need_update = true; if (returned_button == ACTION_STD_CANCEL) button = ACTION_STD_CANCEL; switch ( button ) { case ACTION_STD_OK: /* nothing to do if no files to display */ if ( numentries == 0 ) break; #ifdef HAVE_TAGCACHE switch (id3db?tagtree_enter(&tc):ft_enter(&tc)) { case 1: reload_dir = true; break; case 2: start_wps = true; break; case 3: exit_func = true; break; default: break; } restore = true; break; #endif case ACTION_STD_CANCEL: if (*tc.dirfilter > NUM_FILTER_MODES && tc.dirlevel < 1) { exit_func = true; break; } /* if we are in /, nothing to do */ if (tc.dirlevel == 0 && !strcmp(currdir,"/")) break; #ifdef HAVE_TAGCACHE if (id3db) tagtree_exit(&tc); else if (ft_exit(&tc) == 3) exit_func = true; restore = true; break; #endif case ACTION_TREE_STOP: if (*tc.dirfilter < NUM_FILTER_MODES) { /* Stop the music if it is playing */ if(audio_status()) { if (!global_settings.party_mode) { if (global_settings.fade_on_stop) fade(0); bookmark_autobookmark(); audio_stop(); } } #if defined(CONFIG_CHARGING) && \ (CONFIG_KEYPAD == RECORDER_PAD) && !defined(HAVE_SW_POWEROFF) else { if (!charger_inserted()) { if(shutdown_screen()) reload_dir = true; } else { charging_splash(); } restore = true; } #endif } #if defined(CONFIG_CHARGING) && !defined(HAVE_POWEROFF_WHILE_CHARGING) { static int last_off = 0; if (current_tick - last_off < 50) { if (charger_inserted()) { charging_splash(); restore = true; } } last_off = current_tick; } #endif break; /* case ACTION_TREE_STOP: */ case ACTION_STD_MENU: /* don't enter menu from plugin browser */ if (*tc.dirfilter < NUM_FILTER_MODES) { int i; FOR_NB_SCREENS(i) screens[i].stop_scroll(); action_signalscreenchange(); if (main_menu()) reload_dir = true; restore = true; #ifdef HAVE_TAGCACHE id3db = check_changed_id3mode(id3db); if(id3db) reload_dir = true; } else /* use it as a quick exit instead */ exit_func = true; break; #endif case ACTION_TREE_WPS: /* don't enter wps from plugin browser etc */ if (*tc.dirfilter < NUM_FILTER_MODES) { if (audio_status() & AUDIO_STATUS_PLAY) { start_wps=true; } else { start_resume(false); restore = true; } } break; #ifdef HAVE_QUICKSCREEN case ACTION_STD_QUICKSCREEN: /* don't enter f2 from plugin browser */ if (*tc.dirfilter < NUM_FILTER_MODES) { if (quick_screen_quick(button)) reload_dir = true; restore = true; #ifdef HAVE_TAGCACHE id3db = check_changed_id3mode(id3db); reload_dir = true; } break; #endif #endif #ifdef BUTTON_F3 case ACTION_F3: /* don't enter f3 from plugin browser */ if (*tc.dirfilter < NUM_FILTER_MODES) { if (quick_screen_f3(BUTTON_F3)) reload_dir = true; restore = true; } break; #endif case ACTION_STD_CONTEXT: { int onplay_result; int attr = 0; if(!numentries) onplay_result = onplay(NULL, 0, curr_context); else { #ifdef HAVE_TAGCACHE if (id3db) { if (tagtree_get_attr(&tc) == TREE_ATTR_MPA) { attr = TREE_ATTR_MPA; tagtree_get_filename(&tc, buf, sizeof(buf)); } else attr = ATTR_DIRECTORY; } else #endif { attr = dircache[tc.selected_item].attr; if (currdir[1]) /* Not in / */ snprintf(buf, sizeof buf, "%s/%s", currdir, dircache[tc.selected_item].name); else /* In / */ snprintf(buf, sizeof buf, "/%s", dircache[tc.selected_item].name); } onplay_result = onplay(buf, attr, curr_context); } switch (onplay_result) { case ONPLAY_OK: restore = true; break; case ONPLAY_RELOAD_DIR: reload_dir = true; break; case ONPLAY_START_PLAY: start_wps = true; break; } break; } case ACTION_NONE: if (thumbnail_time != -1 && TIME_AFTER(current_tick, thumbnail_time)) { /* a delayed hovering thumbnail is due now */ int res; int attr; char* name; #ifdef HAVE_TAGCACHE if (id3db) { attr = tagtree_get_attr(&tc); name = tagtree_get_entry(&tc, lasti)->name; } else #endif { attr = dircache[lasti].attr; name = dircache[lasti].name; } if (attr & ATTR_DIRECTORY) { DEBUGF("Playing directory thumbnail: %s", currdir); res = ft_play_dirname(name); if (res < 0) /* failed, not existing */ { /* say the number instead, as a fallback */ talk_id(VOICE_DIR, false); talk_number(lasti+1, true); } } else { DEBUGF("Playing file thumbnail: %s/%s%s\n", currdir, name, file_thumbnail_ext); /* no fallback necessary, we knew in advance that the file exists */ ft_play_filename(currdir, name); } thumbnail_time = -1; /* job done */ } gui_syncstatusbar_draw(&statusbars, false); break; #ifdef HAVE_HOTSWAP case SYS_FS_CHANGED: if (!id3db) reload_dir = true; /* The 'dir no longer valid' situation will be caught later * by checking the showdir() result. */ break; #endif default: if (default_event_handler(button) == SYS_USB_CONNECTED) { if(*tc.dirfilter > NUM_FILTER_MODES) /* leave sub-browsers after usb, doing otherwise might be confusing to the user */ exit_func = true; else reload_dir = true; } break; } if ( button ) { ata_spin(); } if (start_wps && audio_status() ) { int i; FOR_NB_SCREENS(i) screens[i].stop_scroll(); if (gui_wps_show() == SYS_USB_CONNECTED) reload_dir = true; #ifdef HAVE_HOTSWAP else if (!id3db) /* Try reload to catch 'no longer valid' case. */ reload_dir = true; #endif #ifdef HAVE_LCD_COLOR show_main_backdrop(); #endif #ifdef HAVE_TAGCACHE id3db = check_changed_id3mode(id3db); #endif restore = true; start_wps=false; } check_rescan: /* do we need to rescan dir? */ if (reload_dir || reload_root || lastfilter != *tc.dirfilter || lastsortcase != global_settings.sort_case) { if ( reload_root ) { strcpy(currdir, "/"); tc.dirlevel = 0; tc.currtable = 0; tc.currextra = 0; lasttable = -1; lastextra = -1; reload_root = false; } if (! reload_dir ) { gui_synclist_select_item(&tree_lists, 0); gui_synclist_draw(&tree_lists); tc.selected_item = 0; lastdir[0] = 0; } lastfilter = *tc.dirfilter; lastsortcase = global_settings.sort_case; restore = true; } if (exit_func) break; if (restore || reload_dir) { /* restore display */ numentries = update_dir(); if (currdir[1] && (numentries < 0)) { /* not in root and reload failed */ reload_root = true; /* try root */ reload_dir = false; goto check_rescan; } need_update = true; reload_dir = false; } if(need_update) { tc.selected_item = gui_synclist_get_sel_pos(&tree_lists); need_update=false; if ( numentries > 0 ) { /* Voice the file if changed */ if(lasti != tc.selected_item || restore) { int attr; char* name; lasti = tc.selected_item; thumbnail_time = -1; /* Cancel whatever we were about to say */ #ifdef HAVE_TAGCACHE if (id3db) { attr = tagtree_get_attr(&tc); name = tagtree_get_entry(&tc, tc.selected_item)->name; } else #endif { attr = dircache[tc.selected_item].attr; name = dircache[tc.selected_item].name; } /* Directory? */ if (attr & ATTR_DIRECTORY) { /* play directory thumbnail */ switch (global_settings.talk_dir) { case 1: /* dirs as numbers */ talk_id(VOICE_DIR, false); talk_number(tc.selected_item+1, true); break; case 2: /* dirs spelled */ talk_spell(name, false); break; case 3: /* thumbnail clip */ /* "schedule" a thumbnail, to have a little delay */ thumbnail_time = current_tick + HOVER_DELAY; break; default: break; } } else /* file */ { switch (global_settings.talk_file) { case 1: /* files as numbers */ ft_play_filenumber( tc.selected_item-tc.dirsindir+1, attr & TREE_ATTR_MASK); break; case 2: /* files spelled */ talk_spell(name, false); break; case 3: /* thumbnail clip */ /* "schedule" a thumbnail, to have a little delay */ if (attr & TREE_ATTR_THUMBNAIL) thumbnail_time = current_tick + HOVER_DELAY; else /* spell the number as fallback */ talk_spell(name, false); break; default: break; } } } } } } action_signalscreenchange(); return true; } static int plsize = 0; static long pltick; static bool add_dir(char* dirname, int len, int fd) { bool abort = false; DIRCACHED* dir; /* check for user abort */ if (action_userabort(TIMEOUT_NOBLOCK)) return true; dir = opendir_cached(dirname); if(!dir) return true; while (true) { struct dircache_entry *entry; entry = readdir_cached(dir); if (!entry) break; if (entry->attribute & ATTR_DIRECTORY) { int dirlen = strlen(dirname); bool result; if (!strcmp((char *)entry->d_name, ".") || !strcmp((char *)entry->d_name, "..")) continue; if (dirname[1]) snprintf(dirname+dirlen, len-dirlen, "/%s", entry->d_name); else snprintf(dirname, len, "/%s", entry->d_name); result = add_dir(dirname, len, fd); dirname[dirlen] = '\0'; if (result) { abort = true; break; } } else { int x = strlen((char *)entry->d_name); unsigned int i; char *cp = strrchr((char *)entry->d_name,'.'); if (cp) { cp++; /* add all supported audio files to playlists */ for (i=0; i < sizeof(filetypes)/sizeof(struct filetype); i++) { if (filetypes[i].tree_attr == TREE_ATTR_MPA) { if (!strcasecmp(cp, filetypes[i].extension)) { char buf[8]; int i; write(fd, dirname, strlen(dirname)); write(fd, "/", 1); write(fd, entry->d_name, x); write(fd, "\n", 1); plsize++; if(TIME_AFTER(current_tick, pltick+HZ/4)) { pltick = current_tick; snprintf(buf, sizeof buf, "%d", plsize); #ifdef HAVE_LCD_BITMAP FOR_NB_SCREENS(i) { screens[i].puts(0, 4, (unsigned char *)buf); gui_textarea_update(&screens[i]); } #else x = 10; if (plsize > 999) x=7; else { if (plsize > 99) x=8; else { if (plsize > 9) x=9; } } FOR_NB_SCREENS(i) { screens[i].puts(x,0,buf); } #endif } break; } } } } } } closedir_cached(dir); return abort; }