1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/msw/spinctrl.cpp 
   3 // Purpose:     wxSpinCtrl class implementation for Win32 
   4 // Author:      Vadim Zeitlin 
   8 // Copyright:   (c) 1999-2005 Vadim Zeitlin 
   9 // Licence:     wxWindows licence 
  10 ///////////////////////////////////////////////////////////////////////////// 
  12 // ============================================================================ 
  14 // ============================================================================ 
  16 // ---------------------------------------------------------------------------- 
  18 // ---------------------------------------------------------------------------- 
  20 // for compilers that support precompilation, includes "wx.h". 
  21 #include "wx/wxprec.h" 
  29 #include "wx/spinctrl.h" 
  32     #include "wx/hashmap.h" 
  33     #include "wx/msw/wrapcctl.h" // include <commctrl.h> "properly" 
  35     #include "wx/textctrl.h" 
  36     #include "wx/wxcrtvararg.h" 
  39 #include "wx/msw/private.h" 
  42     #include "wx/tooltip.h" 
  43 #endif // wxUSE_TOOLTIPS 
  45 #include <limits.h>         // for INT_MIN 
  47 // ---------------------------------------------------------------------------- 
  49 // ---------------------------------------------------------------------------- 
  51 BEGIN_EVENT_TABLE(wxSpinCtrl
, wxSpinButton
) 
  52     EVT_CHAR(wxSpinCtrl::OnChar
) 
  53     EVT_SET_FOCUS(wxSpinCtrl::OnSetFocus
) 
  54     EVT_KILL_FOCUS(wxSpinCtrl::OnKillFocus
) 
  57 #define GetBuddyHwnd()      (HWND)(m_hwndBuddy) 
  59 // ---------------------------------------------------------------------------- 
  61 // ---------------------------------------------------------------------------- 
  63 // the margin between the up-down control and its buddy (can be arbitrary, 
  64 // choose what you like - or may be decide during run-time depending on the 
  66 static const int MARGIN_BETWEEN 
= 1; 
  69 // --------------------------------------------------------------------------- 
  71 // --------------------------------------------------------------------------- 
  76 // Global hash used to find the spin control corresponding to the given buddy 
  78 WX_DECLARE_HASH_MAP(HWND
, wxSpinCtrl 
*, 
  79                     wxPointerHash
, wxPointerEqual
, 
  82 SpinForTextCtrl gs_spinForTextCtrl
; 
  84 } // anonymous namespace 
  86 // ============================================================================ 
  88 // ============================================================================ 
  90 // ---------------------------------------------------------------------------- 
  91 // wnd proc for the buddy text ctrl 
  92 // ---------------------------------------------------------------------------- 
  94 LRESULT APIENTRY _EXPORT 
