root/trunk/EDIT4/ED4_search.cxx

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