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

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