]> git.saurik.com Git - wxWidgets.git/blame - src/msw/choice.cpp
simplify code so it always returns the same object
[wxWidgets.git] / src / msw / choice.cpp
CommitLineData
2bda0e17 1/////////////////////////////////////////////////////////////////////////////
11e62fe6 2// Name: src/msw/choice.cpp
2bda0e17
KB
3// Purpose: wxChoice
4// Author: Julian Smart
8d99be5f 5// Modified by: Vadim Zeitlin to derive from wxChoiceBase
2bda0e17
KB
6// Created: 04/01/98
7// RCS-ID: $Id$
6c9a19aa 8// Copyright: (c) Julian Smart
65571936 9// Licence: wxWindows licence
2bda0e17
KB
10/////////////////////////////////////////////////////////////////////////////
11
8d99be5f
VZ
12// ============================================================================
13// declarations
14// ============================================================================
15
16// ----------------------------------------------------------------------------
17// headers
18// ----------------------------------------------------------------------------
19
2bda0e17
KB
20// For compilers that support precompilation, includes "wx.h".
21#include "wx/wxprec.h"
22
23#ifdef __BORLANDC__
8d99be5f 24 #pragma hdrstop
2bda0e17
KB
25#endif
26
3180bc0e 27#if wxUSE_CHOICE && !(defined(__SMARTPHONE__) && defined(__WXWINCE__))
1e6feb95 28
b36e08d0
WS
29#include "wx/choice.h"
30
2bda0e17 31#ifndef WX_PRECOMP
8d99be5f 32 #include "wx/utils.h"
2ec335db 33 #include "wx/app.h"
8d99be5f 34 #include "wx/log.h"
f6bcfd97 35 #include "wx/brush.h"
c7401637 36 #include "wx/settings.h"
2bda0e17
KB
37#endif
38
df74e2d2
VZ
39#include "wx/dynlib.h"
40
2bda0e17
KB
41#include "wx/msw/private.h"
42
8d99be5f
VZ
43// ============================================================================
44// implementation
45// ============================================================================
46
47// ----------------------------------------------------------------------------
48// creation
49// ----------------------------------------------------------------------------
50
51bool wxChoice::Create(wxWindow *parent,
52 wxWindowID id,
53 const wxPoint& pos,
54 const wxSize& size,
55 int n, const wxString choices[],
56 long style,
57 const wxValidator& validator,
58 const wxString& name)
2bda0e17 59{
f6bcfd97 60 // Experience shows that wxChoice vs. wxComboBox distinction confuses
8d99be5f
VZ
61 // quite a few people - try to help them
62 wxASSERT_MSG( !(style & wxCB_DROPDOWN) &&
63 !(style & wxCB_READONLY) &&
64 !(style & wxCB_SIMPLE),
9a83f860
VZ
65 wxT("this style flag is ignored by wxChoice, you ")
66 wxT("probably want to use a wxComboBox") );
2bda0e17 67
71e57cd6
VZ
68 return CreateAndInit(parent, id, pos, size, n, choices, style,
69 validator, name);
70}
71
cc61d2eb
VZ
72bool wxChoice::CreateAndInit(wxWindow *parent,
73 wxWindowID id,
71e57cd6 74 const wxPoint& pos,
3dfb79a6 75 const wxSize& size,
71e57cd6
VZ
76 int n, const wxString choices[],
77 long style,
78 const wxValidator& validator,
79 const wxString& name)
80{
81 // initialize wxControl
82 if ( !CreateControl(parent, id, pos, size, style, validator, name) )
02b7b6b0 83 return false;
2bda0e17 84
71e57cd6 85 // now create the real HWND
f31a4098 86 if ( !MSWCreateControl(wxT("COMBOBOX"), wxEmptyString, pos, size) )
02b7b6b0 87 return false;
71e57cd6
VZ
88
89
cc61d2eb 90 // initialize the controls contents
cd747e15 91 Append(n, choices);
2bda0e17 92
cc61d2eb 93 // and now we may finally size the control properly (if needed)
170acdc9 94 SetInitialSize(size);
cc61d2eb 95
02b7b6b0 96 return true;
2bda0e17
KB
97}
98
5de69dd3
VZ
99void wxChoice::SetLabel(const wxString& label)
100{
101 if ( FindString(label) == wxNOT_FOUND )
102 {
103 // unless we explicitly do this here, CB_GETCURSEL will continue to
104 // return the index of the previously selected item which will result
105 // in wrongly replacing the value being set now with the previously
106 // value if the user simply opens and closes (without selecting
107 // anything) the combobox popup
108 SetSelection(-1);
109 }
110
111 wxChoiceBase::SetLabel(label);
112}
113
584ad2a3
MB
114bool wxChoice::Create(wxWindow *parent,
115 wxWindowID id,
116 const wxPoint& pos,
117 const wxSize& size,
118 const wxArrayString& choices,
119 long style,
120 const wxValidator& validator,
121 const wxString& name)
122{
123 wxCArrayString chs(choices);
124 return Create(parent, id, pos, size, chs.GetCount(), chs.GetStrings(),
125 style, validator, name);
126}
127
4b17d2e3
DS
128bool wxChoice::MSWShouldPreProcessMessage(WXMSG *pMsg)
129{
130 MSG *msg = (MSG *) pMsg;
131
dc302518
DS
132 // if the dropdown list is visible, don't preprocess certain keys
133 if ( msg->message == WM_KEYDOWN
134 && (msg->wParam == VK_ESCAPE || msg->wParam == VK_RETURN) )
4b17d2e3
DS
135 {
136 if (::SendMessage(GetHwndOf(this), CB_GETDROPPEDSTATE, 0, 0))
137 {
138 return false;
139 }
140 }
141
142 return wxControl::MSWShouldPreProcessMessage(pMsg);
143}
144
71e57cd6
VZ
145WXDWORD wxChoice::MSWGetStyle(long style, WXDWORD *exstyle) const
146{
147 // we never have an external border
148 WXDWORD msStyle = wxControl::MSWGetStyle
149 (
150 (style & ~wxBORDER_MASK) | wxBORDER_NONE, exstyle
151 );
152
153 // WS_CLIPSIBLINGS is useful with wxChoice and doesn't seem to result in
154 // any problems
155 msStyle |= WS_CLIPSIBLINGS;
156
157 // wxChoice-specific styles
158 msStyle |= CBS_DROPDOWNLIST | WS_HSCROLL | WS_VSCROLL;
159 if ( style & wxCB_SORT )
160 msStyle |= CBS_SORT;
161
162 return msStyle;
163}
164
2ec335db
JS
165#ifndef EP_EDITTEXT
166 #define EP_EDITTEXT 1
167 #define ETS_NORMAL 1
168#endif
169
170wxVisualAttributes
171wxChoice::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant))
172{
173 // it is important to return valid values for all attributes from here,
174 // GetXXX() below rely on this
175 wxVisualAttributes attrs;
176
177 // FIXME: Use better dummy window?
178 wxWindow* wnd = wxTheApp->GetTopWindow();
8c0f0784
JS
179 if (!wnd)
180 return attrs;
2ec335db
JS
181
182 attrs.font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
183
184 // there doesn't seem to be any way to get the text colour using themes
185 // API: TMT_TEXTCOLOR doesn't work neither for EDIT nor COMBOBOX
186 attrs.colFg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
187
188 // NB: use EDIT, not COMBOBOX (the latter works in XP but not Vista)
189 attrs.colBg = wnd->MSWGetThemeColour(L"EDIT",
ce00f59b 190 EP_EDITTEXT,
2ec335db
JS
191 ETS_NORMAL,
192 ThemeColourBackground,
193 wxSYS_COLOUR_WINDOW);
194
195 return attrs;
196}
197
8ee9d618
VZ
198wxChoice::~wxChoice()
199{
a236aa20 200 Clear();
8ee9d618
VZ
201}
202
1b6010d8 203bool wxChoice::MSWGetComboBoxInfo(tagCOMBOBOXINFO* info) const
df74e2d2
VZ
204{
205 // TODO-Win9x: Get rid of this once we officially drop support for Win9x
206 // and just call the function directly.
207#if wxUSE_DYNLIB_CLASS
1b6010d8 208 typedef BOOL (WINAPI *GetComboBoxInfo_t)(HWND, tagCOMBOBOXINFO*);
df74e2d2
VZ
209 static GetComboBoxInfo_t s_pfnGetComboBoxInfo = NULL;
210 static bool s_triedToLoad = false;
211 if ( !s_triedToLoad )
212 {
213 s_triedToLoad = true;
214 wxLoadedDLL dllUser32("user32.dll");
215 wxDL_INIT_FUNC(s_pfn, GetComboBoxInfo, dllUser32);
216 }
217
218 if ( s_pfnGetComboBoxInfo )
219 return (*s_pfnGetComboBoxInfo)(GetHwnd(), info) != 0;
220#endif // wxUSE_DYNLIB_CLASS
221
222 return false;
223}
224
8d99be5f
VZ
225// ----------------------------------------------------------------------------
226// adding/deleting items to/from the list
227// ----------------------------------------------------------------------------
2bda0e17 228
a236aa20
VZ
229int wxChoice::DoInsertItems(const wxArrayStringsAdapter& items,
230 unsigned int pos,
231 void **clientData, wxClientDataType type)
8d99be5f 232{
a236aa20 233 MSWAllocStorage(items, CB_INITSTORAGE);
def6fb9b 234
a236aa20 235 const bool append = pos == GetCount();
2bda0e17 236
a236aa20
VZ
237 // use CB_ADDSTRING when appending at the end to make sure the control is
238 // resorted if it has wxCB_SORT style
239 const unsigned msg = append ? CB_ADDSTRING : CB_INSERTSTRING;
243dbf1a 240
a236aa20
VZ
241 if ( append )
242 pos = 0;
243
244 int n = wxNOT_FOUND;
245 const unsigned numItems = items.GetCount();
246 for ( unsigned i = 0; i < numItems; ++i )
71e57cd6 247 {
a236aa20
VZ
248 n = MSWInsertOrAppendItem(pos, items[i], msg);
249 if ( n == wxNOT_FOUND )
250 return n;
251
252 if ( !append )
253 pos++;
254
255 AssignNewItemClientData(n, clientData, i, type);
71e57cd6 256 }
243dbf1a 257
a236aa20
VZ
258 // we need to refresh our size in order to have enough space for the
259 // newly added items
260 if ( !IsFrozen() )
75004dfb 261 MSWUpdateDropDownHeight();
a236aa20 262
31582e4e 263 InvalidateBestSize();
a236aa20 264
243dbf1a
VZ
265 return n;
266}
267
a236aa20 268void wxChoice::DoDeleteOneItem(unsigned int n)
2bda0e17 269{
8228b893 270 wxCHECK_RET( IsValid(n), wxT("invalid item index in wxChoice::Delete") );
8d99be5f
VZ
271
272 SendMessage(GetHwnd(), CB_DELETESTRING, n, 0);
71e57cd6 273
e0e3a32d 274 if ( !IsFrozen() )
75004dfb 275 MSWUpdateDropDownHeight();
31582e4e
RD
276
277 InvalidateBestSize();
2bda0e17
KB
278}
279
a236aa20 280void wxChoice::DoClear()
8ee9d618 281{
8ee9d618 282 SendMessage(GetHwnd(), CB_RESETCONTENT, 0, 0);
71e57cd6 283
e0e3a32d 284 if ( !IsFrozen() )
75004dfb 285 MSWUpdateDropDownHeight();
31582e4e
RD
286
287 InvalidateBestSize();
8ee9d618
VZ
288}
289
8d99be5f
VZ
290// ----------------------------------------------------------------------------
291// selection
292// ----------------------------------------------------------------------------
2bda0e17 293
8d99be5f 294int wxChoice::GetSelection() const
6ba93d23
VZ
295{
296 // if m_lastAcceptedSelection is set, it means that the dropdown is
297 // currently shown and that we want to use the last "permanent" selection
298 // instead of whatever is under the mouse pointer currently
299 //
300 // otherwise, get the selection from the control
301 return m_lastAcceptedSelection == wxID_NONE ? GetCurrentSelection()
302 : m_lastAcceptedSelection;
303}
304
305int wxChoice::GetCurrentSelection() const
2bda0e17 306{
8d99be5f 307 return (int)SendMessage(GetHwnd(), CB_GETCURSEL, 0, 0);
2bda0e17
KB
308}
309
debe6624 310void wxChoice::SetSelection(int n)
2bda0e17 311{
8d99be5f
VZ
312 SendMessage(GetHwnd(), CB_SETCURSEL, n, 0);
313}
314
315// ----------------------------------------------------------------------------
316// string list functions
317// ----------------------------------------------------------------------------
318
aa61d352 319unsigned int wxChoice::GetCount() const
8d99be5f 320{
aa61d352 321 return (unsigned int)SendMessage(GetHwnd(), CB_GETCOUNT, 0, 0);
2bda0e17
KB
322}
323
11e62fe6 324int wxChoice::FindString(const wxString& s, bool bCase) const
2bda0e17
KB
325{
326#if defined(__WATCOMC__) && defined(__WIN386__)
8d99be5f
VZ
327 // For some reason, Watcom in WIN386 mode crashes in the CB_FINDSTRINGEXACT message.
328 // wxChoice::Do it the long way instead.
aa61d352
VZ
329 unsigned int count = GetCount();
330 for ( unsigned int i = 0; i < count; i++ )
8d99be5f
VZ
331 {
332 // as CB_FINDSTRINGEXACT is case insensitive, be case insensitive too
aa61d352 333 if (GetString(i).IsSameAs(s, bCase))
8d99be5f
VZ
334 return i;
335 }
336
337 return wxNOT_FOUND;
338#else // !Watcom
4cd1ed99
RN
339 //TODO: Evidently some MSW versions (all?) don't like empty strings
340 //passed to SendMessage, so we have to do it ourselves in that case
beedefb9 341 if ( s.empty() )
4cd1ed99 342 {
aa61d352
VZ
343 unsigned int count = GetCount();
344 for ( unsigned int i = 0; i < count; i++ )
11e62fe6 345 {
aa61d352 346 if (GetString(i).empty())
11e62fe6
WS
347 return i;
348 }
349
350 return wxNOT_FOUND;
351 }
352 else if (bCase)
353 {
354 // back to base class search for not native search type
355 return wxItemContainerImmutable::FindString( s, bCase );
4cd1ed99
RN
356 }
357 else
358 {
11e62fe6 359 int pos = (int)SendMessage(GetHwnd(), CB_FINDSTRINGEXACT,
017dc06b 360 (WPARAM)-1, wxMSW_CONV_LPARAM(s));
f31a4098 361
11e62fe6 362 return pos == LB_ERR ? wxNOT_FOUND : pos;
4cd1ed99 363 }
8d99be5f 364#endif // Watcom/!Watcom
2bda0e17
KB
365}
366
aa61d352 367void wxChoice::SetString(unsigned int n, const wxString& s)
6c8a980f 368{
8228b893 369 wxCHECK_RET( IsValid(n), wxT("invalid item index in wxChoice::SetString") );
2b5f62a0
VZ
370
371 // we have to delete and add back the string as there is no way to change a
372 // string in place
373
b4a11fe8
VZ
374 // we need to preserve the client data manually
375 void *oldData = NULL;
376 wxClientData *oldObjData = NULL;
377 if ( HasClientUntypedData() )
378 oldData = GetClientData(n);
379 else if ( HasClientObjectData() )
380 oldObjData = GetClientObject(n);
2b5f62a0 381
b281a923
VZ
382 // and also the selection if we're going to delete the item that was
383 // selected
384 const bool wasSelected = static_cast<int>(n) == GetSelection();
385
2b5f62a0 386 ::SendMessage(GetHwnd(), CB_DELETESTRING, n, 0);
017dc06b 387 ::SendMessage(GetHwnd(), CB_INSERTSTRING, n, wxMSW_CONV_LPARAM(s) );
2b5f62a0 388
b4a11fe8
VZ
389 // restore the client data
390 if ( oldData )
391 SetClientData(n, oldData);
392 else if ( oldObjData )
393 SetClientObject(n, oldObjData);
31582e4e 394
b281a923
VZ
395 // and the selection
396 if ( wasSelected )
397 SetSelection(n);
398
399 // the width could have changed so the best size needs to be recomputed
31582e4e 400 InvalidateBestSize();
6c8a980f
VZ
401}
402
aa61d352 403wxString wxChoice::GetString(unsigned int n) const
2bda0e17 404{
478cabab
VZ
405 int len = (int)::SendMessage(GetHwnd(), CB_GETLBTEXTLEN, n, 0);
406
6c8a980f 407 wxString str;
478cabab
VZ
408 if ( len != CB_ERR && len > 0 )
409 {
410 if ( ::SendMessage
411 (
412 GetHwnd(),
413 CB_GETLBTEXT,
414 n,
415 (LPARAM)(wxChar *)wxStringBuffer(str, len)
416 ) == CB_ERR )
417 {
f6bcfd97 418 wxLogLastError(wxT("SendMessage(CB_GETLBTEXT)"));
21d72d17 419 }
4438caf4 420 }
2bda0e17 421
4438caf4
VZ
422 return str;
423}
2bda0e17 424
8d99be5f
VZ
425// ----------------------------------------------------------------------------
426// client data
427// ----------------------------------------------------------------------------
428
aa61d352 429void wxChoice::DoSetItemClientData(unsigned int n, void* clientData)
8d99be5f 430{
2b5f62a0
VZ
431 if ( ::SendMessage(GetHwnd(), CB_SETITEMDATA,
432 n, (LPARAM)clientData) == CB_ERR )
8d99be5f 433 {
223d09f6 434 wxLogLastError(wxT("CB_SETITEMDATA"));
8d99be5f
VZ
435 }
436}
437
aa61d352 438void* wxChoice::DoGetItemClientData(unsigned int n) const
8d99be5f
VZ
439{
440 LPARAM rc = SendMessage(GetHwnd(), CB_GETITEMDATA, n, 0);
85a39457 441 if ( rc == CB_ERR && GetLastError() != ERROR_SUCCESS )
8d99be5f 442 {
223d09f6 443 wxLogLastError(wxT("CB_GETITEMDATA"));
8d99be5f
VZ
444
445 // unfortunately, there is no way to return an error code to the user
8ee9d618 446 rc = (LPARAM) NULL;
8d99be5f
VZ
447 }
448
449 return (void *)rc;
450}
451
8d99be5f 452// ----------------------------------------------------------------------------
75004dfb 453// wxMSW-specific geometry management
8d99be5f
VZ
454// ----------------------------------------------------------------------------
455
5d4ee50b
VZ
456namespace
457{
458
459// there is a difference between the height passed to CB_SETITEMHEIGHT and the
460// real height of the combobox; it is probably not constant for all Windows
461// versions/settings but right now I don't know how to find what it is so it is
462// temporarily hardcoded to its value under XP systems with normal fonts sizes
463const int COMBO_HEIGHT_ADJ = 6;
464
465} // anonymous namespace
466
75004dfb
VZ
467void wxChoice::MSWUpdateVisibleHeight()
468{
469 if ( m_heightOwn != wxDefaultCoord )
5d4ee50b
VZ
470 {
471 ::SendMessage(GetHwnd(), CB_SETITEMHEIGHT,
472 (WPARAM)-1, m_heightOwn - COMBO_HEIGHT_ADJ);
473 }
75004dfb
VZ
474}
475
476#if wxUSE_DEFERRED_SIZING
477void wxChoice::MSWEndDeferWindowPos()
478{
479 // we can only set the height of the choice itself now as it is reset to
480 // default every time the control is resized
481 MSWUpdateVisibleHeight();
482
483 wxChoiceBase::MSWEndDeferWindowPos();
484}
485#endif // wxUSE_DEFERRED_SIZING
486
487void wxChoice::MSWUpdateDropDownHeight()
71e57cd6 488{
e6968367 489 // be careful to not change the width here
75004dfb
VZ
490 DoSetSize(wxDefaultCoord, wxDefaultCoord, wxDefaultCoord, GetSize().y,
491 wxSIZE_USE_EXISTING);
71e57cd6
VZ
492}
493
18c50997
VZ
494void wxChoice::DoMoveWindow(int x, int y, int width, int height)
495{
496 // here is why this is necessary: if the width is negative, the combobox
497 // window proc makes the window of the size width*height instead of
498 // interpreting height in the usual manner (meaning the height of the drop
499 // down list - usually the height specified in the call to MoveWindow()
500 // will not change the height of combo box per se)
501 //
502 // this behaviour is not documented anywhere, but this is just how it is
503 // here (NT 4.4) and, anyhow, the check shouldn't hurt - however without
504 // the check, constraints/sizers using combos may break the height
505 // constraint will have not at all the same value as expected
506 if ( width < 0 )
507 return;
508
509 wxControl::DoMoveWindow(x, y, width, height);
510}
511
d99957b6
VZ
512void wxChoice::DoGetSize(int *w, int *h) const
513{
5d4ee50b
VZ
514 wxControl::DoGetSize(w, h);
515
94a77ff7
VZ
516 // this is weird: sometimes, the height returned by Windows is clearly the
517 // total height of the control including the drop down list -- but only
5d4ee50b
VZ
518 // sometimes, and sometimes it isn't so work around this here by using our
519 // own stored value if we have it
520 if ( h && m_heightOwn != wxDefaultCoord )
521 *h = m_heightOwn;
d99957b6
VZ
522}
523
4438caf4 524void wxChoice::DoSetSize(int x, int y,
d99957b6 525 int width, int height,
4438caf4
VZ
526 int sizeFlags)
527{
0772a898
VZ
528 const int heightBest = GetBestSize().y;
529
5d4ee50b 530 // we need the real height below so get the current one if it's not given
0772a898
VZ
531 if ( height == wxDefaultCoord )
532 {
533 // height not specified, use the same as before
534 DoGetSize(NULL, &height);
535 }
536 else if ( height == heightBest )
537 {
538 // we don't need to manually manage our height, let the system use the
539 // default one
540 m_heightOwn = wxDefaultCoord;
541 }
542 else // non-default height specified
d4445d24 543 {
75004dfb
VZ
544 // set our new own height but be careful not to make it too big: the
545 // native control apparently stores it as a single byte and so setting
546 // own height to 256 pixels results in default height being used (255
547 // is still ok)
75004dfb
VZ
548 m_heightOwn = height;
549
5d4ee50b
VZ
550 if ( m_heightOwn > UCHAR_MAX )
551 m_heightOwn = UCHAR_MAX;
552 // nor too small: see MSWUpdateVisibleHeight()
553 else if ( m_heightOwn < COMBO_HEIGHT_ADJ )
554 m_heightOwn = COMBO_HEIGHT_ADJ;
d4445d24 555 }
d4445d24 556
5d4ee50b
VZ
557
558 // the height which we must pass to Windows should be the total height of
559 // the control including the drop down list while the height given to us
560 // is, of course, just the height of the permanently visible part of it so
561 // add the drop down height to it
562
563 // don't make the drop down list too tall, arbitrarily limit it to 30
564 // items max and also don't make it too small if it's currently empty
565 size_t nItems = GetCount();
da89830a
JS
566 if (!HasFlag(wxCB_SIMPLE))
567 {
568 if ( !nItems )
569 nItems = 9;
570 else if ( nItems > 30 )
571 nItems = 30;
572 }
5d4ee50b
VZ
573
574 const int hItem = SendMessage(GetHwnd(), CB_GETITEMHEIGHT, 0, 0);
da89830a
JS
575 int heightWithItems = 0;
576 if (!HasFlag(wxCB_SIMPLE))
275c6430
DS
577 // The extra item (" + 1") is required to prevent a vertical
578 // scrollbar from appearing with comctl32.dll versions earlier
579 // than 6.0 (such as found in Win2k).
580 heightWithItems = height + hItem*(nItems + 1);
da89830a
JS
581 else
582 heightWithItems = SetHeightSimpleComboBox(nItems);
5d4ee50b
VZ
583
584
585 // do resize the native control
586 wxControl::DoSetSize(x, y, width, heightWithItems, sizeFlags);
587
d4445d24 588
75004dfb
VZ
589 // make the control itself of the requested height: notice that this
590 // must be done after changing its size or it has no effect (apparently
591 // the height is reset to default during the control layout) and that it's
4c51a665 592 // useless to do it when using the deferred sizing -- in this case it
75004dfb
VZ
593 // will be done from MSWEndDeferWindowPos()
594#if wxUSE_DEFERRED_SIZING
595 if ( m_pendingSize == wxDefaultSize )
596 {
597 // not using deferred sizing, update it immediately
598 MSWUpdateVisibleHeight();
599 }
600 else // in the middle of deferred sizing
601 {
602 // we need to report the size of the visible part of the control back
603 // in GetSize() and not height stored by DoSetSize() in m_pendingSize
5d4ee50b 604 m_pendingSize = wxSize(width, height);
75004dfb
VZ
605 }
606#else // !wxUSE_DEFERRED_SIZING
607 // always update the visible height immediately
608 MSWUpdateVisibleHeight();
609#endif // wxUSE_DEFERRED_SIZING
4438caf4 610}
2bda0e17 611
882a8f40 612wxSize wxChoice::DoGetBestSize() const
4438caf4 613{
7eb0acef 614 // The base version returns the size of the largest string
aa24f946 615 return GetSizeFromTextSize(wxChoiceBase::DoGetBestSize().x);
2bda0e17
KB
616}
617
da89830a
JS
618int wxChoice::SetHeightSimpleComboBox(int nItems) const
619{
620 int cx, cy;
621 wxGetCharSize( GetHWND(), &cx, &cy, GetFont() );
7fc1b0c7 622 int hItem = SendMessage(GetHwnd(), CB_GETITEMHEIGHT, (WPARAM)-1, 0);
da89830a
JS
623 return EDIT_HEIGHT_FROM_CHAR_HEIGHT( cy ) * wxMin( wxMax( nItems, 3 ), 6 ) + hItem - 1;
624}
625
aa24f946
VZ
626wxSize wxChoice::DoGetSizeFromTextSize(int xlen, int ylen) const
627{
628 int cHeight = GetCharHeight();
629
630 // We are interested in the difference of sizes between the whole control
631 // and its child part. I.e. arrow, separators, etc.
632 wxSize tsize(xlen, 0);
633
7bc572ec
VZ
634 // FIXME-VC6: Only VC6 needs this guard, see WINVER definition in
635 // include/wx/msw/wrapwin.h
636#if defined(WINVER) && WINVER >= 0x0500
aa24f946
VZ
637 WinStruct<COMBOBOXINFO> info;
638 if ( MSWGetComboBoxInfo(&info) )
639 {
640 tsize.x += info.rcItem.left + info.rcButton.right - info.rcItem.right
641 + info.rcItem.left + 3; // right and extra margins
642 }
643 else // Just use some rough approximation.
7bc572ec 644#endif // WINVER >= 0x0500
aa24f946
VZ
645 {
646 tsize.x += 4*cHeight;
647 }
648
649 // set height on our own
650 if( HasFlag( wxCB_SIMPLE ) )
651 tsize.y = SetHeightSimpleComboBox(GetCount());
652 else
653 tsize.y = EDIT_HEIGHT_FROM_CHAR_HEIGHT(cHeight);
654
655 // Perhaps the user wants something different from CharHeight
656 if ( ylen > 0 )
657 tsize.IncBy(0, ylen - cHeight);
658
659 return tsize;
660}
661
a24fb9f8
VZ
662// ----------------------------------------------------------------------------
663// Popup operations
664// ----------------------------------------------------------------------------
665
666void wxChoice::MSWDoPopupOrDismiss(bool show)
667{
668 wxASSERT_MSG( !HasFlag(wxCB_SIMPLE),
669 wxT("can't popup/dismiss the list for simple combo box") );
670
671 // we *must* set focus to the combobox before showing or hiding the drop
672 // down as without this we get WM_LBUTTONDOWN messages with invalid HWND
673 // when hiding it (whether programmatically or manually) resulting in a
674 // crash when we pass them to IsDialogMessage()
675 //
676 // this can be seen in the combo page of the widgets sample under Windows 7
677 SetFocus();
678
679 ::SendMessage(GetHwnd(), CB_SHOWDROPDOWN, show, 0);
680}
681
e911dd0a
VZ
682bool wxChoice::Show(bool show)
683{
684 if ( !wxChoiceBase::Show(show) )
685 return false;
686
687 // When hiding the combobox, we also need to hide its popup part as it
688 // doesn't happen automatically.
689 if ( !show && ::SendMessage(GetHwnd(), CB_GETDROPPEDSTATE, 0, 0) )
690 MSWDoPopupOrDismiss(false);
691
692 return true;
693}
694
75004dfb
VZ
695// ----------------------------------------------------------------------------
696// MSW message handlers
697// ----------------------------------------------------------------------------
698
c140b7e7 699WXLRESULT wxChoice::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
2bda0e17 700{
f499e614 701 switch ( nMsg )
2bda0e17 702 {
f499e614
VZ
703 case WM_LBUTTONUP:
704 {
705 int x = (int)LOWORD(lParam);
706 int y = (int)HIWORD(lParam);
707
708 // Ok, this is truly weird, but if a panel with a wxChoice
709 // loses the focus, then you get a *fake* WM_LBUTTONUP message
710 // with x = 65535 and y = 65535. Filter out this nonsense.
711 //
712 // VZ: I'd like to know how to reproduce this please...
713 if ( x == 65535 && y == 65535 )
714 return 0;
715 }
716 break;
717
718 // we have to handle both: one for the normal case and the other
719 // for readonly
720 case WM_CTLCOLOREDIT:
721 case WM_CTLCOLORLISTBOX:
722 case WM_CTLCOLORSTATIC:
723 {
f499e614
VZ
724 WXHDC hdc;
725 WXHWND hwnd;
9f368d0d 726 UnpackCtlColor(wParam, lParam, &hdc, &hwnd);
f499e614 727
2bae4332 728 WXHBRUSH hbr = MSWControlColor((WXHDC)hdc, hwnd);
48fa6bd3
VZ
729 if ( hbr )
730 return (WXLRESULT)hbr;
731 //else: fall through to default window proc
f499e614 732 }
2bda0e17
KB
733 }
734
8d99be5f 735 return wxWindow::MSWWindowProc(nMsg, wParam, lParam);
2bda0e17
KB
736}
737
8d99be5f 738bool wxChoice::MSWCommand(WXUINT param, WXWORD WXUNUSED(id))
2bda0e17 739{
c11f0412
VZ
740 /*
741 The native control provides a great variety in the events it sends in
742 the different selection scenarios (undoubtedly for greater amusement of
743 the programmers using it). For the reference, here are the cases when
744 the final selection is accepted (things are quite interesting when it
745 is cancelled too):
746
747 A. Selecting with just the arrows without opening the dropdown:
748 1. CBN_SELENDOK
749 2. CBN_SELCHANGE
750
751 B. Opening dropdown with F4 and selecting with arrows:
752 1. CBN_DROPDOWN
753 2. many CBN_SELCHANGE while changing selection in the list
754 3. CBN_SELENDOK
755 4. CBN_CLOSEUP
756
757 C. Selecting with the mouse:
758 1. CBN_DROPDOWN
759 -- no intermediate CBN_SELCHANGEs --
760 2. CBN_SELENDOK
761 3. CBN_CLOSEUP
762 4. CBN_SELCHANGE
763
764 Admire the different order of messages in all of those cases, it must
765 surely have taken a lot of effort to Microsoft developers to achieve
766 such originality.
767 */
6ba93d23 768 switch ( param )
2bda0e17 769 {
6ba93d23 770 case CBN_DROPDOWN:
c11f0412
VZ
771 // we use this value both because we don't want to track selection
772 // using CB_GETCURSEL while the dropdown is opened and because we
773 // need to reset the selection back to it if it's eventually
774 // cancelled by user
6ba93d23
VZ
775 m_lastAcceptedSelection = GetCurrentSelection();
776 break;
2bda0e17 777
6ba93d23 778 case CBN_CLOSEUP:
c11f0412
VZ
779 // if the selection was accepted by the user, it should have been
780 // reset to wxID_NONE by CBN_SELENDOK, otherwise the selection was
781 // cancelled and we must restore the old one
782 if ( m_lastAcceptedSelection != wxID_NONE )
783 {
784 SetSelection(m_lastAcceptedSelection);
785 m_lastAcceptedSelection = wxID_NONE;
786 }
6ba93d23
VZ
787 break;
788
c11f0412
VZ
789 case CBN_SELENDOK:
790 // reset it to prevent CBN_CLOSEUP from undoing the selection (it's
791 // ok to reset it now as GetCurrentSelection() will now return the
792 // same thing anyhow)
793 m_lastAcceptedSelection = wxID_NONE;
794
6ba93d23
VZ
795 {
796 const int n = GetSelection();
797
798 wxCommandEvent event(wxEVT_COMMAND_CHOICE_SELECTED, m_windowId);
799 event.SetInt(n);
800 event.SetEventObject(this);
801
802 if ( n > -1 )
803 {
804 event.SetString(GetStringSelection());
593ac33e 805 InitCommandEventWithItems(event, n);
6ba93d23
VZ
806 }
807
808 ProcessCommand(event);
809 }
c11f0412
VZ
810 break;
811
812 // don't handle CBN_SELENDCANCEL: just leave m_lastAcceptedSelection
813 // valid and the selection will be undone in CBN_CLOSEUP above
814
815 // don't handle CBN_SELCHANGE neither, we don't want to generate events
816 // while the dropdown is opened -- but do add it if we ever need this
817
818 default:
819 return false;
8c1c5302 820 }
2bda0e17 821
c11f0412 822 return true;
8d99be5f 823}
2bda0e17 824
2bae4332 825WXHBRUSH wxChoice::MSWControlColor(WXHDC hDC, WXHWND hWnd)
f6bcfd97 826{
6c6b9383 827 if ( !IsThisEnabled() )
48fa6bd3 828 return MSWControlColorDisabled(hDC);
f6bcfd97 829
2bae4332 830 return wxChoiceBase::MSWControlColor(hDC, hWnd);
f6bcfd97
BP
831}
832
3180bc0e 833#endif // wxUSE_CHOICE && !(__SMARTPHONE__ && __WXWINCE__)