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                     char c = 'k';
 828                     if (n >= 1024*1024*1024)        // GB
 829                     {
 830                         n = n >> 20;    // Note: can't round this up in case of overflow
 831                         c = 'G';
 832                     }
 833                     else if (n >= 1024*1024)        // MB
 834                     {
 835                         n = (n + 512) >> 10;
 836                         c = 'M';
 837                     }
 838                     unsigned long f = ((n & 0x3FF) * 10 + 512) >> 10;    // 1 digit of remainder % 1024
 839                     sprintf(dbuf+j, "%3d.%1d%c", n >> 10, f, c);
 840                 }
 841             }
 842             j += SIZE_SIZE;
 843             dbuf[j++] = 0x06;   // Vertical line
 844 
 845             // print modification time
 846             if (ptr->mtime)
 847             {
 848                 struct tm *time = localtime(&(ptr->mtime));
 849                 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);
 850             }
 851             else
 852             {
 853                 sprintf(dbuf+j, "%14s", "");
 854             }
 855             j += TIME_SIZE;
 856             dbuf[j] = 0;
 857 
 858             draw_string_justified(main_x, off_body_y, dbuf, cl_marked, SPACING, main_w-SCROLLBAR, TEXT_LEFT|TEXT_FILL);
 859         }
 860 
 861         //fill the rest of body
 862         if (i>0 && i<BODY_LINES)
 863         {
 864             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);
 865         }
 866 
 867         // scrollbar
 868         int off_sbar_x = main_x + main_w - SCROLLBAR;
 869         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);
 870         if (items.count > BODY_LINES)
 871         {
 872             i = BODY_FONT_LINES - 1;
 873             j = (i * BODY_LINES) / items.count;
 874             if (j < 20) j = 20;
 875             i = ((i - j) * selected->n) / (items.count-1);
 876             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);
 877         }
 878 
 879         //footer
 880         int max_footer_len = NAME_SIZE + SIZE_SIZE + SPACING;
 881         i = strlen(items.dir);
 882         if (i > max_footer_len)
 883         {
 884             strncpy(dbuf, items.dir+i-max_footer_len, max_footer_len);
 885             dbuf[0] = '.';
 886             dbuf[1] = '.';
 887         }
 888         else
 889         {
 890             strcpy(dbuf, items.dir);
 891         }
 892         draw_string_justified(main_x, foot_y, dbuf, MAKE_COLOR(COLOR_GREY, COLOR_WHITE), SPACING, main_w, TEXT_LEFT|TEXT_FILL);
 893 
 894         if (sum_size)
 895         {
 896             sprintf(dbuf, "%d b", sum_size); //selected size
 897         }
 898         else
 899         {
 900             unsigned int fr  = GetFreeCardSpaceKb();
 901             unsigned int tot = GetTotalCardSpaceKb();
 902             if (tot != 0)
 903                 tot = (fr * 100) / tot;
 904 
 905             if (fr < 1024*1024)
 906                 sprintf(dbuf, "%dM (%d%%)",    fr>>10, tot);
 907             else
 908                 sprintf(dbuf, "%d.%dG (%d%%)", fr>>20, ((fr&0x000FFFFF)*100)>>20, tot);
 909         }
 910         draw_string(main_x+main_w-strlen(dbuf)*FONT_WIDTH-BORDER, foot_y, dbuf, MAKE_COLOR(COLOR_GREY, COLOR_WHITE)); // free space
 911 
 912         gui_fselect_redraw = 0;
 913     }
 914 }
 915 
 916 //-------------------------------------------------------------------
 917 static void fselect_delete_file_cb(unsigned int btn)
 918 {
 919     if (btn==MBOX_BTN_YES)
 920     {
 921         started();
 922         delete_file(items.dir, selected->name);
 923         finished();
 924         gui_fselect_readdir = 1;
 925     }
 926     gui_fselect_redraw = 2;
 927 }
 928 
 929 // Find a JPG matching a given RAW name. If 'nested' > 0 search recursively in
 930 // child directories of 'folder.
 931 // Returns 1 if found, 0 if not found (or match is not a RAW file)
 932 static int find_jpg(const char *folder, const char *match, int nested)
 933 {
 934     DIR         *d;
 935     fs_dirent   de;
 936     int         rv = 0;
 937 
 938     // Open directory
 939     d = opendir_fselect(folder);
 940 
 941     if (d)
 942     {
 943         // Process contents
 944         while (fs_readdir(d, &de, folder) && !rv)
 945         {
 946             if (!de.deleted)
 947             {
 948                 // Sub directory? Process recursively (but only 'nested' levels deep)
 949                 if (de.isdir)
 950                 {
 951                     if (!de.isparent && !de.iscurrent && nested)
 952                     {
 953                         // Search sub-directory
 954                         char *path = malloc(strlen(folder) + strlen(de.de->d_name) + 2);
 955                         sprintf(path, "%s/%s", folder, de.de->d_name);
 956                         if (find_jpg(path, match, nested-1))
 957                             rv = 1;
 958                         free(path);
 959                     }
 960                 }
 961                 else
 962                 {
 963                     //If the four digits of the Canon number are the same AND file is JPG
 964                     if (is_jpg(de.de->d_name) && (strncmp(match+4, de.de->d_name+4, 4) == 0))
 965                         rv = 1;
 966                 }
 967             }
 968         }
 969         closedir(d);
 970     }
 971 
 972     return rv;
 973 }
 974 
 975 // If 'file' is a RAW file, scan its 'folder' for a JPG with the same
 976 // image number, if not found delete the RAW image file.
 977 static void purge_file(const char *folder, const char *file)
 978 {
 979     //If no JPG found, delete RAW file
 980     if (is_raw(file))
 981         if (!find_jpg(folder, file, 0))
 982             delete_file(folder, file);
 983 }
 984 
 985 // If 'file' is a RAW file, scan its 'folder' and all sibling folders for a JPG with the same
 986 // image number, if not found delete the RAW image file.
 987 // Used when 'Purge RAW' run on the A/DCIM directory. CHDK can store RAW files in a different
 988 // sub-directory of A/DCIM than the corresponding JPG.
 989 static void purge_file_DCIM(const char *folder, const char *file)
 990 {
 991     //If no JPG found, delete RAW file (search all sub-folders of A/DCIM for JPG)
 992     if (is_raw(file))
 993         if (!find_jpg("A/DCIM", file, 1))
 994             delete_file(folder, file);
 995 }
 996 
 997 static void fselect_purge_cb_DCIM(unsigned int btn)
 998 {
 999     if (btn == MBOX_BTN_YES)
1000     {
1001         //If selected folder is A/DCIM or A/RAW (this is to purge all RAW files in any sub-folder)
1002         process_dir(items.dir, selected->name, 1, purge_file_DCIM, 0);
1003     }
1004 }
1005 
1006 static void fselect_purge_cb_dir(unsigned int btn)
1007 {
1008     if (btn == MBOX_BTN_YES)
1009     {
1010         //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)
1011         process_dir(items.dir, selected->name, 0, purge_file, 0);
1012     }
1013 }
1014 
1015 static void fselect_purge_cb_file(unsigned int btn)
1016 {
1017     if (btn == MBOX_BTN_YES)
1018     {
1019         //Inside a Canon folder (files list)
1020         fitem *ptr, *ptr2;
1021 
1022         //Loop to find all the RAW files in the list
1023         for (ptr=items.head; ptr; ptr=ptr->next)
1024         {
1025             //If file is RAW (Either CRW/CR2 prefix or file extension) and is not marked
1026             if (is_raw(ptr->name) && !ptr->marked)
1027             {
1028                 // Flag for checking if matching JPG exists
1029                 int found = 0;
1030 
1031                 //Loop to find a corresponding JPG file in the list
1032                 for (ptr2=items.head; ptr2; ptr2=ptr2->next)
1033                 {
1034                     //If this is a JPG and the four digits of the Canon number are the same
1035                     if (is_jpg(ptr2->name) && (strncmp(ptr->name+4, ptr2->name+4, 4) == 0))
1036                     {
1037                         found=1;
1038                         break;
1039                     }
1040                 }
1041 
1042                 //If no JPG found, delete RAW file
1043                 if (found == 0)
1044                     delete_file(items.dir, ptr->name);
1045             }
1046         }
1047         gui_fselect_readdir = 1;
1048     }
1049     gui_fselect_redraw = 2;
1050 }
1051 
1052 
1053 //-------------------------------------------------------------------
1054 static void fselect_delete_folder_cb(unsigned int btn)
1055 {
1056     if (btn==MBOX_BTN_YES)
1057     {
1058         process_dir(items.dir, selected->name, 999, delete_file, delete_dir);
1059         gui_fselect_readdir = 1;
1060     }
1061     gui_fselect_redraw = 2;
1062 }
1063 
1064 static void confirm_delete_directory()
1065 {
1066     if (selected->isdir && !selected->isparent)
1067         gui_mbox_init(LANG_BROWSER_ERASE_DIR_TITLE, LANG_BROWSER_ERASE_DIR_TEXT,
1068                       MBOX_TEXT_CENTER|MBOX_BTN_YES_NO|MBOX_DEF_BTN2, fselect_delete_folder_cb);
1069 }
1070 
1071 //-------------------------------------------------------------------
1072 static void fselect_marked_toggle()
1073 {
1074     if (selected && selected->isvalid && !selected->isdir)
1075     {
1076         selected->marked = !selected->marked;
1077     }
1078 }
1079 
1080 //-------------------------------------------------------------------
1081 static void gui_fselect_marked_free_data()
1082 {
1083     free_list(&marked_items);
1084     marked_operation = MARKED_OP_NONE;
1085 }
1086 
1087 //-------------------------------------------------------------------
1088 static void fselect_marked_copy_list()
1089 {
1090     gui_fselect_marked_free_data();
1091 
1092     fitem *ptr;
1093 
1094     for (ptr=items.head; ptr; ptr=ptr->next)
1095         if (ptr->marked)
1096             add_item(&marked_items, ptr->name, ptr->size, ptr->mtime, 1, ptr->isdir, ptr->isparent, ptr->isvalid);
1097 
1098     if (!marked_items.count)
1099         if (selected && selected->isvalid && !selected->isdir)
1100             add_item(&marked_items, selected->name, selected->size, selected->mtime, 1, selected->isdir, selected->isparent, selected->isvalid);
1101 
1102     strcpy(marked_items.dir, items.dir);
1103 }
1104 
1105 //-------------------------------------------------------------------
1106 static void fselect_marked_paste_cb(unsigned int btn)
1107 {
1108     fitem *ptr;
1109     int i = 0;
1110 
1111     if (btn != MBOX_BTN_YES) return;
1112 
1113     if (strcmp(marked_items.dir, items.dir) != 0)
1114     {
1115         for (ptr=marked_items.head; ptr; ptr=ptr->next)
1116         {
1117             if (ptr->isvalid && !ptr->isdir)
1118             {
1119                 started();
1120 
1121                 ++i;
1122                 if (marked_items.count)
1123                     gui_browser_progress_show(lang_str(LANG_FSELECT_PROGRESS_TITLE),i*100/marked_items.count);
1124 
1125                 int copied = copy_file(marked_items.dir, ptr->name, items.dir, ptr->name, 0);
1126 
1127                 if (copied && (marked_operation == MARKED_OP_CUT))
1128                 {
1129                     delete_file(marked_items.dir, ptr->name);
1130                 }
1131 
1132                 finished();
1133             }
1134         }
1135         if (marked_operation == MARKED_OP_CUT)
1136         {
1137             gui_fselect_marked_free_data();
1138         }
1139         gui_fselect_readdir = 1;
1140     }
1141     gui_fselect_redraw = 2;
1142 }
1143 
1144 //-------------------------------------------------------------------
1145 static inline unsigned int fselect_real_marked_count()
1146 {
1147     fitem  *ptr;
1148     register unsigned int cnt = 0;
1149 
1150     for (ptr=items.head; ptr; ptr=ptr->next)
1151     {
1152         if (ptr->isvalid && !ptr->isdir && ptr->marked)
1153             ++cnt;
1154     }
1155     return cnt;
1156 }
1157 
1158 //-------------------------------------------------------------------
1159 static unsigned int fselect_marked_count()
1160 {
1161     register unsigned int cnt = fselect_real_marked_count();
1162 
1163     if (!cnt)
1164     {
1165         if (selected && selected->isvalid && !selected->isdir)
1166             ++cnt;
1167     }
1168 
1169     return cnt;
1170 }
1171 
1172 //-------------------------------------------------------------------
1173 static void fselect_marked_delete_cb(unsigned int btn)
1174 {
1175     fitem  *ptr;
1176     unsigned int del_cnt=0, cnt;
1177 
1178     if (btn != MBOX_BTN_YES) return;
1179 
1180     cnt = fselect_marked_count();
1181     for (ptr=items.head; ptr; ptr=ptr->next)
1182         if (ptr->marked && ptr->isvalid && !ptr->isdir)
1183         {
1184             started();
1185             ++del_cnt;
1186             if (cnt)
1187                 gui_browser_progress_show(lang_str(LANG_FSELECT_PROGRESS_TITLE),del_cnt*100/cnt);
1188             delete_file(items.dir, ptr->name);
1189             finished();
1190         }
1191 
1192     if (del_cnt == 0 && selected)
1193     {
1194         started();
1195         delete_file(items.dir, selected->name);
1196         finished();
1197     }
1198     gui_fselect_readdir = 1;
1199     gui_fselect_redraw = 2;
1200 }
1201 
1202 //-------------------------------------------------------------------
1203 static void fselect_chdk_replace_cb(unsigned int btn)
1204 {
1205     if (btn == MBOX_BTN_YES)
1206     {
1207         copy_file(items.dir, selected->name, "A", "DISKBOOT.BIN", 1);
1208         gui_browser_progress_show("Please reboot",100);
1209     }
1210 }
1211 
1212 //-------------------------------------------------------------------
1213 static void fselect_marked_inverse_selection()
1214 {
1215     fitem  *ptr;
1216 
1217     for (ptr=items.head; ptr; ptr=ptr->next)
1218         if (ptr->isvalid && !ptr->isdir)
1219             ptr->marked = !ptr->marked;
1220 
1221     gui_fselect_redraw = 2;
1222 }
1223 
1224 //-------------------------------------------------------------------
1225 void process_raw_files(void)
1226 {
1227     fitem *ptr;
1228 
1229     if ((fselect_marked_count()>1) && librawop->raw_merge_start(raw_operation))
1230     {
1231         for (ptr=items.head; ptr; ptr=ptr->next)
1232             if (ptr->marked && ptr->isvalid && !ptr->isdir)
1233             {
1234                 sprintf(selected_file, "%s/%s", items.dir, ptr->name);
1235                 librawop->raw_merge_add_file(selected_file);
1236             }
1237         librawop->raw_merge_end();
1238         gui_fselect_readdir = 1;
1239         gui_fselect_redraw = 2;
1240     }
1241 }
1242 
1243 static void fselect_subtract_cb(unsigned int btn)
1244 {
1245     fitem *ptr;
1246     if (btn != MBOX_BTN_YES) return;
1247 
1248     for (ptr=items.head; ptr; ptr=ptr->next)
1249     {
1250         if (ptr->marked && ptr->isvalid && !ptr->isdir && chk_name(ptr->name,selected->name))
1251         {
1252             librawop->raw_subtract(ptr->name, items.dir, selected->name, items.dir);
1253         }
1254     }
1255     gui_fselect_readdir = 1;
1256     gui_fselect_redraw = 2;
1257 }
1258 
1259 #define MAX_SUB_NAMES 6
1260 static void setup_batch_subtract(void)
1261 {
1262     fitem *ptr;
1263     int i;
1264     char *p = buf + sprintf(buf,"%s %s\n",selected->name,lang_str(LANG_FSELECT_SUB_FROM));
1265     for (ptr=items.head, i=0; ptr; ptr=ptr->next)
1266     {
1267         if (ptr->marked && ptr->isvalid && !ptr->isdir && (ptr->size >= camera_sensor.raw_size))
1268         {
1269             if ( i < MAX_SUB_NAMES )
1270             {
1271                 sprintf(p, "%s\n",ptr->name);
1272                 // keep a pointer to the one before the end, so we can stick ...and more on
1273                 if (i < MAX_SUB_NAMES - 1)
1274                 {
1275                     p += strlen(p);
1276                 }
1277             }
1278             i++;
1279         }
1280     }
1281     if (i > MAX_SUB_NAMES)
1282     {
1283 //      "...%d more files"
1284         sprintf(p,lang_str(LANG_FSELECT_SUB_AND_MORE),i - (MAX_SUB_NAMES - 1));
1285     }
1286     gui_mbox_init(LANG_FSELECT_SUBTRACT, (int)buf,
1287                   MBOX_TEXT_CENTER|MBOX_BTN_YES_NO|MBOX_DEF_BTN2, fselect_subtract_cb);
1288 }
1289 //-------------------------------------------------------------------
1290 void process_dng_to_raw_files(void)
1291 {
1292     fitem *ptr;
1293     int i=0;
1294     started();
1295     msleep(100);
1296     finished();
1297 
1298     if (fselect_real_marked_count())
1299     {
1300         for (ptr=items.head; ptr; ptr=ptr->next)
1301             if (ptr->marked && ptr->isvalid && !ptr->isdir)
1302             {
1303                 sprintf(selected_file, "%s/%s", items.dir, ptr->name);
1304                 gui_browser_progress_show(selected_file, (i++)*100/fselect_real_marked_count()) ;
1305                 libdng->convert_dng_to_chdk_raw(selected_file);
1306             }
1307     }
1308     else
1309     {
1310         sprintf(selected_file, "%s/%s", items.dir, selected->name);
1311         libdng->convert_dng_to_chdk_raw(selected_file);
1312     }
1313     gui_fselect_readdir = 1;
1314 }
1315 
1316 static void fselect_mpopup_rawop_cb(unsigned int actn)
1317 {
1318     switch (actn) {
1319         case MPOPUP_RAW_AVERAGE:
1320             raw_operation=RAW_OPERATION_AVERAGE;
1321             process_raw_files();
1322             break;
1323         case MPOPUP_RAW_ADD:
1324             raw_operation=RAW_OPERATION_SUM;
1325             process_raw_files();
1326             break;
1327         case MPOPUP_RAW_DEVELOP:
1328             sprintf(buf, "%s/%s", items.dir, selected->name);
1329             raw_prepare_develop(buf, 1);
1330             break;
1331         case MPOPUP_SUBTRACT:
1332             setup_batch_subtract();
1333             break;
1334         case MPOPUP_DNG_TO_CRW:
1335             process_dng_to_raw_files();
1336             break;
1337     }
1338 }
1339 
1340 static void mkdir_cb(const char* name)
1341 {
1342     if (name)
1343     {
1344         sprintf(selected_file,"%s/%s", items.dir, name);
1345         mkdir(selected_file);
1346         gui_fselect_readdir = 1;
1347         gui_fselect_redraw = 2;
1348     }
1349 }
1350 
1351 static void rename_cb(const char* name)
1352 {
1353     if (name)
1354     {
1355         sprintf(selected_file, "%s/%s", items.dir, selected->name);
1356         sprintf(buf, "%s/%s", items.dir, name);
1357         rename(selected_file, buf);
1358         gui_fselect_readdir = 1;
1359         gui_fselect_redraw = 2;
1360     }
1361 }
1362 
1363 static int mpopup_rawop_flag;
1364 
1365 //-------------------------------------------------------------------
1366 // Return 1 if A/DCIM or A/RAW selected, 0 otherwise
1367 static int isPurgeDCIM()
1368 {
1369     return (chk_name(items.dir, "A") && (chk_name(selected->name, "DCIM") || chk_name(selected->name, "RAW")));
1370 }
1371 
1372 // Return 1 if sub-directory of A/DCIM or A/RAW selected
1373 static int isPurgeDir()
1374 {
1375     return (selected->isdir && !selected->isparent && ((chk_name(items.dir, "A/DCIM")) || (chk_name(items.dir, "A/RAW"))));
1376 }
1377 
1378 static void fselect_mpopup_cb(unsigned int actn)
1379 {
1380     switch (actn)
1381     {
1382         case MPOPUP_CUT:
1383             fselect_marked_copy_list();
1384             marked_operation = MARKED_OP_CUT;
1385             break;
1386         case MPOPUP_COPY:
1387             fselect_marked_copy_list();
1388             marked_operation = MARKED_OP_COPY;
1389             break;
1390         case MPOPUP_PASTE:
1391             sprintf(buf, lang_str((marked_operation == MARKED_OP_CUT)?LANG_FSELECT_CUT_TEXT:LANG_FSELECT_COPY_TEXT), marked_items.count, marked_items.dir);
1392             gui_mbox_init((marked_operation == MARKED_OP_CUT)?LANG_FSELECT_CUT_TITLE:LANG_FSELECT_COPY_TITLE,
1393                           (int)buf, MBOX_TEXT_CENTER|MBOX_BTN_YES_NO|MBOX_DEF_BTN2, fselect_marked_paste_cb);
1394             break;
1395         case MPOPUP_DELETE:
1396             sprintf(buf, lang_str(LANG_FSELECT_DELETE_TEXT), fselect_marked_count());
1397             gui_mbox_init(LANG_FSELECT_DELETE_TITLE, (int)buf,
1398                           MBOX_TEXT_CENTER|MBOX_BTN_YES_NO|MBOX_DEF_BTN2, fselect_marked_delete_cb);
1399             break;
1400         case MPOPUP_RMDIR:
1401             confirm_delete_directory();
1402             break;
1403         case MPOPUP_MKDIR:
1404             libtextbox->textbox_init(LANG_POPUP_MKDIR, LANG_PROMPT_MKDIR, "", 15, mkdir_cb, 0);
1405             break;
1406         case MPOPUP_RENAME:
1407             libtextbox->textbox_init(LANG_POPUP_RENAME, LANG_PROMPT_RENAME, selected->name, 15, rename_cb, 0);
1408             break;
1409         case MPOPUP_PURGE_DCIM:
1410             //If selected folder is A/DCIM or A/RAW (this is to purge all RAW files in any sub-folder)
1411             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);
1412             break;
1413         case MPOPUP_PURGE_DIR:
1414             //If selected item is a Canon folder
1415             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);
1416             break;
1417         case MPOPUP_PURGE_FILE:
1418             //If selected item is a file produced by the camera
1419             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);
1420             break;
1421         case MPOPUP_SELINV:
1422             fselect_marked_inverse_selection();
1423             break;
1424         case MPOPUP_CANCEL:
1425             break;
1426 
1427         case MPOPUP_RAWOPS:
1428             libmpopup->show_popup( popup_rawop, mpopup_rawop_flag, fselect_mpopup_rawop_cb);
1429             break;
1430 
1431         case MPOPUP_CHDK_REPLACE:
1432             gui_mbox_init((int)"Replacing CHDK", (int)"Do you want to replace current CHDK with this file",
1433                           MBOX_TEXT_CENTER|MBOX_BTN_YES_NO|MBOX_DEF_BTN2, fselect_chdk_replace_cb);
1434             break;
1435         case MPOPUP_EDITOR:
1436             gui_mbox_init((int)"Editor", (int)"edit", MBOX_BTN_OK|MBOX_TEXT_CENTER, NULL);
1437             break;
1438     }
1439     gui_fselect_redraw = 2;
1440 }
1441 
1442 //-------------------------------------------------------------------
1443 void finalize_fselect()
1444 {
1445     gui_fselect_free_data();
1446     gui_fselect_marked_free_data();
1447 }
1448 
1449 static void exit_fselect(char* file)
1450 {
1451     finalize_fselect();
1452 
1453     gui_set_mode(gui_fselect_mode_old);
1454 
1455     if (fselect_on_select)
1456     {
1457         fselect_on_select(file);
1458         // if called mode will return control to filemanager - we need to redraw it
1459         gui_fselect_redraw = 2;
1460     }
1461 
1462     running = 0;
1463 }
1464 
1465 //-------------------------------------------------------------------
1466 int gui_fselect_kbd_process()
1467 {
1468     int i;
1469 
1470     switch (kbd_get_autoclicked_key() | get_jogdial_direction())
1471     {
1472         case JOGDIAL_LEFT:
1473         case KEY_UP:
1474             if (selected)
1475             {
1476                 if (camera_info.state.is_shutter_half_press) fselect_goto_prev(4);
1477                 else fselect_goto_prev(1);
1478                 gui_fselect_redraw = 1;
1479             }
1480             break;
1481         case KEY_DOWN:
1482         case JOGDIAL_RIGHT:
1483             if (selected)
1484             {
1485                 if (camera_info.state.is_shutter_half_press) fselect_goto_next(4);
1486                 else fselect_goto_next(1);
1487                 gui_fselect_redraw = 1;
1488             }
1489             break;
1490         case KEY_ZOOM_OUT:
1491             if (selected)
1492             {
1493                 fselect_goto_prev(BODY_LINES-1);
1494                 gui_fselect_redraw = 1;
1495             }
1496             break;
1497         case KEY_ZOOM_IN:
1498             if (selected)
1499             {
1500                 fselect_goto_next(BODY_LINES-1);
1501                 gui_fselect_redraw = 1;
1502             }
1503             break;
1504         case KEY_RIGHT:
1505             if (selected)
1506             {
1507                 fselect_marked_toggle();
1508                 fselect_goto_next(1);
1509                 gui_fselect_redraw = 1;
1510             }
1511             break;
1512         case KEY_LEFT:
1513             if (selected && selected->isvalid)
1514             {
1515                 int marked_count = fselect_marked_count();
1516 
1517                 i = MPOPUP_SELINV|MPOPUP_MKDIR;
1518                 mpopup_rawop_flag = 0;
1519 
1520                 if (marked_count > 0)
1521                 {
1522                     i |= MPOPUP_CUT|MPOPUP_COPY|MPOPUP_DELETE;
1523                     if (marked_count > 1)
1524                         mpopup_rawop_flag |= MPOPUP_RAW_ADD|MPOPUP_RAW_AVERAGE;
1525                     // doesn't make sense to subtract from itself!
1526                     if (selected->marked == 0 && fselect_real_marked_count() > 0)
1527                         mpopup_rawop_flag |= MPOPUP_SUBTRACT;
1528                 }
1529 
1530                 if (marked_operation != MARKED_OP_NONE)
1531                     i |= MPOPUP_PASTE;
1532 
1533                 //Check if 'Purge RAW' applies
1534                 if (isPurgeDCIM())              // DCIM or RAW selected in A
1535                     i |= MPOPUP_PURGE_DCIM;
1536                 if (isPurgeDir())               // sub-dir selected in A/DCIM or A/RAW
1537                     i |= MPOPUP_PURGE_DIR;
1538                 if (is_raw(selected->name))     // raw file selected
1539                     i |= MPOPUP_PURGE_FILE;
1540 
1541                 if (selected->isdir && !selected->isparent)
1542                     i |= MPOPUP_RMDIR;
1543 
1544                 if (!selected->isparent) //If item is not UpDir
1545                     i |= MPOPUP_RENAME;
1546 
1547                 if (selected->size >= camera_sensor.raw_size)
1548                     mpopup_rawop_flag |= MPOPUP_RAW_DEVELOP;
1549 
1550                 if ((marked_count > 1) || (selected->size > camera_sensor.raw_size))
1551                     mpopup_rawop_flag |= MPOPUP_DNG_TO_CRW;
1552 
1553                 if (chk_ext(selected->name, "bin")) //If item is *.bin file
1554                     i |= MPOPUP_CHDK_REPLACE;
1555 
1556                 if (mpopup_rawop_flag)
1557                     i |= MPOPUP_RAWOPS;
1558 
1559                 libmpopup->show_popup( popup, i, fselect_mpopup_cb);
1560             }
1561             break;
1562         case KEY_SET:
1563             if (selected && selected->isvalid && gui_fselect_redraw==0)
1564             {
1565                 if (selected->isdir)
1566                 {
1567                     if (selected->isparent)
1568                     {
1569                         char *s = strrchr(items.dir, '/');
1570                         if (s) *s = 0;
1571                     }
1572                     else
1573                     {
1574                         sprintf(items.dir+strlen(items.dir), "/%s", selected->name);
1575                     }
1576                     gui_fselect_readdir = 1;
1577                     gui_fselect_redraw = 1;
1578                 }
1579                 else
1580                 {
1581                     sprintf(selected_file, "%s/%s", items.dir, selected->name);
1582 
1583                     char *ext = strrchr(selected->name,'.');
1584                     int do_exit = 1;
1585 
1586                     if (!fselect_on_select)
1587                     {
1588                         if (chk_ext(ext,"txt") || chk_ext(ext,"log") || chk_ext(ext,"csv"))
1589                         {
1590                             exit_fselect(0);
1591                             do_exit = 0;
1592                                 libtxtread->read_file(selected_file);
1593                         }
1594                         else if (chk_ext(ext,"flt"))
1595                         {
1596                             exit_fselect(0);
1597                             do_exit = 0;
1598                                 module_run(selected_file);
1599                         }
1600                     }
1601 
1602                     if (do_exit)
1603                         exit_fselect(selected_file);
1604                 }
1605             }
1606             break;
1607         case KEY_ERASE:
1608         case KEY_DISPLAY:
1609             if (selected && selected->isvalid)
1610             {
1611                 if (selected->isdir)
1612                 {
1613                     confirm_delete_directory();
1614                 } else
1615                 {
1616                     gui_mbox_init(LANG_BROWSER_DELETE_FILE_TITLE, LANG_BROWSER_DELETE_FILE_TEXT,
1617                                   MBOX_TEXT_CENTER|MBOX_BTN_YES_NO|MBOX_DEF_BTN2, fselect_delete_file_cb);
1618                 }
1619             }
1620             break;
1621     }
1622     return 0;
1623 }
1624 
1625 void gui_fselect_kbd_process_menu_btn()
1626 {
1627     // just free resource. callback called with NULL ptr
1628     exit_fselect(0);
1629 }
1630 
1631 // =========  MODULE INIT =================
1632 
1633 /***************** BEGIN OF AUXILARY PART *********************
1634   ATTENTION: DO NOT REMOVE OR CHANGE SIGNATURES IN THIS SECTION
1635  **************************************************************/
1636 
1637 //---------------------------------------------------------
1638 // PURPOSE: Finalize module operations (close allocs, etc)
1639 // RETURN VALUE: 0-ok, 1-fail
1640 //---------------------------------------------------------
1641 int _module_unloader()
1642 {
1643     // Free file copy buffer if allocated
1644     if (ubuf)
1645     {
1646         ufree(ubuf);
1647         ubuf = 0;
1648     }
1649     finalize_fselect();
1650     return 0;
1651 }
1652 
1653 int _module_can_unload()
1654 {
1655     return running == 0;
1656 }
1657 
1658 int _module_exit_alt()
1659 {
1660     exit_fselect(0);
1661     return 0;
1662 }
1663 
1664 /******************** Module Information structure ******************/
1665 
1666 libfselect_sym _libfselect =
1667 {
1668     {
1669         0, _module_unloader, _module_can_unload, _module_exit_alt, 0
1670     },
1671 
1672     gui_fselect_init
1673 };
1674 
1675 ModuleInfo _module_info = {
1676     MODULEINFO_V1_MAGICNUM,
1677     sizeof(ModuleInfo),
1678     GUI_FSELECT_VERSION,        // Module version
1679 
1680     ANY_CHDK_BRANCH, 0, OPT_ARCHITECTURE,         // Requirements of CHDK version
1681     ANY_PLATFORM_ALLOWED,       // Specify platform dependency
1682 
1683     -LANG_MENU_MISC_FILE_BROWSER,
1684     MTYPE_EXTENSION,
1685 
1686     &_libfselect.base,
1687 
1688     CONF_VERSION,               // CONF version
1689     CAM_SCREEN_VERSION,         // CAM SCREEN version
1690     CAM_SENSOR_VERSION,         // CAM SENSOR version
1691     CAM_INFO_VERSION,           // CAM INFO version
1692 };
1693 
1694 /*************** END OF AUXILARY PART *******************/

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