new HTML tags parser and entities substitution code
[wxWidgets.git] / src / html / htmlcell.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: htmlcell.cpp
3 // Purpose: wxHtmlCell - basic element of HTML output
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
18 #if wxUSE_HTML && wxUSE_STREAMS
19
20 #ifdef __BORDLANDC__
21 #pragma hdrstop
22 #endif
23
24 #ifndef WXPRECOMP
25 #include "wx/wx.h"
26 #endif
27
28 #include "wx/html/htmlcell.h"
29 #include "wx/html/htmlwin.h"
30 #include <stdlib.h>
31
32
33 //-----------------------------------------------------------------------------
34 // wxHtmlCell
35 //-----------------------------------------------------------------------------
36
37 wxHtmlCell::wxHtmlCell() : wxObject()
38 {
39 m_Next = NULL;
40 m_Parent = NULL;
41 m_Width = m_Height = m_Descent = 0;
42 m_CanLiveOnPagebreak = TRUE;
43 m_Link = NULL;
44 }
45
46 wxHtmlCell::~wxHtmlCell()
47 {
48 if (m_Link) delete m_Link;
49 if (m_Next) delete m_Next;
50 }
51
52
53 void wxHtmlCell::OnMouseClick(wxWindow *parent, int x, int y,
54 const wxMouseEvent& event)
55 {
56 wxHtmlLinkInfo *lnk = GetLink(x, y);
57 if (lnk != NULL)
58 {
59 wxHtmlLinkInfo lnk2(*lnk);
60 lnk2.SetEvent(&event);
61 lnk2.SetHtmlCell(this);
62 ((wxHtmlWindow*)parent)->OnLinkClicked(lnk2);
63 // note : this overcasting is legal because parent is *always* wxHtmlWindow
64 }
65 }
66
67
68
69 bool wxHtmlCell::AdjustPagebreak(int *pagebreak) const
70 {
71 if ((!m_CanLiveOnPagebreak) &&
72 m_PosY < *pagebreak && m_PosY + m_Height > *pagebreak)
73 {
74 *pagebreak = m_PosY;
75 if (m_Next != NULL) m_Next->AdjustPagebreak(pagebreak);
76 return TRUE;
77 }
78
79 else
80 {
81 if (m_Next != NULL) return m_Next->AdjustPagebreak(pagebreak);
82 else return FALSE;
83 }
84 }
85
86
87
88 void wxHtmlCell::SetLink(const wxHtmlLinkInfo& link)
89 {
90 if (m_Link) delete m_Link;
91 m_Link = NULL;
92 if (link.GetHref() != wxEmptyString)
93 m_Link = new wxHtmlLinkInfo(link);
94 }
95
96
97
98 void wxHtmlCell::Layout(int w)
99 {
100 SetPos(0, 0);
101 if (m_Next) m_Next->Layout(w);
102 }
103
104
105 void wxHtmlCell::Draw(wxDC& dc, int x, int y, int view_y1, int view_y2)
106 {
107 if (m_Next) m_Next->Draw(dc, x, y, view_y1, view_y2);
108 }
109
110
111
112 void wxHtmlCell::DrawInvisible(wxDC& dc, int x, int y)
113 {
114 if (m_Next) m_Next->DrawInvisible(dc, x, y);
115 }
116
117
118
119 const wxHtmlCell* wxHtmlCell::Find(int condition, const void* param) const
120 {
121 if (m_Next) return m_Next->Find(condition, param);
122 else return NULL;
123 }
124
125
126
127 //-----------------------------------------------------------------------------
128 // wxHtmlWordCell
129 //-----------------------------------------------------------------------------
130
131 wxHtmlWordCell::wxHtmlWordCell(const wxString& word, wxDC& dc) : wxHtmlCell()
132 {
133 m_Word = word;
134 dc.GetTextExtent(m_Word, &m_Width, &m_Height, &m_Descent);
135 SetCanLiveOnPagebreak(FALSE);
136 }
137
138
139
140 void wxHtmlWordCell::Draw(wxDC& dc, int x, int y, int view_y1, int view_y2)
141 {
142 dc.DrawText(m_Word, x + m_PosX, y + m_PosY);
143 wxHtmlCell::Draw(dc, x, y, view_y1, view_y2);
144 }
145
146
147
148 //-----------------------------------------------------------------------------
149 // wxHtmlContainerCell
150 //-----------------------------------------------------------------------------
151
152
153 wxHtmlContainerCell::wxHtmlContainerCell(wxHtmlContainerCell *parent) : wxHtmlCell()
154 {
155 m_Cells = m_LastCell = NULL;
156 m_Parent = parent;
157 if (m_Parent) m_Parent->InsertCell(this);
158 m_AlignHor = wxHTML_ALIGN_LEFT;
159 m_AlignVer = wxHTML_ALIGN_BOTTOM;
160 m_IndentLeft = m_IndentRight = m_IndentTop = m_IndentBottom = 0;
161 m_WidthFloat = 100; m_WidthFloatUnits = wxHTML_UNITS_PERCENT;
162 m_UseBkColour = FALSE;
163 m_UseBorder = FALSE;
164 m_MinHeight = 0;
165 m_MinHeightAlign = wxHTML_ALIGN_TOP;
166 m_LastLayout = -1;
167 }
168
169 wxHtmlContainerCell::~wxHtmlContainerCell()
170 {
171 if (m_Cells) delete m_Cells;
172 }
173
174
175
176 void wxHtmlContainerCell::SetIndent(int i, int what, int units)
177 {
178 int val = (units == wxHTML_UNITS_PIXELS) ? i : -i;
179 if (what & wxHTML_INDENT_LEFT) m_IndentLeft = val;
180 if (what & wxHTML_INDENT_RIGHT) m_IndentRight = val;
181 if (what & wxHTML_INDENT_TOP) m_IndentTop = val;
182 if (what & wxHTML_INDENT_BOTTOM) m_IndentBottom = val;
183 m_LastLayout = -1;
184 }
185
186
187
188 int wxHtmlContainerCell::GetIndent(int ind) const
189 {
190 if (ind & wxHTML_INDENT_LEFT) return m_IndentLeft;
191 else if (ind & wxHTML_INDENT_RIGHT) return m_IndentRight;
192 else if (ind & wxHTML_INDENT_TOP) return m_IndentTop;
193 else if (ind & wxHTML_INDENT_BOTTOM) return m_IndentBottom;
194 else return -1; /* BUG! Should not be called... */
195 }
196
197
198
199
200 int wxHtmlContainerCell::GetIndentUnits(int ind) const
201 {
202 bool p = FALSE;
203 if (ind & wxHTML_INDENT_LEFT) p = m_IndentLeft < 0;
204 else if (ind & wxHTML_INDENT_RIGHT) p = m_IndentRight < 0;
205 else if (ind & wxHTML_INDENT_TOP) p = m_IndentTop < 0;
206 else if (ind & wxHTML_INDENT_BOTTOM) p = m_IndentBottom < 0;
207 if (p) return wxHTML_UNITS_PERCENT;
208 else return wxHTML_UNITS_PIXELS;
209 }
210
211
212
213 bool wxHtmlContainerCell::AdjustPagebreak(int *pagebreak) const
214 {
215 if (!m_CanLiveOnPagebreak)
216 return wxHtmlCell::AdjustPagebreak(pagebreak);
217
218 else
219 {
220 wxHtmlCell *c = GetFirstCell();
221 bool rt = FALSE;
222 int pbrk = *pagebreak - m_PosY;
223
224 while (c)
225 {
226 if (c->AdjustPagebreak(&pbrk)) rt = TRUE;
227 c = c->GetNext();
228 }
229 if (rt) *pagebreak = pbrk + m_PosY;
230 return rt;
231 }
232 }
233
234
235
236 void wxHtmlContainerCell::Layout(int w)
237 {
238 if (m_LastLayout == w)
239 {
240 wxHtmlCell::Layout(w);
241 return;
242 }
243
244 wxHtmlCell *cell = m_Cells, *line = m_Cells;
245 long xpos = 0, ypos = m_IndentTop;
246 int xdelta = 0, ybasicpos = 0, ydiff;
247 int s_width, s_indent;
248 int ysizeup = 0, ysizedown = 0;
249 int MaxLineWidth = 0;
250 int xcnt = 0;
251
252
253 /*
254
255 WIDTH ADJUSTING :
256
257 */
258
259 if (m_WidthFloatUnits == wxHTML_UNITS_PERCENT)
260 {
261 if (m_WidthFloat < 0) m_Width = (100 + m_WidthFloat) * w / 100;
262 else m_Width = m_WidthFloat * w / 100;
263 }
264 else
265 {
266 if (m_WidthFloat < 0) m_Width = w + m_WidthFloat;
267 else m_Width = m_WidthFloat;
268 }
269
270 if (m_Cells)
271 {
272 int l = (m_IndentLeft < 0) ? (-m_IndentLeft * m_Width / 100) : m_IndentLeft;
273 int r = (m_IndentRight < 0) ? (-m_IndentRight * m_Width / 100) : m_IndentRight;
274 m_Cells->Layout(m_Width - (l + r));
275 }
276
277 /*
278
279 LAYOUTING :
280
281 */
282
283 // adjust indentation:
284 s_indent = (m_IndentLeft < 0) ? (-m_IndentLeft * m_Width / 100) : m_IndentLeft;
285 s_width = m_Width - s_indent - ((m_IndentRight < 0) ? (-m_IndentRight * m_Width / 100) : m_IndentRight);
286
287 // my own layouting:
288 while (cell != NULL)
289 {
290 switch (m_AlignVer)
291 {
292 case wxHTML_ALIGN_TOP : ybasicpos = 0; break;
293 case wxHTML_ALIGN_BOTTOM : ybasicpos = - cell->GetHeight(); break;
294 case wxHTML_ALIGN_CENTER : ybasicpos = - cell->GetHeight() / 2; break;
295 }
296 ydiff = cell->GetHeight() + ybasicpos;
297
298 if (cell->GetDescent() + ydiff > ysizedown) ysizedown = cell->GetDescent() + ydiff;
299 if (ybasicpos + cell->GetDescent() < -ysizeup) ysizeup = - (ybasicpos + cell->GetDescent());
300
301 cell->SetPos(xpos, ybasicpos + cell->GetDescent());
302 xpos += cell->GetWidth();
303 cell = cell->GetNext();
304 xcnt++;
305
306 // force new line if occured:
307 if ((cell == NULL) || (xpos + cell->GetWidth() > s_width))
308 {
309 if (xpos > MaxLineWidth) MaxLineWidth = xpos;
310 if (ysizeup < 0) ysizeup = 0;
311 if (ysizedown < 0) ysizedown = 0;
312 switch (m_AlignHor) {
313 case wxHTML_ALIGN_LEFT :
314 case wxHTML_ALIGN_JUSTIFY :
315 xdelta = 0;
316 break;
317 case wxHTML_ALIGN_RIGHT :
318 xdelta = 0 + (s_width - xpos);
319 break;
320 case wxHTML_ALIGN_CENTER :
321 xdelta = 0 + (s_width - xpos) / 2;
322 break;
323 }
324 if (xdelta < 0) xdelta = 0;
325 xdelta += s_indent;
326
327 ypos += ysizeup;
328
329 if (m_AlignHor != wxHTML_ALIGN_JUSTIFY || cell == NULL)
330 while (line != cell)
331 {
332 line->SetPos(line->GetPosX() + xdelta,
333 ypos + line->GetPosY());
334 line = line->GetNext();
335 }
336 else
337 {
338 int counter = 0;
339 int step = (s_width - xpos);
340 if (step < 0) step = 0;
341 xcnt--;
342 if (xcnt > 0) while (line != cell)
343 {
344 line->SetPos(line->GetPosX() + s_indent +
345 (counter++ * step / xcnt),
346 ypos + line->GetPosY());
347 line = line->GetNext();
348 }
349 xcnt++;
350 }
351
352 ypos += ysizedown;
353 xpos = xcnt = 0;
354 ysizeup = ysizedown = 0;
355 line = cell;
356 }
357 }
358
359 // setup height & width, depending on container layout:
360 m_Height = ypos + (ysizedown + ysizeup) + m_IndentBottom;
361
362 if (m_Height < m_MinHeight)
363 {
364 if (m_MinHeightAlign != wxHTML_ALIGN_TOP)
365 {
366 int diff = m_MinHeight - m_Height;
367 if (m_MinHeightAlign == wxHTML_ALIGN_CENTER) diff /= 2;
368 cell = m_Cells;
369 while (cell)
370 {
371 cell->SetPos(cell->GetPosX(), cell->GetPosY() + diff);
372 cell = cell->GetNext();
373 }
374 }
375 m_Height = m_MinHeight;
376 }
377
378 MaxLineWidth += s_indent + ((m_IndentRight < 0) ? (-m_IndentRight * m_Width / 100) : m_IndentRight);
379 if (m_Width < MaxLineWidth) m_Width = MaxLineWidth;
380
381 m_LastLayout = w;
382
383 wxHtmlCell::Layout(w);
384 }
385
386
387 #define mMin(a, b) (((a) < (b)) ? (a) : (b))
388 #define mMax(a, b) (((a) < (b)) ? (b) : (a))
389
390 void wxHtmlContainerCell::Draw(wxDC& dc, int x, int y, int view_y1, int view_y2)
391 {
392 // container visible, draw it:
393 if ((y + m_PosY < view_y2) && (y + m_PosY + m_Height > view_y1))
394 {
395
396 if (m_UseBkColour)
397 {
398 wxBrush myb = wxBrush(m_BkColour, wxSOLID);
399
400 int real_y1 = mMax(y + m_PosY, view_y1);
401 int real_y2 = mMin(y + m_PosY + m_Height - 1, view_y2);
402
403 dc.SetBrush(myb);
404 dc.SetPen(*wxTRANSPARENT_PEN);
405 dc.DrawRectangle(x + m_PosX, real_y1, m_Width, real_y2 - real_y1 + 1);
406 }
407
408 if (m_UseBorder)
409 {
410 wxPen mypen1(m_BorderColour1, 1, wxSOLID);
411 wxPen mypen2(m_BorderColour2, 1, wxSOLID);
412
413 dc.SetPen(mypen1);
414 dc.DrawLine(x + m_PosX, y + m_PosY, x + m_PosX, y + m_PosY + m_Height - 1);
415 dc.DrawLine(x + m_PosX, y + m_PosY, x + m_PosX + m_Width - 1, y + m_PosY);
416 dc.SetPen(mypen2);
417 dc.DrawLine(x + m_PosX + m_Width - 1, y + m_PosY, x + m_PosX + m_Width - 1, y + m_PosY + m_Height - 1);
418 dc.DrawLine(x + m_PosX, y + m_PosY + m_Height - 1, x + m_PosX + m_Width - 1, y + m_PosY + m_Height - 1);
419 }
420
421 if (m_Cells) m_Cells->Draw(dc, x + m_PosX, y + m_PosY, view_y1, view_y2);
422 }
423 // container invisible, just proceed font+color changing:
424 else
425 {
426 if (m_Cells) m_Cells->DrawInvisible(dc, x + m_PosX, y + m_PosY);
427 }
428
429 wxHtmlCell::Draw(dc, x, y, view_y1, view_y2);
430 }
431
432
433
434 void wxHtmlContainerCell::DrawInvisible(wxDC& dc, int x, int y)
435 {
436 if (m_Cells) m_Cells->DrawInvisible(dc, x + m_PosX, y + m_PosY);
437 wxHtmlCell::DrawInvisible(dc, x, y);
438 }
439
440
441
442 wxHtmlLinkInfo *wxHtmlContainerCell::GetLink(int x, int y) const
443 {
444 wxHtmlCell *c = m_Cells;
445 int cx, cy, cw, ch;
446
447 while (c)
448 {
449 cx = c->GetPosX(), cy = c->GetPosY();
450 cw = c->GetWidth(), ch = c->GetHeight();
451 if ((x >= cx) && (x < cx + cw) && (y >= cy) && (y < cy + ch))
452 return c->GetLink(x - cx, y - cy);
453 c = c->GetNext();
454 }
455 return NULL;
456 }
457
458
459
460 void wxHtmlContainerCell::InsertCell(wxHtmlCell *f)
461 {
462 if (!m_Cells) m_Cells = m_LastCell = f;
463 else
464 {
465 m_LastCell->SetNext(f);
466 m_LastCell = f;
467 if (m_LastCell) while (m_LastCell->GetNext()) m_LastCell = m_LastCell->GetNext();
468 }
469 f->SetParent(this);
470 m_LastLayout = -1;
471 }
472
473
474
475 void wxHtmlContainerCell::SetAlign(const wxHtmlTag& tag)
476 {
477 if (tag.HasParam(wxT("ALIGN")))
478 {
479 wxString alg = tag.GetParam(wxT("ALIGN"));
480 alg.MakeUpper();
481 if (alg == wxT("CENTER"))
482 SetAlignHor(wxHTML_ALIGN_CENTER);
483 else if (alg == wxT("LEFT"))
484 SetAlignHor(wxHTML_ALIGN_LEFT);
485 else if (alg == wxT("JUSTIFY"))
486 SetAlignHor(wxHTML_ALIGN_JUSTIFY);
487 else if (alg == wxT("RIGHT"))
488 SetAlignHor(wxHTML_ALIGN_RIGHT);
489 m_LastLayout = -1;
490 }
491 }
492
493
494
495 void wxHtmlContainerCell::SetWidthFloat(const wxHtmlTag& tag, double pixel_scale)
496 {
497 if (tag.HasParam(wxT("WIDTH")))
498 {
499 int wdi;
500 wxString wd = tag.GetParam(wxT("WIDTH"));
501
502 if (wd[wd.Length()-1] == wxT('%'))
503 {
504 wxSscanf(wd.c_str(), wxT("%i%%"), &wdi);
505 SetWidthFloat(wdi, wxHTML_UNITS_PERCENT);
506 }
507 else
508 {
509 wxSscanf(wd.c_str(), wxT("%i"), &wdi);
510 SetWidthFloat((int)(pixel_scale * (double)wdi), wxHTML_UNITS_PIXELS);
511 }
512 m_LastLayout = -1;
513 }
514 }
515
516
517
518 const wxHtmlCell* wxHtmlContainerCell::Find(int condition, const void* param) const
519 {
520 const wxHtmlCell *r = NULL;
521
522 if (m_Cells)
523 {
524 r = m_Cells->Find(condition, param);
525 if (r) return r;
526 }
527
528 return wxHtmlCell::Find(condition, param);
529 }
530
531
532
533 void wxHtmlContainerCell::OnMouseClick(wxWindow *parent, int x, int y, const wxMouseEvent& event)
534 {
535 if (m_Cells)
536 {
537 wxHtmlCell *c = m_Cells;
538 while (c)
539 {
540 if ( (c->GetPosX() <= x) &&
541 (c->GetPosY() <= y) &&
542 (c->GetPosX() + c->GetWidth() > x) &&
543 (c->GetPosY() + c->GetHeight() > y))
544 {
545 c->OnMouseClick(parent, x - c->GetPosX(), y - c->GetPosY(), event);
546 break;
547 }
548 c = c->GetNext();
549 }
550 }
551 }
552
553
554
555
556
557 //--------------------------------------------------------------------------------
558 // wxHtmlColourCell
559 //--------------------------------------------------------------------------------
560
561 void wxHtmlColourCell::Draw(wxDC& dc, int x, int y, int view_y1, int view_y2)
562 {
563 if (m_Flags & wxHTML_CLR_FOREGROUND)
564 dc.SetTextForeground(m_Colour);
565 if (m_Flags & wxHTML_CLR_BACKGROUND)
566 {
567 dc.SetBackground(wxBrush(m_Colour, wxSOLID));
568 dc.SetTextBackground(m_Colour);
569 }
570 wxHtmlCell::Draw(dc, x, y, view_y1, view_y2);
571 }
572
573 void wxHtmlColourCell::DrawInvisible(wxDC& dc, int x, int y)
574 {
575 if (m_Flags & wxHTML_CLR_FOREGROUND)
576 dc.SetTextForeground(m_Colour);
577 if (m_Flags & wxHTML_CLR_BACKGROUND)
578 {
579 dc.SetBackground(wxBrush(m_Colour, wxSOLID));
580 dc.SetTextBackground(m_Colour);
581 }
582 wxHtmlCell::DrawInvisible(dc, x, y);
583 }
584
585
586
587
588 //--------------------------------------------------------------------------------
589 // wxHtmlFontCell
590 //--------------------------------------------------------------------------------
591
592 void wxHtmlFontCell::Draw(wxDC& dc, int x, int y, int view_y1, int view_y2)
593 {
594 dc.SetFont(m_Font);
595 wxHtmlCell::Draw(dc, x, y, view_y1, view_y2);
596 }
597
598 void wxHtmlFontCell::DrawInvisible(wxDC& dc, int x, int y)
599 {
600 dc.SetFont(m_Font);
601 wxHtmlCell::DrawInvisible(dc, x, y);
602 }
603
604
605
606
607
608
609
610
611 //--------------------------------------------------------------------------------
612 // wxHtmlWidgetCell
613 //--------------------------------------------------------------------------------
614
615 wxHtmlWidgetCell::wxHtmlWidgetCell(wxWindow *wnd, int w)
616 {
617 int sx, sy;
618 m_Wnd = wnd;
619 m_Wnd->GetSize(&sx, &sy);
620 m_Width = sx, m_Height = sy;
621 m_WidthFloat = w;
622 }
623
624
625 void wxHtmlWidgetCell::Draw(wxDC& dc, int x, int y, int view_y1, int view_y2)
626 {
627 int absx = 0, absy = 0, stx, sty;
628 wxHtmlCell *c = this;
629
630 while (c)
631 {
632 absx += c->GetPosX();
633 absy += c->GetPosY();
634 c = c->GetParent();
635 }
636
637 ((wxScrolledWindow*)(m_Wnd->GetParent()))->GetViewStart(&stx, &sty);
638 m_Wnd->SetSize(absx - wxHTML_SCROLL_STEP * stx, absy - wxHTML_SCROLL_STEP * sty, m_Width, m_Height);
639
640 wxHtmlCell::Draw(dc, x, y, view_y1, view_y2);
641 }
642
643
644
645 void wxHtmlWidgetCell::DrawInvisible(wxDC& dc, int x, int y)
646 {
647 int absx = 0, absy = 0, stx, sty;
648 wxHtmlCell *c = this;
649
650 while (c)
651 {
652 absx += c->GetPosX();
653 absy += c->GetPosY();
654 c = c->GetParent();
655 }
656
657 ((wxScrolledWindow*)(m_Wnd->GetParent()))->GetViewStart(&stx, &sty);
658 m_Wnd->SetSize(absx - wxHTML_SCROLL_STEP * stx, absy - wxHTML_SCROLL_STEP * sty, m_Width, m_Height);
659
660 wxHtmlCell::DrawInvisible(dc, x, y);
661 }
662
663
664
665 void wxHtmlWidgetCell::Layout(int w)
666 {
667 if (m_WidthFloat != 0)
668 {
669 m_Width = (w * m_WidthFloat) / 100;
670 m_Wnd->SetSize(m_Width, m_Height);
671 }
672
673 wxHtmlCell::Layout(w);
674 }
675
676 #endif