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)
{