source: tags/ms_r16q3/NTREE/NT_edconf.cxx

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