source: tags/arb-6.0/AWT/AWT_db_browser.cxx

Last change on this file was 12267, checked in by westram, 10 years ago
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 47.5 KB
Line 
1//  ==================================================================== //
2//                                                                       //
3//    File      : AWT_db_browser.cxx                                     //
4//    Purpose   : Simple database viewer                                 //
5//                                                                       //
6//                                                                       //
7//  Coded by Ralf Westram (coder@reallysoft.de) in May 2004              //
8//  Copyright Department of Microbiology (Technical University Munich)   //
9//                                                                       //
10//  Visit our web site at: http://www.arb-home.de/                       //
11//                                                                       //
12//  ==================================================================== //
13
14#include "awt.hxx"
15#include "awt_hexdump.hxx"
16
17#include <aw_window.hxx>
18#include <aw_msg.hxx>
19#include <aw_awar.hxx>
20#include <aw_select.hxx>
21#include <aw_advice.hxx>
22
23#include <arb_str.h>
24#include <arb_strarray.h>
25
26#include <string>
27#include <vector>
28#include <map>
29#include <algorithm>
30#include <arb_misc.h>
31#include <arb_diff.h>
32#include <arb_file.h>
33#include <ad_cb.h>
34
35// do includes above (otherwise depends depend on DEBUG)
36#if defined(DEBUG)
37
38using namespace std;
39
40// used AWARs :
41
42#define AWAR_DBB_BASE     "/dbbrowser"
43#define AWAR_DBB_TMP_BASE "/tmp" AWAR_DBB_BASE
44
45#define AWAR_DBB_DB      AWAR_DBB_BASE "/db"
46#define AWAR_DBB_ORDER   AWAR_DBB_BASE "/order"
47#define AWAR_DBB_PATH    AWAR_DBB_BASE "/path"
48#define AWAR_DBB_HISTORY AWAR_DBB_BASE "/history"
49
50#define AWAR_DBB_BROWSE AWAR_DBB_TMP_BASE "/browse"
51#define AWAR_DBB_INFO   AWAR_DBB_TMP_BASE "/info"
52
53#define AWAR_DBB_RECSIZE AWAR_DBB_BASE "/recsize"
54
55#define AWAR_DUMP_HEX   AWAR_DBB_BASE "/hex"
56#define AWAR_DUMP_ASCII AWAR_DBB_BASE "/ascii"
57#define AWAR_DUMP_SPACE AWAR_DBB_BASE "/space"
58#define AWAR_DUMP_WIDTH AWAR_DBB_BASE "/width"
59#define AWAR_DUMP_BLOCK AWAR_DBB_BASE "/block"
60
61#define HISTORY_PSEUDO_PATH "*history*"
62#define ENTRY_MAX_LENGTH    1000
63#define HISTORY_MAX_LENGTH  20000
64
65inline bool is_dbbrowser_pseudo_path(const char *path) {
66    return
67        path           &&
68        path[0] == '*' &&
69        strcmp(path, HISTORY_PSEUDO_PATH) == 0;
70}
71
72enum SortOrder {
73    SORT_NONE,
74    SORT_NAME,
75    SORT_CONTAINER,
76    SORT_OCCUR,
77    SORT_TYPE,
78    SORT_CONTENT,
79
80    SORT_COUNT
81};
82
83static const char *sort_order_name[SORT_COUNT] = {
84    "Unsorted",
85    "Name",
86    "Name (Container)",
87    "Name (Occur)",
88    "Type",
89    "Content",
90};
91
92// used to sort entries in list
93struct list_entry {
94    const char *key_name;
95    GB_TYPES    type;
96    int         childCount;     // -1 if only one child with key_name exists
97    GBDATA     *gbd;
98    string      content;
99
100    static SortOrder sort_order;
101
102    inline bool less_than_by_name(const list_entry& other) const {
103        int cmp = ARB_stricmp(key_name, other.key_name);
104        if (cmp != 0) return (cmp<0); // name differs!
105        return childCount<other.childCount; // equal names -> compare child count
106    }
107
108    inline int cmp_by_container(const list_entry& other) const { return int(type != GB_DB) - int(other.type != GB_DB); }
109    inline int cmp_by_childcount(const list_entry& other) const { return childCount - other.childCount; }
110
111    inline bool less_than_by_container_name(const list_entry& other) const {
112        int cmp = cmp_by_container(other);
113        if (cmp == 0) return less_than_by_name(other);
114        return cmp<0;
115    }
116    inline bool less_than_by_childcount_name(const list_entry& other) const {
117        int cmp = cmp_by_childcount(other);
118        if (cmp == 0) return less_than_by_name(other);
119        return cmp<0;
120    }
121
122    bool operator<(const list_entry& other) const {
123        bool is_less = false;
124        switch (sort_order) {
125            case SORT_COUNT: break;
126            case SORT_NONE:
127                awt_assert(0);  // not possible
128                break;
129
130            case SORT_NAME:
131                is_less = less_than_by_name(other);
132                break;
133
134            case SORT_CONTAINER:
135                is_less = less_than_by_container_name(other);
136                break;
137
138            case SORT_OCCUR:
139                is_less = less_than_by_childcount_name(other);
140                break;
141
142            case SORT_CONTENT: {
143                int cmp = ARB_stricmp(content.c_str(), other.content.c_str());
144
145                if (cmp != 0) is_less = cmp<0;
146                else is_less          = less_than_by_container_name(other);
147
148                break;
149            }
150            case SORT_TYPE: {
151                int cmp = type-other.type;
152
153                if (cmp == 0) is_less = less_than_by_name(other);
154                else is_less          = cmp<0;
155
156                break;
157            }
158        }
159        return is_less;
160    }
161};
162
163SortOrder list_entry::sort_order = SORT_NONE;
164
165// ---------------------
166//      create AWARs
167
168static MemDump make_userdefined_MemDump(AW_root *awr) {
169    bool   hex      = awr->awar(AWAR_DUMP_HEX)->read_int();
170    bool   ascii    = awr->awar(AWAR_DUMP_ASCII)->read_int();
171    bool   space    = awr->awar(AWAR_DUMP_SPACE)->read_int();
172    size_t width    = awr->awar(AWAR_DUMP_WIDTH)->read_int();
173    size_t separate = awr->awar(AWAR_DUMP_BLOCK)->read_int();
174   
175    bool offset = (hex||ascii) && width;
176
177    return MemDump(offset, hex, ascii, width, separate, space);
178}
179
180static void nodedisplay_changed_cb(AW_root *aw_root) {
181    aw_root->awar(AWAR_DBB_BROWSE)->touch();
182}
183
184void AWT_create_db_browser_awars(AW_root *aw_root, AW_default aw_def) {
185    aw_root->awar_int   (AWAR_DBB_DB,      0,                     aw_def); // index to internal order of announced databases
186    aw_root->awar_int   (AWAR_DBB_ORDER,   SORT_CONTAINER,        aw_def); // sort order for "browse"-box
187    aw_root->awar_string(AWAR_DBB_PATH,    "/",                   aw_def); // path in database
188    aw_root->awar_string(AWAR_DBB_BROWSE,  "",                    aw_def); // selection in browser (= child name)
189    aw_root->awar_string(AWAR_DBB_INFO,    "<select an element>", aw_def); // information about selected item
190    aw_root->awar_string(AWAR_DBB_HISTORY, "",                    aw_def); // '\n'-separated string containing visited nodes
191
192    aw_root->awar_int(AWAR_DBB_RECSIZE, 0,  aw_def)->add_callback(nodedisplay_changed_cb); // collect size recursive?
193
194    // hex-dump-options
195    aw_root->awar_int(AWAR_DUMP_HEX,   1,  aw_def)->add_callback(nodedisplay_changed_cb); // show hex ?
196    aw_root->awar_int(AWAR_DUMP_ASCII, 1,  aw_def)->add_callback(nodedisplay_changed_cb); // show ascii ?
197    aw_root->awar_int(AWAR_DUMP_SPACE, 1,  aw_def)->add_callback(nodedisplay_changed_cb); // space bytes
198    aw_root->awar_int(AWAR_DUMP_WIDTH, 16, aw_def)->add_callback(nodedisplay_changed_cb); // bytes/line
199    aw_root->awar_int(AWAR_DUMP_BLOCK, 8,  aw_def)->add_callback(nodedisplay_changed_cb); // separate each bytes
200}
201
202static GBDATA *GB_search_numbered(GBDATA *gbd, const char *str, GB_TYPES create) { // @@@  this may be moved to ARBDB-sources
203    awt_assert(!GB_have_error());
204    if (str) {
205        if (str[0] == '/' && str[1] == 0) { // root
206            return GB_get_root(gbd);
207        }
208
209        const char *first_bracket = strchr(str, '[');
210        if (first_bracket) {
211            const char *second_bracket = strchr(first_bracket+1, ']');
212            if (second_bracket && (second_bracket[1] == 0 || second_bracket[1] == '/')) {
213                int count = atoi(first_bracket+1);
214                if (count >= 0 && isdigit(first_bracket[1])) {
215                    // we are sure we have sth with number in brackets (e.g. "/species_data/species[42]/name")
216                    const char *previous_slash = first_bracket-1;
217                    while (previous_slash >= str && previous_slash[0] != '/') previous_slash--; //
218                    if (previous_slash<str) previous_slash = 0;
219
220                    GBDATA *gb_parent = 0;
221                    {
222                        if (previous_slash) { // found a slash
223                            char *parent_path = GB_strpartdup(str, previous_slash-1);
224
225                            // we are sure parent path does not contain brackets -> search normal
226                            if (parent_path[0] == 0) { // that means : root-item is numbered (e.g. '/species_data[7]/...')
227                                gb_parent = GB_get_root(gbd);
228                            }
229                            else {
230                                gb_parent = GB_search(gbd, parent_path, GB_FIND);
231                            }
232
233                            if (!gb_parent) fprintf(stderr, "Warning: parent '%s' not found\n", parent_path);
234                            free(parent_path);
235                        }
236                        else {
237                            gb_parent = gbd;
238                        }
239                    }
240
241                    if (gb_parent) {
242                        GBDATA *gb_son = 0;
243                        {
244                            const char *name_start = previous_slash ? previous_slash+1 : str;
245                            char       *key_name   = GB_strpartdup(name_start, first_bracket-1);
246                            int         c          = 0;
247
248                            gb_son = GB_entry(gb_parent, key_name);
249                            while (c<count && gb_son) {
250                                gb_son = GB_nextEntry(gb_son);
251                                if (gb_son) ++c;
252                            }
253
254                            if (!gb_son) fprintf(stderr, "Warning: did not find %i. son '%s'\n", count, key_name);
255                            free(key_name);
256                        }
257
258                        if (gb_son) {
259                            const char *rest = 0;
260                            if (second_bracket[1] == '/') { // continue search ?
261                                if (second_bracket[2]) {
262                                    rest = second_bracket+2;
263                                }
264                            }
265
266                            return rest
267                                ? GB_search_numbered(gb_son, rest, create) // recursive search
268                                : gb_son; // numbering occurred at end of search path
269                        }
270                    }
271                    else {
272                        fprintf(stderr, "Warning: don't know where to start numbered search in '%s'\n", str);
273                    }
274
275                    return 0; // not found
276                }
277                else {
278                    fprintf(stderr, "Warning: Illegal content in search path - expected digits at '%s'\n", first_bracket+1);
279                }
280            }
281            else {
282                fprintf(stderr, "Warning: Unbalanced or illegal [] in search path (%s)\n", str);
283            }
284        }
285        // no brackets -> normal search
286    }
287    awt_assert(!GB_have_error());
288    return GB_search(gbd, str, create); // do normal search
289}
290
291// -----------------------
292//      class KnownDB
293
294class KnownDB {
295    GBDATA& gb_main; 
296    string  description;
297    string  current_path;
298
299public:
300    KnownDB(GBDATA *gb_main_, const char *description_)
301        : gb_main(*gb_main_)
302        , description(description_)
303        , current_path("/")
304    {}
305    KnownDB(const KnownDB& other)
306        : gb_main(other.gb_main),
307          description(other.description),
308          current_path(other.current_path)
309    {}
310    DECLARE_ASSIGNMENT_OPERATOR(KnownDB);
311
312    const GBDATA *get_db() const { return &gb_main; }
313    const string& get_description() const { return description; }
314
315    const string& get_path() const { return current_path; }
316    void set_path(const string& path) { current_path = path; }
317    void set_path(const char* path) { current_path = path; }
318};
319
320class hasDB {
321    GBDATA *db;
322public:
323    hasDB(GBDATA *gbm) : db(gbm) {}
324    bool operator()(const KnownDB& kdb) { return kdb.get_db() == db; }
325};
326
327// --------------------------
328//      class DB_browser
329
330class DB_browser;
331static DB_browser *get_the_browser(bool autocreate);
332
333class DB_browser {
334    typedef vector<KnownDB>::iterator KnownDBiterator;
335
336    vector<KnownDB> known_databases;
337    size_t          current_db; // index of current db (in known_databases)
338
339    AW_window             *aww;                     // browser window
340    AW_option_menu_struct *oms;                     // the DB selector
341    AW_selection_list     *browse_list;             // the browse subwindow
342
343    static SmartPtr<DB_browser> the_browser;
344    friend DB_browser *get_the_browser(bool autocreate);
345
346    void update_DB_selector();
347
348    DB_browser(const DB_browser& other);            // copying not allowed
349    DB_browser& operator = (const DB_browser& other); // assignment not allowed
350public:
351    DB_browser() : current_db(0), aww(0), oms(0) {}
352
353    void add_db(GBDATA *gb_main, const char *description) {
354        known_databases.push_back(KnownDB(gb_main, description));
355        if (aww) update_DB_selector();
356    }
357
358    void del_db(GBDATA *gb_main) {
359        KnownDBiterator known = find_if(known_databases.begin(), known_databases.end(), hasDB(gb_main));
360        if (known != known_databases.end()) known_databases.erase(known);
361#if defined(DEBUG)
362        else awt_assert(0); // no need to delete unknown databases
363#endif // DEBUG
364    }
365
366    AW_window *get_window(AW_root *aw_root);
367    AW_selection_list *get_browser_list() { return browse_list; }
368
369    bool legal_selected_db() const { return current_db < known_databases.size(); }
370
371    size_t get_selected_db() const { awt_assert(legal_selected_db()); return current_db; }
372    void set_selected_db(size_t idx) { awt_assert(idx < known_databases.size()); current_db = idx; }
373
374    const char *get_path() const { return known_databases[get_selected_db()].get_path().c_str(); }
375    void set_path(const char *new_path) { known_databases[get_selected_db()].set_path(new_path); }
376
377    GBDATA *get_db() const {
378        return legal_selected_db() ? const_cast<GBDATA*>(known_databases[get_selected_db()].get_db()) : NULL;
379    }
380};
381
382
383// -----------------------------
384//      DB_browser singleton
385
386SmartPtr<DB_browser> DB_browser::the_browser;
387
388static DB_browser *get_the_browser(bool autocreate = true) {
389    if (DB_browser::the_browser.isNull() && autocreate) {
390        DB_browser::the_browser = new DB_browser;
391    }
392    return &*DB_browser::the_browser;
393}
394
395// --------------------------
396//      announce databases
397
398void AWT_announce_db_to_browser(GBDATA *gb_main, const char *description) {
399    get_the_browser()->add_db(gb_main, description);
400}
401
402static void AWT_announce_properties_to_browser(GBDATA *gb_defaults, const char *defaults_name) {
403    AWT_announce_db_to_browser(gb_defaults, GBS_global_string("Properties (%s)", defaults_name));
404}
405
406void AWT_browser_forget_db(GBDATA *gb_main) {
407    if (gb_main) {
408        DB_browser *browser = get_the_browser(false);
409        awt_assert(browser);
410        if (browser) browser->del_db(gb_main);
411    }
412}
413
414// ---------------------------------------
415//      browser window callbacks
416
417static void toggle_tmp_cb(AW_window *aww) {
418    AW_awar *awar_path = aww->get_root()->awar(AWAR_DBB_PATH);
419    char    *path      = awar_path->read_string();
420    bool     done      = false;
421
422    if (ARB_strBeginsWith(path, "/tmp")) {
423        if (path[4] == '/') {
424            awar_path->write_string(path+4);
425            done = true;
426        }
427        else if (path[4] == 0) {
428            awar_path->write_string("/");
429            done = true;
430        }
431    }
432
433    if (!done && !is_dbbrowser_pseudo_path(path)) {
434        char *path_in_tmp = GBS_global_string_copy("/tmp%s", path);
435
436        char *lslash = strrchr(path_in_tmp, '/');
437        if (lslash && !lslash[1]) { // ends with '/'
438            lslash[0] = 0; // cut-off trailing '/'
439        }
440        awar_path->write_string(path_in_tmp);
441        free(path_in_tmp);
442    }
443    free(path);
444}
445
446static void show_history_cb(AW_window *aww) {
447    aww->get_root()->awar(AWAR_DBB_PATH)->write_string(HISTORY_PSEUDO_PATH);
448}
449
450static void goto_root_cb(AW_window *aww) {
451    AW_awar *awar_path = aww->get_root()->awar(AWAR_DBB_PATH);
452    awar_path->write_string("/");
453}
454static void go_up_cb(AW_window *aww) {
455    AW_awar *awar_path = aww->get_root()->awar(AWAR_DBB_PATH);
456    char    *path      = awar_path->read_string();
457    char    *lslash    = strrchr(path, '/');
458
459    if (lslash) {
460        lslash[0] = 0;
461        if (!path[0]) strcpy(path, "/");
462        awar_path->write_string(path);
463    }
464}
465
466// --------------------------
467//      browser commands:
468
469#define BROWSE_CMD_PREFIX          "browse_cmd___"
470#define BROWSE_CMD_GOTO_VALID_NODE BROWSE_CMD_PREFIX "goto_valid_node"
471#define BROWSE_CMD_GO_UP           BROWSE_CMD_PREFIX "go_up"
472
473struct BrowserCommand {
474    const char *name;
475    GB_ERROR (*function)(AW_window *);
476};
477
478
479static GB_ERROR browse_cmd_go_up(AW_window *aww) {
480    go_up_cb(aww);
481    return 0;
482}
483static GB_ERROR browse_cmd_goto_valid_node(AW_window *aww) {
484    AW_root *aw_root   = aww->get_root();
485    AW_awar *awar_path = aw_root->awar(AWAR_DBB_PATH);
486    char    *path      = awar_path->read_string();
487    int      len       = strlen(path);
488    GBDATA  *gb_main   = get_the_browser()->get_db();
489
490    {
491        GB_transaction t(gb_main);
492        while (len>0 && GB_search_numbered(gb_main, path, GB_FIND) == 0) {
493            GB_clear_error();
494            path[--len] = 0;
495        }
496    }
497
498    awar_path->write_string(len ? path : "/");
499    aw_root->awar(AWAR_DBB_BROWSE)->write_string("");
500    return 0;
501}
502
503static BrowserCommand browser_command_table[] = {
504    { BROWSE_CMD_GOTO_VALID_NODE, browse_cmd_goto_valid_node },
505    { BROWSE_CMD_GO_UP, browse_cmd_go_up },
506    { 0, 0 }
507};
508
509static void execute_browser_command(AW_window *aww, const char *command) {
510    int idx;
511    for (idx = 0; browser_command_table[idx].name; ++idx) {
512        if (strcmp(command, browser_command_table[idx].name) == 0) {
513            GB_ERROR error = browser_command_table[idx].function(aww);
514            if (error) aw_message(error);
515            break;
516        }
517    }
518
519    if (!browser_command_table[idx].name) {
520        aw_message(GBS_global_string("Unknown browser command '%s'", command));
521    }
522}
523
524// ----------------------------
525//      the browser window
526
527static AW_window *create_db_browser(AW_root *aw_root) {
528    return get_the_browser()->get_window(aw_root);
529}
530
531struct counterPair {
532    int occur, count;
533    counterPair() : occur(0), count(0) {}
534};
535
536inline void insert_history_selection(AW_selection_list *sel, const char *entry, int wanted_db) {
537    const char *colon = strchr(entry, ':');
538    if (colon && (atoi(entry) == wanted_db)) {
539        sel->insert(colon+1, colon+1);
540    }
541}
542
543
544static char *get_container_description(GBDATA *gbd) {
545    char *content = NULL;
546    const char *known_children[] = { "@name", "name", "key_name", "alignment_name", "group_name", "key_text", 0 };
547    for (int i = 0; known_children[i]; ++i) {
548        GBDATA *gb_known = GB_entry(gbd, known_children[i]);
549        if (gb_known && GB_read_type(gb_known) != GB_DB && GB_nextEntry(gb_known) == 0) { // exactly one child exits
550            char *asStr = GB_read_as_string(gb_known);
551            content     = GBS_global_string_copy("[%s=%s]", known_children[i], asStr);
552            free(asStr);
553            break;
554        }
555    }
556
557    return content;
558}
559
560static char *get_dbentry_content(GBDATA *gbd, GB_TYPES type, bool shorten_repeats, const MemDump& dump) {
561    awt_assert(type != GB_DB);
562
563    char *content = NULL;
564    if (!dump.wrapped()) content = GB_read_as_string(gbd); // @@@
565    if (!content) { // use dumper
566        long        size;
567        if (type == GB_POINTER) {
568            size = sizeof(GBDATA*);
569        }
570        else {
571            size = GB_read_count(gbd);
572        }
573        const int plen = 30;
574        GBS_strstruct buf(dump.mem_needed_for_dump(size)+plen);
575
576        if (!dump.wrapped()) buf.nprintf(plen, "<%li bytes>: ", size);
577        dump.dump_to(buf, GB_read_pntr(gbd), size);
578
579        content         = buf.release();
580        shorten_repeats = false;
581    }
582    size_t len = shorten_repeats ? GBS_shorten_repeated_data(content) : strlen(content);
583    if (!dump.wrapped() && len>(ENTRY_MAX_LENGTH+15)) {
584        content[ENTRY_MAX_LENGTH] = 0;
585        freeset(content, GBS_global_string_copy("%s [rest skipped]", content));
586    }
587    return content;
588}
589
590static void update_browser_selection_list(AW_root *aw_root, AW_selection_list *id) {
591    awt_assert(!GB_have_error());
592    DB_browser *browser = get_the_browser();
593    char       *path    = aw_root->awar(AWAR_DBB_PATH)->read_string();
594    bool        is_root;
595    GBDATA     *node    = NULL;
596
597    {
598        GBDATA *gb_main = browser->get_db();
599        if (gb_main) {
600            GB_transaction ta(gb_main);
601            is_root = (strcmp(path, "/") == 0);
602            node    = GB_search_numbered(gb_main, path, GB_FIND);
603        }
604    }
605
606    id->clear();
607
608    if (node == 0) {
609        if (strcmp(path, HISTORY_PSEUDO_PATH) == 0) {
610            GB_clear_error(); // ignore error about invalid key
611            char *history = aw_root->awar(AWAR_DBB_HISTORY)->read_string();
612            id->insert("Previously visited nodes:", "");
613            char *start   = history;
614            int   curr_db = aw_root->awar(AWAR_DBB_DB)->read_int();
615
616            for (char *lf = strchr(start, '\n'); lf; start = lf+1, lf = strchr(start, '\n')) {
617                lf[0] = 0;
618                insert_history_selection(id, start, curr_db);
619            }
620            insert_history_selection(id, start, curr_db);
621            free(history);
622        }
623        else {
624            if (GB_have_error()) id->insert(GBS_global_string("Error: %s", GB_await_error()), "");
625            id->insert("No such node!", "");
626            id->insert("-> goto valid node", BROWSE_CMD_GOTO_VALID_NODE);
627        }
628    }
629    else {
630        map<string, counterPair> child_count;
631        GB_transaction           ta(browser->get_db());
632
633        for (GBDATA *child = GB_child(node); child; child = GB_nextChild(child)) {
634            const char *key_name   = GB_read_key_pntr(child);
635            child_count[key_name].occur++;
636        }
637
638        int maxkeylen = 0;
639        int maxtypelen = 0;
640        for (map<string, counterPair>::iterator i = child_count.begin(); i != child_count.end(); ++i) {
641            {
642                int keylen   = i->first.length();
643                int maxcount = i->second.occur;
644
645                if (maxcount != 1) {
646                    keylen += 2;    // brackets
647                    while (maxcount) { maxcount /= 10; keylen++; } // increase keylen for each digit
648                }
649
650                if (keylen>maxkeylen) maxkeylen = keylen;
651            }
652
653            {
654                GBDATA     *child     = GB_entry(node, i->first.c_str()); // find first child
655                const char *type_name = GB_get_type_name(child);
656                int         typelen   = strlen(type_name);
657
658                if (typelen>maxtypelen) maxtypelen = typelen;
659            }
660        }
661
662        if (!is_root) {
663            id->insert(GBS_global_string("%-*s   parent container", maxkeylen, ".."), BROWSE_CMD_GO_UP);
664            id->insert(GBS_global_string("%-*s   container", maxkeylen, "."), "");
665        }
666        else {
667            id->insert(GBS_global_string("%-*s   root container", maxkeylen, "/"), "");
668        }
669       
670        // collect children and sort them
671
672        vector<list_entry> sorted_children;
673
674        MemDump simpleDump(false, true, false);
675        for (GBDATA *child = GB_child(node); child; child = GB_nextChild(child)) {
676            list_entry entry;
677            entry.key_name   = GB_read_key_pntr(child);
678            entry.childCount = -1;
679            entry.type       = GB_read_type(child);
680            entry.gbd        = child;
681
682            int occurrences = child_count[entry.key_name].occur;
683            if (occurrences != 1) {
684                entry.childCount = child_count[entry.key_name].count;
685                child_count[entry.key_name].count++;
686            }
687            char *display = NULL;
688            if (entry.type == GB_DB) {
689                display = get_container_description(entry.gbd);
690            }
691            else {
692                display = get_dbentry_content(entry.gbd, entry.type, true, simpleDump);
693            }
694            if (display) entry.content = display;
695            sorted_children.push_back(entry);
696        }
697
698        list_entry::sort_order = (SortOrder)aw_root->awar(AWAR_DBB_ORDER)->read_int();
699        if (list_entry::sort_order != SORT_NONE) {
700            sort(sorted_children.begin(), sorted_children.end());
701        }
702
703        for (vector<list_entry>::iterator ch = sorted_children.begin(); ch != sorted_children.end(); ++ch) {
704            const list_entry&  entry            = *ch;
705            const char        *key_name         = entry.key_name;
706            char              *numbered_keyname = 0;
707
708            if (entry.childCount >= 0) {
709                numbered_keyname = GBS_global_string_copy("%s[%i]", key_name, entry.childCount);
710            }
711
712            key_name = numbered_keyname ? numbered_keyname : key_name;
713            const char *displayed = GBS_global_string("%-*s   %-*s   %s", maxkeylen, key_name, maxtypelen, GB_get_type_name(entry.gbd), entry.content.c_str());
714            id->insert(displayed, key_name);
715
716            free(numbered_keyname);
717        }
718    }
719    id->insert_default("", "");
720    id->update();
721
722    free(path);
723    awt_assert(!GB_have_error());
724}
725
726static void order_changed_cb(AW_root *aw_root) {
727    DB_browser *browser = get_the_browser();
728    update_browser_selection_list(aw_root, browser->get_browser_list());
729}
730
731inline char *strmove(char *dest, char *source) {
732    return (char*)memmove(dest, source, strlen(source)+1);
733}
734
735static void add_to_history(AW_root *aw_root, const char *path) {
736    // adds 'path' to history
737
738    if (strcmp(path, HISTORY_PSEUDO_PATH) != 0) {
739        int      db           = aw_root->awar(AWAR_DBB_DB)->read_int();
740        AW_awar *awar_history = aw_root->awar(AWAR_DBB_HISTORY);
741        char    *old_history  = awar_history->read_string();
742        char    *entry        = GBS_global_string_copy("%i:%s", db, path);
743        int      entry_len    = strlen(entry);
744
745        char *found = strstr(old_history, entry);
746        while (found) {
747            if (found == old_history || found[-1] == '\n') { // found start of an entry
748                if (found[entry_len] == '\n') {
749                    strmove(found, found+entry_len+1);
750                    found = strstr(old_history, entry);
751                }
752                else if (found[entry_len] == 0) { // found as last entry
753                    if (found == old_history) { // which is also first entry
754                        old_history[0] = 0; // empty history
755                    }
756                    else {
757                        strmove(found, found+entry_len+1);
758                    }
759                    found = strstr(old_history, entry);
760                }
761                else {
762                    found = strstr(found+1, entry);
763                }
764            }
765            else {
766                found = strstr(found+1, entry);
767            }
768        }
769
770        if (old_history[0]) {
771            char *new_history = GBS_global_string_copy("%s\n%s", entry, old_history);
772
773            while (strlen(old_history)>HISTORY_MAX_LENGTH) { // shorten history
774                char *llf = strrchr(old_history, '\n');
775                if (!llf) break;
776                llf[0]    = 0;
777            }
778
779            awar_history->write_string(new_history);
780            free(new_history);
781        }
782        else {
783            awar_history->write_string(entry);
784        }
785
786        free(entry);
787        free(old_history);
788    }
789}
790
791static bool    inside_path_change = false;
792static GBDATA *gb_tracked_node    = NULL;
793
794static void selected_node_modified_cb(GBDATA *gb_node, GB_CB_TYPE cb_type) {
795    awt_assert(gb_node == gb_tracked_node);
796
797    if (cb_type & GB_CB_DELETE) {
798        gb_tracked_node = NULL; // no need to remove callback ?
799    }
800
801    if (!inside_path_change) { // ignore refresh callbacks triggered by dbbrowser-awars
802        static bool avoid_recursion = false;
803        if (!avoid_recursion) {
804            LocallyModify<bool> flag(avoid_recursion, true);
805
806            AW_root *aw_root   = AW_root::SINGLETON;
807            AW_awar *awar_path = aw_root->awar_no_error(AWAR_DBB_PATH);
808            if (awar_path) { // avoid crash during exit
809                AW_awar    *awar_child = aw_root->awar(AWAR_DBB_BROWSE);
810                const char *child      = awar_child->read_char_pntr();
811
812                if (child[0]) {
813                    const char *path     = awar_path->read_char_pntr();
814                    const char *new_path;
815
816                    if (!path[0] || !path[1]) {
817                        new_path = GBS_global_string("/%s", child);
818                    }
819                    else {
820                        new_path = GBS_global_string("%s/%s", path, child);
821                    }
822
823                    char *fixed_path = GBS_string_eval(new_path, "//=/", NULL);
824                    awar_path->write_string(fixed_path);
825                    free(fixed_path);
826                }
827                else {
828                    awar_path->touch();
829                }
830            }
831        }
832    }
833}
834static void untrack_node() {
835    if (gb_tracked_node) {
836        GB_remove_callback(gb_tracked_node, GB_CB_ALL, makeDatabaseCallback(selected_node_modified_cb));
837        gb_tracked_node = NULL;
838    }
839}
840static void track_node(GBDATA *gb_node) {
841    untrack_node();
842    GB_ERROR error = GB_add_callback(gb_node, GB_CB_ALL, makeDatabaseCallback(selected_node_modified_cb));
843    if (error) {
844        aw_message(error);
845    }
846    else {
847        gb_tracked_node = gb_node;
848    }
849}
850
851static void child_changed_cb(AW_root *aw_root) {
852    char *child = aw_root->awar(AWAR_DBB_BROWSE)->read_string();
853    if (strncmp(child, BROWSE_CMD_PREFIX, sizeof(BROWSE_CMD_PREFIX)-1) == 0) { // a symbolic browser command
854        execute_browser_command(get_the_browser()->get_window(aw_root), child);
855    }
856    else {
857        char *path = aw_root->awar(AWAR_DBB_PATH)->read_string();
858
859        if (strcmp(path, HISTORY_PSEUDO_PATH) == 0) {
860            if (child[0]) {
861                LocallyModify<bool> flag(inside_path_change, true);
862                aw_root->awar(AWAR_DBB_PATH)->write_string(child);
863            }
864        }
865        else {
866            static bool avoid_recursion = false;
867            if (!avoid_recursion) {
868                LocallyModify<bool> flag(avoid_recursion, true);
869
870                char *fullpath;
871                if (strcmp(path, "/") == 0) {
872                    fullpath = GBS_global_string_copy("/%s", child);
873                }
874                else if (child[0] == 0) {
875                    fullpath = strdup(path);
876                }
877                else {
878                    fullpath = GBS_global_string_copy("%s/%s", path, child);
879                }
880
881                DB_browser *browser = get_the_browser();
882                GBDATA     *gb_main = browser->get_db();
883
884                if (gb_main) {
885                    GB_transaction  ta(gb_main);
886                    GBDATA         *gb_selected_node = GB_search_numbered(gb_main, fullpath, GB_FIND);
887
888                    string info;
889                    info += GBS_global_string("Fullpath  | '%s'\n", fullpath);
890
891                    if (gb_selected_node == 0) {
892                        info += "Address   | NULL\n";
893                        info += GBS_global_string("Error     | %s\n", GB_have_error() ? GB_await_error() : "<none>");
894                    }
895                    else {
896                        add_to_history(aw_root, fullpath);
897
898                        info += GBS_global_string("Address   | %p\n", gb_selected_node);
899                        info += GBS_global_string("Key index | %i\n", GB_get_quark(gb_selected_node));
900
901
902                        GB_SizeInfo sizeInfo;
903                        bool        collect_recursive = aw_root->awar(AWAR_DBB_RECSIZE)->read_int();
904
905                        GB_TYPES type = GB_read_type(gb_selected_node);
906                        if (collect_recursive || type != GB_DB) {
907                            sizeInfo.collect(gb_selected_node);
908                        }
909
910                        string structure_add;
911                        string childs;
912
913                        if (type == GB_DB) {
914                            long struct_size = GB_calc_structure_size(gb_selected_node);
915                            if (collect_recursive) {
916                                structure_add = GBS_global_string(" (this: %s)", GBS_readable_size(struct_size, "b"));
917                            }
918                            else {
919                                sizeInfo.structure = struct_size;
920                            }
921
922                            info += "Node type | container\n";
923
924                            childs = GBS_global_string("Childs    | %li", GB_number_of_subentries(gb_selected_node));
925                            if (collect_recursive) {
926                                childs = childs+" (rec: " + GBS_readable_size(sizeInfo.containers, "containers");
927                                childs = childs+" + " + GBS_readable_size(sizeInfo.terminals, "terminals");
928                                childs = childs+" = " + GBS_readable_size(sizeInfo.terminals+sizeInfo.containers, "nodes")+')';
929                            }
930                            childs += '\n';
931
932                            {
933                                LocallyModify<bool> flag2(inside_path_change, true);
934                                aw_root->awar(AWAR_DBB_BROWSE)->write_string("");
935                                aw_root->awar(AWAR_DBB_PATH)->write_string(fullpath);
936                            }
937                        }
938                        else {
939                            info += GBS_global_string("Node type | data [type=%s]\n", GB_get_type_name(gb_selected_node));
940                        }
941
942                        long overall = sizeInfo.mem+sizeInfo.structure;
943
944                        info += GBS_global_string("Data size | %7s\n", GBS_readable_size(sizeInfo.data, "b"));
945                        info += GBS_global_string("Memory    | %7s  %5.2f%%\n", GBS_readable_size(sizeInfo.mem, "b"), double(sizeInfo.mem)/overall*100+.0049);
946                        info += GBS_global_string("Structure | %7s  %5.2f%%", GBS_readable_size(sizeInfo.structure, "b"), double(sizeInfo.structure)/overall*100+.0049) + structure_add + '\n';
947                        info += GBS_global_string("Overall   | %7s\n", GBS_readable_size(overall, "b"));
948                        if (sizeInfo.data) {
949                            info += GBS_global_string("CompRatio | %5.2f%% (mem-only: %5.2f%%)\n", double(overall)/sizeInfo.data*100+.0049, double(sizeInfo.mem)/sizeInfo.data*100+.0049);
950                        }
951                        info += childs;
952
953                        {
954                            bool is_tmp             = GB_is_temporary(gb_selected_node);
955                            bool not_tmp_but_in_tmp = !is_tmp && GB_in_temporary_branch(gb_selected_node);
956
957                            if (is_tmp || not_tmp_but_in_tmp) {
958                                info += GBS_global_string("Temporary | yes%s\n", not_tmp_but_in_tmp ? " (in temporary branch)" : "");
959                            }
960                        }
961
962                        info += GBS_global_string("Security  | read=%i write=%i delete=%i\n",
963                                                  GB_read_security_read(gb_selected_node),
964                                                  GB_read_security_write(gb_selected_node),
965                                                  GB_read_security_delete(gb_selected_node));
966
967                        char *callback_info = GB_get_callback_info(gb_selected_node);
968                        if (callback_info) {
969                            ConstStrArray callbacks;
970                            GBT_splitNdestroy_string(callbacks, callback_info, "\n", true);
971
972                            for (size_t i = 0; i<callbacks.size(); ++i) {
973                                const char *prefix = i ? "         " : "Callbacks";
974                                info               = info + prefix + " | " + callbacks[i] + '\n';
975                            }
976
977                            if (gb_selected_node == gb_tracked_node) {
978                                info += "          | (Note: one callback was installed by this browser)\n";
979                            }
980                        }
981
982                        if (type != GB_DB) {
983                            MemDump  dump    = make_userdefined_MemDump(aw_root);
984                            char    *content = get_dbentry_content(gb_selected_node, GB_read_type(gb_selected_node), false, dump);
985                            info             = info+"\nContent:\n"+content+'\n';
986                            free(content);
987                        }
988                    }
989
990                    aw_root->awar(AWAR_DBB_INFO)->write_string(info.c_str());
991                }
992                free(fullpath);
993            }
994        }
995
996        free(path);
997    }
998    free(child);
999}
1000
1001static void path_changed_cb(AW_root *aw_root) {
1002    static bool avoid_recursion = false;
1003    if (!avoid_recursion) {
1004        awt_assert(!GB_have_error());
1005        LocallyModify<bool> flag(avoid_recursion, true);
1006
1007        DB_browser *browser    = get_the_browser();
1008        char       *goto_child = 0;
1009
1010        GBDATA *gb_main = browser->get_db();
1011        if (gb_main) {
1012            GB_transaction  t(gb_main);
1013            AW_awar        *awar_path = aw_root->awar(AWAR_DBB_PATH);
1014            char           *path      = awar_path->read_string();
1015            GBDATA         *found     = GB_search_numbered(gb_main, path, GB_FIND);
1016
1017            if (found && GB_read_type(found) != GB_DB) { // exists, but is not a container
1018                char *lslash = strrchr(path, '/');
1019                if (lslash) {
1020                    goto_child = strdup(lslash+1);
1021                    lslash[lslash == path] = 0; // truncate at last slash (but keep sole slash)
1022                    awar_path->write_string(path);
1023                }
1024            }
1025
1026            if (found) {
1027                LocallyModify<bool> flag2(inside_path_change, true);
1028                add_to_history(aw_root, goto_child ? GBS_global_string("%s/%s", path, goto_child) : path);
1029                GBDATA *father = GB_get_father(found);
1030                track_node(father ? father : found);
1031            }
1032            else if (is_dbbrowser_pseudo_path(path)) {
1033                GB_clear_error(); // ignore error about invalid key
1034            }
1035            else if (GB_have_error()) {
1036                aw_message(GB_await_error());
1037            }
1038            browser->set_path(path);
1039            free(path);
1040        }
1041
1042        update_browser_selection_list(aw_root, browser->get_browser_list());
1043
1044        LocallyModify<bool> flag2(inside_path_change, true);
1045        aw_root->awar(AWAR_DBB_BROWSE)->rewrite_string(goto_child ? goto_child : "");
1046        awt_assert(!GB_have_error());
1047    }
1048}
1049static void db_changed_cb(AW_root *aw_root) {
1050    int         selected = aw_root->awar(AWAR_DBB_DB)->read_int();
1051    DB_browser *browser  = get_the_browser();
1052
1053    LocallyModify<bool> flag(inside_path_change, true);
1054    browser->set_selected_db(selected);
1055    aw_root->awar(AWAR_DBB_PATH)->rewrite_string(browser->get_path());
1056}
1057
1058void DB_browser::update_DB_selector() {
1059    if (!oms) oms = aww->create_option_menu(AWAR_DBB_DB, true);
1060    else aww->clear_option_menu(oms);
1061
1062    int idx = 0;
1063    for (KnownDBiterator i = known_databases.begin(); i != known_databases.end(); ++i, ++idx) {
1064        const KnownDB& db = *i;
1065        aww->insert_option(db.get_description().c_str(), "", idx);
1066    }
1067    aww->update_option_menu();
1068}
1069
1070AW_window *DB_browser::get_window(AW_root *aw_root) {
1071    awt_assert(!known_databases.empty()); // have no db to browse
1072
1073    if (!aww) {
1074        // select current db+path
1075        {
1076            int wanted_db = aw_root->awar(AWAR_DBB_DB)->read_int();
1077            int known     = known_databases.size();
1078            if (wanted_db >= known) {
1079                wanted_db = 0;
1080                aw_root->awar(AWAR_DBB_DB)->write_int(wanted_db);
1081                aw_root->awar(AWAR_DBB_PATH)->write_string("/"); // reset
1082            }
1083            set_selected_db(wanted_db);
1084
1085            char *wanted_path = aw_root->awar(AWAR_DBB_PATH)->read_string();
1086            known_databases[wanted_db].set_path(wanted_path);
1087            free(wanted_path);
1088        }
1089
1090        AW_window_simple *aws = new AW_window_simple;
1091        aww                   = aws;
1092        aws->init(aw_root, "DB_BROWSER", "ARB database browser");
1093        aws->load_xfig("dbbrowser.fig");
1094
1095        aws->at("close"); aws->callback((AW_CB0)AW_POPDOWN);
1096        aws->create_button("CLOSE", "CLOSE", "C");
1097
1098        aws->callback(makeHelpCallback("db_browser.hlp"));
1099        aws->at("help");
1100        aws->create_button("HELP", "HELP", "H");
1101
1102        aws->at("db");
1103        update_DB_selector();
1104
1105        aws->at("order");
1106        aws->create_option_menu(AWAR_DBB_ORDER, true);
1107        for (int idx = 0; idx<SORT_COUNT; ++idx) {
1108            aws->insert_option(sort_order_name[idx], "", idx);
1109        }
1110        aws->update_option_menu();
1111
1112        aws->at("path");
1113        aws->create_input_field(AWAR_DBB_PATH, 10);
1114
1115        aws->auto_space(10, 10);
1116        aws->button_length(8);
1117
1118        aws->at("navigation");
1119        aws->callback(go_up_cb); aws->create_button("go_up", "Up");
1120        aws->callback(goto_root_cb); aws->create_button("goto_root", "Top");
1121        aws->callback(show_history_cb); aws->create_button("history", "History");
1122        aws->callback(toggle_tmp_cb); aws->create_button("toggle_tmp", "/tmp");
1123
1124        aws->label("Rec.size"); aws->create_toggle(AWAR_DBB_RECSIZE);
1125
1126        aws->at("browse");
1127        browse_list = aws->create_selection_list(AWAR_DBB_BROWSE, true);
1128        update_browser_selection_list(aw_root, browse_list);
1129
1130        aws->at("infoopt");
1131        aws->label("ASCII"); aws->create_toggle     (AWAR_DUMP_ASCII);
1132        aws->label("Hex");   aws->create_toggle     (AWAR_DUMP_HEX);
1133        aws->label("Sep?");  aws->create_toggle     (AWAR_DUMP_SPACE);
1134        aws->label("Width"); aws->create_input_field(AWAR_DUMP_WIDTH, 3);
1135        aws->label("Block"); aws->create_input_field(AWAR_DUMP_BLOCK, 3);
1136
1137        aws->at("info");
1138        aws->create_text_field(AWAR_DBB_INFO, 40, 40);
1139
1140        // add callbacks
1141        aw_root->awar(AWAR_DBB_BROWSE)->add_callback(child_changed_cb);
1142        aw_root->awar(AWAR_DBB_PATH)->add_callback(path_changed_cb);
1143        aw_root->awar(AWAR_DBB_DB)->add_callback(db_changed_cb);
1144        aw_root->awar(AWAR_DBB_ORDER)->add_callback(order_changed_cb);
1145
1146        db_changed_cb(aw_root); // force update
1147    }
1148    return aww;
1149}
1150
1151static void callallcallbacks(AW_window *aww, int mode) {
1152    static bool running = false; // avoid deadlock
1153    if (!running) {
1154        LocallyModify<bool> flag(running, true);
1155        aww->get_root()->callallcallbacks(mode);
1156    }
1157}
1158
1159
1160
1161void AWT_create_debug_menu(AW_window *awmm) {
1162    awmm->create_menu("4debugz", "z", AWM_ALL);
1163
1164    awmm->insert_menu_topic("-db_browser", "Browse loaded database(s)", "B", "db_browser.hlp", AWM_ALL, create_db_browser);
1165
1166    awmm->sep______________();
1167    {
1168        awmm->insert_sub_menu("Callbacks (dangerous! use at your own risk)", "C", AWM_ALL);
1169        awmm->insert_menu_topic("!run_all_cbs_alph",  "Call all callbacks (alpha-order)",     "a", "", AWM_ALL, makeWindowCallback(callallcallbacks, 0));
1170        awmm->insert_menu_topic("!run_all_cbs_nalph", "Call all callbacks (alpha-reverse)",   "l", "", AWM_ALL, makeWindowCallback(callallcallbacks, 1));
1171        awmm->insert_menu_topic("!run_all_cbs_loc",   "Call all callbacks (code-order)",      "c", "", AWM_ALL, makeWindowCallback(callallcallbacks, 2));
1172        awmm->insert_menu_topic("!run_all_cbs_nloc",  "Call all callbacks (code-reverse)",    "o", "", AWM_ALL, makeWindowCallback(callallcallbacks, 3));
1173        awmm->insert_menu_topic("!run_all_cbs_rnd",   "Call all callbacks (random)",          "r", "", AWM_ALL, makeWindowCallback(callallcallbacks, 4));
1174        awmm->sep______________();
1175        awmm->insert_menu_topic("!forget_called_cbs", "Forget called",     "F", "", AWM_ALL, makeWindowCallback(callallcallbacks, -1));
1176        awmm->insert_menu_topic("!mark_all_called",   "Mark all called",   "M", "", AWM_ALL, makeWindowCallback(callallcallbacks, -2));
1177        awmm->sep______________();
1178        {
1179            awmm->insert_sub_menu("Call repeated", "p", AWM_ALL);
1180            awmm->insert_menu_topic("!run_all_cbs_alph_inf",  "Call all callbacks (alpha-order repeated)",     "a", "", AWM_ALL, makeWindowCallback(callallcallbacks, 8|0));
1181            awmm->insert_menu_topic("!run_all_cbs_nalph_inf", "Call all callbacks (alpha-reverse repeated)",   "l", "", AWM_ALL, makeWindowCallback(callallcallbacks, 8|1));
1182            awmm->insert_menu_topic("!run_all_cbs_loc_inf",   "Call all callbacks (code-order repeated)",      "c", "", AWM_ALL, makeWindowCallback(callallcallbacks, 8|2));
1183            awmm->insert_menu_topic("!run_all_cbs_nloc_inf",  "Call all callbacks (code-reverse repeated)",    "o", "", AWM_ALL, makeWindowCallback(callallcallbacks, 8|3));
1184            awmm->insert_menu_topic("!run_all_cbs_rnd_inf",   "Call all callbacks (random repeated)",          "r", "", AWM_ALL, makeWindowCallback(callallcallbacks, 8|4));
1185            awmm->close_sub_menu();
1186        }
1187        awmm->close_sub_menu();
1188    }
1189    awmm->sep______________();
1190
1191}
1192
1193#if 0
1194void AWT_check_action_ids(AW_root *, const char *) {
1195}
1196#else
1197void AWT_check_action_ids(AW_root *aw_root, const char *suffix) {
1198    // check actions ids (see #428)
1199    // suffix is appended to filenames (needed if one application may have different states)
1200    GB_ERROR error = NULL;
1201    {
1202        SmartPtr<ConstStrArray> action_ids = aw_root->get_action_ids();
1203
1204        const char *checksdir = GB_path_in_ARBLIB("macros/.checks");
1205        char       *save      = GBS_global_string_copy("%s/%s%s_action.ids", checksdir, aw_root->program_name, suffix);
1206
1207        FILE *out       = fopen(save, "wt");
1208        if (!out) error = GB_IO_error("writing", save);
1209        else {
1210            for (size_t i = 0; i<action_ids->size(); ++i) {
1211                fprintf(out, "%s\n", (*action_ids)[i]);
1212            }
1213            fclose(out);
1214        }
1215
1216        if (!error) {
1217            char *expected         = GBS_global_string_copy("%s.expected", save);
1218            bool  asExpected       = ARB_textfiles_have_difflines(expected, save, 0, 0);
1219            if (!asExpected) error = GBS_global_string("action ids differ from expected (see console)");
1220            free(expected);
1221        }
1222
1223        if (!error) GB_unlink(save);
1224        free(save);
1225    }
1226    if (error) fprintf(stderr, "AWT_check_action_ids: Error: %s\n", error);
1227}
1228#endif
1229
1230#endif // DEBUG
1231
1232AW_root *AWT_create_root(const char *properties, const char *program, UserActionTracker *user_tracker, int *argc, char*** argv) {
1233    AW_root *aw_root = new AW_root(properties, program, false, user_tracker, argc, argv);
1234#if defined(DEBUG)
1235    AWT_announce_properties_to_browser(AW_ROOT_DEFAULT, properties);
1236#endif // DEBUG
1237    init_Advisor(aw_root);
1238    return aw_root;
1239}
1240
1241// ------------------------
1242//      callback guards
1243
1244static void before_callback_guard() {
1245    if (GB_have_error()) {
1246        GB_ERROR error = GB_await_error();
1247        aw_message(GBS_global_string("Error not clear before calling callback\n"
1248                                     "Unhandled error was:\n"
1249                                     "%s", error));
1250#if defined(DEVEL_RALF)
1251        awt_assert(0);
1252#endif // DEVEL_RALF
1253    }
1254}
1255static void after_callback_guard() {
1256    if (GB_have_error()) {
1257        GB_ERROR error = GB_await_error();
1258        aw_message(GBS_global_string("Error not handled by callback!\n"
1259                                     "Unhandled error was:\n"
1260                                     "'%s'", error));
1261#if defined(DEVEL_RALF)
1262        awt_assert(0);
1263#endif // DEVEL_RALF
1264    }
1265}
1266
1267void AWT_install_cb_guards() {
1268    awt_assert(!GB_have_error());
1269    AW_cb::set_AW_cb_guards(before_callback_guard, after_callback_guard);
1270}
1271void AWT_install_postcb_cb(AW_postcb_cb postcb_cb) {
1272    AW_cb::set_AW_postcb_cb(postcb_cb);
1273}
1274
Note: See TracBrowser for help on using the repository browser.