source: tags/ms_r18q1/EDIT4/ED4_no_class.cxx

Last change on this file was 16766, checked in by westram, 6 years ago
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 84.5 KB
Line 
1// =============================================================== //
2//                                                                 //
3//   File      : ED4_no_class.cxx                                  //
4//   Purpose   :                                                   //
5//                                                                 //
6//   Institute of Microbiology (Technical University Munich)       //
7//   http://www.arb-home.de/                                       //
8//                                                                 //
9// =============================================================== //
10
11#include <ed4_extern.hxx>
12
13#include "ed4_awars.hxx"
14#include "ed4_class.hxx"
15#include "ed4_edit_string.hxx"
16#include "ed4_nds.hxx"
17#include "ed4_list.hxx"
18#include "ed4_seq_colors.hxx"
19#include "ed4_flags.hxx"
20
21#include <iupac.h>
22#include <consensus_config.h>
23#include <item_sel_list.h>
24#include <macros.hxx>
25
26#include <awt.hxx>
27#include <awt_config_manager.hxx>
28#include <awt_misc.hxx>
29#include <awt_sel_boxes.hxx>
30
31#include <aw_awars.hxx>
32#include <AW_helix.hxx>
33#include <aw_msg.hxx>
34#include <AW_rename.hxx>
35#include <aw_root.hxx>
36
37#include <ad_config.h>
38
39#include <arb_defs.h>
40#include <arb_global_defs.h>
41#include <arb_progress.h>
42
43#include <cctype>
44#include <limits.h>
45
46#include <vector>
47
48using namespace std;
49
50struct group_folding {
51    int max_depth;    // maximum group level (root-group has level 0)
52    int max_visible;  // max visible group level (even if folded)
53    int max_unfolded; // max visible unfolded group level
54
55    group_folding() :
56        max_depth(0),
57        max_visible(0),
58        max_unfolded(0)
59    {}
60};
61
62static ARB_ERROR update_group_folding(ED4_base *base, group_folding *folding) {
63    if (base->is_group_manager()) {
64        int group_level = base->calc_group_depth()+1;
65        folding->max_depth = std::max(folding->max_depth, group_level);
66
67        bool is_folded = base->has_property(PROP_IS_FOLDED);
68        if (group_level>folding->max_visible || (group_level>folding->max_unfolded && !is_folded)) { // may change other maxima?
69            bool is_visible = !base->is_hidden();
70            if (is_visible) {
71                folding->max_visible = std::max(folding->max_visible, group_level);
72                if (!is_folded) {
73                    folding->max_unfolded = std::max(folding->max_unfolded, group_level);
74                }
75            }
76        }
77    }
78    return NULp;
79}
80
81static void calculate_group_folding(group_folding& folding) {
82    if (ED4_ROOT->main_manager) {
83        ED4_ROOT->main_manager->route_down_hierarchy(makeED4_route_cb(update_group_folding, &folding)).expect_no_error();
84    }
85}
86
87void ED4_calc_terminal_extentions() {
88    ED4_ROOT->recalc_font_group();
89
90    AW_device *device = ED4_ROOT->first_window->get_device(); // any device
91
92    const AW_font_group&  font_group       = ED4_ROOT->font_group;
93    const AW_font_limits& seq_font_limits  = font_group.get_limits(ED4_G_SEQUENCES);
94    const AW_font_limits& seq_equal_limits = device->get_font_limits(ED4_G_SEQUENCES, '=');
95    const AW_font_limits& info_font_limits = font_group.get_limits(ED4_G_STANDARD);
96
97    int info_char_width = info_font_limits.width;
98    int seq_term_descent;
99
100    if (ED4_ROOT->helix->is_enabled() || ED4_ROOT->protstruct) { // display helix ?
101        ED4_ROOT->helix_spacing =
102            seq_equal_limits.ascent // the ascent of '='
103            + ED4_ROOT->helix_add_spacing; // xtra user-defined spacing
104
105        seq_term_descent = ED4_ROOT->helix_spacing;
106    }
107    else {
108        ED4_ROOT->helix_spacing = 0;
109        seq_term_descent  = seq_font_limits.descent;
110    }
111
112    // for wanted_seq_term_height ignore descent, because it additionally allocates 'ED4_ROOT->helix_spacing' space:
113    int wanted_seq_term_height = seq_font_limits.ascent + seq_term_descent + ED4_ROOT->terminal_add_spacing;
114    int wanted_seq_info_height = info_font_limits.get_height() + ED4_ROOT->terminal_add_spacing;
115
116    TERMINAL_HEIGHT = (wanted_seq_term_height>wanted_seq_info_height) ? wanted_seq_term_height : wanted_seq_info_height;
117
118    {
119        group_folding folding;
120        calculate_group_folding(folding);
121
122        int maxbrackets = folding.max_unfolded;
123        int maxchars    = ED4_get_NDS_width();
124
125#if defined(DEBUG)
126        fprintf(stderr, "maxbrackets=%i\n", maxbrackets);
127#endif
128
129        MAXNAME_WIDTH =
130            (maxchars+1+1)*info_char_width + // width defined in NDS window (+ 1 char for marked-box; + 1 extra char to avoid truncation)
131            maxbrackets*BRACKET_WIDTH;       // brackets defined in NDS window
132    }
133
134    {
135        SpeciesFlags& flags     = SpeciesFlags::mutable_instance();
136        int           headerlen = flags.get_header_length();
137
138        if (headerlen) {
139            flags.calculate_header_dimensions(device, ED4_G_FLAG_INFO);
140            FLAG_WIDTH = flags.get_pixel_width();
141        }
142        else {
143            FLAG_WIDTH = 0;
144        }
145    }
146
147    MAXINFO_WIDTH =
148        CHARACTEROFFSET +
149        info_char_width*ED4_ROOT->aw_root->awar(ED4_AWAR_NDS_INFO_WIDTH)->read_int() +
150        1; // + 1 extra char to avoid truncation
151
152    INFO_TERM_TEXT_YOFFSET = info_font_limits.ascent - 1;
153    SEQ_TERM_TEXT_YOFFSET  = seq_font_limits.ascent - 1;
154
155    if (INFO_TERM_TEXT_YOFFSET<SEQ_TERM_TEXT_YOFFSET) INFO_TERM_TEXT_YOFFSET = SEQ_TERM_TEXT_YOFFSET;
156
157#if defined(DEBUG) && 0
158    printf("seq_term_descent = %i\n", seq_term_descent);
159    printf("TERMINAL_HEIGHT  = %i\n", TERMINAL_HEIGHT);
160    printf("MAXNAME_WIDTH    = %i\n", MAXNAME_WIDTH);
161    printf("FLAG_WIDTH       = %i\n", FLAG_WIDTH);
162    printf("MAXINFO_WIDTH    = %i\n", MAXINFO_WIDTH);
163    printf("INFO_TERM_TEXT_YOFFSET= %i\n", INFO_TERM_TEXT_YOFFSET);
164    printf(" SEQ_TERM_TEXT_YOFFSET= %i\n", SEQ_TERM_TEXT_YOFFSET);
165#endif // DEBUG
166}
167
168bool ED4_flag_header_terminal::set_dynamic_size() {
169    return extension.set_size_does_change(WIDTH, FLAG_WIDTH);
170}
171bool ED4_flag_terminal::set_dynamic_size() {
172    return extension.set_size_does_change(WIDTH, FLAG_WIDTH);
173}
174bool ED4_species_name_terminal::set_dynamic_size() {
175    return extension.set_size_does_change(WIDTH, MAXNAME_WIDTH - BRACKET_WIDTH * calc_group_depth());
176}
177bool ED4_sequence_info_terminal::set_dynamic_size() {
178    return extension.set_size_does_change(WIDTH, MAXINFO_WIDTH);
179}
180bool ED4_line_terminal::set_dynamic_size() {
181    // dynamically adapt to ref_terminals
182
183    AW_pos overall_width =
184        TREE_TERMINAL_WIDTH +
185        MAXNAME_WIDTH +
186        ED4_ROOT->ref_terminals.sequence_info()->extension.size[WIDTH] +
187        ED4_ROOT->ref_terminals.sequence()->extension.size[WIDTH];
188
189    return extension.set_size_does_change(WIDTH, overall_width);
190}
191bool ED4_spacer_terminal::set_dynamic_size() {
192    if (!has_property(PROP_DYNA_RESIZE)) return false; // some spacer terminals never change their size (eg. bottom-spacer)
193
194    AW_pos new_height = SPACER_HEIGHT;
195
196    if (parent->is_device_manager()) {
197        if (this == ED4_ROOT->main_manager->get_top_middle_spacer_terminal()) {
198            ED4_terminal *top_middle_line_terminal = ED4_ROOT->main_manager->get_top_middle_line_terminal();
199
200            // top-middle spacer + top-middle line >= font-size (otherwise scrolling relicts!)
201            new_height = TERMINAL_HEIGHT - top_middle_line_terminal->extension.size[HEIGHT];
202        }
203        else {
204            new_height = 1; // use minimal height for other top-group spacers
205        }
206    }
207    else {
208        ED4_manager *grandpa = parent->parent;
209        e4_assert(grandpa);
210
211        if (grandpa->is_group_manager()) {
212            ED4_group_manager *group_man = grandpa->to_group_manager();
213            if (group_man->has_property(PROP_IS_FOLDED)) {
214                if (!AW_root::SINGLETON->awar(ED4_AWAR_CONSENSUS_SHOW)->read_int()) {
215                    new_height = SPACER_NOCONS_HEIGHT;
216                }
217            }
218        }
219    }
220
221    return extension.set_size_does_change(HEIGHT, new_height);
222}
223
224static ARB_ERROR update_extension_size(ED4_base *base) {
225    base->resize_dynamic();
226    return NULp; // doesn't fail
227}
228
229void ED4_resize_all_extensions() { // @@@ pass flag to force resize-request? (eg. for initial-call?)
230    ED4_calc_terminal_extentions();
231
232    // @@@ below calculations have to be done at startup as well (are they done somewhere else or not done?)
233
234    ED4_ROOT->ref_terminals.sequence()->extension.size[HEIGHT]      = TERMINAL_HEIGHT;
235    ED4_ROOT->ref_terminals.sequence_info()->extension.size[HEIGHT] = TERMINAL_HEIGHT;
236    ED4_ROOT->ref_terminals.sequence_info()->extension.size[WIDTH]  = MAXINFO_WIDTH;
237
238    int screenwidth = ED4_ROOT->root_group_man->remap()->shown_sequence_to_screen(MAXSEQUENCECHARACTERLENGTH);
239    while (1) {
240        ED4_ROOT->ref_terminals.sequence()->extension.size[WIDTH] = ED4_ROOT->font_group.get_width(ED4_G_SEQUENCES) * (screenwidth+3);
241
242        ED4_ROOT->main_manager->route_down_hierarchy(makeED4_route_cb(update_extension_size)).expect_no_error();
243
244        ED4_ROOT->resize_all_requesting_childs(); // Note: may change mapping
245
246        int new_screenwidth = ED4_ROOT->root_group_man->remap()->shown_sequence_to_screen(MAXSEQUENCECHARACTERLENGTH);
247        if (new_screenwidth == screenwidth) { // mapping did not change
248            break;
249        }
250        screenwidth = new_screenwidth;
251    }
252}
253
254static ARB_ERROR call_edit(ED4_base *object, ED4_work_info *work_info) {
255    // called after editing consensus to edit single sequences
256    GB_ERROR error = NULp;
257
258    if (object->is_species_seq_terminal()) {
259        int expected_prop = PROP_CURSOR_ALLOWED|PROP_ALIGNMENT_DATA;
260
261        if ((object->dynamic_prop & expected_prop) == expected_prop) {
262            ED4_work_info new_work_info;
263
264            new_work_info.event            = work_info->event;
265            new_work_info.char_position    = work_info->char_position;
266            new_work_info.out_seq_position = work_info->out_seq_position;
267            new_work_info.refresh_needed   = false;
268            new_work_info.cursor_jump      = ED4_JUMP_KEEP_VISIBLE;
269            new_work_info.out_string       = NULp;
270            new_work_info.mode             = work_info->mode;
271            new_work_info.rightward        = work_info->rightward;
272            new_work_info.cannot_handle    = false;
273            new_work_info.is_sequence      = work_info->is_sequence;
274            new_work_info.working_terminal = object->to_terminal();
275
276            if (object->get_species_pointer()) {
277                new_work_info.gb_data   = object->get_species_pointer();
278                new_work_info.string    = NULp;
279            }
280            else {
281                new_work_info.gb_data = NULp;
282                new_work_info.string  = object->id; // @@@ looks obsolete (see [8402] for previous code)
283                e4_assert(0); // assume we never come here
284            }
285
286            new_work_info.repeat_count = 1;
287
288            ED4_ROOT->edit_string->init_edit();
289            error = ED4_ROOT->edit_string->edit(&new_work_info);
290
291            e4_assert(error || !new_work_info.out_string); 
292               
293            if (new_work_info.refresh_needed) {
294                object->request_refresh();
295                if (object->is_sequence_terminal()) {
296                    ED4_sequence_terminal *seq_term = object->to_sequence_terminal();
297                    seq_term->results().searchAgain();
298                }
299            }
300
301            if (move_cursor) {
302                current_cursor().jump_sequence_pos(new_work_info.out_seq_position, ED4_JUMP_KEEP_VISIBLE);
303                move_cursor = 0;
304            }
305        }
306    }
307    return error;
308}
309
310static void executeKeystroke(AW_event *event, int repeatCount) {
311    e4_assert(repeatCount>0);
312
313    if (event->keycode!=AW_KEY_NONE) {
314        ED4_cursor *cursor = &current_cursor();
315        if (cursor->owner_of_cursor && !cursor->owner_of_cursor->flag.hidden) {
316            if (event->keycode == AW_KEY_UP || event->keycode == AW_KEY_DOWN ||
317                ((event->keymodifier & AW_KEYMODE_CONTROL) &&
318                 (event->keycode == AW_KEY_HOME || event->keycode == AW_KEY_END)))
319            {
320                GB_transaction ta(GLOBAL_gb_main);
321                while (repeatCount--) {
322                    cursor->move_cursor(event);
323                }
324            }
325            else {
326                ED4_work_info *work_info = new ED4_work_info;
327       
328                work_info->cannot_handle    = false;
329                work_info->event            = *event;
330                work_info->char_position    = cursor->get_screen_pos();
331                work_info->out_seq_position = cursor->get_sequence_pos();
332                work_info->refresh_needed   = false;
333                work_info->cursor_jump      = ED4_JUMP_KEEP_VISIBLE;
334                work_info->out_string       = NULp;         // nur falls new malloc
335                work_info->repeat_count     = repeatCount;
336
337                ED4_terminal *terminal = cursor->owner_of_cursor;
338                e4_assert(terminal->is_text_terminal());
339
340                work_info->working_terminal = terminal;
341
342                if (terminal->is_sequence_terminal()) {
343                    work_info->mode        = awar_edit_mode;
344                    work_info->rightward   = awar_edit_rightward;
345                    work_info->is_sequence = 1;
346                }
347                else {
348                    work_info->rightward   = true;
349                    work_info->is_sequence = 0;
350
351                    if (terminal->is_pure_text_terminal()) {
352                        work_info->mode = awar_edit_mode;
353                    }
354                    else if (terminal->is_columnStat_terminal()) {
355                        work_info->mode = AD_NOWRITE;
356                    }
357                    else {
358                        e4_assert(0);
359                    }
360                }
361
362                work_info->string  = NULp;
363                work_info->gb_data = NULp;
364
365                if (terminal->get_species_pointer()) {
366                    work_info->gb_data = terminal->get_species_pointer();
367                }
368                else if (terminal->is_columnStat_terminal()) {
369                    work_info->gb_data = terminal->to_columnStat_terminal()->corresponding_sequence_terminal()->get_species_pointer();
370                }
371                else {
372                    e4_assert(terminal->is_consensus_terminal());
373                    ED4_consensus_sequence_terminal *cterm = terminal->to_consensus_sequence_terminal();
374                    work_info->string                      = cterm->temp_cons_seq;
375                }
376
377                ED4_Edit_String *edit_string = new ED4_Edit_String;
378                ARB_ERROR        error       = NULp;
379
380                GB_push_transaction(GLOBAL_gb_main);
381
382                if (terminal->is_consensus_terminal()) {
383                    ED4_consensus_sequence_terminal *cterm         = terminal->to_consensus_sequence_terminal();
384                    ED4_group_manager               *group_manager = terminal->get_parent(LEV_GROUP)->to_group_manager();
385
386                    e4_assert(!cterm->temp_cons_seq);
387                    work_info->string = cterm->temp_cons_seq = group_manager->build_consensus_string();
388
389                    error = edit_string->edit(work_info);
390
391                    cursor->jump_sequence_pos(work_info->out_seq_position, ED4_JUMP_KEEP_VISIBLE);
392
393                    work_info->string = NULp;
394
395                    if (work_info->cannot_handle) {
396                        e4_assert(!error); // see ED4_Edit_String::edit()
397                        move_cursor = 1;
398                        if (!ED4_ROOT->edit_string) {
399                            ED4_ROOT->edit_string = new ED4_Edit_String;
400                        }
401                        error = group_manager->route_down_hierarchy(makeED4_route_cb(call_edit, work_info));
402                        group_manager->rebuild_consensi(group_manager, ED4_U_UP_DOWN);
403                    }
404
405                    freenull(cterm->temp_cons_seq);
406                }
407                else {
408                    error = edit_string->edit(work_info);
409                    cursor->jump_sequence_pos(work_info->out_seq_position, work_info->cursor_jump);
410                }
411
412                edit_string->finish_edit();
413
414                if (error) work_info->refresh_needed = true;
415
416                GB_end_transaction_show_error(GLOBAL_gb_main, error, aw_message);
417
418                if (work_info->refresh_needed) {
419                    GB_transaction ta(GLOBAL_gb_main);
420
421                    terminal->request_refresh();
422                    if (terminal->is_sequence_terminal()) {
423                        ED4_sequence_terminal *seq_term = terminal->to_sequence_terminal();
424                        seq_term->results().searchAgain();
425                    }
426                }
427
428                delete edit_string;
429                delete work_info;
430            }
431        }
432    }
433}
434
435void ED4_remote_event(AW_event *faked_event) { // keystrokes forwarded from SECEDIT
436    ED4_MostRecentWinContext context;
437    executeKeystroke(faked_event, 1);
438}
439
440static int get_max_slider_xpos() {
441    const AW_screen_area& rect = current_device()->get_area_size();
442
443    AW_pos x, y;
444    ED4_base *horizontal_link = ED4_ROOT->scroll_links.link_for_hor_slider;
445    horizontal_link->calc_world_coords(&x, &y);
446
447    AW_pos max_xpos = horizontal_link->extension.size[WIDTH] // overall width of virtual scrolling area
448        - (rect.r - x); // minus width of visible scroll-area (== relative width of horizontal scrollbar)
449
450    if (max_xpos<0) max_xpos = 0; // happens when window-content is smaller than window (e.g. if (folded) alignment is narrow)
451    return int(max_xpos+0.5);
452}
453
454static int get_max_slider_ypos() {
455    const AW_screen_area& rect = current_device()->get_area_size(); 
456
457    AW_pos x, y;
458    ED4_base *vertical_link = ED4_ROOT->scroll_links.link_for_ver_slider;
459    vertical_link->calc_world_coords(&x, &y);
460
461    AW_pos max_ypos = vertical_link->extension.size[HEIGHT] // overall height of virtual scrolling area
462        - (rect.b - y); // minus height of visible scroll-area (== relative height of vertical scrollbar)
463
464    if (max_ypos<0) max_ypos = 0; // happens when window-content is smaller than window (e.g. if ARB_EDIT4 is not filled)
465    return int(max_ypos+0.5);
466}
467
468static void ed4_scroll(AW_window *aww, int xdiff, int ydiff) {
469    int new_xpos = aww->slider_pos_horizontal + (xdiff*ED4_ROOT->aw_root->awar(ED4_AWAR_SCROLL_SPEED_X)->read_int())/10;
470    int new_ypos = aww->slider_pos_vertical   + (ydiff*ED4_ROOT->aw_root->awar(ED4_AWAR_SCROLL_SPEED_Y)->read_int())/10;
471
472    if (xdiff<0) { // scroll left
473        if (new_xpos<0) new_xpos = 0;
474    }
475    else if (xdiff>0) { // scroll right
476        int max_xpos = get_max_slider_xpos();
477        if (max_xpos<0) max_xpos = 0;
478        if (new_xpos>max_xpos) new_xpos = max_xpos;
479    }
480
481    if (ydiff<0) { // scroll up
482        if (new_ypos<0) new_ypos = 0;
483    }
484    else if (ydiff>0) { // scroll down
485        int max_ypos = get_max_slider_ypos();
486        if (max_ypos<0) max_ypos = 0;
487        if (new_ypos>max_ypos) new_ypos = max_ypos;
488    }
489
490    if (new_xpos!=aww->slider_pos_horizontal) {
491        aww->set_horizontal_scrollbar_position(new_xpos);
492        ED4_horizontal_change_cb(aww);
493    }
494
495    if (new_ypos!=aww->slider_pos_vertical) {
496        aww->set_vertical_scrollbar_position(new_ypos);
497        ED4_vertical_change_cb(aww);
498    }
499}
500
501void ED4_input_cb(AW_window *aww) {
502    AW_event event;
503    static AW_event lastEvent;
504    static int repeatCount;
505
506    ED4_LocalWinContext uses(aww);
507
508    aww->get_event(&event);
509
510
511#if defined(DEBUG) && 0
512    printf("event.type=%i event.keycode=%i event.character='%c' event.keymodifier=%i\n", event.type, event.keycode, event.character, event.keymodifier);
513#endif
514
515    switch (event.type) {
516        case AW_Keyboard_Press: {
517            if (repeatCount==0) { // first key event?
518                lastEvent = event;
519                repeatCount = 1;
520            }
521            else {
522                if (lastEvent.keycode==event.keycode &&
523                    lastEvent.character==event.character &&
524                    lastEvent.keymodifier==event.keymodifier) { // same key as last?
525                    repeatCount++;
526                }
527                else { // other key => execute now
528                    executeKeystroke(&lastEvent, repeatCount);
529                    lastEvent = event;
530                    repeatCount = 1;
531                }
532            }
533
534            if (repeatCount) {
535#if defined(DARWIN) || 1
536                // sth goes wrong with OSX -> always execute keystroke
537                // Xfree 4.3 has problems as well, so repeat counting is disabled completely
538                executeKeystroke(&lastEvent, repeatCount);
539                repeatCount                       = 0;
540#else
541                AW_ProcessEventType nextEventType = ED4_ROOT->aw_root->peek_key_event(aww);
542
543                if (nextEventType!=KEY_RELEASED) { // no key waiting => execute now
544                    executeKeystroke(&lastEvent, repeatCount);
545                    repeatCount = 0;
546                }
547#endif
548            }
549            break;
550        }
551        case AW_Keyboard_Release: {
552            AW_ProcessEventType nextEventType = ED4_ROOT->aw_root->peek_key_event(aww);
553
554            if (nextEventType!=KEY_PRESSED && repeatCount) { // no key follows => execute keystrokes (if any)
555                executeKeystroke(&lastEvent, repeatCount);
556                repeatCount = 0;
557            }
558
559            break;
560        }
561        default: {
562            if (event.button == AW_WHEEL_UP || event.button == AW_WHEEL_DOWN) {
563                if (event.type == AW_Mouse_Press) {
564                    bool horizontal = event.keymodifier & AW_KEYMODE_ALT;
565                    int  direction  = event.button == AW_WHEEL_UP ? -1 : 1;
566
567                    int dx = horizontal ? direction*ED4_ROOT->font_group.get_max_width() : 0;
568                    int dy = horizontal ? 0 : direction*ED4_ROOT->font_group.get_max_height();
569               
570                    ed4_scroll(aww, dx, dy);
571                }
572                return;
573            }
574
575            if (event.button == AW_BUTTON_MIDDLE) {
576                if (event.type == AW_Mouse_Press) {
577                    ED4_ROOT->scroll_picture.scroll = 1;
578                    ED4_ROOT->scroll_picture.old_y = event.y;
579                    ED4_ROOT->scroll_picture.old_x = event.x;
580                    return;
581                }
582                if (event.type == AW_Mouse_Release) {
583                    ED4_ROOT->scroll_picture.scroll = 0;
584                    return;
585                }
586            }
587
588#if defined(DEBUG) && 0
589            if (event.button==AW_BUTTON_LEFT) {
590                printf("[ED4_input_cb]  type=%i x=%i y=%i ", (int)event.type, (int)event.x, (int)event.y);
591            }
592#endif
593
594            AW_pos win_x = event.x;
595            AW_pos win_y = event.y;
596            current_ed4w()->win_to_world_coords(&(win_x), &(win_y));
597            event.x = (int) win_x;
598            event.y = (int) win_y;
599
600#if defined(DEBUG) && 0
601            if (event.button==AW_BUTTON_LEFT) {
602                printf("-> x=%i y=%i\n", (int)event.type, (int)event.x, (int)event.y);
603            }
604#endif
605
606            GB_transaction ta(GLOBAL_gb_main);
607            ED4_ROOT->main_manager->event_sent_by_parent(&event, aww);
608            break;
609        }
610    }
611
612    ED4_trigger_instant_refresh();
613}
614
615void ED4_vertical_change_cb(AW_window *aww) {
616    ED4_LocalWinContext uses(aww);
617
618    GB_push_transaction(GLOBAL_gb_main);
619
620    ED4_window *win = current_ed4w();
621    int old_slider_pos = win->slider_pos_vertical;
622
623    { // correct slider_pos if necessary
624        int max_slider_ypos = get_max_slider_ypos();
625
626        if (aww->slider_pos_vertical>max_slider_ypos) aww->set_vertical_scrollbar_position(max_slider_ypos);
627        if (aww->slider_pos_vertical<0)               aww->set_vertical_scrollbar_position(0);
628    }
629
630    int slider_diff = aww->slider_pos_vertical - old_slider_pos;
631
632    win->coords.window_upper_clip_point += slider_diff;
633    win->coords.window_lower_clip_point += slider_diff;
634
635    win->scroll_rectangle(0, -slider_diff);
636    win->slider_pos_vertical = aww->slider_pos_vertical;
637
638    GB_pop_transaction(GLOBAL_gb_main);
639    win->update_window_coords();
640}
641
642void ED4_horizontal_change_cb(AW_window *aww) {
643    ED4_LocalWinContext uses(aww);
644
645    GB_push_transaction(GLOBAL_gb_main);
646
647    ED4_window *win = current_ed4w();
648    int old_slider_pos = win->slider_pos_horizontal;
649
650    { // correct slider_pos if necessary
651        int max_slider_xpos = get_max_slider_xpos();
652
653        if (aww->slider_pos_horizontal>max_slider_xpos) aww->set_horizontal_scrollbar_position(max_slider_xpos);
654        if (aww->slider_pos_horizontal<0)               aww->set_horizontal_scrollbar_position(0);
655    }
656
657    int slider_diff = aww->slider_pos_horizontal - old_slider_pos;
658
659    win->coords.window_left_clip_point  += slider_diff;
660    win->coords.window_right_clip_point += slider_diff;
661
662    win->scroll_rectangle(-slider_diff, 0);
663    win->slider_pos_horizontal = aww->slider_pos_horizontal;
664
665    GB_pop_transaction(GLOBAL_gb_main);
666    win->update_window_coords();
667}
668
669void ED4_scrollbar_change_cb(AW_window *aww) {
670    ED4_LocalWinContext uses(aww);
671
672    GB_push_transaction(GLOBAL_gb_main);
673
674    ED4_window *win = current_ed4w();
675
676    int old_hslider_pos = win->slider_pos_horizontal;
677    int old_vslider_pos = win->slider_pos_vertical;
678
679    {
680        // correct slider_pos if necessary
681        int max_slider_xpos = get_max_slider_xpos();
682        int max_slider_ypos = get_max_slider_ypos();
683
684        if (aww->slider_pos_horizontal>max_slider_xpos) aww->set_horizontal_scrollbar_position(max_slider_xpos);
685        if (aww->slider_pos_horizontal<0)               aww->set_horizontal_scrollbar_position(0);
686
687        if (aww->slider_pos_vertical>max_slider_ypos) aww->set_vertical_scrollbar_position(max_slider_ypos);
688        if (aww->slider_pos_vertical<0)               aww->set_vertical_scrollbar_position(0);
689    }
690
691    int slider_hdiff = aww->slider_pos_horizontal - old_hslider_pos;
692    int slider_vdiff = aww->slider_pos_vertical   - old_vslider_pos;
693
694    ED4_coords *coords = &win->coords;
695    coords->window_left_clip_point  += slider_hdiff;
696    coords->window_right_clip_point += slider_hdiff;
697    coords->window_upper_clip_point += slider_vdiff;
698    coords->window_lower_clip_point += slider_vdiff;
699
700    win->scroll_rectangle(-slider_hdiff, -slider_vdiff);
701
702    win->slider_pos_vertical   = aww->slider_pos_vertical;
703    win->slider_pos_horizontal = aww->slider_pos_horizontal;
704
705    GB_pop_transaction(GLOBAL_gb_main);
706    win->update_window_coords();
707}
708
709void ED4_motion_cb(AW_window *aww) {
710    AW_event event;
711
712    ED4_LocalWinContext uses(aww);
713
714    aww->get_event(&event);
715
716    if (event.type == AW_Mouse_Drag && event.button == AW_BUTTON_MIDDLE) {
717        if (ED4_ROOT->scroll_picture.scroll) {
718            int xdiff = ED4_ROOT->scroll_picture.old_x - event.x;
719            int ydiff = ED4_ROOT->scroll_picture.old_y - event.y;
720
721            ed4_scroll(aww, xdiff, ydiff);
722
723            ED4_ROOT->scroll_picture.old_x = event.x;
724            ED4_ROOT->scroll_picture.old_y = event.y;
725        }
726    }
727    else {
728
729#if defined(DEBUG) && 0
730        if (event.button==AW_BUTTON_LEFT) {
731            printf("[ED4_motion_cb] type=%i x=%i y=%i ", (int)event.type, (int)event.x, (int)event.y);
732        }
733#endif
734
735        AW_pos win_x = event.x;
736        AW_pos win_y = event.y;
737        current_ed4w()->win_to_world_coords(&win_x, &win_y);
738        event.x = (int) win_x;
739        event.y = (int) win_y;
740
741#if defined(DEBUG) && 0
742        if (event.button==AW_BUTTON_LEFT) {
743            printf("-> x=%i y=%i\n", (int)event.type, (int)event.x, (int)event.y);
744        }
745#endif
746
747        GB_transaction ta(GLOBAL_gb_main);
748        ED4_ROOT->main_manager->event_sent_by_parent(&event, aww);
749    }
750}
751
752void ED4_remote_set_cursor_cb(AW_root *awr) {
753    AW_awar *awar = awr->awar(AWAR_SET_CURSOR_POSITION);
754    long     pos  = awar->read_int();
755
756    if (pos != -1) {
757        ED4_MostRecentWinContext context;
758        ED4_cursor *cursor = &current_cursor();
759        cursor->jump_sequence_pos(pos, ED4_JUMP_CENTERED);
760        awar->write_int(-1);
761    }
762}
763
764void ED4_jump_to_cursor_position(AW_window *aww, const char *awar_name, PositionType posType) {
765    ED4_LocalWinContext  uses(aww);
766    ED4_cursor          *cursor = &current_cursor();
767    GB_ERROR             error  = NULp;
768
769    long pos = aww->get_root()->awar(awar_name)->read_int();
770
771    if (pos>0) pos = bio2info(pos);
772    else if (pos<0) { // jump negative (count from back)
773        int last_pos = -1; // [1..]
774
775        switch (posType) {
776            case ED4_POS_SEQUENCE: {
777                last_pos = MAXSEQUENCECHARACTERLENGTH;
778                break;
779            }
780            case ED4_POS_ECOLI: {
781                BI_ecoli_ref *ecoli = ED4_ROOT->ecoli_ref;
782                if (ecoli->gotData()) {
783                    last_pos = ecoli->abs_2_rel(INT_MAX);
784                }
785                else {
786                    last_pos = 0; // doesnt matter (error below)
787                }
788                break;
789            }
790            case ED4_POS_BASE: {
791                last_pos = cursor->sequence2base_position(INT_MAX);
792                break;
793            }
794        }
795
796        e4_assert(last_pos != -1);
797        pos = bio2info(last_pos+1+pos);
798    }
799
800    switch (posType) {
801        case ED4_POS_SEQUENCE: {
802            e4_assert(strcmp(awar_name, current_ed4w()->awar_path_for_cursor)==0);
803            break;
804        }
805        case ED4_POS_ECOLI: {
806            e4_assert(strcmp(awar_name, current_ed4w()->awar_path_for_Ecoli)==0);
807
808            BI_ecoli_ref *ecoli = ED4_ROOT->ecoli_ref;
809            if (ecoli->gotData()) pos = ecoli->rel_2_abs(pos);
810            else error = "No ecoli reference";
811            break;
812        }
813        case ED4_POS_BASE: {
814            e4_assert(strcmp(awar_name, current_ed4w()->awar_path_for_basePos)==0);
815            pos = cursor->base2sequence_position(pos); 
816            break;
817        }
818    }
819
820    // now position is absolute [0..N-1]
821
822    // limit to screen
823    {
824        ED4_remap *remap = ED4_ROOT->root_group_man->remap();
825        long       max   = remap->screen_to_sequence(remap->get_max_screen_pos());
826
827        if (pos > max) pos  = max;
828        else if (pos<0) pos = 0;
829    }
830
831    if (error) {
832        aw_message(error);
833    }
834    else {
835        cursor->jump_sequence_pos(pos, ED4_JUMP_CENTERED);
836    }
837}
838
839void ED4_set_helixnr(AW_window *aww, const char *awar_name) {
840    ED4_LocalWinContext uses(aww);
841    ED4_cursor *cursor = &current_cursor();
842
843    if (cursor->owner_of_cursor) {
844        AW_root  *root     = aww->get_root();
845        char     *helix_nr = root->awar(awar_name)->read_string();
846        BI_helix *helix    = ED4_ROOT->helix;
847
848        if (helix->has_entries()) {
849            long pos = helix->first_position(helix_nr);
850
851            if (pos == -1) {
852                aw_message(GBS_global_string("No helix '%s' found", helix_nr));
853            }
854            else {
855                cursor->jump_sequence_pos(pos, ED4_JUMP_CENTERED);
856            }
857        }
858        else {
859            aw_message("Got no helix information");
860        }
861        free(helix_nr);
862    }
863}
864
865void ED4_set_iupac(AW_window *aww, const char *awar_name) {
866    ED4_LocalWinContext uses(aww);
867    ED4_cursor *cursor = &current_cursor();
868
869    if (cursor->owner_of_cursor) {
870        if (cursor->in_consensus_terminal()) {
871            aw_message("You cannot change the consensus");
872        }
873        else {
874            int   len;
875            char *seq     = cursor->owner_of_cursor->resolve_pointer_to_string_copy(&len);
876            int   seq_pos = cursor->get_sequence_pos();
877
878            e4_assert(seq);
879
880            if (seq_pos<len) {
881                char *iupac    = ED4_ROOT->aw_root->awar(awar_name)->read_string();
882                char  new_char = iupac::encode(iupac, ED4_ROOT->alignment_type);
883
884                seq[seq_pos] = new_char;
885                cursor->owner_of_cursor->write_sequence(seq, len);
886
887                free(iupac);
888            }
889
890            free(seq);
891        }
892    }
893}
894
895void ED4_exit() {
896    ED4_ROOT->aw_root->unlink_awars_from_DB(GLOBAL_gb_main);
897
898    ED4_window *ed4w = ED4_ROOT->first_window;
899
900    while (ed4w) {
901        ed4w->aww->hide();
902        ed4w->cursor.prepare_shutdown(); // removes any callbacks
903        ed4w = ed4w->next;
904    }
905
906    while (ED4_ROOT->first_window)
907        ED4_ROOT->first_window->delete_window(ED4_ROOT->first_window);
908
909
910    shutdown_macro_recording(ED4_ROOT->aw_root);
911
912    delete ED4_ROOT;
913
914    GBDATA *gb_main = GLOBAL_gb_main;
915    GLOBAL_gb_main  = NULp;
916#if defined(DEBUG)
917    AWT_browser_forget_db(gb_main);
918#endif // DEBUG
919    GB_close(gb_main);
920
921    ::exit(EXIT_SUCCESS);
922}
923
924void ED4_quit_editor(AW_window *aww) {
925    ED4_LocalWinContext uses(aww); // @@@ dont use context here
926
927    if (ED4_ROOT->first_window == current_ed4w()) { // quit button has been pressed in first window
928        ED4_exit();
929    }
930    // case : in another window close has been pressed
931    current_aww()->hide();
932    current_ed4w()->is_hidden = true;
933}
934
935static int timer_calls           = 0;
936static int timer_calls_triggered = 0;
937
938static unsigned ED4_timer(AW_root *) {
939    timer_calls++;
940
941#if defined(TRACE_REFRESH)
942    fprintf(stderr, "ED4_timer\n"); fflush(stderr);
943#endif
944    // get all changes from server
945    GB_begin_transaction(GLOBAL_gb_main);
946    GB_tell_server_dont_wait(GLOBAL_gb_main);
947    GB_commit_transaction(GLOBAL_gb_main);
948
949    ED4_ROOT->refresh_all_windows(0);
950
951    if (timer_calls == timer_calls_triggered) {
952        timer_calls_triggered++;
953        return 2000; // trigger callback after 2s
954    }
955    return 0; // do not trigger callback
956}
957
958void ED4_trigger_instant_refresh() {
959#if defined(TRACE_REFRESH)
960    fprintf(stderr, "ED4_trigger_instant_refresh\n"); fflush(stderr);
961#endif
962    timer_calls_triggered++;
963    ED4_ROOT->aw_root->add_timed_callback(1, makeTimedCallback(ED4_timer)); // trigger instant callback
964}
965void ED4_request_full_refresh() {
966    ED4_ROOT->main_manager->request_refresh();
967}
968void ED4_request_full_instant_refresh() {
969    ED4_request_full_refresh();
970    ED4_trigger_instant_refresh();
971}
972
973void ED4_request_relayout() {
974    ED4_resize_all_extensions();
975    ED4_ROOT->main_manager->request_resize();
976    ED4_trigger_instant_refresh();
977}
978
979#define SIGNIFICANT_FIELD_CHARS 30 // length used to compare field contents (in createGroupFromSelected)
980
981static void createGroupFromSelected(GB_CSTR group_name, GB_CSTR field_name, GB_CSTR field_content) {
982    // creates a new group named group_name
983    // if field_name==0 -> all selected species & subgroups are moved to this new group
984    // if field_name!=0 -> all selected species containing field_content in field field_name are moved to this new group
985
986    ED4_multi_species_manager *top_multi_species_manager = ED4_ROOT->top_area_man->get_multi_species_manager();
987    ED4_multi_species_manager *group_content_manager;
988    ED4_group_manager         *new_group_manager         = ED4_build_group_manager_start(top_multi_species_manager, group_name, 1, false, ED4_ROOT->ref_terminals, group_content_manager);
989    ED4_build_group_manager_end(group_content_manager);
990
991    group_content_manager->update_requested_by_child();
992
993    ED4_counter++;
994    ED4_base::touch_world_cache();
995
996    bool lookingForNoContent = !field_content || field_content[0]==0;
997
998    ED4_selected_elem *list_elem = ED4_ROOT->selected_objects->head();
999    while (list_elem) {
1000        ED4_base *object = list_elem->elem()->object;
1001        object = object->get_parent(LEV_SPECIES);
1002
1003        bool move_object = true;
1004        if (object->is_consensus_manager()) {
1005            object = object->get_parent(LEV_GROUP);
1006            if (field_name) move_object = false; // don't move groups if moving by field_name
1007        }
1008        else {
1009            e4_assert(object->is_species_manager());
1010            if (field_name) {
1011                GBDATA *gb_species = object->get_species_pointer();
1012                GBDATA *gb_field = GB_search(gb_species, field_name, GB_FIND);
1013
1014                move_object = lookingForNoContent;
1015                if (gb_field) { // field was found
1016                    char *found_content = GB_read_as_string(gb_field);
1017                    if (found_content) {
1018                        move_object = strncmp(found_content, field_content, SIGNIFICANT_FIELD_CHARS)==0;
1019                        free(found_content);
1020                    }
1021                }
1022            }
1023        }
1024
1025        if (move_object) {
1026            ED4_base *base = object->get_parent(LEV_MULTI_SPECIES);
1027            if (base && base->is_multi_species_manager()) {
1028                ED4_multi_species_manager *old_multi = base->to_multi_species_manager();
1029                old_multi->invalidate_species_counters();
1030            }
1031           
1032            object->parent->remove_member(object);
1033            group_content_manager->append_member(object);
1034
1035            object->parent = group_content_manager;
1036            object->set_width();
1037        }
1038
1039        list_elem = list_elem->next();
1040    }
1041
1042    new_group_manager->create_consensus(new_group_manager, NULp);
1043    group_content_manager->invalidate_species_counters();
1044   
1045    new_group_manager->fold();
1046
1047    group_content_manager->resize_requested_by_child();
1048}
1049
1050static void group_species(bool use_field, AW_window *use_as_main_window) {
1051    GB_ERROR error = NULp;
1052    GB_push_transaction(GLOBAL_gb_main);
1053
1054    ED4_LocalWinContext uses(use_as_main_window);
1055
1056    if (!use_field) {
1057        char *group_name = aw_input("Enter name for new group:");
1058
1059        if (group_name) {
1060            if (strlen(group_name)>GB_GROUP_NAME_MAX) {
1061                group_name[GB_GROUP_NAME_MAX] = 0;
1062                aw_message("Truncated overlong group name");
1063            }
1064            createGroupFromSelected(group_name, NULp, NULp);
1065            free(group_name);
1066        }
1067    }
1068    else {
1069        char   *field_name   = ED4_ROOT->aw_root->awar(AWAR_FIELD_CHOSEN)->read_string();
1070        char   *doneContents = ARB_strdup(";");
1071        size_t  doneLen      = 1;
1072
1073        bool tryAgain     = true;
1074        bool foundField   = false;
1075        bool foundSpecies = false;
1076
1077        if (strcmp(field_name, NO_FIELD_SELECTED) == 0) {
1078            error = "Please select a field to use for grouping.";
1079        }
1080
1081        while (tryAgain && !error) {
1082            tryAgain = false;
1083            ED4_selected_elem *list_elem = ED4_ROOT->selected_objects->head();
1084            while (list_elem && !error) {
1085                ED4_base *object = list_elem->elem()->object;
1086                object = object->get_parent(LEV_SPECIES);
1087                if (!object->is_consensus_manager()) {
1088                    GBDATA *gb_species = object->get_species_pointer();
1089                    GBDATA *gb_field   = NULp;
1090
1091                    if (gb_species) {
1092                        foundSpecies = true;
1093                        gb_field     = GB_search(gb_species, field_name, GB_FIND);
1094                    }
1095
1096                    error = GB_incur_error_if(!gb_field);
1097                    if (!error) {
1098                        e4_assert(gb_field);
1099                        char *field_content = GB_read_as_string(gb_field);
1100                        if (field_content) {
1101                            size_t field_content_len = strlen(field_content);
1102
1103                            foundField = true;
1104                            if (field_content_len>SIGNIFICANT_FIELD_CHARS) {
1105                                field_content[SIGNIFICANT_FIELD_CHARS] = 0;
1106                                field_content_len                      = SIGNIFICANT_FIELD_CHARS;
1107                            }
1108
1109                            char with_semi[SIGNIFICANT_FIELD_CHARS+2+1];
1110                            sprintf(with_semi, ";%s;", field_content);
1111
1112                            if (!strstr(doneContents, with_semi)) { // field_content was not used yet
1113                                createGroupFromSelected(field_content, field_name, field_content);
1114                                tryAgain = true;
1115
1116                                int   newlen  = doneLen + field_content_len + 1;
1117                                char *newDone = ARB_alloc<char>(newlen+1);
1118
1119                                GBS_global_string_to_buffer(newDone, newlen+1, "%s%s;", doneContents, field_content);
1120                                freeset(doneContents, newDone);
1121                                doneLen = newlen;
1122                            }
1123                            free(field_content);
1124                        }
1125                        else {
1126                            error = "Incompatible field type";
1127                        }
1128                    }
1129                }
1130                list_elem = list_elem->next();
1131            }
1132        }
1133
1134        if (!error) {
1135            if      (!foundSpecies) error = "Please select some species in order to insert them into new groups";
1136            else if (!foundField)   error = GBS_global_string("Field not found: '%s'%s", field_name, error ? GBS_global_string(" (Reason: %s)", error) : "");
1137        }
1138
1139        free(doneContents);
1140        free(field_name);
1141    }
1142
1143    GB_end_transaction_show_error(GLOBAL_gb_main, error, aw_message);
1144}
1145
1146static void group_species_by_field_content(AW_window*, AW_window *use_as_main_window, AW_window *window_to_hide) {
1147    group_species(true, use_as_main_window);
1148    window_to_hide->hide();
1149}
1150
1151static AW_window *create_group_species_by_field_window(AW_root *aw_root, AW_window *use_as_main_window) {
1152    AW_window_simple *aws = new AW_window_simple;
1153
1154    aws->init(aw_root, "CREATE_GROUP_USING_FIELD_CONTENT", "Create groups using field");
1155    aws->auto_space(10, 10);
1156
1157    aws->button_length(10);
1158    aws->at_newline();
1159
1160    aws->callback(AW_POPDOWN);
1161    aws->create_button("CLOSE", "CLOSE", "C");
1162
1163    aws->callback(makeHelpCallback("group_by_field.hlp"));
1164    aws->create_button("HELP", "HELP", "H");
1165
1166    aws->at_newline();
1167    aws->label("Use content of field");
1168    create_itemfield_selection_button(aws, FieldSelDef(AWAR_FIELD_CHOSEN, GLOBAL_gb_main, SPECIES_get_selector(), FIELD_FILTER_STRING_READABLE, "group-field"), NULp);
1169
1170    aws->at_newline();
1171    aws->callback(makeWindowCallback(group_species_by_field_content, use_as_main_window, static_cast<AW_window*>(aws)));
1172    aws->create_autosize_button("USE_FIELD", "Group selected species by content", "");
1173
1174    return aws;
1175}
1176
1177void group_species_cb(AW_window *aww, bool use_fields) {
1178    if (!use_fields) {
1179        group_species(false, aww);
1180    }
1181    else {
1182        static AW_window *ask_field_window;
1183
1184        if (!ask_field_window) ask_field_window = create_group_species_by_field_window(ED4_ROOT->aw_root, aww);
1185        ask_field_window->activate();
1186    }
1187}
1188
1189static GB_ERROR ED4_load_new_config(char *name) {
1190    GB_ERROR error;
1191    GBT_config cfg(GLOBAL_gb_main, name, error);
1192    if (cfg.exists()) {
1193        ED4_ROOT->main_manager->clear_whole_background();
1194
1195        max_seq_terminal_length = 0;
1196
1197        ED4_init_notFoundMessage();
1198
1199        if (ED4_ROOT->selected_objects->size() > 0) {
1200            ED4_ROOT->deselect_all();
1201        }
1202
1203        ED4_ROOT->remove_all_callbacks();
1204
1205        ED4_ROOT->scroll_picture.scroll = 0;
1206        ED4_ROOT->scroll_picture.old_x  = 0;
1207        ED4_ROOT->scroll_picture.old_y  = 0;
1208
1209        ED4_ROOT->ref_terminals.clear();
1210
1211        for (ED4_window *window = ED4_ROOT->first_window; window; window=window->next) {
1212            window->cursor.init();
1213            window->aww->set_horizontal_scrollbar_position (0);
1214            window->aww->set_vertical_scrollbar_position (0);
1215        }
1216
1217        ED4_ROOT->scroll_links.link_for_hor_slider = NULp;
1218        ED4_ROOT->scroll_links.link_for_ver_slider = NULp;
1219        ED4_ROOT->middle_area_man                  = NULp;
1220        ED4_ROOT->top_area_man                     = NULp;
1221
1222        delete ED4_ROOT->main_manager;
1223        ED4_ROOT->main_manager = NULp;
1224        delete ED4_ROOT->ecoli_ref;
1225
1226        ED4_ROOT->first_window->reset_all_for_new_config();
1227        ED4_ROOT->create_hierarchy(cfg.get_definition(GBT_config::MIDDLE_AREA), cfg.get_definition(GBT_config::TOP_AREA));
1228    }
1229
1230    return error;
1231}
1232
1233static ED4_EDITMODE ED4_get_edit_mode(AW_root *root) {
1234    if (!root->awar(AWAR_EDIT_MODE)->read_int()) return AD_ALIGN;
1235    return root->awar(AWAR_INSERT_MODE)->read_int() ? AD_INSERT : AD_REPLACE;
1236}
1237
1238void ed4_changesecurity(AW_root *root) {
1239    ED4_EDITMODE  mode      = ED4_get_edit_mode(root);
1240    const char   *awar_name = NULp;
1241
1242    switch (mode) {
1243        case AD_ALIGN:
1244            awar_name = AWAR_EDIT_SECURITY_LEVEL_ALIGN;
1245            break;
1246        default:
1247            awar_name = AWAR_EDIT_SECURITY_LEVEL_CHANGE;
1248    }
1249
1250    ED4_ROOT->aw_root->awar(AWAR_EDIT_SECURITY_LEVEL)->map(awar_name);
1251
1252    long level = ED4_ROOT->aw_root->awar(awar_name)->read_int();
1253    GB_change_my_security(GLOBAL_gb_main, level);
1254}
1255
1256void ed4_change_edit_mode(AW_root *root) {
1257    awar_edit_mode = ED4_get_edit_mode(root);
1258    ed4_changesecurity(root);
1259}
1260
1261ARB_ERROR rebuild_consensus(ED4_base *object) {
1262    if (object->is_consensus_manager()) {
1263        ED4_species_manager *spec_man = object->to_species_manager();
1264        spec_man->do_callbacks();
1265
1266        ED4_base *sequence_data_terminal = spec_man->search_spec_child_rek(LEV_SEQUENCE_STRING);
1267        sequence_data_terminal->request_refresh();
1268    }
1269    return NULp; // needed by route_down_hierarchy
1270}
1271
1272void ED4_new_editor_window(AW_window *aww) {
1273    ED4_LocalWinContext uses(aww);
1274
1275    AW_device  *device;
1276    ED4_window *new_window = NULp;
1277
1278    if (ED4_ROOT->generate_window(&device, &new_window) != ED4_R_BREAK) {
1279        ED4_LocalWinContext now_uses(new_window);
1280
1281        new_window->set_scrolled_rectangle(ED4_ROOT->scroll_links.link_for_hor_slider,
1282                                           ED4_ROOT->scroll_links.link_for_ver_slider,
1283                                           ED4_ROOT->scroll_links.link_for_hor_slider,
1284                                           ED4_ROOT->scroll_links.link_for_ver_slider);
1285
1286        new_window->aww->show();
1287        new_window->update_scrolled_rectangle();
1288    }
1289}
1290
1291
1292
1293static void ED4_start_editor_on_configuration(AW_window *aww) {
1294    aww->hide();
1295
1296    GB_ERROR error;
1297    {
1298        char *name = aww->get_root()->awar(AWAR_EDIT_CONFIGURATION)->read_string();
1299        error      = ED4_load_new_config(name);
1300        free(name);
1301    }
1302
1303    if (error) {
1304        aw_message(error);
1305        aww->show(); // show old window
1306    }
1307}
1308
1309struct cursorpos {
1310    RefPtr<ED4_cursor> cursor;
1311    int screen_rel;
1312    int seq;
1313
1314    cursorpos(ED4_window *win)
1315        : cursor(&win->cursor),
1316          screen_rel(cursor->get_screen_relative_pos()),
1317          seq(cursor->get_sequence_pos())
1318    {}
1319};
1320
1321
1322void ED4_compression_changed_cb(AW_root *awr) {
1323    ED4_remap_mode mode    = (ED4_remap_mode)awr->awar(ED4_AWAR_COMPRESS_SEQUENCE_TYPE)->read_int();
1324    int            percent = awr->awar(ED4_AWAR_COMPRESS_SEQUENCE_PERCENT)->read_int();
1325    GB_transaction ta(GLOBAL_gb_main);
1326
1327    if (ED4_ROOT->root_group_man) {
1328        vector<cursorpos> pos;
1329
1330        for (ED4_window *win = ED4_ROOT->first_window; win; win = win->next) {
1331            pos.push_back(cursorpos(win));
1332        }
1333
1334        ED4_ROOT->root_group_man->remap()->set_mode(mode, percent);
1335        ED4_resize_all_extensions();
1336
1337        for (vector<cursorpos>::iterator i = pos.begin(); i != pos.end(); ++i) {
1338            ED4_cursor  *cursor = i->cursor;
1339            ED4_window  *win    = cursor->window();
1340
1341            win->update_scrolled_rectangle(); // @@@ needed ?
1342
1343            cursor->jump_sequence_pos(i->seq, ED4_JUMP_KEEP_POSITION);
1344            cursor->set_screen_relative_pos(i->screen_rel);
1345        }
1346
1347        ED4_request_full_instant_refresh();
1348    }
1349}
1350
1351void ED4_compression_toggle_changed_cb(AW_root *root, bool hideChanged) {
1352    int gaps = root->awar(ED4_AWAR_COMPRESS_SEQUENCE_GAPS)->read_int();
1353    int hide = root->awar(ED4_AWAR_COMPRESS_SEQUENCE_HIDE)->read_int();
1354
1355    ED4_remap_mode mode = ED4_remap_mode(root->awar(ED4_AWAR_COMPRESS_SEQUENCE_TYPE)->read_int()); // @@@ mode is overwritten below
1356
1357    if (hideChanged) {
1358        // ED4_AWAR_COMPRESS_SEQUENCE_HIDE changed
1359        if (hide!=0 && gaps!=2) {
1360            root->awar(ED4_AWAR_COMPRESS_SEQUENCE_GAPS)->write_int(2);
1361            return;
1362        }
1363    }
1364    else {
1365        // ED4_AWAR_COMPRESS_SEQUENCE_GAPS changed
1366        if (gaps!=2 && hide!=0) {
1367            root->awar(ED4_AWAR_COMPRESS_SEQUENCE_HIDE)->write_int(0);
1368            return;
1369        }
1370    }
1371
1372    mode = ED4_RM_NONE;
1373    switch (gaps) {
1374        case 0: mode = ED4_RM_NONE; break;
1375        case 1: mode = ED4_RM_DYNAMIC_GAPS; break;
1376        case 2: {
1377            switch (hide) {
1378                case 0: mode = ED4_RM_MAX_ALIGN; break;
1379                case 1: mode = ED4_RM_SHOW_ABOVE; break;
1380                default: e4_assert(0); break;
1381            }
1382            break;
1383        }
1384        default: e4_assert(0); break;
1385    }
1386
1387    root->awar(ED4_AWAR_COMPRESS_SEQUENCE_TYPE)->write_int(int(mode));
1388}
1389
1390static AWT_config_mapping_def editor_options_config_mapping[] = {
1391    { ED4_AWAR_COMPRESS_SEQUENCE_GAPS,    "compressgaps" },
1392    { ED4_AWAR_COMPRESS_SEQUENCE_HIDE,    "hidenucs" },
1393    { ED4_AWAR_COMPRESS_SEQUENCE_PERCENT, "hidepercent" },
1394    { AWAR_EDIT_HELIX_SPACING,            "helixspacing" },
1395    { AWAR_EDIT_TERMINAL_SPACING,         "terminalspacing" },
1396    { ED4_AWAR_SCROLL_SPEED_X,            "scrollspeedx" },
1397    { ED4_AWAR_SCROLL_SPEED_Y,            "scrollspeedy" },
1398    { ED4_AWAR_SCROLL_MARGIN,             "scrollmargin" },
1399    { ED4_AWAR_GAP_CHARS,                 "gapchars" },
1400    { ED4_AWAR_DIGITS_AS_REPEAT,          "digitsasrepeat" },
1401    { ED4_AWAR_FAST_CURSOR_JUMP,          "fastcursorjump" },
1402    { ED4_AWAR_ANNOUNCE_CHECKSUM_CHANGES, "announcechecksumchanges" },
1403
1404    { NULp, NULp }
1405};
1406
1407AW_window *ED4_create_editor_options_window(AW_root *root) {
1408    AW_window_simple *aws = new AW_window_simple;
1409
1410    aws->init(root, "EDIT4_PROPS", "EDIT4 Options");
1411    aws->load_xfig("edit4/options.fig");
1412
1413    aws->auto_space(5, 5);
1414
1415    const int SCALEDCOLUMNS = 4;
1416    const int SCALERLEN     = 200;
1417
1418    aws->callback(AW_POPDOWN);
1419    aws->at("close");
1420    aws->create_button("CLOSE", "CLOSE", "C");
1421
1422    aws->callback(makeHelpCallback("e4_options.hlp"));
1423    aws->at("help");
1424    aws->create_button("HELP", "HELP", "H");
1425
1426    //  -----------------------------------
1427    //      Online Sequence Compression
1428
1429    aws->at("gaps");
1430    aws->create_toggle_field(ED4_AWAR_COMPRESS_SEQUENCE_GAPS);
1431    aws->insert_default_toggle("Show all gaps", "A", 0);
1432    aws->insert_toggle("Show some gaps", "S", 1);
1433    aws->insert_toggle("Hide all gaps", "H", 2);
1434    aws->update_toggle_field();
1435
1436    aws->at("hide");
1437    aws->create_toggle_field(ED4_AWAR_COMPRESS_SEQUENCE_HIDE);
1438    aws->insert_default_toggle("Hide no Nucleotides", "0", 0);
1439    aws->insert_toggle("Hide columns with less than...", "1", 1);
1440    aws->update_toggle_field();
1441
1442    aws->at("percent");
1443    aws->create_input_field_with_scaler(ED4_AWAR_COMPRESS_SEQUENCE_PERCENT, SCALEDCOLUMNS, SCALERLEN, AW_SCALER_LINEAR);
1444
1445    //  --------------
1446    //      Layout
1447
1448    aws->at("seq_helix"); aws->create_input_field_with_scaler(AWAR_EDIT_HELIX_SPACING,    SCALEDCOLUMNS, SCALERLEN, AW_SCALER_EXP_CENTER);
1449    aws->at("seq_seq");   aws->create_input_field_with_scaler(AWAR_EDIT_TERMINAL_SPACING, SCALEDCOLUMNS, SCALERLEN, AW_SCALER_EXP_CENTER);
1450
1451    //  --------------------
1452    //      Scroll-Speed
1453
1454    aws->at("scroll_x");
1455    aws->create_input_field(ED4_AWAR_SCROLL_SPEED_X);
1456
1457    aws->at("scroll_y");
1458    aws->create_input_field(ED4_AWAR_SCROLL_SPEED_Y);
1459
1460    aws->at("margin");
1461    aws->create_input_field(ED4_AWAR_SCROLL_MARGIN);
1462
1463    //  ---------------
1464    //      Editing
1465
1466    aws->at("gapchars");
1467    aws->create_input_field(ED4_AWAR_GAP_CHARS);
1468
1469    aws->at("repeat");
1470    aws->create_toggle(ED4_AWAR_DIGITS_AS_REPEAT);
1471
1472    aws->at("fast");
1473    aws->create_toggle(ED4_AWAR_FAST_CURSOR_JUMP);
1474
1475    aws->at("checksum");
1476    aws->create_toggle(ED4_AWAR_ANNOUNCE_CHECKSUM_CHANGES);
1477
1478    aws->at("config");
1479    AWT_insert_config_manager(aws, AW_ROOT_DEFAULT, "options", editor_options_config_mapping);
1480
1481    return aws;
1482}
1483
1484static AWT_config_mapping_def consensus_config_mapping[] = {
1485    { ED4_AWAR_CONSENSUS_COUNTGAPS,   CONSENSUS_CONFIG_COUNTGAPS },
1486    { ED4_AWAR_CONSENSUS_GAPBOUND,    CONSENSUS_CONFIG_GAPBOUND },
1487    { ED4_AWAR_CONSENSUS_GROUP,       CONSENSUS_CONFIG_GROUP },
1488    { ED4_AWAR_CONSENSUS_CONSIDBOUND, CONSENSUS_CONFIG_CONSIDBOUND },
1489    { ED4_AWAR_CONSENSUS_UPPER,       CONSENSUS_CONFIG_UPPER },
1490    { ED4_AWAR_CONSENSUS_LOWER,       CONSENSUS_CONFIG_LOWER },
1491
1492    { NULp, NULp }
1493};
1494
1495AW_window *ED4_create_consensus_definition_window(AW_root *root) {
1496    // keep in sync with ../NTREE/AP_consensus.cxx@AP_create_con_expert_window
1497
1498    static AW_window_simple *aws = NULp;
1499
1500    if (!aws) {
1501        aws = new AW_window_simple;
1502
1503        aws->init(root, "EDIT4_CONSENSUS_DEFm", "EDIT4 Consensus Definition");
1504        aws->load_xfig("edit4/consensus.fig");
1505
1506        aws->auto_space(5, 5);
1507
1508        const int SCALEDCOLUMNS = 3;
1509        const int SCALERSIZE    = 150;
1510
1511        // top part of window:
1512        aws->button_length(9);
1513
1514        aws->callback(AW_POPDOWN);
1515        aws->at("close");
1516        aws->create_button("CLOSE", "CLOSE", "C");
1517
1518        aws->callback(makeHelpCallback("e4_consensus.hlp"));
1519        aws->at("help");
1520        aws->create_button("HELP", "HELP", "H");
1521
1522        // center part of window (same as in NTREE):
1523        aws->at("countgaps");
1524        aws->create_toggle_field(ED4_AWAR_CONSENSUS_COUNTGAPS);
1525        aws->insert_toggle("on", "1", 1);
1526        aws->insert_default_toggle("off", "0", 0);
1527        aws->update_toggle_field();
1528
1529        aws->at("gapbound");
1530        aws->create_input_field_with_scaler(ED4_AWAR_CONSENSUS_GAPBOUND, SCALEDCOLUMNS, SCALERSIZE, AW_SCALER_LINEAR);
1531
1532        aws->at("group");
1533        aws->create_toggle_field(ED4_AWAR_CONSENSUS_GROUP);
1534        aws->insert_toggle("on", "1", 1);
1535        aws->insert_default_toggle("off", "0", 0);
1536        aws->update_toggle_field();
1537
1538        aws->at("considbound");
1539        aws->create_input_field_with_scaler(ED4_AWAR_CONSENSUS_CONSIDBOUND, SCALEDCOLUMNS, SCALERSIZE, AW_SCALER_LINEAR);
1540
1541        aws->at("showgroups");
1542        aws->callback(AWT_create_IUPAC_info_window);
1543        aws->create_autosize_button("SHOW_IUPAC", "Show IUPAC groups", "S");
1544
1545        aws->at("upper");
1546        aws->create_input_field_with_scaler(ED4_AWAR_CONSENSUS_UPPER, SCALEDCOLUMNS, SCALERSIZE, AW_SCALER_LINEAR);
1547
1548        aws->at("lower");
1549        aws->create_input_field_with_scaler(ED4_AWAR_CONSENSUS_LOWER, SCALEDCOLUMNS, SCALERSIZE, AW_SCALER_LINEAR);
1550
1551        // bottom part of window:
1552        aws->at("show");
1553        aws->label("Display consensus?");
1554        aws->create_toggle(ED4_AWAR_CONSENSUS_SHOW);
1555
1556        aws->at("config");
1557        AWT_insert_config_manager(aws, AW_ROOT_DEFAULT, CONSENSUS_CONFIG_ID, consensus_config_mapping);
1558    }
1559
1560    return aws;
1561}
1562
1563static void consensus_upper_lower_changed_cb(AW_root *awr, bool upper_changed) {
1564    AW_awar *awar_lower = awr->awar(ED4_AWAR_CONSENSUS_LOWER);
1565    AW_awar *awar_upper = awr->awar(ED4_AWAR_CONSENSUS_UPPER);
1566
1567    int lower = awar_lower->read_int();
1568    int upper = awar_upper->read_int();
1569
1570    if (upper<lower) {
1571        if (upper_changed) awar_lower->write_int(upper);
1572        else               awar_upper->write_int(lower);
1573    }
1574    ED4_consensus_definition_changed(awr);
1575}
1576
1577void ED4_create_consensus_awars(AW_root *aw_root) {
1578    GB_transaction ta(GLOBAL_gb_main);
1579
1580    aw_root->awar_int(ED4_AWAR_CONSENSUS_COUNTGAPS,   1) ->add_callback(ED4_consensus_definition_changed);
1581    aw_root->awar_int(ED4_AWAR_CONSENSUS_GROUP,       1) ->add_callback(ED4_consensus_definition_changed);
1582    aw_root->awar_int(ED4_AWAR_CONSENSUS_GAPBOUND,    60)->set_minmax(0, 100)->add_callback(ED4_consensus_definition_changed);
1583    aw_root->awar_int(ED4_AWAR_CONSENSUS_CONSIDBOUND, 30)->set_minmax(0, 100)->add_callback(ED4_consensus_definition_changed);
1584    aw_root->awar_int(ED4_AWAR_CONSENSUS_UPPER,       95)->set_minmax(0, 100)->add_callback(makeRootCallback(consensus_upper_lower_changed_cb, true));
1585    aw_root->awar_int(ED4_AWAR_CONSENSUS_LOWER,       70)->set_minmax(0, 100)->add_callback(makeRootCallback(consensus_upper_lower_changed_cb, false));
1586
1587    AW_awar *cons_show = aw_root->awar_int(ED4_AWAR_CONSENSUS_SHOW, 1);
1588
1589    cons_show->write_int(1);
1590    cons_show->add_callback(ED4_consensus_display_changed);
1591}
1592
1593void ED4_reloadConfiguration(AW_window *aww) {
1594    ED4_start_editor_on_configuration(aww);
1595}
1596
1597AW_window *ED4_create_loadConfiguration_window(AW_root *awr) {
1598    static AW_window_simple *aws = NULp;
1599    if (!aws) {
1600        aws = new AW_window_simple;
1601        aws->init(awr, "LOAD_CONFIGURATION", "Load existing configuration");
1602        aws->load_xfig("edit4/load_config.fig");
1603
1604        aws->at("close");
1605        aws->callback(AW_POPDOWN);
1606        aws->create_button("CLOSE", "CLOSE", "C");
1607
1608        aws->at("help");
1609        aws->callback(makeHelpCallback("species_configs_saveload.hlp"));
1610        aws->create_button("HELP", "HELP");
1611
1612        aws->at("confs");
1613        awt_create_CONFIG_selection_list(GLOBAL_gb_main, aws, AWAR_EDIT_CONFIGURATION, false);
1614
1615        aws->at("go");
1616        aws->callback(ED4_start_editor_on_configuration);
1617        aws->create_button("LOAD", "LOAD");
1618
1619        aws->window_fit();
1620    }
1621    return aws;
1622}
1623
1624void ED4_saveConfiguration(AW_window *aww, bool hide_aww) {
1625    if (hide_aww) aww->hide();
1626
1627    char *name = aww->get_root()->awar(AWAR_EDIT_CONFIGURATION)->read_string();
1628    ED4_ROOT->database->save_current_config(name);
1629    free(name);
1630}
1631
1632AW_window *ED4_create_saveConfigurationAs_window(AW_root *awr) {
1633    static AW_window_simple *aws = NULp;
1634    if (!aws) {
1635        aws = new AW_window_simple;
1636        aws->init(awr, "SAVE_CONFIGURATION", "Save current configuration as");
1637        aws->load_xfig("edit4/save_config.fig");
1638
1639        aws->at("close");
1640        aws->callback(AW_POPDOWN);
1641        aws->create_button("CLOSE", "CLOSE");
1642
1643        aws->at("help");
1644        aws->callback(makeHelpCallback("species_configs_saveload.hlp"));
1645        aws->create_button("HELP", "HELP");
1646
1647        aws->at("save");
1648        aws->create_input_field(AWAR_EDIT_CONFIGURATION);
1649
1650        aws->at("confs");
1651        awt_create_CONFIG_selection_list(GLOBAL_gb_main, aws, AWAR_EDIT_CONFIGURATION, false);
1652
1653        aws->at("go");
1654        aws->callback(makeWindowCallback(ED4_saveConfiguration, true));
1655        aws->create_button("SAVE", "SAVE");
1656    }
1657    return aws;
1658}
1659
1660static char *filter_loadable_SAIs(GBDATA *gb_sai) {
1661    GBDATA *gb_ali = GB_search(gb_sai, ED4_ROOT->alignment_name, GB_FIND);
1662    if (gb_ali) {
1663        GBDATA *gb_data = GB_search(gb_ali, "data", GB_FIND);
1664        if (gb_data) {
1665            const char *sai_name = GBT_get_name(gb_sai);
1666            if (!ED4_find_SAI_name_terminal(sai_name)) { // if not loaded yet
1667                return ARB_strdup(sai_name);
1668            }
1669        }
1670    }
1671    return NULp;
1672}
1673
1674AW_window *ED4_create_loadSAI_window(AW_root *awr) {
1675    static AW_window_simple *aws = NULp;
1676    if (!aws) {
1677        aws = new AW_window_simple;
1678        aws->init(awr, "LOAD_SAI", "Load additional SAI");
1679        aws->load_xfig("edit4/load_sai.fig");
1680
1681        aws->at("close");
1682        aws->callback(AW_POPDOWN);
1683        aws->create_button("CLOSE", "CLOSE");
1684
1685        aws->at("help");
1686        aws->callback(makeHelpCallback("e4_get_species.hlp"));
1687        aws->create_button("HELP", "HELP");
1688
1689        aws->at("sai");
1690        awt_create_SAI_selection_list(GLOBAL_gb_main, aws, AWAR_SAI_NAME, false, makeSaiSelectionlistFilterCallback(filter_loadable_SAIs));
1691        ED4_ROOT->loadable_SAIs = LSAI_UPTODATE;
1692
1693        aws->at("go");
1694        aws->callback(ED4_get_and_jump_to_selected_SAI);
1695        aws->create_button("LOAD", "LOAD");
1696    }
1697    return aws;
1698}
1699
1700static GB_ERROR createDataFromConsensus(GBDATA *gb_species, ED4_group_manager *group_man) {
1701    GB_ERROR error = NULp;
1702
1703    int   len;
1704    char *consensus = group_man->build_consensus_string(&len);
1705
1706    char *equal_to = ED4_ROOT->aw_root->awar(ED4_AWAR_CREATE_FROM_CONS_REPL_EQUAL)->read_string();
1707    char *point_to = ED4_ROOT->aw_root->awar(ED4_AWAR_CREATE_FROM_CONS_REPL_POINT)->read_string();
1708    int   allUpper = ED4_ROOT->aw_root->awar(ED4_AWAR_CREATE_FROM_CONS_ALL_UPPER)->read_int();
1709
1710    for (int p=0; p<len; p++) {
1711        switch (consensus[p]) {
1712            case '=': consensus[p] = equal_to[0]; break;
1713            case '.': consensus[p] = point_to[0]; break;
1714            default: {
1715                if (allUpper) {
1716                    consensus[p] = toupper(consensus[p]);
1717                }
1718                break;
1719            }
1720        }
1721    }
1722
1723    if (ED4_ROOT->aw_root->awar(ED4_AWAR_CREATE_FROM_CONS_CREATE_POINTS)) { // points at start & end of sequence?
1724        for (int p=0; p<len; p++) {
1725            if (!ED4_is_gap_character(consensus[p])) break;
1726            consensus[p] = '.';
1727        }
1728        for (int p=len-1; p>=0; p--) {
1729            if (!ED4_is_gap_character(consensus[p])) break;
1730            consensus[p] = '.';
1731        }
1732    }
1733
1734    GB_CSTR ali = GBT_get_default_alignment(GLOBAL_gb_main);
1735    GBDATA *gb_ali = GB_search(gb_species, ali, GB_DB);
1736    if (gb_ali) {
1737        GBDATA *gb_data = GB_search(gb_ali, "data", GB_STRING);
1738        error = GB_write_pntr(gb_data, consensus, len+1, len);
1739    }
1740    else {
1741        error = GB_export_errorf("Can't find alignment '%s'", ali);
1742    }
1743    free(consensus);
1744    return error;
1745}
1746
1747// --------------------------------------------------------------------------------
1748
1749struct SpeciesMergeList {
1750    GBDATA *species;
1751    char   *species_name;
1752
1753    SpeciesMergeList *next;
1754};
1755
1756static ARB_ERROR add_species_to_merge_list(ED4_base *base, SpeciesMergeList **smlp, GBDATA *gb_species_data) {
1757    GB_ERROR error = NULp;
1758
1759    if (base->is_species_name_terminal()) {
1760        ED4_species_name_terminal *name_term = base->to_species_name_terminal();
1761
1762        if (!name_term->inside_consensus_manager()) {
1763            char   *species_name    = name_term->resolve_pointer_to_string_copy();
1764            GBDATA *gb_species      = GBT_find_species_rel_species_data(gb_species_data, species_name);
1765
1766            if (gb_species) {
1767                SpeciesMergeList *sml = new SpeciesMergeList;
1768
1769                sml->species      = gb_species;
1770                sml->species_name = ARB_strdup(species_name);
1771                sml->next         = *smlp;
1772                *smlp             = sml;
1773            }
1774            else {
1775                error = GB_append_exportedError(GBS_global_string("can't find species '%s'", species_name));
1776            }
1777
1778            free(species_name);
1779        }
1780    }
1781    return error;
1782}
1783static int SpeciesMergeListLength(SpeciesMergeList *sml) {
1784    int length = 0;
1785
1786    while (sml) {
1787        length++;
1788        sml = sml->next;
1789    }
1790
1791    return length;
1792}
1793static void freeSpeciesMergeList(SpeciesMergeList *sml) {
1794    while (sml) {
1795        free(sml->species_name);
1796        freeset(sml, sml->next);
1797    }
1798}
1799
1800// --------------------------------------------------------------------------------
1801
1802inline bool nameIsUnique(const char *short_name, GBDATA *gb_species_data) {
1803    return !GBT_find_species_rel_species_data(gb_species_data, short_name);
1804}
1805
1806
1807static void create_new_species(AW_window *, SpeciesCreationMode creation_mode) {
1808    char      *new_species_full_name = ED4_ROOT->aw_root->awar(ED4_AWAR_SPECIES_TO_CREATE)->read_string(); // this contains the full_name now!
1809    ARB_ERROR  error                 = NULp;
1810
1811    e4_assert(creation_mode>=0 && creation_mode<=2);
1812
1813    if (!new_species_full_name || new_species_full_name[0]==0) {
1814        error = "Please enter a full_name for the new species";
1815    }
1816    else {
1817        ED4_MostRecentWinContext context;
1818
1819        error = GB_begin_transaction(GLOBAL_gb_main);
1820
1821        GBDATA *gb_species_data  = GBT_get_species_data(GLOBAL_gb_main);
1822        char   *new_species_name = NULp;
1823        char   *acc              = NULp;
1824        char   *addid            = NULp;
1825
1826        enum e_dataSource { MERGE_FIELDS, COPY_FIELDS } dataSource = (enum e_dataSource)ED4_ROOT->aw_root ->awar(ED4_AWAR_CREATE_FROM_CONS_DATA_SOURCE)->read_int();
1827        enum { NOWHERE, ON_SPECIES, ON_CONSENSUS } where_we_are = NOWHERE;
1828        ED4_terminal *cursor_terminal = NULp;
1829
1830        if (!error) {
1831            if (creation_mode==CREATE_FROM_CONSENSUS || creation_mode==COPY_SPECIES) {
1832                ED4_cursor *cursor = &current_cursor();
1833
1834                if (cursor->owner_of_cursor) {
1835                    cursor_terminal = cursor->owner_of_cursor;
1836                    where_we_are    = cursor_terminal->is_consensus_terminal() ? ON_CONSENSUS : ON_SPECIES;
1837                }
1838            }
1839
1840            if (creation_mode==COPY_SPECIES || (creation_mode==CREATE_FROM_CONSENSUS && dataSource==COPY_FIELDS)) {
1841                if (where_we_are==ON_SPECIES) {
1842                    ED4_species_name_terminal *spec_name   = cursor_terminal->to_sequence_terminal()->corresponding_species_name_terminal();
1843                    const char                *source_name = spec_name->resolve_pointer_to_char_pntr();
1844                    GBDATA                    *gb_source   = GBT_find_species_rel_species_data(gb_species_data, source_name);
1845
1846                    if (!gb_source) error = GBS_global_string("No such species: '%s'", source_name);
1847                    else {
1848                        GBDATA *gb_acc  = GB_search(gb_source, "acc", GB_FIND);
1849                        if (gb_acc) acc = GB_read_string(gb_acc); // if has accession
1850
1851                        const char *add_field = AW_get_nameserver_addid(GLOBAL_gb_main);
1852                        GBDATA     *gb_addid  = add_field[0] ? GB_search(gb_source, add_field, GB_FIND) : NULp;
1853                        if (gb_addid) addid   = GB_read_as_string(gb_addid);
1854                    }
1855                }
1856                else {
1857                    error = "Please place cursor on a species";
1858                }
1859            }
1860        }
1861
1862        if (!error) {
1863            UniqueNameDetector *existingNames = NULp;
1864
1865            if (creation_mode==0) {
1866                error = "It's no good idea to create the short-name for a new species using the nameserver! (has no acc yet)";
1867            }
1868            else {
1869                error = AWTC_generate_one_name(GLOBAL_gb_main, new_species_full_name, acc, addid, new_species_name);
1870                if (!error) {   // name was created
1871                    if (!nameIsUnique(new_species_name, gb_species_data)) {
1872                        if (!existingNames) existingNames = new UniqueNameDetector(gb_species_data);
1873                        freeset(new_species_name, AWTC_makeUniqueShortName(new_species_name, *existingNames));
1874                        if (!new_species_name) error = GB_await_error();
1875                    }
1876                }
1877            }
1878
1879            if (error) {        // try to make a random name
1880                const char *msg = GBS_global_string("%s\nGenerating a random name instead.", error.deliver());
1881                aw_message(msg);
1882                error           = NULp;
1883
1884                if (!existingNames) existingNames = new UniqueNameDetector(gb_species_data);
1885                new_species_name = AWTC_generate_random_name(*existingNames);
1886
1887                if (!new_species_name) {
1888                    error = GBS_global_string("Failed to create a new name for '%s'", new_species_full_name);
1889                }
1890            }
1891
1892            if (existingNames) delete existingNames;
1893        }
1894
1895        if (!error) {
1896            if (!error) {
1897                if (creation_mode==CREATE_NEW_SPECIES) {
1898                    GBDATA *gb_created_species = GBT_find_or_create_species(GLOBAL_gb_main, new_species_name);
1899                    if (!gb_created_species) {
1900                        error = GBS_global_string("Failed to create new species '%s'", new_species_name);
1901                    }
1902                    else {
1903                        GB_CSTR  ali    = GBT_get_default_alignment(GLOBAL_gb_main);
1904                        GBDATA  *gb_ali = GB_search(gb_created_species, ali, GB_DB);
1905
1906                        if (gb_ali) error = GBT_write_string(gb_ali, "data", ".......");
1907                        else error        = GBS_global_string("Can't create alignment '%s' (Reason: %s)", ali, GB_await_error());
1908                    }
1909                    if (!error) error = GBT_write_string(gb_created_species, "full_name", new_species_full_name);
1910                }
1911                else if (creation_mode==CREATE_FROM_CONSENSUS && dataSource==MERGE_FIELDS) {
1912                    // create from consensus (merge fields from all species in container)
1913                    if (where_we_are==NOWHERE) {
1914                        error = "Please place cursor on any sequence/consensus of group";
1915                    }
1916                    else {
1917                        ED4_group_manager *group_man = cursor_terminal->get_parent(LEV_GROUP)->to_group_manager();
1918                        SpeciesMergeList  *sml       = NULp; // list of species in group
1919
1920                        error = group_man->route_down_hierarchy(makeED4_route_cb(add_species_to_merge_list, &sml, gb_species_data));
1921                        if (!error && !sml) {
1922                            error = "Please choose a none empty group!";
1923                        }
1924
1925                        GBDATA *gb_new_species = NULp;
1926                        if (!error) {
1927                            GBDATA *gb_source = sml->species;
1928                            gb_new_species = GB_create_container(gb_species_data, "species");
1929                            error = GB_copy(gb_new_species, gb_source); // copy first found species to create a new species
1930                        }
1931                        if (!error) error = GBT_write_string(gb_new_species, "name", new_species_name); // insert new 'name'
1932                        if (!error) error = GBT_write_string(gb_new_species, "full_name", new_species_full_name); // insert new 'full_name'
1933                        if (!error) error = createDataFromConsensus(gb_new_species, group_man); // insert consensus as 'data'
1934
1935                        if (!error) {
1936                            char             *doneFields = ARB_strdup(";name;full_name;"); // all fields which are already merged
1937                            int               doneLen    = strlen(doneFields);
1938                            SpeciesMergeList *sl         = sml;
1939                            int               sl_length  = SpeciesMergeListLength(sml);
1940                            int              *fieldStat  = new int[sl_length];         // 0 = not used yet ; -1 = don't has field ; 1..n = field content, same number means same content
1941
1942                            arb_progress progress("Merging fields", sl_length);
1943
1944                            while (sl && !error) { // with all species do..
1945                                char *newFields = GB_get_subfields(sl->species);
1946                                char *fieldStart = newFields; // points to ; before next field
1947
1948                                while (fieldStart[1] && !error) { // with all subfields of the species do..
1949                                    char *fieldEnd = strchr(fieldStart+1, ';');
1950
1951                                    e4_assert(fieldEnd);
1952                                    char behind = fieldEnd[1];
1953                                    fieldEnd[1] = 0;
1954
1955                                    if (!strstr(doneFields, fieldStart)) { // field is not merged yet
1956                                        char *fieldName = fieldStart+1;
1957                                        int fieldLen = int(fieldEnd-fieldName);
1958
1959                                        e4_assert(fieldEnd[0]==';');
1960                                        fieldEnd[0] = 0;
1961
1962                                        GBDATA *gb_field = GB_search(sl->species, fieldName, GB_FIND);
1963                                        e4_assert(gb_field); // field has to exist, cause it was found before
1964
1965                                        GB_TYPES type = GB_read_type(gb_field);
1966                                        if (type==GB_STRING) { // we only merge string fields
1967                                            int i;
1968                                            int doneSpecies = 0;
1969                                            int nextStat = 1;
1970
1971                                            for (i=0; i<sl_length; i++) { // clear field status
1972                                                fieldStat[i] = 0;
1973                                            }
1974
1975                                            while (doneSpecies<sl_length) { // since all species in list were handled
1976                                                SpeciesMergeList *sl2 = sml;
1977                                                i = 0;
1978
1979                                                while (sl2) {
1980                                                    if (fieldStat[i]==0) {
1981                                                        gb_field = GB_search(sl2->species, fieldName, GB_FIND);
1982                                                        if (gb_field) {
1983                                                            char *content = GB_read_as_string(gb_field);
1984                                                            SpeciesMergeList *sl3 = sl2->next;
1985
1986                                                            fieldStat[i] = nextStat;
1987                                                            doneSpecies++;
1988                                                            int j = i+1;
1989                                                            while (sl3) {
1990                                                                if (fieldStat[j]==0) {
1991                                                                    gb_field = GB_search(sl3->species, fieldName, GB_FIND);
1992                                                                    if (gb_field) {
1993                                                                        char *content2 = GB_read_as_string(gb_field);
1994
1995                                                                        if (strcmp(content, content2)==0) { // if contents are the same, they get the same status
1996                                                                            fieldStat[j] = nextStat;
1997                                                                            doneSpecies++;
1998                                                                        }
1999                                                                        free(content2);
2000                                                                    }
2001                                                                    else {
2002                                                                        fieldStat[j] = -1;
2003                                                                        doneSpecies++;
2004                                                                    }
2005                                                                }
2006                                                                sl3 = sl3->next;
2007                                                                j++;
2008                                                            }
2009
2010                                                            free(content);
2011                                                            nextStat++;
2012                                                        }
2013                                                        else {
2014                                                            fieldStat[i] = -1; // field does not exist here
2015                                                            doneSpecies++;
2016                                                        }
2017                                                    }
2018                                                    sl2 = sl2->next;
2019                                                    i++;
2020                                                }
2021                                            }
2022
2023                                            e4_assert(nextStat!=1); // this would mean that none of the species contained the field
2024
2025                                            {
2026                                                char *new_content     = NULp;
2027                                                int   new_content_len = 0;
2028
2029                                                if (nextStat==2) { // all species contain same field content or do not have the field
2030                                                    SpeciesMergeList *sl2 = sml;
2031
2032                                                    while (sl2) {
2033                                                        gb_field = GB_search(sl2->species, fieldName, GB_FIND);
2034                                                        if (gb_field) {
2035                                                            new_content = GB_read_as_string(gb_field);
2036                                                            new_content_len = strlen(new_content); // @@@ new_content_len never used
2037                                                            break;
2038                                                        }
2039                                                        sl2 = sl2->next;
2040                                                    }
2041                                                }
2042                                                else { // different field contents
2043                                                    int currStat;
2044                                                    for (currStat=1; currStat<nextStat; currStat++) {
2045                                                        int names_len = 1; // open bracket
2046                                                        SpeciesMergeList *sl2 = sml;
2047                                                        i = 0;
2048                                                        char *content = NULp;
2049
2050                                                        while (sl2) {
2051                                                            if (fieldStat[i]==currStat) {
2052                                                                names_len += strlen(sl2->species_name)+1;
2053                                                                if (!content) {
2054                                                                    gb_field = GB_search(sl2->species, fieldName, GB_FIND);
2055                                                                    e4_assert(gb_field);
2056                                                                    content = GB_read_as_string(gb_field);
2057                                                                }
2058                                                            }
2059                                                            sl2 = sl2->next;
2060                                                            i++;
2061                                                        }
2062
2063                                                        e4_assert(content);
2064                                                        int add_len = names_len+1+strlen(content);
2065                                                        char *whole = ARB_alloc<char>(new_content_len+1+add_len+1);
2066                                                        e4_assert(whole);
2067                                                        char *add = new_content ? whole+sprintf(whole, "%s ", new_content) : whole;
2068                                                        sl2 = sml;
2069                                                        i = 0;
2070                                                        int first = 1;
2071                                                        while (sl2) {
2072                                                            if (fieldStat[i]==currStat) {
2073                                                                add += sprintf(add, "%c%s", first ? '{' : ';', sl2->species_name);
2074                                                                first = 0;
2075                                                            }
2076                                                            sl2 = sl2->next;
2077                                                            i++;
2078                                                        }
2079                                                        add += sprintf(add, "} %s", content);
2080
2081                                                        free(content);
2082
2083                                                        freeset(new_content, whole);
2084                                                        new_content_len = strlen(new_content);
2085                                                    }
2086                                                }
2087
2088                                                if (new_content) {
2089                                                    error = GBT_write_string(gb_new_species, fieldName, new_content);
2090                                                    free(new_content);
2091                                                }
2092                                            }
2093                                        }
2094
2095                                        // mark field as done:
2096                                        char *new_doneFields = ARB_alloc<char>(doneLen+fieldLen+1+1);
2097                                        sprintf(new_doneFields, "%s%s;", doneFields, fieldName);
2098                                        doneLen += fieldLen+1;
2099                                        freeset(doneFields, new_doneFields);
2100
2101                                        fieldEnd[0] = ';';
2102                                    }
2103
2104                                    fieldEnd[1] = behind;
2105                                    fieldStart = fieldEnd;
2106                                }
2107                                free(newFields);
2108                                sl = sl->next;
2109                                progress.inc_and_check_user_abort(error);
2110                            }
2111                            free(doneFields);
2112                            delete [] fieldStat;
2113                        }
2114                        freeSpeciesMergeList(sml); sml = NULp;
2115                    }
2116                }
2117                else { // copy species or create from consensus (copy fields from one species)
2118                    e4_assert(where_we_are==ON_SPECIES);
2119
2120                    ED4_species_name_terminal *spec_name   = cursor_terminal->to_sequence_terminal()->corresponding_species_name_terminal();
2121                    const char                *source_name = spec_name->resolve_pointer_to_char_pntr();
2122                    GBDATA                    *gb_source   = GBT_find_species_rel_species_data(gb_species_data, source_name);
2123
2124                    if (gb_source) {
2125                        GBDATA *gb_new_species = GB_create_container(gb_species_data, "species");
2126                        error                  = GB_copy(gb_new_species, gb_source);
2127                        if (!error) error      = GBT_write_string(gb_new_species, "name", new_species_name);
2128                        if (!error) error      = GBT_write_string(gb_new_species, "full_name", new_species_full_name); // insert new 'full_name'
2129                        if (!error && creation_mode==CREATE_FROM_CONSENSUS) {
2130                            ED4_group_manager *group_man = cursor_terminal->get_parent(LEV_GROUP)->to_group_manager();
2131                            error = createDataFromConsensus(gb_new_species, group_man);
2132                        }
2133                    }
2134                    else {
2135                        error = GBS_global_string("Can't find species '%s'", source_name);
2136                    }
2137                }
2138            }
2139
2140            error = GB_end_transaction(GLOBAL_gb_main, error);
2141            if (!error) ED4_get_and_jump_to_species(new_species_name);
2142        }
2143        else {
2144            GB_abort_transaction(GLOBAL_gb_main);
2145        }
2146
2147        free(addid);
2148        free(acc);
2149        free(new_species_name);
2150    }
2151
2152    aw_message_if(error);
2153    free(new_species_full_name);
2154}
2155
2156AW_window *ED4_create_new_seq_window(AW_root *root, SpeciesCreationMode creation_mode) {
2157    e4_assert(valid(creation_mode));
2158
2159    AW_window_simple *aws = new AW_window_simple;
2160    switch (creation_mode) {
2161        case CREATE_NEW_SPECIES:    aws->init(root, "create_species",                "Create species");                break;
2162        case CREATE_FROM_CONSENSUS: aws->init(root, "create_species_from_consensus", "Create species from consensus"); break;
2163        case COPY_SPECIES:          aws->init(root, "copy_species",                  "Copy current species");          break;
2164    }
2165   
2166    if (creation_mode==CREATE_FROM_CONSENSUS) {
2167        aws->load_xfig("edit4/create_seq_fc.fig");
2168    }
2169    else {
2170        aws->load_xfig("edit4/create_seq.fig");
2171    }
2172
2173    aws->callback(AW_POPDOWN);
2174    aws->at("close");
2175    aws->create_button("CLOSE", "CLOSE", "C");
2176
2177    aws->at("label");
2178    aws->create_autosize_button(NULp, "Please enter the FULL_NAME\nof the new species");
2179
2180    aws->at("input");
2181    aws->create_input_field(ED4_AWAR_SPECIES_TO_CREATE, 30);
2182
2183    aws->at("ok");
2184    aws->callback(makeWindowCallback(create_new_species, creation_mode));
2185    aws->create_button("GO", "GO", "g");
2186
2187    if (creation_mode==CREATE_FROM_CONSENSUS) {
2188        aws->at("replace_equal");
2189        aws->label("Replace '=' by ");
2190        aws->create_input_field(ED4_AWAR_CREATE_FROM_CONS_REPL_EQUAL, 1);
2191
2192        aws->at("replace_point");
2193        aws->label("Replace '.' by ");
2194        aws->create_input_field(ED4_AWAR_CREATE_FROM_CONS_REPL_POINT, 1);
2195
2196        aws->at("replace_start_end");
2197        aws->label("Create ... at ends of sequence?");
2198        aws->create_toggle(ED4_AWAR_CREATE_FROM_CONS_CREATE_POINTS);
2199
2200        aws->at("upper");
2201        aws->label("Convert all chars to upper?");
2202        aws->create_toggle(ED4_AWAR_CREATE_FROM_CONS_ALL_UPPER);
2203
2204        aws->at("data");
2205        aws->label("Other fields");
2206        aws->create_option_menu(ED4_AWAR_CREATE_FROM_CONS_DATA_SOURCE, true);
2207        aws->insert_default_option("Merge from all in group", "", 0);
2208        aws->insert_option("Copy from current species", "", 1);
2209        aws->update_option_menu();
2210    }
2211
2212    return aws;
2213}
2214
Note: See TracBrowser for help on using the repository browser.