source: branches/profile/EDIT4/ED4_search.cxx

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