forked from mia/Aegisub
Rewrite NumValidator
Split int validating and double validating into two separate classes. Make double parsing, validating and stringifying locale-aware. This is far more complicated than it needs to be due to that Aegisub's locale handling is a total mess. Use DoubleValidator rather than wxFloatingPointValidator, because the latter doesn't work with Aegisub's locale mess (on OS X it uses the C locale for some things, and the locale reported by CoreFoundation for others). Closes #1568.
This commit is contained in:
parent
3691849bac
commit
ebd01c50c9
8 changed files with 172 additions and 262 deletions
|
@ -328,8 +328,7 @@ namespace Automation4 {
|
||||||
return scd;
|
return scd;
|
||||||
}
|
}
|
||||||
|
|
||||||
wxFloatingPointValidator<double> val(4, &value, wxNUM_VAL_NO_TRAILING_ZEROES);
|
::DoubleValidator val(&value, min, max);
|
||||||
val.SetRange(min, max);
|
|
||||||
|
|
||||||
cw = new wxTextCtrl(parent, -1, "", wxDefaultPosition, wxDefaultSize, 0, val);
|
cw = new wxTextCtrl(parent, -1, "", wxDefaultPosition, wxDefaultSize, 0, val);
|
||||||
cw->SetToolTip(to_wx(hint));
|
cw->SetToolTip(to_wx(hint));
|
||||||
|
|
|
@ -39,6 +39,7 @@
|
||||||
#include "help_button.h"
|
#include "help_button.h"
|
||||||
#include "libresrc/libresrc.h"
|
#include "libresrc/libresrc.h"
|
||||||
#include "options.h"
|
#include "options.h"
|
||||||
|
#include "validators.h"
|
||||||
#include "video_provider_dummy.h"
|
#include "video_provider_dummy.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -68,9 +69,7 @@ wxSpinCtrl *spin_ctrl(wxWindow *parent, int min, int max, int *value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
wxControl *spin_ctrl(wxWindow *parent, double min, double max, double *value) {
|
wxControl *spin_ctrl(wxWindow *parent, double min, double max, double *value) {
|
||||||
wxFloatingPointValidator<double> val(4, value);
|
return new wxTextCtrl(parent, -1, "", wxDefaultPosition, wxSize(50, -1), 0, DoubleValidator(value, min, max));
|
||||||
val.SetRange(min, max);
|
|
||||||
return new wxTextCtrl(parent, -1, "", wxDefaultPosition, wxSize(50, -1), 0, val);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
wxComboBox *resolution_shortcuts(wxWindow *parent, int width, int height) {
|
wxComboBox *resolution_shortcuts(wxWindow *parent, int width, int height) {
|
||||||
|
|
|
@ -58,7 +58,7 @@ DialogJumpTo::DialogJumpTo(agi::Context *c)
|
||||||
auto LabelFrame = new wxStaticText(this, -1, _("Frame: "));
|
auto LabelFrame = new wxStaticText(this, -1, _("Frame: "));
|
||||||
auto LabelTime = new wxStaticText(this, -1, _("Time: "));
|
auto LabelTime = new wxStaticText(this, -1, _("Time: "));
|
||||||
|
|
||||||
JumpFrame = new wxTextCtrl(this,-1,"",wxDefaultPosition,wxSize(-1,-1),wxTE_PROCESS_ENTER, NumValidator((int)jumpframe));
|
JumpFrame = new wxTextCtrl(this,-1,"",wxDefaultPosition,wxSize(-1,-1),wxTE_PROCESS_ENTER, IntValidator((int)jumpframe));
|
||||||
JumpFrame->SetMaxLength(std::to_string(c->videoController->GetLength() - 1).size());
|
JumpFrame->SetMaxLength(std::to_string(c->videoController->GetLength() - 1).size());
|
||||||
JumpTime = new TimeEdit(this, -1, c, AssTime(c->videoController->TimeAtFrame(jumpframe)).GetAssFormated(), wxSize(-1,-1));
|
JumpTime = new TimeEdit(this, -1, c, AssTime(c->videoController->TimeAtFrame(jumpframe)).GetAssFormated(), wxSize(-1,-1));
|
||||||
|
|
||||||
|
|
|
@ -78,8 +78,8 @@ DialogProperties::DialogProperties(agi::Context *c)
|
||||||
|
|
||||||
// Resolution box
|
// Resolution box
|
||||||
wxSizer *ResSizer = new wxStaticBoxSizer(wxHORIZONTAL,this,_("Resolution"));
|
wxSizer *ResSizer = new wxStaticBoxSizer(wxHORIZONTAL,this,_("Resolution"));
|
||||||
ResX = new wxTextCtrl(this,-1,"",wxDefaultPosition,wxSize(50,20),0,NumValidator(to_wx(c->ass->GetScriptInfo("PlayResX"))));
|
ResX = new wxTextCtrl(this,-1,"",wxDefaultPosition,wxSize(50,20),0,IntValidator(c->ass->GetScriptInfo("PlayResX")));
|
||||||
ResY = new wxTextCtrl(this,-1,"",wxDefaultPosition,wxSize(50,20),0,NumValidator(to_wx(c->ass->GetScriptInfo("PlayResY"))));
|
ResY = new wxTextCtrl(this,-1,"",wxDefaultPosition,wxSize(50,20),0,IntValidator(c->ass->GetScriptInfo("PlayResY")));
|
||||||
wxStaticText *ResText = new wxStaticText(this,-1,"x");
|
wxStaticText *ResText = new wxStaticText(this,-1,"x");
|
||||||
|
|
||||||
wxButton *FromVideo = new wxButton(this,-1,_("From &video"));
|
wxButton *FromVideo = new wxButton(this,-1,_("From &video"));
|
||||||
|
|
|
@ -135,8 +135,8 @@ static wxSpinCtrl *spin_ctrl(wxWindow *parent, float value, int max_value) {
|
||||||
return new wxSpinCtrl(parent, -1, wxString::Format("%g", value), wxDefaultPosition, wxSize(60, -1), wxSP_ARROW_KEYS, 0, max_value, value);
|
return new wxSpinCtrl(parent, -1, wxString::Format("%g", value), wxDefaultPosition, wxSize(60, -1), wxSP_ARROW_KEYS, 0, max_value, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
static wxTextCtrl *num_text_ctrl(wxWindow *parent, double value, bool allow_negative, wxSize size = wxSize(70, 20)) {
|
static wxTextCtrl *num_text_ctrl(wxWindow *parent, double *value, bool allow_negative, wxSize size = wxSize(70, 20)) {
|
||||||
return new wxTextCtrl(parent, -1, "", wxDefaultPosition, size, 0, NumValidator(value, allow_negative));
|
return new wxTextCtrl(parent, -1, "", wxDefaultPosition, size, 0, DoubleValidator(value, allow_negative));
|
||||||
}
|
}
|
||||||
|
|
||||||
DialogStyleEditor::DialogStyleEditor(wxWindow *parent, AssStyle *style, agi::Context *c, AssStyleStorage *store, std::string const& new_name, wxArrayString const& font_list)
|
DialogStyleEditor::DialogStyleEditor(wxWindow *parent, AssStyle *style, agi::Context *c, AssStyleStorage *store, std::string const& new_name, wxArrayString const& font_list)
|
||||||
|
@ -180,7 +180,7 @@ DialogStyleEditor::DialogStyleEditor(wxWindow *parent, AssStyle *style, agi::Con
|
||||||
// Create controls
|
// Create controls
|
||||||
StyleName = new wxTextCtrl(this, -1, to_wx(style->name));
|
StyleName = new wxTextCtrl(this, -1, to_wx(style->name));
|
||||||
FontName = new wxComboBox(this, -1, to_wx(style->font), wxDefaultPosition, wxSize(150, -1), 0, 0, wxCB_DROPDOWN);
|
FontName = new wxComboBox(this, -1, to_wx(style->font), wxDefaultPosition, wxSize(150, -1), 0, 0, wxCB_DROPDOWN);
|
||||||
FontSize = num_text_ctrl(this, style->fontsize, false, wxSize(50, -1));
|
FontSize = num_text_ctrl(this, &work->fontsize, false, wxSize(50, -1));
|
||||||
BoxBold = new wxCheckBox(this, -1, _("&Bold"));
|
BoxBold = new wxCheckBox(this, -1, _("&Bold"));
|
||||||
BoxItalic = new wxCheckBox(this, -1, _("&Italic"));
|
BoxItalic = new wxCheckBox(this, -1, _("&Italic"));
|
||||||
BoxUnderline = new wxCheckBox(this, -1, _("&Underline"));
|
BoxUnderline = new wxCheckBox(this, -1, _("&Underline"));
|
||||||
|
@ -194,13 +194,13 @@ DialogStyleEditor::DialogStyleEditor(wxWindow *parent, AssStyle *style, agi::Con
|
||||||
for (int i = 0; i < 3; i++)
|
for (int i = 0; i < 3; i++)
|
||||||
margin[i] = spin_ctrl(this, style->Margin[i], 9999);
|
margin[i] = spin_ctrl(this, style->Margin[i], 9999);
|
||||||
Alignment = new wxRadioBox(this, -1, _("Alignment"), wxDefaultPosition, wxDefaultSize, 9, alignValues, 3, wxRA_SPECIFY_COLS);
|
Alignment = new wxRadioBox(this, -1, _("Alignment"), wxDefaultPosition, wxDefaultSize, 9, alignValues, 3, wxRA_SPECIFY_COLS);
|
||||||
Outline = num_text_ctrl(this, style->outline_w, false, wxSize(50, -1));
|
Outline = num_text_ctrl(this, &work->outline_w, false, wxSize(50, -1));
|
||||||
Shadow = num_text_ctrl(this, style->shadow_w, true, wxSize(50, -1));
|
Shadow = num_text_ctrl(this, &work->shadow_w, true, wxSize(50, -1));
|
||||||
OutlineType = new wxCheckBox(this, -1, _("&Opaque box"));
|
OutlineType = new wxCheckBox(this, -1, _("&Opaque box"));
|
||||||
ScaleX = num_text_ctrl(this, style->scalex, false);
|
ScaleX = num_text_ctrl(this, &work->scalex, false);
|
||||||
ScaleY = num_text_ctrl(this, style->scaley, false);
|
ScaleY = num_text_ctrl(this, &work->scaley, false);
|
||||||
Angle = num_text_ctrl(this, style->angle, true);
|
Angle = num_text_ctrl(this, &work->angle, true);
|
||||||
Spacing = num_text_ctrl(this, style->spacing, true);
|
Spacing = num_text_ctrl(this, &work->spacing, true);
|
||||||
Encoding = new wxComboBox(this, -1, "", wxDefaultPosition, wxDefaultSize, encodingStrings, wxCB_READONLY);
|
Encoding = new wxComboBox(this, -1, "", wxDefaultPosition, wxDefaultSize, encodingStrings, wxCB_READONLY);
|
||||||
|
|
||||||
// Set control tooltips
|
// Set control tooltips
|
||||||
|
@ -456,24 +456,16 @@ void DialogStyleEditor::Apply(bool apply, bool close) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void DialogStyleEditor::UpdateWorkStyle() {
|
void DialogStyleEditor::UpdateWorkStyle() {
|
||||||
work->font = from_wx(FontName->GetValue());
|
TransferDataFromWindow();
|
||||||
FontSize->GetValue().ToDouble(&(work->fontsize));
|
|
||||||
|
|
||||||
ScaleX->GetValue().ToDouble(&(work->scalex));
|
work->font = from_wx(FontName->GetValue());
|
||||||
ScaleY->GetValue().ToDouble(&(work->scaley));
|
|
||||||
|
|
||||||
long templ = 0;
|
long templ = 0;
|
||||||
Encoding->GetValue().BeforeFirst('-').ToLong(&templ);
|
Encoding->GetValue().BeforeFirst('-').ToLong(&templ);
|
||||||
work->encoding = templ;
|
work->encoding = templ;
|
||||||
|
|
||||||
Angle->GetValue().ToDouble(&(work->angle));
|
|
||||||
Spacing->GetValue().ToDouble(&(work->spacing));
|
|
||||||
|
|
||||||
work->borderstyle = OutlineType->IsChecked() ? 3 : 1;
|
work->borderstyle = OutlineType->IsChecked() ? 3 : 1;
|
||||||
|
|
||||||
Shadow->GetValue().ToDouble(&(work->shadow_w));
|
|
||||||
Outline->GetValue().ToDouble(&(work->outline_w));
|
|
||||||
|
|
||||||
work->alignment = ControlToAlign(Alignment->GetSelection());
|
work->alignment = ControlToAlign(Alignment->GetSelection());
|
||||||
|
|
||||||
for (size_t i = 0; i < 3; ++i)
|
for (size_t i = 0; i < 3; ++i)
|
||||||
|
|
|
@ -228,7 +228,7 @@ SubsEditBox::~SubsEditBox() {
|
||||||
}
|
}
|
||||||
|
|
||||||
wxTextCtrl *SubsEditBox::MakeMarginCtrl(wxString const& tooltip, int margin, wxString const& commit_msg) {
|
wxTextCtrl *SubsEditBox::MakeMarginCtrl(wxString const& tooltip, int margin, wxString const& commit_msg) {
|
||||||
wxTextCtrl *ctrl = new wxTextCtrl(this, -1, "", wxDefaultPosition, wxSize(40,-1), wxTE_CENTRE | wxTE_PROCESS_ENTER, NumValidator());
|
wxTextCtrl *ctrl = new wxTextCtrl(this, -1, "", wxDefaultPosition, wxSize(40,-1), wxTE_CENTRE | wxTE_PROCESS_ENTER, IntValidator());
|
||||||
ctrl->SetMaxLength(4);
|
ctrl->SetMaxLength(4);
|
||||||
ctrl->SetToolTip(tooltip);
|
ctrl->SetToolTip(tooltip);
|
||||||
middle_left_sizer->Add(ctrl, wxSizerFlags().Center());
|
middle_left_sizer->Add(ctrl, wxSizerFlags().Center());
|
||||||
|
|
|
@ -1,37 +1,19 @@
|
||||||
// Copyright (c) 2005, Rodrigo Braz Monteiro
|
// Copyright (c) 2013, Thomas Goyne <plorkyeran@aegisub.org>
|
||||||
// All rights reserved.
|
|
||||||
//
|
//
|
||||||
// Redistribution and use in source and binary forms, with or without
|
// Permission to use, copy, modify, and distribute this software for any
|
||||||
// modification, are permitted provided that the following conditions are met:
|
// purpose with or without fee is hereby granted, provided that the above
|
||||||
|
// copyright notice and this permission notice appear in all copies.
|
||||||
//
|
//
|
||||||
// * Redistributions of source code must retain the above copyright notice,
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
// this list of conditions and the following disclaimer.
|
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
// and/or other materials provided with the distribution.
|
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
// may be used to endorse or promote products derived from this software
|
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
// without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
// POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
//
|
//
|
||||||
// Aegisub Project http://www.aegisub.org/
|
// Aegisub Project http://www.aegisub.org/
|
||||||
|
|
||||||
/// @file validators.cpp
|
|
||||||
/// @brief Various validators for wx
|
|
||||||
/// @ingroup custom_control utility
|
|
||||||
///
|
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include "validators.h"
|
#include "validators.h"
|
||||||
|
@ -40,171 +22,142 @@
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
#include <libaegisub/exception.h>
|
#include <libaegisub/exception.h>
|
||||||
|
#include <libaegisub/util.h>
|
||||||
|
|
||||||
#include <wx/combobox.h>
|
#include <wx/combobox.h>
|
||||||
#include <wx/textctrl.h>
|
#include <wx/textctrl.h>
|
||||||
|
|
||||||
NumValidator::NumValidator(wxString val, bool isfloat, bool issigned)
|
namespace {
|
||||||
: fValue(0)
|
std::string new_value(wxTextCtrl *ctrl, int chr) {
|
||||||
, iValue(0)
|
long from, to;
|
||||||
, isFloat(isfloat)
|
ctrl->GetSelection(&from, &to);
|
||||||
, isSigned(issigned)
|
auto value = ctrl->GetValue();
|
||||||
|
return from_wx(value.substr(0, from) + (wxChar)chr + value.substr(to));
|
||||||
|
}
|
||||||
|
|
||||||
|
wxChar decimal_separator() {
|
||||||
|
auto sep = wxLocale::GetInfo(wxLOCALE_DECIMAL_POINT, wxLOCALE_CAT_NUMBER);
|
||||||
|
return sep.empty() ? '.' : sep[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IntValidator::IntValidator(std::string const& initial)
|
||||||
|
: allow_negative(false)
|
||||||
{
|
{
|
||||||
if (isFloat) {
|
agi::util::try_parse(initial, &value);
|
||||||
val.ToDouble(&fValue);
|
Bind(wxEVT_CHAR, &IntValidator::OnChar, this);
|
||||||
}
|
|
||||||
else {
|
|
||||||
long tLong = 0;
|
|
||||||
val.ToLong(&tLong);
|
|
||||||
iValue = tLong;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NumValidator::NumValidator(int val, bool issigned)
|
IntValidator::IntValidator(int val, bool allow_negative)
|
||||||
: fValue(0)
|
: value(val)
|
||||||
, iValue(val)
|
, allow_negative(allow_negative)
|
||||||
, isFloat(false)
|
|
||||||
, isSigned(issigned)
|
|
||||||
{
|
{
|
||||||
|
Bind(wxEVT_CHAR, &IntValidator::OnChar, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
NumValidator::NumValidator(int64_t val, bool issigned)
|
IntValidator::IntValidator(IntValidator const& rgt)
|
||||||
: fValue(0)
|
: allow_negative(rgt.allow_negative)
|
||||||
, iValue((int)val)
|
|
||||||
, isFloat(false)
|
|
||||||
, isSigned(issigned)
|
|
||||||
{
|
{
|
||||||
|
SetWindow(rgt.GetWindow());
|
||||||
|
Bind(wxEVT_CHAR, &IntValidator::OnChar, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
NumValidator::NumValidator(double val, bool issigned)
|
bool IntValidator::TransferToWindow() {
|
||||||
: fValue(val)
|
static_cast<wxTextCtrl *>(GetWindow())->SetValue(std::to_wstring(value));
|
||||||
, iValue(0)
|
|
||||||
, isFloat(true)
|
|
||||||
, isSigned(issigned)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
NumValidator::NumValidator(const NumValidator &from)
|
|
||||||
: wxValidator()
|
|
||||||
, fValue(from.fValue)
|
|
||||||
, iValue(from.iValue)
|
|
||||||
, isFloat(from.isFloat)
|
|
||||||
, isSigned(from.isSigned)
|
|
||||||
{
|
|
||||||
SetWindow(from.GetWindow());
|
|
||||||
}
|
|
||||||
|
|
||||||
BEGIN_EVENT_TABLE(NumValidator, wxValidator)
|
|
||||||
EVT_CHAR(NumValidator::OnChar)
|
|
||||||
END_EVENT_TABLE()
|
|
||||||
|
|
||||||
wxObject* NumValidator::Clone() const {
|
|
||||||
return new NumValidator(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool NumValidator::Validate(wxWindow*) {
|
|
||||||
wxTextCtrl *ctrl = (wxTextCtrl*) GetWindow();
|
|
||||||
wxString value = ctrl->GetValue();
|
|
||||||
|
|
||||||
if (!ctrl->IsEnabled()) return true;
|
|
||||||
|
|
||||||
if (value.Length() < 1) return false;
|
|
||||||
|
|
||||||
bool gotDecimal = false;
|
|
||||||
for (size_t i = 0; i < value.Length(); i++) {
|
|
||||||
if (!CheckCharacter(value[i], !i, true, gotDecimal))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NumValidator::CheckCharacter(int chr, bool isFirst, bool canSign, bool &gotDecimal) {
|
void IntValidator::OnChar(wxKeyEvent& event) {
|
||||||
// Check sign
|
|
||||||
if (chr == '-' || chr == '+') {
|
|
||||||
return isFirst && canSign && isSigned;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't allow anything before a sign
|
|
||||||
if (isFirst && !canSign) return false;
|
|
||||||
|
|
||||||
// Check decimal point
|
|
||||||
if (chr == '.' || chr == ',') {
|
|
||||||
if (!isFloat || gotDecimal)
|
|
||||||
return false;
|
|
||||||
else {
|
|
||||||
gotDecimal = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check digit
|
|
||||||
return chr >= '0' && chr <= '9';
|
|
||||||
}
|
|
||||||
|
|
||||||
void NumValidator::OnChar(wxKeyEvent& event) {
|
|
||||||
wxTextCtrl *ctrl = (wxTextCtrl*) GetWindow();
|
|
||||||
wxString value = ctrl->GetValue();
|
|
||||||
int chr = event.GetKeyCode();
|
int chr = event.GetKeyCode();
|
||||||
|
|
||||||
// Special keys
|
|
||||||
if (chr < WXK_SPACE || chr == WXK_DELETE || chr > WXK_START) {
|
if (chr < WXK_SPACE || chr == WXK_DELETE || chr > WXK_START) {
|
||||||
event.Skip();
|
event.Skip();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get selection
|
auto ctrl = static_cast<wxTextCtrl *>(GetWindow());
|
||||||
long from,to;
|
auto str = new_value(ctrl, chr);
|
||||||
ctrl->GetSelection(&from,&to);
|
int parsed;
|
||||||
|
if (allow_negative && str == '-')
|
||||||
// Count decimal points and signs outside selection
|
|
||||||
int decimals = 0;
|
|
||||||
int signs = 0;
|
|
||||||
wxChar curchr;
|
|
||||||
for (size_t i=0;i<value.Length();i++) {
|
|
||||||
if (i >= (unsigned)from && i < (unsigned)to) continue;
|
|
||||||
curchr = value[i];
|
|
||||||
if (curchr == '.' || curchr == ',') decimals++;
|
|
||||||
if (curchr == '+' || curchr == '-') signs++;
|
|
||||||
}
|
|
||||||
bool gotDecimal = decimals > 0;
|
|
||||||
|
|
||||||
// Check character
|
|
||||||
if (!CheckCharacter(chr,!from,!signs,gotDecimal)) {
|
|
||||||
if (!wxValidator::IsSilent()) wxBell();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// OK
|
|
||||||
event.Skip();
|
event.Skip();
|
||||||
return;
|
else if (agi::util::try_parse(str, &parsed) && (allow_negative || parsed >= 0))
|
||||||
|
event.Skip();
|
||||||
|
else if (!wxValidator::IsSilent())
|
||||||
|
wxBell();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NumValidator::TransferToWindow() {
|
DoubleValidator::DoubleValidator(double *val, bool allow_negative)
|
||||||
wxTextCtrl *ctrl = (wxTextCtrl*) GetWindow();
|
: value(val)
|
||||||
if (isFloat)
|
, min(allow_negative ? std::numeric_limits<double>::lowest() : 0)
|
||||||
ctrl->SetValue(wxString::Format("%g",fValue));
|
, max(std::numeric_limits<double>::max())
|
||||||
else
|
, decimal_sep(decimal_separator())
|
||||||
ctrl->SetValue(std::to_wstring(iValue));
|
{
|
||||||
|
Bind(wxEVT_CHAR, &DoubleValidator::OnChar, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
DoubleValidator::DoubleValidator(double *val, double min, double max)
|
||||||
|
: value(val)
|
||||||
|
, min(min)
|
||||||
|
, max(max)
|
||||||
|
, decimal_sep(decimal_separator())
|
||||||
|
{
|
||||||
|
Bind(wxEVT_CHAR, &DoubleValidator::OnChar, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
DoubleValidator::DoubleValidator(DoubleValidator const& rgt)
|
||||||
|
: value(rgt.value)
|
||||||
|
, min(rgt.min)
|
||||||
|
, max(rgt.max)
|
||||||
|
, decimal_sep(rgt.decimal_sep)
|
||||||
|
{
|
||||||
|
Bind(wxEVT_CHAR, &DoubleValidator::OnChar, this);
|
||||||
|
SetWindow(rgt.GetWindow());
|
||||||
|
}
|
||||||
|
|
||||||
|
void DoubleValidator::OnChar(wxKeyEvent& event) {
|
||||||
|
int chr = event.GetKeyCode();
|
||||||
|
if (chr < WXK_SPACE || chr == WXK_DELETE || chr > WXK_START) {
|
||||||
|
event.Skip();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chr == decimal_sep)
|
||||||
|
chr = '.';
|
||||||
|
|
||||||
|
auto str = new_value(static_cast<wxTextCtrl *>(GetWindow()), chr);
|
||||||
|
if (decimal_sep != '.')
|
||||||
|
replace(begin(str), end(str), (char)decimal_sep, '.');
|
||||||
|
|
||||||
|
double parsed;
|
||||||
|
bool can_parse = agi::util::try_parse(str, &parsed);
|
||||||
|
if ((min < 0 && str == '-') || str == '.')
|
||||||
|
event.Skip();
|
||||||
|
else if (can_parse && parsed >= min && parsed <= max)
|
||||||
|
event.Skip();
|
||||||
|
else if (can_parse && min < 0 && chr == '-') // allow negating an existing value even if it results in being out of range
|
||||||
|
event.Skip();
|
||||||
|
else if (!wxValidator::IsSilent())
|
||||||
|
wxBell();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DoubleValidator::TransferToWindow() {
|
||||||
|
auto str = wxString::Format("%g", *value);
|
||||||
|
if (decimal_sep != '.')
|
||||||
|
std::replace(str.begin(), str.end(), wxS('.'), decimal_sep);
|
||||||
|
if (str.find(decimal_sep) != str.npos) {
|
||||||
|
while (str.Last() == '0')
|
||||||
|
str.RemoveLast();
|
||||||
|
}
|
||||||
|
static_cast<wxTextCtrl *>(GetWindow())->SetValue(str);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NumValidator::TransferFromWindow() {
|
bool DoubleValidator::TransferFromWindow() {
|
||||||
wxTextCtrl *ctrl = (wxTextCtrl*) GetWindow();
|
auto ctrl = static_cast<wxTextCtrl *>(GetWindow());
|
||||||
wxString value = ctrl->GetValue();
|
|
||||||
|
|
||||||
if (!Validate(ctrl)) return false;
|
if (!Validate(ctrl)) return false;
|
||||||
|
auto str = from_wx(ctrl->GetValue());
|
||||||
// Transfer
|
if (decimal_sep != '.')
|
||||||
if (isFloat) {
|
replace(begin(str), end(str), (char)decimal_sep, '.');
|
||||||
value.ToDouble(&fValue);
|
agi::util::try_parse(str, value);
|
||||||
}
|
|
||||||
else {
|
|
||||||
long tLong;
|
|
||||||
value.ToLong(&tLong);
|
|
||||||
iValue = tLong;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,37 +1,19 @@
|
||||||
// Copyright (c) 2005, Rodrigo Braz Monteiro
|
// Copyright (c) 2013, Thomas Goyne <plorkyeran@aegisub.org>
|
||||||
// All rights reserved.
|
|
||||||
//
|
//
|
||||||
// Redistribution and use in source and binary forms, with or without
|
// Permission to use, copy, modify, and distribute this software for any
|
||||||
// modification, are permitted provided that the following conditions are met:
|
// purpose with or without fee is hereby granted, provided that the above
|
||||||
|
// copyright notice and this permission notice appear in all copies.
|
||||||
//
|
//
|
||||||
// * Redistributions of source code must retain the above copyright notice,
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
// this list of conditions and the following disclaimer.
|
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
// and/or other materials provided with the distribution.
|
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
// may be used to endorse or promote products derived from this software
|
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
// without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
// POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
//
|
//
|
||||||
// Aegisub Project http://www.aegisub.org/
|
// Aegisub Project http://www.aegisub.org/
|
||||||
|
|
||||||
/// @file validators.h
|
|
||||||
/// @see validators.cpp
|
|
||||||
/// @ingroup custom_control utility
|
|
||||||
///
|
|
||||||
|
|
||||||
#include <libaegisub/exception.h>
|
#include <libaegisub/exception.h>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
@ -39,59 +21,44 @@
|
||||||
#include <wx/radiobox.h>
|
#include <wx/radiobox.h>
|
||||||
#include <wx/validate.h>
|
#include <wx/validate.h>
|
||||||
|
|
||||||
/// A wx validator that only allows valid numbers
|
class IntValidator : public wxValidator {
|
||||||
class NumValidator : public wxValidator {
|
int value;
|
||||||
double fValue; ///< Value if isFloat is true
|
bool allow_negative;
|
||||||
int iValue; ///< Value if isFloat is false
|
|
||||||
bool isFloat; ///< Should decimals be allowed?
|
|
||||||
bool isSigned; ///< Can the number be negative?
|
|
||||||
|
|
||||||
/// Polymorphic copy
|
bool CheckCharacter(int chr, bool is_first) const;
|
||||||
wxObject* Clone() const;
|
|
||||||
/// Check if the value in the passed window is valid
|
|
||||||
bool Validate(wxWindow* parent);
|
|
||||||
/// Copy the currently stored value to the associated window
|
|
||||||
bool TransferToWindow();
|
|
||||||
/// Read the value in the associated window and validate it
|
|
||||||
bool TransferFromWindow();
|
|
||||||
|
|
||||||
/// Check a single character
|
|
||||||
/// @param chr Character to check
|
|
||||||
/// @param isFirst Is this the first character in the string?
|
|
||||||
/// @param canSign Can this character be a sign?
|
|
||||||
/// @param gotDecimal[in,out] Has a decimal been found? Set to true if a chr is a decimal
|
|
||||||
/// @return Is this character valid?
|
|
||||||
bool CheckCharacter(int chr,bool isFirst,bool canSign,bool &gotDecimal);
|
|
||||||
|
|
||||||
/// wx character event handler
|
|
||||||
void OnChar(wxKeyEvent& event);
|
void OnChar(wxKeyEvent& event);
|
||||||
|
|
||||||
|
bool Validate(wxWindow *) override { return true; }
|
||||||
|
wxObject* Clone() const override { return new IntValidator(*this); }
|
||||||
|
bool TransferToWindow() override;
|
||||||
|
bool TransferFromWindow() override { return true; }
|
||||||
|
|
||||||
|
IntValidator(IntValidator const& rgt);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// Constructor
|
explicit IntValidator(std::string const& value = "");
|
||||||
/// @param val Initial value to set the associated control to
|
explicit IntValidator(int val, bool allow_negative=false);
|
||||||
/// @param isfloat Allow floats, or just ints?
|
};
|
||||||
/// @param issigned Allow negative numbers?
|
|
||||||
explicit NumValidator(wxString val = wxString(), bool isfloat=false, bool issigned=false);
|
|
||||||
|
|
||||||
/// Constructor
|
class DoubleValidator : public wxValidator {
|
||||||
/// @param val Initial value to set the associated control to
|
double *value;
|
||||||
/// @param issigned Allow negative numbers?
|
double min;
|
||||||
explicit NumValidator(int val, bool issigned=false);
|
double max;
|
||||||
|
wxChar decimal_sep;
|
||||||
|
|
||||||
/// Constructor
|
bool Validate(wxWindow* parent) override { return true; }
|
||||||
/// @param val Initial value to set the associated control to
|
bool CheckCharacter(int chr, bool is_first, bool *got_decimal) const;
|
||||||
/// @param issigned Allow negative numbers?
|
void OnChar(wxKeyEvent& event);
|
||||||
explicit NumValidator(int64_t val, bool issigned=false);
|
|
||||||
|
|
||||||
/// Constructor
|
DoubleValidator(DoubleValidator const& rgt);
|
||||||
/// @param val Initial value to set the associated control to
|
|
||||||
/// @param issigned Allow negative numbers?
|
|
||||||
explicit NumValidator(double val, bool issigned=false);
|
|
||||||
|
|
||||||
/// Copy constructor
|
wxObject* Clone() const override { return new DoubleValidator(*this); }
|
||||||
NumValidator(const NumValidator& from);
|
bool TransferToWindow() override;
|
||||||
|
bool TransferFromWindow() override;
|
||||||
|
|
||||||
DECLARE_EVENT_TABLE()
|
public:
|
||||||
|
explicit DoubleValidator(double *val, bool allow_negative=false);
|
||||||
|
explicit DoubleValidator(double *val, double min, double max);
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
|
Loading…
Reference in a new issue