source: branches/species/PROBE_DESIGN/probe_design.cxx

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