1 ///////////////////////////////////////////////////////////////////////////// 
   4 // Author:      Julian Smart 
   5 // Modified by: Vadim Zeitlin to derive from wxChoiceBase 
   8 // Copyright:   (c) Julian Smart 
   9 // Licence:     wxWindows licence 
  10 ///////////////////////////////////////////////////////////////////////////// 
  12 // ============================================================================ 
  14 // ============================================================================ 
  16 // ---------------------------------------------------------------------------- 
  18 // ---------------------------------------------------------------------------- 
  20 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA) 
  21     #pragma implementation "choice.h" 
  24 // For compilers that support precompilation, includes "wx.h". 
  25 #include "wx/wxprec.h" 
  34     #include "wx/choice.h" 
  38     #include "wx/settings.h" 
  41 #include "wx/msw/private.h" 
  43 #if wxUSE_EXTENDED_RTTI 
  44 WX_DEFINE_FLAGS( wxChoiceStyle 
) 
  46 wxBEGIN_FLAGS( wxChoiceStyle 
) 
  47     // new style border flags, we put them first to 
  48     // use them for streaming out 
  49     wxFLAGS_MEMBER(wxBORDER_SIMPLE
) 
  50     wxFLAGS_MEMBER(wxBORDER_SUNKEN
) 
  51     wxFLAGS_MEMBER(wxBORDER_DOUBLE
) 
  52     wxFLAGS_MEMBER(wxBORDER_RAISED
) 
  53     wxFLAGS_MEMBER(wxBORDER_STATIC
) 
  54     wxFLAGS_MEMBER(wxBORDER_NONE
) 
  56     // old style border flags 
  57     wxFLAGS_MEMBER(wxSIMPLE_BORDER
) 
  58     wxFLAGS_MEMBER(wxSUNKEN_BORDER
) 
  59     wxFLAGS_MEMBER(wxDOUBLE_BORDER
) 
  60     wxFLAGS_MEMBER(wxRAISED_BORDER
) 
  61     wxFLAGS_MEMBER(wxSTATIC_BORDER
) 
  62     wxFLAGS_MEMBER(wxBORDER
) 
  64     // standard window styles 
  65     wxFLAGS_MEMBER(wxTAB_TRAVERSAL
) 
  66     wxFLAGS_MEMBER(wxCLIP_CHILDREN
) 
  67     wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW
) 
  68     wxFLAGS_MEMBER(wxWANTS_CHARS
) 
  69     wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE
) 
  70     wxFLAGS_MEMBER(wxALWAYS_SHOW_SB 
) 
  71     wxFLAGS_MEMBER(wxVSCROLL
) 
  72     wxFLAGS_MEMBER(wxHSCROLL
) 
  74 wxEND_FLAGS( wxChoiceStyle 
) 
  76 IMPLEMENT_DYNAMIC_CLASS_XTI(wxChoice
, wxControl
,"wx/choice.h") 
  78 wxBEGIN_PROPERTIES_TABLE(wxChoice
) 
  79         wxEVENT_PROPERTY( Select 
, wxEVT_COMMAND_CHOICE_SELECTED 
, wxCommandEvent 
) 
  81     wxPROPERTY( Font 
, wxFont 
, SetFont 
, GetFont  
, , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) 
  82     wxPROPERTY_COLLECTION( Choices 
, wxArrayString 
, wxString 
, AppendString 
, GetStrings 
, 0 /*flags*/ , wxT("Helpstring") , wxT("group")) 
  83         wxPROPERTY( Selection 
,int, SetSelection
, GetSelection
, , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) 
  84     wxPROPERTY_FLAGS( WindowStyle 
, wxChoiceStyle 
, long , SetWindowStyleFlag 
, GetWindowStyleFlag 
, , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style 
  85 wxEND_PROPERTIES_TABLE() 
  87 wxBEGIN_HANDLERS_TABLE(wxChoice
) 
  88 wxEND_HANDLERS_TABLE() 
  90 wxCONSTRUCTOR_4( wxChoice 
, wxWindow
* , Parent 
, wxWindowID 
, Id 
, wxPoint 
, Position 
, wxSize 
, Size 
)  
  92 IMPLEMENT_DYNAMIC_CLASS(wxChoice
, wxControl
) 
 101 // ============================================================================ 
 103 // ============================================================================ 
 105 // ---------------------------------------------------------------------------- 
 107 // ---------------------------------------------------------------------------- 
 109 bool wxChoice::Create(wxWindow 
*parent
, 
 113                       int n
, const wxString choices
[], 
 115                       const wxValidator
& validator
, 
 116                       const wxString
& name
) 
 118     // Experience shows that wxChoice vs. wxComboBox distinction confuses 
 119     // quite a few people - try to help them 
 120     wxASSERT_MSG( !(style 
& wxCB_DROPDOWN
) && 
 121                   !(style 
& wxCB_READONLY
) && 
 122                   !(style 
& wxCB_SIMPLE
), 
 123                   _T("this style flag is ignored by wxChoice, you ") 
 124                   _T("probably want to use a wxComboBox") ); 
 126     return CreateAndInit(parent
, id
, pos
, size
, n
, choices
, style
, 
 130 bool wxChoice::CreateAndInit(wxWindow 
*parent
, 
 133                              const wxSize
& sizeOrig
, 
 134                              int n
, const wxString choices
[], 
 136                              const wxValidator
& validator
, 
 137                              const wxString
& name
) 
 139     // this is a bit hackish but we want to prevent MSWCreateControl() from 
 140     // calling SetBestSize() (which it would do if any of the size components 
 141     // is not given) because it wouldn't calculate it correctly if we have any 
 142     // strings as they're not yet added to the control when it is called 
 144     // so: if we have any strings, we fudge the size parameter so that 
 145     // SetBestSize() is not called by MSWCreateControl() but then we do call it 
 147     bool autoSize 
= false; 
 148     wxSize size 
= sizeOrig
; 
 163     // initialize wxControl 
 164     if ( !CreateControl(parent
, id
, pos
, size
, style
, validator
, name
) ) 
 167     // now create the real HWND 
 168     if ( !MSWCreateControl(wxT("COMBOBOX"), _T(""), pos
, size
) ) 
 172     // choice/combobox normally has "white" (depends on colour scheme, of 
 173     // course) background rather than inheriting the parent's background 
 174     SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW
)); 
 176     // initialize the controls contents 
 177     for ( int i 
= 0; i 
< n
; i
++ ) 
 182     // and now we may finally size the control properly (if needed) 
 185         SetBestSize(sizeOrig
); 
 191 bool wxChoice::Create(wxWindow 
*parent
, 
 195                       const wxArrayString
& choices
, 
 197                       const wxValidator
& validator
, 
 198                       const wxString
& name
) 
 200     wxCArrayString 
