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