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