chs(choices
); 
 201     return Create(parent
, id
, pos
, size
, chs
.GetCount(), chs
.GetStrings(), 
 202                   style
, validator
, name
); 
 205 WXDWORD 
wxChoice::MSWGetStyle(long style
, WXDWORD 
*exstyle
) const 
 207     // we never have an external border 
 208     WXDWORD msStyle 
= wxControl::MSWGetStyle
 
 210                         (style 
& ~wxBORDER_MASK
) | wxBORDER_NONE
, exstyle
 
 213     // WS_CLIPSIBLINGS is useful with wxChoice and doesn't seem to result in 
 215     msStyle 
|= WS_CLIPSIBLINGS
; 
 217     // wxChoice-specific styles 
 218     msStyle 
|= CBS_DROPDOWNLIST 
| WS_HSCROLL 
| WS_VSCROLL
; 
 219     if ( style 
& wxCB_SORT 
) 
 225 wxChoice::~wxChoice() 
 230 // ---------------------------------------------------------------------------- 
 231 // adding/deleting items to/from the list 
 232 // ---------------------------------------------------------------------------- 
 234 int wxChoice::DoAppend(const wxString
& item
) 
 236     int n 
= (int)SendMessage(GetHwnd(), CB_ADDSTRING
, 0, (LPARAM
)item
.c_str()); 
 239         wxLogLastError(wxT("SendMessage(CB_ADDSTRING)")); 
 243         // we need to refresh our size in order to have enough space for the 
 245         UpdateVisibleHeight(); 
 251 int wxChoice::DoInsert(const wxString
& item
, int pos
) 
 253     wxCHECK_MSG(!(GetWindowStyle() & wxCB_SORT
), -1, wxT("can't insert into sorted list")); 
 254     wxCHECK_MSG((pos
>=0) && (pos
<=GetCount()), -1, wxT("invalid index")); 
 256     int n 
