source: branches/ali/SL/AW_HELIX/AW_helix.cxx

Last change on this file was 19420, checked in by westram, 17 months ago
  • fix bug: last bond pair did never get auto-removed
  • always use arb_assert (instead of assertions from other modules).
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 12.3 KB
Line 
1// ==================================================================== //
2//                                                                      //
3//   File      : AW_helix.cxx                                           //
4//   Purpose   : Wrapper for BI_helix + AW-specific functions           //
5//                                                                      //
6//                                                                      //
7// Coded by Ralf Westram (coder@reallysoft.de) in December 2004         //
8// Copyright Department of Microbiology (Technical University Munich)   //
9//                                                                      //
10// Visit our web site at: http://www.arb-home.de/                       //
11//                                                                      //
12// ==================================================================== //
13
14#include "AW_helix.hxx"
15
16#include <awt_config_manager.hxx>
17
18#include <aw_root.hxx>
19#include <aw_window.hxx>
20#include <aw_awar.hxx>
21#include <aw_device.hxx>
22
23#include <arbdbt.h>
24#include <arb_strbuf.h>
25
26#include <cctype>
27
28#define AWAR_HELIX_REFRESH         "tmp/Helix/refresh" // used to trigger client refreshes
29#define AWAR_HELIX_ENABLE          "Helix/enable"
30#define HELIX_AWAR_SYMBOL_TEMPLATE "Helix/symbols/%s"
31#define HELIX_AWAR_PAIR_TEMPLATE   "Helix/pairs/%s"
32
33struct helix_pair_def {
34    const char   *awar;
35    BI_PAIR_TYPE  pair_type;
36};
37
38static helix_pair_def helix_awars[] = {
39    { "Strong_Pair",      HELIX_STRONG_PAIR },
40    { "Normal_Pair",      HELIX_NORMAL_PAIR },
41    { "Weak_Pair",        HELIX_WEAK_PAIR },
42    { "No_Pair",          HELIX_NO_PAIR },
43    { "User_Pair",        HELIX_USER0 },
44    { "User_Pair2",       HELIX_USER1 },
45    { "User_Pair3",       HELIX_USER2 },
46    { "User_Pair4",       HELIX_USER3 },
47    { "User_Pair5",       HELIX_USER4 },
48    { "User_Pair6",       HELIX_USER5 },
49    { "Default",          HELIX_DEFAULT },
50    { NULp,               BI_PAIR_TYPE(-1) },
51};
52
53inline const char *helix_symbol_awar(int idx) { return GBS_global_string(HELIX_AWAR_SYMBOL_TEMPLATE, helix_awars[idx].awar); }
54inline const char *helix_pair_awar  (int idx) { return GBS_global_string(HELIX_AWAR_PAIR_TEMPLATE,   helix_awars[idx].awar); }
55
56char *AW_helix::seq_2_helix(char *sequence, char undefsymbol) const {
57    size_t size2 = strlen(sequence);
58    arb_assert(size2<=size()); // if this fails there is a sequence longer than the alignment
59    char *helix = ARB_calloc<char>(size()+1);
60    for (size_t i=0; i<size2; i++) {
61        if (is_pairpos(i)) {
62            size_t j   = opposite_position(i);
63            char   sym = get_symbol(sequence[i], sequence[j]);
64            helix[i]   = sym == ' ' ? undefsymbol : sym;
65        }
66        else {
67            helix[i] = undefsymbol;
68        }
69    }
70    return helix;
71}
72
73void AW_helix::add_callback(const RootCallback& cb) const {
74    // add client callback. triggered when helix or pair definition has changed.
75    awar_refresh->add_callback(cb);
76}
77
78void AW_helix::remove_callback(const RootCallback& cb) const {
79    awar_refresh->remove_callback(cb);
80}
81
82static void helix_setup_changed_cb(AW_root *aw_root, AW_helix *helix) { helix->setup_changed_cb(aw_root); }
83void AW_helix::setup_changed_cb(AW_root *aw_root) {
84    enabled = aw_root->awar(AWAR_HELIX_ENABLE)->read_int();
85
86    for (int j=0; helix_awars[j].awar; j++) {
87        int i = helix_awars[j].pair_type;
88
89        set_pairs_def(i, aw_root->awar(helix_pair_awar(j))->read_char_pntr());
90        set_char_bind(i, aw_root->awar(helix_symbol_awar(j))->read_char_pntr());
91    }
92
93    aw_root->awar(AWAR_HELIX_REFRESH)->touch(); //  trigger client refreshes (EDIT4, SECEDIT, ...)
94}
95
96static void helix_pairs_changed_cb(AW_root *aw_root, int changed_idx) {
97    static bool recursion = false;
98
99    if (!recursion) {
100        AW_awar       *awar_pair  = aw_root->awar(helix_pair_awar(changed_idx));
101        const char    *pairdef_in = awar_pair->read_char_pntr();
102        GBS_strstruct  pairdef_out(strlen(pairdef_in)+2);
103
104        {
105            LocallyModify<bool> flag(recursion, true);
106            for (int i = 0;;) {
107                while (pairdef_in[i] == ' ') ++i; // skip spaces
108                char left = toupper(pairdef_in[i++]); if (!left) break;
109
110                if (!pairdef_out.empty()) {
111                    pairdef_out.put(' ');
112                }
113                pairdef_out.put(left);
114
115                while (pairdef_in[i] == ' ') ++i; // skip spaces
116                char right = toupper(pairdef_in[i++]); if (!right) break; // Note: in case of break this accepts single base @ tail
117
118                pairdef_out.put(right);
119
120                // remove (first) of duplicated pair definitions:
121                {
122                    char pair[] = "xx";
123                    pair[0]     = left;
124                    pair[1]     = right;
125
126                    for (int pass = 1; pass<=2; ++pass) {
127                        const char *data = pairdef_out.get_data();
128
129                        while (1) {
130                            const char *found = strstr(data, pair);
131                            if (!found) break; // not found -> done
132
133                            size_t pos = pairdef_out.get_position();
134                            if (found == data+pos-2) break; // reached current pair -> done
135
136                            // cut unwanted pair:
137                            size_t found_pos = found-data;
138                            pairdef_out.cut(found_pos, 3);
139                        }
140
141                        std::swap(pair[0], pair[1]);
142                    }
143                }
144
145                for (int j = 0; helix_awars[j].awar; j++) {
146                    if (j != changed_idx) {
147                        AW_awar *awar_pair2 = aw_root->awar(helix_pair_awar(j));
148                        char    *pd2        = awar_pair2->read_string();
149                        int      dst        = 0;
150                        bool     modified   = false;
151
152                        for (int k = 0; ; k += 3) {
153                            char l = toupper(pd2[k]); if (!l) break;
154                            pd2[dst] = l;
155
156                            char r = toupper(pd2[k+1]);
157                            if (!r) {
158                                // do NOT silently remove trailing single base @ tail
159                                dst += 2; // only has effect if modified
160                                break;
161                            }
162                            pd2[dst+1] = r;
163
164                            if ((left == l && right == r) || (left == r && right == l)) {
165                                // remove duplicated pair
166                                modified = true;
167                            }
168                            else {
169                                dst += 3;
170                            }
171                            if (!pd2[k+2]) break;
172                        }
173
174                        if (modified) {
175                            int endS = dst>0 ? dst-1 : dst;
176
177                            arb_assert(endS>=0);
178                            pd2[endS] = 0;
179                            awar_pair2->write_string(pd2);
180                        }
181
182                        free(pd2);
183                    }
184                }
185            }
186            awar_pair->write_string(pairdef_out.get_data()); // write back uppercase version
187        }
188        aw_root->awar(AWAR_HELIX_ENABLE)->touch(); // trigger reload of config + refresh
189    }
190}
191
192AW_helix::AW_helix(AW_root *aw_root)
193    : enabled(0)
194{
195    // Note: effectively AW_helix is/hasToBe a SINGLETON
196    // (awar generation will fail on 2nd instance)
197
198    RootCallback rcb = makeRootCallback(helix_setup_changed_cb, this);
199
200    for (int j=0; helix_awars[j].awar; j++) {
201        int i = helix_awars[j].pair_type;
202
203        aw_root->awar_string(helix_pair_awar(j),   get_pairs_def(i))->add_callback(makeRootCallback(helix_pairs_changed_cb, i));
204        aw_root->awar_string(helix_symbol_awar(j), get_char_bind(i))->add_callback(rcb);
205    }
206    aw_root->awar_int(AWAR_HELIX_ENABLE, 1)->add_callback(rcb);
207
208    awar_refresh = aw_root->awar_int(AWAR_HELIX_REFRESH, 1);
209
210    setup_changed_cb(aw_root); // init from AWARs
211}
212
213static void setup_helix_config(AWT_config_definition& cdef) {
214    cdef.add(AWAR_HELIX_ENABLE, "enable");
215
216    for (int j=0; helix_awars[j].awar; j++) {
217        int i = helix_awars[j].pair_type;
218
219        const char *name = helix_awars[j].awar;
220        if (i != HELIX_DEFAULT) {
221            cdef.add(helix_pair_awar(j), name);
222        }
223        cdef.add(helix_symbol_awar(j), GBS_global_string("%s_symbol", name));
224    }
225}
226
227static AWT_predefined_config predefined_helix_configs[] = {
228    { "*traditional",
229      "Traditional helix settings used up to arb 7.0\n(GG is defined as \"No pair\" here)\n",
230      "Default_symbol='';No_Pair='AA AC CC CT CU GG TU';No_Pair_symbol='#';Normal_Pair='GT GU';Normal_Pair_symbol='-';Strong_Pair='CG AT AU';Strong_Pair_symbol='~';"
231      "User_Pair='.A .C .G .T .U';User_Pair2='-A -C -G -T -U';User_Pair2_symbol='#';User_Pair3='UU TT';User_Pair3_symbol='+';"
232      "User_Pair4='';User_Pair4_symbol='';User_Pair5='';User_Pair5_symbol='';User_Pair6='';User_Pair6_symbol='';User_Pair_symbol='*';Weak_Pair='GA';Weak_Pair_symbol='=';enable='1'" },
233    { "*IUPAC_ambiguity_best",
234      "Base pairings using ambiguity codes.\nUses best case, i.e. strongest of possible bonds.\nNote: most combinations form a strong bond \nfollowing that approach. They are NOT explicitely \nlisted, instead they use the default symbol.",
235      "Default_symbol='~';No_Pair='AA AC CC CT CU TT UU';No_Pair_symbol='#';Normal_Pair='GT GU';Normal_Pair_symbol='-';Strong_Pair='CG AT AU';Strong_Pair_symbol='~';"
236      "User_Pair='.A .C .G .T .U .M .R .W .S .Y .K .V .H .D .B .N';User_Pair2='-A -C -G -T -U -M -R -W -S -Y -K -V -H -D -B -N';User_Pair2_symbol='#';User_Pair3='-- .. -.';User_Pair3_symbol='';"
237      "User_Pair4='AM CH CM CW CY MM TY UY YY';User_Pair4_symbol='#';User_Pair5='AR AS AV GR RR';User_Pair5_symbol='=';User_Pair6='BT BU DG GK GW KK KT KU ST SU SW';User_Pair6_symbol='-';User_Pair_symbol='*';Weak_Pair='GA GG';Weak_Pair_symbol='=';enable='1'" },
238    { "*IUPAC_ambiguity_mean",
239      "Base pairings using ambiguity codes.\nUses mean of possible bonds.\nNote: most combinations form a normal bond \nfollowing that approach. They are NOT explicitely \nlisted, instead they use the default symbol.",
240      "Default_symbol='=';No_Pair='AA AC CC CT CU TT UU';No_Pair_symbol='#';Normal_Pair='GT GU';Normal_Pair_symbol='-';Strong_Pair='CG AT AU';Strong_Pair_symbol='~';"
241      "User_Pair='.A .C .G .T .U .M .R .W .S .Y .K .V .H .D .B .N';User_Pair2='-A -C -G -T -U -M -R -W -S -Y -K -V -H -D -B -N';User_Pair2_symbol='#';User_Pair3='-- .. -.';User_Pair3_symbol='';"
242      "User_Pair4='AM AV CH CM CW CY HM HY MM TY UY YY';User_Pair4_symbol='#';User_Pair5='AK BG BR BS CK CR CS GH GM GN GS GV KM KR KV RT RU RY SS';User_Pair5_symbol='-';User_Pair6='GY';User_Pair6_symbol='~';User_Pair_symbol='*';Weak_Pair='GA GG';Weak_Pair_symbol='=';enable='1'" },
243    { NULp, NULp, NULp }
244};
245
246AW_window *create_helix_props_window(AW_root *awr) {
247    AW_window_simple *aws = new AW_window_simple;
248
249    aws->reset_layout("20230607");
250    aws->init(awr, "HELIX_PROPS", "HELIX_PROPERTIES");
251
252    aws->at(10, 10);
253    aws->auto_space(3, 3);
254
255    aws->callback(AW_POPDOWN);
256    aws->create_button("CLOSE", "CLOSE", "C");
257
258    aws->callback(makeHelpCallback("helixsym.hlp"));
259    aws->create_button("HELP", "HELP", "H");
260
261    AWT_insert_config_manager(aws, AW_ROOT_DEFAULT, "helix", makeConfigSetupCallback(setup_helix_config), NULp, predefined_helix_configs);
262
263    aws->at_newline();
264
265    const size_t max_awar_len = 11;
266    aws->label_length(max_awar_len);
267
268    aws->label("Show helix?");
269    aws->create_toggle(AWAR_HELIX_ENABLE);
270
271    aws->at_newline();
272
273    {
274        int x, y;
275        aws->get_at_position(&x, &y);
276        aws->at(100, 30); // set minimum window size
277        aws->at(x, y);
278    }
279
280    const int IF_YSIZE = 32; // lineheight of attached input field
281    for (int j = 0; helix_awars[j].awar; j++) {
282        int  i = helix_awars[j].pair_type;
283
284        arb_assert(strlen(helix_awars[j].awar) <= max_awar_len);
285        aws->label(helix_awars[j].awar);
286
287        aws->create_input_field(helix_symbol_awar(j), 3);
288
289        if (i != HELIX_DEFAULT) {
290            aws->at_attach_to(true, false, -10, IF_YSIZE);
291            aws->create_input_field(helix_pair_awar(j), 30);
292        }
293
294        aws->at_newline();
295    }
296    aws->window_fit();
297
298    return aws;
299}
300
301
Note: See TracBrowser for help on using the repository browser.