removed/replaced include 'wx/wx.h'
[wxWidgets.git] / src / html / m_tables.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: m_tables.cpp
3 // Purpose: wxHtml module for tables
4 // Author: Vaclav Slavik
5 // RCS-ID: $Id$
6 // Copyright: (c) 1999 Vaclav Slavik
7 // Licence: wxWindows Licence
8 /////////////////////////////////////////////////////////////////////////////
9
10 #ifdef __GNUG__
11 #pragma implementation
12 #endif
13
14 #include "wx/wxprec.h"
15
16 #include "wx/defs.h"
17 #if wxUSE_HTML && wxUSE_STREAMS
18 #ifdef __BORDLANDC__
19 #pragma hdrstop
20 #endif
21
22 #ifndef WXPRECOMP
23 #endif
24
25
26 /*
27 REMARKS:
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.
30 */
31
32
33 #include "wx/html/forcelnk.h"
34 #include "wx/html/m_templ.h"
35
36 #include "wx/html/htmlcell.h"
37
38 FORCE_LINK_ME(m_tables)
39
40
41 #define TABLE_BORDER_CLR_1 wxColour(0xC5, 0xC2, 0xC5)
42 #define TABLE_BORDER_CLR_2 wxColour(0x62, 0x61, 0x62)
43
44
45 //-----------------------------------------------------------------------------
46 // wxHtmlTableCell
47 //-----------------------------------------------------------------------------
48
49
50 typedef struct {
51 int width, units;
52 // universal
53 int leftpos, pixwidth, maxrealwidth;
54 // temporary (depends on width of table)
55 } colStruct;
56
57 typedef enum {
58 cellSpan,
59 cellUsed,
60 cellFree
61 } cellState;
62
63 typedef struct {
64 wxHtmlContainerCell *cont;
65 int colspan, rowspan;
66 int minheight, valign;
67 cellState flag;
68 } cellStruct;
69
70
71 class wxHtmlTableCell : public wxHtmlContainerCell
72 {
73 protected:
74 /* These are real attributes: */
75 bool m_HasBorders;
76 // should we draw borders or not?
77 int m_NumCols, m_NumRows;
78 // number of columns; rows
79 colStruct *m_ColsInfo;
80 // array of column information
81 cellStruct **m_CellInfo;
82 // 2D array of all cells in the table : m_CellInfo[row][column]
83 int m_Spacing;
84 // spaces between cells
85 int m_Padding;
86 // cells internal indentation
87
88 private:
89 /* ...and these are valid only during parsing of table: */
90 int m_ActualCol, m_ActualRow;
91 // number of actual column (ranging from 0..m_NumCols)
92
93 // default values (for table and row):
94 wxColour m_tBkg, m_rBkg;
95 wxString m_tValign, m_rValign;
96
97 double m_PixelScale;
98
99
100 public:
101 wxHtmlTableCell(wxHtmlContainerCell *parent, const wxHtmlTag& tag, double pixel_scale = 1.0);
102 ~wxHtmlTableCell();
103 virtual void Layout(int w);
104
105 void AddRow(const wxHtmlTag& tag);
106 void AddCell(wxHtmlContainerCell *cell, const wxHtmlTag& tag);
107 private:
108 void ReallocCols(int cols);
109 void ReallocRows(int rows);
110 // reallocates memory to given number of cols/rows
111 // and changes m_NumCols/m_NumRows value to reflect this change
112 // NOTE! You CAN'T change m_NumCols/m_NumRows before calling this!!
113 };
114
115
116
117 wxHtmlTableCell::wxHtmlTableCell(wxHtmlContainerCell *parent, const wxHtmlTag& tag, double pixel_scale)
118 : wxHtmlContainerCell(parent)
119 {
120 m_PixelScale = pixel_scale;
121 m_HasBorders = (tag.GetParam(wxT("BORDER")) != wxT("0"));
122 m_ColsInfo = NULL;
123 m_NumCols = m_NumRows = 0;
124 m_CellInfo = NULL;
125 m_ActualCol = m_ActualRow = -1;
126
127 /* scan params: */
128 if (tag.HasParam(wxT("BGCOLOR")))
129 tag.GetParamAsColour(wxT("BGCOLOR"), &m_tBkg);
130 if (tag.HasParam(wxT("VALIGN")))
131 m_tValign = tag.GetParam(wxT("VALIGN"));
132 else
133 m_tValign = wxEmptyString;
134 if (!tag.GetParamAsInt(wxT("CELLSPACING"), &m_Spacing))
135 m_Spacing = 2;
136 if (!tag.GetParamAsInt(wxT("CELLPADDING"), &m_Padding))
137 m_Padding = 3;
138 m_Spacing = (int)(m_PixelScale * (double)m_Spacing);
139 m_Padding = (int)(m_PixelScale * (double)m_Padding);
140
141 if (m_HasBorders)
142 SetBorder(TABLE_BORDER_CLR_1, TABLE_BORDER_CLR_2);
143 }
144
145
146
147 wxHtmlTableCell::~wxHtmlTableCell()
148 {
149 if (m_ColsInfo) free(m_ColsInfo);
150 if (m_CellInfo)
151 {
152 for (int i = 0; i < m_NumRows; i++)
153 free(m_CellInfo[i]);
154 free(m_CellInfo);
155 }
156 }
157
158
159
160 void wxHtmlTableCell::ReallocCols(int cols)
161 {
162 int i,j;
163
164 for (i = 0; i < m_NumRows; i++)
165 {
166 m_CellInfo[i] = (cellStruct*) realloc(m_CellInfo[i], sizeof(cellStruct) * cols);
167 for (j = m_NumCols; j < cols; j++)
168 m_CellInfo[i][j].flag = cellFree;
169 }
170
171 m_ColsInfo = (colStruct*) realloc(m_ColsInfo, sizeof(colStruct) * cols);
172 for (j = m_NumCols; j < cols; j++)
173 {
174 m_ColsInfo[j].width = 0;
175 m_ColsInfo[j].units = wxHTML_UNITS_PERCENT;
176 }
177
178 m_NumCols = cols;
179 }
180
181
182
183 void wxHtmlTableCell::ReallocRows(int rows)
184 {
185 m_CellInfo = (cellStruct**) realloc(m_CellInfo, sizeof(cellStruct*) * rows);
186 for (int row = m_NumRows; row < rows ; row++)
187 {
188 if (m_NumCols == 0)
189 m_CellInfo[row] = NULL;
190 else
191 {
192 m_CellInfo[row] = (cellStruct*) malloc(sizeof(cellStruct) * m_NumCols);
193 for (int col = 0; col < m_NumCols; col++)
194 m_CellInfo[row][col].flag = cellFree;
195 }
196 }
197 m_NumRows = rows;
198 }
199
200
201 void wxHtmlTableCell::AddRow(const wxHtmlTag& tag)
202 {
203 m_ActualCol = -1;
204 // VS: real allocation of row entry is done in AddCell in order
205 // to correctly handle empty rows (i.e. "<tr></tr>")
206 // m_ActualCol == -1 indicates that AddCell has to allocate new row.
207
208 // scan params:
209 m_rBkg = m_tBkg;
210 if (tag.HasParam(wxT("BGCOLOR")))
211 tag.GetParamAsColour(wxT("BGCOLOR"), &m_rBkg);
212 if (tag.HasParam(wxT("VALIGN")))
213 m_rValign = tag.GetParam(wxT("VALIGN"));
214 else
215 m_rValign = m_tValign;
216 }
217
218
219
220 void wxHtmlTableCell::AddCell(wxHtmlContainerCell *cell, const wxHtmlTag& tag)
221 {
222 // Is this cell in new row?
223 // VS: we can't do it in AddRow, see my comment there
224 if (m_ActualCol == -1)
225 {
226 if (m_ActualRow + 1 > m_NumRows - 1)
227 ReallocRows(m_ActualRow + 2);
228 m_ActualRow++;
229 }
230
231 // cells & columns:
232 do
233 {
234 m_ActualCol++;
235 } while ((m_ActualCol < m_NumCols) &&
236 (m_CellInfo[m_ActualRow][m_ActualCol].flag != cellFree));
237
238 if (m_ActualCol > m_NumCols - 1)
239 ReallocCols(m_ActualCol + 1);
240
241 int r = m_ActualRow, c = m_ActualCol;
242
243 m_CellInfo[r][c].cont = cell;
244 m_CellInfo[r][c].colspan = 1;
245 m_CellInfo[r][c].rowspan = 1;
246 m_CellInfo[r][c].flag = cellUsed;
247 m_CellInfo[r][c].minheight = 0;
248 m_CellInfo[r][c].valign = wxHTML_ALIGN_TOP;
249
250 /* scan for parameters: */
251
252 // width:
253 {
254 if (tag.HasParam(wxT("WIDTH")))
255 {
256 wxString wd = tag.GetParam(wxT("WIDTH"));
257
258 if (wd[wd.Length()-1] == wxT('%'))
259 {
260 wxSscanf(wd.c_str(), wxT("%i%%"), &m_ColsInfo[c].width);
261 m_ColsInfo[c].units = wxHTML_UNITS_PERCENT;
262 }
263 else
264 {
265 wxSscanf(wd.c_str(), wxT("%i"), &m_ColsInfo[c].width);
266 m_ColsInfo[c].width = (int)(m_PixelScale * (double)m_ColsInfo[c].width);
267 m_ColsInfo[c].units = wxHTML_UNITS_PIXELS;
268 }
269 }
270 }
271
272
273 // spanning:
274 {
275 tag.GetParamAsInt(wxT("COLSPAN"), &m_CellInfo[r][c].colspan);
276 tag.GetParamAsInt(wxT("ROWSPAN"), &m_CellInfo[r][c].rowspan);
277 if ((m_CellInfo[r][c].colspan != 1) || (m_CellInfo[r][c].rowspan != 1))
278 {
279 int i, j;
280
281 if (r + m_CellInfo[r][c].rowspan > m_NumRows)
282 ReallocRows(r + m_CellInfo[r][c].rowspan);
283 if (c + m_CellInfo[r][c].colspan > m_NumCols)
284 ReallocCols(c + m_CellInfo[r][c].colspan);
285 for (i = r; i < r + m_CellInfo[r][c].rowspan; i++)
286 for (j = c; j < c + m_CellInfo[r][c].colspan; j++)
287 m_CellInfo[i][j].flag = cellSpan;
288 m_CellInfo[r][c].flag = cellUsed;
289 }
290 }
291
292 //background color:
293 {
294 wxColour bk = m_rBkg;
295 if (tag.HasParam(wxT("BGCOLOR")))
296 tag.GetParamAsColour(wxT("BGCOLOR"), &bk);
297 if (bk.Ok())
298 cell->SetBackgroundColour(bk);
299 }
300 if (m_HasBorders)
301 cell->SetBorder(TABLE_BORDER_CLR_2, TABLE_BORDER_CLR_1);
302
303 // vertical alignment:
304 {
305 wxString valign;
306 if (tag.HasParam(wxT("VALIGN")))
307 valign = tag.GetParam(wxT("VALIGN"));
308 else
309 valign = m_tValign;
310 valign.MakeUpper();
311 if (valign == wxT("TOP"))
312 m_CellInfo[r][c].valign = wxHTML_ALIGN_TOP;
313 else if (valign == wxT("BOTTOM"))
314 m_CellInfo[r][c].valign = wxHTML_ALIGN_BOTTOM;
315 else m_CellInfo[r][c].valign = wxHTML_ALIGN_CENTER;
316 }
317
318 cell->SetIndent(m_Padding, wxHTML_INDENT_ALL, wxHTML_UNITS_PIXELS);
319 }
320
321
322
323
324
325 void wxHtmlTableCell::Layout(int w)
326 {
327 /*
328
329 WIDTH ADJUSTING :
330
331 */
332
333 if (m_WidthFloatUnits == wxHTML_UNITS_PERCENT)
334 {
335 if (m_WidthFloat < 0) m_Width = (100 + m_WidthFloat) * w / 100;
336 else m_Width = m_WidthFloat * w / 100;
337 }
338 else
339 {
340 if (m_WidthFloat < 0) m_Width = w + m_WidthFloat;
341 else m_Width = m_WidthFloat;
342 }
343
344
345 /*
346
347 LAYOUTING :
348
349 */
350
351 /* 1. setup columns widths: */
352 {
353 int wpix = m_Width - (m_NumCols + 1) * m_Spacing;
354 int i, j;
355 int wtemp = 0;
356
357 // 1a. setup fixed-width columns:
358 for (i = 0; i < m_NumCols; i++)
359 if (m_ColsInfo[i].units == wxHTML_UNITS_PIXELS)
360 wpix -= (m_ColsInfo[i].pixwidth = m_ColsInfo[i].width);
361
362 // 1b. setup floating-width columns:
363 for (i = 0; i < m_NumCols; i++)
364 if ((m_ColsInfo[i].units == wxHTML_UNITS_PERCENT) && (m_ColsInfo[i].width != 0))
365 wtemp += (m_ColsInfo[i].pixwidth = m_ColsInfo[i].width * wpix / 100);
366 wpix -= wtemp;
367
368 // 1c. setup defalut columns (no width specification supplied):
369 // NOTE! This algorithm doesn't conform to HTML standard : it assigns equal widths
370 // instead of optimal
371 for (i = j = 0; i < m_NumCols; i++)
372 if (m_ColsInfo[i].width == 0) j++;
373 for (i = 0; i < m_NumCols; i++)
374 if (m_ColsInfo[i].width == 0)
375 m_ColsInfo[i].pixwidth = wpix / j;
376 }
377
378 /* 2. compute positions of columns: */
379 {
380 int wpos = m_Spacing;
381 for (int i = 0; i < m_NumCols; i++)
382 {
383 m_ColsInfo[i].leftpos = wpos;
384 wpos += m_ColsInfo[i].pixwidth + m_Spacing;
385 }
386 }
387
388 /* 3. sub-layout all cells: */
389 {
390 int *ypos = new int[m_NumRows + 1];
391
392 int actcol, actrow;
393 int fullwid;
394 wxHtmlContainerCell *actcell;
395
396 ypos[0] = m_Spacing;
397 for (actrow = 1; actrow <= m_NumRows; actrow++) ypos[actrow] = -1;
398 for (actrow = 0; actrow < m_NumRows; actrow++)
399 {
400 if (ypos[actrow] == -1) ypos[actrow] = ypos[actrow-1];
401 // 3a. sub-layout and detect max height:
402
403 for (actcol = 0; actcol < m_NumCols; actcol++) {
404 if (m_CellInfo[actrow][actcol].flag != cellUsed) continue;
405 actcell = m_CellInfo[actrow][actcol].cont;
406 fullwid = 0;
407 for (int i = actcol; i < m_CellInfo[actrow][actcol].colspan + actcol; i++)
408 fullwid += m_ColsInfo[i].pixwidth;
409 fullwid += (m_CellInfo[actrow][actcol].colspan - 1) * m_Spacing;
410 actcell->SetMinHeight(m_CellInfo[actrow][actcol].minheight, m_CellInfo[actrow][actcol].valign);
411 actcell->Layout(fullwid);
412
413 if (ypos[actrow] + actcell->GetHeight() + m_CellInfo[actrow][actcol].rowspan * m_Spacing > ypos[actrow + m_CellInfo[actrow][actcol].rowspan])
414 ypos[actrow + m_CellInfo[actrow][actcol].rowspan] =
415 ypos[actrow] + actcell->GetHeight() + m_CellInfo[actrow][actcol].rowspan * m_Spacing;
416 }
417 }
418
419 for (actrow = 0; actrow < m_NumRows; actrow++)
420 {
421 // 3b. place cells in row & let'em all have same height:
422
423 for (actcol = 0; actcol < m_NumCols; actcol++)
424 {
425 if (m_CellInfo[actrow][actcol].flag != cellUsed) continue;
426 actcell = m_CellInfo[actrow][actcol].cont;
427 actcell->SetMinHeight(
428 ypos[actrow + m_CellInfo[actrow][actcol].rowspan] - ypos[actrow] - m_Spacing,
429 m_CellInfo[actrow][actcol].valign);
430 fullwid = 0;
431 for (int i = actcol; i < m_CellInfo[actrow][actcol].colspan + actcol; i++)
432 fullwid += m_ColsInfo[i].pixwidth;
433 fullwid += (m_CellInfo[actrow][actcol].colspan - 1) * m_Spacing;
434 actcell->Layout(fullwid);
435 actcell->SetPos(m_ColsInfo[actcol].leftpos, ypos[actrow]);
436 }
437 }
438 m_Height = ypos[m_NumRows];
439 delete[] ypos;
440 }
441 }
442
443
444
445
446
447
448 //-----------------------------------------------------------------------------
449 // The tables handler:
450 //-----------------------------------------------------------------------------
451
452
453 TAG_HANDLER_BEGIN(TABLE, "TABLE,TR,TD,TH")
454
455 TAG_HANDLER_VARS
456 wxHtmlTableCell* m_Table;
457 wxString m_tAlign, m_rAlign;
458 int m_OldAlign;
459
460 TAG_HANDLER_CONSTR(TABLE)
461 {
462 m_Table = NULL;
463 m_tAlign = m_rAlign = wxEmptyString;
464 m_OldAlign = wxHTML_ALIGN_LEFT;
465 }
466
467
468 TAG_HANDLER_PROC(tag)
469 {
470 wxHtmlContainerCell *c;
471
472 // new table started, backup upper-level table (if any) and create new:
473 if (tag.GetName() == wxT("TABLE"))
474 {
475 wxHtmlTableCell *oldt = m_Table;
476 wxHtmlContainerCell *oldcont;
477 int m_OldAlign;
478
479 oldcont = c = m_WParser->OpenContainer();
480
481 c->SetWidthFloat(tag, m_WParser->GetPixelScale());
482 m_Table = new wxHtmlTableCell(c, tag, m_WParser->GetPixelScale());
483 m_OldAlign = m_WParser->GetAlign();
484 m_tAlign = wxEmptyString;
485 if (tag.HasParam(wxT("ALIGN")))
486 m_tAlign = tag.GetParam(wxT("ALIGN"));
487
488 ParseInner(tag);
489
490 m_WParser->SetAlign(m_OldAlign);
491 m_WParser->SetContainer(oldcont);
492 m_WParser->CloseContainer();
493 m_Table = oldt;
494 return TRUE;
495 }
496
497
498 else if (m_Table && !tag.IsEnding())
499 {
500 // new row in table
501 if (tag.GetName() == wxT("TR"))
502 {
503 m_Table->AddRow(tag);
504 m_rAlign = m_tAlign;
505 if (tag.HasParam(wxT("ALIGN")))
506 m_rAlign = tag.GetParam(wxT("ALIGN"));
507 }
508
509 // new cell
510 else
511 {
512 m_WParser->SetAlign(m_OldAlign);
513 c = m_WParser->SetContainer(new wxHtmlContainerCell(m_Table));
514 m_Table->AddCell(c, tag);
515
516 m_WParser->OpenContainer();
517
518 if (tag.GetName() == wxT("TH")) /*header style*/
519 {
520 m_WParser->SetAlign(wxHTML_ALIGN_CENTER);
521 }
522
523 {
524 wxString als;
525
526 als = m_rAlign;
527 if (tag.HasParam(wxT("ALIGN")))
528 als = tag.GetParam(wxT("ALIGN"));
529 als.MakeUpper();
530 if (als == wxT("RIGHT"))
531 m_WParser->SetAlign(wxHTML_ALIGN_RIGHT);
532 else if (als == wxT("CENTER"))
533 m_WParser->SetAlign(wxHTML_ALIGN_CENTER);
534 }
535 m_WParser->OpenContainer();
536 }
537 }
538 return FALSE;
539 }
540
541 TAG_HANDLER_END(TABLE)
542
543
544
545
546
547 TAGS_MODULE_BEGIN(Tables)
548
549 TAGS_MODULE_ADD(TABLE)
550
551 TAGS_MODULE_END(Tables)
552
553
554 #endif