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