Magic Lantern Forum

Developing Magic Lantern => Modules Development => Topic started by: gerk.raisen on May 20, 2013, 10:38:45 AM

Title: Port CHDK file browser (file_man.mo)
Post by: gerk.raisen on May 20, 2013, 10:38:45 AM
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.

Title: Re: Port CHDK file browser
Post by: a1ex on May 20, 2013, 10:42:05 AM
+1, who's going to do it?
Title: Re: Port CHDK file browser
Post by: feureau on May 26, 2013, 07:15:20 AM
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.
Title: Re: Port CHDK file browser
Post by: gerk.raisen on May 27, 2013, 09:28:17 AM
Any help is welcome..
You can try and then submit a pull request for review/approve :)
Title: Re: Port CHDK file browser
Post by: a1ex on May 28, 2013, 01:49:42 PM
@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.
Title: Re: Port CHDK file browser
Post by: Andy600 on May 28, 2013, 02:32:50 PM
Great work guys. Gonna compile it :)
Title: Re: Port CHDK file browser
Post by: a.d. on May 28, 2013, 02:36:47 PM
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?
Title: Re: Port CHDK file browser
Post by: 1% on May 28, 2013, 02:40:45 PM
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.
Title: Re: Port CHDK file browser
Post by: Andy600 on May 28, 2013, 02:43:27 PM
ok, so I won't compile it yet then  ;D
Title: Re: Port CHDK file browser
Post by: a.d. on May 28, 2013, 02:58:38 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.
Title: Re: Port CHDK file browser
Post by: g3gg0 on May 28, 2013, 03:14:37 PM
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.
Title: Re: Port CHDK file browser
Post by: 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
Title: Re: Port CHDK file browser
Post by: gerk.raisen on May 28, 2013, 03:54:36 PM
All you are the best...
Title: Re: Port CHDK file browser
Post by: Audionut on May 28, 2013, 03:59:51 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.
Title: Re: [DONE] Port CHDK file browser
Post by: minimimi on June 01, 2013, 10:03:14 AM
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

Code: [Select]
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;
 }

Title: Re: [DONE] Port CHDK file browser
Post by: g3gg0 on June 01, 2013, 12:01:01 PM
just wanted to thank for your work, this file manager works really cool.
Title: Re: [DONE] Port CHDK file browser
Post by: ted ramasola on June 01, 2013, 12:17:36 PM
file browser is a very useful feature. Thank you all for your work on this.
Title: Re: [DONE] Port CHDK file browser
Post by: 1% on June 01, 2013, 03:03:36 PM
Can you post a fileman.c from the repo with patch applied already? Easier to copy paste the whole thing back and forth.
Title: Re: [DONE] Port CHDK file browser
Post by: minimimi on June 01, 2013, 03:09:12 PM
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.
 

Code: [Select]
#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()
Title: Re: [DONE] Port CHDK file browser
Post by: 1% on June 01, 2013, 03:18:22 PM
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.
Title: Re: [DONE] Port CHDK file browser
Post by: minimimi on June 01, 2013, 03:55:17 PM
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.
Title: Re: [DONE] Port CHDK file browser
Post by: a1ex on June 01, 2013, 04:00:27 PM
Use FIO_CreateFileEx, it handles this quirk.
Title: Re: [DONE] Port CHDK file browser
Post by: minimimi on June 01, 2013, 04:42:23 PM
Thanks alex , I'm tested CreatefileEx(). It's succeed to overwrite with same filename.
Title: Re: [DONE] Port CHDK file browser
Post by: minimimi on June 01, 2013, 05:04:17 PM
Added ScanDir() when it finished copy/move functions and still showing same dir.

Code: [Select]
#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()
Title: Re: [DONE] Port CHDK file browser
Post by: 1% on June 01, 2013, 07:04:10 PM
Problem with move/copy seems gone.

