]> git.saurik.com Git - wxWidgets.git/blob - src/msw/combobox.cpp
Added some window style metadata
[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
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
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 #include "wx/log.h"
36 // for wxEVT_COMMAND_TEXT_ENTER
37 #include "wx/textctrl.h"
38 #endif
39
40 #include "wx/combobox.h"
41 #include "wx/brush.h"
42 #include "wx/clipbrd.h"
43 #include "wx/msw/private.h"
44
45 #if wxUSE_TOOLTIPS
46 #if !defined(__GNUWIN32_OLD__) || defined(__CYGWIN10__)
47 #include <commctrl.h>
48 #endif
49 #include "wx/tooltip.h"
50 #endif // wxUSE_TOOLTIPS
51
52 // ----------------------------------------------------------------------------
53 // wxWin macros
54 // ----------------------------------------------------------------------------
55
56 #if wxUSE_EXTENDED_RTTI
57 WX_DEFINE_FLAGS( wxComboBoxStyle )
58
59 WX_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
92 WX_END_FLAGS( wxComboBoxStyle )
93
94 IMPLEMENT_DYNAMIC_CLASS_XTI(wxComboBox, wxControl,"wx/combobox.h")
95
96 WX_BEGIN_PROPERTIES_TABLE(wxComboBox)
97 // TODO DELEGATES
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"))
102 WX_PROPERTY_FLAGS( WindowStyle , wxComboBoxStyle , long , SetWindowStyleFlag , GetWindowStyleFlag , , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
103 WX_END_PROPERTIES_TABLE()
104
105 WX_BEGIN_HANDLERS_TABLE(wxComboBox)
106 WX_END_HANDLERS_TABLE()
107
108 WX_CONSTRUCTOR_5( wxComboBox , wxWindow* , Parent , wxWindowID , Id , wxString , Value , wxPoint , Position , wxSize , Size )
109 #else
110 IMPLEMENT_DYNAMIC_CLASS(wxComboBox, wxControl)
111 #endif
112
113 // ----------------------------------------------------------------------------
114 // function prototypes
115 // ----------------------------------------------------------------------------
116
117 LRESULT 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
127 static WXFARPROC gs_wndprocEdit = (WXFARPROC)NULL;
128
129 // ============================================================================
130 // implementation
131 // ============================================================================
132
133 // ----------------------------------------------------------------------------
134 // wnd proc for subclassed edit control
135 // ----------------------------------------------------------------------------
136
137 LRESULT 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 {
147 // forward some messages to the combobox to generate the appropriate
148 // wxEvents from them
149 case WM_KEYUP:
150 case WM_KEYDOWN:
151 case WM_CHAR:
152 case WM_SETFOCUS:
153 case WM_KILLFOCUS:
154 {
155 wxComboBox *combo = wxDynamicCast(win, wxComboBox);
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
169 return 0;
170 }
171 }
172 break;
173
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;
185
186 // deal with tooltips here
187 #if wxUSE_TOOLTIPS && defined(TTN_NEEDTEXT)
188 case WM_NOTIFY:
189 {
190 wxCHECK_MSG( win, 0, _T("should have a parent") );
191
192 NMHDR* hdr = (NMHDR *)lParam;
193 if ( hdr->code == TTN_NEEDTEXT )
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
213 WXHBRUSH wxComboBox::OnCtlColor(WXHDC pDC, WXHWND WXUNUSED(pWnd), WXUINT WXUNUSED(nCtlColor),
214 WXUINT WXUNUSED(message),
215 WXWPARAM WXUNUSED(wParam),
216 WXLPARAM WXUNUSED(lParam)
217 )
218 {
219 HDC hdc = (HDC)pDC;
220 wxColour colBack = GetBackgroundColour();
221
222 if (!IsEnabled())
223 colBack = wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE);
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
237 bool wxComboBox::MSWProcessEditMsg(WXUINT msg, WXWPARAM wParam, WXLPARAM lParam)
238 {
239 switch ( msg )
240 {
241 case WM_CHAR:
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
253 return HandleChar(wParam, lParam, TRUE /* isASCII */);
254
255 case WM_KEYDOWN:
256 return HandleKeyDown(wParam, lParam);
257
258 case WM_KEYUP:
259 return HandleKeyUp(wParam, lParam);
260
261 case WM_SETFOCUS:
262 return HandleSetFocus((WXHWND)wParam);
263
264 case WM_KILLFOCUS:
265 return HandleKillFocus((WXHWND)wParam);
266 }
267
268 return FALSE;
269 }
270
271 bool wxComboBox::MSWCommand(WXUINT param, WXWORD WXUNUSED(id))
272 {
273 wxString value;
274 int sel = -1;
275 switch ( param )
276 {
277 case CBN_SELCHANGE:
278 sel = GetSelection();
279 if ( sel > -1 )
280 {
281 value = GetString(sel);
282
283 wxCommandEvent event(wxEVT_COMMAND_COMBOBOX_SELECTED, GetId());
284 event.SetInt(sel);
285 event.SetEventObject(this);
286 event.SetString(value);
287 ProcessCommand(event);
288 }
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)
297
298 case CBN_EDITCHANGE:
299 {
300 wxCommandEvent event(wxEVT_COMMAND_TEXT_UPDATED, GetId());
301
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)
305 if ( sel == -1 )
306 {
307 value = GetValue();
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
317 event.SetString(value);
318 event.SetEventObject(this);
319 ProcessCommand(event);
320 }
321 break;
322 }
323
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;
327 }
328
329 WXHWND 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
347 bool wxComboBox::Create(wxWindow *parent, wxWindowID id,
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)
355 {
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
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;
370 #ifndef __WXWINCE__
371 else if ( style & wxCB_SIMPLE )
372 msStyle |= CBS_SIMPLE; // A list (shown always) and edit control
373 #endif
374 else
375 msStyle |= CBS_DROPDOWN;
376
377 if ( style & wxCB_SORT )
378 msStyle |= CBS_SORT;
379
380 if ( style & wxCLIP_SIBLINGS )
381 msStyle |= WS_CLIPSIBLINGS;
382
383
384 // and now create the MSW control
385 if ( !MSWCreateControl(_T("COMBOBOX"), msStyle) )
386 return FALSE;
387
388 // A choice/combobox normally has a white background (or other, depending
389 // on global settings) rather than inheriting the parent's background colour.
390 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
391
392 for ( int i = 0; i < n; i++ )
393 {
394 Append(choices[i]);
395 }
396
397 if ( !value.IsEmpty() )
398 {
399 SetValue(value);
400 }
401
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
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 }
418
419 // and finally, show the control
420 Show(TRUE);
421
422 return TRUE;
423 }
424
425 void wxComboBox::SetValue(const wxString& value)
426 {
427 if ( HasFlag(wxCB_READONLY) )
428 SetStringSelection(value);
429 else
430 SetWindowText(GetHwnd(), value.c_str());
431 }
432
433 // Clipboard operations
434 void wxComboBox::Copy()
435 {
436 SendMessage(GetHwnd(), WM_COPY, 0, 0L);
437 }
438
439 void wxComboBox::Cut()
440 {
441 SendMessage(GetHwnd(), WM_CUT, 0, 0L);
442 }
443
444 void wxComboBox::Paste()
445 {
446 SendMessage(GetHwnd(), WM_PASTE, 0, 0L);
447 }
448
449 void wxComboBox::SetEditable(bool WXUNUSED(editable))
450 {
451 // Can't implement in MSW?
452 // HWND hWnd = GetHwnd();
453 // SendMessage(hWnd, EM_SETREADONLY, (WPARAM)!editable, (LPARAM)0L);
454 }
455
456 void wxComboBox::SetInsertionPoint(long pos)
457 {
458 if ( GetWindowStyle() & wxCB_READONLY )
459 return;
460
461 #ifdef __WIN32__
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)
470 SendMessage(hEditWnd, EM_REPLACESEL, 0, (LPARAM) wxEmptyString);
471 }
472 #endif // __WIN32__
473 }
474
475 void wxComboBox::SetInsertionPointEnd()
476 {
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 }
483 }
484
485 long wxComboBox::GetInsertionPoint() const
486 {
487 #ifdef __WIN32__
488 DWORD Pos=(DWORD)SendMessage(GetHwnd(), CB_GETEDITSEL, 0, 0L);
489 return Pos&0xFFFF;
490 #else
491 return 0;
492 #endif
493 }
494
495 long wxComboBox::GetLastPosition() const
496 {
497 HWND hEditWnd = (HWND) GetEditHWND();
498
499 // Get number of characters in the last (only) line. We'll add this to the character
500 // index for the last line, 1st position.
501 int lineLength = (int)SendMessage(hEditWnd, EM_LINELENGTH, (WPARAM) 0, (LPARAM)0L);
502
503 return (long)(lineLength);
504 }
505
506 void wxComboBox::Replace(long from, long to, const wxString& value)
507 {
508 #if wxUSE_CLIPBOARD
509 Remove(from, to);
510
511 // Now replace with 'value', by pasting.
512 wxSetClipboardData(wxDF_TEXT, (wxObject *)(const wxChar *)value, 0, 0);
513
514 // Paste into edit control
515 SendMessage(GetHwnd(), WM_PASTE, (WPARAM)0, (LPARAM)0L);
516 #endif
517 }
518
519 void wxComboBox::Remove(long from, long to)
520 {
521 // Set selection and remove it
522 SetSelection(from, to);
523 SendMessage(GetHwnd(), WM_CUT, (WPARAM)0, (LPARAM)0);
524 }
525
526 void wxComboBox::SetSelection(long from, long to)
527 {
528 HWND hWnd = GetHwnd();
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 }
539
540 if (
541 #ifdef __WIN32__
542 SendMessage(hWnd, CB_SETEDITSEL, (WPARAM)0, (LPARAM)MAKELONG(fromChar, toChar))
543 #else // Win16
544 SendMessage(hWnd, CB_SETEDITSEL, (WPARAM)fromChar, (LPARAM)toChar)
545 #endif
546 == CB_ERR )
547 {
548 wxLogDebug(_T("CB_SETEDITSEL failed"));
549 }
550 }
551
552 #endif
553 // wxUSE_COMBOBOX
554