source: tags/old_import_filter/EDIT4/ED4_cursor.cxx

Last change on this file was 9539, checked in by westram, 11 years ago
  • remove local versions of min, max and abs
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 60.4 KB
Line 
1#include <cctype>
2#include <climits>
3
4#include <arbdbt.h>
5#include <aw_awars.hxx>
6#include <AW_helix.hxx>
7#include <BI_basepos.hxx>
8#include <aw_msg.hxx>
9#include <arb_progress.h>
10#include <aw_root.hxx>
11
12#include <ed4_extern.hxx>
13
14#include "ed4_class.hxx"
15#include "ed4_edit_string.hxx"
16#include "ed4_tools.hxx"
17#include "ed4_awars.hxx"
18#include "ed4_ProteinViewer.hxx"
19
20#include <arb_strbuf.h>
21#include <arb_defs.h>
22
23/* --------------------------------------------------------------------------------
24   CursorShape
25   -------------------------------------------------------------------------------- */
26
27#define MAXLINES  30
28#define MAXPOINTS (2*MAXLINES)
29
30class ED4_CursorShape {
31    int points;
32    int xpos[MAXPOINTS];
33    int ypos[MAXPOINTS];
34
35    int lines;
36    int start[MAXLINES];
37    int end[MAXLINES];
38
39    int char_width;
40
41    bool reverse;
42
43    int point(int x, int y) {
44        e4_assert(points<MAXPOINTS);
45        if (reverse) {
46            xpos[points] = char_width-x;
47        }
48        else {
49            xpos[points] = x;
50        }
51        ypos[points] = y;
52        return points++;
53    }
54    int line(int p1, int p2) {
55        e4_assert(p1>=0 && p1<points);
56        e4_assert(p2>=0 && p2<points);
57
58        e4_assert(lines<MAXLINES);
59        start[lines] = p1;
60        end[lines] = p2;
61
62        return lines++;
63    }
64    int horizontal_line(int x, int y, int half_width) {
65        return line(point(x-half_width, y), point(x+half_width, y));
66    }
67
68public:
69
70    ED4_CursorShape(ED4_CursorType type, int term_height, int character_width, bool reverse_cursor);
71    ~ED4_CursorShape() {}
72
73    void draw(AW_device *device, int x, int y) const {
74        e4_assert(lines);
75        for (int l=0; l<lines; l++) {
76            int p1 = start[l];
77            int p2 = end[l];
78            device->line(ED4_G_CURSOR, xpos[p1]+x, ypos[p1]+y, xpos[p2]+x, ypos[p2]+y, AW_SCREEN);
79        }
80        device->flush();
81    }
82
83    void get_bounding_box(int x, int y, int &xmin, int &ymin, int &xmax, int &ymax) const {
84        e4_assert(points);
85        xmin = ymin = INT_MAX;
86        xmax = ymax = INT_MIN;
87        for (int p=0; p<points; p++) {
88            if (xpos[p]<xmin)      { xmin = xpos[p]; }
89            else if (xpos[p]>xmax) { xmax = xpos[p]; }
90            if (ypos[p]<ymin)      { ymin = ypos[p]; }
91            else if (ypos[p]>ymax) { ymax = ypos[p]; }
92        }
93
94        xmin += x; xmax += x;
95        ymin += y; ymax += y;
96    }
97
98
99};
100
101ED4_CursorShape::ED4_CursorShape(ED4_CursorType typ, /* int x, int y, */ int term_height, int character_width, bool reverse_cursor)
102{
103    lines      = 0;
104    points     = 0;
105    reverse    = reverse_cursor;
106    char_width = character_width;
107
108    int x = 0;
109    int y = 0;
110
111    switch (typ) {
112#define UPPER_OFFSET    (-1)
113#define LOWER_OFFSET    (term_height-1)
114#define CWIDTH          (character_width)
115
116        case ED4_RIGHT_ORIENTED_CURSOR_THIN:
117        case ED4_RIGHT_ORIENTED_CURSOR: {
118            // --CWIDTH--
119            //
120            // 0--------1               UPPER_OFFSET (from top)
121            // |3-------4
122            // ||
123            // ||
124            // ||
125            // ||
126            // ||
127            // ||
128            // 25                       LOWER_OFFSET (from top)
129            //
130            //
131            //  x
132
133            if (typ == ED4_RIGHT_ORIENTED_CURSOR) { // non-thin version
134                int p0 = point(x-1, y+UPPER_OFFSET);
135
136                line(p0, point(x+CWIDTH-1, y+UPPER_OFFSET)); // 0->1
137                line(p0, point(x-1, y+LOWER_OFFSET)); // 0->2
138            }
139
140            int p3 = point(x, y+UPPER_OFFSET+1);
141
142            line(p3, point(x+CWIDTH-1, y+UPPER_OFFSET+1)); // 3->4
143            line(p3, point(x, y+LOWER_OFFSET)); // 3->5
144
145            break;
146
147#undef UPPER_OFFSET
148#undef LOWER_OFFSET
149#undef CWIDTH
150
151        }
152        case ED4_TRADITIONAL_CURSOR_CONNECTED:
153        case ED4_TRADITIONAL_CURSOR_BOTTOM:
154        case ED4_TRADITIONAL_CURSOR: {
155            int small_version = (term_height <= 10) ? 1 : 0;
156
157
158#define UPPER_OFFSET    0
159#define LOWER_OFFSET    (term_height-1)
160#define CWIDTH          3
161
162            //  -----2*CWIDTH----
163            //
164            //  0---------------1       UPPER_OFFSET (from top)
165            //     4---------5
166            //        8---9
167            //         C/C
168            //
169            //         D/D
170            //        A---B
171            //     6---------7
172            //  2---------------3       LOWER_OFFSET (from top)
173
174            bool draw_upper = typ != ED4_TRADITIONAL_CURSOR_BOTTOM;
175
176            if (draw_upper) horizontal_line(x, y+UPPER_OFFSET, CWIDTH-small_version); // 0/1
177            horizontal_line(x, y+LOWER_OFFSET, CWIDTH-small_version); // 2/3
178
179            if (draw_upper) horizontal_line(x, y+UPPER_OFFSET+1, (CWIDTH*2)/3-small_version); // 4/5
180            horizontal_line(x, y+LOWER_OFFSET-1, (CWIDTH*2)/3-small_version); // 6/7
181
182            if (!small_version) {
183                if (draw_upper) horizontal_line(x, y+UPPER_OFFSET+2, CWIDTH/3); // 8/9
184                horizontal_line(x, y+LOWER_OFFSET-2, CWIDTH/3); // A/B
185            }
186
187            int pu = point(x, y+UPPER_OFFSET+3-small_version);
188            int pl = point(x, y+LOWER_OFFSET-3+small_version);
189
190            if (draw_upper) line(pu, pu);   // C/C
191            line(pl, pl);   // D/D
192
193            if (typ == ED4_TRADITIONAL_CURSOR_CONNECTED) {
194                int pu2 = point(x, y+UPPER_OFFSET+5-small_version);
195                int pl2 = point(x, y+LOWER_OFFSET-5+small_version);
196                line(pu2, pl2);
197            }
198
199            break;
200
201#undef UPPER_OFFSET
202#undef LOWER_OFFSET
203#undef CWIDTH
204
205        }
206        case ED4_FUCKING_BIG_CURSOR: {
207
208#define OUTER_HEIGHT    (term_height/4)
209#define INNER_HEIGHT    (term_height-1)
210#define CWIDTH          12
211#define STEPS           6
212#define THICKNESS       2
213
214#if (2*STEPS+THICKNESS > MAXLINES)
215#error Bad definitions!
216#endif
217
218            //          2               3               |
219            //            \           /                 | OUTER_HEIGHT
220            //               \     /                    |
221            //                  0               |       |
222            //                  |               | INNER_HEIGHT
223            //                  |               |
224            //                  1               |
225            //                /    \                    --
226            //             /          \                 --
227            //          4               5
228            //
229            //          -----------------
230            //                2*WIDTH
231
232            int s;
233            for (s=0; s<STEPS; s++) {
234                int size = ((STEPS-s)*CWIDTH)/STEPS;
235
236                horizontal_line(x, y-OUTER_HEIGHT+s,                    size);
237                horizontal_line(x, y+INNER_HEIGHT+OUTER_HEIGHT-s,       size);
238            }
239
240            int y_upper = ypos[s-4];
241            int y_lower = ypos[s-2];
242
243            int t;
244            for (t=0; t<THICKNESS; t++) {
245                int xp = x-(THICKNESS/2)+t+1;
246                line(point(xp, y_upper), point(xp, y_lower));
247            }
248
249            break;
250
251#undef OUTER_HEIGHT
252#undef INNER_HEIGHT
253#undef CWIDTH
254#undef STEPS
255#undef THICKNESS
256
257        }
258        default: {
259            e4_assert(0);
260            break;
261        }
262    }
263}
264
265/* --------------------------------------------------------------------------------
266   ED4_cursor
267   -------------------------------------------------------------------------------- */
268
269ED4_returncode ED4_cursor::draw_cursor(AW_pos x, AW_pos y) { // @@@ remove return value
270    if (cursor_shape) {
271        delete cursor_shape;
272        cursor_shape = 0;
273    }
274
275    cursor_shape = new ED4_CursorShape(ctype,
276                                       SEQ_TERM_TEXT_YOFFSET+2,
277                                       ED4_ROOT->font_group.get_width(ED4_G_SEQUENCES),
278                                       !awar_edit_rightward);
279
280    cursor_shape->draw(window()->get_device(), int(x), int(y));
281
282#if defined(TRACE_REFRESH)
283    printf("draw_cursor(%i, %i)\n", int(x), int(y));
284#endif // TRACE_REFRESH
285   
286    return ED4_R_OK;
287}
288
289ED4_returncode ED4_cursor::delete_cursor(AW_pos del_mark, ED4_base *target_terminal) {
290    AW_pos x, y;
291    target_terminal->calc_world_coords(&x, &y);
292    ED4_base *temp_parent = target_terminal->get_parent(ED4_L_SPECIES);
293    if (!temp_parent || temp_parent->flag.hidden || !is_partly_visible()) {
294        return ED4_R_BREAK;
295    }
296   
297    x = del_mark;
298    win->world_to_win_coords(&x, &y);
299
300    // refresh own terminal + terminal above + terminal below
301
302    const int     MAX_AFFECTED = 3;
303    int           affected     = 0;
304    ED4_terminal *affected_terminal[MAX_AFFECTED];
305
306    affected_terminal[affected++] = target_terminal->to_terminal();
307
308    {
309        ED4_terminal *term = target_terminal->to_terminal();
310        bool backward = true;
311
312        while (1) {
313            int done = 0;
314
315            term = backward ? term->get_prev_terminal() : term->get_next_terminal();
316            if (!term) done = 1;
317            else if ((term->is_sequence_terminal()) && !term->is_in_folded_group()) {
318                affected_terminal[affected++] = term;
319                done                          = 1;
320            }
321
322            if (done) {
323                if (!backward) break;
324                backward = false;
325                term = target_terminal->to_terminal();
326            }
327        }
328    }
329
330    bool refresh_was_requested[MAX_AFFECTED];
331    e4_assert(affected >= 1 && affected <= MAX_AFFECTED);
332    for (int a = 0; a<affected; ++a) {
333        ED4_terminal *term       = affected_terminal[a];
334        // cppcheck-suppress unreadVariable
335        refresh_was_requested[a] = term->update_info.refresh;
336        term->request_refresh();
337    }
338
339    // clear rectangle where cursor is displayed
340
341    AW_device *dev = win->get_device();
342    dev->push_clip_scale();
343
344    int xmin, xmax, ymin, ymax;
345
346    e4_assert(cursor_shape);
347    cursor_shape->get_bounding_box(int(x), int(y), xmin, ymin, xmax, ymax);
348
349#if defined(TRACE_REFRESH)
350    printf("delete_cursor(%i, %i)\n", int(x), int(y));
351#endif // TRACE_REFRESH
352
353    dev->set_font_overlap(true);
354
355#define EXPAND_SIZE 0
356    if (dev->reduceClipBorders(ymin-EXPAND_SIZE, ymax+EXPAND_SIZE, xmin-EXPAND_SIZE, xmax+EXPAND_SIZE)) {
357        dev->clear_part(xmin, ymin, xmax-xmin+1, ymax-ymin+1, AW_ALL_DEVICES);
358        // refresh terminal to hide cursor
359        ED4_LocalWinContext uses(window());
360        LocallyModify<bool> flag(allowed_to_draw, false);
361        ED4_ROOT->special_window_refresh(false);
362    }
363    dev->pop_clip_scale();
364
365    // restore old refresh flags (to avoid refresh of 3 complete terminals when cursor changes)
366    for (int a = 0; a<affected; ++a) {
367        affected_terminal[a]->update_info.refresh = refresh_was_requested[a];
368    }
369
370    return (ED4_R_OK);
371}
372
373
374ED4_cursor::ED4_cursor(ED4_window *win_) : win(win_) {
375    init();
376    allowed_to_draw = true;
377    cursor_shape    = 0;
378
379    ctype = (ED4_CursorType)(ED4_ROOT->aw_root->awar(ED4_AWAR_CURSOR_TYPE)->read_int()%ED4_CURSOR_TYPES);
380}
381void ED4_cursor::init() {
382    // used by ED4_terminal-d-tor
383    owner_of_cursor   = NULL;
384    cursor_abs_x      = 0;
385    screen_position   = 0;
386}
387ED4_cursor::~ED4_cursor() {
388    delete cursor_shape;
389}
390
391int ED4_cursor::get_sequence_pos() const {
392    ED4_remap *remap = ED4_ROOT->root_group_man->remap();
393    size_t max_scrpos = remap->get_max_screen_pos();
394
395    return remap->screen_to_sequence(size_t(screen_position)<=max_scrpos ? screen_position : max_scrpos);
396}
397
398bool ED4_species_manager::setCursorTo(ED4_cursor *cursor, int seq_pos, bool unfold_groups, ED4_CursorJumpType jump_type)
399{
400    ED4_group_manager *group_manager_to_unfold = is_in_folded_group();
401
402    if (unfold_groups) {
403        bool did_unfold = false;
404
405        while (group_manager_to_unfold && unfold_groups) {
406            ED4_base *base = group_manager_to_unfold->search_spec_child_rek(ED4_L_BRACKET);
407            if (!base) break;
408
409            base->to_bracket_terminal()->unfold();
410            did_unfold = true;
411
412            group_manager_to_unfold = is_in_folded_group();
413        }
414
415        if (did_unfold) ED4_ROOT->refresh_all_windows(1); // needed to recalculate world cache of target terminal
416    }
417
418    if (!group_manager_to_unfold) { // species manager is visible (now)
419        ED4_terminal *terminal = search_spec_child_rek(ED4_L_SEQUENCE_STRING)->to_terminal();
420        if (terminal) {
421            if (seq_pos == -1) seq_pos = cursor->get_sequence_pos();
422            cursor->set_to_terminal(terminal, seq_pos, jump_type);
423            return true;
424        }
425    }
426
427    return false;
428}
429
430static void jump_to_species(ED4_species_name_terminal *name_term, int seq_pos, bool unfold_groups, ED4_CursorJumpType jump_type)
431{
432    ED4_species_manager *species_manager = name_term->get_parent(ED4_L_SPECIES)->to_species_manager();
433    ED4_cursor *cursor = &current_cursor();
434    bool jumped = false;
435
436    if (species_manager) jumped = species_manager->setCursorTo(cursor, seq_pos, unfold_groups, jump_type);
437    if (!jumped) cursor->HideCursor();
438}
439
440static bool ignore_selected_species_changes_cb = false;
441static bool ignore_selected_SAI_changes_cb     = false;
442
443static void select_named_sequence_terminal(const char *name) {
444    GB_transaction ta(GLOBAL_gb_main);
445    ED4_species_name_terminal *name_term = ED4_find_species_name_terminal(name);
446    if (name_term) {
447        ED4_MostRecentWinContext context; // use the last used window for selection
448
449        // lookup current name term
450        ED4_species_name_terminal *cursor_name_term = 0;
451        {
452            ED4_cursor *cursor = &current_cursor();
453            if (cursor) {
454                ED4_sequence_terminal *cursor_seq_term = 0;
455
456                if (cursor->owner_of_cursor) {
457                    ED4_terminal *cursor_term = cursor->owner_of_cursor->to_text_terminal();
458                    if (cursor_term->is_sequence_terminal()) {
459                        cursor_seq_term = cursor_term->to_sequence_terminal();
460                    }
461                    else { // cursor is in a non-sequence text terminal -> search for corresponding sequence terminal
462                        ED4_multi_sequence_manager *seq_man = cursor_term->get_parent(ED4_L_MULTI_SEQUENCE)->to_multi_sequence_manager();
463                        if (seq_man) {
464                            cursor_seq_term = seq_man->search_spec_child_rek(ED4_L_SEQUENCE_STRING)->to_sequence_terminal();
465                        }
466                    }
467                }
468                if (cursor_seq_term) {
469                    cursor_name_term = cursor_seq_term->corresponding_species_name_terminal();
470                }
471            }
472        }
473
474        if (name_term!=cursor_name_term) { // do not change if already there!
475#if defined(TRACE_JUMPS)
476            printf("Jumping to species/SAI '%s'\n", name);
477#endif
478            jump_to_species(name_term, -1, false, ED4_JUMP_KEEP_POSITION);
479        }
480        else {
481#if defined(TRACE_JUMPS)
482            printf("Change ignored because same name term!\n");
483#endif
484        }
485    }
486}
487
488void ED4_selected_SAI_changed_cb(AW_root * /* aw_root */) {
489    if (!ignore_selected_SAI_changes_cb) {
490        char *name = GBT_read_string(GLOBAL_gb_main, AWAR_SAI_NAME);
491
492        if (name && name[0]) {
493#if defined(TRACE_JUMPS)
494            printf("Selected SAI is '%s'\n", name);
495#endif // DEBUG
496            LocallyModify<bool> flag(ED4_update_global_cursor_awars_allowed, false);
497            select_named_sequence_terminal(name);
498            free(name);
499        }
500    }
501}
502
503void ED4_selected_species_changed_cb(AW_root * /* aw_root */) {
504    if (!ignore_selected_species_changes_cb) {
505        char *name = GBT_read_string(GLOBAL_gb_main, AWAR_SPECIES_NAME);
506        if (name && name[0]) {
507#if defined(TRACE_JUMPS)
508            printf("Selected species is '%s'\n", name);
509#endif
510            LocallyModify<bool> flag(ED4_update_global_cursor_awars_allowed, false);
511            select_named_sequence_terminal(name);
512        }
513        free(name);
514    }
515    else {
516#if defined(TRACE_JUMPS)
517        printf("Change ignored because ignore_selected_species_changes_cb!\n");
518#endif
519    }
520}
521
522void ED4_jump_to_current_species(AW_window *aww, AW_CL) {
523    ED4_LocalWinContext uses(aww);
524    char *name = GBT_read_string(GLOBAL_gb_main, AWAR_SPECIES_NAME);
525    if (name && name[0]) {
526        GB_transaction dummy(GLOBAL_gb_main);
527#if defined(TRACE_JUMPS)
528        printf("Jump to selected species (%s)\n", name);
529#endif
530        ED4_species_name_terminal *name_term = ED4_find_species_name_terminal(name);
531
532        if (name_term) {
533            jump_to_species(name_term, -1, true, ED4_JUMP_KEEP_POSITION);
534        }
535        else {
536            aw_message(GBS_global_string("Species '%s' is not loaded - use GET to load it.", name));
537        }
538    }
539    else {
540        aw_message("Please select a species");
541    }
542}
543
544static bool multi_species_man_consensus_id_starts_with(ED4_base *base, AW_CL cl_start) { // TRUE if consensus id starts with 'start'
545    ED4_multi_species_manager *ms_man = base->to_multi_species_manager();
546    char *start = (char*)cl_start;
547    ED4_base *consensus = ms_man->search_spec_child_rek(ED4_L_SPECIES);
548
549    if (consensus) {
550        ED4_base *consensus_name = consensus->search_spec_child_rek(ED4_L_SPECIES_NAME);
551
552        if (consensus_name) {
553            if (strncmp(consensus_name->id, start, strlen(start))==0) {
554                ED4_multi_species_manager *cons_ms_man = consensus_name->get_parent(ED4_L_MULTI_SPECIES)->to_multi_species_manager();
555                return cons_ms_man==ms_man;
556            }
557        }
558    }
559
560    return false;
561}
562
563ED4_multi_species_manager *ED4_new_species_multi_species_manager() {     // returns manager into which new species should be inserted
564    ED4_base *manager = ED4_ROOT->root_group_man->find_first_that(ED4_L_MULTI_SPECIES, multi_species_man_consensus_id_starts_with, (AW_CL)"More Sequences");
565    return manager ? manager->to_multi_species_manager() : 0;
566}
567
568void ED4_init_notFoundMessage() {
569    not_found_counter = 0;
570    not_found_message = GBS_stropen(10000);
571    // GBS_strcat(not_found_message,"Species not found: ");
572}
573void ED4_finish_and_show_notFoundMessage() {
574    if (not_found_counter != 0) {
575        if (not_found_counter>MAX_SHOWN_MISSING_SPECIES) {
576            GBS_strcat(not_found_message, GBS_global_string("(skipped display of %zu more species)\n", not_found_counter-MAX_SHOWN_MISSING_SPECIES));
577        }
578        char *out_message = GBS_strclose(not_found_message);
579        aw_message(out_message);
580        aw_message(GBS_global_string("Couldn't load %zu species:", not_found_counter));
581        free(out_message);
582    }
583    else {
584        GBS_strforget(not_found_message);
585    }
586    not_found_message = 0;
587}
588
589void ED4_get_and_jump_to_species(GB_CSTR species_name)
590{
591    e4_assert(species_name && species_name[0]);
592
593    GB_transaction dummy(GLOBAL_gb_main);
594    ED4_species_name_terminal *name_term = ED4_find_species_name_terminal(species_name);
595    int loaded = 0;
596
597    if (!name_term) { // insert new species
598        ED4_multi_species_manager *insert_into_manager = ED4_new_species_multi_species_manager();
599        ED4_group_manager         *group_man           = insert_into_manager->get_parent(ED4_L_GROUP)->to_group_manager();
600        char                      *string              = (char*)GB_calloc(strlen(species_name)+3, sizeof(*string));
601        int                        index               = 0;
602        ED4_index                  y                   = 0;
603        ED4_index                  lot                 = 0;
604
605        ED4_init_notFoundMessage();
606
607        sprintf(string, "-L%s", species_name);
608        ED4_ROOT->database->fill_species(insert_into_manager,
609                                         ED4_ROOT->ref_terminals.get_ref_sequence_info(), ED4_ROOT->ref_terminals.get_ref_sequence(),
610                                         string, &index, &y, 0, &lot, insert_into_manager->calc_group_depth(), NULL);
611        loaded = 1;
612
613        ED4_finish_and_show_notFoundMessage();
614
615        {
616            GBDATA *gb_species = GBT_find_species(GLOBAL_gb_main, species_name);
617            GBDATA *gbd = GBT_read_sequence(gb_species, ED4_ROOT->alignment_name);
618
619            if (gbd) {
620                char *data = GB_read_string(gbd);
621                int   len  = GB_read_string_count(gbd);
622
623                group_man->update_bases(0, 0, data, len);
624            }
625        }
626
627        name_term = ED4_find_species_name_terminal(species_name);
628        if (name_term) {
629            // new AAseqTerminals should be created if it is in ProtView mode
630            if (ED4_ROOT->alignment_type == GB_AT_DNA) {
631                PV_AddCorrespondingOrfTerminals(name_term);
632            }
633            ED4_ROOT->main_manager->request_resize(); // @@@ instead needs to be called whenever adding or deleting PV-terminals
634            // it should create new AA Sequence terminals if the protein viewer is enabled
635        }
636        delete string;
637
638        insert_into_manager->invalidate_species_counters();
639    }
640    if (name_term) {
641        jump_to_species(name_term, -1, true, ED4_JUMP_KEEP_POSITION);
642        if (!loaded) {
643            aw_message(GBS_global_string("'%s' is already loaded.", species_name));
644        }
645    }
646    else {
647        aw_message(GBS_global_string("Cannot get species '%s'", species_name));
648    }
649}
650
651void ED4_get_and_jump_to_current(AW_window *aww, AW_CL) {
652    ED4_LocalWinContext uses(aww);
653    char *name = GBT_read_string(GLOBAL_gb_main, AWAR_SPECIES_NAME);
654    if (name && name[0]) {
655        ED4_get_and_jump_to_species(name);
656    }
657    else {
658        aw_message("Please select a species");
659    }
660}
661
662void ED4_get_and_jump_to_current_from_menu(AW_window *aw, AW_CL cl, AW_CL) {
663    ED4_get_and_jump_to_current(aw, cl);
664}
665
666void ED4_get_marked_from_menu(AW_window *, AW_CL, AW_CL) {
667#define BUFFERSIZE 1000
668    GB_transaction dummy(GLOBAL_gb_main);
669    int marked = GBT_count_marked_species(GLOBAL_gb_main);
670
671    if (marked) {
672        GBDATA *gb_species = GBT_first_marked_species(GLOBAL_gb_main);
673        char   *buffer     = new char[BUFFERSIZE+1];
674        char   *bp         = buffer;
675
676        ED4_multi_species_manager *insert_into_manager = ED4_new_species_multi_species_manager();
677        ED4_group_manager         *group_man           = insert_into_manager->get_parent(ED4_L_GROUP)->to_group_manager();
678
679        int        group_depth       = insert_into_manager->calc_group_depth();
680        int        index             = 0;
681        ED4_index  y                 = 0;
682        ED4_index  lot               = 0;
683        int        inserted          = 0;
684        char      *default_alignment = GBT_get_default_alignment(GLOBAL_gb_main);
685
686        arb_progress progress("Loading species", marked);
687
688        ED4_init_notFoundMessage();
689
690        while (gb_species) {
691            const char *name = GBT_read_name(gb_species);
692            ED4_species_name_terminal *name_term = ED4_find_species_name_terminal(name);
693
694            if (!name_term) { // species not found -> insert
695                {
696                    int namelen = strlen(name);
697                    int buffree = BUFFERSIZE-int(bp-buffer);
698
699                    if ((namelen+2)>buffree) {
700                        *bp = 0;
701                        ED4_ROOT->database->fill_species(insert_into_manager,
702                                                         ED4_ROOT->ref_terminals.get_ref_sequence_info(), ED4_ROOT->ref_terminals.get_ref_sequence(),
703                                                         buffer, &index, &y, 0, &lot, group_depth, NULL);
704                        bp = buffer;
705                        index = 0;
706                    }
707
708                    *bp++ = 1;
709                    *bp++ = 'L';
710                    memcpy(bp, name, namelen);
711                    bp += namelen;
712                }
713
714                {
715                    GBDATA *gbd = GBT_read_sequence(gb_species, default_alignment);
716
717                    if (gbd) {
718                        char *data = GB_read_string(gbd);
719                        int len = GB_read_string_count(gbd);
720                        group_man->table().add(data, len);
721                    }
722                }
723
724                inserted++;
725            }
726            gb_species = GBT_next_marked_species(gb_species);
727            progress.inc();
728        }
729
730        if (bp>buffer) {
731            *bp++ = 0;
732            ED4_ROOT->database->fill_species(insert_into_manager,
733                                             ED4_ROOT->ref_terminals.get_ref_sequence_info(), ED4_ROOT->ref_terminals.get_ref_sequence(),
734                                             buffer, &index, &y, 0, &lot, group_depth, NULL);
735        }
736
737        aw_message(GBS_global_string("Loaded %i of %i marked species.", inserted, marked));
738
739        ED4_finish_and_show_notFoundMessage();
740
741        if (inserted) {
742            // new AAseqTerminals should be created if it is in ProtView mode
743            if (ED4_ROOT->alignment_type == GB_AT_DNA) {
744                PV_AddOrfTerminalsToLoadedSpecies();
745            }
746            ED4_ROOT->main_manager->request_resize(); // @@@ instead needs to be called whenever adding or deleting PV-terminals
747        }
748
749        delete [] buffer;
750        free(default_alignment);
751    }
752    else {
753        aw_message("No species marked.");
754    }
755
756#undef BUFFERSIZE
757}
758
759ED4_returncode ED4_cursor::HideCursor()
760{
761    if (owner_of_cursor) {
762        if (is_partly_visible()) {
763            delete_cursor(cursor_abs_x, owner_of_cursor);
764        }
765        owner_of_cursor = 0;
766    }
767
768    return ED4_R_OK;
769}
770
771void ED4_cursor::changeType(ED4_CursorType typ)
772{
773    if (owner_of_cursor) {
774        ED4_terminal *old_owner_of_cursor = owner_of_cursor;
775
776        HideCursor();
777        ctype = typ;
778        ED4_ROOT->aw_root->awar(ED4_AWAR_CURSOR_TYPE)->write_int(int(ctype));
779        owner_of_cursor = old_owner_of_cursor;
780        ShowCursor(0, ED4_C_NONE, 0);
781    }
782    else {
783        ctype = typ;
784    }
785}
786
787bool ED4_update_global_cursor_awars_allowed = true;
788
789static void trace_termChange_in_global_awar(ED4_terminal *term, char*& last_value, bool& ignore_flag, const char *awar_name) {
790    char *species_name = term->get_name_of_species();
791
792    if (!last_value || strcmp(last_value, species_name) != 0) {
793        freeset(last_value, species_name);
794        LocallyModify<bool> raise(ignore_flag, true);
795#if defined(TRACE_JUMPS)
796        fprintf(stderr, "Writing '%s' to awar '%s'\n", species_name, awar_name);
797#endif
798        GBT_write_string(GLOBAL_gb_main, awar_name, species_name);
799    }
800    else {
801        free(species_name);
802    }
803}
804
805void ED4_cursor::updateAwars(bool new_term_selected) {
806    AW_root *aw_root = ED4_ROOT->aw_root;
807    int      seq_pos = get_sequence_pos();
808
809    if (ED4_update_global_cursor_awars_allowed && new_term_selected) {
810        // @@@ last_set_XXX has to be window-specific (or better cursor specific)
811        if (in_SAI_terminal()) {
812            static char *last_set_SAI = 0;
813            trace_termChange_in_global_awar(owner_of_cursor, last_set_SAI, ignore_selected_SAI_changes_cb, AWAR_SAI_NAME);
814        }
815        else if (in_species_seq_terminal()) {
816            static char *last_set_species = 0;
817            trace_termChange_in_global_awar(owner_of_cursor, last_set_species, ignore_selected_species_changes_cb, AWAR_SPECIES_NAME);
818        }
819    }
820
821    // update awars for cursor position:
822    aw_root->awar(win->awar_path_for_cursor)->write_int(info2bio(seq_pos));
823    if (ED4_update_global_cursor_awars_allowed) {
824        aw_root->awar(AWAR_CURSOR_POSITION)->write_int(info2bio(seq_pos)); // update ARB-global cursor position
825    }
826
827    // look if we have a commented search result under the cursor:
828
829    if (owner_of_cursor && owner_of_cursor->is_sequence_terminal()) {
830        ED4_sequence_terminal *seq_term = owner_of_cursor->to_sequence_terminal();
831        ED4_SearchResults &results = seq_term->results();
832        ED4_SearchPosition *pos = results.get_shown_at(seq_pos);
833
834        if (pos) {
835            GB_CSTR comment = pos->get_comment();
836
837            if (comment) {
838                char *buffer = GB_give_buffer(strlen(comment)+30);
839
840                sprintf(buffer, "%s: %s", ED4_SearchPositionTypeId[pos->get_whatsFound()], comment);
841                aw_message(buffer);
842            }
843        }
844    }
845
846    // update awar for ecoli position:
847    BI_ecoli_ref *ecoli = ED4_ROOT->ecoli_ref;
848    if (ecoli->gotData()) {
849        int ecoli_pos = ecoli->abs_2_rel(seq_pos+1); // inclusive position behind cursor
850        aw_root->awar(win->awar_path_for_Ecoli)->write_int(ecoli_pos);
851    }
852
853    // update awar for base position:
854    int base_pos = base_position.get_base_position(owner_of_cursor, seq_pos+1); // inclusive position behind cursor
855    aw_root->awar(win->awar_path_for_basePos)->write_int(base_pos);
856
857    // update awar for IUPAC:
858
859#define MAXIUPAC 6
860
861    char iupac[MAXIUPAC+1];
862    strcpy(iupac, ED4_IUPAC_EMPTY);
863
864    {
865        char at[2] = "\0";
866
867        if (in_consensus_terminal()) {
868            ED4_group_manager *group_manager = owner_of_cursor->get_parent(ED4_L_GROUP)->to_group_manager();
869            ED4_char_table&    groupTab      = group_manager->table();
870            if (seq_pos<groupTab.size()) {
871                groupTab.build_consensus_string_to(at, ExplicitRange(seq_pos, seq_pos));
872            }
873        }
874        else if (in_species_seq_terminal() || in_SAI_terminal()) {
875            int         len;
876            const char *seq = owner_of_cursor->resolve_pointer_to_char_pntr(&len);
877
878            if (seq_pos<len) at[0] = seq[seq_pos];
879        }
880
881        if (at[0]) {
882            char base = at[0];
883            const char *i = ED4_decode_iupac(base, ED4_ROOT->alignment_type);
884
885            e4_assert(strlen(i)<=MAXIUPAC);
886            strcpy(iupac, i);
887        }
888    }
889
890#undef MAXIUPAC
891
892    aw_root->awar(win->awar_path_for_IUPAC)->write_string(iupac);
893
894    // update awar for helix#:
895    const char *helixNr = ED4_ROOT->helix->helixNr(seq_pos);
896    aw_root->awar(win->awar_path_for_helixNr)->write_string(helixNr ? helixNr : "");
897}
898
899int ED4_cursor::get_screen_relative_pos() const {
900    const ED4_coords& coords = window()->coords;
901    return cursor_abs_x - coords.window_left_clip_point;
902}
903void ED4_cursor::set_screen_relative_pos(int scroll_to_relpos) {
904    int curr_rel_pos  = get_screen_relative_pos();
905    int scroll_amount = curr_rel_pos-scroll_to_relpos;
906
907    int length_of_char = ED4_ROOT->font_group.get_width(ED4_G_SEQUENCES);
908    scroll_amount      = (scroll_amount/length_of_char)*length_of_char; // align to char-size
909
910    if (scroll_amount != 0) {
911        AW_window *aww = window()->aww;
912        aww->set_horizontal_scrollbar_position(aww->slider_pos_horizontal + scroll_amount);
913#if defined(TRACE_JUMPS)
914        printf("set_screen_relative_pos(%i) auto-scrolls %i\n", scroll_to_relpos, scroll_amount);
915#endif
916        ED4_horizontal_change_cb(aww, 0, 0);
917    }
918}
919
920
921void ED4_cursor::jump_screen_pos(int screen_pos, ED4_CursorJumpType jump_type) {
922    if (!owner_of_cursor) {
923        aw_message("First you have to place the cursor");
924        return;
925    }
926
927    if (owner_of_cursor->is_hidden()) return; // do not jump if cursor terminal is hidden
928
929    AW_pos terminal_x, terminal_y;
930    owner_of_cursor->calc_world_coords(&terminal_x, &terminal_y);
931
932#if defined(TRACE_JUMPS)
933    printf("jump_screen_pos(%i)\n", screen_pos);
934#endif
935
936    ED4_terminal *term = owner_of_cursor;
937    ED4_window   *ed4w = window();
938
939    term->scroll_into_view(ed4w); // correct y-position of terminal
940
941    int cursor_diff = screen_pos-screen_position;
942    if (cursor_diff == 0) { // cursor position did not change
943        if (jump_type == ED4_JUMP_KEEP_VISIBLE) return; // nothing special -> done
944    }
945
946    int terminal_pixel_length = ed4w->get_device()->get_string_size(ED4_G_SEQUENCES, 0, owner_of_cursor->to_text_terminal()->get_length());
947    int length_of_char        = ED4_ROOT->font_group.get_width(ED4_G_SEQUENCES);
948    int abs_x_new             = cursor_abs_x+cursor_diff*length_of_char; // position in terminal
949
950    if (abs_x_new > terminal_x+terminal_pixel_length+CHARACTEROFFSET || abs_x_new < terminal_x+CHARACTEROFFSET) {
951        return; // don`t move out of terminal
952    }
953
954    ED4_coords *coords = &ed4w->coords;
955
956    int screen_width  = coords->window_right_clip_point-coords->window_left_clip_point;
957    int scroll_new_to = -1;     // if >0 -> scroll abs_x_new to this screen-relative position
958
959    if (jump_type != ED4_JUMP_KEEP_VISIBLE) {
960        int rel_x = cursor_abs_x-coords->window_left_clip_point;
961
962        if (jump_type == ED4_JUMP_KEEP_POSITION) {
963            bool was_in_screen = rel_x >= 0 && rel_x <= screen_width;
964            if (!was_in_screen) {
965                jump_type = ED4_JUMP_KEEP_VISIBLE; // // don't have useful relative position -> scroll
966            }
967        }
968
969        switch (jump_type) {
970            case ED4_JUMP_CENTERED:             scroll_new_to = screen_width/2;         break;
971            case ED4_JUMP_KEEP_POSITION:        scroll_new_to = rel_x;                  break;
972            case ED4_JUMP_KEEP_VISIBLE:         break; // handled below
973            default: e4_assert(0); break;
974        }
975    }
976
977    if (jump_type == ED4_JUMP_KEEP_VISIBLE) {
978        int margin = length_of_char * ED4_ROOT->aw_root->awar(ED4_AWAR_SCROLL_MARGIN)->read_int();
979
980        int right_margin = coords->window_right_clip_point - margin;
981        int left_margin  = coords->window_left_clip_point + margin;
982
983        if (left_margin >= right_margin)    scroll_new_to = screen_width/2; // margins too big -> center
984        else if (abs_x_new > right_margin)  scroll_new_to = screen_width-margin;
985        else if (abs_x_new < left_margin)   scroll_new_to = margin;
986    }
987
988    delete_cursor(cursor_abs_x, owner_of_cursor);
989
990    if (scroll_new_to >= 0) {   // scroll
991        int rel_x_new     = abs_x_new-coords->window_left_clip_point;
992        int scroll_amount = rel_x_new-scroll_new_to;
993
994        if      (scroll_amount>0) scroll_amount += length_of_char/2;
995        else if (scroll_amount<0) scroll_amount -= length_of_char/2;
996
997        scroll_amount = (scroll_amount/length_of_char)*length_of_char; // align to char-size
998        if (scroll_amount != 0) {
999            AW_window *aww  = ed4w->aww;
1000            aww->set_horizontal_scrollbar_position(aww->slider_pos_horizontal + scroll_amount);
1001#if defined(TRACE_JUMPS)
1002            printf("jump_screen_pos auto-scrolls %i\n", scroll_amount);
1003#endif
1004            LocallyModify<bool> flag(allowed_to_draw, false);
1005            ED4_horizontal_change_cb(aww, 0, 0);
1006        }
1007    }
1008
1009    LocallyModify<bool> flag(allowed_to_draw, true);
1010    if (cursor_diff >= 0) {
1011        ShowCursor(cursor_diff*length_of_char, ED4_C_RIGHT, abs(cursor_diff));
1012    }
1013    else {
1014        ShowCursor(cursor_diff*length_of_char, ED4_C_LEFT, abs(cursor_diff));
1015    }
1016}
1017
1018void ED4_cursor::jump_sequence_pos(int seq_pos, ED4_CursorJumpType jump_type) {
1019    int screen_pos = ED4_ROOT->root_group_man->remap()->sequence_to_screen(seq_pos);
1020    jump_screen_pos(screen_pos, jump_type);
1021}
1022
1023void ED4_cursor::jump_base_pos(int base_pos, ED4_CursorJumpType jump_type) {
1024    int seq_pos = base2sequence_position(base_pos);
1025    jump_sequence_pos(seq_pos, jump_type);
1026}
1027
1028class has_base_at : public ED4_TerminalPredicate {
1029    int  seq_pos;
1030    bool want_base;
1031public:
1032    has_base_at(int seq_pos_, bool want_base_) : seq_pos(seq_pos_), want_base(want_base_) {}
1033    bool fulfilled_by(const ED4_terminal *terminal) const OVERRIDE {
1034        bool test_succeeded = false;
1035
1036        if (terminal->is_sequence_terminal()) {
1037            const ED4_sequence_terminal *seqTerm = terminal->to_sequence_terminal();
1038            int len;
1039            char *seq = seqTerm->resolve_pointer_to_string_copy(&len);
1040            if (seq) {
1041                test_succeeded = len>seq_pos && bool(ADPP_IS_ALIGN_CHARACTER(seq[seq_pos]))!=want_base;
1042            }
1043            free(seq);
1044        }
1045
1046        return test_succeeded;
1047    }
1048};
1049
1050struct acceptAnyTerminal : public ED4_TerminalPredicate {
1051    bool fulfilled_by(const ED4_terminal *) const OVERRIDE { return true; }
1052};
1053
1054static ED4_terminal *get_upper_lower_cursor_pos(ED4_manager *starting_point, ED4_cursor_move cursor_move, AW_pos current_y, bool isScreen, const ED4_TerminalPredicate& predicate) {
1055    // current_y is y-position of terminal at which search starts.
1056    // It may be in world or screen coordinates (isScreen marks which is used)
1057    // This is needed to move the cursor from top- to middle-area w/o scrolling middle-area to top-position.
1058
1059    ED4_terminal *result = 0;
1060
1061    int m      = 0;
1062    int last_m = starting_point->children->members()-1;
1063    int incr   = 1;
1064
1065    if (cursor_move == ED4_C_UP) {
1066        std::swap(m, last_m); incr = -1; // revert search direction
1067        e4_assert(!isScreen);            // use screen coordinates for ED4_C_DOWN only!
1068    }
1069
1070    while (!result) {
1071        ED4_base *member = starting_point->children->member(m);
1072
1073        if (member->is_manager()) {
1074            if (!member->flag.hidden) { // step over hidden managers (e.g. folded groups)
1075                result = get_upper_lower_cursor_pos(member->to_manager(), cursor_move, current_y, isScreen, predicate);
1076            }
1077        }
1078        else {
1079            if (member->dynamic_prop & ED4_P_CURSOR_ALLOWED) {
1080                AW_pos x, y;
1081                member->calc_world_coords(&x, &y);
1082                if (isScreen) current_ed4w()->world_to_win_coords(&x, &y); // if current_y is screen, convert x/y to screen-coordinates as well
1083
1084                bool pos_beyond = (cursor_move == ED4_C_UP) ? y<current_y : y>current_y;
1085                if (pos_beyond) {
1086                    bool skip_top_scrolled = false;
1087                    if (isScreen) {
1088                        ED4_multi_species_manager *marea_man = NULL;
1089                        if (member->get_area_level(&marea_man) == ED4_A_MIDDLE_AREA) {
1090                            // previous position was in top-area -> avoid selecting scrolled-out terminals
1091                            AW_pos y_area     = marea_man->parent->extension.position[Y_POS];
1092                            skip_top_scrolled = y<y_area;
1093                        }
1094                    }
1095
1096                    if (!skip_top_scrolled) {
1097                        ED4_terminal *term = member->to_terminal();
1098                        if (predicate.fulfilled_by(term)) result = term;
1099                    }
1100                }
1101            }
1102        }
1103
1104        if (m == last_m) break;
1105        m += incr;
1106    }
1107
1108    return result;
1109}
1110
1111struct acceptConsensusTerminal : public ED4_TerminalPredicate {
1112    bool fulfilled_by(const ED4_terminal *term) const OVERRIDE { return term->is_consensus_terminal(); }
1113};
1114
1115ED4_returncode ED4_cursor::move_cursor(AW_event *event) {
1116    // move cursor up down
1117    ED4_cursor_move dir     = ED4_C_NONE;
1118    ED4_returncode  result  = ED4_R_OK;
1119    bool            endHome = false;
1120
1121    switch (event->keycode) {
1122        case AW_KEY_UP:   dir = ED4_C_UP;   break;
1123        case AW_KEY_DOWN: dir = ED4_C_DOWN; break;
1124        case AW_KEY_HOME: dir = ED4_C_UP;   endHome = true; break;
1125        case AW_KEY_END:  dir = ED4_C_DOWN; endHome = true; break;
1126        default: e4_assert(0); break; // illegal call of move_cursor()
1127    }
1128
1129    if (dir != ED4_C_NONE) {
1130        if (owner_of_cursor->is_hidden()) result = ED4_R_IMPOSSIBLE; // don't move cursor if terminal is hidden
1131
1132        if (result == ED4_R_OK) {
1133            AW_pos x_dummy, y_world;
1134
1135            owner_of_cursor->calc_world_coords(&x_dummy, &y_world);
1136
1137            int seq_pos = get_sequence_pos();
1138            ED4_terminal *target_terminal = 0;
1139
1140            // stay in current area
1141            ED4_multi_species_manager *start_at_manager = 0;
1142            ED4_AREA_LEVEL             area_level       = owner_of_cursor->get_area_level(&start_at_manager);
1143
1144            if (event->keymodifier & AW_KEYMODE_CONTROL) {
1145                bool has_base = has_base_at(seq_pos, true).fulfilled_by(owner_of_cursor);
1146
1147                if (!endHome) { // not End or Home
1148                    target_terminal = get_upper_lower_cursor_pos(start_at_manager, dir, y_world, false, has_base_at(seq_pos, !has_base));
1149                    if (target_terminal && !has_base && ED4_ROOT->aw_root->awar(ED4_AWAR_FAST_CURSOR_JUMP)->read_int()) {  // if jump_over group
1150                        target_terminal->calc_world_coords(&x_dummy, &y_world);
1151                        target_terminal = get_upper_lower_cursor_pos(start_at_manager, dir, y_world, false, has_base_at(seq_pos, false));
1152                    }
1153                }
1154
1155                if (target_terminal == 0) {
1156                    // either already in last group (or no space behind last group) -> jump to end (or start)
1157                    target_terminal = get_upper_lower_cursor_pos(start_at_manager,
1158                                                                 dir == ED4_C_UP ? ED4_C_DOWN : ED4_C_UP, // use opposite movement direction
1159                                                                 dir == ED4_C_UP ? 0 : INT_MAX, false, // search for top-/bottom-most terminal
1160                                                                 acceptAnyTerminal());
1161                }
1162            }
1163            else {
1164                e4_assert(!endHome); // END and HOME w/o Ctrl should not call move_cursor()
1165                if (event->keymodifier & AW_KEYMODE_ALT) {
1166                    target_terminal = get_upper_lower_cursor_pos(start_at_manager, dir, y_world, false, acceptConsensusTerminal());
1167                }
1168                else {
1169                    bool isScreen = false;
1170                    if (dir == ED4_C_DOWN) {
1171                        if (area_level == ED4_A_TOP_AREA) {
1172                            window()->world_to_win_coords(&x_dummy, &y_world); // special handling to move cursor from top to bottom area
1173                            isScreen = true;
1174                        }
1175                    }
1176                    target_terminal = get_upper_lower_cursor_pos(ED4_ROOT->main_manager, dir, y_world, isScreen, acceptAnyTerminal());
1177                }
1178            }
1179
1180            if (target_terminal) {
1181                set_to_terminal(target_terminal, seq_pos, ED4_JUMP_KEEP_VISIBLE);
1182            }
1183        }
1184    }
1185
1186    return result;
1187}
1188
1189void ED4_cursor::set_abs_x() {
1190    AW_pos x, y;
1191    owner_of_cursor->calc_world_coords(&x, &y);
1192    cursor_abs_x = int(get_sequence_pos()*ED4_ROOT->font_group.get_width(ED4_G_SEQUENCES) + CHARACTEROFFSET + x);
1193}
1194
1195
1196ED4_returncode ED4_cursor::ShowCursor(ED4_index offset_x, ED4_cursor_move move, int move_pos) {
1197    AW_pos x=0, y=0, x_help = 0, y_help;
1198
1199    owner_of_cursor->calc_world_coords(&x, &y);
1200
1201    switch (move) {
1202        case ED4_C_RIGHT: screen_position += move_pos; break;
1203        case ED4_C_LEFT:  screen_position -= move_pos; break;
1204        case ED4_C_NONE: break; // no move - only redisplay
1205        default: e4_assert(0); break;
1206    }
1207
1208    {
1209        LocallyModify<bool> flag(ED4_update_global_cursor_awars_allowed, ED4_update_global_cursor_awars_allowed && move != ED4_C_NONE);
1210        updateAwars(false);
1211    }
1212
1213    x_help = cursor_abs_x + offset_x;
1214    y_help = y;
1215
1216    window()->world_to_win_coords(&x_help, &y_help);
1217
1218    if (allowed_to_draw) draw_cursor(x_help, y_help);
1219#if defined(DEBUG) && 0
1220    else printf("Skip draw_cursor (allowed_to_draw=false)\n");
1221#endif // DEBUG
1222
1223    cursor_abs_x += offset_x;
1224
1225    return (ED4_R_OK);
1226}
1227
1228bool ED4_cursor::is_hidden_inside_group() const {
1229    if (!owner_of_cursor) return false;
1230    if (!owner_of_cursor->is_in_folded_group()) return false;
1231    if (in_consensus_terminal()) {
1232        ED4_base *group_man = owner_of_cursor->get_parent(ED4_L_GROUP);
1233        e4_assert(group_man);
1234        return group_man->is_in_folded_group();
1235    }
1236    return true;
1237}
1238
1239bool ED4_cursor::is_partly_visible() const {
1240    e4_assert(owner_of_cursor);
1241    e4_assert(cursor_shape); // cursor is not drawn, cannot test visibility
1242
1243    if (is_hidden_inside_group()) {
1244        printf("is_hidden_inside_group=true\n");
1245        return false;
1246    }
1247
1248    AW_pos x, y;
1249    owner_of_cursor->calc_world_coords(&x, &y);
1250
1251    int x1, y1, x2, y2;
1252    cursor_shape->get_bounding_box(cursor_abs_x, int(y), x1, y1, x2, y2);
1253
1254    bool visible = false;
1255
1256    switch (owner_of_cursor->get_area_level(0)) {
1257        case ED4_A_TOP_AREA:    visible = win->shows_xpos(x1) || win->shows_xpos(x2); break;
1258        case ED4_A_MIDDLE_AREA: visible = win->partly_shows(x1, y1, x2, y2); break;
1259        default: break;
1260    }
1261
1262    return visible;
1263}
1264
1265bool ED4_cursor::is_completely_visible() const {
1266    e4_assert(owner_of_cursor);
1267    e4_assert(cursor_shape); // cursor is not drawn, cannot test visibility
1268
1269    if (is_hidden_inside_group()) return false;
1270
1271    AW_pos x, y;
1272    owner_of_cursor->calc_world_coords(&x, &y);
1273
1274    int x1, y1, x2, y2;
1275    cursor_shape->get_bounding_box(cursor_abs_x, int(y), x1, y1, x2, y2);
1276
1277    bool visible = false;
1278
1279    switch (owner_of_cursor->get_area_level(0)) {
1280        case ED4_A_TOP_AREA:    visible = win->shows_xpos(x1) && win->shows_xpos(x2); break;
1281        case ED4_A_MIDDLE_AREA: visible = win->completely_shows(x1, y1, x2, y2); break;
1282        default: break;
1283    }
1284
1285    return visible;
1286}
1287
1288#if defined(DEBUG) && 0
1289#define DUMP_SCROLL_INTO_VIEW
1290#endif
1291
1292void ED4_terminal::scroll_into_view(ED4_window *ed4w) { // scroll y-position only
1293    ED4_LocalWinContext uses(ed4w);
1294
1295    AW_pos termw_x, termw_y;
1296    calc_world_coords(&termw_x, &termw_y);
1297
1298    ED4_coords *coords  = &current_ed4w()->coords;
1299
1300    int term_height = int(extension.size[1]);
1301    int win_ysize   = coords->window_lower_clip_point - coords->window_upper_clip_point + 1;
1302
1303    bool scroll = false;
1304    int  slider_pos_y;
1305
1306    ED4_cursor_move direction = ED4_C_NONE;
1307
1308    AW_pos termw_y_upper = termw_y; // upper border of terminal
1309    AW_pos termw_y_lower = termw_y + term_height; // lower border of terminal
1310
1311    if (termw_y_upper > coords->top_area_height) { // don't scroll if terminal is in top area (always visible)
1312        if (termw_y_upper < coords->window_upper_clip_point) { // scroll up
1313#ifdef DUMP_SCROLL_INTO_VIEW
1314            printf("termw_y_upper(%i) < window_upper_clip_point(%i)\n",
1315                   int(termw_y_upper), int(coords->window_upper_clip_point));
1316#endif // DEBUG
1317            slider_pos_y = int(termw_y_upper - coords->top_area_height - term_height);
1318            scroll       = true;
1319            direction    = ED4_C_UP;
1320        }
1321        else if (termw_y_lower > coords->window_lower_clip_point) { // scroll down
1322#ifdef DUMP_SCROLL_INTO_VIEW
1323            printf("termw_y_lower(%i) > window_lower_clip_point(%i)\n",
1324                   int(termw_y_lower), int(coords->window_lower_clip_point));
1325#endif // DEBUG
1326            slider_pos_y = int(termw_y_upper - coords->top_area_height - win_ysize);
1327            scroll       = true;
1328            direction    = ED4_C_DOWN;
1329        }
1330    }
1331
1332#ifdef DUMP_SCROLL_INTO_VIEW
1333    if (!scroll) {
1334        printf("No scroll needed (termw_y_upper=%i termw_y_lower=%i term_height=%i window_upper_clip_point=%i window_lower_clip_point=%i)\n",
1335               int(termw_y_upper), int(termw_y_lower), term_height,
1336               int(coords->window_upper_clip_point), int(coords->window_lower_clip_point));
1337    }
1338#endif // DEBUG
1339
1340    if (scroll) {
1341        AW_window *aww = ed4w->aww;
1342
1343        int pic_ysize         = int(aww->get_scrolled_picture_height());
1344        int slider_pos_yrange = pic_ysize - win_ysize;
1345
1346        if (slider_pos_yrange<0) { // win_ysize > pic_ysize
1347            slider_pos_y = 0;
1348        }
1349        else {
1350            ED4_multi_species_manager *start_at_manager = 0;
1351            get_area_level(&start_at_manager);
1352           
1353            ED4_terminal *nextTerm = get_upper_lower_cursor_pos(start_at_manager, direction, termw_y, false, acceptAnyTerminal());
1354            if (!nextTerm) { // no next term in this area
1355                slider_pos_y = direction == ED4_C_UP ? 0 : slider_pos_yrange; // scroll to limit
1356            }
1357        }
1358
1359        if (slider_pos_y>slider_pos_yrange) slider_pos_y = slider_pos_yrange;
1360        if (slider_pos_y<0) slider_pos_y                 = 0;
1361
1362        aww->set_vertical_scrollbar_position(slider_pos_y);
1363        ED4_scrollbar_change_cb(aww, 0, 0);
1364    }
1365}
1366
1367void ED4_cursor::set_to_terminal(ED4_terminal *terminal, int seq_pos, ED4_CursorJumpType jump_type) {
1368    if (seq_pos == -1) seq_pos = get_sequence_pos();
1369
1370    bool new_term_selected = owner_of_cursor != terminal;
1371    if (new_term_selected) {
1372        if (owner_of_cursor) {
1373            if (get_sequence_pos() != seq_pos) {
1374                jump_sequence_pos(seq_pos, jump_type); // position to wanted column -- scrolls horizontally
1375            }
1376        }
1377
1378        int scr_pos = ED4_ROOT->root_group_man->remap()->sequence_to_screen(seq_pos);
1379        show_cursor_at(terminal, scr_pos);
1380
1381        if (!is_completely_visible()) {
1382            jump_sequence_pos(seq_pos, jump_type);
1383        }
1384    }
1385    else {
1386        jump_sequence_pos(seq_pos, jump_type);
1387    }
1388
1389    GB_transaction ta(GLOBAL_gb_main);
1390    updateAwars(new_term_selected);
1391}
1392
1393ED4_returncode ED4_cursor::show_cursor_at(ED4_terminal *target_terminal, ED4_index scr_pos) {
1394    bool new_term_selected = owner_of_cursor != target_terminal;
1395   
1396    if (owner_of_cursor) {
1397        if (is_partly_visible()) {
1398            delete_cursor(cursor_abs_x, owner_of_cursor);
1399        }
1400        owner_of_cursor = NULL;
1401        DRAW = 1;
1402    }
1403
1404    target_terminal->scroll_into_view(window());
1405
1406    AW_pos termw_x, termw_y;
1407    target_terminal->calc_world_coords(&termw_x, &termw_y);
1408
1409    screen_position = scr_pos;
1410
1411    int length_of_char = ED4_ROOT->font_group.get_width(ED4_G_SEQUENCES);
1412
1413    AW_pos world_x = termw_x + length_of_char*screen_position + CHARACTEROFFSET;
1414    AW_pos world_y = termw_y;
1415
1416    AW_pos win_x = world_x;
1417    AW_pos win_y = world_y;
1418    window()->world_to_win_coords(&win_x, &win_y);
1419
1420    cursor_abs_x = (long int)world_x;
1421    owner_of_cursor = target_terminal;
1422
1423    draw_cursor(win_x, win_y);
1424
1425    GB_transaction gb_dummy(GLOBAL_gb_main);
1426    updateAwars(new_term_selected);
1427
1428    return ED4_R_OK;
1429}
1430
1431ED4_returncode ED4_cursor::show_clicked_cursor(AW_pos click_xpos, ED4_terminal *target_terminal)
1432{
1433    AW_pos termw_x, termw_y;
1434    target_terminal->calc_world_coords(&termw_x, &termw_y);
1435
1436    ED4_index scr_pos = ED4_ROOT->pixel2pos(click_xpos - termw_x);
1437    return show_cursor_at(target_terminal, scr_pos);
1438}
1439
1440
1441/* --------------------------------------------------------------------------------
1442   ED4_base_position
1443   -------------------------------------------------------------------------------- */
1444
1445ED4_base_position::ED4_base_position()
1446    : calced4term(0)
1447{
1448}
1449
1450ED4_base_position::~ED4_base_position() {
1451    invalidate();
1452}
1453
1454static void ed4_bp_sequence_changed_cb(ED4_species_manager *, AW_CL cl_base_pos) {
1455    ED4_base_position *base_pos = (ED4_base_position*)cl_base_pos;
1456    base_pos->invalidate();
1457}
1458
1459void ED4_base_position::invalidate() {
1460    if (calced4term) {
1461        ED4_species_manager *species_manager = calced4term->get_parent(ED4_L_SPECIES)->to_species_manager();
1462        species_manager->remove_sequence_changed_cb(ed4_bp_sequence_changed_cb, (AW_CL)this); // @@@ removes cb called later -> ED4_base.cxx@INVALID_CB_HANDLING
1463
1464        calced4term = 0;
1465    }
1466}
1467
1468static bool is_gap(char c) { return ED4_is_align_character[safeCharIndex(c)]; }
1469static bool is_consensus_gap(char c) { return ED4_is_align_character[safeCharIndex(c)] || c == '='; }
1470
1471void ED4_base_position::calc4term(const ED4_terminal *base)
1472{
1473    e4_assert(base);
1474
1475    ED4_species_manager *species_manager = base->get_parent(ED4_L_SPECIES)->to_species_manager();
1476    int                  len;
1477    char                *seq;
1478
1479    if (calced4term) {
1480        ED4_species_manager *prev_species_manager = calced4term->get_parent(ED4_L_SPECIES)->to_species_manager();
1481        prev_species_manager->remove_sequence_changed_cb(ed4_bp_sequence_changed_cb, (AW_CL)this);
1482    }
1483
1484    species_manager->add_sequence_changed_cb(ed4_bp_sequence_changed_cb, (AW_CL)this);
1485
1486    bool (*isGap_fun)(char);
1487    if (species_manager->is_consensus_manager()) {
1488        ED4_group_manager *group_manager = base->get_parent(ED4_L_GROUP)->to_group_manager();
1489
1490        seq       = group_manager->table().build_consensus_string();
1491        len       = strlen(seq);
1492        isGap_fun = is_consensus_gap;
1493    }
1494    else {
1495        seq = base->resolve_pointer_to_string_copy(&len); 
1496        e4_assert((int)strlen(seq) == len);
1497        isGap_fun = is_gap;
1498    }
1499
1500    e4_assert(seq);
1501
1502#if defined(WARN_TODO)
1503#warning ED4_is_align_character is kinda CharPredicate - refactor
1504#endif
1505    CharPredicate pred_is_gap(isGap_fun);
1506    initialize(seq, len, pred_is_gap);
1507    calced4term = base;
1508
1509    free(seq);
1510}
1511int ED4_base_position::get_base_position(const ED4_terminal *term, int sequence_position) {
1512    if (!term) return 0;
1513    set_term(term);
1514    return abs_2_rel(sequence_position);
1515}
1516int ED4_base_position::get_sequence_position(const ED4_terminal *term, int base_pos) {
1517    if (!term) return 0;
1518    set_term(term);
1519    return rel_2_abs(base_pos);
1520}
1521
1522/* --------------------------------------------------------------------------------
1523   Store/Restore Cursorpositions
1524   -------------------------------------------------------------------------------- */
1525
1526class CursorPos : virtual Noncopyable {
1527    ED4_terminal *terminal;
1528    int seq_pos;
1529
1530    CursorPos *next;
1531
1532    static CursorPos *head;
1533
1534public:
1535
1536    static void clear()
1537    {
1538        while (head) {
1539            CursorPos *p = head->next;
1540
1541            delete head;
1542            head = p;
1543        }
1544    }
1545    static CursorPos *get_head() { return head; }
1546
1547    CursorPos(ED4_terminal *t, int p)
1548    {
1549        terminal = t;
1550        seq_pos = p;
1551        next = head;
1552        head = this;
1553    }
1554    ~CursorPos() {}
1555
1556    ED4_terminal *get_terminal() const { return terminal; }
1557    int get_seq_pos() const { return seq_pos; }
1558
1559    void moveToEnd()
1560    {
1561        e4_assert(this==head);
1562
1563        if (next) {
1564            CursorPos *p = head = next;
1565
1566            while (p->next) {
1567                p = p->next;
1568            }
1569
1570            p->next = this;
1571            this->next = 0;
1572        }
1573    }
1574};
1575
1576CursorPos *CursorPos::head = 0;
1577
1578void ED4_store_curpos(AW_window *aww, AW_CL /* cd1 */, AW_CL /* cd2 */) {
1579    GB_transaction       dummy(GLOBAL_gb_main);
1580    ED4_LocalWinContext  uses(aww);
1581    ED4_cursor          *cursor = &current_cursor();
1582   
1583    if (!cursor->owner_of_cursor) {
1584        aw_message("First you have to place the cursor.");
1585    }
1586    else {
1587        new CursorPos(cursor->owner_of_cursor, cursor->get_sequence_pos());
1588    }
1589}
1590
1591void ED4_restore_curpos(AW_window *aww, AW_CL /* cd1 */, AW_CL /* cd2 */) {
1592    GB_transaction       dummy(GLOBAL_gb_main);
1593    ED4_LocalWinContext  uses(aww);
1594    ED4_cursor          *cursor = &current_cursor();
1595
1596    CursorPos *pos = CursorPos::get_head();
1597    if (!pos) {
1598        aw_message("No cursor position stored.");
1599        return;
1600    }
1601
1602    pos->get_terminal()->setCursorTo(cursor, pos->get_seq_pos(), true, ED4_JUMP_KEEP_VISIBLE);
1603    pos->moveToEnd();
1604}
1605
1606void ED4_clear_stored_curpos(AW_window * /* aww */, AW_CL /* cd1 */, AW_CL /* cd2 */)
1607{
1608    CursorPos::clear();
1609}
1610
1611/* --------------------------------------------------------------------------------
1612   Other stuff
1613   -------------------------------------------------------------------------------- */
1614
1615void ED4_helix_jump_opposite(AW_window *aww, AW_CL /* cd1 */, AW_CL /* cd2 */) {
1616    GB_transaction       dummy(GLOBAL_gb_main);
1617    ED4_LocalWinContext  uses(aww);
1618    ED4_cursor          *cursor = &current_cursor();
1619
1620    if (!cursor->owner_of_cursor) {
1621        aw_message("First you have to place the cursor.");
1622        return;
1623    }
1624
1625    int           seq_pos  = cursor->get_sequence_pos();
1626    AW_helix     *helix    = ED4_ROOT->helix;
1627    BI_PAIR_TYPE  pairType = helix->pairtype(seq_pos);
1628
1629    if (pairType != HELIX_NONE) {
1630        int pairing_pos = helix->opposite_position(seq_pos);
1631        cursor->jump_sequence_pos(pairing_pos, ED4_JUMP_KEEP_POSITION);
1632    }
1633    else {
1634        aw_message("Not at helix position");
1635    }
1636}
1637
1638void ED4_change_cursor(AW_window *aww, AW_CL /* cd1 */, AW_CL /* cd2 */) {
1639    ED4_LocalWinContext uses(aww);
1640    ED4_cursor     *cursor = &current_cursor();
1641    ED4_CursorType  typ    = cursor->getType();
1642
1643    cursor->changeType((ED4_CursorType)((typ+1)%ED4_CURSOR_TYPES));
1644}
1645
1646// --------------------------------------------------------------------------------
1647
1648#ifdef UNIT_TESTS
1649#include <test_unit.h>
1650#include <test_helpers.h>
1651
1652struct test_absrel {
1653    bool torel;
1654    test_absrel(bool r) : torel(r) {}
1655    virtual ~test_absrel() {}
1656
1657    virtual int a2r(int a) const = 0;
1658    virtual int r2a(int r) const = 0;
1659   
1660    virtual int abs_size() const = 0;
1661    virtual int rel_size() const = 0;
1662
1663    int gen(int i) const { return torel ? a2r(i) : r2a(i); }
1664    int get_size() const { return torel ? abs_size()+1 : rel_size(); }
1665
1666    char *genResult() const;
1667};
1668
1669struct membind {
1670    const test_absrel& me;
1671    membind(const test_absrel& ta) : me(ta) {}
1672    int operator()(int i) const { return me.gen(i); }
1673};
1674
1675char *test_absrel::genResult() const {
1676    return collectIntFunResults(membind(*this), 0, get_size()-1, 3, 1, 1);
1677}
1678
1679struct test_ecoli : public test_absrel {
1680    BI_ecoli_ref& eref;
1681    test_ecoli(BI_ecoli_ref& b, bool to_rel) : test_absrel(to_rel), eref(b) {}
1682    int a2r(int a) const OVERRIDE { return eref.abs_2_rel(a); }
1683    int r2a(int r) const OVERRIDE { return eref.rel_2_abs(r); }
1684    int abs_size() const OVERRIDE { return eref.abs_count(); }
1685    int rel_size() const OVERRIDE { return eref.base_count(); }
1686};
1687
1688struct test_basepos : public test_absrel {
1689    BasePosition& bpos;
1690    test_basepos(BasePosition& bpos_, bool to_rel)
1691        : test_absrel(to_rel), bpos(bpos_) {}
1692    int a2r(int a) const OVERRIDE { return bpos.abs_2_rel(a); }
1693    int r2a(int r) const OVERRIDE { return bpos.rel_2_abs(r); }
1694    int abs_size() const OVERRIDE { return bpos.abs_count(); }
1695    int rel_size() const OVERRIDE { return bpos.base_count(); }
1696};
1697
1698#define TEST_ABSREL_EQUALS(tester,expected) do {        \
1699        char *res = tester.genResult();                 \
1700        TEST_EXPECT_EQUAL(res,expected);                \
1701        free(res);                                      \
1702    } while(0)
1703
1704#define TEST_ECOLIREF_EQUALS(data,alen,a2r,r2a) do {            \
1705        BI_ecoli_ref eref;                                      \
1706        eref.init(data,alen);                                   \
1707        TEST_ABSREL_EQUALS(test_ecoli(eref, true), a2r);        \
1708        TEST_ABSREL_EQUALS(test_ecoli(eref, false), r2a);       \
1709    } while(0)
1710
1711
1712#define TEST_BASE_POS_EQUALS(data,a2r,r2a) do {                                 \
1713        {                                                                       \
1714            BasePosition bpos(data, strlen(data), CharPredicate(is_gap));       \
1715            TEST_ABSREL_EQUALS(test_basepos(bpos, true), a2r);                  \
1716            TEST_ABSREL_EQUALS(test_basepos(bpos, false), r2a);                 \
1717        }                                                                       \
1718    } while(0)
1719
1720void TEST_BI_ecoli_ref() {
1721    TEST_ECOLIREF_EQUALS("-.AC-G-T-.", 10,
1722                         "  [0]  0  0  0  1  2  2  3  3  4  4  4  [4]", // abs -> rel
1723                         "  [0]  2  3  5  7  [7]");                     // rel -> abs
1724
1725    TEST_ECOLIREF_EQUALS("-",   1, "  [0]  0  0  [0]",       "  [0]  [0]");
1726    TEST_ECOLIREF_EQUALS("--A", 1, "  [0]  0  0  [0]",       "  [0]  [0]"); // part of sequence
1727    TEST_ECOLIREF_EQUALS("--",  2, "  [0]  0  0  0  [0]",    "  [0]  [0]");
1728    TEST_ECOLIREF_EQUALS("---", 3, "  [0]  0  0  0  0  [0]", "  [0]  [0]");
1729   
1730    TEST_ECOLIREF_EQUALS("A",   1, "  [0]  0  1  [1]",       "  [0]  0  [0]");
1731    TEST_ECOLIREF_EQUALS("A",   2, "  [0]  0  1  1  [1]",    "  [0]  0  [0]");
1732
1733    TEST_ECOLIREF_EQUALS("A-",  2, "  [0]  0  1  1  [1]",    "  [0]  0  [0]");
1734    TEST_ECOLIREF_EQUALS("-A",  2, "  [0]  0  0  1  [1]",    "  [0]  1  [1]");
1735
1736    TEST_ECOLIREF_EQUALS("A",   3, "  [0]  0  1  1  1  [1]", "  [0]  0  [0]"); // more than sequence
1737    TEST_ECOLIREF_EQUALS("A--", 3, "  [0]  0  1  1  1  [1]", "  [0]  0  [0]");
1738    TEST_ECOLIREF_EQUALS("-A-", 3, "  [0]  0  0  1  1  [1]", "  [0]  1  [1]");
1739    TEST_ECOLIREF_EQUALS("--A", 3, "  [0]  0  0  0  1  [1]", "  [0]  2  [2]");
1740}
1741
1742void TEST_base_position() {
1743    ED4_init_is_align_character("-"); // count '.' as base
1744
1745    TEST_BASE_POS_EQUALS("-.AC-G-T-.",
1746                         "  [0]  0  0  1  2  3  3  4  4  5  5  6  [6]", // abs -> rel
1747                         "  [0]  1  2  3  5  7  9  [9]");               // rel -> abs
1748
1749    // ------------------------------
1750    ED4_init_is_align_character(".-");
1751
1752    TEST_BASE_POS_EQUALS("-.AC-G-T-.",
1753                         "  [0]  0  0  0  1  2  2  3  3  4  4  4  [4]", // abs -> rel
1754                         "  [0]  2  3  5  7  [7]");                     // rel -> abs
1755
1756    TEST_BASE_POS_EQUALS("-",   "  [0]  0  0  [0]",       "  [0]  [0]");
1757    TEST_BASE_POS_EQUALS("--",  "  [0]  0  0  0  [0]",    "  [0]  [0]");
1758    TEST_BASE_POS_EQUALS("---", "  [0]  0  0  0  0  [0]", "  [0]  [0]");
1759
1760    TEST_BASE_POS_EQUALS("A",   "  [0]  0  1  [1]",       "  [0]  0  [0]");
1761
1762    TEST_BASE_POS_EQUALS("A-",  "  [0]  0  1  1  [1]",    "  [0]  0  [0]");
1763    TEST_BASE_POS_EQUALS("-A",  "  [0]  0  0  1  [1]",    "  [0]  1  [1]");
1764   
1765    TEST_BASE_POS_EQUALS("A--", "  [0]  0  1  1  1  [1]", "  [0]  0  [0]");
1766    TEST_BASE_POS_EQUALS("-A-", "  [0]  0  0  1  1  [1]", "  [0]  1  [1]");
1767    TEST_BASE_POS_EQUALS("--A", "  [0]  0  0  0  1  [1]", "  [0]  2  [2]");
1768}
1769
1770#endif // UNIT_TESTS
1771
1772
Note: See TracBrowser for help on using the repository browser.