--- a/ui/common/context.c Tue Jan 24 18:46:47 2017 +0100 +++ b/ui/common/context.c Fri Nov 10 17:17:14 2017 +0100 @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2014 Olaf Wintermann. All rights reserved. + * Copyright 2017 Olaf Wintermann. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -34,6 +34,7 @@ #include "context.h" #include "../ui/window.h" #include "document.h" +#include "types.h" UiContext* uic_context(UiObject *toplevel, UcxMempool *mp) { @@ -64,85 +65,67 @@ if(ctx->document == document) { return; } + if(ctx->document) { + uic_context_detach_document(ctx); + } + ctx->document = document; UiContext *docctx = ui_document_context(document); if(!docctx) { + fprintf(stderr, "UiError: uic_context_set_document: pointer is not a document\n"); return; } docctx->obj = ctx->obj; docctx->parent = ctx; - if(ctx->document) { - uic_context_detach_document(ctx, ctx->document); + UiContext *root = uic_root_context(ctx); + if(!root->bound || root->bound->count == 0) { + return; } - //obj->document = document; - ctx->document = document; + // 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(uic_root_context(ctx)->vars, key); + UiVar *v = ucx_map_get(root->bound, key); if(v) { - if(v->isextern) { - fprintf( - stderr, - "UI Error: external variable cannot be moved\n"); - return; - } - // check type - if(var->type != v->type) { - fprintf(stderr, "UI Error: var has incompatible type.\n"); - return; - } - - // copy value - uic_move_var(v, var, TRUE); - var->from = v->from; - - // TODO: free var struct - ucx_map_remove(ctx->vars, key); + // 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); } } } -void uic_context_detach_document(UiContext *ctx, void *document) { - UiContext *docctx = ui_document_context(document); - if(!docctx) { - fprintf( - stderr, - "UiError: ui_detach_document: document is not registered\n"); - return; - } - if(ctx->document != document) { - fprintf(stderr, "UiError: ui_detach_document: wrong document\n"); +void uic_context_detach_document(UiContext *ctx) { + if(!ctx->document) { return; } + 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; + + // unbind all vars UcxMapIterator i = ucx_map_iterator(docctx->vars); UiVar *var; UCX_MAP_FOREACH(key, var, i) { - if(var->from && var->from != docctx->vars) { - // this var is bind to an outer widget, so we move it - UcxAllocator *a = var->from->allocator; - UiVar *newvar = a->malloc(a->pool, sizeof(UiVar)); - newvar->value = uic_create_value(a, var->type); - uic_move_var(var, newvar, 0); - newvar->type = var->type; - newvar->from = var->from; - newvar->isextern = 0; - - ucx_map_put(var->from, key, newvar); - - //ucx_map_remove(doc->vars, key); // TODO: dont remove! + 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); } + uic_unbind_var(var); } - - if(docctx->parent) { - docctx->parent->document = NULL; - } - - docctx->obj = NULL; - docctx->parent = NULL; } UiVar* uic_get_var(UiContext *ctx, char *name) { @@ -161,185 +144,207 @@ return var; } -UiVar* uic_connect_var(UiContext *ctx, char *name, int type) { - // TODO: get current map (Document Container, Tabbed Pane) - UcxMap *from = ctx->vars; +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) { + return cv; // I'm not sure if this can actually happen, lol + } + + // 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 + UiVar *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 - UiVar *var = uic_get_var(ctx, name); - if(var) { - // the value is registered - - // a little type check - if(var->type != type) { - fprintf(stderr, "UI Error: var %s has incompatible type.\n", name); - return NULL; - } else { - // register the current document/wdata map - // if the document is closed, the var will be moved to this map - var->from = from; - - return var; + if(!ctx->bound) { + ctx->bound = ucx_map_new_a(ctx->mempool->allocator, 16); + } + size_t oldcount = ctx->bound->count; + ucx_map_cstr_put(ctx->bound, name, var); + if(ctx->bound->count != oldcount + 1) { + fprintf(stderr, "UiError: var '%s' already bound\n", name); + } + + // 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; } - } else { - // create an empty value and add it to the context variables - // it can be moved to the document vars later - void *value = uic_create_value(ctx->mempool->allocator, type); - if(!value) { - fprintf(stderr, "UI Error: Cannot create empty value.\n"); - return NULL; - } - UiVar *var = ucx_mempool_malloc(ctx->mempool, sizeof(UiVar)); - var->value = value; - var->type = type; - var->isextern = 0; - var->from = from; - ucx_map_cstr_put(ctx->vars, name, var); - return var; } + + return var; } -void uic_move_var(UiVar *from, UiVar *to, UiBool set) { +void* uic_create_value(UiContext *ctx, UiVarType type) { + void *val = NULL; + switch(type) { + case UI_VAR_INTEGER: { + val = ui_int_new(ctx, NULL); + break; + } + case UI_VAR_STRING: { + val = ui_string_new(ctx, NULL); + break; + } + case UI_VAR_TEXT: { + val = ui_text_new(ctx, NULL); + break; + } + case UI_VAR_LIST: { + val = NULL; // TODO + break; + } + case UI_VAR_RANGE: { + val = NULL; // TODO + break; + } + } + return val; +} + +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"); + return; + } + + // copy binding + // we don't copy the observer, because the from var has never one switch(from->type) { + default: fprintf(stderr, "uic_copy_binding: wtf!\n"); break; + case UI_VAR_SPECIAL: break; case UI_VAR_INTEGER: { - //memcpy(to->value, from->value, sizeof(UiInteger)); UiInteger *f = from->value; UiInteger *t = to->value; - t->get = f->get; - t->set = f->set; - t->obj = f->obj; - if(set) { + if(!copytodoc) { + f->value = f->get(f); + } + uic_int_copy(f, t); + if(t->value != 0) { t->set(t, t->value); - } else { - //t->value = t->get(t); - f->value = f->get(f); - //t->value = 0; } break; } case UI_VAR_STRING: { - // TODO + UiString *f = from->value; + UiString *t = to->value; + uic_string_copy(f, t); + if(!copytodoc) { + f->value = f->get(f); + } else { + char *tvalue = t->value ? t->value : ""; + t->set(t, tvalue); + } + break; } case UI_VAR_TEXT: { UiText *f = from->value; UiText *t = to->value; - char *tvalue = t->value ? t->value : ""; - int tpos = t->pos; - memcpy(t, f, sizeof(UiText)); - if(set) { - t->set(t, tvalue); - t->setposition(t, tpos); + uic_text_copy(f, t); + if(!copytodoc) { + f->value = f->get(f); } else { - f->value = f->get(f); - f->pos = f->position(f); - f->set = NULL; - f->get = NULL; - f->setposition = NULL; - f->position = NULL; + char *tvalue = t->value ? t->value : ""; + t->set(t, tvalue); + t->setposition(t, t->pos); } break; } case UI_VAR_LIST: { - UiListVar *f = from->value; - UiListVar *t = to->value; - UiList *list = t->listptr->list; - UiObserver *observers = f->listptr->list->observers; - t->listptr = f->listptr; - if(set) { - t->listptr->list = list; - list->observers = observers; - ui_notify(observers, list); + UiList *f = from->value; + UiList *t = to->value; + uic_list_copy(f, t); + t->update(t, -1); + } + case UI_VAR_RANGE: { + UiRange *f = from->value; + UiRange *t = to->value; + if(!copytodoc) { + f->value = f->get(f); } - break; + uic_range_copy(f, t); + t->setextent(t, t->extent); + t->setrange(t, t->min, t->max); + t->set(t, t->value); } } + + if(copytodoc) { + to->from = from; + + from->orig_val = from->value; + from->value = to->value; + } +} + +void uic_unbind_var(UiVar *var) { + switch(var->type) { + case UI_VAR_INTEGER: uic_int_unbind(var->value); break; + case UI_VAR_STRING: uic_string_unbind(var->value); break; + case UI_VAR_TEXT: uic_text_unbind(var->value); break; + case UI_VAR_LIST: uic_list_unbind(var->value); break; + case UI_VAR_RANGE: uic_range_unbind(var->value); break; + } } -void uic_reg_var(UiContext *ctx, char *name, int type, size_t vs, void *value) { - UiVar *newvar = ucx_mempool_malloc(ctx->mempool, sizeof(UiVar)); - newvar->isextern = 1; - newvar->type = type; - newvar->value = value; - newvar->from = NULL; +void uic_reg_var(UiContext *ctx, char *name, UiVarType type, void *value) { + UiContext *rootctx = uic_root_context(ctx); + + UiVar *b = NULL; + if(rootctx->bound) { + // some widgets are already bound to some vars + b = ucx_map_cstr_get(rootctx->bound, name); + if(b) { + // a widget is bound to a var with this name + // if ctx is the root context we can remove the var from bound + // because set_doc or detach can't fuck things up + if(ctx == rootctx) { + ucx_map_cstr_remove(ctx->bound, name); + // TODO: free stuff + } + } + } - uic_add_var(ctx, name, newvar, type, vs); + // 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; + 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); + } + + // 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_add_var( - UiContext *ctx, - char *name, - UiVar *newvar, - int type, - size_t vs) -{ - // if a parent context has a variable with this name, we remove it and put - // it to this context - UiVar *var = ctx->obj ? uic_get_var(ctx->obj->ctx, name) : NULL; - - if(var && var->from != ctx->vars) { - // external variables cannot be moved - if(var->isextern) { - fprintf( - stderr, - "UI Error: external variable %s " - "cannot be moved to the document.\n", - name); - return; - } - - // check type - if(var->type != type) { - fprintf(stderr, "UI Error: var %s has incompatible type.\n", name); - return; - } - - // override document var with window context var - memcpy(newvar->value, var->value, vs); - - newvar->from = var->from; - - // TODO: free var struct - ucx_map_cstr_remove(var->from, name); - } - - // finally, add the new variable to the document - ucx_map_cstr_put(ctx->vars, name, newvar); +void uic_remove_bound_var(UiContext *ctx, UiVar *var) { + // TODO: implement } -void* uic_create_value(UcxAllocator *a, int type) { - switch(type) { - case UI_VAR_INTEGER: { - UiInteger *i = a->calloc( - a->pool, - 1, - sizeof(UiInteger)); - return i; - } - case UI_VAR_STRING: { - UiString *s = a->calloc( - a->pool, - 1, - sizeof(UiInteger)); - return s; - } - case UI_VAR_TEXT: { - UiText *t = a->calloc( - a->pool, - 1, - sizeof(UiText)); - return t; - } - case UI_VAR_LIST: { - UiListVar *l = a->malloc(a->pool, sizeof(UiListVar)); - UiListPtr *lp = a->malloc(a->pool, sizeof(UiListPtr)); - l->listptr = lp; - lp->list = NULL; - // TODO: create empty list - return l; - } - } - return NULL; -} // public API @@ -348,81 +353,6 @@ ctx->close_data = udata; } -int ui_getint(UiObject *obj, char *name) { - UiVar *var = uic_get_var(obj->ctx, name); - if(var) { - if(var->type == UI_VAR_INTEGER) { - UiInteger *i = var->value; - if(i->get) { - return i->get(i); - } else { - fprintf( - stderr, - "UI Error: variable %s is not connected.\n", - name); - } - } else { - fprintf( - stderr, - "UI Error: requested variable %s is not an integer.\n", - name); - } - } else { - fprintf(stderr, "UI Error: unkown variable %s.\n", name); - } - return 0; -} - -char* ui_getstr(UiObject *obj, char *name) { - UiVar *var = uic_get_var(obj->ctx, name); - if(var) { - if(var->type == UI_VAR_STRING) { - UiString *s = var->value; - if(s->get) { - return s->get(s); - } else { - fprintf( - stderr, - "UI Error: variable %s is not connected.\n", - name); - } - } else { - fprintf( - stderr, - "UI Error: requested variable %s is not an string.\n", - name); - } - } else { - fprintf(stderr, "UI Error: unkown variable %s.\n", name); - } - return NULL; -} - -char* ui_gettext(UiObject *obj, char *name) { - UiVar *var = uic_get_var(obj->ctx, name); - if(var) { - if(var->type == UI_VAR_TEXT) { - UiText *s = var->value; - if(s->get) { - return s->get(s); - } else { - fprintf( - stderr, - "UI Error: variable %s is not connected.\n", - name); - } - } else { - fprintf( - stderr, - "UI Error: requested variable %s is not a text.\n", - name); - } - } else { - fprintf(stderr, "UI Error: unkown variable %s.\n", name); - } - return NULL; -} - void ui_set_group(UiContext *ctx, int group) {