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_debug.h"
13 #include "../lv_core/lv_indev.h"
14 #include "../lv_misc/lv_txt.h"
15 #include "../lv_misc/lv_math.h"
16 #include "../lv_draw/lv_draw_label.h"
17 #include "../lv_themes/lv_theme.h"
18 
19 /*********************
20  *      DEFINES
21  *********************/
22 #define LV_OBJX_NAME "lv_table"
23 
24 /**********************
25  *      TYPEDEFS
26  **********************/
27 
28 /**********************
29  *  STATIC PROTOTYPES
30  **********************/
31 static lv_design_res_t lv_table_design(lv_obj_t * table, const lv_area_t * clip_area, lv_design_mode_t mode);
32 static lv_res_t lv_table_signal(lv_obj_t * table, lv_signal_t sign, void * param);
33 static lv_style_list_t * lv_table_get_style(lv_obj_t * table, uint8_t part);
34 static lv_coord_t get_row_height(lv_obj_t * table, uint16_t row_id, const lv_font_t ** font,
35                                  lv_style_int_t * letter_space, lv_style_int_t * line_space,
36                                  lv_style_int_t * cell_left, lv_style_int_t * cell_right, lv_style_int_t * cell_top, lv_style_int_t * cell_bottom);
37 static void refr_size(lv_obj_t * table);
38 
39 /**********************
40  *  STATIC VARIABLES
41  **********************/
42 static lv_signal_cb_t ancestor_signal;
43 static lv_design_cb_t ancestor_design;
44 
45 /**********************
46  *      MACROS
47  **********************/
48 
49 /**********************
50  *   GLOBAL FUNCTIONS
51  **********************/
52 
53 /**
54  * Create a table object
55  * @param par pointer to an object, it will be the parent of the new table
56  * @param copy pointer to a table object, if not NULL then the new object will be copied from it
57  * @return pointer to the created table
58  */
lv_table_create(lv_obj_t * par,const lv_obj_t * copy)59 lv_obj_t * lv_table_create(lv_obj_t * par, const lv_obj_t * copy)
60 {
61     LV_LOG_TRACE("table create started");
62 
63     /*Create the ancestor of table*/
64     lv_obj_t * table = lv_obj_create(par, copy);
65     LV_ASSERT_MEM(table);
66     if(table == NULL) return NULL;
67 
68     /*Allocate the table type specific extended data*/
69     lv_table_ext_t * ext = lv_obj_allocate_ext_attr(table, sizeof(lv_table_ext_t));
70     LV_ASSERT_MEM(ext);
71     if(ext == NULL) {
72         lv_obj_del(table);
73         return NULL;
74     }
75 
76     if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_cb(table);
77     if(ancestor_design == NULL) ancestor_design = lv_obj_get_design_cb(table);
78 
79     /*Initialize the allocated 'ext' */
80     ext->cell_data     = NULL;
81     ext->col_cnt       = 0;
82     ext->row_cnt       = 0;
83     ext->row_h         = NULL;
84     ext->cell_types    = 1;
85 
86     uint16_t i;
87     for(i = 0; i < LV_TABLE_CELL_STYLE_CNT; i++) {
88         lv_style_list_init(&ext->cell_style[i]);
89     }
90 
91     for(i = 0; i < LV_TABLE_COL_MAX; i++) {
92         ext->col_w[i] = LV_DPI;
93     }
94 
95     /*The signal and design functions are not copied so set them here*/
96     lv_obj_set_signal_cb(table, lv_table_signal);
97     lv_obj_set_design_cb(table, lv_table_design);
98 
99     /*Init the new table table*/
100     if(copy == NULL) {
101         lv_theme_apply(table, LV_THEME_TABLE);
102     }
103     /*Copy an existing table*/
104     else {
105         lv_table_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
106         for(i = 0; i < LV_TABLE_CELL_STYLE_CNT; i++) {
107             lv_style_list_copy(&ext->cell_style[i], &copy_ext->cell_style[i]);
108         }
109         lv_table_set_row_cnt(table, copy_ext->row_cnt);
110         lv_table_set_col_cnt(table, copy_ext->col_cnt);
111 
112         /*Refresh the style with new signal function*/
113         lv_obj_refresh_style(table, LV_OBJ_PART_ALL, LV_STYLE_PROP_ALL);
114     }
115 
116     LV_LOG_INFO("table created");
117 
118     return 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_ASSERT_OBJ(table, LV_OBJX_NAME);
136     LV_ASSERT_NULL(txt);
137 
138     lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
139     if(col >= ext->col_cnt) {
140         LV_LOG_WARN("lv_table_set_cell_value: invalid column");
141         return;
142     }
143 
144     /*Auto expand*/
145     if(row >= ext->row_cnt) {
146         lv_table_set_row_cnt(table, row + 1);
147     }
148 
149     uint32_t cell = row * ext->col_cnt + col;
150     lv_table_cell_format_t format;
151 
152     /*Save the format byte*/
153     if(ext->cell_data[cell]) {
154         format.format_byte = ext->cell_data[cell][0];
155     }
156     /*Initialize the format byte*/
157     else {
158         lv_bidi_dir_t base_dir = lv_obj_get_base_dir(table);
159         if(base_dir == LV_BIDI_DIR_LTR) format.s.align = LV_LABEL_ALIGN_LEFT;
160         else if(base_dir == LV_BIDI_DIR_RTL) format.s.align = LV_LABEL_ALIGN_RIGHT;
161         else if(base_dir == LV_BIDI_DIR_AUTO)
162 #if LV_USE_BIDI
163             format.s.align = _lv_bidi_detect_base_dir(txt);
164 #else
165             format.s.align = LV_LABEL_ALIGN_LEFT;
166 #endif
167         format.s.right_merge = 0;
168         format.s.type        = 0;
169         format.s.crop        = 0;
170     }
171 
172     ext->cell_data[cell] = lv_mem_realloc(ext->cell_data[cell], strlen(txt) + 2); /*+1: trailing '\0; +1: format byte*/
173     LV_ASSERT_MEM(ext->cell_data[cell]);
174     if(ext->cell_data[cell] == NULL) return;
175 
176     strcpy(ext->cell_data[cell] + 1, txt);  /*+1 to skip the format byte*/
177 
178     ext->cell_data[cell][0] = format.format_byte;
179     refr_size(table);
180 }
181 
182 /**
183  * Set the number of rows
184  * @param table table pointer to a Table object
185  * @param row_cnt number of rows
186  */
lv_table_set_row_cnt(lv_obj_t * table,uint16_t row_cnt)187 void lv_table_set_row_cnt(lv_obj_t * table, uint16_t row_cnt)
188 {
189     LV_ASSERT_OBJ(table, LV_OBJX_NAME);
190 
191     lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
192     uint16_t old_row_cnt = ext->row_cnt;
193     ext->row_cnt         = row_cnt;
194 
195     if(ext->row_cnt > 0) {
196         ext->row_h = lv_mem_realloc(ext->row_h, ext->row_cnt * sizeof(ext->row_h[0]));
197         LV_ASSERT_MEM(ext->row_h);
198         if(ext->row_h == NULL) return;
199     }
200     else {
201         lv_mem_free(ext->row_h);
202         ext->row_h = NULL;
203     }
204 
205     if(ext->row_cnt > 0 && ext->col_cnt > 0) {
206         ext->cell_data = lv_mem_realloc(ext->cell_data, ext->row_cnt * ext->col_cnt * sizeof(char *));
207         LV_ASSERT_MEM(ext->cell_data);
208         if(ext->cell_data == NULL) return;
209 
210         /*Initialize the new fields*/
211         if(old_row_cnt < row_cnt) {
212             uint16_t old_cell_cnt = old_row_cnt * ext->col_cnt;
213             uint32_t new_cell_cnt = ext->col_cnt * ext->row_cnt;
214             _lv_memset_00(&ext->cell_data[old_cell_cnt], (new_cell_cnt - old_cell_cnt) * sizeof(ext->cell_data[0]));
215         }
216     }
217     else {
218         lv_mem_free(ext->cell_data);
219         ext->cell_data = NULL;
220     }
221 
222     refr_size(table);
223 }
224 
225 /**
226  * Set the number of columns
227  * @param table table pointer to a Table object
228  * @param col_cnt number of columns. Must be < LV_TABLE_COL_MAX
229  */
lv_table_set_col_cnt(lv_obj_t * table,uint16_t col_cnt)230 void lv_table_set_col_cnt(lv_obj_t * table, uint16_t col_cnt)
231 {
232     LV_ASSERT_OBJ(table, LV_OBJX_NAME);
233 
234     if(col_cnt >= LV_TABLE_COL_MAX) {
235         LV_LOG_WARN("lv_table_set_col_cnt: too many columns. Must be < LV_TABLE_COL_MAX.");
236         return;
237     }
238 
239     lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
240     uint16_t old_col_cnt = ext->col_cnt;
241     ext->col_cnt         = col_cnt;
242 
243     if(ext->row_cnt > 0 && ext->col_cnt > 0) {
244         ext->cell_data = lv_mem_realloc(ext->cell_data, ext->row_cnt * ext->col_cnt * sizeof(char *));
245         LV_ASSERT_MEM(ext->cell_data);
246         if(ext->cell_data == NULL) return;
247 
248         /*Initialize the new fields*/
249         if(old_col_cnt < col_cnt) {
250             uint16_t old_cell_cnt = old_col_cnt * ext->row_cnt;
251             uint32_t new_cell_cnt = ext->col_cnt * ext->row_cnt;
252             _lv_memset_00(&ext->cell_data[old_cell_cnt], (new_cell_cnt - old_cell_cnt) * sizeof(ext->cell_data[0]));
253         }
254 
255     }
256     else {
257         lv_mem_free(ext->cell_data);
258         ext->cell_data = NULL;
259     }
260     refr_size(table);
261 }
262 
263 /**
264  * Set the width of a column
265  * @param table table pointer to a Table object
266  * @param col_id id of the column [0 .. LV_TABLE_COL_MAX -1]
267  * @param w width of the column
268  */
lv_table_set_col_width(lv_obj_t * table,uint16_t col_id,lv_coord_t w)269 void lv_table_set_col_width(lv_obj_t * table, uint16_t col_id, lv_coord_t w)
270 {
271     LV_ASSERT_OBJ(table, LV_OBJX_NAME);
272 
273     if(col_id >= LV_TABLE_COL_MAX) {
274         LV_LOG_WARN("lv_table_set_col_width: too big 'col_id'. Must be < LV_TABLE_COL_MAX.");
275         return;
276     }
277 
278     lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
279     ext->col_w[col_id]   = w;
280     refr_size(table);
281 }
282 
283 /**
284  * Set the text align in a cell
285  * @param table pointer to a Table object
286  * @param row id of the row [0 .. row_cnt -1]
287  * @param col id of the column [0 .. col_cnt -1]
288  * @param align LV_LABEL_ALIGN_LEFT or LV_LABEL_ALIGN_CENTER or LV_LABEL_ALIGN_RIGHT
289  */
lv_table_set_cell_align(lv_obj_t * table,uint16_t row,uint16_t col,lv_label_align_t align)290 void lv_table_set_cell_align(lv_obj_t * table, uint16_t row, uint16_t col, lv_label_align_t align)
291 {
292     LV_ASSERT_OBJ(table, LV_OBJX_NAME);
293 
294     lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
295     if(col >= ext->col_cnt) {
296         LV_LOG_WARN("lv_table_set_cell_align: invalid column");
297         return;
298     }
299 
300     /*Auto expand*/
301     if(row >= ext->row_cnt) {
302         lv_table_set_row_cnt(table, row + 1);
303     }
304     uint32_t cell = row * ext->col_cnt + col;
305 
306     if(ext->cell_data[cell] == NULL) {
307         ext->cell_data[cell]    = lv_mem_alloc(2); /*+1: trailing '\0; +1: format byte*/
308         LV_ASSERT_MEM(ext->cell_data[cell]);
309         if(ext->cell_data[cell] == NULL) return;
310 
311         ext->cell_data[cell][0] = 0;
312         ext->cell_data[cell][1] = '\0';
313     }
314 
315     lv_table_cell_format_t format;
316     format.format_byte      = ext->cell_data[cell][0];
317     format.s.align          = align;
318     ext->cell_data[cell][0] = format.format_byte;
319 }
320 
321 /**
322  * Set the type of a cell.
323  * @param table pointer to a Table object
324  * @param row id of the row [0 .. row_cnt -1]
325  * @param col id of the column [0 .. col_cnt -1]
326  * @param type 1,2,3 or 4. The cell style will be chosen accordingly.
327  */
lv_table_set_cell_type(lv_obj_t * table,uint16_t row,uint16_t col,uint8_t type)328 void lv_table_set_cell_type(lv_obj_t * table, uint16_t row, uint16_t col, uint8_t type)
329 {
330     LV_ASSERT_OBJ(table, LV_OBJX_NAME);
331 
332     lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
333     if(col >= ext->col_cnt) {
334         LV_LOG_WARN("lv_table_set_cell_type: invalid column");
335         return;
336     }
337 
338     /*Auto expand*/
339     if(row >= ext->row_cnt) {
340         lv_table_set_row_cnt(table, row + 1);
341     }
342 
343     uint32_t cell = row * ext->col_cnt + col;
344 
345     if(ext->cell_data[cell] == NULL) {
346         ext->cell_data[cell]    = lv_mem_alloc(2); /*+1: trailing '\0; +1: format byte*/
347         LV_ASSERT_MEM(ext->cell_data[cell]);
348         if(ext->cell_data[cell] == NULL) return;
349 
350         ext->cell_data[cell][0] = 0;
351         ext->cell_data[cell][1] = '\0';
352     }
353 
354     if(type > 0) type--; /*User gives 1,2,3,4 but easier to handle 0, 1, 2, 3*/
355     if(type >= LV_TABLE_CELL_STYLE_CNT) type = LV_TABLE_CELL_STYLE_CNT - 1;
356 
357     lv_table_cell_format_t format;
358     format.format_byte      = ext->cell_data[cell][0];
359     format.s.type           = type;
360     ext->cell_data[cell][0] = format.format_byte;
361 
362     ext->cell_types |= 1 << type;
363 }
364 
365 /**
366  * Set the cell crop. (Don't adjust the height of the cell according to its content)
367  * @param table pointer to a Table object
368  * @param row id of the row [0 .. row_cnt -1]
369  * @param col id of the column [0 .. col_cnt -1]
370  * @param crop true: crop the cell content; false: set the cell height to the content.
371  */
lv_table_set_cell_crop(lv_obj_t * table,uint16_t row,uint16_t col,bool crop)372 void lv_table_set_cell_crop(lv_obj_t * table, uint16_t row, uint16_t col, bool crop)
373 {
374     LV_ASSERT_OBJ(table, LV_OBJX_NAME);
375 
376     lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
377     if(col >= ext->col_cnt) {
378         LV_LOG_WARN("lv_table_set_cell_crop: invalid column");
379         return;
380     }
381 
382     /*Auto expand*/
383     if(row >= ext->row_cnt) {
384         lv_table_set_row_cnt(table, row + 1);
385     }
386 
387     uint32_t cell = row * ext->col_cnt + col;
388 
389     if(ext->cell_data[cell] == NULL) {
390         ext->cell_data[cell]    = lv_mem_alloc(2); /*+1: trailing '\0; +1: format byte*/
391         LV_ASSERT_MEM(ext->cell_data[cell]);
392         if(ext->cell_data[cell] == NULL) return;
393 
394         ext->cell_data[cell][0] = 0;
395         ext->cell_data[cell][1] = '\0';
396     }
397 
398     lv_table_cell_format_t format;
399     format.format_byte      = ext->cell_data[cell][0];
400     format.s.crop           = crop;
401     ext->cell_data[cell][0] = format.format_byte;
402 }
403 
404 /**
405  * Merge a cell with the right neighbor. The value of the cell to the right won't be displayed.
406  * @param table table pointer to a Table object
407  * @param row id of the row [0 .. row_cnt -1]
408  * @param col id of the column [0 .. col_cnt -1]
409  * @param en true: merge right; false: don't merge right
410  */
lv_table_set_cell_merge_right(lv_obj_t * table,uint16_t row,uint16_t col,bool en)411 void lv_table_set_cell_merge_right(lv_obj_t * table, uint16_t row, uint16_t col, bool en)
412 {
413     LV_ASSERT_OBJ(table, LV_OBJX_NAME);
414 
415     lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
416     if(col >= ext->col_cnt) {
417         LV_LOG_WARN("lv_table_set_cell_merge_right: invalid column");
418         return;
419     }
420 
421     /*Auto expand*/
422     if(row >= ext->row_cnt) {
423         lv_table_set_row_cnt(table, row + 1);
424     }
425 
426     uint32_t cell = row * ext->col_cnt + col;
427 
428     if(ext->cell_data[cell] == NULL) {
429         ext->cell_data[cell]    = lv_mem_alloc(2); /*+1: trailing '\0; +1: format byte*/
430         LV_ASSERT_MEM(ext->cell_data[cell]);
431         if(ext->cell_data[cell] == NULL) return;
432 
433         ext->cell_data[cell][0] = 0;
434         ext->cell_data[cell][1] = '\0';
435     }
436 
437     lv_table_cell_format_t format;
438     format.format_byte      = ext->cell_data[cell][0];
439     format.s.right_merge    = en ? 1 : 0;
440     ext->cell_data[cell][0] = format.format_byte;
441     refr_size(table);
442 }
443 
444 /*=====================
445  * Getter functions
446  *====================*/
447 
448 /**
449  * Get the value of a cell.
450  * @param table pointer to a Table object
451  * @param row id of the row [0 .. row_cnt -1]
452  * @param col id of the column [0 .. col_cnt -1]
453  * @return text in the cell
454  */
lv_table_get_cell_value(lv_obj_t * table,uint16_t row,uint16_t col)455 const char * lv_table_get_cell_value(lv_obj_t * table, uint16_t row, uint16_t col)
456 {
457     LV_ASSERT_OBJ(table, LV_OBJX_NAME);
458 
459     lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
460     if(row >= ext->row_cnt || col >= ext->col_cnt) {
461         LV_LOG_WARN("lv_table_set_cell_value: invalid row or column");
462         return "";
463     }
464     uint32_t cell = row * ext->col_cnt + col;
465 
466     if(ext->cell_data[cell] == NULL) return "";
467 
468     return &ext->cell_data[cell][1]; /*Skip the format byte*/
469 }
470 
471 /**
472  * Get the number of rows.
473  * @param table table pointer to a Table object
474  * @return number of rows.
475  */
lv_table_get_row_cnt(lv_obj_t * table)476 uint16_t lv_table_get_row_cnt(lv_obj_t * table)
477 {
478     LV_ASSERT_OBJ(table, LV_OBJX_NAME);
479 
480     lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
481     return ext->row_cnt;
482 }
483 
484 /**
485  * Get the number of columns.
486  * @param table table pointer to a Table object
487  * @return number of columns.
488  */
lv_table_get_col_cnt(lv_obj_t * table)489 uint16_t lv_table_get_col_cnt(lv_obj_t * table)
490 {
491     LV_ASSERT_OBJ(table, LV_OBJX_NAME);
492 
493     lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
494     return ext->col_cnt;
495 }
496 
497 /**
498  * Get the width of a column
499  * @param table table pointer to a Table object
500  * @param col_id id of the column [0 .. LV_TABLE_COL_MAX -1]
501  * @return width of the column
502  */
lv_table_get_col_width(lv_obj_t * table,uint16_t col_id)503 lv_coord_t lv_table_get_col_width(lv_obj_t * table, uint16_t col_id)
504 {
505     LV_ASSERT_OBJ(table, LV_OBJX_NAME);
506 
507     if(col_id >= LV_TABLE_COL_MAX) {
508         LV_LOG_WARN("lv_table_set_col_width: too big 'col_id'. Must be < LV_TABLE_COL_MAX.");
509         return 0;
510     }
511 
512     lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
513     return ext->col_w[col_id];
514 }
515 
516 /**
517  * Get the text align of a cell
518  * @param table pointer to a Table object
519  * @param row id of the row [0 .. row_cnt -1]
520  * @param col id of the column [0 .. col_cnt -1]
521  * @return LV_LABEL_ALIGN_LEFT (default in case of error) or LV_LABEL_ALIGN_CENTER or
522  * LV_LABEL_ALIGN_RIGHT
523  */
lv_table_get_cell_align(lv_obj_t * table,uint16_t row,uint16_t col)524 lv_label_align_t lv_table_get_cell_align(lv_obj_t * table, uint16_t row, uint16_t col)
525 {
526     LV_ASSERT_OBJ(table, LV_OBJX_NAME);
527 
528     lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
529     if(row >= ext->row_cnt || col >= ext->col_cnt) {
530         LV_LOG_WARN("lv_table_set_cell_align: invalid row or column");
531         return LV_LABEL_ALIGN_LEFT; /*Just return with something*/
532     }
533     uint32_t cell = row * ext->col_cnt + col;
534 
535     if(ext->cell_data[cell] == NULL)
536         return LV_LABEL_ALIGN_LEFT; /*Just return with something*/
537     else {
538         lv_table_cell_format_t format;
539         format.format_byte = ext->cell_data[cell][0];
540         return format.s.align;
541     }
542 }
543 
544 /**
545  * Get the type of a cell
546  * @param table pointer to a Table object
547  * @param row id of the row [0 .. row_cnt -1]
548  * @param col id of the column [0 .. col_cnt -1]
549  * @return 1,2,3 or 4
550  */
lv_table_get_cell_type(lv_obj_t * table,uint16_t row,uint16_t col)551 lv_label_align_t lv_table_get_cell_type(lv_obj_t * table, uint16_t row, uint16_t col)
552 {
553     LV_ASSERT_OBJ(table, LV_OBJX_NAME);
554 
555     lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
556     if(row >= ext->row_cnt || col >= ext->col_cnt) {
557         LV_LOG_WARN("lv_table_get_cell_type: invalid row or column");
558         return 1; /*Just return with something*/
559     }
560     uint32_t cell = row * ext->col_cnt + col;
561 
562     if(ext->cell_data[cell] == NULL)
563         return 1; /*Just return with something*/
564     else {
565         lv_table_cell_format_t format;
566         format.format_byte = ext->cell_data[cell][0];
567         return format.s.type + 1; /*0,1,2,3 is stored but user sees 1,2,3,4*/
568     }
569 }
570 
571 /**
572  * Get the crop property of a cell
573  * @param table pointer to a Table object
574  * @param row id of the row [0 .. row_cnt -1]
575  * @param col id of the column [0 .. col_cnt -1]
576  * @return true: text crop enabled; false: disabled
577  */
lv_table_get_cell_crop(lv_obj_t * table,uint16_t row,uint16_t col)578 lv_label_align_t lv_table_get_cell_crop(lv_obj_t * table, uint16_t row, uint16_t col)
579 {
580     LV_ASSERT_OBJ(table, LV_OBJX_NAME);
581 
582     lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
583     if(row >= ext->row_cnt || col >= ext->col_cnt) {
584         LV_LOG_WARN("lv_table_get_cell_crop: invalid row or column");
585         return false; /*Just return with something*/
586     }
587     uint32_t cell = row * ext->col_cnt + col;
588 
589     if(ext->cell_data[cell] == NULL)
590         return false; /*Just return with something*/
591     else {
592         lv_table_cell_format_t format;
593         format.format_byte = ext->cell_data[cell][0];
594         return format.s.crop;
595     }
596 }
597 
598 /**
599  * Get the cell merge attribute.
600  * @param table table pointer to a Table object
601  * @param row id of the row [0 .. row_cnt -1]
602  * @param col id of the column [0 .. col_cnt -1]
603  * @return true: merge right; false: don't merge right
604  */
lv_table_get_cell_merge_right(lv_obj_t * table,uint16_t row,uint16_t col)605 bool lv_table_get_cell_merge_right(lv_obj_t * table, uint16_t row, uint16_t col)
606 {
607     LV_ASSERT_OBJ(table, LV_OBJX_NAME);
608 
609     lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
610     if(row >= ext->row_cnt || col >= ext->col_cnt) {
611         LV_LOG_WARN("lv_table_get_cell_merge_right: invalid row or column");
612         return false;
613     }
614 
615     uint32_t cell = row * ext->col_cnt + col;
616 
617     if(ext->cell_data[cell] == NULL)
618         return false;
619     else {
620         lv_table_cell_format_t format;
621         format.format_byte = ext->cell_data[cell][0];
622         return format.s.right_merge ? true : false;
623     }
624 }
625 
626 /**
627  * Get the last pressed or being pressed cell
628  * @param table pointer to a table object
629  * @param row pointer to variable to store the pressed row
630  * @param col pointer to variable to store the pressed column
631  * @return LV_RES_OK: a valid pressed cell was found, LV_RES_INV: no valid cell is pressed
632  */
lv_table_get_pressed_cell(lv_obj_t * table,uint16_t * row,uint16_t * col)633 lv_res_t lv_table_get_pressed_cell(lv_obj_t * table, uint16_t * row, uint16_t * col)
634 {
635     lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
636 
637     lv_indev_type_t type = lv_indev_get_type(lv_indev_get_act());
638     if(type != LV_INDEV_TYPE_POINTER && type != LV_INDEV_TYPE_BUTTON) {
639         if(col) *col = 0xFFFF;
640         if(row) *row = 0xFFFF;
641         return LV_RES_INV;
642     }
643 
644     lv_point_t p;
645     lv_indev_get_point(lv_indev_get_act(), &p);
646 
647     lv_coord_t tmp;
648     if(col) {
649         lv_coord_t x = p.x;
650         x -= table->coords.x1;
651         x -= lv_obj_get_style_pad_left(table, LV_TABLE_PART_BG);
652         *col = 0;
653         tmp = 0;
654         for(*col = 0; *col < ext->col_cnt; (*col)++) {
655             tmp += ext->col_w[*col];
656             if(x < tmp) break;
657         }
658     }
659 
660     if(row) {
661         lv_coord_t y = p.y;
662         y -= table->coords.y1;
663         y -= lv_obj_get_style_pad_top(table, LV_TABLE_PART_BG);
664 
665         *row = 0;
666         tmp = 0;
667 
668         for(*row = 0; *row < ext->row_cnt; (*row)++) {
669             tmp += ext->row_h[*row];
670             if(y < tmp) break;
671         }
672     }
673 
674     return LV_RES_OK;
675 }
676 
677 /**********************
678  *   STATIC FUNCTIONS
679  **********************/
680 
681 /**
682  * Handle the drawing related tasks of the tables
683  * @param table pointer to an object
684  * @param clip_area the object will be drawn only in this area
685  * @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area
686  *                                  (return 'true' if yes)
687  *             LV_DESIGN_DRAW: draw the object (always return 'true')
688  *             LV_DESIGN_DRAW_POST: drawing after every children are drawn
689  * @param return an element of `lv_design_res_t`
690  */
lv_table_design(lv_obj_t * table,const lv_area_t * clip_area,lv_design_mode_t mode)691 static lv_design_res_t lv_table_design(lv_obj_t * table, const lv_area_t * clip_area, lv_design_mode_t mode)
692 {
693     /*Return false if the object is not covers the mask_p area*/
694     if(mode == LV_DESIGN_COVER_CHK) {
695         return ancestor_design(table, clip_area, mode);
696     }
697     /*Draw the object*/
698     else if(mode == LV_DESIGN_DRAW_MAIN) {
699         /*Draw the background*/
700         ancestor_design(table, clip_area, mode);
701 
702         lv_table_ext_t * ext        = lv_obj_get_ext_attr(table);
703 
704         lv_point_t txt_size;
705         lv_area_t cell_area;
706         lv_area_t txt_area;
707         lv_txt_flag_t txt_flags;
708 
709         lv_style_int_t bg_top = lv_obj_get_style_pad_top(table, LV_TABLE_PART_BG);
710         lv_style_int_t bg_bottom = lv_obj_get_style_pad_bottom(table, LV_TABLE_PART_BG);
711         lv_style_int_t bg_left = lv_obj_get_style_pad_left(table, LV_TABLE_PART_BG);
712         lv_style_int_t bg_right = lv_obj_get_style_pad_right(table, LV_TABLE_PART_BG);
713 
714         lv_draw_rect_dsc_t rect_dsc[LV_TABLE_CELL_STYLE_CNT];
715         lv_draw_label_dsc_t label_dsc[LV_TABLE_CELL_STYLE_CNT];
716         lv_draw_line_dsc_t line_dsc[LV_TABLE_CELL_STYLE_CNT];
717         lv_style_int_t cell_left[LV_TABLE_CELL_STYLE_CNT];
718         lv_style_int_t cell_right[LV_TABLE_CELL_STYLE_CNT];
719         lv_style_int_t cell_top[LV_TABLE_CELL_STYLE_CNT];
720         lv_style_int_t cell_bottom[LV_TABLE_CELL_STYLE_CNT];
721 
722         uint16_t i;
723         for(i = 0; i < LV_TABLE_CELL_STYLE_CNT; i++) {
724             if((ext->cell_types & (1 << i)) == 0) continue; /*Skip unused cell types*/
725             lv_draw_rect_dsc_init(&rect_dsc[i]);
726             lv_obj_init_draw_rect_dsc(table, LV_TABLE_PART_CELL1 + i, &rect_dsc[i]);
727 
728             lv_draw_label_dsc_init(&label_dsc[i]);
729             lv_obj_init_draw_label_dsc(table, LV_TABLE_PART_CELL1 + i, &label_dsc[i]);
730 
731             lv_draw_line_dsc_init(&line_dsc[i]);
732             lv_obj_init_draw_line_dsc(table, LV_TABLE_PART_CELL1 + i, &line_dsc[i]);
733 
734             cell_left[i] = lv_obj_get_style_pad_left(table, LV_TABLE_PART_CELL1 + i);
735             cell_right[i] = lv_obj_get_style_pad_right(table, LV_TABLE_PART_CELL1 + i);
736             cell_top[i] = lv_obj_get_style_pad_top(table, LV_TABLE_PART_CELL1 + i);
737             cell_bottom[i] = lv_obj_get_style_pad_bottom(table, LV_TABLE_PART_CELL1 + i);
738         }
739 
740         uint16_t col;
741         uint16_t row;
742         uint16_t cell = 0;
743 
744         cell_area.y2 = table->coords.y1 + bg_top - 1;
745         for(row = 0; row < ext->row_cnt; row++) {
746             lv_coord_t h_row = ext->row_h[row];
747 
748             cell_area.y1 = cell_area.y2 + 1;
749             cell_area.y2 = cell_area.y1 + h_row - 1;
750 
751             if(cell_area.y1 > clip_area->y2) return LV_DESIGN_RES_OK;
752 
753             cell_area.x2 = table->coords.x1 + bg_left - 1;
754 
755             for(col = 0; col < ext->col_cnt; col++) {
756 
757                 lv_table_cell_format_t format;
758                 if(ext->cell_data[cell]) {
759                     format.format_byte = ext->cell_data[cell][0];
760                 }
761                 else {
762                     format.s.right_merge = 0;
763                     format.s.align       = LV_LABEL_ALIGN_LEFT;
764                     format.s.type        = 0;
765                     format.s.crop        = 1;
766                 }
767 
768                 cell_area.x1 = cell_area.x2 + 1;
769                 cell_area.x2 = cell_area.x1 + ext->col_w[col] - 1;
770 
771                 uint16_t col_merge = 0;
772                 for(col_merge = 0; col_merge + col < ext->col_cnt - 1; col_merge++) {
773                     if(ext->cell_data[cell + col_merge] != NULL) {
774                         format.format_byte = ext->cell_data[cell + col_merge][0];
775                         if(format.s.right_merge)
776                             cell_area.x2 += ext->col_w[col + col_merge + 1];
777                         else
778                             break;
779                     }
780                     else {
781                         break;
782                     }
783                 }
784 
785                 if(cell_area.y2 < clip_area->y1) {
786                     cell += col_merge + 1;
787                     col += col_merge;
788                     continue;
789                 }
790 
791                 uint8_t cell_type = format.s.type;
792 
793                 /*Expand the cell area with a half border to avoid drawing 2 borders next to each other*/
794                 lv_area_t cell_area_border;
795                 lv_area_copy(&cell_area_border, &cell_area);
796                 if((rect_dsc[cell_type].border_side & LV_BORDER_SIDE_LEFT) && cell_area_border.x1 > table->coords.x1 + bg_left) {
797                     cell_area_border.x1 -= rect_dsc[cell_type].border_width / 2;
798                 }
799                 if((rect_dsc[cell_type].border_side & LV_BORDER_SIDE_TOP) && cell_area_border.y1 > table->coords.y1 + bg_top) {
800                     cell_area_border.y1 -= rect_dsc[cell_type].border_width / 2;
801                 }
802                 if((rect_dsc[cell_type].border_side & LV_BORDER_SIDE_RIGHT) && cell_area_border.x2 < table->coords.x2 - bg_right - 1) {
803                     cell_area_border.x2 += rect_dsc[cell_type].border_width / 2 + (rect_dsc[cell_type].border_width & 0x1);
804                 }
805                 if((rect_dsc[cell_type].border_side & LV_BORDER_SIDE_BOTTOM) &&
806                    cell_area_border.y2 < table->coords.y2 - bg_bottom - 1) {
807                     cell_area_border.y2 += rect_dsc[cell_type].border_width / 2 + (rect_dsc[cell_type].border_width & 0x1);
808                 }
809 
810                 lv_draw_rect(&cell_area_border, clip_area, &rect_dsc[cell_type]);
811 
812                 if(ext->cell_data[cell]) {
813                     txt_area.x1 = cell_area.x1 + cell_left[cell_type];
814                     txt_area.x2 = cell_area.x2 - cell_right[cell_type];
815                     txt_area.y1 = cell_area.y1 + cell_top[cell_type];
816                     txt_area.y2 = cell_area.y2 - cell_bottom[cell_type];
817 
818                     /*Align the content to the middle if not cropped*/
819                     if(format.s.crop == 0) {
820                         txt_flags = LV_TXT_FLAG_NONE;
821                     }
822                     else {
823                         txt_flags = LV_TXT_FLAG_EXPAND;
824                     }
825 
826                     _lv_txt_get_size(&txt_size, ext->cell_data[cell] + 1, label_dsc[cell_type].font,
827                                      label_dsc[cell_type].letter_space, label_dsc[cell_type].line_space,
828                                      lv_area_get_width(&txt_area), txt_flags);
829 
830                     label_dsc[cell_type].flag = 0;
831                     /*Align the content to the middle if not cropped*/
832                     if(format.s.crop == 0) {
833                         txt_area.y1 = cell_area.y1 + h_row / 2 - txt_size.y / 2;
834                         txt_area.y2 = cell_area.y1 + h_row / 2 + txt_size.y / 2;
835                     }
836 
837                     switch(format.s.align) {
838                         default:
839                         case LV_LABEL_ALIGN_LEFT:
840                             label_dsc[cell_type].flag |= LV_TXT_FLAG_NONE;
841                             break;
842                         case LV_LABEL_ALIGN_RIGHT:
843                             label_dsc[cell_type].flag |= LV_TXT_FLAG_RIGHT;
844                             break;
845                         case LV_LABEL_ALIGN_CENTER:
846                             label_dsc[cell_type].flag |= LV_TXT_FLAG_CENTER;
847                             break;
848                     }
849 
850                     lv_area_t label_mask;
851                     bool label_mask_ok;
852                     label_mask_ok = _lv_area_intersect(&label_mask, clip_area, &cell_area);
853                     if(label_mask_ok) {
854                         lv_draw_label(&txt_area, &label_mask, &label_dsc[cell_type], ext->cell_data[cell] + 1, NULL);
855                     }
856 
857                     /*Draw lines after '\n's*/
858                     lv_point_t p1;
859                     lv_point_t p2;
860                     p1.x = cell_area.x1;
861                     p2.x = cell_area.x2;
862                     for(i = 1; ext->cell_data[cell][i] != '\0'; i++) {
863                         if(ext->cell_data[cell][i] == '\n') {
864                             ext->cell_data[cell][i] = '\0';
865                             _lv_txt_get_size(&txt_size, ext->cell_data[cell] + 1, label_dsc[cell_type].font,
866                                              label_dsc[cell_type].letter_space, label_dsc[cell_type].line_space,
867                                              lv_area_get_width(&txt_area), txt_flags);
868 
869                             p1.y = txt_area.y1 + txt_size.y + label_dsc[cell_type].line_space / 2;
870                             p2.y = txt_area.y1 + txt_size.y + label_dsc[cell_type].line_space / 2;
871                             lv_draw_line(&p1, &p2, clip_area, &line_dsc[cell_type]);
872 
873                             ext->cell_data[cell][i] = '\n';
874                         }
875                     }
876                 }
877 
878                 cell += col_merge + 1;
879                 col += col_merge;
880             }
881         }
882     }
883     /*Post draw when the children are drawn*/
884     else if(mode == LV_DESIGN_DRAW_POST) {
885         ancestor_design(table, clip_area, mode);
886     }
887 
888     return LV_DESIGN_RES_OK;
889 }
890 
891 /**
892  * Signal function of the table
893  * @param table pointer to a table object
894  * @param sign a signal type from lv_signal_t enum
895  * @param param pointer to a signal specific variable
896  * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
897  */
lv_table_signal(lv_obj_t * table,lv_signal_t sign,void * param)898 static lv_res_t lv_table_signal(lv_obj_t * table, lv_signal_t sign, void * param)
899 {
900     lv_res_t res;
901     if(sign == LV_SIGNAL_GET_STYLE) {
902         lv_get_style_info_t * info = param;
903         info->result = lv_table_get_style(table, info->part);
904         if(info->result != NULL) return LV_RES_OK;
905         else return ancestor_signal(table, sign, param);
906     }
907 
908     /* Include the ancient signal function */
909     res = ancestor_signal(table, sign, param);
910     if(res != LV_RES_OK) return res;
911     if(sign == LV_SIGNAL_GET_TYPE) return lv_obj_handle_get_type_signal(param, LV_OBJX_NAME);
912 
913     if(sign == LV_SIGNAL_CLEANUP) {
914         /*Free the cell texts*/
915         lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
916         uint16_t i;
917         for(i = 0; i < ext->col_cnt * ext->row_cnt; i++) {
918             if(ext->cell_data[i]) {
919                 lv_mem_free(ext->cell_data[i]);
920                 ext->cell_data[i] = NULL;
921             }
922         }
923 
924         if(ext->cell_data) lv_mem_free(ext->cell_data);
925         if(ext->row_h) lv_mem_free(ext->row_h);
926 
927         for(i = 0; i < LV_TABLE_CELL_STYLE_CNT; i++) {
928             lv_obj_clean_style_list(table, LV_TABLE_PART_CELL1 + i);
929         }
930     }
931     else if(sign == LV_SIGNAL_STYLE_CHG) {
932         refr_size(table);
933     }
934 
935     return res;
936 }
937 
938 
939 /**
940  * Get the style descriptor of a part of the object
941  * @param table pointer the object
942  * @param part the part from. (LV_TABLE_PART_...)
943  * @return pointer to the style descriptor of the specified part
944  */
lv_table_get_style(lv_obj_t * table,uint8_t part)945 static lv_style_list_t * lv_table_get_style(lv_obj_t * table, uint8_t part)
946 {
947     LV_ASSERT_OBJ(table, LV_OBJX_NAME);
948 
949     lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
950     lv_style_list_t * style_dsc_p;
951 
952     switch(part) {
953         case LV_TABLE_PART_BG:
954             style_dsc_p = &table->style_list;
955             break;
956         case LV_TABLE_PART_CELL1:
957             style_dsc_p = &ext->cell_style[0];
958             break;
959         case LV_TABLE_PART_CELL2:
960             style_dsc_p = &ext->cell_style[1];
961             break;
962         case LV_TABLE_PART_CELL3:
963             style_dsc_p = &ext->cell_style[2];
964             break;
965         case LV_TABLE_PART_CELL4:
966             style_dsc_p = &ext->cell_style[3];
967             break;
968         default:
969             style_dsc_p = NULL;
970     }
971 
972     return style_dsc_p;
973 }
974 
refr_size(lv_obj_t * table)975 static void refr_size(lv_obj_t * table)
976 {
977     lv_coord_t h = 0;
978     lv_coord_t w = 0;
979 
980     lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
981     if(ext->row_cnt == 0 || ext->col_cnt == 0) {
982         lv_obj_set_size(table, w, h);
983         return;
984     }
985 
986     uint16_t i;
987     for(i = 0; i < ext->col_cnt; i++) {
988         w += ext->col_w[i];
989     }
990 
991     lv_style_int_t cell_left[LV_TABLE_CELL_STYLE_CNT];
992     lv_style_int_t cell_right[LV_TABLE_CELL_STYLE_CNT];
993     lv_style_int_t cell_top[LV_TABLE_CELL_STYLE_CNT];
994     lv_style_int_t cell_bottom[LV_TABLE_CELL_STYLE_CNT];
995     lv_style_int_t letter_space[LV_TABLE_CELL_STYLE_CNT];
996     lv_style_int_t line_space[LV_TABLE_CELL_STYLE_CNT];
997     const lv_font_t * font[LV_TABLE_CELL_STYLE_CNT];
998 
999     for(i = 0; i < LV_TABLE_CELL_STYLE_CNT; i++) {
1000         if((ext->cell_types & (1 << i)) == 0) continue; /*Skip unused cell types*/
1001         cell_left[i] = lv_obj_get_style_pad_left(table, LV_TABLE_PART_CELL1 + i);
1002         cell_right[i] = lv_obj_get_style_pad_right(table, LV_TABLE_PART_CELL1 + i);
1003         cell_top[i] = lv_obj_get_style_pad_top(table, LV_TABLE_PART_CELL1 + i);
1004         cell_bottom[i] = lv_obj_get_style_pad_bottom(table, LV_TABLE_PART_CELL1 + i);
1005         letter_space[i] = lv_obj_get_style_text_letter_space(table, LV_TABLE_PART_CELL1 + i);
1006         line_space[i] = lv_obj_get_style_text_line_space(table, LV_TABLE_PART_CELL1 + i);
1007         font[i] = lv_obj_get_style_text_font(table, LV_TABLE_PART_CELL1 + i);
1008     }
1009 
1010 
1011     for(i = 0; i < ext->row_cnt; i++) {
1012         ext->row_h[i] = get_row_height(table, i, font, letter_space, line_space,
1013                                        cell_left, cell_right, cell_top, cell_bottom);
1014         h += ext->row_h[i];
1015     }
1016 
1017     lv_style_int_t bg_top = lv_obj_get_style_pad_top(table, LV_TABLE_PART_BG);
1018     lv_style_int_t bg_bottom = lv_obj_get_style_pad_bottom(table, LV_TABLE_PART_BG);
1019     lv_style_int_t bg_left = lv_obj_get_style_pad_left(table, LV_TABLE_PART_BG);
1020     lv_style_int_t bg_right = lv_obj_get_style_pad_right(table, LV_TABLE_PART_BG);
1021     w += bg_left + bg_right;
1022     h += bg_top + bg_bottom;
1023 
1024     lv_obj_set_size(table, w + 1, h + 1);
1025     lv_obj_invalidate(table); /*Always invalidate even if the size hasn't changed*/
1026 }
1027 
get_row_height(lv_obj_t * table,uint16_t row_id,const lv_font_t ** font,lv_style_int_t * letter_space,lv_style_int_t * line_space,lv_style_int_t * cell_left,lv_style_int_t * cell_right,lv_style_int_t * cell_top,lv_style_int_t * cell_bottom)1028 static lv_coord_t get_row_height(lv_obj_t * table, uint16_t row_id, const lv_font_t ** font,
1029                                  lv_style_int_t * letter_space, lv_style_int_t * line_space,
1030                                  lv_style_int_t * cell_left, lv_style_int_t * cell_right, lv_style_int_t * cell_top, lv_style_int_t * cell_bottom)
1031 {
1032     lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
1033     lv_point_t txt_size;
1034     lv_coord_t txt_w;
1035 
1036     uint16_t row_start = row_id * ext->col_cnt;
1037     uint16_t cell;
1038     uint16_t col;
1039     lv_coord_t h_max = lv_font_get_line_height(font[0]) + cell_top[0] + cell_bottom[0];
1040 
1041     for(cell = row_start, col = 0; cell < row_start + ext->col_cnt; cell++, col++) {
1042         if(ext->cell_data[cell] != NULL) {
1043             txt_w              = ext->col_w[col];
1044             uint16_t col_merge = 0;
1045             for(col_merge = 0; col_merge + col < ext->col_cnt - 1; col_merge++) {
1046 
1047                 if(ext->cell_data[cell + col_merge] != NULL) {
1048                     lv_table_cell_format_t format;
1049                     format.format_byte = ext->cell_data[cell + col_merge][0];
1050                     if(format.s.right_merge)
1051                         txt_w += ext->col_w[col + col_merge + 1];
1052                     else
1053                         break;
1054                 }
1055                 else {
1056                     break;
1057                 }
1058             }
1059 
1060             lv_table_cell_format_t format;
1061             format.format_byte = ext->cell_data[cell][0];
1062             uint8_t cell_type  = format.s.type;
1063 
1064             /*With text crop assume 1 line*/
1065             if(format.s.crop) {
1066                 h_max = LV_MATH_MAX(lv_font_get_line_height(font[cell_type]) + cell_top[cell_type] + cell_bottom[cell_type],
1067                                     h_max);
1068             }
1069             /*Without text crop calculate the height of the text in the cell*/
1070             else {
1071                 txt_w -= cell_left[cell_type] + cell_right[cell_type];
1072 
1073                 _lv_txt_get_size(&txt_size, ext->cell_data[cell] + 1, font[cell_type],
1074                                  letter_space[cell_type], line_space[cell_type], txt_w, LV_TXT_FLAG_NONE);
1075 
1076                 h_max = LV_MATH_MAX(txt_size.y + cell_top[cell_type] + cell_bottom[cell_type], h_max);
1077                 cell += col_merge;
1078                 col += col_merge;
1079             }
1080         }
1081     }
1082 
1083     return h_max;
1084 }
1085 
1086 #endif
1087