1 /////////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/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())