]> git.saurik.com Git - wxWidgets.git/commitdiff
Native SmartPhone wxChoice implementation.
authorWłodzimierz Skiba <abx@abx.art.pl>
Mon, 23 Aug 2004 10:53:57 +0000 (10:53 +0000)
committerWłodzimierz Skiba <abx@abx.art.pl>
Mon, 23 Aug 2004 10:53:57 +0000 (10:53 +0000)
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@28865 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775

build/bakefiles/files.bkl
docs/changes.txt
include/wx/choice.h
include/wx/msw/wince/choicece.h [new file with mode: 0644]
src/msw/choice.cpp
src/msw/control.cpp
src/msw/wince/choicece.cpp [new file with mode: 0644]

index 9b127643abefce4e789fafdd7f9db1d9e2897fdd..1c00e590f4236a870d2bdadf3b5a32eef71521d2 100644 (file)
@@ -1318,15 +1318,17 @@ IMPORTANT: please read docs/tech/tn0016.txt before modifying this file!
     src/generic/dirdlgg.cpp
     src/generic/fdrepdlg.cpp
     src/generic/fontdlgg.cpp
+    src/msw/wince/choicece.cpp 
     src/msw/wince/crt.cpp
     src/msw/wince/filedlgwce.cpp
     src/msw/wince/helpwce.cpp
-    src/msw/wince/tbarwce.cpp
     src/msw/wince/menuce.cpp
+    src/msw/wince/tbarwce.cpp
 </set>
 <set var="WINCE_HDR" hints="files">
     wx/generic/fdrepdlg.h
     wx/generic/fontdlgg.h
+    wx/msw/wince/choicece.h 
     wx/msw/wince/helpwce.h
     wx/msw/wince/libraries.h
     wx/msw/wince/missing.h
index 8584dde74954e536ef3785fd03273d86adb652e4..8616b050af898831918123ae137ab0bb7a753b58 100644 (file)
@@ -273,6 +273,7 @@ wxUniv/X11:
 
 wxWinCE:
 
+- added native SmartPhone wxChoice implementation using spinners
 - added automatized but customizable handling of native SmartPhone menus
 - fixed wxRadioBox and wxStaticBox
 
index 3a95d2eff07f967bf7a174b196894638f4a0d173..c11aa3555030e04456db752875597fa8ecf164bd 100644 (file)
@@ -67,6 +67,8 @@ private:
 
 #if defined(__WXUNIVERSAL__)
     #include "wx/univ/choice.h"
+#elif defined(__SMARTPHONE__)
+    #include "wx/msw/wince/choicece.h"
 #elif defined(__WXMSW__)
     #include "wx/msw/choice.h"
 #elif defined(__WXMOTIF__)
