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