source: branches/properties/WINDOW/AW_awar.cxx

Last change on this file was 18959, checked in by westram, 3 years ago
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 24.5 KB
Line 
1// =============================================================== //
2//                                                                 //
3//   File      : AW_awar.cxx                                       //
4//   Purpose   :                                                   //
5//                                                                 //
6//   Institute of Microbiology (Technical University Munich)       //
7//   http://www.arb-home.de/                                       //
8//                                                                 //
9// =============================================================== //
10
11#include "aw_nawar.hxx"
12#include "aw_awar.hxx"
13#include "aw_root.hxx"
14#include "aw_msg.hxx"
15#include "aw_window.hxx"
16#include "aw_select.hxx"
17#include <arb_str.h>
18#include <arb_file.h>
19#include <ad_cb.h>
20#include <list>
21#include <sys/stat.h>
22#include <climits>
23#include <cfloat>
24
25#if defined(DEBUG)
26// uncomment next line to dump all awar-changes to stderr
27// #define DUMP_AWAR_CHANGES
28#endif // DEBUG
29
30#define AWAR_EPS 0.00000001
31
32#if defined(DUMP_AWAR_CHANGES)
33#define AWAR_CHANGE_DUMP(name, where, format) fprintf(stderr, "change awar '%s' " where "(" format ")\n", name, para)
34#else
35#define AWAR_CHANGE_DUMP(name, where, format)
36#endif // DEBUG
37
38#if defined(ASSERTION_USED)
39bool AW_awar::deny_read = false;
40bool AW_awar::deny_write = false;
41#endif
42
43struct AW_widget_refresh_cb : virtual Noncopyable {
44    AW_widget_refresh_cb(AW_widget_refresh_cb *previous, AW_awar *vs, AW_CL cd1, Widget w, AW_widget_type type, AW_window *awi);
45    ~AW_widget_refresh_cb();
46
47    AW_CL           cd;
48    AW_awar        *awar;
49    Widget          widget;
50    AW_widget_type  widget_type;
51    AW_window      *aw;
52
53    AW_widget_refresh_cb *next;
54};
55
56
57static void aw_cp_awar_2_widget_cb(AW_root *root, AW_widget_refresh_cb *widgetlist) {
58    if (widgetlist->widget == root->changer_of_variable) {
59        root->changer_of_variable = NULp;
60        root->value_changed       = false;
61        return;
62    }
63
64    {
65        char *var_value;
66        var_value = widgetlist->awar->read_as_string();
67
68        // und benachrichtigen der anderen
69        switch (widgetlist->widget_type) {
70
71            case AW_WIDGET_INPUT_FIELD:
72                widgetlist->aw->update_input_field(widgetlist->widget, var_value);
73                break;
74            case AW_WIDGET_TEXT_FIELD:
75                widgetlist->aw->update_text_field(widgetlist->widget, var_value);
76                break;
77            case AW_WIDGET_TOGGLE:
78                widgetlist->aw->update_toggle(widgetlist->widget, var_value, widgetlist->cd);
79                break;
80            case AW_WIDGET_LABEL_FIELD:
81                widgetlist->aw->update_label(widgetlist->widget, var_value);
82                break;
83            case AW_WIDGET_CHOICE_MENU:
84                widgetlist->aw->refresh_option_menu((AW_option_menu_struct*)widgetlist->cd);
85                break;
86            case AW_WIDGET_TOGGLE_FIELD:
87                widgetlist->aw->refresh_toggle_field((int)widgetlist->cd);
88                break;
89            case AW_WIDGET_SELECTION_LIST:
90                ((AW_selection_list *)widgetlist->cd)->refresh();
91                break;
92            case AW_WIDGET_SCALER:
93                widgetlist->aw->update_scaler(widgetlist->widget, widgetlist->awar, (AW_ScalerType)widgetlist->cd);
94                break;
95            default:
96                break;
97        }
98        free(var_value);
99    }
100    root->value_changed = false;     // Maybe value changed is set because Motif calls me
101}
102
103
104AW_widget_refresh_cb::AW_widget_refresh_cb(AW_widget_refresh_cb *previous, AW_awar *vs, AW_CL cd1, Widget w, AW_widget_type type, AW_window *awi) {
105    cd          = cd1;
106    widget      = w;
107    widget_type = type;
108    awar        = vs;
109    aw          = awi;
110    next        = previous;
111
112    awar->add_callback(makeRootCallback(aw_cp_awar_2_widget_cb, this));
113}
114
115AW_widget_refresh_cb::~AW_widget_refresh_cb() {
116    if (next) delete next;
117    awar->remove_callback(makeRootCallback(aw_cp_awar_2_widget_cb, this));
118}
119
120AW_var_target::AW_var_target(void* pntr, AW_var_target *nexti) {
121    next    = nexti;
122    pointer = pntr;
123}
124
125
126bool AW_awar::allowed_to_run_callbacks = true;
127
128static GB_ERROR AW_MSG_UNMAPPED_AWAR = "Error (unmapped AWAR):\n"
129    "You cannot write to this field because it is either deleted or\n"
130    "unmapped. Try to select a different item, reselect this and retry.";
131
132#define WRITE_BODY(self,format,func)                    \
133    if (!gb_var) return AW_MSG_UNMAPPED_AWAR;           \
134    aw_assert(!deny_write);                             \
135    GB_transaction ta(gb_var);                          \
136    AWAR_CHANGE_DUMP(awar_name, #self, format);         \
137    GB_ERROR error = func(gb_var, para);                \
138    if (!error) update_tmp_state_during_change()
139
140#define WRITE_SKELETON(self,type,format,func)           \
141    GB_ERROR AW_awar::self(type para) {                 \
142        WRITE_BODY(self, format, func);                 \
143        return error;                                   \
144    }                                                   \
145    GB_ERROR AW_awar::concatenate(re,self)(type para) { \
146        WRITE_BODY(self, format, func);                 \
147        GB_touch(gb_var);                               \
148        return error;                                   \
149    }
150
151WRITE_SKELETON(write_string, const char*, "%s", GB_write_string) // defines rewrite_string
152WRITE_SKELETON(write_int, long, "%li", GB_write_int) // defines rewrite_int
153WRITE_SKELETON(write_float, float, "%f", GB_write_float) // defines rewrite_float
154WRITE_SKELETON(write_as_string, const char*, "%s", GB_write_autoconv_string) // defines rewrite_as_string // @@@ rename -> write_autoconvert_string?
155WRITE_SKELETON(write_pointer, GBDATA*, "%p", GB_write_pointer) // defines rewrite_pointer
156
157#undef WRITE_SKELETON
158#undef AWAR_CHANGE_DUMP
159
160
161char *AW_awar::read_as_string() const {
162    aw_assert(!deny_read);
163    if (!gb_var) return strdup("");
164    GB_transaction ta(gb_var);
165    return GB_read_as_string(gb_var);
166}
167
168const char *AW_awar::read_char_pntr() const {
169    aw_assert(!deny_read);
170    aw_assert(variable_type == AW_STRING);
171
172    if (!gb_var) return "";
173    GB_transaction ta(gb_var);
174    return GB_read_char_pntr(gb_var);
175}
176
177float AW_awar::read_float() const {
178    aw_assert(!deny_read);
179    if (!gb_var) return 0.0;
180    GB_transaction ta(gb_var);
181    return GB_read_float(gb_var);
182}
183
184long AW_awar::read_int() const {
185    aw_assert(!deny_read);
186    if (!gb_var) return 0;
187    GB_transaction ta(gb_var);
188    return GB_read_int(gb_var);
189}
190
191GBDATA *AW_awar::read_pointer() const {
192    aw_assert(!deny_read);
193    if (!gb_var) return NULp;
194    GB_transaction ta(gb_var);
195    return GB_read_pointer(gb_var);
196}
197
198char *AW_awar::read_string() const {
199    aw_assert(!deny_read);
200    aw_assert(variable_type == AW_STRING);
201
202    if (!gb_var) return strdup("");
203    GB_transaction ta(gb_var);
204    return GB_read_string(gb_var);
205}
206
207void AW_awar::touch() {
208    aw_assert(!deny_write);
209    if (gb_var) {
210        GB_transaction ta(gb_var);
211        GB_touch(gb_var);
212    }
213}
214GB_ERROR AW_awar::reset_to_default() {
215    aw_assert(!deny_write);
216    GB_ERROR error = NULp;
217    switch (variable_type) {
218        case AW_STRING:  error = write_string (default_value.s); break;
219        case AW_INT:     error = write_int    (default_value.l); break;
220        case AW_FLOAT:   error = write_float  (default_value.f); break;
221        case AW_POINTER: error = write_pointer(default_value.p); break;
222        default: aw_assert(0); break;
223    }
224    return error;
225}
226
227void AW_awar::untie_all_widgets() {
228    delete refresh_list; refresh_list = NULp;
229}
230
231AW_awar *AW_awar::add_callback(const RootCallback& rcb) {
232    AW_root_cblist::add(callback_list, rcb);
233    return this;
234}
235
236AW_awar *AW_awar::add_target_var(char **ppchr) {
237    assert_var_type(AW_STRING);
238    target_list = new AW_var_target((void *)ppchr, target_list);
239    update_target(target_list);
240    return this;
241}
242
243AW_awar *AW_awar::add_target_var(float *pfloat) {
244    assert_var_type(AW_FLOAT);
245    target_list = new AW_var_target((void *)pfloat, target_list);
246    update_target(target_list);
247    return this;
248}
249
250AW_awar *AW_awar::add_target_var(long *pint) {
251    assert_var_type(AW_INT);
252    target_list = new AW_var_target((void *)pint, target_list);
253    update_target(target_list);
254    return this;
255}
256
257void AW_awar::tie_widget(AW_CL cd1, Widget widget, AW_widget_type type, AW_window *aww) {
258    refresh_list = new AW_widget_refresh_cb(refresh_list, this, cd1, widget, type, aww);
259}
260
261
262AW_VARIABLE_TYPE AW_awar::get_type() const {
263    return this->variable_type;
264}
265
266
267// extern "C"
268static void AW_var_gbdata_callback(GBDATA*, AW_awar *awar) {
269    awar->update();
270}
271
272
273static void AW_var_gbdata_callback_delete_intern(GBDATA *gbd, AW_awar *awar) {
274    if (awar->gb_origin == gbd) {
275        // make awar zombie
276        awar->gb_var    = NULp;
277        awar->gb_origin = NULp;
278    }
279    else {
280        aw_assert(awar->gb_var == gbd);             // forgot to remove a callback ?
281        awar->gb_var = awar->gb_origin;             // unmap
282    }
283
284    awar->update();
285}
286
287AW_awar::AW_awar(AW_VARIABLE_TYPE var_type, const char *var_name,
288                 const char *var_value, float var_float_value,
289                 AW_default default_file, AW_root *rooti)
290    : callback_list(NULp),
291      target_list(NULp),
292      refresh_list(NULp),
293      callback_time_sum(0.0),
294      callback_time_count(0),
295#if defined(DEBUG)
296      is_global(false),
297#endif
298      root(rooti),
299      gb_var(NULp)
300{
301    pp.f.min = 0;
302    pp.f.max = 0;
303    pp.srt   = NULp;
304
305    GB_transaction ta(default_file);
306
307    aw_assert(var_name && var_name[0] != 0);
308
309#if defined(DEBUG)
310    GB_ERROR err = GB_check_hkey(var_name);
311    aw_assert(!err);
312#endif // DEBUG
313
314    awar_name      = strdup(var_name);
315    GBDATA *gb_def = GB_search(default_file, var_name, GB_FIND);
316
317    in_tmp_branch = strncmp(var_name, "tmp/", 4) == 0;
318
319    GB_TYPES wanted_gbtype = (GB_TYPES)var_type;
320
321    if (gb_def) { // use value stored in DB
322        GB_TYPES gbtype = GB_read_type(gb_def);
323
324        if (gbtype != wanted_gbtype) {
325            GB_warningf("Existing awar '%s' has wrong type (%i instead of %i) - recreating\n",
326                        var_name, int(gbtype), int(wanted_gbtype));
327            GB_delete(gb_def);
328            gb_def = NULp;
329        }
330    }
331
332    // store default-value in member
333    switch (var_type) {
334        case AW_STRING:  default_value.s = nulldup(var_value); break;
335        case AW_INT:     default_value.l = (long)var_value; break;
336        case AW_FLOAT:   default_value.f = var_float_value; break;
337        case AW_POINTER: default_value.p = (GBDATA*)var_value; break;
338        default: aw_assert(0); break;
339    }
340
341    if (!gb_def) { // set AWAR to default value
342        gb_def = GB_search(default_file, var_name, wanted_gbtype);
343
344        switch (var_type) {
345            case AW_STRING:
346#if defined(DUMP_AWAR_CHANGES)
347                fprintf(stderr, "creating awar_string '%s' with default value '%s'\n", var_name, default_value.s);
348#endif // DUMP_AWAR_CHANGES
349                GB_write_string(gb_def, default_value.s);
350                break;
351
352            case AW_INT: {
353#if defined(DUMP_AWAR_CHANGES)
354                fprintf(stderr, "creating awar_int '%s' with default value '%li'\n", var_name, default_value.l);
355#endif // DUMP_AWAR_CHANGES
356                GB_write_int(gb_def, default_value.l);
357                break;
358            }
359            case AW_FLOAT:
360#if defined(DUMP_AWAR_CHANGES)
361                fprintf(stderr, "creating awar_float '%s' with default value '%f'\n", var_name, default_value.f);
362#endif // DUMP_AWAR_CHANGES
363                GB_write_float(gb_def, default_value.f);
364                break;
365
366            case AW_POINTER: {
367#if defined(DUMP_AWAR_CHANGES)
368                fprintf(stderr, "creating awar_pointer '%s' with default value '%p'\n", var_name, default_value.p);
369#endif // DUMP_AWAR_CHANGES
370                GB_write_pointer(gb_def, default_value.p);
371                break;
372            }
373            default:
374                GB_warningf("AWAR '%s' cannot be created because of disallowed type", var_name);
375                break;
376        }
377
378        GB_ERROR error = GB_set_temporary(gb_def);
379        if (error) GB_warningf("AWAR '%s': failed to set temporary on creation (Reason: %s)", var_name, error);
380    }
381
382    variable_type = var_type;
383    gb_origin     = gb_def;
384    this->map(gb_def);
385
386    aw_assert(is_valid());
387}
388
389
390void AW_awar::update() {
391    bool fix_value = false;
392
393    aw_assert(is_valid());
394
395    if (gb_var && ((pp.f.min != pp.f.max) || pp.srt)) {
396        switch (variable_type) {
397            case AW_INT: {
398                long lo = read_int();
399                if (lo < pp.f.min -.5) {
400                    fix_value = true;
401                    lo = (int)(pp.f.min + 0.5);
402                }
403                if (lo>pp.f.max + .5) {
404                    fix_value = true;
405                    lo = (int)(pp.f.max + 0.5);
406                }
407                if (fix_value) {
408                    if (root) root->changer_of_variable = NULp;
409                    write_int(lo);
410                }
411                break;
412            }
413            case AW_FLOAT: {
414                float fl = read_float();
415                if (fl < pp.f.min) {
416                    fix_value = true;
417                    fl = pp.f.min+AWAR_EPS;
418                }
419                if (fl>pp.f.max) {
420                    fix_value = true;
421                    fl = pp.f.max-AWAR_EPS;
422                }
423                if (fix_value) {
424                    if (root) root->changer_of_variable = NULp;
425                    write_float(fl);
426                }
427                break;
428            }
429            case AW_STRING: {
430                arb_assert(pp.srt); // otherwise call to GBS_string_eval is expensive
431                char *str = read_string();
432                char *n   = GBS_string_eval(str, pp.srt);
433
434                if (!n) GBK_terminatef("SRT ERROR %s %s", pp.srt, GB_await_error());
435
436                if (strcmp(n, str) != 0) {
437                    fix_value = true;
438                    if (root) root->changer_of_variable = NULp;
439                    write_string(n);
440                }
441                free(n);
442                free(str);
443                break;
444            }
445            default:
446                break;
447        }
448    }
449
450    if (!fix_value) { // function was already recalled by write_xxx() above
451        this->update_targets();
452        this->run_callbacks();
453    }
454
455    aw_assert(is_valid());
456}
457
458
459void AW_awar::update_target(AW_var_target *pntr) {
460    // send data to all variables
461    if (!pntr->pointer) return;
462    switch (variable_type) {
463        case AW_STRING: this->get((char **)pntr->pointer); break;
464        case AW_FLOAT:  this->get((float *)pntr->pointer); break;
465        case AW_INT:    this->get((long *)pntr->pointer); break;
466        default: aw_assert(0); GB_warning("Unknown awar type"); break;
467    }
468}
469
470
471
472void AW_awar::assert_var_type(AW_VARIABLE_TYPE wanted_type) {
473    if (wanted_type != variable_type) {
474        GBK_terminatef("AWAR '%s' has wrong type (got=%i, expected=%i)",
475                       awar_name, variable_type, wanted_type);
476    }
477}
478
479// extern "C"
480static void AW_var_gbdata_callback_delete(GBDATA *gbd, AW_awar *awar) {
481    AW_var_gbdata_callback_delete_intern(gbd, awar);
482}
483
484AW_awar *AW_awar::map(AW_default gbd) {
485    if (gb_var) {                                   // remove old mapping
486        GB_remove_callback((GBDATA *)gb_var, GB_CB_CHANGED, makeDatabaseCallback(AW_var_gbdata_callback, this));
487        if (gb_var != gb_origin) {                  // keep callback if pointing to origin!
488            GB_remove_callback((GBDATA *)gb_var, GB_CB_DELETE, makeDatabaseCallback(AW_var_gbdata_callback_delete, this));
489        }
490        gb_var = NULp;
491    }
492
493    if (!gbd) {                                     // attempt to remap to NULp -> unmap
494        gbd = gb_origin;
495    }
496
497    if (gbd) {
498        GB_transaction ta(gbd);
499
500        GB_ERROR error = GB_add_callback((GBDATA *) gbd, GB_CB_CHANGED, makeDatabaseCallback(AW_var_gbdata_callback, this));
501        if (!error && gbd != gb_origin) {           // do not add delete callback if mapping to origin
502            error = GB_add_callback((GBDATA *) gbd, GB_CB_DELETE, makeDatabaseCallback(AW_var_gbdata_callback_delete, this));
503        }
504        if (error) aw_message(error);
505
506        gb_var = gbd;
507        update();
508    }
509    else {
510        update();
511    }
512
513    aw_assert(is_valid());
514
515    return this;
516}
517
518AW_awar *AW_awar::map(AW_awar *dest) {
519    return this->map(dest->gb_var);
520}
521AW_awar *AW_awar::map(const char *awarn) {
522    return map(root->awar(awarn));
523}
524
525AW_awar *AW_awar::remove_callback(const RootCallback& rcb) {
526    AW_root_cblist::remove(callback_list, rcb);
527    return this;
528}
529
530AW_awar *AW_awar::set_minmax(float min, float max) {
531    if (variable_type == AW_STRING) GBK_terminatef("set_minmax does not apply to string AWAR '%s'", awar_name);
532    if (min>=max) {
533        // 'min>max'  would set impossible limits
534        // 'min==max' would remove limits, which is not allowed!
535        GBK_terminatef("illegal values in set_minmax for AWAR '%s'", awar_name);
536    }
537
538    pp.f.min = min;
539    pp.f.max = max;
540    update(); // corrects wrong default value
541    return this;
542}
543
544float AW_awar::get_min() const {
545    if (variable_type == AW_STRING) GBK_terminatef("get_min does not apply to string AWAR '%s'", awar_name);
546    bool isSet = (pp.f.min != pp.f.max); // as used in AW_awar::update
547    if (!isSet) {
548        aw_assert(float(INT_MIN)>=-FLT_MAX);
549        if (variable_type == AW_INT) return float(INT_MIN);
550        aw_assert(variable_type == AW_FLOAT);
551        return -FLT_MAX;
552    }
553    return pp.f.min;
554}
555float AW_awar::get_max() const {
556    if (variable_type == AW_STRING) GBK_terminatef("get_max does not apply to string AWAR '%s'", awar_name);
557    bool isSet = (pp.f.min != pp.f.max); // as used in AW_awar::update
558    if (!isSet) {
559        aw_assert(float(INT_MAX)<=FLT_MAX);
560        if (variable_type == AW_INT) return float(INT_MAX);
561        aw_assert(variable_type == AW_FLOAT);
562        return FLT_MAX;
563    }
564    return pp.f.max;
565}
566
567AW_awar *AW_awar::set_srt(const char *srt) {
568    assert_var_type(AW_STRING);
569    pp.srt = srt;
570    return this;
571}
572
573GB_ERROR AW_awar::toggle_toggle() {
574    char *var = this->read_as_string();
575    GB_ERROR    error = NULp;
576    if (var[0] == '0' || var[0] == 'n') {
577        switch (this->variable_type) {
578            case AW_STRING:     error = this->write_string("yes"); break;
579            case AW_INT:        error = this->write_int(1); break;
580            case AW_FLOAT:      error = this->write_float(1.0); break;
581            default: break;
582        }
583    }
584    else {
585        switch (this->variable_type) {
586            case AW_STRING:     error = this->write_string("no"); break;
587            case AW_INT:        error = this->write_int(0); break;
588            case AW_FLOAT:      error = this->write_float(0.0); break;
589            default: break;
590        }
591    }
592    free(var);
593    return error;
594}
595
596AW_awar *AW_awar::unmap() {
597    return this->map(gb_origin);
598}
599
600void AW_awar::run_callbacks() {
601    if (allowed_to_run_callbacks) {
602        clock_t start = clock();
603
604        AW_root_cblist::call(callback_list, root);
605
606        clock_t end = clock();
607
608        if (start<end) { // ignore overflown
609            double cpu_time_used = ((double) (end - start)) / CLOCKS_PER_SEC;
610
611            if (callback_time_count>19 && (callback_time_count%2) == 0) {
612                callback_time_count = callback_time_count/2;
613                callback_time_sum   = callback_time_sum/2.0;
614            }
615
616            callback_time_sum += cpu_time_used;
617            callback_time_count++;
618        }
619    }
620}
621
622
623void AW_awar::update_targets() {
624    // send data to all variables
625    AW_var_target*pntr;
626    for (pntr = target_list; pntr; pntr = pntr->next) {
627        update_target(pntr);
628    }
629}
630
631
632void AW_awar::remove_all_callbacks() {
633    AW_root_cblist::clear(callback_list);
634}
635
636// --------------------------------------------------------------------------------
637
638inline bool member_of_DB(GBDATA *gbd, GBDATA *gb_main) {
639    return gbd && GB_get_root(gbd) == gb_main;;
640}
641
642void AW_awar::unlink() {
643    aw_assert(this->is_valid());
644    remove_all_callbacks();
645    remove_all_target_vars();
646    gb_origin = NULp;                               // make zombie awar
647    unmap();                                        // unmap to nothing
648    aw_assert(is_valid());
649}
650
651bool AW_awar::unlink_from_DB(GBDATA *gb_main) {
652    bool make_zombie  = false;
653    bool mapped_to_DB = member_of_DB(gb_var, gb_main);
654    bool origin_in_DB = member_of_DB(gb_origin, gb_main);
655
656    aw_assert(is_valid());
657
658    if (mapped_to_DB) {
659        if (origin_in_DB) make_zombie = true;
660        else unmap();
661    }
662    else if (origin_in_DB) {
663        // origin is in DB, current mapping is not
664        // -> remap permanentely
665        gb_origin = gb_var;
666    }
667
668    if (make_zombie) unlink();
669
670    aw_assert(is_valid());
671
672    return make_zombie;
673}
674
675// --------------------------------------------------------------------------------
676
677
678
679void AW_awar::remove_all_target_vars() {
680    while (target_list) {
681        AW_var_target *tar = target_list;
682        target_list        = tar->next;
683        delete tar;
684    }
685}
686
687
688void AW_awar::update_tmp_state_during_change() {
689    // here it's known that the awar is inside the correct DB (main-DB or prop-DB)
690
691    if (has_managed_tmp_state()) {
692        aw_assert(GB_get_transaction_level(gb_origin) != 0);
693        // still should be inside change-TA (or inside TA opened in update_tmp_state())
694        // (or in no-TA-mode; as true for properties)
695
696        bool has_default_value = false;
697        switch (variable_type) {
698            case AW_STRING:  has_default_value = ARB_strNULLcmp(GB_read_char_pntr(gb_origin), default_value.s) == 0; break;
699            case AW_INT:     has_default_value = GB_read_int(gb_origin)     == default_value.l; break;
700            case AW_FLOAT:   has_default_value = GB_read_float(gb_origin)   == default_value.f; break;
701            case AW_POINTER: has_default_value = GB_read_pointer(gb_origin) == default_value.p; break;
702            default: aw_assert(0); GB_warning("Unknown awar type"); break;
703        }
704
705        if (GB_is_temporary(gb_origin) != has_default_value) {
706            GB_ERROR error = has_default_value ? GB_set_temporary(gb_origin) : GB_clear_temporary(gb_origin);
707            if (error) GB_warning(GBS_global_string("Failed to set temporary for AWAR '%s' (Reason: %s)", awar_name, error));
708        }
709    }
710}
711
712void AW_awar::set_temp_if_is_default(GBDATA *gb_db) {
713    if (has_managed_tmp_state() && member_of_DB(gb_origin, gb_db)) { // ignore awars in "other" DB (main-DB vs properties)
714        aw_assert(GB_get_transaction_level(gb_origin)<1); // make sure allowed_to_run_callbacks works as expected
715
716        allowed_to_run_callbacks = false;                 // avoid AWAR-change-callbacks
717        {
718            GB_transaction ta(gb_origin);
719            update_tmp_state_during_change();
720        }
721        allowed_to_run_callbacks = true;
722    }
723}
724
725AW_awar::~AW_awar() {
726    unlink();
727    untie_all_widgets();
728    if (variable_type == AW_STRING) free(default_value.s);
729    free(awar_name);
730}
731
732// --------------------------------------------------------------------------------
733
734#ifdef UNIT_TESTS
735#include <test_unit.h>
736
737static int test_cb1_called;
738static int test_cb2_called;
739
740static void test_cb1(AW_root *, AW_CL cd1, AW_CL cd2) { test_cb1_called += (cd1+cd2); }
741static void test_cb2(AW_root *, AW_CL cd1, AW_CL cd2) { test_cb2_called += (cd1+cd2); }
742
743#define TEST_EXPECT_CBS_CALLED(cbl, c1,c2)              \
744    do {                                                \
745        test_cb1_called = test_cb2_called = 0;          \
746        AW_root_cblist::call(cbl, NULp);                \
747        TEST_EXPECT_EQUAL(test_cb1_called, c1);         \
748        TEST_EXPECT_EQUAL(test_cb2_called, c2);         \
749    } while(0)
750
751void TEST_AW_root_cblist() {
752    AW_root_cblist *cb_list = NULp;
753
754    RootCallback tcb1(test_cb1, 1, 0);
755    RootCallback tcb2(test_cb2, 0, 1);
756    RootCallback wrong_tcb2(test_cb2, 1, 0);
757
758    AW_root_cblist::add(cb_list, tcb1); TEST_EXPECT_CBS_CALLED(cb_list, 1, 0);
759    AW_root_cblist::add(cb_list, tcb2); TEST_EXPECT_CBS_CALLED(cb_list, 1, 1);
760
761    AW_root_cblist::remove(cb_list, tcb1);       TEST_EXPECT_CBS_CALLED(cb_list, 0, 1);
762    AW_root_cblist::remove(cb_list, wrong_tcb2); TEST_EXPECT_CBS_CALLED(cb_list, 0, 1);
763    AW_root_cblist::remove(cb_list, tcb2);       TEST_EXPECT_CBS_CALLED(cb_list, 0, 0);
764
765    AW_root_cblist::add(cb_list, tcb1);
766    AW_root_cblist::add(cb_list, tcb1); // add callback twice
767    TEST_EXPECT_CBS_CALLED(cb_list, 1, 0);  // should only be called once
768
769    AW_root_cblist::add(cb_list, tcb2);
770    AW_root_cblist::clear(cb_list);
771    TEST_EXPECT_CBS_CALLED(cb_list, 0, 0); // list clear - nothing should be called
772}
773TEST_PUBLISH(TEST_AW_root_cblist);
774
775#endif // UNIT_TESTS
776
777
Note: See TracBrowser for help on using the repository browser.