Prevent GUI from crashing when invalid values were written in numeric fields. Includes basic validation. #1709

This commit is contained in:
Alessandro Ranellucci 2014-03-24 01:07:30 +01:00
parent 67f3e9962b
commit 7a58457add
7 changed files with 132 additions and 71 deletions

View File

@ -3,7 +3,7 @@ use Moo;
use List::Util qw(first); use List::Util qw(first);
use Wx qw(:combobox :font :misc :sizer :systemsettings :textctrl); use Wx qw(:combobox :font :misc :sizer :systemsettings :textctrl);
use Wx::Event qw(EVT_CHECKBOX EVT_COMBOBOX EVT_SPINCTRL EVT_TEXT); use Wx::Event qw(EVT_CHECKBOX EVT_COMBOBOX EVT_SPINCTRL EVT_TEXT EVT_KILL_FOCUS);
=head1 NAME =head1 NAME
@ -52,6 +52,7 @@ has 'label_width' => (is => 'ro', default => sub { 180 });
has 'extra_column' => (is => 'ro'); has 'extra_column' => (is => 'ro');
has 'label_font' => (is => 'ro'); has 'label_font' => (is => 'ro');
has 'sidetext_font' => (is => 'ro', default => sub { Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT) }); has 'sidetext_font' => (is => 'ro', default => sub { Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT) });
has 'ignore_on_change_return' => (is => 'ro', default => sub { 1 });
has 'sizer' => (is => 'rw'); has 'sizer' => (is => 'rw');
has '_triggers' => (is => 'ro', default => sub { {} }); has '_triggers' => (is => 'ro', default => sub { {} });
@ -166,7 +167,7 @@ sub _build_field {
my ($opt) = @_; my ($opt) = @_;
my $opt_key = $opt->{opt_key}; my $opt_key = $opt->{opt_key};
$self->_triggers->{$opt_key} = $opt->{on_change} || sub {}; $self->_triggers->{$opt_key} = $opt->{on_change} || sub { return 1 };
my $field; my $field;
my $tooltip = $opt->{tooltip}; my $tooltip = $opt->{tooltip};
@ -176,11 +177,16 @@ sub _build_field {
# default width on Windows is too large # default width on Windows is too large
my $size = Wx::Size->new($opt->{width} || 60, $opt->{height} || -1); my $size = Wx::Size->new($opt->{width} || 60, $opt->{height} || -1);
my $on_change = sub { $self->_on_change($opt_key, $field->GetValue) }; my $on_change = sub {
my $value = $field->GetValue;
$value ||= 0 if $opt->{type} =~ /^(i|f|percent)$/; # prevent crash trying to pass empty strings to Config
$self->_on_change($opt_key, $value);
};
if ($opt->{type} eq 'i') { if ($opt->{type} eq 'i') {
$field = Wx::SpinCtrl->new($self->parent, -1, $opt->{default}, wxDefaultPosition, $size, $style, $opt->{min} || 0, $opt->{max} || 2147483647, $opt->{default}); $field = Wx::SpinCtrl->new($self->parent, -1, $opt->{default}, wxDefaultPosition, $size, $style, $opt->{min} || 0, $opt->{max} || 2147483647, $opt->{default});
$self->_setters->{$opt_key} = sub { $field->SetValue($_[0]) }; $self->_setters->{$opt_key} = sub { $field->SetValue($_[0]) };
EVT_SPINCTRL ($self->parent, $field, $on_change); EVT_SPINCTRL ($self->parent, $field, $on_change);
EVT_KILL_FOCUS($field, sub { $self->on_kill_focus($opt_key) });
} elsif ($opt->{values}) { } elsif ($opt->{values}) {
$field = Wx::ComboBox->new($self->parent, -1, $opt->{default}, wxDefaultPosition, $size, $opt->{labels} || $opt->{values}); $field = Wx::ComboBox->new($self->parent, -1, $opt->{default}, wxDefaultPosition, $size, $opt->{labels} || $opt->{values});
$self->_setters->{$opt_key} = sub { $self->_setters->{$opt_key} = sub {
@ -191,10 +197,12 @@ sub _build_field {
$self->_on_change($opt_key, $on_change); $self->_on_change($opt_key, $on_change);
}); });
EVT_TEXT($self->parent, $field, $on_change); EVT_TEXT($self->parent, $field, $on_change);
EVT_KILL_FOCUS($field, sub { $self->on_kill_focus($opt_key) });
} else { } else {
$field = Wx::TextCtrl->new($self->parent, -1, $opt->{default}, wxDefaultPosition, $size, $style); $field = Wx::TextCtrl->new($self->parent, -1, $opt->{default}, wxDefaultPosition, $size, $style);
$self->_setters->{$opt_key} = sub { $field->ChangeValue($_[0]) }; $self->_setters->{$opt_key} = sub { $field->ChangeValue($_[0]) };
EVT_TEXT($self->parent, $field, $on_change); EVT_TEXT($self->parent, $field, $on_change);
EVT_KILL_FOCUS($field, sub { $self->on_kill_focus($opt_key) });
} }
$field->Disable if $opt->{readonly}; $field->Disable if $opt->{readonly};
$tooltip .= " (default: " . $opt->{default} . ")" if ($opt->{default}); $tooltip .= " (default: " . $opt->{default} . ")" if ($opt->{default});
@ -220,8 +228,10 @@ sub _build_field {
$tooltip . " (default: " . join(",", @{$opt->{default}}) . ")" $tooltip . " (default: " . join(",", @{$opt->{default}}) . ")"
) for @items; ) for @items;
} }
EVT_TEXT($self->parent, $_, sub { $self->_on_change($opt_key, [ $x_field->GetValue, $y_field->GetValue ]) }) foreach my $field ($x_field, $y_field) {
for $x_field, $y_field; EVT_TEXT($self->parent, $field, sub { $self->_on_change($opt_key, [ $x_field->GetValue, $y_field->GetValue ]) });
EVT_KILL_FOCUS($field, sub { $self->on_kill_focus($opt_key) });
}
$self->_setters->{$opt_key} = sub { $self->_setters->{$opt_key} = sub {
$x_field->SetValue($_[0][0]); $x_field->SetValue($_[0][0]);
$y_field->SetValue($_[0][1]); $y_field->SetValue($_[0][1]);
@ -260,7 +270,7 @@ sub _on_change {
my ($opt_key, $value) = @_; my ($opt_key, $value) = @_;
return if $self->sizer->GetStaticBox->GetParent->{disabled}; return if $self->sizer->GetStaticBox->GetParent->{disabled};
$self->_triggers->{$opt_key}->($value); $self->_triggers->{$opt_key}->($value) or $self->ignore_on_change_return or return;
$self->on_change->($opt_key, $value); $self->on_change->($opt_key, $value);
} }
@ -285,6 +295,8 @@ sub set_value {
return 0; return 0;
} }
sub on_kill_focus {}
package Slic3r::GUI::ConfigOptionsGroup; package Slic3r::GUI::ConfigOptionsGroup;
use Moo; use Moo;
@ -313,6 +325,7 @@ use List::Util qw(first);
has 'config' => (is => 'ro', required => 1); has 'config' => (is => 'ro', required => 1);
has 'full_labels' => (is => 'ro', default => sub {0}); has 'full_labels' => (is => 'ro', default => sub {0});
has '+ignore_on_change_return' => (is => 'ro', default => sub { 0 });
sub _trigger_options { sub _trigger_options {
my $self = shift; my $self = shift;
@ -323,13 +336,17 @@ sub _trigger_options {
my $full_key = $opt; my $full_key = $opt;
my ($opt_key, $index) = $self->_split_key($full_key); my ($opt_key, $index) = $self->_split_key($full_key);
my $config_opt = $Slic3r::Config::Options->{$opt_key}; my $config_opt = $Slic3r::Config::Options->{$opt_key};
my $default = $config_opt->{default};
$default = $default->[$index] if defined($index) && $index <= $#$default;
$opt = { $opt = {
opt_key => $full_key, opt_key => $full_key,
config => 1, config => 1,
label => ($self->full_labels && defined $config_opt->{full_label}) ? $config_opt->{full_label} : $config_opt->{label}, label => ($self->full_labels && defined $config_opt->{full_label}) ? $config_opt->{full_label} : $config_opt->{label},
(map { $_ => $config_opt->{$_} } qw(type tooltip sidetext width height full_width min max labels values multiline readonly)), (map { $_ => $config_opt->{$_} } qw(type tooltip sidetext width height full_width min max labels values multiline readonly)),
default => $self->_get_config($opt_key, $index), default => $default,
on_change => sub { $self->_set_config($opt_key, $index, $_[0]) }, on_change => sub { return $self->_set_config($opt_key, $index, $_[0]) },
}; };
} }
$opt; $opt;
@ -367,6 +384,16 @@ sub set_value {
return $changed; return $changed;
} }
sub on_kill_focus {
my ($self, $full_key) = @_;
# when a field loses focus, reapply the config value to it
# (thus discarding any invalid input and reverting to the last
# accepted value)
my ($key, $index) = $self->_split_key($full_key);
$self->SUPER::set_value($full_key, $self->_get_config($key, $index));
}
sub _split_key { sub _split_key {
my $self = shift; my $self = shift;
my ($opt_key) = @_; my ($opt_key) = @_;
@ -378,10 +405,10 @@ sub _split_key {
sub _get_config { sub _get_config {
my $self = shift; my $self = shift;
my ($opt_key, $index) = @_; my ($opt_key, $index, $config) = @_;
my ($get_m, $serialized) = $self->_config_methods($opt_key, $index); my ($get_m, $serialized) = $self->_config_methods($opt_key, $index);
my $value = $self->config->$get_m($opt_key); my $value = ($config // $self->config)->$get_m($opt_key);
if (defined $index) { if (defined $index) {
$value->[$index] //= $value->[0]; #/ $value->[$index] //= $value->[0]; #/
$value = $value->[$index]; $value = $value->[$index];
@ -400,9 +427,9 @@ sub _set_config {
$self->config->set($opt_key, $values); $self->config->set($opt_key, $values);
} else { } else {
if ($serialized) { if ($serialized) {
$self->config->set_deserialize($opt_key, $value); return $self->config->set_deserialize($opt_key, $value);
} else { } else {
$self->config->set($opt_key, $value); return $self->config->set($opt_key, $value);
} }
} }
} }

View File

@ -33,19 +33,20 @@ ConfigBase::serialize(const t_config_option_key opt_key) {
return opt->serialize(); return opt->serialize();
} }
void bool
ConfigBase::set_deserialize(const t_config_option_key opt_key, std::string str) { ConfigBase::set_deserialize(const t_config_option_key opt_key, std::string str) {
if (this->def->count(opt_key) == 0) throw "Calling set_deserialize() on unknown option"; if (this->def->count(opt_key) == 0) throw "Calling set_deserialize() on unknown option";
ConfigOptionDef* optdef = &(*this->def)[opt_key]; ConfigOptionDef* optdef = &(*this->def)[opt_key];
if (!optdef->shortcut.empty()) { if (!optdef->shortcut.empty()) {
for (std::vector<t_config_option_key>::iterator it = optdef->shortcut.begin(); it != optdef->shortcut.end(); ++it) for (std::vector<t_config_option_key>::iterator it = optdef->shortcut.begin(); it != optdef->shortcut.end(); ++it) {
this->set_deserialize(*it, str); if (!this->set_deserialize(*it, str)) return false;
return; }
return true;
} }
ConfigOption* opt = this->option(opt_key, true); ConfigOption* opt = this->option(opt_key, true);
assert(opt != NULL); assert(opt != NULL);
opt->deserialize(str); return opt->deserialize(str);
} }
double double
@ -164,31 +165,37 @@ ConfigBase::get_at(t_config_option_key opt_key, size_t i) {
} }
} }
void bool
ConfigBase::set(t_config_option_key opt_key, SV* value) { ConfigBase::set(t_config_option_key opt_key, SV* value) {
ConfigOption* opt = this->option(opt_key, true); ConfigOption* opt = this->option(opt_key, true);
if (opt == NULL) CONFESS("Trying to set non-existing option"); if (opt == NULL) CONFESS("Trying to set non-existing option");
if (ConfigOptionFloat* optv = dynamic_cast<ConfigOptionFloat*>(opt)) { if (ConfigOptionFloat* optv = dynamic_cast<ConfigOptionFloat*>(opt)) {
if (!looks_like_number(value)) return false;
optv->value = SvNV(value); optv->value = SvNV(value);
} else if (ConfigOptionFloats* optv = dynamic_cast<ConfigOptionFloats*>(opt)) { } else if (ConfigOptionFloats* optv = dynamic_cast<ConfigOptionFloats*>(opt)) {
optv->values.clear(); std::vector<double> values;
AV* av = (AV*)SvRV(value); AV* av = (AV*)SvRV(value);
const size_t len = av_len(av)+1; const size_t len = av_len(av)+1;
for (size_t i = 0; i < len; i++) { for (size_t i = 0; i < len; i++) {
SV** elem = av_fetch(av, i, 0); SV** elem = av_fetch(av, i, 0);
optv->values.push_back(SvNV(*elem)); if (!looks_like_number(*elem)) return false;
values.push_back(SvNV(*elem));
} }
optv->values = values;
} else if (ConfigOptionInt* optv = dynamic_cast<ConfigOptionInt*>(opt)) { } else if (ConfigOptionInt* optv = dynamic_cast<ConfigOptionInt*>(opt)) {
if (!looks_like_number(value)) return false;
optv->value = SvIV(value); optv->value = SvIV(value);
} else if (ConfigOptionInts* optv = dynamic_cast<ConfigOptionInts*>(opt)) { } else if (ConfigOptionInts* optv = dynamic_cast<ConfigOptionInts*>(opt)) {
optv->values.clear(); std::vector<int> values;
AV* av = (AV*)SvRV(value); AV* av = (AV*)SvRV(value);
const size_t len = av_len(av)+1; const size_t len = av_len(av)+1;
for (size_t i = 0; i < len; i++) { for (size_t i = 0; i < len; i++) {
SV** elem = av_fetch(av, i, 0); SV** elem = av_fetch(av, i, 0);
optv->values.push_back(SvIV(*elem)); if (!looks_like_number(*elem)) return false;
values.push_back(SvIV(*elem));
} }
optv->values = values;
} else if (ConfigOptionString* optv = dynamic_cast<ConfigOptionString*>(opt)) { } else if (ConfigOptionString* optv = dynamic_cast<ConfigOptionString*>(opt)) {
optv->value = std::string(SvPV_nolen(value), SvCUR(value)); optv->value = std::string(SvPV_nolen(value), SvCUR(value));
} else if (ConfigOptionStrings* optv = dynamic_cast<ConfigOptionStrings*>(opt)) { } else if (ConfigOptionStrings* optv = dynamic_cast<ConfigOptionStrings*>(opt)) {
@ -200,17 +207,18 @@ ConfigBase::set(t_config_option_key opt_key, SV* value) {
optv->values.push_back(std::string(SvPV_nolen(*elem), SvCUR(*elem))); optv->values.push_back(std::string(SvPV_nolen(*elem), SvCUR(*elem)));
} }
} else if (ConfigOptionPoint* optv = dynamic_cast<ConfigOptionPoint*>(opt)) { } else if (ConfigOptionPoint* optv = dynamic_cast<ConfigOptionPoint*>(opt)) {
optv->point.from_SV(value); return optv->point.from_SV(value);
} else if (ConfigOptionPoints* optv = dynamic_cast<ConfigOptionPoints*>(opt)) { } else if (ConfigOptionPoints* optv = dynamic_cast<ConfigOptionPoints*>(opt)) {
optv->values.clear(); std::vector<Pointf> values;
AV* av = (AV*)SvRV(value); AV* av = (AV*)SvRV(value);
const size_t len = av_len(av)+1; const size_t len = av_len(av)+1;
for (size_t i = 0; i < len; i++) { for (size_t i = 0; i < len; i++) {
SV** elem = av_fetch(av, i, 0); SV** elem = av_fetch(av, i, 0);
Pointf point; Pointf point;
point.from_SV(*elem); if (!point.from_SV(*elem)) return false;
optv->values.push_back(point); values.push_back(point);
} }
optv->values = values;
} else if (ConfigOptionBool* optv = dynamic_cast<ConfigOptionBool*>(opt)) { } else if (ConfigOptionBool* optv = dynamic_cast<ConfigOptionBool*>(opt)) {
optv->value = SvTRUE(value); optv->value = SvTRUE(value);
} else if (ConfigOptionBools* optv = dynamic_cast<ConfigOptionBools*>(opt)) { } else if (ConfigOptionBools* optv = dynamic_cast<ConfigOptionBools*>(opt)) {
@ -222,8 +230,9 @@ ConfigBase::set(t_config_option_key opt_key, SV* value) {
optv->values.push_back(SvTRUE(*elem)); optv->values.push_back(SvTRUE(*elem));
} }
} else { } else {
opt->deserialize( std::string(SvPV_nolen(value)) ); if (!opt->deserialize( std::string(SvPV_nolen(value)) )) return false;
} }
return true;
} }
#endif #endif
@ -347,16 +356,17 @@ StaticConfig::option(const t_config_option_key opt_key) const
} }
#ifdef SLIC3RXS #ifdef SLIC3RXS
void bool
StaticConfig::set(t_config_option_key opt_key, SV* value) { StaticConfig::set(t_config_option_key opt_key, SV* value) {
ConfigOptionDef* optdef = &(*this->def)[opt_key]; ConfigOptionDef* optdef = &(*this->def)[opt_key];
if (!optdef->shortcut.empty()) { if (!optdef->shortcut.empty()) {
for (std::vector<t_config_option_key>::iterator it = optdef->shortcut.begin(); it != optdef->shortcut.end(); ++it) for (std::vector<t_config_option_key>::iterator it = optdef->shortcut.begin(); it != optdef->shortcut.end(); ++it) {
this->set(*it, value); if (!this->set(*it, value)) return false;
return; }
return true;
} }
static_cast<ConfigBase*>(this)->set(opt_key, value); return static_cast<ConfigBase*>(this)->set(opt_key, value);
} }
#endif #endif

