diff --git a/include/wx/generic/spinctlg.h b/include/wx/generic/spinctlg.h index c37fe357eb..a35cc8a4a4 100644 --- a/include/wx/generic/spinctlg.h +++ b/include/wx/generic/spinctlg.h @@ -117,9 +117,15 @@ protected: virtual void DoEnable(bool enable); #endif // __WXMSW__ + enum SendEvent + { + SendEvent_None, + SendEvent_Text + }; + // generic double valued functions double DoGetValue() const { return m_value; } - bool DoSetValue(double val); + bool DoSetValue(double val, SendEvent sendEvent); void DoSetRange(double min_val, double max_val); void DoSetIncrement(double inc); @@ -129,7 +135,7 @@ protected: // can also change the text control if its value is invalid // // return true if our value has changed - bool SyncSpinToText(); + bool SyncSpinToText(SendEvent sendEvent); // Send the correct event type virtual void DoSendEvent() = 0; @@ -201,7 +207,7 @@ public: bool ok = wxTextCtrl::Create(parent, id, value, pos, size, style, wxDefaultValidator, name); - DoSetValue(initial); + DoSetValue(initial, SendEvent_None); return ok; } @@ -237,9 +243,20 @@ protected: return n; } - bool DoSetValue(double val) + bool DoSetValue(double val, SendEvent sendEvent) { - wxTextCtrl::SetValue(wxString::Format(m_format.c_str(), val)); + wxString str(wxString::Format(m_format, val)); + switch ( sendEvent ) + { + case SendEvent_None: + wxTextCtrl::ChangeValue(str); + break; + + case SendEvent_Text: + wxTextCtrl::SetValue(str); + break; + } + return true; } void DoSetRange(double min_val, double max_val) @@ -305,7 +322,7 @@ public: // operations void SetValue(const wxString& value) { wxSpinCtrlGenericBase::SetValue(value); } - void SetValue( int value ) { DoSetValue(value); } + void SetValue( int value ) { DoSetValue(value, SendEvent_None); } void SetRange( int minVal, int maxVal ) { DoSetRange(minVal, maxVal); } void SetIncrement(int inc) { DoSetIncrement(inc); } @@ -381,7 +398,7 @@ public: // operations void SetValue(const wxString& value) { wxSpinCtrlGenericBase::SetValue(value); } - void SetValue(double value) { DoSetValue(value); } + void SetValue(double value) { DoSetValue(value, SendEvent_None); } void SetRange(double minVal, double maxVal) { DoSetRange(minVal, maxVal); } void SetIncrement(double inc) { DoSetIncrement(inc); } void SetDigits(unsigned digits); diff --git a/interface/wx/spinctrl.h b/interface/wx/spinctrl.h index ce8128b11e..404451ab11 100644 --- a/interface/wx/spinctrl.h +++ b/interface/wx/spinctrl.h @@ -179,12 +179,20 @@ public: virtual void SetSelection(long from, long to); /** - Sets the value of the spin control. Use the variant using int instead. + Sets the value of the spin control. + + It is recommended to use the overload taking an integer value instead. + + Notice that, unlike wxTextCtrl::SetValue(), but like most of the other + setter methods in wxWidgets, calling this method does not generate any + events as events are only generated for the user actions. */ virtual void SetValue(const wxString& text); /** Sets the value of the spin control. + + Calling this method doesn't generate any @c wxEVT_SPINCTRL events. */ void SetValue(int value); }; @@ -318,12 +326,20 @@ public: void SetRange(double minVal, double maxVal); /** - Sets the value of the spin control. Use the variant using double instead. + Sets the value of the spin control. + + It is recommended to use the overload taking a double value instead. + + Notice that, unlike wxTextCtrl::SetValue(), but like most of the other + setter methods in wxWidgets, calling this method does not generate any + events as events are only generated for the user actions. */ virtual void SetValue(const wxString& text); /** Sets the value of the spin control. + + Calling this method doesn't generate any @c wxEVT_SPINCTRLDOUBLE events. */ void SetValue(double value); }; diff --git a/src/generic/spinctlg.cpp b/src/generic/spinctlg.cpp index 3b5b06fd14..3da88c9404 100644 --- a/src/generic/spinctlg.cpp +++ b/src/generic/spinctlg.cpp @@ -406,7 +406,7 @@ void wxSpinCtrlGenericBase::OnSpinButton(wxSpinEvent& event) // Sync the textctrl since the user expects that the button will modify // what they see in the textctrl. - SyncSpinToText(); + SyncSpinToText(SendEvent_None); int spin_value = event.GetPosition(); double step = (event.GetEventType() == wxEVT_SCROLL_LINEUP) ? 1 : -1; @@ -427,13 +427,14 @@ void wxSpinCtrlGenericBase::OnSpinButton(wxSpinEvent& event) m_spin_value = spin_value; - if ( DoSetValue(value) ) + // Notify about the change in wxTextCtrl too. + if ( DoSetValue(value, SendEvent_Text) ) DoSendEvent(); } void wxSpinCtrlGenericBase::OnTextLostFocus(wxFocusEvent& event) { - SyncSpinToText(); + SyncSpinToText(SendEvent_Text); DoSendEvent(); event.Skip(); @@ -473,9 +474,10 @@ void wxSpinCtrlGenericBase::OnTextChar(wxKeyEvent& event) value = AdjustToFitInRange(value); - SyncSpinToText(); + SyncSpinToText(SendEvent_None); - if ( DoSetValue(value) ) + // No need to send event, it was already generated by wxTextCtrl itself. + if ( DoSetValue(value, SendEvent_None) ) DoSendEvent(); } @@ -483,7 +485,7 @@ void wxSpinCtrlGenericBase::OnTextChar(wxKeyEvent& event) // Textctrl functions // ---------------------------------------------------------------------------- -bool wxSpinCtrlGenericBase::SyncSpinToText() +bool wxSpinCtrlGenericBase::SyncSpinToText(SendEvent sendEvent) { if ( !m_textCtrl || !m_textCtrl->IsModified() ) return false; @@ -505,7 +507,7 @@ bool wxSpinCtrlGenericBase::SyncSpinToText() // we must always set the value here, even if it's equal to m_value, as // otherwise we could be left with an out of range value when leaving the // text control and the current value is already m_max for example - return DoSetValue(textValue); + return DoSetValue(textValue, sendEvent); } // ---------------------------------------------------------------------------- @@ -519,16 +521,16 @@ void wxSpinCtrlGenericBase::SetValue(const wxString& text) double val; if ( DoTextToValue(text, &val) && InRange(val) ) { - DoSetValue(val); + DoSetValue(val, SendEvent_None); } else // not a number at all or out of range { - m_textCtrl->SetValue(text); + m_textCtrl->ChangeValue(text); m_textCtrl->SelectAll(); } } -bool wxSpinCtrlGenericBase::DoSetValue(double val) +bool wxSpinCtrlGenericBase::DoSetValue(double val, SendEvent sendEvent) { wxCHECK_MSG( m_textCtrl, false, wxT("invalid call to wxSpinCtrl::SetValue") ); @@ -556,7 +558,18 @@ bool wxSpinCtrlGenericBase::DoSetValue(double val) { if ( !DoTextToValue(str, &m_value ) ) // wysiwyg for textctrl m_value = val; - m_textCtrl->SetValue( str ); + + switch ( sendEvent ) + { + case SendEvent_None: + m_textCtrl->ChangeValue(str); + break; + + case SendEvent_Text: + m_textCtrl->SetValue(str); + break; + } + m_textCtrl->SelectAll(); m_textCtrl->DiscardEdits(); return true; @@ -579,10 +592,10 @@ void wxSpinCtrlGenericBase::DoSetRange(double min, double max) { m_min = min; if ( m_value < m_min ) - DoSetValue(m_min); + DoSetValue(m_min, SendEvent_None); m_max = max; if ( m_value > m_max ) - DoSetValue(m_max); + DoSetValue(m_max, SendEvent_None); } void wxSpinCtrlGenericBase::DoSetIncrement(double inc) @@ -593,7 +606,7 @@ void wxSpinCtrlGenericBase::DoSetIncrement(double inc) void wxSpinCtrlGenericBase::SetSnapToTicks(bool snap_to_ticks) { m_snap_to_ticks = snap_to_ticks; - DoSetValue(m_value); + DoSetValue(m_value, SendEvent_None); } void wxSpinCtrlGenericBase::SetSelection(long from, long to) @@ -628,7 +641,7 @@ bool wxSpinCtrl::SetBase(int base) // ... but DoValueToText() after doing it. if ( hasValidVal ) - m_textCtrl->SetValue(DoValueToText(val)); + m_textCtrl->ChangeValue(DoValueToText(val)); return true; } @@ -708,7 +721,7 @@ void wxSpinCtrlDouble::SetDigits(unsigned digits) m_format.Printf(wxT("%%0.%ulf"), digits); - DoSetValue(m_value); + DoSetValue(m_value, SendEvent_None); } #endif // wxUSE_SPINBTN diff --git a/src/gtk/spinctrl.cpp b/src/gtk/spinctrl.cpp index e9063e1409..588e5ce4d7 100644 --- a/src/gtk/spinctrl.cpp +++ b/src/gtk/spinctrl.cpp @@ -471,7 +471,9 @@ void wxSpinCtrlDouble::SetDigits(unsigned digits) { wxCHECK_RET( m_widget, "invalid spin button" ); + GtkDisableEvents(); gtk_spin_button_set_digits( GTK_SPIN_BUTTON(m_widget), digits); + GtkEnableEvents(); } #endif // wxUSE_SPINCTRL diff --git a/tests/controls/spinctrldbltest.cpp b/tests/controls/spinctrldbltest.cpp index b320d986e9..4406fa5ae2 100644 --- a/tests/controls/spinctrldbltest.cpp +++ b/tests/controls/spinctrldbltest.cpp @@ -76,13 +76,15 @@ void SpinCtrlDoubleTestCase::NoEventsInCtor() delete m_spin; m_spin = new wxSpinCtrlDouble; - EventCounter updated(m_spin, wxEVT_SPINCTRLDOUBLE); + EventCounter updatedSpin(m_spin, wxEVT_SPINCTRLDOUBLE); + EventCounter updatedText(m_spin, wxEVT_TEXT); m_spin->Create(parent, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, 0, 0., 100., 17.); - CPPUNIT_ASSERT_EQUAL(0, updated.GetCount()); + CPPUNIT_ASSERT_EQUAL(0, updatedSpin.GetCount()); + CPPUNIT_ASSERT_EQUAL(0, updatedText.GetCount()); } void SpinCtrlDoubleTestCase::Arrows() @@ -141,7 +143,21 @@ void SpinCtrlDoubleTestCase::Range() CPPUNIT_ASSERT_EQUAL(0.0, m_spin->GetMin()); CPPUNIT_ASSERT_EQUAL(100.0, m_spin->GetMax()); - //Test neagtive ranges + // Test that the value is adjusted to be inside the new valid range but + // that this doesn't result in any events (as this is not something done by + // the user). + { + EventCounter updatedSpin(m_spin, wxEVT_SPINCTRLDOUBLE); + EventCounter updatedText(m_spin, wxEVT_TEXT); + + m_spin->SetRange(1., 10.); + CPPUNIT_ASSERT_EQUAL(1., m_spin->GetValue()); + + CPPUNIT_ASSERT_EQUAL(0, updatedSpin.GetCount()); + CPPUNIT_ASSERT_EQUAL(0, updatedText.GetCount()); + } + + //Test negative ranges m_spin->SetRange(-10.0, 10.0); CPPUNIT_ASSERT_EQUAL(-10.0, m_spin->GetMin()); @@ -156,18 +172,23 @@ void SpinCtrlDoubleTestCase::Range() void SpinCtrlDoubleTestCase::Value() { + EventCounter updatedSpin(m_spin, wxEVT_SPINCTRLDOUBLE); + EventCounter updatedText(m_spin, wxEVT_TEXT); + m_spin->SetDigits(2); m_spin->SetIncrement(0.1); CPPUNIT_ASSERT_EQUAL(0.0, m_spin->GetValue()); m_spin->SetValue(50.0); - CPPUNIT_ASSERT_EQUAL(50.0, m_spin->GetValue()); m_spin->SetValue(49.1); - CPPUNIT_ASSERT_EQUAL(49.1, m_spin->GetValue()); + + // Calling SetValue() shouldn't have generated any events. + CPPUNIT_ASSERT_EQUAL(0, updatedSpin.GetCount()); + CPPUNIT_ASSERT_EQUAL(0, updatedText.GetCount()); } void SpinCtrlDoubleTestCase::Increment() diff --git a/tests/controls/spinctrltest.cpp b/tests/controls/spinctrltest.cpp index 123031c310..1ef3068a14 100644 --- a/tests/controls/spinctrltest.cpp +++ b/tests/controls/spinctrltest.cpp @@ -106,13 +106,15 @@ void SpinCtrlTestCase::NoEventsInCtor() delete m_spin; m_spin = new wxSpinCtrl; - EventCounter updated(m_spin, wxEVT_SPINCTRL); + EventCounter updatedSpin(m_spin, wxEVT_SPINCTRL); + EventCounter updatedText(m_spin, wxEVT_TEXT); m_spin->Create(parent, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, 0, 0, 100, 17); - CPPUNIT_ASSERT_EQUAL(0, updated.GetCount()); + CPPUNIT_ASSERT_EQUAL(0, updatedSpin.GetCount()); + CPPUNIT_ASSERT_EQUAL(0, updatedText.GetCount()); } void SpinCtrlTestCase::Arrows() @@ -176,11 +178,14 @@ void SpinCtrlTestCase::Range() // that this doesn't result in any events (as this is not something done by // the user). { - EventCounter updated(m_spin, wxEVT_SPINCTRL); + EventCounter updatedSpin(m_spin, wxEVT_SPINCTRL); + EventCounter updatedText(m_spin, wxEVT_TEXT); m_spin->SetRange(1, 10); CPPUNIT_ASSERT_EQUAL(1, m_spin->GetValue()); - CPPUNIT_ASSERT_EQUAL(0, updated.GetCount()); + + CPPUNIT_ASSERT_EQUAL(0, updatedSpin.GetCount()); + CPPUNIT_ASSERT_EQUAL(0, updatedText.GetCount()); } //Test negative ranges @@ -198,19 +203,23 @@ void SpinCtrlTestCase::Range() void SpinCtrlTestCase::Value() { + EventCounter updatedSpin(m_spin, wxEVT_SPINCTRL); + EventCounter updatedText(m_spin, wxEVT_TEXT); + CPPUNIT_ASSERT_EQUAL(0, m_spin->GetValue()); m_spin->SetValue(50); - CPPUNIT_ASSERT_EQUAL(50, m_spin->GetValue()); m_spin->SetValue(-10); - CPPUNIT_ASSERT_EQUAL(0, m_spin->GetValue()); m_spin->SetValue(110); - CPPUNIT_ASSERT_EQUAL(100, m_spin->GetValue()); + + // Calling SetValue() shouldn't have generated any events. + CPPUNIT_ASSERT_EQUAL(0, updatedSpin.GetCount()); + CPPUNIT_ASSERT_EQUAL(0, updatedText.GetCount()); } #endif