diff -r 18892c0a9adc -r b70e2a77dea0 ui/common/context.c --- a/ui/common/context.c Sat Dec 05 11:54:58 2020 +0100 +++ b/ui/common/context.c Sat Dec 05 17:50:22 2020 +0100 @@ -44,8 +44,8 @@ ctx->obj = toplevel; ctx->vars = ucx_map_new_a(mp->allocator, 16); - ctx->set_document = uic_context_set_document; - ctx->detach_document = uic_context_detach_document; + ctx->attach_document = uic_context_attach_document; + ctx->detach_document2 = uic_context_detach_document2; #ifdef UI_GTK if(toplevel->widget) { @@ -61,139 +61,126 @@ return ctx->parent ? uic_root_context(ctx->parent) : ctx; } -void uic_context_set_document(UiContext *ctx, void *document) { - if(ctx->document == document) { - return; - } - if(ctx->document) { - uic_context_detach_document(ctx); - } - ctx->document = document; +void uic_context_attach_document(UiContext *ctx, void *document) { + ctx->documents = ucx_list_append_a(ctx->mempool->allocator, ctx->documents, document); + ctx->document = ctx->documents->data; + + UiContext *doc_ctx = ui_document_context(document); - UiContext *docctx = ui_document_context(document); - if(!docctx) { - fprintf(stderr, "UiError: uic_context_set_document: pointer is not a document\n"); - return; + // check if any parent context has an unbound variable with the same name + // as any document variable + UiContext *var_ctx = ctx; + while(var_ctx) { + if(var_ctx->vars_unbound && var_ctx->vars_unbound->count > 0) { + UcxMapIterator i = ucx_map_iterator(var_ctx->vars_unbound); + UiVar *var; + // rmkeys holds all keys, that shall be removed from vars_unbound + UcxKey *rmkeys = calloc(var_ctx->vars_unbound->count, sizeof(UcxKey)); + size_t numkeys = 0; + UCX_MAP_FOREACH(key, var, i) { + UiVar *docvar = ucx_map_get(doc_ctx->vars, key); + if(docvar) { + // bind var to document var + uic_copy_binding(var, docvar, TRUE); + rmkeys[numkeys++] = key; // save the key for removal + } + } + // now that we may have bound some vars to the document, + // we can remove them from the unbound map + for(size_t k=0;kvars_unbound, rmkeys[k]); + } + } + + var_ctx = ctx->parent; } - docctx->obj = ctx->obj; - docctx->parent = ctx; - - UiContext *root = uic_root_context(ctx); - if(!root->bound || root->bound->count == 0) { - return; +} + +static void uic_context_unbind_vars(UiContext *ctx) { + UcxMapIterator i = ucx_map_iterator(ctx->vars); + UiVar *var; + UCX_MAP_FOREACH(key, var, i) { + if(var->from && var->from_ctx) { + uic_save_var2(var); + uic_copy_binding(var, var->from, FALSE); + ucx_map_put(var->from_ctx->vars_unbound, key, var->from); + var->from_ctx = ctx; + } } - // there are bound variables in the root ctx - // copy bindings with correct name to doc vars - UcxMapIterator i = ucx_map_iterator(docctx->vars); - UiVar *var; - UCX_MAP_FOREACH(key, var, i) { - UiVar *v = ucx_map_get(root->bound, key); - if(v) { - // copy binding: after this, all doc vars with names of previously - // bound variables are bound to the widgets - // the widgets still hold a pointer to the root ctx vars, but this - // vars have a pointer to the document variable value - confusing - uic_copy_binding(v, var, TRUE); - } + UCX_FOREACH(elm, ctx->documents) { + UiContext *subctx = ui_document_context(elm->data); + uic_context_unbind_vars(subctx); } } -void uic_context_detach_document(UiContext *ctx) { - if(!ctx->document) { - return; +void uic_context_detach_document2(UiContext *ctx, void *document) { + // find the document in the documents list + UcxList *doc = NULL; + UCX_FOREACH(elm, ctx->documents) { + if(elm->data == document) { + doc = elm; + break; + } + } + if(!doc) { + return; // document is not a subdocument of this context } - UiContext *docctx = ui_document_context(ctx->document); - if(!docctx) { - fprintf(stderr, "UiError: Cannot detach document: no context\n"); - return; - } - ctx->document = NULL; - docctx->parent = NULL; - docctx->obj = NULL; + ctx->documents = ucx_list_remove_a(ctx->mempool->allocator, ctx->documents, doc); + ctx->document = ctx->documents ? ctx->documents->data : NULL; - // unbind all vars - UcxMapIterator i = ucx_map_iterator(docctx->vars); - UiVar *var; - UCX_MAP_FOREACH(key, var, i) { - uic_save_var(var); - if(var->from) { - // restore old root bound var val - var->from->value = var->from->orig_val; - var->from->orig_val = NULL; - // copy - uic_copy_binding(var, var->from, FALSE); + UiContext *docctx = ui_document_context(document); + uic_context_unbind_vars(docctx); // unbind all doc/subdoc vars from the parent +} + +void uic_context_detach_all(UiContext *ctx) { + UcxList *ls = ucx_list_clone(ctx->documents, NULL, NULL); + UCX_FOREACH(elm, ls) { + ctx->detach_document2(ctx, elm->data); + } + ucx_list_free(ls); +} + +static UiVar* ctx_getvar(UiContext *ctx, UcxKey key) { + UiVar *var = ucx_map_get(ctx->vars, key); + if(!var) { + UCX_FOREACH(elm, ctx->documents) { + UiContext *subctx = ui_document_context(elm->data); + var = ctx_getvar(subctx, key); + if(var) { + break; + } } - uic_unbind_var(var); } + return var; } UiVar* uic_get_var(UiContext *ctx, char *name) { - // check document variables first - UiVar *var = NULL; - UiContext *doc_ctx = ui_document_context(ctx->document); - if(doc_ctx) { - var = uic_get_var(doc_ctx, name); - } - - // check variables of this context - if(!var) { - var = ucx_map_cstr_get(ctx->vars, name); - } - - return var; + UcxKey key = ucx_key(name, strlen(name)); + return ctx_getvar(ctx, key); } UiVar* uic_create_var(UiContext *ctx, char *name, UiVarType type) { - // check if this context has a var with this name - // otherweise add it to the bound map - UiVar *cv = ucx_map_cstr_get(ctx->vars, name); - if(cv) { - if(cv->type == type) { - return cv; + UiVar *var = uic_get_var(ctx, name); + if(var) { + if(var->type == type) { + return var; } else { - fprintf(stderr, "UiError: var '%s' already exists with different type\n", name); - } - } - - UiVar *var; - if(ctx->bound) { - var = ucx_map_cstr_get(ctx->bound, name); - if(var) { - if(var->type != type) { - fprintf(stderr, "UiError: var '%s' already bound with different type\n", name); - } else { - return var; - } + fprintf(stderr, "UiError: var '%s' already bound with different type\n", name); } } - // create var and add it to the bound map - // this map contains vars that are created by widgets - // the vars can later be moved to subdocuments var = ui_malloc(ctx, sizeof(UiVar)); var->type = type; var->value = uic_create_value(ctx, type); - var->orig_val = NULL; - var->from = NULL; // not connected to a doc var - - if(!ctx->bound) { - ctx->bound = ucx_map_new_a(ctx->mempool->allocator, 16); + var->from = NULL; + var->from_ctx = ctx; + + if(!ctx->vars_unbound) { + ctx->vars_unbound = ucx_map_new_a(ctx->mempool->allocator, 16); } - ucx_map_cstr_put(ctx->bound, name, var); - - // if a subdocument has a variable with this name, we must copy - // the binding to the doc var - UiContext *doc_ctx = ui_document_context(ctx->document); - if(doc_ctx) { - UiVar *docvar = uic_get_var(doc_ctx, name); - if(docvar) { - var->orig_val = var->value; - var->value = docvar->value; - docvar->from = var; - } - } + ucx_map_cstr_put(ctx->vars_unbound, name, var); return var; } @@ -230,11 +217,6 @@ } void uic_copy_binding(UiVar *from, UiVar *to, UiBool copytodoc) { - if(copytodoc && from->from) { - fprintf(stderr, "UiError: var already connected to a document\n"); - return; - } - // check type if(from->type != to->type) { fprintf(stderr, "UI Error: var has incompatible type.\n"); @@ -245,9 +227,7 @@ // update var if(copytodoc) { to->from = from; - - from->orig_val = from->value; - from->value = to->value; + to->from_ctx = from->from_ctx; } // copy binding @@ -311,8 +291,9 @@ } } -void uic_save_var(UiVar *var) { +void uic_save_var2(UiVar *var) { switch(var->type) { + case UI_VAR_SPECIAL: break; case UI_VAR_INTEGER: uic_int_save(var->value); break; case UI_VAR_DOUBLE: uic_double_save(var->value); break; case UI_VAR_STRING: uic_string_save(var->value); break; @@ -334,8 +315,14 @@ } void uic_reg_var(UiContext *ctx, char *name, UiVarType type, void *value) { - UiContext *rootctx = uic_root_context(ctx); - + // TODO: do we need/want this? Why adding vars to a context after + // widgets reference these? Workarounds: + // 1. add vars to ctx before creating ui + // 2. create ui, create new document with vars, attach doc + // also it would be possible to create a function, that scans unbound vars + // and connects them to available vars + /* + UiContext *rootctx = uic_root_context(ctx); UiVar *b = NULL; if(rootctx->bound) { // some widgets are already bound to some vars @@ -350,33 +337,46 @@ } } } + */ // create new var and add it to doc's vars UiVar *var = ui_malloc(ctx, sizeof(UiVar)); - var->from = NULL; - var->orig_val = NULL; var->type = type; var->value = value; + var->from = NULL; + var->from_ctx = ctx; size_t oldcount = ctx->vars->count; ucx_map_cstr_put(ctx->vars, name, var); if(ctx->vars->count != oldcount + 1) { fprintf(stderr, "UiError: var '%s' already exists\n", name); } + // TODO: remove? // a widget is already bound to a var with this name // copy the binding (like uic_context_set_document) + /* if(b) { uic_copy_binding(b, var, TRUE); } + */ } void uic_remove_bound_var(UiContext *ctx, UiVar *var) { // TODO: implement + printf("TODO: implement uic_remove_bound_var\n"); } // public API +void ui_attach_document(UiContext *ctx, void *document) { + uic_context_attach_document(ctx, document); +} + +void ui_detach_document2(UiContext *ctx, void *document) { + uic_context_detach_document2(ctx, document); +} + void ui_context_closefunc(UiContext *ctx, ui_callback fnc, void *udata) { ctx->close_callback = fnc; ctx->close_data = udata;