]> git.saurik.com Git - wxWidgets.git/blame - src/msw/combobox.cpp
Added chapter on collection and container classes to contents
[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$
8// Copyright: (c) Julian Smart and Markus Holzem
c085e333 9// Licence: wxWindows licence
2bda0e17
KB
10/////////////////////////////////////////////////////////////////////////////
11
f6bcfd97
BP
12// ============================================================================
13// declarations
14// ============================================================================
15
16// ----------------------------------------------------------------------------
17// headers
18// ----------------------------------------------------------------------------
19
2bda0e17
KB
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
af79c52d
VZ
31#if wxUSE_COMBOBOX
32
2bda0e17 33#ifndef WX_PRECOMP
af79c52d 34#include "wx/settings.h"
2bda0e17
KB
35#endif
36
2bda0e17 37#include "wx/combobox.h"
f6bcfd97 38#include "wx/brush.h"
2bda0e17
KB
39#include "wx/clipbrd.h"
40#include "wx/msw/private.h"
41
f6bcfd97
BP
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
2bda0e17 53IMPLEMENT_DYNAMIC_CLASS(wxComboBox, wxControl)
2bda0e17 54
f6bcfd97
BP
55// ----------------------------------------------------------------------------
56// function prototypes
57// ----------------------------------------------------------------------------
58
59LRESULT 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
69static WXFARPROC gs_wndprocEdit = (WXFARPROC)NULL;
70
71// ============================================================================
72// implementation
73// ============================================================================
74
75// ----------------------------------------------------------------------------
76// wnd proc for subclassed edit control
77// ----------------------------------------------------------------------------
78
79LRESULT 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
143WXHBRUSH 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
179bool 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
debe6624 196bool wxComboBox::MSWCommand(WXUINT param, WXWORD WXUNUSED(id))
2bda0e17 197{
cd0b1709 198 switch ( param )
8c1c5302 199 {
cd0b1709
VZ
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;
29006414 210
cd0b1709
VZ
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 }
29006414 220
cd0b1709
VZ
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;
2bda0e17
KB
224}
225
f6bcfd97
BP
226WXHWND 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
debe6624 244bool wxComboBox::Create(wxWindow *parent, wxWindowID id,
c085e333
VZ
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)
2bda0e17 252{
f6bcfd97
BP
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 }
c085e333 284
f6bcfd97
BP
285 if ( !value.IsEmpty() )
286 {
287 SetValue(value);
288 }
2bda0e17 289
f6bcfd97
BP
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 }
2bda0e17 302
f6bcfd97 303 return TRUE;
2bda0e17
KB
304}
305
f6bcfd97
BP
306// TODO: update and clear all this horrible mess (VZ)
307
2bda0e17
KB
308void 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 {
837e5743 321 wxChar *tmp = new wxChar[len + singletons + 1];
2bda0e17
KB
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;
4438caf4 334 SetWindowText(GetHwnd(), tmp);
2bda0e17
KB
335 delete[] tmp;
336 }
337 else
4438caf4 338 SetWindowText(GetHwnd(), value);
2bda0e17
KB
339}
340
341// Clipboard operations
1c4a764c 342void wxComboBox::Copy()
2bda0e17 343{
4438caf4 344 HWND hWnd = GetHwnd();
2bda0e17
KB
345 SendMessage(hWnd, WM_COPY, 0, 0L);
346}
347
1c4a764c 348void wxComboBox::Cut()
2bda0e17 349{
4438caf4 350 HWND hWnd = GetHwnd();
2bda0e17
KB
351 SendMessage(hWnd, WM_CUT, 0, 0L);
352}
353
1c4a764c 354void wxComboBox::Paste()
2bda0e17 355{
4438caf4 356 HWND hWnd = GetHwnd();
2bda0e17
KB
357 SendMessage(hWnd, WM_PASTE, 0, 0L);
358}
359
debe6624 360void wxComboBox::SetEditable(bool editable)
2bda0e17
KB
361{
362 // Can't implement in MSW?
4438caf4 363// HWND hWnd = GetHwnd();
2bda0e17
KB
364// SendMessage(hWnd, EM_SETREADONLY, (WPARAM)!editable, (LPARAM)0L);
365}
366
debe6624 367void wxComboBox::SetInsertionPoint(long pos)
2bda0e17 368{
2bda0e17 369#ifdef __WIN32__
f7703477
JS
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 }
2bda0e17 381#endif
2bda0e17
KB
382}
383
1c4a764c 384void wxComboBox::SetInsertionPointEnd()
2bda0e17 385{
2bda0e17
KB
386 long pos = GetLastPosition();
387 SetInsertionPoint(pos);
2bda0e17
KB
388}
389
1c4a764c 390long wxComboBox::GetInsertionPoint() const
2bda0e17 391{
f7703477
JS
392#ifdef __WIN32__
393 DWORD Pos=(DWORD)SendMessage(GetHwnd(), CB_GETEDITSEL, 0, 0L);
394 return Pos&0xFFFF;
395#else
396 return 0;
397#endif
2bda0e17
KB
398}
399
1c4a764c 400long wxComboBox::GetLastPosition() const
2bda0e17 401{
f7703477 402 HWND hEditWnd = (HWND) GetEditHWND();
4438caf4 403
f7703477 404 // Get number of characters in the last (only) line. We'll add this to the character
2bda0e17 405 // index for the last line, 1st position.
f7703477 406 int lineLength = (int)SendMessage(hEditWnd, EM_LINELENGTH, (WPARAM) 0, (LPARAM)0L);
2bda0e17 407
f7703477 408 return (long)(lineLength);
2bda0e17
KB
409}
410
debe6624 411void wxComboBox::Replace(long from, long to, const wxString& value)
2bda0e17 412{
47d67540 413#if wxUSE_CLIPBOARD
4438caf4 414 HWND hWnd = GetHwnd();
2bda0e17
KB
415 long fromChar = from;
416 long toChar = to;
4438caf4 417
2bda0e17
KB
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.
837e5743 427 wxSetClipboardData(wxDF_TEXT, (wxObject *)(const wxChar *)value, 0, 0);
2bda0e17
KB
428
429 // Paste into edit control
430 SendMessage(hWnd, WM_PASTE, (WPARAM)0, (LPARAM)0L);
431#endif
432}
433
debe6624 434void wxComboBox::Remove(long from, long to)
2bda0e17 435{
4438caf4 436 HWND hWnd = GetHwnd();
2bda0e17
KB
437 long fromChar = from;
438 long toChar = to;
4438caf4 439
2bda0e17
KB
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
debe6624 449void wxComboBox::SetSelection(long from, long to)
2bda0e17 450{
4438caf4 451 HWND hWnd = GetHwnd();
2bda0e17
KB
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 }
4438caf4 462
2bda0e17
KB
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
882a8f40 472void wxComboBox::DoMoveWindow(int x, int y, int width, int height)
4438caf4 473{
f6bcfd97
BP
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
882a8f40
VZ
487 int cx, cy;
488 wxGetCharSize(GetHWND(), &cx, &cy, &GetFont());
489
8def743e
VZ
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)
882a8f40 493 int n = GetCount();
f6bcfd97 494 if ( !n || (n > 10) )
8def743e 495 n = 10;
882a8f40 496
f6bcfd97 497 height = (n + 1)* EDIT_HEIGHT_FROM_CHAR_HEIGHT(cy);
882a8f40
VZ
498
499 wxControl::DoMoveWindow(x, y, width, height);
500}
501
502wxSize 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;
4438caf4
VZ
513}
514
2bda0e17 515#endif
47d67540 516 // wxUSE_COMBOBOX
2bda0e17 517