source: branches/species/EDIT4/ED4_no_class.cxx

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