source: branches/tree/EDIT4/ED4_search.cxx

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