source: tags/old_import_filter/WINDOW/AW_root.cxx

Last change on this file was 10083, checked in by westram, 11 years ago

reintegrated branch 'macros2'

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 19.5 KB
Line 
1// =============================================================== //
2//                                                                 //
3//   File      : AW_root.cxx                                       //
4//   Purpose   :                                                   //
5//                                                                 //
6//   Institute of Microbiology (Technical University Munich)       //
7//   http://www.arb-home.de/                                       //
8//                                                                 //
9// =============================================================== //
10
11#include "aw_root.hxx"
12#include "aw_awar.hxx"
13#include "aw_nawar.hxx"
14#include "aw_msg.hxx"
15#include "aw_window.hxx"
16#include "aw_window_Xm.hxx"
17#include "aw_status.hxx"
18#include "aw_xkey.hxx"
19
20#include <arb_handlers.h>
21#include <arbdbt.h>
22
23#include <list>
24
25#include <X11/cursorfont.h>
26
27AW_root *AW_root::SINGLETON = NULL;
28
29void AW_system(AW_window *aww, const char *command, const char *auto_help_file) {
30    if (auto_help_file) AW_POPUP_HELP(aww, (AW_CL)auto_help_file);
31    aw_message_if(GBK_system(command));
32}
33
34
35void AW_root::process_events() {
36    XtAppProcessEvent(p_r->context, XtIMAll);
37}
38void AW_root::process_pending_events() {
39    XtInputMask pending = XtAppPending(p_r->context);
40    while (pending) {
41        XtAppProcessEvent(p_r->context, pending);
42        pending = XtAppPending(p_r->context);
43    }
44}
45
46AW_ProcessEventType AW_root::peek_key_event(AW_window * /* aww */) {
47    //! Returns type if key event follows, else 0
48
49    XEvent xevent;
50    Boolean result = XtAppPeekEvent(p_r->context, &xevent);
51
52    if (!result) return NO_EVENT;
53    if ((xevent.type != KeyPress) && (xevent.type != KeyRelease)) return NO_EVENT;
54    return (AW_ProcessEventType)xevent.type;
55}
56
57AW_default AW_root::load_properties(const char *default_name) {
58    GBDATA   *gb_default = GB_open(default_name, "rwcD");
59    GB_ERROR  error;
60
61    if (gb_default) {
62        error = GB_no_transaction(gb_default);
63        if (!error) {
64            GBDATA *gb_tmp = GB_search(gb_default, "tmp", GB_CREATE_CONTAINER);
65            error          = GB_set_temporary(gb_tmp);
66        }
67    }
68    else {
69        error = GB_await_error();
70    }
71
72    if (error) {
73        const char *shown_name      = strrchr(default_name, '/');
74        if (!shown_name) shown_name = default_name;
75
76        GBK_terminatef("Error loading properties '%s': %s", shown_name, error);
77    }
78
79    return (AW_default) gb_default;
80}
81
82static void destroy_AW_root() {
83    delete AW_root::SINGLETON;
84    AW_root::SINGLETON = NULL;
85}
86
87
88bool AW_root::is_focus_callback(AW_RCB fcb) const {
89    return focus_callback_list && focus_callback_list->contains(AW_root_callback(fcb, 0, 0));
90}
91
92AW_root::AW_root(const char *propertyFile, const char *program, bool no_exit, UserActionTracker *user_tracker, int */*argc*/, char ***/*argv*/) {
93    aw_assert(!AW_root::SINGLETON);                 // only one instance allowed
94    AW_root::SINGLETON = this;
95
96    memset((char *)this, 0, sizeof(AW_root));
97
98    prvt = new AW_root_Motif;
99
100    init_variables(load_properties(propertyFile));
101    init_root(program, no_exit);
102
103    tracker = user_tracker;
104
105    atexit(destroy_AW_root); // do not call this before opening properties DB!
106}
107
108#if defined(UNIT_TESTS)
109AW_root::AW_root(const char *propertyFile) {
110    aw_assert(!AW_root::SINGLETON);                 // only one instance allowed
111    AW_root::SINGLETON = this;
112
113    memset((char *)this, 0, sizeof(AW_root));
114    init_variables(load_properties(propertyFile));
115    atexit(destroy_AW_root); // do not call this before opening properties DB!
116}
117#endif
118
119void AW_root::setUserActionTracker(UserActionTracker *user_tracker) {
120    aw_assert(user_tracker);
121    aw_assert(tracker->is_replaceable()); // there is already another tracker (program-logic-error)
122
123    delete tracker;
124    tracker = user_tracker;
125}
126
127AW_awar *AW_root::label_is_awar(const char *label) {
128    AW_awar *awar_exists = NULL;
129    size_t   off         = strcspn(label, "/ ");
130
131    if (label[off] == '/') {                        // contains '/' and no space before first '/'
132        awar_exists = awar_no_error(label);
133    }
134    return awar_exists;
135}
136
137void AW_root::define_remote_command(AW_cb_struct *cbs) {
138    if (cbs->f == (AW_CB)AW_POPDOWN) {
139        aw_assert(!cbs->get_cd1() && !cbs->get_cd2()); // popdown takes no parameters (please pass ", 0, 0"!)
140    }
141
142    AW_cb_struct *old_cbs = (AW_cb_struct*)GBS_write_hash(prvt->action_hash, cbs->id, (long)cbs);
143    if (old_cbs) {
144        if (!old_cbs->is_equal(*cbs)) {                  // existing remote command replaced by different callback
145#if defined(DEBUG)
146            fputs(GBS_global_string("Warning: reused callback id '%s' for different callback\n", old_cbs->id), stderr);
147#if defined(DEVEL_RALF) && 1
148            aw_assert(0);
149#endif // DEVEL_RALF
150#endif // DEBUG
151        }
152        // do not free old_cbs, cause it's still reachable from first widget that defined this remote command
153    }
154}
155
156AW_cb_struct *AW_root::search_remote_command(const char *action) {
157    return (AW_cb_struct *)GBS_read_hash(prvt->action_hash, action);
158}
159
160static long set_focus_policy(const char *, long cl_aww, void *) {
161    AW_window *aww = (AW_window*)cl_aww;
162    aww->set_focus_policy(aww->get_root()->focus_follows_mouse);
163    return cl_aww;
164}
165void AW_root::apply_focus_policy(bool follow_mouse) {
166    focus_follows_mouse = follow_mouse;
167    GBS_hash_do_loop(hash_for_windows, set_focus_policy, 0);
168}
169
170void AW_root::apply_sensitivity(AW_active mask) {
171    aw_assert(legal_mask(mask));
172    AW_buttons_struct *list;
173
174    global_mask = mask;
175    for (list = button_sens_list; list; list = list->next) {
176        XtSetSensitive(list->button, (list->mask & mask) ? True : False);
177    }
178}
179
180
181struct fallbacks {
182    const char *fb;
183    const char *awar;
184    const char *init;
185};
186
187static struct fallbacks aw_fb[] = {
188    // Name         fallback awarname    default value
189    { "FontList",   "window/font",       "8x13bold" },
190    { "background", "window/background", "grey" },
191    { "foreground", "window/foreground", "Black", },
192    { 0,            "window/color_1",    "red", },
193    { 0,            "window/color_2",    "green", },
194    { 0,            "window/color_3",    "blue", },
195    { 0,            0,                   0 }
196};
197
198
199void AW_root::init_variables(AW_default database) {
200    application_database     = database;
201    hash_table_for_variables = GBS_create_hash(1000, GB_MIND_CASE);
202    hash_for_windows         = GBS_create_hash(100, GB_MIND_CASE);
203
204    for (int i=0; aw_fb[i].awar; ++i) {
205        awar_string(aw_fb[i].awar, aw_fb[i].init, application_database);
206    }
207}
208
209static void aw_message_and_dump_stderr(const char *msg) {
210    fflush(stdout);
211    fprintf(stderr, "ARB: %s\n", msg); // print to console as well
212    fflush(stderr);
213    aw_message(msg);
214}
215static void dump_stdout(const char *msg) {
216    fprintf(stdout, "ARB: %s\n", msg);
217}
218
219static arb_status_implementation AW_status_impl = {
220    AST_RANDOM, 
221    aw_openstatus,
222    aw_closestatus,
223    aw_status_title, // set_title
224    AW_status, // set_subtitle
225    AW_status, // set_gauge
226    AW_status, // user_abort
227};
228
229static arb_handlers aw_handlers = {
230    aw_message_and_dump_stderr,
231    aw_message,
232    dump_stdout,
233    AW_status_impl, 
234};
235
236void AW_root::init_root(const char *programname, bool no_exit) {
237    // initialize ARB X application
238    int          a             = 0;
239    XFontStruct *fontstruct;
240    const int    MAX_FALLBACKS = 30;
241    char        *fallback_resources[MAX_FALLBACKS];
242
243    prvt->action_hash = GBS_create_hash(1000, GB_MIND_CASE);
244
245    p_r-> no_exit = no_exit;
246    program_name  = strdup(programname);
247
248    int i;
249    for (i=0; aw_fb[i].fb; i++) {
250        GBDATA *gb_awar       = GB_search((GBDATA*)application_database, aw_fb[i].awar, GB_FIND);
251        fallback_resources[i] = GBS_global_string_copy("*%s: %s", aw_fb[i].fb, GB_read_char_pntr(gb_awar));
252    }
253    fallback_resources[i] = 0;
254    aw_assert(i<MAX_FALLBACKS);
255
256    ARB_install_handlers(aw_handlers);
257
258    // @@@ FIXME: the next line hangs if program runs inside debugger
259    p_r->toplevel_widget = XtOpenApplication(&(p_r->context), programname,
260            NULL, 0, // XrmOptionDescRec+numOpts
261            &a, // &argc
262            NULL, // argv
263            fallback_resources,
264            applicationShellWidgetClass, // widget class
265            NULL, 0);
266
267    for (i=0; fallback_resources[i]; i++) free(fallback_resources[i]);
268
269    p_r->display = XtDisplay(p_r->toplevel_widget);
270
271    if (p_r->display == NULL) {
272        printf("cannot open display\n");
273        exit(-1);
274    }
275    {
276        GBDATA *gbd = (GBDATA*)application_database;
277        const char *font = GB_read_char_pntr(GB_search(gbd, "window/font", GB_FIND));
278        if (!(fontstruct = XLoadQueryFont(p_r->display, font))) {
279            if (!(fontstruct = XLoadQueryFont(p_r->display, "fixed"))) {
280                printf("can not load font\n");
281                exit(-1);
282            }
283        }
284    }
285
286    if (fontstruct->max_bounds.width == fontstruct->min_bounds.width) {
287        font_width = fontstruct->max_bounds.width;
288    }
289    else {
290        font_width = (fontstruct->min_bounds.width
291                + fontstruct->max_bounds.width) / 2;
292    }
293
294    font_height = fontstruct->max_bounds.ascent
295            + fontstruct->max_bounds.descent;
296    font_ascent = fontstruct->max_bounds.ascent;
297
298    p_r->fontlist = XmFontListCreate(fontstruct, XmSTRING_DEFAULT_CHARSET);
299
300    button_sens_list = 0;
301
302    p_r->last_option_menu = p_r->current_option_menu = p_r->option_menu_list = NULL;
303    p_r->last_toggle_field = p_r->toggle_field_list = NULL;
304    p_r->last_selection_list = p_r->selection_list = NULL;
305
306    value_changed = false;
307    y_correction_for_input_labels = 5;
308    global_mask = AWM_ALL;
309
310    p_r->screen_depth = PlanesOfScreen(XtScreen(p_r->toplevel_widget));
311    if (p_r->screen_depth == 1) {
312        color_mode = AW_MONO_COLOR;
313    }
314    else {
315        color_mode = AW_RGB_COLOR;
316    }
317    p_r->colormap = DefaultColormapOfScreen(XtScreen(p_r->toplevel_widget));
318    p_r->clock_cursor = XCreateFontCursor(XtDisplay(p_r->toplevel_widget), XC_watch);
319    p_r->question_cursor = XCreateFontCursor(XtDisplay(p_r->toplevel_widget), XC_question_arrow);
320
321    create_colormap();
322    aw_root_init_font(XtDisplay(p_r->toplevel_widget));
323    aw_install_xkeys(XtDisplay(p_r->toplevel_widget));
324
325}
326
327/**
328 * A list of awar names that contain color names
329 */
330static const char *aw_awar_2_color[] = {
331    "window/background",
332    "window/foreground",
333    "window/color_1",
334    "window/color_2",
335    "window/color_3",
336    0
337};
338
339void AW_root::create_colormap() {
340
341    XColor xcolor_returned, xcolor_exakt;
342    GBDATA *gbd = check_properties(NULL);
343    prvt->color_table = (AW_rgb*)GB_calloc(sizeof(AW_rgb), AW_STD_COLOR_IDX_MAX);
344
345    // Color monitor, B&W monitor is no longer supported
346    const char **awar_2_color;
347    int color;
348    for (color = 0, awar_2_color = aw_awar_2_color;
349         *awar_2_color;
350         awar_2_color++, color++)
351    {
352        const char *name_of_color = GB_read_char_pntr(GB_search(gbd, *awar_2_color, GB_FIND));
353        if (XAllocNamedColor(prvt->display, prvt->colormap, name_of_color, &xcolor_returned, &xcolor_exakt) == 0) {
354            fprintf(stderr, "XAllocColor failed: %s\n", name_of_color);
355        }
356        else {
357            prvt->color_table[color] = xcolor_returned.pixel;
358        }
359    }
360
361    prvt->foreground = BlackPixelOfScreen(XtScreen(prvt->toplevel_widget));
362    XtVaGetValues(prvt->toplevel_widget, XmNbackground, &prvt->background, NULL);
363    // AW_WINDOW_DRAG see init_devices
364}
365
366
367void AW_root::window_hide(AW_window *aww) {
368    active_windows--;
369    if (active_windows<0) {
370        exit(0);
371    }
372    if (current_modal_window == aww) {
373        current_modal_window = NULL;
374    }
375}
376void AW_root::window_show() {
377    active_windows++;
378}
379
380/// begin timer stuff
381
382// internal struct to pass data to handler
383struct AW_timer_cb_struct : virtual Noncopyable {
384    AW_root *ar;
385    AW_RCB   f;
386    AW_CL    cd1;
387    AW_CL    cd2;
388
389    AW_timer_cb_struct(AW_root *ari, AW_RCB cb, AW_CL cd1i, AW_CL cd2i)
390        : ar(ari), f(cb), cd1(cd1i), cd2(cd2i) {}
391};
392
393static void AW_timer_callback(XtPointer aw_timer_cb_struct, XtIntervalId */*id*/) {
394    AW_timer_cb_struct *tcbs = (AW_timer_cb_struct *) aw_timer_cb_struct;
395    if (!tcbs)
396        return;
397
398    AW_root *root = tcbs->ar;
399    if (root->disable_callbacks) {
400        // delay the timer callback for 25ms
401        XtAppAddTimeOut(p_global->context,
402        (unsigned long)25, // wait 25 msec = 1/40 sec
403        (XtTimerCallbackProc)AW_timer_callback,
404        aw_timer_cb_struct);
405    }
406    else {
407        tcbs->f(root, tcbs->cd1, tcbs->cd2);
408        delete tcbs; // timer only once
409    }
410}
411
412static void AW_timer_callback_never_disabled(XtPointer aw_timer_cb_struct, XtIntervalId */*id*/) {
413    AW_timer_cb_struct *tcbs = (AW_timer_cb_struct *) aw_timer_cb_struct;
414    if (!tcbs)
415        return;
416
417    tcbs->f(tcbs->ar, tcbs->cd1, tcbs->cd2);
418    delete tcbs; // timer only once
419}
420
421void AW_root::add_timed_callback(int ms, AW_RCB2 f, AW_CL cd1, AW_CL cd2) {
422    XtAppAddTimeOut(p_r->context,
423                    (unsigned long)ms,
424                    (XtTimerCallbackProc)AW_timer_callback,
425                    (XtPointer) new AW_timer_cb_struct(this, f, cd1, cd2));
426}
427
428void AW_root::add_timed_callback_never_disabled(int ms, AW_RCB f, AW_CL cd1, AW_CL cd2) {
429    XtAppAddTimeOut(p_r->context,
430                    (unsigned long)ms,
431                    (XtTimerCallbackProc)AW_timer_callback_never_disabled,
432                    (XtPointer) new AW_timer_cb_struct(this, f, cd1, cd2));
433}
434/// end timer stuff
435
436
437AW_awar *AW_root::awar(const char *var_name) {
438    AW_awar *vs = awar_no_error(var_name);
439    if (!vs) GBK_terminatef("AWAR %s not defined", var_name);
440    return vs;
441}
442
443AW_awar *AW_root::awar_float(const char *var_name, float default_value, AW_default default_file) {
444    AW_awar *vs = awar_no_error(var_name);
445    if (!vs) {
446        default_file = check_properties(default_file);
447        vs           = new AW_awar(AW_FLOAT, var_name, "", (double)default_value, default_file, this);
448        GBS_write_hash(hash_table_for_variables, var_name, (long)vs);
449    }
450    return vs;
451}
452
453AW_awar *AW_root::awar_string(const char *var_name, const char *default_value, AW_default default_file) {
454    AW_awar *vs = awar_no_error(var_name);
455    if (!vs) {
456        default_file = check_properties(default_file);
457        vs           = new AW_awar(AW_STRING, var_name, default_value, 0, default_file, this);
458        GBS_write_hash(hash_table_for_variables, var_name, (long)vs);
459    }
460    return vs;
461}
462
463AW_awar *AW_root::awar_int(const char *var_name, long default_value, AW_default default_file) {
464    AW_awar *vs = awar_no_error(var_name);
465    if (!vs) {
466        default_file = check_properties(default_file);
467        vs           = new AW_awar(AW_INT, var_name, (const char *)default_value, 0, default_file, this);
468        GBS_write_hash(hash_table_for_variables, var_name, (long)vs);
469    }
470    return vs;
471}
472
473AW_awar *AW_root::awar_no_error(const char *var_name) {
474    return hash_table_for_variables ? (AW_awar *)GBS_read_hash(hash_table_for_variables, var_name) : NULL;
475}
476
477
478AW_awar *AW_root::awar_pointer(const char *var_name, void *default_value, AW_default default_file) {
479    AW_awar *vs = awar_no_error(var_name);
480    if (!vs) {
481        default_file = check_properties(default_file);
482        vs           = new AW_awar(AW_POINTER, var_name, (const char *)default_value, 0.0, default_file, this);
483        GBS_write_hash(hash_table_for_variables, var_name, (long)vs);
484    }
485    return vs;
486}
487
488static long awar_set_temp_if_is_default(const char *, long val, void *cl_gb_db) {
489    AW_awar *awar = (AW_awar*)val;
490    awar->set_temp_if_is_default((GBDATA*)cl_gb_db);
491    return val;
492}
493
494void AW_root::dont_save_awars_with_default_value(GBDATA *gb_db) {
495    // this should only be called
496    // - before saving properties
497    // - before saving any main application DB that may contain AWARs
498    //
499    // Bug: Awars created in main DB by clients (e.g. EDIT4) will stay temporary
500    //      and will never be saved again.
501    //
502    // Note: uninstanciated AWARs will not be affected, so different applications with
503    //       different AWAR subsets should be no problem.
504    //       'different applications' here e.g. also includes different calls to arb_ntree
505    //       (e.g. merge-tool, importer, tree-window, ...)
506    //
507    // Problems arise when an awar with the same name is used for two different purposes
508    // or with different default values (regardless whether in properties or main-DB).
509    // But this has already been problematic before.
510
511    GBS_hash_do_loop(hash_table_for_variables, awar_set_temp_if_is_default, (void*)gb_db);
512}
513
514void AW_root::main_loop() {
515    XtAppMainLoop(p_r->context);
516}
517
518void AW_root::set_focus_callback(AW_RCB fcb, AW_CL cd1, AW_CL cd2) {
519    AW_root_cblist::add(focus_callback_list, AW_root_callback(fcb, cd1, cd2));
520}
521
522static long AW_unlink_awar_from_DB(const char */*key*/, long cl_awar, void *cl_gb_main) {
523    AW_awar *awar    = (AW_awar*)cl_awar;
524    GBDATA  *gb_main = (GBDATA*)cl_gb_main;
525
526    awar->unlink_from_DB(gb_main);
527    return cl_awar;
528}
529void AW_root::unlink_awars_from_DB(AW_default database) {
530    GBDATA *gb_main = (GBDATA*)database;
531
532    aw_assert(GB_get_root(gb_main) == gb_main);
533
534    GB_transaction ta(gb_main); // needed in awar-callbacks during unlink
535    GBS_hash_do_loop(hash_table_for_variables, AW_unlink_awar_from_DB, gb_main);
536}
537
538AW_root::~AW_root() {
539    delete tracker;
540
541    AW_root_cblist::clear(focus_callback_list);
542    delete button_sens_list;    button_sens_list = NULL;
543
544    exit_root();
545    exit_variables();
546    aw_assert(this == AW_root::SINGLETON);
547
548    delete prvt;
549
550    free(program_name);
551
552    AW_root::SINGLETON = NULL;
553}
554
555typedef std::list<GBDATA*> DataPointers;
556
557static GB_ERROR set_parents_with_only_temp_childs_temp(GBDATA *gbd, DataPointers& made_temp) {
558    GB_ERROR error = NULL;
559
560    if (GB_read_type(gbd) == GB_DB && !GB_is_temporary(gbd)) {
561        bool has_savable_child = false;
562        for (GBDATA *gb_child = GB_child(gbd); gb_child && !error; gb_child = GB_nextChild(gb_child)) {
563            bool is_tmp = GB_is_temporary(gb_child);
564            if (!is_tmp) {
565                error              = set_parents_with_only_temp_childs_temp(gb_child, made_temp);
566                if (!error) is_tmp = GB_is_temporary(gb_child);         // may have changed
567
568                if (!is_tmp) has_savable_child = true;
569            }
570        }
571        if (!error && !has_savable_child) {
572            error = GB_set_temporary(gbd);
573            made_temp.push_back(gbd);
574        }
575    }
576
577    return error;
578}
579static GB_ERROR clear_temp_flags(DataPointers& made_temp) {
580    GB_ERROR error = NULL;
581    for (DataPointers::iterator mt = made_temp.begin(); mt != made_temp.end() && !error; ++mt) {
582        error = GB_clear_temporary(*mt);
583    }
584    return error;
585}
586
587GB_ERROR AW_root::save_properties(const char *filename) {
588    GB_ERROR  error   = NULL;
589    GBDATA   *gb_prop = application_database;
590
591    if (!gb_prop) {
592        error = "No properties loaded - won't save";
593    }
594    else {
595        error = GB_push_transaction(gb_prop);
596        if (!error) {
597            aw_update_all_window_geometry_awars(this);
598            error = GB_pop_transaction(gb_prop);
599            if (!error) {
600                dont_save_awars_with_default_value(gb_prop);
601
602                DataPointers made_temp;
603                error             = set_parents_with_only_temp_childs_temp(gb_prop, made_temp); // avoid saving empty containers
604                if (!error) error = GB_save_in_arbprop(gb_prop, filename, "a");
605                if (!error) error = clear_temp_flags(made_temp);
606            }
607        }
608    }
609
610    return error;
611}
612
Note: See TracBrowser for help on using the repository browser.