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__