source: tags/ms_r16q2/AWT/AWT_sel_boxes.cxx

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