View File

@ -21,7 +21,7 @@ class ConfigOption {
public: public:
virtual ~ConfigOption() {}; virtual ~ConfigOption() {};
virtual std::string serialize() const = 0; virtual std::string serialize() const = 0;
virtual void deserialize(std::string str) = 0; virtual bool deserialize(std::string str) = 0;
}; };
template <class T> template <class T>
@ -54,8 +54,9 @@ class ConfigOptionFloat : public ConfigOption
return ss.str(); return ss.str();
}; };
void deserialize(std::string str) { bool deserialize(std::string str) {
this->value = ::atof(str.c_str()); this->value = ::atof(str.c_str());
return true;
}; };
}; };
@ -72,13 +73,14 @@ class ConfigOptionFloats : public ConfigOption, public ConfigOptionVector<double
return ss.str(); return ss.str();
}; };
void deserialize(std::string str) { bool deserialize(std::string str) {
this->values.clear(); this->values.clear();
std::istringstream is(str); std::istringstream is(str);
std::string item_str; std::string item_str;
while (std::getline(is, item_str, ',')) { while (std::getline(is, item_str, ',')) {
this->values.push_back(::atof(item_str.c_str())); this->values.push_back(::atof(item_str.c_str()));
} }
return true;
}; };
}; };
@ -96,8 +98,9 @@ class ConfigOptionInt : public ConfigOption
return ss.str(); return ss.str();
}; };
void deserialize(std::string str) { bool deserialize(std::string str) {
this->value = ::atoi(str.c_str()); this->value = ::atoi(str.c_str());
return true;
}; };
}; };
@ -114,13 +117,14 @@ class ConfigOptionInts : public ConfigOption, public ConfigOptionVector<int>
return ss.str(); return ss.str();
}; };
void deserialize(std::string str) { bool deserialize(std::string str) {
this->values.clear(); this->values.clear();
std::istringstream is(str); std::istringstream is(str);
std::string item_str; std::string item_str;
while (std::getline(is, item_str, ',')) { while (std::getline(is, item_str, ',')) {
this->values.push_back(::atoi(item_str.c_str())); this->values.push_back(::atoi(item_str.c_str()));
} }
return true;
}; };
}; };
@ -145,7 +149,7 @@ class ConfigOptionString : public ConfigOption
return str; return str;
}; };
void deserialize(std::string str) { bool deserialize(std::string str) {
// s/\\n/\n/g // s/\\n/\n/g
size_t pos = 0; size_t pos = 0;
while ((pos = str.find("\\n", pos)) != std::string::npos) { while ((pos = str.find("\\n", pos)) != std::string::npos) {
@ -154,6 +158,7 @@ class ConfigOptionString : public ConfigOption
} }
this->value = str; this->value = str;
return true;
}; };
}; };
@ -171,13 +176,14 @@ class ConfigOptionStrings : public ConfigOption, public ConfigOptionVector<std::
return ss.str(); return ss.str();
}; };
void deserialize(std::string str) { bool deserialize(std::string str) {
this->values.clear(); this->values.clear();
std::istringstream is(str); std::istringstream is(str);
std::string item_str; std::string item_str;
while (std::getline(is, item_str, ';')) { while (std::getline(is, item_str, ';')) {
this->values.push_back(item_str); this->values.push_back(item_str);
} }
return true;
}; };
}; };
@ -199,9 +205,10 @@ class ConfigOptionPercent : public ConfigOption
return s; return s;
}; };
void deserialize(std::string str) { bool deserialize(std::string str) {
// don't try to parse the trailing % since it's optional // don't try to parse the trailing % since it's optional
sscanf(str.c_str(), "%lf", &this->value); int res = sscanf(str.c_str(), "%lf", &this->value);
return res == 1;
}; };
}; };
@ -228,14 +235,16 @@ class ConfigOptionFloatOrPercent : public ConfigOption
return s; return s;
}; };
void deserialize(std::string str) { bool deserialize(std::string str) {
if (str.find_first_of("%") != std::string::npos) { if (str.find_first_of("%") != std::string::npos) {
sscanf(str.c_str(), "%lf%%", &this->value); int res = sscanf(str.c_str(), "%lf%%", &this->value);
if (res == 0) return false;
this->percent = true; this->percent = true;
} else { } else {
this->value = ::atof(str.c_str()); this->value = ::atof(str.c_str());
this->percent = false; this->percent = false;
} }
return true;
}; };
}; };
@ -255,8 +264,9 @@ class ConfigOptionPoint : public ConfigOption
return ss.str(); return ss.str();
}; };
void deserialize(std::string str) { bool deserialize(std::string str) {
sscanf(str.c_str(), "%lf%*1[,x]%lf", &this->point.x, &this->point.y); int res = sscanf(str.c_str(), "%lf%*1[,x]%lf", &this->point.x, &this->point.y);
return res == 2;
}; };
}; };
@ -275,15 +285,17 @@ class ConfigOptionPoints : public ConfigOption, public ConfigOptionVector<Pointf
return ss.str(); return ss.str();
}; };
void deserialize(std::string str) { bool deserialize(std::string str) {
this->values.clear(); this->values.clear();
std::istringstream is(str); std::istringstream is(str);
std::string point_str; std::string point_str;
while (std::getline(is, point_str, ',')) { while (std::getline(is, point_str, ',')) {
Pointf point; Pointf point;
sscanf(point_str.c_str(), "%lfx%lf", &point.x, &point.y); int res = sscanf(point_str.c_str(), "%lfx%lf", &point.x, &point.y);
if (res != 2) return false;
this->values.push_back(point); this->values.push_back(point);
} }
return true;
}; };
}; };
@ -299,8 +311,9 @@ class ConfigOptionBool : public ConfigOption
return std::string(this->value ? "1" : "0"); return std::string(this->value ? "1" : "0");
}; };
void deserialize(std::string str) { bool deserialize(std::string str) {
this->value = (str.compare("1") == 0); this->value = (str.compare("1") == 0);
return true;
}; };
}; };
@ -317,13 +330,14 @@ class ConfigOptionBools : public ConfigOption, public ConfigOptionVector<bool>
return ss.str(); return ss.str();
}; };
void deserialize(std::string str) { bool deserialize(std::string str) {
this->values.clear(); this->values.clear();
std::istringstream is(str); std::istringstream is(str);
std::string item_str; std::string item_str;
while (std::getline(is, item_str, ',')) { while (std::getline(is, item_str, ',')) {
this->values.push_back(item_str.compare("1") == 0); this->values.push_back(item_str.compare("1") == 0);
} }
return true;
}; };
}; };
@ -345,10 +359,11 @@ class ConfigOptionEnum : public ConfigOption
return ""; return "";
}; };
void deserialize(std::string str) { bool deserialize(std::string str) {
t_config_enum_values enum_keys_map = ConfigOptionEnum<T>::get_enum_values(); t_config_enum_values enum_keys_map = ConfigOptionEnum<T>::get_enum_values();
assert(enum_keys_map.count(str) > 0); if (enum_keys_map.count(str) == 0) return false;
this->value = static_cast<T>(enum_keys_map[str]); this->value = static_cast<T>(enum_keys_map[str]);
return true;
}; };
static t_config_enum_values get_enum_values(); static t_config_enum_values get_enum_values();
@ -371,9 +386,10 @@ class ConfigOptionEnumGeneric : public ConfigOption
return ""; return "";
}; };
void deserialize(std::string str) { bool deserialize(std::string str) {
assert(this->keys_map->count(str) != 0); if (this->keys_map->count(str) == 0) return false;
this->value = (*this->keys_map)[str]; this->value = (*this->keys_map)[str];
return true;
}; };
}; };
@ -435,7 +451,7 @@ class ConfigBase
virtual void keys(t_config_option_keys *keys) const = 0; virtual void keys(t_config_option_keys *keys) const = 0;
void apply(const ConfigBase &other, bool ignore_nonexistent = false); void apply(const ConfigBase &other, bool ignore_nonexistent = false);
std::string serialize(const t_config_option_key opt_key); std::string serialize(const t_config_option_key opt_key);
void set_deserialize(const t_config_option_key opt_key, std::string str); bool set_deserialize(const t_config_option_key opt_key, std::string str);
double get_abs_value(const t_config_option_key opt_key); double get_abs_value(const t_config_option_key opt_key);
double get_abs_value(const t_config_option_key opt_key, double ratio_over); double get_abs_value(const t_config_option_key opt_key, double ratio_over);
@ -443,7 +459,7 @@ class ConfigBase
SV* as_hash(); SV* as_hash();
SV* get(t_config_option_key opt_key); SV* get(t_config_option_key opt_key);
SV* get_at(t_config_option_key opt_key, size_t i); SV* get_at(t_config_option_key opt_key, size_t i);
void set(t_config_option_key opt_key, SV* value); bool set(t_config_option_key opt_key, SV* value);
#endif #endif
}; };
@ -477,7 +493,7 @@ class StaticConfig : public ConfigBase
const ConfigOption* option(const t_config_option_key opt_key) const; const ConfigOption* option(const t_config_option_key opt_key) const;
#ifdef SLIC3RXS #ifdef SLIC3RXS
void set(t_config_option_key opt_key, SV* value); bool set(t_config_option_key opt_key, SV* value);
#endif #endif
}; };