wxBuddyTextWndProc(HWND hwnd
, 
  99     wxSpinCtrl 
* const spin 
= wxSpinCtrl::GetSpinForTextCtrl(hwnd
); 
 101     // forward some messages (mostly the key and focus ones) to the spin ctrl 
 105             // if the focus comes from the spin control itself, don't set it 
 106             // back to it -- we don't want to go into an infinite loop 
 107             if ( (WXHWND
)wParam 
== spin
->GetHWND() ) 
 117         // we need to forward WM_HELP too to ensure that the context help 
 118         // associated with wxSpinCtrl is shown when the text control part of it 
 119         // is clicked with the "?" cursor 
 124                 if ( spin
->MSWHandleMessage(&result
, message
, wParam
, lParam
) ) 
 126                     // Do not let the message be processed by the window proc 
 127                     // of the text control if it had been already handled at wx 
 128                     // level, this is consistent with what happens for normal, 
 129                     // non-composite controls. 
 133                 // The control may have been deleted at this point, so check. 
 134                 if ( !::IsWindow(hwnd
) ) 
 140             if ( spin
->HasFlag(wxTE_PROCESS_ENTER
) ) 
 142                 long dlgCode 
= ::CallWindowProc
 
 144                                     CASTWNDPROC spin
->GetBuddyWndProc(), 
 150                 dlgCode 
|= DLGC_WANTMESSAGE
; 
 156     return ::CallWindowProc(CASTWNDPROC spin
->GetBuddyWndProc(), 
 157                             hwnd
, message
, wParam
, lParam
); 
 161 wxSpinCtrl 
*wxSpinCtrl::GetSpinForTextCtrl(WXHWND hwndBuddy
) 
 163     const SpinForTextCtrl::const_iterator
 
 164         it 
= gs_spinForTextCtrl
.find(hwndBuddy
); 
 165     if ( it 
== gs_spinForTextCtrl
.end() ) 
 168     wxSpinCtrl 
* const spin 
= it
->second
; 
 171     wxASSERT_MSG( spin
->m_hwndBuddy 
== hwndBuddy
, 
 172                   wxT("wxSpinCtrl has incorrect buddy HWND!") ); 
 177 // process a WM_COMMAND generated by the buddy text control 
 178 bool wxSpinCtrl::ProcessTextCommand(WXWORD cmd
, WXWORD 
WXUNUSED(id
)) 
 180     if ( (cmd 
== EN_CHANGE
) && (!m_blockEvent 
)) 
 182         wxCommandEvent 
event(wxEVT_COMMAND_TEXT_UPDATED
, GetId()); 
 183         event
.SetEventObject(this); 
 184         wxString val 
= wxGetWindowText(m_hwndBuddy
); 
 185         event
.SetString(val
); 
 186         event
.SetInt(GetValue()); 
 187         return HandleWindowEvent(event
); 
 194 void wxSpinCtrl::OnChar(wxKeyEvent
& event
) 
 196     switch ( event
.GetKeyCode() ) 
 200                 wxCommandEvent 
event(wxEVT_COMMAND_TEXT_ENTER
, m_windowId
); 
 201                 InitCommandEvent(event
); 
 202                 wxString val 
= wxGetWindowText(m_hwndBuddy
); 
 203                 event
.SetString(val
); 
 204                 event
.SetInt(GetValue()); 
 205                 if ( HandleWindowEvent(event
) ) 
 211             // always produce navigation event - even if we process TAB 
 212             // ourselves the fact that we got here means that the user code 
 213             // decided to skip processing of this TAB - probably to let it 
 214             // do its default job. 
 216                 wxNavigationKeyEvent eventNav
; 
 217                 eventNav
.SetDirection(!event
.ShiftDown()); 
 218                 eventNav
.SetWindowChange(event
.ControlDown()); 
 219                 eventNav
.SetEventObject(this); 
 221                 if ( GetParent()->HandleWindowEvent(eventNav
) ) 
 227     // no, we didn't process it 
 231 void wxSpinCtrl::OnKillFocus(wxFocusEvent
& event
) 
 233     // ensure that a correct value is shown by the control 
 238 void wxSpinCtrl::OnSetFocus(wxFocusEvent
& event
) 
 240     // when we get focus, give it to our buddy window as it needs it more than 
 242     ::SetFocus((HWND
)m_hwndBuddy
); 
 247 void wxSpinCtrl::NormalizeValue() 
 249     const int value 
= GetValue(); 
 250     const bool changed 
= value 
!= m_oldValue
; 
 252     // notice that we have to call SetValue() even if the value didn't change 
 253     // because otherwise we could be left with empty buddy control when value 
 254     // is 0, see comment in SetValue() 
 259         SendSpinUpdate(value
); 
 263 // ---------------------------------------------------------------------------- 
 265 // ---------------------------------------------------------------------------- 
 267 void wxSpinCtrl::Init() 
 269     m_blockEvent 
= false; 
 271     m_wndProcBuddy 
= NULL
; 
 272     m_oldValue 
= INT_MIN
; 
 275 bool wxSpinCtrl::Create(wxWindow 
*parent
, 
 277                         const wxString
& value
, 
 281                         int min
, int max
, int initial
, 
 282                         const wxString
& name
) 
 284     // before using DoGetBestSize(), have to set style to let the base class 
 285     // know whether this is a horizontal or vertical control (we're always 
 287     style 
|= wxSP_VERTICAL
; 
 289     if ( (style 
& wxBORDER_MASK
) == wxBORDER_DEFAULT 
) 
 291         style 
|= wxBORDER_SIMPLE
; 
 293         style 
|= wxBORDER_SUNKEN
; 
 296     SetWindowStyle(style
); 
 299     WXDWORD msStyle 
= MSWGetStyle(GetWindowStyle(), & exStyle
) ; 
 301     // Scroll text automatically if there is not enough space to show all of 
 302     // it, this is better than not allowing to enter more digits at all. 
 303     msStyle 
|= ES_AUTOHSCROLL
; 
 305     // propagate text alignment style to text ctrl 
 306     if ( style 
& wxALIGN_RIGHT 
) 
 308     else if ( style 
& wxALIGN_CENTER 
) 
 309         msStyle 
|= ES_CENTER
; 
 311     // calculate the sizes: the size given is the total size for both controls 
 312     // and we need to fit them both in the given width (height is the same) 
 313     wxSize 
sizeText(size
), sizeBtn(size
); 
 314     sizeBtn
.x 
= wxSpinButton::DoGetBestSize().x
; 
 315     if ( sizeText
.x 
<= 0 ) 
 317         // DEFAULT_ITEM_WIDTH is the default width for the text control 
 318         sizeText
.x 
= DEFAULT_ITEM_WIDTH 
+ MARGIN_BETWEEN 
+ sizeBtn
.x
; 
 321     sizeText
.x 
-= sizeBtn
.x 
+ MARGIN_BETWEEN
; 
 322     if ( sizeText
.x 
<= 0 ) 
 324         wxLogDebug(wxT("not enough space for wxSpinCtrl!")); 
 328     posBtn
.x 
+= sizeText
.x 
+ MARGIN_BETWEEN
; 
 330     // we must create the text control before the spin button for the purpose 
 331     // of the dialog navigation: if there is a static text just before the spin 
 332     // control, activating it by Alt-letter should give focus to the text 
 333     // control, not the spin and the dialog navigation code will give focus to 
 334     // the next control (at Windows level), not the one after it 
 336     // create the text window 
 338     m_hwndBuddy 
= (WXHWND
)::CreateWindowEx
 
 340                      exStyle
,                // sunken border 
 341                      wxT("EDIT"),             // window class 
 342                      NULL
,                   // no window title 
 343                      msStyle
,                // style (will be shown later) 
 344                      pos
.x
, pos
.y
,           // position 
 345                      0, 0,                   // size (will be set later) 
 346                      GetHwndOf(parent
),      // parent 
 347                      (HMENU
)-1,              // control id 
 348                      wxGetInstance(),        // app instance 
 349                      NULL                    
// unused client data 
 354         wxLogLastError(wxT("CreateWindow(buddy text window)")); 
 360     // create the spin button 
 361     if ( !wxSpinButton::Create(parent
, id
, posBtn
, sizeBtn
, style
, name
) ) 
 366     wxSpinButtonBase::SetRange(min
, max
); 
 368     // subclass the text ctrl to be able to intercept some events 
 369     gs_spinForTextCtrl
[GetBuddyHwnd()] = this; 
 371     m_wndProcBuddy 
= (WXFARPROC
)wxSetWindowProc(GetBuddyHwnd(), 
 374     // set up fonts and colours  (This is nomally done in MSWCreateControl) 
 377         SetFont(GetDefaultAttributes().font
); 
 379     // set the size of the text window - can do it only now, because we 
 380     // couldn't call DoGetBestSize() before as font wasn't set 
 381     if ( sizeText
.y 
<= 0 ) 
 384         wxGetCharSize(GetHWND(), &cx
, &cy
, GetFont()); 
 386         sizeText
.y 
= EDIT_HEIGHT_FROM_CHAR_HEIGHT(cy
); 
 389     SetInitialSize(size
); 
 391     (void)::ShowWindow(GetBuddyHwnd(), SW_SHOW
); 
 393     // associate the text window with the spin button 
 394     (void)::SendMessage(GetHwnd(), UDM_SETBUDDY
, (WPARAM
)m_hwndBuddy
, 0); 
 396     // If the initial text value is actually a number, it overrides the 
 397     // "initial" argument specified later. 
 398     long initialFromText
; 
 399     if ( value
.ToLong(&initialFromText
) ) 
 400         initial 
= initialFromText
; 
 404     m_oldValue 
= initial
; 
 406     // Set the range in the native control 
 409     // Also set the text part of the control if it was specified independently 
 410     // but don't generate an event for this, it would be unexpected. 
 412     if ( !value
.empty() ) 
 414     m_blockEvent 
= false; 
 419 wxSpinCtrl::~wxSpinCtrl() 
 421     // destroy the buddy window because this pointer which wxBuddyTextWndProc 
 422     // uses will not soon be valid any more 
 423     ::DestroyWindow( GetBuddyHwnd() ); 
 425     gs_spinForTextCtrl
.erase(GetBuddyHwnd()); 
 428 // ---------------------------------------------------------------------------- 
 429 // wxSpinCtrl-specific methods 
 430 // ---------------------------------------------------------------------------- 
 432 int wxSpinCtrl::GetBase() const 
 434     return ::SendMessage(GetHwnd(), UDM_GETBASE
, 0, 0); 
 437 bool wxSpinCtrl::SetBase(int base
) 
 439     if ( !::SendMessage(GetHwnd(), UDM_SETBASE
, base
, 0) ) 
 442     // Whether we need to be able enter "x" or not influences whether we should 
 443     // use ES_NUMBER for the buddy control. 
 449 // ---------------------------------------------------------------------------- 
 450 // wxTextCtrl-like methods 
 451 // ---------------------------------------------------------------------------- 
 453 void wxSpinCtrl::SetValue(const wxString
& text
) 
 455     if ( !::SetWindowText(GetBuddyHwnd(), text
.c_str()) ) 
 457         wxLogLastError(wxT("SetWindowText(buddy)")); 
 461 void  wxSpinCtrl::SetValue(int val
) 
 465     wxSpinButton::SetValue(val
); 
 467     // Normally setting the value of the spin button is enough as it updates 
 468     // its buddy control automatically but in a couple of situations it doesn't 
 469     // do it, for whatever reason, do it explicitly then: 
 470     const wxString text 
= wxGetWindowText(m_hwndBuddy
); 
 472     // First case is when the text control is empty and the value is 0: the 
 473     // spin button just leaves it empty in this case, while we want to show 0 
 475     if ( text
.empty() && !val 
) 
 477         ::SetWindowText(GetBuddyHwnd(), wxT("0")); 
 480     // Another one is when we're using hexadecimal base but the user input 
 481     // doesn't start with "0x" -- we prefer to show it to avoid ambiguity 
 482     // between decimal and hexadecimal. 
 483     if ( GetBase() == 16 && 
 484             (text
.length() < 3 || text
[0] != '0' || 
 485                 (text
[1] != 'x' && text
[1] != 'X')) ) 
 487         ::SetWindowText(GetBuddyHwnd(), 
 488                         wxPrivate::wxSpinCtrlFormatAsHex(val
, m_max
).t_str()); 
 491     m_oldValue 
= GetValue(); 
 493     m_blockEvent 
= false; 
 496 int wxSpinCtrl::GetValue() const 
 498     const wxString val 
= wxGetWindowText(m_hwndBuddy
); 
 501     if ( !val
.ToLong(&n
, GetBase()) ) 
 512 void wxSpinCtrl::SetSelection(long from
, long to
) 
 514     // if from and to are both -1, it means (in wxWidgets) that all text should 
 515     // be selected - translate into Windows convention 
 516     if ( (from 
== -1) && (to 
== -1) ) 
 521     ::SendMessage(GetBuddyHwnd(), EM_SETSEL
, (WPARAM
)from
, (LPARAM
)to
); 
 524 // ---------------------------------------------------------------------------- 
 525 // wxSpinButton methods 
 526 // ---------------------------------------------------------------------------- 
 528 void wxSpinCtrl::SetRange(int minVal
, int maxVal
) 
 530     // Manually adjust the old value to avoid an event being sent from 
 531     // NormalizeValue() called from inside the base class SetRange() as we're 
 532     // not supposed to generate any events from here. 
 533     if ( m_oldValue 
< minVal 
) 
 535     else if ( m_oldValue 
> maxVal 
) 
 538     wxSpinButton::SetRange(minVal
, maxVal
); 
 543 void wxSpinCtrl::UpdateBuddyStyle() 
 545     // this control is used for numeric entry so restrict the input to numeric 
 546     // keys only -- but only if we don't need to be able to enter "-" in it as 
 547     // otherwise this would become impossible and also if we don't use 
 548     // hexadecimal as entering "x" of the "0x" prefix wouldn't be allowed 
 550     const DWORD styleOld 
= ::GetWindowLong(GetBuddyHwnd(), GWL_STYLE
); 
 552     if ( m_min 
< 0 || GetBase() != 10 ) 
 553         styleNew 
= styleOld 
& ~ES_NUMBER
; 
 555         styleNew 
= styleOld 
| ES_NUMBER
; 
 557     if ( styleNew 
!= styleOld 
) 
 558         ::SetWindowLong(GetBuddyHwnd(), GWL_STYLE
, styleNew
); 
 561 // ---------------------------------------------------------------------------- 
 562 // forward some methods to subcontrols 
 563 // ---------------------------------------------------------------------------- 
 565 bool wxSpinCtrl::SetFont(const wxFont
& font
) 
 567     if ( !wxWindowBase::SetFont(font
) ) 
 573     WXHANDLE hFont 
= GetFont().GetResourceHandle(); 
 574     (void)::SendMessage(GetBuddyHwnd(), WM_SETFONT
, (WPARAM
)hFont
, TRUE
); 
 579 bool wxSpinCtrl::Show(bool show
) 
 581     if ( !wxControl::Show(show
) ) 
 586     ::ShowWindow(GetBuddyHwnd(), show 
? SW_SHOW 
: SW_HIDE
); 
 591 bool wxSpinCtrl::Reparent(wxWindowBase 
*newParent
) 
 593     // Reparenting both the updown control and its buddy does not seem to work: 
 594     // they continue to be connected somehow, but visually there is no feedback 
 595     // on the buddy edit control. To avoid this problem, we reparent the buddy 
 596     // window normally, but we recreate the updown control and reassign its 
 599     // Get the position before changing the parent as it would be offset after 
 601     const wxRect rect 
= GetRect(); 
 603     if ( !wxWindowBase::Reparent(newParent
) ) 
 606     newParent
->GetChildren().DeleteObject(this); 
 608     // destroy the old spin button after detaching it from this wxWindow object 
 609     // (notice that m_hWnd will be reset by UnsubclassWin() so save it first) 
 610     const HWND hwndOld 
= GetHwnd(); 
 612     if ( !::DestroyWindow(hwndOld
) ) 
 614         wxLogLastError(wxT("DestroyWindow")); 
 617     // create and initialize the new one 
 618     if ( !wxSpinButton::Create(GetParent(), GetId(), 
 619                                rect
.GetPosition(), rect
.GetSize(), 
 620                                GetWindowStyle(), GetName()) ) 
 623     // reapply our values to wxSpinButton 
 624     wxSpinButton::SetValue(GetValue()); 
 625     SetRange(m_min
, m_max
); 
 627     // also set the size again with wxSIZE_ALLOW_MINUS_ONE flag: this is 
 628     // necessary if our original position used -1 for either x or y 
 629     SetSize(rect
, wxSIZE_ALLOW_MINUS_ONE
); 
 631     // associate it with the buddy control again 
 632     ::SetParent(GetBuddyHwnd(), GetHwndOf(GetParent())); 
 633     (void)::SendMessage(GetHwnd(), UDM_SETBUDDY
, (WPARAM
)GetBuddyHwnd(), 0); 
 638 bool wxSpinCtrl::Enable(bool enable
) 
 640     if ( !wxControl::Enable(enable
) ) 
 645     MSWEnableHWND(GetBuddyHwnd(), enable
); 
 650 void wxSpinCtrl::SetFocus() 
 652     ::SetFocus(GetBuddyHwnd()); 
 657 void wxSpinCtrl::DoSetToolTip(wxToolTip 
*tip
) 
 659     wxSpinButton::DoSetToolTip(tip
); 
 662         tip
->AddOtherWindow(m_hwndBuddy
); 
 665 #endif // wxUSE_TOOLTIPS 
 667 // ---------------------------------------------------------------------------- 
 668 // events processing and generation 
 669 // ---------------------------------------------------------------------------- 
 671 void wxSpinCtrl::SendSpinUpdate(int value
) 
 673     wxCommandEvent 
event(wxEVT_COMMAND_SPINCTRL_UPDATED
, GetId()); 
 674     event
.SetEventObject(this); 
 677     (void)HandleWindowEvent(event
); 
 682 bool wxSpinCtrl::MSWOnScroll(int WXUNUSED(orientation
), WXWORD wParam
, 
 683                              WXWORD 
WXUNUSED(pos
), WXHWND control
) 
 685     wxCHECK_MSG( control
, false, wxT("scrolling what?") ); 
 687     if ( wParam 
!= SB_THUMBPOSITION 
) 
 689         // probable SB_ENDSCROLL - we don't react to it 
 693     // Notice that we can't use "pos" from WM_VSCROLL as it is 16 bit and we 
 694     // might be using 32 bit range. 
 695     int new_value 
= GetValue(); 
 696     if (m_oldValue 
!= new_value
) 
 697        SendSpinUpdate( new_value 
); 
 702 bool wxSpinCtrl::MSWOnNotify(int WXUNUSED(idCtrl
), WXLPARAM lParam
, WXLPARAM 
*result
) 
 704     NM_UPDOWN 
*lpnmud 
= (NM_UPDOWN 
*)lParam
; 
 706     if (lpnmud
->hdr
.hwndFrom 
!= GetHwnd()) // make sure it is the right control 
 709     *result 
= 0;  // never reject UP and DOWN events 
 715 // ---------------------------------------------------------------------------- 
 717 // ---------------------------------------------------------------------------- 
 719 wxSize 
wxSpinCtrl::DoGetBestSize() const 
 721     return DoGetSizeFromTextSize(DEFAULT_ITEM_WIDTH
); 
 724 wxSize 
wxSpinCtrl::DoGetSizeFromTextSize(int xlen
, int ylen
) const 
 726     wxSize sizeBtn 
= wxSpinButton::DoGetBestSize(); 
 729     wxGetCharSize(GetHWND(), NULL
, &y
, GetFont()); 
 730     // JACS: we should always use the height calculated 
 731     // from above, because otherwise we'll get a spin control 
 732     // that's too big. So never use the height calculated 
 733     // from wxSpinButton::DoGetBestSize(). 
 735     wxSize 
tsize(xlen 
+ sizeBtn
.x 
+ MARGIN_BETWEEN 
+ 0.3 * y 
+ 10, 
 736                  EDIT_HEIGHT_FROM_CHAR_HEIGHT(y
)); 
 738     // Check if the user requested a non-standard height. 
 740         tsize
.IncBy(0, ylen 
- y
); 
 745 void wxSpinCtrl::DoMoveWindow(int x
, int y
, int width
, int height
) 
 747     int widthBtn 
= wxSpinButton::DoGetBestSize().x
; 
 748     int widthText 
= width 
- widthBtn 
- MARGIN_BETWEEN
; 
 749     if ( widthText 
<= 0 ) 
 751         wxLogDebug(wxT("not enough space for wxSpinCtrl!")); 
 754     // 1) The buddy window 
 755     DoMoveSibling(m_hwndBuddy
, x
, y
, widthText
, height
); 
 757     // 2) The button window 
 758     x 
+= widthText 
+ MARGIN_BETWEEN
; 
 759     wxSpinButton::DoMoveWindow(x
, y
, widthBtn
, height
); 
 762 // get total size of the control 
 763 void wxSpinCtrl::DoGetSize(int *x
, int *y
) const 
 765     RECT spinrect
, textrect
, ctrlrect
; 
 766     GetWindowRect(GetHwnd(), &spinrect
); 
 767     GetWindowRect(GetBuddyHwnd(), &textrect
); 
 768     UnionRect(&ctrlrect
,&textrect
, &spinrect
); 
 771         *x 
= ctrlrect
.right 
- ctrlrect
.left
; 
 773         *y 
= ctrlrect
.bottom 
- ctrlrect
.top
; 
 776 void wxSpinCtrl::DoGetClientSize(int *x
, int *y
) const 
 778     RECT spinrect 
= wxGetClientRect(GetHwnd()); 
 779     RECT textrect 
= wxGetClientRect(GetBuddyHwnd()); 
 781     UnionRect(&ctrlrect
,&textrect
, &spinrect
); 
 784         *x 
= ctrlrect
.right 
- ctrlrect
.left
; 
 786         *y 
= ctrlrect
.bottom 
- ctrlrect
.top
; 
 789 void wxSpinCtrl::DoGetPosition(int *x
, int *y
) const 
 791     // hack: pretend that our HWND is the text control just for a moment 
 792     WXHWND hWnd 
= GetHWND(); 
 793     wxConstCast(this, wxSpinCtrl
)->m_hWnd 
= m_hwndBuddy
; 
 795     wxSpinButton::DoGetPosition(x
, y
); 
 797     wxConstCast(this, wxSpinCtrl
)->m_hWnd 
= hWnd
; 
 800 #endif // wxUSE_SPINCTRL