many wxItemContainer-related changes:
[wxWidgets.git] / src / msw / choice.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/choice.cpp
3 // Purpose: wxChoice
4 // Author: Julian Smart
5 // Modified by: Vadim Zeitlin to derive from wxChoiceBase
6 // Created: 04/01/98
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
22
23 #ifdef __BORLANDC__
24 #pragma hdrstop
25 #endif
26
27 #if wxUSE_CHOICE && !(defined(__SMARTPHONE__) && defined(__WXWINCE__))
28
29 #include "wx/choice.h"
30
31 #ifndef WX_PRECOMP
32 #include "wx/utils.h"
33 #include "wx/log.h"
34 #include "wx/brush.h"
35 #include "wx/settings.h"
36 #endif
37
38 #include "wx/msw/private.h"
39
40 #if wxUSE_EXTENDED_RTTI
41 WX_DEFINE_FLAGS( wxChoiceStyle )
42
43 wxBEGIN_FLAGS( wxChoiceStyle )
44 // new style border flags, we put them first to
45 // use them for streaming out
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)
52
53 // old style border flags
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)
59 wxFLAGS_MEMBER(wxBORDER)
60
61 // standard window styles
62 wxFLAGS_MEMBER(wxTAB_TRAVERSAL)
63 wxFLAGS_MEMBER(wxCLIP_CHILDREN)
64 wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW)
65 wxFLAGS_MEMBER(wxWANTS_CHARS)
66 wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE)
67 wxFLAGS_MEMBER(wxALWAYS_SHOW_SB )
68 wxFLAGS_MEMBER(wxVSCROLL)
69 wxFLAGS_MEMBER(wxHSCROLL)
70
71 wxEND_FLAGS( wxChoiceStyle )
72
73 IMPLEMENT_DYNAMIC_CLASS_XTI(wxChoice, wxControlWithItems,"wx/choice.h")
74
75 wxBEGIN_PROPERTIES_TABLE(wxChoice)
76 wxEVENT_PROPERTY( Select , wxEVT_COMMAND_CHOICE_SELECTED , wxCommandEvent )
77
78 wxPROPERTY( Font , wxFont , SetFont , GetFont , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
79 wxPROPERTY_COLLECTION( Choices , wxArrayString , wxString , AppendString , GetStrings , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
80 wxPROPERTY( Selection ,int, SetSelection, GetSelection, EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
81 wxPROPERTY_FLAGS( WindowStyle , wxChoiceStyle , long , SetWindowStyleFlag , GetWindowStyleFlag , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
82 wxEND_PROPERTIES_TABLE()
83
84 wxBEGIN_HANDLERS_TABLE(wxChoice)
85 wxEND_HANDLERS_TABLE()
86
87 wxCONSTRUCTOR_4( wxChoice , wxWindow* , Parent , wxWindowID , Id , wxPoint , Position , wxSize , Size )
88 #else
89 IMPLEMENT_DYNAMIC_CLASS(wxChoice, wxControlWithItems)
90 #endif
91 /*
92 TODO PROPERTIES
93 selection (long)
94 content (list)
95 item
96 */
97
98 // ============================================================================
99 // implementation
100 // ============================================================================
101
102 // ----------------------------------------------------------------------------
103 // creation
104 // ----------------------------------------------------------------------------
105
106 bool 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)
114 {
115 // Experience shows that wxChoice vs. wxComboBox distinction confuses
116 // quite a few people - try to help them
117 wxASSERT_MSG( !(style & wxCB_DROPDOWN) &&
118 !(style & wxCB_READONLY) &&
119 !(style & wxCB_SIMPLE),
120 _T("this style flag is ignored by wxChoice, you ")
121 _T("probably want to use a wxComboBox") );
122
123 return CreateAndInit(parent, id, pos, size, n, choices, style,
124 validator, name);
125 }
126
127 bool wxChoice::CreateAndInit(wxWindow *parent,
128 wxWindowID id,
129 const wxPoint& pos,
130 const wxSize& size,
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) )
138 return false;
139
140 // now create the real HWND
141 if ( !MSWCreateControl(wxT("COMBOBOX"), wxEmptyString, pos, size) )
142 return false;
143
144
145 // choice/combobox normally has "white" (depends on colour scheme, of
146 // course) background rather than inheriting the parent's background
147 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
148
149 // initialize the controls contents
150 for ( int i = 0; i < n; i++ )
151 {
152 Append(choices[i]);
153 }
154
155 // and now we may finally size the control properly (if needed)
156 SetInitialSize(size);
157
158 return true;
159 }
160
161 bool 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
175 bool wxChoice::MSWShouldPreProcessMessage(WXMSG *pMsg)
176 {
177 MSG *msg = (MSG *) pMsg;
178
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) )
182 {
183 if (::SendMessage(GetHwndOf(this), CB_GETDROPPEDSTATE, 0, 0))
184 {
185 return false;
186 }
187 }
188
189 return wxControl::MSWShouldPreProcessMessage(pMsg);
190 }
191
192 WXDWORD 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
212 wxChoice::~wxChoice()
213 {
214 Clear();
215 }
216
217 // ----------------------------------------------------------------------------
218 // adding/deleting items to/from the list
219 // ----------------------------------------------------------------------------
220
221 int wxChoice::DoInsertItems(const wxArrayStringsAdapter& items,
222 unsigned int pos,
223 void **clientData, wxClientDataType type)
224 {
225 MSWAllocStorage(items, CB_INITSTORAGE);
226
227 const bool append = pos == GetCount();
228
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;
232
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 )
239 {
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);
248 }
249
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
255 InvalidateBestSize();
256
257 return n;
258 }
259
260 void wxChoice::DoDeleteOneItem(unsigned int n)
261 {
262 wxCHECK_RET( IsValid(n), wxT("invalid item index in wxChoice::Delete") );
263
264 SendMessage(GetHwnd(), CB_DELETESTRING, n, 0);
265
266 if ( !IsFrozen() )
267 UpdateVisibleHeight();
268
269 InvalidateBestSize();
270 }
271
272 void wxChoice::DoClear()
273 {
274 SendMessage(GetHwnd(), CB_RESETCONTENT, 0, 0);
275
276 if ( !IsFrozen() )
277 UpdateVisibleHeight();
278
279 InvalidateBestSize();
280 }
281
282 // ----------------------------------------------------------------------------
283 // selection
284 // ----------------------------------------------------------------------------
285
286 int wxChoice::GetSelection() const
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
297 int wxChoice::GetCurrentSelection() const
298 {
299 return (int)SendMessage(GetHwnd(), CB_GETCURSEL, 0, 0);
300 }
301
302 void wxChoice::SetSelection(int n)
303 {
304 SendMessage(GetHwnd(), CB_SETCURSEL, n, 0);
305 }
306
307 // ----------------------------------------------------------------------------
308 // string list functions
309 // ----------------------------------------------------------------------------
310
311 unsigned int wxChoice::GetCount() const
312 {
313 return (unsigned int)SendMessage(GetHwnd(), CB_GETCOUNT, 0, 0);
314 }
315
316 int wxChoice::FindString(const wxString& s, bool bCase) const
317 {
318 #if defined(__WATCOMC__) && defined(__WIN386__)
319 // For some reason, Watcom in WIN386 mode crashes in the CB_FINDSTRINGEXACT message.
320 // wxChoice::Do it the long way instead.
321 unsigned int count = GetCount();
322 for ( unsigned int i = 0; i < count; i++ )
323 {
324 // as CB_FINDSTRINGEXACT is case insensitive, be case insensitive too
325 if (GetString(i).IsSameAs(s, bCase))
326 return i;
327 }
328
329 return wxNOT_FOUND;
330 #else // !Watcom
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
333 if ( s.empty() )
334 {
335 unsigned int count = GetCount();
336 for ( unsigned int i = 0; i < count; i++ )
337 {
338 if (GetString(i).empty())
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 );
348 }
349 else
350 {
351 int pos = (int)SendMessage(GetHwnd(), CB_FINDSTRINGEXACT,
352 (WPARAM)-1, (LPARAM)s.wx_str());
353
354 return pos == LB_ERR ? wxNOT_FOUND : pos;
355 }
356 #endif // Watcom/!Watcom
357 }
358
359 void wxChoice::SetString(unsigned int n, const wxString& s)
360 {
361 wxCHECK_RET( IsValid(n), wxT("invalid item index in wxChoice::SetString") );
362
363 // we have to delete and add back the string as there is no way to change a
364 // string in place
365
366 // we need to preserve the client data
367 void *data;
368 if ( m_clientDataItemsType != wxClientData_None )
369 {
370 data = DoGetItemClientData(n);
371 }
372 else // no client data
373 {
374 data = NULL;
375 }
376
377 ::SendMessage(GetHwnd(), CB_DELETESTRING, n, 0);
378 ::SendMessage(GetHwnd(), CB_INSERTSTRING, n, (LPARAM)s.wx_str() );
379
380 if ( data )
381 {
382 DoSetItemClientData(n, data);
383 }
384 //else: it's already NULL by default
385
386 InvalidateBestSize();
387 }
388
389 wxString wxChoice::GetString(unsigned int n) const
390 {
391 int len = (int)::SendMessage(GetHwnd(), CB_GETLBTEXTLEN, n, 0);
392
393 wxString str;
394 if ( len != CB_ERR && len > 0 )
395 {
396 if ( ::SendMessage
397 (
398 GetHwnd(),
399 CB_GETLBTEXT,
400 n,
401 (LPARAM)(wxChar *)wxStringBuffer(str, len)
402 ) == CB_ERR )
403 {
404 wxLogLastError(wxT("SendMessage(CB_GETLBTEXT)"));
405 }
406 }
407
408 return str;
409 }
410
411 // ----------------------------------------------------------------------------
412 // client data
413 // ----------------------------------------------------------------------------
414
415 void wxChoice::DoSetItemClientData(unsigned int n, void* clientData)
416 {
417 if ( ::SendMessage(GetHwnd(), CB_SETITEMDATA,
418 n, (LPARAM)clientData) == CB_ERR )
419 {
420 wxLogLastError(wxT("CB_SETITEMDATA"));
421 }
422 }
423
424 void* wxChoice::DoGetItemClientData(unsigned int n) const
425 {
426 LPARAM rc = SendMessage(GetHwnd(), CB_GETITEMDATA, n, 0);
427 if ( rc == CB_ERR )
428 {
429 wxLogLastError(wxT("CB_GETITEMDATA"));
430
431 // unfortunately, there is no way to return an error code to the user
432 rc = (LPARAM) NULL;
433 }
434
435 return (void *)rc;
436 }
437
438 // ----------------------------------------------------------------------------
439 // wxMSW specific helpers
440 // ----------------------------------------------------------------------------
441
442 void wxChoice::UpdateVisibleHeight()
443 {
444 // be careful to not change the width here
445 DoSetSize(wxDefaultCoord, wxDefaultCoord, wxDefaultCoord, GetSize().y, wxSIZE_USE_EXISTING);
446 }
447
448 void wxChoice::DoMoveWindow(int x, int y, int width, int height)
449 {
450 // here is why this is necessary: if the width is negative, the combobox
451 // window proc makes the window of the size width*height instead of
452 // interpreting height in the usual manner (meaning the height of the drop
453 // down list - usually the height specified in the call to MoveWindow()
454 // will not change the height of combo box per se)
455 //
456 // this behaviour is not documented anywhere, but this is just how it is
457 // here (NT 4.4) and, anyhow, the check shouldn't hurt - however without
458 // the check, constraints/sizers using combos may break the height
459 // constraint will have not at all the same value as expected
460 if ( width < 0 )
461 return;
462
463 wxControl::DoMoveWindow(x, y, width, height);
464 }
465
466 void wxChoice::DoGetSize(int *w, int *h) const
467 {
468 // this is weird: sometimes, the height returned by Windows is clearly the
469 // total height of the control including the drop down list -- but only
470 // sometimes, and normally it isn't... I have no idea about what to do with
471 // this
472 wxControl::DoGetSize(w, h);
473 }
474
475 void wxChoice::DoSetSize(int x, int y,
476 int width, int height,
477 int sizeFlags)
478 {
479 int heightOrig = height;
480
481 // the height which we must pass to Windows should be the total height of
482 // the control including the drop down list while the height given to us
483 // is, of course, just the height of the permanently visible part of it
484 if ( height != wxDefaultCoord )
485 {
486 // don't make the drop down list too tall, arbitrarily limit it to 40
487 // items max and also don't leave it empty
488 size_t nItems = GetCount();
489 if ( !nItems )
490 nItems = 9;
491 else if ( nItems > 24 )
492 nItems = 24;
493
494 // add space for the drop down list
495 const int hItem = SendMessage(GetHwnd(), CB_GETITEMHEIGHT, 0, 0);
496 height += hItem*(nItems + 1);
497 }
498 else
499 {
500 // We cannot pass wxDefaultCoord as height to wxControl. wxControl uses
501 // wxGetWindowRect() to determine the current height of the combobox,
502 // and then again sets the combobox's height to that value. Unfortunately,
503 // wxGetWindowRect doesn't include the dropdown list's height (at least
504 // on Win2K), so this would result in a combobox with dropdown height of
505 // 1 pixel. We have to determine the default height ourselves and call
506 // wxControl with that value instead.
507 int w, h;
508 RECT r;
509 DoGetSize(&w, &h);
510 if (::SendMessage(GetHwnd(), CB_GETDROPPEDCONTROLRECT, 0, (LPARAM) &r) != 0)
511 {
512 height = h + r.bottom - r.top;
513 }
514 }
515
516 wxControl::DoSetSize(x, y, width, height, sizeFlags);
517
518 // If we're storing a pending size, make sure we store
519 // the original size for reporting back to the app.
520 if (m_pendingSize != wxDefaultSize)
521 m_pendingSize = wxSize(width, heightOrig);
522
523 // This solution works on XP, but causes choice/combobox lists to be
524 // too short on W2K and earlier.
525 #if 0
526 int widthCurrent, heightCurrent;
527 DoGetSize(&widthCurrent, &heightCurrent);
528
529 // the height which we must pass to Windows should be the total height of
530 // the control including the drop down list while the height given to us
531 // is, of course, just the height of the permanently visible part of it
532 if ( height != wxDefaultCoord && height != heightCurrent )
533 {
534 // don't make the drop down list too tall, arbitrarily limit it to 40
535 // items max and also don't leave it empty
536 unsigned int nItems = GetCount();
537 if ( !nItems )
538 nItems = 9;
539 else if ( nItems > 24 )
540 nItems = 24;
541
542 // add space for the drop down list
543 const int hItem = SendMessage(GetHwnd(), CB_GETITEMHEIGHT, 0, 0);
544 height += hItem*(nItems + 1);
545 }
546 else // keep the same height as now
547 {
548 // normally wxWindow::DoSetSize() checks if we set the same size as the
549 // window already has and does nothing in this case, but for us the
550 // check fails as the size we pass to it includes the dropdown while
551 // the size returned by our GetSize() does not, so test if the size
552 // didn't really change ourselves here
553 if ( width == wxDefaultCoord || width == widthCurrent )
554 {
555 // size doesn't change, what about position?
556 int xCurrent, yCurrent;
557 DoGetPosition(&xCurrent, &yCurrent);
558 const bool defMeansUnchanged = !(sizeFlags & wxSIZE_ALLOW_MINUS_ONE);
559 if ( ((x == wxDefaultCoord && defMeansUnchanged) || x == xCurrent)
560 &&
561 ((y == wxDefaultCoord && defMeansUnchanged) || y == yCurrent) )
562 {
563 // nothing changes, nothing to do
564 return;
565 }
566 }
567
568 // We cannot pass wxDefaultCoord as height to wxControl. wxControl uses
569 // wxGetWindowRect() to determine the current height of the combobox,
570 // and then again sets the combobox's height to that value. Unfortunately,
571 // wxGetWindowRect doesn't include the dropdown list's height (at least
572 // on Win2K), so this would result in a combobox with dropdown height of
573 // 1 pixel. We have to determine the default height ourselves and call
574 // wxControl with that value instead.
575 //
576 // Also notice that sometimes CB_GETDROPPEDCONTROLRECT seems to return
577 // wildly incorrect values (~32000) which looks like a bug in it, just
578 // ignore them in this case
579 RECT r;
580 if ( ::SendMessage(GetHwnd(), CB_GETDROPPEDCONTROLRECT, 0, (LPARAM) &r)
581 && r.bottom < 30000 )
582 {
583 height = heightCurrent + r.bottom - r.top;
584 }
585 }
586
587 wxControl::DoSetSize(x, y, width, height, sizeFlags);
588 #endif
589 }
590
591 wxSize wxChoice::DoGetBestSize() const
592 {
593 // find the widest string
594 int wChoice = 0;
595 const unsigned int nItems = GetCount();
596 for ( unsigned int i = 0; i < nItems; i++ )
597 {
598 int wLine;
599 GetTextExtent(GetString(i), &wLine, NULL);
600 if ( wLine > wChoice )
601 wChoice = wLine;
602 }
603
604 // give it some reasonable default value if there are no strings in the
605 // list
606 if ( wChoice == 0 )
607 wChoice = 100;
608
609 // the combobox should be slightly larger than the widest string
610 wChoice += 5*GetCharWidth();
611
612 wxSize best(wChoice, EDIT_HEIGHT_FROM_CHAR_HEIGHT(GetCharHeight()));
613 CacheBestSize(best);
614 return best;
615 }
616
617 WXLRESULT wxChoice::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
618 {
619 switch ( nMsg )
620 {
621 case WM_LBUTTONUP:
622 {
623 int x = (int)LOWORD(lParam);
624 int y = (int)HIWORD(lParam);
625
626 // Ok, this is truly weird, but if a panel with a wxChoice
627 // loses the focus, then you get a *fake* WM_LBUTTONUP message
628 // with x = 65535 and y = 65535. Filter out this nonsense.
629 //
630 // VZ: I'd like to know how to reproduce this please...
631 if ( x == 65535 && y == 65535 )
632 return 0;
633 }
634 break;
635
636 // we have to handle both: one for the normal case and the other
637 // for readonly
638 case WM_CTLCOLOREDIT:
639 case WM_CTLCOLORLISTBOX:
640 case WM_CTLCOLORSTATIC:
641 {
642 WXHDC hdc;
643 WXHWND hwnd;
644 UnpackCtlColor(wParam, lParam, &hdc, &hwnd);
645
646 WXHBRUSH hbr = MSWControlColor((WXHDC)hdc, hwnd);
647 if ( hbr )
648 return (WXLRESULT)hbr;
649 //else: fall through to default window proc
650 }
651 }
652
653 return wxWindow::MSWWindowProc(nMsg, wParam, lParam);
654 }
655
656 bool wxChoice::MSWCommand(WXUINT param, WXWORD WXUNUSED(id))
657 {
658 /*
659 The native control provides a great variety in the events it sends in
660 the different selection scenarios (undoubtedly for greater amusement of
661 the programmers using it). For the reference, here are the cases when
662 the final selection is accepted (things are quite interesting when it
663 is cancelled too):
664
665 A. Selecting with just the arrows without opening the dropdown:
666 1. CBN_SELENDOK
667 2. CBN_SELCHANGE
668
669 B. Opening dropdown with F4 and selecting with arrows:
670 1. CBN_DROPDOWN
671 2. many CBN_SELCHANGE while changing selection in the list
672 3. CBN_SELENDOK
673 4. CBN_CLOSEUP
674
675 C. Selecting with the mouse:
676 1. CBN_DROPDOWN
677 -- no intermediate CBN_SELCHANGEs --
678 2. CBN_SELENDOK
679 3. CBN_CLOSEUP
680 4. CBN_SELCHANGE
681
682 Admire the different order of messages in all of those cases, it must
683 surely have taken a lot of effort to Microsoft developers to achieve
684 such originality.
685 */
686 switch ( param )
687 {
688 case CBN_DROPDOWN:
689 // we use this value both because we don't want to track selection
690 // using CB_GETCURSEL while the dropdown is opened and because we
691 // need to reset the selection back to it if it's eventually
692 // cancelled by user
693 m_lastAcceptedSelection = GetCurrentSelection();
694 if ( m_lastAcceptedSelection == -1 )
695 {
696 // no current selection so no need to restore it later (this
697 // happens when opening a combobox containing text not from its
698 // list of items and we shouldn't erase this text)
699 m_lastAcceptedSelection = wxID_NONE;
700 }
701 break;
702
703 case CBN_CLOSEUP:
704 // if the selection was accepted by the user, it should have been
705 // reset to wxID_NONE by CBN_SELENDOK, otherwise the selection was
706 // cancelled and we must restore the old one
707 if ( m_lastAcceptedSelection != wxID_NONE )
708 {
709 SetSelection(m_lastAcceptedSelection);
710 m_lastAcceptedSelection = wxID_NONE;
711 }
712 break;
713
714 case CBN_SELENDOK:
715 // reset it to prevent CBN_CLOSEUP from undoing the selection (it's
716 // ok to reset it now as GetCurrentSelection() will now return the
717 // same thing anyhow)
718 m_lastAcceptedSelection = wxID_NONE;
719
720 {
721 const int n = GetSelection();
722
723 wxCommandEvent event(wxEVT_COMMAND_CHOICE_SELECTED, m_windowId);
724 event.SetInt(n);
725 event.SetEventObject(this);
726
727 if ( n > -1 )
728 {
729 event.SetString(GetStringSelection());
730 InitCommandEventWithItems(event, n);
731 }
732
733 ProcessCommand(event);
734 }
735 break;
736
737 // don't handle CBN_SELENDCANCEL: just leave m_lastAcceptedSelection
738 // valid and the selection will be undone in CBN_CLOSEUP above
739
740 // don't handle CBN_SELCHANGE neither, we don't want to generate events
741 // while the dropdown is opened -- but do add it if we ever need this
742
743 default:
744 return false;
745 }
746
747 return true;
748 }
749
750 WXHBRUSH wxChoice::MSWControlColor(WXHDC hDC, WXHWND hWnd)
751 {
752 if ( !IsEnabled() )
753 return MSWControlColorDisabled(hDC);
754
755 return wxChoiceBase::MSWControlColor(hDC, hWnd);
756 }
757
758 #endif // wxUSE_CHOICE && !(__SMARTPHONE__ && __WXWINCE__)