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