source: tags/ms_r18q1/AWT/AWT_sel_boxes.cxx

Last change on this file was 16936, checked in by westram, 6 years ago
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 42.4 KB
Line 
1// ================================================================ //
2//                                                                  //
3//   File      : AWT_sel_boxes.cxx                                  //
4//   Purpose   :                                                    //
5//                                                                  //
6//   Institute of Microbiology (Technical University Munich)        //
7//   http://www.arb-home.de/                                        //
8//                                                                  //
9// ================================================================ //
10
11#include "awt.hxx"
12#include "awt_sel_boxes.hxx"
13
14#include <item_sel_list.h>
15
16#include <aw_awars.hxx>
17#include <aw_file.hxx>
18#include <aw_msg.hxx>
19#include <aw_root.hxx>
20#include <aw_edit.hxx>
21
22#include <ad_config.h>
23#include <ad_cb.h>
24
25#include <arbdbt.h>
26#include <arb_strbuf.h>
27#include <arb_strarray.h>
28#include <arb_file.h>
29#include <arb_global_defs.h>
30
31#include "awt_modules.hxx"
32#include <BufferedFileReader.h>
33
34#include <list>
35#include <map>
36#include <set>
37
38using namespace std;
39
40typedef map<string, AW_window*> WinMap;
41
42class SelectionListSpec {
43    string        awar_name;
44    static WinMap window_map; // contains popup windows of all selection list popups
45
46    virtual AW_DB_selection *create(AW_selection_list *sellist) const = 0;
47    AW_DB_selection *init(AW_selection_list *sellist) const {
48        AW_DB_selection *sel = create(sellist);
49        sel->refresh();
50        return sel;
51    }
52
53public:
54    SelectionListSpec(const char *awar_name_)
55        : awar_name(awar_name_)
56    {}
57    virtual ~SelectionListSpec() {}
58
59    virtual const char *get_macro_id() const = 0;
60    virtual const char *get_title() const    = 0;
61
62    const char *get_awar_name() const { return awar_name.c_str(); }
63
64    AW_DB_selection *create_list(AW_window *aws, bool fallback2default) const {
65        return init(aws->create_selection_list(get_awar_name(), 40, 4, fallback2default));
66    }
67
68    void popup() const {
69        WinMap::iterator  found  = window_map.find(awar_name);
70        if (found == window_map.end()) {
71            AW_window_simple *aws = new AW_window_simple;
72            aws->init(AW_root::SINGLETON, get_macro_id(), get_title());
73            aws->load_xfig("select_simple.fig");
74
75            aws->at("selection");
76            AW_root::SINGLETON->awar(get_awar_name())->add_callback(makeRootCallback(awt_auto_popdown_cb, aws));
77            create_list(aws, true);
78
79            aws->at("button");
80            aws->callback(AW_POPDOWN);
81            aws->create_button("CLOSE", "CLOSE", "C");
82
83            aws->window_fit();
84
85            window_map[awar_name] = aws;
86            aws->activate();
87        }
88        else {
89            found->second->activate();
90        }
91    }
92
93    void createButton(AW_window *aws) const;
94};
95WinMap SelectionListSpec::window_map; 
96
97static void popup_SelectionListSpec_cb(UNFIXED, const SelectionListSpec *spec) {
98    spec->popup();
99}
100
101void SelectionListSpec::createButton(AW_window *aws) const {
102    // WARNING: this is bound to callback (do not free)
103    aws->callback(makeWindowCallback(popup_SelectionListSpec_cb, this));
104    aws->create_button(get_macro_id(), get_awar_name());
105}
106
107// --------------------------------------
108//      selection boxes on alignments
109
110class ALI_selection : public AW_DB_selection { // derived from a Noncopyable
111    char *ali_type_match;                           // filter for wanted alignments (GBS_string_eval command)
112public:
113    ALI_selection(AW_selection_list *sellist_, GBDATA *gb_presets, const char *ali_type_match_)
114        : AW_DB_selection(sellist_, gb_presets),
115          ali_type_match(nulldup(ali_type_match_))
116    {}
117   
118    void fill() OVERRIDE {
119        GBDATA         *gb_presets = get_gbd();
120        GB_transaction  ta(gb_presets);
121
122        for (GBDATA *gb_alignment = GB_entry(gb_presets, "alignment");
123             gb_alignment;
124             gb_alignment = GB_nextEntry(gb_alignment))
125        {
126            char *alignment_type = GBT_read_string(gb_alignment, "alignment_type");
127            char *alignment_name = GBT_read_string(gb_alignment, "alignment_name");
128            char *str            = GBS_string_eval(alignment_type, ali_type_match);
129
130            if (!*str) insert(alignment_name, alignment_name);
131            free(str);
132            free(alignment_type);
133            free(alignment_name);
134        }
135        insert_default(DISPLAY_NONE, NO_ALI_SELECTED);
136    }
137
138    void reconfigure(const char *new_ali_type_match) {
139        freedup(ali_type_match, new_ali_type_match);
140        refresh();
141    }
142};
143
144class ALI_sellst_spec : public SelectionListSpec, virtual Noncopyable {
145    GBDATA *gb_main;
146    string  ali_type_match;
147
148    AW_DB_selection *create(AW_selection_list *sellist) const {
149        GB_transaction ta(gb_main);
150        return new ALI_selection(sellist, GBT_get_presets(gb_main), ali_type_match.c_str());
151    }
152
153public:
154    ALI_sellst_spec(const char *awar_name_, GBDATA *gb_main_, const char *ali_type_match_)
155        : SelectionListSpec(awar_name_),
156          gb_main(gb_main_),
157          ali_type_match(ali_type_match_)
158    {}
159
160    const char *get_macro_id() const { return "SELECT_ALI"; }
161    const char *get_title() const { return "Select alignment"; }
162};
163
164AW_DB_selection *awt_create_ALI_selection_list(GBDATA *gb_main, AW_window *aws, const char *varname, const char *ali_type_match) {
165    // Create selection lists on alignments
166    //
167    // if 'ali_type_match' is set, then only insert alignments,
168    // where 'ali_type_match' GBS_string_eval's the alignment type
169
170    ALI_sellst_spec spec(varname, gb_main, ali_type_match);
171    return spec.create_list(aws, true);
172}
173
174void awt_create_ALI_selection_button(GBDATA *gb_main, AW_window *aws, const char *varname, const char *ali_type_match) {
175    (new ALI_sellst_spec(varname, gb_main, ali_type_match))->createButton(aws); // do not free (bound to callback)
176}
177
178void awt_reconfigure_ALI_selection_list(AW_DB_selection *dbsel, const char *ali_type_match) {
179    ALI_selection *alisel = dynamic_cast<ALI_selection*>(dbsel);
180    alisel->reconfigure(ali_type_match);
181}
182
183// ---------------------------------
184//      selection boxes on trees
185
186struct AWT_tree_selection: public AW_DB_selection {
187    AWT_tree_selection(AW_selection_list *sellist_, GBDATA *gb_tree_data)
188        : AW_DB_selection(sellist_, gb_tree_data)
189    {}
190
191    void fill() OVERRIDE {
192        GBDATA         *gb_main = get_gb_main();
193        GB_transaction  ta(gb_main);
194
195        ConstStrArray tree_names;
196        GBT_get_tree_names(tree_names, gb_main, true);
197
198        if (!tree_names.empty()) {
199            int maxTreeNameLen = 0;
200            for (int i = 0; tree_names[i]; ++i) {
201                const char *tree = tree_names[i];
202                int         len  = strlen(tree);
203                if (len>maxTreeNameLen) maxTreeNameLen = len;
204            }
205            for (int i = 0; tree_names[i]; ++i) {
206                const char *tree = tree_names[i];
207                const char *info = GBT_tree_info_string(gb_main, tree, maxTreeNameLen);
208                if (info) {
209                    insert(info, tree);
210                }
211                else {
212                    aw_message(GB_await_error());
213                    insert(tree, tree);
214                }
215            }
216        }
217        insert_default(DISPLAY_NONE, NO_TREE_SELECTED); 
218    }
219};
220
221AW_DB_selection *awt_create_TREE_selection_list(GBDATA *gb_main, AW_window *aws, const char *varname, bool fallback2default) {
222    GBDATA *gb_tree_data;
223    {
224        GB_transaction ta(gb_main);
225        gb_tree_data = GBT_get_tree_data(gb_main);
226    }
227    AW_selection_list  *sellist = aws->create_selection_list(varname, 40, 4, fallback2default);
228    AWT_tree_selection *treesel = new AWT_tree_selection(sellist, gb_tree_data); // owned by nobody
229    treesel->refresh();
230    return treesel;
231}
232
233
234// --------------------------------------
235//      selection boxes on pt-servers
236
237#define PT_SERVERNAME_LENGTH        23              // that's for buttons
238#define PT_SERVERNAME_SELLIST_WIDTH 30              // this for lists
239
240class PT_selection : public AW_selection {
241    typedef list<PT_selection*> PT_selections;
242   
243    static PT_selections ptserver_selections;
244public:
245    PT_selection(AW_selection_list *sellist_);
246
247    void fill() OVERRIDE;
248
249    static void refresh_all();
250};
251
252PT_selection::PT_selections PT_selection::ptserver_selections;
253
254void PT_selection::fill() {
255    const char * const *pt_servers = GBS_get_arb_tcp_entries("ARB_PT_SERVER*");
256
257    int count = 0;
258    while (pt_servers[count]) count++;
259
260    for (int i=0; i<count; i++) {
261        char *choice = GBS_ptserver_id_to_choice(i, 1);
262        if (!choice) {
263            aw_message(GB_await_error());
264            break;
265        }
266        insert(choice, (long)i);
267        free(choice);
268    }
269
270    insert_default("-undefined-", (long)-1);
271}
272
273void PT_selection::refresh_all() {
274    PT_selections::iterator end = ptserver_selections.end();
275    for (PT_selections::iterator pts_sel = ptserver_selections.begin(); pts_sel != end; ++pts_sel) {
276        (*pts_sel)->refresh();
277    }
278}
279
280static void ptserverlog_changed_cb(const char *) { PT_selection::refresh_all(); }
281static void arbtcpdat_changed_cb(const char*)    { PT_selection::refresh_all(); }
282
283PT_selection::PT_selection(AW_selection_list *sellist_)
284    : AW_selection(sellist_)
285{
286    if (ptserver_selections.empty()) {
287        // first pt server selection list -> install log tracker
288        AW_add_inotification(GBS_ptserver_logname(), makeFileChangedCallback(ptserverlog_changed_cb));
289    }
290    ptserver_selections.push_back(this);
291}
292
293
294void awt_edit_arbtcpdat_cb(AW_window *) {
295    char *filename = GB_arbtcpdat_path();
296    AW_edit_notified(filename, makeFileChangedCallback(arbtcpdat_changed_cb));
297    free(filename);
298}
299
300void awt_auto_popdown_cb(AW_root*, AW_window_simple *aw_popup) {
301    /*! auto pops-down a simple selection window when the awar bound
302     * to the selection list gets changed.
303     */
304    aw_popup->hide();
305}
306
307static char *readable_pt_servername(int index, int maxlength) {
308    char *fullname = GBS_ptserver_id_to_choice(index, 0);
309    if (!fullname) {
310#ifdef DEBUG
311        printf("awar given to ptserver-selection does not contain a valid index\n");
312#endif
313        GB_clear_error();
314        return ARB_strdup("-undefined-");
315    }
316
317    int len = strlen(fullname);
318    if (len <= maxlength) {
319        return fullname;
320    }
321
322    int remove  = len-maxlength;
323    fullname[0] = '.';
324    fullname[1] = '.';
325    strcpy(fullname+2, fullname+2+remove);
326
327    return fullname;
328}
329
330static void update_ptserver_button(AW_root *, AW_awar *awar_ptserver, AW_awar *awar_buttontext_name) {
331    char *readable_name = readable_pt_servername(awar_ptserver->read_int(), PT_SERVERNAME_LENGTH);
332    awar_buttontext_name->write_string(readable_name);
333    free(readable_name);
334}
335
336static AW_window *create_PTSERVER_selection_window(AW_root *aw_root, const char *varname) {
337    AW_window_simple *aw_popup = new AW_window_simple;
338
339    aw_popup->init(aw_root, "SELECT_PT_SERVER", "Select a PT-Server");
340    aw_popup->auto_space(10, 10);
341
342    const char *CLOSE_ID = "CLOSE";
343
344    aw_popup->at_newline();
345    aw_root->awar(varname)->add_callback(makeRootCallback(awt_auto_popdown_cb, aw_popup));
346    AW_selection_list *sellist = aw_popup->create_selection_list(varname, PT_SERVERNAME_SELLIST_WIDTH, 20, true);
347
348    aw_popup->at_newline();
349    aw_popup->callback(AW_POPDOWN);
350    aw_popup->create_button(CLOSE_ID, "CLOSE", "C");
351
352    aw_popup->window_fit();
353    aw_popup->recalc_pos_atShow(AW_REPOS_TO_MOUSE);
354
355    (new PT_selection(sellist))->refresh();
356
357    return aw_popup;
358}
359
360void awt_create_PTSERVER_selection_button(AW_window *aws, const char *varname) {
361    AW_root *aw_root              = aws->get_root();
362    char    *awar_buttontext_name = GBS_global_string_copy("/tmp/%s_BUTTON", varname);
363    AW_awar *awar_ptserver        = aw_root->awar(varname);
364    int      ptserver_index       = awar_ptserver->read_int();
365
366    if (ptserver_index<0) { // fix invalid pt_server indices
367        ptserver_index = 0;
368        awar_ptserver->write_int(ptserver_index);
369    }
370
371    char *readable_name = readable_pt_servername(ptserver_index, PT_SERVERNAME_LENGTH);
372
373    awt_assert(!GB_have_error());
374
375    AW_awar *awar_buttontext = aw_root->awar_string(awar_buttontext_name, readable_name, AW_ROOT_DEFAULT);
376    awar_ptserver->add_callback(makeRootCallback(update_ptserver_button, awar_ptserver, awar_buttontext));
377
378    int old_button_length = aws->get_button_length();
379
380    aws->button_length(PT_SERVERNAME_LENGTH+1);
381    aws->callback(makeCreateWindowCallback(create_PTSERVER_selection_window, awar_ptserver->awar_name));
382    aws->create_button("CURR_PT_SERVER", awar_buttontext_name);
383
384    aws->button_length(old_button_length);
385
386    free(readable_name);
387    free(awar_buttontext_name);
388}
389void awt_create_PTSERVER_selection_list(AW_window *aws, const char *varname) {
390    (new PT_selection(aws->create_selection_list(varname, true)))->refresh();
391}
392
393// -------------------------------------------------
394//      selection boxes on editor configurations
395
396struct AWT_configuration_selection : public AW_DB_selection {
397    AWT_configuration_selection(AW_selection_list *sellist_, GBDATA *gb_configuration_data)
398        : AW_DB_selection(sellist_, gb_configuration_data)
399    {}
400
401    int getConfigInfo(const char *name, string& comment) {
402        // returns number of species in config + sets comment
403        GB_ERROR   error;
404        GBT_config cfg(get_gb_main(), name, error);
405
406        int count = 0;
407        if (!error) {
408            const char *cmt = cfg.get_comment();
409            comment         = cmt ? cmt : "";
410            for (int area = 0; area<2; ++area) {
411                GBT_config_parser parser(cfg, area);
412                while (1) {
413                    const GBT_config_item& item = parser.nextItem(error);
414                    if (error || item.type == CI_END_OF_CONFIG) break;
415                    if (item.type == CI_SPECIES) ++count;
416                }
417            }
418        }
419        else {
420            comment = "";
421        }
422        return count;
423    }
424
425    void fill() OVERRIDE {
426        ConstStrArray config;
427        GBT_get_configuration_names(config, get_gb_main());
428
429        if (!config.empty()) {
430            int     maxlen   = 0;
431            int     maxcount = 0;
432            int    *count    = new int[config.size()];
433            string *comment  = new string[config.size()];
434
435            for (int c = 0; config[c]; ++c) {
436                maxlen   = max(maxlen, int(strlen(config[c])));
437                count[c] = getConfigInfo(config[c], comment[c]);
438                maxcount = max(maxcount, count[c]);
439            }
440            int maxdigits = calc_digits(maxcount);
441            for (int c = 0; config[c]; ++c) {
442                int digits = calc_digits(count[c]);
443                insert(GBS_global_string("%-*s %*s(%i) %s", maxlen, config[c], (maxdigits-digits), "", count[c], comment[c].c_str()), config[c]);
444            }
445            delete [] comment;
446            delete [] count;
447        }
448        insert_default(DISPLAY_NONE, NO_CONFIG_SELECTED);
449    }
450};
451
452AW_DB_selection *awt_create_CONFIG_selection_list(GBDATA *gb_main, AW_window *aws, const char *varname, bool fallback2default) {
453    GBDATA *gb_configuration_data;
454    {
455        GB_transaction ta(gb_main);
456        gb_configuration_data = GB_search(gb_main, CONFIG_DATA_PATH, GB_CREATE_CONTAINER);
457    }
458    AW_selection_list           *sellist = aws->create_selection_list(varname, 40, 15, fallback2default);
459    AWT_configuration_selection *confSel = new AWT_configuration_selection(sellist, gb_configuration_data);
460    confSel->refresh();
461    return confSel;
462}
463
464char *awt_create_CONFIG_string(GBDATA *gb_main) {
465    // returns semicolon-separated string containing configuration names
466    // (or NULp if no configs exist)
467
468    GB_push_transaction(gb_main);
469
470    ConstStrArray config;
471    GBT_get_configuration_names(config, gb_main);
472
473    char *result = NULp;
474
475    if (!config.empty()) {
476        GBS_strstruct *out = GBS_stropen(1000);
477        for (int c = 0; config[c]; c++) {
478            if (c>0) GBS_chrcat(out, ';');
479            GBS_strcat(out, config[c]);
480        }
481        result = GBS_strclose(out);
482    }
483
484    GB_pop_transaction(gb_main);
485    return result;
486}
487
488// ----------------------
489//      SAI selection
490
491
492static char *get_SAI_description(GBDATA *gb_extended) {
493    const char *name     = GBT_read_name(gb_extended);
494    GBDATA     *gb_group = GB_entry(gb_extended, "sai_group");
495
496    if (gb_group) {
497        const char *group = GB_read_char_pntr(gb_group);
498        return GBS_global_string_copy("[%s] %s", group, name);;
499    }
500    return ARB_strdup(name);
501}
502
503const SaiSelectionlistFilterCallback& awt_std_SAI_filter_cb() {
504    static SaiSelectionlistFilterCallback std_SAI_filter_cb = makeSaiSelectionlistFilterCallback(get_SAI_description);
505    return std_SAI_filter_cb;
506}
507
508class SAI_selection : public AW_DB_selection { // derived from a Noncopyable
509    SaiSelectionlistFilterCallback filter_cb;
510
511public:
512
513    SAI_selection(AW_selection_list *sellist_, GBDATA *gb_sai_data, const SaiSelectionlistFilterCallback& fcb)
514        : AW_DB_selection(sellist_, gb_sai_data),
515          filter_cb(fcb)
516    {}
517
518    void fill() OVERRIDE;
519};
520
521void SAI_selection::fill() {
522    AW_selection_list *sel = get_sellist();
523    sel->clear();
524
525    GBDATA         *gb_main = get_gb_main();
526    GB_transaction  ta(gb_main);
527
528    for (GBDATA *gb_extended = GBT_first_SAI(gb_main);
529         gb_extended;
530         gb_extended = GBT_next_SAI(gb_extended))
531    {
532        char *res = filter_cb(gb_extended);
533        if (res) {
534            sel->insert(res, GBT_read_name(gb_extended));
535            free(res);
536        }
537    }
538    sel->sort(false, false);
539
540    sel->insert_default(DISPLAY_NONE, "");
541    sel->update();
542}
543
544class SAI_sellst_spec : public SelectionListSpec, virtual Noncopyable {
545    GBDATA                         *gb_main;
546    SaiSelectionlistFilterCallback  filter_cb;
547
548    AW_DB_selection *create(AW_selection_list *sellist) const {
549        GB_transaction ta(gb_main);
550        return new SAI_selection(sellist, GBT_get_SAI_data(gb_main), filter_cb);
551    }
552
553public:
554    SAI_sellst_spec(const char *awar_name_, GBDATA *gb_main_, const SaiSelectionlistFilterCallback& fcb)
555        : SelectionListSpec(awar_name_),
556          gb_main(gb_main_),
557          filter_cb(fcb)
558    {
559        // Warning: do not use different filters for same awar! (wont work as expected) // @@@ add assertion against
560    }
561
562    const char *get_macro_id() const { return "SELECT_SAI"; }
563    const char *get_title() const { return "Select SAI"; }
564};
565
566void awt_popup_SAI_selection_list(AW_window *, const char *awar_name, GBDATA *gb_main) {
567    SAI_sellst_spec spec(awar_name, gb_main, awt_std_SAI_filter_cb());
568    spec.popup();
569}
570
571AW_DB_selection *awt_create_SAI_selection_list(GBDATA *gb_main, AW_window *aws, const char *varname, bool fallback2default, const SaiSelectionlistFilterCallback& fcb) {
572    /* Selection list for SAIs
573     * only shows those SAIs for which fcb returns a string (string must be a heap copy)
574     */
575    SAI_sellst_spec spec(varname, gb_main, fcb);
576    return spec.create_list(aws, fallback2default);
577}
578
579void awt_create_SAI_selection_button(GBDATA *gb_main, AW_window *aws, const char *varname, const SaiSelectionlistFilterCallback& fcb) {
580    (new SAI_sellst_spec(varname, gb_main, fcb))->createButton(aws); // do not free (bound to callback)
581}
582
583// --------------------------------------------------
584//      save/load selection content to/from file
585
586static GB_ERROR standard_list2file(const CharPtrArray& display, const CharPtrArray& value, StrArray& line) {
587    GB_ERROR error = NULp;
588    for (size_t i = 0; i<display.size() && !error; ++i) {
589        const char *disp = display[i];
590
591        if (disp[0] == '#') { // would interpret as comment when loaded
592            error = "Invalid character '#' at start of displayed text (won't load)";
593        }
594        else {
595            if (strchr(disp, ',')) {
596                // would be interpreted as separator between display and value on load
597                error = "Invalid character ',' in displayed text (won't load correctly)";
598            }
599            else {
600                awt_assert(!strchr(disp, '\n'));
601
602                const char *val = value[i];
603                if (strcmp(disp, val) == 0) {
604                    line.put(ARB_strdup(disp));
605                }
606                else {
607                    char *escaped = GBS_escape_string(val, "\n", '\\');
608                    line.put(GBS_global_string_copy("%s,%s", disp, escaped));
609                    free(escaped);
610                }
611            }
612        }
613    }
614    return NULp;
615}
616
617
618
619static GB_ERROR standard_file2list(const CharPtrArray& line, StrArray& display, StrArray& value) {
620    for (size_t i = 0; i<line.size(); ++i) {
621        if (line[i][0] == '#') continue; // ignore comments
622
623        const char *comma = strchr(line[i], ',');
624        if (comma) {
625            display.put(ARB_strpartdup(line[i], comma-1));
626
627            comma++;
628            const char *rest      = comma+strspn(comma, " \t");
629            char       *unescaped = GBS_unescape_string(rest, "\n", '\\');
630            value.put(unescaped);
631        }
632        else {
633            display.put(ARB_strdup(line[i]));
634            value.put(ARB_strdup(line[i]));
635        }
636    }
637
638    return NULp;
639}
640
641StorableSelectionList::StorableSelectionList(const TypedSelectionList& tsl_)
642    : tsl(tsl_),
643      list2file(standard_list2file), 
644      file2list(standard_file2list)
645{}
646
647inline char *get_shared_sellist_awar_base(const TypedSelectionList& typedsellst) {
648    return GBS_global_string_copy("tmp/sellist/%s", typedsellst.get_shared_id());
649}
650inline char *get_shared_sellist_awar_name(const TypedSelectionList& typedsellst, const char *name) {
651    char *base      = get_shared_sellist_awar_base(typedsellst);
652    char *awar_name = GBS_global_string_copy("%s/%s", base, name);
653    free(base);
654    return awar_name;
655}
656
657GB_ERROR StorableSelectionList::save(const char *filename, long number_of_lines) const {
658    // number_of_lines == 0 -> save all lines (otherwise truncate after 'number_of_lines')
659
660    StrArray           display, values;
661    AW_selection_list *sellist = tsl.get_sellist();
662
663    sellist->to_array(display, false);
664    sellist->to_array(values, true);
665
666    awt_assert(display.size() == values.size());
667
668    if (number_of_lines>0) { // limit number of lines?
669        display.resize(number_of_lines);
670        values.resize(number_of_lines);
671    }
672
673    GB_ERROR error = NULp;
674    if (display.size()<1) {
675        error = "List is empty (did not save)";
676    }
677    else {
678        StrArray line;
679        error = list2file(display, values, line);
680        if (!error) {
681            if (line.size()<1) {
682                error = "list>file conversion produced nothing (internal error)";
683            }
684            else {
685                FILE *out = fopen(filename, "wt");
686                if (!out) {
687                    error = GB_IO_error("writing", filename);
688                }
689                else {
690                    const char *warning = NULp;
691                    for (size_t i = 0; i<line.size(); ++i) {
692                        if (!warning && strchr(line[i], '\n')) {
693                            warning = "Warning: Saved content contains LFs (loading will be impossible)";
694                        }
695                        fputs(line[i], out);
696                        fputc('\n', out);
697                    }
698                    fclose(out);
699                    error = warning;
700                }
701            }
702        }
703    }
704
705    return error;
706}
707
708
709
710inline char *string2heapcopy(const string& s) {
711    return ARB_strndup(s.c_str(), s.length());
712}
713
714GB_ERROR StorableSelectionList::load(const char *filemask, bool append) const {
715    GB_ERROR error = NULp;
716    StrArray fnames;
717
718    if (GB_is_directory(filemask)) {
719        error = GBS_global_string("refusing to load all files from directory\n"
720                                  "'%s'\n"
721                                  "(one possibility is to enter '*.%s')'",
722                                  filemask, get_filter());
723    }
724    else {
725        GBS_read_dir(fnames, filemask, NULp);
726        error = GB_incur_error_if(fnames.empty());
727    }
728
729    StrArray lines;
730    for (int f = 0; fnames[f] && !error; ++f) {
731        FILE *in = fopen(fnames[f], "rb");
732        if (!in) {
733            error = GB_IO_error("reading", fnames[f]);
734        }
735        else {
736            BufferedFileReader file(fnames[f], in);
737            string line;
738            while (file.getLine(line)) {
739                if (!line.empty()) lines.put(string2heapcopy(line));
740            }
741        }
742    }
743
744    AW_selection_list *sellist = tsl.get_sellist();
745    if (!append) sellist->clear();
746
747    if (!error) {
748        StrArray displayed, values;
749        error = file2list(lines, displayed, values);
750        if (!error) {
751            int dsize = displayed.size();
752            int vsize = values.size();
753
754            if (dsize != vsize) {
755                error = GBS_global_string("Error in translation (value/display mismatch: %i!=%i)", vsize, dsize);
756            }
757            else {
758                for (int i = 0; i<dsize; ++i) {
759                    sellist->insert(displayed[i], values[i]);
760                }
761                sellist->insert_default("", "");
762            }
763        }
764    }
765
766    if (error) {
767        sellist->insert_default(GBS_global_string("Error: %s", error), "");
768    }
769    sellist->update();
770
771    return error;
772}
773
774static void save_list_cb(AW_window *aww, const StorableSelectionList *storabsellist) {
775    const TypedSelectionList& typedsellist = storabsellist->get_typedsellist();
776
777    char    *awar_prefix = get_shared_sellist_awar_base(typedsellist);
778    char    *bline_anz   = get_shared_sellist_awar_name(typedsellist, "line_anz");
779    AW_root *aw_root     = aww->get_root();
780    char    *filename    = AW_get_selected_fullname(aw_root, awar_prefix);
781
782    long     lineLimit = aw_root->awar(bline_anz)->read_int();
783    GB_ERROR error     = storabsellist->save(filename, lineLimit);
784
785    if (!error) AW_refresh_fileselection(aw_root, awar_prefix);
786    aww->hide_or_notify(error);
787
788    free(filename);
789    free(bline_anz);
790    free(awar_prefix);
791}
792
793static void load_list_cb(AW_window *aww, const StorableSelectionList *storabsellist) {
794    const TypedSelectionList& typedsellist = storabsellist->get_typedsellist();
795
796    AW_root  *aw_root     = aww->get_root();
797    char     *awar_prefix = get_shared_sellist_awar_base(typedsellist);
798    char     *awar_append = get_shared_sellist_awar_name(typedsellist, "append");
799    bool      append      = aw_root->awar(awar_append)->read_int();
800    char     *filename    = AW_get_selected_fullname(aw_root, awar_prefix);
801    GB_ERROR  error       = storabsellist->load(filename, append);
802
803    aww->hide_or_notify(error);
804
805    free(filename);
806    free(awar_append);
807    free(awar_prefix);
808}
809
810AW_window *create_save_box_for_selection_lists(AW_root *aw_root, const StorableSelectionList *storabsellist) {
811    const TypedSelectionList& typedsellist = storabsellist->get_typedsellist();
812
813    char *awar_base     = get_shared_sellist_awar_base(typedsellist);
814    char *awar_line_anz = get_shared_sellist_awar_name(typedsellist, "line_anz");
815    {
816        char *def_name = GBS_string_2_key(typedsellist.whats_contained());
817        AW_create_fileselection_awars(aw_root, awar_base, ".", storabsellist->get_filter(), def_name);
818        free(def_name);
819        aw_root->awar_int(awar_line_anz, 0, AW_ROOT_DEFAULT);
820    }
821
822    AW_window_simple *aws = new AW_window_simple;
823
824    char *window_id    = GBS_global_string_copy("SAVE_SELECTION_BOX_%s", typedsellist.get_unique_id());
825    char *window_title = GBS_global_string_copy("Save %s", typedsellist.whats_contained());
826
827    aws->init(aw_root, window_id, window_title);
828    aws->load_xfig("sl_s_box.fig");
829
830    aws->button_length(10);
831
832    aws->at("cancel");
833    aws->callback(AW_POPDOWN);
834    aws->create_button("CANCEL", "CANCEL", "C");
835
836    aws->at("save");
837    aws->highlight();
838    aws->callback(makeWindowCallback(save_list_cb, storabsellist));
839    aws->create_button("SAVE", "SAVE", "S");
840
841    aws->at("nlines");
842    aws->create_option_menu(awar_line_anz, true);
843    aws->insert_default_option("all",   "a", 0);
844    aws->insert_option        ("10",    "",  10);
845    aws->insert_option        ("50",    "",  50);
846    aws->insert_option        ("100",   "",  100);
847    aws->insert_option        ("500",   "",  500);
848    aws->insert_option        ("1000",  "",  1000);
849    aws->insert_option        ("5000",  "",  5000);
850    aws->insert_option        ("10000", "",  10000);
851    aws->update_option_menu();
852
853    AW_create_standard_fileselection(aws, awar_base);
854
855    free(window_title);
856    free(window_id);
857    free(awar_line_anz);
858    free(awar_base);
859
860    aws->recalc_pos_atShow(AW_REPOS_TO_MOUSE);
861
862    return aws;
863}
864
865AW_window *create_load_box_for_selection_lists(AW_root *aw_root, const StorableSelectionList *storabsellist) {
866    const TypedSelectionList& typedsellist = storabsellist->get_typedsellist();
867
868    char *awar_base_name = get_shared_sellist_awar_base(typedsellist);
869    char *awar_append    = get_shared_sellist_awar_name(typedsellist, "append");
870
871    AW_create_fileselection_awars(aw_root, awar_base_name, ".", storabsellist->get_filter(), "");
872    aw_root->awar_int(awar_append, 1); // append is default ( = old behavior)
873
874    AW_window_simple *aws = new AW_window_simple;
875
876    char *window_id    = GBS_global_string_copy("LOAD_SELECTION_BOX_%s", typedsellist.get_unique_id());
877    char *window_title = GBS_global_string_copy("Load %s", typedsellist.whats_contained());
878
879    aws->init(aw_root, window_id, window_title);
880    aws->load_xfig("sl_l_box.fig");
881
882    aws->at("cancel");
883    aws->callback(AW_POPDOWN);
884    aws->create_button("CANCEL", "CANCEL", "C");
885
886    aws->at("load");
887    aws->highlight();
888    aws->callback(makeWindowCallback(load_list_cb, storabsellist));
889    aws->create_button("LOAD", "LOAD", "L");
890
891    aws->at("append");
892    aws->label("Append?");
893    aws->create_toggle(awar_append);
894
895    AW_create_fileselection(aws, awar_base_name, "", "PWD", ANY_DIR, true);
896
897    aws->recalc_pos_atShow(AW_REPOS_TO_MOUSE);
898
899    free(window_title);
900    free(window_id);
901    free(awar_append);
902    free(awar_base_name);
903
904    return aws;
905}
906
907void awt_clear_selection_list_cb(AW_window *, AW_selection_list *sellist) {
908    sellist->clear();
909    sellist->insert_default("", "");
910    sellist->update();
911}
912
913void create_print_box_for_selection_lists(AW_window *aw_window, const TypedSelectionList *typedsellist) {
914    char *data = typedsellist->get_sellist()->get_content_as_string(0);
915    AWT_create_ascii_print_window(aw_window->get_root(), data, typedsellist->whats_contained());
916    free(data);
917}
918
919AW_window *awt_create_load_box(AW_root     *aw_root,
920                               const char  *action,
921                               const char  *what,
922                               const char  *default_directory,
923                               const char  *file_extension,
924                               char       **set_file_name_awar,
925                               const WindowCallback& ok_cb,
926                               const WindowCallback& close_cb,
927                               const char *close_button_text)
928{
929    /* general purpose file selection box
930     *
931     * 'action' describes what is intended to be done (e.g. "Load").
932     * used for window title and button.
933     *
934     * 'what' describes what is going to be loaded (e.g. "destination database")
935     * It is also used to create the awars for the filebox, i.e. same description for multiple
936     * fileboxes makes them share the awars.
937     *
938     * if 'set_file_name_awar' is non-NULp, it'll be set to a heap-copy of the awar-name
939     * containing the full selected filename.
940     *
941     * 'default_directory' specifies the directory opened in the filebox
942     *
943     * 'file_extension' specifies the filter to be used (which files are shown)
944     *
945     * You have to provide an 'ok_cb', which will be called when 'OK' is pressed.
946     * Optionally you may pass a 'close_cb' which will be called when 'CLOSE' is pressed.
947     * If not given, AW_POPDOWN will be called.
948     *
949     * Both callbacks will be called as callbacks of the load-box-window.
950     * The load-box does not popdown, the callback has to do that.
951     *
952     * Optionally you may also pass the button text for the 'CLOSE'-button (e.g. 'EXIT' or 'Abort')
953     */
954
955
956    char *what_key  = GBS_string_2_key(what);
957    char *base_name = GBS_global_string_copy("tmp/load_box_%s", what_key);
958
959    AW_create_fileselection_awars(aw_root, base_name, default_directory, file_extension, "");
960
961    if (set_file_name_awar) {
962        *set_file_name_awar = GBS_global_string_copy("%s/file_name", base_name);
963    }
964
965    AW_window_simple *aws = new AW_window_simple;
966    {
967        char title[100];
968        sprintf(title, "%s %s", action, what);
969        aws->init(aw_root, title, title);
970        aws->load_xfig("load_box.fig");
971    }
972
973    aws->at("close");
974    aws->callback(close_cb);
975    if (close_button_text) {
976        aws->create_button("CLOSE", close_button_text, "");
977    }
978    else {
979        aws->create_button("CLOSE", "CLOSE", "C");
980    }
981
982#if 0
983    // @@@ allow to pass helpfile
984    aws->at("help");
985    aws->callback(makeHelpCallback(""));
986    aws->create_button("HELP", "HELP");
987#endif
988
989    aws->at("go");
990    aws->callback(ok_cb);
991    aws->create_autosize_button("GO", action);
992
993    AW_create_standard_fileselection(aws, base_name);
994    free(base_name);
995    free(what_key);
996    aws->recalc_pos_atShow(AW_REPOS_TO_MOUSE);
997
998    return aws;
999}
1000
1001// --------------------------------------------------------------------------------
1002
1003#define SUBSET_NOELEM_DISPLAY "<none>"
1004
1005class AW_subset_selection : public AW_selection { // only works with string-type selections
1006    AW_selection_list& parent_sellist;
1007
1008    SubsetChangedCb subChanged_cb;
1009    AW_CL           cl_user;
1010
1011    static void finish_fill_box(AW_selection_list *parent_sellist, AW_selection_list *sub_sellist) {
1012        sub_sellist->insert_default(parent_sellist->get_default_display(), *parent_sellist->get_default_value());
1013        sub_sellist->update();
1014    }
1015
1016    static AW_selection_list *create_box(AW_window *aww, AW_selection_list& parent_sellist) {
1017        const char *parent_awar_name = parent_sellist.get_awar_name();
1018        awt_assert(parent_awar_name[0] != '/');
1019        awt_assert(parent_sellist.get_awar_type() == GB_STRING); // only impl for strings
1020
1021        AW_root *aw_root   = aww->get_root();
1022        char    *awar_name = GBS_global_string_copy("tmp/subsel/%s", parent_awar_name);
1023
1024        aw_root->awar_string(awar_name);
1025
1026        AW_selection_list *sub_sellist = aww->create_selection_list(awar_name, true);
1027        finish_fill_box(&parent_sellist, sub_sellist);
1028
1029        free(awar_name);
1030
1031        return sub_sellist;
1032    }
1033
1034    void callChangedCallback(bool interactive_change) { if (subChanged_cb) subChanged_cb(this, interactive_change, cl_user); }
1035
1036public:
1037    AW_subset_selection(AW_window *aww, AW_selection_list& parent_sellist_, SubsetChangedCb subChanged_cb_, AW_CL cl_user_)
1038        : AW_selection(create_box(aww, parent_sellist_)),
1039          parent_sellist(parent_sellist_),
1040          subChanged_cb(subChanged_cb_),
1041          cl_user(cl_user_)
1042    {
1043        callChangedCallback(false);
1044    }
1045
1046    AW_selection_list *get_parent_sellist() const { return &parent_sellist; }
1047
1048    const char *default_select_value() const { return parent_sellist.get_default_value()->get_string(); }
1049    const char *default_select_display() const { return parent_sellist.get_default_display(); }
1050
1051    void fill() OVERRIDE { awt_assert(0); } // unused
1052
1053    void collect_subset_cb(awt_collect_mode what) {
1054        AW_selection_list *subset_list = get_sellist();
1055        AW_selection_list *whole_list  = get_parent_sellist();
1056
1057        switch(what) {
1058            case ACM_FILL:
1059                for (AW_selection_list_iterator listEntry(whole_list); listEntry; ++listEntry) {
1060                    if (subset_list->get_index_of(*listEntry.get_value()) == -1) { // only add not already existing elements
1061                        subset_list->insert(listEntry.get_displayed(), *listEntry.get_value());
1062                    }
1063                }
1064                finish_fill_box(whole_list, subset_list);
1065                break;
1066
1067            case ACM_ADD: {
1068                if (!whole_list->default_is_selected()) {
1069                    AW_scalar selected  = whole_list->get_awar_value();
1070                    int       src_index = whole_list->get_index_of(selected);
1071
1072                    if (subset_list->get_index_of(selected) == -1) { // not yet in subset_list
1073                        AW_selection_list_iterator entry(whole_list, src_index);
1074                        subset_list->insert(entry.get_displayed(), *entry.get_value());
1075                        subset_list->update();
1076                    }
1077
1078                    subset_list->set_awar_value(selected);      // position right side to newly added or already existing alignment
1079                    whole_list->select_element_at(src_index+1); // go down 1 position on left side
1080                }
1081
1082                break;
1083            }
1084            case ACM_REMOVE: {
1085                if (!subset_list->default_is_selected()) {
1086                    AW_scalar selected     = subset_list->get_awar_value();
1087                    int       old_position = subset_list->get_index_of(selected);
1088
1089                    subset_list->delete_element_at(old_position);
1090                    finish_fill_box(whole_list, subset_list);
1091
1092                    subset_list->select_element_at(old_position);
1093                    whole_list->set_awar_value(selected); // set left selection to deleted alignment
1094                }
1095                break;
1096            }
1097            case ACM_EMPTY:
1098                subset_list->clear();
1099                finish_fill_box(whole_list, subset_list);
1100                break;
1101        }
1102        callChangedCallback(true);
1103    }
1104    void reorder_subset_cb(awt_reorder_mode dest) {
1105        AW_selection_list *subset_list = get_sellist();
1106
1107        if (!subset_list->default_is_selected()) {
1108            AW_scalar selected = subset_list->get_awar_value();
1109
1110            StrArray listContent;
1111            subset_list->to_array(listContent, true);
1112
1113            int old_pos = listContent.index_of(selected.get_string());
1114            if (old_pos >= 0) {
1115                int new_pos = 0;
1116                switch (dest) {
1117                    case ARM_TOP:    new_pos= 0;         break;
1118                    case ARM_UP:     new_pos= old_pos-1; break;
1119                    case ARM_DOWN:   new_pos= old_pos+1; break;
1120                    case ARM_BOTTOM: new_pos= -1;        break;
1121                }
1122                if (old_pos != new_pos) {
1123                    listContent.move(old_pos, new_pos);
1124                    subset_list->init_from_array(listContent, subset_list->get_default_display(), subset_list->get_default_value()->get_string());
1125                }
1126            }
1127        }
1128        callChangedCallback(true);
1129    }
1130
1131    void delete_entries_missing_in_parent() {
1132        // check subset for entries missing in parent,
1133        // delete these and update
1134        typedef std::set<const char*, charpLess> Entries;
1135
1136        bool    deleted = false;
1137        Entries pEntry;
1138        {
1139            AW_selection_list_iterator pIter(&parent_sellist);
1140            while (pIter) {
1141                pEntry.insert(pIter.get_value()->get_string());
1142                ++pIter;
1143            }
1144        }
1145
1146        AW_selection_list *subsel = get_sellist();
1147        int                size   = subsel->size();
1148
1149        for (int i = 0; i<size; ++i) {
1150            if (pEntry.find(subsel->get_value_at(i)->get_string()) == pEntry.end()) { // entry missing in parent list
1151                subsel->delete_element_at(i);
1152                deleted = true;
1153                --i; --size;
1154            }
1155        }
1156
1157        if (deleted) {
1158            subsel->update();
1159            callChangedCallback(false);
1160        }
1161    }
1162
1163    void fill_entries_matching_values(const CharPtrArray& values) {
1164        AW_selection_list *subset_list = get_sellist();
1165        subset_list->clear();
1166
1167        for (size_t e = 0; e<values.size(); ++e) {
1168            const char *value = values[e];
1169
1170            AW_selection_list_iterator pIter(&parent_sellist);
1171            while (pIter) {
1172                if (strcmp(pIter.get_value()->get_string(), value) == 0) {
1173                    subset_list->insert(pIter.get_displayed(), *pIter.get_value());
1174                    break;
1175                }
1176                ++pIter;
1177            }
1178        }
1179
1180        finish_fill_box(&parent_sellist, subset_list);
1181        callChangedCallback(false);
1182    }
1183};
1184
1185static void collect_subset_cb(AW_window *, awt_collect_mode what, AW_subset_selection *subsel) { subsel->collect_subset_cb(what); }
1186static void reorder_subset_cb(AW_window *, awt_reorder_mode dest, AW_subset_selection *subsel) { subsel->reorder_subset_cb(dest); }
1187
1188static void correct_subselection_cb(AW_selection_list *IF_ASSERTION_USED(parent_sel), AW_CL cl_subsel) {
1189    AW_subset_selection *subsel = (AW_subset_selection*)cl_subsel;
1190    aw_assert(subsel->get_parent_sellist() == parent_sel);
1191    subsel->delete_entries_missing_in_parent();
1192}
1193
1194AW_selection *awt_create_subset_selection_list(AW_window *aww, AW_selection_list *parent_selection, const char *at_box, const char *at_add, const char *at_sort, bool autocorrect_subselection, SubsetChangedCb subChanged_cb, AW_CL cl_user) {
1195    awt_assert(parent_selection);
1196
1197    aww->at(at_box);
1198    int x_list = aww->get_at_xposition();
1199
1200    AW_subset_selection *subsel = new AW_subset_selection(aww, *parent_selection, subChanged_cb, cl_user);
1201
1202    aww->button_length(0);
1203
1204    aww->at(at_add);
1205    int x_buttons = aww->get_at_xposition();
1206
1207    bool move_rightwards = x_list>x_buttons;
1208    awt_create_collect_buttons(aww, move_rightwards, collect_subset_cb, subsel);
1209
1210    aww->at(at_sort);
1211    awt_create_order_buttons(aww, reorder_subset_cb, subsel);
1212
1213    if (autocorrect_subselection) parent_selection->set_update_callback(correct_subselection_cb, AW_CL(subsel));
1214
1215    return subsel;
1216}
1217
1218void awt_set_subset_selection_content(AW_selection *subset_sel_, const CharPtrArray& values) {
1219    /*! sets content of a subset-selection-list
1220     * @param subset_sel_ selection list created by awt_create_subset_selection_list()
1221     * @param values      e.g. retrieved using subset_sel_->get_values()
1222     */
1223    AW_subset_selection *subset_sel = dynamic_cast<AW_subset_selection*>(subset_sel_);
1224    if (subset_sel) subset_sel->fill_entries_matching_values(values);
1225}
1226
1227AW_selection_list *awt_create_selection_list_with_input_field(AW_window *aww, const char *awar_name, const char *at_box, const char *at_field) {
1228    /*! create selection_list and input_field on awar 'awar_name'
1229     * @param aww window where to create gui elements
1230     * @param at_box position of selection_list
1231     * @param at_field position of input_field
1232     */
1233
1234    aww->at(at_field);
1235    aww->create_input_field(awar_name);
1236    aww->at(at_box);
1237    return aww->create_selection_list(awar_name, false);
1238}
1239
1240
Note: See TracBrowser for help on using the repository browser.