1 /**
2 * @file lv_xml.c
3 *
4 */
5
6 /*********************
7 * INCLUDES
8 *********************/
9
10 #include "lv_xml.h"
11 #if LV_USE_XML
12
13 #include "lv_xml_base_types.h"
14 #include "lv_xml_parser.h"
15 #include "lv_xml_component.h"
16 #include "lv_xml_component_private.h"
17 #include "lv_xml_widget.h"
18 #include "lv_xml_style.h"
19 #include "lv_xml.h"
20 #include "lv_xml_utils.h"
21 #include "lv_xml_private.h"
22 #include "parsers/lv_xml_obj_parser.h"
23 #include "parsers/lv_xml_button_parser.h"
24 #include "parsers/lv_xml_label_parser.h"
25 #include "parsers/lv_xml_image_parser.h"
26 #include "parsers/lv_xml_slider_parser.h"
27 #include "parsers/lv_xml_tabview_parser.h"
28 #include "parsers/lv_xml_chart_parser.h"
29 #include "parsers/lv_xml_table_parser.h"
30 #include "parsers/lv_xml_dropdown_parser.h"
31 #include "../../libs/expat/expat.h"
32 #include "../../draw/lv_draw_image.h"
33
34 /*********************
35 * DEFINES
36 *********************/
37
38 /**********************
39 * TYPEDEFS
40 **********************/
41
42 /**********************
43 * STATIC PROTOTYPES
44 **********************/
45 static void view_start_element_handler(void * user_data, const char * name, const char ** attrs);
46 static void view_end_element_handler(void * user_data, const char * name);
47 static void register_builtin_fonts(void);
48
49 /**********************
50 * STATIC VARIABLES
51 **********************/
52 static lv_ll_t font_ll;
53 static lv_ll_t image_ll;
54
55 /**********************
56 * MACROS
57 **********************/
58
59 /**********************
60 * GLOBAL FUNCTIONS
61 **********************/
62
lv_xml_init(void)63 void lv_xml_init(void)
64 {
65 lv_ll_init(&font_ll, sizeof(lv_xml_font_t));
66 lv_ll_init(&image_ll, sizeof(lv_xml_image_t));
67
68 lv_xml_component_init();
69
70 register_builtin_fonts();
71
72 lv_xml_widget_register("lv_obj", lv_xml_obj_create, lv_xml_obj_apply);
73 lv_xml_widget_register("lv_button", lv_xml_button_create, lv_xml_button_apply);
74 lv_xml_widget_register("lv_label", lv_xml_label_create, lv_xml_label_apply);
75 lv_xml_widget_register("lv_image", lv_xml_image_create, lv_xml_image_apply);
76 lv_xml_widget_register("lv_slider", lv_xml_slider_create, lv_xml_slider_apply);
77 lv_xml_widget_register("lv_tabview", lv_xml_tabview_create, lv_xml_tabview_apply);
78 lv_xml_widget_register("lv_tabview-tab_bar", lv_xml_tabview_tab_bar_create, lv_xml_tabview_tab_bar_apply);
79 lv_xml_widget_register("lv_tabview-tab", lv_xml_tabview_tab_create, lv_xml_tabview_tab_apply);
80 lv_xml_widget_register("lv_chart", lv_xml_chart_create, lv_xml_chart_apply);
81 lv_xml_widget_register("lv_chart-cursor", lv_xml_chart_cursor_create, lv_xml_chart_cursor_apply);
82 lv_xml_widget_register("lv_chart-series", lv_xml_chart_series_create, lv_xml_chart_series_apply);
83 lv_xml_widget_register("lv_chart-axis", lv_xml_chart_axis_create, lv_xml_chart_axis_apply);
84 lv_xml_widget_register("lv_table", lv_xml_table_create, lv_xml_table_apply);
85 lv_xml_widget_register("lv_table-column", lv_xml_table_column_create, lv_xml_table_column_apply);
86 lv_xml_widget_register("lv_table-cell", lv_xml_table_cell_create, lv_xml_table_cell_apply);
87 lv_xml_widget_register("lv_dropdown", lv_xml_dropdown_create, lv_xml_dropdown_apply);
88 lv_xml_widget_register("lv_dropdown-list", lv_xml_dropdown_list_create, lv_xml_dropdown_list_apply);
89 }
90
lv_xml_create_from_ctx(lv_obj_t * parent,lv_xml_component_ctx_t * parent_ctx,lv_xml_component_ctx_t * ctx,const char ** attrs)91 void * lv_xml_create_from_ctx(lv_obj_t * parent, lv_xml_component_ctx_t * parent_ctx, lv_xml_component_ctx_t * ctx,
92 const char ** attrs)
93 {
94 /* Initialize the parser state */
95 lv_xml_parser_state_t state;
96 lv_xml_parser_state_init(&state);
97 state.ctx = *ctx;
98 state.parent = parent;
99 state.parent_attrs = attrs;
100 state.parent_ctx = parent_ctx;
101
102 lv_obj_t ** parent_node = lv_ll_ins_head(&state.parent_ll);
103 *parent_node = parent;
104
105 /* Create an XML parser and set handlers */
106 XML_Parser parser = XML_ParserCreate(NULL);
107 XML_SetUserData(parser, &state);
108 XML_SetElementHandler(parser, view_start_element_handler, view_end_element_handler);
109
110 /* Parse the XML */
111 if(XML_Parse(parser, ctx->view_def, lv_strlen(ctx->view_def), XML_TRUE) == XML_STATUS_ERROR) {
112 LV_LOG_WARN("XML parsing error: %s on line %lu", XML_ErrorString(XML_GetErrorCode(parser)),
113 XML_GetCurrentLineNumber(parser));
114 XML_ParserFree(parser);
115 return NULL;
116 }
117
118 state.item = state.view;
119
120 if(attrs) {
121 ctx->root_widget->apply_cb(&state, attrs);
122 }
123
124 lv_ll_clear(&state.parent_ll);
125 XML_ParserFree(parser);
126
127 return state.view;
128 }
129
lv_xml_create(lv_obj_t * parent,const char * name,const char ** attrs)130 void * lv_xml_create(lv_obj_t * parent, const char * name, const char ** attrs)
131 {
132 lv_obj_t * item = NULL;
133
134 /* Select the widget specific parser type based on the name */
135 lv_widget_processor_t * p = lv_xml_widget_get_processor(name);
136 if(p) {
137 lv_xml_parser_state_t state;
138 lv_xml_parser_state_init(&state);
139 state.parent = parent;
140 state.item = p->create_cb(&state, attrs);
141 if(attrs) {
142 p->apply_cb(&state, attrs);
143 }
144 return state.item;
145 }
146
147 lv_xml_component_ctx_t * ctx = lv_xml_component_get_ctx(name);
148 if(ctx) {
149 item = lv_xml_create_from_ctx(parent, NULL, ctx, attrs);
150 return item;
151 }
152
153 /* If it isn't a component either then it is unknown */
154 LV_LOG_WARN("'%s' in not a known widget, element, or component", name);
155 return NULL;
156 }
157
158
lv_xml_register_font(const char * name,const lv_font_t * font)159 lv_result_t lv_xml_register_font(const char * name, const lv_font_t * font)
160 {
161 lv_xml_font_t * f = lv_ll_ins_head(&font_ll);
162 f->name = lv_strdup(name);
163 f->font = font;
164
165 return LV_RESULT_OK;
166 }
167
lv_xml_get_font(const char * name)168 const lv_font_t * lv_xml_get_font(const char * name)
169 {
170 lv_xml_font_t * f;
171 LV_LL_READ(&font_ll, f) {
172 if(lv_streq(f->name, name)) return f->font;
173 }
174
175 return NULL;
176 }
177
lv_xml_register_image(const char * name,const void * src)178 lv_result_t lv_xml_register_image(const char * name, const void * src)
179 {
180 lv_xml_image_t * img = lv_ll_ins_head(&image_ll);
181 img->name = lv_strdup(name);
182 if(lv_image_src_get_type(src) == LV_IMAGE_SRC_FILE) {
183 img->src = lv_strdup(src);
184 }
185 else {
186 img->src = src;
187 }
188
189 return LV_RESULT_OK;
190 }
191
lv_xml_get_image(const char * name)192 const void * lv_xml_get_image(const char * name)
193 {
194 lv_xml_image_t * img;
195 LV_LL_READ(&image_ll, img) {
196 if(lv_streq(img->name, name)) return img->src;
197 }
198
199 return NULL;
200 }
201
202 /**********************
203 * STATIC FUNCTIONS
204 **********************/
205
get_param_type(lv_xml_component_ctx_t * ctx,const char * name)206 static const char * get_param_type(lv_xml_component_ctx_t * ctx, const char * name)
207 {
208 lv_xml_param_t * p;
209 LV_LL_READ(&ctx->param_ll, p) {
210 if(lv_streq(p->name, name)) return p->type;
211 }
212 return NULL;
213 }
214
get_param_default(lv_xml_component_ctx_t * ctx,const char * name)215 static const char * get_param_default(lv_xml_component_ctx_t * ctx, const char * name)
216 {
217 lv_xml_param_t * p;
218 LV_LL_READ(&ctx->param_ll, p) {
219 if(lv_streq(p->name, name)) return p->def;
220 }
221 return NULL;
222 }
223
resolve_params(lv_xml_component_ctx_t * item_ctx,lv_xml_component_ctx_t * parent_ctx,const char ** item_attrs,const char ** parent_attrs)224 static void resolve_params(lv_xml_component_ctx_t * item_ctx, lv_xml_component_ctx_t * parent_ctx,
225 const char ** item_attrs, const char ** parent_attrs)
226 {
227 uint32_t i;
228 for(i = 0; item_attrs[i]; i += 2) {
229 const char * name = item_attrs[i];
230 const char * value = item_attrs[i + 1];
231 if(lv_streq(name, "styles")) continue; /*Styles will handle it themselves*/
232 if(value[0] == '$') {
233 /*E.g. the ${my_color} value is the my_color attribute name on the parent*/
234 const char * name_clean = &value[1]; /*skips `$`*/
235
236 const char * type = get_param_type(item_ctx, name_clean);
237 if(type == NULL) {
238 LV_LOG_WARN("'%s' parameter is not defined on '%s'", name_clean, item_ctx->name);
239 }
240 const char * ext_value = lv_xml_get_value_of(parent_attrs, name_clean);
241 if(ext_value) {
242 /*If the value is not resolved earlier (e.g. it's a top level element created manually)
243 * use the default value*/
244 if(ext_value[0] == '#' || ext_value[0] == '$') {
245 ext_value = get_param_default(item_ctx, name_clean);
246 }
247 else if(lv_streq(type, "style")) {
248 lv_xml_style_t * s = lv_xml_get_style_by_name(parent_ctx, ext_value);
249 ext_value = s->long_name;
250 }
251 }
252 else {
253 /*If the API attribute is not provide don't set it*/
254 ext_value = get_param_default(item_ctx, name_clean);
255 }
256 if(ext_value) {
257 item_attrs[i + 1] = ext_value;
258 }
259 else {
260 /*Not set and no default value either
261 *Don't set this property*/
262 item_attrs[i] = "";
263 item_attrs[i + 1] = "";
264 }
265 }
266 }
267 }
268
resolve_consts(const char ** item_attrs,lv_xml_component_ctx_t * ctx)269 static void resolve_consts(const char ** item_attrs, lv_xml_component_ctx_t * ctx)
270 {
271 uint32_t i;
272 for(i = 0; item_attrs[i]; i += 2) {
273 const char * name = item_attrs[i];
274 const char * value = item_attrs[i + 1];
275 if(lv_streq(name, "styles")) continue; /*Styles will handle it themselves*/
276 if(value[0] == '#') {
277 const char * value_clean = &value[1];
278
279 lv_xml_const_t * c;
280 LV_LL_READ(&ctx->const_ll, c) {
281 if(lv_streq(c->name, value_clean)) {
282 item_attrs[i + 1] = c->value;
283 break;
284 }
285 }
286
287 /*If the const attribute is not provide don't set it*/
288 if(c == NULL) {
289 item_attrs[i] = "";
290 item_attrs[i + 1] = "";
291 }
292 }
293 }
294 }
295
view_start_element_handler(void * user_data,const char * name,const char ** attrs)296 static void view_start_element_handler(void * user_data, const char * name, const char ** attrs)
297 {
298 lv_xml_parser_state_t * state = (lv_xml_parser_state_t *)user_data;
299 bool is_view = false;
300
301 if(lv_streq(name, "view")) {
302 const char * extends = lv_xml_get_value_of(attrs, "extends");
303 name = extends ? extends : "lv_obj";
304 is_view = true;
305 }
306
307 lv_obj_t ** current_parent_p = lv_ll_get_tail(&state->parent_ll);
308 if(current_parent_p == NULL) {
309 if(state->parent == NULL) {
310 LV_LOG_ERROR("There is no parent object available for %s. This also should never happen.", name);
311 return;
312 }
313 else {
314 current_parent_p = &state->parent;
315 }
316 }
317 else {
318 state->parent = *current_parent_p;
319 }
320
321 /*In `state->attrs` we have parameters of the component creation
322 *E.g. <my_button x="10" title="Hello"/>
323 *In `attrs` we have the attributes of child of the view.
324 *E.g. in `my_button` `<lv_label x="5" text="${title}".
325 *This function changes the pointers in the child attributes if the start with '$'
326 *with the corresponding parameter. E.g. "text", "${title}" -> "text", "Hello" */
327 resolve_params(&state->ctx, state->parent_ctx, attrs, state->parent_attrs);
328
329 resolve_consts(attrs, &state->ctx);
330
331 void * item = NULL;
332 /* Select the widget specific parser type based on the name */
333 lv_widget_processor_t * p = lv_xml_widget_get_processor(name);
334 if(p) {
335 item = p->create_cb(state, attrs);
336 state->item = item;
337
338
339 /*If it's a widget remove all styles. E.g. if it extends an `lv_button`
340 *now it has the button theme styles. However if it were a real widget
341 *it had e.g. `my_widget_class` so the button's theme wouldn't apply on it.
342 *Removing the style will ensure a better preview*/
343 if(state->ctx.is_widget && is_view) lv_obj_remove_style_all(item);
344
345 /*Apply the attributes from e.g. `<lv_slider value="30" x="20">`*/
346 if(item) {
347 p->apply_cb(state, attrs);
348 }
349 }
350
351 /* If not a widget, check if it is a component */
352 if(item == NULL) {
353 item = lv_xml_component_process(state, name, attrs);
354 state->item = item;
355 }
356
357 /* If it isn't a component either then it is unknown */
358 if(item == NULL) {
359 LV_LOG_WARN("'%s' in not a known widget, element, or component", name);
360 return;
361 }
362
363 void ** new_parent = lv_ll_ins_tail(&state->parent_ll);
364 *new_parent = item;
365
366 if(is_view) {
367 state->view = item;
368 }
369 }
370
view_end_element_handler(void * user_data,const char * name)371 static void view_end_element_handler(void * user_data, const char * name)
372 {
373 LV_UNUSED(name);
374
375 lv_xml_parser_state_t * state = (lv_xml_parser_state_t *)user_data;
376
377 lv_obj_t ** current_parent = lv_ll_get_tail(&state->parent_ll);
378 if(current_parent) {
379 lv_ll_remove(&state->parent_ll, current_parent);
380 lv_free(current_parent);
381 }
382 }
383
384
register_builtin_fonts(void)385 static void register_builtin_fonts(void)
386 {
387 #if LV_FONT_MONTSERRAT_8
388 lv_xml_register_font("lv_montserrat_8", &lv_font_montserrat_8);
389 #endif
390
391 #if LV_FONT_MONTSERRAT_10
392 lv_xml_register_font("lv_montserrat_10", &lv_font_montserrat_10);
393 #endif
394
395 #if LV_FONT_MONTSERRAT_12
396 lv_xml_register_font("lv_montserrat_12", &lv_font_montserrat_12);
397 #endif
398
399 #if LV_FONT_MONTSERRAT_14
400 lv_xml_register_font("lv_montserrat_14", &lv_font_montserrat_14);
401 #endif
402
403 #if LV_FONT_MONTSERRAT_16
404 lv_xml_register_font("lv_montserrat_16", &lv_font_montserrat_16);
405 #endif
406
407 #if LV_FONT_MONTSERRAT_18
408 lv_xml_register_font("lv_montserrat_18", &lv_font_montserrat_18);
409 #endif
410
411 #if LV_FONT_MONTSERRAT_20
412 lv_xml_register_font("lv_montserrat_20", &lv_font_montserrat_20);
413 #endif
414
415 #if LV_FONT_MONTSERRAT_22
416 lv_xml_register_font("lv_montserrat_22", &lv_font_montserrat_22);
417 #endif
418
419 #if LV_FONT_MONTSERRAT_24
420 lv_xml_register_font("lv_montserrat_24", &lv_font_montserrat_24);
421 #endif
422
423 #if LV_FONT_MONTSERRAT_26
424 lv_xml_register_font("lv_montserrat_26", &lv_font_montserrat_26);
425 #endif
426
427 #if LV_FONT_MONTSERRAT_28
428 lv_xml_register_font("lv_montserrat_28", &lv_font_montserrat_28);
429 #endif
430
431 #if LV_FONT_MONTSERRAT_30
432 lv_xml_register_font("lv_montserrat_30", &lv_font_montserrat_30);
433 #endif
434
435 #if LV_FONT_MONTSERRAT_32
436 lv_xml_register_font("lv_montserrat_32", &lv_font_montserrat_32);
437 #endif
438
439 #if LV_FONT_MONTSERRAT_34
440 lv_xml_register_font("lv_montserrat_34", &lv_font_montserrat_34);
441 #endif
442
443 #if LV_FONT_MONTSERRAT_36
444 lv_xml_register_font("lv_montserrat_36", &lv_font_montserrat_36);
445 #endif
446
447 #if LV_FONT_MONTSERRAT_38
448 lv_xml_register_font("lv_montserrat_38", &lv_font_montserrat_38);
449 #endif
450
451 #if LV_FONT_MONTSERRAT_40
452 lv_xml_register_font("lv_montserrat_40", &lv_font_montserrat_40);
453 #endif
454
455 #if LV_FONT_MONTSERRAT_42
456 lv_xml_register_font("lv_montserrat_42", &lv_font_montserrat_42);
457 #endif
458
459 #if LV_FONT_MONTSERRAT_44
460 lv_xml_register_font("lv_montserrat_44", &lv_font_montserrat_44);
461 #endif
462
463 #if LV_FONT_MONTSERRAT_46
464 lv_xml_register_font("lv_montserrat_46", &lv_font_montserrat_46);
465 #endif
466
467 #if LV_FONT_MONTSERRAT_48
468 lv_xml_register_font("lv_montserrat_48", &lv_font_montserrat_48);
469 #endif
470
471 #if LV_FONT_UNSCII_8
472 lv_xml_register_font("lv_unscii_8", &lv_font_unscii_8);
473 #endif
474
475 #if LV_FONT_UNSCII_16
476 lv_xml_register_font("lv_unscii_16", &lv_font_unscii_16);
477 #endif
478
479 #if LV_FONT_SIMSUN_16_CJK
480 lv_xml_register_font("lv_simsun_cjk_16", &lv_font_simsun_16_cjk);
481 #endif
482
483 #if LV_FONT_DEJAVU_16_PERSIAN_HEBREW
484 lv_xml_register_font("lv_font_dejavu_16_persian_hebrew", &lv_font_dejavu_16_persian_hebrew);
485 #endif
486 }
487
488 #endif /* LV_USE_XML */
489