source: trunk/SL/DB_SCANNER/db_scanner.cxx

Last change on this file was 19346, checked in by westram, 2 years ago
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 19.0 KB
Line 
1// =============================================================== //
2//                                                                 //
3//   File      : db_scanner.cxx                                    //
4//   Purpose   :                                                   //
5//                                                                 //
6//   Institute of Microbiology (Technical University Munich)       //
7//   http://www.arb-home.de/                                       //
8//                                                                 //
9// =============================================================== //
10
11#include <db_scanner.hxx>
12
13#include <AW_rename.hxx>
14#include <aw_select.hxx>
15#include <aw_msg.hxx>
16#include <aw_root.hxx>
17
18#include <ad_cb.h>
19
20#include <arb_progress.h>
21#include <arb_str.h>
22#include <arb_strbuf.h>
23#include <arb_strarray.h>
24
25#include <StrUniquifier.h>
26
27const size_t INFO_WIDTH = 1000;
28
29STATIC_ASSERT(INFO_WIDTH<=AW_selection_list_entry::MAX_DISPLAY_LENGTH);
30
31void DbScanner::editfield_value_changed_cb() {
32    if (ignore_editfield_change) return;
33
34    LocallyModify<bool> do_not_recurse(ignore_editfield_change, true);
35
36    bool update_self = false;
37    {
38        GB_begin_transaction(gb_main);
39
40        GB_ERROR    error      = NULp;
41        const char *field_name = awar_selected_field->read_char_pntr();
42
43        if (!gb_item) {
44            error = "No item selected";
45        }
46        else if (!field_name[0]) {
47            error = "No field selected";
48        }
49        else if (!awar_edit_enabled->read_int()) { // edit disabled
50            awar_editfield->write_string("");
51            error = "Change ignored (edit is disabled!)";
52        }
53        else {
54            char *value    = awar_editfield->read_string();
55            int   vlen     = strlen(value);
56
57            while (vlen>0 && value[vlen-1] == '\n') vlen--; // remove trailing newlines
58            value[vlen]     = 0;
59
60            GBDATA *gbd = search_selected_field();
61
62            if (!gbd) { // field does not exist -> create new element
63                if (vlen) {
64                    GBDATA *gb_key      = GBT_get_changekey(gb_main, field_name, selector.change_key_path);
65                    GBDATA *gb_key_type = GB_entry(gb_key, CHANGEKEY_TYPE);
66                    GBDATA *gb_new      = GB_search(gb_item, field_name, (GB_TYPES)GB_read_int(gb_key_type));
67                    if (!gb_new) error  = GB_await_error();
68                    else    error       = GB_write_autoconv_string(gb_new, value);
69                }
70            }
71            else { // change old element
72                if (strcmp(field_name, "name") == 0) { // This is a real rename !!! // @@@ should use MutableItemSelector::id_field (no real problem)
73                    if (speciesOrOrganism(selector.type)) { // species/organism
74                        arb_progress  progress("Manual change of species ID");
75                        const char   *name = GBT_get_name(gb_item);
76
77                        if (!name) {
78                            error = "cannot rename unnamed item";
79                        }
80                        else {
81                            char *namedup = ARB_strdup(name);
82
83                            if (strlen(value)) {
84                                GBT_begin_rename_session(gb_main, 0);
85
86                                error = GBT_rename_species(namedup, value, false);
87
88                                if (error) GBT_abort_rename_session();
89                                else error = GBT_commit_rename_session();
90                            }
91                            else {
92                                error = AWTC_recreate_name(gb_item);
93                            }
94
95                            free(namedup);
96                        }
97                    }
98                    else { // non-species (gene, experiment, etc.)
99                        if (strlen(value)) {
100                            GBDATA *gb_exists    = NULp;
101                            GBDATA *gb_item_data = GB_get_father(gb_item);
102
103                            for (gb_exists = selector.get_first_item(gb_item_data, QUERY_ALL_ITEMS);
104                                 gb_exists;
105                                 gb_exists = selector.get_next_item(gb_exists, QUERY_ALL_ITEMS))
106                            {
107                                if (ARB_stricmp(GBT_get_name_or_description(gb_exists), value) == 0) break;
108                            }
109
110                            if (gb_exists) error = GBS_global_string("There is already a %s named '%s'", selector.item_name, value);
111                            else error           = GB_write_autoconv_string(gbd, value);
112                        }
113                        else {
114                            error = "The 'name' field can't be empty.";
115                        }
116                    }
117
118                    if (!error) update_self = true;
119                }
120                else {
121                    if (vlen) {
122                        error = GB_write_autoconv_string(gbd, value);
123                    }
124                    else {
125                        if (GB_child(gbd)) {
126                            error = "Sorry, cannot perform a deletion.\n(The selected entry has child entries. Delete them first.)";
127                        }
128                        else {
129                            error = GB_delete(gbd);
130                        }
131                    }
132                }
133            }
134            free(value);
135        }
136
137        aw_message_if(GB_end_transaction(gb_main, error));
138    }
139
140    awar_selected_field->touch(); // force refresh of edit-box
141
142    if (update_self) { // if the name changed -> rewrite awars AFTER transaction was closed
143        GB_transaction ta(gb_main);
144
145        char *my_id = selector.generate_item_id(gb_main, gb_item);
146        selector.update_item_awars(gb_main, get_root(), my_id); // update awars (e.g. AWAR_SPECIES_NAME)
147        free(my_id);
148    }
149}
150
151void DbScanner::toggle_marked_cb() {
152    if (gb_item && !ignore_marktoggle_change) {
153        long flag = awar_mark->read_int();
154        GB_transaction ta(gb_main);
155        GB_write_flag(gb_item, flag);
156    }
157}
158
159void DbScanner::remap_edit_box() {
160    GB_transaction ta(gb_main);
161
162    GBDATA *gb_wanted_field = NULp;
163    if (awar_edit_enabled->read_int()) {
164        gb_wanted_field = search_selected_field(); // map only if editing is allowed
165    }
166
167    if (gb_wanted_field != gb_field) {
168        DatabaseCallback editbox_update_cb = makeDatabaseCallback(DbScanner::field_changed_cb, this);
169        if (gb_field) GB_remove_callback(gb_field, GB_CB_CHANGED_OR_DELETED, editbox_update_cb);
170
171        gb_field = gb_wanted_field; // change field
172
173        if (gb_field) {
174            GB_add_callback(gb_field, GB_CB_CHANGED_OR_DELETED, editbox_update_cb);
175        }
176        field_changed_cb(GB_CB_CHANGED);
177    }
178}
179
180inline const char *awarname(const char *scanner_id, const char *entry) {
181    return GBS_global_string("tmp/dbscan/%s/%s", scanner_id, entry);
182}
183
184void DbScanner::create_awars(const char *scanner_id, bool have_edit_field, bool have_edit_toggle, bool have_mark_toggle) {
185    arb_assert(implicated(have_edit_toggle, have_edit_field));
186
187    // create scanner-local AWARS
188    const char *list_awarname = awarname(scanner_id, "list");
189    if (get_root()->awar_no_error(list_awarname)) { // awar already exists
190        GBK_terminatef("multiple scanners bound to AWAR '%s'", list_awarname);
191    }
192    awar_selected_field = get_root()->awar_string(list_awarname, "", AW_ROOT_DEFAULT);
193
194    awar_mapped_item_ID = get_root()->awar_string(awarname(scanner_id, "id"), "<undef>", AW_ROOT_DEFAULT);
195    if (have_edit_field) {
196        awar_editfield = get_root()->awar_string(awarname(scanner_id, "edit"), "", AW_ROOT_DEFAULT);
197        if (have_edit_toggle) {
198            awar_edit_enabled = get_root()->awar_int(awarname(scanner_id, "edit_enable"), true, AW_ROOT_DEFAULT);
199        }
200    }
201    if (have_mark_toggle) {
202        awar_mark = get_root()->awar_int(awarname(scanner_id, "mark"), true, AW_ROOT_DEFAULT);
203    }
204}
205
206void DbScanner::create_field_edit_widgets(const char *edit_pos_fig, const char *edit_enable_pos_fig) {
207    arb_assert(edit_pos_fig);
208
209    RootCallback   remap_rcb = makeRootCallback(DbScanner::remap_edit_box, this);
210    WindowCallback remap_wcb = makeWindowCallback(DbScanner::remap_edit_box, this);
211
212    if (edit_enable_pos_fig) {
213        aws->at(edit_enable_pos_fig);
214        aws->create_toggle(awar_edit_enabled->awar_name);
215    }
216
217    awar_selected_field->add_callback(remap_rcb);
218    awar_mapped_item_ID->add_callback(remap_rcb); // refresh edit-field if item changes
219    if (edit_enable_pos_fig) awar_edit_enabled->add_callback(remap_rcb);
220    awar_editfield->add_callback(makeRootCallback(DbScanner::editfield_value_changed_cb, this));
221
222    aws->at(edit_pos_fig);
223    aws->create_text_field(awar_editfield->awar_name, 20, 10);
224}
225
226
227DbScanner *DbScanner::create(GBDATA         *gb_main,
228                             const char     *scanner_id,
229                             AW_window      *aws,
230                             const char     *box_pos_fig,
231                             const char     *edit_pos_fig,
232                             const char     *edit_enable_pos_fig,
233                             DB_SCANNERMODE  scannermode,
234                             const char     *mark_pos_fig,
235                             ItemSelector&   selector)
236{
237    /* create an unmapped scanner box and optionally some buttons,
238     * the return value is used as handle to further scanner functions
239     */
240
241    DbScanner *scanner = new DbScanner(scannermode, selector, aws, gb_main);
242
243    if (gb_main) GB_push_transaction(gb_main);
244
245    scanner->create_awars(scanner_id, edit_pos_fig, edit_enable_pos_fig, mark_pos_fig);
246
247    // create GUI elements
248    aws->at(box_pos_fig);
249    scanner->create_field_selection_list();
250    if (mark_pos_fig) {
251        aws->at(mark_pos_fig);
252        scanner->create_mark_toggle();
253    }
254    if (edit_pos_fig) {
255        scanner->create_field_edit_widgets(edit_pos_fig, edit_enable_pos_fig);
256    }
257
258    if (gb_main) GB_pop_transaction(gb_main);
259    aws->set_popup_callback(makeWindowCallback(DbScanner::remap_item, scanner));
260    return scanner;
261}
262
263void DbScanner::append_field_data(GBS_strstruct& buf, GB_TYPES type, GBDATA *gbd) {
264    /*! insert 'type', protection and data into 'buf'.
265     * note that 'gbd' may be NULp -> only insert type
266     */
267
268    buf.put(GB_type_2_char(type));
269
270    if (gbd) { // existing entry
271        buf.put(GB_read_security_write(gbd)+'0');
272        buf.put(':');
273        buf.put(' ');
274
275        char *data = GB_read_as_string(gbd);
276        if (data) {
277            int rest    = INFO_WIDTH-buf.get_position();
278            int ssize   = strlen(data);
279
280            arb_assert(rest>0);
281            arb_assert(ssize>=0);
282
283            if (ssize > rest) {
284                ssize = GBS_shorten_repeated_data(data);
285                if (ssize > rest) {
286                    if (ssize>5) strcpy(data+rest-5, "[...]");
287                    ssize = rest;
288                }
289            }
290
291            buf.ncat(data, ssize);
292            free(data);
293        }
294        else {
295            buf.ncat("<unprintable>", 13);
296        }
297    }
298    else { // non-existing entry
299        buf.ncat(" :", 2);
300    }
301}
302
303
304
305int DbScanner::fill_fields_recursive(GBDATA *gbd, const int depth, GBS_strstruct& buf) {
306    static StrUniquifier trackKeys; // track keys with identical names and assign unique IDs.
307
308    int max_keyname_length = 0;
309    arb_assert(buf.empty());
310    for (int i = 0; i<depth; ++i) buf.ncat(": ", 2);
311
312    const char *key    = GB_read_key_pntr(gbd);
313    int         keylen = strlen(key);
314    buf.ncatPadded(key, keylen, previous_max_keyname_length);
315    buf.put(' ');
316    max_keyname_length = std::max(max_keyname_length, keylen);
317
318    bool isContainer = GB_read_type(gbd) == GB_DB;
319    if (isContainer) buf.ncat("<CONTAINER>:", 12);
320    else             append_field_data(buf, GB_read_type(gbd), gbd);
321
322    {
323        const char *id = trackKeys.make_unique_key(key); // create unique IDs for multiple fields with same key
324        field_sel->insert(buf.get_data(), id);
325        buf.erase();
326    }
327
328    if (isContainer) {
329        for (GBDATA *gb2 = GB_child(gbd); gb2; gb2 = GB_nextChild(gb2)) {
330            max_keyname_length = std::max(max_keyname_length, fill_fields_recursive(gb2, depth+1, buf));
331        }
332    }
333
334    // cleanup static variable (at top of recursion):
335    if (!depth) trackKeys.clear();
336
337    return max_keyname_length;
338}
339
340int DbScanner::fill_fields_by_keydata(GBS_strstruct& buf) {
341    int max_keyname_length = 0;
342
343    GBDATA *gb_key_data = GB_search(gb_main, get_selector().change_key_path, GB_CREATE_CONTAINER);
344
345    for (int existing = 1; existing >= 0; --existing) {
346        for (GBDATA *gb_key = GB_entry(gb_key_data, CHANGEKEY); gb_key; gb_key = GB_nextEntry(gb_key)) {
347            GBDATA *gb_key_hidden = GB_entry(gb_key, CHANGEKEY_HIDDEN);
348            if (gb_key_hidden && GB_read_int(gb_key_hidden)) continue; // don't show hidden fields in 'species information' window
349
350            GBDATA *gb_key_name = GB_entry(gb_key, CHANGEKEY_NAME);
351            if (!gb_key_name) continue;
352
353            GBDATA *gb_key_type = GB_entry(gb_key, CHANGEKEY_TYPE);
354
355            const char *key = GB_read_char_pntr(gb_key_name);
356            GBDATA     *gbd = GB_search(gb_item, key, GB_FIND);
357
358            if ((!existing) == (!gbd)) { // first print only existing; then non-existing entries
359                buf.erase();
360
361                int keylen         = strlen(key);
362                buf.ncatPadded(key, keylen, previous_max_keyname_length);
363                buf.put(' ');
364                max_keyname_length = std::max(max_keyname_length, keylen);
365
366                GB_TYPES type = GB_TYPES(GB_read_int(gb_key_type));
367                append_field_data(buf, type, gbd);
368
369                field_sel->insert(buf.get_data(), key);
370            }
371        }
372    }
373
374    return max_keyname_length;
375}
376
377void DbScanner::field_changed_cb(GB_CB_TYPE cbtype) {
378    if (cbtype == GB_CB_DELETE) {
379        gb_field = NULp;
380    }
381
382    LocallyModify<bool> just_a_refresh(ignore_editfield_change, true);
383
384    if (gb_field) {
385        char *data = GB_read_as_string(gb_field);
386        if (!data) data = strdup("<you cannot edit this datatype>");
387        awar_editfield->write_string(data);
388        free(data);
389    }
390    else {
391        awar_editfield->write_string("");
392    }
393}
394
395inline bool width_adjusted(int& prevMaxLen, int currMaxLen) {
396    bool adjusted = false;
397    if (prevMaxLen != currMaxLen) {
398        prevMaxLen = currMaxLen;
399        adjusted = true;
400    }
401    return adjusted;
402}
403
404void DbScanner::changed_cb(GB_CB_TYPE cbtype) {
405    // performs updates necessary when mapped item changes:
406    // - refresh field selection list
407    // - update display of item name
408
409    if (cbtype == GB_CB_DELETE) {
410        gb_item = NULp;
411    }
412    if (gb_item && !aws->is_shown()) {
413        return;
414    }
415
416    // update selection list
417    field_sel->clear();
418    if (gb_item) {
419        GB_transaction ta(gb_main);
420        GBS_strstruct buf;
421
422        int fillCount = 0;
423        while (true) {
424            int max_keyname_length = 0;
425            switch (scannermode) {
426                case DB_SCANNER:   max_keyname_length = fill_fields_recursive(gb_item, 0, buf); break;
427                case DB_KEYVIEWER: max_keyname_length = fill_fields_by_keydata(buf); break;
428            }
429            fillCount++;
430
431            if (!width_adjusted(previous_max_keyname_length, max_keyname_length)) break;
432
433            // otherwise occurring keyname length changed. refill (once) to fix used width.
434            if (fillCount>=2) {
435                arb_assert(0); // formatting should not fail multiple times
436                break;
437            }
438            field_sel->clear();
439        }
440    }
441
442    field_sel->insert_default("", "");
443    field_sel->update();
444
445    if (gb_item) {
446        GB_transaction ta(gb_main);
447
448        if (awar_mark) {
449            LocallyModify<bool> ignoreUpdate(ignore_marktoggle_change, true);
450            long flag = GB_read_flag(gb_item);
451            awar_mark->write_int(flag);
452        }
453
454        char *id = get_mapped_item_id();
455        awar_mapped_item_ID->write_string(id);
456        free(id);
457    }
458    else {
459        awar_mapped_item_ID->write_string("<none selected>");
460    }
461    awar_selected_field->touch();
462}
463
464void DbScanner::keydata_modified_cb(GB_CB_TYPE cbtype) {
465    // unmap edit field if 'key_data' has been changed (entries might have been deleted)
466
467    bool deselect = false;
468    {
469        const char *field_name = awar_selected_field->read_char_pntr();
470        if (field_name[0]) {
471            GBDATA *gb_key = GBT_get_changekey(gb_main, field_name, selector.change_key_path);
472
473            if (!gb_key) { // key unknown
474                deselect = true;
475            }
476            else {
477                GBDATA *gb_key_hidden = GB_entry(gb_key, CHANGEKEY_HIDDEN);
478                if (gb_key_hidden && GB_read_int(gb_key_hidden)) { // key hidden -> deselect
479                    deselect = true;
480                }
481            }
482        }
483    }
484
485    if (deselect) awar_selected_field->write_string(""); // deselect field
486    changed_cb(cbtype);
487}
488
489void DbScanner::Map(GBDATA *gb_new_item, const char *key_path) {
490    GB_transaction ta(gb_main);
491
492    arb_assert(implicated(scannermode == DB_KEYVIEWER, key_path != NULp));
493
494    GBDATA *gb_key_data = scannermode == DB_KEYVIEWER ? GB_search(gb_main, key_path, GB_CREATE_CONTAINER) : NULp;
495
496    if (gb_item) {
497        GB_remove_callback(gb_item, GB_CB_CHANGED_OR_DELETED, makeDatabaseCallback(DbScanner::changed_cb, this));
498        if (scannermode == DB_KEYVIEWER) {
499            GB_remove_callback(gb_key_data, GB_CB_CHANGED, makeDatabaseCallback(DbScanner::keydata_modified_cb, this));
500        }
501    }
502
503    gb_item = gb_new_item;
504
505    if (gb_item) {
506        GB_add_callback(gb_item, GB_CB_CHANGED_OR_DELETED, makeDatabaseCallback(DbScanner::changed_cb, this));
507        if (scannermode == DB_KEYVIEWER) {
508            GB_add_callback(gb_key_data, GB_CB_CHANGED, makeDatabaseCallback(DbScanner::keydata_modified_cb, this));
509        }
510    }
511
512    changed_cb(GB_CB_CHANGED);
513}
514
515void DbScanner::remap_item() {
516    GB_transaction ta(gb_main);
517    Map(selector.get_selected_item(gb_main, AW_root::SINGLETON), selector.change_key_path);
518}
519
520void collectKeysRegisteredInDatabase(StrArray& fields, GBDATA *gb_main, ItemSelector& sel, bool skipContainers, bool skipHidden) {
521    arb_assert(gb_main);
522
523    GB_transaction ta(gb_main);
524
525    GBDATA *gb_key_data = GB_search(gb_main, sel.change_key_path, GB_FIND);
526    GBDATA *gb_key      = gb_key_data ? GB_entry(gb_key_data, CHANGEKEY) : NULp;
527
528    while (gb_key) {
529        bool skipThis = false;
530        if (!skipThis && skipContainers) {
531            GB_TYPES type = GB_TYPES(*GBT_read_int(gb_key, CHANGEKEY_TYPE));
532            skipThis      = type == GB_DB; // skip when type is container
533        }
534        if (!skipThis && skipHidden) {
535            GBDATA *gb_key_hidden = GB_entry(gb_key, CHANGEKEY_HIDDEN);
536            skipThis = gb_key_hidden && GB_read_int(gb_key_hidden); // skip when hidden
537        }
538
539        if (!skipThis) {
540            char *key_name = GBT_read_string(gb_key, CHANGEKEY_NAME);
541            fields.put(key_name);
542        }
543
544        GBDATA *gb_next_key = GB_nextEntry(gb_key);
545        gb_key              = gb_next_key;
546    }
547}
548
Note: See TracBrowser for help on using the repository browser.