]> git.saurik.com Git - wxWidgets.git/blob - src/msw/combobox.cpp
merged fix from 2.2 branch
[wxWidgets.git] / src / msw / combobox.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: msw/combobox.cpp
3 // Purpose: wxComboBox class
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 01/02/97
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart and Markus Holzem
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 #ifdef __GNUG__
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__
28 #pragma hdrstop
29 #endif
30
31 #if wxUSE_COMBOBOX
32
33 #ifndef WX_PRECOMP
34 #include "wx/settings.h"
35 #endif
36
37 #include "wx/combobox.h"
38 #include "wx/brush.h"
39 #include "wx/clipbrd.h"
40 #include "wx/msw/private.h"
41
42 #if wxUSE_TOOLTIPS
43 #ifndef __GNUWIN32_OLD__
44 #include <commctrl.h>
45 #endif
46 #include "wx/tooltip.h"
47 #endif // wxUSE_TOOLTIPS
48
49 // ----------------------------------------------------------------------------
50 // wxWin macros
51 // ----------------------------------------------------------------------------
52
53 IMPLEMENT_DYNAMIC_CLASS(wxComboBox, wxControl)
54
55 // ----------------------------------------------------------------------------
56 // function prototypes
57 // ----------------------------------------------------------------------------
58
59 LRESULT APIENTRY _EXPORT wxComboEditWndProc(HWND hWnd,
60 UINT message,
61 WPARAM wParam,
62 LPARAM lParam);
63
64 // ---------------------------------------------------------------------------
65 // global vars
66 // ---------------------------------------------------------------------------
67
68 // the pointer to standard radio button wnd proc
69 static WXFARPROC gs_wndprocEdit = (WXFARPROC)NULL;
70
71 // ============================================================================
72 // implementation
73 // ============================================================================
74
75 // ----------------------------------------------------------------------------
76 // wnd proc for subclassed edit control
77 // ----------------------------------------------------------------------------
78
79 LRESULT APIENTRY _EXPORT wxComboEditWndProc(HWND hWnd,
80 UINT message,
81 WPARAM wParam,
82 LPARAM lParam)
83 {
84 HWND hwndCombo = ::GetParent(hWnd);
85 wxWindow *win = wxFindWinFromHandle((WXHWND)hwndCombo);
86
87 switch ( message )
88 {
89 // forward some messages to the combobox
90 case WM_KEYUP:
91 case WM_KEYDOWN:
92 case WM_CHAR:
93 {
94 wxComboBox *combo = wxDynamicCast(win, wxComboBox);
95 wxCHECK_MSG( combo, 0, _T("should have combo as parent") );
96
97 if ( combo->MSWProcessEditMsg(message, wParam, lParam) )
98 return 0;
99 }
100 break;
101
102 #if 0
103 case WM_GETDLGCODE:
104 {
105 wxCHECK_MSG( win, 0, _T("should have a parent") );
106
107 if ( win->GetWindowStyle() & wxPROCESS_ENTER )
108 {
109 // need to return a custom dlg code or we'll never get it
110 return DLGC_WANTMESSAGE;
111 }
112 }
113 break;
114 #endif // 0
115
116 // deal with tooltips here
117 #if wxUSE_TOOLTIPS
118 case WM_NOTIFY:
119 {
120 wxCHECK_MSG( win, 0, _T("should have a parent") );
121
122 NMHDR* hdr = (NMHDR *)lParam;
123 if ( (int)hdr->code == TTN_NEEDTEXT )
124 {
125 wxToolTip *tooltip = win->GetToolTip();
126 if ( tooltip )
127 {
128 TOOLTIPTEXT *ttt = (TOOLTIPTEXT *)lParam;
129 ttt->lpszText = (wxChar *)tooltip->GetTip().c_str();
130 }
131
132 // processed
133 return 0;
134 }
135 }
136 break;
137 #endif // wxUSE_TOOLTIPS
138 }
139
140 return ::CallWindowProc(CASTWNDPROC gs_wndprocEdit, hWnd, message, wParam, lParam);
141 }
142
143 WXHBRUSH wxComboBox::OnCtlColor(WXHDC pDC, WXHWND pWnd, WXUINT nCtlColor,
144 WXUINT message,
145 WXWPARAM wParam,
146 WXLPARAM lParam)
147 {
148 #if wxUSE_CTL3D
149 if ( m_useCtl3D )
150 {
151 HBRUSH hbrush = Ctl3dCtlColorEx(message, wParam, lParam);
152 return (WXHBRUSH) hbrush;
153 }
154 #endif // wxUSE_CTL3D
155
156 HDC hdc = (HDC)pDC;
157 if (GetParent()->GetTransparentBackground())
158 SetBkMode(hdc, TRANSPARENT);
159 else
160 SetBkMode(hdc, OPAQUE);
161
162 wxColour colBack = GetBackgroundColour();
163
164 if (!IsEnabled())
165 colBack = wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE);
166
167 ::SetBkColor(hdc, wxColourToRGB(colBack));
168 ::SetTextColor(hdc, wxColourToRGB(GetForegroundColour()));
169
170 wxBrush *brush = wxTheBrushList->FindOrCreateBrush(colBack, wxSOLID);
171
172 return (WXHBRUSH)brush->GetResourceHandle();
173 }
174
175 // ----------------------------------------------------------------------------
176 // wxComboBox
177 // ----------------------------------------------------------------------------
178
179 bool wxComboBox::MSWProcessEditMsg(WXUINT msg, WXWPARAM wParam, WXLPARAM lParam)
180 {
181 switch ( msg )
182 {
183 case WM_CHAR:
184 return HandleChar(wParam, lParam, TRUE /* isASCII */);
185
186 case WM_KEYDOWN:
187 return HandleKeyDown(wParam, lParam);
188
189 case WM_KEYUP:
190 return HandleKeyUp(wParam, lParam);
191 }
192
193 return FALSE;
194 }
195
196 bool wxComboBox::MSWCommand(WXUINT param, WXWORD WXUNUSED(id))
197 {
198 switch ( param )
199 {
200 case CBN_SELCHANGE:
201 if (GetSelection() > -1)
202 {
203 wxCommandEvent event(wxEVT_COMMAND_COMBOBOX_SELECTED, GetId());
204 event.SetInt(GetSelection());
205 event.SetEventObject(this);
206 event.SetString(GetStringSelection());
207 ProcessCommand(event);
208 }
209 break;
210
211 case CBN_EDITCHANGE:
212 {
213 wxCommandEvent event(wxEVT_COMMAND_TEXT_UPDATED, GetId());
214 event.SetString(GetValue());
215 event.SetEventObject(this);
216 ProcessCommand(event);
217 }
218 break;
219 }
220
221 // there is no return value for the CBN_ notifications, so always return
222 // FALSE from here to pass the message to DefWindowProc()
223 return FALSE;
224 }
225
226 WXHWND wxComboBox::GetEditHWND() const
227 {
228 // this function should not be called for wxCB_READONLY controls, it is
229 // the callers responsability to check this
230 wxASSERT_MSG( !(GetWindowStyle() & wxCB_READONLY),
231 _T("read-only combobox doesn't have any edit control") );
232
233 POINT pt;
234 pt.x = pt.y = 4;
235 HWND hwndEdit = ::ChildWindowFromPoint(GetHwnd(), pt);
236 if ( !hwndEdit || hwndEdit == GetHwnd() )
237 {
238 wxFAIL_MSG(_T("not read only combobox without edit control?"));
239 }
240
241 return (WXHWND)hwndEdit;
242 }
243
244 bool wxComboBox::Create(wxWindow *parent, wxWindowID id,
245 const wxString& value,
246 const wxPoint& pos,
247 const wxSize& size,
248 int n, const wxString choices[],
249 long style,
250 const wxValidator& validator,
251 const wxString& name)
252 {
253 // first create wxWin object
254 if ( !CreateControl(parent, id, pos, size, style, validator, name) )
255 return FALSE;
256
257 // get the right style
258 long msStyle = WS_TABSTOP | WS_VSCROLL | WS_HSCROLL |
259 CBS_AUTOHSCROLL | CBS_NOINTEGRALHEIGHT /* | WS_CLIPSIBLINGS */;
260 if ( style & wxCB_READONLY )
261 msStyle |= CBS_DROPDOWNLIST;
262 else if ( style & wxCB_SIMPLE )
263 msStyle |= CBS_SIMPLE; // A list (shown always) and edit control
264 else
265 msStyle |= CBS_DROPDOWN;
266
267 if ( style & wxCB_SORT )
268 msStyle |= CBS_SORT;
269
270 // and now create the MSW control
271 if ( !MSWCreateControl(_T("COMBOBOX"), msStyle) )
272 return FALSE;
273
274 SetSize(pos.x, pos.y, size.x, size.y);
275
276 // A choice/combobox normally has a white background (or other, depending
277 // on global settings) rather than inheriting the parent's background colour.
278 SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_WINDOW));
279
280 for ( int i = 0; i < n; i++ )
281 {
282 Append(choices[i]);
283 }
284
285 if ( !value.IsEmpty() )
286 {
287 SetValue(value);
288 }
289
290 // a (not read only) combobox is, in fact, 2 controls: the combobox itself
291 // and an edit control inside it and if we want to catch events from this
292 // edit control, we must subclass it as well
293 if ( !(style & wxCB_READONLY) )
294 {
295 gs_wndprocEdit = (WXFARPROC)::SetWindowLong
296 (
297 (HWND)GetEditHWND(),
298 GWL_WNDPROC,
299 (LPARAM)wxComboEditWndProc
300 );
301 }
302
303 return TRUE;
304 }
305
306 // TODO: update and clear all this horrible mess (VZ)
307
308 void wxComboBox::SetValue(const wxString& value)
309 {
310 // If newlines are denoted by just 10, must stick 13 in front.
311 int singletons = 0;
312 int len = value.Length();
313 int i;
314 for (i = 0; i < len; i ++)
315 {
316 if ((i > 0) && (value[i] == 10) && (value[i-1] != 13))
317 singletons ++;
318 }
319 if (singletons > 0)
320 {
321 wxChar *tmp = new wxChar[len + singletons + 1];
322 int j = 0;
323 for (i = 0; i < len; i ++)
324 {
325 if ((i > 0) && (value[i] == 10) && (value[i-1] != 13))
326 {
327 tmp[j] = 13;
328 j ++;
329 }
330 tmp[j] = value[i];
331 j ++;
332 }
333 tmp[j] = 0;
334 SetWindowText(GetHwnd(), tmp);
335 delete[] tmp;
336 }
337 else
338 SetWindowText(GetHwnd(), value);
339 }
340
341 // Clipboard operations
342 void wxComboBox::Copy()
343 {
344 HWND hWnd = GetHwnd();
345 SendMessage(hWnd, WM_COPY, 0, 0L);
346 }
347
348 void wxComboBox::Cut()
349 {
350 HWND hWnd = GetHwnd();
351 SendMessage(hWnd, WM_CUT, 0, 0L);
352 }
353
354 void wxComboBox::Paste()
355 {
356 HWND hWnd = GetHwnd();
357 SendMessage(hWnd, WM_PASTE, 0, 0L);
358 }
359
360 void wxComboBox::SetEditable(bool editable)
361 {
362 // Can't implement in MSW?
363 // HWND hWnd = GetHwnd();
364 // SendMessage(hWnd, EM_SETREADONLY, (WPARAM)!editable, (LPARAM)0L);
365 }
366
367 void wxComboBox::SetInsertionPoint(long pos)
368 {
369 if ( GetWindowStyle() & wxCB_READONLY )
370 return;
371
372 #ifdef __WIN32__
373 HWND hWnd = GetHwnd();
374 ::SendMessage(hWnd, CB_SETEDITSEL, 0, MAKELPARAM(pos, pos));
375 HWND hEditWnd = (HWND) GetEditHWND() ;
376 if ( hEditWnd )
377 {
378 // Scroll insertion point into view
379 SendMessage(hEditWnd, EM_SCROLLCARET, (WPARAM)0, (LPARAM)0);
380 // Why is this necessary? (Copied from wxTextCtrl::SetInsertionPoint)
381 SendMessage(hEditWnd, EM_REPLACESEL, 0, (LPARAM)_T(""));
382 }
383 #endif // __WIN32__
384 }
385
386 void wxComboBox::SetInsertionPointEnd()
387 {
388 // setting insertion point doesn't make sense for read only comboboxes
389 if ( !(GetWindowStyle() & wxCB_READONLY) )
390 {
391 long pos = GetLastPosition();
392 SetInsertionPoint(pos);
393 }
394 }
395
396 long wxComboBox::GetInsertionPoint() const
397 {
398 #ifdef __WIN32__
399 DWORD Pos=(DWORD)SendMessage(GetHwnd(), CB_GETEDITSEL, 0, 0L);
400 return Pos&0xFFFF;
401 #else
402 return 0;
403 #endif
404 }
405
406 long wxComboBox::GetLastPosition() const
407 {
408 HWND hEditWnd = (HWND) GetEditHWND();
409
410 // Get number of characters in the last (only) line. We'll add this to the character
411 // index for the last line, 1st position.
412 int lineLength = (int)SendMessage(hEditWnd, EM_LINELENGTH, (WPARAM) 0, (LPARAM)0L);
413
414 return (long)(lineLength);
415 }
416
417 void wxComboBox::Replace(long from, long to, const wxString& value)
418 {
419 #if wxUSE_CLIPBOARD
420 HWND hWnd = GetHwnd();
421 long fromChar = from;
422 long toChar = to;
423
424 // Set selection and remove it
425 #ifdef __WIN32__
426 SendMessage(hWnd, CB_SETEDITSEL, fromChar, toChar);
427 #else
428 SendMessage(hWnd, CB_SETEDITSEL, (WPARAM)0, (LPARAM)MAKELONG(fromChar, toChar));
429 #endif
430 SendMessage(hWnd, WM_CUT, (WPARAM)0, (LPARAM)0);
431
432 // Now replace with 'value', by pasting.
433 wxSetClipboardData(wxDF_TEXT, (wxObject *)(const wxChar *)value, 0, 0);
434
435 // Paste into edit control
436 SendMessage(hWnd, WM_PASTE, (WPARAM)0, (LPARAM)0L);
437 #endif
438 }
439
440 void wxComboBox::Remove(long from, long to)
441 {
442 HWND hWnd = GetHwnd();
443 long fromChar = from;
444 long toChar = to;
445
446 // Cut all selected text
447 #ifdef __WIN32__
448 SendMessage(hWnd, CB_SETEDITSEL, fromChar, toChar);
449 #else
450 SendMessage(hWnd, CB_SETEDITSEL, (WPARAM)0, (LPARAM)MAKELONG(fromChar, toChar));
451 #endif
452 SendMessage(hWnd, WM_CUT, (WPARAM)0, (LPARAM)0);
453 }
454
455 void wxComboBox::SetSelection(long from, long to)
456 {
457 HWND hWnd = GetHwnd();
458 long fromChar = from;
459 long toChar = to;
460 // if from and to are both -1, it means
461 // (in wxWindows) that all text should be selected.
462 // This translates into Windows convention
463 if ((from == -1) && (to == -1))
464 {
465 fromChar = 0;
466 toChar = -1;
467 }
468
469 #ifdef __WIN32__
470 SendMessage(hWnd, CB_SETEDITSEL, (WPARAM)fromChar, (LPARAM)toChar);
471 // SendMessage(hWnd, EM_SCROLLCARET, (WPARAM)0, (LPARAM)0);
472 #else
473 // WPARAM is 0: selection is scrolled into view
474 SendMessage(hWnd, CB_SETEDITSEL, (WPARAM)0, (LPARAM)MAKELONG(fromChar, toChar));
475 #endif
476 }
477
478 void wxComboBox::DoMoveWindow(int x, int y, int width, int height)
479 {
480 // here is why this is necessary: if the width is negative, the combobox
481 // window proc makes the window of the size width*height instead of
482 // interpreting height in the usual manner (meaning the height of the drop
483 // down list - usually the height specified in the call to MoveWindow()
484 // will not change the height of combo box per se)
485 //
486 // this behaviour is not documented anywhere, but this is just how it is
487 // here (NT 4.4) and, anyhow, the check shouldn't hurt - however without
488 // the check, constraints/sizers using combos may break the height
489 // constraint will have not at all the same value as expected
490 if ( width < 0 )
491 return;
492
493 int cx, cy;
494 wxGetCharSize(GetHWND(), &cx, &cy, &GetFont());
495
496 // what should the height of the drop down list be? we choose 10 items by
497 // default and also 10 items max (if we always use n, the list will never
498 // have vertical scrollbar)
499 int n = GetCount();
500 if ( !n || (n > 10) )
501 n = 10;
502
503 height = (n + 1)* EDIT_HEIGHT_FROM_CHAR_HEIGHT(cy);
504
505 wxControl::DoMoveWindow(x, y, width, height);
506 }
507
508 wxSize wxComboBox::DoGetBestSize() const
509 {
510 // the choice calculates the horz size correctly, but not the vertical
511 // component: correct it
512 wxSize size = wxChoice::DoGetBestSize();
513
514 int cx, cy;
515 wxGetCharSize(GetHWND(), &cx, &cy, &GetFont());
516 size.y = EDIT_HEIGHT_FROM_CHAR_HEIGHT(cy);
517
518 return size;
519 }
520
521 #endif
522 // wxUSE_COMBOBOX
523