1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/generic/odcombo.cpp 
   3 // Purpose:     wxOwnerDrawnComboBox, wxVListBoxComboPopup 
   4 // Author:      Jaakko Salli 
   6 // Created:     Apr-30-2006 
   8 // Copyright:   (c) 2005 Jaakko Salli 
   9 // Licence:     wxWindows licence 
  10 ///////////////////////////////////////////////////////////////////////////// 
  12 // ============================================================================ 
  14 // ============================================================================ 
  16 // ---------------------------------------------------------------------------- 
  18 // ---------------------------------------------------------------------------- 
  20 #include "wx/wxprec.h" 
  28 #include "wx/odcombo.h" 
  32     #include "wx/combobox.h" 
  33     #include "wx/dcclient.h" 
  34     #include "wx/settings.h" 
  35     #include "wx/dialog.h" 
  40 // ============================================================================ 
  42 // ============================================================================ 
  45 // ---------------------------------------------------------------------------- 
  46 // wxVListBoxComboPopup is a wxVListBox customized to act as a popup control 
  48 // ---------------------------------------------------------------------------- 
  51 BEGIN_EVENT_TABLE(wxVListBoxComboPopup
, wxVListBox
) 
  52     EVT_MOTION(wxVListBoxComboPopup::OnMouseMove
) 
  53     EVT_KEY_DOWN(wxVListBoxComboPopup::OnKey
) 
  54     EVT_LEFT_UP(wxVListBoxComboPopup::OnLeftClick
) 
  58 void wxVListBoxComboPopup::Init() 
  66     m_clientDataItemsType 
= wxClientData_None
; 
  69 bool wxVListBoxComboPopup::Create(wxWindow
* parent
) 
  71     if ( !wxVListBox::Create(parent
, 
  75                              wxBORDER_SIMPLE 
| wxLB_INT_HEIGHT 
| wxWANTS_CHARS
) ) 
  78     m_useFont 
= m_combo
->GetFont(); 
  80     wxVListBox::SetItemCount(m_strings
.GetCount()); 
  82     // TODO: Move this to SetFont 
  83     m_itemHeight 
= GetCharHeight() + 0; 
  88 wxVListBoxComboPopup::~wxVListBoxComboPopup() 
  93 bool wxVListBoxComboPopup::LazyCreate() 
  95     // NB: There is a bug with wxVListBox that can be avoided by creating 
  96     //     it later (bug causes empty space to be shown if initial selection 
  97     //     is at the end of a list longer than the control can show at once). 
 101 // paint the control itself 
 102 void wxVListBoxComboPopup::PaintComboControl( wxDC
& dc
, const wxRect
& rect 
) 
 104     if ( !(m_combo
->GetWindowStyle() & wxODCB_STD_CONTROL_PAINT
) ) 
 106         m_combo
->DrawFocusBackground(dc
,rect
,0); 
 109             OnDrawItem(dc
,rect
,m_value
,wxCP_PAINTING_CONTROL
); 
 114     wxComboPopup::PaintComboControl(dc
,rect
); 
 117 void wxVListBoxComboPopup::OnDrawItem(wxDC
& dc
, const wxRect
& rect
, size_t n
) const 
 119     // TODO: Maybe this code could be moved to wxVListBox::OnPaint? 
 120     dc
.SetFont(m_useFont
); 
 122     // Set correct text colour for selected items 
 123     if ( wxVListBox::GetSelection() == (int) n 
) 
 124         dc
.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT
) ); 
 126         dc
.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT
) ); 
 128     OnDrawItem(dc
,rect
,(int)n
,0); 
 131 wxCoord 
wxVListBoxComboPopup::OnMeasureItem(size_t WXUNUSED(n
)) const 
 136 wxCoord 
wxVListBoxComboPopup::OnMeasureItemWidth(size_t WXUNUSED(n
)) const 
 138     //return OnMeasureListItemWidth(n); 
 142 void wxVListBoxComboPopup::OnDrawBackground(wxDC
& dc
, const wxRect
& rect
, size_t n
) const 
 144     // we need to render selected and current items differently 
 147         m_combo
->DrawFocusBackground( dc
, rect
, wxCONTROL_ISSUBMENU
|wxCONTROL_SELECTED 
); 
 149     //else: do nothing for the normal items 
 152 // This is called from wxVListBoxComboPopup::OnDrawItem, with text colour and font prepared 
 153 void wxVListBoxComboPopup::OnDrawItem( wxDC
& dc
, const wxRect
& rect
, int item
, int flags 
) const 
 155     if ( flags 
& wxCP_PAINTING_CONTROL 
) 
 157         dc
