source: tags/arb-6.0-rc1/WINDOW/AW_file.cxx

Last change on this file was 11968, checked in by westram, 11 years ago
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 31.6 KB
Line 
1// ================================================================ //
2//                                                                  //
3//   File      : AW_file.cxx                                        //
4//   Purpose   :                                                    //
5//                                                                  //
6//   Institute of Microbiology (Technical University Munich)        //
7//   http://www.arb-home.de/                                        //
8//                                                                  //
9// ================================================================ //
10
11#include "aw_file.hxx"
12#include "aw_awar.hxx"
13#include "aw_root.hxx"
14#include "aw_select.hxx"
15#include "aw_msg.hxx"
16#include "aw_question.hxx"
17
18#include <arbdbt.h>
19#include <arb_file.h>
20#include <arb_strbuf.h>
21#include <arb_misc.h>
22#include <arb_str.h>
23#include <arb_strarray.h>
24
25#include <sys/stat.h>
26#include <dirent.h>
27#include <set>
28#include <string>
29
30using std::set;
31using std::string;
32
33#if defined(DEBUG)
34// #define TRACE_FILEBOX
35#endif // DEBUG
36
37static GB_CSTR expand_symbolic_directories(const char *pwd_envar) { 
38    GB_CSTR res;
39
40    if (strcmp(pwd_envar, "PWD") == 0) {
41        res = GB_getcwd();
42    }
43    else {
44        res = NULL;
45    }
46
47    return res;
48}
49
50
51char *AW_unfold_path(const char *pwd_envar, const char *path) {
52    //! create a full path
53    gb_getenv_hook  oldHook = GB_install_getenv_hook(expand_symbolic_directories);
54    char           *result  = nulldup(GB_unfold_path(pwd_envar, path));
55    GB_install_getenv_hook(oldHook);
56    return result;
57}
58
59char *AW_extract_directory(const char *path) {
60    const char *lslash = strrchr(path, '/');
61    if (!lslash) return 0;
62
63    char *result        = strdup(path);
64    result[lslash-path] = 0;
65
66    return result;
67}
68
69// -----------------------------
70//      file selection boxes
71
72void AW_create_fileselection_awars(AW_root *awr, const char *awar_base, const char *directories, const char *filter, const char *file_name) {
73    int        base_len     = strlen(awar_base);
74    bool       has_slash    = awar_base[base_len-1] == '/';
75    char      *awar_name    = new char[base_len+30]; // use private buffer, because caller will most likely use GBS_global_string for arguments
76    AW_default default_file = AW_ROOT_DEFAULT;
77
78    sprintf(awar_name, "%s%s", awar_base, "/directory"+int(has_slash));
79    AW_awar *awar_dir = awr->awar_string(awar_name, directories, default_file);
80
81    sprintf(awar_name, "%s%s", awar_base, "/filter"   + int(has_slash));
82    AW_awar *awar_filter = awr->awar_string(awar_name, filter, default_file);
83
84    sprintf(awar_name, "%s%s", awar_base, "/file_name"+int(has_slash));
85    AW_awar *awar_filename = awr->awar_string(awar_name, file_name, default_file);
86
87    bool is_tmp_awar = strncmp(awar_base, "tmp/", 4) == 0;
88    aw_assert(is_tmp_awar); // you need to use a temp awar for file selections
89
90    awar_dir->write_string(directories);
91    awar_filter->write_string(filter);
92    awar_filename->write_string(file_name);
93
94    // create all (default) directories
95    {
96        ConstStrArray dirs;
97        GBT_split_string(dirs, directories, ":", true);
98        for (unsigned i = 0; i<dirs.size(); ++i) {
99            if (!GB_is_directory(dirs[i])) {
100                fprintf(stderr, "Creating directory '%s'\n", dirs[i]);
101                GB_ERROR error = GB_create_directory(dirs[i]);
102                if (error) aw_message(GBS_global_string("Failed to create directory '%s' (Reason: %s)", dirs[i], error));
103            }
104        }
105    }
106
107    delete [] awar_name;
108}
109
110enum DirSortOrder {
111    SORT_ALPHA,
112    SORT_DATE,
113    SORT_SIZE,
114
115    DIR_SORT_ORDERS // order count
116};
117
118class LimitedTime {
119    double       max_duration;
120    time_t       start;
121    mutable bool aborted;
122
123public:
124    LimitedTime(double max_duration_seconds) : max_duration(max_duration_seconds) { reset(); }
125    void reset() {
126        time(&start);
127        aborted = false;
128    }
129    double allowed_duration() const { return max_duration; }
130    bool finished_in_time() const { return !aborted; }
131    bool available() const {
132        if (!aborted) {
133            time_t now;
134            time(&now);
135            aborted = difftime(now, start) > max_duration;
136        }
137        return !aborted;
138    }
139    void increase() { max_duration *= 2.5; }
140};
141
142class File_selection { // @@@ derive from AW_selection?
143    AW_root *awr;
144
145    AW_selection_list *filelist;
146
147    char *def_name;
148    char *def_dir;
149    char *def_filter;
150
151    char *pwd;
152    char *pwdx;                                     // additional directories
153
154    DirDisplay dirdisp;
155
156    bool leave_wildcards;
157    bool filled_by_wildcard; // last fill done with wildcard?
158
159    bool show_subdirs;  // show or hide subdirs
160    bool show_hidden;   // show or hide files/directories starting with '.'
161
162    DirSortOrder sort_order;
163
164    LimitedTime searchTime;
165
166    int shown_name_len;
167
168    void bind_callbacks();
169    void execute_browser_command(const char *browser_command);
170    void fill_recursive(const char *fulldir, int skipleft, const char *mask, bool recurse, bool showdir);
171
172    void format_columns();
173
174public:
175
176    File_selection(AW_root *aw_root, const char *awar_prefix, const char *pwd_, DirDisplay disp_dirs, bool allow_wildcards)
177        : awr(aw_root),
178          filelist(NULL),
179          pwd(strdup(pwd_)),
180          pwdx(NULL),
181          dirdisp(disp_dirs),
182          leave_wildcards(allow_wildcards),
183          filled_by_wildcard(false),
184          show_subdirs(true),
185          show_hidden(false),
186          sort_order(SORT_ALPHA),
187          searchTime(1.3)
188    {
189        {
190            char *multiple_dirs_in_pwd = strchr(pwd, '^');
191            if (multiple_dirs_in_pwd) {
192                multiple_dirs_in_pwd[0] = 0;
193                pwdx = multiple_dirs_in_pwd+1;
194            }
195        }
196
197        def_name   = GBS_string_eval(awar_prefix, "*=*/file_name", 0);
198        def_dir    = GBS_string_eval(awar_prefix, "*=*/directory", 0);
199        def_filter = GBS_string_eval(awar_prefix, "*=*/filter", 0);
200
201        aw_assert(!GB_have_error());
202
203        bind_callbacks();
204    }
205
206    void create_gui_elements(AW_window *aws, const char *at_prefix) {
207        aw_assert(!filelist);
208
209        char buffer[1024];
210        sprintf(buffer, "%sfilter", at_prefix);
211        if (aws->at_ifdef(buffer)) {
212            aws->at(buffer);
213            aws->create_input_field(def_filter, 5);
214        }
215
216        sprintf(buffer, "%sfile_name", at_prefix);
217        if (aws->at_ifdef(buffer)) {
218            aws->at(buffer);
219            aws->create_input_field(def_name, 20);
220        }
221
222        sprintf(buffer, "%sbox", at_prefix);
223        aws->at(buffer);
224        filelist = aws->create_selection_list(def_name, 2, 2);
225    }
226
227    void fill();
228
229    void filename_changed(bool post_filter_change_HACK);
230
231    GB_ULONG get_newest_dir_modtime() const {
232        ConstStrArray dirs;
233        GBT_split_string(dirs, awr->awar(def_dir)->read_char_pntr(), ":", true);
234        unsigned long maxtof = 0;
235        for (unsigned i = 0; i<dirs.size(); ++i) {
236            unsigned long tof = GB_time_of_file(dirs[i]);
237            if (tof>maxtof) maxtof = tof;
238        }
239        return maxtof;
240    }
241
242    void trigger_refresh() { awr->awar(def_dir)->touch(); }
243};
244
245static GB_CSTR get_suffix(GB_CSTR fullpath) { // returns pointer behind '.' of suffix (or NULL if no suffix found)
246    GB_CSTR dot = strrchr(fullpath, '.');
247    if (!dot) return 0;
248
249    GB_CSTR lslash = strrchr(fullpath, '/');
250    if (lslash && lslash>dot) return 0; // no . behind last /
251    return dot+1;
252}
253
254
255static char *set_suffix(const char *name, const char *suffix) {
256    // returns "name.suffix" (name may contain path information)
257    // - eliminates multiple dots
258    // - sets name to 'noname' if no name part is given
259
260    char *path, *fullname;
261    GB_split_full_path(name, &path, &fullname, NULL, NULL);
262
263    // remove dots and spaces from suffix:
264    while (suffix[0] == '.' || suffix[0] == ' ') ++suffix;
265    if (!suffix[0]) suffix = 0;
266
267    GBS_strstruct *out = GBS_stropen(FILENAME_MAX+1);
268    if (path) {
269        GBS_strcat(out, path);
270        GBS_chrcat(out, '/');
271    }
272
273    if (fullname) GBS_strcat(out, fullname);
274
275    if (GB_is_directory(GBS_mempntr(out))) {
276        // if 'out' contains a directory now, 'name' was lacking a filename
277        // (in this case it was only a directory)
278        GBS_strcat(out, "/noname"); // invent a name
279    }
280
281    if (suffix) {
282        GBS_chrcat(out, '.');
283        GBS_strcat(out, suffix);
284    }
285
286    free(path);
287    free(fullname);
288
289    return GBS_strclose(out);
290}
291
292
293inline const char *valid_path(const char *path) { return path[0] ? path : "."; }
294
295inline bool AW_is_dir(const char *path) { return GB_is_directory(valid_path(path)); }
296inline bool AW_is_file(const char *path) { return GB_is_regularfile(valid_path(path)); }
297inline bool AW_is_link(const char *path) { return GB_is_link(valid_path(path)); }
298
299void File_selection::execute_browser_command(const char *browser_command) {
300    if (strcmp(browser_command, "sort") == 0) {
301        sort_order = DirSortOrder((sort_order+1)%DIR_SORT_ORDERS);
302    }
303    else if (strcmp(browser_command, "hide") == 0) {
304        show_subdirs = false;
305    }
306    else if (strcmp(browser_command, "show") == 0) {
307        show_subdirs = true;
308    }
309    else if (strcmp(browser_command, "dot") == 0) {
310        show_hidden = !show_hidden;
311    }
312    else if (strcmp(browser_command, "inctime") == 0) {
313        searchTime.increase();
314    }
315    else {
316        aw_message(GBS_global_string("Unknown browser command '%s'", browser_command));
317    }
318}
319
320inline int entryType(const char *entry) {
321    const char *typechar = "DFL";
322    for (int i = 0; typechar[i]; ++i) {
323        if (entry[0] == typechar[i]) return i;
324    }
325    return -1;
326}
327
328void File_selection::format_columns() {
329    const int FORMATTED_TYPES = 3;
330
331    int maxlen[FORMATTED_TYPES] = { 17, 17, 17 };
332
333    for (int pass = 1; pass<=2; ++pass) {
334        AW_selection_list_iterator entry(filelist);
335        while (entry) {
336            const char *disp = entry.get_displayed();
337            int         type = entryType(disp);
338
339            if (type>=0) {
340                const char *q1 = strchr(disp, '?');
341                if (q1) {
342                    const char *q2 = strchr(q1+1, '?');
343                    if (q2) {
344                        int len = q2-q1-1;
345                        if (pass == 1) {
346                            if (maxlen[type]<len) maxlen[type] = len;
347                        }
348                        else {
349                            GBS_strstruct buf(200);
350                            buf.ncat(disp, q1-disp);
351                            buf.ncat(q1+1, len);
352                            buf.nput(' ', maxlen[type]-len);
353                            buf.cat(q2+1);
354                            entry.set_displayed(buf.get_data());
355                        }
356                    }
357                }
358            }
359            ++entry;
360        }
361    }
362}
363
364void File_selection::fill_recursive(const char *fulldir, int skipleft, const char *mask, bool recurse, bool showdir) {
365    DIR *dirp = opendir(fulldir);
366
367#if defined(TRACE_FILEBOX)
368    printf("fill_fileselection_recursive for directory '%s'\n", fulldir);
369#endif // TRACE_FILEBOX
370
371    if (!dirp) {
372        filelist->insert(GBS_global_string("x Your directory path is invalid (%s)", fulldir), "?");
373        return;
374    }
375
376    struct dirent *dp;
377    for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
378        const char *entry       = dp->d_name;
379        char       *nontruepath = GBS_global_string_copy("%s/%s", fulldir, entry);
380        char       *fullname;
381
382        if (strlen(fulldir)) fullname = strdup(GB_concat_full_path(fulldir, entry));
383        else fullname                 = strdup(GB_canonical_path(entry));
384
385        if (AW_is_dir(fullname)) {
386            if (!(entry[0] == '.' && (!show_hidden || entry[1] == 0 || (entry[1] == '.' && entry[2] == 0)))) { // skip "." and ".." and dotdirs if requested
387                if (showdir) {
388                    filelist->insert(GBS_global_string("D ?%s? (%s)", entry, fullname), fullname); // '?' used in format_columns()
389                }
390                if (recurse && !AW_is_link(nontruepath)) { // don't follow links
391                    if (searchTime.available()) {
392                        fill_recursive(nontruepath, skipleft, mask, recurse, showdir);
393                    }
394                }
395            }
396        }
397        else {
398            if (GBS_string_matches(entry, mask, GB_IGNORE_CASE)) { // entry matches mask
399                if ((entry[0] != '.' || show_hidden) && AW_is_file(fullname)) { // regular existing file
400                    struct stat stt;
401
402                    stat(fullname, &stt);
403
404                    char       atime[256];
405                    struct tm *tms = localtime(&stt.st_mtime);
406                    strftime(atime, 255, "%Y/%m/%d %k:%M", tms);
407
408                    char *size     = strdup(GBS_readable_size(stt.st_size, "b"));
409                    char  typechar = AW_is_link(nontruepath) ? 'L' : 'F';
410
411                    const char *sel_entry = 0;
412                    switch (sort_order) {
413                        case SORT_ALPHA:
414                            sel_entry = GBS_global_string("%c ?%s?  %7s  %s", typechar, nontruepath+skipleft, size, atime); // '?' used in format_columns()
415                            break;
416                        case SORT_DATE:
417                            sel_entry = GBS_global_string("%c %s  %7s  %s", typechar, atime, size, nontruepath+skipleft);
418                            break;
419                        case SORT_SIZE:
420                            sel_entry = GBS_global_string("%c %7s  %s  %s", typechar, size, atime, nontruepath+skipleft);
421                            break;
422                        case DIR_SORT_ORDERS: break;
423                    }
424
425                    filelist->insert(sel_entry, nontruepath);
426                    free(size);
427                }
428            }
429        }
430        free(fullname);
431        free(nontruepath);
432    }
433
434    closedir(dirp);
435}
436
437class DuplicateLinkFilter {
438    set<string> insertedDirectories;
439
440public:
441    DuplicateLinkFilter() {}
442
443    bool not_seen_yet(const string& dir) const { return insertedDirectories.find(dir) == insertedDirectories.end(); }
444    void register_directory(const string& dir) {
445        insertedDirectories.insert(dir);
446    }
447};
448
449
450static void show_soft_link(AW_selection_list *filelist, const char *envar, DuplicateLinkFilter& unDup) {
451    // adds a soft link (e.g. ARBMACROHOME or ARB_WORKDIR) into file selection box
452    // if content of 'envar' matches 'cwd' nothing is inserted
453
454    const char *expanded_dir        = expand_symbolic_directories(envar);
455    if (!expanded_dir) expanded_dir = GB_getenv(envar);
456
457    if (expanded_dir) {
458        string edir(expanded_dir);
459
460        if (unDup.not_seen_yet(edir)) {
461            unDup.register_directory(edir);
462            const char *entry = GBS_global_string("$ %-18s(%s)", GBS_global_string("'%s'", envar), expanded_dir);
463            filelist->insert(entry, expanded_dir);
464        }
465    }
466}
467
468inline bool fileOrLink(const char *d) { return d[0] == 'F' || d[0] == 'L'; }
469inline const char *gotounit(const char *d) {
470    ++d;
471    while (d[0] == ' ') ++d;
472    while (d[0] != ' ') ++d;
473    while (d[0] == ' ') ++d;
474    return d;
475}
476static int cmpBySize(const char *disp1, const char *disp2) {
477    if (fileOrLink(disp1) && fileOrLink(disp2)) {
478        const char *u1 = gotounit(disp1);
479        const char *u2 = gotounit(disp2);
480
481        if (u1[0] != u2[0]) { // diff units
482            static const char *units = "bkMGTPEZY"; // see also ../CORE/arb_misc.cxx@Tera
483
484            const char *p1 = strchr(units, u1[0]);
485            const char *p2 = strchr(units, u2[0]);
486            if (p1 != p2) {
487                return p1-p2;
488            }
489        }
490    }
491    return ARB_stricmp(disp1, disp2);
492}
493
494void File_selection::fill() {
495    AW_root *aw_root = awr;
496    filelist->clear();
497
498    char *filter  = aw_root->awar(def_filter)->read_string();
499    char *name    = aw_root->awar(def_name)->read_string();
500
501    const char *name_only = 0;
502    {
503        char *slash = strrchr(name, '/');
504        name_only   = slash ? slash+1 : name;
505    }
506
507    StrArray dirs;
508    {
509        char *diru = aw_root->awar(def_dir)->read_string();
510        if (dirdisp == MULTI_DIRS) {
511            ConstStrArray cdirs;
512            GBT_split_string(cdirs, diru, ":", true);
513            for (unsigned i = 0; i<cdirs.size(); ++i) dirs.put(strdup(cdirs[i]));
514        }
515        else {
516            if (name[0] == '/' && AW_is_dir(name)) {
517                dirs.put(strdup(name));
518                name_only = "";
519            }
520            else {
521                char *fulldir = AW_unfold_path(pwd, diru);
522                dirs.put(fulldir);
523            }
524        }
525        free(diru);
526    }
527
528    filled_by_wildcard = strchr(name_only, '*');
529
530    if (dirdisp == ANY_DIR) {
531        aw_assert(dirs.size() == 1);
532        const char *fulldir    = dirs[0];
533
534        DuplicateLinkFilter unDup;
535        unDup.register_directory(fulldir);
536
537        if (filled_by_wildcard) {
538            if (leave_wildcards) {
539                filelist->insert(GBS_global_string("  ALL '%s' in '%s'", name_only, fulldir), name);
540            }
541            else {
542                filelist->insert(GBS_global_string("  ALL '%s' in+below '%s'", name_only, fulldir), name);
543            }
544        }
545        else {
546            filelist->insert(GBS_global_string("  CONTENTS OF '%s'", fulldir), fulldir);
547            if (filter[0]) {
548                filelist->insert(GBS_global_string("!  Find all         (*%s)", filter), "*");
549            }
550        }
551
552        if (strcmp("/", fulldir)) {
553            filelist->insert("! \'PARENT DIR\'      (..)", "..");
554        }
555        if (show_subdirs) {
556            show_soft_link(filelist, pwd, unDup);
557
558            if (pwdx) {        // additional directories
559                char *start = pwdx;
560                while (start) {
561                    char *multiple = strchr(start, '^');
562                    if (multiple) {
563                        multiple[0] = 0;
564                        show_soft_link(filelist, start, unDup);
565                        multiple[0] = '^';
566                        start       = multiple+1;
567                    }
568                    else {
569                        show_soft_link(filelist, start, unDup);
570                        start = 0;
571                    }
572                }
573            }
574
575            show_soft_link(filelist, "HOME", unDup);
576            show_soft_link(filelist, "PWD", unDup);
577            show_soft_link(filelist, "ARB_WORKDIR", unDup);
578
579            filelist->insert("!  Sub-directories  (shown)", GBS_global_string("%s?hide?", name));
580        }
581        else {
582            filelist->insert("!  Sub-directories  (hidden)", GBS_global_string("%s?show?", name));
583        }
584        filelist->insert(GBS_global_string("!  Hidden           (%s)", show_hidden ? "shown" : "not shown"), GBS_global_string("%s?dot?", name));
585    }
586    else {
587        aw_assert(dirdisp == MULTI_DIRS);
588    }
589
590    static const char *order_name[DIR_SORT_ORDERS] = { "alpha", "date", "size" };
591
592    filelist->insert(GBS_global_string("!  Sort order       (%s)", order_name[sort_order]), GBS_global_string("%s?sort?", name));
593
594    bool insert_dirs = dirdisp == ANY_DIR && show_subdirs;
595
596    searchTime.reset(); // limits time spent in fill_recursive
597    for (unsigned i = 0; i<dirs.size(); ++i) {
598        const char *fulldir = dirs[i];
599        if (filled_by_wildcard) {
600            if (leave_wildcards) {
601                fill_recursive(fulldir, strlen(fulldir)+1, name_only, false, insert_dirs);
602            }
603            else {
604                if (dirdisp == ANY_DIR) { // recursive wildcarded search
605                    fill_recursive(fulldir, strlen(fulldir)+1, name_only, true, false);
606                }
607                else {
608                    char *mask = GBS_global_string_copy("%s*%s", name_only, filter);
609                    fill_recursive(fulldir, strlen(fulldir)+1, mask, false, false);
610                    free(mask);
611                }
612            }
613        }
614        else {
615            char *mask = GBS_global_string_copy("*%s", filter);
616
617            fill_recursive(fulldir, strlen(fulldir)+1, mask, false, insert_dirs);
618            free(mask);
619        }
620    }
621
622    if (!searchTime.finished_in_time()) {
623        filelist->insert(GBS_global_string("!  Find aborted    (after %.1fs; click to search longer)", searchTime.allowed_duration()), GBS_global_string("%s?inctime?", name));
624    }
625
626    filelist->insert_default("", "");
627    if (sort_order == SORT_SIZE) {
628        filelist->sortCustom(cmpBySize);
629    }
630    else {
631        filelist->sort(false, false);
632    }
633    format_columns();
634    filelist->update();
635
636    if (filled_by_wildcard && !leave_wildcards) { // avoid returning wildcarded filename (if !leave_wildcards)
637        aw_root->awar(def_name)->write_string("");
638    }
639
640    free(name);
641    free(filter);
642}
643
644void File_selection::filename_changed(bool post_filter_change_HACK) {
645    AW_root *aw_root = awr;
646    char    *fname   = aw_root->awar(def_name)->read_string();
647
648#if defined(TRACE_FILEBOX)
649    printf("fileselection_filename_changed_cb:\n"
650           "- fname   ='%s'\n", fname);
651#endif // TRACE_FILEBOX
652
653    if (fname[0]) {
654        char *browser_command = 0;
655        {
656            // internal browser commands (e.g. '?sort?') are simply appended to the filename
657
658            char *lquestion = strrchr(fname, '?');
659            if (lquestion) {
660                lquestion[0] = 0; // remove last '?' + everything behind
661                lquestion    = strrchr(fname, '?');
662                if (lquestion) {
663                    browser_command = lquestion+1;
664                    lquestion[0]    = 0; // completely remove the browser command
665                }
666            }
667        }
668        if (browser_command) {
669            aw_root->awar(def_name)->write_string(fname); // re-write w/o browser_command
670            execute_browser_command(browser_command);
671            trigger_refresh();
672        }
673        else if (dirdisp != MULTI_DIRS) {
674            char *newName = 0;
675            char *dir     = aw_root->awar(def_dir)->read_string();
676
677#if defined(TRACE_FILEBOX)
678            printf("- dir     ='%s'\n", dir);
679#endif // TRACE_FILEBOX
680
681            if (fname[0] == '/' || fname[0] == '~') {
682                newName = strdup(GB_canonical_path(fname));
683            }
684            else {
685                if (dir[0]) {
686                    if (dir[0] == '/') {
687                        newName = strdup(GB_concat_full_path(dir, fname));
688                    }
689                    else {
690                        char *fulldir = 0;
691
692                        if (dir[0] == '.') fulldir = AW_unfold_path(pwd, dir);
693                        else fulldir               = strdup(dir);
694
695                        newName = strdup(GB_concat_full_path(fulldir, fname));
696                        free(fulldir);
697                    }
698                }
699                else {
700                    newName = AW_unfold_path(pwd, fname);
701                }
702            }
703
704            if (newName) {
705#if defined(TRACE_FILEBOX)
706                printf("- newName ='%s'\n", newName);
707#endif // TRACE_FILEBOX
708
709                if (AW_is_dir(newName)) {
710                    aw_root->awar(def_name)->write_string("");
711                    aw_assert(dirdisp != MULTI_DIRS); // overwriting content unwanted if displaying MULTI_DIRS
712                    aw_root->awar(def_dir)->write_string(newName);
713                    aw_root->awar(def_name)->write_string("");
714                }
715                else {
716                    char *lslash = strrchr(newName, '/');
717                    if (lslash) {
718                        if (lslash == newName) { // root directory
719                            aw_assert(dirdisp != MULTI_DIRS); // overwriting content unwanted if displaying MULTI_DIRS
720                            aw_root->awar(def_dir)->write_string("/"); // write directory part
721                        }
722                        else {
723                            lslash[0] = 0;
724                            aw_assert(dirdisp != MULTI_DIRS); // overwriting content unwanted if displaying MULTI_DIRS
725                            aw_root->awar(def_dir)->write_string(newName); // write directory part
726                            lslash[0] = '/';
727                        }
728                    }
729
730                    // now check the correct suffix :
731                    {
732                        char *filter = aw_root->awar(def_filter)->read_string();
733                        if (filter[0]) {
734                            char *pfilter = strrchr(filter, '.');
735                            pfilter       = pfilter ? pfilter+1 : filter;
736
737                            char *suffix = (char*)get_suffix(newName); // cast ok, since get_suffix points into newName
738
739                            if (!suffix || strcmp(suffix, pfilter) != 0) {
740                                if (suffix && post_filter_change_HACK) {
741                                    if (suffix[-1] == '.') suffix[-1] = 0;
742                                }
743                                freeset(newName, set_suffix(newName, pfilter));
744                            }
745                        }
746                        free(filter);
747                    }
748
749                    if (strcmp(newName, fname) != 0) {
750                        aw_root->awar(def_name)->write_string(newName); // loops back if changed !!!
751                    }
752                }
753            }
754            free(dir);
755
756            if (strchr(fname, '*')) { // wildcard -> search for suffix
757                trigger_refresh();
758            }
759        }
760    }
761
762    free(fname);
763}
764
765static bool avoid_multi_refresh = false;
766
767static void fill_fileselection_cb(AW_root*, File_selection *cbs) {
768    if (!avoid_multi_refresh) {
769        LocallyModify<bool> flag(avoid_multi_refresh, true);
770        cbs->fill();
771    }
772}
773static void fileselection_filename_changed_cb(AW_root*, File_selection *cbs) {
774    if (!avoid_multi_refresh) {
775        LocallyModify<bool> flag(avoid_multi_refresh, true);
776        cbs->filename_changed(false);
777        cbs->fill();
778    }
779    else {
780        cbs->filename_changed(false);
781    }
782}
783static void fileselection_filter_changed_cb(AW_root*, File_selection *cbs) {
784    if (!avoid_multi_refresh) {
785        LocallyModify<bool> flag(avoid_multi_refresh, true);
786        cbs->filename_changed(true);
787        cbs->fill();
788    }
789    else {
790        cbs->filename_changed(true);
791    }
792}
793
794void File_selection::bind_callbacks() {
795    awr->awar(def_name)  ->add_callback(makeRootCallback(fileselection_filename_changed_cb, this));
796    awr->awar(def_dir)   ->add_callback(makeRootCallback(fill_fileselection_cb, this));
797    awr->awar(def_filter)->add_callback(makeRootCallback(fileselection_filter_changed_cb, this));
798}
799
800#define SELBOX_AUTOREFRESH_FREQUENCY 3000 // refresh every XXX ms
801
802struct selbox_autorefresh_info {
803    unsigned long            modtime;
804    File_selection          *acbs;
805    selbox_autorefresh_info *next;
806};
807static selbox_autorefresh_info *autorefresh_info = 0;
808
809static unsigned autorefresh_selboxes(AW_root *) {
810    selbox_autorefresh_info *check = autorefresh_info;
811
812    while (check) {
813        GB_ULONG mtime = check->acbs->get_newest_dir_modtime();
814        if (mtime != check->modtime) {
815            check->modtime = mtime;
816            check->acbs->trigger_refresh();
817        }
818        check = check->next;
819    }
820
821    // refresh again and again and again..
822    return SELBOX_AUTOREFRESH_FREQUENCY;
823}
824
825static void selbox_install_autorefresh(AW_root *aw_root, File_selection *acbs) {
826    if (!autorefresh_info) {    // when installing first selbox
827        aw_root->add_timed_callback(SELBOX_AUTOREFRESH_FREQUENCY, makeTimedCallback(autorefresh_selboxes));
828    }
829
830    selbox_autorefresh_info *install = new selbox_autorefresh_info;
831
832    install->acbs    = acbs;
833    install->modtime = acbs->get_newest_dir_modtime();
834
835    install->next    = autorefresh_info;
836    autorefresh_info = install;
837}
838
839void AW_create_fileselection(AW_window *aws, const char *awar_prefix, const char *at_prefix, const char *pwd, DirDisplay disp_dirs, bool allow_wildcards) {
840    /*! Create a file selection box, this box needs 3 AWARS:
841     *
842     * 1. "$awar_prefix/filter"
843     * 2. "$awar_prefix/directory"
844     * 3. "$awar_prefix/file_name"
845     *
846     * (Note: The function AW_create_fileselection_awars() can be used to create them)
847     *
848     * the "$awar_prefix/file_name" contains the full filename
849     * Use AW_get_selected_fullname() to read it.
850     *
851     * The items are placed at
852     *
853     * 1. "$at_prefix""filter"
854     * 2. "$at_prefix""box"
855     * 3. "$at_prefix""file_name"
856     *
857     * if disp_dirs == ANY_DIR, then show directories and files
858     * if disp_dirs == MULTI_DIRS, then only show files, but from multiple directories
859     *
860     * pwd is the name of a 'shell environment variable' which indicates the base directory
861     * (e.g. 'PWD' or 'ARBHOME')
862     */
863
864    AW_root        *aw_root = aws->get_root();
865    File_selection *acbs    = new File_selection(aw_root, awar_prefix, pwd, disp_dirs, allow_wildcards);
866
867    acbs->create_gui_elements(aws, at_prefix);
868
869    fill_fileselection_cb(0, acbs);
870    fileselection_filename_changed_cb(0, acbs);    // this fixes the path name
871
872    selbox_install_autorefresh(aw_root, acbs);
873}
874
875char *AW_get_selected_fullname(AW_root *awr, const char *awar_prefix) { // @@@ add flag to select whether wildcards are allowed
876    char *file = awr->awar(GBS_global_string("%s/file_name", awar_prefix))->read_string();
877    if (file[0] != '/') {
878        // if name w/o directory was entered by hand (or by default) then append the directory :
879
880        char    *awar_dir_name = GBS_global_string_copy("%s/directory", awar_prefix);
881        AW_awar *awar_dir      = awr->awar_no_error(awar_dir_name);
882
883        if (!awar_dir) {
884            // file selection box was not active (happens e.g. for print tree)
885            awar_dir = awr->awar_string(awar_dir_name, GB_getcwd());
886        }
887
888        aw_assert(awar_dir);
889
890        char *dir = awar_dir->read_string();
891        if (!dir[0]) {          // empty -> fillin current dir
892            awar_dir->write_string(GB_getcwd());
893            freeset(dir, awar_dir->read_string());
894        }
895
896        char *full = strdup(GB_concat_full_path(dir, file));
897
898        free(dir);
899        free(file);
900
901        file = full;
902
903        free(awar_dir_name);
904    }
905
906    return file;
907}
908
909void AW_set_selected_fullname(AW_root *awr, const char *awar_prefix, const char *to_fullname) {
910    awr->awar(GBS_global_string("%s/file_name", awar_prefix))->write_string(to_fullname);
911}
912
913void AW_refresh_fileselection(AW_root *awr, const char *awar_prefix) {
914    // call optionally to force instant refresh
915    // (automatic refresh is done every SELBOX_AUTOREFRESH_FREQUENCY)
916
917    awr->awar(GBS_global_string("%s/directory", awar_prefix))->touch();
918}
919
920// --------------------------------------------------------------------------------
921
922#ifdef UNIT_TESTS
923#include <test_unit.h>
924
925#define TEST_EXPECT_EQUAL_DUPPED(cs1, cs2)                              \
926    do {                                                                \
927        char *s1, *s2;                                                  \
928        TEST_EXPECT_EQUAL(s1 = (cs1), s2 = (cs2));                      \
929        free(s1);                                                       \
930        free(s2);                                                       \
931    } while(0)
932
933void TEST_path_unfolding() {
934    const char *currDir = GB_getcwd();
935    {
936        gb_getenv_hook old = GB_install_getenv_hook(expand_symbolic_directories);
937
938        TEST_EXPECT_EQUAL(GB_getenv("PWD"), currDir);
939        TEST_EXPECT_EQUAL(GB_getenv("ARBHOME"), GB_getenvARBHOME());
940        TEST_EXPECT_NULL(GB_getenv("ARB_NONEXISTING_ENVAR"));
941
942        GB_install_getenv_hook(old);
943    }
944
945    TEST_EXPECT_EQUAL_DUPPED(AW_unfold_path("PWD", "/bin"),                strdup("/bin"));
946    TEST_EXPECT_EQUAL_DUPPED(AW_unfold_path("PWD", "../tests"),            strdup(GB_path_in_ARBHOME("UNIT_TESTER/tests")));
947    TEST_EXPECT_EQUAL_DUPPED(AW_unfold_path("ARB_NONEXISTING_ENVAR", "."), strdup(currDir));
948}
949
950#endif // UNIT_TESTS
951
952
Note: See TracBrowser for help on using the repository browser.