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 /////////////////////////////////////////////////////////////////////////////// 
  13 // ============================================================================ 
  15 // ============================================================================ 
  17 // ---------------------------------------------------------------------------- 
  19 // ---------------------------------------------------------------------------- 
  21 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA) 
  22     #pragma implementation "choicece.h" 
  25 // For compilers that support precompilation, includes "wx.h". 
  26 #include "wx/wxprec.h" 
  33     #include "wx/choice.h" 
  36 #include "wx/spinbutt.h" // for wxSpinnerBestSize 
  39 #include "wx/msw/missing.h" 
  40 #include "wx/msw/winundef.h" 
  42 #if wxUSE_CHOICE && defined(__SMARTPHONE__) && defined(__WXWINCE__) 
  44 #if wxUSE_EXTENDED_RTTI 
  47 IMPLEMENT_DYNAMIC_CLASS(wxChoice
, wxControl
) 
  50 #define GetBuddyHwnd()      (HWND)(m_hwndBuddy) 
  52 #define IsVertical(wxStyle) ( (wxStyle & wxSP_HORIZONTAL) != wxSP_HORIZONTAL ) 
  54 // ---------------------------------------------------------------------------- 
  56 // ---------------------------------------------------------------------------- 
  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 
  61 static const int MARGIN_BETWEEN 
= 0; 
  63 // ============================================================================ 
  65 // ============================================================================ 
  67 wxArrayChoiceSpins 
wxChoice::ms_allChoiceSpins
; 
  69 // ---------------------------------------------------------------------------- 
  70 // wnd proc for the buddy text ctrl 
  71 // ---------------------------------------------------------------------------- 
  73 LRESULT APIENTRY _EXPORT 
wxBuddyChoiceWndProc(HWND hwnd
, 
  78     wxChoice 
*spin 
= (wxChoice 
*)wxGetWindowUserData(hwnd
); 
  80     // forward some messages (the key and focus ones only so far) to 
  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() ) 
  96             spin
->MSWWindowProc(message
, wParam
, lParam
); 
  98             // The control may have been deleted at this point, so check. 
  99             if ( !::IsWindow(hwnd
) || wxGetWindowUserData(hwnd
) != spin 
) 
 104             // we want to get WXK_RETURN in order to generate the event for it 
 105             return DLGC_WANTCHARS
; 
 108     return ::CallWindowProc(CASTWNDPROC spin
->GetBuddyWndProc(), 
 109                             hwnd
, message
, wParam
, lParam
); 
 112 wxChoice 
*wxChoice::GetChoiceForListBox(WXHWND hwndBuddy
) 
 114     wxChoice 
*choice 
= (wxChoice 
*)wxGetWindowUserData((HWND
)hwndBuddy
); 
 116     int i 
