1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: generic/htmllbox.cpp
3 // Purpose: implementation of wxHtmlListBox
4 // Author: Vadim Zeitlin
8 // Copyright: (c) 2003 Vadim Zeitlin <vadim@wxwindows.org>
9 // License: wxWindows license
10 ///////////////////////////////////////////////////////////////////////////////
12 // ============================================================================
14 // ============================================================================
16 // ----------------------------------------------------------------------------
18 // ----------------------------------------------------------------------------
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
28 #include "wx/dcclient.h"
33 #include "wx/htmllbox.h"
35 #include "wx/html/htmlcell.h"
36 #include "wx/html/winpars.h"
38 // this hack forces the linker to always link in m_* files
39 #include "wx/html/forcelnk.h"
40 FORCE_WXHTML_MODULES()
42 // ----------------------------------------------------------------------------
44 // ----------------------------------------------------------------------------
46 // small border always added to the cells:
47 static const wxCoord CELL_BORDER
= 2;
49 const char wxHtmlListBoxNameStr
[] = "htmlListBox";
50 const char wxSimpleHtmlListBoxNameStr
[] = "simpleHtmlListBox";
52 // ============================================================================
54 // ============================================================================
56 // ----------------------------------------------------------------------------
58 // ----------------------------------------------------------------------------
60 // this class is used by wxHtmlListBox to cache the parsed representation of
61 // the items to avoid doing it anew each time an item must be drawn
62 class wxHtmlListBoxCache
65 // invalidate a single item, used by Clear() and InvalidateRange()
66 void InvalidateItem(size_t n
)
68 m_items
[n
] = (size_t)-1;
76 for ( size_t n
= 0; n
< SIZE
; n
++ )
78 m_items
[n
] = (size_t)-1;
87 for ( size_t n
= 0; n
< SIZE
; n
++ )
93 // completely invalidate the cache
96 for ( size_t n
= 0; n
< SIZE
; n
++ )
102 // return the cached cell for this index or NULL if none
103 wxHtmlCell
*Get(size_t item
) const
105 for ( size_t n
= 0; n
< SIZE
; n
++ )
107 if ( m_items
[n
] == item
)
114 // returns true if we already have this item cached
115 bool Has(size_t item
) const { return Get(item
) != NULL
; }
117 // ensure that the item is cached
118 void Store(size_t item
, wxHtmlCell
*cell
)
120 delete m_cells
[m_next
];
121 m_cells
[m_next
] = cell
;
122 m_items
[m_next
] = item
;
124 // advance to the next item wrapping around if there are no more
125 if ( ++m_next
== SIZE
)
129 // forget the cached value of the item(s) between the given ones (inclusive)
130 void InvalidateRange(size_t from
, size_t to
)
132 for ( size_t n
= 0; n
< SIZE
; n
++ )
134 if ( m_items
[n
] >= from
&& m_items
[n
] <= to
)
142 // the max number of the items we cache
145 // the index of the LRU (oldest) cell
148 // the parsed representation of the cached item or NULL
149 wxHtmlCell
*m_cells
[SIZE
];
151 // the index of the currently cached item (only valid if m_cells != NULL)
152 size_t m_items
[SIZE
];
155 // ----------------------------------------------------------------------------
156 // wxHtmlListBoxStyle
157 // ----------------------------------------------------------------------------
159 // just forward wxDefaultHtmlRenderingStyle callbacks to the main class so that
160 // they could be overridden by the user code
161 class wxHtmlListBoxStyle
: public wxDefaultHtmlRenderingStyle
164 wxHtmlListBoxStyle(const wxHtmlListBox
& hlbox
) : m_hlbox(hlbox
) { }
166 virtual wxColour
GetSelectedTextColour(const wxColour
& colFg
)
168 // by default wxHtmlListBox doesn't implement GetSelectedTextColour()
169 // and returns wxNullColour from it, so use the default HTML colour for
171 wxColour col
= m_hlbox
.GetSelectedTextColour(colFg
);
174 col
= wxDefaultHtmlRenderingStyle::GetSelectedTextColour(colFg
);
180 virtual wxColour
GetSelectedTextBgColour(const wxColour
& colBg
)
182 wxColour col
= m_hlbox
.GetSelectedTextBgColour(colBg
);
185 col
= wxDefaultHtmlRenderingStyle::GetSelectedTextBgColour(colBg
);
192 const wxHtmlListBox
& m_hlbox
;
194 wxDECLARE_NO_COPY_CLASS(wxHtmlListBoxStyle
);
197 // ----------------------------------------------------------------------------
199 // ----------------------------------------------------------------------------
201 BEGIN_EVENT_TABLE(wxHtmlListBox
, wxVListBox
)
202 EVT_SIZE(wxHtmlListBox::OnSize
)
203 EVT_MOTION(wxHtmlListBox::OnMouseMove
)
204 EVT_LEFT_DOWN(wxHtmlListBox::OnLeftDown
)
207 // ============================================================================
209 // ============================================================================
211 IMPLEMENT_ABSTRACT_CLASS(wxHtmlListBox
, wxVListBox
)
214 // ----------------------------------------------------------------------------
215 // wxHtmlListBox creation
216 // ----------------------------------------------------------------------------
218 wxHtmlListBox::wxHtmlListBox()
219 : wxHtmlWindowMouseHelper(this)
224 // normal constructor which calls Create() internally
225 wxHtmlListBox::wxHtmlListBox(wxWindow
*parent
,
230 const wxString
& name
)
231 : wxHtmlWindowMouseHelper(this)
235 (void)Create(parent
, id
, pos
, size
, style
, name
);
238 void wxHtmlListBox::Init()
241 m_htmlRendStyle
= new wxHtmlListBoxStyle(*this);
242 m_cache
= new wxHtmlListBoxCache
;
245 bool wxHtmlListBox::Create(wxWindow
*parent
,
250 const wxString
& name
)
252 return wxVListBox::Create(parent
, id
, pos
, size
, style
, name
);
255 wxHtmlListBox::~wxHtmlListBox()
261 delete m_htmlParser
->GetDC();
265 delete m_htmlRendStyle
;
268 // ----------------------------------------------------------------------------
269 // wxHtmlListBox appearance
270 // ----------------------------------------------------------------------------
273 wxHtmlListBox::GetSelectedTextColour(const wxColour
& WXUNUSED(colFg
)) const
279 wxHtmlListBox::GetSelectedTextBgColour(const wxColour
& WXUNUSED(colBg
)) const
281 return GetSelectionBackground();
284 // ----------------------------------------------------------------------------
285 // wxHtmlListBox items markup
286 // ----------------------------------------------------------------------------
288 wxString
wxHtmlListBox::OnGetItemMarkup(size_t n
) const
290 // we don't even need to wrap the value returned by OnGetItem() inside
291 // "<html><body>" and "</body></html>" because wxHTML can parse it even
292 // without these tags
296 // ----------------------------------------------------------------------------
297 // wxHtmlListBox cache handling
298 // ----------------------------------------------------------------------------
300 void wxHtmlListBox::CacheItem(size_t n
) const
302 if ( !m_cache
->Has(n
) )
306 wxHtmlListBox
*self
= wxConstCast(this, wxHtmlListBox
);
308 self
->m_htmlParser
= new wxHtmlWinParser(self
);
309 m_htmlParser
->SetDC(new wxClientDC(self
));
310 m_htmlParser
->SetFS(&self
->m_filesystem
);
313 m_htmlParser
->SetInputEncoding(GetFont().GetEncoding());
315 // use system's default GUI font by default:
316 m_htmlParser
->SetStandardFonts();
319 wxHtmlContainerCell
*cell
= (wxHtmlContainerCell
*)m_htmlParser
->
320 Parse(OnGetItemMarkup(n
));
321 wxCHECK_RET( cell
, _T("wxHtmlParser::Parse() returned NULL?") );
323 // set the cell's ID to item's index so that CellCoordsToPhysical()
324 // can quickly find the item:
325 cell
->SetId(wxString::Format(_T("%lu"), (unsigned long)n
));
327 cell
->Layout(GetClientSize().x
- 2*GetMargins().x
);
329 m_cache
->Store(n
, cell
);
333 void wxHtmlListBox::OnSize(wxSizeEvent
& event
)
335 // we need to relayout all the cached cells
341 void wxHtmlListBox::RefreshRow(size_t line
)
343 m_cache
->InvalidateRange(line
, line
);
345 wxVListBox::RefreshRow(line
);
348 void wxHtmlListBox::RefreshRows(size_t from
, size_t to
)
350 m_cache
->InvalidateRange(from
, to
);
352 wxVListBox::RefreshRows(from
, to
);
355 void wxHtmlListBox::RefreshAll()
359 wxVListBox::RefreshAll();
362 void wxHtmlListBox::SetItemCount(size_t count
)
364 // the items are going to change, forget the old ones
367 wxVListBox::SetItemCount(count
);
370 // ----------------------------------------------------------------------------
371 // wxHtmlListBox implementation of wxVListBox pure virtuals
372 // ----------------------------------------------------------------------------
375 wxHtmlListBox::OnDrawBackground(wxDC
& dc
, const wxRect
& rect
, size_t n
) const
379 if ( DoDrawSolidBackground
381 GetSelectedTextBgColour(GetBackgroundColour()),
389 //else: no custom selection background colour, use base class version
392 wxVListBox::OnDrawBackground(dc
, rect
, n
);
395 void wxHtmlListBox::OnDrawItem(wxDC
& dc
, const wxRect
& rect
, size_t n
) const
399 wxHtmlCell
*cell
= m_cache
->Get(n
);
400 wxCHECK_RET( cell
, _T("this cell should be cached!") );
402 wxHtmlRenderingInfo htmlRendInfo
;
404 // draw the selected cell in selected state ourselves if we're using custom
405 // colours (to test for this, check the callbacks by passing them any dummy
406 // (but valid, to avoid asserts) colour):
407 if ( IsSelected(n
) &&
408 (GetSelectedTextColour(*wxBLACK
).IsOk() ||
409 GetSelectedTextBgColour(*wxWHITE
).IsOk()) )
411 wxHtmlSelection htmlSel
;
412 htmlSel
.Set(wxPoint(0,0), cell
, wxPoint(INT_MAX
, INT_MAX
), cell
);
413 htmlRendInfo
.SetSelection(&htmlSel
);
414 htmlRendInfo
.SetStyle(m_htmlRendStyle
);
415 htmlRendInfo
.GetState().SetSelectionState(wxHTML_SEL_IN
);
417 //else: normal item or selected item with default colours, its background
418 // was already taken care of in the base class
420 // note that we can't stop drawing exactly at the window boundary as then
421 // even the visible cells part could be not drawn, so always draw the
424 rect
.x
+ CELL_BORDER
, rect
.y
+ CELL_BORDER
,
425 0, INT_MAX
, htmlRendInfo
);
428 wxCoord
wxHtmlListBox::OnMeasureItem(size_t n
) const
432 wxHtmlCell
*cell
= m_cache
->Get(n
);
433 wxCHECK_MSG( cell
, 0, _T("this cell should be cached!") );
435 return cell
->GetHeight() + cell
->GetDescent() + 4;
438 // ----------------------------------------------------------------------------
439 // wxHtmlListBox implementation of wxHtmlListBoxWinInterface
440 // ----------------------------------------------------------------------------
442 void wxHtmlListBox::SetHTMLWindowTitle(const wxString
& WXUNUSED(title
))
447 void wxHtmlListBox::OnHTMLLinkClicked(const wxHtmlLinkInfo
& link
)
449 OnLinkClicked(GetItemForCell(link
.GetHtmlCell()), link
);
452 void wxHtmlListBox::OnLinkClicked(size_t WXUNUSED(n
),
453 const wxHtmlLinkInfo
& link
)
455 wxHtmlLinkEvent
event(GetId(), link
);
456 GetEventHandler()->ProcessEvent(event
);
460 wxHtmlListBox::OnHTMLOpeningURL(wxHtmlURLType
WXUNUSED(type
),
461 const wxString
& WXUNUSED(url
),
462 wxString
*WXUNUSED(redirect
)) const
467 wxPoint
wxHtmlListBox::HTMLCoordsToWindow(wxHtmlCell
*cell
,
468 const wxPoint
& pos
) const
470 return CellCoordsToPhysical(pos
, cell
);
473 wxWindow
* wxHtmlListBox::GetHTMLWindow() { return this; }
475 wxColour
wxHtmlListBox::GetHTMLBackgroundColour() const
477 return GetBackgroundColour();
480 void wxHtmlListBox::SetHTMLBackgroundColour(const wxColour
& WXUNUSED(clr
))
485 void wxHtmlListBox::SetHTMLBackgroundImage(const wxBitmap
& WXUNUSED(bmpBg
))
490 void wxHtmlListBox::SetHTMLStatusText(const wxString
& WXUNUSED(text
))
495 wxCursor
wxHtmlListBox::GetHTMLCursor(HTMLCursor type
) const
497 // we don't want to show text selection cursor in listboxes
498 if (type
== HTMLCursor_Text
)
499 return wxHtmlWindow::GetDefaultHTMLCursor(HTMLCursor_Default
);
501 // in all other cases, use the same cursor as wxHtmlWindow:
502 return wxHtmlWindow::GetDefaultHTMLCursor(type
);
505 // ----------------------------------------------------------------------------
506 // wxHtmlListBox handling of HTML links
507 // ----------------------------------------------------------------------------
509 wxPoint
wxHtmlListBox::GetRootCellCoords(size_t n
) const
511 wxPoint
pos(CELL_BORDER
, CELL_BORDER
);
513 pos
.y
+= GetRowsHeight(GetVisibleBegin(), n
);
517 bool wxHtmlListBox::PhysicalCoordsToCell(wxPoint
& pos
, wxHtmlCell
*& cell
) const
519 int n
= VirtualHitTest(pos
.y
);
520 if ( n
== wxNOT_FOUND
)
523 // convert mouse coordinates to coords relative to item's wxHtmlCell:
524 pos
-= GetRootCellCoords(n
);
527 cell
= m_cache
->Get(n
);
532 size_t wxHtmlListBox::GetItemForCell(const wxHtmlCell
*cell
) const
534 wxCHECK_MSG( cell
, 0, _T("no cell") );
536 cell
= cell
->GetRootCell();
538 wxCHECK_MSG( cell
, 0, _T("no root cell") );
540 // the cell's ID contains item index, see CacheItem():
542 if ( !cell
->GetId().ToULong(&n
) )
544 wxFAIL_MSG( _T("unexpected root cell's ID") );
552 wxHtmlListBox::CellCoordsToPhysical(const wxPoint
& pos
, wxHtmlCell
*cell
) const
554 return pos
+ GetRootCellCoords(GetItemForCell(cell
));
557 void wxHtmlListBox::OnInternalIdle()
559 wxVListBox::OnInternalIdle();
561 if ( wxHtmlWindowMouseHelper::DidMouseMove() )
563 wxPoint pos
= ScreenToClient(wxGetMousePosition());
566 if ( !PhysicalCoordsToCell(pos
, cell
) )
569 wxHtmlWindowMouseHelper::HandleIdle(cell
, pos
);
573 void wxHtmlListBox::OnMouseMove(wxMouseEvent
& event
)
575 wxHtmlWindowMouseHelper::HandleMouseMoved();
579 void wxHtmlListBox::OnLeftDown(wxMouseEvent
& event
)
581 wxPoint pos
= event
.GetPosition();
584 if ( !PhysicalCoordsToCell(pos
, cell
) )
590 if ( !wxHtmlWindowMouseHelper::HandleMouseClick(cell
, pos
, event
) )
592 // no link was clicked, so let the listbox code handle the click (e.g.
593 // by selecting another item in the list):
599 // ----------------------------------------------------------------------------
600 // wxSimpleHtmlListBox
601 // ----------------------------------------------------------------------------
603 bool wxSimpleHtmlListBox::Create(wxWindow
*parent
, wxWindowID id
,
606 int n
, const wxString choices
[],
608 const wxValidator
& validator
,
609 const wxString
& name
)
611 if (!wxHtmlListBox::Create(parent
, id
, pos
, size
, style
, name
))
615 SetValidator(validator
);
623 bool wxSimpleHtmlListBox::Create(wxWindow
*parent
, wxWindowID id
,
626 const wxArrayString
& choices
,
628 const wxValidator
& validator
,
629 const wxString
& name
)
631 if (!wxHtmlListBox::Create(parent
, id
, pos
, size
, style
, name
))
635 SetValidator(validator
);
643 wxSimpleHtmlListBox::~wxSimpleHtmlListBox()
645 wxItemContainer::Clear();
648 void wxSimpleHtmlListBox::DoClear()
650 wxASSERT(m_items
.GetCount() == m_HTMLclientData
.GetCount());
653 m_HTMLclientData
.Clear();
658 void wxSimpleHtmlListBox::Clear()
663 void wxSimpleHtmlListBox::DoDeleteOneItem(unsigned int n
)
667 m_HTMLclientData
.RemoveAt(n
);
672 int wxSimpleHtmlListBox::DoInsertItems(const wxArrayStringsAdapter
& items
,
675 wxClientDataType type
)
677 const unsigned int count
= items
.GetCount();
679 m_items
.Insert(wxEmptyString
, pos
, count
);
680 m_HTMLclientData
.Insert(NULL
, pos
, count
);
682 for ( unsigned int i
= 0; i
< count
; ++i
, ++pos
)
684 m_items
[pos
] = items
[i
];
685 AssignNewItemClientData(pos
, clientData
, i
, type
);
693 void wxSimpleHtmlListBox::SetString(unsigned int n
, const wxString
& s
)
695 wxCHECK_RET( IsValid(n
),
696 wxT("invalid index in wxSimpleHtmlListBox::SetString") );
702 wxString
wxSimpleHtmlListBox::GetString(unsigned int n
) const
704 wxCHECK_MSG( IsValid(n
), wxEmptyString
,
705 wxT("invalid index in wxSimpleHtmlListBox::GetString") );
710 void wxSimpleHtmlListBox::UpdateCount()
712 wxASSERT(m_items
.GetCount() == m_HTMLclientData
.GetCount());
713 wxHtmlListBox::SetItemCount(m_items
.GetCount());
715 // very small optimization: if you need to add lot of items to
716 // a wxSimpleHtmlListBox be sure to use the
717 // wxSimpleHtmlListBox::Append(const wxArrayString&) method instead!
718 if (!this->IsFrozen())