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

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