1 /**
2  * @file lv_table.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include "lv_table.h"
10 #if LV_USE_TABLE != 0
11 
12 #include "../lv_misc/lv_txt.h"
13 #include "../lv_misc/lv_math.h"
14 #include "../lv_draw/lv_draw_label.h"
15 #include "../lv_themes/lv_theme.h"
16 
17 /*********************
18  *      DEFINES
19  *********************/
20 
21 /**********************
22  *      TYPEDEFS
23  **********************/
24 
25 /**********************
26  *  STATIC PROTOTYPES
27  **********************/
28 static bool lv_table_design(lv_obj_t * table, const lv_area_t * mask, lv_design_mode_t mode);
29 static lv_res_t lv_table_signal(lv_obj_t * table, lv_signal_t sign, void * param);
30 static lv_coord_t get_row_height(lv_obj_t * table, uint16_t row_id);
31 static void refr_size(lv_obj_t * table);
32 
33 /**********************
34  *  STATIC VARIABLES
35  **********************/
36 static lv_signal_cb_t ancestor_signal;
37 static lv_design_cb_t ancestor_scrl_design;
38 
39 /**********************
40  *      MACROS
41  **********************/
42 
43 /**********************
44  *   GLOBAL FUNCTIONS
45  **********************/
46 
47 /**
48  * Create a table object
49  * @param par pointer to an object, it will be the parent of the new table
50  * @param copy pointer to a table object, if not NULL then the new object will be copied from it
51  * @return pointer to the created table
52  */
lv_table_create(lv_obj_t * par,const lv_obj_t * copy)53 lv_obj_t * lv_table_create(lv_obj_t * par, const lv_obj_t * copy)
54 {
55     LV_LOG_TRACE("table create started");
56 
57     /*Create the ancestor of table*/
58     lv_obj_t * new_table = lv_obj_create(par, copy);
59     lv_mem_assert(new_table);
60     if(new_table == NULL) return NULL;
61 
62     /*Allocate the table type specific extended data*/
63     lv_table_ext_t * ext = lv_obj_allocate_ext_attr(new_table, sizeof(lv_table_ext_t));
64     lv_mem_assert(ext);
65     if(ext == NULL) return NULL;
66     if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_cb(new_table);
67     if(ancestor_scrl_design == NULL) ancestor_scrl_design = lv_obj_get_design_cb(new_table);
68 
69     /*Initialize the allocated 'ext' */
70     ext->cell_data     = NULL;
71     ext->cell_style[0] = &lv_style_plain;
72     ext->cell_style[1] = &lv_style_plain;
73     ext->cell_style[2] = &lv_style_plain;
74     ext->cell_style[3] = &lv_style_plain;
75     ext->col_cnt       = 0;
76     ext->row_cnt       = 0;
77 
78     uint16_t i;
79     for(i = 0; i < LV_TABLE_COL_MAX; i++) {
80         ext->col_w[i] = LV_DPI;
81     }
82 
83     /*The signal and design functions are not copied so set them here*/
84     lv_obj_set_signal_cb(new_table, lv_table_signal);
85     lv_obj_set_design_cb(new_table, lv_table_design);
86 
87     /*Init the new table table*/
88     if(copy == NULL) {
89         /*Set the default styles*/
90         lv_theme_t * th = lv_theme_get_current();
91         if(th) {
92             lv_table_set_style(new_table, LV_TABLE_STYLE_BG, th->style.table.bg);
93             lv_table_set_style(new_table, LV_TABLE_STYLE_CELL1, th->style.table.cell);
94             lv_table_set_style(new_table, LV_TABLE_STYLE_CELL2, th->style.table.cell);
95             lv_table_set_style(new_table, LV_TABLE_STYLE_CELL3, th->style.table.cell);
96             lv_table_set_style(new_table, LV_TABLE_STYLE_CELL4, th->style.table.cell);
97         } else {
98             lv_table_set_style(new_table, LV_TABLE_STYLE_BG, &lv_style_plain_color);
99         }
100         lv_obj_set_click(new_table, false); /*Can be removed if click support is added*/
101     }
102     /*Copy an existing table*/
103     else {
104         lv_table_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
105         ext->cell_style[0]        = copy_ext->cell_style[0];
106         ext->cell_style[1]        = copy_ext->cell_style[1];
107         ext->cell_style[2]        = copy_ext->cell_style[2];
108         ext->cell_style[3]        = copy_ext->cell_style[3];
109         ext->col_cnt              = copy_ext->col_cnt;
110         ext->row_cnt              = copy_ext->row_cnt;
111 
112         /*Refresh the style with new signal function*/
113         lv_obj_refresh_style(new_table);
114     }
115 
116     LV_LOG_INFO("table created");
117 
118     return new_table;
119 }
120 
121 /*=====================
122  * Setter functions
123  *====================*/
124 
125 /**
126  * Set the value of a cell.
127  * @param table pointer to a Table object
128  * @param row id of the row [0 .. row_cnt -1]
129  * @param col id of the column [0 .. col_cnt -1]
130  * @param txt text to display in the cell. It will be copied and saved so this variable is not
131  * required after this function call.
132  */
lv_table_set_cell_value(lv_obj_t * table,uint16_t row,uint16_t col,const char * txt)133 void lv_table_set_cell_value(lv_obj_t * table, uint16_t row, uint16_t col, const char * txt)
134 {
135     lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
136     if(row >= ext->row_cnt || col >= ext->col_cnt) {
137         LV_LOG_WARN("lv_table_set_cell_value: invalid row or column");
138         return;
139     }
140     uint32_t cell = row * ext->col_cnt + col;
141     lv_table_cell_format_t format;
142 
143     /*Save the format byte*/
144     if(ext->cell_data[cell]) {
145         format.format_byte = ext->cell_data[cell][0];
146     }
147     /*Initialize the format byte*/
148     else {
149         format.s.align       = LV_LABEL_ALIGN_LEFT;
150         format.s.right_merge = 0;
151         format.s.type        = 0;
152         format.s.crop        = 0;
153     }
154 
155     ext->cell_data[cell] = lv_mem_realloc(ext->cell_data[cell], strlen(txt) + 2); /*+1: trailing '\0; +1: format byte*/
156     strcpy(ext->cell_data[cell] + 1, txt);                                        /*Leave the format byte*/
157     ext->cell_data[cell][0] = format.format_byte;
158     refr_size(table);
159 }
160 
161 /**
162  * Set the number of rows
163  * @param table table pointer to a Table object
164  * @param row_cnt number of rows
165  */
lv_table_set_row_cnt(lv_obj_t * table,uint16_t row_cnt)166 void lv_table_set_row_cnt(lv_obj_t * table, uint16_t row_cnt)
167 {
168     lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
169     uint16_t old_row_cnt = ext->row_cnt;
170     ext->row_cnt         = row_cnt;
171 
172     if(ext->row_cnt > 0 && ext->col_cnt > 0) {
173         ext->cell_data = lv_mem_realloc(ext->cell_data, ext->row_cnt * ext->col_cnt * sizeof(char *));
174 
175         /*Initilize the new fields*/
176         if(old_row_cnt < row_cnt) {
177             uint16_t old_cell_cnt = old_row_cnt * ext->col_cnt;
178             uint32_t new_cell_cnt = ext->col_cnt * ext->row_cnt;
179             memset(&ext->cell_data[old_cell_cnt], 0, (new_cell_cnt - old_cell_cnt) * sizeof(ext->cell_data[0]));
180         }
181     } else {
182         lv_mem_free(ext->cell_data);
183         ext->cell_data = NULL;
184     }
185 
186     refr_size(table);
187 }
188 
189 /**
190  * Set the number of columns
191  * @param table table pointer to a Table object
192  * @param col_cnt number of columns. Must be < LV_TABLE_COL_MAX
193  */
lv_table_set_col_cnt(lv_obj_t * table,uint16_t col_cnt)194 void lv_table_set_col_cnt(lv_obj_t * table, uint16_t col_cnt)
195 {
196 
197     if(col_cnt >= LV_TABLE_COL_MAX) {
198         LV_LOG_WARN("lv_table_set_col_cnt: too many columns. Must be < LV_TABLE_COL_MAX.");
199         return;
200     }
201 
202     lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
203     uint16_t old_col_cnt = ext->col_cnt;
204     ext->col_cnt         = col_cnt;
205 
206     if(ext->row_cnt > 0 && ext->col_cnt > 0) {
207         ext->cell_data = lv_mem_realloc(ext->cell_data, ext->row_cnt * ext->col_cnt * sizeof(char *));
208         /*Initilize the new fields*/
209         if(old_col_cnt < col_cnt) {
210             uint16_t old_cell_cnt = old_col_cnt * ext->row_cnt;
211             uint32_t new_cell_cnt = ext->col_cnt * ext->row_cnt;
212             memset(&ext->cell_data[old_cell_cnt], 0, (new_cell_cnt - old_cell_cnt) * sizeof(ext->cell_data[0]));
213         }
214 
215     } else {
216         lv_mem_free(ext->cell_data);
217         ext->cell_data = NULL;
218     }
219     refr_size(table);
220 }
221 
222 /**
223  * Set the width of a column
224  * @param table table pointer to a Table object
225  * @param col_id id of the column [0 .. LV_TABLE_COL_MAX -1]
226  * @param w width of the column
227  */
lv_table_set_col_width(lv_obj_t * table,uint16_t col_id,lv_coord_t w)228 void lv_table_set_col_width(lv_obj_t * table, uint16_t col_id, lv_coord_t w)
229 {
230     if(col_id >= LV_TABLE_COL_MAX) {
231         LV_LOG_WARN("lv_table_set_col_width: too big 'col_id'. Must be < LV_TABLE_COL_MAX.");
232         return;
233     }
234 
235     lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
236     ext->col_w[col_id]   = w;
237     refr_size(table);
238 }
239 
240 /**
241  * Set the text align in a cell
242  * @param table pointer to a Table object
243  * @param row id of the row [0 .. row_cnt -1]
244  * @param col id of the column [0 .. col_cnt -1]
245  * @param align LV_LABEL_ALIGN_LEFT or LV_LABEL_ALIGN_CENTER or LV_LABEL_ALIGN_RIGHT
246  */
lv_table_set_cell_align(lv_obj_t * table,uint16_t row,uint16_t col,lv_label_align_t align)247 void lv_table_set_cell_align(lv_obj_t * table, uint16_t row, uint16_t col, lv_label_align_t align)
248 {
249     lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
250     if(row >= ext->row_cnt || col >= ext->col_cnt) {
251         LV_LOG_WARN("lv_table_set_cell_align: invalid row or column");
252         return;
253     }
254     uint32_t cell = row * ext->col_cnt + col;
255 
256     if(ext->cell_data[cell] == NULL) {
257         ext->cell_data[cell]    = lv_mem_alloc(2); /*+1: trailing '\0; +1: format byte*/
258         ext->cell_data[cell][0] = 0;
259         ext->cell_data[cell][1] = '\0';
260     }
261 
262     lv_table_cell_format_t format;
263     format.format_byte      = ext->cell_data[cell][0];
264     format.s.align          = align;
265     ext->cell_data[cell][0] = format.format_byte;
266 }
267 
268 /**
269  * Set the type of a cell.
270  * @param table pointer to a Table object
271  * @param row id of the row [0 .. row_cnt -1]
272  * @param col id of the column [0 .. col_cnt -1]
273  * @param type 1,2,3 or 4. The cell style will be chosen accordingly.
274  */
lv_table_set_cell_type(lv_obj_t * table,uint16_t row,uint16_t col,uint8_t type)275 void lv_table_set_cell_type(lv_obj_t * table, uint16_t row, uint16_t col, uint8_t type)
276 {
277     lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
278     if(row >= ext->row_cnt || col >= ext->col_cnt) {
279         LV_LOG_WARN("lv_table_set_cell_type: invalid row or column");
280         return;
281     }
282     uint32_t cell = row * ext->col_cnt + col;
283 
284     if(ext->cell_data[cell] == NULL) {
285         ext->cell_data[cell]    = lv_mem_alloc(2); /*+1: trailing '\0; +1: format byte*/
286         ext->cell_data[cell][0] = 0;
287         ext->cell_data[cell][1] = '\0';
288     }
289 
290     if(type > 0) type--; /*User gives 1,2,3,4 but easier to handle 0, 1, 2, 3*/
291     if(type >= LV_TABLE_CELL_STYLE_CNT) type = LV_TABLE_CELL_STYLE_CNT - 1;
292 
293     lv_table_cell_format_t format;
294     format.format_byte      = ext->cell_data[cell][0];
295     format.s.type           = type;
296     ext->cell_data[cell][0] = format.format_byte;
297 }
298 
299 /**
300  * Set the cell crop. (Don't adjust the height of the cell according to its content)
301  * @param table pointer to a Table object
302  * @param row id of the row [0 .. row_cnt -1]
303  * @param col id of the column [0 .. col_cnt -1]
304  * @param crop true: crop the cell content; false: set the cell height to the content.
305  */
lv_table_set_cell_crop(lv_obj_t * table,uint16_t row,uint16_t col,bool crop)306 void lv_table_set_cell_crop(lv_obj_t * table, uint16_t row, uint16_t col, bool crop)
307 {
308     lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
309     if(row >= ext->row_cnt || col >= ext->col_cnt) {
310         LV_LOG_WARN("lv_table_set_cell_crop: invalid row or column");
311         return;
312     }
313     uint32_t cell = row * ext->col_cnt + col;
314 
315     if(ext->cell_data[cell] == NULL) {
316         ext->cell_data[cell]    = lv_mem_alloc(2); /*+1: trailing '\0; +1: format byte*/
317         ext->cell_data[cell][0] = 0;
318         ext->cell_data[cell][1] = '\0';
319     }
320 
321     lv_table_cell_format_t format;
322     format.format_byte      = ext->cell_data[cell][0];
323     format.s.crop           = crop;
324     ext->cell_data[cell][0] = format.format_byte;
325 }
326 
327 /**
328  * Merge a cell with the right neighbor. The value of the cell to the right won't be displayed.
329  * @param table table pointer to a Table object
330  * @param row id of the row [0 .. row_cnt -1]
331  * @param col id of the column [0 .. col_cnt -1]
332  * @param en true: merge right; false: don't merge right
333  */
lv_table_set_cell_merge_right(lv_obj_t * table,uint16_t row,uint16_t col,bool en)334 void lv_table_set_cell_merge_right(lv_obj_t * table, uint16_t row, uint16_t col, bool en)
335 {
336     lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
337     if(row >= ext->row_cnt || col >= ext->col_cnt) {
338         LV_LOG_WARN("lv_table_set_cell_merge_right: invalid row or column");
339         return;
340     }
341 
342     uint32_t cell = row * ext->col_cnt + col;
343 
344     if(ext->cell_data[cell] == NULL) {
345         ext->cell_data[cell]    = lv_mem_alloc(2); /*+1: trailing '\0; +1: format byte*/
346         ext->cell_data[cell][0] = 0;
347         ext->cell_data[cell][1] = '\0';
348     }
349 
350     lv_table_cell_format_t format;
351     format.format_byte      = ext->cell_data[cell][0];
352     format.s.right_merge    = en ? 1 : 0;
353     ext->cell_data[cell][0] = format.format_byte;
354     refr_size(table);
355 }
356 
357 /**
358  * Set a style of a table.
359  * @param table pointer to table object
360  * @param type which style should be set
361  * @param style pointer to a style
362  */
lv_table_set_style(lv_obj_t * table,lv_table_style_t type,const lv_style_t * style)363 void lv_table_set_style(lv_obj_t * table, lv_table_style_t type, const lv_style_t * style)
364 {
365     lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
366 
367     switch(type) {
368         case LV_TABLE_STYLE_BG:
369             lv_obj_set_style(table, style);
370             refr_size(table);
371             break;
372         case LV_TABLE_STYLE_CELL1:
373             ext->cell_style[0] = style;
374             refr_size(table);
375             break;
376         case LV_TABLE_STYLE_CELL2:
377             ext->cell_style[1] = style;
378             refr_size(table);
379             break;
380         case LV_TABLE_STYLE_CELL3:
381             ext->cell_style[2] = style;
382             refr_size(table);
383             break;
384         case LV_TABLE_STYLE_CELL4:
385             ext->cell_style[3] = style;
386             refr_size(table);
387             break;
388     }
389 }
390 
391 /*=====================
392  * Getter functions
393  *====================*/
394 
395 /**
396  * Get the value of a cell.
397  * @param table pointer to a Table object
398  * @param row id of the row [0 .. row_cnt -1]
399  * @param col id of the column [0 .. col_cnt -1]
400  * @return text in the cell
401  */
lv_table_get_cell_value(lv_obj_t * table,uint16_t row,uint16_t col)402 const char * lv_table_get_cell_value(lv_obj_t * table, uint16_t row, uint16_t col)
403 {
404     lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
405     if(row >= ext->row_cnt || col >= ext->col_cnt) {
406         LV_LOG_WARN("lv_table_set_cell_value: invalid row or column");
407         return "";
408     }
409     uint32_t cell = row * ext->col_cnt + col;
410 
411     if(ext->cell_data[cell] == NULL) return "";
412 
413     return &ext->cell_data[cell][1]; /*Skip the format byte*/
414 }
415 
416 /**
417  * Get the number of rows.
418  * @param table table pointer to a Table object
419  * @return number of rows.
420  */
lv_table_get_row_cnt(lv_obj_t * table)421 uint16_t lv_table_get_row_cnt(lv_obj_t * table)
422 {
423     lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
424     return ext->row_cnt;
425 }
426 
427 /**
428  * Get the number of columns.
429  * @param table table pointer to a Table object
430  * @return number of columns.
431  */
lv_table_get_col_cnt(lv_obj_t * table)432 uint16_t lv_table_get_col_cnt(lv_obj_t * table)
433 {
434     lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
435     return ext->col_cnt;
436 }
437 
438 /**
439  * Get the width of a column
440  * @param table table pointer to a Table object
441  * @param col_id id of the column [0 .. LV_TABLE_COL_MAX -1]
442  * @return width of the column
443  */
lv_table_get_col_width(lv_obj_t * table,uint16_t col_id)444 lv_coord_t lv_table_get_col_width(lv_obj_t * table, uint16_t col_id)
445 {
446     if(col_id >= LV_TABLE_COL_MAX) {
447         LV_LOG_WARN("lv_table_set_col_width: too big 'col_id'. Must be < LV_TABLE_COL_MAX.");
448         return 0;
449     }
450 
451     lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
452     return ext->col_w[col_id];
453 }
454 
455 /**
456  * Get the text align of a cell
457  * @param table pointer to a Table object
458  * @param row id of the row [0 .. row_cnt -1]
459  * @param col id of the column [0 .. col_cnt -1]
460  * @return LV_LABEL_ALIGN_LEFT (default in case of error) or LV_LABEL_ALIGN_CENTER or
461  * LV_LABEL_ALIGN_RIGHT
462  */
lv_table_get_cell_align(lv_obj_t * table,uint16_t row,uint16_t col)463 lv_label_align_t lv_table_get_cell_align(lv_obj_t * table, uint16_t row, uint16_t col)
464 {
465     lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
466     if(row >= ext->row_cnt || col >= ext->col_cnt) {
467         LV_LOG_WARN("lv_table_set_cell_align: invalid row or column");
468         return LV_LABEL_ALIGN_LEFT; /*Just return with something*/
469     }
470     uint32_t cell = row * ext->col_cnt + col;
471 
472     if(ext->cell_data[cell] == NULL)
473         return LV_LABEL_ALIGN_LEFT; /*Just return with something*/
474     else {
475         lv_table_cell_format_t format;
476         format.format_byte = ext->cell_data[cell][0];
477         return format.s.align;
478     }
479 }
480 
481 /**
482  * Get the type of a cell
483  * @param table pointer to a Table object
484  * @param row id of the row [0 .. row_cnt -1]
485  * @param col id of the column [0 .. col_cnt -1]
486  * @return 1,2,3 or 4
487  */
lv_table_get_cell_type(lv_obj_t * table,uint16_t row,uint16_t col)488 lv_label_align_t lv_table_get_cell_type(lv_obj_t * table, uint16_t row, uint16_t col)
489 {
490     lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
491     if(row >= ext->row_cnt || col >= ext->col_cnt) {
492         LV_LOG_WARN("lv_table_get_cell_type: invalid row or column");
493         return 1; /*Just return with something*/
494     }
495     uint32_t cell = row * ext->col_cnt + col;
496 
497     if(ext->cell_data[cell] == NULL)
498         return 1; /*Just return with something*/
499     else {
500         lv_table_cell_format_t format;
501         format.format_byte = ext->cell_data[cell][0];
502         return format.s.type + 1; /*0,1,2,3 is stored but user sees 1,2,3,4*/
503     }
504 }
505 
506 /**
507  * Get the crop property of a cell
508  * @param table pointer to a Table object
509  * @param row id of the row [0 .. row_cnt -1]
510  * @param col id of the column [0 .. col_cnt -1]
511  * @return true: text crop enabled; false: disabled
512  */
lv_table_get_cell_crop(lv_obj_t * table,uint16_t row,uint16_t col)513 lv_label_align_t lv_table_get_cell_crop(lv_obj_t * table, uint16_t row, uint16_t col)
514 {
515     lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
516     if(row >= ext->row_cnt || col >= ext->col_cnt) {
517         LV_LOG_WARN("lv_table_get_cell_crop: invalid row or column");
518         return false; /*Just return with something*/
519     }
520     uint32_t cell = row * ext->col_cnt + col;
521 
522     if(ext->cell_data[cell] == NULL)
523         return false; /*Just return with something*/
524     else {
525         lv_table_cell_format_t format;
526         format.format_byte = ext->cell_data[cell][0];
527         return format.s.crop;
528     }
529 }
530 
531 /**
532  * Get the cell merge attribute.
533  * @param table table pointer to a Table object
534  * @param row id of the row [0 .. row_cnt -1]
535  * @param col id of the column [0 .. col_cnt -1]
536  * @return true: merge right; false: don't merge right
537  */
lv_table_get_cell_merge_right(lv_obj_t * table,uint16_t row,uint16_t col)538 bool lv_table_get_cell_merge_right(lv_obj_t * table, uint16_t row, uint16_t col)
539 {
540     lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
541     if(row >= ext->row_cnt || col >= ext->col_cnt) {
542         LV_LOG_WARN("lv_table_get_cell_merge_right: invalid row or column");
543         return false;
544     }
545 
546     uint32_t cell = row * ext->col_cnt + col;
547 
548     if(ext->cell_data[cell] == NULL)
549         return false;
550     else {
551         lv_table_cell_format_t format;
552         format.format_byte = ext->cell_data[cell][0];
553         return format.s.right_merge ? true : false;
554     }
555 }
556 
557 /**
558  * Get style of a table.
559  * @param table pointer to table object
560  * @param type which style should be get
561  * @return style pointer to the style
562  */
lv_table_get_style(const lv_obj_t * table,lv_table_style_t type)563 const lv_style_t * lv_table_get_style(const lv_obj_t * table, lv_table_style_t type)
564 {
565     lv_table_ext_t * ext     = lv_obj_get_ext_attr(table);
566     const lv_style_t * style = NULL;
567 
568     switch(type) {
569         case LV_TABLE_STYLE_BG: style = lv_obj_get_style(table); break;
570         case LV_TABLE_STYLE_CELL1: style = ext->cell_style[0]; break;
571         case LV_TABLE_STYLE_CELL2: style = ext->cell_style[1]; break;
572         case LV_TABLE_STYLE_CELL3: style = ext->cell_style[2]; break;
573         case LV_TABLE_STYLE_CELL4: style = ext->cell_style[3]; break;
574         default: return NULL;
575     }
576 
577     return style;
578 }
579 
580 /**********************
581  *   STATIC FUNCTIONS
582  **********************/
583 
584 /**
585  * Handle the drawing related tasks of the tables
586  * @param table pointer to an object
587  * @param mask the object will be drawn only in this area
588  * @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area
589  *                                  (return 'true' if yes)
590  *             LV_DESIGN_DRAW: draw the object (always return 'true')
591  *             LV_DESIGN_DRAW_POST: drawing after every children are drawn
592  * @param return true/false, depends on 'mode'
593  */
lv_table_design(lv_obj_t * table,const lv_area_t * mask,lv_design_mode_t mode)594 static bool lv_table_design(lv_obj_t * table, const lv_area_t * mask, lv_design_mode_t mode)
595 {
596     /*Return false if the object is not covers the mask_p area*/
597     if(mode == LV_DESIGN_COVER_CHK) {
598         return false;
599     }
600     /*Draw the object*/
601     else if(mode == LV_DESIGN_DRAW_MAIN) {
602         ancestor_scrl_design(table, mask, mode);
603 
604         lv_table_ext_t * ext        = lv_obj_get_ext_attr(table);
605         const lv_style_t * bg_style = lv_obj_get_style(table);
606         const lv_style_t * cell_style;
607         lv_coord_t h_row;
608         lv_point_t txt_size;
609         lv_area_t cell_area;
610         lv_area_t txt_area;
611         lv_txt_flag_t txt_flags;
612         lv_opa_t opa_scale = lv_obj_get_opa_scale(table);
613 
614         uint16_t col;
615         uint16_t row;
616         uint16_t cell = 0;
617 
618         cell_area.y2 = table->coords.y1 + bg_style->body.padding.top;
619         for(row = 0; row < ext->row_cnt; row++) {
620             h_row = get_row_height(table, row);
621 
622             cell_area.y1 = cell_area.y2;
623             cell_area.y2 = cell_area.y1 + h_row;
624 
625             cell_area.x2 = table->coords.x1 + bg_style->body.padding.left;
626 
627             for(col = 0; col < ext->col_cnt; col++) {
628 
629                 lv_table_cell_format_t format;
630                 if(ext->cell_data[cell]) {
631                     format.format_byte = ext->cell_data[cell][0];
632                 } else {
633                     format.s.right_merge = 0;
634                     format.s.align       = LV_LABEL_ALIGN_LEFT;
635                     format.s.type        = 0;
636                     format.s.crop        = 1;
637                 }
638 
639                 cell_style   = ext->cell_style[format.s.type];
640                 cell_area.x1 = cell_area.x2;
641                 cell_area.x2 = cell_area.x1 + ext->col_w[col];
642 
643                 uint16_t col_merge = 0;
644                 for(col_merge = 0; col_merge + col < ext->col_cnt - 1; col_merge++) {
645 
646                     if(ext->cell_data[cell + col_merge] != NULL) {
647                         format.format_byte = ext->cell_data[cell + col_merge][0];
648                         if(format.s.right_merge)
649                             cell_area.x2 += ext->col_w[col + col_merge + 1];
650                         else
651                             break;
652                     } else {
653                         break;
654                     }
655                 }
656 
657                 lv_draw_rect(&cell_area, mask, cell_style, opa_scale);
658 
659                 if(ext->cell_data[cell]) {
660 
661                     txt_area.x1 = cell_area.x1 + cell_style->body.padding.left;
662                     txt_area.x2 = cell_area.x2 - cell_style->body.padding.right;
663                     txt_area.y1 = cell_area.y1 + cell_style->body.padding.top;
664                     txt_area.y2 = cell_area.y2 - cell_style->body.padding.bottom;
665                     /*Align the content to the middle if not cropped*/
666                     if(format.s.crop == 0) {
667                         txt_flags = LV_TXT_FLAG_NONE;
668                     } else {
669                         txt_flags = LV_TXT_FLAG_EXPAND;
670                     }
671 
672                     lv_txt_get_size(&txt_size, ext->cell_data[cell] + 1, cell_style->text.font,
673                                     cell_style->text.letter_space, cell_style->text.line_space,
674                                     lv_area_get_width(&txt_area), txt_flags);
675 
676                     /*Align the content to the middle if not cropped*/
677                     if(format.s.crop == 0) {
678                         txt_area.y1 = cell_area.y1 + h_row / 2 - txt_size.y / 2;
679                         txt_area.y2 = cell_area.y1 + h_row / 2 + txt_size.y / 2;
680                     }
681 
682                     switch(format.s.align) {
683                         default:
684                         case LV_LABEL_ALIGN_LEFT: txt_flags |= LV_TXT_FLAG_NONE; break;
685                         case LV_LABEL_ALIGN_RIGHT: txt_flags |= LV_TXT_FLAG_RIGHT; break;
686                         case LV_LABEL_ALIGN_CENTER: txt_flags |= LV_TXT_FLAG_CENTER; break;
687                     }
688 
689                     lv_area_t label_mask;
690                     bool label_mask_ok;
691                     label_mask_ok = lv_area_intersect(&label_mask, mask, &cell_area);
692                     if(label_mask_ok) {
693                         lv_draw_label(&txt_area, &label_mask, cell_style, opa_scale, ext->cell_data[cell] + 1,
694                                       txt_flags, NULL, -1, -1, NULL);
695                     }
696                     /*Draw lines after '\n's*/
697                     lv_point_t p1;
698                     lv_point_t p2;
699                     p1.x = cell_area.x1;
700                     p2.x = cell_area.x2;
701                     uint16_t i;
702                     for(i = 1; ext->cell_data[cell][i] != '\0'; i++) {
703                         if(ext->cell_data[cell][i] == '\n') {
704                             ext->cell_data[cell][i] = '\0';
705                             lv_txt_get_size(&txt_size, ext->cell_data[cell] + 1, cell_style->text.font,
706                                             cell_style->text.letter_space, cell_style->text.line_space,
707                                             lv_area_get_width(&txt_area), txt_flags);
708 
709                             p1.y = txt_area.y1 + txt_size.y + cell_style->text.line_space / 2;
710                             p2.y = txt_area.y1 + txt_size.y + cell_style->text.line_space / 2;
711                             lv_draw_line(&p1, &p2, mask, cell_style, opa_scale);
712 
713                             ext->cell_data[cell][i] = '\n';
714                         }
715                     }
716                 }
717 
718                 cell += col_merge + 1;
719                 col += col_merge;
720             }
721         }
722     }
723     /*Post draw when the children are drawn*/
724     else if(mode == LV_DESIGN_DRAW_POST) {
725     }
726 
727     return true;
728 }
729 
730 /**
731  * Signal function of the table
732  * @param table pointer to a table object
733  * @param sign a signal type from lv_signal_t enum
734  * @param param pointer to a signal specific variable
735  * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
736  */
lv_table_signal(lv_obj_t * table,lv_signal_t sign,void * param)737 static lv_res_t lv_table_signal(lv_obj_t * table, lv_signal_t sign, void * param)
738 {
739     lv_res_t res;
740 
741     /* Include the ancient signal function */
742     res = ancestor_signal(table, sign, param);
743     if(res != LV_RES_OK) return res;
744 
745     if(sign == LV_SIGNAL_CLEANUP) {
746         /*Free the cell texts*/
747         lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
748         uint16_t cell;
749         for(cell = 0; cell < ext->col_cnt * ext->row_cnt; cell++) {
750             if(ext->cell_data[cell]) {
751                 lv_mem_free(ext->cell_data[cell]);
752                 ext->cell_data[cell] = NULL;
753             }
754         }
755     } else if(sign == LV_SIGNAL_GET_TYPE) {
756         lv_obj_type_t * buf = param;
757         uint8_t i;
758         for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/
759             if(buf->type[i] == NULL) break;
760         }
761         buf->type[i] = "lv_table";
762     }
763 
764     return res;
765 }
766 
refr_size(lv_obj_t * table)767 static void refr_size(lv_obj_t * table)
768 {
769     lv_coord_t h = 0;
770     lv_coord_t w = 0;
771 
772     lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
773 
774     uint16_t i;
775     for(i = 0; i < ext->col_cnt; i++) {
776         w += ext->col_w[i];
777     }
778     for(i = 0; i < ext->row_cnt; i++) {
779         h += get_row_height(table, i);
780     }
781 
782     const lv_style_t * bg_style = lv_obj_get_style(table);
783 
784     w += bg_style->body.padding.left + bg_style->body.padding.right;
785     h += bg_style->body.padding.top + bg_style->body.padding.bottom;
786 
787     lv_obj_set_size(table, w + 1, h + 1);
788     lv_obj_invalidate(table);
789 }
790 
get_row_height(lv_obj_t * table,uint16_t row_id)791 static lv_coord_t get_row_height(lv_obj_t * table, uint16_t row_id)
792 {
793     lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
794     lv_point_t txt_size;
795     lv_coord_t txt_w;
796     const lv_style_t * cell_style;
797 
798     uint16_t row_start = row_id * ext->col_cnt;
799     uint16_t cell;
800     uint16_t col;
801     lv_coord_t h_max = lv_font_get_line_height(ext->cell_style[0]->text.font) + ext->cell_style[0]->body.padding.top +
802                        ext->cell_style[0]->body.padding.bottom;
803 
804     for(cell = row_start, col = 0; cell < row_start + ext->col_cnt; cell++, col++) {
805         if(ext->cell_data[cell] != NULL) {
806 
807             txt_w              = ext->col_w[col];
808             uint16_t col_merge = 0;
809             for(col_merge = 0; col_merge + col < ext->col_cnt - 1; col_merge++) {
810 
811                 if(ext->cell_data[cell + col_merge] != NULL) {
812                     lv_table_cell_format_t format;
813                     format.format_byte = ext->cell_data[cell + col_merge][0];
814                     if(format.s.right_merge)
815                         txt_w += ext->col_w[col + col_merge + 1];
816                     else
817                         break;
818                 } else {
819                     break;
820                 }
821             }
822 
823             lv_table_cell_format_t format;
824             format.format_byte = ext->cell_data[cell][0];
825             cell_style         = ext->cell_style[format.s.type];
826 
827             /*With text crop assume 1 line*/
828             if(format.s.crop) {
829                 h_max = LV_MATH_MAX(lv_font_get_line_height(cell_style->text.font) + cell_style->body.padding.top +
830                                         cell_style->body.padding.bottom,
831                                     h_max);
832             }
833             /*Without text crop calculate the height of the text in the cell*/
834             else {
835                 txt_w -= cell_style->body.padding.left + cell_style->body.padding.right;
836 
837                 lv_txt_get_size(&txt_size, ext->cell_data[cell] + 1, cell_style->text.font,
838                                 cell_style->text.letter_space, cell_style->text.line_space, txt_w, LV_TXT_FLAG_NONE);
839 
840                 h_max = LV_MATH_MAX(txt_size.y + cell_style->body.padding.top + cell_style->body.padding.bottom, h_max);
841                 cell += col_merge;
842                 col += col_merge;
843             }
844         }
845     }
846 
847     return h_max;
848 }
849 
850 #endif
851