source: tags/ms_r16q2/AWT/AWT_db_browser.cxx

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