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