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     #ifndef __GNUWIN32_OLD__ 
  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 
  95                 wxComboBox 
*combo 
= wxDynamicCast(win
, wxComboBox
); 
  96                 wxCHECK_MSG( combo
, 0, _T("should have combo as parent") ); 
  98                 if ( combo
->MSWProcessEditMsg(message
, wParam
, lParam
) ) 
 106                 wxCHECK_MSG( win
, 0, _T("should have a parent") ); 
 108                 if ( win
->GetWindowStyle() & wxPROCESS_ENTER 
) 
 110                     // need to return a custom dlg code or we'll never get it 
 111                     return DLGC_WANTMESSAGE
; 
 117         // deal with tooltips here 
 121                 wxCHECK_MSG( win
, 0, _T("should have a parent") ); 
 123                 NMHDR
* hdr 
= (NMHDR 
*)lParam
; 
 124                 if ( (int)hdr
->code 
== TTN_NEEDTEXT 
) 
 126                     wxToolTip 
*tooltip 
= win
->GetToolTip(); 
 129                         TOOLTIPTEXT 
*ttt 
= (TOOLTIPTEXT 
*)lParam
; 
 130                         ttt
->lpszText 
= (wxChar 
*)tooltip
->GetTip().c_str(); 
 138 #endif // wxUSE_TOOLTIPS 
 141     return ::CallWindowProc(CASTWNDPROC gs_wndprocEdit
, hWnd
, message
, wParam
, lParam
); 
 144 WXHBRUSH 
wxComboBox::OnCtlColor(WXHDC pDC
, WXHWND 
WXUNUSED(pWnd
), WXUINT 
WXUNUSED(nCtlColor
), 
 150                                WXUINT 
WXUNUSED(message
), 
 151                                WXWPARAM 
WXUNUSED(wParam
), 
 152                                WXLPARAM 
WXUNUSED(lParam
) 
 159         HBRUSH hbrush 
= Ctl3dCtlColorEx(message
, wParam
, lParam
); 
 160         return (WXHBRUSH
) hbrush
; 
 162 #endif // wxUSE_CTL3D 
 165     if (GetParent()->GetTransparentBackground()) 
 166         SetBkMode(hdc
, TRANSPARENT
); 
 168         SetBkMode(hdc
, OPAQUE
); 
 170     wxColour colBack 
= GetBackgroundColour(); 
 173         colBack 
= wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE
); 
 175     ::SetBkColor(hdc
, wxColourToRGB(colBack
)); 
 176     ::SetTextColor(hdc
, wxColourToRGB(GetForegroundColour())); 
 178     wxBrush 
*brush 
= wxTheBrushList
->FindOrCreateBrush(colBack
, wxSOLID
); 
 180     return (WXHBRUSH
)brush
->GetResourceHandle(); 
 183 // ---------------------------------------------------------------------------- 
 185 // ---------------------------------------------------------------------------- 
 187 bool wxComboBox::MSWProcessEditMsg(WXUINT msg
, WXWPARAM wParam
, WXLPARAM lParam
) 
 192             return HandleChar(wParam
, lParam
, TRUE 
/* isASCII */); 
 195             return HandleKeyDown(wParam
, lParam
); 
 198             return HandleKeyUp(wParam
, lParam
); 
 204 bool wxComboBox::MSWCommand(WXUINT param
, WXWORD 
WXUNUSED(id
)) 
 211             sel 
= GetSelection(); 
 214                 value 
= GetString(sel
); 
 216                 wxCommandEvent 
event(wxEVT_COMMAND_COMBOBOX_SELECTED
, GetId()); 
 218                 event
.SetEventObject(this); 
 219                 event
.SetString(value
); 
 220                 ProcessCommand(event
); 
 227             // fall through: for compability with wxGTK, also send the text 
 228             // update event when the selection changes (this also seems more 
 229             // logical as the text does change) 
 233                 // if sel != -1, value was initialized above (and we can't use 
 234                 // GetValue() here as it would return the old selection and we 
 236                 wxCommandEvent 
event(wxEVT_COMMAND_TEXT_UPDATED
, GetId()); 
 237                 event
.SetString(sel 
== -1 ? GetValue() : value
); 
 238                 event
