source: tags/ms_r16q3/EDIT4/ED4_cursor.cxx

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