2018-01-17 19:22:57 +01:00
|
|
|
#include "table.h"
|
|
|
|
#include "string_buffer.h"
|
|
|
|
#include "cell.h"
|
|
|
|
#include "vector.h"
|
|
|
|
#include "row.h"
|
|
|
|
|
2018-09-01 14:25:07 +02:00
|
|
|
FT_INTERNAL
|
|
|
|
separator_t *create_separator(int enabled)
|
|
|
|
{
|
|
|
|
separator_t *res = (separator_t *)F_CALLOC(1, sizeof(separator_t));
|
|
|
|
if (res == NULL)
|
|
|
|
return NULL;
|
|
|
|
res->enabled = enabled;
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
FT_INTERNAL
|
|
|
|
void destroy_separator(separator_t *sep)
|
|
|
|
{
|
|
|
|
F_FREE(sep);
|
|
|
|
}
|
2018-01-17 19:22:57 +01:00
|
|
|
|
|
|
|
|
2018-11-03 10:41:58 +01:00
|
|
|
FT_INTERNAL
|
|
|
|
separator_t *copy_separator(separator_t *sep)
|
|
|
|
{
|
|
|
|
assert(sep);
|
|
|
|
return create_separator(sep->enabled);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-09-01 14:25:07 +02:00
|
|
|
static
|
2018-05-05 21:34:45 +02:00
|
|
|
fort_row_t *get_row_implementation(ft_table_t *table, size_t row, enum PolicyOnNull policy)
|
2018-01-17 19:22:57 +01:00
|
|
|
{
|
|
|
|
if (table == NULL || table->rows == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (policy) {
|
|
|
|
case DoNotCreate:
|
|
|
|
if (row < vector_size(table->rows)) {
|
2018-03-31 12:33:37 +02:00
|
|
|
return *(fort_row_t **)vector_at(table->rows, row);
|
2018-01-17 19:22:57 +01:00
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
case Create:
|
2018-03-31 12:33:37 +02:00
|
|
|
while (row >= vector_size(table->rows)) {
|
2018-01-17 19:22:57 +01:00
|
|
|
fort_row_t *new_row = create_row();
|
|
|
|
if (new_row == NULL)
|
|
|
|
return NULL;
|
2018-05-05 17:38:45 +02:00
|
|
|
if (FT_IS_ERROR(vector_push(table->rows, &new_row))) {
|
2018-01-17 19:22:57 +01:00
|
|
|
destroy_row(new_row);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
2018-03-31 12:33:37 +02:00
|
|
|
return *(fort_row_t **)vector_at(table->rows, row);
|
2018-01-17 19:22:57 +01:00
|
|
|
}
|
2019-01-01 17:02:12 +01:00
|
|
|
|
|
|
|
assert(0 && "Shouldn't be here!");
|
2018-01-17 19:22:57 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2018-09-01 14:25:07 +02:00
|
|
|
|
|
|
|
FT_INTERNAL
|
2018-05-05 21:34:45 +02:00
|
|
|
fort_row_t *get_row(ft_table_t *table, size_t row)
|
2018-01-17 19:22:57 +01:00
|
|
|
{
|
|
|
|
return get_row_implementation(table, row, DoNotCreate);
|
|
|
|
}
|
|
|
|
|
2018-09-01 14:25:07 +02:00
|
|
|
|
|
|
|
FT_INTERNAL
|
2018-05-05 21:34:45 +02:00
|
|
|
const fort_row_t *get_row_c(const ft_table_t *table, size_t row)
|
2018-01-17 19:22:57 +01:00
|
|
|
{
|
2018-05-05 21:34:45 +02:00
|
|
|
return get_row((ft_table_t *)table, row);
|
2018-01-17 19:22:57 +01:00
|
|
|
}
|
|
|
|
|
2018-09-01 14:25:07 +02:00
|
|
|
|
|
|
|
FT_INTERNAL
|
2018-05-05 21:34:45 +02:00
|
|
|
fort_row_t *get_row_and_create_if_not_exists(ft_table_t *table, size_t row)
|
2018-01-17 19:22:57 +01:00
|
|
|
{
|
|
|
|
return get_row_implementation(table, row, Create);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-09-01 14:25:07 +02:00
|
|
|
FT_INTERNAL
|
2018-05-05 21:34:45 +02:00
|
|
|
string_buffer_t *get_cur_str_buffer_and_create_if_not_exists(ft_table_t *table)
|
2018-01-17 19:22:57 +01:00
|
|
|
{
|
|
|
|
assert(table);
|
|
|
|
|
|
|
|
fort_row_t *row = get_row_and_create_if_not_exists(table, table->cur_row);
|
|
|
|
if (row == NULL)
|
|
|
|
return NULL;
|
|
|
|
fort_cell_t *cell = get_cell_and_create_if_not_exists(row, table->cur_col);
|
|
|
|
if (cell == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return cell_get_string_buffer(cell);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Returns number of cells (rows * cols)
|
|
|
|
*/
|
2018-09-01 14:25:07 +02:00
|
|
|
FT_INTERNAL
|
2018-05-05 21:34:45 +02:00
|
|
|
fort_status_t get_table_sizes(const ft_table_t *table, size_t *rows, size_t *cols)
|
2018-01-17 19:22:57 +01:00
|
|
|
{
|
|
|
|
*rows = 0;
|
|
|
|
*cols = 0;
|
|
|
|
if (table && table->rows) {
|
|
|
|
*rows = vector_size(table->rows);
|
2019-01-01 19:43:21 +01:00
|
|
|
/*
|
2018-01-17 19:22:57 +01:00
|
|
|
fort_row_t *row = NULL;
|
2018-03-31 12:33:37 +02:00
|
|
|
FOR_EACH(fort_row_t *, row, table->rows) {
|
2018-11-18 09:32:07 +01:00
|
|
|
(void)i0;
|
2018-01-17 19:22:57 +01:00
|
|
|
size_t cols_in_row = columns_in_row(row);
|
|
|
|
if (cols_in_row > *cols)
|
|
|
|
*cols = cols_in_row;
|
|
|
|
}
|
2019-01-01 19:43:21 +01:00
|
|
|
*/
|
2019-01-01 21:13:41 +01:00
|
|
|
size_t row_index = 0;
|
2019-01-01 21:09:53 +01:00
|
|
|
for (row_index = 0; row_index < vector_size(table->rows); ++row_index) {
|
2019-01-01 21:06:19 +01:00
|
|
|
fort_row_t *row = *(fort_row_t **)vector_at(table->rows, row_index);
|
2019-01-01 19:43:21 +01:00
|
|
|
size_t cols_in_row = columns_in_row(row);
|
|
|
|
if (cols_in_row > *cols)
|
|
|
|
*cols = cols_in_row;
|
|
|
|
}
|
2018-01-17 19:22:57 +01:00
|
|
|
}
|
2018-03-19 21:07:18 +01:00
|
|
|
return FT_SUCCESS;
|
2018-01-17 19:22:57 +01:00
|
|
|
}
|
|
|
|
|
2018-09-01 14:25:07 +02:00
|
|
|
|
|
|
|
FT_INTERNAL
|
2018-05-05 21:34:45 +02:00
|
|
|
fort_status_t table_rows_and_cols_geometry(const ft_table_t *table,
|
2018-03-31 12:33:37 +02:00
|
|
|
size_t **col_width_arr_p, size_t *col_width_arr_sz,
|
2018-11-10 07:58:21 +01:00
|
|
|
size_t **row_height_arr_p, size_t *row_height_arr_sz,
|
|
|
|
enum request_geom_type geom)
|
2018-01-17 19:22:57 +01:00
|
|
|
{
|
|
|
|
if (table == NULL) {
|
2018-03-19 21:07:18 +01:00
|
|
|
return FT_ERROR;
|
2018-01-17 19:22:57 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
size_t cols = 0;
|
|
|
|
size_t rows = 0;
|
|
|
|
int status = get_table_sizes(table, &rows, &cols);
|
2018-05-05 17:38:45 +02:00
|
|
|
if (FT_IS_ERROR(status))
|
2018-01-17 19:22:57 +01:00
|
|
|
return status;
|
|
|
|
|
2018-03-17 19:53:38 +01:00
|
|
|
size_t *col_width_arr = (size_t *)F_CALLOC(sizeof(size_t), cols);
|
|
|
|
size_t *row_height_arr = (size_t *)F_CALLOC(sizeof(size_t), rows);
|
2018-01-17 19:22:57 +01:00
|
|
|
if (col_width_arr == NULL || row_height_arr == NULL) {
|
|
|
|
F_FREE(col_width_arr);
|
|
|
|
F_FREE(row_height_arr);
|
2018-03-19 21:07:18 +01:00
|
|
|
return FT_ERROR;
|
2018-01-17 19:22:57 +01:00
|
|
|
}
|
|
|
|
|
2018-05-02 16:55:29 +02:00
|
|
|
int combined_cells_found = 0;
|
2018-02-26 19:42:48 +01:00
|
|
|
context_t context;
|
2018-11-03 21:50:30 +01:00
|
|
|
context.table_properties = (table->properties ? table->properties : &g_table_properties);
|
2018-03-09 10:44:16 +01:00
|
|
|
size_t col = 0;
|
|
|
|
for (col = 0; col < cols; ++col) {
|
2018-01-17 19:22:57 +01:00
|
|
|
col_width_arr[col] = 0;
|
2018-03-09 10:44:16 +01:00
|
|
|
size_t row = 0;
|
|
|
|
for (row = 0; row < rows; ++row) {
|
2018-01-17 19:22:57 +01:00
|
|
|
const fort_row_t *row_p = get_row_c(table, row);
|
|
|
|
const fort_cell_t *cell = get_cell_c(row_p, col);
|
2018-02-26 19:42:48 +01:00
|
|
|
context.column = col;
|
|
|
|
context.row = row;
|
2018-01-17 19:22:57 +01:00
|
|
|
if (cell) {
|
2018-05-02 16:55:29 +02:00
|
|
|
switch (get_cell_type(cell)) {
|
|
|
|
case CommonCell:
|
2018-11-10 07:58:21 +01:00
|
|
|
col_width_arr[col] = MAX(col_width_arr[col], hint_width_cell(cell, &context, geom));
|
2018-05-02 16:55:29 +02:00
|
|
|
break;
|
|
|
|
case GroupMasterCell:
|
|
|
|
combined_cells_found = 1;
|
|
|
|
break;
|
|
|
|
case GroupSlaveCell:
|
|
|
|
; /* Do nothing */
|
|
|
|
break;
|
|
|
|
}
|
2018-02-26 19:42:48 +01:00
|
|
|
row_height_arr[row] = MAX(row_height_arr[row], hint_height_cell(cell, &context));
|
2019-01-03 10:06:24 +01:00
|
|
|
} else {
|
|
|
|
size_t cell_empty_string_height = get_cell_property_value_hierarcial(context.table_properties, context.row, context.column, FT_CPROP_EMPTY_STR_HEIGHT);
|
|
|
|
if (cell_empty_string_height) {
|
|
|
|
size_t cell_top_padding = get_cell_property_value_hierarcial(context.table_properties, context.row, context.column, FT_CPROP_TOP_PADDING);
|
|
|
|
size_t cell_bottom_padding = get_cell_property_value_hierarcial(context.table_properties, context.row, context.column, FT_CPROP_BOTTOM_PADDING);
|
|
|
|
row_height_arr[row] = MAX(row_height_arr[row], cell_empty_string_height + cell_top_padding + cell_bottom_padding);
|
|
|
|
}
|
2018-01-17 19:22:57 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-02 16:55:29 +02:00
|
|
|
if (combined_cells_found) {
|
|
|
|
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) {
|
2018-11-10 07:58:21 +01:00
|
|
|
size_t hint_width = hint_width_cell(cell, &context, geom);
|
2018-05-02 16:55:29 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-17 19:22:57 +01:00
|
|
|
/* 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
|
|
|
|
*/
|
2018-03-09 10:44:16 +01:00
|
|
|
/*
|
2018-11-03 21:50:30 +01:00
|
|
|
if (table->properties) {
|
2018-03-09 10:44:16 +01:00
|
|
|
for (size_t i = 0; i < cols; ++i) {
|
2018-11-03 21:50:30 +01:00
|
|
|
col_width_arr[i] = MAX((int)col_width_arr[i], fort_props_column_width(table->properties, i));
|
2018-03-09 10:44:16 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
*/
|
2018-01-17 19:22:57 +01:00
|
|
|
|
|
|
|
*col_width_arr_p = col_width_arr;
|
|
|
|
*col_width_arr_sz = cols;
|
|
|
|
*row_height_arr_p = row_height_arr;
|
|
|
|
*row_height_arr_sz = rows;
|
2018-03-19 21:07:18 +01:00
|
|
|
return FT_SUCCESS;
|
2018-01-17 19:22:57 +01:00
|
|
|
}
|
|
|
|
|
2018-09-01 14:25:07 +02:00
|
|
|
|
2018-01-17 19:22:57 +01:00
|
|
|
/*
|
|
|
|
* Returns geometry in characters
|
|
|
|
*/
|
2018-09-01 14:25:07 +02:00
|
|
|
FT_INTERNAL
|
2018-05-05 21:34:45 +02:00
|
|
|
fort_status_t table_geometry(const ft_table_t *table, size_t *height, size_t *width)
|
2018-01-17 19:22:57 +01:00
|
|
|
{
|
|
|
|
if (table == NULL)
|
2018-03-19 21:07:18 +01:00
|
|
|
return FT_ERROR;
|
2018-01-17 19:22:57 +01:00
|
|
|
|
|
|
|
*height = 0;
|
|
|
|
*width = 0;
|
|
|
|
size_t cols = 0;
|
|
|
|
size_t rows = 0;
|
|
|
|
size_t *col_width_arr = NULL;
|
|
|
|
size_t *row_height_arr = NULL;
|
|
|
|
|
2018-11-10 07:58:21 +01:00
|
|
|
int status = table_rows_and_cols_geometry(table, &col_width_arr, &cols, &row_height_arr, &rows, INTERN_REPR_GEOMETRY);
|
2018-05-05 17:38:45 +02:00
|
|
|
if (FT_IS_ERROR(status))
|
2018-01-17 19:22:57 +01:00
|
|
|
return status;
|
|
|
|
|
2018-03-09 10:44:16 +01:00
|
|
|
*width = 1 + (cols == 0 ? 1 : cols) + 1; /* for boundaries (that take 1 symbol) + newline */
|
|
|
|
size_t i = 0;
|
|
|
|
for (i = 0; i < cols; ++i) {
|
2018-01-17 19:22:57 +01:00
|
|
|
*width += col_width_arr[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
/* todo: add check for non printable horizontal row separators */
|
2018-03-09 10:44:16 +01:00
|
|
|
*height = 1 + (rows == 0 ? 1 : rows); /* for boundaries (that take 1 symbol) */
|
|
|
|
for (i = 0; i < rows; ++i) {
|
2018-01-17 19:22:57 +01:00
|
|
|
*height += row_height_arr[i];
|
|
|
|
}
|
|
|
|
F_FREE(col_width_arr);
|
|
|
|
F_FREE(row_height_arr);
|
2018-03-25 10:11:08 +02:00
|
|
|
|
2018-11-03 21:50:30 +01:00
|
|
|
if (table->properties) {
|
|
|
|
*height += table->properties->entire_table_properties.top_margin;
|
|
|
|
*height += table->properties->entire_table_properties.bottom_margin;
|
|
|
|
*width += table->properties->entire_table_properties.left_margin;
|
|
|
|
*width += table->properties->entire_table_properties.right_margin;
|
2018-03-25 10:11:08 +02:00
|
|
|
}
|
|
|
|
|
2018-05-02 20:16:41 +02:00
|
|
|
/* Take into account that border elements can be more than one byte long */
|
2018-11-03 21:50:30 +01:00
|
|
|
fort_table_properties_t *table_properties = table->properties ? table->properties : &g_table_properties;
|
|
|
|
size_t max_border_elem_len = max_border_elem_strlen(table_properties);
|
2018-05-02 20:16:41 +02:00
|
|
|
*width *= max_border_elem_len;
|
|
|
|
|
2018-03-19 21:07:18 +01:00
|
|
|
return FT_SUCCESS;
|
2018-01-17 19:22:57 +01:00
|
|
|
|
|
|
|
}
|
|
|
|
|