1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        msw/combobox.cpp 
   3 // Purpose:     wxComboBox class 
   4 // Author:      Julian Smart 
   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 "combobox.h" 
  24 // For compilers that support precompilation, includes "wx.h". 
  25 #include "wx/wxprec.h" 
  34     #include "wx/settings.h" 
  36     // for wxEVT_COMMAND_TEXT_ENTER 
  37     #include "wx/textctrl.h" 
  40 #include "wx/combobox.h" 
  42 #include "wx/clipbrd.h" 
  43 #include "wx/msw/private.h" 
  46     #if !defined(__GNUWIN32_OLD__) || defined(__CYGWIN10__) 
  49     #include "wx/tooltip.h" 
  50 #endif // wxUSE_TOOLTIPS 
  52 // ---------------------------------------------------------------------------- 
  54 // ---------------------------------------------------------------------------- 
  56 #if wxUSE_EXTENDED_RTTI 
  57 WX_DEFINE_FLAGS( wxComboBoxStyle 
) 
  59 wxBEGIN_FLAGS( wxComboBoxStyle 
) 
  60     // new style border flags, we put them first to 
  61     // use them for streaming out 
  62     wxFLAGS_MEMBER(wxBORDER_SIMPLE
) 
  63     wxFLAGS_MEMBER(wxBORDER_SUNKEN
) 
  64     wxFLAGS_MEMBER(wxBORDER_DOUBLE
) 
  65     wxFLAGS_MEMBER(wxBORDER_RAISED
) 
  66     wxFLAGS_MEMBER(wxBORDER_STATIC
) 
  67     wxFLAGS_MEMBER(wxBORDER_NONE
) 
  69     // old style border flags 
  70     wxFLAGS_MEMBER(wxSIMPLE_BORDER
) 
  71     wxFLAGS_MEMBER(wxSUNKEN_BORDER
) 
  72     wxFLAGS_MEMBER(wxDOUBLE_BORDER
) 
  73     wxFLAGS_MEMBER(wxRAISED_BORDER
) 
  74     wxFLAGS_MEMBER(wxSTATIC_BORDER
) 
  75     wxFLAGS_MEMBER(wxBORDER
) 
  77     // standard window styles 
  78     wxFLAGS_MEMBER(wxTAB_TRAVERSAL
) 
  79     wxFLAGS_MEMBER(wxCLIP_CHILDREN
) 
  80     wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW
) 
  81     wxFLAGS_MEMBER(wxWANTS_CHARS
) 
  82     wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE
) 
  83     wxFLAGS_MEMBER(wxALWAYS_SHOW_SB 
) 
  84     wxFLAGS_MEMBER(wxVSCROLL
) 
  85     wxFLAGS_MEMBER(wxHSCROLL
) 
  87     wxFLAGS_MEMBER(wxCB_SIMPLE
) 
  88     wxFLAGS_MEMBER(wxCB_SORT
) 
  89     wxFLAGS_MEMBER(wxCB_READONLY
) 
  90     wxFLAGS_MEMBER(wxCB_DROPDOWN
) 
  92 wxEND_FLAGS( wxComboBoxStyle 
) 
  94 IMPLEMENT_DYNAMIC_CLASS_XTI(wxComboBox
, wxControl
,"wx/combobox.h") 
  96 wxBEGIN_PROPERTIES_TABLE(wxComboBox
) 
  97         wxEVENT_PROPERTY( Select 
, wxEVT_COMMAND_COMBOBOX_SELECTED 
, wxCommandEvent 
) 
  98     wxEVENT_PROPERTY( TextEnter 
, wxEVT_COMMAND_TEXT_ENTER 
, wxCommandEvent 
) 
 101         wxPROPERTY( Font 
, wxFont 
, SetFont 
, GetFont  
, , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) 
 102     wxPROPERTY_COLLECTION( Choices 
, wxArrayString 
, wxString 
, AppendString 
, GetStrings 
, 0 /*flags*/ , wxT("Helpstring") , wxT("group")) 
 103         wxPROPERTY( Value 
,wxString
, SetValue
, GetValue
, , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) 
 104         wxPROPERTY( Selection 
,int, SetSelection
, GetSelection
, , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) 
 105     wxPROPERTY_FLAGS( WindowStyle 
, wxComboBoxStyle 
, long , SetWindowStyleFlag 
, GetWindowStyleFlag 
, , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style 
 106 wxEND_PROPERTIES_TABLE() 
 108 wxBEGIN_HANDLERS_TABLE(wxComboBox
) 
 109 wxEND_HANDLERS_TABLE() 
 111 wxCONSTRUCTOR_5( wxComboBox 
, wxWindow
* , Parent 
, wxWindowID 
, Id 
, wxString 
, Value 
, wxPoint 
, Position 
, wxSize 
, Size 
) 
 113 IMPLEMENT_DYNAMIC_CLASS(wxComboBox
, wxControl
) 
 116 // ---------------------------------------------------------------------------- 
 117 // function prototypes 
 118 // ---------------------------------------------------------------------------- 
 120 LRESULT APIENTRY _EXPORT 
wxComboEditWndProc(HWND hWnd
, 
 125 // --------------------------------------------------------------------------- 
 127 // --------------------------------------------------------------------------- 
 129 // the pointer to standard radio button wnd proc 
 130 static WXFARPROC gs_wndprocEdit 
= (WXFARPROC
)NULL
; 
 132 // ============================================================================ 
 134 // ============================================================================ 
 136 // ---------------------------------------------------------------------------- 
 137 // wnd proc for subclassed edit control 
 138 // ---------------------------------------------------------------------------- 
 140 LRESULT APIENTRY _EXPORT 
wxComboEditWndProc(HWND hWnd
, 
 145     HWND hwndCombo 
= ::GetParent(hWnd
); 
 146     wxWindow 
*win 
= wxFindWinFromHandle((WXHWND
)hwndCombo
); 
 150         // forward some messages to the combobox to generate the appropriate 
 151         // wxEvents from them 
 158                 wxComboBox 
*combo 
= wxDynamicCast(win
, wxComboBox
); 
 161                     // we can get WM_KILLFOCUS while our parent is already half 
 162                     // destroyed and hence doesn't look like a combobx any 
 163                     // longer, check for it to avoid bogus assert failures 
 164                     if ( !win
->IsBeingDeleted() ) 
 166                         wxFAIL_MSG( _T("should have combo as parent") ); 
 169                 else if ( combo
->MSWProcessEditMsg(message
, wParam
, lParam
) ) 
 179                 wxCHECK_MSG( win
, 0, _T("should have a parent") ); 
 181                 if ( win
->GetWindowStyle() & wxPROCESS_ENTER 
) 
 183                     // need to return a custom dlg code or we'll never get it 
 184                     return DLGC_WANTMESSAGE
; 
 189         // deal with tooltips here 
 190 #if wxUSE_TOOLTIPS && defined(TTN_NEEDTEXT) 
 193                 wxCHECK_MSG( win
, 0, _T("should have a parent") ); 
 195                 NMHDR
* hdr 
= (NMHDR 
*)lParam
; 
 196                 if ( hdr
->code 
== TTN_NEEDTEXT 
) 
 198                     wxToolTip 
*tooltip 
= win
->GetToolTip(); 
 201                         TOOLTIPTEXT 
*ttt 
= (TOOLTIPTEXT 
*)lParam
; 
 202                         ttt
->lpszText 
= (wxChar 
*)tooltip
->GetTip().c_str(); 
 210 #endif // wxUSE_TOOLTIPS 
 213     return ::CallWindowProc(CASTWNDPROC gs_wndprocEdit
, hWnd
, message
, wParam
, lParam
); 
 216 WXHBRUSH 
wxComboBox::OnCtlColor(WXHDC pDC
, WXHWND 
WXUNUSED(pWnd
), WXUINT 
WXUNUSED(nCtlColor
), 
 217                                WXUINT 
WXUNUSED(message
), 
 218                                WXWPARAM 
WXUNUSED(wParam
), 
 219                                WXLPARAM 
WXUNUSED(lParam
) 
 223     wxColour colBack 
= GetBackgroundColour(); 
 226         colBack 
= wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE
); 
 228     ::SetBkColor(hdc
, wxColourToRGB(colBack
)); 
 229     ::SetTextColor(hdc
, wxColourToRGB(GetForegroundColour())); 
 231     wxBrush 
*brush 
= wxTheBrushList
->FindOrCreateBrush(colBack
, wxSOLID
); 
 233     return (WXHBRUSH
)brush
->GetResourceHandle(); 
 236 // ---------------------------------------------------------------------------- 
 238 // ---------------------------------------------------------------------------- 
 240 bool wxComboBox::MSWProcessEditMsg(WXUINT msg
, WXWPARAM wParam
, WXLPARAM lParam
) 
 245             // for compatibility with wxTextCtrl, generate a special message 
 246             // when Enter is pressed 
 247             if ( wParam 
== VK_RETURN 
) 
 249                 wxCommandEvent 
event(wxEVT_COMMAND_TEXT_ENTER
, m_windowId
); 
 250                 InitCommandEvent(event
); 
 251                 event
.SetString(GetValue()); 
 252                 event
.SetInt(GetSelection()); 
 253                 ProcessCommand(event
); 
 256             return HandleChar(wParam
, lParam
, TRUE 
/* isASCII */); 
 259             return HandleKeyDown(wParam
, lParam
); 
 262             return HandleKeyUp(wParam
, lParam
); 
 265             return HandleSetFocus((WXHWND
)wParam
); 
 268             return HandleKillFocus((WXHWND
)wParam
); 
 274 bool wxComboBox::MSWCommand(WXUINT param
, WXWORD 
WXUNUSED(id
)) 
 281             sel 
= GetSelection(); 
 284                 value 
= GetString(sel
); 
 286                 wxCommandEvent 
event(wxEVT_COMMAND_COMBOBOX_SELECTED
, GetId()); 
 288                 event
.SetEventObject(this); 
 289                 event
.SetString(value
); 
 290                 ProcessCommand(event
); 
 297             // fall through: for compability with wxGTK, also send the text 
 298             // update event when the selection changes (this also seems more 
 299             // logical as the text does change) 
 303                 wxCommandEvent 
event(wxEVT_COMMAND_TEXT_UPDATED
, GetId()); 
 305                 // if sel != -1, value was initialized above (and we can't use 
 306                 // GetValue() here as it would return the old selection and we 
 312                 else // we're synthesizing text updated event from sel change 
 314                     // We need to retrieve the current selection because the user 
 315                     // may have changed it in the previous handler (for CBN_SELCHANGE 
 317                     sel 
= GetSelection(); 
 320                         value 
= GetString(sel
); 
 322                     // we need to do this because the user code expects 
 323                     // wxComboBox::GetValue() to return the new value from 
 324                     // "text updated" handler but it hadn't been updated yet 
 328                 event
.SetString(value
); 
 329                 event
.SetEventObject(this); 
 330                 ProcessCommand(event
); 
 335     // there is no return value for the CBN_ notifications, so always return 
 336     // FALSE from here to pass the message to DefWindowProc() 
 340 WXHWND 
wxComboBox::GetEditHWND() const 
 342     // this function should not be called for wxCB_READONLY controls, it is 
 343     // the callers responsability to check this 
 344     wxASSERT_MSG( !(GetWindowStyle() & wxCB_READONLY
), 
 345                   _T("read-only combobox doesn't have any edit control") ); 
 349     HWND hwndEdit 
= ::ChildWindowFromPoint(GetHwnd(), pt
); 
 350     if ( !hwndEdit 
|| hwndEdit 
== GetHwnd() ) 
 352         wxFAIL_MSG(_T("not read only combobox without edit control?")); 
 355     return (WXHWND
)hwndEdit
; 
 358 // ---------------------------------------------------------------------------- 
 359 // wxComboBox creation 
 360 // ---------------------------------------------------------------------------- 
 362 bool wxComboBox::Create(wxWindow 
*parent
, wxWindowID id
, 
 363                         const wxString
& value
, 
 366                         int n
, const wxString choices
[], 
 368                         const wxValidator
& validator
, 
 369                         const wxString
& name
) 
 371     // pretend that wxComboBox is hidden while it is positioned and resized and 
 372     // show it only right before leaving this method because otherwise there is 
 373     // some noticeable flicker while the control rearranges itself 
 376     if ( !CreateAndInit(parent
, id
, pos
, size
, n
, choices
, style
, 
 382     // a (not read only) combobox is, in fact, 2 controls: the combobox itself 
 383     // and an edit control inside it and if we want to catch events from this 
 384     // edit control, we must subclass it as well 
 385     if ( !(style 
& wxCB_READONLY
) ) 
 387         gs_wndprocEdit 
= (WXFARPROC
)::SetWindowLong
 
 391                                         (LPARAM
)wxComboEditWndProc
 
 395     // and finally, show the control 
 401 bool wxComboBox::Create(wxWindow 
*parent
, wxWindowID id
, 
 402                         const wxString
& value
, 
 405                         const wxArrayString
& choices
, 
 407                         const wxValidator
& validator
, 
 408                         const wxString
& name
) 
 410     wxCArrayString 
chs(choices
); 
 411     return Create(parent
, id
, value
, pos
, size
, chs
.GetCount(), 
 412                   chs
.GetStrings(), style
, validator
, name
); 
 415 WXDWORD 
wxComboBox::MSWGetStyle(long style
, WXDWORD 
*exstyle
) const 
 417     // we never have an external border 
 418     WXDWORD msStyle 
= wxChoice::MSWGetStyle
 
 420                         (style 
& ~wxBORDER_MASK
) | wxBORDER_NONE
, exstyle
 
 423     // remove the style always added by wxChoice 
 424     msStyle 
&= ~CBS_DROPDOWNLIST
; 
 426     if ( style 
& wxCB_READONLY 
) 
 427         msStyle 
|= CBS_DROPDOWNLIST
; 
 429     else if ( style 
& wxCB_SIMPLE 
) 
 430         msStyle 
|= CBS_SIMPLE
; // A list (shown always) and edit control 
 433         msStyle 
|= CBS_DROPDOWN
; 
 435     // there is no reason to not always use CBS_AUTOHSCROLL, so do use it 
 436     msStyle 
|= CBS_AUTOHSCROLL
; 
 438     // NB: we used to also add CBS_NOINTEGRALHEIGHT here but why? 
 443 // ---------------------------------------------------------------------------- 
 444 // wxComboBox text control-like methods 
 445 // ---------------------------------------------------------------------------- 
 447 void wxComboBox::SetValue(const wxString
& value
) 
 449     if ( HasFlag(wxCB_READONLY
) ) 
 450         SetStringSelection(value
); 
 452         SetWindowText(GetHwnd(), value
.c_str()); 
 455 // Clipboard operations 
 456 void wxComboBox::Copy() 
 458   SendMessage(GetHwnd(), WM_COPY
, 0, 0L); 
 461 void wxComboBox::Cut() 
 463   SendMessage(GetHwnd(), WM_CUT
, 0, 0L); 
 466 void wxComboBox::Paste() 
 468   SendMessage(GetHwnd(), WM_PASTE
, 0, 0L); 
 471 void wxComboBox::SetEditable(bool WXUNUSED(editable
)) 
 473   // Can't implement in MSW? 
 474 //  HWND hWnd = GetHwnd(); 
 475 //  SendMessage(hWnd, EM_SETREADONLY, (WPARAM)!editable, (LPARAM)0L); 
 478 void wxComboBox::SetInsertionPoint(long pos
) 
 480     if ( GetWindowStyle() & wxCB_READONLY 
) 
 484     HWND hWnd 
= GetHwnd(); 
 485     ::SendMessage(hWnd
, CB_SETEDITSEL
, 0, MAKELPARAM(pos
, pos
)); 
 486     HWND hEditWnd 
= (HWND
) GetEditHWND() ; 
 489         // Scroll insertion point into view 
 490         SendMessage(hEditWnd
, EM_SCROLLCARET
, (WPARAM
)0, (LPARAM
)0); 
 491         // Why is this necessary? (Copied from wxTextCtrl::SetInsertionPoint) 
 492         SendMessage(hEditWnd
, EM_REPLACESEL
, 0, (LPARAM
) wxEmptyString
); 
 497 void wxComboBox::SetInsertionPointEnd() 
 499     // setting insertion point doesn't make sense for read only comboboxes 
 500     if ( !(GetWindowStyle() & wxCB_READONLY
) ) 
 502         long pos 
= GetLastPosition(); 
 503         SetInsertionPoint(pos
); 
 507 long wxComboBox::GetInsertionPoint() const 
 510     DWORD Pos
=(DWORD
)SendMessage(GetHwnd(), CB_GETEDITSEL
, 0, 0L); 
 517 long wxComboBox::GetLastPosition() const 
 519     HWND hEditWnd 
= (HWND
) GetEditHWND(); 
 521     // Get number of characters in the last (only) line. We'll add this to the character 
 522     // index for the last line, 1st position. 
 523     int lineLength 
= (int)SendMessage(hEditWnd
, EM_LINELENGTH
, (WPARAM
) 0, (LPARAM
)0L); 
 525     return (long)(lineLength
); 
 528 void wxComboBox::Replace(long from
, long to
, const wxString
& value
) 
 533     // Now replace with 'value', by pasting. 
 534     wxSetClipboardData(wxDF_TEXT
, (wxObject 
*)(const wxChar 
*)value
, 0, 0); 
 536     // Paste into edit control 
 537     SendMessage(GetHwnd(), WM_PASTE
, (WPARAM
)0, (LPARAM
)0L); 
 541 void wxComboBox::Remove(long from
, long to
) 
 543     // Set selection and remove it 
 544     SetSelection(from
, to
); 
 545     SendMessage(GetHwnd(), WM_CUT
, (WPARAM
)0, (LPARAM
)0); 
 548 void wxComboBox::SetSelection(long from
, long to
) 
 550     HWND hWnd 
= GetHwnd(); 
 551     long fromChar 
= from
; 
 553     // if from and to are both -1, it means 
 554     // (in wxWindows) that all text should be selected. 
 555     // This translates into Windows convention 
 556     if ((from 
== -1) && (to 
== -1)) 
 564     SendMessage(hWnd
, CB_SETEDITSEL
, (WPARAM
)0, (LPARAM
)MAKELONG(fromChar
, toChar
)) 
 566     SendMessage(hWnd
, CB_SETEDITSEL
, (WPARAM
)fromChar
, (LPARAM
)toChar
) 
 570         wxLogDebug(_T("CB_SETEDITSEL failed")); 
 574 #endif // wxUSE_COMBOBOX