1. added abstract interface to wxHtmlWindow for use by wxHtmlWinParser and implemente...
[wxWidgets.git] / src / generic / htmllbox.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: generic/htmllbox.cpp
3 // Purpose: implementation of wxHtmlListBox
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 31.05.03
7 // RCS-ID: $Id$
8 // Copyright: (c) 2003 Vadim Zeitlin <vadim@wxwindows.org>
9 // License: wxWindows license
10 ///////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
22
23 #ifdef __BORLANDC__
24 #pragma hdrstop
25 #endif
26
27 #ifndef WX_PRECOMP
28 #include "wx/dcclient.h"
29 #endif //WX_PRECOMP
30
31 #if wxUSE_HTML
32
33 #include "wx/htmllbox.h"
34
35 #include "wx/html/htmlcell.h"
36 #include "wx/html/winpars.h"
37
38 // this hack forces the linker to always link in m_* files
39 #include "wx/html/forcelnk.h"
40 FORCE_WXHTML_MODULES()
41
42 // ----------------------------------------------------------------------------
43 // constants
44 // ----------------------------------------------------------------------------
45
46 // small border always added to the cells:
47 static const wxCoord CELL_BORDER = 2;
48
49 // ============================================================================
50 // private classes
51 // ============================================================================
52
53 // ----------------------------------------------------------------------------
54 // wxHtmlListBoxCache
55 // ----------------------------------------------------------------------------
56
57 // this class is used by wxHtmlListBox to cache the parsed representation of
58 // the items to avoid doing it anew each time an item must be drawn
59 class wxHtmlListBoxCache
60 {
61 private:
62 // invalidate a single item, used by Clear() and InvalidateRange()
63 void InvalidateItem(size_t n)
64 {
65 m_items[n] = (size_t)-1;
66 delete m_cells[n];
67 m_cells[n] = NULL;
68 }
69
70 public:
71 wxHtmlListBoxCache()
72 {
73 for ( size_t n = 0; n < SIZE; n++ )
74 {
75 m_items[n] = (size_t)-1;
76 m_cells[n] = NULL;
77 }
78
79 m_next = 0;
80 }
81
82 ~wxHtmlListBoxCache()
83 {
84 for ( size_t n = 0; n < SIZE; n++ )
85 {
86 delete m_cells[n];
87 }
88 }
89
90 // completely invalidate the cache
91 void Clear()
92 {
93 for ( size_t n = 0; n < SIZE; n++ )
94 {
95 InvalidateItem(n);
96 }
97 }
98
99 // return the cached cell for this index or NULL if none
100 wxHtmlCell *Get(size_t item) const
101 {
102 for ( size_t n = 0; n < SIZE; n++ )
103 {
104 if ( m_items[n] == item )
105 return m_cells[n];
106 }
107
108 return NULL;
109 }
110
111 // returns true if we already have this item cached
112 bool Has(size_t item) const { return Get(item) != NULL; }
113
114 // ensure that the item is cached
115 void Store(size_t item, wxHtmlCell *cell)
116 {
117 delete m_cells[m_next];
118 m_cells[m_next] = cell;
119 m_items[m_next] = item;
120
121 // advance to the next item wrapping around if there are no more
122 if ( ++m_next == SIZE )
123 m_next = 0;
124 }
125
126 // forget the cached value of the item(s) between the given ones (inclusive)
127 void InvalidateRange(size_t from, size_t to)
128 {
129 for ( size_t n = 0; n < SIZE; n++ )
130 {
131 if ( m_items[n] >= from && m_items[n] <= to )
132 {
133 InvalidateItem(n);
134 }
135 }
136 }
137
138 private:
139 // the max number of the items we cache
140 enum { SIZE = 50 };
141
142 // the index of the LRU (oldest) cell
143 size_t m_next;
144
145 // the parsed representation of the cached item or NULL
146 wxHtmlCell *m_cells[SIZE];
147
148 // the index of the currently cached item (only valid if m_cells != NULL)
149 size_t m_items[SIZE];
150 };
151
152 // ----------------------------------------------------------------------------
153 // wxHtmlListBoxStyle
154 // ----------------------------------------------------------------------------
155
156 // just forward wxDefaultHtmlRenderingStyle callbacks to the main class so that
157 // they could be overridden by the user code
158 class wxHtmlListBoxStyle : public wxDefaultHtmlRenderingStyle
159 {
160 public:
161 wxHtmlListBoxStyle(const wxHtmlListBox& hlbox) : m_hlbox(hlbox) { }
162
163 virtual wxColour GetSelectedTextColour(const wxColour& colFg)
164 {
165 return m_hlbox.GetSelectedTextColour(colFg);
166 }
167
168 virtual wxColour GetSelectedTextBgColour(const wxColour& colBg)
169 {
170 return m_hlbox.GetSelectedTextBgColour(colBg);
171 }
172
173 private:
174 const wxHtmlListBox& m_hlbox;
175
176 DECLARE_NO_COPY_CLASS(wxHtmlListBoxStyle)
177 };
178
179 // ----------------------------------------------------------------------------
180 // event tables
181 // ----------------------------------------------------------------------------
182
183 BEGIN_EVENT_TABLE(wxHtmlListBox, wxVListBox)
184 EVT_SIZE(wxHtmlListBox::OnSize)
185 EVT_MOTION(wxHtmlListBox::OnMouseMove)
186 EVT_LEFT_DOWN(wxHtmlListBox::OnLeftDown)
187 END_EVENT_TABLE()
188
189 // ============================================================================
190 // implementation
191 // ============================================================================
192
193 IMPLEMENT_ABSTRACT_CLASS(wxHtmlListBox, wxVListBox)
194
195
196 // ----------------------------------------------------------------------------
197 // wxHtmlListBox creation
198 // ----------------------------------------------------------------------------
199
200 wxHtmlListBox::wxHtmlListBox()
201 : wxHtmlWindowMouseHelper(this)
202 {
203 Init();
204 }
205
206 // normal constructor which calls Create() internally
207 wxHtmlListBox::wxHtmlListBox(wxWindow *parent,
208 wxWindowID id,
209 const wxPoint& pos,
210 const wxSize& size,
211 long style,
212 const wxString& name)
213 : wxHtmlWindowMouseHelper(this)
214 {
215 Init();
216
217 (void)Create(parent, id, pos, size, style, name);
218 }
219
220 void wxHtmlListBox::Init()
221 {
222 m_htmlParser = NULL;
223 m_htmlRendStyle = new wxHtmlListBoxStyle(*this);
224 m_cache = new wxHtmlListBoxCache;
225 }
226
227 bool wxHtmlListBox::Create(wxWindow *parent,
228 wxWindowID id,
229 const wxPoint& pos,
230 const wxSize& size,
231 long style,
232 const wxString& name)
233 {
234 return wxVListBox::Create(parent, id, pos, size, style, name);
235 }
236
237 wxHtmlListBox::~wxHtmlListBox()
238 {
239 delete m_cache;
240
241 if ( m_htmlParser )
242 {
243 delete m_htmlParser->GetDC();
244 delete m_htmlParser;
245 }
246
247 delete m_htmlRendStyle;
248 }
249
250 // ----------------------------------------------------------------------------
251 // wxHtmlListBox appearance
252 // ----------------------------------------------------------------------------
253
254 wxColour wxHtmlListBox::GetSelectedTextColour(const wxColour& colFg) const
255 {
256 return m_htmlRendStyle->
257 wxDefaultHtmlRenderingStyle::GetSelectedTextColour(colFg);
258 }
259
260 wxColour
261 wxHtmlListBox::GetSelectedTextBgColour(const wxColour& WXUNUSED(colBg)) const
262 {
263 return GetSelectionBackground();
264 }
265
266 // ----------------------------------------------------------------------------
267 // wxHtmlListBox items markup
268 // ----------------------------------------------------------------------------
269
270 wxString wxHtmlListBox::OnGetItemMarkup(size_t n) const
271 {
272 // we don't even need to wrap the value returned by OnGetItem() inside
273 // "<html><body>" and "</body></html>" because wxHTML can parse it even
274 // without these tags
275 return OnGetItem(n);
276 }
277
278 // ----------------------------------------------------------------------------
279 // wxHtmlListBox cache handling
280 // ----------------------------------------------------------------------------
281
282 void wxHtmlListBox::CacheItem(size_t n) const
283 {
284 if ( !m_cache->Has(n) )
285 {
286 if ( !m_htmlParser )
287 {
288 wxHtmlListBox *self = wxConstCast(this, wxHtmlListBox);
289
290 self->m_htmlParser = new wxHtmlWinParser(self);
291 m_htmlParser->SetDC(new wxClientDC(self));
292 m_htmlParser->SetFS(&self->m_filesystem);
293
294 // use system's default GUI font by default:
295 m_htmlParser->SetStandardFonts();
296 }
297
298 wxHtmlContainerCell *cell = (wxHtmlContainerCell *)m_htmlParser->
299 Parse(OnGetItemMarkup(n));
300 wxCHECK_RET( cell, _T("wxHtmlParser::Parse() returned NULL?") );
301
302 // set the cell's ID to item's index so that CellCoordsToPhysical()
303 // can quickly find the item:
304 cell->SetId(wxString::Format(_T("%u"), n));
305
306 cell->Layout(GetClientSize().x - 2*GetMargins().x);
307
308 m_cache->Store(n, cell);
309 }
310 }
311
312 void wxHtmlListBox::OnSize(wxSizeEvent& event)
313 {
314 // we need to relayout all the cached cells
315 m_cache->Clear();
316
317 event.Skip();
318 }
319
320 void wxHtmlListBox::RefreshLine(size_t line)
321 {
322 m_cache->InvalidateRange(line, line);
323
324 wxVListBox::RefreshLine(line);
325 }
326
327 void wxHtmlListBox::RefreshLines(size_t from, size_t to)
328 {
329 m_cache->InvalidateRange(from, to);
330
331 wxVListBox::RefreshLines(from, to);
332 }
333
334 void wxHtmlListBox::RefreshAll()
335 {
336 m_cache->Clear();
337
338 wxVListBox::RefreshAll();
339 }
340
341 void wxHtmlListBox::SetItemCount(size_t count)
342 {
343 // the items are going to change, forget the old ones
344 m_cache->Clear();
345
346 wxVListBox::SetItemCount(count);
347 }
348
349 // ----------------------------------------------------------------------------
350 // wxHtmlListBox implementation of wxVListBox pure virtuals
351 // ----------------------------------------------------------------------------
352
353 void wxHtmlListBox::OnDrawItem(wxDC& dc, const wxRect& rect, size_t n) const
354 {
355 CacheItem(n);
356
357 wxHtmlCell *cell = m_cache->Get(n);
358 wxCHECK_RET( cell, _T("this cell should be cached!") );
359
360 wxHtmlRenderingInfo htmlRendInfo;
361
362 // draw the selected cell in selected state
363 if ( IsSelected(n) )
364 {
365 wxHtmlSelection htmlSel;
366 htmlSel.Set(wxPoint(0,0), cell, wxPoint(INT_MAX, INT_MAX), cell);
367 htmlRendInfo.SetSelection(&htmlSel);
368 if ( m_htmlRendStyle )
369 htmlRendInfo.SetStyle(m_htmlRendStyle);
370 htmlRendInfo.GetState().SetSelectionState(wxHTML_SEL_IN);
371 }
372
373 // note that we can't stop drawing exactly at the window boundary as then
374 // even the visible cells part could be not drawn, so always draw the
375 // entire cell
376 cell->Draw(dc,
377 rect.x + CELL_BORDER, rect.y + CELL_BORDER,
378 0, INT_MAX, htmlRendInfo);
379 }
380
381 wxCoord wxHtmlListBox::OnMeasureItem(size_t n) const
382 {
383 CacheItem(n);
384
385 wxHtmlCell *cell = m_cache->Get(n);
386 wxCHECK_MSG( cell, 0, _T("this cell should be cached!") );
387
388 return cell->GetHeight() + cell->GetDescent() + 4;
389 }
390
391 // ----------------------------------------------------------------------------
392 // wxHtmlListBox implementation of wxHtmlListBoxWinInterface
393 // ----------------------------------------------------------------------------
394
395 void wxHtmlListBox::SetHTMLWindowTitle(const wxString& WXUNUSED(title))
396 {
397 // nothing to do
398 }
399
400 void wxHtmlListBox::OnHTMLLinkClicked(const wxHtmlLinkInfo& link)
401 {
402 OnLinkClicked(GetItemForCell(link.GetHtmlCell()), link);
403 }
404
405 wxHtmlOpeningStatus
406 wxHtmlListBox::OnHTMLOpeningURL(wxHtmlURLType WXUNUSED(type),
407 const wxString& WXUNUSED(url),
408 wxString *WXUNUSED(redirect)) const
409 {
410 return wxHTML_OPEN;
411 }
412
413 wxPoint wxHtmlListBox::HTMLCoordsToWindow(wxHtmlCell *cell,
414 const wxPoint& pos) const
415 {
416 return CellCoordsToPhysical(pos, cell);
417 }
418
419 wxWindow* wxHtmlListBox::GetHTMLWindow() { return this; }
420
421 wxColour wxHtmlListBox::GetHTMLBackgroundColour() const
422 {
423 return GetBackgroundColour();
424 }
425
426 void wxHtmlListBox::SetHTMLBackgroundColour(const wxColour& WXUNUSED(clr))
427 {
428 // nothing to do
429 }
430
431 void wxHtmlListBox::SetHTMLBackgroundImage(const wxBitmap& WXUNUSED(bmpBg))
432 {
433 // nothing to do
434 }
435
436 void wxHtmlListBox::SetHTMLStatusText(const wxString& WXUNUSED(text))
437 {
438 // nothing to do
439 }
440
441 // ----------------------------------------------------------------------------
442 // wxHtmlListBox handling of HTML links
443 // ----------------------------------------------------------------------------
444
445 wxPoint wxHtmlListBox::GetRootCellCoords(size_t n) const
446 {
447 wxPoint pos(CELL_BORDER, CELL_BORDER);
448 pos += GetMargins();
449 pos.y += GetLinesHeight(GetFirstVisibleLine(), n);
450 return pos;
451 }
452
453 bool wxHtmlListBox::PhysicalCoordsToCell(wxPoint& pos, wxHtmlCell*& cell) const
454 {
455 int n = HitTest(pos);
456 if ( n == wxNOT_FOUND )
457 return false;
458
459 // convert mouse coordinates to coords relative to item's wxHtmlCell:
460 pos -= GetRootCellCoords(n);
461
462 CacheItem(n);
463 cell = m_cache->Get(n);
464
465 return true;
466 }
467
468 size_t wxHtmlListBox::GetItemForCell(const wxHtmlCell *cell) const
469 {
470 wxCHECK_MSG( cell, 0, _T("no cell") );
471
472 cell = cell->GetRootCell();
473
474 wxCHECK_MSG( cell, 0, _T("no root cell") );
475
476 // the cell's ID contains item index, see CacheItem():
477 unsigned long n;
478 if ( !cell->GetId().ToULong(&n) )
479 {
480 wxFAIL_MSG( _T("unexpected root cell's ID") );
481 return 0;
482 }
483
484 return n;
485 }
486
487 wxPoint
488 wxHtmlListBox::CellCoordsToPhysical(const wxPoint& pos, wxHtmlCell *cell) const
489 {
490 return pos + GetRootCellCoords(GetItemForCell(cell));
491 }
492
493 void wxHtmlListBox::OnInternalIdle()
494 {
495 wxVListBox::OnInternalIdle();
496
497 if ( wxHtmlWindowMouseHelper::DidMouseMove() )
498 {
499 wxPoint pos = ScreenToClient(wxGetMousePosition());
500 wxHtmlCell *cell;
501
502 if ( !PhysicalCoordsToCell(pos, cell) )
503 return;
504
505 wxHtmlWindowMouseHelper::HandleIdle(cell, pos);
506 }
507 }
508
509 void wxHtmlListBox::OnMouseMove(wxMouseEvent& event)
510 {
511 wxHtmlWindowMouseHelper::HandleMouseMoved();
512 event.Skip();
513 }
514
515 void wxHtmlListBox::OnLeftDown(wxMouseEvent& event)
516 {
517 wxPoint pos = event.GetPosition();
518 wxHtmlCell *cell;
519
520 if ( !PhysicalCoordsToCell(pos, cell) )
521 {
522 event.Skip();
523 return;
524 }
525
526 if ( !wxHtmlWindowMouseHelper::HandleMouseClick(cell, pos, event) )
527 {
528 // no link was clicked, so let the listbox code handle the click (e.g.
529 // by selecting another item in the list):
530 event.Skip();
531 }
532 }
533
534 #endif // wxUSE_HTML