1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/wince/choicece.cpp
3 // Purpose: wxChoice implementation for smart phones driven by WinCE
4 // Author: Wlodzimierz ABX Skiba
8 // Copyright: (c) Wlodzimierz Skiba
9 // License: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
12 // ============================================================================
14 // ============================================================================
16 // ----------------------------------------------------------------------------
18 // ----------------------------------------------------------------------------
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
27 #if wxUSE_CHOICE && defined(__SMARTPHONE__) && defined(__WXWINCE__)
29 #include "wx/choice.h"
32 #include "wx/msw/wrapcctl.h" // include <commctrl.h> "properly"
35 #include "wx/spinbutt.h" // for wxSpinnerBestSize
37 #if wxUSE_EXTENDED_RTTI
40 IMPLEMENT_DYNAMIC_CLASS(wxChoice
, wxControl
)
43 #define GetBuddyHwnd() (HWND)(m_hwndBuddy)
45 #define IsVertical(wxStyle) ( (wxStyle & wxSP_HORIZONTAL) != wxSP_HORIZONTAL )
47 // ----------------------------------------------------------------------------
49 // ----------------------------------------------------------------------------
51 // the margin between the up-down control and its buddy (can be arbitrary,
52 // choose what you like - or may be decide during run-time depending on the
54 static const int MARGIN_BETWEEN
= 0;
56 // ============================================================================
58 // ============================================================================
60 wxArrayChoiceSpins
wxChoice::ms_allChoiceSpins
;
62 // ----------------------------------------------------------------------------
63 // wnd proc for the buddy text ctrl
64 // ----------------------------------------------------------------------------
66 LRESULT APIENTRY _EXPORT
wxBuddyChoiceWndProc(HWND hwnd
,
71 wxChoice
*spin
= (wxChoice
*)wxGetWindowUserData(hwnd
);
73 // forward some messages (the key and focus ones only so far) to
78 // if the focus comes from the spin control itself, don't set it
79 // back to it -- we don't want to go into an infinite loop
80 if ( (WXHWND
)wParam
== spin
->GetHWND() )
89 spin
->MSWWindowProc(message
, wParam
, lParam
);
91 // The control may have been deleted at this point, so check.
92 if ( !::IsWindow(hwnd
) || wxGetWindowUserData(hwnd
) != spin
)
97 // we want to get WXK_RETURN in order to generate the event for it
98 return DLGC_WANTCHARS
;
101 return ::CallWindowProc(CASTWNDPROC spin
->GetBuddyWndProc(),
102 hwnd
, message
, wParam
, lParam
);
105 wxChoice
*wxChoice::GetChoiceForListBox(WXHWND hwndBuddy
)
107 wxChoice
*choice
= (wxChoice
*)wxGetWindowUserData((HWND
)hwndBuddy
);
109 int i
= ms_allChoiceSpins
.Index(choice
);
111 if ( i
== wxNOT_FOUND
)
115 wxASSERT_MSG( choice
->m_hwndBuddy
== hwndBuddy
,
116 _T("wxChoice has incorrect buddy HWND!") );
121 // ----------------------------------------------------------------------------
123 // ----------------------------------------------------------------------------
125 bool wxChoice::Create(wxWindow
*parent
,
129 int n
, const wxString choices
[],
131 const wxValidator
& validator
,
132 const wxString
& name
)
134 return CreateAndInit(parent
, id
, pos
, size
, n
, choices
, style
,
138 bool wxChoice::CreateAndInit(wxWindow
*parent
,
142 int n
, const wxString choices
[],
144 const wxValidator
& validator
,
145 const wxString
& name
)
147 if ( !(style
& wxSP_VERTICAL
) )
148 style
|= wxSP_HORIZONTAL
;
150 if ( (style
& wxBORDER_MASK
) == wxBORDER_DEFAULT
)
151 style
|= wxBORDER_SIMPLE
;
153 style
|= wxSP_ARROW_KEYS
;
155 SetWindowStyle(style
);
158 WXDWORD msStyle
= MSWGetStyle(GetWindowStyle(), & exStyle
) ;
160 wxSize
sizeText(size
), sizeBtn(size
);
161 sizeBtn
.x
= GetBestSpinnerSize(IsVertical(style
)).x
;
163 if ( sizeText
.x
== wxDefaultCoord
)
165 // DEFAULT_ITEM_WIDTH is the default width for the text control
166 sizeText
.x
= DEFAULT_ITEM_WIDTH
+ MARGIN_BETWEEN
+ sizeBtn
.x
;
169 sizeText
.x
-= sizeBtn
.x
+ MARGIN_BETWEEN
;
170 if ( sizeText
.x
<= 0 )
172 wxLogDebug(_T("not enough space for wxSpinCtrl!"));
176 posBtn
.x
+= sizeText
.x
+ MARGIN_BETWEEN
;
178 // we must create the list control before the spin button for the purpose
179 // of the dialog navigation: if there is a static text just before the spin
180 // control, activating it by Alt-letter should give focus to the text
181 // control, not the spin and the dialog navigation code will give focus to
182 // the next control (at Windows level), not the one after it
184 // create the text window
186 m_hwndBuddy
= (WXHWND
)::CreateWindowEx
188 exStyle
, // sunken border
189 _T("LISTBOX"), // window class
190 NULL
, // no window title
191 msStyle
, // style (will be shown later)
192 pos
.x
, pos
.y
, // position
193 0, 0, // size (will be set later)
194 GetHwndOf(parent
), // parent
195 (HMENU
)-1, // control id
196 wxGetInstance(), // app instance
197 NULL
// unused client data
202 wxLogLastError(wxT("CreateWindow(buddy text window)"));
207 // initialize wxControl
208 if ( !CreateControl(parent
, id
, posBtn
, sizeBtn
, style
, validator
, name
) )
211 // now create the real HWND
212 WXDWORD spiner_style
= WS_VISIBLE
|
218 if ( !IsVertical(style
) )
219 spiner_style
|= UDS_HORZ
;
221 if ( style
& wxSP_WRAP
)
222 spiner_style
|= UDS_WRAP
;
224 if ( !MSWCreateControl(UPDOWN_CLASS
, spiner_style
, posBtn
, sizeBtn
, wxEmptyString
, 0) )
227 // subclass the text ctrl to be able to intercept some events
228 wxSetWindowUserData(GetBuddyHwnd(), this);
229 m_wndProcBuddy
= (WXFARPROC
)wxSetWindowProc(GetBuddyHwnd(),
230 wxBuddyChoiceWndProc
);
232 // set up fonts and colours (This is nomally done in MSWCreateControl)
235 SetFont(GetDefaultAttributes().font
);
237 // set the size of the text window - can do it only now, because we
238 // couldn't call DoGetBestSize() before as font wasn't set
239 if ( sizeText
.y
<= 0 )
242 wxGetCharSize(GetHWND(), &cx
, &cy
, GetFont());
244 sizeText
.y
= EDIT_HEIGHT_FROM_CHAR_HEIGHT(cy
);
247 SetInitialSize(size
);
249 (void)::ShowWindow(GetBuddyHwnd(), SW_SHOW
);
251 // associate the list window with the spin button
252 (void)::SendMessage(GetHwnd(), UDM_SETBUDDY
, (WPARAM
)GetBuddyHwnd(), 0);
254 // do it after finishing with m_hwndBuddy creation to avoid generating
255 // initial wxEVT_COMMAND_TEXT_UPDATED message
256 ms_allChoiceSpins
.Add(this);
258 // initialize the controls contents
259 for ( int i
= 0; i
< n
; i
++ )
267 bool wxChoice::Create(wxWindow
*parent
,
271 const wxArrayString
& choices
,
273 const wxValidator
& validator
,
274 const wxString
& name
)
276 wxCArrayString
chs(choices
);
277 return Create(parent
, id
, pos
, size
, chs
.GetCount(), chs
.GetStrings(),
278 style
, validator
, name
);
281 WXDWORD
wxChoice::MSWGetStyle(long style
, WXDWORD
*exstyle
) const
283 // we never have an external border
284 WXDWORD msStyle
= wxControl::MSWGetStyle
286 (style
& ~wxBORDER_MASK
) | wxBORDER_NONE
, exstyle
289 msStyle
|= WS_VISIBLE
;
291 // wxChoice-specific styles
292 msStyle
|= LBS_NOINTEGRALHEIGHT
;
293 if ( style
& wxCB_SORT
)
296 msStyle
|= LBS_NOTIFY
;
301 bool wxChoice::MSWCommand(WXUINT param
, WXWORD
WXUNUSED(id
))
303 if ( param
!= LBN_SELCHANGE
)
305 // "selection changed" is the only event we're after
309 int n
= GetSelection();
312 wxCommandEvent
event(wxEVT_COMMAND_CHOICE_SELECTED
, m_windowId
);
314 event
.SetEventObject(this);
315 event
.SetString(GetStringSelection());
316 if ( HasClientObjectData() )
317 event
.SetClientObject( GetClientObject(n
) );
318 else if ( HasClientUntypedData() )
319 event
.SetClientData( GetClientData(n
) );
320 ProcessCommand(event
);
326 wxChoice::~wxChoice()
331 // ----------------------------------------------------------------------------
332 // adding/deleting items to/from the list
333 // ----------------------------------------------------------------------------
335 int wxChoice::DoAppend(const wxString
& item
)
337 int n
= (int)::SendMessage(GetBuddyHwnd(), LB_ADDSTRING
, 0, (LPARAM
)item
.c_str());
341 wxLogLastError(wxT("SendMessage(LB_ADDSTRING)"));
347 int wxChoice::DoInsert(const wxString
& item
, unsigned int pos
)
349 wxCHECK_MSG(!(GetWindowStyle() & wxCB_SORT
), -1, wxT("can't insert into choice"));
350 wxCHECK_MSG(IsValidInsert(pos
), -1, wxT("invalid index"));
352 int n
= (int)::SendMessage(GetBuddyHwnd(), LB_INSERTSTRING
, pos
, (LPARAM
)item
.c_str());
355 wxLogLastError(wxT("SendMessage(LB_INSERTSTRING)"));
361 void wxChoice::Delete(unsigned int n
)
363 wxCHECK_RET( IsValid(n
), wxT("invalid item index in wxChoice::Delete") );
365 if ( HasClientObjectData() )
367 delete GetClientObject(n
);
370 ::SendMessage(GetBuddyHwnd(), LB_DELETESTRING
, n
, 0);
373 void wxChoice::Clear()
377 ::SendMessage(GetBuddyHwnd(), LB_RESETCONTENT
, 0, 0);
380 void wxChoice::Free()
382 if ( HasClientObjectData() )
384 unsigned int count
= GetCount();
385 for ( unsigned int n
= 0; n
< count
; n
++ )
387 delete GetClientObject(n
);
392 // ----------------------------------------------------------------------------
394 // ----------------------------------------------------------------------------
396 int wxChoice::GetSelection() const
398 return (int)::SendMessage(GetBuddyHwnd(), LB_GETCURSEL
, 0, 0);
401 void wxChoice::SetSelection(int n
)
403 ::SendMessage(GetBuddyHwnd(), LB_SETCURSEL
, n
, 0);
406 // ----------------------------------------------------------------------------
407 // string list functions
408 // ----------------------------------------------------------------------------
410 unsigned int wxChoice::GetCount() const
412 return (unsigned int)::SendMessage(GetBuddyHwnd(), LB_GETCOUNT
, 0, 0);
415 int wxChoice::FindString(const wxString
& s
, bool bCase
) const
417 // back to base class search for not native search type
419 return wxItemContainerImmutable::FindString( s
, bCase
);
421 int pos
= (int)::SendMessage(GetBuddyHwnd(), LB_FINDSTRINGEXACT
,
422 (WPARAM
)-1, (LPARAM
)s
.c_str());
424 return pos
== LB_ERR
? wxNOT_FOUND
: pos
;
427 void wxChoice::SetString(unsigned int n
, const wxString
& s
)
429 wxCHECK_RET( IsValid(n
),
430 wxT("invalid item index in wxChoice::SetString") );
432 // we have to delete and add back the string as there is no way to change a
435 // we need to preserve the client data
437 if ( m_clientDataItemsType
!= wxClientData_None
)
439 data
= DoGetItemClientData(n
);
441 else // no client data
446 ::SendMessage(GetBuddyHwnd(), LB_DELETESTRING
, n
, 0);
447 ::SendMessage(GetBuddyHwnd(), LB_INSERTSTRING
, n
, (LPARAM
)s
.c_str() );
451 DoSetItemClientData(n
, data
);
453 //else: it's already NULL by default
456 wxString
wxChoice::GetString(unsigned int n
) const
458 int len
= (int)::SendMessage(GetBuddyHwnd(), LB_GETTEXTLEN
, n
, 0);
461 if ( len
!= LB_ERR
&& len
> 0 )
468 (LPARAM
)(wxChar
*)wxStringBuffer(str
, len
)
471 wxLogLastError(wxT("SendMessage(LB_GETLBTEXT)"));
478 // ----------------------------------------------------------------------------
480 // ----------------------------------------------------------------------------
482 void wxChoice::DoSetItemClientData(unsigned int n
, void* clientData
)
484 if ( ::SendMessage(GetHwnd(), LB_SETITEMDATA
,
485 n
, (LPARAM
)clientData
) == LB_ERR
)
487 wxLogLastError(wxT("LB_SETITEMDATA"));
491 void* wxChoice::DoGetItemClientData(unsigned int n
) const
493 LPARAM rc
= ::SendMessage(GetHwnd(), LB_GETITEMDATA
, n
, 0);
496 wxLogLastError(wxT("LB_GETITEMDATA"));
498 // unfortunately, there is no way to return an error code to the user
505 void wxChoice::DoSetItemClientObject(unsigned int n
, wxClientData
* clientData
)
507 DoSetItemClientData(n
, clientData
);
510 wxClientData
* wxChoice::DoGetItemClientObject(unsigned int n
) const
512 return (wxClientData
*)DoGetItemClientData(n
);
515 // ----------------------------------------------------------------------------
517 // ----------------------------------------------------------------------------
519 wxSize
wxChoice::DoGetBestSize() const
521 wxSize sizeBtn
= GetBestSpinnerSize(IsVertical(GetWindowStyle()));
522 sizeBtn
.x
+= DEFAULT_ITEM_WIDTH
+ MARGIN_BETWEEN
;
525 wxGetCharSize(GetHWND(), NULL
, &y
, GetFont());
526 y
= EDIT_HEIGHT_FROM_CHAR_HEIGHT(y
);
528 // JACS: we should always use the height calculated
529 // from above, because otherwise we'll get a spin control
530 // that's too big. So never use the height calculated
531 // from wxSpinButton::DoGetBestSize().
533 // if ( sizeBtn.y < y )
535 // make the text tall enough
542 void wxChoice::DoMoveWindow(int x
, int y
, int width
, int height
)
544 int widthBtn
= GetBestSpinnerSize(IsVertical(GetWindowStyle())).x
;
545 int widthText
= width
- widthBtn
- MARGIN_BETWEEN
;
546 if ( widthText
<= 0 )
548 wxLogDebug(_T("not enough space for wxSpinCtrl!"));
551 if ( !::MoveWindow(GetBuddyHwnd(), x
, y
, widthText
, height
, TRUE
) )
553 wxLogLastError(wxT("MoveWindow(buddy)"));
556 x
+= widthText
+ MARGIN_BETWEEN
;
557 if ( !::MoveWindow(GetHwnd(), x
, y
, widthBtn
, height
, TRUE
) )
559 wxLogLastError(wxT("MoveWindow"));
563 // get total size of the control
564 void wxChoice::DoGetSize(int *x
, int *y
) const
566 RECT spinrect
, textrect
, ctrlrect
;
567 GetWindowRect(GetHwnd(), &spinrect
);
568 GetWindowRect(GetBuddyHwnd(), &textrect
);
569 UnionRect(&ctrlrect
, &textrect
, &spinrect
);
572 *x
= ctrlrect
.right
- ctrlrect
.left
;
574 *y
= ctrlrect
.bottom
- ctrlrect
.top
;
577 void wxChoice::DoGetPosition(int *x
, int *y
) const
579 // hack: pretend that our HWND is the text control just for a moment
580 WXHWND hWnd
= GetHWND();
581 wxConstCast(this, wxChoice
)->m_hWnd
= m_hwndBuddy
;
583 wxChoiceBase::DoGetPosition(x
, y
);
585 wxConstCast(this, wxChoice
)->m_hWnd
= hWnd
;
588 #endif // wxUSE_CHOICE && __SMARTPHONE__ && __WXWINCE__