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 // Licence: wxWindows licence
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;
75 for ( size_t n
= 0; n
< SIZE
; n
++ )
77 m_items
[n
] = (size_t)-1;
86 for ( size_t n
= 0; n
< SIZE
; n
++ )
92 // completely invalidate the cache
95 for ( size_t n
= 0; n
< SIZE
; n
++ )
101 // return the cached cell for this index or NULL if none
102 wxHtmlCell
*Get(size_t item
) const
104 for ( size_t n
= 0; n
< SIZE
; n
++ )
106 if ( m_items
[n
] == item
)
113 // returns true if we already have this item cached
114 bool Has(size_t item
) const { return Get(item
) != NULL
; }
116 // ensure that the item is cached
117 void Store(size_t item
, wxHtmlCell
*cell
)
119 delete m_cells
[m_next
];
120 m_cells
[m_next
] = cell
;
121 m_items
[m_next
] = item
;
123 // advance to the next item wrapping around if there are no more
124 if ( ++m_next
== SIZE
)
128 // forget the cached value of the item(s) between the given ones (inclusive)
129 void InvalidateRange(size_t from
, size_t to
)
131 for ( size_t n
= 0; n
< SIZE
; n
++ )
133 if ( m_items
[n
] >= from
&& m_items
[n
] <= to
)
141 // the max number of the items we cache
144 // the index of the LRU (oldest) cell
147 // the parsed representation of the cached item or NULL
148 wxHtmlCell
*m_cells
[SIZE
];
150 // the index of the currently cached item (only valid if m_cells != NULL)
151 size_t m_items
[SIZE
];
154 // ----------------------------------------------------------------------------
155 // wxHtmlListBoxStyle
156 // ----------------------------------------------------------------------------
158 // just forward wxDefaultHtmlRenderingStyle callbacks to the main class so that
159 // they could be overridden by the user code
160 class wxHtmlListBoxStyle
: public wxDefaultHtmlRenderingStyle
163 wxHtmlListBoxStyle(const wxHtmlListBox
& hlbox
) : m_hlbox(hlbox
) { }
165 virtual wxColour
GetSelectedTextColour(const wxColour
& colFg
)
167 // by default wxHtmlListBox doesn't implement GetSelectedTextColour()
168 // and returns wxNullColour from it, so use the default HTML colour for
170 wxColour col
= m_hlbox
.GetSelectedTextColour(colFg
);
173 col
= wxDefaultHtmlRenderingStyle::GetSelectedTextColour(colFg
);
179 virtual wxColour
GetSelectedTextBgColour(const wxColour
& colBg
)
181 wxColour col
= m_hlbox
.GetSelectedTextBgColour(colBg
);
184 col
= wxDefaultHtmlRenderingStyle::GetSelectedTextBgColour(colBg
);
191 const wxHtmlListBox
& m_hlbox
;
193 wxDECLARE_NO_COPY_CLASS(wxHtmlListBoxStyle
);
196 // ----------------------------------------------------------------------------
198 // ----------------------------------------------------------------------------
200 BEGIN_EVENT_TABLE(wxHtmlListBox
, wxVListBox
)
201 EVT_SIZE(wxHtmlListBox::OnSize
)
202 EVT_MOTION(wxHtmlListBox::OnMouseMove
)
203 EVT_LEFT_DOWN(wxHtmlListBox::OnLeftDown
)
206 // ============================================================================
208 // ============================================================================
210 IMPLEMENT_ABSTRACT_CLASS(wxHtmlListBox
, wxVListBox
)
213 // ----------------------------------------------------------------------------
214 // wxHtmlListBox creation
215 // ----------------------------------------------------------------------------
217 wxHtmlListBox::wxHtmlListBox()
218 : wxHtmlWindowMouseHelper(this)
223 // normal constructor which calls Create() internally
224 wxHtmlListBox::wxHtmlListBox(wxWindow
*parent
,
229 const wxString
& name
)
230 : wxHtmlWindowMouseHelper(this)
234 (void)Create(parent
, id
, pos
, size
, style
, name
);
237 void wxHtmlListBox::Init()
240 m_htmlRendStyle
= new wxHtmlListBoxStyle(*this);
241 m_cache
= new wxHtmlListBoxCache
;
244 bool wxHtmlListBox::Create(wxWindow
*parent
,
249 const wxString
& name
)
251 return wxVListBox::Create(parent
, id
, pos
, size
, style
, name
);
254 wxHtmlListBox::~wxHtmlListBox()
260 delete m_htmlParser
->GetDC();
264 delete m_htmlRendStyle
;
267 // ----------------------------------------------------------------------------
268 // wxHtmlListBox appearance
269 // ----------------------------------------------------------------------------
272 wxHtmlListBox::GetSelectedTextColour(const wxColour
& WXUNUSED(colFg
)) const
278 wxHtmlListBox::GetSelectedTextBgColour(const wxColour
& WXUNUSED(colBg
)) const
280 return GetSelectionBackground();
283 // ----------------------------------------------------------------------------
284 // wxHtmlListBox items markup
285 // ----------------------------------------------------------------------------
287 wxString
wxHtmlListBox::OnGetItemMarkup(size_t n
) const
289 // we don't even need to wrap the value returned by OnGetItem() inside
290 // "<html><body>" and "</body></html>" because wxHTML can parse it even
291 // without these tags
295 // ----------------------------------------------------------------------------
296 // wxHtmlListBox cache handling
297 // ----------------------------------------------------------------------------
299 void wxHtmlListBox::CacheItem(size_t n
) const
301 if ( !m_cache
->Has(n
) )
305 wxHtmlListBox
*self
= wxConstCast(this, wxHtmlListBox
);
307 self
->m_htmlParser
= new wxHtmlWinParser(self
);
308 m_htmlParser
->SetDC(new wxClientDC(self
));
309 m_htmlParser
->SetFS(&self
->m_filesystem
);
312 m_htmlParser
->SetInputEncoding(GetFont().GetEncoding());
314 // use system's default GUI font by default:
315 m_htmlParser
->SetStandardFonts();
318 wxHtmlContainerCell
*cell
= (wxHtmlContainerCell
*)m_htmlParser
->
319 Parse(OnGetItemMarkup(n
));
320 wxCHECK_RET( cell
, wxT("wxHtmlParser::Parse() returned NULL?") );
322 // set the cell's ID to item's index so that CellCoordsToPhysical()
323 // can quickly find the item:
324 cell
->SetId(wxString::Format(wxT("%lu"), (unsigned long)n
));
326 cell
->Layout(GetClientSize().x
- 2*GetMargins().x
);
328 m_cache
->Store(n
, cell
);
332 void wxHtmlListBox::OnSize(wxSizeEvent
& event
)
334 // we need to relayout all the cached cells
340 void wxHtmlListBox::RefreshRow(size_t line
)
342 m_cache
->InvalidateRange(line
, line
);
344 wxVListBox::RefreshRow(line
);
347 void wxHtmlListBox::RefreshRows(size_t from
, size_t to
)
349 m_cache
->InvalidateRange(from
, to
);
351 wxVListBox::RefreshRows(from
, to
);
354 void wxHtmlListBox::RefreshAll()
358 wxVListBox::RefreshAll();
361 void wxHtmlListBox::SetItemCount(size_t count
)
363 // the items are going to change, forget the old ones
366 wxVListBox::SetItemCount(count
);
369 // ----------------------------------------------------------------------------
370 // wxHtmlListBox implementation of wxVListBox pure virtuals
371 // ----------------------------------------------------------------------------
374 wxHtmlListBox::OnDrawBackground(wxDC
& dc
, const wxRect
& rect
, size_t n
) const
378 if ( DoDrawSolidBackground
380 GetSelectedTextBgColour(GetBackgroundColour()),
388 //else: no custom selection background colour, use base class version
391 wxVListBox::OnDrawBackground(dc
, rect
, n
);
394 void wxHtmlListBox::OnDrawItem(wxDC
& dc
, const wxRect
& rect
, size_t n
) const
398 wxHtmlCell
*cell
= m_cache
->Get(n
);
399 wxCHECK_RET( cell
, wxT("this cell should be cached!") );
401 wxHtmlRenderingInfo htmlRendInfo
;
403 // draw the selected cell in selected state ourselves if we're using custom
404 // colours (to test for this, check the callbacks by passing them any dummy
405 // (but valid, to avoid asserts) colour):
406 if ( IsSelected(n
) &&
407 (GetSelectedTextColour(*wxBLACK
).IsOk() ||
408 GetSelectedTextBgColour(*wxWHITE
).IsOk()) )
410 wxHtmlSelection htmlSel
;
411 htmlSel
.Set(wxPoint(0,0), cell
, wxPoint(INT_MAX
, INT_MAX
), cell
);
412 htmlRendInfo
.SetSelection(&htmlSel
);
413 htmlRendInfo
.SetStyle(m_htmlRendStyle
);
414 htmlRendInfo
.GetState().SetSelectionState(wxHTML_SEL_IN
);
416 //else: normal item or selected item with default colours, its background
417 // was already taken care of in the base class
419 // note that we can't stop drawing exactly at the window boundary as then
420 // even the visible cells part could be not drawn, so always draw the
423 rect
.x
+ CELL_BORDER
, rect
.y
+ CELL_BORDER
,
424 0, INT_MAX
, htmlRendInfo
);
427 wxCoord
wxHtmlListBox::OnMeasureItem(size_t n
) const
431 wxHtmlCell
*cell
= m_cache
->Get(n
);
432 wxCHECK_MSG( cell
, 0, wxT("this cell should be cached!") );
434 return cell
->GetHeight() + cell
->GetDescent() + 4;
437 // ----------------------------------------------------------------------------
438 // wxHtmlListBox implementation of wxHtmlListBoxWinInterface
439 // ----------------------------------------------------------------------------
441 void wxHtmlListBox::SetHTMLWindowTitle(const wxString
& WXUNUSED(title
))
446 void wxHtmlListBox::OnHTMLLinkClicked(const wxHtmlLinkInfo
& link
)
448 OnLinkClicked(GetItemForCell(link
.GetHtmlCell()), link
);
451 void wxHtmlListBox::OnLinkClicked(size_t WXUNUSED(n
),
452 const wxHtmlLinkInfo
& link
)
454 wxHtmlLinkEvent
event(GetId(), link
);
455 GetEventHandler()->ProcessEvent(event
);
459 wxHtmlListBox::OnHTMLOpeningURL(wxHtmlURLType
WXUNUSED(type
),
460 const wxString
& WXUNUSED(url
),
461 wxString
*WXUNUSED(redirect
)) const
466 wxPoint
wxHtmlListBox::HTMLCoordsToWindow(wxHtmlCell
*cell
,
467 const wxPoint
& pos
) const
469 return CellCoordsToPhysical(pos
, cell
);
472 wxWindow
* wxHtmlListBox::GetHTMLWindow() { return this; }
474 wxColour
wxHtmlListBox::GetHTMLBackgroundColour() const
476 return GetBackgroundColour();
479 void wxHtmlListBox::SetHTMLBackgroundColour(const wxColour
& WXUNUSED(clr
))
484 void wxHtmlListBox::SetHTMLBackgroundImage(const wxBitmap
& WXUNUSED(bmpBg
))
489 void wxHtmlListBox::SetHTMLStatusText(const wxString
& WXUNUSED(text
))
494 wxCursor
wxHtmlListBox::GetHTMLCursor(HTMLCursor type
) const
496 // we don't want to show text selection cursor in listboxes
497 if (type
== HTMLCursor_Text
)
498 return wxHtmlWindow::GetDefaultHTMLCursor(HTMLCursor_Default
);
500 // in all other cases, use the same cursor as wxHtmlWindow:
501 return wxHtmlWindow::GetDefaultHTMLCursor(type
);
504 // ----------------------------------------------------------------------------
505 // wxHtmlListBox handling of HTML links
506 // ----------------------------------------------------------------------------
508 wxPoint
wxHtmlListBox::GetRootCellCoords(size_t n
) const
510 wxPoint
pos(CELL_BORDER
, CELL_BORDER
);
512 pos
.y
+= GetRowsHeight(GetVisibleBegin(), n
);
516 bool wxHtmlListBox::PhysicalCoordsToCell(wxPoint
& pos
, wxHtmlCell
*& cell
) const
518 int n
= VirtualHitTest(pos
.y
);
519 if ( n
== wxNOT_FOUND
)
522 // convert mouse coordinates to coords relative to item's wxHtmlCell:
523 pos
-= GetRootCellCoords(n
);
526 cell
= m_cache
->Get(n
);
531 size_t wxHtmlListBox::GetItemForCell(const wxHtmlCell
*cell
) const
533 wxCHECK_MSG( cell
, 0, wxT("no cell") );
535 cell
= cell
->GetRootCell();
537 wxCHECK_MSG( cell
, 0, wxT("no root cell") );
539 // the cell's ID contains item index, see CacheItem():
541 if ( !cell
->GetId().ToULong(&n
) )
543 wxFAIL_MSG( wxT("unexpected root cell's ID") );
551 wxHtmlListBox::CellCoordsToPhysical(const wxPoint
& pos
, wxHtmlCell
*cell
) const
553 return pos
+ GetRootCellCoords(GetItemForCell(cell
));
556 void wxHtmlListBox::OnInternalIdle()
558 wxVListBox::OnInternalIdle();
560 if ( wxHtmlWindowMouseHelper::DidMouseMove() )
562 wxPoint pos
= ScreenToClient(wxGetMousePosition());
565 if ( !PhysicalCoordsToCell(pos
, cell
) )
568 wxHtmlWindowMouseHelper::HandleIdle(cell
, pos
);
572 void wxHtmlListBox::OnMouseMove(wxMouseEvent
& event
)
574 wxHtmlWindowMouseHelper::HandleMouseMoved();
578 void wxHtmlListBox::OnLeftDown(wxMouseEvent
& event
)
580 wxPoint pos
= event
.GetPosition();
583 if ( !PhysicalCoordsToCell(pos
, cell
) )
589 if ( !wxHtmlWindowMouseHelper::HandleMouseClick(cell
, pos
, event
) )
591 // no link was clicked, so let the listbox code handle the click (e.g.
592 // by selecting another item in the list):
598 // ----------------------------------------------------------------------------
599 // wxSimpleHtmlListBox
600 // ----------------------------------------------------------------------------
602 IMPLEMENT_ABSTRACT_CLASS(wxSimpleHtmlListBox
, wxHtmlListBox
)
605 bool wxSimpleHtmlListBox::Create(wxWindow
*parent
, wxWindowID id
,
608 int n
, const wxString choices
[],
610 const wxValidator
& validator
,
611 const wxString
& name
)
613 if (!wxHtmlListBox::Create(parent
, id
, pos
, size
, style
, name
))
617 SetValidator(validator
);
625 bool wxSimpleHtmlListBox::Create(wxWindow
*parent
, wxWindowID id
,
628 const wxArrayString
& choices
,
630 const wxValidator
& validator
,
631 const wxString
& name
)
633 if (!wxHtmlListBox::Create(parent
, id
, pos
, size
, style
, name
))
637 SetValidator(validator
);
645 wxSimpleHtmlListBox::~wxSimpleHtmlListBox()
647 wxItemContainer::Clear();
650 void wxSimpleHtmlListBox::DoClear()
652 wxASSERT(m_items
.GetCount() == m_HTMLclientData
.GetCount());
655 m_HTMLclientData
.Clear();
660 void wxSimpleHtmlListBox::Clear()
665 void wxSimpleHtmlListBox::DoDeleteOneItem(unsigned int n
)
669 m_HTMLclientData
.RemoveAt(n
);
674 int wxSimpleHtmlListBox::DoInsertItems(const wxArrayStringsAdapter
& items
,
677 wxClientDataType type
)
679 const unsigned int count
= items
.GetCount();
681 m_items
.Insert(wxEmptyString
, pos
, count
);
682 m_HTMLclientData
.Insert(NULL
, pos
, count
);
684 for ( unsigned int i
= 0; i
< count
; ++i
, ++pos
)
686 m_items
[pos
] = items
[i
];
687 AssignNewItemClientData(pos
, clientData
, i
, type
);
695 void wxSimpleHtmlListBox::SetString(unsigned int n
, const wxString
& s
)
697 wxCHECK_RET( IsValid(n
),
698 wxT("invalid index in wxSimpleHtmlListBox::SetString") );
704 wxString
wxSimpleHtmlListBox::GetString(unsigned int n
) const
706 wxCHECK_MSG( IsValid(n
), wxEmptyString
,
707 wxT("invalid index in wxSimpleHtmlListBox::GetString") );
712 void wxSimpleHtmlListBox::UpdateCount()
714 wxASSERT(m_items
.GetCount() == m_HTMLclientData
.GetCount());
715 wxHtmlListBox::SetItemCount(m_items
.GetCount());
717 // very small optimization: if you need to add lot of items to
718 // a wxSimpleHtmlListBox be sure to use the
719 // wxSimpleHtmlListBox::Append(const wxArrayString&) method instead!
720 if (!this->IsFrozen())