source: tags/arb_5.3/AWT/AWT_db_browser.cxx

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