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