+wxArraySpins wxSpinCtrl::ms_allSpins;
+
+// ----------------------------------------------------------------------------
+// wnd proc for the buddy text ctrl
+// ----------------------------------------------------------------------------
+
+LRESULT APIENTRY _EXPORT wxBuddyTextWndProc(HWND hwnd,
+ UINT message,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ wxSpinCtrl *spin = (wxSpinCtrl *)::GetWindowLong(hwnd, GWL_USERDATA);
+
+ // 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 ( 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) && ((wxSpinCtrl *)::GetWindowLong(hwnd, GWL_USERDATA)) == 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);
+}
+
+/* static */
+wxSpinCtrl *wxSpinCtrl::GetSpinForTextCtrl(WXHWND hwndBuddy)
+{
+ wxSpinCtrl *spin = (wxSpinCtrl *)::GetWindowLong((HWND)hwndBuddy,
+ GWL_USERDATA);
+
+ int i = ms_allSpins.Index(spin);
+
+ if ( i == wxNOT_FOUND )
+ return NULL;
+
+ // sanity check
+ wxASSERT_MSG( spin->m_hwndBuddy == hwndBuddy,
+ _T("wxSpinCtrl has incorrect buddy HWND!") );
+
+ return spin;
+}
+
+// process a WM_COMMAND generated by the buddy text control
+bool wxSpinCtrl::ProcessTextCommand(WXWORD cmd, WXWORD WXUNUSED(id))
+{
+ switch (cmd)
+ {
+ case EN_CHANGE:
+ {
+ wxCommandEvent event(wxEVT_COMMAND_TEXT_UPDATED, GetId());
+ event.SetEventObject(this);
+ wxString val = wxGetWindowText(m_hwndBuddy);
+ event.SetString(val);
+ event.SetInt(GetValue());
+ return GetEventHandler()->ProcessEvent(event);
+ }
+ case EN_SETFOCUS:
+ case EN_KILLFOCUS:
+ {
+ wxFocusEvent event(cmd == EN_KILLFOCUS ? wxEVT_KILL_FOCUS
+ : wxEVT_SET_FOCUS,
+ m_windowId);
+ event.SetEventObject( this );
+ return GetEventHandler()->ProcessEvent(event);
+ }
+ default:
+ break;
+ }
+
+ // not processed
+ return FALSE;
+}
+
+void wxSpinCtrl::OnChar(wxKeyEvent& event)
+{
+ switch ( event.GetKeyCode() )
+ {
+ case WXK_RETURN:
+ {
+ wxCommandEvent event(wxEVT_COMMAND_TEXT_ENTER, m_windowId);
+ InitCommandEvent(event);
+ wxString val = wxGetWindowText(m_hwndBuddy);
+ event.SetString(val);
+ event.SetInt(GetValue());
+ if ( GetEventHandler()->ProcessEvent(event) )
+ return;
+ break;
+ }
+
+ case WXK_TAB:
+ // always produce navigation event - even if we process TAB
+ // ourselves the fact that we got here means that the user code
+ // decided to skip processing of this TAB - probably to let it
+ // do its default job.
+ {
+ wxNavigationKeyEvent eventNav;
+ eventNav.SetDirection(!event.ShiftDown());
+ eventNav.SetWindowChange(event.ControlDown());
+ eventNav.SetEventObject(this);
+
+ if ( GetParent()->GetEventHandler()->ProcessEvent(eventNav) )
+ return;
+ }
+ break;
+ }
+
+ // no, we didn't process it
+ event.Skip();
+}
+
+void wxSpinCtrl::OnSetFocus(wxFocusEvent& event)
+{
+ // when we get focus, give it to our buddy window as it needs it more than
+ // we do
+ ::SetFocus((HWND)m_hwndBuddy);
+
+ event.Skip();
+}
+