source: branches/help/EDIT4/ED4_block.cxx

Last change on this file was 18730, checked in by westram, 3 years ago
  • remove trailing whitespace from c source.
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 38.4 KB
Line 
1#include "ed4_awars.hxx"
2#include "ed4_class.hxx"
3#include "ed4_tools.hxx"
4#include "ed4_block.hxx"
5#include "ed4_edit_string.hxx"
6#include "ed4_list.hxx"
7
8#include <awt_sel_boxes.hxx>
9#include <fast_aligner.hxx>
10
11#include <aw_awars.hxx>
12#include <aw_msg.hxx>
13#include <aw_root.hxx>
14#include <aw_select.hxx>
15
16#include <arbdbt.h>
17#include <gb_aci.h>
18
19#include <climits>
20#include <cctype>
21#include <map>
22
23using namespace std;
24
25// --------------------------------------------------------------------------------
26
27class ED4_block : virtual Noncopyable {
28    // stores columnrange of selected region
29    // (linerange is stored in EDIT4 marks)
30    ED4_blocktype type;
31    bool          columnBlockUsed;
32    PosRange      range;
33
34public:
35    ED4_block()
36        : type(ED4_BT_NOBLOCK),
37          columnBlockUsed(false)
38    {}
39
40    ED4_blocktype get_type() const { return type; }
41    void set_type(ED4_blocktype bt);
42    void toggle_type();
43    void autocorrect_type();
44
45    const PosRange& get_colblock_range() const {
46        e4_assert(type == ED4_BT_COLUMNBLOCK || type == ED4_BT_MODIFIED_COLUMNBLOCK); // otherwise range may be undefined
47        return range;
48    }
49    void set_range(const PosRange& new_range) { range = new_range; }
50
51    PosRange get_range_according_to_type() {
52        PosRange res;
53
54        switch (type) {
55            case ED4_BT_NOBLOCK:
56                res = PosRange::empty();
57                break;
58
59            case ED4_BT_COLUMNBLOCK:
60            case ED4_BT_MODIFIED_COLUMNBLOCK:
61                res = get_colblock_range();
62                break;
63
64            case ED4_BT_LINEBLOCK:
65                res = PosRange::whole();
66                break;
67        }
68
69        return res;
70    }
71};
72
73static ED4_block block;
74
75// --------------------------------------------------------------------------------
76
77class SeqPart {
78    const char *seq;
79
80    int offset;
81    int len; // of part
82
83    static char to_gap(char c) { return ED4_is_gap_character(c) ? c : 0; }
84
85public:
86    SeqPart(const char *seq_, int offset_, int len_)
87        : seq(seq_),
88          offset(offset_),
89          len(len_)
90    {}
91
92    const char *data() const { return seq+offset; }
93    int length() const { return len; }
94
95    // detect which gap to use at border of SeqPart:
96    char left_gap() const {
97        char gap                = to_gap(seq[offset]);
98        if (!gap && offset) gap = to_gap(seq[offset-1]);
99        if (!gap) gap           = '-';
100        return gap;
101    }
102    char right_gap() const {
103        char gap      = to_gap(seq[offset+len-1]);
104        if (!gap) gap = to_gap(seq[offset+len]);
105        if (!gap) gap = '-';
106        return gap;
107    }
108};
109
110// --------------------------------------------------------------------------------
111
112static void col_block_refresh_on_seq_term(ED4_sequence_terminal *seq_term) {
113    seq_term->request_refresh();
114    ED4_columnStat_terminal *colStatTerm = seq_term->corresponding_columnStat_terminal();
115    if (colStatTerm) colStatTerm->request_refresh();
116}
117
118static void refresh_selected(bool refresh_name_terminals) {
119    ED4_selected_elem *listElem = ED4_ROOT->selected_objects->head();
120    while (listElem) {
121        ED4_species_name_terminal *name_term = listElem->elem()->object;
122        ED4_sequence_terminal     *seq_term  = name_term->corresponding_sequence_terminal();
123
124        if (refresh_name_terminals) name_term->request_refresh();
125        if (seq_term) col_block_refresh_on_seq_term(seq_term);
126
127        listElem = listElem->next();
128    }
129}
130
131// --------------------------------------------------------------------------------
132
133void ED4_block::set_type(ED4_blocktype bt) {
134    if (type != bt) {
135        type = bt;
136        refresh_selected(true);
137        if (type==ED4_BT_COLUMNBLOCK || type==ED4_BT_MODIFIED_COLUMNBLOCK) {
138            columnBlockUsed = true;
139        }
140    }
141}
142
143void ED4_block::toggle_type() {
144    switch (type) {
145        case ED4_BT_NOBLOCK: {
146            aw_message("No block selected.");
147            break;
148        }
149        case ED4_BT_LINEBLOCK: {
150            if (columnBlockUsed) {
151                set_type(ED4_BT_MODIFIED_COLUMNBLOCK);
152            }
153            else {
154                aw_message("No columnblock marked so far  - I can't guess the column range");
155            }
156            break;
157        }
158        case ED4_BT_MODIFIED_COLUMNBLOCK:
159        case ED4_BT_COLUMNBLOCK: {
160            set_type(ED4_BT_LINEBLOCK);
161            break;
162        }
163    }
164}
165
166void ED4_block::autocorrect_type() {
167    // this has to be called every time the selection has changed
168
169    if (!ED4_ROOT->selected_objects->head()) { // no objects are selected
170        set_type(ED4_BT_NOBLOCK);
171    }
172    else {
173        switch (type) {
174            case ED4_BT_NOBLOCK: {
175                set_type(ED4_BT_LINEBLOCK);
176                break;
177            }
178            case ED4_BT_COLUMNBLOCK: {
179                set_type(ED4_BT_MODIFIED_COLUMNBLOCK);
180                break;
181            }
182            case ED4_BT_LINEBLOCK:
183            case ED4_BT_MODIFIED_COLUMNBLOCK: {
184                break;
185            }
186        }
187    }
188}
189
190// --------------------------------------------------------------------------------
191
192// if block_operation returns NULp => no changes will be made to database
193// otherwise the changed sequence(part) will be written to the database
194
195static GB_ERROR perform_block_operation_on_whole_sequence(const ED4_block_operator& block_operator, ED4_sequence_terminal *term) {
196    GBDATA   *gbd   = term->get_species_pointer();
197    GB_ERROR  error = NULp;
198
199    if (gbd) {
200        char *seq = GB_read_string(gbd);
201        int len = GB_read_string_count(gbd);
202
203        int   new_len;
204        char *new_seq = block_operator.operate(SeqPart(seq, 0, len), new_len);
205        error         = block_operator.get_error();
206
207        if (new_seq) {
208            if (new_len<len) {
209                memcpy(seq, new_seq, new_len);
210                char gap = ED4_is_gap_character(seq[len-1]) ? seq[len-1] : '.';
211                int l;
212                for (l=new_len; l<len; l++) {
213                    seq[l] = gap;
214                }
215                seq[l] = 0;
216            }
217            else if (new_len>len) {
218                for (int l=new_len-1; l>=len; l--) {
219                    if (!ED4_is_gap_character(new_seq[l])) {
220                        error = "Result of block-operation to large (not enough gaps at end of sequence data)";
221                        break;
222                    }
223                }
224
225                if (!error) { // there are enough gaps at end of sequence
226                    memcpy(seq, new_seq, len);
227                }
228            }
229            else {
230                memcpy(seq, new_seq, len);
231            }
232
233            if (!error) {
234                error = GB_write_string(gbd, seq);
235                if (!error) term->request_refresh();
236            }
237            free(new_seq);
238        }
239        free(seq);
240    }
241
242    return error;
243}
244
245// uses range_col1 till range_col2 as range
246static GB_ERROR perform_block_operation_on_part_of_sequence(const ED4_block_operator& block_operator, ED4_sequence_terminal *term) {
247    GBDATA   *gbd   = term->get_species_pointer();
248    GB_ERROR  error = NULp;
249
250    if (gbd) {
251        char *seq = GB_read_string(gbd);
252        int   len = GB_read_string_count(gbd);
253
254        ExplicitRange range(block.get_colblock_range(), len);
255
256        int   len_part     = range.size();
257        char *seq_part     = seq+range.start();
258        int   new_len;
259        char *new_seq_part = block_operator.operate(SeqPart(seq, range.start(), len_part), new_len);
260        error              = block_operator.get_error();
261
262        if (new_seq_part) {
263            if (new_len<len_part) {
264                memcpy(seq_part, new_seq_part, new_len);
265                char gap = '-';
266                if (seq_part[len_part-1] == '.' || seq_part[len_part] == '.') gap = '.';
267
268                for (int l=new_len; l<len_part; l++) {
269                    seq_part[l] = gap;
270                }
271            }
272            else if (new_len>len_part) {
273                for (int l=new_len-1; l>=len_part; l--) {
274                    if (!ED4_is_gap_character(new_seq_part[l])) {
275                        error = "Result of block-operation to large (not enough gaps at end of marked columnblock)";
276                        break;
277                    }
278                }
279
280                if (!error) { // there are enough gaps at end of sequence
281                    memcpy(seq_part, new_seq_part, len_part);
282                }
283            }
284            else {
285                memcpy(seq_part, new_seq_part, len_part);
286            }
287            delete new_seq_part;
288
289            if (!error) {
290                error = GB_write_autoconv_string(gbd, seq);
291                if (!error) term->request_refresh();
292            }
293        }
294
295        delete seq;
296    }
297
298    return error;
299}
300
301static void ED4_with_whole_block(const ED4_block_operator& block_operator) {
302    GBDATA   *gb_main = ED4_ROOT->get_gb_main();
303    GB_ERROR  error   = GB_begin_transaction(gb_main);
304
305    typedef map<ED4_window*, int> CursorPositions;
306    CursorPositions at_base;
307
308    for (ED4_window *win = ED4_ROOT->first_window; win; win = win->next) {
309        ED4_cursor& cursor = win->cursor;
310        if (cursor.owner_of_cursor) at_base[win] = cursor.get_base_position();
311    }
312
313
314    switch (block.get_type()) {
315        case ED4_BT_NOBLOCK: {
316            aw_message("No block marked -- use right mouse button");
317            break;
318        }
319        case ED4_BT_LINEBLOCK:
320        case ED4_BT_MODIFIED_COLUMNBLOCK:
321        case ED4_BT_COLUMNBLOCK: {
322            ED4_selected_elem *listElem = ED4_ROOT->selected_objects->head();
323            while (listElem && !error) {
324                ED4_species_name_terminal *nameTerm = listElem->elem()->object;
325                ED4_sequence_terminal     *seqTerm  = nameTerm->corresponding_sequence_terminal();
326
327                error = block.get_type() == ED4_BT_LINEBLOCK
328                    ? perform_block_operation_on_whole_sequence(block_operator, seqTerm)
329                    : perform_block_operation_on_part_of_sequence(block_operator, seqTerm);
330
331                listElem = listElem->next();
332            }
333            break;
334        }
335        default: {
336            error = "Illegal blocktype";
337            break;
338        }
339    }
340
341    if (error) error = GBS_global_string("[In block operation] %s", error);
342    GB_end_transaction_show_error(gb_main, error, aw_message);
343
344    if (!error) {
345        for (CursorPositions::iterator ab = at_base.begin(); ab != at_base.end(); ++ab) {
346            ED4_window *win = ab->first;
347            win->cursor.jump_base_pos(ab->second, ED4_JUMP_KEEP_VISIBLE); // restore cursor at same base
348        }
349    }
350}
351
352bool ED4_get_selected_range(ED4_terminal *term, PosRange& range) { // @@@ function will get useless, when multi-column-blocks are possible
353    if (block.get_type() == ED4_BT_NOBLOCK) return false;
354    if (!term->containing_species_manager()->is_selected()) return false;
355
356    range = block.get_range_according_to_type();
357    return true;
358}
359
360ED4_blocktype ED4_getBlocktype() { return block.get_type(); }
361void ED4_setBlocktype(ED4_blocktype bt) { block.set_type(bt); }
362void ED4_toggle_block_type() { block.toggle_type(); }
363void ED4_correctBlocktypeAfterSelection() { block.autocorrect_type(); }
364
365static void select_and_update(ED4_sequence_terminal *term1, ED4_sequence_terminal *term2, ED4_index pos1, ED4_index pos2, int initial_call) {
366    static ED4_sequence_terminal *last_term1, *last_term2;
367    static PosRange               last_range;
368
369    if (!term1) return; // e.g. when 1st click was into consensus
370
371    if (pos1>pos2) {
372        block.set_range(PosRange(pos2, pos1));
373    }
374    else {
375        block.set_range(PosRange(pos1, pos2));
376    }
377
378    if (block.get_type()==ED4_BT_MODIFIED_COLUMNBLOCK) {
379        refresh_selected(false);
380    }
381    else {
382        { // ensure term1 is the upper terminal
383            AW_pos dummy, y1, y2;
384
385            term1->calc_world_coords(&dummy, &y1);
386            term2->calc_world_coords(&dummy, &y2);
387
388            if (y1>y2) {
389                ED4_sequence_terminal *t = term1; term1 = term2; term2 = t;
390                AW_pos y = y1; y1 = y2; y2 = y;
391            }
392        }
393
394        int do_above = 1; // we have to update terminals between last_term1 and term1
395        int do_below = 1; // we have to update terminals between term2 and last_term2
396
397        ED4_terminal *term = term1;
398
399        bool xRangeChanged = block.get_range_according_to_type() != last_range;
400
401        while (term) {
402            if (term->is_sequence_terminal()) {
403                ED4_sequence_terminal *seq_term = term->to_sequence_terminal();
404
405                if (seq_term==last_term1) {
406                    do_above = 0;
407                }
408                if (seq_term==last_term2) {
409                    do_below = 0;
410                }
411
412                ED4_species_name_terminal *name_term = seq_term->corresponding_species_name_terminal();
413                ED4_species_manager *species_man = name_term->containing_species_manager();
414                if (species_man->is_selected()) { // already selected
415                    if (xRangeChanged) {
416                        col_block_refresh_on_seq_term(seq_term);
417                    }
418                }
419                else { // select it
420                    if (!species_man->is_consensus_manager()) {
421                        ED4_ROOT->add_to_selected(name_term);
422                    }
423                }
424            }
425            if (term==term2) {
426                break;
427            }
428            term = term->get_next_terminal();
429        }
430
431        if (!initial_call) {
432            if (do_below) {
433                while (term) {
434                    if (term->is_species_name_terminal()) {
435                        ED4_species_manager *species_man = term->containing_species_manager();
436                        if (species_man->is_selected() && !species_man->is_consensus_manager()) {
437                            ED4_ROOT->remove_from_selected(term->to_species_name_terminal());
438                        }
439                    }
440                    if (term==last_term2) break;
441                    term = term->get_next_terminal();
442                }
443            }
444
445            if (do_above) {
446                term = last_term1->corresponding_species_name_terminal();
447                while (term && term!=term1) {
448                    if (term->is_species_name_terminal()) {
449                        ED4_species_manager *species_man = term->containing_species_manager();
450                        if (species_man->is_selected() && !species_man->is_consensus_manager()) {
451                            ED4_ROOT->remove_from_selected(term->to_species_name_terminal());
452                        }
453                    }
454                    term = term->get_next_terminal();
455                }
456            }
457        }
458    }
459
460    last_term1 = term1;
461    last_term2 = term2;
462    last_range = block.get_range_according_to_type();
463}
464
465void ED4_setColumnblockCorner(AW_event *event, ED4_sequence_terminal *seq_term) {
466    static ED4_sequence_terminal *fix_term = NULp;
467    static ED4_index              fix_pos  = 0;
468
469    ED4_index seq_pos;
470    {
471        AW_pos termw_x, termw_y;
472        seq_term->calc_world_coords(&termw_x, &termw_y);
473
474        ED4_index scr_pos = ED4_ROOT->pixel2pos(event->x - termw_x);
475        ED4_remap *remap = ED4_ROOT->root_group_man->remap();
476        seq_pos = remap->screen_to_sequence(scr_pos);
477    }
478
479    switch (event->type) {
480        case AW_Mouse_Press: {
481            if (block.get_type() == ED4_BT_NOBLOCK) { // initial columnblock
482                if (!seq_term->is_consensus_terminal()) {
483                    block.set_type(ED4_BT_COLUMNBLOCK);
484
485                    fix_term = seq_term;
486                    fix_pos = seq_pos;
487
488                    select_and_update(fix_term, seq_term, fix_pos, seq_pos, 1);
489                }
490            }
491            else if (block.get_type()==ED4_BT_LINEBLOCK) { // change lineblock to columnblock
492                block.set_type(ED4_BT_MODIFIED_COLUMNBLOCK);
493
494                fix_term = seq_term;
495                if (seq_pos<(MAXSEQUENCECHARACTERLENGTH/2)) { // in first half of sequence
496                    fix_pos = MAXSEQUENCECHARACTERLENGTH;
497                }
498                else {
499                    fix_pos = 0;
500                }
501
502                select_and_update(fix_term, seq_term, fix_pos, seq_pos, 1);
503            }
504            else { // expand columnblock (search nearest corner/border -> fix opposite corner/border)
505                e4_assert(block.get_type()==ED4_BT_COLUMNBLOCK || block.get_type()==ED4_BT_MODIFIED_COLUMNBLOCK);
506
507                ED4_selected_elem *listElem = ED4_ROOT->selected_objects->head();
508                e4_assert(listElem);
509
510                if (block.get_type()==ED4_BT_COLUMNBLOCK) {
511                    AW_pos                     min_term_y = LONG_MAX;
512                    AW_pos                     max_term_y = LONG_MIN;
513                    ED4_species_name_terminal *min_term   = NULp;
514                    ED4_species_name_terminal *max_term   = NULp;
515                    AW_pos                     xpos, ypos;
516
517                    while (listElem) {
518                        ED4_species_name_terminal *name_term = listElem->elem()->object;
519                        name_term->calc_world_coords(&xpos, &ypos);
520
521                        if (ypos<min_term_y) {
522                            min_term_y = ypos;
523                            min_term = name_term;
524                        }
525                        if (ypos>max_term_y) {
526                            max_term_y = ypos;
527                            max_term = name_term;
528                        }
529
530                        listElem = listElem->next();
531                    }
532
533                    seq_term->calc_world_coords(&xpos, &ypos);
534                    ED4_species_name_terminal *fix_name_term;
535                    if (fabs(ypos-min_term_y)<fabs(ypos-max_term_y)) { // seq_term is closer to min_term
536                        fix_name_term = max_term; // select max_term as fixed corner
537                    }
538                    else {
539                        fix_name_term = min_term;
540                    }
541                    e4_assert(fix_name_term);
542                    fix_term = fix_name_term->corresponding_sequence_terminal();
543                }
544
545                AW_screen_area area_rect;
546                {
547                    AW_pos ex = event->x;
548                    AW_pos ey = event->y;
549                    current_ed4w()->world_to_win_coords(&ex, &ey);
550
551                    if (ED4_ROOT->get_area_rectangle(&area_rect, ex, ey)!=ED4_R_OK) {
552                        e4_assert(0);
553                        break;
554                    }
555                }
556
557
558                const ED4_remap *rm = ED4_ROOT->root_group_man->remap();
559
560                PosRange screen_range = rm->clip_screen_range(seq_term->calc_interval_displayed_in_rectangle(&area_rect));
561                int      scr_pos      = rm->sequence_to_screen(seq_pos);
562
563                PosRange block_screen_range = rm->sequence_to_screen(block.get_colblock_range());
564                PosRange block_visible_part = intersection(screen_range, block_screen_range);
565
566                if (block_visible_part.is_empty()) {
567                    if (scr_pos>block_screen_range.end()) {
568                        fix_pos = block.get_colblock_range().start();
569                    }
570                    else {
571                        e4_assert(scr_pos<block_screen_range.start());
572                        fix_pos = block.get_colblock_range().end();
573                    }
574                }
575                else {
576                    int dist_left  = abs(scr_pos-block_visible_part.start());
577                    int dist_right = abs(scr_pos-block_visible_part.end());
578
579                    if (dist_left < dist_right) {   // click nearer to left border of visible part of block
580                        fix_pos = block.get_colblock_range().end(); // keep right block-border
581                    }
582                    else {
583                        fix_pos = block.get_colblock_range().start();
584                    }
585                }
586
587                select_and_update(fix_term, seq_term, fix_pos, seq_pos, 0);
588            }
589            break;
590        }
591        case AW_Mouse_Drag: {
592            select_and_update(fix_term, seq_term, fix_pos, seq_pos, 0);
593            break;
594        }
595        case AW_Mouse_Release: {
596            select_and_update(fix_term, seq_term, fix_pos, seq_pos, 0);
597            break;
598        }
599        default: {
600            e4_assert(0);
601            break;
602        }
603    }
604}
605
606// --------------------------------------------------------------------------------
607//      Replace
608
609inline bool matchesUsingWildcard(GB_CSTR s1, GB_CSTR s2, int len) {
610    // s2 may contain '?' as wildcard
611    int cmp = 0;
612
613    for (int i = 0; i<len && !cmp; ++i) {
614        cmp = int(s1[i])-int(s2[i]);
615        if (cmp && s2[i] == '?') cmp = 0;
616    }
617
618    return !cmp;
619}
620
621class replace_op : public ED4_block_operator { // derived from Noncopyable
622    const char *oldString;
623    const char *newString;
624
625    int olen;
626    int nlen;
627
628public:
629    replace_op(const char *oldString_, const char *newString_)
630        : oldString(oldString_),
631          newString(newString_)
632    {
633        olen = strlen(oldString);
634        nlen = strlen(newString);
635    }
636
637    char* operate(const SeqPart& part, int& new_len) const OVERRIDE {
638        int maxlen;
639        int len   = part.length();
640        int max_o = len-olen;
641
642        if (nlen<=olen) {
643            maxlen = len;
644        }
645        else {
646            maxlen = (len/olen+1)*nlen;
647        }
648
649        char *new_seq  = ARB_alloc<char>(maxlen+1);
650        int   replaced = 0;
651        int   o        = 0;
652        int   n        = 0;
653
654        const char *sequence = part.data();
655        while (o<len) {
656            if (o <= max_o && matchesUsingWildcard(sequence+o, oldString, olen)) {
657                memcpy(new_seq+n, newString, nlen);
658                n += nlen;
659                o += olen;
660                replaced++;
661            }
662            else {
663                new_seq[n++] = sequence[o++];
664            }
665        }
666        new_seq[n] = 0;
667
668        if (replaced) {
669            new_len = n;
670        }
671        else {
672            freenull(new_seq);
673        }
674
675        return new_seq;
676    }
677};
678
679static void replace_in_block(AW_window*) {
680    AW_root *awr = ED4_ROOT->aw_root;
681    ED4_with_whole_block(
682        replace_op(awr->awar(ED4_AWAR_REP_SEARCH_PATTERN)->read_char_pntr(),
683                   awr->awar(ED4_AWAR_REP_REPLACE_PATTERN)->read_char_pntr()));
684}
685
686AW_window *ED4_create_replace_window(AW_root *root) {
687    AW_window_simple *aws = new AW_window_simple;
688
689    aws->init(root, "REPLACE", "Search & Replace");
690    aws->load_xfig("edit4/replace.fig");
691
692    aws->at("close");
693    aws->callback(AW_POPDOWN);
694    aws->create_button("CLOSE", "Close", "C");
695
696    aws->at("help");
697    aws->callback(makeHelpCallback("e4_replace.hlp"));
698    aws->create_button("HELP", "Help", "H");
699
700    aws->at("spattern");
701    aws->create_input_field(ED4_AWAR_REP_SEARCH_PATTERN, 30);
702
703    aws->at("rpattern");
704    aws->create_input_field(ED4_AWAR_REP_REPLACE_PATTERN, 30);
705
706    aws->at("go");
707    aws->callback(replace_in_block);
708    aws->create_button("GO", "Go", "G");
709
710    return aws;
711}
712
713// --------------------------------------------------------------------------------
714//      Other block operations
715
716inline char *dont_return_unchanged(char *result, int& new_len, const SeqPart& part) {
717    if (result) {
718        if (new_len == part.length()) {
719            if (memcmp(result, part.data(), new_len) == 0) {
720                freenull(result);
721            }
722        }
723    }
724    return result;
725}
726
727class case_op : public ED4_block_operator {
728    bool to_upper;
729public:
730    case_op(bool to_upper_) : to_upper(to_upper_) {}
731
732    char *operate(const SeqPart& part, int& new_len) const OVERRIDE {
733        int         len     = part.length();
734        const char *seq     = part.data();
735        char       *new_seq = ARB_alloc<char>(len+1);
736
737        if (to_upper) {
738            for (int i=0; i<len; i++) new_seq[i] = toupper(seq[i]);
739        }
740        else {
741            for (int i=0; i<len; i++) new_seq[i] = tolower(seq[i]);
742        }
743        new_seq[len] = 0;
744
745        new_len = len;
746        return dont_return_unchanged(new_seq, new_len, part);
747    }
748};
749
750class revcomp_op : public ED4_block_operator {
751    bool reverse;
752    bool complement;
753    char T_or_U;
754
755public:
756    revcomp_op(GB_alignment_type aliType, bool reverse_, bool complement_)
757        : reverse(reverse_),
758          complement(complement_),
759          T_or_U(0)
760    {
761        if (complement) {
762            error = GBT_determine_T_or_U(aliType, &T_or_U, reverse ? "reverse-complement" : "complement");
763        }
764    }
765
766    char *operate(const SeqPart& part, int& new_len) const OVERRIDE {
767        char *result = NULp;
768        if (!error) {
769            int len = part.length();
770            if (complement) {
771                result = GBT_complementNucSequence(part.data(), len, T_or_U);
772                if (reverse) freeset(result, GBT_reverseNucSequence(result, len));
773            }
774            else if (reverse) result = GBT_reverseNucSequence(part.data(), len);
775
776            new_len = len;
777        }
778        return dont_return_unchanged(result, new_len, part);
779    }
780
781};
782
783class unalign_op : public ED4_block_operator {
784    int direction;
785public:
786    unalign_op(int direction_) : direction(direction_) {}
787
788    char *operate(const SeqPart& part, int& new_len) const OVERRIDE {
789        int         len    = part.length();
790        const char *seq    = part.data();
791        char       *result = ARB_alloc<char>(len+1);
792
793        int o = 0;
794        int n = 0;
795
796        while (o<len) {
797            if (!ED4_is_gap_character(seq[o])) result[n++] = seq[o];
798            o++;
799        }
800
801        if (n<len) { // (move and) dot rest
802            int gapcount = len-n;
803            switch (direction) {
804                case 1: // rightwards
805                    memmove(result+gapcount, result, n);
806                    memset(result, part.left_gap(), gapcount);
807                    break;
808                case 0: { // center
809                    int leftgaps  = gapcount/2;
810                    int rightgaps = gapcount-leftgaps;
811
812                    memmove(result+leftgaps, result, n);
813                    memset(result, part.left_gap(), leftgaps);
814                    memset(result+leftgaps+n, part.right_gap(), rightgaps);
815
816                    break;
817                }
818                case -1: // leftwards
819                    memset(result+n, part.right_gap(), gapcount);
820                    break;
821            }
822        }
823        result[len] = 0;
824        new_len     = len;
825
826        return dont_return_unchanged(result, new_len, part);
827    }
828
829
830};
831
832class shift_op : public ED4_block_operator {
833    int direction;
834
835    char *shift_left_sequence(const SeqPart& part, int& new_len) const {
836        char       *result = NULp;
837        const char *seq    = part.data();
838
839        if (!ED4_is_gap_character(seq[0])) {
840            error = "Need a gap at block start for shifting left";
841        }
842        else {
843            int len       = part.length();
844            ARB_alloc(result, len+1);
845            result[len]   = 0;
846            new_len       = len;
847            result[len-1] = part.right_gap();
848            memcpy(result, seq+1, len-1);
849        }
850        return result;
851    }
852
853    char *shift_right_sequence(const SeqPart& part, int& new_len) const {
854        char       *result = NULp;
855        const char *seq    = part.data();
856        int         len    = part.length();
857
858        if (!ED4_is_gap_character(seq[len-1])) {
859            error = "Need a gap at block end for shifting right";
860        }
861        else {
862            ARB_alloc(result, len+1);
863            result[len] = 0;
864            new_len     = len;
865            result[0]   = part.left_gap();
866            memcpy(result+1, seq, len-1);
867        }
868        return result;
869    }
870
871
872public:
873    shift_op(int direction_) : direction(direction_) {}
874
875    char *operate(const SeqPart& part, int& new_len) const OVERRIDE {
876        char *result = direction<0
877            ? shift_left_sequence(part, new_len)
878            : shift_right_sequence(part, new_len);
879        return dont_return_unchanged(result, new_len, part);
880    }
881};
882
883void ED4_perform_block_operation(ED4_blockoperation_type operationType) {
884    switch (operationType) {
885        case ED4_BO_UPPER_CASE: ED4_with_whole_block(case_op(true));  break;
886        case ED4_BO_LOWER_CASE: ED4_with_whole_block(case_op(false)); break;
887
888        case ED4_BO_REVERSE:            ED4_with_whole_block(revcomp_op(ED4_ROOT->alignment_type, true,  false)); break;
889        case ED4_BO_COMPLEMENT:         ED4_with_whole_block(revcomp_op(ED4_ROOT->alignment_type, false, true));  break;
890        case ED4_BO_REVERSE_COMPLEMENT: ED4_with_whole_block(revcomp_op(ED4_ROOT->alignment_type, true,  true));  break;
891
892        case ED4_BO_UNALIGN_LEFT:   ED4_with_whole_block(unalign_op(-1)); break;
893        case ED4_BO_UNALIGN_CENTER: ED4_with_whole_block(unalign_op(0));  break;
894        case ED4_BO_UNALIGN_RIGHT:  ED4_with_whole_block(unalign_op(1));  break;
895
896        case ED4_BO_SHIFT_LEFT:  ED4_with_whole_block(shift_op(-1)); break;
897        case ED4_BO_SHIFT_RIGHT: ED4_with_whole_block(shift_op(1));  break;
898
899        default: {
900            e4_assert(0);
901            break;
902        }
903    }
904}
905
906// --------------------------------------
907//      modify range of selected sai
908
909#define AWAR_MOD_SAI_SCRIPT "modsai/script"
910
911static void modsai_cb(AW_window *aww) {
912    ED4_MostRecentWinContext context;
913
914    ED4_cursor *cursor = &current_cursor();
915    GB_ERROR    error  = NULp;
916
917    if (!cursor->in_SAI_terminal()) {
918        error = "Please select the SAI you like to modify";
919    }
920    else if (block.get_type() == ED4_BT_NOBLOCK) {
921        error = "Please select the range where the SAI shall be modified";
922    }
923    else {
924        AW_root    *aw_root = aww->get_root();
925        char       *script  = aw_root->awar(AWAR_MOD_SAI_SCRIPT)->read_string();
926        const char *sainame = aw_root->awar(AWAR_SAI_NAME)->read_char_pntr();
927
928        GBDATA         *gb_main = ED4_ROOT->get_gb_main();
929        GB_transaction  ta(gb_main);
930        GBDATA         *gb_sai  = GBT_find_SAI(gb_main, sainame);
931
932        if (!gb_sai) {
933            error = GB_have_error()
934                ? GB_await_error()
935                : GBS_global_string("Failed to find SAI '%s'", sainame);
936        }
937        else {
938            GBDATA *gb_data = GBT_find_sequence(gb_sai, ED4_ROOT->alignment_name);
939
940            if (!gb_data) error = GB_await_error();
941            else {
942                char *seq = GB_read_string(gb_data); // @@@ NOT_ALL_SAI_HAVE_DATA
943                int   len = GB_read_string_count(gb_data);
944
945                ExplicitRange  range(block.get_range_according_to_type(), len);
946                char          *part = range.dup_corresponding_part(seq, len);
947
948                char *result       = GB_command_interpreter_in_env(part, script, GBL_simple_call_env(gb_sai));
949                if (!result) error = GB_await_error();
950                else {
951                    int reslen   = strlen(result);
952                    if (reslen>range.size()) {
953                        error = GBS_global_string("Cannot insert modified range (result too long; %i>%i)", reslen, range.size());
954                    }
955                    else {
956                        memcpy(seq+range.start(), result, reslen);
957                        int rest = range.size()-reslen;
958                        if (rest>0) memset(seq+range.start()+reslen, '-', rest);
959
960                        error = GB_write_string(gb_data, seq);
961                    }
962                    free(result);
963                }
964
965                free(part);
966                free(seq);
967            }
968        }
969        free(script);
970        error = ta.close(error);
971    }
972
973    aw_message_if(error);
974}
975
976AW_window *ED4_create_modsai_window(AW_root *root) {
977    root->awar_string(AWAR_MOD_SAI_SCRIPT)->write_string("");
978
979    AW_window_simple *aws = new AW_window_simple;
980    aws->init(root, "modsai", "Modify SAI range");
981    aws->load_xfig("edit4/modsai.fig");
982
983    aws->at("close");
984    aws->callback(AW_POPDOWN);
985    aws->create_button("CLOSE", "Close", "C");
986
987    aws->at("help");
988    aws->callback(makeHelpCallback("e4_modsai.hlp"));
989    aws->create_button("HELP", "Help", "H");
990
991    AW_selection_list *sellist = awt_create_selection_list_with_input_field(aws, AWAR_MOD_SAI_SCRIPT, "box", "script");
992    GB_ERROR           error;
993    {
994        StorableSelectionList storable_sellist(TypedSelectionList("sellst", sellist, "SRT/ACI scripts", "srt_aci"));
995        error = storable_sellist.load(GB_path_in_ARBLIB("sellists/mod_sequence*.sellst"), false);
996    }
997    aw_message_if(error);
998
999    aws->at("go");
1000    aws->callback(modsai_cb);
1001    aws->create_button("go", "GO");
1002
1003    return aws;
1004}
1005
1006
1007// --------------------------------------------------------------------------------
1008
1009#ifdef UNIT_TESTS
1010#ifndef TEST_UNIT_H
1011#include <test_unit.h>
1012#endif
1013
1014static arb_test::match_expectation blockop_expected_io(const ED4_block_operator& blockop, const char *oversized_input, const char *expected_result, const char *part_of_error) {
1015    int      whole_len = strlen(oversized_input);
1016    SeqPart  part(oversized_input, 1, whole_len-2);
1017    int      new_len   = 0;
1018    char    *result    = blockop.operate(part, new_len);
1019
1020    using namespace arb_test;
1021
1022    expectation_group expectations;
1023    expectations.add(part_of_error
1024                     ? that(blockop.get_error()).does_contain(part_of_error)
1025                     : that(blockop.get_error()).is_equal_to_NULL());
1026    expectations.add(that(result).is_equal_to(expected_result));
1027    if (expected_result) expectations.add(that(new_len).is_equal_to(whole_len-2));
1028
1029    free(result);
1030
1031    return all().ofgroup(expectations);
1032}
1033
1034#define TEST_EXPECT_BLOCKOP_PERFORMS(oversized_input,blockop,expected)         TEST_EXPECTATION(blockop_expected_io(blockop, oversized_input, expected, NULp))
1035#define TEST_EXPECT_BLOCKOP_PERFORMS__BROKEN(oversized_input,blockop,expected) TEST_EXPECTATION__BROKEN(blockop_expected_io(blockop, oversized_input, expected, NULp))
1036#define TEST_EXPECT_BLOCKOP_ERRORHAS(oversized_input,blockop,expected)         TEST_EXPECTATION(blockop_expected_io(blockop, oversized_input, NULp, expected))
1037#define TEST_EXPECT_BLOCKOP_ERRORHAS__BROKEN(oversized_input,blockop,expected) TEST_EXPECTATION__BROKEN(blockop_expected_io(blockop, oversized_input, NULp, expected))
1038
1039void TEST_block_operators() {
1040    ED4_setup_gaps_and_alitype("-.", GB_AT_RNA);
1041
1042    // Note: make sure tests perform an identity block operation at least once for each operator
1043
1044    // replace_op
1045    TEST_EXPECT_BLOCKOP_PERFORMS("-A-C--", replace_op("-",  "."),  "A.C.");
1046    TEST_EXPECT_BLOCKOP_PERFORMS("-A-C--", replace_op("?",  "."),  "....");
1047    TEST_EXPECT_BLOCKOP_PERFORMS("AACAG-", replace_op("AC", "CA"), "CAAG");
1048    TEST_EXPECT_BLOCKOP_PERFORMS("-ACAG-", replace_op("A?", "Ax"), "AxAx");
1049
1050    TEST_EXPECT_BLOCKOP_PERFORMS("GACAG-", replace_op("GA", "AG"), NULp);   // unchanged
1051    TEST_EXPECT_BLOCKOP_PERFORMS("GAGAGA", replace_op("GA", "AG"), "AAGG");
1052    TEST_EXPECT_BLOCKOP_PERFORMS("GACAGA", replace_op("GA", "AG"), NULp);
1053    TEST_EXPECT_BLOCKOP_PERFORMS("AGAGAG", replace_op("GA", "AG"), "AGAG");
1054
1055    // case_op
1056    TEST_EXPECT_BLOCKOP_PERFORMS("-AcGuT-", case_op(true), "ACGUT");
1057    TEST_EXPECT_BLOCKOP_PERFORMS("-AcGuT-", case_op(false), "acgut");
1058    TEST_EXPECT_BLOCKOP_PERFORMS("-acgut-", case_op(false), NULp);
1059    TEST_EXPECT_BLOCKOP_PERFORMS("-------", case_op(false), NULp);
1060
1061    // revcomp_op
1062    TEST_EXPECT_BLOCKOP_PERFORMS("-Ac-GuT-", revcomp_op(GB_AT_RNA, false, false), NULp);     // noop
1063    TEST_EXPECT_BLOCKOP_PERFORMS("-Ac-GuT-", revcomp_op(GB_AT_RNA, true,  false), "TuG-cA");
1064    TEST_EXPECT_BLOCKOP_PERFORMS("-Ac-GuT-", revcomp_op(GB_AT_RNA, false, true),  "Ug-CaA");
1065    TEST_EXPECT_BLOCKOP_PERFORMS("-Ac-GuT-", revcomp_op(GB_AT_RNA, true,  true),  "AaC-gU");
1066    TEST_EXPECT_BLOCKOP_PERFORMS("-Ac-GuT-", revcomp_op(GB_AT_DNA, true,  true),  "AaC-gT");
1067    TEST_EXPECT_BLOCKOP_PERFORMS("-AcGCgT-", revcomp_op(GB_AT_DNA, true,  true),  NULp);
1068    TEST_EXPECT_BLOCKOP_PERFORMS("--------", revcomp_op(GB_AT_DNA, true,  true),  NULp);
1069
1070    TEST_EXPECT_BLOCKOP_PERFORMS("-AR-DQF-", revcomp_op(GB_AT_AA, false, false), NULp); // noop
1071    TEST_EXPECT_BLOCKOP_PERFORMS("-AR-DQF-", revcomp_op(GB_AT_AA, true,  false), "FQD-RA");
1072    TEST_EXPECT_BLOCKOP_ERRORHAS("-AR-DQF-", revcomp_op(GB_AT_AA, false, true),  "complement not available");
1073    TEST_EXPECT_BLOCKOP_ERRORHAS("-AR-DQF-", revcomp_op(GB_AT_AA, true,  true),  "reverse-complement not available");
1074
1075    // unalign_op
1076    TEST_EXPECT_BLOCKOP_PERFORMS("-A-c-G--T-", unalign_op(-1), "AcGT----");
1077    TEST_EXPECT_BLOCKOP_PERFORMS("-A-c-G-T-T", unalign_op(-1), "AcGT----");
1078    TEST_EXPECT_BLOCKOP_PERFORMS("-A-c-G--TT", unalign_op(-1), "AcGT----");
1079    TEST_EXPECT_BLOCKOP_PERFORMS("-A-c-G--T.", unalign_op(-1), "AcGT....");
1080    TEST_EXPECT_BLOCKOP_PERFORMS("-A-c-G-T.T", unalign_op(-1), "AcGT....");
1081
1082    TEST_EXPECT_BLOCKOP_PERFORMS("A-Ac-G--T-", unalign_op(+1), "----AcGT");
1083    TEST_EXPECT_BLOCKOP_PERFORMS("-A-c-G--T-", unalign_op(+1), "----AcGT");
1084    TEST_EXPECT_BLOCKOP_PERFORMS("A.Ac-G--T-", unalign_op(+1), "....AcGT");
1085    TEST_EXPECT_BLOCKOP_PERFORMS(".A-c-G--T-", unalign_op(+1), "....AcGT");
1086    TEST_EXPECT_BLOCKOP_PERFORMS("AA-c-G--T-", unalign_op(+1), "----AcGT");
1087
1088    TEST_EXPECT_BLOCKOP_PERFORMS("AA-c-G--TT", unalign_op(0), "--AcGT--");
1089    TEST_EXPECT_BLOCKOP_PERFORMS(".A-c-G--T-", unalign_op(0), "..AcGT--");
1090    TEST_EXPECT_BLOCKOP_PERFORMS(".A-c-G--T.", unalign_op(0), "..AcGT..");
1091    TEST_EXPECT_BLOCKOP_PERFORMS("-A-c-G--T.", unalign_op(0), "--AcGT..");
1092    TEST_EXPECT_BLOCKOP_PERFORMS("-A-c-Gc-T.", unalign_op(0), "-AcGcT..");
1093
1094    TEST_EXPECT_BLOCKOP_PERFORMS("-AcGcT.",  unalign_op(0), NULp);
1095    TEST_EXPECT_BLOCKOP_PERFORMS("-AcGcT..", unalign_op(0), NULp);
1096    TEST_EXPECT_BLOCKOP_PERFORMS("--AcGcT.", unalign_op(0), "AcGcT.");
1097
1098    TEST_EXPECT_BLOCKOP_PERFORMS("-ACGT-", unalign_op(-1), NULp);
1099    TEST_EXPECT_BLOCKOP_PERFORMS("-ACGT-", unalign_op(+1), NULp);
1100    TEST_EXPECT_BLOCKOP_PERFORMS("------", unalign_op(+1), NULp);
1101
1102    // shift_op
1103    TEST_EXPECT_BLOCKOP_PERFORMS("-A-C--", shift_op(+1), "-A-C"); // take gap outside region
1104    TEST_EXPECT_BLOCKOP_PERFORMS(".A-C--", shift_op(+1), ".A-C"); // -"-
1105    TEST_EXPECT_BLOCKOP_PERFORMS("-.AC--", shift_op(+1), "..AC"); // take gap inside region
1106
1107    TEST_EXPECT_BLOCKOP_PERFORMS("--A-C-", shift_op(-1), "A-C-"); // same for other direction
1108    TEST_EXPECT_BLOCKOP_PERFORMS("--A-C.", shift_op(-1), "A-C.");
1109    TEST_EXPECT_BLOCKOP_PERFORMS("--AC..", shift_op(-1), "AC..");
1110    TEST_EXPECT_BLOCKOP_PERFORMS("------", shift_op(-1), NULp);
1111
1112    TEST_EXPECT_BLOCKOP_PERFORMS("G-TTAC", shift_op(-1), "TTA-"); // no gap reachable
1113
1114    TEST_EXPECT_BLOCKOP_ERRORHAS("GATTAC", shift_op(-1), "Need a gap at block");  // no space
1115    TEST_EXPECT_BLOCKOP_ERRORHAS("GATTAC", shift_op(+1), "Need a gap at block");  // no space
1116}
1117
1118#endif // UNIT_TESTS
1119
1120// --------------------------------------------------------------------------------
1121
Note: See TracBrowser for help on using the repository browser.