root/trunk/AWT/AWT_db_browser.cxx

Revision 8729, 38.7 KB (checked in by westram, 13 days ago)
  • [readability]
    • renamed most selection lists still called 'id' or similar
    • renamed some globals (aw_help_global->HELP, pd_gl->PD)
  • removed a few more obsolete AW_window*
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
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
22#include <arb_str.h>
23
24#include <string>
25#include <vector>
26#include <map>
27#include <algorithm>
28
29// do includes above (otherwise depends depend on DEBUG)
30#if defined(DEBUG)
31
32using namespace std;
33
34// used AWARs :
35
36#define AWAR_DBB_BASE     "/dbbrowser"
37#define AWAR_DBB_TMP_BASE "/tmp" AWAR_DBB_BASE
38
39#define AWAR_DBB_DB      AWAR_DBB_BASE "/db"
40#define AWAR_DBB_ORDER   AWAR_DBB_BASE "/order"
41#define AWAR_DBB_PATH    AWAR_DBB_BASE "/path"
42#define AWAR_DBB_HISTORY AWAR_DBB_BASE "/history"
43
44#define AWAR_DBB_BROWSE AWAR_DBB_TMP_BASE "/browse"
45#define AWAR_DBB_INFO   AWAR_DBB_TMP_BASE "/info"
46
47#define AWAR_DUMP_HEX   AWAR_DBB_BASE "/hex"
48#define AWAR_DUMP_ASCII AWAR_DBB_BASE "/ascii"
49#define AWAR_DUMP_SPACE AWAR_DBB_BASE "/space"
50#define AWAR_DUMP_WIDTH AWAR_DBB_BASE "/width"
51#define AWAR_DUMP_BLOCK AWAR_DBB_BASE "/block"
52
53#define HISTORY_PSEUDO_PATH "*history*"
54#define ENTRY_MAX_LENGTH    1000
55#define HISTORY_MAX_LENGTH  20000
56
57enum SortOrder {
58    SORT_NONE,
59    SORT_NAME,
60    SORT_CONTAINER,
61    SORT_OCCUR,
62    SORT_TYPE,
63    SORT_CONTENT,
64
65    SORT_COUNT
66};
67
68static const char *sort_order_name[SORT_COUNT] = {
69    "Unsorted",
70    "Name",
71    "Name (Container)",
72    "Name (Occur)",
73    "Type",
74    "Content",
75};
76
77// used to sort entries in list
78struct list_entry {
79    const char *key_name;
80    GB_TYPES    type;
81    int         childCount;     // -1 if only one child with key_name exists
82    GBDATA     *gbd;
83    string      content;
84
85    static SortOrder sort_order;
86
87    inline bool less_than_by_name(const list_entry& other) const {
88        int cmp = ARB_stricmp(key_name, other.key_name);
89        if (cmp != 0) return (cmp<0); // name differs!
90        return childCount<other.childCount; // equal names -> compare child count
91    }
92
93    inline int cmp_by_container(const list_entry& other) const { return int(type != GB_DB) - int(other.type != GB_DB); }
94    inline int cmp_by_childcount(const list_entry& other) const { return childCount - other.childCount; }
95
96    inline bool less_than_by_container_name(const list_entry& other) const {
97        int cmp = cmp_by_container(other);
98        if (cmp == 0) return less_than_by_name(other);
99        return cmp<0;
100    }
101    inline bool less_than_by_childcount_name(const list_entry& other) const {
102        int cmp = cmp_by_childcount(other);
103        if (cmp == 0) return less_than_by_name(other);
104        return cmp<0;
105    }
106
107    bool operator<(const list_entry& other) const {
108        bool is_less = false;
109        switch (sort_order) {
110            case SORT_COUNT: break;
111            case SORT_NONE:
112                awt_assert(0);  // not possible
113                break;
114
115            case SORT_NAME:
116                is_less = less_than_by_name(other);
117                break;
118
119            case SORT_CONTAINER:
120                is_less = less_than_by_container_name(other);
121                break;
122
123            case SORT_OCCUR:
124                is_less = less_than_by_childcount_name(other);
125                break;
126
127            case SORT_CONTENT: {
128                int cmp = ARB_stricmp(content.c_str(), other.content.c_str());
129
130                if (cmp != 0) is_less = cmp<0;
131                else is_less          = less_than_by_container_name(other);
132
133                break;
134            }
135            case SORT_TYPE: {
136                int cmp = type-other.type;
137
138                if (cmp == 0) is_less = less_than_by_name(other);
139                else is_less          = cmp<0;
140
141                break;
142            }
143        }
144        return is_less;
145    }
146};
147
148SortOrder list_entry::sort_order = SORT_NONE;
149
150// ---------------------
151//      create AWARs
152
153static MemDump make_userdefined_MemDump(AW_root *awr) {
154    bool   hex      = awr->awar(AWAR_DUMP_HEX)->read_int();
155    bool   ascii    = awr->awar(AWAR_DUMP_ASCII)->read_int();
156    bool   space    = awr->awar(AWAR_DUMP_SPACE)->read_int();
157    size_t width    = awr->awar(AWAR_DUMP_WIDTH)->read_int();
158    size_t separate = awr->awar(AWAR_DUMP_BLOCK)->read_int();
159   
160    bool offset = (hex||ascii) && width;
161
162    return MemDump(offset, hex, ascii, width, separate, space);
163}
164
165static void hexmode_changed_cb(AW_root *aw_root) {
166    aw_root->awar(AWAR_DBB_BROWSE)->touch();
167}
168
169void AWT_create_db_browser_awars(AW_root *aw_root, AW_default aw_def) {
170    aw_root->awar_int   (AWAR_DBB_DB,      0,                     aw_def); // index to internal order of announced databases
171    aw_root->awar_int   (AWAR_DBB_ORDER,   SORT_CONTAINER,        aw_def); // sort order for "browse"-box
172    aw_root->awar_string(AWAR_DBB_PATH,    "/",                   aw_def); // path in database
173    aw_root->awar_string(AWAR_DBB_BROWSE,  "",                    aw_def); // selection in browser (= child name)
174    aw_root->awar_string(AWAR_DBB_INFO,    "<select an element>", aw_def); // information about selected item
175    aw_root->awar_string(AWAR_DBB_HISTORY, "",                    aw_def); // '\n'-separated string containing visited nodes
176
177    // hex-dump-options
178    aw_root->awar_int(AWAR_DUMP_HEX,   1,  aw_def)->add_callback(hexmode_changed_cb); // show hex ?
179    aw_root->awar_int(AWAR_DUMP_ASCII, 1,  aw_def)->add_callback(hexmode_changed_cb); // show ascii ?
180    aw_root->awar_int(AWAR_DUMP_SPACE, 1,  aw_def)->add_callback(hexmode_changed_cb); // space bytes
181    aw_root->awar_int(AWAR_DUMP_WIDTH, 16, aw_def)->add_callback(hexmode_changed_cb); // bytes/line
182    aw_root->awar_int(AWAR_DUMP_BLOCK, 8,  aw_def)->add_callback(hexmode_changed_cb); // separate each bytes
183}
184
185static GBDATA *GB_search_numbered(GBDATA *gbd, const char *str, GB_TYPES create) { // @@@  this may be moved to ARBDB-sources
186    if (str) {
187        if (str[0] == '/' && str[1] == 0) { // root
188            return GB_get_root(gbd);
189        }
190
191        const char *first_bracket = strchr(str, '[');
192        if (first_bracket) {
193            const char *second_bracket = strchr(first_bracket+1, ']');
194            if (second_bracket && (second_bracket[1] == 0 || second_bracket[1] == '/')) {
195                int count = atoi(first_bracket+1);
196                if (count >= 0 && isdigit(first_bracket[1])) {
197                    // we are sure we have sth with number in brackets (e.g. "/species_data/species[42]/name")
198                    const char *previous_slash = first_bracket-1;
199                    while (previous_slash >= str && previous_slash[0] != '/') previous_slash--; //
200                    if (previous_slash<str) previous_slash = 0;
201
202                    GBDATA *gb_parent = 0;
203                    {
204                        if (previous_slash) { // found a slash
205                            char *parent_path = GB_strpartdup(str, previous_slash-1);
206
207                            // we are sure parent path does not contain brackets -> search normal
208                            if (parent_path[0] == 0) { // that means : root-item is numbered (e.g. '/species_data[7]/...')
209                                gb_parent = GB_get_root(gbd);
210                            }
211                            else {
212                                gb_parent = GB_search(gbd, parent_path, GB_FIND);
213                            }
214
215                            if (!gb_parent) fprintf(stderr, "Warning: parent '%s' not found\n", parent_path);
216                            free(parent_path);
217                        }
218                        else {
219                            gb_parent = gbd;
220                        }
221                    }
222
223                    if (gb_parent) {
224                        GBDATA *gb_son = 0;
225                        {
226                            const char *name_start = previous_slash ? previous_slash+1 : str;
227                            char       *key_name   = GB_strpartdup(name_start, first_bracket-1);
228                            int         c          = 0;
229
230                            gb_son = GB_entry(gb_parent, key_name);
231                            while (c<count && gb_son) {
232                                gb_son = GB_nextEntry(gb_son);
233                                if (gb_son) ++c;
234                            }
235
236                            if (!gb_son) fprintf(stderr, "Warning: did not find %i. son '%s'\n", count, key_name);
237                            free(key_name);
238                        }
239
240                        if (gb_son) {
241                            const char *rest = 0;
242                            if (second_bracket[1] == '/') { // continue search ?
243                                if (second_bracket[2]) {
244                                    rest = second_bracket+2;
245                                }
246                            }
247
248                            return rest
249                                ? GB_search_numbered(gb_son, rest, create) // recursive search
250                                : gb_son; // numbering occurred at end of search path
251                        }
252                    }
253                    else {
254                        fprintf(stderr, "Warning: don't know where to start numbered search in '%s'\n", str);
255                    }
256
257                    return 0; // not found
258                }
259                else {
260                    fprintf(stderr, "Warning: Illegal content in search path - expected digits at '%s'\n", first_bracket+1);
261                }
262            }
263            else {
264                fprintf(stderr, "Warning: Unbalanced or illegal [] in search path (%s)\n", str);
265            }
266        }
267        // no brackets -> normal search
268    }
269    return GB_search(gbd, str, create); // do normal search
270}
271
272// -----------------------
273//      class KnownDB
274
275class KnownDB {
276    GBDATA& gb_main; 
277    string  description;
278    string  current_path;
279
280public:
281    KnownDB(GBDATA *gb_main_, const char *description_)
282        : gb_main(*gb_main_)
283        , description(description_)
284        , current_path("/")
285    {}
286    KnownDB(const KnownDB& other)
287        : gb_main(other.gb_main),
288          description(other.description),
289          current_path(other.current_path)
290    {}
291    DECLARE_ASSIGNMENT_OPERATOR(KnownDB);
292
293    const GBDATA *get_db() const { return &gb_main; }
294    const string& get_description() const { return description; }
295
296    const string& get_path() const { return current_path; }
297    void set_path(const string& path) { current_path = path; }
298    void set_path(const char* path) { current_path = path; }
299};
300
301class hasDB {
302    GBDATA *db;
303public:
304    hasDB(GBDATA *gbm) : db(gbm) {}
305    bool operator()(const KnownDB& kdb) { return kdb.get_db() == db; }
306};
307
308// --------------------------
309//      class DB_browser
310
311class DB_browser;
312static DB_browser *get_the_browser(bool autocreate);
313
314class DB_browser {
315    typedef vector<KnownDB>::iterator KnownDBiterator;
316
317    vector<KnownDB> known_databases;
318    size_t          current_db; // index of current db (in known_databases)
319
320    AW_window             *aww;                     // browser window
321    AW_option_menu_struct *oms;                     // the DB selector
322    AW_selection_list     *browse_list;             // the browse subwindow
323
324    static SmartPtr<DB_browser> the_browser;
325    friend DB_browser *get_the_browser(bool autocreate);
326
327    void update_DB_selector();
328
329    DB_browser(const DB_browser& other);            // copying not allowed
330    DB_browser& operator = (const DB_browser& other); // assignment not allowed
331public:
332    DB_browser() : current_db(0), aww(0), oms(0) {}
333
334    void add_db(GBDATA *gb_main, const char *description) {
335        known_databases.push_back(KnownDB(gb_main, description));
336        if (aww) update_DB_selector();
337    }
338
339    void del_db(GBDATA *gb_main) {
340        KnownDBiterator known = find_if(known_databases.begin(), known_databases.end(), hasDB(gb_main));
341        if (known != known_databases.end()) known_databases.erase(known);
342#if defined(DEBUG)
343        else awt_assert(0); // no need to delete unknown databases
344#endif // DEBUG
345    }
346
347    AW_window *get_window(AW_root *aw_root);
348    AW_selection_list *get_browser_list() { return browse_list; }
349
350    size_t get_selected_db() const { return current_db; }
351    void set_selected_db(size_t idx) {
352        awt_assert(idx < known_databases.size());
353        current_db = idx;
354    }
355
356    const char *get_path() const { return known_databases[current_db].get_path().c_str(); }
357    void set_path(const char *new_path) { known_databases[current_db].set_path(new_path); }
358
359    GBDATA *get_db() const { return const_cast<GBDATA*>(known_databases[current_db].get_db()); }
360};
361
362
363// -----------------------------
364//      DB_browser singleton
365
366SmartPtr<DB_browser> DB_browser::the_browser;
367
368static DB_browser *get_the_browser(bool autocreate = true) {
369    if (DB_browser::the_browser.isNull() && autocreate) {
370        DB_browser::the_browser = new DB_browser;
371    }
372    return &*DB_browser::the_browser;
373}
374
375// --------------------------
376//      announce databases
377
378void AWT_announce_db_to_browser(GBDATA *gb_main, const char *description) {
379    get_the_browser()->add_db(gb_main, description);
380}
381
382static void AWT_announce_properties_to_browser(GBDATA *gb_defaults, const char *defaults_name) {
383    AWT_announce_db_to_browser(gb_defaults, GBS_global_string("Properties (%s)", defaults_name));
384}
385
386void AWT_browser_forget_db(GBDATA *gb_main) {
387    DB_browser *browser = get_the_browser(false);
388    awt_assert(browser);
389    if (browser) browser->del_db(gb_main);
390}
391
392// ---------------------------------------
393//      browser window callbacks
394
395static void toggle_tmp_cb(AW_window *aww) {
396    AW_awar *awar_path = aww->get_root()->awar(AWAR_DBB_PATH);
397    char    *path      = awar_path->read_string();
398    bool     done      = false;
399
400    if (ARB_strscmp(path, "/tmp") == 0) {
401        if (path[4] == '/') {
402            awar_path->write_string(path+4);
403            done = true;
404        }
405        else if (path[4] == 0) {
406            awar_path->write_string("/");
407            done = true;
408        }
409    }
410
411    if (!done) {
412        awar_path->write_string(GBS_global_string("/tmp%s", path));
413    }
414    free(path);
415}
416
417static void show_history_cb(AW_window *aww) {
418    aww->get_root()->awar(AWAR_DBB_PATH)->write_string(HISTORY_PSEUDO_PATH);
419}
420
421static void goto_root_cb(AW_window *aww) {
422    AW_awar *awar_path = aww->get_root()->awar(AWAR_DBB_PATH);
423    awar_path->write_string("/");
424}
425static void go_up_cb(AW_window *aww) {
426    AW_awar *awar_path = aww->get_root()->awar(AWAR_DBB_PATH);
427    char    *path      = awar_path->read_string();
428    char    *lslash    = strrchr(path, '/');
429
430    if (lslash) {
431        lslash[0] = 0;
432        if (!path[0]) strcpy(path, "/");
433        awar_path->write_string(path);
434    }
435}
436
437// --------------------------
438//      browser commands:
439
440#define BROWSE_CMD_PREFIX          "browse_cmd___"
441#define BROWSE_CMD_GOTO_VALID_NODE BROWSE_CMD_PREFIX "goto_valid_node"
442#define BROWSE_CMD_GO_UP           BROWSE_CMD_PREFIX "go_up"
443
444struct BrowserCommand {
445    const char *name;
446    GB_ERROR (*function)(AW_window *);
447};
448
449
450static GB_ERROR browse_cmd_go_up(AW_window *aww) {
451    go_up_cb(aww);
452    return 0;
453}
454static GB_ERROR browse_cmd_goto_valid_node(AW_window *aww) {
455    AW_root *aw_root   = aww->get_root();
456    AW_awar *awar_path = aw_root->awar(AWAR_DBB_PATH);
457    char    *path      = awar_path->read_string();
458    int      len       = strlen(path);
459    GBDATA  *gb_main   = get_the_browser()->get_db();
460
461    {
462        GB_transaction t(gb_main);
463        while (len>0 && GB_search_numbered(gb_main, path, GB_FIND) == 0) {
464            path[--len] = 0;
465        }
466    }
467
468    awar_path->write_string(len ? path : "/");
469    aw_root->awar(AWAR_DBB_BROWSE)->write_string("");
470    return 0;
471}
472
473static BrowserCommand browser_command_table[] = {
474    { BROWSE_CMD_GOTO_VALID_NODE, browse_cmd_goto_valid_node },
475    { BROWSE_CMD_GO_UP, browse_cmd_go_up },
476    { 0, 0 }
477};
478
479static void execute_browser_command(AW_window *aww, const char *command) {
480    int idx;
481    for (idx = 0; browser_command_table[idx].name; ++idx) {
482        if (strcmp(command, browser_command_table[idx].name) == 0) {
483            GB_ERROR error = browser_command_table[idx].function(aww);
484            if (error) aw_message(error);
485            break;
486        }
487    }
488
489    if (!browser_command_table[idx].name) {
490        aw_message(GBS_global_string("Unknown browser command '%s'", command));
491    }
492}
493
494// ----------------------------
495//      the browser window
496
497static AW_window *create_db_browser(AW_root *aw_root) {
498    return get_the_browser()->get_window(aw_root);
499}
500
501static void child_changed_cb(AW_root *aw_root); // prototype
502static void browsed_node_changed_cb(GBDATA *, int *cl_aw_root, GB_CB_TYPE) {
503    child_changed_cb((AW_root*)cl_aw_root);
504}
505
506static void set_callback_node(GBDATA *node, AW_root *aw_root) {
507    static GBDATA *active_node = 0;
508
509    if (active_node != node) {
510        if (active_node) GB_remove_callback(active_node, GB_CB_CHANGED, browsed_node_changed_cb, (int*)aw_root);
511        if (node) GB_ensure_callback(node, GB_CB_CHANGED, browsed_node_changed_cb, (int*)aw_root);
512        active_node = node;
513    }
514}
515
516struct counterPair {
517    int occur, count;
518    counterPair() : occur(0), count(0) {}
519};
520
521inline void insert_history_selection(AW_selection_list *sel, const char *entry, int wanted_db) {
522    const char *colon = strchr(entry, ':');
523    if (colon && (atoi(entry) == wanted_db)) {
524        sel->insert(colon+1, colon+1);
525    }
526}
527
528
529static char *get_container_description(GBDATA *gbd) {
530    char *content = NULL;
531    const char *known_children[] = { "@name", "name", "key_name", "alignment_name", "group_name", "key_text", 0 };
532    for (int i = 0; known_children[i]; ++i) {
533        GBDATA *gb_known = GB_entry(gbd, known_children[i]);
534        if (gb_known && GB_read_type(gb_known) != GB_DB && GB_nextEntry(gb_known) == 0) { // exactly one child exits
535            char *asStr = GB_read_as_string(gb_known);
536            content     = GBS_global_string_copy("[%s=%s]", known_children[i], asStr);
537            free(asStr);
538            break;
539        }
540    }
541
542    return content;
543}
544
545static char *get_dbentry_content(GBDATA *gbd, GB_TYPES type, bool shorten_repeats, const MemDump& dump) {
546    awt_assert(type != GB_DB);
547   
548    char *content = NULL;
549    if (!dump.wrapped()) content = GB_read_as_string(gbd);
550    if (!content) { // use dumper
551        long      size = GB_read_count(gbd);
552        const int plen = 30;
553
554        GBS_strstruct buf(dump.mem_needed_for_dump(size)+plen);
555
556        if (!dump.wrapped()) buf.nprintf(plen, "<%li bytes>: ", size);
557        dump.dump_to(buf, GB_read_pntr(gbd), size);
558
559        content         = buf.release();
560        shorten_repeats = false;
561    }
562    size_t len = shorten_repeats ? GBS_shorten_repeated_data(content) : strlen(content);
563    if (!dump.wrapped() && len>(ENTRY_MAX_LENGTH+15)) {
564        content[ENTRY_MAX_LENGTH] = 0;
565        freeset(content, GBS_global_string_copy("%s [rest skipped]", content));
566    }
567    return content;
568}
569
570static void update_browser_selection_list(AW_root *aw_root, AW_selection_list *id) {
571    DB_browser *browser = get_the_browser();
572    char       *path    = aw_root->awar(AWAR_DBB_PATH)->read_string();
573    bool        is_root;
574    GBDATA     *node;
575
576    {
577        GBDATA         *gb_main = browser->get_db();
578        GB_transaction  ta(gb_main);
579
580        is_root = (strcmp(path, "/") == 0);
581        node    = GB_search_numbered(gb_main, path, GB_FIND);
582        set_callback_node(node, aw_root);
583    }
584
585    id->clear();
586
587    if (node == 0) {
588        if (strcmp(path, HISTORY_PSEUDO_PATH) == 0) {
589            char *history = aw_root->awar(AWAR_DBB_HISTORY)->read_string();
590            id->insert("Previously visited nodes:", "");
591            char *start   = history;
592            int   curr_db = aw_root->awar(AWAR_DBB_DB)->read_int();
593
594            for (char *lf = strchr(start, '\n'); lf; start = lf+1, lf = strchr(start, '\n')) {
595                lf[0] = 0;
596                insert_history_selection(id, start, curr_db);
597            }
598            insert_history_selection(id, start, curr_db);
599            free(history);
600        }
601        else {
602            id->insert("No such node!", "");
603            id->insert("-> goto valid node", BROWSE_CMD_GOTO_VALID_NODE);
604        }
605    }
606    else {
607        map<string, counterPair> child_count;
608        GB_transaction           ta(browser->get_db());
609
610        for (GBDATA *child = GB_child(node); child; child = GB_nextChild(child)) {
611            const char *key_name   = GB_read_key_pntr(child);
612            child_count[key_name].occur++;
613        }
614
615        int maxkeylen = 0;
616        int maxtypelen = 0;
617        for (map<string, counterPair>::iterator i = child_count.begin(); i != child_count.end(); ++i) {
618            {
619                int keylen   = i->first.length();
620                int maxcount = i->second.occur;
621
622                if (maxcount != 1) {
623                    keylen += 2;    // brackets
624                    while (maxcount) { maxcount /= 10; keylen++; } // increase keylen for each digit
625                }
626
627                if (keylen>maxkeylen) maxkeylen = keylen;
628            }
629
630            {
631                GBDATA     *child     = GB_entry(node, i->first.c_str()); // find first child
632                const char *type_name = GB_get_type_name(child);
633                int         typelen   = strlen(type_name);
634
635                if (typelen>maxtypelen) maxtypelen = typelen;
636            }
637        }
638
639        if (!is_root) {
640            id->insert(GBS_global_string("%-*s   parent container", maxkeylen, ".."), BROWSE_CMD_GO_UP);
641            id->insert(GBS_global_string("%-*s   container", maxkeylen, "."), "");
642        }
643        else {
644            id->insert(GBS_global_string("%-*s   root container", maxkeylen, "/"), "");
645        }
646       
647        // collect children and sort them
648
649        vector<list_entry> sorted_children;
650
651        MemDump simpleDump(false, true, false);
652        for (GBDATA *child = GB_child(node); child; child = GB_nextChild(child)) {
653            list_entry entry;
654            entry.key_name   = GB_read_key_pntr(child);
655            entry.childCount = -1;
656            entry.type       = GB_read_type(child);
657            entry.gbd        = child;
658
659            int occurrences = child_count[entry.key_name].occur;
660            if (occurrences != 1) {
661                entry.childCount = child_count[entry.key_name].count;
662                child_count[entry.key_name].count++;
663            }
664            char *display = NULL;
665            if (entry.type == GB_DB) {
666                display = get_container_description(entry.gbd);
667            }
668            else {
669                display = get_dbentry_content(entry.gbd, entry.type, true, simpleDump);
670            }
671            if (display) entry.content = display;
672            sorted_children.push_back(entry);
673        }
674
675        list_entry::sort_order = (SortOrder)aw_root->awar(AWAR_DBB_ORDER)->read_int();
676        if (list_entry::sort_order != SORT_NONE) {
677            sort(sorted_children.begin(), sorted_children.end());
678        }
679
680        for (vector<list_entry>::iterator ch = sorted_children.begin(); ch != sorted_children.end(); ++ch) {
681            const list_entry&  entry            = *ch;
682            const char        *key_name         = entry.key_name;
683            char              *numbered_keyname = 0;
684
685            if (entry.childCount >= 0) {
686                numbered_keyname = GBS_global_string_copy("%s[%i]", key_name, entry.childCount);
687            }
688
689            key_name = numbered_keyname ? numbered_keyname : key_name;
690            const char *displayed = GBS_global_string("%-*s   %-*s   %s", maxkeylen, key_name, maxtypelen, GB_get_type_name(entry.gbd), entry.content.c_str());
691            id->insert(displayed, key_name);
692
693            free(numbered_keyname);
694        }
695    }
696    id->insert_default("", "");
697    id->update();
698
699    free(path);
700}
701
702static void order_changed_cb(AW_root *aw_root) {
703    DB_browser *browser = get_the_browser();
704    update_browser_selection_list(aw_root, browser->get_browser_list());
705}
706
707inline char *strmove(char *dest, char *source) {
708    return (char*)memmove(dest, source, strlen(source)+1);
709}
710
711static void add_to_history(AW_root *aw_root, const char *path) {
712    // adds 'path' to history
713
714    if (strcmp(path, HISTORY_PSEUDO_PATH) != 0) {
715        int      db           = aw_root->awar(AWAR_DBB_DB)->read_int();
716        AW_awar *awar_history = aw_root->awar(AWAR_DBB_HISTORY);
717        char    *old_history  = awar_history->read_string();
718        char    *entry        = GBS_global_string_copy("%i:%s", db, path);
719        int      entry_len    = strlen(entry);
720
721        char *found = strstr(old_history, entry);
722        while (found) {
723            if (found == old_history || found[-1] == '\n') { // found start of an entry
724                if (found[entry_len] == '\n') {
725                    strmove(found, found+entry_len+1);
726                    found = strstr(old_history, entry);
727                }
728                else if (found[entry_len] == 0) { // found as last entry
729                    if (found == old_history) { // which is also first entry
730                        old_history[0] = 0; // empty history
731                    }
732                    else {
733                        strmove(found, found+entry_len+1);
734                    }
735                    found = strstr(old_history, entry);
736                }
737                else {
738                    found = strstr(found+1, entry);
739                }
740            }
741            else {
742                found = strstr(found+1, entry);
743            }
744        }
745
746        if (old_history[0]) {
747            char *new_history = GBS_global_string_copy("%s\n%s", entry, old_history);
748
749            while (strlen(old_history)>HISTORY_MAX_LENGTH) { // shorten history
750                char *llf = strrchr(old_history, '\n');
751                if (!llf) break;
752                llf[0]    = 0;
753            }
754
755            awar_history->write_string(new_history);
756            free(new_history);
757        }
758        else {
759            awar_history->write_string(entry);
760        }
761
762        free(entry);
763        free(old_history);
764    }
765}
766
767static void child_changed_cb(AW_root *aw_root) {
768    char *child = aw_root->awar(AWAR_DBB_BROWSE)->read_string();
769    if (strncmp(child, BROWSE_CMD_PREFIX, sizeof(BROWSE_CMD_PREFIX)-1) == 0) { // a symbolic browser command
770        execute_browser_command(get_the_browser()->get_window(aw_root), child);
771    }
772    else {
773        char *path = aw_root->awar(AWAR_DBB_PATH)->read_string();
774
775        if (strcmp(path, HISTORY_PSEUDO_PATH) == 0) {
776            if (child[0]) aw_root->awar(AWAR_DBB_PATH)->write_string(child);
777        }
778        else {
779            static bool avoid_recursion = false;
780            if (!avoid_recursion) {
781                LocallyModify<bool> flag(avoid_recursion, true);
782
783                char *fullpath;
784                if (strcmp(path, "/") == 0) {
785                    fullpath = GBS_global_string_copy("/%s", child);
786                }
787                else if (child[0] == 0) {
788                    fullpath = strdup(path);
789                }
790                else {
791                    fullpath = GBS_global_string_copy("%s/%s", path, child);
792                }
793
794                DB_browser     *browser          = get_the_browser();
795                GBDATA         *gb_main          = browser->get_db();
796                GB_transaction  dummy(gb_main);
797                GBDATA         *gb_selected_node = GB_search_numbered(gb_main, fullpath, GB_FIND);
798
799                string info;
800                info += GBS_global_string("fullpath='%s'\n", fullpath);
801
802                if (gb_selected_node == 0) {
803                    info += "Node does not exist.\n";
804                }
805                else {
806                    add_to_history(aw_root, fullpath);
807               
808                    info += GBS_global_string("Node exists [address=%p]\n", gb_selected_node);
809                    info += GBS_global_string("Key index | %i\n", GB_get_quark(gb_selected_node));
810
811                    GB_TYPES type = GB_read_type(gb_selected_node);
812                    if (type == GB_DB) {
813                        info += "Node type | container\n";
814                        info += GBS_global_string("Entries   | %li\n", GB_number_of_subentries(gb_selected_node));
815
816                        aw_root->awar(AWAR_DBB_BROWSE)->write_string("");
817                        aw_root->awar(AWAR_DBB_PATH)->write_string(fullpath);
818                    }
819                    else {
820                        info += GBS_global_string("Node type | data [type=%s]\n", GB_get_type_name(gb_selected_node));
821                        info += GBS_global_string("Data size | %li\n", GB_read_count(gb_selected_node));
822                        info += GBS_global_string("Memory    | %li\n", GB_read_memuse(gb_selected_node));
823                    }
824
825                    info += GBS_global_string("Security  | read=%i write=%i delete=%i\n",
826                                              GB_read_security_read(gb_selected_node),
827                                              GB_read_security_write(gb_selected_node),
828                                              GB_read_security_delete(gb_selected_node));
829
830                    char *callback_info = GB_get_callback_info(gb_selected_node);
831                    if (callback_info) {
832                        info = info+"Callbacks |\n"+callback_info+'\n';
833                        free(callback_info);
834                    }
835
836                    if (type != GB_DB) {
837                        MemDump  dump    = make_userdefined_MemDump(aw_root);
838                        char    *content = get_dbentry_content(gb_selected_node, GB_read_type(gb_selected_node), false, dump);
839                        info             = info+"Content |\n"+content+'\n';
840                        free(content);
841                    }
842                }
843
844                aw_root->awar(AWAR_DBB_INFO)->write_string(info.c_str());
845
846                free(fullpath);
847            }
848        }
849
850        free(path);
851    }
852    free(child);
853}
854
855static void path_changed_cb(AW_root *aw_root) {
856    static bool avoid_recursion = false;
857    if (!avoid_recursion) {
858        LocallyModify<bool> flag(avoid_recursion, true);
859
860        DB_browser *browser    = get_the_browser();
861        char       *goto_child = 0;
862
863        {
864            GBDATA         *gb_main   = browser->get_db();
865            GB_transaction  t(gb_main);
866            AW_awar        *awar_path = aw_root->awar(AWAR_DBB_PATH);
867            char           *path      = awar_path->read_string();
868            GBDATA         *found     = GB_search_numbered(gb_main, path, GB_FIND);
869
870            if (found && GB_read_type(found) != GB_DB) { // exists, but is not a container
871                char *lslash = strrchr(path, '/');
872                if (lslash) {
873                    lslash[0]  = 0;
874                    awar_path->write_string(path);
875                    goto_child = strdup(lslash+1);
876                }
877            }
878
879            if (found) {
880                add_to_history(aw_root, goto_child ? GBS_global_string("%s/%s", path, goto_child) : path);
881            }
882            browser->set_path(path);
883            free(path);
884        }
885
886        update_browser_selection_list(aw_root, browser->get_browser_list());
887        aw_root->awar(AWAR_DBB_BROWSE)->write_string(goto_child ? goto_child : "");
888    }
889}
890static void db_changed_cb(AW_root *aw_root) {
891    int         selected = aw_root->awar(AWAR_DBB_DB)->read_int();
892    DB_browser *browser  = get_the_browser();
893    browser->set_selected_db(selected);
894    aw_root->awar(AWAR_DBB_PATH)->rewrite_string(browser->get_path());
895}
896
897void DB_browser::update_DB_selector() {
898    if (!oms) oms = aww->create_option_menu(AWAR_DBB_DB);
899    else aww->clear_option_menu(oms);
900
901    int idx = 0;
902    for (KnownDBiterator i = known_databases.begin(); i != known_databases.end(); ++i, ++idx) {
903        const KnownDB& db = *i;
904        aww->insert_option(db.get_description().c_str(), "", idx);
905    }
906    aww->update_option_menu();
907}
908
909AW_window *DB_browser::get_window(AW_root *aw_root) {
910    awt_assert(!known_databases.empty()); // have no db to browse
911
912    if (!aww) {
913        // select current db+path
914        {
915            int wanted_db = aw_root->awar(AWAR_DBB_DB)->read_int();
916            int known     = known_databases.size();
917            if (wanted_db >= known) {
918                wanted_db = 0;
919                aw_root->awar(AWAR_DBB_DB)->write_int(wanted_db);
920                aw_root->awar(AWAR_DBB_PATH)->write_string("/"); // reset
921            }
922            current_db = wanted_db;
923
924            char *wanted_path = aw_root->awar(AWAR_DBB_PATH)->read_string();
925            known_databases[wanted_db].set_path(wanted_path);
926            free(wanted_path);
927        }
928
929        AW_window_simple *aws = new AW_window_simple;
930        aww                   = aws;
931        aws->init(aw_root, "DB_BROWSER", "ARB database browser");
932        aws->load_xfig("dbbrowser.fig");
933
934        aws->at("close"); aws->callback((AW_CB0)AW_POPDOWN);
935        aws->create_button("CLOSE", "CLOSE", "C");
936
937        aws->callback(AW_POPUP_HELP, (AW_CL)"db_browser.hlp");
938        aws->at("help");
939        aws->create_button("HELP", "HELP", "H");
940
941        aws->at("db");
942        update_DB_selector();
943
944        aws->at("order");
945        aws->create_option_menu(AWAR_DBB_ORDER);
946        for (int idx = 0; idx<SORT_COUNT; ++idx) {
947            aws->insert_option(sort_order_name[idx], "", idx);
948        }
949        aws->update_option_menu();
950
951        aws->at("path");
952        aws->create_input_field(AWAR_DBB_PATH, 10);
953
954        aws->auto_space(10, 10);
955        aws->button_length(8);
956
957        aws->at("navigation");
958        aws->callback(go_up_cb); aws->create_button("go_up", "Up");
959        aws->callback(goto_root_cb); aws->create_button("goto_root", "Top");
960        aws->callback(show_history_cb); aws->create_button("history", "History");
961        aws->callback(toggle_tmp_cb); aws->create_button("toggle_tmp", "/tmp");
962
963        aws->at("browse");
964        browse_list = aws->create_selection_list(AWAR_DBB_BROWSE);
965        update_browser_selection_list(aw_root, browse_list);
966
967        aws->at("infoopt");
968        aws->label("ASCII"); aws->create_toggle     (AWAR_DUMP_ASCII);
969        aws->label("Hex");   aws->create_toggle     (AWAR_DUMP_HEX);
970        aws->label("Sep?");  aws->create_toggle     (AWAR_DUMP_SPACE);
971        aws->label("Width"); aws->create_input_field(AWAR_DUMP_WIDTH, 3);
972        aws->label("Block"); aws->create_input_field(AWAR_DUMP_BLOCK, 3);
973
974        aws->at("info");
975        aws->create_text_field(AWAR_DBB_INFO, 40, 40);
976
977        // add callbacks
978        aw_root->awar(AWAR_DBB_BROWSE)->add_callback(child_changed_cb);
979        aw_root->awar(AWAR_DBB_PATH)->add_callback(path_changed_cb);
980        aw_root->awar(AWAR_DBB_DB)->add_callback(db_changed_cb);
981        aw_root->awar(AWAR_DBB_ORDER)->add_callback(order_changed_cb);
982
983        db_changed_cb(aw_root); // force update
984    }
985    return aww;
986}
987
988static void callallcallbacks(AW_window *aww, AW_CL mode, AW_CL) {
989    static bool running = false; // avoid deadlock
990    if (!running) {
991        LocallyModify<bool> flag(running, true);
992        aww->get_root()->callallcallbacks(mode);
993    }
994}
995
996
997
998void AWT_create_debug_menu(AW_window *awmm) {
999    awmm->create_menu("4debug", "4", AWM_ALL);
1000
1001    awmm->insert_menu_topic("-db_browser", "Browse loaded database(s)", "B", "db_browser.hlp", AWM_ALL, AW_POPUP, (AW_CL)create_db_browser, 0);
1002
1003    awmm->sep______________();
1004    {
1005        awmm->insert_sub_menu("Callbacks (dangerous! use at your own risk)", "C", AWM_ALL);
1006        awmm->insert_menu_topic("!run_all_cbs_alph",  "Call all callbacks (alpha-order)",     "a", "", AWM_ALL, callallcallbacks, 0, 0);
1007        awmm->insert_menu_topic("!run_all_cbs_nalph", "Call all callbacks (alpha-reverse)",   "l", "", AWM_ALL, callallcallbacks, 1, 0);
1008        awmm->insert_menu_topic("!run_all_cbs_loc",   "Call all callbacks (code-order)",      "c", "", AWM_ALL, callallcallbacks, 2, 0);
1009        awmm->insert_menu_topic("!run_all_cbs_nloc",  "Call all callbacks (code-reverse)",    "o", "", AWM_ALL, callallcallbacks, 3, 0);
1010        awmm->insert_menu_topic("!run_all_cbs_rnd",   "Call all callbacks (random)",          "r", "", AWM_ALL, callallcallbacks, 4, 0);
1011        awmm->sep______________();
1012        awmm->insert_menu_topic("!forget_called_cbs", "Forget called",     "F", "", AWM_ALL, callallcallbacks, -1, 0);
1013        awmm->insert_menu_topic("!mark_all_called",   "Mark all called",   "M", "", AWM_ALL, callallcallbacks, -2, 0);
1014        awmm->sep______________();
1015        awmm->insert_menu_topic("!run_all_cbs_alph_inf",  "Call all callbacks (alpha-order repeated)",     "", "", AWM_ALL, callallcallbacks, 8|0, 0);
1016        awmm->insert_menu_topic("!run_all_cbs_nalph_inf", "Call all callbacks (alpha-reverse repeated)",   "", "", AWM_ALL, callallcallbacks, 8|1, 0);
1017        awmm->insert_menu_topic("!run_all_cbs_loc_inf",   "Call all callbacks (code-order repeated)",      "", "", AWM_ALL, callallcallbacks, 8|2, 0);
1018        awmm->insert_menu_topic("!run_all_cbs_nloc_inf",  "Call all callbacks (code-reverse repeated)",    "", "", AWM_ALL, callallcallbacks, 8|3, 0);
1019        awmm->insert_menu_topic("!run_all_cbs_rnd_inf",   "Call all callbacks (random repeated)",          "", "", AWM_ALL, callallcallbacks, 8|4, 0);
1020        awmm->close_sub_menu();
1021    }
1022    awmm->sep______________();
1023
1024}
1025
1026#endif // DEBUG
1027
1028AW_root *AWT_create_root(const char *properties, const char *program) {
1029    AW_root *aw_root = new AW_root(properties, program, false);
1030#if defined(DEBUG)
1031    AWT_announce_properties_to_browser(AW_ROOT_DEFAULT, properties);
1032#endif // DEBUG
1033    return aw_root;
1034}
1035
1036// ------------------------
1037//      callback guards
1038
1039static void before_callback_guard() {
1040    if (GB_have_error()) {
1041        GB_ERROR error = GB_await_error();
1042        aw_message(GBS_global_string("Error not clear before calling callback\n"
1043                                     "Unhandled error was:\n"
1044                                     "%s", error));
1045#if defined(DEVEL_RALF)
1046        awt_assert(0);
1047#endif // DEVEL_RALF
1048    }
1049}
1050static void after_callback_guard() {
1051    if (GB_have_error()) {
1052        GB_ERROR error = GB_await_error();
1053        aw_message(GBS_global_string("Error not handled by callback!\n"
1054                                     "Unhandled error was:\n"
1055                                     "'%s'", error));
1056#if defined(DEVEL_RALF)
1057        awt_assert(0);
1058#endif // DEVEL_RALF
1059    }
1060}
1061
1062void AWT_install_cb_guards() {
1063    awt_assert(!GB_have_error());
1064    AW_cb_struct::set_AW_cb_guards(before_callback_guard, after_callback_guard);
1065}
1066void AWT_install_postcb_cb(AW_postcb_cb postcb_cb) {
1067    AW_cb_struct::set_AW_postcb_cb(postcb_cb);
1068}
Note: See TracBrowser for help on using the browser.