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