fixes for tables renderer: now recognizes missing border property correctly
[wxWidgets.git] / src / html / m_tables.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: m_tables.cpp
3 // Purpose: wxHtml module for tables
4 // Author: Vaclav Slavik
5 // RCS-ID: $Id$
6 // Copyright: (c) 1999 Vaclav Slavik
7 // Licence: wxWindows Licence
8 /////////////////////////////////////////////////////////////////////////////
9
10 #ifdef __GNUG__
11 #pragma implementation
12 #endif
13
14 #include "wx/wxprec.h"
15
16 #include "wx/defs.h"
17 #if wxUSE_HTML && wxUSE_STREAMS
18 #ifdef __BORDLANDC__
19 #pragma hdrstop
20 #endif
21
22 #ifndef WXPRECOMP
23 #endif
24
25
26 /*
27 REMARKS:
28 1. This version of m_tables doesn't support auto-layout algorithm.
29 This means that all columns are of same width unless explicitly specified.
30 */
31
32
33 #include "wx/html/forcelnk.h"
34 #include "wx/html/m_templ.h"
35
36 #include "wx/html/htmlcell.h"
37
38 FORCE_LINK_ME(m_tables)
39
40
41 #define TABLE_BORDER_CLR_1 wxColour(0xC5, 0xC2, 0xC5)
42 #define TABLE_BORDER_CLR_2 wxColour(0x62, 0x61, 0x62)
43
44
45 //-----------------------------------------------------------------------------
46 // wxHtmlTableCell
47 //-----------------------------------------------------------------------------
48
49
50 typedef struct {
51 int width, units;
52 // universal
53 int leftpos, pixwidth, maxrealwidth;
54 // temporary (depends on width of table)
55 } colStruct;
56
57 typedef enum {
58 cellSpan,
59 cellUsed,
60 cellFree
61 } cellState;
62
63 typedef struct {
64 wxHtmlContainerCell *cont;
65 int colspan, rowspan;
66 int minheight, valign;
67 cellState flag;
68 } cellStruct;
69
70
71 class wxHtmlTableCell : public wxHtmlContainerCell
72 {
73 protected:
74 /* These are real attributes: */
75 bool m_HasBorders;
76 // should we draw borders or not?
77 int m_NumCols, m_NumRows;
78 // number of columns; rows
79 colStruct *m_ColsInfo;
80 // array of column information
81 cellStruct **m_CellInfo;
82 // 2D array of all cells in the table : m_CellInfo[row][column]
83 int m_Spacing;
84 // spaces between cells
85 int m_Padding;
86 // cells internal indentation
87
88 private:
89 /* ...and these are valid only during parsing of table: */
90 int m_ActualCol, m_ActualRow;
91 // number of actual column (ranging from 0..m_NumCols)
92
93 // default values (for table and row):
94 wxColour m_tBkg, m_rBkg;
95 wxString m_tValign, m_rValign;
96
97 double m_PixelScale;
98
99
100 public:
101 wxHtmlTableCell(wxHtmlContainerCell *parent, const wxHtmlTag& tag, double pixel_scale = 1.0);
102 ~wxHtmlTableCell();
103 virtual void Layout(int w);
104
105 void AddRow(const wxHtmlTag& tag);
106 void AddCell(wxHtmlContainerCell *cell, const wxHtmlTag& tag);
107 private:
108 void ReallocCols(int cols);
109 void ReallocRows(int rows);
110 // reallocates memory to given number of cols/rows
111 // and changes m_NumCols/m_NumRows value to reflect this change
112 // NOTE! You CAN'T change m_NumCols/m_NumRows before calling this!!
113 };
114
115
116
117 wxHtmlTableCell::wxHtmlTableCell(wxHtmlContainerCell *parent, const wxHtmlTag& tag, double pixel_scale)
118 : wxHtmlContainerCell(parent)
119 {
120 m_PixelScale = pixel_scale;
121 m_HasBorders =
122 (tag.HasParam(wxT("BORDER")) && tag.GetParam(wxT("BORDER")) != wxT("0"));
123 m_ColsInfo = NULL;
124 m_NumCols = m_NumRows = 0;
125 m_CellInfo = NULL;
126 m_ActualCol = m_ActualRow = -1;
127
128 /* scan params: */
129 if (tag.HasParam(wxT("BGCOLOR")))
130 tag.GetParamAsColour(wxT("BGCOLOR"), &m_tBkg);
131 if (tag.HasParam(wxT("VALIGN")))
132 m_tValign = tag.GetParam(wxT("VALIGN"));
133 else
134 m_tValign = wxEmptyString;
135 if (!tag.GetParamAsInt(wxT("CELLSPACING"), &m_Spacing))
136 m_Spacing = 2;
137 if (!tag.GetParamAsInt(wxT("CELLPADDING"), &m_Padding))
138 m_Padding = 3;
139 m_Spacing = (int)(m_PixelScale * (double)m_Spacing);
140 m_Padding = (int)(m_PixelScale * (double)m_Padding);
141
142 if (m_HasBorders)
143 SetBorder(TABLE_BORDER_CLR_1, TABLE_BORDER_CLR_2);
144 }
145
146
147
148 wxHtmlTableCell::~wxHtmlTableCell()
149 {
150 if (m_ColsInfo) free(m_ColsInfo);
151 if (m_CellInfo)
152 {
153 for (int i = 0; i < m_NumRows; i++)
154 free(m_CellInfo[i]);
155 free(m_CellInfo);
156 }
157 }
158
159
160
161 void wxHtmlTableCell::ReallocCols(int cols)
162 {
163 int i,j;
164
165 for (i = 0; i < m_NumRows; i++)
166 {
167 m_CellInfo[i] = (cellStruct*) realloc(m_CellInfo[i], sizeof(cellStruct) * cols);
168 for (j = m_NumCols; j < cols; j++)
169 m_CellInfo[i][j].flag = cellFree;
170 }
171
172 m_ColsInfo = (colStruct*) realloc(m_ColsInfo, sizeof(colStruct) * cols);
173 for (j = m_NumCols; j < cols; j++)
174 {
175 m_ColsInfo[j].width = 0;
176 m_ColsInfo[j].units = wxHTML_UNITS_PERCENT;
177 }
178
179 m_NumCols = cols;
180 }
181
182
183
184 void wxHtmlTableCell::ReallocRows(int rows)
185 {
186 m_CellInfo = (cellStruct**) realloc(m_CellInfo, sizeof(cellStruct*) * rows);
187 for (int row = m_NumRows; row < rows ; row++)
188 {
189 if (m_NumCols == 0)
190 m_CellInfo[row] = NULL;
191 else
192 {
193 m_CellInfo[row] = (cellStruct*) malloc(sizeof(cellStruct) * m_NumCols);
194 for (int col = 0; col < m_NumCols; col++)
195 m_CellInfo[row][col].flag = cellFree;
196 }
197 }
198 m_NumRows = rows;
199 }
200
201
202 void wxHtmlTableCell::AddRow(const wxHtmlTag& tag)
203 {
204 m_ActualCol = -1;
205 // VS: real allocation of row entry is done in AddCell in order
206 // to correctly handle empty rows (i.e. "<tr></tr>")
207 // m_ActualCol == -1 indicates that AddCell has to allocate new row.
208
209 // scan params:
210 m_rBkg = m_tBkg;
211 if (tag.HasParam(wxT("BGCOLOR")))
212 tag.GetParamAsColour(wxT("BGCOLOR"), &m_rBkg);
213 if (tag.HasParam(wxT("VALIGN")))
214 m_rValign = tag.GetParam(wxT("VALIGN"));
215 else
216 m_rValign = m_tValign;
217 }
218
219
220
221 void wxHtmlTableCell::AddCell(wxHtmlContainerCell *cell, const wxHtmlTag& tag)
222 {
223 // Is this cell in new row?
224 // VS: we can't do it in AddRow, see my comment there
225 if (m_ActualCol == -1)
226 {
227 if (m_ActualRow + 1 > m_NumRows - 1)
228 ReallocRows(m_ActualRow + 2);
229 m_ActualRow++;
230 }
231
232 // cells & columns:
233 do
234 {
235 m_ActualCol++;
236 } while ((m_ActualCol < m_NumCols) &&
237 (m_CellInfo[m_ActualRow][m_ActualCol].flag != cellFree));
238
239 if (m_ActualCol > m_NumCols - 1)
240 ReallocCols(m_ActualCol + 1);
241
242 int r = m_ActualRow, c = m_ActualCol;
243
244 m_CellInfo[r][c].cont = cell;
245 m_CellInfo[r][c].colspan = 1;
246 m_CellInfo[r][c].rowspan = 1;
247 m_CellInfo[r][c].flag = cellUsed;
248 m_CellInfo[r][c].minheight = 0;
249 m_CellInfo[r][c].valign = wxHTML_ALIGN_TOP;
250
251 /* scan for parameters: */
252
253 // width:
254 {
255 if (tag.HasParam(wxT("WIDTH")))
256 {
257 wxString wd = tag.GetParam(wxT("WIDTH"));
258
259 if (wd[wd.Length()-1] == wxT('%'))
260 {
261 wxSscanf(wd.c_str(), wxT("%i%%"), &m_ColsInfo[c].width);
262 m_ColsInfo[c].units = wxHTML_UNITS_PERCENT;
263 }
264 else
265 {
266 wxSscanf(wd.c_str(), wxT("%i"), &m_ColsInfo[c].width);
267 m_ColsInfo[c].width = (int)(m_PixelScale * (double)m_ColsInfo[c].width);
268 m_ColsInfo[c].units = wxHTML_UNITS_PIXELS;
269 }
270 }
271 }
272
273
274 // spanning:
275 {
276 tag.GetParamAsInt(wxT("COLSPAN"), &m_CellInfo[r][c].colspan);
277 tag.GetParamAsInt(wxT("ROWSPAN"), &m_CellInfo[r][c].rowspan);
278 if ((m_CellInfo[r][c].colspan != 1) || (m_CellInfo[r][c].rowspan != 1))
279 {
280 int i, j;
281
282 if (r + m_CellInfo[r][c].rowspan > m_NumRows)
283 ReallocRows(r + m_CellInfo[r][c].rowspan);
284 if (c + m_CellInfo[r][c].colspan > m_NumCols)
285 ReallocCols(c + m_CellInfo[r][c].colspan);
286 for (i = r; i < r + m_CellInfo[r][c].rowspan; i++)
287 for (j = c; j < c + m_CellInfo[r][c].colspan; j++)
288 m_CellInfo[i][j].flag = cellSpan;
289 m_CellInfo[r][c].flag = cellUsed;
290 }
291 }
292
293 //background color:
294 {
295 wxColour bk = m_rBkg;
296 if (tag.HasParam(wxT("BGCOLOR")))
297 tag.GetParamAsColour(wxT("BGCOLOR"), &bk);
298 if (bk.Ok())
299 cell->SetBackgroundColour(bk);
300 }
301 if (m_HasBorders)
302 cell->SetBorder(TABLE_BORDER_CLR_2, TABLE_BORDER_CLR_1);
303
304 // vertical alignment:
305 {
306 wxString valign;
307 if (tag.HasParam(wxT("VALIGN")))
308 valign = tag.GetParam(wxT("VALIGN"));
309 else
310 valign = m_tValign;
311 valign.MakeUpper();
312 if (valign == wxT("TOP"))
313 m_CellInfo[r][c].valign = wxHTML_ALIGN_TOP;
314 else if (valign == wxT("BOTTOM"))
315 m_CellInfo[r][c].valign = wxHTML_ALIGN_BOTTOM;
316 else m_CellInfo[r][c].valign = wxHTML_ALIGN_CENTER;
317 }
318
319 cell->SetIndent(m_Padding, wxHTML_INDENT_ALL, wxHTML_UNITS_PIXELS);
320 }
321
322
323
324
325
326 void wxHtmlTableCell::Layout(int w)
327 {
328 /*
329
330 WIDTH ADJUSTING :
331
332 */
333
334 if (m_WidthFloatUnits == wxHTML_UNITS_PERCENT)
335 {
336 if (m_WidthFloat < 0) m_Width = (100 + m_WidthFloat) * w / 100;
337 else m_Width = m_WidthFloat * w / 100;
338 }
339 else
340 {
341 if (m_WidthFloat < 0) m_Width = w + m_WidthFloat;
342 else m_Width = m_WidthFloat;
343 }
344
345
346 /*
347
348 LAYOUTING :
349
350 */
351
352 /* 1. setup columns widths: */
353 {
354 int wpix = m_Width - (m_NumCols + 1) * m_Spacing;
355 int i, j;
356 int wtemp = 0;
357
358 // 1a. setup fixed-width columns:
359 for (i = 0; i < m_NumCols; i++)
360 if (m_ColsInfo[i].units == wxHTML_UNITS_PIXELS)
361 wpix -= (m_ColsInfo[i].pixwidth = m_ColsInfo[i].width);
362
363 // 1b. setup floating-width columns:
364 for (i = 0; i < m_NumCols; i++)
365 if ((m_ColsInfo[i].units == wxHTML_UNITS_PERCENT) && (m_ColsInfo[i].width != 0))
366 wtemp += (m_ColsInfo[i].pixwidth = m_ColsInfo[i].width * wpix / 100);
367 wpix -= wtemp;
368
369 // 1c. setup defalut columns (no width specification supplied):
370 // NOTE! This algorithm doesn't conform to HTML standard : it assigns equal widths
371 // instead of optimal
372 for (i = j = 0; i < m_NumCols; i++)
373 if (m_ColsInfo[i].width == 0) j++;
374 for (i = 0; i < m_NumCols; i++)
375 if (m_ColsInfo[i].width == 0)
376 m_ColsInfo[i].pixwidth = wpix / j;
377 }
378
379 /* 2. compute positions of columns: */
380 {
381 int wpos = m_Spacing;
382 for (int i = 0; i < m_NumCols; i++)
383 {
384 m_ColsInfo[i].leftpos = wpos;
385 wpos += m_ColsInfo[i].pixwidth + m_Spacing;
386 }
387 }
388
389 /* 3. sub-layout all cells: */
390 {
391 int *ypos = new int[m_NumRows + 1];
392
393 int actcol, actrow;
394 int fullwid;
395 wxHtmlContainerCell *actcell;
396
397 ypos[0] = m_Spacing;
398 for (actrow = 1; actrow <= m_NumRows; actrow++) ypos[actrow] = -1;
399 for (actrow = 0; actrow < m_NumRows; actrow++)
400 {
401 if (ypos[actrow] == -1) ypos[actrow] = ypos[actrow-1];
402 // 3a. sub-layout and detect max height:
403
404 for (actcol = 0; actcol < m_NumCols; actcol++) {
405 if (m_CellInfo[actrow][actcol].flag != cellUsed) continue;
406 actcell = m_CellInfo[actrow][actcol].cont;
407 fullwid = 0;
408 for (int i = actcol; i < m_CellInfo[actrow][actcol].colspan + actcol; i++)
409 fullwid += m_ColsInfo[i].pixwidth;
410 fullwid += (m_CellInfo[actrow][actcol].colspan - 1) * m_Spacing;
411 actcell->SetMinHeight(m_CellInfo[actrow][actcol].minheight, m_CellInfo[actrow][actcol].valign);
412 actcell->Layout(fullwid);
413
414 if (ypos[actrow] + actcell->GetHeight() + m_CellInfo[actrow][actcol].rowspan * m_Spacing > ypos[actrow + m_CellInfo[actrow][actcol].rowspan])
415 ypos[actrow + m_CellInfo[actrow][actcol].rowspan] =
416 ypos[actrow] + actcell->GetHeight() + m_CellInfo[actrow][actcol].rowspan * m_Spacing;
417 }
418 }
419
420 for (actrow = 0; actrow < m_NumRows; actrow++)
421 {
422 // 3b. place cells in row & let'em all have same height:
423
424 for (actcol = 0; actcol < m_NumCols; actcol++)
425 {
426 if (m_CellInfo[actrow][actcol].flag != cellUsed) continue;
427 actcell = m_CellInfo[actrow][actcol].cont;
428 actcell->SetMinHeight(
429 ypos[actrow + m_CellInfo[actrow][actcol].rowspan] - ypos[actrow] - m_Spacing,
430 m_CellInfo[actrow][actcol].valign);
431 fullwid = 0;
432 for (int i = actcol; i < m_CellInfo[actrow][actcol].colspan + actcol; i++)
433 fullwid += m_ColsInfo[i].pixwidth;
434 fullwid += (m_CellInfo[actrow][actcol].colspan - 1) * m_Spacing;
435 actcell->Layout(fullwid);
436 actcell->SetPos(m_ColsInfo[actcol].leftpos, ypos[actrow]);
437 }
438 }
439 m_Height = ypos[m_NumRows];
440 delete[] ypos;
441 }
442 }
443
444
445
446
447
448
449 //-----------------------------------------------------------------------------
450 // The tables handler:
451 //-----------------------------------------------------------------------------
452
453
454 TAG_HANDLER_BEGIN(TABLE, "TABLE,TR,TD,TH")
455
456 TAG_HANDLER_VARS
457 wxHtmlTableCell* m_Table;
458 wxString m_tAlign, m_rAlign;
459 int m_OldAlign;
460
461 TAG_HANDLER_CONSTR(TABLE)
462 {
463 m_Table = NULL;
464 m_tAlign = m_rAlign = wxEmptyString;
465 m_OldAlign = wxHTML_ALIGN_LEFT;
466 }
467
468
469 TAG_HANDLER_PROC(tag)
470 {
471 wxHtmlContainerCell *c;
472
473 // new table started, backup upper-level table (if any) and create new:
474 if (tag.GetName() == wxT("TABLE"))
475 {
476 wxHtmlTableCell *oldt = m_Table;
477 wxHtmlContainerCell *oldcont;
478 int m_OldAlign;
479
480 oldcont = c = m_WParser->OpenContainer();
481
482 c->SetWidthFloat(tag, m_WParser->GetPixelScale());
483 m_Table = new wxHtmlTableCell(c, tag, m_WParser->GetPixelScale());
484 m_OldAlign = m_WParser->GetAlign();
485 m_tAlign = wxEmptyString;
486 if (tag.HasParam(wxT("ALIGN")))
487 m_tAlign = tag.GetParam(wxT("ALIGN"));
488
489 ParseInner(tag);
490
491 m_WParser->SetAlign(m_OldAlign);
492 m_WParser->SetContainer(oldcont);
493 m_WParser->CloseContainer();
494 m_Table = oldt;
495 return TRUE;
496 }
497
498
499 else if (m_Table && !tag.IsEnding())
500 {
501 // new row in table
502 if (tag.GetName() == wxT("TR"))
503 {
504 m_Table->AddRow(tag);
505 m_rAlign = m_tAlign;
506 if (tag.HasParam(wxT("ALIGN")))
507 m_rAlign = tag.GetParam(wxT("ALIGN"));
508 }
509
510 // new cell
511 else
512 {
513 m_WParser->SetAlign(m_OldAlign);
514 c = m_WParser->SetContainer(new wxHtmlContainerCell(m_Table));
515 m_Table->AddCell(c, tag);
516
517 m_WParser->OpenContainer();
518
519 if (tag.GetName() == wxT("TH")) /*header style*/
520 {
521 m_WParser->SetAlign(wxHTML_ALIGN_CENTER);
522 }
523
524 {
525 wxString als;
526
527 als = m_rAlign;
528 if (tag.HasParam(wxT("ALIGN")))
529 als = tag.GetParam(wxT("ALIGN"));
530 als.MakeUpper();
531 if (als == wxT("RIGHT"))
532 m_WParser->SetAlign(wxHTML_ALIGN_RIGHT);
533 else if (als == wxT("CENTER"))
534 m_WParser->SetAlign(wxHTML_ALIGN_CENTER);
535 }
536 m_WParser->OpenContainer();
537 }
538 }
539 return FALSE;
540 }
541
542 TAG_HANDLER_END(TABLE)
543
544
545
546
547
548 TAGS_MODULE_BEGIN(Tables)
549
550 TAGS_MODULE_ADD(TABLE)
551
552 TAGS_MODULE_END(Tables)
553
554
555 #endif