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) { |
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 } |