2017-11-02 16:21:34 +01:00
#include <cassert>
2020-09-14 16:27:55 +02:00
#include "Exception.hpp"
2017-09-19 13:55:48 +02:00
#include "Preset.hpp"
2020-08-08 17:03:20 +02:00
#include "AppConfig.hpp"
2017-09-19 13:55:48 +02:00
2018-12-12 19:02:18 +01:00
#ifdef _MSC_VER
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <Windows.h>
#endif /* _MSC_VER */
2020-06-16 16:58:41 +02:00
// instead of #include "slic3r/GUI/I18N.hpp" :
#ifndef L
// !!! If you needed to translate some string,
// !!! please use _L(string)
// !!! _() - is a standard wxWidgets macro to translate
// !!! L() is used only for marking localizable string
// !!! It will be used in "xgettext" to create a Locating Message Catalog.
#define L(s) s
#endif /* L */
2019-01-30 15:22:17 +01:00
#include <algorithm>
2017-09-19 13:55:48 +02:00
#include <fstream>
2018-04-12 20:04:48 +02:00
#include <stdexcept>
2019-02-08 10:33:07 +01:00
#include <unordered_map>
2018-04-12 20:04:48 +02:00
#include <boost/format.hpp>
2017-10-02 17:35:00 +02:00
#include <boost/filesystem.hpp>
2018-04-11 13:12:08 +02:00
#include <boost/filesystem/fstream.hpp>
2020-06-26 09:58:39 +02:00
#include <boost/algorithm/string.hpp>
2017-09-19 13:55:48 +02:00
#include <boost/algorithm/string/predicate.hpp>
2017-10-25 12:53:31 +02:00
#include <boost/nowide/cenv.hpp>
2018-12-12 19:02:18 +01:00
#include <boost/nowide/convert.hpp>
2017-10-26 17:17:39 +02:00
#include <boost/nowide/cstdio.hpp>
2017-10-25 12:53:31 +02:00
#include <boost/nowide/fstream.hpp>
#include <boost/property_tree/ini_parser.hpp>
#include <boost/property_tree/ptree.hpp>
2017-10-26 17:17:39 +02:00
#include <boost/locale.hpp>
2018-05-01 10:35:07 +02:00
#include <boost/log/trivial.hpp>
2017-10-25 12:53:31 +02:00
2020-06-16 16:58:41 +02:00
#include "libslic3r.h"
#include "Utils.hpp"
#include "PlaceholderParser.hpp"
2017-10-25 12:53:31 +02:00
2018-04-11 13:12:08 +02:00
using boost :: property_tree :: ptree ;
2017-09-19 13:55:48 +02:00
namespace Slic3r {
2018-04-11 13:12:08 +02:00
ConfigFileType guess_config_file_type ( const ptree & tree )
2017-11-01 19:30:05 +01:00
{
size_t app_config = 0 ;
size_t bundle = 0 ;
size_t config = 0 ;
2018-04-11 13:12:08 +02:00
for ( const ptree :: value_type & v : tree ) {
2017-11-01 19:30:05 +01:00
if ( v . second . empty ()) {
if ( v . first == "background_processing" ||
v . first == "last_output_path" ||
v . first == "no_controller" ||
v . first == "no_defaults" )
++ app_config ;
else if ( v . first == "nozzle_diameter" ||
v . first == "filament_diameter" )
++ config ;
} else if ( boost :: algorithm :: starts_with ( v . first , "print:" ) ||
boost :: algorithm :: starts_with ( v . first , "filament:" ) ||
boost :: algorithm :: starts_with ( v . first , "printer:" ) ||
v . first == "settings" )
++ bundle ;
else if ( v . first == "presets" ) {
++ app_config ;
++ bundle ;
} else if ( v . first == "recent" ) {
for ( auto & kvp : v . second )
if ( kvp . first == "config_directory" || kvp . first == "skein_directory" )
++ app_config ;
}
}
return ( app_config > bundle && app_config > config ) ? CONFIG_FILE_TYPE_APP_CONFIG :
( bundle > config ) ? CONFIG_FILE_TYPE_CONFIG_BUNDLE : CONFIG_FILE_TYPE_CONFIG ;
}
2018-04-11 13:12:08 +02:00
VendorProfile VendorProfile :: from_ini ( const boost :: filesystem :: path & path , bool load_all )
{
ptree tree ;
boost :: filesystem :: ifstream ifs ( path );
boost :: property_tree :: read_ini ( ifs , tree );
return VendorProfile :: from_ini ( tree , path , load_all );
}
2019-02-08 10:33:07 +01:00
static const std :: unordered_map < std :: string , std :: string > pre_family_model_map {{
{ "MK3" , "MK3" },
{ "MK3MMU2" , "MK3" },
{ "MK2.5" , "MK2.5" },
{ "MK2.5MMU2" , "MK2.5" },
{ "MK2S" , "MK2" },
{ "MK2SMM" , "MK2" },
{ "SL1" , "SL1" },
}};
2018-04-11 13:12:08 +02:00
VendorProfile VendorProfile :: from_ini ( const ptree & tree , const boost :: filesystem :: path & path , bool load_all )
{
static const std :: string printer_model_key = "printer_model:" ;
2019-04-12 16:18:46 +02:00
static const std :: string filaments_section = "default_filaments" ;
static const std :: string materials_section = "default_sla_materials" ;
2018-04-11 13:12:08 +02:00
const std :: string id = path . stem (). string ();
2018-04-12 20:04:48 +02:00
if ( ! boost :: filesystem :: exists ( path )) {
2020-09-14 16:27:55 +02:00
throw Slic3r :: RuntimeError (( boost :: format ( "Cannot load Vendor Config Bundle `%1%`: File not found: `%2%`." ) % id % path ). str ());
2018-04-12 20:04:48 +02:00
}
2018-04-11 13:12:08 +02:00
VendorProfile res ( id );
2019-04-12 16:18:46 +02:00
// Helper to get compulsory fields
2018-04-11 13:12:08 +02:00
auto get_or_throw = [ & ]( const ptree & tree , const std :: string & key ) -> ptree :: const_assoc_iterator
{
auto res = tree . find ( key );
if ( res == tree . not_found ()) {
2020-09-14 16:27:55 +02:00
throw Slic3r :: RuntimeError (( boost :: format ( "Vendor Config Bundle `%1%` is not valid: Missing secion or key: `%2%`." ) % id % key ). str ());
2018-04-11 13:12:08 +02:00
}
return res ;
};
2019-04-12 16:18:46 +02:00
// Load the header
2018-04-11 13:12:08 +02:00
const auto & vendor_section = get_or_throw ( tree , "vendor" ) -> second ;
res . name = get_or_throw ( vendor_section , "name" ) -> second . data ();
auto config_version_str = get_or_throw ( vendor_section , "config_version" ) -> second . data ();
auto config_version = Semver :: parse ( config_version_str );
if ( ! config_version ) {
2020-09-14 16:27:55 +02:00
throw Slic3r :: RuntimeError (( boost :: format ( "Vendor Config Bundle `%1%` is not valid: Cannot parse config_version: `%2%`." ) % id % config_version_str ). str ());
2018-04-11 13:12:08 +02:00
} else {
res . config_version = std :: move ( * config_version );
}
2019-04-12 16:18:46 +02:00
// Load URLs
2019-05-13 18:38:48 +02:00
const auto config_update_url = vendor_section . find ( "config_update_url" );
2018-04-11 13:12:08 +02:00
if ( config_update_url != vendor_section . not_found ()) {
res . config_update_url = config_update_url -> second . data ();
}
2019-05-13 18:38:48 +02:00
const auto changelog_url = vendor_section . find ( "changelog_url" );
if ( changelog_url != vendor_section . not_found ()) {
res . changelog_url = changelog_url -> second . data ();
}
2018-04-11 13:12:08 +02:00
if ( ! load_all ) {
return res ;
}
2019-04-12 16:18:46 +02:00
// Load printer models
2018-04-11 13:12:08 +02:00
for ( auto & section : tree ) {
if ( boost :: starts_with ( section . first , printer_model_key )) {
VendorProfile :: PrinterModel model ;
model . id = section . first . substr ( printer_model_key . size ());
model . name = section . second . get < std :: string > ( "name" , model . id );
2019-01-30 15:22:17 +01:00
2019-02-07 15:55:47 +01:00
const char * technology_fallback = boost :: algorithm :: starts_with ( model . id , "SL" ) ? "SLA" : "FFF" ;
auto technology_field = section . second . get < std :: string > ( "technology" , technology_fallback );
2018-07-31 15:09:57 +02:00
if ( ! ConfigOptionEnum < PrinterTechnology >:: from_string ( technology_field , model . technology )) {
BOOST_LOG_TRIVIAL ( error ) << boost :: format ( "Vendor bundle: `%1%`: Invalid printer technology field: `%2%`" ) % id % technology_field ;
model . technology = ptFFF ;
}
2019-01-30 15:22:17 +01:00
2019-02-07 15:55:47 +01:00
model . family = section . second . get < std :: string > ( "family" , std :: string ());
2019-02-08 10:33:07 +01:00
if ( model . family . empty () && res . name == "Prusa Research" ) {
// If no family is specified, it can be inferred for known printers
const auto from_pre_map = pre_family_model_map . find ( model . id );
if ( from_pre_map != pre_family_model_map . end ()) { model . family = from_pre_map -> second ; }
}
2018-12-21 20:09:25 +01:00
#if 0
2019-08-06 16:51:32 +02:00
// Remove SLA printers from the initial alpha.
if (model.technology == ptSLA)
continue;
2018-12-21 20:09:25 +01:00
#endif
2019-08-06 16:51:32 +02:00
section . second . get < std :: string > ( "variants" , "" );
2018-05-01 10:35:07 +02:00
const auto variants_field = section . second . get < std :: string > ( "variants" , "" );
2018-04-11 13:12:08 +02:00
std :: vector < std :: string > variants ;
2018-05-01 10:35:07 +02:00
if ( Slic3r :: unescape_strings_cstyle ( variants_field , variants )) {
2018-04-11 13:12:08 +02:00
for ( const std :: string & variant_name : variants ) {
if ( model . variant ( variant_name ) == nullptr )
model . variants . emplace_back ( VendorProfile :: PrinterVariant ( variant_name ));
}
} else {
2018-05-01 10:35:07 +02:00
BOOST_LOG_TRIVIAL ( error ) << boost :: format ( "Vendor bundle: `%1%`: Malformed variants field: `%2%`" ) % id % variants_field ;
2018-04-11 13:12:08 +02:00
}
2020-01-13 16:01:01 +01:00
auto default_materials_field = section . second . get < std :: string > ( "default_materials" , "" );
if ( default_materials_field . empty ())
default_materials_field = section . second . get < std :: string > ( "default_filaments" , "" );
if ( Slic3r :: unescape_strings_cstyle ( default_materials_field , model . default_materials )) {
Slic3r :: sort_remove_duplicates ( model . default_materials );
2020-03-11 18:12:16 +01:00
if ( ! model . default_materials . empty () && model . default_materials . front (). empty ())
// An empty material was inserted into the list of default materials. Remove it.
model . default_materials . erase ( model . default_materials . begin ());
2020-01-13 16:01:01 +01:00
} else {
BOOST_LOG_TRIVIAL ( error ) << boost :: format ( "Vendor bundle: `%1%`: Malformed default_materials field: `%2%`" ) % id % default_materials_field ;
}
2020-01-07 14:08:21 +01:00
model . bed_model = section . second . get < std :: string > ( "bed_model" , "" );
model . bed_texture = section . second . get < std :: string > ( "bed_texture" , "" );
2018-04-11 13:12:08 +02:00
if ( ! model . id . empty () && ! model . variants . empty ())
res . models . push_back ( std :: move ( model ));
}
}
2019-04-12 16:18:46 +02:00
// Load filaments and sla materials to be installed by default
const auto filaments = tree . find ( filaments_section );
if ( filaments != tree . not_found ()) {
for ( auto & pair : filaments -> second ) {
if ( pair . second . data () == "1" ) {
res . default_filaments . insert ( pair . first );
}
}
}
const auto materials = tree . find ( materials_section );
if ( materials != tree . not_found ()) {
for ( auto & pair : materials -> second ) {
if ( pair . second . data () == "1" ) {
res . default_sla_materials . insert ( pair . first );
}
}
}
2018-04-11 13:12:08 +02:00
return res ;
}
2019-01-30 15:22:17 +01:00
std :: vector < std :: string > VendorProfile :: families () const
{
std :: vector < std :: string > res ;
unsigned num_familiies = 0 ;
for ( auto & model : models ) {
2019-02-07 15:55:47 +01:00
if ( std :: find ( res . begin (), res . end (), model . family ) == res . end ()) {
2019-01-30 15:22:17 +01:00
res . push_back ( model . family );
num_familiies ++ ;
}
}
return res ;
}
2018-04-11 13:12:08 +02:00
2017-10-26 17:17:39 +02:00
// Suffix to be added to a modified preset name in the combo box.
2017-10-25 12:53:31 +02:00
static std :: string g_suffix_modified = " (modified)" ;
2017-10-30 18:41:50 +01:00
const std :: string & Preset :: suffix_modified ()
{
return g_suffix_modified ;
}
2018-07-23 15:44:01 +02:00
2020-06-16 16:58:41 +02:00
void Preset :: update_suffix_modified ( const std :: string & new_suffix_modified )
2018-07-23 15:44:01 +02:00
{
2020-06-16 16:58:41 +02:00
g_suffix_modified = new_suffix_modified ;
2018-07-23 15:44:01 +02:00
}
2017-10-26 17:17:39 +02:00
// Remove an optional "(modified)" suffix from a name.
// This converts a UI name to a unique preset identifier.
2017-10-30 18:41:50 +01:00
std :: string Preset :: remove_suffix_modified ( const std :: string & name )
2017-10-26 17:17:39 +02:00
{
return boost :: algorithm :: ends_with ( name , g_suffix_modified ) ?
name . substr ( 0 , name . size () - g_suffix_modified . size ()) :
name ;
}
2017-10-25 12:53:31 +02:00
2017-11-01 19:30:05 +01:00
// Update new extruder fields at the printer profile.
void Preset :: normalize ( DynamicPrintConfig & config )
{
auto * nozzle_diameter = dynamic_cast < const ConfigOptionFloats *> ( config . option ( "nozzle_diameter" ));
if ( nozzle_diameter != nullptr )
2018-07-31 15:09:57 +02:00
// Loaded the FFF Printer settings. Verify, that all extruder dependent values have enough values.
2019-10-15 16:31:20 +02:00
config . set_num_extruders (( unsigned int ) nozzle_diameter -> values . size ());
2017-11-02 16:21:34 +01:00
if ( config . option ( "filament_diameter" ) != nullptr ) {
// This config contains single or multiple filament presets.
// Ensure that the filament preset vector options contain the correct number of values.
size_t n = ( nozzle_diameter == nullptr ) ? 1 : nozzle_diameter -> values . size ();
const auto & defaults = FullPrintConfig :: defaults ();
for ( const std :: string & key : Preset :: filament_options ()) {
2019-08-06 16:51:32 +02:00
if ( key == "compatible_prints" || key == "compatible_printers" )
continue ;
2017-11-02 16:21:34 +01:00
auto * opt = config . option ( key , false );
2018-05-23 14:15:10 +02:00
/*assert(opt != nullptr);
assert(opt->is_vector());*/
2017-11-02 16:21:34 +01:00
if ( opt != nullptr && opt -> is_vector ())
static_cast < ConfigOptionVectorBase *> ( opt ) -> resize ( n , defaults . option ( key ));
}
2018-05-23 13:19:25 +02:00
// The following keys are mandatory for the UI, but they are not part of FullPrintConfig, therefore they are handled separately.
for ( const std :: string & key : { "filament_settings_id" }) {
auto * opt = config . option ( key , false );
2018-11-06 19:09:54 +01:00
assert ( opt == nullptr || opt -> type () == coStrings );
2018-05-23 13:19:25 +02:00
if ( opt != nullptr && opt -> type () == coStrings )
static_cast < ConfigOptionStrings *> ( opt ) -> values . resize ( n , std :: string ());
}
2017-11-02 16:21:34 +01:00
}
2021-02-10 17:28:56 +01:00
if ( const auto * gap_fill_speed = config . option < ConfigOptionFloat > ( "gap_fill_speed" , false ); gap_fill_speed && gap_fill_speed -> value <= 0. ) {
// Legacy conversion. If the gap fill speed is zero, it means the gap fill is not enabled.
// Set the new gap_fill_enabled value, so that it will show up in the UI as disabled.
if ( auto * gap_fill_enabled = config . option < ConfigOptionBool > ( "gap_fill_enabled" , false ); gap_fill_enabled )
gap_fill_enabled -> value = false ;
}
2017-11-01 19:30:05 +01:00
}
2018-10-31 16:22:36 +01:00
std :: string Preset :: remove_invalid_keys ( DynamicPrintConfig & config , const DynamicPrintConfig & default_config )
2017-09-19 13:55:48 +02:00
{
2018-10-31 16:22:36 +01:00
std :: string incorrect_keys ;
for ( const std :: string & key : config . keys ())
if ( ! default_config . has ( key )) {
if ( incorrect_keys . empty ())
incorrect_keys = key ;
else {
incorrect_keys += ", " ;
incorrect_keys += key ;
}
config . erase ( key );
2017-11-01 19:30:05 +01:00
}
2018-10-31 16:22:36 +01:00
return incorrect_keys ;
2017-09-19 13:55:48 +02:00
}
2017-10-25 12:53:31 +02:00
void Preset :: save ()
{
this -> config . save ( this -> file );
}
// Return a label of this preset, consisting of a name and a "(modified)" suffix, if this preset is dirty.
std :: string Preset :: label () const
{
return this -> name + ( this -> is_dirty ? g_suffix_modified : "" );
}
2020-02-27 11:44:01 +01:00
bool is_compatible_with_print ( const PresetWithVendorProfile & preset , const PresetWithVendorProfile & active_print , const PresetWithVendorProfile & active_printer )
2018-12-04 17:56:49 +01:00
{
2020-02-27 11:44:01 +01:00
if ( preset . vendor != nullptr && preset . vendor != active_printer . vendor )
2019-12-05 14:48:11 +01:00
// The current profile has a vendor assigned and it is different from the active print's vendor.
return false ;
auto & condition = preset . preset . compatible_prints_condition ();
auto * compatible_prints = dynamic_cast < const ConfigOptionStrings *> ( preset . preset . config . option ( "compatible_prints" ));
2018-12-04 17:56:49 +01:00
bool has_compatible_prints = compatible_prints != nullptr && ! compatible_prints -> values . empty ();
if ( ! has_compatible_prints && ! condition . empty ()) {
try {
2019-12-05 14:48:11 +01:00
return PlaceholderParser :: evaluate_boolean_expression ( condition , active_print . preset . config );
2018-12-04 17:56:49 +01:00
} catch ( const std :: runtime_error & err ) {
//FIXME in case of an error, return "compatible with everything".
2019-12-05 14:48:11 +01:00
printf ( "Preset::is_compatible_with_print - parsing error of compatible_prints_condition %s: \n %s \n " , active_print . preset . name . c_str (), err . what ());
2018-12-04 17:56:49 +01:00
return true ;
}
}
2019-12-05 14:48:11 +01:00
return preset . preset . is_default || active_print . preset . name . empty () || ! has_compatible_prints ||
std :: find ( compatible_prints -> values . begin (), compatible_prints -> values . end (), active_print . preset . name ) !=
2018-12-04 17:56:49 +01:00
compatible_prints -> values . end ();
}
2019-12-05 14:48:11 +01:00
bool is_compatible_with_printer ( const PresetWithVendorProfile & preset , const PresetWithVendorProfile & active_printer , const DynamicPrintConfig * extra_config )
2017-09-19 13:55:48 +02:00
{
2019-12-05 14:48:11 +01:00
if ( preset . vendor != nullptr && preset . vendor != active_printer . vendor )
// The current profile has a vendor assigned and it is different from the active print's vendor.
return false ;
auto & condition = preset . preset . compatible_printers_condition ();
auto * compatible_printers = dynamic_cast < const ConfigOptionStrings *> ( preset . preset . config . option ( "compatible_printers" ));
2017-12-20 13:32:02 +01:00
bool has_compatible_printers = compatible_printers != nullptr && ! compatible_printers -> values . empty ();
2018-06-27 16:57:42 +02:00
if ( ! has_compatible_printers && ! condition . empty ()) {
2017-12-18 15:07:38 +01:00
try {
2019-12-05 14:48:11 +01:00
return PlaceholderParser :: evaluate_boolean_expression ( condition , active_printer . preset . config , extra_config );
2017-12-18 15:07:38 +01:00
} catch ( const std :: runtime_error & err ) {
//FIXME in case of an error, return "compatible with everything".
2019-12-05 14:48:11 +01:00
printf ( "Preset::is_compatible_with_printer - parsing error of compatible_printers_condition %s: \n %s \n " , active_printer . preset . name . c_str (), err . what ());
2017-12-18 15:07:38 +01:00
return true ;
}
}
2019-12-05 14:48:11 +01:00
return preset . preset . is_default || active_printer . preset . name . empty () || ! has_compatible_printers ||
std :: find ( compatible_printers -> values . begin (), compatible_printers -> values . end (), active_printer . preset . name ) !=
2017-09-19 13:55:48 +02:00
compatible_printers -> values . end ();
2017-11-10 17:27:05 +01:00
}
2019-12-05 14:48:11 +01:00
bool is_compatible_with_printer ( const PresetWithVendorProfile & preset , const PresetWithVendorProfile & active_printer )
2017-11-10 17:27:05 +01:00
{
2017-12-19 16:48:14 +01:00
DynamicPrintConfig config ;
2019-12-05 14:48:11 +01:00
config . set_key_value ( "printer_preset" , new ConfigOptionString ( active_printer . preset . name ));
const ConfigOption * opt = active_printer . preset . config . option ( "nozzle_diameter" );
2019-08-06 16:51:32 +02:00
if ( opt )
config . set_key_value ( "num_extruders" , new ConfigOptionInt (( int ) static_cast < const ConfigOptionFloats *> ( opt ) -> values . size ()));
2019-12-05 14:48:11 +01:00
return is_compatible_with_printer ( preset , active_printer , & config );
2017-09-19 13:55:48 +02:00
}
2018-03-29 17:54:43 +02:00
void Preset :: set_visible_from_appconfig ( const AppConfig & app_config )
{
if ( vendor == nullptr ) { return ; }
2019-04-12 16:18:46 +02:00
if ( type == TYPE_PRINTER ) {
const std :: string & model = config . opt_string ( "printer_model" );
const std :: string & variant = config . opt_string ( "printer_variant" );
2020-03-02 16:15:41 +01:00
if ( model . empty () || variant . empty ())
return ;
2019-04-12 16:18:46 +02:00
is_visible = app_config . get_variant ( vendor -> id , model , variant );
2020-03-02 16:15:41 +01:00
} else if ( type == TYPE_FILAMENT || type == TYPE_SLA_MATERIAL ) {
2020-03-14 18:35:42 +01:00
const std :: string & section_name = ( type == TYPE_FILAMENT ) ? AppConfig :: SECTION_FILAMENTS : AppConfig :: SECTION_MATERIALS ;
2020-03-02 16:15:41 +01:00
if ( app_config . has_section ( section_name )) {
// Check whether this profile is marked as "installed" in PrusaSlicer.ini,
// or whether a profile is marked as "installed", which this profile may have been renamed from.
const std :: map < std :: string , std :: string > & installed = app_config . get_section ( section_name );
auto has = [ & installed ]( const std :: string & name ) {
auto it = installed . find ( name );
return it != installed . end () && ! it -> second . empty ();
};
is_visible = has ( this -> name );
for ( auto it = this -> renamed_from . begin (); ! is_visible && it != this -> renamed_from . end (); ++ it )
is_visible = has ( * it );
}
2019-04-12 16:18:46 +02:00
}
2018-03-29 17:54:43 +02:00
}
2017-11-01 19:30:05 +01:00
const std :: vector < std :: string >& Preset :: print_options ()
2019-08-06 16:51:32 +02:00
{
2017-11-01 19:30:05 +01:00
static std :: vector < std :: string > s_opts {
2020-02-05 16:53:26 +01:00
"layer_height" , "first_layer_height" , "perimeters" , "spiral_vase" , "slice_closing_radius" ,
"top_solid_layers" , "top_solid_min_thickness" , "bottom_solid_layers" , "bottom_solid_min_thickness" ,
2019-08-06 16:51:32 +02:00
"extra_perimeters" , "ensure_vertical_shell_thickness" , "avoid_crossing_perimeters" , "thin_walls" , "overhangs" ,
2021-02-10 17:28:56 +01:00
"seam_position" , "external_perimeters_first" , "fill_density" , "fill_pattern" , "top_fill_pattern" , "bottom_fill_pattern" ,
2019-08-06 16:51:32 +02:00
"infill_every_layers" , "infill_only_where_needed" , "solid_infill_every_layers" , "fill_angle" , "bridge_angle" ,
2020-04-14 11:53:28 +02:00
"solid_infill_below_area" , "only_retract_when_crossing_perimeters" , "infill_first" ,
"ironing" , "ironing_type" , "ironing_flowrate" , "ironing_speed" , "ironing_spacing" ,
2020-10-16 02:25:45 +02:00
"max_print_speed" , "max_volumetric_speed" , "avoid_crossing_perimeters_max_detour" ,
2021-02-10 16:02:32 +01:00
"fuzzy_skin" , "fuzzy_skin_thickness" , "fuzzy_skin_point_dist" ,
2019-01-29 18:07:45 +01:00
#ifdef HAS_PRESSURE_EQUALIZER
2019-08-06 16:51:32 +02:00
"max_volumetric_extrusion_rate_slope_positive" , "max_volumetric_extrusion_rate_slope_negative" ,
2019-01-29 18:07:45 +01:00
#endif /* HAS_PRESSURE_EQUALIZER */
2019-08-06 16:51:32 +02:00
"perimeter_speed" , "small_perimeter_speed" , "external_perimeter_speed" , "infill_speed" , "solid_infill_speed" ,
2017-11-01 19:30:05 +01:00
"top_solid_infill_speed" , "support_material_speed" , "support_material_xy_spacing" , "support_material_interface_speed" ,
2021-02-10 17:28:56 +01:00
"bridge_speed" , "gap_fill_speed" , "gap_fill_enabled" , "travel_speed" , "first_layer_speed" , "perimeter_acceleration" , "infill_acceleration" ,
2020-03-14 07:46:39 +01:00
"bridge_acceleration" , "first_layer_acceleration" , "default_acceleration" , "skirts" , "skirt_distance" , "skirt_height" , "draft_shield" ,
2021-02-03 15:12:53 +01:00
"min_skirt_length" , "brim_width" , "brim_offset" , "brim_type" , "support_material" , "support_material_auto" , "support_material_threshold" , "support_material_enforce_layers" ,
2019-08-06 16:51:32 +02:00
"raft_layers" , "support_material_pattern" , "support_material_with_sheath" , "support_material_spacing" ,
"support_material_synchronize_layers" , "support_material_angle" , "support_material_interface_layers" ,
"support_material_interface_spacing" , "support_material_interface_contact_loops" , "support_material_contact_distance" ,
"support_material_buildplate_only" , "dont_support_bridges" , "notes" , "complete_objects" , "extruder_clearance_radius" ,
"extruder_clearance_height" , "gcode_comments" , "gcode_label_objects" , "output_filename_format" , "post_process" , "perimeter_extruder" ,
"infill_extruder" , "solid_infill_extruder" , "support_material_extruder" , "support_material_interface_extruder" ,
"ooze_prevention" , "standby_temperature_delta" , "interface_shells" , "extrusion_width" , "first_layer_extrusion_width" ,
"perimeter_extrusion_width" , "external_perimeter_extrusion_width" , "infill_extrusion_width" , "solid_infill_extrusion_width" ,
2020-11-24 16:00:46 +01:00
"top_infill_extrusion_width" , "support_material_extrusion_width" , "infill_overlap" , "infill_anchor" , "infill_anchor_max" , "bridge_flow_ratio" , "clip_multipart_objects" ,
2017-11-01 19:30:05 +01:00
"elefant_foot_compensation" , "xy_size_compensation" , "threads" , "resolution" , "wipe_tower" , "wipe_tower_x" , "wipe_tower_y" ,
2019-08-06 16:51:32 +02:00
"wipe_tower_width" , "wipe_tower_rotation_angle" , "wipe_tower_bridging" , "single_extruder_multi_material_priming" ,
2019-09-19 16:30:01 +02:00
"wipe_tower_no_sparse_layers" , "compatible_printers" , "compatible_printers_condition" , "inherits"
2017-11-01 19:30:05 +01:00
};
return s_opts ;
}
const std :: vector < std :: string >& Preset :: filament_options ()
2019-08-06 16:51:32 +02:00
{
2017-11-01 19:30:05 +01:00
static std :: vector < std :: string > s_opts {
2018-04-06 12:33:12 +02:00
"filament_colour" , "filament_diameter" , "filament_type" , "filament_soluble" , "filament_notes" , "filament_max_volumetric_speed" ,
2020-11-20 14:30:48 +01:00
"extrusion_multiplier" , "filament_density" , "filament_cost" , "filament_spool_weight" , "filament_loading_speed" , "filament_loading_speed_start" , "filament_load_time" ,
2018-08-21 14:36:24 +02:00
"filament_unloading_speed" , "filament_unloading_speed_start" , "filament_unload_time" , "filament_toolchange_delay" , "filament_cooling_moves" ,
"filament_cooling_initial_speed" , "filament_cooling_final_speed" , "filament_ramming_parameters" , "filament_minimal_purge_on_wipe_tower" ,
"temperature" , "first_layer_temperature" , "bed_temperature" , "first_layer_bed_temperature" , "fan_always_on" , "cooling" , "min_fan_speed" ,
2020-12-04 11:53:02 +01:00
"max_fan_speed" , "bridge_fan_speed" , "disable_fan_first_layers" , "full_fan_speed_layer" , "fan_below_layer_time" , "slowdown_below_layer_time" , "min_print_speed" ,
2018-12-04 17:56:49 +01:00
"start_filament_gcode" , "end_filament_gcode" ,
2019-07-23 14:15:42 +02:00
// Retract overrides
2019-08-06 16:51:32 +02:00
"filament_retract_length" , "filament_retract_lift" , "filament_retract_lift_above" , "filament_retract_lift_below" , "filament_retract_speed" , "filament_deretract_speed" , "filament_retract_restart_extra" , "filament_retract_before_travel" ,
"filament_retract_layer_change" , "filament_wipe" , "filament_retract_before_wipe" ,
// Profile compatibility
2019-04-12 16:18:46 +02:00
"filament_vendor" , "compatible_prints" , "compatible_prints_condition" , "compatible_printers" , "compatible_printers_condition" , "inherits"
2017-11-01 19:30:05 +01:00
};
return s_opts ;
}
2020-10-02 17:31:55 +02:00
const std :: vector < std :: string >& Preset :: machine_limits_options ()
{
static std :: vector < std :: string > s_opts ;
if ( s_opts . empty ()) {
s_opts = {
"machine_max_acceleration_extruding" , "machine_max_acceleration_retracting" ,
"machine_max_acceleration_x" , "machine_max_acceleration_y" , "machine_max_acceleration_z" , "machine_max_acceleration_e" ,
"machine_max_feedrate_x" , "machine_max_feedrate_y" , "machine_max_feedrate_z" , "machine_max_feedrate_e" ,
"machine_min_extruding_rate" , "machine_min_travel_rate" ,
"machine_max_jerk_x" , "machine_max_jerk_y" , "machine_max_jerk_z" , "machine_max_jerk_e" ,
};
}
return s_opts ;
}
2017-11-01 19:30:05 +01:00
const std :: vector < std :: string >& Preset :: printer_options ()
2019-08-06 16:51:32 +02:00
{
2017-11-01 19:30:05 +01:00
static std :: vector < std :: string > s_opts ;
if ( s_opts . empty ()) {
s_opts = {
2018-07-31 15:09:57 +02:00
"printer_technology" ,
2020-10-28 09:51:05 +01:00
"bed_shape" , "bed_custom_texture" , "bed_custom_model" , "z_offset" , "gcode_flavor" , "use_relative_e_distances" ,
2018-08-24 12:20:24 +02:00
"use_firmware_retraction" , "use_volumetric_e" , "variable_layer_height" ,
2020-10-28 09:51:05 +01:00
//FIXME the print host keys are left here just for conversion from the Printer preset to Physical Printer preset.
2018-08-24 12:20:24 +02:00
"host_type" , "print_host" , "printhost_apikey" , "printhost_cafile" ,
2017-11-28 15:19:57 +01:00
"single_extruder_multi_material" , "start_gcode" , "end_gcode" , "before_layer_gcode" , "layer_gcode" , "toolchange_gcode" ,
2020-06-03 10:42:47 +02:00
"color_change_gcode" , "pause_print_gcode" , "template_custom_gcode" ,
2018-04-06 12:33:12 +02:00
"between_objects_gcode" , "printer_vendor" , "printer_model" , "printer_variant" , "printer_notes" , "cooling_tube_retraction" ,
2019-08-06 16:51:32 +02:00
"cooling_tube_length" , "high_current_on_filament_swap" , "parking_pos_retraction" , "extra_loading_move" , "max_print_height" ,
2018-12-14 20:09:10 +01:00
"default_print_profile" , "inherits" ,
2020-10-02 17:31:55 +02:00
"remaining_times" , "silent_mode" ,
"machine_limits_usage" , "thumbnails"
2017-11-01 19:30:05 +01:00
};
2020-10-02 17:31:55 +02:00
s_opts . insert ( s_opts . end (), Preset :: machine_limits_options (). begin (), Preset :: machine_limits_options (). end ());
2017-11-01 19:30:05 +01:00
s_opts . insert ( s_opts . end (), Preset :: nozzle_options (). begin (), Preset :: nozzle_options (). end ());
}
return s_opts ;
}
2019-08-06 16:51:32 +02:00
// The following nozzle options of a printer profile will be adjusted to match the size
2018-03-13 09:45:04 +01:00
// of the nozzle_diameter vector.
2017-11-01 19:30:05 +01:00
const std :: vector < std :: string >& Preset :: nozzle_options ()
{
2019-10-15 16:31:20 +02:00
return print_config_def . extruder_option_keys ();
2017-11-01 19:30:05 +01:00
}
2018-11-16 17:36:23 +01:00
const std :: vector < std :: string >& Preset :: sla_print_options ()
2019-08-06 16:51:32 +02:00
{
2018-11-16 17:36:23 +01:00
static std :: vector < std :: string > s_opts ;
if ( s_opts . empty ()) {
s_opts = {
"layer_height" ,
2019-02-18 16:04:55 +01:00
"faded_layers" ,
2018-11-22 18:02:05 +01:00
"supports_enable" ,
2018-11-23 13:03:07 +01:00
"support_head_front_diameter" ,
2018-11-16 17:36:23 +01:00
"support_head_penetration" ,
"support_head_width" ,
2018-11-23 13:03:07 +01:00
"support_pillar_diameter" ,
2020-07-08 11:31:01 +02:00
"support_small_pillar_diameter_percent" ,
2020-03-02 12:43:00 +01:00
"support_max_bridges_on_pillar" ,
2019-01-09 12:21:43 +01:00
"support_pillar_connection_mode" ,
2019-02-05 11:16:03 +01:00
"support_buildplate_only" ,
2018-11-23 11:51:45 +01:00
"support_pillar_widening_factor" ,
2018-11-23 13:03:07 +01:00
"support_base_diameter" ,
2018-11-16 17:36:23 +01:00
"support_base_height" ,
2019-06-11 17:57:39 +02:00
"support_base_safety_distance" ,
2018-11-16 17:36:23 +01:00
"support_critical_angle" ,
"support_max_bridge_length" ,
2019-03-08 11:39:34 +01:00
"support_max_pillar_link_distance" ,
2018-11-19 17:58:08 +01:00
"support_object_elevation" ,
2019-02-19 16:34:52 +01:00
"support_points_density_relative" ,
"support_points_minimal_distance" ,
2019-03-01 17:53:02 +01:00
"slice_closing_radius" ,
2018-11-20 16:12:04 +01:00
"pad_enable" ,
2018-11-16 17:36:23 +01:00
"pad_wall_thickness" ,
"pad_wall_height" ,
2019-09-24 15:15:49 +02:00
"pad_brim_size" ,
2018-11-16 17:36:23 +01:00
"pad_max_merge_distance" ,
2019-08-06 16:51:32 +02:00
// "pad_edge_radius",
2019-02-25 16:04:46 +01:00
"pad_wall_slope" ,
2019-06-12 13:15:42 +02:00
"pad_object_gap" ,
2019-08-28 11:32:49 +02:00
"pad_around_object" ,
2019-09-24 15:15:49 +02:00
"pad_around_object_everywhere" ,
2019-06-11 17:57:39 +02:00
"pad_object_connector_stride" ,
"pad_object_connector_width" ,
"pad_object_connector_penetration" ,
2019-11-06 13:38:43 +01:00
"hollowing_enable" ,
"hollowing_min_thickness" ,
2019-11-08 09:21:30 +01:00
"hollowing_quality" ,
2019-11-08 16:51:43 +01:00
"hollowing_closing_distance" ,
2019-08-06 16:51:32 +02:00
"output_filename_format" ,
2018-11-16 17:36:23 +01:00
"default_sla_print_profile" ,
"compatible_printers" ,
2019-08-06 16:51:32 +02:00
"compatible_printers_condition" ,
2018-11-16 17:36:23 +01:00
"inherits"
};
}
return s_opts ;
}
2018-12-03 13:14:28 +01:00
const std :: vector < std :: string >& Preset :: sla_material_options ()
2019-08-06 16:51:32 +02:00
{
2018-12-03 13:14:28 +01:00
static std :: vector < std :: string > s_opts ;
if ( s_opts . empty ()) {
s_opts = {
2019-08-02 16:15:27 +02:00
"material_type" ,
2018-12-03 13:14:28 +01:00
"initial_layer_height" ,
2019-09-26 16:33:55 +02:00
"bottle_cost" ,
"bottle_volume" ,
"bottle_weight" ,
"material_density" ,
2019-08-20 17:24:48 +02:00
"exposure_time" ,
"initial_exposure_time" ,
2019-04-03 17:34:46 +02:00
"material_correction" ,
2018-12-03 13:14:28 +01:00
"material_notes" ,
2019-04-12 16:18:46 +02:00
"material_vendor" ,
2018-12-03 13:14:28 +01:00
"default_sla_material_profile" ,
2019-08-06 16:51:32 +02:00
"compatible_prints" , "compatible_prints_condition" ,
2018-12-04 17:56:49 +01:00
"compatible_printers" , "compatible_printers_condition" , "inherits"
2018-12-03 13:14:28 +01:00
};
}
return s_opts ;
}
const std :: vector < std :: string >& Preset :: sla_printer_options ()
2019-08-06 16:51:32 +02:00
{
2018-12-03 13:14:28 +01:00
static std :: vector < std :: string > s_opts ;
if ( s_opts . empty ()) {
s_opts = {
"printer_technology" ,
2019-07-18 14:39:19 +02:00
"bed_shape" , "bed_custom_texture" , "bed_custom_model" , "max_print_height" ,
2018-12-03 13:14:28 +01:00
"display_width" , "display_height" , "display_pixels_x" , "display_pixels_y" ,
2019-05-18 23:21:59 +02:00
"display_mirror_x" , "display_mirror_y" ,
2018-12-13 12:42:45 +01:00
"display_orientation" ,
2019-02-18 16:04:55 +01:00
"fast_tilt_time" , "slow_tilt_time" , "area_fill" ,
2019-04-09 13:42:32 +02:00
"relative_correction" ,
"absolute_correction" ,
2020-02-06 14:09:48 +01:00
"elefant_foot_compensation" ,
2020-02-06 16:28:02 +01:00
"elefant_foot_min_width" ,
2019-04-09 13:42:32 +02:00
"gamma_correction" ,
2019-08-20 17:24:48 +02:00
"min_exposure_time" , "max_exposure_time" ,
"min_initial_exposure_time" , "max_initial_exposure_time" ,
2020-10-28 09:51:05 +01:00
//FIXME the print host keys are left here just for conversion from the Printer preset to Physical Printer preset.
2018-12-18 14:34:16 +01:00
"print_host" , "printhost_apikey" , "printhost_cafile" ,
2018-12-03 13:14:28 +01:00
"printer_notes" ,
"inherits"
};
}
return s_opts ;
}
2018-08-03 14:14:25 +02:00
PresetCollection :: PresetCollection ( Preset :: Type type , const std :: vector < std :: string > & keys , const Slic3r :: StaticPrintConfig & defaults , const std :: string & default_name ) :
2017-09-19 13:55:48 +02:00
m_type ( type ),
2017-10-25 12:53:31 +02:00
m_edited_preset ( type , "" , false ),
2020-06-16 12:57:49 +02:00
m_idx_selected ( 0 )
2017-09-19 13:55:48 +02:00
{
// Insert just the default preset.
2018-08-03 14:14:25 +02:00
this -> add_default_preset ( keys , defaults , default_name );
2017-10-25 12:53:31 +02:00
m_edited_preset . config . apply ( m_presets . front (). config );
}
2017-12-19 19:51:22 +01:00
void PresetCollection :: reset ( bool delete_files )
{
2018-08-01 11:09:51 +02:00
if ( m_presets . size () > m_num_default_presets ) {
2017-12-19 19:51:22 +01:00
if ( delete_files ) {
// Erase the preset files.
for ( Preset & preset : m_presets )
2018-03-09 16:37:33 +01:00
if ( ! preset . is_default && ! preset . is_external && ! preset . is_system )
2017-12-19 19:51:22 +01:00
boost :: nowide :: remove ( preset . file . c_str ());
}
// Don't use m_presets.resize() here as it requires a default constructor for Preset.
2018-08-01 11:09:51 +02:00
m_presets . erase ( m_presets . begin () + m_num_default_presets , m_presets . end ());
2017-12-19 19:51:22 +01:00
this -> select_preset ( 0 );
}
2020-01-23 16:04:17 +01:00
m_map_alias_to_profile_name . clear ();
2020-01-02 16:40:32 +01:00
m_map_system_profile_renamed . clear ();
2017-12-19 19:51:22 +01:00
}
2018-08-03 14:14:25 +02:00
void PresetCollection :: add_default_preset ( const std :: vector < std :: string > & keys , const Slic3r :: StaticPrintConfig & defaults , const std :: string & preset_name )
2018-08-01 11:09:51 +02:00
{
// Insert just the default preset.
m_presets . emplace_back ( Preset ( this -> type (), preset_name , true ));
2019-08-06 16:51:32 +02:00
m_presets . back (). config . apply_only ( defaults , keys . empty () ? defaults . keys () : keys );
m_presets . back (). loaded = true ;
2018-08-01 11:09:51 +02:00
++ m_num_default_presets ;
}
2017-10-02 17:35:00 +02:00
// Load all presets found in dir_path.
// Throws an exception on error.
void PresetCollection :: load_presets ( const std :: string & dir_path , const std :: string & subdir )
{
2020-09-01 14:15:19 +02:00
// Don't use boost::filesystem::canonical() on Windows, it is broken in regard to reparse points,
// see https://github.com/prusa3d/PrusaSlicer/issues/732
boost :: filesystem :: path dir = boost :: filesystem :: absolute ( boost :: filesystem :: path ( dir_path ) / subdir ). make_preferred ();
2019-08-06 16:51:32 +02:00
m_dir_path = dir . string ();
2017-11-28 10:08:01 +01:00
std :: string errors_cummulative ;
2019-08-06 16:51:32 +02:00
// Store the loaded presets into a new vector, otherwise the binary search for already existing presets would be broken.
// (see the "Preset already present, not loading" message).
std :: deque < Preset > presets_loaded ;
for ( auto & dir_entry : boost :: filesystem :: directory_iterator ( dir ))
2019-02-03 15:30:37 +01:00
if ( Slic3r :: is_ini_file ( dir_entry )) {
2017-11-02 16:40:25 +01:00
std :: string name = dir_entry . path (). filename (). string ();
2017-10-02 17:35:00 +02:00
// Remove the .ini suffix.
name . erase ( name . size () - 4 );
2018-03-09 16:37:33 +01:00
if ( this -> find_preset ( name , false )) {
2018-05-01 10:35:07 +02:00
// This happens when there's is a preset (most likely legacy one) with the same name as a system preset
// that's already been loaded from a bundle.
BOOST_LOG_TRIVIAL ( warning ) << "Preset already present, not loading: " << name ;
2018-03-09 16:37:33 +01:00
continue ;
}
2017-10-02 17:35:00 +02:00
try {
Preset preset ( m_type , name , false );
2017-11-02 16:40:25 +01:00
preset . file = dir_entry . path (). string ();
2018-10-31 16:22:36 +01:00
// Load the preset file, apply preset values on top of defaults.
try {
DynamicPrintConfig config ;
config . load_from_ini ( preset . file );
// Find a default preset for the config. The PrintPresetCollection provides different default preset based on the "printer_technology" field.
2019-08-06 16:51:32 +02:00
const Preset & default_preset = this -> default_preset_for ( config );
2018-10-31 16:22:36 +01:00
preset . config = default_preset . config ;
preset . config . apply ( std :: move ( config ));
Preset :: normalize ( preset . config );
// Report configuration fields, which are misplaced into a wrong group.
2019-08-06 16:51:32 +02:00
std :: string incorrect_keys = Preset :: remove_invalid_keys ( config , default_preset . config );
2018-10-31 16:22:36 +01:00
if ( ! incorrect_keys . empty ())
BOOST_LOG_TRIVIAL ( error ) << "Error in a preset file: The preset \" " <<
preset . file << " \" contains the following incorrect keys: " << incorrect_keys << ", which were removed" ;
preset . loaded = true ;
} catch ( const std :: ifstream :: failure & err ) {
2020-09-14 16:27:55 +02:00
throw Slic3r :: RuntimeError ( std :: string ( "The selected preset cannot be loaded: " ) + preset . file + " \n\t Reason: " + err . what ());
2018-10-31 16:22:36 +01:00
} catch ( const std :: runtime_error & err ) {
2020-09-14 16:27:55 +02:00
throw Slic3r :: RuntimeError ( std :: string ( "Failed loading the preset file: " ) + preset . file + " \n\t Reason: " + err . what ());
2018-10-31 16:22:36 +01:00
}
2019-08-06 16:51:32 +02:00
presets_loaded . emplace_back ( preset );
2017-11-28 10:08:01 +01:00
} catch ( const std :: runtime_error & err ) {
errors_cummulative += err . what ();
errors_cummulative += " \n " ;
2019-08-06 16:51:32 +02:00
}
2017-10-02 17:35:00 +02:00
}
2019-08-06 16:51:32 +02:00
m_presets . insert ( m_presets . end (), std :: make_move_iterator ( presets_loaded . begin ()), std :: make_move_iterator ( presets_loaded . end ()));
2018-08-01 11:09:51 +02:00
std :: sort ( m_presets . begin () + m_num_default_presets , m_presets . end ());
2017-10-26 17:17:39 +02:00
this -> select_preset ( first_visible_idx ());
2017-11-28 10:08:01 +01:00
if ( ! errors_cummulative . empty ())
2020-09-14 16:27:55 +02:00
throw Slic3r :: RuntimeError ( errors_cummulative );
2017-10-25 12:53:31 +02:00
}
// Load a preset from an already parsed config file, insert it into the sorted sequence of presets
// and select it, losing previous modifications.
Preset & PresetCollection :: load_preset ( const std :: string & path , const std :: string & name , const DynamicPrintConfig & config , bool select )
2017-11-02 16:21:34 +01:00
{
DynamicPrintConfig cfg ( this -> default_preset (). config );
2017-11-03 19:14:33 +01:00
cfg . apply_only ( config , cfg . keys (), true );
2018-06-22 14:17:03 +02:00
return this -> load_preset ( path , name , std :: move ( cfg ), select );
}
2020-02-18 14:13:48 +01:00
static bool profile_print_params_same ( const DynamicPrintConfig & cfg_old , const DynamicPrintConfig & cfg_new )
{
t_config_option_keys diff = cfg_old . diff ( cfg_new );
2018-06-26 11:31:01 +02:00
// Following keys are used by the UI, not by the slicing core, therefore they are not important
// when comparing profiles for equality. Ignore them.
2019-08-06 16:51:32 +02:00
for ( const char * key : { "compatible_prints" , "compatible_prints_condition" ,
"compatible_printers" , "compatible_printers_condition" , "inherits" ,
2018-12-03 13:14:28 +01:00
"print_settings_id" , "filament_settings_id" , "sla_print_settings_id" , "sla_material_settings_id" , "printer_settings_id" ,
2018-12-06 14:42:15 +01:00
"printer_model" , "printer_variant" , "default_print_profile" , "default_filament_profile" , "default_sla_print_profile" , "default_sla_material_profile" ,
2020-10-28 09:51:05 +01:00
//FIXME remove the print host keys?
2020-02-18 14:13:48 +01:00
"print_host" , "printhost_apikey" , "printhost_cafile" })
2018-06-26 11:31:01 +02:00
diff . erase ( std :: remove ( diff . begin (), diff . end (), key ), diff . end ());
// Preset with the same name as stored inside the config exists.
2020-10-28 09:51:05 +01:00
return diff . empty ();
2018-06-26 11:31:01 +02:00
}
2018-06-22 14:17:03 +02:00
// Load a preset from an already parsed config file, insert it into the sorted sequence of presets
// and select it, losing previous modifications.
2020-12-04 10:48:44 +01:00
// Only a single profile could be edited at at the same time, which introduces complexity when loading
// filament profiles for multi-extruder printers.
std :: pair < Preset * , bool > PresetCollection :: load_external_preset (
2018-06-22 14:17:03 +02:00
// Path to the profile source file (a G-code, an AMF or 3MF file, a config file)
const std :: string & path ,
// Name of the profile, derived from the source file name.
const std :: string & name ,
// Original name of the profile, extracted from the loaded config. Empty, if the name has not been stored.
const std :: string & original_name ,
2020-12-05 08:14:04 +01:00
// Config to initialize the preset from. It may contain configs of all presets merged in a single dictionary!
const DynamicPrintConfig & combined_config ,
2018-06-22 14:17:03 +02:00
// Select the preset after loading?
2020-12-04 10:48:44 +01:00
LoadAndSelect select )
2018-06-22 14:17:03 +02:00
{
// Load the preset over a default preset, so that the missing fields are filled in from the default preset.
2020-12-05 08:14:04 +01:00
DynamicPrintConfig cfg ( this -> default_preset_for ( combined_config ). config );
const auto & keys = cfg . keys ();
cfg . apply_only ( combined_config , keys , true );
2020-12-04 10:48:44 +01:00
std :: string & inherits = Preset :: inherits ( cfg );
if ( select == LoadAndSelect :: Never ) {
// Some filament profile has been selected and modified already.
// Check whether this profile is equal to the modified edited profile.
const Preset & edited = this -> get_edited_preset ();
if (( edited . name == original_name || edited . name == inherits ) && profile_print_params_same ( edited . config , cfg ))
// Just point to that already selected and edited profile.
return std :: make_pair ( & ( * this -> find_preset_internal ( edited . name )), false );
}
2018-06-22 14:17:03 +02:00
// Is there a preset already loaded with the name stored inside the config?
2020-12-04 10:48:44 +01:00
std :: deque < Preset >:: iterator it = this -> find_preset_internal ( original_name );
bool found = it != m_presets . end () && it -> name == original_name ;
2020-01-02 16:40:32 +01:00
if ( ! found ) {
// Try to match the original_name against the "renamed_from" profile names of loaded system profiles.
it = this -> find_preset_renamed ( original_name );
found = it != m_presets . end ();
}
2020-10-28 09:51:05 +01:00
if ( found && profile_print_params_same ( it -> config , cfg )) {
// The preset exists and it matches the values stored inside config.
2020-12-04 10:48:44 +01:00
if ( select == LoadAndSelect :: Always )
2020-10-28 09:51:05 +01:00
this -> select_preset ( it - m_presets . begin ());
2020-12-04 10:48:44 +01:00
return std :: make_pair ( & ( * it ), false );
2018-06-26 11:31:01 +02:00
}
2020-12-04 10:48:44 +01:00
if ( ! found && select != LoadAndSelect :: Never && ! inherits . empty ()) {
// Try to use a system profile as a base to select the system profile
// and override its settings with the loaded ones.
assert ( it == m_presets . end ());
it = this -> find_preset_internal ( inherits );
found = it != m_presets . end () && it -> name == inherits ;
if ( found && profile_print_params_same ( it -> config , cfg )) {
// The system preset exists and it matches the values stored inside config.
if ( select == LoadAndSelect :: Always )
this -> select_preset ( it - m_presets . begin ());
return std :: make_pair ( & ( * it ), false );
}
2018-06-27 16:57:42 +02:00
}
2020-12-04 10:48:44 +01:00
if ( found ) {
if ( select != LoadAndSelect :: Never ) {
// Select the existing preset and override it with new values, so that
// the differences will be shown in the preset editor against the referenced profile.
this -> select_preset ( it - m_presets . begin ());
2020-12-05 08:14:04 +01:00
// The source config may contain keys from many possible preset types. Just copy those that relate to this preset.
this -> get_edited_preset (). config . apply_only ( combined_config , keys , true );
2020-12-04 10:48:44 +01:00
this -> update_dirty ();
assert ( this -> get_edited_preset (). is_dirty );
return std :: make_pair ( & ( * it ), this -> get_edited_preset (). is_dirty );
}
if ( inherits . empty ()) {
// Update the "inherits" field.
// There is a profile with the same name already loaded. Should we update the "inherits" field?
inherits = it -> vendor ? it -> name : it -> inherits ();
}
}
2018-06-26 11:31:01 +02:00
// The external preset does not match an internal preset, load the external preset.
std :: string new_name ;
for ( size_t idx = 0 ;; ++ idx ) {
std :: string suffix ;
if ( original_name . empty ()) {
if ( idx > 0 )
suffix = " (" + std :: to_string ( idx ) + ")" ;
} else {
if ( idx == 0 )
2019-08-06 16:51:32 +02:00
suffix = " (" + original_name + ")" ;
2018-06-26 11:31:01 +02:00
else
suffix = " (" + original_name + "-" + std :: to_string ( idx ) + ")" ;
}
new_name = name + suffix ;
it = this -> find_preset_internal ( new_name );
2018-06-28 20:13:01 +02:00
if ( it == m_presets . end () || it -> name != new_name )
2018-06-26 11:31:01 +02:00
// Unique profile name. Insert a new profile.
break ;
if ( profile_print_params_same ( it -> config , cfg )) {
2018-06-22 14:17:03 +02:00
// The preset exists and it matches the values stored inside config.
2020-12-04 10:48:44 +01:00
if ( select == LoadAndSelect :: Always )
2018-06-22 14:17:03 +02:00
this -> select_preset ( it - m_presets . begin ());
2020-12-04 10:48:44 +01:00
return std :: make_pair ( & ( * it ), false );
2018-06-22 14:17:03 +02:00
}
2018-06-26 11:31:01 +02:00
// Form another profile name.
2018-06-22 14:17:03 +02:00
}
2018-06-26 11:31:01 +02:00
// Insert a new profile.
2020-12-04 10:48:44 +01:00
Preset & preset = this -> load_preset ( path , new_name , std :: move ( cfg ), select == LoadAndSelect :: Always );
2018-06-22 14:17:03 +02:00
preset . is_external = true ;
2018-07-17 10:41:17 +02:00
if ( & this -> get_selected_preset () == & preset )
this -> get_edited_preset (). is_external = true ;
2020-12-04 10:48:44 +01:00
return std :: make_pair ( & preset , false );
2017-11-02 16:21:34 +01:00
}
Preset & PresetCollection :: load_preset ( const std :: string & path , const std :: string & name , DynamicPrintConfig && config , bool select )
2017-10-25 12:53:31 +02:00
{
2017-12-06 16:47:53 +01:00
auto it = this -> find_preset_internal ( name );
if ( it == m_presets . end () || it -> name != name ) {
// The preset was not found. Create a new preset.
2017-10-25 12:53:31 +02:00
it = m_presets . emplace ( it , Preset ( m_type , name , false ));
2017-12-06 16:47:53 +01:00
}
2017-10-25 12:53:31 +02:00
Preset & preset = * it ;
preset . file = path ;
2017-11-02 16:21:34 +01:00
preset . config = std :: move ( config );
2017-10-25 12:53:31 +02:00
preset . loaded = true ;
2017-11-02 16:21:34 +01:00
preset . is_dirty = false ;
2017-10-25 12:53:31 +02:00
if ( select )
this -> select_preset_by_name ( name , true );
return preset ;
}
2020-03-06 17:41:00 +01:00
void PresetCollection :: save_current_preset ( const std :: string & new_name , bool detach )
2017-10-26 17:17:39 +02:00
{
2019-08-06 16:51:32 +02:00
// 1) Find the preset with a new_name or create a new one,
// initialize it with the edited config.
2017-12-06 16:47:53 +01:00
auto it = this -> find_preset_internal ( new_name );
if ( it != m_presets . end () && it -> name == new_name ) {
2017-10-26 17:17:39 +02:00
// Preset with the same name found.
Preset & preset = * it ;
2018-03-12 16:04:32 +01:00
if ( preset . is_default || preset . is_external || preset . is_system )
2017-10-26 17:17:39 +02:00
// Cannot overwrite the default preset.
return ;
// Overwriting an existing preset.
preset . config = std :: move ( m_edited_preset . config );
2019-08-06 16:51:32 +02:00
// The newly saved preset will be activated -> make it visible.
preset . is_visible = true ;
2020-03-06 17:41:00 +01:00
if ( detach ) {
// Clear the link to the parent profile.
preset . vendor = nullptr ;
preset . inherits (). clear ();
preset . alias . clear ();
preset . renamed_from . clear ();
}
2017-10-26 17:17:39 +02:00
} else {
// Creating a new preset.
2019-08-06 16:51:32 +02:00
Preset & preset = * m_presets . insert ( it , m_edited_preset );
2018-06-27 16:57:42 +02:00
std :: string & inherits = preset . inherits ();
2018-03-12 16:04:32 +01:00
std :: string old_name = preset . name ;
2017-10-26 17:17:39 +02:00
preset . name = new_name ;
2019-08-06 16:51:32 +02:00
preset . file = this -> path_from_name ( new_name );
2018-03-12 16:04:32 +01:00
preset . vendor = nullptr ;
2020-03-06 17:41:00 +01:00
preset . alias . clear ();
preset . renamed_from . clear ();
if ( detach ) {
// Clear the link to the parent profile.
inherits . clear ();
} else if ( preset . is_system ) {
2018-03-12 16:04:32 +01:00
// Inheriting from a system preset.
inherits = /* preset.vendor->name + "/" + */ old_name ;
} else if ( inherits . empty ()) {
// Inheriting from a user preset. Link the new preset to the old preset.
2018-03-13 09:45:04 +01:00
// inherits = old_name;
2018-03-12 16:04:32 +01:00
} else {
2019-08-06 16:51:32 +02:00
// Inherited from a user preset. Just maintain the "inherited" flag,
2018-03-12 16:04:32 +01:00
// meaning it will inherit from either the system preset, or the inherited user preset.
}
preset . is_default = false ;
preset . is_system = false ;
preset . is_external = false ;
2019-08-06 16:51:32 +02:00
// The newly saved preset will be activated -> make it visible.
preset . is_visible = true ;
2019-11-29 11:02:30 +01:00
// Just system presets have aliases
preset . alias . clear ();
2019-08-06 16:51:32 +02:00
}
// 2) Activate the saved preset.
this -> select_preset_by_name ( new_name , true );
// 2) Store the active preset to disk.
this -> get_selected_preset (). save ();
2017-10-26 17:17:39 +02:00
}
2019-03-22 12:11:23 +01:00
bool PresetCollection :: delete_current_preset ()
2017-10-26 17:17:39 +02:00
{
const Preset & selected = this -> get_selected_preset ();
2019-08-06 16:51:32 +02:00
if ( selected . is_default )
return false ;
if ( ! selected . is_external && ! selected . is_system ) {
// Erase the preset file.
boost :: nowide :: remove ( selected . file . c_str ());
}
2017-10-26 17:17:39 +02:00
// Remove the preset from the list.
m_presets . erase ( m_presets . begin () + m_idx_selected );
// Find the next visible preset.
2017-11-02 16:21:34 +01:00
size_t new_selected_idx = m_idx_selected ;
if ( new_selected_idx < m_presets . size ())
for (; new_selected_idx < m_presets . size () && ! m_presets [ new_selected_idx ]. is_visible ; ++ new_selected_idx ) ;
if ( new_selected_idx == m_presets . size ())
2019-08-06 16:51:32 +02:00
for ( -- new_selected_idx ; new_selected_idx > 0 && ! m_presets [ new_selected_idx ]. is_visible ; -- new_selected_idx );
2017-11-02 16:21:34 +01:00
this -> select_preset ( new_selected_idx );
2019-08-06 16:51:32 +02:00
return true ;
2017-10-26 17:17:39 +02:00
}
2019-10-02 22:50:55 +02:00
bool PresetCollection :: delete_preset ( const std :: string & name )
{
auto it = this -> find_preset_internal ( name );
const Preset & preset = * it ;
if ( preset . is_default )
return false ;
if ( ! preset . is_external && ! preset . is_system ) {
// Erase the preset file.
boost :: nowide :: remove ( preset . file . c_str ());
}
m_presets . erase ( it );
return true ;
}
2018-03-12 16:04:32 +01:00
const Preset * PresetCollection :: get_selected_preset_parent () const
{
2020-04-22 12:49:52 +02:00
if ( this -> get_selected_idx () == size_t ( - 1 ))
2019-05-15 17:15:52 +02:00
// This preset collection has no preset activated yet. Only the get_edited_preset() is valid.
return nullptr ;
2019-07-01 14:56:28 +02:00
2020-01-02 16:40:32 +01:00
const Preset & selected_preset = this -> get_selected_preset ();
if ( selected_preset . is_system || selected_preset . is_default )
return & selected_preset ;
const Preset & edited_preset = this -> get_edited_preset ();
const std :: string & inherits = edited_preset . inherits ();
const Preset * preset = nullptr ;
if ( inherits . empty ()) {
if ( selected_preset . is_external )
2019-07-01 14:56:28 +02:00
return nullptr ;
2020-01-02 16:40:32 +01:00
preset = & this -> default_preset ( m_type == Preset :: Type :: TYPE_PRINTER && edited_preset . printer_technology () == ptSLA ? 1 : 0 );
} else
preset = this -> find_preset ( inherits , false );
if ( preset == nullptr ) {
// Resolve the "renamed_from" field.
assert ( ! inherits . empty ());
auto it = this -> find_preset_renamed ( inherits );
if ( it != m_presets . end ())
preset = & ( * it );
2019-07-01 14:56:28 +02:00
}
return ( preset == nullptr /* || preset->is_default*/ || preset -> is_external ) ? nullptr : preset ;
2018-03-12 16:04:32 +01:00
}
2018-04-09 16:50:17 +02:00
const Preset * PresetCollection :: get_preset_parent ( const Preset & child ) const
{
2018-06-27 16:57:42 +02:00
const std :: string & inherits = child . inherits ();
if ( inherits . empty ())
2019-08-06 16:51:32 +02:00
// return this->get_selected_preset().is_system ? &this->get_selected_preset() : nullptr;
return nullptr ;
2018-06-27 16:57:42 +02:00
const Preset * preset = this -> find_preset ( inherits , false );
2020-01-02 16:40:32 +01:00
if ( preset == nullptr ) {
auto it = this -> find_preset_renamed ( inherits );
if ( it != m_presets . end ())
preset = & ( * it );
}
2020-12-07 17:09:02 +01:00
return
// not found
( preset == nullptr /* || preset->is_default */ ||
// this should not happen, user profile should not derive from an external profile
preset -> is_external ||
// this should not happen, however people are creative, see GH #4996
preset == & child ) ?
nullptr :
preset ;
2018-04-09 16:50:17 +02:00
}
2019-12-05 14:48:11 +01:00
// Return vendor of the first parent profile, for which the vendor is defined, or null if such profile does not exist.
PresetWithVendorProfile PresetCollection :: get_preset_with_vendor_profile ( const Preset & preset ) const
{
const Preset * p = & preset ;
const VendorProfile * v = nullptr ;
do {
if ( p -> vendor != nullptr ) {
v = p -> vendor ;
break ;
}
p = this -> get_preset_parent ( * p );
} while ( p != nullptr );
return PresetWithVendorProfile ( preset , v );
}
2019-11-29 11:02:30 +01:00
const std :: string & PresetCollection :: get_preset_name_by_alias ( const std :: string & alias ) const
2019-11-25 15:57:36 +01:00
{
2020-01-23 16:04:17 +01:00
for (
// Find the 1st profile name with the alias.
auto it = Slic3r :: lower_bound_by_predicate ( m_map_alias_to_profile_name . begin (), m_map_alias_to_profile_name . end (), [ & alias ]( auto & l ){ return l . first < alias ; });
// Continue over all profile names with the same alias.
it != m_map_alias_to_profile_name . end () && it -> first == alias ; ++ it )
if ( auto it_preset = this -> find_preset_internal ( it -> second );
it_preset != m_presets . end () && it_preset -> name == it -> second &&
2020-03-03 14:52:16 +01:00
it_preset -> is_visible && ( it_preset -> is_compatible || size_t ( it_preset - m_presets . begin ()) == m_idx_selected ))
2020-01-23 16:04:17 +01:00
return it_preset -> name ;
2019-11-25 15:57:36 +01:00
return alias ;
}
2020-03-14 18:35:42 +01:00
const std :: string * PresetCollection :: get_preset_name_renamed ( const std :: string & old_name ) const
{
auto it_renamed = m_map_system_profile_renamed . find ( old_name );
if ( it_renamed != m_map_system_profile_renamed . end ())
return & it_renamed -> second ;
return nullptr ;
}
2018-04-06 15:42:52 +02:00
const std :: string & PresetCollection :: get_suffix_modified () {
2019-08-06 16:51:32 +02:00
return g_suffix_modified ;
2018-04-06 15:42:52 +02:00
}
2017-10-25 12:53:31 +02:00
// Return a preset by its name. If the preset is active, a temporary copy is returned.
// If a preset is not found by its name, null is returned.
Preset * PresetCollection :: find_preset ( const std :: string & name , bool first_visible_if_not_found )
{
Preset key ( m_type , name , false );
2017-12-06 16:47:53 +01:00
auto it = this -> find_preset_internal ( name );
2017-10-25 12:53:31 +02:00
// Ensure that a temporary copy is returned if the preset found is currently selected.
2019-08-06 16:51:32 +02:00
return ( it != m_presets . end () && it -> name == key . name ) ? & this -> preset ( it - m_presets . begin ()) :
2017-10-25 12:53:31 +02:00
first_visible_if_not_found ? & this -> first_visible () : nullptr ;
}
// Return index of the first visible preset. Certainly at least the '- default -' preset shall be visible.
size_t PresetCollection :: first_visible_idx () const
{
2018-08-01 11:09:51 +02:00
size_t idx = m_default_suppressed ? m_num_default_presets : 0 ;
2017-10-25 12:53:31 +02:00
for (; idx < this -> m_presets . size (); ++ idx )
if ( m_presets [ idx ]. is_visible )
break ;
2018-09-10 09:11:49 +02:00
if ( idx == m_presets . size ())
2017-10-25 12:53:31 +02:00
idx = 0 ;
return idx ;
2017-10-02 17:35:00 +02:00
}
2017-09-19 13:55:48 +02:00
void PresetCollection :: set_default_suppressed ( bool default_suppressed )
{
if ( m_default_suppressed != default_suppressed ) {
m_default_suppressed = default_suppressed ;
2019-08-06 16:51:32 +02:00
bool default_visible = ! default_suppressed || m_idx_selected < m_num_default_presets ;
for ( size_t i = 0 ; i < m_num_default_presets ; ++ i )
m_presets [ i ]. is_visible = default_visible ;
2017-09-19 13:55:48 +02:00
}
}
2020-02-27 11:44:01 +01:00
size_t PresetCollection :: update_compatible_internal ( const PresetWithVendorProfile & active_printer , const PresetWithVendorProfile * active_print , PresetSelectCompatibleType unselect_if_incompatible )
2017-09-19 13:55:48 +02:00
{
2017-12-19 16:48:14 +01:00
DynamicPrintConfig config ;
2019-12-05 14:48:11 +01:00
config . set_key_value ( "printer_preset" , new ConfigOptionString ( active_printer . preset . name ));
const ConfigOption * opt = active_printer . preset . config . option ( "nozzle_diameter" );
2018-08-01 11:09:51 +02:00
if ( opt )
config . set_key_value ( "num_extruders" , new ConfigOptionInt (( int ) static_cast < const ConfigOptionFloats *> ( opt ) -> values . size ()));
2020-03-06 17:41:00 +01:00
bool some_compatible = false ;
2018-08-01 11:09:51 +02:00
for ( size_t idx_preset = m_num_default_presets ; idx_preset < m_presets . size (); ++ idx_preset ) {
2017-11-10 17:27:05 +01:00
bool selected = idx_preset == m_idx_selected ;
Preset & preset_selected = m_presets [ idx_preset ];
Preset & preset_edited = selected ? m_edited_preset : preset_selected ;
2019-11-19 12:44:55 +01:00
2019-12-05 14:48:11 +01:00
const PresetWithVendorProfile this_preset_with_vendor_profile = this -> get_preset_with_vendor_profile ( preset_edited );
2020-02-27 11:44:01 +01:00
bool was_compatible = preset_edited . is_compatible ;
2019-12-05 14:48:11 +01:00
preset_edited . is_compatible = is_compatible_with_printer ( this_preset_with_vendor_profile , active_printer , & config );
2020-03-06 17:41:00 +01:00
some_compatible |= preset_edited . is_compatible ;
2019-12-05 14:48:11 +01:00
if ( active_print != nullptr )
2020-02-27 11:44:01 +01:00
preset_edited . is_compatible &= is_compatible_with_print ( this_preset_with_vendor_profile , * active_print , active_printer );
if ( ! preset_edited . is_compatible && selected &&
( unselect_if_incompatible == PresetSelectCompatibleType :: Always || ( unselect_if_incompatible == PresetSelectCompatibleType :: OnlyIfWasCompatible && was_compatible )))
2019-11-19 12:44:55 +01:00
m_idx_selected = size_t ( - 1 );
2017-11-10 17:27:05 +01:00
if ( selected )
preset_selected . is_compatible = preset_edited . is_compatible ;
}
2020-03-06 17:41:00 +01:00
// Update visibility of the default profiles here if the defaults are suppressed, the current profile is not compatible and we don't want to select another compatible profile.
if ( m_idx_selected >= m_num_default_presets && m_default_suppressed )
for ( size_t i = 0 ; i < m_num_default_presets ; ++ i )
m_presets [ i ]. is_visible = ! some_compatible ;
2018-04-13 14:49:33 +02:00
return m_idx_selected ;
2017-09-19 13:55:48 +02:00
}
2017-10-25 12:53:31 +02:00
// Save the preset under a new name. If the name is different from the old one,
// a new preset is stored into the list of presets.
// All presets are marked as not modified and the new preset is activated.
//void PresetCollection::save_current_preset(const std::string &new_name);
// Delete the current preset, activate the first visible preset.
//void PresetCollection::delete_current_preset();
2017-09-19 13:55:48 +02:00
2020-06-16 12:57:49 +02:00
// Update a dirty flag of the current preset
2017-10-25 12:53:31 +02:00
// Return true if the dirty flag changed.
2020-06-16 12:57:49 +02:00
bool PresetCollection :: update_dirty ()
2017-10-25 12:53:31 +02:00
{
bool was_dirty = this -> get_selected_preset (). is_dirty ;
bool is_dirty = current_is_dirty ();
this -> get_selected_preset (). is_dirty = is_dirty ;
2017-11-01 19:30:05 +01:00
this -> get_edited_preset (). is_dirty = is_dirty ;
2020-06-16 12:57:49 +02:00
2017-10-25 12:53:31 +02:00
return was_dirty != is_dirty ;
}
2018-12-04 17:56:49 +01:00
template < class T >
void add_correct_opts_to_diff ( const std :: string & opt_key , t_config_option_keys & vec , const ConfigBase & other , const ConfigBase & this_c )
{
2019-08-06 16:51:32 +02:00
const T * opt_init = static_cast < const T *> ( other . option ( opt_key ));
const T * opt_cur = static_cast < const T *> ( this_c . option ( opt_key ));
int opt_init_max_id = opt_init -> values . size () - 1 ;
2019-11-19 12:44:55 +01:00
for ( int i = 0 ; i < int ( opt_cur -> values . size ()); i ++ )
2019-08-06 16:51:32 +02:00
{
int init_id = i <= opt_init_max_id ? i : 0 ;
if ( opt_cur -> values [ i ] != opt_init -> values [ init_id ])
vec . emplace_back ( opt_key + "#" + std :: to_string ( i ));
}
2018-12-04 17:56:49 +01:00
}
// Use deep_diff to correct return of changed options, considering individual options for each extruder.
inline t_config_option_keys deep_diff ( const ConfigBase & config_this , const ConfigBase & config_other )
{
t_config_option_keys diff ;
for ( const t_config_option_key & opt_key : config_this . keys ()) {
const ConfigOption * this_opt = config_this . option ( opt_key );
const ConfigOption * other_opt = config_other . option ( opt_key );
if ( this_opt != nullptr && other_opt != nullptr && * this_opt != * other_opt )
{
2020-02-24 10:41:20 +01:00
if ( opt_key == "bed_shape" || opt_key == "thumbnails" || opt_key == "compatible_prints" || opt_key == "compatible_printers" ) {
2021-01-04 11:13:10 +01:00
// Scalar variable, or a vector variable, which is independent from number of extruders,
// thus the vector is presented to the user as a single input.
2018-12-04 17:56:49 +01:00
diff . emplace_back ( opt_key );
2021-01-04 11:13:10 +01:00
} else if ( opt_key == "default_filament_profile" ) {
// Ignore this field, it is not presented to the user, therefore showing a "modified" flag for this parameter does not help.
// Also the length of this field may differ, which may lead to a crash if the block below is used.
} else {
switch ( other_opt -> type ()) {
case coInts : add_correct_opts_to_diff < ConfigOptionInts > ( opt_key , diff , config_other , config_this ); break ;
case coBools : add_correct_opts_to_diff < ConfigOptionBools > ( opt_key , diff , config_other , config_this ); break ;
case coFloats : add_correct_opts_to_diff < ConfigOptionFloats > ( opt_key , diff , config_other , config_this ); break ;
case coStrings : add_correct_opts_to_diff < ConfigOptionStrings > ( opt_key , diff , config_other , config_this ); break ;
case coPercents : add_correct_opts_to_diff < ConfigOptionPercents > ( opt_key , diff , config_other , config_this ); break ;
case coPoints : add_correct_opts_to_diff < ConfigOptionPoints > ( opt_key , diff , config_other , config_this ); break ;
default : diff . emplace_back ( opt_key ); break ;
}
2018-12-04 17:56:49 +01:00
}
}
}
return diff ;
}
2018-08-08 17:47:59 +02:00
std :: vector < std :: string > PresetCollection :: dirty_options ( const Preset * edited , const Preset * reference , const bool deep_compare /*= false*/ )
2018-03-12 16:04:32 +01:00
{
std :: vector < std :: string > changed ;
2019-08-06 16:51:32 +02:00
if ( edited != nullptr && reference != nullptr ) {
2018-08-08 17:47:59 +02:00
changed = deep_compare ?
2019-08-06 16:51:32 +02:00
deep_diff ( edited -> config , reference -> config ) :
reference -> config . diff ( edited -> config );
2018-03-12 16:04:32 +01:00
// The "compatible_printers" option key is handled differently from the others:
// It is not mandatory. If the key is missing, it means it is compatible with any printer.
// If the key exists and it is empty, it means it is compatible with no printer.
2018-12-04 17:56:49 +01:00
std :: initializer_list < const char *> optional_keys { "compatible_prints" , "compatible_printers" };
2018-03-12 16:04:32 +01:00
for ( auto & opt_key : optional_keys ) {
if ( reference -> config . has ( opt_key ) != edited -> config . has ( opt_key ))
changed . emplace_back ( opt_key );
}
2017-12-18 15:07:38 +01:00
}
2017-12-13 14:44:27 +01:00
return changed ;
}
2017-12-13 14:00:14 +01:00
// Select a new preset. This resets all the edits done to the currently selected preset.
// If the preset with index idx does not exist, a first visible preset is selected.
2017-10-25 12:53:31 +02:00
Preset & PresetCollection :: select_preset ( size_t idx )
{
2017-11-02 16:21:34 +01:00
for ( Preset & preset : m_presets )
preset . is_dirty = false ;
2017-10-25 12:53:31 +02:00
if ( idx >= m_presets . size ())
idx = first_visible_idx ();
m_idx_selected = idx ;
m_edited_preset = m_presets [ idx ];
2019-08-06 16:51:32 +02:00
bool default_visible = ! m_default_suppressed || m_idx_selected < m_num_default_presets ;
for ( size_t i = 0 ; i < m_num_default_presets ; ++ i )
m_presets [ i ]. is_visible = default_visible ;
2017-10-25 12:53:31 +02:00
return m_presets [ idx ];
}
2017-10-26 17:17:39 +02:00
bool PresetCollection :: select_preset_by_name ( const std :: string & name_w_suffix , bool force )
2019-08-06 16:51:32 +02:00
{
2017-10-30 18:41:50 +01:00
std :: string name = Preset :: remove_suffix_modified ( name_w_suffix );
2017-10-25 12:53:31 +02:00
// 1) Try to find the preset by its name.
2017-12-06 16:47:53 +01:00
auto it = this -> find_preset_internal ( name );
2017-10-25 12:53:31 +02:00
size_t idx = 0 ;
2019-08-06 16:51:32 +02:00
if ( it != m_presets . end () && it -> name == name && it -> is_visible )
2018-04-13 14:49:33 +02:00
// Preset found by its name and it is visible.
2017-10-25 12:53:31 +02:00
idx = it - m_presets . begin ();
else {
// Find the first visible preset.
2018-08-01 11:09:51 +02:00
for ( size_t i = m_default_suppressed ? m_num_default_presets : 0 ; i < m_presets . size (); ++ i )
2017-10-25 12:53:31 +02:00
if ( m_presets [ i ]. is_visible ) {
idx = i ;
break ;
}
// If the first visible preset was not found, return the 0th element, which is the default preset.
}
// 2) Select the new preset.
if ( m_idx_selected != idx || force ) {
this -> select_preset ( idx );
return true ;
}
return false ;
}
2018-04-13 14:49:33 +02:00
bool PresetCollection :: select_preset_by_name_strict ( const std :: string & name )
2019-08-06 16:51:32 +02:00
{
2018-04-13 14:49:33 +02:00
// 1) Try to find the preset by its name.
auto it = this -> find_preset_internal ( name );
size_t idx = ( size_t ) - 1 ;
2019-08-06 16:51:32 +02:00
if ( it != m_presets . end () && it -> name == name && it -> is_visible )
2018-04-13 14:49:33 +02:00
// Preset found by its name.
idx = it - m_presets . begin ();
// 2) Select the new preset.
if ( idx != ( size_t ) - 1 ) {
this -> select_preset ( idx );
return true ;
}
m_idx_selected = idx ;
return false ;
}
2018-04-18 13:35:51 +02:00
// Merge one vendor's presets with the other vendor's presets, report duplicates.
2019-06-17 16:39:22 +02:00
std :: vector < std :: string > PresetCollection :: merge_presets ( PresetCollection && other , const VendorMap & new_vendors )
2018-04-18 13:35:51 +02:00
{
std :: vector < std :: string > duplicates ;
for ( Preset & preset : other . m_presets ) {
if ( preset . is_default || preset . is_external )
continue ;
Preset key ( m_type , preset . name );
2018-08-01 11:09:51 +02:00
auto it = std :: lower_bound ( m_presets . begin () + m_num_default_presets , m_presets . end (), key );
2018-04-18 13:35:51 +02:00
if ( it == m_presets . end () || it -> name != preset . name ) {
if ( preset . vendor != nullptr ) {
// Re-assign a pointer to the vendor structure in the new PresetBundle.
2019-06-17 16:39:22 +02:00
auto it = new_vendors . find ( preset . vendor -> id );
2018-04-18 13:35:51 +02:00
assert ( it != new_vendors . end ());
2019-06-17 16:39:22 +02:00
preset . vendor = & it -> second ;
2018-04-18 13:35:51 +02:00
}
this -> m_presets . emplace ( it , std :: move ( preset ));
} else
duplicates . emplace_back ( std :: move ( preset . name ));
}
return duplicates ;
}
2021-02-04 09:42:32 +01:00
void PresetCollection :: update_vendor_ptrs_after_copy ( const VendorMap & new_vendors )
{
for ( Preset & preset : m_presets )
if ( preset . vendor != nullptr ) {
assert ( ! preset . is_default && ! preset . is_external );
// Re-assign a pointer to the vendor structure in the new PresetBundle.
auto it = new_vendors . find ( preset . vendor -> id );
assert ( it != new_vendors . end ());
preset . vendor = & it -> second ;
}
}
2020-01-23 16:04:17 +01:00
void PresetCollection :: update_map_alias_to_profile_name ()
{
m_map_alias_to_profile_name . clear ();
for ( const Preset & preset : m_presets )
m_map_alias_to_profile_name . emplace_back ( preset . alias , preset . name );
std :: sort ( m_map_alias_to_profile_name . begin (), m_map_alias_to_profile_name . end (), []( auto & l , auto & r ) { return l . first < r . first ; });
}
2020-01-02 16:40:32 +01:00
void PresetCollection :: update_map_system_profile_renamed ()
{
m_map_system_profile_renamed . clear ();
for ( Preset & preset : m_presets )
for ( const std :: string & renamed_from : preset . renamed_from ) {
const auto [ it , success ] = m_map_system_profile_renamed . insert ( std :: pair < std :: string , std :: string > ( renamed_from , preset . name ));
if ( ! success )
BOOST_LOG_TRIVIAL ( error ) << boost :: format ( "Preset name \" %1% \" was marked as renamed from \" %2% \" , though preset name \" %3% \" was marked as renamed from \" %2% \" as well." ) % preset . name % renamed_from % it -> second ;
}
}
2017-10-25 12:53:31 +02:00
std :: string PresetCollection :: name () const
{
switch ( this -> type ()) {
2019-01-21 12:34:28 +01:00
case Preset :: TYPE_PRINT : return L ( "print" );
case Preset :: TYPE_FILAMENT : return L ( "filament" );
case Preset :: TYPE_SLA_PRINT : return L ( "SLA print" );
2019-01-31 15:09:16 +01:00
case Preset :: TYPE_SLA_MATERIAL : return L ( "SLA material" );
2019-01-21 12:34:28 +01:00
case Preset :: TYPE_PRINTER : return L ( "printer" );
2018-12-04 17:56:49 +01:00
default : return "invalid" ;
2017-10-25 12:53:31 +02:00
}
}
2019-02-03 20:17:55 +01:00
std :: string PresetCollection :: section_name () const
{
switch ( this -> type ()) {
case Preset :: TYPE_PRINT : return "print" ;
case Preset :: TYPE_FILAMENT : return "filament" ;
case Preset :: TYPE_SLA_PRINT : return "sla_print" ;
case Preset :: TYPE_SLA_MATERIAL : return "sla_material" ;
case Preset :: TYPE_PRINTER : return "printer" ;
default : return "invalid" ;
}
}
2020-12-08 17:45:50 +01:00
// Used for validating the "inherits" flag when importing user's config bundles.
// Returns names of all system presets including the former names of these presets.
2019-01-08 10:52:11 +01:00
std :: vector < std :: string > PresetCollection :: system_preset_names () const
{
size_t num = 0 ;
for ( const Preset & preset : m_presets )
if ( preset . is_system )
++ num ;
std :: vector < std :: string > out ;
out . reserve ( num );
2019-08-06 16:51:32 +02:00
for ( const Preset & preset : m_presets )
2020-12-08 17:45:50 +01:00
if ( preset . is_system ) {
2019-08-06 16:51:32 +02:00
out . emplace_back ( preset . name );
2020-12-08 17:45:50 +01:00
out . insert ( out . end (), preset . renamed_from . begin (), preset . renamed_from . end ());
}
2019-01-08 10:52:11 +01:00
std :: sort ( out . begin (), out . end ());
return out ;
}
2017-12-20 11:28:16 +01:00
// Generate a file path from a profile name. Add the ".ini" suffix if it is missing.
std :: string PresetCollection :: path_from_name ( const std :: string & new_name ) const
{
2019-08-06 16:51:32 +02:00
std :: string file_name = boost :: iends_with ( new_name , ".ini" ) ? new_name : ( new_name + ".ini" );
2017-12-20 11:28:16 +01:00
return ( boost :: filesystem :: path ( m_dir_path ) / file_name ). make_preferred (). string ();
}
2019-08-06 16:51:32 +02:00
const Preset & PrinterPresetCollection :: default_preset_for ( const DynamicPrintConfig & config ) const
{
2018-10-31 16:22:36 +01:00
const ConfigOptionEnumGeneric * opt_printer_technology = config . opt < ConfigOptionEnumGeneric > ( "printer_technology" );
2019-08-06 16:51:32 +02:00
return this -> default_preset (( opt_printer_technology == nullptr || opt_printer_technology -> value == ptFFF ) ? 0 : 1 );
2018-10-31 16:22:36 +01:00
}
2019-04-26 16:53:04 +02:00
const Preset * PrinterPresetCollection :: find_by_model_id ( const std :: string & model_id ) const
{
if ( model_id . empty ()) { return nullptr ; }
const auto it = std :: find_if ( cbegin (), cend (), [ & ]( const Preset & preset ) {
return preset . config . opt_string ( "printer_model" ) == model_id ;
});
return it != cend () ? &* it : nullptr ;
}
2020-06-24 08:50:01 +02:00
// -------------------------
// *** PhysicalPrinter ***
// -------------------------
2020-07-14 15:34:08 +02:00
std :: string PhysicalPrinter :: separator ()
{
return " * " ;
}
2020-06-24 08:50:01 +02:00
const std :: vector < std :: string >& PhysicalPrinter :: printer_options ()
{
static std :: vector < std :: string > s_opts ;
if ( s_opts . empty ()) {
s_opts = {
"preset_name" ,
"printer_technology" ,
2020-10-01 22:48:00 +02:00
// "printer_model",
2020-10-28 09:51:05 +01:00
"host_type" ,
"print_host" ,
"printhost_apikey" ,
2020-06-25 12:58:59 +02:00
"printhost_cafile" ,
2020-10-28 09:51:05 +01:00
"printhost_port" ,
2020-09-08 15:30:59 +02:00
"printhost_authorization_type" ,
// HTTP digest authentization (RFC 2617)
2020-10-28 09:51:05 +01:00
"printhost_user" ,
2020-09-08 15:30:59 +02:00
"printhost_password"
2020-06-24 08:50:01 +02:00
};
}
return s_opts ;
2020-06-16 12:57:49 +02:00
}
2020-10-28 09:51:05 +01:00
static constexpr auto legacy_print_host_options = {
"print_host" ,
"printhost_apikey" ,
"printhost_cafile" ,
};
2020-07-21 16:21:18 +02:00
2020-07-28 11:31:16 +02:00
std :: vector < std :: string > PhysicalPrinter :: presets_with_print_host_information ( const PrinterPresetCollection & printer_presets )
2020-07-21 16:21:18 +02:00
{
2020-07-28 11:31:16 +02:00
std :: vector < std :: string > presets ;
2020-07-21 16:21:18 +02:00
for ( const Preset & preset : printer_presets )
if ( has_print_host_information ( preset . config ))
2020-07-28 11:31:16 +02:00
presets . emplace_back ( preset . name );
2020-07-21 16:21:18 +02:00
2020-07-28 11:31:16 +02:00
return presets ;
2020-07-21 16:21:18 +02:00
}
bool PhysicalPrinter :: has_print_host_information ( const DynamicPrintConfig & config )
{
2020-10-28 09:51:05 +01:00
for ( const char * opt : legacy_print_host_options )
2020-07-21 16:21:18 +02:00
if ( ! config . opt_string ( opt ). empty ())
return true ;
return false ;
}
2020-07-14 15:34:08 +02:00
const std :: set < std :: string >& PhysicalPrinter :: get_preset_names () const
2020-06-26 09:58:39 +02:00
{
2020-07-14 15:34:08 +02:00
return preset_names ;
2020-06-26 09:58:39 +02:00
}
2020-06-30 14:12:47 +02:00
bool PhysicalPrinter :: has_empty_config () const
{
2020-09-08 15:30:59 +02:00
return config . opt_string ( "print_host" ). empty () &&
config . opt_string ( "printhost_apikey" ). empty () &&
config . opt_string ( "printhost_cafile" ). empty () &&
2020-10-28 09:51:05 +01:00
config . opt_string ( "printhost_port" ). empty () &&
2020-09-08 15:30:59 +02:00
config . opt_string ( "printhost_user" ). empty () &&
config . opt_string ( "printhost_password" ). empty ();
2020-06-30 14:12:47 +02:00
}
2020-07-14 15:34:08 +02:00
void PhysicalPrinter :: update_preset_names_in_config ()
{
if ( ! preset_names . empty ()) {
std :: string name ;
for ( auto el : preset_names )
name += el + ";" ;
name . pop_back ();
config . set_key_value ( "preset_name" , new ConfigOptionString ( name ));
}
}
void PhysicalPrinter :: save ( const std :: string & file_name_from , const std :: string & file_name_to )
{
// rename the file
boost :: nowide :: rename ( file_name_from . data (), file_name_to . data ());
this -> file = file_name_to ;
// save configuration
this -> config . save ( this -> file );
}
2020-06-24 08:50:01 +02:00
void PhysicalPrinter :: update_from_preset ( const Preset & preset )
{
2020-09-08 15:30:59 +02:00
config . apply_only ( preset . config , printer_options (), true );
2020-07-14 15:34:08 +02:00
// add preset names to the options list
2020-10-06 12:32:11 +02:00
preset_names . emplace ( preset . name );
2020-07-14 15:34:08 +02:00
update_preset_names_in_config ();
2020-06-24 08:50:01 +02:00
}
void PhysicalPrinter :: update_from_config ( const DynamicPrintConfig & new_config )
{
config . apply_only ( new_config , printer_options (), false );
2020-07-14 15:34:08 +02:00
std :: string str = config . opt_string ( "preset_name" );
std :: set < std :: string > values {};
if ( ! str . empty ()) {
boost :: split ( values , str , boost :: is_any_of ( ";" ));
for ( const std :: string & val : values )
preset_names . emplace ( val );
}
preset_names = values ;
2020-06-24 08:50:01 +02:00
}
2020-07-14 15:34:08 +02:00
void PhysicalPrinter :: reset_presets ()
{
return preset_names . clear ();
}
bool PhysicalPrinter :: add_preset ( const std :: string & preset_name )
{
return preset_names . emplace ( preset_name ). second ;
}
2020-07-20 14:56:09 +02:00
bool PhysicalPrinter :: delete_preset ( const std :: string & preset_name )
{
return preset_names . erase ( preset_name ) > 0 ;
}
2020-12-09 12:46:26 +01:00
PhysicalPrinter :: PhysicalPrinter ( const std :: string & name , const DynamicPrintConfig & default_config ) :
name ( name ), config ( default_config )
{
update_from_config ( config );
}
2020-09-08 15:30:59 +02:00
PhysicalPrinter :: PhysicalPrinter ( const std :: string & name , const DynamicPrintConfig & default_config , const Preset & preset ) :
name ( name ), config ( default_config )
2020-06-24 08:50:01 +02:00
{
update_from_preset ( preset );
}
2020-06-26 09:58:39 +02:00
void PhysicalPrinter :: set_name ( const std :: string & name )
{
this -> name = name ;
}
2020-07-17 14:32:38 +02:00
std :: string PhysicalPrinter :: get_full_name ( std :: string preset_name ) const
2020-06-26 09:58:39 +02:00
{
2020-07-17 14:32:38 +02:00
return name + separator () + preset_name ;
2020-06-26 09:58:39 +02:00
}
std :: string PhysicalPrinter :: get_short_name ( std :: string full_name )
{
2020-07-14 15:34:08 +02:00
int pos = full_name . find ( separator ());
2020-07-17 14:32:38 +02:00
if ( pos > 0 )
boost :: erase_tail ( full_name , full_name . length () - pos );
2020-06-26 09:58:39 +02:00
return full_name ;
}
2020-07-17 14:32:38 +02:00
std :: string PhysicalPrinter :: get_preset_name ( std :: string name )
2020-07-14 15:34:08 +02:00
{
2020-07-17 14:32:38 +02:00
int pos = name . find ( separator ());
boost :: erase_head ( name , pos + 3 );
return Preset :: remove_suffix_modified ( name );
2020-07-14 15:34:08 +02:00
}
2020-06-24 08:50:01 +02:00
// -----------------------------------
// *** PhysicalPrinterCollection ***
// -----------------------------------
PhysicalPrinterCollection :: PhysicalPrinterCollection ( const std :: vector < std :: string >& keys )
{
2020-09-08 15:30:59 +02:00
// Default config for a physical printer containing all key/value pairs of PhysicalPrinter::printer_options().
for ( const std :: string & key : keys ) {
const ConfigOptionDef * opt = print_config_def . get ( key );
assert ( opt );
assert ( opt -> default_value );
m_default_config . set_key_value ( key , opt -> default_value -> clone ());
}
2020-06-24 08:50:01 +02:00
}
2020-07-20 16:27:39 +02:00
// Load all printers found in dir_path.
2020-06-24 08:50:01 +02:00
// Throws an exception on error.
void PhysicalPrinterCollection :: load_printers ( const std :: string & dir_path , const std :: string & subdir )
{
2020-09-01 14:15:19 +02:00
// Don't use boost::filesystem::canonical() on Windows, it is broken in regard to reparse points,
// see https://github.com/prusa3d/PrusaSlicer/issues/732
boost :: filesystem :: path dir = boost :: filesystem :: absolute ( boost :: filesystem :: path ( dir_path ) / subdir ). make_preferred ();
2020-06-24 08:50:01 +02:00
m_dir_path = dir . string ();
std :: string errors_cummulative ;
// Store the loaded printers into a new vector, otherwise the binary search for already existing presets would be broken.
std :: deque < PhysicalPrinter > printers_loaded ;
for ( auto & dir_entry : boost :: filesystem :: directory_iterator ( dir ))
if ( Slic3r :: is_ini_file ( dir_entry )) {
std :: string name = dir_entry . path (). filename (). string ();
// Remove the .ini suffix.
name . erase ( name . size () - 4 );
if ( this -> find_printer ( name , false )) {
// This happens when there's is a preset (most likely legacy one) with the same name as a system preset
// that's already been loaded from a bundle.
2020-07-20 16:27:39 +02:00
BOOST_LOG_TRIVIAL ( warning ) << "Printer already present, not loading: " << name ;
2020-06-24 08:50:01 +02:00
continue ;
}
try {
2020-09-08 15:30:59 +02:00
PhysicalPrinter printer ( name , this -> default_config ());
2020-06-24 08:50:01 +02:00
printer . file = dir_entry . path (). string ();
// Load the preset file, apply preset values on top of defaults.
try {
DynamicPrintConfig config ;
config . load_from_ini ( printer . file );
printer . update_from_config ( config );
printer . loaded = true ;
}
catch ( const std :: ifstream :: failure & err ) {
2020-09-14 16:27:55 +02:00
throw Slic3r :: RuntimeError ( std :: string ( "The selected preset cannot be loaded: " ) + printer . file + " \n\t Reason: " + err . what ());
2020-06-24 08:50:01 +02:00
}
catch ( const std :: runtime_error & err ) {
2020-09-14 16:27:55 +02:00
throw Slic3r :: RuntimeError ( std :: string ( "Failed loading the preset file: " ) + printer . file + " \n\t Reason: " + err . what ());
2020-06-24 08:50:01 +02:00
}
printers_loaded . emplace_back ( printer );
}
catch ( const std :: runtime_error & err ) {
errors_cummulative += err . what ();
errors_cummulative += " \n " ;
}
}
m_printers . insert ( m_printers . end (), std :: make_move_iterator ( printers_loaded . begin ()), std :: make_move_iterator ( printers_loaded . end ()));
std :: sort ( m_printers . begin (), m_printers . end ());
if ( ! errors_cummulative . empty ())
2020-09-14 16:27:55 +02:00
throw Slic3r :: RuntimeError ( errors_cummulative );
2020-06-24 08:50:01 +02:00
}
2020-10-01 22:48:00 +02:00
void PhysicalPrinterCollection :: load_printer ( const std :: string & path , const std :: string & name , DynamicPrintConfig && config , bool select , bool save /* = false*/ )
{
auto it = this -> find_printer_internal ( name );
if ( it == m_printers . end () || it -> name != name ) {
// The preset was not found. Create a new preset.
it = m_printers . emplace ( it , PhysicalPrinter ( name , config ));
}
it -> file = path ;
it -> config = std :: move ( config );
it -> loaded = true ;
if ( select )
this -> select_printer ( * it );
if ( save )
it -> save ();
}
2020-07-20 16:27:39 +02:00
// if there is saved user presets, contains information about "Print Host upload",
// Create default printers with this presets
2020-07-21 16:21:18 +02:00
// Note! "Print Host upload" options will be cleared after physical printer creations
2020-07-28 11:31:16 +02:00
void PhysicalPrinterCollection :: load_printers_from_presets ( PrinterPresetCollection & printer_presets )
2020-07-20 16:27:39 +02:00
{
int cnt = 0 ;
2020-07-21 16:21:18 +02:00
for ( Preset & preset : printer_presets ) {
DynamicPrintConfig & config = preset . config ;
2020-10-28 09:51:05 +01:00
for ( const char * option : legacy_print_host_options ) {
2020-07-21 16:21:18 +02:00
if ( ! config . opt_string ( option ). empty ()) {
// check if printer with those "Print Host upload" options already exist
PhysicalPrinter * existed_printer = find_printer_with_same_config ( config );
if ( existed_printer )
// just add preset for this printer
existed_printer -> add_preset ( preset . name );
else {
2020-07-28 11:31:16 +02:00
std :: string new_printer_name = ( boost :: format ( "Printer %1%" ) % ++ cnt ). str ();
while ( find_printer ( new_printer_name ))
new_printer_name = ( boost :: format ( "Printer %1%" ) % ++ cnt ). str ();
2020-07-21 16:21:18 +02:00
// create new printer from this preset
2020-09-08 15:30:59 +02:00
PhysicalPrinter printer ( new_printer_name , this -> default_config (), preset );
2020-07-21 16:21:18 +02:00
printer . loaded = true ;
save_printer ( printer );
}
// erase "Print Host upload" information from the preset
2020-10-28 09:51:05 +01:00
for ( const char * opt : legacy_print_host_options )
2020-07-21 16:21:18 +02:00
config . opt_string ( opt ). clear ();
// save changes for preset
preset . save ();
// update those changes for edited preset if it's equal to the preset
Preset & edited = printer_presets . get_edited_preset ();
if ( preset . name == edited . name ) {
2020-10-28 09:51:05 +01:00
for ( const char * opt : legacy_print_host_options )
2020-07-21 16:21:18 +02:00
edited . config . opt_string ( opt ). clear ();
}
break ;
}
2020-07-20 16:27:39 +02:00
}
}
}
2020-10-01 22:48:00 +02:00
PhysicalPrinter * PhysicalPrinterCollection :: find_printer ( const std :: string & name , bool case_sensitive_search )
2020-06-24 08:50:01 +02:00
{
2020-10-01 22:48:00 +02:00
auto it = this -> find_printer_internal ( name , case_sensitive_search );
2020-06-24 08:50:01 +02:00
// Ensure that a temporary copy is returned if the preset found is currently selected.
2020-10-01 22:48:00 +02:00
auto is_equal_name = [ name , case_sensitive_search ]( const std :: string & in_name ) {
if ( case_sensitive_search )
return in_name == name ;
return boost :: to_lower_copy < std :: string > ( in_name ) == boost :: to_lower_copy < std :: string > ( name );
};
if ( it == m_printers . end () || ! is_equal_name ( it -> name ))
return nullptr ;
return & this -> printer ( it - m_printers . begin ());
}
std :: deque < PhysicalPrinter >:: iterator PhysicalPrinterCollection :: find_printer_internal ( const std :: string & name , bool case_sensitive_search /* = true*/ )
{
if ( case_sensitive_search )
return Slic3r :: lower_bound_by_predicate ( m_printers . begin (), m_printers . end (), [ & name ]( const auto & l ) { return l . name < name ; });
std :: string low_name = boost :: to_lower_copy < std :: string > ( name );
2020-10-06 12:32:11 +02:00
size_t i = 0 ;
2020-10-01 22:48:00 +02:00
for ( const PhysicalPrinter & printer : m_printers ) {
if ( boost :: to_lower_copy < std :: string > ( printer . name ) == low_name )
break ;
i ++ ;
}
if ( i == m_printers . size ())
return m_printers . end ();
return m_printers . begin () + i ;
2020-06-24 08:50:01 +02:00
}
2020-07-21 16:21:18 +02:00
PhysicalPrinter * PhysicalPrinterCollection :: find_printer_with_same_config ( const DynamicPrintConfig & config )
{
for ( const PhysicalPrinter & printer : * this ) {
bool is_equal = true ;
2020-10-28 09:51:05 +01:00
for ( const char * opt : legacy_print_host_options )
2020-07-21 16:21:18 +02:00
if ( is_equal && printer . config . opt_string ( opt ) != config . opt_string ( opt ))
is_equal = false ;
if ( is_equal )
return find_printer ( printer . name );
}
return nullptr ;
}
2020-06-24 08:50:01 +02:00
// Generate a file path from a profile name. Add the ".ini" suffix if it is missing.
std :: string PhysicalPrinterCollection :: path_from_name ( const std :: string & new_name ) const
{
std :: string file_name = boost :: iends_with ( new_name , ".ini" ) ? new_name : ( new_name + ".ini" );
return ( boost :: filesystem :: path ( m_dir_path ) / file_name ). make_preferred (). string ();
}
2020-07-20 14:56:09 +02:00
void PhysicalPrinterCollection :: save_printer ( PhysicalPrinter & edited_printer , const std :: string & renamed_from /* = ""*/ )
2020-06-16 12:57:49 +02:00
{
2020-07-20 14:56:09 +02:00
// controll and update preset_names in edited_printer config
edited_printer . update_preset_names_in_config ();
2020-07-14 15:34:08 +02:00
std :: string name = renamed_from . empty () ? edited_printer . name : renamed_from ;
2020-06-16 12:57:49 +02:00
// 1) Find the printer with a new_name or create a new one,
// initialize it with the edited config.
2020-07-14 15:34:08 +02:00
auto it = this -> find_printer_internal ( name );
if ( it != m_printers . end () && it -> name == name ) {
2020-06-24 08:50:01 +02:00
// Printer with the same name found.
2020-06-16 12:57:49 +02:00
// Overwriting an existing preset.
2020-06-24 08:50:01 +02:00
it -> config = std :: move ( edited_printer . config );
2020-07-14 15:34:08 +02:00
it -> name = edited_printer . name ;
it -> preset_names = edited_printer . preset_names ;
2020-10-01 22:48:00 +02:00
// sort printers and get new it
std :: sort ( m_printers . begin (), m_printers . end ());
it = this -> find_printer_internal ( edited_printer . name );
2020-06-16 12:57:49 +02:00
}
else {
// Creating a new printer.
2020-10-01 22:48:00 +02:00
it = m_printers . emplace ( it , edited_printer );
2020-06-16 12:57:49 +02:00
}
2020-06-24 08:50:01 +02:00
assert ( it != m_printers . end ());
// 2) Save printer
PhysicalPrinter & printer = * it ;
if ( printer . file . empty ())
printer . file = this -> path_from_name ( printer . name );
2020-07-14 15:34:08 +02:00
if ( printer . file == this -> path_from_name ( printer . name ))
printer . save ();
else
// if printer was renamed, we should rename a file and than save the config
printer . save ( printer . file , this -> path_from_name ( printer . name ));
2020-06-24 08:50:01 +02:00
// update idx_selected
m_idx_selected = it - m_printers . begin ();
2020-06-16 12:57:49 +02:00
}
2020-06-24 08:50:01 +02:00
bool PhysicalPrinterCollection :: delete_printer ( const std :: string & name )
{
auto it = this -> find_printer_internal ( name );
2020-06-24 12:28:00 +02:00
if ( it == m_printers . end ())
return false ;
2020-06-24 08:50:01 +02:00
const PhysicalPrinter & printer = * it ;
// Erase the preset file.
boost :: nowide :: remove ( printer . file . c_str ());
m_printers . erase ( it );
return true ;
}
2020-06-24 12:28:00 +02:00
bool PhysicalPrinterCollection :: delete_selected_printer ()
{
if ( ! has_selection ())
return false ;
const PhysicalPrinter & printer = this -> get_selected_printer ();
// Erase the preset file.
boost :: nowide :: remove ( printer . file . c_str ());
// Remove the preset from the list.
m_printers . erase ( m_printers . begin () + m_idx_selected );
// unselect all printers
unselect_printer ();
return true ;
}
2020-07-28 11:31:16 +02:00
bool PhysicalPrinterCollection :: delete_preset_from_printers ( const std :: string & preset_name )
2020-07-20 14:56:09 +02:00
{
std :: vector < std :: string > printers_for_delete ;
2020-07-28 11:31:16 +02:00
for ( PhysicalPrinter & printer : m_printers ) {
2020-07-20 14:56:09 +02:00
if ( printer . preset_names . size () == 1 && * printer . preset_names . begin () == preset_name )
printers_for_delete . emplace_back ( printer . name );
2020-07-28 11:31:16 +02:00
else if ( printer . delete_preset ( preset_name ))
2020-07-20 14:56:09 +02:00
save_printer ( printer );
2020-07-28 11:31:16 +02:00
}
2020-07-20 14:56:09 +02:00
2020-07-28 11:31:16 +02:00
if ( ! printers_for_delete . empty ())
2020-07-20 14:56:09 +02:00
for ( const std :: string & printer_name : printers_for_delete )
delete_printer ( printer_name );
2020-07-28 11:31:16 +02:00
unselect_printer ();
2020-07-20 14:56:09 +02:00
return true ;
}
2020-07-28 11:31:16 +02:00
// Get list of printers which have more than one preset and "preset_name" preset is one of them
std :: vector < std :: string > PhysicalPrinterCollection :: get_printers_with_preset ( const std :: string & preset_name )
{
std :: vector < std :: string > printers ;
for ( auto printer : m_printers ) {
if ( printer . preset_names . size () == 1 )
continue ;
if ( printer . preset_names . find ( preset_name ) != printer . preset_names . end ())
printers . emplace_back ( printer . name );
}
return printers ;
}
// Get list of printers which has only "preset_name" preset
std :: vector < std :: string > PhysicalPrinterCollection :: get_printers_with_only_preset ( const std :: string & preset_name )
{
std :: vector < std :: string > printers ;
for ( auto printer : m_printers )
if ( printer . preset_names . size () == 1 && * printer . preset_names . begin () == preset_name )
printers . emplace_back ( printer . name );
return printers ;
}
2020-07-17 14:32:38 +02:00
std :: string PhysicalPrinterCollection :: get_selected_full_printer_name () const
2020-06-24 08:50:01 +02:00
{
2020-07-17 14:32:38 +02:00
return ( m_idx_selected == size_t ( - 1 )) ? std :: string () : this -> get_selected_printer (). get_full_name ( m_selected_preset );
}
2020-07-23 12:17:18 +02:00
void PhysicalPrinterCollection :: select_printer ( const std :: string & full_name )
2020-07-17 14:32:38 +02:00
{
std :: string printer_name = PhysicalPrinter :: get_short_name ( full_name );
auto it = this -> find_printer_internal ( printer_name );
2020-07-23 12:17:18 +02:00
if ( it == m_printers . end ()) {
unselect_printer ();
return ;
}
2020-06-24 08:50:01 +02:00
// update idx_selected
2020-07-23 12:17:18 +02:00
m_idx_selected = it - m_printers . begin ();
2020-07-17 14:32:38 +02:00
// update name of the currently selected preset
2020-07-23 12:17:18 +02:00
if ( printer_name == full_name )
// use first preset in the list
2020-07-17 14:32:38 +02:00
m_selected_preset = * it -> preset_names . begin ();
2020-07-23 12:17:18 +02:00
else
m_selected_preset = it -> get_preset_name ( full_name );
2020-06-24 08:50:01 +02:00
}
2020-12-15 12:09:03 +01:00
void PhysicalPrinterCollection :: select_printer ( const std :: string & printer_name , const std :: string & preset_name )
{
if ( preset_name . empty ())
return select_printer ( printer_name );
return select_printer ( printer_name + PhysicalPrinter :: separator () + preset_name );
}
2020-07-23 12:17:18 +02:00
void PhysicalPrinterCollection :: select_printer ( const PhysicalPrinter & printer )
2020-07-20 14:56:09 +02:00
{
return select_printer ( printer . name );
}
2020-07-17 14:32:38 +02:00
bool PhysicalPrinterCollection :: has_selection () const
{
return m_idx_selected != size_t ( - 1 );
}
void PhysicalPrinterCollection :: unselect_printer ()
{
m_idx_selected = size_t ( - 1 );
m_selected_preset . clear ();
}
bool PhysicalPrinterCollection :: is_selected ( PhysicalPrinterCollection :: ConstIterator it , const std :: string & preset_name ) const
{
2020-10-06 12:32:11 +02:00
return m_idx_selected == size_t ( it - m_printers . begin ()) &&
2020-07-17 14:32:38 +02:00
m_selected_preset == preset_name ;
}
2020-06-24 08:50:01 +02:00
2020-01-13 16:01:01 +01:00
namespace PresetUtils {
const VendorProfile :: PrinterModel * system_printer_model ( const Preset & preset )
{
const VendorProfile :: PrinterModel * out = nullptr ;
if ( preset . vendor != nullptr ) {
auto * printer_model = preset . config . opt < ConfigOptionString > ( "printer_model" );
if ( printer_model != nullptr && ! printer_model -> value . empty ()) {
auto it = std :: find_if ( preset . vendor -> models . begin (), preset . vendor -> models . end (), [ printer_model ]( const VendorProfile :: PrinterModel & pm ) { return pm . id == printer_model -> value ; });
if ( it != preset . vendor -> models . end ())
out = & ( * it );
}
}
return out ;
}
2020-08-21 10:59:07 +02:00
std :: string system_printer_bed_model ( const Preset & preset )
{
std :: string out ;
const VendorProfile :: PrinterModel * pm = PresetUtils :: system_printer_model ( preset );
2020-11-19 19:02:32 +01:00
if ( pm != nullptr && ! pm -> bed_model . empty ()) {
out = Slic3r :: data_dir () + "/vendor/" + preset . vendor -> id + "/" + pm -> bed_model ;
if ( ! boost :: filesystem :: exists ( boost :: filesystem :: path ( out )))
out = Slic3r :: resources_dir () + "/profiles/" + preset . vendor -> id + "/" + pm -> bed_model ;
}
2020-08-21 10:59:07 +02:00
return out ;
}
std :: string system_printer_bed_texture ( const Preset & preset )
{
std :: string out ;
const VendorProfile :: PrinterModel * pm = PresetUtils :: system_printer_model ( preset );
2020-11-19 19:02:32 +01:00
if ( pm != nullptr && ! pm -> bed_texture . empty ()) {
out = Slic3r :: data_dir () + "/vendor/" + preset . vendor -> id + "/" + pm -> bed_texture ;
if ( ! boost :: filesystem :: exists ( boost :: filesystem :: path ( out )))
out = Slic3r :: resources_dir () + "/profiles/" + preset . vendor -> id + "/" + pm -> bed_texture ;
}
2020-08-21 10:59:07 +02:00
return out ;
}
2020-01-13 16:01:01 +01:00
} // namespace PresetUtils
2017-09-19 13:55:48 +02:00
} // namespace Slic3r