source: tags/ms_r16q3/SL/DB_SCANNER/db_scanner.cxx

Last change on this file was 14786, checked in by westram, 6 years ago
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 20.9 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
15#include <aw_awar.hxx>
16#include <aw_select.hxx>
17#include <aw_msg.hxx>
18#include <arb_progress.h>
19#include <aw_root.hxx>
20
21#include <arb_str.h>
22#include <arb_strbuf.h>
23#include <ad_cb.h>
24
25#include <unistd.h>
26#include <ctime>
27
28/*!************************************************************************
29    create a database scanner
30        the scanned database is displayed in a selection list
31        the gbdata pointer can be read
32***************************************************************************/
33
34#if defined(WARN_TODO)
35#warning derive DbScanner from AW_DB_selection
36#endif
37
38struct DbScanner {
39    ItemSelector& selector;
40
41    AW_window *aws;
42    AW_root   *awr;
43    GBDATA    *gb_main;
44
45    AW_selection_list *fields;
46
47    GBDATA         *gb_user;
48    GBDATA         *gb_edit;
49    bool            may_be_an_error;
50    DB_SCANNERMODE  scannermode;
51
52    char *awarname_editfield;
53    char *awarname_current_item; // awar contains pointer to mapped item (GBDATA*)
54    char *awarname_edit_enabled;
55    char *awarname_mark;
56
57    DbScanner(ItemSelector& selector_)
58        : selector(selector_),
59          aws(NULL),
60          awr(NULL),
61          gb_main(NULL),
62          fields(NULL),
63          gb_user(NULL),
64          gb_edit(NULL),
65          may_be_an_error(false),
66          scannermode(DB_SCANNER),
67          awarname_editfield(NULL), 
68          awarname_current_item(NULL), 
69          awarname_edit_enabled(NULL), 
70          awarname_mark(NULL) 
71    {
72        arb_assert(&selector);
73    }
74
75    char *get_mapped_item_id() const {
76        char *id = NULL;
77        if (gb_user) {
78            GB_transaction ta(gb_main);
79            id = selector.generate_item_id(gb_main, gb_user);
80        }
81        return id;
82    }
83};
84
85static GBDATA *get_mapped_item_and_begin_trans(DbScanner *cbs) {
86    /* return the selected GBDATA pntr
87     * there should be no running transaction; this function will begin a transaction */
88    cbs->may_be_an_error = false;
89    GB_push_transaction(cbs->gb_main);
90
91    GBDATA *gbd = cbs->awr->awar(cbs->awarname_current_item)->read_pointer();
92
93    if (!cbs->gb_user || !gbd || cbs->may_be_an_error) { // something changed in the database
94        return NULL;
95    }
96    return gbd;
97}
98
99
100
101static bool inside_scanner_keydata(DbScanner *cbs, GBDATA *gbd) {
102    // return true if 'gbd' is below keydata of scanner
103    GBDATA *gb_key_data;
104    gb_key_data = GB_search(cbs->gb_main, cbs->selector.change_key_path, GB_CREATE_CONTAINER);
105    return GB_check_father(gbd, gb_key_data);
106}
107
108static void scanner_delete_selected_field(AW_window*, DbScanner *cbs) {
109    GBDATA *gbd = get_mapped_item_and_begin_trans(cbs);
110    if (!gbd) {
111        aw_message("Sorry, cannot perform your operation, please redo it");
112    }
113    else if (inside_scanner_keydata(cbs, gbd)) {       // already deleted
114        ;
115    }
116    else {
117        GB_ERROR error = GB_delete(gbd);
118        if (error) aw_message((char *)error);
119    }
120    GB_commit_transaction(cbs->gb_main);
121}
122
123static void selected_field_changed_cb(GBDATA *, DbScanner *cbs, GB_CB_TYPE gbtype) {
124    cbs->may_be_an_error = true;
125
126    if (gbtype == GB_CB_DELETE) {
127        cbs->gb_edit = 0;
128    }
129    if (cbs->gb_edit) {
130        if (inside_scanner_keydata(cbs, cbs->gb_edit)) {    // doesn't exist
131            cbs->awr->awar(cbs->awarname_editfield)->write_string("");
132        }
133        else {
134            char *data = GB_read_as_string(cbs->gb_edit);
135            if (!data) data = strdup("<YOU CANNOT EDIT THIS TYPE>");
136            cbs->awr->awar(cbs->awarname_editfield)->write_string(data);
137            free(data);
138        }
139    }
140    else {
141        cbs->awr->awar(cbs->awarname_editfield)->write_string("");
142    }
143}
144
145
146static void editfield_value_changed(AW_window*, DbScanner *cbs)
147{
148    char *value = cbs->awr->awar(cbs->awarname_editfield)->read_string();
149    int   vlen  = strlen(value);
150
151    while (vlen>0 && value[vlen-1] == '\n') vlen--; // remove trailing newlines
152    value[vlen]     = 0;
153
154    // read the value from the window
155    GBDATA   *gbd         = get_mapped_item_and_begin_trans(cbs);
156    GB_ERROR  error       = 0;
157    bool      update_self = false;
158
159    if (!gbd) {
160        error = "No item or fields selected";
161    }
162    else if (!cbs->awr->awar(cbs->awarname_edit_enabled)->read_int()) { // edit disabled
163        cbs->awr->awar(cbs->awarname_editfield)->write_string("");
164        error = "Edit is disabled";
165    }
166    else {
167        char *key_name = 0;
168
169        if (inside_scanner_keydata(cbs, gbd)) { // not exist, create new element
170            GBDATA *gb_key_name = GB_entry(gbd, CHANGEKEY_NAME);
171            key_name            = GB_read_string(gb_key_name);
172            GBDATA *gb_key_type = GB_entry(gbd, CHANGEKEY_TYPE);
173
174            if (strlen(value)) {
175                GBDATA *gb_new     = GB_search(cbs->gb_user, key_name, (GB_TYPES)GB_read_int(gb_key_type));
176                if (!gb_new) error = GB_await_error();
177                else    error      = GB_write_autoconv_string(gb_new, value);
178
179                cbs->awr->awar(cbs->awarname_current_item)->write_pointer(gb_new); // remap arbdb
180            }
181        }
182        else { // change old element
183            key_name = GB_read_key(gbd);
184            if (GB_get_father(gbd) == cbs->gb_user && strcmp(key_name, "name") == 0) { // This is a real rename !!!
185                ItemSelector& selector = cbs->selector;
186
187                if (selector.type == QUERY_ITEM_SPECIES) { // species
188                    arb_progress  progress("Manual change of species ID");
189                    char         *name = nulldup(GBT_read_name(cbs->gb_user));
190
191                    if (strlen(value)) {
192                        GBT_begin_rename_session(cbs->gb_main, 0);
193
194                        error = GBT_rename_species(name, value, false);
195
196                        if (error) GBT_abort_rename_session();
197                        else error = GBT_commit_rename_session();
198                    }
199                    else {
200                        error = AWTC_recreate_name(cbs->gb_user);
201                    }
202
203                    free(name);
204                }
205                else { // non-species (gene, experiment, etc.)
206                    if (strlen(value)) {
207                        GBDATA *gb_exists    = 0;
208                        GBDATA *gb_item_data = GB_get_father(cbs->gb_user);
209
210                        for (gb_exists = selector.get_first_item(gb_item_data, QUERY_ALL_ITEMS);
211                             gb_exists;
212                             gb_exists = selector.get_next_item(gb_exists, QUERY_ALL_ITEMS))
213                        {
214                            if (ARB_stricmp(GBT_read_name(gb_exists), value) == 0) break;
215                        }
216
217                        if (gb_exists) error = GBS_global_string("There is already a %s named '%s'", selector.item_name, value);
218                        else error           = GB_write_autoconv_string(gbd, value);
219                    }
220                    else {
221                        error = "The 'name' field can't be empty.";
222                    }
223                }
224
225                if (!error) update_self = true;
226            }
227            else {
228                if (strlen(value)) {
229                    error = GB_write_autoconv_string(gbd, value);
230                }
231                else {
232                    GBDATA *gb_key = GBT_get_changekey(cbs->gb_main, key_name, cbs->selector.change_key_path);
233                    if (GB_child(gbd)) {
234                        error = "Sorry, cannot perform a deletion.\n(The selected entry has child entries. Delete them first.)";
235                    }
236                    else {
237                        error = GB_delete(gbd);
238                        if (!error) {
239                            cbs->awr->awar(cbs->awarname_current_item)->write_pointer(gb_key);
240                        }
241                    }
242                }
243            }
244
245        }
246        free(key_name);
247    }
248
249    selected_field_changed_cb(0, cbs, GB_CB_CHANGED); // refresh edit field
250
251    if (error) {
252        aw_message(error);
253        GB_abort_transaction(cbs->gb_main);
254    }
255    else {
256        GB_touch(cbs->gb_user); // change of linked object does not change source of link, so do it by hand
257        GB_commit_transaction(cbs->gb_main);
258    }
259
260    if (update_self) { // if the name changed -> rewrite awars AFTER transaction was closed
261        GB_transaction ta(cbs->gb_main);
262
263        char *my_id = cbs->selector.generate_item_id(cbs->gb_main, cbs->gb_user);
264        cbs->selector.update_item_awars(cbs->gb_main, cbs->awr, my_id); // update awars (e.g. AWAR_SPECIES_NAME)
265        free(my_id);
266    }
267
268    free(value);
269}
270
271static void toggle_marked_cb(AW_window *aws, DbScanner *cbs) {
272    cbs->may_be_an_error = false;
273
274    long flag = aws->get_root()->awar(cbs->awarname_mark)->read_int();
275    GB_push_transaction(cbs->gb_main);
276    if ((!cbs->gb_user) || cbs->may_be_an_error) {      // something changed in the database
277    }
278    else {
279        GB_write_flag(cbs->gb_user, flag);
280    }
281    GB_pop_transaction(cbs->gb_main);
282}
283
284static void remap_edit_box(UNFIXED, DbScanner *cbs) {
285    cbs->may_be_an_error = false;
286    GB_push_transaction(cbs->gb_main);
287    if (cbs->may_be_an_error) {     // sorry
288        cbs->awr->awar(cbs->awarname_current_item)->write_pointer(NULL);
289    }
290    GBDATA *gbd = cbs->awr->awar(cbs->awarname_current_item)->read_pointer();
291
292    if (cbs->gb_edit) {
293        GB_remove_callback(cbs->gb_edit, GB_CB_CHANGED_OR_DELETED, makeDatabaseCallback(selected_field_changed_cb, cbs));
294    }
295
296    if (cbs->awr->awar(cbs->awarname_edit_enabled)->read_int()) {      // edit enabled
297        cbs->gb_edit = gbd;
298    }
299    else {
300        cbs->gb_edit = 0;       // disable map
301    }
302    if (cbs->gb_edit) {
303        GB_add_callback(cbs->gb_edit, GB_CB_CHANGED_OR_DELETED, makeDatabaseCallback(selected_field_changed_cb, cbs));
304    }
305    selected_field_changed_cb(gbd, cbs, GB_CB_CHANGED);
306
307    GB_pop_transaction(cbs->gb_main);
308}
309
310
311
312static void scanner_changed_cb(UNFIXED, DbScanner *cbs, GB_CB_TYPE gbtype);
313
314DbScanner *create_db_scanner(GBDATA         *gb_main,
315                             AW_window      *aws,
316                             const char     *box_pos_fig,            // the position for the box in the xfig file
317                             const char     *delete_pos_fig,
318                             const char     *edit_pos_fig,
319                             const char     *edit_enable_pos_fig,
320                             DB_SCANNERMODE  scannermode,
321                             const char     *mark_pos_fig,
322                             ItemSelector&   selector)
323{
324    /* create an unmapped scanner box and optional some buttons,
325       the return value is the id to further scanner functions */
326
327    static int scanner_id = 0;
328
329    DbScanner *cbs = new DbScanner(selector);
330
331    char     buffer[256];
332    AW_root *aw_root = aws->get_root();
333
334    GB_push_transaction(gb_main);
335    //!************* Create local AWARS ******************
336    sprintf(buffer, "tmp/arbdb_scanner_%i/list", scanner_id);
337    cbs->awarname_current_item = strdup(buffer);
338    aw_root->awar_pointer(cbs->awarname_current_item, 0, AW_ROOT_DEFAULT);
339
340    sprintf(buffer, "tmp/arbdb_scanner_%i/edit_enable", scanner_id);
341    cbs->awarname_edit_enabled = strdup(buffer);
342    aw_root->awar_int(cbs->awarname_edit_enabled, true, AW_ROOT_DEFAULT);
343
344    sprintf(buffer, "tmp/arbdb_scanner_%i/mark", scanner_id);
345    cbs->awarname_mark = strdup(buffer);
346    aw_root->awar_int(cbs->awarname_mark, true, AW_ROOT_DEFAULT);
347
348    aws->at(box_pos_fig);
349
350    cbs->fields      = aws->create_selection_list(cbs->awarname_current_item, 20, 10, true);
351    cbs->aws         = aws;
352    cbs->awr         = aw_root;
353    cbs->gb_main     = gb_main;
354    cbs->gb_user     = 0;
355    cbs->gb_edit     = 0;
356    cbs->scannermode = scannermode;
357
358    //!************* Create the delete button ***************
359    if (delete_pos_fig) {
360        aws->at(delete_pos_fig);
361        aws->callback(makeWindowCallback(scanner_delete_selected_field, cbs));
362        aws->create_button("DELETE_DB_FIELD", "DELETE", "D");
363    }
364
365    //!************* Create the enable edit selector ***************
366    if (edit_enable_pos_fig) {
367        aws->at(edit_enable_pos_fig);
368        aws->callback(makeWindowCallback(remap_edit_box, cbs)); // @@@ used as TOGGLE_CLICK_CB (see #559)
369        aws->create_toggle(cbs->awarname_edit_enabled);
370    }
371
372    if (mark_pos_fig) {
373        aws->at(mark_pos_fig);
374        aws->callback(makeWindowCallback(toggle_marked_cb, cbs)); // @@@ used as TOGGLE_CLICK_CB (see #559)
375        aws->create_toggle(cbs->awarname_mark);
376    }
377
378    cbs->awarname_editfield = 0;
379    if (edit_pos_fig) {
380        RootCallback remap_cb = makeRootCallback(remap_edit_box, cbs);
381        aw_root->awar(cbs->awarname_current_item)->add_callback(remap_cb);
382        if (edit_enable_pos_fig) {
383            aw_root->awar(cbs->awarname_edit_enabled)->add_callback(remap_cb);
384        }
385        sprintf(buffer, "tmp/arbdb_scanner_%i/edit", scanner_id);
386        cbs->awarname_editfield = strdup(buffer);
387        aw_root->awar_string(cbs->awarname_editfield, "", AW_ROOT_DEFAULT);
388
389        aws->at(edit_pos_fig);
390        aws->callback(makeWindowCallback(editfield_value_changed, cbs)); // @@@ used as TEXTFIELD_CB (see #559);
391        aws->create_text_field(cbs->awarname_editfield, 20, 10);
392    }
393
394    scanner_id++;
395    GB_pop_transaction(gb_main);
396    aws->set_popup_callback(makeWindowCallback(scanner_changed_cb, cbs, GB_CB_CHANGED));
397    return cbs;
398}
399
400static void scan_fields_recursive(GBDATA *gbd, DbScanner *cbs, int deep, AW_selection_list *fields)
401{
402    GB_TYPES  type = GB_read_type(gbd);
403    char     *key  = GB_read_key(gbd);
404
405    GBS_strstruct *out = GBS_stropen(1000);
406    for (int i = 0; i < deep; i++) GBS_strcat(out, ": ");
407    GBS_strnprintf(out, 30, "%-12s", key);
408
409    switch (type) {
410        case GB_DB: {
411            GBS_strcat(out, "<CONTAINER>:");
412            cbs->fields->insert(GBS_mempntr(out), gbd);
413            GBS_strforget(out);
414
415            for (GBDATA *gb2 = GB_child(gbd); gb2; gb2 = GB_nextChild(gb2)) {
416                scan_fields_recursive(gb2, cbs, deep + 1, fields);
417            }
418            break;
419        }
420        case GB_LINK: {
421            GBS_strnprintf(out, 100, "LINK TO '%s'", GB_read_link_pntr(gbd));
422            cbs->fields->insert(GBS_mempntr(out), gbd);
423            GBS_strforget(out);
424
425            GBDATA *gb_al = GB_follow_link(gbd);
426            if (gb_al) {
427                for (GBDATA *gb2 = GB_child(gb_al); gb2; gb2 = GB_nextChild(gb2)) {
428                    scan_fields_recursive(gb2, cbs, deep + 1, fields);
429                }
430            }
431            break;
432        }
433        default: {
434            char *data = GB_read_as_string(gbd);
435
436            if (data) {
437                GBS_strcat(out, data);
438                free(data);
439            }
440            else {
441                GBS_strcat(out, "<unprintable>");
442            }
443            cbs->fields->insert(GBS_mempntr(out), gbd);
444            GBS_strforget(out);
445            break;
446        }
447    }
448    free(key);
449}
450
451static void scan_list(GBDATA *, DbScanner *cbs) {
452#define INFO_WIDTH 1000
453 refresh_again :
454    char buffer[INFO_WIDTH+1];
455    memset(buffer, 0, INFO_WIDTH+1);
456
457    static int last_max_name_width;
458    int        max_name_width = 0;
459
460    if (last_max_name_width == 0) last_max_name_width = 15;
461
462    cbs->fields->clear();
463
464    GBDATA *gb_key_data = GB_search(cbs->gb_main, cbs->selector.change_key_path, GB_CREATE_CONTAINER);
465
466    for (int existing = 1; existing >= 0; --existing) {
467        for (GBDATA *gb_key = GB_entry(gb_key_data, CHANGEKEY); gb_key; gb_key = GB_nextEntry(gb_key)) {
468            GBDATA *gb_key_hidden = GB_entry(gb_key, CHANGEKEY_HIDDEN);
469            if (gb_key_hidden && GB_read_int(gb_key_hidden)) continue; // don't show hidden fields in 'species information' window
470
471            GBDATA *gb_key_name = GB_entry(gb_key, CHANGEKEY_NAME);
472            if (!gb_key_name) continue;
473
474            GBDATA *gb_key_type = GB_entry(gb_key, CHANGEKEY_TYPE);
475
476            const char *name = GB_read_char_pntr(gb_key_name);
477            GBDATA     *gbd  = GB_search(cbs->gb_user, name, GB_FIND);
478
479            if ((!existing) == (!gbd)) { // first print only existing; then non-existing entries
480                char *p      = buffer;
481                int   len    = sprintf(p, "%-*s %c", last_max_name_width, name, GB_type_2_char((GB_TYPES)GB_read_int(gb_key_type)));
482
483                p += len;
484
485                int name_width = strlen(name);
486                if (name_width>max_name_width) max_name_width = name_width;
487
488                if (gbd) {      // existing entry
489                    *(p++) = GB_read_security_write(gbd)+'0';
490                    *(p++) = ':';
491                    *(p++) = ' ';
492                    *p     = 0;
493
494                    {
495                        char *data = GB_read_as_string(gbd);
496                        if (data) {
497                            int rest  = INFO_WIDTH-(p-buffer);
498                            int ssize = strlen(data);
499
500                            if (ssize > rest) {
501                                ssize = GBS_shorten_repeated_data(data);
502                                if (ssize > rest) {
503                                    if (ssize>5) strcpy(data+rest-5, "[...]");
504                                    ssize = rest;
505                                }
506                            }
507
508                            memcpy(p, data, ssize);
509                            p[ssize] = 0;
510
511                            free(data);
512                        }
513                    }
514                    cbs->fields->insert(buffer, gbd);
515                }
516                else { // non-existing entry
517                    p[0] = ' '; p[1] = ':'; p[2] = 0;
518                    cbs->fields->insert(buffer, gb_key);
519                }
520            }
521        }
522    }
523
524    if (last_max_name_width < max_name_width) {
525        last_max_name_width = max_name_width;
526        goto refresh_again;
527    }
528#undef INFO_WIDTH
529}
530
531static void scanner_changed_cb(UNFIXED, DbScanner *cbs, GB_CB_TYPE gbtype) { // called by DB-callback or WindowCallback
532    cbs->may_be_an_error = true;
533
534    if (gbtype == GB_CB_DELETE) {
535        cbs->gb_user = 0;
536    }
537    if (cbs->gb_user && !cbs->aws->is_shown()) {
538        // unmap invisible window
539        // map_db_scanner(cbs,0);
540        // recalls this function !!!!
541        return;
542    }
543    cbs->fields->clear();
544    if (cbs->gb_user) {
545        GB_transaction ta(cbs->gb_main);
546
547        switch (cbs->scannermode) {
548            case DB_SCANNER:
549                scan_fields_recursive(cbs->gb_user, cbs, 0, cbs->fields);
550                break;
551            case DB_VIEWER:
552                scan_list(cbs->gb_user, cbs);
553                break;
554        }
555    }
556    cbs->fields->insert_default("", (GBDATA*)NULL);
557    cbs->fields->update();
558    if (cbs->gb_user) {
559        GB_transaction ta(cbs->gb_main);
560
561        long flag = GB_read_flag(cbs->gb_user);
562        cbs->awr->awar(cbs->awarname_mark)->write_int(flag);
563    }
564}
565/*!********** Unmap edit field if 'key_data' has been changed (maybe entries deleted)
566 *********/
567static void scanner_changed_dcb2(GBDATA*, DbScanner *cbs, GB_CB_TYPE gbtype) {
568    cbs->awr->awar(cbs->awarname_current_item)->write_pointer(NULL);
569    // unmap edit field
570    scanner_changed_cb(NULL, cbs, gbtype);
571}
572
573void map_db_scanner(DbScanner *scanner, GBDATA *gb_pntr, const char *key_path) {
574    GB_transaction ta(scanner->gb_main);
575
576    GBDATA *gb_key_data = GB_search(scanner->gb_main, key_path, GB_CREATE_CONTAINER);
577
578    if (scanner->gb_user) {
579        GB_remove_callback(scanner->gb_user, GB_CB_CHANGED_OR_DELETED, makeDatabaseCallback(scanner_changed_cb, scanner));
580    }
581    if (scanner->scannermode == DB_VIEWER) {
582        GB_remove_callback(gb_key_data, GB_CB_CHANGED, makeDatabaseCallback(scanner_changed_dcb2, scanner));
583    }
584
585    scanner->gb_user = gb_pntr;
586
587    if (gb_pntr) {
588        GB_add_callback(gb_pntr, GB_CB_CHANGED_OR_DELETED, makeDatabaseCallback(scanner_changed_cb, scanner));
589        if (scanner->scannermode == DB_VIEWER) {
590            GB_add_callback(gb_key_data, GB_CB_CHANGED, makeDatabaseCallback(scanner_changed_dcb2, scanner));
591        }
592    }
593
594    scanner->awr->awar(scanner->awarname_current_item)->write_pointer(NULL);
595    scanner_changed_cb(NULL, scanner, GB_CB_CHANGED);
596}
597
598GBDATA *get_db_scanner_main(const DbScanner *scanner) {
599    return scanner->gb_main;
600}
601
602char *get_id_of_item_mapped_in(const DbScanner *scanner) {
603    return scanner->get_mapped_item_id();
604}
605
606const ItemSelector& get_itemSelector_of(const DbScanner *scanner) {
607    return scanner->selector;
608}
Note: See TracBrowser for help on using the repository browser.