#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <stdarg.h>
#include "context.h"
#include "../ui/window.h"
#include "document.h"
#include "types.h"
static UiContext* global_context;
void uic_init_global_context(
void) {
UcxMempool *mp = ucx_mempool_new(
32);
global_context = uic_context(
NULL, mp);
}
UiContext* ui_global_context(
void) {
return global_context;
}
UiContext* uic_context(UiObject *toplevel, UcxMempool *mp) {
UiContext *ctx = ucx_mempool_malloc(mp,
sizeof(UiContext));
memset(ctx,
0,
sizeof(UiContext));
ctx->mempool = mp;
ctx->obj = toplevel;
ctx->vars = ucx_map_new_a(mp->allocator,
16);
ctx->attach_document = uic_context_attach_document;
ctx->detach_document2 = uic_context_detach_document2;
#ifdef UI_GTK
if(toplevel && toplevel->widget) {
ctx->accel_group = gtk_accel_group_new();
gtk_window_add_accel_group(
GTK_WINDOW(toplevel->widget), ctx->accel_group);
}
#endif
return ctx;
}
UiContext* uic_root_context(UiContext *ctx) {
return ctx->parent ? uic_root_context(ctx->parent) : ctx;
}
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 *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;
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) {
uic_copy_binding(var, docvar,
TRUE);
rmkeys[numkeys++] = key;
}
}
for(
size_t k=
0;k<numkeys;k++) {
ucx_map_remove(var_ctx->vars_unbound, rmkeys[k]);
}
}
var_ctx = ctx->parent;
}
}
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;
}
}
UCX_FOREACH(elm, ctx->documents) {
UiContext *subctx = ui_document_context(elm->data);
uic_context_unbind_vars(subctx);
}
}
void uic_context_detach_document2(UiContext *ctx,
void *document) {
UcxList *doc =
NULL;
UCX_FOREACH(elm, ctx->documents) {
if(elm->data == document) {
doc = elm;
break;
}
}
if(!doc) {
return;
}
ctx->documents = ucx_list_remove_a(ctx->mempool->allocator, ctx->documents, doc);
ctx->document = ctx->documents ? ctx->documents->data :
NULL;
UiContext *docctx = ui_document_context(document);
uic_context_unbind_vars(docctx);
}
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;
}
}
}
return var;
}
UiVar* uic_get_var(UiContext *ctx,
const char *name) {
UcxKey key = ucx_key(name, strlen(name));
return ctx_getvar(ctx, key);
}
UiVar* uic_create_var(UiContext *ctx,
const char *name, UiVarType type) {
UiVar *var = uic_get_var(ctx, name);
if(var) {
if(var->type == type) {
return var;
}
else {
fprintf(stderr,
"UiError: var ''%s'' already bound with different type\n", name);
}
}
var = ui_malloc(ctx,
sizeof(UiVar));
var->type = type;
var->value = uic_create_value(ctx, type);
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->vars_unbound, name, var);
return var;
}
void* uic_create_value(UiContext *ctx, UiVarType type) {
void *val =
NULL;
switch(type) {
case UI_VAR_SPECIAL:
break;
case UI_VAR_INTEGER: {
val = ui_int_new(ctx,
NULL);
break;
}
case UI_VAR_DOUBLE: {
val = ui_double_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 = ui_list_new(ctx,
NULL);
break;
}
case UI_VAR_RANGE: {
val = ui_range_new(ctx,
NULL);
break;
}
}
return val;
}
void uic_copy_binding(UiVar *from, UiVar *to, UiBool copytodoc) {
if(from->type != to->type) {
fprintf(stderr,
"UI Error: var has incompatible type.\n");
return;
}
void *fromvalue = from->value;
if(copytodoc) {
to->from = from;
to->from_ctx = from->from_ctx;
}
switch(from->type) {
default: fprintf(stderr,
"uic_copy_binding: wtf!\n");
break;
case UI_VAR_SPECIAL:
break;
case UI_VAR_INTEGER: {
UiInteger *f = fromvalue;
UiInteger *t = to->value;
if(!f->obj)
break;
uic_int_copy(f, t);
t->set(t, t->value);
break;
}
case UI_VAR_DOUBLE: {
UiDouble *f = fromvalue;
UiDouble *t = to->value;
if(!f->obj)
break;
uic_double_copy(f, t);
t->set(t, t->value);
break;
}
case UI_VAR_STRING: {
UiString *f = fromvalue;
UiString *t = to->value;
if(!f->obj)
break;
uic_string_copy(f, t);
char *tvalue = t->value.ptr ? t->value.ptr :
"";
t->set(t, tvalue);
break;
}
case UI_VAR_TEXT: {
UiText *f = fromvalue;
UiText *t = to->value;
if(!f->obj)
break;
uic_text_copy(f, t);
char *tvalue = t->value.ptr ? t->value.ptr :
"";
t->set(t, tvalue);
t->setposition(t, t->pos);
break;
}
case UI_VAR_LIST: {
UiList *f = fromvalue;
UiList *t = to->value;
if(!f->obj)
break;
uic_list_copy(f, t);
t->update(t, -
1);
break;
}
case UI_VAR_RANGE: {
UiRange *f = fromvalue;
UiRange *t = to->value;
if(!f->obj)
break;
uic_range_copy(f, t);
t->setextent(t, t->extent);
t->setrange(t, t->min, t->max);
t->set(t, t->value);
break;
}
}
}
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;
case UI_VAR_TEXT: uic_text_save(var->value);
break;
case UI_VAR_LIST:
break;
case UI_VAR_RANGE: uic_range_save(var->value);
break;
}
}
void uic_unbind_var(UiVar *var) {
switch(var->type) {
case UI_VAR_SPECIAL:
break;
case UI_VAR_INTEGER: uic_int_unbind(var->value);
break;
case UI_VAR_DOUBLE: uic_double_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, UiVarType type,
void *value) {
UiVar *var = ui_malloc(ctx,
sizeof(UiVar));
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);
}
}
void uic_remove_bound_var(UiContext *ctx, UiVar *var) {
printf(
"TODO: implement uic_remove_bound_var\n");
}
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;
}
void ui_set_group(UiContext *ctx,
int group) {
if(ucx_list_find(ctx->groups, (
void*)(
intptr_t)group,
NULL,
NULL) == -
1) {
ctx->groups = ucx_list_append_a(ctx->mempool->allocator, ctx->groups, (
void*)(
intptr_t)group);
}
uic_check_group_widgets(ctx);
}
void ui_unset_group(UiContext *ctx,
int group) {
int i = ucx_list_find(ctx->groups, (
void*)(
intptr_t)group,
NULL,
NULL);
if(i != -
1) {
UcxList *elm = ucx_list_get(ctx->groups, i);
ctx->groups = ucx_list_remove_a(ctx->mempool->allocator, ctx->groups, elm);
}
uic_check_group_widgets(ctx);
}
int* ui_active_groups(UiContext *ctx,
int *ngroups) {
if(!ctx->groups) {
return NULL;
}
int nelm = ucx_list_size(ctx->groups);
int *groups = calloc(
sizeof(
int), nelm);
int i =
0;
UCX_FOREACH(elm, ctx->groups) {
groups[i++] = (
intptr_t)elm->data;
}
*ngroups = nelm;
return groups;
}
void uic_check_group_widgets(UiContext *ctx) {
int ngroups =
0;
int *groups = ui_active_groups(ctx, &ngroups);
UCX_FOREACH(elm, ctx->group_widgets) {
UiGroupWidget *gw = elm->data;
char *check = calloc(
1, gw->numgroups);
for(
int i=
0;i<ngroups;i++) {
for(
int k=
0;k<gw->numgroups;k++) {
if(groups[i] == gw->groups[k]) {
check[k] =
1;
}
}
}
int enable =
1;
for(
int i=
0;i<gw->numgroups;i++) {
if(check[i] ==
0) {
enable =
0;
break;
}
}
gw->enable(gw->widget, enable);
}
if(groups) {
free(groups);
}
}
void ui_widget_set_groups(UiContext *ctx,
UIWIDGET widget, ui_enablefunc enable, ...) {
UcxList *groups =
NULL;
va_list ap;
va_start(ap, enable);
int group;
while((group = va_arg(ap,
int)) != -
1) {
groups = ucx_list_append(groups, (
void*)(
intptr_t)group);
}
va_end(ap);
uic_add_group_widget(ctx, widget, enable, groups);
ucx_list_free(groups);
}
void uic_add_group_widget(UiContext *ctx,
void *widget, ui_enablefunc enable, UcxList *groups) {
UcxMempool *mp = ctx->mempool;
UiGroupWidget *gw = ucx_mempool_malloc(mp,
sizeof(UiGroupWidget));
gw->widget = widget;
gw->enable = enable;
gw->numgroups = ucx_list_size(groups);
gw->groups = ucx_mempool_calloc(mp, gw->numgroups,
sizeof(
int));
int i =
0;
UCX_FOREACH(elm, groups) {
gw->groups[i++] = (
intptr_t)elm->data;
}
ctx->group_widgets = ucx_list_append_a(
mp->allocator,
ctx->group_widgets,
gw);
}
void* ui_malloc(UiContext *ctx,
size_t size) {
return ctx ? ucx_mempool_malloc(ctx->mempool, size) :
NULL;
}
void* ui_calloc(UiContext *ctx,
size_t nelem,
size_t elsize) {
return ctx ? ucx_mempool_calloc(ctx->mempool, nelem, elsize) :
NULL;
}
void ui_free(UiContext *ctx,
void *ptr) {
if(ctx) {
ucx_mempool_free(ctx->mempool, ptr);
}
}
void* ui_realloc(UiContext *ctx,
void *ptr,
size_t size) {
return ctx ? ucx_mempool_realloc(ctx->mempool, ptr, size) :
NULL;
}