Remove all lines containing cvs/svn "$Id$" keyword.
[wxWidgets.git] / src / generic / htmllbox.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/generic/htmllbox.cpp
3 // Purpose: implementation of wxHtmlListBox
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 31.05.03
7 // Copyright: (c) 2003 Vadim Zeitlin <vadim@wxwindows.org>
8 // Licence: wxWindows licence
9 ///////////////////////////////////////////////////////////////////////////////
10
11 // ============================================================================
12 // declarations
13 // ============================================================================
14
15 // ----------------------------------------------------------------------------
16 // headers
17 // ----------------------------------------------------------------------------
18
19 // For compilers that support precompilation, includes "wx.h".
20 #include "wx/wxprec.h"
21
22 #ifdef __BORLANDC__
23 #pragma hdrstop
24 #endif
25
26 #ifndef WX_PRECOMP
27 #include "wx/dcclient.h"
28 #endif //WX_PRECOMP
29
30 #if wxUSE_HTML
31
32 #include "wx/htmllbox.h"
33
34 #include "wx/html/htmlcell.h"
35 #include "wx/html/winpars.h"
36
37 // this hack forces the linker to always link in m_* files
38 #include "wx/html/forcelnk.h"
39 FORCE_WXHTML_MODULES()
40
41 // ----------------------------------------------------------------------------
42 // constants
43 // ----------------------------------------------------------------------------
44
45 // small border always added to the cells:
46 static const wxCoord CELL_BORDER = 2;
47
48 const char wxHtmlListBoxNameStr[] = "htmlListBox";
49 const char wxSimpleHtmlListBoxNameStr[] = "simpleHtmlListBox";
50
51 // ============================================================================
52 // private classes
53 // ============================================================================
54
55 // ----------------------------------------------------------------------------
56 // wxHtmlListBoxCache
57 // ----------------------------------------------------------------------------
58
59 // this class is used by wxHtmlListBox to cache the parsed representation of
60 // the items to avoid doing it anew each time an item must be drawn
61 class wxHtmlListBoxCache
62 {
63 private:
64 // invalidate a single item, used by Clear() and InvalidateRange()
65 void InvalidateItem(size_t n)
66 {
67 m_items[n] = (size_t)-1;
68 wxDELETE(m_cells[n]);
69 }
70
71 public:
72 wxHtmlListBoxCache()
73 {
74 for ( size_t n = 0; n < SIZE; n++ )
75 {
76 m_items[n] = (size_t)-1;
77 m_cells[n] = NULL;
78 }
79
80 m_next = 0;
81 }
82
83 ~wxHtmlListBoxCache()
84 {
85 for ( size_t n = 0; n < SIZE; n++ )
86 {
87 delete m_cells[n];
88 }
89 }
90
91 // completely invalidate the cache
92 void Clear()
93 {
94 for ( size_t n = 0; n < SIZE; n++ )
95 {
96 InvalidateItem(n);
97 }
98 }
99
100 // return the cached cell for this index or NULL if none
101 wxHtmlCell *Get(size_t item) const
102 {
103 for ( size_t n = 0; n < SIZE; n++ )
104 {
105 if ( m_items[n] == item )
106 return m_cells[n];
107 }
108
109 return NULL;
110 }
111
112 // returns true if we already have this item cached
113 bool Has(size_t item) const { return Get(item) != NULL; }
114
115 // ensure that the item is cached
116 void Store(size_t item, wxHtmlCell *cell)
117 {
118 delete m_cells[m_next];
119 m_cells[m_next] = cell;
120 m_items[m_next] = item;
121
122 // advance to the next item wrapping around if there are no more
123 if ( ++m_next == SIZE )
124 m_next = 0;
125 }
126
127 // forget the cached value of the item(s) between the given ones (inclusive)
128 void InvalidateRange(size_t from, size_t to)
129 {
130 for ( size_t n = 0; n < SIZE; n++ )
131 {
132 if ( m_items[n] >= from && m_items[n] <= to )
133 {
134 InvalidateItem(n);
135 }
136 }
137 }
138
139 private:
140 // the max number of the items we cache
141 enum { SIZE = 50 };
142
143 // the index of the LRU (oldest) cell
144 size_t m_next;
145
146 // the parsed representation of the cached item or NULL
147 wxHtmlCell *m_cells[SIZE];
148
149 // the index of the currently cached item (only valid if m_cells != NULL)
150 size_t m_items[SIZE];
151 };
152
153 // ----------------------------------------------------------------------------
154 // wxHtmlListBoxStyle
155 // ----------------------------------------------------------------------------
156
157 // just forward wxDefaultHtmlRenderingStyle callbacks to the main class so that
158 // they could be overridden by the user code
159 class wxHtmlListBoxStyle : public wxDefaultHtmlRenderingStyle
160 {
161 public:
162 wxHtmlListBoxStyle(const wxHtmlListBox& hlbox) : m_hlbox(hlbox) { }
163
164 virtual wxColour GetSelectedTextColour(const wxColour& colFg)
165 {
166 // by default wxHtmlListBox doesn't implement GetSelectedTextColour()
167 // and returns wxNullColour from it, so use the default HTML colour for
168 // selection
169 wxColour col = m_hlbox.GetSelectedTextColour(colFg);
170 if ( !col.IsOk() )
171 {
172 col = wxDefaultHtmlRenderingStyle::GetSelectedTextColour(colFg);
173 }
174
175 return col;
176 }
177
178 virtual wxColour GetSelectedTextBgColour(const wxColour& colBg)
179 {
180 wxColour col = m_hlbox.GetSelectedTextBgColour(colBg);
181 if ( !col.IsOk() )
182 {
183 col = wxDefaultHtmlRenderingStyle::GetSelectedTextBgColour(colBg);
184 }
185
186 return col;
187 }
188
189 private:
190 const wxHtmlListBox& m_hlbox;
191
192 wxDECLARE_NO_COPY_CLASS(wxHtmlListBoxStyle);
193 };
194
195 // ----------------------------------------------------------------------------
196 // event tables
197 // ----------------------------------------------------------------------------
198
199 BEGIN_EVENT_TABLE(wxHtmlListBox, wxVListBox)
200 EVT_SIZE(wxHtmlListBox::OnSize)
201 EVT_MOTION(wxHtmlListBox::OnMouseMove)
202 EVT_LEFT_DOWN(wxHtmlListBox::OnLeftDown)
203 END_EVENT_TABLE()
204
205 // ============================================================================
206 // implementation
207 // ============================================================================
208
209 IMPLEMENT_ABSTRACT_CLASS(wxHtmlListBox, wxVListBox)
210
211
212 // ----------------------------------------------------------------------------
213 // wxHtmlListBox creation
214 // ----------------------------------------------------------------------------
215
216 wxHtmlListBox::wxHtmlListBox()
217 : wxHtmlWindowMouseHelper(this)
218 {
219 Init();
220 }
221
222 // normal constructor which calls Create() internally
223 wxHtmlListBox::wxHtmlListBox(wxWindow *parent,
224 wxWindowID id,
225 const wxPoint& pos,
226 const wxSize& size,
227 long style,
228 const wxString& name)
229 : wxHtmlWindowMouseHelper(this)
230 {
231 Init();
232
233 (void)Create(parent, id, pos, size, style, name);
234 }
235
236 void wxHtmlListBox::Init()
237 {
238 m_htmlParser = NULL;
239 m_htmlRendStyle = new wxHtmlListBoxStyle(*this);
240 m_cache = new wxHtmlListBoxCache;
241 }
242
243 bool wxHtmlListBox::Create(wxWindow *parent,
244 wxWindowID id,
245 const wxPoint& pos,
246 const wxSize& size,
247 long style,
248 const wxString& name)
249 {
250 return wxVListBox::Create(parent, id, pos, size, style, name);
251 }
252
253 wxHtmlListBox::~wxHtmlListBox()
254 {
255 delete m_cache;
256
257 if ( m_htmlParser )
258 {
259 delete m_htmlParser->GetDC();
260 delete m_htmlParser;
261 }
262
263 delete m_htmlRendStyle;
264 }
265
266 // ----------------------------------------------------------------------------
267 // wxHtmlListBox appearance
268 // ----------------------------------------------------------------------------
269
270 wxColour
271 wxHtmlListBox::GetSelectedTextColour(const wxColour& WXUNUSED(colFg)) const
272 {
273 return wxNullColour;
274 }
275
276 wxColour
277 wxHtmlListBox::GetSelectedTextBgColour(const wxColour& WXUNUSED(colBg)) const
278 {
279 return GetSelectionBackground();
280 }
281
282 // ----------------------------------------------------------------------------
283 // wxHtmlListBox items markup
284 // ----------------------------------------------------------------------------
285
286 wxString wxHtmlListBox::OnGetItemMarkup(size_t n) const
287 {
288 // we don't even need to wrap the value returned by OnGetItem() inside
289 // "<html><body>" and "</body></html>" because wxHTML can parse it even
290 // without these tags
291 return OnGetItem(n);
292 }
293
294 // ----------------------------------------------------------------------------
295 // wxHtmlListBox cache handling
296 // ----------------------------------------------------------------------------
297
298 void wxHtmlListBox::CacheItem(size_t n) const
299 {
300 if ( !m_cache->Has(n) )
301 {
302 if ( !m_htmlParser )
303 {
304 wxHtmlListBox *self = wxConstCast(this, wxHtmlListBox);
305
306 self->m_htmlParser = new wxHtmlWinParser(self);
307 m_htmlParser->SetDC(new wxClientDC(self));
308 m_htmlParser->SetFS(&self->m_filesystem);
309 #if !wxUSE_UNICODE
310 if (GetFont().IsOk())
311 m_htmlParser->SetInputEncoding(GetFont().GetEncoding());
312 #endif
313 // use system's default GUI font by default:
314 m_htmlParser->SetStandardFonts();
315 }
316
317 wxHtmlContainerCell *cell = (wxHtmlContainerCell *)m_htmlParser->
318 Parse(OnGetItemMarkup(n));
319 wxCHECK_RET( cell, wxT("wxHtmlParser::Parse() returned NULL?") );
320
321 // set the cell's ID to item's index so that CellCoordsToPhysical()
322 // can quickly find the item:
323 cell->SetId(wxString::Format(wxT("%lu"), (unsigned long)n));
324
325 cell->Layout(GetClientSize().x - 2*GetMargins().x);
326
327 m_cache->Store(n, cell);
328 }
329 }
330
331 void wxHtmlListBox::OnSize(wxSizeEvent& event)
332 {
333 // we need to relayout all the cached cells
334 m_cache->Clear();
335
336 event.Skip();
337 }
338
339 void wxHtmlListBox::RefreshRow(size_t line)
340 {
341 m_cache->InvalidateRange(line, line);
342
343 wxVListBox::RefreshRow(line);
344 }
345
346 void wxHtmlListBox::RefreshRows(size_t from, size_t to)
347 {
348 m_cache->InvalidateRange(from, to);
349
350 wxVListBox::RefreshRows(from, to);
351 }
352
353 void wxHtmlListBox::RefreshAll()
354 {
355 m_cache->Clear();
356
357 wxVListBox::RefreshAll();
358 }
359
360 void wxHtmlListBox::SetItemCount(size_t count)
361 {
362 // the items are going to change, forget the old ones
363 m_cache->Clear();
364
365 wxVListBox::SetItemCount(count);
366 }
367
368 // ----------------------------------------------------------------------------
369 // wxHtmlListBox implementation of wxVListBox pure virtuals
370 // ----------------------------------------------------------------------------
371
372 void
373 wxHtmlListBox::OnDrawBackground(wxDC& dc, const wxRect& rect, size_t n) const
374 {
375 if ( IsSelected(n) )
376 {
377 if ( DoDrawSolidBackground
378 (
379 GetSelectedTextBgColour(GetBackgroundColour()),
380 dc,
381 rect,
382 n
383 ) )
384 {
385 return;
386 }
387 //else: no custom selection background colour, use base class version
388 }
389
390 wxVListBox::OnDrawBackground(dc, rect, n);
391 }
392
393 void wxHtmlListBox::OnDrawItem(wxDC& dc, const wxRect& rect, size_t n) const
394 {
395 CacheItem(n);
396
397 wxHtmlCell *cell = m_cache->Get(n);
398 wxCHECK_RET( cell, wxT("this cell should be cached!") );
399
400 wxHtmlRenderingInfo htmlRendInfo;
401
402 // draw the selected cell in selected state ourselves if we're using custom
403 // colours (to test for this, check the callbacks by passing them any dummy
404 // (but valid, to avoid asserts) colour):
405 if ( IsSelected(n) &&
406 (GetSelectedTextColour(*wxBLACK).IsOk() ||
407 GetSelectedTextBgColour(*wxWHITE).IsOk()) )
408 {
409 wxHtmlSelection htmlSel;
410 htmlSel.Set(wxPoint(0,0), cell, wxPoint(INT_MAX, INT_MAX), cell);
411 htmlRendInfo.SetSelection(&htmlSel);
412 htmlRendInfo.SetStyle(m_htmlRendStyle);
413 htmlRendInfo.GetState().SetSelectionState(wxHTML_SEL_IN);
414 }
415 //else: normal item or selected item with default colours, its background
416 // was already taken care of in the base class
417
418 // note that we can't stop drawing exactly at the window boundary as then
419 // even the visible cells part could be not drawn, so always draw the
420 // entire cell
421 cell->Draw(dc,
422 rect.x + CELL_BORDER, rect.y + CELL_BORDER,
423 0, INT_MAX, htmlRendInfo);
424 }
425
426 wxCoord wxHtmlListBox::OnMeasureItem(size_t n) const
427 {
428 CacheItem(n);
429
430 wxHtmlCell *cell = m_cache->Get(n);
431 wxCHECK_MSG( cell, 0, wxT("this cell should be cached!") );
432
433 return cell->GetHeight() + cell->GetDescent() + 4;
434 }
435
436 // ----------------------------------------------------------------------------
437 // wxHtmlListBox implementation of wxHtmlListBoxWinInterface
438 // ----------------------------------------------------------------------------
439
440 void wxHtmlListBox::SetHTMLWindowTitle(const wxString& WXUNUSED(title))
441 {
442 // nothing to do
443 }
444
445 void wxHtmlListBox::OnHTMLLinkClicked(const wxHtmlLinkInfo& link)
446 {
447 OnLinkClicked(GetItemForCell(link.GetHtmlCell()), link);
448 }
449
450 void wxHtmlListBox::OnLinkClicked(size_t WXUNUSED(n),
451 const wxHtmlLinkInfo& link)
452 {
453 wxHtmlLinkEvent event(GetId(), link);
454 GetEventHandler()->ProcessEvent(event);
455 }
456
457 wxHtmlOpeningStatus
458 wxHtmlListBox::OnHTMLOpeningURL(wxHtmlURLType WXUNUSED(type),
459 const wxString& WXUNUSED(url),
460 wxString *WXUNUSED(redirect)) const
461 {
462 return wxHTML_OPEN;
463 }
464
465 wxPoint wxHtmlListBox::HTMLCoordsToWindow(wxHtmlCell *cell,
466 const wxPoint& pos) const
467 {
468 return CellCoordsToPhysical(pos, cell);
469 }
470
471 wxWindow* wxHtmlListBox::GetHTMLWindow() { return this; }
472
473 wxColour wxHtmlListBox::GetHTMLBackgroundColour() const
474 {
475 return GetBackgroundColour();
476 }
477
478 void wxHtmlListBox::SetHTMLBackgroundColour(const wxColour& WXUNUSED(clr))
479 {
480 // nothing to do
481 }
482
483 void wxHtmlListBox::SetHTMLBackgroundImage(const wxBitmap& WXUNUSED(bmpBg))
484 {
485 // nothing to do
486 }
487
488 void wxHtmlListBox::SetHTMLStatusText(const wxString& WXUNUSED(text))
489 {
490 // nothing to do
491 }
492
493 wxCursor wxHtmlListBox::GetHTMLCursor(HTMLCursor type) const
494 {
495 // we don't want to show text selection cursor in listboxes
496 if (type == HTMLCursor_Text)
497 return wxHtmlWindow::GetDefaultHTMLCursor(HTMLCursor_Default);
498
499 // in all other cases, use the same cursor as wxHtmlWindow:
500 return wxHtmlWindow::GetDefaultHTMLCursor(type);
501 }
502
503 // ----------------------------------------------------------------------------
504 // wxHtmlListBox handling of HTML links
505 // ----------------------------------------------------------------------------
506
507 wxPoint wxHtmlListBox::GetRootCellCoords(size_t n) const
508 {
509 wxPoint pos(CELL_BORDER, CELL_BORDER);
510 pos += GetMargins();
511 pos.y += GetRowsHeight(GetVisibleBegin(), n);
512 return pos;
513 }
514
515 bool wxHtmlListBox::PhysicalCoordsToCell(wxPoint& pos, wxHtmlCell*& cell) const
516 {
517 int n = VirtualHitTest(pos.y);
518 if ( n == wxNOT_FOUND )
519 return false;
520
521 // convert mouse coordinates to coords relative to item's wxHtmlCell:
522 pos -= GetRootCellCoords(n);
523
524 CacheItem(n);
525 cell = m_cache->Get(n);
526
527 return true;
528 }
529
530 size_t wxHtmlListBox::GetItemForCell(const wxHtmlCell *cell) const
531 {
532 wxCHECK_MSG( cell, 0, wxT("no cell") );
533
534 cell = cell->GetRootCell();
535
536 wxCHECK_MSG( cell, 0, wxT("no root cell") );
537
538 // the cell's ID contains item index, see CacheItem():
539 unsigned long n;
540 if ( !cell->GetId().ToULong(&n) )
541 {
542 wxFAIL_MSG( wxT("unexpected root cell's ID") );
543 return 0;
544 }
545
546 return n;
547 }
548
549 wxPoint
550 wxHtmlListBox::CellCoordsToPhysical(const wxPoint& pos, wxHtmlCell *cell) const
551 {
552 return pos + GetRootCellCoords(GetItemForCell(cell));
553 }
554
555 void wxHtmlListBox::OnInternalIdle()
556 {
557 wxVListBox::OnInternalIdle();
558
559 if ( wxHtmlWindowMouseHelper::DidMouseMove() )
560 {
561 wxPoint pos = ScreenToClient(wxGetMousePosition());
562 wxHtmlCell *cell;
563
564 if ( !PhysicalCoordsToCell(pos, cell) )
565 return;
566
567 wxHtmlWindowMouseHelper::HandleIdle(cell, pos);
568 }
569 }
570
571 void wxHtmlListBox::OnMouseMove(wxMouseEvent& event)
572 {
573 wxHtmlWindowMouseHelper::HandleMouseMoved();
574 event.Skip();
575 }
576
577 void wxHtmlListBox::OnLeftDown(wxMouseEvent& event)
578 {
579 wxPoint pos = event.GetPosition();
580 wxHtmlCell *cell;
581
582 if ( !PhysicalCoordsToCell(pos, cell) )
583 {
584 event.Skip();
585 return;
586 }
587
588 if ( !wxHtmlWindowMouseHelper::HandleMouseClick(cell, pos, event) )
589 {
590 // no link was clicked, so let the listbox code handle the click (e.g.
591 // by selecting another item in the list):
592 event.Skip();
593 }
594 }
595
596
597 // ----------------------------------------------------------------------------
598 // wxSimpleHtmlListBox
599 // ----------------------------------------------------------------------------
600
601 IMPLEMENT_ABSTRACT_CLASS(wxSimpleHtmlListBox, wxHtmlListBox)
602
603
604 bool wxSimpleHtmlListBox::Create(wxWindow *parent, wxWindowID id,
605 const wxPoint& pos,
606 const wxSize& size,
607 int n, const wxString choices[],
608 long style,
609 const wxValidator& validator,
610 const wxString& name)
611 {
612 if (!wxHtmlListBox::Create(parent, id, pos, size, style, name))
613 return false;
614
615 #if wxUSE_VALIDATORS
616 SetValidator(validator);
617 #endif
618
619 Append(n, choices);
620
621 return true;
622 }
623
624 bool wxSimpleHtmlListBox::Create(wxWindow *parent, wxWindowID id,
625 const wxPoint& pos,
626 const wxSize& size,
627 const wxArrayString& choices,
628 long style,
629 const wxValidator& validator,
630 const wxString& name)
631 {
632 if (!wxHtmlListBox::Create(parent, id, pos, size, style, name))
633 return false;
634
635 #if wxUSE_VALIDATORS
636 SetValidator(validator);
637 #endif
638
639 Append(choices);
640
641 return true;
642 }
643
644 wxSimpleHtmlListBox::~wxSimpleHtmlListBox()
645 {
646 wxItemContainer::Clear();
647 }
648
649 void wxSimpleHtmlListBox::DoClear()
650 {
651 wxASSERT(m_items.GetCount() == m_HTMLclientData.GetCount());
652
653 m_items.Clear();
654 m_HTMLclientData.Clear();
655
656 UpdateCount();
657 }
658
659 void wxSimpleHtmlListBox::Clear()
660 {
661 DoClear();
662 }
663
664 void wxSimpleHtmlListBox::DoDeleteOneItem(unsigned int n)
665 {
666 m_items.RemoveAt(n);
667
668 m_HTMLclientData.RemoveAt(n);
669
670 UpdateCount();
671 }
672
673 int wxSimpleHtmlListBox::DoInsertItems(const wxArrayStringsAdapter& items,
674 unsigned int pos,
675 void **clientData,
676 wxClientDataType type)
677 {
678 const unsigned int count = items.GetCount();
679
680 m_items.Insert(wxEmptyString, pos, count);
681 m_HTMLclientData.Insert(NULL, pos, count);
682
683 for ( unsigned int i = 0; i < count; ++i, ++pos )
684 {
685 m_items[pos] = items[i];
686 AssignNewItemClientData(pos, clientData, i, type);
687 }
688
689 UpdateCount();
690
691 return pos - 1;
692 }
693
694 void wxSimpleHtmlListBox::SetString(unsigned int n, const wxString& s)
695 {
696 wxCHECK_RET( IsValid(n),
697 wxT("invalid index in wxSimpleHtmlListBox::SetString") );
698
699 m_items[n]=s;
700 RefreshRow(n);
701 }
702
703 wxString wxSimpleHtmlListBox::GetString(unsigned int n) const
704 {
705 wxCHECK_MSG( IsValid(n), wxEmptyString,
706 wxT("invalid index in wxSimpleHtmlListBox::GetString") );
707
708 return m_items[n];
709 }
710
711 void wxSimpleHtmlListBox::UpdateCount()
712 {
713 wxASSERT(m_items.GetCount() == m_HTMLclientData.GetCount());
714 wxHtmlListBox::SetItemCount(m_items.GetCount());
715
716 // very small optimization: if you need to add lot of items to
717 // a wxSimpleHtmlListBox be sure to use the
718 // wxSimpleHtmlListBox::Append(const wxArrayString&) method instead!
719 if (!this->IsFrozen())
720 RefreshAll();
721 }
722
723 #endif // wxUSE_HTML