source: branches/help/PROBE_DESIGN/probe_design.cxx

Last change on this file was 18159, checked in by westram, 5 years ago
  • full update from child 'fix' into 'trunk'
    • fix item name accessors (GBT_get_name + GBT_get_name_or_description)
    • add null2empty
  • adds: log:branches/fix@18140:18158
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 110.6 KB
Line 
1// =============================================================== //
2//                                                                 //
3//   File      : probe_design.cxx                                  //
4//   Purpose   :                                                   //
5//                                                                 //
6//   Institute of Microbiology (Technical University Munich)       //
7//   http://www.arb-home.de/                                       //
8//                                                                 //
9// =============================================================== //
10
11#include "SaiProbeVisualization.hxx"
12#include "probe_match_parser.hxx"
13
14#include <PT_com.h>
15#include <PT_server.h> // needed for DOMAIN_MIN_LENGTH
16#include <client.h>
17#include <servercntrl.h>
18#include <probe_gui.hxx>
19#include <probe_local.hxx>
20
21#include <GEN.hxx>
22#include <TreeCallbacks.hxx>
23
24#include <iupac.h>
25#include <awt_config_manager.hxx>
26#include <awt_sel_boxes.hxx>
27#include <awt_misc.hxx>
28
29#include <aw_awars.hxx>
30#include <aw_preset.hxx>
31#include <aw_select.hxx>
32#include <aw_msg.hxx>
33#include <aw_root.hxx>
34#include <aw_question.hxx>
35#include <aw_edit.hxx>
36#include <rootAsWin.h>
37
38#include <adGene.h>
39
40#include <arb_progress.h>
41#include <arb_strbuf.h>
42#include <arb_file.h>
43#include <arb_misc.h>
44
45#include "probe_collection.hxx"
46
47// general awars
48
49#define AWAR_PROBE_CREATE_GENE_SERVER "tmp/probe_admin/gene_server" // whether to create a gene pt server
50
51#define AWAR_ITARGET_STRING       "nt/itarget_string"
52
53// probe match awars
54
55// #define AWAR_PD_MATCH_ITEM     AWAR_SPECIES_NAME
56#define AWAR_PD_SELECTED_MATCH "tmp/probe_design/match"
57#define AWAR_PD_MATCH_RESOLVE  "tmp/probe_design/match_resolve" // for IUPAC resolution
58
59#define AWAR_PD_MATCH_SORTBY       "probe_match/sort_by"     // weighted mismatches
60#define AWAR_PD_MATCH_ALSO_REVCOMP "probe_match/complement"  // check reverse complement too
61#define AWAR_PD_MATCH_MARKHITS     "probe_match/mark_hits"   // mark hitten species in database
62#define AWAR_PD_MATCH_WRITE2TMP    "probe_match/write_2_tmp" // write result to field tmp
63#define AWAR_PD_MATCH_AUTOMATCH    "probe_match/auto_match"  // auto match probes when target string changes
64
65#define AWAR_PD_MATCH_NHITS    "tmp/probe_match/nhits" // display the 'number of hits'
66
67// probe design awars
68
69#define AWAR_PD_DESIGN_CLIPRESULT "probe_design/CLIPRESULT" // 'length of output' (how many probes will get designed)
70#define AWAR_PD_DESIGN_MISHIT     "probe_design/MISHIT"     // 'non group hits'
71#define AWAR_PD_DESIGN_MAXBOND    "probe_design/MAXBOND"    // max hairpinbonds ?
72#define AWAR_PD_DESIGN_MINTARGETS "probe_design/MINTARGETS" // 'min. group hits (%)'
73
74#define AWAR_PD_DESIGN_MIN_LENGTH "probe_design/PROBELENGTH"      // min. length of probe
75#define AWAR_PD_DESIGN_MAX_LENGTH "probe_design/PROBEMAXLENGTH"   // max. length of probe (or empty)
76
77#define AWAR_PD_DESIGN_MIN_TEMP     "probe_design/MINTEMP"     // temperature (min)
78#define AWAR_PD_DESIGN_MAX_TEMP     "probe_design/MAXTEMP"     // temperature (max)
79#define AWAR_PD_DESIGN_MIN_GC       "probe_design/MINGC"       // GC content (min)
80#define AWAR_PD_DESIGN_MAX_GC       "probe_design/MAXGC"       // GC content (max)
81#define AWAR_PD_DESIGN_MIN_ECOLIPOS "probe_design/MINECOLI"    // ecolipos (min)
82#define AWAR_PD_DESIGN_MAX_ECOLIPOS "probe_design/MAXECOLI"    // ecolipos (max)
83
84#define AWAR_PD_DESIGN_GENE "probe_design/gene" // generate probes for genes ?
85
86// probe design/match (expert window)
87#define AWAR_PD_COMMON_EXP_BONDS_FORMAT "probe_design/bonds/pos%i"  // format to generate bond awar names
88#define AWAR_PD_COMMON_EXP_SPLIT        "probe_design/SPLIT"
89
90#define AWAR_PD_DESIGN_EXP_DTEDGE "probe_design/DTEDGE"
91#define AWAR_PD_DESIGN_EXP_DT     "probe_design/DT"
92
93#define AWAR_PD_MATCH_NMATCHES   "probe_match/nmatches"
94#define AWAR_PD_MATCH_LIM_NMATCH "probe_match/lim_nmatch"
95#define AWAR_PD_MATCH_MAX_RES    "probe_match/maxres"
96
97// --------------------------------
98// probe collection awars
99
100// probe collection window
101#define AWAR_PC_TARGET_STRING      "probe_collection/target_string"
102#define AWAR_PC_TARGET_NAME        "probe_collection/target_name"
103#define AWAR_PC_MATCH_WEIGHTS      "probe_collection/match_weights/pos"
104#define AWAR_PC_MATCH_WIDTH        "probe_collection/match_weights/width"
105#define AWAR_PC_MATCH_BIAS         "probe_collection/match_weights/bias"
106#define AWAR_PC_AUTO_MATCH         "probe_collection/auto"
107#define AWAR_PC_CURRENT_COLLECTION "probe_collection/current"
108
109#define AWAR_PC_SELECTED_PROBE     "tmp/probe_collection/probe"
110#define AWAR_PC_MATCH_NHITS        "tmp/probe_collection/nhits"
111
112// probe collection matching control parameters
113#define AWAR_PC_MISMATCH_THRESHOLD "probe_collection/mismatch_threshold"
114
115#define REPLACE_TARGET_CONTROL_CHARS ":#=_:\\:=_"
116
117// ----------------------------------------
118
119static saiProbeData *g_spd = NULp;
120
121static struct {
122    aisc_com          *link;
123    T_PT_LOCS          locs;
124    T_PT_MAIN          com;
125    AW_window_simple  *win;
126    AW_selection_list *resultList; // @@@ replace by TypedSelectionList?
127} PD;
128
129struct ProbeMatchEventParam : virtual Noncopyable {
130    GBDATA            *gb_main;
131    AW_selection_list *selection_id;                // may be NULp
132    int               *counter;                     // may be NULp (if specified -> afterwards contains number of hits)
133
134    // results set by probe_match_event:
135    std::string hits_summary; // shown in probe match window
136    bool        refresh_sai_display;
137
138    void init_results() {
139        refresh_sai_display = false;
140    }
141
142    ProbeMatchEventParam(GBDATA *gb_main_, int *counter_)         : gb_main(gb_main_), selection_id(NULp), counter(counter_) { init_results(); }
143    ProbeMatchEventParam(GBDATA *gb_main_, AW_selection_list *id) : gb_main(gb_main_), selection_id(id),   counter(NULp)     { init_results(); }
144};
145
146struct AutoMatchSettings {
147    ProbeMatchEventParam *event_param; // not owned!
148    bool                  disable;
149
150    AutoMatchSettings(ProbeMatchEventParam *event_param_, bool disable_)
151        : event_param(event_param_), disable(disable_)
152    {}
153    AutoMatchSettings() :
154        event_param(NULp),
155        disable(true)
156    {}
157
158    bool initialized() const { return event_param; }
159};
160
161static AutoMatchSettings auto_match_cb_settings;
162
163// prototypes:
164static void probe_match_event_using_awars(AW_root *root, ProbeMatchEventParam *event_param);
165
166static void auto_match_cb(AW_root *root) {
167    if (!auto_match_cb_settings.disable) {
168        char *ts = root->awar(AWAR_TARGET_STRING)->read_string();
169        if (strlen(ts) > 0) {
170            probe_match_event_using_awars(root, auto_match_cb_settings.event_param);
171        }
172        free(ts);
173    }
174}
175
176static const char *auto_match_sensitive_awars[] = {
177    AWAR_TARGET_STRING,
178    AWAR_PT_SERVER,
179    AWAR_PD_MATCH_ALSO_REVCOMP,
180    AWAR_MAX_MISMATCHES,
181    AWAR_PD_MATCH_SORTBY,
182    NULp
183};
184
185static void auto_match_changed(AW_root *root) {
186    static bool callback_active = false;
187    int         autoMatch       = root->awar(AWAR_PD_MATCH_AUTOMATCH)->read_int();
188
189    if (autoMatch) {
190        if (!callback_active) {
191            for (int i = 0; auto_match_sensitive_awars[i]; ++i) {
192                root->awar(auto_match_sensitive_awars[i])->add_callback(auto_match_cb);
193            }
194        }
195    }
196    else {
197        if (callback_active) {
198            for (int i = 0; auto_match_sensitive_awars[i]; ++i) {
199                root->awar(auto_match_sensitive_awars[i])->remove_callback(auto_match_cb);
200            }
201        }
202    }
203    callback_active = bool(autoMatch);
204}
205
206static void enable_auto_match_cb(AW_root *root, ProbeMatchEventParam *event_param) {
207    if (!event_param && auto_match_cb_settings.initialized()) {
208        // "partially" enable (w/o ProbeMatchEventParam) is only done
209        // if not already "fully enabled"
210        return;
211    }
212
213    auto_match_cb_settings = AutoMatchSettings(event_param, false);
214    auto_match_changed(root);
215}
216
217static void popup_match_window_cb(AW_window *aww, GBDATA *gb_main) { // @@@ shall be called by auto_match_cb (if never opened b4)
218    AW_root   *root         = aww->get_root();
219    AW_window *match_window = create_probe_match_window(root, gb_main);
220    match_window->activate();
221    root->awar(AWAR_TARGET_STRING)->touch(); // force re-match
222}
223
224// --------------------------------------------------------------------------------
225
226static void popup_probe_design_result_window(AW_window *aww, GBDATA *gb_main) {
227    if (!PD.win) {
228        AW_root *root = aww->get_root();
229
230        PD.win = new AW_window_simple;
231        PD.win->init(root, "PD_RESULT", "PD RESULT");
232        PD.win->load_xfig("pd_reslt.fig");
233
234        PD.win->button_length(6);
235        PD.win->auto_space(10, 10);
236
237        PD.win->at("help");
238        PD.win->callback(makeHelpCallback("probedesignresult.hlp"));
239        PD.win->create_button("HELP", "HELP", "H");
240
241        PD.win->at("result");
242        PD.resultList = PD.win->create_selection_list(AWAR_TARGET_STRING, 40, 5, false);
243        const StorableSelectionList *storable_result_list = new StorableSelectionList(TypedSelectionList("prb", PD.resultList, "designed probes", "designed")); // @@@ make member of PD ?
244
245        PD.resultList->clear();
246        PD.resultList->insert_default("No probes designed yet", "");
247
248        PD.win->at("buttons");
249
250        PD.win->callback(AW_POPDOWN);
251        PD.win->create_button("CLOSE", "CLOSE", "C");
252
253        PD.win->callback(makeWindowCallback(awt_clear_selection_list_cb, PD.resultList));
254        PD.win->create_button("CLEAR", "CLEAR", "R");
255
256        PD.win->callback(makeCreateWindowCallback(create_load_box_for_selection_lists, storable_result_list));
257        PD.win->create_button("LOAD", "LOAD", "L");
258
259        PD.win->callback(makeCreateWindowCallback(create_save_box_for_selection_lists, storable_result_list));
260        PD.win->create_button("SAVE", "SAVE", "S");
261
262        PD.win->callback(makeWindowCallback(create_print_box_for_selection_lists, &storable_result_list->get_typedsellist()));
263        PD.win->create_button("PRINT", "PRINT", "P");
264
265        PD.win->callback(makeWindowCallback(popup_match_window_cb, gb_main));
266        PD.win->create_button("MATCH", "MATCH", "M");
267
268        PD.win->label("Auto match");
269        PD.win->create_toggle(AWAR_PD_MATCH_AUTOMATCH);
270
271        enable_auto_match_cb(root, NULp);
272    }
273    PD.win->activate();
274}
275
276static int init_local_com_struct() {
277    const char *user = GB_getenvUSER();
278
279    if (aisc_create(PD.link, PT_MAIN, PD.com,
280                    MAIN_LOCS, PT_LOCS, PD.locs,
281                    LOCS_USER, user,
282                    NULp)) {
283        return 1;
284    }
285    return 0;
286}
287
288static const char *PD_probe_pt_look_for_server(int serverNumber, GB_ERROR& error) {
289    // return PT server info string (see GBS_read_arb_tcp for details)
290    // or NULp (in this case 'error' is set)
291
292    // DRY vs  ../TOOLS/arb_probe.cxx@AP_probe_pt_look_for_server
293    // DRY vs  ../MULTI_PROBE/MP_noclass.cxx@MP_probe_pt_look_for_server
294
295    const char *result     = NULp;
296    const char *server_tag = GBS_ptserver_tag(serverNumber);
297
298    error = arb_look_and_start_server(AISC_MAGIC_NUMBER, server_tag);
299    if (!error) {
300        result             = GBS_read_arb_tcp(server_tag);
301        if (!result) error = GB_await_error();
302    }
303    pd_assert(contradicted(result, error));
304    return result;
305}
306
307static GB_ERROR species_requires(GBDATA *gb_species, const char *whats_required) {
308    return GBS_global_string("Species '%s' needs %s", GBT_get_name_or_description(gb_species), whats_required);
309}
310
311static GB_ERROR gene_requires(GBDATA *gb_gene, const char *whats_required) {
312    GBDATA *gb_species = GB_get_grandfather(gb_gene);
313    pd_assert(gb_species);
314    return GBS_global_string("Gene '%s' of organism '%s' needs %s", GBT_get_name_or_description(gb_gene), GBT_get_name_or_description(gb_species), whats_required);
315}
316
317static GB_ERROR pd_get_the_names(GBDATA *gb_main, bytestring &bs, bytestring &checksum) {
318    GBS_strstruct *names     = GBS_stropen(1024);
319    GBS_strstruct *checksums = GBS_stropen(1024);
320    GB_ERROR       error     = NULp;
321
322    GB_begin_transaction(gb_main);
323
324    char *use = GBT_get_default_alignment(gb_main);
325
326    for (GBDATA *gb_species = GBT_first_marked_species(gb_main); gb_species; gb_species = GBT_next_marked_species(gb_species)) {
327        GBDATA *gb_name = GB_entry(gb_species, "name");
328        if (!gb_name) { error = species_requires(gb_species, "name"); break; }
329
330        GBDATA *gb_data = GBT_find_sequence(gb_species, use);
331        if (!gb_data) { error = species_requires(gb_species, GBS_global_string("data in '%s'", use)); break; }
332
333        GBS_intcat(checksums, GBS_checksum(GB_read_char_pntr(gb_data), 1, ".-"));
334        GBS_strcat(names, GB_read_char_pntr(gb_name));
335        GBS_chrcat(checksums, '#');
336        GBS_chrcat(names, '#');
337    }
338
339    GBS_str_cut_tail(names, 1); // remove trailing '#'
340    GBS_str_cut_tail(checksums, 1); // remove trailing '#'
341
342    free(use);
343
344    bs.size = GBS_memoffset(names)+1;
345    bs.data = GBS_strclose(names);
346
347    checksum.size = GBS_memoffset(checksums)+1;
348    checksum.data = GBS_strclose(checksums);
349
350    GB_commit_transaction(gb_main);
351
352    return error;
353}
354
355static GB_ERROR pd_get_the_gene_names(GBDATA *gb_main, bytestring &bs, bytestring &checksum) {
356    GBS_strstruct *names     = GBS_stropen(1024);
357    GBS_strstruct *checksums = GBS_stropen(1024);
358    GB_ERROR       error     = NULp;
359
360    GB_begin_transaction(gb_main);
361    const char *use = GENOM_ALIGNMENT; // gene pt server is always build on 'ali_genom'
362
363    for (GBDATA *gb_species = GEN_first_organism(gb_main); gb_species && !error; gb_species = GEN_next_organism(gb_species)) {
364        const char *species_name = NULp;
365        {
366            GBDATA *gb_data = GBT_find_sequence(gb_species, use);
367            if (!gb_data) { error = species_requires(gb_species, GBS_global_string("data in '%s'", use)); break; }
368
369            GBDATA *gb_name = GB_search(gb_species, "name", GB_FIND);
370            if (!gb_name) { error = species_requires(gb_species, "name"); break; }
371            species_name = GB_read_char_pntr(gb_name);
372        }
373
374        for (GBDATA *gb_gene = GEN_first_marked_gene(gb_species); gb_gene && !error; gb_gene = GEN_next_marked_gene(gb_gene)) {
375            const char *gene_name = NULp;
376            {
377                GBDATA *gb_gene_name = GB_entry(gb_gene, "name");
378                if (!gb_gene_name) { error = gene_requires(gb_gene, "name"); break; }
379                gene_name = GB_read_char_pntr(gb_gene_name);
380            }
381
382            {
383                char *gene_seq       = GBT_read_gene_sequence(gb_gene, false, 0);
384                if (!gene_seq) error = GB_await_error();
385                else {
386                    long        CheckSum = GBS_checksum(gene_seq, 1, ".-");
387                    const char *id       = GBS_global_string("%s/%s", species_name, gene_name);
388
389                    GBS_intcat(checksums, CheckSum);
390                    GBS_strcat(names, id);
391                    GBS_chrcat(checksums, '#');
392                    GBS_chrcat(names, '#');
393
394                    free(gene_seq);
395                }
396            }
397        }
398    }
399
400    GBS_str_cut_tail(names, 1); // remove trailing '#'
401    GBS_str_cut_tail(checksums, 1); // remove trailing '#'
402
403    if (error) {
404        GBS_strforget(names);
405        GBS_strforget(checksums);
406    }
407    else {
408        bs.size = GBS_memoffset(names)+1;
409        bs.data = GBS_strclose(names);
410
411        checksum.size = GBS_memoffset(checksums)+1;
412        checksum.data = GBS_strclose(checksums);
413    }
414    error = GB_end_transaction(gb_main, error);
415    return error;
416}
417
418inline const char *bond_awar_name(int i) {
419    return GBS_global_string(AWAR_PD_COMMON_EXP_BONDS_FORMAT, i);
420}
421
422struct ProbeCommonSettings {
423    float split;
424    float bonds[16];
425
426    explicit ProbeCommonSettings(AW_root *root)
427        : split(root->awar(AWAR_PD_COMMON_EXP_SPLIT)->read_float())
428    {
429        for (int i=0; i<16; i++) {
430            bonds[i] = root->awar(bond_awar_name(i))->read_float();
431        }
432    }
433};
434
435static int probe_common_send_data(const ProbeCommonSettings& commonSettings) {
436    // send data common to probe-design AND -match
437    if (aisc_put(PD.link, PT_LOCS, PD.locs,
438                 LOCS_SPLIT, (double)commonSettings.split,
439                 NULp))
440        return 1;
441
442    for (int i=0; i<16; i++) {
443        if (aisc_put(PD.link, PT_LOCS, PD.locs,
444                     PT_INDEX,     (long)i,
445                     LOCS_BONDVAL, (double)commonSettings.bonds[i],
446                     NULp))
447            return 1;
448    }
449    return 0;
450}
451static int probe_design_send_data(AW_root *root, const T_PT_PDC& pdc) {
452    if (aisc_put(PD.link, PT_PDC, pdc,
453                 PDC_DTEDGE,     (double)root->awar(AWAR_PD_DESIGN_EXP_DTEDGE)->read_float()*100.0,
454                 PDC_DT,         (double)root->awar(AWAR_PD_DESIGN_EXP_DT)->read_float()*100.0,
455                 PDC_CLIPRESULT, (long)root->awar(AWAR_PD_DESIGN_CLIPRESULT)->read_int(),
456                 NULp))
457        return 1;
458
459    return probe_common_send_data(ProbeCommonSettings(root));
460}
461
462struct ProbeMatchSettings : public ProbeCommonSettings {
463    int serverNumber;
464    int nmatches;
465    int nlimit;
466    int maxhits;
467    int alsoRevcompl;
468    int sortBy;
469    int maxMismatches;
470    int markHits;
471    int write2tmp;
472
473    std::string targetString;
474
475    explicit ProbeMatchSettings(AW_root *root)
476        : ProbeCommonSettings(root),
477          serverNumber(root->awar(AWAR_PT_SERVER)->read_int()),
478          nmatches(root->awar(AWAR_PD_MATCH_NMATCHES)->read_int()),
479          nlimit(root->awar(AWAR_PD_MATCH_LIM_NMATCH)->read_int()),
480          maxhits(root->awar(AWAR_PD_MATCH_MAX_RES)->read_int()),
481          alsoRevcompl(root->awar(AWAR_PD_MATCH_ALSO_REVCOMP)->read_int()),
482          sortBy(root->awar(AWAR_PD_MATCH_SORTBY)->read_int()),
483          maxMismatches(root->awar(AWAR_MAX_MISMATCHES)->read_int()),
484          markHits(root->awar(AWAR_PD_MATCH_MARKHITS)->read_int()),
485          write2tmp(root->awar(AWAR_PD_MATCH_WRITE2TMP)->read_int()),
486          targetString(root->awar(AWAR_TARGET_STRING)->read_char_pntr())
487    {}
488};
489
490static int probe_match_send_data(const ProbeMatchSettings& matchSettings) {
491    if (aisc_put(PD.link, PT_LOCS, PD.locs,
492                 LOCS_MATCH_N_ACCEPT, (long)matchSettings.nmatches,
493                 LOCS_MATCH_N_LIMIT,  (long)matchSettings.nlimit,
494                 LOCS_MATCH_MAX_HITS, (long)matchSettings.maxhits,
495                 NULp))
496        return 1;
497
498    return probe_common_send_data(matchSettings);
499}
500
501static int ecolipos2int(const char *awar_val) {
502    int i = atoi(awar_val);
503    return i>0 ? i : -1;
504}
505
506static char *readableUnknownNames(const ConstStrArray& unames) {
507    GBS_strstruct readable(100);
508
509    int ulast = unames.size()-1;
510    int umax  = ulast <= 10 ? ulast : 10;
511    for (int u = 0; u <= umax; ++u) {
512        if (u) readable.cat(u == ulast ? " and " : ", ");
513        readable.cat(unames[u]);
514    }
515
516    if (umax<ulast) readable.nprintf(30, " (and %i other)", ulast-umax);
517
518    return readable.release();
519}
520
521static void probe_design_event(AW_window *aww, GBDATA *gb_main) {
522    AW_root     *root  = aww->get_root();
523    T_PT_PDC     pdc;
524    T_PT_TPROBE  tprobe;
525    bytestring   bs;
526    bytestring   check;
527    GB_ERROR     error = NULp;
528
529    arb_progress progress("Probe design");
530    progress.subtitle("Connecting PT-server");
531
532    {
533        const char *servername = PD_probe_pt_look_for_server(root->awar(AWAR_PT_SERVER)->read_int(), error);
534        if (servername) {
535            PD.link = aisc_open(servername, PD.com, AISC_MAGIC_NUMBER, &error);
536            if (!PD.link) error = "can't contact PT server";
537            PD.locs.clear();
538        }
539    }
540
541    if (!error && init_local_com_struct()) {
542        error = "Cannot contact to probe server: Connection Refused";
543    }
544
545    bool design_gene_probes = root->awar(AWAR_PD_DESIGN_GENE)->read_int();
546    if (design_gene_probes) {
547        GB_transaction ta(gb_main);
548        if (!GEN_is_genome_db(gb_main, -1)) design_gene_probes = false;
549    }
550
551    if (!error) {
552        if (design_gene_probes) { // design probes for genes
553            error = pd_get_the_gene_names(gb_main, bs, check);
554        }
555        else {
556            error = pd_get_the_names(gb_main, bs, check);
557        }
558    }
559
560    if (error) {
561        aw_message(error);
562        return;
563    }
564
565    progress.subtitle("probe design running");
566
567    if (aisc_create(PD.link, PT_LOCS, PD.locs,
568                    LOCS_PROBE_DESIGN_CONFIG, PT_PDC, pdc,
569                    PDC_MIN_PROBELEN, (long)root->awar(AWAR_PD_DESIGN_MIN_LENGTH)->read_int(),
570                    PDC_MAX_PROBELEN, (long)root->awar(AWAR_PD_DESIGN_MAX_LENGTH)->read_int(),
571                    PDC_MINTEMP,      (double)root->awar(AWAR_PD_DESIGN_MIN_TEMP)->read_float(),
572                    PDC_MAXTEMP,      (double)root->awar(AWAR_PD_DESIGN_MAX_TEMP)->read_float(),
573                    PDC_MINGC,        (double)root->awar(AWAR_PD_DESIGN_MIN_GC)->read_float()/100.0,
574                    PDC_MAXGC,        (double)root->awar(AWAR_PD_DESIGN_MAX_GC)->read_float()/100.0,
575                    PDC_MAXBOND,      (double)root->awar(AWAR_PD_DESIGN_MAXBOND)->read_int(),
576                    PDC_MIN_ECOLIPOS, (long)ecolipos2int(root->awar(AWAR_PD_DESIGN_MIN_ECOLIPOS)->read_char_pntr()),
577                    PDC_MAX_ECOLIPOS, (long)ecolipos2int(root->awar(AWAR_PD_DESIGN_MAX_ECOLIPOS)->read_char_pntr()),
578                    PDC_MISHIT,       (long)root->awar(AWAR_PD_DESIGN_MISHIT)->read_int(),
579                    PDC_MINTARGETS,   (double)root->awar(AWAR_PD_DESIGN_MINTARGETS)->read_float()/100.0,
580                    NULp))
581    {
582        aw_message("Connection to PT_SERVER lost (1)");
583        return;
584    }
585
586    if (probe_design_send_data(root, pdc)) {
587        aw_message("Connection to PT_SERVER lost (1)");
588        return;
589    }
590
591    aisc_put(PD.link, PT_PDC, pdc,
592             PDC_NAMES, &bs,
593             PDC_CHECKSUMS, &check,
594             NULp);
595
596
597    // Get the unknown names
598    bytestring unknown_names;
599    if (aisc_get(PD.link, PT_PDC, pdc,
600                 PDC_UNKNOWN_NAMES, &unknown_names,
601                 NULp))
602    {
603        aw_message("Connection to PT_SERVER lost (1)");
604        return;
605    }
606
607    bool abort = false;
608
609    if (unknown_names.size>1) {
610        ConstStrArray unames_array;
611        GBT_split_string(unames_array, unknown_names.data, "#", true);
612
613        char *readable_unames = readableUnknownNames(unames_array);
614
615        if (design_gene_probes) { // updating sequences of missing genes is not possible with gene PT server
616            aw_message(GBS_global_string("Your PT server is not up to date or wrongly chosen!\n"
617                                         "It knows nothing about the following genes:\n"
618                                         "\n"
619                                         "  %s\n"
620                                         "\n"
621                                         "You have to rebuild the PT server.",
622                                         readable_unames));
623            abort = true;
624        }
625        else if (aw_question("ptserver_add_unknown",
626                             GBS_global_string("Your PT server is not up to date or wrongly chosen!\n"
627                                               "It knows nothing about the following species:\n"
628                                               "\n"
629                                               "  %s\n"
630                                               "\n"
631                                               "You may now temporarily add these sequences for probe design.\n",
632                                               readable_unames),
633                             "Add and continue,Abort"))
634        {
635            abort = true;
636        }
637        else {
638            GB_transaction ta(gb_main);
639
640            char *ali_name = GBT_get_default_alignment(gb_main);
641
642            for (size_t u = 0; !abort && u<unames_array.size(); ++u) {
643                const char *uname      = unames_array[u];
644                GBDATA     *gb_species = GBT_find_species(gb_main, uname);
645                if (!gb_species) {
646                    aw_message(GBS_global_string("Species '%s' not found", uname));
647                    abort = true;
648                }
649                else {
650                    GBDATA *data = GBT_find_sequence(gb_species, ali_name);
651                    if (!data) {
652                        aw_message(GB_export_errorf("Species '%s' has no sequence belonging to alignment '%s'", uname, ali_name));
653                        abort = true;
654                    }
655                    else {
656                        T_PT_SEQUENCE pts;
657
658                        bytestring bs_seq;
659                        bs_seq.data = (char*)GB_read_char_pntr(data);
660                        bs_seq.size = GB_read_string_count(data)+1;
661
662                        aisc_create(PD.link, PT_PDC, pdc,
663                                    PDC_SEQUENCE, PT_SEQUENCE, pts,
664                                    SEQUENCE_SEQUENCE, &bs_seq,
665                                    NULp);
666                    }
667                }
668            }
669            free(ali_name);
670        }
671        free(readable_unames);
672        free(unknown_names.data);
673    }
674
675    if (!abort) {
676        aisc_put(PD.link, PT_PDC, pdc,
677                 PDC_GO, (long)0,
678                 NULp);
679
680        progress.subtitle("Reading results from server");
681        {
682            char *locs_error = NULp;
683            if (aisc_get(PD.link, PT_LOCS, PD.locs,
684                         LOCS_ERROR, &locs_error,
685                         NULp))
686            {
687                aw_message("Connection to PT_SERVER lost (1)");
688                abort = true;
689            }
690            else if (*locs_error) {
691                aw_message(locs_error);
692                abort = true;
693            }
694            else {
695                free(locs_error);
696            }
697        }
698    }
699
700    if (!abort) {
701        aisc_get(PD.link, PT_PDC, pdc,
702                 PDC_TPROBE, tprobe.as_result_param(),
703                 NULp);
704
705        popup_probe_design_result_window(aww, gb_main);
706        PD.resultList->clear();
707
708        {
709            char *match_info = NULp;
710            aisc_get(PD.link, PT_PDC, pdc,
711                     PDC_INFO_HEADER, &match_info,
712                     NULp);
713
714            char *s = strtok(match_info, "\n");
715            while (s) {
716                PD.resultList->insert(s, "");
717                s = strtok(NULp, "\n");
718            }
719            free(match_info);
720        }
721
722        while (tprobe.exists()) {
723            T_PT_TPROBE  tprobe_next;
724            char        *match_info = NULp;
725
726            if (aisc_get(PD.link, PT_TPROBE, tprobe,
727                         TPROBE_NEXT, tprobe_next.as_result_param(),
728                         TPROBE_INFO, &match_info,
729                         NULp)) break;
730
731            tprobe.assign(tprobe_next);
732
733            char *probe, *space;
734            probe = strpbrk(match_info, "acgtuACGTU");
735            if (probe) space = strchr(probe, ' ');
736            if (probe && space) {
737                *space = 0; probe = ARB_strdup(probe); *space=' ';
738            }
739            else {
740                probe = ARB_strdup("");
741            }
742            PD.resultList->insert(match_info, probe);
743            free(probe);
744            free(match_info);
745        }
746        PD.resultList->insert_default("", "");
747        PD.resultList->update();
748    }
749
750    aisc_close(PD.link, PD.com); PD.link = NULp;
751    return;
752}
753
754static bool allow_probe_match_event = true;
755
756static __ATTR__USERESULT GB_ERROR probe_match_event(const ProbeMatchSettings& matchSettings, ProbeMatchEventParam *event_param) {
757    GB_ERROR error = NULp;
758    if (allow_probe_match_event) {
759#if defined(ASSERTION_USED)
760        // runtime check against awar usage when NOT called from probe match window!
761        typedef SmartPtr< LocallyModify<bool> > Maybe;
762
763        Maybe dontReadAwars;
764        Maybe dontWriteAwars;
765
766        if (!event_param || !event_param->selection_id) {
767            dontReadAwars = new LocallyModify<bool>(AW_awar::deny_read, true);
768            dontWriteAwars = new LocallyModify<bool>(AW_awar::deny_write, true);
769        }
770#endif
771
772        AW_selection_list *selection_id = event_param ? event_param->selection_id : NULp;
773        int               *counter      = event_param ? event_param->counter : NULp;
774        GBDATA            *gb_main      = event_param ? event_param->gb_main : NULp;
775        int                show_status  = 0;
776        int                extras       = 1;        // mark species and write to temp fields
777
778        if (!gb_main) { error = "Please open probe match window once to enable auto-match"; }
779
780        SmartPtr<arb_progress> progress;
781
782        if (!error) {
783            const char *servername = PD_probe_pt_look_for_server(matchSettings.serverNumber, error);
784
785            if (!error) {
786                if (selection_id) {
787                    selection_id->clear();
788                    pd_assert(!counter);
789                    show_status = 1;
790                }
791                else if (counter) {
792                    extras = 0;
793                }
794
795                if (show_status) {
796                    progress = new arb_progress("Probe match");
797                    progress->subtitle("Connecting PT-server");
798                }
799
800                PD.link = aisc_open(servername, PD.com, AISC_MAGIC_NUMBER, &error);
801                if (!error && !PD.link) error = "Cannot contact PT-server";
802                PD.locs.clear();
803            }
804        }
805
806        if (!error && init_local_com_struct())              error = "Cannot contact PT-server (2)";
807        if (!error && probe_match_send_data(matchSettings)) error = "Connection to PT_SERVER lost (2)";
808
809        const char *probe = matchSettings.targetString.c_str();
810        if (!error) {
811            if (show_status) progress->subtitle("Probe match running");
812
813            if (aisc_nput(PD.link, PT_LOCS, PD.locs,
814                          LOCS_MATCH_ALSO_REVCOMP,   (long)matchSettings.alsoRevcompl,
815                          LOCS_COMPLEMENT_FIRST,     (long)0, // (use sequence passed below as is. do not complement it.)
816                          LOCS_MATCH_SORT_BY,        (long)matchSettings.sortBy,
817                          LOCS_MATCH_MAX_MISMATCHES, (long)matchSettings.maxMismatches,
818                          LOCS_SEARCHMATCH,          probe,
819                          NULp))
820            {
821                error = "Connection to PT_SERVER lost (2)";
822            }
823            else {
824                delete(g_spd);              // delete previous probe data
825                g_spd = new saiProbeData;
826                transferProbeData(g_spd);
827
828                g_spd->setProbeTarget(probe);
829            }
830        }
831
832        bytestring bs;
833        bs.data = NULp;
834
835        long matches_truncated = 0;
836        if (!error) {
837            if (show_status) progress->subtitle("Reading results");
838
839            T_PT_MATCHLIST  match_list;
840            long            match_list_cnt    = 0;
841            char           *locs_error        = NULp;
842
843            if (aisc_get(PD.link, PT_LOCS, PD.locs,
844                         LOCS_MATCH_LIST,        match_list.as_result_param(),
845                         LOCS_MATCH_LIST_CNT,    &match_list_cnt,
846                         LOCS_MATCH_STRING,      &bs,
847                         LOCS_MATCHES_TRUNCATED, &matches_truncated,
848                         LOCS_ERROR,             &locs_error,
849                         NULp))
850            {
851                error = "Connection to PT_SERVER lost (3)";
852            }
853            else {
854                if (locs_error) {
855                    if (*locs_error) error = GBS_static_string(locs_error);
856                    free(locs_error);
857                }
858                else {
859                    error = "Missing status from server (connection aborted?)";
860                }
861            }
862
863            event_param->hits_summary = GBS_global_string(matches_truncated ? "[more than %li]" : "%li", match_list_cnt);
864            if (matches_truncated) {
865                aw_message("Too many matches, displaying a random digest.\n" // @@@ should better be handled by caller
866                           "Increase the limit in the expert window.");
867            }
868        }
869
870        long mcount                = 0;
871        long unknown_species_count = 0;
872        long unknown_gene_count    = 0;
873
874        if (!error) {
875            char        toksep[2]  = { 1, 0 };
876            char       *strtok_ptr = NULp; // stores strtok position
877            const char *hinfo      = strtok_r(bs.data, toksep, &strtok_ptr);
878
879            bool              gene_flag = false;
880            ProbeMatchParser *parser    = NULp;
881            char             *result    = ARB_alloc<char>(1024);
882
883
884            if (hinfo) {
885                g_spd->setHeadline(hinfo);
886                parser = new ProbeMatchParser(probe, hinfo);
887                error  = parser->get_error();
888                if (!error) gene_flag = parser->is_gene_result();
889            }
890
891            if (selection_id) {
892                int width         = 0;
893                if (parser && !error) width = parser->get_probe_region_offset()+2+10; // 2 cause headline is shorter and 10 for match prefix region
894
895                const char *searched = GBS_global_string("%-*s%s", width, "Searched for ", probe);
896                selection_id->insert(searched, probe);
897                if (hinfo) selection_id->insert(hinfo, "");
898            }
899
900
901            // clear all marks and delete all 'tmp' entries
902
903            int mark        = extras && matchSettings.markHits;
904            int write_2_tmp = extras && matchSettings.write2tmp;
905
906            GB_push_transaction(gb_main);
907
908            GBDATA *gb_species_data = GBT_get_species_data(gb_main);
909            if (mark && !error) {
910                if (show_status) progress->subtitle(gene_flag ? "Unmarking all species and genes" : "Unmarking all species");
911                for (GBDATA *gb_species = GBT_first_marked_species_rel_species_data(gb_species_data);
912                     gb_species;
913                     gb_species = GBT_next_marked_species(gb_species))
914                {
915                    GB_write_flag(gb_species, 0);
916                }
917
918                if (gene_flag) {
919                    // unmark genes of ALL species
920                    for (GBDATA *gb_species = GBT_first_species_rel_species_data(gb_species_data);
921                         gb_species;
922                         gb_species = GBT_next_species(gb_species))
923                    {
924                        GBDATA *genData = GEN_find_gene_data(gb_species);
925                        if (genData) {
926                            for (GBDATA *gb_gene = GEN_first_marked_gene(gb_species);
927                                 gb_gene;
928                                 gb_gene = GEN_next_marked_gene(gb_gene))
929                            {
930                                GB_write_flag(gb_gene, 0);
931                            }
932                        }
933                    }
934                }
935            }
936            if (write_2_tmp && !error) {
937                if (show_status) progress->subtitle("Deleting old 'tmp' fields");
938                for (GBDATA *gb_species = GBT_first_species_rel_species_data(gb_species_data);
939                     gb_species;
940                     gb_species = GBT_next_species(gb_species))
941                {
942                    GBDATA *gb_tmp = GB_entry(gb_species, "tmp");
943                    if (gb_tmp) GB_delete(gb_tmp);
944                    if (gene_flag) {
945                        for (GBDATA *gb_gene = GEN_first_gene(gb_species);
946                             gb_gene;
947                             gb_gene = GEN_next_gene(gb_species))
948                        {
949                            gb_tmp = GB_entry(gb_gene, "tmp");
950                            if (gb_tmp) GB_delete(gb_tmp);
951                        }
952                    }
953                }
954            }
955
956            // read results from pt-server :
957
958            if (!error) {
959                if (show_status) progress->subtitle("Parsing results");
960
961                g_spd->probeSpecies.clear();
962                g_spd->probeSeq.clear();
963
964                if (gene_flag) {
965                    if (!GEN_is_genome_db(gb_main, -1)) {
966                        error = "Wrong PT-Server chosen (selected one is built for genes)";
967                    }
968                }
969            }
970
971            const char *match_name = NULp;
972            while (hinfo && !error && (match_name = strtok_r(NULp, toksep, &strtok_ptr))) {
973                const char *match_info = strtok_r(NULp, toksep, &strtok_ptr);
974                if (!match_info) break;
975
976                pd_assert(parser);
977                ParsedProbeMatch  ppm(match_info, *parser);
978                char             *gene_str = NULp;
979
980                if (gene_flag) {
981                    gene_str = ppm.get_column_content("genename", true);
982                }
983
984                if (!error) {
985                    char flags[]       = "xx";
986                    GBDATA *gb_species = GBT_find_species_rel_species_data(gb_species_data, match_name);
987
988                    if (gb_species) {
989                        GBDATA *gb_gene = NULp;
990                        if (gene_flag && strncmp(gene_str, "intergene_", 10) != 0) { // real gene
991                            gb_gene = GEN_find_gene(gb_species, gene_str);
992                            if (!gb_gene) {
993                                aw_message(GBS_global_string("Gene '%s' not found in organism '%s'", gene_str, match_name));
994                            }
995                        }
996
997                        if (mark) {
998                            GB_write_flag(gb_species, 1);
999                            flags[0] = '*';
1000                            if (gb_gene) {
1001                                GB_write_flag(gb_gene, 1);
1002                                flags[1] = '*';
1003                            }
1004                            else {
1005                                flags[1] = '?'; // no gene
1006                            }
1007                        }
1008                        else {
1009                            flags[0] = " *"[GB_read_flag(gb_species)];
1010                            flags[1] = " *?"[gb_gene ? GB_read_flag(gb_gene) : 2];
1011                        }
1012
1013                        if (write_2_tmp) {
1014                            // write or append to field 'tmp'
1015
1016                            GBDATA   *gb_tmp = NULp;
1017                            GB_ERROR  error2 = NULp;
1018                            bool      append = true;
1019                            {
1020                                GBDATA *gb_parent = gene_flag ? gb_gene : gb_species;
1021                                gb_tmp            = GB_search(gb_parent, "tmp", GB_FIND);
1022                                if (!gb_tmp) {
1023                                    append              = false;
1024                                    gb_tmp              = GB_search(gb_parent, "tmp", GB_STRING);
1025                                    if (!gb_tmp) error2 = GB_await_error();
1026                                }
1027                            }
1028
1029                            if (!error2) {
1030                                pd_assert(gb_tmp);
1031                                if (append) {
1032                                    char *prevContent = GB_read_string(gb_tmp);
1033                                    if (!prevContent) {
1034                                        error2 = GB_await_error();
1035                                    }
1036                                    else {
1037                                        char *concatenatedContent = ARB_alloc<char>(strlen(prevContent)+1+strlen(match_info)+1);
1038                                        sprintf(concatenatedContent, "%s\n%s", prevContent, match_info);
1039                                        error2 = GB_write_string(gb_tmp, concatenatedContent);
1040                                        free(concatenatedContent);
1041                                        free(prevContent);
1042                                    }
1043                                }
1044                                else {
1045                                    error2 = GB_write_string(gb_tmp, match_info);
1046                                }
1047                            }
1048
1049                            if (error2) aw_message(error2);
1050                        }
1051                    }
1052                    else {
1053                        flags[0] = flags[1] = '?'; // species does not exist
1054                        unknown_species_count++;
1055                    }
1056
1057
1058                    if (gene_flag) {
1059                        sprintf(result, "%s %s", flags, match_info+1); // both flags (skip 1 space from match info to keep alignment)
1060                        char *gene_match_name = new char[strlen(match_name) + strlen(gene_str)+2];
1061                        sprintf(gene_match_name, "%s/%s", match_name, gene_str);
1062                        if (selection_id) selection_id->insert(result, gene_match_name);   // @@@ wert fuer awar eintragen
1063                    }
1064                    else {
1065                        sprintf(result, "%c %s", flags[0], match_info); // only first flag ( = species related)
1066                        if (selection_id) selection_id->insert(result, match_name);   // @@@ wert fuer awar eintragen
1067
1068                        // storing probe data into linked lists
1069                        g_spd->probeSeq.push_back(std::string(match_info));
1070                        g_spd->probeSpecies.push_back(std::string(match_name));
1071                    }
1072                    mcount++;
1073                }
1074
1075                free(gene_str);
1076            }
1077
1078            if (error) error = GBS_static_string(error); // make static copy (error may be freed by delete parser)
1079            delete parser;
1080            free(result);
1081
1082            GB_pop_transaction(gb_main);
1083        }
1084
1085        if (error) {
1086            if (event_param) event_param->hits_summary = "[none]"; // clear hits
1087        }
1088        else {
1089            if (unknown_species_count>0) {
1090                error = GBS_global_string("%li matches hit unknown species -- PT-server is out-of-date or build upon a different database", unknown_species_count);
1091            }
1092            if (unknown_gene_count>0 && !error) {
1093                error = GBS_global_string("%li matches hit unknown genes -- PT-server is out-of-date or build upon a different database", unknown_gene_count);
1094            }
1095
1096            if (selection_id) {     // if !selection_id then probe match window is not opened
1097                pd_assert(g_spd);
1098                event_param->refresh_sai_display = true; // want refresh of SAI/Probe window
1099            }
1100        }
1101
1102        if (counter) *counter = mcount;
1103
1104        aisc_close(PD.link, PD.com);
1105        PD.link = NULp;
1106
1107        if (selection_id) {
1108            const char *last_line                 = NULp;
1109            if (error) last_line                  = GBS_global_string("****** Error: %s *******", error);
1110            else if (matches_truncated) last_line = "****** List is truncated *******";
1111            else        last_line                 = "****** End of List *******";
1112
1113            selection_id->insert_default(last_line, "");
1114            selection_id->update();
1115        }
1116
1117        free(bs.data);
1118    }
1119    return error;
1120}
1121
1122static void probe_match_event_using_awars(AW_root *root, ProbeMatchEventParam *event_param) {
1123    if (allow_probe_match_event) {
1124        GB_ERROR error = probe_match_event(ProbeMatchSettings(root), event_param);
1125        aw_message_if(error);
1126
1127        LocallyModify<bool> flag(allow_probe_match_event, false);
1128
1129        if (event_param) {
1130            if (event_param->refresh_sai_display) {
1131                root->awar(AWAR_SPV_DB_FIELD_NAME)->touch(); // force refresh of SAI/Probe window
1132            }
1133            if (event_param->selection_id) {
1134                root->awar(AWAR_PD_MATCH_NHITS)->write_string(event_param->hits_summary.c_str()); // update hits in probe match window
1135            }
1136        }
1137        root->awar(AWAR_TREE_REFRESH)->touch(); // refresh tree
1138    }
1139}
1140
1141static void probe_match_all_event(AW_window *aww, AW_selection_list *iselection_id, GBDATA *gb_main) {
1142    AW_selection_list_iterator selentry(iselection_id);
1143    arb_progress               progress("Matching all resolved strings", iselection_id->size());
1144    ProbeMatchSettings         matchSettings(aww->get_root());
1145    const bool                 got_result = selentry;
1146
1147    while (selentry) {
1148        const char *entry          = selentry.get_value()->get_string(); // @@@ rename -> probe
1149        matchSettings.targetString = entry;
1150
1151        int                  counter = -1;
1152        ProbeMatchEventParam match_event(gb_main, &counter);
1153        GB_ERROR             error   = probe_match_event(matchSettings, &match_event);
1154
1155        // write # of hits to list entries:
1156        {
1157#define DISP_FORMAT "%7i %s"
1158            const char *displayed;
1159            if (error) {
1160                displayed = GBS_global_string(DISP_FORMAT " (Error: %s)", 0, entry, error);
1161            }
1162            else {
1163                displayed = GBS_global_string(DISP_FORMAT, counter, entry);
1164            }
1165            selentry.set_displayed(displayed);
1166#undef DISP_FORMAT
1167        }
1168
1169        ++selentry;
1170        progress.inc();
1171    }
1172
1173    if (got_result) {
1174        iselection_id->sort(true, true);
1175        iselection_id->update();
1176    }
1177}
1178
1179static void resolved_probe_chosen(AW_root *root) {
1180    char *string = root->awar(AWAR_PD_MATCH_RESOLVE)->read_string();
1181    root->awar(AWAR_TARGET_STRING)->write_string(string);
1182}
1183
1184static void selected_match_changed_cb(AW_root *root) {
1185    // this gets called when ever the selected probe match changes
1186    char *temp;
1187    char *selected_match = root->awar(AWAR_PD_SELECTED_MATCH)->read_string();
1188
1189    if (strchr(selected_match, '/')) { // "organism/gene"
1190        temp = strtok(selected_match, "/");
1191        root->awar(AWAR_SPECIES_NAME)->write_string(temp);
1192        temp = strtok(NULp, " /\n");
1193        root->awar(AWAR_GENE_NAME)->write_string(temp);
1194    }
1195    else {
1196        root->awar(AWAR_SPECIES_NAME)->write_string(selected_match);
1197    }
1198
1199    {
1200        LocallyModify<bool> flag(allow_probe_match_event, false); // avoid recursion
1201        root->awar(AWAR_TARGET_STRING)->touch(); // forces editor to jump to probe match in gene
1202    }
1203
1204    free(selected_match);
1205}
1206
1207static void probelength_changed_cb(AW_root *root, bool maxChanged) {
1208    static bool avoid_recursion = false;
1209    if (!avoid_recursion) {
1210        AW_awar *awar_minl = root->awar(AWAR_PD_DESIGN_MIN_LENGTH);
1211        AW_awar *awar_maxl = root->awar(AWAR_PD_DESIGN_MAX_LENGTH);
1212
1213        int minl = awar_minl->read_int();
1214        int maxl = awar_maxl->read_int();
1215
1216        if (minl>maxl) {
1217            if (maxChanged) awar_minl->write_int(maxl);
1218            else            awar_maxl->write_int(minl);
1219        }
1220    }
1221}
1222
1223static void minmax_awar_pair_changed_cb(AW_root *root, bool maxChanged, const char *minAwarName, const char *maxAwarName) {
1224    static bool avoid_recursion = false;
1225    if (!avoid_recursion) {
1226        LocallyModify<bool> flag(avoid_recursion, true);
1227
1228        AW_awar *awar_min = root->awar(minAwarName);
1229        AW_awar *awar_max = root->awar(maxAwarName);
1230
1231        float currMin = awar_min->read_float();
1232        float currMax = awar_max->read_float();
1233
1234        if (currMin>currMax) { // invalid -> correct
1235            if (maxChanged) awar_min->write_float(currMax);
1236            else            awar_max->write_float(currMin);
1237        }
1238    }
1239}
1240static void gc_minmax_changed_cb(AW_root *root, bool maxChanged) {
1241    minmax_awar_pair_changed_cb(root, maxChanged, AWAR_PD_DESIGN_MIN_GC, AWAR_PD_DESIGN_MAX_GC);
1242}
1243static void temp_minmax_changed_cb(AW_root *root, bool maxChanged) {
1244    minmax_awar_pair_changed_cb(root, maxChanged, AWAR_PD_DESIGN_MIN_TEMP, AWAR_PD_DESIGN_MAX_TEMP);
1245}
1246
1247void create_probe_design_variables(AW_root *root, AW_default props, AW_default db) {
1248    PD.win = NULp; // design result window not created
1249
1250    root->awar_string(AWAR_SPECIES_NAME,         "", props);
1251    root->awar_string(AWAR_PD_SELECTED_MATCH,    "", props)->add_callback(selected_match_changed_cb);
1252    root->awar_float (AWAR_PD_DESIGN_EXP_DTEDGE, .5, props);
1253    root->awar_float (AWAR_PD_DESIGN_EXP_DT,     .5, props);
1254
1255    double default_bonds[16] = {
1256        0.0, 0.0, 0.5, 1.1,
1257        0.0, 0.0, 1.5, 0.0,
1258        0.5, 1.5, 0.4, 0.9,
1259        1.1, 0.0, 0.9, 0.0
1260    };
1261
1262    for (int i=0; i<16; i++) {
1263        root->awar_float(bond_awar_name(i), default_bonds[i], props)->set_minmax(0, 3.0);
1264    }
1265    root->awar_float(AWAR_PD_COMMON_EXP_SPLIT,  .5, props);
1266    root->awar_float(AWAR_PD_DESIGN_EXP_DTEDGE, .5, props);
1267    root->awar_float(AWAR_PD_DESIGN_EXP_DT,     .5, props);
1268
1269    root->awar_int  (AWAR_PD_DESIGN_CLIPRESULT, 50,   props)->set_minmax(0, 1000);
1270    root->awar_int  (AWAR_PD_DESIGN_MISHIT,     0,    props)->set_minmax(0, 100000);
1271    root->awar_int  (AWAR_PD_DESIGN_MAXBOND,    4,    props)->set_minmax(0, 20);
1272    root->awar_float(AWAR_PD_DESIGN_MINTARGETS, 50.0, props)->set_minmax(0, 100);
1273
1274    AW_awar *awar_min_len = root->awar_int(AWAR_PD_DESIGN_MIN_LENGTH, 18, props);
1275    awar_min_len->set_minmax(DOMAIN_MIN_LENGTH, 100)->add_callback(makeRootCallback(probelength_changed_cb, false));
1276    root->awar_int(AWAR_PD_DESIGN_MAX_LENGTH, awar_min_len->read_int(), props)->set_minmax(DOMAIN_MIN_LENGTH, 100)->add_callback(makeRootCallback(probelength_changed_cb, true));
1277
1278    root->awar_float(AWAR_PD_DESIGN_MIN_TEMP,     30.0,   props)->set_minmax(0, 1000)->add_callback(makeRootCallback(temp_minmax_changed_cb, false));
1279    root->awar_float(AWAR_PD_DESIGN_MAX_TEMP,     100.0,  props)->set_minmax(0, 1000)->add_callback(makeRootCallback(temp_minmax_changed_cb, true));
1280
1281    root->awar_float(AWAR_PD_DESIGN_MIN_GC, 50.0,  props)->add_callback(makeRootCallback(gc_minmax_changed_cb, false));
1282    root->awar_float(AWAR_PD_DESIGN_MAX_GC, 100.0, props)->add_callback(makeRootCallback(gc_minmax_changed_cb, true));
1283
1284    gc_minmax_changed_cb(root, false);
1285    gc_minmax_changed_cb(root, true);
1286
1287    root->awar_string(AWAR_PD_DESIGN_MIN_ECOLIPOS, "", props);
1288    root->awar_string(AWAR_PD_DESIGN_MAX_ECOLIPOS, "", props);
1289
1290    root->awar_int(AWAR_PT_SERVER,      0, props);
1291    root->awar_int(AWAR_PD_DESIGN_GENE, 0, props);
1292
1293    root->awar_int(AWAR_PD_MATCH_MARKHITS,     1, props);
1294    root->awar_int(AWAR_PD_MATCH_SORTBY,       0, props);
1295    root->awar_int(AWAR_PD_MATCH_WRITE2TMP,    0, props);
1296    root->awar_int(AWAR_PD_MATCH_ALSO_REVCOMP, 0, props);
1297
1298    root->awar_int   (AWAR_MAX_MISMATCHES, 0,    db)->set_minmax(0, 200);
1299    root->awar_string(AWAR_TARGET_STRING,  NULp, db);
1300
1301    root->awar_string(AWAR_PD_MATCH_NHITS,      "[none]", props);
1302    root->awar_int   (AWAR_PD_MATCH_NMATCHES,   1,        props);
1303    root->awar_int   (AWAR_PD_MATCH_LIM_NMATCH, 4,        props);
1304    root->awar_int   (AWAR_PD_MATCH_MAX_RES,    1000000,  props);
1305
1306    root->awar_int(AWAR_PD_MATCH_AUTOMATCH, 0, props)->add_callback(auto_match_changed);
1307
1308    root->awar_string(AWAR_PD_MATCH_RESOLVE, "", props)->add_callback(resolved_probe_chosen);
1309    root->awar_string(AWAR_ITARGET_STRING,   "", db);
1310
1311    root->awar_int(AWAR_PROBE_ADMIN_PT_SERVER,    0, db);
1312    root->awar_int(AWAR_PROBE_CREATE_GENE_SERVER, 0, db);
1313
1314    root->awar_string(AWAR_SPV_SAI_2_PROBE,    "",     db); // name of SAI selected in list
1315    root->awar_string(AWAR_SPV_DB_FIELD_NAME,  "name", db); // name of displayed species field
1316    root->awar_int   (AWAR_SPV_DB_FIELD_WIDTH, 10,     db); // width of displayed species field
1317    root->awar_string(AWAR_SPV_ACI_COMMAND,    "",     db); // User defined or pre-defined ACI command to display
1318
1319    root->awar_int(AWAR_PC_MATCH_NHITS,     0, db);
1320    root->awar_int(AWAR_PC_AUTO_MATCH,      0, props);
1321
1322    root->awar_string(AWAR_PC_TARGET_STRING,  "", db)->set_srt(REPLACE_TARGET_CONTROL_CHARS);
1323    root->awar_string(AWAR_PC_TARGET_NAME,    "", db)->set_srt(REPLACE_TARGET_CONTROL_CHARS);
1324    root->awar_string(AWAR_PC_SELECTED_PROBE, "", db);
1325
1326    root->awar_float(AWAR_PC_MATCH_WIDTH, 1.0, db)->set_minmax(0.01, 100.0);
1327    root->awar_float(AWAR_PC_MATCH_BIAS,  0.0, db)->set_minmax(-1.0, 1.0);
1328
1329    root->awar_float(AWAR_PC_MISMATCH_THRESHOLD, 0.0, db)->set_minmax(0, 100); // Note: limits will be modified by probe_match_with_specificity_event
1330
1331    float default_weights[16] = {0.0};
1332    float default_width = 1.0;
1333    float default_bias  = 0.0;
1334
1335    ArbProbeCollection& g_probe_collection = get_probe_collection();
1336    g_probe_collection.getParameters(default_weights, default_width, default_bias);
1337    g_probe_collection.clear();
1338
1339    char buffer[256] = {0};
1340
1341    for (int i = 0; i < 16 ; i++) {
1342        sprintf(buffer, AWAR_PC_MATCH_WEIGHTS"%i", i);
1343        AW_awar *awar = root->awar_float(buffer, 0.0, db);
1344
1345        awar->set_minmax(0, 10);
1346        default_weights[i] = awar->read_float();
1347    }
1348
1349    g_probe_collection.setParameters(default_weights, default_width, default_bias);
1350
1351    // read probes from DB and add them to collection
1352    {
1353        AW_awar *awar_current = root->awar_string(AWAR_PC_CURRENT_COLLECTION, "", db);
1354        char    *current      = awar_current->read_string();
1355
1356        if (current && current[0]) {
1357            // Note: target sequences/names do not contain '#'/':' (see REPLACE_TARGET_CONTROL_CHARS)
1358            ConstStrArray probes;
1359            GBT_splitNdestroy_string(probes, current, "#", true);
1360
1361            for (size_t i = 0; i<probes.size(); ++i) {
1362                const char *probe = probes[i];
1363                const char *colon = strchr(probe, ':');
1364
1365                if (colon) {
1366                    char       *name = ARB_strpartdup(probe, colon-1);
1367                    const char *seq  = colon+1;
1368                    g_probe_collection.add(name, seq);
1369                    free(name);
1370                }
1371                else {
1372                    aw_message(GBS_global_string("Saved probe ignored: has wrong format ('%s', expected 'name:seq')", probe));
1373                }
1374            }
1375        }
1376        free(current);
1377    }
1378    root->awar_string(AWAR_SPV_SELECTED_PROBE, "",     db); // For highlighting the selected PROBE
1379}
1380
1381static AW_window *create_probe_expert_window(AW_root *root, bool for_design) {
1382    AW_window_simple *aws = new AW_window_simple;
1383    if (for_design) {
1384        aws->init(root, "PD_exp", "Probe Design (Expert)");
1385        aws->load_xfig("pd_spec.fig");
1386    }
1387    else {
1388        aws->init(root, "PM_exp", "Probe Match (Expert)");
1389        aws->load_xfig("pm_spec.fig");
1390    }
1391
1392    aws->label_length(30);
1393    aws->button_length(10);
1394
1395    aws->at("close");
1396    aws->callback(AW_POPDOWN);
1397    aws->create_button("CLOSE", "CLOSE", "C");
1398
1399    aws->callback(makeHelpCallback(for_design ? "pd_spec_param.hlp" : "pm_spec_param.hlp")); // uses_hlp_res("pm_spec_param.hlp", "pd_spec_param.hlp"); see ../SOURCE_TOOLS/check_resources.pl@uses_hlp_res
1400    aws->at("help");
1401    aws->create_button("HELP", "HELP", "C");
1402
1403    for (int i=0; i<16; i++) { // bond matrix
1404        char bondAt[20];
1405        sprintf(bondAt, "%i", i);
1406        aws->at(bondAt);
1407
1408        aws->create_input_field(bond_awar_name(i), 4);
1409    }
1410
1411    aws->sens_mask(AWM_EXP);
1412    aws->at("split"); aws->create_input_field(AWAR_PD_COMMON_EXP_SPLIT,  6);
1413
1414    if (for_design) {
1415        aws->at("dt_edge"); aws->create_input_field(AWAR_PD_DESIGN_EXP_DTEDGE, 6);
1416        aws->at("dt");      aws->create_input_field(AWAR_PD_DESIGN_EXP_DT,     6);
1417        aws->sens_mask(AWM_ALL);
1418    }
1419    else {
1420        aws->at("nmatches");   aws->create_input_field(AWAR_PD_MATCH_NMATCHES,   3);
1421        aws->at("lim_nmatch"); aws->create_input_field(AWAR_PD_MATCH_LIM_NMATCH, 3);
1422        aws->sens_mask(AWM_ALL);
1423        aws->at("max_res");    aws->create_input_field(AWAR_PD_MATCH_MAX_RES,    14);
1424    }
1425
1426    return aws;
1427}
1428
1429static AWT_config_mapping_def probe_design_mapping_def[] = {
1430    // main window:
1431    { AWAR_PD_DESIGN_CLIPRESULT,   "clip" },
1432    { AWAR_PD_DESIGN_MISHIT,       "mishit" },
1433    { AWAR_PD_DESIGN_MAXBOND,      "maxbond" },
1434    { AWAR_PD_DESIGN_MINTARGETS,   "mintarget" },
1435    { AWAR_PD_DESIGN_MIN_LENGTH,   "probelen" },
1436    { AWAR_PD_DESIGN_MAX_LENGTH,   "probemaxlen" },
1437    { AWAR_PD_DESIGN_MIN_TEMP,     "mintemp" },
1438    { AWAR_PD_DESIGN_MAX_TEMP,     "maxtemp" },
1439    { AWAR_PD_DESIGN_MIN_GC,       "mingc" },
1440    { AWAR_PD_DESIGN_MAX_GC,       "maxgc" },
1441    { AWAR_PD_DESIGN_MIN_ECOLIPOS, "minecoli" },
1442    { AWAR_PD_DESIGN_MAX_ECOLIPOS, "maxecoli" },
1443    { AWAR_PD_DESIGN_GENE,         "gene" },
1444
1445    // expert window:
1446    { AWAR_PD_DESIGN_EXP_DTEDGE,   "dtedge" },
1447    { AWAR_PD_DESIGN_EXP_DT,       "dt" },
1448
1449    { NULp, NULp }
1450};
1451
1452static void setup_probe_config(AWT_config_definition& cdef, const AWT_config_mapping_def *mapping) {
1453    cdef.add(mapping);
1454
1455    // entries common for both expert windows (design + match)
1456    cdef.add(AWAR_PD_COMMON_EXP_SPLIT, "split");
1457    for (int i = 0; i<16; ++i) {
1458        cdef.add(bond_awar_name(i), "bond", i);
1459    }
1460}
1461
1462AW_window *create_probe_design_window(AW_root *root, GBDATA *gb_main) {
1463    bool is_genom_db;
1464    {
1465        GB_transaction ta(gb_main);
1466        is_genom_db = GEN_is_genome_db(gb_main, -1);
1467    }
1468
1469    AW_window_simple *aws = new AW_window_simple;
1470    aws->init(root, "PROBE_DESIGN", "PROBE DESIGN");
1471
1472    aws->load_xfig("pd_main.fig");
1473
1474    aws->at("close");
1475    aws->callback(AW_POPDOWN);
1476    aws->create_button("CLOSE", "CLOSE", "C");
1477
1478    aws->at("help");
1479    aws->callback(makeHelpCallback("probedesign.hlp"));
1480    aws->create_button("HELP", "HELP", "H");
1481
1482    aws->callback(makeWindowCallback(probe_design_event, gb_main));
1483    aws->at("design");
1484    aws->highlight();
1485    aws->create_button("GO", "GO", "G");
1486
1487    aws->callback(makeWindowCallback(popup_probe_design_result_window, gb_main));
1488    aws->at("result");
1489    aws->create_button("RESULT", "RESULT", "S");
1490
1491    aws->callback(makeCreateWindowCallback(create_probe_expert_window, true));
1492    aws->at("expert");
1493    aws->create_button("EXPERT", "EXPERT", "S");
1494
1495    aws->at("pt_server");
1496    aws->label("PT-Server:");
1497    awt_create_PTSERVER_selection_button(aws, AWAR_PT_SERVER);
1498
1499    aws->at("lenout");   aws->create_input_field(AWAR_PD_DESIGN_CLIPRESULT, 6);
1500    aws->at("mishit");   aws->create_input_field(AWAR_PD_DESIGN_MISHIT,     6);
1501    aws->sens_mask(AWM_EXP);
1502    aws->at("maxbonds"); aws->create_input_field(AWAR_PD_DESIGN_MAXBOND,    6);
1503    aws->sens_mask(AWM_ALL);
1504    aws->at("minhits");  aws->create_input_field(AWAR_PD_DESIGN_MINTARGETS, 6);
1505
1506    aws->at("minlen"); aws->create_input_field(AWAR_PD_DESIGN_MIN_LENGTH,   5);
1507    aws->at("maxlen"); aws->create_input_field(AWAR_PD_DESIGN_MAX_LENGTH,   5);
1508    aws->at("mint");   aws->create_input_field(AWAR_PD_DESIGN_MIN_TEMP,     5);
1509    aws->at("maxt");   aws->create_input_field(AWAR_PD_DESIGN_MAX_TEMP,     5);
1510    aws->at("mingc");  aws->create_input_field(AWAR_PD_DESIGN_MIN_GC,       5);
1511    aws->at("maxgc");  aws->create_input_field(AWAR_PD_DESIGN_MAX_GC,       5);
1512    aws->at("minpos"); aws->create_input_field(AWAR_PD_DESIGN_MIN_ECOLIPOS, 5);
1513    aws->at("maxpos"); aws->create_input_field(AWAR_PD_DESIGN_MAX_ECOLIPOS, 5);
1514
1515    if (is_genom_db) {
1516        aws->at("gene");
1517        aws->label("Gene probes?");
1518        aws->create_toggle(AWAR_PD_DESIGN_GENE);
1519    }
1520
1521    aws->at("save");
1522    AWT_insert_config_manager(aws, AW_ROOT_DEFAULT, "probe_design", makeConfigSetupCallback(setup_probe_config, probe_design_mapping_def));
1523
1524    return aws;
1525}
1526
1527// -------------------------------------------------------------------
1528
1529inline void my_strupr(char *s) {
1530    pd_assert(s);
1531    for (int i=0; s[i]; i++) {
1532        s[i] = toupper(s[i]);
1533    }
1534}
1535
1536static void resolve_IUPAC_target_string(AW_root *root, AW_selection_list *selection_id, GBDATA *gb_main) {
1537    selection_id->clear();
1538
1539    GB_alignment_type ali_type = GBT_get_alignment_type(gb_main, GBT_get_default_alignment(gb_main));
1540
1541    GB_ERROR err = NULp;
1542    if (ali_type != GB_AT_RNA && ali_type!=GB_AT_DNA) {
1543        err = "Wrong alignment type";
1544        GB_clear_error();
1545    }
1546    else {
1547        int   index   = ali_type==GB_AT_RNA ? 1 : 0;
1548        char *istring = root->awar(AWAR_ITARGET_STRING)->read_string();
1549
1550        if (istring && istring[0]) { // contains sth?
1551            my_strupr(istring);
1552
1553            int bases_to_resolve = 0;
1554            char *istr = istring;
1555            int istring_length = strlen(istring);
1556
1557            for (;;) {
1558                char i = *istr++;
1559                if (!i) break;
1560                if (i=='?') continue; // ignore '?'
1561
1562                int idx = i-'A';
1563                if (idx<0 || idx>=26 || !iupac::nuc_group[idx][index].members) {
1564                    err = GBS_global_string("Illegal character '%c' in IUPAC-String", i);
1565                    break;
1566                }
1567
1568                if (iupac::nuc_group[idx][index].count>1) {
1569                    bases_to_resolve++;
1570                }
1571            }
1572
1573            if (!err) {
1574                int *offsets_to_resolve = new int[bases_to_resolve];
1575                int resolutions = 1;
1576                {
1577                    istr = istring;
1578                    int offset = 0;
1579                    int offset_count = 0;
1580                    for (;;) {
1581                        char i = *istr++;
1582                        if (!i) break;
1583
1584                        if (i!='?') {
1585                            int idx = iupac::to_index(i);
1586                            pd_assert(iupac::nuc_group[idx][index].members);
1587
1588                            if (iupac::nuc_group[idx][index].count>1) {
1589                                offsets_to_resolve[offset_count++] = offset; // store string offsets of non-unique base-codes
1590                                resolutions *= iupac::nuc_group[idx][index].count; // calc # of resolutions
1591                            }
1592                        }
1593                        offset++;
1594                    }
1595                }
1596
1597                {
1598                    int *resolution_idx = new int[bases_to_resolve];
1599                    int *resolution_max_idx = new int[bases_to_resolve];
1600                    {
1601                        int i;
1602                        for (i=0; i<bases_to_resolve; i++) {
1603                            resolution_idx[i] = 0;
1604                            int idx = iupac::to_index(istring[offsets_to_resolve[i]]);
1605                            resolution_max_idx[i] = iupac::nuc_group[idx][index].count-1;
1606                        }
1607                    }
1608
1609                    char *buffer = new char[istring_length+1];
1610                    int not_last = resolutions-1;
1611
1612                    for (;;) {
1613                        // create string according to resolution_idx[]:
1614                        int i;
1615
1616                        memcpy(buffer, istring, istring_length+1);
1617                        for (i=0; i<bases_to_resolve; i++) {
1618                            int off = offsets_to_resolve[i];
1619                            int idx = iupac::to_index(istring[off]);
1620
1621                            pd_assert(iupac::nuc_group[idx][index].members);
1622                            buffer[off] = iupac::nuc_group[idx][index].members[resolution_idx[i]];
1623                        }
1624
1625                        selection_id->insert(buffer, buffer);
1626                        not_last--;
1627
1628                        // permute indices:
1629                        int nidx = bases_to_resolve-1;
1630                        int done = 0;
1631                        while (!done && nidx>=0) {
1632                            if (resolution_idx[nidx]<resolution_max_idx[nidx]) {
1633                                resolution_idx[nidx]++;
1634                                done = 1;
1635                                break;
1636                            }
1637                            nidx--;
1638                        }
1639                        if (!done) break; // we did all permutations!
1640
1641                        nidx++; // do not touch latest incremented index
1642                        while (nidx<bases_to_resolve) resolution_idx[nidx++] = 0; // zero all other indices
1643                    }
1644
1645                    delete [] buffer;
1646                    delete [] resolution_max_idx;
1647                    delete [] resolution_idx;
1648
1649                    pd_assert(!err);
1650                    selection_id->insert_default("", "");
1651                }
1652
1653                delete [] offsets_to_resolve;
1654            }
1655        }
1656        else { // empty input
1657            selection_id->insert_default("", "");
1658        }
1659    }
1660    if (err) selection_id->insert_default(err, "");
1661    selection_id->update();
1662}
1663
1664enum ModMode { TS_MOD_CLEAR, TS_MOD_REV_COMPL, TS_MOD_COMPL };
1665
1666static void modify_target_string(AW_window *aww, GBDATA *gb_main, ModMode mod_mode) {
1667    AW_root  *root          = aww->get_root();
1668    char     *target_string = root->awar(AWAR_TARGET_STRING)->read_string();
1669    GB_ERROR  error         = NULp;
1670
1671    if (mod_mode == TS_MOD_CLEAR) target_string[0] = 0;
1672    else {
1673        GB_transaction    ta(gb_main);
1674        GB_alignment_type ali_type = GBT_get_alignment_type(gb_main, GBT_get_default_alignment(gb_main));
1675        if (ali_type == GB_AT_UNKNOWN) {
1676            error = GB_await_error();
1677        }
1678        else if (mod_mode == TS_MOD_REV_COMPL) {
1679            char T_or_U;
1680            error = GBT_determine_T_or_U(ali_type, &T_or_U, "reverse-complement");
1681            if (!error) GBT_reverseComplementNucSequence(target_string, strlen(target_string), T_or_U);
1682        }
1683        else if (mod_mode == TS_MOD_COMPL) {
1684            char T_or_U;
1685            error = GBT_determine_T_or_U(ali_type, &T_or_U, "complement");
1686            if (!error) freeset(target_string, GBT_complementNucSequence(target_string, strlen(target_string), T_or_U));
1687        }
1688    }
1689
1690    if (error) aw_message(error);
1691    else root->awar(AWAR_TARGET_STRING)->write_string(target_string);
1692    free(target_string);
1693}
1694
1695static void clear_itarget(AW_window *aww) {
1696    aww->get_root()->awar(AWAR_ITARGET_STRING)->write_string("");
1697}
1698
1699static AW_window *create_IUPAC_resolve_window(AW_root *root, GBDATA *gb_main) {
1700    AW_window_simple *aws = new AW_window_simple;
1701
1702    aws->init(root, "PROBE_MATCH_RESOLVE_IUPAC", "Resolve IUPAC for Probe Match");
1703    aws->load_xfig("pd_match_iupac.fig");
1704
1705    aws->button_length(11);
1706
1707    aws->at("close");
1708    aws->callback(AW_POPDOWN);
1709    aws->create_button("CLOSE", "CLOSE", "C");
1710
1711    aws->at("help");
1712    aws->callback(makeHelpCallback("pd_match_iupac.hlp"));
1713    aws->create_button("HELP", "HELP", "H");
1714
1715    aws->at("istring");
1716    aws->create_input_field(AWAR_ITARGET_STRING, 32);
1717
1718    aws->at("iresult");
1719    AW_selection_list *iselection_id;
1720    iselection_id = aws->create_selection_list(AWAR_PD_MATCH_RESOLVE, 32, 15, true);
1721    iselection_id->insert_default("---empty---", "");
1722
1723    // add callback for automatic decomposition of AWAR_ITARGET_STRING:
1724    RootCallback  upd_cb       = makeRootCallback(resolve_IUPAC_target_string, iselection_id, gb_main);
1725    AW_awar      *awar_itarget = root->awar(AWAR_ITARGET_STRING);
1726    awar_itarget->add_callback(upd_cb);
1727    root->awar(AWAR_DEFAULT_ALIGNMENT)->add_callback(upd_cb);
1728
1729    aws->at("match_all");
1730    aws->callback(makeWindowCallback(probe_match_all_event, iselection_id, gb_main));
1731    aws->create_button("MATCH_ALL", "Match all", "M");
1732
1733    aws->at("clear");
1734    aws->callback(clear_itarget);
1735    aws->create_button("CLEAR", "Clear", "r");
1736
1737    aws->at("iupac_info");
1738    aws->callback(AWT_create_IUPAC_info_window);
1739    aws->create_button("IUPAC_INFO", "IUPAC info", "I");
1740
1741    awar_itarget->touch(); // force initial refresh for saved value
1742
1743    return aws;
1744}
1745
1746static void popupSaiProbeMatchWindow(AW_window *aw, GBDATA *gb_main) {
1747    static AW_window *aw_saiProbeMatch = NULp;
1748
1749    if (!aw_saiProbeMatch) aw_saiProbeMatch = createSaiProbeMatchWindow(aw->get_root(), gb_main);
1750    if (g_spd) transferProbeData(g_spd); // transferring probe data to saiProbeMatch function
1751
1752    aw_saiProbeMatch->activate();
1753}
1754
1755static AWT_config_mapping_def probe_match_mapping_def[] = {
1756    // main window:
1757    { AWAR_TARGET_STRING,         "target" },
1758    { AWAR_PD_MATCH_ALSO_REVCOMP, "complement" },
1759    { AWAR_PD_MATCH_MARKHITS,     "markhits" },
1760    { AWAR_PD_MATCH_WRITE2TMP,    "writetmp" },
1761    { AWAR_PD_MATCH_AUTOMATCH,    "automatch" },
1762
1763    // expert window:
1764    { AWAR_PD_MATCH_NMATCHES,   "nmatches" },
1765    { AWAR_PD_MATCH_LIM_NMATCH, "limitnmatch" },
1766    { AWAR_PD_MATCH_MAX_RES,    "maxresults" },
1767
1768    { NULp, NULp }
1769};
1770
1771
1772AW_window *create_probe_match_window(AW_root *root, GBDATA *gb_main) {
1773    static AW_window_simple *aws = NULp; // the one and only probe match window
1774    if (!aws) {
1775        aws = new AW_window_simple;
1776
1777        aws->init(root, "PROBE_MATCH", "PROBE MATCH");
1778        aws->load_xfig("pd_match.fig");
1779
1780        aws->auto_space(5, 5);
1781
1782        aws->at("close");
1783        aws->callback(AW_POPDOWN);
1784        aws->create_button("CLOSE", "CLOSE", "C");
1785
1786        aws->callback(makeHelpCallback("probematch.hlp"));
1787        aws->at("help");
1788        aws->create_button("HELP", "HELP", "H");
1789
1790        aws->at("string");
1791        aws->create_input_field(AWAR_TARGET_STRING, 32);
1792
1793        AW_selection_list *selection_id;
1794        aws->at("result");
1795        selection_id = aws->create_selection_list(AWAR_PD_SELECTED_MATCH, 110, 15, true);
1796        selection_id->insert_default("****** No results yet *******", "");  // if list is empty -> crashed if new species was selected in ARB_EDIT4
1797
1798        static SmartPtr<TypedSelectionList> typed_selection = new TypedSelectionList("match", selection_id, "probe match", "probe_match");
1799        aws->at("print");
1800        aws->callback(makeWindowCallback(create_print_box_for_selection_lists, &*typed_selection));
1801        aws->create_button("PRINT", "PRINT", "P");
1802
1803        aws->at("matchSai");
1804        aws->help_text("saiProbe.hlp");
1805        aws->callback(makeWindowCallback(popupSaiProbeMatchWindow, gb_main));
1806        aws->create_button("MATCH_SAI", "Match SAI", "S");
1807
1808        aws->callback(makeCreateWindowCallback(create_probe_expert_window, false));
1809        aws->at("expert");
1810        aws->create_button("EXPERT", "EXPERT", "X");
1811
1812        aws->at("pt_server");
1813        awt_create_PTSERVER_selection_button(aws, AWAR_PT_SERVER);
1814
1815        aws->at("complement");
1816        aws->create_toggle(AWAR_PD_MATCH_ALSO_REVCOMP);
1817
1818        aws->at("mark");
1819        aws->create_toggle(AWAR_PD_MATCH_MARKHITS);
1820
1821        aws->at("weighted");
1822        aws->create_toggle(AWAR_PD_MATCH_SORTBY);
1823
1824        aws->at("mismatches");
1825        aws->create_input_field_with_scaler(AWAR_MAX_MISMATCHES, 5, 200, AW_SCALER_EXP_LOWER);
1826
1827        aws->at("tmp");
1828        aws->create_toggle(AWAR_PD_MATCH_WRITE2TMP);
1829
1830        aws->at("nhits");
1831        aws->create_button(NULp, AWAR_PD_MATCH_NHITS);
1832
1833        aws->button_length(9);
1834
1835        ProbeMatchEventParam *event_param = new ProbeMatchEventParam(gb_main, selection_id);
1836        aws->callback(RootAsWindowCallback::simple(probe_match_event_using_awars, event_param));
1837        aws->at("match");
1838        aws->create_button("MATCH", "MATCH", "D");
1839
1840        aws->at("auto");
1841        aws->label("Auto");
1842        aws->create_toggle(AWAR_PD_MATCH_AUTOMATCH);
1843        enable_auto_match_cb(root, event_param);
1844
1845        aws->callback(makeWindowCallback(modify_target_string, gb_main, TS_MOD_CLEAR));
1846        aws->at("clear");
1847        aws->create_button("CLEAR", "Clear", "0");
1848
1849        aws->callback(makeWindowCallback(modify_target_string, gb_main, TS_MOD_REV_COMPL));
1850        aws->at("revcompl");
1851        aws->create_button("REVCOMPL", "RevCompl", "R");
1852
1853        aws->callback(makeWindowCallback(modify_target_string, gb_main, TS_MOD_COMPL));
1854        aws->at("compl");
1855        aws->create_button("COMPL", "Compl", "C");
1856
1857        aws->callback(makeCreateWindowCallback(create_IUPAC_resolve_window, gb_main));
1858        aws->at("iupac");
1859        aws->create_button("IUPAC", "IUPAC", "I");
1860
1861        aws->at("config");
1862        AWT_insert_config_manager(aws, AW_ROOT_DEFAULT, "probe_match", makeConfigSetupCallback(setup_probe_config, probe_match_mapping_def));
1863    }
1864    return aws;
1865}
1866
1867static void pd_start_pt_server(AW_window *aww) {
1868    const char *server_tag = GBS_ptserver_tag(aww->get_root()->awar(AWAR_PROBE_ADMIN_PT_SERVER)->read_int());
1869    arb_progress progress("Connecting PT-server");
1870    GB_ERROR error = arb_look_and_start_server(AISC_MAGIC_NUMBER, server_tag);
1871    if (error) aw_message(error);
1872}
1873
1874static void pd_kill_pt_server(AW_window *aww, bool kill_all) {
1875    if (aw_ask_sure("kill_ptserver",
1876                    GBS_global_string("Are you sure to stop %s", kill_all ? "all servers" : "that server")))
1877    {
1878        long min = 0;
1879        long max = 0;
1880
1881        if (kill_all) {
1882            const char * const *pt_servers = GBS_get_arb_tcp_entries("ARB_PT_SERVER*");
1883            while (pt_servers[max]) max++;
1884        }
1885        else {
1886            min = max = aww->get_root()->awar(AWAR_PROBE_ADMIN_PT_SERVER)->read_int(); // selected server
1887        }
1888
1889        arb_progress progress("Stopping PT-servers..", max-min+1);
1890        GB_ERROR     error = NULp;
1891
1892        for (int i = min; i <= max && !error;  i++) {
1893            char *choice = GBS_ptserver_id_to_choice(i, 0);
1894            if (!choice) {
1895                error = GB_await_error();
1896            }
1897            else {
1898                progress.subtitle(GBS_global_string("Trying to stop '%s'", choice));
1899
1900                const char *server_tag = GBS_ptserver_tag(i);
1901                GB_ERROR    kill_error = arb_look_and_kill_server(AISC_MAGIC_NUMBER, server_tag);
1902
1903                if (kill_error) aw_message(GBS_global_string("Could not stop '%s' (Reason: %s)", choice, kill_error));
1904                else aw_message(GBS_global_string("Stopped '%s'", choice));
1905
1906                free(choice);
1907            }
1908            progress.inc_and_check_user_abort(error);
1909        }
1910        progress.done();
1911        aw_message_if(error);
1912    }
1913}
1914
1915static const char *ptserver_directory_info_command(const char *dirname, const char *directory) {
1916    return GBS_global_string("echo 'Contents of directory %s:'; echo; (cd \"%s\"; ls -hl); echo; "
1917                             "echo 'Available disk space in %s:'; echo; df -h \"%s\"; echo; ",
1918                             dirname, directory,
1919                             dirname, directory);
1920
1921}
1922
1923static void pd_query_pt_server(AW_window *aww) {
1924    const char *server_tag = GBS_ptserver_tag(aww->get_root()->awar(AWAR_PROBE_ADMIN_PT_SERVER)->read_int());
1925
1926    GBS_strstruct query_cmd(1024);
1927    query_cmd.cat(ptserver_directory_info_command("ARBHOME/lib/pts", "$ARBHOME/lib/pts"));
1928
1929    const char *ARB_LOCAL_PTS = ARB_getenv_ignore_empty("ARB_LOCAL_PTS");
1930    if (ARB_LOCAL_PTS) query_cmd.cat(ptserver_directory_info_command("ARB_LOCAL_PTS", "$ARB_LOCAL_PTS")); // only defined if called via 'arb'-script
1931    else               query_cmd.cat(ptserver_directory_info_command("HOME/.arb_pts", "${HOME}/.arb_pts"));
1932
1933    query_cmd.cat("echo 'Running ARB programs:'; echo; ");
1934
1935    GB_ERROR error = NULp;
1936    {
1937        const char *socketid = GBS_read_arb_tcp(server_tag);
1938        if (!socketid) {
1939            error = GB_await_error();
1940        }
1941        else {
1942            char *arb_who = createCallOnSocketHost(socketid, "$ARBHOME/bin/", "arb_who", WAIT_FOR_TERMINATION, NULp);
1943            query_cmd.cat(arb_who);
1944            free(arb_who);
1945        }
1946    }
1947
1948    if (!error) error = GB_xcmd(query_cmd.get_data(), XCMD_ASYNC_WAITKEY);
1949    aw_message_if(error);
1950}
1951
1952static void pd_export_pt_server(AW_window *aww, GBDATA *gb_main) {
1953    AW_root  *awr   = aww->get_root();
1954    GB_ERROR  error = NULp;
1955
1956    bool create_gene_server = awr->awar(AWAR_PROBE_CREATE_GENE_SERVER)->read_int();
1957    {
1958        GB_transaction ta(gb_main);
1959        if (create_gene_server && !GEN_is_genome_db(gb_main, -1)) create_gene_server = false;
1960
1961        // check alignment first
1962        if (create_gene_server) {
1963            GBDATA *gb_ali     = GBT_get_alignment(gb_main, GENOM_ALIGNMENT);
1964            if (!gb_ali) error = GB_await_error();
1965        }
1966        else { // normal pt server
1967            char              *use     = GBT_get_default_alignment(gb_main);
1968            GB_alignment_type  alitype = GBT_get_alignment_type(gb_main, use);
1969            if (alitype == GB_AT_AA) error = "The PT-server does only support RNA/DNA sequence data";
1970            free(use);
1971        }
1972    }
1973
1974    long        server_num = awr->awar(AWAR_PROBE_ADMIN_PT_SERVER)->read_int();
1975    const char *server_tag  = GBS_ptserver_tag(server_num);
1976
1977    if (!error &&
1978        aw_question("update_ptserver",
1979                    "This function will send your loaded database to the pt_server,\n"
1980                    "which will need a long time (up to several hours) to analyse the data.\n"
1981                    "Until the new server has analyzed all data, no server functions are available.\n\n"
1982                    "Note 1: You must have the write permissions to do that ($ARBHOME/lib/pts/xxx))\n"
1983                    "Note 2: The server will do the job in background,\n"
1984                    "        quitting this program won't affect the server",
1985                    "Cancel,Do it"))
1986    {
1987        arb_progress progress("Updating PT-server");
1988        progress.subtitle("Stopping PT-server");
1989        arb_look_and_kill_server(AISC_MAGIC_NUMBER, server_tag);
1990
1991        const char *ipPort = GBS_read_arb_tcp(server_tag);
1992        const char *file   = NULp;
1993        if (!ipPort) error = GB_await_error();
1994        else {
1995            file = GBS_scan_arb_tcp_param(ipPort, "-d");
1996            GBS_add_ptserver_logentry(GBS_global_string("Started build of '%s'", file));
1997
1998            char *db_name = awr->awar(AWAR_DB_PATH)->read_string();
1999            GBS_add_ptserver_logentry(GBS_global_string("Exporting DB '%s'", db_name));
2000            free(db_name);
2001        }
2002
2003        if (!error) {
2004            progress.subtitle("Exporting the database");
2005            {
2006                const char *mode = GB_supports_mapfile() ? "mbf" : "bf"; // save PT-server database with Fastload file (if supported)
2007                if (create_gene_server) {
2008                    char *temp_server_name = GBS_string_eval(file, "*.arb=*_temp.arb");
2009                    error = GB_save_as(gb_main, temp_server_name, mode);
2010
2011                    if (!error) {
2012                        // convert database (genes -> species)
2013                        progress.subtitle("Preparing DB for gene PT server");
2014                        GBS_add_ptserver_logentry("Preparing DB for gene PT server");
2015                        char *command = GBS_global_string_copy("$ARBHOME/bin/arb_gene_probe %s %s", temp_server_name, file);
2016                        error = GBK_system(command);
2017                        if (error) error = GBS_global_string("Couldn't convert database for gene pt server\n(Reason: %s)", error);
2018                        free(command);
2019                    }
2020                    free(temp_server_name);
2021                }
2022                else { // normal pt_server
2023                    error = GB_save_as(gb_main, file, mode);
2024                }
2025            }
2026        }
2027
2028        if (!error) { // set pt-server database file to same permissions as pts directory
2029            char *dir = const_cast<char*>(strrchr(file, '/'));
2030            if (dir) {
2031                *dir = 0;
2032                long modi = GB_mode_of_file(file);
2033                *dir = '/';
2034                modi &= 0666;
2035                error = GB_set_mode_of_file(file, modi);
2036            }
2037        }
2038
2039        if (!error) {
2040            progress.subtitle("Start PT-server (builds in background)");
2041            error = arb_start_server(server_tag, 1);
2042        }
2043    }
2044    if (error) aw_message(error);
2045}
2046
2047static void pd_edit_ptserver_log(AW_window*) {
2048    AW_edit(GBS_ptserver_logname());
2049}
2050static void pd_view_server_console(AW_window*) {
2051    AWT_system_in_console_cb("arb_show_log.pl server", XCMD_ASYNC_WAIT_ON_ERROR);
2052}
2053
2054AW_window *create_probe_admin_window(AW_root *root, GBDATA *gb_main) {
2055    bool is_genom_db;
2056    {
2057        GB_transaction ta(gb_main);
2058        is_genom_db = GEN_is_genome_db(gb_main, -1);
2059    }
2060
2061    AW_window_simple *aws = new AW_window_simple;
2062    aws->init(root, "PT_SERVER_ADMIN", "PT_SERVER ADMIN");
2063
2064    aws->load_xfig("pd_admin.fig");
2065
2066
2067    aws->callback(makeHelpCallback("probeadmin.hlp"));
2068    aws->at("help");
2069    aws->create_button("HELP", "HELP", "H");
2070
2071    aws->at("close");
2072    aws->callback(AW_POPDOWN);
2073    aws->create_button("CLOSE", "CLOSE", "C");
2074
2075    aws->button_length(18);
2076
2077    aws->at("pt_server");
2078    awt_create_PTSERVER_selection_list(aws, AWAR_PROBE_ADMIN_PT_SERVER);
2079
2080    aws->at("start");
2081    aws->callback(pd_start_pt_server);
2082    aws->create_button("START_SERVER", "Start server");
2083
2084    aws->at("kill");
2085    aws->callback(makeWindowCallback(pd_kill_pt_server, false));
2086    aws->create_button("KILL_SERVER", "Stop server");
2087
2088    aws->at("query");
2089    aws->callback(pd_query_pt_server);
2090    aws->create_button("CHECK_SERVER", "Check server");
2091
2092    aws->at("kill_all");
2093    aws->callback(makeWindowCallback(pd_kill_pt_server, true));
2094    aws->create_button("KILL_ALL_SERVERS", "Stop all servers");
2095
2096    aws->at("edit");
2097    aws->callback(awt_edit_arbtcpdat_cb);
2098    aws->create_button("CREATE_TEMPLATE", "Configure");
2099
2100    aws->at("log");
2101    aws->callback(pd_edit_ptserver_log);
2102    aws->create_button("EDIT_LOG", "Build history");
2103
2104    aws->at("slog");
2105    aws->callback(pd_view_server_console);
2106    aws->create_button("VIEW_SERVER_LOG", "View server log");
2107
2108    aws->at("export");
2109    aws->callback(makeWindowCallback(pd_export_pt_server, gb_main));
2110    aws->create_button("UPDATE_SERVER", "Build server");
2111
2112    if (is_genom_db) {
2113        aws->at("gene_server");
2114        aws->label("Gene server?");
2115        aws->create_toggle(AWAR_PROBE_CREATE_GENE_SERVER);
2116    }
2117
2118    return aws;
2119}
2120
2121// ----------------------------------------------------------------------------
2122
2123static AW_window *create_probe_match_specificity_control_window(AW_root *root) {
2124    static AW_window_simple *aws = NULp;
2125
2126    if (!aws) {
2127        aws = new AW_window_simple;
2128
2129        aws->init(root, "MATCH_DISPLAYCONTROL", "MATCH DISPLAY CONTROL");
2130
2131        aws->auto_space(10, 10);
2132        aws->label_length(35);
2133
2134        const int FIELDSIZE  = 5;
2135        const int SCALERSIZE = 500;
2136
2137        aws->label("Mismatch threshold");
2138        aws->create_input_field_with_scaler(AWAR_PC_MISMATCH_THRESHOLD, FIELDSIZE, SCALERSIZE);
2139
2140        aws->at_newline();
2141
2142        aws->label("Clade marked threshold");
2143        aws->create_input_field_with_scaler(AWAR_DTREE_GROUP_MARKED_THRESHOLD, FIELDSIZE, SCALERSIZE);
2144
2145        aws->at_newline();
2146
2147        aws->label("Clade partially marked threshold");
2148        aws->create_input_field_with_scaler(AWAR_DTREE_GROUP_PARTIALLY_MARKED_THRESHOLD, FIELDSIZE, SCALERSIZE);
2149
2150        aws->at_newline();
2151
2152        aws->callback(TREE_create_marker_settings_window);
2153        aws->create_autosize_button("MARKER_SETTINGS", "Marker display settings", "d");
2154
2155        aws->at_newline();
2156    }
2157
2158    return aws;
2159}
2160
2161// ----------------------------------------------------------------------------
2162
2163struct ArbPM_Context {
2164    AW_selection_list *probes_id;
2165    TREE_canvas       *ntw;
2166
2167    ArbPM_Context() :
2168        probes_id(NULp),
2169        ntw(NULp)
2170    {}
2171};
2172
2173static ArbPM_Context PM_Context;
2174
2175static void       probe_match_update_probe_list(ArbPM_Context *pContext);
2176static AW_window *create_probe_collection_window(AW_root *root, ArbPM_Context *pContext);
2177
2178struct ArbWriteFile_Context {
2179    FILE         *pFile;
2180    arb_progress *pProgress;
2181    int           nLastPercent;
2182
2183    ArbWriteFile_Context() :
2184        pFile(NULp),
2185        pProgress(NULp),
2186        nLastPercent(0)
2187    {}
2188};
2189
2190// ----------------------------------------------------------------------------
2191
2192static bool probe_match_with_specificity_enum_callback(void *pVoidContext, const char *pResult, bool bIsComment, int nItem, int nItems) {
2193    ArbWriteFile_Context *pContext = (ArbWriteFile_Context*)pVoidContext;
2194    int                   nPercent = (int)(100.0 * nItem / nItems);
2195
2196    if (!bIsComment) {
2197        if (pContext->nLastPercent != nPercent) {
2198            pContext->nLastPercent = nPercent;
2199            pContext->pProgress->inc();
2200        }
2201    }
2202
2203    if (pContext->pFile) {
2204        // Update status - put after matching cause matching writes its own status messages
2205        fprintf(pContext->pFile, "%s\n", pResult);
2206    }
2207
2208    return pContext->pProgress->aborted();
2209}
2210
2211// ----------------------------------------------------------------------------
2212
2213static void probe_match_with_specificity_edit_event() {
2214    AW_edit(get_results_manager().resultsFileName());
2215}
2216
2217// ----------------------------------------------------------------------------
2218
2219class GetMatchesContext { // @@@ merge with ProbeCollDisplay?
2220    float mismatchThreshold;
2221    int   nProbes;
2222
2223    typedef ArbMatchResultPtrByStringMultiMap          MatchMap;
2224    typedef ArbMatchResultPtrByStringMultiMapConstIter MatchMapIter;
2225
2226    const MatchMap& results;
2227public:
2228    GetMatchesContext(float misThres, int numProbes)
2229        : mismatchThreshold(misThres),
2230          nProbes(numProbes),
2231          results(get_results_manager().resultsMap())
2232    {}
2233
2234    void detect(std::string speciesName, NodeMarkers& matches) const {
2235        std::pair<MatchMapIter,MatchMapIter> iter = results.equal_range(speciesName);
2236
2237        for (; iter.first != iter.second ; ++iter.first) {
2238            const ArbMatchResult *pMatchResult = iter.first->second;
2239            if (pMatchResult->weight() <= mismatchThreshold) {
2240                int nProbe = pMatchResult->index();
2241                matches.incMarker(nProbe);
2242            }
2243        }
2244        matches.incNodeSize();
2245    }
2246};
2247
2248static SmartPtr<GetMatchesContext> getMatchesContext;
2249
2250class ProbeCollDisplay : public MarkerDisplay {
2251    ArbProbe *find_probe(int markerIdx) const {
2252        const ArbProbePtrList&   rProbeList = get_probe_collection().probeList();
2253        ArbProbePtrListConstIter wanted     = rProbeList.begin();
2254
2255        if (markerIdx>=0 && markerIdx<int(rProbeList.size())) advance(wanted, markerIdx);
2256        else wanted = rProbeList.end();
2257
2258        return wanted == rProbeList.end() ? NULp : *wanted;
2259    }
2260
2261public:
2262    ProbeCollDisplay(int numProbes)
2263        : MarkerDisplay(numProbes)
2264    {}
2265
2266    // MarkerDisplay interface
2267    const char *get_marker_name(int markerIdx) const OVERRIDE {
2268        ArbProbe *probe = find_probe(markerIdx);
2269        return probe ? probe->name().c_str() : GBS_global_string("<invalid probeindex %i>", markerIdx);
2270    }
2271    void retrieve_marker_state(const char *speciesName, NodeMarkers& matches) OVERRIDE {
2272        getMatchesContext->detect(speciesName, matches);
2273    }
2274
2275    void handle_click(int markerIdx, AW_MouseButton, AWT_graphic_exports&) OVERRIDE {
2276        // select probe in selection list
2277        ArbProbe *probe = find_probe(markerIdx);
2278        if (probe) AW_root::SINGLETON->awar(AWAR_PC_SELECTED_PROBE)->write_string(probe->sequence().c_str());
2279#if defined(DEBUG)
2280        else fprintf(stderr, "ProbeCollDisplay::handle_click: no probe found for markerIdx=%i\n", markerIdx);
2281#endif
2282    }
2283};
2284
2285inline bool displays_probeColl_markers(MarkerDisplay *md) { return dynamic_cast<ProbeCollDisplay*>(md); }
2286
2287static void refresh_matchedProbesDisplay_cb(AW_root *root, TREE_canvas *ntw) {
2288    // setup parameters for display of probe collection matches and trigger tree refresh
2289    LocallyModify<bool> flag(allow_probe_match_event, false);
2290
2291    AWT_graphic_tree *agt     = DOWNCAST(AWT_graphic_tree*, ntw->gfx);
2292    bool              display = get_results_manager().hasResults();
2293
2294    MarkerDisplay *markerDisplay = agt->get_marker_display();
2295    bool           redraw        = false;
2296    if (display) {
2297        size_t probesCount = get_probe_collection().probeList().size();
2298        getMatchesContext  = new GetMatchesContext(root->awar(AWAR_PC_MISMATCH_THRESHOLD)->read_float(), probesCount);
2299
2300        if (displays_probeColl_markers(markerDisplay) && probesCount == size_t(markerDisplay->size())) {
2301            markerDisplay->flush_cache();
2302        }
2303        else {
2304            agt->set_marker_display(new ProbeCollDisplay(get_probe_collection().probeList().size()));
2305        }
2306        redraw = true;
2307    }
2308    else {
2309        if (displays_probeColl_markers(markerDisplay)) {
2310            agt->hide_marker_display();
2311            redraw = true;
2312        }
2313    }
2314
2315    if (redraw) root->awar(AWAR_TREE_REFRESH)->touch();
2316}
2317
2318// ----------------------------------------------------------------------------
2319
2320static void probe_match_with_specificity_event(AW_root *root, TREE_canvas *ntw) {
2321    if (allow_probe_match_event) {
2322        GB_ERROR error = NULp;
2323
2324        ProbeMatchSettings matchSettings(root);
2325        matchSettings.markHits  = 0;
2326        matchSettings.write2tmp = 0;
2327
2328        ArbMatchResultsManager& g_results_manager = get_results_manager();
2329        g_results_manager.reset();
2330
2331        // Using g_probe_collection instance of ArbProbeCollection, need to loop
2332        // through all the probes in the collect and then call probe_match_event,
2333        // collating the results as we go. The results can be obtained from the
2334        // g_spd->probeSeq list.
2335        const ArbProbePtrList& rProbeList = get_probe_collection().probeList();
2336
2337        const long nItems = rProbeList.size();
2338        long       nItem  = 1;
2339
2340        int nHits       = 0;
2341        int nProbeIndex = 0;
2342
2343        // This extra scope needed to ensure the arb_progress object is released
2344        // prior to the next one being used to show progress on write results to file
2345        {
2346            arb_progress progress("Matching probe collection", nItems);
2347
2348            ArbProbeCollection& g_probe_collection = get_probe_collection();
2349
2350            for (ArbProbePtrListConstIter ProbeIter = rProbeList.begin() ; ProbeIter != rProbeList.end() && !error; ++ProbeIter) {
2351                const ArbProbe *pProbe = *ProbeIter;
2352
2353                if (pProbe) {
2354                    int                nMatches;
2355                    ArbMatchResultSet *pResultSet = g_results_manager.addResultSet(pProbe);
2356
2357                    // Update status - put after matching cause matching writes its own status messages
2358                    progress.subtitle(GBS_global_string("Matching probe %li of %li", nItem, nItems));
2359
2360                    // Perform match on pProbe
2361                    matchSettings.targetString  = pProbe->sequence();
2362                    matchSettings.maxMismatches = pProbe->allowedMismatches();
2363
2364                    int                   counter = -1;
2365                    ProbeMatchEventParam  match_event(ntw->gb_main, &counter);
2366
2367                    error = probe_match_event(matchSettings, &match_event);
2368
2369                    pResultSet->initialise(pProbe, nProbeIndex);
2370                    nProbeIndex++;
2371
2372                    if (g_spd && g_spd->getHeadline() && !error) {
2373                        ProbeMatchParser parser(pProbe->sequence().c_str(), g_spd->getHeadline());
2374
2375                        if (parser.get_error()) {
2376                            error = parser.get_error();
2377                        }
2378                        else {
2379                            int nStartFullName  = 0;
2380                            int nEndFullName    = 0;
2381
2382                            parser.getColumnRange("fullname", &nStartFullName, &nEndFullName);
2383
2384                            pResultSet->headline(g_spd->getHeadline(), nEndFullName);
2385
2386                            // Collate match results
2387                            nMatches = g_spd->probeSeq.size();
2388
2389                            for (int cn = 0 ; cn < nMatches && !error ; cn++) {
2390                                const std::string& sResult = g_spd->probeSeq[cn];
2391
2392                                ParsedProbeMatch parsed(sResult.c_str(), parser);
2393
2394                                if (parsed.get_error()) {
2395                                    error = parsed.get_error();
2396                                }
2397                                else {
2398                                    char       *pName      = parsed.get_column_content("name", true);
2399                                    char       *pFullName  = parsed.get_column_content("fullname", true);
2400                                    const char *pMatchPart = parsed.get_probe_region();
2401
2402                                    pResultSet->add(pName,
2403                                                    pFullName,
2404                                                    pMatchPart,
2405                                                    sResult.c_str(),
2406                                                    g_probe_collection.matchWeighting());
2407
2408                                    free(pName);
2409                                    free(pFullName);
2410                                }
2411                            }
2412                        }
2413                    }
2414
2415                    if (error) error = GBS_global_string("while matching probe #%li: %s", nItem, error);
2416
2417                    nItem++;
2418                }
2419                progress.inc_and_check_user_abort(error);
2420            }
2421        }
2422
2423        if (!error) {
2424            ArbWriteFile_Context Context;
2425
2426            arb_progress progress("Writing results to file", 100L);
2427            progress.subtitle(g_results_manager.resultsFileName());
2428
2429            Context.pFile         = fopen(g_results_manager.resultsFileName(), "w");
2430            Context.pProgress     = &progress;
2431            Context.nLastPercent  = 0;
2432
2433            nHits = g_results_manager.enumerate_results(probe_match_with_specificity_enum_callback, (void*)&Context);
2434
2435            fclose(Context.pFile);
2436
2437            if (!error) error = progress.error_if_aborted();
2438            progress.done();
2439        }
2440
2441        root->awar(AWAR_PC_MATCH_NHITS)->write_int(nHits);
2442
2443        if (error) {
2444            aw_message(error);
2445
2446            // Clear the results set
2447            g_results_manager.reset();
2448        }
2449        else {
2450            // Open the Probe Match Specificity dialog to interactively show how the
2451            // matches target the phylongeny
2452            create_probe_match_specificity_control_window(root)->show();
2453
2454            double oldMaxWeight = g_results_manager.maximumWeight();
2455            g_results_manager.updateResults();
2456            double newMaxWeight = g_results_manager.maximumWeight();
2457
2458            if (newMaxWeight != oldMaxWeight) {
2459                // set new limits for scaler and force current value into limits
2460                newMaxWeight = std::max(newMaxWeight, 0.000001); // avoid invalid limits
2461                root->awar(AWAR_PC_MISMATCH_THRESHOLD)->set_minmax(0.0, newMaxWeight)->touch();
2462            }
2463        }
2464
2465        refresh_matchedProbesDisplay_cb(root, ntw);
2466    }
2467}
2468
2469static void auto_match_cb(AW_root *root, TREE_canvas *ntw) {
2470    if (root->awar(AWAR_PC_AUTO_MATCH)->read_int()) {
2471        probe_match_with_specificity_event(root, ntw);
2472    }
2473}
2474static void trigger_auto_match(AW_root *root) {
2475    root->awar(AWAR_PC_AUTO_MATCH)->touch();
2476}
2477
2478// ----------------------------------------------------------------------------
2479
2480static void probe_forget_matches_event(AW_window *aww, ArbPM_Context *pContext) {
2481    AW_root *root = aww->get_root();
2482
2483    get_results_manager().reset();
2484    root->awar(AWAR_PC_MATCH_NHITS)->write_int(0);
2485    refresh_matchedProbesDisplay_cb(root, pContext->ntw);
2486}
2487
2488static void selected_probe_changed_cb(AW_root *root) {
2489    char *pSequence = root->awar(AWAR_PC_SELECTED_PROBE)->read_string();
2490    if (pSequence) {
2491        const ArbProbe *pProbe = get_probe_collection().find(pSequence);
2492        if (pProbe) {
2493            const char *seq = pProbe->sequence().c_str();
2494            root->awar(AWAR_PC_TARGET_STRING)->write_string(seq);
2495            root->awar(AWAR_PC_TARGET_NAME)  ->write_string(pProbe->name().c_str());
2496
2497            root->awar(AWAR_TARGET_STRING)->write_string(seq); // copy to probe match & match in edit4
2498        }
2499    }
2500    free(pSequence);
2501}
2502
2503static void target_string_changed_cb(AW_root *root) {
2504    char *pSequence = root->awar(AWAR_PC_TARGET_STRING)->read_string();
2505    if (pSequence && pSequence[0]) {
2506        const ArbProbe *pProbe = get_probe_collection().find(pSequence);
2507        if (pProbe) root->awar(AWAR_PC_SELECTED_PROBE)->write_string(pSequence);
2508    }
2509    free(pSequence);
2510}
2511
2512static void match_changed_cb(AW_root *root) {
2513    // automatically adapt to last probe matched
2514    // (also adapts to last selected designed probe)
2515    char *pSequence = root->awar(AWAR_TARGET_STRING)->read_string();
2516    if (pSequence && pSequence[0]) {
2517        AW_awar *awar_target = root->awar(AWAR_PC_TARGET_STRING);
2518        if (strcmp(awar_target->read_char_pntr(), pSequence) != 0) {
2519            root->awar(AWAR_PC_TARGET_NAME)->write_string(""); // clear name
2520        }
2521        awar_target->write_string(pSequence);
2522    }
2523    free(pSequence);
2524}
2525
2526// ----------------------------------------------------------------------------
2527
2528AW_window *create_probe_match_with_specificity_window(AW_root *root, TREE_canvas *ntw) {
2529    static AW_window_simple *aws = NULp; // the one and only probeSpec window
2530
2531    if (!aws) {
2532        root->awar(AWAR_PC_MISMATCH_THRESHOLD)->add_callback(makeRootCallback(refresh_matchedProbesDisplay_cb, ntw));
2533
2534        aws = new AW_window_simple;
2535
2536        aws->init(root, "PROBE_MATCH_WITH_SPECIFICITY", "PROBE MATCH WITH SPECIFICITY");
2537
2538        aws->load_xfig("pd_match_with_specificity.fig");
2539
2540        aws->at("close");
2541        aws->callback(AW_POPDOWN);
2542        aws->create_button("CLOSE", "CLOSE", "C");
2543
2544        aws->callback(makeHelpCallback("probespec.hlp"));
2545        aws->at("help");
2546        aws->create_button("HELP", "HELP", "H");
2547
2548        AW_selection_list *probes_id;
2549
2550        aws->at("probes");
2551        probes_id = aws->create_selection_list(AWAR_PC_SELECTED_PROBE, 110, 10, false);
2552        probes_id->insert_default("", "");
2553
2554        PM_Context.probes_id = probes_id;
2555        PM_Context.ntw       = ntw;
2556
2557        aws->callback(makeWindowCallback(probe_match_with_specificity_edit_event));
2558        aws->at("results");
2559        aws->create_button("RESULTS", "RESULTS", "R");
2560
2561        aws->at("pt_server");
2562        awt_create_PTSERVER_selection_button(aws, AWAR_PT_SERVER);
2563
2564        aws->at("nhits");
2565        aws->create_button(NULp, AWAR_PC_MATCH_NHITS);
2566
2567        aws->callback(makeCreateWindowCallback(create_probe_collection_window, &PM_Context));
2568        aws->at("edit");
2569        aws->create_button("EDIT", "EDIT", "E");
2570
2571        aws->callback(RootAsWindowCallback::simple(probe_match_with_specificity_event, ntw));
2572        aws->at("match");
2573        aws->create_button("MATCH", "MATCH", "M");
2574
2575        aws->callback(makeWindowCallback(probe_forget_matches_event, &PM_Context));
2576        aws->at("forget");
2577        aws->create_button("FORGET", "FORGET", "F");
2578
2579        aws->at("auto");
2580        aws->label("Auto match");
2581        aws->create_toggle(AWAR_PC_AUTO_MATCH);
2582
2583        AW_awar *awar_automatch = root->awar(AWAR_PC_AUTO_MATCH);
2584        awar_automatch->add_callback(makeRootCallback(auto_match_cb, ntw));
2585
2586        aws->callback(makeCreateWindowCallback(create_probe_match_specificity_control_window));
2587        aws->at("control");
2588        aws->create_autosize_button("CONTROL", "Display control", "D");
2589
2590        probe_match_update_probe_list(&PM_Context);
2591
2592        root->awar(AWAR_PC_SELECTED_PROBE)->add_callback(makeRootCallback(selected_probe_changed_cb));
2593        root->awar(AWAR_PC_TARGET_STRING)->add_callback(makeRootCallback(target_string_changed_cb));
2594        root->awar(AWAR_TARGET_STRING)->add_callback(makeRootCallback(match_changed_cb));
2595
2596        awar_automatch->touch(); // automatically run match if 'auto-match' is checked at startup
2597    }
2598
2599    return aws;
2600}
2601
2602// ----------------------------------------------------------------------------
2603
2604struct ArbPC_Context {
2605    AW_selection_list *selection_id;
2606    ArbPM_Context     *PM_Context;
2607
2608    ArbPC_Context() :
2609        selection_id(NULp),
2610        PM_Context(NULp)
2611    {}
2612};
2613
2614static ArbPC_Context PC_Context;
2615
2616// ----------------------------------------------------------------------------
2617
2618static void save_probe_list_to_DB(const ArbProbePtrList& rProbeList, AW_root *root) {
2619    std::string saved;
2620    for (ArbProbePtrListConstIter ProbeIter = rProbeList.begin() ; ProbeIter != rProbeList.end() ; ++ProbeIter) {
2621        const ArbProbe *pProbe = *ProbeIter;
2622        if (pProbe) {
2623            // Note: target sequences/names do not contain '#'/':' (see REPLACE_TARGET_CONTROL_CHARS)
2624            saved = saved + '#'+pProbe->name()+':'+pProbe->sequence();
2625        }
2626    }
2627
2628    root->awar(AWAR_PC_CURRENT_COLLECTION)->write_string(saved.empty() ? "" : saved.c_str()+1);
2629}
2630
2631static void show_probes_in_sellist(const ArbProbePtrList& rProbeList, AW_selection_list *sellist) {
2632    sellist->clear();
2633    sellist->insert_default("", "");
2634    for (ArbProbePtrListConstIter ProbeIter = rProbeList.begin() ; ProbeIter != rProbeList.end() ; ++ProbeIter) {
2635        const ArbProbe *pProbe = *ProbeIter;
2636        if (pProbe) {
2637            sellist->insert(pProbe->displayName().c_str(), pProbe->sequence().c_str());
2638        }
2639    }
2640    sellist->update();
2641}
2642
2643static void load_probe_collection(AW_window *aww, ArbPC_Context *Context, const char * const *awar_filename) {
2644    char *pFileName = aww->get_root()->awar(*awar_filename)->read_string();
2645
2646    ArbProbeCollection ProbeCollection;
2647    std::string        sErrorMessage;
2648
2649    if (ProbeCollection.openXML(pFileName, sErrorMessage)) {
2650        int    cn;
2651        char   buffer[256] = {0};
2652        float  weights[16] = {0.0};
2653        float  dWidth      = 1.0;
2654        float  dBias       = 0.0;
2655
2656        ArbProbeCollection& g_probe_collection = get_probe_collection();
2657        g_probe_collection = ProbeCollection;
2658
2659        g_probe_collection.getParameters(weights, dWidth, dBias);
2660
2661        AW_root *root = AW_root::SINGLETON;
2662        root->awar(AWAR_PC_MATCH_WIDTH)->write_float(dWidth);
2663        root->awar(AWAR_PC_MATCH_BIAS)->write_float(dBias);
2664
2665        for (cn = 0; cn < 16 ; cn++) {
2666            sprintf(buffer, AWAR_PC_MATCH_WEIGHTS"%i", cn);
2667
2668            root->awar(buffer)->write_float(weights[cn]);
2669        }
2670
2671        const ArbProbePtrList& rProbeList = g_probe_collection.probeList();
2672
2673        save_probe_list_to_DB(rProbeList, root);
2674        show_probes_in_sellist(rProbeList, Context->selection_id);
2675
2676        probe_match_update_probe_list(Context->PM_Context);
2677        aww->hide();
2678        trigger_auto_match(root);
2679    }
2680    else {
2681        // Print error message
2682        aw_message(sErrorMessage.c_str());
2683    }
2684
2685    free(pFileName);
2686}
2687
2688// ----------------------------------------------------------------------------
2689
2690static void probe_collection_update_parameters() {
2691    AW_root *root = AW_root::SINGLETON;
2692
2693    int cn;
2694    char buffer[256] = {0};
2695
2696    float weights[16] = {0.0};
2697    float dWidth = root->awar(AWAR_PC_MATCH_WIDTH)->read_float();
2698    float dBias  = root->awar(AWAR_PC_MATCH_BIAS)->read_float();
2699
2700    for (cn = 0; cn < 16 ; cn++) {
2701        sprintf(buffer, AWAR_PC_MATCH_WEIGHTS"%i", cn);
2702
2703        weights[cn] = root->awar(buffer)->read_float();
2704    }
2705
2706    ArbProbeCollection& g_probe_collection = get_probe_collection();
2707    g_probe_collection.setParameters(weights, dWidth, dBias);
2708
2709    save_probe_list_to_DB(g_probe_collection.probeList(), root);
2710}
2711
2712// ----------------------------------------------------------------------------
2713
2714static void save_probe_collection(AW_window *aww, const char * const *awar_filename) {
2715    char *pFileName = aww->get_root()->awar(*awar_filename)->read_string();
2716
2717    struct stat   FileStatus;
2718    int           nResult = ::stat(pFileName, &FileStatus);
2719    bool          bWrite  = true;
2720
2721    if (nResult == 0) {
2722        bWrite = (aw_question("probe_collection_save", "File already exists. Overwrite?", "YES,NO") == 0);
2723    }
2724
2725    if (bWrite) {
2726        probe_collection_update_parameters();
2727        get_probe_collection().saveXML(pFileName);
2728
2729        aww->hide();
2730    }
2731
2732    free(pFileName);
2733}
2734
2735// ----------------------------------------------------------------------------
2736
2737static void add_probe_to_collection_event(AW_window *aww, ArbPC_Context *pContext) {
2738    AW_selection_list *selection_id = pContext->selection_id;
2739    if (selection_id) {
2740        AW_root *root      = aww->get_root();
2741        char    *pSequence = root->awar(AWAR_PC_TARGET_STRING)->read_string();
2742        char    *pName     = root->awar(AWAR_PC_TARGET_NAME)->read_string();
2743
2744        GB_ERROR error = NULp;
2745        if (!pSequence || !pSequence[0]) {
2746            error = "Please enter a target string";
2747        }
2748        else if (get_probe_collection().find(pSequence)) {
2749            error = "Target string already in collection";
2750        }
2751
2752        if (!error) {
2753            const ArbProbe *pProbe = NULp;
2754
2755            if (get_probe_collection().add(pName, pSequence, &pProbe) && pProbe) {
2756                selection_id->insert(pProbe->displayName().c_str(), pSequence);
2757                selection_id->update();
2758                probe_match_update_probe_list(pContext->PM_Context);
2759                probe_collection_update_parameters();
2760
2761                root->awar(AWAR_PC_SELECTED_PROBE)->write_string(pSequence);
2762                trigger_auto_match(root);
2763            }
2764            else {
2765                error = "failed to add probe";
2766            }
2767        }
2768
2769        aw_message_if(error);
2770
2771        free(pName);
2772        free(pSequence);
2773    }
2774}
2775
2776static void modify_probe_event(AW_window *aww, ArbPC_Context *pContext) {
2777    AW_selection_list *selection_id = pContext->selection_id;
2778    if (selection_id) {
2779        AW_root *root            = aww->get_root();
2780        AW_awar *awar_selected   = root->awar(AWAR_PC_SELECTED_PROBE);
2781        char    *oldSequence     = awar_selected->read_string();
2782        char    *pSequence       = root->awar(AWAR_PC_TARGET_STRING)->read_string();
2783        char    *pName           = root->awar(AWAR_PC_TARGET_NAME)->read_string();
2784        bool     sequenceChanged = false;
2785
2786        GB_ERROR error = NULp;
2787        if (!pSequence || !pSequence[0]) {
2788            error = "Please enter a target string";
2789        }
2790        else if (!oldSequence || !oldSequence[0]) {
2791            error = "Please select probe to modify";
2792        }
2793        else {
2794            sequenceChanged = strcmp(oldSequence, pSequence) != 0;
2795            if (sequenceChanged) { // sequence changed -> test vs duplicate
2796                if (get_probe_collection().find(pSequence)) {
2797                    error = "Target string already in collection";
2798                }
2799            }
2800        }
2801
2802        if (!error) {
2803            const ArbProbe *pProbe = NULp;
2804
2805            if (get_probe_collection().replace(oldSequence, pName, pSequence, &pProbe) && pProbe) {
2806                AW_selection_list_iterator entry(selection_id, selection_id->get_index_of_selected());
2807                entry.set_displayed(pProbe->displayName().c_str());
2808                entry.set_value(AW_scalar(pSequence));
2809                selection_id->update();
2810
2811                probe_match_update_probe_list(pContext->PM_Context);
2812                probe_collection_update_parameters();
2813
2814                awar_selected->write_string(pSequence);
2815                trigger_auto_match(root);
2816            }
2817            else {
2818                error = "failed to replace probe";
2819            }
2820        }
2821
2822        aw_message_if(error);
2823
2824        free(pName);
2825        free(pSequence);
2826        free(oldSequence);
2827    }
2828}
2829
2830static void remove_probe_from_collection_event(AW_window *aww, ArbPC_Context *pContext) {
2831    AW_selection_list *selection_id = pContext->selection_id;
2832    if (selection_id) {
2833        AW_root *root      = aww->get_root();
2834        char    *pSequence = root->awar(AWAR_PC_SELECTED_PROBE)->read_string();
2835        if (pSequence) {
2836            const ArbProbe *pProbe = get_probe_collection().find(pSequence);
2837            if (pProbe) {
2838                int idx = selection_id->get_index_of_selected();
2839                selection_id->delete_element_at(idx);
2840
2841                if (selection_id->size() < 1) {
2842                    selection_id->insert_default("", "");
2843                }
2844                selection_id->update();
2845                selection_id->select_element_at(idx); // select next probe for deletion
2846
2847                get_probe_collection().remove(pSequence);
2848                probe_match_update_probe_list(pContext->PM_Context);
2849                probe_collection_update_parameters();
2850
2851                trigger_auto_match(root);
2852            }
2853            free(pSequence);
2854        }
2855    }
2856}
2857
2858// ----------------------------------------------------------------------------
2859
2860static AW_window *probe_collection_load_prompt(AW_root *root, ArbPC_Context *pContext) {
2861    static char *awar_name = NULp; // do not free, bound to callback
2862    return awt_create_load_box(root, "Load", "probe collection",
2863                               ".", "xpc", &awar_name,
2864                               makeWindowCallback(load_probe_collection, pContext, (const char*const*)&awar_name));
2865}
2866static AW_window *probe_collection_save_prompt(AW_root *root) {
2867    static char *awar_name = NULp; // do not free, bound to callback
2868    return awt_create_load_box(root, "Save", "probe collection",
2869                               ".", "xpc", &awar_name,
2870                               makeWindowCallback(save_probe_collection, (const char*const*)&awar_name));
2871}
2872
2873// ----------------------------------------------------------------------------
2874
2875static void probe_match_update_probe_list(ArbPM_Context *pContext) {
2876    if (pContext) {
2877        AW_selection_list *sellist = pContext->probes_id;
2878        if (sellist) show_probes_in_sellist(get_probe_collection().probeList(), sellist);
2879    }
2880}
2881static void clear_probe_collection_event(AW_window *aww, ArbPC_Context *pContext) {
2882    if (get_probe_collection().clear()) {
2883        probe_match_update_probe_list(pContext->PM_Context);
2884        show_probes_in_sellist(get_probe_collection().probeList(), pContext->selection_id);
2885        trigger_auto_match(aww->get_root());
2886    }
2887}
2888
2889// ----------------------------------------------------------------------------
2890
2891static void probe_collection_close(AW_window *aww) {
2892    probe_collection_update_parameters();
2893    aww->hide();
2894}
2895
2896// ----------------------------------------------------------------------------
2897
2898static AW_window *create_probe_collection_window(AW_root *root, ArbPM_Context *pContext) {
2899    static AW_window_simple *aws = NULp; // the one and only probe match window
2900    char                     buffer[256];
2901
2902    pd_assert(pContext);
2903    PC_Context.PM_Context = pContext;
2904
2905    if (!aws) {
2906        aws = new AW_window_simple;
2907
2908        aws->init(root, "PROBE_COLLECTION", "PROBE COLLECTION");
2909
2910        aws->load_xfig("pd_match_probe_collection.fig");
2911
2912        aws->callback(probe_collection_close);
2913        aws->at("close");
2914        aws->create_button("CLOSE", "CLOSE", "C");
2915
2916        aws->callback(makeHelpCallback("probespec.hlp"));
2917        aws->at("help");
2918        aws->create_button("HELP", "HELP", "H");
2919
2920        AW_selection_list *selection_id;
2921
2922        aws->at("probes");
2923        selection_id = aws->create_selection_list(AWAR_PC_SELECTED_PROBE, 110, 10, false);
2924        selection_id->insert_default("", "");
2925
2926        PC_Context.selection_id = selection_id;
2927
2928        aws->at("string");
2929        aws->create_input_field(AWAR_PC_TARGET_STRING, 32);
2930
2931        aws->at("name");
2932        aws->create_input_field(AWAR_PC_TARGET_NAME, 32);
2933
2934        aws->callback(makeWindowCallback(add_probe_to_collection_event, &PC_Context));
2935        aws->at("add");
2936        aws->create_button("ADD", "ADD", "A");
2937
2938        aws->callback(makeWindowCallback(modify_probe_event, &PC_Context));
2939        aws->at("modify");
2940        aws->create_button("MODIFY", "MODIFY", "M");
2941
2942        aws->callback(makeWindowCallback(remove_probe_from_collection_event, &PC_Context));
2943        aws->at("remove");
2944        aws->create_button("REMOVE", "REMOVE", "R");
2945
2946        aws->callback(makeCreateWindowCallback(probe_collection_load_prompt, &PC_Context));
2947        aws->at("open");
2948        aws->create_button("LOAD", "LOAD", "L");
2949
2950        aws->callback(makeCreateWindowCallback(probe_collection_save_prompt));
2951        aws->at("save");
2952        aws->create_button("SAVE", "SAVE", "S");
2953
2954        aws->callback(makeWindowCallback(clear_probe_collection_event, &PC_Context));
2955        aws->at("clear");
2956        aws->create_button("CLEAR", "CLEAR", "L");
2957
2958        for (int i = 0 ; i < 16 ; i++) {
2959            sprintf(buffer, "%i", i);
2960            aws->at(buffer);
2961            sprintf(buffer, AWAR_PC_MATCH_WEIGHTS"%i", i);
2962            aws->create_input_field(buffer, 4);
2963            root->awar(buffer)->add_callback(trigger_auto_match);
2964        }
2965
2966        aws->at("width");
2967        aws->create_input_field(AWAR_PC_MATCH_WIDTH, 5);
2968        root->awar(AWAR_PC_MATCH_WIDTH)->add_callback(trigger_auto_match);
2969
2970        aws->at("bias");
2971        aws->create_input_field(AWAR_PC_MATCH_BIAS, 5);
2972        root->awar(AWAR_PC_MATCH_BIAS)->add_callback(trigger_auto_match);
2973
2974        show_probes_in_sellist(get_probe_collection().probeList(), selection_id);
2975    }
2976    return aws;
2977}
Note: See TracBrowser for help on using the repository browser.