[A] Added cell combining
This commit is contained in:
parent
c8fe3fe280
commit
cbb05f3db2
@ -123,6 +123,18 @@ int main(void)
|
||||
printf("Table:\n%s\n", ft_to_string(table));
|
||||
ft_destroy_table(table);
|
||||
|
||||
|
||||
/* Debug */
|
||||
ft_set_default_border_style(FT_DOT_STYLE);
|
||||
table = create_basic_table();
|
||||
ft_set_cell_option(table, FT_CUR_ROW, FT_ANY_COLUMN, FT_COPT_ROW_TYPE, FT_ROW_HEADER);
|
||||
ft_write_ln(table, "Summary", "", "", "8.7");
|
||||
ft_some_api(table, 6, 0, 3);
|
||||
ft_some_api(table, 0, 0, 3);
|
||||
printf("Table:\n%s\n", ft_to_string(table));
|
||||
ft_destroy_table(table);
|
||||
return 0;
|
||||
|
||||
/*-------------------------------------------------------------*/
|
||||
#if defined(FT_HAVE_WCHAR) && !defined(FT_MICROSOFT_COMPILER)
|
||||
setlocale(LC_CTYPE, "");
|
||||
|
@ -591,6 +591,9 @@ FT_EXTERN int ft_set_tbl_option(FTABLE *table, uint32_t option, int value);
|
||||
FT_EXTERN void ft_set_memory_funcs(void *(*f_malloc)(size_t size), void (*f_free)(void *ptr));
|
||||
|
||||
|
||||
|
||||
FT_EXTERN void ft_some_api(FTABLE *table, size_t row, size_t col, size_t group_width);
|
||||
|
||||
#ifdef FT_HAVE_WCHAR
|
||||
|
||||
|
||||
|
16
src/cell.c
16
src/cell.c
@ -9,6 +9,7 @@
|
||||
|
||||
struct fort_cell {
|
||||
string_buffer_t *str_buffer;
|
||||
enum CellType cell_type;
|
||||
};
|
||||
|
||||
fort_cell_t *create_cell(void)
|
||||
@ -21,7 +22,7 @@ fort_cell_t *create_cell(void)
|
||||
F_FREE(cell);
|
||||
return NULL;
|
||||
}
|
||||
/*init_cell_options(&(cell->options));*/
|
||||
cell->cell_type = CommonCell;
|
||||
return cell;
|
||||
}
|
||||
|
||||
@ -33,6 +34,19 @@ void destroy_cell(fort_cell_t *cell)
|
||||
F_FREE(cell);
|
||||
}
|
||||
|
||||
void set_cell_type(fort_cell_t *cell, enum CellType type)
|
||||
{
|
||||
assert(cell);
|
||||
cell->cell_type = type;
|
||||
}
|
||||
|
||||
enum CellType get_cell_type(const fort_cell_t *cell)
|
||||
{
|
||||
assert(cell);
|
||||
return cell->cell_type;
|
||||
}
|
||||
|
||||
|
||||
size_t hint_width_cell(const fort_cell_t *cell, const context_t *context)
|
||||
{
|
||||
/* todo:
|
||||
|
@ -17,6 +17,9 @@ size_t hint_width_cell(const fort_cell_t *cell, const context_t *context);
|
||||
size_t hint_height_cell(const fort_cell_t *cell, const context_t *context);
|
||||
|
||||
|
||||
void set_cell_type(fort_cell_t *cell, enum CellType type);
|
||||
enum CellType get_cell_type(const fort_cell_t *cell);
|
||||
|
||||
/*
|
||||
* Returns number of lines in cell. If cell is empty or
|
||||
* contains empty string, then 0 is returned.
|
||||
|
25
src/fort.c
25
src/fort.c
@ -626,7 +626,7 @@ const char *ft_to_string(const FTABLE *table)
|
||||
clear:
|
||||
F_FREE(col_width_arr);
|
||||
F_FREE(row_height_arr);
|
||||
F_FREE(buffer);
|
||||
// F_FREE(buffer);
|
||||
return NULL;
|
||||
#undef cur_F_STRDUP
|
||||
}
|
||||
@ -727,7 +727,7 @@ const wchar_t *ft_to_wstring(const FTABLE *table)
|
||||
clear:
|
||||
F_FREE(col_width_arr);
|
||||
F_FREE(row_height_arr);
|
||||
F_FREE(buffer);
|
||||
// F_FREE(buffer);
|
||||
return NULL;
|
||||
#undef cur_F_STRDUP
|
||||
}
|
||||
@ -937,3 +937,24 @@ FT_EXTERN void ft_set_memory_funcs(void *(*f_malloc)(size_t size), void (*f_free
|
||||
{
|
||||
set_memory_funcs(f_malloc, f_free);
|
||||
}
|
||||
|
||||
#include "cell.h"
|
||||
FT_EXTERN void ft_some_api(FTABLE *table, size_t row, size_t col, size_t group_width)
|
||||
{
|
||||
assert(table);
|
||||
if (group_width == 0)
|
||||
return;
|
||||
|
||||
fort_row_t *row_p = get_row(table, row);
|
||||
fort_cell_t *main_cell = get_cell(row_p, col);
|
||||
set_cell_type(main_cell, GroupMasterCell);
|
||||
--group_width;
|
||||
++col;
|
||||
|
||||
while (group_width) {
|
||||
fort_cell_t *slave_cell = get_cell(row_p, col);
|
||||
set_cell_type(slave_cell, GroupSlaveCell);
|
||||
--group_width;
|
||||
++col;
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,8 @@
|
||||
|
||||
#define FORT_COL_SEPARATOR '|'
|
||||
|
||||
#define FORT_COL_SEPARATOR_LENGTH 1
|
||||
|
||||
#define FORT_UNUSED __attribute__((unused))
|
||||
|
||||
#define F_MALLOC fort_malloc
|
||||
@ -102,6 +104,14 @@ typedef struct separator separator_t;
|
||||
|
||||
|
||||
|
||||
enum CellType
|
||||
{
|
||||
CommonCell,
|
||||
GroupMasterCell,
|
||||
GroupSlaveCell
|
||||
};
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* LIBFORT helpers
|
||||
*****************************************************************************/
|
||||
|
146
src/row.c
146
src/row.c
@ -122,6 +122,41 @@ fort_status_t swap_row(fort_row_t *cur_row, fort_row_t *ins_row, size_t pos)
|
||||
return vector_swap(cur_row->cells, ins_row->cells, pos);
|
||||
}
|
||||
|
||||
size_t group_cell_number(const fort_row_t *row, size_t master_cell_col)
|
||||
{
|
||||
assert(row);
|
||||
const fort_cell_t *cell = get_cell_c(row, master_cell_col);
|
||||
if (cell == NULL)
|
||||
return 0;
|
||||
|
||||
size_t total_cols = vector_size(row->cells);
|
||||
size_t slave_col = master_cell_col + 1;
|
||||
while (slave_col < total_cols) {
|
||||
const fort_cell_t *cell = get_cell_c(row, slave_col);
|
||||
if (cell && get_cell_type(cell) == GroupSlaveCell) {
|
||||
++slave_col;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return slave_col - master_cell_col;
|
||||
}
|
||||
|
||||
int get_row_cell_types(const fort_row_t *row, enum CellType *types, size_t types_sz)
|
||||
{
|
||||
assert(row);
|
||||
assert(types);
|
||||
size_t i = 0;
|
||||
for (i = 0; i < types_sz; ++i) {
|
||||
const fort_cell_t *cell = get_cell_c(row, i);
|
||||
if (cell) {
|
||||
types[i] = get_cell_type(cell);
|
||||
} else {
|
||||
types[i] = CommonCell;
|
||||
}
|
||||
}
|
||||
return FT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -140,6 +175,34 @@ int print_row_separator(char *buffer, size_t buffer_sz,
|
||||
assert(buffer);
|
||||
assert(context);
|
||||
|
||||
int status = -1;
|
||||
|
||||
/* Get cell types
|
||||
*
|
||||
* Regions above top row and below bottom row areconsidered full of virtual
|
||||
* GroupSlaveCell cells
|
||||
*/
|
||||
enum CellType *top_row_types = F_MALLOC(sizeof(enum CellType) * cols * 2);
|
||||
if (top_row_types == NULL) {
|
||||
return FT_MEMORY_ERROR;
|
||||
}
|
||||
enum CellType *bottom_row_types = top_row_types + cols;
|
||||
if (upper_row) {
|
||||
get_row_cell_types(upper_row, top_row_types, cols);
|
||||
} else {
|
||||
size_t i = 0;
|
||||
for (i = 0; i < cols; ++i)
|
||||
top_row_types[i] = GroupSlaveCell;
|
||||
}
|
||||
if (lower_row) {
|
||||
get_row_cell_types(lower_row, bottom_row_types, cols);
|
||||
} else {
|
||||
size_t i = 0;
|
||||
for (i = 0; i < cols; ++i)
|
||||
bottom_row_types[i] = GroupSlaveCell;
|
||||
}
|
||||
|
||||
|
||||
int written = 0;
|
||||
int tmp = 0;
|
||||
|
||||
@ -152,15 +215,20 @@ int print_row_separator(char *buffer, size_t buffer_sz,
|
||||
upper_row_type = (enum ft_row_type)get_cell_opt_value_hierarcial(context->table_options, context->row - 1, FT_ANY_COLUMN, FT_COPT_ROW_TYPE);
|
||||
}
|
||||
|
||||
|
||||
/* Row separator anatomy
|
||||
/* Row separator anatomy
|
||||
*
|
||||
* L I I I IV I I I R
|
||||
*/
|
||||
* | C11 | C12 C13 | C14 C15 |
|
||||
* L I I I IV I I IT I I I IB I I II I I R
|
||||
* | C21 | C22 | C23 C24 C25 |
|
||||
*/
|
||||
const char *L = NULL;
|
||||
const char *I = NULL;
|
||||
const char *IV = NULL;
|
||||
const char *R = NULL;
|
||||
const char *IT = NULL;
|
||||
const char *IB = NULL;
|
||||
const char *II = NULL;
|
||||
|
||||
|
||||
typedef const char (*border_chars_point_t)[BorderItemPosSize];
|
||||
const char (*border_chars)[BorderItemPosSize] = NULL;
|
||||
@ -174,6 +242,10 @@ int print_row_separator(char *buffer, size_t buffer_sz,
|
||||
I = &(context->table_options->border_style.separator_chars[IH_sip]);
|
||||
IV = &(context->table_options->border_style.separator_chars[II_sip]);
|
||||
R = &(context->table_options->border_style.separator_chars[RH_sip]);
|
||||
|
||||
IT = &(context->table_options->border_style.separator_chars[II_sip]);
|
||||
IB = &(context->table_options->border_style.separator_chars[II_sip]);
|
||||
II = &(context->table_options->border_style.separator_chars[IH_sip]);
|
||||
} else {
|
||||
switch (separatorPos) {
|
||||
case TopSeparator:
|
||||
@ -181,18 +253,30 @@ int print_row_separator(char *buffer, size_t buffer_sz,
|
||||
I = &(*border_chars)[TT_bip];
|
||||
IV = &(*border_chars)[TV_bip];
|
||||
R = &(*border_chars)[TR_bip];
|
||||
|
||||
IT = &(*border_chars)[TV_bip];
|
||||
IB = &(*border_chars)[TV_bip];
|
||||
II = &(*border_chars)[TT_bip];
|
||||
break;
|
||||
case InsideSeparator:
|
||||
L = &(*border_chars)[LH_bip];
|
||||
I = &(*border_chars)[IH_bip];
|
||||
IV = &(*border_chars)[II_bip];
|
||||
R = &(*border_chars)[RH_bip];
|
||||
|
||||
IT = &(*border_chars)[TV_bip];
|
||||
IB = &(*border_chars)[BV_bip];
|
||||
II = &(*border_chars)[IH_bip];
|
||||
break;
|
||||
case BottomSeparator:
|
||||
L = &(*border_chars)[BL_bip];
|
||||
I = &(*border_chars)[BB_bip];
|
||||
IV = &(*border_chars)[BV_bip];
|
||||
R = &(*border_chars)[BR_bip];
|
||||
|
||||
IT = &(*border_chars)[BV_bip];
|
||||
IB = &(*border_chars)[BV_bip];
|
||||
II = &(*border_chars)[BB_bip];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@ -200,8 +284,10 @@ int print_row_separator(char *buffer, size_t buffer_sz,
|
||||
}
|
||||
|
||||
/* If all chars are not printable, skip line separator */ /* todo: add processing for wchar_t */
|
||||
if (!isprint(*L) && !isprint(*I) && !isprint(*IV) && !isprint(*R))
|
||||
return 0;
|
||||
if (!isprint(*L) && !isprint(*I) && !isprint(*IV) && !isprint(*R)) {
|
||||
status = 0;
|
||||
goto clear;
|
||||
}
|
||||
|
||||
size_t i = 0;
|
||||
|
||||
@ -212,7 +298,16 @@ int print_row_separator(char *buffer, size_t buffer_sz,
|
||||
if (i == 0) {
|
||||
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_chars_(buffer + written, buffer_sz - written, 1, (char_type)*L));
|
||||
} else {
|
||||
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_chars_(buffer + written, buffer_sz - written, 1, (char_type)*IV));
|
||||
if ((top_row_types[i] == CommonCell || top_row_types[i] == GroupMasterCell)
|
||||
&& (bottom_row_types[i] == CommonCell || bottom_row_types[i] == GroupMasterCell)) {
|
||||
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_chars_(buffer + written, buffer_sz - written, 1, (char_type)*IV));
|
||||
} else if (top_row_types[i] == GroupSlaveCell && bottom_row_types[i] == GroupSlaveCell) {
|
||||
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_chars_(buffer + written, buffer_sz - written, 1, (char_type)*II));
|
||||
} else if (top_row_types[i] == GroupSlaveCell) {
|
||||
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_chars_(buffer + written, buffer_sz - written, 1, (char_type)*IT));
|
||||
} else {
|
||||
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_chars_(buffer + written, buffer_sz - written, 1, (char_type)*IB));
|
||||
}
|
||||
}
|
||||
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_chars_(buffer + written, buffer_sz - written, col_width_arr[i], (char_type)*I));
|
||||
}
|
||||
@ -223,10 +318,11 @@ int print_row_separator(char *buffer, size_t buffer_sz,
|
||||
|
||||
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_chars_(buffer + written, buffer_sz - written, 1, new_line_char));
|
||||
|
||||
return written;
|
||||
status = written;
|
||||
|
||||
clear:
|
||||
return -1;
|
||||
F_FREE(top_row_types);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
@ -665,26 +761,44 @@ int snprintf_row(const fort_row_t *row, char *buffer, size_t buf_sz, size_t *col
|
||||
/* Print left margin */
|
||||
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_chars_(buffer + written, buf_sz - written, context->table_options->entire_table_options.left_margin, space_char));
|
||||
|
||||
/* Print left table boundary */
|
||||
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_chars_(buffer + written, buf_sz - written, 1, (char_type)*L));
|
||||
size_t j = 0;
|
||||
for (j = 0; j < col_width_arr_sz; ++j) {
|
||||
((context_t *)context)->column = j;
|
||||
while (j < col_width_arr_sz) {
|
||||
if (j < cols_in_row) {
|
||||
((context_t *)context)->column = j;
|
||||
fort_cell_t *cell = *(fort_cell_t **)vector_at(row->cells, j);
|
||||
CHCK_RSLT_ADD_TO_WRITTEN(cell_printf_(cell, i, buffer + written, col_width_arr[j] + 1, context));
|
||||
size_t cell_width = 0;
|
||||
|
||||
size_t group_slave_sz = group_cell_number(row, j);
|
||||
cell_width = col_width_arr[j];
|
||||
size_t slave_j = 0;
|
||||
size_t master_j = j;
|
||||
for (slave_j = master_j + 1; slave_j < (master_j + group_slave_sz); ++slave_j) {
|
||||
cell_width += col_width_arr[slave_j] + FORT_COL_SEPARATOR_LENGTH;
|
||||
++j;
|
||||
}
|
||||
|
||||
CHCK_RSLT_ADD_TO_WRITTEN(cell_printf_(cell, i, buffer + written, cell_width + 1, context));
|
||||
} else {
|
||||
/* Print empty cell */
|
||||
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_chars_(buffer + written, buf_sz - written, col_width_arr[j], space_char));
|
||||
}
|
||||
if (j == col_width_arr_sz - 1) {
|
||||
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_chars_(buffer + written, buf_sz - written, 1, (char_type)*R));
|
||||
} else {
|
||||
|
||||
/* Print boundary between cells */
|
||||
if (j < col_width_arr_sz - 1)
|
||||
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_chars_(buffer + written, buf_sz - written, 1, (char_type)*IV));
|
||||
}
|
||||
|
||||
++j;
|
||||
}
|
||||
|
||||
/* Print right table boundary */
|
||||
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_chars_(buffer + written, buf_sz - written, 1, (char_type)*R));
|
||||
|
||||
/* Print right margin */
|
||||
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_chars_(buffer + written, buf_sz - written, context->table_options->entire_table_options.right_margin, space_char));
|
||||
|
||||
/* Print new line character */
|
||||
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_chars_(buffer + written, buf_sz - written, 1, new_line_char));
|
||||
}
|
||||
return written;
|
||||
|
@ -27,7 +27,8 @@ const fort_cell_t *get_cell_c(const fort_row_t *row, size_t col);
|
||||
fort_cell_t *get_cell_and_create_if_not_exists(fort_row_t *row, size_t col);
|
||||
fort_status_t swap_row(fort_row_t *cur_row, fort_row_t *ins_row, size_t pos);
|
||||
|
||||
|
||||
size_t group_cell_number(const fort_row_t *row, size_t master_cell_col);
|
||||
int get_row_cell_types(const fort_row_t *row, enum CellType *types, size_t types_sz);
|
||||
|
||||
int print_row_separator(char *buffer, size_t buffer_sz,
|
||||
const size_t *col_width_arr, size_t cols,
|
||||
|
47
src/table.c
47
src/table.c
@ -112,6 +112,7 @@ fort_status_t table_rows_and_cols_geometry(const FTABLE *table,
|
||||
return FT_ERROR;
|
||||
}
|
||||
|
||||
int combined_cells_found = 0;
|
||||
context_t context;
|
||||
context.table_options = (table->options ? table->options : &g_table_options);
|
||||
size_t col = 0;
|
||||
@ -124,12 +125,56 @@ fort_status_t table_rows_and_cols_geometry(const FTABLE *table,
|
||||
context.column = col;
|
||||
context.row = row;
|
||||
if (cell) {
|
||||
col_width_arr[col] = MAX(col_width_arr[col], hint_width_cell(cell, &context));
|
||||
switch (get_cell_type(cell)) {
|
||||
case CommonCell:
|
||||
col_width_arr[col] = MAX(col_width_arr[col], hint_width_cell(cell, &context));
|
||||
break;
|
||||
case GroupMasterCell:
|
||||
combined_cells_found = 1;
|
||||
break;
|
||||
case GroupSlaveCell:
|
||||
; /* Do nothing */
|
||||
break;
|
||||
}
|
||||
row_height_arr[row] = MAX(row_height_arr[row], hint_height_cell(cell, &context));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (combined_cells_found) {
|
||||
col = 0;
|
||||
for (col = 0; col < cols; ++col) {
|
||||
size_t row = 0;
|
||||
for (row = 0; row < rows; ++row) {
|
||||
const fort_row_t *row_p = get_row_c(table, row);
|
||||
const fort_cell_t *cell = get_cell_c(row_p, col);
|
||||
context.column = col;
|
||||
context.row = row;
|
||||
if (cell) {
|
||||
if (get_cell_type(cell) == GroupMasterCell) {
|
||||
size_t hint_width = hint_width_cell(cell, &context);
|
||||
size_t slave_col = col + group_cell_number(row_p, col);
|
||||
size_t cur_adj_col = col;
|
||||
size_t group_width = col_width_arr[col];
|
||||
size_t i;
|
||||
for (i = col + 1; i < slave_col; ++i)
|
||||
group_width += col_width_arr[i] + FORT_COL_SEPARATOR_LENGTH;
|
||||
/* adjust col. widths */
|
||||
while (1) {
|
||||
if (group_width >= hint_width)
|
||||
break;
|
||||
col_width_arr[cur_adj_col] += 1;
|
||||
group_width++;
|
||||
cur_adj_col++;
|
||||
if (cur_adj_col == slave_col)
|
||||
cur_adj_col = col;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* todo: Maybe it is better to move min width checking to a particular cell width checking.
|
||||
* At the moment min width includes paddings. Maybe it is better that min width weren't include
|
||||
* paddings but be min width of the cell content without padding
|
||||
|
Loading…
Reference in New Issue
Block a user