source: branches/species/WINDOW/AW_root.cxx

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