Store HTML "id" parameter value in wxHtmlCell.
[wxWidgets.git] / src / html / m_tables.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/html/m_tables.cpp
3 // Purpose: wxHtml module for tables
4 // Author: Vaclav Slavik
5 // Copyright: (c) 1999 Vaclav Slavik
6 // Licence: wxWindows licence
7 /////////////////////////////////////////////////////////////////////////////
8
9 #include "wx/wxprec.h"
10
11 #ifdef __BORLANDC__
12 #pragma hdrstop
13 #endif
14
15 #if wxUSE_HTML && wxUSE_STREAMS
16
17 #ifndef WX_PRECOMP
18 #include "wx/wxcrtvararg.h"
19 #include "wx/brush.h"
20 #endif
21
22 #include "wx/html/forcelnk.h"
23 #include "wx/html/m_templ.h"
24
25 #include "wx/html/htmlcell.h"
26
27 FORCE_LINK_ME(m_tables)
28
29
30 #define TABLE_BORDER_CLR_1 wxColour(0xC5, 0xC2, 0xC5)
31 #define TABLE_BORDER_CLR_2 wxColour(0x62, 0x61, 0x62)
32
33
34 //-----------------------------------------------------------------------------
35 // wxHtmlTableCell
36 //-----------------------------------------------------------------------------
37
38
39 struct colStruct
40 {
41 int width, units;
42 // width of the column either in pixels or percents
43 // ('width' is the number, 'units' determines its meaning)
44 int minWidth, maxWidth;
45 // minimal/maximal column width. This is needed by HTML 4.0
46 // layout algorithm and can be determined by trying to
47 // layout table cells with width=1 and width=infinity
48 int leftpos, pixwidth, maxrealwidth;
49 // temporary (depends on actual width of table)
50 };
51
52 enum cellState
53 {
54 cellSpan,
55 cellUsed,
56 cellFree
57 };
58
59 struct cellStruct
60 {
61 wxHtmlContainerCell *cont;
62 int colspan, rowspan;
63 int minheight, valign;
64 cellState flag;
65 bool nowrap;
66 };
67
68
69 class wxHtmlTableCell : public wxHtmlContainerCell
70 {
71 protected:
72 /* These are real attributes: */
73
74 // number of columns; rows
75 int m_NumCols, m_NumRows;
76 // array of column information
77 colStruct *m_ColsInfo;
78 // 2D array of all cells in the table : m_CellInfo[row][column]
79 cellStruct **m_CellInfo;
80 // spaces between cells
81 int m_Spacing;
82 // cells internal indentation
83 int m_Padding;
84
85 private:
86 /* ...and these are valid only when parsing the table: */
87
88 // number of actual column (ranging from 0..m_NumCols)
89 int m_ActualCol, m_ActualRow;
90
91 // default values (for table and row):
92 wxColour m_tBkg, m_rBkg;
93 wxString m_tValign, m_rValign;
94
95 double m_PixelScale;
96
97
98 public:
99 wxHtmlTableCell(wxHtmlContainerCell *parent, const wxHtmlTag& tag, double pixel_scale = 1.0);
100 virtual ~wxHtmlTableCell();
101
102 virtual void RemoveExtraSpacing(bool top, bool bottom);
103
104 virtual void Layout(int w);
105
106 void AddRow(const wxHtmlTag& tag);
107 void AddCell(wxHtmlContainerCell *cell, const wxHtmlTag& tag);
108
109 const wxColour& GetRowDefaultBackgroundColour() const { return m_rBkg; }
110
111 private:
112 // Reallocates memory to given number of cols/rows
113 // and changes m_NumCols/m_NumRows value to reflect this change
114 // NOTE! You CAN'T change m_NumCols/m_NumRows before calling this!!
115 void ReallocCols(int cols);
116 void ReallocRows(int rows);
117
118 // Computes minimal and maximal widths of columns. Needs to be called
119 // only once, before first Layout().
120 void ComputeMinMaxWidths();
121
122 wxDECLARE_NO_COPY_CLASS(wxHtmlTableCell);
123 };
124
125
126
127 wxHtmlTableCell::wxHtmlTableCell(wxHtmlContainerCell *parent, const wxHtmlTag& tag, double pixel_scale)
128 : wxHtmlContainerCell(parent)
129 {
130 m_PixelScale = pixel_scale;
131 m_ColsInfo = NULL;
132 m_NumCols = m_NumRows = 0;
133 m_CellInfo = NULL;
134 m_ActualCol = m_ActualRow = -1;
135
136 /* scan params: */
137 if (tag.HasParam(wxT("BGCOLOR")))
138 {
139 tag.GetParamAsColour(wxT("BGCOLOR"), &m_tBkg);
140 if (m_tBkg.IsOk())
141 SetBackgroundColour(m_tBkg);
142 }
143 if (tag.HasParam(wxT("VALIGN")))
144 m_tValign = tag.GetParam(wxT("VALIGN"));
145 else
146 m_tValign = wxEmptyString;
147 if (!tag.GetParamAsInt(wxT("CELLSPACING"), &m_Spacing))
148 m_Spacing = 2;
149 if (!tag.GetParamAsInt(wxT("CELLPADDING"), &m_Padding))
150 m_Padding = 3;
151 m_Spacing = (int)(m_PixelScale * (double)m_Spacing);
152 m_Padding = (int)(m_PixelScale * (double)m_Padding);
153
154 if(tag.HasParam(wxT("BORDER")))
155 {
156 if(tag.GetParam("BORDER").IsEmpty())
157 m_Border = 1;
158 else
159 tag.GetParamAsInt(wxT("BORDER"), &m_Border);
160 }
161 if (m_Border == 1)
162 SetBorder(TABLE_BORDER_CLR_1, TABLE_BORDER_CLR_2, m_Border); // special case see wxHtmlContainerCell::Draw
163 else if (m_Border> 0)
164 SetBorder(TABLE_BORDER_CLR_1, TABLE_BORDER_CLR_2, (int)(m_PixelScale * (double)m_Border));
165 else
166 m_Border = 0;
167
168 }
169
170
171
172 wxHtmlTableCell::~wxHtmlTableCell()
173 {
174 if (m_ColsInfo) free(m_ColsInfo);
175 if (m_CellInfo)
176 {
177 for (int i = 0; i < m_NumRows; i++)
178 free(m_CellInfo[i]);
179 free(m_CellInfo);
180 }
181 }
182
183
184 void wxHtmlTableCell::RemoveExtraSpacing(bool WXUNUSED(top),
185 bool WXUNUSED(bottom))
186 {
187 // Don't remove any spacing in the table -- it's always desirable,
188 // because it's part of table's definition.
189 // (If wxHtmlContainerCell::RemoveExtraSpacing() was applied to tables,
190 // then upper left cell of a table would be positioned above other cells
191 // if the table was the first element on the page.)
192 }
193
194 void wxHtmlTableCell::ReallocCols(int cols)
195 {
196 int i,j;
197
198 for (i = 0; i < m_NumRows; i++)
199 {
200 m_CellInfo[i] = (cellStruct*) realloc(m_CellInfo[i], sizeof(cellStruct) * cols);
201 for (j = m_NumCols; j < cols; j++)
202 m_CellInfo[i][j].flag = cellFree;
203 }
204
205 m_ColsInfo = (colStruct*) realloc(m_ColsInfo, sizeof(colStruct) * cols);
206 for (j = m_NumCols; j < cols; j++)
207 {
208 m_ColsInfo[j].width = 0;
209 m_ColsInfo[j].units = wxHTML_UNITS_PERCENT;
210 m_ColsInfo[j].minWidth = m_ColsInfo[j].maxWidth = -1;
211 }
212
213 m_NumCols = cols;
214 }
215
216
217
218 void wxHtmlTableCell::ReallocRows(int rows)
219 {
220 m_CellInfo = (cellStruct**) realloc(m_CellInfo, sizeof(cellStruct*) * rows);
221 for (int row = m_NumRows; row < rows ; row++)
222 {
223 if (m_NumCols == 0)
224 m_CellInfo[row] = NULL;
225 else
226 {
227 m_CellInfo[row] = (cellStruct*) malloc(sizeof(cellStruct) * m_NumCols);
228 for (int col = 0; col < m_NumCols; col++)
229 m_CellInfo[row][col].flag = cellFree;
230 }
231 }
232 m_NumRows = rows;
233 }
234
235
236 void wxHtmlTableCell::AddRow(const wxHtmlTag& tag)
237 {
238 m_ActualCol = -1;
239 // VS: real allocation of row entry is done in AddCell in order
240 // to correctly handle empty rows (i.e. "<tr></tr>")
241 // m_ActualCol == -1 indicates that AddCell has to allocate new row.
242
243 // scan params:
244 m_rBkg = m_tBkg;
245 if (tag.HasParam(wxT("BGCOLOR")))
246 tag.GetParamAsColour(wxT("BGCOLOR"), &m_rBkg);
247 if (tag.HasParam(wxT("VALIGN")))
248 m_rValign = tag.GetParam(wxT("VALIGN"));
249 else
250 m_rValign = m_tValign;
251 }
252
253
254
255 void wxHtmlTableCell::AddCell(wxHtmlContainerCell *cell, const wxHtmlTag& tag)
256 {
257 // Is this cell in new row?
258 // VS: we can't do it in AddRow, see my comment there
259 if (m_ActualCol == -1)
260 {
261 if (m_ActualRow + 1 > m_NumRows - 1)
262 ReallocRows(m_ActualRow + 2);
263 m_ActualRow++;
264 }
265
266 // cells & columns:
267 do
268 {
269 m_ActualCol++;
270 } while ((m_ActualCol < m_NumCols) &&
271 (m_CellInfo[m_ActualRow][m_ActualCol].flag != cellFree));
272
273 if (m_ActualCol > m_NumCols - 1)
274 ReallocCols(m_ActualCol + 1);
275
276 int r = m_ActualRow, c = m_ActualCol;
277
278 m_CellInfo[r][c].cont = cell;
279 m_CellInfo[r][c].colspan = 1;
280 m_CellInfo[r][c].rowspan = 1;
281 m_CellInfo[r][c].flag = cellUsed;
282 m_CellInfo[r][c].minheight = 0;
283 m_CellInfo[r][c].valign = wxHTML_ALIGN_TOP;
284
285 /* scan for parameters: */
286
287 // id:
288 wxString idvalue;
289 if (tag.GetParamAsString(wxT("ID"), &idvalue))
290 {
291 cell->SetId(idvalue);
292 }
293
294 // width:
295 {
296 if (tag.HasParam(wxT("WIDTH")))
297 {
298 wxString wd = tag.GetParam(wxT("WIDTH"));
299
300 if (!wd.empty() && wd[wd.length()-1] == wxT('%'))
301 {
302 if ( wxSscanf(wd.c_str(), wxT("%i%%"), &m_ColsInfo[c].width) == 1 )
303 {
304 m_ColsInfo[c].units = wxHTML_UNITS_PERCENT;
305 }
306 }
307 else
308 {
309 long width;
310 if ( wd.ToLong(&width) )
311 {
312 m_ColsInfo[c].width = (int)(m_PixelScale * (double)width);
313 m_ColsInfo[c].units = wxHTML_UNITS_PIXELS;
314 }
315 }
316 }
317 }
318
319
320 // spanning:
321 {
322 tag.GetParamAsInt(wxT("COLSPAN"), &m_CellInfo[r][c].colspan);
323 tag.GetParamAsInt(wxT("ROWSPAN"), &m_CellInfo[r][c].rowspan);
324
325 // VS: the standard says this about col/rowspan:
326 // "This attribute specifies the number of rows spanned by the
327 // current cell. The default value of this attribute is one ("1").
328 // The value zero ("0") means that the cell spans all rows from the
329 // current row to the last row of the table." All mainstream
330 // browsers act as if 0==1, though, and so does wxHTML.
331 if (m_CellInfo[r][c].colspan < 1)
332 m_CellInfo[r][c].colspan = 1;
333 if (m_CellInfo[r][c].rowspan < 1)
334 m_CellInfo[r][c].rowspan = 1;
335
336 if ((m_CellInfo[r][c].colspan > 1) || (m_CellInfo[r][c].rowspan > 1))
337 {
338 int i, j;
339
340 if (r + m_CellInfo[r][c].rowspan > m_NumRows)
341 ReallocRows(r + m_CellInfo[r][c].rowspan);
342 if (c + m_CellInfo[r][c].colspan > m_NumCols)
343 ReallocCols(c + m_CellInfo[r][c].colspan);
344 for (i = r; i < r + m_CellInfo[r][c].rowspan; i++)
345 for (j = c; j < c + m_CellInfo[r][c].colspan; j++)
346 m_CellInfo[i][j].flag = cellSpan;
347 m_CellInfo[r][c].flag = cellUsed;
348 }
349 }
350
351 //background color:
352 {
353 wxColour bk = m_rBkg;
354 if (tag.HasParam(wxT("BGCOLOR")))
355 tag.GetParamAsColour(wxT("BGCOLOR"), &bk);
356 if (bk.IsOk())
357 cell->SetBackgroundColour(bk);
358 }
359 if (m_Border > 0)
360 cell->SetBorder(TABLE_BORDER_CLR_2, TABLE_BORDER_CLR_1);
361
362 // vertical alignment:
363 {
364 wxString valign;
365 if (tag.HasParam(wxT("VALIGN")))
366 valign = tag.GetParam(wxT("VALIGN"));
367 else
368 valign = m_tValign;
369 valign.MakeUpper();
370 if (valign == wxT("TOP"))
371 m_CellInfo[r][c].valign = wxHTML_ALIGN_TOP;
372 else if (valign == wxT("BOTTOM"))
373 m_CellInfo[r][c].valign = wxHTML_ALIGN_BOTTOM;
374 else m_CellInfo[r][c].valign = wxHTML_ALIGN_CENTER;
375 }
376
377 // nowrap
378 if (tag.HasParam(wxT("NOWRAP")))
379 m_CellInfo[r][c].nowrap = true;
380 else
381 m_CellInfo[r][c].nowrap = false;
382
383 cell->SetIndent(m_Padding, wxHTML_INDENT_ALL, wxHTML_UNITS_PIXELS);
384 }
385
386 void wxHtmlTableCell::ComputeMinMaxWidths()
387 {
388 if (m_NumCols == 0 || m_ColsInfo[0].minWidth != wxDefaultCoord) return;
389
390 m_MaxTotalWidth = 0;
391 int percentage = 0;
392 for (int c = 0; c < m_NumCols; c++)
393 {
394 for (int r = 0; r < m_NumRows; r++)
395 {
396 cellStruct& cell = m_CellInfo[r][c];
397 if (cell.flag == cellUsed)
398 {
399 cell.cont->Layout(2*m_Padding + 1);
400 int maxWidth = cell.cont->GetMaxTotalWidth();
401 int width = cell.nowrap?maxWidth:cell.cont->GetWidth();
402 width -= (cell.colspan-1) * m_Spacing;
403 maxWidth -= (cell.colspan-1) * m_Spacing;
404 // HTML 4.0 says it is acceptable to distribute min/max
405 width /= cell.colspan;
406 maxWidth /= cell.colspan;
407 for (int j = 0; j < cell.colspan; j++) {
408 if (width > m_ColsInfo[c+j].minWidth)
409 m_ColsInfo[c+j].minWidth = width;
410 if (maxWidth > m_ColsInfo[c+j].maxWidth)
411 m_ColsInfo[c+j].maxWidth = maxWidth;
412 }
413 }
414 }
415 // Calculate maximum table width, required for nested tables
416 if (m_ColsInfo[c].units == wxHTML_UNITS_PIXELS)
417 m_MaxTotalWidth += wxMax(m_ColsInfo[c].width, m_ColsInfo[c].minWidth);
418 else if ((m_ColsInfo[c].units == wxHTML_UNITS_PERCENT) && (m_ColsInfo[c].width != 0))
419 percentage += m_ColsInfo[c].width;
420 else
421 m_MaxTotalWidth += m_ColsInfo[c].maxWidth;
422 }
423
424 if (percentage >= 100)
425 {
426 // Table would have infinite length
427 // Make it ridiculous large
428 m_MaxTotalWidth = 0xFFFFFF;
429 }
430 else
431 m_MaxTotalWidth = m_MaxTotalWidth * 100 / (100 - percentage);
432
433 m_MaxTotalWidth += (m_NumCols + 1) * m_Spacing + 2 * m_Border;
434 }
435
436 void wxHtmlTableCell::Layout(int w)
437 {
438 ComputeMinMaxWidths();
439
440 wxHtmlCell::Layout(w);
441
442 /*
443
444 WIDTH ADJUSTING :
445
446 */
447
448 if (m_WidthFloatUnits == wxHTML_UNITS_PERCENT)
449 {
450 if (m_WidthFloat < 0)
451 {
452 if (m_WidthFloat < -100)
453 m_WidthFloat = -100;
454 m_Width = (100 + m_WidthFloat) * w / 100;
455 }
456 else
457 {
458 if (m_WidthFloat > 100)
459 m_WidthFloat = 100;
460 m_Width = m_WidthFloat * w / 100;
461 }
462 }
463 else
464 {
465 if (m_WidthFloat < 0) m_Width = w + m_WidthFloat;
466 else m_Width = m_WidthFloat;
467 }
468
469
470 /*
471
472 LAYOUT :
473
474 */
475
476 /* 1. setup columns widths:
477
478 The algorithm tries to keep the table size less than w if possible.
479 */
480 {
481 int wpix = m_Width - (m_NumCols + 1) * m_Spacing - 2 * m_Border; // Available space for cell content
482 int i, j;
483
484 // 1a. setup fixed-width columns:
485 for (i = 0; i < m_NumCols; i++)
486 if (m_ColsInfo[i].units == wxHTML_UNITS_PIXELS)
487 {
488 m_ColsInfo[i].pixwidth = wxMax(m_ColsInfo[i].width,
489 m_ColsInfo[i].minWidth);
490 wpix -= m_ColsInfo[i].pixwidth;
491 }
492
493 // 1b. Calculate maximum possible width if line wrapping would be disabled
494 // Recalculate total width if m_WidthFloat is zero to keep tables as small
495 // as possible.
496 int maxWidth = 0;
497 for (i = 0; i < m_NumCols; i++)
498 if (m_ColsInfo[i].width == 0)
499 {
500 maxWidth += m_ColsInfo[i].maxWidth;
501 }
502
503 if (!m_WidthFloat)
504 {
505 // Recalculate table width since no table width was initially given
506 int newWidth = m_Width - wpix + maxWidth;
507
508 // Make sure that floating-width columns will have the right size.
509 // Calculate sum of all floating-width columns
510 int percentage = 0;
511 for (i = 0; i < m_NumCols; i++)
512 if ((m_ColsInfo[i].units == wxHTML_UNITS_PERCENT) && (m_ColsInfo[i].width != 0))
513 percentage += m_ColsInfo[i].width;
514
515 if (percentage >= 100)
516 newWidth = w;
517 else
518 newWidth = newWidth * 100 / (100 - percentage);
519
520 newWidth = wxMin(newWidth, w - (m_NumCols + 1) * m_Spacing - 2 * m_Border);
521 wpix -= m_Width - newWidth;
522 m_Width = newWidth;
523 }
524
525
526 // 1c. setup floating-width columns:
527 int wtemp = wpix;
528 for (i = 0; i < m_NumCols; i++)
529 if ((m_ColsInfo[i].units == wxHTML_UNITS_PERCENT) && (m_ColsInfo[i].width != 0))
530 {
531 m_ColsInfo[i].pixwidth = wxMin(m_ColsInfo[i].width, 100) * wpix / 100;
532
533 // Make sure to leave enough space for the other columns
534 int minRequired = m_Border;
535 for (j = 0; j < m_NumCols; j++)
536 {
537 if ((m_ColsInfo[j].units == wxHTML_UNITS_PERCENT && j > i) ||
538 !m_ColsInfo[j].width)
539 minRequired += m_ColsInfo[j].minWidth;
540 }
541 m_ColsInfo[i].pixwidth = wxMax(wxMin(wtemp - minRequired, m_ColsInfo[i].pixwidth), m_ColsInfo[i].minWidth);
542
543 wtemp -= m_ColsInfo[i].pixwidth;
544 }
545 wpix = wtemp; // minimum cells width
546
547 // 1d. setup default columns (no width specification supplied):
548 // The algorithm assigns calculates the maximum possible width if line
549 // wrapping would be disabled and assigns column width as a fraction
550 // based upon the maximum width of a column
551 // FIXME: I'm not sure if this algorithm is conform to HTML standard,
552 // though it seems to be much better than the old one
553
554 for (i = j = 0; i < m_NumCols; i++)
555 if (m_ColsInfo[i].width == 0) j++;
556 if (wpix < m_Border)
557 wpix = m_Border;
558
559 // Assign widths
560 for (i = 0; i < m_NumCols; i++)
561 if (m_ColsInfo[i].width == 0)
562 {
563 // Assign with, make sure not to drop below minWidth
564 if (maxWidth)
565 m_ColsInfo[i].pixwidth = (int)(wpix * (m_ColsInfo[i].maxWidth / (float)maxWidth) + 0.5);
566 else
567 m_ColsInfo[i].pixwidth = wpix / j;
568
569 // Make sure to leave enough space for the other columns
570 int minRequired = 0;
571 int r;
572 for (r = i + 1; r < m_NumCols; r++)
573 {
574 if (!m_ColsInfo[r].width)
575 minRequired += m_ColsInfo[r].minWidth;
576 }
577 m_ColsInfo[i].pixwidth = wxMax(wxMin(wpix - minRequired, m_ColsInfo[i].pixwidth), m_ColsInfo[i].minWidth);
578
579 if (maxWidth)
580 {
581 if (m_ColsInfo[i].pixwidth > (wpix * (m_ColsInfo[i].maxWidth / (float)maxWidth) + 0.5))
582 {
583 int diff = (int)(m_ColsInfo[i].pixwidth - (wpix * m_ColsInfo[i].maxWidth / (float)maxWidth + 0.5));
584 maxWidth += diff - m_ColsInfo[i].maxWidth;
585 }
586 else
587 maxWidth -= m_ColsInfo[i].maxWidth;
588 }
589 wpix -= m_ColsInfo[i].pixwidth;
590 }
591 }
592
593 /* 2. compute positions of columns: */
594 {
595 int wpos = m_Spacing + m_Border;
596 for (int i = 0; i < m_NumCols; i++)
597 {
598 m_ColsInfo[i].leftpos = wpos;
599 wpos += m_ColsInfo[i].pixwidth + m_Spacing;
600 }
601
602 // add the remaining space to the last column
603 if (m_NumCols > 0 && wpos < m_Width - m_Border)
604 m_ColsInfo[m_NumCols-1].pixwidth += m_Width - wpos - m_Border;
605 }
606
607 /* 3. sub-layout all cells: */
608 {
609 int *ypos = new int[m_NumRows + 1];
610
611 int actcol, actrow;
612 int fullwid;
613 wxHtmlContainerCell *actcell;
614
615 ypos[0] = m_Spacing + m_Border;
616 for (actrow = 1; actrow <= m_NumRows; actrow++) ypos[actrow] = -1;
617 for (actrow = 0; actrow < m_NumRows; actrow++)
618 {
619 if (ypos[actrow] == -1) ypos[actrow] = ypos[actrow-1];
620 // 3a. sub-layout and detect max height:
621
622 for (actcol = 0; actcol < m_NumCols; actcol++) {
623 if (m_CellInfo[actrow][actcol].flag != cellUsed) continue;
624 actcell = m_CellInfo[actrow][actcol].cont;
625 fullwid = 0;
626 for (int i = actcol; i < m_CellInfo[actrow][actcol].colspan + actcol; i++)
627 fullwid += m_ColsInfo[i].pixwidth;
628 fullwid += (m_CellInfo[actrow][actcol].colspan - 1) * m_Spacing;
629 actcell->SetMinHeight(m_CellInfo[actrow][actcol].minheight, m_CellInfo[actrow][actcol].valign);
630 actcell->Layout(fullwid);
631
632 if (ypos[actrow] + actcell->GetHeight() + m_CellInfo[actrow][actcol].rowspan * m_Spacing > ypos[actrow + m_CellInfo[actrow][actcol].rowspan])
633 ypos[actrow + m_CellInfo[actrow][actcol].rowspan] =
634 ypos[actrow] + actcell->GetHeight() + m_CellInfo[actrow][actcol].rowspan * m_Spacing;
635 }
636 }
637
638 for (actrow = 0; actrow < m_NumRows; actrow++)
639 {
640 // 3b. place cells in row & let'em all have same height:
641
642 for (actcol = 0; actcol < m_NumCols; actcol++)
643 {
644 if (m_CellInfo[actrow][actcol].flag != cellUsed) continue;
645 actcell = m_CellInfo[actrow][actcol].cont;
646 actcell->SetMinHeight(
647 ypos[actrow + m_CellInfo[actrow][actcol].rowspan] - ypos[actrow] - m_Spacing,
648 m_CellInfo[actrow][actcol].valign);
649 fullwid = 0;
650 for (int i = actcol; i < m_CellInfo[actrow][actcol].colspan + actcol; i++)
651 fullwid += m_ColsInfo[i].pixwidth;
652 fullwid += (m_CellInfo[actrow][actcol].colspan - 1) * m_Spacing;
653 actcell->Layout(fullwid);
654 actcell->SetPos(m_ColsInfo[actcol].leftpos, ypos[actrow]);
655 }
656 }
657 m_Height = ypos[m_NumRows] + m_Border;
658 delete[] ypos;
659 }
660
661 /* 4. adjust table's width if it was too small: */
662 if (m_NumCols > 0)
663 {
664 int twidth = m_ColsInfo[m_NumCols-1].leftpos +
665 m_ColsInfo[m_NumCols-1].pixwidth + m_Spacing + m_Border;
666 if (twidth > m_Width)
667 m_Width = twidth;
668 }
669 }
670
671
672
673
674
675
676 //-----------------------------------------------------------------------------
677 // The tables handler:
678 //-----------------------------------------------------------------------------
679
680
681 TAG_HANDLER_BEGIN(TABLE, "TABLE,TR,TD,TH")
682
683 TAG_HANDLER_VARS
684 wxHtmlTableCell* m_Table;
685 wxString m_tAlign, m_rAlign;
686 wxHtmlContainerCell *m_enclosingContainer;
687
688 // Call ParseInner() preserving background colour and mode after any
689 // changes done by it.
690 void CallParseInnerWithBg(const wxHtmlTag& tag, const wxColour& colBg)
691 {
692 const wxColour oldbackclr = m_WParser->GetActualBackgroundColor();
693 const int oldbackmode = m_WParser->GetActualBackgroundMode();
694 if ( colBg.IsOk() )
695 {
696 m_WParser->SetActualBackgroundColor(colBg);
697 m_WParser->SetActualBackgroundMode(wxBRUSHSTYLE_SOLID);
698 m_WParser->GetContainer()->InsertCell(
699 new wxHtmlColourCell(colBg, wxHTML_CLR_BACKGROUND)
700 );
701 }
702
703 ParseInner(tag);
704
705 if ( oldbackmode != m_WParser->GetActualBackgroundMode() ||
706 oldbackclr != m_WParser->GetActualBackgroundColor() )
707 {
708 m_WParser->SetActualBackgroundMode(oldbackmode);
709 m_WParser->SetActualBackgroundColor(oldbackclr);
710 m_WParser->GetContainer()->InsertCell(
711 new wxHtmlColourCell(oldbackclr,
712 oldbackmode == wxBRUSHSTYLE_TRANSPARENT
713 ? wxHTML_CLR_TRANSPARENT_BACKGROUND
714 : wxHTML_CLR_BACKGROUND)
715 );
716 }
717 }
718
719 TAG_HANDLER_CONSTR(TABLE)
720 {
721 m_Table = NULL;
722 m_enclosingContainer = NULL;
723 m_tAlign = m_rAlign = wxEmptyString;
724 }
725
726
727 TAG_HANDLER_PROC(tag)
728 {
729 wxHtmlContainerCell *c;
730
731 // new table started, backup upper-level table (if any) and create new:
732 if (tag.GetName() == wxT("TABLE"))
733 {
734 wxHtmlTableCell *oldt = m_Table;
735
736 wxHtmlContainerCell *oldEnclosing = m_enclosingContainer;
737 m_enclosingContainer = c = m_WParser->OpenContainer();
738
739 m_Table = new wxHtmlTableCell(c, tag, m_WParser->GetPixelScale());
740
741 // width:
742 {
743 if (tag.HasParam(wxT("WIDTH")))
744 {
745 int width = 0;
746 bool wpercent = false;
747 if (tag.GetParamAsIntOrPercent(wxT("WIDTH"), &width, wpercent))
748 {
749 if (wpercent)
750 {
751 m_Table->SetWidthFloat(width, wxHTML_UNITS_PERCENT);
752 }
753 else
754 {
755 m_Table->SetWidthFloat((int)(m_WParser->GetPixelScale() * width), wxHTML_UNITS_PIXELS);
756 }
757 }
758 }
759 else
760 m_Table->SetWidthFloat(0, wxHTML_UNITS_PIXELS);
761 }
762 int oldAlign = m_WParser->GetAlign();
763 m_tAlign = wxEmptyString;
764 if (tag.HasParam(wxT("ALIGN")))
765 m_tAlign = tag.GetParam(wxT("ALIGN"));
766
767 CallParseInnerWithBg(tag, m_Table->GetBackgroundColour());
768
769 m_WParser->SetAlign(oldAlign);
770 m_WParser->SetContainer(m_enclosingContainer);
771 m_WParser->CloseContainer();
772
773 m_Table = oldt;
774 m_enclosingContainer = oldEnclosing;
775
776 return true; // ParseInner() called
777 }
778
779
780 else if (m_Table)
781 {
782 // new row in table
783 if (tag.GetName() == wxT("TR"))
784 {
785 m_Table->AddRow(tag);
786 m_rAlign = m_tAlign;
787 if (tag.HasParam(wxT("ALIGN")))
788 m_rAlign = tag.GetParam(wxT("ALIGN"));
789 }
790
791 // new cell
792 else
793 {
794 c = m_WParser->SetContainer(new wxHtmlContainerCell(m_Table));
795 m_Table->AddCell(c, tag);
796
797 m_WParser->OpenContainer();
798
799 const bool isHeader = tag.GetName() == wxT("TH");
800
801 wxString als;
802 if (tag.HasParam(wxT("ALIGN")))
803 als = tag.GetParam(wxT("ALIGN"));
804 else
805 als = m_rAlign;
806 als.MakeUpper();
807
808 if (als == wxT("RIGHT"))
809 m_WParser->SetAlign(wxHTML_ALIGN_RIGHT);
810 else if (als == wxT("LEFT"))
811 m_WParser->SetAlign(wxHTML_ALIGN_LEFT);
812 else if (als == wxT("CENTER"))
813 m_WParser->SetAlign(wxHTML_ALIGN_CENTER);
814 else // use default alignment
815 m_WParser->SetAlign(isHeader ? wxHTML_ALIGN_CENTER
816 : wxHTML_ALIGN_LEFT);
817
818 m_WParser->OpenContainer();
819
820 // the header should be rendered in bold by default
821 int boldOld = 0;
822 if ( isHeader )
823 {
824 boldOld = m_WParser->GetFontBold();
825 m_WParser->SetFontBold(true);
826 m_WParser->GetContainer()->InsertCell(
827 new wxHtmlFontCell(m_WParser->CreateCurrentFont()));
828 }
829
830 wxColour bgCol;
831 if ( !tag.GetParamAsColour(wxT("BGCOLOR"), &bgCol) )
832 bgCol = m_Table->GetRowDefaultBackgroundColour();
833
834 CallParseInnerWithBg(tag, bgCol);
835
836 if ( isHeader )
837 {
838 m_WParser->SetFontBold(boldOld);
839 m_WParser->GetContainer()->InsertCell(
840 new wxHtmlFontCell(m_WParser->CreateCurrentFont()));
841 }
842
843 // set the current container back to the enclosing one so that
844 // text outside of <th> and <td> isn't included in any cell
845 // (this happens often enough in practice because it's common
846 // to use whitespace between </td> and the next <td>):
847 m_WParser->SetContainer(m_enclosingContainer);
848
849 return true; // ParseInner() called
850 }
851 }
852
853 return false;
854 }
855
856 TAG_HANDLER_END(TABLE)
857
858
859
860
861
862 TAGS_MODULE_BEGIN(Tables)
863
864 TAGS_MODULE_ADD(TABLE)
865
866 TAGS_MODULE_END(Tables)
867
868
869 #endif