diff --git a/docs/changes.txt b/docs/changes.txt index db58e1099f..c73058132a 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -577,6 +577,10 @@ Major new features in this release 3.0.3: (released 2015-??-??) ---------------------------- +All (GUI): + +- Fix several floating point rounding bugs in wxPropertyGrid (Artur Wieczorek). + 3.0.2: (released 2014-10-06) ---------------------------- diff --git a/include/wx/propgrid/props.h b/include/wx/propgrid/props.h index ea902d13e6..168a0f560c 100644 --- a/include/wx/propgrid/props.h +++ b/include/wx/propgrid/props.h @@ -316,6 +316,7 @@ public: const wxString& text, int argFlags = 0 ) const; virtual bool DoSetAttribute( const wxString& name, wxVariant& value ); + virtual wxVariant DoGetAttribute( const wxString& name ) const; virtual bool ValidateValue( wxVariant& value, wxPGValidationInfo& validationInfo ) const; diff --git a/src/propgrid/advprops.cpp b/src/propgrid/advprops.cpp index 784dba57dc..ce31cfe610 100644 --- a/src/propgrid/advprops.cpp +++ b/src/propgrid/advprops.cpp @@ -62,6 +62,7 @@ #endif #include "wx/odcombo.h" +#include "wx/numformatter.h" // ----------------------------------------------------------------------- @@ -361,7 +362,14 @@ bool wxPGSpinCtrlEditor::OnEvent( wxPropertyGrid* propgrid, wxPGProperty* proper // Min/Max check wxFloatProperty::DoValidation(property, v_d, NULL, mode); - wxPropertyGrid::DoubleToString(s, v_d, 6, true, NULL); + int precision = -1; + wxVariant v = property->GetAttribute(wxPG_FLOAT_PRECISION); + if ( !v.IsNull() ) + { + precision = v.GetInteger(); + } + + s = wxNumberFormatter::ToString(v_d, precision, wxNumberFormatter::Style_NoTrailingZeroes); } else { diff --git a/src/propgrid/property.cpp b/src/propgrid/property.cpp index bafa791fc4..5067b8d2e7 100644 --- a/src/propgrid/property.cpp +++ b/src/propgrid/property.cpp @@ -1770,6 +1770,10 @@ wxVariant wxPGProperty::DoGetAttribute( const wxString& WXUNUSED(name) ) const wxVariant wxPGProperty::GetAttribute( const wxString& name ) const { + wxVariant value = DoGetAttribute(name); + if ( !value.IsNull() ) + return value; + return m_attributes.FindValue(name); } diff --git a/src/propgrid/props.cpp b/src/propgrid/props.cpp index 69d136813b..8a0f44e802 100644 --- a/src/propgrid/props.cpp +++ b/src/propgrid/props.cpp @@ -52,6 +52,9 @@ #include "wx/filename.h" #include "wx/propgrid/propgrid.h" +#include "wx/numformatter.h" + +#include #define wxPG_CUSTOM_IMAGE_WIDTH 20 // for wxColourProperty etc. @@ -341,8 +344,8 @@ bool wxIntProperty::IntToValue( wxVariant& variant, int value, int WXUNUSED(argF // implementations. // // Note that 'value' is reference on purpose, so we can write -// back to it when mode is wxPG_PROPERTY_VALIDATION_SATURATE. -// +// back to it when mode is wxPG_PROPERTY_VALIDATION_SATURATE or wxPG_PROPERTY_VALIDATION_WRAP. +// For argument 'value' of type 'double' there is a specialized function (below). template bool NumericValidation( const wxPGProperty* property, T& value, @@ -426,6 +429,115 @@ bool NumericValidation( const wxPGProperty* property, return true; } +// Template specialization for argument 'value' of type 'double'. +// It takes into account required precision of the numbers +// to avoid rounding and conversion errors. +template<> +bool NumericValidation( const wxPGProperty* property, + double& value, + wxPGValidationInfo* pValidationInfo, + int mode, + const wxString& strFmt ) +{ + double min = DBL_MIN; + double max = DBL_MAX; + wxVariant variant; + bool minOk = false; + bool maxOk = false; + + variant = property->GetAttribute(wxPGGlobalVars->m_strMin); + if ( !variant.IsNull() ) + { + variant.Convert(&min); + minOk = true; + } + + variant = property->GetAttribute(wxPGGlobalVars->m_strMax); + if ( !variant.IsNull() ) + { + variant.Convert(&max); + maxOk = true; + } + + if ( minOk || maxOk ) + { + // Get required precision. + int precision = -1; + variant = property->GetAttribute(wxPG_FLOAT_PRECISION); + if ( !variant.IsNull() ) + { + precision = variant.GetInteger(); + } + + // Round current value to the required precision. + wxString strVal = wxNumberFormatter::ToString(value, precision, wxNumberFormatter::Style_None); + strVal.ToDouble(&value); + + // Round minimal value to the required precision. + strVal = wxNumberFormatter::ToString(min, precision, wxNumberFormatter::Style_None); + strVal.ToDouble(&min); + + // Round maximal value to the required precision. + strVal = wxNumberFormatter::ToString(max, precision, wxNumberFormatter::Style_None); + strVal.ToDouble(&max); + } + + if ( minOk ) + { + if ( value < min ) + { + if ( mode == wxPG_PROPERTY_VALIDATION_ERROR_MESSAGE ) + { + wxString msg; + wxString smin = wxString::Format(strFmt, min); + wxString smax = wxString::Format(strFmt, max); + if ( !maxOk ) + msg = wxString::Format( + _("Value must be %s or higher."), + smin.c_str()); + else + msg = wxString::Format( + _("Value must be between %s and %s."), + smin.c_str(), smax.c_str()); + pValidationInfo->SetFailureMessage(msg); + } + else if ( mode == wxPG_PROPERTY_VALIDATION_SATURATE ) + value = min; + else + value = max - (min - value); + return false; + } + } + + if ( maxOk ) + { + if ( value > max ) + { + if ( mode == wxPG_PROPERTY_VALIDATION_ERROR_MESSAGE ) + { + wxString msg; + wxString smin = wxString::Format(strFmt, min); + wxString smax = wxString::Format(strFmt, max); + if ( !minOk ) + msg = wxString::Format( + _("Value must be %s or less."), + smax.c_str()); + else + msg = wxString::Format( + _("Value must be between %s and %s."), + smin.c_str(), smax.c_str()); + pValidationInfo->SetFailureMessage(msg); + } + else if ( mode == wxPG_PROPERTY_VALIDATION_SATURATE ) + value = max; + else + value = min + (value - max); + return false; + } + } + return true; +} + bool wxIntProperty::DoValidation( const wxPGProperty* property, wxLongLong_t& value, wxPGValidationInfo* pValidationInfo, @@ -755,11 +867,9 @@ wxString wxFloatProperty::ValueToString( wxVariant& value, wxString text; if ( !value.IsNull() ) { - wxPropertyGrid::DoubleToString(text, - value, - m_precision, - !(argFlags & wxPG_FULL_VALUE), - NULL); + text = wxNumberFormatter::ToString(value.GetDouble(), m_precision, + argFlags & wxPG_FULL_VALUE ? wxNumberFormatter::Style_None + : wxNumberFormatter::Style_NoTrailingZeroes); } return text; } @@ -821,6 +931,16 @@ bool wxFloatProperty::DoSetAttribute( const wxString& name, wxVariant& value ) return false; } +wxVariant wxFloatProperty::DoGetAttribute( const wxString& name ) const +{ + wxVariant value; + if ( name == wxPG_FLOAT_PRECISION ) + { + value = (long)m_precision; + } + return value; +} + wxValidator* wxFloatProperty::GetClassValidator() {