View File

@ -201,12 +201,17 @@ Pointf::to_SV_pureperl() const {
return newRV_noinc((SV*)av); return newRV_noinc((SV*)av);
} }
void bool
Pointf::from_SV(SV* point_sv) Pointf::from_SV(SV* point_sv)
{ {
AV* point_av = (AV*)SvRV(point_sv); AV* point_av = (AV*)SvRV(point_sv);
this->x = SvNV(*av_fetch(point_av, 0, 0)); SV* sv_x = *av_fetch(point_av, 0, 0);
this->y = SvNV(*av_fetch(point_av, 1, 0)); SV* sv_y = *av_fetch(point_av, 1, 0);
if (!looks_like_number(sv_x) || !looks_like_number(sv_x)) return false;
this->x = SvNV(sv_x);
this->y = SvNV(sv_y);
return true;
} }
#endif #endif

View File

@ -66,7 +66,7 @@ class Pointf
void translate(double x, double y); void translate(double x, double y);
#ifdef SLIC3RXS #ifdef SLIC3RXS
void from_SV(SV* point_sv); bool from_SV(SV* point_sv);
SV* to_SV_pureperl() const; SV* to_SV_pureperl() const;
#endif #endif
}; };

View File

@ -228,6 +228,7 @@ class PrintConfigDef
Options["fan_below_layer_time"].sidetext = "approximate seconds"; Options["fan_below_layer_time"].sidetext = "approximate seconds";
Options["fan_below_layer_time"].cli = "fan-below-layer-time=i"; Options["fan_below_layer_time"].cli = "fan-below-layer-time=i";
Options["fan_below_layer_time"].width = 60; Options["fan_below_layer_time"].width = 60;
Options["fan_below_layer_time"].min = 0;
Options["fan_below_layer_time"].max = 1000; Options["fan_below_layer_time"].max = 1000;
Options["filament_diameter"].type = coFloats; Options["filament_diameter"].type = coFloats;
@ -235,6 +236,7 @@ class PrintConfigDef
Options["filament_diameter"].tooltip = "Enter your filament diameter here. Good precision is required, so use a caliper and do multiple measurements along the filament, then compute the average."; Options["filament_diameter"].tooltip = "Enter your filament diameter here. Good precision is required, so use a caliper and do multiple measurements along the filament, then compute the average.";
Options["filament_diameter"].sidetext = "mm"; Options["filament_diameter"].sidetext = "mm";
Options["filament_diameter"].cli = "filament-diameter=f@"; Options["filament_diameter"].cli = "filament-diameter=f@";
Options["filament_diameter"].min = 0;
Options["fill_angle"].type = coInt; Options["fill_angle"].type = coInt;
Options["fill_angle"].label = "Fill angle"; Options["fill_angle"].label = "Fill angle";
@ -242,6 +244,7 @@ class PrintConfigDef
Options["fill_angle"].tooltip = "Default base angle for infill orientation. Cross-hatching will be applied to this. Bridges will be infilled using the best direction Slic3r can detect, so this setting does not affect them."; Options["fill_angle"].tooltip = "Default base angle for infill orientation. Cross-hatching will be applied to this. Bridges will be infilled using the best direction Slic3r can detect, so this setting does not affect them.";
Options["fill_angle"].sidetext = "°"; Options["fill_angle"].sidetext = "°";
Options["fill_angle"].cli = "fill-angle=i"; Options["fill_angle"].cli = "fill-angle=i";
Options["fill_angle"].min = 0;
Options["fill_angle"].max = 359; Options["fill_angle"].max = 359;
Options["fill_density"].type = coPercent; Options["fill_density"].type = coPercent;

