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

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