source: trunk/WINDOW/AW_root.cxx

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