.SetEventObject(this); 
 239                 ProcessCommand(event
); 
 244     // there is no return value for the CBN_ notifications, so always return 
 245     // FALSE from here to pass the message to DefWindowProc() 
 249 WXHWND 
wxComboBox::GetEditHWND() const 
 251     // this function should not be called for wxCB_READONLY controls, it is 
 252     // the callers responsability to check this 
 253     wxASSERT_MSG( !(GetWindowStyle() & wxCB_READONLY
), 
 254                   _T("read-only combobox doesn't have any edit control") ); 
 258     HWND hwndEdit 
= ::ChildWindowFromPoint(GetHwnd(), pt
); 
 259     if ( !hwndEdit 
|| hwndEdit 
== GetHwnd() ) 
 261         wxFAIL_MSG(_T("not read only combobox without edit control?")); 
 264     return (WXHWND
)hwndEdit
; 
 267 bool wxComboBox::Create(wxWindow 
*parent
, wxWindowID id
, 
 268                         const wxString
& value
, 
 271                         int n
, const wxString choices
[], 
 273                         const wxValidator
& validator
, 
 274                         const wxString
& name
) 
 276     // first create wxWin object 
 277     if ( !CreateControl(parent
, id
, pos
, size
, style
, validator
, name
) ) 
 280     // get the right style 
 281     long msStyle 
= WS_TABSTOP 
| WS_VSCROLL 
| WS_HSCROLL 
| 
 282                    CBS_AUTOHSCROLL 
| CBS_NOINTEGRALHEIGHT 
/* | WS_CLIPSIBLINGS */; 
 283     if ( style 
& wxCB_READONLY 
) 
 284         msStyle 
|= CBS_DROPDOWNLIST
; 
 285     else if ( style 
& wxCB_SIMPLE 
) 
 286         msStyle 
|= CBS_SIMPLE
; // A list (shown always) and edit control 
 288         msStyle 
|= CBS_DROPDOWN
; 
 290     if ( style 
& wxCB_SORT 
) 
 293     if ( style 
& wxCLIP_SIBLINGS 
) 
 294         msStyle 
|= WS_CLIPSIBLINGS
; 
 297     // and now create the MSW control 
 298     if ( !MSWCreateControl(_T("COMBOBOX"), msStyle
) ) 
 301     SetSize(pos
.x
, pos
.y
, size
.x
, size
.y
); 
 303     // A choice/combobox normally has a white background (or other, depending 
 304     // on global settings) rather than inheriting the parent's background colour. 
 305     SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_WINDOW
)); 
 307     for ( int i 
= 0; i 
< n
; i
++ ) 
 312     if ( !value
.IsEmpty() ) 
 317     // a (not read only) combobox is, in fact, 2 controls: the combobox itself 
 318     // and an edit control inside it and if we want to catch events from this 
 319     // edit control, we must subclass it as well 
 320     if ( !(style 
& wxCB_READONLY
) ) 
 322         gs_wndprocEdit 
= (WXFARPROC
)::SetWindowLong
 
 326                                         (LPARAM
)wxComboEditWndProc
 
 333 // TODO: update and clear all this horrible mess (VZ) 
 335 void wxComboBox::SetValue(const wxString
& value
) 
 337   // If newlines are denoted by just 10, must stick 13 in front. 
 339   int len 
= value
.Length(); 
 341   for (i 
= 0; i 
< len
; i 
++) 
 343     if ((i 
> 0) && (value
[i
] == 10) && (value
[i
-1] != 13)) 
 348     wxChar 
*tmp 
= new wxChar
[len 
+ singletons 
+ 1]; 
 350     for (i 
= 0; i 
< len
; i 
++) 
 352       if ((i 
> 0) && (value
[i
] == 10) && (value
[i
-1] != 13)) 
 361     SetWindowText(GetHwnd(), tmp
); 
 365     SetWindowText(GetHwnd(), value
); 
 368 // Clipboard operations 
 369 void wxComboBox::Copy() 
 371   HWND hWnd 
= GetHwnd(); 
 372   SendMessage(hWnd
, WM_COPY
, 0, 0L); 
 375 void wxComboBox::Cut() 
 377   HWND hWnd 
