Cogs.Core
table.cpp
1#ifdef _WIN32
2#pragma warning(disable: 4100) // unreferenced formal parameter
3#elif defined(__clang__)
4#pragma clang diagnostic ignored "-Wunused-parameter"
5#endif
6#include "html.h"
7#include "table.h"
8#include "html_tag.h"
9
10void litehtml::table_grid::add_cell(element::ptr& el)
11{
12 table_cell cell;
13 cell.el = el;
14 cell.colspan = t_atoi(el->get_attr(_t("colspan"), _t("1")));
15 cell.rowspan = t_atoi(el->get_attr(_t("rowspan"), _t("1")));
16 cell.borders = el->get_borders();
17
18 while( is_rowspanned( (int) m_cells.size() - 1, (int) m_cells.back().size() ) )
19 {
20 m_cells.back().push_back(table_cell());
21 }
22
23 m_cells.back().push_back(cell);
24 for(int i = 1; i < cell.colspan; i++)
25 {
26 table_cell empty_cell;
27 m_cells.back().push_back(empty_cell);
28 }
29}
30
31
32void litehtml::table_grid::begin_row(element::ptr& row)
33{
34 std::vector<table_cell> r;
35 m_cells.push_back(r);
36
37 m_rows.push_back(table_row(0, row));
38
39}
40
41
42bool litehtml::table_grid::is_rowspanned( int r, int c )
43{
44 for(int row = r - 1; row >= 0; row--)
45 {
46 if(c < (int) m_cells[row].size())
47 {
48 if(m_cells[row][c].rowspan > 1)
49 {
50 if(m_cells[row][c].rowspan >= r - row + 1)
51 {
52 return true;
53 }
54 }
55 }
56 }
57 return false;
58}
59
60void litehtml::table_grid::finish()
61{
62 m_rows_count = (int) m_cells.size();
63 m_cols_count = 0;
64 for(int i = 0; i < (int) m_cells.size(); i++)
65 {
66 m_cols_count = std::max(m_cols_count, (int) m_cells[i].size());
67 }
68 for(int i = 0; i < (int) m_cells.size(); i++)
69 {
70 for(int j = (int) m_cells[i].size(); j < m_cols_count; j++)
71 {
72 table_cell empty_cell;
73 m_cells[i].push_back(empty_cell);
74 }
75 }
76
77 m_columns.clear();
78 for(int i = 0; i < m_cols_count; i++)
79 {
80 m_columns.push_back(table_column(0, 0));
81 }
82
83 for(int col = 0; col < m_cols_count; col++)
84 {
85 for(int row = 0; row < m_rows_count; row++)
86 {
87 if(cell(col, row)->el)
88 {
89 // find minimum left border width
90 if(m_columns[col].border_left)
91 {
92 m_columns[col].border_left = std::min(m_columns[col].border_left, cell(col, row)->borders.left);
93 } else
94 {
95 m_columns[col].border_left = cell(col, row)->borders.left;
96 }
97 // find minimum right border width
98 if(m_columns[col].border_right)
99 {
100 m_columns[col].border_right = std::min(m_columns[col].border_right, cell(col, row)->borders.right);
101 } else
102 {
103 m_columns[col].border_right = cell(col, row)->borders.right;
104 }
105 // find minimum top border width
106 if(m_rows[row].border_top)
107 {
108 m_rows[row].border_top = std::min(m_rows[row].border_top, cell(col, row)->borders.top);
109 } else
110 {
111 m_rows[row].border_top = cell(col, row)->borders.top;
112 }
113 // find minimum bottom border width
114 if(m_rows[row].border_bottom)
115 {
116 m_rows[row].border_bottom = std::min(m_rows[row].border_bottom, cell(col, row)->borders.bottom);
117 } else
118 {
119 m_rows[row].border_bottom = cell(col, row)->borders.bottom;
120 }
121 }
122
123 if(cell(col, row)->el && cell(col, row)->colspan <= 1)
124 {
125 if (!cell(col, row)->el->get_css_width().is_predefined() && m_columns[col].css_width.is_predefined())
126 {
127 m_columns[col].css_width = cell(col, row)->el->get_css_width();
128 }
129 }
130 }
131 }
132
133 for(int col = 0; col < m_cols_count; col++)
134 {
135 for(int row = 0; row < m_rows_count; row++)
136 {
137 if(cell(col, row)->el)
138 {
139 cell(col, row)->el->set_css_width(m_columns[col].css_width);
140 }
141 }
142 }
143}
144
145litehtml::table_cell* litehtml::table_grid::cell( int t_col, int t_row )
146{
147 if(t_col >= 0 && t_col < m_cols_count && t_row >= 0 && t_row < m_rows_count)
148 {
149 return &m_cells[t_row][t_col];
150 }
151 return 0;
152}
153
154void litehtml::table_grid::distribute_max_width( int width, int start, int end )
155{
156 table_column_accessor_max_width selector;
157 distribute_width(width, start, end, &selector);
158}
159
160void litehtml::table_grid::distribute_min_width( int width, int start, int end )
161{
162 table_column_accessor_min_width selector;
163 distribute_width(width, start, end, &selector);
164}
165
166void litehtml::table_grid::distribute_width( int width, int start, int end, table_column_accessor* acc )
167{
168 if(!(start >= 0 && start < m_cols_count && end >= 0 && end < m_cols_count))
169 {
170 return;
171 }
172
173 int cols_width = 0;
174 for(int col = start; col <= end; col++)
175 {
176 cols_width += m_columns[col].max_width;
177 }
178
179 int add = width / (end - start + 1);
180 int added_width = 0;
181 for(int col = start; col <= end; col++)
182 {
183 if(cols_width)
184 {
185 add = round_f( (float) width * ((float) m_columns[col].max_width / (float) cols_width) );
186 }
187 added_width += add;
188 acc->get(m_columns[col]) += add;
189 }
190 if(added_width < width)
191 {
192 acc->get(m_columns[start]) += width - added_width;
193 }
194}
195
196void litehtml::table_grid::distribute_width( int width, int start, int end )
197{
198 if(!(start >= 0 && start < m_cols_count && end >= 0 && end < m_cols_count))
199 {
200 return;
201 }
202
203 std::vector<table_column*> distribute_columns;
204
205 for(int step = 0; step < 3; step++)
206 {
207 distribute_columns.clear();
208
209 switch(step)
210 {
211 case 0:
212 {
213 // distribute between the columns with width == auto
214 for(int col = start; col <= end; col++)
215 {
216 if(m_columns[col].css_width.is_predefined())
217 {
218 distribute_columns.push_back(&m_columns[col]);
219 }
220 }
221 }
222 break;
223 case 1:
224 {
225 // distribute between the columns with percents
226 for(int col = start; col <= end; col++)
227 {
228 if(!m_columns[col].css_width.is_predefined() && m_columns[col].css_width.units() == css_units_percentage)
229 {
230 distribute_columns.push_back(&m_columns[col]);
231 }
232 }
233 }
234 break;
235 case 2:
236 {
237 //well distribute between all columns
238 for(int col = start; col <= end; col++)
239 {
240 distribute_columns.push_back(&m_columns[col]);
241 }
242 }
243 break;
244 }
245
246 int added_width = 0;
247
248 if(!distribute_columns.empty() || step == 2)
249 {
250 int cols_width = 0;
251 for(std::vector<table_column*>::iterator col = distribute_columns.begin(); col != distribute_columns.end(); col++)
252 {
253 cols_width += (*col)->max_width - (*col)->min_width;
254 }
255
256 if(cols_width)
257 {
258 int add = width / (int) distribute_columns.size();
259 for(std::vector<table_column*>::iterator col = distribute_columns.begin(); col != distribute_columns.end(); col++)
260 {
261 add = round_f( (float) width * ((float) ((*col)->max_width - (*col)->min_width) / (float) cols_width) );
262 if((*col)->width + add >= (*col)->min_width)
263 {
264 (*col)->width += add;
265 added_width += add;
266 } else
267 {
268 added_width += ((*col)->width - (*col)->min_width) * (add / abs(add));
269 (*col)->width = (*col)->min_width;
270 }
271 }
272 if(added_width < width && step)
273 {
274 distribute_columns.front()->width += width - added_width;
275 added_width = width;
276 }
277 } else
278 {
279 distribute_columns.back()->width += width;
280 added_width = width;
281 }
282 }
283
284 if(added_width == width)
285 {
286 break;
287 } else
288 {
289 width -= added_width;
290 }
291 }
292}
293
294int litehtml::table_grid::calc_table_width(int block_width, bool is_auto, int& min_table_width, int& max_table_width)
295{
296 //int table_width = 0;
297
298 min_table_width = 0; // MIN
299 max_table_width = 0; // MAX
300
301 int cur_width = 0;
302 int max_w = 0;
303 int min_w = 0;
304
305 for(int col = 0; col < m_cols_count; col++)
306 {
307 min_table_width += m_columns[col].min_width;
308 max_table_width += m_columns[col].max_width;
309
310 if(!m_columns[col].css_width.is_predefined())
311 {
312 m_columns[col].width = m_columns[col].css_width.calc_percent(block_width);
313 m_columns[col].width = std::max(m_columns[col].width, m_columns[col].min_width);
314 } else
315 {
316 m_columns[col].width = m_columns[col].min_width;
317 max_w += m_columns[col].max_width;
318 min_w += m_columns[col].min_width;
319 }
320
321 cur_width += m_columns[col].width;
322 }
323
324 if(cur_width == block_width)
325 {
326 return cur_width;
327 }
328
329 if(cur_width < block_width)
330 {
331 if(cur_width - min_w + max_w <= block_width)
332 {
333 cur_width = 0;
334 for(int col = 0; col < m_cols_count; col++)
335 {
336 if(m_columns[col].css_width.is_predefined())
337 {
338 m_columns[col].width = m_columns[col].max_width;
339 }
340 cur_width += m_columns[col].width;
341 }
342 if(cur_width == block_width || is_auto)
343 {
344 return cur_width;
345 }
346 }
347 distribute_width(block_width - cur_width, 0, m_cols_count - 1);
348 cur_width = 0;
349 for(int col = 0; col < m_cols_count; col++)
350 {
351 cur_width += m_columns[col].width;
352 }
353 } else
354 {
355 int fixed_width = 0;
356 float percent = 0;
357 for(int col = 0; col < m_cols_count; col++)
358 {
359 if(!m_columns[col].css_width.is_predefined() && m_columns[col].css_width.units() == css_units_percentage)
360 {
361 percent += m_columns[col].css_width.val();
362 } else
363 {
364 fixed_width += m_columns[col].width;
365 }
366 }
367 float scale = (float) (100.0 / percent);
368 cur_width = 0;
369 for(int col = 0; col < m_cols_count; col++)
370 {
371 if(!m_columns[col].css_width.is_predefined() && m_columns[col].css_width.units() == css_units_percentage)
372 {
373 css_length w;
374 w.set_value(m_columns[col].css_width.val() * scale, css_units_percentage);
375 m_columns[col].width = w.calc_percent(block_width - fixed_width);
376 if(m_columns[col].width < m_columns[col].min_width)
377 {
378 m_columns[col].width = m_columns[col].min_width;
379 }
380 }
381 cur_width += m_columns[col].width;
382 }
383 }
384 return cur_width;
385}
386
387void litehtml::table_grid::clear()
388{
389 m_rows_count = 0;
390 m_cols_count = 0;
391 m_cells.clear();
392 m_columns.clear();
393 m_rows.clear();
394}
395
396void litehtml::table_grid::calc_horizontal_positions( margins& table_borders, border_collapse bc, int bdr_space_x)
397{
398 if(bc == border_collapse_separate)
399 {
400 int left = bdr_space_x;
401 for(int i = 0; i < m_cols_count; i++)
402 {
403 m_columns[i].left = left;
404 m_columns[i].right = m_columns[i].left + m_columns[i].width;
405 left = m_columns[i].right + bdr_space_x;
406 }
407 } else
408 {
409 int left = 0;
410 if(m_cols_count)
411 {
412 left -= std::min(table_borders.left, m_columns[0].border_left);
413 }
414 for(int i = 0; i < m_cols_count; i++)
415 {
416 if(i > 0)
417 {
418 left -= std::min(m_columns[i - 1].border_right, m_columns[i].border_left);
419 }
420
421 m_columns[i].left = left;
422 m_columns[i].right = m_columns[i].left + m_columns[i].width;
423 left = m_columns[i].right;
424 }
425 }
426}
427
428void litehtml::table_grid::calc_vertical_positions( margins& table_borders, border_collapse bc, int bdr_space_y )
429{
430 if(bc == border_collapse_separate)
431 {
432 int top = bdr_space_y;
433 for(int i = 0; i < m_rows_count; i++)
434 {
435 m_rows[i].top = top;
436 m_rows[i].bottom = m_rows[i].top + m_rows[i].height;
437 top = m_rows[i].bottom + bdr_space_y;
438 }
439 } else
440 {
441 int top = 0;
442 if(m_rows_count)
443 {
444 top -= std::min(table_borders.top, m_rows[0].border_top);
445 }
446 for(int i = 0; i < m_rows_count; i++)
447 {
448 if(i > 0)
449 {
450 top -= std::min(m_rows[i - 1].border_bottom, m_rows[i].border_top);
451 }
452
453 m_rows[i].top = top;
454 m_rows[i].bottom = m_rows[i].top + m_rows[i].height;
455 top = m_rows[i].bottom;
456 }
457 }
458}
459
460void litehtml::table_grid::calc_rows_height(int blockHeight, int borderSpacingY)
461{
462 int min_table_height = 0;
463
464 // compute vertical size inferred by cells
465 for (auto& row : m_rows)
466 {
467 if (!row.css_height.is_predefined())
468 {
469 if (row.css_height.units() != css_units_percentage)
470 {
471 if (row.height < (int)row.css_height.val())
472 {
473 row.height = (int)row.css_height.val();
474 }
475 }
476 }
477 row.min_height = row.height;
478 min_table_height += row.height;
479 }
480
481 //min_table_height += borderSpacingY * ((int) m_rows.size() + 1);
482
483 if (blockHeight > min_table_height)
484 {
485 int extra_height = blockHeight - min_table_height;
486 int auto_count = 0; // number of rows with height=auto
487 for (auto& row : m_rows)
488 {
489 if (!row.css_height.is_predefined() && row.css_height.units() == css_units_percentage)
490 {
491 row.height = row.css_height.calc_percent(blockHeight);
492 if (row.height < row.min_height)
493 {
494 row.height = row.min_height;
495 }
496
497 extra_height -= row.height - row.min_height;
498
499 if (extra_height <= 0) break;
500 }
501 else if (row.css_height.is_predefined())
502 {
503 auto_count++;
504 }
505 }
506 if (extra_height > 0)
507 {
508 if (auto_count)
509 {
510 // distribute height to the rows with height=auto
511 int extra_row_height = (int)(extra_height / auto_count);
512 for (auto& row : m_rows)
513 {
514 if (row.css_height.is_predefined())
515 {
516 row.height += extra_row_height;
517 }
518 }
519 }
520 else
521 {
522 // We don't have rows with height=auto, so distribute height to all rows
523 if (!m_rows.empty())
524 {
525 int extra_row_height = (int)(extra_height / m_rows.size());
526 for (auto& row : m_rows)
527 {
528 row.height += extra_row_height;
529 }
530 }
531 }
532 }
533 else if (extra_height < 0)
534 {
535 extra_height = -extra_height;
536 for (auto row = m_rows.rbegin(); row < m_rows.rend() && extra_height > 0; row++)
537 {
538 if (row->height > row->min_height)
539 {
540 if (row->height - extra_height >= row->min_height)
541 {
542 row->height -= extra_height;
543 extra_height = 0;
544 }
545 else
546 {
547 extra_height -= row->height - row->min_height;
548 row->height = row->min_height;
549 }
550 }
551 }
552 }
553 }
554}
555
557
558int& litehtml::table_column_accessor_max_width::get( table_column& col )
559{
560 return col.max_width;
561}
562
563int& litehtml::table_column_accessor_min_width::get( table_column& col )
564{
565 return col.min_width;
566}
567
568int& litehtml::table_column_accessor_width::get( table_column& col )
569{
570 return col.width;
571}