= ms_allChoiceSpins
.Index(choice
); 
 118     if ( i 
== wxNOT_FOUND 
) 
 122     wxASSERT_MSG( choice
->m_hwndBuddy 
== hwndBuddy
, 
 123                   _T("wxChoice has incorrect buddy HWND!") ); 
 128 // ---------------------------------------------------------------------------- 
 130 // ---------------------------------------------------------------------------- 
 132 bool wxChoice::Create(wxWindow 
*parent
, 
 136                       int n
, const wxString choices
[], 
 138                       const wxValidator
& validator
, 
 139                       const wxString
& name
) 
 141     return CreateAndInit(parent
, id
, pos
, size
, n
, choices
, style
, 
 145 bool wxChoice::CreateAndInit(wxWindow 
*parent
, 
 149                              int n
, const wxString choices
[], 
 151                              const wxValidator
& validator
, 
 152                              const wxString
& name
) 
 154     if ( !(style 
& wxSP_VERTICAL
) ) 
 155         style 
|= wxSP_HORIZONTAL
; 
 157     if ( (style 
& wxBORDER_MASK
) == wxBORDER_DEFAULT 
) 
 158         style 
|= wxBORDER_SIMPLE
; 
 160     style 
|= wxSP_ARROW_KEYS
; 
 162     SetWindowStyle(style
); 
 165     WXDWORD msStyle 
= MSWGetStyle(GetWindowStyle(), & exStyle
) ; 
 167     wxSize 
sizeText(size
), sizeBtn(size
); 
 168     sizeBtn
.x 
= GetBestSpinnerSize(IsVertical(style
)).x
; 
 170     if ( sizeText
.x 
== wxDefaultCoord 
) 
 172         // DEFAULT_ITEM_WIDTH is the default width for the text control 
 173         sizeText
.x 
= DEFAULT_ITEM_WIDTH 
+ MARGIN_BETWEEN 
+ sizeBtn
.x
; 
 176     sizeText
.x 
-= sizeBtn
.x 
+ MARGIN_BETWEEN
; 
 177     if ( sizeText
.x 
<= 0 ) 
 179         wxLogDebug(_T("not enough space for wxSpinCtrl!")); 
 183     posBtn
.x 
+= sizeText
.x 
+ MARGIN_BETWEEN
; 
 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 
 191     // create the text window 
 193     m_hwndBuddy 
= (WXHWND
)::CreateWindowEx
 
 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 
 209         wxLogLastError(wxT("CreateWindow(buddy text window)")); 
 214     // initialize wxControl 
 215     if ( !CreateControl(parent
, id
, posBtn
, sizeBtn
, style
, validator
, name
) ) 
 218     // now create the real HWND 
 219     WXDWORD spiner_style 
= WS_VISIBLE 
| 
 225     if ( !IsVertical(style
) ) 
 226         spiner_style 
|= UDS_HORZ
; 
 228     if ( style 
& wxSP_WRAP 
) 
 229         spiner_style 
|= UDS_WRAP
; 
 231     if ( !MSWCreateControl(UPDOWN_CLASS
, spiner_style
, posBtn
, sizeBtn
, _T(""), 0) ) 
 234     // subclass the text ctrl to be able to intercept some events 
 235     wxSetWindowUserData(GetBuddyHwnd(), this); 
 236     m_wndProcBuddy 
= (WXFARPROC
)wxSetWindowProc(GetBuddyHwnd(), 
 237                                                 wxBuddyChoiceWndProc
); 
 239     // set up fonts and colours  (This is nomally done in MSWCreateControl) 
 242         SetFont(GetDefaultAttributes().font
); 
 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 ) 
 249         wxGetCharSize(GetHWND(), &cx
, &cy
, GetFont()); 
 251         sizeText
.y 
= EDIT_HEIGHT_FROM_CHAR_HEIGHT(cy
); 
 256     (void)::ShowWindow(GetBuddyHwnd(), SW_SHOW
); 
 258     // associate the list window with the spin button 
 259     (void)::SendMessage(GetHwnd(), UDM_SETBUDDY
, (WPARAM
)GetBuddyHwnd(), 0); 
 261     // do it after finishing with m_hwndBuddy creation to avoid generating 
 262     // initial wxEVT_COMMAND_TEXT_UPDATED message 
 263     ms_allChoiceSpins
.Add(this); 
 265     // initialize the controls contents 
 266     for ( int i 
= 0; i 
< n
; i
++ ) 
 274 bool wxChoice::Create(wxWindow 
*parent
, 
 278                       const wxArrayString
& choices
, 
 280                       const wxValidator
& validator
, 
 281                       const wxString
& name
) 
 283     wxCArrayString 
chs(choices
); 
 284     return Create(parent
, id
, pos
, size
, chs
.GetCount(), chs
.GetStrings(), 
 285                   style
, validator
, name
); 
 288 WXDWORD 
wxChoice::MSWGetStyle(long style
, WXDWORD 
*exstyle
) const 
 290     // we never have an external border 
 291     WXDWORD msStyle 
= wxControl::MSWGetStyle
 
 293                         (style 
& ~wxBORDER_MASK
) | wxBORDER_NONE
, exstyle
 
 296     msStyle 
|= WS_VISIBLE
; 
 298     // wxChoice-specific styles 
 299     msStyle 
|= LBS_NOINTEGRALHEIGHT
; 
 300     if ( style 
& wxCB_SORT 
) 
 303     msStyle 
