8f8d58d193
Change {DECLARE,IMPLEMENT}_*CLASS and {DECLARE,BEGIN,END}_EVENT_TABLE occurrences to use the wx-prefixed version of the macros.
300 lines
9.1 KiB
C++
300 lines
9.1 KiB
C++
/////////////////////////////////////////////////////////////////////////////
|
|
// 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 "wx/valnum.h"
|
|
#include "wx/numformatter.h"
|
|
|
|
// ============================================================================
|
|
// wxNumValidatorBase implementation
|
|
// ============================================================================
|
|
|
|
wxBEGIN_EVENT_TABLE(wxNumValidatorBase, wxValidator)
|
|
EVT_CHAR(wxNumValidatorBase::OnChar)
|
|
EVT_KILL_FOCUS(wxNumValidatorBase::OnKillFocus)
|
|
wxEND_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
|
|
|
|
#if wxUSE_COMBOBOX
|
|
if ( wxComboBox *combo = wxDynamicCast(m_validatorWindow, wxComboBox) )
|
|
return combo;
|
|
#endif // wxUSE_COMBOBOX
|
|
|
|
wxFAIL_MSG("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
|
|
const int ch = event.GetUnicodeKey();
|
|
if ( ch == WXK_NONE )
|
|
{
|
|
// 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 ( ch < WXK_SPACE || ch == 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
|