source: tags/ms_r16q3/WINDOW/AW_awar.cxx

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