= (int)SendMessage(GetHwnd(), CB_INSERTSTRING
, pos
, (LPARAM
)item
.c_str()); 
 259         wxLogLastError(wxT("SendMessage(CB_INSERTSTRING)")); 
 263         UpdateVisibleHeight(); 
 269 void wxChoice::Delete(int n
) 
 271     wxCHECK_RET( n 
< GetCount(), wxT("invalid item index in wxChoice::Delete") ); 
 273     if ( HasClientObjectData() ) 
 275         delete GetClientObject(n
); 
 278     SendMessage(GetHwnd(), CB_DELETESTRING
, n
, 0); 
 280     UpdateVisibleHeight(); 
 283 void wxChoice::Clear() 
 287     SendMessage(GetHwnd(), CB_RESETCONTENT
, 0, 0); 
 289     UpdateVisibleHeight(); 
 292 void wxChoice::Free() 
 294     if ( HasClientObjectData() ) 
 296         size_t count 
= GetCount(); 
 297         for ( size_t n 
= 0; n 
< count
; n
++ ) 
 299             delete GetClientObject(n
); 
 304 // ---------------------------------------------------------------------------- 
 306 // ---------------------------------------------------------------------------- 
 308 int wxChoice::GetSelection() const 
 310     return (int)SendMessage(GetHwnd(), CB_GETCURSEL
, 0, 0); 
 313 void wxChoice::SetSelection(int n
) 
 315     SendMessage(GetHwnd(), CB_SETCURSEL
, n
, 0); 
 318 // ---------------------------------------------------------------------------- 
 319 // string list functions 
 320 // ---------------------------------------------------------------------------- 
 322 int wxChoice::GetCount() const 
 324     return (int)SendMessage(GetHwnd(), CB_GETCOUNT
, 0, 0); 
 327 int wxChoice::FindString(const wxString
& s
) const 
 329 #if defined(__WATCOMC__) && defined(__WIN386__) 
 330     // For some reason, Watcom in WIN386 mode crashes in the CB_FINDSTRINGEXACT message. 
 331     // wxChoice::Do it the long way instead. 
 332     int count 
= GetCount(); 
 333     for ( int i 
= 0; i 
< count
; i
++ ) 
 335         // as CB_FINDSTRINGEXACT is case insensitive, be case insensitive too 
 336         if ( GetString(i
).IsSameAs(s
, FALSE
) ) 
 342     int pos 
= (int)SendMessage(GetHwnd(), CB_FINDSTRINGEXACT
, 
 343                                (WPARAM
)-1, (LPARAM
)s
.c_str()); 
 345     return pos 
== LB_ERR 
? wxNOT_FOUND 
: pos
; 
 346 #endif // Watcom/!Watcom 
 349 void wxChoice::SetString(int n
, const wxString
& s
) 
 351     wxCHECK_RET( n 
>= 0 && n 
< GetCount(), 
 352                  wxT("invalid item index in wxChoice::SetString") ); 
 354     // we have to delete and add back the string as there is no way to change a 
 357     // we need to preserve the client data 
 359     if ( m_clientDataItemsType 
!= wxClientData_None 
) 
 361         data 
