source: tags/arb-6.0.6/EDIT4/ED4_search.cxx

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