.DrawText( m_combo
->GetValue(), 
 158                      rect
.x 
+ m_combo
->GetTextIndent(), 
 159                      (rect
.height
-dc
.GetCharHeight())/2 + rect
.y 
); 
 163         dc
.DrawText( GetString(item
), rect
.x 
+ 2, rect
.y 
); 
 167 void wxVListBoxComboPopup::DismissWithEvent() 
 169     int selection 
= wxVListBox::GetSelection(); 
 174     if ( selection 
!= wxNOT_FOUND 
) 
 175         valStr 
= m_strings
[selection
]; 
 177         valStr 
= wxEmptyString
; 
 181     if ( valStr 
!= m_combo
->GetValue() ) 
 182         m_combo
->SetValue(valStr
); 
 184     SendComboBoxEvent(selection
); 
 187 void wxVListBoxComboPopup::SendComboBoxEvent( int selection 
) 
 189     wxCommandEvent 
evt(wxEVT_COMMAND_COMBOBOX_SELECTED
,m_combo
->GetId()); 
 191     evt
.SetEventObject(m_combo
); 
 193     evt
.SetInt(selection
); 
 195     // Set client data, if any 
 196     if ( selection 
>= 0 && (int)m_clientDatas
.GetCount() > selection 
) 
 198         void* clientData 
= m_clientDatas
[selection
]; 
 199         if ( m_clientDataItemsType 
== wxClientData_Object 
) 
 200             evt
.SetClientObject((wxClientData
*)clientData
); 
 202             evt
.SetClientData(clientData
); 
 205     m_combo
->GetEventHandler()->AddPendingEvent(evt
); 
 208 // returns true if key was consumed 
 209 bool wxVListBoxComboPopup::HandleKey( int keycode
, bool saturate 
) 
 212     int itemCount 
= GetCount(); 
 214     if ( keycode 
== WXK_DOWN 
|| keycode 
== WXK_RIGHT 
) 
 218     else if ( keycode 
== WXK_UP 
|| keycode 
== WXK_LEFT 
) 
 222     else if ( keycode 
== WXK_PAGEDOWN 
) 
 226     else if ( keycode 
== WXK_PAGEUP 
) 
 235         if ( value 
>= itemCount 
) 
 236             value 
= itemCount 
- 1; 
 237         else if ( value 
< 0 ) 
 242         if ( value 
>= itemCount 
) 
 244         else if ( value 
< 0 ) 
 248     if ( value 
== m_value 
) 
 249         // Even if value was same, don't skip the event 
 250         // (good for consistency) 
 256         m_combo
->SetValue(m_strings
[value
]); 
 258     SendComboBoxEvent(m_value
); 
 263 void wxVListBoxComboPopup::OnComboDoubleClick() 
 265     // Cycle on dclick (disable saturation to allow true cycling). 
 266     if ( !::wxGetKeyState(WXK_SHIFT
) ) 
 267         HandleKey(WXK_DOWN
,false); 
 269         HandleKey(WXK_UP
,false); 
 272 void wxVListBoxComboPopup::OnComboKeyEvent( wxKeyEvent
& event 
) 
 274     // Saturated key movement on 
 275     if ( !HandleKey(event
.GetKeyCode(),true) ) 
 279 void wxVListBoxComboPopup::OnPopup() 
 281     // *must* set value after size is set (this is because of a vlbox bug) 
 282     wxVListBox::SetSelection(m_value
); 
 285 void wxVListBoxComboPopup::OnMouseMove(wxMouseEvent
& event
) 
 287     // Move selection to cursor if it is inside the popup 
 288     int itemHere 
= GetItemAtPosition(event
.GetPosition()); 
 290         wxVListBox::SetSelection(itemHere
); 
 295 void wxVListBoxComboPopup::OnLeftClick(wxMouseEvent
& WXUNUSED(event
)) 
 300 void wxVListBoxComboPopup::OnKey(wxKeyEvent
& event
) 
 302     // Select item if ENTER is pressed 
 303     if ( event
.GetKeyCode() == WXK_RETURN 
|| event
.GetKeyCode() == WXK_NUMPAD_ENTER 
) 
 307     // Hide popup if ESC is pressed 
 308     else if ( event
.GetKeyCode() == WXK_ESCAPE 
) 
 314 void wxVListBoxComboPopup::CheckWidth( int pos 
) 
 316     wxCoord x 
