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 // Licence:     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 #define GetBuddyHwnd()      (HWND)(m_hwndBuddy) 
  39 #define IsVertical(wxStyle) ( (wxStyle & wxSP_HORIZONTAL) != wxSP_HORIZONTAL ) 
  41 // ---------------------------------------------------------------------------- 
  43 // ---------------------------------------------------------------------------- 
  45 // the margin between the up-down control and its buddy (can be arbitrary, 
  46 // choose what you like - or may be decide during run-time depending on the 
  48 static const int MARGIN_BETWEEN 
= 0; 
  50 // ============================================================================ 
  52 // ============================================================================ 
  54 wxArrayChoiceSpins 
wxChoice::ms_allChoiceSpins
; 
  56 // ---------------------------------------------------------------------------- 
  57 // wnd proc for the buddy text ctrl 
  58 // ---------------------------------------------------------------------------- 
  60 LRESULT APIENTRY _EXPORT 
wxBuddyChoiceWndProc(HWND hwnd
, 
  65     wxChoice 
*spin 
= (wxChoice 
*)wxGetWindowUserData(hwnd
); 
  67     // forward some messages (the key and focus ones only so far) to 
  72             // if the focus comes from the spin control itself, don't set it 
  73             // back to it -- we don't want to go into an infinite loop 
  74             if ( (WXHWND
)wParam 
== spin
->GetHWND() ) 
  83             spin
->MSWWindowProc(message
, wParam
, lParam
); 
  85             // The control may have been deleted at this point, so check. 
  86             if ( !::IsWindow(hwnd
) || wxGetWindowUserData(hwnd
) != spin 
) 
  91             // we want to get WXK_RETURN in order to generate the event for it 
  92             return DLGC_WANTCHARS
; 
  95     return ::CallWindowProc(CASTWNDPROC spin
->GetBuddyWndProc(), 
  96                             hwnd
, message
, wParam
, lParam
); 
  99 wxChoice 
*wxChoice::GetChoiceForListBox(WXHWND hwndBuddy
) 
 101     wxChoice 
*choice 
= (wxChoice 
*)wxGetWindowUserData((HWND
)hwndBuddy
); 
 103     int i 
= ms_allChoiceSpins
.Index(choice
); 
 105     if ( i 
== wxNOT_FOUND 
) 
 109     wxASSERT_MSG( choice
->m_hwndBuddy 
== hwndBuddy
, 
 110                   wxT("wxChoice has incorrect buddy HWND!") ); 
 115 // ---------------------------------------------------------------------------- 
 117 // ---------------------------------------------------------------------------- 
 119 bool wxChoice::Create(wxWindow 
*parent
, 
 123                       int n
, const wxString choices
[], 
 125                       const wxValidator
& validator
, 
 126                       const wxString
& name
) 
 128     return CreateAndInit(parent
, id
, pos
, size
, n
, choices
, style
, 
 132 bool wxChoice::CreateAndInit(wxWindow 
*parent
, 
 136                              int n
, const wxString choices
[], 
 138                              const wxValidator
& validator
, 
 139                              const wxString
& name
) 
 141     if ( !(style 
& wxSP_VERTICAL
) ) 
 142         style 
|= wxSP_HORIZONTAL
; 
 144     if ( (style 
& wxBORDER_MASK
) == wxBORDER_DEFAULT 
) 
 145         style 
|= wxBORDER_SIMPLE
; 
 147     style 
|= wxSP_ARROW_KEYS
; 
 149     SetWindowStyle(style
); 
 152     WXDWORD msStyle 
= MSWGetStyle(GetWindowStyle(), & exStyle
) ; 
 154     wxSize 
sizeText(size
), sizeBtn(size
); 
 155     sizeBtn
.x 
= GetBestSpinnerSize(IsVertical(style
)).x
; 
 157     if ( sizeText
.x 
== wxDefaultCoord 
) 
 159         // DEFAULT_ITEM_WIDTH is the default width for the text control 
 160         sizeText
.x 
= DEFAULT_ITEM_WIDTH 
+ MARGIN_BETWEEN 
+ sizeBtn
.x
; 
 163     sizeText
.x 
-= sizeBtn
.x 
+ MARGIN_BETWEEN
; 
 164     if ( sizeText
.x 
<= 0 ) 
 166         wxLogDebug(wxT("not enough space for wxSpinCtrl!")); 
 170     posBtn
.x 
+= sizeText
.x 
+ MARGIN_BETWEEN
; 
 172     // we must create the list control before the spin button for the purpose 
 173     // of the dialog navigation: if there is a static text just before the spin 
 174     // control, activating it by Alt-letter should give focus to the text 
 175     // control, not the spin and the dialog navigation code will give focus to 
 176     // the next control (at Windows level), not the one after it 
 178     // create the text window 
 180     m_hwndBuddy 
= (WXHWND
)::CreateWindowEx
 
 182                      exStyle
,                // sunken border 
 183                      wxT("LISTBOX"),         // window class 
 184                      NULL
,                   // no window title 
 185                      msStyle
,                // style (will be shown later) 
 186                      pos
.x
, pos
.y
,           // position 
 187                      0, 0,                   // size (will be set later) 
 188                      GetHwndOf(parent
),      // parent 
 189                      (HMENU
)-1,              // control id 
 190                      wxGetInstance(),        // app instance 
 191                      NULL                    
// unused client data 
 196         wxLogLastError(wxT("CreateWindow(buddy text window)")); 
 201     // initialize wxControl 
 202     if ( !CreateControl(parent
, id
, posBtn
, sizeBtn
, style
, validator
, name
) ) 
 205     // now create the real HWND 
 206     WXDWORD spiner_style 
= WS_VISIBLE 
| 
 212     if ( !IsVertical(style
) ) 
 213         spiner_style 
|= UDS_HORZ
; 
 215     if ( style 
& wxSP_WRAP 
) 
 216         spiner_style 
|= UDS_WRAP
; 
 218     if ( !MSWCreateControl(UPDOWN_CLASS
, spiner_style
, posBtn
, sizeBtn
, wxEmptyString
, 0) ) 
 221     // subclass the text ctrl to be able to intercept some events 
 222     wxSetWindowUserData(GetBuddyHwnd(), this); 
 223     m_wndProcBuddy 
= (WXFARPROC
)wxSetWindowProc(GetBuddyHwnd(), 
 224                                                 wxBuddyChoiceWndProc
); 
 226     // set up fonts and colours  (This is nomally done in MSWCreateControl) 
 229         SetFont(GetDefaultAttributes().font
); 
 231     // set the size of the text window - can do it only now, because we 
 232     // couldn't call DoGetBestSize() before as font wasn't set 
 233     if ( sizeText
.y 
<= 0 ) 
 236         wxGetCharSize(GetHWND(), &cx
, &cy
, GetFont()); 
 238         sizeText
.y 
= EDIT_HEIGHT_FROM_CHAR_HEIGHT(cy
); 
 241     SetInitialSize(size
); 
 243     (void)::ShowWindow(GetBuddyHwnd(), SW_SHOW
); 
 245     // associate the list window with the spin button 
 246     (void)::SendMessage(GetHwnd(), UDM_SETBUDDY
, (WPARAM
)GetBuddyHwnd(), 0); 
 248     // do it after finishing with m_hwndBuddy creation to avoid generating 
 249     // initial wxEVT_COMMAND_TEXT_UPDATED message 
 250     ms_allChoiceSpins
.Add(this); 
 252     // initialize the controls contents 
 253     for ( int i 
= 0; i 
< n
; i
++ ) 
 261 bool wxChoice::Create(wxWindow 
*parent
, 
 265                       const wxArrayString
& choices
, 
 267                       const wxValidator
& validator
, 
 268                       const wxString
& name
) 
 270     wxCArrayString 
chs(choices
); 
 271     return Create(parent
, id
, pos
, size
, chs
.GetCount(), chs
.GetStrings(), 
 272                   style
, validator
, name
); 
 275 WXDWORD 
wxChoice::MSWGetStyle(long style
, WXDWORD 
*exstyle
) const 
 277     // we never have an external border 
 278     WXDWORD msStyle 
= wxControl::MSWGetStyle
 
 280                         (style 
& ~wxBORDER_MASK
) | wxBORDER_NONE
, exstyle
 
 283     msStyle 
|= WS_VISIBLE
; 
 285     // wxChoice-specific styles 
 286     msStyle 
|= LBS_NOINTEGRALHEIGHT
; 
 287     if ( style 
& wxCB_SORT 
) 
 290     msStyle 
|= LBS_NOTIFY
; 
 295 bool wxChoice::MSWCommand(WXUINT param
, WXWORD 
WXUNUSED(id
)) 
 297     if ( param 
!= LBN_SELCHANGE
) 
 299         // "selection changed" is the only event we're after 
 303     int n 
= GetSelection(); 
 306         wxCommandEvent 
event(wxEVT_COMMAND_CHOICE_SELECTED
, m_windowId
); 
 308         event
.SetEventObject(this); 
 309         event
.SetString(GetStringSelection()); 
 310         if ( HasClientObjectData() ) 
 311             event
.SetClientObject( GetClientObject(n
) ); 
 312         else if ( HasClientUntypedData() ) 
 313             event
.SetClientData( GetClientData(n
) ); 
 314         ProcessCommand(event
); 
 320 wxChoice::~wxChoice() 
 325 // ---------------------------------------------------------------------------- 
 326 // adding/deleting items to/from the list 
 327 // ---------------------------------------------------------------------------- 
 329 int wxChoice::DoInsertItems(const wxArrayStringsAdapter
& items
, 
 332                             wxClientDataType type
) 
 334     MSWAllocStorage(items
, LB_INITSTORAGE
); 
 336     const bool append 
= pos 
== GetCount(); 
 337     const unsigned msg 
= append 
? LB_ADDSTRING 
: LB_INSERTSTRING
; 
 343     const unsigned int numItems 
= items
.GetCount(); 
 344     for ( unsigned int i 
= 0; i 
< numItems
; ++i 
) 
 346         n 
= MSWInsertOrAppendItem(pos
, items
[i
], msg
); 
 350         AssignNewItemClientData(n
, clientData
, i
, type
); 
 356 void wxChoice::DoDeleteOneItem(unsigned int n
) 
 358     wxCHECK_RET( IsValid(n
), wxT("invalid item index in wxChoice::Delete") ); 
 360     ::SendMessage(GetBuddyHwnd(), LB_DELETESTRING
, n
, 0); 
 363 void wxChoice::DoClear() 
 365     ::SendMessage(GetBuddyHwnd(), LB_RESETCONTENT
, 0, 0); 
 368 // ---------------------------------------------------------------------------- 
 370 // ---------------------------------------------------------------------------- 
 372 int wxChoice::GetSelection() const 
 374     return (int)::SendMessage(GetBuddyHwnd(), LB_GETCURSEL
, 0, 0); 
 377 void wxChoice::SetSelection(int n
) 
 379     ::SendMessage(GetBuddyHwnd(), LB_SETCURSEL
, n
, 0); 
 382 // ---------------------------------------------------------------------------- 
 383 // string list functions 
 384 // ---------------------------------------------------------------------------- 
 386 unsigned int wxChoice::GetCount() const 
 388     return (unsigned int)::SendMessage(GetBuddyHwnd(), LB_GETCOUNT
, 0, 0); 
 391 int wxChoice::FindString(const wxString
& s
, bool bCase
) const 
 393     // back to base class search for not native search type 
 395        return wxItemContainerImmutable::FindString( s
, bCase 
); 
 397     int pos 
= (int)::SendMessage(GetBuddyHwnd(), LB_FINDSTRINGEXACT
, 
 398                                (WPARAM
)-1, (LPARAM
)s
.c_str()); 
 400     return pos 
== LB_ERR 
? wxNOT_FOUND 
: pos
; 
 403 void wxChoice::SetString(unsigned int n
, const wxString
& s
) 
 405     wxCHECK_RET( IsValid(n
), 
 406                  wxT("invalid item index in wxChoice::SetString") ); 
 408     // we have to delete and add back the string as there is no way to change a 
 411     // we need to preserve the client data 
 413     if ( m_clientDataItemsType 
!= wxClientData_None 
) 
 415         data 
= DoGetItemClientData(n
); 
 417     else // no client data 
 422     ::SendMessage(GetBuddyHwnd(), LB_DELETESTRING
, n
, 0); 
 423     ::SendMessage(GetBuddyHwnd(), LB_INSERTSTRING
, n
, (LPARAM
)s
.c_str() ); 
 427         DoSetItemClientData(n
, data
); 
 429     //else: it's already NULL by default 
 432 wxString 
wxChoice::GetString(unsigned int n
) const 
 434     int len 
= (int)::SendMessage(GetBuddyHwnd(), LB_GETTEXTLEN
, n
, 0); 
 437     if ( len 
!= LB_ERR 
&& len 
> 0 ) 
 444                 (LPARAM
)(wxChar 
*)wxStringBuffer(str
, len
) 
 447             wxLogLastError(wxT("SendMessage(LB_GETLBTEXT)")); 
 454 // ---------------------------------------------------------------------------- 
 456 // ---------------------------------------------------------------------------- 
 458 void wxChoice::DoSetItemClientData(unsigned int n
, void* clientData
) 
 460     if ( ::SendMessage(GetHwnd(), LB_SETITEMDATA
, 
 461                        n
, (LPARAM
)clientData
) == LB_ERR 
) 
 463         wxLogLastError(wxT("LB_SETITEMDATA")); 
 467 void* wxChoice::DoGetItemClientData(unsigned int n
) const 
 469     LPARAM rc 
= ::SendMessage(GetHwnd(), LB_GETITEMDATA
, n
, 0); 
 472         wxLogLastError(wxT("LB_GETITEMDATA")); 
 474         // unfortunately, there is no way to return an error code to the user 
 481 // ---------------------------------------------------------------------------- 
 483 // ---------------------------------------------------------------------------- 
 485 wxSize 
wxChoice::DoGetBestSize() const 
 487     wxSize sizeBtn 
= GetBestSpinnerSize(IsVertical(GetWindowStyle())); 
 488     sizeBtn
.x 
+= DEFAULT_ITEM_WIDTH 
+ MARGIN_BETWEEN
; 
 491     wxGetCharSize(GetHWND(), NULL
, &y
, GetFont()); 
 492     y 
= EDIT_HEIGHT_FROM_CHAR_HEIGHT(y
); 
 494     // JACS: we should always use the height calculated 
 495     // from above, because otherwise we'll get a spin control 
 496     // that's too big. So never use the height calculated 
 497     // from wxSpinButton::DoGetBestSize(). 
 499     // if ( sizeBtn.y < y ) 
 501         // make the text tall enough 
 508 void wxChoice::DoMoveWindow(int x
, int y
, int width
, int height
) 
 510     int widthBtn 
= GetBestSpinnerSize(IsVertical(GetWindowStyle())).x
; 
 511     int widthText 
= width 
- widthBtn 
- MARGIN_BETWEEN
; 
 512     if ( widthText 
<= 0 ) 
 514         wxLogDebug(wxT("not enough space for wxSpinCtrl!")); 
 517     if ( !::MoveWindow(GetBuddyHwnd(), x
, y
, widthText
, height
, TRUE
) ) 
 519         wxLogLastError(wxT("MoveWindow(buddy)")); 
 522     x 
+= widthText 
+ MARGIN_BETWEEN
; 
 523     if ( !::MoveWindow(GetHwnd(), x
, y
, widthBtn
, height
, TRUE
) ) 
 525         wxLogLastError(wxT("MoveWindow")); 
 529 // get total size of the control 
 530 void wxChoice::DoGetSize(int *x
, int *y
) const 
 532     RECT spinrect
, textrect
, ctrlrect
; 
 533     GetWindowRect(GetHwnd(), &spinrect
); 
 534     GetWindowRect(GetBuddyHwnd(), &textrect
); 
 535     UnionRect(&ctrlrect
, &textrect
, &spinrect
); 
 538         *x 
= ctrlrect
.right 
- ctrlrect
.left
; 
 540         *y 
= ctrlrect
.bottom 
- ctrlrect
.top
; 
 543 void wxChoice::DoGetPosition(int *x
, int *y
) const 
 545     // hack: pretend that our HWND is the text control just for a moment 
 546     WXHWND hWnd 
= GetHWND(); 
 547     wxConstCast(this, wxChoice
)->m_hWnd 
= m_hwndBuddy
; 
 549     wxChoiceBase::DoGetPosition(x
, y
); 
 551     wxConstCast(this, wxChoice
)->m_hWnd 
= hWnd
; 
 554 #endif // wxUSE_CHOICE && __SMARTPHONE__ && __WXWINCE__