source: branches/ali/PROBE_DESIGN/probe_design.cxx

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