source: branches/species/SL/DB_QUERY/db_query.cxx

Last change on this file was 19691, checked in by westram, 2 weeks ago
  • reintegrates 'macros' into 'trunk'
    • fixes macro IDs for mergetool (completing #870).
      • most common problem:
        • several modules were reused (twice) for items of same type, but in different databases.
        • auto-generated IDs were identical.
  • adds: log:branches/macros@19667:19690
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 106.8 KB
Line 
1// ============================================================ //
2//                                                              //
3//   File      : db_query.cxx                                   //
4//   Purpose   : Database queries                               //
5//                                                              //
6//   Institute of Microbiology (Technical University Munich)    //
7//   http://www.arb-home.de/                                    //
8//                                                              //
9// ============================================================ //
10
11#include "db_query.h"
12#include "db_query_local.h"
13#include "query_expr.h"
14
15#include <dbui.h>
16#include <item_sel_list.h>
17#include <config_manager.hxx>
18#include <sel_boxes.hxx>
19
20#include <aw_advice.hxx>
21#include <aw_color_groups.hxx>
22#include <aw_file.hxx>
23#include <aw_msg.hxx>
24#include <aw_awar.hxx>
25#include <arb_progress.h>
26#include <aw_root.hxx>
27#include <aw_question.hxx>
28#include <rootAsWin.h>
29
30#include <ad_cb.h>
31
32#include <arb_strbuf.h>
33#include <arb_sort.h>
34#include <arb_global_defs.h>
35#include <Keeper.h>
36
37#include <list>
38#include <stack>
39
40using namespace std;
41using namespace QUERY;
42
43#define MAX_QUERY_LIST_LEN  100000
44
45#define AWAR_COLORIZE            "tmp/dbquery_all/colorize"
46#define AWAR_COLOR_LOADSAVE_NAME "tmp/colorset/name"
47
48static void free_hit_description(long info) {
49    free(reinterpret_cast<char*>(info));
50}
51
52inline void SET_QUERIED(GBDATA *gb_species, DbQuery *query, const char *hitInfo, size_t hitInfoLen = 0) {
53    dbq_assert(hitInfo);
54
55    GB_raise_user_flag(gb_species, query->select_bit);
56
57    char *name = query->selector.generate_item_id(query->gb_main, gb_species);
58    char *info;
59
60    if (hitInfoLen == 0) hitInfoLen = strlen(hitInfo);
61    if (hitInfoLen>MAX_SHOWN_DATA_SIZE) {
62        char *dupInfo = strdup(hitInfo);
63        hitInfoLen    = GBS_shorten_repeated_data(dupInfo);
64        if (hitInfoLen>MAX_SHOWN_DATA_SIZE) {
65            strcpy(dupInfo+hitInfoLen-5, "[...]");
66        }
67        info = strdup(dupInfo);
68        free(dupInfo);
69    }
70    else {
71        info = strdup(hitInfo);
72    }
73
74    GBS_write_hash(query->hit_description, name, reinterpret_cast<long>(info)); // overwrite hit info (also deallocates)
75    free(name);
76}
77
78inline void CLEAR_QUERIED(GBDATA *gb_species, DbQuery *query) {
79    GB_clear_user_flag(gb_species, query->select_bit);
80
81    char *name = query->selector.generate_item_id(query->gb_main, gb_species);
82    GBS_write_hash(query->hit_description, name, 0); // delete hit info (also deallocates)
83    free(name);
84}
85
86inline const char *getHitInfo(const char *item_id, DbQuery *query) {
87    long info = GBS_read_hash(query->hit_description, item_id);
88    return reinterpret_cast<const char*>(info);
89}
90inline const char *getHitInfo(GBDATA *gb_species, DbQuery *query) {
91    char       *name   = query->selector.generate_item_id(query->gb_main, gb_species);
92    const char *result = getHitInfo(name, query);
93    free(name);
94    return result;
95}
96inline string keptHitReason(const string& currentHitReason, GBDATA *gb_item, DbQuery *query) {
97    string      reason  = string("kept because ")+currentHitReason;
98    const char *hitinfo = getHitInfo(gb_item, query);
99    if (hitinfo) reason = string(hitinfo)+" ("+reason+')';
100    return reason;
101}
102
103static void create_query_independent_awars(AW_root *aw_root, AW_default aw_def) {
104    aw_root->awar_int(AWAR_COLORIZE, 0, aw_def);
105    aw_root->awar_string(AWAR_COLOR_LOADSAVE_NAME, "", aw_def);
106}
107
108GBDATA *QUERY::query_get_gb_main(DbQuery *query) {
109    return query->gb_main;
110}
111
112const ItemSelector& QUERY::get_queried_itemtype(DbQuery *query) {
113    return query->selector;
114}
115
116enum EXT_QUERY_TYPES {
117    EXT_QUERY_NONE,
118    EXT_QUERY_COMPARE_LINES,
119    EXT_QUERY_COMPARE_WORDS
120};
121
122query_spec::query_spec(ItemSelector& selector_)
123    : selector(selector_),
124      gb_main(NULp),
125      gb_ref(NULp),
126      expect_hit_in_ref_list(false),
127      species_name(NULp),
128      tree_name(NULp),
129      select_bit(GB_USERFLAG_QUERY), // always == GB_USERFLAG_QUERY atm (nevertheless DO NOT hardcode)
130      use_menu(0),
131      ere_pos_fig(NULp),
132      where_pos_fig(NULp),
133      by_pos_fig(NULp),
134      qbox_pos_fig(NULp),
135      key_pos_fig(NULp),
136      query_pos_fig(NULp),
137      result_pos_fig(NULp),
138      count_pos_fig(NULp),
139      do_query_pos_fig(NULp),
140      config_pos_fig(NULp),
141      do_mark_pos_fig(NULp),
142      do_unmark_pos_fig(NULp),
143      do_mark_nt_pos_fig(NULp),
144      do_unmark_nt_pos_fig(NULp),
145      do_delete_pos_fig(NULp),
146      do_set_pos_fig(NULp),
147      open_parser_pos_fig(NULp),
148      do_refresh_pos_fig(NULp),
149      popup_info_window(NULp),
150      info_box_pos_fig(NULp)
151{
152    dbq_assert(&selector);
153}
154
155bool query_spec::is_queried(GBDATA *gb_item) const {
156    return GB_user_flag(gb_item, select_bit);
157}
158
159bool QUERY::IS_QUERIED(GBDATA *gb_item, const DbQuery *query) {
160    return query->is_queried(gb_item);
161}
162
163long QUERY::count_queried_items(DbQuery *query, QUERY_RANGE range) {
164    GBDATA       *gb_main  = query->gb_main;
165    ItemSelector& selector = query->selector;
166
167    long count = 0;
168
169    for (GBDATA *gb_item_container = selector.get_first_item_container(gb_main, query->aws->get_root(), range);
170         gb_item_container;
171         gb_item_container = selector.get_next_item_container(gb_item_container, range))
172    {
173        for (GBDATA *gb_item = selector.get_first_item(gb_item_container, QUERY_ALL_ITEMS);
174             gb_item;
175             gb_item = selector.get_next_item(gb_item, QUERY_ALL_ITEMS))
176        {
177            if (IS_QUERIED(gb_item, query)) count++;
178        }
179    }
180    return count;
181}
182
183// @@@ replace query_count_items by "method" of selector
184static int query_count_items(DbQuery *query, QUERY_RANGE range, QUERY_MODES mode) {
185    int             count    = 0;
186    GBDATA         *gb_main  = query->gb_main;
187    ItemSelector&   selector = query->selector;
188    GB_transaction  ta(gb_main);
189
190    for (GBDATA *gb_item_container = selector.get_first_item_container(gb_main, query->aws->get_root(), range);
191         gb_item_container;
192         gb_item_container = selector.get_next_item_container(gb_item_container, range))
193    {
194        for (GBDATA *gb_item = selector.get_first_item(gb_item_container, QUERY_ALL_ITEMS);
195             gb_item;
196             gb_item = selector.get_next_item(gb_item, QUERY_ALL_ITEMS))
197        {
198            switch (mode) {
199                case QUERY_GENERATE: ++count; break;
200                case QUERY_ENLARGE:  count += !IS_QUERIED(gb_item, query); break;
201                case QUERY_REDUCE:   count +=  IS_QUERIED(gb_item, query); break;
202            }
203        }
204    }
205    return count;
206}
207
208const int MAX_CRITERIA = int(sizeof(unsigned long)*8/QUERY_SORT_CRITERIA_BITS);
209
210static void split_sort_mask(unsigned long sort_mask, QUERY_RESULT_ORDER *order) {
211    // splits the sort order bit mask 'sort_mask' into single sort criteria and write these into 'order'
212    // (order[0] will contain the primary sort criteria, order[1] the secondary, ...)
213
214    for (int o = 0; o<MAX_CRITERIA; ++o) {
215        order[o] = QUERY_RESULT_ORDER(sort_mask&QUERY_SORT_CRITERIA_MASK);
216        dbq_assert(order[o] == (order[o]&QUERY_SORT_CRITERIA_MASK));
217        sort_mask = sort_mask>>QUERY_SORT_CRITERIA_BITS;
218    }
219}
220
221static QUERY_RESULT_ORDER find_display_determining_sort_order(QUERY_RESULT_ORDER *order) {
222    // Returns the first criteria in 'order' (which has to have MAX_CRITERIA elements)
223    // that matches
224    // - QUERY_SORT_BY_1STFIELD_CONTENT or
225    // - QUERY_SORT_NUM_BY_1STFIELD_CONTENT or
226    // - QUERY_SORT_BY_HIT_DESCRIPTION
227    // (or QUERY_SORT_NONE if none of the above is used)
228
229    QUERY_RESULT_ORDER first = QUERY_SORT_NONE;
230    for (int o = 0; o<MAX_CRITERIA && first == QUERY_SORT_NONE; ++o) {
231        if (order[o] & (QUERY_SORT_BY_1STFIELD_CONTENT|QUERY_SORT_NUM_BY_1STFIELD_CONTENT|QUERY_SORT_BY_HIT_DESCRIPTION)) {
232            first = order[o];
233        }
234    }
235    return first;
236}
237
238static void remove_keydependent_sort_criteria(QUERY_RESULT_ORDER *order) {
239    // removes all sort-criteria from order which would use the order of the currently selected primary key
240
241    int n = 0;
242    for (int o = 0; o<MAX_CRITERIA; ++o) {
243        if (order[o] != QUERY_SORT_BY_1STFIELD_CONTENT && order[o] != QUERY_SORT_NUM_BY_1STFIELD_CONTENT) {
244            order[n++] = order[o];
245        }
246    }
247    for (; n<MAX_CRITERIA; ++n) {
248        order[n] = QUERY_SORT_NONE;
249    }
250}
251
252static void first_searchkey_changed_cb(AW_root *, DbQuery *query) {
253    QUERY_RESULT_ORDER order[MAX_CRITERIA];
254    split_sort_mask(query->sort_mask, order);
255
256    QUERY_RESULT_ORDER usedOrder = find_display_determining_sort_order(order);
257    if (usedOrder != QUERY_SORT_BY_HIT_DESCRIPTION && usedOrder != QUERY_SORT_NONE) { // do we display values?
258        DbQuery_update_list(query);
259    }
260}
261
262inline bool keep_criteria(QUERY_RESULT_ORDER old_criteria, QUERY_RESULT_ORDER new_criteria) {
263    return
264        old_criteria  != QUERY_SORT_NONE &&     // do not keep 'unsorted' (it is no real criteria)
265        (old_criteria != new_criteria        ||     // do not keep new criteria (added later)
266         old_criteria == QUERY_SORT_REVERSE);   // reverse may occur several times -> always keep
267}
268
269static void result_sort_order_changed_cb(AW_root *aw_root, DbQuery *query) {
270    // adds the new selected sort order to the sort order mask
271    // (added order removes itself, if it previously existed in sort order mask)
272    //
273    // if 'unsorted' is selected -> delete sort order
274
275    QUERY_RESULT_ORDER new_criteria = (QUERY_RESULT_ORDER)aw_root->awar(query->awar_sort)->read_int();
276    if (new_criteria == QUERY_SORT_NONE) {
277        query->sort_mask = QUERY_SORT_NONE; // clear sort_mask
278    }
279    else {
280        QUERY_RESULT_ORDER order[MAX_CRITERIA];
281        split_sort_mask(query->sort_mask, order);
282
283        int empty_or_same = 0;
284        for (int o = 0; o<MAX_CRITERIA; o++) {
285            if (!keep_criteria(order[o], new_criteria)) {
286                empty_or_same++; // these criteria will be skipped below
287            }
288        }
289
290        unsigned long new_sort_mask = 0;
291        for (int o = MAX_CRITERIA-(empty_or_same>0 ? 1 : 2); o >= 0; o--) {
292            if (keep_criteria(order[o], new_criteria)) {
293                new_sort_mask = (new_sort_mask<<QUERY_SORT_CRITERIA_BITS)|order[o];
294            }
295        }
296        query->sort_mask = (new_sort_mask<<QUERY_SORT_CRITERIA_BITS)|new_criteria; // add new primary key
297    }
298    DbQuery_update_list(query);
299}
300
301struct hits_sort_params : virtual Noncopyable {
302    DbQuery            *query;
303    char               *first_key;
304    GB_TYPES            first_type;
305    QUERY_RESULT_ORDER  order[MAX_CRITERIA];
306
307    hits_sort_params(DbQuery *q, const char *fk)
308        : query(q),
309          first_key(strdup(fk))
310    {
311        first_type = GBT_get_type_of_changekey(query->gb_main, first_key, query->selector.change_key_path);
312    }
313
314    ~hits_sort_params() {
315        free(first_key);
316    }
317};
318
319inline int numeric_string_cmp(const char *str1, const char *str2) {
320    const char *end1;
321    const char *end2;
322
323    double double1 = strtod(str1, const_cast<char**>(&end1));
324    double double2 = strtod(str2, const_cast<char**>(&end2));
325
326    bool conv1 = !end1[0] && str1[0];
327    bool conv2 = !end2[0] && str2[0];
328
329    bool both_converted = conv1 && conv2;
330
331    int cmp;
332    if (both_converted) {
333        cmp = double_cmp(double1, double2);
334    }
335    else {
336        bool conv_partial1 = end1>str1;
337        bool conv_partial2 = end2>str2;
338
339        if (conv_partial1 && conv_partial2) {
340            cmp = double_cmp(double1, double2);
341            if (!cmp) {
342                cmp = numeric_string_cmp(end1, end2);
343            }
344        }
345        else cmp = strcmp(str1, str2);
346    }
347    return cmp;
348}
349
350static int compare_hits(const void *cl_item1, const void *cl_item2, void *cl_param) {
351    const hits_sort_params *param = static_cast<const hits_sort_params*>(cl_param);
352
353    GBDATA *gb_item1 = (GBDATA*)cl_item1;
354    GBDATA *gb_item2 = (GBDATA*)cl_item2;
355
356    DbQuery      *query    = param->query;
357    ItemSelector& selector = query->selector;
358
359    int cmp = 0;
360
361    for (int o = 0; o<MAX_CRITERIA && cmp == 0; o++) {
362        QUERY_RESULT_ORDER criteria = param->order[o];
363
364        switch (criteria) {
365            case QUERY_SORT_NONE:
366                o = MAX_CRITERIA; // don't sort further
367                break;
368
369            case QUERY_SORT_BY_1STFIELD_CONTENT: {
370                char *field1 = GBT_read_as_string(gb_item1, param->first_key);
371                char *field2 = GBT_read_as_string(gb_item2, param->first_key);
372
373                cmp = ARB_strNULLcmp(field1, field2);
374
375                free(field2);
376                free(field1);
377                break;
378            }
379            case QUERY_SORT_NUM_BY_1STFIELD_CONTENT: {
380                switch (param->first_type) {
381                    case GB_INT: {
382                        long *have1 = GBT_read_int(gb_item1, param->first_key);
383                        long  L1    = have1 ? *have1 : 0;
384                        long *have2 = GBT_read_int(gb_item2, param->first_key);
385                        long  L2    = have2 ? *have2 : 0;
386
387                        cmp = have1 // use same logic as ARB_strNULLcmp
388                            ? (have2 ? long_cmp(L1,L2) : -1)
389                            : (have2 ? 1 : 0);
390                        break;
391                    }
392                    case GB_FLOAT: {
393                        float *have1 = GBT_read_float(gb_item1, param->first_key);
394                        float  f1    = have1 ? *have1 : 0;
395                        float *have2 = GBT_read_float(gb_item2, param->first_key);
396                        float  f2    = have2 ? *have2 : 0;
397
398                        cmp = have1
399                            ? (have2 ? float_cmp(f1,f2) : -1)
400                            : (have2 ? 1 : 0);
401                        break;
402                    }
403                    default: {
404                        char *field1 = GBT_read_as_string(gb_item1, param->first_key);
405                        char *field2 = GBT_read_as_string(gb_item2, param->first_key);
406
407                        cmp = (field1 && field2)
408                            ? numeric_string_cmp(field1, field2)
409                            : ARB_strNULLcmp(field1, field2);
410
411                        free(field2);
412                        free(field1);
413                    }
414                }
415                break;
416            }
417            case QUERY_SORT_BY_NESTED_PID: {
418                if (selector.parent_selector) {
419                    GBDATA *gb_parent1 = selector.get_parent(gb_item1);
420                    GBDATA *gb_parent2 = selector.get_parent(gb_item2);
421
422                    char *pid1 = selector.generate_item_id(query->gb_main, gb_parent1);
423                    char *pid2 = selector.generate_item_id(query->gb_main, gb_parent2);
424
425                    cmp = ARB_strNULLcmp(pid1, pid2);
426
427                    free(pid2);
428                    free(pid1);
429                }
430                break;
431            }
432            case QUERY_SORT_BY_ID: {
433                const char *id1 = GBT_read_char_pntr(gb_item1, selector.id_field);
434                const char *id2 = GBT_read_char_pntr(gb_item2, selector.id_field);
435
436                cmp = strcmp(id1, id2);
437                break;
438            }
439            case QUERY_SORT_BY_MARKED:
440                cmp = GB_read_flag(gb_item2)-GB_read_flag(gb_item1);
441                break;
442
443            case QUERY_SORT_BY_HIT_DESCRIPTION: {
444                const char *id1   = GBT_read_char_pntr(gb_item1, selector.id_field);
445                const char *id2   = GBT_read_char_pntr(gb_item2, selector.id_field);
446                const char *info1 = reinterpret_cast<const char *>(GBS_read_hash(query->hit_description, id1));
447                const char *info2 = reinterpret_cast<const char *>(GBS_read_hash(query->hit_description, id2));
448                cmp = ARB_strNULLcmp(info1, info2);
449                break;
450            }
451            case QUERY_SORT_REVERSE: {
452                GBDATA *tmp = gb_item1; // swap items for following compares (this is a prefix revert!)
453                gb_item1    = gb_item2;
454                gb_item2    = tmp;
455                break;
456            }
457        }
458    }
459
460    return cmp;
461}
462
463static void detectMaxNameLength(const char *key, long /*val*/, void *cl_len) {
464    int *len  = (int*)cl_len;
465    int  klen = strlen(key);
466
467    if (klen>*len) *len = klen;
468}
469
470#if defined(ASSERTION_USED)
471inline bool SLOW_is_pseudo_key(const char *key) {
472    return
473        strcmp(key, PSEUDO_FIELD_ANY_FIELD) == 0 ||
474        strcmp(key, PSEUDO_FIELD_ALL_FIELDS) == 0 ||
475        strcmp(key, PSEUDO_FIELD_ANY_FIELD_REC) == 0 ||
476        strcmp(key, PSEUDO_FIELD_ALL_FIELDS_REC) == 0;
477}
478#endif
479inline bool is_pseudo_key(const char *key) {
480    // returns true, if 'key' is a pseudo-key
481    bool is_pseudo = key[0] == '[';
482    dbq_assert(is_pseudo == SLOW_is_pseudo_key(key));
483    return is_pseudo;
484}
485
486void QUERY::DbQuery_update_list(DbQuery *query) {
487    GB_push_transaction(query->gb_main);
488
489    dbq_assert(query->hitlist);
490    query->hitlist->clear();
491
492    AW_window     *aww      = query->aws;
493    AW_root       *aw_root  = aww->get_root();
494    QUERY_RANGE    range    = (QUERY_RANGE)aw_root->awar(query->awar_where)->read_int();
495    ItemSelector&  selector = query->selector;
496
497    // create array of hits
498    long     count  = count_queried_items(query, range);
499    GBDATA **sorted = ARB_alloc<GBDATA*>(count);
500    {
501        long s = 0;
502
503        for (GBDATA *gb_item_container = selector.get_first_item_container(query->gb_main, aw_root, range);
504             gb_item_container;
505             gb_item_container = selector.get_next_item_container(gb_item_container, range))
506        {
507            for (GBDATA *gb_item = selector.get_first_item(gb_item_container, QUERY_ALL_ITEMS);
508                 gb_item;
509                 gb_item = selector.get_next_item(gb_item, QUERY_ALL_ITEMS))
510            {
511                if (IS_QUERIED(gb_item, query)) sorted[s++] = gb_item;
512            }
513        }
514    }
515
516    // sort hits
517
518    hits_sort_params param(query, aww->get_root()->awar(query->awar_keys[0])->read_char_pntr());
519
520    bool is_pseudo  = is_pseudo_key(param.first_key);
521    bool show_value = !is_pseudo; // cannot refer to key-value of pseudo key
522
523    if (query->sort_mask != QUERY_SORT_NONE) {    // unsorted -> don't sort
524        split_sort_mask(query->sort_mask, param.order);
525        if (is_pseudo) {
526            remove_keydependent_sort_criteria(param.order);
527        }
528        if (show_value && find_display_determining_sort_order(param.order) == QUERY_SORT_BY_HIT_DESCRIPTION) {
529            show_value = false;
530        }
531        GB_sort((void**)sorted, 0, count, compare_hits, &param);
532    }
533
534    // display hits
535
536    int name_len = selector.item_name_length;
537    if (name_len == -1) { // if name_len is unknown -> detect
538        GBS_hash_do_const_loop(query->hit_description, detectMaxNameLength, &name_len);
539    }
540
541    long i;
542    for (i = 0; i<count && i<MAX_QUERY_LIST_LEN; i++) {
543        char *name = selector.generate_item_id(query->gb_main, sorted[i]);
544        if (name) {
545            char       *toFree = NULp;
546            const char *info;
547
548            if (show_value) {
549                toFree = GBT_read_as_string(sorted[i], param.first_key);
550                if (toFree) {
551                    if (strlen(toFree)>MAX_SHOWN_DATA_SIZE) {
552                        size_t shortened_len = GBS_shorten_repeated_data(toFree);
553                        if (shortened_len>MAX_SHOWN_DATA_SIZE) {
554                            strcpy(toFree+MAX_SHOWN_DATA_SIZE-5, "[...]");
555                        }
556                    }
557                }
558                else {
559                    toFree = GBS_global_string_copy("<%s has no data>", param.first_key);
560                }
561                info = toFree;
562            }
563            else {
564                info = getHitInfo(name, query);
565                if (!info) info = "<no hit info>";
566            }
567
568            dbq_assert(info);
569            const char *line = GBS_global_string("%c %-*s :%s",
570                                                 GB_read_flag(sorted[i]) ? '*' : ' ',
571                                                 name_len, name,
572                                                 info);
573
574            query->hitlist->insert(line, name);
575            free(toFree);
576            free(name);
577        }
578    }
579
580    if (count>MAX_QUERY_LIST_LEN) {
581        query->hitlist->insert("*****  List truncated  *****", "");
582    }
583
584    free(sorted);
585
586    query->hitlist->insert_default("End of list", "");
587    query->hitlist->update();
588    aww->get_root()->awar(query->awar_count)->write_int((long)count);
589    GB_pop_transaction(query->gb_main);
590}
591
592static void mark_queried_cb(AW_window*, DbQuery *query, int mark) {
593    // Mark listed species
594    // mark = 1 -> mark listed
595    // mark | 8 -> don't change rest
596
597    ItemSelector& selector = query->selector;
598    GB_push_transaction(query->gb_main);
599
600    for (GBDATA *gb_item_container = selector.get_first_item_container(query->gb_main, query->aws->get_root(), QUERY_ALL_ITEMS);
601         gb_item_container;
602         gb_item_container = selector.get_next_item_container(gb_item_container, QUERY_ALL_ITEMS))
603        {
604            for (GBDATA *gb_item = selector.get_first_item(gb_item_container, QUERY_ALL_ITEMS);
605                 gb_item;
606                 gb_item = selector.get_next_item(gb_item, QUERY_ALL_ITEMS))
607                {
608                    if (IS_QUERIED(gb_item, query)) {
609                        GB_write_flag(gb_item, mark&1);
610                    }
611                    else if ((mark&8) == 0) {
612                        GB_write_flag(gb_item, 1-(mark&1));
613                    }
614                }
615        }
616
617    DbQuery_update_list(query);
618    GB_pop_transaction(query->gb_main);
619}
620
621void QUERY::unquery_all(void *, DbQuery *query) {
622    GB_push_transaction(query->gb_main);
623    GBDATA *gb_species;
624    for (gb_species = GBT_first_species(query->gb_main);
625                gb_species;
626                gb_species = GBT_next_species(gb_species)) {
627        CLEAR_QUERIED(gb_species, query);
628    }
629    DbQuery_update_list(query);
630    GB_pop_transaction(query->gb_main);
631}
632
633static void delete_queried_species_cb(AW_window*, DbQuery *query) {
634    ItemSelector& selector = query->selector;
635    GB_begin_transaction(query->gb_main);
636
637    long cnt = 0;
638    for (GBDATA *gb_item_container = selector.get_first_item_container(query->gb_main, query->aws->get_root(), QUERY_ALL_ITEMS);
639         gb_item_container;
640         gb_item_container = selector.get_next_item_container(gb_item_container, QUERY_ALL_ITEMS))
641    {
642        for (GBDATA *gb_item = selector.get_first_item(gb_item_container, QUERY_ALL_ITEMS);
643             gb_item;
644             gb_item = selector.get_next_item(gb_item, QUERY_ALL_ITEMS))
645        {
646            if (IS_QUERIED(gb_item, query)) cnt++;
647        }
648    }
649
650    if (!cnt || !aw_ask_sure("delete_queried_species", GBS_global_string("Are you sure to delete %li %s", cnt, selector.items_name))) {
651        GB_abort_transaction(query->gb_main);
652        return;
653    }
654
655    GB_ERROR error = NULp;
656
657    for (GBDATA *gb_item_container = selector.get_first_item_container(query->gb_main, query->aws->get_root(), QUERY_ALL_ITEMS);
658         !error && gb_item_container;
659         gb_item_container = selector.get_next_item_container(gb_item_container, QUERY_ALL_ITEMS))
660        {
661            for (GBDATA *gb_item = selector.get_first_item(gb_item_container, QUERY_ALL_ITEMS);
662                 !error && gb_item;
663                 gb_item = selector.get_next_item(gb_item, QUERY_ALL_ITEMS))
664                {
665                    if (IS_QUERIED(gb_item, query)) {
666                        error = GB_delete(gb_item);
667                    }
668                }
669        }
670
671    if (error) {
672        GB_abort_transaction(query->gb_main);
673        aw_message(error);
674    }
675    else {
676        DbQuery_update_list(query);
677        GB_commit_transaction(query->gb_main);
678    }
679}
680
681static GB_HASH *create_ref_hash(const DbQuery *query, const char *key, bool split_words) {
682    GBDATA  *gb_ref       = query->gb_ref;
683    bool     queried_only = query->expect_hit_in_ref_list;
684    GB_HASH *hash         = GBS_create_hash(GBT_get_species_count(gb_ref), GB_IGNORE_CASE);
685
686    for (GBDATA  *gb_species = GBT_first_species(gb_ref);
687         gb_species;
688         gb_species = GBT_next_species(gb_species))
689    {
690        if (!queried_only || IS_QUERIED(gb_species, query)) {
691            GBDATA *gb_name = GB_search(gb_species, key, GB_FIND);
692            if (gb_name) {
693                char *keyas = GB_read_as_string(gb_name);
694                if (keyas && strlen(keyas)) {
695                    if (split_words) {
696                        char *t;
697                        for (t = strtok(keyas, " "); t; t = strtok(NULp, " ")) {
698                            if (t[0]) GBS_write_hash(hash, t, (long)gb_species);
699                        }
700                    }
701                    else {
702                        GBS_write_hash(hash, keyas, (long)gb_species);
703                    }
704                }
705                free(keyas);
706            }
707        }
708    }
709    return hash;
710}
711
712class TargetItem : public QueryTarget {
713    RefPtr<GBDATA> gb_item;
714public:
715    TargetItem(const DbQuery& query) :
716        QueryTarget(query.gb_main, query.get_tree_name()),
717        gb_item(NULp)
718    {}
719
720    void aimTo(GBDATA *gb_next_item) {
721        dbq_assert(gb_next_item); // @@@ maybe allow? (to invalidate target)
722        gb_item = gb_next_item;
723    }
724    GBDATA *get_item() const {
725        dbq_assert(gb_item); // you have to call aimTo before!
726        return gb_item;
727    }
728
729    // QueryTarget interface:
730    GBDATA *get_ACI_item() const OVERRIDE { return get_item(); }
731};
732
733class ItemQueryKey FINAL_TYPE : public QueryKey, virtual Noncopyable {
734    char    *key;             // search key (=name of DB field or pseudo-field)
735    bool     hierarchical;    // is 'key' hierarchical?
736    GBQUARK  keyquark;        // valid only if get_type() == QKEY_EXPLICIT!
737
738    typedef std::stack<GBDATA*> ParentStack;
739
740    mutable GBDATA      *gb_key; // last read key-entry
741    mutable ParentStack  parent; // container(s) descended
742
743    static query_key_type detect_key_type(const char *field_name) {
744        if (is_pseudo_key(field_name)) {
745            if (strcmp(field_name, PSEUDO_FIELD_ANY_FIELD)      == 0) return QKEY_ANY;
746            if (strcmp(field_name, PSEUDO_FIELD_ANY_FIELD_REC)  == 0) return QKEY_ANY_REC;
747            if (strcmp(field_name, PSEUDO_FIELD_ALL_FIELDS)     == 0) return QKEY_ALL;
748            if (strcmp(field_name, PSEUDO_FIELD_ALL_FIELDS_REC) == 0) return QKEY_ALL_REC;
749            dbq_assert(0);
750        }
751        return QKEY_EXPLICIT;
752    }
753
754public:
755    ItemQueryKey(GBDATA *gb_main, const char *field_name) :
756        QueryKey(detect_key_type(field_name)),
757        key(strdup(field_name)),
758        hierarchical(false),
759        keyquark(0), // invalid quark
760        gb_key(NULp)
761    {
762        if (get_type() == QKEY_EXPLICIT) {
763            if (GB_first_non_key_char(key)) {
764                hierarchical = true;
765            }
766            else {
767                keyquark = GB_find_or_create_quark(gb_main, key);
768            }
769        }
770    }
771    ~ItemQueryKey() {
772        free(key);
773    }
774
775    // QueryKey interface:
776    char *get_target_data(const QueryTarget& target, GB_ERROR& error) const OVERRIDE {
777        // retrieve content of target key
778        if (!gb_key) {
779            const TargetItem& target_item = DOWNCAST_REFERENCE(const TargetItem, target);
780            gb_key = get_first_field(target_item.get_item());
781        }
782
783        char *data;
784        if (gb_key) {
785            data = GB_read_as_string(gb_key);
786            if (!data) {
787                if (GB_readable_as_string(gb_key)) {
788                    error = GB_await_error();
789                }
790                else {
791                    gb_assert(!GB_have_error());
792                    error = GBS_global_string("field '%s' has wrong type", GB_read_key_pntr(gb_key));
793                }
794            }
795        }
796        else {
797            data = strdup(""); // non-existing field -> assume "" as default value
798                               // (needed to search for missing fields using '!=*' ?)
799        }
800        return data;
801    }
802    const char *get_name()  const OVERRIDE {
803        // name of target (e.g. for reports)
804        // - for explicit keys, it simply returns the specified key name
805        // - for iterating keys, it returns the name of the DB field at current iteration position,
806        //   showing path to key relative to item (e.g. 'ali_16s/data')
807        //   If not yet iterating, it returns the name of the pseudo-field.
808
809        if (get_type() != QKEY_EXPLICIT && gb_key) {
810            const char *keyname = GB_read_key_pntr(gb_key);
811            if (!parent.empty()) {
812                const char *parent_path  = GB_get_db_path(parent.top());
813                dbq_assert(parent.top() == GB_get_father(gb_key));
814                const char *rslash       = strrchr(parent_path, '/');
815                for (int nest = parent.size()-1; rslash && nest; --nest) {
816                    rslash = strrchr(rslash-1, '/');
817                }
818                return GBS_global_string("%s/%s", rslash ? rslash+1 : "<unknown>", keyname);
819            }
820            return keyname;
821        }
822        return key;
823    }
824
825private:
826    bool accept_or_iterate() const {
827        dbq_assert(get_type() != QKEY_EXPLICIT);
828        if (gb_key) {
829            GB_TYPES ktype = GB_read_type(gb_key);
830            if (ktype == GB_DB) {
831                switch (get_type()) {
832                    case QKEY_ALL:
833                    case QKEY_ANY:
834                        return iterate(); // = skip over containers
835
836                    case QKEY_ALL_REC:
837                    case QKEY_ANY_REC:
838                        parent.push(gb_key); // remember position ..
839                        gb_key = GB_child(gb_key); // .. and descend into container
840                        break;
841
842                    case QKEY_EXPLICIT:
843                        dbq_assert(0);
844                }
845                return accept_or_iterate();
846            }
847            if (!GB_TYPE_readable_as_string(ktype)) {
848                return iterate();
849            }
850            return true;
851        }
852        if (!parent.empty()) {
853            gb_key = parent.top();
854            parent.pop();
855            return iterate();
856        }
857        return false;
858    }
859public:
860
861    bool iterate() const OVERRIDE { // iterate key to next item
862        dbq_assert(get_type() != QKEY_EXPLICIT);
863        if (gb_key) gb_key = GB_nextChild(gb_key);
864        return accept_or_iterate();
865    }
866    void reset() const OVERRIDE {
867        // restart multi-key iteration
868        gb_key = NULp;
869        parent = ParentStack();
870    }
871    // ----------
872
873
874    GBDATA *get_first_field(GBDATA *gb_item) const {
875        GBDATA *gb_field = NULp;
876
877        if (hierarchical) {
878            gb_field = GB_search(gb_item, key, GB_FIND);
879        }
880        else if (get_type() != QKEY_EXPLICIT) {
881            gb_field = GB_child(gb_item);
882            // no need to use same method as normal search (we just need any key here)
883            while (gb_field && GB_read_type(gb_field) == GB_DB) {
884                gb_field = GB_nextChild(gb_field);
885            }
886        }
887        else {
888            gb_field = GB_find_sub_by_quark(gb_item, keyquark, NULp, 0);
889        }
890
891        return gb_field;
892    }
893};
894
895inline query_operator awarvalue2query_operator(const char *awarvalue) {
896    if (strcmp(awarvalue, "and") == 0) return AND;
897    if (strcmp(awarvalue, "or")  == 0) return OR;
898    return ILLEGAL;
899}
900
901SmartPtr<QueryExpr> DbQuery::buildQueryExpr() {
902    AW_root   *aw_root  = aws->get_root();
903    QueryExpr *first_qe = NULp;
904
905    for (int idx = 0; idx<QUERY_EXPRESSIONS; ++idx) {
906        query_operator aqo = idx
907            ? awarvalue2query_operator(aw_root->awar(awar_operator[idx])->read_char_pntr())
908            : OR; // initial value is false, (false OR QUERY1) == QUERY1
909
910        if (aqo != ILLEGAL) {
911            bool         not_equal  = aw_root->awar(awar_not[idx])->read_int() != 0;
912            const char  *expression = aw_root->awar(awar_queries[idx])->read_char_pntr();
913            const char  *field_name = aw_root->awar(awar_keys[idx])->read_char_pntr();
914            QueryKeyPtr  qkey       = new ItemQueryKey(gb_main, field_name);
915
916            QueryExpr *qe = new QueryExpr(aqo, qkey, not_equal, expression);
917
918            if (!field_name[0] || strcmp(field_name, NO_FIELD_SELECTED) == 0) {
919                qe->setError("No field selected");
920            }
921
922            if (!first_qe) first_qe = qe;
923            else           first_qe->append(qe);
924        }
925    }
926
927    return first_qe;
928}
929
930static void perform_query_cb(AW_window*, DbQuery *query, EXT_QUERY_TYPES ext_query) {
931    ItemSelector& selector = query->selector;
932
933    GB_push_transaction(query->gb_main);
934
935    AW_root *aw_root = query->aws->get_root();
936    SmartPtr<QueryExpr> qexpr = query->buildQueryExpr();
937
938    QUERY_MODES mode  = (QUERY_MODES)aw_root->awar(query->awar_ere)->read_int();
939    QUERY_RANGE range = (QUERY_RANGE)aw_root->awar(query->awar_where)->read_int();
940    QUERY_TYPES type  = (QUERY_TYPES)aw_root->awar(query->awar_by)->read_int();
941
942    if (query->gb_ref && type != QUERY_MARKED) {  // special for merge tool!
943        char *first_query = aw_root->awar(query->awar_queries[0])->read_string();
944        if (strlen(first_query) == 0) {
945            if (!ext_query) ext_query  = EXT_QUERY_COMPARE_LINES;
946        }
947        free(first_query);
948    }
949
950    GB_ERROR error = qexpr->getError();
951
952    if (!error) {
953        size_t item_count = query_count_items(query, range, mode);
954        if (item_count) {
955            arb_progress progress("Searching", item_count);
956
957            if (query->gb_ref && // merge tool only
958                (ext_query == EXT_QUERY_COMPARE_LINES || ext_query == EXT_QUERY_COMPARE_WORDS))
959            {
960                if (qexpr->get_key_type() != QKEY_EXPLICIT) {
961                    error = "Please select an explicit field or specify a search string (for first expression)";
962                }
963                else {
964                    GB_push_transaction(query->gb_ref);
965
966                    char    *first_keyname = aw_root->awar(query->awar_keys[0])->read_string();
967                    GB_HASH *ref_hash      = create_ref_hash(query, first_keyname, ext_query == EXT_QUERY_COMPARE_WORDS);
968
969                    if (GBS_hash_elements(ref_hash) == 0) {
970                        error = GBS_global_string("No data found in field '%s'", first_keyname);
971                    }
972
973#if defined(DEBUG)
974                    printf("query: search identical %s in field %s%s\n",
975                           (ext_query == EXT_QUERY_COMPARE_WORDS ? "words" : "values"),
976                           first_keyname,
977                           query->expect_hit_in_ref_list ? " of species listed in other hitlist" : "");
978#endif // DEBUG
979
980                    for (GBDATA *gb_item_container = selector.get_first_item_container(query->gb_main, aw_root, range);
981                         gb_item_container && !error;
982                         gb_item_container = selector.get_next_item_container(gb_item_container, range))
983                    {
984                        for (GBDATA *gb_item = selector.get_first_item(gb_item_container, QUERY_ALL_ITEMS);
985                             gb_item && !error;
986                             gb_item = selector.get_next_item(gb_item, QUERY_ALL_ITEMS))
987                        {
988                            switch (mode) {
989                                case QUERY_GENERATE: CLEAR_QUERIED(gb_item, query); break;
990                                case QUERY_ENLARGE:  if (IS_QUERIED(gb_item, query)) continue; break;
991                                case QUERY_REDUCE:   if (!IS_QUERIED(gb_item, query)) continue; break;
992                            }
993
994                            // Note: here only the first query is used (special case in merge tool only)
995
996                            ItemQueryKey&  query_key = DOWNCAST_REFERENCE(ItemQueryKey, qexpr->get_key());
997                            GBDATA        *gb_key    = query_key.get_first_field(gb_item);
998                            if (gb_key) {
999                                char *data = GB_read_as_string(gb_key);
1000
1001                                if (data && data[0]) {
1002                                    string hit_reason;
1003                                    bool   this_hit = false;
1004
1005                                    if (ext_query == EXT_QUERY_COMPARE_WORDS) {
1006                                        for (char *t = strtok(data, " "); t; t = strtok(NULp, " ")) {
1007                                            GBDATA *gb_ref_pntr = (GBDATA *)GBS_read_hash(ref_hash, t);
1008                                            if (gb_ref_pntr) { // found item in other DB, with 'first_keyname' containing word from 'gb_key'
1009                                                this_hit   = true;
1010                                                hit_reason = GBS_global_string("%s%s has word '%s' in %s",
1011                                                                               query->expect_hit_in_ref_list ? "Hit " : "",
1012                                                                               selector.generate_item_id(query->gb_ref, gb_ref_pntr),
1013                                                                               t, first_keyname);
1014                                            }
1015                                        }
1016                                    }
1017                                    else {
1018                                        GBDATA *gb_ref_pntr = (GBDATA *)GBS_read_hash(ref_hash, data);
1019                                        if (gb_ref_pntr) { // found item in other DB, with identical 'first_keyname'
1020                                            this_hit   = true;
1021                                            hit_reason = GBS_global_string("%s%s matches %s",
1022                                                                           query->expect_hit_in_ref_list ? "Hit " : "",
1023                                                                           selector.generate_item_id(query->gb_ref, gb_ref_pntr),
1024                                                                           first_keyname);
1025                                        }
1026                                    }
1027
1028                                    if (type == QUERY_DONT_MATCH) {
1029                                        this_hit = !this_hit;
1030                                        if (this_hit) hit_reason = "<no matching entry>";
1031                                    }
1032
1033                                    if (this_hit) {
1034                                        dbq_assert(!hit_reason.empty());
1035                                        SET_QUERIED(gb_item, query, hit_reason.c_str(), hit_reason.length());
1036                                    }
1037                                    else CLEAR_QUERIED(gb_item, query);
1038                                }
1039                                free(data);
1040                            }
1041
1042                            progress.inc_and_check_user_abort(error);
1043                        }
1044                    }
1045
1046                    GBS_free_hash(ref_hash);
1047                    GB_pop_transaction(query->gb_ref);
1048                }
1049            }
1050            else { // "normal" query
1051                if (type == QUERY_DONT_MATCH) {
1052#if defined(DEBUG)
1053                    fputs("query: !(", stdout); qexpr->dump();
1054#endif // DEBUG
1055
1056                    qexpr->negate();
1057                    type = QUERY_MATCH;
1058
1059#if defined(DEBUG)
1060                    fputs(") => query: ", stdout); qexpr->dump(); fputc('\n', stdout);
1061#endif // DEBUG
1062                }
1063#if defined(DEBUG)
1064                else { fputs("query: ", stdout); qexpr->dump(); fputc('\n', stdout); }
1065#endif // DEBUG
1066
1067                TargetItem target_item(*query);
1068
1069                for (GBDATA *gb_item_container = selector.get_first_item_container(query->gb_main, aw_root, range);
1070                     gb_item_container && !error;
1071                     gb_item_container = selector.get_next_item_container(gb_item_container, range))
1072                {
1073                    for (GBDATA *gb_item = selector.get_first_item(gb_item_container, QUERY_ALL_ITEMS);
1074                         gb_item && !error;
1075                         gb_item = selector.get_next_item(gb_item, QUERY_ALL_ITEMS))
1076                    {
1077                        switch (mode) {
1078                            case QUERY_GENERATE: CLEAR_QUERIED(gb_item, query); break;
1079                            case QUERY_ENLARGE:  if (IS_QUERIED(gb_item, query)) continue; break;
1080                            case QUERY_REDUCE:   if (!IS_QUERIED(gb_item, query)) continue; break;
1081                        }
1082
1083                        bool   hit = false;
1084                        string hit_reason;
1085
1086                        switch (type) {
1087                            case QUERY_MARKED:
1088                                hit        = GB_read_flag(gb_item);
1089                                hit_reason = "<marked>";
1090                                break;
1091
1092                            case QUERY_MATCH:
1093                                dbq_assert(ext_query == EXT_QUERY_NONE);
1094                                target_item.aimTo(gb_item);
1095                                hit = qexpr->matches(target_item, hit_reason);
1096                                break;
1097
1098                            default: dbq_assert(0); break;
1099                        }
1100
1101                        if (hit) {
1102                            dbq_assert(!hit_reason.empty());
1103
1104                            if (mode == QUERY_REDUCE) hit_reason = keptHitReason(hit_reason, gb_item, query);
1105                            SET_QUERIED(gb_item, query, hit_reason.c_str(), hit_reason.length());
1106                        }
1107                        else CLEAR_QUERIED(gb_item, query);
1108
1109                        if (error) {
1110                            error = GB_failedTo_error("query", GBT_get_name_or_description(gb_item), error);
1111                        }
1112                        else {
1113                            progress.inc_and_check_user_abort(error);
1114                        }
1115                    }
1116                }
1117            }
1118
1119            if (error) progress.done();
1120        }
1121    }
1122
1123    if (!error) error = qexpr->getError(); // check for query error
1124
1125    if (error) aw_message(error);
1126    else DbQuery_update_list(query);
1127
1128    GB_pop_transaction(query->gb_main);
1129}
1130
1131void QUERY::copy_selection_list_2_query_box(DbQuery *query, AW_selection_list *srclist, const char *hit_description) {
1132    GB_transaction ta(query->gb_main);
1133
1134    dbq_assert(strstr(hit_description, "%s")); // hit_description needs '%s' (which is replaced by visible content of 'id')
1135
1136    GB_ERROR         error     = NULp;
1137    AW_window       *aww       = query->aws;
1138    AW_root         *aw_root   = aww->get_root();
1139    GB_HASH         *list_hash = srclist->to_hash(false);
1140    QUERY_MODES  mode      = (QUERY_MODES)aw_root->awar(query->awar_ere)->read_int();
1141    QUERY_TYPES  type      = (QUERY_TYPES)aw_root->awar(query->awar_by)->read_int();
1142
1143    if (type == QUERY_MARKED) {
1144        error = "Query mode 'that are marked' does not apply here.\nEither select 'that match the query' or 'that don't match the q.'";
1145    }
1146
1147    if (type != QUERY_MATCH || mode != QUERY_GENERATE) { // different behavior as in the past -> advice
1148        AW_advice("'Move to hitlist' now depends on the values selected for\n"
1149                  " * 'Search/Add/Keep species' and\n"
1150                  " * 'that match/don't match the query'\n"
1151                  "in the search tool.",
1152                  AW_ADVICE_TOGGLE_AND_HELP,
1153                  "Behavior changed",
1154                  "next_neighbours.hlp");
1155    }
1156
1157    long inHitlist = GBS_hash_elements(list_hash);
1158    long seenInDB  = 0;
1159
1160    for (GBDATA *gb_species = GBT_first_species(query->gb_main);
1161         gb_species && !error;
1162         gb_species = GBT_next_species(gb_species))
1163    {
1164        switch (mode) {
1165            case QUERY_GENERATE: CLEAR_QUERIED(gb_species, query); break;
1166            case QUERY_ENLARGE:  if (IS_QUERIED(gb_species, query)) continue; break;
1167            case QUERY_REDUCE:   if (!IS_QUERIED(gb_species, query)) continue; break;
1168        }
1169
1170        const char *displayed = reinterpret_cast<const char*>(GBS_read_hash(list_hash, GBT_get_name_or_description(gb_species)));
1171
1172        if (displayed) seenInDB++;
1173
1174        if (contradicted(displayed, type == QUERY_DONT_MATCH)) {
1175            string hit_reason = GBS_global_string(hit_description, displayed ? displayed : "<no near neighbour>");
1176
1177            if (mode == QUERY_REDUCE) hit_reason = keptHitReason(hit_reason, gb_species, query);
1178            SET_QUERIED(gb_species, query, hit_reason.c_str(), hit_reason.length());
1179        }
1180        else {
1181            CLEAR_QUERIED(gb_species, query);
1182        }
1183    }
1184
1185    if (seenInDB < inHitlist) {
1186        aw_message(GBS_global_string("%li of %li hits were found in database", seenInDB, inHitlist));
1187    }
1188
1189    GBS_free_hash(list_hash);
1190    if (error) aw_message(error);
1191    DbQuery_update_list(query);
1192}
1193
1194
1195void QUERY::search_duplicated_field_content(AW_window *, DbQuery *query, bool tokenize) {
1196    AW_root  *aw_root = query->aws->get_root();
1197    char     *key     = aw_root->awar(query->awar_keys[0])->read_string();
1198    GB_ERROR  error   = NULp;
1199
1200    if (strlen(key) == 0) {
1201        error = "Please select a key (in the first query expression)";
1202    }
1203    else if (is_pseudo_key(key)) {
1204        error = "You have to select an explicit key (in the first query expression)";
1205    }
1206    else {
1207        GB_transaction dumy(query->gb_main);
1208        ItemSelector&  selector = query->selector;
1209
1210        GBDATA      *gb_species_data = GBT_get_species_data(query->gb_main);
1211        long         hashsize;
1212        QUERY_RANGE  range           = QUERY_ALL_ITEMS;
1213        QUERY_TYPES  type            = (QUERY_TYPES)aw_root->awar(query->awar_by)->read_int();
1214
1215        switch (selector.type) {
1216            case QUERY_ITEM_SPECIES:
1217            case QUERY_ITEM_ORGANISM: {
1218                hashsize = GB_number_of_subentries(gb_species_data);
1219                break;
1220            }
1221            case QUERY_ITEM_EXPERIMENT:
1222            case QUERY_ITEM_GENE: {
1223                // handle species sub-items
1224                hashsize = 0;
1225
1226                for (GBDATA *gb_item_container = selector.get_first_item_container(query->gb_main, aw_root, range);
1227                     gb_item_container;
1228                     gb_item_container = selector.get_next_item_container(gb_item_container, range))
1229                {
1230                    hashsize += GB_number_of_subentries(gb_item_container);
1231                }
1232
1233                break;
1234            }
1235            case QUERY_ITEM_SAI: {
1236                 dbq_assert(0); // search+query not fit for SAI
1237                 hashsize = 0;
1238                 break;
1239             }
1240        }
1241
1242        if (!hashsize) {
1243            error = "No items exist";
1244        }
1245        else if (type == QUERY_MARKED) {
1246            error = "'that are marked' is not applicable here";
1247        }
1248
1249        if (!error) {
1250            GB_HASH *hash = GBS_create_hash(hashsize, GB_IGNORE_CASE);
1251
1252            for (GBDATA *gb_item_container = selector.get_first_item_container(query->gb_main, aw_root, range);
1253                 gb_item_container;
1254                 gb_item_container = selector.get_next_item_container(gb_item_container, range))
1255            {
1256                for (GBDATA *gb_item = selector.get_first_item(gb_item_container, QUERY_ALL_ITEMS);
1257                     gb_item;
1258                     gb_item = selector.get_next_item(gb_item, QUERY_ALL_ITEMS))
1259                {
1260                    CLEAR_QUERIED(gb_item, query);
1261                    GB_write_flag(gb_item, 0);
1262
1263                    GBDATA *gb_key = GB_search(gb_item, key, GB_FIND);  if (!gb_key) continue;
1264                    char   *data   = GB_read_as_string(gb_key);         if (!data) continue;
1265
1266                    if (tokenize) {
1267                        char *s;
1268                        for (s=strtok(data, ",; \t."); s; s = strtok(NULp, ",; \t.")) {
1269                            GBDATA *gb_old = (GBDATA *)GBS_read_hash(hash, s);
1270                            if (gb_old) {
1271                                const char *oldInfo   = NULp;
1272                                char       *firstInfo = GBS_global_string_copy("1st=%s", s);
1273
1274                                if (IS_QUERIED(gb_old, query)) {
1275                                    const char *prevInfo = getHitInfo(gb_old, query);
1276                                    if (!prevInfo) {
1277                                        oldInfo = firstInfo;
1278                                    }
1279                                    else if (!strstr(prevInfo, firstInfo)) { // not already have 1st-entry here
1280                                        oldInfo = GBS_global_string("%s %s", prevInfo, firstInfo);
1281                                    }
1282                                }
1283                                else {
1284                                    oldInfo = firstInfo;
1285                                }
1286
1287                                if (oldInfo) SET_QUERIED(gb_old, query, oldInfo);
1288                                SET_QUERIED(gb_item, query, GBS_global_string("dup=%s", s));
1289                                GB_write_flag(gb_item, 1);
1290
1291                                free(firstInfo);
1292                            }
1293                            else {
1294                                GBS_write_hash(hash, s, (long)gb_item);
1295                            }
1296                        }
1297                    }
1298                    else {
1299                        GBDATA *gb_old = (GBDATA *)GBS_read_hash(hash, data);
1300                        if (gb_old) {
1301                            if (!IS_QUERIED(gb_old, query)) {
1302                                SET_QUERIED(gb_old, query, GBS_global_string("%s (1st)", data));
1303                            }
1304                            SET_QUERIED(gb_item, query, GBS_global_string("%s (duplicate)", data));
1305                            GB_write_flag(gb_item, 1);
1306                        }
1307                        else {
1308                            GBS_write_hash(hash, data, (long)gb_item);
1309                        }
1310                    }
1311
1312                    free(data);
1313                }
1314
1315                if (type == QUERY_DONT_MATCH) {
1316                    for (GBDATA *gb_item = selector.get_first_item(gb_item_container, QUERY_ALL_ITEMS);
1317                         gb_item;
1318                         gb_item = selector.get_next_item(gb_item, QUERY_ALL_ITEMS))
1319                    {
1320                        if (IS_QUERIED(gb_item, query)) {
1321                            CLEAR_QUERIED(gb_item, query);
1322                            GB_write_flag(gb_item, 0); // unmark
1323                        }
1324                        else {
1325                            SET_QUERIED(gb_item, query, tokenize ? "<entry with unique words>" : "<unique entry>");
1326                        }
1327                    }
1328                }
1329            }
1330
1331            GBS_free_hash(hash);
1332        }
1333
1334        if (type != QUERY_MATCH) {
1335            AW_advice("'Find equal entries' now depends on the values selected for\n"
1336                      " * 'that match/don't match the query'\n"
1337                      "in the search tool.",
1338                      AW_ADVICE_TOGGLE_AND_HELP,
1339                      "Behavior changed",
1340                      "search_duplicates.hlp");
1341        }
1342    }
1343
1344    free(key);
1345
1346    if (error) aw_message(error);
1347    DbQuery_update_list(query);
1348}
1349
1350static void modify_fields_of_queried_cb(AW_window*, DbQuery *query) {
1351    ItemSelector&  selector = query->selector;
1352    AW_root       *aw_root  = query->aws->get_root();
1353    GB_ERROR       error    = GB_begin_transaction(query->gb_main);
1354
1355    const char *key;
1356    if (!error) {
1357        bool        acceptIdMod = aw_root->awar(query->awar_acceptIdMod)->read_int();
1358        FailIfField failMode    = acceptIdMod ? FIF_ALLOW_ID_CHANGE : FIF_STANDARD;
1359        key                     = prepare_and_get_selected_itemfield(aw_root, query->awar_parskey, query->gb_main, selector, failMode);
1360        if (!key) error         = GB_await_error();
1361    }
1362
1363    if (!error) {
1364        GB_TYPES  key_type        = GBT_get_type_of_changekey(query->gb_main, key, selector.change_key_path);
1365        bool      safe_conversion = (key_type != GB_STRING) && !aw_root->awar(query->awar_acceptConvError)->read_int();
1366        char     *command         = aw_root->awar(query->awar_parsvalue)->read_string();
1367
1368        if (!strlen(command)) error = "Please enter a command";
1369
1370        if (!error) {
1371            long  ncount = aw_root->awar(query->awar_count)->read_int();
1372            char *deftag = aw_root->awar(query->awar_deftag)->read_string();
1373            char *tag    = aw_root->awar(query->awar_tag)->read_string();
1374
1375            {
1376                long use_tag = aw_root->awar(query->awar_use_tag)->read_int();
1377                if (!use_tag || !strlen(tag)) {
1378                    freenull(tag);
1379                }
1380            }
1381            int double_pars = aw_root->awar(query->awar_double_pars)->read_int();
1382
1383            arb_progress progress("Parse fields", ncount);
1384            QUERY_RANGE  range = (QUERY_RANGE)aw_root->awar(query->awar_where)->read_int();
1385            GBL_env      env(query->gb_main, query->get_tree_name());
1386
1387            for (GBDATA *gb_item_container = selector.get_first_item_container(query->gb_main, aw_root, range);
1388                 !error && gb_item_container;
1389                 gb_item_container = selector.get_next_item_container(gb_item_container, range))
1390            {
1391                for (GBDATA *gb_item = selector.get_first_item(gb_item_container, QUERY_ALL_ITEMS);
1392                     !error && gb_item;
1393                     gb_item = selector.get_next_item(gb_item, QUERY_ALL_ITEMS))
1394                {
1395                    if (IS_QUERIED(gb_item, query)) {
1396                        GBDATA *gb_new = GB_search(gb_item, key, GB_FIND);
1397                        error          = GB_incur_error_if(!gb_new);
1398
1399                        if (!error) {
1400                            GBL_call_env callEnv(gb_item, env);
1401
1402                            char *str    = gb_new ? GB_read_as_string(gb_new) : strdup("");
1403                            char *parsed = NULp;
1404
1405                            if (double_pars) {
1406                                char *com2 = GB_command_interpreter_in_env(str, command, callEnv);
1407                                if (com2) {
1408                                    if (tag) parsed = GBS_modify_tagged_string_with_ACI("", deftag, tag, com2, callEnv);
1409                                    else parsed     = GB_command_interpreter_in_env    ("", com2, callEnv);
1410
1411                                    free(com2);
1412                                }
1413                            }
1414                            else {
1415                                if (tag) parsed = GBS_modify_tagged_string_with_ACI(str, deftag, tag, command, callEnv);
1416                                else parsed     = GB_command_interpreter_in_env    (str, command, callEnv);
1417                            }
1418
1419                            if (!parsed) error = GB_await_error();
1420                            else {
1421                                if (strcmp(parsed, str) != 0) { // any change?
1422                                    if (gb_new && parsed[0] == 0) { // empty result -> delete field
1423                                        error = GB_delete(gb_new);
1424                                    }
1425                                    else {
1426                                        if (!gb_new) {
1427                                            gb_new = GB_search(gb_item, key, key_type);
1428                                            if (!gb_new) error = GB_await_error();
1429                                        }
1430                                        if (!error) {
1431                                            error = GB_write_autoconv_string(gb_new, parsed); // <- field is actually written HERE
1432                                            if (!error && safe_conversion) {
1433                                                dbq_assert(key_type != GB_STRING);
1434                                                char *resulting = GB_read_as_string(gb_new);
1435                                                if (!resulting) {
1436                                                    error = GB_await_error();
1437                                                }
1438                                                else {
1439                                                    if (strcmp(resulting, parsed) != 0) {
1440                                                        error = GBS_global_string("Conversion error: writing '%s'\n"
1441                                                                                  "resulted in '%s'\n"
1442                                                                                  "(mark checkbox to accept conversion errors)",
1443                                                                                  parsed, resulting);
1444                                                    }
1445                                                }
1446                                            }
1447                                        }
1448                                    }
1449                                }
1450                                free(parsed);
1451                            }
1452                            free(str);
1453                        }
1454
1455                        if (error) {
1456                            char *errCopy = strdup(error);
1457                            char *itemId  = selector.generate_item_id(query->gb_main, gb_item);
1458
1459                            error = GBS_global_string("While modifying field of %s '%s':\n%s",
1460                                                      selector.item_name, itemId, errCopy);
1461
1462                            free(itemId);
1463                            free(errCopy);
1464                        }
1465
1466                        progress.inc_and_check_user_abort(error);
1467                    }
1468                }
1469            }
1470
1471            delete tag;
1472            free(deftag);
1473
1474            if (error) progress.done();
1475        }
1476
1477        free(command);
1478    }
1479
1480    error = GB_end_transaction(query->gb_main, error);
1481    aw_message_if(error);
1482}
1483
1484static void predef_prg(AW_root *aw_root, DbQuery *query) {
1485    char *str = aw_root->awar(query->awar_parspredefined)->read_string();
1486    char *brk = strchr(str, '#');
1487    if (brk) {
1488        *(brk++) = 0;
1489        char *kv = str;
1490        if (!strcmp(str, "ali_*/data")) { // when the target field contains this string -> replace it with current alignment
1491            GB_transaction  ta(query->gb_main);
1492            char           *use = GBT_get_default_alignment(query->gb_main);
1493            if (!use) {
1494                aw_message(GB_await_error());
1495            }
1496            else {
1497                kv = GBS_global_string_copy("%s/data", use);
1498                free(use);
1499            }
1500        }
1501        aw_root->awar(query->awar_parskey)->write_string(kv);
1502        if (kv != str) free(kv);
1503        aw_root->awar(query->awar_parsvalue)->write_string(brk);
1504    }
1505    else {
1506        aw_root->awar(query->awar_parsvalue)->write_string(str);
1507    }
1508    free(str);
1509}
1510
1511static void colorize_queried_cb(AW_window *, DbQuery *query) {
1512    ItemSelector&   selector    = query->selector;
1513    GB_transaction  ta(query->gb_main);
1514    GB_ERROR        error       = NULp;
1515    AW_root        *aw_root     = query->aws->get_root();
1516    int             color_group = aw_root->awar(AWAR_COLORIZE)->read_int();
1517    QUERY_RANGE     range       = (QUERY_RANGE)aw_root->awar(query->awar_where)->read_int();
1518    bool            changed     = false;
1519
1520    for (GBDATA *gb_item_container = selector.get_first_item_container(query->gb_main, aw_root, range);
1521         !error && gb_item_container;
1522         gb_item_container = selector.get_next_item_container(gb_item_container, range))
1523    {
1524        for (GBDATA *gb_item = selector.get_first_item(gb_item_container, QUERY_ALL_ITEMS);
1525             !error && gb_item;
1526             gb_item       = selector.get_next_item(gb_item, QUERY_ALL_ITEMS))
1527        {
1528            if (IS_QUERIED(gb_item, query)) {
1529                error               = GBT_set_color_group(gb_item, color_group);
1530                if (!error) changed = true;
1531            }
1532        }
1533    }
1534
1535    if (error) GB_export_error(error);
1536    else if (changed) selector.trigger_display_refresh();
1537}
1538
1539static void colorize_marked_cb(AW_window *aww, BoundItemSel *cmd) {
1540    ItemSelector&   sel         = cmd->selector;
1541    GB_transaction  ta(cmd->gb_main);
1542    GB_ERROR        error       = NULp;
1543    AW_root        *aw_root     = aww->get_root();
1544    int             color_group = aw_root->awar(AWAR_COLORIZE)->read_int();
1545    QUERY_RANGE     range       = QUERY_ALL_ITEMS;         // @@@ FIXME: make customizable
1546
1547    for (GBDATA *gb_item_container = sel.get_first_item_container(cmd->gb_main, aw_root, range);
1548         !error && gb_item_container;
1549         gb_item_container = sel.get_next_item_container(gb_item_container, range))
1550    {
1551        for (GBDATA *gb_item = sel.get_first_item(gb_item_container, QUERY_ALL_ITEMS);
1552             !error && gb_item;
1553             gb_item       = sel.get_next_item(gb_item, QUERY_ALL_ITEMS))
1554        {
1555            if (GB_read_flag(gb_item)) {
1556                error = GBT_set_color_group(gb_item, color_group);
1557            }
1558        }
1559    }
1560
1561    if (error) GB_export_error(error);
1562}
1563
1564enum mark_mode {
1565    UNMARK,
1566    MARK,
1567    INVERT,
1568};
1569
1570static void mark_colored_cb(AW_window *aww, BoundItemSel *cmd, mark_mode mode) {
1571    // @@@ mark_colored_cb is obsolete! (will be replaced by dynamic coloring in the future)
1572    ItemSelector&  sel         = cmd->selector;
1573    AW_root       *aw_root     = aww->get_root();
1574    int            color_group = aw_root->awar(AWAR_COLORIZE)->read_int();
1575    QUERY_RANGE    range       = QUERY_ALL_ITEMS;          // @@@ FIXME: make customizable
1576
1577    GB_transaction ta(cmd->gb_main);
1578
1579    for (GBDATA *gb_item_container = sel.get_first_item_container(cmd->gb_main, aw_root, range);
1580         gb_item_container;
1581         gb_item_container = sel.get_next_item_container(gb_item_container, range))
1582    {
1583        for (GBDATA *gb_item = sel.get_first_item(gb_item_container, QUERY_ALL_ITEMS);
1584             gb_item;
1585             gb_item = sel.get_next_item(gb_item, QUERY_ALL_ITEMS))
1586        {
1587            int my_color = GBT_get_color_group(gb_item);
1588            if (my_color == color_group) {
1589                bool marked = GB_read_flag(gb_item);
1590
1591                switch (mode) {
1592                    case UNMARK: marked = 0;       break;
1593                    case MARK:   marked = 1;       break;
1594                    case INVERT: marked = !marked; break;
1595
1596                    default: dbq_assert(0); break;
1597                }
1598
1599                GB_write_flag(gb_item, marked);
1600            }
1601        }
1602    }
1603
1604}
1605
1606// --------------------------------------------------------------------------------
1607// color sets
1608
1609class colorset_data {
1610    BoundItemSel      *bsel;
1611    AW_selection_list *colorsets;
1612
1613public:
1614    colorset_data(BoundItemSel *bsel_) : bsel(bsel_), colorsets(NULp) { }
1615
1616    BoundItemSel *get_selector() { return bsel; }
1617
1618    const char *get_items_name() const { return bsel->selector.items_name; }
1619    const char *get_items_key() const { return ITEMS_get_key(bsel->selector); }
1620    const char *get_items_type_key() const { return ITEMS_get_type_key(bsel->selector.type); }
1621
1622    GBDATA *get_db_root_container() const { return GBT_colorset_root(bsel->gb_main, get_items_type_key()); }
1623
1624    void update_selection_list() const {
1625        dbq_assert(colorsets);
1626
1627        ConstStrArray foundSets;
1628        {
1629            GB_transaction ta(bsel->gb_main);
1630            GBT_get_colorset_names(foundSets, get_db_root_container());
1631        }
1632
1633        colorsets->clear();
1634        colorsets->insert_default("<new colorset>", "");
1635        for (size_t i = 0; i<foundSets.size(); ++i) {
1636            colorsets->insert(foundSets[i], foundSets[i]);
1637        }
1638        colorsets->update();
1639    }
1640
1641    void create_selection_list(AW_window *aws) {
1642        dbq_assert(!colorsets);
1643        colorsets = awt_create_selection_list_with_input_field(aws, AWAR_COLOR_LOADSAVE_NAME, "list", "name");
1644        update_selection_list();
1645    }
1646};
1647
1648static void colorset_changed_cb(GBDATA*, const colorset_data *csd, GB_CB_TYPE cbt) {
1649    if (cbt&GB_CB_CHANGED) {
1650        csd->update_selection_list();
1651    }
1652}
1653
1654static void create_colorset_representation(BoundItemSel *bsel, AW_root *aw_root, StrArray& colordefs, GB_ERROR& error) {
1655    ItemSelector&  sel     = bsel->selector;
1656    GBDATA        *gb_main = bsel->gb_main;
1657
1658    dbq_assert(!error);
1659
1660    for (GBDATA *gb_item_container = sel.get_first_item_container(gb_main, aw_root, QUERY_ALL_ITEMS);
1661         gb_item_container;
1662         gb_item_container = sel.get_next_item_container(gb_item_container, QUERY_ALL_ITEMS))
1663    {
1664        for (GBDATA *gb_item = sel.get_first_item(gb_item_container, QUERY_ALL_ITEMS);
1665             gb_item;
1666             gb_item = sel.get_next_item(gb_item, QUERY_ALL_ITEMS))
1667        {
1668            int color = GBT_get_color_group(gb_item);
1669            if (color>0) {
1670                char *id        = sel.generate_item_id(gb_main, gb_item);
1671                char *color_def = GBS_global_string_copy("%s=%i", id, color);
1672
1673                colordefs.put(color_def);
1674                free(id);
1675            }
1676        }
1677    }
1678
1679    if (colordefs.empty()) {
1680        error = GBS_global_string("Could not find any colored %s", sel.items_name);
1681    }
1682}
1683
1684static GB_ERROR clear_all_colors(BoundItemSel *bsel, AW_root *aw_root) {
1685    ItemSelector& sel     = bsel->selector;
1686    GB_ERROR      error   = NULp;
1687    bool          changed = false;
1688
1689    for (GBDATA *gb_item_container = sel.get_first_item_container(bsel->gb_main, aw_root, QUERY_ALL_ITEMS);
1690         !error && gb_item_container;
1691         gb_item_container = sel.get_next_item_container(gb_item_container, QUERY_ALL_ITEMS))
1692    {
1693        for (GBDATA *gb_item = sel.get_first_item(gb_item_container, QUERY_ALL_ITEMS);
1694             !error && gb_item;
1695             gb_item = sel.get_next_item(gb_item, QUERY_ALL_ITEMS))
1696        {
1697            error               = GBT_set_color_group(gb_item, 0); // clear colors
1698            if (!error) changed = true;
1699        }
1700    }
1701
1702    if (changed && !error) sel.trigger_display_refresh();
1703    return error;
1704}
1705
1706static void clear_all_colors_cb(AW_window *aww, BoundItemSel *bsel) {
1707    GB_transaction ta(bsel->gb_main);
1708    GB_ERROR       error = clear_all_colors(bsel, aww->get_root());
1709
1710    if (error) {
1711        error = ta.close(error);
1712        aw_message(error);
1713    }
1714}
1715
1716static GB_ERROR restore_colorset_representation(BoundItemSel *bsel, CharPtrArray& colordefs) {
1717    ItemSelector&  sel     = bsel->selector;
1718    GBDATA        *gb_main = bsel->gb_main;
1719    bool           changed = false;
1720    GB_ERROR       error   = NULp;
1721    int            ignores = 0;
1722
1723    for (size_t d = 0; d<colordefs.size() && !error; ++d) {
1724        const char *def   = colordefs[d];
1725        const char *equal = strchr(def, '=');
1726
1727        if (equal) {
1728            LocallyModify<char> tempSplit(const_cast<char*>(equal)[0], 0);
1729
1730            const char *id      = def;
1731            GBDATA     *gb_item = sel.find_item_by_id(gb_main, id);
1732            if (!gb_item) {
1733                aw_message(GBS_global_string("No such %s: '%s' (ignored)", sel.item_name, id)); // only warn
1734                ++ignores;
1735            }
1736            else {
1737                int color_group = atoi(equal+1);
1738                if (color_group>0) { // bugfix: saved color groups contained zero (which means "no color") by mistake; ignore
1739                    error = GBT_set_color_group(gb_item, color_group);
1740                    if (!error) changed = true;
1741                }
1742            }
1743        }
1744        else {
1745            aw_message(GBS_global_string("Invalid colordef '%s' (ignored)", def));
1746            ++ignores;
1747        }
1748    }
1749    if (changed && !error) sel.trigger_display_refresh();
1750    if (ignores>0 && !error) {
1751        aw_message(GBS_global_string("Warning: failed to restore color assignment for %i %s", ignores, sel.items_name));
1752    }
1753
1754    return error;
1755}
1756
1757enum loadsave_mode {
1758    SAVE,
1759    LOAD,
1760    OVERLAY,
1761    DELETE,
1762};
1763
1764static void loadsave_colorset_cb(AW_window *aws, colorset_data *csd, loadsave_mode mode) {
1765    AW_root  *aw_root = aws->get_root();
1766    char     *name    = aw_root->awar(AWAR_COLOR_LOADSAVE_NAME)->read_string();
1767    GB_ERROR  error   = NULp;
1768
1769    if (name[0] == 0) error = "Please enter a name for the colorset.";
1770    else {
1771        BoundItemSel   *bsel = csd->get_selector();
1772        GB_transaction  ta(bsel->gb_main);
1773
1774        GBDATA *gb_colorset_root = csd->get_db_root_container();
1775        GBDATA *gb_colorset      = gb_colorset_root ? GBT_find_colorset(gb_colorset_root, name) : NULp;
1776
1777        error = GB_incur_error();
1778        if (!error) {
1779            if (mode == SAVE) {
1780                if (!gb_colorset) { // create new colorset
1781                    gb_colorset             = GBT_find_or_create_colorset(gb_colorset_root, name);
1782                    if (!gb_colorset) error = GB_await_error();
1783                }
1784                dbq_assert(gb_colorset || error);
1785
1786                if (!error) {
1787                    StrArray colordefs;
1788                    create_colorset_representation(bsel, aw_root, colordefs, error);
1789                    if (!error) error = GBT_save_colorset(gb_colorset, colordefs);
1790                }
1791            }
1792            else {
1793                if (!gb_colorset) error = GBS_global_string("Colorset '%s' not found", name);
1794                else {
1795                    if (mode == LOAD || mode == OVERLAY) {
1796                        ConstStrArray colordefs;
1797                        error = GBT_load_colorset(gb_colorset, colordefs);
1798
1799                        if (!error && colordefs.empty()) error = "oops.. empty colorset";
1800                        if (!error && mode == LOAD)      error = clear_all_colors(bsel, aw_root);
1801                        if (!error)                      error = restore_colorset_representation(bsel, colordefs);
1802                    }
1803                    else {
1804                        dbq_assert(mode == DELETE);
1805                        error = GB_delete(gb_colorset);
1806                    }
1807                }
1808            }
1809        }
1810        error = ta.close(error);
1811    }
1812    free(name);
1813
1814    if (error) aw_message(error);
1815}
1816
1817static AW_window *create_loadsave_colored_window(AW_root *aw_root, colorset_data *csd) {
1818    AW_window_simple *aws = new AW_window_simple;
1819    {
1820        char *window_id = GBS_global_string_copy("colorset_loadsave_%s", csd->get_items_key());
1821        aws->init(aw_root, window_id, GBS_global_string("Load/Save %s colorset", csd->get_items_name()));
1822        free(window_id);
1823    }
1824
1825    aws->load_xfig("query/color_loadsave.fig");
1826
1827    aws->at("close");
1828    aws->callback(AW_POPDOWN);
1829    aws->create_button("CLOSE", "CLOSE", "C");
1830
1831    aws->at("help");
1832    aws->callback(makeHelpCallback("color_loadsave.hlp"));
1833    aws->create_button("HELP", "HELP", "H");
1834
1835    csd->create_selection_list(aws);
1836
1837    aws->at("save");    aws->callback(makeWindowCallback(loadsave_colorset_cb, csd, SAVE));    aws->create_button("save",    "Save",    "S");
1838    aws->at("load");    aws->callback(makeWindowCallback(loadsave_colorset_cb, csd, LOAD));    aws->create_button("load",    "Load",    "L");
1839    aws->at("overlay"); aws->callback(makeWindowCallback(loadsave_colorset_cb, csd, OVERLAY)); aws->create_button("overlay", "Overlay", "O");
1840    aws->at("delete");  aws->callback(makeWindowCallback(loadsave_colorset_cb, csd, DELETE));  aws->create_button("delete",  "Delete",  "D");
1841
1842    aws->at("reset");
1843    aws->callback(makeWindowCallback(clear_all_colors_cb, csd->get_selector()));
1844    aws->create_button("reset", "Reset", "R");
1845
1846    // callbacks
1847    {
1848        GB_transaction  ta(csd->get_selector()->gb_main);
1849        GBDATA         *gb_colorset = csd->get_db_root_container();
1850        GB_add_callback(gb_colorset, GB_CB_CHANGED, makeDatabaseCallback(colorset_changed_cb, csd));
1851    }
1852
1853    return aws;
1854}
1855
1856static AW_window *create_colorize_window(AW_root *aw_root, GBDATA *gb_main, DbQuery *query, ItemSelector *sel) {
1857    // invoked by   'colorize listed'                   (sel   != 0)
1858    // and          'colorize marked/mark colored'      (query != 0)
1859
1860    enum { COLORIZE_INVALID, COLORIZE_LISTED, COLORIZE_MARKED } mode = COLORIZE_INVALID;
1861
1862    create_query_independent_awars(aw_root, AW_ROOT_DEFAULT);
1863
1864    AW_window_simple *aws = new AW_window_simple;
1865
1866    dbq_assert(contradicted(query, sel));
1867
1868    if (query) {
1869        dbq_assert(mode == COLORIZE_INVALID);
1870        mode = COLORIZE_LISTED;
1871    }
1872    if (sel) {
1873        dbq_assert(mode == COLORIZE_INVALID);
1874        mode = COLORIZE_MARKED;
1875    }
1876    dbq_assert(!(mode == COLORIZE_INVALID));
1877
1878    ItemSelector& Sel  = mode == COLORIZE_LISTED ? query->selector : *sel;
1879    const char   *what = mode == COLORIZE_LISTED ? "listed" : "marked";
1880
1881    {
1882        char *id           = GBS_global_string_copy("COLORIZE_%s", what);
1883        char *title_format = GBS_global_string_copy("Colorize %s %%s", what);
1884
1885        init_item_specific_window(aw_root, aws, Sel, id, true, title_format, true);
1886
1887        free(title_format);
1888        free(id);
1889    }
1890
1891    aws->load_xfig("query/colorize.fig");
1892
1893    aws->auto_space(10, 10);
1894
1895    aws->at("close");
1896    aws->callback(AW_POPDOWN);
1897    aws->create_button("CLOSE", "CLOSE", "C");
1898
1899    aws->at("help");
1900    aws->callback(makeHelpCallback(mode == COLORIZE_LISTED ? "set_color_of_listed.hlp" : "colorize.hlp"));
1901    aws->create_button("HELP", "HELP", "H");
1902
1903    aws->at("colorize");
1904
1905    BoundItemSel *bsel = new BoundItemSel(gb_main, ((mode == COLORIZE_MARKED) ? *sel : query->selector)); // do not free, bound to CB
1906
1907    if (mode == COLORIZE_LISTED) aws->callback(makeWindowCallback(colorize_queried_cb, query));
1908    else                         aws->callback(makeWindowCallback(colorize_marked_cb, bsel));
1909
1910    aws->create_autosize_button("COLORIZE", GBS_global_string_copy("Set color of %s %s to ...", what, Sel.items_name), "S", 2);
1911
1912    {
1913        int color_group;
1914
1915        aws->create_option_menu(AWAR_COLORIZE);
1916        aws->insert_default_option("No color group", "none", 0);
1917        for (color_group = 1; color_group <= AW_COLOR_GROUPS; ++color_group) {
1918            char *name = AW_get_color_group_name(aw_root, color_group);
1919            aws->insert_option(name, "", color_group);
1920            free(name);
1921        }
1922        aws->update_option_menu();
1923    }
1924
1925    colorset_data *csd = new colorset_data(bsel); // do not free, bound to CB
1926
1927    aws->at("loadsave");
1928    aws->callback(makeCreateWindowCallback(create_loadsave_colored_window, csd));
1929    aws->create_autosize_button("LOADSAVE_COLORED", "Load/Save", "L");
1930
1931    if (mode == COLORIZE_MARKED) {
1932        aws->at("mark");
1933        aws->callback(makeWindowCallback(mark_colored_cb, bsel, MARK));
1934        aws->create_autosize_button("MARK_COLORED", GBS_global_string_copy("Mark all %s of ...", Sel.items_name), "M", 2);
1935
1936        aws->at("unmark");
1937        aws->callback(makeWindowCallback(mark_colored_cb, bsel, UNMARK));
1938        aws->create_autosize_button("UNMARK_COLORED", GBS_global_string_copy("Unmark all %s of ...", Sel.items_name), "U", 2);
1939
1940        aws->at("invert");
1941        aws->callback(makeWindowCallback(mark_colored_cb, bsel, INVERT));
1942        aws->create_autosize_button("INVERT_COLORED", GBS_global_string_copy("Invert all %s of ...", Sel.items_name), "I", 2);
1943    }
1944
1945    aws->at_newline();
1946    aws->window_fit();
1947
1948    return aws;
1949}
1950
1951static AW_window *create_colorize_queried_window(AW_root *aw_root, DbQuery *query) {
1952    return create_colorize_window(aw_root, query->gb_main, query, NULp);
1953}
1954
1955AW_window *QUERY::create_colorize_items_window(AW_root *aw_root, GBDATA *gb_main, ItemSelector& sel) {
1956    return create_colorize_window(aw_root, gb_main, NULp, &sel);
1957}
1958
1959static void setup_modify_fields_config(AWT_config_definition& cdef, const DbQuery *query) {
1960    const char *typeawar = get_itemfield_type_awarname(query->awar_parskey);
1961    dbq_assert(typeawar);
1962
1963    cdef.add(query->awar_parskey,         "key");
1964    cdef.add(typeawar,                    "type");
1965    cdef.add(query->awar_use_tag,         "usetag");
1966    cdef.add(query->awar_deftag,          "deftag");
1967    cdef.add(query->awar_tag,             "modtag");
1968    cdef.add(query->awar_double_pars,     "doubleparse");
1969    cdef.add(query->awar_acceptConvError, "acceptconv");
1970    cdef.add(query->awar_acceptIdMod,     "acceptidmod");
1971    cdef.add(query->awar_parsvalue,       "aci");
1972}
1973
1974static AW_window *create_modify_fields_window(AW_root *aw_root, DbQuery *query) {
1975    AW_window_simple *aws = new AW_window_simple;
1976
1977    init_item_specific_window(aw_root, aws, query->selector, "MODIFY_DATABASE_FIELD", true, "MODIFY DATABASE FIELD of listed %s", true);
1978
1979    aws->load_xfig("query/modify_fields.fig");
1980
1981    aws->at("close");
1982    aws->callback(AW_POPDOWN);
1983    aws->create_button("CLOSE", "CLOSE", "C");
1984
1985    aws->at("help");
1986    aws->callback(makeHelpCallback("mod_field_list.hlp"));
1987    aws->create_button("HELP", "HELP", "H");
1988
1989    aws->at("helptags");
1990    aws->callback(makeHelpCallback("tags.hlp"));
1991    aws->create_button("HELP_TAGS", "Help tags", "H");
1992
1993    create_itemfield_selection_button(aws, FieldSelDef(query->awar_parskey, query->gb_main, query->selector, FIELD_FILTER_STRING_READABLE, "target field", SF_ALLOW_NEW), "field");
1994    aws->at("acceptErr");   aws->create_toggle(query->awar_acceptConvError);
1995
1996    // ------------------------
1997    //      expert section
1998    aws->at("usetag");   aws->create_toggle     (query->awar_use_tag);
1999    aws->at("deftag");   aws->create_input_field(query->awar_deftag);
2000    aws->at("tag");      aws->create_input_field(query->awar_tag);
2001    aws->at("double");   aws->create_toggle     (query->awar_double_pars);
2002    aws->at("acceptID"); aws->create_toggle     (query->awar_acceptIdMod);
2003    // ------------------------
2004
2005    aws->at("parser");
2006    aws->create_text_field(query->awar_parsvalue);
2007
2008    aws->at("go");
2009    aws->callback(makeWindowCallback(modify_fields_of_queried_cb, query));
2010    aws->create_button("GO", "GO", "G");
2011
2012    aws->at("pre");
2013    AW_selection_list *programs = aws->create_selection_list(query->awar_parspredefined);
2014
2015    const char *sellst = NULp;
2016    switch (query->selector.type) {
2017        case QUERY_ITEM_SPECIES:
2018        case QUERY_ITEM_ORGANISM:    sellst = "mod_fields*.sellst"; break;
2019        case QUERY_ITEM_GENE:        sellst = "mod_gene_fields*.sellst"; break;
2020        case QUERY_ITEM_EXPERIMENT:  sellst = "mod_experiment_fields*.sellst"; break;
2021        case QUERY_ITEM_SAI:  dbq_assert(0); break; // search+query not fit for SAI
2022    }
2023
2024    GB_ERROR error;
2025    if (sellst) {
2026        StorableSelectionList storable_sellist(TypedSelectionList("sellst", programs, "field modification scripts", "mod_fields"));
2027        error = storable_sellist.load(GB_concat_path_in_ARBLIB("sellists", sellst), false);
2028    }
2029    else {
2030        error = "No default selection list for query-type";
2031    }
2032
2033    if (error) {
2034        aw_message(error);
2035    }
2036    else {
2037        aws->get_root()->awar(query->awar_parspredefined)->add_callback(makeRootCallback(predef_prg, query));
2038    }
2039
2040    aws->at("config");
2041    const char *item_name = ITEMS_get_possible_former_itemname(query->selector);
2042    AWT_define_config_manager_conversion(GBS_global_string("mod_%s_fields", item_name), "mod_fields", item_name);
2043    AWT_insert_config_manager(aws, AW_ROOT_DEFAULT, "mod_fields", makeConfigSetupCallback(setup_modify_fields_config, query));
2044
2045    return aws;
2046}
2047
2048static void set_field_of_queried_cb(AW_window*, DbQuery *query, bool append) {
2049    // set fields of listed items
2050    ItemSelector&  selector   = query->selector;
2051    GB_ERROR       error      = NULp;
2052    AW_root       *aw_root    = query->aws->get_root();
2053    bool           allow_loss = aw_root->awar(query->awar_writelossy)->read_int();
2054
2055    char *value = aw_root->awar(query->awar_setvalue)->read_string();
2056    if (value[0] == 0) freenull(value);
2057
2058    size_t value_len = value ? strlen(value) : 0;
2059
2060    GB_begin_transaction(query->gb_main);
2061
2062    const char *key = prepare_and_get_selected_itemfield(aw_root, query->awar_writekey, query->gb_main, selector);
2063    if (!key) error = GB_await_error();
2064
2065    for (GBDATA *gb_item_container = selector.get_first_item_container(query->gb_main, aw_root, QUERY_ALL_ITEMS);
2066         !error && gb_item_container;
2067         gb_item_container = selector.get_next_item_container(gb_item_container, QUERY_ALL_ITEMS))
2068    {
2069        for (GBDATA *gb_item = selector.get_first_item(gb_item_container, QUERY_ALL_ITEMS);
2070             !error && gb_item;
2071             gb_item = selector.get_next_item(gb_item, QUERY_ALL_ITEMS))
2072        {
2073            if (IS_QUERIED(gb_item, query)) {
2074                if (!value) { // empty value -> delete field
2075                    if (!append) {
2076                        GBDATA *gb_field    = GB_search(gb_item, key, GB_FIND);
2077                        if (gb_field) error = GB_delete(gb_field);
2078                    }
2079                    // otherwise "nothing" is appended (=noop)
2080                }
2081                else {
2082                    GBDATA *gb_field     = GBT_searchOrCreate_itemfield_according_to_changekey(gb_item, key, selector.change_key_path);
2083                    if (!gb_field) error = GB_await_error();
2084                    else {
2085                        const char   *new_content = value;
2086                        SmartCharPtr  content;
2087
2088                        if (append) {
2089                            char *old       = GB_read_as_string(gb_field);
2090                            if (!old) error = "field has incompatible type";
2091                            else {
2092                                size_t        old_len = strlen(old);
2093                                GBS_strstruct buf(old_len+value_len+2);
2094
2095                                buf.ncat(old, old_len);
2096                                buf.ncat(value, value_len);
2097
2098                                content     = buf.release();
2099                                new_content = &*content;
2100                            }
2101                        }
2102
2103                        error = GB_write_autoconv_string(gb_field, new_content);
2104
2105                        if (!allow_loss && !error) {
2106                            char *result = GB_read_as_string(gb_field);
2107                            dbq_assert(result);
2108
2109                            if (strcmp(new_content, result) != 0) {
2110                                error = GBS_global_string("value modified by type conversion\n('%s' -> '%s')", new_content, result);
2111                            }
2112                            free(result);
2113                        }
2114                    }
2115                }
2116
2117                if (error) { // add name of current item to error
2118                    const char *name = GBT_read_char_pntr(gb_item, "name");
2119                    error            = GBS_global_string("Error while writing to %s '%s':\n%s", selector.item_name, name, error);
2120                }
2121            }
2122        }
2123    }
2124
2125    GB_end_transaction_show_error(query->gb_main, error, aw_message);
2126
2127    free(value);
2128}
2129
2130static AW_window *create_writeFieldOfListed_window(AW_root *aw_root, DbQuery *query) {
2131    AW_window_simple *aws = new AW_window_simple;
2132    init_item_specific_window(aw_root, aws, query->selector, "WRITE_DB_FIELD_OF_LISTED", false, "Write field of listed %s", true);
2133    aws->load_xfig("query/write_fields.fig");
2134
2135    aws->at("close");
2136    aws->callback(AW_POPDOWN);
2137    aws->create_button("CLOSE", "CLOSE", "C");
2138
2139    aws->at("help");
2140    aws->callback(makeHelpCallback("write_field_list.hlp"));
2141    aws->create_button("HELP", "HELP", "H");
2142
2143    create_itemfield_selection_button(aws, FieldSelDef(query->awar_writekey, query->gb_main, query->selector, FIELD_FILTER_STRING_READABLE, "target field", SF_ALLOW_NEW), "field");
2144
2145    aws->at("val");
2146    aws->create_text_field(query->awar_setvalue, 2, 2);
2147
2148    aws->at("lossy");
2149    aws->label("Allow lossy conversion?");
2150    aws->create_toggle(query->awar_writelossy);
2151
2152    aws->at("create");
2153    aws->callback(makeWindowCallback(set_field_of_queried_cb, query, false));
2154    aws->create_button("SET_SINGLE_FIELD_OF_LISTED", "WRITE");
2155
2156    aws->at("do");
2157    aws->callback(makeWindowCallback(set_field_of_queried_cb, query, true));
2158    aws->create_button("APPEND_SINGLE_FIELD_OF_LISTED", "APPEND");
2159
2160    return aws;
2161}
2162
2163static void set_protection_of_queried_cb(AW_window*, DbQuery *query) {
2164    // set protection of listed items
2165    ItemSelector&  selector = query->selector;
2166    AW_root       *aw_root  = query->aws->get_root();
2167    char          *key      = aw_root->awar(query->awar_protectkey)->read_string();
2168
2169    if (strcmp(key, NO_FIELD_SELECTED) == 0) {
2170        aw_message("Please select a field for which you want to set the protection");
2171    }
2172    else {
2173        GB_begin_transaction(query->gb_main);
2174
2175        GB_ERROR  error       = NULp;
2176        GBDATA   *gb_key_data = GB_search(query->gb_main, selector.change_key_path, GB_CREATE_CONTAINER);
2177        GBDATA   *gb_key_name = GB_find_string(gb_key_data, CHANGEKEY_NAME, key, GB_IGNORE_CASE, SEARCH_GRANDCHILD);
2178
2179        if (!gb_key_name) {
2180            error = GBS_global_string("The destination field '%s' does not exists", key);
2181        }
2182        else {
2183            int         level = aw_root->awar(query->awar_setprotection)->read_int();
2184            QUERY_RANGE range = (QUERY_RANGE)aw_root->awar(query->awar_where)->read_int();
2185
2186            for (GBDATA *gb_item_container = selector.get_first_item_container(query->gb_main, aw_root, range);
2187                 !error && gb_item_container;
2188                 gb_item_container = selector.get_next_item_container(gb_item_container, range))
2189            {
2190                for (GBDATA *gb_item = selector.get_first_item(gb_item_container, QUERY_ALL_ITEMS);
2191                     !error && gb_item;
2192                     gb_item = selector.get_next_item(gb_item, QUERY_ALL_ITEMS))
2193                {
2194                    if (IS_QUERIED(gb_item, query)) {
2195                        GBDATA *gb_new = GB_search(gb_item, key, GB_FIND);
2196                        if (gb_new) {
2197                            error             = GB_write_security_delete(gb_new, level);
2198                            if (!error) error = GB_write_security_write(gb_new, level);
2199                        }
2200                    }
2201                }
2202            }
2203        }
2204        GB_end_transaction_show_error(query->gb_main, error, aw_message);
2205    }
2206
2207    free(key);
2208}
2209
2210static AW_window *create_set_protection_window(AW_root *aw_root, DbQuery *query) {
2211    AW_window_simple *aws = new AW_window_simple;
2212    init_item_specific_window(aw_root, aws, query->selector, "SET_PROTECTION_OF_FIELD_OF_LISTED", false, "Protect field of listed %s", true);
2213    aws->load_xfig("query/set_protection.fig");
2214
2215    aws->at("close");
2216    aws->callback(AW_POPDOWN);
2217    aws->create_button("CLOSE", "CLOSE", "C");
2218
2219    aws->at("help");
2220    aws->callback(makeHelpCallback("set_protection.hlp"));
2221    aws->create_button("HELP", "HELP", "H");
2222
2223
2224    aws->at("prot");
2225    aws->create_toggle_field(query->awar_setprotection, AW_VERTICAL);
2226    aws->insert_toggle("0 temporary", "0", 0);
2227    aws->insert_toggle("1 checked",   "1", 1);
2228    aws->insert_toggle("2",           "2", 2);
2229    aws->insert_toggle("3",           "3", 3);
2230    aws->insert_toggle("4 normal",    "4", 4);
2231    aws->insert_toggle("5 ",          "5", 5);
2232    aws->insert_toggle("6 the truth", "5", 6);
2233    aws->update_toggle_field();
2234
2235    create_itemfield_selection_list(aws, FieldSelDef(query->awar_protectkey, query->gb_main, query->selector, FIELD_UNFILTERED, "field to protect"), "list");
2236
2237    aws->at("go");
2238    aws->callback(makeWindowCallback(set_protection_of_queried_cb, query));
2239    aws->create_autosize_button("SET_PROTECTION_OF_FIELD_OF_LISTED", "Assign\nprotection\nto field\nof listed");
2240
2241    return aws;
2242}
2243
2244static void toggle_flag_cb(AW_window *aww, DbQuery *query) {
2245    GB_transaction  ta(query->gb_main);
2246    GBDATA         *gb_item = query->selector.get_selected_item(query->gb_main, aww->get_root());
2247    if (gb_item) {
2248        long flag = GB_read_flag(gb_item);
2249        GB_write_flag(gb_item, 1-flag);
2250    }
2251    DbQuery_update_list(query);
2252}
2253
2254static void new_selection_made_cb(AW_root *aw_root, const char *awar_selection, DbQuery *query) {
2255    char *item_name = aw_root->awar(awar_selection)->read_as_string();
2256    query->selector.update_item_awars(query->gb_main, aw_root, item_name);
2257    free(item_name);
2258}
2259
2260static void query_box_setup_config(AWT_config_definition& cdef, DbQuery *query) {
2261    // this defines what is saved/restored to/from configs
2262    for (int key_id = 0; key_id<QUERY_EXPRESSIONS; ++key_id) {
2263        cdef.add(query->awar_keys[key_id], "key", key_id);
2264        cdef.add(query->awar_queries[key_id], "query", key_id);
2265        cdef.add(query->awar_not[key_id], "not", key_id);
2266        cdef.add(query->awar_operator[key_id], "operator", key_id);
2267    }
2268}
2269
2270template<typename CB>
2271static void query_rel_menu_entry(AW_window *aws, const char *id, const char *query_id, const char* label, const char *mnemonic, const char *helpText, AW_active Mask, const CB& cb) {
2272    char *rel_id = GBS_global_string_copy("%s_%s", query_id, id);
2273    aws->insert_menu_topic(rel_id, label, mnemonic, helpText, Mask, cb);
2274    free(rel_id);
2275}
2276
2277const char *DbQuery::get_tree_name() const {
2278    if (!awar_tree_name)
2279        return NULp;
2280    else
2281        return aws->get_root()->awar(awar_tree_name)->read_char_pntr();
2282}
2283
2284DbQuery::~DbQuery() {
2285    for (int s = 0; s<QUERY_EXPRESSIONS; ++s) {
2286        free(awar_keys[s]);
2287        free(awar_queries[s]);
2288        free(awar_not[s]);
2289        free(awar_operator[s]);
2290    }
2291
2292    free(species_name);
2293
2294    free(awar_writekey);
2295    free(awar_writelossy);
2296    free(awar_protectkey);
2297    free(awar_setprotection);
2298    free(awar_setvalue);
2299
2300    free(awar_parskey);
2301    free(awar_parsvalue);
2302    free(awar_parspredefined);
2303    free(awar_acceptConvError);
2304    free(awar_acceptIdMod);
2305
2306    free(awar_ere);
2307    free(awar_where);
2308    free(awar_by);
2309    free(awar_use_tag);
2310    free(awar_double_pars);
2311    free(awar_deftag);
2312    free(awar_tag);
2313    free(awar_count);
2314    free(awar_sort);
2315
2316    GBS_free_hash(hit_description);
2317}
2318
2319// ----------------------------------------
2320// store query data until DB close
2321
2322typedef Keeper<DbQuery*> QueryKeeper;
2323template<> void Keeper<DbQuery*>::destroy(DbQuery *q) { delete q; }
2324static SmartPtr<QueryKeeper> queryKeeper;
2325static void destroyKeptQueries() {
2326    queryKeeper.setNull();
2327}
2328static void keepQuery(GBDATA *gbmain, DbQuery *q) {
2329    if (queryKeeper.isNull()) {
2330        queryKeeper = new QueryKeeper;
2331        GB_atclose_callback(gbmain, makeDatabaseCallback(destroyKeptQueries));
2332    }
2333    queryKeeper->keep(q);
2334}
2335
2336// ----------------------------------------
2337
2338DbQuery *QUERY::create_query_box(AW_window *aws, query_spec *awtqs, const char *query_id) {
2339    char     buffer[256];
2340    AW_root *aw_root = aws->get_root();
2341    GBDATA  *gb_main = awtqs->gb_main;
2342    DbQuery *query   = new DbQuery(awtqs->get_queried_itemtype());
2343
2344    keepQuery(gb_main, query); // transfers ownership of query
2345
2346    // @@@ set all the things below via ctor!
2347    // @@@ create a copyable object containing everything query_spec and DbQuery have in common -> pass that object to DbQuery-ctor
2348
2349    query->gb_main                = awtqs->gb_main;
2350    query->aws                    = aws;
2351    query->gb_ref                 = awtqs->gb_ref;
2352    query->expect_hit_in_ref_list = awtqs->expect_hit_in_ref_list;
2353    query->select_bit             = awtqs->select_bit;
2354    query->species_name           = strdup(awtqs->species_name);
2355
2356    query->set_tree_awar_name(awtqs->tree_name);
2357    query->hit_description = GBS_create_dynaval_hash(query_count_items(query, QUERY_ALL_ITEMS, QUERY_GENERATE), GB_IGNORE_CASE, free_hit_description);
2358
2359    GB_push_transaction(gb_main);
2360
2361    // Create query box AWARS
2362    create_query_independent_awars(aw_root, AW_ROOT_DEFAULT);
2363
2364    {
2365        const char *default_key[QUERY_EXPRESSIONS+1] = { "name", "name", "name", NULp };
2366
2367        for (int key_id = 0; key_id<QUERY_EXPRESSIONS; ++key_id) {
2368            sprintf(buffer, "tmp/dbquery_%s/key_%i", query_id, key_id);
2369            query->awar_keys[key_id] = strdup(buffer);
2370            dbq_assert(default_key[key_id]);
2371            aw_root->awar_string(query->awar_keys[key_id], default_key[key_id], AW_ROOT_DEFAULT);
2372
2373            sprintf(buffer, "tmp/dbquery_%s/query_%i", query_id, key_id);
2374            query->awar_queries[key_id] = strdup(buffer);
2375            aw_root->awar_string(query->awar_queries[key_id], "*", AW_ROOT_DEFAULT);
2376
2377            sprintf(buffer, "tmp/dbquery_%s/not_%i", query_id, key_id);
2378            query->awar_not[key_id] = strdup(buffer);
2379            aw_root->awar_int(query->awar_not[key_id], 0, AW_ROOT_DEFAULT);
2380
2381            sprintf(buffer, "tmp/dbquery_%s/operator_%i", query_id, key_id);
2382            query->awar_operator[key_id] = strdup(buffer);
2383            aw_root->awar_string(query->awar_operator[key_id], "ign", AW_ROOT_DEFAULT);
2384        }
2385        aw_root->awar(query->awar_keys[0])->add_callback(makeRootCallback(first_searchkey_changed_cb, query));
2386    }
2387
2388    sprintf(buffer, "tmp/dbquery_%s/ere", query_id);
2389    query->awar_ere = strdup(buffer);
2390    aw_root->awar_int(query->awar_ere, QUERY_GENERATE);
2391
2392    sprintf(buffer, "tmp/dbquery_%s/where", query_id);
2393    query->awar_where = strdup(buffer);
2394    aw_root->awar_int(query->awar_where, QUERY_ALL_ITEMS);
2395
2396    sprintf(buffer, "tmp/dbquery_%s/count", query_id);
2397    query->awar_count = strdup(buffer);
2398    aw_root->awar_int(query->awar_count, 0);
2399
2400    sprintf(buffer, "tmp/dbquery_%s/sort", query_id);
2401    query->awar_sort = strdup(buffer);
2402    aw_root->awar_int(query->awar_sort, QUERY_SORT_NONE)->add_callback(makeRootCallback(result_sort_order_changed_cb, query));
2403    query->sort_mask = QUERY_SORT_NONE; // default to unsorted
2404
2405    sprintf(buffer, "tmp/dbquery_%s/by", query_id);
2406    query->awar_by = strdup(buffer);
2407    aw_root->awar_int(query->awar_by, QUERY_MATCH);
2408
2409    const char *items = query->selector.items_name;
2410
2411    if (awtqs->ere_pos_fig) {
2412        aws->at(awtqs->ere_pos_fig);
2413        aws->create_toggle_field(query->awar_ere);
2414
2415        aws->insert_toggle(GBS_global_string("Search %s", items), "G", (int)QUERY_GENERATE);
2416        aws->insert_toggle(GBS_global_string("Add %s",    items), "E", (int)QUERY_ENLARGE);
2417        aws->insert_toggle(GBS_global_string("Keep %s",   items), "R", (int)QUERY_REDUCE);
2418
2419        aws->update_toggle_field();
2420    }
2421    if (awtqs->where_pos_fig) {
2422        aws->at(awtqs->where_pos_fig);
2423        aws->create_toggle_field(query->awar_where);
2424        aws->insert_toggle("of current organism", "C", (int)QUERY_CURRENT_ITEM);
2425        aws->insert_toggle("of marked organisms", "M", (int)QUERY_MARKED_ITEMS);
2426        aws->insert_toggle("of all organisms", "A", (int)QUERY_ALL_ITEMS);
2427        aws->update_toggle_field();
2428
2429    }
2430    if (awtqs->by_pos_fig) {
2431        aws->at(awtqs->by_pos_fig);
2432        aws->create_toggle_field(query->awar_by);
2433        aws->insert_toggle("that match the query", "M", (int)QUERY_MATCH);
2434        aws->insert_toggle("that don't match the q.", "D", (int)QUERY_DONT_MATCH);
2435        aws->insert_toggle("that are marked", "A", (int)QUERY_MARKED);
2436        aws->update_toggle_field();
2437    }
2438
2439    // distances for multiple queries :
2440
2441#define KEY_Y_OFFSET 32
2442
2443    int xpos_calc[3] = { -1, -1, -1 }; // X-positions for elements in search expressions
2444
2445    if (awtqs->qbox_pos_fig) {
2446        aws->auto_space(1, 1);
2447        aws->at(awtqs->qbox_pos_fig);
2448
2449        SmartPtr<AW_at_storage> at_size(AW_at_storage::make(aws, AW_AT_SIZE_AND_ATTACH));
2450
2451        int xpos, ypos;
2452        aws->get_at_position(&xpos, &ypos);
2453
2454        int ypos_dummy;
2455
2456        for (int key = QUERY_EXPRESSIONS-1;  key >= 0; --key) {
2457            if (key) {
2458                aws->at(xpos, ypos+key*KEY_Y_OFFSET);
2459                aws->create_option_menu(query->awar_operator[key]);
2460                aws->insert_option("and", "", "and");
2461                aws->insert_option("or", "", "or");
2462                aws->insert_option("ign", "", "ign");
2463                aws->update_option_menu();
2464
2465                if (xpos_calc[0] == -1) aws->get_at_position(&xpos_calc[0], &ypos_dummy);
2466            }
2467
2468            aws->at(xpos_calc[0], ypos+key*KEY_Y_OFFSET);
2469            aws->restore_at_from(*at_size);
2470
2471            create_itemfield_selection_button(aws, FieldSelDef(query->awar_keys[key], gb_main, awtqs->get_queried_itemtype(), FIELD_FILTER_STRING_READABLE, "source field", SF_PSEUDO), NULp);
2472
2473            if (xpos_calc[1] == -1) aws->get_at_position(&xpos_calc[1], &ypos_dummy);
2474
2475            aws->at(xpos_calc[1], ypos+key*KEY_Y_OFFSET);
2476            aws->create_toggle(query->awar_not[key], "#equal.xpm", "#notEqual.xpm");
2477
2478            if (xpos_calc[2] == -1) aws->get_at_position(&xpos_calc[2], &ypos_dummy);
2479        }
2480    }
2481    if (awtqs->key_pos_fig) {
2482        aws->at(awtqs->key_pos_fig);
2483        aws->create_input_field(query->awar_keys[0], 12);
2484    }
2485
2486    if (awtqs->query_pos_fig) {
2487        aws->at(awtqs->query_pos_fig);
2488
2489        SmartPtr<AW_at_storage> at_size(AW_at_storage::make(aws, AW_AT_SIZE_AND_ATTACH));
2490
2491        int xpos, ypos;
2492        aws->get_at_position(&xpos, &ypos);
2493
2494        for (int key = 0; key<QUERY_EXPRESSIONS; ++key) {
2495            aws->at(xpos_calc[2], ypos+key*KEY_Y_OFFSET);
2496            aws->restore_at_from(*at_size);
2497            aws->d_callback(makeWindowCallback(perform_query_cb, query, EXT_QUERY_NONE)); // enable ENTER in searchfield to start search
2498            aws->create_input_field(query->awar_queries[key], 12);
2499        }
2500    }
2501
2502    if (awtqs->result_pos_fig) {
2503        aws->at(awtqs->result_pos_fig);
2504        aws->d_callback(makeWindowCallback(toggle_flag_cb, query));
2505
2506        {
2507            const char *this_awar_name = ARB_keep_string(GBS_global_string_copy("tmp/dbquery_%s/select", query_id)); // do not free this cause it's passed to new_selection_made_cb
2508            AW_awar    *awar           = aw_root->awar_string(this_awar_name, "", AW_ROOT_DEFAULT);
2509
2510            query->hitlist = aws->create_selection_list(this_awar_name, 5, 5);
2511            awar->add_callback(makeRootCallback(new_selection_made_cb, this_awar_name, query));
2512        }
2513        query->hitlist->insert_default("end of list", "");
2514        query->hitlist->update();
2515    }
2516
2517    if (awtqs->count_pos_fig) {
2518        aws->button_length(13);
2519        aws->at(awtqs->count_pos_fig);
2520        aws->label("Hits:");
2521        aws->create_button(NULp, query->awar_count, NULp, "+");
2522
2523        if (awtqs->popup_info_window) {
2524            aws->callback(RootAsWindowCallback::simple(awtqs->popup_info_window, query->gb_main));
2525            aws->create_button("popinfo", "Info");
2526        }
2527    }
2528    else {
2529        dbq_assert(!awtqs->popup_info_window); // dont know where to display
2530    }
2531
2532    if (awtqs->config_pos_fig) {
2533        aws->button_length(0);
2534        aws->at(awtqs->config_pos_fig);
2535        char *macro_id = GBS_global_string_copy("SAVELOAD_CONFIG_%s", query_id);
2536        AWT_insert_config_manager(aws, AW_ROOT_DEFAULT, "query_box", makeConfigSetupCallback(query_box_setup_config, query), macro_id);
2537        free(macro_id);
2538    }
2539
2540    aws->button_length(18);
2541
2542    if (awtqs->do_query_pos_fig) {
2543        aws->at(awtqs->do_query_pos_fig);
2544        aws->callback(makeWindowCallback(perform_query_cb, query, EXT_QUERY_NONE));
2545        char *macro_id = GBS_global_string_copy("SEARCH_%s", query_id);
2546        aws->create_button(macro_id, "Search", "S", "+");
2547        free(macro_id);
2548    }
2549    if (awtqs->do_refresh_pos_fig) {
2550        aws->at(awtqs->do_refresh_pos_fig);
2551        aws->create_option_menu(query->awar_sort);
2552        aws->insert_default_option("unsorted",  "u", QUERY_SORT_NONE);
2553        aws->insert_option        ("by value",  "v", QUERY_SORT_BY_1STFIELD_CONTENT);
2554        aws->insert_option        ("by number", "n", QUERY_SORT_NUM_BY_1STFIELD_CONTENT);
2555        aws->insert_option        ("by id",     "i", QUERY_SORT_BY_ID);
2556        if (query->selector.parent_selector) {
2557            aws->insert_option    ("by parent", "p", QUERY_SORT_BY_NESTED_PID);
2558        }
2559        aws->insert_option        ("by marked", "m", QUERY_SORT_BY_MARKED);
2560        aws->insert_option        ("by hit",    "h", QUERY_SORT_BY_HIT_DESCRIPTION);
2561        aws->insert_option        ("reverse",   "r", QUERY_SORT_REVERSE);
2562        aws->update_option_menu();
2563    }
2564    else {
2565        dbq_assert(0); // hmm - no sort button here. -> tell ralf where this happens
2566    }
2567
2568    if (awtqs->do_mark_pos_fig) {
2569        aws->at(awtqs->do_mark_pos_fig);
2570        aws->help_text("mark_list.hlp");
2571        aws->callback(makeWindowCallback(mark_queried_cb, query, 1));
2572        aws->create_button("MARK_LISTED_UNMARK_REST", "Mark Listed\nUnmark Rest", "M");
2573    }
2574    if (awtqs->do_unmark_pos_fig) {
2575        aws->at(awtqs->do_unmark_pos_fig);
2576        aws->help_text("unmark_list.hlp");
2577        aws->callback(makeWindowCallback(mark_queried_cb, query, 0));
2578        aws->create_button("UNMARK_LISTED_MARK_REST", "Unmark Listed\nMark Rest", "U");
2579    }
2580
2581    if (awtqs->do_mark_nt_pos_fig) {
2582        aws->at(awtqs->do_mark_nt_pos_fig);
2583        aws->help_text("mark_list.hlp");
2584        aws->callback(makeWindowCallback(mark_queried_cb, query, 9));
2585        aws->create_button("MARK_LISTED_KEEP_REST", "Mark Listed", "M");
2586    }
2587    if (awtqs->do_unmark_nt_pos_fig) {
2588        aws->at(awtqs->do_unmark_nt_pos_fig);
2589        aws->help_text("unmark_list.hlp");
2590        aws->callback(makeWindowCallback(mark_queried_cb, query, 8));
2591        aws->create_button("UNMARK_LISTED_KEEP_REST", "Unmark Listed", "U");
2592    }
2593
2594    if (awtqs->do_delete_pos_fig) {
2595        aws->at(awtqs->do_delete_pos_fig);
2596        aws->help_text("del_list.hlp");
2597        aws->callback(makeWindowCallback(delete_queried_species_cb, query));
2598        char *macro_id = GBS_global_string_copy("DELETE_LISTED_%s", query_id);
2599        aws->create_button(macro_id, "Delete Listed", "D");
2600        free(macro_id);
2601    }
2602    if (awtqs->do_set_pos_fig) {
2603        query->awar_writekey      = GBS_global_string_copy("tmp/dbquery_%s/write_key",      query_id); aw_root->awar_string(query->awar_writekey);
2604        query->awar_writelossy    = GBS_global_string_copy("tmp/dbquery_%s/write_lossy",    query_id); aw_root->awar_int   (query->awar_writelossy);
2605        query->awar_protectkey    = GBS_global_string_copy("tmp/dbquery_%s/protect_key",    query_id); aw_root->awar_string(query->awar_protectkey);
2606        query->awar_setprotection = GBS_global_string_copy("tmp/dbquery_%s/set_protection", query_id); aw_root->awar_int   (query->awar_setprotection, 4);
2607        query->awar_setvalue      = GBS_global_string_copy("tmp/dbquery_%s/set_value",      query_id); aw_root->awar_string(query->awar_setvalue);
2608
2609        aws->at(awtqs->do_set_pos_fig);
2610        aws->help_text("mod_field_list.hlp");
2611        aws->callback(makeCreateWindowCallback(create_writeFieldOfListed_window, query));
2612        char *macro_id = GBS_global_string_copy("WRITE_TO_FIELDS_OF_LISTED_%s", query_id);
2613        aws->create_button(macro_id, "Write to Fields\nof Listed", "S");
2614        free(macro_id);
2615    }
2616
2617    if ((awtqs->use_menu || awtqs->open_parser_pos_fig)) {
2618        sprintf(buffer, "tmp/dbquery_%s/tag",                 query_id); query->awar_tag             = strdup(buffer); aw_root->awar_string(query->awar_tag);
2619        sprintf(buffer, "tmp/dbquery_%s/use_tag",             query_id); query->awar_use_tag         = strdup(buffer); aw_root->awar_int   (query->awar_use_tag);
2620        sprintf(buffer, "tmp/dbquery_%s/deftag",              query_id); query->awar_deftag          = strdup(buffer); aw_root->awar_string(query->awar_deftag);
2621        sprintf(buffer, "tmp/dbquery_%s/double_pars",         query_id); query->awar_double_pars     = strdup(buffer); aw_root->awar_int   (query->awar_double_pars);
2622        sprintf(buffer, "tmp/dbquery_%s/parskey",             query_id); query->awar_parskey         = strdup(buffer); aw_root->awar_string(query->awar_parskey);
2623        sprintf(buffer, "tmp/dbquery_%s/parsvalue",           query_id); query->awar_parsvalue       = strdup(buffer); aw_root->awar_string(query->awar_parsvalue);
2624        sprintf(buffer, "tmp/dbquery_%s/awar_parspredefined", query_id); query->awar_parspredefined  = strdup(buffer); aw_root->awar_string(query->awar_parspredefined);
2625        sprintf(buffer, "tmp/dbquery_%s/acceptConvError",     query_id); query->awar_acceptConvError = strdup(buffer); aw_root->awar_int   (query->awar_acceptConvError);
2626        sprintf(buffer, "tmp/dbquery_%s/acceptNameMod",       query_id); query->awar_acceptIdMod     = strdup(buffer); aw_root->awar_int   (query->awar_acceptIdMod);
2627
2628        if (awtqs->use_menu) {
2629            sprintf(buffer, "Modify fields of listed %s", items); query_rel_menu_entry(aws, "mod_fields_of_listed", query_id, buffer, "f", "mod_field_list.hlp", AWM_ALL, makeCreateWindowCallback(create_modify_fields_window, query));
2630        }
2631        else {
2632            aws->at(awtqs->open_parser_pos_fig);
2633            aws->callback(makeCreateWindowCallback(create_modify_fields_window, query));
2634            aws->create_button("MODIFY_FIELDS_OF_LISTED", "MODIFY FIELDS\nOF LISTED", "F");
2635        }
2636    }
2637    if (awtqs->use_menu) {
2638        sprintf(buffer, "Set protection of fields of listed %s", items); query_rel_menu_entry(aws, "s_prot_of_listed", query_id, buffer, "p", "set_protection.hlp", AWM_ALL, makeCreateWindowCallback(create_set_protection_window, query));
2639        aws->sep______________();
2640        sprintf(buffer, "Mark listed %s, don't change rest",   items); query_rel_menu_entry(aws, "mark_listed",             query_id, buffer, "M", "mark.hlp", AWM_ALL, makeWindowCallback(mark_queried_cb, query, 1|8));
2641        sprintf(buffer, "Mark listed %s, unmark rest",         items); query_rel_menu_entry(aws, "mark_listed_unmark_rest", query_id, buffer, "l", "mark.hlp", AWM_ALL, makeWindowCallback(mark_queried_cb, query, 1  ));
2642        sprintf(buffer, "Unmark listed %s, don't change rest", items); query_rel_menu_entry(aws, "unmark_listed",           query_id, buffer, "U", "mark.hlp", AWM_ALL, makeWindowCallback(mark_queried_cb, query, 0|8));
2643        sprintf(buffer, "Unmark listed %s, mark rest",         items); query_rel_menu_entry(aws, "unmark_listed_mark_rest", query_id, buffer, "r", "mark.hlp", AWM_ALL, makeWindowCallback(mark_queried_cb, query, 0  ));
2644        aws->sep______________();
2645
2646
2647        sprintf(buffer, "Set color of listed %s", items);    query_rel_menu_entry(aws, "set_color_of_listed", query_id, buffer, "c", "set_color_of_listed.hlp", AWM_ALL, makeCreateWindowCallback(create_colorize_queried_window, query));
2648
2649        if (query->gb_ref) {
2650            dbq_assert(speciesOrOrganism(query->selector.type)); // stuff below works only for species/organisms
2651            aws->sep______________();
2652            if (query->expect_hit_in_ref_list) {
2653                aws->insert_menu_topic("search_equal_fields_and_listed_in_I", "Search entries existing in both DBs and listed in the source DB hitlist", "S",
2654                                       "search_equal_fields.hlp", AWM_ALL, makeWindowCallback(perform_query_cb, query, EXT_QUERY_COMPARE_LINES));
2655                aws->insert_menu_topic("search_equal_words_and_listed_in_I",  "Search words existing in entries of both DBs and listed in the source DB hitlist", "w",
2656                                       "search_equal_fields.hlp", AWM_ALL, makeWindowCallback(perform_query_cb, query, EXT_QUERY_COMPARE_WORDS));
2657            }
2658            else {
2659                aws->insert_menu_topic("search_equal_field_in_both_db", "Search entries existing in both DBs", "S",
2660                                       "search_equal_fields.hlp", AWM_ALL, makeWindowCallback(perform_query_cb, query, EXT_QUERY_COMPARE_LINES));
2661                aws->insert_menu_topic("search_equal_word_in_both_db", "Search words existing in entries of both DBs", "w",
2662                                       "search_equal_fields.hlp", AWM_ALL, makeWindowCallback(perform_query_cb, query, EXT_QUERY_COMPARE_WORDS));
2663            }
2664        }
2665    }
2666
2667    GB_pop_transaction(gb_main);
2668    return query;
2669}
2670
2671// --------------------------------------------------------------------------------
2672
2673#ifdef UNIT_TESTS
2674#ifndef TEST_UNIT_H
2675#include <test_unit.h>
2676#endif
2677
2678void TEST_nullcmp() {
2679    const char *whatever = "bla";
2680    TEST_EXPECT_EQUAL(ARB_strNULLcmp(NULp, NULp), 0);
2681    TEST_EXPECT_EQUAL(ARB_strNULLcmp(whatever, whatever), 0);
2682    TEST_EXPECT_EQUAL(ARB_strNULLcmp("a", "b"), -1);
2683
2684    // document uncommon behavior: NULp is bigger than any other text!
2685    TEST_EXPECT_EQUAL(ARB_strNULLcmp(whatever, NULp), -1);
2686    TEST_EXPECT_EQUAL(ARB_strNULLcmp(NULp, whatever), 1);
2687}
2688
2689#endif // UNIT_TESTS
2690
2691// --------------------------------------------------------------------------------
2692
Note: See TracBrowser for help on using the repository browser.