1/////////////////////////////////////////////////////////////////////////////// 2// Name: generic/htmllbox.cpp 3// Purpose: implementation of wxHtmlListBox 4// Author: Vadim Zeitlin 5// Modified by: 6// Created: 31.05.03 7// RCS-ID: $Id: htmllbox.cpp 44026 2006-12-21 18:24:27Z VS $ 8// Copyright: (c) 2003 Vadim Zeitlin <vadim@wxwindows.org> 9// License: wxWindows license 10/////////////////////////////////////////////////////////////////////////////// 11 12// ============================================================================ 13// declarations 14// ============================================================================ 15 16// ---------------------------------------------------------------------------- 17// headers 18// ---------------------------------------------------------------------------- 19 20// For compilers that support precompilation, includes "wx.h". 21#include "wx/wxprec.h" 22 23#ifdef __BORLANDC__ 24 #pragma hdrstop 25#endif 26 27#ifndef WX_PRECOMP 28 #include "wx/dcclient.h" 29#endif //WX_PRECOMP 30 31#if wxUSE_HTML 32 33#include "wx/htmllbox.h" 34 35#include "wx/html/htmlcell.h" 36#include "wx/html/winpars.h" 37 38// this hack forces the linker to always link in m_* files 39#include "wx/html/forcelnk.h" 40FORCE_WXHTML_MODULES() 41 42// ---------------------------------------------------------------------------- 43// constants 44// ---------------------------------------------------------------------------- 45 46// small border always added to the cells: 47static const wxCoord CELL_BORDER = 2; 48 49const wxChar wxHtmlListBoxNameStr[] = wxT("htmlListBox"); 50const wxChar wxSimpleHtmlListBoxNameStr[] = wxT("simpleHtmlListBox"); 51 52// ============================================================================ 53// private classes 54// ============================================================================ 55 56// ---------------------------------------------------------------------------- 57// wxHtmlListBoxCache 58// ---------------------------------------------------------------------------- 59 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 62class wxHtmlListBoxCache 63{ 64private: 65 // invalidate a single item, used by Clear() and InvalidateRange() 66 void InvalidateItem(size_t n) 67 { 68 m_items[n] = (size_t)-1; 69 delete m_cells[n]; 70 m_cells[n] = NULL; 71 } 72 73public: 74 wxHtmlListBoxCache() 75 { 76 for ( size_t n = 0; n < SIZE; n++ ) 77 { 78 m_items[n] = (size_t)-1; 79 m_cells[n] = NULL; 80 } 81 82 m_next = 0; 83 } 84 85 ~wxHtmlListBoxCache() 86 { 87 for ( size_t n = 0; n < SIZE; n++ ) 88 { 89 delete m_cells[n]; 90 } 91 } 92 93 // completely invalidate the cache 94 void Clear() 95 { 96 for ( size_t n = 0; n < SIZE; n++ ) 97 { 98 InvalidateItem(n); 99 } 100 } 101 102 // return the cached cell for this index or NULL if none 103 wxHtmlCell *Get(size_t item) const 104 { 105 for ( size_t n = 0; n < SIZE; n++ ) 106 { 107 if ( m_items[n] == item ) 108 return m_cells[n]; 109 } 110 111 return NULL; 112 } 113 114 // returns true if we already have this item cached 115 bool Has(size_t item) const { return Get(item) != NULL; } 116 117 // ensure that the item is cached 118 void Store(size_t item, wxHtmlCell *cell) 119 { 120 delete m_cells[m_next]; 121 m_cells[m_next] = cell; 122 m_items[m_next] = item; 123 124 // advance to the next item wrapping around if there are no more 125 if ( ++m_next == SIZE ) 126 m_next = 0; 127 } 128 129 // forget the cached value of the item(s) between the given ones (inclusive) 130 void InvalidateRange(size_t from, size_t to) 131 { 132 for ( size_t n = 0; n < SIZE; n++ ) 133 { 134 if ( m_items[n] >= from && m_items[n] <= to ) 135 { 136 InvalidateItem(n); 137 } 138 } 139 } 140 141private: 142 // the max number of the items we cache 143 enum { SIZE = 50 }; 144 145 // the index of the LRU (oldest) cell 146 size_t m_next; 147 148 // the parsed representation of the cached item or NULL 149 wxHtmlCell *m_cells[SIZE]; 150 151 // the index of the currently cached item (only valid if m_cells != NULL) 152 size_t m_items[SIZE]; 153}; 154 155// ---------------------------------------------------------------------------- 156// wxHtmlListBoxStyle 157// ---------------------------------------------------------------------------- 158 159// just forward wxDefaultHtmlRenderingStyle callbacks to the main class so that 160// they could be overridden by the user code 161class wxHtmlListBoxStyle : public wxDefaultHtmlRenderingStyle 162{ 163public: 164 wxHtmlListBoxStyle(const wxHtmlListBox& hlbox) : m_hlbox(hlbox) { } 165 166 virtual wxColour GetSelectedTextColour(const wxColour& colFg) 167 { 168 return m_hlbox.GetSelectedTextColour(colFg); 169 } 170 171 virtual wxColour GetSelectedTextBgColour(const wxColour& colBg) 172 { 173 return m_hlbox.GetSelectedTextBgColour(colBg); 174 } 175 176private: 177 const wxHtmlListBox& m_hlbox; 178 179 DECLARE_NO_COPY_CLASS(wxHtmlListBoxStyle) 180}; 181 182// ---------------------------------------------------------------------------- 183// event tables 184// ---------------------------------------------------------------------------- 185 186BEGIN_EVENT_TABLE(wxHtmlListBox, wxVListBox) 187 EVT_SIZE(wxHtmlListBox::OnSize) 188 EVT_MOTION(wxHtmlListBox::OnMouseMove) 189 EVT_LEFT_DOWN(wxHtmlListBox::OnLeftDown) 190END_EVENT_TABLE() 191 192// ============================================================================ 193// implementation 194// ============================================================================ 195 196IMPLEMENT_ABSTRACT_CLASS(wxHtmlListBox, wxVListBox) 197 198 199// ---------------------------------------------------------------------------- 200// wxHtmlListBox creation 201// ---------------------------------------------------------------------------- 202 203wxHtmlListBox::wxHtmlListBox() 204 : wxHtmlWindowMouseHelper(this) 205{ 206 Init(); 207} 208 209// normal constructor which calls Create() internally 210wxHtmlListBox::wxHtmlListBox(wxWindow *parent, 211 wxWindowID id, 212 const wxPoint& pos, 213 const wxSize& size, 214 long style, 215 const wxString& name) 216 : wxHtmlWindowMouseHelper(this) 217{ 218 Init(); 219 220 (void)Create(parent, id, pos, size, style, name); 221} 222 223void wxHtmlListBox::Init() 224{ 225 m_htmlParser = NULL; 226 m_htmlRendStyle = new wxHtmlListBoxStyle(*this); 227 m_cache = new wxHtmlListBoxCache; 228} 229 230bool wxHtmlListBox::Create(wxWindow *parent, 231 wxWindowID id, 232 const wxPoint& pos, 233 const wxSize& size, 234 long style, 235 const wxString& name) 236{ 237 return wxVListBox::Create(parent, id, pos, size, style, name); 238} 239 240wxHtmlListBox::~wxHtmlListBox() 241{ 242 delete m_cache; 243 244 if ( m_htmlParser ) 245 { 246 delete m_htmlParser->GetDC(); 247 delete m_htmlParser; 248 } 249 250 delete m_htmlRendStyle; 251} 252 253// ---------------------------------------------------------------------------- 254// wxHtmlListBox appearance 255// ---------------------------------------------------------------------------- 256 257wxColour wxHtmlListBox::GetSelectedTextColour(const wxColour& colFg) const 258{ 259 return m_htmlRendStyle-> 260 wxDefaultHtmlRenderingStyle::GetSelectedTextColour(colFg); 261} 262 263wxColour 264wxHtmlListBox::GetSelectedTextBgColour(const wxColour& WXUNUSED(colBg)) const 265{ 266 return GetSelectionBackground(); 267} 268 269// ---------------------------------------------------------------------------- 270// wxHtmlListBox items markup 271// ---------------------------------------------------------------------------- 272 273wxString wxHtmlListBox::OnGetItemMarkup(size_t n) const 274{ 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 278 return OnGetItem(n); 279} 280 281// ---------------------------------------------------------------------------- 282// wxHtmlListBox cache handling 283// ---------------------------------------------------------------------------- 284 285void wxHtmlListBox::CacheItem(size_t n) const 286{ 287 if ( !m_cache->Has(n) ) 288 { 289 if ( !m_htmlParser ) 290 { 291 wxHtmlListBox *self = wxConstCast(this, wxHtmlListBox); 292 293 self->m_htmlParser = new wxHtmlWinParser(self); 294 m_htmlParser->SetDC(new wxClientDC(self)); 295 m_htmlParser->SetFS(&self->m_filesystem); 296#if !wxUSE_UNICODE 297 if (GetFont().Ok()) 298 m_htmlParser->SetInputEncoding(GetFont().GetEncoding()); 299#endif 300 // use system's default GUI font by default: 301 m_htmlParser->SetStandardFonts(); 302 } 303 304 wxHtmlContainerCell *cell = (wxHtmlContainerCell *)m_htmlParser-> 305 Parse(OnGetItemMarkup(n)); 306 wxCHECK_RET( cell, _T("wxHtmlParser::Parse() returned NULL?") ); 307 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)); 311 312 cell->Layout(GetClientSize().x - 2*GetMargins().x); 313 314 m_cache->Store(n, cell); 315 } 316} 317 318void wxHtmlListBox::OnSize(wxSizeEvent& event) 319{ 320 // we need to relayout all the cached cells 321 m_cache->Clear(); 322 323 event.Skip(); 324} 325 326void wxHtmlListBox::RefreshLine(size_t line) 327{ 328 m_cache->InvalidateRange(line, line); 329 330 wxVListBox::RefreshLine(line); 331} 332 333void wxHtmlListBox::RefreshLines(size_t from, size_t to) 334{ 335 m_cache->InvalidateRange(from, to); 336 337 wxVListBox::RefreshLines(from, to); 338} 339 340void wxHtmlListBox::RefreshAll() 341{ 342 m_cache->Clear(); 343 344 wxVListBox::RefreshAll(); 345} 346 347void wxHtmlListBox::SetItemCount(size_t count) 348{ 349 // the items are going to change, forget the old ones 350 m_cache->Clear(); 351 352 wxVListBox::SetItemCount(count); 353} 354 355// ---------------------------------------------------------------------------- 356// wxHtmlListBox implementation of wxVListBox pure virtuals 357// ---------------------------------------------------------------------------- 358 359void wxHtmlListBox::OnDrawItem(wxDC& dc, const wxRect& rect, size_t n) const 360{ 361 CacheItem(n); 362 363 wxHtmlCell *cell = m_cache->Get(n); 364 wxCHECK_RET( cell, _T("this cell should be cached!") ); 365 366 wxHtmlRenderingInfo htmlRendInfo; 367 368 // draw the selected cell in selected state 369 if ( IsSelected(n) ) 370 { 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); 377 } 378 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 381 // entire cell 382 cell->Draw(dc, 383 rect.x + CELL_BORDER, rect.y + CELL_BORDER, 384 0, INT_MAX, htmlRendInfo); 385} 386 387wxCoord wxHtmlListBox::OnMeasureItem(size_t n) const 388{ 389 CacheItem(n); 390 391 wxHtmlCell *cell = m_cache->Get(n); 392 wxCHECK_MSG( cell, 0, _T("this cell should be cached!") ); 393 394 return cell->GetHeight() + cell->GetDescent() + 4; 395} 396 397// ---------------------------------------------------------------------------- 398// wxHtmlListBox implementation of wxHtmlListBoxWinInterface 399// ---------------------------------------------------------------------------- 400 401void wxHtmlListBox::SetHTMLWindowTitle(const wxString& WXUNUSED(title)) 402{ 403 // nothing to do 404} 405 406void wxHtmlListBox::OnHTMLLinkClicked(const wxHtmlLinkInfo& link) 407{ 408 OnLinkClicked(GetItemForCell(link.GetHtmlCell()), link); 409} 410 411void wxHtmlListBox::OnLinkClicked(size_t WXUNUSED(n), 412 const wxHtmlLinkInfo& link) 413{ 414 wxHtmlLinkEvent event(GetId(), link); 415 GetEventHandler()->ProcessEvent(event); 416} 417 418wxHtmlOpeningStatus 419wxHtmlListBox::OnHTMLOpeningURL(wxHtmlURLType WXUNUSED(type), 420 const wxString& WXUNUSED(url), 421 wxString *WXUNUSED(redirect)) const 422{ 423 return wxHTML_OPEN; 424} 425 426wxPoint wxHtmlListBox::HTMLCoordsToWindow(wxHtmlCell *cell, 427 const wxPoint& pos) const 428{ 429 return CellCoordsToPhysical(pos, cell); 430} 431 432wxWindow* wxHtmlListBox::GetHTMLWindow() { return this; } 433 434wxColour wxHtmlListBox::GetHTMLBackgroundColour() const 435{ 436 return GetBackgroundColour(); 437} 438 439void wxHtmlListBox::SetHTMLBackgroundColour(const wxColour& WXUNUSED(clr)) 440{ 441 // nothing to do 442} 443 444void wxHtmlListBox::SetHTMLBackgroundImage(const wxBitmap& WXUNUSED(bmpBg)) 445{ 446 // nothing to do 447} 448 449void wxHtmlListBox::SetHTMLStatusText(const wxString& WXUNUSED(text)) 450{ 451 // nothing to do 452} 453 454wxCursor wxHtmlListBox::GetHTMLCursor(HTMLCursor type) const 455{ 456 // we don't want to show text selection cursor in listboxes 457 if (type == HTMLCursor_Text) 458 return wxHtmlWindow::GetDefaultHTMLCursor(HTMLCursor_Default); 459 460 // in all other cases, use the same cursor as wxHtmlWindow: 461 return wxHtmlWindow::GetDefaultHTMLCursor(type); 462} 463 464// ---------------------------------------------------------------------------- 465// wxHtmlListBox handling of HTML links 466// ---------------------------------------------------------------------------- 467 468wxPoint wxHtmlListBox::GetRootCellCoords(size_t n) const 469{ 470 wxPoint pos(CELL_BORDER, CELL_BORDER); 471 pos += GetMargins(); 472 pos.y += GetLinesHeight(GetFirstVisibleLine(), n); 473 return pos; 474} 475 476bool wxHtmlListBox::PhysicalCoordsToCell(wxPoint& pos, wxHtmlCell*& cell) const 477{ 478 int n = HitTest(pos); 479 if ( n == wxNOT_FOUND ) 480 return false; 481 482 // convert mouse coordinates to coords relative to item's wxHtmlCell: 483 pos -= GetRootCellCoords(n); 484 485 CacheItem(n); 486 cell = m_cache->Get(n); 487 488 return true; 489} 490 491size_t wxHtmlListBox::GetItemForCell(const wxHtmlCell *cell) const 492{ 493 wxCHECK_MSG( cell, 0, _T("no cell") ); 494 495 cell = cell->GetRootCell(); 496 497 wxCHECK_MSG( cell, 0, _T("no root cell") ); 498 499 // the cell's ID contains item index, see CacheItem(): 500 unsigned long n; 501 if ( !cell->GetId().ToULong(&n) ) 502 { 503 wxFAIL_MSG( _T("unexpected root cell's ID") ); 504 return 0; 505 } 506 507 return n; 508} 509 510wxPoint 511wxHtmlListBox::CellCoordsToPhysical(const wxPoint& pos, wxHtmlCell *cell) const 512{ 513 return pos + GetRootCellCoords(GetItemForCell(cell)); 514} 515 516void wxHtmlListBox::OnInternalIdle() 517{ 518 wxVListBox::OnInternalIdle(); 519 520 if ( wxHtmlWindowMouseHelper::DidMouseMove() ) 521 { 522 wxPoint pos = ScreenToClient(wxGetMousePosition()); 523 wxHtmlCell *cell; 524 525 if ( !PhysicalCoordsToCell(pos, cell) ) 526 return; 527 528 wxHtmlWindowMouseHelper::HandleIdle(cell, pos); 529 } 530} 531 532void wxHtmlListBox::OnMouseMove(wxMouseEvent& event) 533{ 534 wxHtmlWindowMouseHelper::HandleMouseMoved(); 535 event.Skip(); 536} 537 538void wxHtmlListBox::OnLeftDown(wxMouseEvent& event) 539{ 540 wxPoint pos = event.GetPosition(); 541 wxHtmlCell *cell; 542 543 if ( !PhysicalCoordsToCell(pos, cell) ) 544 { 545 event.Skip(); 546 return; 547 } 548 549 if ( !wxHtmlWindowMouseHelper::HandleMouseClick(cell, pos, event) ) 550 { 551 // no link was clicked, so let the listbox code handle the click (e.g. 552 // by selecting another item in the list): 553 event.Skip(); 554 } 555} 556 557 558// ---------------------------------------------------------------------------- 559// wxSimpleHtmlListBox 560// ---------------------------------------------------------------------------- 561 562bool wxSimpleHtmlListBox::Create(wxWindow *parent, wxWindowID id, 563 const wxPoint& pos, 564 const wxSize& size, 565 int n, const wxString choices[], 566 long style, 567 const wxValidator& validator, 568 const wxString& name) 569{ 570 if (!wxHtmlListBox::Create(parent, id, pos, size, style, name)) 571 return false; 572 573#if wxUSE_VALIDATORS 574 SetValidator(validator); 575#endif 576 for (int i=0; i<n; i++) 577 Append(choices[i]); 578 579 return true; 580} 581 582bool wxSimpleHtmlListBox::Create(wxWindow *parent, wxWindowID id, 583 const wxPoint& pos, 584 const wxSize& size, 585 const wxArrayString& choices, 586 long style, 587 const wxValidator& validator, 588 const wxString& name) 589{ 590 if (!wxHtmlListBox::Create(parent, id, pos, size, style, name)) 591 return false; 592 593#if wxUSE_VALIDATORS 594 SetValidator(validator); 595#endif 596 Append(choices); 597 598 return true; 599} 600 601wxSimpleHtmlListBox::~wxSimpleHtmlListBox() 602{ 603 wxASSERT(m_items.GetCount() == m_HTMLclientData.GetCount()); 604 if (HasClientObjectData()) 605 { 606 // clear the array of client data objects 607 for (size_t i=0; i<m_items.GetCount(); i++) 608 delete DoGetItemClientObject(i); 609 } 610 611 m_items.Clear(); 612 m_HTMLclientData.Clear(); 613} 614 615void wxSimpleHtmlListBox::Clear() 616{ 617 m_items.Clear(); 618 m_HTMLclientData.Clear(); 619 UpdateCount(); 620} 621 622void wxSimpleHtmlListBox::Delete(unsigned int n) 623{ 624 m_items.RemoveAt(n); 625 m_HTMLclientData.RemoveAt(n); 626 UpdateCount(); 627} 628 629void wxSimpleHtmlListBox::Append(const wxArrayString& strings) 630{ 631 // append all given items at once 632 WX_APPEND_ARRAY(m_items, strings); 633 m_HTMLclientData.Add(NULL, strings.GetCount()); 634 UpdateCount(); 635} 636 637int wxSimpleHtmlListBox::DoAppend(const wxString& item) 638{ 639 m_items.Add(item); 640 m_HTMLclientData.Add(NULL); 641 UpdateCount(); 642 return GetCount()-1; 643} 644 645int wxSimpleHtmlListBox::DoInsert(const wxString& item, unsigned int pos) 646{ 647 m_items.Insert(item, pos); 648 m_HTMLclientData.Insert(NULL, pos); 649 UpdateCount(); 650 return pos; 651} 652 653void wxSimpleHtmlListBox::SetString(unsigned int n, const wxString& s) 654{ 655 wxCHECK_RET( IsValid(n), 656 wxT("invalid index in wxSimpleHtmlListBox::SetString") ); 657 658 m_items[n]=s; 659 RefreshLine(n); 660} 661 662wxString wxSimpleHtmlListBox::GetString(unsigned int n) const 663{ 664 wxCHECK_MSG( IsValid(n), wxEmptyString, 665 wxT("invalid index in wxSimpleHtmlListBox::GetString") ); 666 667 return m_items[n]; 668} 669 670void wxSimpleHtmlListBox::UpdateCount() 671{ 672 wxASSERT(m_items.GetCount() == m_HTMLclientData.GetCount()); 673 wxHtmlListBox::SetItemCount(m_items.GetCount()); 674 675 // very small optimization: if you need to add lot of items to 676 // a wxSimpleHtmlListBox be sure to use the 677 // wxSimpleHtmlListBox::Append(const wxArrayString&) method instead! 678 if (!this->IsFrozen()) 679 RefreshAll(); 680} 681 682#endif // wxUSE_HTML 683