Numbers are still there
Title: Re: [DONE] Port CHDK file browser
Post by: minimimi on June 01, 2013, 08:23:47 PM
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".
Title: Re: [DONE] Port CHDK file browser
Post by: 1% on June 01, 2013, 09:34:43 PM
Ok, I'll keep updating.
Title: Re: [DONE] Port CHDK file browser
Post by: 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 :)

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.
Title: Re: [DONE] Port CHDK file browser
Post by: a1ex on June 01, 2013, 10:48:53 PM
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.
Title: Re: [DONE] Port CHDK file browser
Post by: minimimi on June 02, 2013, 03:35:08 AM
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.
Code: [Select]
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?
Title: Re: [DONE] Port CHDK file browser
Post by: minimimi on June 02, 2013, 04:54:43 AM
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.
Title: Re: [DONE] Port CHDK file browser
Post by: a1ex on June 02, 2013, 09:59:30 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.
Title: Re: [DONE] Port CHDK file browser
Post by: minimimi on June 02, 2013, 01:37:32 PM
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...
Title: Re: [DONE] Port CHDK file browser
Post by: minimimi on June 02, 2013, 05:20:20 PM
OK!!!!
I found answer for multiple file selection!!!!!
Soon will open new repo.
Already , almost codes are working. Yay, wait a moment
Title: Re: [DONE] Port CHDK file browser
Post by: 1% on June 02, 2013, 05:32:35 PM
EOSM now have to touch screen to go up :(
Title: Re: [DONE] Port CHDK file browser
Post by: minimimi on June 02, 2013, 05:36:49 PM
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?
Title: Re: [DONE] Port CHDK file browser
Post by: 1% on June 02, 2013, 05:50:29 PM
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.
Title: Re: [DONE] Port CHDK file browser
Post by: aaphotog on June 02, 2013, 06:28:22 PM
......
Title: Re: [DONE] Port CHDK file browser
Post by: minimimi on June 02, 2013, 06:31:20 PM
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.
Title: Re: [DONE] Port CHDK file browser
Post by: a1ex on June 02, 2013, 06:32:38 PM
Modules were designed to be portable, so... better just add it back?
Title: Re: [DONE] Port CHDK file browser
Post by: minimimi on June 02, 2013, 06:52:56 PM
I got it. Never use ifdefs in modules.
Then, how do you think about M 1%?
Title: Re: [DONE] Port CHDK file browser
Post by: 1% on June 02, 2013, 07:48:20 PM
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.
Title: Re: [DONE] Port CHDK file browser
Post by: minimimi on June 03, 2013, 04:34:32 PM
Alex:
Oh! important point is conflicted.

Are you already disabled "Q" ??
Title: Re: [DONE] Port CHDK file browser
Post by: a1ex on June 03, 2013, 04:47:28 PM
?! Q works fine here, it goes back by default (unless you override it when creating menu entries).
Title: Re: [DONE] Port CHDK file browser
Post by: minimimi on June 03, 2013, 04:59:11 PM
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.
Title: Re: [DONE] Port CHDK file browser
Post by: a1ex on June 05, 2013, 07:07:54 PM
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 (!)
Title: Re: [DONE] Port CHDK file browser
Post by: minimimi on June 06, 2013, 10:20:22 AM
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...)
Title: Re: [DONE] Port CHDK file browser
Post by: a1ex on June 06, 2013, 01:46:55 PM
For powersave I would add only a warning in menu.
Title: Re: [DONE] Port CHDK file browser
Post by: minimimi on June 06, 2013, 04:45:48 PM
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.
Title: Re: [DONE] Port CHDK file browser
Post by: Marsu42 on June 06, 2013, 05:45:38 PM
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?
Title: Re: [DONE] Port CHDK file browser
Post by: a1ex on June 06, 2013, 05:48:56 PM
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.
Title: Re: [DONE] Port CHDK file browser
Post by: Marsu42 on June 06, 2013, 05:57:50 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
Title: Re: [DONE] Port CHDK file browser
Post by: platu on June 16, 2013, 07:37:06 PM
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.
Title: Re: [DONE] Port CHDK file browser
Post by: a1ex on June 16, 2013, 08:15:28 PM
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).
Title: Re: [DONE] Port CHDK file browser
Post by: platu on June 17, 2013, 03:40:42 AM
Ok... thanks.  Will use a separate Canon folder for each timelapse and that should suffice.
Title: Re: [DONE] Port CHDK file browser
Post by: Pelican on July 26, 2013, 03:22:01 PM
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.
Title: Re: [DONE] Port CHDK file browser
Post by: Pelican on July 30, 2013, 03:16:07 AM
Something like this.
(https://www.magiclantern.fm/forum/proxy.php?request=http%3A%2F%2Fpel.hu%2Fdown%2FFILEMAN.BMP&hash=0f632169b4a9282ad70e341ec88df3b8)

I couldn't figure out how to convert timestamp to struct tm...

Edit:
Done.
Download here: http://pel.hu/ML/file_man.mo
Title: Re: [DONE] Port CHDK file browser
Post by: Marsu42 on July 31, 2013, 09:50:00 AM
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
Title: Re: [DONE] Port CHDK file browser
Post by: ayshih on January 09, 2014, 05:03:41 AM
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:

FilesTotalSort
900<1 s11 ms
28003 s40 ms
550010 s88 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.
Title: Re: Port CHDK file browser (file_man.mo)
Post by: a1ex on January 09, 2014, 10:20:20 AM
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.
Title: Re: Port CHDK file browser (file_man.mo)
Post by: ayshih on January 11, 2014, 10:41:36 PM
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.
Title: Re: Port CHDK file browser (file_man.mo)
Post by: josd on May 11, 2014, 06:12:10 PM
Can we open a video or a pictures with file_man ?
Title: Re: Port CHDK file browser (file_man.mo)
Post by: Marsu42 on October 15, 2016, 11:00:34 PM
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)?
Title: Re: Port CHDK file browser (file_man.mo)
Post by: 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.

For rating status, I guess one should parse the CR2 header somehow, similar to exiftool. Should be doable (maybe even in Lua).
Title: Re: Port CHDK file browser (file_man.mo)
Post by: Marsu42 on October 16, 2016, 11:13:52 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 :-\
Title: Re: Port CHDK file browser (file_man.mo)
Post by: garry23 on November 27, 2019, 08:28:17 AM
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?