source: branches/tree/AWT/AWT_sel_boxes.cxx

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