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

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