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