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
); 
 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__