source: tags/ms_r16q2/WINDOW/AW_root.cxx

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