source: trunk/EDIT4/ED4_edit_string.cxx

Last change on this file was 19393, checked in by westram, 17 months ago
  • reintegrates 'ali' into 'trunk'
    • refactored misused enum
    • support for helix pairs:
      • drop non-standard defs
      • add more user defs
    • change defaults
    • add several predefined configs (esp. for IUPAC ambiguity codes)
  • adds: log:branches/ali@19376:19392
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 44.5 KB
Line 
1#include <arbdbt.h>
2#include <AW_helix.hxx>
3
4#include <fast_aligner.hxx>
5#include <awt_map_key.hxx>
6
7#include "ed4_edit_string.hxx"
8#include "ed4_class.hxx"
9#include "ed4_awars.hxx"
10#include "ed4_seq_colors.hxx"
11
12#include <aw_awar.hxx>
13#include <aw_msg.hxx>
14#include <aw_root.hxx>
15#include <aw_question.hxx>
16#include <aw_advice.hxx>
17#include <cctype>
18
19#define SAFE_EDITING        // undef this to remove sequence test code
20
21int ED4_Edit_String::nrepeat                = 0;    // # of times command should be repeated
22int ED4_Edit_String::nrepeat_zero_requested = 0;    // nrepeat should be set to zero
23int ED4_Edit_String::nrepeat_is_already_set = 0;    // nrepeat was zero (and was set to 1)
24
25GB_ERROR ED4_Edit_String::insert(char *text, long position, int direction, int removeAtNextGap) {
26    long i;
27    int text_len = strlen(text);
28    if (text_len == 0) return NULp;
29
30    int rest_len;
31
32    if (direction>=0) {
33        rest_len = seq_len-position;
34    }
35    else {
36        rest_len = position;
37    }
38
39    if (rest_len<=0 || text_len>rest_len) {
40        return GBS_global_string("You can't insert text after the end of the sequence!");
41    }
42
43    if (removeAtNextGap) {
44        int nextGap = get_next_gap(position, direction);
45        if (nextGap==-1) {
46            goto no_gaps;
47        }
48
49        int afterGap = nextGap+direction;
50
51        if (afterGap>=0 && afterGap<seq_len) { // in sequence
52            if (seq[afterGap]!=seq[nextGap]) { // gap type changed -> warn to avoid overwrite of '?'
53                return GBS_global_string("Only one gap %s text => insert aborted", direction>=0 ? "after" : "before");
54            }
55        }
56
57        rest_len = (afterGap-position)*direction;
58    }
59
60    if (direction>=0) {
61        if (text_len + position >= seq_len) {
62            return GBS_global_string("You cannot insert that many characters after cursor!");
63        }
64
65        for (i = rest_len-text_len; i<rest_len; i++) {
66            if (!ED4_is_gap_character(seq[position+i])) {
67                goto no_gaps;
68            }
69        }
70
71        for (i = position+rest_len-text_len-1; i>=position; i--) {
72            seq[i+text_len] = seq[i];
73        }
74
75        for (i = 0; i<text_len; i++) {
76            seq[position+i] = text[i];
77        }
78    }
79    else {
80        if (position - text_len < 0) {
81            return GBS_global_string("You cannot insert that many characters before cursor!");
82        }
83
84        for (i = 0; i<text_len; i++) {
85            if (i<0 || !ED4_is_gap_character(seq[i])) {
86                goto no_gaps;
87            }
88        }
89        for (i = 0; i<position-text_len; i++) {
90            seq[i] = seq[i+text_len];
91        }
92
93        for (i = 0; i<text_len; i++) {
94            seq[position-i-1] = text[i];
95        }
96    }
97
98    return NULp;
99
100 no_gaps :
101    return GBS_global_string("There are no/not enough gaps at %s of sequence => can't insert characters%s",
102                             direction >= 0 ? "end" : "start",
103                             direction >= 0 ? "\nMaybe your sequences are not formatted?" : "");
104}
105
106GB_ERROR ED4_Edit_String::remove(int len, long position, int direction, int insertAtNextGap) {
107    int rest_len; // bases between cursor-position and the directed sequence end
108
109    if (direction>=0) { // forward
110        if ((len + position) >= seq_len) len = (int)(seq_len-position);
111        rest_len = seq_len-position;
112    }
113    else { // backward
114        position--;
115        if (len>position) len = position;
116        rest_len = position;
117    }
118
119    if ((position >= seq_len) || (len == 0) || (seq_len - len < 0)) {
120        return GBS_global_string("Illegal delete position/length");
121    }
122
123    if (insertAtNextGap) {
124        int nextGap = get_next_gap(position+len*direction, direction);
125
126        if (nextGap!=-1) {
127            rest_len = (nextGap-position)*direction;
128        }
129    }
130
131    int new_len = rest_len-len; // no of bases that will be copied
132    int i;
133
134    if (direction>=0) {
135        for (i=0; i<new_len; i++) {
136            seq[position+i] = seq[position+len+i];
137        }
138        for (; i<rest_len; i++) {
139            seq[position+i] = '.';
140        }
141    }
142    else {
143        for (i=0; i<new_len; i++) {
144            seq[position-i] = seq[position-len-i];
145        }
146        for (; i<rest_len; i++) {
147            seq[position-i] = '.';
148        }
149    }
150
151    e4_assert(seq[seq_len]=='\0');
152
153    return NULp;
154}
155
156GB_ERROR ED4_Edit_String::replace(char *text, long position, int direction) {
157    int text_len = strlen(text);
158    int i;
159    if (direction>=0) {
160
161        if ((position + text_len > seq_len) || (position > seq_len)) {
162            return GBS_global_string("Replace after end of sequence !");
163        }
164        for (i = 0; i < text_len;   i ++) {
165            seq[i+position] = text[i];
166        }
167    }
168    else {
169        if ((position - text_len < 0) || (position > seq_len)) {
170            return GBS_global_string("Replace before start of sequence !");
171        }
172        for (i = 0; i < text_len;   i ++) {
173            seq[position - i - 1] = text[i];
174        }
175    }
176    return NULp;
177}
178
179GB_ERROR ED4_Edit_String::swap_gaps(long position, char ch) {
180    long i;
181    for (i = position; i < seq_len; i++) {
182        if (!ED4_is_gap_character(seq[i])) break;
183        seq[i] = ch;
184    }
185    for (i = position; i >= 0; i--) {
186        if (!ED4_is_gap_character(seq[i])) break;
187        seq[i] = ch;
188    }
189    return NULp;
190}
191
192
193GB_ERROR ED4_Edit_String::moveBase(long source_position, long dest_position, unsigned char gap_to_use) {
194    e4_assert(source_position!=dest_position);
195#ifdef SAFE_EDITING
196    if (!legal_seqpos(dest_position) || !legal_seqpos(source_position)) {
197        return "Position out of sequence";
198    }
199#endif
200
201    int direction = dest_position<source_position ? -1 : 1;
202    int base = seq[source_position];
203    e4_assert(!ED4_is_gap_character(base));
204    seq[source_position] = gap_to_use;
205
206    long p = source_position+direction;
207    while (p!=dest_position) {
208#ifdef SAFE_EDITING
209        if (!ED4_is_gap_character(seq[p])) {
210            e4_assert(0);
211            return "Internal error: Tried to change base order in moveBase()";
212        }
213#endif
214        e4_assert(ED4_is_gap_character(seq[p]));
215        seq[p] = gap_to_use;
216        p += direction;
217    }
218
219    seq[dest_position] = base;
220    return NULp;
221}
222
223GB_ERROR ED4_Edit_String::shiftBases(long source_pos, long last_source, long dest_pos, int direction, long *last_dest, unsigned char gap_to_use) {
224    // shifts bases from source_pos-last_source to dest_pos
225    // last_dest is set to the position after the last dest_pos (direction<0 ? pos. right of bases : pos. left of bases)
226    GB_ERROR err = NULp;
227
228    if (direction<0) {
229        e4_assert(dest_pos<source_pos);
230        e4_assert(source_pos<=last_source);
231        while (1) {
232            err = moveBase(source_pos, dest_pos, gap_to_use);
233            if (err || source_pos>=last_source) break;
234            source_pos++;
235            dest_pos++;
236            while (!ED4_is_gap_character(seq[dest_pos])) {
237                dest_pos++;
238            }
239        }
240    }
241    else {
242        e4_assert(source_pos<dest_pos);
243        e4_assert(last_source<=source_pos);
244        while (1) {
245            err = moveBase(source_pos, dest_pos, gap_to_use);
246            if (err || source_pos<=last_source) break;
247            source_pos--;
248            dest_pos--;
249            while (!ED4_is_gap_character(seq[dest_pos])) {
250                dest_pos--;
251            }
252        }
253    }
254
255    if (last_dest) {
256        *last_dest = dest_pos;
257    }
258
259    return err;
260}
261
262long ED4_Edit_String::get_next_base(long position, int direction) {
263    long pos;
264    if (direction < 0) position--;
265    for (pos = position; pos>=0 && pos < seq_len; pos += direction) {
266        if (!ED4_is_gap_character(seq[pos])) break;
267    }
268
269    return pos<0 || pos>=seq_len ? -1 : pos;
270}
271long ED4_Edit_String::get_next_gap(long position, int direction) {
272    long pos;
273    if (direction < 0) position--;
274    for (pos = position; pos >= 0 && pos < seq_len; pos += direction) {
275        if (ED4_is_gap_character(seq[pos])) break;
276    }
277
278    return pos<0 || pos>=seq_len ? -1 : pos;
279}
280long ED4_Edit_String::get_next_visible_base(long position, int direction) {
281    long pos;
282    if (direction < 0) position--;
283    for (pos = position; pos>=0 && pos < seq_len; pos += direction) {
284        if (!ED4_is_gap_character(seq[pos]) && remap->is_shown(pos)) {
285            break;
286        }
287    }
288
289    return pos<0 || pos>=seq_len ? -1 : pos;
290}
291long ED4_Edit_String::get_next_visible_gap(long position, int direction) {
292    long pos;
293    if (direction < 0) position--;
294    for (pos = position; pos >= 0 && pos < seq_len; pos += direction) {
295        if (ED4_is_gap_character(seq[pos]) && remap->is_shown(pos)) {
296            break;
297        }
298    }
299
300    return pos<0 || pos>=seq_len ? -1 : pos;
301}
302long ED4_Edit_String::get_next_visible_pos(long position, int direction) {
303    long pos;
304    if (direction < 0) position--;
305    for (pos = position; pos >= 0 && pos < seq_len; pos += direction) {
306        if (remap->is_shown(pos)) {
307            break;
308        }
309    }
310    return pos<0 || pos>=seq_len ? -1 : pos;
311}
312
313unsigned char ED4_Edit_String::get_gap_type(long pos, int direction) {
314    pos += direction;
315    if (!legal_seqpos(pos)) return '.';
316    char gap = seq[pos];
317    if (ED4_is_gap_character(gap)) return gap;
318    return '-';
319}
320
321static GB_ERROR toggle_cursor_group_folding() {
322    GB_ERROR      error  = NULp;
323    ED4_cursor&   cursor = current_cursor();
324    if (cursor.owner_of_cursor) {
325        ED4_terminal *cursor_term = cursor.owner_of_cursor;
326
327        if (cursor_term->is_in_folded_group()) {
328            // cursor not visible -> unfold group which hides cursor
329            cursor_term->setCursorTo(&cursor, cursor.get_sequence_pos(), true, ED4_JUMP_KEEP_POSITION);
330        }
331        else {
332            ED4_base *group = cursor.owner_of_cursor->get_parent(LEV_GROUP);
333            if (group) {
334                ED4_group_manager *group_man = group->to_group_manager();
335                if (group_man->has_property(PROP_IS_FOLDED)) {
336                    group_man->unfold();
337                }
338                else {
339                    ED4_species_manager *consensus_man = group_man->get_multi_species_manager()->get_consensus_manager();
340                    if (consensus_man) consensus_man->setCursorTo(&cursor, cursor.get_sequence_pos(), true, ED4_JUMP_KEEP_POSITION);
341
342                    group_man->fold();
343                }
344            }
345        }
346    }
347    return error;
348}
349
350static void toggle_mark_of_specData(GBDATA *gb_data) {
351    // toggle mark of species owning data 'gb_data'
352    GB_transaction  ta(gb_data);
353    GBDATA         *gb_species = GB_get_grandfather(gb_data);
354    if (gb_species) GB_write_flag(gb_species, !GB_read_flag(gb_species));
355}
356
357GB_ERROR ED4_Edit_String::command(AW_key_mod keymod, AW_key_code keycode, char key, int direction, ED4_EDITMODE mode, bool is_consensus,
358                                  long &seq_pos, bool &changed_flag, ED4_CursorJumpType& cursor_jump, bool &cannot_handle, bool &write_fault, GBDATA* gb_data, bool is_sequence)
359{
360    changed_flag = 0;
361    write_fault  = 0;
362    if (keymod+keycode+key==0) return NULp;
363
364    e4_assert(nrepeat>0);
365
366    long old_seq_pos = seq_pos;
367    int  screen_len  = remap->sequence_to_screen(seq_len);
368    int  cursorpos   = remap->sequence_to_screen(seq_pos);
369
370    char str[2];
371    str[0] = key;
372    str[1] = 0;
373
374    direction = direction>0 ? 1 : -1;
375
376    if ((cursorpos > screen_len) || (cursorpos < 0)) {
377        if (cursorpos<MAXSEQUENCECHARACTERLENGTH && cursorpos>=0) {
378            char *seq2 = new char[MAXSEQUENCECHARACTERLENGTH+1];
379
380            memcpy(seq2, seq, seq_len);
381            memset(seq2+seq_len, '.', MAXSEQUENCECHARACTERLENGTH-seq_len);
382            seq_len = MAXSEQUENCECHARACTERLENGTH;
383            seq2[seq_len] = 0;
384
385            delete seq;
386            seq = seq2;
387
388            changed_flag = 1;
389        }
390        else {
391            return GBS_global_string("Cursor out of sequence at screen_pos #%i!", cursorpos);
392        }
393    }
394
395    GB_ERROR ad_err          = NULp;
396    long     h;
397    bool     reinterpret_key = true;
398
399    while (reinterpret_key) {
400        reinterpret_key = false;
401
402        switch (keycode) {
403            case AW_KEY_HOME: {
404                int new_seq_pos = get_next_visible_base(0, 1);
405                if (new_seq_pos>=0) seq_pos = new_seq_pos==seq_pos ? 0 : new_seq_pos;
406                else seq_pos = 0;
407                break;
408            }
409            case AW_KEY_END: {
410                int new_seq_pos = get_next_visible_base(seq_len, -1);
411                if (new_seq_pos>=0) {
412                    int new_gap_pos = get_next_visible_gap(new_seq_pos, 1);
413                    if (new_gap_pos >= 0) {
414                        seq_pos = new_gap_pos==seq_pos ? seq_len : new_gap_pos;
415                    }
416                    else {
417                        seq_pos = seq_len;
418                    }
419                }
420                else {
421                    seq_pos = seq_len;
422                }
423                break;
424            }
425            case AW_KEY_LEFT:
426            case AW_KEY_RIGHT: {
427                direction = keycode==AW_KEY_RIGHT ? 1 : -1;
428
429                // no key modifier -> just move cursor
430
431                int n = nrepeat;
432
433                if (keymod == AW_KEYMODE_NONE) {
434                    while (n--) {
435                        do {
436                            seq_pos += direction;
437                        }
438                        while (legal_curpos(seq_pos) && !remap->is_shown(seq_pos));
439                    }
440                    break;
441                }
442
443                if (mode==AD_NOWRITE) { write_fault = 1; break; }
444
445                int jump_or_fetch = 0;  // 1=jump selected 2=fetch selected (for repeat only)
446                int push_or_pull = 0;   // 1=push selected 2=pull selected (for repeat only)
447
448                // ------------------
449                // loop over nrepeat:
450
451                while (!ad_err && n-->0 && legal_curpos(seq_pos)) {
452                    cursorpos = remap->sequence_to_screen(seq_pos);
453
454                    int adjacent_scr_pos = cursorpos - (direction<0); // screen position next to the cursor
455                    if (adjacent_scr_pos<0 || size_t(adjacent_scr_pos)>remap->get_max_screen_pos()) {
456                        ad_err = GBS_global_string("Action beyond end of screen!");
457                        break;
458                    }
459
460                    int adjacent_seq_pos = remap->screen_to_sequence(adjacent_scr_pos); // _visible_ sequence position next to the cursor
461                    int real_adjacent_seq_pos = seq_pos - (direction<0); // sequence position next to cursor (not necessarily visible)
462
463                    if (adjacent_seq_pos<0 || adjacent_seq_pos>=seq_len) {
464                        ad_err = GBS_global_string("Action beyond end of sequence!");
465                        break;
466                    }
467
468                    // Ctrl+Cursor = move cursor to next end of word (or to end of next word)
469
470                    if (keymod & AW_KEYMODE_CONTROL) {
471                        if (adjacent_scr_pos>=0) {
472                            long pos = adjacent_seq_pos;
473
474                            if (ED4_ROOT->aw_root->awar(ED4_AWAR_FAST_CURSOR_JUMP)->read_int()) { // should cursor jump over next group?
475                                if (ED4_is_gap_character(seq[pos])) {
476                                    pos = get_next_visible_base(pos, direction);
477                                    if (pos>=0) pos = get_next_visible_gap(pos, direction);
478                                }
479                                else {
480                                    pos = get_next_visible_gap(pos, direction);
481                                }
482
483                                seq_pos = (pos>=0
484                                           ? (direction<0 ? pos+1 : pos)
485                                           : (direction<0 ? 0 : seq_len-1));
486                            }
487                            else {
488                                if (ED4_is_gap_character(seq[pos])) { seq_pos = get_next_visible_base(pos, direction); }
489                                else                                { seq_pos = get_next_visible_gap(pos, direction); }
490
491                                if (direction<0) { seq_pos = seq_pos==-1 ? 0       : seq_pos+1; }
492                                else             { seq_pos = seq_pos==-1 ? seq_len : seq_pos; }
493                            }
494                        }
495                        continue;
496                    }
497
498                    // ALT/META+Cursor = jump & fetch
499
500                    if (keymod & (AW_KEYMODE_ALT)) {
501                        if (is_consensus) { cannot_handle = 1; return NULp; }
502
503                        if (ED4_is_gap_character(seq[adjacent_seq_pos])) { // there's a _gap_ next to the cursor -> let's fetch
504                            if (jump_or_fetch!=1) {
505                                jump_or_fetch = 2;
506                                long source_pos = get_next_base(adjacent_seq_pos, direction); // position of base to fetch
507                                if (source_pos==-1) { // there is nothing to fetch
508                                    n = 0;
509                                }
510                                else {
511                                    ad_err = moveBase(source_pos, adjacent_seq_pos, get_gap_type(source_pos, direction));
512                                    seq_pos = adjacent_seq_pos + (direction>0);
513                                }
514                            }
515                            else {
516                                n = 0;
517                            }
518                        }
519                        else { // there's a _base_ next to the cursor -> let it jump
520                            if (jump_or_fetch!=2) {
521                                jump_or_fetch = 1;
522                                int next_gap = adjacent_seq_pos - direction;
523
524                                if (ED4_is_gap_character(seq[next_gap])) {
525                                    int dest_pos = get_next_base(next_gap, -direction);
526
527                                    if (dest_pos<0) {
528                                        dest_pos = direction>0 ? 0 : seq_len-1;
529                                    }
530                                    else {
531                                        dest_pos += direction;
532                                    }
533
534                                    if (ED4_is_gap_character(seq[dest_pos])) {
535                                        ad_err = moveBase(adjacent_seq_pos, dest_pos, get_gap_type(adjacent_seq_pos, direction));
536                                        if (!ad_err) {
537                                            seq_pos = get_next_base(seq_pos, direction)+(direction<0);
538                                            if (seq_pos==-1) {
539                                                seq_pos = direction<0 ? 0 : seq_len;
540                                            }
541                                        }
542                                    }
543                                    else {
544                                        e4_assert(0);
545                                    }
546                                }
547                                else {
548                                    ad_err = GBS_global_string("You can only jump single bases.");
549                                }
550                            }
551                            else {
552                                n = 0;
553                            }
554                        }
555
556                        if (!ad_err) {
557                            changed_flag = 1;
558                        }
559
560                        continue;
561                    }
562
563                    // Shift+Cursor = push/pull character
564
565                    if (is_consensus) { cannot_handle = 1; return NULp; };
566
567                    if (ED4_is_gap_character(seq[real_adjacent_seq_pos])) { // pull
568                        long dest_pos = real_adjacent_seq_pos;
569                        long source_pos = real_adjacent_seq_pos-direction;
570
571                        if (!ED4_is_gap_character(seq[source_pos]) && push_or_pull!=1) {
572                            push_or_pull = 2;
573
574                            long next_gap = get_next_gap(source_pos, -direction);
575                            long last_source = next_gap>=0 ? next_gap : (direction>0 ? 0 : seq_len-1);
576
577                            if (ED4_is_gap_character(seq[last_source])) {
578                                last_source = get_next_base(last_source, direction);
579                            }
580
581                            ad_err = shiftBases(source_pos, last_source, dest_pos, direction, NULp,
582                                                is_sequence ? get_gap_type(last_source, -direction) : '.');
583
584                            if (!ad_err) {
585                                seq_pos = dest_pos + (direction>0);
586                                changed_flag = 1;
587                            }
588                        }
589                        else {
590                            n = 0;
591                        }
592                    }
593                    else { // push
594                        long next_gap = get_next_gap(adjacent_seq_pos, direction);
595
596                        if (next_gap>=0 && push_or_pull!=2) {
597                            push_or_pull = 1;
598                            long dest_pos = next_gap;
599                            long source_pos = get_next_base(next_gap, -direction);
600                            long last_source = adjacent_seq_pos;
601
602                            e4_assert(source_pos>=0);
603                            ad_err = shiftBases(source_pos, last_source, dest_pos, direction, &dest_pos,
604                                                is_sequence ? get_gap_type(last_source, -direction) : '.');
605
606                            if (!ad_err) {
607                                seq_pos = dest_pos + (direction<0);
608                                changed_flag = 1;
609                            }
610                        }
611                        else {
612                            n = 0;
613                        }
614                    }
615                }
616                break;
617            }
618
619            case AW_KEY_BACKSPACE:
620                h = seq_pos;
621
622                if (direction>0) {
623                    seq_pos -= nrepeat;
624                }
625                else {
626                    seq_pos += nrepeat;
627                }
628                if (seq_pos <0 || seq_pos >= seq_len) {
629                    seq_pos = h;
630                    break;
631                }
632                // fall-through
633
634            case AW_KEY_DELETE:
635                h = seq_pos;
636
637                if (is_consensus) { cannot_handle = 1; return NULp; };
638                if (keymod) { return NULp; }
639
640                if (mode==AD_NOWRITE) { write_fault = 1; break; }
641
642                switch (mode) {
643                    case AD_ALIGN: {
644                        int len;
645                        int offset;
646
647                        ad_err = NULp;
648                        if (direction>=0)   offset = 0;
649                        else        offset = -nrepeat;
650
651                        for (len = nrepeat-1; len>=0; len--) {
652                            if (!ED4_is_gap_character(seq[h+offset+len])) {
653                                ad_err = GBS_global_string("You cannot remove bases in align mode");
654                                break;
655                            }
656                        }
657                        if (ad_err) break;
658                        FALLTHROUGH;
659                    }
660                    case AD_REPLACE:
661                    case AD_INSERT:
662                        ad_err = remove(nrepeat, h, direction, !is_sequence);
663                        if (!ad_err) {
664                            changed_flag = 1;
665                        }
666                        break;
667                    default:
668                        break;
669                }
670
671                break;
672
673            case AW_KEY_RETURN:
674                ad_err = toggle_cursor_group_folding();
675                break;
676
677            case AW_KEY_ASCII: {
678
679                //      keyboard layout:
680                //
681                //      - CTRL-A    Align                               ok
682                //      - CTRL-D    Toggle view differences             ok
683                //      - CTRL-E    Toggle edit/align                   ok
684                //      - CTRL-I    Toggle insert/replace               ok
685                //      - CTRL-J    Jump opposite helix position        ok
686                //      - CTRL-K    Toggle compression on/off           ok
687                //      - CTRL-L    Refresh                             ok
688                //      - CTRL-M    Invert mark                         ok
689                //      - CTRL-O    = ALT-LEFT                          ok
690                //      - CTRL-P    = ALT-RIGHT                         ok
691                //      - CTRL-R    Set aligner OR viewDiff reference species       ok
692                //      - CTRL-S    Repeat last search                  ok
693                //      - CTRL-U    Undo                                @@@ crashes! disabled atm!
694
695
696                if (key >0 && key<=26) { // CTRL-Keys
697                    switch (key+'A'-1) {
698                        case 'A': { // CTRL-A = Start Fast-Aligner
699                            AW_window *aw_tmp = current_aww();
700                            if (is_consensus) { cannot_handle = 1; return NULp; };
701                            if (mode==AD_NOWRITE) { write_fault = 1; return NULp; }
702
703                            AlignDataAccess localDataAccess(*ED4_get_aligner_data_access()); // use local modified copy
704                            localDataAccess.do_refresh = false;
705
706                            FastAligner_set_align_current(ED4_ROOT->aw_root, ED4_ROOT->props_db);
707                            AW_clock_cursor(ED4_ROOT->aw_root);
708                            GB_commit_transaction(localDataAccess.gb_main);
709
710                            FastAligner_start(aw_tmp, &localDataAccess);
711
712                            GB_begin_transaction(localDataAccess.gb_main);
713                            AW_normal_cursor(ED4_ROOT->aw_root);
714
715                            int basesLeftOf = 0;
716                            int pos;
717
718                            for (pos=0; pos < seq_pos && pos<seq_len; pos++) {    // count # of bases left of cursorpos
719                                if (!ED4_is_gap_character(seq[pos])) {
720                                    basesLeftOf++;
721                                }
722                            }
723
724                            char *aligned_seq = GB_read_string(gb_data); // read new sequence
725                            ad_err            = GB_write_string(gb_data, seq); // restore old sequence
726
727                            freeset(seq, aligned_seq); // set new sequence
728                            changed_flag = 1;       // and mark changed
729
730                            {
731                                int basesLeftOf2   = 0;
732                                int lastCorrectPos = -1;
733                                for (pos=0; pos<seq_pos && pos<seq_len; pos++) {    // count # of bases left of cursorpos
734                                    if (!ED4_is_gap_character(seq[pos])) {
735                                        basesLeftOf2++;
736                                        if (basesLeftOf2 == basesLeftOf) lastCorrectPos = pos;
737                                    }
738                                }
739
740                                if (basesLeftOf != basesLeftOf2) { // old cursor-position has different basepos
741                                    if (lastCorrectPos != -1) { // already seen position with same basepos
742                                        seq_len = lastCorrectPos+1;
743                                    }
744                                    else {
745                                        for (; pos<seq_len; pos++) {
746                                            if (!ED4_is_gap_character(seq[pos])) {
747                                                basesLeftOf2++;
748                                                if (basesLeftOf2 == basesLeftOf) {
749                                                    seq_pos = pos+1;
750                                                    break; // stop after first matching position
751                                                }
752                                            }
753                                        }
754                                    }
755                                }
756                            }
757                            break;
758                        }
759                        case 'R': {  // CTRL-R = set aligner reference species OR set reference for diff-mode
760                            ED4_reference *ref = ED4_ROOT->reference;
761                            if (ref->is_set()) { // if "view differences" is active = > set new reference
762                                ED4_viewDifferences_setNewReference();
763                            }
764                            else { // otherwise set aligner reference
765                                if (is_consensus) { cannot_handle = 1; return NULp; };
766                                FastAligner_set_reference_species(ED4_ROOT->aw_root);
767                            }
768                            break;
769                        }
770                        case 'D': { // CTRL-D = Toggle view differences
771                            ED4_toggle_viewDifferences(ED4_ROOT->aw_root);
772                            break;
773                        }
774                        case 'E': { // CTRL-E = Toggle Edit/Align-Mode
775                            ED4_ROOT->aw_root->awar(AWAR_EDIT_MODE)->toggle_toggle();
776                            break;
777                        }
778                        case 'I': { // CTRL-I = Toggle Insert/Replace-Mode
779                            ED4_ROOT->aw_root->awar(AWAR_INSERT_MODE)->toggle_toggle();
780                            break;
781                        }
782                        case 'J': { // CTRL-J = Jump to opposite helix position
783                            AW_helix *helix = ED4_ROOT->helix;
784
785                            if (!helix->has_entries()) ad_err = ARB_strdup("Got no helix information");
786                            else if (helix->is_pairpos(seq_pos)) {
787                                seq_pos = helix->opposite_position(seq_pos);
788                                cursor_jump = ED4_JUMP_KEEP_POSITION;
789                            }
790                            else ad_err = ARB_strdup("Not at helix position");
791                            break;
792                        }
793                        case 'K': { // Ctrl-K = Compression on/off
794                            static int last_mode;
795                            int current_mode = (ED4_remap_mode)ED4_ROOT->aw_root->awar(ED4_AWAR_COMPRESS_SEQUENCE_GAPS)->read_int();
796                            int next_mode = last_mode;
797
798                            if (current_mode==0) { // if not compressed
799                                if (last_mode==0) next_mode = 2; // force compress
800                            }
801                            else {  // if compressed
802                                next_mode = 0; // force uncompressed
803                            }
804
805                            ED4_ROOT->aw_root->awar(ED4_AWAR_COMPRESS_SEQUENCE_GAPS)->write_int(next_mode);
806
807                            last_mode = current_mode;
808                            break;
809                        }
810                        case 'L': {  // CTRL-L = Refresh
811                            ED4_request_full_refresh();
812                            ED4_request_relayout();
813                            cursor_jump = ED4_JUMP_CENTERED;
814                            break;
815                        }
816                        case 'M': { // CTRL-M = Invert mark(s)
817                            if (is_consensus) { cannot_handle = 1; return NULp; }
818                            toggle_mark_of_specData(gb_data);
819                            break;
820                        }
821                        case 'O': { //  for ALT-left
822                            keycode = AW_KEY_LEFT;
823                            keymod = AW_KEYMODE_ALT;
824                            reinterpret_key = true;
825                            break;
826                        }
827                        case 'P': { // for ALT-right
828                            keycode = AW_KEY_RIGHT;
829                            keymod = AW_KEYMODE_ALT;
830                            reinterpret_key = true;
831                            break;
832                        }
833                        case 'S': { // CTRL-S = Repeat last search
834                            ad_err      = ED4_repeat_last_search(current_ed4w());
835                            seq_pos     = current_cursor().get_sequence_pos();
836                            cursor_jump = ED4_JUMP_KEEP_POSITION;
837                            break;
838                        }
839                        case 'U': {
840                            // ad_err = GB_undo(gb_main, GB_UNDO_UNDO); // @@@ stuerzt ab - wahrscheinlich weil Transaktion offen ist
841                            break;
842                        }
843                    }
844                }
845                else { // normal characters
846                    if (is_consensus) { cannot_handle = 1; return NULp; }
847                    if (mode==AD_NOWRITE) { write_fault = 1; break; }
848
849                    if (key == ' ') {
850                        if (is_sequence) {
851                            long left = seq_pos>0 ? seq_pos-1 : 0;
852                            int  l    = seq[left];
853                            int  r    = seq[seq_pos];
854
855                            char gapchar_at_pos = ED4_is_gap_character(l) ? l : (ED4_is_gap_character(r) ? r : 0);
856
857                            switch (keymod) {
858                                case AW_KEYMODE_NONE:
859                                    key = gapchar_at_pos ? gapchar_at_pos : '-'; // insert (same) gap
860                                    break;
861
862                                case AW_KEYMODE_CONTROL:
863                                    if (gapchar_at_pos) key = gapchar_at_pos == '-' ? '.' : '-'; // toggle gaptype
864                                    break;
865
866                                default: break;
867                            }
868                        }
869                        str[0] = key;
870                    }
871
872                    if (ED4_is_gap_character(key) && keymod == AW_KEYMODE_CONTROL) { // gap-type change works in all modes
873                        // gap type functions ('.' <-> '-')
874
875                        long left = seq_pos>0 ? seq_pos-1 : 0;
876                        int  l    = seq[left];
877                        int  r    = seq[seq_pos];
878
879                        if      (ED4_is_gap_character(l) && l!=key) { ad_err = swap_gaps(left, key);    changed_flag = 1; }
880                        else if (ED4_is_gap_character(r) && r!=key) { ad_err = swap_gaps(seq_pos, key); changed_flag = 1; }
881                    }
882                    else {
883                        switch (mode) {
884                            case AD_ALIGN: {
885                                if (ED4_is_gap_character(key)) {
886                                    if (keymod == AW_KEYMODE_NONE) {
887                                        if (!ad_err) {
888                                            char *nstr = ARB_calloc<char>(nrepeat+1);
889
890                                            for (int i = 0; i< nrepeat; i++) nstr[i] = key;
891                                            ad_err = insert(nstr, seq_pos, direction, 0);
892                                            if (!ad_err) seq_pos = get_next_visible_pos(seq_pos+(direction>=0 ? nrepeat : 0), direction);
893                                            delete nstr;
894                                        }
895                                        changed_flag = 1;
896                                    }
897                                }
898                                else { // check typed bases against sequence
899                                    while (nrepeat && !ad_err) {
900                                        nrepeat--;
901                                        seq_pos = get_next_visible_base(seq_pos, direction);
902                                        if (seq_pos<0 || seq_pos>=seq_len) {
903                                            ad_err = GBS_global_string("End of sequence reached");
904                                        }
905                                        else if (seq[seq_pos]!=key) {
906                                            ad_err = GBS_global_string("Base '%c' at %li does not match '%c'", seq[seq_pos], seq_pos, key);
907                                        }
908                                        else {
909                                            seq_pos = get_next_visible_pos(seq_pos, direction)+1;
910                                            ad_err = NULp;
911                                        }
912                                        if (ad_err) {
913                                            ed4_beep();
914                                        }
915                                    }
916                                }
917                                break;
918                            }
919                            case AD_REPLACE: {
920                                while (nrepeat && !ad_err) {
921                                    nrepeat--;
922                                    if (direction>0) {
923                                        ad_err = replace(str, seq_pos, 1);
924
925                                        if (!ad_err) {
926                                            seq_pos++;
927                                            changed_flag = 1;
928                                        }
929                                    }
930                                    else {
931                                        int left_pos = get_next_visible_pos(seq_pos, -1);
932
933                                        if (left_pos>=0)    ad_err = replace(str, left_pos+1, -1);
934                                        else            ad_err = "End of sequence";
935
936                                        if (!ad_err) {
937                                            seq_pos = left_pos;
938                                            changed_flag = 1;
939                                        }
940                                    }
941                                }
942                                break;
943                            }
944                            case AD_INSERT: {
945                                while (nrepeat && !ad_err) {
946                                    nrepeat--;
947                                    if (direction>0) {
948                                        ad_err = insert(str, seq_pos, 1, !is_sequence);
949
950                                        if (!ad_err) {
951                                            seq_pos++;
952                                            changed_flag = 1;
953                                        }
954                                    }
955                                    else {
956                                        int left_pos = get_next_visible_pos(seq_pos, -1);
957
958                                        if (left_pos>=0) {
959                                            ad_err = insert(str, left_pos+1, -1, !is_sequence);
960                                        }
961                                        else {
962                                            ad_err = "End of sequence";
963                                        }
964
965                                        if (!ad_err) {
966                                            seq_pos = left_pos;
967                                            changed_flag = 1;
968                                        }
969                                    }
970                                }
971                                break;
972                            }
973                            default: {
974                                break;
975                            }
976                        }
977                    }
978
979                }
980                break;
981            }
982            default: {
983                break;
984            }
985
986        }
987    }
988
989    if (ad_err) seq_pos = old_seq_pos;
990    seq_pos = seq_pos<0 ? 0 : (seq_pos>seq_len ? seq_len : seq_pos);    // ensure legal position
991
992    return ad_err;
993}
994
995ED4_Edit_String::ED4_Edit_String() :
996    gbd(NULp),
997    seq(NULp),
998    seq_len(-1),
999    old_seq(NULp),
1000    old_seq_len(-1),
1001    remap(ED4_ROOT->root_group_man->remap())
1002{
1003}
1004
1005ED4_Edit_String::~ED4_Edit_String() {
1006    // @@@ does member 'seq' leak?
1007    free(old_seq);
1008}
1009
1010GB_ERROR ED4_Edit_String::edit(ED4_work_info *info) {
1011    e4_assert(info->working_terminal);
1012
1013    if (!info->rightward) { // 5'<-3'
1014        info->char_position++;
1015    }
1016
1017    info->out_seq_position = remap->screen_to_sequence(info->char_position);
1018    info->refresh_needed   = false;
1019    info->cursor_jump      = ED4_JUMP_KEEP_VISIBLE;
1020    info->cannot_handle    = false;
1021    old_seq                = NULp;
1022
1023    if (info->string) {
1024        seq = info->string;
1025        seq_len = strlen(seq);
1026    }
1027    else {
1028        e4_assert(info->gb_data);
1029        int seq_len_int;
1030        seq = info->working_terminal->resolve_pointer_to_string_copy(&seq_len_int);
1031        seq_len = seq_len_int;
1032    }
1033
1034    int map_key;
1035    if (info->event.keymodifier == AW_KEYMODE_NONE) {
1036        map_key = ED4_ROOT->edk->map_key(info->event.character);
1037        if (map_key != info->event.character) { // remapped
1038            if (info->event.character == ' ') { // space
1039                AW_advice("You have keymapped the space-key!\n"
1040                          "The new functionality assigned to space-key is not available when\n"
1041                          "key-mapping is enabled.\n",
1042                          AW_ADVICE_TOGGLE_AND_HELP, "Obsolete default keymapping", "nekey_map.hlp");
1043            }
1044        }
1045    }
1046    else {
1047        map_key = info->event.character;
1048    }
1049    GB_ERROR err = NULp;
1050
1051    nrepeat_zero_requested = 1;
1052    if (!nrepeat) {
1053        nrepeat_is_already_set = 1;
1054        nrepeat = 1;
1055    }
1056
1057    if (info->event.keycode==AW_KEY_ASCII &&
1058        isdigit(map_key) &&
1059        ED4_ROOT->aw_root->awar(ED4_AWAR_DIGITS_AS_REPEAT)->read_int()) { // handle digits for repeated commands
1060
1061        if (nrepeat_is_already_set) { nrepeat = 0; }
1062        nrepeat_zero_requested = 0;
1063
1064        for (int r=0; r<info->repeat_count; r++) {
1065            nrepeat = nrepeat * 10 + (map_key - '0');
1066            if (nrepeat>10000) {
1067                nrepeat = 10000;
1068            }
1069            else if (nrepeat<0) {
1070                nrepeat = 0;
1071            }
1072        }
1073#ifdef DEBUG
1074        if (nrepeat>1) {
1075            printf("nrepeat=%i\n", nrepeat);
1076        }
1077#endif
1078    }
1079    else if (info->event.keymodifier==AW_KEYMODE_NONE && info->event.keycode==AW_KEY_NONE) {
1080        nrepeat_zero_requested = 0;
1081    }
1082    else {
1083        nrepeat += info->repeat_count-1;
1084
1085#ifdef DEBUG
1086        if (nrepeat>1) {
1087            printf("nrepeat is %i\n", nrepeat);
1088        }
1089#endif
1090
1091        int is_remapped_sequence = !info->is_sequence && ED4_ROOT->aw_root->awar(ED4_AWAR_COMPRESS_SEQUENCE_TYPE)->read_int()!=ED4_RM_NONE;
1092        if (info->mode==AD_NOWRITE_IF_COMPRESSED) {
1093            if (is_remapped_sequence) {
1094                info->mode = AD_NOWRITE;
1095            }
1096            else {
1097                info->mode = AD_INSERT;
1098            }
1099        }
1100
1101        bool write_fault = 0;
1102
1103        err = command(info->event.keymodifier,
1104                      info->event.keycode,
1105                      map_key,
1106                      info->rightward,
1107                      info->mode,
1108                      bool(info->string),
1109                      info->out_seq_position,
1110                      info->refresh_needed,
1111                      info->cursor_jump,
1112                      info->cannot_handle,
1113                      write_fault,
1114                      info->gb_data,
1115                      info->is_sequence);
1116
1117        e4_assert(!(err && info->cannot_handle));
1118
1119        if (write_fault) {
1120            e4_assert(info->mode==AD_NOWRITE);
1121            aw_message(is_remapped_sequence
1122                       ? "Check 'Show all gaps' when editing remarks"
1123                       : "This terminal is write-protected!");
1124        }
1125    }
1126
1127    if (!err) {
1128        if (info->gb_data) {
1129            if (info->refresh_needed) {
1130                e4_assert(info->working_terminal->get_species_pointer() == info->gb_data);
1131
1132                int old_seq_len_int;
1133                old_seq     = info->working_terminal->resolve_pointer_to_string_copy(&old_seq_len_int);
1134                old_seq_len = old_seq_len_int;
1135
1136                err = info->working_terminal->write_sequence(seq, seq_len);
1137                if (err) {
1138                    info->out_seq_position = remap->screen_to_sequence(info->char_position);    // correct cursor_pos if protection error occurred
1139                }
1140            }
1141            freenull(seq);
1142        }
1143    }
1144
1145    if (!info->rightward) {
1146        info->char_position    = remap->sequence_to_screen_PLAIN(info->out_seq_position);
1147        e4_assert(info->char_position >= 0);
1148        info->char_position--;
1149        info->out_seq_position = remap->screen_to_sequence(info->char_position);
1150    }
1151
1152    return err;
1153}
1154
1155void ED4_Edit_String::finish_edit() {
1156    nrepeat_is_already_set = 0;
1157    if (nrepeat_zero_requested) {
1158        nrepeat_zero_requested = 0;
1159        nrepeat = 0;
1160    }
1161}
1162
Note: See TracBrowser for help on using the repository browser.