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