UNIXworkcode

1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 * 4 * Copyright 2017 Olaf Wintermann. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <string.h> 32 #include <inttypes.h> 33 #include <stdarg.h> 34 35 #include "context.h" 36 #include "../ui/window.h" 37 #include "document.h" 38 #include "types.h" 39 40 static UiContext* global_context; 41 42 void uic_init_global_context(void) { 43 UcxMempool *mp = ucx_mempool_new(32); 44 global_context = uic_context(NULL, mp); 45 } 46 47 UiContext* ui_global_context(void) { 48 return global_context; 49 } 50 51 UiContext* uic_context(UiObject *toplevel, UcxMempool *mp) { 52 UiContext *ctx = ucx_mempool_malloc(mp, sizeof(UiContext)); 53 memset(ctx, 0, sizeof(UiContext)); 54 ctx->mempool = mp; 55 ctx->obj = toplevel; 56 ctx->vars = ucx_map_new_a(mp->allocator, 16); 57 58 ctx->attach_document = uic_context_attach_document; 59 ctx->detach_document2 = uic_context_detach_document2; 60 61 #ifdef UI_GTK 62 if(toplevel && toplevel->widget) { 63 ctx->accel_group = gtk_accel_group_new(); 64 gtk_window_add_accel_group(GTK_WINDOW(toplevel->widget), ctx->accel_group); 65 } 66 #endif 67 68 return ctx; 69 } 70 71 UiContext* uic_root_context(UiContext *ctx) { 72 return ctx->parent ? uic_root_context(ctx->parent) : ctx; 73 } 74 75 void uic_context_attach_document(UiContext *ctx, void *document) { 76 ctx->documents = ucx_list_append_a(ctx->mempool->allocator, ctx->documents, document); 77 ctx->document = ctx->documents->data; 78 79 UiContext *doc_ctx = ui_document_context(document); 80 81 // check if any parent context has an unbound variable with the same name 82 // as any document variable 83 UiContext *var_ctx = ctx; 84 while(var_ctx) { 85 if(var_ctx->vars_unbound && var_ctx->vars_unbound->count > 0) { 86 UcxMapIterator i = ucx_map_iterator(var_ctx->vars_unbound); 87 UiVar *var; 88 // rmkeys holds all keys, that shall be removed from vars_unbound 89 UcxKey *rmkeys = calloc(var_ctx->vars_unbound->count, sizeof(UcxKey)); 90 size_t numkeys = 0; 91 UCX_MAP_FOREACH(key, var, i) { 92 UiVar *docvar = ucx_map_get(doc_ctx->vars, key); 93 if(docvar) { 94 // bind var to document var 95 uic_copy_binding(var, docvar, TRUE); 96 rmkeys[numkeys++] = key; // save the key for removal 97 } 98 } 99 // now that we may have bound some vars to the document, 100 // we can remove them from the unbound map 101 for(size_t k=0;k<numkeys;k++) { 102 ucx_map_remove(var_ctx->vars_unbound, rmkeys[k]); 103 } 104 } 105 106 var_ctx = ctx->parent; 107 } 108 } 109 110 static void uic_context_unbind_vars(UiContext *ctx) { 111 UcxMapIterator i = ucx_map_iterator(ctx->vars); 112 UiVar *var; 113 UCX_MAP_FOREACH(key, var, i) { 114 if(var->from && var->from_ctx) { 115 uic_save_var2(var); 116 uic_copy_binding(var, var->from, FALSE); 117 ucx_map_put(var->from_ctx->vars_unbound, key, var->from); 118 var->from_ctx = ctx; 119 } 120 } 121 122 UCX_FOREACH(elm, ctx->documents) { 123 UiContext *subctx = ui_document_context(elm->data); 124 uic_context_unbind_vars(subctx); 125 } 126 } 127 128 void uic_context_detach_document2(UiContext *ctx, void *document) { 129 // find the document in the documents list 130 UcxList *doc = NULL; 131 UCX_FOREACH(elm, ctx->documents) { 132 if(elm->data == document) { 133 doc = elm; 134 break; 135 } 136 } 137 if(!doc) { 138 return; // document is not a subdocument of this context 139 } 140 141 ctx->documents = ucx_list_remove_a(ctx->mempool->allocator, ctx->documents, doc); 142 ctx->document = ctx->documents ? ctx->documents->data : NULL; 143 144 UiContext *docctx = ui_document_context(document); 145 uic_context_unbind_vars(docctx); // unbind all doc/subdoc vars from the parent 146 } 147 148 void uic_context_detach_all(UiContext *ctx) { 149 UcxList *ls = ucx_list_clone(ctx->documents, NULL, NULL); 150 UCX_FOREACH(elm, ls) { 151 ctx->detach_document2(ctx, elm->data); 152 } 153 ucx_list_free(ls); 154 } 155 156 static UiVar* ctx_getvar(UiContext *ctx, UcxKey key) { 157 UiVar *var = ucx_map_get(ctx->vars, key); 158 if(!var) { 159 UCX_FOREACH(elm, ctx->documents) { 160 UiContext *subctx = ui_document_context(elm->data); 161 var = ctx_getvar(subctx, key); 162 if(var) { 163 break; 164 } 165 } 166 } 167 return var; 168 } 169 170 UiVar* uic_get_var(UiContext *ctx, const char *name) { 171 UcxKey key = ucx_key(name, strlen(name)); 172 return ctx_getvar(ctx, key); 173 } 174 175 UiVar* uic_create_var(UiContext *ctx, const char *name, UiVarType type) { 176 UiVar *var = uic_get_var(ctx, name); 177 if(var) { 178 if(var->type == type) { 179 return var; 180 } else { 181 fprintf(stderr, "UiError: var ''%s'' already bound with different type\n", name); 182 } 183 } 184 185 var = ui_malloc(ctx, sizeof(UiVar)); 186 var->type = type; 187 var->value = uic_create_value(ctx, type); 188 var->from = NULL; 189 var->from_ctx = ctx; 190 191 if(!ctx->vars_unbound) { 192 ctx->vars_unbound = ucx_map_new_a(ctx->mempool->allocator, 16); 193 } 194 ucx_map_cstr_put(ctx->vars_unbound, name, var); 195 196 return var; 197 } 198 199 void* uic_create_value(UiContext *ctx, UiVarType type) { 200 void *val = NULL; 201 switch(type) { 202 case UI_VAR_SPECIAL: break; 203 case UI_VAR_INTEGER: { 204 val = ui_int_new(ctx, NULL); 205 break; 206 } 207 case UI_VAR_DOUBLE: { 208 val = ui_double_new(ctx, NULL); 209 break; 210 } 211 case UI_VAR_STRING: { 212 val = ui_string_new(ctx, NULL); 213 break; 214 } 215 case UI_VAR_TEXT: { 216 val = ui_text_new(ctx, NULL); 217 break; 218 } 219 case UI_VAR_LIST: { 220 val = ui_list_new(ctx, NULL); 221 break; 222 } 223 case UI_VAR_RANGE: { 224 val = ui_range_new(ctx, NULL); 225 break; 226 } 227 } 228 return val; 229 } 230 231 void uic_copy_binding(UiVar *from, UiVar *to, UiBool copytodoc) { 232 // check type 233 if(from->type != to->type) { 234 fprintf(stderr, "UI Error: var has incompatible type.\n"); 235 return; 236 } 237 238 void *fromvalue = from->value; 239 // update var 240 if(copytodoc) { 241 to->from = from; 242 to->from_ctx = from->from_ctx; 243 } 244 245 // copy binding 246 // we don't copy the observer, because the from var has never one 247 switch(from->type) { 248 default: fprintf(stderr, "uic_copy_binding: wtf!\n"); break; 249 case UI_VAR_SPECIAL: break; 250 case UI_VAR_INTEGER: { 251 UiInteger *f = fromvalue; 252 UiInteger *t = to->value; 253 if(!f->obj) break; 254 uic_int_copy(f, t); 255 t->set(t, t->value); 256 break; 257 } 258 case UI_VAR_DOUBLE: { 259 UiDouble *f = fromvalue; 260 UiDouble *t = to->value; 261 if(!f->obj) break; 262 uic_double_copy(f, t); 263 t->set(t, t->value); 264 break; 265 } 266 case UI_VAR_STRING: { 267 UiString *f = fromvalue; 268 UiString *t = to->value; 269 if(!f->obj) break; 270 uic_string_copy(f, t); 271 char *tvalue = t->value.ptr ? t->value.ptr : ""; 272 t->set(t, tvalue); 273 break; 274 } 275 case UI_VAR_TEXT: { 276 UiText *f = fromvalue; 277 UiText *t = to->value; 278 if(!f->obj) break; 279 uic_text_copy(f, t); 280 char *tvalue = t->value.ptr ? t->value.ptr : ""; 281 t->set(t, tvalue); 282 t->setposition(t, t->pos); 283 break; 284 } 285 case UI_VAR_LIST: { 286 UiList *f = fromvalue; 287 UiList *t = to->value; 288 if(!f->obj) break; 289 uic_list_copy(f, t); 290 t->update(t, -1); 291 break; 292 } 293 case UI_VAR_RANGE: { 294 UiRange *f = fromvalue; 295 UiRange *t = to->value; 296 if(!f->obj) break; 297 uic_range_copy(f, t); 298 t->setextent(t, t->extent); 299 t->setrange(t, t->min, t->max); 300 t->set(t, t->value); 301 break; 302 } 303 } 304 } 305 306 void uic_save_var2(UiVar *var) { 307 switch(var->type) { 308 case UI_VAR_SPECIAL: break; 309 case UI_VAR_INTEGER: uic_int_save(var->value); break; 310 case UI_VAR_DOUBLE: uic_double_save(var->value); break; 311 case UI_VAR_STRING: uic_string_save(var->value); break; 312 case UI_VAR_TEXT: uic_text_save(var->value); break; 313 case UI_VAR_LIST: break; 314 case UI_VAR_RANGE: uic_range_save(var->value); break; 315 } 316 } 317 318 void uic_unbind_var(UiVar *var) { 319 switch(var->type) { 320 case UI_VAR_SPECIAL: break; 321 case UI_VAR_INTEGER: uic_int_unbind(var->value); break; 322 case UI_VAR_DOUBLE: uic_double_unbind(var->value); break; 323 case UI_VAR_STRING: uic_string_unbind(var->value); break; 324 case UI_VAR_TEXT: uic_text_unbind(var->value); break; 325 case UI_VAR_LIST: uic_list_unbind(var->value); break; 326 case UI_VAR_RANGE: uic_range_unbind(var->value); break; 327 } 328 } 329 330 void uic_reg_var(UiContext *ctx, char *name, UiVarType type, void *value) { 331 // TODO: do we need/want this? Why adding vars to a context after 332 // widgets reference these? Workarounds: 333 // 1. add vars to ctx before creating ui 334 // 2. create ui, create new document with vars, attach doc 335 // also it would be possible to create a function, that scans unbound vars 336 // and connects them to available vars 337 /* 338 UiContext *rootctx = uic_root_context(ctx); 339 UiVar *b = NULL; 340 if(rootctx->bound) { 341 // some widgets are already bound to some vars 342 b = ucx_map_cstr_get(rootctx->bound, name); 343 if(b) { 344 // a widget is bound to a var with this name 345 // if ctx is the root context we can remove the var from bound 346 // because set_doc or detach can't fuck things up 347 if(ctx == rootctx) { 348 ucx_map_cstr_remove(ctx->bound, name); 349 // TODO: free stuff 350 } 351 } 352 } 353 */ 354 355 // create new var and add it to doc's vars 356 UiVar *var = ui_malloc(ctx, sizeof(UiVar)); 357 var->type = type; 358 var->value = value; 359 var->from = NULL; 360 var->from_ctx = ctx; 361 size_t oldcount = ctx->vars->count; 362 ucx_map_cstr_put(ctx->vars, name, var); 363 if(ctx->vars->count != oldcount + 1) { 364 fprintf(stderr, "UiError: var ''%s'' already exists\n", name); 365 } 366 367 // TODO: remove? 368 // a widget is already bound to a var with this name 369 // copy the binding (like uic_context_set_document) 370 /* 371 if(b) { 372 uic_copy_binding(b, var, TRUE); 373 } 374 */ 375 } 376 377 void uic_remove_bound_var(UiContext *ctx, UiVar *var) { 378 // TODO: implement 379 printf("TODO: implement uic_remove_bound_var\n"); 380 } 381 382 383 // public API 384 385 void ui_attach_document(UiContext *ctx, void *document) { 386 uic_context_attach_document(ctx, document); 387 } 388 389 void ui_detach_document2(UiContext *ctx, void *document) { 390 uic_context_detach_document2(ctx, document); 391 } 392 393 void ui_context_closefunc(UiContext *ctx, ui_callback fnc, void *udata) { 394 ctx->close_callback = fnc; 395 ctx->close_data = udata; 396 } 397 398 399 void ui_set_group(UiContext *ctx, int group) { 400 if(ucx_list_find(ctx->groups, (void*)(intptr_t)group, NULL, NULL) == -1) { 401 ctx->groups = ucx_list_append_a(ctx->mempool->allocator, ctx->groups, (void*)(intptr_t)group); 402 } 403 404 // enable/disable group widgets 405 uic_check_group_widgets(ctx); 406 } 407 408 void ui_unset_group(UiContext *ctx, int group) { 409 int i = ucx_list_find(ctx->groups, (void*)(intptr_t)group, NULL, NULL); 410 if(i != -1) { 411 UcxList *elm = ucx_list_get(ctx->groups, i); 412 ctx->groups = ucx_list_remove_a(ctx->mempool->allocator, ctx->groups, elm); 413 } 414 415 // enable/disable group widgets 416 uic_check_group_widgets(ctx); 417 } 418 419 int* ui_active_groups(UiContext *ctx, int *ngroups) { 420 if(!ctx->groups) { 421 return NULL; 422 } 423 424 int nelm = ucx_list_size(ctx->groups); 425 int *groups = calloc(sizeof(int), nelm); 426 427 int i = 0; 428 UCX_FOREACH(elm, ctx->groups) { 429 groups[i++] = (intptr_t)elm->data; 430 } 431 432 *ngroups = nelm; 433 return groups; 434 } 435 436 void uic_check_group_widgets(UiContext *ctx) { 437 int ngroups = 0; 438 int *groups = ui_active_groups(ctx, &ngroups); 439 440 UCX_FOREACH(elm, ctx->group_widgets) { 441 UiGroupWidget *gw = elm->data; 442 char *check = calloc(1, gw->numgroups); 443 444 for(int i=0;i<ngroups;i++) { 445 for(int k=0;k<gw->numgroups;k++) { 446 if(groups[i] == gw->groups[k]) { 447 check[k] = 1; 448 } 449 } 450 } 451 452 int enable = 1; 453 for(int i=0;i<gw->numgroups;i++) { 454 if(check[i] == 0) { 455 enable = 0; 456 break; 457 } 458 } 459 gw->enable(gw->widget, enable); 460 } 461 462 if(groups) { 463 free(groups); 464 } 465 } 466 467 void ui_widget_set_groups(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, ...) { 468 // get groups 469 UcxList *groups = NULL; 470 va_list ap; 471 va_start(ap, enable); 472 int group; 473 while((group = va_arg(ap, int)) != -1) { 474 groups = ucx_list_append(groups, (void*)(intptr_t)group); 475 } 476 va_end(ap); 477 478 uic_add_group_widget(ctx, widget, enable, groups); 479 480 ucx_list_free(groups); 481 } 482 483 void uic_add_group_widget(UiContext *ctx, void *widget, ui_enablefunc enable, UcxList *groups) { 484 UcxMempool *mp = ctx->mempool; 485 UiGroupWidget *gw = ucx_mempool_malloc(mp, sizeof(UiGroupWidget)); 486 487 gw->widget = widget; 488 gw->enable = enable; 489 gw->numgroups = ucx_list_size(groups); 490 gw->groups = ucx_mempool_calloc(mp, gw->numgroups, sizeof(int)); 491 int i = 0; 492 UCX_FOREACH(elm, groups) { 493 gw->groups[i++] = (intptr_t)elm->data; 494 } 495 496 ctx->group_widgets = ucx_list_append_a( 497 mp->allocator, 498 ctx->group_widgets, 499 gw); 500 } 501 502 void* ui_malloc(UiContext *ctx, size_t size) { 503 return ctx ? ucx_mempool_malloc(ctx->mempool, size) : NULL; 504 } 505 506 void* ui_calloc(UiContext *ctx, size_t nelem, size_t elsize) { 507 return ctx ? ucx_mempool_calloc(ctx->mempool, nelem, elsize) : NULL; 508 } 509 510 void ui_free(UiContext *ctx, void *ptr) { 511 if(ctx) { 512 ucx_mempool_free(ctx->mempool, ptr); 513 } 514 } 515 516 void* ui_realloc(UiContext *ctx, void *ptr, size_t size) { 517 return ctx ? ucx_mempool_realloc(ctx->mempool, ptr, size) : NULL; 518 } 519 520