source: tags/ms_r16q2/NTREE/NT_edconf.cxx

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