1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        msw/combobox.cpp 
   3 // Purpose:     wxComboBox class 
   4 // Author:      Julian Smart 
   8 // Copyright:   (c) Julian Smart and Markus Holzem 
   9 // Licence:     wxWindows licence 
  10 ///////////////////////////////////////////////////////////////////////////// 
  12 // ============================================================================ 
  14 // ============================================================================ 
  16 // ---------------------------------------------------------------------------- 
  18 // ---------------------------------------------------------------------------- 
  21 #pragma implementation "combobox.h" 
  24 // For compilers that support precompilation, includes "wx.h". 
  25 #include "wx/wxprec.h" 
  34     #include "wx/settings.h" 
  38 #include "wx/combobox.h" 
  40 #include "wx/clipbrd.h" 
  41 #include "wx/msw/private.h" 
  44     #if !defined(__GNUWIN32_OLD__) || defined(__CYGWIN10__) 
  47     #include "wx/tooltip.h" 
  48 #endif // wxUSE_TOOLTIPS 
  50 // ---------------------------------------------------------------------------- 
  52 // ---------------------------------------------------------------------------- 
  54 IMPLEMENT_DYNAMIC_CLASS(wxComboBox
, wxControl
) 
  56 // ---------------------------------------------------------------------------- 
  57 // function prototypes 
  58 // ---------------------------------------------------------------------------- 
  60 LRESULT APIENTRY _EXPORT 
