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 26FORCE_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 38struct 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 51enum cellState 52{ 53 cellSpan, 54 cellUsed, 55 cellFree 56}; 57 58struct cellStruct 59{ 60 wxHtmlContainerCell *cont; 61 int colspan, rowspan; 62 int minheight, valign; 63 cellState flag; 64 bool nowrap; 65}; 66 67 68class wxHtmlTableCell : public wxHtmlContainerCell 69{ 70protected: 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 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); 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 110private: 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 126wxHtmlTableCell::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 161wxHtmlTableCell::~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 173void 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 183void 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 207void 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 225void 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 244void 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 368void 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 418void 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 663TAG_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 670 TAG_HANDLER_CONSTR(TABLE) 671 { 672 m_Table = NULL; 673 m_enclosingContainer = NULL; 674 m_tAlign = m_rAlign = wxEmptyString; 675 } 676 677 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 786TAG_HANDLER_END(TABLE) 787 788 789 790 791 792TAGS_MODULE_BEGIN(Tables) 793 794 TAGS_MODULE_ADD(TABLE) 795 796TAGS_MODULE_END(Tables) 797 798 799#endif 800