source: branches/help/NTREE/NT_edconf.cxx

Last change on this file was 18463, 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.7 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(null2empty(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
641static void nt_build_sai_string(const char *topAreaSaiList, GBS_strstruct *topfile, GBS_strstruct *middlefile) {
642    // collect all Sais,
643    // place some SAI in top area (those listed in 'toparea_SAIs'; SAI-groups will be ignored here)
644    // rest of SAI goes into middle area (SAI-groups respected here)
645
646    GBDATA *gb_sai_data = GBT_get_SAI_data(GLOBAL.gb_main);
647    if (gb_sai_data) {
648        GB_HASH *hash = GBS_create_hash(GB_number_of_subentries(gb_sai_data), GB_IGNORE_CASE);
649
650        ConstStrArray topAreaSai;
651        GBT_split_string(topAreaSai, topAreaSaiList, ",;: \t", true);
652
653        for (GBDATA *gb_sai = GBT_first_SAI_rel_SAI_data(gb_sai_data); gb_sai; gb_sai = GBT_next_SAI(gb_sai)) {
654            GBDATA *gb_name = GB_search(gb_sai, "name", GB_FIND);
655            if (gb_name) {
656                char *name = GB_read_string(gb_name);
657
658                bool wantedInTop = false;
659                for (unsigned s = 0; !wantedInTop && s<topAreaSai.size(); ++s) {
660                    wantedInTop = strcmp(name, topAreaSai[s]) == 0;
661                }
662
663                if (!wantedInTop) {
664                    GBDATA *gb_gn = GB_search(gb_sai, "sai_group", GB_FIND);
665                    char   *gn;
666
667                    if (gb_gn)  gn = GB_read_string(gb_gn);
668                    else        gn = ARB_strdup("SAI's");
669
670                    char *cn = new char[strlen(gn) + strlen(name) + 2];
671                    sprintf(cn, "%s%c%s", gn, 1, name);
672                    GBS_write_hash(hash, cn, 1);
673                    delete [] cn;
674                    free(gn);
675                }
676                free(name);
677            }
678        }
679
680        // add top area SAIs in defined order:
681        for (unsigned s = 0; s<topAreaSai.size(); ++s) {
682            GBDATA *gb_sai = GBT_find_SAI_rel_SAI_data(gb_sai_data, topAreaSai[s]);
683            if (gb_sai) {
684                GBS_chrcat(topfile, 1); // separated by ASCII 1
685                GBS_strcat(topfile, "S");
686                GBS_strcat(topfile, topAreaSai[s]);
687            }
688        }
689
690        // open surrounding SAI-group:
691        GBS_chrcat(middlefile, 1);
692        GBS_strcat(middlefile, "GSAI-Maingroup");
693
694        SAI_string_builder sai_builder = { middlefile, NULp };
695        GBS_hash_do_const_sorted_loop(hash, nt_build_sai_string_by_hash, GBS_HCF_sortedByKey, &sai_builder);
696        if (sai_builder.last_group_name) {
697            GBS_chrcat(middlefile, 1);              // Separated by 1
698            GBS_chrcat(middlefile, 'E');             // End of old group
699        }
700
701        // close surrounding SAI-group:
702        GBS_chrcat(middlefile, 1);
703        GBS_chrcat(middlefile, 'E');
704
705        GBS_free_hash(hash);
706    }
707}
708
709static void nt_build_conf_marked(GB_HASH *used, GBS_strstruct *file) {
710    GBS_chrcat(file, 1);            // Separated by 1
711    GBS_strcat(file, "FMore Sequences");
712    GBDATA *gb_species;
713    for (gb_species = GBT_first_marked_species(GLOBAL.gb_main);
714         gb_species;
715         gb_species = GBT_next_marked_species(gb_species)) {
716        char *name = GBT_read_string(gb_species, "name");
717        if (GBS_read_hash(used, name)) {
718            free(name);
719            continue;
720        }
721        GBS_chrcat(file, 1);
722        GBS_strcat(file, "L");
723        GBS_strcat(file, name);
724        free(name);
725    }
726
727    GBS_chrcat(file, 1); // Separated by 1
728    GBS_chrcat(file, 'E');   // Group end indicated by 'E'
729}
730
731static void nt_extract_configuration(UNFIXED, extractType ext_type) {
732    GB_transaction  ta(GLOBAL.gb_main);
733    AW_root        *aw_root = AW_root::SINGLETON;
734    char           *cn      = aw_root->awar(AWAR_CONFIGURATION)->read_string();
735
736    if (strcmp(cn, NO_CONFIG_SELECTED) == 0) {
737        aw_message("Please select a configuration");
738    }
739    else {
740        GB_ERROR   error = NULp;
741        GBT_config cfg(GLOBAL.gb_main, cn, error);
742
743        if (!error) {
744            size_t  unknown_species = 0;
745            bool    refresh         = false;
746
747            GB_HASH *was_marked = NULp; // only used for CONF_COMBINE
748
749            switch (ext_type) {
750                case CONF_EXTRACT: // unmark all
751                    GBT_mark_all(GLOBAL.gb_main, 0);
752                    refresh = true;
753                    break;
754
755                case CONF_COMBINE: // store all marked species in hash and unmark them
756                    was_marked = GBT_create_marked_species_hash(GLOBAL.gb_main);
757                    GBT_mark_all(GLOBAL.gb_main, 0);
758                    refresh    = GBS_hash_elements(was_marked);
759                    break;
760
761                default:
762                    break;
763            }
764
765            for (int area = 0; area<=1 && !error; ++area) {
766                GBT_config_parser cparser(cfg, area);
767
768                while (1) {
769                    const GBT_config_item& citem = cparser.nextItem(error);
770                    if (error || citem.type == CI_END_OF_CONFIG) break;
771
772                    if (citem.type == CI_SPECIES) {
773                        GBDATA *gb_species = GBT_find_species(GLOBAL.gb_main, citem.name);
774
775                        if (gb_species) {
776                            int oldmark = GB_read_flag(gb_species);
777                            int newmark = oldmark;
778                            switch (ext_type) {
779                                case CONF_EXTRACT:
780                                case CONF_MARK:     newmark = 1; break;
781                                case CONF_UNMARK:   newmark = 0; break;
782                                case CONF_INVERT:   newmark = !oldmark; break;
783                                case CONF_COMBINE: {
784                                    nt_assert(!oldmark); // should have been unmarked above
785                                    newmark = GBS_read_hash(was_marked, citem.name); // mark if was_marked
786                                    break;
787                                }
788                                default: nt_assert(0); break;
789                            }
790                            if (newmark != oldmark) {
791                                GB_write_flag(gb_species, newmark);
792                                refresh = true;
793                            }
794                        }
795                        else {
796                            unknown_species++;
797                        }
798                    }
799                }
800            }
801
802            if (was_marked) GBS_free_hash(was_marked);
803            if (unknown_species>0 && !error) error = GBS_global_string("configuration '%s' contains %zu unknown species", cn, unknown_species);
804            if (refresh) aw_root->awar(AWAR_TREE_REFRESH)->touch();
805        }
806        aw_message_if(error);
807    }
808    free(cn);
809}
810
811static void nt_delete_configuration(AW_window *aww, AW_DB_selection *dbsel) {
812    GB_transaction ta(GLOBAL.gb_main);
813
814    AW_awar *awar_selected    = aww->get_root()->awar(AWAR_CONFIGURATION);
815    char    *name             = awar_selected->read_string();
816    GBDATA  *gb_configuration = GBT_find_configuration(GLOBAL.gb_main, name);
817
818    if (gb_configuration) {
819        dbsel->get_sellist()->move_selection(1);
820
821        GB_ERROR error = GB_delete(gb_configuration);
822        error          = ta.close(error);
823        if (error) {
824            aw_message(error);
825        }
826        else {
827            configuration_deleted_cb(name);
828        }
829    }
830    free(name);
831}
832
833enum ConfigCreation {
834    BY_CALLING_THE_EDITOR,
835    FROM_IMPORTER,
836    FROM_MANAGER,
837};
838
839static GB_ERROR nt_create_configuration(TreeNode *tree, const char *conf_name, int use_species_aside, ConfigCreation creation) {
840    GB_ERROR error = NULp;
841
842    if (!conf_name || !conf_name[0]) error = "no config name given";
843    else {
844        if (use_species_aside==-1) {
845            static int last_used_species_aside = 3;
846            {
847                const char *val                    = GBS_global_string("%i", last_used_species_aside);
848                char       *use_species            = aw_input("How many extra species to view aside marked:", val);
849                if (use_species) use_species_aside = atoi(use_species);
850                free(use_species);
851            }
852
853            if (use_species_aside<1) error = "illegal number of 'species aside'";
854            else last_used_species_aside = use_species_aside; // remember for next time
855        }
856
857        if (!error) {
858            GB_transaction  ta(GLOBAL.gb_main); // open close transaction
859            AW_root        *awr = AW_root::SINGLETON;
860
861            GBT_config newcfg;
862            {
863                GB_HASH       *used    = GBS_create_hash(GBT_get_species_count(GLOBAL.gb_main), GB_MIND_CASE);
864                GBS_strstruct topfile(1000);
865                GBS_strstruct midfile(10000);
866                {
867                    GBS_strstruct middlefile(10000);
868
869                    nt_build_sai_string(awr->awar(AWAR_TOPAREA_SAIS)->read_char_pntr(), &topfile, &midfile);
870
871                    if (use_species_aside) {
872                        Store_species *extra_marked_species = NULp;
873                        int            auto_mark            = 0;
874                        int            marked_at_right;
875
876                        nt_build_conf_string_rek(used, tree, &middlefile, &extra_marked_species, use_species_aside, &auto_mark, use_species_aside, &marked_at_right);
877                        if (extra_marked_species) {
878                            extra_marked_species->call(unmark_species);
879                            delete extra_marked_species;
880                        }
881                    }
882                    else {
883                        int dummy_1=0, dummy_2;
884                        nt_build_conf_string_rek(used, tree, &middlefile, NULp, 0, &dummy_1, 0, &dummy_2);
885                    }
886                    nt_build_conf_marked(used, &midfile);
887                    midfile.ncat(middlefile.get_data(), middlefile.get_position());
888                }
889
890                newcfg.set_definition(GBT_config::TOP_AREA,    topfile.release());
891                newcfg.set_definition(GBT_config::MIDDLE_AREA, midfile.release());
892
893                GBS_free_hash(used);
894            }
895
896            GBT_config previous(GLOBAL.gb_main, conf_name, error);
897            error = NULp; // ignore
898
899            const char *prevComment         = NULp; // old or fixed comment
900            const char *comment             = NULp;
901            bool        warnIfSavingDefault = true;
902            switch (creation) {
903                case BY_CALLING_THE_EDITOR: { // always saves DEFAULT_CONFIGURATION!
904                    prevComment         = "This configuration will be OVERWRITTEN each time\nARB_EDIT4 is started w/o specifying a config!\n---";
905                    comment             = "created for ARB_EDIT4";
906                    warnIfSavingDefault = false;
907                    break;
908                }
909                case FROM_MANAGER: {
910                    if (previous.exists()) {
911                        prevComment = previous.get_comment();
912                        comment     = "updated manually";
913                    }
914                    else {
915                        prevComment                      = awr->awar(AWAR_CONFIG_COMMENT)->read_char_pntr();
916                        if (!prevComment[0]) prevComment = NULp;
917                        comment                          = "created manually";
918                    }
919                    break;
920                }
921                case FROM_IMPORTER:
922                    nt_assert(!previous.exists());
923                    comment = "created by importer";
924                    break;
925            }
926
927            nt_assert(implicated(prevComment, comment));
928            if (comment) {
929                // annotate with treename
930                const char *treename = awr->awar(AWAR_TREE_NAME)->read_char_pntr();
931                if (treename[0]) {
932                    comment = GBS_global_string("%s (tree=%s)", comment, treename);
933                }
934                else {
935                    comment = GBS_global_string("%s (no tree)", comment);
936                }
937                char *dated = GBS_log_action_to(prevComment, comment, true);
938                newcfg.set_comment(dated);
939                free(dated);
940            }
941
942            error = newcfg.save(GLOBAL.gb_main, conf_name, warnIfSavingDefault);
943            awr->awar(AWAR_CONFIGURATION)->touch(); // refreshes comment field
944        }
945    }
946
947    return error;
948}
949
950static void nt_store_configuration(AW_window*, TREE_canvas *ntw) {
951    const char *cfgName = AW_root::SINGLETON->awar(AWAR_CONFIGURATION)->read_char_pntr();
952    GB_ERROR    err     = nt_create_configuration(NT_get_tree_root_of_canvas(ntw), cfgName, 0, FROM_MANAGER);
953    aw_message_if(err);
954}
955
956static void nt_rename_configuration(AW_window *aww) {
957    AW_awar  *awar_curr_cfg = aww->get_root()->awar(AWAR_CONFIGURATION);
958    char     *old_name      = awar_curr_cfg->read_string();
959
960    {
961        char *new_name = aw_input("Rename selection", "Enter the new name of the selection", old_name);
962        if (new_name) {
963            GB_ERROR err = NULp;
964
965            {
966                GB_transaction ta(GLOBAL.gb_main);
967
968                GBDATA *gb_existing_cfg  = GBT_find_configuration(GLOBAL.gb_main, new_name);
969                if (gb_existing_cfg) err = GBS_global_string("There is already a selection named '%s'", new_name);
970                else {
971                    GBDATA *gb_old_cfg = GBT_find_configuration(GLOBAL.gb_main, old_name);
972                    if (gb_old_cfg) {
973                        GBDATA *gb_name = GB_entry(gb_old_cfg, "name");
974                        if (gb_name) {
975                            err = GB_write_string(gb_name, new_name);
976                            if (!err) awar_curr_cfg->write_string(new_name);
977                        }
978                        else err = "Selection has no name";
979                    }
980                    else err = "Can't find that selection";
981                }
982                err = ta.close(err);
983            }
984
985            if (err) {
986                aw_message(err);
987            }
988            else {
989                nt_assert(GB_get_transaction_level(GLOBAL.gb_main) == 0); // otherwise callback below behaves wrong
990                configuration_renamed_cb(old_name, new_name);
991            }
992            free(new_name);
993        }
994    }
995    free(old_name);
996}
997
998static void selected_config_changed_cb(AW_root *root) {
999    const char *config = root->awar(AWAR_CONFIGURATION)->read_char_pntr();
1000
1001    bool    nonexisting_config = false;
1002    GBDATA *gb_target_commment = NULp;
1003    if (config[0]) {
1004        GBDATA *gb_configuration = GBT_find_configuration(GLOBAL.gb_main, config);
1005        if (gb_configuration) {
1006            gb_target_commment = GB_entry(gb_configuration, "comment");
1007        }
1008        else {
1009            nonexisting_config = true;
1010        }
1011    }
1012
1013    AW_awar *awar_comment = root->awar(AWAR_CONFIG_COMMENT);
1014    if (gb_target_commment) {
1015        if (!awar_comment->is_mapped()) awar_comment->write_string("");
1016        awar_comment->map(gb_target_commment);
1017    }
1018    else {
1019        char *reuse_comment = nonexisting_config ? awar_comment->read_string() : ARB_strdup("");
1020        if (awar_comment->is_mapped()) {
1021            awar_comment->unmap();
1022        }
1023        awar_comment->write_string(reuse_comment);
1024        free(reuse_comment);
1025    }
1026}
1027static void config_comment_changed_cb(AW_root *root) {
1028    // called when comment-awar changes or gets re-map-ped
1029
1030    AW_awar    *awar_comment = root->awar(AWAR_CONFIG_COMMENT);
1031    const char *comment      = awar_comment->read_char_pntr();
1032
1033    const char *config           = root->awar(AWAR_CONFIGURATION)->read_char_pntr();
1034    GBDATA     *gb_configuration = config[0] ? GBT_find_configuration(GLOBAL.gb_main, config) : NULp;
1035
1036    GB_ERROR error = NULp;
1037    if (awar_comment->is_mapped()) {
1038        if (!comment[0]) { // empty existing comment
1039            nt_assert(gb_configuration);
1040            GBDATA *gb_commment = GB_entry(gb_configuration, "comment");
1041            nt_assert(gb_commment);
1042            if (gb_commment) {
1043                awar_comment->unmap();
1044                error = GB_delete(gb_commment);
1045            }
1046        }
1047    }
1048    else {
1049        if (comment[0]) { // ignore empty comment for unmapped awar
1050            if (gb_configuration) {
1051                nt_assert(!GB_entry(gb_configuration, "comment"));
1052                error = GBT_write_string(gb_configuration, "comment", comment);
1053                if (!error) {
1054                    awar_comment->write_string("");
1055                    selected_config_changed_cb(root);
1056                }
1057            }
1058            else if (!config[0]) {
1059                // do NOT warn if name field contains (not yet) existing name
1060                // (allows to edit comment while creating new config)
1061                error = "Please select an existing species selection to edit its comment";
1062            }
1063        }
1064    }
1065
1066    aw_message_if(error);
1067}
1068
1069static void init_config_admin_awars(AW_root *root) {
1070    init_config_awars(root);
1071    root->awar_string(AWAR_CONFIG_COMMENT, "", GLOBAL.gb_main)->add_callback(config_comment_changed_cb);
1072    root->awar(AWAR_CONFIGURATION)->add_callback(selected_config_changed_cb)->touch();
1073}
1074
1075#pragma GCC diagnostic push
1076#if (GCC_VERSION_CODE<700)
1077#pragma GCC diagnostic ignored "-Wstrict-overflow" // gcc 6.x produces a bogus overflow warning (gcc 7.x is smart enough)
1078#endif
1079
1080static GB_ERROR swap_configs(GBDATA *gb_main, StrArray& config, int i1, int i2) {
1081    GB_ERROR error = NULp;
1082
1083    if (i1>i2) swap(i1, i2); // otherwise overwrite below does not work
1084    nt_assert(i1<i2 && i1>=0 && i2<int(config.size()));
1085
1086    GBT_config c1(gb_main, config[i1], error);
1087    if (!error) {
1088        GBT_config c2(gb_main, config[i2], error);
1089        if (!error) error = c1.saveAsOver(gb_main, config[i1], config[i2], false);
1090        if (!error) error = c2.saveAsOver(gb_main, config[i2], config[i1], false);
1091        if (!error) config.swap(i1, i2);
1092    }
1093    return error;
1094}
1095
1096#pragma GCC diagnostic pop
1097
1098static void reorder_configs_cb(AW_window *aww, awt_reorder_mode mode, AW_DB_selection *sel) {
1099    AW_root    *awr         = aww->get_root();
1100    AW_awar    *awar_config = awr->awar(AWAR_CONFIGURATION);
1101    const char *selected    = awar_config->read_char_pntr();
1102
1103    if (selected && selected[0]) {
1104        AW_selection_list *sellist = sel->get_sellist();
1105
1106        int source_idx = sellist->get_index_of(AW_scalar(selected));
1107        int target_idx = -1;
1108        switch (mode) {
1109            case ARM_TOP:    target_idx = 0;            break;
1110            case ARM_UP:     target_idx = source_idx-1; break;
1111            case ARM_DOWN:   target_idx = source_idx+1; break;
1112            case ARM_BOTTOM: target_idx = -1;           break;
1113        }
1114
1115        int entries = sellist->size();
1116        target_idx  = (target_idx+entries)%entries;
1117
1118        {
1119            GBDATA         *gb_main = sel->get_gb_main();
1120            GB_transaction  ta(gb_main);
1121
1122            StrArray config;
1123            sellist->to_array(config, true);
1124
1125            GB_ERROR error = NULp;
1126            if (source_idx<target_idx) {
1127                for (int i = source_idx+1; i<=target_idx; ++i) {
1128                    swap_configs(gb_main, config, i-1, i);
1129                }
1130            }
1131            else if (source_idx>target_idx) {
1132                for (int i = source_idx-1; i>=target_idx; --i) {
1133                    swap_configs(gb_main, config, i+1, i);
1134                }
1135            }
1136
1137            error = ta.close(error);
1138            aw_message_if(error);
1139        }
1140        awar_config->touch();
1141    }
1142}
1143
1144static void clear_comment_cb(AW_window *aww) {
1145    AW_awar *awar_comment = aww->get_root()->awar(AWAR_CONFIG_COMMENT);
1146    char    *comment      = awar_comment->read_string();
1147
1148    ConstStrArray line;
1149    GBT_splitNdestroy_string(line, comment, '\n');
1150
1151    bool    removedDatedLines = false;
1152    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)
1153    for (int i = line.size()-1; i >= 0; --i) {
1154        const RegMatch *match = datedLine.match(line[i]);
1155        gb_assert(implicated(!match, !datedLine.has_failed())); // assert RegExpr compiles
1156        if (match && match->didMatch()) {
1157            line.safe_remove(i);
1158            removedDatedLines = true;
1159        }
1160    }
1161
1162    if (!removedDatedLines) line.clear(); // erase all
1163
1164    comment = GBT_join_strings(line, '\n');
1165    awar_comment->write_string(comment);
1166}
1167
1168static AW_window *create_configuration_admin_window(AW_root *root, TREE_canvas *ntw) {
1169    static AW_window_simple *existing_aws[MAX_NT_WINDOWS] = { MAX_NT_WINDOWS_NULLINIT };
1170
1171    int ntw_id = ntw->get_index();
1172    if (!existing_aws[ntw_id]) {
1173        init_config_admin_awars(root);
1174
1175        AW_window_simple *aws = new AW_window_simple;
1176        aws->init(root, GBS_global_string("SPECIES_SELECTIONS_%i", ntw_id), "Species Selections");
1177        aws->load_xfig("nt_selection.fig");
1178
1179        aws->at("close");
1180        aws->callback(AW_POPDOWN);
1181        aws->create_button("CLOSE", "CLOSE", "C");
1182
1183        aws->at("help");
1184        aws->callback(makeHelpCallback("species_configs.hlp"));
1185        aws->create_button("HELP", "HELP", "H");
1186
1187        aws->at("name");
1188        aws->create_input_field(AWAR_CONFIGURATION);
1189
1190        aws->at("comment");
1191        aws->create_text_field(AWAR_CONFIG_COMMENT);
1192
1193        aws->at("clr");
1194        aws->callback(clear_comment_cb);
1195        aws->create_autosize_button("CLEAR", "Clear", "l");
1196
1197        aws->at("list");
1198        AW_DB_selection *dbsel = awt_create_CONFIG_selection_list(GLOBAL.gb_main, aws, AWAR_CONFIGURATION, false);
1199
1200        aws->button_length(8);
1201
1202        aws->at("store");
1203        aws->callback(makeWindowCallback(nt_store_configuration, ntw));
1204        aws->create_button(GBS_global_string("STORE_%i", ntw_id), "STORE", "S");
1205
1206        aws->at("extract");
1207        aws->callback(makeWindowCallback(nt_extract_configuration, CONF_EXTRACT));
1208        aws->create_button("EXTRACT", "EXTRACT", "E");
1209
1210        aws->at("mark");
1211        aws->callback(makeWindowCallback(nt_extract_configuration, CONF_MARK));
1212        aws->create_button("MARK", "MARK", "M");
1213
1214        aws->at("unmark");
1215        aws->callback(makeWindowCallback(nt_extract_configuration, CONF_UNMARK));
1216        aws->create_button("UNMARK", "UNMARK", "U");
1217
1218        aws->at("invert");
1219        aws->callback(makeWindowCallback(nt_extract_configuration, CONF_INVERT));
1220        aws->create_button("INVERT", "INVERT", "I");
1221
1222        aws->at("combine");
1223        aws->callback(makeWindowCallback(nt_extract_configuration, CONF_COMBINE));
1224        aws->create_button("COMBINE", "COMBINE", "C");
1225
1226        aws->at("delete");
1227        aws->callback(makeWindowCallback(nt_delete_configuration, dbsel));
1228        aws->create_button("DELETE", "DELETE", "D");
1229
1230        aws->at("rename");
1231        aws->callback(nt_rename_configuration);
1232        aws->create_button("RENAME", "RENAME", "R");
1233
1234        aws->at("highlight");
1235        aws->callback(makeCreateWindowCallback(create_configuration_marker_window, ntw));
1236        aws->create_autosize_button(GBS_global_string("HIGHLIGHT_%i", ntw_id), "Highlight in tree", "t");
1237
1238        aws->button_length(0);
1239        aws->at("sort");
1240        awt_create_order_buttons(aws, reorder_configs_cb, dbsel);
1241
1242        existing_aws[ntw_id] = aws;
1243    }
1244    return existing_aws[ntw_id];
1245}
1246
1247void NT_popup_configuration_admin(AW_window *aw_main, TREE_canvas *ntw) {
1248    create_configuration_admin_window(aw_main->get_root(), ntw)->activate();
1249}
1250
1251// -----------------------------------------
1252//      various ways to start the editor
1253
1254static void nt_start_editor_on_configuration(AW_window *aww) {
1255    aww->hide();
1256
1257    const char *cfgName   = aww->get_root()->awar(AWAR_CONFIGURATION)->read_char_pntr();
1258    char       *quotedCfg = GBK_singlequote(cfgName);
1259
1260    AWT_system_cb(GBS_global_string("arb_edit4 -c %s &", quotedCfg));
1261
1262    free(quotedCfg);
1263}
1264
1265AW_window *NT_create_startEditorOnOldConfiguration_window(AW_root *awr) {
1266    static AW_window_simple *aws = NULp;
1267    if (!aws) {
1268        init_config_awars(awr);
1269
1270        aws = new AW_window_simple;
1271        aws->init(awr, "SELECT_CONFIGURATION", "SELECT A CONFIGURATION");
1272        aws->at(10, 10);
1273        aws->auto_space(0, 0);
1274        awt_create_CONFIG_selection_list(GLOBAL.gb_main, aws, AWAR_CONFIGURATION, false);
1275        aws->at_newline();
1276
1277        aws->callback(nt_start_editor_on_configuration);
1278        aws->create_button("START", "START");
1279
1280        aws->callback(AW_POPDOWN);
1281        aws->create_button("CLOSE", "CLOSE", "C");
1282
1283        aws->window_fit();
1284    }
1285    return aws;
1286}
1287
1288void NT_start_editor_on_tree(AW_window *aww, int use_species_aside, TREE_canvas *ntw) {
1289    init_config_awars(aww->get_root());
1290    GB_ERROR error    = nt_create_configuration(NT_get_tree_root_of_canvas(ntw), DEFAULT_CONFIGURATION, use_species_aside, BY_CALLING_THE_EDITOR);
1291    if (!error) error = GBK_system("arb_edit4 -c " DEFAULT_CONFIGURATION " &");
1292    aw_message_if(error);
1293}
1294
1295inline void nt_create_config_after_import(TREE_canvas *ntw) {
1296    init_config_awars(ntw->awr);
1297
1298    const char *dated_suffix = ARB_dateTime_suffix();
1299    char       *configName   = GBS_global_string_copy("imported_%s", dated_suffix);
1300
1301    // ensure unique config-name
1302    {
1303        int unique = 1;
1304        GB_transaction ta(ntw->gb_main);
1305        while (GBT_find_configuration(ntw->gb_main, configName)) {
1306            freeset(configName, GBS_global_string_copy("imported_%s_%i", dated_suffix, ++unique));
1307        }
1308    }
1309
1310    GB_ERROR error = nt_create_configuration(NT_get_tree_root_of_canvas(ntw), configName, 0, FROM_IMPORTER);
1311    aw_message_if(error);
1312
1313    free(configName);
1314}
1315
1316void NT_create_config_after_import(TREE_canvas *ntw, bool imported_from_scratch) {
1317    /*! create a new config after import
1318     * @param imported_from_scratch if true -> DB was created from scratch, all species in DB are marked.
1319     *                              if false -> data was imported into existing DB. Other species may be marked as well, imported species are "queried".
1320     */
1321
1322    if (imported_from_scratch) {
1323        nt_create_config_after_import(ntw);
1324    }
1325    else {
1326        GB_transaction ta(ntw->gb_main);
1327
1328        // remember marks + mark queried species:
1329        for (GBDATA *gb_species = GBT_first_species(ntw->gb_main); gb_species; gb_species = GBT_next_species(gb_species)) {
1330            GB_write_user_flag(gb_species, GB_USERFLAG_WASMARKED, GB_read_flag(gb_species));
1331            GB_write_flag(gb_species, GB_user_flag(gb_species, GB_USERFLAG_QUERY));
1332        }
1333
1334        nt_create_config_after_import(ntw);
1335
1336        // restore old marks:
1337        for (GBDATA *gb_species = GBT_first_species(ntw->gb_main); gb_species; gb_species = GBT_next_species(gb_species)) {
1338            GB_write_flag(gb_species, GB_user_flag(gb_species, GB_USERFLAG_WASMARKED));
1339            GB_clear_user_flag(gb_species, GB_USERFLAG_WASMARKED);
1340        }
1341    }
1342}
1343
1344
1345
Note: See TracBrowser for help on using the repository browser.