source: trunk/SL/DB_UI/ui_species.cxx

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