source: tags/ms_r17q2/EDIT4/ED4_search.cxx

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