diff --git a/include/wx/msw/wince/choicece.h b/include/wx/msw/wince/choicece.h
new file mode 100644 (file)
index 0000000..bb89360
--- /dev/null
@@ -0,0 +1,141 @@
+///////////////////////////////////////////////////////////////////////////////
+// Name:        wx/msw/wince/choicece.h
+// Purpose:     wxChoice implementation for Smartphones
+// Author:      Wlodzimierz ABX Skiba
+// Modified by:
+// Created:     29.07.2004
+// RCS-ID:      $Id$
+// Copyright:   (c) Wlodzimierz Skiba
+// License:     wxWindows licence
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef _WX_CHOICECE_H_BASE_
+#define _WX_CHOICECE_H_BASE_
+
+// ----------------------------------------------------------------------------
+// headers
+// ----------------------------------------------------------------------------
+
+#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
+    #pragma interface "choicece.h"
+#endif
+
+#include "wx/defs.h"
+
+#if wxUSE_CHOICE
+
+#include "wx/dynarray.h"
+
+class WXDLLEXPORT wxChoice;
+WX_DEFINE_EXPORTED_ARRAY_PTR(wxChoice *, wxArrayChoiceSpins);
+
+// ----------------------------------------------------------------------------
+// Choice item
+// ----------------------------------------------------------------------------
+
+class WXDLLEXPORT wxChoice : public wxChoiceBase
+{
+public:
+    // ctors
+    wxChoice() { }
+    virtual ~wxChoice();
+
+    wxChoice(wxWindow *parent,
+             wxWindowID id,
+             const wxPoint& pos = wxDefaultPosition,
+             const wxSize& size = wxDefaultSize,
+             int n = 0, const wxString choices[] = NULL,
+             long style = 0,
+             const wxValidator& validator = wxDefaultValidator,
+             const wxString& name = wxChoiceNameStr)
+    {
+        Create(parent, id, pos, size, n, choices, style, validator, name);
+    }
+    wxChoice(wxWindow *parent,
+             wxWindowID id,
+             const wxPoint& pos,
+             const wxSize& size,
+             const wxArrayString& choices,
+             long style = 0,
+             const wxValidator& validator = wxDefaultValidator,
+             const wxString& name = wxChoiceNameStr)
+    {
+        Create(parent, id, pos, size, choices, style, validator, name);
+    }
+
+    bool Create(wxWindow *parent,
+                wxWindowID id,
+                const wxPoint& pos = wxDefaultPosition,
+                const wxSize& size = wxDefaultSize,
+                int n = 0, const wxString choices[] = NULL,
+                long style = 0,
+                const wxValidator& validator = wxDefaultValidator,
+                const wxString& name = wxChoiceNameStr);
+
+    bool Create(wxWindow *parent,
+                wxWindowID id,
+                const wxPoint& pos,
+                const wxSize& size,
+                const wxArrayString& choices,
+                long style = 0,
+                const wxValidator& validator = wxDefaultValidator,
+                const wxString& name = wxChoiceNameStr);
+
+    // implement base class pure virtuals
+    virtual int DoAppend(const wxString& item);
+    virtual int DoInsert(const wxString& item, int pos);
+    virtual void Delete(int n);
+    virtual void Clear() ;
+
+    virtual int GetCount() const;
+    virtual int GetSelection() const;
+    virtual void SetSelection(int n);
+
+    virtual int FindString(const wxString& s) const;
+    virtual wxString GetString(int n) const;
+    virtual void SetString(int n, const wxString& s);
+
+    // get the subclassed window proc of the buddy list of choices
+    WXFARPROC GetBuddyWndProc() const { return m_wndProcBuddy; }
+
+protected:
+    virtual void DoSetItemClientData( int n, void* clientData );
+    virtual void* DoGetItemClientData( int n ) const;
+    virtual void DoSetItemClientObject( int n, wxClientData* clientData );
+    virtual wxClientData* DoGetItemClientObject( int n ) const;
+
+    // MSW implementation
+    virtual void DoGetPosition(int *x, int *y) const;
+    virtual void DoMoveWindow(int x, int y, int width, int height);
+    virtual wxSize DoGetBestSize() const;
+    virtual void DoGetSize(int *width, int *height) const;
+
+    virtual WXDWORD MSWGetStyle(long style, WXDWORD *exstyle) const;
+
+    // create and initialize the control
+    bool CreateAndInit(wxWindow *parent, wxWindowID id,
+                       const wxPoint& pos,
+                       const wxSize& size,
+                       int n, const wxString choices[],
+                       long style,
+                       const wxValidator& validator,
+                       const wxString& name);
+
+    // free all memory we have (used by Clear() and dtor)
+    void Free();
+
+    // the data for the "buddy" list
+    WXHWND     m_hwndBuddy;
+    WXFARPROC  m_wndProcBuddy;
+
+    // all existing wxChoice - this allows to find the one corresponding to
+    // the given buddy window in GetSpinChoiceCtrl()
+    static wxArrayChoiceSpins ms_allChoiceSpins;
+
+private:
+    DECLARE_DYNAMIC_CLASS_NO_COPY(wxChoice)
+};
+
+#endif // wxUSE_CHOICE
+
+#endif // _WX_CHOICECE_H_BASE_
index 893d0ad9a2fa3ca6eeae2f31d8904e94a499d002..7faa959c51f06e78cbb7a15aff3729407ef048ab 100644 (file)
@@ -28,7 +28,7 @@
     #pragma hdrstop
 #endif
 
