Hello,
I think is becoming even more useful to have a file browser to interact with file oncamera.
With this you can also delete *.DNG and raw recording, renaming it and directly and more.
Why not trying to port the file browser of CHKD? Even if as a module?
It can be a good start point.
+1, who's going to do it?
Is it outside the scope for a beginner btw? I understand the modules are written in picoc?
I've been wanting to dive into ML programming. Got some experience with C but I'm better with python/java.
Any help is welcome..
You can try and then submit a pull request for review/approve :)
@minimimi did it:
https://bitbucket.org/minimimi/magic-lantern-filer
I've polished it a little and it's now in the main repo:
https://bitbucket.org/hudson/magic-lantern/src/tip/modules/file_man/
You need to compile the file_man module (and, of course, enable module support).
Text file viewing and file deletion (with confirmation) seems to work.
Great work guys. Gonna compile it :)
Currently the implementation of the file browser is not ideal, 'cause user could delete system files!! It's not better "chroot" it to 100EOS5D folder?
Lol, now I can view log files in camera.
Quote'cause user could delete system files!!
So don't.... maybe should make system files red.
ok, so I won't compile it yet then ;D
Quote from: 1% on May 28, 2013, 02:40:45 PM
Lol, now I can view log files in camera.
So don't.... maybe should make system files red.
To be clear, minimi did a great job, but ...
why we should prevent user to format the system files, however you can delete them in the file browser afterward?
you still could read your log files in the chrooted folder, if we change the folder structure.
uhm. simply check for autoexec.bin and display an additional confirmation message.
seriously, people who simply delete stuff without thinking, should not install ML.
the same can happen with their windows explorer anyway.
i still hope that people use their brain before deleting.
I hope, I'm wrong. ;D
After, 5D3 dot, dot, dot :o ... we will see
All you are the best...
Quote from: a.d. on May 28, 2013, 03:26:52 PM
I hope, I'm wrong. ;D
After, 5D3 dot, dot, dot :o ... we will see
Understanding that deleting the autoexec.bin file, that is needed for ML to run, might cause a problem, isn't exactly rocket science.
Software such as ML shouldn't be designed with the lowest percentile in mind IMO.
It's already done , but I would like to collect testers for implove this module.
I just added copy and move codes for A1ex refucterd code.
If you have compile environment, please test it with Non-important SD card for Testing
diff -r de0bcff3619c modules/file_man/file_man.c
--- a/modules/file_man/file_man.c Sat Jun 01 02:08:29 2013 +0300
+++ b/modules/file_man/file_man.c Sat Jun 01 16:57:08 2013 +0900
@@ -8,6 +8,8 @@
#define MAX_PATH_LEN 0x80
static char gPath[MAX_PATH_LEN];
+static char gSrcFile[MAX_PATH_LEN];
+static unsigned int op_mode;
static int cf_present;
static int sd_present;
@@ -26,6 +28,13 @@
#define TYPE_FILE 1
#define TYPE_ACTION 2
+enum _FILER_OP {
+ FILE_OP_NONE,
+ FILE_OP_COPY,
+ FILE_OP_MOVE,
+ FILE_OP_PREVIEW
+};
+
static struct file_entry * file_entries = 0;
/* view file mode */
@@ -144,6 +153,26 @@
return;
}
+ if(op_mode != FILE_OP_NONE)
+ {
+ console_printf("ScanDir\n");
+ char srcpath[MAX_PATH_LEN];
+ strcpy(srcpath,gSrcFile);
+ char *p = srcpath+strlen(srcpath);
+ while (p > srcpath && *p != '/') p--;
+ *(p+1) = 0;
+
+ console_printf("src: %s\n",srcpath);
+ console_printf("dst: %s\n",path);
+
+
+ if(strcmp(path,srcpath) != 0){
+ add_file_entry("***Select Here***", TYPE_DIR, 0);
+ add_file_entry("*** Cancel OP ***", TYPE_DIR, 0);
+ }
+ }
+
+
add_file_entry("../", TYPE_DIR, 0);
struct fio_file file;
@@ -230,6 +259,100 @@
}
}
+static int
+ML_FIO_CopyFile(char *src,char *dst){
+ const int bufsize = 128*1024;
+ void* buf = alloc_dma_memory(bufsize);
+ if (!buf) return 1;
+
+ FILE* f = FIO_Open(src, O_RDONLY | O_SYNC);
+ if (f == INVALID_PTR) return 1;
+
+ FILE* g = FIO_CreateFile(dst);
+ if (g == INVALID_PTR) { FIO_CloseFile(f); return 1; }
+
+ int r = 0;
+ while ((r = FIO_ReadFile(f, buf, bufsize)))
+ FIO_WriteFile(g, buf, r);
+
+ FIO_CloseFile(f);
+ FIO_CloseFile(g);
+ msleep(1000); // this decreases the chances of getting corrupted files (fig ure out why!)
+ free_dma_memory(buf);
+ return 0;
+}
+
+static int
+ML_FIO_MoveFile(char *src,char *dst){
+
+ ML_FIO_CopyFile(src,dst);
+ FIO_RemoveFile(src);
+ return 0;
+}
+
+static void
+FileCopy(void *unused)
+{
+ char fname[MAX_PATH_LEN];
+ size_t totallen = strlen(gSrcFile);
+ char *p = gSrcFile + totallen;
+ while (p > gSrcFile && *p != '/') p--;
+ strcpy(fname,p+1);
+
+ char dstfile[MAX_PATH_LEN];
+ snprintf(dstfile,MAX_PATH_LEN,"%s%s",gPath,fname);
+
+ console_printf("Copy\n");
+ console_printf("src: %s\n",gSrcFile);
+ console_printf("dst: %s\n",dstfile);
+ ML_FIO_CopyFile(gSrcFile,dstfile);
+
+}
+
+static void
+FileMove(void *unused)
+{
+ char fname[MAX_PATH_LEN];
+ size_t totallen = strlen(gSrcFile);
+ char *p = gSrcFile + totallen;
+ while (p > gSrcFile && *p != '/') p--;
+ strcpy(fname,p+1);
+
+ char dstfile[MAX_PATH_LEN];
+ snprintf(dstfile,MAX_PATH_LEN,"%s%s",gPath,fname);
+
+ console_printf("Move\n");
+ console_printf("src: %s\n",gSrcFile);
+ console_printf("dst: %s\n",dstfile);
+ ML_FIO_MoveFile(gSrcFile,dstfile);
+
+}
+
+
+static void FileOperation(){
+
+ switch(op_mode){
+ case FILE_OP_COPY:
+ task_create("FileCopy_task", 0x1b, 0x4000, FileCopy, 0);
+ break;
+ case FILE_OP_MOVE:
+ task_create("FileMove_task", 0x1b, 0x4000, FileMove, 0);
+ break;
+ case FILE_OP_PREVIEW:
+ break;
+ }
+ //cleanup
+ op_mode = FILE_OP_NONE;
+
+ ScanDir(gPath);
+}
+
+static void FileOpCancel(){
+ gSrcFile[0] = 0;
+ op_mode = FILE_OP_NONE;
+ ScanDir(gPath);
+}
+
static MENU_SELECT_FUNC(BrowseUpMenu)
{
BrowseUp();
@@ -239,18 +362,18 @@
{
struct file_entry * fe = (struct file_entry *) priv;
char* name = (char*) fe->name;
- if ((name[0] == '.' &&
- name[1] == '.' &&
- name[2] == '/')
- || (delta < 0)
- )
- {
- BrowseUp();
- }
+ if(!strcmp(name,"***Select Here***")){
+ FileOperation();
+ }else if(!strcmp(name,"*** Cancel OP ***")){
+ FileOpCancel();
+ }else if (!strcmp(name,"../") || (delta < 0))
+ {
+ BrowseUp();
+ }
else
- {
- BrowseDown(name);
- }
+ {
+ BrowseDown(name);
+ }
}
static MENU_UPDATE_FUNC(update_dir)
@@ -288,6 +411,30 @@
return str;
}
+static MENU_SELECT_FUNC(CopyFile)
+{
+ strcpy(gSrcFile,gPath);
+ console_printf("Copysrc: %s\n",gSrcFile);
+ op_mode = FILE_OP_COPY;
+}
+
+static MENU_UPDATE_FUNC(CopyFileProgress)
+{
+
+}
+
+static MENU_SELECT_FUNC(MoveFile)
+{
+ strcpy(gSrcFile,gPath);
+ console_printf("Movesrc: %s\n",gSrcFile);
+ op_mode = FILE_OP_MOVE;
+}
+
+static MENU_UPDATE_FUNC(MoveFileProgress)
+{
+
+}
+
static MENU_SELECT_FUNC(viewfile_toggle)
{
view_file = !view_file;
@@ -375,6 +522,14 @@
e->menu_entry.select = BrowseUpMenu;
e->menu_entry.select_Q = BrowseUpMenu;
e->menu_entry.priv = e;
+
+ e = add_file_entry("Copy", TYPE_ACTION, 0);
+ e->menu_entry.select = CopyFile;
+ e->menu_entry.update = CopyFileProgress;
+
+ e = add_file_entry("Move", TYPE_ACTION, 0);
+ e->menu_entry.select = MoveFile;
+ e->menu_entry.update = MoveFileProgress;
e = add_file_entry("View", TYPE_ACTION, 0);
e->menu_entry.select = viewfile_toggle;
@@ -438,6 +593,7 @@
unsigned int fileman_init()
{
menu_add("Debug", fileman_menu, COUNT(fileman_menu));
+ op_mode = FILE_OP_NONE;
InitRootDir();
return 0;
}
just wanted to thank for your work, this file manager works really cool.
file browser is a very useful feature. Thank you all for your work on this.
Can you post a fileman.c from the repo with patch applied already? Easier to copy paste the whole thing back and forth.
1% I see.
And now , Copy and Move menu entry has some strange numbers shown on same line. I guess it's referenced from info->xx but I can't solve it now.
#define CONFIG_CONSOLE
#include <module.h>
#include <dryos.h>
#include <property.h>
#include <bmp.h>
#include <menu.h>
#define MAX_PATH_LEN 0x80
static char gPath[MAX_PATH_LEN];
static char gSrcFile[MAX_PATH_LEN];
static unsigned int op_mode;
static int cf_present;
static int sd_present;
struct file_entry
{
struct file_entry * next;
struct menu_entry menu_entry;
char name[MAX_PATH_LEN];
unsigned int size;
unsigned int type: 2;
unsigned int added: 1;
};
#define TYPE_DIR 0
#define TYPE_FILE 1
#define TYPE_ACTION 2
enum _FILER_OP {
FILE_OP_NONE,
FILE_OP_COPY,
FILE_OP_MOVE,
FILE_OP_PREVIEW
};
static struct file_entry * file_entries = 0;
/* view file mode */
static int view_file = 0;
static MENU_SELECT_FUNC(select_dir);
static MENU_UPDATE_FUNC(update_dir);
static MENU_SELECT_FUNC(select_file);
static MENU_UPDATE_FUNC(update_file);
static MENU_SELECT_FUNC(default_select_action);
static MENU_UPDATE_FUNC(update_action);
static MENU_SELECT_FUNC(BrowseUpMenu);
static struct menu_entry fileman_menu[] =
{
{
.name = "File Browser",
.select = menu_open_submenu,
.submenu_width = 710,
.children = (struct menu_entry[]) {
MENU_EOL,
}
}
};
static void clear_file_menu()
{
while (file_entries)
{
struct file_entry * next = file_entries->next;
menu_remove("File Browser", &(file_entries->menu_entry), 1);
console_printf("%s\n", file_entries->name);
FreeMemory(file_entries);
file_entries = next;
}
}
static struct file_entry * add_file_entry(char* txt, int type, int size)
{
struct file_entry * fe = AllocateMemory(sizeof(struct file_entry));
if (!fe) return 0;
memset(fe, 0, sizeof(struct file_entry));
snprintf(fe->name, sizeof(fe->name), "%s", txt);
fe->size = size;
fe->menu_entry.name = fe->name;
fe->menu_entry.priv = fe;
fe->type = type;
fe->menu_entry.select_Q = BrowseUpMenu;
if (fe->type == TYPE_DIR)
{
fe->menu_entry.select = select_dir;
fe->menu_entry.update = update_dir;
}
else if (fe->type == TYPE_FILE)
{
fe->menu_entry.select = select_file;
fe->menu_entry.update = update_file;
}
else if (fe->type == TYPE_ACTION)
{
fe->menu_entry.select = default_select_action;
fe->menu_entry.update = update_action;
fe->menu_entry.icon_type = IT_ACTION;
}
fe->next = file_entries;
file_entries = fe;
return fe;
}
static void build_file_menu()
{
/* HaCKeD Sort */
int done = 0;
while (!done)
{
done = 1;
for (struct file_entry * fe = file_entries; fe; fe = fe->next)
{
if (!fe->added)
{
/* are there any entries that should be before "fe" ? */
/* if yes, skip "fe", add those entries, and try again */
int should_skip = 0;
for (struct file_entry * e = file_entries; e; e = e->next)
{
if (!e->added && e != fe)
{
if (e->type < fe->type) { should_skip = 1; break; }
if ((e->type == fe->type) && strcmp(e->name, fe->name) < 0) { should_skip = 1; break; }
}
}
if (!should_skip)
{
menu_add("File Browser", &(fe->menu_entry), 1);
fe->added = 1;
}
else done = 0;
}
}
}
}
static void ScanDir(char *path)
{
clear_file_menu();
if (strlen(path) == 0)
{
add_file_entry("A:/", TYPE_DIR, 0);
add_file_entry("B:/", TYPE_DIR, 0);
build_file_menu();
return;
}
if(op_mode != FILE_OP_NONE)
{
console_printf("ScanDir\n");
char srcpath[MAX_PATH_LEN];
strcpy(srcpath,gSrcFile);
char *p = srcpath+strlen(srcpath);
while (p > srcpath && *p != '/') p--;
*(p+1) = 0;
console_printf("src: %s\n",srcpath);
console_printf("dst: %s\n",path);
if(strcmp(path,srcpath) != 0){
add_file_entry("***Select Here***", TYPE_DIR, 0);
add_file_entry("*** Cancel OP ***", TYPE_DIR, 0);
}
}
add_file_entry("../", TYPE_DIR, 0);
struct fio_file file;
struct fio_dirent * dirent = 0;
dirent = FIO_FindFirstEx( path, &file );
if( IS_ERROR(dirent) )
{
build_file_menu();
return;
}
do
{
if (file.name[0] == '.') continue;
if (file.mode & ATTR_DIRECTORY)
{
int len = strlen(file.name);
snprintf(file.name + len, sizeof(file.name) - len, "/");
add_file_entry(file.name, TYPE_DIR, 0);
}
else
{
add_file_entry(file.name, TYPE_FILE, file.size);
}
}
while( FIO_FindNextEx( dirent, &file ) == 0);
build_file_menu();
FIO_CleanupAfterFindNext_maybe(dirent);
}
static void Browse(char* path)
{
snprintf(gPath, sizeof(gPath), path);
ScanDir(gPath);
}
static void BrowseDown(char* path)
{
STR_APPEND(gPath, "%s", path);
ScanDir(gPath);
}
static void restore_menu_selection(char* old_dir)
{
for (struct file_entry * fe = file_entries; fe; fe = fe->next)
{
if (streq(fe->name, old_dir))
{
fe->menu_entry.selected = 1;
for (struct file_entry * e = file_entries; e; e = e->next)
if (e != fe) e->menu_entry.selected = 0;
break;
}
}
}
static void BrowseUp()
{
char* p = gPath + strlen(gPath) - 2;
while (p > gPath && *p != '/') p--;
if (*p == '/') /* up one level */
{
char old_dir[MAX_PATH_LEN];
snprintf(old_dir, sizeof(old_dir), p+1);
*(p+1) = 0;
ScanDir(gPath);
restore_menu_selection(old_dir);
}
else if (cf_present && sd_present && strlen(gPath) > 0) /* two cards: show A:/ and B:/ in menu */
{
char old_dir[MAX_PATH_LEN];
snprintf(old_dir, sizeof(old_dir), "%s", gPath);
gPath[0] = 0;
ScanDir("");
restore_menu_selection(old_dir);
}
else /* already at the top, close the file browser */
{
menu_close_submenu();
}
}
static int
ML_FIO_CopyFile(char *src,char *dst){
const int bufsize = 128*1024;
void* buf = alloc_dma_memory(bufsize);
if (!buf) return 1;
FILE* f = FIO_Open(src, O_RDONLY | O_SYNC);
if (f == INVALID_PTR) return 1;
FILE* g = FIO_CreateFile(dst);
if (g == INVALID_PTR) { FIO_CloseFile(f); return 1; }
int r = 0;
while ((r = FIO_ReadFile(f, buf, bufsize)))
FIO_WriteFile(g, buf, r);
FIO_CloseFile(f);
FIO_CloseFile(g);
msleep(1000); // this decreases the chances of getting corrupted files (fig ure out why!)
free_dma_memory(buf);
return 0;
}
static int
ML_FIO_MoveFile(char *src,char *dst){
ML_FIO_CopyFile(src,dst);
FIO_RemoveFile(src);
return 0;
}
static void
FileCopy(void *unused)
{
char fname[MAX_PATH_LEN];
size_t totallen = strlen(gSrcFile);
char *p = gSrcFile + totallen;
while (p > gSrcFile && *p != '/') p--;
strcpy(fname,p+1);
char dstfile[MAX_PATH_LEN];
snprintf(dstfile,MAX_PATH_LEN,"%s%s",gPath,fname);
console_printf("Copy\n");
console_printf("src: %s\n",gSrcFile);
console_printf("dst: %s\n",dstfile);
ML_FIO_CopyFile(gSrcFile,dstfile);
}
static void
FileMove(void *unused)
{
char fname[MAX_PATH_LEN];
size_t totallen = strlen(gSrcFile);
char *p = gSrcFile + totallen;
while (p > gSrcFile && *p != '/') p--;
strcpy(fname,p+1);
char dstfile[MAX_PATH_LEN];
snprintf(dstfile,MAX_PATH_LEN,"%s%s",gPath,fname);
console_printf("Move\n");
console_printf("src: %s\n",gSrcFile);
console_printf("dst: %s\n",dstfile);
ML_FIO_MoveFile(gSrcFile,dstfile);
}
static void FileOperation(){
switch(op_mode){
case FILE_OP_COPY:
task_create("FileCopy_task", 0x1b, 0x4000, FileCopy, 0);
break;
case FILE_OP_MOVE:
task_create("FileMove_task", 0x1b, 0x4000, FileMove, 0);
break;
case FILE_OP_PREVIEW:
break;
}
//cleanup
op_mode = FILE_OP_NONE;
ScanDir(gPath);
}
static void FileOpCancel(){
gSrcFile[0] = 0;
op_mode = FILE_OP_NONE;
ScanDir(gPath);
}
static MENU_SELECT_FUNC(BrowseUpMenu)
{
BrowseUp();
}
static MENU_SELECT_FUNC(select_dir)
{
struct file_entry * fe = (struct file_entry *) priv;
char* name = (char*) fe->name;
if(!strcmp(name,"***Select Here***")){
FileOperation();
}else if(!strcmp(name,"*** Cancel OP ***")){
FileOpCancel();
}else if (!strcmp(name,"../") || (delta < 0))
{
BrowseUp();
}
else
{
BrowseDown(name);
}
}
static MENU_UPDATE_FUNC(update_dir)
{
MENU_SET_VALUE("");
MENU_SET_ICON(MNI_AUTO, 0);
MENU_SET_HELP(gPath);
if (entry->selected) view_file = 0;
}
const char * format_size( unsigned size)
{
static char str[ 32 ];
if( size > 1024*1024*1024 )
{
int size_gb = (size/1024 + 5) * 10 / 1024 / 1024;
snprintf( str, sizeof(str), "%d.%dGB", size_gb/10, size_gb%10);
}
else if( size > 1024*1024 )
{
int size_mb = (size/1024 + 5) * 10 / 1024;
snprintf( str, sizeof(str), "%d.%dMB", size_mb/10, size_mb%10);
}
else if( size > 1024 )
{
int size_kb = (size/1024 + 5) * 10;
snprintf( str, sizeof(str), "%d.%dkB", size_kb/10, size_kb%10);
}
else
{
snprintf( str, sizeof(str), "%db", size);
}
return str;
}
static MENU_SELECT_FUNC(CopyFile)
{
strcpy(gSrcFile,gPath);
console_printf("Copysrc: %s\n",gSrcFile);
op_mode = FILE_OP_COPY;
}
static MENU_UPDATE_FUNC(CopyFileProgress)
{
}
static MENU_SELECT_FUNC(MoveFile)
{
strcpy(gSrcFile,gPath);
console_printf("Movesrc: %s\n",gSrcFile);
op_mode = FILE_OP_MOVE;
}
static MENU_UPDATE_FUNC(MoveFileProgress)
{
}
static MENU_SELECT_FUNC(viewfile_toggle)
{
view_file = !view_file;
}
static MENU_UPDATE_FUNC(viewfile_show)
{
if (view_file)
{
static char buf[1025];
FILE * file = FIO_Open( gPath, O_RDONLY | O_SYNC );
if (file != INVALID_PTR)
{
int r = FIO_ReadFile(file, buf, sizeof(buf)-1);
FIO_CloseFile(file);
buf[r] = 0;
info->custom_drawing = CUSTOM_DRAW_THIS_MENU;
clrscr();
big_bmp_printf(FONT_MED, 0, 0, "%s", buf);
}
else
{
MENU_SET_WARNING(MENU_WARN_ADVICE, "Error reading %s", gPath);
view_file = 0;
}
}
else
{
update_action(entry, info);
}
}
static int delete_confirm_flag = 0;
static MENU_SELECT_FUNC(delete_file)
{
if (streq(gPath+1, ":/AUTOEXEC.BIN"))
{
beep();
return;
}
if (!delete_confirm_flag)
{
delete_confirm_flag = get_ms_clock_value();
beep();
}
else
{
delete_confirm_flag = 0;
FIO_RemoveFile(gPath);
BrowseUp();
}
}
static MENU_UPDATE_FUNC(delete_confirm)
{
update_action(entry, info);
/* delete confirmation timeout after 2 seconds */
if (get_ms_clock_value() > delete_confirm_flag + 2000)
delete_confirm_flag = 0;
/* no question mark in in our font, fsck! */
if (delete_confirm_flag)
MENU_SET_RINFO("Press SET to confirm");
}
static MENU_SELECT_FUNC(select_file)
{
struct file_entry * fe = (struct file_entry *) priv;
/* fe will be freed in clear_file_menu; backup things that we are going to reuse */
char name[MAX_PATH_LEN];
snprintf(name, sizeof(name), "%s", fe->name);
int size = fe->size;
STR_APPEND(gPath, "%s", name);
clear_file_menu();
/* at this point, fe was freed and is no longer valid */
fe = 0;
struct file_entry * e = add_file_entry(name, TYPE_FILE, size);
if (!e) return;
e->menu_entry.select = BrowseUpMenu;
e->menu_entry.select_Q = BrowseUpMenu;
e->menu_entry.priv = e;
e = add_file_entry("Copy", TYPE_ACTION, 0);
e->menu_entry.select = CopyFile;
e->menu_entry.update = CopyFileProgress;
e = add_file_entry("Move", TYPE_ACTION, 0);
e->menu_entry.select = MoveFile;
e->menu_entry.update = MoveFileProgress;
e = add_file_entry("View", TYPE_ACTION, 0);
e->menu_entry.select = viewfile_toggle;
e->menu_entry.update = viewfile_show;
e = add_file_entry("Delete", TYPE_ACTION, 0);
e->menu_entry.select = delete_file;
e->menu_entry.update = delete_confirm;
//~ e = add_file_entry("Copy", TYPE_ACTION, 0);
//~ e = add_file_entry("Rename", TYPE_ACTION, 0);
build_file_menu();
}
static MENU_UPDATE_FUNC(update_file)
{
struct file_entry * fe = (struct file_entry *) entry->priv;
MENU_SET_VALUE("");
MENU_SET_RINFO("%s", format_size(fe->size));
MENU_SET_ICON(MNI_OFF, 0);
MENU_SET_HELP(gPath);
if (entry->selected) view_file = 0;
}
static MENU_SELECT_FUNC(default_select_action)
{
beep();
}
static MENU_UPDATE_FUNC(update_action)
{
MENU_SET_VALUE("");
MENU_SET_HELP(gPath);
if (entry->selected) view_file = 0;
}
static int InitRootDir()
{
cf_present = is_dir("A:/");
sd_present = is_dir("B:/");
if (cf_present && !sd_present)
{
Browse("A:/");
}
else if (sd_present && !cf_present)
{
Browse("B:/");
}
else if (sd_present && cf_present)
{
Browse("");
}
else return -1;
return 0;
}
unsigned int fileman_init()
{
menu_add("Debug", fileman_menu, COUNT(fileman_menu));
op_mode = FILE_OP_NONE;
InitRootDir();
return 0;
}
unsigned int fileman_deinit()
{
return 0;
}
MODULE_INFO_START()
MODULE_INIT(fileman_init)
MODULE_DEINIT(fileman_deinit)
MODULE_INFO_END()
MODULE_STRINGS_START()
MODULE_STRING("Author", "ML dev.")
MODULE_STRING("License", "GPL")
MODULE_STRING("Description", "File browser")
MODULE_STRINGS_END()
Is the number the pointer to the file?
Copy works but you have to back out of the folder to see that it did. No problems on my SD Card on 6D.
Funny thing happens: Copy Vram0.bmp / ML
Vram0.bmp in ML.
Now move vram0.bmp to ML
2x vram0.bmp in ML
It survives reboot... on the PC only one vram0.bmp in camera 2
Deleted the file, 1 file remains on SD card on the PC and 1 file remains in ML.
Oh, OK.
I'm only used FIO_CreateFile() , so maybe Dryos make a 2 different i-node files with same filename.
I need to overwrite check and use FIO_Open for overwrite.
Use FIO_CreateFileEx, it handles this quirk.
Thanks alex , I'm tested CreatefileEx(). It's succeed to overwrite with same filename.
Added ScanDir() when it finished copy/move functions and still showing same dir.
#define CONFIG_CONSOLE
#include <module.h>
#include <dryos.h>
#include <property.h>
#include <bmp.h>
#include <menu.h>
#define MAX_PATH_LEN 0x80
static char gPath[MAX_PATH_LEN];
static char gSrcFile[MAX_PATH_LEN];
static unsigned int op_mode;
static int cf_present;
static int sd_present;
struct file_entry
{
struct file_entry * next;
struct menu_entry menu_entry;
char name[MAX_PATH_LEN];
unsigned int size;
unsigned int type: 2;
unsigned int added: 1;
};
#define TYPE_DIR 0
#define TYPE_FILE 1
#define TYPE_ACTION 2
enum _FILER_OP {
FILE_OP_NONE,
FILE_OP_COPY,
FILE_OP_MOVE,
FILE_OP_PREVIEW
};
static struct file_entry * file_entries = 0;
/* view file mode */
static int view_file = 0;
static MENU_SELECT_FUNC(select_dir);
static MENU_UPDATE_FUNC(update_dir);
static MENU_SELECT_FUNC(select_file);
static MENU_UPDATE_FUNC(update_file);
static MENU_SELECT_FUNC(default_select_action);
static MENU_UPDATE_FUNC(update_action);
static MENU_SELECT_FUNC(BrowseUpMenu);
static struct menu_entry fileman_menu[] =
{
{
.name = "File Browser",
.select = menu_open_submenu,
.submenu_width = 710,
.children = (struct menu_entry[]) {
MENU_EOL,
}
}
};
static void clear_file_menu()
{
while (file_entries)
{
struct file_entry * next = file_entries->next;
menu_remove("File Browser", &(file_entries->menu_entry), 1);
console_printf("%s\n", file_entries->name);
FreeMemory(file_entries);
file_entries = next;
}
}
static struct file_entry * add_file_entry(char* txt, int type, int size)
{
struct file_entry * fe = AllocateMemory(sizeof(struct file_entry));
if (!fe) return 0;
memset(fe, 0, sizeof(struct file_entry));
snprintf(fe->name, sizeof(fe->name), "%s", txt);
fe->size = size;
fe->menu_entry.name = fe->name;
fe->menu_entry.priv = fe;
fe->type = type;
fe->menu_entry.select_Q = BrowseUpMenu;
if (fe->type == TYPE_DIR)
{
fe->menu_entry.select = select_dir;
fe->menu_entry.update = update_dir;
}
else if (fe->type == TYPE_FILE)
{
fe->menu_entry.select = select_file;
fe->menu_entry.update = update_file;
}
else if (fe->type == TYPE_ACTION)
{
fe->menu_entry.select = default_select_action;
fe->menu_entry.update = update_action;
fe->menu_entry.icon_type = IT_ACTION;
}
fe->next = file_entries;
file_entries = fe;
return fe;
}
static void build_file_menu()
{
/* HaCKeD Sort */
int done = 0;
while (!done)
{
done = 1;
for (struct file_entry * fe = file_entries; fe; fe = fe->next)
{
if (!fe->added)
{
/* are there any entries that should be before "fe" ? */
/* if yes, skip "fe", add those entries, and try again */
int should_skip = 0;
for (struct file_entry * e = file_entries; e; e = e->next)
{
if (!e->added && e != fe)
{
if (e->type < fe->type) { should_skip = 1; break; }
if ((e->type == fe->type) && strcmp(e->name, fe->name) < 0) { should_skip = 1; break; }
}
}
if (!should_skip)
{
menu_add("File Browser", &(fe->menu_entry), 1);
fe->added = 1;
}
else done = 0;
}
}
}
}
static void ScanDir(char *path)
{
clear_file_menu();
if (strlen(path) == 0)
{
add_file_entry("A:/", TYPE_DIR, 0);
add_file_entry("B:/", TYPE_DIR, 0);
build_file_menu();
return;
}
if(op_mode != FILE_OP_NONE)
{
console_printf("ScanDir\n");
char srcpath[MAX_PATH_LEN];
strcpy(srcpath,gSrcFile);
char *p = srcpath+strlen(srcpath);
while (p > srcpath && *p != '/') p--;
*(p+1) = 0;
console_printf("src: %s\n",srcpath);
console_printf("dst: %s\n",path);
if(strcmp(path,srcpath) != 0){
add_file_entry("***Select Here***", TYPE_DIR, 0);
add_file_entry("*** Cancel OP ***", TYPE_DIR, 0);
}
}
add_file_entry("../", TYPE_DIR, 0);
struct fio_file file;
struct fio_dirent * dirent = 0;
dirent = FIO_FindFirstEx( path, &file );
if( IS_ERROR(dirent) )
{
build_file_menu();
return;
}
do
{
if (file.name[0] == '.') continue;
if (file.mode & ATTR_DIRECTORY)
{
int len = strlen(file.name);
snprintf(file.name + len, sizeof(file.name) - len, "/");
add_file_entry(file.name, TYPE_DIR, 0);
}
else
{
add_file_entry(file.name, TYPE_FILE, file.size);
}
}
while( FIO_FindNextEx( dirent, &file ) == 0);
build_file_menu();
FIO_CleanupAfterFindNext_maybe(dirent);
}
static void Browse(char* path)
{
snprintf(gPath, sizeof(gPath), path);
ScanDir(gPath);
}
static void BrowseDown(char* path)
{
STR_APPEND(gPath, "%s", path);
ScanDir(gPath);
}
static void restore_menu_selection(char* old_dir)
{
for (struct file_entry * fe = file_entries; fe; fe = fe->next)
{
if (streq(fe->name, old_dir))
{
fe->menu_entry.selected = 1;
for (struct file_entry * e = file_entries; e; e = e->next)
if (e != fe) e->menu_entry.selected = 0;
break;
}
}
}
static void BrowseUp()
{
char* p = gPath + strlen(gPath) - 2;
while (p > gPath && *p != '/') p--;
if (*p == '/') /* up one level */
{
char old_dir[MAX_PATH_LEN];
snprintf(old_dir, sizeof(old_dir), p+1);
*(p+1) = 0;
ScanDir(gPath);
restore_menu_selection(old_dir);
}
else if (cf_present && sd_present && strlen(gPath) > 0) /* two cards: show A:/ and B:/ in menu */
{
char old_dir[MAX_PATH_LEN];
snprintf(old_dir, sizeof(old_dir), "%s", gPath);
gPath[0] = 0;
ScanDir("");
restore_menu_selection(old_dir);
}
else /* already at the top, close the file browser */
{
menu_close_submenu();
}
}
static int
ML_FIO_CopyFile(char *src,char *dst){
const int bufsize = 128*1024;
void* buf = alloc_dma_memory(bufsize);
if (!buf) return 1;
FILE* f = FIO_Open(src, O_RDONLY | O_SYNC);
if (f == INVALID_PTR) return 1;
FILE* g = FIO_CreateFileEx(dst);
if (g == INVALID_PTR) { FIO_CloseFile(f); return 1; }
int r = 0;
while ((r = FIO_ReadFile(f, buf, bufsize)))
FIO_WriteFile(g, buf, r);
FIO_CloseFile(f);
FIO_CloseFile(g);
msleep(1000); // this decreases the chances of getting corrupted files (fig ure out why!)
free_dma_memory(buf);
return 0;
}
static int
ML_FIO_MoveFile(char *src,char *dst){
ML_FIO_CopyFile(src,dst);
FIO_RemoveFile(src);
return 0;
}
static void
FileCopy(void *unused)
{
char fname[MAX_PATH_LEN],tmpdst[MAX_PATH_LEN];
strcpy(tmpdst,gPath);
size_t totallen = strlen(gSrcFile);
char *p = gSrcFile + totallen;
while (p > gSrcFile && *p != '/') p--;
strcpy(fname,p+1);
char dstfile[MAX_PATH_LEN];
snprintf(dstfile,MAX_PATH_LEN,"%s%s",gPath,fname);
ML_FIO_CopyFile(gSrcFile,dstfile);
if(!strcmp(gPath,tmpdst)) ScanDir(gPath);
}
static void
FileMove(void *unused)
{
char fname[MAX_PATH_LEN],tmpdst[MAX_PATH_LEN];
strcpy(tmpdst,gPath);
size_t totallen = strlen(gSrcFile);
char *p = gSrcFile + totallen;
while (p > gSrcFile && *p != '/') p--;
strcpy(fname,p+1);
char dstfile[MAX_PATH_LEN];
snprintf(dstfile,MAX_PATH_LEN,"%s%s",gPath,fname);
console_printf("Move\n");
console_printf("src: %s\n",gSrcFile);
console_printf("dst: %s\n",dstfile);
ML_FIO_MoveFile(gSrcFile,dstfile);
if(!strcmp(gPath,tmpdst)) ScanDir(gPath);
}
static void FileOperation(){
switch(op_mode){
case FILE_OP_COPY:
task_create("FileCopy_task", 0x1b, 0x4000, FileCopy, 0);
break;
case FILE_OP_MOVE:
task_create("FileMove_task", 0x1b, 0x4000, FileMove, 0);
break;
case FILE_OP_PREVIEW:
break;
}
//cleanup
op_mode = FILE_OP_NONE;
ScanDir(gPath);
}
static void FileOpCancel(){
gSrcFile[0] = 0;
op_mode = FILE_OP_NONE;
ScanDir(gPath);
}
static MENU_SELECT_FUNC(BrowseUpMenu)
{
BrowseUp();
}
static MENU_SELECT_FUNC(select_dir)
{
struct file_entry * fe = (struct file_entry *) priv;
char* name = (char*) fe->name;
if(!strcmp(name,"***Select Here***")){
FileOperation();
}else if(!strcmp(name,"*** Cancel OP ***")){
FileOpCancel();
}else if (!strcmp(name,"../") || (delta < 0))
{
BrowseUp();
}
else
{
BrowseDown(name);
}
}
static MENU_UPDATE_FUNC(update_dir)
{
MENU_SET_VALUE("");
MENU_SET_ICON(MNI_AUTO, 0);
MENU_SET_HELP(gPath);
if (entry->selected) view_file = 0;
}
const char * format_size( unsigned size)
{
static char str[ 32 ];
if( size > 1024*1024*1024 )
{
int size_gb = (size/1024 + 5) * 10 / 1024 / 1024;
snprintf( str, sizeof(str), "%d.%dGB", size_gb/10, size_gb%10);
}
else if( size > 1024*1024 )
{
int size_mb = (size/1024 + 5) * 10 / 1024;
snprintf( str, sizeof(str), "%d.%dMB", size_mb/10, size_mb%10);
}
else if( size > 1024 )
{
int size_kb = (size/1024 + 5) * 10;
snprintf( str, sizeof(str), "%d.%dkB", size_kb/10, size_kb%10);
}
else
{
snprintf( str, sizeof(str), "%db", size);
}
return str;
}
static MENU_SELECT_FUNC(CopyFile)
{
strcpy(gSrcFile,gPath);
console_printf("Copysrc: %s\n",gSrcFile);
op_mode = FILE_OP_COPY;
}
static MENU_UPDATE_FUNC(CopyFileProgress)
{
}
static MENU_SELECT_FUNC(MoveFile)
{
strcpy(gSrcFile,gPath);
console_printf("Movesrc: %s\n",gSrcFile);
op_mode = FILE_OP_MOVE;
}
static MENU_UPDATE_FUNC(MoveFileProgress)
{
}
static MENU_SELECT_FUNC(viewfile_toggle)
{
view_file = !view_file;
}
static MENU_UPDATE_FUNC(viewfile_show)
{
if (view_file)
{
static char buf[1025];
FILE * file = FIO_Open( gPath, O_RDONLY | O_SYNC );
if (file != INVALID_PTR)
{
int r = FIO_ReadFile(file, buf, sizeof(buf)-1);
FIO_CloseFile(file);
buf[r] = 0;
info->custom_drawing = CUSTOM_DRAW_THIS_MENU;
clrscr();
big_bmp_printf(FONT_MED, 0, 0, "%s", buf);
}
else
{
MENU_SET_WARNING(MENU_WARN_ADVICE, "Error reading %s", gPath);
view_file = 0;
}
}
else
{
update_action(entry, info);
}
}
static int delete_confirm_flag = 0;
static MENU_SELECT_FUNC(delete_file)
{
if (streq(gPath+1, ":/AUTOEXEC.BIN"))
{
beep();
return;
}
if (!delete_confirm_flag)
{
delete_confirm_flag = get_ms_clock_value();
beep();
}
else
{
delete_confirm_flag = 0;
FIO_RemoveFile(gPath);
BrowseUp();
}
}
static MENU_UPDATE_FUNC(delete_confirm)
{
update_action(entry, info);
/* delete confirmation timeout after 2 seconds */
if (get_ms_clock_value() > delete_confirm_flag + 2000)
delete_confirm_flag = 0;
/* no question mark in in our font, fsck! */
if (delete_confirm_flag)
MENU_SET_RINFO("Press SET to confirm");
}
static MENU_SELECT_FUNC(select_file)
{
struct file_entry * fe = (struct file_entry *) priv;
/* fe will be freed in clear_file_menu; backup things that we are going to reuse */
char name[MAX_PATH_LEN];
snprintf(name, sizeof(name), "%s", fe->name);
int size = fe->size;
STR_APPEND(gPath, "%s", name);
clear_file_menu();
/* at this point, fe was freed and is no longer valid */
fe = 0;
struct file_entry * e = add_file_entry(name, TYPE_FILE, size);
if (!e) return;
e->menu_entry.select = BrowseUpMenu;
e->menu_entry.select_Q = BrowseUpMenu;
e->menu_entry.priv = e;
e = add_file_entry("Copy", TYPE_ACTION, 0);
e->menu_entry.select = CopyFile;
e->menu_entry.update = CopyFileProgress;
e = add_file_entry("Move", TYPE_ACTION, 0);
e->menu_entry.select = MoveFile;
e->menu_entry.update = MoveFileProgress;
e = add_file_entry("View", TYPE_ACTION, 0);
e->menu_entry.select = viewfile_toggle;
e->menu_entry.update = viewfile_show;
e = add_file_entry("Delete", TYPE_ACTION, 0);
e->menu_entry.select = delete_file;
e->menu_entry.update = delete_confirm;
//~ e = add_file_entry("Copy", TYPE_ACTION, 0);
//~ e = add_file_entry("Rename", TYPE_ACTION, 0);
build_file_menu();
}
static MENU_UPDATE_FUNC(update_file)
{
struct file_entry * fe = (struct file_entry *) entry->priv;
MENU_SET_VALUE("");
MENU_SET_RINFO("%s", format_size(fe->size));
MENU_SET_ICON(MNI_OFF, 0);
MENU_SET_HELP(gPath);
if (entry->selected) view_file = 0;
}
static MENU_SELECT_FUNC(default_select_action)
{
beep();
}
static MENU_UPDATE_FUNC(update_action)
{
MENU_SET_VALUE("");
MENU_SET_HELP(gPath);
if (entry->selected) view_file = 0;
}
static int InitRootDir()
{
cf_present = is_dir("A:/");
sd_present = is_dir("B:/");
if (cf_present && !sd_present)
{
Browse("A:/");
}
else if (sd_present && !cf_present)
{
Browse("B:/");
}
else if (sd_present && cf_present)
{
Browse("");
}
else return -1;
return 0;
}
unsigned int fileman_init()
{
menu_add("Debug", fileman_menu, COUNT(fileman_menu));
op_mode = FILE_OP_NONE;
InitRootDir();
return 0;
}
unsigned int fileman_deinit()
{
return 0;
}
MODULE_INFO_START()
MODULE_INIT(fileman_init)
MODULE_DEINIT(fileman_deinit)
MODULE_INFO_END()
MODULE_STRINGS_START()
MODULE_STRING("Author", "ML dev.")
MODULE_STRING("License", "GPL")
MODULE_STRING("Description", "File browser")
MODULE_STRINGS_END()
Problem with move/copy seems gone.
Numbers are still there
made a repo
https://bitbucket.org/minimimi/magic-lantern-filer-alex-based
and Found reason for number issue . Solved it now.
Next, I will think about how to implement multiple-selection and "file operation status window".
Ok, I'll keep updating.
ah sorry, i already checked in your new source code.
just wanted to write that here then i saw you forked on bitbucket :)
btw the numbers come from a missing
e->menu_entry.priv = NULL;
after every menu entry.
i also added functions so that registering custom file types by other modules is possible.
so we can playback raw from within the filemanager.
I wouldn't recommend deleting the priv entry, since it was designed to identify the item. Just make sure you call the default update_action if you use a custom update function.
For actions, please use TYPE_ACTION.
Progress updates can be displayed from update_status (displayed at the bottom).
ScanDir is not thread safe, it needs a semaphore.
G3gg0:
Thanks , I already used alex method(Used TYPE_ACTION).But I'm override menu_entry->update myself.It was problem. Solved it now.
And , I'm also thinking RAW playback from filer.
See here.
enum _FILER_OP {
FILE_OP_NONE,
FILE_OP_COPY,
FILE_OP_MOVE,
FILE_OP_PREVIEW
};
So I want to know, how to know raw_rac.mo is already loaded, and how to call funcstion in anothers.
If you have a patch file, share it please,
Alex:
Thanks suggestions for making Progress. Will try it.
ScanDir things. need a "menu_sem" ? . So I added functions in menu.c in previous codes src/filer.c version.
Need to add, GetSemWrapper()/ReleaseSemWrapper() in menu.c?
Is this better way? How do you think?
Quote from: g3gg0 on June 01, 2013, 09:57:57 PM
ah sorry, i already checked in your new source code.
just wanted to write that here then i saw you forked on bitbucket :)
You already have write permission on my repo. Because I'm used same permission with main repo.
Quote from: minimimi on June 02, 2013, 03:35:08 AM
ScanDir things. need a "menu_sem" ?
menu_add already has semaphores, so I think it should be alright. ScanDir was manipulating a linked list, so I've added the scandir_sem just in case. The only situation when things could have gone wrong was if you pressed some key exactly when a copy/move operation finished.
G3gg0 and Alex
Thank you for your merge/implove/brush up.
It's perfect for me. I'm using 64gig SD and 32gig CF. So I wanted to move RAW to SD.
Maybe here and
http://www.magiclantern.fm/forum/index.php?topic=5936.new#new
are close-able.
Multiple file selection is future TODO...I have no idea to make multiple file selection now...
OK!!!!
I found answer for multiple file selection!!!!!
Soon will open new repo.
Already , almost codes are working. Yay, wait a moment
EOSM now have to touch screen to go up :(
https://bitbucket.org/minimimi/magic-lantern-filer-multiselect
basic functions are working. push file twice.
now debugging
-already registerd check when we push copy menu.
-cancel copy operation when we un-select all files
1% ,
we need "../" ?? or need ifdef for EOSM?
Q on eos M is 1 finger tap. No q button, only set. I don't know if you "need" it. I can use it either way.
......
1%
I mean , use ifdef for :
-disable Q
-show "../"
Then you can use file manager with cursol keys. Is this a correct understanding?
I think almost fine on my multiple select codes. please check it except M(sorry.
Modules were designed to be portable, so... better just add it back?
I got it. Never use ifdefs in modules.
Then, how do you think about M 1%?
Either leave as is or add ../ back. Its only an issue where it will be confusing to people.
* Although probably only 2 people with eos M care.
Alex:
Oh! important point is conflicted.
Are you already disabled "Q" ??
?! Q works fine here, it goes back by default (unless you override it when creating menu entries).
https://bitbucket.org/minimimi/magic-lantern-filer-multiselect/compare/hudson/magic-lantern:unified..unified#chg-modules/file_man/file_man.c
see here.
If you have no problem, enbale selectQ plz for multiple file selection.
Modified
Alex. sorry for confusing. I just understood you are not disable Q everyware. So I can solve myself.
File selection works very well, you can even select files from multiple folders and copy them to the same destination dir, very nice!
I've changed the buttons as follows:
- PLAY selects individual files
- menu entry for selecting all files with the same extension (e.g. if you shoot raw+jpeg and you want to delete all jpegs)
We need to be very careful with background tasks and exclusive access to certain data structures; for example, it was trivial to break the copy operation just by navigating to some other directory while copying (!)
Thanks your daily cooperation and polish it up. :D
I think we need to add some more miner chagens. When I make it, I will make a pull req.
-we need skip , when src & dst files are idential
-disable automatic power save (However we can't use half-push tric here. and we don't wanna change NVRAM value...)
For powersave I would add only a warning in menu.
I see. It's one of solution for current our knowlege.
And , pushed small pathces for "src&dst files are idential " and "cleanup memory when unloading".
Please review it.
Um, on my 60d the file browser is extremely slow when I try to browse a directory with a lot of files (like 1000) - is that just me, is it being worked on, or is this just the way it is?
Yeah, the HaCKeD sort is slowing it down. Feel free to optimize it a little.
QuickSort or HeapSort should be pretty good. Or some other algorithm with O(n log n) complexity.
Quote from: a1ex on June 06, 2013, 05:48:56 PM
Yeah, the HaCKeD sort is slowing it down. Feel free to optimize it a little.
Ha, this is the exact thing computer scientists know about, it was only part of my studies and I certainly am not a hardcore algorithm programer :-p ... since the main use for the file browser probably is working with files Canon cannot handle like dng maybe it would make sense to drop them in another directory than the standard Canon DCIM that quickly fills up with cr2/jpg/txt/xmp/... ?
But no big deal if I know I have to just wait 1min or so until the directory is open :-p
Just a few observations about the file manager module...
I spent a few hours shooting both raw video as well as a couple of timelapses. The timelapses were short, only 360 shots each (using silent pics) plus associated xmp files. In total, about 1500 files plus 20 raw files. As others have mentioned, it takes a very long time for directories with a lot of files to open. So long that it makes it a bit impractical to review/playback files between takes. In order to try to solve this while I was out filming, I decided to use the move files feature to move the timelapse related files to another folder on the same CF card. But this turns out to be a very long process as it seems to behave more like a file copy with regards to time than a move. I suggest the following...
1. To make this a more workable solution, is it possible for the move operation when performed within the same card only modify the file allocation table (for fat32 and exfat)? If that's already what it's doing then disregard this suggestion.
2. If the above is not possible, could files created via the intervalometer be automatically created in subfolders? Each intervalometer based operation would be saved in its own subfolder. This would allow viewing of standard raw files without the slowdown that currently exists. This would be ideal since it would not only solve the speed issue, it would allow for logical grouping of timelapses in a way not possible at the moment.
Rename support is very weak in DryOS (on some cameras the function is there, but it just doesn't work). So, copying is the only portable way.
You can create folders from Canon menu; ML can't help you with that (unless you are OK with ERR70 messages).
Ok... thanks. Will use a separate Canon folder for each timelapse and that should suffice.
I've just play with the file browser on the 7D.
It has worked well.
Some suggestions:
- The module name is file manager but the name of the menu item is file browser. It could be the same.
- There is enough space between the file name and the file size so the file date could be a useful info there. The date format can set as the camera's date format using the corresponding property.
Also I've found a bug in the module loader:
- If one module loading fails all the other modules show error and not load.
Something like this.
(http://pel.hu/down/FILEMAN.BMP)
I couldn't figure out how to convert timestamp to struct tm...
Edit:
Done.
Download here: http://pel.hu/ML/file_man.mo
Doh, I should have added this feature request here, sorry, didn't see this thread: http://www.magiclantern.fm/forum/index.php?topic=7410.0
Quote from: a1ex on June 06, 2013, 05:48:56 PM
QuickSort or HeapSort should be pretty good. Or some other algorithm with O(n log n) complexity.
Mergesort has now been implemented (https://bitbucket.org/hudson/magic-lantern/pull-request/319/improvements-to-the-file-manager/diff). I did a few time tests on my 50D, recording both the time it took for the menu to appear and separately printing out the time for just the sort:
Files | Total | Sort |
900 | <1 s | 11 ms |
2800 | 3 s | 40 ms |
5500 | 10 s | 88 ms |
Thus, the sort itself scales like N*log(N) as it should, but the rendering of the menu now completely dominates the total time and scales more like N^2.
Hm, I'd say this one is an overhead from menu_add / menu_remove. What we can try is to compact the linked list into a single array, re-allocate it, and add it to the menu structure with a single call.
For 2800 files:
- total: 2100ms
- menu_add loop: 1900ms
- FindNext loop: 200ms (most likely overhead in add_file_entry and AllocateMemory)
- go back (up one level), menu_remove loop: 1200ms
- console_printf call from menu_remove loop: 200ms (can be deleted)
Note: if you want to measure times longer than 1 second and you have a CPU-intensive loop, be careful that hardware timer overflows every second and the software task that counts the overflows may not wakeup; to avoid this, you can add some get_ms_clock_value calls in the processing loops. Or, use a stopwatch :P
I've tried to run the FindFirst/FindNext loop twice (first time just for counting the items), and first run only takes 1ms!
=> I think it now makes sense to drop the entire linked list thingie and just use a single AllocateMemory call. This should solve all our speed problems.
P.S. menu rendering can't be the problem, because once the menu is built, scrolling is fast.
I've now done detailed time testing, and nearly all of the slowdown is due to unnecessary traversals of the menu_entry list. I just submitted a pull request that dramatically improves the speed of the file manager. The menu now takes under a second to open a 5500-file directory; build_file_menu takes ~230 ms and appears to scale as N now. Menu closing is also really fast.
Can we open a video or a pictures with file_man ?
Feature request: Delete all un/zero-rated and not protected files from directory.
Rationale: When shooting bursts, I often (pre)-select the pictures by rating them or at least protecting the ones I want to review further. Problem is: How to get rid of the rest? At home, I've written a script only moves all rated/protected files from the card. But with only the camera, I'd like to have a function to delete all at once except endless clicking trash->set or set+erase ... which is also prone to human with rated, but not protected files.
Any volunteers to add this to the file browser (Alex :-))? Would it even be possible, b/c ML of course would need to be able to check for the existence of a rating or the protection status (i.e. if the file is write protected or not)?
Protection status should be the same as DOS read-only attribute, so I guess it's easy to read.
For rating status, I guess one should parse the CR2 header somehow, similar to exiftool. Should be doable (maybe even in Lua).
Quote from: a1ex on October 16, 2016, 10:11:12 AM
Protection status should be the same as DOS read-only attribute, so I guess it's easy to read.
Sounds promising, b/c the "quickly protect good images and delete the rest" method is the emergency rescue if you're running out of card space. But in these cases, you're often pressed for time and don't really want to press trash/set for a couple of minutes :-\
Forgive me for opening up this older subject, but I've only just started using the file manager.
I seem to be able to select the files I wish to move from the CF to the sd card, ie ,mlvs.
The screen then has a "move here" item which I click, but I can't work out how to select B (sd card) and complete the move.
Has anyone worked out how to move multiple files from the CF to the sd, say?