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