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"
34 // include <commctrl.h> "properly"
35 #include "wx/msw/wrapcctl.h"
37 #include "wx/spinbutt.h" // for wxSpinnerBestSize
39 #if wxUSE_EXTENDED_RTTI
42 IMPLEMENT_DYNAMIC_CLASS(wxChoice
, wxControl
)
45 #define GetBuddyHwnd() (HWND)(m_hwndBuddy)
47 #define IsVertical(wxStyle) ( (wxStyle & wxSP_HORIZONTAL) != wxSP_HORIZONTAL )
49 // ----------------------------------------------------------------------------
51 // ----------------------------------------------------------------------------
53 // the margin between the up-down control and its buddy (can be arbitrary,
54 // choose what you like - or may be decide during run-time depending on the
56 static const int MARGIN_BETWEEN
= 0;
58 // ============================================================================
60 // ============================================================================
62 wxArrayChoiceSpins
wxChoice::ms_allChoiceSpins
;
64 // ----------------------------------------------------------------------------
65 // wnd proc for the buddy text ctrl
66 // ----------------------------------------------------------------------------
68 LRESULT APIENTRY _EXPORT
wxBuddyChoiceWndProc(HWND hwnd
,
73 wxChoice
*spin
= (wxChoice
*)wxGetWindowUserData(hwnd
);
75 // forward some messages (the key and focus ones only so far) to
80 // if the focus comes from the spin control itself, don't set it
81 // back to it -- we don't want to go into an infinite loop
82 if ( (WXHWND
)wParam
== spin
->GetHWND() )
91 spin
->MSWWindowProc(message
, wParam
, lParam
);
93 // The control may have been deleted at this point, so check.
94 if ( !::IsWindow(hwnd
) || wxGetWindowUserData(hwnd
) != spin
)
99 // we want to get WXK_RETURN in order to generate the event for it
100 return DLGC_WANTCHARS
;
103 return ::CallWindowProc(CASTWNDPROC spin
->GetBuddyWndProc(),
104 hwnd
, message
, wParam
, lParam
);
107 wxChoice
*wxChoice::GetChoiceForListBox(WXHWND hwndBuddy
)
109 wxChoice
*choice
= (wxChoice
*)wxGetWindowUserData((HWND
)hwndBuddy
);
111 int i
= ms_allChoiceSpins
.Index(choice
);
113 if ( i
== wxNOT_FOUND
)
117 wxASSERT_MSG( choice
->m_hwndBuddy
== hwndBuddy
,
118 _T("wxChoice has incorrect buddy HWND!") );
123 // ----------------------------------------------------------------------------
125 // ----------------------------------------------------------------------------
127 bool wxChoice::Create(wxWindow
*parent
,
131 int n
, const wxString choices
[],
133 const wxValidator
& validator
,
134 const wxString
& name
)
136 return CreateAndInit(parent
, id
, pos
, size
, n
, choices
, style
,
140 bool wxChoice::CreateAndInit(wxWindow
*parent
,
144 int n
, const wxString choices
[],
146 const wxValidator
& validator
,
147 const wxString
& name
)
149 if ( !(style
& wxSP_VERTICAL
) )
150 style
|= wxSP_HORIZONTAL
;
152 if ( (style
& wxBORDER_MASK
) == wxBORDER_DEFAULT
)
153 style
|= wxBORDER_SIMPLE
;
155 style
|= wxSP_ARROW_KEYS
;
157 SetWindowStyle(style
);
160 WXDWORD msStyle
= MSWGetStyle(GetWindowStyle(), & exStyle
) ;
162 wxSize
sizeText(size
), sizeBtn(size
);
163 sizeBtn
.x
= GetBestSpinnerSize(IsVertical(style
)).x
;
165 if ( sizeText
.x
== wxDefaultCoord
)
167 // DEFAULT_ITEM_WIDTH is the default width for the text control
168 sizeText
.x
= DEFAULT_ITEM_WIDTH
+ MARGIN_BETWEEN
+ sizeBtn
.x
;
171 sizeText
.x
-= sizeBtn
.x
+ MARGIN_BETWEEN
;
172 if ( sizeText
.x
<= 0 )
174 wxLogDebug(_T("not enough space for wxSpinCtrl!"));
178 posBtn
.x
+= sizeText
.x
+ MARGIN_BETWEEN
;
180 // we must create the list control before the spin button for the purpose
181 // of the dialog navigation: if there is a static text just before the spin
182 // control, activating it by Alt-letter should give focus to the text
183 // control, not the spin and the dialog navigation code will give focus to
184 // the next control (at Windows level), not the one after it
186 // create the text window
188 m_hwndBuddy
= (WXHWND
)::CreateWindowEx
190 exStyle
, // sunken border
191 _T("LISTBOX"), // window class
192 NULL
, // no window title
193 msStyle
, // style (will be shown later)
194 pos
.x
, pos
.y
, // position
195 0, 0, // size (will be set later)
196 GetHwndOf(parent
), // parent
197 (HMENU
)-1, // control id
198 wxGetInstance(), // app instance
199 NULL
// unused client data
204 wxLogLastError(wxT("CreateWindow(buddy text window)"));
209 // initialize wxControl
210 if ( !CreateControl(parent
, id
, posBtn
, sizeBtn
, style
, validator
, name
) )
213 // now create the real HWND
214 WXDWORD spiner_style
= WS_VISIBLE
|
220 if ( !IsVertical(style
) )
221 spiner_style
|= UDS_HORZ
;
223 if ( style
& wxSP_WRAP
)
224 spiner_style
|= UDS_WRAP
;
226 if ( !MSWCreateControl(UPDOWN_CLASS
, spiner_style
, posBtn
, sizeBtn
, wxEmptyString
, 0) )
229 // subclass the text ctrl to be able to intercept some events
230 wxSetWindowUserData(GetBuddyHwnd(), this);
231 m_wndProcBuddy
= (WXFARPROC
)wxSetWindowProc(GetBuddyHwnd(),
232 wxBuddyChoiceWndProc
);
234 // set up fonts and colours (This is nomally done in MSWCreateControl)
237 SetFont(GetDefaultAttributes().font
);
239 // set the size of the text window - can do it only now, because we
240 // couldn't call DoGetBestSize() before as font wasn't set
241 if ( sizeText
.y
<= 0 )
244 wxGetCharSize(GetHWND(), &cx
, &cy
, GetFont());
246 sizeText
.y
= EDIT_HEIGHT_FROM_CHAR_HEIGHT(cy
);
251 (void)::ShowWindow(GetBuddyHwnd(), SW_SHOW
);
253 // associate the list window with the spin button
254 (void)::SendMessage(GetHwnd(), UDM_SETBUDDY
, (WPARAM
)GetBuddyHwnd(), 0);
256 // do it after finishing with m_hwndBuddy creation to avoid generating
257 // initial wxEVT_COMMAND_TEXT_UPDATED message
258 ms_allChoiceSpins
.Add(this);
260 // initialize the controls contents
261 for ( int i
= 0; i
< n
; i
++ )
269 bool wxChoice::Create(wxWindow
*parent
,
273 const wxArrayString
& choices
,
275 const wxValidator
& validator
,
276 const wxString
& name
)
278 wxCArrayString
chs(choices
);
279 return Create(parent
, id
, pos
, size
, chs
.GetCount(), chs
.GetStrings(),
280 style
, validator
, name
);
283 WXDWORD
wxChoice::MSWGetStyle(long style
, WXDWORD
*exstyle
) const
285 // we never have an external border
286 WXDWORD msStyle
= wxControl::MSWGetStyle
288 (style
& ~wxBORDER_MASK
) | wxBORDER_NONE
, exstyle
291 msStyle
|= WS_VISIBLE
;
293 // wxChoice-specific styles
294 msStyle
|= LBS_NOINTEGRALHEIGHT
;
295 if ( style
& wxCB_SORT
)
298 msStyle
|= LBS_NOTIFY
;
303 bool wxChoice::MSWCommand(WXUINT param
, WXWORD
WXUNUSED(id
))
305 if ( param
!= LBN_SELCHANGE
)
307 // "selection changed" is the only event we're after
311 int n
= GetSelection();
314 wxCommandEvent
event(wxEVT_COMMAND_CHOICE_SELECTED
, m_windowId
);
316 event
.SetEventObject(this);
317 event
.SetString(GetStringSelection());
318 if ( HasClientObjectData() )
319 event
.SetClientObject( GetClientObject(n
) );
320 else if ( HasClientUntypedData() )
321 event
.SetClientData( GetClientData(n
) );
322 ProcessCommand(event
);
328 wxChoice::~wxChoice()
333 // ----------------------------------------------------------------------------
334 // adding/deleting items to/from the list
335 // ----------------------------------------------------------------------------
337 int wxChoice::DoAppend(const wxString
& item
)
339 int n
= (int)::SendMessage(GetBuddyHwnd(), LB_ADDSTRING
, 0, (LPARAM
)item
.c_str());
343 wxLogLastError(wxT("SendMessage(LB_ADDSTRING)"));
349 int wxChoice::DoInsert(const wxString
& item
, unsigned int pos
)
351 wxCHECK_MSG(!(GetWindowStyle() & wxCB_SORT
), -1, wxT("can't insert into choice"));
352 wxCHECK_MSG(IsValidInsert(pos
), -1, wxT("invalid index"));
354 int n
= (int)::SendMessage(GetBuddyHwnd(), LB_INSERTSTRING
, pos
, (LPARAM
)item
.c_str());
357 wxLogLastError(wxT("SendMessage(LB_INSERTSTRING)"));
363 void wxChoice::Delete(unsigned int n
)
365 wxCHECK_RET( IsValid(n
), wxT("invalid item index in wxChoice::Delete") );
367 if ( HasClientObjectData() )
369 delete GetClientObject(n
);
372 ::SendMessage(GetBuddyHwnd(), LB_DELETESTRING
, n
, 0);
375 void wxChoice::Clear()
379 ::SendMessage(GetBuddyHwnd(), LB_RESETCONTENT
, 0, 0);
382 void wxChoice::Free()
384 if ( HasClientObjectData() )
386 unsigned int count
= GetCount();
387 for ( unsigned int n
= 0; n
< count
; n
++ )
389 delete GetClientObject(n
);
394 // ----------------------------------------------------------------------------
396 // ----------------------------------------------------------------------------
398 int wxChoice::GetSelection() const
400 return (int)::SendMessage(GetBuddyHwnd(), LB_GETCURSEL
, 0, 0);
403 void wxChoice::SetSelection(int n
)
405 ::SendMessage(GetBuddyHwnd(), LB_SETCURSEL
, n
, 0);
408 // ----------------------------------------------------------------------------
409 // string list functions
410 // ----------------------------------------------------------------------------
412 unsigned int wxChoice::GetCount() const
414 return (unsigned int)::SendMessage(GetBuddyHwnd(), LB_GETCOUNT
, 0, 0);
417 int wxChoice::FindString(const wxString
& s
, bool bCase
) const
419 // back to base class search for not native search type
421 return wxItemContainerImmutable::FindString( s
, bCase
);
423 int pos
= (int)::SendMessage(GetBuddyHwnd(), LB_FINDSTRINGEXACT
,
424 (WPARAM
)-1, (LPARAM
)s
.c_str());
426 return pos
== LB_ERR
? wxNOT_FOUND
: pos
;
429 void wxChoice::SetString(unsigned int n
, const wxString
& s
)
431 wxCHECK_RET( IsValid(n
),
432 wxT("invalid item index in wxChoice::SetString") );
434 // we have to delete and add back the string as there is no way to change a
437 // we need to preserve the client data
439 if ( m_clientDataItemsType
!= wxClientData_None
)
441 data
= DoGetItemClientData(n
);
443 else // no client data
448 ::SendMessage(GetBuddyHwnd(), LB_DELETESTRING
, n
, 0);
449 ::SendMessage(GetBuddyHwnd(), LB_INSERTSTRING
, n
, (LPARAM
)s
.c_str() );
453 DoSetItemClientData(n
, data
);
455 //else: it's already NULL by default
458 wxString
wxChoice::GetString(unsigned int n
) const
460 int len
= (int)::SendMessage(GetBuddyHwnd(), LB_GETTEXTLEN
, n
, 0);
463 if ( len
!= LB_ERR
&& len
> 0 )
470 (LPARAM
)(wxChar
*)wxStringBuffer(str
, len
)
473 wxLogLastError(wxT("SendMessage(LB_GETLBTEXT)"));
480 // ----------------------------------------------------------------------------
482 // ----------------------------------------------------------------------------
484 void wxChoice::DoSetItemClientData(unsigned int n
, void* clientData
)
486 if ( ::SendMessage(GetHwnd(), LB_SETITEMDATA
,
487 n
, (LPARAM
)clientData
) == LB_ERR
)
489 wxLogLastError(wxT("LB_SETITEMDATA"));
493 void* wxChoice::DoGetItemClientData(unsigned int n
) const
495 LPARAM rc
= ::SendMessage(GetHwnd(), LB_GETITEMDATA
, n
, 0);
498 wxLogLastError(wxT("LB_GETITEMDATA"));
500 // unfortunately, there is no way to return an error code to the user
507 void wxChoice::DoSetItemClientObject(unsigned int n
, wxClientData
* clientData
)
509 DoSetItemClientData(n
, clientData
);
512 wxClientData
* wxChoice::DoGetItemClientObject(unsigned int n
) const
514 return (wxClientData
*)DoGetItemClientData(n
);
517 // ----------------------------------------------------------------------------
519 // ----------------------------------------------------------------------------
521 wxSize
wxChoice::DoGetBestSize() const
523 wxSize sizeBtn
= GetBestSpinnerSize(IsVertical(GetWindowStyle()));
524 sizeBtn
.x
+= DEFAULT_ITEM_WIDTH
+ MARGIN_BETWEEN
;
527 wxGetCharSize(GetHWND(), NULL
, &y
, GetFont());
528 y
= EDIT_HEIGHT_FROM_CHAR_HEIGHT(y
);
530 // JACS: we should always use the height calculated
531 // from above, because otherwise we'll get a spin control
532 // that's too big. So never use the height calculated
533 // from wxSpinButton::DoGetBestSize().
535 // if ( sizeBtn.y < y )
537 // make the text tall enough
544 void wxChoice::DoMoveWindow(int x
, int y
, int width
, int height
)
546 int widthBtn
= GetBestSpinnerSize(IsVertical(GetWindowStyle())).x
;
547 int widthText
= width
- widthBtn
- MARGIN_BETWEEN
;
548 if ( widthText
<= 0 )
550 wxLogDebug(_T("not enough space for wxSpinCtrl!"));
553 if ( !::MoveWindow(GetBuddyHwnd(), x
, y
, widthText
, height
, TRUE
) )
555 wxLogLastError(wxT("MoveWindow(buddy)"));
558 x
+= widthText
+ MARGIN_BETWEEN
;
559 if ( !::MoveWindow(GetHwnd(), x
, y
, widthBtn
, height
, TRUE
) )
561 wxLogLastError(wxT("MoveWindow"));
565 // get total size of the control
566 void wxChoice::DoGetSize(int *x
, int *y
) const
568 RECT spinrect
, textrect
, ctrlrect
;
569 GetWindowRect(GetHwnd(), &spinrect
);
570 GetWindowRect(GetBuddyHwnd(), &textrect
);
571 UnionRect(&ctrlrect
, &textrect
, &spinrect
);
574 *x
= ctrlrect
.right
- ctrlrect
.left
;
576 *y
= ctrlrect
.bottom
- ctrlrect
.top
;
579 void wxChoice::DoGetPosition(int *x
, int *y
) const
581 // hack: pretend that our HWND is the text control just for a moment
582 WXHWND hWnd
= GetHWND();
583 wxConstCast(this, wxChoice
)->m_hWnd
= m_hwndBuddy
;
585 wxChoiceBase::DoGetPosition(x
, y
);
587 wxConstCast(this, wxChoice
)->m_hWnd
= hWnd
;
590 #endif // wxUSE_CHOICE && __SMARTPHONE__ && __WXWINCE__