source: tags/ms_r16q3/SL/DB_UI/ui_species.cxx

Last change on this file was 15176, checked in by westram, 8 years ago
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 60.5 KB
Line 
1// =============================================================== //
2//                                                                 //
3//   File      : ui_species.cxx                                    //
4//   Purpose   :                                                   //
5//                                                                 //
6//   Institute of Microbiology (Technical University Munich)       //
7//   http://www.arb-home.de/                                       //
8//                                                                 //
9// =============================================================== //
10
11#include "dbui.h"
12
13#include <awt_sel_boxes.hxx>
14#include <arb_strbuf.h>
15#include <cmath>
16#include <probe_gui.hxx>
17#include <arb_defs.h>
18#include <awtc_next_neighbours.hxx>
19#include <db_scanner.hxx>
20#include <db_query.h>
21#include <AW_rename.hxx>
22#include <aw_awar_defs.hxx>
23#include <aw_awar.hxx>
24#include <aw_root.hxx>
25#include <aw_msg.hxx>
26#include <aw_question.hxx>
27#include <algorithm>
28#include <arb_progress.h>
29#include <item_sel_list.h>
30#include <map>
31#include <info_window.h>
32#include <awt_config_manager.hxx>
33
34using namespace DBUI;
35using namespace QUERY;
36
37#define ui_assert(cond) arb_assert(cond)
38
39#define AWAR_SPECIES_DEST "tmp/adspec/dest"
40
41// awars shared between different itemtypes:
42#define AWAR_FIELD_REORDER_ORDER "tmp/adreorder/order"
43
44// separate awars for different itemtypes:
45#define AWAR_FIELD_REORDER_SOURCE_TMPL "tmp/adreorder/%s/source"
46#define AWAR_FIELD_REORDER_DEST_TMPL   "tmp/adreorder/%s/dest"
47#define AWAR_FIELD_CREATE_NAME_TMPL    "tmp/adfield/%s/name"
48#define AWAR_FIELD_CREATE_TYPE_TMPL    "tmp/adfield/%s/type"
49#define AWAR_FIELD_DELETE_TMPL         "tmp/adfield/%s/source"
50#define AWAR_FIELD_CONVERT_SOURCE_TMPL "tmp/adconvert/%s/source"
51#define AWAR_FIELD_CONVERT_TYPE_TMPL   "tmp/adconvert/%s/type"
52
53// next neighbours of listed and selected:
54// more defined in ../../AWTC/awtc_next_neighbours.hxx@AWAR_NN_BASE
55#define AWAR_NN_COMPLEMENT  AWAR_NN_BASE "complement"
56#define AWAR_NN_RANGE_START AWAR_NN_BASE "range_start"
57#define AWAR_NN_RANGE_END   AWAR_NN_BASE "range_end"
58#define AWAR_NN_MIN_SCORE   AWAR_NN_BASE "min_scored"
59#define AWAR_NN_MAX_HITS    AWAR_NN_BASE "max_hits"
60
61// next neighbours of selected only:
62#define AWAR_NN_BASE_SELECTED        AWAR_NN_BASE "selected/"
63#define AWAR_NN_SELECTED_HIT_COUNT   "tmp/" AWAR_NN_BASE_SELECTED "hit_count"
64#define AWAR_NN_SELECTED_AUTO_SEARCH "tmp/" AWAR_NN_BASE_SELECTED "auto_search"
65#define AWAR_NN_SELECTED_AUTO_MARK   "tmp/" AWAR_NN_BASE_SELECTED "auto_mark"
66
67// next neighbours of listed only:
68#define AWAR_NN_BASE_LISTED           AWAR_NN_BASE "listed/"
69#define AWAR_NN_LISTED_DEST_FIELD     AWAR_NN_BASE_LISTED "dest_field"
70#define AWAR_NN_LISTED_SCORED_ENTRIES AWAR_NN_BASE_LISTED "scored_entries"
71
72enum ReorderMode {
73    // real orders
74    ORDER_ALPHA,
75    ORDER_TYPE,
76    ORDER_FREQ,
77   
78    // special modes
79    RIGHT_BEHIND_LEFT,
80    REVERSE_ORDER,
81};
82
83void DBUI::create_dbui_awars(AW_root *aw_root) {
84    aw_root->awar_string(AWAR_SPECIES_DEST,        "",          AW_ROOT_DEFAULT);
85    aw_root->awar_int   (AWAR_FIELD_REORDER_ORDER, ORDER_ALPHA, AW_ROOT_DEFAULT);
86}
87
88inline const char *itemAwar(const char *name_template, ItemSelector& itype) {
89    return GBS_global_string(name_template, itype.item_name);
90}
91static void init_itemspecific_DBUI_awars(AW_root *aw_root, ItemSelector& itype) {
92    static bool initialized[QUERY_ITEM_TYPES] = { false };
93
94    if (!initialized[itype.type]) {
95        aw_root->awar_string(itemAwar(AWAR_FIELD_REORDER_SOURCE_TMPL, itype), "",          AW_ROOT_DEFAULT);
96        aw_root->awar_string(itemAwar(AWAR_FIELD_REORDER_DEST_TMPL,   itype), "",          AW_ROOT_DEFAULT);
97        aw_root->awar_string(itemAwar(AWAR_FIELD_CREATE_NAME_TMPL,    itype), "",          AW_ROOT_DEFAULT);
98        aw_root->awar_int   (itemAwar(AWAR_FIELD_CREATE_TYPE_TMPL,    itype), GB_STRING,   AW_ROOT_DEFAULT);
99        aw_root->awar_string(itemAwar(AWAR_FIELD_DELETE_TMPL,         itype), "",          AW_ROOT_DEFAULT);
100        aw_root->awar_string(itemAwar(AWAR_FIELD_CONVERT_SOURCE_TMPL, itype), "",          AW_ROOT_DEFAULT);
101        aw_root->awar_int   (itemAwar(AWAR_FIELD_CONVERT_TYPE_TMPL,   itype), GB_STRING,   AW_ROOT_DEFAULT);
102
103        initialized[itype.type] = true;
104    }
105}
106
107static void move_species_to_extended(AW_window *aww, GBDATA *gb_main) {
108    char     *source = aww->get_root()->awar(AWAR_SPECIES_NAME)->read_string();
109    GB_ERROR  error  = GB_begin_transaction(gb_main);
110
111    if (!error) {
112        GBDATA *gb_sai_data     = GBT_get_SAI_data(gb_main);
113        if (!gb_sai_data) error = GB_await_error();
114        else {
115            GBDATA *gb_species = GBT_find_species(gb_main, source);
116            GBDATA *gb_dest    = GBT_find_SAI_rel_SAI_data(gb_sai_data, source);
117
118            if (gb_dest) error = GBS_global_string("SAI '%s' already exists", source);
119            else if (gb_species) {
120                gb_dest             = GB_create_container(gb_sai_data, "extended");
121                if (!gb_dest) error = GB_await_error();
122                else {
123                    error = GB_copy(gb_dest, gb_species);
124                    if (!error) {
125                        error = GB_delete(gb_species);
126                        if (!error) aww->get_root()->awar(AWAR_SPECIES_NAME)->write_string("");
127                    }
128                }
129            }
130            else error = "Please select a species";
131        }
132    }
133    GB_end_transaction_show_error(gb_main, error, aw_message);
134    free(source);
135}
136
137
138static void species_create_cb(AW_window *aww, GBDATA *gb_main) {
139    char *dest = aww->get_root()->awar(AWAR_SPECIES_DEST)->read_string();
140    if (dest[0]) {
141        GB_ERROR error = GB_begin_transaction(gb_main);
142        if (!error) {
143            GBDATA *gb_species_data     = GBT_get_species_data(gb_main);
144            if (!gb_species_data) error = GB_await_error();
145            else {
146                GBDATA *gb_dest = GBT_find_species_rel_species_data(gb_species_data, dest);
147
148                if (gb_dest) error = GBS_global_string("Species '%s' already exists", dest);
149                else {
150                    gb_dest             = GBT_find_or_create_species_rel_species_data(gb_species_data, dest);
151                    if (!gb_dest) error = GB_await_error();
152                    else aww->get_root()->awar(AWAR_SPECIES_NAME)->write_string(dest);
153                }
154            }
155        }
156        GB_end_transaction_show_error(gb_main, error, aw_message);
157    }
158    else {
159        aw_message("Please enter a name for the new species");
160    }
161    free(dest);
162}
163
164static AW_window *create_species_create_window(AW_root *root, GBDATA *gb_main) {
165    AW_window_simple *aws = new AW_window_simple;
166
167    aws->init(root, "CREATE_SPECIES", "SPECIES CREATE");
168    aws->load_xfig("ad_al_si.fig");
169
170    aws->at("close");
171    aws->callback(AW_POPDOWN);
172    aws->create_button("CLOSE", "Close", "C");
173
174    aws->at("label");
175    aws->create_autosize_button(0, "Please enter the name\nof the new species");
176
177    aws->at("input");
178    aws->create_input_field(AWAR_SPECIES_DEST, 15);
179
180    aws->at("ok");
181    aws->callback(makeWindowCallback(species_create_cb, gb_main));
182    aws->create_button("GO", "Go", "G");
183
184    return aws;
185}
186
187static GBDATA *expect_species_selected(AW_root *aw_root, GBDATA *gb_main, char **give_name = 0) {
188    GB_transaction  ta(gb_main);
189    char           *name       = aw_root->awar(AWAR_SPECIES_NAME)->read_string();
190    GBDATA         *gb_species = GBT_find_species(gb_main, name);
191
192    if (!gb_species) {
193        if (name && name[0]) aw_message(GBS_global_string("Species '%s' does not exist.", name));
194        else aw_message("Please select a species first");
195    }
196
197    if (give_name) *give_name = name;
198    else free(name);
199
200    return gb_species;
201}
202
203static void species_copy_cb(AW_window *aww, GBDATA *gb_main) {
204    AW_root *aw_root    = aww->get_root();
205    char    *name;
206    GBDATA  *gb_species = expect_species_selected(aw_root, gb_main, &name);
207
208    if (gb_species) {
209        GB_transaction      ta(gb_main);
210        GBDATA             *gb_species_data = GB_get_father(gb_species);
211        UniqueNameDetector  und(gb_species_data);
212        GB_ERROR            error           = 0;
213        char               *copy_name       = AWTC_makeUniqueShortName(GBS_global_string("c%s", name), und);
214
215        if (!copy_name) error = GB_await_error();
216        else {
217            GBDATA *gb_new_species = GB_create_container(gb_species_data, "species");
218
219            if (!gb_new_species) error = GB_await_error();
220            else {
221                error = GB_copy(gb_new_species, gb_species);
222                if (!error) {
223                    error = GBT_write_string(gb_new_species, "name", copy_name);
224                    if (!error) aw_root->awar(AWAR_SPECIES_NAME)->write_string(copy_name); // set focus
225                }
226            }
227
228            free(copy_name);
229        }
230        if (error) {
231            error = ta.close(error);
232            aw_message(error);
233        }
234    }
235}
236
237static void species_rename_cb(AW_window *aww, GBDATA *gb_main) {
238    AW_root *aw_root    = aww->get_root();
239    GBDATA  *gb_species = expect_species_selected(aw_root, gb_main);
240    if (gb_species) {
241        GB_transaction  ta(gb_main);
242        GBDATA         *gb_full_name  = GB_search(gb_species, "full_name", GB_STRING);
243        const char     *full_name     = gb_full_name ? GB_read_char_pntr(gb_full_name) : "";
244        char           *new_full_name = aw_input("Enter new 'full_name' of species:", full_name);
245
246        if (new_full_name) {
247            GB_ERROR error = 0;
248
249            if (strcmp(full_name, new_full_name) != 0) {
250                error = GB_write_string(gb_full_name, new_full_name);
251            }
252            if (!error) {
253                bool recreateID = ARB_in_expert_mode(aw_root) && // never re-create ID in novice mode
254                    aw_ask_sure("recreate_name_field",
255                                "Regenerate species identifier ('name')?\n"
256                                "(only do this if you know what you're doing)");
257                if (recreateID) {
258                    arb_progress progress("Regenerating species ID", 1);
259                    error = AWTC_recreate_name(gb_species);
260                    if (!error) aw_root->awar(AWAR_SPECIES_NAME)->write_string(GBT_read_name(gb_species)); // set focus
261                }
262            }
263
264            if (error) {
265                error = ta.close(error);
266                aw_message(error);
267            }
268        }
269    }
270}
271
272static void species_delete_cb(AW_window *aww, GBDATA *gb_main) {
273    AW_root  *aw_root    = aww->get_root();
274    char     *name;
275    GBDATA   *gb_species = expect_species_selected(aw_root, gb_main, &name);
276    GB_ERROR  error      = 0;
277
278    if (!gb_species) {
279        error = "Please select a species first";
280    }
281    else if (aw_ask_sure("info_delete_species", GBS_global_string("Are you sure to delete the species '%s'?", name))) {
282        GB_transaction ta(gb_main);
283        error = GB_delete(gb_species);
284        error = ta.close(error);
285        if (!error) aw_root->awar(AWAR_SPECIES_NAME)->write_string("");
286    }
287
288    if (error) aw_message(error);
289    free(name);
290}
291
292static long count_field_occurrance(BoundItemSel *bsel, const char *field_name) {
293    QUERY_RANGE   RANGE = QUERY_ALL_ITEMS;
294    long          count = 0;
295    ItemSelector& sel   = bsel->selector;
296
297    for (GBDATA *gb_container = sel.get_first_item_container(bsel->gb_main, NULL, RANGE);
298         gb_container;
299         gb_container = sel.get_next_item_container(gb_container, RANGE))
300    {
301        for (GBDATA *gb_item = sel.get_first_item(gb_container, RANGE);
302             gb_item;
303             gb_item = sel.get_next_item(gb_item, RANGE))
304        {
305            GBDATA *gb_field = GB_entry(gb_item, field_name);
306            if (gb_field) ++count;
307        }
308    }
309    return count;
310}
311
312class KeySorter : virtual Noncopyable {
313    int      key_count;
314    GBDATA **key;
315
316    int field_count; // = key_count - container_count
317
318    bool order_changed;
319
320    // helper variables for sorting
321    static GB_HASH      *order_hash;
322    static BoundItemSel *bitem_selector;
323    static arb_progress *sort_progress;
324
325    bool legal_pos(int p) { return p >= 0 && p<key_count; }
326    bool legal_field_pos(int p) const { return p >= 0 && p<field_count; }
327    bool legal_field_pos(int p1, int p2) const { return legal_field_pos(p1) && legal_field_pos(p2); }
328
329    void swap(int p1, int p2) {
330        ui_assert(legal_pos(p1));
331        ui_assert(legal_pos(p2));
332
333        GBDATA *k = key[p1];
334        key[p1]   = key[p2];
335        key[p2]   = k;
336
337        order_changed = true;
338    }
339
340    const char *field_name(int p) const {
341        GBDATA *gb_key_name = GB_entry(key[p], "key_name");
342        if (gb_key_name) return GB_read_char_pntr(gb_key_name);
343        return NULL;
344    }
345    GB_TYPES field_type(int p) const {
346        GBDATA *gb_key_type = GB_entry(key[p], "key_type");
347        if (gb_key_type) return GB_TYPES(GB_read_int(gb_key_type));
348        return GB_NONE;
349    }
350    int field_freq(int p) const {
351        const char *name            = field_name(p);
352        if (!order_hash) order_hash = GBS_create_hash(key_count, GB_IGNORE_CASE);
353
354        long freq = GBS_read_hash(order_hash, name);
355        if (!freq) {
356            freq = 1+count_field_occurrance(bitem_selector, name);
357            GBS_write_hash(order_hash, name, freq);
358            if (sort_progress) sort_progress->inc();
359        }
360        return freq;
361    }
362
363    int compare(int p1, int p2, ReorderMode mode) {
364        switch (mode) {
365            case RIGHT_BEHIND_LEFT:
366            case REVERSE_ORDER: 
367                ui_assert(0); // illegal ReorderMode
368                break;
369
370            case ORDER_TYPE:  return field_type(p2)-field_type(p1);
371            case ORDER_ALPHA: return strcasecmp(field_name(p1), field_name(p2));
372            case ORDER_FREQ:  return field_freq(p2)-field_freq(p1);
373        }
374        return p2-p1; // keep order
375    }
376
377public:
378    KeySorter(GBDATA *gb_key_data) {
379        key_count   = 0;
380        field_count = 0;
381        key         = NULL;
382
383        for (GBDATA *gb_key = GB_child(gb_key_data); gb_key; gb_key = GB_nextChild(gb_key)) {
384            key_count++;
385        }
386
387        if (key_count) {
388            ARB_alloc(key, key_count);
389           
390            int container_count = 0;
391            for (GBDATA *gb_key = GB_child(gb_key_data); gb_key; gb_key = GB_nextChild(gb_key)) {
392                GBDATA   *gb_type = GB_entry(gb_key, CHANGEKEY_TYPE);
393                GB_TYPES  type    = GB_TYPES(GB_read_int(gb_type));
394
395                if (type == GB_DB) { // move containers behind fields
396                    key[key_count-1-container_count++] = gb_key;
397                }
398                else {
399                    key[field_count++] = gb_key;
400                }
401            }
402            ui_assert((container_count+field_count) == key_count);
403            reverse_order(field_count, key_count-1); // of containers
404        }
405        order_changed = false;
406    }
407    ~KeySorter() {
408        ui_assert(!order_changed); // order changed but not written
409        free(key);
410    }
411
412    int get_field_count() const { return field_count; }
413
414    void bubble_sort(int p1, int p2, ReorderMode mode, BoundItemSel *selector) {
415        if (p1>p2) std::swap(p1, p2);
416        if (legal_field_pos(p1, p2)) {
417            if (mode == ORDER_FREQ) {
418                sort_progress = new arb_progress("Calculating field frequencies", p2-p1+1);
419            }
420            bitem_selector = selector;
421            while (p1<p2) {
422                bool changed = false;
423
424                int i = p2;
425                while (i>p1) {
426                    if (compare(i-1, i, mode)>0) {
427                        swap(i-1, i);
428                        changed = true;
429                    }
430                    --i;
431                }
432                if (!changed) break;
433                ++p1;
434            }
435            if (order_hash) {
436                GBS_free_hash(order_hash);
437                order_hash = NULL;
438            }
439            if (sort_progress) {
440                delete sort_progress;
441                sort_progress = NULL;
442            }
443        }
444    }
445    void reverse_order(int p1, int p2) {
446        if (p1>p2) std::swap(p1, p2);
447        if (legal_field_pos(p1, p2)) while (p1<p2) swap(p1++, p2--);
448    }
449
450    int index_of(GBDATA *gb_key) {
451        int i;
452        for (i = 0; i<key_count; i++) {
453            if (gb_key == key[i]) break;
454        }
455        if (i == key_count) {
456            ui_assert(0);
457            i = -1;
458        }
459        return i;
460    }
461
462    void move_to(int to_move, int wanted_position) {
463        if (legal_field_pos(to_move, wanted_position)) {
464            while (to_move<wanted_position) {
465                swap(to_move, to_move+1);
466                to_move++;
467            }
468            while (to_move>wanted_position) {
469                swap(to_move, to_move-1);
470                to_move--;
471            }
472        }
473    }
474
475    __ATTR__USERESULT GB_ERROR save_changes() {
476        GB_ERROR warning = NULL;
477        if (order_changed) {
478            if (key_count) {
479                GBDATA *gb_main = GB_get_root(key[0]);
480                warning         = GB_resort_data_base(gb_main, key, key_count);
481            }
482            order_changed = false;
483        }
484        return warning;
485    }
486};
487
488GB_HASH      *KeySorter::order_hash     = NULL;
489BoundItemSel *KeySorter::bitem_selector = NULL;
490arb_progress *KeySorter::sort_progress  = NULL;
491
492static void reorder_keys(AW_window *aws, ReorderMode mode, Itemfield_Selection *sel_left, Itemfield_Selection *sel_right) {
493    ItemSelector& selector = sel_left->get_selector();
494    ui_assert(&selector == &sel_right->get_selector());
495
496    int left_index  = sel_left->get_sellist()->get_index_of_selected();
497    int right_index = sel_right->get_sellist()->get_index_of_selected();
498
499    GB_ERROR warning = 0;
500
501    GBDATA  *gb_main = sel_left->get_gb_main();
502    AW_root *awr     = aws->get_root();
503   
504    init_itemspecific_DBUI_awars(awr, selector);
505
506    GB_begin_transaction(gb_main);
507
508    GBDATA *gb_left_field  = GBT_get_changekey(gb_main, awr->awar(itemAwar(AWAR_FIELD_REORDER_SOURCE_TMPL, selector))->read_char_pntr(), selector.change_key_path);
509    GBDATA *gb_right_field = GBT_get_changekey(gb_main, awr->awar(itemAwar(AWAR_FIELD_REORDER_DEST_TMPL, selector))->read_char_pntr(), selector.change_key_path);
510
511    if (!gb_left_field || !gb_right_field || gb_left_field == gb_right_field) {
512        warning = "Please select different fields in both list";
513    }
514    else {
515        GBDATA    *gb_key_data = GB_search(gb_main, selector.change_key_path, GB_CREATE_CONTAINER);
516        KeySorter  sorter(gb_key_data);
517
518        int left_key_idx  = sorter.index_of(gb_left_field);
519        int right_key_idx = sorter.index_of(gb_right_field);
520
521        switch (mode) {
522            case RIGHT_BEHIND_LEFT:
523                sorter.move_to(right_key_idx, left_key_idx+(left_key_idx<right_key_idx));
524                if (right_index>left_index) { left_index++; right_index++; } // make it simple to move several consecutive keys
525                break;
526            case REVERSE_ORDER:
527                sorter.reverse_order(left_key_idx, right_key_idx);
528                std::swap(left_index, right_index);
529                break;
530            default: {
531                BoundItemSel bis(gb_main, selector);
532                sorter.bubble_sort(left_key_idx, right_key_idx, mode, &bis);
533                break;
534            }
535        }
536
537        warning = sorter.save_changes();
538    }
539    GB_commit_transaction(gb_main);
540
541    if (warning) {
542        aw_message(warning);
543    }
544    else {
545        sel_left->get_sellist()->select_element_at(left_index);
546        sel_right->get_sellist()->select_element_at(right_index);
547    }
548}
549
550static void reorder_right_behind_left(AW_window *aws, Itemfield_Selection *selleft, Itemfield_Selection *selright) { reorder_keys(aws, RIGHT_BEHIND_LEFT, selleft, selright); }
551static void reverse_key_order        (AW_window *aws, Itemfield_Selection *selleft, Itemfield_Selection *selright) { reorder_keys(aws, REVERSE_ORDER,     selleft, selright); }
552
553static void sort_keys(AW_window *aws, Itemfield_Selection *selleft, Itemfield_Selection *selright) {
554    ReorderMode mode = ReorderMode(aws->get_root()->awar(AWAR_FIELD_REORDER_ORDER)->read_int());
555    reorder_keys(aws, mode, selleft, selright);
556}
557
558static void reorder_up_down(AW_window *aws, Itemfield_Selection *sel_right, int dir) {
559    GBDATA *gb_main = sel_right->get_gb_main();
560
561    GB_begin_transaction(gb_main);
562    ItemSelector& selector   = sel_right->get_selector();
563    int           list_index = sel_right->get_sellist()->get_index_of_selected();
564
565    const char *field_name = aws->get_root()->awar(itemAwar(AWAR_FIELD_REORDER_DEST_TMPL, selector))->read_char_pntr();
566    GBDATA     *gb_field   = GBT_get_changekey(gb_main, field_name, selector.change_key_path);
567    GB_ERROR    warning    = 0;
568
569    if (!gb_field) {
570        warning = "Please select the item to move in the right box";
571    }
572    else {
573        GBDATA    *gb_key_data = GB_search(gb_main, selector.change_key_path, GB_CREATE_CONTAINER);
574        KeySorter  sorter(gb_key_data);
575
576        int curr_index = sorter.index_of(gb_field);
577        int dest_index = -1;
578        if (abs(dir) == 1) {
579            dest_index = curr_index+dir;
580            list_index = -1;
581        }
582        else {
583            dest_index = dir<0 ? 0 : sorter.get_field_count()-1;
584        }
585
586        sorter.move_to(curr_index, dest_index);
587        warning = sorter.save_changes();
588
589    }
590
591    GB_commit_transaction(gb_main);
592    if (list_index >= 0) sel_right->get_sellist()->select_element_at(list_index);
593    if (warning) aw_message(warning);
594}
595
596AW_window *DBUI::create_fields_reorder_window(AW_root *root, BoundItemSel *bound_selector) {
597    ItemSelector& selector = bound_selector->selector;
598
599    static AW_window_simple *awsa[QUERY_ITEM_TYPES];
600    if (!awsa[selector.type]) {
601        AW_window_simple *aws = new AW_window_simple;
602        awsa[selector.type]  = aws;
603
604        init_itemspecific_DBUI_awars(root, selector);
605        init_itemType_specific_window(root, aws, selector, "REORDER_FIELDS", "Reorder %s fields");
606        aws->load_xfig("ad_kreo.fig");
607
608        aws->at("close");
609        aws->callback(AW_POPDOWN);
610        aws->create_button("CLOSE", "Close", "C");
611
612        aws->at("help");
613        const char *HELPFILE = "spaf_reorder.hlp";
614        aws->callback(makeHelpCallback(HELPFILE));
615        aws->create_button("HELP", "Help", "H");
616
617        Itemfield_Selection *sel1 = create_itemfield_selection_list(aws, FieldSelDef(itemAwar(AWAR_FIELD_REORDER_SOURCE_TMPL, selector), bound_selector->gb_main, selector, FIELD_UNFILTERED), "source");
618        Itemfield_Selection *sel2 = create_itemfield_selection_list(aws, FieldSelDef(itemAwar(AWAR_FIELD_REORDER_DEST_TMPL,   selector), bound_selector->gb_main, selector, FIELD_UNFILTERED), "dest");
619
620        aws->button_length(8);
621
622        aws->at("sort");
623        aws->callback(makeWindowCallback(sort_keys, sel1, sel2));
624        aws->help_text(HELPFILE);
625        aws->create_button("SORT", "Sort by");
626
627        aws->at("sorttype");
628        aws->create_option_menu(AWAR_FIELD_REORDER_ORDER, true);
629        aws->insert_option("name",      "a", ORDER_ALPHA);
630        aws->insert_option("type",      "t", ORDER_TYPE);
631        aws->insert_option("frequency", "f", ORDER_FREQ);
632        aws->update_option_menu();
633
634        aws->at("leftright");
635        aws->callback(makeWindowCallback(reorder_right_behind_left, sel1, sel2));
636        aws->help_text(HELPFILE);
637        aws->create_autosize_button("MOVE_RIGHT_BEHIND_LEFT", "Move right\nbehind left");
638
639        aws->at("reverse");
640        aws->callback(makeWindowCallback(reverse_key_order, sel1, sel2));
641        aws->help_text(HELPFILE);
642        aws->create_autosize_button("REVERSE", "Reverse");
643       
644        aws->button_length(6);
645        struct {
646            const char *tag;
647            const char *macro;
648            int         dir;
649        } reorder[4] = {
650            { "Top",    "MOVE_TOP_RIGHT",  -2 },
651            { "Up",     "MOVE_UP_RIGHT",   -1 },
652            { "Down",   "MOVE_DOWN_RIGHT", +1 },
653            { "Bottom", "MOVE_BOT_RIGHT",  +2 },
654        };
655
656        for (int i = 0; i<4; ++i) {
657            aws->at(reorder[i].tag);
658            aws->callback(makeWindowCallback(reorder_up_down, sel2, reorder[i].dir));
659            aws->help_text(HELPFILE);
660            aws->create_button(reorder[i].macro, reorder[i].tag);
661        }
662    }
663
664    return awsa[selector.type];
665}
666
667static void hide_field_cb(AW_window *aws, Itemfield_Selection *item_sel, int hide) {
668    GBDATA   *gb_main = item_sel->get_gb_main();
669    GB_ERROR  error   = GB_begin_transaction(gb_main);
670
671    if (!error) {
672        ItemSelector&  selector  = item_sel->get_selector();
673        char          *source    = aws->get_root()->awar(itemAwar(AWAR_FIELD_DELETE_TMPL, selector))->read_string();
674        GBDATA        *gb_source = GBT_get_changekey(gb_main, source, selector.change_key_path);
675
676        if (!gb_source) error = "Please select the field you want to (un)hide";
677        else error            = GBT_write_int(gb_source, CHANGEKEY_HIDDEN, hide);
678
679        free(source);
680    }
681    GB_end_transaction_show_error(gb_main, error, aw_message);
682    if (!error) item_sel->get_sellist()->move_selection(1);
683}
684
685static void field_delete_cb(AW_window *aws, Itemfield_Selection *item_sel) {
686    GBDATA   *gb_main = item_sel->get_gb_main();
687    GB_ERROR  error   = GB_begin_transaction(gb_main);
688
689    if (!error) {
690        ItemSelector&      selector   = item_sel->get_selector();
691        char              *source     = aws->get_root()->awar(itemAwar(AWAR_FIELD_DELETE_TMPL, selector))->read_string();
692        AW_selection_list *sellist    = item_sel->get_sellist();
693        int                curr_index = sellist->get_index_of_selected();
694        GBDATA            *gb_source  = GBT_get_changekey(gb_main, source, selector.change_key_path);
695
696        if (!gb_source) error = "Please select the field you want to delete";
697        else error            = GB_delete(gb_source);
698
699        for (GBDATA *gb_item_container = selector.get_first_item_container(gb_main, aws->get_root(), QUERY_ALL_ITEMS);
700             !error && gb_item_container;
701             gb_item_container = selector.get_next_item_container(gb_item_container, QUERY_ALL_ITEMS))
702        {
703            for (GBDATA * gb_item = selector.get_first_item(gb_item_container, QUERY_ALL_ITEMS);
704                 !error && gb_item;
705                 gb_item = selector.get_next_item(gb_item, QUERY_ALL_ITEMS))
706            {
707                GBDATA *gbd = GB_search(gb_item, source, GB_FIND);
708
709                if (gbd) {
710                    error = GB_delete(gbd);
711                    if (!error) {
712                        // item has disappeared, this selects the next one:
713                        sellist->select_element_at(curr_index);
714                    }
715                }
716            }
717        }
718
719        free(source);
720    }
721
722    GB_end_transaction_show_error(gb_main, error, aw_message);
723}
724
725
726AW_window *DBUI::create_field_delete_window(AW_root *root, BoundItemSel *bound_selector) {
727    ItemSelector& selector = bound_selector->selector;
728
729    static AW_window_simple *awsa[QUERY_ITEM_TYPES];
730    if (!awsa[selector.type]) {
731        AW_window_simple *aws = new AW_window_simple;
732        awsa[selector.type]  = aws;
733
734        init_itemspecific_DBUI_awars(root, selector);
735        init_itemType_specific_window(root, aws, selector, "DELETE_FIELD", "Delete %s field");
736        aws->load_xfig("ad_delof.fig");
737        aws->button_length(6);
738
739        aws->at("close"); aws->callback(AW_POPDOWN);
740        aws->create_button("CLOSE", "Close", "C");
741
742        aws->at("help"); aws->callback(makeHelpCallback("spaf_delete.hlp"));
743        aws->create_button("HELP", "Help", "H");
744
745        Itemfield_Selection *item_sel = create_itemfield_selection_list(aws, FieldSelDef(itemAwar(AWAR_FIELD_DELETE_TMPL, selector), bound_selector->gb_main, selector, FIELD_UNFILTERED, "field", SF_HIDDEN), "source");
746
747        aws->button_length(13);
748        aws->at("hide");
749        aws->callback(makeWindowCallback(hide_field_cb, item_sel, 1));
750        aws->create_button("HIDE_FIELD", "Hide field", "H");
751
752        aws->at("unhide");
753        aws->callback(makeWindowCallback(hide_field_cb, item_sel, 0));
754        aws->create_button("UNHIDE_FIELD", "Unhide field", "U");
755
756        aws->at("delf");
757        aws->callback(makeWindowCallback(field_delete_cb, item_sel));
758        aws->create_button("DELETE_FIELD", "Delete field\n(data deleted)", "C");
759    }
760   
761    return awsa[selector.type];
762}
763
764static void field_create_cb(AW_window *aws, BoundItemSel *bound_selector) {
765    ItemSelector& selector = bound_selector->selector;
766
767    GB_push_transaction(bound_selector->gb_main);
768    char     *name   = aws->get_root()->awar(itemAwar(AWAR_FIELD_CREATE_NAME_TMPL, selector))->read_string();
769    GB_ERROR  error  = GB_check_key(name);
770    GB_ERROR  error2 = GB_check_hkey(name);
771    if (error && !error2) {
772        aw_message("Warning: Your key contain a '/' character,\n"
773                   "    that means it is a hierarchical key");
774        error = 0;
775    }
776
777    int type = (int)aws->get_root()->awar(itemAwar(AWAR_FIELD_CREATE_TYPE_TMPL, selector))->read_int();
778
779    if (!error) error = GBT_add_new_changekey_to_keypath(bound_selector->gb_main, name, type, selector.change_key_path);
780    aws->hide_or_notify(error);
781    free(name);
782    GB_pop_transaction(bound_selector->gb_main);
783}
784
785inline void insert_fieldtype_toggles(AW_window *aws) {
786    aws->insert_toggle("Ascii text",        "s", (int)GB_STRING);
787    aws->insert_toggle("Rounded numerical", "i", (int)GB_INT);
788    aws->insert_toggle("Floating-point n.", "F", (int)GB_FLOAT);
789    aws->insert_toggle("Bitmask (0/1)",     "B", (int)GB_BITS);
790    aws->insert_toggle("Link",              "L", (int)GB_LINK);
791    // keep in sync with ../ITEMS/item_sel_list.cxx@FIELD_TYPE_DESCRIPTIONS
792}
793
794AW_window *DBUI::create_field_create_window(AW_root *root, BoundItemSel *bound_selector) {
795    ItemSelector& selector = bound_selector->selector;
796
797    static AW_window_simple *awsa[QUERY_ITEM_TYPES];
798    if (awsa[selector.type]) return awsa[selector.type];
799
800    AW_window_simple *aws = new AW_window_simple;
801    awsa[selector.type]  = aws;
802
803    init_itemspecific_DBUI_awars(root, selector);
804    init_itemType_specific_window(root, aws, selector, "CREATE_FIELD", "Create new %s field");
805    aws->load_xfig("ad_fcrea.fig");
806
807    aws->at("close");
808    aws->callback(AW_POPDOWN);
809    aws->create_button("CLOSE", "Close", "C");
810
811    aws->at("input");
812    aws->label("FIELD NAME");
813    aws->create_input_field(itemAwar(AWAR_FIELD_CREATE_NAME_TMPL, selector), 15);
814
815    aws->at("type");
816    aws->create_toggle_field(itemAwar(AWAR_FIELD_CREATE_TYPE_TMPL, selector), "FIELD TYPE", "F");
817    insert_fieldtype_toggles(aws);
818    aws->update_toggle_field();
819
820    aws->at("ok");
821    aws->callback(makeWindowCallback(field_create_cb, bound_selector));
822    aws->create_button("CREATE", "Create", "C");
823
824    return aws;
825}
826
827#if defined(WARN_TODO)
828#warning GBT_convert_changekey currently only works for species fields, make it work with genes/exp/... as well (use selector)
829#endif
830
831static void field_convert_commit_cb(AW_window *aws, BoundItemSel *bound_selector) {
832    AW_root *root    = aws->get_root();
833    GBDATA  *gb_main = bound_selector->gb_main;
834
835    GB_push_transaction(gb_main);
836    ItemSelector& selector = bound_selector->selector;
837    GB_ERROR error = GBT_convert_changekey(gb_main,
838                                           root->awar(itemAwar(AWAR_FIELD_CONVERT_SOURCE_TMPL, selector))->read_char_pntr(),
839                                           (GB_TYPES)root->awar(itemAwar(AWAR_FIELD_CONVERT_TYPE_TMPL, selector))->read_int());
840
841    GB_end_transaction_show_error(gb_main, error, aw_message);
842}
843
844static void field_convert_update_typesel_cb(AW_root *root, BoundItemSel *bound_selector) {
845    ItemSelector& selector = bound_selector->selector;
846    int type;
847    {
848        GBDATA         *gb_main = bound_selector->gb_main;
849        GB_transaction  ta(gb_main);
850
851        type = GBT_get_type_of_changekey(gb_main,
852                                         root->awar(itemAwar(AWAR_FIELD_CONVERT_SOURCE_TMPL, selector))->read_char_pntr(),
853                                         selector.change_key_path);
854    }
855
856    root->awar(itemAwar(AWAR_FIELD_CONVERT_TYPE_TMPL, selector))->write_int(type);
857}
858
859static AW_window *create_field_convert_window(AW_root *root, BoundItemSel *bound_selector) {
860    ItemSelector& selector = bound_selector->selector;
861
862    static AW_window_simple *awsa[QUERY_ITEM_TYPES];
863    if (awsa[selector.type]) return awsa[selector.type];
864
865    AW_window_simple *aws = new AW_window_simple;
866    awsa[selector.type]  = aws;
867
868    init_itemspecific_DBUI_awars(root, selector);
869    init_itemType_specific_window(root, aws, selector, "CONVERT_FIELD", "Convert %s field");
870    aws->load_xfig("ad_conv.fig");
871
872    aws->at("close");
873    aws->callback(AW_POPDOWN);
874    aws->create_button("CLOSE", "Close", "C");
875
876    aws->at("help");
877    aws->callback(makeHelpCallback("spaf_convert.hlp"));
878    aws->create_button("HELP", "Help", "H");
879
880    const char *awarname_field = itemAwar(AWAR_FIELD_CONVERT_SOURCE_TMPL, selector);
881    root->awar(awarname_field)->add_callback(makeRootCallback(field_convert_update_typesel_cb, bound_selector));
882    create_itemfield_selection_list(aws, FieldSelDef(awarname_field, bound_selector->gb_main, selector, FIELD_FILTER_STRING_READABLE, "field", SF_SHOW_TYPE), "source");
883
884    aws->at("typesel");
885    aws->create_toggle_field(itemAwar(AWAR_FIELD_CONVERT_TYPE_TMPL, selector), NULL, "F");
886    insert_fieldtype_toggles(aws);
887    aws->update_toggle_field();
888
889    aws->at("convert");
890    aws->callback(makeWindowCallback(field_convert_commit_cb, bound_selector));
891    aws->create_button("CONVERT", "Convert", "T");
892
893    return aws;
894}
895
896void DBUI::insert_field_admin_menuitems(AW_window *aws, GBDATA *gb_main) {
897    static BoundItemSel *bis = new BoundItemSel(gb_main, SPECIES_get_selector());
898    ui_assert(bis->gb_main == gb_main);
899   
900    aws->insert_menu_topic(aws->local_id("spec_reorder_fields"), "Reorder fields ...",     "R", "spaf_reorder.hlp", AWM_ALL, makeCreateWindowCallback(create_fields_reorder_window, bis));
901    aws->insert_menu_topic(aws->local_id("spec_delete_field"),   "Delete/Hide fields ...", "D", "spaf_delete.hlp",  AWM_EXP, makeCreateWindowCallback(create_field_delete_window,   bis));
902    aws->insert_menu_topic(aws->local_id("spec_create_field"),   "Create fields ...",      "C", "spaf_create.hlp",  AWM_ALL, makeCreateWindowCallback(create_field_create_window,   bis));
903    aws->insert_menu_topic(aws->local_id("spec_convert_field"),  "Convert fields ...",     "t", "spaf_convert.hlp", AWM_EXP, makeCreateWindowCallback(create_field_convert_window,  bis));
904    aws->sep______________();
905    aws->insert_menu_topic("spec_unhide_fields",  "Show all hidden fields", "S", "scandb.hlp", AWM_ALL, makeWindowCallback(species_field_selection_list_unhide_all_cb, gb_main));
906    aws->insert_menu_topic("spec_refresh_fields", "Refresh fields",         "f", "scandb.hlp", AWM_ALL, makeWindowCallback(species_field_selection_list_update_cb,     gb_main));
907}
908
909inline int get_and_fix_range_from_awar(AW_awar *awar) {
910    const char *input = awar->read_char_pntr();
911    int         bpos = atoi(input);
912    int         ipos;
913
914    if (bpos>0) {
915        awar->write_string(GBS_global_string("%i", bpos));
916        ipos = bio2info(bpos);
917    }
918    else {
919        ipos = -1;
920        awar->write_string("");
921    }
922    return ipos;
923}
924
925class NN_GlobalData {
926    DbQuery           *query;
927    AW_selection_list *resultList;     // result list from create_next_neighbours_selected_window()
928
929public:
930    NN_GlobalData() : query(0), resultList(0) {}
931
932    void set_query(DbQuery *new_query) {
933        if (new_query != query) {
934            ui_assert(!query); // need redesign b4 changing query works
935            query = new_query;
936        }
937    }
938    void set_result_list(AW_selection_list *new_resultList) {
939        if (new_resultList != resultList) {
940            ui_assert(!resultList); // need redesign b4 changing query works
941            resultList = new_resultList;
942        }
943    }
944
945    DbQuery *get_query() const { ui_assert(query); return query; }
946
947    AW_selection_list *get_result_list() const { ui_assert(resultList); return resultList; }
948    GBDATA *get_gb_main() const { return query_get_gb_main(get_query()); }
949};
950static NN_GlobalData NN_GLOBAL;
951
952static PosRange get_nn_range_from_awars(AW_root *aw_root) {
953    int start = get_and_fix_range_from_awar(aw_root->awar(AWAR_NN_RANGE_START));
954    int end   = get_and_fix_range_from_awar(aw_root->awar(AWAR_NN_RANGE_END));
955
956    return PosRange(start, end);
957}
958
959inline char *read_sequence_region(GBDATA *gb_data, const PosRange& range) {
960    return range.dup_corresponding_part(GB_read_char_pntr(gb_data), GB_read_count(gb_data));
961}
962
963static void awtc_nn_search_all_listed(AW_window *aww) {
964    DbQuery *query   = NN_GLOBAL.get_query();
965    GBDATA  *gb_main = query_get_gb_main(query);
966
967    ui_assert(get_queried_itemtype(query).type == QUERY_ITEM_SPECIES);
968
969    GB_begin_transaction(gb_main);
970
971    long        queriedCount = count_queried_items(query, QUERY_ALL_ITEMS);
972    GB_ERROR    error        = queriedCount ? NULL : "No species listed in query";
973    const char *dest_field   = NULL;
974    AW_root    *aw_root      = aww->get_root();
975
976    if (!error) {
977        dest_field = prepare_and_get_selected_itemfield(aw_root, AWAR_NN_LISTED_DEST_FIELD, gb_main, get_queried_itemtype(query));
978        error = dest_field ? NULL : GB_await_error();
979    }
980
981    if (!error) {
982        arb_progress progress("Searching next neighbours", queriedCount);
983        progress.auto_subtitles("Species");
984
985        int    pts            = aw_root->awar(AWAR_PROBE_ADMIN_PT_SERVER)->read_int();
986        char  *ali_name       = aw_root->awar(AWAR_DEFAULT_ALIGNMENT)->read_string();
987        int    oligo_len      = aw_root->awar(AWAR_NN_OLIGO_LEN)->read_int();
988        int    mismatches     = aw_root->awar(AWAR_NN_MISMATCHES)->read_int();
989        bool   fast_mode      = aw_root->awar(AWAR_NN_FAST_MODE)->read_int();
990        bool   rel_matches    = aw_root->awar(AWAR_NN_REL_MATCHES)->read_int();
991        int    wanted_entries = aw_root->awar(AWAR_NN_MAX_HITS)->read_int();
992        bool   scored_entries = aw_root->awar(AWAR_NN_LISTED_SCORED_ENTRIES)->read_int();
993        float  min_score      = aw_root->awar(AWAR_NN_MIN_SCORE)->read_float();
994
995        FF_complement        compl_mode  = static_cast<FF_complement>(aw_root->awar(AWAR_NN_COMPLEMENT)->read_int());
996        RelativeScoreScaling rel_scaling = static_cast<RelativeScoreScaling>(aw_root->awar(AWAR_NN_REL_SCALING)->read_int());
997
998        PosRange org_range = get_nn_range_from_awars(aw_root);
999
1000        for (GBDATA *gb_species = GBT_first_species(gb_main);
1001             !error && gb_species;
1002             gb_species = GBT_next_species(gb_species))
1003        {
1004            if (!IS_QUERIED(gb_species, query)) continue;
1005            GBDATA *gb_data = GBT_find_sequence(gb_species, ali_name);
1006            if (gb_data) {
1007                PosRange         range    = org_range; // modified by read_sequence_region
1008                char            *sequence = read_sequence_region(gb_data, range);
1009                PT_FamilyFinder  ff(gb_main, pts, oligo_len, mismatches, fast_mode, rel_matches, rel_scaling);
1010
1011                ff.restrict_2_region(range);
1012
1013                error = ff.searchFamily(sequence, compl_mode, wanted_entries, min_score); 
1014                if (!error) {
1015                    const FamilyList *fm = ff.getFamilyList();
1016
1017                    GBS_strstruct *value = NULL;
1018                    while (fm) {
1019                        const char *thisValue = 0;
1020                        if (rel_matches) {
1021                            ui_assert((fm->rel_matches*100) >= min_score); // filtered by ptserver
1022                            thisValue = scored_entries
1023                                ? GBS_global_string("%.1f%%:%s", fm->rel_matches*100, fm->name)
1024                                : fm->name;
1025                        }
1026                        else {
1027                            ui_assert(fm->matches >= min_score); // filtered by ptserver
1028                            thisValue = scored_entries
1029                                ? GBS_global_string("%li:%s", fm->matches, fm->name)
1030                                : fm->name;
1031                        }
1032
1033                        if (thisValue) {
1034                            if (value == NULL) { // first entry
1035                                value = GBS_stropen(1000);
1036                            }
1037                            else {
1038                                GBS_chrcat(value, ';');
1039                            }
1040                            GBS_strcat(value, thisValue);
1041                        }
1042
1043                        fm = fm->next;
1044                    }
1045
1046                    if (value) {
1047                        ui_assert(GBT_get_type_of_changekey(gb_main, dest_field, CHANGE_KEY_PATH) == GB_STRING);
1048                        GBDATA *gb_dest = GB_search(gb_species, dest_field, GB_STRING);
1049                        error = GB_write_string(gb_dest, GBS_mempntr(value));
1050                        GBS_strforget(value);
1051                    }
1052                    else {
1053                        GBDATA *gb_dest = GB_search(gb_species, dest_field, GB_FIND);
1054                        if (gb_dest) error = GB_delete(gb_dest);
1055                    }
1056                }
1057                free(sequence);
1058            }
1059            progress.inc_and_check_user_abort(error);
1060        }
1061        free(ali_name);
1062    }
1063    GB_end_transaction_show_error(gb_main, error, aw_message);
1064}
1065
1066static void awtc_mark_hits(AW_window *) {
1067    AW_selection_list *resultList = NN_GLOBAL.get_result_list();
1068    GB_HASH           *list_hash  = resultList->to_hash(false);
1069    GBDATA            *gb_main    = NN_GLOBAL.get_gb_main();
1070
1071    GB_transaction ta(gb_main);
1072    for (GBDATA *gb_species = GBT_first_species(gb_main);
1073         gb_species;
1074         gb_species = GBT_next_species(gb_species))
1075    {
1076        int hit = GBS_read_hash(list_hash, GBT_get_name(gb_species));
1077        GB_write_flag(gb_species, hit);
1078    }
1079}
1080
1081static void awtc_nn_search(AW_window *) {
1082    AW_root  *aw_root  = AW_root::SINGLETON;
1083    GBDATA   *gb_main  = NN_GLOBAL.get_gb_main();
1084    GB_ERROR  error    = 0;
1085    PosRange  range    = get_nn_range_from_awars(aw_root);
1086    char     *sequence = 0;
1087    {
1088        GB_transaction  ta(gb_main);
1089
1090        char   *sel_species = aw_root->awar(AWAR_SPECIES_NAME)->read_string();
1091        GBDATA *gb_species  = GBT_find_species(gb_main, sel_species);
1092
1093        if (!gb_species) {
1094            error = "Select a species first";
1095        }
1096        else {
1097            char   *ali_name = aw_root->awar(AWAR_DEFAULT_ALIGNMENT)->read_string();
1098            GBDATA *gb_data  = GBT_find_sequence(gb_species, ali_name);
1099
1100            if (gb_data) {
1101                sequence = read_sequence_region(gb_data, range);
1102            }
1103            else {
1104                error = GBS_global_string("Species '%s' has no sequence '%s'", sel_species, ali_name);
1105            }
1106            free(ali_name);
1107        }
1108        free(sel_species);
1109    }
1110
1111    int   pts         = aw_root->awar(AWAR_PROBE_ADMIN_PT_SERVER)->read_int();
1112    int   oligo_len   = aw_root->awar(AWAR_NN_OLIGO_LEN)->read_int();
1113    int   mismatches  = aw_root->awar(AWAR_NN_MISMATCHES)->read_int();
1114    bool  fast_mode   = aw_root->awar(AWAR_NN_FAST_MODE)->read_int();
1115    bool  rel_matches = aw_root->awar(AWAR_NN_REL_MATCHES)->read_int();
1116    float min_score   = aw_root->awar(AWAR_NN_MIN_SCORE)->read_float();
1117
1118    RelativeScoreScaling rel_scaling = static_cast<RelativeScoreScaling>(aw_root->awar(AWAR_NN_REL_SCALING)->read_int());
1119
1120    PT_FamilyFinder ff(gb_main, pts, oligo_len, mismatches, fast_mode, rel_matches, rel_scaling);
1121
1122    ff.restrict_2_region(range);
1123
1124    int max_hits = 0; // max wanted hits
1125
1126    if (!error) {
1127        FF_complement compl_mode = static_cast<FF_complement>(aw_root->awar(AWAR_NN_COMPLEMENT)->read_int());
1128        max_hits                 = aw_root->awar(AWAR_NN_MAX_HITS)->read_int();
1129
1130        error = ff.searchFamily(sequence, compl_mode, max_hits, min_score);
1131    }
1132
1133    // update result list
1134    {
1135        AW_selection_list* sel = NN_GLOBAL.get_result_list();
1136        sel->clear();
1137
1138        int hits = 0;
1139        if (error) {
1140            aw_message(error);
1141            sel->insert_default("<Error>", "");
1142        }
1143        else {
1144            int count     = 1;
1145            int shownHits = max_hits>0 ? max_hits : ff.getRealHits();
1146            int numWidth  = log(shownHits)/log(10)+1;
1147
1148            for (const FamilyList *fm = ff.getFamilyList(); fm; fm = fm->next) {
1149                const char *dis;
1150                if (rel_matches) {
1151                    dis = GBS_global_string("#%0*i %-12s Rel.hits: %5.1f%%", numWidth, count, fm->name, fm->rel_matches*100);
1152                }
1153                else {
1154                    dis = GBS_global_string("#%0*i %-12s Hits: %4li", numWidth, count, fm->name, fm->matches);
1155                }
1156
1157                sel->insert(dis, fm->name);
1158                count++;
1159            }
1160
1161            sel->insert_default(ff.hits_were_truncated() ? "<List truncated>" : "<No more hits>", "");
1162            hits = ff.getRealHits();
1163        }
1164        aw_root->awar(AWAR_NN_SELECTED_HIT_COUNT)->write_int(hits);
1165        if (aw_root->awar(AWAR_NN_SELECTED_AUTO_MARK)->read_int()) {
1166            awtc_mark_hits(NULL);
1167            aw_root->awar(AWAR_TREE_REFRESH)->touch();
1168        }
1169        sel->update();
1170    }
1171
1172    free(sequence);
1173}
1174
1175static void awtc_move_hits(AW_window *aww) {
1176    AW_root *aw_root         = aww->get_root();
1177    char    *current_species = aw_root->awar(AWAR_SPECIES_NAME)->read_string();
1178
1179    if (!current_species) current_species = strdup("<unknown>");
1180
1181    char *hit_description = GBS_global_string_copy("<neighbour of %s: %%s>", current_species);
1182
1183    copy_selection_list_2_query_box(NN_GLOBAL.get_query(), NN_GLOBAL.get_result_list(), hit_description);
1184
1185    free(hit_description);
1186    free(current_species);
1187}
1188
1189static bool autosearch_triggered = false;
1190static unsigned nn_perform_delayed_autosearch_cb(AW_root*) {
1191    awtc_nn_search(NULL);
1192    autosearch_triggered = false;
1193    return 0;
1194}
1195static void nn_trigger_delayed_autosearch_cb(AW_root *awr) {
1196    // automatic search is triggered delayed to make sure
1197    // dependencies between involved awars have propagated.
1198    if (!autosearch_triggered) { // ignore multiple triggers (happens when multiple awars change, e.g. when loading config)
1199        autosearch_triggered = true;
1200        awr->add_timed_callback(200, makeTimedCallback(nn_perform_delayed_autosearch_cb));
1201    }
1202}
1203
1204static void nn_auto_search_changed_cb(AW_root *awr) {
1205    int auto_search = awr->awar(AWAR_NN_SELECTED_AUTO_SEARCH)->read_int();
1206
1207    AW_awar *awar_sel_species = awr->awar(AWAR_SPECIES_NAME);
1208    if (auto_search) {
1209        awar_sel_species->add_callback(nn_trigger_delayed_autosearch_cb);
1210        nn_trigger_delayed_autosearch_cb(awr);
1211    }
1212    else {
1213        awar_sel_species->remove_callback(nn_trigger_delayed_autosearch_cb);
1214    }
1215}
1216
1217static AW_window *nn_of_sel_win = NULL;
1218static void nn_searchRel_awar_changed_cb(AW_root *awr) {
1219    int auto_search = awr->awar(AWAR_NN_SELECTED_AUTO_SEARCH)->read_int();
1220    if (auto_search &&
1221        nn_of_sel_win && nn_of_sel_win->is_shown()) // do not trigger if window is not shown
1222    {
1223        nn_trigger_delayed_autosearch_cb(awr);
1224    }
1225}
1226
1227static void create_next_neighbours_vars(AW_root *aw_root) {
1228    static bool created = false;
1229
1230    if (!created) {
1231        RootCallback searchRel_awar_changed_cb = makeRootCallback(nn_searchRel_awar_changed_cb);
1232
1233        aw_root->awar_int(AWAR_PROBE_ADMIN_PT_SERVER)->add_callback(searchRel_awar_changed_cb);
1234        aw_root->awar_int(AWAR_NN_COMPLEMENT,  FF_FORWARD)->add_callback(searchRel_awar_changed_cb);
1235
1236        aw_root->awar_string(AWAR_NN_RANGE_START, "")->add_callback(searchRel_awar_changed_cb);
1237        aw_root->awar_string(AWAR_NN_RANGE_END,   "")->add_callback(searchRel_awar_changed_cb);
1238        aw_root->awar_int   (AWAR_NN_MAX_HITS,    10)->set_minmax(0, 1000)->add_callback(searchRel_awar_changed_cb);;
1239        aw_root->awar_float (AWAR_NN_MIN_SCORE,   80)->set_minmax(0, 200)->add_callback(searchRel_awar_changed_cb);;
1240       
1241        aw_root->awar_int(AWAR_NN_SELECTED_HIT_COUNT,   0);
1242        aw_root->awar_int(AWAR_NN_SELECTED_AUTO_SEARCH, 0)->add_callback(nn_auto_search_changed_cb);
1243        aw_root->awar_int(AWAR_NN_SELECTED_AUTO_MARK,   0);
1244
1245        aw_root->awar_string(AWAR_NN_LISTED_DEST_FIELD,     "tmp");
1246        aw_root->awar_int   (AWAR_NN_LISTED_SCORED_ENTRIES, 1);
1247
1248        AWTC_create_common_next_neighbour_vars(aw_root, searchRel_awar_changed_cb);
1249
1250        created = true;
1251    }
1252}
1253
1254static AWT_config_mapping_def next_neighbour_config_mapping[] = {
1255    // same as ../FAST_ALIGNER/fast_aligner.cxx@RELATIVES_CONFIG
1256    { AWAR_NN_OLIGO_LEN,   "oligolen" },
1257    { AWAR_NN_MISMATCHES,  "mismatches" },
1258    { AWAR_NN_FAST_MODE,   "fastmode" },
1259    { AWAR_NN_REL_MATCHES, "relmatches" },
1260    { AWAR_NN_REL_SCALING, "relscaling" },
1261
1262    { AWAR_NN_COMPLEMENT,  "complement" },
1263    { AWAR_NN_RANGE_START, "rangestart" },
1264    { AWAR_NN_RANGE_END,   "rangeend" },
1265    { AWAR_NN_MAX_HITS,    "maxhits" },
1266    { AWAR_NN_MIN_SCORE,   "minscore" },
1267
1268    { 0, 0}
1269};
1270
1271static void setup_next_neighbour_config(AWT_config_definition& cdef, bool for_listed) {
1272    // fields common for 'listed' and 'selected'
1273    cdef.add(next_neighbour_config_mapping);
1274
1275    if (for_listed) {
1276        cdef.add(AWAR_NN_LISTED_SCORED_ENTRIES, "addscore");
1277    }
1278    else {
1279        cdef.add(AWAR_NN_SELECTED_AUTO_SEARCH, "autosearch");
1280        cdef.add(AWAR_NN_SELECTED_AUTO_MARK,   "automark");
1281    }
1282}
1283
1284static void create_common_next_neighbour_fields(AW_window *aws, bool for_listed) {
1285    aws->at("pt_server");
1286    awt_create_PTSERVER_selection_button(aws, AWAR_PROBE_ADMIN_PT_SERVER);
1287
1288    const int SCALER_LENGTH = 200;
1289
1290    aws->auto_space(5, 5);
1291    AWTC_create_common_next_neighbour_fields(aws, SCALER_LENGTH);
1292
1293   
1294    aws->at("range");
1295    aws->create_input_field(AWAR_NN_RANGE_START, 6);
1296    aws->create_input_field(AWAR_NN_RANGE_END,   6);
1297
1298    aws->at("compl");
1299    aws->create_option_menu(AWAR_NN_COMPLEMENT, true);
1300    aws->insert_default_option("forward",            "", FF_FORWARD);
1301    aws->insert_option        ("reverse",            "", FF_REVERSE);
1302    aws->insert_option        ("complement",         "", FF_COMPLEMENT);
1303    aws->insert_option        ("reverse-complement", "", FF_REVERSE_COMPLEMENT);
1304    aws->insert_option        ("fwd + rev-compl",    "", FF_FORWARD|FF_REVERSE_COMPLEMENT);
1305    aws->insert_option        ("rev + compl",        "", FF_REVERSE|FF_COMPLEMENT);
1306    aws->insert_option        ("any",                "", FF_FORWARD|FF_REVERSE|FF_COMPLEMENT|FF_REVERSE_COMPLEMENT);
1307    aws->update_option_menu();
1308
1309    aws->at("results");
1310    aws->create_input_field_with_scaler(AWAR_NN_MAX_HITS, 5, SCALER_LENGTH, AW_SCALER_EXP_LOWER);
1311
1312    aws->at("min_score");
1313    aws->create_input_field_with_scaler(AWAR_NN_MIN_SCORE, 5, SCALER_LENGTH, AW_SCALER_LINEAR);
1314
1315    aws->at("config");
1316    AWT_insert_config_manager(aws, AW_ROOT_DEFAULT, "next_neighbours", makeConfigSetupCallback(setup_next_neighbour_config, for_listed));
1317}
1318
1319static AW_window *create_next_neighbours_listed_window(AW_root *aw_root, DbQuery *query) {
1320    static AW_window_simple *aws = 0;
1321    NN_GLOBAL.set_query(query);
1322    if (!aws) {
1323        create_next_neighbours_vars(aw_root);
1324
1325        aws = new AW_window_simple;
1326        aws->init(aw_root, "SEARCH_NEXT_NEIGHBOURS_OF_LISTED", "Search Next Neighbours of Listed");
1327        aws->load_xfig("ad_spec_nnm.fig");
1328
1329        aws->at("close");
1330        aws->callback(AW_POPDOWN);
1331        aws->create_button("CLOSE", "Close", "C");
1332
1333        aws->at("help");
1334        aws->callback(makeHelpCallback("next_neighbours_listed.hlp"));
1335        aws->create_button("HELP", "Help", "H");
1336
1337        create_common_next_neighbour_fields(aws, true);
1338
1339        aws->at("add_score");
1340        aws->create_toggle(AWAR_NN_LISTED_SCORED_ENTRIES);
1341       
1342        aws->at("field");
1343        create_itemfield_selection_button(aws, FieldSelDef(AWAR_NN_LISTED_DEST_FIELD, query_get_gb_main(query), SPECIES_get_selector(), FIELD_FILTER_STRING_WRITEABLE, "target field", SF_ALLOW_NEW), "field");
1344
1345        aws->at("go");
1346        aws->callback(awtc_nn_search_all_listed);
1347        aws->button_length(10);
1348        aws->create_button("WRITE_FIELDS", "GO");
1349    }
1350    return aws;
1351}
1352
1353static AW_window *create_next_neighbours_selected_window(AW_root *aw_root, DbQuery *query) {
1354    static AW_window_simple *aws = 0;
1355    NN_GLOBAL.set_query(query);
1356    if (!aws) {
1357        create_next_neighbours_vars(aw_root);
1358
1359        aws = new AW_window_simple;
1360        aws->init(aw_root, "SEARCH_NEXT_RELATIVE_OF_SELECTED", "Search Next Neighbours of Selected");
1361        aws->load_xfig("ad_spec_nn.fig");
1362
1363        aws->at("close");
1364        aws->callback(AW_POPDOWN);
1365        aws->create_button("CLOSE", "Close", "C");
1366
1367        aws->at("help");
1368        aws->callback(makeHelpCallback("next_neighbours.hlp"));
1369        aws->create_button("HELP", "Help", "H");
1370
1371        create_common_next_neighbour_fields(aws, false);
1372
1373        aws->button_length(10);
1374        aws->at("hit_count");
1375        aws->create_button(0, AWAR_NN_SELECTED_HIT_COUNT, 0, "+");
1376
1377        aws->at("hits");
1378        AW_selection_list *resultList = aws->create_selection_list(AWAR_SPECIES_NAME, false);
1379        NN_GLOBAL.set_result_list(resultList);
1380        resultList->insert_default("No hits found", "");
1381        resultList->update();
1382
1383        aws->at("go");
1384        aws->callback(awtc_nn_search);
1385        aws->create_button("SEARCH", "Search");
1386
1387        aws->at("auto_go");
1388        aws->label("Auto search on change");
1389        aws->create_toggle(AWAR_NN_SELECTED_AUTO_SEARCH);
1390       
1391        aws->at("mark");
1392        aws->callback(awtc_mark_hits);
1393        aws->create_autosize_button("MARK_HITS", "Mark hits");
1394
1395        aws->at("auto_mark");
1396        aws->label("Auto");
1397        aws->create_toggle(AWAR_NN_SELECTED_AUTO_MARK);
1398
1399        aws->at("move");
1400        aws->callback(awtc_move_hits);
1401        aws->create_autosize_button("MOVE_TO_HITLIST", "Move to hitlist");
1402
1403        nn_of_sel_win = aws; // store current window (to disable auto search when this window was popped down)
1404    }
1405    return aws;
1406}
1407
1408// ---------------------------------------------
1409//      species/organism specific callbacks
1410
1411static AW_window *popup_new_speciesOrganismWindow(AW_root *aw_root, GBDATA *gb_main, bool organismWindow, int detach_id);
1412
1413static void popup_detached_speciesOrganismWindow(AW_window *aw_parent, const InfoWindow *infoWin) {
1414    const InfoWindow *reusable = InfoWindowRegistry::infowin.find_reusable_of_same_type_as(*infoWin);
1415    if (reusable) reusable->reuse();
1416    else { // create a new window if none is reusable
1417        popup_new_speciesOrganismWindow(aw_parent->get_root(),
1418                                        infoWin->get_gbmain(),
1419                                        infoWin->mapsOrganism(),
1420                                        InfoWindowRegistry::infowin.allocate_detach_id(*infoWin));
1421    }
1422}
1423
1424static AW_window *popup_new_speciesOrganismWindow(AW_root *aw_root, GBDATA *gb_main, bool organismWindow, int detach_id) { // INFO_WINDOW_CREATOR
1425    // if detach_id is MAIN_WINDOW -> create main window (not detached)
1426
1427    AW_window_simple_menu *aws      = new AW_window_simple_menu;
1428    const ItemSelector&    itemType = organismWindow ? ORGANISM_get_selector() : SPECIES_get_selector();
1429
1430    init_info_window(aw_root, aws, itemType, detach_id);
1431
1432    aws->load_xfig("ad_spec.fig");
1433
1434    aws->button_length(8);
1435
1436    aws->at("close");
1437    aws->callback(AW_POPDOWN);
1438    aws->create_button("CLOSE", "Close", "C");
1439
1440    aws->at("search");
1441    aws->callback(makeCreateWindowCallback(DBUI::create_species_query_window, gb_main));
1442    aws->create_autosize_button("SEARCH", "Search...", "S");
1443
1444    aws->at("help");
1445    aws->callback(makeHelpCallback(detach_id ? "sp_info_locked.hlp" : "sp_info.hlp")); // uses_hlp_res("sp_info_locked.hlp", "sp_info.hlp"); see ../../SOURCE_TOOLS/check_resources.pl@uses_hlp_res
1446    aws->create_button("HELP", "Help", "H");
1447
1448    DbScanner         *scanner = create_db_scanner(gb_main, aws, "box", 0, "field", "enable", DB_VIEWER, "mark", itemType);
1449    const InfoWindow&  infoWin = InfoWindowRegistry::infowin.registerInfoWindow(aws, scanner, detach_id);
1450
1451    if (infoWin.is_maininfo()) {
1452        if (organismWindow) aws->create_menu("ORGANISM",    "O", AWM_ALL);
1453        else                aws->create_menu("SPECIES",     "S", AWM_ALL);
1454
1455        aws->insert_menu_topic("species_delete",                "Delete", "D", "spa_delete.hlp", AWM_ALL, makeWindowCallback      (species_delete_cb,            gb_main));
1456        aws->insert_menu_topic("species_rename",                "Rename", "R", "spa_rename.hlp", AWM_ALL, makeWindowCallback      (species_rename_cb,            gb_main));
1457        aws->insert_menu_topic("species_copy",                  "Copy",   "y", "spa_copy.hlp",   AWM_ALL, makeWindowCallback      (species_copy_cb,              gb_main));
1458        aws->insert_menu_topic(aws->local_id("species_create"), "Create", "C", "spa_create.hlp", AWM_ALL, makeCreateWindowCallback(create_species_create_window, gb_main));
1459        aws->sep______________();
1460        aws->insert_menu_topic("species_convert_2_sai", "Convert to SAI", "S", "sp_sp_2_ext.hlp", AWM_ALL, makeWindowCallback      (move_species_to_extended,     gb_main));
1461    }
1462
1463    aws->create_menu("FIELDS", "F", AWM_ALL);
1464    insert_field_admin_menuitems(aws, gb_main);
1465
1466    aws->at("detach");
1467    infoWin.add_detachOrGet_button(popup_detached_speciesOrganismWindow);
1468
1469    aws->show();
1470    infoWin.attach_selected_item();
1471    return aws;
1472}
1473
1474static void popup_speciesOrganismWindow(AW_root *aw_root, GBDATA *gb_main, bool organismWindow) {
1475    int windowIdx = (int)organismWindow;
1476
1477    static AW_window *AWS[2] = { 0, 0 };
1478    if (!AWS[windowIdx]) {
1479        AWS[windowIdx] = popup_new_speciesOrganismWindow(aw_root, gb_main, organismWindow, InfoWindow::MAIN_WINDOW);
1480    }
1481    else {
1482        AWS[windowIdx]->activate();
1483    }
1484}
1485
1486void DBUI::popup_species_info_window (AW_root *aw_root, GBDATA *gb_main) { popup_speciesOrganismWindow(aw_root, gb_main, false); }
1487void DBUI::popup_organism_info_window(AW_root *aw_root, GBDATA *gb_main) { popup_speciesOrganismWindow(aw_root, gb_main, true);  }
1488
1489static DbQuery *GLOBAL_species_query = NULL; // @@@ fix design
1490
1491void DBUI::unquery_all() {
1492    QUERY::unquery_all(0, GLOBAL_species_query);
1493}
1494
1495void DBUI::query_update_list() {
1496    DbQuery_update_list(GLOBAL_species_query);
1497}
1498
1499AW_window *DBUI::create_species_query_window(AW_root *aw_root, GBDATA *gb_main) {
1500    static AW_window_simple_menu *aws = 0;
1501    if (!aws) {
1502        aws = new AW_window_simple_menu;
1503        aws->init(aw_root, "SPECIES_QUERY", "SEARCH and QUERY");
1504        aws->create_menu("More functions", "f");
1505        aws->load_xfig("ad_query.fig");
1506
1507
1508        query_spec awtqs(SPECIES_get_selector());
1509
1510        awtqs.gb_main             = gb_main;
1511        awtqs.species_name        = AWAR_SPECIES_NAME;
1512        awtqs.tree_name           = AWAR_TREE;
1513        awtqs.select_bit          = GB_USERFLAG_QUERY;
1514        awtqs.use_menu            = 1;
1515        awtqs.ere_pos_fig         = "ere2";
1516        awtqs.by_pos_fig          = "by2";
1517        awtqs.qbox_pos_fig        = "qbox";
1518        awtqs.key_pos_fig         = 0;
1519        awtqs.query_pos_fig       = "content";
1520        awtqs.result_pos_fig      = "result";
1521        awtqs.count_pos_fig       = "count";
1522        awtqs.do_query_pos_fig    = "doquery";
1523        awtqs.config_pos_fig      = "doconfig";
1524        awtqs.do_mark_pos_fig     = "domark";
1525        awtqs.do_unmark_pos_fig   = "dounmark";
1526        awtqs.do_delete_pos_fig   = "dodelete";
1527        awtqs.do_set_pos_fig      = "doset";
1528        awtqs.do_refresh_pos_fig  = "dorefresh";
1529        awtqs.open_parser_pos_fig = "openparser";
1530        awtqs.popup_info_window   = popup_species_info_window;
1531
1532        DbQuery *query           = create_query_box(aws, &awtqs, "spec");
1533        GLOBAL_species_query = query;
1534
1535        aws->create_menu("More search",     "s");
1536        aws->insert_menu_topic("spec_search_equal_fields_within_db", "Search For Equal Fields and Mark Duplicates",                "E", "search_duplicates.hlp",      AWM_ALL, makeWindowCallback      (search_duplicated_field_content,        query, false));
1537        aws->insert_menu_topic("spec_search_equal_words_within_db",  "Search For Equal Words Between Fields and Mark Duplicates",  "W", "search_duplicates.hlp",      AWM_ALL, makeWindowCallback      (search_duplicated_field_content,        query, true));
1538        aws->insert_menu_topic("spec_search_next_relativ_of_sel",    "Search Next Relatives of SELECTED Species in PT_Server ...", "R", "next_neighbours.hlp",        AWM_ALL, makeCreateWindowCallback(create_next_neighbours_selected_window, query));
1539        aws->insert_menu_topic("spec_search_next_relativ_of_listed", "Search Next Relatives of LISTED Species in PT_Server ...",   "L", "next_neighbours_listed.hlp", AWM_ALL, makeCreateWindowCallback(create_next_neighbours_listed_window,   query));
1540
1541        aws->button_length(7);
1542
1543        aws->at("close");
1544        aws->callback(AW_POPDOWN);
1545        aws->create_button("CLOSE", "Close", "C");
1546
1547        aws->at("help");
1548        aws->callback(makeHelpCallback("sp_search.hlp"));
1549        aws->create_button("HELP", "Help", "H");
1550    }
1551    return aws;
1552}
1553
1554
Note: See TracBrowser for help on using the repository browser.