source: branches/alilink/NTREE/NT_edconf.cxx

Last change on this file was 18126, checked in by westram, 5 years ago
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 49.1 KB
Line 
1// =============================================================== //
2//                                                                 //
3//   File      : NT_edconf.cxx                                     //
4//   Purpose   :                                                   //
5//                                                                 //
6//   Institute of Microbiology (Technical University Munich)       //
7//   http://www.arb-home.de/                                       //
8//                                                                 //
9// =============================================================== //
10
11#include "NT_local.h"
12#include "ad_trees.h"
13
14#include <TreeNode.h>
15#include <TreeDisplay.hxx>
16#include <RegExpr.hxx>
17
18#include <awt_sel_boxes.hxx>
19#include <awt_misc.hxx>
20#include <awt_config_manager.hxx>
21#include <awt_modules.hxx>
22
23#include <aw_awars.hxx>
24#include <aw_root.hxx>
25#include <aw_msg.hxx>
26#include <aw_select.hxx>
27
28#include <ad_config.h>
29#include <ad_cb_prot.h>
30
31#include <arb_strbuf.h>
32#include <arb_global_defs.h>
33
34#include <map>
35#include <set>
36#include <string>
37
38using namespace std;
39
40// AWT_canvas-local (%i=canvas-id):
41#define AWAR_CL_SELECTED_CONFIGS       "configuration_data/win%i/selected"
42#define AWAR_CL_DISPLAY_CONFIG_MARKERS "configuration_data/win%i/display"
43
44#define AWAR_CONFIG_COMMENT "tmp/configuration/comment"
45
46enum extractType {
47    CONF_EXTRACT,
48    CONF_MARK,
49    CONF_UNMARK,
50    CONF_INVERT,
51    CONF_COMBINE // logical AND
52};
53static void nt_extract_configuration(UNFIXED, extractType ext_type);
54
55typedef map<string, string> ConfigHits; // key=speciesname; value[markerIdx]==1 -> highlighted
56
57class ConfigMarkerDisplay FINAL_TYPE : public MarkerDisplay, virtual Noncopyable {
58    GBDATA                  *gb_main;
59    SmartPtr<ConstStrArray>  config; // configuration names
60    StrArray                 errors; // config load-errors
61    ConfigHits               hits;
62
63    void updateHits() {
64        flush_cache();
65        hits.clear();
66        errors.erase();
67        for (int c = 0; c<size(); ++c) {
68            GB_ERROR   error;
69            GBT_config cfg(gb_main, (*config)[c], error);
70
71            for (int area = 0; area<=1 && !error; ++area) {
72                GBT_config_parser cparser(cfg, area);
73
74                while (1) {
75                    const GBT_config_item& item = cparser.nextItem(error);
76                    if (error || item.type == CI_END_OF_CONFIG) break;
77                    if (item.type == CI_SPECIES) {
78                        ConfigHits::iterator found = hits.find(item.name);
79                        if (found == hits.end()) {
80                            string h(size(), '0');
81                            h[c]            = '1';
82                            hits[item.name] = h;
83                        }
84                        else {
85                            (found->second)[c] = '1';
86                        }
87                    }
88                }
89            }
90
91            errors.put(ARB_strdup(error ? error : ""));
92        }
93    }
94
95public:
96    ConfigMarkerDisplay(SmartPtr<ConstStrArray> config_, GBDATA *gb_main_)
97        : MarkerDisplay(config_->size()),
98          gb_main(gb_main_),
99          config(config_)
100    {
101        updateHits();
102    }
103    const char *get_marker_name(int markerIdx) const OVERRIDE {
104        const char *error = errors[markerIdx];
105        const char *name  = (*config)[markerIdx];
106        if (error && error[0]) return GBS_global_string("%s (Error: %s)", name, error);
107        return name;
108    }
109    void retrieve_marker_state(const char *speciesName, NodeMarkers& node) OVERRIDE {
110        ConfigHits::const_iterator found = hits.find(speciesName);
111        if (found != hits.end()) {
112            const string& hit = found->second;
113
114            for (int c = 0; c<size(); ++c) {
115                if (hit[c] == '1') node.incMarker(c);
116            }
117        }
118        node.incNodeSize();
119    }
120
121    void handle_click(int markerIdx, AW_MouseButton button, AWT_graphic_exports& exports) OVERRIDE {
122        if (button == AW_BUTTON_LEFT || button == AW_BUTTON_RIGHT) {
123            AW_root::SINGLETON->awar(AWAR_CONFIGURATION)->write_string(get_marker_name(markerIdx)); // select config of clicked marker
124            if (button == AW_BUTTON_RIGHT) { // extract configuration
125                nt_extract_configuration(NULp, CONF_EXTRACT);
126                exports.request_structure_update(); // needed to recalculate branch colors
127            }
128        }
129    }
130};
131
132inline bool displays_config_markers(MarkerDisplay *md) { return dynamic_cast<ConfigMarkerDisplay*>(md); }
133
134#define CONFIG_SEPARATOR "\1"
135
136inline AW_awar *get_canvas_awar(const char *awar_name_format, int canvas_id) {
137    return AW_root::SINGLETON->awar_no_error(GBS_global_string(awar_name_format, canvas_id));
138}
139inline AW_awar *get_config_awar        (int canvas_id) { return get_canvas_awar(AWAR_CL_SELECTED_CONFIGS,       canvas_id); }
140inline AW_awar *get_display_toggle_awar(int canvas_id) { return get_canvas_awar(AWAR_CL_DISPLAY_CONFIG_MARKERS, canvas_id); }
141
142static SmartPtr<ConstStrArray> get_selected_configs_from_awar(int canvas_id) {
143    // returns configs stored in awar as array (empty array if awar undefined!)
144    SmartPtr<ConstStrArray> config(new ConstStrArray);
145
146    AW_awar *awar = get_config_awar(canvas_id);
147    if (awar) {
148        char *config_str = awar->read_string();
149        GBT_splitNdestroy_string(*config, config_str, CONFIG_SEPARATOR, true);
150    }
151
152    return config;
153}
154static void write_configs_to_awar(int canvas_id, const CharPtrArray& configs) {
155    char *config_str = GBT_join_strings(configs, CONFIG_SEPARATOR[0]);
156    AW_root::SINGLETON->awar(GBS_global_string(AWAR_CL_SELECTED_CONFIGS, canvas_id))->write_string(config_str);
157    free(config_str);
158}
159
160// --------------------------------------------------------------------------------
161
162static AW_selection *selected_configs_list[MAX_NT_WINDOWS] = { MAX_NT_WINDOWS_NULLINIT };
163static bool allow_selection2awar_update = true;
164static bool allow_to_activate_display   = false;
165
166static void init_config_awars(AW_root *root) {
167    root->awar_string(AWAR_CONFIGURATION, DEFAULT_CONFIGURATION, GLOBAL.gb_main);
168}
169static void selected_configs_awar_changed_cb(AW_root *aw_root, TREE_canvas *ntw) {
170    AWT_graphic_tree        *agt    = DOWNCAST(AWT_graphic_tree*, ntw->gfx);
171    int                      ntw_id = ntw->get_index();
172    SmartPtr<ConstStrArray>  config = get_selected_configs_from_awar(ntw_id);
173    bool                     redraw = false;
174
175    if (config->empty() || get_display_toggle_awar(ntw_id)->read_int() == 0) {
176        if (displays_config_markers(agt->get_marker_display())) { // only hide config markers
177            agt->hide_marker_display();
178            redraw = true;
179        }
180    }
181    else {
182        bool activate = allow_to_activate_display || displays_config_markers(agt->get_marker_display());
183
184        if (activate) {
185            init_config_awars(aw_root);
186            ConfigMarkerDisplay *disp = new ConfigMarkerDisplay(config, ntw->gb_main);
187            agt->set_marker_display(disp);
188            redraw = true;
189        }
190    }
191
192    if (selected_configs_list[ntw_id]) { // if configuration_marker_window has been opened
193        // update content of subset-selection (needed when reloading a config-set (not implemented yet) or after renaming a config)
194        LocallyModify<bool> avoid(allow_selection2awar_update, false);
195        awt_set_subset_selection_content(selected_configs_list[ntw_id], *config);
196    }
197
198    if (redraw) AW_root::SINGLETON->awar(AWAR_TREE_REFRESH)->touch();
199}
200
201static void selected_configs_display_awar_changed_cb(AW_root *root, TREE_canvas *ntw) {
202    LocallyModify<bool> allowInteractiveActivation(allow_to_activate_display, true);
203    selected_configs_awar_changed_cb(root, ntw);
204}
205
206static void configs_selectionlist_changed_cb(AW_selection *selected_configs, bool interactive_change, AW_CL ntw_id) {
207    if (allow_selection2awar_update) {
208        LocallyModify<bool> allowInteractiveActivation(allow_to_activate_display, interactive_change);
209
210        StrArray config;
211        selected_configs->get_values(config);
212        write_configs_to_awar(ntw_id, config);
213    }
214}
215
216static void config_modified_cb(GBDATA *gb_cfg_area) { // called with "top_area" AND "middle_area" entry!
217    static GBDATA   *gb_lastname = NULp;
218    static GB_ULONG  lastcall     = 0;
219
220    GBDATA   *gb_name  = GB_entry(GB_get_father(gb_cfg_area), "name");
221    GB_ULONG  thiscall = GB_time_of_day();
222
223    bool is_same_modification = gb_name == gb_lastname && (thiscall == lastcall || thiscall == (lastcall+1));
224    if (!is_same_modification) { // avoid duplicate check if "top_area" and "middle_area" changed (=standard case)
225        // touch all canvas-specific awars that contain 'name'
226        const char *name = GB_read_char_pntr(gb_name);
227
228        for (int canvas_id = 0; canvas_id<MAX_NT_WINDOWS; ++canvas_id) {
229            SmartPtr<ConstStrArray> config = get_selected_configs_from_awar(canvas_id);
230            for (size_t c = 0; c<config->size(); ++c) {
231                if (strcmp((*config)[c], name) == 0) {
232                    get_config_awar(canvas_id)->touch();
233                    break;
234                }
235            }
236        }
237    }
238    gb_lastname = gb_name;
239    lastcall    = thiscall;
240}
241
242#define CONFIG_BASE_PATH "/configuration_data/configuration"
243
244static void install_config_change_callbacks(GBDATA *gb_main) {
245    static bool installed = false;
246    if (!installed) {
247        DatabaseCallback dbcb = makeDatabaseCallback(config_modified_cb);
248        ASSERT_NO_ERROR(GB_add_hierarchy_callback(gb_main, CONFIG_BASE_PATH "/middle_area", GB_CB_CHANGED, dbcb));
249        ASSERT_NO_ERROR(GB_add_hierarchy_callback(gb_main, CONFIG_BASE_PATH "/top_area",    GB_CB_CHANGED, dbcb));
250
251        installed = true;
252    }
253}
254
255void NT_activate_configMarkers_display(TREE_canvas *ntw) {
256    GBDATA *gb_main = ntw->gb_main;
257
258    int      ntw_idx      = ntw->get_index();
259    AW_awar *awar_selCfgs = ntw->awr->awar_string(GBS_global_string(AWAR_CL_SELECTED_CONFIGS, ntw_idx), "", gb_main);
260    awar_selCfgs->add_callback(makeRootCallback(selected_configs_awar_changed_cb, ntw));
261
262    AW_awar *awar_dispCfgs = ntw->awr->awar_int(GBS_global_string(AWAR_CL_DISPLAY_CONFIG_MARKERS, ntw_idx), 1, gb_main);
263    awar_dispCfgs->add_callback(makeRootCallback(selected_configs_display_awar_changed_cb, ntw));
264
265    awar_selCfgs->touch(); // force initial refresh
266    install_config_change_callbacks(gb_main);
267}
268
269// define where to store config-sets (using config-manager):
270#define MANAGED_CONFIGSET_SECTION "configmarkers"
271#define MANAGED_CONFIGSET_ENTRY   "selected_configs"
272
273static void setup_configmarker_config_cb(AWT_config_definition& config, int ntw_id) {
274    AW_awar *selcfg_awar = get_config_awar(ntw_id);
275    nt_assert(selcfg_awar);
276    if (selcfg_awar) {
277        config.add(selcfg_awar->awar_name, MANAGED_CONFIGSET_ENTRY);
278    }
279}
280
281struct ConfigModifier : virtual Noncopyable {
282    virtual ~ConfigModifier() {}
283    virtual const char *modify(const char *old) const = 0;
284
285    bool modifyConfig(ConstStrArray& config) const {
286        bool changed = false;
287        for (size_t i = 0; i<config.size(); ++i) {
288            const char *newContent = modify(config[i]);
289            if (!newContent) {
290                config.remove(i);
291                changed = true;
292            }
293            else if (strcmp(newContent, config[i]) != 0) {
294                config.replace(i, newContent);
295                changed = true;
296            }
297        }
298        return changed;
299    }
300};
301class ConfigRenamer : public ConfigModifier { // derived from Noncopyable
302    const char *oldName;
303    const char *newName;
304    const char *modify(const char *name) const OVERRIDE {
305        return strcmp(name, oldName) == 0 ? newName : name;
306    }
307public:
308    ConfigRenamer(const char *oldName_, const char *newName_)
309        : oldName(oldName_),
310          newName(newName_)
311    {}
312};
313class ConfigDeleter : public ConfigModifier { // derived from Noncopyable
314    const char *toDelete;
315    const char *modify(const char *name) const OVERRIDE {
316        return strcmp(name, toDelete) == 0 ? NULp : name;
317    }
318public:
319    ConfigDeleter(const char *toDelete_)
320        : toDelete(toDelete_)
321    {}
322};
323
324static char *correct_managed_configsets_cb(const char *key, const char *value, AW_CL cl_ConfigModifier) {
325    char *modified_value = NULp;
326    if (strcmp(key, MANAGED_CONFIGSET_ENTRY) == 0) {
327        const ConfigModifier *mod = (const ConfigModifier*)cl_ConfigModifier;
328        ConstStrArray         config;
329        GBT_split_string(config, value, CONFIG_SEPARATOR, true);
330        if (mod->modifyConfig(config)) {
331            modified_value = GBT_join_strings(config, CONFIG_SEPARATOR[0]);
332        }
333    }
334    return modified_value ? modified_value : ARB_strdup(value);
335}
336static void modify_configurations(const ConfigModifier& mod) {
337    for (int canvas_id = 0; canvas_id<MAX_NT_WINDOWS; ++canvas_id) {
338        // modify currently selected configs:
339        SmartPtr<ConstStrArray> config = get_selected_configs_from_awar(canvas_id);
340        if (mod.modifyConfig(*config)) {
341            write_configs_to_awar(canvas_id, *config);
342        }
343    }
344    // change all configuration-sets stored in config-manager (shared by all windows)
345    AWT_modify_managed_configs(GLOBAL.gb_main, MANAGED_CONFIGSET_SECTION, correct_managed_configsets_cb, AW_CL(&mod));
346}
347
348static void configuration_renamed_cb(const char *old_name, const char *new_name) { modify_configurations(ConfigRenamer(old_name, new_name)); }
349static void configuration_deleted_cb(const char *name)                           { modify_configurations(ConfigDeleter(name)); }
350
351static AW_window *create_configuration_marker_window(AW_root *root, TREE_canvas *ntw) {
352    AW_window_simple *aws = new AW_window_simple;
353
354    int ntw_id = ntw->get_index();
355    aws->init(root, GBS_global_string("MARK_CONFIGS_%i", ntw_id), "Highlight configurations in tree");
356    aws->load_xfig("mark_configs.fig");
357
358    aws->auto_space(10, 10);
359
360    aws->at("close");
361    aws->callback(AW_POPDOWN);
362    aws->create_button("CLOSE", "CLOSE", "C");
363
364    aws->at("help");
365    aws->callback(makeHelpCallback("species_configs_highlight.hlp"));
366    aws->create_button("HELP", "HELP", "H");
367
368
369    aws->at("list");
370    AW_DB_selection *all_configs = awt_create_CONFIG_selection_list(GLOBAL.gb_main, aws, AWAR_CONFIGURATION, true);
371    AW_selection *sub_sel;
372    {
373        LocallyModify<bool> avoid(allow_selection2awar_update, false); // avoid awar gets updated from empty sub-selectionlist
374        sub_sel = awt_create_subset_selection_list(aws, all_configs->get_sellist(), "selected", "add", "sort", false, configs_selectionlist_changed_cb, ntw->get_index());
375    }
376
377    awt_set_subset_selection_content(sub_sel, *get_selected_configs_from_awar(ntw_id));
378    selected_configs_list[ntw_id] = sub_sel;
379
380    // @@@ would like to use ntw-specific awar for this selection list (opening two lists links them)
381
382    aws->at("show");
383    aws->label("Display?");
384    aws->create_toggle(get_display_toggle_awar(ntw_id)->awar_name);
385
386    aws->at("settings");
387    aws->callback(TREE_create_marker_settings_window);
388    aws->create_autosize_button("SETTINGS", "Settings", "S");
389
390    AWT_insert_config_manager(aws, GLOBAL.gb_main, MANAGED_CONFIGSET_SECTION, makeConfigSetupCallback(setup_configmarker_config_cb, ntw_id));
391
392    return aws;
393}
394
395// -----------------------------
396//      class Store_species
397
398class Store_species : virtual Noncopyable {
399    // stores an amount of species:
400    TreeNode *node;
401    Store_species *next;
402public:
403    Store_species(TreeNode *aNode) {
404        node = aNode;
405        next = NULp;
406    }
407    ~Store_species();
408
409    Store_species* add(Store_species *list) {
410        nt_assert(!next);
411        next = list;
412        return this;
413    }
414
415    Store_species* remove() {
416        Store_species *follower = next;
417        next = NULp;
418        return follower;
419    }
420
421    TreeNode *getNode() const { return node; }
422
423    void call(void (*aPizza)(TreeNode*)) const;
424};
425
426Store_species::~Store_species() {
427    delete next;
428}
429
430void Store_species::call(void (*aPizza)(TreeNode*)) const {
431    aPizza(node);
432    if (next) next->call(aPizza);
433}
434
435static void unmark_species(TreeNode *node) {
436    nt_assert(node);
437    nt_assert(node->gb_node);
438    nt_assert(GB_read_flag(node->gb_node)!=0);
439    GB_write_flag(node->gb_node, 0);
440}
441
442static void mark_species(TreeNode *node, Store_species **extra_marked_species) {
443    nt_assert(node);
444    nt_assert(node->gb_node);
445    nt_assert(GB_read_flag(node->gb_node)==0);
446    GB_write_flag(node->gb_node, 1);
447
448    *extra_marked_species = (new Store_species(node))->add(*extra_marked_species);
449}
450
451
452
453static TreeNode *rightmost_leaf(TreeNode *node) {
454    nt_assert(node);
455    while (!node->is_leaf()) {
456        node = node->get_rightson();
457        nt_assert(node);
458    }
459    return node;
460}
461
462static TreeNode *left_neighbour_leaf(TreeNode *node) {
463    if (node) {
464        TreeNode *father = node->get_father();
465        while (father) {
466            if (father->rightson==node) {
467                node = rightmost_leaf(father->get_leftson());
468                nt_assert(node->is_leaf());
469                if (!node->gb_node) { // Zombie
470                    node = left_neighbour_leaf(node);
471                }
472                return node;
473            }
474            node = father;
475            father = node->get_father();
476        }
477    }
478    return NULp;
479}
480
481static int nt_build_conf_string_rek(GB_HASH *used, TreeNode *tree, GBS_strstruct *memfile,
482                             Store_species **extra_marked_species, int use_species_aside,
483                             int *auto_mark, int marked_at_left, int *marked_at_right)
484{
485    /*! Builds a configuration string from a tree.
486     *
487     * @param used                      all species inserted by this function are stored here
488     * @param tree                      used for group information
489     * @param memfile                   generated configuration string is stored here
490     * @param extra_marked_species      all extra marked species are inserted here
491     * @param use_species_aside         number of species to mark left and right of marked species
492     * @param auto_mark                 number species to extra-mark (if not already marked)
493     * @param marked_at_left            number of species which were marked (looking to left)
494     * @param marked_at_right           number of species which are marked (when returning from recursion)
495     *
496     * @return the number of marked species
497     *
498     * --------------------------------------------------
499     * Format of configuration string : [Part]+ \0
500     *
501     * Part : '\A' ( Group | Species | Sai )
502     *
503     * Group : ( OpenedGroup | ClosedGroup )
504     * OpenedGroup : 'G' GroupDef
505     * ClosedGroup : 'F' GroupDef
506     * GroupDef : 'groupname' [PART]* EndGroup
507     * EndGroup : '\AE'
508     *
509     * SPECIES : 'L' 'speciesname'
510     * SAI : 'S' 'sainame'
511     *
512     * \0 : ASCII 0 (eos)
513     * \A : ASCII 1
514     */
515
516    if (!tree) return 0;
517    if (tree->is_leaf()) {
518        if (!tree->gb_node) {
519            *marked_at_right = marked_at_left;
520            return 0;   // Zombie
521        }
522
523        if (!GB_read_flag(tree->gb_node)) { // unmarked species
524            if (*auto_mark) {
525                (*auto_mark)--;
526                mark_species(tree, extra_marked_species);
527            }
528            else {
529                *marked_at_right = 0;
530                return 0;
531            }
532        }
533        else { // marked species
534            if (marked_at_left<use_species_aside) {
535                // on the left side there are not as many marked species as needed!
536
537                nt_assert(marked_at_left>=0);
538
539                TreeNode *leaf_at_left = tree;
540                int       step_over    = marked_at_left+1; // step over myself
541                int       then_mark    = use_species_aside-marked_at_left;
542
543                while (step_over--) { // step over self and over any adjacent, marked species
544                    leaf_at_left = left_neighbour_leaf(leaf_at_left);
545                }
546
547                Store_species *marked_back = NULp;
548                while (leaf_at_left && then_mark--) { // then additionally mark some species
549                    if (GB_read_flag(leaf_at_left->gb_node) == 0) { // if they are not marked yet
550                        mark_species(leaf_at_left, extra_marked_species);
551                        marked_back = (new Store_species(leaf_at_left))->add(marked_back);
552                    }
553                    leaf_at_left = left_neighbour_leaf(leaf_at_left);
554                }
555
556                while (marked_back) {
557                    GBS_chrcat(memfile, 1);             // Separated by 1
558                    GBS_strcat(memfile, "L");
559                    GBS_strcat(memfile, marked_back->getNode()->name);
560                    GBS_write_hash(used, marked_back->getNode()->name, 1);      // Mark species
561
562                    Store_species *rest = marked_back->remove();
563                    delete marked_back;
564                    marked_back = rest;
565                }
566
567                marked_at_left = use_species_aside;
568            }
569            // now use_species_aside species to left are marked!
570            *auto_mark = use_species_aside;
571        }
572
573        GBS_chrcat(memfile, 1);             // Separated by 1
574        GBS_strcat(memfile, "L");
575        GBS_strcat(memfile, tree->name);
576        GBS_write_hash(used, tree->name, 1);    // Mark species
577
578        *marked_at_right = marked_at_left+1;
579        return 1;
580    }
581
582    long oldpos = GBS_memoffset(memfile);
583    if (tree->gb_node && tree->name) {      // but we are a group
584        GBDATA *gb_grouped = GB_entry(tree->gb_node, "grouped");
585        GBS_chrcat(memfile, 1);             // Separated by 1
586        if (gb_grouped && GB_read_byte(gb_grouped)) {
587            GBS_strcat(memfile, "F");
588        }
589        else {
590            GBS_strcat(memfile, "G");
591        }
592
593        GBS_strcat(memfile, tree->name);
594    }
595
596    int  right_of_leftson;
597    long nspecies=   nt_build_conf_string_rek(used, tree->get_leftson(),  memfile, extra_marked_species, use_species_aside, auto_mark, marked_at_left,   &right_of_leftson);
598    nspecies      += nt_build_conf_string_rek(used, tree->get_rightson(), memfile, extra_marked_species, use_species_aside, auto_mark, right_of_leftson, marked_at_right);
599
600    if (tree->gb_node && tree->name) {      // but we are a group
601        GBS_chrcat(memfile, 1);         // Separated by 1
602        GBS_chrcat(memfile, 'E');        // Group end indicated by 'E'
603    }
604
605    if (!nspecies) {
606        long newpos = GBS_memoffset(memfile);
607        GBS_str_cut_tail(memfile, newpos-oldpos);   // delete group info
608    }
609    return nspecies;
610}
611
612struct SAI_string_builder {
613    GBS_strstruct *sai_middle;
614    const char    *last_group_name;
615};
616
617static void nt_build_sai_string_by_hash(const char *key, long /*val*/, void *cd_sai_builder) {
618    SAI_string_builder *sai_builder = (SAI_string_builder*)cd_sai_builder;
619
620    const char *sep = strchr(key, 1);
621    if (sep) {
622        GBS_strstruct *sai_middle      = sai_builder->sai_middle;
623        const char    *last_group_name = sai_builder->last_group_name;
624
625        if (!last_group_name || strncmp(key, last_group_name, sep-key)) { // new group
626            if (last_group_name) {
627                GBS_chrcat(sai_middle, 1);              // Separated by 1
628                GBS_chrcat(sai_middle, 'E');             // End of old group
629            }
630            GBS_chrcat(sai_middle, 1);                  // Separated by 1
631            GBS_strcat(sai_middle, "FSAI:");
632            GBS_strncat(sai_middle, key, sep-key);
633            sai_builder->last_group_name = key;
634        }
635        GBS_chrcat(sai_middle, 1);                      // Separated by 1
636        GBS_strcat(sai_middle, "S");
637        GBS_strcat(sai_middle, sep+1);
638    }
639}
640
641
642static void nt_build_sai_string(GBS_strstruct *topfile, GBS_strstruct *middlefile) {
643    //! collect all Sais, place some SAI in top area, rest in middle
644
645    GBDATA *gb_sai_data = GBT_get_SAI_data(GLOBAL.gb_main);
646    if (gb_sai_data) {
647        GB_HASH *hash = GBS_create_hash(GB_number_of_subentries(gb_sai_data), GB_IGNORE_CASE);
648
649        for (GBDATA *gb_sai = GBT_first_SAI_rel_SAI_data(gb_sai_data); gb_sai; gb_sai = GBT_next_SAI(gb_sai)) {
650            GBDATA *gb_name = GB_search(gb_sai, "name", GB_FIND);
651            if (gb_name) {
652                char *name = GB_read_string(gb_name);
653
654                if (strcmp(name,  "HELIX") == 0  || strcmp(name,  "HELIX_NR") == 0 || strcmp(name,  "ECOLI") == 0) {
655                    GBS_chrcat(topfile, 1);             // Separated by 1
656                    GBS_strcat(topfile, "S");
657                    GBS_strcat(topfile, name);
658                }
659                else {
660                    GBDATA *gb_gn = GB_search(gb_sai, "sai_group", GB_FIND);
661                    char   *gn;
662
663                    if (gb_gn)  gn = GB_read_string(gb_gn);
664                    else        gn = ARB_strdup("SAI's");
665
666                    char *cn = new char[strlen(gn) + strlen(name) + 2];
667                    sprintf(cn, "%s%c%s", gn, 1, name);
668                    GBS_write_hash(hash, cn, 1);
669                    delete [] cn;
670                    free(gn);
671                }
672                free(name);
673            }
674        }
675
676        // open surrounding SAI-group:
677        GBS_chrcat(middlefile, 1);
678        GBS_strcat(middlefile, "GSAI-Maingroup");
679
680        SAI_string_builder sai_builder = { middlefile, NULp };
681        GBS_hash_do_const_sorted_loop(hash, nt_build_sai_string_by_hash, GBS_HCF_sortedByKey, &sai_builder);
682        if (sai_builder.last_group_name) {
683            GBS_chrcat(middlefile, 1);              // Separated by 1
684            GBS_chrcat(middlefile, 'E');             // End of old group
685        }
686
687        // close surrounding SAI-group:
688        GBS_chrcat(middlefile, 1);
689        GBS_chrcat(middlefile, 'E');
690
691        GBS_free_hash(hash);
692    }
693}
694
695static void nt_build_conf_marked(GB_HASH *used, GBS_strstruct *file) {
696    GBS_chrcat(file, 1);            // Separated by 1
697    GBS_strcat(file, "FMore Sequences");
698    GBDATA *gb_species;
699    for (gb_species = GBT_first_marked_species(GLOBAL.gb_main);
700         gb_species;
701         gb_species = GBT_next_marked_species(gb_species)) {
702        char *name = GBT_read_string(gb_species, "name");
703        if (GBS_read_hash(used, name)) {
704            free(name);
705            continue;
706        }
707        GBS_chrcat(file, 1);
708        GBS_strcat(file, "L");
709        GBS_strcat(file, name);
710        free(name);
711    }
712
713    GBS_chrcat(file, 1); // Separated by 1
714    GBS_chrcat(file, 'E');   // Group end indicated by 'E'
715}
716
717static void nt_extract_configuration(UNFIXED, extractType ext_type) {
718    GB_transaction  ta(GLOBAL.gb_main);
719    AW_root        *aw_root = AW_root::SINGLETON;
720    char           *cn      = aw_root->awar(AWAR_CONFIGURATION)->read_string();
721
722    if (strcmp(cn, NO_CONFIG_SELECTED) == 0) {
723        aw_message("Please select a configuration");
724    }
725    else {
726        GB_ERROR   error = NULp;
727        GBT_config cfg(GLOBAL.gb_main, cn, error);
728
729        if (!error) {
730            size_t  unknown_species = 0;
731            bool    refresh         = false;
732
733            GB_HASH *was_marked = NULp; // only used for CONF_COMBINE
734
735            switch (ext_type) {
736                case CONF_EXTRACT: // unmark all
737                    GBT_mark_all(GLOBAL.gb_main, 0);
738                    refresh = true;
739                    break;
740
741                case CONF_COMBINE: // store all marked species in hash and unmark them
742                    was_marked = GBT_create_marked_species_hash(GLOBAL.gb_main);
743                    GBT_mark_all(GLOBAL.gb_main, 0);
744                    refresh    = GBS_hash_elements(was_marked);
745                    break;
746
747                default:
748                    break;
749            }
750
751            for (int area = 0; area<=1 && !error; ++area) {
752                GBT_config_parser cparser(cfg, area);
753
754                while (1) {
755                    const GBT_config_item& citem = cparser.nextItem(error);
756                    if (error || citem.type == CI_END_OF_CONFIG) break;
757
758                    if (citem.type == CI_SPECIES) {
759                        GBDATA *gb_species = GBT_find_species(GLOBAL.gb_main, citem.name);
760
761                        if (gb_species) {
762                            int oldmark = GB_read_flag(gb_species);
763                            int newmark = oldmark;
764                            switch (ext_type) {
765                                case CONF_EXTRACT:
766                                case CONF_MARK:     newmark = 1; break;
767                                case CONF_UNMARK:   newmark = 0; break;
768                                case CONF_INVERT:   newmark = !oldmark; break;
769                                case CONF_COMBINE: {
770                                    nt_assert(!oldmark); // should have been unmarked above
771                                    newmark = GBS_read_hash(was_marked, citem.name); // mark if was_marked
772                                    break;
773                                }
774                                default: nt_assert(0); break;
775                            }
776                            if (newmark != oldmark) {
777                                GB_write_flag(gb_species, newmark);
778                                refresh = true;
779                            }
780                        }
781                        else {
782                            unknown_species++;
783                        }
784                    }
785                }
786            }
787
788            if (was_marked) GBS_free_hash(was_marked);
789            if (unknown_species>0 && !error) error = GBS_global_string("configuration '%s' contains %zu unknown species", cn, unknown_species);
790            if (refresh) aw_root->awar(AWAR_TREE_REFRESH)->touch();
791        }
792        aw_message_if(error);
793    }
794    free(cn);
795}
796
797static void nt_delete_configuration(AW_window *aww, AW_DB_selection *dbsel) {
798    GB_transaction ta(GLOBAL.gb_main);
799
800    AW_awar *awar_selected    = aww->get_root()->awar(AWAR_CONFIGURATION);
801    char    *name             = awar_selected->read_string();
802    GBDATA  *gb_configuration = GBT_find_configuration(GLOBAL.gb_main, name);
803
804    if (gb_configuration) {
805        dbsel->get_sellist()->move_selection(1);
806
807        GB_ERROR error = GB_delete(gb_configuration);
808        error          = ta.close(error);
809        if (error) {
810            aw_message(error);
811        }
812        else {
813            configuration_deleted_cb(name);
814        }
815    }
816    free(name);
817}
818
819enum ConfigCreation {
820    BY_CALLING_THE_EDITOR,
821    FROM_IMPORTER,
822    FROM_MANAGER,
823};
824
825static GB_ERROR nt_create_configuration(TreeNode *tree, const char *conf_name, int use_species_aside, ConfigCreation creation) {
826    GB_ERROR error = NULp;
827
828    if (!conf_name || !conf_name[0]) error = "no config name given";
829    else {
830        if (use_species_aside==-1) {
831            static int last_used_species_aside = 3;
832            {
833                const char *val                    = GBS_global_string("%i", last_used_species_aside);
834                char       *use_species            = aw_input("How many extra species to view aside marked:", val);
835                if (use_species) use_species_aside = atoi(use_species);
836                free(use_species);
837            }
838
839            if (use_species_aside<1) error = "illegal number of 'species aside'";
840            else last_used_species_aside = use_species_aside; // remember for next time
841        }
842
843        if (!error) {
844            GB_transaction ta(GLOBAL.gb_main);  // open close transaction
845
846            GBT_config newcfg;
847            {
848                GB_HASH       *used    = GBS_create_hash(GBT_get_species_count(GLOBAL.gb_main), GB_MIND_CASE);
849                GBS_strstruct topfile(1000);
850                GBS_strstruct topmid(10000);
851                {
852                    GBS_strstruct middlefile(10000);
853                    nt_build_sai_string(&topfile, &topmid);
854
855                    if (use_species_aside) {
856                        Store_species *extra_marked_species = NULp;
857                        int            auto_mark            = 0;
858                        int            marked_at_right;
859                   
860                        nt_build_conf_string_rek(used, tree, &middlefile, &extra_marked_species, use_species_aside, &auto_mark, use_species_aside, &marked_at_right);
861                        if (extra_marked_species) {
862                            extra_marked_species->call(unmark_species);
863                            delete extra_marked_species;
864                        }
865                    }
866                    else {
867                        int dummy_1=0, dummy_2;
868                        nt_build_conf_string_rek(used, tree, &middlefile, NULp, 0, &dummy_1, 0, &dummy_2);
869                    }
870                    nt_build_conf_marked(used, &topmid);
871                    topmid.ncat(middlefile.get_data(), middlefile.get_position());
872                }
873
874                newcfg.set_definition(GBT_config::TOP_AREA,    topfile.release());
875                newcfg.set_definition(GBT_config::MIDDLE_AREA, topmid.release());
876
877                GBS_free_hash(used);
878            }
879
880            AW_root *awr = AW_root::SINGLETON;
881
882            GBT_config previous(GLOBAL.gb_main, conf_name, error);
883            error = NULp; // ignore
884
885            const char *prevComment         = NULp; // old or fixed comment
886            const char *comment             = NULp;
887            bool        warnIfSavingDefault = true;
888            switch (creation) {
889                case BY_CALLING_THE_EDITOR: { // always saves DEFAULT_CONFIGURATION!
890                    prevComment         = "This configuration will be OVERWRITTEN each time\nARB_EDIT4 is started w/o specifying a config!\n---";
891                    comment             = "created for ARB_EDIT4";
892                    warnIfSavingDefault = false;
893                    break;
894                }
895                case FROM_MANAGER: {
896                    if (previous.exists()) {
897                        prevComment = previous.get_comment();
898                        comment     = "updated manually";
899                    }
900                    else {
901                        prevComment                      = awr->awar(AWAR_CONFIG_COMMENT)->read_char_pntr();
902                        if (!prevComment[0]) prevComment = NULp;
903                        comment                          = "created manually";
904                    }
905                    break;
906                }
907                case FROM_IMPORTER:
908                    nt_assert(!previous.exists());
909                    comment = "created by importer";
910                    break;
911            }
912
913            nt_assert(implicated(prevComment, comment));
914            if (comment) {
915                // annotate with treename
916                const char *treename = awr->awar(AWAR_TREE_NAME)->read_char_pntr();
917                if (treename[0]) {
918                    comment = GBS_global_string("%s (tree=%s)", comment, treename);
919                }
920                else {
921                    comment = GBS_global_string("%s (no tree)", comment);
922                }
923                char *dated = GBS_log_action_to(prevComment, comment, true);
924                newcfg.set_comment(dated);
925                free(dated);
926            }
927
928            error = newcfg.save(GLOBAL.gb_main, conf_name, warnIfSavingDefault);
929            awr->awar(AWAR_CONFIGURATION)->touch(); // refreshes comment field
930        }
931    }
932
933    return error;
934}
935
936static void nt_store_configuration(AW_window*, TREE_canvas *ntw) {
937    const char *cfgName = AW_root::SINGLETON->awar(AWAR_CONFIGURATION)->read_char_pntr();
938    GB_ERROR    err     = nt_create_configuration(NT_get_tree_root_of_canvas(ntw), cfgName, 0, FROM_MANAGER);
939    aw_message_if(err);
940}
941
942static void nt_rename_configuration(AW_window *aww) {
943    AW_awar  *awar_curr_cfg = aww->get_root()->awar(AWAR_CONFIGURATION);
944    char     *old_name      = awar_curr_cfg->read_string();
945
946    {
947        char *new_name = aw_input("Rename selection", "Enter the new name of the selection", old_name);
948        if (new_name) {
949            GB_ERROR err = NULp;
950
951            {
952                GB_transaction ta(GLOBAL.gb_main);
953
954                GBDATA *gb_existing_cfg  = GBT_find_configuration(GLOBAL.gb_main, new_name);
955                if (gb_existing_cfg) err = GBS_global_string("There is already a selection named '%s'", new_name);
956                else {
957                    GBDATA *gb_old_cfg = GBT_find_configuration(GLOBAL.gb_main, old_name);
958                    if (gb_old_cfg) {
959                        GBDATA *gb_name = GB_entry(gb_old_cfg, "name");
960                        if (gb_name) {
961                            err = GB_write_string(gb_name, new_name);
962                            if (!err) awar_curr_cfg->write_string(new_name);
963                        }
964                        else err = "Selection has no name";
965                    }
966                    else err = "Can't find that selection";
967                }
968                err = ta.close(err);
969            }
970
971            if (err) {
972                aw_message(err);
973            }
974            else {
975                nt_assert(GB_get_transaction_level(GLOBAL.gb_main) == 0); // otherwise callback below behaves wrong
976                configuration_renamed_cb(old_name, new_name);
977            }
978            free(new_name);
979        }
980    }
981    free(old_name);
982}
983
984static void selected_config_changed_cb(AW_root *root) {
985    const char *config = root->awar(AWAR_CONFIGURATION)->read_char_pntr();
986
987    bool    nonexisting_config = false;
988    GBDATA *gb_target_commment = NULp;
989    if (config[0]) {
990        GBDATA *gb_configuration = GBT_find_configuration(GLOBAL.gb_main, config);
991        if (gb_configuration) {
992            gb_target_commment = GB_entry(gb_configuration, "comment");
993        }
994        else {
995            nonexisting_config = true;
996        }
997    }
998
999    AW_awar *awar_comment = root->awar(AWAR_CONFIG_COMMENT);
1000    if (gb_target_commment) {
1001        if (!awar_comment->is_mapped()) awar_comment->write_string("");
1002        awar_comment->map(gb_target_commment);
1003    }
1004    else {
1005        char *reuse_comment = nonexisting_config ? awar_comment->read_string() : ARB_strdup("");
1006        if (awar_comment->is_mapped()) {
1007            awar_comment->unmap();
1008        }
1009        awar_comment->write_string(reuse_comment);
1010        free(reuse_comment);
1011    }
1012}
1013static void config_comment_changed_cb(AW_root *root) {
1014    // called when comment-awar changes or gets re-map-ped
1015
1016    AW_awar    *awar_comment = root->awar(AWAR_CONFIG_COMMENT);
1017    const char *comment      = awar_comment->read_char_pntr();
1018
1019    const char *config           = root->awar(AWAR_CONFIGURATION)->read_char_pntr();
1020    GBDATA     *gb_configuration = config[0] ? GBT_find_configuration(GLOBAL.gb_main, config) : NULp;
1021
1022    GB_ERROR error = NULp;
1023    if (awar_comment->is_mapped()) {
1024        if (!comment[0]) { // empty existing comment
1025            nt_assert(gb_configuration);
1026            GBDATA *gb_commment = GB_entry(gb_configuration, "comment");
1027            nt_assert(gb_commment);
1028            if (gb_commment) {
1029                awar_comment->unmap();
1030                error = GB_delete(gb_commment);
1031            }
1032        }
1033    }
1034    else {
1035        if (comment[0]) { // ignore empty comment for unmapped awar
1036            if (gb_configuration) {
1037                nt_assert(!GB_entry(gb_configuration, "comment"));
1038                error = GBT_write_string(gb_configuration, "comment", comment);
1039                if (!error) {
1040                    awar_comment->write_string("");
1041                    selected_config_changed_cb(root);
1042                }
1043            }
1044            else if (!config[0]) {
1045                // do NOT warn if name field contains (not yet) existing name
1046                // (allows to edit comment while creating new config)
1047                error = "Please select an existing species selection to edit its comment";
1048            }
1049        }
1050    }
1051
1052    aw_message_if(error);
1053}
1054
1055static void init_config_admin_awars(AW_root *root) {
1056    init_config_awars(root);
1057    root->awar_string(AWAR_CONFIG_COMMENT, "", GLOBAL.gb_main)->add_callback(config_comment_changed_cb);
1058    root->awar(AWAR_CONFIGURATION)->add_callback(selected_config_changed_cb)->touch();
1059}
1060
1061#pragma GCC diagnostic push
1062#if (GCC_VERSION_CODE<700)
1063#pragma GCC diagnostic ignored "-Wstrict-overflow" // gcc 6.x produces a bogus overflow warning (gcc 7.x is smart enough)
1064#endif
1065
1066static GB_ERROR swap_configs(GBDATA *gb_main, StrArray& config, int i1, int i2) {
1067    GB_ERROR error = NULp;
1068
1069    if (i1>i2) swap(i1, i2); // otherwise overwrite below does not work
1070    nt_assert(i1<i2 && i1>=0 && i2<int(config.size()));
1071
1072    GBT_config c1(gb_main, config[i1], error);
1073    if (!error) {
1074        GBT_config c2(gb_main, config[i2], error);
1075        if (!error) error = c1.saveAsOver(gb_main, config[i1], config[i2], false);
1076        if (!error) error = c2.saveAsOver(gb_main, config[i2], config[i1], false);
1077        if (!error) config.swap(i1, i2);
1078    }
1079    return error;
1080}
1081
1082#pragma GCC diagnostic pop
1083
1084static void reorder_configs_cb(AW_window *aww, awt_reorder_mode mode, AW_DB_selection *sel) {
1085    AW_root    *awr         = aww->get_root();
1086    AW_awar    *awar_config = awr->awar(AWAR_CONFIGURATION);
1087    const char *selected    = awar_config->read_char_pntr();
1088
1089    if (selected && selected[0]) {
1090        AW_selection_list *sellist = sel->get_sellist();
1091
1092        int source_idx = sellist->get_index_of(AW_scalar(selected));
1093        int target_idx = -1;
1094        switch (mode) {
1095            case ARM_TOP:    target_idx = 0;            break;
1096            case ARM_UP:     target_idx = source_idx-1; break;
1097            case ARM_DOWN:   target_idx = source_idx+1; break;
1098            case ARM_BOTTOM: target_idx = -1;           break;
1099        }
1100
1101        int entries = sellist->size();
1102        target_idx  = (target_idx+entries)%entries;
1103
1104        {
1105            GBDATA         *gb_main = sel->get_gb_main();
1106            GB_transaction  ta(gb_main);
1107
1108            StrArray config;
1109            sellist->to_array(config, true);
1110
1111            GB_ERROR error = NULp;
1112            if (source_idx<target_idx) {
1113                for (int i = source_idx+1; i<=target_idx; ++i) {
1114                    swap_configs(gb_main, config, i-1, i);
1115                }
1116            }
1117            else if (source_idx>target_idx) {
1118                for (int i = source_idx-1; i>=target_idx; --i) {
1119                    swap_configs(gb_main, config, i+1, i);
1120                }
1121            }
1122
1123            error = ta.close(error);
1124            aw_message_if(error);
1125        }
1126        awar_config->touch();
1127    }
1128}
1129
1130static void clear_comment_cb(AW_window *aww) {
1131    AW_awar *awar_comment = aww->get_root()->awar(AWAR_CONFIG_COMMENT);
1132    char    *comment      = awar_comment->read_string();
1133
1134    ConstStrArray line;
1135    GBT_splitNdestroy_string(line, comment, '\n');
1136
1137    bool    removedDatedLines = false;
1138    RegExpr datedLine("^([A-Z][a-z]{2}\\s){2}[0-9]+\\s([0-9]{2}:){2}[0-9]{2}\\s[0-9]{4}:\\s", false); // matches lines created with GBS_log_action_to(..., stamp=true)
1139    for (int i = line.size()-1; i >= 0; --i) {
1140        const RegMatch *match = datedLine.match(line[i]);
1141        gb_assert(implicated(!match, !datedLine.has_failed())); // assert RegExpr compiles
1142        if (match && match->didMatch()) {
1143            line.safe_remove(i);
1144            removedDatedLines = true;
1145        }
1146    }
1147
1148    if (!removedDatedLines) line.clear(); // erase all
1149
1150    comment = GBT_join_strings(line, '\n');
1151    awar_comment->write_string(comment);
1152}
1153
1154static AW_window *create_configuration_admin_window(AW_root *root, TREE_canvas *ntw) {
1155    static AW_window_simple *existing_aws[MAX_NT_WINDOWS] = { MAX_NT_WINDOWS_NULLINIT };
1156
1157    int ntw_id = ntw->get_index();
1158    if (!existing_aws[ntw_id]) {
1159        init_config_admin_awars(root);
1160
1161        AW_window_simple *aws = new AW_window_simple;
1162        aws->init(root, GBS_global_string("SPECIES_SELECTIONS_%i", ntw_id), "Species Selections");
1163        aws->load_xfig("nt_selection.fig");
1164
1165        aws->at("close");
1166        aws->callback(AW_POPDOWN);
1167        aws->create_button("CLOSE", "CLOSE", "C");
1168
1169        aws->at("help");
1170        aws->callback(makeHelpCallback("species_configs.hlp"));
1171        aws->create_button("HELP", "HELP", "H");
1172
1173        aws->at("name");
1174        aws->create_input_field(AWAR_CONFIGURATION);
1175
1176        aws->at("comment");
1177        aws->create_text_field(AWAR_CONFIG_COMMENT);
1178
1179        aws->at("clr");
1180        aws->callback(clear_comment_cb);
1181        aws->create_autosize_button("CLEAR", "Clear", "l");
1182
1183        aws->at("list");
1184        AW_DB_selection *dbsel = awt_create_CONFIG_selection_list(GLOBAL.gb_main, aws, AWAR_CONFIGURATION, false);
1185
1186        aws->button_length(8);
1187
1188        aws->at("store");
1189        aws->callback(makeWindowCallback(nt_store_configuration, ntw));
1190        aws->create_button(GBS_global_string("STORE_%i", ntw_id), "STORE", "S");
1191
1192        aws->at("extract");
1193        aws->callback(makeWindowCallback(nt_extract_configuration, CONF_EXTRACT));
1194        aws->create_button("EXTRACT", "EXTRACT", "E");
1195
1196        aws->at("mark");
1197        aws->callback(makeWindowCallback(nt_extract_configuration, CONF_MARK));
1198        aws->create_button("MARK", "MARK", "M");
1199
1200        aws->at("unmark");
1201        aws->callback(makeWindowCallback(nt_extract_configuration, CONF_UNMARK));
1202        aws->create_button("UNMARK", "UNMARK", "U");
1203
1204        aws->at("invert");
1205        aws->callback(makeWindowCallback(nt_extract_configuration, CONF_INVERT));
1206        aws->create_button("INVERT", "INVERT", "I");
1207
1208        aws->at("combine");
1209        aws->callback(makeWindowCallback(nt_extract_configuration, CONF_COMBINE));
1210        aws->create_button("COMBINE", "COMBINE", "C");
1211
1212        aws->at("delete");
1213        aws->callback(makeWindowCallback(nt_delete_configuration, dbsel));
1214        aws->create_button("DELETE", "DELETE", "D");
1215
1216        aws->at("rename");
1217        aws->callback(nt_rename_configuration);
1218        aws->create_button("RENAME", "RENAME", "R");
1219
1220        aws->at("highlight");
1221        aws->callback(makeCreateWindowCallback(create_configuration_marker_window, ntw));
1222        aws->create_autosize_button(GBS_global_string("HIGHLIGHT_%i", ntw_id), "Highlight in tree", "t");
1223
1224        aws->button_length(0);
1225        aws->at("sort");
1226        awt_create_order_buttons(aws, reorder_configs_cb, dbsel);
1227
1228        existing_aws[ntw_id] = aws;
1229    }
1230    return existing_aws[ntw_id];
1231}
1232
1233void NT_popup_configuration_admin(AW_window *aw_main, TREE_canvas *ntw) {
1234    create_configuration_admin_window(aw_main->get_root(), ntw)->activate();
1235}
1236
1237// -----------------------------------------
1238//      various ways to start the editor
1239
1240static void nt_start_editor_on_configuration(AW_window *aww) {
1241    aww->hide();
1242
1243    const char *cfgName   = aww->get_root()->awar(AWAR_CONFIGURATION)->read_char_pntr();
1244    char       *quotedCfg = GBK_singlequote(cfgName);
1245
1246    AWT_system_cb(GBS_global_string("arb_edit4 -c %s &", quotedCfg));
1247
1248    free(quotedCfg);
1249}
1250
1251AW_window *NT_create_startEditorOnOldConfiguration_window(AW_root *awr) {
1252    static AW_window_simple *aws = NULp;
1253    if (!aws) {
1254        init_config_awars(awr);
1255
1256        aws = new AW_window_simple;
1257        aws->init(awr, "SELECT_CONFIGURATION", "SELECT A CONFIGURATION");
1258        aws->at(10, 10);
1259        aws->auto_space(0, 0);
1260        awt_create_CONFIG_selection_list(GLOBAL.gb_main, aws, AWAR_CONFIGURATION, false);
1261        aws->at_newline();
1262
1263        aws->callback(nt_start_editor_on_configuration);
1264        aws->create_button("START", "START");
1265
1266        aws->callback(AW_POPDOWN);
1267        aws->create_button("CLOSE", "CLOSE", "C");
1268
1269        aws->window_fit();
1270    }
1271    return aws;
1272}
1273
1274void NT_start_editor_on_tree(AW_window *aww, int use_species_aside, TREE_canvas *ntw) {
1275    init_config_awars(aww->get_root());
1276    GB_ERROR error    = nt_create_configuration(NT_get_tree_root_of_canvas(ntw), DEFAULT_CONFIGURATION, use_species_aside, BY_CALLING_THE_EDITOR);
1277    if (!error) error = GBK_system("arb_edit4 -c " DEFAULT_CONFIGURATION " &");
1278    aw_message_if(error);
1279}
1280
1281inline void nt_create_config_after_import(TREE_canvas *ntw) {
1282    init_config_awars(ntw->awr);
1283
1284    const char *dated_suffix = ARB_dateTime_suffix();
1285    char       *configName   = GBS_global_string_copy("imported_%s", dated_suffix);
1286
1287    // ensure unique config-name
1288    {
1289        int unique = 1;
1290        GB_transaction ta(ntw->gb_main);
1291        while (GBT_find_configuration(ntw->gb_main, configName)) {
1292            freeset(configName, GBS_global_string_copy("imported_%s_%i", dated_suffix, ++unique));
1293        }
1294    }
1295
1296    GB_ERROR error = nt_create_configuration(NT_get_tree_root_of_canvas(ntw), configName, 0, FROM_IMPORTER);
1297    aw_message_if(error);
1298
1299    free(configName);
1300}
1301
1302void NT_create_config_after_import(TREE_canvas *ntw, bool imported_from_scratch) {
1303    /*! create a new config after import
1304     * @param imported_from_scratch if true -> DB was created from scratch, all species in DB are marked.
1305     *                              if false -> data was imported into existing DB. Other species may be marked as well, imported species are "queried".
1306     */
1307
1308    if (imported_from_scratch) {
1309        nt_create_config_after_import(ntw);
1310    }
1311    else {
1312        GB_transaction ta(ntw->gb_main);
1313
1314        // remember marks + mark queried species:
1315        for (GBDATA *gb_species = GBT_first_species(ntw->gb_main); gb_species; gb_species = GBT_next_species(gb_species)) {
1316            GB_write_user_flag(gb_species, GB_USERFLAG_WASMARKED, GB_read_flag(gb_species));
1317            GB_write_flag(gb_species, GB_user_flag(gb_species, GB_USERFLAG_QUERY));
1318        }
1319
1320        nt_create_config_after_import(ntw);
1321
1322        // restore old marks:
1323        for (GBDATA *gb_species = GBT_first_species(ntw->gb_main); gb_species; gb_species = GBT_next_species(gb_species)) {
1324            GB_write_flag(gb_species, GB_user_flag(gb_species, GB_USERFLAG_WASMARKED));
1325            GB_clear_user_flag(gb_species, GB_USERFLAG_WASMARKED);
1326        }
1327    }
1328}
1329
1330
1331
Note: See TracBrowser for help on using the repository browser.