root/modules/gui_fselect.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. free_list
  2. add_item
  3. fselect_sort
  4. sort_list
  5. chk_ext
  6. chk_prefix
  7. chk_name
  8. is_parent
  9. is_current
  10. is_raw
  11. is_jpg
  12. delete_file
  13. delete_dir
  14. opendir_fselect
  15. copy_file
  16. fs_readdir
  17. process_dir
  18. fselect_goto_prev
  19. fselect_goto_next
  20. gui_fselect_free_data
  21. gui_fselect_read_dir
  22. gui_fselect_find_start_dir
  23. gui_fselect_init
  24. gui_fselect_draw
  25. fselect_delete_file_cb
  26. find_jpg
  27. purge_file
  28. purge_file_DCIM
  29. fselect_purge_cb_DCIM
  30. fselect_purge_cb_dir
  31. fselect_purge_cb_file
  32. fselect_delete_folder_cb
  33. confirm_delete_directory
  34. fselect_marked_toggle
  35. gui_fselect_marked_free_data
  36. fselect_marked_copy_list
  37. fselect_marked_paste_cb
  38. fselect_real_marked_count
  39. fselect_marked_count
  40. fselect_marked_delete_cb
  41. fselect_chdk_replace_cb
  42. fselect_marked_inverse_selection
  43. process_raw_files
  44. fselect_subtract_cb
  45. setup_batch_subtract
  46. process_dng_to_raw_files
  47. fselect_mpopup_rawop_cb
  48. mkdir_cb
  49. rename_cb
  50. isPurgeDCIM
  51. isPurgeDir
  52. fselect_mpopup_cb
  53. finalize_fselect
  54. exit_fselect
  55. gui_fselect_kbd_process
  56. gui_fselect_kbd_process_menu_btn
  57. _module_unloader
  58. _module_can_unload
  59. _module_exit_alt

   1 #include "camera_info.h"
   2 #include "keyboard.h"
   3 #include "modes.h"
   4 #include "sd_card.h"
   5 #include "debug_led.h"
   6 #include "lang.h"
   7 #include "gui.h"
   8 #include "gui_draw.h"
   9 #include "gui_lang.h"
  10 #include "gui_mbox.h"
  11 #include "raw.h"
  12 #include "conf.h"
  13 #include "time.h"
  14 #include "dirent.h"
  15 #include "ctype.h"
  16 
  17 #include "gui_fselect.h"
  18 #include "raw_merge.h"
  19 #include "dng.h"
  20 #include "gui_mpopup.h"
  21 #include "gui_tbox.h"
  22 #include "gui_read.h"
  23 
  24 #include "module_load.h"
  25 
  26 /*
  27     HISTORY:    1.1 - added tbox usage [CHDK 1.1.1 required]
  28 */
  29 
  30 int gui_fselect_kbd_process();
  31 void gui_fselect_kbd_process_menu_btn();
  32 void gui_fselect_draw(int enforce_redraw);
  33 
  34 gui_handler GUI_MODE_FSELECT_MODULE =
  35     /*GUI_MODE_FSELECT*/    { GUI_MODE_FSELECT, gui_fselect_draw, gui_fselect_kbd_process, gui_fselect_kbd_process_menu_btn, 0, 0 };
  36 
  37 //-------------------------------------------------------------------
  38 #define BODY_LINES              12
  39 #define BODY_FONT_LINES         BODY_LINES * FONT_HEIGHT
  40 
  41 #define NAME_SIZE               camera_screen.fselect_name_size  // "FILENAME.123 "  (8.3 filenames)
  42 #define SIZE_SIZE               camera_screen.fselect_size_size  // "1000 b|M|G"
  43 #define TIME_SIZE               camera_screen.fselect_time_size  // "01.01'70 00:00"
  44 
  45 #define NAME_FONT_SIZE          NAME_SIZE * FONT_WIDTH
  46 #define EXTE_FONT_SIZE          EXTE_SIZE * FONT_WIDTH
  47 #define SIZE_FONT_SIZE          SIZE_SIZE * FONT_WIDTH
  48 #define TIME_FONT_SIZE          TIME_SIZE * FONT_WIDTH
  49 
  50 #define SPACING                 4
  51 #define TAB_DIVIDER             1
  52 #define BORDER                  2
  53 #define SCROLLBAR               4
  54 
  55 #define MARKED_OP_NONE          0
  56 #define MARKED_OP_CUT           1
  57 #define MARKED_OP_COPY          2
  58 
  59 //-------------------------------------------------------------------
  60 // Module state
  61 static int running = 0;
  62 static gui_handler *gui_fselect_mode_old; // stored previous gui_mode
  63 
  64 #define MAX_PATH_LEN    100
  65 
  66 // Current selection, etc
  67 static char selected_file[MAX_PATH_LEN];    // This full path to current file. So it is return value
  68 static char buf[MAX_PATH_LEN];
  69 
  70 // basic element of file list (struct fields ordered to minimize size)
  71 typedef struct _fitem
  72 {
  73     struct _fitem   *prev, *next;   // Linked list of items in directory
  74     unsigned long   size;
  75     unsigned long   mtime;
  76     unsigned short  n;
  77     unsigned char   marked;
  78     unsigned char   isdir;      // directory?
  79     unsigned char   isparent;   // '..'
  80     unsigned char   isvalid;    // stat() call succeeded on item
  81     char            name[1];    // this structure must dynamically allocated !!!
  82                                 // when allocating add the required length of the name (malloc(sizeof(fitem) + strlen(name))
  83                                 // only one buffer then needs to be allocated and released
  84 } fitem;
  85 
  86 // List container
  87 typedef struct
  88 {
  89     fitem           *head;
  90     fitem           *tail;
  91     unsigned short  count;
  92     char            dir[MAX_PATH_LEN];  // Current directory for list
  93 } flist;
  94 
  95 static flist    items;          // head of list<fitem>:  holder of current directory list
  96 static flist    marked_items;   // head of list<fitem>:  holder of selected files list (keep independent filelist). made by Cut/Copy
  97 
  98 static fitem    *top;           // ptr to first displayed file (top on the screen)
  99 static fitem    *selected;      // ptr to current file (file on cursor)
 100 
 101 static char marked_operation;   // info for paste: MARKED_OP_NONE, MARKED_OP_CUT, MARKED_OP_COPY
 102 
 103 #define MAIN_H  (FONT_HEIGHT + TAB_DIVIDER + BODY_FONT_LINES + TAB_DIVIDER + FONT_HEIGHT)   // Constant
 104 
 105 static coord main_x, main_y, main_w; //main browser window coord (without BORDERs)
 106 static coord body_y; //main body window coord
 107 static coord foot_y; //footer window coord
 108 
 109 static int gui_fselect_redraw;  // flag request fselect redraw: 0-no, 1-only filelist, 2-whole_redraw(incl.border)
 110 static int gui_fselect_readdir; // flag to force re-read of current directory
 111 static char *fselect_title;     // Title of fselect window (could be different: Choose Text, Choose Script, etc)
 112 
 113 static void (*fselect_on_select)(const char *fn);
 114 static char raw_operation;      // info for process_raw_files() RAW_OPERATION_AVERAGE, RAW_OPERATION_SUM,
 115 
 116 // FSelector POPUP
 117 #define MPOPUP_CUT              0x0001
 118 #define MPOPUP_COPY             0x0002
 119 #define MPOPUP_PASTE            0x0004
 120 #define MPOPUP_DELETE           0x0008
 121 #define MPOPUP_SELINV           0x0010
 122 #define MPOPUP_RAWOPS           0x0020
 123 #define MPOPUP_PURGE_DCIM       0x0040
 124 #define MPOPUP_PURGE_DIR        0x0080
 125 #define MPOPUP_PURGE_FILE       0x0100
 126 #define MPOPUP_RMDIR            0x0200
 127 #define MPOPUP_MKDIR            0x0400
 128 #define MPOPUP_RENAME           0x0800
 129 #define MPOPUP_EDITOR           0x1000
 130 #define MPOPUP_CHDK_REPLACE     0x2000
 131 
 132 static struct mpopup_item popup[]= {
 133         { MPOPUP_CUT,           LANG_POPUP_CUT    },
 134         { MPOPUP_COPY,          LANG_POPUP_COPY   },
 135         { MPOPUP_PASTE,         LANG_POPUP_PASTE  },
 136         { MPOPUP_DELETE,        LANG_POPUP_DELETE },
 137         { MPOPUP_RMDIR,         LANG_POPUP_RMDIR  },
 138         { MPOPUP_PURGE_DCIM,    LANG_POPUP_PURGE  },
 139         { MPOPUP_PURGE_DIR,     LANG_POPUP_PURGE  },
 140         { MPOPUP_PURGE_FILE,    LANG_POPUP_PURGE  },
 141         { MPOPUP_MKDIR,         LANG_POPUP_MKDIR  },
 142         { MPOPUP_RENAME,        LANG_POPUP_RENAME },
 143         { MPOPUP_SELINV,        LANG_POPUP_SELINV },
 144         { MPOPUP_EDITOR,        (int)"Edit" },
 145         { MPOPUP_CHDK_REPLACE,  (int)"Set this CHDK" },
 146         { MPOPUP_RAWOPS,        (int)"Raw ops ->" },
 147         { 0,                    0 },
 148 };
 149 
 150 #define MPOPUP_RAW_ADD          0x0020
 151 #define MPOPUP_RAW_AVERAGE      0x0040
 152 #define MPOPUP_SUBTRACT         0x0100
 153 #define MPOPUP_RAW_DEVELOP      0x0200
 154 #define MPOPUP_DNG_TO_CRW       0x0400
 155 
 156 static struct mpopup_item popup_rawop[]= {
 157         { MPOPUP_RAW_ADD,       LANG_POPUP_RAW_SUM},
 158         { MPOPUP_RAW_AVERAGE,   LANG_POPUP_RAW_AVERAGE },
 159         { MPOPUP_RAW_DEVELOP,   LANG_MENU_RAW_DEVELOP },
 160         { MPOPUP_SUBTRACT,      LANG_POPUP_SUB_FROM_MARKED  },
 161         { MPOPUP_DNG_TO_CRW,    (int)"DNG -> CHDK RAW"},
 162         { 0,                    0 },
 163 };
 164 
 165 //-------------------------------------------------------------------
 166 // List helper functions
 167 
 168 static void free_list(flist *list)
 169 {
 170     fitem *ptr = list->head;
 171 
 172     while (ptr)
 173     {
 174         fitem *prev = ptr;
 175         ptr = ptr->next;
 176         free(prev);
 177     }
 178 
 179     list->head = list->tail = 0;
 180     list->count = 0;
 181 }
 182 
 183 static void add_item(flist *list, const char *name,
 184         unsigned long size, unsigned long mtime,
 185         unsigned char marked, unsigned char isdir, unsigned char isparent, unsigned char isvalid)
 186 {
 187     fitem *p = malloc(sizeof(fitem) + strlen(name));
 188     if (p)
 189     {
 190         p->n = list->count;
 191         strcpy(p->name, name);
 192         p->size = size;
 193         p->mtime = mtime;
 194         p->marked = marked;
 195         p->isdir = isdir;
 196         p->isparent = isparent;
 197         p->isvalid = isvalid;
 198 
 199         p->next = 0;
 200         p->prev = list->tail;
 201         if (list->tail)
 202             list->tail->next = p;
 203         list->tail = p;
 204         if (list->head == 0)
 205             list->head = p;
 206 
 207         list->count++;
 208     }
 209 }
 210 
 211 int fselect_sort(const void* v1, const void* v2)
 212 {
 213     fitem *i1 = *((fitem **)v1);
 214     fitem *i2 = *((fitem **)v2);
 215 
 216     if (i1->isdir)
 217     {
 218         if (i2->isdir)
 219         {
 220             if (i1->isparent)
 221             {
 222                 return -1;
 223             }
 224             else if (i2->isparent)
 225             {
 226                 return 1;
 227             }
 228             else
 229             {
 230                 return strcmp(i1->name, i2->name);
 231             }
 232         }
 233         else
 234         {
 235             return -1;
 236         }
 237     }
 238     else
 239     {
 240         if (i2->isdir)
 241         {
 242             return 1;
 243         }
 244         else
 245         {
 246             return strcmp(i1->name, i2->name);
 247         }
 248     }
 249 }
 250 
 251 static void sort_list(flist *list)
 252 {
 253     if (list->count)
 254     {
 255         // sort
 256         fitem **sbuf = malloc(list->count*sizeof(fitem*));
 257         if (sbuf)
 258         {
 259             fitem *ptr = list->head;
 260             int i = 0;
 261             while (ptr)
 262             {
 263                 sbuf[i++] = ptr;
 264                 ptr = ptr->next;
 265             }
 266 
 267             extern int fselect_sort_nothumb(const void* v1, const void* v2);
 268             qsort(sbuf, list->count, sizeof(fitem*), fselect_sort_nothumb);
 269 
 270             list->head = sbuf[0];
 271             list->tail = sbuf[list->count-1];
 272             for (i=0; i<list->count-1; i++)
 273             {
 274                 sbuf[i]->n = i;
 275                 sbuf[i]->next = sbuf[i+1];
 276                 sbuf[i+1]->prev = sbuf[i];
 277             }
 278             list->head->prev = 0;
 279             list->tail->next = 0;
 280             list->tail->n = list->count - 1;
 281 
 282             free(sbuf);
 283         }
 284     }
 285 }
 286 
 287 //-------------------------------------------------------------------
 288 // Helper methods to check filenames (case insensitive)
 289 
 290 // Match extension
 291 static int chk_ext(const char *ext, const char *tst)
 292 {
 293     if (ext && (*ext != '.'))    // if passed file name, find extension
 294         ext = strrchr(ext, '.');
 295     if (ext)
 296     {
 297         ext++;
 298         if (strlen(ext) == strlen(tst))
 299         {
 300             int i;
 301             for (i=0; i<strlen(tst); i++)
 302                 if (toupper(ext[i]) != toupper(tst[i]))
 303                     return 0;
 304             return 1;
 305         }
 306     }
 307     return 0;
 308 }
 309 
 310 // Match start of filename
 311 static int chk_prefix(const char *name, const char *prefix)
 312 {
 313     if (name && (strlen(name) >= strlen(prefix)))
 314     {
 315         int i;
 316         for (i=0; i<strlen(prefix); i++)
 317             if (toupper(prefix[i]) != toupper(name[i]))
 318                 return 0;
 319         return 1;
 320     }
 321     return 0;
 322 }
 323 
 324 // Match entire name
 325 static int chk_name(const char *name, const char *tst)
 326 {
 327     if (name && (strlen(name) == strlen(tst)))
 328     {
 329         int i;
 330         for (i=0; i<strlen(tst); i++)
 331             if (toupper(tst[i]) != toupper(name[i]))
 332                 return 0;
 333         return 1;
 334     }
 335     return 0;
 336 }
 337 
 338 // Check for current or parent directory
 339 static int is_parent(const char *name)  { return (strcmp(name, "..") == 0); }
 340 static int is_current(const char *name)  { return (strcmp(name, ".") == 0); }
 341 
 342 // Check if file is a RAW image
 343 static int is_raw(const char *name)
 344 {
 345     return ((chk_prefix(name,"crw_") || (chk_prefix(name,"img_")))) &&
 346            ((chk_ext(name,"cr2") || (chk_ext(name,"crw") || (chk_ext(name,"dng")))));
 347 }
 348 
 349 // Check if file is a JPG
 350 static int is_jpg(const char *name)
 351 {
 352     return (chk_prefix(name,"img_")) && (chk_ext(name,"jpg"));
 353 }
 354 
 355 //-------------------------------------------------------------------
 356 // File / folder helper functions
 357 
 358 static void delete_file(const char *path, const char *name)
 359 {
 360     sprintf(selected_file, "%s/%s", path, name);
 361     remove(selected_file);
 362 }
 363 
 364 static void delete_dir(const char *path)
 365 {
 366     remove(path);
 367 }
 368 
 369 // helper to handle gui lfn option
 370 static DIR * opendir_fselect(const char *path) {
 371     return opendir_chdk(path,(conf.disable_lfn_parser_ui?OPENDIR_FL_NONE:OPENDIR_FL_CHDK_LFN));
 372 }
 373 
 374 // Uncached memory buffer for file copy
 375 // Keep byffer after allocation (until module exits)
 376 static unsigned char    *ubuf = 0;
 377 #define COPY_BUF_SIZE   16384           // Copy buffer size
 378 
 379 static int copy_file(const char *src_dir, const char *src_file, const char *dst_dir, const char *dst_file, int overwrite)
 380 {
 381     int rv = 0;
 382 
 383     // If source and destination are the same abort (and fail) copy operation
 384     if (chk_name(src_dir, dst_dir) && chk_name(src_file, dst_file))
 385         return 0;
 386 
 387     // Get source path and open file
 388     sprintf(selected_file, "%s/%s", src_dir, src_file);
 389     int fsrc = open(selected_file, O_RDONLY, 0777);
 390 
 391     if (fsrc >= 0)
 392     {
 393         // Get destination path, check for overwrite, and open file
 394         sprintf(selected_file,"%s/%s", dst_dir, dst_file);
 395         if (!overwrite)
 396         {
 397             struct stat st;
 398             if (stat(selected_file, &st) == 0)
 399             {
 400                 close(fsrc);
 401                 // destination exists, and overwrite not selected, copy failed
 402                 return 0;
 403             }
 404         }
 405         int fdst = open(selected_file, O_WRONLY|O_CREAT|O_TRUNC, 0777);
 406 
 407         if (fdst >= 0)
 408         {
 409             int ss, sd = 0;
 410 
 411             // Allocate buffer if not already allocated
 412             if (ubuf == 0)
 413             {
 414                 ubuf = umalloc(COPY_BUF_SIZE);
 415                 if (ubuf == 0)
 416                 {
 417                     // umalloc failed - ToDo this should display an error popup?
 418                     close(fdst);
 419                     close(fsrc);
 420                     return 0;
 421                 }
 422             }
 423 
 424             // Copy contents
 425             do
 426             {
 427                 ss = read(fsrc, ubuf, COPY_BUF_SIZE);
 428                 if (ss > 0)
 429                     sd = write(fdst, ubuf, ss);
 430             } while (ss > 0 && ss == sd);
 431 
 432             if (ss == 0)
 433                 rv = 1;
 434 
 435             close(fdst);
 436 
 437             struct utimbuf t;
 438             t.actime = t.modtime = selected->mtime;
 439             utime(selected_file, &t);
 440         }
 441 
 442         close(fsrc);
 443     }
 444 
 445     return rv;
 446 }
 447 
 448 //-------------------------------------------------------------------
 449 // Structure to hold info about a file or directory
 450 typedef struct
 451 {
 452     struct dirent   *de;
 453     unsigned long   size;
 454     unsigned long   mtime;
 455     unsigned char   deleted;        // deleted entry?
 456     unsigned char   isdir;          // directory?
 457     unsigned char   isparent;       // parent directory (..)?
 458     unsigned char   iscurrent;      // current directory (.)?
 459     unsigned char   isvalid;        // stat() call succeeded
 460     unsigned char   ishidden;       // hidden attribute?
 461 } fs_dirent;
 462 
 463 // Custom readdir - populates extra info about the file or directory
 464 // Uses stat() after calling readdir() - we don't currently get this
 465 // extra data from the firmware function.
 466 static int fs_readdir(DIR *d, fs_dirent *de, const char* path)
 467 {
 468     // DO NOT USE 'selected_file' global var.
 469     // This function is called from GUI task, 'selected_file' is used by 'KBD' task
 470     char pbuf[MAX_PATH_LEN];
 471 
 472     de->de = readdir(d);
 473     de->size = 0;
 474     de->mtime = 0;
 475     de->deleted  = 0;
 476     de->isparent = 0;
 477     de->iscurrent = 0;
 478     de->isdir = 0;
 479     de->isvalid = 0;
 480     de->ishidden = 0;
 481 
 482     if (de->de)
 483     {
 484         if (de->de->d_name[0] == 0xE5)
 485         {
 486             de->deleted = 1;
 487         }
 488         else
 489         {
 490             de->isparent  = is_parent(de->de->d_name);
 491             de->iscurrent = is_current(de->de->d_name);
 492 
 493             sprintf(pbuf, "%s/%s", path, de->de->d_name);
 494             struct stat st;
 495             if (de->isparent || de->iscurrent)
 496             {
 497                 de->isdir = 1;
 498                 de->isvalid = 1;
 499             }
 500             else if (stat(pbuf, &st) == 0)
 501             {
 502                 de->size  = st.st_size;
 503                 de->mtime = st.st_mtime;
 504                 de->isvalid = 1;
 505                 de->isdir = ((st.st_attrib & DOS_ATTR_DIRECTORY) != 0);
 506                 de->ishidden = ((st.st_attrib & DOS_ATTR_HIDDEN) != 0);
 507             }
 508         }
 509 
 510         return 1;
 511     }
 512 
 513     return 0;
 514 }
 515 
 516 // Process contents of named directory
 517 //  - for sub directories, process recursively if required (up to 'nested' levels deep)
 518 //  - for files, call 'file_process' function
 519 // Once file & directory processing done, call 'dir_process' function on input path
 520 static void process_dir(const char *parent, const char *name, int nested, void (*file_process)(const char *path, const char *file), void (*dir_process)(const char *path))
 521 {
 522     DIR         *d;
 523     fs_dirent   de;
 524 
 525     // Get full name
 526     char *path;
 527     if (name)
 528     {
 529         path = malloc(strlen(parent) + strlen(name) + 2);
 530         sprintf(path, "%s/%s", parent, name);
 531     }
 532     else
 533     {
 534         path = (char*)parent;
 535     }
 536 
 537     // Open directory
 538     d = opendir_fselect(path);
 539 
 540     if (d)
 541     {
 542         // Process contents
 543         while (fs_readdir(d, &de, path))
 544         {
 545             if (!de.deleted)
 546             {
 547                 // Sub directory? Process recursively (but only 'nested' level deep)
 548                 if (de.isdir)
 549                 {
 550                     if (!de.isparent && !de.iscurrent && nested)
 551                         process_dir(path, de.de->d_name, nested-1, file_process, dir_process);
 552                 }
 553                 else if (file_process)
 554                 {
 555                     file_process(path, de.de->d_name);
 556                 }
 557             }
 558         }
 559         closedir(d);
 560 
 561         if (dir_process)
 562             dir_process(path);
 563     }
 564 
 565     if (name)
 566         free(path);
 567 }
 568 
 569 //-------------------------------------------------------------------
 570 static void fselect_goto_prev(int step)
 571 {
 572     int j;
 573 
 574     for (j=0; j<step; ++j)
 575     {
 576         if (selected->prev)
 577         {
 578             selected = selected->prev;      // previous line
 579             if (selected == top && top->prev)
 580                 top = top->prev;
 581         }
 582         else if (step == 1)
 583         {
 584             // off top - jump to bottom
 585             selected = top = items.tail;
 586             while (((selected->n - top->n) < (BODY_LINES - 1)) && top->prev)
 587                 top = top->prev;
 588         }
 589     }
 590 }
 591 
 592 //-------------------------------------------------------------------
 593 static void fselect_goto_next(int step)
 594 {
 595     int j;
 596     for (j=0; j<step; ++j)
 597     {
 598         if (selected->next)
 599         {
 600             selected = selected->next;      // next line
 601             if (((selected->n - top->n) == (BODY_LINES - 1)) && selected->next)
 602                 top = top->next;
 603         }
 604         else if (step == 1)
 605         {
 606             selected = top = items.head;    // off bottom - jump to top
 607         }
 608     }
 609 }
 610 
 611 //-------------------------------------------------------------------
 612 static void gui_fselect_free_data()
 613 {
 614     free_list(&items);
 615     top = selected = NULL;
 616 }
 617 
 618 //-------------------------------------------------------------------
 619 static void gui_fselect_read_dir()
 620 {
 621     DIR         *d;
 622     fs_dirent   de;
 623     int         fndParent = 0;  // Set if parent ".." directory returned by fs_readdir (does not exist on exFat paritions)
 624 
 625     gui_fselect_free_data();
 626 
 627     if ((items.dir[0] == 'A') && (items.dir[1] == 0))
 628         d = opendir_fselect("A/");
 629     else
 630         d = opendir_fselect(items.dir);
 631 
 632     if (d)
 633     {
 634         while (fs_readdir(d, &de, items.dir))
 635         {
 636             if (!de.deleted && !de.iscurrent && (conf.show_hiddenfiles || !de.ishidden))
 637             {
 638                 add_item(&items, de.de->d_name, de.size, de.mtime, 0, de.isdir, de.isparent, de.isvalid);
 639                 if (de.isparent)
 640                     fndParent = 1;
 641             }
 642         }
 643         closedir(d);
 644     }
 645 
 646     // If not reading root directory, and ".." not found, then add it (for exFat partitions)
 647     if ((strlen(items.dir) > 2) && !fndParent)
 648     {
 649         add_item(&items, "..", 0, 0, 0, 1, 1, 1);
 650     }
 651 
 652     sort_list(&items);
 653 
 654     top = selected = items.head;
 655 }
 656 
 657 //-------------------------------------------------------------------
 658 // Attempt to set startup directory (and file) based on input 'dir'
 659 // Note: 'dir' may be a directory name or a file name (including path)
 660 // Returns 1 if valid directory/file found, 0 otherwise
 661 int gui_fselect_find_start_dir(const char* dir)
 662 {
 663     selected_file[0] = 0;
 664     strcpy(items.dir, dir);
 665 
 666     // Make sure there is something left to check
 667     while (strlen(items.dir) > 0)
 668     {
 669         // Find start of filename part (if present)
 670         char *p = strrchr(items.dir,'/');
 671 
 672         struct stat st;
 673         // check if input 'dir' exists
 674         if (stat(items.dir,&st) == 0)
 675         {
 676             // exists - check if it is a directory or file
 677             if ((st.st_attrib & DOS_ATTR_DIRECTORY) == 0)
 678             {
 679                 // 'dir' is a file, copy filename to 'selected_file' and remove from 'items.dir'
 680                 strcpy(selected_file, p+1);
 681                 *p = 0;
 682             }
 683             return 1;
 684         }
 685         else
 686         {
 687             // could not find 'dir' - try one level up
 688             if (p)
 689                 *p = 0;
 690             else
 691                 return 0;
 692         }
 693     }
 694 
 695     return 0;
 696 }
 697 
 698 //-------------------------------------------------------------------
 699 void gui_fselect_init(int title, const char* prev_dir, const char* default_dir, void (*on_select)(const char *fn))
 700 {
 701     running = 1;
 702 
 703     main_w = SPACING + NAME_FONT_SIZE + FONT_WIDTH + SIZE_FONT_SIZE + FONT_WIDTH + TIME_FONT_SIZE + SPACING + SCROLLBAR;
 704     main_x = (camera_screen.width  - main_w) >> 1;
 705     main_y = (camera_screen.height - MAIN_H) >> 1;
 706 
 707     body_y = main_y + FONT_HEIGHT + TAB_DIVIDER;
 708     foot_y = body_y + BODY_FONT_LINES + TAB_DIVIDER;
 709 
 710     fselect_title = lang_str(title);
 711 
 712     // Try and set start directory, and optionally selected file, from inputs
 713     if (!gui_fselect_find_start_dir(prev_dir))
 714         if (!gui_fselect_find_start_dir(default_dir))
 715             gui_fselect_find_start_dir("A");
 716 
 717     gui_fselect_read_dir();
 718 
 719     // Find selected file if it exists in list
 720     if (selected_file[0])
 721     {
 722         fitem *p = items.head;
 723         while (p)
 724         {
 725             if (chk_name(p->name,selected_file))
 726             {
 727                 break;
 728             }
 729             p = p->next;
 730             fselect_goto_next(1);
 731         }
 732         if (!p) selected_file[0] = 0;
 733     }
 734 
 735     fselect_on_select = on_select;
 736     marked_operation = MARKED_OP_NONE;
 737     gui_fselect_redraw = 2;
 738     gui_fselect_readdir = 0;
 739     gui_fselect_mode_old = gui_set_mode(&GUI_MODE_FSELECT_MODULE);
 740 }
 741 
 742 //-------------------------------------------------------------------
 743 void gui_fselect_draw(int enforce_redraw)
 744 {
 745     int i, j;
 746     twoColors cl_marked;
 747 
 748     if (gui_fselect_readdir)
 749     {
 750         gui_fselect_readdir = 0;
 751         gui_fselect_read_dir();
 752     }
 753 
 754     if (enforce_redraw)
 755         gui_fselect_redraw = 2;
 756 
 757     if (gui_fselect_redraw)
 758     {
 759         char dbuf[46];
 760 
 761         if (gui_fselect_redraw == 2)
 762         {
 763             // Title
 764             draw_string_justified(main_x, main_y, fselect_title, MAKE_COLOR(COLOR_BLACK, COLOR_WHITE), 0, main_w, TEXT_CENTER|TEXT_FILL);
 765 
 766             draw_rectangle(main_x-BORDER, main_y-BORDER, main_x+main_w+BORDER-1, main_y+MAIN_H+BORDER-1, MAKE_COLOR(COLOR_WHITE, COLOR_WHITE), RECT_BORDER2); //border frame
 767             draw_line(main_x, body_y-1, main_x+main_w-1, body_y-1, COLOR_WHITE); //border head-body
 768             draw_line(main_x, foot_y-1, main_x+main_w-1, foot_y-1, COLOR_WHITE); //border body-foot
 769         }
 770 
 771         int off_body_y = body_y;
 772 
 773         fitem *ptr;
 774         unsigned long sum_size = 0;
 775         for (i=0, ptr=top; i<BODY_LINES && ptr; ++i, ptr=ptr->next, off_body_y += FONT_HEIGHT)
 776         {
 777             cl_marked = MAKE_COLOR((ptr==selected)?COLOR_RED:COLOR_GREY, (ptr->marked)?COLOR_YELLOW:COLOR_WHITE);
 778 
 779             // print name
 780             j = strlen(ptr->name);
 781             strncpy(dbuf, ptr->name, NAME_SIZE);
 782             if (j > NAME_SIZE)
 783                 dbuf[NAME_SIZE-1] = '~'; // too long name
 784 
 785             if (ptr->isdir && ptr->isvalid)
 786             {
 787                 if (j < NAME_SIZE)
 788                 {
 789                     dbuf[j++] = '/';
 790                 }
 791                 else
 792                 {
 793                     dbuf[NAME_SIZE-2] = '~';
 794                     dbuf[NAME_SIZE-1] = '/';
 795                 }
 796             }
 797             for (; j < NAME_SIZE; j++)
 798                 dbuf[j] = ' ';
 799             j = NAME_SIZE;
 800             dbuf[j++] = 0x06;   // Vertical line
 801 
 802             // print size or <Dir>
 803             if (ptr->isdir)
 804             {
 805                 if (!ptr->isvalid)
 806                 {
 807                     sprintf(dbuf+j, "  ??? ");
 808                 }
 809                 else if (ptr->isparent)
 810                 {
 811                     sprintf(dbuf+j, " <Up> ");
 812                 }
 813                 else
 814                 {
 815                     sprintf(dbuf+j, " <Dir>");
 816                 }
 817             }
 818             else
 819             {
 820                 unsigned long n = ptr->size;
 821 
 822                 if (ptr->marked)
 823                   sum_size += n;
 824 
 825                 if (n < 1024)
 826                     sprintf(dbuf+j, "%5db", n); // " 1023 b"
 827                 else
 828                 {
 829                     static char* suffixes = "kMG";
 830                     int sfx = 0;
 831                     if (n >= 4294967245ul)    // 4GB - 51 - avoid overflow
 832                     {
 833                         sfx = 2;    // 'G' suffix
 834                         n = 4096;   // 4G
 835                     }
 836                     else
 837                     {
 838                         // Round to 1 decimal place (51 = 1024 * 0.05)
 839                         n += 51;
 840                         // Reduce and round until < 1M, incrementing size suffix index
 841                         while (n >= 1024*1024)
 842                         {
 843                             n >>= 10;
 844                             n += 51;
 845                             sfx += 1;
 846                         }
 847                     }
 848                     unsigned long f = ((n & 0x3FF) * 10) >> 10;    // 1 digit of remainder % 1024
 849                     sprintf(dbuf+j, "%3d.%1d%c", n >> 10, f, suffixes[sfx]);
 850                 }
 851             }
 852             j += SIZE_SIZE;
 853             dbuf[j++] = 0x06;   // Vertical line
 854 
 855             // print modification time
 856             if (ptr->mtime)
 857             {
 858                 struct tm *time = localtime(&(ptr->mtime));
 859                 sprintf(dbuf+j, "%02u.%02u'%02u %02u:%02u", time->tm_mday, time->tm_mon+1, (time->tm_year<100)?time->tm_year:time->tm_year-100, time->tm_hour, time->tm_min);
 860             }
 861             else
 862             {
 863                 sprintf(dbuf+j, "%14s", "");
 864             }
 865             j += TIME_SIZE;
 866             dbuf[j] = 0;
 867 
 868             draw_string_justified(main_x, off_body_y, dbuf, cl_marked, SPACING, main_w-SCROLLBAR, TEXT_LEFT|TEXT_FILL);
 869         }
 870 
 871         //fill the rest of body
 872         if (i>0 && i<BODY_LINES)
 873         {
 874             draw_rectangle(main_x, off_body_y, main_x+main_w-SCROLLBAR-1, body_y+BODY_FONT_LINES-1, MAKE_COLOR(COLOR_GREY, COLOR_GREY), RECT_BORDER0|DRAW_FILLED);
 875         }
 876 
 877         // scrollbar
 878         int off_sbar_x = main_x + main_w - SCROLLBAR;
 879         draw_rectangle(off_sbar_x, body_y, off_sbar_x+SCROLLBAR-1, body_y+BODY_FONT_LINES-1, MAKE_COLOR(COLOR_BLACK, COLOR_BLACK), RECT_BORDER0|DRAW_FILLED);
 880         if (items.count > BODY_LINES)
 881         {
 882             i = BODY_FONT_LINES - 1;
 883             j = (i * BODY_LINES) / items.count;
 884             if (j < 20) j = 20;
 885             i = ((i - j) * selected->n) / (items.count-1);
 886             draw_rectangle(off_sbar_x, body_y+i, off_sbar_x+SCROLLBAR-2, body_y+i+j, MAKE_COLOR(COLOR_WHITE, COLOR_WHITE), RECT_BORDER0|DRAW_FILLED);
 887         }
 888 
 889         //footer
 890         int max_footer_len = NAME_SIZE + SIZE_SIZE + SPACING;
 891         i = strlen(items.dir);
 892         if (i > max_footer_len)
 893         {
 894             strncpy(dbuf, items.dir+i-max_footer_len, max_footer_len);
 895             dbuf[0] = '.';
 896             dbuf[1] = '.';
 897         }
 898         else
 899         {
 900             strcpy(dbuf, items.dir);
 901         }
 902         draw_string_justified(main_x, foot_y, dbuf, MAKE_COLOR(COLOR_GREY, COLOR_WHITE), SPACING, main_w, TEXT_LEFT|TEXT_FILL);
 903 
 904         if (sum_size)
 905         {
 906             sprintf(dbuf, "%d b", sum_size); //selected size
 907         }
 908         else
 909         {
 910             unsigned int fr  = GetFreeCardSpaceKb();
 911             unsigned int tot = GetTotalCardSpaceKb();
 912             if (tot != 0)
 913                 tot = (fr * 100) / tot;
 914 
 915             if (fr < 1024*1024)
 916                 sprintf(dbuf, "%dM (%d%%)",    fr>>10, tot);
 917             else
 918                 sprintf(dbuf, "%d.%dG (%d%%)", fr>>20, ((fr&0x000FFFFF)*100)>>20, tot);
 919         }
 920         draw_string(main_x+main_w-strlen(dbuf)*FONT_WIDTH-BORDER, foot_y, dbuf, MAKE_COLOR(COLOR_GREY, COLOR_WHITE)); // free space
 921 
 922         gui_fselect_redraw = 0;
 923     }
 924 }
 925 
 926 //-------------------------------------------------------------------
 927 static void fselect_delete_file_cb(unsigned int btn)
 928 {
 929     if (btn==MBOX_BTN_YES)
 930     {
 931         started();
 932         delete_file(items.dir, selected->name);
 933         finished();
 934         gui_fselect_readdir = 1;
 935     }
 936     gui_fselect_redraw = 2;
 937 }
 938 
 939 // Find a JPG matching a given RAW name. If 'nested' > 0 search recursively in
 940 // child directories of 'folder.
 941 // Returns 1 if found, 0 if not found (or match is not a RAW file)
 942 static int find_jpg(const char *folder, const char *match, int nested)
 943 {
 944     DIR         *d;
 945     fs_dirent   de;
 946     int         rv = 0;
 947 
 948     // Open directory
 949     d = opendir_fselect(folder);
 950 
 951     if (d)
 952     {
 953         // Process contents
 954         while (fs_readdir(d, &de, folder) && !rv)
 955         {
 956             if (!de.deleted)
 957             {
 958                 // Sub directory? Process recursively (but only 'nested' levels deep)
 959                 if (de.isdir)
 960                 {
 961                     if (!de.isparent && !de.iscurrent && nested)
 962                     {
 963                         // Search sub-directory
 964                         char *path = malloc(strlen(folder) + strlen(de.de->d_name) + 2);
 965                         sprintf(path, "%s/%s", folder, de.de->d_name);
 966                         if (find_jpg(path, match, nested-1))
 967                             rv = 1;
 968                         free(path);
 969                     }
 970                 }
 971                 else
 972                 {
 973                     //If the four digits of the Canon number are the same AND file is JPG
 974                     if (is_jpg(de.de->d_name) && (strncmp(match+4, de.de->d_name+4, 4) == 0))
 975                         rv = 1;
 976                 }
 977             }
 978         }
 979         closedir(d);
 980     }
 981 
 982     return rv;
 983 }
 984 
 985 // If 'file' is a RAW file, scan its 'folder' for a JPG with the same
 986 // image number, if not found delete the RAW image file.
 987 static void purge_file(const char *folder, const char *file)
 988 {
 989     //If no JPG found, delete RAW file
 990     if (is_raw(file))
 991         if (!find_jpg(folder, file, 0))
 992             delete_file(folder, file);
 993 }
 994 
 995 // If 'file' is a RAW file, scan its 'folder' and all sibling folders for a JPG with the same
 996 // image number, if not found delete the RAW image file.
 997 // Used when 'Purge RAW' run on the A/DCIM directory. CHDK can store RAW files in a different
 998 // sub-directory of A/DCIM than the corresponding JPG.
 999 static void purge_file_DCIM(const char *folder, const char *file)
