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