-#if wxUSE_CHOICE
+#if wxUSE_CHOICE && !defined(__SMARTPHONE__)
 
 #ifndef WX_PRECOMP
     #include "wx/choice.h"
@@ -601,4 +601,4 @@ WXHBRUSH wxChoice::OnCtlColor(WXHDC pDC, WXHWND WXUNUSED(pWnd), WXUINT WXUNUSED(
     return (WXHBRUSH)brush->GetResourceHandle();
 }
 
-#endif // wxUSE_CHOICE
+#endif // wxUSE_CHOICE && !__SMARTPHONE__
index d00df75d1d312cb3ce3a68eef567328bd64af7eb..20ae5ca0dce9bbe091fc27d784a62b737075ed08 100644 (file)
@@ -213,8 +213,14 @@ wxSize wxControl::DoGetBestSize() const
 wxSize wxControl::GetBestSpinerSize(const bool is_vertical) const
 {
     // take size according to layout
-    wxSize bestSize(GetSystemMetrics(is_vertical ? SM_CXVSCROLL : SM_CXHSCROLL),
-                    GetSystemMetrics(is_vertical ? SM_CYVSCROLL : SM_CYHSCROLL));
+    wxSize bestSize(
+#ifdef __SMARTPHONE__
+                    0,GetCharHeight()
+#else !__SMARTPHONE__
+                    GetSystemMetrics(is_vertical ? SM_CXVSCROLL : SM_CXHSCROLL),
+                    GetSystemMetrics(is_vertical ? SM_CYVSCROLL : SM_CYHSCROLL)
+#endif __SMARTPHONE__/!__SMARTPHONE__
+    );
 
     // correct size as for undocumented MSW variants cases (WinCE and perhaps others)
     if (bestSize.x==0)
diff --git a/src/msw/wince/choicece.cpp b/src/msw/wince/choicece.cpp
new file mode 100644 (file)
index 0000000..5f46de4
--- /dev/null
@@ -0,0 +1,550 @@
+///////////////////////////////////////////////////////////////////////////////
+// Name:        src/msw/wince/choicece.cpp
+// Purpose:     wxChoice implementation for Smartphones
+// Author:      Wlodzimierz ABX Skiba
+// Modified by:
+// Created:     29.07.2004
+// RCS-ID:      $Id$
+// Copyright:   (c) Wlodzimierz Skiba
+// License:     wxWindows licence
+///////////////////////////////////////////////////////////////////////////////
+
+
+// ============================================================================
+// declarations
+// ============================================================================
+
+// ----------------------------------------------------------------------------
+// headers
+// ----------------------------------------------------------------------------
+
+#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
+    #pragma implementation "choicece.h"
+#endif
+
+// For compilers that support precompilation, includes "wx.h".
+#include "wx/wxprec.h"
+
+#ifdef __BORLANDC__
+    #pragma hdrstop
+#endif
+
+#ifndef WX_PRECOMP
+    #include "wx/choice.h"
+#endif
+
+#include "wx/spinbutt.h" // for wxSpinnerBestSize
+
+#include <commctrl.h>
+#include "wx/msw/missing.h"
+#include "wx/msw/winundef.h"
+
+#if wxUSE_CHOICE && defined(__SMARTPHONE__)
+
+#if wxUSE_EXTENDED_RTTI
+// TODO
+#else
+IMPLEMENT_DYNAMIC_CLASS(wxChoice, wxControl)
+#endif
+
+#define GetBuddyHwnd()      (HWND)(m_hwndBuddy)
+
+#define IsVertical(wxStyle) ( (wxStyle & wxSP_HORIZONTAL) != wxSP_HORIZONTAL )
+
+// ----------------------------------------------------------------------------
+// constants
+// ----------------------------------------------------------------------------
+
+// the margin between the up-down control and its buddy (can be arbitrary,
+// choose what you like - or may be decide during run-time depending on the
+// font size?)
+static const int MARGIN_BETWEEN = 0;
+
+// ============================================================================
+// implementation
+// ============================================================================
+
+wxArrayChoiceSpins wxChoice::ms_allChoiceSpins;
+
+// ----------------------------------------------------------------------------
+// wnd proc for the buddy text ctrl
+// ----------------------------------------------------------------------------
+
+LRESULT APIENTRY _EXPORT wxBuddyChoiceWndProc(HWND hwnd,
+                                            UINT message,
+                                            WPARAM wParam,
+                                            LPARAM lParam)
+{
+    wxChoice *spin = (wxChoice *)wxGetWindowUserData(hwnd);
+
+    // forward some messages (the key and focus ones only so far) to
+    // the spin ctrl
+    switch ( message )
+    {
+        case WM_SETFOCUS:
+            // if the focus comes from the spin control itself, don't set it
+            // back to it -- we don't want to go into an infinite loop
+            if ( (WXHWND)wParam == spin->GetHWND() )
+                break;
+            //else: fall through
+
+        case WM_KILLFOCUS:
+        case WM_CHAR:
+        case WM_DEADCHAR:
+        case WM_KEYUP:
+        case WM_KEYDOWN:
+            spin->MSWWindowProc(message, wParam, lParam);
+
+            // The control may have been deleted at this point, so check.
+            if ( !::IsWindow(hwnd) || wxGetWindowUserData(hwnd) != spin )
+                return 0;
+            break;
+
+        case WM_GETDLGCODE:
+            // we want to get WXK_RETURN in order to generate the event for it
+            return DLGC_WANTCHARS;
+    }
+
+    return ::CallWindowProc(CASTWNDPROC spin->GetBuddyWndProc(),
+                            hwnd, message, wParam, lParam);
+}
+
+// ----------------------------------------------------------------------------
+// creation
+// ----------------------------------------------------------------------------
+
+bool wxChoice::Create(wxWindow *parent,
+                      wxWindowID id,
+                      const wxPoint& pos,
+                      const wxSize& size,
+                      int n, const wxString choices[],
+                      long style,
+                      const wxValidator& validator,
+                      const wxString& name)
+{
+    return CreateAndInit(parent, id, pos, size, n, choices, style,
+                         validator, name);
+}
+
+bool wxChoice::CreateAndInit(wxWindow *parent,
+                             wxWindowID id,
+                             const wxPoint& pos,
+                             const wxSize& size,
+                             int n, const wxString choices[],
+                             long style,
+                             const wxValidator& validator,
+                             const wxString& name)
+{
+    if ( !(style & wxSP_VERTICAL) )
+        style |= wxSP_HORIZONTAL;
+
+    if ( (style & wxBORDER_MASK) == wxBORDER_DEFAULT )
+        style |= wxBORDER_SIMPLE;
+
+    style |= wxSP_ARROW_KEYS;
+
+    SetWindowStyle(style);
+
+    WXDWORD exStyle = 0;
+    WXDWORD msStyle = MSWGetStyle(GetWindowStyle(), & exStyle) ;
+
+    wxSize sizeText(size), sizeBtn(size);
+    sizeBtn.x = GetBestSpinerSize(IsVertical(style)).x;
+
+    sizeBtn.x;
+
+    if ( sizeText.x == wxDefaultCoord )
+    {
+        // DEFAULT_ITEM_WIDTH is the default width for the text control
+        sizeText.x = DEFAULT_ITEM_WIDTH + MARGIN_BETWEEN + sizeBtn.x;
+    }
+
+    sizeText.x -= sizeBtn.x + MARGIN_BETWEEN;
+    if ( sizeText.x <= 0 )
+    {
+        wxLogDebug(_T("not enough space for wxSpinCtrl!"));
+    }
+
+    wxPoint posBtn(pos);
+    posBtn.x += sizeText.x + MARGIN_BETWEEN;
+
+    // we must create the list control before the spin button for the purpose
+    // of the dialog navigation: if there is a static text just before the spin
+    // control, activating it by Alt-letter should give focus to the text
+    // control, not the spin and the dialog navigation code will give focus to
+    // the next control (at Windows level), not the one after it
+
+    // create the text window
+
+    m_hwndBuddy = (WXHWND)::CreateWindowEx
+                    (
+                     exStyle,                // sunken border
+                     _T("LISTBOX"),          // window class
+                     NULL,                   // no window title
+                     msStyle,                // style (will be shown later)
+                     pos.x, pos.y,           // position
+                     0, 0,                   // size (will be set later)
+                     GetHwndOf(parent),      // parent
+                     (HMENU)-1,              // control id
+                     wxGetInstance(),        // app instance
+                     NULL                    // unused client data
+                    );
+
+    if ( !m_hwndBuddy )
+    {
+        wxLogLastError(wxT("CreateWindow(buddy text window)"));
+
+        return false;
+    }
+
+    // initialize wxControl
+    if ( !CreateControl(parent, id, posBtn, sizeBtn, style, validator, name) )
+        return false;
+
+    // now create the real HWND
+    WXDWORD spiner_style = WS_VISIBLE |
+                           UDS_ALIGNRIGHT |
+                           UDS_ARROWKEYS |
+                           UDS_SETBUDDYINT |
+                           UDS_EXPANDABLE;
+
+    if ( !IsVertical(style) )
+        spiner_style |= UDS_HORZ;
+
+    if ( style & wxSP_WRAP )
+        spiner_style |= UDS_WRAP;
+
+    if ( !MSWCreateControl(UPDOWN_CLASS, spiner_style, posBtn, sizeBtn, _T(""), 0) )
+        return false;
+
+    // subclass the text ctrl to be able to intercept some events
+    wxSetWindowUserData(GetBuddyHwnd(), this);
+    m_wndProcBuddy = (WXFARPROC)wxSetWindowProc(GetBuddyHwnd(),
+                                                wxBuddyChoiceWndProc);
+
+    // set up fonts and colours  (This is nomally done in MSWCreateControl)
+    InheritAttributes();
+    if (!m_hasFont)
+        SetFont(GetDefaultAttributes().font);
+
+    // set the size of the text window - can do it only now, because we
+    // couldn't call DoGetBestSize() before as font wasn't set
+    if ( sizeText.y <= 0 )
+    {
+        int cx, cy;
+        wxGetCharSize(GetHWND(), &cx, &cy, GetFont());
+
+        sizeText.y = EDIT_HEIGHT_FROM_CHAR_HEIGHT(cy);
+    }
+
+    SetBestSize(size);
+
+    (void)::ShowWindow(GetBuddyHwnd(), SW_SHOW);
+
+    // associate the list window with the spin button
+    (void)::SendMessage(GetHwnd(), UDM_SETBUDDY, (WPARAM)GetBuddyHwnd(), 0);
+
+    // do it after finishing with m_hwndBuddy creation to avoid generating
+    // initial wxEVT_COMMAND_TEXT_UPDATED message
+    ms_allChoiceSpins.Add(this);
+
+    // initialize the controls contents
+    for ( int i = 0; i < n; i++ )
+    {
+        Append(choices[i]);
+    }
+
+    return true;
+}
+
+bool wxChoice::Create(wxWindow *parent,
+                      wxWindowID id,
+                      const wxPoint& pos,
+                      const wxSize& size,
+                      const wxArrayString& choices,
+                      long style,
+                      const wxValidator& validator,
+                      const wxString& name)
+{
+    wxCArrayString chs(choices);
+    return Create(parent, id, pos, size, chs.GetCount(), chs.GetStrings(),
+                  style, validator, name);
+}
+
+WXDWORD wxChoice::MSWGetStyle(long style, WXDWORD *exstyle) const
+{
+    // we never have an external border
+    WXDWORD msStyle = wxControl::MSWGetStyle
+                      (
+                        (style & ~wxBORDER_MASK) | wxBORDER_NONE, exstyle
+                      );
+
+    msStyle |= WS_VISIBLE;
+
+    // wxChoice-specific styles
+    msStyle |= LBS_NOINTEGRALHEIGHT;
+    if ( style & wxCB_SORT )
+        msStyle |= LBS_SORT;
+
+    return msStyle;
+}
+
+wxChoice::~wxChoice()
+{
+    Free();
+}
+
+// ----------------------------------------------------------------------------
+// adding/deleting items to/from the list
+// ----------------------------------------------------------------------------
+
+int wxChoice::DoAppend(const wxString& item)
+{
+    int n = (int)SendMessage(GetBuddyHwnd(), LB_ADDSTRING, 0, (LPARAM)item.c_str());
+
+    if ( n == LB_ERR )
+    {
+        wxLogLastError(wxT("SendMessage(LB_ADDSTRING)"));
+    }
+
+    return n;
+}
+
+int wxChoice::DoInsert(const wxString& item, int pos)
+{
+    wxCHECK_MSG(!(GetWindowStyle() & wxCB_SORT), -1, wxT("can't insert into choice"));
+    wxCHECK_MSG((pos>=0) && (pos<=GetCount()), -1, wxT("invalid index"));
+
+    int n = (int)SendMessage(GetBuddyHwnd(), LB_INSERTSTRING, pos, (LPARAM)item.c_str());
+    if ( n == LB_ERR )
+    {
+        wxLogLastError(wxT("SendMessage(LB_INSERTSTRING)"));
+    }
+
+    return n;
+}
+
+void wxChoice::Delete(int n)
+{
+    wxCHECK_RET( n < GetCount(), wxT("invalid item index in wxChoice::Delete") );
+
+    if ( HasClientObjectData() )
+    {
+        delete GetClientObject(n);
+    }
+
+    SendMessage(GetBuddyHwnd(), LB_DELETESTRING, n, 0);
+}
+
+void wxChoice::Clear()
+{
+    Free();
+
+    SendMessage(GetBuddyHwnd(), LB_RESETCONTENT, 0, 0);
+}
+
+void wxChoice::Free()
+{
+    if ( HasClientObjectData() )
+    {
+        size_t count = GetCount();
+        for ( size_t n = 0; n < count; n++ )
+        {
+            delete GetClientObject(n);
+        }
+    }
+}
+
+// ----------------------------------------------------------------------------
+// selection
+// ----------------------------------------------------------------------------
+
+int wxChoice::GetSelection() const
+{
+    return (int)SendMessage(GetBuddyHwnd(), LB_GETCURSEL, 0, 0);
+}
+
+void wxChoice::SetSelection(int n)
+{
+    SendMessage(GetBuddyHwnd(), LB_SETCURSEL, n, 0);
+}
+
+// ----------------------------------------------------------------------------
+// string list functions
+// ----------------------------------------------------------------------------
+
+int wxChoice::GetCount() const
+{
+    return (int)SendMessage(GetBuddyHwnd(), LB_GETCOUNT, 0, 0);
+}
+
+int wxChoice::FindString(const wxString& s) const
+{
+    int pos = (int)SendMessage(GetBuddyHwnd(), LB_FINDSTRINGEXACT,
+                               (WPARAM)-1, (LPARAM)s.c_str());
+
+    return pos == LB_ERR ? wxNOT_FOUND : pos;
+}
+
+void wxChoice::SetString(int n, const wxString& s)
+{
+    wxCHECK_RET( n >= 0 && n < GetCount(),
+                 wxT("invalid item index in wxChoice::SetString") );
+
+    // we have to delete and add back the string as there is no way to change a
+    // string in place
+
+    // we need to preserve the client data
+    void *data;
+    if ( m_clientDataItemsType != wxClientData_None )
+    {
+        data = DoGetItemClientData(n);
+    }
+    else // no client data
+    {
+        data = NULL;
+    }
+
+    ::SendMessage(GetBuddyHwnd(), LB_DELETESTRING, n, 0);
+    ::SendMessage(GetBuddyHwnd(), LB_INSERTSTRING, n, (LPARAM)s.c_str() );
+
+    if ( data )
+    {
+        DoSetItemClientData(n, data);
+    }
+    //else: it's already NULL by default
+}
+
+wxString wxChoice::GetString(int n) const
+{
+    int len = (int)::SendMessage(GetBuddyHwnd(), LB_GETTEXTLEN, n, 0);
+
+    wxString str;
+    if ( len != LB_ERR && len > 0 )
+    {
+        if ( ::SendMessage
+               (
+                GetBuddyHwnd(),
+                LB_GETTEXT,
+                n,
+                (LPARAM)(wxChar *)wxStringBuffer(str, len)
+                ) == LB_ERR )
+        {
+            wxLogLastError(wxT("SendMessage(LB_GETLBTEXT)"));
+        }
+    }
+
+    return str;
+}
+
+// ----------------------------------------------------------------------------
+// client data
+// ----------------------------------------------------------------------------
+
+void wxChoice::DoSetItemClientData( int n, void* clientData )
+{
+    if ( ::SendMessage(GetHwnd(), LB_SETITEMDATA,
+                       n, (LPARAM)clientData) == LB_ERR )
+    {
+        wxLogLastError(wxT("LB_SETITEMDATA"));
+    }
+}
+
+void* wxChoice::DoGetItemClientData( int n ) const
+{
+    LPARAM rc = SendMessage(GetHwnd(), LB_GETITEMDATA, n, 0);
+    if ( rc == LB_ERR )
+    {
+        wxLogLastError(wxT("LB_GETITEMDATA"));
+
+        // unfortunately, there is no way to return an error code to the user
+        rc = (LPARAM) NULL;
+    }
+
+    return (void *)rc;
+}
+
+void wxChoice::DoSetItemClientObject( int n, wxClientData* clientData )
+{
+    DoSetItemClientData(n, clientData);
+}
+
+wxClientData* wxChoice::DoGetItemClientObject( int n ) const
+{
+    return (wxClientData *)DoGetItemClientData(n);
+}
+
+// ----------------------------------------------------------------------------
+// size calculations
+// ----------------------------------------------------------------------------
+
+wxSize wxChoice::DoGetBestSize() const
+{
+    wxSize sizeBtn = GetBestSpinerSize(IsVertical(GetWindowStyle()));
+    sizeBtn.x += DEFAULT_ITEM_WIDTH + MARGIN_BETWEEN;
+
+    int y;
+    wxGetCharSize(GetHWND(), NULL, &y, GetFont());
+    y = EDIT_HEIGHT_FROM_CHAR_HEIGHT(y);
+
+    // JACS: we should always use the height calculated
+    // from above, because otherwise we'll get a spin control
+    // that's too big. So never use the height calculated
+    // from wxSpinButton::DoGetBestSize().
+
+    // if ( sizeBtn.y < y )
+    {
+        // make the text tall enough
+        sizeBtn.y = y;
+    }
+
+    return sizeBtn;
+}
+
+void wxChoice::DoMoveWindow(int x, int y, int width, int height)
+{
+    int widthBtn = GetBestSpinerSize(IsVertical(GetWindowStyle())).x;
+    int widthText = width - widthBtn - MARGIN_BETWEEN;
+    if ( widthText <= 0 )
+    {
+        wxLogDebug(_T("not enough space for wxSpinCtrl!"));
+    }
+
+    if ( !::MoveWindow(GetBuddyHwnd(), x, y, widthText, height, TRUE) )
+    {
+        wxLogLastError(wxT("MoveWindow(buddy)"));
+    }
+
+    x += widthText + MARGIN_BETWEEN;
+    if ( !::MoveWindow(GetHwnd(), x, y, widthBtn, height, TRUE) )
+    {
+        wxLogLastError(wxT("MoveWindow"));
+    }
+}
+
+// get total size of the control
+void wxChoice::DoGetSize(int *x, int *y) const
+{
+    RECT spinrect, textrect, ctrlrect;
+    GetWindowRect(GetHwnd(), &spinrect);
+    GetWindowRect(GetBuddyHwnd(), &textrect);
+    UnionRect(&ctrlrect, &textrect, &spinrect);
+
+    if ( x )
+        *x = ctrlrect.right - ctrlrect.left;
+    if ( y )
+        *y = ctrlrect.bottom - ctrlrect.top;
+}
+
+void wxChoice::DoGetPosition(int *x, int *y) const
+{
+    // hack: pretend that our HWND is the text control just for a moment
+    WXHWND hWnd = GetHWND();
+    wxConstCast(this, wxChoice)->m_hWnd = m_hwndBuddy;
+
+    wxChoiceBase::DoGetPosition(x, y);
+
+    wxConstCast(this, wxChoice)->m_hWnd = hWnd;
+}
+
+#endif // wxUSE_CHOICE && __SMARTPHONE__