30 #include <stdlib.h> |
30 #include <stdlib.h> |
31 #include <string.h> |
31 #include <string.h> |
32 #include <inttypes.h> |
32 #include <inttypes.h> |
33 #include <stdarg.h> |
33 #include <stdarg.h> |
34 |
34 |
|
35 #include <cx/array_list.h> |
|
36 #include <cx/compare.h> |
|
37 #include <cx/mempool.h> |
|
38 |
35 #include "context.h" |
39 #include "context.h" |
36 #include "../ui/window.h" |
40 #include "../ui/window.h" |
37 #include "document.h" |
41 #include "document.h" |
38 #include "types.h" |
42 #include "types.h" |
39 |
43 |
|
44 |
40 static UiContext* global_context; |
45 static UiContext* global_context; |
41 |
46 |
42 void uic_init_global_context(void) { |
47 void uic_init_global_context(void) { |
43 UcxMempool *mp = ucx_mempool_new(32); |
48 CxMempool *mp = cxBasicMempoolCreate(32); |
44 global_context = uic_context(NULL, mp); |
49 global_context = uic_context(NULL, mp); |
45 } |
50 } |
46 |
51 |
47 UiContext* ui_global_context(void) { |
52 UiContext* ui_global_context(void) { |
48 return global_context; |
53 return global_context; |
49 } |
54 } |
50 |
55 |
51 UiContext* uic_context(UiObject *toplevel, UcxMempool *mp) { |
56 UiContext* uic_context(UiObject *toplevel, CxMempool *mp) { |
52 UiContext *ctx = ucx_mempool_malloc(mp, sizeof(UiContext)); |
57 UiContext *ctx = cxMalloc(mp->allocator, sizeof(UiContext)); |
53 memset(ctx, 0, sizeof(UiContext)); |
58 memset(ctx, 0, sizeof(UiContext)); |
54 ctx->mempool = mp; |
59 ctx->mp = mp; |
|
60 ctx->allocator = mp->allocator; |
55 ctx->obj = toplevel; |
61 ctx->obj = toplevel; |
56 ctx->vars = ucx_map_new_a(mp->allocator, 16); |
62 ctx->vars = cxHashMapCreate(mp->allocator, CX_STORE_POINTERS, 16); |
|
63 |
|
64 ctx->documents = cxLinkedListCreate(mp->allocator, cx_cmp_intptr, CX_STORE_POINTERS); |
|
65 ctx->group_widgets = cxLinkedListCreate(mp->allocator, cx_cmp_ptr, sizeof(UiGroupWidget)); |
|
66 ctx->groups = cxArrayListCreate(mp->allocator, cx_cmp_int, sizeof(int), 32); |
57 |
67 |
58 ctx->attach_document = uic_context_attach_document; |
68 ctx->attach_document = uic_context_attach_document; |
59 ctx->detach_document2 = uic_context_detach_document2; |
69 ctx->detach_document2 = uic_context_detach_document2; |
60 |
70 |
61 #ifdef UI_GTK |
71 #if UI_GTK2 || UI_GTK3 |
62 if(toplevel && toplevel->widget) { |
72 if(toplevel && toplevel->widget) { |
63 ctx->accel_group = gtk_accel_group_new(); |
73 ctx->accel_group = gtk_accel_group_new(); |
64 gtk_window_add_accel_group(GTK_WINDOW(toplevel->widget), ctx->accel_group); |
74 gtk_window_add_accel_group(GTK_WINDOW(toplevel->widget), ctx->accel_group); |
65 } |
75 } |
66 #endif |
76 #endif |
70 |
80 |
71 UiContext* uic_root_context(UiContext *ctx) { |
81 UiContext* uic_root_context(UiContext *ctx) { |
72 return ctx->parent ? uic_root_context(ctx->parent) : ctx; |
82 return ctx->parent ? uic_root_context(ctx->parent) : ctx; |
73 } |
83 } |
74 |
84 |
|
85 void uic_context_prepare_close(UiContext *ctx) { |
|
86 cxListClear(ctx->groups); |
|
87 cxListClear(ctx->group_widgets); |
|
88 } |
|
89 |
75 void uic_context_attach_document(UiContext *ctx, void *document) { |
90 void uic_context_attach_document(UiContext *ctx, void *document) { |
76 ctx->documents = ucx_list_append_a(ctx->mempool->allocator, ctx->documents, document); |
91 cxListAdd(ctx->documents, document); |
77 ctx->document = ctx->documents->data; |
92 ctx->document = document; |
78 |
93 |
79 UiContext *doc_ctx = ui_document_context(document); |
94 UiContext *doc_ctx = ui_document_context(document); |
80 |
95 |
81 // check if any parent context has an unbound variable with the same name |
96 // check if any parent context has an unbound variable with the same name |
82 // as any document variable |
97 // as any document variable |
83 UiContext *var_ctx = ctx; |
98 UiContext *var_ctx = ctx; |
84 while(var_ctx) { |
99 while(var_ctx) { |
85 if(var_ctx->vars_unbound && var_ctx->vars_unbound->count > 0) { |
100 if(var_ctx->vars_unbound && cxMapSize(var_ctx->vars_unbound) > 0) { |
86 UcxMapIterator i = ucx_map_iterator(var_ctx->vars_unbound); |
101 CxIterator i = cxMapIterator(var_ctx->vars_unbound); |
87 UiVar *var; |
102 cx_foreach(CxMapEntry*, entry, i) { |
88 // rmkeys holds all keys, that shall be removed from vars_unbound |
103 printf("attach %s\n", entry->key->data); |
89 UcxKey *rmkeys = calloc(var_ctx->vars_unbound->count, sizeof(UcxKey)); |
104 UiVar *var = entry->value; |
90 size_t numkeys = 0; |
105 UiVar *docvar = cxMapGet(doc_ctx->vars, *entry->key); |
91 UCX_MAP_FOREACH(key, var, i) { |
|
92 UiVar *docvar = ucx_map_get(doc_ctx->vars, key); |
|
93 if(docvar) { |
106 if(docvar) { |
94 // bind var to document var |
107 // bind var to document var |
95 uic_copy_binding(var, docvar, TRUE); |
108 uic_copy_binding(var, docvar, TRUE); |
96 rmkeys[numkeys++] = key; // save the key for removal |
109 cxIteratorFlagRemoval(i); |
97 } |
110 } |
98 } |
111 } |
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 } |
112 } |
105 |
113 |
106 var_ctx = ctx->parent; |
114 var_ctx = ctx->parent; |
107 } |
115 } |
108 } |
116 } |
109 |
117 |
110 static void uic_context_unbind_vars(UiContext *ctx) { |
118 static void uic_context_unbind_vars(UiContext *ctx) { |
111 UcxMapIterator i = ucx_map_iterator(ctx->vars); |
119 CxIterator i = cxMapIterator(ctx->vars); |
112 UiVar *var; |
120 cx_foreach(CxMapEntry*, entry, i) { |
113 UCX_MAP_FOREACH(key, var, i) { |
121 UiVar *var = entry->value; |
114 if(var->from && var->from_ctx) { |
122 if(var->from && var->from_ctx) { |
115 uic_save_var2(var); |
123 uic_save_var2(var); |
116 uic_copy_binding(var, var->from, FALSE); |
124 uic_copy_binding(var, var->from, FALSE); |
117 ucx_map_put(var->from_ctx->vars_unbound, key, var->from); |
125 cxMapPut(var->from_ctx->vars_unbound, *entry->key, var->from); |
118 var->from_ctx = ctx; |
126 var->from_ctx = ctx; |
119 } |
127 } |
120 } |
128 } |
121 |
129 |
122 UCX_FOREACH(elm, ctx->documents) { |
130 if(ctx->documents) { |
123 UiContext *subctx = ui_document_context(elm->data); |
131 i = cxListIterator(ctx->documents); |
124 uic_context_unbind_vars(subctx); |
132 cx_foreach(void *, doc, i) { |
|
133 UiContext *subctx = ui_document_context(doc); |
|
134 uic_context_unbind_vars(subctx); |
|
135 } |
125 } |
136 } |
126 } |
137 } |
127 |
138 |
128 void uic_context_detach_document2(UiContext *ctx, void *document) { |
139 void uic_context_detach_document2(UiContext *ctx, void *document) { |
129 // find the document in the documents list |
140 // find the document in the documents list |
130 UcxList *doc = NULL; |
141 ssize_t docIndex = cxListFind(ctx->documents, document); |
131 UCX_FOREACH(elm, ctx->documents) { |
142 if(docIndex < 0) { |
132 if(elm->data == document) { |
143 return; |
133 doc = elm; |
144 } |
134 break; |
145 |
135 } |
146 cxListRemove(ctx->documents, docIndex); |
136 } |
147 ctx->document = cxListAt(ctx->documents, 0); |
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 |
148 |
144 UiContext *docctx = ui_document_context(document); |
149 UiContext *docctx = ui_document_context(document); |
145 uic_context_unbind_vars(docctx); // unbind all doc/subdoc vars from the parent |
150 uic_context_unbind_vars(docctx); // unbind all doc/subdoc vars from the parent |
146 } |
151 } |
147 |
152 |
148 void uic_context_detach_all(UiContext *ctx) { |
153 void uic_context_detach_all(UiContext *ctx) { |
149 UcxList *ls = ucx_list_clone(ctx->documents, NULL, NULL); |
154 // copy list |
150 UCX_FOREACH(elm, ls) { |
155 CxList *ls = cxLinkedListCreate(cxDefaultAllocator, NULL, CX_STORE_POINTERS); |
151 ctx->detach_document2(ctx, elm->data); |
156 CxIterator i = cxListIterator(ctx->documents); |
152 } |
157 cx_foreach(void *, doc, i) { |
153 ucx_list_free(ls); |
158 cxListAdd(ls, doc); |
154 } |
159 } |
155 |
160 |
156 static UiVar* ctx_getvar(UiContext *ctx, UcxKey key) { |
161 // detach documents |
157 UiVar *var = ucx_map_get(ctx->vars, key); |
162 i = cxListIterator(ls); |
158 if(!var) { |
163 cx_foreach(void *, doc, i) { |
159 UCX_FOREACH(elm, ctx->documents) { |
164 ctx->detach_document2(ctx, doc); |
160 UiContext *subctx = ui_document_context(elm->data); |
165 } |
|
166 |
|
167 cxListDestroy(ls); |
|
168 } |
|
169 |
|
170 static UiVar* ctx_getvar(UiContext *ctx, CxHashKey key) { |
|
171 UiVar *var = cxMapGet(ctx->vars, key); |
|
172 if(!var && ctx->documents) { |
|
173 CxIterator i = cxListIterator(ctx->documents); |
|
174 cx_foreach(void *, doc, i) { |
|
175 UiContext *subctx = ui_document_context(doc); |
161 var = ctx_getvar(subctx, key); |
176 var = ctx_getvar(subctx, key); |
162 if(var) { |
177 if(var) { |
163 break; |
178 break; |
164 } |
179 } |
165 } |
180 } |
166 } |
181 } |
167 return var; |
182 return var; |
168 } |
183 } |
169 |
184 |
170 UiVar* uic_get_var(UiContext *ctx, const char *name) { |
185 UiVar* uic_get_var(UiContext *ctx, const char *name) { |
171 UcxKey key = ucx_key(name, strlen(name)); |
186 CxHashKey key = cx_hash_key(name, strlen(name)); |
172 return ctx_getvar(ctx, key); |
187 return ctx_getvar(ctx, key); |
173 } |
188 } |
174 |
189 |
175 UiVar* uic_create_var(UiContext *ctx, const char *name, UiVarType type) { |
190 UiVar* uic_create_var(UiContext *ctx, const char *name, UiVarType type) { |
|
191 if(ctx->vars_unbound) { |
|
192 UiVar *unbound = cxMapGet(ctx->vars_unbound, name); |
|
193 if(unbound) { |
|
194 return unbound; |
|
195 } |
|
196 } |
|
197 |
176 UiVar *var = uic_get_var(ctx, name); |
198 UiVar *var = uic_get_var(ctx, name); |
177 if(var) { |
199 if(var) { |
178 if(var->type == type) { |
200 if(var->type == type) { |
179 return var; |
201 return var; |
180 } else { |
202 } else { |
393 void ui_context_closefunc(UiContext *ctx, ui_callback fnc, void *udata) { |
463 void ui_context_closefunc(UiContext *ctx, ui_callback fnc, void *udata) { |
394 ctx->close_callback = fnc; |
464 ctx->close_callback = fnc; |
395 ctx->close_data = udata; |
465 ctx->close_data = udata; |
396 } |
466 } |
397 |
467 |
|
468 UIEXPORT void ui_context_destroy(UiContext *ctx) { |
|
469 cxMempoolDestroy(ctx->mp); |
|
470 } |
|
471 |
398 |
472 |
399 void ui_set_group(UiContext *ctx, int group) { |
473 void ui_set_group(UiContext *ctx, int group) { |
400 if(ucx_list_find(ctx->groups, (void*)(intptr_t)group, NULL, NULL) == -1) { |
474 if(cxListFind(ctx->groups, &group) == -1) { |
401 ctx->groups = ucx_list_append_a(ctx->mempool->allocator, ctx->groups, (void*)(intptr_t)group); |
475 cxListAdd(ctx->groups, &group); |
402 } |
476 } |
403 |
477 |
404 // enable/disable group widgets |
478 // enable/disable group widgets |
405 uic_check_group_widgets(ctx); |
479 uic_check_group_widgets(ctx); |
406 } |
480 } |
407 |
481 |
408 void ui_unset_group(UiContext *ctx, int group) { |
482 void ui_unset_group(UiContext *ctx, int group) { |
409 int i = ucx_list_find(ctx->groups, (void*)(intptr_t)group, NULL, NULL); |
483 int i = cxListFind(ctx->groups, &group); |
410 if(i != -1) { |
484 if(i != -1) { |
411 UcxList *elm = ucx_list_get(ctx->groups, i); |
485 cxListRemove(ctx->groups, i); |
412 ctx->groups = ucx_list_remove_a(ctx->mempool->allocator, ctx->groups, elm); |
|
413 } |
486 } |
414 |
487 |
415 // enable/disable group widgets |
488 // enable/disable group widgets |
416 uic_check_group_widgets(ctx); |
489 uic_check_group_widgets(ctx); |
417 } |
490 } |
418 |
491 |
419 int* ui_active_groups(UiContext *ctx, int *ngroups) { |
492 int* ui_active_groups(UiContext *ctx, int *ngroups) { |
420 if(!ctx->groups) { |
493 *ngroups = cxListSize(ctx->groups); |
421 return NULL; |
494 return cxListAt(ctx->groups, 0); |
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 } |
495 } |
435 |
496 |
436 void uic_check_group_widgets(UiContext *ctx) { |
497 void uic_check_group_widgets(UiContext *ctx) { |
437 int ngroups = 0; |
498 int ngroups = 0; |
438 int *groups = ui_active_groups(ctx, &ngroups); |
499 int *groups = ui_active_groups(ctx, &ngroups); |
439 |
500 |
440 UCX_FOREACH(elm, ctx->group_widgets) { |
501 CxIterator i = cxListIterator(ctx->group_widgets); |
441 UiGroupWidget *gw = elm->data; |
502 cx_foreach(UiGroupWidget *, gw, i) { |
442 char *check = calloc(1, gw->numgroups); |
503 char *check = calloc(1, gw->numgroups); |
443 |
504 |
444 for(int i=0;i<ngroups;i++) { |
505 for(int i=0;i<ngroups;i++) { |
445 for(int k=0;k<gw->numgroups;k++) { |
506 for(int k=0;k<gw->numgroups;k++) { |
446 if(groups[i] == gw->groups[k]) { |
507 if(groups[i] == gw->groups[k]) { |
454 if(check[i] == 0) { |
515 if(check[i] == 0) { |
455 enable = 0; |
516 enable = 0; |
456 break; |
517 break; |
457 } |
518 } |
458 } |
519 } |
|
520 free(check); |
459 gw->enable(gw->widget, enable); |
521 gw->enable(gw->widget, enable); |
460 } |
|
461 |
|
462 if(groups) { |
|
463 free(groups); |
|
464 } |
522 } |
465 } |
523 } |
466 |
524 |
467 void ui_widget_set_groups(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, ...) { |
525 void ui_widget_set_groups(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, ...) { |
468 // get groups |
526 // get groups |
469 UcxList *groups = NULL; |
527 CxList *groups = cxArrayListCreate(cxDefaultAllocator, NULL, sizeof(int), 16); |
470 va_list ap; |
528 va_list ap; |
471 va_start(ap, enable); |
529 va_start(ap, enable); |
472 int group; |
530 int group; |
473 while((group = va_arg(ap, int)) != -1) { |
531 while((group = va_arg(ap, int)) != -1) { |
474 groups = ucx_list_append(groups, (void*)(intptr_t)group); |
532 cxListAdd(groups, &group); |
475 } |
533 } |
476 va_end(ap); |
534 va_end(ap); |
477 |
535 |
478 uic_add_group_widget(ctx, widget, enable, groups); |
536 uic_add_group_widget(ctx, widget, enable, groups); |
479 |
537 |
480 ucx_list_free(groups); |
538 cxListDestroy(groups); |
481 } |
539 } |
482 |
540 |
483 void uic_add_group_widget(UiContext *ctx, void *widget, ui_enablefunc enable, UcxList *groups) { |
541 size_t uic_group_array_size(const int *groups) { |
484 UcxMempool *mp = ctx->mempool; |
542 int i; |
485 UiGroupWidget *gw = ucx_mempool_malloc(mp, sizeof(UiGroupWidget)); |
543 for(i=0;groups[i] >= 0;i++) { } |
486 |
544 return i; |
487 gw->widget = widget; |
545 } |
488 gw->enable = enable; |
546 |
489 gw->numgroups = ucx_list_size(groups); |
547 void uic_add_group_widget(UiContext *ctx, void *widget, ui_enablefunc enable, CxList *groups) { |
490 gw->groups = ucx_mempool_calloc(mp, gw->numgroups, sizeof(int)); |
548 uic_add_group_widget_i(ctx, widget, enable, cxListAt(groups, 0), cxListSize(groups)); |
491 int i = 0; |
549 } |
492 UCX_FOREACH(elm, groups) { |
550 |
493 gw->groups[i++] = (intptr_t)elm->data; |
551 void uic_add_group_widget_i(UiContext *ctx, void *widget, ui_enablefunc enable, const int *groups, size_t numgroups) { |
494 } |
552 const CxAllocator *a = ctx->allocator; |
495 |
553 UiGroupWidget gw; |
496 ctx->group_widgets = ucx_list_append_a( |
554 |
497 mp->allocator, |
555 gw.widget = widget; |
498 ctx->group_widgets, |
556 gw.enable = enable; |
499 gw); |
557 gw.numgroups = numgroups; |
|
558 gw.groups = cxCalloc(a, numgroups, sizeof(int)); |
|
559 |
|
560 // copy groups |
|
561 if(groups) { |
|
562 memcpy(gw.groups, groups, gw.numgroups * sizeof(int)); |
|
563 } |
|
564 |
|
565 cxListAdd(ctx->group_widgets, &gw); |
|
566 } |
|
567 |
|
568 void uic_remove_group_widget(UiContext *ctx, void *widget) { |
|
569 (void)cxListFindRemove(ctx->group_widgets, widget); |
|
570 } |
|
571 |
|
572 UIEXPORT void *ui_allocator(UiContext *ctx) { |
|
573 return (void*)ctx->allocator; |
|
574 } |
|
575 |
|
576 void* ui_cx_mempool(UiContext *ctx) { |
|
577 return ctx->mp; |
500 } |
578 } |
501 |
579 |
502 void* ui_malloc(UiContext *ctx, size_t size) { |
580 void* ui_malloc(UiContext *ctx, size_t size) { |
503 return ctx ? ucx_mempool_malloc(ctx->mempool, size) : NULL; |
581 return ctx ? cxMalloc(ctx->allocator, size) : NULL; |
504 } |
582 } |
505 |
583 |
506 void* ui_calloc(UiContext *ctx, size_t nelem, size_t elsize) { |
584 void* ui_calloc(UiContext *ctx, size_t nelem, size_t elsize) { |
507 return ctx ? ucx_mempool_calloc(ctx->mempool, nelem, elsize) : NULL; |
585 return ctx ? cxCalloc(ctx->allocator, nelem, elsize) : NULL; |
508 } |
586 } |
509 |
587 |
510 void ui_free(UiContext *ctx, void *ptr) { |
588 void ui_free(UiContext *ctx, void *ptr) { |
511 if(ctx) { |
589 if(ctx && ptr) { |
512 ucx_mempool_free(ctx->mempool, ptr); |
590 cxFree(ctx->allocator, ptr); |
513 } |
591 } |
514 } |
592 } |
515 |
593 |
516 void* ui_realloc(UiContext *ctx, void *ptr, size_t size) { |
594 void* ui_realloc(UiContext *ctx, void *ptr, size_t size) { |
517 return ctx ? ucx_mempool_realloc(ctx->mempool, ptr, size) : NULL; |
595 return ctx ? cxRealloc(ctx->allocator, ptr, size) : NULL; |
518 } |
596 } |
519 |
597 |
|
598 UIEXPORT char* ui_strdup(UiContext *ctx, const char *str) { |
|
599 if(!ctx) { |
|
600 return NULL; |
|
601 } |
|
602 cxstring s = cx_str(str); |
|
603 cxmutstr d = cx_strdup_a(ctx->allocator, s); |
|
604 return d.ptr; |
|
605 } |