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