]> git.saurik.com Git - wxWidgets.git/blob - src/msw/combobox.cpp
Added first stab at GetBoundingRect for generic tree control
[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 #ifdef __WIN32__
370 HWND hWnd = GetHwnd();
371 SendMessage(hWnd, CB_SETEDITSEL, 0, MAKELPARAM(pos, pos));
372 HWND hEditWnd = (HWND) GetEditHWND() ;
373 if (hEditWnd)
374 {
375 // Scroll insertion point into view
376 SendMessage(hEditWnd, EM_SCROLLCARET, (WPARAM)0, (LPARAM)0);
377 // Why is this necessary? (Copied from wxTextCtrl::SetInsertionPoint)
378 static const wxChar *nothing = _T("");
379 SendMessage(hEditWnd, EM_REPLACESEL, 0, (LPARAM)nothing);
380 }
381 #endif
382 }
383
384 void wxComboBox::SetInsertionPointEnd()
385 {
386 long pos = GetLastPosition();
387 SetInsertionPoint(pos);
388 }
389
390 long wxComboBox::GetInsertionPoint() const
391 {
392 #ifdef __WIN32__
393 DWORD Pos=(DWORD)SendMessage(GetHwnd(), CB_GETEDITSEL, 0, 0L);
394 return Pos&0xFFFF;
395 #else
396 return 0;
397 #endif
398 }
399
400 long wxComboBox::GetLastPosition() const
401 {
402 HWND hEditWnd = (HWND) GetEditHWND();
403
404 // Get number of characters in the last (only) line. We'll add this to the character
405 // index for the last line, 1st position.
406 int lineLength = (int)SendMessage(hEditWnd, EM_LINELENGTH, (WPARAM) 0, (LPARAM)0L);
407
408 return (long)(lineLength);
409 }
410
411 void wxComboBox::Replace(long from, long to, const wxString& value)
412 {
413 #if wxUSE_CLIPBOARD
414 HWND hWnd = GetHwnd();
415 long fromChar = from;
416 long toChar = to;
417
418 // Set selection and remove it
419 #ifdef __WIN32__
420 SendMessage(hWnd, CB_SETEDITSEL, fromChar, toChar);
421 #else
422 SendMessage(hWnd, CB_SETEDITSEL, (WPARAM)0, (LPARAM)MAKELONG(fromChar, toChar));
423 #endif
424 SendMessage(hWnd, WM_CUT, (WPARAM)0, (LPARAM)0);
425
426 // Now replace with 'value', by pasting.
427 wxSetClipboardData(wxDF_TEXT, (wxObject *)(const wxChar *)value, 0, 0);
428
429 // Paste into edit control
430 SendMessage(hWnd, WM_PASTE, (WPARAM)0, (LPARAM)0L);
431 #endif
432 }
433
434 void wxComboBox::Remove(long from, long to)
435 {
436 HWND hWnd = GetHwnd();
437 long fromChar = from;
438 long toChar = to;
439
440 // Cut all selected text
441 #ifdef __WIN32__
442 SendMessage(hWnd, CB_SETEDITSEL, fromChar, toChar);
443 #else
444 SendMessage(hWnd, CB_SETEDITSEL, (WPARAM)0, (LPARAM)MAKELONG(fromChar, toChar));
445 #endif
446 SendMessage(hWnd, WM_CUT, (WPARAM)0, (LPARAM)0);
447 }
448
449 void wxComboBox::SetSelection(long from, long to)
450 {
451 HWND hWnd = GetHwnd();
452 long fromChar = from;
453 long toChar = to;
454 // if from and to are both -1, it means
455 // (in wxWindows) that all text should be selected.
456 // This translates into Windows convention
457 if ((from == -1) && (to == -1))
458 {
459 fromChar = 0;
460 toChar = -1;
461 }
462
463 #ifdef __WIN32__
464 SendMessage(hWnd, CB_SETEDITSEL, (WPARAM)fromChar, (LPARAM)toChar);
465 // SendMessage(hWnd, EM_SCROLLCARET, (WPARAM)0, (LPARAM)0);
466 #else
467 // WPARAM is 0: selection is scrolled into view
468 SendMessage(hWnd, CB_SETEDITSEL, (WPARAM)0, (LPARAM)MAKELONG(fromChar, toChar));
469 #endif
470 }
471
472 void wxComboBox::DoMoveWindow(int x, int y, int width, int height)
473 {
474 // here is why this is necessary: if the width is negative, the combobox
475 // window proc makes the window of the size width*height instead of
476 // interpreting height in the usual manner (meaning the height of the drop
477 // down list - usually the height specified in the call to MoveWindow()
478 // will not change the height of combo box per se)
479 //
480 // this behaviour is not documented anywhere, but this is just how it is
481 // here (NT 4.4) and, anyhow, the check shouldn't hurt - however without
482 // the check, constraints/sizers using combos may break the height
483 // constraint will have not at all the same value as expected
484 if ( width < 0 )
485 return;
486
487 int cx, cy;
488 wxGetCharSize(GetHWND(), &cx, &cy, &GetFont());
489
490 // what should the height of the drop down list be? we choose 10 items by
491 // default and also 10 items max (if we always use n, the list will never
492 // have vertical scrollbar)
493 int n = GetCount();
494 if ( !n || (n > 10) )
495 n = 10;
496
497 height = (n + 1)* EDIT_HEIGHT_FROM_CHAR_HEIGHT(cy);
498
499 wxControl::DoMoveWindow(x, y, width, height);
500 }
501
502 wxSize wxComboBox::DoGetBestSize() const
503 {
504 // the choice calculates the horz size correctly, but not the vertical
505 // component: correct it
506 wxSize size = wxChoice::DoGetBestSize();
507
508 int cx, cy;
509 wxGetCharSize(GetHWND(), &cx, &cy, &GetFont());
510 size.y = EDIT_HEIGHT_FROM_CHAR_HEIGHT(cy);
511
512 return size;
513 }
514
515 #endif
516 // wxUSE_COMBOBOX
517