Fix several rounding problems with float values in wxPropertyGrid.

Loss of precision when converting floating point numbers to text and back
could result in several problems, notably comparing a valid value with the
minimum could fail after a round trip through wxSpinCtrl.

Fix this by using a specialization of NumericValidation() handling floating
point values specially and correctly.

Closes #15625.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@75980 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin
2014-02-22 14:54:54 +00:00
parent 4ec4dcd3f5
commit c59b815f2c
3 changed files with 125 additions and 2 deletions

View File

@@ -362,7 +362,14 @@ bool wxPGSpinCtrlEditor::OnEvent( wxPropertyGrid* propgrid, wxPGProperty* proper
// Min/Max check
wxFloatProperty::DoValidation(property, v_d, NULL, mode);
s = wxNumberFormatter::ToString(v_d, -1, wxNumberFormatter::Style_None);
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
{

View File

@@ -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);
}

View File

@@ -54,6 +54,8 @@
#include "wx/propgrid/propgrid.h"
#include "wx/numformatter.h"
#include <float.h>
#define wxPG_CUSTOM_IMAGE_WIDTH 20 // for wxColourProperty etc.
@@ -342,7 +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<typename T>
bool NumericValidation( const wxPGProperty* property,
@@ -427,6 +430,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,