source: tags/ms_r16q3/EDIT4/ED4_block.cxx

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