= OnMeasureItemWidth(pos
); 
 320         if ( !m_useFont
.Ok() ) 
 321             m_useFont 
= m_combo
->GetFont(); 
 324         m_combo
->GetTextExtent(m_strings
[pos
], &x
, &y
, 0, 0, &m_useFont
); 
 328     if ( m_widestWidth 
< x 
) 
 334 void wxVListBoxComboPopup::Insert( const wxString
& item
, int pos 
) 
 336     // Need to change selection? 
 338     if ( !(m_combo
->GetWindowStyle() & wxCB_READONLY
) && 
 339          m_combo
->GetValue() == item 
) 
 344     m_strings
.Insert(item
,pos
); 
 347         wxVListBox::SetItemCount( wxVListBox::GetItemCount()+1 ); 
 353 int wxVListBoxComboPopup::Append(const wxString
& item
) 
 355     int pos 
= (int)m_strings
.GetCount(); 
 357     if ( m_combo
->GetWindowStyle() & wxCB_SORT 
) 
 360         // TODO: Could be optimized with binary search 
 361         wxArrayString strings 
= m_strings
; 
 364         for ( i
=0; i
<strings
.GetCount(); i
++ ) 
 366             if ( item
.Cmp(strings
.Item(i
)) < 0 ) 
 379 void wxVListBoxComboPopup::Clear() 
 387     m_value 
= wxNOT_FOUND
; 
 390         wxVListBox::SetItemCount(0); 
 393 void wxVListBoxComboPopup::ClearClientDatas() 
 395     if ( m_clientDataItemsType 
== wxClientData_Object 
) 
 398         for ( i
=0; i
<m_clientDatas
.GetCount(); i
++ ) 
 399             delete (wxClientData
*) m_clientDatas
[i
]; 
 402     m_clientDatas
.Empty(); 
 405 void wxVListBoxComboPopup::SetItemClientData( unsigned int n
, 
 407                                               wxClientDataType clientDataItemsType 
) 
 409     // It should be sufficient to update this variable only here 
 410     m_clientDataItemsType 
= clientDataItemsType
; 
 412     m_clientDatas
.SetCount(n
+1,NULL
); 
 413     m_clientDatas
[n
] = clientData
; 
 416 void* wxVListBoxComboPopup::GetItemClientData(unsigned int n
) const 
 418     if ( m_clientDatas
.GetCount() > n 
) 
 419         return m_clientDatas
[n
]; 
 424 void wxVListBoxComboPopup::Delete( unsigned int item 
) 
 426     // Remove client data, if set 
 427     if ( m_clientDatas
.GetCount() ) 
 429         if ( m_clientDataItemsType 
== wxClientData_Object 
) 
 430             delete (wxClientData
*) m_clientDatas
[item
]; 
 432         m_clientDatas
.RemoveAt(item
); 
 435     m_strings
.RemoveAt(item
); 
 438         wxVListBox::SetItemCount( wxVListBox::GetItemCount()-1 ); 
 441 int wxVListBoxComboPopup::FindString(const wxString
& s
, bool bCase
) const 
 443     return m_strings
.Index(s
, bCase
); 
 446 unsigned int wxVListBoxComboPopup::GetCount() const 
 448     return m_strings
.GetCount(); 
 451 wxString 
wxVListBoxComboPopup::GetString( int item 
) const 
 453     return m_strings
[item
]; 
 456 void wxVListBoxComboPopup::SetString( int item
, const wxString
& str 
) 
 458     m_strings
[item
] = str
; 
 461 wxString 
wxVListBoxComboPopup::GetStringValue() const 
 464         return m_strings
[m_value
]; 
 465     return wxEmptyString
; 
 468 void wxVListBoxComboPopup::SetSelection( int item 
) 
 470     wxCHECK_RET( item 
== wxNOT_FOUND 
|| ((unsigned int)item 
< GetCount()), 
 471                  wxT("invalid index in wxVListBoxComboPopup::SetSelection") ); 
 476         wxVListBox::SetSelection(item
); 
 479 int wxVListBoxComboPopup::GetSelection() const 
 484 void wxVListBoxComboPopup::SetStringValue( const wxString
& value 
) 
 486     int index 
= m_strings
.Index(value
); 
 490     if ( index 
>= -1 && index 
< (int)wxVListBox::GetItemCount() ) 
 491         wxVListBox::SetSelection(index
); 
 494 wxSize 
