1///////////////////////////////////////////////////////////////////////////// 2// Name: src/generic/odcombo.cpp 3// Purpose: wxOwnerDrawnComboBox, wxVListBoxComboPopup 4// Author: Jaakko Salli 5// Modified by: 6// Created: Apr-30-2006 7// RCS-ID: $Id: odcombo.cpp 64259 2010-05-09 10:48:37Z JMS $ 8// Copyright: (c) 2005 Jaakko Salli 9// Licence: wxWindows licence 10///////////////////////////////////////////////////////////////////////////// 11 12// ============================================================================ 13// declarations 14// ============================================================================ 15 16// ---------------------------------------------------------------------------- 17// headers 18// ---------------------------------------------------------------------------- 19 20#include "wx/wxprec.h" 21 22#ifdef __BORLANDC__ 23 #pragma hdrstop 24#endif 25 26#if wxUSE_ODCOMBOBOX 27 28#include "wx/odcombo.h" 29 30#ifndef WX_PRECOMP 31 #include "wx/log.h" 32 #include "wx/combobox.h" 33 #include "wx/dcclient.h" 34 #include "wx/settings.h" 35 #include "wx/dialog.h" 36#endif 37 38#include "wx/combo.h" 39 40// ============================================================================ 41// implementation 42// ============================================================================ 43 44// time in milliseconds before partial completion buffer drops 45#define wxODCB_PARTIAL_COMPLETION_TIME 1000 46 47// ---------------------------------------------------------------------------- 48// wxVListBoxComboPopup is a wxVListBox customized to act as a popup control 49// 50// ---------------------------------------------------------------------------- 51 52 53BEGIN_EVENT_TABLE(wxVListBoxComboPopup, wxVListBox) 54 EVT_MOTION(wxVListBoxComboPopup::OnMouseMove) 55 EVT_KEY_DOWN(wxVListBoxComboPopup::OnKey) 56 EVT_LEFT_UP(wxVListBoxComboPopup::OnLeftClick) 57END_EVENT_TABLE() 58 59 60void wxVListBoxComboPopup::Init() 61{ 62 m_widestWidth = 0; 63 m_widestItem = -1; 64 m_widthsDirty = false; 65 m_findWidest = false; 66 m_itemHeight = 0; 67 m_value = -1; 68 m_itemHover = -1; 69 m_clientDataItemsType = wxClientData_None; 70 m_partialCompletionString = wxEmptyString; 71} 72 73bool wxVListBoxComboPopup::Create(wxWindow* parent) 74{ 75 if ( !wxVListBox::Create(parent, 76 wxID_ANY, 77 wxDefaultPosition, 78 wxDefaultSize, 79 wxBORDER_SIMPLE | wxLB_INT_HEIGHT | wxWANTS_CHARS) ) 80 return false; 81 82 m_useFont = m_combo->GetFont(); 83 84 wxVListBox::SetItemCount(m_strings.GetCount()); 85 86 // TODO: Move this to SetFont 87 m_itemHeight = GetCharHeight() + 0; 88 89 return true; 90} 91 92wxVListBoxComboPopup::~wxVListBoxComboPopup() 93{ 94 Clear(); 95} 96 97#ifdef __WXMSW__ 98 99void wxVListBoxComboPopup::SetFocus() 100{ 101 // Suppress SetFocus() warning by simply not calling it. This combo popup 102 // has already been designed with the assumption that SetFocus() may not 103 // do anything useful, so it really doesn't need to be called. 104} 105 106#endif // __WXMSW__ 107 108bool wxVListBoxComboPopup::LazyCreate() 109{ 110 // NB: There is a bug with wxVListBox that can be avoided by creating 111 // it later (bug causes empty space to be shown if initial selection 112 // is at the end of a list longer than the control can show at once). 113 return true; 114} 115 116// paint the control itself 117void wxVListBoxComboPopup::PaintComboControl( wxDC& dc, const wxRect& rect ) 118{ 119 if ( !(m_combo->GetWindowStyle() & wxODCB_STD_CONTROL_PAINT) ) 120 { 121 int flags = wxODCB_PAINTING_CONTROL; 122 123 if ( m_combo->ShouldDrawFocus() ) 124 flags |= wxODCB_PAINTING_SELECTED; 125 126 OnDrawBg(dc, rect, m_value, flags); 127 128 if ( m_value >= 0 ) 129 { 130 OnDrawItem(dc,rect,m_value,flags); 131 return; 132 } 133 } 134 135 wxComboPopup::PaintComboControl(dc,rect); 136} 137 138void wxVListBoxComboPopup::OnDrawItem(wxDC& dc, const wxRect& rect, size_t n) const 139{ 140 // TODO: Maybe this code could be moved to wxVListBox::OnPaint? 141 dc.SetFont(m_useFont); 142 143 int flags = 0; 144 145 // Set correct text colour for selected items 146 if ( wxVListBox::GetSelection() == (int) n ) 147 { 148 dc.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT) ); 149 flags |= wxODCB_PAINTING_SELECTED; 150 } 151 else 152 { 153 dc.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT) ); 154 } 155 156 OnDrawItem(dc,rect,(int)n,flags); 157} 158 159wxCoord wxVListBoxComboPopup::OnMeasureItem(size_t n) const 160{ 161 wxOwnerDrawnComboBox* combo = (wxOwnerDrawnComboBox*) m_combo; 162 163 wxASSERT_MSG( combo->IsKindOf(CLASSINFO(wxOwnerDrawnComboBox)), 164 wxT("you must subclass wxVListBoxComboPopup for drawing and measuring methods") ); 165 166 wxCoord h = combo->OnMeasureItem(n); 167 if ( h < 0 ) 168 h = m_itemHeight; 169 return h; 170} 171 172wxCoord wxVListBoxComboPopup::OnMeasureItemWidth(size_t n) const 173{ 174 wxOwnerDrawnComboBox* combo = (wxOwnerDrawnComboBox*) m_combo; 175 176 wxASSERT_MSG( combo->IsKindOf(CLASSINFO(wxOwnerDrawnComboBox)), 177 wxT("you must subclass wxVListBoxComboPopup for drawing and measuring methods") ); 178 179 return combo->OnMeasureItemWidth(n); 180} 181 182void wxVListBoxComboPopup::OnDrawBg( wxDC& dc, 183 const wxRect& rect, 184 int item, 185 int flags ) const 186{ 187 wxOwnerDrawnComboBox* combo = (wxOwnerDrawnComboBox*) m_combo; 188 189 wxASSERT_MSG( combo->IsKindOf(CLASSINFO(wxOwnerDrawnComboBox)), 190 wxT("you must subclass wxVListBoxComboPopup for drawing and measuring methods") ); 191 192 if ( IsCurrent((size_t)item) && !(flags & wxODCB_PAINTING_CONTROL) ) 193 flags |= wxODCB_PAINTING_SELECTED; 194 195 combo->OnDrawBackground(dc,rect,item,flags); 196} 197 198void wxVListBoxComboPopup::OnDrawBackground(wxDC& dc, const wxRect& rect, size_t n) const 199{ 200 OnDrawBg(dc,rect,(int)n,0); 201} 202 203// This is called from wxVListBoxComboPopup::OnDrawItem, with text colour and font prepared 204void wxVListBoxComboPopup::OnDrawItem( wxDC& dc, const wxRect& rect, int item, int flags ) const 205{ 206 wxOwnerDrawnComboBox* combo = (wxOwnerDrawnComboBox*) m_combo; 207 208 wxASSERT_MSG( combo->IsKindOf(CLASSINFO(wxOwnerDrawnComboBox)), 209 wxT("you must subclass wxVListBoxComboPopup for drawing and measuring methods") ); 210 211 combo->OnDrawItem(dc,rect,item,flags); 212} 213 214void wxVListBoxComboPopup::DismissWithEvent() 215{ 216 StopPartialCompletion(); 217 218 int selection = wxVListBox::GetSelection(); 219 220 Dismiss(); 221 222 wxString valStr; 223 if ( selection != wxNOT_FOUND ) 224 valStr = m_strings[selection]; 225 else 226 valStr = wxEmptyString; 227 228 m_value = selection; 229 230 if ( valStr != m_combo->GetValue() ) 231 m_combo->SetValueWithEvent(valStr); 232 233 SendComboBoxEvent(selection); 234} 235 236void wxVListBoxComboPopup::SendComboBoxEvent( int selection ) 237{ 238 wxCommandEvent evt(wxEVT_COMMAND_COMBOBOX_SELECTED,m_combo->GetId()); 239 240 evt.SetEventObject(m_combo); 241 242 evt.SetInt(selection); 243 244 // Set client data, if any 245 if ( selection >= 0 && (int)m_clientDatas.GetCount() > selection ) 246 { 247 void* clientData = m_clientDatas[selection]; 248 if ( m_clientDataItemsType == wxClientData_Object ) 249 evt.SetClientObject((wxClientData*)clientData); 250 else 251 evt.SetClientData(clientData); 252 } 253 254 m_combo->GetEventHandler()->AddPendingEvent(evt); 255} 256 257// returns true if key was consumed 258bool wxVListBoxComboPopup::HandleKey( int keycode, bool saturate, wxChar unicode ) 259{ 260 const int itemCount = GetCount(); 261 262 // keys do nothing in the empty control and returning immediately avoids 263 // using invalid indices below 264 if ( !itemCount ) 265 return false; 266 267 int value = m_value; 268 int comboStyle = m_combo->GetWindowStyle(); 269 270 // this is the character equivalent of the code 271 wxChar keychar = 0; 272 if ( keycode < WXK_START ) 273 { 274#if wxUSE_UNICODE 275 if ( unicode > 0 ) 276 { 277 if ( wxIsprint(unicode) ) 278 keychar = unicode; 279 } 280 else 281#else 282 wxUnusedVar(unicode); 283#endif 284 if ( wxIsprint(keycode) ) 285 { 286 keychar = (wxChar) keycode; 287 } 288 } 289 290 if ( keycode == WXK_DOWN || keycode == WXK_RIGHT ) 291 { 292 value++; 293 StopPartialCompletion(); 294 } 295 else if ( keycode == WXK_UP || keycode == WXK_LEFT ) 296 { 297 value--; 298 StopPartialCompletion(); 299 } 300 else if ( keycode == WXK_PAGEDOWN ) 301 { 302 value+=10; 303 StopPartialCompletion(); 304 } 305 else if ( keycode == WXK_PAGEUP ) 306 { 307 value-=10; 308 StopPartialCompletion(); 309 } 310 else if ( keychar && (comboStyle & wxCB_READONLY) ) 311 { 312 // Try partial completion 313 314 // find the new partial completion string 315#if wxUSE_TIMER 316 if (m_partialCompletionTimer.IsRunning()) 317 m_partialCompletionString+=wxString(keychar); 318 else 319#endif // wxUSE_TIMER 320 m_partialCompletionString=wxString(keychar); 321 322 // now search through the values to see if this is found 323 int found = -1; 324 unsigned int length=m_partialCompletionString.length(); 325 int i; 326 for (i=0; i<itemCount; i++) 327 { 328 wxString item=GetString(i); 329 if (( item.length() >= length) && (! m_partialCompletionString.CmpNoCase(item.Left(length)))) 330 { 331 found=i; 332 break; 333 } 334 } 335 336 if (found<0) 337 { 338 StopPartialCompletion(); 339 ::wxBell(); 340 return true; // to stop the first value being set 341 } 342 else 343 { 344 value=i; 345#if wxUSE_TIMER 346 m_partialCompletionTimer.Start(wxODCB_PARTIAL_COMPLETION_TIME, true); 347#endif // wxUSE_TIMER 348 } 349 } 350 else 351 return false; 352 353 if ( saturate ) 354 { 355 if ( value >= itemCount ) 356 value = itemCount - 1; 357 else if ( value < 0 ) 358 value = 0; 359 } 360 else 361 { 362 if ( value >= itemCount ) 363 value -= itemCount; 364 else if ( value < 0 ) 365 value += itemCount; 366 } 367 368 if ( value == m_value ) 369 // Even if value was same, don't skip the event 370 // (good for consistency) 371 return true; 372 373 m_value = value; 374 375 if ( value >= 0 ) 376 m_combo->SetValue(m_strings[value]); 377 378 SendComboBoxEvent(m_value); 379 380 return true; 381} 382 383// stop partial completion 384void wxVListBoxComboPopup::StopPartialCompletion() 385{ 386 m_partialCompletionString = wxEmptyString; 387#if wxUSE_TIMER 388 m_partialCompletionTimer.Stop(); 389#endif // wxUSE_TIMER 390} 391 392void wxVListBoxComboPopup::OnComboDoubleClick() 393{ 394 // Cycle on dclick (disable saturation to allow true cycling). 395 if ( !::wxGetKeyState(WXK_SHIFT) ) 396 HandleKey(WXK_DOWN,false); 397 else 398 HandleKey(WXK_UP,false); 399} 400 401void wxVListBoxComboPopup::OnComboKeyEvent( wxKeyEvent& event ) 402{ 403 // Saturated key movement on 404 if ( !HandleKey(event.GetKeyCode(),true, 405#if wxUSE_UNICODE 406 event.GetUnicodeKey() 407#else 408 0 409#endif 410 ) ) 411 event.Skip(); 412} 413 414void wxVListBoxComboPopup::OnPopup() 415{ 416 // *must* set value after size is set (this is because of a vlbox bug) 417 wxVListBox::SetSelection(m_value); 418} 419 420void wxVListBoxComboPopup::OnMouseMove(wxMouseEvent& event) 421{ 422 event.Skip(); 423 424 // Move selection to cursor if it is inside the popup 425 426 int y = event.GetPosition().y; 427 int fromBottom = GetClientSize().y - y; 428 429 // Since in any case we need to find out if the last item is only 430 // partially visible, we might just as well replicate the HitTest 431 // loop here. 432 const size_t lineMax = GetVisibleEnd(); 433 for ( size_t line = GetVisibleBegin(); line < lineMax; line++ ) 434 { 435 y -= OnGetLineHeight(line); 436 if ( y < 0 ) 437 { 438 // Only change selection if item is fully visible 439 if ( (y + fromBottom) >= 0 ) 440 { 441 wxVListBox::SetSelection((int)line); 442 return; 443 } 444 } 445 } 446} 447 448void wxVListBoxComboPopup::OnLeftClick(wxMouseEvent& WXUNUSED(event)) 449{ 450 DismissWithEvent(); 451} 452 453void wxVListBoxComboPopup::OnKey(wxKeyEvent& event) 454{ 455 // Hide popup if certain key or key combination was pressed 456 if ( m_combo->IsKeyPopupToggle(event) ) 457 { 458 StopPartialCompletion(); 459 Dismiss(); 460 } 461 else if ( event.AltDown() ) 462 { 463 // On both wxGTK and wxMSW, pressing Alt down seems to 464 // completely freeze things in popup (ie. arrow keys and 465 // enter won't work). 466 return; 467 } 468 // Select item if ENTER is pressed 469 else if ( event.GetKeyCode() == WXK_RETURN || event.GetKeyCode() == WXK_NUMPAD_ENTER ) 470 { 471 DismissWithEvent(); 472 } 473 else 474 { 475 int comboStyle = m_combo->GetWindowStyle(); 476 int keycode = event.GetKeyCode(); 477 // Process partial completion key codes here, but not the arrow keys as the base class will do that for us 478 if ((comboStyle & wxCB_READONLY) && 479 (keycode >= WXK_SPACE) && (keycode <=255) && (keycode != WXK_DELETE) && wxIsprint(keycode)) 480 { 481 OnComboKeyEvent(event); 482 SetSelection(m_value); // ensure the highlight bar moves 483 } 484 else 485 event.Skip(); 486 } 487} 488 489void wxVListBoxComboPopup::Insert( const wxString& item, int pos ) 490{ 491 // Need to change selection? 492 wxString strValue; 493 if ( !(m_combo->GetWindowStyle() & wxCB_READONLY) && 494 m_combo->GetValue() == item ) 495 { 496 m_value = pos; 497 } 498 499 m_strings.Insert(item,pos); 500 m_widths.Insert(-1,pos); 501 m_widthsDirty = true; 502 503 if ( IsCreated() ) 504 wxVListBox::SetItemCount( wxVListBox::GetItemCount()+1 ); 505} 506 507int wxVListBoxComboPopup::Append(const wxString& item) 508{ 509 int pos = (int)m_strings.GetCount(); 510 511 if ( m_combo->GetWindowStyle() & wxCB_SORT ) 512 { 513 // Find position 514 // TODO: Could be optimized with binary search 515 wxArrayString strings = m_strings; 516 unsigned int i; 517 518 for ( i=0; i<strings.GetCount(); i++ ) 519 { 520 if ( item.CmpNoCase(strings.Item(i)) < 0 ) 521 { 522 pos = (int)i; 523 break; 524 } 525 } 526 } 527 528 Insert(item,pos); 529 530 return pos; 531} 532 533void wxVListBoxComboPopup::Clear() 534{ 535 wxASSERT(m_combo); 536 537 m_strings.Empty(); 538 m_widths.Empty(); 539 540 m_widestWidth = 0; 541 m_widestItem = -1; 542 543 ClearClientDatas(); 544 545 m_value = wxNOT_FOUND; 546 547 if ( IsCreated() ) 548 wxVListBox::SetItemCount(0); 549} 550 551void wxVListBoxComboPopup::ClearClientDatas() 552{ 553 if ( m_clientDataItemsType == wxClientData_Object ) 554 { 555 size_t i; 556 for ( i=0; i<m_clientDatas.GetCount(); i++ ) 557 delete (wxClientData*) m_clientDatas[i]; 558 } 559 560 m_clientDatas.Empty(); 561} 562 563void wxVListBoxComboPopup::SetItemClientData( unsigned int n, 564 void* clientData, 565 wxClientDataType clientDataItemsType ) 566{ 567 // It should be sufficient to update this variable only here 568 m_clientDataItemsType = clientDataItemsType; 569 570 m_clientDatas.SetCount(n+1,NULL); 571 m_clientDatas[n] = clientData; 572 573 ItemWidthChanged(n); 574} 575 576void* wxVListBoxComboPopup::GetItemClientData(unsigned int n) const 577{ 578 if ( m_clientDatas.GetCount() > n ) 579 return m_clientDatas[n]; 580 581 return NULL; 582} 583 584void wxVListBoxComboPopup::Delete( unsigned int item ) 585{ 586 // Remove client data, if set 587 if ( m_clientDatas.GetCount() ) 588 { 589 if ( m_clientDataItemsType == wxClientData_Object ) 590 delete (wxClientData*) m_clientDatas[item]; 591 592 m_clientDatas.RemoveAt(item); 593 } 594 595 m_strings.RemoveAt(item); 596 m_widths.RemoveAt(item); 597 598 if ( (int)item == m_widestItem ) 599 m_findWidest = true; 600 601 int sel = GetSelection(); 602 603 if ( IsCreated() ) 604 wxVListBox::SetItemCount( wxVListBox::GetItemCount()-1 ); 605 606 // Fix selection 607 if ( (int)item < sel ) 608 SetSelection(sel-1); 609 else if ( (int)item == sel ) 610 SetSelection(wxNOT_FOUND); 611} 612 613int wxVListBoxComboPopup::FindString(const wxString& s, bool bCase) const 614{ 615 return m_strings.Index(s, bCase); 616} 617 618unsigned int wxVListBoxComboPopup::GetCount() const 619{ 620 return m_strings.GetCount(); 621} 622 623wxString wxVListBoxComboPopup::GetString( int item ) const 624{ 625 return m_strings[item]; 626} 627 628void wxVListBoxComboPopup::SetString( int item, const wxString& str ) 629{ 630 m_strings[item] = str; 631 ItemWidthChanged(item); 632} 633 634wxString wxVListBoxComboPopup::GetStringValue() const 635{ 636 if ( m_value >= 0 ) 637 return m_strings[m_value]; 638 return wxEmptyString; 639} 640 641void wxVListBoxComboPopup::SetSelection( int item ) 642{ 643 wxCHECK_RET( item == wxNOT_FOUND || ((unsigned int)item < GetCount()), 644 wxT("invalid index in wxVListBoxComboPopup::SetSelection") ); 645 646 m_value = item; 647 648 if ( IsCreated() ) 649 wxVListBox::SetSelection(item); 650} 651 652int wxVListBoxComboPopup::GetSelection() const 653{ 654 return m_value; 655} 656 657void wxVListBoxComboPopup::SetStringValue( const wxString& value ) 658{ 659 int index = m_strings.Index(value); 660 661 if ( index >= 0 && index < (int)wxVListBox::GetItemCount() ) 662 { 663 m_value = index; 664 wxVListBox::SetSelection(index); 665 } 666} 667 668void wxVListBoxComboPopup::CalcWidths() 669{ 670 bool doFindWidest = m_findWidest; 671 672 // Measure items with dirty width. 673 if ( m_widthsDirty ) 674 { 675 unsigned int i; 676 unsigned int n = m_widths.GetCount(); 677 int dirtyHandled = 0; 678 wxArrayInt& widths = m_widths; 679 680 // I think using wxDC::GetTextExtent is faster than 681 // wxWindow::GetTextExtent (assuming same dc is used 682 // for all calls, as we do here). 683 wxClientDC dc(m_combo); 684 dc.SetFont(m_useFont); 685 686 for ( i=0; i<n; i++ ) 687 { 688 if ( widths[i] < 0 ) 689 { 690 wxCoord x = OnMeasureItemWidth(i); 691 692 if ( x < 0 ) 693 { 694 const wxString& text = m_strings[i]; 695 696 // To make sure performance won't suck in extreme scenarios, 697 // we'll estimate length after some arbitrary number of items 698 // have been checked precily. 699 if ( dirtyHandled < 1024 ) 700 { 701 wxCoord y; 702 dc.GetTextExtent(text, &x, &y, 0, 0); 703 x += 4; 704 } 705 else 706 { 707 x = text.length() * (dc.GetCharWidth()+1); 708 } 709 } 710 711 widths[i] = x; 712 713 if ( x >= m_widestWidth ) 714 { 715 m_widestWidth = x; 716 m_widestItem = (int)i; 717 } 718 else if ( (int)i == m_widestItem ) 719 { 720 // Width of previously widest item has been decreased, so 721 // we'll have to check all to find current widest item. 722 doFindWidest = true; 723 } 724 725 dirtyHandled++; 726 } 727 } 728 729 m_widthsDirty = false; 730 } 731 732 if ( doFindWidest ) 733 { 734 unsigned int i; 735 unsigned int n = m_widths.GetCount(); 736 737 int bestWidth = -1; 738 int bestIndex = -1; 739 740 for ( i=0; i<n; i++ ) 741 { 742 int w = m_widths[i]; 743 if ( w > bestWidth ) 744 { 745 bestIndex = (int)i; 746 bestWidth = w; 747 } 748 } 749 750 m_widestWidth = bestWidth; 751 m_widestItem = bestIndex; 752 753 m_findWidest = false; 754 } 755} 756 757wxSize wxVListBoxComboPopup::GetAdjustedSize( int minWidth, int prefHeight, int maxHeight ) 758{ 759 int height = 250; 760 761 maxHeight -= 2; // Must take borders into account 762 763 if ( m_strings.GetCount() ) 764 { 765 if ( prefHeight > 0 ) 766 height = prefHeight; 767 768 if ( height > maxHeight ) 769 height = maxHeight; 770 771 int totalHeight = GetTotalHeight(); // + 3; 772 773#if defined(__WXMAC__) 774 // Take borders into account, or there will be scrollbars even for one or two items. 775 totalHeight += 2; 776#endif 777 if ( height >= totalHeight ) 778 { 779 height = totalHeight; 780 } 781 else 782 { 783 // Adjust height to a multiple of the height of the first item 784 // NB: Calculations that take variable height into account 785 // are unnecessary. 786 int fih = GetLineHeight(0); 787 height -= height % fih; 788 } 789 } 790 else 791 height = 50; 792 793 CalcWidths(); 794 795 // Take scrollbar into account in width calculations 796 int widestWidth = m_widestWidth + wxSystemSettings::GetMetric(wxSYS_VSCROLL_X); 797 return wxSize(minWidth > widestWidth ? minWidth : widestWidth, 798 height+2); 799} 800 801//void wxVListBoxComboPopup::Populate( int n, const wxString choices[] ) 802void wxVListBoxComboPopup::Populate( const wxArrayString& choices ) 803{ 804 int i; 805 806 int n = choices.GetCount(); 807 808 for ( i=0; i<n; i++ ) 809 { 810 const wxString& item = choices.Item(i); 811 m_strings.Add(item); 812 } 813 814 m_widths.SetCount(n,-1); 815 m_widthsDirty = true; 816 817 if ( IsCreated() ) 818 wxVListBox::SetItemCount(n); 819 820 // Sort the initial choices 821 if ( m_combo->GetWindowStyle() & wxCB_SORT ) 822 m_strings.Sort(); 823 824 // Find initial selection 825 wxString strValue = m_combo->GetValue(); 826 if ( strValue.length() ) 827 m_value = m_strings.Index(strValue); 828} 829 830// ---------------------------------------------------------------------------- 831// wxOwnerDrawnComboBox 832// ---------------------------------------------------------------------------- 833 834 835BEGIN_EVENT_TABLE(wxOwnerDrawnComboBox, wxComboCtrl) 836END_EVENT_TABLE() 837 838 839IMPLEMENT_DYNAMIC_CLASS2(wxOwnerDrawnComboBox, wxComboCtrl, wxControlWithItems) 840 841void wxOwnerDrawnComboBox::Init() 842{ 843} 844 845bool wxOwnerDrawnComboBox::Create(wxWindow *parent, 846 wxWindowID id, 847 const wxString& value, 848 const wxPoint& pos, 849 const wxSize& size, 850 long style, 851 const wxValidator& validator, 852 const wxString& name) 853{ 854 return wxComboCtrl::Create(parent,id,value,pos,size,style,validator,name); 855} 856 857wxOwnerDrawnComboBox::wxOwnerDrawnComboBox(wxWindow *parent, 858 wxWindowID id, 859 const wxString& value, 860 const wxPoint& pos, 861 const wxSize& size, 862 const wxArrayString& choices, 863 long style, 864 const wxValidator& validator, 865 const wxString& name) 866 : wxComboCtrl() 867{ 868 Init(); 869 870 Create(parent,id,value,pos,size,choices,style, validator, name); 871} 872 873bool wxOwnerDrawnComboBox::Create(wxWindow *parent, 874 wxWindowID id, 875 const wxString& value, 876 const wxPoint& pos, 877 const wxSize& size, 878 const wxArrayString& choices, 879 long style, 880 const wxValidator& validator, 881 const wxString& name) 882{ 883 m_initChs = choices; 884 //wxCArrayString chs(choices); 885 886 //return Create(parent, id, value, pos, size, chs.GetCount(), 887 // chs.GetStrings(), style, validator, name); 888 return Create(parent, id, value, pos, size, 0, 889 NULL, style, validator, name); 890} 891 892bool wxOwnerDrawnComboBox::Create(wxWindow *parent, 893 wxWindowID id, 894 const wxString& value, 895 const wxPoint& pos, 896 const wxSize& size, 897 int n, 898 const wxString choices[], 899 long style, 900 const wxValidator& validator, 901 const wxString& name) 902{ 903 904 if ( !Create(parent, id, value, pos, size, style, 905 validator, name) ) 906 { 907 return false; 908 } 909 910 int i; 911 for ( i=0; i<n; i++ ) 912 m_initChs.Add(choices[i]); 913 914 return true; 915} 916 917wxOwnerDrawnComboBox::~wxOwnerDrawnComboBox() 918{ 919 if ( m_popupInterface ) 920 GetVListBoxComboPopup()->ClearClientDatas(); 921} 922 923void wxOwnerDrawnComboBox::DoSetPopupControl(wxComboPopup* popup) 924{ 925 if ( !popup ) 926 { 927 popup = new wxVListBoxComboPopup(); 928 } 929 930 wxComboCtrl::DoSetPopupControl(popup); 931 932 wxASSERT(popup); 933 934 // Add initial choices to the wxVListBox 935 if ( !GetVListBoxComboPopup()->GetCount() ) 936 { 937 GetVListBoxComboPopup()->Populate(m_initChs); 938 m_initChs.Clear(); 939 } 940} 941 942// ---------------------------------------------------------------------------- 943// wxOwnerDrawnComboBox item manipulation methods 944// ---------------------------------------------------------------------------- 945 946void wxOwnerDrawnComboBox::Clear() 947{ 948 EnsurePopupControl(); 949 950 GetVListBoxComboPopup()->Clear(); 951 952 SetValue(wxEmptyString); 953} 954 955void wxOwnerDrawnComboBox::Delete(unsigned int n) 956{ 957 wxCHECK_RET( IsValid(n), _T("invalid index in wxOwnerDrawnComboBox::Delete") ); 958 959 if ( GetSelection() == (int) n ) 960 SetValue(wxEmptyString); 961 962 GetVListBoxComboPopup()->Delete(n); 963} 964 965unsigned int wxOwnerDrawnComboBox::GetCount() const 966{ 967 if ( !m_popupInterface ) 968 return m_initChs.GetCount(); 969 970 return GetVListBoxComboPopup()->GetCount(); 971} 972 973wxString wxOwnerDrawnComboBox::GetString(unsigned int n) const 974{ 975 wxCHECK_MSG( IsValid(n), wxEmptyString, _T("invalid index in wxOwnerDrawnComboBox::GetString") ); 976 977 if ( !m_popupInterface ) 978 return m_initChs.Item(n); 979 980 return GetVListBoxComboPopup()->GetString(n); 981} 982 983void wxOwnerDrawnComboBox::SetString(unsigned int n, const wxString& s) 984{ 985 EnsurePopupControl(); 986 987 wxCHECK_RET( IsValid(n), _T("invalid index in wxOwnerDrawnComboBox::SetString") ); 988 989 GetVListBoxComboPopup()->SetString(n,s); 990} 991 992int wxOwnerDrawnComboBox::FindString(const wxString& s, bool bCase) const 993{ 994 if ( !m_popupInterface ) 995 return m_initChs.Index(s, bCase); 996 997 return GetVListBoxComboPopup()->FindString(s, bCase); 998} 999 1000void wxOwnerDrawnComboBox::Select(int n) 1001{ 1002 EnsurePopupControl(); 1003 1004 wxCHECK_RET( (n == wxNOT_FOUND) || IsValid(n), _T("invalid index in wxOwnerDrawnComboBox::Select") ); 1005 1006 GetVListBoxComboPopup()->SetSelection(n); 1007 1008 wxString str; 1009 if ( n >= 0 ) 1010 str = GetVListBoxComboPopup()->GetString(n); 1011 1012 // Refresh text portion in control 1013 if ( m_text ) 1014 m_text->SetValue( str ); 1015 else 1016 m_valueString = str; 1017 1018 Refresh(); 1019} 1020 1021int wxOwnerDrawnComboBox::GetSelection() const 1022{ 1023 if ( !m_popupInterface ) 1024 return m_initChs.Index(m_valueString); 1025 1026 return GetVListBoxComboPopup()->GetSelection(); 1027} 1028 1029int wxOwnerDrawnComboBox::DoAppend(const wxString& item) 1030{ 1031 EnsurePopupControl(); 1032 wxASSERT(m_popupInterface); 1033 1034 return GetVListBoxComboPopup()->Append(item); 1035} 1036 1037int wxOwnerDrawnComboBox::DoInsert(const wxString& item, unsigned int pos) 1038{ 1039 EnsurePopupControl(); 1040 1041 wxCHECK_MSG(!(GetWindowStyle() & wxCB_SORT), -1, wxT("can't insert into sorted list")); 1042 wxCHECK_MSG(IsValidInsert(pos), -1, wxT("invalid index")); 1043 1044 GetVListBoxComboPopup()->Insert(item,pos); 1045 1046 return pos; 1047} 1048 1049void wxOwnerDrawnComboBox::DoSetItemClientData(unsigned int n, void* clientData) 1050{ 1051 EnsurePopupControl(); 1052 1053 GetVListBoxComboPopup()->SetItemClientData(n,clientData,m_clientDataItemsType); 1054} 1055 1056void* wxOwnerDrawnComboBox::DoGetItemClientData(unsigned int n) const 1057{ 1058 if ( !m_popupInterface ) 1059 return NULL; 1060 1061 return GetVListBoxComboPopup()->GetItemClientData(n); 1062} 1063 1064void wxOwnerDrawnComboBox::DoSetItemClientObject(unsigned int n, wxClientData* clientData) 1065{ 1066 DoSetItemClientData(n, (void*) clientData); 1067} 1068 1069wxClientData* wxOwnerDrawnComboBox::DoGetItemClientObject(unsigned int n) const 1070{ 1071 return (wxClientData*) DoGetItemClientData(n); 1072} 1073 1074// ---------------------------------------------------------------------------- 1075// wxOwnerDrawnComboBox item drawing and measuring default implementations 1076// ---------------------------------------------------------------------------- 1077 1078void wxOwnerDrawnComboBox::OnDrawItem( wxDC& dc, 1079 const wxRect& rect, 1080 int item, 1081 int flags ) const 1082{ 1083 if ( flags & wxODCB_PAINTING_CONTROL ) 1084 { 1085 dc.DrawText( GetValue(), 1086 rect.x + GetTextIndent(), 1087 (rect.height-dc.GetCharHeight())/2 + rect.y ); 1088 } 1089 else 1090 { 1091 dc.DrawText( GetVListBoxComboPopup()->GetString(item), rect.x + 2, rect.y ); 1092 } 1093} 1094 1095wxCoord wxOwnerDrawnComboBox::OnMeasureItem( size_t WXUNUSED(item) ) const 1096{ 1097 return -1; 1098} 1099 1100wxCoord wxOwnerDrawnComboBox::OnMeasureItemWidth( size_t WXUNUSED(item) ) const 1101{ 1102 return -1; 1103} 1104 1105void wxOwnerDrawnComboBox::OnDrawBackground(wxDC& dc, 1106 const wxRect& rect, 1107 int WXUNUSED(item), 1108 int flags) const 1109{ 1110 // We need only to explicitly draw background for items 1111 // that should have selected background. Also, call PrepareBackground 1112 // always when painting the control so that clipping is done properly. 1113 1114 if ( (flags & wxODCB_PAINTING_SELECTED) || 1115 ((flags & wxODCB_PAINTING_CONTROL) && HasFlag(wxCB_READONLY)) ) 1116 { 1117 int bgFlags = wxCONTROL_SELECTED; 1118 1119 if ( !(flags & wxODCB_PAINTING_CONTROL) ) 1120 bgFlags |= wxCONTROL_ISSUBMENU; 1121 1122 PrepareBackground(dc, rect, bgFlags); 1123 } 1124} 1125 1126#endif // wxUSE_ODCOMBOBOX 1127