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