1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        wx/valnum.h
3 // Purpose:     Numeric validator classes.
4 // Author:      Vadim Zeitlin based on the submission of Fulvio Senore
5 // Created:     2010-11-06
6 // Copyright:   (c) 2010 wxWidgets team
7 //              (c) 2011 Vadim Zeitlin <vadim@wxwidgets.org>
8 // Licence:     wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10 
11 #ifndef _WIDGETS_VALNUM_H_
12 #define _WIDGETS_VALNUM_H_
13 
14 #include <memory>
15 #include <wx/setup.h> // for wxUSE_* macros
16 #include <wx/defs.h>
17 
18 #if wxUSE_VALIDATORS
19 
20 #include <wx/textctrl.h> // complete type needed in template function
21 #include <wx/validate.h> // to inherit
22 
23 #include <limits>
24 
25 class TranslatableString;
26 
27 // Bit masks used for numeric validator styles.
28 enum class NumValidatorStyle : int
29 {
30     DEFAULT               = 0x0,
31     THOUSANDS_SEPARATOR   = 0x1,
32     ZERO_AS_BLANK         = 0x2,
33     NO_TRAILING_ZEROES    = 0x4,
34     ONE_TRAILING_ZERO     = 0x8,
35     TWO_TRAILING_ZEROES   = 0x10,
36     THREE_TRAILING_ZEROES = 0x20
37 };
38 
39 inline NumValidatorStyle operator | (NumValidatorStyle x, NumValidatorStyle y)
40 { return NumValidatorStyle( int(x) | int(y) ); }
41 
42 inline int operator & (NumValidatorStyle x, NumValidatorStyle y)
43 { return int(x) & int(y); }
44 
45 // ----------------------------------------------------------------------------
46 // Base class for all numeric validators.
47 // ----------------------------------------------------------------------------
48 
49 class AUDACITY_DLL_API NumValidatorBase /* not final */ : public wxValidator
50 {
51 public:
52     // Change the validator style. Usually it's specified during construction.
SetStyle(NumValidatorStyle style)53     void SetStyle(NumValidatorStyle style) { m_style = style; }
54 
55     // Called when the value in the window must be validated.
56     // This function can pop up an error message.
57     bool Validate(wxWindow * parent) override;
58 
59 protected:
NumValidatorBase(NumValidatorStyle style)60     NumValidatorBase(NumValidatorStyle style)
61     {
62         m_style = style;
63         m_minSet = false;
64         m_maxSet = false;
65     }
66 
NumValidatorBase(const NumValidatorBase & other)67     NumValidatorBase(const NumValidatorBase& other) : wxValidator()
68     {
69         m_style = other.m_style;
70         m_minSet = other.m_minSet;
71         m_maxSet = other.m_maxSet;
72     }
73 
HasFlag(NumValidatorStyle style)74     bool HasFlag(NumValidatorStyle style) const
75     {
76         return (m_style & style) != 0;
77     }
78 
79     // Get the text entry of the associated control. Normally shouldn't ever
80     // return NULL (and will assert if it does return it) but the caller should
81     // still test the return value for safety.
82     wxTextEntry *GetTextEntry() const;
83 
84     // Convert NumValidatorStyle::THOUSANDS_SEPARATOR and NumValidatorStyle::NO_TRAILING_ZEROES
85     // bits of our style to the corresponding NumberFormatter::Style values.
86     int GetFormatFlags() const;
87 
88     // Return true if pressing a '-' key is acceptable for the current control
89     // contents and insertion point. This is meant to be called from the
90     // derived class IsCharOk() implementation.
91     bool IsMinusOk(const wxString& val, int pos) const;
92 
93     // Return the string which would result from inserting the given character
94     // at the specified position.
GetValueAfterInsertingChar(const wxString & valArg,int pos,wxChar ch)95     wxString GetValueAfterInsertingChar(const wxString &valArg, int pos, wxChar ch) const
96     {
97         wxString val(valArg);
98         val.insert(pos, ch);
99         return val;
100     }
101 
102     bool m_minSet;
103     bool m_maxSet;
104 
105 private:
106     // Check whether the specified character can be inserted in the control at
107     // the given position in the string representing the current controls
108     // contents.
109     //
110     // Notice that the base class checks for '-' itself so it's never passed to
111     // this function.
112     virtual bool IsCharOk(const wxString& val, int pos, wxChar ch) const = 0;
113 
114     // NormalizeString the contents of the string if it's a valid number, return
115     // empty string otherwise.
116     virtual wxString NormalizeString(const wxString& s) const = 0;
117 
118     // Do all checks to ensure this is a valid value.
119     // Returns 'true' if the control has valid value.
120     // Otherwise the cause is indicated in 'errMsg'.
121     virtual bool DoValidateNumber(TranslatableString * errMsg) const = 0;
122 
123     // Event handlers.
124     void OnChar(wxKeyEvent& event);
125     void OnPaste(wxClipboardTextEvent& event);
126     void OnKillFocus(wxFocusEvent& event);
127 
128 
129     // Determine the current insertion point and text in the associated control.
130     void GetCurrentValueAndInsertionPoint(wxString& val, int& pos) const;
131 
132 
133     // Combination of wxVAL_NUM_XXX values.
134     NumValidatorStyle m_style;
135 
136     DECLARE_EVENT_TABLE()
137 
138     DECLARE_NO_ASSIGN_CLASS(NumValidatorBase)
139 };
140 
141 namespace Private
142 {
143 
144 // This is a helper class used by IntegerValidator and FloatingPointValidator
145 // below that implements Transfer{To,From}Window() adapted to the type of the
146 // variable.
147 //
148 // The template argument B is the name of the base class which must derive from
149 // NumValidatorBase and define LongestValueType type and {To,As}String()
150 // methods i.e. basically be one of {Integer,Number}ValidatorBase classes.
151 //
152 // The template argument T is just the type handled by the validator that will
153 // inherit from this one.
154 template <class B, typename T>
155 class NumValidator /* final */ : public B
156 {
157 public:
158     typedef B BaseValidator;
159     typedef T ValueType;
160 
161     typedef typename BaseValidator::LongestValueType LongestValueType;
162 
163     // FIXME-VC6: This compiler fails to compile the assert below with a
164     // nonsensical error C2248: "'LongestValueType' : cannot access protected
165     // typedef declared in class 'IntegerValidatorBase'" so just disable the
166     // check for it.
167 #ifndef __VISUALC6__
168     wxCOMPILE_TIME_ASSERT
169     (
170         sizeof(ValueType) <= sizeof(LongestValueType),
171         UnsupportedType
172     );
173 #endif // __VISUALC6__
174 
SetMin(ValueType min)175     void SetMin(ValueType min)
176     {
177         this->DoSetMin(min);
178         BaseValidator::m_minSet = (min != std::numeric_limits<T>::lowest());
179     }
180 
SetMax(ValueType max)181     void SetMax(ValueType max)
182     {
183         this->DoSetMax(max);
184         BaseValidator::m_maxSet = (max != std::numeric_limits<T>::max());
185     }
186 
SetRange(ValueType min,ValueType max)187     void SetRange(ValueType min, ValueType max)
188     {
189         SetMin(min);
190         SetMax(max);
191     }
192 
TransferToWindow()193     bool TransferToWindow() override
194     {
195         if ( m_value )
196         {
197             wxTextEntry * const control = BaseValidator::GetTextEntry();
198             if ( !control )
199                 return false;
200 
201             control->ChangeValue(NormalizeValue(*m_value));
202         }
203 
204         return true;
205     }
206 
TransferFromWindow()207     bool TransferFromWindow() override
208     {
209         if ( m_value )
210         {
211             wxTextEntry * const control = BaseValidator::GetTextEntry();
212             if ( !control )
213                 return false;
214 
215             // If window is disabled, simply return
216             if ( !this->m_validatorWindow->IsEnabled() )
217                 return true;
218 
219             const wxString s(control->GetValue());
220             LongestValueType value;
221             if ( s.empty() && BaseValidator::HasFlag(NumValidatorStyle::ZERO_AS_BLANK) )
222                 value = 0;
223             else if ( !BaseValidator::FromString(s, &value) )
224                 return false;
225 
226             if ( !this->IsInRange(value) )
227                 return false;
228 
229             *m_value = static_cast<ValueType>(value);
230         }
231 
232         return true;
233     }
234 
235 protected:
NumValidator(ValueType * value,NumValidatorStyle style)236     NumValidator(ValueType *value, NumValidatorStyle style)
237         : BaseValidator(style),
238           m_value(value)
239     {
240     }
241 
242     // Implement NumValidatorBase virtual method which is the same for
243     // both integer and floating point numbers.
NormalizeString(const wxString & s)244     wxString NormalizeString(const wxString& s) const override
245     {
246         LongestValueType value;
247         return BaseValidator::FromString(s, &value) ? NormalizeValue(value)
248                                                     : wxString();
249     }
250 
251 private:
252     // Just a helper which is a common part of TransferToWindow() and
253     // NormalizeString(): returns string representation of a number honouring
254     // NumValidatorStyle::ZERO_AS_BLANK flag.
NormalizeValue(LongestValueType value)255     wxString NormalizeValue(LongestValueType value) const
256     {
257         wxString s;
258         if ( value != 0 || !BaseValidator::HasFlag(NumValidatorStyle::ZERO_AS_BLANK) )
259             s = this->ToString(value);
260 
261         return s;
262     }
263 
264 
265     ValueType * const m_value;
266 
267     DECLARE_NO_ASSIGN_CLASS(NumValidator)
268 };
269 
270 } // namespace Private
271 
272 // ----------------------------------------------------------------------------
273 // Validators for integer numbers.
274 // ----------------------------------------------------------------------------
275 
276 // Base class for integer numbers validator. This class contains all non
277 // type-dependent code of wxIntegerValidator<> and always works with values of
278 // type LongestValueType. It is not meant to be used directly, please use
279 // IntegerValidator<> only instead.
280 class AUDACITY_DLL_API IntegerValidatorBase /* not final */
281    : public NumValidatorBase
282 {
283 protected:
284     // Define the type we use here, it should be the maximal-sized integer type
285     // we support to make it possible to base IntegerValidator<> for any type
286     // on it.
287 #ifdef wxLongLong_t
288     typedef wxLongLong_t LongestValueType;
289 #else
290     typedef long LongestValueType;
291 #endif
292 
IntegerValidatorBase(NumValidatorStyle style)293     IntegerValidatorBase(NumValidatorStyle style)
294         : NumValidatorBase(style)
295     {
296         wxASSERT_MSG( !(style & NumValidatorStyle::NO_TRAILING_ZEROES),
297                       wxT("This style doesn't make sense for integers.") );
298         wxASSERT_MSG( !(style & NumValidatorStyle::ONE_TRAILING_ZERO),
299                       wxT("This style doesn't make sense for integers.") );
300         wxASSERT_MSG( !(style & NumValidatorStyle::TWO_TRAILING_ZEROES),
301                       wxT("This style doesn't make sense for integers.") );
302         wxASSERT_MSG( !(style & NumValidatorStyle::THREE_TRAILING_ZEROES),
303                       wxT("This style doesn't make sense for integers.") );
304     }
305 
IntegerValidatorBase(const IntegerValidatorBase & other)306     IntegerValidatorBase(const IntegerValidatorBase& other)
307         : NumValidatorBase(other)
308     {
309         m_min = other.m_min;
310         m_max = other.m_max;
311     }
312 
313     // Provide methods for NumValidator use.
314     wxString ToString(LongestValueType value) const;
315     static bool FromString(const wxString& s, LongestValueType *value);
316 
DoSetMin(LongestValueType min)317     void DoSetMin(LongestValueType min) { m_min = min; }
DoSetMax(LongestValueType max)318     void DoSetMax(LongestValueType max) { m_max = max; }
319 
IsInRange(LongestValueType value)320     bool IsInRange(LongestValueType value) const
321     {
322         return m_min <= value && value <= m_max;
323     }
324 
325     // Implement NumValidatorBase pure virtual method.
326     bool IsCharOk(const wxString& val, int pos, wxChar ch) const override;
327     bool DoValidateNumber(TranslatableString * errMsg) const override;
328 
329 private:
330     // Minimal and maximal values accepted (inclusive).
331     LongestValueType m_min, m_max;
332 
333     DECLARE_NO_ASSIGN_CLASS(IntegerValidatorBase)
334 };
335 
336 // Validator for integer numbers. It can actually work with any integer type
337 // (short, int or long and long long if supported) and their unsigned versions
338 // as well.
339 template <typename T>
340 class IntegerValidator final
341     : public Private::NumValidator<IntegerValidatorBase, T>
342 {
343 public:
344     typedef T ValueType;
345 
346     typedef
347         Private::NumValidator<IntegerValidatorBase, T> Base;
348 
349     // Ctor for an integer validator.
350     //
351     // Sets the range appropriately for the type, including setting 0 as the
352     // minimal value for the unsigned types.
353     IntegerValidator(
354          ValueType *value = NULL,
355          NumValidatorStyle style = NumValidatorStyle::DEFAULT,
356          ValueType min = std::numeric_limits<ValueType>::min(),
357          ValueType max = std::numeric_limits<ValueType>::max())
Base(value,style)358       : Base(value, style)
359     {
360        this->SetRange(min, max);
361     }
362 
363     // Clone is required by wxwidgets; implemented via copy constructor
Clone()364     wxObject *Clone() const override { return safenew IntegerValidator(*this); }
365 
366 private:
367     DECLARE_NO_ASSIGN_CLASS(IntegerValidator)
368 };
369 
370 // Helper function for creating integer validators which allows to avoid
371 // explicitly specifying the type as it deduces it from its parameter.
372 template <typename T>
373 inline IntegerValidator<T>
374 MakeIntegerValidator(T *value, NumValidatorStyle style = NumValidatorStyle::DEFAULT)
375 {
376     return IntegerValidator<T>(value, style);
377 }
378 
379 // ----------------------------------------------------------------------------
380 // Validators for floating point numbers.
381 // ----------------------------------------------------------------------------
382 
383 // Similar to IntegerValidatorBase, this class is not meant to be used
384 // directly, only FloatingPointValidator<> should be used in the user code.
385 class AUDACITY_DLL_API FloatingPointValidatorBase /* not final */
386    : public NumValidatorBase
387 {
388 public:
389     // Set precision i.e. the number of digits shown (and accepted on input)
390     // after the decimal point. By default this is set to the maximal precision
391     // supported by the type handled by the validator.
SetPrecision(unsigned precision)392     void SetPrecision(unsigned precision) { m_precision = precision; }
393 
394 protected:
395     // Notice that we can't use "long double" here because it's not supported
396     // by NumberFormatter yet, so restrict ourselves to just double (and
397     // float).
398     typedef double LongestValueType;
399 
FloatingPointValidatorBase(NumValidatorStyle style)400     FloatingPointValidatorBase(NumValidatorStyle style)
401         : NumValidatorBase(style)
402     {
403     }
404 
FloatingPointValidatorBase(const FloatingPointValidatorBase & other)405     FloatingPointValidatorBase(const FloatingPointValidatorBase& other)
406         : NumValidatorBase(other)
407     {
408         m_precision = other.m_precision;
409 
410         m_min = other.m_min;
411         m_max = other.m_max;
412     }
413 
414     // Provide methods for NumValidator use.
415     wxString ToString(LongestValueType value) const;
416     static bool FromString(const wxString& s, LongestValueType *value);
417 
DoSetMin(LongestValueType min)418     void DoSetMin(LongestValueType min) { m_min = min; }
DoSetMax(LongestValueType max)419     void DoSetMax(LongestValueType max) { m_max = max; }
420 
IsInRange(LongestValueType value)421     bool IsInRange(LongestValueType value) const
422     {
423         return m_min <= value && value <= m_max;
424     }
425 
426     // Implement NumValidatorBase pure virtual method.
427     bool IsCharOk(const wxString& val, int pos, wxChar ch) const override;
428     bool DoValidateNumber(TranslatableString * errMsg) const override;
429 
430     //Checks that it doesn't have too many decimal digits.
431     bool ValidatePrecision(const wxString& s) const;
432 
433 private:
434     // Maximum number of decimals digits after the decimal separator.
435     unsigned m_precision;
436 
437     // Minimal and maximal values accepted (inclusive).
438     LongestValueType m_min, m_max;
439 
440     DECLARE_NO_ASSIGN_CLASS(FloatingPointValidatorBase)
441 };
442 
443 // Validator for floating point numbers. It can be used with float, double or
444 // long double values.
445 template <typename T>
446 class FloatingPointValidator final
447     : public Private::NumValidator<FloatingPointValidatorBase, T>
448 {
449 public:
450     typedef T ValueType;
451     typedef Private::NumValidator<FloatingPointValidatorBase, T> Base;
452 
453     // Ctor using implicit (maximal) precision for this type.
454     FloatingPointValidator(ValueType *value = NULL,
455                              NumValidatorStyle style = NumValidatorStyle::DEFAULT)
Base(value,style)456         : Base(value, style)
457     {
458         DoSetMinMax();
459 
460         this->SetPrecision(std::numeric_limits<ValueType>::digits10);
461     }
462 
463     // Ctor specifying an explicit precision.
464     FloatingPointValidator(int precision,
465                       ValueType *value = NULL,
466                       NumValidatorStyle style = NumValidatorStyle::DEFAULT,
467                       ValueType min = std::numeric_limits<ValueType>::lowest(),
468                       ValueType max =  std::numeric_limits<ValueType>::max())
Base(value,style)469         : Base(value, style)
470     {
471         this->SetRange( min, max );
472 
473         this->SetPrecision(precision);
474     }
475 
476     // Clone is required by wxwidgets; implemented via copy constructor
Clone()477     wxObject *Clone() const override
478     {
479         return safenew FloatingPointValidator(*this);
480     }
481 
482 private:
DoSetMinMax()483     void DoSetMinMax()
484     {
485         // NB: Do not use min(), it's not the smallest representable value for
486         //     the floating point types but rather the smallest representable
487         //     positive value.
488         this->DoSetMin( std::numeric_limits<ValueType>::lowest());
489         this->DoSetMax( std::numeric_limits<ValueType>::max());
490     }
491 };
492 
493 // Helper similar to MakeIntValidator().
494 //
495 // NB: Unfortunately we can't just have a MakeNumericValidator() which would
496 //     return either IntegerValidator<> or FloatingPointValidator<> so we
497 //     do need two different functions.
498 template <typename T>
499 inline FloatingPointValidator<T>
500 MakeFloatingPointValidator(T *value, NumValidatorStyle style = NumValidatorStyle::DEFAULT)
501 {
502     return FloatingPointValidator<T>(value, style);
503 }
504 
505 template <typename T>
506 inline FloatingPointValidator<T>
507 MakeFloatingPointValidator(int precision, T *value, NumValidatorStyle style = NumValidatorStyle::DEFAULT)
508 {
509     return FloatingPointValidator<T>(precision, value, style);
510 }
511 
512 // Sometimes useful for specifying max and min values for validators, when they
513 // must have the same precision as the validated value
514 AUDACITY_DLL_API double RoundValue(int precision, double value);
515 
516 #endif // wxUSE_VALIDATORS
517 
518 #endif // _WIDGETS_VALNUM_H_
519