]> git.saurik.com Git - wxWidgets.git/blob - src/msw/wince/choicece.cpp
Applied patch to avoid g_lib wanrings.
[wxWidgets.git] / src / msw / wince / choicece.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/wince/choicece.cpp
3 // Purpose: wxChoice implementation for Smartphones
4 // Author: Wlodzimierz ABX Skiba
5 // Modified by:
6 // Created: 29.07.2004
7 // RCS-ID: $Id$
8 // Copyright: (c) Wlodzimierz Skiba
9 // License: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
11
12
13 // ============================================================================
14 // declarations
15 // ============================================================================
16
17 // ----------------------------------------------------------------------------
18 // headers
19 // ----------------------------------------------------------------------------
20
21 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
22 #pragma implementation "choicece.h"
23 #endif
24
25 // For compilers that support precompilation, includes "wx.h".
26 #include "wx/wxprec.h"
27
28 #ifdef __BORLANDC__
29 #pragma hdrstop
30 #endif
31
32 #ifndef WX_PRECOMP
33 #include "wx/choice.h"
34 #endif
35
36 #include "wx/spinbutt.h" // for wxSpinnerBestSize
37
38 #include <commctrl.h>
39 #include "wx/msw/missing.h"
40 #include "wx/msw/winundef.h"
41
42 #if wxUSE_CHOICE && defined(__SMARTPHONE__)
43
44 #if wxUSE_EXTENDED_RTTI
45 // TODO
46 #else
47 IMPLEMENT_DYNAMIC_CLASS(wxChoice, wxControl)
48 #endif
49
50 #define GetBuddyHwnd() (HWND)(m_hwndBuddy)
51
52 #define IsVertical(wxStyle) ( (wxStyle & wxSP_HORIZONTAL) != wxSP_HORIZONTAL )
53
54 // ----------------------------------------------------------------------------
55 // constants
56 // ----------------------------------------------------------------------------
57
58 // the margin between the up-down control and its buddy (can be arbitrary,
59 // choose what you like - or may be decide during run-time depending on the
60 // font size?)
61 static const int MARGIN_BETWEEN = 0;
62
63 // ============================================================================
64 // implementation
65 // ============================================================================
66
67 wxArrayChoiceSpins wxChoice::ms_allChoiceSpins;
68
69 // ----------------------------------------------------------------------------
70 // wnd proc for the buddy text ctrl
71 // ----------------------------------------------------------------------------
72
73 LRESULT APIENTRY _EXPORT wxBuddyChoiceWndProc(HWND hwnd,
74 UINT message,
75 WPARAM wParam,
76 LPARAM lParam)
77 {
78 wxChoice *spin = (wxChoice *)wxGetWindowUserData(hwnd);
79
80 // forward some messages (the key and focus ones only so far) to
81 // the spin ctrl
82 switch ( message )
83 {
84 case WM_SETFOCUS:
85 // if the focus comes from the spin control itself, don't set it
86 // back to it -- we don't want to go into an infinite loop
87 if ( (WXHWND)wParam == spin->GetHWND() )
88 break;
89 //else: fall through
90
91 case WM_KILLFOCUS:
92 case WM_CHAR:
93 case WM_DEADCHAR:
94 case WM_KEYUP:
95 case WM_KEYDOWN:
96 spin->MSWWindowProc(message, wParam, lParam);
97
98 // The control may have been deleted at this point, so check.
99 if ( !::IsWindow(hwnd) || wxGetWindowUserData(hwnd) != spin )
100 return 0;
101 break;
102
103 case WM_GETDLGCODE:
104 // we want to get WXK_RETURN in order to generate the event for it
105 return DLGC_WANTCHARS;
106 }
107
108 return ::CallWindowProc(CASTWNDPROC spin->GetBuddyWndProc(),
109 hwnd, message, wParam, lParam);
110 }
111
112 // ----------------------------------------------------------------------------
113 // creation
114 // ----------------------------------------------------------------------------
115
116 bool wxChoice::Create(wxWindow *parent,
117 wxWindowID id,
118 const wxPoint& pos,
119 const wxSize& size,
120 int n, const wxString choices[],
121 long style,
122 const wxValidator& validator,
123 const wxString& name)
124 {
125 return CreateAndInit(parent, id, pos, size, n, choices, style,
126 validator, name);
127 }
128
129 bool wxChoice::CreateAndInit(wxWindow *parent,
130 wxWindowID id,
131 const wxPoint& pos,
132 const wxSize& size,
133 int n, const wxString choices[],
134 long style,
135 const wxValidator& validator,
136 const wxString& name)
137 {
138 if ( !(style & wxSP_VERTICAL) )
139 style |= wxSP_HORIZONTAL;
140
141 if ( (style & wxBORDER_MASK) == wxBORDER_DEFAULT )
142 style |= wxBORDER_SIMPLE;
143
144 style |= wxSP_ARROW_KEYS;
145
146 SetWindowStyle(style);
147
148 WXDWORD exStyle = 0;
149 WXDWORD msStyle = MSWGetStyle(GetWindowStyle(), & exStyle) ;
150
151 wxSize sizeText(size), sizeBtn(size);
152 sizeBtn.x = GetBestSpinerSize(IsVertical(style)).x;
153
154 sizeBtn.x;
155
156 if ( sizeText.x == wxDefaultCoord )
157 {
158 // DEFAULT_ITEM_WIDTH is the default width for the text control
159 sizeText.x = DEFAULT_ITEM_WIDTH + MARGIN_BETWEEN + sizeBtn.x;
160 }
161
162 sizeText.x -= sizeBtn.x + MARGIN_BETWEEN;
163 if ( sizeText.x <= 0 )
164 {
165 wxLogDebug(_T("not enough space for wxSpinCtrl!"));
166 }
167
168 wxPoint posBtn(pos);
169 posBtn.x += sizeText.x + MARGIN_BETWEEN;
170
171 // we must create the list control before the spin button for the purpose
172 // of the dialog navigation: if there is a static text just before the spin
173 // control, activating it by Alt-letter should give focus to the text
174 // control, not the spin and the dialog navigation code will give focus to
175 // the next control (at Windows level), not the one after it
176
177 // create the text window
178
179 m_hwndBuddy = (WXHWND)::CreateWindowEx
180 (
181 exStyle, // sunken border
182 _T("LISTBOX"), // window class
183 NULL, // no window title
184 msStyle, // style (will be shown later)
185 pos.x, pos.y, // position
186 0, 0, // size (will be set later)
187 GetHwndOf(parent), // parent
188 (HMENU)-1, // control id
189 wxGetInstance(), // app instance
190 NULL // unused client data
191 );
192
193 if ( !m_hwndBuddy )
194 {
195 wxLogLastError(wxT("CreateWindow(buddy text window)"));
196
197 return false;
198 }
199
200 // initialize wxControl
201 if ( !CreateControl(parent, id, posBtn, sizeBtn, style, validator, name) )
202 return false;
203
204 // now create the real HWND
205 WXDWORD spiner_style = WS_VISIBLE |
206 UDS_ALIGNRIGHT |
207 UDS_ARROWKEYS |
208 UDS_SETBUDDYINT |
209 UDS_EXPANDABLE;
210
211 if ( !IsVertical(style) )
212 spiner_style |= UDS_HORZ;
213
214 if ( style & wxSP_WRAP )
215 spiner_style |= UDS_WRAP;
216
217 if ( !MSWCreateControl(UPDOWN_CLASS, spiner_style, posBtn, sizeBtn, _T(""), 0) )
218 return false;
219
220 // subclass the text ctrl to be able to intercept some events
221 wxSetWindowUserData(GetBuddyHwnd(), this);
222 m_wndProcBuddy = (WXFARPROC)wxSetWindowProc(GetBuddyHwnd(),
223 wxBuddyChoiceWndProc);
224
225 // set up fonts and colours (This is nomally done in MSWCreateControl)
226 InheritAttributes();
227 if (!m_hasFont)
228 SetFont(GetDefaultAttributes().font);
229
230 // set the size of the text window - can do it only now, because we
231 // couldn't call DoGetBestSize() before as font wasn't set
232 if ( sizeText.y <= 0 )
233 {
234 int cx, cy;
235 wxGetCharSize(GetHWND(), &cx, &cy, GetFont());
236
237 sizeText.y = EDIT_HEIGHT_FROM_CHAR_HEIGHT(cy);
238 }
239
240 SetBestSize(size);
241
242 (void)::ShowWindow(GetBuddyHwnd(), SW_SHOW);
243
244 // associate the list window with the spin button
245 (void)::SendMessage(GetHwnd(), UDM_SETBUDDY, (WPARAM)GetBuddyHwnd(), 0);
246
247 // do it after finishing with m_hwndBuddy creation to avoid generating
248 // initial wxEVT_COMMAND_TEXT_UPDATED message
249 ms_allChoiceSpins.Add(this);
250
251 // initialize the controls contents
252 for ( int i = 0; i < n; i++ )
253 {
254 Append(choices[i]);
255 }
256
257 return true;
258 }
259
260 bool wxChoice::Create(wxWindow *parent,
261 wxWindowID id,
262 const wxPoint& pos,
263 const wxSize& size,
264 const wxArrayString& choices,
265 long style,
266 const wxValidator& validator,
267 const wxString& name)
268 {
269 wxCArrayString chs(choices);
270 return Create(parent, id, pos, size, chs.GetCount(), chs.GetStrings(),
271 style, validator, name);
272 }
273
274 WXDWORD wxChoice::MSWGetStyle(long style, WXDWORD *exstyle) const
275 {
276 // we never have an external border
277 WXDWORD msStyle = wxControl::MSWGetStyle
278 (
279 (style & ~wxBORDER_MASK) | wxBORDER_NONE, exstyle
280 );
281
282 msStyle |= WS_VISIBLE;
283
284 // wxChoice-specific styles
285 msStyle |= LBS_NOINTEGRALHEIGHT;
286 if ( style & wxCB_SORT )
287 msStyle |= LBS_SORT;
288
289 return msStyle;
290 }
291
292 wxChoice::~wxChoice()
293 {
294 Free();
295 }
296
297 // ----------------------------------------------------------------------------
298 // adding/deleting items to/from the list
299 // ----------------------------------------------------------------------------
300
301 int wxChoice::DoAppend(const wxString& item)
302 {
303 int n = (int)SendMessage(GetBuddyHwnd(), LB_ADDSTRING, 0, (LPARAM)item.c_str());
304
305 if ( n == LB_ERR )
306 {
307 wxLogLastError(wxT("SendMessage(LB_ADDSTRING)"));
308 }
309
310 return n;
311 }
312
313 int wxChoice::DoInsert(const wxString& item, int pos)
314 {
315 wxCHECK_MSG(!(GetWindowStyle() & wxCB_SORT), -1, wxT("can't insert into choice"));
316 wxCHECK_MSG((pos>=0) && (pos<=GetCount()), -1, wxT("invalid index"));
317
318 int n = (int)SendMessage(GetBuddyHwnd(), LB_INSERTSTRING, pos, (LPARAM)item.c_str());
319 if ( n == LB_ERR )
320 {
321 wxLogLastError(wxT("SendMessage(LB_INSERTSTRING)"));
322 }
323
324 return n;
325 }
326
327 void wxChoice::Delete(int n)
328 {
329 wxCHECK_RET( n < GetCount(), wxT("invalid item index in wxChoice::Delete") );
330
331 if ( HasClientObjectData() )
332 {
333 delete GetClientObject(n);
334 }
335
336 SendMessage(GetBuddyHwnd(), LB_DELETESTRING, n, 0);
337 }
338
339 void wxChoice::Clear()
340 {
341 Free();
342
343 SendMessage(GetBuddyHwnd(), LB_RESETCONTENT, 0, 0);
344 }
345
346 void wxChoice::Free()
347 {
348 if ( HasClientObjectData() )
349 {
350 size_t count = GetCount();
351 for ( size_t n = 0; n < count; n++ )
352 {
353 delete GetClientObject(n);
354 }
355 }
356 }
357
358 // ----------------------------------------------------------------------------
359 // selection
360 // ----------------------------------------------------------------------------
361
362 int wxChoice::GetSelection() const
363 {
364 return (int)SendMessage(GetBuddyHwnd(), LB_GETCURSEL, 0, 0);
365 }
366
367 void wxChoice::SetSelection(int n)
368 {
369 SendMessage(GetBuddyHwnd(), LB_SETCURSEL, n, 0);
370 }
371
372 // ----------------------------------------------------------------------------
373 // string list functions
374 // ----------------------------------------------------------------------------
375
376 int wxChoice::GetCount() const
377 {
378 return (int)SendMessage(GetBuddyHwnd(), LB_GETCOUNT, 0, 0);
379 }
380
381 int wxChoice::FindString(const wxString& s) const
382 {
383 int pos = (int)SendMessage(GetBuddyHwnd(), LB_FINDSTRINGEXACT,
384 (WPARAM)-1, (LPARAM)s.c_str());
385
386 return pos == LB_ERR ? wxNOT_FOUND : pos;
387 }
388
389 void wxChoice::SetString(int n, const wxString& s)
390 {
391 wxCHECK_RET( n >= 0 && n < GetCount(),
392 wxT("invalid item index in wxChoice::SetString") );
393
394 // we have to delete and add back the string as there is no way to change a
395 // string in place
396
397 // we need to preserve the client data
398 void *data;
399 if ( m_clientDataItemsType != wxClientData_None )
400 {
401 data = DoGetItemClientData(n);
402 }
403 else // no client data
404 {
405 data = NULL;
406 }
407
408 ::SendMessage(GetBuddyHwnd(), LB_DELETESTRING, n, 0);
409 ::SendMessage(GetBuddyHwnd(), LB_INSERTSTRING, n, (LPARAM)s.c_str() );
410
411 if ( data )
412 {
413 DoSetItemClientData(n, data);
414 }
415 //else: it's already NULL by default
416 }
417
418 wxString wxChoice::GetString(int n) const
419 {
420 int len = (int)::SendMessage(GetBuddyHwnd(), LB_GETTEXTLEN, n, 0);
421
422 wxString str;
423 if ( len != LB_ERR && len > 0 )
424 {
425 if ( ::SendMessage
426 (
427 GetBuddyHwnd(),
428 LB_GETTEXT,
429 n,
430 (LPARAM)(wxChar *)wxStringBuffer(str, len)
431 ) == LB_ERR )
432 {
433 wxLogLastError(wxT("SendMessage(LB_GETLBTEXT)"));
434 }
435 }
436
437 return str;
438 }
439
440 // ----------------------------------------------------------------------------
441 // client data
442 // ----------------------------------------------------------------------------
443
444 void wxChoice::DoSetItemClientData( int n, void* clientData )
445 {
446 if ( ::SendMessage(GetHwnd(), LB_SETITEMDATA,
447 n, (LPARAM)clientData) == LB_ERR )
448 {
449 wxLogLastError(wxT("LB_SETITEMDATA"));
450 }
451 }
452
453 void* wxChoice::DoGetItemClientData( int n ) const
454 {
455 LPARAM rc = SendMessage(GetHwnd(), LB_GETITEMDATA, n, 0);
456 if ( rc == LB_ERR )
457 {
458 wxLogLastError(wxT("LB_GETITEMDATA"));
459
460 // unfortunately, there is no way to return an error code to the user
461 rc = (LPARAM) NULL;
462 }
463
464 return (void *)rc;
465 }
466
467 void wxChoice::DoSetItemClientObject( int n, wxClientData* clientData )
468 {
469 DoSetItemClientData(n, clientData);
470 }
471
472 wxClientData* wxChoice::DoGetItemClientObject( int n ) const
473 {
474 return (wxClientData *)DoGetItemClientData(n);
475 }
476
477 // ----------------------------------------------------------------------------
478 // size calculations
479 // ----------------------------------------------------------------------------
480
481 wxSize wxChoice::DoGetBestSize() const
482 {
483 wxSize sizeBtn = GetBestSpinerSize(IsVertical(GetWindowStyle()));
484 sizeBtn.x += DEFAULT_ITEM_WIDTH + MARGIN_BETWEEN;
485
486 int y;
487 wxGetCharSize(GetHWND(), NULL, &y, GetFont());
488 y = EDIT_HEIGHT_FROM_CHAR_HEIGHT(y);
489
490 // JACS: we should always use the height calculated
491 // from above, because otherwise we'll get a spin control
492 // that's too big. So never use the height calculated
493 // from wxSpinButton::DoGetBestSize().
494
495 // if ( sizeBtn.y < y )
496 {
497 // make the text tall enough
498 sizeBtn.y = y;
499 }
500
501 return sizeBtn;
502 }
503
504 void wxChoice::DoMoveWindow(int x, int y, int width, int height)
505 {
506 int widthBtn = GetBestSpinerSize(IsVertical(GetWindowStyle())).x;
507 int widthText = width - widthBtn - MARGIN_BETWEEN;
508 if ( widthText <= 0 )
509 {
510 wxLogDebug(_T("not enough space for wxSpinCtrl!"));
511 }
512
513 if ( !::MoveWindow(GetBuddyHwnd(), x, y, widthText, height, TRUE) )
514 {
515 wxLogLastError(wxT("MoveWindow(buddy)"));
516 }
517
518 x += widthText + MARGIN_BETWEEN;
519 if ( !::MoveWindow(GetHwnd(), x, y, widthBtn, height, TRUE) )
520 {
521 wxLogLastError(wxT("MoveWindow"));
522 }
523 }
524
525 // get total size of the control
526 void wxChoice::DoGetSize(int *x, int *y) const
527 {
528 RECT spinrect, textrect, ctrlrect;
529 GetWindowRect(GetHwnd(), &spinrect);
530 GetWindowRect(GetBuddyHwnd(), &textrect);
531 UnionRect(&ctrlrect, &textrect, &spinrect);
532
533 if ( x )
534 *x = ctrlrect.right - ctrlrect.left;
535 if ( y )
536 *y = ctrlrect.bottom - ctrlrect.top;
537 }
538
539 void wxChoice::DoGetPosition(int *x, int *y) const
540 {
541 // hack: pretend that our HWND is the text control just for a moment
542 WXHWND hWnd = GetHWND();
543 wxConstCast(this, wxChoice)->m_hWnd = m_hwndBuddy;
544
545 wxChoiceBase::DoGetPosition(x, y);
546
547 wxConstCast(this, wxChoice)->m_hWnd = hWnd;
548 }
549
550 #endif // wxUSE_CHOICE && __SMARTPHONE__