source: tags/arb-6.0-rc1/EDIT4/ED4_block.cxx

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