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 wxChar wxHtmlListBoxNameStr
[] = wxT("htmlListBox");
50 const wxChar wxSimpleHtmlListBoxNameStr
[] = wxT("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 return m_hlbox
.GetSelectedTextColour(colFg
);
171 virtual wxColour
GetSelectedTextBgColour(const wxColour
& colBg
)
173 return m_hlbox
.GetSelectedTextBgColour(colBg
);
177 const wxHtmlListBox
& m_hlbox
;
179 DECLARE_NO_COPY_CLASS(wxHtmlListBoxStyle
)
182 // ----------------------------------------------------------------------------
184 // ----------------------------------------------------------------------------
186 BEGIN_EVENT_TABLE(wxHtmlListBox
, wxVListBox
)
187 EVT_SIZE(wxHtmlListBox::OnSize
)
188 EVT_MOTION(wxHtmlListBox::OnMouseMove
)
189 EVT_LEFT_DOWN(wxHtmlListBox::OnLeftDown
)
192 // ============================================================================
194 // ============================================================================
196 IMPLEMENT_ABSTRACT_CLASS(wxHtmlListBox
, wxVListBox
)
199 // ----------------------------------------------------------------------------
200 // wxHtmlListBox creation
201 // ----------------------------------------------------------------------------
203 wxHtmlListBox::wxHtmlListBox()
204 : wxHtmlWindowMouseHelper(this)
209 // normal constructor which calls Create() internally
210 wxHtmlListBox::wxHtmlListBox(wxWindow
*parent
,
215 const wxString
& name
)
216 : wxHtmlWindowMouseHelper(this)
220 (void)Create(parent
, id
, pos
, size
, style
, name
);
223 void wxHtmlListBox::Init()
226 m_htmlRendStyle
= new wxHtmlListBoxStyle(*this);
227 m_cache
= new wxHtmlListBoxCache
;
230 bool wxHtmlListBox::Create(wxWindow
*parent
,
235 const wxString
& name
)
237 return wxVListBox::Create(parent
, id
, pos
, size
, style
, name
);
240 wxHtmlListBox::~wxHtmlListBox()
246 delete m_htmlParser
->GetDC();
250 delete m_htmlRendStyle
;
253 // ----------------------------------------------------------------------------
254 // wxHtmlListBox appearance
255 // ----------------------------------------------------------------------------
257 wxColour
wxHtmlListBox::GetSelectedTextColour(const wxColour
& colFg
) const
259 return m_htmlRendStyle
->
260 wxDefaultHtmlRenderingStyle::GetSelectedTextColour(colFg
);
264 wxHtmlListBox::GetSelectedTextBgColour(const wxColour
& WXUNUSED(colBg
)) const
266 return GetSelectionBackground();
269 // ----------------------------------------------------------------------------
270 // wxHtmlListBox items markup
271 // ----------------------------------------------------------------------------
273 wxString
wxHtmlListBox::OnGetItemMarkup(size_t n
) const
275 // we don't even need to wrap the value returned by OnGetItem() inside
276 // "<html><body>" and "</body></html>" because wxHTML can parse it even
277 // without these tags
281 // ----------------------------------------------------------------------------
282 // wxHtmlListBox cache handling
283 // ----------------------------------------------------------------------------
285 void wxHtmlListBox::CacheItem(size_t n
) const
287 if ( !m_cache
->Has(n
) )
291 wxHtmlListBox
*self
= wxConstCast(this, wxHtmlListBox
);
293 self
->m_htmlParser
= new wxHtmlWinParser(self
);
294 m_htmlParser
->SetDC(new wxClientDC(self
));
295 m_htmlParser
->SetFS(&self
->m_filesystem
);
298 m_htmlParser
->SetInputEncoding(GetFont().GetEncoding());
300 // use system's default GUI font by default:
301 m_htmlParser
->SetStandardFonts();
304 wxHtmlContainerCell
*cell
= (wxHtmlContainerCell
*)m_htmlParser
->
305 Parse(OnGetItemMarkup(n
));
306 wxCHECK_RET( cell
, _T("wxHtmlParser::Parse() returned NULL?") );
308 // set the cell's ID to item's index so that CellCoordsToPhysical()
309 // can quickly find the item:
310 cell
->SetId(wxString::Format(_T("%lu"), (unsigned long)n
));
312 cell
->Layout(GetClientSize().x
- 2*GetMargins().x
);
314 m_cache
->Store(n
, cell
);
318 void wxHtmlListBox::OnSize(wxSizeEvent
& event
)
320 // we need to relayout all the cached cells
326 void wxHtmlListBox::RefreshLine(size_t line
)
328 m_cache
->InvalidateRange(line
, line
);
330 wxVListBox::RefreshLine(line
);
333 void wxHtmlListBox::RefreshLines(size_t from
, size_t to
)
335 m_cache
->InvalidateRange(from
, to
);
337 wxVListBox::RefreshLines(from
, to
);
340 void wxHtmlListBox::RefreshAll()
344 wxVListBox::RefreshAll();
347 void wxHtmlListBox::SetItemCount(size_t count
)
349 // the items are going to change, forget the old ones
352 wxVListBox::SetItemCount(count
);
355 // ----------------------------------------------------------------------------
356 // wxHtmlListBox implementation of wxVListBox pure virtuals
357 // ----------------------------------------------------------------------------
359 void wxHtmlListBox::OnDrawItem(wxDC
& dc
, const wxRect
& rect
, size_t n
) const
363 wxHtmlCell
*cell
= m_cache
->Get(n
);
364 wxCHECK_RET( cell
, _T("this cell should be cached!") );
366 wxHtmlRenderingInfo htmlRendInfo
;
368 // draw the selected cell in selected state
371 wxHtmlSelection htmlSel
;
372 htmlSel
.Set(wxPoint(0,0), cell
, wxPoint(INT_MAX
, INT_MAX
), cell
);
373 htmlRendInfo
.SetSelection(&htmlSel
);
374 if ( m_htmlRendStyle
)
375 htmlRendInfo
.SetStyle(m_htmlRendStyle
);
376 htmlRendInfo
.GetState().SetSelectionState(wxHTML_SEL_IN
);
379 // note that we can't stop drawing exactly at the window boundary as then
380 // even the visible cells part could be not drawn, so always draw the
383 rect
.x
+ CELL_BORDER
, rect
.y
+ CELL_BORDER
,
384 0, INT_MAX
, htmlRendInfo
);
387 wxCoord
wxHtmlListBox::OnMeasureItem(size_t n
) const
391 wxHtmlCell
*cell
= m_cache
->Get(n
);
392 wxCHECK_MSG( cell
, 0, _T("this cell should be cached!") );
394 return cell
->GetHeight() + cell
->GetDescent() + 4;
397 // ----------------------------------------------------------------------------
398 // wxHtmlListBox implementation of wxHtmlListBoxWinInterface
399 // ----------------------------------------------------------------------------
401 void wxHtmlListBox::SetHTMLWindowTitle(const wxString
& WXUNUSED(title
))
406 void wxHtmlListBox::OnHTMLLinkClicked(const wxHtmlLinkInfo
& link
)
408 OnLinkClicked(GetItemForCell(link
.GetHtmlCell()), link
);
411 void wxHtmlListBox::OnLinkClicked(size_t WXUNUSED(n
),
412 const wxHtmlLinkInfo
& link
)
414 wxHtmlLinkEvent
event(GetId(), link
);
415 GetEventHandler()->ProcessEvent(event
);
419 wxHtmlListBox::OnHTMLOpeningURL(wxHtmlURLType
WXUNUSED(type
),
420 const wxString
& WXUNUSED(url
),
421 wxString
*WXUNUSED(redirect
)) const
426 wxPoint
wxHtmlListBox::HTMLCoordsToWindow(wxHtmlCell
*cell
,
427 const wxPoint
& pos
) const
429 return CellCoordsToPhysical(pos
, cell
);
432 wxWindow
* wxHtmlListBox::GetHTMLWindow() { return this; }
434 wxColour
wxHtmlListBox::GetHTMLBackgroundColour() const
436 return GetBackgroundColour();
439 void wxHtmlListBox::SetHTMLBackgroundColour(const wxColour
& WXUNUSED(clr
))
444 void wxHtmlListBox::SetHTMLBackgroundImage(const wxBitmap
& WXUNUSED(bmpBg
))
449 void wxHtmlListBox::SetHTMLStatusText(const wxString
& WXUNUSED(text
))
454 wxCursor
wxHtmlListBox::GetHTMLCursor(HTMLCursor type
) const
456 // we don't want to show text selection cursor in listboxes
457 if (type
== HTMLCursor_Text
)
458 return wxHtmlWindow::GetDefaultHTMLCursor(HTMLCursor_Default
);
460 // in all other cases, use the same cursor as wxHtmlWindow:
461 return wxHtmlWindow::GetDefaultHTMLCursor(type
);
464 // ----------------------------------------------------------------------------
465 // wxHtmlListBox handling of HTML links
466 // ----------------------------------------------------------------------------
468 wxPoint
wxHtmlListBox::GetRootCellCoords(size_t n
) const
470 wxPoint
pos(CELL_BORDER
, CELL_BORDER
);
472 pos
.y
+= GetLinesHeight(GetFirstVisibleLine(), n
);
476 bool wxHtmlListBox::PhysicalCoordsToCell(wxPoint
& pos
, wxHtmlCell
*& cell
) const
478 int n
= HitTest(pos
);
479 if ( n
== wxNOT_FOUND
)
482 // convert mouse coordinates to coords relative to item's wxHtmlCell:
483 pos
-= GetRootCellCoords(n
);
486 cell
= m_cache
->Get(n
);
491 size_t wxHtmlListBox::GetItemForCell(const wxHtmlCell
*cell
) const
493 wxCHECK_MSG( cell
, 0, _T("no cell") );
495 cell
= cell
->GetRootCell();
497 wxCHECK_MSG( cell
, 0, _T("no root cell") );
499 // the cell's ID contains item index, see CacheItem():
501 if ( !cell
->GetId().ToULong(&n
) )
503 wxFAIL_MSG( _T("unexpected root cell's ID") );
511 wxHtmlListBox::CellCoordsToPhysical(const wxPoint
& pos
, wxHtmlCell
*cell
) const
513 return pos
+ GetRootCellCoords(GetItemForCell(cell
));
516 void wxHtmlListBox::OnInternalIdle()
518 wxVListBox::OnInternalIdle();
520 if ( wxHtmlWindowMouseHelper::DidMouseMove() )
522 wxPoint pos
= ScreenToClient(wxGetMousePosition());
525 if ( !PhysicalCoordsToCell(pos
, cell
) )
528 wxHtmlWindowMouseHelper::HandleIdle(cell
, pos
);
532 void wxHtmlListBox::OnMouseMove(wxMouseEvent
& event
)
534 wxHtmlWindowMouseHelper::HandleMouseMoved();
538 void wxHtmlListBox::OnLeftDown(wxMouseEvent
& event
)
540 wxPoint pos
= event
.GetPosition();
543 if ( !PhysicalCoordsToCell(pos
, cell
) )
549 if ( !wxHtmlWindowMouseHelper::HandleMouseClick(cell
, pos
, event
) )
551 // no link was clicked, so let the listbox code handle the click (e.g.
552 // by selecting another item in the list):
558 // ----------------------------------------------------------------------------
559 // wxSimpleHtmlListBox
560 // ----------------------------------------------------------------------------
562 bool wxSimpleHtmlListBox::Create(wxWindow
*parent
, wxWindowID id
,
565 int n
, const wxString choices
[],
567 const wxValidator
& validator
,
568 const wxString
& name
)
570 if (!wxHtmlListBox::Create(parent
, id
, pos
, size
, style
, name
))
573 SetValidator(validator
);
574 for (int i
=0; i
<n
; i
++)
580 bool wxSimpleHtmlListBox::Create(wxWindow
*parent
, wxWindowID id
,
583 const wxArrayString
& choices
,
585 const wxValidator
& validator
,
586 const wxString
& name
)
588 if (!wxHtmlListBox::Create(parent
, id
, pos
, size
, style
, name
))
591 SetValidator(validator
);
597 wxSimpleHtmlListBox::~wxSimpleHtmlListBox()
599 wxASSERT(m_items
.GetCount() == m_HTMLclientData
.GetCount());
600 if (HasClientObjectData())
602 // clear the array of client data objects
603 for (size_t i
=0; i
<m_items
.GetCount(); i
++)
604 delete DoGetItemClientObject(i
);
608 m_HTMLclientData
.Clear();
611 void wxSimpleHtmlListBox::Clear()
614 m_HTMLclientData
.Clear();
618 void wxSimpleHtmlListBox::Delete(unsigned int n
)
621 m_HTMLclientData
.RemoveAt(n
);
625 void wxSimpleHtmlListBox::Append(const wxArrayString
& strings
)
627 // append all given items at once
628 WX_APPEND_ARRAY(m_items
, strings
);
629 m_HTMLclientData
.Add(NULL
, strings
.GetCount());
633 int wxSimpleHtmlListBox::DoAppend(const wxString
& item
)
636 m_HTMLclientData
.Add(NULL
);
641 int wxSimpleHtmlListBox::DoInsert(const wxString
& item
, unsigned int pos
)
643 m_items
.Insert(item
, pos
);
644 m_HTMLclientData
.Insert(NULL
, pos
);
649 void wxSimpleHtmlListBox::SetString(unsigned int n
, const wxString
& s
)
651 wxCHECK_RET( IsValid(n
),
652 wxT("invalid index in wxSimpleHtmlListBox::SetString") );
658 wxString
wxSimpleHtmlListBox::GetString(unsigned int n
) const
660 wxCHECK_MSG( IsValid(n
), wxEmptyString
,
661 wxT("invalid index in wxSimpleHtmlListBox::GetString") );
666 void wxSimpleHtmlListBox::UpdateCount()
668 wxASSERT(m_items
.GetCount() == m_HTMLclientData
.GetCount());
669 wxHtmlListBox::SetItemCount(m_items
.GetCount());
671 // very small optimization: if you need to add lot of items to
672 // a wxSimpleHtmlListBox be sure to use the
673 // wxSimpleHtmlListBox::Append(const wxArrayString&) method instead!
674 if (!this->IsFrozen())