wxComboEditWndProc(HWND hWnd
, 
  65 // --------------------------------------------------------------------------- 
  67 // --------------------------------------------------------------------------- 
  69 // the pointer to standard radio button wnd proc 
  70 static WXFARPROC gs_wndprocEdit 
= (WXFARPROC
)NULL
; 
  72 // ============================================================================ 
  74 // ============================================================================ 
  76 // ---------------------------------------------------------------------------- 
  77 // wnd proc for subclassed edit control 
  78 // ---------------------------------------------------------------------------- 
  80 LRESULT APIENTRY _EXPORT 
wxComboEditWndProc(HWND hWnd
, 
  85     HWND hwndCombo 
= ::GetParent(hWnd
); 
  86     wxWindow 
*win 
= wxFindWinFromHandle((WXHWND
)hwndCombo
); 
  90         // forward some messages to the combobox to generate the appropriate 
  98                 wxComboBox 
*combo 
= wxDynamicCast(win
, wxComboBox
); 
 101                     // we can get WM_KILLFOCUS while our parent is already half 
 102                     // destroyed and hence doesn't look like a combobx any 
 103                     // longer, check for it to avoid bogus assert failures 
 104                     if ( !win
->IsBeingDeleted() ) 
 106                         wxFAIL_MSG( _T("should have combo as parent") ); 
 109                 else if ( combo
->MSWProcessEditMsg(message
, wParam
, lParam
) ) 
 119                 wxCHECK_MSG( win
, 0, _T("should have a parent") ); 
 121                 if ( win
->GetWindowStyle() & wxPROCESS_ENTER 
) 
 123                     // need to return a custom dlg code or we'll never get it 
 124                     return DLGC_WANTMESSAGE
; 
 129         // deal with tooltips here 
 130 #if wxUSE_TOOLTIPS && defined(TTN_NEEDTEXT) 
 133                 wxCHECK_MSG( win
, 0, _T("should have a parent") ); 
 135                 NMHDR
* hdr 
= (NMHDR 
*)lParam
; 
 136                 if ( (int)hdr
->code 
== TTN_NEEDTEXT 
) 
 138                     wxToolTip 
*tooltip 
= win
->GetToolTip(); 
 141                         TOOLTIPTEXT 
*ttt 
= (TOOLTIPTEXT 
*)lParam
; 
 142                         ttt
->lpszText 
= (wxChar 
*)tooltip
->GetTip().c_str(); 
 150 #endif // wxUSE_TOOLTIPS 
 153     return ::CallWindowProc(CASTWNDPROC gs_wndprocEdit
, hWnd
, message
, wParam
, lParam
); 
 156 WXHBRUSH 
wxComboBox::OnCtlColor(WXHDC pDC
, WXHWND 
WXUNUSED(pWnd
), WXUINT 
WXUNUSED(nCtlColor
), 
 162                                WXUINT 
WXUNUSED(message
), 
 163                                WXWPARAM 
WXUNUSED(wParam
), 
 164                                WXLPARAM 
WXUNUSED(lParam
) 
 171         HBRUSH hbrush 
= Ctl3dCtlColorEx(message
, wParam
, lParam
); 
 172         return (WXHBRUSH
) hbrush
; 
 174 #endif // wxUSE_CTL3D 
 177     if (GetParent()->GetTransparentBackground()) 
 178         SetBkMode(hdc
, TRANSPARENT
); 
 180         SetBkMode(hdc
, OPAQUE
); 
 182     wxColour colBack 
= GetBackgroundColour(); 
 185         colBack 
= wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE
); 
 187     ::SetBkColor(hdc
, wxColourToRGB(colBack
)); 
 188     ::SetTextColor(hdc
, wxColourToRGB(GetForegroundColour())); 
 190     wxBrush 
*brush 
= wxTheBrushList
->FindOrCreateBrush(colBack
, wxSOLID
); 
 192     return (WXHBRUSH
)brush
->GetResourceHandle(); 
 195 // ---------------------------------------------------------------------------- 
 197 // ---------------------------------------------------------------------------- 
 199 bool wxComboBox::MSWProcessEditMsg(WXUINT msg
, WXWPARAM wParam
, WXLPARAM lParam
) 
 204             return HandleChar(wParam
, lParam
, TRUE 
/* isASCII */); 
 207             return HandleKeyDown(wParam
, lParam
); 
 210             return HandleKeyUp(wParam
, lParam
); 
 213             return HandleSetFocus((WXHWND
)wParam
); 
 216             return HandleKillFocus((WXHWND
)wParam
); 
 222 bool wxComboBox::MSWCommand(WXUINT param
, WXWORD 
WXUNUSED(id
)) 
 229             sel 
= GetSelection(); 
 232                 value 
= GetString(sel
); 
 234                 wxCommandEvent 
event(wxEVT_COMMAND_COMBOBOX_SELECTED
, GetId()); 
 236                 event
.SetEventObject(this); 
 237                 event
.SetString(value
); 
 238                 ProcessCommand(event
); 
 245             // fall through: for compability with wxGTK, also send the text 
 246             // update event when the selection changes (this also seems more 
 247             // logical as the text does change) 
 251                 wxCommandEvent 
event(wxEVT_COMMAND_TEXT_UPDATED
, GetId()); 
 253                 // if sel != -1, value was initialized above (and we can't use 
 254                 // GetValue() here as it would return the old selection and we 
 260                 else // we're synthesizing text updated event from sel change 
 262                     // we need to do this because the user code expects 
 263                     // wxComboBox::GetValue() to return the new value from 
 264                     // "text updated" handler but it hadn't been updated yet 
 268                 event
.SetString(value
); 
 269                 event
.SetEventObject(this); 
 270                 ProcessCommand(event
); 
 275     // there is no return value for the CBN_ notifications, so always return 
 276     // FALSE from here to pass the message to DefWindowProc() 
 280 WXHWND 
wxComboBox::GetEditHWND() const 
 282     // this function should not be called for wxCB_READONLY controls, it is 
 283     // the callers responsability to check this 
 284     wxASSERT_MSG( !(GetWindowStyle() & wxCB_READONLY
), 
 285                   _T("read-only combobox doesn't have any edit control") ); 
 289     HWND hwndEdit 
= ::ChildWindowFromPoint(GetHwnd(), pt
); 
 290     if ( !hwndEdit 
|| hwndEdit 
== GetHwnd() ) 
 292         wxFAIL_MSG(_T("not read only combobox without edit control?")); 
 295     return (WXHWND
)hwndEdit
; 
 298 bool wxComboBox::Create(wxWindow 
*parent
, wxWindowID id
, 
 299                         const wxString
& value
, 
 302                         int n
, const wxString choices
[], 
 304                         const wxValidator
& validator
, 
 305                         const wxString
& name
) 
 307     // first create wxWin object 
 308     if ( !CreateControl(parent
, id
, pos
, size
, style
, validator
, name
) ) 
 311     // get the right style 
 312     long msStyle 
= WS_TABSTOP 
| WS_VSCROLL 
| WS_HSCROLL 
| 
 313                    CBS_AUTOHSCROLL 
| CBS_NOINTEGRALHEIGHT 
/* | WS_CLIPSIBLINGS */; 
 314     if ( style 
& wxCB_READONLY 
) 
 315         msStyle 
|= CBS_DROPDOWNLIST
; 
 316     else if ( style 
& wxCB_SIMPLE 
) 
 317         msStyle 
|= CBS_SIMPLE
; // A list (shown always) and edit control 
 319         msStyle 
|= CBS_DROPDOWN
; 
 321     if ( style 
& wxCB_SORT 
) 
 324     if ( style 
& wxCLIP_SIBLINGS 
) 
 325         msStyle 
|= WS_CLIPSIBLINGS
; 
 328     // and now create the MSW control 
 329     if ( !MSWCreateControl(_T("COMBOBOX"), msStyle
, pos
, size
, wxEmptyString
, (WXDWORD
)-1, false) ) 
 332     // A choice/combobox normally has a white background (or other, depending 
 333     // on global settings) rather than inheriting the parent's background colour. 
 334     SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW
)); 
 336     for ( int i 
= 0; i 
< n
; i
++ ) 
 341     if ( !value
.IsEmpty() ) 
 346     // do this after appending the values to the combobox so that autosizing 
 348     SetSize(pos
.x
, pos
.y
, size
.x
, size
.y
); 
 350     // a (not read only) combobox is, in fact, 2 controls: the combobox itself 
 351     // and an edit control inside it and if we want to catch events from this 
 352     // edit control, we must subclass it as well 
 353     if ( !(style 
& wxCB_READONLY
) ) 
 355         gs_wndprocEdit 
