]>
git.saurik.com Git - wxWidgets.git/blob - src/html/m_tables.cpp
1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: wxHtml module for tables
4 // Author: Vaclav Slavik
6 // Copyright: (c) 1999 Vaclav Slavik
7 // Licence: wxWindows Licence
8 /////////////////////////////////////////////////////////////////////////////
10 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
11 #pragma implementation
14 #include "wx/wxprec.h"
17 #if wxUSE_HTML && wxUSE_STREAMS
28 1. This version of m_tables doesn't support auto-layout algorithm.
29 This means that all columns are of same width unless explicitly specified.
33 #include "wx/html/forcelnk.h"
34 #include "wx/html/m_templ.h"
36 #include "wx/html/htmlcell.h"
38 FORCE_LINK_ME(m_tables
)
41 #define TABLE_BORDER_CLR_1 wxColour(0xC5, 0xC2, 0xC5)
42 #define TABLE_BORDER_CLR_2 wxColour(0x62, 0x61, 0x62)
45 //-----------------------------------------------------------------------------
47 //-----------------------------------------------------------------------------
53 // width of the column either in pixels or percents
54 // ('width' is the number, 'units' determines its meaning)
55 int minWidth
, maxWidth
;
56 // minimal/maximal column width. This is needed by HTML 4.0
57 // layouting algorithm and can be determined by trying to
58 // layout table cells with width=1 and width=infinity
59 int leftpos
, pixwidth
, maxrealwidth
;
60 // temporary (depends on actual width of table)
72 wxHtmlContainerCell
*cont
;
74 int minheight
, valign
;
79 class wxHtmlTableCell
: public wxHtmlContainerCell
82 /* These are real attributes: */
84 // should we draw borders or not?
86 // number of columns; rows
87 int m_NumCols
, m_NumRows
;
88 // array of column information
89 colStruct
*m_ColsInfo
;
90 // 2D array of all cells in the table : m_CellInfo[row][column]
91 cellStruct
**m_CellInfo
;
92 // spaces between cells
94 // cells internal indentation
98 /* ...and these are valid only when parsing the table: */
100 // number of actual column (ranging from 0..m_NumCols)
101 int m_ActualCol
, m_ActualRow
;
103 // default values (for table and row):
104 wxColour m_tBkg
, m_rBkg
;
105 wxString m_tValign
, m_rValign
;
111 wxHtmlTableCell(wxHtmlContainerCell
*parent
, const wxHtmlTag
& tag
, double pixel_scale
= 1.0);
113 virtual void Layout(int w
);
115 void AddRow(const wxHtmlTag
& tag
);
116 void AddCell(wxHtmlContainerCell
*cell
, const wxHtmlTag
& tag
);
119 // Reallocates memory to given number of cols/rows
120 // and changes m_NumCols/m_NumRows value to reflect this change
121 // NOTE! You CAN'T change m_NumCols/m_NumRows before calling this!!
122 void ReallocCols(int cols
);
123 void ReallocRows(int rows
);
125 // Computes minimal and maximal widths of columns. Needs to be called
126 // only once, before first Layout().
127 void ComputeMinMaxWidths();
129 DECLARE_NO_COPY_CLASS(wxHtmlTableCell
)
134 wxHtmlTableCell::wxHtmlTableCell(wxHtmlContainerCell
*parent
, const wxHtmlTag
& tag
, double pixel_scale
)
135 : wxHtmlContainerCell(parent
)
137 m_PixelScale
= pixel_scale
;
139 (tag
.HasParam(wxT("BORDER")) && tag
.GetParam(wxT("BORDER")) != wxT("0"));
141 m_NumCols
= m_NumRows
= 0;
143 m_ActualCol
= m_ActualRow
= -1;
146 if (tag
.HasParam(wxT("BGCOLOR")))
147 tag
.GetParamAsColour(wxT("BGCOLOR"), &m_tBkg
);
148 if (tag
.HasParam(wxT("VALIGN")))
149 m_tValign
= tag
.GetParam(wxT("VALIGN"));
151 m_tValign
= wxEmptyString
;
152 if (!tag
.GetParamAsInt(wxT("CELLSPACING"), &m_Spacing
))
154 if (!tag
.GetParamAsInt(wxT("CELLPADDING"), &m_Padding
))
156 m_Spacing
= (int)(m_PixelScale
* (double)m_Spacing
);
157 m_Padding
= (int)(m_PixelScale
* (double)m_Padding
);
160 SetBorder(TABLE_BORDER_CLR_1
, TABLE_BORDER_CLR_2
);
165 wxHtmlTableCell::~wxHtmlTableCell()
167 if (m_ColsInfo
) free(m_ColsInfo
);
170 for (int i
= 0; i
< m_NumRows
; i
++)
178 void wxHtmlTableCell::ReallocCols(int cols
)
182 for (i
= 0; i
< m_NumRows
; i
++)
184 m_CellInfo
[i
] = (cellStruct
*) realloc(m_CellInfo
[i
], sizeof(cellStruct
) * cols
);
185 for (j
= m_NumCols
; j
< cols
; j
++)
186 m_CellInfo
[i
][j
].flag
= cellFree
;
189 m_ColsInfo
= (colStruct
*) realloc(m_ColsInfo
, sizeof(colStruct
) * cols
);
190 for (j
= m_NumCols
; j
< cols
; j
++)
192 m_ColsInfo
[j
].width
= 0;
193 m_ColsInfo
[j
].units
= wxHTML_UNITS_PERCENT
;
194 m_ColsInfo
[j
].minWidth
= m_ColsInfo
[j
].maxWidth
= -1;
202 void wxHtmlTableCell::ReallocRows(int rows
)
204 m_CellInfo
= (cellStruct
**) realloc(m_CellInfo
, sizeof(cellStruct
*) * rows
);
205 for (int row
= m_NumRows
; row
< rows
; row
++)
208 m_CellInfo
[row
] = NULL
;
211 m_CellInfo
[row
] = (cellStruct
*) malloc(sizeof(cellStruct
) * m_NumCols
);
212 for (int col
= 0; col
< m_NumCols
; col
++)
213 m_CellInfo
[row
][col
].flag
= cellFree
;
220 void wxHtmlTableCell::AddRow(const wxHtmlTag
& tag
)
223 // VS: real allocation of row entry is done in AddCell in order
224 // to correctly handle empty rows (i.e. "<tr></tr>")
225 // m_ActualCol == -1 indicates that AddCell has to allocate new row.
229 if (tag
.HasParam(wxT("BGCOLOR")))
230 tag
.GetParamAsColour(wxT("BGCOLOR"), &m_rBkg
);
231 if (tag
.HasParam(wxT("VALIGN")))
232 m_rValign
= tag
.GetParam(wxT("VALIGN"));
234 m_rValign
= m_tValign
;
239 void wxHtmlTableCell::AddCell(wxHtmlContainerCell
*cell
, const wxHtmlTag
& tag
)
241 // Is this cell in new row?
242 // VS: we can't do it in AddRow, see my comment there
243 if (m_ActualCol
== -1)
245 if (m_ActualRow
+ 1 > m_NumRows
- 1)
246 ReallocRows(m_ActualRow
+ 2);
254 } while ((m_ActualCol
< m_NumCols
) &&
255 (m_CellInfo
[m_ActualRow
][m_ActualCol
].flag
!= cellFree
));
257 if (m_ActualCol
> m_NumCols
- 1)
258 ReallocCols(m_ActualCol
+ 1);
260 int r
= m_ActualRow
, c
= m_ActualCol
;
262 m_CellInfo
[r
][c
].cont
= cell
;
263 m_CellInfo
[r
][c
].colspan
= 1;
264 m_CellInfo
[r
][c
].rowspan
= 1;
265 m_CellInfo
[r
][c
].flag
= cellUsed
;
266 m_CellInfo
[r
][c
].minheight
= 0;
267 m_CellInfo
[r
][c
].valign
= wxHTML_ALIGN_TOP
;
269 /* scan for parameters: */
273 if (tag
.HasParam(wxT("WIDTH")))
275 wxString wd
= tag
.GetParam(wxT("WIDTH"));
277 if (wd
[wd
.Length()-1] == wxT('%'))
279 wxSscanf(wd
.c_str(), wxT("%i%%"), &m_ColsInfo
[c
].width
);
280 m_ColsInfo
[c
].units
= wxHTML_UNITS_PERCENT
;
284 wxSscanf(wd
.c_str(), wxT("%i"), &m_ColsInfo
[c
].width
);
285 m_ColsInfo
[c
].width
= (int)(m_PixelScale
* (double)m_ColsInfo
[c
].width
);
286 m_ColsInfo
[c
].units
= wxHTML_UNITS_PIXELS
;
294 tag
.GetParamAsInt(wxT("COLSPAN"), &m_CellInfo
[r
][c
].colspan
);
295 tag
.GetParamAsInt(wxT("ROWSPAN"), &m_CellInfo
[r
][c
].rowspan
);
297 // VS: the standard says this about col/rowspan:
298 // "This attribute specifies the number of rows spanned by the
299 // current cell. The default value of this attribute is one ("1").
300 // The value zero ("0") means that the cell spans all rows from the
301 // current row to the last row of the table." All mainstream
302 // browsers act as if 0==1, though, and so does wxHTML.
303 if (m_CellInfo
[r
][c
].colspan
< 1)
304 m_CellInfo
[r
][c
].colspan
= 1;
305 if (m_CellInfo
[r
][c
].rowspan
< 1)
306 m_CellInfo
[r
][c
].rowspan
= 1;
308 if ((m_CellInfo
[r
][c
].colspan
> 1) || (m_CellInfo
[r
][c
].rowspan
> 1))
312 if (r
+ m_CellInfo
[r
][c
].rowspan
> m_NumRows
)
313 ReallocRows(r
+ m_CellInfo
[r
][c
].rowspan
);
314 if (c
+ m_CellInfo
[r
][c
].colspan
> m_NumCols
)
315 ReallocCols(c
+ m_CellInfo
[r
][c
].colspan
);
316 for (i
= r
; i
< r
+ m_CellInfo
[r
][c
].rowspan
; i
++)
317 for (j
= c
; j
< c
+ m_CellInfo
[r
][c
].colspan
; j
++)
318 m_CellInfo
[i
][j
].flag
= cellSpan
;
319 m_CellInfo
[r
][c
].flag
= cellUsed
;
325 wxColour bk
= m_rBkg
;
326 if (tag
.HasParam(wxT("BGCOLOR")))
327 tag
.GetParamAsColour(wxT("BGCOLOR"), &bk
);
329 cell
->SetBackgroundColour(bk
);
332 cell
->SetBorder(TABLE_BORDER_CLR_2
, TABLE_BORDER_CLR_1
);
334 // vertical alignment:
337 if (tag
.HasParam(wxT("VALIGN")))
338 valign
= tag
.GetParam(wxT("VALIGN"));
342 if (valign
== wxT("TOP"))
343 m_CellInfo
[r
][c
].valign
= wxHTML_ALIGN_TOP
;
344 else if (valign
== wxT("BOTTOM"))
345 m_CellInfo
[r
][c
].valign
= wxHTML_ALIGN_BOTTOM
;
346 else m_CellInfo
[r
][c
].valign
= wxHTML_ALIGN_CENTER
;
349 cell
->SetIndent(m_Padding
, wxHTML_INDENT_ALL
, wxHTML_UNITS_PIXELS
);
352 void wxHtmlTableCell::ComputeMinMaxWidths()
354 if (m_NumCols
== 0 || m_ColsInfo
[0].minWidth
!= -1) return;
356 for (int c
= 0; c
< m_NumCols
; c
++)
358 for (int r
= 0; r
< m_NumRows
; r
++)
360 cellStruct
& cell
= m_CellInfo
[r
][c
];
361 if (cell
.flag
== cellUsed
)
363 cell
.cont
->Layout(2*m_Padding
+ 1);
364 int width
= cell
.cont
->GetWidth();
365 width
-= (cell
.colspan
-1) * m_Spacing
;
366 // HTML 4.0 says it is acceptable to distribute min/max
367 width
/= cell
.colspan
;
368 for (int j
= 0; j
< cell
.colspan
; j
++)
369 if (width
> m_ColsInfo
[c
+j
].minWidth
)
370 m_ColsInfo
[c
+j
].minWidth
= width
;
375 // FIXME -- compute maxWidth as well. Not needed yet, so there's no
376 // point in computing it.
380 void wxHtmlTableCell::Layout(int w
)
382 ComputeMinMaxWidths();
384 wxHtmlCell::Layout(w
);
392 if (m_WidthFloatUnits
== wxHTML_UNITS_PERCENT
)
394 if (m_WidthFloat
< 0) m_Width
= (100 + m_WidthFloat
) * w
/ 100;
395 else m_Width
= m_WidthFloat
* w
/ 100;
399 if (m_WidthFloat
< 0) m_Width
= w
+ m_WidthFloat
;
400 else m_Width
= m_WidthFloat
;
410 /* 1. setup columns widths: */
412 int wpix
= m_Width
- (m_NumCols
+ 1) * m_Spacing
;
415 // 1a. setup fixed-width columns:
416 for (i
= 0; i
< m_NumCols
; i
++)
417 if (m_ColsInfo
[i
].units
== wxHTML_UNITS_PIXELS
)
419 m_ColsInfo
[i
].pixwidth
= wxMax(m_ColsInfo
[i
].width
,
420 m_ColsInfo
[i
].minWidth
);
421 wpix
-= m_ColsInfo
[i
].pixwidth
;
424 // 1b. setup floating-width columns:
426 for (i
= 0; i
< m_NumCols
; i
++)
427 if ((m_ColsInfo
[i
].units
== wxHTML_UNITS_PERCENT
) && (m_ColsInfo
[i
].width
!= 0))
429 m_ColsInfo
[i
].pixwidth
= wxMax(m_ColsInfo
[i
].width
* wpix
/ 100,
430 m_ColsInfo
[i
].minWidth
);
431 wtemp
+= m_ColsInfo
[i
].pixwidth
;
435 // 1c. setup defalut columns (no width specification supplied):
436 // FIXME: This algorithm doesn't conform to HTML standard : it assigns
437 // equal widths instead of optimal
438 for (i
= j
= 0; i
< m_NumCols
; i
++)
439 if (m_ColsInfo
[i
].width
== 0) j
++;
440 for (i
= 0; i
< m_NumCols
; i
++)
441 if (m_ColsInfo
[i
].width
== 0)
443 // FIXME: this is not optimal, because if we allocate more than
444 // wpix/j pixels to one column, we should try to allocate
445 // smaller place to other columns
446 m_ColsInfo
[i
].pixwidth
= wxMax(wpix
/j
, m_ColsInfo
[i
].minWidth
);
450 /* 2. compute positions of columns: */
452 int wpos
= m_Spacing
;
453 for (int i
= 0; i
< m_NumCols
; i
++)
455 m_ColsInfo
[i
].leftpos
= wpos
;
456 wpos
+= m_ColsInfo
[i
].pixwidth
+ m_Spacing
;
460 /* 3. sub-layout all cells: */
462 int *ypos
= new int[m_NumRows
+ 1];
466 wxHtmlContainerCell
*actcell
;
469 for (actrow
= 1; actrow
<= m_NumRows
; actrow
++) ypos
[actrow
] = -1;
470 for (actrow
= 0; actrow
< m_NumRows
; actrow
++)
472 if (ypos
[actrow
] == -1) ypos
[actrow
] = ypos
[actrow
-1];
473 // 3a. sub-layout and detect max height:
475 for (actcol
= 0; actcol
< m_NumCols
; actcol
++) {
476 if (m_CellInfo
[actrow
][actcol
].flag
!= cellUsed
) continue;
477 actcell
= m_CellInfo
[actrow
][actcol
].cont
;
479 for (int i
= actcol
; i
< m_CellInfo
[actrow
][actcol
].colspan
+ actcol
; i
++)
480 fullwid
+= m_ColsInfo
[i
].pixwidth
;
481 fullwid
+= (m_CellInfo
[actrow
][actcol
].colspan
- 1) * m_Spacing
;
482 actcell
->SetMinHeight(m_CellInfo
[actrow
][actcol
].minheight
, m_CellInfo
[actrow
][actcol
].valign
);
483 actcell
->Layout(fullwid
);
485 if (ypos
[actrow
] + actcell
->GetHeight() + m_CellInfo
[actrow
][actcol
].rowspan
* m_Spacing
> ypos
[actrow
+ m_CellInfo
[actrow
][actcol
].rowspan
])
486 ypos
[actrow
+ m_CellInfo
[actrow
][actcol
].rowspan
] =
487 ypos
[actrow
] + actcell
->GetHeight() + m_CellInfo
[actrow
][actcol
].rowspan
* m_Spacing
;
491 for (actrow
= 0; actrow
< m_NumRows
; actrow
++)
493 // 3b. place cells in row & let'em all have same height:
495 for (actcol
= 0; actcol
< m_NumCols
; actcol
++)
497 if (m_CellInfo
[actrow
][actcol
].flag
!= cellUsed
) continue;
498 actcell
= m_CellInfo
[actrow
][actcol
].cont
;
499 actcell
->SetMinHeight(
500 ypos
[actrow
+ m_CellInfo
[actrow
][actcol
].rowspan
] - ypos
[actrow
] - m_Spacing
,
501 m_CellInfo
[actrow
][actcol
].valign
);
503 for (int i
= actcol
; i
< m_CellInfo
[actrow
][actcol
].colspan
+ actcol
; i
++)
504 fullwid
+= m_ColsInfo
[i
].pixwidth
;
505 fullwid
+= (m_CellInfo
[actrow
][actcol
].colspan
- 1) * m_Spacing
;
506 actcell
->Layout(fullwid
);
507 actcell
->SetPos(m_ColsInfo
[actcol
].leftpos
, ypos
[actrow
]);
510 m_Height
= ypos
[m_NumRows
];
514 /* 4. adjust table's width if it was too small: */
517 int twidth
= m_ColsInfo
[m_NumCols
-1].leftpos
+
518 m_ColsInfo
[m_NumCols
-1].pixwidth
+ m_Spacing
;
519 if (twidth
> m_Width
)
529 //-----------------------------------------------------------------------------
530 // The tables handler:
531 //-----------------------------------------------------------------------------
534 TAG_HANDLER_BEGIN(TABLE
, "TABLE,TR,TD,TH")
537 wxHtmlTableCell
* m_Table
;
538 wxString m_tAlign
, m_rAlign
;
540 TAG_HANDLER_CONSTR(TABLE
)
543 m_tAlign
= m_rAlign
= wxEmptyString
;
547 TAG_HANDLER_PROC(tag
)
549 wxHtmlContainerCell
*c
;
551 // new table started, backup upper-level table (if any) and create new:
552 if (tag
.GetName() == wxT("TABLE"))
554 wxHtmlTableCell
*oldt
= m_Table
;
555 wxHtmlContainerCell
*oldcont
;
557 oldcont
= c
= m_WParser
->OpenContainer();
559 c
->SetWidthFloat(tag
, m_WParser
->GetPixelScale());
560 m_Table
= new wxHtmlTableCell(c
, tag
, m_WParser
->GetPixelScale());
561 int oldAlign
= m_WParser
->GetAlign();
562 m_tAlign
= wxEmptyString
;
563 if (tag
.HasParam(wxT("ALIGN")))
564 m_tAlign
= tag
.GetParam(wxT("ALIGN"));
568 m_WParser
->SetAlign(oldAlign
);
569 m_WParser
->SetContainer(oldcont
);
570 m_WParser
->CloseContainer();
580 if (tag
.GetName() == wxT("TR"))
582 m_Table
->AddRow(tag
);
584 if (tag
.HasParam(wxT("ALIGN")))
585 m_rAlign
= tag
.GetParam(wxT("ALIGN"));
591 c
= m_WParser
->SetContainer(new wxHtmlContainerCell(m_Table
));
592 m_Table
->AddCell(c
, tag
);
594 m_WParser
->OpenContainer();
596 if (tag
.GetName() == wxT("TH")) /*header style*/
597 m_WParser
->SetAlign(wxHTML_ALIGN_CENTER
);
599 m_WParser
->SetAlign(wxHTML_ALIGN_LEFT
);
604 if (tag
.HasParam(wxT("ALIGN")))
605 als
= tag
.GetParam(wxT("ALIGN"));
607 if (als
== wxT("RIGHT"))
608 m_WParser
->SetAlign(wxHTML_ALIGN_RIGHT
);
609 else if (als
== wxT("LEFT"))
610 m_WParser
->SetAlign(wxHTML_ALIGN_LEFT
);
611 else if (als
== wxT("CENTER"))
612 m_WParser
->SetAlign(wxHTML_ALIGN_CENTER
);
614 m_WParser
->OpenContainer();
620 TAG_HANDLER_END(TABLE
)
626 TAGS_MODULE_BEGIN(Tables
)
628 TAGS_MODULE_ADD(TABLE
)
630 TAGS_MODULE_END(Tables
)