]> git.saurik.com Git - wxWidgets.git/blame - src/html/m_tables.cpp
add support for alpha in color dialog on OSX, see #14127
[wxWidgets.git] / src / html / m_tables.cpp
CommitLineData
5526e819 1/////////////////////////////////////////////////////////////////////////////
93763ad5 2// Name: src/html/m_tables.cpp
5526e819
VS
3// Purpose: wxHtml module for tables
4// Author: Vaclav Slavik
69941f05 5// RCS-ID: $Id$
5526e819 6// Copyright: (c) 1999 Vaclav Slavik
65571936 7// Licence: wxWindows licence
5526e819
VS
8/////////////////////////////////////////////////////////////////////////////
9
3096bd2f 10#include "wx/wxprec.h"
3364ab79 11
2b5f62a0 12#ifdef __BORLANDC__
93763ad5 13 #pragma hdrstop
3364ab79
RS
14#endif
15
93763ad5
WS
16#if wxUSE_HTML && wxUSE_STREAMS
17
b4f4d3dd 18#ifndef WX_PRECOMP
193d0c93 19 #include "wx/wxcrtvararg.h"
929bb9d2 20 #include "wx/brush.h"
3364ab79
RS
21#endif
22
69941f05
VS
23#include "wx/html/forcelnk.h"
24#include "wx/html/m_templ.h"
5526e819 25
69941f05 26#include "wx/html/htmlcell.h"
5526e819 27
c88293a4 28FORCE_LINK_ME(m_tables)
5526e819
VS
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
79d6c018
VS
40struct 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
d13b34d3 47 // layout algorithm and can be determined by trying to
79d6c018
VS
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
53enum cellState
54{
55 cellSpan,
56 cellUsed,
57 cellFree
58};
59
60struct cellStruct
61{
62 wxHtmlContainerCell *cont;
63 int colspan, rowspan;
64 int minheight, valign;
65 cellState flag;
ca16b7a9 66 bool nowrap;
79d6c018 67};
5526e819
VS
68
69
70class wxHtmlTableCell : public wxHtmlContainerCell
71{
79d6c018
VS
72protected:
73 /* These are real attributes: */
74
79d6c018
VS
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
86private:
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
99public:
100 wxHtmlTableCell(wxHtmlContainerCell *parent, const wxHtmlTag& tag, double pixel_scale = 1.0);
d3c7fc99 101 virtual ~wxHtmlTableCell();
3bc7a423
VS
102
103 virtual void RemoveExtraSpacing(bool top, bool bottom);
104
79d6c018
VS
105 virtual void Layout(int w);
106
107 void AddRow(const wxHtmlTag& tag);
108 void AddCell(wxHtmlContainerCell *cell, const wxHtmlTag& tag);
109
ff8af61c
VZ
110 const wxColour& GetRowDefaultBackgroundColour() const { return m_rBkg; }
111
79d6c018
VS
112private:
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
25c71ccc 120 // only once, before first Layout().
79d6c018 121 void ComputeMinMaxWidths();
22f3361e 122
c0c133e1 123 wxDECLARE_NO_COPY_CLASS(wxHtmlTableCell);
5526e819
VS
124};
125
126
127
edbd0635 128wxHtmlTableCell::wxHtmlTableCell(wxHtmlContainerCell *parent, const wxHtmlTag& tag, double pixel_scale)
5526e819
VS
129 : wxHtmlContainerCell(parent)
130{
edbd0635 131 m_PixelScale = pixel_scale;
5526e819
VS
132 m_ColsInfo = NULL;
133 m_NumCols = m_NumRows = 0;
134 m_CellInfo = NULL;
135 m_ActualCol = m_ActualRow = -1;
136
137 /* scan params: */
04dbb646 138 if (tag.HasParam(wxT("BGCOLOR")))
884898a7 139 {
8bd72d90 140 tag.GetParamAsColour(wxT("BGCOLOR"), &m_tBkg);
a1b806b9 141 if (m_tBkg.IsOk())
884898a7
VZ
142 SetBackgroundColour(m_tBkg);
143 }
04dbb646
VZ
144 if (tag.HasParam(wxT("VALIGN")))
145 m_tValign = tag.GetParam(wxT("VALIGN"));
146 else
8bd72d90
VS
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;
edbd0635
VS
152 m_Spacing = (int)(m_PixelScale * (double)m_Spacing);
153 m_Padding = (int)(m_PixelScale * (double)m_Padding);
5526e819 154
3aaaf1aa
VZ
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
5526e819
VS
169}
170
171
172
173wxHtmlTableCell::~wxHtmlTableCell()
174{
175 if (m_ColsInfo) free(m_ColsInfo);
04dbb646 176 if (m_CellInfo)
4f9297b0 177 {
5526e819
VS
178 for (int i = 0; i < m_NumRows; i++)
179 free(m_CellInfo[i]);
180 free(m_CellInfo);
181 }
182}
183
184
3bc7a423
VS
185void 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}
5526e819
VS
194
195void wxHtmlTableCell::ReallocCols(int cols)
196{
197 int i,j;
198
04dbb646 199 for (i = 0; i < m_NumRows; i++)
4f9297b0 200 {
5526e819
VS
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);
04dbb646 207 for (j = m_NumCols; j < cols; j++)
4f9297b0 208 {
5526e819 209 m_ColsInfo[j].width = 0;
efba2b89 210 m_ColsInfo[j].units = wxHTML_UNITS_PERCENT;
79d6c018 211 m_ColsInfo[j].minWidth = m_ColsInfo[j].maxWidth = -1;
5526e819
VS
212 }
213
214 m_NumCols = cols;
215}
216
217
218
219void wxHtmlTableCell::ReallocRows(int rows)
220{
221 m_CellInfo = (cellStruct**) realloc(m_CellInfo, sizeof(cellStruct*) * rows);
04dbb646 222 for (int row = m_NumRows; row < rows ; row++)
80eab469 223 {
04dbb646 224 if (m_NumCols == 0)
80eab469 225 m_CellInfo[row] = NULL;
04dbb646 226 else
80eab469
VS
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 }
5526e819 232 }
5526e819
VS
233 m_NumRows = rows;
234}
235
236
5526e819
VS
237void wxHtmlTableCell::AddRow(const wxHtmlTag& tag)
238{
5526e819 239 m_ActualCol = -1;
d361bbff
VS
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.
5526e819 243
d361bbff 244 // scan params:
5526e819 245 m_rBkg = m_tBkg;
04dbb646 246 if (tag.HasParam(wxT("BGCOLOR")))
8bd72d90 247 tag.GetParamAsColour(wxT("BGCOLOR"), &m_rBkg);
04dbb646
VZ
248 if (tag.HasParam(wxT("VALIGN")))
249 m_rValign = tag.GetParam(wxT("VALIGN"));
250 else
d361bbff 251 m_rValign = m_tValign;
5526e819
VS
252}
253
254
255
256void wxHtmlTableCell::AddCell(wxHtmlContainerCell *cell, const wxHtmlTag& tag)
257{
d361bbff
VS
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:
04dbb646 268 do
4f9297b0 269 {
5526e819 270 m_ActualCol++;
04dbb646 271 } while ((m_ActualCol < m_NumCols) &&
8bd72d90 272 (m_CellInfo[m_ActualRow][m_ActualCol].flag != cellFree));
04dbb646 273
5526e819
VS
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;
efba2b89 284 m_CellInfo[r][c].valign = wxHTML_ALIGN_TOP;
5526e819
VS
285
286 /* scan for parameters: */
287
288 // width:
289 {
8bd72d90 290 if (tag.HasParam(wxT("WIDTH")))
2fa3b707 291 {
8bd72d90 292 wxString wd = tag.GetParam(wxT("WIDTH"));
5526e819 293
18764039 294 if (!wd.empty() && wd[wd.length()-1] == wxT('%'))
2fa3b707 295 {
e388dc21
VS
296 if ( wxSscanf(wd.c_str(), wxT("%i%%"), &m_ColsInfo[c].width) == 1 )
297 {
298 m_ColsInfo[c].units = wxHTML_UNITS_PERCENT;
299 }
5526e819 300 }
04dbb646 301 else
2fa3b707 302 {
e388dc21
VS
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 }
5526e819
VS
309 }
310 }
311 }
312
313
314 // spanning:
315 {
8bd72d90
VS
316 tag.GetParamAsInt(wxT("COLSPAN"), &m_CellInfo[r][c].colspan);
317 tag.GetParamAsInt(wxT("ROWSPAN"), &m_CellInfo[r][c].rowspan);
fbe77cea
VS
318
319 // VS: the standard says this about col/rowspan:
25c71ccc
VZ
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
fbe77cea 324 // browsers act as if 0==1, though, and so does wxHTML.
25c71ccc 325 if (m_CellInfo[r][c].colspan < 1)
fbe77cea 326 m_CellInfo[r][c].colspan = 1;
25c71ccc 327 if (m_CellInfo[r][c].rowspan < 1)
fbe77cea
VS
328 m_CellInfo[r][c].rowspan = 1;
329
330 if ((m_CellInfo[r][c].colspan > 1) || (m_CellInfo[r][c].rowspan > 1))
2fa3b707 331 {
5526e819
VS
332 int i, j;
333
04dbb646 334 if (r + m_CellInfo[r][c].rowspan > m_NumRows)
8bd72d90 335 ReallocRows(r + m_CellInfo[r][c].rowspan);
04dbb646 336 if (c + m_CellInfo[r][c].colspan > m_NumCols)
8bd72d90 337 ReallocCols(c + m_CellInfo[r][c].colspan);
5526e819
VS
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 {
8bd72d90 347 wxColour bk = m_rBkg;
04dbb646 348 if (tag.HasParam(wxT("BGCOLOR")))
8bd72d90 349 tag.GetParamAsColour(wxT("BGCOLOR"), &bk);
a1b806b9 350 if (bk.IsOk())
8bd72d90 351 cell->SetBackgroundColour(bk);
5526e819 352 }
3aaaf1aa 353 if (m_Border > 0)
4f9297b0 354 cell->SetBorder(TABLE_BORDER_CLR_2, TABLE_BORDER_CLR_1);
5526e819
VS
355
356 // vertical alignment:
357 {
358 wxString valign;
04dbb646
VZ
359 if (tag.HasParam(wxT("VALIGN")))
360 valign = tag.GetParam(wxT("VALIGN"));
361 else
8bd72d90 362 valign = m_tValign;
5526e819 363 valign.MakeUpper();
04dbb646 364 if (valign == wxT("TOP"))
8bd72d90 365 m_CellInfo[r][c].valign = wxHTML_ALIGN_TOP;
04dbb646 366 else if (valign == wxT("BOTTOM"))
8bd72d90 367 m_CellInfo[r][c].valign = wxHTML_ALIGN_BOTTOM;
efba2b89 368 else m_CellInfo[r][c].valign = wxHTML_ALIGN_CENTER;
5526e819
VS
369 }
370
ca16b7a9
VS
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
4f9297b0 377 cell->SetIndent(m_Padding, wxHTML_INDENT_ALL, wxHTML_UNITS_PIXELS);
5526e819
VS
378}
379
79d6c018
VS
380void wxHtmlTableCell::ComputeMinMaxWidths()
381{
d1da8872 382 if (m_NumCols == 0 || m_ColsInfo[0].minWidth != wxDefaultCoord) return;
25c71ccc 383
ca16b7a9
VS
384 m_MaxTotalWidth = 0;
385 int percentage = 0;
79d6c018
VS
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 {
026d1fac 393 cell.cont->Layout(2*m_Padding + 1);
ca16b7a9
VS
394 int maxWidth = cell.cont->GetMaxTotalWidth();
395 int width = cell.nowrap?maxWidth:cell.cont->GetWidth();
026d1fac 396 width -= (cell.colspan-1) * m_Spacing;
ca16b7a9 397 maxWidth -= (cell.colspan-1) * m_Spacing;
79d6c018 398 // HTML 4.0 says it is acceptable to distribute min/max
79d6c018 399 width /= cell.colspan;
ca16b7a9
VS
400 maxWidth /= cell.colspan;
401 for (int j = 0; j < cell.colspan; j++) {
79d6c018
VS
402 if (width > m_ColsInfo[c+j].minWidth)
403 m_ColsInfo[c+j].minWidth = width;
ca16b7a9
VS
404 if (maxWidth > m_ColsInfo[c+j].maxWidth)
405 m_ColsInfo[c+j].maxWidth = maxWidth;
406 }
79d6c018
VS
407 }
408 }
ca16b7a9
VS
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;
79d6c018 416 }
5526e819 417
ca16b7a9
VS
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
3aaaf1aa 427 m_MaxTotalWidth += (m_NumCols + 1) * m_Spacing + 2 * m_Border;
ca16b7a9 428}
5526e819
VS
429
430void wxHtmlTableCell::Layout(int w)
431{
79d6c018 432 ComputeMinMaxWidths();
25c71ccc 433
026d1fac 434 wxHtmlCell::Layout(w);
79d6c018 435
5526e819
VS
436 /*
437
438 WIDTH ADJUSTING :
439
440 */
441
04dbb646 442 if (m_WidthFloatUnits == wxHTML_UNITS_PERCENT)
4f9297b0 443 {
ca16b7a9
VS
444 if (m_WidthFloat < 0)
445 {
446 if (m_WidthFloat < -100)
447 m_WidthFloat = -100;
448 m_Width = (100 + m_WidthFloat) * w / 100;
449 }
25c71ccc 450 else
ca16b7a9
VS
451 {
452 if (m_WidthFloat > 100)
453 m_WidthFloat = 100;
454 m_Width = m_WidthFloat * w / 100;
455 }
5526e819 456 }
04dbb646 457 else
4f9297b0 458 {
5526e819
VS
459 if (m_WidthFloat < 0) m_Width = w + m_WidthFloat;
460 else m_Width = m_WidthFloat;
461 }
462
463
464 /*
465
d13b34d3 466 LAYOUT :
5526e819
VS
467
468 */
469
25c71ccc
VZ
470 /* 1. setup columns widths:
471
ca16b7a9
VS
472 The algorithm tries to keep the table size less than w if possible.
473 */
5526e819 474 {
3aaaf1aa 475 int wpix = m_Width - (m_NumCols + 1) * m_Spacing - 2 * m_Border; // Available space for cell content
5526e819 476 int i, j;
5526e819
VS
477
478 // 1a. setup fixed-width columns:
479 for (i = 0; i < m_NumCols; i++)
efba2b89 480 if (m_ColsInfo[i].units == wxHTML_UNITS_PIXELS)
79d6c018 481 {
25c71ccc 482 m_ColsInfo[i].pixwidth = wxMax(m_ColsInfo[i].width,
79d6c018
VS
483 m_ColsInfo[i].minWidth);
484 wpix -= m_ColsInfo[i].pixwidth;
485 }
5526e819 486
ca16b7a9
VS
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);
25c71ccc 513
3aaaf1aa 514 newWidth = wxMin(newWidth, w - (m_NumCols + 1) * m_Spacing - 2 * m_Border);
ca16b7a9
VS
515 wpix -= m_Width - newWidth;
516 m_Width = newWidth;
517 }
25c71ccc 518
ca16b7a9
VS
519
520 // 1c. setup floating-width columns:
521 int wtemp = wpix;
5526e819 522 for (i = 0; i < m_NumCols; i++)
efba2b89 523 if ((m_ColsInfo[i].units == wxHTML_UNITS_PERCENT) && (m_ColsInfo[i].width != 0))
79d6c018 524 {
ca16b7a9
VS
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
3aaaf1aa 528 int minRequired = m_Border;
ca16b7a9
VS
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;
79d6c018 538 }
3aaaf1aa 539 wpix = wtemp; // minimum cells width
ca16b7a9
VS
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
5526e819 547
5526e819
VS
548 for (i = j = 0; i < m_NumCols; i++)
549 if (m_ColsInfo[i].width == 0) j++;
3aaaf1aa
VZ
550 if (wpix < m_Border)
551 wpix = m_Border;
ca16b7a9
VS
552
553 // Assign widths
5526e819
VS
554 for (i = 0; i < m_NumCols; i++)
555 if (m_ColsInfo[i].width == 0)
8c571f46 556 {
ca16b7a9
VS
557 // Assign with, make sure not to drop below minWidth
558 if (maxWidth)
25c71ccc 559 m_ColsInfo[i].pixwidth = (int)(wpix * (m_ColsInfo[i].maxWidth / (float)maxWidth) + 0.5);
ca16b7a9
VS
560 else
561 m_ColsInfo[i].pixwidth = wpix / j;
25c71ccc 562
ca16b7a9
VS
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 {
25c71ccc 577 int diff = (int)(m_ColsInfo[i].pixwidth - (wpix * m_ColsInfo[i].maxWidth / (float)maxWidth + 0.5));
ca16b7a9
VS
578 maxWidth += diff - m_ColsInfo[i].maxWidth;
579 }
580 else
581 maxWidth -= m_ColsInfo[i].maxWidth;
582 }
583 wpix -= m_ColsInfo[i].pixwidth;
8c571f46 584 }
5526e819
VS
585 }
586
587 /* 2. compute positions of columns: */
588 {
3aaaf1aa 589 int wpos = m_Spacing + m_Border;
04dbb646 590 for (int i = 0; i < m_NumCols; i++)
2fa3b707 591 {
5526e819
VS
592 m_ColsInfo[i].leftpos = wpos;
593 wpos += m_ColsInfo[i].pixwidth + m_Spacing;
594 }
7ed0328e
VZ
595
596 // add the remaining space to the last column
3aaaf1aa
VZ
597 if (m_NumCols > 0 && wpos < m_Width - m_Border)
598 m_ColsInfo[m_NumCols-1].pixwidth += m_Width - wpos - m_Border;
5526e819
VS
599 }
600
601 /* 3. sub-layout all cells: */
602 {
2776d7c3 603 int *ypos = new int[m_NumRows + 1];
5526e819
VS
604
605 int actcol, actrow;
606 int fullwid;
607 wxHtmlContainerCell *actcell;
608
3aaaf1aa 609 ypos[0] = m_Spacing + m_Border;
2fa3b707 610 for (actrow = 1; actrow <= m_NumRows; actrow++) ypos[actrow] = -1;
04dbb646 611 for (actrow = 0; actrow < m_NumRows; actrow++)
2fa3b707
VS
612 {
613 if (ypos[actrow] == -1) ypos[actrow] = ypos[actrow-1];
5526e819
VS
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;
a97a264f 622 fullwid += (m_CellInfo[actrow][actcol].colspan - 1) * m_Spacing;
4f9297b0
VS
623 actcell->SetMinHeight(m_CellInfo[actrow][actcol].minheight, m_CellInfo[actrow][actcol].valign);
624 actcell->Layout(fullwid);
5526e819 625
4f9297b0 626 if (ypos[actrow] + actcell->GetHeight() + m_CellInfo[actrow][actcol].rowspan * m_Spacing > ypos[actrow + m_CellInfo[actrow][actcol].rowspan])
5526e819 627 ypos[actrow + m_CellInfo[actrow][actcol].rowspan] =
4f9297b0 628 ypos[actrow] + actcell->GetHeight() + m_CellInfo[actrow][actcol].rowspan * m_Spacing;
5526e819
VS
629 }
630 }
631
04dbb646 632 for (actrow = 0; actrow < m_NumRows; actrow++)
2fa3b707 633 {
5526e819
VS
634 // 3b. place cells in row & let'em all have same height:
635
04dbb646 636 for (actcol = 0; actcol < m_NumCols; actcol++)
2fa3b707 637 {
5526e819
VS
638 if (m_CellInfo[actrow][actcol].flag != cellUsed) continue;
639 actcell = m_CellInfo[actrow][actcol].cont;
4f9297b0 640 actcell->SetMinHeight(
a97a264f 641 ypos[actrow + m_CellInfo[actrow][actcol].rowspan] - ypos[actrow] - m_Spacing,
5526e819
VS
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;
a97a264f 646 fullwid += (m_CellInfo[actrow][actcol].colspan - 1) * m_Spacing;
4f9297b0
VS
647 actcell->Layout(fullwid);
648 actcell->SetPos(m_ColsInfo[actcol].leftpos, ypos[actrow]);
5526e819 649 }
5526e819 650 }
3aaaf1aa 651 m_Height = ypos[m_NumRows] + m_Border;
2776d7c3 652 delete[] ypos;
5526e819 653 }
8c571f46
VS
654
655 /* 4. adjust table's width if it was too small: */
656 if (m_NumCols > 0)
657 {
25c71ccc 658 int twidth = m_ColsInfo[m_NumCols-1].leftpos +
3aaaf1aa 659 m_ColsInfo[m_NumCols-1].pixwidth + m_Spacing + m_Border;
8c571f46
VS
660 if (twidth > m_Width)
661 m_Width = twidth;
662 }
5526e819
VS
663}
664
665
666
667
668
669
670//-----------------------------------------------------------------------------
671// The tables handler:
672//-----------------------------------------------------------------------------
673
674
675TAG_HANDLER_BEGIN(TABLE, "TABLE,TR,TD,TH")
676
677 TAG_HANDLER_VARS
678 wxHtmlTableCell* m_Table;
679 wxString m_tAlign, m_rAlign;
55ad60c9 680 wxHtmlContainerCell *m_enclosingContainer;
5526e819 681
ff8af61c
VZ
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
5526e819
VS
713 TAG_HANDLER_CONSTR(TABLE)
714 {
715 m_Table = NULL;
55ad60c9 716 m_enclosingContainer = NULL;
01325161 717 m_tAlign = m_rAlign = wxEmptyString;
5526e819
VS
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:
04dbb646 726 if (tag.GetName() == wxT("TABLE"))
2fa3b707 727 {
5526e819 728 wxHtmlTableCell *oldt = m_Table;
5526e819 729
a90db8ee 730 wxHtmlContainerCell *oldEnclosing = m_enclosingContainer;
55ad60c9 731 m_enclosingContainer = c = m_WParser->OpenContainer();
5526e819 732
6987a0c4 733 m_Table = new wxHtmlTableCell(c, tag, m_WParser->GetPixelScale());
ca16b7a9
VS
734
735 // width:
736 {
737 if (tag.HasParam(wxT("WIDTH")))
738 {
e0e4b2b0
VZ
739 int width = 0;
740 bool wpercent = false;
741 if (tag.GetParamAsIntOrPercent(wxT("WIDTH"), &width, wpercent))
ca16b7a9 742 {
e0e4b2b0
VZ
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 }
ca16b7a9
VS
751 }
752 }
753 else
754 m_Table->SetWidthFloat(0, wxHTML_UNITS_PIXELS);
755 }
2755d437 756 int oldAlign = m_WParser->GetAlign();
5526e819 757 m_tAlign = wxEmptyString;
8bd72d90
VS
758 if (tag.HasParam(wxT("ALIGN")))
759 m_tAlign = tag.GetParam(wxT("ALIGN"));
5526e819 760
ff8af61c 761 CallParseInnerWithBg(tag, m_Table->GetBackgroundColour());
5526e819 762
2755d437 763 m_WParser->SetAlign(oldAlign);
55ad60c9 764 m_WParser->SetContainer(m_enclosingContainer);
4f9297b0 765 m_WParser->CloseContainer();
25c71ccc 766
5526e819 767 m_Table = oldt;
a90db8ee 768 m_enclosingContainer = oldEnclosing;
55ad60c9
VS
769
770 return true; // ParseInner() called
5526e819
VS
771 }
772
773
211dfedd 774 else if (m_Table)
2fa3b707 775 {
5526e819 776 // new row in table
04dbb646 777 if (tag.GetName() == wxT("TR"))
2fa3b707 778 {
4f9297b0 779 m_Table->AddRow(tag);
5526e819 780 m_rAlign = m_tAlign;
04dbb646 781 if (tag.HasParam(wxT("ALIGN")))
8bd72d90 782 m_rAlign = tag.GetParam(wxT("ALIGN"));
5526e819
VS
783 }
784
785 // new cell
04dbb646 786 else
2fa3b707 787 {
4f9297b0
VS
788 c = m_WParser->SetContainer(new wxHtmlContainerCell(m_Table));
789 m_Table->AddCell(c, tag);
5526e819 790
4f9297b0 791 m_WParser->OpenContainer();
5526e819 792
f14217ab 793 const bool isHeader = tag.GetName() == wxT("TH");
2755d437
VS
794
795 wxString als;
2755d437
VS
796 if (tag.HasParam(wxT("ALIGN")))
797 als = tag.GetParam(wxT("ALIGN"));
f14217ab
VZ
798 else
799 als = m_rAlign;
2755d437 800 als.MakeUpper();
f14217ab 801
2755d437
VS
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);
f14217ab
VZ
808 else // use default alignment
809 m_WParser->SetAlign(isHeader ? wxHTML_ALIGN_CENTER
810 : wxHTML_ALIGN_LEFT);
2755d437 811
4f9297b0 812 m_WParser->OpenContainer();
55ad60c9 813
f14217ab 814 // the header should be rendered in bold by default
8793514e 815 int boldOld = 0;
f14217ab
VZ
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
4bfa6c94
VZ
824 wxColour bgCol;
825 if ( !tag.GetParamAsColour(wxT("BGCOLOR"), &bgCol) )
826 bgCol = m_Table->GetRowDefaultBackgroundColour();
827
828 CallParseInnerWithBg(tag, bgCol);
55ad60c9 829
f14217ab
VZ
830 if ( isHeader )
831 {
832 m_WParser->SetFontBold(boldOld);
833 m_WParser->GetContainer()->InsertCell(
834 new wxHtmlFontCell(m_WParser->CreateCurrentFont()));
835 }
836
55ad60c9
VS
837 // set the current container back to the enclosing one so that
838 // text outside of <th> and <td> isn't included in any cell
839 // (this happens often enough in practice because it's common
840 // to use whitespace between </td> and the next <td>):
841 m_WParser->SetContainer(m_enclosingContainer);
842
843 return true; // ParseInner() called
5526e819
VS
844 }
845 }
55ad60c9 846
d1da8872 847 return false;
5526e819
VS
848 }
849
850TAG_HANDLER_END(TABLE)
851
852
853
854
855
856TAGS_MODULE_BEGIN(Tables)
857
858 TAGS_MODULE_ADD(TABLE)
859
860TAGS_MODULE_END(Tables)
861
862
863#endif