= DoGetItemClientData(n
); 
 363     else // no client data 
 368     ::SendMessage(GetHwnd(), CB_DELETESTRING
, n
, 0); 
 369     ::SendMessage(GetHwnd(), CB_INSERTSTRING
, n
, (LPARAM
)s
.c_str() ); 
 373         DoSetItemClientData(n
, data
); 
 375     //else: it's already NULL by default 
 378 wxString 
wxChoice::GetString(int n
) const 
 380     int len 
= (int)::SendMessage(GetHwnd(), CB_GETLBTEXTLEN
, n
, 0); 
 383     if ( len 
!= CB_ERR 
&& len 
> 0 ) 
 390                 (LPARAM
)(wxChar 
*)wxStringBuffer(str
, len
) 
 393             wxLogLastError(wxT("SendMessage(CB_GETLBTEXT)")); 
 400 // ---------------------------------------------------------------------------- 
 402 // ---------------------------------------------------------------------------- 
 404 void wxChoice::DoSetItemClientData( int n
, void* clientData 
) 
 406     if ( ::SendMessage(GetHwnd(), CB_SETITEMDATA
, 
 407                        n
, (LPARAM
)clientData
) == CB_ERR 
) 
 409         wxLogLastError(wxT("CB_SETITEMDATA")); 
 413 void* wxChoice::DoGetItemClientData( int n 
) const 
 415     LPARAM rc 
= SendMessage(GetHwnd(), CB_GETITEMDATA
, n
, 0); 
 418         wxLogLastError(wxT("CB_GETITEMDATA")); 
 420         // unfortunately, there is no way to return an error code to the user 
 427 void wxChoice::DoSetItemClientObject( int n
, wxClientData
* clientData 
) 
 429     DoSetItemClientData(n
, clientData
); 
 432 wxClientData