= GetHwnd(); 
 378   SendMessage(hWnd
, WM_CUT
, 0, 0L); 
 381 void wxComboBox::Paste() 
 383   HWND hWnd 
= GetHwnd(); 
 384   SendMessage(hWnd
, WM_PASTE
, 0, 0L); 
 387 void wxComboBox::SetEditable(bool WXUNUSED(editable
)) 
 389   // Can't implement in MSW? 
 390 //  HWND hWnd = GetHwnd(); 
 391 //  SendMessage(hWnd, EM_SETREADONLY, (WPARAM)!editable, (LPARAM)0L); 
 394 void wxComboBox::SetInsertionPoint(long pos
) 
 396     if ( GetWindowStyle() & wxCB_READONLY 
) 
 400     HWND hWnd 
= GetHwnd(); 
 401     ::SendMessage(hWnd
, CB_SETEDITSEL
, 0, MAKELPARAM(pos
, pos
)); 
 402     HWND hEditWnd 
= (HWND
) GetEditHWND() ; 
 405         // Scroll insertion point into view 
 406         SendMessage(hEditWnd
, EM_SCROLLCARET
, (WPARAM
)0, (LPARAM
)0); 
 407         // Why is this necessary? (Copied from wxTextCtrl::SetInsertionPoint) 
 408         SendMessage(hEditWnd
, EM_REPLACESEL
, 0, (LPARAM
)_T("")); 
 413 void wxComboBox::SetInsertionPointEnd() 
 415     // setting insertion point doesn't make sense for read only comboboxes 
 416     if ( !(GetWindowStyle() & wxCB_READONLY
) ) 
 418         long pos 
= GetLastPosition(); 
 419         SetInsertionPoint(pos
); 
 423 long wxComboBox::GetInsertionPoint() const 
 426     DWORD Pos
=(DWORD
)SendMessage(GetHwnd(), CB_GETEDITSEL
, 0, 0L); 
 433 long wxComboBox::GetLastPosition() const 
 435     HWND hEditWnd 
= (HWND
) GetEditHWND(); 
 437     // Get number of characters in the last (only) line. We'll add this to the character 
 438     // index for the last line, 1st position. 
 439     int lineLength 
= (int)SendMessage(hEditWnd
, EM_LINELENGTH
, (WPARAM
) 0, (LPARAM
)0L); 
 441     return (long)(lineLength
); 
 444 void wxComboBox::Replace(long from
, long to
, const wxString
& value
) 
 449     // Now replace with 'value', by pasting. 
 450     wxSetClipboardData(wxDF_TEXT
, (wxObject 
*)(const wxChar 
*)value
, 0, 0); 
 452     // Paste into edit control 
 453     SendMessage(GetHwnd(), WM_PASTE
, (WPARAM
)0, (LPARAM
)0L); 
 457 void wxComboBox::Remove(long from
, long to
) 
 459     // Set selection and remove it 
 460     SetSelection(from
, to
); 
 461     SendMessage(GetHwnd(), WM_CUT
, (WPARAM
)0, (LPARAM
)0); 
 464 void wxComboBox::SetSelection(long from
, long to
) 
 466     HWND hWnd 
= GetHwnd(); 
 467     long fromChar 
= from
; 
 469     // if from and to are both -1, it means 
 470     // (in wxWindows) that all text should be selected. 
 471     // This translates into Windows convention 
 472     if ((from 
== -1) && (to 
== -1)) 
 480     SendMessage(hWnd
, CB_SETEDITSEL
, (WPARAM
)0, (LPARAM
)MAKELONG(fromChar
, toChar
)) 
 482     SendMessage(hWnd
, CB_SETEDITSEL
, (WPARAM
)fromChar
, (LPARAM
)toChar
) 
 486         wxLogDebug(_T("CB_SETEDITSEL failed")); 
 490 wxSize 
wxComboBox::DoGetBestSize() const 
 492     // the choice calculates the horz size correctly, but not the vertical 
 493     // component: correct it 
 494     wxSize size 
= wxChoice::DoGetBestSize(); 
 497     wxGetCharSize(GetHWND(), &cx
, &cy
, &GetFont()); 
 498     size
.y 
= EDIT_HEIGHT_FROM_CHAR_HEIGHT(cy
);