|= LBS_NOTIFY
; 
 308 bool wxChoice::MSWCommand(WXUINT param
, WXWORD 
WXUNUSED(id
)) 
 310     if ( param 
!= LBN_SELCHANGE
) 
 312         // "selection changed" is the only event we're after 
 316     int n 
= GetSelection(); 
 319         wxCommandEvent 
event(wxEVT_COMMAND_CHOICE_SELECTED
, m_windowId
); 
 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
); 
 333 wxChoice::~wxChoice() 
 338 // ---------------------------------------------------------------------------- 
 339 // adding/deleting items to/from the list 
 340 // ---------------------------------------------------------------------------- 
 342 int wxChoice::DoAppend(const wxString
& item
) 
 344     int n 
= (int)::SendMessage(GetBuddyHwnd(), LB_ADDSTRING
, 0, (LPARAM
)item
.c_str()); 
 348         wxLogLastError(wxT("SendMessage(LB_ADDSTRING)")); 
 354 int wxChoice::DoInsert(const wxString
& item
, int pos
) 
 356     wxCHECK_MSG(!(GetWindowStyle() & wxCB_SORT
), -1, wxT("can't insert into choice")); 
 357     wxCHECK_MSG((pos
>=0) && (pos
<=GetCount()), -1, wxT("invalid index")); 
 359     int n 
= (int)::SendMessage(GetBuddyHwnd(), LB_INSERTSTRING
, pos
, (LPARAM
)item
.c_str()); 
 362         wxLogLastError(wxT("SendMessage(LB_INSERTSTRING)")); 
 368 void wxChoice::Delete(int n
) 
 370     wxCHECK_RET( n 
< GetCount(), wxT("invalid item index in wxChoice::Delete") ); 
 372     if ( HasClientObjectData() ) 
 374         delete GetClientObject(n
); 
 377     ::SendMessage(GetBuddyHwnd(), LB_DELETESTRING
, n
, 0); 
 380 void wxChoice::Clear() 
 384     ::SendMessage(GetBuddyHwnd(), LB_RESETCONTENT
, 0, 0); 
 387 void wxChoice::Free() 
 389     if ( HasClientObjectData() ) 
 391         size_t count 
= GetCount(); 
 392         for ( size_t n 
= 0; n 
< count
; n
++ ) 
 394             delete GetClientObject(n
); 
 399 // ---------------------------------------------------------------------------- 
 401 // ---------------------------------------------------------------------------- 
 403 int wxChoice::GetSelection() const 
 405     return (int)::SendMessage(GetBuddyHwnd(), LB_GETCURSEL
, 0, 0); 
 408 void wxChoice::SetSelection(int n
) 
 410     ::SendMessage(GetBuddyHwnd(), LB_SETCURSEL
, n
, 0); 
 413 // ---------------------------------------------------------------------------- 
 414 // string list functions 
 415 // ---------------------------------------------------------------------------- 
 417 int wxChoice::GetCount() const 
 419     return (int)::SendMessage(GetBuddyHwnd(), LB_GETCOUNT
, 0, 0); 
 422 int wxChoice::FindString(const wxString
& s
) const 
 424     int pos 
= (int)::SendMessage(GetBuddyHwnd(), LB_FINDSTRINGEXACT
, 
 425                                (WPARAM
)-1, (LPARAM
)s
.c_str()); 
 427     return pos 
== LB_ERR 
? wxNOT_FOUND 
: pos
; 
 430 void wxChoice::SetString(int n
, const wxString
& s
) 
 432     wxCHECK_RET( n 
>= 0 && n 
< GetCount(), 
 433                  wxT("invalid item index in wxChoice::SetString") ); 
 435     // we have to delete and add back the string as there is no way to change a 
 438     // we need to preserve the client data 
 440     if ( m_clientDataItemsType 
!= wxClientData_None 
) 
 442         data 
= DoGetItemClientData(n
); 
 444     else // no client data 
 449     ::SendMessage(GetBuddyHwnd(), LB_DELETESTRING
, n
, 0); 
 450     ::SendMessage(GetBuddyHwnd(), LB_INSERTSTRING
, n
, (LPARAM
)s
.c_str() ); 
 454         DoSetItemClientData(n
, data
); 
 456     //else: it's already NULL by default 
 459 wxString 
