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
);
297 // use system's default GUI font by default:
298 m_htmlParser
->SetStandardFonts();
301 wxHtmlContainerCell
*cell
= (wxHtmlContainerCell
*)m_htmlParser
->
302 Parse(OnGetItemMarkup(n
));
303 wxCHECK_RET( cell
, _T("wxHtmlParser::Parse() returned NULL?") );
305 // set the cell's ID to item's index so that CellCoordsToPhysical()
306 // can quickly find the item:
307 cell
->SetId(wxString::Format(_T("%lu"), (unsigned long)n
));
309 cell
->Layout(GetClientSize().x
- 2*GetMargins().x
);
311 m_cache
->Store(n
, cell
);
315 void wxHtmlListBox::OnSize(wxSizeEvent
& event
)
317 // we need to relayout all the cached cells
323 void wxHtmlListBox::RefreshLine(size_t line
)
325 m_cache
->InvalidateRange(line
, line
);
327 wxVListBox::RefreshLine(line
);
330 void wxHtmlListBox::RefreshLines(size_t from
, size_t to
)
332 m_cache
->InvalidateRange(from
, to
);
334 wxVListBox::RefreshLines(from
, to
);
337 void wxHtmlListBox::RefreshAll()
341 wxVListBox::RefreshAll();
344 void wxHtmlListBox::SetItemCount(size_t count
)
346 // the items are going to change, forget the old ones
349 wxVListBox::SetItemCount(count
);
352 // ----------------------------------------------------------------------------
353 // wxHtmlListBox implementation of wxVListBox pure virtuals
354 // ----------------------------------------------------------------------------
356 void wxHtmlListBox::OnDrawItem(wxDC
& dc
, const wxRect
& rect
, size_t n
) const
360 wxHtmlCell
*cell
= m_cache
->Get(n
);
361 wxCHECK_RET( cell
, _T("this cell should be cached!") );
363 wxHtmlRenderingInfo htmlRendInfo
;
365 // draw the selected cell in selected state
368 wxHtmlSelection htmlSel
;
369 htmlSel
.Set(wxPoint(0,0), cell
, wxPoint(INT_MAX
, INT_MAX
), cell
);
370 htmlRendInfo
.SetSelection(&htmlSel
);
371 if ( m_htmlRendStyle
)
372 htmlRendInfo
.SetStyle(m_htmlRendStyle
);
373 htmlRendInfo
.GetState().SetSelectionState(wxHTML_SEL_IN
);
376 // note that we can't stop drawing exactly at the window boundary as then
377 // even the visible cells part could be not drawn, so always draw the
380 rect
.x
+ CELL_BORDER
, rect
.y
+ CELL_BORDER
,
381 0, INT_MAX
, htmlRendInfo
);
384 wxCoord
wxHtmlListBox::OnMeasureItem(size_t n
) const
388 wxHtmlCell
*cell
= m_cache
->Get(n
);
389 wxCHECK_MSG( cell
, 0, _T("this cell should be cached!") );
391 return cell
->GetHeight() + cell
->GetDescent() + 4;
394 // ----------------------------------------------------------------------------
395 // wxHtmlListBox implementation of wxHtmlListBoxWinInterface
396 // ----------------------------------------------------------------------------
398 void wxHtmlListBox::SetHTMLWindowTitle(const wxString
& WXUNUSED(title
))
403 void wxHtmlListBox::OnHTMLLinkClicked(const wxHtmlLinkInfo
& link
)
405 OnLinkClicked(GetItemForCell(link
.GetHtmlCell()), link
);
409 wxHtmlListBox::OnHTMLOpeningURL(wxHtmlURLType
WXUNUSED(type
),
410 const wxString
& WXUNUSED(url
),
411 wxString
*WXUNUSED(redirect
)) const
416 wxPoint
wxHtmlListBox::HTMLCoordsToWindow(wxHtmlCell
*cell
,
417 const wxPoint
& pos
) const
419 return CellCoordsToPhysical(pos
, cell
);
422 wxWindow
* wxHtmlListBox::GetHTMLWindow() { return this; }
424 wxColour
wxHtmlListBox::GetHTMLBackgroundColour() const
426 return GetBackgroundColour();
429 void wxHtmlListBox::SetHTMLBackgroundColour(const wxColour
& WXUNUSED(clr
))
434 void wxHtmlListBox::SetHTMLBackgroundImage(const wxBitmap
& WXUNUSED(bmpBg
))
439 void wxHtmlListBox::SetHTMLStatusText(const wxString
& WXUNUSED(text
))
444 wxCursor
wxHtmlListBox::GetHTMLCursor(HTMLCursor type
) const
446 // we don't want to show text selection cursor in listboxes
447 if (type
== HTMLCursor_Text
)
448 return wxHtmlWindow::GetDefaultHTMLCursor(HTMLCursor_Default
);
450 // in all other cases, use the same cursor as wxHtmlWindow:
451 return wxHtmlWindow::GetDefaultHTMLCursor(type
);
454 // ----------------------------------------------------------------------------
455 // wxHtmlListBox handling of HTML links
456 // ----------------------------------------------------------------------------
458 wxPoint
wxHtmlListBox::GetRootCellCoords(size_t n
) const
460 wxPoint
pos(CELL_BORDER
, CELL_BORDER
);
462 pos
.y
+= GetLinesHeight(GetFirstVisibleLine(), n
);
466 bool wxHtmlListBox::PhysicalCoordsToCell(wxPoint
& pos
, wxHtmlCell
*& cell
) const
468 int n
= HitTest(pos
);
469 if ( n
== wxNOT_FOUND
)
472 // convert mouse coordinates to coords relative to item's wxHtmlCell:
473 pos
-= GetRootCellCoords(n
);
476 cell
= m_cache
->Get(n
);
481 size_t wxHtmlListBox::GetItemForCell(const wxHtmlCell
*cell
) const
483 wxCHECK_MSG( cell
, 0, _T("no cell") );
485 cell
= cell
->GetRootCell();
487 wxCHECK_MSG( cell
, 0, _T("no root cell") );
489 // the cell's ID contains item index, see CacheItem():
491 if ( !cell
->GetId().ToULong(&n
) )
493 wxFAIL_MSG( _T("unexpected root cell's ID") );
501 wxHtmlListBox::CellCoordsToPhysical(const wxPoint
& pos
, wxHtmlCell
*cell
) const
503 return pos
+ GetRootCellCoords(GetItemForCell(cell
));
506 void wxHtmlListBox::OnInternalIdle()
508 wxVListBox::OnInternalIdle();
510 if ( wxHtmlWindowMouseHelper::DidMouseMove() )
512 wxPoint pos
= ScreenToClient(wxGetMousePosition());
515 if ( !PhysicalCoordsToCell(pos
, cell
) )
518 wxHtmlWindowMouseHelper::HandleIdle(cell
, pos
);
522 void wxHtmlListBox::OnMouseMove(wxMouseEvent
& event
)
524 wxHtmlWindowMouseHelper::HandleMouseMoved();
528 void wxHtmlListBox::OnLeftDown(wxMouseEvent
& event
)
530 wxPoint pos
= event
.GetPosition();
533 if ( !PhysicalCoordsToCell(pos
, cell
) )
539 if ( !wxHtmlWindowMouseHelper::HandleMouseClick(cell
, pos
, event
) )
541 // no link was clicked, so let the listbox code handle the click (e.g.
542 // by selecting another item in the list):
548 // ----------------------------------------------------------------------------
549 // wxSimpleHtmlListBox
550 // ----------------------------------------------------------------------------
552 bool wxSimpleHtmlListBox::Create(wxWindow
*parent
, wxWindowID id
,
555 int n
, const wxString choices
[],
557 const wxValidator
& validator
,
558 const wxString
& name
)
560 if (!wxHtmlListBox::Create(parent
, id
, pos
, size
, style
, name
))
563 SetValidator(validator
);
564 for (int i
=0; i
<n
; i
++)
570 bool wxSimpleHtmlListBox::Create(wxWindow
*parent
, wxWindowID id
,
573 const wxArrayString
& choices
,
575 const wxValidator
& validator
,
576 const wxString
& name
)
578 if (!wxHtmlListBox::Create(parent
, id
, pos
, size
, style
, name
))
581 SetValidator(validator
);
587 wxSimpleHtmlListBox::~wxSimpleHtmlListBox()
589 wxASSERT(m_items
.GetCount() == m_clientData
.GetCount());
590 if (HasClientObjectData())
592 // clear the array of client data objects
593 for (size_t i
=0; i
<m_items
.GetCount(); i
++)
594 delete DoGetItemClientObject(i
);
598 m_clientData
.Clear();
601 void wxSimpleHtmlListBox::Clear()
604 m_clientData
.Clear();
608 void wxSimpleHtmlListBox::Delete(unsigned int n
)
611 m_clientData
.RemoveAt(n
);
615 void wxSimpleHtmlListBox::Append(const wxArrayString
& strings
)
617 // we know how many items are going to be added - avoid too many reallocs
618 // m_items.Alloc(strings.GetCount());
619 // FIXME: Alloc() will clear all the old contents.
620 // Suggested fix = make Grow() public and make WX_APPEND_ARRAY take
621 // care of calling it automatically
623 // append all given items at once
624 WX_APPEND_ARRAY(m_items
, strings
);
625 m_clientData
.Add(NULL
, strings
.GetCount());
629 int wxSimpleHtmlListBox::DoAppend(const wxString
& item
)
632 m_clientData
.Add(NULL
);
637 int wxSimpleHtmlListBox::DoInsert(const wxString
& item
, unsigned int pos
)
639 m_items
.Insert(item
, pos
);
640 m_clientData
.Insert(NULL
, pos
);
645 void wxSimpleHtmlListBox::SetString(unsigned int n
, const wxString
& s
)
647 wxCHECK_RET( IsValid(n
),
648 wxT("invalid index in wxSimpleHtmlListBox::SetString") );
654 wxString
wxSimpleHtmlListBox::GetString(unsigned int n
) const
656 wxCHECK_MSG( IsValid(n
), wxEmptyString
,
657 wxT("invalid index in wxSimpleHtmlListBox::GetString") );
662 void wxSimpleHtmlListBox::UpdateCount()
664 wxASSERT(m_items
.GetCount() == m_clientData
.GetCount());
665 wxHtmlListBox::SetItemCount(m_items
.GetCount());
667 // very small optimization: if you need to add lot of items to
668 // a wxSimpleHtmlListBox be sure to use the
669 // wxSimpleHtmlListBox::Append(const wxArrayString&) method instead!
670 if (!this->IsFrozen())