source: tags/svn.1.5.4/AWT/AWT_db_browser.cxx

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