1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/generic/htmllbox.cpp
3 // Purpose: implementation of wxHtmlListBox
4 // Author: Vadim Zeitlin
7 // Copyright: (c) 2003 Vadim Zeitlin <vadim@wxwindows.org>
8 // Licence: wxWindows licence
9 ///////////////////////////////////////////////////////////////////////////////
11 // ============================================================================
13 // ============================================================================
15 // ----------------------------------------------------------------------------
17 // ----------------------------------------------------------------------------
19 // For compilers that support precompilation, includes "wx.h".
20 #include "wx/wxprec.h"
27 #include "wx/dcclient.h"
32 #include "wx/htmllbox.h"
34 #include "wx/html/htmlcell.h"
35 #include "wx/html/winpars.h"
37 // this hack forces the linker to always link in m_* files
38 #include "wx/html/forcelnk.h"
39 FORCE_WXHTML_MODULES()
41 // ----------------------------------------------------------------------------
43 // ----------------------------------------------------------------------------
45 // small border always added to the cells:
46 static const wxCoord CELL_BORDER
= 2;
48 const char wxHtmlListBoxNameStr
[] = "htmlListBox";
49 const char wxSimpleHtmlListBoxNameStr
[] = "simpleHtmlListBox";
51 // ============================================================================
53 // ============================================================================
55 // ----------------------------------------------------------------------------
57 // ----------------------------------------------------------------------------
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
64 // invalidate a single item, used by Clear() and InvalidateRange()
65 void InvalidateItem(size_t n
)
67 m_items
[n
] = (size_t)-1;
74 for ( size_t n
= 0; n
< SIZE
; n
++ )
76 m_items
[n
] = (size_t)-1;
85 for ( size_t n
= 0; n
< SIZE
; n
++ )
91 // completely invalidate the cache
94 for ( size_t n
= 0; n
< SIZE
; n
++ )
100 // return the cached cell for this index or NULL if none
101 wxHtmlCell
*Get(size_t item
) const
103 for ( size_t n
= 0; n
< SIZE
; n
++ )
105 if ( m_items
[n
] == item
)
112 // returns true if we already have this item cached
113 bool Has(size_t item
) const { return Get(item
) != NULL
; }
115 // ensure that the item is cached
116 void Store(size_t item
, wxHtmlCell
*cell
)
118 delete m_cells
[m_next
];
119 m_cells
[m_next
] = cell
;
120 m_items
[m_next
] = item
;
122 // advance to the next item wrapping around if there are no more
123 if ( ++m_next
== SIZE
)
127 // forget the cached value of the item(s) between the given ones (inclusive)
128 void InvalidateRange(size_t from
, size_t to
)
130 for ( size_t n
= 0; n
< SIZE
; n
++ )
132 if ( m_items
[n
] >= from
&& m_items
[n
] <= to
)
140 // the max number of the items we cache
143 // the index of the LRU (oldest) cell
146 // the parsed representation of the cached item or NULL
147 wxHtmlCell
*m_cells
[SIZE
];
149 // the index of the currently cached item (only valid if m_cells != NULL)
150 size_t m_items
[SIZE
];
153 // ----------------------------------------------------------------------------
154 // wxHtmlListBoxStyle
155 // ----------------------------------------------------------------------------
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
162 wxHtmlListBoxStyle(const wxHtmlListBox
& hlbox
) : m_hlbox(hlbox
) { }
164 virtual wxColour
GetSelectedTextColour(const wxColour
& colFg
)
166 // by default wxHtmlListBox doesn't implement GetSelectedTextColour()
167 // and returns wxNullColour from it, so use the default HTML colour for
169 wxColour col
= m_hlbox
.GetSelectedTextColour(colFg
);
172 col
= wxDefaultHtmlRenderingStyle::GetSelectedTextColour(colFg
);
178 virtual wxColour
GetSelectedTextBgColour(const wxColour
& colBg
)
180 wxColour col
= m_hlbox
.GetSelectedTextBgColour(colBg
);
183 col
= wxDefaultHtmlRenderingStyle::GetSelectedTextBgColour(colBg
);
190 const wxHtmlListBox
& m_hlbox
;
192 wxDECLARE_NO_COPY_CLASS(wxHtmlListBoxStyle
);
195 // ----------------------------------------------------------------------------
197 // ----------------------------------------------------------------------------
199 BEGIN_EVENT_TABLE(wxHtmlListBox
, wxVListBox
)
200 EVT_SIZE(wxHtmlListBox::OnSize
)
201 EVT_MOTION(wxHtmlListBox::OnMouseMove
)
202 EVT_LEFT_DOWN(wxHtmlListBox::OnLeftDown
)
205 // ============================================================================
207 // ============================================================================
209 IMPLEMENT_ABSTRACT_CLASS(wxHtmlListBox
, wxVListBox
)
212 // ----------------------------------------------------------------------------
213 // wxHtmlListBox creation
214 // ----------------------------------------------------------------------------
216 wxHtmlListBox::wxHtmlListBox()
217 : wxHtmlWindowMouseHelper(this)
222 // normal constructor which calls Create() internally
223 wxHtmlListBox::wxHtmlListBox(wxWindow
*parent
,
228 const wxString
& name
)
229 : wxHtmlWindowMouseHelper(this)
233 (void)Create(parent
, id
, pos
, size
, style
, name
);
236 void wxHtmlListBox::Init()
239 m_htmlRendStyle
= new wxHtmlListBoxStyle(*this);
240 m_cache
= new wxHtmlListBoxCache
;
243 bool wxHtmlListBox::Create(wxWindow
*parent
,
248 const wxString
& name
)
250 return wxVListBox::Create(parent
, id
, pos
, size
, style
, name
);
253 wxHtmlListBox::~wxHtmlListBox()
259 delete m_htmlParser
->GetDC();
263 delete m_htmlRendStyle
;
266 // ----------------------------------------------------------------------------
267 // wxHtmlListBox appearance
268 // ----------------------------------------------------------------------------
271 wxHtmlListBox::GetSelectedTextColour(const wxColour
& WXUNUSED(colFg
)) const
277 wxHtmlListBox::GetSelectedTextBgColour(const wxColour
& WXUNUSED(colBg
)) const
279 return GetSelectionBackground();
282 // ----------------------------------------------------------------------------
283 // wxHtmlListBox items markup
284 // ----------------------------------------------------------------------------
286 wxString
wxHtmlListBox::OnGetItemMarkup(size_t n
) const
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
294 // ----------------------------------------------------------------------------
295 // wxHtmlListBox cache handling
296 // ----------------------------------------------------------------------------
298 void wxHtmlListBox::CacheItem(size_t n
) const
300 if ( !m_cache
->Has(n
) )
304 wxHtmlListBox
*self
= wxConstCast(this, wxHtmlListBox
);
306 self
->m_htmlParser
= new wxHtmlWinParser(self
);
307 m_htmlParser
->SetDC(new wxClientDC(self
));
308 m_htmlParser
->SetFS(&self
->m_filesystem
);
310 if (GetFont().IsOk())
311 m_htmlParser
->SetInputEncoding(GetFont().GetEncoding());
313 // use system's default GUI font by default:
314 m_htmlParser
->SetStandardFonts();
317 wxHtmlContainerCell
*cell
= (wxHtmlContainerCell
*)m_htmlParser
->
318 Parse(OnGetItemMarkup(n
));
319 wxCHECK_RET( cell
, wxT("wxHtmlParser::Parse() returned NULL?") );
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
));
325 cell
->Layout(GetClientSize().x
- 2*GetMargins().x
);
327 m_cache
->Store(n
, cell
);
331 void wxHtmlListBox::OnSize(wxSizeEvent
& event
)
333 // we need to relayout all the cached cells
339 void wxHtmlListBox::RefreshRow(size_t line
)
341 m_cache
->InvalidateRange(line
, line
);
343 wxVListBox::RefreshRow(line
);
346 void wxHtmlListBox::RefreshRows(size_t from
, size_t to
)
348 m_cache
->InvalidateRange(from
, to
);
350 wxVListBox::RefreshRows(from
, to
);
353 void wxHtmlListBox::RefreshAll()
357 wxVListBox::RefreshAll();
360 void wxHtmlListBox::SetItemCount(size_t count
)
362 // the items are going to change, forget the old ones
365 wxVListBox::SetItemCount(count
);
368 // ----------------------------------------------------------------------------
369 // wxHtmlListBox implementation of wxVListBox pure virtuals
370 // ----------------------------------------------------------------------------
373 wxHtmlListBox::OnDrawBackground(wxDC
& dc
, const wxRect
& rect
, size_t n
) const
377 if ( DoDrawSolidBackground
379 GetSelectedTextBgColour(GetBackgroundColour()),
387 //else: no custom selection background colour, use base class version
390 wxVListBox::OnDrawBackground(dc
, rect
, n
);
393 void wxHtmlListBox::OnDrawItem(wxDC
& dc
, const wxRect
& rect
, size_t n
) const
397 wxHtmlCell
*cell
= m_cache
->Get(n
);
398 wxCHECK_RET( cell
, wxT("this cell should be cached!") );
400 wxHtmlRenderingInfo htmlRendInfo
;
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()) )
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
);
415 //else: normal item or selected item with default colours, its background
416 // was already taken care of in the base class
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
422 rect
.x
+ CELL_BORDER
, rect
.y
+ CELL_BORDER
,
423 0, INT_MAX
, htmlRendInfo
);
426 wxCoord
wxHtmlListBox::OnMeasureItem(size_t n
) const
430 wxHtmlCell
*cell
= m_cache
->Get(n
);
431 wxCHECK_MSG( cell
, 0, wxT("this cell should be cached!") );
433 return cell
->GetHeight() + cell
->GetDescent() + 4;
436 // ----------------------------------------------------------------------------
437 // wxHtmlListBox implementation of wxHtmlListBoxWinInterface
438 // ----------------------------------------------------------------------------
440 void wxHtmlListBox::SetHTMLWindowTitle(const wxString
& WXUNUSED(title
))
445 void wxHtmlListBox::OnHTMLLinkClicked(const wxHtmlLinkInfo
& link
)
447 OnLinkClicked(GetItemForCell(link
.GetHtmlCell()), link
);
450 void wxHtmlListBox::OnLinkClicked(size_t WXUNUSED(n
),
451 const wxHtmlLinkInfo
& link
)
453 wxHtmlLinkEvent
event(GetId(), link
);
454 GetEventHandler()->ProcessEvent(event
);
458 wxHtmlListBox::OnHTMLOpeningURL(wxHtmlURLType
WXUNUSED(type
),
459 const wxString
& WXUNUSED(url
),
460 wxString
*WXUNUSED(redirect
)) const
465 wxPoint
wxHtmlListBox::HTMLCoordsToWindow(wxHtmlCell
*cell
,
466 const wxPoint
& pos
) const
468 return CellCoordsToPhysical(pos
, cell
);
471 wxWindow
* wxHtmlListBox::GetHTMLWindow() { return this; }
473 wxColour
wxHtmlListBox::GetHTMLBackgroundColour() const
475 return GetBackgroundColour();
478 void wxHtmlListBox::SetHTMLBackgroundColour(const wxColour
& WXUNUSED(clr
))
483 void wxHtmlListBox::SetHTMLBackgroundImage(const wxBitmap
& WXUNUSED(bmpBg
))
488 void wxHtmlListBox::SetHTMLStatusText(const wxString
& WXUNUSED(text
))
493 wxCursor
wxHtmlListBox::GetHTMLCursor(HTMLCursor type
) const
495 // we don't want to show text selection cursor in listboxes
496 if (type
== HTMLCursor_Text
)
497 return wxHtmlWindow::GetDefaultHTMLCursor(HTMLCursor_Default
);
499 // in all other cases, use the same cursor as wxHtmlWindow:
500 return wxHtmlWindow::GetDefaultHTMLCursor(type
);
503 // ----------------------------------------------------------------------------
504 // wxHtmlListBox handling of HTML links
505 // ----------------------------------------------------------------------------
507 wxPoint
wxHtmlListBox::GetRootCellCoords(size_t n
) const
509 wxPoint
pos(CELL_BORDER
, CELL_BORDER
);
511 pos
.y
+= GetRowsHeight(GetVisibleBegin(), n
);
515 bool wxHtmlListBox::PhysicalCoordsToCell(wxPoint
& pos
, wxHtmlCell
*& cell
) const
517 int n
= VirtualHitTest(pos
.y
);
518 if ( n
== wxNOT_FOUND
)
521 // convert mouse coordinates to coords relative to item's wxHtmlCell:
522 pos
-= GetRootCellCoords(n
);
525 cell
= m_cache
->Get(n
);
530 size_t wxHtmlListBox::GetItemForCell(const wxHtmlCell
*cell
) const
532 wxCHECK_MSG( cell
, 0, wxT("no cell") );
534 cell
= cell
->GetRootCell();
536 wxCHECK_MSG( cell
, 0, wxT("no root cell") );
538 // the cell's ID contains item index, see CacheItem():
540 if ( !cell
->GetId().ToULong(&n
) )
542 wxFAIL_MSG( wxT("unexpected root cell's ID") );
550 wxHtmlListBox::CellCoordsToPhysical(const wxPoint
& pos
, wxHtmlCell
*cell
) const
552 return pos
+ GetRootCellCoords(GetItemForCell(cell
));
555 void wxHtmlListBox::OnInternalIdle()
557 wxVListBox::OnInternalIdle();
559 if ( wxHtmlWindowMouseHelper::DidMouseMove() )
561 wxPoint pos
= ScreenToClient(wxGetMousePosition());
564 if ( !PhysicalCoordsToCell(pos
, cell
) )
567 wxHtmlWindowMouseHelper::HandleIdle(cell
, pos
);
571 void wxHtmlListBox::OnMouseMove(wxMouseEvent
& event
)
573 wxHtmlWindowMouseHelper::HandleMouseMoved();
577 void wxHtmlListBox::OnLeftDown(wxMouseEvent
& event
)
579 wxPoint pos
= event
.GetPosition();
582 if ( !PhysicalCoordsToCell(pos
, cell
) )
588 if ( !wxHtmlWindowMouseHelper::HandleMouseClick(cell
, pos
, event
) )
590 // no link was clicked, so let the listbox code handle the click (e.g.
591 // by selecting another item in the list):
597 // ----------------------------------------------------------------------------
598 // wxSimpleHtmlListBox
599 // ----------------------------------------------------------------------------
601 IMPLEMENT_ABSTRACT_CLASS(wxSimpleHtmlListBox
, wxHtmlListBox
)
604 bool wxSimpleHtmlListBox::Create(wxWindow
*parent
, wxWindowID id
,
607 int n
, const wxString choices
[],
609 const wxValidator
& validator
,
610 const wxString
& name
)
612 if (!wxHtmlListBox::Create(parent
, id
, pos
, size
, style
, name
))
616 SetValidator(validator
);
624 bool wxSimpleHtmlListBox::Create(wxWindow
*parent
, wxWindowID id
,
627 const wxArrayString
& choices
,
629 const wxValidator
& validator
,
630 const wxString
& name
)
632 if (!wxHtmlListBox::Create(parent
, id
, pos
, size
, style
, name
))
636 SetValidator(validator
);
644 wxSimpleHtmlListBox::~wxSimpleHtmlListBox()
646 wxItemContainer::Clear();
649 void wxSimpleHtmlListBox::DoClear()
651 wxASSERT(m_items
.GetCount() == m_HTMLclientData
.GetCount());
654 m_HTMLclientData
.Clear();
659 void wxSimpleHtmlListBox::Clear()
664 void wxSimpleHtmlListBox::DoDeleteOneItem(unsigned int n
)
668 m_HTMLclientData
.RemoveAt(n
);
673 int wxSimpleHtmlListBox::DoInsertItems(const wxArrayStringsAdapter
& items
,
676 wxClientDataType type
)
678 const unsigned int count
= items
.GetCount();
680 m_items
.Insert(wxEmptyString
, pos
, count
);
681 m_HTMLclientData
.Insert(NULL
, pos
, count
);
683 for ( unsigned int i
= 0; i
< count
; ++i
, ++pos
)
685 m_items
[pos
] = items
[i
];
686 AssignNewItemClientData(pos
, clientData
, i
, type
);
694 void wxSimpleHtmlListBox::SetString(unsigned int n
, const wxString
& s
)
696 wxCHECK_RET( IsValid(n
),
697 wxT("invalid index in wxSimpleHtmlListBox::SetString") );
703 wxString
wxSimpleHtmlListBox::GetString(unsigned int n
) const
705 wxCHECK_MSG( IsValid(n
), wxEmptyString
,
706 wxT("invalid index in wxSimpleHtmlListBox::GetString") );
711 void wxSimpleHtmlListBox::UpdateCount()
713 wxASSERT(m_items
.GetCount() == m_HTMLclientData
.GetCount());
714 wxHtmlListBox::SetItemCount(m_items
.GetCount());
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())