add support for Ayatana indicator to Notification plugin
[claws.git] / src / plugins / litehtml_viewer / litehtml / render_table.cpp
blobfdff1f826d4f949f6fc65c0cc0be283ed77dc48e
1 #include "html.h"
2 #include "render_table.h"
3 #include "document.h"
4 #include "iterators.h"
7 litehtml::render_item_table::render_item_table(std::shared_ptr<element> _src_el) :
8 render_item(std::move(_src_el)),
9 m_border_spacing_x(0),
10 m_border_spacing_y(0)
14 int litehtml::render_item_table::_render(int x, int y, const containing_block_context &containing_block_size, formatting_context* fmt_ctx, bool second_pass)
16 if (!m_grid) return 0;
18 containing_block_context self_size = calculate_containing_block_context(containing_block_size);
20 // Calculate table spacing
21 int table_width_spacing = 0;
22 if (src_el()->css().get_border_collapse() == border_collapse_separate)
24 table_width_spacing = m_border_spacing_x * (m_grid->cols_count() + 1);
26 else
28 table_width_spacing = 0;
30 if (m_grid->cols_count())
32 table_width_spacing -= std::min(border_left(), m_grid->column(0).border_left);
33 table_width_spacing -= std::min(border_right(), m_grid->column(m_grid->cols_count() - 1).border_right);
36 for (int col = 1; col < m_grid->cols_count(); col++)
38 table_width_spacing -= std::min(m_grid->column(col).border_left, m_grid->column(col - 1).border_right);
43 // Calculate the minimum content width (MCW) of each cell: the formatted content may span any number of lines but may not overflow the cell box.
44 // If the specified 'width' (W) of the cell is greater than MCW, W is the minimum cell width. A value of 'auto' means that MCW is the minimum
45 // cell width.
47 // Also, calculate the "maximum" cell width of each cell: formatting the content without breaking lines other than where explicit line breaks occur.
49 if (m_grid->cols_count() == 1 && self_size.width.type != containing_block_context::cbc_value_type_auto)
51 for (int row = 0; row < m_grid->rows_count(); row++)
53 table_cell* cell = m_grid->cell(0, row);
54 if (cell && cell->el)
56 cell->min_width = cell->max_width = cell->el->render(0, 0, self_size.new_width(self_size.render_width - table_width_spacing), fmt_ctx);
57 cell->el->pos().width = cell->min_width - cell->el->content_offset_left() -
58 cell->el->content_offset_right();
62 else
64 for (int row = 0; row < m_grid->rows_count(); row++)
66 for (int col = 0; col < m_grid->cols_count(); col++)
68 table_cell* cell = m_grid->cell(col, row);
69 if (cell && cell->el)
71 if (!m_grid->column(col).css_width.is_predefined() && m_grid->column(col).css_width.units() != css_units_percentage)
73 int css_w = m_grid->column(col).css_width.calc_percent(self_size.width);
74 int el_w = cell->el->render(0, 0, self_size.new_width(css_w),fmt_ctx);
75 cell->min_width = cell->max_width = std::max(css_w, el_w);
76 cell->el->pos().width = cell->min_width - cell->el->content_offset_left() -
77 cell->el->content_offset_right();
79 else
81 // calculate minimum content width
82 cell->min_width = cell->el->render(0, 0, self_size.new_width(cell->el->content_offset_width()), fmt_ctx);
83 // calculate maximum content width
84 cell->max_width = cell->el->render(0, 0, self_size.new_width(self_size.render_width - table_width_spacing), fmt_ctx);
91 // For each column, determine a maximum and minimum column width from the cells that span only that column.
92 // The minimum is that required by the cell with the largest minimum cell width (or the column 'width', whichever is larger).
93 // The maximum is that required by the cell with the largest maximum cell width (or the column 'width', whichever is larger).
95 for (int col = 0; col < m_grid->cols_count(); col++)
97 m_grid->column(col).max_width = 0;
98 m_grid->column(col).min_width = 0;
99 for (int row = 0; row < m_grid->rows_count(); row++)
101 if (m_grid->cell(col, row)->colspan <= 1)
103 m_grid->column(col).max_width = std::max(m_grid->column(col).max_width, m_grid->cell(col, row)->max_width);
104 m_grid->column(col).min_width = std::max(m_grid->column(col).min_width, m_grid->cell(col, row)->min_width);
109 // For each cell that spans more than one column, increase the minimum widths of the columns it spans so that together,
110 // they are at least as wide as the cell. Do the same for the maximum widths.
111 // If possible, widen all spanned columns by approximately the same amount.
113 for (int col = 0; col < m_grid->cols_count(); col++)
115 for (int row = 0; row < m_grid->rows_count(); row++)
117 if (m_grid->cell(col, row)->colspan > 1)
119 int max_total_width = m_grid->column(col).max_width;
120 int min_total_width = m_grid->column(col).min_width;
121 for (int col2 = col + 1; col2 < col + m_grid->cell(col, row)->colspan; col2++)
123 max_total_width += m_grid->column(col2).max_width;
124 min_total_width += m_grid->column(col2).min_width;
126 if (min_total_width < m_grid->cell(col, row)->min_width)
128 m_grid->distribute_min_width(m_grid->cell(col, row)->min_width - min_total_width, col, col + m_grid->cell(col, row)->colspan - 1);
130 if (max_total_width < m_grid->cell(col, row)->max_width)
132 m_grid->distribute_max_width(m_grid->cell(col, row)->max_width - max_total_width, col, col + m_grid->cell(col, row)->colspan - 1);
138 // If the 'table' or 'inline-table' element's 'width' property has a computed value (W) other than 'auto', the used width is the
139 // greater of W, CAPMIN, and the minimum width required by all the columns plus cell spacing or borders (MIN).
140 // If the used width is greater than MIN, the extra width should be distributed over the columns.
142 // If the 'table' or 'inline-table' element has 'width: auto', the used width is the greater of the table's containing block width,
143 // CAPMIN, and MIN. However, if either CAPMIN or the maximum width required by the columns plus cell spacing or borders (MAX) is
144 // less than that of the containing block, use max(MAX, CAPMIN).
147 int table_width = 0;
148 int min_table_width = 0;
149 int max_table_width = 0;
151 if (self_size.width.type == containing_block_context::cbc_value_type_absolute)
153 table_width = m_grid->calc_table_width(self_size.render_width - table_width_spacing, false, min_table_width, max_table_width);
155 else
157 table_width = m_grid->calc_table_width(self_size.render_width - table_width_spacing, self_size.width.type == containing_block_context::cbc_value_type_auto, min_table_width, max_table_width);
160 min_table_width += table_width_spacing;
161 max_table_width += table_width_spacing;
162 table_width += table_width_spacing;
163 m_grid->calc_horizontal_positions(m_borders, src_el()->css().get_border_collapse(), m_border_spacing_x);
165 bool row_span_found = false;
167 // render cells with computed width
168 for (int row = 0; row < m_grid->rows_count(); row++)
170 m_grid->row(row).height = 0;
171 for (int col = 0; col < m_grid->cols_count(); col++)
173 table_cell* cell = m_grid->cell(col, row);
174 if (cell->el)
176 int span_col = col + cell->colspan - 1;
177 if (span_col >= m_grid->cols_count())
179 span_col = m_grid->cols_count() - 1;
181 int cell_width = m_grid->column(span_col).right - m_grid->column(col).left;
183 //if (cell->el->pos().width != cell_width - cell->el->content_offset_left() -
184 // cell->el->content_offset_right())
186 cell->el->render(m_grid->column(col).left, 0, self_size.new_width(cell_width), fmt_ctx, true);
187 cell->el->pos().width = cell_width - cell->el->content_offset_left() -
188 cell->el->content_offset_right();
190 /*else
192 cell->el->pos().x = m_grid->column(col).left + cell->el->content_offset_left();
195 if (cell->rowspan <= 1)
197 m_grid->row(row).height = std::max(m_grid->row(row).height, cell->el->height());
199 else
201 row_span_found = true;
208 if (row_span_found)
210 for (int col = 0; col < m_grid->cols_count(); col++)
212 for (int row = 0; row < m_grid->rows_count(); row++)
214 table_cell* cell = m_grid->cell(col, row);
215 if (cell->el)
217 int span_row = row + cell->rowspan - 1;
218 if (span_row >= m_grid->rows_count())
220 span_row = m_grid->rows_count() - 1;
222 if (span_row != row)
224 int h = 0;
225 for (int i = row; i <= span_row; i++)
227 h += m_grid->row(i).height;
229 if (h < cell->el->height())
231 m_grid->row(span_row).height += cell->el->height() - h;
239 // Calculate vertical table spacing
240 int table_height_spacing = 0;
241 if (src_el()->css().get_border_collapse() == border_collapse_separate)
243 table_height_spacing = m_border_spacing_y * (m_grid->rows_count() + 1);
245 else
247 table_height_spacing = 0;
249 if (m_grid->rows_count())
251 table_height_spacing -= std::min(border_top(), m_grid->row(0).border_top);
252 table_height_spacing -= std::min(border_bottom(), m_grid->row(m_grid->rows_count() - 1).border_bottom);
255 for (int row = 1; row < m_grid->rows_count(); row++)
257 table_height_spacing -= std::min(m_grid->row(row).border_top, m_grid->row(row - 1).border_bottom);
262 // calculate block height
263 int block_height = 0;
264 if(self_size.height.type != containing_block_context::cbc_value_type_auto && self_size.height > 0)
266 block_height = self_size.height - (m_padding.height() + m_borders.height());
269 // calculate minimum height from m_css.get_min_height()
270 int min_height = 0;
271 if (!src_el()->css().get_min_height().is_predefined() && src_el()->css().get_min_height().units() == css_units_percentage)
273 min_height = src_el()->css().get_min_height().calc_percent(containing_block_size.height);
275 else
277 min_height = (int)src_el()->css().get_min_height().val();
280 int minimum_table_height = std::max(block_height, min_height);
282 m_grid->calc_rows_height(minimum_table_height - table_height_spacing, m_border_spacing_y);
283 m_grid->calc_vertical_positions(m_borders, src_el()->css().get_border_collapse(), m_border_spacing_y);
285 int table_height = 0;
287 // place cells vertically
288 for (int col = 0; col < m_grid->cols_count(); col++)
290 for (int row = 0; row < m_grid->rows_count(); row++)
292 table_cell* cell = m_grid->cell(col, row);
293 if (cell->el)
295 int span_row = row + cell->rowspan - 1;
296 if (span_row >= m_grid->rows_count())
298 span_row = m_grid->rows_count() - 1;
300 cell->el->pos().y = m_grid->row(row).top + cell->el->content_offset_top();
301 cell->el->pos().height = m_grid->row(span_row).bottom - m_grid->row(row).top -
302 cell->el->content_offset_top() -
303 cell->el->content_offset_bottom();
304 table_height = std::max(table_height, m_grid->row(span_row).bottom);
305 cell->el->apply_vertical_align();
310 if (src_el()->css().get_border_collapse() == border_collapse_collapse)
312 if (m_grid->rows_count())
314 table_height -= std::min(border_bottom(), m_grid->row(m_grid->rows_count() - 1).border_bottom);
317 else
319 table_height += m_border_spacing_y;
322 // Render table captions
323 // Table border doesn't round the caption, so we have to start caption in the border position
324 int top_captions = -border_top();
326 for (auto& caption : m_grid->captions())
328 if(caption->css().get_caption_side() == caption_side_top)
330 caption->render(-border_left(), top_captions, self_size.new_width(table_width + border_left() + border_right()), fmt_ctx);
331 top_captions += caption->height();
335 if (top_captions)
337 // Add border height to get the top of cells
338 top_captions += border_top();
340 // Save caption height for draw_background
341 m_grid->top_captions_height(top_captions);
343 // Move table cells to the bottom side
344 for (int row = 0; row < m_grid->rows_count(); row++)
346 m_grid->row(row).el_row->pos().y += top_captions;
347 for (int col = 0; col < m_grid->cols_count(); col++)
349 table_cell* cell = m_grid->cell(col, row);
350 if (cell->el)
352 cell->el->pos().y += top_captions;
358 int bottom_captions = 0;
360 for (auto& caption : m_grid->captions())
362 if(caption->css().get_caption_side() == caption_side_bottom)
364 caption->render(-border_left(), table_height + top_captions + bottom_captions, self_size.new_width(table_width + border_left() + border_right()), fmt_ctx);
365 bottom_captions += caption->height();
369 m_pos.move_to(x + content_offset_left(), y + content_offset_top());
370 m_pos.width = table_width;
371 m_pos.height = table_height + top_captions + bottom_captions;
373 if(self_size.width.type != containing_block_context::cbc_value_type_absolute)
375 return std::min(table_width, max_table_width) + content_offset_width();
377 return table_width + content_offset_width();
380 std::shared_ptr<litehtml::render_item> litehtml::render_item_table::init()
382 // Initialize Grid
383 m_grid = std::unique_ptr<table_grid>(new table_grid());
385 go_inside_table table_selector;
386 table_rows_selector row_selector;
387 table_cells_selector cell_selector;
389 elements_iterator row_iter(false, &table_selector, &row_selector);
391 row_iter.process(shared_from_this(), [&](std::shared_ptr<render_item>& el, iterator_item_type item_type)
393 m_grid->begin_row(el);
396 elements_iterator cell_iter(true, &table_selector, &cell_selector);
397 cell_iter.process(el, [&](std::shared_ptr<render_item>& el, iterator_item_type item_type)
399 if(item_type != iterator_item_type_end_parent)
401 el = el->init();
402 m_grid->add_cell(el);
407 for (auto& el : m_children)
409 if (el->src_el()->css().get_display() == display_table_caption)
411 el = el->init();
412 m_grid->captions().push_back(el);
416 m_grid->finish();
418 if(src_el()->css().get_border_collapse() == border_collapse_separate)
420 int font_size = src_el()->css().get_font_size();
421 document::ptr doc = src_el()->get_document();
422 m_border_spacing_x = doc->to_pixels(src_el()->css().get_border_spacing_x(), font_size);
423 m_border_spacing_y = doc->to_pixels(src_el()->css().get_border_spacing_y(), font_size);
424 } else
426 m_border_spacing_x = 0;
427 m_border_spacing_y = 0;
430 src_el()->add_render(shared_from_this());
432 return shared_from_this();
435 void litehtml::render_item_table::draw_children(uint_ptr hdc, int x, int y, const position* clip, draw_flag flag, int zindex)
437 if (!m_grid) return;
439 position pos = m_pos;
440 pos.x += x;
441 pos.y += y;
442 for (auto& caption : m_grid->captions())
444 if (flag == draw_block)
446 caption->src_el()->draw(hdc, pos.x, pos.y, clip, caption);
448 caption->draw_children(hdc, pos.x, pos.y, clip, flag, zindex);
450 for (int row = 0; row < m_grid->rows_count(); row++)
452 if (flag == draw_block)
454 m_grid->row(row).el_row->src_el()->draw_background(hdc, pos.x, pos.y, clip, m_grid->row(row).el_row);
456 for (int col = 0; col < m_grid->cols_count(); col++)
458 table_cell* cell = m_grid->cell(col, row);
459 if (cell->el)
461 if (flag == draw_block)
463 cell->el->src_el()->draw(hdc, pos.x, pos.y, clip, cell->el);
465 cell->el->draw_children(hdc, pos.x, pos.y, clip, flag, zindex);
471 int litehtml::render_item_table::get_draw_vertical_offset()
473 if(m_grid)
475 return m_grid->top_captions_height();
477 return 0;
480 void litehtml::render_item_table_row::get_inline_boxes( position::vector& boxes ) const
482 position pos;
483 for(auto& el : m_children)
485 if(el->src_el()->css().get_display() == display_table_cell)
487 pos.x = el->left() + el->margin_left();
488 pos.y = el->top() - m_padding.top - m_borders.top;
490 pos.width = el->right() - pos.x - el->margin_right() - el->margin_left();
491 pos.height = el->height() + m_padding.top + m_padding.bottom + m_borders.top + m_borders.bottom;
493 boxes.push_back(pos);