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