diff options
Diffstat (limited to 'libs/litehtml/src/table.cpp')
-rw-r--r-- | libs/litehtml/src/table.cpp | 609 |
1 files changed, 609 insertions, 0 deletions
diff --git a/libs/litehtml/src/table.cpp b/libs/litehtml/src/table.cpp new file mode 100644 index 0000000000..08c167b222 --- /dev/null +++ b/libs/litehtml/src/table.cpp @@ -0,0 +1,609 @@ +#include "html.h" +#include "table.h" +#include "element.h" +#include "render_item.h" + +void litehtml::table_grid::add_cell(const std::shared_ptr<render_item>& el) +{ + table_cell cell; + cell.el = el; + cell.colspan = atoi(el->src_el()->get_attr("colspan", "1")); + cell.rowspan = atoi(el->src_el()->get_attr("rowspan", "1")); + cell.borders = el->get_borders(); + + while( is_rowspanned( (int) m_cells.size() - 1, (int) m_cells.back().size() ) ) + { + m_cells.back().push_back(table_cell()); + } + + m_cells.back().push_back(cell); + for(int i = 1; i < cell.colspan; i++) + { + table_cell empty_cell; + m_cells.back().push_back(empty_cell); + } +} + + +void litehtml::table_grid::begin_row(const std::shared_ptr<render_item>& row) +{ + std::vector<table_cell> r; + m_cells.push_back(r); + + m_rows.push_back(table_row(0, row)); +} + + +bool litehtml::table_grid::is_rowspanned( int r, int c ) +{ + for(int row = r - 1; row >= 0; row--) + { + if(c < (int) m_cells[row].size()) + { + if(m_cells[row][c].rowspan > 1) + { + if(m_cells[row][c].rowspan >= r - row + 1) + { + return true; + } + } + } + } + return false; +} + +void litehtml::table_grid::finish() +{ + m_rows_count = (int) m_cells.size(); + m_cols_count = 0; + for(auto& cell : m_cells) + { + m_cols_count = std::max(m_cols_count, (int) cell.size()); + } + for(auto& cell : m_cells) + { + for(int j = (int) cell.size(); j < m_cols_count; j++) + { + table_cell empty_cell; + cell.push_back(empty_cell); + } + } + + m_columns.clear(); + for(int i = 0; i < m_cols_count; i++) + { + m_columns.push_back(table_column(0, 0)); + } + + for(int col = 0; col < m_cols_count; col++) + { + for(int row = 0; row < m_rows_count; row++) + { + if(cell(col, row)->el) + { + // find minimum left border width + if(m_columns[col].border_left) + { + m_columns[col].border_left = std::min(m_columns[col].border_left, cell(col, row)->borders.left); + } else + { + m_columns[col].border_left = cell(col, row)->borders.left; + } + // find minimum right border width + if(m_columns[col].border_right) + { + m_columns[col].border_right = std::min(m_columns[col].border_right, cell(col, row)->borders.right); + } else + { + m_columns[col].border_right = cell(col, row)->borders.right; + } + // find minimum top border width + if(m_rows[row].border_top) + { + m_rows[row].border_top = std::min(m_rows[row].border_top, cell(col, row)->borders.top); + } else + { + m_rows[row].border_top = cell(col, row)->borders.top; + } + // find minimum bottom border width + if(m_rows[row].border_bottom) + { + m_rows[row].border_bottom = std::min(m_rows[row].border_bottom, cell(col, row)->borders.bottom); + } else + { + m_rows[row].border_bottom = cell(col, row)->borders.bottom; + } + } + + if(cell(col, row)->el && cell(col, row)->colspan <= 1) + { + if (!cell(col, row)->el->src_el()->css().get_width().is_predefined() && m_columns[col].css_width.is_predefined()) + { + m_columns[col].css_width = cell(col, row)->el->src_el()->css().get_width(); + } + } + } + } + + for(int col = 0; col < m_cols_count; col++) + { + for(int row = 0; row < m_rows_count; row++) + { + if(cell(col, row)->el && cell(col, row)->colspan == 1) + { + cell(col, row)->el->src_el()->css_w().set_width(m_columns[col].css_width); + } + } + } +} + +litehtml::table_cell* litehtml::table_grid::cell( int t_col, int t_row ) +{ + if(t_col >= 0 && t_col < m_cols_count && t_row >= 0 && t_row < m_rows_count) + { + return &m_cells[t_row][t_col]; + } + return nullptr; +} + +void litehtml::table_grid::distribute_max_width( int width, int start, int end ) +{ + table_column_accessor_max_width selector; + distribute_width(width, start, end, &selector); +} + +void litehtml::table_grid::distribute_min_width( int width, int start, int end ) +{ + table_column_accessor_min_width selector; + distribute_width(width, start, end, &selector); +} + +void litehtml::table_grid::distribute_width( int width, int start, int end, table_column_accessor* acc ) +{ + if(!(start >= 0 && start < m_cols_count && end >= 0 && end < m_cols_count)) + { + return; + } + + int cols_width = 0; + for(int col = start; col <= end; col++) + { + cols_width += m_columns[col].max_width; + } + + int add = width / (end - start + 1); + int added_width = 0; + for(int col = start; col <= end; col++) + { + if(cols_width) + { + add = round_f( (float) width * ((float) m_columns[col].max_width / (float) cols_width) ); + } + added_width += add; + acc->get(m_columns[col]) += add; + } + if(added_width < width) + { + acc->get(m_columns[start]) += width - added_width; + } +} + +void litehtml::table_grid::distribute_width( int width, int start, int end ) +{ + if(!(start >= 0 && start < m_cols_count && end >= 0 && end < m_cols_count)) + { + return; + } + + std::vector<table_column*> distribute_columns; + + for(int step = 0; step < 3; step++) + { + distribute_columns.clear(); + + switch(step) + { + case 0: + { + // distribute between the columns with width == auto + for(int col = start; col <= end; col++) + { + if(m_columns[col].css_width.is_predefined()) + { + distribute_columns.push_back(&m_columns[col]); + } + } + } + break; + case 1: + { + // distribute between the columns with percents + for(int col = start; col <= end; col++) + { + if(!m_columns[col].css_width.is_predefined() && m_columns[col].css_width.units() == css_units_percentage) + { + distribute_columns.push_back(&m_columns[col]); + } + } + } + break; + case 2: + { + //well distribute between all columns + for(int col = start; col <= end; col++) + { + distribute_columns.push_back(&m_columns[col]); + } + } + break; + } + + int added_width = 0; + + if(!distribute_columns.empty() || step == 2) + { + int cols_width = 0; + for(const auto& column : distribute_columns) + { + cols_width += column->max_width - column->min_width; + } + + if(cols_width) + { + int add = width / (int) distribute_columns.size(); + for(const auto& column : distribute_columns) + { + add = round_f( (float) width * ((float) (column->max_width - column->min_width) / (float) cols_width) ); + if(column->width + add >= column->min_width) + { + column->width += add; + added_width += add; + } else + { + added_width += (column->width - column->min_width) * (add / abs(add)); + column->width = column->min_width; + } + } + if(added_width < width && step) + { + distribute_columns.front()->width += width - added_width; + added_width = width; + } + } else + { + distribute_columns.back()->width += width; + added_width = width; + } + } + + if(added_width == width) + { + break; + } else + { + width -= added_width; + } + } +} + +int litehtml::table_grid::calc_table_width(int block_width, bool is_auto, int& min_table_width, int& max_table_width) +{ + //int table_width = 0; + + min_table_width = 0; // MIN + max_table_width = 0; // MAX + + int cur_width = 0; + int max_w = 0; + int min_w = 0; + + for(int col = 0; col < m_cols_count; col++) + { + min_table_width += m_columns[col].min_width; + max_table_width += m_columns[col].max_width; + + if(!m_columns[col].css_width.is_predefined()) + { + m_columns[col].width = m_columns[col].css_width.calc_percent(block_width); + m_columns[col].width = std::max(m_columns[col].width, m_columns[col].min_width); + } else + { + m_columns[col].width = m_columns[col].min_width; + max_w += m_columns[col].max_width; + min_w += m_columns[col].min_width; + } + + cur_width += m_columns[col].width; + } + + if(cur_width == block_width) + { + return cur_width; + } + + if(cur_width < block_width) + { + if(cur_width - min_w + max_w <= block_width) + { + cur_width = 0; + for(int col = 0; col < m_cols_count; col++) + { + if(m_columns[col].css_width.is_predefined()) + { + m_columns[col].width = m_columns[col].max_width; + } + cur_width += m_columns[col].width; + } + if(cur_width == block_width || is_auto) + { + return cur_width; + } + } + distribute_width(block_width - cur_width, 0, m_cols_count - 1); + cur_width = 0; + for(int col = 0; col < m_cols_count; col++) + { + cur_width += m_columns[col].width; + } + } else + { + int fixed_width = 0; + float percent = 0; + for(int col = 0; col < m_cols_count; col++) + { + if(!m_columns[col].css_width.is_predefined() && m_columns[col].css_width.units() == css_units_percentage) + { + percent += m_columns[col].css_width.val(); + } else + { + fixed_width += m_columns[col].width; + } + } + auto scale = (float) (100.0 / percent); + cur_width = 0; + for(int col = 0; col < m_cols_count; col++) + { + if(!m_columns[col].css_width.is_predefined() && m_columns[col].css_width.units() == css_units_percentage) + { + css_length w; + w.set_value(m_columns[col].css_width.val() * scale, css_units_percentage); + m_columns[col].width = w.calc_percent(block_width - fixed_width); + if(m_columns[col].width < m_columns[col].min_width) + { + m_columns[col].width = m_columns[col].min_width; + } + } + cur_width += m_columns[col].width; + } + // If the table is still too wide shrink columns with % widths + if(cur_width > block_width) + { + while(true) + { + bool shrunk = false; + for(int col = 0; col < m_cols_count; col++) + { + if(!m_columns[col].css_width.is_predefined() && m_columns[col].css_width.units() == css_units_percentage) + { + if(m_columns[col].width > m_columns[col].min_width) + { + m_columns[col].width--; + cur_width--; + shrunk = true; + if(cur_width == block_width) + { + break; + } + } + } + } + if(cur_width == block_width || !shrunk) + { + break; + } + } + } + } + return cur_width; +} + +void litehtml::table_grid::clear() +{ + m_rows_count = 0; + m_cols_count = 0; + m_cells.clear(); + m_columns.clear(); + m_rows.clear(); +} + +void litehtml::table_grid::calc_horizontal_positions( const margins& table_borders, border_collapse bc, int bdr_space_x) +{ + if(bc == border_collapse_separate) + { + int left = bdr_space_x; + for(int i = 0; i < m_cols_count; i++) + { + m_columns[i].left = left; + m_columns[i].right = m_columns[i].left + m_columns[i].width; + left = m_columns[i].right + bdr_space_x; + } + } else + { + int left = 0; + if(m_cols_count) + { + left -= std::min(table_borders.left, m_columns[0].border_left); + } + for(int i = 0; i < m_cols_count; i++) + { + if(i > 0) + { + left -= std::min(m_columns[i - 1].border_right, m_columns[i].border_left); + } + + m_columns[i].left = left; + m_columns[i].right = m_columns[i].left + m_columns[i].width; + left = m_columns[i].right; + } + } +} + +void litehtml::table_grid::calc_vertical_positions( const margins& table_borders, border_collapse bc, int bdr_space_y ) +{ + if(bc == border_collapse_separate) + { + int top = bdr_space_y; + for(int i = 0; i < m_rows_count; i++) + { + m_rows[i].top = top; + m_rows[i].bottom = m_rows[i].top + m_rows[i].height; + top = m_rows[i].bottom + bdr_space_y; + } + } else + { + int top = 0; + if(m_rows_count) + { + top -= std::min(table_borders.top, m_rows[0].border_top); + } + for(int i = 0; i < m_rows_count; i++) + { + if(i > 0) + { + top -= std::min(m_rows[i - 1].border_bottom, m_rows[i].border_top); + } + + m_rows[i].top = top; + m_rows[i].bottom = m_rows[i].top + m_rows[i].height; + top = m_rows[i].bottom; + } + } +} + +void litehtml::table_grid::calc_rows_height(int blockHeight, int /*borderSpacingY*/) +{ + int min_table_height = 0; + + // compute vertical size inferred by cells + for (auto& row : m_rows) + { + if (!row.css_height.is_predefined()) + { + if (row.css_height.units() != css_units_percentage) + { + if (row.height < (int)row.css_height.val()) + { + row.height = (int)row.css_height.val(); + } + } + } + row.min_height = row.height; + min_table_height += row.height; + } + + //min_table_height += borderSpacingY * ((int) m_rows.size() + 1); + + if (blockHeight > min_table_height) + { + int extra_height = blockHeight - min_table_height; + int auto_count = 0; // number of rows with height=auto + for (auto& row : m_rows) + { + if (!row.css_height.is_predefined() && row.css_height.units() == css_units_percentage) + { + row.height = row.css_height.calc_percent(blockHeight); + if (row.height < row.min_height) + { + row.height = row.min_height; + } + + extra_height -= row.height - row.min_height; + + if (extra_height <= 0) break; + } + else if (row.css_height.is_predefined()) + { + auto_count++; + } + } + if (extra_height > 0) + { + if (auto_count) + { + // distribute height to the rows with height=auto + int extra_row_height = (int)(extra_height / auto_count); + for (auto& row : m_rows) + { + if (row.css_height.is_predefined()) + { + row.height += extra_row_height; + } + } + } + else + { + // We don't have rows with height=auto, so distribute height to all rows + if (!m_rows.empty()) + { + int extra_row_height = (int)(extra_height / m_rows.size()); + for (auto& row : m_rows) + { + row.height += extra_row_height; + } + } + } + } + else if (extra_height < 0) + { + extra_height = -extra_height; + for (auto row = m_rows.rbegin(); row < m_rows.rend() && extra_height > 0; row++) + { + if (row->height > row->min_height) + { + if (row->height - extra_height >= row->min_height) + { + row->height -= extra_height; + extra_height = 0; + } + else + { + extra_height -= row->height - row->min_height; + row->height = row->min_height; + } + } + } + } + } +} + +////////////////////////////////////////////////////////////////////////// + +int& litehtml::table_column_accessor_max_width::get( table_column& col ) +{ + return col.max_width; +} + +int& litehtml::table_column_accessor_min_width::get( table_column& col ) +{ + return col.min_width; +} + +int& litehtml::table_column_accessor_width::get( table_column& col ) +{ + return col.width; +} + +litehtml::table_row::table_row(int h, const std::shared_ptr<render_item>& row) +{ + min_height = 0; + height = h; + el_row = row; + border_bottom = 0; + border_top = 0; + top = 0; + bottom = 0; + if (row) + { + css_height = row->src_el()->css().get_height(); + } +} |