1/////////////////////////////////////////////////////////////////////////////
2// Name:        src/html/htmlcell.cpp
3// Purpose:     wxHtmlCell - basic element of HTML output
4// Author:      Vaclav Slavik
5// RCS-ID:      $Id: htmlcell.cpp 53318 2008-04-23 11:54:05Z VS $
6// Copyright:   (c) 1999 Vaclav Slavik
7// Licence:     wxWindows licence
8/////////////////////////////////////////////////////////////////////////////
9
10#include "wx/wxprec.h"
11
12#ifdef __BORLANDC__
13    #pragma hdrstop
14#endif
15
16#if wxUSE_HTML && wxUSE_STREAMS
17
18#ifndef WXPRECOMP
19    #include "wx/dynarray.h"
20    #include "wx/brush.h"
21    #include "wx/colour.h"
22    #include "wx/dc.h"
23    #include "wx/settings.h"
24    #include "wx/module.h"
25#endif
26
27#include "wx/html/htmlcell.h"
28#include "wx/html/htmlwin.h"
29
30#include <stdlib.h>
31
32//-----------------------------------------------------------------------------
33// Helper classes
34//-----------------------------------------------------------------------------
35
36void wxHtmlSelection::Set(const wxPoint& fromPos, const wxHtmlCell *fromCell,
37                          const wxPoint& toPos, const wxHtmlCell *toCell)
38{
39    m_fromCell = fromCell;
40    m_toCell = toCell;
41    m_fromPos = fromPos;
42    m_toPos = toPos;
43}
44
45void wxHtmlSelection::Set(const wxHtmlCell *fromCell, const wxHtmlCell *toCell)
46{
47    wxPoint p1 = fromCell ? fromCell->GetAbsPos() : wxDefaultPosition;
48    wxPoint p2 = toCell ? toCell->GetAbsPos() : wxDefaultPosition;
49    if ( toCell )
50    {
51        p2.x += toCell->GetWidth();
52        p2.y += toCell->GetHeight();
53    }
54    Set(p1, fromCell, p2, toCell);
55}
56
57wxColour
58wxDefaultHtmlRenderingStyle::
59GetSelectedTextColour(const wxColour& WXUNUSED(clr))
60{
61    return wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT);
62}
63
64wxColour
65wxDefaultHtmlRenderingStyle::
66GetSelectedTextBgColour(const wxColour& WXUNUSED(clr))
67{
68    return wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT);
69}
70
71
72//-----------------------------------------------------------------------------
73// wxHtmlCell
74//-----------------------------------------------------------------------------
75
76IMPLEMENT_ABSTRACT_CLASS(wxHtmlCell, wxObject)
77
78wxHtmlCell::wxHtmlCell() : wxObject()
79{
80    m_Next = NULL;
81    m_Parent = NULL;
82    m_Width = m_Height = m_Descent = 0;
83    m_ScriptMode = wxHTML_SCRIPT_NORMAL;        // <sub> or <sup> mode
84    m_ScriptBaseline = 0;                       // <sub> or <sup> baseline
85    m_CanLiveOnPagebreak = true;
86    m_Link = NULL;
87}
88
89wxHtmlCell::~wxHtmlCell()
90{
91    delete m_Link;
92}
93
94// Update the descent value when whe are in a <sub> or <sup>.
95// prevbase is the parent base
96void wxHtmlCell::SetScriptMode(wxHtmlScriptMode mode, long previousBase)
97{
98    m_ScriptMode = mode;
99
100    if (mode == wxHTML_SCRIPT_SUP)
101        m_ScriptBaseline = previousBase - (m_Height + 1) / 2;
102    else if (mode == wxHTML_SCRIPT_SUB)
103        m_ScriptBaseline = previousBase + (m_Height + 1) / 6;
104    else
105        m_ScriptBaseline = 0;
106
107    m_Descent += m_ScriptBaseline;
108}
109
110#if WXWIN_COMPATIBILITY_2_6
111
112struct wxHtmlCellOnMouseClickCompatHelper;
113
114static wxHtmlCellOnMouseClickCompatHelper *gs_helperOnMouseClick = NULL;
115
116// helper for routing calls to new ProcessMouseClick() method to deprecated
117// OnMouseClick() method
118struct wxHtmlCellOnMouseClickCompatHelper
119{
120    wxHtmlCellOnMouseClickCompatHelper(wxHtmlWindowInterface *window_,
121                                       const wxPoint& pos_,
122                                       const wxMouseEvent& event_)
123        : window(window_), pos(pos_), event(event_), retval(false)
124    {
125    }
126
127    bool CallOnMouseClick(wxHtmlCell *cell)
128    {
129        wxHtmlCellOnMouseClickCompatHelper *oldHelper = gs_helperOnMouseClick;
130        gs_helperOnMouseClick = this;
131        cell->OnMouseClick
132              (
133                window ? window->GetHTMLWindow() : NULL,
134                pos.x, pos.y,
135                event
136              );
137        gs_helperOnMouseClick = oldHelper;
138        return retval;
139    }
140
141    wxHtmlWindowInterface *window;
142    const wxPoint& pos;
143    const wxMouseEvent& event;
144    bool retval;
145};
146#endif // WXWIN_COMPATIBILITY_2_6
147
148bool wxHtmlCell::ProcessMouseClick(wxHtmlWindowInterface *window,
149                                   const wxPoint& pos,
150                                   const wxMouseEvent& event)
151{
152    wxCHECK_MSG( window, false, _T("window interface must be provided") );
153
154#if WXWIN_COMPATIBILITY_2_6
155    // NB: this hack puts the body of ProcessMouseClick() into OnMouseClick()
156    //     (for which it has to pass the arguments and return value via a
157    //     helper variable because these two methods have different
158    //     signatures), so that old code overriding OnMouseClick will continue
159    //     to work
160    wxHtmlCellOnMouseClickCompatHelper compat(window, pos, event);
161    return compat.CallOnMouseClick(this);
162}
163
164void wxHtmlCell::OnMouseClick(wxWindow *, int, int, const wxMouseEvent& event)
165{
166    wxCHECK_RET( gs_helperOnMouseClick, _T("unexpected call to OnMouseClick") );
167    wxHtmlWindowInterface *window = gs_helperOnMouseClick->window;
168    const wxPoint& pos = gs_helperOnMouseClick->pos;
169#endif // WXWIN_COMPATIBILITY_2_6
170
171    wxHtmlLinkInfo *lnk = GetLink(pos.x, pos.y);
172    bool retval = false;
173
174    if (lnk)
175    {
176        wxHtmlLinkInfo lnk2(*lnk);
177        lnk2.SetEvent(&event);
178        lnk2.SetHtmlCell(this);
179
180        window->OnHTMLLinkClicked(lnk2);
181        retval = true;
182    }
183
184#if WXWIN_COMPATIBILITY_2_6
185    gs_helperOnMouseClick->retval = retval;
186#else
187    return retval;
188#endif // WXWIN_COMPATIBILITY_2_6
189}
190
191#if WXWIN_COMPATIBILITY_2_6
192wxCursor wxHtmlCell::GetCursor() const
193{
194    return wxNullCursor;
195}
196#endif // WXWIN_COMPATIBILITY_2_6
197
198wxCursor wxHtmlCell::GetMouseCursor(wxHtmlWindowInterface *window) const
199{
200#if WXWIN_COMPATIBILITY_2_6
201    // NB: Older versions of wx used GetCursor() virtual method in place of
202    //     GetMouseCursor(interface). This code ensures that user code that
203    //     overriden GetCursor() continues to work. The trick is that the base
204    //     wxHtmlCell::GetCursor() method simply returns wxNullCursor, so we
205    //     know that GetCursor() was overriden iff it returns valid cursor.
206    wxCursor cur = GetCursor();
207    if (cur.Ok())
208        return cur;
209#endif // WXWIN_COMPATIBILITY_2_6
210
211    if ( GetLink() )
212    {
213        return window->GetHTMLCursor(wxHtmlWindowInterface::HTMLCursor_Link);
214    }
215    else
216    {
217        return window->GetHTMLCursor(wxHtmlWindowInterface::HTMLCursor_Default);
218    }
219}
220
221
222bool wxHtmlCell::AdjustPagebreak(int *pagebreak,
223                                 wxArrayInt& WXUNUSED(known_pagebreaks)) const
224{
225    if ((!m_CanLiveOnPagebreak) &&
226                m_PosY < *pagebreak && m_PosY + m_Height > *pagebreak)
227    {
228        *pagebreak = m_PosY;
229        return true;
230    }
231
232    return false;
233}
234
235
236
237void wxHtmlCell::SetLink(const wxHtmlLinkInfo& link)
238{
239    if (m_Link) delete m_Link;
240    m_Link = NULL;
241    if (link.GetHref() != wxEmptyString)
242        m_Link = new wxHtmlLinkInfo(link);
243}
244
245
246void wxHtmlCell::Layout(int WXUNUSED(w))
247{
248    SetPos(0, 0);
249}
250
251
252
253const wxHtmlCell* wxHtmlCell::Find(int WXUNUSED(condition), const void* WXUNUSED(param)) const
254{
255    return NULL;
256}
257
258
259wxHtmlCell *wxHtmlCell::FindCellByPos(wxCoord x, wxCoord y,
260                                      unsigned flags) const
261{
262    if ( x >= 0 && x < m_Width && y >= 0 && y < m_Height )
263    {
264        return wxConstCast(this, wxHtmlCell);
265    }
266    else
267    {
268        if ((flags & wxHTML_FIND_NEAREST_AFTER) &&
269                (y < 0 || (y < 0+m_Height && x < 0+m_Width)))
270            return wxConstCast(this, wxHtmlCell);
271        else if ((flags & wxHTML_FIND_NEAREST_BEFORE) &&
272                (y >= 0+m_Height || (y >= 0 && x >= 0)))
273            return wxConstCast(this, wxHtmlCell);
274        else
275            return NULL;
276    }
277}
278
279
280wxPoint wxHtmlCell::GetAbsPos(wxHtmlCell *rootCell) const
281{
282    wxPoint p(m_PosX, m_PosY);
283    for (wxHtmlCell *parent = m_Parent; parent && parent != rootCell;
284         parent = parent->m_Parent)
285    {
286        p.x += parent->m_PosX;
287        p.y += parent->m_PosY;
288    }
289    return p;
290}
291
292wxHtmlCell *wxHtmlCell::GetRootCell() const
293{
294    wxHtmlCell *c = wxConstCast(this, wxHtmlCell);
295    while ( c->m_Parent )
296        c = c->m_Parent;
297    return c;
298}
299
300unsigned wxHtmlCell::GetDepth() const
301{
302    unsigned d = 0;
303    for (wxHtmlCell *p = m_Parent; p; p = p->m_Parent)
304        d++;
305    return d;
306}
307
308bool wxHtmlCell::IsBefore(wxHtmlCell *cell) const
309{
310    const wxHtmlCell *c1 = this;
311    const wxHtmlCell *c2 = cell;
312    unsigned d1 = GetDepth();
313    unsigned d2 = cell->GetDepth();
314
315    if ( d1 > d2 )
316        for (; d1 != d2; d1-- )
317            c1 = c1->m_Parent;
318    else if ( d1 < d2 )
319        for (; d1 != d2; d2-- )
320            c2 = c2->m_Parent;
321
322    if ( cell == this )
323        return true;
324
325    while ( c1 && c2 )
326    {
327        if ( c1->m_Parent == c2->m_Parent )
328        {
329            while ( c1 )
330            {
331                if ( c1 == c2 )
332                    return true;
333                c1 = c1->GetNext();
334            }
335            return false;
336        }
337        else
338        {
339            c1 = c1->m_Parent;
340            c2 = c2->m_Parent;
341        }
342    }
343
344    wxFAIL_MSG(_T("Cells are in different trees"));
345    return false;
346}
347
348
349//-----------------------------------------------------------------------------
350// wxHtmlWordCell
351//-----------------------------------------------------------------------------
352
353IMPLEMENT_ABSTRACT_CLASS(wxHtmlWordCell, wxHtmlCell)
354
355wxHtmlWordCell::wxHtmlWordCell(const wxString& word, const wxDC& dc) : wxHtmlCell()
356{
357    m_Word = word;
358    dc.GetTextExtent(m_Word, &m_Width, &m_Height, &m_Descent);
359    SetCanLiveOnPagebreak(false);
360    m_allowLinebreak = true;
361}
362
363void wxHtmlWordCell::SetPreviousWord(wxHtmlWordCell *cell)
364{
365    if ( cell && m_Parent == cell->m_Parent &&
366         !wxIsspace(cell->m_Word.Last()) && !wxIsspace(m_Word[0u]) )
367    {
368        m_allowLinebreak = false;
369    }
370}
371
372// Splits m_Word into up to three parts according to selection, returns
373// substring before, in and after selection and the points (in relative coords)
374// where s2 and s3 start:
375void wxHtmlWordCell::Split(const wxDC& dc,
376                           const wxPoint& selFrom, const wxPoint& selTo,
377                           unsigned& pos1, unsigned& pos2) const
378{
379    wxPoint pt1 = (selFrom == wxDefaultPosition) ?
380                   wxDefaultPosition : selFrom - GetAbsPos();
381    wxPoint pt2 = (selTo == wxDefaultPosition) ?
382                   wxPoint(m_Width, wxDefaultCoord) : selTo - GetAbsPos();
383
384    // if the selection is entirely within this cell, make sure pt1 < pt2 in
385    // order to make the rest of this function simpler:
386    if ( selFrom != wxDefaultPosition && selTo != wxDefaultPosition &&
387         selFrom.x > selTo.x )
388    {
389        wxPoint tmp = pt1;
390        pt1 = pt2;
391        pt2 = tmp;
392    }
393
394    unsigned len = m_Word.length();
395    unsigned i = 0;
396    pos1 = 0;
397
398    // adjust for cases when the start/end position is completely
399    // outside the cell:
400    if ( pt1.y < 0 )
401        pt1.x = 0;
402    if ( pt2.y >= m_Height )
403        pt2.x = m_Width;
404
405    // before selection:
406    // (include character under caret only if in first half of width)
407#ifdef __WXMAC__
408    // implementation using PartialExtents to support fractional widths
409    wxArrayInt widths ;
410    dc.GetPartialTextExtents(m_Word,widths) ;
411    while( i < len && pt1.x >= widths[i] )
412        i++ ;
413    if ( i < len )
414    {
415        int charW = (i > 0) ? widths[i] - widths[i-1] : widths[i];
416        if ( widths[i] - pt1.x < charW/2 )
417            i++;
418    }
419#else // !__WXMAC__
420    wxCoord charW, charH;
421    while ( pt1.x > 0 && i < len )
422    {
423        dc.GetTextExtent(m_Word[i], &charW, &charH);
424        pt1.x -= charW;
425        if ( pt1.x >= -charW/2 )
426        {
427            pos1 += charW;
428            i++;
429        }
430    }
431#endif // __WXMAC__/!__WXMAC__
432
433    // in selection:
434    // (include character under caret only if in first half of width)
435    unsigned j = i;
436#ifdef __WXMAC__
437    while( j < len && pt2.x >= widths[j] )
438        j++ ;
439    if ( j < len )
440    {
441        int charW = (j > 0) ? widths[j] - widths[j-1] : widths[j];
442        if ( widths[j] - pt2.x < charW/2 )
443            j++;
444    }
445#else // !__WXMAC__
446    pos2 = pos1;
447    pt2.x -= pos2;
448    while ( pt2.x > 0 && j < len )
449    {
450        dc.GetTextExtent(m_Word[j], &charW, &charH);
451        pt2.x -= charW;
452        if ( pt2.x >= -charW/2 )
453        {
454            pos2 += charW;
455            j++;
456        }
457    }
458#endif // __WXMAC__/!__WXMAC__
459
460    pos1 = i;
461    pos2 = j;
462}
463
464void wxHtmlWordCell::SetSelectionPrivPos(const wxDC& dc, wxHtmlSelection *s) const
465{
466    unsigned p1, p2;
467
468    Split(dc,
469          this == s->GetFromCell() ? s->GetFromPos() : wxDefaultPosition,
470          this == s->GetToCell() ? s->GetToPos() : wxDefaultPosition,
471          p1, p2);
472
473    wxPoint p(0, m_Word.length());
474
475    if ( this == s->GetFromCell() )
476        p.x = p1; // selection starts here
477    if ( this == s->GetToCell() )
478        p.y = p2; // selection ends here
479
480    if ( this == s->GetFromCell() )
481        s->SetFromPrivPos(p);
482    if ( this == s->GetToCell() )
483        s->SetToPrivPos(p);
484}
485
486
487static void SwitchSelState(wxDC& dc, wxHtmlRenderingInfo& info,
488                           bool toSelection)
489{
490    wxColour fg = info.GetState().GetFgColour();
491    wxColour bg = info.GetState().GetBgColour();
492
493    if ( toSelection )
494    {
495        dc.SetBackgroundMode(wxSOLID);
496        dc.SetTextForeground(info.GetStyle().GetSelectedTextColour(fg));
497        dc.SetTextBackground(info.GetStyle().GetSelectedTextBgColour(bg));
498        dc.SetBackground(wxBrush(info.GetStyle().GetSelectedTextBgColour(bg),
499                                 wxSOLID));
500    }
501    else
502    {
503        dc.SetBackgroundMode(wxTRANSPARENT);
504        dc.SetTextForeground(fg);
505        dc.SetTextBackground(bg);
506        dc.SetBackground(wxBrush(bg, wxSOLID));
507    }
508}
509
510
511void wxHtmlWordCell::Draw(wxDC& dc, int x, int y,
512                          int WXUNUSED(view_y1), int WXUNUSED(view_y2),
513                          wxHtmlRenderingInfo& info)
514{
515#if 0 // useful for debugging
516    dc.SetPen(*wxBLACK_PEN);
517    dc.DrawRectangle(x+m_PosX,y+m_PosY,m_Width /* VZ: +1? */ ,m_Height);
518#endif
519
520    bool drawSelectionAfterCell = false;
521
522    if ( info.GetState().GetSelectionState() == wxHTML_SEL_CHANGING )
523    {
524        // Selection changing, we must draw the word piecewise:
525        wxHtmlSelection *s = info.GetSelection();
526        wxString txt;
527        int w, h;
528        int ofs = 0;
529
530        wxPoint priv = (this == s->GetFromCell()) ?
531                           s->GetFromPrivPos() : s->GetToPrivPos();
532
533        // NB: this is quite a hack: in order to compute selection boundaries
534        //     (in word's characters) we must know current font, which is only
535        //     possible inside rendering code. Therefore we update the
536        //     information here and store it in wxHtmlSelection so that
537        //     ConvertToText can use it later:
538        if ( priv == wxDefaultPosition )
539        {
540            SetSelectionPrivPos(dc, s);
541            priv = (this == s->GetFromCell()) ?
542                    s->GetFromPrivPos() : s->GetToPrivPos();
543        }
544
545        int part1 = priv.x;
546        int part2 = priv.y;
547
548        if ( part1 > 0 )
549        {
550            txt = m_Word.Mid(0, part1);
551            dc.DrawText(txt, x + m_PosX, y + m_PosY);
552            dc.GetTextExtent(txt, &w, &h);
553            ofs += w;
554        }
555
556        SwitchSelState(dc, info, true);
557
558        txt = m_Word.Mid(part1, part2-part1);
559        dc.DrawText(txt, ofs + x + m_PosX, y + m_PosY);
560
561        if ( (size_t)part2 < m_Word.length() )
562        {
563            dc.GetTextExtent(txt, &w, &h);
564            ofs += w;
565            SwitchSelState(dc, info, false);
566            txt = m_Word.Mid(part2);
567            dc.DrawText(txt, ofs + x + m_PosX, y + m_PosY);
568        }
569        else
570            drawSelectionAfterCell = true;
571    }
572    else
573    {
574        wxHtmlSelectionState selstate = info.GetState().GetSelectionState();
575        // Not changing selection state, draw the word in single mode:
576        if ( selstate != wxHTML_SEL_OUT &&
577             dc.GetBackgroundMode() != wxSOLID )
578        {
579            SwitchSelState(dc, info, true);
580        }
581        else if ( selstate == wxHTML_SEL_OUT &&
582                  dc.GetBackgroundMode() == wxSOLID )
583        {
584            SwitchSelState(dc, info, false);
585        }
586        dc.DrawText(m_Word, x + m_PosX, y + m_PosY);
587        drawSelectionAfterCell = (selstate != wxHTML_SEL_OUT);
588    }
589
590    // NB: If the text is justified then there is usually some free space
591    //     between adjacent cells and drawing the selection only onto cells
592    //     would result in ugly unselected spaces. The code below detects
593    //     this special case and renders the selection *outside* the sell,
594    //     too.
595    if ( m_Parent->GetAlignHor() == wxHTML_ALIGN_JUSTIFY &&
596         drawSelectionAfterCell )
597    {
598        wxHtmlCell *nextCell = m_Next;
599        while ( nextCell && nextCell->IsFormattingCell() )
600            nextCell = nextCell->GetNext();
601        if ( nextCell )
602        {
603            int nextX = nextCell->GetPosX();
604            if ( m_PosX + m_Width < nextX )
605            {
606                dc.SetBrush(dc.GetBackground());
607                dc.SetPen(*wxTRANSPARENT_PEN);
608                dc.DrawRectangle(x + m_PosX + m_Width, y + m_PosY,
609                                 nextX - m_PosX - m_Width, m_Height);
610            }
611        }
612    }
613}
614
615
616wxString wxHtmlWordCell::ConvertToText(wxHtmlSelection *s) const
617{
618    if ( s && (this == s->GetFromCell() || this == s->GetToCell()) )
619    {
620        wxPoint priv = this == s->GetFromCell() ? s->GetFromPrivPos()
621                                                : s->GetToPrivPos();
622
623        // VZ: we may be called before we had a chance to re-render ourselves
624        //     and in this case GetFrom/ToPrivPos() is not set yet -- assume
625        //     that this only happens in case of a double/triple click (which
626        //     seems to be the case now) and so it makes sense to select the
627        //     entire contents of the cell in this case
628        //
629        // TODO: but this really needs to be fixed in some better way later...
630        if ( priv != wxDefaultPosition )
631        {
632            int part1 = priv.x;
633            int part2 = priv.y;
634            if ( part1 == part2 )
635                return wxEmptyString;
636            return m_Word.Mid(part1, part2-part1);
637        }
638        //else: return the whole word below
639    }
640
641    return m_Word;
642}
643
644wxCursor wxHtmlWordCell::GetMouseCursor(wxHtmlWindowInterface *window) const
645{
646    if ( !GetLink() )
647    {
648        return window->GetHTMLCursor(wxHtmlWindowInterface::HTMLCursor_Text);
649    }
650    else
651    {
652        return wxHtmlCell::GetMouseCursor(window);
653    }
654}
655
656
657//-----------------------------------------------------------------------------
658// wxHtmlContainerCell
659//-----------------------------------------------------------------------------
660
661IMPLEMENT_ABSTRACT_CLASS(wxHtmlContainerCell, wxHtmlCell)
662
663wxHtmlContainerCell::wxHtmlContainerCell(wxHtmlContainerCell *parent) : wxHtmlCell()
664{
665    m_Cells = m_LastCell = NULL;
666    m_Parent = parent;
667    m_MaxTotalWidth = 0;
668    if (m_Parent) m_Parent->InsertCell(this);
669    m_AlignHor = wxHTML_ALIGN_LEFT;
670    m_AlignVer = wxHTML_ALIGN_BOTTOM;
671    m_IndentLeft = m_IndentRight = m_IndentTop = m_IndentBottom = 0;
672    m_WidthFloat = 100; m_WidthFloatUnits = wxHTML_UNITS_PERCENT;
673    m_UseBkColour = false;
674    m_UseBorder = false;
675    m_MinHeight = 0;
676    m_MinHeightAlign = wxHTML_ALIGN_TOP;
677    m_LastLayout = -1;
678}
679
680wxHtmlContainerCell::~wxHtmlContainerCell()
681{
682    wxHtmlCell *cell = m_Cells;
683    while ( cell )
684    {
685        wxHtmlCell *cellNext = cell->GetNext();
686        delete cell;
687        cell = cellNext;
688    }
689}
690
691
692
693void wxHtmlContainerCell::SetIndent(int i, int what, int units)
694{
695    int val = (units == wxHTML_UNITS_PIXELS) ? i : -i;
696    if (what & wxHTML_INDENT_LEFT) m_IndentLeft = val;
697    if (what & wxHTML_INDENT_RIGHT) m_IndentRight = val;
698    if (what & wxHTML_INDENT_TOP) m_IndentTop = val;
699    if (what & wxHTML_INDENT_BOTTOM) m_IndentBottom = val;
700    m_LastLayout = -1;
701}
702
703
704
705int wxHtmlContainerCell::GetIndent(int ind) const
706{
707    if (ind & wxHTML_INDENT_LEFT) return m_IndentLeft;
708    else if (ind & wxHTML_INDENT_RIGHT) return m_IndentRight;
709    else if (ind & wxHTML_INDENT_TOP) return m_IndentTop;
710    else if (ind & wxHTML_INDENT_BOTTOM) return m_IndentBottom;
711    else return -1; /* BUG! Should not be called... */
712}
713
714
715
716
717int wxHtmlContainerCell::GetIndentUnits(int ind) const
718{
719    bool p = false;
720    if (ind & wxHTML_INDENT_LEFT) p = m_IndentLeft < 0;
721    else if (ind & wxHTML_INDENT_RIGHT) p = m_IndentRight < 0;
722    else if (ind & wxHTML_INDENT_TOP) p = m_IndentTop < 0;
723    else if (ind & wxHTML_INDENT_BOTTOM) p = m_IndentBottom < 0;
724    if (p) return wxHTML_UNITS_PERCENT;
725    else return wxHTML_UNITS_PIXELS;
726}
727
728
729bool wxHtmlContainerCell::AdjustPagebreak(int *pagebreak,
730                                          wxArrayInt& known_pagebreaks) const
731{
732    if (!m_CanLiveOnPagebreak)
733        return wxHtmlCell::AdjustPagebreak(pagebreak, known_pagebreaks);
734
735    wxHtmlCell *c = GetFirstChild();
736    bool rt = false;
737    int pbrk = *pagebreak - m_PosY;
738
739    while (c)
740    {
741        if (c->AdjustPagebreak(&pbrk, known_pagebreaks))
742            rt = true;
743        c = c->GetNext();
744    }
745    if (rt)
746        *pagebreak = pbrk + m_PosY;
747    return rt;
748}
749
750
751void wxHtmlContainerCell::Layout(int w)
752{
753    wxHtmlCell::Layout(w);
754
755    if (m_LastLayout == w)
756        return;
757    m_LastLayout = w;
758
759    // VS: Any attempt to layout with negative or zero width leads to hell,
760    // but we can't ignore such attempts completely, since it sometimes
761    // happen (e.g. when trying how small a table can be). The best thing we
762    // can do is to set the width of child cells to zero
763    if (w < 1)
764    {
765       m_Width = 0;
766       for (wxHtmlCell *cell = m_Cells; cell; cell = cell->GetNext())
767            cell->Layout(0);
768            // this does two things: it recursively calls this code on all
769            // child contrainers and resets children's position to (0,0)
770       return;
771    }
772
773    wxHtmlCell *nextCell;
774    long xpos = 0, ypos = m_IndentTop;
775    int xdelta = 0, ybasicpos = 0, ydiff;
776    int s_width, nextWordWidth, s_indent;
777    int ysizeup = 0, ysizedown = 0;
778    int MaxLineWidth = 0;
779    int curLineWidth = 0;
780    m_MaxTotalWidth = 0;
781
782
783    /*
784
785    WIDTH ADJUSTING :
786
787    */
788
789    if (m_WidthFloatUnits == wxHTML_UNITS_PERCENT)
790    {
791        if (m_WidthFloat < 0) m_Width = (100 + m_WidthFloat) * w / 100;
792        else m_Width = m_WidthFloat * w / 100;
793    }
794    else
795    {
796        if (m_WidthFloat < 0) m_Width = w + m_WidthFloat;
797        else m_Width = m_WidthFloat;
798    }
799
800    if (m_Cells)
801    {
802        int l = (m_IndentLeft < 0) ? (-m_IndentLeft * m_Width / 100) : m_IndentLeft;
803        int r = (m_IndentRight < 0) ? (-m_IndentRight * m_Width / 100) : m_IndentRight;
804        for (wxHtmlCell *cell = m_Cells; cell; cell = cell->GetNext())
805            cell->Layout(m_Width - (l + r));
806    }
807
808    /*
809
810    LAYOUTING :
811
812    */
813
814    // adjust indentation:
815    s_indent = (m_IndentLeft < 0) ? (-m_IndentLeft * m_Width / 100) : m_IndentLeft;
816    s_width = m_Width - s_indent - ((m_IndentRight < 0) ? (-m_IndentRight * m_Width / 100) : m_IndentRight);
817
818    // my own layouting:
819    wxHtmlCell *cell = m_Cells,
820               *line = m_Cells;
821    while (cell != NULL)
822    {
823        switch (m_AlignVer)
824        {
825            case wxHTML_ALIGN_TOP :      ybasicpos = 0; break;
826            case wxHTML_ALIGN_BOTTOM :   ybasicpos = - cell->GetHeight(); break;
827            case wxHTML_ALIGN_CENTER :   ybasicpos = - cell->GetHeight() / 2; break;
828        }
829        ydiff = cell->GetHeight() + ybasicpos;
830
831        if (cell->GetDescent() + ydiff > ysizedown) ysizedown = cell->GetDescent() + ydiff;
832        if (ybasicpos + cell->GetDescent() < -ysizeup) ysizeup = - (ybasicpos + cell->GetDescent());
833
834        // layout nonbreakable run of cells:
835        cell->SetPos(xpos, ybasicpos + cell->GetDescent());
836        xpos += cell->GetWidth();
837        if (!cell->IsTerminalCell())
838        {
839            // Container cell indicates new line
840            if (curLineWidth > m_MaxTotalWidth)
841                m_MaxTotalWidth = curLineWidth;
842
843            if (wxMax(cell->GetWidth(), cell->GetMaxTotalWidth()) > m_MaxTotalWidth)
844                m_MaxTotalWidth = cell->GetMaxTotalWidth();
845            curLineWidth = 0;
846        }
847        else
848            // Normal cell, add maximum cell width to line width
849            curLineWidth += cell->GetMaxTotalWidth();
850
851        cell = cell->GetNext();
852
853        // compute length of the next word that would be added:
854        nextWordWidth = 0;
855        if (cell)
856        {
857            nextCell = cell;
858            do
859            {
860                nextWordWidth += nextCell->GetWidth();
861                nextCell = nextCell->GetNext();
862            } while (nextCell && !nextCell->IsLinebreakAllowed());
863        }
864
865        // force new line if occurred:
866        if ((cell == NULL) ||
867            (xpos + nextWordWidth > s_width && cell->IsLinebreakAllowed()))
868        {
869            if (xpos > MaxLineWidth) MaxLineWidth = xpos;
870            if (ysizeup < 0) ysizeup = 0;
871            if (ysizedown < 0) ysizedown = 0;
872            switch (m_AlignHor) {
873                case wxHTML_ALIGN_LEFT :
874                case wxHTML_ALIGN_JUSTIFY :
875                         xdelta = 0;
876                         break;
877                case wxHTML_ALIGN_RIGHT :
878                         xdelta = 0 + (s_width - xpos);
879                         break;
880                case wxHTML_ALIGN_CENTER :
881                         xdelta = 0 + (s_width - xpos) / 2;
882                         break;
883            }
884            if (xdelta < 0) xdelta = 0;
885            xdelta += s_indent;
886
887            ypos += ysizeup;
888
889            if (m_AlignHor != wxHTML_ALIGN_JUSTIFY || cell == NULL)
890            {
891                while (line != cell)
892                {
893                    line->SetPos(line->GetPosX() + xdelta,
894                                   ypos + line->GetPosY());
895                    line = line->GetNext();
896                }
897            }
898            else // align == justify
899            {
900                // we have to distribute the extra horz space between the cells
901                // on this line
902
903                // an added complication is that some cells have fixed size and
904                // shouldn't get any increment (it so happens that these cells
905                // also don't allow line break on them which provides with an
906                // easy way to test for this) -- and neither should the cells
907                // adjacent to them as this could result in a visible space
908                // between two cells separated by, e.g. font change, cell which
909                // is wrong
910
911                int step = s_width - xpos;
912                if ( step > 0 )
913                {
914                    // first count the cells which will get extra space
915                    int total = -1;
916
917                    const wxHtmlCell *c;
918                    if ( line != cell )
919                    {
920                        for ( c = line; c != cell; c = c->GetNext() )
921                        {
922                            if ( c->IsLinebreakAllowed() )
923                            {
924                                total++;
925                            }
926                        }
927                    }
928
929                    // and now extra space to those cells which merit it
930                    if ( total )
931                    {
932                        // first visible cell on line is not moved:
933                        while (line !=cell && !line->IsLinebreakAllowed())
934                        {
935                            line->SetPos(line->GetPosX() + s_indent,
936                                         line->GetPosY() + ypos);
937                            line = line->GetNext();
938                        }
939
940                        if (line != cell)
941                        {
942                            line->SetPos(line->GetPosX() + s_indent,
943                                         line->GetPosY() + ypos);
944
945                            line = line->GetNext();
946                        }
947
948                        for ( int n = 0; line != cell; line = line->GetNext() )
949                        {
950                            if ( line->IsLinebreakAllowed() )
951                            {
952                                // offset the next cell relative to this one
953                                // thus increasing our size
954                                n++;
955                            }
956
957                            line->SetPos(line->GetPosX() + s_indent +
958                                           ((n * step) / total),
959                                           line->GetPosY() + ypos);
960                        }
961                    }
962                    else
963                    {
964                        // this will cause the code to enter "else branch" below:
965                        step = 0;
966                    }
967                }
968                // else branch:
969                if ( step <= 0 ) // no extra space to distribute
970                {
971                    // just set the indent properly
972                    while (line != cell)
973                    {
974                        line->SetPos(line->GetPosX() + s_indent,
975                                     line->GetPosY() + ypos);
976                        line = line->GetNext();
977                    }
978                }
979            }
980
981            ypos += ysizedown;
982            xpos = 0;
983            ysizeup = ysizedown = 0;
984            line = cell;
985        }
986    }
987
988    // setup height & width, depending on container layout:
989    m_Height = ypos + (ysizedown + ysizeup) + m_IndentBottom;
990
991    if (m_Height < m_MinHeight)
992    {
993        if (m_MinHeightAlign != wxHTML_ALIGN_TOP)
994        {
995            int diff = m_MinHeight - m_Height;
996            if (m_MinHeightAlign == wxHTML_ALIGN_CENTER) diff /= 2;
997            cell = m_Cells;
998            while (cell)
999            {
1000                cell->SetPos(cell->GetPosX(), cell->GetPosY() + diff);
1001                cell = cell->GetNext();
1002            }
1003        }
1004        m_Height = m_MinHeight;
1005    }
1006
1007    if (curLineWidth > m_MaxTotalWidth)
1008        m_MaxTotalWidth = curLineWidth;
1009
1010    m_MaxTotalWidth += s_indent + ((m_IndentRight < 0) ? (-m_IndentRight * m_Width / 100) : m_IndentRight);
1011    MaxLineWidth += s_indent + ((m_IndentRight < 0) ? (-m_IndentRight * m_Width / 100) : m_IndentRight);
1012    if (m_Width < MaxLineWidth) m_Width = MaxLineWidth;
1013}
1014
1015void wxHtmlContainerCell::UpdateRenderingStatePre(wxHtmlRenderingInfo& info,
1016                                                  wxHtmlCell *cell) const
1017{
1018    wxHtmlSelection *s = info.GetSelection();
1019    if (!s) return;
1020    if (s->GetFromCell() == cell || s->GetToCell() == cell)
1021    {
1022        info.GetState().SetSelectionState(wxHTML_SEL_CHANGING);
1023    }
1024}
1025
1026void wxHtmlContainerCell::UpdateRenderingStatePost(wxHtmlRenderingInfo& info,
1027                                                   wxHtmlCell *cell) const
1028{
1029    wxHtmlSelection *s = info.GetSelection();
1030    if (!s) return;
1031    if (s->GetToCell() == cell)
1032        info.GetState().SetSelectionState(wxHTML_SEL_OUT);
1033    else if (s->GetFromCell() == cell)
1034        info.GetState().SetSelectionState(wxHTML_SEL_IN);
1035}
1036
1037#define mMin(a, b) (((a) < (b)) ? (a) : (b))
1038#define mMax(a, b) (((a) < (b)) ? (b) : (a))
1039
1040void wxHtmlContainerCell::Draw(wxDC& dc, int x, int y, int view_y1, int view_y2,
1041                               wxHtmlRenderingInfo& info)
1042{
1043#if 0 // useful for debugging
1044    dc.SetPen(*wxRED_PEN);
1045    dc.DrawRectangle(x+m_PosX,y+m_PosY,m_Width,m_Height);
1046#endif
1047
1048    int xlocal = x + m_PosX;
1049    int ylocal = y + m_PosY;
1050
1051    if (m_UseBkColour)
1052    {
1053        wxBrush myb = wxBrush(m_BkColour, wxSOLID);
1054
1055        int real_y1 = mMax(ylocal, view_y1);
1056        int real_y2 = mMin(ylocal + m_Height - 1, view_y2);
1057
1058        dc.SetBrush(myb);
1059        dc.SetPen(*wxTRANSPARENT_PEN);
1060        dc.DrawRectangle(xlocal, real_y1, m_Width, real_y2 - real_y1 + 1);
1061    }
1062
1063    if (m_UseBorder)
1064    {
1065        wxPen mypen1(m_BorderColour1, 1, wxSOLID);
1066        wxPen mypen2(m_BorderColour2, 1, wxSOLID);
1067
1068        dc.SetPen(mypen1);
1069        dc.DrawLine(xlocal, ylocal, xlocal, ylocal + m_Height - 1);
1070        dc.DrawLine(xlocal, ylocal, xlocal + m_Width, ylocal);
1071        dc.SetPen(mypen2);
1072        dc.DrawLine(xlocal + m_Width - 1, ylocal, xlocal +  m_Width - 1, ylocal + m_Height - 1);
1073        dc.DrawLine(xlocal, ylocal + m_Height - 1, xlocal + m_Width, ylocal + m_Height - 1);
1074    }
1075
1076    if (m_Cells)
1077    {
1078        // draw container's contents:
1079        for (wxHtmlCell *cell = m_Cells; cell; cell = cell->GetNext())
1080        {
1081
1082            // optimize drawing: don't render off-screen content:
1083            if ((ylocal + cell->GetPosY() <= view_y2) &&
1084                (ylocal + cell->GetPosY() + cell->GetHeight() > view_y1))
1085            {
1086                // the cell is visible, draw it:
1087                UpdateRenderingStatePre(info, cell);
1088                cell->Draw(dc,
1089                           xlocal, ylocal, view_y1, view_y2,
1090                           info);
1091                UpdateRenderingStatePost(info, cell);
1092            }
1093            else
1094            {
1095                // the cell is off-screen, proceed with font+color+etc.
1096                // changes only:
1097                cell->DrawInvisible(dc, xlocal, ylocal, info);
1098            }
1099        }
1100    }
1101}
1102
1103
1104
1105void wxHtmlContainerCell::DrawInvisible(wxDC& dc, int x, int y,
1106                                        wxHtmlRenderingInfo& info)
1107{
1108    if (m_Cells)
1109    {
1110        for (wxHtmlCell *cell = m_Cells; cell; cell = cell->GetNext())
1111        {
1112            UpdateRenderingStatePre(info, cell);
1113            cell->DrawInvisible(dc, x + m_PosX, y + m_PosY, info);
1114            UpdateRenderingStatePost(info, cell);
1115        }
1116    }
1117}
1118
1119
1120wxColour wxHtmlContainerCell::GetBackgroundColour()
1121{
1122    if (m_UseBkColour)
1123        return m_BkColour;
1124    else
1125        return wxNullColour;
1126}
1127
1128
1129
1130wxHtmlLinkInfo *wxHtmlContainerCell::GetLink(int x, int y) const
1131{
1132    wxHtmlCell *cell = FindCellByPos(x, y);
1133
1134    // VZ: I don't know if we should pass absolute or relative coords to
1135    //     wxHtmlCell::GetLink()? As the base class version just ignores them
1136    //     anyhow, it hardly matters right now but should still be clarified
1137    return cell ? cell->GetLink(x, y) : NULL;
1138}
1139
1140
1141
1142void wxHtmlContainerCell::InsertCell(wxHtmlCell *f)
1143{
1144    if (!m_Cells) m_Cells = m_LastCell = f;
1145    else
1146    {
1147        m_LastCell->SetNext(f);
1148        m_LastCell = f;
1149        if (m_LastCell) while (m_LastCell->GetNext()) m_LastCell = m_LastCell->GetNext();
1150    }
1151    f->SetParent(this);
1152    m_LastLayout = -1;
1153}
1154
1155
1156
1157void wxHtmlContainerCell::SetAlign(const wxHtmlTag& tag)
1158{
1159    if (tag.HasParam(wxT("ALIGN")))
1160    {
1161        wxString alg = tag.GetParam(wxT("ALIGN"));
1162        alg.MakeUpper();
1163        if (alg == wxT("CENTER"))
1164            SetAlignHor(wxHTML_ALIGN_CENTER);
1165        else if (alg == wxT("LEFT"))
1166            SetAlignHor(wxHTML_ALIGN_LEFT);
1167        else if (alg == wxT("JUSTIFY"))
1168            SetAlignHor(wxHTML_ALIGN_JUSTIFY);
1169        else if (alg == wxT("RIGHT"))
1170            SetAlignHor(wxHTML_ALIGN_RIGHT);
1171        m_LastLayout = -1;
1172    }
1173}
1174
1175
1176
1177void wxHtmlContainerCell::SetWidthFloat(const wxHtmlTag& tag, double pixel_scale)
1178{
1179    if (tag.HasParam(wxT("WIDTH")))
1180    {
1181        int wdi;
1182        wxString wd = tag.GetParam(wxT("WIDTH"));
1183
1184        if (wd[wd.length()-1] == wxT('%'))
1185        {
1186            wxSscanf(wd.c_str(), wxT("%i%%"), &wdi);
1187            SetWidthFloat(wdi, wxHTML_UNITS_PERCENT);
1188        }
1189        else
1190        {
1191            wxSscanf(wd.c_str(), wxT("%i"), &wdi);
1192            SetWidthFloat((int)(pixel_scale * (double)wdi), wxHTML_UNITS_PIXELS);
1193        }
1194        m_LastLayout = -1;
1195    }
1196}
1197
1198
1199
1200const wxHtmlCell* wxHtmlContainerCell::Find(int condition, const void* param) const
1201{
1202    if (m_Cells)
1203    {
1204        for (wxHtmlCell *cell = m_Cells; cell; cell = cell->GetNext())
1205        {
1206            const wxHtmlCell *r = cell->Find(condition, param);
1207            if (r) return r;
1208        }
1209    }
1210    return NULL;
1211}
1212
1213
1214wxHtmlCell *wxHtmlContainerCell::FindCellByPos(wxCoord x, wxCoord y,
1215                                               unsigned flags) const
1216{
1217    if ( flags & wxHTML_FIND_EXACT )
1218    {
1219        for ( const wxHtmlCell *cell = m_Cells; cell; cell = cell->GetNext() )
1220        {
1221            int cx = cell->GetPosX(),
1222                cy = cell->GetPosY();
1223
1224            if ( (cx <= x) && (cx + cell->GetWidth() > x) &&
1225                 (cy <= y) && (cy + cell->GetHeight() > y) )
1226            {
1227                return cell->FindCellByPos(x - cx, y - cy, flags);
1228            }
1229        }
1230    }
1231    else if ( flags & wxHTML_FIND_NEAREST_AFTER )
1232    {
1233        wxHtmlCell *c;
1234        for ( const wxHtmlCell *cell = m_Cells; cell; cell = cell->GetNext() )
1235        {
1236            if ( cell->IsFormattingCell() )
1237                continue;
1238            int cellY = cell->GetPosY();
1239            if (!( y < cellY || (y < cellY + cell->GetHeight() &&
1240                                 x < cell->GetPosX() + cell->GetWidth()) ))
1241                continue;
1242
1243            c = cell->FindCellByPos(x - cell->GetPosX(), y - cellY, flags);
1244            if (c) return c;
1245        }
1246    }
1247    else if ( flags & wxHTML_FIND_NEAREST_BEFORE )
1248    {
1249        wxHtmlCell *c2, *c = NULL;
1250        for ( const wxHtmlCell *cell = m_Cells; cell; cell = cell->GetNext() )
1251        {
1252            if ( cell->IsFormattingCell() )
1253                continue;
1254            int cellY = cell->GetPosY();
1255            if (!( cellY + cell->GetHeight() <= y ||
1256                   (y >= cellY && x >= cell->GetPosX()) ))
1257                break;
1258            c2 = cell->FindCellByPos(x - cell->GetPosX(), y - cellY, flags);
1259            if (c2)
1260                c = c2;
1261        }
1262        if (c) return c;
1263    }
1264
1265    return NULL;
1266}
1267
1268
1269bool wxHtmlContainerCell::ProcessMouseClick(wxHtmlWindowInterface *window,
1270                                            const wxPoint& pos,
1271                                            const wxMouseEvent& event)
1272{
1273#if WXWIN_COMPATIBILITY_2_6
1274    wxHtmlCellOnMouseClickCompatHelper compat(window, pos, event);
1275    return compat.CallOnMouseClick(this);
1276}
1277
1278void wxHtmlContainerCell::OnMouseClick(wxWindow*,
1279                                       int, int, const wxMouseEvent& event)
1280{
1281    wxCHECK_RET( gs_helperOnMouseClick, _T("unexpected call to OnMouseClick") );
1282    wxHtmlWindowInterface *window = gs_helperOnMouseClick->window;
1283    const wxPoint& pos = gs_helperOnMouseClick->pos;
1284#endif // WXWIN_COMPATIBILITY_2_6
1285
1286    bool retval = false;
1287    wxHtmlCell *cell = FindCellByPos(pos.x, pos.y);
1288    if ( cell )
1289        retval = cell->ProcessMouseClick(window, pos, event);
1290
1291#if WXWIN_COMPATIBILITY_2_6
1292    gs_helperOnMouseClick->retval = retval;
1293#else
1294    return retval;
1295#endif // WXWIN_COMPATIBILITY_2_6
1296}
1297
1298
1299wxHtmlCell *wxHtmlContainerCell::GetFirstTerminal() const
1300{
1301    if ( m_Cells )
1302    {
1303        wxHtmlCell *c2;
1304        for (wxHtmlCell *c = m_Cells; c; c = c->GetNext())
1305        {
1306            c2 = c->GetFirstTerminal();
1307            if ( c2 )
1308                return c2;
1309        }
1310    }
1311    return NULL;
1312}
1313
1314wxHtmlCell *wxHtmlContainerCell::GetLastTerminal() const
1315{
1316    if ( m_Cells )
1317    {
1318        // most common case first:
1319        wxHtmlCell *c = m_LastCell->GetLastTerminal();
1320        if ( c )
1321            return c;
1322
1323        wxHtmlCell *ctmp;
1324        wxHtmlCell *c2 = NULL;
1325        for (c = m_Cells; c; c = c->GetNext())
1326        {
1327            ctmp = c->GetLastTerminal();
1328            if ( ctmp )
1329                c2 = ctmp;
1330        }
1331        return c2;
1332    }
1333    else
1334        return NULL;
1335}
1336
1337
1338static bool IsEmptyContainer(wxHtmlContainerCell *cell)
1339{
1340    for ( wxHtmlCell *c = cell->GetFirstChild(); c; c = c->GetNext() )
1341    {
1342        if ( !c->IsTerminalCell() || !c->IsFormattingCell() )
1343            return false;
1344    }
1345    return true;
1346}
1347
1348void wxHtmlContainerCell::RemoveExtraSpacing(bool top, bool bottom)
1349{
1350    if ( top )
1351        SetIndent(0, wxHTML_INDENT_TOP);
1352    if ( bottom )
1353        SetIndent(0, wxHTML_INDENT_BOTTOM);
1354
1355    if ( m_Cells )
1356    {
1357        wxHtmlCell *c;
1358        wxHtmlContainerCell *cont;
1359        if ( top )
1360        {
1361            for ( c = m_Cells; c; c = c->GetNext() )
1362            {
1363                if ( c->IsTerminalCell() )
1364                {
1365                    if ( !c->IsFormattingCell() )
1366                        break;
1367                }
1368                else
1369                {
1370                    cont = (wxHtmlContainerCell*)c;
1371                    if ( IsEmptyContainer(cont) )
1372                    {
1373                        cont->SetIndent(0, wxHTML_INDENT_VERTICAL);
1374                    }
1375                    else
1376                    {
1377                        cont->RemoveExtraSpacing(true, false);
1378                        break;
1379                    }
1380                }
1381            }
1382        }
1383
1384        if ( bottom )
1385        {
1386            wxArrayPtrVoid arr;
1387            for ( c = m_Cells; c; c = c->GetNext() )
1388                arr.Add((void*)c);
1389
1390            for ( int i = arr.GetCount() - 1; i >= 0; i--)
1391            {
1392                c = (wxHtmlCell*)arr[i];
1393                if ( c->IsTerminalCell() )
1394                {
1395                    if ( !c->IsFormattingCell() )
1396                        break;
1397                }
1398                else
1399                {
1400                    cont = (wxHtmlContainerCell*)c;
1401                    if ( IsEmptyContainer(cont) )
1402                    {
1403                        cont->SetIndent(0, wxHTML_INDENT_VERTICAL);
1404                    }
1405                    else
1406                    {
1407                        cont->RemoveExtraSpacing(false, true);
1408                        break;
1409                    }
1410                }
1411            }
1412        }
1413    }
1414}
1415
1416
1417
1418
1419// --------------------------------------------------------------------------
1420// wxHtmlColourCell
1421// --------------------------------------------------------------------------
1422
1423IMPLEMENT_ABSTRACT_CLASS(wxHtmlColourCell, wxHtmlCell)
1424
1425void wxHtmlColourCell::Draw(wxDC& dc,
1426                            int x, int y,
1427                            int WXUNUSED(view_y1), int WXUNUSED(view_y2),
1428                            wxHtmlRenderingInfo& info)
1429{
1430    DrawInvisible(dc, x, y, info);
1431}
1432
1433void wxHtmlColourCell::DrawInvisible(wxDC& dc,
1434                                     int WXUNUSED(x), int WXUNUSED(y),
1435                                     wxHtmlRenderingInfo& info)
1436{
1437    wxHtmlRenderingState& state = info.GetState();
1438    if (m_Flags & wxHTML_CLR_FOREGROUND)
1439    {
1440        state.SetFgColour(m_Colour);
1441        if (state.GetSelectionState() != wxHTML_SEL_IN)
1442            dc.SetTextForeground(m_Colour);
1443        else
1444            dc.SetTextForeground(
1445                    info.GetStyle().GetSelectedTextColour(m_Colour));
1446    }
1447    if (m_Flags & wxHTML_CLR_BACKGROUND)
1448    {
1449        state.SetBgColour(m_Colour);
1450        if (state.GetSelectionState() != wxHTML_SEL_IN)
1451        {
1452            dc.SetTextBackground(m_Colour);
1453            dc.SetBackground(wxBrush(m_Colour, wxSOLID));
1454        }
1455        else
1456        {
1457            wxColour c = info.GetStyle().GetSelectedTextBgColour(m_Colour);
1458            dc.SetTextBackground(c);
1459            dc.SetBackground(wxBrush(c, wxSOLID));
1460        }
1461    }
1462}
1463
1464
1465
1466
1467// ---------------------------------------------------------------------------
1468// wxHtmlFontCell
1469// ---------------------------------------------------------------------------
1470
1471IMPLEMENT_ABSTRACT_CLASS(wxHtmlFontCell, wxHtmlCell)
1472
1473void wxHtmlFontCell::Draw(wxDC& dc,
1474                          int WXUNUSED(x), int WXUNUSED(y),
1475                          int WXUNUSED(view_y1), int WXUNUSED(view_y2),
1476                          wxHtmlRenderingInfo& WXUNUSED(info))
1477{
1478    dc.SetFont(m_Font);
1479}
1480
1481void wxHtmlFontCell::DrawInvisible(wxDC& dc, int WXUNUSED(x), int WXUNUSED(y),
1482                                   wxHtmlRenderingInfo& WXUNUSED(info))
1483{
1484    dc.SetFont(m_Font);
1485}
1486
1487
1488
1489
1490
1491
1492
1493
1494// ---------------------------------------------------------------------------
1495// wxHtmlWidgetCell
1496// ---------------------------------------------------------------------------
1497
1498IMPLEMENT_ABSTRACT_CLASS(wxHtmlWidgetCell, wxHtmlCell)
1499
1500wxHtmlWidgetCell::wxHtmlWidgetCell(wxWindow *wnd, int w)
1501{
1502    int sx, sy;
1503    m_Wnd = wnd;
1504    m_Wnd->GetSize(&sx, &sy);
1505    m_Width = sx, m_Height = sy;
1506    m_WidthFloat = w;
1507}
1508
1509
1510void wxHtmlWidgetCell::Draw(wxDC& WXUNUSED(dc),
1511                            int WXUNUSED(x), int WXUNUSED(y),
1512                            int WXUNUSED(view_y1), int WXUNUSED(view_y2),
1513                            wxHtmlRenderingInfo& WXUNUSED(info))
1514{
1515    int absx = 0, absy = 0, stx, sty;
1516    wxHtmlCell *c = this;
1517
1518    while (c)
1519    {
1520        absx += c->GetPosX();
1521        absy += c->GetPosY();
1522        c = c->GetParent();
1523    }
1524
1525    wxScrolledWindow *scrolwin =
1526        wxDynamicCast(m_Wnd->GetParent(), wxScrolledWindow);
1527    wxCHECK_RET( scrolwin,
1528                 _T("widget cells can only be placed in wxHtmlWindow") );
1529
1530    scrolwin->GetViewStart(&stx, &sty);
1531    m_Wnd->SetSize(absx - wxHTML_SCROLL_STEP * stx,
1532                   absy  - wxHTML_SCROLL_STEP * sty,
1533                   m_Width, m_Height);
1534}
1535
1536
1537
1538void wxHtmlWidgetCell::DrawInvisible(wxDC& WXUNUSED(dc),
1539                                     int WXUNUSED(x), int WXUNUSED(y),
1540                                     wxHtmlRenderingInfo& WXUNUSED(info))
1541{
1542    int absx = 0, absy = 0, stx, sty;
1543    wxHtmlCell *c = this;
1544
1545    while (c)
1546    {
1547        absx += c->GetPosX();
1548        absy += c->GetPosY();
1549        c = c->GetParent();
1550    }
1551
1552    ((wxScrolledWindow*)(m_Wnd->GetParent()))->GetViewStart(&stx, &sty);
1553    m_Wnd->SetSize(absx - wxHTML_SCROLL_STEP * stx, absy  - wxHTML_SCROLL_STEP * sty, m_Width, m_Height);
1554}
1555
1556
1557
1558void wxHtmlWidgetCell::Layout(int w)
1559{
1560    if (m_WidthFloat != 0)
1561    {
1562        m_Width = (w * m_WidthFloat) / 100;
1563        m_Wnd->SetSize(m_Width, m_Height);
1564    }
1565
1566    wxHtmlCell::Layout(w);
1567}
1568
1569
1570
1571// ----------------------------------------------------------------------------
1572// wxHtmlTerminalCellsInterator
1573// ----------------------------------------------------------------------------
1574
1575const wxHtmlCell* wxHtmlTerminalCellsInterator::operator++()
1576{
1577    if ( !m_pos )
1578        return NULL;
1579
1580    do
1581    {
1582        if ( m_pos == m_to )
1583        {
1584            m_pos = NULL;
1585            return NULL;
1586        }
1587
1588        if ( m_pos->GetNext() )
1589            m_pos = m_pos->GetNext();
1590        else
1591        {
1592            // we must go up the hierarchy until we reach container where this
1593            // is not the last child, and then go down to first terminal cell:
1594            while ( m_pos->GetNext() == NULL )
1595            {
1596                m_pos = m_pos->GetParent();
1597                if ( !m_pos )
1598                    return NULL;
1599            }
1600            m_pos = m_pos->GetNext();
1601        }
1602        while ( m_pos->GetFirstChild() != NULL )
1603            m_pos = m_pos->GetFirstChild();
1604    } while ( !m_pos->IsTerminalCell() );
1605
1606    return m_pos;
1607}
1608
1609#endif
1610