* wxChoice::DoGetItemClientObject( int n 
) const 
 434     return (wxClientData 
*)DoGetItemClientData(n
); 
 437 // ---------------------------------------------------------------------------- 
 438 // wxMSW specific helpers 
 439 // ---------------------------------------------------------------------------- 
 441 void wxChoice::UpdateVisibleHeight() 
 443     // be careful to not change the width here 
 444     DoSetSize(-1, -1, -1, GetSize().y
, wxSIZE_USE_EXISTING
); 
 447 void wxChoice::DoMoveWindow(int x
, int y
, int width
, int height
) 
 449     // here is why this is necessary: if the width is negative, the combobox 
 450     // window proc makes the window of the size width*height instead of 
 451     // interpreting height in the usual manner (meaning the height of the drop 
 452     // down list - usually the height specified in the call to MoveWindow() 
 453     // will not change the height of combo box per se) 
 455     // this behaviour is not documented anywhere, but this is just how it is 
 456     // here (NT 4.4) and, anyhow, the check shouldn't hurt - however without 
 457     // the check, constraints/sizers using combos may break the height 
 458     // constraint will have not at all the same value as expected 
 462     wxControl::DoMoveWindow(x
, y
, width
, height
); 
 465 void wxChoice::DoGetSize(int *w
, int *h
) const 
 467     // this is weird: sometimes, the height returned by Windows is clearly the 
 468     // total height of the control including the drop down list -- but only 
 469     // sometimes, and normally it isn't... I have no idea about what to do with 
 471     wxControl::DoGetSize(w
, h
); 
 474 void wxChoice::DoSetSize(int x
, int y
, 
 475                          int width
, int height
, 
 478     int heightOrig 
= height
; 
 480     // the height which we must pass to Windows should be the total height of 
 481     // the control including the drop down list while the height given to us 
 482     // is, of course, just the height of the permanently visible part of it 
 485         // don't make the drop down list too tall, arbitrarily limit it to 40 
 486         // items max and also don't leave it empty 
 487         size_t nItems 
= GetCount(); 
 490         else if ( nItems 
> 39 ) 
 493         // add space for the drop down list 
 494         const int hItem 
= SendMessage(GetHwnd(), CB_GETITEMHEIGHT
, 0, 0); 
 495         height 
+= hItem
*(nItems 
+ 1); 
 498     wxControl::DoSetSize(x
, y
, width
, height
, sizeFlags
); 
 500     // if the height specified for the visible part of the control is 
 501     // different from the current one, we need to change it separately 
 502     // as it is not affected by normal WM_SETSIZE 
 505         const int delta 
= heightOrig 
- GetSize().y
; 
 508             int h 
= ::SendMessage(GetHwnd(), CB_GETITEMHEIGHT
, (WPARAM
)-1, 0); 
 509             SendMessage(GetHwnd(), CB_SETITEMHEIGHT
, (WPARAM
)-1, h 
+ delta
); 
 514 wxSize 
wxChoice::DoGetBestSize() const 
 516     // find the widest string 
 518     const size_t nItems 
= GetCount(); 
 519     for ( size_t i 
= 0; i 
< nItems
; i
++ ) 
 522         GetTextExtent(GetString(i
), &wLine
, NULL
); 
 523         if ( wLine 
> wChoice 
) 
 527     // give it some reasonable default value if there are no strings in the 
 532     // the combobox should be slightly larger than the widest string 
 533     wChoice 
+= 5*GetCharWidth(); 
 535     return wxSize(wChoice
, EDIT_HEIGHT_FROM_CHAR_HEIGHT(GetCharHeight())); 
 538 WXLRESULT 
wxChoice::MSWWindowProc(WXUINT nMsg
, WXWPARAM wParam
, WXLPARAM lParam
) 
 540     if ( nMsg 
== WM_LBUTTONUP 
) 
 542         int x 
= (int)LOWORD(lParam
); 
 543         int y 
= (int)HIWORD(lParam
); 
 545         // Ok, this is truly weird, but if a panel with a wxChoice loses the 
 546         // focus, then you get a *fake* WM_LBUTTONUP message with x = 65535 and 
 547         // y = 65535. Filter out this nonsense. 
 549         // VZ: I'd like to know how to reproduce this please... 
 550         if ( x 
== 65535 && y 
== 65535 ) 
 554     return wxWindow::MSWWindowProc(nMsg
, wParam
, lParam
); 
 557 bool wxChoice::MSWCommand(WXUINT param
, WXWORD 
WXUNUSED(id
)) 
 559     if ( param 
!= CBN_SELCHANGE
) 
 561         // "selection changed" is the only event we're after 
 565     int n 
= GetSelection(); 
 568         wxCommandEvent 
event(wxEVT_COMMAND_CHOICE_SELECTED
, m_windowId
); 
 570         event
.SetEventObject(this); 
 571         event
.SetString(GetStringSelection()); 
 572         if ( HasClientObjectData() ) 
 573             event
.SetClientObject( GetClientObject(n
) ); 
 574         else if ( HasClientUntypedData() ) 
 575             event
.SetClientData( GetClientData(n
) ); 
 576         ProcessCommand(event
); 
 582 WXHBRUSH 
wxChoice::OnCtlColor(WXHDC pDC
, WXHWND 
WXUNUSED(pWnd
), WXUINT 
WXUNUSED(nCtlColor
), 
 583                                WXUINT 
WXUNUSED(message
), 
 584                                WXWPARAM 
WXUNUSED(wParam
), 
 585                                WXLPARAM 
WXUNUSED(lParam
) 
 589     wxColour colBack 
= GetBackgroundColour(); 
 592         colBack 
= wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE
); 
 594     ::SetBkColor(hdc
, wxColourToRGB(colBack
)); 
 595     ::SetTextColor(hdc
, wxColourToRGB(GetForegroundColour())); 
 597     wxBrush 
*brush 
= wxTheBrushList
->FindOrCreateBrush(colBack
, wxSOLID
); 
 599     return (WXHBRUSH
)brush
->GetResourceHandle(); 
 602 #endif // wxUSE_CHOICE