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