[A] Added cell combining

This commit is contained in:
seleznevae 2018-05-02 17:55:29 +03:00
parent c8fe3fe280
commit cbb05f3db2
9 changed files with 244 additions and 21 deletions

View File

@ -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, "");

View File

@ -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

View File

@ -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:

View File

@ -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.

View File

@ -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;
}
}

View File

@ -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
View File

@ -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;

View File

@ -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,

View File

@ -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