]> git.saurik.com Git - wxWidgets.git/blame - src/msw/choice.cpp
fix off by one (or rather "off by border width") bug in ScrollWindow() (part of patch...
[wxWidgets.git] / src / msw / choice.cpp
CommitLineData
2bda0e17 1/////////////////////////////////////////////////////////////////////////////
11e62fe6 2// Name: src/msw/choice.cpp
2bda0e17
KB
3// Purpose: wxChoice
4// Author: Julian Smart
8d99be5f 5// Modified by: Vadim Zeitlin to derive from wxChoiceBase
2bda0e17
KB
6// Created: 04/01/98
7// RCS-ID: $Id$
6c9a19aa 8// Copyright: (c) Julian Smart
65571936 9// Licence: wxWindows licence
2bda0e17
KB
10/////////////////////////////////////////////////////////////////////////////
11
8d99be5f
VZ
12// ============================================================================
13// declarations
14// ============================================================================
15
16// ----------------------------------------------------------------------------
17// headers
18// ----------------------------------------------------------------------------
19
2bda0e17
KB
20// For compilers that support precompilation, includes "wx.h".
21#include "wx/wxprec.h"
22
23#ifdef __BORLANDC__
8d99be5f 24 #pragma hdrstop
2bda0e17
KB
25#endif
26
3180bc0e 27#if wxUSE_CHOICE && !(defined(__SMARTPHONE__) && defined(__WXWINCE__))
1e6feb95 28
b36e08d0
WS
29#include "wx/choice.h"
30
2bda0e17 31#ifndef WX_PRECOMP
8d99be5f
VZ
32 #include "wx/utils.h"
33 #include "wx/log.h"
f6bcfd97 34 #include "wx/brush.h"
c7401637 35 #include "wx/settings.h"
2bda0e17
KB
36#endif
37
38#include "wx/msw/private.h"
39
6a89f9ee 40#if wxUSE_EXTENDED_RTTI
bc9fb572
JS
41WX_DEFINE_FLAGS( wxChoiceStyle )
42
3ff066a4 43wxBEGIN_FLAGS( wxChoiceStyle )
bc9fb572
JS
44 // new style border flags, we put them first to
45 // use them for streaming out
3ff066a4
SC
46 wxFLAGS_MEMBER(wxBORDER_SIMPLE)
47 wxFLAGS_MEMBER(wxBORDER_SUNKEN)
48 wxFLAGS_MEMBER(wxBORDER_DOUBLE)
49 wxFLAGS_MEMBER(wxBORDER_RAISED)
50 wxFLAGS_MEMBER(wxBORDER_STATIC)
51 wxFLAGS_MEMBER(wxBORDER_NONE)
3dfb79a6 52
bc9fb572 53 // old style border flags
3ff066a4
SC
54 wxFLAGS_MEMBER(wxSIMPLE_BORDER)
55 wxFLAGS_MEMBER(wxSUNKEN_BORDER)
56 wxFLAGS_MEMBER(wxDOUBLE_BORDER)
57 wxFLAGS_MEMBER(wxRAISED_BORDER)
58 wxFLAGS_MEMBER(wxSTATIC_BORDER)
cb0afb26 59 wxFLAGS_MEMBER(wxBORDER)
bc9fb572
JS
60
61 // standard window styles
3ff066a4
SC
62 wxFLAGS_MEMBER(wxTAB_TRAVERSAL)
63 wxFLAGS_MEMBER(wxCLIP_CHILDREN)
64 wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW)
65 wxFLAGS_MEMBER(wxWANTS_CHARS)
cb0afb26 66 wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE)
3ff066a4
SC
67 wxFLAGS_MEMBER(wxALWAYS_SHOW_SB )
68 wxFLAGS_MEMBER(wxVSCROLL)
69 wxFLAGS_MEMBER(wxHSCROLL)
bc9fb572 70
3ff066a4 71wxEND_FLAGS( wxChoiceStyle )
bc9fb572 72
0ad2a19e 73IMPLEMENT_DYNAMIC_CLASS_XTI(wxChoice, wxControlWithItems,"wx/choice.h")
6a89f9ee 74
3ff066a4 75wxBEGIN_PROPERTIES_TABLE(wxChoice)
3dfb79a6 76 wxEVENT_PROPERTY( Select , wxEVT_COMMAND_CHOICE_SELECTED , wxCommandEvent )
c5ca409b 77
af498247 78 wxPROPERTY( Font , wxFont , SetFont , GetFont , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
3ff066a4 79 wxPROPERTY_COLLECTION( Choices , wxArrayString , wxString , AppendString , GetStrings , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
3dfb79a6 80 wxPROPERTY( Selection ,int, SetSelection, GetSelection, EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
af498247 81 wxPROPERTY_FLAGS( WindowStyle , wxChoiceStyle , long , SetWindowStyleFlag , GetWindowStyleFlag , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
3ff066a4 82wxEND_PROPERTIES_TABLE()
6a89f9ee 83
3ff066a4
SC
84wxBEGIN_HANDLERS_TABLE(wxChoice)
85wxEND_HANDLERS_TABLE()
2bda0e17 86
3dfb79a6 87wxCONSTRUCTOR_4( wxChoice , wxWindow* , Parent , wxWindowID , Id , wxPoint , Position , wxSize , Size )
6a89f9ee 88#else
0ad2a19e 89IMPLEMENT_DYNAMIC_CLASS(wxChoice, wxControlWithItems)
6a89f9ee 90#endif
066f1b7a 91/*
3dfb79a6
VZ
92 TODO PROPERTIES
93 selection (long)
94 content (list)
95 item
066f1b7a
SC
96*/
97
8d99be5f
VZ
98// ============================================================================
99// implementation
100// ============================================================================
101
102// ----------------------------------------------------------------------------
103// creation
104// ----------------------------------------------------------------------------
105
106bool wxChoice::Create(wxWindow *parent,
107 wxWindowID id,
108 const wxPoint& pos,
109 const wxSize& size,
110 int n, const wxString choices[],
111 long style,
112 const wxValidator& validator,
113 const wxString& name)
2bda0e17 114{
f6bcfd97 115 // Experience shows that wxChoice vs. wxComboBox distinction confuses
8d99be5f
VZ
116 // quite a few people - try to help them
117 wxASSERT_MSG( !(style & wxCB_DROPDOWN) &&
118 !(style & wxCB_READONLY) &&
119 !(style & wxCB_SIMPLE),
f6bcfd97
BP
120 _T("this style flag is ignored by wxChoice, you ")
121 _T("probably want to use a wxComboBox") );
2bda0e17 122
71e57cd6
VZ
123 return CreateAndInit(parent, id, pos, size, n, choices, style,
124 validator, name);
125}
126
cc61d2eb
VZ
127bool wxChoice::CreateAndInit(wxWindow *parent,
128 wxWindowID id,
71e57cd6 129 const wxPoint& pos,
3dfb79a6 130 const wxSize& size,
71e57cd6
VZ
131 int n, const wxString choices[],
132 long style,
133 const wxValidator& validator,
134 const wxString& name)
135{
136 // initialize wxControl
137 if ( !CreateControl(parent, id, pos, size, style, validator, name) )
02b7b6b0 138 return false;
2bda0e17 139
71e57cd6 140 // now create the real HWND
f31a4098 141 if ( !MSWCreateControl(wxT("COMBOBOX"), wxEmptyString, pos, size) )
02b7b6b0 142 return false;
71e57cd6
VZ
143
144
145 // choice/combobox normally has "white" (depends on colour scheme, of
146 // course) background rather than inheriting the parent's background
a756f210 147 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
c92d798f 148
cc61d2eb 149 // initialize the controls contents
8d99be5f
VZ
150 for ( int i = 0; i < n; i++ )
151 {
152 Append(choices[i]);
153 }
2bda0e17 154
cc61d2eb 155 // and now we may finally size the control properly (if needed)
170acdc9 156 SetInitialSize(size);
cc61d2eb 157
02b7b6b0 158 return true;
2bda0e17
KB
159}
160
584ad2a3
MB
161bool wxChoice::Create(wxWindow *parent,
162 wxWindowID id,
163 const wxPoint& pos,
164 const wxSize& size,
165 const wxArrayString& choices,
166 long style,
167 const wxValidator& validator,
168 const wxString& name)
169{
170 wxCArrayString chs(choices);
171 return Create(parent, id, pos, size, chs.GetCount(), chs.GetStrings(),
172 style, validator, name);
173}
174
4b17d2e3
DS
175bool wxChoice::MSWShouldPreProcessMessage(WXMSG *pMsg)
176{
177 MSG *msg = (MSG *) pMsg;
178
dc302518
DS
179 // if the dropdown list is visible, don't preprocess certain keys
180 if ( msg->message == WM_KEYDOWN
181 && (msg->wParam == VK_ESCAPE || msg->wParam == VK_RETURN) )
4b17d2e3
DS
182 {
183 if (::SendMessage(GetHwndOf(this), CB_GETDROPPEDSTATE, 0, 0))
184 {
185 return false;
186 }
187 }
188
189 return wxControl::MSWShouldPreProcessMessage(pMsg);
190}
191
71e57cd6
VZ
192WXDWORD wxChoice::MSWGetStyle(long style, WXDWORD *exstyle) const
193{
194 // we never have an external border
195 WXDWORD msStyle = wxControl::MSWGetStyle
196 (
197 (style & ~wxBORDER_MASK) | wxBORDER_NONE, exstyle
198 );
199
200 // WS_CLIPSIBLINGS is useful with wxChoice and doesn't seem to result in
201 // any problems
202 msStyle |= WS_CLIPSIBLINGS;
203
204 // wxChoice-specific styles
205 msStyle |= CBS_DROPDOWNLIST | WS_HSCROLL | WS_VSCROLL;
206 if ( style & wxCB_SORT )
207 msStyle |= CBS_SORT;
208
209 return msStyle;
210}
211
8ee9d618
VZ
212wxChoice::~wxChoice()
213{
a236aa20 214 Clear();
8ee9d618
VZ
215}
216
8d99be5f
VZ
217// ----------------------------------------------------------------------------
218// adding/deleting items to/from the list
219// ----------------------------------------------------------------------------
2bda0e17 220
a236aa20
VZ
221int wxChoice::DoInsertItems(const wxArrayStringsAdapter& items,
222 unsigned int pos,
223 void **clientData, wxClientDataType type)
8d99be5f 224{
a236aa20 225 MSWAllocStorage(items, CB_INITSTORAGE);
def6fb9b 226
a236aa20 227 const bool append = pos == GetCount();
2bda0e17 228
a236aa20
VZ
229 // use CB_ADDSTRING when appending at the end to make sure the control is
230 // resorted if it has wxCB_SORT style
231 const unsigned msg = append ? CB_ADDSTRING : CB_INSERTSTRING;
243dbf1a 232
a236aa20
VZ
233 if ( append )
234 pos = 0;
235
236 int n = wxNOT_FOUND;
237 const unsigned numItems = items.GetCount();
238 for ( unsigned i = 0; i < numItems; ++i )
71e57cd6 239 {
a236aa20
VZ
240 n = MSWInsertOrAppendItem(pos, items[i], msg);
241 if ( n == wxNOT_FOUND )
242 return n;
243
244 if ( !append )
245 pos++;
246
247 AssignNewItemClientData(n, clientData, i, type);
71e57cd6 248 }
243dbf1a 249
a236aa20
VZ
250 // we need to refresh our size in order to have enough space for the
251 // newly added items
252 if ( !IsFrozen() )
253 UpdateVisibleHeight();
254
31582e4e 255 InvalidateBestSize();
a236aa20 256
243dbf1a
VZ
257 return n;
258}
259
a236aa20 260void wxChoice::DoDeleteOneItem(unsigned int n)
2bda0e17 261{
8228b893 262 wxCHECK_RET( IsValid(n), wxT("invalid item index in wxChoice::Delete") );
8d99be5f
VZ
263
264 SendMessage(GetHwnd(), CB_DELETESTRING, n, 0);
71e57cd6 265
e0e3a32d
VZ
266 if ( !IsFrozen() )
267 UpdateVisibleHeight();
31582e4e
RD
268
269 InvalidateBestSize();
2bda0e17
KB
270}
271
a236aa20 272void wxChoice::DoClear()
8ee9d618 273{
8ee9d618 274 SendMessage(GetHwnd(), CB_RESETCONTENT, 0, 0);
71e57cd6 275
e0e3a32d
VZ
276 if ( !IsFrozen() )
277 UpdateVisibleHeight();
31582e4e
RD
278
279 InvalidateBestSize();
8ee9d618
VZ
280}
281
8d99be5f
VZ
282// ----------------------------------------------------------------------------
283// selection
284// ----------------------------------------------------------------------------
2bda0e17 285
8d99be5f 286int wxChoice::GetSelection() const
6ba93d23
VZ
287{
288 // if m_lastAcceptedSelection is set, it means that the dropdown is
289 // currently shown and that we want to use the last "permanent" selection
290 // instead of whatever is under the mouse pointer currently
291 //
292 // otherwise, get the selection from the control
293 return m_lastAcceptedSelection == wxID_NONE ? GetCurrentSelection()
294 : m_lastAcceptedSelection;
295}
296
297int wxChoice::GetCurrentSelection() const
2bda0e17 298{
8d99be5f 299 return (int)SendMessage(GetHwnd(), CB_GETCURSEL, 0, 0);
2bda0e17
KB
300}
301
debe6624 302void wxChoice::SetSelection(int n)
2bda0e17 303{
8d99be5f
VZ
304 SendMessage(GetHwnd(), CB_SETCURSEL, n, 0);
305}
306
307// ----------------------------------------------------------------------------
308// string list functions
309// ----------------------------------------------------------------------------
310
aa61d352 311unsigned int wxChoice::GetCount() const
8d99be5f 312{
aa61d352 313 return (unsigned int)SendMessage(GetHwnd(), CB_GETCOUNT, 0, 0);
2bda0e17
KB
314}
315
11e62fe6 316int wxChoice::FindString(const wxString& s, bool bCase) const
2bda0e17
KB
317{
318#if defined(__WATCOMC__) && defined(__WIN386__)
8d99be5f
VZ
319 // For some reason, Watcom in WIN386 mode crashes in the CB_FINDSTRINGEXACT message.
320 // wxChoice::Do it the long way instead.
aa61d352
VZ
321 unsigned int count = GetCount();
322 for ( unsigned int i = 0; i < count; i++ )
8d99be5f
VZ
323 {
324 // as CB_FINDSTRINGEXACT is case insensitive, be case insensitive too
aa61d352 325 if (GetString(i).IsSameAs(s, bCase))
8d99be5f
VZ
326 return i;
327 }
328
329 return wxNOT_FOUND;
330#else // !Watcom
4cd1ed99
RN
331 //TODO: Evidently some MSW versions (all?) don't like empty strings
332 //passed to SendMessage, so we have to do it ourselves in that case
beedefb9 333 if ( s.empty() )
4cd1ed99 334 {
aa61d352
VZ
335 unsigned int count = GetCount();
336 for ( unsigned int i = 0; i < count; i++ )
11e62fe6 337 {
aa61d352 338 if (GetString(i).empty())
11e62fe6
WS
339 return i;
340 }
341
342 return wxNOT_FOUND;
343 }
344 else if (bCase)
345 {
346 // back to base class search for not native search type
347 return wxItemContainerImmutable::FindString( s, bCase );
4cd1ed99
RN
348 }
349 else
350 {
11e62fe6 351 int pos = (int)SendMessage(GetHwnd(), CB_FINDSTRINGEXACT,
c9f78968 352 (WPARAM)-1, (LPARAM)s.wx_str());
f31a4098 353
11e62fe6 354 return pos == LB_ERR ? wxNOT_FOUND : pos;
4cd1ed99 355 }
8d99be5f 356#endif // Watcom/!Watcom
2bda0e17
KB
357}
358
aa61d352 359void wxChoice::SetString(unsigned int n, const wxString& s)
6c8a980f 360{
8228b893 361 wxCHECK_RET( IsValid(n), wxT("invalid item index in wxChoice::SetString") );
2b5f62a0
VZ
362
363 // we have to delete and add back the string as there is no way to change a
364 // string in place
365
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);
c9f78968 378 ::SendMessage(GetHwnd(), CB_INSERTSTRING, n, (LPARAM)s.wx_str() );
2b5f62a0
VZ
379
380 if ( data )
381 {
382 DoSetItemClientData(n, data);
383 }
384 //else: it's already NULL by default
31582e4e
RD
385
386 InvalidateBestSize();
6c8a980f
VZ
387}
388
aa61d352 389wxString wxChoice::GetString(unsigned int n) const
2bda0e17 390{
478cabab
VZ
391 int len = (int)::SendMessage(GetHwnd(), CB_GETLBTEXTLEN, n, 0);
392
6c8a980f 393 wxString str;
478cabab
VZ
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 {
f6bcfd97 404 wxLogLastError(wxT("SendMessage(CB_GETLBTEXT)"));
21d72d17 405 }
4438caf4 406 }
2bda0e17 407
4438caf4
VZ
408 return str;
409}
2bda0e17 410
8d99be5f
VZ
411// ----------------------------------------------------------------------------
412// client data
413// ----------------------------------------------------------------------------
414
aa61d352 415void wxChoice::DoSetItemClientData(unsigned int n, void* clientData)
8d99be5f 416{
2b5f62a0
VZ
417 if ( ::SendMessage(GetHwnd(), CB_SETITEMDATA,
418 n, (LPARAM)clientData) == CB_ERR )
8d99be5f 419 {
223d09f6 420 wxLogLastError(wxT("CB_SETITEMDATA"));
8d99be5f
VZ
421 }
422}
423
aa61d352 424void* wxChoice::DoGetItemClientData(unsigned int n) const
8d99be5f
VZ
425{
426 LPARAM rc = SendMessage(GetHwnd(), CB_GETITEMDATA, n, 0);
427 if ( rc == CB_ERR )
428 {
223d09f6 429 wxLogLastError(wxT("CB_GETITEMDATA"));
8d99be5f
VZ
430
431 // unfortunately, there is no way to return an error code to the user
8ee9d618 432 rc = (LPARAM) NULL;
8d99be5f
VZ
433 }
434
435 return (void *)rc;
436}
437
8d99be5f
VZ
438// ----------------------------------------------------------------------------
439// wxMSW specific helpers
440// ----------------------------------------------------------------------------
441
71e57cd6
VZ
442void wxChoice::UpdateVisibleHeight()
443{
e6968367 444 // be careful to not change the width here
02b7b6b0 445 DoSetSize(wxDefaultCoord, wxDefaultCoord, wxDefaultCoord, GetSize().y, wxSIZE_USE_EXISTING);
71e57cd6
VZ
446}
447
18c50997
VZ
448void 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
d99957b6
VZ
466void wxChoice::DoGetSize(int *w, int *h) const
467{
94a77ff7
VZ
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
02b7b6b0 472 wxControl::DoGetSize(w, h);
d99957b6
VZ
473}
474
4438caf4 475void wxChoice::DoSetSize(int x, int y,
d99957b6 476 int width, int height,
4438caf4
VZ
477 int sizeFlags)
478{
36043052 479 int heightOrig = height;
f4322df6 480
d4445d24
JS
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;
a8e65eee 493
d4445d24
JS
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
5637cc35
JS
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
d4445d24
JS
523 // This solution works on XP, but causes choice/combobox lists to be
524 // too short on W2K and earlier.
525#if 0
e3e78de1
VZ
526 int widthCurrent, heightCurrent;
527 DoGetSize(&widthCurrent, &heightCurrent);
528
d99957b6
VZ
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
e3e78de1 532 if ( height != wxDefaultCoord && height != heightCurrent )
d99957b6 533 {
e3e78de1
VZ
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
aa61d352 536 unsigned int nItems = GetCount();
e3e78de1
VZ
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);
d99957b6 545 }
e3e78de1 546 else // keep the same height as now
d6959d6f 547 {
e3e78de1
VZ
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
d6959d6f 568 // We cannot pass wxDefaultCoord as height to wxControl. wxControl uses
f31a4098
WS
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
d6959d6f
DS
573 // 1 pixel. We have to determine the default height ourselves and call
574 // wxControl with that value instead.
e3e78de1
VZ
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
d6959d6f 579 RECT r;
e3e78de1
VZ
580 if ( ::SendMessage(GetHwnd(), CB_GETDROPPEDCONTROLRECT, 0, (LPARAM) &r)
581 && r.bottom < 30000 )
d6959d6f 582 {
e3e78de1 583 height = heightCurrent + r.bottom - r.top;
d6959d6f
DS
584 }
585 }
d99957b6
VZ
586
587 wxControl::DoSetSize(x, y, width, height, sizeFlags);
ef036883 588#endif
4438caf4 589}
2bda0e17 590
882a8f40 591wxSize wxChoice::DoGetBestSize() const
4438caf4
VZ
592{
593 // find the widest string
4438caf4 594 int wChoice = 0;
aa61d352
VZ
595 const unsigned int nItems = GetCount();
596 for ( unsigned int i = 0; i < nItems; i++ )
2bda0e17 597 {
d99957b6
VZ
598 int wLine;
599 GetTextExtent(GetString(i), &wLine, NULL);
4438caf4
VZ
600 if ( wLine > wChoice )
601 wChoice = wLine;
2bda0e17 602 }
fd3f686c 603
4438caf4
VZ
604 // give it some reasonable default value if there are no strings in the
605 // list
606 if ( wChoice == 0 )
607 wChoice = 100;
2bda0e17 608
d99957b6
VZ
609 // the combobox should be slightly larger than the widest string
610 wChoice += 5*GetCharWidth();
2bda0e17 611
1d13cc5d
RD
612 wxSize best(wChoice, EDIT_HEIGHT_FROM_CHAR_HEIGHT(GetCharHeight()));
613 CacheBestSize(best);
614 return best;
2bda0e17
KB
615}
616
c140b7e7 617WXLRESULT wxChoice::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
2bda0e17 618{
f499e614 619 switch ( nMsg )
2bda0e17 620 {
f499e614
VZ
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 {
f499e614
VZ
642 WXHDC hdc;
643 WXHWND hwnd;
9f368d0d 644 UnpackCtlColor(wParam, lParam, &hdc, &hwnd);
f499e614 645
2bae4332 646 WXHBRUSH hbr = MSWControlColor((WXHDC)hdc, hwnd);
48fa6bd3
VZ
647 if ( hbr )
648 return (WXLRESULT)hbr;
649 //else: fall through to default window proc
f499e614 650 }
2bda0e17
KB
651 }
652
8d99be5f 653 return wxWindow::MSWWindowProc(nMsg, wParam, lParam);
2bda0e17
KB
654}
655
8d99be5f 656bool wxChoice::MSWCommand(WXUINT param, WXWORD WXUNUSED(id))
2bda0e17 657{
c11f0412
VZ
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 */
6ba93d23 686 switch ( param )
2bda0e17 687 {
6ba93d23 688 case CBN_DROPDOWN:
c11f0412
VZ
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
6ba93d23 693 m_lastAcceptedSelection = GetCurrentSelection();
f785bfdd
VZ
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 }
6ba93d23 701 break;
2bda0e17 702
6ba93d23 703 case CBN_CLOSEUP:
c11f0412
VZ
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 }
6ba93d23
VZ
712 break;
713
c11f0412
VZ
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
6ba93d23
VZ
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());
593ac33e 730 InitCommandEventWithItems(event, n);
6ba93d23
VZ
731 }
732
733 ProcessCommand(event);
734 }
c11f0412
VZ
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;
8c1c5302 745 }
2bda0e17 746
c11f0412 747 return true;
8d99be5f 748}
2bda0e17 749
2bae4332 750WXHBRUSH wxChoice::MSWControlColor(WXHDC hDC, WXHWND hWnd)
f6bcfd97 751{
48fa6bd3
VZ
752 if ( !IsEnabled() )
753 return MSWControlColorDisabled(hDC);
f6bcfd97 754
2bae4332 755 return wxChoiceBase::MSWControlColor(hDC, hWnd);
f6bcfd97
BP
756}
757
3180bc0e 758#endif // wxUSE_CHOICE && !(__SMARTPHONE__ && __WXWINCE__)