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