source: tags/testbuild/WINDOW/AW_root.cxx

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