]> git.saurik.com Git - wxWidgets.git/blame - src/msw/wince/choicece.cpp
Massive reworking of wxMediaCtrl code - backend everything, search for backends via...
[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
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
8300f815 42#if wxUSE_CHOICE && defined(__SMARTPHONE__) && defined(__WXWINCE__)
1550d5f8
WS
43
44#if wxUSE_EXTENDED_RTTI
45// TODO
46#else
47IMPLEMENT_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?)
61static const int MARGIN_BETWEEN = 0;
62
63// ============================================================================
64// implementation
65// ============================================================================
66
67wxArrayChoiceSpins wxChoice::ms_allChoiceSpins;
68
69// ----------------------------------------------------------------------------
70// wnd proc for the buddy text ctrl
71// ----------------------------------------------------------------------------
72
73LRESULT 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
116bool 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
129bool 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
1550d5f8
WS
154 if ( sizeText.x == wxDefaultCoord )
155 {
156 // DEFAULT_ITEM_WIDTH is the default width for the text control
157 sizeText.x = DEFAULT_ITEM_WIDTH + MARGIN_BETWEEN + sizeBtn.x;
158 }
159
160 sizeText.x -= sizeBtn.x + MARGIN_BETWEEN;
161 if ( sizeText.x <= 0 )
162 {
163 wxLogDebug(_T("not enough space for wxSpinCtrl!"));
164 }
165
166 wxPoint posBtn(pos);
167 posBtn.x += sizeText.x + MARGIN_BETWEEN;
168
169 // we must create the list control before the spin button for the purpose
170 // of the dialog navigation: if there is a static text just before the spin
171 // control, activating it by Alt-letter should give focus to the text
172 // control, not the spin and the dialog navigation code will give focus to
173 // the next control (at Windows level), not the one after it
174
175 // create the text window
176
177 m_hwndBuddy = (WXHWND)::CreateWindowEx
178 (
179 exStyle, // sunken border
180 _T("LISTBOX"), // window class
181 NULL, // no window title
182 msStyle, // style (will be shown later)
183 pos.x, pos.y, // position
184 0, 0, // size (will be set later)
185 GetHwndOf(parent), // parent
186 (HMENU)-1, // control id
187 wxGetInstance(), // app instance
188 NULL // unused client data
189 );
190
191 if ( !m_hwndBuddy )
192 {
193 wxLogLastError(wxT("CreateWindow(buddy text window)"));
194
195 return false;
196 }
197
198 // initialize wxControl
199 if ( !CreateControl(parent, id, posBtn, sizeBtn, style, validator, name) )
200 return false;
201
202 // now create the real HWND
203 WXDWORD spiner_style = WS_VISIBLE |
204 UDS_ALIGNRIGHT |
205 UDS_ARROWKEYS |
206 UDS_SETBUDDYINT |
207 UDS_EXPANDABLE;
208
209 if ( !IsVertical(style) )
210 spiner_style |= UDS_HORZ;
211
212 if ( style & wxSP_WRAP )
213 spiner_style |= UDS_WRAP;
214
215 if ( !MSWCreateControl(UPDOWN_CLASS, spiner_style, posBtn, sizeBtn, _T(""), 0) )
216 return false;
217
218 // subclass the text ctrl to be able to intercept some events
219 wxSetWindowUserData(GetBuddyHwnd(), this);
220 m_wndProcBuddy = (WXFARPROC)wxSetWindowProc(GetBuddyHwnd(),
221 wxBuddyChoiceWndProc);
222
223 // set up fonts and colours (This is nomally done in MSWCreateControl)
224 InheritAttributes();
225 if (!m_hasFont)
226 SetFont(GetDefaultAttributes().font);
227
228 // set the size of the text window - can do it only now, because we
229 // couldn't call DoGetBestSize() before as font wasn't set
230 if ( sizeText.y <= 0 )
231 {
232 int cx, cy;
233 wxGetCharSize(GetHWND(), &cx, &cy, GetFont());
234
235 sizeText.y = EDIT_HEIGHT_FROM_CHAR_HEIGHT(cy);
236 }
237
238 SetBestSize(size);
239
240 (void)::ShowWindow(GetBuddyHwnd(), SW_SHOW);
241
242 // associate the list window with the spin button
243 (void)::SendMessage(GetHwnd(), UDM_SETBUDDY, (WPARAM)GetBuddyHwnd(), 0);
244
245 // do it after finishing with m_hwndBuddy creation to avoid generating
246 // initial wxEVT_COMMAND_TEXT_UPDATED message
247 ms_allChoiceSpins.Add(this);
248
249 // initialize the controls contents
250 for ( int i = 0; i < n; i++ )
251 {
252 Append(choices[i]);
253 }
254
255 return true;
256}
257
258bool wxChoice::Create(wxWindow *parent,
259 wxWindowID id,
260 const wxPoint& pos,
261 const wxSize& size,
262 const wxArrayString& choices,
263 long style,
264 const wxValidator& validator,
265 const wxString& name)
266{
267 wxCArrayString chs(choices);
268 return Create(parent, id, pos, size, chs.GetCount(), chs.GetStrings(),
269 style, validator, name);
270}
271
272WXDWORD wxChoice::MSWGetStyle(long style, WXDWORD *exstyle) const
273{
274 // we never have an external border
275 WXDWORD msStyle = wxControl::MSWGetStyle
276 (
277 (style & ~wxBORDER_MASK) | wxBORDER_NONE, exstyle
278 );
279
280 msStyle |= WS_VISIBLE;
281
282 // wxChoice-specific styles
283 msStyle |= LBS_NOINTEGRALHEIGHT;
284 if ( style & wxCB_SORT )
285 msStyle |= LBS_SORT;
286
287 return msStyle;
288}
289
290wxChoice::~wxChoice()
291{
292 Free();
293}
294
295// ----------------------------------------------------------------------------
296// adding/deleting items to/from the list
297// ----------------------------------------------------------------------------
298
299int wxChoice::DoAppend(const wxString& item)
300{
39fc096d 301 int n = (int)::SendMessage(GetBuddyHwnd(), LB_ADDSTRING, 0, (LPARAM)item.c_str());
1550d5f8
WS
302
303 if ( n == LB_ERR )
304 {
305 wxLogLastError(wxT("SendMessage(LB_ADDSTRING)"));
306 }
307
308 return n;
309}
310
311int wxChoice::DoInsert(const wxString& item, int pos)
312{
313 wxCHECK_MSG(!(GetWindowStyle() & wxCB_SORT), -1, wxT("can't insert into choice"));
314 wxCHECK_MSG((pos>=0) && (pos<=GetCount()), -1, wxT("invalid index"));
315
39fc096d 316 int n = (int)::SendMessage(GetBuddyHwnd(), LB_INSERTSTRING, pos, (LPARAM)item.c_str());
1550d5f8
WS
317 if ( n == LB_ERR )
318 {
319 wxLogLastError(wxT("SendMessage(LB_INSERTSTRING)"));
320 }
321
322 return n;
323}
324
325void wxChoice::Delete(int n)
326{
327 wxCHECK_RET( n < GetCount(), wxT("invalid item index in wxChoice::Delete") );
328
329 if ( HasClientObjectData() )
330 {
331 delete GetClientObject(n);
332 }
333
39fc096d 334 ::SendMessage(GetBuddyHwnd(), LB_DELETESTRING, n, 0);
1550d5f8
WS
335}
336
337void wxChoice::Clear()
338{
339 Free();
340
39fc096d 341 ::SendMessage(GetBuddyHwnd(), LB_RESETCONTENT, 0, 0);
1550d5f8
WS
342}
343
344void wxChoice::Free()
345{
346 if ( HasClientObjectData() )
347 {
348 size_t count = GetCount();
349 for ( size_t n = 0; n < count; n++ )
350 {
351 delete GetClientObject(n);
352 }
353 }
354}
355
356// ----------------------------------------------------------------------------
357// selection
358// ----------------------------------------------------------------------------
359
360int wxChoice::GetSelection() const
361{
39fc096d 362 return (int)::SendMessage(GetBuddyHwnd(), LB_GETCURSEL, 0, 0);
1550d5f8
WS
363}
364
365void wxChoice::SetSelection(int n)
366{
39fc096d 367 ::SendMessage(GetBuddyHwnd(), LB_SETCURSEL, n, 0);
1550d5f8
WS
368}
369
370// ----------------------------------------------------------------------------
371// string list functions
372// ----------------------------------------------------------------------------
373
374int wxChoice::GetCount() const
375{
39fc096d 376 return (int)::SendMessage(GetBuddyHwnd(), LB_GETCOUNT, 0, 0);
1550d5f8
WS
377}
378
379int wxChoice::FindString(const wxString& s) const
380{
39fc096d 381 int pos = (int)::SendMessage(GetBuddyHwnd(), LB_FINDSTRINGEXACT,
1550d5f8
WS
382 (WPARAM)-1, (LPARAM)s.c_str());
383
384 return pos == LB_ERR ? wxNOT_FOUND : pos;
385}
386
387void wxChoice::SetString(int n, const wxString& s)
388{
389 wxCHECK_RET( n >= 0 && n < GetCount(),
390 wxT("invalid item index in wxChoice::SetString") );
391
392 // we have to delete and add back the string as there is no way to change a
393 // string in place
394
395 // we need to preserve the client data
396 void *data;
397 if ( m_clientDataItemsType != wxClientData_None )
398 {
399 data = DoGetItemClientData(n);
400 }
401 else // no client data
402 {
403 data = NULL;
404 }
405
406 ::SendMessage(GetBuddyHwnd(), LB_DELETESTRING, n, 0);
407 ::SendMessage(GetBuddyHwnd(), LB_INSERTSTRING, n, (LPARAM)s.c_str() );
408
409 if ( data )
410 {
411 DoSetItemClientData(n, data);
412 }
413 //else: it's already NULL by default
414}
415
416wxString wxChoice::GetString(int n) const
417{
418 int len = (int)::SendMessage(GetBuddyHwnd(), LB_GETTEXTLEN, n, 0);
419
420 wxString str;
421 if ( len != LB_ERR && len > 0 )
422 {
423 if ( ::SendMessage
424 (
425 GetBuddyHwnd(),
426 LB_GETTEXT,
427 n,
428 (LPARAM)(wxChar *)wxStringBuffer(str, len)
429 ) == LB_ERR )
430 {
431 wxLogLastError(wxT("SendMessage(LB_GETLBTEXT)"));
432 }
433 }
434
435 return str;
436}
437
438// ----------------------------------------------------------------------------
439// client data
440// ----------------------------------------------------------------------------
441
442void wxChoice::DoSetItemClientData( int n, void* clientData )
443{
444 if ( ::SendMessage(GetHwnd(), LB_SETITEMDATA,
445 n, (LPARAM)clientData) == LB_ERR )
446 {
447 wxLogLastError(wxT("LB_SETITEMDATA"));
448 }
449}
450
451void* wxChoice::DoGetItemClientData( int n ) const
452{
39fc096d 453 LPARAM rc = ::SendMessage(GetHwnd(), LB_GETITEMDATA, n, 0);
1550d5f8
WS
454 if ( rc == LB_ERR )
455 {
456 wxLogLastError(wxT("LB_GETITEMDATA"));
457
458 // unfortunately, there is no way to return an error code to the user
459 rc = (LPARAM) NULL;
460 }
461
462 return (void *)rc;
463}
464
465void wxChoice::DoSetItemClientObject( int n, wxClientData* clientData )
466{
467 DoSetItemClientData(n, clientData);
468}
469
470wxClientData* wxChoice::DoGetItemClientObject( int n ) const
471{
472 return (wxClientData *)DoGetItemClientData(n);
473}
474
475// ----------------------------------------------------------------------------
476// size calculations
477// ----------------------------------------------------------------------------
478
479wxSize wxChoice::DoGetBestSize() const
480{
481 wxSize sizeBtn = GetBestSpinerSize(IsVertical(GetWindowStyle()));
482 sizeBtn.x += DEFAULT_ITEM_WIDTH + MARGIN_BETWEEN;
483
484 int y;
485 wxGetCharSize(GetHWND(), NULL, &y, GetFont());
486 y = EDIT_HEIGHT_FROM_CHAR_HEIGHT(y);
487
488 // JACS: we should always use the height calculated
489 // from above, because otherwise we'll get a spin control
490 // that's too big. So never use the height calculated
491 // from wxSpinButton::DoGetBestSize().
492
493 // if ( sizeBtn.y < y )
494 {
495 // make the text tall enough
496 sizeBtn.y = y;
497 }
498
499 return sizeBtn;
500}
501
502void wxChoice::DoMoveWindow(int x, int y, int width, int height)
503{
504 int widthBtn = GetBestSpinerSize(IsVertical(GetWindowStyle())).x;
505 int widthText = width - widthBtn - MARGIN_BETWEEN;
506 if ( widthText <= 0 )
507 {
508 wxLogDebug(_T("not enough space for wxSpinCtrl!"));
509 }
510
511 if ( !::MoveWindow(GetBuddyHwnd(), x, y, widthText, height, TRUE) )
512 {
513 wxLogLastError(wxT("MoveWindow(buddy)"));
514 }
515
516 x += widthText + MARGIN_BETWEEN;
517 if ( !::MoveWindow(GetHwnd(), x, y, widthBtn, height, TRUE) )
518 {
519 wxLogLastError(wxT("MoveWindow"));
520 }
521}
522
523// get total size of the control
524void wxChoice::DoGetSize(int *x, int *y) const
525{
526 RECT spinrect, textrect, ctrlrect;
527 GetWindowRect(GetHwnd(), &spinrect);
528 GetWindowRect(GetBuddyHwnd(), &textrect);
529 UnionRect(&ctrlrect, &textrect, &spinrect);
530
531 if ( x )
532 *x = ctrlrect.right - ctrlrect.left;
533 if ( y )
534 *y = ctrlrect.bottom - ctrlrect.top;
535}
536
537void wxChoice::DoGetPosition(int *x, int *y) const
538{
539 // hack: pretend that our HWND is the text control just for a moment
540 WXHWND hWnd = GetHWND();
541 wxConstCast(this, wxChoice)->m_hWnd = m_hwndBuddy;
542
543 wxChoiceBase::DoGetPosition(x, y);
544
545 wxConstCast(this, wxChoice)->m_hWnd = hWnd;
546}
547
8300f815 548#endif // wxUSE_CHOICE && __SMARTPHONE__ && __WXWINCE__