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