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
, 
 217                                 WXHWND 
WXUNUSED(pWnd
), 
 218                                 WXUINT 
WXUNUSED(nCtlColor
), 
 219                                 WXUINT 
WXUNUSED(message
), 
 220                                 WXWPARAM 
WXUNUSED(wParam
), 
 221                                 WXLPARAM 
WXUNUSED(lParam
)) 
 224     wxColour colBack 
= GetBackgroundColour(); 
 227         colBack 
= wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE
); 
 229     ::SetBkColor(hdc
, wxColourToRGB(colBack
)); 
 230     ::SetTextColor(hdc
, wxColourToRGB(GetForegroundColour())); 
 232     wxBrush 
*brush 
= wxTheBrushList
->FindOrCreateBrush(colBack
, wxSOLID
); 
 234     return (WXHBRUSH
)brush
->GetResourceHandle(); 
 237 // ---------------------------------------------------------------------------- 
 238 // wxComboBox callbacks 
 239 // ---------------------------------------------------------------------------- 
 241 long wxComboBox::MSWWindowProc(WXUINT nMsg
, WXWPARAM wParam
, WXLPARAM lParam
) 
 243     // handle WM_CTLCOLOR messages from our EDIT control to be able to set its 
 244     // colour correctly (to be the same as our own one) 
 247         // we have to handle both: one for the normal case and the other for 
 249         case WM_CTLCOLOREDIT
: 
 250         case WM_CTLCOLORSTATIC
: 
 254             UnpackCtlColor(wParam
, lParam
, &nCtlColor
, &hdc
, &hwnd
); 
 256             return OnCtlColor(hdc
, hwnd
, nCtlColor
, nMsg
, wParam
, lParam
); 
 259     return wxChoice::MSWWindowProc(nMsg
, wParam
, lParam
); 
 262 bool wxComboBox::MSWProcessEditMsg(WXUINT msg
, WXWPARAM wParam
, WXLPARAM lParam
) 
 267             // for compatibility with wxTextCtrl, generate a special message 
 268             // when Enter is pressed 
 269             if ( wParam 
== VK_RETURN 
) 
 271                 wxCommandEvent 
event(wxEVT_COMMAND_TEXT_ENTER
, m_windowId
); 
 272                 InitCommandEvent(event
); 
 273                 event
.SetString(GetValue()); 
 274                 event
.SetInt(GetSelection()); 
 275                 ProcessCommand(event
); 
 278             return HandleChar(wParam
, lParam
, TRUE 
/* isASCII */); 
 281             return HandleKeyDown(wParam
, lParam
); 
 284             return HandleKeyUp(wParam
, lParam
); 
 287             return HandleSetFocus((WXHWND
)wParam
); 
 290             return HandleKillFocus((WXHWND
)wParam
); 
 296 bool wxComboBox::MSWCommand(WXUINT param
, WXWORD 
WXUNUSED(id
)) 
 303             sel 
= GetSelection(); 
 306                 value 
= GetString(sel
); 
 308                 wxCommandEvent 
event(wxEVT_COMMAND_COMBOBOX_SELECTED
, GetId()); 
 310                 event
.SetEventObject(this); 
 311                 event
.SetString(value
); 
 312                 ProcessCommand(event
); 
 319             // fall through: for compability with wxGTK, also send the text 
 320             // update event when the selection changes (this also seems more 
 321             // logical as the text does change) 
 325                 wxCommandEvent 
event(wxEVT_COMMAND_TEXT_UPDATED
, GetId()); 
 327                 // if sel != -1, value was initialized above (and we can't use 
 328                 // GetValue() here as it would return the old selection and we 
 334                 else // we're synthesizing text updated event from sel change 
 336                     // We need to retrieve the current selection because the user 
 337                     // may have changed it in the previous handler (for CBN_SELCHANGE 
 339                     sel 
= GetSelection(); 
 342                         value 
= GetString(sel
); 
 344                     // we need to do this because the user code expects 
 345                     // wxComboBox::GetValue() to return the new value from 
 346                     // "text updated" handler but it hadn't been updated yet 
 350                 event