wxChoice::GetString(int n
) const 
 461     int len 
= (int)::SendMessage(GetBuddyHwnd(), LB_GETTEXTLEN
, n
, 0); 
 464     if ( len 
!= LB_ERR 
&& len 
> 0 ) 
 471                 (LPARAM
)(wxChar 
*)wxStringBuffer(str
, len
) 
 474             wxLogLastError(wxT("SendMessage(LB_GETLBTEXT)")); 
 481 // ---------------------------------------------------------------------------- 
 483 // ---------------------------------------------------------------------------- 
 485 void wxChoice::DoSetItemClientData( int n
, void* clientData 
) 
 487     if ( ::SendMessage(GetHwnd(), LB_SETITEMDATA
, 
 488                        n
, (LPARAM
)clientData
) == LB_ERR 
) 
 490         wxLogLastError(wxT("LB_SETITEMDATA")); 
 494 void* wxChoice::DoGetItemClientData( int n 
) const 
 496     LPARAM rc 
= ::SendMessage(GetHwnd(), LB_GETITEMDATA
, n
, 0); 
 499         wxLogLastError(wxT("LB_GETITEMDATA")); 
 501         // unfortunately, there is no way to return an error code to the user 
 508 void wxChoice::DoSetItemClientObject( int n
, wxClientData
* clientData 
) 
 510     DoSetItemClientData(n
, clientData
); 
 513 wxClientData
* wxChoice::DoGetItemClientObject( int n 
) const 
 515     return (wxClientData 
*)DoGetItemClientData(n
); 
 518 // ---------------------------------------------------------------------------- 
 520 // ---------------------------------------------------------------------------- 
 522 wxSize 
wxChoice::DoGetBestSize() const 
 524     wxSize sizeBtn 
= GetBestSpinnerSize(IsVertical(GetWindowStyle())); 
 525     sizeBtn
.x 
+= DEFAULT_ITEM_WIDTH 
+ MARGIN_BETWEEN
; 
 528     wxGetCharSize(GetHWND(), NULL
, &y
, GetFont()); 
 529     y 
= EDIT_HEIGHT_FROM_CHAR_HEIGHT(y
); 
 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(). 
 536     // if ( sizeBtn.y < y ) 
 538         // make the text tall enough 
 545 void wxChoice::DoMoveWindow(int x
, int y
, int width
, int height
) 
 547     int widthBtn 
= GetBestSpinnerSize(IsVertical(GetWindowStyle())).x
; 
 548     int widthText 
= width 
- widthBtn 
- MARGIN_BETWEEN
; 
 549     if ( widthText 
<= 0 ) 
 551         wxLogDebug(_T("not enough space for wxSpinCtrl!")); 
 554     if ( !::MoveWindow(GetBuddyHwnd(), x
, y
, widthText
, height
, TRUE
) ) 
 556         wxLogLastError(wxT("MoveWindow(buddy)")); 
 559     x 
+= widthText 
+ MARGIN_BETWEEN
; 
 560     if ( !::MoveWindow(GetHwnd(), x
, y
, widthBtn
, height
, TRUE
) ) 
 562         wxLogLastError(wxT("MoveWindow")); 
 566 // get total size of the control 
 567 void wxChoice::DoGetSize(int *x
, int *y
) const 
 569     RECT spinrect
, textrect
, ctrlrect
; 
 570     GetWindowRect(GetHwnd(), &spinrect
); 
 571     GetWindowRect(GetBuddyHwnd(), &textrect
); 
 572     UnionRect(&ctrlrect
, &textrect
, &spinrect
); 
 575         *x 
= ctrlrect
.right 
- ctrlrect
.left
; 
 577         *y 
= ctrlrect
.bottom 
- ctrlrect
.top
; 
 580 void wxChoice::DoGetPosition(int *x
, int *y
) const 
 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
; 
 586     wxChoiceBase::DoGetPosition(x
, y
); 
 588     wxConstCast(this, wxChoice
)->m_hWnd 
= hWnd
; 
 591 #endif // wxUSE_CHOICE && __SMARTPHONE__ && __WXWINCE__