X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/e816f5c736124307fe587f3fcf3524e699a0f4c1..b4607e897c31cd59c5ede98c3b5b5d3910902930:/src/msw/spinctrl.cpp diff --git a/src/msw/spinctrl.cpp b/src/msw/spinctrl.cpp index d8368999a9..bb867d36a8 100644 --- a/src/msw/spinctrl.cpp +++ b/src/msw/spinctrl.cpp @@ -32,6 +32,7 @@ #include "wx/msw/wrapcctl.h" // include "properly" #include "wx/event.h" #include "wx/textctrl.h" + #include "wx/wxcrtvararg.h" #endif #include "wx/msw/private.h" @@ -87,7 +88,7 @@ wxEND_FLAGS( wxSpinCtrlStyle ) IMPLEMENT_DYNAMIC_CLASS_XTI(wxSpinCtrl, wxControl,"wx/spinbut.h") wxBEGIN_PROPERTIES_TABLE(wxSpinCtrl) - wxEVENT_RANGE_PROPERTY( Spin , wxEVT_SCROLL_TOP , wxEVT_SCROLL_ENDSCROLL , wxSpinEvent ) + wxEVENT_RANGE_PROPERTY( Spin , wxEVT_SCROLL_TOP , wxEVT_SCROLL_CHANGED , wxSpinEvent ) wxEVENT_PROPERTY( Updated , wxEVT_COMMAND_SPINCTRL_UPDATED , wxCommandEvent ) wxEVENT_PROPERTY( TextUpdated , wxEVT_COMMAND_TEXT_UPDATED , wxCommandEvent ) wxEVENT_PROPERTY( TextEnter , wxEVT_COMMAND_TEXT_ENTER , wxCommandEvent ) @@ -113,10 +114,8 @@ IMPLEMENT_DYNAMIC_CLASS(wxSpinCtrl, wxControl) BEGIN_EVENT_TABLE(wxSpinCtrl, wxSpinButton) EVT_CHAR(wxSpinCtrl::OnChar) - EVT_SET_FOCUS(wxSpinCtrl::OnSetFocus) EVT_KILL_FOCUS(wxSpinCtrl::OnKillFocus) - EVT_SPIN(wxID_ANY, wxSpinCtrl::OnSpinChange) END_EVENT_TABLE() #define GetBuddyHwnd() (HWND)(m_hwndBuddy) @@ -147,8 +146,7 @@ LRESULT APIENTRY _EXPORT wxBuddyTextWndProc(HWND hwnd, { wxSpinCtrl *spin = (wxSpinCtrl *)wxGetWindowUserData(hwnd); - // forward some messages (the key and focus ones only so far) to - // the spin ctrl + // forward some messages (mostly the key and focus ones) to the spin ctrl switch ( message ) { case WM_SETFOCUS: @@ -163,6 +161,12 @@ LRESULT APIENTRY _EXPORT wxBuddyTextWndProc(HWND hwnd, case WM_DEADCHAR: case WM_KEYUP: case WM_KEYDOWN: +#ifdef WM_HELP + // we need to forward WM_HELP too to ensure that the context help + // associated with wxSpinCtrl is shown when the text control part of it + // is clicked with the "?" cursor + case WM_HELP: +#endif spin->MSWWindowProc(message, wParam, lParam); // The control may have been deleted at this point, so check. @@ -171,8 +175,20 @@ LRESULT APIENTRY _EXPORT wxBuddyTextWndProc(HWND hwnd, break; case WM_GETDLGCODE: - // we want to get WXK_RETURN in order to generate the event for it - return DLGC_WANTCHARS; + if ( spin->HasFlag(wxTE_PROCESS_ENTER) ) + { + long dlgCode = ::CallWindowProc + ( + CASTWNDPROC spin->GetBuddyWndProc(), + hwnd, + message, + wParam, + lParam + ); + dlgCode |= DLGC_WANTMESSAGE; + return dlgCode; + } + break; } return ::CallWindowProc(CASTWNDPROC spin->GetBuddyWndProc(), @@ -191,7 +207,7 @@ wxSpinCtrl *wxSpinCtrl::GetSpinForTextCtrl(WXHWND hwndBuddy) // sanity check wxASSERT_MSG( spin->m_hwndBuddy == hwndBuddy, - _T("wxSpinCtrl has incorrect buddy HWND!") ); + wxT("wxSpinCtrl has incorrect buddy HWND!") ); return spin; } @@ -199,14 +215,14 @@ wxSpinCtrl *wxSpinCtrl::GetSpinForTextCtrl(WXHWND hwndBuddy) // process a WM_COMMAND generated by the buddy text control bool wxSpinCtrl::ProcessTextCommand(WXWORD cmd, WXWORD WXUNUSED(id)) { - if ( cmd == EN_CHANGE ) + if ( (cmd == EN_CHANGE) && (!m_blockEvent )) { 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); + return HandleWindowEvent(event); } // not processed @@ -224,7 +240,7 @@ void wxSpinCtrl::OnChar(wxKeyEvent& event) wxString val = wxGetWindowText(m_hwndBuddy); event.SetString(val); event.SetInt(GetValue()); - if ( GetEventHandler()->ProcessEvent(event) ) + if ( HandleWindowEvent(event) ) return; break; } @@ -240,7 +256,7 @@ void wxSpinCtrl::OnChar(wxKeyEvent& event) eventNav.SetWindowChange(event.ControlDown()); eventNav.SetEventObject(this); - if ( GetParent()->GetEventHandler()->ProcessEvent(eventNav) ) + if ( GetParent()->HandleWindowEvent(eventNav) ) return; } break; @@ -269,16 +285,17 @@ void wxSpinCtrl::OnSetFocus(wxFocusEvent& event) void wxSpinCtrl::NormalizeValue() { const int value = GetValue(); - if ( value == m_oldValue ) - return; + const bool changed = value != m_oldValue; + // notice that we have to call SetValue() even if the value didn't change + // because otherwise we could be left with empty buddy control when value + // is 0, see comment in SetValue() SetValue(value); - wxCommandEvent event(wxEVT_COMMAND_SPINCTRL_UPDATED, GetId()); - event.SetEventObject(this); - event.SetInt(value); - GetEventHandler()->ProcessEvent(event); - m_oldValue = value; + if ( changed ) + { + SendSpinUpdate(value); + } } // ---------------------------------------------------------------------------- @@ -294,6 +311,13 @@ bool wxSpinCtrl::Create(wxWindow *parent, int min, int max, int initial, const wxString& name) { + m_blockEvent = false; + + // this should be in ctor/init function but I don't want to add one to 2.8 + // to avoid problems with default ctor which can be inlined in the user + // code and so might not get this fix without recompilation + m_oldValue = INT_MIN; + // before using DoGetBestSize(), have to set style to let the base class // know whether this is a horizontal or vertical control (we're always // vertical) @@ -311,7 +335,13 @@ bool wxSpinCtrl::Create(wxWindow *parent, WXDWORD exStyle = 0; WXDWORD msStyle = MSWGetStyle(GetWindowStyle(), & exStyle) ; - // calculate the sizes: the size given is the toal size for both controls + // propagate text alignment style to text ctrl + if ( style & wxALIGN_RIGHT ) + msStyle |= ES_RIGHT; + else if ( style & wxALIGN_CENTER ) + msStyle |= ES_CENTER; + + // calculate the sizes: the size given is the total size for both controls // and we need to fit them both in the given width (height is the same) wxSize sizeText(size), sizeBtn(size); sizeBtn.x = wxSpinButton::DoGetBestSize().x; @@ -324,7 +354,7 @@ bool wxSpinCtrl::Create(wxWindow *parent, sizeText.x -= sizeBtn.x + MARGIN_BETWEEN; if ( sizeText.x <= 0 ) { - wxLogDebug(_T("not enough space for wxSpinCtrl!")); + wxLogDebug(wxT("not enough space for wxSpinCtrl!")); } wxPoint posBtn(pos); @@ -341,7 +371,7 @@ bool wxSpinCtrl::Create(wxWindow *parent, m_hwndBuddy = (WXHWND)::CreateWindowEx ( exStyle, // sunken border - _T("EDIT"), // window class + wxT("EDIT"), // window class NULL, // no window title msStyle, // style (will be shown later) pos.x, pos.y, // position @@ -366,10 +396,7 @@ bool wxSpinCtrl::Create(wxWindow *parent, return false; } - SetRange(min, max); - SetValue(initial); - - m_oldValue = initial; + wxSpinButtonBase::SetRange(min, max); // subclass the text ctrl to be able to intercept some events wxSetWindowUserData(GetBuddyHwnd(), this); @@ -398,9 +425,20 @@ bool wxSpinCtrl::Create(wxWindow *parent, // associate the text window with the spin button (void)::SendMessage(GetHwnd(), UDM_SETBUDDY, (WPARAM)m_hwndBuddy, 0); + SetValue(initial); + + // Set the range in the native control + SetRange(min, max); + if ( !value.empty() ) { SetValue(value); + m_oldValue = (int) wxAtol(value); + } + else + { + SetValue(wxString::Format(wxT("%d"), initial)); + m_oldValue = initial; } // do it after finishing with m_hwndBuddy creation to avoid generating @@ -437,6 +475,8 @@ void wxSpinCtrl::SetValue(const wxString& text) void wxSpinCtrl::SetValue(int val) { + m_blockEvent = true; + wxSpinButton::SetValue(val); // normally setting the value of the spin button is enough as it updates @@ -447,10 +487,13 @@ void wxSpinCtrl::SetValue(int val) // text control is currently empty, the spin button seems to be happy // to leave it like this, while we really want to always show the // current value in the control, so do it manually - ::SetWindowText(GetBuddyHwnd(), wxString::Format(_T("%d"), val)); + ::SetWindowText(GetBuddyHwnd(), + wxString::Format(wxT("%d"), val).wx_str()); } m_oldValue = GetValue(); + + m_blockEvent = false; } int wxSpinCtrl::GetValue() const @@ -481,6 +524,28 @@ void wxSpinCtrl::SetSelection(long from, long to) ::SendMessage(GetBuddyHwnd(), EM_SETSEL, (WPARAM)from, (LPARAM)to); } +// ---------------------------------------------------------------------------- +// wxSpinButton methods +// ---------------------------------------------------------------------------- + +void wxSpinCtrl::SetRange(int minVal, int maxVal) +{ + wxSpinButton::SetRange(minVal, maxVal); + + // this control is used for numeric entry so restrict the input to numeric + // keys only -- but only if we don't need to be able to enter "-" in it as + // otherwise this would become impossible + const DWORD styleOld = ::GetWindowLong(GetBuddyHwnd(), GWL_STYLE); + DWORD styleNew; + if ( minVal < 0 ) + styleNew = styleOld & ~ES_NUMBER; + else + styleNew = styleOld | ES_NUMBER; + + if ( styleNew != styleOld ) + ::SetWindowLong(GetBuddyHwnd(), GWL_STYLE, styleNew); +} + // ---------------------------------------------------------------------------- // forward some methods to subcontrols // ---------------------------------------------------------------------------- @@ -511,6 +576,48 @@ bool wxSpinCtrl::Show(bool show) return true; } +bool wxSpinCtrl::Reparent(wxWindowBase *newParent) +{ + // Reparenting both the updown control and its buddy does not seem to work: + // they continue to be connected somehow, but visually there is no feedback + // on the buddy edit control. To avoid this problem, we reparent the buddy + // window normally, but we recreate the updown control and reassign its + // buddy. + + if ( !wxWindowBase::Reparent(newParent) ) + return false; + + newParent->GetChildren().DeleteObject(this); + + // preserve the old values + const wxSize size = GetSize(); + int value = GetValue(); + const wxRect btnRect = wxRectFromRECT(wxGetWindowRect(GetHwnd())); + + // destroy the old spin button + UnsubclassWin(); + if ( !::DestroyWindow(GetHwnd()) ) + { + wxLogLastError(wxT("DestroyWindow")); + } + + // create and initialize the new one + if ( !wxSpinButton::Create(GetParent(), GetId(), + btnRect.GetPosition(), btnRect.GetSize(), + GetWindowStyle(), GetName()) ) + return false; + + SetValue(value); + SetRange(m_min, m_max); + SetInitialSize(size); + + // associate it with the buddy control again + ::SetParent(GetBuddyHwnd(), GetHwndOf(GetParent())); + (void)::SendMessage(GetHwnd(), UDM_SETBUDDY, (WPARAM)GetBuddyHwnd(), 0); + + return true; +} + bool wxSpinCtrl::Enable(bool enable) { if ( !wxControl::Enable(enable) ) @@ -518,7 +625,7 @@ bool wxSpinCtrl::Enable(bool enable) return false; } - ::EnableWindow(GetBuddyHwnd(), enable); + MSWEnableHWND(GetBuddyHwnd(), enable); return true; } @@ -541,27 +648,51 @@ void wxSpinCtrl::DoSetToolTip(wxToolTip *tip) #endif // wxUSE_TOOLTIPS // ---------------------------------------------------------------------------- -// event processing +// events processing and generation // ---------------------------------------------------------------------------- -void wxSpinCtrl::OnSpinChange(wxSpinEvent& eventSpin) +void wxSpinCtrl::SendSpinUpdate(int value) { wxCommandEvent event(wxEVT_COMMAND_SPINCTRL_UPDATED, GetId()); event.SetEventObject(this); - int value = eventSpin.GetPosition(); - event.SetInt( value ); + event.SetInt(value); + + (void)HandleWindowEvent(event); - if (value != m_oldValue) - (void)GetEventHandler()->ProcessEvent(event); + m_oldValue = value; +} - if ( eventSpin.GetSkipped() ) +bool wxSpinCtrl::MSWOnScroll(int WXUNUSED(orientation), WXWORD wParam, + WXWORD pos, WXHWND control) +{ + wxCHECK_MSG( control, false, wxT("scrolling what?") ); + + if ( wParam != SB_THUMBPOSITION ) { - event.Skip(); + // probable SB_ENDSCROLL - we don't react to it + return false; } - m_oldValue = value; + int new_value = (short) pos; + if (m_oldValue != new_value) + SendSpinUpdate( new_value ); + + return TRUE; } +bool wxSpinCtrl::MSWOnNotify(int WXUNUSED(idCtrl), WXLPARAM lParam, WXLPARAM *result) +{ + NM_UPDOWN *lpnmud = (NM_UPDOWN *)lParam; + + if (lpnmud->hdr.hwndFrom != GetHwnd()) // make sure it is the right control + return false; + + *result = 0; // never reject UP and DOWN events + + return TRUE; +} + + // ---------------------------------------------------------------------------- // size calculations // ---------------------------------------------------------------------------- @@ -595,7 +726,7 @@ void wxSpinCtrl::DoMoveWindow(int x, int y, int width, int height) int widthText = width - widthBtn - MARGIN_BETWEEN; if ( widthText <= 0 ) { - wxLogDebug(_T("not enough space for wxSpinCtrl!")); + wxLogDebug(wxT("not enough space for wxSpinCtrl!")); } // 1) The buddy window @@ -620,6 +751,19 @@ void wxSpinCtrl::DoGetSize(int *x, int *y) const *y = ctrlrect.bottom - ctrlrect.top; } +void wxSpinCtrl::DoGetClientSize(int *x, int *y) const +{ + RECT spinrect = wxGetClientRect(GetHwnd()); + RECT textrect = wxGetClientRect(GetBuddyHwnd()); + RECT ctrlrect; + UnionRect(&ctrlrect,&textrect, &spinrect); + + if ( x ) + *x = ctrlrect.right - ctrlrect.left; + if ( y ) + *y = ctrlrect.bottom - ctrlrect.top; +} + void wxSpinCtrl::DoGetPosition(int *x, int *y) const { // hack: pretend that our HWND is the text control just for a moment