.SetString(value
); 
 351                 event
.SetEventObject(this); 
 352                 ProcessCommand(event
); 
 357     // there is no return value for the CBN_ notifications, so always return 
 358     // FALSE from here to pass the message to DefWindowProc() 
 362 WXHWND 
wxComboBox::GetEditHWND() const 
 364     // this function should not be called for wxCB_READONLY controls, it is 
 365     // the callers responsability to check this 
 366     wxASSERT_MSG( !(GetWindowStyle() & wxCB_READONLY
), 
 367                   _T("read-only combobox doesn't have any edit control") ); 
 371     HWND hwndEdit 
= ::ChildWindowFromPoint(GetHwnd(), pt
); 
 372     if ( !hwndEdit 
|| hwndEdit 
== GetHwnd() ) 
 374         wxFAIL_MSG(_T("not read only combobox without edit control?")); 
 377     return (WXHWND
)hwndEdit
; 
 380 // ---------------------------------------------------------------------------- 
 381 // wxComboBox creation 
 382 // ---------------------------------------------------------------------------- 
 384 bool wxComboBox::Create(wxWindow 
*parent
, wxWindowID id
, 
 385                         const wxString
& value
, 
 388                         int n
, const wxString choices
[], 
 390                         const wxValidator
& validator
, 
 391                         const wxString
& name
) 
 393     // pretend that wxComboBox is hidden while it is positioned and resized and 
 394     // show it only right before leaving this method because otherwise there is 
 395     // some noticeable flicker while the control rearranges itself 
 398     if ( !CreateAndInit(parent
, id
, pos
, size
, n
, choices
, style
, 
 404     // a (not read only) combobox is, in fact, 2 controls: the combobox itself 
 405     // and an edit control inside it and if we want to catch events from this 
 406     // edit control, we must subclass it as well 
 407     if ( !(style 
& wxCB_READONLY
) ) 
 409         gs_wndprocEdit 
= (WXFARPROC
)::SetWindowLong
 
 413                                         (LPARAM
)wxComboEditWndProc
 
 417     // and finally, show the control 
 423 bool wxComboBox::Create(wxWindow 
*parent
, wxWindowID id
, 
 424                         const wxString
& value
, 
 427                         const wxArrayString
& choices
, 
 429                         const wxValidator
& validator
, 
 430                         const wxString
& name
) 
 432     wxCArrayString 
chs(choices
); 
 433     return Create(parent
, id
, value
, pos
, size
, chs
.GetCount(), 
 434                   chs
.GetStrings(), style
, validator
, name
); 
 437 WXDWORD 
wxComboBox::MSWGetStyle(long style
, WXDWORD 
*exstyle
) const 
 439     // we never have an external border 
 440     WXDWORD msStyle 
= wxChoice::MSWGetStyle
 
 442                         (style 
& ~wxBORDER_MASK
) | wxBORDER_NONE
, exstyle
 
 445     // remove the style always added by wxChoice 
 446     msStyle 
&= ~CBS_DROPDOWNLIST
; 
 448     if ( style 
& wxCB_READONLY 
) 
 449         msStyle 
|= CBS_DROPDOWNLIST
; 
 451     else if ( style 
& wxCB_SIMPLE 
) 
 452         msStyle 
|= CBS_SIMPLE
; // A list (shown always) and edit control 
 455         msStyle 
|= CBS_DROPDOWN
; 
 457     // there is no reason to not always use CBS_AUTOHSCROLL, so do use it 
 458     msStyle 
|= CBS_AUTOHSCROLL
; 
 460     // NB: we used to also add CBS_NOINTEGRALHEIGHT here but why? 
 465 // ---------------------------------------------------------------------------- 
 466 // wxComboBox text control-like methods 
 467 // ---------------------------------------------------------------------------- 
 469 void wxComboBox::SetValue(const wxString
& value
) 
 471     if ( HasFlag(wxCB_READONLY
) ) 
 472         SetStringSelection(value
); 
 474         SetWindowText(GetHwnd(), value
.c_str()); 
 477 // Clipboard operations 
 478 void wxComboBox::Copy() 
 480   SendMessage(GetHwnd(), WM_COPY
, 0, 0L); 
 483 void wxComboBox::Cut() 
 485   SendMessage(GetHwnd(), WM_CUT
, 0, 0L); 
 488 void wxComboBox::Paste() 
 490   SendMessage(GetHwnd(), WM_PASTE
, 0, 0L); 
 493 void wxComboBox::SetEditable(bool WXUNUSED(editable
)) 
 495   // Can't implement in MSW? 
 496 //  HWND hWnd = GetHwnd(); 
 497 //  SendMessage(hWnd, EM_SETREADONLY, (WPARAM)!editable, (LPARAM)0L); 
 500 void wxComboBox::SetInsertionPoint(long pos
) 
 502     if ( GetWindowStyle() & wxCB_READONLY 
) 
 506     HWND hWnd 
= GetHwnd(); 
 507     ::SendMessage(hWnd
, CB_SETEDITSEL
, 0, MAKELPARAM(pos
, pos
)); 
 508     HWND hEditWnd 
= (HWND
) GetEditHWND() ; 
 511         // Scroll insertion point into view 
 512         SendMessage(hEditWnd
, EM_SCROLLCARET
, (WPARAM
)0, (LPARAM
)0); 
 513         // Why is this necessary? (Copied from wxTextCtrl::SetInsertionPoint) 
 514         SendMessage(hEditWnd
, EM_REPLACESEL
, 0, (LPARAM
) wxEmptyString
); 
 519 void wxComboBox::SetInsertionPointEnd() 
 521     // setting insertion point doesn't make sense for read only comboboxes 
 522     if ( !(GetWindowStyle() & wxCB_READONLY
) ) 
 524         long pos 
= GetLastPosition(); 
 525         SetInsertionPoint(pos
); 
 529 long wxComboBox::GetInsertionPoint() const 
 532     DWORD Pos
=(DWORD
)SendMessage(GetHwnd(), CB_GETEDITSEL
, 0, 0L); 
 539 long wxComboBox::GetLastPosition() const 
 541     HWND hEditWnd 
= (HWND
) GetEditHWND(); 
 543     // Get number of characters in the last (only) line. We'll add this to the character 
 544     // index for the last line, 1st position. 
 545     int lineLength 
= (int)SendMessage(hEditWnd
, EM_LINELENGTH
, (WPARAM
) 0, (LPARAM
)0L); 
 547     return (long)(lineLength
); 
 550 void wxComboBox::Replace(long from
, long to
, const wxString
& value
) 
 555     // Now replace with 'value', by pasting. 
 556     wxSetClipboardData(wxDF_TEXT
, (wxObject 
*)(const wxChar 
*)value
, 0, 0); 
 558     // Paste into edit control 
 559     SendMessage(GetHwnd(), WM_PASTE
, (WPARAM
)0, (LPARAM
)0L); 
 563 void wxComboBox::Remove(long from
, long to
) 
 565     // Set selection and remove it 
 566     SetSelection(from
, to
); 
 567     SendMessage(GetHwnd(), WM_CUT
, (WPARAM
)0, (LPARAM
)0); 
 570 void wxComboBox::SetSelection(long from
, long to
) 
 572     HWND hWnd 
= GetHwnd(); 
 573     long fromChar 
= from
; 
 575     // if from and to are both -1, it means 
 576     // (in wxWindows) that all text should be selected. 
 577     // This translates into Windows convention 
 578     if ((from 
== -1) && (to 
== -1)) 
 586     SendMessage(hWnd
, CB_SETEDITSEL
, (WPARAM
)0, (LPARAM
)MAKELONG(fromChar
, toChar
)) 
 588     SendMessage(hWnd
, CB_SETEDITSEL
, (WPARAM
)fromChar
, (LPARAM
)toChar
) 
 592         wxLogDebug(_T("CB_SETEDITSEL failed")); 
 596 #endif // wxUSE_COMBOBOX