Index: apps/lang/english.lang =================================================================== --- apps/lang/english.lang (revision 31463) +++ apps/lang/english.lang (working copy) @@ -12993,3 +12993,59 @@ *: "Restart Sleep Timer On Keypress" + + id: LANG_HIDE + desc: in playlist viewer on+play menu + user: core + + *: "Hide" + + + *: "Hide" + + + *: "Hide" + + + + id: LANG_UNHIDE + desc: in playlist viewer on+play menu + user: core + + *: "Unhide" + + + *: "Unhide" + + + *: "Unhide" + + + + id: LANG_HIDING + desc: in playlist viewer on+play menu + user: core + + *: "Hiding..." + + + *: "Hiding..." + + + *: "Hiding..." + + + + id: LANG_UNHIDING + desc: in playlist viewer on+play menu + user: core + + *: "Unhiding..." + + + *: "Unhiding..." + + + *: "Unhiding..." + + Index: apps/plugins/properties.c =================================================================== --- apps/plugins/properties.c (revision 31463) +++ apps/plugins/properties.c (working copy) @@ -23,6 +23,8 @@ bool its_a_dir = false; +int attr = 0; +char str_attr[32]; char str_filename[MAX_PATH]; char str_dirname[MAX_PATH]; @@ -258,18 +260,21 @@ static const char * get_props(int select rb->strlcpy(buffer, its_a_dir ? str_size : str_date, buffer_len); break; case 4: - rb->strlcpy(buffer, its_a_dir ? "" : str_time, buffer_len); + rb->strlcpy(buffer, its_a_dir ? str_attr : str_time, buffer_len); break; case 5: - rb->strlcpy(buffer, its_a_dir ? "" : str_artist, buffer_len); + rb->strlcpy(buffer, str_attr, buffer_len); break; case 6: - rb->strlcpy(buffer, its_a_dir ? "" : str_title, buffer_len); + rb->strlcpy(buffer, its_a_dir ? "" : str_artist, buffer_len); break; case 7: - rb->strlcpy(buffer, its_a_dir ? "" : str_album, buffer_len); + rb->strlcpy(buffer, its_a_dir ? "" : str_title, buffer_len); break; case 8: + rb->strlcpy(buffer, its_a_dir ? "" : str_album, buffer_len); + break; + case 9: rb->strlcpy(buffer, its_a_dir ? "" : str_duration, buffer_len); break; default: @@ -307,6 +312,7 @@ enum plugin_status plugin_start(const vo { struct dirinfo info = rb->dir_get_info(dir, entry); its_a_dir = info.attribute & ATTR_DIRECTORY ? true : false; + attr = info.attribute; found = true; break; } @@ -336,13 +342,15 @@ enum plugin_status plugin_start(const vo FOR_NB_SCREENS(i) rb->viewportmanager_theme_enable(i, true, NULL); #endif + rb->snprintf(str_attr, sizeof str_attr, "Attr: [%s]Read-only [%s]Hidden", + (attr&ATTR_READ_ONLY)?"+":" ", (attr&ATTR_HIDDEN)?"+":" "); rb->gui_synclist_init(&properties_lists, &get_props, file, false, 1, NULL); rb->gui_synclist_set_title(&properties_lists, its_a_dir ? "Directory properties" : "File properties", NOICON); rb->gui_synclist_set_icon_callback(&properties_lists, NULL); - rb->gui_synclist_set_nb_items(&properties_lists, num_properties); + rb->gui_synclist_set_nb_items(&properties_lists, num_properties+1); rb->gui_synclist_limit_scroll(&properties_lists, true); rb->gui_synclist_select_item(&properties_lists, 0); rb->gui_synclist_draw(&properties_lists); Index: apps/onplay.c =================================================================== --- apps/onplay.c (revision 31463) +++ apps/onplay.c (working copy) @@ -484,8 +484,12 @@ static void draw_slider(void) #define draw_slider() #endif -/* helper function to remove a non-empty directory */ -static int remove_dir(char* dirname, int len) +struct hide_arg { + bool hide; +}; + +/* helper function to remove / hide a non-empty directory */ +static int remove_hide_dir(char* dirname, int len, const struct hide_arg *const ha) { int result = 0; DIR* dir; @@ -510,19 +514,22 @@ static int remove_dir(char* dirname, int /* append name to current directory */ snprintf(dirname+dirlen, len-dirlen, "/%s", entry->d_name); if (info.attribute & ATTR_DIRECTORY) - { /* remove a subdirectory */ + { /* remove / hide a subdirectory */ if (!strcmp((char *)entry->d_name, ".") || !strcmp((char *)entry->d_name, "..")) continue; /* skip these */ - result = remove_dir(dirname, len); /* recursion */ + result = remove_hide_dir(dirname, len, ha); /* recursion */ if (result) break; /* or better continue, delete what we can? */ } else - { /* remove a file */ + { /* remove / hide a file */ draw_slider(); - result = remove(dirname); + if (!ha) + result = remove(dirname); + else + result = hide(dirname, ha->hide); } if(ACTION_STD_CANCEL == get_action(CONTEXT_STD,TIMEOUT_NOBLOCK)) { @@ -534,10 +541,13 @@ static int remove_dir(char* dirname, int closedir(dir); if (!result) - { /* remove the now empty directory */ + { dirname[dirlen] = '\0'; /* terminate to original length */ - result = rmdir(dirname); + if (!ha) /* remove the now empty directory */ + result = rmdir(dirname); + else /* hide target directory */ + result = hidedir(dirname, ha->hide); } return result; @@ -573,7 +583,7 @@ static bool delete_file_dir(void) char pathname[MAX_PATH]; /* space to go deep */ cpu_boost(true); strlcpy(pathname, file_to_delete, sizeof(pathname)); - res = remove_dir(pathname, sizeof(pathname)); + res = remove_hide_dir(pathname, sizeof(pathname), NULL); cpu_boost(false); } else @@ -604,6 +614,49 @@ static bool rename_file(void) return false; } +static bool hide_file_dir(void *param) +{ + int ret; + int lang_id; + bool recurse = false; + const struct hide_arg ha = { .hide = (bool)(intptr_t)param }; + + if (ha.hide) + lang_id = LANG_HIDING; + else + lang_id = LANG_UNHIDING; + + if (selected_file_attr & ATTR_DIRECTORY) { + const char *line[] = { + ID2P(LANG_RECURSE_DIRECTORY_QUESTION), + selected_file + }; + const struct text_message message = {line, 2}; + if (gui_syncyesno_run(&message, NULL, NULL) == YESNO_YES) + recurse = true; + } + + splash(0, str(lang_id)); + + if (recurse) { + char pathname[MAX_PATH]; /* space to go deep */ + cpu_boost(true); + strlcpy(pathname, selected_file, sizeof(pathname)); + ret = remove_hide_dir(pathname, sizeof(pathname), &ha); + cpu_boost(false); + } else { + if (selected_file_attr & ATTR_DIRECTORY) + ret = hidedir(selected_file, ha.hide); + else + ret = hide(selected_file, ha.hide); + } + + if (!ret) + onplay_result = ONPLAY_RELOAD_DIR; + + return (ret == 0); +} + static bool create_dir(void) { char dirname[MAX_PATH]; @@ -771,7 +824,7 @@ static bool clipboard_pastedirectory(cha /* If it worked, remove the source directory */ if (result) { - remove_dir(src, srclen); + remove_hide_dir(src, srclen, NULL); } } #endif @@ -891,7 +944,7 @@ static bool clipboard_paste(void) if (success && !clipboard_is_copy) { strlcpy(srcpath, clipboard_selection, sizeof(srcpath)); - remove_dir(srcpath, sizeof(srcpath)); + remove_hide_dir(srcpath, sizeof(srcpath), NULL); } } } else { @@ -1013,6 +1066,10 @@ MENUITEM_FUNCTION(delete_dir_item, 0, ID delete_file_dir, NULL, clipboard_callback, Icon_NOICON); MENUITEM_FUNCTION(create_dir_item, 0, ID2P(LANG_CREATE_DIR), create_dir, NULL, clipboard_callback, Icon_NOICON); +MENUITEM_FUNCTION(hide_file_item, MENU_FUNC_USEPARAM, ID2P(LANG_HIDE), + hide_file_dir, (void *)true, clipboard_callback, Icon_NOICON); +MENUITEM_FUNCTION(unhide_file_item, MENU_FUNC_USEPARAM, ID2P(LANG_UNHIDE), + hide_file_dir, (void *)false, clipboard_callback, Icon_NOICON); /* other items */ static bool list_viewers(void) @@ -1089,7 +1146,9 @@ static int clipboard_callback(int action if ((selected_file_attr & FAT_ATTR_VOLUME) && (this_item == &rename_file_item || this_item == &delete_dir_item || - this_item == &clipboard_cut_item) ) + this_item == &clipboard_cut_item || + this_item == &hide_file_item || + this_item == &unhide_file_item) ) return ACTION_EXIT_MENUITEM; /* no rename+delete for volumes */ if ((selected_file_attr & ATTR_VOLUME) && @@ -1119,6 +1178,22 @@ static int clipboard_callback(int action } else if (selected_file) { + /* only for hidden / unhidden */ + if (!(selected_file_attr & ATTR_HIDDEN)) + { + if (this_item == &hide_file_item) + { + return action; + } + } + else + { + if (this_item == &unhide_file_item) + { + return action; + } + } + /* requires an actual file */ if (this_item == &rename_file_item || this_item == &clipboard_cut_item || @@ -1189,7 +1264,8 @@ MAKE_ONPLAYMENU( wps_onplay_menu, ID2P(L MAKE_ONPLAYMENU( tree_onplay_menu, ID2P(LANG_ONPLAY_MENU_TITLE), onplaymenu_callback, Icon_file_view_menu, &tree_playlist_menu, &cat_playlist_menu, - &rename_file_item, &clipboard_cut_item, &clipboard_copy_item, + &rename_file_item, &hide_file_item, &unhide_file_item, + &clipboard_cut_item, &clipboard_copy_item, &clipboard_paste_item, &delete_file_item, &delete_dir_item, #if LCD_DEPTH > 1 &set_backdrop_item, Index: firmware/export/fat.h =================================================================== --- firmware/export/fat.h (revision 31463) +++ firmware/export/fat.h (working copy) @@ -127,6 +127,7 @@ extern int fat_rename(struct fat_file* f struct fat_dir* dir, const unsigned char* newname, long size, int attr); +extern int fat_attr(struct fat_file* file, long size, int attr); extern int fat_opendir(IF_MV2(int volume,) struct fat_dir *ent, unsigned long startcluster, Index: firmware/test/fat/main.c =================================================================== --- firmware/test/fat/main.c (revision 31463) +++ firmware/test/fat/main.c (working copy) @@ -550,6 +550,8 @@ int dbg_cmd(int argc, char *argv[]) " append \n" " test \n" " ren \n" + " hide \n" + " unhide \n" ); return -1; } @@ -667,6 +669,18 @@ int dbg_cmd(int argc, char *argv[]) return rename(arg1, arg2); } + if (!strcasecmp(cmd, "hide")) + { + if (arg1) + return hide(arg1, true); + } + + if (!strcasecmp(cmd, "unhide")) + { + if (arg1) + return hide(arg1, false); + } + return 0; } Index: firmware/include/dircache.h =================================================================== --- firmware/include/dircache.h (revision 31463) +++ firmware/include/dircache.h (working copy) @@ -91,6 +91,7 @@ void dircache_update_filetime(int fd); void dircache_mkdir(const char *path); void dircache_rmdir(const char *path); void dircache_remove(const char *name); +void dircache_hide(const char *name, bool hide); void dircache_rename(const char *oldpath, const char *newpath); void dircache_add_file(const char *path, long startcluster); Index: firmware/include/file.h =================================================================== --- firmware/include/file.h (revision 31463) +++ firmware/include/file.h (working copy) @@ -30,6 +30,7 @@ /* this has SEEK_SET et al */ #include #endif +#include #undef MAX_PATH /* this avoids problems when building simulator */ @@ -42,6 +43,8 @@ # define creat(x,m) app_creat(x, m) # define remove(x) app_remove(x) # define rename(x,y) app_rename(x,y) +# define hide(x,y) app_hide(x,y) +# define hidedir(x,y) app_hide(x,y) extern int app_open(const char *name, int o, ...); extern int app_creat(const char *name, mode_t mode); extern int app_remove(const char* pathname); @@ -60,6 +63,8 @@ extern int app_rename(const char* path, # define creat(x,m) sim_creat(x,m) # define remove(x) sim_remove(x) # define rename(x,y) sim_rename(x,y) +# define hide(x,y) sim_hide(x,y) +# define hidedir(x,y) sim_hide(x,y) # define filesize(x) sim_filesize(x) # define fsync(x) sim_fsync(x) # define ftruncate(x,y) sim_ftruncate(x,y) @@ -101,6 +106,8 @@ extern int rename(const char* path, cons extern int ftruncate(int fd, off_t length); extern off_t filesize(int fd); extern int release_files(int volume); +extern int hide(const char* pathname, bool hide); +extern int hidedir(const char* pathname, bool hide); int fdprintf (int fd, const char *fmt, ...) ATTRIBUTE_PRINTF(2, 3); #endif /* !CODEC && !PLUGIN */ #endif Index: firmware/common/dircache.c =================================================================== --- firmware/common/dircache.c (revision 31463) +++ firmware/common/dircache.c (working copy) @@ -1441,6 +1441,37 @@ void dircache_remove(const char *name) entry->d_name = NULL; } +/* Hide / Unhide a file from cache */ +void dircache_hide(const char *name, bool hide) +{ /* Test ok. */ + struct dircache_entry *entry; + int attr; + + if (block_until_ready()) + return ; + + logf("%shide: %s", hide?"":"un", name); + + entry = dircache_get_entry(name, false); + + if (entry == NULL) + { + logf("not found!"); + dircache_initialized = false; + return ; + } + + attr = entry->info.attribute; + + if (( hide && !(attr & ATTR_HIDDEN)) || + (!hide && (attr & ATTR_HIDDEN))) { + if (hide) + entry->info.attribute = attr | ATTR_HIDDEN; + else + entry->info.attribute = attr & ~ATTR_HIDDEN; + } +} + void dircache_rename(const char *oldpath, const char *newpath) { /* Test ok. */ struct dircache_entry *entry, *newentry; Index: firmware/common/rbpaths.c =================================================================== --- firmware/common/rbpaths.c (revision 31463) +++ firmware/common/rbpaths.c (working copy) @@ -208,6 +208,12 @@ int app_remove(const char *name) return remove(fname); } +int app_hide(const char *name, bool hide) +{ + /* XXX */ + return 0; +} + int app_rename(const char *old, const char *new) { char realpath_old[MAX_PATH], realpath_new[MAX_PATH]; Index: firmware/common/file.c =================================================================== --- firmware/common/file.c (revision 31463) +++ firmware/common/file.c (working copy) @@ -834,3 +834,132 @@ int release_files(int volume) } return closed; /* return how many we did */ } + +int hidedir(const char* pathname, bool do_hide) +{ + DIR_UNCACHED *dir; + struct dirent_uncached* entry; + int attr; +#ifdef HAVE_DIRCACHE + bool update = false; +#endif + int pathnamesize = strlen(pathname) + 1; + char pathnamecopy[pathnamesize]; + char *name; + struct fat_file file; + + LDEBUGF("hidedir:%s, %d\n", pathname, do_hide); + + if (pathname[0] != '/') { + return -5; + } + + strlcpy(pathnamecopy, pathname, pathnamesize); + + /* locate filename */ + name = strrchr(pathnamecopy+1,'/'); + if (name) { + *name = 0; + dir = opendir_uncached(pathnamecopy); + *name = '/'; + name++; + } + else { + dir = opendir_uncached("/"); + name = pathnamecopy+1; + } + if (!dir) { + return -6; + } + + if(name[0] == 0) { + DEBUGF("Empty file name\n"); + closedir_uncached(dir); + return -7; + } + + /* scan dir for name */ + while ((entry = readdir_uncached(dir))) { + if ( !strcasecmp(name, entry->d_name) ) { + fat_open(IF_MV2(dir->fatdir.file.volume,) + entry->startcluster, + &file, + &(dir->fatdir)); + attr = entry->info.attribute; + break; + } + } + if (!entry) { + LDEBUGF("Didn't find file %s\n",name); + closedir_uncached(dir); + return -8; + } + + LDEBUGF("%s, attr=%x\n", entry->d_name, attr); + + if (( do_hide && !(attr & FAT_ATTR_HIDDEN)) || + (!do_hide && (attr & FAT_ATTR_HIDDEN))) { + if (do_hide) + attr |= FAT_ATTR_HIDDEN; + else + attr &= ~FAT_ATTR_HIDDEN; + LDEBUGF("hide_dir: attr=%x\n", attr); +#ifdef HAVE_DIRCACHE + update = true; +#endif + fat_attr(&file, entry->info.size, attr); + } + + closedir_uncached(dir); + +#ifdef HAVE_DIRCACHE + if (update) + dircache_hide(pathname, do_hide); +#endif + + return 0; +} + +/* hide / unhide selected file / directory */ +int hide(const char* name, bool do_hide) +{ + int rc; + struct filedesc* file; +#ifdef HAVE_DIRCACHE + bool update = false; +#endif + /* Can't use dircache now, because we need to access the fat structures. */ + int fd = open_internal(name, O_WRONLY, false); + if ( fd < 0 ) { + if (errno == EISDIR) { + return hidedir(name, do_hide); + } else { + return fd * 10 - 1; + } + } + + file = &openfiles[fd]; + LDEBUGF("hide: file->attr=%x\n", file->attr); + if (( do_hide && !(file->attr & FAT_ATTR_HIDDEN)) || + (!do_hide && (file->attr & FAT_ATTR_HIDDEN))) { + if (do_hide) + file->attr |= FAT_ATTR_HIDDEN; + else + file->attr &= ~FAT_ATTR_HIDDEN; + LDEBUGF("hide: file->attr=%x\n", file->attr); +#ifdef HAVE_DIRCACHE + update = true; +#endif + } + + rc = close(fd); + if (rc<0) + return rc * 10 - 2; + +#ifdef HAVE_DIRCACHE + if (update) + dircache_hide(name, do_hide); +#endif + + return 0; +} Index: firmware/drivers/fat.c =================================================================== --- firmware/drivers/fat.c (revision 31463) +++ firmware/drivers/fat.c (working copy) @@ -1630,8 +1630,8 @@ static int update_short_entry( struct fa struct fat_file dir; int rc; - LDEBUGF("update_file_size(cluster:%lx entry:%d size:%ld)\n", - file->firstcluster, file->direntry, size); + LDEBUGF("update_file_size(cluster:%lx entry:%d size:%ld attr:%x)\n", + file->firstcluster, file->direntry, size, attr); /* create a temporary file handle for the dir holding this file */ rc = fat_open(IF_MV2(file->volume,) file->dircluster, &dir, NULL); @@ -2662,3 +2663,19 @@ bool fat_ismounted(int volume) return (volumevolume]; +#endif + int rc = update_short_entry(file, size, attr); + if (rc < 0) + return rc * 10 - 1; + + rc = flush_fat(IF_MV(fat_bpb)); + if (rc < 0) + return rc * 10 - 2; + + return 0; +} Index: uisimulator/common/io.c =================================================================== --- uisimulator/common/io.c (revision 31463) +++ uisimulator/common/io.c (working copy) @@ -152,6 +152,7 @@ int dircache_get_entry_id(const char *fi void dircache_add_file(const char *name, long startcluster); void dircache_remove(const char *name); void dircache_rename(const char *oldname, const char *newname); +void dircache_hide(const char *name, bool hide); #endif @@ -511,6 +512,15 @@ int sim_rename(const char *oldname, cons return RENAME(sim_old, sim_new); } +int sim_hide(const char *name, bool hide) +{ + /* XXX */ +#ifdef HAVE_DIRCACHE + dircache_hide(name, hide); +#endif + return 0; +} + /* rockbox off_t may be different from system off_t */ long sim_lseek(int fildes, long offset, int whence) {