source: tags/ms_r18q1/EDIT4/ED4_block.cxx

Last change on this file was 16763, checked in by westram, 6 years ago
  • 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    GB_ERROR error = GB_begin_transaction(GLOBAL_gb_main);
303
304    typedef map<ED4_window*, int> CursorPositions;
305    CursorPositions at_base;
306
307    for (ED4_window *win = ED4_ROOT->first_window; win; win = win->next) {
308        ED4_cursor& cursor = win->cursor;
309        if (cursor.owner_of_cursor) at_base[win] = cursor.get_base_position();
310    }
311
312
313    switch (block.get_type()) {
314        case ED4_BT_NOBLOCK: {
315            aw_message("No block marked -- use right mouse button");
316            break;
317        }
318        case ED4_BT_LINEBLOCK:
319        case ED4_BT_MODIFIED_COLUMNBLOCK:
320        case ED4_BT_COLUMNBLOCK: {
321            ED4_selected_elem *listElem = ED4_ROOT->selected_objects->head();
322            while (listElem && !error) {
323                ED4_species_name_terminal *nameTerm = listElem->elem()->object;
324                ED4_sequence_terminal     *seqTerm  = nameTerm->corresponding_sequence_terminal();
325
326                error = block.get_type() == ED4_BT_LINEBLOCK
327                    ? perform_block_operation_on_whole_sequence(block_operator, seqTerm)
328                    : perform_block_operation_on_part_of_sequence(block_operator, seqTerm);
329
330                listElem = listElem->next();
331            }
332            break;
333        }
334        default: {
335            error = "Illegal blocktype";
336            break;
337        }
338    }
339
340    if (error) error = GBS_global_string("[In block operation] %s", error);
341    GB_end_transaction_show_error(GLOBAL_gb_main, error, aw_message);
342
343    if (!error) {
344        for (CursorPositions::iterator ab = at_base.begin(); ab != at_base.end(); ++ab) {
345            ED4_window *win = ab->first;
346            win->cursor.jump_base_pos(ab->second, ED4_JUMP_KEEP_VISIBLE); // restore cursor at same base
347        }
348    }
349}
350
351bool ED4_get_selected_range(ED4_terminal *term, PosRange& range) { // @@@ function will get useless, when multi-column-blocks are possible
352    if (block.get_type() == ED4_BT_NOBLOCK) return false;
353    if (!term->containing_species_manager()->is_selected()) return false;
354
355    range = block.get_range_according_to_type();
356    return true;
357}
358
359ED4_blocktype ED4_getBlocktype() { return block.get_type(); }
360void ED4_setBlocktype(ED4_blocktype bt) { block.set_type(bt); }
361void ED4_toggle_block_type() { block.toggle_type(); }
362void ED4_correctBlocktypeAfterSelection() { block.autocorrect_type(); }
363
364static void select_and_update(ED4_sequence_terminal *term1, ED4_sequence_terminal *term2, ED4_index pos1, ED4_index pos2, int initial_call) {
365    static ED4_sequence_terminal *last_term1, *last_term2;
366    static PosRange               last_range;
367
368    if (!term1) return; // e.g. when 1st click was into consensus
369
370    if (pos1>pos2) {
371        block.set_range(PosRange(pos2, pos1));
372    }
373    else {
374        block.set_range(PosRange(pos1, pos2));
375    }
376
377    if (block.get_type()==ED4_BT_MODIFIED_COLUMNBLOCK) {
378        refresh_selected(false);
379    }
380    else {
381        { // ensure term1 is the upper terminal
382            AW_pos dummy, y1, y2;
383
384            term1->calc_world_coords(&dummy, &y1);
385            term2->calc_world_coords(&dummy, &y2);
386
387            if (y1>y2) {
388                ED4_sequence_terminal *t = term1; term1 = term2; term2 = t;
389                AW_pos y = y1; y1 = y2; y2 = y;
390            }
391        }
392
393        int do_above = 1; // we have to update terminals between last_term1 and term1
394        int do_below = 1; // we have to update terminals between term2 and last_term2
395
396        ED4_terminal *term = term1;
397
398        bool xRangeChanged = block.get_range_according_to_type() != last_range;
399
400        while (term) {
401            if (term->is_sequence_terminal()) {
402                ED4_sequence_terminal *seq_term = term->to_sequence_terminal();
403
404                if (seq_term==last_term1) {
405                    do_above = 0;
406                }
407                if (seq_term==last_term2) {
408                    do_below = 0;
409                }
410
411                ED4_species_name_terminal *name_term = seq_term->corresponding_species_name_terminal();
412                ED4_species_manager *species_man = name_term->containing_species_manager();
413                if (species_man->is_selected()) { // already selected
414                    if (xRangeChanged) {
415                        col_block_refresh_on_seq_term(seq_term);
416                    }
417                }
418                else { // select it
419                    if (!species_man->is_consensus_manager()) {
420                        ED4_ROOT->add_to_selected(name_term);
421                    }
422                }
423            }
424            if (term==term2) {
425                break;
426            }
427            term = term->get_next_terminal();
428        }
429
430        if (!initial_call) {
431            if (do_below) {
432                while (term) {
433                    if (term->is_species_name_terminal()) {
434                        ED4_species_manager *species_man = term->containing_species_manager();
435                        if (species_man->is_selected() && !species_man->is_consensus_manager()) {
436                            ED4_ROOT->remove_from_selected(term->to_species_name_terminal());
437                        }
438                    }
439                    if (term==last_term2) break;
440                    term = term->get_next_terminal();
441                }
442            }
443
444            if (do_above) {
445                term = last_term1->corresponding_species_name_terminal();
446                while (term && term!=term1) {
447                    if (term->is_species_name_terminal()) {
448                        ED4_species_manager *species_man = term->containing_species_manager();
449                        if (species_man->is_selected() && !species_man->is_consensus_manager()) {
450                            ED4_ROOT->remove_from_selected(term->to_species_name_terminal());
451                        }
452                    }
453                    term = term->get_next_terminal();
454                }
455            }
456        }
457    }
458
459    last_term1 = term1;
460    last_term2 = term2;
461    last_range = block.get_range_according_to_type();
462}
463
464void ED4_setColumnblockCorner(AW_event *event, ED4_sequence_terminal *seq_term) {
465    static ED4_sequence_terminal *fix_term = NULp;
466    static ED4_index              fix_pos  = 0;
467
468    ED4_index seq_pos;
469    {
470        AW_pos termw_x, termw_y;
471        seq_term->calc_world_coords(&termw_x, &termw_y);
472
473        ED4_index scr_pos = ED4_ROOT->pixel2pos(event->x - termw_x);
474        ED4_remap *remap = ED4_ROOT->root_group_man->remap();
475        seq_pos = remap->screen_to_sequence(scr_pos);
476    }
477
478    switch (event->type) {
479        case AW_Mouse_Press: {
480            if (block.get_type() == ED4_BT_NOBLOCK) { // initial columnblock
481                if (!seq_term->is_consensus_terminal()) {
482                    block.set_type(ED4_BT_COLUMNBLOCK);
483
484                    fix_term = seq_term;
485                    fix_pos = seq_pos;
486
487                    select_and_update(fix_term, seq_term, fix_pos, seq_pos, 1);
488                }
489            }
490            else if (block.get_type()==ED4_BT_LINEBLOCK) { // change lineblock to columnblock
491                block.set_type(ED4_BT_MODIFIED_COLUMNBLOCK);
492
493                fix_term = seq_term;
494                if (seq_pos<(MAXSEQUENCECHARACTERLENGTH/2)) { // in first half of sequence
495                    fix_pos = MAXSEQUENCECHARACTERLENGTH;
496                }
497                else {
498                    fix_pos = 0;
499                }
500
501                select_and_update(fix_term, seq_term, fix_pos, seq_pos, 1);
502            }
503            else { // expand columnblock (search nearest corner/border -> fix opposite corner/border)
504                e4_assert(block.get_type()==ED4_BT_COLUMNBLOCK || block.get_type()==ED4_BT_MODIFIED_COLUMNBLOCK);
505
506                ED4_selected_elem *listElem = ED4_ROOT->selected_objects->head();
507                e4_assert(listElem);
508
509                if (block.get_type()==ED4_BT_COLUMNBLOCK) {
510                    AW_pos                     min_term_y = LONG_MAX;
511                    AW_pos                     max_term_y = LONG_MIN;
512                    ED4_species_name_terminal *min_term   = NULp;
513                    ED4_species_name_terminal *max_term   = NULp;
514                    AW_pos                     xpos, ypos;
515
516                    while (listElem) {
517                        ED4_species_name_terminal *name_term = listElem->elem()->object;
518                        name_term->calc_world_coords(&xpos, &ypos);
519
520                        if (ypos<min_term_y) {
521                            min_term_y = ypos;
522                            min_term = name_term;
523                        }
524                        if (ypos>max_term_y) {
525                            max_term_y = ypos;
526                            max_term = name_term;
527                        }
528
529                        listElem = listElem->next();
530                    }
531
532                    seq_term->calc_world_coords(&xpos, &ypos);
533                    ED4_species_name_terminal *fix_name_term;
534                    if (fabs(ypos-min_term_y)<fabs(ypos-max_term_y)) { // seq_term is closer to min_term
535                        fix_name_term = max_term; // select max_term as fixed corner
536                    }
537                    else {
538                        fix_name_term = min_term;
539                    }
540                    e4_assert(fix_name_term);
541                    fix_term = fix_name_term->corresponding_sequence_terminal();
542                }
543
544                AW_screen_area area_rect;
545                {
546                    AW_pos ex = event->x;
547                    AW_pos ey = event->y;
548                    current_ed4w()->world_to_win_coords(&ex, &ey);
549
550                    if (ED4_ROOT->get_area_rectangle(&area_rect, ex, ey)!=ED4_R_OK) {
551                        e4_assert(0);
552                        break;
553                    }
554                }
555
556
557                const ED4_remap *rm = ED4_ROOT->root_group_man->remap();
558
559                PosRange screen_range = rm->clip_screen_range(seq_term->calc_interval_displayed_in_rectangle(&area_rect));
560                int      scr_pos      = rm->sequence_to_screen(seq_pos);
561
562                PosRange block_screen_range = rm->sequence_to_screen(block.get_colblock_range());
563                PosRange block_visible_part = intersection(screen_range, block_screen_range);
564               
565                if (block_visible_part.is_empty()) {
566                    if (scr_pos>block_screen_range.end()) {
567                        fix_pos = block.get_colblock_range().start();
568                    }
569                    else {
570                        e4_assert(scr_pos<block_screen_range.start());
571                        fix_pos = block.get_colblock_range().end();
572                    }
573                }
574                else {
575                    int dist_left  = abs(scr_pos-block_visible_part.start());
576                    int dist_right = abs(scr_pos-block_visible_part.end());
577
578                    if (dist_left < dist_right) {   // click nearer to left border of visible part of block
579                        fix_pos = block.get_colblock_range().end(); // keep right block-border
580                    }
581                    else {
582                        fix_pos = block.get_colblock_range().start();
583                    }
584                }
585
586                select_and_update(fix_term, seq_term, fix_pos, seq_pos, 0);
587            }
588            break;
589        }
590        case AW_Mouse_Drag: {
591            select_and_update(fix_term, seq_term, fix_pos, seq_pos, 0);
592            break;
593        }
594        case AW_Mouse_Release: {
595            select_and_update(fix_term, seq_term, fix_pos, seq_pos, 0);
596            break;
597        }
598        default: {
599            e4_assert(0);
600            break;
601        }
602    }
603}
604
605// --------------------------------------------------------------------------------
606//      Replace
607
608inline bool matchesUsingWildcard(GB_CSTR s1, GB_CSTR s2, int len) {
609    // s2 may contain '?' as wildcard
610    int cmp = 0;
611
612    for (int i = 0; i<len && !cmp; ++i) {
613        cmp = int(s1[i])-int(s2[i]);
614        if (cmp && s2[i] == '?') cmp = 0;
615    }
616
617    return !cmp;
618}
619
620class replace_op : public ED4_block_operator { // derived from Noncopyable
621    const char *oldString;
622    const char *newString;
623   
624    int olen;
625    int nlen;
626
627public:
628    replace_op(const char *oldString_, const char *newString_)
629        : oldString(oldString_),
630          newString(newString_)
631    {
632        olen = strlen(oldString);
633        nlen = strlen(newString);
634    }
635
636    char* operate(const SeqPart& part, int& new_len) const OVERRIDE {
637        int maxlen;
638        int len   = part.length();
639        int max_o = len-olen;
640
641        if (nlen<=olen) {
642            maxlen = len;
643        }
644        else {
645            maxlen = (len/olen+1)*nlen;
646        }
647
648        char *new_seq  = ARB_alloc<char>(maxlen+1);
649        int   replaced = 0;
650        int   o        = 0;
651        int   n        = 0;
652
653        const char *sequence = part.data();
654        while (o<len) {
655            if (o <= max_o && matchesUsingWildcard(sequence+o, oldString, olen)) {
656                memcpy(new_seq+n, newString, nlen);
657                n += nlen;
658                o += olen;
659                replaced++;
660            }
661            else {
662                new_seq[n++] = sequence[o++];
663            }
664        }
665        new_seq[n] = 0;
666
667        if (replaced) {
668            new_len = n;
669        }
670        else {
671            freenull(new_seq);
672        }
673
674        return new_seq;
675    }
676};
677
678static void replace_in_block(AW_window*) {
679    AW_root *awr = ED4_ROOT->aw_root;
680    ED4_with_whole_block(
681        replace_op(awr->awar(ED4_AWAR_REP_SEARCH_PATTERN)->read_char_pntr(),
682                   awr->awar(ED4_AWAR_REP_REPLACE_PATTERN)->read_char_pntr()));
683}
684
685AW_window *ED4_create_replace_window(AW_root *root) {
686    AW_window_simple *aws = new AW_window_simple;
687
688    aws->init(root, "REPLACE", "Search & Replace");
689    aws->load_xfig("edit4/replace.fig");
690
691    aws->at("close");
692    aws->callback(AW_POPDOWN);
693    aws->create_button("CLOSE", "Close", "C");
694
695    aws->at("help");
696    aws->callback(makeHelpCallback("e4_replace.hlp"));
697    aws->create_button("HELP", "Help", "H");
698
699    aws->at("spattern");
700    aws->create_input_field(ED4_AWAR_REP_SEARCH_PATTERN, 30);
701
702    aws->at("rpattern");
703    aws->create_input_field(ED4_AWAR_REP_REPLACE_PATTERN, 30);
704
705    aws->at("go");
706    aws->callback(replace_in_block);
707    aws->create_button("GO", "Go", "G");
708
709    return aws;
710}
711
712// --------------------------------------------------------------------------------
713//      Other block operations
714
715inline char *dont_return_unchanged(char *result, int& new_len, const SeqPart& part) {
716    if (result) {
717        if (new_len == part.length()) {
718            if (memcmp(result, part.data(), new_len) == 0) {
719                freenull(result);
720            }
721        }
722    }
723    return result;
724}
725
726class case_op : public ED4_block_operator {
727    bool to_upper;
728public:
729    case_op(bool to_upper_) : to_upper(to_upper_) {}
730
731    char *operate(const SeqPart& part, int& new_len) const OVERRIDE {
732        int         len     = part.length();
733        const char *seq     = part.data();
734        char       *new_seq = ARB_alloc<char>(len+1);
735
736        if (to_upper) {
737            for (int i=0; i<len; i++) new_seq[i] = toupper(seq[i]);
738        }
739        else {
740            for (int i=0; i<len; i++) new_seq[i] = tolower(seq[i]);
741        }
742        new_seq[len] = 0;
743
744        new_len = len;
745        return dont_return_unchanged(new_seq, new_len, part);
746    }
747};
748
749class revcomp_op : public ED4_block_operator {
750    bool reverse;
751    bool complement;
752    char T_or_U;
753
754public:
755    revcomp_op(GB_alignment_type aliType, bool reverse_, bool complement_)
756        : reverse(reverse_),
757          complement(complement_),
758          T_or_U(0)
759    {
760        if (complement) {
761            error = GBT_determine_T_or_U(aliType, &T_or_U, reverse ? "reverse-complement" : "complement");
762        }
763    }
764
765    char *operate(const SeqPart& part, int& new_len) const OVERRIDE {
766        char *result = NULp;
767        if (!error) {
768            int len = part.length();
769            if (complement) {
770                result = GBT_complementNucSequence(part.data(), len, T_or_U);
771                if (reverse) freeset(result, GBT_reverseNucSequence(result, len));
772            }
773            else if (reverse) result = GBT_reverseNucSequence(part.data(), len);
774
775            new_len = len;
776        }
777        return dont_return_unchanged(result, new_len, part);
778    }
779
780};
781
782class unalign_op : public ED4_block_operator {
783    int direction;
784public:
785    unalign_op(int direction_) : direction(direction_) {}
786
787    char *operate(const SeqPart& part, int& new_len) const OVERRIDE {
788        int         len    = part.length();
789        const char *seq    = part.data();
790        char       *result = ARB_alloc<char>(len+1);
791
792        int o = 0;
793        int n = 0;
794
795        while (o<len) {
796            if (!ED4_is_gap_character(seq[o])) result[n++] = seq[o];
797            o++;
798        }
799
800        if (n<len) { // (move and) dot rest
801            int gapcount = len-n;
802            switch (direction) {
803                case 1: // rightwards
804                    memmove(result+gapcount, result, n);
805                    memset(result, part.left_gap(), gapcount);
806                    break;
807                case 0: { // center
808                    int leftgaps  = gapcount/2;
809                    int rightgaps = gapcount-leftgaps;
810
811                    memmove(result+leftgaps, result, n);
812                    memset(result, part.left_gap(), leftgaps);
813                    memset(result+leftgaps+n, part.right_gap(), rightgaps);
814                   
815                    break;
816                }
817                case -1: // leftwards
818                    memset(result+n, part.right_gap(), gapcount);
819                    break;
820            }
821        }
822        result[len] = 0;
823        new_len     = len;
824
825        return dont_return_unchanged(result, new_len, part);
826    }
827
828
829};
830
831class shift_op : public ED4_block_operator {
832    int direction;
833
834    char *shift_left_sequence(const SeqPart& part, int& new_len) const {
835        char       *result = NULp;
836        const char *seq    = part.data();
837
838        if (!ED4_is_gap_character(seq[0])) {
839            error = "Need a gap at block start for shifting left";
840        }
841        else {
842            int len       = part.length();
843            ARB_alloc(result, len+1);
844            result[len]   = 0;
845            new_len       = len;
846            result[len-1] = part.right_gap();
847            memcpy(result, seq+1, len-1);
848        }
849        return result;
850    }
851
852    char *shift_right_sequence(const SeqPart& part, int& new_len) const {
853        char       *result = NULp;
854        const char *seq    = part.data();
855        int         len    = part.length();
856
857        if (!ED4_is_gap_character(seq[len-1])) {
858            error = "Need a gap at block end for shifting right";
859        }
860        else {
861            ARB_alloc(result, len+1);
862            result[len] = 0;
863            new_len     = len;
864            result[0]   = part.left_gap();
865            memcpy(result+1, seq, len-1);
866        }
867        return result;
868    }
869
870   
871public:
872    shift_op(int direction_) : direction(direction_) {}
873
874    char *operate(const SeqPart& part, int& new_len) const OVERRIDE {
875        char *result = direction<0
876            ? shift_left_sequence(part, new_len)
877            : shift_right_sequence(part, new_len);
878        return dont_return_unchanged(result, new_len, part);
879    }
880};
881
882void ED4_perform_block_operation(ED4_blockoperation_type operationType) {
883    switch (operationType) {
884        case ED4_BO_UPPER_CASE: ED4_with_whole_block(case_op(true));  break;
885        case ED4_BO_LOWER_CASE: ED4_with_whole_block(case_op(false)); break;
886
887        case ED4_BO_REVERSE:            ED4_with_whole_block(revcomp_op(ED4_ROOT->alignment_type, true,  false)); break;
888        case ED4_BO_COMPLEMENT:         ED4_with_whole_block(revcomp_op(ED4_ROOT->alignment_type, false, true));  break;
889        case ED4_BO_REVERSE_COMPLEMENT: ED4_with_whole_block(revcomp_op(ED4_ROOT->alignment_type, true,  true));  break;
890
891        case ED4_BO_UNALIGN_LEFT:   ED4_with_whole_block(unalign_op(-1)); break;
892        case ED4_BO_UNALIGN_CENTER: ED4_with_whole_block(unalign_op(0));  break;
893        case ED4_BO_UNALIGN_RIGHT:  ED4_with_whole_block(unalign_op(1));  break;
894
895        case ED4_BO_SHIFT_LEFT:  ED4_with_whole_block(shift_op(-1)); break;
896        case ED4_BO_SHIFT_RIGHT: ED4_with_whole_block(shift_op(1));  break;
897
898        default: {
899            e4_assert(0);
900            break;
901        }
902    }
903}
904
905// --------------------------------------
906//      modify range of selected sai
907
908#define AWAR_MOD_SAI_SCRIPT "modsai/script"
909
910static void modsai_cb(AW_window *aww) {
911    ED4_MostRecentWinContext context;
912
913    ED4_cursor *cursor = &current_cursor();
914    GB_ERROR    error  = NULp;
915
916    if (!cursor->in_SAI_terminal()) {
917        error = "Please select the SAI you like to modify";
918    }
919    else if (block.get_type() == ED4_BT_NOBLOCK) {
920        error = "Please select the range where the SAI shall be modified";
921    }
922    else {
923        AW_root    *aw_root = aww->get_root();
924        char       *script  = aw_root->awar(AWAR_MOD_SAI_SCRIPT)->read_string();
925        const char *sainame = aw_root->awar(AWAR_SAI_NAME)->read_char_pntr();
926
927        GB_transaction ta(GLOBAL_gb_main);
928        GBDATA *gb_sai = GBT_find_SAI(GLOBAL_gb_main, sainame);
929
930        if (!gb_sai) {
931            error = GB_have_error()
932                ? GB_await_error()
933                : GBS_global_string("Failed to find SAI '%s'", sainame);
934        }
935        else {
936            GBDATA *gb_data = GBT_find_sequence(gb_sai, ED4_ROOT->alignment_name);
937
938            if (!gb_data) error = GB_await_error();
939            else {
940                char *seq = GB_read_string(gb_data);
941                int   len = GB_read_string_count(gb_data);
942
943                ExplicitRange  range(block.get_range_according_to_type(), len);
944                char          *part = range.dup_corresponding_part(seq, len);
945
946                char *result       = GB_command_interpreter_in_env(part, script, GBL_simple_call_env(gb_sai));
947                if (!result) error = GB_await_error();
948                else {
949                    int reslen   = strlen(result);
950                    if (reslen>range.size()) {
951                        error = GBS_global_string("Cannot insert modified range (result too long; %i>%i)", reslen, range.size());
952                    }
953                    else {
954                        memcpy(seq+range.start(), result, reslen);
955                        int rest = range.size()-reslen;
956                        if (rest>0) memset(seq+range.start()+reslen, '-', rest);
957
958                        error = GB_write_string(gb_data, seq);
959                    }
960                    free(result);
961                }
962
963                free(part);
964                free(seq);
965            }
966        }
967        free(script);
968        error = ta.close(error);
969    }
970
971    aw_message_if(error);
972}
973
974AW_window *ED4_create_modsai_window(AW_root *root) {
975    root->awar_string(AWAR_MOD_SAI_SCRIPT)->write_string("");
976
977    AW_window_simple *aws = new AW_window_simple;
978    aws->init(root, "modsai", "Modify SAI range");
979    aws->load_xfig("edit4/modsai.fig");
980
981    aws->at("close");
982    aws->callback(AW_POPDOWN);
983    aws->create_button("CLOSE", "Close", "C");
984
985    aws->at("help");
986    aws->callback(makeHelpCallback("e4_modsai.hlp"));
987    aws->create_button("HELP", "Help", "H");
988
989    AW_selection_list *sellist = awt_create_selection_list_with_input_field(aws, AWAR_MOD_SAI_SCRIPT, "box", "script");
990    GB_ERROR           error;
991    {
992        StorableSelectionList storable_sellist(TypedSelectionList("sellst", sellist, "SRT/ACI scripts", "srt_aci"));
993        error = storable_sellist.load(GB_path_in_ARBLIB("sellists/mod_sequence*.sellst"), false);
994    }
995    aw_message_if(error);
996
997    aws->at("go");
998    aws->callback(modsai_cb);
999    aws->create_button("go", "GO");
1000
1001    return aws;
1002}
1003
1004
1005// --------------------------------------------------------------------------------
1006
1007#ifdef UNIT_TESTS
1008#ifndef TEST_UNIT_H
1009#include <test_unit.h>
1010#endif
1011
1012static 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) {
1013    int      whole_len = strlen(oversized_input);
1014    SeqPart  part(oversized_input, 1, whole_len-2);
1015    int      new_len   = 0;
1016    char    *result    = blockop.operate(part, new_len);
1017
1018    using namespace arb_test;
1019
1020    expectation_group expectations;
1021    expectations.add(part_of_error
1022                     ? that(blockop.get_error()).does_contain(part_of_error)
1023                     : that(blockop.get_error()).is_equal_to_NULL());
1024    expectations.add(that(result).is_equal_to(expected_result));
1025    if (expected_result) expectations.add(that(new_len).is_equal_to(whole_len-2));
1026
1027    free(result);
1028
1029    return all().ofgroup(expectations);
1030}
1031
1032#define TEST_EXPECT_BLOCKOP_PERFORMS(oversized_input,blockop,expected)         TEST_EXPECTATION(blockop_expected_io(blockop, oversized_input, expected, NULp))
1033#define TEST_EXPECT_BLOCKOP_PERFORMS__BROKEN(oversized_input,blockop,expected) TEST_EXPECTATION__BROKEN(blockop_expected_io(blockop, oversized_input, expected, NULp))
1034#define TEST_EXPECT_BLOCKOP_ERRORHAS(oversized_input,blockop,expected)         TEST_EXPECTATION(blockop_expected_io(blockop, oversized_input, NULp, expected))
1035#define TEST_EXPECT_BLOCKOP_ERRORHAS__BROKEN(oversized_input,blockop,expected) TEST_EXPECTATION__BROKEN(blockop_expected_io(blockop, oversized_input, NULp, expected))
1036
1037void TEST_block_operators() {
1038    ED4_setup_gaps_and_alitype("-.", GB_AT_RNA);
1039
1040    // Note: make sure tests perform an identity block operation at least once for each operator
1041
1042    // replace_op
1043    TEST_EXPECT_BLOCKOP_PERFORMS("-A-C--", replace_op("-",  "."),  "A.C.");
1044    TEST_EXPECT_BLOCKOP_PERFORMS("-A-C--", replace_op("?",  "."),  "....");
1045    TEST_EXPECT_BLOCKOP_PERFORMS("AACAG-", replace_op("AC", "CA"), "CAAG");
1046    TEST_EXPECT_BLOCKOP_PERFORMS("-ACAG-", replace_op("A?", "Ax"), "AxAx");
1047
1048    TEST_EXPECT_BLOCKOP_PERFORMS("GACAG-", replace_op("GA", "AG"), NULp);   // unchanged
1049    TEST_EXPECT_BLOCKOP_PERFORMS("GAGAGA", replace_op("GA", "AG"), "AAGG");
1050    TEST_EXPECT_BLOCKOP_PERFORMS("GACAGA", replace_op("GA", "AG"), NULp);
1051    TEST_EXPECT_BLOCKOP_PERFORMS("AGAGAG", replace_op("GA", "AG"), "AGAG");
1052
1053    // case_op
1054    TEST_EXPECT_BLOCKOP_PERFORMS("-AcGuT-", case_op(true), "ACGUT");
1055    TEST_EXPECT_BLOCKOP_PERFORMS("-AcGuT-", case_op(false), "acgut");
1056    TEST_EXPECT_BLOCKOP_PERFORMS("-acgut-", case_op(false), NULp);
1057    TEST_EXPECT_BLOCKOP_PERFORMS("-------", case_op(false), NULp);
1058
1059    // revcomp_op
1060    TEST_EXPECT_BLOCKOP_PERFORMS("-Ac-GuT-", revcomp_op(GB_AT_RNA, false, false), NULp);     // noop
1061    TEST_EXPECT_BLOCKOP_PERFORMS("-Ac-GuT-", revcomp_op(GB_AT_RNA, true,  false), "TuG-cA");
1062    TEST_EXPECT_BLOCKOP_PERFORMS("-Ac-GuT-", revcomp_op(GB_AT_RNA, false, true),  "Ug-CaA");
1063    TEST_EXPECT_BLOCKOP_PERFORMS("-Ac-GuT-", revcomp_op(GB_AT_RNA, true,  true),  "AaC-gU");
1064    TEST_EXPECT_BLOCKOP_PERFORMS("-Ac-GuT-", revcomp_op(GB_AT_DNA, true,  true),  "AaC-gT");
1065    TEST_EXPECT_BLOCKOP_PERFORMS("-AcGCgT-", revcomp_op(GB_AT_DNA, true,  true),  NULp);
1066    TEST_EXPECT_BLOCKOP_PERFORMS("--------", revcomp_op(GB_AT_DNA, true,  true),  NULp);
1067
1068    TEST_EXPECT_BLOCKOP_PERFORMS("-AR-DQF-", revcomp_op(GB_AT_AA, false, false), NULp); // noop
1069    TEST_EXPECT_BLOCKOP_PERFORMS("-AR-DQF-", revcomp_op(GB_AT_AA, true,  false), "FQD-RA");
1070    TEST_EXPECT_BLOCKOP_ERRORHAS("-AR-DQF-", revcomp_op(GB_AT_AA, false, true),  "complement not available");
1071    TEST_EXPECT_BLOCKOP_ERRORHAS("-AR-DQF-", revcomp_op(GB_AT_AA, true,  true),  "reverse-complement not available");
1072
1073    // unalign_op
1074    TEST_EXPECT_BLOCKOP_PERFORMS("-A-c-G--T-", unalign_op(-1), "AcGT----");
1075    TEST_EXPECT_BLOCKOP_PERFORMS("-A-c-G-T-T", unalign_op(-1), "AcGT----");
1076    TEST_EXPECT_BLOCKOP_PERFORMS("-A-c-G--TT", unalign_op(-1), "AcGT----");
1077    TEST_EXPECT_BLOCKOP_PERFORMS("-A-c-G--T.", unalign_op(-1), "AcGT....");
1078    TEST_EXPECT_BLOCKOP_PERFORMS("-A-c-G-T.T", unalign_op(-1), "AcGT....");
1079
1080    TEST_EXPECT_BLOCKOP_PERFORMS("A-Ac-G--T-", unalign_op(+1), "----AcGT");
1081    TEST_EXPECT_BLOCKOP_PERFORMS("-A-c-G--T-", unalign_op(+1), "----AcGT");
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("AA-c-G--T-", unalign_op(+1), "----AcGT");
1085
1086    TEST_EXPECT_BLOCKOP_PERFORMS("AA-c-G--TT", unalign_op(0), "--AcGT--");
1087    TEST_EXPECT_BLOCKOP_PERFORMS(".A-c-G--T-", unalign_op(0), "..AcGT--");
1088    TEST_EXPECT_BLOCKOP_PERFORMS(".A-c-G--T.", unalign_op(0), "..AcGT..");
1089    TEST_EXPECT_BLOCKOP_PERFORMS("-A-c-G--T.", unalign_op(0), "--AcGT..");
1090    TEST_EXPECT_BLOCKOP_PERFORMS("-A-c-Gc-T.", unalign_op(0), "-AcGcT..");
1091
1092    TEST_EXPECT_BLOCKOP_PERFORMS("-AcGcT.",  unalign_op(0), NULp);
1093    TEST_EXPECT_BLOCKOP_PERFORMS("-AcGcT..", unalign_op(0), NULp);
1094    TEST_EXPECT_BLOCKOP_PERFORMS("--AcGcT.", unalign_op(0), "AcGcT.");
1095
1096    TEST_EXPECT_BLOCKOP_PERFORMS("-ACGT-", unalign_op(-1), NULp);
1097    TEST_EXPECT_BLOCKOP_PERFORMS("-ACGT-", unalign_op(+1), NULp);
1098    TEST_EXPECT_BLOCKOP_PERFORMS("------", unalign_op(+1), NULp);
1099
1100    // shift_op
1101    TEST_EXPECT_BLOCKOP_PERFORMS("-A-C--", shift_op(+1), "-A-C"); // take gap outside region
1102    TEST_EXPECT_BLOCKOP_PERFORMS(".A-C--", shift_op(+1), ".A-C"); // -"-
1103    TEST_EXPECT_BLOCKOP_PERFORMS("-.AC--", shift_op(+1), "..AC"); // take gap inside region
1104
1105    TEST_EXPECT_BLOCKOP_PERFORMS("--A-C-", shift_op(-1), "A-C-"); // same for other direction
1106    TEST_EXPECT_BLOCKOP_PERFORMS("--A-C.", shift_op(-1), "A-C.");
1107    TEST_EXPECT_BLOCKOP_PERFORMS("--AC..", shift_op(-1), "AC..");
1108    TEST_EXPECT_BLOCKOP_PERFORMS("------", shift_op(-1), NULp);
1109
1110    TEST_EXPECT_BLOCKOP_PERFORMS("G-TTAC", shift_op(-1), "TTA-"); // no gap reachable
1111
1112    TEST_EXPECT_BLOCKOP_ERRORHAS("GATTAC", shift_op(-1), "Need a gap at block");  // no space
1113    TEST_EXPECT_BLOCKOP_ERRORHAS("GATTAC", shift_op(+1), "Need a gap at block");  // no space
1114}
1115
1116#endif // UNIT_TESTS
1117
1118// --------------------------------------------------------------------------------
1119
Note: See TracBrowser for help on using the repository browser.