Aegisub/src/validators.cpp

211 lines
5.9 KiB
C++

// Copyright (c) 2013, Thomas Goyne <plorkyeran@aegisub.org>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
// Aegisub Project http://www.aegisub.org/
#include "validators.h"
#include "compat.h"
#include <libaegisub/exception.h>
#include <libaegisub/util.h>
#include <wx/combobox.h>
#include <wx/spinctrl.h>
#include <wx/textctrl.h>
namespace {
std::string new_value(wxTextCtrl *ctrl, int chr) {
long from, to;
ctrl->GetSelection(&from, &to);
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(int val, bool allow_negative)
: value(val)
, allow_negative(allow_negative)
{
Bind(wxEVT_CHAR, &IntValidator::OnChar, this);
}
IntValidator::IntValidator(IntValidator const& rgt)
: wxValidator(rgt)
, value(rgt.value)
, allow_negative(rgt.allow_negative)
{
SetWindow(rgt.GetWindow());
Bind(wxEVT_CHAR, &IntValidator::OnChar, this);
}
bool IntValidator::TransferToWindow() {
static_cast<wxTextCtrl *>(GetWindow())->SetValue(std::to_wstring(value));
return true;
}
void IntValidator::OnChar(wxKeyEvent& event) {
int chr = event.GetKeyCode();
if (chr < WXK_SPACE || chr == WXK_DELETE || chr > WXK_START) {
event.Skip();
return;
}
auto ctrl = static_cast<wxTextCtrl *>(GetWindow());
auto str = new_value(ctrl, chr);
int parsed;
if (allow_negative && str == "-")
event.Skip();
else if (agi::util::try_parse(str, &parsed) && (allow_negative || parsed >= 0))
event.Skip();
else if (!wxValidator::IsSilent())
wxBell();
}
DoubleValidator::DoubleValidator(double *val, bool allow_negative)
: value(val)
, min(allow_negative ? std::numeric_limits<double>::lowest() : 0)
, max(std::numeric_limits<double>::max())
, decimal_sep(decimal_separator())
{
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)
: wxValidator(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 = std::to_wstring(*value);
if (decimal_sep != '.')
std::replace(str.begin(), str.end(), L'.', decimal_sep);
if (str.find(decimal_sep) != str.npos) {
while (str.back() == '0')
str.pop_back();
}
static_cast<wxTextCtrl *>(GetWindow())->SetValue(str);
return true;
}
bool DoubleValidator::TransferFromWindow() {
auto ctrl = static_cast<wxTextCtrl *>(GetWindow());
if (!Validate(ctrl)) return false;
auto str = from_wx(ctrl->GetValue());
if (decimal_sep != '.')
replace(begin(str), end(str), (char)decimal_sep, '.');
agi::util::try_parse(str, value);
return true;
}
bool DoubleSpinValidator::TransferToWindow() {
static_cast<wxSpinCtrlDouble*>(GetWindow())->SetValue(*value);
return true;
}
bool DoubleSpinValidator::TransferFromWindow() {
auto ctrl = static_cast<wxSpinCtrlDouble*>(GetWindow());
#ifndef wxHAS_NATIVE_SPINCTRLDOUBLE
wxFocusEvent evt;
ctrl->OnTextLostFocus(evt);
#endif
*value = ctrl->GetValue();
return true;
}
int EnumBinderBase::Get() {
if (auto rb = dynamic_cast<wxRadioBox*>(GetWindow()))
return rb->GetSelection();
if (auto rb = dynamic_cast<wxComboBox*>(GetWindow()))
return rb->GetSelection();
throw agi::InternalError("Control type not supported by EnumBinder");
}
void EnumBinderBase::Set(int value) {
if (auto rb = dynamic_cast<wxRadioBox*>(GetWindow()))
rb->SetSelection(value);
else if (auto rb = dynamic_cast<wxComboBox*>(GetWindow()))
rb->SetSelection(value);
else
throw agi::InternalError("Control type not supported by EnumBinder");
}
bool StringBinder::TransferFromWindow() {
wxWindow *window = GetWindow();
if (wxTextCtrl *ctrl = dynamic_cast<wxTextCtrl*>(window))
*value = from_wx(ctrl->GetValue());
else if (wxComboBox *ctrl = dynamic_cast<wxComboBox*>(window))
*value = from_wx(ctrl->GetValue());
else
throw agi::InternalError("Unsupported control type");
return true;
}
bool StringBinder::TransferToWindow() {
wxWindow *window = GetWindow();
if (wxTextCtrl *ctrl = dynamic_cast<wxTextCtrl*>(window))
ctrl->SetValue(to_wx(*value));
else if (wxComboBox *ctrl = dynamic_cast<wxComboBox*>(window))
ctrl->SetValue(to_wx(*value));
else
throw agi::InternalError("Unsupported control type");
return true;
}