source: trunk/AWT/AWT_sel_boxes.cxx

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