1 /*
2 * File: table.cc
3 *
4 * Copyright 2008 Jorge Arellano Cid <jcid@dillo.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 */
11
12 #include "table.hh"
13 #include "html_common.hh"
14
15 #include "dw/style.hh"
16 #include "dw/textblock.hh"
17 #include "dw/table.hh"
18
19 #include "prefs.h"
20 #include "msg.h"
21 #include "css.hh"
22
23 using namespace dw;
24 using namespace dw::core;
25 using namespace dw::core::style;
26
27 /*
28 * Forward declarations
29 */
30
31 static void Html_tag_open_table_cell(DilloHtml *html,
32 const char *tag, int tagsize,
33 dw::core::style::TextAlignType text_align);
34 static void Html_tag_content_table_cell(DilloHtml *html,
35 const char *tag, int tagsize);
36
37 /*
38 * <TABLE>
39 */
Html_tag_open_table(DilloHtml * html,const char * tag,int tagsize)40 void Html_tag_open_table(DilloHtml *html, const char *tag, int tagsize)
41 {
42 const char *attrbuf;
43 int32_t border = -1, cellspacing = -1, cellpadding = -1, bgcolor = -1;
44 CssLength cssLength;
45
46 if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "border")))
47 border = isdigit(attrbuf[0]) ? strtol (attrbuf, NULL, 10) : 1;
48 if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "cellspacing"))) {
49 cellspacing = strtol (attrbuf, NULL, 10);
50 if (html->DocType == DT_HTML && html->DocTypeVersion >= 5.0f)
51 BUG_MSG("<table> cellspacing attribute is obsolete.");
52 }
53
54 if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "cellpadding"))) {
55 cellpadding = strtol (attrbuf, NULL, 10);
56 if (html->DocType == DT_HTML && html->DocTypeVersion >= 5.0f)
57 BUG_MSG("<table> cellpadding attribute is obsolete.");
58 }
59
60 if (border != -1) {
61 cssLength = CSS_CREATE_LENGTH (border, CSS_LENGTH_TYPE_PX);
62 html->styleEngine->setNonCssHint (CSS_PROPERTY_BORDER_TOP_WIDTH,
63 CSS_TYPE_LENGTH_PERCENTAGE, cssLength);
64 html->styleEngine->setNonCssHint (CSS_PROPERTY_BORDER_BOTTOM_WIDTH,
65 CSS_TYPE_LENGTH_PERCENTAGE, cssLength);
66 html->styleEngine->setNonCssHint (CSS_PROPERTY_BORDER_LEFT_WIDTH,
67 CSS_TYPE_LENGTH_PERCENTAGE, cssLength);
68 html->styleEngine->setNonCssHint (CSS_PROPERTY_BORDER_RIGHT_WIDTH,
69 CSS_TYPE_LENGTH_PERCENTAGE, cssLength);
70 html->styleEngine->setNonCssHint (CSS_PROPERTY_BORDER_TOP_STYLE,
71 CSS_TYPE_ENUM, BORDER_OUTSET);
72 html->styleEngine->setNonCssHint (CSS_PROPERTY_BORDER_BOTTOM_STYLE,
73 CSS_TYPE_ENUM, BORDER_OUTSET);
74 html->styleEngine->setNonCssHint (CSS_PROPERTY_BORDER_LEFT_STYLE,
75 CSS_TYPE_ENUM, BORDER_OUTSET);
76 html->styleEngine->setNonCssHint (CSS_PROPERTY_BORDER_RIGHT_STYLE,
77 CSS_TYPE_ENUM, BORDER_OUTSET);
78 }
79
80 if (cellspacing != -1) {
81 cssLength = CSS_CREATE_LENGTH (cellspacing, CSS_LENGTH_TYPE_PX);
82 html->styleEngine->setNonCssHint (CSS_PROPERTY_BORDER_SPACING,
83 CSS_TYPE_LENGTH_PERCENTAGE, cssLength);
84 }
85
86 if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "width"))) {
87 html->styleEngine->setNonCssHint (CSS_PROPERTY_WIDTH,
88 CSS_TYPE_LENGTH_PERCENTAGE,
89 a_Html_parse_length (html, attrbuf));
90 if (html->DocType == DT_HTML && html->DocTypeVersion >= 5.0f)
91 BUG_MSG("<table> width attribute is obsolete.");
92 }
93
94 if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "align"))) {
95 if (dStrAsciiCasecmp (attrbuf, "left") == 0)
96 html->styleEngine->setNonCssHint (CSS_PROPERTY_TEXT_ALIGN,
97 CSS_TYPE_ENUM, TEXT_ALIGN_LEFT);
98 else if (dStrAsciiCasecmp (attrbuf, "right") == 0)
99 html->styleEngine->setNonCssHint (CSS_PROPERTY_TEXT_ALIGN,
100 CSS_TYPE_ENUM, TEXT_ALIGN_RIGHT);
101 else if (dStrAsciiCasecmp (attrbuf, "center") == 0)
102 html->styleEngine->setNonCssHint (CSS_PROPERTY_TEXT_ALIGN,
103 CSS_TYPE_ENUM, TEXT_ALIGN_CENTER);
104 if (html->DocType == DT_HTML && html->DocTypeVersion >= 5.0f)
105 BUG_MSG("<table> align attribute is obsolete.");
106 }
107
108 if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "bgcolor"))) {
109 bgcolor = a_Html_color_parse(html, attrbuf, -1);
110 if (bgcolor != -1)
111 html->styleEngine->setNonCssHint (CSS_PROPERTY_BACKGROUND_COLOR,
112 CSS_TYPE_COLOR, bgcolor);
113 if (html->DocType == DT_HTML && html->DocTypeVersion >= 5.0f)
114 BUG_MSG("<table> bgcolor attribute is obsolete.");
115 }
116
117 html->style (); // evaluate now, so we can build non-css hints for the cells
118
119 /* The style for the cells */
120 html->styleEngine->clearNonCssHints ();
121 if (border > 0) {
122 cssLength = CSS_CREATE_LENGTH (1, CSS_LENGTH_TYPE_PX);
123 html->styleEngine->setNonCssHint (CSS_PROPERTY_BORDER_TOP_WIDTH,
124 CSS_TYPE_LENGTH_PERCENTAGE, cssLength);
125 html->styleEngine->setNonCssHint (CSS_PROPERTY_BORDER_BOTTOM_WIDTH,
126 CSS_TYPE_LENGTH_PERCENTAGE, cssLength);
127 html->styleEngine->setNonCssHint (CSS_PROPERTY_BORDER_LEFT_WIDTH,
128 CSS_TYPE_LENGTH_PERCENTAGE, cssLength);
129 html->styleEngine->setNonCssHint (CSS_PROPERTY_BORDER_RIGHT_WIDTH,
130 CSS_TYPE_LENGTH_PERCENTAGE, cssLength);
131 html->styleEngine->setNonCssHint (CSS_PROPERTY_BORDER_TOP_STYLE,
132 CSS_TYPE_ENUM, BORDER_INSET);
133 html->styleEngine->setNonCssHint (CSS_PROPERTY_BORDER_BOTTOM_STYLE,
134 CSS_TYPE_ENUM, BORDER_INSET);
135 html->styleEngine->setNonCssHint (CSS_PROPERTY_BORDER_LEFT_STYLE,
136 CSS_TYPE_ENUM, BORDER_INSET);
137 html->styleEngine->setNonCssHint (CSS_PROPERTY_BORDER_RIGHT_STYLE,
138 CSS_TYPE_ENUM, BORDER_INSET);
139 }
140
141 if (cellpadding != -1) {
142 cssLength = CSS_CREATE_LENGTH (cellpadding, CSS_LENGTH_TYPE_PX);
143 html->styleEngine->setNonCssHint (CSS_PROPERTY_PADDING_TOP,
144 CSS_TYPE_LENGTH_PERCENTAGE, cssLength);
145 html->styleEngine->setNonCssHint (CSS_PROPERTY_PADDING_BOTTOM,
146 CSS_TYPE_LENGTH_PERCENTAGE, cssLength);
147 html->styleEngine->setNonCssHint (CSS_PROPERTY_PADDING_LEFT,
148 CSS_TYPE_LENGTH_PERCENTAGE, cssLength);
149 html->styleEngine->setNonCssHint (CSS_PROPERTY_PADDING_RIGHT,
150 CSS_TYPE_LENGTH_PERCENTAGE, cssLength);
151 }
152
153 }
Html_tag_content_table(DilloHtml * html,const char * tag,int tagsize)154 void Html_tag_content_table(DilloHtml *html, const char *tag, int tagsize)
155 {
156 dw::core::Widget *table;
157
158 HT2TB(html)->addParbreak (0, html->wordStyle ());
159 table = new dw::Table(prefs.limit_text_width);
160 HT2TB(html)->addWidget (table, html->style ());
161 HT2TB(html)->addParbreak (0, html->wordStyle ());
162
163 S_TOP(html)->table_mode = DILLO_HTML_TABLE_MODE_TOP;
164 S_TOP(html)->table_border_mode = DILLO_HTML_TABLE_BORDER_SEPARATE;
165 S_TOP(html)->cell_text_align_set = FALSE;
166 S_TOP(html)->table = table;
167
168 }
169
170 /*
171 * <TR>
172 */
Html_tag_open_tr(DilloHtml * html,const char * tag,int tagsize)173 void Html_tag_open_tr(DilloHtml *html, const char *tag, int tagsize)
174 {
175 const char *attrbuf;
176 int32_t bgcolor = -1;
177
178 html->styleEngine->inheritNonCssHints ();
179
180 switch (S_TOP(html)->table_mode) {
181 case DILLO_HTML_TABLE_MODE_NONE:
182 _MSG("Invalid HTML syntax: <tr> outside <table>\n");
183 return;
184
185 case DILLO_HTML_TABLE_MODE_TOP:
186 case DILLO_HTML_TABLE_MODE_TR:
187 case DILLO_HTML_TABLE_MODE_TD:
188
189 if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "bgcolor"))) {
190 bgcolor = a_Html_color_parse(html, attrbuf, -1);
191 if (bgcolor != -1)
192 html->styleEngine->setNonCssHint (CSS_PROPERTY_BACKGROUND_COLOR,
193 CSS_TYPE_COLOR, bgcolor);
194 if (html->DocType == DT_HTML && html->DocTypeVersion >= 5.0f)
195 BUG_MSG("<tr> bgcolor attribute is obsolete.");
196 }
197
198 if (a_Html_get_attr (html, tag, tagsize, "align")) {
199 S_TOP(html)->cell_text_align_set = TRUE;
200 a_Html_tag_set_align_attr (html, tag, tagsize);
201 }
202
203 html->styleEngine->inheritBackgroundColor ();
204
205 if (bgcolor != -1) {
206 html->styleEngine->setNonCssHint(CSS_PROPERTY_BACKGROUND_COLOR,
207 CSS_TYPE_COLOR, bgcolor);
208 }
209 a_Html_tag_set_valign_attr (html, tag, tagsize);
210 break;
211 default:
212 break;
213 }
214 }
215
Html_tag_content_tr(DilloHtml * html,const char * tag,int tagsize)216 void Html_tag_content_tr(DilloHtml *html, const char *tag, int tagsize)
217 {
218 switch (S_TOP(html)->table_mode) {
219 case DILLO_HTML_TABLE_MODE_NONE:
220 return;
221 case DILLO_HTML_TABLE_MODE_TOP:
222 case DILLO_HTML_TABLE_MODE_TR:
223 case DILLO_HTML_TABLE_MODE_TD:
224 ((dw::Table*)S_TOP(html)->table)->addRow (html->style ());
225 default:
226 break;
227 }
228
229 S_TOP(html)->table_mode = DILLO_HTML_TABLE_MODE_TR;
230 }
231
232 /*
233 * <TD>
234 */
Html_tag_open_td(DilloHtml * html,const char * tag,int tagsize)235 void Html_tag_open_td(DilloHtml *html, const char *tag, int tagsize)
236 {
237 Html_tag_open_table_cell (html, tag, tagsize,
238 dw::core::style::TEXT_ALIGN_LEFT);
239 }
240
Html_tag_content_td(DilloHtml * html,const char * tag,int tagsize)241 void Html_tag_content_td(DilloHtml *html, const char *tag, int tagsize)
242 {
243 Html_tag_content_table_cell (html, tag, tagsize);
244 }
245
246 /*
247 * <TH>
248 */
Html_tag_open_th(DilloHtml * html,const char * tag,int tagsize)249 void Html_tag_open_th(DilloHtml *html, const char *tag, int tagsize)
250 {
251 Html_tag_open_table_cell (html, tag, tagsize,
252 dw::core::style::TEXT_ALIGN_CENTER);
253 }
254
Html_tag_content_th(DilloHtml * html,const char * tag,int tagsize)255 void Html_tag_content_th(DilloHtml *html, const char *tag, int tagsize)
256 {
257 Html_tag_content_table_cell (html, tag, tagsize);
258 }
259
260 /*
261 * Utilities
262 */
263
264 /*
265 * The table border model is stored in the table's stack item
266 */
Html_table_get_border_model(DilloHtml * html)267 static int Html_table_get_border_model(DilloHtml *html)
268 {
269 static int i_TABLE = -1;
270 if (i_TABLE == -1)
271 i_TABLE = a_Html_tag_index("table");
272
273 int s_idx = html->stack->size();
274 while (--s_idx > 0 && html->stack->getRef(s_idx)->tag_idx != i_TABLE)
275 ;
276 return html->stack->getRef(s_idx)->table_border_mode;
277 }
278
279 /*
280 * Set current table's border model
281 */
Html_table_set_border_model(DilloHtml * html,DilloHtmlTableBorderMode mode)282 static void Html_table_set_border_model(DilloHtml *html,
283 DilloHtmlTableBorderMode mode)
284 {
285 int s_idx = html->stack->size(), i_TABLE = a_Html_tag_index("table");
286
287 while (--s_idx > 0 && html->stack->getRef(s_idx)->tag_idx != i_TABLE) ;
288 if (s_idx > 0)
289 html->stack->getRef(s_idx)->table_border_mode = mode;
290 }
291
292 /* WORKAROUND: collapsing border model requires moving rendering code from
293 * the cell to the table, and making table-code aware of each
294 * cell style.
295 * This workaround mimics collapsing model within separate model. This is not
296 * a complete emulation but should be enough for most cases.
297 */
Html_set_collapsing_border_model(DilloHtml * html,Widget * col_tb)298 static void Html_set_collapsing_border_model(DilloHtml *html, Widget *col_tb)
299 {
300 dw::core::style::Style *collapseStyle, *tableStyle;
301 dw::core::style::StyleAttrs collapseCellAttrs, collapseTableAttrs;
302 int borderWidth, marginWidth;
303
304 tableStyle = ((dw::Table*)S_TOP(html)->table)->getStyle ();
305 borderWidth = html->style ()->borderWidth.top;
306 marginWidth = tableStyle->margin.top;
307
308 collapseCellAttrs = *(html->style ());
309 collapseCellAttrs.margin.setVal (0);
310 collapseCellAttrs.borderWidth.left = 0;
311 collapseCellAttrs.borderWidth.top = 0;
312 collapseCellAttrs.borderWidth.right = borderWidth;
313 collapseCellAttrs.borderWidth.bottom = borderWidth;
314 collapseCellAttrs.hBorderSpacing = 0;
315 collapseCellAttrs.vBorderSpacing = 0;
316 collapseStyle = Style::create(&collapseCellAttrs);
317 col_tb->setStyle (collapseStyle);
318
319 if (Html_table_get_border_model(html) != DILLO_HTML_TABLE_BORDER_COLLAPSE) {
320 Html_table_set_border_model(html, DILLO_HTML_TABLE_BORDER_COLLAPSE);
321 collapseTableAttrs = *tableStyle;
322 collapseTableAttrs.margin.setVal (marginWidth);
323 collapseTableAttrs.borderWidth.left = borderWidth;
324 collapseTableAttrs.borderWidth.top = borderWidth;
325 collapseTableAttrs.borderWidth.right = 0;
326 collapseTableAttrs.borderWidth.bottom = 0;
327 collapseTableAttrs.hBorderSpacing = 0;
328 collapseTableAttrs.vBorderSpacing = 0;
329 collapseTableAttrs.borderColor = collapseCellAttrs.borderColor;
330 collapseTableAttrs.borderStyle = collapseCellAttrs.borderStyle;
331 /* CSS2 17.6.2: table does not have padding (in collapsing mode) */
332 collapseTableAttrs.padding.setVal (0);
333 collapseStyle = Style::create(&collapseTableAttrs);
334 ((dw::Table*)S_TOP(html)->table)->setStyle (collapseStyle);
335 }
336 }
337
338 /*
339 * Adjust style for separate border model.
340 * (Dw uses this model internally).
341 */
Html_set_separate_border_model(DilloHtml * html,Widget * col_tb)342 static void Html_set_separate_border_model(DilloHtml *html, Widget *col_tb)
343 {
344 dw::core::style::Style *separateStyle;
345 dw::core::style::StyleAttrs separateCellAttrs;
346
347 separateCellAttrs = *(html->style ());
348 /* CSS2 17.5: Internal table elements do not have margins */
349 separateCellAttrs.margin.setVal (0);
350 separateStyle = Style::create(&separateCellAttrs);
351 col_tb->setStyle (separateStyle);
352 }
353
354 /*
355 * used by <TD> and <TH>
356 */
Html_tag_open_table_cell(DilloHtml * html,const char * tag,int tagsize,dw::core::style::TextAlignType text_align)357 static void Html_tag_open_table_cell(DilloHtml *html,
358 const char *tag, int tagsize,
359 dw::core::style::TextAlignType text_align)
360 {
361 const char *attrbuf;
362 int32_t bgcolor;
363
364 html->styleEngine->inheritNonCssHints ();
365
366 switch (S_TOP(html)->table_mode) {
367 case DILLO_HTML_TABLE_MODE_NONE:
368 return;
369
370 case DILLO_HTML_TABLE_MODE_TOP:
371 /* a_Dw_table_add_cell takes care that dillo does not crash. */
372 /* continues */
373 case DILLO_HTML_TABLE_MODE_TR:
374 case DILLO_HTML_TABLE_MODE_TD:
375 /* text style */
376 if (!S_TOP(html)->cell_text_align_set) {
377 html->styleEngine->setNonCssHint (CSS_PROPERTY_TEXT_ALIGN,
378 CSS_TYPE_ENUM, text_align);
379 }
380 if (a_Html_get_attr(html, tag, tagsize, "nowrap")) {
381 if (html->DocType == DT_HTML && html->DocTypeVersion >= 5.0f)
382 BUG_MSG("<t%c> nowrap attribute is obsolete.",
383 (tagsize >=3 && (D_ASCII_TOLOWER(tag[2]) == 'd')) ? 'd' : 'h');
384 html->styleEngine->setNonCssHint(CSS_PROPERTY_WHITE_SPACE,
385 CSS_TYPE_ENUM, WHITE_SPACE_NOWRAP);
386 }
387
388 a_Html_tag_set_align_attr (html, tag, tagsize);
389
390 if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "width"))) {
391 html->styleEngine->setNonCssHint (CSS_PROPERTY_WIDTH,
392 CSS_TYPE_LENGTH_PERCENTAGE,
393 a_Html_parse_length (html, attrbuf));
394 if (html->DocType == DT_HTML && html->DocTypeVersion >= 5.0f)
395 BUG_MSG("<t%c> width attribute is obsolete.",
396 (tagsize >=3 && (D_ASCII_TOLOWER(tag[2]) == 'd')) ? 'd' : 'h');
397 }
398
399 a_Html_tag_set_valign_attr (html, tag, tagsize);
400
401 if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "bgcolor"))) {
402 bgcolor = a_Html_color_parse(html, attrbuf, -1);
403 if (bgcolor != -1)
404 html->styleEngine->setNonCssHint (CSS_PROPERTY_BACKGROUND_COLOR,
405 CSS_TYPE_COLOR, bgcolor);
406 if (html->DocType == DT_HTML && html->DocTypeVersion >= 5.0f)
407 BUG_MSG("<t%c> bgcolor attribute is obsolete.",
408 (tagsize >=3 && (D_ASCII_TOLOWER(tag[2]) == 'd')) ? 'd' : 'h');
409 }
410
411 default:
412 /* compiler happiness */
413 break;
414 }
415 }
416
Html_tag_content_table_cell(DilloHtml * html,const char * tag,int tagsize)417 static void Html_tag_content_table_cell(DilloHtml *html,
418 const char *tag, int tagsize)
419 {
420 int colspan = 1, rowspan = 1;
421 const char *attrbuf;
422 Widget *col_tb;
423
424 switch (S_TOP(html)->table_mode) {
425 case DILLO_HTML_TABLE_MODE_NONE:
426 BUG_MSG("<t%c> outside <table>.",
427 (tagsize >=3 && (D_ASCII_TOLOWER(tag[2]) == 'd')) ? 'd' : 'h');
428 return;
429
430 case DILLO_HTML_TABLE_MODE_TOP:
431 BUG_MSG("<t%c> outside <tr>.",
432 (tagsize >=3 && (D_ASCII_TOLOWER(tag[2]) == 'd')) ? 'd' : 'h');
433 /* a_Dw_table_add_cell takes care that dillo does not crash. */
434 /* continues */
435 case DILLO_HTML_TABLE_MODE_TR:
436 case DILLO_HTML_TABLE_MODE_TD:
437 if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "colspan"))) {
438 char *invalid;
439 colspan = strtol(attrbuf, &invalid, 10);
440 if ((colspan < 0) || (attrbuf == invalid))
441 colspan = 1;
442 }
443 /* TODO: check errors? */
444 if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "rowspan")))
445 rowspan = MAX(1, strtol (attrbuf, NULL, 10));
446 if (html->style ()->textAlign
447 == TEXT_ALIGN_STRING)
448 col_tb = new dw::TableCell (
449 ((dw::Table*)S_TOP(html)->table)->getCellRef (),
450 prefs.limit_text_width);
451 else
452 col_tb = new Textblock (prefs.limit_text_width);
453
454 if (html->style()->borderCollapse == BORDER_MODEL_COLLAPSE){
455 Html_set_collapsing_border_model(html, col_tb);
456 } else {
457 Html_set_separate_border_model(html, col_tb);
458 }
459
460 ((dw::Table*)S_TOP(html)->table)->addCell (col_tb, colspan, rowspan);
461 S_TOP(html)->textblock = html->dw = col_tb;
462 break;
463
464 default:
465 /* compiler happiness */
466 break;
467 }
468
469 S_TOP(html)->table_mode = DILLO_HTML_TABLE_MODE_TD;
470 }
471