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