View File

@ -12,8 +12,8 @@
SV* as_hash(); SV* as_hash();
SV* get(t_config_option_key opt_key); SV* get(t_config_option_key opt_key);
SV* get_at(t_config_option_key opt_key, int i); SV* get_at(t_config_option_key opt_key, int i);
void set(t_config_option_key opt_key, SV* value); bool set(t_config_option_key opt_key, SV* value);
void set_deserialize(t_config_option_key opt_key, std::string str); bool set_deserialize(t_config_option_key opt_key, std::string str);
std::string serialize(t_config_option_key opt_key); std::string serialize(t_config_option_key opt_key);
double get_abs_value(t_config_option_key opt_key); double get_abs_value(t_config_option_key opt_key);
%name{get_abs_value_over} %name{get_abs_value_over}
@ -34,8 +34,8 @@
SV* as_hash(); SV* as_hash();
SV* get(t_config_option_key opt_key); SV* get(t_config_option_key opt_key);
SV* get_at(t_config_option_key opt_key, int i); SV* get_at(t_config_option_key opt_key, int i);
void set(t_config_option_key opt_key, SV* value); bool set(t_config_option_key opt_key, SV* value);
void set_deserialize(t_config_option_key opt_key, std::string str); bool set_deserialize(t_config_option_key opt_key, std::string str);
std::string serialize(t_config_option_key opt_key); std::string serialize(t_config_option_key opt_key);
double get_abs_value(t_config_option_key opt_key); double get_abs_value(t_config_option_key opt_key);
%name{get_abs_value_over} %name{get_abs_value_over}
@ -54,8 +54,8 @@
SV* as_hash(); SV* as_hash();
SV* get(t_config_option_key opt_key); SV* get(t_config_option_key opt_key);
SV* get_at(t_config_option_key opt_key, int i); SV* get_at(t_config_option_key opt_key, int i);
void set(t_config_option_key opt_key, SV* value); bool set(t_config_option_key opt_key, SV* value);
void set_deserialize(t_config_option_key opt_key, std::string str); bool set_deserialize(t_config_option_key opt_key, std::string str);
std::string serialize(t_config_option_key opt_key); std::string serialize(t_config_option_key opt_key);
double get_abs_value(t_config_option_key opt_key); double get_abs_value(t_config_option_key opt_key);
%name{get_abs_value_over} %name{get_abs_value_over}
@ -75,8 +75,8 @@
SV* as_hash(); SV* as_hash();
SV* get(t_config_option_key opt_key); SV* get(t_config_option_key opt_key);
SV* get_at(t_config_option_key opt_key, int i); SV* get_at(t_config_option_key opt_key, int i);
void set(t_config_option_key opt_key, SV* value); bool set(t_config_option_key opt_key, SV* value);
void set_deserialize(t_config_option_key opt_key, std::string str); bool set_deserialize(t_config_option_key opt_key, std::string str);
std::string serialize(t_config_option_key opt_key); std::string serialize(t_config_option_key opt_key);
double get_abs_value(t_config_option_key opt_key); double get_abs_value(t_config_option_key opt_key);
%name{get_abs_value_over} %name{get_abs_value_over}
@ -96,8 +96,8 @@
SV* as_hash(); SV* as_hash();
SV* get(t_config_option_key opt_key); SV* get(t_config_option_key opt_key);
SV* get_at(t_config_option_key opt_key, int i); SV* get_at(t_config_option_key opt_key, int i);
void set(t_config_option_key opt_key, SV* value); bool set(t_config_option_key opt_key, SV* value);
void set_deserialize(t_config_option_key opt_key, std::string str); bool set_deserialize(t_config_option_key opt_key, std::string str);
std::string serialize(t_config_option_key opt_key); std::string serialize(t_config_option_key opt_key);
double get_abs_value(t_config_option_key opt_key); double get_abs_value(t_config_option_key opt_key);
%name{get_abs_value_over} %name{get_abs_value_over}