static int tree_get_filecolor(int selected_item, void * data) { if (*tc.dirfilter == SHOW_ID3DB) return -1; struct tree_context * local_tc=(struct tree_context *)data; struct entry* e = tree_get_entry_at(local_tc, selected_item); return filetype_get_color(e->name, e->attr); }
static const char* tree_get_filename(int selected_item, void *data, char *buffer, size_t buffer_len) { struct tree_context * local_tc=(struct tree_context *)data; char *name; int attr=0; bool stripit = false; #ifdef HAVE_TAGCACHE bool id3db = *(local_tc->dirfilter) == SHOW_ID3DB; if (id3db) { return tagtree_get_entry_name(&tc, selected_item, buffer, buffer_len); } else #endif { struct entry* e = tree_get_entry_at(local_tc, selected_item); name = e->name; attr = e->attr; } if(!(attr & ATTR_DIRECTORY)) { switch(global_settings.show_filename_ext) { case 0: /* show file extension: off */ stripit = true; break; case 1: /* show file extension: on */ break; case 2: /* show file extension: only unknown types */ stripit = filetype_supported(attr); break; case 3: default: /* show file extension: only when viewing all */ stripit = (*(local_tc->dirfilter) != SHOW_ID3DB) && (*(local_tc->dirfilter) != SHOW_ALL); break; } } if(stripit) { return(strip_extension(buffer, buffer_len, name)); } return(name); }
/* * Returns the position of a given file in the current directory * returns -1 if not found */ static int tree_get_file_position(char * filename) { int i; struct entry* e; /* use lastfile to determine the selected item (default=0) */ for (i=0; i < tc.filesindir; i++) { e = tree_get_entry_at(&tc, i); if (!strcasecmp(e->name, filename)) return(i); } return(-1);/* no file can match, returns undefined */ }
static enum themable_icons tree_get_fileicon(int selected_item, void * data) { struct tree_context * local_tc=(struct tree_context *)data; #ifdef HAVE_TAGCACHE bool id3db = *(local_tc->dirfilter) == SHOW_ID3DB; if (id3db) { return tagtree_get_icon(&tc); } else #endif { struct entry* e = tree_get_entry_at(local_tc, selected_item); return filetype_get_icon(e->attr); } }
char* get_current_file(char* buffer, size_t buffer_len) { #ifdef HAVE_TAGCACHE /* in ID3DB mode it is a bad idea to call this function */ /* (only happens with `follow playlist') */ if( *tc.dirfilter == SHOW_ID3DB ) return NULL; #endif struct entry* e = tree_get_entry_at(&tc, tc.selected_item); if (getcwd(buffer, buffer_len)) { if (tc.dirlength) { if (buffer[strlen(buffer)-1] != '/') strlcat(buffer, "/", buffer_len); if (strlcat(buffer, e->name, buffer_len) >= buffer_len) return NULL; } return buffer; } return NULL; }
/* main loop, handles key events */ static int dirbrowse(void) { int numentries=0; char buf[MAX_PATH]; int button, oldbutton; bool reload_root = false; int lastfilter = *tc.dirfilter; bool lastsortcase = global_settings.sort_case; bool exit_func = false; 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; 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; numentries = update_dir(); reload_dir = false; if (numentries == -1) return GO_TO_PREVIOUS; /* currdir is not a directory */ if (*tc.dirfilter > NUM_FILTER_MODES && numentries==0) { splash(HZ*2, ID2P(LANG_NO_FILES)); return GO_TO_PREVIOUS; /* No files found for rockbox_browse() */ } gui_synclist_draw(&tree_lists); while(1) { bool restore = false; if (tc.dirlevel < 0) tc.dirlevel = 0; /* shouldnt be needed.. this code needs work! */ button = get_action(CONTEXT_TREE, list_do_action_timeout(&tree_lists, HZ/2)); oldbutton = button; gui_synclist_do_button(&tree_lists, &button,LIST_WRAP_UNLESS_HELD); tc.selected_item = gui_synclist_get_sel_pos(&tree_lists); switch ( button ) { case ACTION_STD_OK: /* nothing to do if no files to display */ if ( numentries == 0 ) break; short attr = tree_get_entry_at(&tc, tc.selected_item)->attr; if ((tc.browse->flags & BROWSE_SELECTONLY) && !(attr & ATTR_DIRECTORY)) { tc.browse->flags |= BROWSE_SELECTED; get_current_file(tc.browse->buf, tc.browse->bufsize); return GO_TO_PREVIOUS; } #ifdef HAVE_TAGCACHE switch (id3db?tagtree_enter(&tc):ft_enter(&tc)) #else switch (ft_enter(&tc)) #endif { case GO_TO_FILEBROWSER: reload_dir = true; break; case GO_TO_WPS: return GO_TO_WPS; #if CONFIG_TUNER case GO_TO_FM: return GO_TO_FM; #endif case GO_TO_ROOT: exit_func = true; break; default: break; } restore = true; break; case ACTION_STD_CANCEL: if (*tc.dirfilter > NUM_FILTER_MODES && tc.dirlevel < 1) { exit_func = true; break; } if ((*tc.dirfilter == SHOW_ID3DB && tc.dirlevel == 0) || ((*tc.dirfilter != SHOW_ID3DB && !strcmp(currdir,"/")))) { #ifdef HAVE_LCD_BITMAP /* charcell doesnt have ACTION_TREE_PGLEFT so this isnt needed */ if (oldbutton == ACTION_TREE_PGLEFT) break; else #endif return GO_TO_ROOT; } #ifdef HAVE_TAGCACHE if (id3db) tagtree_exit(&tc); else #endif if (ft_exit(&tc) == 3) exit_func = true; restore = true; break; case ACTION_TREE_STOP: if (list_stop_handler()) restore = true; break; case ACTION_STD_MENU: return GO_TO_ROOT; break; #ifdef HAVE_RECORDING case ACTION_STD_REC: return GO_TO_RECSCREEN; #endif case ACTION_TREE_WPS: return GO_TO_PREVIOUS_MUSIC; 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; } break; #endif #ifdef BUTTON_F3 case ACTION_F3: /* don't enter f3 from plugin browser */ if (*tc.dirfilter < NUM_FILTER_MODES) { if (quick_screen_f3(ACTION_F3)) reload_dir = true; restore = true; } break; #endif #ifdef HAVE_HOTKEY case ACTION_TREE_HOTKEY: if (!global_settings.hotkey_tree) break; /* fall through */ #endif case ACTION_STD_CONTEXT: { bool hotkey = button == ACTION_TREE_HOTKEY; int onplay_result; int attr = 0; if (tc.browse->flags & BROWSE_NO_CONTEXT_MENU) break; if(!numentries) onplay_result = onplay(NULL, 0, curr_context, hotkey); else { #ifdef HAVE_TAGCACHE if (id3db) { if (tagtree_get_attr(&tc) == FILE_ATTR_AUDIO) { attr = FILE_ATTR_AUDIO; tagtree_get_filename(&tc, buf, sizeof(buf)); } else attr = ATTR_DIRECTORY; } else #endif { struct entry *entry = tree_get_entry_at(&tc, tc.selected_item); attr = entry->attr; if (currdir[1]) /* Not in / */ snprintf(buf, sizeof buf, "%s/%s", currdir, entry->name); else /* In / */ snprintf(buf, sizeof buf, "/%s", entry->name); } onplay_result = onplay(buf, attr, curr_context, hotkey); } switch (onplay_result) { case ONPLAY_MAINMENU: return GO_TO_ROOT; case ONPLAY_OK: restore = true; break; case ONPLAY_RELOAD_DIR: reload_dir = true; break; case ONPLAY_START_PLAY: return GO_TO_WPS; break; } break; } #ifdef HAVE_HOTSWAP case SYS_FS_CHANGED: #ifdef HAVE_TAGCACHE if (!id3db) #endif 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 (start_wps) return GO_TO_WPS; if (button && !IS_SYSEVENT(button)) { storage_spin(); } 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; #ifdef HAVE_TAGCACHE tc.currtable = 0; tc.currextra = 0; lasttable = -1; lastextra = -1; #endif 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) return GO_TO_PREVIOUS; if (restore || reload_dir) { /* restore display */ numentries = update_dir(); reload_dir = false; if (currdir[1] && (numentries < 0)) { /* not in root and reload failed */ reload_root = true; /* try root */ goto check_rescan; } } } return true; }
static int tree_voice_cb(int selected_item, void * data) { struct tree_context * local_tc=(struct tree_context *)data; char *name; int attr=0; #ifdef HAVE_TAGCACHE bool id3db = *(local_tc->dirfilter) == SHOW_ID3DB; char buf[AVERAGE_FILENAME_LENGTH*2]; if (id3db) { attr = tagtree_get_attr(local_tc); name = tagtree_get_entry_name(local_tc, selected_item, buf, sizeof(buf)); } else #endif { struct entry* e = tree_get_entry_at(local_tc, selected_item); name = e->name; attr = e->attr; } bool is_dir = (attr & ATTR_DIRECTORY); bool did_clip = false; /* First the .talk clip case */ if(is_dir) { if(global_settings.talk_dir_clip) { did_clip = true; if(ft_play_dirname(name) <0) /* failed, not existing */ did_clip = false; } } else { /* it's a file */ if (global_settings.talk_file_clip && (attr & FILE_ATTR_THUMBNAIL)) { did_clip = true; ft_play_filename(local_tc->currdir, name); } } if(!did_clip) { /* say the number or spell if required or as a fallback */ switch (is_dir ? global_settings.talk_dir : global_settings.talk_file) { case 1: /* as numbers */ 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(); if(global_settings.talk_filetype) { if(is_dir) talk_id(VOICE_DIR, true); else if(*local_tc->dirfilter < NUM_FILTER_MODES) say_filetype(attr); } talk_spell(name, true); break; } } return 0; }
int ft_enter(struct tree_context* c) { int rc = GO_TO_PREVIOUS; char buf[MAX_PATH]; struct entry* file = tree_get_entry_at(c, c->selected_item); int file_attr = file->attr; if (c->currdir[1]) snprintf(buf,sizeof(buf),"%s/%s",c->currdir, file->name); else snprintf(buf,sizeof(buf),"/%s",file->name); if (file_attr & ATTR_DIRECTORY) { memcpy(c->currdir, buf, sizeof(c->currdir)); if ( c->dirlevel < MAX_DIR_LEVELS ) c->selected_item_history[c->dirlevel] = c->selected_item; c->dirlevel++; c->selected_item=0; } else { int seed = current_tick; bool play = false; int start_index=0; switch ( file_attr & FILE_ATTR_MASK ) { case FILE_ATTR_M3U: if (!bookmark_autoload(buf)) playlist_viewer_ex(buf); break; case FILE_ATTR_AUDIO: if (bookmark_autoload(c->currdir)) break; splash(0, ID2P(LANG_WAIT)); /* about to create a new current playlist... allow user to cancel the operation */ if (!warn_on_pl_erase()) break; if (global_settings.party_mode && audio_status()) { playlist_insert_track(NULL, buf, PLAYLIST_INSERT_LAST, true, true); splash(HZ, ID2P(LANG_QUEUE_LAST)); } else if (playlist_create(c->currdir, NULL) != -1) { start_index = ft_build_playlist(c, c->selected_item); if (global_settings.playlist_shuffle) { start_index = playlist_shuffle(seed, start_index); /* when shuffling dir.: play all files even if the file selected by user is not the first one */ if (!global_settings.play_selected) start_index = 0; } playlist_start(start_index, 0); play = true; } break; #if CONFIG_TUNER /* fmr preset file */ case FILE_ATTR_FMR: splash(0, ID2P(LANG_WAIT)); /* Preset inside the default folder. */ if(!strncasecmp(FMPRESET_PATH, buf, strlen(FMPRESET_PATH))) { set_file(buf, global_settings.fmr_file, MAX_FILENAME); radio_load_presets(global_settings.fmr_file); } /* * Preset outside default folder, we can choose such only * if we are out of the radio screen, so the check for the * radio status isn't neccessary */ else { radio_load_presets(buf); } rc = GO_TO_FM; break; case FILE_ATTR_FMS: splash(0, ID2P(LANG_WAIT)); set_file(buf, (char *)global_settings.fms_file, MAX_FILENAME); settings_apply_skins(); break; #ifdef HAVE_REMOTE_LCD case FILE_ATTR_RFMS: splash(0, ID2P(LANG_WAIT)); set_file(buf, (char *)global_settings.rfms_file, MAX_FILENAME); settings_apply_skins(); break; #endif #endif #ifdef HAVE_LCD_BITMAP case FILE_ATTR_SBS: splash(0, ID2P(LANG_WAIT)); set_file(buf, (char *)global_settings.sbs_file, MAX_FILENAME); settings_apply_skins(); break; #endif #ifdef HAVE_REMOTE_LCD case FILE_ATTR_RSBS: splash(0, ID2P(LANG_WAIT)); set_file(buf, (char *)global_settings.rsbs_file, MAX_FILENAME); settings_apply_skins(); break; #endif /* wps config file */ case FILE_ATTR_WPS: splash(0, ID2P(LANG_WAIT)); set_file(buf, (char *)global_settings.wps_file, MAX_FILENAME); settings_apply_skins(); break; #if defined(HAVE_REMOTE_LCD) && (NB_SCREENS > 1) /* remote-wps config file */ case FILE_ATTR_RWPS: splash(0, ID2P(LANG_WAIT)); set_file(buf, (char *)global_settings.rwps_file, MAX_FILENAME); settings_apply_skins(); break; #endif case FILE_ATTR_CFG: splash(0, ID2P(LANG_WAIT)); if (!settings_load_config(buf,true)) break; splash(HZ, ID2P(LANG_SETTINGS_LOADED)); break; case FILE_ATTR_BMARK: splash(0, ID2P(LANG_WAIT)); bookmark_load(buf, false); rc = GO_TO_FILEBROWSER; break; case FILE_ATTR_LNG: splash(0, ID2P(LANG_WAIT)); if (lang_core_load(buf)) { splash(HZ, ID2P(LANG_FAILED)); break; } set_file(buf, (char *)global_settings.lang_file, MAX_FILENAME); talk_init(); /* use voice of same language */ viewportmanager_theme_changed(THEME_LANGUAGE); settings_apply_skins(); splash(HZ, ID2P(LANG_LANGUAGE_LOADED)); break; #ifdef HAVE_LCD_BITMAP case FILE_ATTR_FONT: ft_load_font(buf); break; case FILE_ATTR_KBD: splash(0, ID2P(LANG_WAIT)); if (!load_kbd(buf)) splash(HZ, ID2P(LANG_KEYBOARD_LOADED)); set_file(buf, (char *)global_settings.kbd_file, MAX_FILENAME); break; #endif #if (CONFIG_PLATFORM & PLATFORM_NATIVE) /* firmware file */ case FILE_ATTR_MOD: splash(0, ID2P(LANG_WAIT)); audio_hard_stop(); rolo_load(buf); break; #endif /* plugin file */ case FILE_ATTR_ROCK: case FILE_ATTR_LUA: { char *plugin = buf, *argument = NULL, lua_path[MAX_PATH]; int ret; if ((file_attr & FILE_ATTR_MASK) == FILE_ATTR_LUA) { snprintf(lua_path, sizeof(lua_path)-1, "%s/lua.rock", VIEWERS_DIR); /* Use a #define here ? */ plugin = lua_path; argument = buf; } if (global_settings.party_mode && audio_status()) { splash(HZ, ID2P(LANG_PARTY_MODE)); break; } ret = plugin_load(plugin, argument); switch (ret) { case PLUGIN_GOTO_WPS: play = true; break; case PLUGIN_USB_CONNECTED: if(*c->dirfilter > NUM_FILTER_MODES) /* leave sub-browsers after usb, doing otherwise might be confusing to the user */ rc = GO_TO_ROOT; else rc = GO_TO_FILEBROWSER; break; /* case PLUGIN_ERROR: case PLUGIN_OK: */ default: break; } break; } case FILE_ATTR_CUE: display_cuesheet_content(buf); break; default: { const char* plugin; if (global_settings.party_mode && audio_status()) { splash(HZ, ID2P(LANG_PARTY_MODE)); break; } struct entry* file = tree_get_entry_at(c, c->selected_item); plugin = filetype_get_plugin(file); if (plugin) { switch (plugin_load(plugin,buf)) { case PLUGIN_USB_CONNECTED: rc = GO_TO_FILEBROWSER; break; case PLUGIN_GOTO_WPS: rc = GO_TO_WPS; break; /* case PLUGIN_OK: case PLUGIN_ERROR: */ default: break; } } break; } } if ( play ) { /* the resume_index must always be the index in the shuffled list in case shuffle is enabled */ global_status.resume_index = start_index; global_status.resume_offset = 0; status_save(); rc = GO_TO_WPS; } else { if (*c->dirfilter > NUM_FILTER_MODES && *c->dirfilter != SHOW_CFG && *c->dirfilter != SHOW_FONT && *c->dirfilter != SHOW_PLUGINS) { rc = GO_TO_ROOT; } } } return rc; }
/* load and sort directory into the tree's cache. returns NULL on failure. */ int ft_load(struct tree_context* c, const char* tempdir) { int files_in_dir = 0; int name_buffer_used = 0; struct dirent *entry; bool (*callback_show_item)(char *, int, struct tree_context *) = NULL; DIR *dir; if (tempdir) dir = opendir(tempdir); else { dir = opendir(c->currdir); callback_show_item = c->browse? c->browse->callback_show_item: NULL; } if(!dir) return -1; /* not a directory */ c->dirsindir = 0; c->dirfull = false; tree_lock_cache(c); while ((entry = readdir(dir))) { int len; struct dirinfo info; struct entry* dptr = tree_get_entry_at(c, files_in_dir); if (!entry) break; info = dir_get_info(dir, entry); len = strlen((char *)entry->d_name); /* skip directories . and .. */ if ((info.attribute & ATTR_DIRECTORY) && (((len == 1) && (!strncmp((char *)entry->d_name, ".", 1))) || ((len == 2) && (!strncmp((char *)entry->d_name, "..", 2))))) { continue; } /* Skip FAT volume ID */ if (info.attribute & ATTR_VOLUME_ID) { continue; } /* filter out dotfiles and hidden files */ if (*c->dirfilter != SHOW_ALL && ((entry->d_name[0]=='.') || (info.attribute & ATTR_HIDDEN))) { continue; } dptr->attr = info.attribute; /* check for known file types */ if ( !(dptr->attr & ATTR_DIRECTORY) ) dptr->attr |= filetype_get_attr((char *)entry->d_name); /* filter out non-visible files */ if ((!(dptr->attr & ATTR_DIRECTORY) && ( (*c->dirfilter == SHOW_PLAYLIST && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_M3U) || ((*c->dirfilter == SHOW_MUSIC && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_AUDIO) && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_M3U) || (*c->dirfilter == SHOW_SUPPORTED && !filetype_supported(dptr->attr)))) || (*c->dirfilter == SHOW_WPS && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_WPS) || #ifdef HAVE_LCD_BITMAP (*c->dirfilter == SHOW_FONT && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_FONT) || (*c->dirfilter == SHOW_SBS && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_SBS) || #if CONFIG_TUNER (*c->dirfilter == SHOW_FMS && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_FMS) || #endif #endif #ifdef HAVE_REMOTE_LCD (*c->dirfilter == SHOW_RWPS && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_RWPS) || (*c->dirfilter == SHOW_RSBS && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_RSBS) || #if CONFIG_TUNER (*c->dirfilter == SHOW_RFMS && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_RFMS) || #endif #endif #if CONFIG_TUNER (*c->dirfilter == SHOW_FMR && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_FMR) || #endif (*c->dirfilter == SHOW_M3U && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_M3U) || (*c->dirfilter == SHOW_CFG && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_CFG) || (*c->dirfilter == SHOW_LNG && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_LNG) || (*c->dirfilter == SHOW_MOD && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_MOD) || (*c->dirfilter == SHOW_PLUGINS && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_ROCK && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_LUA) || (callback_show_item && !callback_show_item(entry->d_name, dptr->attr, c))) { continue; } if ((len > c->cache.name_buffer_size - name_buffer_used - 1) || (files_in_dir >= c->cache.max_entries)) { /* Tell the world that we ran out of buffer space */ c->dirfull = true; break; } ++files_in_dir; dptr->name = core_get_data(c->cache.name_buffer_handle)+name_buffer_used; dptr->time_write = (long)info.wrtdate<<16 | (long)info.wrttime; /* in one # */ strcpy(dptr->name, (char *)entry->d_name); name_buffer_used += len + 1; if (dptr->attr & ATTR_DIRECTORY) /* count the remaining dirs */ c->dirsindir++; } c->filesindir = files_in_dir; c->dirlength = files_in_dir; closedir(dir); compare_sort_dir = c->sort_dir; qsort(tree_get_entries(c), files_in_dir, sizeof(struct entry), compare); /* If thumbnail talking is enabled, make an extra run to mark files with associated thumbnails, so we don't do unsuccessful spinups later. */ if (global_settings.talk_file_clip) check_file_thumbnails(c); /* map .talk to ours */ tree_unlock_cache(c); return 0; }