diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index 55048604f..2917f9c6f 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -501,16 +501,6 @@ void ConfigBase::set_deserialize(std::initializer_list items this->set_deserialize(item.opt_key, item.opt_value, substitutions_ctxt, item.append); } -static inline bool looks_like_enum_value(const std::string &value) -{ - if (value.empty() || value.size() > 64 || ! isalpha(value.front())) - return false; - for (const char c : value) - if (! (isalnum(c) || c == '_' || c == '-')) - return false; - return true; -} - bool ConfigBase::set_deserialize_raw(const t_config_option_key &opt_key_src, const std::string &value, ConfigSubstitutionContext& substitutions_ctxt, bool append) { t_config_option_key opt_key = opt_key_src; @@ -547,32 +537,43 @@ bool ConfigBase::set_deserialize_raw(const t_config_option_key &opt_key_src, con ConfigOption *opt = this->option(opt_key, true); assert(opt != nullptr); - bool success = opt->deserialize(value, append); - if (! success && substitutions_ctxt.rule != ForwardCompatibilitySubstitutionRule::Disable && - // Only allow substitutions of an enum value by another enum value or a boolean value with an enum value. - // That means, we expect enum values being added in the future and possibly booleans being converted to enums. - (optdef->type == coEnum || optdef->type == coBool)) - { - // Deserialize failed, try to substitute with a default value. - assert(substitutions_ctxt.rule == ForwardCompatibilitySubstitutionRule::Enable || substitutions_ctxt.rule == ForwardCompatibilitySubstitutionRule::EnableSilent); + bool success = false; + bool substituted = false; + if (optdef->type == coBools && substitutions_ctxt.rule != ForwardCompatibilitySubstitutionRule::Disable) { + //FIXME Special handling of vectors of bools, quick and not so dirty solution before PrusaSlicer 2.3.2 release. + auto result = opt->nullable() ? + static_cast(opt)->deserialize_with_substitutions(value, append, true) : + static_cast(opt)->deserialize_with_substitutions(value, append, true); + success = result != ConfigHelpers::DeserializationResult::Failed; + substituted = result == ConfigHelpers::DeserializationResult::Substituted; + } else { + success = opt->deserialize(value, append); + if (! success && substitutions_ctxt.rule != ForwardCompatibilitySubstitutionRule::Disable && + // Only allow substitutions of an enum value by another enum value or a boolean value with an enum value. + // That means, we expect enum values being added in the future and possibly booleans being converted to enums. + (optdef->type == coEnum || optdef->type == coBool) && ConfigHelpers::looks_like_enum_value(value)) { + // Deserialize failed, try to substitute with a default value. + assert(substitutions_ctxt.rule == ForwardCompatibilitySubstitutionRule::Enable || substitutions_ctxt.rule == ForwardCompatibilitySubstitutionRule::EnableSilent); + if (optdef->type == coEnum && opt_key == "gcode_flavor" && (value == "marlin2" || value == "marlinfirmware")) + static_cast*>(opt)->value = gcfMarlin; + else if (optdef->type == coBool) + static_cast(opt)->value = ConfigHelpers::enum_looks_like_true_value(value); + else + // Just use the default of the option. + opt->set(optdef->default_value.get()); + success = true; + substituted = true; + } + } - if (optdef->type == coEnum && opt_key == "gcode_flavor" && (value == "marlin2" || value == "marlinfirmware")) - static_cast*>(opt)->value = gcfMarlin; - else if (optdef->type == coBool && looks_like_enum_value(value)) - static_cast(opt)->value = boost::iequals(value, "enabled") || boost::iequals(value, "on"); - else - opt->set(optdef->default_value.get()); - - if (substitutions_ctxt.rule == ForwardCompatibilitySubstitutionRule::Enable || - substitutions_ctxt.rule == ForwardCompatibilitySubstitutionRule::EnableSystemSilent) { - // Log the substitution. - ConfigSubstitution config_substitution; - config_substitution.opt_def = optdef; - config_substitution.old_value = value;//std::unique_ptr(opt); - config_substitution.new_value = ConfigOptionUniquePtr(this->option(opt_key, true)->clone()); - substitutions_ctxt.substitutions.emplace_back(std::move(config_substitution)); - } - return true; + if (substituted && (substitutions_ctxt.rule == ForwardCompatibilitySubstitutionRule::Enable || + substitutions_ctxt.rule == ForwardCompatibilitySubstitutionRule::EnableSystemSilent)) { + // Log the substitution. + ConfigSubstitution config_substitution; + config_substitution.opt_def = optdef; + config_substitution.old_value = value; + config_substitution.new_value = ConfigOptionUniquePtr(opt->clone()); + substitutions_ctxt.substitutions.emplace_back(std::move(config_substitution)); } return success; } diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index 1504448b5..a4882d038 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -37,6 +37,30 @@ extern bool unescape_strings_cstyle(const std::string &str, std::vector< extern std::string escape_ampersand(const std::string& str); +namespace ConfigHelpers { + inline bool looks_like_enum_value(std::string value) + { + boost::trim(value); + if (value.empty() || value.size() > 64 || ! isalpha(value.front())) + return false; + for (const char c : value) + if (! (isalnum(c) || c == '_' || c == '-')) + return false; + return true; + } + + inline bool enum_looks_like_true_value(std::string value) { + boost::trim(value); + return boost::iequals(value, "enabled") || boost::iequals(value, "on"); + } + + enum DeserializationResult { + Loaded, + Substituted, + Failed, + }; +}; + // Base for all exceptions thrown by the configuration layer. class ConfigurationError : public Slic3r::RuntimeError { public: @@ -1320,24 +1344,39 @@ public: } return vv; } - - bool deserialize(const std::string &str, bool append = false) override + + ConfigHelpers::DeserializationResult deserialize_with_substitutions(const std::string &str, bool append, bool substitute) { if (! append) this->values.clear(); std::istringstream is(str); std::string item_str; + bool substituted = false; while (std::getline(is, item_str, ',')) { boost::trim(item_str); + unsigned char new_value = 0; if (item_str == "nil") { if (NULLABLE) this->values.push_back(nil_value()); else throw ConfigurationError("Deserializing nil into a non-nullable object"); + } else if (item_str == "1") { + new_value = true; + } else if (item_str == "0") { + new_value = false; + } else if (substitute && ConfigHelpers::looks_like_enum_value(item_str)) { + new_value = ConfigHelpers::enum_looks_like_true_value(item_str); + substituted = true; } else - this->values.push_back(item_str.compare("1") == 0); + return ConfigHelpers::DeserializationResult::Failed; + this->values.push_back(new_value); } - return true; + return substituted ? ConfigHelpers::DeserializationResult::Substituted : ConfigHelpers::DeserializationResult::Loaded; + } + + bool deserialize(const std::string &str, bool append = false) override + { + return this->deserialize_with_substitutions(str, append, false) == ConfigHelpers::DeserializationResult::Loaded; } protected: diff --git a/src/slic3r/GUI/GUI.cpp b/src/slic3r/GUI/GUI.cpp index 4bcb5a940..d397de061 100644 --- a/src/slic3r/GUI/GUI.cpp +++ b/src/slic3r/GUI/GUI.cpp @@ -271,7 +271,9 @@ static void add_config_substitutions(const ConfigSubstitutions& conf_substitutio const ConfigOptionDef* def = conf_substitution.opt_def; if (!def) continue; - if (def->type == coEnum) { + switch (def->type) { + case coEnum: + { const std::vector& labels = def->enum_labels; const std::vector& values = def->enum_values; int val = conf_substitution.new_value->getInt(); @@ -296,9 +298,24 @@ static void add_config_substitutions(const ConfigSubstitutions& conf_substitutio } else new_val = from_u8(_utf8(labels[val])); + break; } - else if (def->type == coBool) + case coBool: new_val = conf_substitution.new_value->getBool() ? "true" : "false"; + break; + case coBools: + if (conf_substitution.new_value->nullable()) + for (const char v : static_cast(conf_substitution.new_value.get())->values) + new_val += std::string(v == ConfigOptionBoolsNullable::nil_value() ? "nil" : v ? "true" : "false") + ", "; + else + for (const char v : static_cast(conf_substitution.new_value.get())->values) + new_val += std::string(v ? "true" : "false") + ", "; + if (! new_val.empty()) + new_val.erase(new_val.begin() + new_val.size() - 2, new_val.end()); + break; + default: + assert(false); + } changes += "" + bold(_(def->label)) + ": " + format_wxstr(_L("new unknown value %1% was changed to default value %2%"), bold(conf_substitution.old_value), bold(new_val)) +