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