2018-12-06 11:52:28 +00:00
# include "libslic3r/libslic3r.h"
# include "libslic3r/Utils.hpp"
2017-10-30 17:41:50 +00:00
# include "AppConfig.hpp"
2020-09-14 14:27:55 +00:00
# include "Exception.hpp"
2021-05-10 06:13:23 +00:00
# include "LocalesUtils.hpp"
2020-10-22 12:45:15 +00:00
# include "Thread.hpp"
2021-06-25 14:44:06 +00:00
# include "format.hpp"
2021-05-10 06:13:23 +00:00
2017-10-30 17:41:50 +00:00
# include <utility>
2018-03-28 09:36:36 +00:00
# include <vector>
2018-04-11 11:12:08 +00:00
# include <stdexcept>
2017-10-30 17:41:50 +00:00
2020-02-28 10:13:14 +00:00
# include <boost/filesystem/path.hpp>
# include <boost/filesystem/operations.hpp>
2017-10-30 17:41:50 +00:00
# include <boost/nowide/cenv.hpp>
# include <boost/nowide/fstream.hpp>
# include <boost/property_tree/ini_parser.hpp>
2020-02-28 10:13:14 +00:00
# include <boost/property_tree/ptree_fwd.hpp>
2018-03-28 09:36:36 +00:00
# include <boost/algorithm/string/predicate.hpp>
2020-02-28 10:13:14 +00:00
# include <boost/format/format_fwd.hpp>
2021-06-25 14:44:06 +00:00
# include <boost/log/trivial.hpp>
2017-10-30 17:41:50 +00:00
2021-06-25 14:44:06 +00:00
# ifdef WIN32
//FIXME replace the two following includes with <boost/md5.hpp> after it becomes mainstream.
# include <boost/uuid/detail/md5.hpp>
# include <boost/algorithm/hex.hpp>
# endif
2019-08-19 10:55:57 +00:00
2017-10-30 17:41:50 +00:00
namespace Slic3r {
2018-03-28 09:36:36 +00:00
static const std : : string VENDOR_PREFIX = " vendor: " ;
static const std : : string MODEL_PREFIX = " model: " ;
2021-06-25 15:53:31 +00:00
// Because of a crash in PrusaSlicer 2.3.0/2.3.1 when showing an update notification with some locales, we don't want PrusaSlicer 2.3.0/2.3.1
// to show this notification. On the other hand, we would like PrusaSlicer 2.3.2 to show an update notification of the upcoming PrusaSlicer 2.4.0.
// Thus we will let PrusaSlicer 2.3.2 and couple of follow-up versions to download the version number from an alternate file until the PrusaSlicer 2.3.0/2.3.1
// are phased out, then we will revert to the original name.
//static const std::string VERSION_CHECK_URL = "https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/PrusaSlicer.version";
static const std : : string VERSION_CHECK_URL = " https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/PrusaSlicer.version2 " ;
2018-03-28 09:36:36 +00:00
2019-06-03 08:15:26 +00:00
const std : : string AppConfig : : SECTION_FILAMENTS = " filaments " ;
const std : : string AppConfig : : SECTION_MATERIALS = " sla_materials " ;
2017-10-30 17:41:50 +00:00
void AppConfig : : reset ( )
{
m_storage . clear ( ) ;
set_defaults ( ) ;
} ;
// Override missing or keys with their defaults.
void AppConfig : : set_defaults ( )
{
2020-10-05 13:42:35 +00:00
if ( m_mode = = EAppMode : : Editor ) {
// Reset the empty fields to defaults.
if ( get ( " autocenter " ) . empty ( ) )
set ( " autocenter " , " 0 " ) ;
// Disable background processing by default as it is not stable.
if ( get ( " background_processing " ) . empty ( ) )
set ( " background_processing " , " 0 " ) ;
// If set, the "Controller" tab for the control of the printer over serial line and the serial port settings are hidden.
// By default, Prusa has the controller hidden.
if ( get ( " no_controller " ) . empty ( ) )
set ( " no_controller " , " 1 " ) ;
// If set, the "- default -" selections of print/filament/printer are suppressed, if there is a valid preset available.
if ( get ( " no_defaults " ) . empty ( ) )
set ( " no_defaults " , " 1 " ) ;
if ( get ( " show_incompatible_presets " ) . empty ( ) )
set ( " show_incompatible_presets " , " 0 " ) ;
2020-11-16 07:39:25 +00:00
if ( get ( " show_drop_project_dialog " ) . empty ( ) )
set ( " show_drop_project_dialog " , " 1 " ) ;
if ( get ( " drop_project_action " ) . empty ( ) )
set ( " drop_project_action " , " 1 " ) ;
2020-10-05 13:42:35 +00:00
if ( get ( " version_check " ) . empty ( ) )
set ( " version_check " , " 1 " ) ;
if ( get ( " preset_update " ) . empty ( ) )
set ( " preset_update " , " 1 " ) ;
if ( get ( " export_sources_full_pathnames " ) . empty ( ) )
set ( " export_sources_full_pathnames " , " 0 " ) ;
2020-11-26 09:09:34 +00:00
# ifdef _WIN32
if ( get ( " associate_3mf " ) . empty ( ) )
set ( " associate_3mf " , " 0 " ) ;
if ( get ( " associate_stl " ) . empty ( ) )
set ( " associate_stl " , " 0 " ) ;
2021-06-18 17:46:04 +00:00
if ( get ( " dark_color_mode " ) . empty ( ) )
set ( " dark_color_mode " , " 0 " ) ;
if ( get ( " tabs_as_menu " ) . empty ( ) )
set ( " tabs_as_menu " , " 0 " ) ;
2020-11-26 09:09:34 +00:00
# endif // _WIN32
2020-10-05 13:42:35 +00:00
// remove old 'use_legacy_opengl' parameter from this config, if present
if ( ! get ( " use_legacy_opengl " ) . empty ( ) )
erase ( " " , " use_legacy_opengl " ) ;
2018-05-22 11:57:28 +00:00
2019-08-19 10:55:57 +00:00
# ifdef __APPLE__
2020-10-05 13:42:35 +00:00
if ( get ( " use_retina_opengl " ) . empty ( ) )
set ( " use_retina_opengl " , " 1 " ) ;
2019-01-24 10:30:29 +00:00
# endif
2020-10-05 13:42:35 +00:00
if ( get ( " single_instance " ) . empty ( ) )
2020-10-17 07:23:05 +00:00
set ( " single_instance " ,
# ifdef __APPLE__
" 1 "
2020-10-23 05:51:58 +00:00
# else // __APPLE__
2020-10-17 07:23:05 +00:00
" 0 "
2020-10-23 05:51:58 +00:00
# endif // __APPLE__
2020-10-17 07:23:05 +00:00
) ;
2020-04-29 08:50:28 +00:00
2020-10-05 13:42:35 +00:00
if ( get ( " remember_output_path " ) . empty ( ) )
set ( " remember_output_path " , " 1 " ) ;
2018-09-17 13:12:13 +00:00
2020-10-05 13:42:35 +00:00
if ( get ( " remember_output_path_removable " ) . empty ( ) )
set ( " remember_output_path_removable " , " 1 " ) ;
if ( get ( " use_custom_toolbar_size " ) . empty ( ) )
set ( " use_custom_toolbar_size " , " 0 " ) ;
if ( get ( " custom_toolbar_size " ) . empty ( ) )
set ( " custom_toolbar_size " , " 100 " ) ;
if ( get ( " auto_toolbar_size " ) . empty ( ) )
set ( " auto_toolbar_size " , " 100 " ) ;
# if ENABLE_ENVIRONMENT_MAP
if ( get ( " use_environment_map " ) . empty ( ) )
set ( " use_environment_map " , " 0 " ) ;
# endif // ENABLE_ENVIRONMENT_MAP
if ( get ( " use_inches " ) . empty ( ) )
set ( " use_inches " , " 0 " ) ;
2020-11-26 10:00:24 +00:00
if ( get ( " default_action_on_close_application " ) . empty ( ) )
set ( " default_action_on_close_application " , " none " ) ; // , "discard" or "save"
if ( get ( " default_action_on_select_preset " ) . empty ( ) )
set ( " default_action_on_select_preset " , " none " ) ; // , "transfer", "discard" or "save"
2021-02-09 15:03:32 +00:00
2021-09-22 10:44:13 +00:00
if ( get ( " default_action_on_new_project " ) . empty ( ) )
set ( " default_action_on_new_project " , " none " ) ; // , "keep(transfer)", "discard" or "save"
2021-02-09 15:03:32 +00:00
if ( get ( " color_mapinulation_panel " ) . empty ( ) )
set ( " color_mapinulation_panel " , " 0 " ) ;
2021-05-26 13:36:02 +00:00
if ( get ( " order_volumes " ) . empty ( ) )
set ( " order_volumes " , " 1 " ) ;
2021-09-30 08:19:58 +00:00
if ( get ( " clear_undo_redo_stack_on_new_project " ) . empty ( ) )
set ( " clear_undo_redo_stack_on_new_project " , " 1 " ) ;
2020-10-05 13:42:35 +00:00
}
2020-11-26 09:09:34 +00:00
else {
# ifdef _WIN32
if ( get ( " associate_gcode " ) . empty ( ) )
set ( " associate_gcode " , " 0 " ) ;
# endif // _WIN32
}
2020-10-05 13:42:35 +00:00
2020-10-15 08:25:13 +00:00
if ( get ( " seq_top_layer_only " ) . empty ( ) )
set ( " seq_top_layer_only " , " 1 " ) ;
2019-06-24 13:55:14 +00:00
if ( get ( " use_perspective_camera " ) . empty ( ) )
set ( " use_perspective_camera " , " 1 " ) ;
2019-06-20 08:02:52 +00:00
2020-01-16 08:12:36 +00:00
if ( get ( " use_free_camera " ) . empty ( ) )
set ( " use_free_camera " , " 0 " ) ;
2020-11-02 12:08:13 +00:00
if ( get ( " reverse_mouse_wheel_zoom " ) . empty ( ) )
set ( " reverse_mouse_wheel_zoom " , " 0 " ) ;
2020-06-10 09:02:54 +00:00
2020-09-10 14:03:43 +00:00
if ( get ( " show_splash_screen " ) . empty ( ) )
set ( " show_splash_screen " , " 1 " ) ;
2021-07-29 11:15:17 +00:00
if ( get ( " show_hints " ) . empty ( ) )
set ( " show_hints " , " 1 " ) ;
2020-11-26 10:00:24 +00:00
# ifdef _WIN32
if ( get ( " use_legacy_3DConnexion " ) . empty ( ) )
set ( " use_legacy_3DConnexion " , " 0 " ) ;
# endif // _WIN32
2020-10-15 14:48:48 +00:00
2018-09-17 13:12:13 +00:00
// Remove legacy window positions/sizes
erase ( " " , " main_frame_maximized " ) ;
erase ( " " , " main_frame_pos " ) ;
erase ( " " , " main_frame_size " ) ;
erase ( " " , " object_settings_maximized " ) ;
erase ( " " , " object_settings_pos " ) ;
erase ( " " , " object_settings_size " ) ;
2017-10-30 17:41:50 +00:00
}
2021-06-25 14:44:06 +00:00
# ifdef WIN32
static std : : string appconfig_md5_hash_line ( const std : : string_view data )
{
//FIXME replace the two following includes with <boost/md5.hpp> after it becomes mainstream.
// return boost::md5(data).hex_str_value();
// boost::uuids::detail::md5 is an internal namespace thus it may change in the future.
// Also this implementation is not the fastest, it was designed for short blocks of text.
using boost : : uuids : : detail : : md5 ;
md5 md5_hash ;
// unsigned int[4], 128 bits
md5 : : digest_type md5_digest { } ;
std : : string md5_digest_str ;
md5_hash . process_bytes ( data . data ( ) , data . size ( ) ) ;
md5_hash . get_digest ( md5_digest ) ;
boost : : algorithm : : hex ( md5_digest , md5_digest + std : : size ( md5_digest ) , std : : back_inserter ( md5_digest_str ) ) ;
// MD5 hash is 32 HEX digits long.
assert ( md5_digest_str . size ( ) = = 32 ) ;
// This line will be emited at the end of the file.
return " # MD5 checksum " + md5_digest_str + " \n " ;
} ;
// Assume that the last line with the comment inside the config file contains a checksum and that the user didn't modify the config file.
static bool verify_config_file_checksum ( boost : : nowide : : ifstream & ifs )
{
auto read_whole_config_file = [ & ifs ] ( ) - > std : : string {
std : : stringstream ss ;
ss < < ifs . rdbuf ( ) ;
return ss . str ( ) ;
} ;
ifs . seekg ( 0 , boost : : nowide : : ifstream : : beg ) ;
std : : string whole_config = read_whole_config_file ( ) ;
// The checksum should be on the last line in the config file.
if ( size_t last_comment_pos = whole_config . find_last_of ( ' # ' ) ; last_comment_pos ! = std : : string : : npos ) {
// Split read config into two parts, one with checksum, and the second part is part with configuration from the checksum was computed.
// Verify existence and validity of the MD5 checksum line at the end of the file.
// When the checksum isn't found, the checksum was not saved correctly, it was removed or it is an older config file without the checksum.
// If the checksum is incorrect, then the file was either not saved correctly or modified.
if ( std : : string_view ( whole_config . c_str ( ) + last_comment_pos , whole_config . size ( ) - last_comment_pos ) = = appconfig_md5_hash_line ( { whole_config . data ( ) , last_comment_pos } ) )
return true ;
}
return false ;
}
# endif
2020-08-08 15:03:20 +00:00
std : : string AppConfig : : load ( )
2017-10-30 17:41:50 +00:00
{
// 1) Read the complete config file into a boost::property_tree.
namespace pt = boost : : property_tree ;
pt : : ptree tree ;
2021-06-25 14:44:06 +00:00
boost : : nowide : : ifstream ifs ;
bool recovered = false ;
2019-08-19 10:55:57 +00:00
try {
2021-06-25 14:44:06 +00:00
ifs . open ( AppConfig : : config_path ( ) ) ;
# ifdef WIN32
// Verify the checksum of the config file without taking just for debugging purpose.
if ( ! verify_config_file_checksum ( ifs ) )
BOOST_LOG_TRIVIAL ( info ) < < " The configuration file " < < AppConfig : : config_path ( ) < <
" has a wrong MD5 checksum or the checksum is missing. This may indicate a file corruption or a harmless user edit. " ;
ifs . seekg ( 0 , boost : : nowide : : ifstream : : beg ) ;
# endif
2019-08-19 10:55:57 +00:00
pt : : read_ini ( ifs , tree ) ;
} catch ( pt : : ptree_error & ex ) {
2021-06-25 14:44:06 +00:00
# ifdef WIN32
// The configuration file is corrupted, try replacing it with the backup configuration.
ifs . close ( ) ;
std : : string backup_path = ( boost : : format ( " %1%.bak " ) % AppConfig : : config_path ( ) ) . str ( ) ;
if ( boost : : filesystem : : exists ( backup_path ) ) {
// Compute checksum of the configuration backup file and try to load configuration from it when the checksum is correct.
boost : : nowide : : ifstream backup_ifs ( backup_path ) ;
if ( ! verify_config_file_checksum ( backup_ifs ) ) {
BOOST_LOG_TRIVIAL ( error ) < < format ( " Both \" %1% \" and \" %2% \" are corrupted. It isn't possible to restore configuration from the backup. " , AppConfig : : config_path ( ) , backup_path ) ;
backup_ifs . close ( ) ;
boost : : filesystem : : remove ( backup_path ) ;
} else if ( std : : string error_message ; copy_file ( backup_path , AppConfig : : config_path ( ) , error_message , false ) ! = SUCCESS ) {
BOOST_LOG_TRIVIAL ( error ) < < format ( " Configuration file \" %1% \" is corrupted. Failed to restore from backup \" %2% \" : %3% " , AppConfig : : config_path ( ) , backup_path , error_message ) ;
backup_ifs . close ( ) ;
boost : : filesystem : : remove ( backup_path ) ;
} else {
BOOST_LOG_TRIVIAL ( info ) < < format ( " Configuration file \" %1% \" was corrupted. It has been succesfully restored from the backup \" %2% \" . " , AppConfig : : config_path ( ) , backup_path ) ;
// Try parse configuration file after restore from backup.
try {
ifs . open ( AppConfig : : config_path ( ) ) ;
pt : : read_ini ( ifs , tree ) ;
recovered = true ;
} catch ( pt : : ptree_error & ex ) {
BOOST_LOG_TRIVIAL ( info ) < < format ( " Failed to parse configuration file \" %1% \" after it has been restored from backup: %2% " , AppConfig : : config_path ( ) , ex . what ( ) ) ;
}
}
} else
# endif // WIN32
BOOST_LOG_TRIVIAL ( info ) < < format ( " Failed to parse configuration file \" %1% \" : %2% " , AppConfig : : config_path ( ) , ex . what ( ) ) ;
if ( ! recovered ) {
// Report the initial error of parsing PrusaSlicer.ini.
// Error while parsing config file. We'll customize the error message and rethrow to be displayed.
// ! But to avoid the use of _utf8 (related to use of wxWidgets)
// we will rethrow this exception from the place of load() call, if returned value wouldn't be empty
/*
throw Slic3r : : RuntimeError (
_utf8 ( L ( " Error parsing PrusaSlicer config file, it is probably corrupted. "
" Try to manually delete the file to recover from the error. Your user profiles will not be affected. " ) ) +
" \n \n " + AppConfig : : config_path ( ) + " \n \n " + ex . what ( ) ) ;
*/
return ex . what ( ) ;
}
2019-08-19 10:55:57 +00:00
}
2017-10-30 17:41:50 +00:00
// 2) Parse the property_tree, extract the sections and key / value pairs.
for ( const auto & section : tree ) {
if ( section . second . empty ( ) ) {
// This may be a top level (no section) entry, or an empty section.
std : : string data = section . second . data ( ) ;
if ( ! data . empty ( ) )
// If there is a non-empty data, then it must be a top-level (without a section) config entry.
m_storage [ " " ] [ section . first ] = data ;
2018-03-28 09:36:36 +00:00
} else if ( boost : : starts_with ( section . first , VENDOR_PREFIX ) ) {
// This is a vendor section listing enabled model / variants
const auto vendor_name = section . first . substr ( VENDOR_PREFIX . size ( ) ) ;
auto & vendor = m_vendors [ vendor_name ] ;
for ( const auto & kvp : section . second ) {
if ( ! boost : : starts_with ( kvp . first , MODEL_PREFIX ) ) { continue ; }
const auto model_name = kvp . first . substr ( MODEL_PREFIX . size ( ) ) ;
std : : vector < std : : string > variants ;
if ( ! unescape_strings_cstyle ( kvp . second . data ( ) , variants ) ) { continue ; }
for ( const auto & variant : variants ) {
vendor [ model_name ] . insert ( variant ) ;
}
}
2017-10-30 17:41:50 +00:00
} else {
// This must be a section name. Read the entries of a section.
std : : map < std : : string , std : : string > & storage = m_storage [ section . first ] ;
for ( auto & kvp : section . second )
storage [ kvp . first ] = kvp . second . data ( ) ;
}
}
2018-04-16 14:52:11 +00:00
// Figure out if datadir has legacy presets
auto ini_ver = Semver : : parse ( get ( " version " ) ) ;
2018-04-19 16:29:19 +00:00
m_legacy_datadir = false ;
if ( ini_ver ) {
2018-05-17 14:19:40 +00:00
m_orig_version = * ini_ver ;
2018-04-19 16:29:19 +00:00
// Make 1.40.0 alphas compare well
ini_ver - > set_metadata ( boost : : none ) ;
ini_ver - > set_prerelease ( boost : : none ) ;
m_legacy_datadir = ini_ver < Semver ( 1 , 40 , 0 ) ;
}
2018-04-16 14:52:11 +00:00
2020-10-27 11:48:20 +00:00
// Legacy conversion
if ( m_mode = = EAppMode : : Editor ) {
// Convert [extras] "physical_printer" to [presets] "physical_printer",
// remove the [extras] section if it becomes empty.
if ( auto it_section = m_storage . find ( " extras " ) ; it_section ! = m_storage . end ( ) ) {
if ( auto it_physical_printer = it_section - > second . find ( " physical_printer " ) ; it_physical_printer ! = it_section - > second . end ( ) ) {
m_storage [ " presets " ] [ " physical_printer " ] = it_physical_printer - > second ;
it_section - > second . erase ( it_physical_printer ) ;
}
if ( it_section - > second . empty ( ) )
m_storage . erase ( it_section ) ;
}
}
2017-10-30 17:41:50 +00:00
// Override missing or keys with their defaults.
this - > set_defaults ( ) ;
m_dirty = false ;
2020-08-08 15:03:20 +00:00
return " " ;
2017-10-30 17:41:50 +00:00
}
void AppConfig : : save ( )
{
2020-10-26 07:09:03 +00:00
{
// Returns "undefined" if the thread naming functionality is not supported by the operating system.
std : : optional < std : : string > current_thread_name = get_current_thread_name ( ) ;
if ( current_thread_name & & * current_thread_name ! = " slic3r_main " )
throw CriticalException ( " Calling AppConfig::save() from a worker thread ! " ) ;
}
2020-10-22 12:45:15 +00:00
2018-12-11 15:33:43 +00:00
// The config is first written to a file with a PID suffix and then moved
// to avoid race conditions with multiple instances of Slic3r
const auto path = config_path ( ) ;
std : : string path_pid = ( boost : : format ( " %1%.%2% " ) % path % get_current_pid ( ) ) . str ( ) ;
2021-06-25 14:44:06 +00:00
std : : stringstream config_ss ;
2020-10-05 13:42:35 +00:00
if ( m_mode = = EAppMode : : Editor )
2021-06-25 14:44:06 +00:00
config_ss < < " # " < < Slic3r : : header_slic3r_generated ( ) < < std : : endl ;
2020-10-05 13:42:35 +00:00
else
2021-06-25 14:44:06 +00:00
config_ss < < " # " < < Slic3r : : header_gcodeviewer_generated ( ) < < std : : endl ;
2017-10-30 17:41:50 +00:00
// Make sure the "no" category is written first.
2021-01-29 15:16:23 +00:00
for ( const auto & kvp : m_storage [ " " ] )
2021-06-25 14:44:06 +00:00
config_ss < < kvp . first < < " = " < < kvp . second < < std : : endl ;
2017-10-30 17:41:50 +00:00
// Write the other categories.
2021-01-29 15:16:23 +00:00
for ( const auto & category : m_storage ) {
2017-10-30 17:41:50 +00:00
if ( category . first . empty ( ) )
continue ;
2021-06-25 14:44:06 +00:00
config_ss < < std : : endl < < " [ " < < category . first < < " ] " < < std : : endl ;
2021-01-29 15:16:23 +00:00
for ( const auto & kvp : category . second )
2021-06-25 14:44:06 +00:00
config_ss < < kvp . first < < " = " < < kvp . second < < std : : endl ;
2017-10-30 17:41:50 +00:00
}
2018-03-28 09:36:36 +00:00
// Write vendor sections
for ( const auto & vendor : m_vendors ) {
size_t size_sum = 0 ;
for ( const auto & model : vendor . second ) { size_sum + = model . second . size ( ) ; }
if ( size_sum = = 0 ) { continue ; }
2021-06-25 14:44:06 +00:00
config_ss < < std : : endl < < " [ " < < VENDOR_PREFIX < < vendor . first < < " ] " < < std : : endl ;
2018-03-28 09:36:36 +00:00
for ( const auto & model : vendor . second ) {
2021-06-25 14:44:06 +00:00
if ( model . second . empty ( ) ) { continue ; }
2018-03-28 09:36:36 +00:00
const std : : vector < std : : string > variants ( model . second . begin ( ) , model . second . end ( ) ) ;
const auto escaped = escape_strings_cstyle ( variants ) ;
2021-06-25 14:44:06 +00:00
config_ss < < MODEL_PREFIX < < model . first < < " = " < < escaped < < std : : endl ;
2018-03-28 09:36:36 +00:00
}
}
2021-06-25 14:44:06 +00:00
// One empty line before the MD5 sum.
config_ss < < std : : endl ;
std : : string config_str = config_ss . str ( ) ;
boost : : nowide : : ofstream c ;
c . open ( path_pid , std : : ios : : out | std : : ios : : trunc ) ;
c < < config_str ;
# ifdef WIN32
// WIN32 specific: The final "rename_file()" call is not safe in case of an application crash, there is no atomic "rename file" API
// provided by Windows (sic!). Therefore we save a MD5 checksum to be able to verify file corruption. In addition,
// we save the config file into a backup first before moving it to the final destination.
c < < appconfig_md5_hash_line ( config_str ) ;
# endif
2017-10-30 17:41:50 +00:00
c . close ( ) ;
2021-06-25 14:44:06 +00:00
# ifdef WIN32
// Make a backup of the configuration file before copying it to the final destination.
std : : string error_message ;
std : : string backup_path = ( boost : : format ( " %1%.bak " ) % path ) . str ( ) ;
// Copy configuration file with PID suffix into the configuration file with "bak" suffix.
if ( copy_file ( path_pid , backup_path , error_message , false ) ! = SUCCESS )
BOOST_LOG_TRIVIAL ( error ) < < " Copying from " < < path_pid < < " to " < < backup_path < < " failed. Failed to create a backup configuration. " ;
# endif
2018-12-11 15:33:43 +00:00
2021-06-25 14:44:06 +00:00
// Rename the config atomically.
// On Windows, the rename is likely NOT atomic, thus it may fail if PrusaSlicer crashes on another thread in the meanwhile.
// To cope with that, we already made a backup of the config on Windows.
2018-12-11 15:33:43 +00:00
rename_file ( path_pid , path ) ;
2017-10-30 17:41:50 +00:00
m_dirty = false ;
}
2018-03-28 09:36:36 +00:00
bool AppConfig : : get_variant ( const std : : string & vendor , const std : : string & model , const std : : string & variant ) const
{
const auto it_v = m_vendors . find ( vendor ) ;
if ( it_v = = m_vendors . end ( ) ) { return false ; }
const auto it_m = it_v - > second . find ( model ) ;
return it_m = = it_v - > second . end ( ) ? false : it_m - > second . find ( variant ) ! = it_m - > second . end ( ) ;
}
void AppConfig : : set_variant ( const std : : string & vendor , const std : : string & model , const std : : string & variant , bool enable )
{
if ( enable ) {
if ( get_variant ( vendor , model , variant ) ) { return ; }
m_vendors [ vendor ] [ model ] . insert ( variant ) ;
} else {
auto it_v = m_vendors . find ( vendor ) ;
if ( it_v = = m_vendors . end ( ) ) { return ; }
auto it_m = it_v - > second . find ( model ) ;
if ( it_m = = it_v - > second . end ( ) ) { return ; }
auto it_var = it_m - > second . find ( variant ) ;
if ( it_var = = it_m - > second . end ( ) ) { return ; }
it_m - > second . erase ( it_var ) ;
}
// If we got here, there was an update
m_dirty = true ;
2018-03-29 15:54:43 +00:00
}
void AppConfig : : set_vendors ( const AppConfig & from )
{
m_vendors = from . m_vendors ;
m_dirty = true ;
2018-03-28 09:36:36 +00:00
}
2017-10-30 17:41:50 +00:00
std : : string AppConfig : : get_last_dir ( ) const
{
const auto it = m_storage . find ( " recent " ) ;
if ( it ! = m_storage . end ( ) ) {
{
const auto it2 = it - > second . find ( " skein_directory " ) ;
if ( it2 ! = it - > second . end ( ) & & ! it2 - > second . empty ( ) )
return it2 - > second ;
}
{
const auto it2 = it - > second . find ( " config_directory " ) ;
if ( it2 ! = it - > second . end ( ) & & ! it2 - > second . empty ( ) )
return it2 - > second ;
}
}
return std : : string ( ) ;
}
2019-07-12 13:36:01 +00:00
std : : vector < std : : string > AppConfig : : get_recent_projects ( ) const
{
std : : vector < std : : string > ret ;
const auto it = m_storage . find ( " recent_projects " ) ;
if ( it ! = m_storage . end ( ) )
{
for ( const std : : map < std : : string , std : : string > : : value_type & item : it - > second )
{
ret . push_back ( item . second ) ;
}
}
return ret ;
}
void AppConfig : : set_recent_projects ( const std : : vector < std : : string > & recent_projects )
{
auto it = m_storage . find ( " recent_projects " ) ;
if ( it = = m_storage . end ( ) )
it = m_storage . insert ( std : : map < std : : string , std : : map < std : : string , std : : string > > : : value_type ( " recent_projects " , std : : map < std : : string , std : : string > ( ) ) ) . first ;
it - > second . clear ( ) ;
for ( unsigned int i = 0 ; i < ( unsigned int ) recent_projects . size ( ) ; + + i )
{
it - > second [ std : : to_string ( i + 1 ) ] = recent_projects [ i ] ;
}
}
2021-05-10 06:13:23 +00:00
void AppConfig : : set_mouse_device ( const std : : string & name , double translation_speed , double translation_deadzone ,
float rotation_speed , float rotation_deadzone , double zoom_speed , bool swap_yz )
2019-10-03 08:26:28 +00:00
{
std : : string key = std : : string ( " mouse_device: " ) + name ;
auto it = m_storage . find ( key ) ;
if ( it = = m_storage . end ( ) )
it = m_storage . insert ( std : : map < std : : string , std : : map < std : : string , std : : string > > : : value_type ( key , std : : map < std : : string , std : : string > ( ) ) ) . first ;
it - > second . clear ( ) ;
2021-05-10 06:13:23 +00:00
it - > second [ " translation_speed " ] = float_to_string_decimal_point ( translation_speed ) ;
it - > second [ " translation_deadzone " ] = float_to_string_decimal_point ( translation_deadzone ) ;
it - > second [ " rotation_speed " ] = float_to_string_decimal_point ( rotation_speed ) ;
it - > second [ " rotation_deadzone " ] = float_to_string_decimal_point ( rotation_deadzone ) ;
it - > second [ " zoom_speed " ] = float_to_string_decimal_point ( zoom_speed ) ;
2020-03-20 15:13:08 +00:00
it - > second [ " swap_yz " ] = swap_yz ? " 1 " : " 0 " ;
2019-10-03 08:26:28 +00:00
}
2020-03-04 10:36:36 +00:00
std : : vector < std : : string > AppConfig : : get_mouse_device_names ( ) const
2019-10-03 08:26:28 +00:00
{
2020-04-22 10:49:52 +00:00
static constexpr const char * prefix = " mouse_device: " ;
static const size_t prefix_len = strlen ( prefix ) ;
std : : vector < std : : string > out ;
2021-01-29 17:08:04 +00:00
for ( const auto & key_value_pair : m_storage )
2020-04-22 10:49:52 +00:00
if ( boost : : starts_with ( key_value_pair . first , prefix ) & & key_value_pair . first . size ( ) > prefix_len )
2020-03-04 10:36:36 +00:00
out . emplace_back ( key_value_pair . first . substr ( prefix_len ) ) ;
2020-04-22 10:49:52 +00:00
return out ;
2020-01-03 13:42:52 +00:00
}
2017-10-30 17:41:50 +00:00
void AppConfig : : update_config_dir ( const std : : string & dir )
{
this - > set ( " recent " , " config_directory " , dir ) ;
}
void AppConfig : : update_skein_dir ( const std : : string & dir )
{
2021-07-26 12:40:19 +00:00
if ( is_shapes_dir ( dir ) )
2021-07-21 14:13:40 +00:00
return ; // do not save "shapes gallery" directory
2017-10-30 17:41:50 +00:00
this - > set ( " recent " , " skein_directory " , dir ) ;
}
2020-01-02 15:30:28 +00:00
/*
2017-10-30 17:41:50 +00:00
std : : string AppConfig : : get_last_output_dir ( const std : : string & alt ) const
{
2019-11-27 12:30:45 +00:00
2017-10-30 17:41:50 +00:00
const auto it = m_storage . find ( " " ) ;
if ( it ! = m_storage . end ( ) ) {
const auto it2 = it - > second . find ( " last_output_path " ) ;
const auto it3 = it - > second . find ( " remember_output_path " ) ;
if ( it2 ! = it - > second . end ( ) & & it3 ! = it - > second . end ( ) & & ! it2 - > second . empty ( ) & & it3 - > second = = " 1 " )
return it2 - > second ;
}
return alt ;
}
void AppConfig : : update_last_output_dir ( const std : : string & dir )
{
this - > set ( " " , " last_output_path " , dir ) ;
}
2020-01-02 15:30:28 +00:00
*/
std : : string AppConfig : : get_last_output_dir ( const std : : string & alt , const bool removable ) const
{
std : : string s1 = ( removable ? " last_output_path_removable " : " last_output_path " ) ;
std : : string s2 = ( removable ? " remember_output_path_removable " : " remember_output_path " ) ;
const auto it = m_storage . find ( " " ) ;
if ( it ! = m_storage . end ( ) ) {
const auto it2 = it - > second . find ( s1 ) ;
const auto it3 = it - > second . find ( s2 ) ;
if ( it2 ! = it - > second . end ( ) & & it3 ! = it - > second . end ( ) & & ! it2 - > second . empty ( ) & & it3 - > second = = " 1 " )
return it2 - > second ;
}
2021-07-26 12:40:19 +00:00
return is_shapes_dir ( alt ) ? get_last_dir ( ) : alt ;
2020-01-02 15:30:28 +00:00
}
void AppConfig : : update_last_output_dir ( const std : : string & dir , const bool removable )
{
this - > set ( " " , ( removable ? " last_output_path_removable " : " last_output_path " ) , dir ) ;
}
2017-10-30 17:41:50 +00:00
2018-03-14 12:29:50 +00:00
void AppConfig : : reset_selections ( )
{
auto it = m_storage . find ( " presets " ) ;
if ( it ! = m_storage . end ( ) ) {
it - > second . erase ( " print " ) ;
it - > second . erase ( " filament " ) ;
2018-11-19 10:10:22 +00:00
it - > second . erase ( " sla_print " ) ;
2018-07-31 13:09:57 +00:00
it - > second . erase ( " sla_material " ) ;
2018-03-14 12:29:50 +00:00
it - > second . erase ( " printer " ) ;
2020-10-27 11:48:20 +00:00
it - > second . erase ( " physical_printer " ) ;
2018-03-14 12:29:50 +00:00
m_dirty = true ;
}
}
2017-10-30 17:41:50 +00:00
std : : string AppConfig : : config_path ( )
{
2020-10-05 13:42:35 +00:00
std : : string path = ( m_mode = = EAppMode : : Editor ) ?
( boost : : filesystem : : path ( Slic3r : : data_dir ( ) ) / ( SLIC3R_APP_KEY " .ini " ) ) . make_preferred ( ) . string ( ) :
( boost : : filesystem : : path ( Slic3r : : data_dir ( ) ) / ( GCODEVIEWER_APP_KEY " .ini " ) ) . make_preferred ( ) . string ( ) ;
return path ;
2017-10-30 17:41:50 +00:00
}
2018-04-25 15:43:01 +00:00
std : : string AppConfig : : version_check_url ( ) const
{
auto from_settings = get ( " version_check_url " ) ;
return from_settings . empty ( ) ? VERSION_CHECK_URL : from_settings ;
}
2017-10-30 17:41:50 +00:00
bool AppConfig : : exists ( )
{
2020-10-05 13:42:35 +00:00
return boost : : filesystem : : exists ( config_path ( ) ) ;
2017-10-30 17:41:50 +00:00
}
} ; // namespace Slic3r