source: tags/arb_5.1/EDIT4/ED4_search.cxx

Last change on this file was 5885, checked in by westram, 15 years ago
  • moved fast-aligner to separate library
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 56.0 KB
Line 
1#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4#include <limits.h>
5#include <ctype.h>
6#include <errno.h>
7
8#include <arbdb.h>
9#include <arbdbt.h>
10#include <aw_root.hxx>
11#include <aw_keysym.hxx>
12#include <aw_window.hxx>
13#include <aw_awars.hxx>
14#include <aw_global.hxx>
15#include <fast_aligner.hxx>
16#include <awt.hxx>
17#include <awt_config_manager.hxx>
18
19#include <ed4_extern.hxx>
20#include "ed4_awars.hxx"
21#include "ed4_class.hxx"
22#include "ed4_tools.hxx"
23
24static int result_counter      = 0;
25static int ignore_more_results = false;
26
27const char *ED4_SearchPositionTypeId[SEARCH_PATTERNS+1] =
28{
29    "User1", "User2",
30    "Probe",
31    "Primer (local)", "Primer (region)", "Primer (global)",
32    "Signature (local)", "Signature (region)", "Signature (global)",
33    "Any"
34};
35
36// --------------------------------------------------------------------------------
37
38typedef struct S_SearchAwarList // contains names of awars
39{
40    const char *pattern,
41        *min_mismatches,
42        *max_mismatches,
43        *case_sensitive,
44        *tu,
45        *pat_gaps,
46        *seq_gaps,
47        *reverse,
48        *complement,
49        *exact,
50        *show,
51        *openFolded,
52        *autoJump;
53
54} *SearchAwarList;
55
56class SearchSettings
57{
58    char            *pattern;
59    int              min_mismatches;
60    int              max_mismatches;
61    ED4_SEARCH_CASE  case_sensitive;
62    ED4_SEARCH_TU    tu;
63    ED4_SEARCH_GAPS  pat_gaps;
64    ED4_SEARCH_GAPS  seq_gaps;
65    int              reverse;
66    int              complement;
67    int              exact;
68    int              open_folded;
69    int              autoJump;
70
71    SearchSettings(const SearchSettings&) { e4_assert(0); } // forbidden
72
73public:
74
75    void update(SearchAwarList awarList)
76    {
77        AW_root *root = ED4_ROOT->aw_root;
78
79        freeset(pattern, root->awar(awarList->pattern)->read_string());
80
81        min_mismatches = root->awar(awarList->min_mismatches)->read_int();
82        max_mismatches = root->awar(awarList->max_mismatches)->read_int();
83        case_sensitive = ED4_SEARCH_CASE(root->awar(awarList->case_sensitive)->read_int());
84        tu             = ED4_SEARCH_TU(root->awar(awarList->tu)->read_int());
85        pat_gaps       = ED4_SEARCH_GAPS(root->awar(awarList->pat_gaps)->read_int());
86        seq_gaps       = ED4_SEARCH_GAPS(root->awar(awarList->seq_gaps)->read_int());
87        open_folded    = root->awar(awarList->openFolded)->read_int();
88        autoJump       = root->awar(awarList->autoJump)->read_int();
89        reverse        = root->awar(awarList->reverse)->read_int();
90        complement     = root->awar(awarList->complement)->read_int();
91        exact          = root->awar(awarList->exact)->read_int();
92
93        if (complement) {
94            if (IS_AMINO()) {
95                complement = 0;
96                root->awar(awarList->complement)->write_int(0);
97                aw_question(GBS_global_string("Search for complement is not supported for this alignment type"), "Disable");
98            }
99        }
100    }
101
102    SearchSettings(SearchAwarList awarList)     { pattern = 0; update(awarList); }
103    ~SearchSettings()                           { delete pattern; }
104
105    GB_CSTR get_pattern() const                 { return pattern; }
106    int get_min_mismatches() const              { return min_mismatches; }
107    int get_max_mismatches() const              { return max_mismatches; }
108    ED4_SEARCH_CASE get_case_sensitive() const  { return case_sensitive; }
109    ED4_SEARCH_TU get_tu() const                { return tu; }
110    ED4_SEARCH_GAPS get_pat_gaps() const        { return pat_gaps; }
111    ED4_SEARCH_GAPS get_seq_gaps() const        { return seq_gaps; }
112    int get_open_folded() const                 { return open_folded; }
113    int get_autoJump() const                    { return autoJump; }
114    int get_reverse() const                     { return reverse; }
115    int get_complement() const                  { return complement; }
116    int get_exact() const                       { return exact; }
117};
118
119// --------------------------------------------------------------------------------
120
121class SearchTree;
122typedef void (*reportMatch)(int start, int end, GB_CSTR comment, int mismatches[MAX_MISMATCHES]);
123
124class SearchTreeNode
125{
126    char            c;          // character
127    SearchTreeNode *son;        // son != 0 (exception: FOUND)
128    SearchTreeNode *brother;
129    char           *comment;    // 0 or comment given in search pattern
130
131    static SearchTreeNode  FOUND;
132    static int             start_offset;
133    static reportMatch     report;
134    static int             min_mismatches;
135    static int             max_mismatches;
136    static int            *uni2real; // transform unified positions to real sequence positions
137
138    SearchTreeNode(const SearchTreeNode &) { e4_assert(0); } // forbidden
139
140public:
141
142    SearchTreeNode(GB_CSTR pattern, GB_CSTR comment);
143    ~SearchTreeNode();
144
145    SearchTreeNode *insert_unified_pattern(GB_CSTR pattern, GB_CSTR pattern_comment);
146    void findMatches(int off, GB_CSTR seq, int len, int mismatches, int mismatch_list[MAX_MISMATCHES]);
147
148    // you must call the following functions before calling findMatches():
149
150    static void set_start_offset(int off)               { start_offset = off; }
151    static void set_report(reportMatch r, int *u2r)     { report = r; uni2real = u2r; }
152    static void set_mismatches(int minMis, int maxMis) 
153    { 
154        e4_assert(maxMis <= MAX_MISMATCHES);
155        e4_assert(minMis <= maxMis);
156        min_mismatches = minMis; 
157        max_mismatches = maxMis; 
158    }
159};
160
161// --------------------------------------------------------------------------------
162
163SearchTreeNode  SearchTreeNode::FOUND(0, 0);
164int             SearchTreeNode::start_offset;
165reportMatch     SearchTreeNode::report;
166int             SearchTreeNode::min_mismatches;
167int             SearchTreeNode::max_mismatches;
168int            *SearchTreeNode::uni2real;
169
170SearchTreeNode::SearchTreeNode(GB_CSTR pattern, GB_CSTR pattern_comment)
171{
172    comment = 0;
173    if (pattern) {
174        e4_assert(pattern[0]);
175        c = pattern[0];
176        if (pattern[1]) {
177            son = new SearchTreeNode(pattern+1, pattern_comment);
178        }
179        else {
180            son = &FOUND;
181            comment = nulldup(pattern_comment);
182        }
183    }
184    else {
185        e4_assert(this==&FOUND);
186        c = 0;
187        son = 0;
188        comment = nulldup(pattern_comment);
189    }
190    brother = 0;
191}
192
193SearchTreeNode::~SearchTreeNode()
194{
195    if (brother!=&FOUND) delete brother;
196    if (son!=&FOUND) delete son;
197    delete comment;
198}
199
200
201SearchTreeNode *SearchTreeNode::insert_unified_pattern(GB_CSTR pattern, GB_CSTR pattern_comment)
202{
203    if (!this) {
204        if (pattern[0]) {
205            return new SearchTreeNode(pattern, pattern_comment);
206        }
207
208        return &FOUND;
209    }
210
211    if (this==&FOUND) {
212        if (pattern[0]) {
213            SearchTreeNode *neu = new SearchTreeNode(pattern, pattern_comment);
214
215            neu->brother = &FOUND;
216            return neu;
217        }
218        return &FOUND;
219    }
220
221    e4_assert(c);
222
223    if (pattern[0]) { // pattern contains sth.
224        if (c==pattern[0]) {
225            e4_assert(son);
226            son = son->insert_unified_pattern(pattern+1, pattern_comment);
227        }
228        else {
229            if (brother) {
230                brother = brother->insert_unified_pattern(pattern, pattern_comment);
231            }
232            else {
233                brother = new SearchTreeNode(pattern, pattern_comment);
234            }
235        }
236    }
237    else { // empty pattern -> insert FOUND
238        if (brother) {
239            if (brother!=&FOUND) {
240                brother = brother->insert_unified_pattern(pattern, pattern_comment);
241            }
242        }
243        else {
244            brother = &FOUND;
245        }
246    }
247
248    return this;
249}
250
251void SearchTreeNode::findMatches(int off, GB_CSTR seq, int len, int mismatches, int mismatch_list[MAX_MISMATCHES])
252{
253    e4_assert(mismatches < MAX_MISMATCHES);
254    if (len) {
255        int matches = c=='?' || c==seq[0];
256        int use_mismatch = 0;
257
258        if (!matches && mismatches<max_mismatches) {
259            int c_is_gap = c=='-' || c=='.';
260            int seq_is_gap = seq[0]=='-' || seq[0]=='.';
261
262            if (c_is_gap==seq_is_gap) {
263                mismatch_list[mismatches] = uni2real[off];
264                mismatches++;
265                use_mismatch = 1;
266                matches = 1;
267            }
268        }
269
270        if (matches) {
271            if (son==&FOUND) {
272                if (mismatches>=min_mismatches) {
273                    report(uni2real[start_offset], uni2real[off], comment, mismatch_list);
274                }
275            }
276            else {
277                son->findMatches(off+1, seq+1, len-1, mismatches, mismatch_list);
278            }
279
280            if (use_mismatch) {
281                mismatches--;
282                mismatch_list[mismatches] = -1;
283            }
284        }
285    }
286
287    if (brother==&FOUND) {
288        if (mismatches>=min_mismatches) {
289            report(uni2real[start_offset], uni2real[off-1], comment, mismatch_list);
290        }
291    }
292    else if (brother) {
293        brother->findMatches(off, seq, len, mismatches, mismatch_list);
294    }
295}
296
297// --------------------------------------------------------------------------------
298
299class SearchTree
300{
301    const SearchSettings *sett;
302    SearchTreeNode *root;
303    unsigned char unified[256];
304    int shortestPattern;
305
306    static char unify_char(char c, int case_sensitive, int T_equal_U);
307
308    char *unify_str(const char *data, int len, ED4_SEARCH_GAPS gaps, int *new_len, int **uni2real);
309    char *unify_pattern(const char *pattern, int *new_len);
310    char *unify_sequence(const char *sequence, int len, int *new_len, int **uni2real);
311
312    SearchTree(const SearchTree &); // forbidden
313
314public:
315
316    SearchTree(const SearchSettings *s);
317    ~SearchTree();
318
319    void findMatches(const char *seq, int len, reportMatch report);
320    int get_shortestPattern() const { return shortestPattern; }
321};
322
323// --------------------------------------------------------------------------------
324
325static char *shortenString(char *s)
326{
327    char *end = strchr(s, '\0');
328
329    while (end>s && isspace(end[-1])) {
330        *--end = 0;
331    }
332    while (isspace(s[0])) {
333        s++;
334    }
335
336    return s;
337}
338
339static void splitTokComment(char **tok, char **commentP)
340{
341    char *num = strchr(*tok, '#');
342
343    if (num) {
344        num[0] = 0;
345        *commentP = shortenString(num+1);
346    }
347    else {
348        *commentP = 0;
349    }
350
351    *tok = shortenString(*tok);
352}
353
354static char *appendComment(const char *s1, int l1, const char *s2) {
355    if (s1) {
356        int l2 = strlen(s2);
357        char *s = (char*)malloc(l1+1+l2+1);
358
359        sprintf(s, "%s %s", s1, s2);
360        return s;
361    }
362
363    return 0;
364}
365
366SearchTree::SearchTree(const SearchSettings *s)
367{
368    sett = s;
369    root = 0;
370    shortestPattern = INT_MAX;
371
372    {
373        int i;
374        int case_sensitive = (sett->get_case_sensitive()==ED4_SC_CASE_SENSITIVE);
375        int T_equal_U = (sett->get_tu()==ED4_ST_T_EQUAL_U);
376
377        for (i=0; i<256; i++) {
378            unified[i] = unify_char(i, case_sensitive, T_equal_U);
379        }
380    }
381
382#define INSERT_ROOT_PATTERN(tok,com)                        \
383    do {                                                    \
384        if (root) {                                         \
385            root = root->insert_unified_pattern(tok, com);  \
386        }                                                   \
387        else {                                              \
388            root = new SearchTreeNode(tok, com);            \
389        }                                                   \
390    } while(0)
391
392
393    {
394        char       *pattern           = strdup(sett->get_pattern());
395        const char *trenner           = "\n,";
396        char       *tok               = strtok(pattern, trenner);
397        char       *comment;
398        char        T_or_U;
399        GB_ERROR    T_or_U_error      = GBT_determine_T_or_U(ED4_ROOT->alignment_type, &T_or_U, "complement");
400        bool        show_T_or_U_error = false;
401
402        while (tok) {
403            splitTokComment(&tok, &comment);
404            int uni_tok_len;
405            char *uni_tok = unify_pattern(tok, &uni_tok_len);
406
407            if (uni_tok[0]) {
408                int s_exact      = sett->get_exact();
409                int s_reverse    = sett->get_reverse();
410                int s_complement = sett->get_complement();
411
412                if (uni_tok_len<shortestPattern) {
413                    shortestPattern = uni_tok_len;
414                }
415
416                if (!s_exact || (!s_reverse && !s_complement)) {
417                    // insert original search pattern if all patterns shall be used (!s_exact)
418                    // or if neither s_reverse nor s_complement
419                    INSERT_ROOT_PATTERN(uni_tok, comment);
420                }
421                int commentLen = comment ? strlen(comment) : 0;
422
423                if (s_reverse) {
424                    char *reverse = GBT_reverseNucSequence(uni_tok, uni_tok_len);
425
426                    if (!s_exact || !s_complement) {
427                        char *reverseComment = appendComment(comment, commentLen, "(reverse)");
428
429                        INSERT_ROOT_PATTERN(reverse, reverseComment); // insert reverse pattern
430                        free(reverseComment);
431                    }
432                    if (s_complement) {
433                        e4_assert(IS_NUCLEOTIDE());
434                        // char T_or_U = ED4_ROOT->alignment_type==GB_AT_DNA ? 'T' : 'U';
435                        if (T_or_U) {
436                            char *revcomp        = GBT_complementNucSequence(reverse, uni_tok_len, T_or_U);
437                            char *revcompComment = appendComment(comment, commentLen, "(reverse complement)");
438                            char *uni_revcomp    = unify_pattern(revcomp, 0);
439
440                            INSERT_ROOT_PATTERN(uni_revcomp, revcompComment); // insert reverse complement pattern
441
442                            free(uni_revcomp);
443                            free(revcompComment);
444                            free(revcomp);
445                        }
446                        else {
447                            show_T_or_U_error = true; // only show error if it matters
448                        }
449                    }
450
451                    free(reverse);
452                }
453               
454                if (s_complement) {
455                    GB_transaction dummy(GLOBAL_gb_main);
456
457                    if (T_or_U) {
458                        if (!s_exact || !s_reverse) {
459                            char *complement        = GBT_complementNucSequence(uni_tok, uni_tok_len, T_or_U);
460                            char *complementComment = appendComment(comment, commentLen, "(complement)");
461                            char *uni_complement    = unify_pattern(complement, 0);
462
463                            INSERT_ROOT_PATTERN(uni_complement, complementComment); // insert complement pattern
464
465                            free(uni_complement);
466                            free(complementComment);
467                            free(complement);
468                        }
469                    }
470                    else {
471                        show_T_or_U_error = true; // only show error if it matters
472                    }
473                }
474
475            }
476
477            tok = strtok(0, trenner);
478            free(uni_tok);
479        }
480        free(pattern);
481
482        if (show_T_or_U_error && T_or_U_error) aw_message(T_or_U_error);
483    }
484
485#undef INSERT_ROOT_PATTERN
486}
487
488SearchTree::~SearchTree()
489{
490    delete root;
491}
492
493char SearchTree::unify_char(char c, int case_sensitive, int T_equal_U)
494{
495    if (!case_sensitive) {
496        c = tolower(c);
497    }
498
499    if (T_equal_U) {
500        if (c=='t') {
501            c = 'u';
502        }
503        else if (c=='T') {
504            c = 'U';
505        }
506    }
507
508    if (c=='.') {
509        c = '-';
510    }
511
512    return c;
513}
514
515char *SearchTree::unify_str(const char *data, int len, ED4_SEARCH_GAPS gaps, int *new_len, int **uni2real)
516{
517    char *p = (char*)malloc(len+1);
518
519    if (!p) {
520        return 0;
521    }
522
523    char *pp      = p;
524    int   nlen    = 0;
525    int   realPos = 0;
526
527    if (uni2real) {
528        int *u2r = *uni2real;
529
530        if (gaps==ED4_SG_CONSIDER_GAPS) {
531            while(realPos<len) {
532                *pp++ = unified[(unsigned char)data[realPos]];
533                u2r[nlen++] = realPos++;
534            }
535        }
536        else {
537            while(realPos<len) {
538                unsigned char c = data[realPos];
539
540                if (c!='-' && c!='.') {
541                    *pp++ = unified[c];
542                    u2r[nlen++] = realPos;
543                }
544                realPos++;
545            }
546        }
547    }
548    else {
549        if (gaps==ED4_SG_CONSIDER_GAPS) {
550            while(realPos<len) {
551                *pp++ = unified[(unsigned char)data[realPos++]];
552                nlen++;
553            }
554        }
555        else {
556            while(realPos<len) {
557                unsigned char c = data[realPos++];
558
559                if (c!='-' && c!='.') {
560                    *pp++ = unified[c];
561                    nlen++;
562                }
563            }
564        }
565    }
566
567    // ---------------
568
569    pp[0] = 0;
570    if (new_len) {
571        *new_len = nlen;
572    }
573    return p;
574}
575
576char *SearchTree::unify_pattern(const char *pattern, int *new_len)
577{
578    int len = strlen(pattern);
579    return unify_str(pattern, len, sett->get_pat_gaps(), new_len, 0);
580}
581
582char *SearchTree::unify_sequence(const char *sequence, int len, int *new_len, int **uni2real)
583{
584    return unify_str(sequence, len, sett->get_seq_gaps(), new_len, uni2real);
585}
586
587void SearchTree::findMatches(const char *seq, int len, reportMatch report)
588{
589    if (root) {
590        int new_len;
591        int *uni2real = (int*)malloc(len*sizeof(int));
592        char *uni_seq = uni2real ? unify_sequence(seq, len, &new_len, &uni2real) : NULL;
593
594        if (uni_seq) {
595            int off;
596            char *useq = uni_seq;
597            int mismatch_list[MAX_MISMATCHES];
598
599            for (off=0; off<MAX_MISMATCHES; off++) {
600                mismatch_list[off] = -1;
601            }
602
603            SearchTreeNode::set_report(report, uni2real);
604            SearchTreeNode::set_mismatches(sett->get_min_mismatches(), sett->get_max_mismatches());
605
606            for (off=0; off<new_len && !ignore_more_results; off++,useq++) {
607                SearchTreeNode::set_start_offset(off);
608                root->findMatches(off, useq, new_len-off, 0, mismatch_list);
609            }
610
611            free(uni_seq);
612            free(uni2real);
613        }
614        else {
615            aw_message("Out of swapspace?");
616            if (uni2real) free(uni2real);
617        }
618    }
619}
620
621// --------------------------------------------------------------------------------
622
623#define AWAR_NAME(t,s)  ED4_AWAR_##t##_SEARCH_##s
624
625#define AWAR_LIST(t)                            \
626    AWAR_NAME(t,PATTERN),                       \
627    AWAR_NAME(t,MIN_MISMATCHES),                \
628    AWAR_NAME(t,MAX_MISMATCHES),                \
629    AWAR_NAME(t,CASE),                          \
630    AWAR_NAME(t,TU),                            \
631    AWAR_NAME(t,PAT_GAPS),                      \
632    AWAR_NAME(t,SEQ_GAPS),                      \
633    AWAR_NAME(t,REVERSE),                       \
634    AWAR_NAME(t,COMPLEMENT),                    \
635    AWAR_NAME(t,EXACT),                         \
636    AWAR_NAME(t,SHOW),                          \
637    AWAR_NAME(t,OPEN_FOLDED),                   \
638    AWAR_NAME(t,AUTO_JUMP)
639
640static struct S_SearchAwarList awar_list[SEARCH_PATTERNS] = {
641    { AWAR_LIST(USER1) },
642    { AWAR_LIST(USER2) },
643    { AWAR_LIST(PROBE) },
644    { AWAR_LIST(PRIMER1) },
645    { AWAR_LIST(PRIMER2) },
646    { AWAR_LIST(PRIMER3) },
647    { AWAR_LIST(SIG1) },
648    { AWAR_LIST(SIG2) },
649    { AWAR_LIST(SIG3) },
650};
651
652static inline int resultsAreShown(ED4_SearchPositionType type) {
653    return ED4_ROOT->aw_root->awar(awar_list[type].show)->read_int();
654}
655
656enum search_params_changed_action {
657    REFRESH_IF_SHOWN   = 1,
658    REFRESH_ALWAYS     = 2,
659    RECALC_SEARCH_TREE = 4,
660    TEST_MIN_MISMATCH  = 8,
661    TEST_MAX_MISMATCH  = 16,
662    DO_AUTO_JUMP       = 32
663};
664
665// --------------------------------------------------------------------------------
666
667static SearchSettings *settings[SEARCH_PATTERNS]; // last searched settings for each type
668static SearchTree     *tree[SEARCH_PATTERNS]; // Search trees for each type
669
670// --------------------------------------------------------------------------------
671
672static void searchParamsChanged(AW_root *root, AW_CL cl_type, AW_CL cl_action)
673{
674    ED4_SearchPositionType type = ED4_SearchPositionType(cl_type);
675    enum search_params_changed_action action = (enum search_params_changed_action)cl_action;
676
677    result_counter      = 0;
678    ignore_more_results = false;
679
680    // check awar values
681
682    if (action & (TEST_MIN_MISMATCH|TEST_MAX_MISMATCH)) {
683        int mimi = root->awar(awar_list[type].min_mismatches)->read_int();
684        int mami = root->awar(awar_list[type].max_mismatches)->read_int();
685
686        if (mimi>mami) {
687            if (action & TEST_MIN_MISMATCH) { // max has changed
688                root->awar(awar_list[type].min_mismatches)->write_int(mami);
689            }
690            if (action & TEST_MAX_MISMATCH) { // min has changed
691                root->awar(awar_list[type].max_mismatches)->write_int(mimi);
692            }
693        }
694    }
695
696    // init new search
697
698 recalc:
699    ED4_SearchResults::setNewSearch(type);
700    if (!settings[type]) return;
701    settings[type]->update(&awar_list[type]);
702
703    if (action & RECALC_SEARCH_TREE) {
704        delete tree[type];
705        tree[type] = new SearchTree(settings[type]);
706    }
707
708    if (action & (RECALC_SEARCH_TREE|TEST_MIN_MISMATCH|TEST_MAX_MISMATCH)) {
709        int patLen = tree[type]->get_shortestPattern();
710        int maxMis = root->awar(awar_list[type].max_mismatches)->read_int();
711
712        if (patLen < 3*maxMis) {
713            int maxMaxMis = tree[type]->get_shortestPattern()/3;
714
715            aw_message(GBS_global_string("Too many mismatches (patterns of length=%i allow only %i max. mismatches)", patLen, maxMaxMis));
716
717            root->awar(awar_list[type].max_mismatches)->write_int(maxMaxMis);
718            if (root->awar(awar_list[type].min_mismatches)->read_int() > maxMaxMis) {
719                root->awar(awar_list[type].min_mismatches)->write_int(maxMaxMis);
720            }
721            goto recalc;
722        }
723    }
724
725    if (action & REFRESH_IF_SHOWN) {
726        if (resultsAreShown(type)) {
727            action = (enum search_params_changed_action)(action|REFRESH_ALWAYS);
728        }
729    }
730
731    if (settings[type]->get_autoJump() && (action & DO_AUTO_JUMP)) { // auto jump
732        ED4_cursor *cursor = &ED4_ROOT->get_ed4w()->cursor;
733        bool jumped = false;
734
735        if (cursor->owner_of_cursor && cursor->owner_of_cursor->is_sequence_terminal()) {
736            int pos = cursor->get_sequence_pos();
737            ED4_sequence_terminal *seq_term = cursor->owner_of_cursor->to_sequence_terminal();
738            ED4_SearchResults *result = &seq_term->results();
739
740            result->search(seq_term);
741            ED4_SearchPosition *found = result->get_last_starting_before(type, pos+1, 0);
742            int bestPos = -1;
743
744            if (found) {
745                bestPos = found->get_start_pos();
746            }
747
748            if (pos>=1) {
749                found = result->get_first_starting_after(type, pos-1, 0);
750                if (found) {
751                    int next_pos = found->get_start_pos();
752
753                    if (abs(pos-next_pos)<abs(pos-bestPos)) {
754                        bestPos = next_pos;
755                    }
756                }
757            }
758
759            if (bestPos!=-1) {
760                if (bestPos == pos) {
761                    jumped = true; // already there
762                }
763                else {
764                    jumped = seq_term->setCursorTo(cursor, bestPos, 1, ED4_JUMP_KEEP_POSITION);
765                }
766            }
767        }
768
769        if (!jumped) {
770            ED4_search(0, ED4_encodeSearchDescriptor(+1, type));
771        }
772    }
773
774    if (action & REFRESH_ALWAYS) {
775        bool old_update                        = ED4_update_global_cursor_awars_allowed;
776        ED4_update_global_cursor_awars_allowed = false;
777        ED4_refresh_window(ED4_ROOT->get_aww(), 0, 0);
778        ED4_update_global_cursor_awars_allowed = old_update;
779    }
780
781    root->awar(ED4_AWAR_SEARCH_RESULT_CHANGED)->touch(); // trigger refresh in SECEDIT
782}
783
784void ED4_create_search_awars(AW_root *root)
785{
786#define cb(action) add_callback(searchParamsChanged, AW_CL(i), AW_CL(action))
787
788    int i;
789    for (i=0; i<SEARCH_PATTERNS; i++) {
790        root->awar_string(awar_list[i].pattern, "", GLOBAL_gb_main)                            ->cb(REFRESH_IF_SHOWN | RECALC_SEARCH_TREE | DO_AUTO_JUMP);
791        root->awar_int(awar_list[i].case_sensitive, ED4_SC_CASE_INSENSITIVE, GLOBAL_gb_main)   ->cb(REFRESH_IF_SHOWN | RECALC_SEARCH_TREE | DO_AUTO_JUMP);
792        root->awar_int(awar_list[i].tu, ED4_ST_T_EQUAL_U, GLOBAL_gb_main)                      ->cb(REFRESH_IF_SHOWN | RECALC_SEARCH_TREE | DO_AUTO_JUMP);
793        root->awar_int(awar_list[i].pat_gaps, ED4_SG_IGNORE_GAPS, GLOBAL_gb_main)              ->cb(REFRESH_IF_SHOWN | RECALC_SEARCH_TREE | DO_AUTO_JUMP);
794        root->awar_int(awar_list[i].reverse, 0, GLOBAL_gb_main)                                ->cb(REFRESH_IF_SHOWN | RECALC_SEARCH_TREE | DO_AUTO_JUMP);
795        root->awar_int(awar_list[i].complement, 0, GLOBAL_gb_main)                             ->cb(REFRESH_IF_SHOWN | RECALC_SEARCH_TREE | DO_AUTO_JUMP);
796        root->awar_int(awar_list[i].exact, 0, GLOBAL_gb_main)                                  ->cb(REFRESH_IF_SHOWN | RECALC_SEARCH_TREE | DO_AUTO_JUMP);
797        root->awar_int(awar_list[i].min_mismatches, 0, GLOBAL_gb_main)                         ->cb(REFRESH_IF_SHOWN | TEST_MAX_MISMATCH | DO_AUTO_JUMP);
798        root->awar_int(awar_list[i].max_mismatches, 0, GLOBAL_gb_main)                         ->cb(REFRESH_IF_SHOWN | TEST_MIN_MISMATCH | DO_AUTO_JUMP);
799        root->awar_int(awar_list[i].seq_gaps, ED4_SG_IGNORE_GAPS, GLOBAL_gb_main)              ->cb(REFRESH_IF_SHOWN | DO_AUTO_JUMP);
800        root->awar_int(awar_list[i].show, 1, GLOBAL_gb_main)                                   ->cb(REFRESH_ALWAYS);
801        root->awar_int(awar_list[i].openFolded, 1, GLOBAL_gb_main)                             ->cb(0);
802        root->awar_int(awar_list[i].autoJump, 1, GLOBAL_gb_main)                               ->cb(DO_AUTO_JUMP);
803
804        settings[i] = new SearchSettings(&awar_list[i]);
805        tree[i] = new SearchTree(settings[i]);
806    }
807
808    root->awar_int(ED4_AWAR_SEARCH_RESULT_CHANGED, 0, GLOBAL_gb_main);
809
810#undef cb
811
812    // awars to save/load search parameters:
813    {
814        char *dir = GBS_global_string_copy("%s/.arb_prop/search_settings", GB_getenvHOME());
815        aw_create_selection_box_awars(root, ED4_SEARCH_SAVE_BASE, dir, ".asp", "noname.asp");
816        root->awar(ED4_SEARCH_SAVE_BASE"/directory")->write_string(dir);
817        free(dir);
818    }
819}
820
821// --------------------------------------------------------------------------------
822
823char *ED4_SearchPosition::lastShownComment = 0;
824
825ED4_SearchPosition::ED4_SearchPosition(int sp, int ep, ED4_SearchPositionType wf, GB_CSTR found_comment, int mismatches[MAX_MISMATCHES])
826{
827    e4_assert(sp<=ep && sp>=0); // && ep<20000);
828    start_pos = sp;
829    end_pos = ep;
830    whatsFound = wf;
831    next = 0;
832    comment = found_comment;
833    memcpy(mismatch, mismatches, sizeof(*mismatch)*MAX_MISMATCHES);
834}
835ED4_SearchPosition::ED4_SearchPosition(const ED4_SearchPosition& other) {
836    start_pos = other.start_pos;
837    end_pos = other.end_pos;
838    whatsFound = other.whatsFound;
839    next = 0;
840    comment = strdup(other.comment);
841    memcpy(mismatch, other.mismatch, sizeof(mismatch[0])*MAX_MISMATCHES);
842}
843
844ED4_SearchPosition *ED4_SearchPosition::insert(ED4_SearchPosition *toAdd)
845{
846    ED4_SearchPosition *head = this;
847    ED4_SearchPosition *self = this;
848    ED4_SearchPosition *last = 0;
849
850    while (1) {
851        if (toAdd->cmp(*self)<=0) { // add before self
852            toAdd->next          = self;
853            if (last) last->next = toAdd;
854            else head            = toAdd;
855            break;
856        }
857        if (!self->next) { // add to end of list
858            self->next = toAdd;
859            break;
860        }
861        last = self;
862        self = self->next;
863    }
864
865    return head;
866}
867ED4_SearchPosition *ED4_SearchPosition::remove(ED4_SearchPositionType typeToRemove)
868{
869    ED4_SearchPosition *head = this;
870    ED4_SearchPosition *self = this;
871    ED4_SearchPosition *last = 0;
872
873    while (self) {
874        if (self->whatsFound == typeToRemove) { // remove self
875            ED4_SearchPosition **ptrToMe = last ? &(last->next) : &head;
876
877            *ptrToMe   = self->next;
878            self->next = 0;
879            delete self;
880            self       = *ptrToMe;
881        }
882        else { // do not remove
883            last = self;
884            self = self->next;
885        }
886    }
887
888    return head;
889}
890
891#ifdef TEST_SEARCH_POSITION
892int ED4_SearchPosition::ok() const
893{
894#ifndef NDEBUG
895    if (next) {
896        int c = cmp(*next);
897
898        if (c>0) {
899            printf("ED4_SearchPosition: list not sorted\n");
900            return 0;
901        }
902
903        return next->ok();
904    }
905#endif
906    return 1;
907}
908#endif
909
910GB_CSTR ED4_SearchPosition::get_comment() const
911{
912    if (!comment) return 0;
913    if (lastShownComment && strcmp(lastShownComment, comment)==0) return 0; // do not show comment twice
914
915    delete lastShownComment;
916    lastShownComment = strdup(comment);
917    return lastShownComment;
918}
919
920ED4_SearchPosition *ED4_SearchPosition::get_next_at(int pos) const
921{
922    ED4_SearchPosition *self = this->next;
923
924    while (self) {
925        if (self->start_pos > pos) break; // none found
926        if (self->containsPos(pos)) return self;
927        self = self->next;
928    }
929    return 0;
930}
931
932// --------------------------------------------------------------------------------
933
934int   ED4_SearchResults::initialized = 0;
935int   ED4_SearchResults::timeOfLastSearch[SEARCH_PATTERNS];
936int   ED4_SearchResults::timeOfNextSearch[SEARCH_PATTERNS];
937int   ED4_SearchResults::shown[SEARCH_PATTERNS];
938int   ED4_SearchResults::bufferSize;
939char *ED4_SearchResults::buffer;
940
941ED4_SearchResults::ED4_SearchResults()
942{
943    if (!initialized) {
944        int i;
945
946        for (i=0; i<SEARCH_PATTERNS; i++) {
947            timeOfLastSearch[i] = 0;
948            timeOfNextSearch[i] = 1;
949            shown[i] = resultsAreShown(ED4_SearchPositionType(i));
950        }
951
952        bufferSize = 100;
953        buffer = (char*)GB_calloc(bufferSize, sizeof(char));
954
955        initialized = 1;
956    }
957
958    arraySize = 0;              // list-format
959    array     = 0;
960    first     = 0;
961    int i;
962    for (i=0; i<SEARCH_PATTERNS; i++) {
963        timeOf[i] = 0;
964    }
965}
966
967ED4_SearchResults::~ED4_SearchResults() {
968    delete first;
969    free(array);
970}
971
972// --------------------------------------------------------------------------------
973
974static ED4_SearchResults      *reportToResult = 0;
975static ED4_SearchPositionType  reportType;
976
977static void reportSearchPosition(int start, int end, GB_CSTR comment, int mismatches[MAX_MISMATCHES])
978{
979    ED4_SearchPosition *pos = new ED4_SearchPosition(start, end, reportType, comment, mismatches);
980    reportToResult->addSearchPosition(pos);
981}
982
983// --------------------------------------------------------------------------------
984
985void ED4_SearchResults::addSearchPosition(ED4_SearchPosition *pos) {
986    static int max_allowed_results = 100000;
987
988    if (ignore_more_results) return;
989
990    if (is_array()) {
991        to_list();
992    }
993
994    ++result_counter;
995    if (result_counter >= max_allowed_results) {
996        if (aw_question(GBS_global_string("More than %i results found!", result_counter), "Allow more,That's enough") == 0) {
997            max_allowed_results = max_allowed_results*2;
998        }
999        else {
1000            ignore_more_results = true;
1001            return;
1002        }
1003    }
1004
1005    if (first) {
1006        first = first->insert(pos);
1007#ifdef TEST_SEARCH_POSITION
1008        e4_assert(first->ok());
1009#endif
1010    }
1011    else {
1012        first = pos;
1013    }
1014}
1015
1016void ED4_SearchResults::search(const ED4_sequence_terminal *seq_terminal)
1017{
1018    int i;
1019    int needed[SEARCH_PATTERNS];
1020    int st_needed = 0;
1021
1022    for (i=0; i<SEARCH_PATTERNS; i++) {
1023        if (timeOf[i]<timeOfNextSearch[i]) {
1024            timeOf[i] = timeOfNextSearch[i];
1025            timeOfLastSearch[i] = timeOfNextSearch[i];
1026            needed[i] = 1;
1027            st_needed = 1;
1028            if (first) {
1029                if (is_array()) {
1030                    to_list();
1031                }
1032                first = first->remove(ED4_SearchPositionType(i));
1033#if defined TEST_SEARCH_POSITION
1034                e4_assert(!first || first->ok());
1035#endif
1036            }
1037        }
1038        else {
1039            needed[i] = 0;
1040        }
1041    }
1042
1043    if (st_needed) {
1044        int len;
1045        char *seq = seq_terminal->resolve_pointer_to_string_copy(&len);
1046
1047        if (seq) {
1048            reportToResult = this;
1049            for (i=0; i<SEARCH_PATTERNS; i++) {
1050                reportType = ED4_SearchPositionType(i);
1051                if (needed[i]) {
1052                    if (is_array()) {
1053                        to_list();
1054                    }
1055                    tree[i]->findMatches(seq, len, reportSearchPosition);
1056                }
1057            }
1058            reportToResult = 0;
1059        }
1060        free(seq);
1061    }
1062}
1063
1064ED4_SearchPosition *ED4_SearchResults::get_first_at(ED4_SearchPositionType type, int start, int end) const {
1065    if (is_list()) {
1066        to_array();
1067    }
1068
1069    int l = 0;
1070    int h = arraySize-1;
1071
1072    ED4_SearchPosition *pos = 0;
1073    int m = 0;
1074
1075    while (l<=h) {
1076        m = (l+h)/2;
1077        pos = array[m];
1078        if (pos->get_end_pos()<start) {
1079            l = m+1;
1080        }
1081        else if (pos->get_start_pos()>end) {
1082            h = m-1;
1083        }
1084        else {
1085            e4_assert(pos->get_end_pos()>=start && pos->get_start_pos()<=end);
1086            break;
1087        }
1088    }
1089
1090    if (l>h) {
1091        return 0;
1092    }
1093
1094    if (pos) {
1095        int best_m = m;
1096
1097        while (m>0) {
1098            m--;
1099            pos = array[m];
1100            if (pos->get_end_pos()>=start && pos->get_start_pos()<=end) {
1101                best_m = m;
1102            }
1103        }
1104        pos = array[best_m];
1105
1106        while (pos) {
1107            if (type==ED4_ANY_PATTERN || pos->get_whatsFound()==type) {
1108                break;
1109            }
1110            pos = pos->get_next();
1111        }
1112
1113        return pos;
1114    }
1115
1116    return 0;
1117}
1118
1119ED4_SearchPosition *ED4_SearchResults::get_first_starting_after(ED4_SearchPositionType type, int pos, int mustBeShown) const
1120{
1121    ED4_SearchPosition *sp = first;
1122
1123    while (sp) {
1124        if (type==ED4_ANY_PATTERN || sp->get_whatsFound()==type) {
1125            if (sp->get_start_pos()>pos && (!mustBeShown || resultsAreShown(sp->get_whatsFound()))) {
1126                break;
1127            }
1128        }
1129        sp = sp->get_next();
1130    }
1131
1132    return sp;
1133}
1134
1135ED4_SearchPosition *ED4_SearchResults::get_last_starting_before(ED4_SearchPositionType type, int pos, int mustBeShown) const
1136{
1137    ED4_SearchPosition *sp = first,
1138        *best = 0;
1139
1140    while (sp) {
1141        if (type==ED4_ANY_PATTERN || sp->get_whatsFound()==type) {
1142            if (sp->get_start_pos()<pos && (!mustBeShown || resultsAreShown(sp->get_whatsFound()))) {
1143                best = sp;
1144            }
1145            else {
1146                break;
1147            }
1148        }
1149        sp = sp->get_next();
1150    }
1151
1152    return best;
1153}
1154
1155void ED4_SearchResults::setNewSearch(ED4_SearchPositionType type)
1156{
1157    if (type==ED4_ANY_PATTERN) {
1158        int i;
1159        for (i=0; i<SEARCH_PATTERNS; i++) {
1160            setNewSearch(ED4_SearchPositionType(i));
1161        }
1162    }
1163    else {
1164        int next_unused_stamp = timeOfLastSearch[type] + 1;
1165
1166        shown[type] = resultsAreShown(type);
1167        if (timeOfNextSearch[type]!=next_unused_stamp) {
1168            timeOfNextSearch[type] = next_unused_stamp;
1169        }
1170    }
1171}
1172
1173void ED4_SearchResults::searchAgain()
1174{
1175    int i;
1176
1177    for (i=0; i<SEARCH_PATTERNS; i++) {
1178        timeOf[i] = 0;
1179    }
1180}
1181
1182char *ED4_SearchResults::buildColorString(const ED4_sequence_terminal *seq_terminal, int start, int end) 
1183    // builds a char buffer (access is only valid from result[start] till result[end])
1184{
1185    int i;
1186    int st_shown = 0;
1187
1188    e4_assert(start<=end);                  //confirming the condition
1189    for (i=0; i<SEARCH_PATTERNS; i++) {
1190        if (shown[i]) {
1191            st_shown = 1;
1192            break;
1193        }
1194    }
1195    if (!st_shown) {
1196        return 0; // nothing shown
1197    }
1198
1199    search(seq_terminal);
1200    if (!get_first()) {
1201        return 0; // nothing found
1202    }
1203
1204    int needed_size = end-start+1;
1205    if (needed_size>bufferSize) {
1206        free(buffer);
1207        bufferSize = needed_size;
1208        buffer = (char*)GB_calloc(bufferSize, sizeof(char));
1209    }
1210    else {
1211        memset(buffer, 0, sizeof(char)*needed_size);
1212    }
1213
1214    // search first search-result that goes in between start-end
1215
1216    ED4_SearchPosition *pos = get_first_at(ED4_ANY_PATTERN, start, end);
1217    e4_assert(!pos || (pos->get_start_pos()<=end && pos->get_end_pos()>=start));
1218
1219    int correct_neg_values = 0;
1220
1221    while (pos && pos->get_start_pos()<=end) {
1222        int what = int(pos->get_whatsFound());
1223
1224        if (shown[what]) {
1225            int color = ED4_G_SBACK_0 + what;
1226            int s = max(pos->get_start_pos(), start)-start;
1227            int e = min(pos->get_end_pos(), end)-start;
1228
1229            for (i=s; i<=e; i++) {
1230                if (buffer[i]==0 || abs(buffer[i])>abs(color)) {
1231                    buffer[i] = color;
1232                }
1233            }
1234
1235            const int *mismatches = pos->getMismatches();
1236
1237            for (i=0; i<5 && mismatches[i]>=0; i++) {
1238                int mpos = mismatches[i];
1239
1240                if (mpos>=start && mpos<=end) {
1241                    int rpos = mpos-start;
1242                    if (buffer[rpos]==color) {
1243                        buffer[rpos] = -buffer[rpos];
1244                        correct_neg_values = 1;
1245                    }
1246                }
1247            }
1248        }
1249        pos = pos->get_next();
1250    }
1251
1252    if (correct_neg_values) {
1253        for (i=end-start; i>=0; i--) {
1254            if (buffer[i]<0) {
1255                buffer[i] = ED4_G_MBACK;
1256            }
1257        }
1258    }
1259
1260    return buffer-start;
1261}
1262
1263const char *ED4_buildColorString(const ED4_sequence_terminal *seq_terminal, int start, int end) {
1264    return seq_terminal->results().buildColorString(seq_terminal, start, end);
1265}
1266
1267ED4_SearchPosition *ED4_SearchResults::get_shown_at(int pos) const
1268// used by ED4_cursor::updateAwars to get search-comment
1269{
1270    ED4_SearchPosition *p = get_last_starting_before(ED4_ANY_PATTERN, pos, 0); // @@@ tofix: should find last_ending before
1271    ED4_SearchPosition *best = 0;
1272
1273    if (!p) {
1274        p = get_first();
1275    }
1276
1277    if (p && !p->containsPos(pos)) {
1278        p = p->get_next_at(pos);
1279    }
1280
1281    while (p) {
1282        e4_assert(p->containsPos(pos));
1283        ED4_SearchPositionType type = p->get_whatsFound();
1284        if (shown[type]) {
1285            if (!best || type<best->get_whatsFound()) {
1286                best = p;
1287            }
1288        }
1289        p = p->get_next_at(pos);
1290    }
1291
1292    return best;
1293}
1294
1295void ED4_SearchResults::to_array() const {
1296    e4_assert(arraySize==0); // assert list-format
1297
1298    ED4_SearchPosition *pos = first;
1299
1300    {
1301        int a_arraySize = 0;
1302
1303        while (pos) {
1304            a_arraySize++;
1305            pos = pos->get_next();
1306        }
1307        *((int*)&arraySize) = a_arraySize;
1308    }
1309
1310    ED4_SearchPosition **a_array = (ED4_SearchPosition**)malloc(sizeof(ED4_SearchPosition*)*arraySize);
1311
1312    int e;
1313    pos = first;
1314    for (e=0; e<arraySize; e++) {
1315        e4_assert(pos);
1316        a_array[e] = pos;
1317        pos = pos->get_next();
1318    }
1319
1320    *((ED4_SearchPosition***)&array) = a_array;
1321}
1322void ED4_SearchResults::to_list() const {
1323    e4_assert(arraySize>0); // assert array-format
1324    free(array);
1325
1326    *((int*)&arraySize) = 0;
1327    *((ED4_SearchPosition***)&array) = 0;
1328}
1329
1330
1331// --------------------------------------------------------------------------------
1332
1333inline void decodeSearchDescriptor(int descriptor, int *direction, ED4_SearchPositionType *pattern)
1334{
1335    *direction = descriptor&1 ? 1 : -1;
1336    *pattern = ED4_SearchPositionType(descriptor/2);
1337}
1338
1339static AW_CL last_searchDescriptor = -1;
1340
1341GB_ERROR ED4_repeat_last_search(void) {
1342    if (int(last_searchDescriptor)==-1) {
1343        return GBS_global_string("You have to search first, before you can repeat a search.");
1344    }
1345
1346    ED4_search(0, last_searchDescriptor);
1347    return 0;
1348}
1349
1350void ED4_search(AW_window */*aww*/, AW_CL searchDescriptor)
1351{
1352    int direction;
1353    ED4_SearchPositionType pattern;
1354    int searchOnlyForShownPatterns;
1355
1356    last_searchDescriptor = searchDescriptor;
1357    decodeSearchDescriptor(searchDescriptor, &direction, &pattern);
1358    searchOnlyForShownPatterns = pattern==ED4_ANY_PATTERN;
1359
1360    // detect position where to start searching
1361
1362    ED4_terminal *terminal = 0; // start search at this terminal
1363    int pos; // ... and at this position
1364
1365    ED4_cursor *cursor = &ED4_ROOT->get_ed4w()->cursor;
1366    if (cursor->owner_of_cursor) { // if cursor is shown -> use cursor position
1367        terminal = cursor->owner_of_cursor->to_terminal();
1368        pos = cursor->get_sequence_pos();
1369    }
1370    else { // start at end or beginning
1371        if (direction==1) {
1372            terminal = ED4_ROOT->root_group_man->get_first_terminal();
1373            pos = INT_MIN;
1374        }
1375        else {
1376            terminal = ED4_ROOT->root_group_man->get_last_terminal();
1377            pos = INT_MAX;
1378        }
1379    }
1380
1381    ED4_terminal *start_terminal = terminal;
1382    int start_pos = pos;
1383    int last_loop = 0;
1384
1385    while (terminal) {
1386        if (terminal->is_sequence_terminal()) {
1387            ED4_sequence_terminal *seq_terminal = terminal->to_sequence_terminal();
1388            ED4_SearchResults&     results      = seq_terminal->results();
1389            ED4_SearchPosition    *found        = 0;
1390
1391            if (pattern==ED4_ANY_PATTERN || settings[pattern]->get_open_folded() || !terminal->is_in_folded_group()) {
1392                results.search(seq_terminal);
1393
1394                if (direction==1) {
1395                    found = results.get_first_starting_after(pattern, pos, searchOnlyForShownPatterns);
1396                }
1397                else {
1398                    found = results.get_last_starting_before(pattern, pos, searchOnlyForShownPatterns);
1399                }
1400
1401                if (found) {
1402                    pos = found->get_start_pos();
1403                    if (terminal==start_terminal && pos==start_pos) {
1404                        if (searchOnlyForShownPatterns) {
1405                            aw_message("There are no other shown patterns");
1406                        }
1407                        else {
1408                            aw_message("This is the only occurrence of the search pattern");
1409                        }
1410                    }
1411                    else {
1412                        seq_terminal->setCursorTo(&ED4_ROOT->get_ed4w()->cursor, pos, true, ED4_JUMP_KEEP_POSITION);
1413                    }
1414                    break;
1415                }
1416                else if (last_loop) {
1417                    if (searchOnlyForShownPatterns) {
1418                        aw_message("There are no shown patterns");
1419                    }
1420                    else {
1421                        aw_message("Search pattern was not found in any sequence");
1422                    }
1423                    break;
1424                }
1425            }
1426        }
1427
1428        if (direction==1) {
1429            terminal = terminal->get_next_terminal();
1430            if (!terminal) terminal = ED4_ROOT->root_group_man->get_first_terminal();
1431            pos = INT_MIN;
1432        }
1433        else {
1434            terminal = terminal->get_prev_terminal();
1435            if (!terminal) terminal = ED4_ROOT->root_group_man->get_last_terminal();
1436            pos = INT_MAX;
1437        }
1438
1439        if (terminal==start_terminal) {
1440            last_loop = 1;
1441        }
1442    }
1443}
1444
1445void ED4_mark_matching_species(AW_window */*aww*/, AW_CL cl_pattern) {
1446    ED4_SearchPositionType  pattern  = ED4_SearchPositionType(cl_pattern);
1447    ED4_terminal           *terminal = ED4_ROOT->root_group_man->get_first_terminal();
1448    GB_ERROR                error    = 0;
1449    GB_transaction          ta(GLOBAL_gb_main);
1450
1451    while (terminal && !error) {
1452        if (terminal->is_sequence_terminal()) {
1453            ED4_sequence_terminal *seq_terminal = terminal->to_sequence_terminal();
1454            ED4_SearchResults&     results      = seq_terminal->results();
1455
1456            results.search(seq_terminal);
1457            ED4_SearchPosition *found = results.get_first_starting_after(pattern, INT_MIN, false);
1458
1459            if (found) {
1460                ED4_species_manager *species_man = seq_terminal->get_parent(ED4_L_SPECIES)->to_species_manager();;
1461                if (!species_man->flag.is_consensus) {
1462                    GBDATA *gbd = species_man->get_species_pointer();
1463                    e4_assert(gbd);
1464                    error = GB_write_flag(gbd, 1);
1465                }           
1466            }
1467        }
1468
1469        terminal = terminal->get_next_terminal();
1470    }
1471
1472    if (error) {
1473        error = ta.close(error);
1474        aw_message(error);
1475    }
1476}
1477
1478#define ESC '\\'
1479
1480static char *pattern2str(GB_CSTR p) {
1481    char *s = GB_give_buffer(strlen(p)*2);
1482    char *s1 = s;
1483    GB_CSTR p1 = p;
1484
1485    while (1) {
1486        char c = *p1++;
1487
1488        if (!c) {
1489            break;
1490        }
1491        else if (c==ESC) {
1492            *s1++ = ESC;
1493            *s1++ = ESC;
1494        }
1495        else if (c=='\n') {
1496            *s1++ = ESC;
1497            *s1++ = 'n';
1498        }
1499        else {
1500            *s1++ = c;
1501        }
1502    }
1503
1504    *s1 = 0;
1505    return strdup(s);
1506}
1507
1508static void str2pattern(char *s) { // works on string
1509    char *a = s;
1510    char *n = s;
1511
1512    while (1) {
1513        char c = *a++;
1514
1515        if (c==ESC) {
1516            c = *a++;
1517            if (c==ESC) {
1518                *n++ = ESC;
1519            }
1520            else if (c=='n') {
1521                *n++ = '\n';
1522            }
1523            else { // should not occur
1524                *n++ = ESC;
1525                *n++ = c;
1526            }
1527        }
1528        else {
1529            *n++ = c;
1530            if (!c) {
1531                break;
1532            }
1533        }
1534    }
1535}
1536
1537#undef ESC
1538
1539static void save_search_paras_to_file(AW_window *aw, AW_CL cl_type) {
1540    AW_root *root = ED4_ROOT->aw_root;
1541    ED4_SearchPositionType type = ED4_SearchPositionType(cl_type);
1542    char *filename = root->awar(ED4_SEARCH_SAVE_BASE"/file_name")->read_string();
1543    FILE *in = fopen(filename, "rt");
1544
1545    if (in) {
1546        fclose(in);
1547        GB_CSTR error = GBS_global_string("'%s' already exists", filename);
1548        int answer = aw_question(error, "Overwrite,Cancel");
1549        if (answer!=0) {
1550            return;
1551        }
1552    }
1553
1554    FILE *out = fopen(filename, "wt");
1555
1556    if (!out) {
1557        GB_CSTR error = GBS_global_string("Can't write file '%s' (%s)", filename, strerror(errno));
1558        aw_popup_ok(error);
1559    }
1560    else {
1561        SearchSettings *s = settings[type];
1562
1563        char *fpat = pattern2str(s->get_pattern());
1564
1565        fprintf(out,
1566                "pattern=%s\n"
1567                "minmis=%i\n"
1568                "maxmis=%i\n"
1569                "case=%i\n"
1570                "tu=%i\n"
1571                "patgaps=%i\n"
1572                "seqgaps=%i\n"
1573                "openfolded=%i\n"
1574                "autojump=%i\n"
1575                "reverse=%i\n"
1576                "complement=%i\n"
1577                "exact=%i\n",
1578                fpat,
1579                s->get_min_mismatches(),
1580                s->get_max_mismatches(),
1581                s->get_case_sensitive(),
1582                s->get_tu(),
1583                s->get_pat_gaps(),
1584                s->get_seq_gaps(),
1585                s->get_open_folded(),
1586                s->get_autoJump(),
1587                s->get_reverse(),
1588                s->get_complement(),
1589                s->get_exact());
1590
1591        free(fpat);
1592        fclose(out);
1593    }
1594    AW_POPDOWN(aw);
1595}
1596
1597static void load_search_paras_from_file(AW_window *aw, AW_CL cl_type) {
1598    AW_root *root = ED4_ROOT->aw_root;
1599    ED4_SearchPositionType type = ED4_SearchPositionType(cl_type);
1600    char *filename = root->awar(ED4_SEARCH_SAVE_BASE"/file_name")->read_string();
1601    FILE *in = fopen(filename, "rt");
1602
1603    if (!in) {
1604        GB_CSTR error = GBS_global_string("File '%s' not found", filename);
1605        aw_popup_ok(error);
1606        return;
1607    }
1608
1609#define BUFFERSIZE 10000
1610
1611    SearchAwarList al = &awar_list[type];
1612    while(1) {
1613        char buffer[BUFFERSIZE];
1614
1615        buffer[0] = 0;
1616        fgets(buffer, BUFFERSIZE, in);
1617        if (!buffer[0]) {
1618            break;
1619        }
1620        char *content = strchr(buffer, '=');
1621        if (!content) {
1622            aw_message(GBS_global_string("Format error in '%s' - ignored!", filename));
1623            continue;
1624        }
1625        *content++ = 0;
1626
1627        if (strcmp(buffer, "pattern")==0) {
1628            str2pattern(content);
1629            root->awar(al->pattern)->write_string(content);
1630        }
1631        else {
1632            int value = atoi(content);
1633
1634            if (strcmp(buffer, "minmis")==0)            root->awar(al->min_mismatches)->write_int(value);
1635            else if (strcmp(buffer, "maxmis")==0)       root->awar(al->max_mismatches)->write_int(value);
1636            else if (strcmp(buffer, "case")==0)         root->awar(al->case_sensitive)->write_int(value);
1637            else if (strcmp(buffer, "tu")==0)           root->awar(al->tu)->write_int(value);
1638            else if (strcmp(buffer, "patgaps")==0)      root->awar(al->pat_gaps)->write_int(value);
1639            else if (strcmp(buffer, "seqgaps")==0)      root->awar(al->seq_gaps)->write_int(value);
1640            else if (strcmp(buffer, "openfolded")==0)   root->awar(al->openFolded)->write_int(value);
1641            else if (strcmp(buffer, "autojump")==0)     root->awar(al->autoJump)->write_int(value);
1642            else if (strcmp(buffer, "reverse")==0)      root->awar(al->reverse)->write_int(value);
1643            else if (strcmp(buffer, "complement")==0)   root->awar(al->complement)->write_int(value);
1644            else if (strcmp(buffer, "exact")==0)        root->awar(al->exact)->write_int(value);
1645            else {
1646                aw_message(GBS_global_string("Unknown tag '%s' in '%s' - ignored!", buffer, filename));
1647            }
1648        }
1649    }
1650
1651#undef BUFFERSIZE
1652
1653    fclose(in);
1654    AW_POPDOWN(aw);
1655}
1656
1657static AW_window *loadsave_search_parameters(AW_root *root, ED4_SearchPositionType type, int load) {
1658    AW_window_simple *aws = new AW_window_simple;
1659
1660    if (load) {
1661        ED4_aws_init(root, aws, "load_%s_search_para", "Load %s Search Parameters", ED4_SearchPositionTypeId[type]);
1662    }
1663    else {
1664        ED4_aws_init(root, aws, "save_%s_search_para", "Save %s Search Parameters", ED4_SearchPositionTypeId[type]);
1665    }
1666
1667    aws->load_xfig("edit4/save_search.fig");
1668
1669    aws->at("close");aws->callback((AW_CB0)AW_POPDOWN);
1670    aws->create_button("CLOSE","CLOSE","C");
1671
1672    aws->callback( AW_POPUP_HELP, (AW_CL)"search_parameters.hlp");
1673    aws->at("help");
1674    aws->create_button("HELP","HELP","H");
1675
1676    awt_create_selection_box((AW_window *)aws, ED4_SEARCH_SAVE_BASE);
1677
1678    aws->callback( (AW_CB0)AW_POPDOWN);
1679    aws->at("cancel");
1680    aws->create_button("CANCEL","CANCEL","C");
1681
1682    aws->at("save");
1683    if (load) {
1684        aws->callback(load_search_paras_from_file, (AW_CL)type);
1685        aws->create_button("LOAD","LOAD","L");
1686    }
1687    else {
1688        aws->callback(save_search_paras_to_file, (AW_CL)type);
1689        aws->create_button("SAVE","SAVE","S");
1690    }
1691
1692    return aws;
1693}
1694
1695static AW_window *load_search_parameters(AW_root *root, AW_CL cl_type) {
1696    return loadsave_search_parameters(root, ED4_SearchPositionType(cl_type), 1);
1697}
1698
1699static AW_window *save_search_parameters(AW_root *root, AW_CL cl_type) {
1700    return loadsave_search_parameters(root, ED4_SearchPositionType(cl_type), 0);
1701}
1702
1703
1704static void search_init_config(AWT_config_definition& cdef, int search_type) {
1705    SearchAwarList awarList = &awar_list[search_type];
1706
1707    cdef.add(awarList->show, "show");
1708    cdef.add(awarList->openFolded, "openFolded");
1709    cdef.add(awarList->autoJump, "autoJump");
1710    cdef.add(awarList->pattern, "pattern");
1711    cdef.add(awarList->min_mismatches, "min_mismatches");
1712    cdef.add(awarList->max_mismatches, "max_mismatches");
1713    cdef.add(awarList->seq_gaps, "seq_gaps");
1714    cdef.add(awarList->pat_gaps, "pat_gaps");
1715    cdef.add(awarList->tu, "tu");
1716    cdef.add(awarList->case_sensitive, "case_sensitive");
1717    cdef.add(awarList->reverse, "reverse");
1718    cdef.add(awarList->complement, "complement");
1719    cdef.add(awarList->exact, "exact");
1720}
1721
1722//  -------------------------------------------------------------------------
1723//      static char *search_store_config(AW_window *aww, AW_CL , AW_CL )
1724//  -------------------------------------------------------------------------
1725static char *search_store_config(AW_window *aww, AW_CL cl_search_type, AW_CL ) {
1726    AWT_config_definition cdef(aww->get_root());
1727    search_init_config(cdef, int(cl_search_type));
1728    return cdef.read();
1729}
1730//  -----------------------------------------------------------------------------------------------------
1731//      static void search_restore_config(AW_window *aww, const char *stored_string, AW_CL , AW_CL )
1732//  -----------------------------------------------------------------------------------------------------
1733static void search_restore_config(AW_window *aww, const char *stored_string, AW_CL cl_search_type, AW_CL ) {
1734    AWT_config_definition cdef(aww->get_root());
1735    search_init_config(cdef, int(cl_search_type));
1736    cdef.write(stored_string);
1737}
1738
1739
1740AW_window *ED4_create_search_window(AW_root *root, AW_CL cl) {
1741    ED4_SearchPositionType type = ED4_SearchPositionType(cl);
1742    SearchAwarList awarList = &awar_list[type];
1743    AW_window_simple *aws = new AW_window_simple;
1744
1745    ED4_aws_init(root, aws, "%s_search", "%s Search", ED4_SearchPositionTypeId[type]);
1746    aws->load_xfig("edit4/search.fig");
1747
1748    aws->at("close");
1749    aws->callback( (AW_CB0)AW_POPDOWN);
1750    aws->create_button("CLOSE", "CLOSE","C");
1751
1752    aws->at("help");
1753    aws->callback( AW_POPUP_HELP,(AW_CL)"e4_search.hlp");
1754    aws->create_button("HELP", "HELP","H");
1755
1756    aws->at("load");
1757    aws->callback(AW_POPUP, (AW_CL)load_search_parameters, (AW_CL)type);
1758    aws->create_button("LOAD", "LOAD", "L");
1759
1760    aws->at("save");
1761    aws->callback(AW_POPUP, (AW_CL)save_search_parameters, (AW_CL)type);
1762    aws->create_button("SAVE", "SAVE", "S");
1763
1764    aws->at("next");
1765    aws->callback(ED4_search, (AW_CL)ED4_encodeSearchDescriptor(+1, type));
1766    aws->create_button("SEARCH_NEXT", "#edit/next.bitmap", "N");
1767
1768    aws->at("previous");
1769    aws->callback(ED4_search, (AW_CL)ED4_encodeSearchDescriptor(-1, type));
1770    aws->create_button("SEARCH_LAST", "#edit/last.bitmap", "L");
1771
1772    aws->at("mark");
1773    aws->callback(ED4_mark_matching_species, (AW_CL)type);
1774    aws->create_autosize_button("MARK_SPECIES", "Mark species with matches", "M");
1775
1776    aws->at("show");
1777    aws->create_toggle(awarList->show);
1778
1779    aws->at("open");
1780    aws->create_toggle(awarList->openFolded);
1781
1782    aws->at("jump");
1783    aws->create_toggle(awarList->autoJump);
1784
1785    aws->at("pattern");
1786    aws->create_text_field(awarList->pattern, 28, 17);
1787
1788    aws->at("minmis");
1789    aws->create_toggle_field(awarList->min_mismatches, 1);
1790    aws->insert_default_toggle("0", "0", 0);
1791    aws->insert_toggle("1", "1", 1);
1792    aws->insert_toggle("2", "2", 2);
1793    aws->update_toggle_field();
1794
1795    aws->at("maxmis");
1796    aws->create_toggle_field(awarList->max_mismatches, 1);
1797    aws->insert_default_toggle("0", "0", 0);
1798    aws->insert_toggle("1", "1", 1);
1799    aws->insert_toggle("2", "2", 2);
1800    aws->insert_toggle("3", "3", 3);
1801    aws->insert_toggle("4", "4", 4);
1802    aws->insert_toggle("5", "5", 5);
1803    aws->update_toggle_field();
1804
1805    aws->at("seq_gaps");
1806    aws->create_toggle(awarList->seq_gaps);
1807    aws->at("pat_gaps");
1808    aws->create_toggle(awarList->pat_gaps);
1809    aws->at("tu");
1810    aws->create_toggle(awarList->tu);
1811    aws->at("case");
1812    aws->create_toggle(awarList->case_sensitive);
1813    aws->at("reverse");
1814    aws->create_toggle(awarList->reverse);
1815    aws->at("complement");
1816    aws->create_toggle(awarList->complement);
1817    aws->at("exact");
1818    aws->create_toggle(awarList->exact);
1819
1820    aws->at("config");
1821    AWT_insert_config_manager(aws, AW_ROOT_DEFAULT, "search", search_store_config, search_restore_config, (AW_CL)type, 0);
1822
1823    return (AW_window *)aws;
1824}
1825
1826static int has_species_name(ED4_base *base, AW_CL cl_species_name) {
1827    if (base->is_sequence_terminal()) {
1828        ED4_sequence_terminal *seq_term = base->to_sequence_terminal();
1829        const char *species_name = (const char *)cl_species_name;
1830        return species_name && seq_term && seq_term->species_name && strcmp(species_name, seq_term->species_name)==0;
1831    }
1832    return 0;
1833}
1834
1835ED4_sequence_terminal *ED4_find_seq_terminal(const char *species_name) {
1836    ED4_base *base = ED4_ROOT->main_manager->find_first_that(ED4_L_SEQUENCE_STRING, has_species_name, (AW_CL)species_name);
1837    ED4_sequence_terminal *seq_term = base->to_sequence_terminal();
1838
1839    return seq_term;
1840}
1841
Note: See TracBrowser for help on using the repository browser.