= (WXFARPROC
)::SetWindowLong
 
 359                                         (LPARAM
)wxComboEditWndProc
 
 363     // finally, show the combo box 
 369 // TODO: update and clear all this horrible mess (VZ) 
 371 void wxComboBox::SetValue(const wxString
& value
) 
 373   // If newlines are denoted by just 10, must stick 13 in front. 
 375   int len 
= value
.Length(); 
 377   for (i 
= 0; i 
< len
; i 
++) 
 379     if ((i 
> 0) && (value
[i
] == 10) && (value
[i
-1] != 13)) 
 384     wxChar 
*tmp 
= new wxChar
[len 
+ singletons 
+ 1]; 
 386     for (i 
= 0; i 
< len
; i 
++) 
 388       if ((i 
> 0) && (value
[i
] == 10) && (value
[i
-1] != 13)) 
 397     SetWindowText(GetHwnd(), tmp
); 
 401     SetWindowText(GetHwnd(), value
); 
 404 // Clipboard operations 
 405 void wxComboBox::Copy() 
 407   HWND hWnd 
= GetHwnd(); 
 408   SendMessage(hWnd
, WM_COPY
, 0, 0L); 
 411 void wxComboBox::Cut() 
 413   HWND hWnd 
= GetHwnd(); 
 414   SendMessage(hWnd
, WM_CUT
, 0, 0L); 
 417 void wxComboBox::Paste() 
 419   HWND hWnd 
= GetHwnd(); 
 420   SendMessage(hWnd
, WM_PASTE
, 0, 0L); 
 423 void wxComboBox::SetEditable(bool WXUNUSED(editable
)) 
 425   // Can't implement in MSW? 
 426 //  HWND hWnd = GetHwnd(); 
 427 //  SendMessage(hWnd, EM_SETREADONLY, (WPARAM)!editable, (LPARAM)0L); 
 430 void wxComboBox::SetInsertionPoint(long pos
) 
 432     if ( GetWindowStyle() & wxCB_READONLY 
) 
 436     HWND hWnd 
= GetHwnd(); 
 437     ::SendMessage(hWnd
, CB_SETEDITSEL
, 0, MAKELPARAM(pos
, pos
)); 
 438     HWND hEditWnd 
= (HWND
) GetEditHWND() ; 
 441         // Scroll insertion point into view 
 442         SendMessage(hEditWnd
, EM_SCROLLCARET
, (WPARAM
)0, (LPARAM
)0); 
 443         // Why is this necessary? (Copied from wxTextCtrl::SetInsertionPoint) 
 444         SendMessage(hEditWnd
, EM_REPLACESEL
, 0, (LPARAM
)_T("")); 
 449 void wxComboBox::SetInsertionPointEnd() 
 451     // setting insertion point doesn't make sense for read only comboboxes 
 452     if ( !(GetWindowStyle() & wxCB_READONLY
) ) 
 454         long pos 
= GetLastPosition(); 
 455         SetInsertionPoint(pos
); 
 459 long wxComboBox::GetInsertionPoint() const 
 462     DWORD Pos
=(DWORD
)SendMessage(GetHwnd(), CB_GETEDITSEL
, 0, 0L); 
 469 long wxComboBox::GetLastPosition() const 
 471     HWND hEditWnd 
= (HWND
) GetEditHWND(); 
 473     // Get number of characters in the last (only) line. We'll add this to the character 
 474     // index for the last line, 1st position. 
 475     int lineLength 
= (int)SendMessage(hEditWnd
, EM_LINELENGTH
, (WPARAM
) 0, (LPARAM
)0L); 
 477     return (long)(lineLength
); 
 480 void wxComboBox::Replace(long from
, long to
, const wxString
& value
) 
 485     // Now replace with 'value', by pasting. 
 486     wxSetClipboardData(wxDF_TEXT
, (wxObject 
*)(const wxChar 
*)value
, 0, 0); 
 488     // Paste into edit control 
 489     SendMessage(GetHwnd(), WM_PASTE
, (WPARAM
)0, (LPARAM
)0L); 
 493 void wxComboBox::Remove(long from
, long to
) 
 495     // Set selection and remove it 
 496     SetSelection(from
, to
); 
 497     SendMessage(GetHwnd(), WM_CUT
, (WPARAM
)0, (LPARAM
)0); 
 500 void wxComboBox::SetSelection(long from
, long to
) 
 502     HWND hWnd 
= GetHwnd(); 
 503     long fromChar 
= from
; 
 505     // if from and to are both -1, it means 
 506     // (in wxWindows) that all text should be selected. 
 507     // This translates into Windows convention 
 508     if ((from 
== -1) && (to 
== -1)) 
 516     SendMessage(hWnd
, CB_SETEDITSEL
, (WPARAM
)0, (LPARAM
)MAKELONG(fromChar
, toChar
)) 
 518     SendMessage(hWnd
, CB_SETEDITSEL
, (WPARAM
)fromChar
, (LPARAM
)toChar
) 
 522         wxLogDebug(_T("CB_SETEDITSEL failed"));