]> git.saurik.com Git - wxWidgets.git/blame - src/msw/choice.cpp
don't create m_dottedPen with an invalid 0 style which results in an assert under MSW
[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
VZ
32 #include "wx/utils.h"
33 #include "wx/log.h"
f6bcfd97 34 #include "wx/brush.h"
c7401637 35 #include "wx/settings.h"
2bda0e17
KB
36#endif
37
38#include "wx/msw/private.h"
39
6a89f9ee 40#if wxUSE_EXTENDED_RTTI
bc9fb572
JS
41WX_DEFINE_FLAGS( wxChoiceStyle )
42
3ff066a4 43wxBEGIN_FLAGS( wxChoiceStyle )
bc9fb572
JS
44 // new style border flags, we put them first to
45 // use them for streaming out
3ff066a4
SC
46 wxFLAGS_MEMBER(wxBORDER_SIMPLE)
47 wxFLAGS_MEMBER(wxBORDER_SUNKEN)
48 wxFLAGS_MEMBER(wxBORDER_DOUBLE)
49 wxFLAGS_MEMBER(wxBORDER_RAISED)
50 wxFLAGS_MEMBER(wxBORDER_STATIC)
51 wxFLAGS_MEMBER(wxBORDER_NONE)
3dfb79a6 52
bc9fb572 53 // old style border flags
3ff066a4
SC
54 wxFLAGS_MEMBER(wxSIMPLE_BORDER)
55 wxFLAGS_MEMBER(wxSUNKEN_BORDER)
56 wxFLAGS_MEMBER(wxDOUBLE_BORDER)
57 wxFLAGS_MEMBER(wxRAISED_BORDER)
58 wxFLAGS_MEMBER(wxSTATIC_BORDER)
cb0afb26 59 wxFLAGS_MEMBER(wxBORDER)
bc9fb572
JS
60
61 // standard window styles
3ff066a4
SC
62 wxFLAGS_MEMBER(wxTAB_TRAVERSAL)
63 wxFLAGS_MEMBER(wxCLIP_CHILDREN)
64 wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW)
65 wxFLAGS_MEMBER(wxWANTS_CHARS)
cb0afb26 66 wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE)
3ff066a4
SC
67 wxFLAGS_MEMBER(wxALWAYS_SHOW_SB )
68 wxFLAGS_MEMBER(wxVSCROLL)
69 wxFLAGS_MEMBER(wxHSCROLL)
bc9fb572 70
3ff066a4 71wxEND_FLAGS( wxChoiceStyle )
bc9fb572 72
0ad2a19e 73IMPLEMENT_DYNAMIC_CLASS_XTI(wxChoice, wxControlWithItems,"wx/choice.h")
6a89f9ee 74
3ff066a4 75wxBEGIN_PROPERTIES_TABLE(wxChoice)
3dfb79a6 76 wxEVENT_PROPERTY( Select , wxEVT_COMMAND_CHOICE_SELECTED , wxCommandEvent )
c5ca409b 77
af498247 78 wxPROPERTY( Font , wxFont , SetFont , GetFont , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
3ff066a4 79 wxPROPERTY_COLLECTION( Choices , wxArrayString , wxString , AppendString , GetStrings , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
3dfb79a6 80 wxPROPERTY( Selection ,int, SetSelection, GetSelection, EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
af498247 81 wxPROPERTY_FLAGS( WindowStyle , wxChoiceStyle , long , SetWindowStyleFlag , GetWindowStyleFlag , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
3ff066a4 82wxEND_PROPERTIES_TABLE()
6a89f9ee 83
3ff066a4
SC
84wxBEGIN_HANDLERS_TABLE(wxChoice)
85wxEND_HANDLERS_TABLE()
2bda0e17 86
3dfb79a6 87wxCONSTRUCTOR_4( wxChoice , wxWindow* , Parent , wxWindowID , Id , wxPoint , Position , wxSize , Size )
6a89f9ee 88#else
0ad2a19e 89IMPLEMENT_DYNAMIC_CLASS(wxChoice, wxControlWithItems)
6a89f9ee 90#endif
066f1b7a 91/*
3dfb79a6
VZ
92 TODO PROPERTIES
93 selection (long)
94 content (list)
95 item
066f1b7a
SC
96*/
97
8d99be5f
VZ
98// ============================================================================
99// implementation
100// ============================================================================
101
102// ----------------------------------------------------------------------------
103// creation
104// ----------------------------------------------------------------------------
105
106bool wxChoice::Create(wxWindow *parent,
107 wxWindowID id,
108 const wxPoint& pos,
109 const wxSize& size,
110 int n, const wxString choices[],
111 long style,
112 const wxValidator& validator,
113 const wxString& name)
2bda0e17 114{
f6bcfd97 115 // Experience shows that wxChoice vs. wxComboBox distinction confuses
8d99be5f
VZ
116 // quite a few people - try to help them
117 wxASSERT_MSG( !(style & wxCB_DROPDOWN) &&
118 !(style & wxCB_READONLY) &&
119 !(style & wxCB_SIMPLE),
f6bcfd97
BP
120 _T("this style flag is ignored by wxChoice, you ")
121 _T("probably want to use a wxComboBox") );
2bda0e17 122
71e57cd6
VZ
123 return CreateAndInit(parent, id, pos, size, n, choices, style,
124 validator, name);
125}
126
cc61d2eb
VZ
127bool wxChoice::CreateAndInit(wxWindow *parent,
128 wxWindowID id,
71e57cd6 129 const wxPoint& pos,
3dfb79a6 130 const wxSize& size,
71e57cd6
VZ
131 int n, const wxString choices[],
132 long style,
133 const wxValidator& validator,
134 const wxString& name)
135{
136 // initialize wxControl
137 if ( !CreateControl(parent, id, pos, size, style, validator, name) )
02b7b6b0 138 return false;
2bda0e17 139
71e57cd6 140 // now create the real HWND
f31a4098 141 if ( !MSWCreateControl(wxT("COMBOBOX"), wxEmptyString, pos, size) )
02b7b6b0 142 return false;
71e57cd6
VZ
143
144
145 // choice/combobox normally has "white" (depends on colour scheme, of
146 // course) background rather than inheriting the parent's background
a756f210 147 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
c92d798f 148
cc61d2eb 149 // initialize the controls contents
8d99be5f
VZ
150 for ( int i = 0; i < n; i++ )
151 {
152 Append(choices[i]);
153 }
2bda0e17 154
cc61d2eb 155 // and now we may finally size the control properly (if needed)
170acdc9 156 SetInitialSize(size);
cc61d2eb 157
02b7b6b0 158 return true;
2bda0e17
KB
159}
160
584ad2a3
MB
161bool wxChoice::Create(wxWindow *parent,
162 wxWindowID id,
163 const wxPoint& pos,
164 const wxSize& size,
165 const wxArrayString& choices,
166 long style,
167 const wxValidator& validator,
168 const wxString& name)
169{
170 wxCArrayString chs(choices);
171 return Create(parent, id, pos, size, chs.GetCount(), chs.GetStrings(),
172 style, validator, name);
173}
174
4b17d2e3
DS
175bool wxChoice::MSWShouldPreProcessMessage(WXMSG *pMsg)
176{
177 MSG *msg = (MSG *) pMsg;
178
dc302518
DS
179 // if the dropdown list is visible, don't preprocess certain keys
180 if ( msg->message == WM_KEYDOWN
181 && (msg->wParam == VK_ESCAPE || msg->wParam == VK_RETURN) )
4b17d2e3
DS
182 {
183 if (::SendMessage(GetHwndOf(this), CB_GETDROPPEDSTATE, 0, 0))
184 {
185 return false;
186 }
187 }
188
189 return wxControl::MSWShouldPreProcessMessage(pMsg);
190}
191
71e57cd6
VZ
192WXDWORD wxChoice::MSWGetStyle(long style, WXDWORD *exstyle) const
193{
194 // we never have an external border
195 WXDWORD msStyle = wxControl::MSWGetStyle
196 (
197 (style & ~wxBORDER_MASK) | wxBORDER_NONE, exstyle
198 );
199
200 // WS_CLIPSIBLINGS is useful with wxChoice and doesn't seem to result in
201 // any problems
202 msStyle |= WS_CLIPSIBLINGS;
203
204 // wxChoice-specific styles
205 msStyle |= CBS_DROPDOWNLIST | WS_HSCROLL | WS_VSCROLL;
206 if ( style & wxCB_SORT )
207 msStyle |= CBS_SORT;
208
209 return msStyle;
210}
211
8ee9d618
VZ
212wxChoice::~wxChoice()
213{
a236aa20 214 Clear();
8ee9d618
VZ
215}
216
8d99be5f
VZ
217// ----------------------------------------------------------------------------
218// adding/deleting items to/from the list
219// ----------------------------------------------------------------------------
2bda0e17 220
a236aa20
VZ
221int wxChoice::DoInsertItems(const wxArrayStringsAdapter& items,
222 unsigned int pos,
223 void **clientData, wxClientDataType type)
8d99be5f 224{
a236aa20 225 MSWAllocStorage(items, CB_INITSTORAGE);
def6fb9b 226
a236aa20 227 const bool append = pos == GetCount();
2bda0e17 228
a236aa20
VZ
229 // use CB_ADDSTRING when appending at the end to make sure the control is
230 // resorted if it has wxCB_SORT style
231 const unsigned msg = append ? CB_ADDSTRING : CB_INSERTSTRING;
243dbf1a 232
a236aa20
VZ
233 if ( append )
234 pos = 0;
235
236 int n = wxNOT_FOUND;
237 const unsigned numItems = items.GetCount();
238 for ( unsigned i = 0; i < numItems; ++i )
71e57cd6 239 {
a236aa20
VZ
240 n = MSWInsertOrAppendItem(pos, items[i], msg);
241 if ( n == wxNOT_FOUND )
242 return n;
243
244 if ( !append )
245 pos++;
246
247 AssignNewItemClientData(n, clientData, i, type);
71e57cd6 248 }
243dbf1a 249
a236aa20
VZ
250 // we need to refresh our size in order to have enough space for the
251 // newly added items
252 if ( !IsFrozen() )
253 UpdateVisibleHeight();
254
31582e4e 255 InvalidateBestSize();
a236aa20 256
243dbf1a
VZ
257 return n;
258}
259
a236aa20 260void wxChoice::DoDeleteOneItem(unsigned int n)
2bda0e17 261{
8228b893 262 wxCHECK_RET( IsValid(n), wxT("invalid item index in wxChoice::Delete") );
8d99be5f
VZ
263
264 SendMessage(GetHwnd(), CB_DELETESTRING, n, 0);
71e57cd6 265
e0e3a32d
VZ
266 if ( !IsFrozen() )
267 UpdateVisibleHeight();
31582e4e
RD
268
269 InvalidateBestSize();
2bda0e17
KB
270}
271
a236aa20 272void wxChoice::DoClear()
8ee9d618 273{
8ee9d618 274 SendMessage(GetHwnd(), CB_RESETCONTENT, 0, 0);
71e57cd6 275
e0e3a32d
VZ
276 if ( !IsFrozen() )
277 UpdateVisibleHeight();
31582e4e
RD
278
279 InvalidateBestSize();
8ee9d618
VZ
280}
281
8d99be5f
VZ
282// ----------------------------------------------------------------------------
283// selection
284// ----------------------------------------------------------------------------
2bda0e17 285
8d99be5f 286int wxChoice::GetSelection() const
6ba93d23
VZ
287{
288 // if m_lastAcceptedSelection is set, it means that the dropdown is
289 // currently shown and that we want to use the last "permanent" selection
290 // instead of whatever is under the mouse pointer currently
291 //
292 // otherwise, get the selection from the control
293 return m_lastAcceptedSelection == wxID_NONE ? GetCurrentSelection()
294 : m_lastAcceptedSelection;
295}
296
297int wxChoice::GetCurrentSelection() const
2bda0e17 298{
8d99be5f 299 return (int)SendMessage(GetHwnd(), CB_GETCURSEL, 0, 0);
2bda0e17
KB
300}
301
debe6624 302void wxChoice::SetSelection(int n)
2bda0e17 303{
8d99be5f
VZ
304 SendMessage(GetHwnd(), CB_SETCURSEL, n, 0);
305}
306
307// ----------------------------------------------------------------------------
308// string list functions
309// ----------------------------------------------------------------------------
310
aa61d352 311unsigned int wxChoice::GetCount() const
8d99be5f 312{
aa61d352 313 return (unsigned int)SendMessage(GetHwnd(), CB_GETCOUNT, 0, 0);
2bda0e17
KB
314}
315
11e62fe6 316int wxChoice::FindString(const wxString& s, bool bCase) const
2bda0e17
KB
317{
318#if defined(__WATCOMC__) && defined(__WIN386__)
8d99be5f
VZ
319 // For some reason, Watcom in WIN386 mode crashes in the CB_FINDSTRINGEXACT message.
320 // wxChoice::Do it the long way instead.
aa61d352
VZ
321 unsigned int count = GetCount();
322 for ( unsigned int i = 0; i < count; i++ )
8d99be5f
VZ
323 {
324 // as CB_FINDSTRINGEXACT is case insensitive, be case insensitive too
aa61d352 325 if (GetString(i).IsSameAs(s, bCase))
8d99be5f
VZ
326 return i;
327 }
328
329 return wxNOT_FOUND;
330#else // !Watcom
4cd1ed99
RN
331 //TODO: Evidently some MSW versions (all?) don't like empty strings
332 //passed to SendMessage, so we have to do it ourselves in that case
beedefb9 333 if ( s.empty() )
4cd1ed99 334 {
aa61d352
VZ
335 unsigned int count = GetCount();
336 for ( unsigned int i = 0; i < count; i++ )
11e62fe6 337 {
aa61d352 338 if (GetString(i).empty())
11e62fe6
WS
339 return i;
340 }
341
342 return wxNOT_FOUND;
343 }
344 else if (bCase)
345 {
346 // back to base class search for not native search type
347 return wxItemContainerImmutable::FindString( s, bCase );
4cd1ed99
RN
348 }
349 else
350 {
11e62fe6 351 int pos = (int)SendMessage(GetHwnd(), CB_FINDSTRINGEXACT,
c9f78968 352 (WPARAM)-1, (LPARAM)s.wx_str());
f31a4098 353
11e62fe6 354 return pos == LB_ERR ? wxNOT_FOUND : pos;
4cd1ed99 355 }
8d99be5f 356#endif // Watcom/!Watcom
2bda0e17
KB
357}
358
aa61d352 359void wxChoice::SetString(unsigned int n, const wxString& s)
6c8a980f 360{
8228b893 361 wxCHECK_RET( IsValid(n), wxT("invalid item index in wxChoice::SetString") );
2b5f62a0
VZ
362
363 // we have to delete and add back the string as there is no way to change a
364 // string in place
365
b4a11fe8
VZ
366 // we need to preserve the client data manually
367 void *oldData = NULL;
368 wxClientData *oldObjData = NULL;
369 if ( HasClientUntypedData() )
370 oldData = GetClientData(n);
371 else if ( HasClientObjectData() )
372 oldObjData = GetClientObject(n);
2b5f62a0
VZ
373
374 ::SendMessage(GetHwnd(), CB_DELETESTRING, n, 0);
c9f78968 375 ::SendMessage(GetHwnd(), CB_INSERTSTRING, n, (LPARAM)s.wx_str() );
2b5f62a0 376
b4a11fe8
VZ
377 // restore the client data
378 if ( oldData )
379 SetClientData(n, oldData);
380 else if ( oldObjData )
381 SetClientObject(n, oldObjData);
31582e4e
RD
382
383 InvalidateBestSize();
6c8a980f
VZ
384}
385
aa61d352 386wxString wxChoice::GetString(unsigned int n) const
2bda0e17 387{
478cabab
VZ
388 int len = (int)::SendMessage(GetHwnd(), CB_GETLBTEXTLEN, n, 0);
389
6c8a980f 390 wxString str;
478cabab
VZ
391 if ( len != CB_ERR && len > 0 )
392 {
393 if ( ::SendMessage
394 (
395 GetHwnd(),
396 CB_GETLBTEXT,
397 n,
398 (LPARAM)(wxChar *)wxStringBuffer(str, len)
399 ) == CB_ERR )
400 {
f6bcfd97 401 wxLogLastError(wxT("SendMessage(CB_GETLBTEXT)"));
21d72d17 402 }
4438caf4 403 }
2bda0e17 404
4438caf4
VZ
405 return str;
406}
2bda0e17 407
8d99be5f
VZ
408// ----------------------------------------------------------------------------
409// client data
410// ----------------------------------------------------------------------------
411
aa61d352 412void wxChoice::DoSetItemClientData(unsigned int n, void* clientData)
8d99be5f 413{
2b5f62a0
VZ
414 if ( ::SendMessage(GetHwnd(), CB_SETITEMDATA,
415 n, (LPARAM)clientData) == CB_ERR )
8d99be5f 416 {
223d09f6 417 wxLogLastError(wxT("CB_SETITEMDATA"));
8d99be5f
VZ
418 }
419}
420
aa61d352 421void* wxChoice::DoGetItemClientData(unsigned int n) const
8d99be5f
VZ
422{
423 LPARAM rc = SendMessage(GetHwnd(), CB_GETITEMDATA, n, 0);
424 if ( rc == CB_ERR )
425 {
223d09f6 426 wxLogLastError(wxT("CB_GETITEMDATA"));
8d99be5f
VZ
427
428 // unfortunately, there is no way to return an error code to the user
8ee9d618 429 rc = (LPARAM) NULL;
8d99be5f
VZ
430 }
431
432 return (void *)rc;
433}
434
8d99be5f
VZ
435// ----------------------------------------------------------------------------
436// wxMSW specific helpers
437// ----------------------------------------------------------------------------
438
71e57cd6
VZ
439void wxChoice::UpdateVisibleHeight()
440{
e6968367 441 // be careful to not change the width here
02b7b6b0 442 DoSetSize(wxDefaultCoord, wxDefaultCoord, wxDefaultCoord, GetSize().y, wxSIZE_USE_EXISTING);
71e57cd6
VZ
443}
444
18c50997
VZ
445void wxChoice::DoMoveWindow(int x, int y, int width, int height)
446{
447 // here is why this is necessary: if the width is negative, the combobox
448 // window proc makes the window of the size width*height instead of
449 // interpreting height in the usual manner (meaning the height of the drop
450 // down list - usually the height specified in the call to MoveWindow()
451 // will not change the height of combo box per se)
452 //
453 // this behaviour is not documented anywhere, but this is just how it is
454 // here (NT 4.4) and, anyhow, the check shouldn't hurt - however without
455 // the check, constraints/sizers using combos may break the height
456 // constraint will have not at all the same value as expected
457 if ( width < 0 )
458 return;
459
460 wxControl::DoMoveWindow(x, y, width, height);
461}
462
d99957b6
VZ
463void wxChoice::DoGetSize(int *w, int *h) const
464{
94a77ff7
VZ
465 // this is weird: sometimes, the height returned by Windows is clearly the
466 // total height of the control including the drop down list -- but only
467 // sometimes, and normally it isn't... I have no idea about what to do with
468 // this
02b7b6b0 469 wxControl::DoGetSize(w, h);
d99957b6
VZ
470}
471
4438caf4 472void wxChoice::DoSetSize(int x, int y,
d99957b6 473 int width, int height,
4438caf4
VZ
474 int sizeFlags)
475{
36043052 476 int heightOrig = height;
f4322df6 477
d4445d24
JS
478 // the height which we must pass to Windows should be the total height of
479 // the control including the drop down list while the height given to us
480 // is, of course, just the height of the permanently visible part of it
481 if ( height != wxDefaultCoord )
482 {
483 // don't make the drop down list too tall, arbitrarily limit it to 40
484 // items max and also don't leave it empty
485 size_t nItems = GetCount();
486 if ( !nItems )
487 nItems = 9;
488 else if ( nItems > 24 )
489 nItems = 24;
a8e65eee 490
d4445d24
JS
491 // add space for the drop down list
492 const int hItem = SendMessage(GetHwnd(), CB_GETITEMHEIGHT, 0, 0);
493 height += hItem*(nItems + 1);
494 }
495 else
496 {
497 // We cannot pass wxDefaultCoord as height to wxControl. wxControl uses
498 // wxGetWindowRect() to determine the current height of the combobox,
499 // and then again sets the combobox's height to that value. Unfortunately,
500 // wxGetWindowRect doesn't include the dropdown list's height (at least
501 // on Win2K), so this would result in a combobox with dropdown height of
502 // 1 pixel. We have to determine the default height ourselves and call
503 // wxControl with that value instead.
504 int w, h;
505 RECT r;
506 DoGetSize(&w, &h);
507 if (::SendMessage(GetHwnd(), CB_GETDROPPEDCONTROLRECT, 0, (LPARAM) &r) != 0)
508 {
509 height = h + r.bottom - r.top;
510 }
511 }
512
513 wxControl::DoSetSize(x, y, width, height, sizeFlags);
514
5637cc35
JS
515 // If we're storing a pending size, make sure we store
516 // the original size for reporting back to the app.
517 if (m_pendingSize != wxDefaultSize)
518 m_pendingSize = wxSize(width, heightOrig);
519
d4445d24
JS
520 // This solution works on XP, but causes choice/combobox lists to be
521 // too short on W2K and earlier.
522#if 0
e3e78de1
VZ
523 int widthCurrent, heightCurrent;
524 DoGetSize(&widthCurrent, &heightCurrent);
525
d99957b6
VZ
526 // the height which we must pass to Windows should be the total height of
527 // the control including the drop down list while the height given to us
528 // is, of course, just the height of the permanently visible part of it
e3e78de1 529 if ( height != wxDefaultCoord && height != heightCurrent )
d99957b6 530 {
e3e78de1
VZ
531 // don't make the drop down list too tall, arbitrarily limit it to 40
532 // items max and also don't leave it empty
aa61d352 533 unsigned int nItems = GetCount();
e3e78de1
VZ
534 if ( !nItems )
535 nItems = 9;
536 else if ( nItems > 24 )
537 nItems = 24;
538
539 // add space for the drop down list
540 const int hItem = SendMessage(GetHwnd(), CB_GETITEMHEIGHT, 0, 0);
541 height += hItem*(nItems + 1);
d99957b6 542 }
e3e78de1 543 else // keep the same height as now
d6959d6f 544 {
e3e78de1
VZ
545 // normally wxWindow::DoSetSize() checks if we set the same size as the
546 // window already has and does nothing in this case, but for us the
547 // check fails as the size we pass to it includes the dropdown while
548 // the size returned by our GetSize() does not, so test if the size
549 // didn't really change ourselves here
550 if ( width == wxDefaultCoord || width == widthCurrent )
551 {
552 // size doesn't change, what about position?
553 int xCurrent, yCurrent;
554 DoGetPosition(&xCurrent, &yCurrent);
555 const bool defMeansUnchanged = !(sizeFlags & wxSIZE_ALLOW_MINUS_ONE);
556 if ( ((x == wxDefaultCoord && defMeansUnchanged) || x == xCurrent)
557 &&
558 ((y == wxDefaultCoord && defMeansUnchanged) || y == yCurrent) )
559 {
560 // nothing changes, nothing to do
561 return;
562 }
563 }
564
d6959d6f 565 // We cannot pass wxDefaultCoord as height to wxControl. wxControl uses
f31a4098
WS
566 // wxGetWindowRect() to determine the current height of the combobox,
567 // and then again sets the combobox's height to that value. Unfortunately,
568 // wxGetWindowRect doesn't include the dropdown list's height (at least
569 // on Win2K), so this would result in a combobox with dropdown height of
d6959d6f
DS
570 // 1 pixel. We have to determine the default height ourselves and call
571 // wxControl with that value instead.
e3e78de1
VZ
572 //
573 // Also notice that sometimes CB_GETDROPPEDCONTROLRECT seems to return
574 // wildly incorrect values (~32000) which looks like a bug in it, just
575 // ignore them in this case
d6959d6f 576 RECT r;
e3e78de1
VZ
577 if ( ::SendMessage(GetHwnd(), CB_GETDROPPEDCONTROLRECT, 0, (LPARAM) &r)
578 && r.bottom < 30000 )
d6959d6f 579 {
e3e78de1 580 height = heightCurrent + r.bottom - r.top;
d6959d6f
DS
581 }
582 }
d99957b6
VZ
583
584 wxControl::DoSetSize(x, y, width, height, sizeFlags);
ef036883 585#endif
4438caf4 586}
2bda0e17 587
882a8f40 588wxSize wxChoice::DoGetBestSize() const
4438caf4
VZ
589{
590 // find the widest string
4438caf4 591 int wChoice = 0;
aa61d352
VZ
592 const unsigned int nItems = GetCount();
593 for ( unsigned int i = 0; i < nItems; i++ )
2bda0e17 594 {
d99957b6
VZ
595 int wLine;
596 GetTextExtent(GetString(i), &wLine, NULL);
4438caf4
VZ
597 if ( wLine > wChoice )
598 wChoice = wLine;
2bda0e17 599 }
fd3f686c 600
4438caf4
VZ
601 // give it some reasonable default value if there are no strings in the
602 // list
603 if ( wChoice == 0 )
604 wChoice = 100;
2bda0e17 605
d99957b6
VZ
606 // the combobox should be slightly larger than the widest string
607 wChoice += 5*GetCharWidth();
2bda0e17 608
1d13cc5d
RD
609 wxSize best(wChoice, EDIT_HEIGHT_FROM_CHAR_HEIGHT(GetCharHeight()));
610 CacheBestSize(best);
611 return best;
2bda0e17
KB
612}
613
c140b7e7 614WXLRESULT wxChoice::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
2bda0e17 615{
f499e614 616 switch ( nMsg )
2bda0e17 617 {
f499e614
VZ
618 case WM_LBUTTONUP:
619 {
620 int x = (int)LOWORD(lParam);
621 int y = (int)HIWORD(lParam);
622
623 // Ok, this is truly weird, but if a panel with a wxChoice
624 // loses the focus, then you get a *fake* WM_LBUTTONUP message
625 // with x = 65535 and y = 65535. Filter out this nonsense.
626 //
627 // VZ: I'd like to know how to reproduce this please...
628 if ( x == 65535 && y == 65535 )
629 return 0;
630 }
631 break;
632
633 // we have to handle both: one for the normal case and the other
634 // for readonly
635 case WM_CTLCOLOREDIT:
636 case WM_CTLCOLORLISTBOX:
637 case WM_CTLCOLORSTATIC:
638 {
f499e614
VZ
639 WXHDC hdc;
640 WXHWND hwnd;
9f368d0d 641 UnpackCtlColor(wParam, lParam, &hdc, &hwnd);
f499e614 642
2bae4332 643 WXHBRUSH hbr = MSWControlColor((WXHDC)hdc, hwnd);
48fa6bd3
VZ
644 if ( hbr )
645 return (WXLRESULT)hbr;
646 //else: fall through to default window proc
f499e614 647 }
2bda0e17
KB
648 }
649
8d99be5f 650 return wxWindow::MSWWindowProc(nMsg, wParam, lParam);
2bda0e17
KB
651}
652
8d99be5f 653bool wxChoice::MSWCommand(WXUINT param, WXWORD WXUNUSED(id))
2bda0e17 654{
c11f0412
VZ
655 /*
656 The native control provides a great variety in the events it sends in
657 the different selection scenarios (undoubtedly for greater amusement of
658 the programmers using it). For the reference, here are the cases when
659 the final selection is accepted (things are quite interesting when it
660 is cancelled too):
661
662 A. Selecting with just the arrows without opening the dropdown:
663 1. CBN_SELENDOK
664 2. CBN_SELCHANGE
665
666 B. Opening dropdown with F4 and selecting with arrows:
667 1. CBN_DROPDOWN
668 2. many CBN_SELCHANGE while changing selection in the list
669 3. CBN_SELENDOK
670 4. CBN_CLOSEUP
671
672 C. Selecting with the mouse:
673 1. CBN_DROPDOWN
674 -- no intermediate CBN_SELCHANGEs --
675 2. CBN_SELENDOK
676 3. CBN_CLOSEUP
677 4. CBN_SELCHANGE
678
679 Admire the different order of messages in all of those cases, it must
680 surely have taken a lot of effort to Microsoft developers to achieve
681 such originality.
682 */
6ba93d23 683 switch ( param )
2bda0e17 684 {
6ba93d23 685 case CBN_DROPDOWN:
c11f0412
VZ
686 // we use this value both because we don't want to track selection
687 // using CB_GETCURSEL while the dropdown is opened and because we
688 // need to reset the selection back to it if it's eventually
689 // cancelled by user
6ba93d23 690 m_lastAcceptedSelection = GetCurrentSelection();
f785bfdd
VZ
691 if ( m_lastAcceptedSelection == -1 )
692 {
693 // no current selection so no need to restore it later (this
694 // happens when opening a combobox containing text not from its
695 // list of items and we shouldn't erase this text)
696 m_lastAcceptedSelection = wxID_NONE;
697 }
6ba93d23 698 break;
2bda0e17 699
6ba93d23 700 case CBN_CLOSEUP:
c11f0412
VZ
701 // if the selection was accepted by the user, it should have been
702 // reset to wxID_NONE by CBN_SELENDOK, otherwise the selection was
703 // cancelled and we must restore the old one
704 if ( m_lastAcceptedSelection != wxID_NONE )
705 {
706 SetSelection(m_lastAcceptedSelection);
707 m_lastAcceptedSelection = wxID_NONE;
708 }
6ba93d23
VZ
709 break;
710
c11f0412
VZ
711 case CBN_SELENDOK:
712 // reset it to prevent CBN_CLOSEUP from undoing the selection (it's
713 // ok to reset it now as GetCurrentSelection() will now return the
714 // same thing anyhow)
715 m_lastAcceptedSelection = wxID_NONE;
716
6ba93d23
VZ
717 {
718 const int n = GetSelection();
719
720 wxCommandEvent event(wxEVT_COMMAND_CHOICE_SELECTED, m_windowId);
721 event.SetInt(n);
722 event.SetEventObject(this);
723
724 if ( n > -1 )
725 {
726 event.SetString(GetStringSelection());
593ac33e 727 InitCommandEventWithItems(event, n);
6ba93d23
VZ
728 }
729
730 ProcessCommand(event);
731 }
c11f0412
VZ
732 break;
733
734 // don't handle CBN_SELENDCANCEL: just leave m_lastAcceptedSelection
735 // valid and the selection will be undone in CBN_CLOSEUP above
736
737 // don't handle CBN_SELCHANGE neither, we don't want to generate events
738 // while the dropdown is opened -- but do add it if we ever need this
739
740 default:
741 return false;
8c1c5302 742 }
2bda0e17 743
c11f0412 744 return true;
8d99be5f 745}
2bda0e17 746
2bae4332 747WXHBRUSH wxChoice::MSWControlColor(WXHDC hDC, WXHWND hWnd)
f6bcfd97 748{
48fa6bd3
VZ
749 if ( !IsEnabled() )
750 return MSWControlColorDisabled(hDC);
f6bcfd97 751
2bae4332 752 return wxChoiceBase::MSWControlColor(hDC, hWnd);
f6bcfd97
BP
753}
754
3180bc0e 755#endif // wxUSE_CHOICE && !(__SMARTPHONE__ && __WXWINCE__)