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