removed GetHorizontalConstraints -- no longer needed
[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 "htmlcell.h"
12 #endif
13
14 #include "wx/wxprec.h"
15
16 #include "wx/defs.h"
17
18 #if wxUSE_HTML && wxUSE_STREAMS
19
20 #ifdef __BORLANDC__
21 #pragma hdrstop
22 #endif
23
24 #ifndef WXPRECOMP
25 #include "wx/brush.h"
26 #include "wx/colour.h"
27 #include "wx/dc.h"
28 #endif
29
30 #include "wx/html/htmlcell.h"
31 #include "wx/html/htmlwin.h"
32 #include "wx/settings.h"
33 #include "wx/module.h"
34
35 #include <stdlib.h>
36
37 //-----------------------------------------------------------------------------
38 // Global variables
39 //-----------------------------------------------------------------------------
40
41 static wxCursor *gs_cursorLink = NULL;
42 static wxCursor *gs_cursorText = NULL;
43
44
45 //-----------------------------------------------------------------------------
46 // Helper classes
47 //-----------------------------------------------------------------------------
48
49 void wxHtmlSelection::Set(const wxPoint& fromPos, const wxHtmlCell *fromCell,
50 const wxPoint& toPos, const wxHtmlCell *toCell)
51 {
52 m_fromCell = fromCell;
53 m_toCell = toCell;
54 m_fromPos = fromPos;
55 m_toPos = toPos;
56 }
57
58 void wxHtmlSelection::Set(const wxHtmlCell *fromCell, const wxHtmlCell *toCell)
59 {
60 wxPoint p1 = fromCell ? fromCell->GetAbsPos() : wxDefaultPosition;
61 wxPoint p2 = toCell ? toCell->GetAbsPos() : wxDefaultPosition;
62 if ( toCell )
63 {
64 p2.x += toCell->GetWidth()-1;
65 p2.y += toCell->GetHeight()-1;
66 }
67 Set(p1, fromCell, p2, toCell);
68 }
69
70 wxColour wxDefaultHtmlRenderingStyle::GetSelectedTextColour(
71 const wxColour& clr)
72 {
73 return wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT);
74 }
75
76 wxColour wxDefaultHtmlRenderingStyle::GetSelectedTextBgColour(
77 const wxColour& WXUNUSED(clr))
78 {
79 return wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT);
80 }
81
82
83 //-----------------------------------------------------------------------------
84 // wxHtmlCell
85 //-----------------------------------------------------------------------------
86
87 wxHtmlCell::wxHtmlCell() : wxObject()
88 {
89 m_Next = NULL;
90 m_Parent = NULL;
91 m_Width = m_Height = m_Descent = 0;
92 m_CanLiveOnPagebreak = TRUE;
93 m_Link = NULL;
94 }
95
96 wxHtmlCell::~wxHtmlCell()
97 {
98 delete m_Link;
99 }
100
101
102 void wxHtmlCell::OnMouseClick(wxWindow *parent, int x, int y,
103 const wxMouseEvent& event)
104 {
105 wxHtmlLinkInfo *lnk = GetLink(x, y);
106 if (lnk != NULL)
107 {
108 wxHtmlLinkInfo lnk2(*lnk);
109 lnk2.SetEvent(&event);
110 lnk2.SetHtmlCell(this);
111
112 // note : this cast is legal because parent is *always* wxHtmlWindow
113 wxStaticCast(parent, wxHtmlWindow)->OnLinkClicked(lnk2);
114 }
115 }
116
117
118 wxCursor wxHtmlCell::GetCursor() const
119 {
120 if ( GetLink() )
121 {
122 if ( !gs_cursorLink )
123 gs_cursorLink = new wxCursor(wxCURSOR_HAND);
124 return *gs_cursorLink;
125 }
126 else
127 return *wxSTANDARD_CURSOR;
128 }
129
130
131 bool wxHtmlCell::AdjustPagebreak(int *pagebreak, int* WXUNUSED(known_pagebreaks), int WXUNUSED(number_of_pages)) const
132 {
133 if ((!m_CanLiveOnPagebreak) &&
134 m_PosY < *pagebreak && m_PosY + m_Height > *pagebreak)
135 {
136 *pagebreak = m_PosY;
137 return TRUE;
138 }
139
140 return FALSE;
141 }
142
143
144
145 void wxHtmlCell::SetLink(const wxHtmlLinkInfo& link)
146 {
147 if (m_Link) delete m_Link;
148 m_Link = NULL;
149 if (link.GetHref() != wxEmptyString)
150 m_Link = new wxHtmlLinkInfo(link);
151 }
152
153
154 void wxHtmlCell::Layout(int WXUNUSED(w))
155 {
156 SetPos(0, 0);
157 }
158
159
160
161 const wxHtmlCell* wxHtmlCell::Find(int WXUNUSED(condition), const void* WXUNUSED(param)) const
162 {
163 return NULL;
164 }
165
166
167 wxHtmlCell *wxHtmlCell::FindCellByPos(wxCoord x, wxCoord y,
168 unsigned flags) const
169 {
170 if ( x >= 0 && x < m_Width && y >= 0 && y < m_Height )
171 {
172 return wxConstCast(this, wxHtmlCell);
173 }
174 else
175 {
176 if ((flags & wxHTML_FIND_NEAREST_AFTER) &&
177 (y < 0 || (y < 0+m_Height && x < 0+m_Width)))
178 return wxConstCast(this, wxHtmlCell);
179 else if ((flags & wxHTML_FIND_NEAREST_BEFORE) &&
180 (y >= 0+m_Height || (y >= 0 && x >= 0)))
181 return wxConstCast(this, wxHtmlCell);
182 else
183 return NULL;
184 }
185 }
186
187
188 wxPoint wxHtmlCell::GetAbsPos() const
189 {
190 wxPoint p(m_PosX, m_PosY);
191 for (wxHtmlCell *parent = m_Parent; parent; parent = parent->m_Parent)
192 {
193 p.x += parent->m_PosX;
194 p.y += parent->m_PosY;
195 }
196 return p;
197 }
198
199 unsigned wxHtmlCell::GetDepth() const
200 {
201 unsigned d = 0;
202 for (wxHtmlCell *p = m_Parent; p; p = p->m_Parent)
203 d++;
204 return d;
205 }
206
207 bool wxHtmlCell::IsBefore(wxHtmlCell *cell) const
208 {
209 const wxHtmlCell *c1 = this;
210 const wxHtmlCell *c2 = cell;
211 unsigned d1 = GetDepth();
212 unsigned d2 = cell->GetDepth();
213
214 if ( d1 > d2 )
215 for (; d1 != d2; d1-- )
216 c1 = c1->m_Parent;
217 else if ( d1 < d2 )
218 for (; d1 != d2; d2-- )
219 c2 = c2->m_Parent;
220
221 if ( cell == this )
222 return true;
223
224 while ( c1 && c2 )
225 {
226 if ( c1->m_Parent == c2->m_Parent )
227 {
228 while ( c1 )
229 {
230 if ( c1 == c2 )
231 return true;
232 c1 = c1->GetNext();
233 }
234 return false;
235 }
236 else
237 {
238 c1 = c1->m_Parent;
239 c2 = c2->m_Parent;
240 }
241 }
242
243 wxFAIL_MSG(_T("Cells are in different trees"));
244 return false;
245 }
246
247
248 //-----------------------------------------------------------------------------
249 // wxHtmlWordCell
250 //-----------------------------------------------------------------------------
251
252 wxHtmlWordCell::wxHtmlWordCell(const wxString& word, wxDC& dc) : wxHtmlCell()
253 {
254 m_Word = word;
255 dc.GetTextExtent(m_Word, &m_Width, &m_Height, &m_Descent);
256 SetCanLiveOnPagebreak(FALSE);
257 m_allowLinebreak = true;
258 }
259
260 void wxHtmlWordCell::SetPreviousWord(wxHtmlWordCell *cell)
261 {
262 if ( cell && m_Parent == cell->m_Parent &&
263 !wxIsspace(cell->m_Word.Last()) && !wxIsspace(m_Word[0u]) )
264 {
265 m_allowLinebreak = false;
266 }
267 }
268
269 // Splits m_Word into up to three parts according to selection, returns
270 // substring before, in and after selection and the points (in relative coords)
271 // where s2 and s3 start:
272 void wxHtmlWordCell::Split(wxDC& dc,
273 const wxPoint& selFrom, const wxPoint& selTo,
274 unsigned& pos1, unsigned& pos2) const
275 {
276 wxPoint pt1 = (selFrom == wxDefaultPosition) ?
277 wxDefaultPosition : selFrom - GetAbsPos();
278 wxPoint pt2 = (selTo == wxDefaultPosition) ?
279 wxPoint(m_Width, -1) : selTo - GetAbsPos();
280
281 wxCoord charW, charH;
282 unsigned len = m_Word.length();
283 unsigned i = 0;
284 pos1 = 0;
285
286 // adjust for cases when the start/end position is completely
287 // outside the cell:
288 if ( pt1.y < 0 )
289 pt1.x = 0;
290 if ( pt2.y >= m_Height )
291 pt2.x = m_Width;
292
293 // before selection:
294 while ( pt1.x > 0 && i < len )
295 {
296 dc.GetTextExtent(m_Word[i], &charW, &charH);
297 pt1.x -= charW;
298 if ( pt1.x >= 0 )
299 {
300 pos1 += charW;
301 i++;
302 }
303 }
304
305 // in selection:
306 unsigned j = i;
307 pos2 = pos1;
308 pt2.x -= pos2;
309 while ( pt2.x > 0 && j < len )
310 {
311 dc.GetTextExtent(m_Word[j], &charW, &charH);
312 pt2.x -= charW;
313 if ( pt2.x >= 0 )
314 {
315 pos2 += charW;
316 j++;
317 }
318 }
319
320 pos1 = i;
321 pos2 = j;
322 }
323
324 void wxHtmlWordCell::SetSelectionPrivPos(wxDC& dc, wxHtmlSelection *s) const
325 {
326 unsigned p1, p2;
327
328 Split(dc,
329 this == s->GetFromCell() ? s->GetFromPos() : wxDefaultPosition,
330 this == s->GetToCell() ? s->GetToPos() : wxDefaultPosition,
331 p1, p2);
332
333 wxPoint p(0, m_Word.length());
334
335 if ( this == s->GetFromCell() )
336 p.x = p1; // selection starts here
337 if ( this == s->GetToCell() )
338 p.y = p2; // selection ends here
339
340 if ( this == s->GetFromCell() )
341 s->SetFromPrivPos(p);
342 if ( this == s->GetToCell() )
343 s->SetToPrivPos(p);
344 }
345
346
347 static void SwitchSelState(wxDC& dc, wxHtmlRenderingInfo& info,
348 bool toSelection)
349 {
350 wxColour fg = info.GetState().GetFgColour();
351 wxColour bg = info.GetState().GetBgColour();
352
353 if ( toSelection )
354 {
355 dc.SetBackgroundMode(wxSOLID);
356 dc.SetTextForeground(info.GetStyle().GetSelectedTextColour(fg));
357 dc.SetTextBackground(info.GetStyle().GetSelectedTextBgColour(bg));
358 dc.SetBackground(wxBrush(info.GetStyle().GetSelectedTextBgColour(bg),
359 wxSOLID));
360 }
361 else
362 {
363 dc.SetBackgroundMode(wxTRANSPARENT);
364 dc.SetTextForeground(fg);
365 dc.SetTextBackground(bg);
366 dc.SetBackground(wxBrush(bg, wxSOLID));
367 }
368 }
369
370
371 void wxHtmlWordCell::Draw(wxDC& dc, int x, int y,
372 int WXUNUSED(view_y1), int WXUNUSED(view_y2),
373 wxHtmlRenderingInfo& info)
374 {
375 #if 0 // useful for debugging
376 dc.DrawRectangle(x+m_PosX,y+m_PosY,m_Width,m_Height);
377 #endif
378
379 bool drawSelectionAfterCell = false;
380
381 if ( info.GetState().GetSelectionState() == wxHTML_SEL_CHANGING )
382 {
383 // Selection changing, we must draw the word piecewise:
384 wxHtmlSelection *s = info.GetSelection();
385 wxString txt;
386 int w, h;
387 int ofs = 0;
388
389 wxPoint priv = (this == s->GetFromCell()) ?
390 s->GetFromPrivPos() : s->GetToPrivPos();
391
392 // NB: this is quite a hack: in order to compute selection boundaries
393 // (in word's characters) we must know current font, which is only
394 // possible inside rendering code. Therefore we update the
395 // information here and store it in wxHtmlSelection so that
396 // ConvertToText can use it later:
397 if ( priv == wxDefaultPosition )
398 {
399 SetSelectionPrivPos(dc, s);
400 priv = (this == s->GetFromCell()) ?
401 s->GetFromPrivPos() : s->GetToPrivPos();
402 }
403
404 int part1 = priv.x;
405 int part2 = priv.y;
406
407 if ( part1 > 0 )
408 {
409 txt = m_Word.Mid(0, part1);
410 dc.DrawText(txt, x + m_PosX, y + m_PosY);
411 dc.GetTextExtent(txt, &w, &h);
412 ofs += w;
413 }
414
415 SwitchSelState(dc, info, true);
416
417 txt = m_Word.Mid(part1, part2-part1);
418 dc.DrawText(txt, ofs + x + m_PosX, y + m_PosY);
419
420 if ( (size_t)part2 < m_Word.length() )
421 {
422 dc.GetTextExtent(txt, &w, &h);
423 ofs += w;
424 SwitchSelState(dc, info, false);
425 txt = m_Word.Mid(part2);
426 dc.DrawText(txt, ofs + x + m_PosX, y + m_PosY);
427 }
428 else
429 drawSelectionAfterCell = true;
430 }
431 else
432 {
433 wxHtmlSelectionState selstate = info.GetState().GetSelectionState();
434 // Not changing selection state, draw the word in single mode:
435 if ( selstate != wxHTML_SEL_OUT &&
436 dc.GetBackgroundMode() != wxSOLID )
437 {
438 SwitchSelState(dc, info, true);
439 }
440 else if ( selstate == wxHTML_SEL_OUT &&
441 dc.GetBackgroundMode() == wxSOLID )
442 {
443 SwitchSelState(dc, info, false);
444 }
445 dc.DrawText(m_Word, x + m_PosX, y + m_PosY);
446 drawSelectionAfterCell = (selstate != wxHTML_SEL_OUT);
447 }
448
449 // NB: If the text is justified then there is usually some free space
450 // between adjacent cells and drawing the selection only onto cells
451 // would result in ugly unselected spaces. The code below detects
452 // this special case and renders the selection *outside* the sell,
453 // too.
454 if ( m_Parent->GetAlignHor() == wxHTML_ALIGN_JUSTIFY &&
455 drawSelectionAfterCell )
456 {
457 wxHtmlCell *nextCell = m_Next;
458 while ( nextCell && nextCell->IsFormattingCell() )
459 nextCell = nextCell->GetNext();
460 if ( nextCell )
461 {
462 int nextX = nextCell->GetPosX();
463 if ( m_PosX + m_Width < nextX )
464 {
465 dc.SetBrush(dc.GetBackground());
466 dc.SetPen(*wxTRANSPARENT_PEN);
467 dc.DrawRectangle(x + m_PosX + m_Width, y + m_PosY,
468 nextX - m_PosX - m_Width, m_Height);
469 }
470 }
471 }
472 }
473
474
475 wxString wxHtmlWordCell::ConvertToText(wxHtmlSelection *s) const
476 {
477 if ( s && (this == s->GetFromCell() || this == s->GetToCell()) )
478 {
479 wxPoint priv = this == s->GetFromCell() ? s->GetFromPrivPos()
480 : s->GetToPrivPos();
481
482 // VZ: we may be called before we had a chance to re-render ourselves
483 // and in this case GetFrom/ToPrivPos() is not set yet -- assume
484 // that this only happens in case of a double/triple click (which
485 // seems to be the case now) and so it makes sense to select the
486 // entire contents of the cell in this case
487 //
488 // TODO: but this really needs to be fixed in some better way later...
489 if ( priv != wxDefaultPosition )
490 {
491 int part1 = priv.x;
492 int part2 = priv.y;
493 return m_Word.Mid(part1, part2-part1);
494 }
495 //else: return the whole word below
496 }
497
498 return m_Word;
499 }
500
501 wxCursor wxHtmlWordCell::GetCursor() const
502 {
503 if ( !GetLink() )
504 {
505 if ( !gs_cursorText )
506 gs_cursorText = new wxCursor(wxCURSOR_IBEAM);
507 return *gs_cursorText;
508 }
509 else
510 return wxHtmlCell::GetCursor();
511 }
512
513
514 //-----------------------------------------------------------------------------
515 // wxHtmlContainerCell
516 //-----------------------------------------------------------------------------
517
518
519 wxHtmlContainerCell::wxHtmlContainerCell(wxHtmlContainerCell *parent) : wxHtmlCell()
520 {
521 m_Cells = m_LastCell = NULL;
522 m_Parent = parent;
523 if (m_Parent) m_Parent->InsertCell(this);
524 m_AlignHor = wxHTML_ALIGN_LEFT;
525 m_AlignVer = wxHTML_ALIGN_BOTTOM;
526 m_IndentLeft = m_IndentRight = m_IndentTop = m_IndentBottom = 0;
527 m_WidthFloat = 100; m_WidthFloatUnits = wxHTML_UNITS_PERCENT;
528 m_UseBkColour = FALSE;
529 m_UseBorder = FALSE;
530 m_MinHeight = 0;
531 m_MinHeightAlign = wxHTML_ALIGN_TOP;
532 m_LastLayout = -1;
533 }
534
535 wxHtmlContainerCell::~wxHtmlContainerCell()
536 {
537 wxHtmlCell *cell = m_Cells;
538 while ( cell )
539 {
540 wxHtmlCell *cellNext = cell->GetNext();
541 delete cell;
542 cell = cellNext;
543 }
544 }
545
546
547
548 void wxHtmlContainerCell::SetIndent(int i, int what, int units)
549 {
550 int val = (units == wxHTML_UNITS_PIXELS) ? i : -i;
551 if (what & wxHTML_INDENT_LEFT) m_IndentLeft = val;
552 if (what & wxHTML_INDENT_RIGHT) m_IndentRight = val;
553 if (what & wxHTML_INDENT_TOP) m_IndentTop = val;
554 if (what & wxHTML_INDENT_BOTTOM) m_IndentBottom = val;
555 m_LastLayout = -1;
556 }
557
558
559
560 int wxHtmlContainerCell::GetIndent(int ind) const
561 {
562 if (ind & wxHTML_INDENT_LEFT) return m_IndentLeft;
563 else if (ind & wxHTML_INDENT_RIGHT) return m_IndentRight;
564 else if (ind & wxHTML_INDENT_TOP) return m_IndentTop;
565 else if (ind & wxHTML_INDENT_BOTTOM) return m_IndentBottom;
566 else return -1; /* BUG! Should not be called... */
567 }
568
569
570
571
572 int wxHtmlContainerCell::GetIndentUnits(int ind) const
573 {
574 bool p = FALSE;
575 if (ind & wxHTML_INDENT_LEFT) p = m_IndentLeft < 0;
576 else if (ind & wxHTML_INDENT_RIGHT) p = m_IndentRight < 0;
577 else if (ind & wxHTML_INDENT_TOP) p = m_IndentTop < 0;
578 else if (ind & wxHTML_INDENT_BOTTOM) p = m_IndentBottom < 0;
579 if (p) return wxHTML_UNITS_PERCENT;
580 else return wxHTML_UNITS_PIXELS;
581 }
582
583
584
585 bool wxHtmlContainerCell::AdjustPagebreak(int *pagebreak, int* known_pagebreaks, int number_of_pages) const
586 {
587 if (!m_CanLiveOnPagebreak)
588 return wxHtmlCell::AdjustPagebreak(pagebreak, known_pagebreaks, number_of_pages);
589
590 else
591 {
592 wxHtmlCell *c = GetFirstChild();
593 bool rt = FALSE;
594 int pbrk = *pagebreak - m_PosY;
595
596 while (c)
597 {
598 if (c->AdjustPagebreak(&pbrk, known_pagebreaks, number_of_pages))
599 rt = TRUE;
600 c = c->GetNext();
601 }
602 if (rt)
603 *pagebreak = pbrk + m_PosY;
604 return rt;
605 }
606 }
607
608
609
610 void wxHtmlContainerCell::Layout(int w)
611 {
612 wxHtmlCell::Layout(w);
613
614 if (m_LastLayout == w) return;
615
616 // VS: Any attempt to layout with negative or zero width leads to hell,
617 // but we can't ignore such attempts completely, since it sometimes
618 // happen (e.g. when trying how small a table can be). The best thing we
619 // can do is to set the width of child cells to zero
620 if (w < 1)
621 {
622 m_Width = 0;
623 for (wxHtmlCell *cell = m_Cells; cell; cell = cell->GetNext())
624 cell->Layout(0);
625 // this does two things: it recursively calls this code on all
626 // child contrainers and resets children's position to (0,0)
627 return;
628 }
629
630 wxHtmlCell *cell = m_Cells, *line = m_Cells;
631 wxHtmlCell *nextCell;
632 long xpos = 0, ypos = m_IndentTop;
633 int xdelta = 0, ybasicpos = 0, ydiff;
634 int s_width, nextWordWidth, s_indent;
635 int ysizeup = 0, ysizedown = 0;
636 int MaxLineWidth = 0;
637 int xcnt = 0;
638
639
640 /*
641
642 WIDTH ADJUSTING :
643
644 */
645
646 if (m_WidthFloatUnits == wxHTML_UNITS_PERCENT)
647 {
648 if (m_WidthFloat < 0) m_Width = (100 + m_WidthFloat) * w / 100;
649 else m_Width = m_WidthFloat * w / 100;
650 }
651 else
652 {
653 if (m_WidthFloat < 0) m_Width = w + m_WidthFloat;
654 else m_Width = m_WidthFloat;
655 }
656
657 if (m_Cells)
658 {
659 int l = (m_IndentLeft < 0) ? (-m_IndentLeft * m_Width / 100) : m_IndentLeft;
660 int r = (m_IndentRight < 0) ? (-m_IndentRight * m_Width / 100) : m_IndentRight;
661 for (wxHtmlCell *cell = m_Cells; cell; cell = cell->GetNext())
662 cell->Layout(m_Width - (l + r));
663 }
664
665 /*
666
667 LAYOUTING :
668
669 */
670
671 // adjust indentation:
672 s_indent = (m_IndentLeft < 0) ? (-m_IndentLeft * m_Width / 100) : m_IndentLeft;
673 s_width = m_Width - s_indent - ((m_IndentRight < 0) ? (-m_IndentRight * m_Width / 100) : m_IndentRight);
674
675 // my own layouting:
676 while (cell != NULL)
677 {
678 switch (m_AlignVer)
679 {
680 case wxHTML_ALIGN_TOP : ybasicpos = 0; break;
681 case wxHTML_ALIGN_BOTTOM : ybasicpos = - cell->GetHeight(); break;
682 case wxHTML_ALIGN_CENTER : ybasicpos = - cell->GetHeight() / 2; break;
683 }
684 ydiff = cell->GetHeight() + ybasicpos;
685
686 if (cell->GetDescent() + ydiff > ysizedown) ysizedown = cell->GetDescent() + ydiff;
687 if (ybasicpos + cell->GetDescent() < -ysizeup) ysizeup = - (ybasicpos + cell->GetDescent());
688
689 // layout nonbreakable run of cells:
690 cell->SetPos(xpos, ybasicpos + cell->GetDescent());
691 xpos += cell->GetWidth();
692 cell = cell->GetNext();
693 xcnt++;
694
695 // compute length of the next word that would be added:
696 nextWordWidth = 0;
697 if (cell)
698 {
699 nextCell = cell;
700 do
701 {
702 nextWordWidth += nextCell->GetWidth();
703 nextCell = nextCell->GetNext();
704 } while (nextCell && !nextCell->IsLinebreakAllowed());
705 }
706
707 // force new line if occured:
708 if ((cell == NULL) ||
709 (xpos + nextWordWidth > s_width && cell->IsLinebreakAllowed()))
710 {
711 if (xpos > MaxLineWidth) MaxLineWidth = xpos;
712 if (ysizeup < 0) ysizeup = 0;
713 if (ysizedown < 0) ysizedown = 0;
714 switch (m_AlignHor) {
715 case wxHTML_ALIGN_LEFT :
716 case wxHTML_ALIGN_JUSTIFY :
717 xdelta = 0;
718 break;
719 case wxHTML_ALIGN_RIGHT :
720 xdelta = 0 + (s_width - xpos);
721 break;
722 case wxHTML_ALIGN_CENTER :
723 xdelta = 0 + (s_width - xpos) / 2;
724 break;
725 }
726 if (xdelta < 0) xdelta = 0;
727 xdelta += s_indent;
728
729 ypos += ysizeup;
730
731 if (m_AlignHor != wxHTML_ALIGN_JUSTIFY || cell == NULL)
732 while (line != cell)
733 {
734 line->SetPos(line->GetPosX() + xdelta,
735 ypos + line->GetPosY());
736 line = line->GetNext();
737 }
738 else
739 {
740 int counter = 0;
741 int step = (s_width - xpos);
742 if (step < 0) step = 0;
743 xcnt--;
744 if (xcnt > 0) while (line != cell)
745 {
746 line->SetPos(line->GetPosX() + s_indent +
747 (counter++ * step / xcnt),
748 ypos + line->GetPosY());
749 line = line->GetNext();
750 }
751 xcnt++;
752 }
753
754 ypos += ysizedown;
755 xpos = xcnt = 0;
756 ysizeup = ysizedown = 0;
757 line = cell;
758 }
759 }
760
761 // setup height & width, depending on container layout:
762 m_Height = ypos + (ysizedown + ysizeup) + m_IndentBottom;
763
764 if (m_Height < m_MinHeight)
765 {
766 if (m_MinHeightAlign != wxHTML_ALIGN_TOP)
767 {
768 int diff = m_MinHeight - m_Height;
769 if (m_MinHeightAlign == wxHTML_ALIGN_CENTER) diff /= 2;
770 cell = m_Cells;
771 while (cell)
772 {
773 cell->SetPos(cell->GetPosX(), cell->GetPosY() + diff);
774 cell = cell->GetNext();
775 }
776 }
777 m_Height = m_MinHeight;
778 }
779
780 MaxLineWidth += s_indent + ((m_IndentRight < 0) ? (-m_IndentRight * m_Width / 100) : m_IndentRight);
781 if (m_Width < MaxLineWidth) m_Width = MaxLineWidth;
782
783 m_LastLayout = w;
784 }
785
786 void wxHtmlContainerCell::UpdateRenderingStatePre(wxHtmlRenderingInfo& info,
787 wxHtmlCell *cell) const
788 {
789 wxHtmlSelection *s = info.GetSelection();
790 if (!s) return;
791 if (s->GetFromCell() == cell || s->GetToCell() == cell)
792 {
793 info.GetState().SetSelectionState(wxHTML_SEL_CHANGING);
794 }
795 }
796
797 void wxHtmlContainerCell::UpdateRenderingStatePost(wxHtmlRenderingInfo& info,
798 wxHtmlCell *cell) const
799 {
800 wxHtmlSelection *s = info.GetSelection();
801 if (!s) return;
802 if (s->GetToCell() == cell)
803 info.GetState().SetSelectionState(wxHTML_SEL_OUT);
804 else if (s->GetFromCell() == cell)
805 info.GetState().SetSelectionState(wxHTML_SEL_IN);
806 }
807
808 #define mMin(a, b) (((a) < (b)) ? (a) : (b))
809 #define mMax(a, b) (((a) < (b)) ? (b) : (a))
810
811 void wxHtmlContainerCell::Draw(wxDC& dc, int x, int y, int view_y1, int view_y2,
812 wxHtmlRenderingInfo& info)
813 {
814 // container visible, draw it:
815 if ((y + m_PosY <= view_y2) && (y + m_PosY + m_Height > view_y1))
816 {
817 if (m_UseBkColour)
818 {
819 wxBrush myb = wxBrush(m_BkColour, wxSOLID);
820
821 int real_y1 = mMax(y + m_PosY, view_y1);
822 int real_y2 = mMin(y + m_PosY + m_Height - 1, view_y2);
823
824 dc.SetBrush(myb);
825 dc.SetPen(*wxTRANSPARENT_PEN);
826 dc.DrawRectangle(x + m_PosX, real_y1, m_Width, real_y2 - real_y1 + 1);
827 }
828
829 if (m_UseBorder)
830 {
831 wxPen mypen1(m_BorderColour1, 1, wxSOLID);
832 wxPen mypen2(m_BorderColour2, 1, wxSOLID);
833
834 dc.SetPen(mypen1);
835 dc.DrawLine(x + m_PosX, y + m_PosY, x + m_PosX, y + m_PosY + m_Height - 1);
836 dc.DrawLine(x + m_PosX, y + m_PosY, x + m_PosX + m_Width, y + m_PosY);
837 dc.SetPen(mypen2);
838 dc.DrawLine(x + m_PosX + m_Width - 1, y + m_PosY, x + m_PosX + m_Width - 1, y + m_PosY + m_Height - 1);
839 dc.DrawLine(x + m_PosX, y + m_PosY + m_Height - 1, x + m_PosX + m_Width, y + m_PosY + m_Height - 1);
840 }
841
842 if (m_Cells)
843 {
844 // draw container's contents:
845 for (wxHtmlCell *cell = m_Cells; cell; cell = cell->GetNext())
846 {
847 UpdateRenderingStatePre(info, cell);
848 cell->Draw(dc,
849 x + m_PosX, y + m_PosY, view_y1, view_y2,
850 info);
851 UpdateRenderingStatePost(info, cell);
852 }
853 }
854 }
855 // container invisible, just proceed font+color changing:
856 else
857 {
858 DrawInvisible(dc, x, y, info);
859 }
860 }
861
862
863
864 void wxHtmlContainerCell::DrawInvisible(wxDC& dc, int x, int y,
865 wxHtmlRenderingInfo& info)
866 {
867 if (m_Cells)
868 {
869 for (wxHtmlCell *cell = m_Cells; cell; cell = cell->GetNext())
870 {
871 UpdateRenderingStatePre(info, cell);
872 cell->DrawInvisible(dc, x + m_PosX, y + m_PosY, info);
873 UpdateRenderingStatePost(info, cell);
874 }
875 }
876 }
877
878
879 wxColour wxHtmlContainerCell::GetBackgroundColour()
880 {
881 if (m_UseBkColour)
882 return m_BkColour;
883 else
884 return wxNullColour;
885 }
886
887
888
889 wxHtmlLinkInfo *wxHtmlContainerCell::GetLink(int x, int y) const
890 {
891 wxHtmlCell *cell = FindCellByPos(x, y);
892
893 // VZ: I don't know if we should pass absolute or relative coords to
894 // wxHtmlCell::GetLink()? As the base class version just ignores them
895 // anyhow, it hardly matters right now but should still be clarified
896 return cell ? cell->GetLink(x, y) : NULL;
897 }
898
899
900
901 void wxHtmlContainerCell::InsertCell(wxHtmlCell *f)
902 {
903 if (!m_Cells) m_Cells = m_LastCell = f;
904 else
905 {
906 m_LastCell->SetNext(f);
907 m_LastCell = f;
908 if (m_LastCell) while (m_LastCell->GetNext()) m_LastCell = m_LastCell->GetNext();
909 }
910 f->SetParent(this);
911 m_LastLayout = -1;
912 }
913
914
915
916 void wxHtmlContainerCell::SetAlign(const wxHtmlTag& tag)
917 {
918 if (tag.HasParam(wxT("ALIGN")))
919 {
920 wxString alg = tag.GetParam(wxT("ALIGN"));
921 alg.MakeUpper();
922 if (alg == wxT("CENTER"))
923 SetAlignHor(wxHTML_ALIGN_CENTER);
924 else if (alg == wxT("LEFT"))
925 SetAlignHor(wxHTML_ALIGN_LEFT);
926 else if (alg == wxT("JUSTIFY"))
927 SetAlignHor(wxHTML_ALIGN_JUSTIFY);
928 else if (alg == wxT("RIGHT"))
929 SetAlignHor(wxHTML_ALIGN_RIGHT);
930 m_LastLayout = -1;
931 }
932 }
933
934
935
936 void wxHtmlContainerCell::SetWidthFloat(const wxHtmlTag& tag, double pixel_scale)
937 {
938 if (tag.HasParam(wxT("WIDTH")))
939 {
940 int wdi;
941 wxString wd = tag.GetParam(wxT("WIDTH"));
942
943 if (wd[wd.Length()-1] == wxT('%'))
944 {
945 wxSscanf(wd.c_str(), wxT("%i%%"), &wdi);
946 SetWidthFloat(wdi, wxHTML_UNITS_PERCENT);
947 }
948 else
949 {
950 wxSscanf(wd.c_str(), wxT("%i"), &wdi);
951 SetWidthFloat((int)(pixel_scale * (double)wdi), wxHTML_UNITS_PIXELS);
952 }
953 m_LastLayout = -1;
954 }
955 }
956
957
958
959 const wxHtmlCell* wxHtmlContainerCell::Find(int condition, const void* param) const
960 {
961 if (m_Cells)
962 {
963 const wxHtmlCell *r = NULL;
964
965 for (wxHtmlCell *cell = m_Cells; cell; cell = cell->GetNext())
966 {
967 r = cell->Find(condition, param);
968 if (r) return r;
969 }
970 }
971 return NULL;
972 }
973
974
975 wxHtmlCell *wxHtmlContainerCell::FindCellByPos(wxCoord x, wxCoord y,
976 unsigned flags) const
977 {
978 if ( flags & wxHTML_FIND_EXACT )
979 {
980 for ( const wxHtmlCell *cell = m_Cells; cell; cell = cell->GetNext() )
981 {
982 int cx = cell->GetPosX(),
983 cy = cell->GetPosY();
984
985 if ( (cx <= x) && (cx + cell->GetWidth() > x) &&
986 (cy <= y) && (cy + cell->GetHeight() > y) )
987 {
988 return cell->FindCellByPos(x - cx, y - cy, flags);
989 }
990 }
991 }
992 else if ( flags & wxHTML_FIND_NEAREST_AFTER )
993 {
994 wxHtmlCell *c;
995 for ( const wxHtmlCell *cell = m_Cells; cell; cell = cell->GetNext() )
996 {
997 if ( cell->IsFormattingCell() )
998 continue;
999 int cellY = cell->GetPosY();
1000 if (!( y < cellY || (y < cellY + cell->GetHeight() &&
1001 x < cell->GetPosX() + cell->GetWidth()) ))
1002 continue;
1003
1004 c = cell->FindCellByPos(x - cell->GetPosX(), y - cellY, flags);
1005 if (c) return c;
1006 }
1007 }
1008 else if ( flags & wxHTML_FIND_NEAREST_BEFORE )
1009 {
1010 wxHtmlCell *c2, *c = NULL;
1011 for ( const wxHtmlCell *cell = m_Cells; cell; cell = cell->GetNext() )
1012 {
1013 if ( cell->IsFormattingCell() )
1014 continue;
1015 int cellY = cell->GetPosY();
1016 if (!( cellY + cell->GetHeight() <= y ||
1017 (y >= cellY && x >= cell->GetPosX()) ))
1018 break;
1019 c2 = cell->FindCellByPos(x - cell->GetPosX(), y - cellY, flags);
1020 if (c2)
1021 c = c2;
1022 }
1023 if (c) return c;
1024 }
1025
1026 return NULL;
1027 }
1028
1029
1030 void wxHtmlContainerCell::OnMouseClick(wxWindow *parent, int x, int y, const wxMouseEvent& event)
1031 {
1032 wxHtmlCell *cell = FindCellByPos(x, y);
1033 if ( cell )
1034 cell->OnMouseClick(parent, x, y, event);
1035 }
1036
1037
1038
1039 wxHtmlCell *wxHtmlContainerCell::GetFirstTerminal() const
1040 {
1041 if ( m_Cells )
1042 {
1043 wxHtmlCell *c2;
1044 for (wxHtmlCell *c = m_Cells; c; c = c->GetNext())
1045 {
1046 c2 = c->GetFirstTerminal();
1047 if ( c2 )
1048 return c2;
1049 }
1050 }
1051 return NULL;
1052 }
1053
1054 wxHtmlCell *wxHtmlContainerCell::GetLastTerminal() const
1055 {
1056 if ( m_Cells )
1057 {
1058 // most common case first:
1059 wxHtmlCell *c = m_LastCell->GetLastTerminal();
1060 if ( c )
1061 return c;
1062
1063 wxHtmlCell *c2 = NULL;
1064 for (c = m_Cells; c; c = c->GetNext())
1065 c2 = c->GetLastTerminal();
1066 return c2;
1067 }
1068 else
1069 return NULL;
1070 }
1071
1072
1073
1074
1075 // --------------------------------------------------------------------------
1076 // wxHtmlColourCell
1077 // --------------------------------------------------------------------------
1078
1079 void wxHtmlColourCell::Draw(wxDC& dc,
1080 int x, int y,
1081 int WXUNUSED(view_y1), int WXUNUSED(view_y2),
1082 wxHtmlRenderingInfo& info)
1083 {
1084 DrawInvisible(dc, x, y, info);
1085 }
1086
1087 void wxHtmlColourCell::DrawInvisible(wxDC& dc,
1088 int WXUNUSED(x), int WXUNUSED(y),
1089 wxHtmlRenderingInfo& info)
1090 {
1091 wxHtmlRenderingState& state = info.GetState();
1092 if (m_Flags & wxHTML_CLR_FOREGROUND)
1093 {
1094 state.SetFgColour(m_Colour);
1095 if (state.GetSelectionState() != wxHTML_SEL_IN)
1096 dc.SetTextForeground(m_Colour);
1097 else
1098 dc.SetTextForeground(
1099 info.GetStyle().GetSelectedTextColour(m_Colour));
1100 }
1101 if (m_Flags & wxHTML_CLR_BACKGROUND)
1102 {
1103 state.SetBgColour(m_Colour);
1104 if (state.GetSelectionState() != wxHTML_SEL_IN)
1105 {
1106 dc.SetTextBackground(m_Colour);
1107 dc.SetBackground(wxBrush(m_Colour, wxSOLID));
1108 }
1109 else
1110 {
1111 wxColour c = info.GetStyle().GetSelectedTextBgColour(m_Colour);
1112 dc.SetTextBackground(c);
1113 dc.SetBackground(wxBrush(c, wxSOLID));
1114 }
1115 }
1116 }
1117
1118
1119
1120
1121 // ---------------------------------------------------------------------------
1122 // wxHtmlFontCell
1123 // ---------------------------------------------------------------------------
1124
1125 void wxHtmlFontCell::Draw(wxDC& dc,
1126 int WXUNUSED(x), int WXUNUSED(y),
1127 int WXUNUSED(view_y1), int WXUNUSED(view_y2),
1128 wxHtmlRenderingInfo& WXUNUSED(info))
1129 {
1130 dc.SetFont(m_Font);
1131 }
1132
1133 void wxHtmlFontCell::DrawInvisible(wxDC& dc, int WXUNUSED(x), int WXUNUSED(y),
1134 wxHtmlRenderingInfo& WXUNUSED(info))
1135 {
1136 dc.SetFont(m_Font);
1137 }
1138
1139
1140
1141
1142
1143
1144
1145
1146 // ---------------------------------------------------------------------------
1147 // wxHtmlWidgetCell
1148 // ---------------------------------------------------------------------------
1149
1150 wxHtmlWidgetCell::wxHtmlWidgetCell(wxWindow *wnd, int w)
1151 {
1152 int sx, sy;
1153 m_Wnd = wnd;
1154 m_Wnd->GetSize(&sx, &sy);
1155 m_Width = sx, m_Height = sy;
1156 m_WidthFloat = w;
1157 }
1158
1159
1160 void wxHtmlWidgetCell::Draw(wxDC& WXUNUSED(dc),
1161 int WXUNUSED(x), int WXUNUSED(y),
1162 int WXUNUSED(view_y1), int WXUNUSED(view_y2),
1163 wxHtmlRenderingInfo& WXUNUSED(info))
1164 {
1165 int absx = 0, absy = 0, stx, sty;
1166 wxHtmlCell *c = this;
1167
1168 while (c)
1169 {
1170 absx += c->GetPosX();
1171 absy += c->GetPosY();
1172 c = c->GetParent();
1173 }
1174
1175 ((wxScrolledWindow*)(m_Wnd->GetParent()))->GetViewStart(&stx, &sty);
1176 m_Wnd->SetSize(absx - wxHTML_SCROLL_STEP * stx, absy - wxHTML_SCROLL_STEP * sty, m_Width, m_Height);
1177 }
1178
1179
1180
1181 void wxHtmlWidgetCell::DrawInvisible(wxDC& WXUNUSED(dc),
1182 int WXUNUSED(x), int WXUNUSED(y),
1183 wxHtmlRenderingInfo& WXUNUSED(info))
1184 {
1185 int absx = 0, absy = 0, stx, sty;
1186 wxHtmlCell *c = this;
1187
1188 while (c)
1189 {
1190 absx += c->GetPosX();
1191 absy += c->GetPosY();
1192 c = c->GetParent();
1193 }
1194
1195 ((wxScrolledWindow*)(m_Wnd->GetParent()))->GetViewStart(&stx, &sty);
1196 m_Wnd->SetSize(absx - wxHTML_SCROLL_STEP * stx, absy - wxHTML_SCROLL_STEP * sty, m_Width, m_Height);
1197 }
1198
1199
1200
1201 void wxHtmlWidgetCell::Layout(int w)
1202 {
1203 if (m_WidthFloat != 0)
1204 {
1205 m_Width = (w * m_WidthFloat) / 100;
1206 m_Wnd->SetSize(m_Width, m_Height);
1207 }
1208
1209 wxHtmlCell::Layout(w);
1210 }
1211
1212
1213
1214 // ----------------------------------------------------------------------------
1215 // wxHtmlTerminalCellsInterator
1216 // ----------------------------------------------------------------------------
1217
1218 const wxHtmlCell* wxHtmlTerminalCellsInterator::operator++()
1219 {
1220 if ( !m_pos )
1221 return NULL;
1222
1223 do
1224 {
1225 if ( m_pos == m_to )
1226 {
1227 m_pos = NULL;
1228 return NULL;
1229 }
1230
1231 if ( m_pos->GetNext() )
1232 m_pos = m_pos->GetNext();
1233 else
1234 {
1235 // we must go up the hierarchy until we reach container where this
1236 // is not the last child, and then go down to first terminal cell:
1237 while ( m_pos->GetNext() == NULL )
1238 {
1239 m_pos = m_pos->GetParent();
1240 if ( !m_pos )
1241 return NULL;
1242 }
1243 m_pos = m_pos->GetNext();
1244 }
1245 while ( m_pos->GetFirstChild() != NULL )
1246 m_pos = m_pos->GetFirstChild();
1247 } while ( !m_pos->IsTerminalCell() );
1248
1249 return m_pos;
1250 }
1251
1252
1253
1254
1255
1256
1257
1258 //-----------------------------------------------------------------------------
1259 // Cleanup
1260 //-----------------------------------------------------------------------------
1261
1262 class wxHtmlCellModule: public wxModule
1263 {
1264 DECLARE_DYNAMIC_CLASS(wxHtmlCellModule)
1265 public:
1266 wxHtmlCellModule() : wxModule() {}
1267 bool OnInit() { return true; }
1268 void OnExit()
1269 {
1270 wxDELETE(gs_cursorLink);
1271 wxDELETE(gs_cursorText);
1272 }
1273 };
1274
1275 IMPLEMENT_DYNAMIC_CLASS(wxHtmlCellModule, wxModule)
1276
1277 #endif