source: tags/ms_r18q1/EDIT4/ED4_search.cxx

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