1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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 <cx/array_list.h>
36 #include <cx/compare.h>
37 #include <cx/mempool.h>
38
39 #include "context.h"
40 #include "../ui/window.h"
41 #include "document.h"
42 #include "types.h"
43
44
45 static UiContext* global_context;
46
47 void uic_init_global_context(
void) {
48 CxMempool *mp = cxBasicMempoolCreate(
32);
49 global_context = uic_context(
NULL, mp);
50 }
51
52 UiContext* ui_global_context(
void) {
53 return global_context;
54 }
55
56 UiContext* uic_context(UiObject *toplevel, CxMempool *mp) {
57 UiContext *ctx = cxMalloc(mp->allocator,
sizeof(UiContext));
58 memset(ctx,
0,
sizeof(UiContext));
59 ctx->mp = mp;
60 ctx->allocator = mp->allocator;
61 ctx->obj = toplevel;
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);
67
68 ctx->attach_document = uic_context_attach_document;
69 ctx->detach_document2 = uic_context_detach_document2;
70
71 #if UI_GTK2 ||
UI_GTK3
72 if(toplevel && toplevel->widget) {
73 ctx->accel_group = gtk_accel_group_new();
74 gtk_window_add_accel_group(
GTK_WINDOW(toplevel->widget), ctx->accel_group);
75 }
76 #endif
77
78 return ctx;
79 }
80
81 UiContext* uic_root_context(UiContext *ctx) {
82 return ctx->parent ? uic_root_context(ctx->parent) : ctx;
83 }
84
85 void uic_context_prepare_close(UiContext *ctx) {
86 cxListClear(ctx->groups);
87 cxListClear(ctx->group_widgets);
88 }
89
90 void uic_context_attach_document(UiContext *ctx,
void *document) {
91 cxListAdd(ctx->documents, document);
92 ctx->document = document;
93
94 UiContext *doc_ctx = ui_document_context(document);
95
96
97
98 UiContext *var_ctx = ctx;
99 while(var_ctx) {
100 if(var_ctx->vars_unbound && cxMapSize(var_ctx->vars_unbound) >
0) {
101 CxIterator i = cxMapIterator(var_ctx->vars_unbound);
102 cx_foreach(CxMapEntry*, entry, i) {
103 UiVar *var = entry->value;
104 UiVar *docvar = cxMapGet(doc_ctx->vars, *entry->key);
105 if(docvar) {
106
107 uic_copy_binding(var, docvar,
TRUE);
108 cxIteratorFlagRemoval(i);
109 }
110 }
111 }
112
113 var_ctx = ctx->parent;
114 }
115 }
116
117 static void uic_context_unbind_vars(UiContext *ctx) {
118 CxIterator i = cxMapIterator(ctx->vars);
119 cx_foreach(CxMapEntry*, entry, i) {
120 UiVar *var = entry->value;
121 if(var->from && var->from_ctx) {
122 uic_save_var2(var);
123 uic_copy_binding(var, var->from,
FALSE);
124 cxMapPut(var->from_ctx->vars_unbound, *entry->key, var->from);
125 var->from_ctx = ctx;
126 }
127 }
128
129 if(ctx->documents) {
130 i = cxListIterator(ctx->documents);
131 cx_foreach(
void *, doc, i) {
132 UiContext *subctx = ui_document_context(doc);
133 uic_context_unbind_vars(subctx);
134 }
135 }
136 }
137
138 void uic_context_detach_document2(UiContext *ctx,
void *document) {
139
140 ssize_t docIndex = cxListFind(ctx->documents, document);
141 if(docIndex <
0) {
142 return;
143 }
144
145 cxListRemove(ctx->documents, docIndex);
146 ctx->document = cxListAt(ctx->documents,
0);
147
148 UiContext *docctx = ui_document_context(document);
149 uic_context_unbind_vars(docctx);
150 }
151
152 void uic_context_detach_all(UiContext *ctx) {
153
154 CxList *ls = cxLinkedListCreate(cxDefaultAllocator,
NULL,
CX_STORE_POINTERS);
155 CxIterator i = cxListIterator(ctx->documents);
156 cx_foreach(
void *, doc, i) {
157 cxListAdd(ls, doc);
158 }
159
160
161 i = cxListIterator(ls);
162 cx_foreach(
void *, doc, i) {
163 ctx->detach_document2(ctx, doc);
164 }
165
166 cxListDestroy(ls);
167 }
168
169 static UiVar* ctx_getvar(UiContext *ctx, CxHashKey key) {
170 UiVar *var = cxMapGet(ctx->vars, key);
171 if(!var && ctx->documents) {
172 CxIterator i = cxListIterator(ctx->documents);
173 cx_foreach(
void *, doc, i) {
174 UiContext *subctx = ui_document_context(doc);
175 var = ctx_getvar(subctx, key);
176 if(var) {
177 break;
178 }
179 }
180 }
181 return var;
182 }
183
184 UiVar* uic_get_var(UiContext *ctx,
const char *name) {
185 CxHashKey key = cx_hash_key(name, strlen(name));
186 return ctx_getvar(ctx, key);
187 }
188
189 UiVar* uic_create_var(UiContext *ctx,
const char *name, UiVarType type) {
190 if(ctx->vars_unbound) {
191 UiVar *unbound = cxMapGet(ctx->vars_unbound, name);
192 if(unbound) {
193 return unbound;
194 }
195 }
196
197 UiVar *var = uic_get_var(ctx, name);
198 if(var) {
199 if(var->type == type) {
200 return var;
201 }
else {
202 fprintf(stderr,
"UiError: var ''%s'' already bound with different type\n", name);
203 }
204 }
205
206 var = ui_malloc(ctx,
sizeof(UiVar));
207 var->type = type;
208 var->value = uic_create_value(ctx, type);
209 var->from =
NULL;
210 var->from_ctx = ctx;
211
212 cxMempoolSetDestructor(var, (cx_destructor_func)uic_unbind_var);
213
214 if(!ctx->vars_unbound) {
215 ctx->vars_unbound = cxHashMapCreate(ctx->allocator,
CX_STORE_POINTERS,
16);
216 }
217 cxMapPut(ctx->vars_unbound, name, var);
218
219 return var;
220 }
221
222 UiVar* uic_create_value_var(UiContext* ctx,
void* value) {
223 UiVar *var = (UiVar*)ui_malloc(ctx,
sizeof(UiVar));
224 var->from =
NULL;
225 var->from_ctx = ctx;
226 var->value = value;
227 var->type =
UI_VAR_SPECIAL;
228 return var;
229 }
230
231 void* uic_create_value(UiContext *ctx, UiVarType type) {
232 void *val =
NULL;
233 switch(type) {
234 case UI_VAR_SPECIAL:
break;
235 case UI_VAR_INTEGER: {
236 val = ui_int_new(ctx,
NULL);
237 break;
238 }
239 case UI_VAR_DOUBLE: {
240 val = ui_double_new(ctx,
NULL);
241 break;
242 }
243 case UI_VAR_STRING: {
244 val = ui_string_new(ctx,
NULL);
245 break;
246 }
247 case UI_VAR_TEXT: {
248 val = ui_text_new(ctx,
NULL);
249 break;
250 }
251 case UI_VAR_LIST: {
252 val = ui_list_new(ctx,
NULL);
253 break;
254 }
255 case UI_VAR_RANGE: {
256 val = ui_range_new(ctx,
NULL);
257 break;
258 }
259 case UI_VAR_GENERIC: {
260 val = ui_generic_new(ctx,
NULL);
261 }
262 }
263 return val;
264 }
265
266
267 UiVar* uic_widget_var(UiContext* toplevel, UiContext* current,
void* value,
const char* varname, UiVarType type) {
268 if (value) {
269 return uic_create_value_var(current, value);
270 }
271 if (varname) {
272 return uic_create_var(toplevel, varname, type);
273 }
274 return NULL;
275 }
276
277
278 void uic_copy_binding(UiVar *from, UiVar *to, UiBool copytodoc) {
279
280 if(from->type != to->type) {
281 fprintf(stderr,
"UI Error: var has incompatible type.\n");
282 return;
283 }
284
285 void *fromvalue = from->value;
286
287 if(copytodoc) {
288 to->from = from;
289 to->from_ctx = from->from_ctx;
290 }
291
292
293
294 switch(from->type) {
295 default: fprintf(stderr,
"uic_copy_binding: wtf!\n");
break;
296 case UI_VAR_SPECIAL:
break;
297 case UI_VAR_INTEGER: {
298 UiInteger *f = fromvalue;
299 UiInteger *t = to->value;
300 if(!f->obj)
break;
301 uic_int_copy(f, t);
302 t->set(t, t->value);
303 break;
304 }
305 case UI_VAR_DOUBLE: {
306 UiDouble *f = fromvalue;
307 UiDouble *t = to->value;
308 if(!f->obj)
break;
309 uic_double_copy(f, t);
310 t->set(t, t->value);
311 break;
312 }
313 case UI_VAR_STRING: {
314 UiString *f = fromvalue;
315 UiString *t = to->value;
316 if(!f->obj)
break;
317 uic_string_copy(f, t);
318 char *tvalue = t->value.ptr ? t->value.ptr :
"";
319 t->set(t, tvalue);
320 break;
321 }
322 case UI_VAR_TEXT: {
323 UiText *f = fromvalue;
324 UiText *t = to->value;
325 if(!f->obj)
break;
326 uic_text_copy(f, t);
327 char *tvalue = t->value.ptr ? t->value.ptr :
"";
328 t->set(t, tvalue);
329 t->setposition(t, t->pos);
330 break;
331 }
332 case UI_VAR_LIST: {
333
334
335 UiList *f = from->value;
336 UiList *t = to->value;
337 if (f->obj) {
338 t->obj = f->obj;
339 t->update = f->update;
340 t->getselection = f->getselection;
341 t->setselection = f->setselection;
342 }
343
344 UiVar tmp = *from;
345 *from = *to;
346 *to = tmp;
347
348 UiList* t2 = to->value;
349 ui_notify(t2->observers,
NULL);
350
351 break;
352 }
353 case UI_VAR_RANGE: {
354 UiRange *f = fromvalue;
355 UiRange *t = to->value;
356 if(!f->obj)
break;
357 uic_range_copy(f, t);
358 t->setextent(t, t->extent);
359 t->setrange(t, t->min, t->max);
360 t->set(t, t->value);
361 break;
362 }
363 case UI_VAR_GENERIC: {
364 UiGeneric *f = fromvalue;
365 UiGeneric *t = to->value;
366 if(!f->obj)
break;
367 uic_generic_copy(f, t);
368 t->set(t, t->value, t->type);
369 break;
370 }
371 }
372 }
373
374 void uic_save_var2(UiVar *var) {
375 switch(var->type) {
376 case UI_VAR_SPECIAL:
break;
377 case UI_VAR_INTEGER: uic_int_save(var->value);
break;
378 case UI_VAR_DOUBLE: uic_double_save(var->value);
break;
379 case UI_VAR_STRING: uic_string_save(var->value);
break;
380 case UI_VAR_TEXT: uic_text_save(var->value);
break;
381 case UI_VAR_LIST:
break;
382 case UI_VAR_RANGE: uic_range_save(var->value);
break;
383 case UI_VAR_GENERIC: uic_generic_save(var->value);
break;
384 }
385 }
386
387 void uic_unbind_var(UiVar *var) {
388 switch(var->type) {
389 case UI_VAR_SPECIAL:
break;
390 case UI_VAR_INTEGER: uic_int_unbind(var->value);
break;
391 case UI_VAR_DOUBLE: uic_double_unbind(var->value);
break;
392 case UI_VAR_STRING: uic_string_unbind(var->value);
break;
393 case UI_VAR_TEXT: uic_text_unbind(var->value);
break;
394 case UI_VAR_LIST: uic_list_unbind(var->value);
break;
395 case UI_VAR_RANGE: uic_range_unbind(var->value);
break;
396 case UI_VAR_GENERIC: uic_generic_unbind(var->value);
break;
397 }
398 }
399
400 void uic_reg_var(UiContext *ctx,
char *name, UiVarType type,
void *value) {
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426 UiVar *var = ui_malloc(ctx,
sizeof(UiVar));
427 var->type = type;
428 var->value = value;
429 var->from =
NULL;
430 var->from_ctx = ctx;
431 size_t oldcount = cxMapSize(ctx->vars);
432 cxMapPut(ctx->vars, name, var);
433 if(cxMapSize(ctx->vars) != oldcount +
1) {
434 fprintf(stderr,
"UiError: var ''%s'' already exists\n", name);
435 }
436
437
438
439
440
441
442
443
444
445 }
446
447 void uic_remove_bound_var(UiContext *ctx, UiVar *var) {
448
449 }
450
451
452
453
454 void ui_attach_document(UiContext *ctx,
void *document) {
455 uic_context_attach_document(ctx, document);
456 }
457
458 void ui_detach_document2(UiContext *ctx,
void *document) {
459 uic_context_detach_document2(ctx, document);
460 }
461
462 void ui_context_closefunc(UiContext *ctx, ui_callback fnc,
void *udata) {
463 ctx->close_callback = fnc;
464 ctx->close_data = udata;
465 }
466
467 UIEXPORT void ui_context_destroy(UiContext *ctx) {
468 cxMempoolDestroy(ctx->mp);
469 }
470
471
472 void ui_set_group(UiContext *ctx,
int group) {
473 if(cxListFind(ctx->groups, &group) == -
1) {
474 cxListAdd(ctx->groups, &group);
475 }
476
477
478 uic_check_group_widgets(ctx);
479 }
480
481 void ui_unset_group(UiContext *ctx,
int group) {
482 int i = cxListFind(ctx->groups, &group);
483 if(i != -
1) {
484 cxListRemove(ctx->groups, i);
485 }
486
487
488 uic_check_group_widgets(ctx);
489 }
490
491 int* ui_active_groups(UiContext *ctx,
int *ngroups) {
492 *ngroups = cxListSize(ctx->groups);
493 return cxListAt(ctx->groups,
0);
494 }
495
496 void uic_check_group_widgets(UiContext *ctx) {
497 int ngroups =
0;
498 int *groups = ui_active_groups(ctx, &ngroups);
499
500 CxIterator i = cxListIterator(ctx->group_widgets);
501 cx_foreach(UiGroupWidget *, gw, i) {
502 char *check = calloc(
1, gw->numgroups);
503
504 for(
int i=
0;i<ngroups;i++) {
505 for(
int k=
0;k<gw->numgroups;k++) {
506 if(groups[i] == gw->groups[k]) {
507 check[k] =
1;
508 }
509 }
510 }
511
512 int enable =
1;
513 for(
int i=
0;i<gw->numgroups;i++) {
514 if(check[i] ==
0) {
515 enable =
0;
516 break;
517 }
518 }
519 free(check);
520 gw->enable(gw->widget, enable);
521 }
522 }
523
524 void ui_widget_set_groups(UiContext *ctx,
UIWIDGET widget, ui_enablefunc enable, ...) {
525
526 CxList *groups = cxArrayListCreate(cxDefaultAllocator,
NULL,
sizeof(
int),
16);
527 va_list ap;
528 va_start(ap, enable);
529 int group;
530 while((group = va_arg(ap,
int)) != -
1) {
531 cxListAdd(groups, &group);
532 }
533 va_end(ap);
534
535 uic_add_group_widget(ctx, widget, enable, groups);
536
537 cxListDestroy(groups);
538 }
539
540 size_t uic_group_array_size(
const int *groups) {
541 int i;
542 for(i=
0;groups[i] >=
0;i++) { }
543 return i;
544 }
545
546 void uic_add_group_widget(UiContext *ctx,
void *widget, ui_enablefunc enable, CxList *groups) {
547 uic_add_group_widget_i(ctx, widget, enable, cxListAt(groups,
0), cxListSize(groups));
548 }
549
550 void uic_add_group_widget_i(UiContext *ctx,
void *widget, ui_enablefunc enable,
const int *groups,
size_t numgroups) {
551 const CxAllocator *a = ctx->allocator;
552 UiGroupWidget gw;
553
554 gw.widget = widget;
555 gw.enable = enable;
556 gw.numgroups = numgroups;
557 gw.groups = cxCalloc(a, numgroups,
sizeof(
int));
558
559
560 if(groups) {
561 memcpy(gw.groups, groups, gw.numgroups *
sizeof(
int));
562 }
563
564 cxListAdd(ctx->group_widgets, &gw);
565 }
566
567 void uic_remove_group_widget(UiContext *ctx,
void *widget) {
568 (
void)cxListFindRemove(ctx->group_widgets, widget);
569 }
570
571 UIEXPORT void *ui_allocator(UiContext *ctx) {
572 return (
void*)ctx->allocator;
573 }
574
575 void* ui_cx_mempool(UiContext *ctx) {
576 return ctx->mp;
577 }
578
579 void* ui_malloc(UiContext *ctx,
size_t size) {
580 return ctx ? cxMalloc(ctx->allocator, size) :
NULL;
581 }
582
583 void* ui_calloc(UiContext *ctx,
size_t nelem,
size_t elsize) {
584 return ctx ? cxCalloc(ctx->allocator, nelem, elsize) :
NULL;
585 }
586
587 void ui_free(UiContext *ctx,
void *ptr) {
588 if(ctx && ptr) {
589 cxFree(ctx->allocator, ptr);
590 }
591 }
592
593 void* ui_realloc(UiContext *ctx,
void *ptr,
size_t size) {
594 return ctx ? cxRealloc(ctx->allocator, ptr, size) :
NULL;
595 }
596
597 UIEXPORT char* ui_strdup(UiContext *ctx,
const char *str) {
598 if(!ctx) {
599 return NULL;
600 }
601 cxstring s = cx_str(str);
602 cxmutstr d = cx_strdup_a(ctx->allocator, s);
603 return d.ptr;
604 }
605