source: trunk/SL/GUI_TK/sel_boxes.cxx

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