wxVListBoxComboPopup::GetAdjustedSize( int minWidth
, int prefHeight
, int maxHeight 
) 
 498     if ( m_strings
.GetCount() ) 
 500         if ( prefHeight 
> 0 ) 
 503         if ( height 
> maxHeight 
) 
 506         int totalHeight 
= GetTotalHeight(); // + 3; 
 507         if ( height 
>= totalHeight 
) 
 509             height 
= totalHeight
; 
 513             // Adjust height to a multiple of the height of the first item 
 514             // NB: Calculations that take variable height into account 
 516             int fih 
= GetLineHeight(0); 
 517             int shown 
= height
/fih
; 
 518             height 
= shown 
* fih
; 
 524     // Take scrollbar into account in width calculations 
 525     int widestWidth 
= m_widestWidth 
+ wxSystemSettings::GetMetric(wxSYS_VSCROLL_X
); 
 526     return wxSize(minWidth 
> widestWidth 
? minWidth 
: widestWidth
, 
 530 //void wxVListBoxComboPopup::Populate( int n, const wxString choices[] ) 
 531 void wxVListBoxComboPopup::Populate( const wxArrayString
& choices 
) 
 535     int n 
= choices
.GetCount(); 
 537     for ( i
=0; i
<n
; i
++ ) 
 539         const wxString
& item 
= choices
.Item(i
); 
 545         wxVListBox::SetItemCount(n
); 
 547     // Sort the initial choices 
 548     if ( m_combo
->GetWindowStyle() & wxCB_SORT 
) 
 551     // Find initial selection 
 552     wxString strValue 
= m_combo
->GetValue(); 
 553     if ( strValue
.Length() ) 
 554         m_value 
= m_strings
.Index(strValue
); 
 557 // ---------------------------------------------------------------------------- 
 558 // wxOwnerDrawnComboBox 
 559 // ---------------------------------------------------------------------------- 
 562 BEGIN_EVENT_TABLE(wxOwnerDrawnComboBox
, wxComboCtrl
) 
 566 IMPLEMENT_DYNAMIC_CLASS2(wxOwnerDrawnComboBox
, wxComboCtrl
, wxControlWithItems
) 
 568 void wxOwnerDrawnComboBox::Init() 
 570     m_popupInterface 
= NULL
; 
 573 bool wxOwnerDrawnComboBox::Create(wxWindow 
*parent
, 
 575                                   const wxString
& value
, 
 579                                   const wxValidator
& validator
, 
 580                                   const wxString
& name
) 
 582     return wxComboCtrl::Create(parent
,id
,value
,pos
,size
,style
,validator
,name
); 
 585 wxOwnerDrawnComboBox::wxOwnerDrawnComboBox(wxWindow 
*parent
, 
 587                                            const wxString
& value
, 
 590                                            const wxArrayString
& choices
, 
 592                                            const wxValidator
& validator
, 
 593                                            const wxString
& name
) 
 598     Create(parent
,id
,value
,pos
,size
,choices
,style
, validator
, name
); 
 601 bool wxOwnerDrawnComboBox::Create(wxWindow 
*parent
, 
 603                                   const wxString
& value
, 
 606                                   const wxArrayString
& choices
, 
 608                                   const wxValidator
& validator
, 
 609                                   const wxString
& name
) 
 612     //wxCArrayString chs(choices); 
 614     //return Create(parent, id, value, pos, size, chs.GetCount(), 
 615     //              chs.GetStrings(), style, validator, name); 
 616     return Create(parent
, id
, value
, pos
, size
, 0, 
 617                   NULL
, style
, validator
, name
); 
 620 bool wxOwnerDrawnComboBox::Create(wxWindow 
*parent
, 
 622                                   const wxString
& value
, 
 626                                   const wxString choices
[], 
 628                                   const wxValidator
& validator
, 
 629                                   const wxString
& name
) 
 632     if ( !Create(parent
, id
, value
, pos
, size
, style
, 
 639     for ( i
=0; i
<n
; i
++ ) 
 640         m_initChs
.Add(choices
[i
]); 
 645 wxOwnerDrawnComboBox::~wxOwnerDrawnComboBox() 
 647     if ( m_popupInterface 
) 
 648         m_popupInterface
->ClearClientDatas(); 
 651 void wxOwnerDrawnComboBox::SetPopupControl( wxComboPopup
* popup 
) 
 655         popup 
= new wxVListBoxComboPopup(); 
 658     wxComboCtrl::SetPopupControl(popup
); 
 661     m_popupInterface 
= (wxVListBoxComboPopup
*) popup
; 
 663     // Add initial choices to the wxVListBox 
 664     if ( !m_popupInterface
->GetCount() ) 
 666         //m_popupInterface->Populate(m_initChs.GetCount(),m_initChs.GetStrings()); 
 667         m_popupInterface
->Populate(m_initChs
); 
 672 // ---------------------------------------------------------------------------- 
 673 // wxOwnerDrawnComboBox item manipulation methods 
 674 // ---------------------------------------------------------------------------- 
 676 void wxOwnerDrawnComboBox::Clear() 
 678     EnsurePopupControl(); 
 680     m_popupInterface
->Clear(); 
 682     SetValue(wxEmptyString
); 
 685 void wxOwnerDrawnComboBox::Delete(unsigned int n
) 
 687     wxCHECK_RET( IsValid(n
), _T("invalid index in wxOwnerDrawnComboBox::Delete") ); 
 689     if ( GetSelection() == (int) n 
) 
 690         SetValue(wxEmptyString
); 
 692     m_popupInterface
->Delete(n
); 
 695 unsigned int wxOwnerDrawnComboBox::GetCount() const 
 697     wxASSERT_MSG( m_popupInterface
, wxT("no popup interface") ); 
 698     return m_popupInterface
->GetCount(); 
 701 wxString 
wxOwnerDrawnComboBox::GetString(unsigned int n
) const 
 703     wxCHECK_MSG( IsValid(n
), wxEmptyString
, _T("invalid index in wxOwnerDrawnComboBox::GetString") ); 
 704     return m_popupInterface
->GetString(n
); 
 707 void wxOwnerDrawnComboBox::SetString(unsigned int n
, const wxString
& s
) 
 709     wxCHECK_RET( IsValid(n
), _T("invalid index in wxOwnerDrawnComboBox::SetString") ); 
 710     m_popupInterface
->SetString(n
,s
); 
 713 int wxOwnerDrawnComboBox::FindString(const wxString
& s
, bool bCase
) const 
 715     wxASSERT_MSG( m_popupInterface
, wxT("no popup interface") ); 
 716     return m_popupInterface
->FindString(s
, bCase
); 
 719 void wxOwnerDrawnComboBox::Select(int n
) 
 721     wxCHECK_RET( (n 
== wxNOT_FOUND
) || IsValid(n
), _T("invalid index in wxOwnerDrawnComboBox::Select") ); 
 722     EnsurePopupControl(); 
 724     m_popupInterface
->SetSelection(n
); 
 728         str 
= m_popupInterface
->GetString(n
); 
 730     // Refresh text portion in control 
 732         m_text
->SetValue( str 
); 
 739 int wxOwnerDrawnComboBox::GetSelection() const 
 741     wxASSERT_MSG( m_popupInterface
, wxT("no popup interface") ); 
 742     return m_popupInterface
->GetSelection(); 
 745 int wxOwnerDrawnComboBox::DoAppend(const wxString
& item
) 
 747     EnsurePopupControl(); 
 748     wxASSERT(m_popupInterface
); 
 749     return m_popupInterface
->Append(item
); 
 752 int wxOwnerDrawnComboBox::DoInsert(const wxString
& item
, unsigned int pos
) 
 754     wxCHECK_MSG(!(GetWindowStyle() & wxCB_SORT
), -1, wxT("can't insert into sorted list")); 
 755     wxCHECK_MSG(IsValidInsert(pos
), -1, wxT("invalid index")); 
 757     EnsurePopupControl(); 
 758     m_popupInterface
->Insert(item
,pos
); 
 763 void wxOwnerDrawnComboBox::DoSetItemClientData(unsigned int n
, void* clientData
) 
 765     EnsurePopupControl(); 
 766     m_popupInterface
->SetItemClientData(n
,clientData
,m_clientDataItemsType
); 
 769 void* wxOwnerDrawnComboBox::DoGetItemClientData(unsigned int n
) const 
 771     wxASSERT_MSG( m_popupInterface
, wxT("no popup interface") ); 
 772     return m_popupInterface
->GetItemClientData(n
); 
 775 void wxOwnerDrawnComboBox::DoSetItemClientObject(unsigned int n
, wxClientData
* clientData
) 
 777     DoSetItemClientData(n
, (void*) clientData
); 
 780 wxClientData
* wxOwnerDrawnComboBox::DoGetItemClientObject(unsigned int n
) const 
 782     return (wxClientData
*) DoGetItemClientData(n
); 
 785 #endif // wxUSE_ODCOMBOBOX