Backported wxWidgets-3.0rc1 number validators
The are pretty darn slick. There's an integer one and a floating point one. They support automatic range limiting (ex., you can't even type a number outside of the range), proper number formats (ex., you can't enter a decimal point in an integer field), you can't enter bogus numbers like "0.3-.2", thousands separators are supported, decimal precision may be specified and proper number formatting for string values (or automatic conversion to int, double, float, etc.).
This commit is contained in:
parent
dd8e0a5edd
commit
08c94d5372
|
@ -1162,6 +1162,8 @@
|
|||
28F1D81D0A2D0019005506A7 /* AttachableScrollBar.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 28F1D8170A2D0018005506A7 /* AttachableScrollBar.cpp */; };
|
||||
28F1D81E0A2D0019005506A7 /* ExpandingToolBar.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 28F1D8190A2D0018005506A7 /* ExpandingToolBar.cpp */; };
|
||||
28F1D81F0A2D0019005506A7 /* ImageRoll.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 28F1D81B0A2D0019005506A7 /* ImageRoll.cpp */; };
|
||||
28F2CED4181867BB00573D61 /* numformatter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 28F2CED0181867BB00573D61 /* numformatter.cpp */; };
|
||||
28F2CED5181867BB00573D61 /* valnum.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 28F2CED2181867BB00573D61 /* valnum.cpp */; };
|
||||
28F5C1110BE5886A00D17341 /* FileDialogPrivate.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 28F5C10F0BE5886A00D17341 /* FileDialogPrivate.cpp */; };
|
||||
28FC1AFB0A47762C00A188AE /* WrappedType.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 28FC1AF90A47762C00A188AE /* WrappedType.cpp */; };
|
||||
28FE4A080ABF4E960056F5C4 /* mmx_optimized.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 28FE4A060ABF4E960056F5C4 /* mmx_optimized.cpp */; };
|
||||
|
@ -3876,6 +3878,10 @@
|
|||
28F1D81A0A2D0018005506A7 /* ExpandingToolBar.h */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 3; lastKnownFileType = sourcecode.c.h; path = ExpandingToolBar.h; sourceTree = "<group>"; tabWidth = 3; };
|
||||
28F1D81B0A2D0019005506A7 /* ImageRoll.cpp */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 3; lastKnownFileType = sourcecode.cpp.cpp; path = ImageRoll.cpp; sourceTree = "<group>"; tabWidth = 3; };
|
||||
28F1D81C0A2D0019005506A7 /* ImageRoll.h */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 3; lastKnownFileType = sourcecode.c.h; path = ImageRoll.h; sourceTree = "<group>"; tabWidth = 3; };
|
||||
28F2CED0181867BB00573D61 /* numformatter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = numformatter.cpp; sourceTree = "<group>"; };
|
||||
28F2CED1181867BB00573D61 /* numformatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = numformatter.h; sourceTree = "<group>"; };
|
||||
28F2CED2181867BB00573D61 /* valnum.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = valnum.cpp; sourceTree = "<group>"; };
|
||||
28F2CED3181867BB00573D61 /* valnum.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = valnum.h; sourceTree = "<group>"; };
|
||||
28F3A3F60E28289500729866 /* algrd_internal.h */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 3; lastKnownFileType = sourcecode.c.h; name = algrd_internal.h; path = portsmf/algrd_internal.h; sourceTree = "<group>"; tabWidth = 3; };
|
||||
28F3A3F70E28289500729866 /* algsmfrd_internal.h */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 3; lastKnownFileType = sourcecode.c.h; name = algsmfrd_internal.h; path = portsmf/algsmfrd_internal.h; sourceTree = "<group>"; tabWidth = 3; };
|
||||
28F3A3F80E28289500729866 /* allegro.cpp */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 3; lastKnownFileType = sourcecode.cpp.cpp; name = allegro.cpp; path = portsmf/allegro.cpp; sourceTree = "<group>"; tabWidth = 3; };
|
||||
|
@ -5331,12 +5337,16 @@
|
|||
1790B10409883BFD008A330A /* Meter.h */,
|
||||
1790B10509883BFD008A330A /* MultiDialog.cpp */,
|
||||
1790B10609883BFD008A330A /* MultiDialog.h */,
|
||||
28F2CED0181867BB00573D61 /* numformatter.cpp */,
|
||||
28F2CED1181867BB00573D61 /* numformatter.h */,
|
||||
28530C4A0DF2105200555C94 /* ProgressDialog.cpp */,
|
||||
28530C4B0DF2105200555C94 /* ProgressDialog.h */,
|
||||
1790B10709883BFD008A330A /* Ruler.cpp */,
|
||||
1790B10809883BFD008A330A /* Ruler.h */,
|
||||
1790B10909883BFD008A330A /* TimeTextCtrl.cpp */,
|
||||
1790B10A09883BFD008A330A /* TimeTextCtrl.h */,
|
||||
28F2CED2181867BB00573D61 /* valnum.cpp */,
|
||||
28F2CED3181867BB00573D61 /* valnum.h */,
|
||||
1790B10B09883BFD008A330A /* Warning.cpp */,
|
||||
1790B10C09883BFD008A330A /* Warning.h */,
|
||||
);
|
||||
|
@ -8824,6 +8834,8 @@
|
|||
284FD04217FC72A50009A025 /* ScienFilter.cpp in Sources */,
|
||||
284FD04517FC72EE0009A025 /* Biquad.cpp in Sources */,
|
||||
28C3946D1818356800FDDAC9 /* AudacityLogger.cpp in Sources */,
|
||||
28F2CED4181867BB00573D61 /* numformatter.cpp in Sources */,
|
||||
28F2CED5181867BB00573D61 /* valnum.cpp in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
|
@ -257,6 +257,8 @@ OBJS = \
|
|||
widgets/Ruler.o \
|
||||
widgets/TimeTextCtrl.o \
|
||||
widgets/Warning.o \
|
||||
widgets/numformatter.o \
|
||||
widgets/valnum.o \
|
||||
xml/XMLFileReader.o \
|
||||
xml/XMLWriter.o \
|
||||
@OPTOBJS@
|
||||
|
|
|
@ -53,6 +53,7 @@ effects from this one class.
|
|||
#include "../../LabelTrack.h"
|
||||
#include "../../Internat.h"
|
||||
#include "../../ShuttleGui.h"
|
||||
#include "../../widgets/valnum.h"
|
||||
|
||||
#include "Nyquist.h"
|
||||
|
||||
|
@ -1217,8 +1218,16 @@ NyquistDialog::NyquistDialog(wxWindow * parent, wxWindowID id,
|
|||
item = new wxTextCtrl(this, ID_NYQ_TEXT+i, wxT(""),
|
||||
wxDefaultPosition, wxSize(60, -1));
|
||||
item->SetName(ctrl->name);
|
||||
wxTextValidator vld(wxFILTER_NUMERIC);
|
||||
item->SetValidator(vld);
|
||||
if (ctrl->type == NYQ_CTRL_REAL) {
|
||||
wxFloatingPointValidator<double> vld(2, &ctrl->val);
|
||||
vld.SetRange(ctrl->low, ctrl->high);
|
||||
item->SetValidator(vld);
|
||||
}
|
||||
else {
|
||||
wxIntegerValidator<double> vld(&ctrl->val);
|
||||
vld.SetRange(ctrl->low, ctrl->high);
|
||||
item->SetValidator(vld);
|
||||
}
|
||||
|
||||
grid->Add(item, 0, wxALIGN_CENTRE | wxALIGN_CENTER_VERTICAL | wxALL, 5);
|
||||
|
||||
|
@ -1298,33 +1307,8 @@ void NyquistDialog::OnSlider(wxCommandEvent & /* event */)
|
|||
newVal /= pow(10.0, precision);
|
||||
|
||||
ctrl->val = newVal;
|
||||
}
|
||||
|
||||
wxString valStr;
|
||||
if (ctrl->type == NYQ_CTRL_REAL) {
|
||||
// If this is a user-typed value, allow unlimited precision
|
||||
if (ctrl->val != newVal)
|
||||
{
|
||||
valStr = Internat::ToDisplayString(ctrl->val);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (precision == 0)
|
||||
{
|
||||
valStr.Printf(wxT("%d"), (int)floor(ctrl->val + 0.5));
|
||||
}
|
||||
else
|
||||
{
|
||||
valStr = Internat::ToDisplayString(ctrl->val, precision);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (ctrl->type == NYQ_CTRL_INT) {
|
||||
valStr.Printf(wxT("%d"), (int)floor(ctrl->val + 0.5));
|
||||
}
|
||||
|
||||
if (valStr != wxT("")) {
|
||||
text->SetValue(valStr);
|
||||
text->GetValidator()->TransferToWindow();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1365,24 +1349,21 @@ void NyquistDialog::OnText(wxCommandEvent &event)
|
|||
wxTextCtrl *text = (wxTextCtrl *)FindWindow(ID_NYQ_TEXT + ctrlId);
|
||||
wxASSERT(text);
|
||||
|
||||
ctrl->valStr = text->GetValue();
|
||||
|
||||
if (ctrl->type != NYQ_CTRL_STRING) {
|
||||
text->GetValidator()->TransferFromWindow();
|
||||
wxSlider *slider = (wxSlider *)FindWindow(ID_NYQ_SLIDER + ctrlId);
|
||||
wxASSERT(slider);
|
||||
|
||||
if (ctrl->valStr.ToDouble(&ctrl->val)) {
|
||||
int pos = (int)floor((ctrl->val - ctrl->low) /
|
||||
(ctrl->high - ctrl->low) * ctrl->ticks + 0.5);
|
||||
if (pos < 0) {
|
||||
pos = 0;
|
||||
}
|
||||
else if (pos > ctrl->ticks) {
|
||||
pos = ctrl->ticks;
|
||||
}
|
||||
|
||||
slider->SetValue(pos);
|
||||
int pos = (int)floor((ctrl->val - ctrl->low) /
|
||||
(ctrl->high - ctrl->low) * ctrl->ticks + 0.5);
|
||||
if (pos < 0) {
|
||||
pos = 0;
|
||||
}
|
||||
else if (pos > ctrl->ticks) {
|
||||
pos = ctrl->ticks;
|
||||
}
|
||||
|
||||
slider->SetValue(pos);
|
||||
}
|
||||
|
||||
mInHandler = false;
|
||||
|
|
|
@ -0,0 +1,348 @@
|
|||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Backport from wxWidgets-3.0-rc1
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// Name: src/common/numformatter.cpp
|
||||
// Purpose: wxNumberFormatter
|
||||
// Author: Fulvio Senore, Vadim Zeitlin
|
||||
// Created: 2010-11-06
|
||||
// Copyright: (c) 2010 wxWidgets team
|
||||
// Licence: wxWindows licence
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// headers
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// For compilers that support precompilation, includes "wx.h".
|
||||
#include "wx/wxprec.h"
|
||||
|
||||
#ifdef __BORLANDC__
|
||||
#pragma hdrstop
|
||||
#endif
|
||||
|
||||
#ifdef __WIN32__
|
||||
#include "wx/msw/private.h"
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#include "numformatter.h"
|
||||
#include "wx/intl.h"
|
||||
|
||||
#include <locale.h> // for setlocale and LC_ALL
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// local helpers
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
// Contains information about the locale which was used to initialize our
|
||||
// cached values of the decimal and thousands separators. Notice that it isn't
|
||||
// enough to store just wxLocale because the user code may call setlocale()
|
||||
// directly and storing just C locale string is not enough because we can use
|
||||
// the OS API directly instead of the CRT ones on some platforms. So just store
|
||||
// both.
|
||||
class LocaleId
|
||||
{
|
||||
public:
|
||||
LocaleId()
|
||||
{
|
||||
#if wxUSE_INTL
|
||||
m_wxloc = NULL;
|
||||
#endif // wxUSE_INTL
|
||||
m_cloc = NULL;
|
||||
}
|
||||
|
||||
~LocaleId()
|
||||
{
|
||||
Free();
|
||||
}
|
||||
|
||||
#if wxUSE_INTL
|
||||
// Return true if this is the first time this function is called for this
|
||||
// object or if the program locale has changed since the last time it was
|
||||
// called. Otherwise just return false indicating that updating locale-
|
||||
// dependent information is not necessary.
|
||||
bool NotInitializedOrHasChanged()
|
||||
{
|
||||
wxLocale * const wxloc = wxGetLocale();
|
||||
const char * const cloc = setlocale(LC_ALL, NULL);
|
||||
if ( m_wxloc || m_cloc )
|
||||
{
|
||||
if ( m_wxloc == wxloc && strcmp(m_cloc, cloc) == 0 )
|
||||
return false;
|
||||
|
||||
Free();
|
||||
}
|
||||
//else: Not initialized yet.
|
||||
|
||||
m_wxloc = wxloc;
|
||||
m_cloc = strdup(cloc);
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif // wxUSE_INTL
|
||||
|
||||
private:
|
||||
void Free()
|
||||
{
|
||||
#if wxUSE_INTL
|
||||
free(m_cloc);
|
||||
#endif // wxUSE_INTL
|
||||
}
|
||||
|
||||
#if wxUSE_INTL
|
||||
// Non-owned pointer to wxLocale which was used.
|
||||
wxLocale *m_wxloc;
|
||||
#endif // wxUSE_INTL
|
||||
|
||||
// Owned pointer to the C locale string.
|
||||
char *m_cloc;
|
||||
|
||||
// wxDECLARE_NO_COPY_CLASS(LocaleId);
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
// ============================================================================
|
||||
// wxNumberFormatter implementation
|
||||
// ============================================================================
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Locale information accessors
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
wxChar wxNumberFormatter::GetDecimalSeparator()
|
||||
{
|
||||
#if wxUSE_INTL
|
||||
// Notice that while using static variable here is not MT-safe, the worst
|
||||
// that can happen is that we redo the initialization if we're called
|
||||
// concurrently from more than one thread so it's not a real problem.
|
||||
static wxChar s_decimalSeparator = 0;
|
||||
|
||||
// Remember the locale which was current when we initialized, we must redo
|
||||
// the initialization if the locale changed.
|
||||
static LocaleId s_localeUsedForInit;
|
||||
|
||||
if ( s_localeUsedForInit.NotInitializedOrHasChanged() )
|
||||
{
|
||||
const wxString
|
||||
s = wxLocale::GetInfo(wxLOCALE_DECIMAL_POINT, wxLOCALE_CAT_NUMBER);
|
||||
if ( s.empty() )
|
||||
{
|
||||
// We really must have something for decimal separator, so fall
|
||||
// back to the C locale default.
|
||||
s_decimalSeparator = '.';
|
||||
}
|
||||
else
|
||||
{
|
||||
// To the best of my knowledge there are no locales like this.
|
||||
wxASSERT_MSG( s.length() == 1,
|
||||
wxT("Multi-character decimal separator?") );
|
||||
|
||||
s_decimalSeparator = s[0];
|
||||
}
|
||||
}
|
||||
|
||||
return s_decimalSeparator;
|
||||
#else // !wxUSE_INTL
|
||||
return wxT('.');
|
||||
#endif // wxUSE_INTL/!wxUSE_INTL
|
||||
}
|
||||
|
||||
bool wxNumberFormatter::GetThousandsSeparatorIfUsed(wxChar *sep)
|
||||
{
|
||||
#if wxUSE_INTL
|
||||
static wxChar s_thousandsSeparator = 0;
|
||||
static LocaleId s_localeUsedForInit;
|
||||
|
||||
if ( s_localeUsedForInit.NotInitializedOrHasChanged() )
|
||||
{
|
||||
#if defined(__WXMSW__)
|
||||
wxUint32 lcid = LOCALE_USER_DEFAULT;
|
||||
|
||||
if (wxGetLocale())
|
||||
{
|
||||
const wxLanguageInfo *info = wxLocale::GetLanguageInfo(wxGetLocale()->GetLanguage());
|
||||
if (info)
|
||||
{ ;
|
||||
lcid = MAKELCID(MAKELANGID(info->WinLang, info->WinSublang),
|
||||
SORT_DEFAULT);
|
||||
}
|
||||
}
|
||||
|
||||
wxString s;
|
||||
wxChar buffer[256];
|
||||
buffer[0] = wxT('\0');
|
||||
size_t count = GetLocaleInfo(lcid, LOCALE_STHOUSAND, buffer, 256);
|
||||
if (!count)
|
||||
s << wxT(",");
|
||||
else
|
||||
s << buffer;
|
||||
#else
|
||||
wxString
|
||||
s = wxLocale::GetInfo(wxLOCALE_THOUSANDS_SEP, wxLOCALE_CAT_NUMBER);
|
||||
#endif
|
||||
if ( !s.empty() )
|
||||
{
|
||||
wxASSERT_MSG( s.length() == 1,
|
||||
wxT("Multi-character thousands separator?") );
|
||||
|
||||
s_thousandsSeparator = s[0];
|
||||
}
|
||||
//else: Unlike above it's perfectly fine for the thousands separator to
|
||||
// be empty if grouping is not used, so just leave it as 0.
|
||||
}
|
||||
|
||||
if ( !s_thousandsSeparator )
|
||||
return false;
|
||||
|
||||
if ( sep )
|
||||
*sep = s_thousandsSeparator;
|
||||
|
||||
return true;
|
||||
#else // !wxUSE_INTL
|
||||
wxUnusedVar(sep);
|
||||
return false;
|
||||
#endif // wxUSE_INTL/!wxUSE_INTL
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Conversion to string and helpers
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
wxString wxNumberFormatter::PostProcessIntString(wxString s, int style)
|
||||
{
|
||||
if ( style & Style_WithThousandsSep )
|
||||
AddThousandsSeparators(s);
|
||||
|
||||
wxASSERT_MSG( !(style & Style_NoTrailingZeroes),
|
||||
wxT("Style_NoTrailingZeroes can't be used with integer values") );
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
wxString wxNumberFormatter::ToString(long val, int style)
|
||||
{
|
||||
return PostProcessIntString(wxString::Format(wxT("%ld"), val), style);
|
||||
}
|
||||
|
||||
#ifdef wxHAS_LONG_LONG_T_DIFFERENT_FROM_LONG
|
||||
|
||||
wxString wxNumberFormatter::ToString(wxLongLong_t val, int style)
|
||||
{
|
||||
return PostProcessIntString(wxString::Format(wxT("%") wxLongLongFmtSpec wxT("d"), val),
|
||||
style);
|
||||
}
|
||||
|
||||
#endif // wxHAS_LONG_LONG_T_DIFFERENT_FROM_LONG
|
||||
|
||||
wxString wxNumberFormatter::ToString(double val, int precision, int style)
|
||||
{
|
||||
wxString format;
|
||||
if ( precision == -1 )
|
||||
{
|
||||
format = wxT("%g");
|
||||
}
|
||||
else // Use fixed precision.
|
||||
{
|
||||
format.Printf(wxT("%%.%df"), precision);
|
||||
}
|
||||
|
||||
wxString s = wxString::Format(format, val);
|
||||
|
||||
if ( style & Style_WithThousandsSep )
|
||||
AddThousandsSeparators(s);
|
||||
|
||||
if ( style & Style_NoTrailingZeroes )
|
||||
RemoveTrailingZeroes(s);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void wxNumberFormatter::AddThousandsSeparators(wxString& s)
|
||||
{
|
||||
wxChar thousandsSep;
|
||||
if ( !GetThousandsSeparatorIfUsed(&thousandsSep) )
|
||||
return;
|
||||
|
||||
size_t pos = s.find(GetDecimalSeparator());
|
||||
if ( pos == wxString::npos )
|
||||
{
|
||||
// Start grouping at the end of an integer number.
|
||||
pos = s.length();
|
||||
}
|
||||
|
||||
// End grouping at the beginning of the digits -- there could be at a sign
|
||||
// before their start.
|
||||
const size_t start = s.find_first_of(wxT("0123456789"));
|
||||
|
||||
// We currently group digits by 3 independently of the locale. This is not
|
||||
// the right thing to do and we should use lconv::grouping (under POSIX)
|
||||
// and GetLocaleInfo(LOCALE_SGROUPING) (under MSW) to get information about
|
||||
// the correct grouping to use. This is something that needs to be done at
|
||||
// wxLocale level first and then used here in the future (TODO).
|
||||
const size_t GROUP_LEN = 3;
|
||||
|
||||
while ( pos > start + GROUP_LEN )
|
||||
{
|
||||
pos -= GROUP_LEN;
|
||||
s.insert(pos, thousandsSep);
|
||||
}
|
||||
}
|
||||
|
||||
void wxNumberFormatter::RemoveTrailingZeroes(wxString& s)
|
||||
{
|
||||
const size_t posDecSep = s.find(GetDecimalSeparator());
|
||||
wxCHECK_RET( posDecSep != wxString::npos,
|
||||
wxString::Format(wxT("No decimal separator in \"%s\""), s.c_str()) );
|
||||
wxCHECK_RET( posDecSep, wxT("Can't start with decimal separator" ));
|
||||
|
||||
// Find the last character to keep.
|
||||
size_t posLastNonZero = s.find_last_not_of(wxT("0"));
|
||||
|
||||
// If it's the decimal separator itself, don't keep it neither.
|
||||
if ( posLastNonZero == posDecSep )
|
||||
posLastNonZero--;
|
||||
|
||||
s.erase(posLastNonZero + 1);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Conversion from strings
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
void wxNumberFormatter::RemoveThousandsSeparators(wxString& s)
|
||||
{
|
||||
wxChar thousandsSep;
|
||||
if ( !GetThousandsSeparatorIfUsed(&thousandsSep) )
|
||||
return;
|
||||
|
||||
s.Replace(wxString(thousandsSep), wxString());
|
||||
}
|
||||
|
||||
bool wxNumberFormatter::FromString(wxString s, long *val)
|
||||
{
|
||||
RemoveThousandsSeparators(s);
|
||||
return s.ToLong(val);
|
||||
}
|
||||
|
||||
#ifdef wxHAS_LONG_LONG_T_DIFFERENT_FROM_LONG
|
||||
|
||||
bool wxNumberFormatter::FromString(wxString s, wxLongLong_t *val)
|
||||
{
|
||||
RemoveThousandsSeparators(s);
|
||||
return s.ToLongLong(val);
|
||||
}
|
||||
|
||||
#endif // wxHAS_LONG_LONG_T_DIFFERENT_FROM_LONG
|
||||
|
||||
bool wxNumberFormatter::FromString(wxString s, double *val)
|
||||
{
|
||||
RemoveThousandsSeparators(s);
|
||||
return s.ToDouble(val);
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Backport from wxWidgets-3.0-rc1
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// Name: wx/numformatter.h
|
||||
// Purpose: wxNumberFormatter class
|
||||
// Author: Fulvio Senore, Vadim Zeitlin
|
||||
// Created: 2010-11-06
|
||||
// Copyright: (c) 2010 wxWidgets team
|
||||
// Licence: wxWindows licence
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _WX_NUMFORMATTER_H_
|
||||
#define _WX_NUMFORMATTER_H_
|
||||
|
||||
#include "wx/string.h"
|
||||
|
||||
#define wxHAS_LONG_LONG_T_DIFFERENT_FROM_LONG 1
|
||||
|
||||
// Helper class for formatting numbers with thousands separators which also
|
||||
// supports parsing the numbers formatted by it.
|
||||
class wxNumberFormatter
|
||||
{
|
||||
public:
|
||||
// Bit masks for ToString()
|
||||
enum Style
|
||||
{
|
||||
Style_None = 0x00,
|
||||
Style_WithThousandsSep = 0x01,
|
||||
Style_NoTrailingZeroes = 0x02 // Only for floating point numbers
|
||||
};
|
||||
|
||||
// Format a number as a string. By default, the thousands separator is
|
||||
// used, specify Style_None to prevent this. For floating point numbers,
|
||||
// precision can also be specified.
|
||||
static wxString ToString(long val,
|
||||
int style = Style_WithThousandsSep);
|
||||
#ifdef wxHAS_LONG_LONG_T_DIFFERENT_FROM_LONG
|
||||
static wxString ToString(wxLongLong_t val,
|
||||
int style = Style_WithThousandsSep);
|
||||
#endif // wxHAS_LONG_LONG_T_DIFFERENT_FROM_LONG
|
||||
static wxString ToString(double val,
|
||||
int precision,
|
||||
int style = Style_WithThousandsSep);
|
||||
|
||||
// Parse a string representing a number, possibly with thousands separator.
|
||||
//
|
||||
// Return true on success and stores the result in the provided location
|
||||
// which must be a valid non-NULL pointer.
|
||||
static bool FromString(wxString s, long *val);
|
||||
#ifdef wxHAS_LONG_LONG_T_DIFFERENT_FROM_LONG
|
||||
static bool FromString(wxString s, wxLongLong_t *val);
|
||||
#endif // wxHAS_LONG_LONG_T_DIFFERENT_FROM_LONG
|
||||
static bool FromString(wxString s, double *val);
|
||||
|
||||
|
||||
// Get the decimal separator for the current locale. It is always defined
|
||||
// and we fall back to returning '.' in case of an error.
|
||||
static wxChar GetDecimalSeparator();
|
||||
|
||||
// Get the thousands separator if grouping of the digits is used by the
|
||||
// current locale. The value returned in sep should be only used if the
|
||||
// function returns true.
|
||||
static bool GetThousandsSeparatorIfUsed(wxChar *sep);
|
||||
|
||||
private:
|
||||
// Post-process the string representing an integer.
|
||||
static wxString PostProcessIntString(wxString s, int style);
|
||||
|
||||
// Add the thousands separators to a string representing a number without
|
||||
// the separators. This is used by ToString(Style_WithThousandsSep).
|
||||
static void AddThousandsSeparators(wxString& s);
|
||||
|
||||
// Remove trailing zeroes and, if there is nothing left after it, the
|
||||
// decimal separator itself from a string representing a floating point
|
||||
// number. Also used by ToString().
|
||||
static void RemoveTrailingZeroes(wxString& s);
|
||||
|
||||
// Remove all thousands separators from a string representing a number.
|
||||
static void RemoveThousandsSeparators(wxString& s);
|
||||
};
|
||||
|
||||
#endif // _WX_NUMFORMATTER_H_
|
|
@ -0,0 +1,300 @@
|
|||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Backport from wxWidgets-3.0-rc1
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// Name: src/common/valnum.cpp
|
||||
// Purpose: Numeric validator classes implementation
|
||||
// Author: Vadim Zeitlin based on the submission of Fulvio Senore
|
||||
// Created: 2010-11-06
|
||||
// Copyright: (c) 2010 wxWidgets team
|
||||
// Licence: wxWindows licence
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// ============================================================================
|
||||
// Declarations
|
||||
// ============================================================================
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// headers
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// For compilers that support precompilation, includes "wx.h".
|
||||
#include "wx/wxprec.h"
|
||||
|
||||
#ifdef __BORLANDC__
|
||||
#pragma hdrstop
|
||||
#endif
|
||||
|
||||
#if wxUSE_VALIDATORS && wxUSE_TEXTCTRL
|
||||
|
||||
#ifndef WX_PRECOMP
|
||||
#include "wx/textctrl.h"
|
||||
#include "wx/combobox.h"
|
||||
#endif
|
||||
|
||||
#include "valnum.h"
|
||||
#include "numformatter.h"
|
||||
|
||||
// ============================================================================
|
||||
// wxNumValidatorBase implementation
|
||||
// ============================================================================
|
||||
|
||||
BEGIN_EVENT_TABLE(wxNumValidatorBase, wxValidator)
|
||||
EVT_CHAR(wxNumValidatorBase::OnChar)
|
||||
EVT_KILL_FOCUS(wxNumValidatorBase::OnKillFocus)
|
||||
END_EVENT_TABLE()
|
||||
|
||||
int wxNumValidatorBase::GetFormatFlags() const
|
||||
{
|
||||
int flags = wxNumberFormatter::Style_None;
|
||||
if ( m_style & wxNUM_VAL_THOUSANDS_SEPARATOR )
|
||||
flags |= wxNumberFormatter::Style_WithThousandsSep;
|
||||
if ( m_style & wxNUM_VAL_NO_TRAILING_ZEROES )
|
||||
flags |= wxNumberFormatter::Style_NoTrailingZeroes;
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
wxTextEntry *wxNumValidatorBase::GetTextEntry() const
|
||||
{
|
||||
#if wxUSE_TEXTCTRL
|
||||
if ( wxTextCtrl *text = wxDynamicCast(m_validatorWindow, wxTextCtrl) )
|
||||
return text;
|
||||
#endif // wxUSE_TEXTCTRL
|
||||
|
||||
wxFAIL_MSG(wxT("Can only be used with wxTextCtrl or wxComboBox"));
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
wxNumValidatorBase::GetCurrentValueAndInsertionPoint(wxString& val,
|
||||
int& pos) const
|
||||
{
|
||||
wxTextEntry * const control = GetTextEntry();
|
||||
if ( !control )
|
||||
return;
|
||||
|
||||
val = control->GetValue();
|
||||
pos = control->GetInsertionPoint();
|
||||
|
||||
long selFrom, selTo;
|
||||
control->GetSelection(&selFrom, &selTo);
|
||||
|
||||
const long selLen = selTo - selFrom;
|
||||
if ( selLen )
|
||||
{
|
||||
// Remove selected text because pressing a key would make it disappear.
|
||||
val.erase(selFrom, selLen);
|
||||
|
||||
// And adjust the insertion point to have correct position in the new
|
||||
// string.
|
||||
if ( pos > selFrom )
|
||||
{
|
||||
if ( pos >= selTo )
|
||||
pos -= selLen;
|
||||
else
|
||||
pos = selFrom;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool wxNumValidatorBase::IsMinusOk(const wxString& val, int pos) const
|
||||
{
|
||||
// Minus is only ever accepted in the beginning of the string.
|
||||
if ( pos != 0 )
|
||||
return false;
|
||||
|
||||
// And then only if there is no existing minus sign there.
|
||||
if ( !val.empty() && val[0] == '-' )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void wxNumValidatorBase::OnChar(wxKeyEvent& event)
|
||||
{
|
||||
// By default we just validate this key so don't prevent the normal
|
||||
// handling from taking place.
|
||||
event.Skip();
|
||||
|
||||
if ( !m_validatorWindow )
|
||||
return;
|
||||
|
||||
#if wxUSE_UNICODE
|
||||
wxKeyEvent & e = event;
|
||||
const int ch = event.GetUnicodeKey();
|
||||
const int c = event.GetKeyCode();
|
||||
if ( c > WXK_START )
|
||||
{
|
||||
// It's a character without any Unicode equivalent at all, e.g. cursor
|
||||
// arrow or function key, we never filter those.
|
||||
return;
|
||||
}
|
||||
#else // !wxUSE_UNICODE
|
||||
const int ch = event.GetKeyCode();
|
||||
if ( ch > WXK_DELETE )
|
||||
{
|
||||
// Not a character neither.
|
||||
return;
|
||||
}
|
||||
#endif // wxUSE_UNICODE/!wxUSE_UNICODE
|
||||
|
||||
if ( c < WXK_SPACE || c == WXK_DELETE )
|
||||
{
|
||||
// Allow ASCII control characters and Delete.
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if this character is allowed in the current state.
|
||||
wxString val;
|
||||
int pos;
|
||||
GetCurrentValueAndInsertionPoint(val, pos);
|
||||
|
||||
if ( !IsCharOk(val, pos, ch) )
|
||||
{
|
||||
if ( !wxValidator::IsSilent() )
|
||||
wxBell();
|
||||
|
||||
// Do not skip the event in this case, stop handling it here.
|
||||
event.Skip(false);
|
||||
}
|
||||
}
|
||||
|
||||
void wxNumValidatorBase::OnKillFocus(wxFocusEvent& event)
|
||||
{
|
||||
wxTextEntry * const control = GetTextEntry();
|
||||
if ( !control )
|
||||
return;
|
||||
|
||||
// When we change the control value below, its "modified" status is reset
|
||||
// so we need to explicitly keep it marked as modified if it was so in the
|
||||
// first place.
|
||||
//
|
||||
// Notice that only wxTextCtrl (and not wxTextEntry) has
|
||||
// IsModified()/MarkDirty() methods hence the need for dynamic cast.
|
||||
wxTextCtrl * const text = wxDynamicCast(m_validatorWindow, wxTextCtrl);
|
||||
const bool wasModified = text ? text->IsModified() : false;
|
||||
|
||||
control->ChangeValue(NormalizeString(control->GetValue()));
|
||||
|
||||
if ( wasModified )
|
||||
text->MarkDirty();
|
||||
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// wxIntegerValidatorBase implementation
|
||||
// ============================================================================
|
||||
|
||||
wxString wxIntegerValidatorBase::ToString(LongestValueType value) const
|
||||
{
|
||||
return wxNumberFormatter::ToString(value, GetFormatFlags());
|
||||
}
|
||||
|
||||
bool
|
||||
wxIntegerValidatorBase::FromString(const wxString& s, LongestValueType *value)
|
||||
{
|
||||
return wxNumberFormatter::FromString(s, value);
|
||||
}
|
||||
|
||||
bool
|
||||
wxIntegerValidatorBase::IsCharOk(const wxString& val, int pos, wxChar ch) const
|
||||
{
|
||||
// We may accept minus sign if we can represent negative numbers at all.
|
||||
if ( ch == '-' )
|
||||
{
|
||||
// Notice that entering '-' can make our value invalid, for example if
|
||||
// we're limited to -5..15 range and the current value is 12, then the
|
||||
// new value would be (invalid) -12. We consider it better to let the
|
||||
// user do this because perhaps he is going to press Delete key next to
|
||||
// make it -2 and forcing him to delete 1 first would be unnatural.
|
||||
//
|
||||
// TODO: It would be nice to indicate that the current control contents
|
||||
// is invalid (if it's indeed going to be the case) once
|
||||
// wxValidator supports doing this non-intrusively.
|
||||
return m_min < 0 && IsMinusOk(val, pos);
|
||||
}
|
||||
|
||||
// We only accept digits here (remember that '-' is taken care of by the
|
||||
// base class already).
|
||||
if ( ch < '0' || ch > '9' )
|
||||
return false;
|
||||
|
||||
// And the value after insertion needs to be in the defined range.
|
||||
LongestValueType value;
|
||||
if ( !FromString(GetValueAfterInsertingChar(val, pos, ch), &value) )
|
||||
return false;
|
||||
|
||||
return IsInRange(value);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// wxFloatingPointValidatorBase implementation
|
||||
// ============================================================================
|
||||
|
||||
wxString wxFloatingPointValidatorBase::ToString(LongestValueType value) const
|
||||
{
|
||||
return wxNumberFormatter::ToString(value, m_precision, GetFormatFlags());
|
||||
}
|
||||
|
||||
bool
|
||||
wxFloatingPointValidatorBase::FromString(const wxString& s,
|
||||
LongestValueType *value)
|
||||
{
|
||||
return wxNumberFormatter::FromString(s, value);
|
||||
}
|
||||
|
||||
bool
|
||||
wxFloatingPointValidatorBase::IsCharOk(const wxString& val,
|
||||
int pos,
|
||||
wxChar ch) const
|
||||
{
|
||||
// We may accept minus sign if we can represent negative numbers at all.
|
||||
if ( ch == '-' )
|
||||
return m_min < 0 && IsMinusOk(val, pos);
|
||||
|
||||
const wxChar separator = wxNumberFormatter::GetDecimalSeparator();
|
||||
if ( ch == separator )
|
||||
{
|
||||
if ( val.find(separator) != wxString::npos )
|
||||
{
|
||||
// There is already a decimal separator, can't insert another one.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Prepending a separator before the minus sign isn't allowed.
|
||||
if ( pos == 0 && !val.empty() && val[0] == '-' )
|
||||
return false;
|
||||
|
||||
// Otherwise always accept it, adding a decimal separator doesn't
|
||||
// change the number value and, in particular, can't make it invalid.
|
||||
// OTOH the checks below might not pass because strings like "." or
|
||||
// "-." are not valid numbers so parsing them would fail, hence we need
|
||||
// to treat it specially here.
|
||||
return true;
|
||||
}
|
||||
|
||||
// Must be a digit then.
|
||||
if ( ch < '0' || ch > '9' )
|
||||
return false;
|
||||
|
||||
// Check whether the value we'd obtain if we accepted this key is correct.
|
||||
const wxString newval(GetValueAfterInsertingChar(val, pos, ch));
|
||||
|
||||
LongestValueType value;
|
||||
if ( !FromString(newval, &value) )
|
||||
return false;
|
||||
|
||||
// Also check that it doesn't have too many decimal digits.
|
||||
const size_t posSep = newval.find(separator);
|
||||
if ( posSep != wxString::npos && newval.length() - posSep - 1 > m_precision )
|
||||
return false;
|
||||
|
||||
// Finally check whether it is in the range.
|
||||
return IsInRange(value);
|
||||
}
|
||||
|
||||
#endif // wxUSE_VALIDATORS && wxUSE_TEXTCTRL
|
|
@ -0,0 +1,465 @@
|
|||
/////////////////////////////////////////////////////////////////////////////
|
||||
// Name: wx/valnum.h
|
||||
// Purpose: Numeric validator classes.
|
||||
// Author: Vadim Zeitlin based on the submission of Fulvio Senore
|
||||
// Created: 2010-11-06
|
||||
// Copyright: (c) 2010 wxWidgets team
|
||||
// (c) 2011 Vadim Zeitlin <vadim@wxwidgets.org>
|
||||
// Licence: wxWindows licence
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _WX_VALNUM_H_
|
||||
#define _WX_VALNUM_H_
|
||||
|
||||
#include "wx/defs.h"
|
||||
|
||||
#if wxUSE_VALIDATORS
|
||||
|
||||
#include "wx/validate.h"
|
||||
|
||||
#include <limits>
|
||||
|
||||
#define wxTextEntry wxTextCtrl
|
||||
|
||||
// Bit masks used for numeric validator styles.
|
||||
enum wxNumValidatorStyle
|
||||
{
|
||||
wxNUM_VAL_DEFAULT = 0x0,
|
||||
wxNUM_VAL_THOUSANDS_SEPARATOR = 0x1,
|
||||
wxNUM_VAL_ZERO_AS_BLANK = 0x2,
|
||||
wxNUM_VAL_NO_TRAILING_ZEROES = 0x4
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Base class for all numeric validators.
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
class wxNumValidatorBase : public wxValidator
|
||||
{
|
||||
public:
|
||||
// Change the validator style. Usually it's specified during construction.
|
||||
void SetStyle(int style) { m_style = style; }
|
||||
|
||||
|
||||
// Override base class method to not do anything but always return success:
|
||||
// we don't need this as we do our validation on the fly here.
|
||||
virtual bool Validate(wxWindow * WXUNUSED(parent)) { return true; }
|
||||
|
||||
protected:
|
||||
wxNumValidatorBase(int style)
|
||||
{
|
||||
m_style = style;
|
||||
}
|
||||
|
||||
wxNumValidatorBase(const wxNumValidatorBase& other) : wxValidator()
|
||||
{
|
||||
m_style = other.m_style;
|
||||
}
|
||||
|
||||
bool HasFlag(wxNumValidatorStyle style) const
|
||||
{
|
||||
return (m_style & style) != 0;
|
||||
}
|
||||
|
||||
// Get the text entry of the associated control. Normally shouldn't ever
|
||||
// return NULL (and will assert if it does return it) but the caller should
|
||||
// still test the return value for safety.
|
||||
wxTextEntry *GetTextEntry() const;
|
||||
|
||||
// Convert wxNUM_VAL_THOUSANDS_SEPARATOR and wxNUM_VAL_NO_TRAILING_ZEROES
|
||||
// bits of our style to the corresponding wxNumberFormatter::Style values.
|
||||
int GetFormatFlags() const;
|
||||
|
||||
// Return true if pressing a '-' key is acceptable for the current control
|
||||
// contents and insertion point. This is meant to be called from the
|
||||
// derived class IsCharOk() implementation.
|
||||
bool IsMinusOk(const wxString& val, int pos) const;
|
||||
|
||||
// Return the string which would result from inserting the given character
|
||||
// at the specified position.
|
||||
wxString GetValueAfterInsertingChar(wxString val, int pos, wxChar ch) const
|
||||
{
|
||||
val.insert(pos, ch);
|
||||
return val;
|
||||
}
|
||||
|
||||
private:
|
||||
// Check whether the specified character can be inserted in the control at
|
||||
// the given position in the string representing the current controls
|
||||
// contents.
|
||||
//
|
||||
// Notice that the base class checks for '-' itself so it's never passed to
|
||||
// this function.
|
||||
virtual bool IsCharOk(const wxString& val, int pos, wxChar ch) const = 0;
|
||||
|
||||
// NormalizeString the contents of the string if it's a valid number, return
|
||||
// empty string otherwise.
|
||||
virtual wxString NormalizeString(const wxString& s) const = 0;
|
||||
|
||||
|
||||
// Event handlers.
|
||||
void OnChar(wxKeyEvent& event);
|
||||
void OnKillFocus(wxFocusEvent& event);
|
||||
|
||||
|
||||
// Determine the current insertion point and text in the associated control.
|
||||
void GetCurrentValueAndInsertionPoint(wxString& val, int& pos) const;
|
||||
|
||||
|
||||
// Combination of wxVAL_NUM_XXX values.
|
||||
int m_style;
|
||||
|
||||
|
||||
DECLARE_EVENT_TABLE();
|
||||
|
||||
DECLARE_NO_ASSIGN_CLASS(wxNumValidatorBase);
|
||||
};
|
||||
|
||||
namespace wxPrivate
|
||||
{
|
||||
|
||||
// This is a helper class used by wxIntegerValidator and wxFloatingPointValidator
|
||||
// below that implements Transfer{To,From}Window() adapted to the type of the
|
||||
// variable.
|
||||
//
|
||||
// The template argument B is the name of the base class which must derive from
|
||||
// wxNumValidatorBase and define LongestValueType type and {To,As}String()
|
||||
// methods i.e. basically be one of wx{Integer,Number}ValidatorBase classes.
|
||||
//
|
||||
// The template argument T is just the type handled by the validator that will
|
||||
// inherit from this one.
|
||||
template <class B, typename T>
|
||||
class wxNumValidator : public B
|
||||
{
|
||||
public:
|
||||
typedef B BaseValidator;
|
||||
typedef T ValueType;
|
||||
|
||||
typedef typename BaseValidator::LongestValueType LongestValueType;
|
||||
|
||||
// FIXME-VC6: This compiler fails to compile the assert below with a
|
||||
// nonsensical error C2248: "'LongestValueType' : cannot access protected
|
||||
// typedef declared in class 'wxIntegerValidatorBase'" so just disable the
|
||||
// check for it.
|
||||
#ifndef __VISUALC6__
|
||||
wxCOMPILE_TIME_ASSERT
|
||||
(
|
||||
sizeof(ValueType) <= sizeof(LongestValueType),
|
||||
UnsupportedType
|
||||
);
|
||||
#endif // __VISUALC6__
|
||||
|
||||
void SetMin(ValueType min)
|
||||
{
|
||||
this->DoSetMin(min);
|
||||
}
|
||||
|
||||
void SetMax(ValueType max)
|
||||
{
|
||||
this->DoSetMax(max);
|
||||
}
|
||||
|
||||
void SetRange(ValueType min, ValueType max)
|
||||
{
|
||||
SetMin(min);
|
||||
SetMax(max);
|
||||
}
|
||||
|
||||
virtual bool TransferToWindow()
|
||||
{
|
||||
if ( m_value )
|
||||
{
|
||||
wxTextEntry * const control = BaseValidator::GetTextEntry();
|
||||
if ( !control )
|
||||
return false;
|
||||
|
||||
control->SetValue(NormalizeValue(*m_value));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool TransferFromWindow()
|
||||
{
|
||||
if ( m_value )
|
||||
{
|
||||
wxTextEntry * const control = BaseValidator::GetTextEntry();
|
||||
if ( !control )
|
||||
return false;
|
||||
|
||||
const wxString s(control->GetValue());
|
||||
LongestValueType value;
|
||||
if ( s.empty() && BaseValidator::HasFlag(wxNUM_VAL_ZERO_AS_BLANK) )
|
||||
value = 0;
|
||||
else if ( !BaseValidator::FromString(s, &value) )
|
||||
return false;
|
||||
|
||||
if ( !this->IsInRange(value) )
|
||||
return false;
|
||||
|
||||
*m_value = static_cast<ValueType>(value);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
wxNumValidator(ValueType *value, int style)
|
||||
: BaseValidator(style),
|
||||
m_value(value)
|
||||
{
|
||||
}
|
||||
|
||||
// Implement wxNumValidatorBase virtual method which is the same for
|
||||
// both integer and floating point numbers.
|
||||
virtual wxString NormalizeString(const wxString& s) const
|
||||
{
|
||||
LongestValueType value;
|
||||
return BaseValidator::FromString(s, &value) ? NormalizeValue(value)
|
||||
: wxString();
|
||||
}
|
||||
|
||||
private:
|
||||
// Just a helper which is a common part of TransferToWindow() and
|
||||
// NormalizeString(): returns string representation of a number honouring
|
||||
// wxNUM_VAL_ZERO_AS_BLANK flag.
|
||||
wxString NormalizeValue(LongestValueType value) const
|
||||
{
|
||||
wxString s;
|
||||
if ( value != 0 || !BaseValidator::HasFlag(wxNUM_VAL_ZERO_AS_BLANK) )
|
||||
s = this->ToString(value);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
ValueType * const m_value;
|
||||
|
||||
DECLARE_NO_ASSIGN_CLASS(wxNumValidator);
|
||||
};
|
||||
|
||||
} // namespace wxPrivate
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Validators for integer numbers.
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// Base class for integer numbers validator. This class contains all non
|
||||
// type-dependent code of wxIntegerValidator<> and always works with values of
|
||||
// type LongestValueType. It is not meant to be used directly, please use
|
||||
// wxIntegerValidator<> only instead.
|
||||
class wxIntegerValidatorBase : public wxNumValidatorBase
|
||||
{
|
||||
protected:
|
||||
// Define the type we use here, it should be the maximal-sized integer type
|
||||
// we support to make it possible to base wxIntegerValidator<> for any type
|
||||
// on it.
|
||||
#ifdef wxLongLong_t
|
||||
typedef wxLongLong_t LongestValueType;
|
||||
#else
|
||||
typedef long LongestValueType;
|
||||
#endif
|
||||
|
||||
wxIntegerValidatorBase(int style)
|
||||
: wxNumValidatorBase(style)
|
||||
{
|
||||
wxASSERT_MSG( !(style & wxNUM_VAL_NO_TRAILING_ZEROES),
|
||||
wxT("This style doesn't make sense for integers.") );
|
||||
}
|
||||
|
||||
wxIntegerValidatorBase(const wxIntegerValidatorBase& other)
|
||||
: wxNumValidatorBase(other)
|
||||
{
|
||||
m_min = other.m_min;
|
||||
m_max = other.m_max;
|
||||
}
|
||||
|
||||
// Provide methods for wxNumValidator use.
|
||||
wxString ToString(LongestValueType value) const;
|
||||
static bool FromString(const wxString& s, LongestValueType *value);
|
||||
|
||||
void DoSetMin(LongestValueType min) { m_min = min; }
|
||||
void DoSetMax(LongestValueType max) { m_max = max; }
|
||||
|
||||
bool IsInRange(LongestValueType value) const
|
||||
{
|
||||
return m_min <= value && value <= m_max;
|
||||
}
|
||||
|
||||
// Implement wxNumValidatorBase pure virtual method.
|
||||
virtual bool IsCharOk(const wxString& val, int pos, wxChar ch) const;
|
||||
|
||||
private:
|
||||
// Minimal and maximal values accepted (inclusive).
|
||||
LongestValueType m_min, m_max;
|
||||
|
||||
DECLARE_NO_ASSIGN_CLASS(wxIntegerValidatorBase);
|
||||
};
|
||||
|
||||
// Validator for integer numbers. It can actually work with any integer type
|
||||
// (short, int or long and long long if supported) and their unsigned versions
|
||||
// as well.
|
||||
template <typename T>
|
||||
class wxIntegerValidator
|
||||
: public wxPrivate::wxNumValidator<wxIntegerValidatorBase, T>
|
||||
{
|
||||
public:
|
||||
typedef T ValueType;
|
||||
|
||||
typedef
|
||||
wxPrivate::wxNumValidator<wxIntegerValidatorBase, T> Base;
|
||||
|
||||
// Ctor for an integer validator.
|
||||
//
|
||||
// Sets the range appropriately for the type, including setting 0 as the
|
||||
// minimal value for the unsigned types.
|
||||
wxIntegerValidator(ValueType *value = NULL, int style = wxNUM_VAL_DEFAULT)
|
||||
: Base(value, style)
|
||||
{
|
||||
this->DoSetMin(std::numeric_limits<ValueType>::min());
|
||||
this->DoSetMax(std::numeric_limits<ValueType>::max());
|
||||
}
|
||||
|
||||
virtual wxObject *Clone() const { return new wxIntegerValidator(*this); }
|
||||
|
||||
private:
|
||||
DECLARE_NO_ASSIGN_CLASS(wxIntegerValidator);
|
||||
};
|
||||
|
||||
// Helper function for creating integer validators which allows to avoid
|
||||
// explicitly specifying the type as it deduces it from its parameter.
|
||||
template <typename T>
|
||||
inline wxIntegerValidator<T>
|
||||
wxMakeIntegerValidator(T *value, int style = wxNUM_VAL_DEFAULT)
|
||||
{
|
||||
return wxIntegerValidator<T>(value, style);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Validators for floating point numbers.
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// Similar to wxIntegerValidatorBase, this class is not meant to be used
|
||||
// directly, only wxFloatingPointValidator<> should be used in the user code.
|
||||
class wxFloatingPointValidatorBase : public wxNumValidatorBase
|
||||
{
|
||||
public:
|
||||
// Set precision i.e. the number of digits shown (and accepted on input)
|
||||
// after the decimal point. By default this is set to the maximal precision
|
||||
// supported by the type handled by the validator.
|
||||
void SetPrecision(unsigned precision) { m_precision = precision; }
|
||||
|
||||
protected:
|
||||
// Notice that we can't use "long double" here because it's not supported
|
||||
// by wxNumberFormatter yet, so restrict ourselves to just double (and
|
||||
// float).
|
||||
typedef double LongestValueType;
|
||||
|
||||
wxFloatingPointValidatorBase(int style)
|
||||
: wxNumValidatorBase(style)
|
||||
{
|
||||
}
|
||||
|
||||
wxFloatingPointValidatorBase(const wxFloatingPointValidatorBase& other)
|
||||
: wxNumValidatorBase(other)
|
||||
{
|
||||
m_precision = other.m_precision;
|
||||
|
||||
m_min = other.m_min;
|
||||
m_max = other.m_max;
|
||||
}
|
||||
|
||||
// Provide methods for wxNumValidator use.
|
||||
wxString ToString(LongestValueType value) const;
|
||||
static bool FromString(const wxString& s, LongestValueType *value);
|
||||
|
||||
void DoSetMin(LongestValueType min) { m_min = min; }
|
||||
void DoSetMax(LongestValueType max) { m_max = max; }
|
||||
|
||||
bool IsInRange(LongestValueType value) const
|
||||
{
|
||||
return m_min <= value && value <= m_max;
|
||||
}
|
||||
|
||||
// Implement wxNumValidatorBase pure virtual method.
|
||||
virtual bool IsCharOk(const wxString& val, int pos, wxChar ch) const;
|
||||
|
||||
private:
|
||||
// Maximum number of decimals digits after the decimal separator.
|
||||
unsigned m_precision;
|
||||
|
||||
// Minimal and maximal values accepted (inclusive).
|
||||
LongestValueType m_min, m_max;
|
||||
|
||||
DECLARE_NO_ASSIGN_CLASS(wxFloatingPointValidatorBase);
|
||||
};
|
||||
|
||||
// Validator for floating point numbers. It can be used with float, double or
|
||||
// long double values.
|
||||
template <typename T>
|
||||
class wxFloatingPointValidator
|
||||
: public wxPrivate::wxNumValidator<wxFloatingPointValidatorBase, T>
|
||||
{
|
||||
public:
|
||||
typedef T ValueType;
|
||||
typedef wxPrivate::wxNumValidator<wxFloatingPointValidatorBase, T> Base;
|
||||
|
||||
// Ctor using implicit (maximal) precision for this type.
|
||||
wxFloatingPointValidator(ValueType *value = NULL,
|
||||
int style = wxNUM_VAL_DEFAULT)
|
||||
: Base(value, style)
|
||||
{
|
||||
DoSetMinMax();
|
||||
|
||||
this->SetPrecision(std::numeric_limits<ValueType>::digits10);
|
||||
}
|
||||
|
||||
// Ctor specifying an explicit precision.
|
||||
wxFloatingPointValidator(int precision,
|
||||
ValueType *value = NULL,
|
||||
int style = wxNUM_VAL_DEFAULT)
|
||||
: Base(value, style)
|
||||
{
|
||||
DoSetMinMax();
|
||||
|
||||
this->SetPrecision(precision);
|
||||
}
|
||||
|
||||
virtual wxObject *Clone() const
|
||||
{
|
||||
return new wxFloatingPointValidator(*this);
|
||||
}
|
||||
|
||||
private:
|
||||
void DoSetMinMax()
|
||||
{
|
||||
// NB: Do not use min(), it's not the smallest representable value for
|
||||
// the floating point types but rather the smallest representable
|
||||
// positive value.
|
||||
this->DoSetMin(-std::numeric_limits<ValueType>::max());
|
||||
this->DoSetMax( std::numeric_limits<ValueType>::max());
|
||||
}
|
||||
};
|
||||
|
||||
// Helper similar to wxMakeIntValidator().
|
||||
//
|
||||
// NB: Unfortunately we can't just have a wxMakeNumericValidator() which would
|
||||
// return either wxIntegerValidator<> or wxFloatingPointValidator<> so we
|
||||
// do need two different functions.
|
||||
template <typename T>
|
||||
inline wxFloatingPointValidator<T>
|
||||
wxMakeFloatingPointValidator(T *value, int style = wxNUM_VAL_DEFAULT)
|
||||
{
|
||||
return wxFloatingPointValidator<T>(value, style);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline wxFloatingPointValidator<T>
|
||||
wxMakeFloatingPointValidator(int precision, T *value, int style = wxNUM_VAL_DEFAULT)
|
||||
{
|
||||
return wxFloatingPointValidator<T>(precision, value, style);
|
||||
}
|
||||
|
||||
#endif // wxUSE_VALIDATORS
|
||||
|
||||
#endif // _WX_VALNUM_H_
|
|
@ -1911,6 +1911,14 @@
|
|||
RelativePath="..\..\..\src\widgets\MultiDialog.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\..\src\widgets\numformatter.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\..\src\widgets\numformatter.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\..\src\widgets\ProgressDialog.cpp"
|
||||
>
|
||||
|
@ -1935,6 +1943,14 @@
|
|||
RelativePath="..\..\..\src\widgets\TimeTextCtrl.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\..\src\widgets\valnum.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\..\src\widgets\valnum.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\..\src\widgets\Warning.cpp"
|
||||
>
|
||||
|
|
Loading…
Reference in New Issue