2017-11-02 15:21:34 +00:00
# include <cassert>
2017-09-19 11:55:48 +00:00
# include "Preset.hpp"
2018-03-29 15:54:43 +00:00
# include "AppConfig.hpp"
2018-04-19 10:08:59 +00:00
# include "BitmapCache.hpp"
2018-11-26 13:41:58 +00:00
# include "I18N.hpp"
2019-04-08 07:37:23 +00:00
# include "wxExtensions.hpp"
2017-09-19 11:55:48 +00:00
2018-12-12 18:02:18 +00:00
# ifdef _MSC_VER
# define WIN32_LEAN_AND_MEAN
# define NOMINMAX
# include <Windows.h>
# endif /* _MSC_VER */
2019-01-30 14:22:17 +00:00
# include <algorithm>
2017-09-19 11:55:48 +00:00
# include <fstream>
2018-04-12 18:04:48 +00:00
# include <stdexcept>
2019-02-08 09:33:07 +00:00
# include <unordered_map>
2018-04-12 18:04:48 +00:00
# include <boost/format.hpp>
2017-10-02 15:35:00 +00:00
# include <boost/filesystem.hpp>
2018-04-11 11:12:08 +00:00
# include <boost/filesystem/fstream.hpp>
2017-09-19 11:55:48 +00:00
# include <boost/algorithm/string/predicate.hpp>
2017-10-25 10:53:31 +00:00
# include <boost/nowide/cenv.hpp>
2018-12-12 18:02:18 +00:00
# include <boost/nowide/convert.hpp>
2017-10-26 15:17:39 +00:00
# include <boost/nowide/cstdio.hpp>
2017-10-25 10:53:31 +00:00
# include <boost/nowide/fstream.hpp>
# include <boost/property_tree/ini_parser.hpp>
# include <boost/property_tree/ptree.hpp>
2017-10-26 15:17:39 +00:00
# include <boost/locale.hpp>
2018-05-01 08:35:07 +00:00
# include <boost/log/trivial.hpp>
2017-10-25 10:53:31 +00:00
2017-09-19 11:55:48 +00:00
# include <wx/image.h>
2017-10-25 10:53:31 +00:00
# include <wx/choice.h>
2017-09-19 11:55:48 +00:00
# include <wx/bmpcbox.h>
2017-10-27 22:18:02 +00:00
# include <wx/wupdlock.h>
2017-09-19 11:55:48 +00:00
2018-11-26 13:41:58 +00:00
# include "libslic3r/libslic3r.h"
# include "libslic3r/Utils.hpp"
# include "libslic3r/PlaceholderParser.hpp"
2018-10-09 10:41:05 +00:00
# include "Plater.hpp"
2017-10-25 10:53:31 +00:00
2018-04-11 11:12:08 +00:00
using boost : : property_tree : : ptree ;
2017-09-19 11:55:48 +00:00
namespace Slic3r {
2018-04-11 11:12:08 +00:00
ConfigFileType guess_config_file_type ( const ptree & tree )
2017-11-01 18:30:05 +00:00
{
size_t app_config = 0 ;
size_t bundle = 0 ;
size_t config = 0 ;
2018-04-11 11:12:08 +00:00
for ( const ptree : : value_type & v : tree ) {
2017-11-01 18:30:05 +00: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 11:12:08 +00: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 09:33:07 +00: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 11:12:08 +00: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 14:18:46 +00:00
static const std : : string filaments_section = " default_filaments " ;
static const std : : string materials_section = " default_sla_materials " ;
2018-04-11 11:12:08 +00:00
const std : : string id = path . stem ( ) . string ( ) ;
2018-04-12 18:04:48 +00:00
if ( ! boost : : filesystem : : exists ( path ) ) {
throw std : : runtime_error ( ( boost : : format ( " Cannot load Vendor Config Bundle `%1%`: File not found: `%2%`. " ) % id % path ) . str ( ) ) ;
}
2018-04-11 11:12:08 +00:00
VendorProfile res ( id ) ;
2019-04-12 14:18:46 +00:00
// Helper to get compulsory fields
2018-04-11 11:12:08 +00: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 ( ) ) {
throw std : : runtime_error ( ( boost : : format ( " Vendor Config Bundle `%1%` is not valid: Missing secion or key: `%2%`. " ) % id % key ) . str ( ) ) ;
}
return res ;
} ;
2019-04-12 14:18:46 +00:00
// Load the header
2018-04-11 11:12:08 +00: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 ) {
throw std : : runtime_error ( ( boost : : format ( " Vendor Config Bundle `%1%` is not valid: Cannot parse config_version: `%2%`. " ) % id % config_version_str ) . str ( ) ) ;
} else {
res . config_version = std : : move ( * config_version ) ;
}
2019-04-12 14:18:46 +00:00
// Load URLs
2019-05-13 16:38:48 +00:00
const auto config_update_url = vendor_section . find ( " config_update_url " ) ;
2018-04-11 11:12:08 +00:00
if ( config_update_url ! = vendor_section . not_found ( ) ) {
res . config_update_url = config_update_url - > second . data ( ) ;
}
2019-05-13 16:38:48 +00: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 11:12:08 +00:00
if ( ! load_all ) {
return res ;
}
2019-04-12 14:18:46 +00:00
// Load printer models
2018-04-11 11:12:08 +00: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 14:22:17 +00:00
2019-02-07 14:55:47 +00: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 13:09:57 +00: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 14:22:17 +00:00
2019-02-07 14:55:47 +00:00
model . family = section . second . get < std : : string > ( " family " , std : : string ( ) ) ;
2019-02-08 09:33:07 +00: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 19:09:25 +00:00
#if 0
2019-08-06 14:51:32 +00:00
// Remove SLA printers from the initial alpha.
if ( model . technology = = ptSLA )
continue ;
2018-12-21 19:09:25 +00:00
# endif
2019-08-06 14:51:32 +00:00
section . second . get < std : : string > ( " variants " , " " ) ;
2018-05-01 08:35:07 +00:00
const auto variants_field = section . second . get < std : : string > ( " variants " , " " ) ;
2018-04-11 11:12:08 +00:00
std : : vector < std : : string > variants ;
2018-05-01 08:35:07 +00:00
if ( Slic3r : : unescape_strings_cstyle ( variants_field , variants ) ) {
2018-04-11 11:12:08 +00: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 08:35:07 +00:00
BOOST_LOG_TRIVIAL ( error ) < < boost : : format ( " Vendor bundle: `%1%`: Malformed variants field: `%2%` " ) % id % variants_field ;
2018-04-11 11:12:08 +00:00
}
if ( ! model . id . empty ( ) & & ! model . variants . empty ( ) )
res . models . push_back ( std : : move ( model ) ) ;
}
}
2019-04-12 14:18:46 +00: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 11:12:08 +00:00
return res ;
}
2019-01-30 14:22:17 +00:00
std : : vector < std : : string > VendorProfile : : families ( ) const
{
std : : vector < std : : string > res ;
unsigned num_familiies = 0 ;
for ( auto & model : models ) {
2019-02-07 14:55:47 +00:00
if ( std : : find ( res . begin ( ) , res . end ( ) , model . family ) = = res . end ( ) ) {
2019-01-30 14:22:17 +00:00
res . push_back ( model . family ) ;
num_familiies + + ;
}
}
return res ;
}
2018-04-11 11:12:08 +00:00
2017-10-26 15:17:39 +00:00
// Suffix to be added to a modified preset name in the combo box.
2017-10-25 10:53:31 +00:00
static std : : string g_suffix_modified = " (modified) " ;
2017-10-30 17:41:50 +00:00
const std : : string & Preset : : suffix_modified ( )
{
return g_suffix_modified ;
}
2018-07-23 13:44:01 +00:00
void Preset : : update_suffix_modified ( )
{
2019-08-06 14:51:32 +00:00
g_suffix_modified = ( " ( " + _ ( L ( " modified " ) ) + " ) " ) . ToUTF8 ( ) . data ( ) ;
2018-07-23 13:44:01 +00:00
}
2017-10-26 15:17:39 +00:00
// Remove an optional "(modified)" suffix from a name.
// This converts a UI name to a unique preset identifier.
2017-10-30 17:41:50 +00:00
std : : string Preset : : remove_suffix_modified ( const std : : string & name )
2017-10-26 15:17:39 +00:00
{
return boost : : algorithm : : ends_with ( name , g_suffix_modified ) ?
name . substr ( 0 , name . size ( ) - g_suffix_modified . size ( ) ) :
name ;
}
2017-10-25 10:53:31 +00:00
2017-11-01 18:30:05 +00: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 13:09:57 +00:00
// Loaded the FFF Printer settings. Verify, that all extruder dependent values have enough values.
2019-10-15 14:31:20 +00:00
config . set_num_extruders ( ( unsigned int ) nozzle_diameter - > values . size ( ) ) ;
2017-11-02 15:21:34 +00: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 14:51:32 +00:00
if ( key = = " compatible_prints " | | key = = " compatible_printers " )
continue ;
2017-11-02 15:21:34 +00:00
auto * opt = config . option ( key , false ) ;
2018-05-23 12:15:10 +00:00
/*assert(opt != nullptr);
assert ( opt - > is_vector ( ) ) ; */
2017-11-02 15:21:34 +00:00
if ( opt ! = nullptr & & opt - > is_vector ( ) )
static_cast < ConfigOptionVectorBase * > ( opt ) - > resize ( n , defaults . option ( key ) ) ;
}
2018-05-23 11:19:25 +00: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 18:09:54 +00:00
assert ( opt = = nullptr | | opt - > type ( ) = = coStrings ) ;
2018-05-23 11:19:25 +00:00
if ( opt ! = nullptr & & opt - > type ( ) = = coStrings )
static_cast < ConfigOptionStrings * > ( opt ) - > values . resize ( n , std : : string ( ) ) ;
}
2017-11-02 15:21:34 +00:00
}
2017-10-25 10:53:31 +00:00
}
2018-10-31 15:22:36 +00:00
std : : string Preset : : remove_invalid_keys ( DynamicPrintConfig & config , const DynamicPrintConfig & default_config )
2017-09-19 11:55:48 +00:00
{
2018-10-31 15:22:36 +00: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 18:30:05 +00:00
}
2018-10-31 15:22:36 +00:00
return incorrect_keys ;
2017-09-19 11:55:48 +00:00
}
2017-10-25 10:53:31 +00: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 : " " ) ;
}
2019-12-05 13:48:11 +00:00
bool is_compatible_with_print ( const PresetWithVendorProfile & preset , const PresetWithVendorProfile & active_print )
2018-12-04 16:56:49 +00:00
{
2019-12-05 13:48:11 +00:00
if ( preset . vendor ! = nullptr & & preset . vendor ! = active_print . 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_prints_condition ( ) ;
auto * compatible_prints = dynamic_cast < const ConfigOptionStrings * > ( preset . preset . config . option ( " compatible_prints " ) ) ;
2018-12-04 16:56:49 +00:00
bool has_compatible_prints = compatible_prints ! = nullptr & & ! compatible_prints - > values . empty ( ) ;
if ( ! has_compatible_prints & & ! condition . empty ( ) ) {
try {
2019-12-05 13:48:11 +00:00
return PlaceholderParser : : evaluate_boolean_expression ( condition , active_print . preset . config ) ;
2018-12-04 16:56:49 +00:00
} catch ( const std : : runtime_error & err ) {
//FIXME in case of an error, return "compatible with everything".
2019-12-05 13:48:11 +00: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 16:56:49 +00:00
return true ;
}
}
2019-12-05 13:48:11 +00: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 16:56:49 +00:00
compatible_prints - > values . end ( ) ;
}
2019-12-05 13:48:11 +00:00
bool is_compatible_with_printer ( const PresetWithVendorProfile & preset , const PresetWithVendorProfile & active_printer , const DynamicPrintConfig * extra_config )
2017-09-19 11:55:48 +00:00
{
2019-12-05 13:48:11 +00: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 12:32:02 +00:00
bool has_compatible_printers = compatible_printers ! = nullptr & & ! compatible_printers - > values . empty ( ) ;
2018-06-27 14:57:42 +00:00
if ( ! has_compatible_printers & & ! condition . empty ( ) ) {
2017-12-18 14:07:38 +00:00
try {
2019-12-05 13:48:11 +00:00
return PlaceholderParser : : evaluate_boolean_expression ( condition , active_printer . preset . config , extra_config ) ;
2017-12-18 14:07:38 +00:00
} catch ( const std : : runtime_error & err ) {
//FIXME in case of an error, return "compatible with everything".
2019-12-05 13:48:11 +00: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 14:07:38 +00:00
return true ;
}
}
2019-12-05 13:48:11 +00: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 11:55:48 +00:00
compatible_printers - > values . end ( ) ;
2017-11-10 16:27:05 +00:00
}
2019-12-05 13:48:11 +00:00
bool is_compatible_with_printer ( const PresetWithVendorProfile & preset , const PresetWithVendorProfile & active_printer )
2017-12-19 15:48:14 +00:00
{
DynamicPrintConfig config ;
2019-12-05 13:48:11 +00: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 14:51:32 +00:00
if ( opt )
config . set_key_value ( " num_extruders " , new ConfigOptionInt ( ( int ) static_cast < const ConfigOptionFloats * > ( opt ) - > values . size ( ) ) ) ;
2019-12-05 13:48:11 +00:00
return is_compatible_with_printer ( preset , active_printer , & config ) ;
2017-09-19 11:55:48 +00:00
}
2018-03-29 15:54:43 +00:00
void Preset : : set_visible_from_appconfig ( const AppConfig & app_config )
{
if ( vendor = = nullptr ) { return ; }
2019-04-12 14:18:46 +00:00
if ( type = = TYPE_PRINTER ) {
const std : : string & model = config . opt_string ( " printer_model " ) ;
const std : : string & variant = config . opt_string ( " printer_variant " ) ;
if ( model . empty ( ) | | variant . empty ( ) ) { return ; }
is_visible = app_config . get_variant ( vendor - > id , model , variant ) ;
} else if ( type = = TYPE_FILAMENT ) {
is_visible = app_config . has ( " filaments " , name ) ;
} else if ( type = = TYPE_SLA_MATERIAL ) {
is_visible = app_config . has ( " sla_materials " , name ) ;
}
2018-03-29 15:54:43 +00:00
}
2017-11-01 18:30:05 +00:00
const std : : vector < std : : string > & Preset : : print_options ( )
2019-08-06 14:51:32 +00:00
{
2017-11-01 18:30:05 +00:00
static std : : vector < std : : string > s_opts {
2019-08-06 14:51:32 +00:00
" layer_height " , " first_layer_height " , " perimeters " , " spiral_vase " , " slice_closing_radius " , " top_solid_layers " , " bottom_solid_layers " ,
" extra_perimeters " , " ensure_vertical_shell_thickness " , " avoid_crossing_perimeters " , " thin_walls " , " overhangs " ,
2019-02-22 14:25:35 +00:00
" seam_position " , " external_perimeters_first " , " fill_density " , " fill_pattern " , " top_fill_pattern " , " bottom_fill_pattern " ,
2019-08-06 14:51:32 +00:00
" infill_every_layers " , " infill_only_where_needed " , " solid_infill_every_layers " , " fill_angle " , " bridge_angle " ,
" solid_infill_below_area " , " only_retract_when_crossing_perimeters " , " infill_first " , " max_print_speed " ,
" max_volumetric_speed " ,
2019-01-29 17:07:45 +00:00
# ifdef HAS_PRESSURE_EQUALIZER
2019-08-06 14:51:32 +00:00
" max_volumetric_extrusion_rate_slope_positive " , " max_volumetric_extrusion_rate_slope_negative " ,
2019-01-29 17:07:45 +00:00
# endif /* HAS_PRESSURE_EQUALIZER */
2019-08-06 14:51:32 +00:00
" perimeter_speed " , " small_perimeter_speed " , " external_perimeter_speed " , " infill_speed " , " solid_infill_speed " ,
2017-11-01 18:30:05 +00:00
" top_solid_infill_speed " , " support_material_speed " , " support_material_xy_spacing " , " support_material_interface_speed " ,
2019-08-06 14:51:32 +00:00
" bridge_speed " , " gap_fill_speed " , " travel_speed " , " first_layer_speed " , " perimeter_acceleration " , " infill_acceleration " ,
2017-11-01 18:30:05 +00:00
" bridge_acceleration " , " first_layer_acceleration " , " default_acceleration " , " skirts " , " skirt_distance " , " skirt_height " ,
2019-08-06 14:51:32 +00:00
" min_skirt_length " , " brim_width " , " support_material " , " support_material_auto " , " support_material_threshold " , " support_material_enforce_layers " ,
" 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 " ,
" top_infill_extrusion_width " , " support_material_extrusion_width " , " infill_overlap " , " bridge_flow_ratio " , " clip_multipart_objects " ,
2017-11-01 18:30:05 +00:00
" elefant_foot_compensation " , " xy_size_compensation " , " threads " , " resolution " , " wipe_tower " , " wipe_tower_x " , " wipe_tower_y " ,
2019-08-06 14:51:32 +00:00
" wipe_tower_width " , " wipe_tower_rotation_angle " , " wipe_tower_bridging " , " single_extruder_multi_material_priming " ,
2019-09-19 14:30:01 +00:00
" wipe_tower_no_sparse_layers " , " compatible_printers " , " compatible_printers_condition " , " inherits "
2017-11-01 18:30:05 +00:00
} ;
return s_opts ;
}
const std : : vector < std : : string > & Preset : : filament_options ( )
2019-08-06 14:51:32 +00:00
{
2017-11-01 18:30:05 +00:00
static std : : vector < std : : string > s_opts {
2018-04-06 10:33:12 +00:00
" filament_colour " , " filament_diameter " , " filament_type " , " filament_soluble " , " filament_notes " , " filament_max_volumetric_speed " ,
2018-08-21 12:36:24 +00:00
" extrusion_multiplier " , " filament_density " , " filament_cost " , " filament_loading_speed " , " filament_loading_speed_start " , " filament_load_time " ,
" 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 " ,
" max_fan_speed " , " bridge_fan_speed " , " disable_fan_first_layers " , " fan_below_layer_time " , " slowdown_below_layer_time " , " min_print_speed " ,
2018-12-04 16:56:49 +00:00
" start_filament_gcode " , " end_filament_gcode " ,
WIP: Nullable configuration value concept, implemented for
ConfigOptionFloatsNullable, ConfigOptionIntsNullable,
ConfigOptionPercentsNullable, ConfigOptionBoolsNullable.
retract override values were added to the Filament profile:
vector of floats: "retract_length", "retract_lift", "retract_lift_above",
"retract_lift_below", "retract_speed", "deretract_speed",
"retract_restart_extra", "retract_before_travel",
vector of bools: "retract_layer_change", "wipe"
vector of percents: "retract_before_wipe"
2019-07-23 12:15:42 +00:00
// Retract overrides
2019-08-06 14:51:32 +00: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 14:18:46 +00:00
" filament_vendor " , " compatible_prints " , " compatible_prints_condition " , " compatible_printers " , " compatible_printers_condition " , " inherits "
2017-11-01 18:30:05 +00:00
} ;
return s_opts ;
}
const std : : vector < std : : string > & Preset : : printer_options ( )
2019-08-06 14:51:32 +00:00
{
2017-11-01 18:30:05 +00:00
static std : : vector < std : : string > s_opts ;
if ( s_opts . empty ( ) ) {
s_opts = {
2018-07-31 13:09:57 +00:00
" printer_technology " ,
2019-07-18 10:56:52 +00:00
" bed_shape " , " bed_custom_texture " , " bed_custom_model " , " z_offset " , " gcode_flavor " , " use_relative_e_distances " , " serial_port " , " serial_speed " ,
2018-08-24 10:20:24 +00:00
" use_firmware_retraction " , " use_volumetric_e " , " variable_layer_height " ,
" host_type " , " print_host " , " printhost_apikey " , " printhost_cafile " ,
2017-11-28 14:19:57 +00:00
" single_extruder_multi_material " , " start_gcode " , " end_gcode " , " before_layer_gcode " , " layer_gcode " , " toolchange_gcode " ,
2018-04-06 10:33:12 +00:00
" between_objects_gcode " , " printer_vendor " , " printer_model " , " printer_variant " , " printer_notes " , " cooling_tube_retraction " ,
2019-08-06 14:51:32 +00:00
" cooling_tube_length " , " high_current_on_filament_swap " , " parking_pos_retraction " , " extra_loading_move " , " max_print_height " ,
2018-12-14 19:09:10 +00:00
" default_print_profile " , " inherits " ,
2018-08-04 15:38:25 +00:00
" remaining_times " , " silent_mode " , " machine_max_acceleration_extruding " , " machine_max_acceleration_retracting " ,
2019-08-06 14:51:32 +00:00
" 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 " ,
2019-11-15 16:33:31 +00:00
" machine_max_jerk_x " , " machine_max_jerk_y " , " machine_max_jerk_z " , " machine_max_jerk_e " ,
" thumbnails "
2017-11-01 18:30:05 +00:00
} ;
s_opts . insert ( s_opts . end ( ) , Preset : : nozzle_options ( ) . begin ( ) , Preset : : nozzle_options ( ) . end ( ) ) ;
}
return s_opts ;
}
2019-08-06 14:51:32 +00:00
// The following nozzle options of a printer profile will be adjusted to match the size
2018-03-13 08:45:04 +00:00
// of the nozzle_diameter vector.
2017-11-01 18:30:05 +00:00
const std : : vector < std : : string > & Preset : : nozzle_options ( )
{
2019-10-15 14:31:20 +00:00
return print_config_def . extruder_option_keys ( ) ;
2017-11-01 18:30:05 +00:00
}
2018-12-03 12:14:28 +00:00
const std : : vector < std : : string > & Preset : : sla_print_options ( )
2019-08-06 14:51:32 +00:00
{
2018-07-31 13:09:57 +00:00
static std : : vector < std : : string > s_opts ;
if ( s_opts . empty ( ) ) {
s_opts = {
2018-12-03 12:14:28 +00:00
" layer_height " ,
2019-02-18 15:04:55 +00:00
" faded_layers " ,
2018-12-03 12:14:28 +00:00
" supports_enable " ,
" support_head_front_diameter " ,
" support_head_penetration " ,
" support_head_width " ,
" support_pillar_diameter " ,
2019-01-09 11:21:43 +00:00
" support_pillar_connection_mode " ,
2019-02-05 10:16:03 +00:00
" support_buildplate_only " ,
2018-12-03 12:14:28 +00:00
" support_pillar_widening_factor " ,
" support_base_diameter " ,
" support_base_height " ,
2019-06-11 15:57:39 +00:00
" support_base_safety_distance " ,
2018-12-03 12:14:28 +00:00
" support_critical_angle " ,
" support_max_bridge_length " ,
2019-03-08 10:39:34 +00:00
" support_max_pillar_link_distance " ,
2018-12-03 12:14:28 +00:00
" support_object_elevation " ,
2019-02-19 15:34:52 +00:00
" support_points_density_relative " ,
" support_points_minimal_distance " ,
2019-03-01 16:53:02 +00:00
" slice_closing_radius " ,
2018-12-03 12:14:28 +00:00
" pad_enable " ,
" pad_wall_thickness " ,
" pad_wall_height " ,
2019-09-24 13:15:49 +00:00
" pad_brim_size " ,
2018-12-03 12:14:28 +00:00
" pad_max_merge_distance " ,
2019-08-06 14:51:32 +00:00
// "pad_edge_radius",
2019-02-25 15:04:46 +00:00
" pad_wall_slope " ,
2019-06-12 11:15:42 +00:00
" pad_object_gap " ,
2019-08-28 09:32:49 +00:00
" pad_around_object " ,
2019-09-24 13:15:49 +00:00
" pad_around_object_everywhere " ,
2019-06-11 15:57:39 +00:00
" pad_object_connector_stride " ,
" pad_object_connector_width " ,
" pad_object_connector_penetration " ,
2019-08-06 14:51:32 +00:00
" output_filename_format " ,
2018-12-03 12:14:28 +00:00
" default_sla_print_profile " ,
" compatible_printers " ,
2019-08-06 14:51:32 +00:00
" compatible_printers_condition " ,
2018-07-31 13:09:57 +00:00
" inherits "
} ;
}
return s_opts ;
}
const std : : vector < std : : string > & Preset : : sla_material_options ( )
2019-08-06 14:51:32 +00:00
{
2018-07-31 13:09:57 +00:00
static std : : vector < std : : string > s_opts ;
if ( s_opts . empty ( ) ) {
s_opts = {
2019-08-02 14:15:27 +00:00
" material_type " ,
2018-12-03 12:14:28 +00:00
" initial_layer_height " ,
2019-09-26 14:33:55 +00:00
" bottle_cost " ,
" bottle_volume " ,
" bottle_weight " ,
" material_density " ,
2019-08-20 15:24:48 +00:00
" exposure_time " ,
" initial_exposure_time " ,
2019-04-03 15:34:46 +00:00
" material_correction " ,
2018-08-01 09:09:51 +00:00
" material_notes " ,
2019-04-12 14:18:46 +00:00
" material_vendor " ,
2018-10-31 15:22:36 +00:00
" default_sla_material_profile " ,
2019-08-06 14:51:32 +00:00
" compatible_prints " , " compatible_prints_condition " ,
2018-12-04 16:56:49 +00:00
" compatible_printers " , " compatible_printers_condition " , " inherits "
2018-07-31 13:09:57 +00:00
} ;
}
return s_opts ;
}
2018-12-03 12:14:28 +00:00
const std : : vector < std : : string > & Preset : : sla_printer_options ( )
2019-08-06 14:51:32 +00:00
{
2018-11-16 16:36:23 +00:00
static std : : vector < std : : string > s_opts ;
if ( s_opts . empty ( ) ) {
s_opts = {
2018-12-03 12:14:28 +00:00
" printer_technology " ,
2019-07-18 12:39:19 +00:00
" bed_shape " , " bed_custom_texture " , " bed_custom_model " , " max_print_height " ,
2018-12-03 12:14:28 +00:00
" bed_shape " , " max_print_height " ,
" display_width " , " display_height " , " display_pixels_x " , " display_pixels_y " ,
2019-05-18 21:21:59 +00:00
" display_mirror_x " , " display_mirror_y " ,
2018-12-13 11:42:45 +00:00
" display_orientation " ,
2019-02-18 15:04:55 +00:00
" fast_tilt_time " , " slow_tilt_time " , " area_fill " ,
2019-04-09 11:42:32 +00:00
" relative_correction " ,
" absolute_correction " ,
" gamma_correction " ,
2019-08-20 15:24:48 +00:00
" min_exposure_time " , " max_exposure_time " ,
" min_initial_exposure_time " , " max_initial_exposure_time " ,
2018-12-18 13:34:16 +00:00
" print_host " , " printhost_apikey " , " printhost_cafile " ,
2018-12-03 12:14:28 +00:00
" printer_notes " ,
2018-11-16 16:36:23 +00:00
" inherits "
} ;
}
return s_opts ;
}
2018-08-03 12:14:25 +00:00
PresetCollection : : PresetCollection ( Preset : : Type type , const std : : vector < std : : string > & keys , const Slic3r : : StaticPrintConfig & defaults , const std : : string & default_name ) :
2017-09-19 11:55:48 +00:00
m_type ( type ) ,
2017-10-25 10:53:31 +00:00
m_edited_preset ( type , " " , false ) ,
m_idx_selected ( 0 ) ,
2018-04-19 10:08:59 +00:00
m_bitmap_main_frame ( new wxBitmap ) ,
2019-03-22 14:45:51 +00:00
m_bitmap_add ( new wxBitmap ) ,
2019-08-06 14:51:32 +00:00
m_bitmap_cache ( new GUI : : BitmapCache )
2017-09-19 11:55:48 +00:00
{
// Insert just the default preset.
2018-08-03 12:14:25 +00:00
this - > add_default_preset ( keys , defaults , default_name ) ;
2017-10-25 10:53:31 +00:00
m_edited_preset . config . apply ( m_presets . front ( ) . config ) ;
}
PresetCollection : : ~ PresetCollection ( )
{
delete m_bitmap_main_frame ;
m_bitmap_main_frame = nullptr ;
2019-03-22 14:45:51 +00:00
delete m_bitmap_add ;
m_bitmap_add = nullptr ;
2019-08-06 14:51:32 +00:00
delete m_bitmap_cache ;
m_bitmap_cache = nullptr ;
2017-09-19 11:55:48 +00:00
}
2017-12-19 18:51:22 +00:00
void PresetCollection : : reset ( bool delete_files )
{
2018-08-01 09:09:51 +00:00
if ( m_presets . size ( ) > m_num_default_presets ) {
2017-12-19 18:51:22 +00:00
if ( delete_files ) {
// Erase the preset files.
for ( Preset & preset : m_presets )
2018-03-09 15:37:33 +00:00
if ( ! preset . is_default & & ! preset . is_external & & ! preset . is_system )
2017-12-19 18:51:22 +00: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 09:09:51 +00:00
m_presets . erase ( m_presets . begin ( ) + m_num_default_presets , m_presets . end ( ) ) ;
2017-12-19 18:51:22 +00:00
this - > select_preset ( 0 ) ;
}
2020-01-02 15:40:32 +00:00
m_map_system_profile_renamed . clear ( ) ;
2017-12-19 18:51:22 +00:00
}
2018-08-03 12:14:25 +00:00
void PresetCollection : : add_default_preset ( const std : : vector < std : : string > & keys , const Slic3r : : StaticPrintConfig & defaults , const std : : string & preset_name )
2018-08-01 09:09:51 +00:00
{
// Insert just the default preset.
m_presets . emplace_back ( Preset ( this - > type ( ) , preset_name , true ) ) ;
2019-08-06 14:51:32 +00:00
m_presets . back ( ) . config . apply_only ( defaults , keys . empty ( ) ? defaults . keys ( ) : keys ) ;
m_presets . back ( ) . loaded = true ;
2018-08-01 09:09:51 +00:00
+ + m_num_default_presets ;
}
2017-10-02 15:35:00 +00: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 )
{
2019-08-06 14:51:32 +00:00
boost : : filesystem : : path dir = boost : : filesystem : : canonical ( boost : : filesystem : : path ( dir_path ) / subdir ) . make_preferred ( ) ;
m_dir_path = dir . string ( ) ;
2017-11-28 09:08:01 +00:00
std : : string errors_cummulative ;
2019-08-06 14:51:32 +00: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 14:30:37 +00:00
if ( Slic3r : : is_ini_file ( dir_entry ) ) {
2017-11-02 15:40:25 +00:00
std : : string name = dir_entry . path ( ) . filename ( ) . string ( ) ;
2017-10-02 15:35:00 +00:00
// Remove the .ini suffix.
name . erase ( name . size ( ) - 4 ) ;
2018-03-09 15:37:33 +00:00
if ( this - > find_preset ( name , false ) ) {
2018-05-01 08:35:07 +00: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 15:37:33 +00:00
continue ;
}
2017-10-02 15:35:00 +00:00
try {
Preset preset ( m_type , name , false ) ;
2017-11-02 15:40:25 +00:00
preset . file = dir_entry . path ( ) . string ( ) ;
2018-10-31 15:22:36 +00: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 14:51:32 +00:00
const Preset & default_preset = this - > default_preset_for ( config ) ;
2018-10-31 15:22:36 +00: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 14:51:32 +00:00
std : : string incorrect_keys = Preset : : remove_invalid_keys ( config , default_preset . config ) ;
2018-10-31 15:22:36 +00: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 ) {
2019-08-06 14:51:32 +00:00
throw std : : runtime_error ( std : : string ( " The selected preset cannot be loaded: " ) + preset . file + " \n \t Reason: " + err . what ( ) ) ;
2018-10-31 15:22:36 +00:00
} catch ( const std : : runtime_error & err ) {
2019-08-06 14:51:32 +00:00
throw std : : runtime_error ( std : : string ( " Failed loading the preset file: " ) + preset . file + " \n \t Reason: " + err . what ( ) ) ;
2018-10-31 15:22:36 +00:00
}
2019-08-06 14:51:32 +00:00
presets_loaded . emplace_back ( preset ) ;
2017-11-28 09:08:01 +00:00
} catch ( const std : : runtime_error & err ) {
errors_cummulative + = err . what ( ) ;
errors_cummulative + = " \n " ;
2019-08-06 14:51:32 +00:00
}
2017-10-02 15:35:00 +00:00
}
2019-08-06 14:51:32 +00:00
m_presets . insert ( m_presets . end ( ) , std : : make_move_iterator ( presets_loaded . begin ( ) ) , std : : make_move_iterator ( presets_loaded . end ( ) ) ) ;
2018-08-01 09:09:51 +00:00
std : : sort ( m_presets . begin ( ) + m_num_default_presets , m_presets . end ( ) ) ;
2017-10-26 15:17:39 +00:00
this - > select_preset ( first_visible_idx ( ) ) ;
2017-11-28 09:08:01 +00:00
if ( ! errors_cummulative . empty ( ) )
throw std : : runtime_error ( errors_cummulative ) ;
2017-10-25 10:53:31 +00: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 15:21:34 +00:00
{
DynamicPrintConfig cfg ( this - > default_preset ( ) . config ) ;
2017-11-03 18:14:33 +00:00
cfg . apply_only ( config , cfg . keys ( ) , true ) ;
2018-06-22 12:17:03 +00:00
return this - > load_preset ( path , name , std : : move ( cfg ) , select ) ;
}
2018-06-26 09:31:01 +00:00
static bool profile_print_params_same ( const DynamicPrintConfig & cfg1 , const DynamicPrintConfig & cfg2 )
{
t_config_option_keys diff = cfg1 . diff ( cfg2 ) ;
// 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 14:51:32 +00:00
for ( const char * key : { " compatible_prints " , " compatible_prints_condition " ,
" compatible_printers " , " compatible_printers_condition " , " inherits " ,
2018-12-03 12:14:28 +00:00
" print_settings_id " , " filament_settings_id " , " sla_print_settings_id " , " sla_material_settings_id " , " printer_settings_id " ,
2018-12-06 13:42:15 +00:00
" printer_model " , " printer_variant " , " default_print_profile " , " default_filament_profile " , " default_sla_print_profile " , " default_sla_material_profile " ,
" printhost_apikey " , " printhost_cafile " } )
2018-06-26 09:31:01 +00:00
diff . erase ( std : : remove ( diff . begin ( ) , diff . end ( ) , key ) , diff . end ( ) ) ;
// Preset with the same name as stored inside the config exists.
return diff . empty ( ) ;
}
2018-06-22 12:17:03 +00:00
// Load a preset from an already parsed config file, insert it into the sorted sequence of presets
// and select it, losing previous modifications.
2019-08-06 14:51:32 +00:00
// In case
2018-06-22 12:17:03 +00:00
Preset & PresetCollection : : load_external_preset (
// 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 ,
// Config to initialize the preset from.
const DynamicPrintConfig & config ,
// Select the preset after loading?
bool select )
{
// Load the preset over a default preset, so that the missing fields are filled in from the default preset.
2018-12-03 12:14:28 +00:00
DynamicPrintConfig cfg ( this - > default_preset_for ( config ) . config ) ;
2018-06-22 12:17:03 +00:00
cfg . apply_only ( config , cfg . keys ( ) , true ) ;
// Is there a preset already loaded with the name stored inside the config?
2018-06-28 18:13:01 +00:00
std : : deque < Preset > : : iterator it = this - > find_preset_internal ( original_name ) ;
2019-08-06 14:51:32 +00:00
bool found = it ! = m_presets . end ( ) & & it - > name = = original_name ;
2020-01-02 15:40:32 +00: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 ( ) ;
}
2018-11-06 18:09:54 +00:00
if ( found & & profile_print_params_same ( it - > config , cfg ) ) {
2018-06-26 09:31:01 +00:00
// The preset exists and it matches the values stored inside config.
if ( select )
this - > select_preset ( it - m_presets . begin ( ) ) ;
return * it ;
}
2018-06-27 14:57:42 +00:00
// Update the "inherits" field.
std : : string & inherits = Preset : : inherits ( cfg ) ;
2018-11-06 18:09:54 +00:00
if ( found & & inherits . empty ( ) ) {
2018-06-27 14:57:42 +00:00
// There is a profile with the same name already loaded. Should we update the "inherits" field?
if ( it - > vendor = = nullptr )
inherits = it - > inherits ( ) ;
else
inherits = it - > name ;
}
2018-06-26 09:31:01 +00: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 14:51:32 +00:00
suffix = " ( " + original_name + " ) " ;
2018-06-26 09:31:01 +00:00
else
suffix = " ( " + original_name + " - " + std : : to_string ( idx ) + " ) " ;
}
new_name = name + suffix ;
it = this - > find_preset_internal ( new_name ) ;
2018-06-28 18:13:01 +00:00
if ( it = = m_presets . end ( ) | | it - > name ! = new_name )
2018-06-26 09:31:01 +00:00
// Unique profile name. Insert a new profile.
break ;
if ( profile_print_params_same ( it - > config , cfg ) ) {
2018-06-22 12:17:03 +00:00
// The preset exists and it matches the values stored inside config.
if ( select )
this - > select_preset ( it - m_presets . begin ( ) ) ;
return * it ;
}
2018-06-26 09:31:01 +00:00
// Form another profile name.
2018-06-22 12:17:03 +00:00
}
2018-06-26 09:31:01 +00:00
// Insert a new profile.
Preset & preset = this - > load_preset ( path , new_name , std : : move ( cfg ) , select ) ;
2018-06-22 12:17:03 +00:00
preset . is_external = true ;
2018-07-17 08:41:17 +00:00
if ( & this - > get_selected_preset ( ) = = & preset )
this - > get_edited_preset ( ) . is_external = true ;
2018-06-22 12:17:03 +00:00
return preset ;
2017-11-02 15:21:34 +00:00
}
Preset & PresetCollection : : load_preset ( const std : : string & path , const std : : string & name , DynamicPrintConfig & & config , bool select )
2017-10-25 10:53:31 +00:00
{
2017-12-06 15:47:53 +00: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 10:53:31 +00:00
it = m_presets . emplace ( it , Preset ( m_type , name , false ) ) ;
2017-12-06 15:47:53 +00:00
}
2017-10-25 10:53:31 +00:00
Preset & preset = * it ;
preset . file = path ;
2017-11-02 15:21:34 +00:00
preset . config = std : : move ( config ) ;
2017-10-25 10:53:31 +00:00
preset . loaded = true ;
2017-11-02 15:21:34 +00:00
preset . is_dirty = false ;
2017-10-25 10:53:31 +00:00
if ( select )
this - > select_preset_by_name ( name , true ) ;
return preset ;
}
2017-10-26 15:17:39 +00:00
void PresetCollection : : save_current_preset ( const std : : string & new_name )
{
2019-08-06 14:51:32 +00:00
// 1) Find the preset with a new_name or create a new one,
// initialize it with the edited config.
2017-12-06 15:47:53 +00:00
auto it = this - > find_preset_internal ( new_name ) ;
if ( it ! = m_presets . end ( ) & & it - > name = = new_name ) {
2017-10-26 15:17:39 +00:00
// Preset with the same name found.
Preset & preset = * it ;
2018-03-12 15:04:32 +00:00
if ( preset . is_default | | preset . is_external | | preset . is_system )
2017-10-26 15:17:39 +00:00
// Cannot overwrite the default preset.
return ;
// Overwriting an existing preset.
preset . config = std : : move ( m_edited_preset . config ) ;
2019-08-06 14:51:32 +00:00
// The newly saved preset will be activated -> make it visible.
preset . is_visible = true ;
2017-10-26 15:17:39 +00:00
} else {
// Creating a new preset.
2019-08-06 14:51:32 +00:00
Preset & preset = * m_presets . insert ( it , m_edited_preset ) ;
2018-06-27 14:57:42 +00:00
std : : string & inherits = preset . inherits ( ) ;
2018-03-12 15:04:32 +00:00
std : : string old_name = preset . name ;
2017-10-26 15:17:39 +00:00
preset . name = new_name ;
2019-08-06 14:51:32 +00:00
preset . file = this - > path_from_name ( new_name ) ;
2018-03-12 15:04:32 +00:00
preset . vendor = nullptr ;
if ( preset . is_system ) {
// 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 08:45:04 +00:00
// inherits = old_name;
2018-03-12 15:04:32 +00:00
} else {
2019-08-06 14:51:32 +00:00
// Inherited from a user preset. Just maintain the "inherited" flag,
2018-03-12 15:04:32 +00: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 14:51:32 +00:00
// The newly saved preset will be activated -> make it visible.
preset . is_visible = true ;
2019-11-29 10:02:30 +00:00
// Just system presets have aliases
preset . alias . clear ( ) ;
2019-08-06 14:51:32 +00: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 15:17:39 +00:00
}
2019-03-22 11:11:23 +00:00
bool PresetCollection : : delete_current_preset ( )
2017-10-26 15:17:39 +00:00
{
const Preset & selected = this - > get_selected_preset ( ) ;
2019-08-06 14:51:32 +00: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 15:17:39 +00:00
// Remove the preset from the list.
m_presets . erase ( m_presets . begin ( ) + m_idx_selected ) ;
// Find the next visible preset.
2017-11-02 15:21:34 +00: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 14:51:32 +00:00
for ( - - new_selected_idx ; new_selected_idx > 0 & & ! m_presets [ new_selected_idx ] . is_visible ; - - new_selected_idx ) ;
2017-11-02 15:21:34 +00:00
this - > select_preset ( new_selected_idx ) ;
2019-08-06 14:51:32 +00:00
return true ;
2017-10-26 15:17:39 +00:00
}
2019-10-02 20:50:55 +00: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 ;
}
2019-04-09 15:40:14 +00:00
void PresetCollection : : load_bitmap_default ( wxWindow * window , const std : : string & file_name )
2017-10-25 10:53:31 +00:00
{
2019-04-12 08:57:30 +00:00
// XXX: See note in PresetBundle::load_compatible_bitmaps()
( void ) window ;
* m_bitmap_main_frame = create_scaled_bitmap ( nullptr , file_name ) ;
2017-10-25 10:53:31 +00:00
}
2019-04-09 15:40:14 +00:00
void PresetCollection : : load_bitmap_add ( wxWindow * window , const std : : string & file_name )
2019-03-22 14:45:51 +00:00
{
2019-04-12 08:57:30 +00:00
// XXX: See note in PresetBundle::load_compatible_bitmaps()
( void ) window ;
* m_bitmap_add = create_scaled_bitmap ( nullptr , file_name ) ;
2019-03-22 14:45:51 +00:00
}
2018-03-12 15:04:32 +00:00
const Preset * PresetCollection : : get_selected_preset_parent ( ) const
{
2019-05-15 15:15:52 +00:00
if ( this - > get_selected_idx ( ) = = - 1 )
// This preset collection has no preset activated yet. Only the get_edited_preset() is valid.
return nullptr ;
2019-07-01 12:56:28 +00:00
2020-01-02 15:40:32 +00:00
const Preset & selected_preset = this - > get_selected_preset ( ) ;
if ( selected_preset . is_system | | selected_preset . is_default )
return & selected_preset ;
2019-08-06 14:51:32 +00:00
2020-01-02 15:40:32 +00:00
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 )
return nullptr ;
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 12:56:28 +00:00
}
return ( preset = = nullptr /* || preset->is_default*/ | | preset - > is_external ) ? nullptr : preset ;
2018-03-12 15:04:32 +00:00
}
2018-04-09 14:50:17 +00:00
const Preset * PresetCollection : : get_preset_parent ( const Preset & child ) const
{
2018-06-27 14:57:42 +00:00
const std : : string & inherits = child . inherits ( ) ;
if ( inherits . empty ( ) )
2019-08-06 14:51:32 +00:00
// return this->get_selected_preset().is_system ? &this->get_selected_preset() : nullptr;
return nullptr ;
2018-06-27 14:57:42 +00:00
const Preset * preset = this - > find_preset ( inherits , false ) ;
2020-01-02 15:40:32 +00:00
if ( preset = = nullptr ) {
auto it = this - > find_preset_renamed ( inherits ) ;
if ( it ! = m_presets . end ( ) )
preset = & ( * it ) ;
}
2018-04-09 14:50:17 +00:00
return ( preset = = nullptr /* || preset->is_default */ | | preset - > is_external ) ? nullptr : preset ;
}
2019-12-05 13:48:11 +00: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 10:02:30 +00:00
const std : : string & PresetCollection : : get_preset_name_by_alias ( const std : : string & alias ) const
2019-11-25 14:57:36 +00:00
{
for ( size_t i = this - > m_presets . front ( ) . is_visible ? 0 : m_num_default_presets ; i < this - > m_presets . size ( ) ; + + i ) {
const Preset & preset = this - > m_presets [ i ] ;
if ( ! preset . is_visible | | ( ! preset . is_compatible & & i ! = m_idx_selected ) )
continue ;
if ( preset . alias = = alias )
return preset . name ;
}
return alias ;
}
2018-04-06 13:42:52 +00:00
const std : : string & PresetCollection : : get_suffix_modified ( ) {
2019-08-06 14:51:32 +00:00
return g_suffix_modified ;
2018-04-06 13:42:52 +00:00
}
2017-10-25 10:53:31 +00: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 15:47:53 +00:00
auto it = this - > find_preset_internal ( name ) ;
2017-10-25 10:53:31 +00:00
// Ensure that a temporary copy is returned if the preset found is currently selected.
2019-08-06 14:51:32 +00:00
return ( it ! = m_presets . end ( ) & & it - > name = = key . name ) ? & this - > preset ( it - m_presets . begin ( ) ) :
2017-10-25 10:53:31 +00: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 09:09:51 +00:00
size_t idx = m_default_suppressed ? m_num_default_presets : 0 ;
2017-10-25 10:53:31 +00:00
for ( ; idx < this - > m_presets . size ( ) ; + + idx )
if ( m_presets [ idx ] . is_visible )
break ;
2018-09-10 07:11:49 +00:00
if ( idx = = m_presets . size ( ) )
2017-10-25 10:53:31 +00:00
idx = 0 ;
return idx ;
2017-10-02 15:35:00 +00:00
}
2017-09-19 11:55:48 +00:00
void PresetCollection : : set_default_suppressed ( bool default_suppressed )
{
if ( m_default_suppressed ! = default_suppressed ) {
m_default_suppressed = default_suppressed ;
2019-08-06 14:51:32 +00: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 11:55:48 +00:00
}
}
2019-12-05 13:48:11 +00:00
size_t PresetCollection : : update_compatible_internal ( const PresetWithVendorProfile & active_printer , const PresetWithVendorProfile * active_print , bool unselect_if_incompatible )
2017-09-19 11:55:48 +00:00
{
2017-12-19 15:48:14 +00:00
DynamicPrintConfig config ;
2019-12-05 13:48:11 +00: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 09:09:51 +00:00
if ( opt )
config . set_key_value ( " num_extruders " , new ConfigOptionInt ( ( int ) static_cast < const ConfigOptionFloats * > ( opt ) - > values . size ( ) ) ) ;
for ( size_t idx_preset = m_num_default_presets ; idx_preset < m_presets . size ( ) ; + + idx_preset ) {
2017-11-10 16:27:05 +00: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-12-05 13:48:11 +00:00
const PresetWithVendorProfile this_preset_with_vendor_profile = this - > get_preset_with_vendor_profile ( preset_edited ) ;
preset_edited . is_compatible = is_compatible_with_printer ( this_preset_with_vendor_profile , active_printer , & config ) ;
if ( active_print ! = nullptr )
preset_edited . is_compatible & = is_compatible_with_print ( this_preset_with_vendor_profile , * active_print ) ;
if ( ! preset_edited . is_compatible & & selected & & unselect_if_incompatible )
2018-10-31 15:22:36 +00:00
m_idx_selected = - 1 ;
2017-11-10 16:27:05 +00:00
if ( selected )
preset_selected . is_compatible = preset_edited . is_compatible ;
}
2018-04-13 12:49:33 +00:00
return m_idx_selected ;
2017-09-19 11:55:48 +00:00
}
2017-10-25 10:53:31 +00: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 11:55:48 +00:00
// Update the wxChoice UI component from this list of presets.
2019-08-06 14:51:32 +00:00
// Hide the
2018-10-09 10:41:05 +00:00
void PresetCollection : : update_platter_ui ( GUI : : PresetComboBox * ui )
2017-09-19 11:55:48 +00:00
{
2019-03-16 21:23:51 +00:00
if ( ui = = nullptr )
2017-09-19 11:55:48 +00:00
return ;
2019-03-13 12:13:18 +00:00
2017-10-26 15:17:39 +00:00
// Otherwise fill in the list from scratch.
2017-10-27 22:18:02 +00:00
ui - > Freeze ( ) ;
2017-10-26 15:17:39 +00:00
ui - > Clear ( ) ;
2019-11-21 19:33:15 +00:00
size_t selected_preset_item = INT_MAX ; // some value meaning that no one item is selected
2018-04-19 10:08:59 +00:00
2019-08-06 14:51:32 +00:00
const Preset & selected_preset = this - > get_selected_preset ( ) ;
// Show wide icons if the currently selected preset is not compatible with the current printer,
// and draw a red flag in front of the selected preset.
bool wide_icons = ! selected_preset . is_compatible & & m_bitmap_incompatible ! = nullptr ;
2018-04-19 10:08:59 +00:00
2019-04-23 14:33:06 +00:00
/* It's supposed that standard size of an icon is 16px*16px for 100% scaled display.
* So set sizes for solid_colored icons used for filament preset
* and scale them in respect to em_unit value
*/
const float scale_f = ui - > em_unit ( ) * 0.1f ;
const int icon_height = 16 * scale_f + 0.5f ;
const int icon_width = 16 * scale_f + 0.5f ;
const int thin_space_icon_width = 4 * scale_f + 0.5f ;
const int wide_space_icon_width = 6 * scale_f + 0.5f ;
2019-08-06 14:51:32 +00:00
std : : map < wxString , wxBitmap * > nonsys_presets ;
wxString selected = " " ;
2019-11-25 14:57:36 +00:00
wxString tooltip = " " ;
2019-08-06 14:51:32 +00:00
if ( ! this - > m_presets . front ( ) . is_visible )
2019-03-22 16:07:12 +00:00
ui - > set_label_marker ( ui - > Append ( PresetCollection : : separator ( L ( " System presets " ) ) , wxNullBitmap ) ) ;
2019-08-06 14:51:32 +00:00
for ( size_t i = this - > m_presets . front ( ) . is_visible ? 0 : m_num_default_presets ; i < this - > m_presets . size ( ) ; + + i ) {
2017-10-26 15:17:39 +00:00
const Preset & preset = this - > m_presets [ i ] ;
2017-11-10 16:27:05 +00:00
if ( ! preset . is_visible | | ( ! preset . is_compatible & & i ! = m_idx_selected ) )
continue ;
2019-08-06 14:51:32 +00:00
std : : string bitmap_key = " " ;
// If the filament preset is not compatible and there is a "red flag" icon loaded, show it left
// to the filament color image.
if ( wide_icons )
bitmap_key + = preset . is_compatible ? " ,cmpt " : " ,ncmpt " ;
bitmap_key + = ( preset . is_system | | preset . is_default ) ? " ,syst " : " ,nsyst " ;
wxBitmap * bmp = m_bitmap_cache - > find ( bitmap_key ) ;
if ( bmp = = nullptr ) {
// Create the bitmap with color bars.
std : : vector < wxBitmap > bmps ;
if ( wide_icons )
// Paint a red flag for incompatible presets.
bmps . emplace_back ( preset . is_compatible ? m_bitmap_cache - > mkclear ( icon_width , icon_height ) : * m_bitmap_incompatible ) ;
// Paint the color bars.
bmps . emplace_back ( m_bitmap_cache - > mkclear ( thin_space_icon_width , icon_height ) ) ;
bmps . emplace_back ( * m_bitmap_main_frame ) ;
// Paint a lock at the system presets.
bmps . emplace_back ( m_bitmap_cache - > mkclear ( wide_space_icon_width , icon_height ) ) ;
bmps . emplace_back ( ( preset . is_system | | preset . is_default ) ? * m_bitmap_lock : m_bitmap_cache - > mkclear ( icon_width , icon_height ) ) ;
bmp = m_bitmap_cache - > insert ( bitmap_key , bmps ) ;
}
2019-11-25 14:57:36 +00:00
const std : : string name = preset . alias . empty ( ) ? preset . name : preset . alias ;
2019-08-06 14:51:32 +00:00
if ( preset . is_default | | preset . is_system ) {
2019-11-25 14:57:36 +00:00
ui - > Append ( wxString : : FromUTF8 ( ( /*preset.*/ name + ( preset . is_dirty ? g_suffix_modified : " " ) ) . c_str ( ) ) ,
2019-08-06 14:51:32 +00:00
( bmp = = 0 ) ? ( m_bitmap_main_frame ? * m_bitmap_main_frame : wxNullBitmap ) : * bmp ) ;
2019-11-21 19:33:15 +00:00
if ( i = = m_idx_selected | |
// just in case: mark selected_preset_item as a first added element
2019-11-25 14:57:36 +00:00
selected_preset_item = = INT_MAX ) {
2019-08-06 14:51:32 +00:00
selected_preset_item = ui - > GetCount ( ) - 1 ;
2019-11-25 14:57:36 +00:00
tooltip = wxString : : FromUTF8 ( preset . name . c_str ( ) ) ;
}
2019-08-06 14:51:32 +00:00
}
else
{
2019-11-25 14:57:36 +00:00
nonsys_presets . emplace ( wxString : : FromUTF8 ( ( /*preset.*/ name + ( preset . is_dirty ? g_suffix_modified : " " ) ) . c_str ( ) ) , bmp /*preset.is_compatible*/ ) ;
if ( i = = m_idx_selected ) {
selected = wxString : : FromUTF8 ( ( /*preset.*/ name + ( preset . is_dirty ? g_suffix_modified : " " ) ) . c_str ( ) ) ;
tooltip = wxString : : FromUTF8 ( preset . name . c_str ( ) ) ;
}
2019-08-06 14:51:32 +00:00
}
if ( i + 1 = = m_num_default_presets )
2019-03-22 16:07:12 +00:00
ui - > set_label_marker ( ui - > Append ( PresetCollection : : separator ( L ( " System presets " ) ) , wxNullBitmap ) ) ;
2019-08-06 14:51:32 +00:00
}
if ( ! nonsys_presets . empty ( ) )
{
2019-03-22 16:07:12 +00:00
ui - > set_label_marker ( ui - > Append ( PresetCollection : : separator ( L ( " User presets " ) ) , wxNullBitmap ) ) ;
2019-08-06 14:51:32 +00:00
for ( std : : map < wxString , wxBitmap * > : : iterator it = nonsys_presets . begin ( ) ; it ! = nonsys_presets . end ( ) ; + + it ) {
ui - > Append ( it - > first , * it - > second ) ;
2019-11-21 19:33:15 +00:00
if ( it - > first = = selected | |
// just in case: mark selected_preset_item as a first added element
selected_preset_item = = INT_MAX )
2019-08-06 14:51:32 +00:00
selected_preset_item = ui - > GetCount ( ) - 1 ;
}
}
if ( m_type = = Preset : : TYPE_PRINTER ) {
std : : string bitmap_key = " " ;
// If the filament preset is not compatible and there is a "red flag" icon loaded, show it left
// to the filament color image.
if ( wide_icons )
bitmap_key + = " wide, " ;
bitmap_key + = " add_printer " ;
wxBitmap * bmp = m_bitmap_cache - > find ( bitmap_key ) ;
if ( bmp = = nullptr ) {
// Create the bitmap with color bars.
std : : vector < wxBitmap > bmps ;
if ( wide_icons )
// Paint a red flag for incompatible presets.
bmps . emplace_back ( m_bitmap_cache - > mkclear ( icon_width , icon_height ) ) ;
// Paint the color bars.
bmps . emplace_back ( m_bitmap_cache - > mkclear ( thin_space_icon_width , icon_height ) ) ;
bmps . emplace_back ( * m_bitmap_main_frame ) ;
// Paint a lock at the system presets.
bmps . emplace_back ( m_bitmap_cache - > mkclear ( wide_space_icon_width , icon_height ) ) ;
bmps . emplace_back ( m_bitmap_add ? * m_bitmap_add : wxNullBitmap ) ;
bmp = m_bitmap_cache - > insert ( bitmap_key , bmps ) ;
}
2019-06-04 16:01:41 +00:00
ui - > set_label_marker ( ui - > Append ( PresetCollection : : separator ( L ( " Add a new printer " ) ) , * bmp ) , GUI : : PresetComboBox : : LABEL_ITEM_WIZARD_PRINTERS ) ;
} else if ( m_type = = Preset : : TYPE_SLA_MATERIAL ) {
ui - > set_label_marker ( ui - > Append ( PresetCollection : : separator ( L ( " Add/Remove materials " ) ) , wxNullBitmap ) , GUI : : PresetComboBox : : LABEL_ITEM_WIZARD_MATERIALS ) ;
2019-08-06 14:51:32 +00:00
}
2019-11-21 19:33:15 +00:00
/* But, if selected_preset_item is still equal to INT_MAX, it means that
* there is no presets added to the list .
* So , select last combobox item ( " Add/Remove preset " )
*/
if ( selected_preset_item = = INT_MAX )
selected_preset_item = ui - > GetCount ( ) - 1 ;
2019-08-06 14:51:32 +00:00
ui - > SetSelection ( selected_preset_item ) ;
2019-11-25 14:57:36 +00:00
ui - > SetToolTip ( tooltip . IsEmpty ( ) ? ui - > GetString ( selected_preset_item ) : tooltip ) ;
2019-03-23 09:04:44 +00:00
ui - > check_selection ( ) ;
2019-04-23 14:33:06 +00:00
ui - > Thaw ( ) ;
// Update control min size after rescale (changed Display DPI under MSW)
if ( ui - > GetMinWidth ( ) ! = 20 * ui - > em_unit ( ) )
ui - > SetMinSize ( wxSize ( 20 * ui - > em_unit ( ) , ui - > GetSize ( ) . GetHeight ( ) ) ) ;
2017-09-19 11:55:48 +00:00
}
2019-04-23 14:33:06 +00:00
size_t PresetCollection : : update_tab_ui ( wxBitmapComboBox * ui , bool show_incompatible , const int em /* = 10*/ )
2017-10-25 10:53:31 +00:00
{
if ( ui = = nullptr )
2018-04-19 10:08:59 +00:00
return 0 ;
2017-10-27 22:18:02 +00:00
ui - > Freeze ( ) ;
2017-10-25 10:53:31 +00:00
ui - > Clear ( ) ;
2019-11-21 19:33:15 +00:00
size_t selected_preset_item = INT_MAX ; // some value meaning that no one item is selected
2018-04-19 10:08:59 +00:00
2019-04-23 14:33:06 +00:00
/* It's supposed that standard size of an icon is 16px*16px for 100% scaled display.
* So set sizes for solid_colored ( empty ) icons used for preset
* and scale them in respect to em_unit value
*/
const float scale_f = em * 0.1f ;
const int icon_height = 16 * scale_f + 0.5f ;
const int icon_width = 16 * scale_f + 0.5f ;
2019-08-06 14:51:32 +00:00
std : : map < wxString , wxBitmap * > nonsys_presets ;
wxString selected = " " ;
if ( ! this - > m_presets . front ( ) . is_visible )
ui - > Append ( PresetCollection : : separator ( L ( " System presets " ) ) , wxNullBitmap ) ;
for ( size_t i = this - > m_presets . front ( ) . is_visible ? 0 : m_num_default_presets ; i < this - > m_presets . size ( ) ; + + i ) {
2017-10-25 10:53:31 +00:00
const Preset & preset = this - > m_presets [ i ] ;
2017-12-13 13:00:14 +00:00
if ( ! preset . is_visible | | ( ! show_incompatible & & ! preset . is_compatible & & i ! = m_idx_selected ) )
2017-11-10 16:27:05 +00:00
continue ;
2019-08-06 14:51:32 +00:00
std : : string bitmap_key = " tab " ;
bitmap_key + = preset . is_compatible ? " ,cmpt " : " ,ncmpt " ;
bitmap_key + = ( preset . is_system | | preset . is_default ) ? " ,syst " : " ,nsyst " ;
wxBitmap * bmp = m_bitmap_cache - > find ( bitmap_key ) ;
if ( bmp = = nullptr ) {
// Create the bitmap with color bars.
std : : vector < wxBitmap > bmps ;
const wxBitmap * tmp_bmp = preset . is_compatible ? m_bitmap_compatible : m_bitmap_incompatible ;
bmps . emplace_back ( ( tmp_bmp = = 0 ) ? ( m_bitmap_main_frame ? * m_bitmap_main_frame : wxNullBitmap ) : * tmp_bmp ) ;
// Paint a lock at the system presets.
bmps . emplace_back ( ( preset . is_system | | preset . is_default ) ? * m_bitmap_lock : m_bitmap_cache - > mkclear ( icon_width , icon_height ) ) ;
bmp = m_bitmap_cache - > insert ( bitmap_key , bmps ) ;
}
if ( preset . is_default | | preset . is_system ) {
ui - > Append ( wxString : : FromUTF8 ( ( preset . name + ( preset . is_dirty ? g_suffix_modified : " " ) ) . c_str ( ) ) ,
( bmp = = 0 ) ? ( m_bitmap_main_frame ? * m_bitmap_main_frame : wxNullBitmap ) : * bmp ) ;
2019-11-21 19:33:15 +00:00
if ( i = = m_idx_selected | |
// just in case: mark selected_preset_item as a first added element
selected_preset_item = = INT_MAX )
2019-08-06 14:51:32 +00:00
selected_preset_item = ui - > GetCount ( ) - 1 ;
}
else
{
nonsys_presets . emplace ( wxString : : FromUTF8 ( ( preset . name + ( preset . is_dirty ? g_suffix_modified : " " ) ) . c_str ( ) ) , bmp /*preset.is_compatible*/ ) ;
if ( i = = m_idx_selected )
selected = wxString : : FromUTF8 ( ( preset . name + ( preset . is_dirty ? g_suffix_modified : " " ) ) . c_str ( ) ) ;
}
if ( i + 1 = = m_num_default_presets )
ui - > Append ( PresetCollection : : separator ( L ( " System presets " ) ) , wxNullBitmap ) ;
}
if ( ! nonsys_presets . empty ( ) )
{
ui - > Append ( PresetCollection : : separator ( L ( " User presets " ) ) , wxNullBitmap ) ;
for ( std : : map < wxString , wxBitmap * > : : iterator it = nonsys_presets . begin ( ) ; it ! = nonsys_presets . end ( ) ; + + it ) {
ui - > Append ( it - > first , * it - > second ) ;
2019-11-21 19:33:15 +00:00
if ( it - > first = = selected | |
// just in case: mark selected_preset_item as a first added element
selected_preset_item = = INT_MAX )
2019-08-06 14:51:32 +00:00
selected_preset_item = ui - > GetCount ( ) - 1 ;
}
2017-10-25 10:53:31 +00:00
}
2019-03-22 14:45:51 +00:00
if ( m_type = = Preset : : TYPE_PRINTER ) {
wxBitmap * bmp = m_bitmap_cache - > find ( " add_printer_tab " ) ;
if ( bmp = = nullptr ) {
// Create the bitmap with color bars.
std : : vector < wxBitmap > bmps ;
bmps . emplace_back ( * m_bitmap_main_frame ) ;
bmps . emplace_back ( m_bitmap_add ? * m_bitmap_add : wxNullBitmap ) ;
bmp = m_bitmap_cache - > insert ( " add_printer_tab " , bmps ) ;
}
2019-03-22 16:07:12 +00:00
ui - > Append ( PresetCollection : : separator ( " Add a new printer " ) , * bmp ) ;
2019-03-22 14:45:51 +00:00
}
2019-11-21 19:33:15 +00:00
/* But, if selected_preset_item is still equal to INT_MAX, it means that
* there is no presets added to the list .
* So , select last combobox item ( " Add/Remove preset " )
*/
if ( selected_preset_item = = INT_MAX )
selected_preset_item = ui - > GetCount ( ) - 1 ;
2019-08-06 14:51:32 +00:00
ui - > SetSelection ( selected_preset_item ) ;
ui - > SetToolTip ( ui - > GetString ( selected_preset_item ) ) ;
2017-10-27 22:18:02 +00:00
ui - > Thaw ( ) ;
2019-08-06 14:51:32 +00:00
return selected_preset_item ;
2017-10-25 10:53:31 +00:00
}
// Update a dirty floag of the current preset, update the labels of the UI component accordingly.
// Return true if the dirty flag changed.
2017-11-10 16:27:05 +00:00
bool PresetCollection : : update_dirty_ui ( wxBitmapComboBox * ui )
2017-10-25 10:53:31 +00:00
{
2017-11-10 16:27:05 +00:00
wxWindowUpdateLocker noUpdates ( ui ) ;
2017-10-25 10:53:31 +00:00
// 1) Update the dirty flag of the current preset.
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 18:30:05 +00:00
this - > get_edited_preset ( ) . is_dirty = is_dirty ;
2017-10-25 10:53:31 +00:00
// 2) Update the labels.
for ( unsigned int ui_id = 0 ; ui_id < ui - > GetCount ( ) ; + + ui_id ) {
std : : string old_label = ui - > GetString ( ui_id ) . utf8_str ( ) . data ( ) ;
2017-10-30 17:41:50 +00:00
std : : string preset_name = Preset : : remove_suffix_modified ( old_label ) ;
2017-10-25 10:53:31 +00:00
const Preset * preset = this - > find_preset ( preset_name , false ) ;
2018-10-23 13:27:31 +00:00
// The old_label could be the "----- system presets ------" or the "------- user presets --------" separator.
// assert(preset != nullptr);
2019-08-06 14:51:32 +00:00
if ( preset ! = nullptr ) {
std : : string new_label = preset - > is_dirty ? preset - > name + g_suffix_modified : preset - > name ;
if ( old_label ! = new_label )
ui - > SetString ( ui_id , wxString : : FromUTF8 ( new_label . c_str ( ) ) ) ;
}
2017-10-25 10:53:31 +00:00
}
2017-12-20 12:46:43 +00:00
# ifdef __APPLE__
// wxWidgets on OSX do not upload the text of the combo box line automatically.
// Force it to update by re-selecting.
ui - > SetSelection ( ui - > GetSelection ( ) ) ;
# endif /* __APPLE __ */
2017-10-25 10:53:31 +00:00
return was_dirty ! = is_dirty ;
}
2018-12-04 16:56:49 +00: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 14:51:32 +00: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 ;
for ( int i = 0 ; i < opt_cur - > values . size ( ) ; i + + )
{
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 16:56:49 +00: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 )
{
if ( opt_key = = " bed_shape " | | opt_key = = " compatible_prints " | | opt_key = = " compatible_printers " ) {
diff . emplace_back ( opt_key ) ;
continue ;
}
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 ;
}
}
}
return diff ;
}
2018-08-08 15:47:59 +00:00
std : : vector < std : : string > PresetCollection : : dirty_options ( const Preset * edited , const Preset * reference , const bool deep_compare /*= false*/ )
2018-03-12 15:04:32 +00:00
{
std : : vector < std : : string > changed ;
2019-08-06 14:51:32 +00:00
if ( edited ! = nullptr & & reference ! = nullptr ) {
2018-08-08 15:47:59 +00:00
changed = deep_compare ?
2019-08-06 14:51:32 +00:00
deep_diff ( edited - > config , reference - > config ) :
reference - > config . diff ( edited - > config ) ;
2018-03-12 15:04:32 +00: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 16:56:49 +00:00
std : : initializer_list < const char * > optional_keys { " compatible_prints " , " compatible_printers " } ;
2018-03-12 15:04:32 +00: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 14:07:38 +00:00
}
2017-12-13 13:44:27 +00:00
return changed ;
}
2017-12-13 13:00:14 +00: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 10:53:31 +00:00
Preset & PresetCollection : : select_preset ( size_t idx )
{
2017-11-02 15:21:34 +00:00
for ( Preset & preset : m_presets )
preset . is_dirty = false ;
2017-10-25 10:53:31 +00:00
if ( idx > = m_presets . size ( ) )
idx = first_visible_idx ( ) ;
m_idx_selected = idx ;
m_edited_preset = m_presets [ idx ] ;
2019-08-06 14:51:32 +00: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 10:53:31 +00:00
return m_presets [ idx ] ;
}
2017-10-26 15:17:39 +00:00
bool PresetCollection : : select_preset_by_name ( const std : : string & name_w_suffix , bool force )
2019-08-06 14:51:32 +00:00
{
2017-10-30 17:41:50 +00:00
std : : string name = Preset : : remove_suffix_modified ( name_w_suffix ) ;
2017-10-25 10:53:31 +00:00
// 1) Try to find the preset by its name.
2017-12-06 15:47:53 +00:00
auto it = this - > find_preset_internal ( name ) ;
2017-10-25 10:53:31 +00:00
size_t idx = 0 ;
2019-08-06 14:51:32 +00:00
if ( it ! = m_presets . end ( ) & & it - > name = = name & & it - > is_visible )
2018-04-13 12:49:33 +00:00
// Preset found by its name and it is visible.
2017-10-25 10:53:31 +00:00
idx = it - m_presets . begin ( ) ;
else {
// Find the first visible preset.
2018-08-01 09:09:51 +00:00
for ( size_t i = m_default_suppressed ? m_num_default_presets : 0 ; i < m_presets . size ( ) ; + + i )
2017-10-25 10:53:31 +00: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 12:49:33 +00:00
bool PresetCollection : : select_preset_by_name_strict ( const std : : string & name )
2019-08-06 14:51:32 +00:00
{
2018-04-13 12:49:33 +00: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 14:51:32 +00:00
if ( it ! = m_presets . end ( ) & & it - > name = = name & & it - > is_visible )
2018-04-13 12:49:33 +00: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 11:35:51 +00:00
// Merge one vendor's presets with the other vendor's presets, report duplicates.
2019-06-17 14:39:22 +00:00
std : : vector < std : : string > PresetCollection : : merge_presets ( PresetCollection & & other , const VendorMap & new_vendors )
2018-04-18 11:35:51 +00: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 09:09:51 +00:00
auto it = std : : lower_bound ( m_presets . begin ( ) + m_num_default_presets , m_presets . end ( ) , key ) ;
2018-04-18 11:35:51 +00: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 14:39:22 +00:00
auto it = new_vendors . find ( preset . vendor - > id ) ;
2018-04-18 11:35:51 +00:00
assert ( it ! = new_vendors . end ( ) ) ;
2019-06-17 14:39:22 +00:00
preset . vendor = & it - > second ;
2018-04-18 11:35:51 +00:00
}
this - > m_presets . emplace ( it , std : : move ( preset ) ) ;
} else
duplicates . emplace_back ( std : : move ( preset . name ) ) ;
}
return duplicates ;
}
2020-01-02 15:40:32 +00: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 10:53:31 +00:00
std : : string PresetCollection : : name ( ) const
{
switch ( this - > type ( ) ) {
2019-01-21 11:34:28 +00: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 14:09:16 +00:00
case Preset : : TYPE_SLA_MATERIAL : return L ( " SLA material " ) ;
2019-01-21 11:34:28 +00:00
case Preset : : TYPE_PRINTER : return L ( " printer " ) ;
2018-12-04 16:56:49 +00:00
default : return " invalid " ;
2017-10-25 10:53:31 +00:00
}
}
2019-02-03 19:17:55 +00: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 " ;
}
}
2019-01-08 09:52:11 +00: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 14:51:32 +00:00
for ( const Preset & preset : m_presets )
if ( preset . is_system )
out . emplace_back ( preset . name ) ;
2019-01-08 09:52:11 +00:00
std : : sort ( out . begin ( ) , out . end ( ) ) ;
return out ;
}
2017-12-20 10:28:16 +00: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 14:51:32 +00:00
std : : string file_name = boost : : iends_with ( new_name , " .ini " ) ? new_name : ( new_name + " .ini " ) ;
2017-12-20 10:28:16 +00:00
return ( boost : : filesystem : : path ( m_dir_path ) / file_name ) . make_preferred ( ) . string ( ) ;
}
2019-04-10 07:56:32 +00:00
void PresetCollection : : clear_bitmap_cache ( )
{
m_bitmap_cache - > clear ( ) ;
}
2019-03-22 16:07:12 +00:00
wxString PresetCollection : : separator ( const std : : string & label )
{
2019-08-06 14:51:32 +00:00
return wxString : : FromUTF8 ( PresetCollection : : separator_head ( ) ) + _ ( label ) + wxString : : FromUTF8 ( PresetCollection : : separator_tail ( ) ) ;
2019-03-22 16:07:12 +00:00
}
2019-08-06 14:51:32 +00:00
const Preset & PrinterPresetCollection : : default_preset_for ( const DynamicPrintConfig & config ) const
{
2018-10-31 15:22:36 +00:00
const ConfigOptionEnumGeneric * opt_printer_technology = config . opt < ConfigOptionEnumGeneric > ( " printer_technology " ) ;
2019-08-06 14:51:32 +00:00
return this - > default_preset ( ( opt_printer_technology = = nullptr | | opt_printer_technology - > value = = ptFFF ) ? 0 : 1 ) ;
2018-10-31 15:22:36 +00:00
}
2019-04-26 14:53:04 +00: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 ;
}
2017-09-19 11:55:48 +00:00
} // namespace Slic3r