]> git.saurik.com Git - wxWidgets.git/blame - src/msw/combobox.cpp
unicode fixes
[wxWidgets.git] / src / msw / combobox.cpp
CommitLineData
2bda0e17 1/////////////////////////////////////////////////////////////////////////////
f6bcfd97 2// Name: msw/combobox.cpp
2bda0e17
KB
3// Purpose: wxComboBox class
4// Author: Julian Smart
5// Modified by:
6// Created: 01/02/97
7// RCS-ID: $Id$
6c9a19aa 8// Copyright: (c) Julian Smart
c085e333 9// Licence: wxWindows licence
2bda0e17
KB
10/////////////////////////////////////////////////////////////////////////////
11
f6bcfd97
BP
12// ============================================================================
13// declarations
14// ============================================================================
15
16// ----------------------------------------------------------------------------
17// headers
18// ----------------------------------------------------------------------------
19
14f355c2 20#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
2bda0e17
KB
21#pragma implementation "combobox.h"
22#endif
23
24// For compilers that support precompilation, includes "wx.h".
25#include "wx/wxprec.h"
26
27#ifdef __BORLANDC__
fddfd9e1 28 #pragma hdrstop
2bda0e17
KB
29#endif
30
af79c52d
VZ
31#if wxUSE_COMBOBOX
32
2bda0e17 33#ifndef WX_PRECOMP
fddfd9e1
VZ
34 #include "wx/settings.h"
35 #include "wx/log.h"
2b5f62a0
VZ
36 // for wxEVT_COMMAND_TEXT_ENTER
37 #include "wx/textctrl.h"
2bda0e17
KB
38#endif
39
2bda0e17 40#include "wx/combobox.h"
f6bcfd97 41#include "wx/brush.h"
2bda0e17
KB
42#include "wx/clipbrd.h"
43#include "wx/msw/private.h"
44
f6bcfd97 45#if wxUSE_TOOLTIPS
ae090fdb 46 #if !defined(__GNUWIN32_OLD__) || defined(__CYGWIN10__)
f6bcfd97
BP
47 #include <commctrl.h>
48 #endif
49 #include "wx/tooltip.h"
50#endif // wxUSE_TOOLTIPS
51
52// ----------------------------------------------------------------------------
53// wxWin macros
54// ----------------------------------------------------------------------------
55
6a89f9ee 56#if wxUSE_EXTENDED_RTTI
bc9fb572
JS
57WX_DEFINE_FLAGS( wxComboBoxStyle )
58
59WX_BEGIN_FLAGS( wxComboBoxStyle )
60 // new style border flags, we put them first to
61 // use them for streaming out
62 WX_FLAGS_MEMBER(wxBORDER_SIMPLE)
63 WX_FLAGS_MEMBER(wxBORDER_SUNKEN)
64 WX_FLAGS_MEMBER(wxBORDER_DOUBLE)
65 WX_FLAGS_MEMBER(wxBORDER_RAISED)
66 WX_FLAGS_MEMBER(wxBORDER_STATIC)
67 WX_FLAGS_MEMBER(wxBORDER_NONE)
68
69 // old style border flags
70 WX_FLAGS_MEMBER(wxSIMPLE_BORDER)
71 WX_FLAGS_MEMBER(wxSUNKEN_BORDER)
72 WX_FLAGS_MEMBER(wxDOUBLE_BORDER)
73 WX_FLAGS_MEMBER(wxRAISED_BORDER)
74 WX_FLAGS_MEMBER(wxSTATIC_BORDER)
75 WX_FLAGS_MEMBER(wxNO_BORDER)
76
77 // standard window styles
78 WX_FLAGS_MEMBER(wxTAB_TRAVERSAL)
79 WX_FLAGS_MEMBER(wxCLIP_CHILDREN)
80 WX_FLAGS_MEMBER(wxTRANSPARENT_WINDOW)
81 WX_FLAGS_MEMBER(wxWANTS_CHARS)
82 WX_FLAGS_MEMBER(wxNO_FULL_REPAINT_ON_RESIZE)
83 WX_FLAGS_MEMBER(wxALWAYS_SHOW_SB )
84 WX_FLAGS_MEMBER(wxVSCROLL)
85 WX_FLAGS_MEMBER(wxHSCROLL)
86
87 WX_FLAGS_MEMBER(wxCB_SIMPLE)
88 WX_FLAGS_MEMBER(wxCB_SORT)
89 WX_FLAGS_MEMBER(wxCB_READONLY)
90 WX_FLAGS_MEMBER(wxCB_DROPDOWN)
91
92WX_END_FLAGS( wxComboBoxStyle )
93
6a89f9ee
SC
94IMPLEMENT_DYNAMIC_CLASS_XTI(wxComboBox, wxControl,"wx/combobox.h")
95
96WX_BEGIN_PROPERTIES_TABLE(wxComboBox)
97 // TODO DELEGATES
067e9be6
SC
98 WX_PROPERTY( Font , wxFont , SetFont , GetFont , , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
99 WX_PROPERTY_COLLECTION( Choices , wxArrayString , wxString , AppendString , GetStrings , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
100 WX_PROPERTY( Value ,wxString, SetValue, GetValue, , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
101 WX_PROPERTY( Selection ,int, SetSelection, GetSelection, , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
bc9fb572 102 WX_PROPERTY_FLAGS( WindowStyle , wxComboBoxStyle , long , SetWindowStyleFlag , GetWindowStyleFlag , , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
6a89f9ee
SC
103WX_END_PROPERTIES_TABLE()
104
105WX_BEGIN_HANDLERS_TABLE(wxComboBox)
106WX_END_HANDLERS_TABLE()
2bda0e17 107
6a89f9ee
SC
108WX_CONSTRUCTOR_5( wxComboBox , wxWindow* , Parent , wxWindowID , Id , wxString , Value , wxPoint , Position , wxSize , Size )
109#else
110IMPLEMENT_DYNAMIC_CLASS(wxComboBox, wxControl)
111#endif
066f1b7a 112
f6bcfd97
BP
113// ----------------------------------------------------------------------------
114// function prototypes
115// ----------------------------------------------------------------------------
116
117LRESULT APIENTRY _EXPORT wxComboEditWndProc(HWND hWnd,
118 UINT message,
119 WPARAM wParam,
120 LPARAM lParam);
121
122// ---------------------------------------------------------------------------
123// global vars
124// ---------------------------------------------------------------------------
125
126// the pointer to standard radio button wnd proc
127static WXFARPROC gs_wndprocEdit = (WXFARPROC)NULL;
128
129// ============================================================================
130// implementation
131// ============================================================================
132
133// ----------------------------------------------------------------------------
134// wnd proc for subclassed edit control
135// ----------------------------------------------------------------------------
136
137LRESULT APIENTRY _EXPORT wxComboEditWndProc(HWND hWnd,
138 UINT message,
139 WPARAM wParam,
140 LPARAM lParam)
141{
142 HWND hwndCombo = ::GetParent(hWnd);
143 wxWindow *win = wxFindWinFromHandle((WXHWND)hwndCombo);
144
145 switch ( message )
146 {
53c3a78b
VZ
147 // forward some messages to the combobox to generate the appropriate
148 // wxEvents from them
f6bcfd97
BP
149 case WM_KEYUP:
150 case WM_KEYDOWN:
151 case WM_CHAR:
53c3a78b
VZ
152 case WM_SETFOCUS:
153 case WM_KILLFOCUS:
f6bcfd97
BP
154 {
155 wxComboBox *combo = wxDynamicCast(win, wxComboBox);
64b39766
VZ
156 if ( !combo )
157 {
158 // we can get WM_KILLFOCUS while our parent is already half
159 // destroyed and hence doesn't look like a combobx any
160 // longer, check for it to avoid bogus assert failures
161 if ( !win->IsBeingDeleted() )
162 {
163 wxFAIL_MSG( _T("should have combo as parent") );
164 }
165 }
166 else if ( combo->MSWProcessEditMsg(message, wParam, lParam) )
167 {
168 // handled by parent
f6bcfd97 169 return 0;
64b39766 170 }
f6bcfd97
BP
171 }
172 break;
173
f6bcfd97
BP
174 case WM_GETDLGCODE:
175 {
176 wxCHECK_MSG( win, 0, _T("should have a parent") );
177
178 if ( win->GetWindowStyle() & wxPROCESS_ENTER )
179 {
180 // need to return a custom dlg code or we'll never get it
181 return DLGC_WANTMESSAGE;
182 }
183 }
184 break;
f6bcfd97
BP
185
186 // deal with tooltips here
ae090fdb 187#if wxUSE_TOOLTIPS && defined(TTN_NEEDTEXT)
f6bcfd97
BP
188 case WM_NOTIFY:
189 {
190 wxCHECK_MSG( win, 0, _T("should have a parent") );
191
192 NMHDR* hdr = (NMHDR *)lParam;
2b5f62a0 193 if ( hdr->code == TTN_NEEDTEXT )
f6bcfd97
BP
194 {
195 wxToolTip *tooltip = win->GetToolTip();
196 if ( tooltip )
197 {
198 TOOLTIPTEXT *ttt = (TOOLTIPTEXT *)lParam;
199 ttt->lpszText = (wxChar *)tooltip->GetTip().c_str();
200 }
201
202 // processed
203 return 0;
204 }
205 }
206 break;
207#endif // wxUSE_TOOLTIPS
208 }
209
210 return ::CallWindowProc(CASTWNDPROC gs_wndprocEdit, hWnd, message, wParam, lParam);
211}
212
33ac7e6f
KB
213WXHBRUSH wxComboBox::OnCtlColor(WXHDC pDC, WXHWND WXUNUSED(pWnd), WXUINT WXUNUSED(nCtlColor),
214 WXUINT WXUNUSED(message),
215 WXWPARAM WXUNUSED(wParam),
788722ac 216 WXLPARAM WXUNUSED(lParam)
788722ac 217 )
f6bcfd97 218{
f6bcfd97 219 HDC hdc = (HDC)pDC;
f6bcfd97
BP
220 wxColour colBack = GetBackgroundColour();
221
222 if (!IsEnabled())
a756f210 223 colBack = wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE);
f6bcfd97
BP
224
225 ::SetBkColor(hdc, wxColourToRGB(colBack));
226 ::SetTextColor(hdc, wxColourToRGB(GetForegroundColour()));
227
228 wxBrush *brush = wxTheBrushList->FindOrCreateBrush(colBack, wxSOLID);
229
230 return (WXHBRUSH)brush->GetResourceHandle();
231}
232
233// ----------------------------------------------------------------------------
234// wxComboBox
235// ----------------------------------------------------------------------------
236
237bool wxComboBox::MSWProcessEditMsg(WXUINT msg, WXWPARAM wParam, WXLPARAM lParam)
238{
239 switch ( msg )
240 {
241 case WM_CHAR:
2b5f62a0
VZ
242 // for compatibility with wxTextCtrl, generate a special message
243 // when Enter is pressed
244 if ( wParam == VK_RETURN )
245 {
246 wxCommandEvent event(wxEVT_COMMAND_TEXT_ENTER, m_windowId);
247 InitCommandEvent(event);
248 event.SetString(GetValue());
249 event.SetInt(GetSelection());
250 ProcessCommand(event);
251 }
252
f6bcfd97 253 return HandleChar(wParam, lParam, TRUE /* isASCII */);
889f0b7c 254
f6bcfd97
BP
255 case WM_KEYDOWN:
256 return HandleKeyDown(wParam, lParam);
257
258 case WM_KEYUP:
259 return HandleKeyUp(wParam, lParam);
53c3a78b
VZ
260
261 case WM_SETFOCUS:
262 return HandleSetFocus((WXHWND)wParam);
263
264 case WM_KILLFOCUS:
265 return HandleKillFocus((WXHWND)wParam);
f6bcfd97
BP
266 }
267
268 return FALSE;
269}
270
debe6624 271bool wxComboBox::MSWCommand(WXUINT param, WXWORD WXUNUSED(id))
2bda0e17 272{
fddfd9e1
VZ
273 wxString value;
274 int sel = -1;
cd0b1709 275 switch ( param )
8c1c5302 276 {
cd0b1709 277 case CBN_SELCHANGE:
fddfd9e1
VZ
278 sel = GetSelection();
279 if ( sel > -1 )
cd0b1709 280 {
fddfd9e1
VZ
281 value = GetString(sel);
282
cd0b1709 283 wxCommandEvent event(wxEVT_COMMAND_COMBOBOX_SELECTED, GetId());
fddfd9e1 284 event.SetInt(sel);
cd0b1709 285 event.SetEventObject(this);
fddfd9e1 286 event.SetString(value);
cd0b1709
VZ
287 ProcessCommand(event);
288 }
fddfd9e1
VZ
289 else
290 {
291 break;
292 }
293
294 // fall through: for compability with wxGTK, also send the text
295 // update event when the selection changes (this also seems more
296 // logical as the text does change)
29006414 297
cd0b1709
VZ
298 case CBN_EDITCHANGE:
299 {
7ba166dd 300 wxCommandEvent event(wxEVT_COMMAND_TEXT_UPDATED, GetId());
3adc70bf 301
fddfd9e1
VZ
302 // if sel != -1, value was initialized above (and we can't use
303 // GetValue() here as it would return the old selection and we
304 // want the new one)
7ba166dd 305 if ( sel == -1 )
3adc70bf 306 {
7ba166dd 307 value = GetValue();
3adc70bf
VZ
308 }
309 else // we're synthesizing text updated event from sel change
310 {
311 // we need to do this because the user code expects
312 // wxComboBox::GetValue() to return the new value from
313 // "text updated" handler but it hadn't been updated yet
314 SetValue(value);
315 }
316
7ba166dd 317 event.SetString(value);
cd0b1709 318 event.SetEventObject(this);
0ef2ebba 319 ProcessCommand(event);
cd0b1709
VZ
320 }
321 break;
322 }
29006414 323
cd0b1709
VZ
324 // there is no return value for the CBN_ notifications, so always return
325 // FALSE from here to pass the message to DefWindowProc()
326 return FALSE;
2bda0e17
KB
327}
328
f6bcfd97
BP
329WXHWND wxComboBox::GetEditHWND() const
330{
331 // this function should not be called for wxCB_READONLY controls, it is
332 // the callers responsability to check this
333 wxASSERT_MSG( !(GetWindowStyle() & wxCB_READONLY),
334 _T("read-only combobox doesn't have any edit control") );
335
336 POINT pt;
337 pt.x = pt.y = 4;
338 HWND hwndEdit = ::ChildWindowFromPoint(GetHwnd(), pt);
339 if ( !hwndEdit || hwndEdit == GetHwnd() )
340 {
341 wxFAIL_MSG(_T("not read only combobox without edit control?"));
342 }
343
344 return (WXHWND)hwndEdit;
345}
346
debe6624 347bool wxComboBox::Create(wxWindow *parent, wxWindowID id,
c085e333
VZ
348 const wxString& value,
349 const wxPoint& pos,
350 const wxSize& size,
351 int n, const wxString choices[],
352 long style,
353 const wxValidator& validator,
354 const wxString& name)
2bda0e17 355{
bdf5c30d
VZ
356 // pretend that wxComboBox is hidden while it is positioned and resized and
357 // show it only right before leaving this method because otherwise there is
358 // some noticeable flicker while the control rearranges itself
359 m_isShown = FALSE;
360
f6bcfd97
BP
361 // first create wxWin object
362 if ( !CreateControl(parent, id, pos, size, style, validator, name) )
363 return FALSE;
364
365 // get the right style
366 long msStyle = WS_TABSTOP | WS_VSCROLL | WS_HSCROLL |
367 CBS_AUTOHSCROLL | CBS_NOINTEGRALHEIGHT /* | WS_CLIPSIBLINGS */;
368 if ( style & wxCB_READONLY )
369 msStyle |= CBS_DROPDOWNLIST;
4676948b 370#ifndef __WXWINCE__
f6bcfd97
BP
371 else if ( style & wxCB_SIMPLE )
372 msStyle |= CBS_SIMPLE; // A list (shown always) and edit control
4676948b 373#endif
f6bcfd97
BP
374 else
375 msStyle |= CBS_DROPDOWN;
376
377 if ( style & wxCB_SORT )
378 msStyle |= CBS_SORT;
379
b0766406
JS
380 if ( style & wxCLIP_SIBLINGS )
381 msStyle |= WS_CLIPSIBLINGS;
382
383
f6bcfd97 384 // and now create the MSW control
2eb4c3aa 385 if ( !MSWCreateControl(_T("COMBOBOX"), msStyle) )
f6bcfd97
BP
386 return FALSE;
387
f6bcfd97
BP
388 // A choice/combobox normally has a white background (or other, depending
389 // on global settings) rather than inheriting the parent's background colour.
a756f210 390 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
f6bcfd97
BP
391
392 for ( int i = 0; i < n; i++ )
393 {
394 Append(choices[i]);
395 }
c085e333 396
f6bcfd97
BP
397 if ( !value.IsEmpty() )
398 {
399 SetValue(value);
400 }
2bda0e17 401
db34b147
VZ
402 // do this after appending the values to the combobox so that autosizing
403 // works correctly
404 SetSize(pos.x, pos.y, size.x, size.y);
405
f6bcfd97
BP
406 // a (not read only) combobox is, in fact, 2 controls: the combobox itself
407 // and an edit control inside it and if we want to catch events from this
408 // edit control, we must subclass it as well
409 if ( !(style & wxCB_READONLY) )
410 {
411 gs_wndprocEdit = (WXFARPROC)::SetWindowLong
412 (
413 (HWND)GetEditHWND(),
414 GWL_WNDPROC,
415 (LPARAM)wxComboEditWndProc
416 );
417 }
2bda0e17 418
bdf5c30d
VZ
419 // and finally, show the control
420 Show(TRUE);
421
f6bcfd97 422 return TRUE;
2bda0e17
KB
423}
424
2bda0e17
KB
425void wxComboBox::SetValue(const wxString& value)
426{
2b5f62a0
VZ
427 if ( HasFlag(wxCB_READONLY) )
428 SetStringSelection(value);
b38f3ff3 429 else
2b5f62a0 430 SetWindowText(GetHwnd(), value.c_str());
2bda0e17
KB
431}
432
433// Clipboard operations
1c4a764c 434void wxComboBox::Copy()
2bda0e17 435{
2b5f62a0 436 SendMessage(GetHwnd(), WM_COPY, 0, 0L);
2bda0e17
KB
437}
438
1c4a764c 439void wxComboBox::Cut()
2bda0e17 440{
2b5f62a0 441 SendMessage(GetHwnd(), WM_CUT, 0, 0L);
2bda0e17
KB
442}
443
1c4a764c 444void wxComboBox::Paste()
2bda0e17 445{
2b5f62a0 446 SendMessage(GetHwnd(), WM_PASTE, 0, 0L);
2bda0e17
KB
447}
448
33ac7e6f 449void wxComboBox::SetEditable(bool WXUNUSED(editable))
2bda0e17
KB
450{
451 // Can't implement in MSW?
4438caf4 452// HWND hWnd = GetHwnd();
2bda0e17
KB
453// SendMessage(hWnd, EM_SETREADONLY, (WPARAM)!editable, (LPARAM)0L);
454}
455
debe6624 456void wxComboBox::SetInsertionPoint(long pos)
2bda0e17 457{
6d14cac7
VZ
458 if ( GetWindowStyle() & wxCB_READONLY )
459 return;
460
2bda0e17 461#ifdef __WIN32__
6d14cac7
VZ
462 HWND hWnd = GetHwnd();
463 ::SendMessage(hWnd, CB_SETEDITSEL, 0, MAKELPARAM(pos, pos));
464 HWND hEditWnd = (HWND) GetEditHWND() ;
465 if ( hEditWnd )
466 {
467 // Scroll insertion point into view
468 SendMessage(hEditWnd, EM_SCROLLCARET, (WPARAM)0, (LPARAM)0);
469 // Why is this necessary? (Copied from wxTextCtrl::SetInsertionPoint)
fda7962d 470 SendMessage(hEditWnd, EM_REPLACESEL, 0, (LPARAM) wxEmptyString);
6d14cac7
VZ
471 }
472#endif // __WIN32__
2bda0e17
KB
473}
474
1c4a764c 475void wxComboBox::SetInsertionPointEnd()
2bda0e17 476{
6d14cac7
VZ
477 // setting insertion point doesn't make sense for read only comboboxes
478 if ( !(GetWindowStyle() & wxCB_READONLY) )
479 {
480 long pos = GetLastPosition();
481 SetInsertionPoint(pos);
482 }
2bda0e17
KB
483}
484
1c4a764c 485long wxComboBox::GetInsertionPoint() const
2bda0e17 486{
f7703477
JS
487#ifdef __WIN32__
488 DWORD Pos=(DWORD)SendMessage(GetHwnd(), CB_GETEDITSEL, 0, 0L);
489 return Pos&0xFFFF;
490#else
491 return 0;
492#endif
2bda0e17
KB
493}
494
1c4a764c 495long wxComboBox::GetLastPosition() const
2bda0e17 496{
f7703477 497 HWND hEditWnd = (HWND) GetEditHWND();
4438caf4 498
f7703477 499 // Get number of characters in the last (only) line. We'll add this to the character
2bda0e17 500 // index for the last line, 1st position.
f7703477 501 int lineLength = (int)SendMessage(hEditWnd, EM_LINELENGTH, (WPARAM) 0, (LPARAM)0L);
2bda0e17 502
f7703477 503 return (long)(lineLength);
2bda0e17
KB
504}
505
debe6624 506void wxComboBox::Replace(long from, long to, const wxString& value)
2bda0e17 507{
47d67540 508#if wxUSE_CLIPBOARD
fddfd9e1 509 Remove(from, to);
2bda0e17
KB
510
511 // Now replace with 'value', by pasting.
837e5743 512 wxSetClipboardData(wxDF_TEXT, (wxObject *)(const wxChar *)value, 0, 0);
2bda0e17
KB
513
514 // Paste into edit control
fddfd9e1 515 SendMessage(GetHwnd(), WM_PASTE, (WPARAM)0, (LPARAM)0L);
2bda0e17
KB
516#endif
517}
518
debe6624 519void wxComboBox::Remove(long from, long to)
2bda0e17 520{
fddfd9e1
VZ
521 // Set selection and remove it
522 SetSelection(from, to);
523 SendMessage(GetHwnd(), WM_CUT, (WPARAM)0, (LPARAM)0);
2bda0e17
KB
524}
525
debe6624 526void wxComboBox::SetSelection(long from, long to)
2bda0e17 527{
4438caf4 528 HWND hWnd = GetHwnd();
2bda0e17
KB
529 long fromChar = from;
530 long toChar = to;
531 // if from and to are both -1, it means
532 // (in wxWindows) that all text should be selected.
533 // This translates into Windows convention
534 if ((from == -1) && (to == -1))
535 {
536 fromChar = 0;
537 toChar = -1;
538 }
4438caf4 539
33ac7e6f 540 if (
2bda0e17 541#ifdef __WIN32__
fddfd9e1
VZ
542 SendMessage(hWnd, CB_SETEDITSEL, (WPARAM)0, (LPARAM)MAKELONG(fromChar, toChar))
543#else // Win16
544 SendMessage(hWnd, CB_SETEDITSEL, (WPARAM)fromChar, (LPARAM)toChar)
2bda0e17 545#endif
fddfd9e1
VZ
546 == CB_ERR )
547 {
548 wxLogDebug(_T("CB_SETEDITSEL failed"));
549 }
2bda0e17
KB
550}
551
552#endif
47d67540 553 // wxUSE_COMBOBOX
2bda0e17 554