ui/common/context.c

changeset 163
b70e2a77dea0
parent 154
8a4451fcb736
child 164
1d912f78fd1d
equal deleted inserted replaced
162:18892c0a9adc 163:b70e2a77dea0
42 memset(ctx, 0, sizeof(UiContext)); 42 memset(ctx, 0, sizeof(UiContext));
43 ctx->mempool = mp; 43 ctx->mempool = mp;
44 ctx->obj = toplevel; 44 ctx->obj = toplevel;
45 ctx->vars = ucx_map_new_a(mp->allocator, 16); 45 ctx->vars = ucx_map_new_a(mp->allocator, 16);
46 46
47 ctx->set_document = uic_context_set_document; 47 ctx->attach_document = uic_context_attach_document;
48 ctx->detach_document = uic_context_detach_document; 48 ctx->detach_document2 = uic_context_detach_document2;
49 49
50 #ifdef UI_GTK 50 #ifdef UI_GTK
51 if(toplevel->widget) { 51 if(toplevel->widget) {
52 ctx->accel_group = gtk_accel_group_new(); 52 ctx->accel_group = gtk_accel_group_new();
53 gtk_window_add_accel_group(GTK_WINDOW(toplevel->widget), ctx->accel_group); 53 gtk_window_add_accel_group(GTK_WINDOW(toplevel->widget), ctx->accel_group);
59 59
60 UiContext* uic_root_context(UiContext *ctx) { 60 UiContext* uic_root_context(UiContext *ctx) {
61 return ctx->parent ? uic_root_context(ctx->parent) : ctx; 61 return ctx->parent ? uic_root_context(ctx->parent) : ctx;
62 } 62 }
63 63
64 void uic_context_set_document(UiContext *ctx, void *document) { 64 void uic_context_attach_document(UiContext *ctx, void *document) {
65 if(ctx->document == document) { 65 ctx->documents = ucx_list_append_a(ctx->mempool->allocator, ctx->documents, document);
66 return; 66 ctx->document = ctx->documents->data;
67 } 67
68 if(ctx->document) { 68 UiContext *doc_ctx = ui_document_context(document);
69 uic_context_detach_document(ctx); 69
70 } 70 // check if any parent context has an unbound variable with the same name
71 ctx->document = document; 71 // as any document variable
72 72 UiContext *var_ctx = ctx;
73 UiContext *docctx = ui_document_context(document); 73 while(var_ctx) {
74 if(!docctx) { 74 if(var_ctx->vars_unbound && var_ctx->vars_unbound->count > 0) {
75 fprintf(stderr, "UiError: uic_context_set_document: pointer is not a document\n"); 75 UcxMapIterator i = ucx_map_iterator(var_ctx->vars_unbound);
76 return; 76 UiVar *var;
77 } 77 // rmkeys holds all keys, that shall be removed from vars_unbound
78 docctx->obj = ctx->obj; 78 UcxKey *rmkeys = calloc(var_ctx->vars_unbound->count, sizeof(UcxKey));
79 docctx->parent = ctx; 79 size_t numkeys = 0;
80 80 UCX_MAP_FOREACH(key, var, i) {
81 UiContext *root = uic_root_context(ctx); 81 UiVar *docvar = ucx_map_get(doc_ctx->vars, key);
82 if(!root->bound || root->bound->count == 0) { 82 if(docvar) {
83 return; 83 // bind var to document var
84 } 84 uic_copy_binding(var, docvar, TRUE);
85 85 rmkeys[numkeys++] = key; // save the key for removal
86 // there are bound variables in the root ctx 86 }
87 // copy bindings with correct name to doc vars 87 }
88 UcxMapIterator i = ucx_map_iterator(docctx->vars); 88 // now that we may have bound some vars to the document,
89 // we can remove them from the unbound map
90 for(size_t k=0;k<numkeys;k++) {
91 ucx_map_remove(var_ctx->vars_unbound, rmkeys[k]);
92 }
93 }
94
95 var_ctx = ctx->parent;
96 }
97 }
98
99 static void uic_context_unbind_vars(UiContext *ctx) {
100 UcxMapIterator i = ucx_map_iterator(ctx->vars);
89 UiVar *var; 101 UiVar *var;
90 UCX_MAP_FOREACH(key, var, i) { 102 UCX_MAP_FOREACH(key, var, i) {
91 UiVar *v = ucx_map_get(root->bound, key); 103 if(var->from && var->from_ctx) {
92 if(v) { 104 uic_save_var2(var);
93 // copy binding: after this, all doc vars with names of previously
94 // bound variables are bound to the widgets
95 // the widgets still hold a pointer to the root ctx vars, but this
96 // vars have a pointer to the document variable value - confusing
97 uic_copy_binding(v, var, TRUE);
98 }
99 }
100 }
101
102 void uic_context_detach_document(UiContext *ctx) {
103 if(!ctx->document) {
104 return;
105 }
106
107 UiContext *docctx = ui_document_context(ctx->document);
108 if(!docctx) {
109 fprintf(stderr, "UiError: Cannot detach document: no context\n");
110 return;
111 }
112 ctx->document = NULL;
113 docctx->parent = NULL;
114 docctx->obj = NULL;
115
116 // unbind all vars
117 UcxMapIterator i = ucx_map_iterator(docctx->vars);
118 UiVar *var;
119 UCX_MAP_FOREACH(key, var, i) {
120 uic_save_var(var);
121 if(var->from) {
122 // restore old root bound var val
123 var->from->value = var->from->orig_val;
124 var->from->orig_val = NULL;
125 // copy
126 uic_copy_binding(var, var->from, FALSE); 105 uic_copy_binding(var, var->from, FALSE);
127 } 106 ucx_map_put(var->from_ctx->vars_unbound, key, var->from);
128 uic_unbind_var(var); 107 var->from_ctx = ctx;
129 } 108 }
109 }
110
111 UCX_FOREACH(elm, ctx->documents) {
112 UiContext *subctx = ui_document_context(elm->data);
113 uic_context_unbind_vars(subctx);
114 }
115 }
116
117 void uic_context_detach_document2(UiContext *ctx, void *document) {
118 // find the document in the documents list
119 UcxList *doc = NULL;
120 UCX_FOREACH(elm, ctx->documents) {
121 if(elm->data == document) {
122 doc = elm;
123 break;
124 }
125 }
126 if(!doc) {
127 return; // document is not a subdocument of this context
128 }
129
130 ctx->documents = ucx_list_remove_a(ctx->mempool->allocator, ctx->documents, doc);
131 ctx->document = ctx->documents ? ctx->documents->data : NULL;
132
133 UiContext *docctx = ui_document_context(document);
134 uic_context_unbind_vars(docctx); // unbind all doc/subdoc vars from the parent
135 }
136
137 void uic_context_detach_all(UiContext *ctx) {
138 UcxList *ls = ucx_list_clone(ctx->documents, NULL, NULL);
139 UCX_FOREACH(elm, ls) {
140 ctx->detach_document2(ctx, elm->data);
141 }
142 ucx_list_free(ls);
143 }
144
145 static UiVar* ctx_getvar(UiContext *ctx, UcxKey key) {
146 UiVar *var = ucx_map_get(ctx->vars, key);
147 if(!var) {
148 UCX_FOREACH(elm, ctx->documents) {
149 UiContext *subctx = ui_document_context(elm->data);
150 var = ctx_getvar(subctx, key);
151 if(var) {
152 break;
153 }
154 }
155 }
156 return var;
130 } 157 }
131 158
132 UiVar* uic_get_var(UiContext *ctx, char *name) { 159 UiVar* uic_get_var(UiContext *ctx, char *name) {
133 // check document variables first 160 UcxKey key = ucx_key(name, strlen(name));
134 UiVar *var = NULL; 161 return ctx_getvar(ctx, key);
135 UiContext *doc_ctx = ui_document_context(ctx->document);
136 if(doc_ctx) {
137 var = uic_get_var(doc_ctx, name);
138 }
139
140 // check variables of this context
141 if(!var) {
142 var = ucx_map_cstr_get(ctx->vars, name);
143 }
144
145 return var;
146 } 162 }
147 163
148 UiVar* uic_create_var(UiContext *ctx, char *name, UiVarType type) { 164 UiVar* uic_create_var(UiContext *ctx, char *name, UiVarType type) {
149 // check if this context has a var with this name 165 UiVar *var = uic_get_var(ctx, name);
150 // otherweise add it to the bound map 166 if(var) {
151 UiVar *cv = ucx_map_cstr_get(ctx->vars, name); 167 if(var->type == type) {
152 if(cv) { 168 return var;
153 if(cv->type == type) {
154 return cv;
155 } else { 169 } else {
156 fprintf(stderr, "UiError: var '%s' already exists with different type\n", name); 170 fprintf(stderr, "UiError: var '%s' already bound with different type\n", name);
157 } 171 }
158 } 172 }
159 173
160 UiVar *var;
161 if(ctx->bound) {
162 var = ucx_map_cstr_get(ctx->bound, name);
163 if(var) {
164 if(var->type != type) {
165 fprintf(stderr, "UiError: var '%s' already bound with different type\n", name);
166 } else {
167 return var;
168 }
169 }
170 }
171
172 // create var and add it to the bound map
173 // this map contains vars that are created by widgets
174 // the vars can later be moved to subdocuments
175 var = ui_malloc(ctx, sizeof(UiVar)); 174 var = ui_malloc(ctx, sizeof(UiVar));
176 var->type = type; 175 var->type = type;
177 var->value = uic_create_value(ctx, type); 176 var->value = uic_create_value(ctx, type);
178 var->orig_val = NULL; 177 var->from = NULL;
179 var->from = NULL; // not connected to a doc var 178 var->from_ctx = ctx;
180 179
181 if(!ctx->bound) { 180 if(!ctx->vars_unbound) {
182 ctx->bound = ucx_map_new_a(ctx->mempool->allocator, 16); 181 ctx->vars_unbound = ucx_map_new_a(ctx->mempool->allocator, 16);
183 } 182 }
184 ucx_map_cstr_put(ctx->bound, name, var); 183 ucx_map_cstr_put(ctx->vars_unbound, name, var);
185
186 // if a subdocument has a variable with this name, we must copy
187 // the binding to the doc var
188 UiContext *doc_ctx = ui_document_context(ctx->document);
189 if(doc_ctx) {
190 UiVar *docvar = uic_get_var(doc_ctx, name);
191 if(docvar) {
192 var->orig_val = var->value;
193 var->value = docvar->value;
194 docvar->from = var;
195 }
196 }
197 184
198 return var; 185 return var;
199 } 186 }
200 187
201 void* uic_create_value(UiContext *ctx, UiVarType type) { 188 void* uic_create_value(UiContext *ctx, UiVarType type) {
228 } 215 }
229 return val; 216 return val;
230 } 217 }
231 218
232 void uic_copy_binding(UiVar *from, UiVar *to, UiBool copytodoc) { 219 void uic_copy_binding(UiVar *from, UiVar *to, UiBool copytodoc) {
233 if(copytodoc && from->from) {
234 fprintf(stderr, "UiError: var already connected to a document\n");
235 return;
236 }
237
238 // check type 220 // check type
239 if(from->type != to->type) { 221 if(from->type != to->type) {
240 fprintf(stderr, "UI Error: var has incompatible type.\n"); 222 fprintf(stderr, "UI Error: var has incompatible type.\n");
241 return; 223 return;
242 } 224 }
243 225
244 void *fromvalue = from->value; 226 void *fromvalue = from->value;
245 // update var 227 // update var
246 if(copytodoc) { 228 if(copytodoc) {
247 to->from = from; 229 to->from = from;
248 230 to->from_ctx = from->from_ctx;
249 from->orig_val = from->value;
250 from->value = to->value;
251 } 231 }
252 232
253 // copy binding 233 // copy binding
254 // we don't copy the observer, because the from var has never one 234 // we don't copy the observer, because the from var has never one
255 switch(from->type) { 235 switch(from->type) {
309 break; 289 break;
310 } 290 }
311 } 291 }
312 } 292 }
313 293
314 void uic_save_var(UiVar *var) { 294 void uic_save_var2(UiVar *var) {
315 switch(var->type) { 295 switch(var->type) {
296 case UI_VAR_SPECIAL: break;
316 case UI_VAR_INTEGER: uic_int_save(var->value); break; 297 case UI_VAR_INTEGER: uic_int_save(var->value); break;
317 case UI_VAR_DOUBLE: uic_double_save(var->value); break; 298 case UI_VAR_DOUBLE: uic_double_save(var->value); break;
318 case UI_VAR_STRING: uic_string_save(var->value); break; 299 case UI_VAR_STRING: uic_string_save(var->value); break;
319 case UI_VAR_TEXT: uic_text_save(var->value); break; 300 case UI_VAR_TEXT: uic_text_save(var->value); break;
320 case UI_VAR_LIST: break; 301 case UI_VAR_LIST: break;
332 case UI_VAR_RANGE: uic_range_unbind(var->value); break; 313 case UI_VAR_RANGE: uic_range_unbind(var->value); break;
333 } 314 }
334 } 315 }
335 316
336 void uic_reg_var(UiContext *ctx, char *name, UiVarType type, void *value) { 317 void uic_reg_var(UiContext *ctx, char *name, UiVarType type, void *value) {
337 UiContext *rootctx = uic_root_context(ctx); 318 // TODO: do we need/want this? Why adding vars to a context after
338 319 // widgets reference these? Workarounds:
320 // 1. add vars to ctx before creating ui
321 // 2. create ui, create new document with vars, attach doc
322 // also it would be possible to create a function, that scans unbound vars
323 // and connects them to available vars
324 /*
325 UiContext *rootctx = uic_root_context(ctx);
339 UiVar *b = NULL; 326 UiVar *b = NULL;
340 if(rootctx->bound) { 327 if(rootctx->bound) {
341 // some widgets are already bound to some vars 328 // some widgets are already bound to some vars
342 b = ucx_map_cstr_get(rootctx->bound, name); 329 b = ucx_map_cstr_get(rootctx->bound, name);
343 if(b) { 330 if(b) {
348 ucx_map_cstr_remove(ctx->bound, name); 335 ucx_map_cstr_remove(ctx->bound, name);
349 // TODO: free stuff 336 // TODO: free stuff
350 } 337 }
351 } 338 }
352 } 339 }
340 */
353 341
354 // create new var and add it to doc's vars 342 // create new var and add it to doc's vars
355 UiVar *var = ui_malloc(ctx, sizeof(UiVar)); 343 UiVar *var = ui_malloc(ctx, sizeof(UiVar));
356 var->from = NULL;
357 var->orig_val = NULL;
358 var->type = type; 344 var->type = type;
359 var->value = value; 345 var->value = value;
346 var->from = NULL;
347 var->from_ctx = ctx;
360 size_t oldcount = ctx->vars->count; 348 size_t oldcount = ctx->vars->count;
361 ucx_map_cstr_put(ctx->vars, name, var); 349 ucx_map_cstr_put(ctx->vars, name, var);
362 if(ctx->vars->count != oldcount + 1) { 350 if(ctx->vars->count != oldcount + 1) {
363 fprintf(stderr, "UiError: var '%s' already exists\n", name); 351 fprintf(stderr, "UiError: var '%s' already exists\n", name);
364 } 352 }
365 353
354 // TODO: remove?
366 // a widget is already bound to a var with this name 355 // a widget is already bound to a var with this name
367 // copy the binding (like uic_context_set_document) 356 // copy the binding (like uic_context_set_document)
357 /*
368 if(b) { 358 if(b) {
369 uic_copy_binding(b, var, TRUE); 359 uic_copy_binding(b, var, TRUE);
370 } 360 }
361 */
371 } 362 }
372 363
373 void uic_remove_bound_var(UiContext *ctx, UiVar *var) { 364 void uic_remove_bound_var(UiContext *ctx, UiVar *var) {
374 // TODO: implement 365 // TODO: implement
366 printf("TODO: implement uic_remove_bound_var\n");
375 } 367 }
376 368
377 369
378 // public API 370 // public API
371
372 void ui_attach_document(UiContext *ctx, void *document) {
373 uic_context_attach_document(ctx, document);
374 }
375
376 void ui_detach_document2(UiContext *ctx, void *document) {
377 uic_context_detach_document2(ctx, document);
378 }
379 379
380 void ui_context_closefunc(UiContext *ctx, ui_callback fnc, void *udata) { 380 void ui_context_closefunc(UiContext *ctx, ui_callback fnc, void *udata) {
381 ctx->close_callback = fnc; 381 ctx->close_callback = fnc;
382 ctx->close_data = udata; 382 ctx->close_data = udata;
383 } 383 }

mercurial