1000 {
1001     //If no JPG found, delete RAW file (search all sub-folders of A/DCIM for JPG)
1002     if (is_raw(file))
1003         if (!find_jpg("A/DCIM", file, 1))
1004             delete_file(folder, file);
1005 }
1006 
1007 static void fselect_purge_cb_DCIM(unsigned int btn)
1008 {
1009     if (btn == MBOX_BTN_YES)
1010     {
1011         //If selected folder is A/DCIM or A/RAW (this is to purge all RAW files in any sub-folder)
1012         process_dir(items.dir, selected->name, 1, purge_file_DCIM, 0);
1013     }
1014 }
1015 
1016 static void fselect_purge_cb_dir(unsigned int btn)
1017 {
1018     if (btn == MBOX_BTN_YES)
1019     {
1020         //If item is a Canon sub-folder of A/DCIM or A/RAW (this is to purge all RAW files inside a single Canon folder)
1021         process_dir(items.dir, selected->name, 0, purge_file, 0);
1022     }
1023 }
1024 
1025 static void fselect_purge_cb_file(unsigned int btn)
1026 {
1027     if (btn == MBOX_BTN_YES)
1028     {
1029         //Inside a Canon folder (files list)
1030         fitem *ptr, *ptr2;
1031 
1032         //Loop to find all the RAW files in the list
1033         for (ptr=items.head; ptr; ptr=ptr->next)
1034         {
1035             //If file is RAW (Either CRW/CR2 prefix or file extension) and is not marked
1036             if (is_raw(ptr->name) && !ptr->marked)
1037             {
1038                 // Flag for checking if matching JPG exists
1039                 int found = 0;
1040 
1041                 //Loop to find a corresponding JPG file in the list
1042                 for (ptr2=items.head; ptr2; ptr2=ptr2->next)
1043                 {
1044                     //If this is a JPG and the four digits of the Canon number are the same
1045                     if (is_jpg(ptr2->name) && (strncmp(ptr->name+4, ptr2->name+4, 4) == 0))
1046                     {
1047                         found=1;
1048                         break;
1049                     }
1050                 }
1051 
1052                 //If no JPG found, delete RAW file
1053                 if (found == 0)
1054                     delete_file(items.dir, ptr->name);
1055             }
1056         }
1057         gui_fselect_readdir = 1;
1058     }
1059     gui_fselect_redraw = 2;
1060 }
1061 
1062 
1063 //-------------------------------------------------------------------
1064 static void fselect_delete_folder_cb(unsigned int btn)
1065 {
1066     if (btn==MBOX_BTN_YES)
1067     {
1068         process_dir(items.dir, selected->name, 999, delete_file, delete_dir);
1069         gui_fselect_readdir = 1;
1070     }
1071     gui_fselect_redraw = 2;
1072 }
1073 
1074 static void confirm_delete_directory()
1075 {
1076     if (selected->isdir && !selected->isparent)
1077         gui_mbox_init(LANG_BROWSER_ERASE_DIR_TITLE, LANG_BROWSER_ERASE_DIR_TEXT,
1078                       MBOX_TEXT_CENTER|MBOX_BTN_YES_NO|MBOX_DEF_BTN2, fselect_delete_folder_cb);
1079 }
1080 
1081 //-------------------------------------------------------------------
1082 static void fselect_marked_toggle()
1083 {
1084     if (selected && selected->isvalid && !selected->isdir)
1085     {
1086         selected->marked = !selected->marked;
1087     }
1088 }
1089 
1090 //-------------------------------------------------------------------
1091 static void gui_fselect_marked_free_data()
1092 {
1093     free_list(&marked_items);
1094     marked_operation = MARKED_OP_NONE;
1095 }
1096 
1097 //-------------------------------------------------------------------
1098 static void fselect_marked_copy_list()
1099 {
1100     gui_fselect_marked_free_data();
1101 
1102     fitem *ptr;
1103 
1104     for (ptr=items.head; ptr; ptr=ptr->next)
1105         if (ptr->marked)
1106             add_item(&marked_items, ptr->name, ptr->size, ptr->mtime, 1, ptr->isdir, ptr->isparent, ptr->isvalid);
1107 
1108     if (!marked_items.count)
1109         if (selected && selected->isvalid && !selected->isdir)
1110             add_item(&marked_items, selected->name, selected->size, selected->mtime, 1, selected->isdir, selected->isparent, selected->isvalid);
1111 
1112     strcpy(marked_items.dir, items.dir);
1113 }
1114 
1115 //-------------------------------------------------------------------
1116 static void fselect_marked_paste_cb(unsigned int btn)
1117 {
1118     fitem *ptr;
1119     int i = 0;
1120 
1121     if (btn != MBOX_BTN_YES) return;
1122 
1123     if (strcmp(marked_items.dir, items.dir) != 0)
1124     {
1125         for (ptr=marked_items.head; ptr; ptr=ptr->next)
1126         {
1127             if (ptr->isvalid && !ptr->isdir)
1128             {
1129                 started();
1130 
1131                 ++i;
1132                 if (marked_items.count)
1133                     gui_browser_progress_show(lang_str(LANG_FSELECT_PROGRESS_TITLE),i*100/marked_items.count);
1134 
1135                 int copied = copy_file(marked_items.dir, ptr->name, items.dir, ptr->name, 0);
1136 
1137                 if (copied && (marked_operation == MARKED_OP_CUT))
1138                 {
1139                     delete_file(marked_items.dir, ptr->name);
1140                 }
1141 
1142                 finished();
1143             }
1144         }
1145         if (marked_operation == MARKED_OP_CUT)
1146         {
1147             gui_fselect_marked_free_data();
1148         }
1149         gui_fselect_readdir = 1;
1150     }
1151     gui_fselect_redraw = 2;
1152 }
1153 
1154 //-------------------------------------------------------------------
1155 static inline unsigned int fselect_real_marked_count()
1156 {
1157     fitem  *ptr;
1158     register unsigned int cnt = 0;
1159 
1160     for (ptr=items.head; ptr; ptr=ptr->next)
1161     {
1162         if (ptr->isvalid && !ptr->isdir && ptr->marked)
1163             ++cnt;
1164     }
1165     return cnt;
1166 }
1167 
1168 //-------------------------------------------------------------------
1169 static unsigned int fselect_marked_count()
1170 {
1171     register unsigned int cnt = fselect_real_marked_count();
1172 
1173     if (!cnt)
1174     {
1175         if (selected && selected->isvalid && !selected->isdir)
1176             ++cnt;
1177     }
1178 
1179     return cnt;
1180 }
1181 
1182 //-------------------------------------------------------------------
1183 static void fselect_marked_delete_cb(unsigned int btn)
1184 {
1185     fitem  *ptr;
1186     unsigned int del_cnt=0, cnt;
1187 
1188     if (btn != MBOX_BTN_YES) return;
1189 
1190     cnt = fselect_marked_count();
1191     for (ptr=items.head; ptr; ptr=ptr->next)
1192         if (ptr->marked && ptr->isvalid && !ptr->isdir)
1193         {
1194             started();
1195             ++del_cnt;
1196             if (cnt)
1197                 gui_browser_progress_show(lang_str(LANG_FSELECT_PROGRESS_TITLE),del_cnt*100/cnt);
1198             delete_file(items.dir, ptr->name);
1199             finished();
1200         }
1201 
1202     if (del_cnt == 0 && selected)
1203     {
1204         started();
1205         delete_file(items.dir, selected->name);
1206         finished();
1207     }
1208     gui_fselect_readdir = 1;
1209     gui_fselect_redraw = 2;
1210 }
1211 
1212 //-------------------------------------------------------------------
1213 static void fselect_chdk_replace_cb(unsigned int btn)
1214 {
1215     if (btn == MBOX_BTN_YES)
1216     {
1217         copy_file(items.dir, selected->name, "A", "DISKBOOT.BIN", 1);
1218         gui_browser_progress_show("Please reboot",100);
1219     }
1220 }
1221 
1222 //-------------------------------------------------------------------
1223 static void fselect_marked_inverse_selection()
1224 {
1225     fitem  *ptr;
1226 
1227     for (ptr=items.head; ptr; ptr=ptr->next)
1228         if (ptr->isvalid && !ptr->isdir)
1229             ptr->marked = !ptr->marked;
1230 
1231     gui_fselect_redraw = 2;
1232 }
1233 
1234 //-------------------------------------------------------------------
1235 void process_raw_files(void)
1236 {
1237     fitem *ptr;
1238 
1239     if ((fselect_marked_count()>1) && librawop->raw_merge_start(raw_operation))
1240     {
1241         for (ptr=items.head; ptr; ptr=ptr->next)
1242             if (ptr->marked && ptr->isvalid && !ptr->isdir)
1243             {
1244                 sprintf(selected_file, "%s/%s", items.dir, ptr->name);
1245                 librawop->raw_merge_add_file(selected_file);
1246             }
1247         librawop->raw_merge_end();
1248         gui_fselect_readdir = 1;
1249         gui_fselect_redraw = 2;
1250     }
1251 }
1252 
1253 static void fselect_subtract_cb(unsigned int btn)
1254 {
1255     fitem *ptr;
1256     if (btn != MBOX_BTN_YES) return;
1257 
1258     for (ptr=items.head; ptr; ptr=ptr->next)
1259     {
1260         if (ptr->marked && ptr->isvalid && !ptr->isdir && chk_name(ptr->name,selected->name))
1261         {
1262             librawop->raw_subtract(ptr->name, items.dir, selected->name, items.dir);
1263         }
1264     }
1265     gui_fselect_readdir = 1;
1266     gui_fselect_redraw = 2;
1267 }
1268 
1269 #define MAX_SUB_NAMES 6
1270 static void setup_batch_subtract(void)
1271 {
1272     fitem *ptr;
1273     int i;
1274     char *p = buf + sprintf(buf,"%s %s\n",selected->name,lang_str(LANG_FSELECT_SUB_FROM));
1275     for (ptr=items.head, i=0; ptr; ptr=ptr->next)
1276     {
1277         if (ptr->marked && ptr->isvalid && !ptr->isdir && (ptr->size >= camera_sensor.raw_size))
1278         {
1279             if ( i < MAX_SUB_NAMES )
1280             {
1281                 sprintf(p, "%s\n",ptr->name);
1282                 // keep a pointer to the one before the end, so we can stick ...and more on
1283                 if (i < MAX_SUB_NAMES - 1)
1284                 {
1285                     p += strlen(p);
1286                 }
1287             }
1288             i++;
1289         }
1290     }
1291     if (i > MAX_SUB_NAMES)
1292     {
1293 //      "...%d more files"
1294         sprintf(p,lang_str(LANG_FSELECT_SUB_AND_MORE),i - (MAX_SUB_NAMES - 1));
1295     }
1296     gui_mbox_init(LANG_FSELECT_SUBTRACT, (int)buf,
1297                   MBOX_TEXT_CENTER|MBOX_BTN_YES_NO|MBOX_DEF_BTN2, fselect_subtract_cb);
1298 }
1299 //-------------------------------------------------------------------
1300 void process_dng_to_raw_files(void)
1301 {
1302     fitem *ptr;
1303     int i=0;
1304     started();
1305     msleep(100);
1306     finished();
1307 
1308     if (fselect_real_marked_count())
1309     {
1310         for (ptr=items.head; ptr; ptr=ptr->next)
1311             if (ptr->marked && ptr->isvalid && !ptr->isdir)
1312             {
1313                 sprintf(selected_file, "%s/%s", items.dir, ptr->name);
1314                 gui_browser_progress_show(selected_file, (i++)*100/fselect_real_marked_count()) ;
1315                 libdng->convert_dng_to_chdk_raw(selected_file);
1316             }
1317     }
1318     else
1319     {
1320         sprintf(selected_file, "%s/%s", items.dir, selected->name);
1321         libdng->convert_dng_to_chdk_raw(selected_file);
1322     }
1323     gui_fselect_readdir = 1;
1324 }
1325 
1326 static void fselect_mpopup_rawop_cb(unsigned int actn)
1327 {
1328     switch (actn) {
1329         case MPOPUP_RAW_AVERAGE:
1330             raw_operation=RAW_OPERATION_AVERAGE;
1331             process_raw_files();
1332             break;
1333         case MPOPUP_RAW_ADD:
1334             raw_operation=RAW_OPERATION_SUM;
1335             process_raw_files();
1336             break;
1337         case MPOPUP_RAW_DEVELOP:
1338             sprintf(buf, "%s/%s", items.dir, selected->name);
1339             raw_prepare_develop(buf, 1);
1340             break;
1341         case MPOPUP_SUBTRACT:
1342             setup_batch_subtract();
1343             break;
1344         case MPOPUP_DNG_TO_CRW:
1345             process_dng_to_raw_files();
1346             break;
1347     }
1348 }
1349 
1350 static void mkdir_cb(const char* name)
1351 {
1352     if (name)
1353     {
1354         sprintf(selected_file,"%s/%s", items.dir, name);
1355         mkdir(selected_file);
1356         gui_fselect_readdir = 1;
1357         gui_fselect_redraw = 2;
1358     }
1359 }
1360 
1361 static void rename_cb(const char* name)
1362 {
1363     if (name)
1364     {
1365         sprintf(selected_file, "%s/%s", items.dir, selected->name);
1366         sprintf(buf, "%s/%s", items.dir, name);
1367         rename(selected_file, buf);
1368         gui_fselect_readdir = 1;
1369         gui_fselect_redraw = 2;
1370     }
1371 }
1372 
1373 static int mpopup_rawop_flag;
1374 
1375 //-------------------------------------------------------------------
1376 // Return 1 if A/DCIM or A/RAW selected, 0 otherwise
1377 static int isPurgeDCIM()
1378 {
1379     return (chk_name(items.dir, "A") && (chk_name(selected->name, "DCIM") || chk_name(selected->name, "RAW")));
1380 }
1381 
1382 // Return 1 if sub-directory of A/DCIM or A/RAW selected
1383 static int isPurgeDir()
1384 {
1385     return (selected->isdir && !selected->isparent && ((chk_name(items.dir, "A/DCIM")) || (chk_name(items.dir, "A/RAW"))));
1386 }
1387 
1388 static void fselect_mpopup_cb(unsigned int actn)
1389 {
1390     switch (actn)
1391     {
1392         case MPOPUP_CUT:
1393             fselect_marked_copy_list();
1394             marked_operation = MARKED_OP_CUT;
1395             break;
1396         case MPOPUP_COPY:
1397             fselect_marked_copy_list();
1398             marked_operation = MARKED_OP_COPY;
1399             break;
1400         case MPOPUP_PASTE:
1401             sprintf(buf, lang_str((marked_operation == MARKED_OP_CUT)?LANG_FSELECT_CUT_TEXT:LANG_FSELECT_COPY_TEXT), marked_items.count, marked_items.dir);
1402             gui_mbox_init((marked_operation == MARKED_OP_CUT)?LANG_FSELECT_CUT_TITLE:LANG_FSELECT_COPY_TITLE,
1403                           (int)buf, MBOX_TEXT_CENTER|MBOX_BTN_YES_NO|MBOX_DEF_BTN2, fselect_marked_paste_cb);
1404             break;
1405         case MPOPUP_DELETE:
1406             sprintf(buf, lang_str(LANG_FSELECT_DELETE_TEXT), fselect_marked_count());
1407             gui_mbox_init(LANG_FSELECT_DELETE_TITLE, (int)buf,
1408                           MBOX_TEXT_CENTER|MBOX_BTN_YES_NO|MBOX_DEF_BTN2, fselect_marked_delete_cb);
1409             break;
1410         case MPOPUP_RMDIR:
1411             confirm_delete_directory();
1412             break;
1413         case MPOPUP_MKDIR:
1414             libtextbox->textbox_init(LANG_POPUP_MKDIR, LANG_PROMPT_MKDIR, "", 15, mkdir_cb, 0);
1415             break;
1416         case MPOPUP_RENAME:
1417             libtextbox->textbox_init(LANG_POPUP_RENAME, LANG_PROMPT_RENAME, selected->name, 15, rename_cb, 0);
1418             break;
1419         case MPOPUP_PURGE_DCIM:
1420             //If selected folder is A/DCIM or A/RAW (this is to purge all RAW files in any sub-folder)
1421             gui_mbox_init(LANG_FSELECT_PURGE_TITLE, LANG_FSELECT_PURGE_DCIM_TEXT, MBOX_TEXT_CENTER|MBOX_BTN_YES_NO|MBOX_DEF_BTN2, fselect_purge_cb_DCIM);
1422             break;
1423         case MPOPUP_PURGE_DIR:
1424             //If selected item is a Canon folder
1425             gui_mbox_init(LANG_FSELECT_PURGE_TITLE, LANG_FSELECT_PURGE_CANON_FOLDER_TEXT, MBOX_TEXT_CENTER|MBOX_BTN_YES_NO|MBOX_DEF_BTN2, fselect_purge_cb_dir);
1426             break;
1427         case MPOPUP_PURGE_FILE:
1428             //If selected item is a file produced by the camera
1429             gui_mbox_init(LANG_FSELECT_PURGE_TITLE, LANG_FSELECT_PURGE_LIST_TEXT, MBOX_TEXT_CENTER|MBOX_BTN_YES_NO|MBOX_DEF_BTN2, fselect_purge_cb_file);
1430             break;
1431         case MPOPUP_SELINV:
1432             fselect_marked_inverse_selection();
1433             break;
1434         case MPOPUP_CANCEL:
1435             break;
1436 
1437         case MPOPUP_RAWOPS:
1438             libmpopup->show_popup( popup_rawop, mpopup_rawop_flag, fselect_mpopup_rawop_cb);
1439             break;
1440 
1441         case MPOPUP_CHDK_REPLACE:
1442             gui_mbox_init((int)"Replacing CHDK", (int)"Do you want to replace current CHDK with this file",
1443                           MBOX_TEXT_CENTER|MBOX_BTN_YES_NO|MBOX_DEF_BTN2, fselect_chdk_replace_cb);
1444             break;
1445         case MPOPUP_EDITOR:
1446             gui_mbox_init((int)"Editor", (int)"edit", MBOX_BTN_OK|MBOX_TEXT_CENTER, NULL);
1447             break;
1448     }
1449     gui_fselect_redraw = 2;
1450 }
1451 
1452 //-------------------------------------------------------------------
1453 void finalize_fselect()
1454 {
1455     gui_fselect_free_data();
1456     gui_fselect_marked_free_data();
1457 }
1458 
1459 static void exit_fselect(char* file)
1460 {
1461     finalize_fselect();
1462 
1463     gui_set_mode(gui_fselect_mode_old);
1464 
1465     // call callback if set. file will be null if canceled (menu or leaving alt)
1466     if (fselect_on_select)
1467     {
1468         fselect_on_select(file);
1469         // if called mode will return control to filemanager - we need to redraw it
1470         gui_fselect_redraw = 2;
1471     }
1472 
1473     running = 0;
1474 }
1475 
1476 //-------------------------------------------------------------------
1477 int gui_fselect_kbd_process()
1478 {
1479     int i;
1480     int do_exit = 0;
1481 
1482     switch (kbd_get_autoclicked_key() | get_jogdial_direction())
1483     {
1484         case JOGDIAL_LEFT:
1485         case KEY_UP:
1486             if (selected)
1487             {
1488                 if (camera_info.state.is_shutter_half_press) fselect_goto_prev(4);
1489                 else fselect_goto_prev(1);
1490                 gui_fselect_redraw = 1;
1491             }
1492             break;
1493         case KEY_DOWN:
1494         case JOGDIAL_RIGHT:
1495             if (selected)
1496             {
1497                 if (camera_info.state.is_shutter_half_press) fselect_goto_next(4);
1498                 else fselect_goto_next(1);
1499                 gui_fselect_redraw = 1;
1500             }
1501             break;
1502         case KEY_ZOOM_OUT:
1503             if (selected)
1504             {
1505                 fselect_goto_prev(BODY_LINES-1);
1506                 gui_fselect_redraw = 1;
1507             }
1508             break;
1509         case KEY_ZOOM_IN:
1510             if (selected)
1511             {
1512                 fselect_goto_next(BODY_LINES-1);
1513                 gui_fselect_redraw = 1;
1514             }
1515             break;
1516         case KEY_RIGHT:
1517             if (selected)
1518             {
1519                 fselect_marked_toggle();
1520                 fselect_goto_next(1);
1521                 gui_fselect_redraw = 1;
1522             }
1523             break;
1524         case KEY_LEFT:
1525             if (selected && selected->isvalid)
1526             {
1527                 int marked_count = fselect_marked_count();
1528 
1529                 i = MPOPUP_SELINV|MPOPUP_MKDIR;
1530                 mpopup_rawop_flag = 0;
1531 
1532                 if (marked_count > 0)
1533                 {
1534                     i |= MPOPUP_CUT|MPOPUP_COPY|MPOPUP_DELETE;
1535                     if (marked_count > 1)
1536                         mpopup_rawop_flag |= MPOPUP_RAW_ADD|MPOPUP_RAW_AVERAGE;
1537                     // doesn't make sense to subtract from itself!
1538                     if (selected->marked == 0 && fselect_real_marked_count() > 0)
1539                         mpopup_rawop_flag |= MPOPUP_SUBTRACT;
1540                 }
1541 
1542                 if (marked_operation != MARKED_OP_NONE)
1543                     i |= MPOPUP_PASTE;
1544 
1545                 //Check if 'Purge RAW' applies
1546                 if (isPurgeDCIM())              // DCIM or RAW selected in A
1547                     i |= MPOPUP_PURGE_DCIM;
1548                 if (isPurgeDir())               // sub-dir selected in A/DCIM or A/RAW
1549                     i |= MPOPUP_PURGE_DIR;
1550                 if (is_raw(selected->name))     // raw file selected
1551                     i |= MPOPUP_PURGE_FILE;
1552 
1553                 if (selected->isdir && !selected->isparent)
1554                     i |= MPOPUP_RMDIR;
1555 
1556                 if (!selected->isparent) //If item is not UpDir
1557                     i |= MPOPUP_RENAME;
1558 
1559                 if (selected->size >= camera_sensor.raw_size)
1560                     mpopup_rawop_flag |= MPOPUP_RAW_DEVELOP;
1561 
1562                 if ((marked_count > 1) || (selected->size > camera_sensor.raw_size))
1563                     mpopup_rawop_flag |= MPOPUP_DNG_TO_CRW;
1564 
1565                 if (chk_ext(selected->name, "bin")) //If item is *.bin file
1566                     i |= MPOPUP_CHDK_REPLACE;
1567 
1568                 if (mpopup_rawop_flag)
1569                     i |= MPOPUP_RAWOPS;
1570 
1571                 libmpopup->show_popup( popup, i, fselect_mpopup_cb);
1572             }
1573             break;
1574         case KEY_SET:
1575             if (selected && selected->isvalid && gui_fselect_redraw==0)
1576             {
1577                 if (selected->isdir)
1578                 {
1579                     if (selected->isparent)
1580                     {
1581                         char *s = strrchr(items.dir, '/');
1582                         if (s) *s = 0;
1583                     }
1584                     else
1585                     {
1586                         sprintf(items.dir+strlen(items.dir), "/%s", selected->name);
1587                     }
1588                     gui_fselect_readdir = 1;
1589                     gui_fselect_redraw = 1;
1590                 }
1591                 else
1592                 {
1593                     sprintf(selected_file, "%s/%s", items.dir, selected->name);
1594 
1595                     char *ext = strrchr(selected->name,'.');
1596                     do_exit = 1;
1597 
1598                     if (!fselect_on_select)
1599                     {
1600                         if (chk_ext(ext,"txt") || chk_ext(ext,"log") || chk_ext(ext,"csv"))
1601                         {
1602                             fselect_on_select = (void (*)(const char*))libtxtread->read_file;
1603                         }
1604                         else if (chk_ext(ext,"flt"))
1605                         {
1606                             fselect_on_select = (void (*)(const char*))module_run;
1607                         }
1608                     }
1609                 }
1610             }
1611             break;
1612         case KEY_ERASE:
1613         case KEY_DISPLAY:
1614             if (selected && selected->isvalid)
1615             {
1616                 if (selected->isdir)
1617                 {
1618                     confirm_delete_directory();
1619                 } else
1620                 {
1621                     gui_mbox_init(LANG_BROWSER_DELETE_FILE_TITLE, LANG_BROWSER_DELETE_FILE_TEXT,
1622                                   MBOX_TEXT_CENTER|MBOX_BTN_YES_NO|MBOX_DEF_BTN2, fselect_delete_file_cb);
1623                 }
1624             }
1625             break;
1626     }
1627 
1628     if (do_exit)
1629         exit_fselect(selected_file);
1630 
1631     return 0;
1632 }
1633 
1634 void gui_fselect_kbd_process_menu_btn()
1635 {
1636     // just free resource. callback called with NULL ptr
1637     exit_fselect(0);
1638 }
1639 
1640 // =========  MODULE INIT =================
1641 
1642 /***************** BEGIN OF AUXILARY PART *********************
1643   ATTENTION: DO NOT REMOVE OR CHANGE SIGNATURES IN THIS SECTION
1644  **************************************************************/
1645 
1646 //---------------------------------------------------------
1647 // PURPOSE: Finalize module operations (close allocs, etc)
1648 // RETURN VALUE: 0-ok, 1-fail
1649 //---------------------------------------------------------
1650 int _module_unloader()
1651 {
1652     // Free file copy buffer if allocated
1653     if (ubuf)
1654     {
1655         ufree(ubuf);
1656         ubuf = 0;
1657     }
1658     finalize_fselect();
1659     return 0;
1660 }
1661 
1662 int _module_can_unload()
1663 {
1664     return running == 0;
1665 }
1666 
1667 int _module_exit_alt()
1668 {
1669     exit_fselect(0);
1670     return 0;
1671 }
1672 
1673 /******************** Module Information structure ******************/
1674 
1675 libfselect_sym _libfselect =
1676 {
1677     {
1678         0, _module_unloader, _module_can_unload, _module_exit_alt, 0
1679     },
1680 
1681     gui_fselect_init
1682 };
1683 
1684 ModuleInfo _module_info = {
1685     MODULEINFO_V1_MAGICNUM,
1686     sizeof(ModuleInfo),
1687     GUI_FSELECT_VERSION,        // Module version
1688 
1689     ANY_CHDK_BRANCH, 0, OPT_ARCHITECTURE,         // Requirements of CHDK version
1690     ANY_PLATFORM_ALLOWED,       // Specify platform dependency
1691 
1692     -LANG_MENU_MISC_FILE_BROWSER,
1693     MTYPE_EXTENSION,
1694 
1695     &_libfselect.base,
1696 
1697     CONF_VERSION,               // CONF version
1698     CAM_SCREEN_VERSION,         // CAM SCREEN version
1699     CAM_SENSOR_VERSION,         // CAM SENSOR version
1700     CAM_INFO_VERSION,           // CAM INFO version
1701 
1702     0,
1703 };
1704 
1705 /*************** END OF AUXILARY PART *******************/

/* [<][>][^][v][top][bottom][index][help] */