From 79d9fd154eb051641e21a5cc4e8087b1137dbbe1 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Mon, 25 Oct 2021 17:40:18 +0200 Subject: [PATCH 1/5] Wrong build on windows system (32 vs 64bit) dialog --- src/slic3r/GUI/GUI_App.cpp | 23 +++++++++++++++++++++++ src/slic3r/GUI/NotificationManager.hpp | 2 +- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 49fbb8a313..0b7bf45bca 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -812,6 +812,29 @@ bool GUI_App::OnInit() bool GUI_App::on_init_inner() { +// win32 build on win64 and viceversa +#ifdef _WIN64 + if (wxPlatformInfo::Get().GetArchName().substr(0, 2) == "") { + wxRichMessageDialog dlg(nullptr, + _L("You have started PrusaSlicer for 64-bit architecture on 32-bit system." + "\nPlease download and install correct version at https://www.prusa3d.cz/prusaslicer/." + "\nDo you wish to continue?"), + "PrusaSlicer", wxICON_QUESTION | wxYES_NO); + if (dlg.ShowModal() != wxID_YES) + return false; + } +#elif _WIN32 + if (wxPlatformInfo::Get().GetArchName().substr(0, 2) == "64") { + wxRichMessageDialog dlg(nullptr, + _L("You have started PrusaSlicer for 32-bit architecture on 64-bit system." + "\nPlease download and install correct version at https://www.prusa3d.cz/prusaslicer/." + "\nDo you wish to continue?"), + "PrusaSlicer", wxICON_QUESTION | wxYES_NO); + if (dlg.ShowModal() != wxID_YES) + return false; + } +#endif // _WIN64 + // Forcing back menu icons under gtk2 and gtk3. Solution is based on: // https://docs.gtk.org/gtk3/class.Settings.html // see also https://docs.wxwidgets.org/3.0/classwx_menu_item.html#a2b5d6bcb820b992b1e4709facbf6d4fb diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index 9adcc240d0..655b17e634 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -112,7 +112,7 @@ enum class NotificationType // information about netfabb is finished repairing model (blocking proccess) NetfabbFinished, // Short meesage to fill space between start and finish of export - ExportOngoing + ExportOngoing, }; class NotificationManager From 8bf4408731339d0d56ee51dd86c44a67e1984a03 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Tue, 26 Oct 2021 14:03:58 +0200 Subject: [PATCH 2/5] Fix of #7170. Delayed notification is not moved until push will succeed. --- src/slic3r/GUI/NotificationManager.cpp | 8 ++++++-- src/slic3r/GUI/NotificationManager.hpp | 1 + src/slic3r/GUI/Plater.cpp | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index a984b6ba75..0af8e32f5e 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -2068,9 +2068,11 @@ bool NotificationManager::update_notifications(GLCanvas3D& canvas) if ((*it).remaining_time > 0) (*it).remaining_time -= time_since_render; if ((*it).remaining_time <= 0) { - if ((*it).condition_callback()) { // push notification, erase it from waiting list (frame is scheduled by push) + if ((*it).notification && (*it).condition_callback()) { // push notification, erase it from waiting list (frame is scheduled by push) (*it).notification->reset_timer(); - if (push_notification_data(std::move((*it).notification), 0)) { + // if activate_existing returns false, we expect push to return true. + if(!this->activate_existing((*it).notification.get()) || (*it).delay_interval == 0) { + push_notification_data(std::move((*it).notification), 0); it = m_waiting_notifications.erase(it); continue; } @@ -2107,11 +2109,13 @@ bool NotificationManager::activate_existing(const NotificationManager::PopNotifi const std::string &new_text = notification->get_data().text1; for (auto it = m_pop_notifications.begin(); it != m_pop_notifications.end(); ++it) { if ((*it)->get_type() == new_type && !(*it)->is_finished()) { + // multiple of one type allowed, but must have different text if (std::find(m_multiple_types.begin(), m_multiple_types.end(), new_type) != m_multiple_types.end()) { // If found same type and same text, return true - update will be performed on the old notif if ((*it)->compare_text(new_text) == false) { continue; } + // multiple of one type allowed, but must have different text nad ObjectID } else if (new_type == NotificationType::SlicingWarning) { auto w1 = dynamic_cast(notification); auto w2 = dynamic_cast(it->get()); diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index 655b17e634..6c50f64070 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -706,6 +706,7 @@ private: // Otherwise another delay interval waiting. Timestamp is 0. // Note that notification object is constructed when being added to the waiting list, but there are no updates called on it and its timer is reset at regular push. // Also note that no control of same notification is done during push_delayed_notification_data but if waiting notif fails to push, it continues waiting. + // If delay_interval is 0, notification is pushed only after initial_delay no matter the result. void push_delayed_notification_data(std::unique_ptr notification, std::function condition_callback, int64_t initial_delay, int64_t delay_interval); //finds older notification of same type and moves it to the end of queue. returns true if found bool activate_existing(const NotificationManager::PopNotification* notification); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index ec09ac1ad2..57c8a23283 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -4030,7 +4030,7 @@ void Plater::priv::on_export_began(wxCommandEvent& evt) { if (show_warning_dialog) warnings_dialog(); - notification_manager->push_delayed_notification(NotificationType::ExportOngoing, [](){return true;}, 1000, 1000); + notification_manager->push_delayed_notification(NotificationType::ExportOngoing, [](){return true;}, 1000, 0); } void Plater::priv::on_slicing_began() { From f23a0298ed280fc6f1547899a4586b6e19eb7437 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Tue, 26 Oct 2021 14:22:35 +0200 Subject: [PATCH 3/5] ExportOutgoing notification should not show on upload --- src/slic3r/GUI/Plater.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 57c8a23283..710a6ffd25 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3294,6 +3294,7 @@ void Plater::priv::export_gcode(fs::path output_path, bool output_path_on_remova show_warning_dialog = true; if (! output_path.empty()) { background_process.schedule_export(output_path.string(), output_path_on_removable_media); + notification_manager->push_delayed_notification(NotificationType::ExportOngoing, []() {return true; }, 1000, 0); } else { background_process.schedule_upload(std::move(upload_job)); } @@ -4030,7 +4031,6 @@ void Plater::priv::on_export_began(wxCommandEvent& evt) { if (show_warning_dialog) warnings_dialog(); - notification_manager->push_delayed_notification(NotificationType::ExportOngoing, [](){return true;}, 1000, 0); } void Plater::priv::on_slicing_began() { From 4c8e13947cf6a50b82f9ed0af2fd8c8a4f0596f5 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Tue, 26 Oct 2021 10:04:48 +0200 Subject: [PATCH 4/5] Hint hypertext preferences highlight --- resources/data/hints.ini | 7 +++++-- src/slic3r/GUI/HintNotification.cpp | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/resources/data/hints.ini b/resources/data/hints.ini index a79a8228a1..96d006d55a 100644 --- a/resources/data/hints.ini +++ b/resources/data/hints.ini @@ -34,8 +34,9 @@ # # Open preferences (might add item to highlight) # hypertext_type = preferences -# hypertext_preferences_page = 0 (values 0-2 according to prefernces tab to be opened) -# +# hypertext_preferences_page = 2 (values 0-2 according to prefernces tab to be opened) +# hypertext_preferences_item = show_collapse_button (name of variable saved in prusaslicer.ini connected to the setting in preferences) +# # Open gallery (no aditional var) # hypertext_type = gallery # @@ -97,6 +98,7 @@ documentation_link = https://help.prusa3d.com/en/article/reload-from-disk_120427 text = Hiding sidebar\nDid you know that you can hide the right sidebar using the shortcut Shift+Tab? You can also enable the icon for this from thePreferences. hypertext_type = preferences hypertext_preferences_page = 2 +hypertext_preferences_item = show_collapse_button [hint:Perspective camera] text = Perspective camera\nDid you know that you can use the K key to quickly switch between an orthographic and perspective camera? @@ -213,6 +215,7 @@ disabled_tags = SLA text = Settings in non-modal window\nDid you know that you can open the Settings in a new non-modal window? This means you can have settings open on one screen and the G-code Preview on the other. Go to thePreferencesand select Settings in non-modal window. hypertext_type = preferences hypertext_preferences_page = 2 +hypertext_preferences_item = dlg_settings_layout_mode [hint:Adaptive infills] text = Adaptive infills\nDid you know that you can use the Adaptive cubic and Support cubic infills to decrease the print time and lower the filament consumption? Read more in the documentation. diff --git a/src/slic3r/GUI/HintNotification.cpp b/src/slic3r/GUI/HintNotification.cpp index 21aecd15b1..450067c48a 100644 --- a/src/slic3r/GUI/HintNotification.cpp +++ b/src/slic3r/GUI/HintNotification.cpp @@ -413,9 +413,9 @@ void HintDatabase::load_hints_from_file(const boost::filesystem::path& path) // open preferences } else if(dict["hypertext_type"] == "preferences") { int page = static_cast(std::atoi(dict["hypertext_preferences_page"].c_str())); - HintData hint_data{ id_string, text1, weight, was_displayed, hypertext_text, follow_text, disabled_tags, enabled_tags, false, documentation_link, [page]() { wxGetApp().open_preferences(page); } }; + std::string item = dict["hypertext_preferences_item"]; + HintData hint_data{ id_string, text1, weight, was_displayed, hypertext_text, follow_text, disabled_tags, enabled_tags, false, documentation_link, [page, item]() { wxGetApp().open_preferences(page, item); } }; m_loaded_hints.emplace_back(hint_data); - } else if (dict["hypertext_type"] == "plater") { std::string item = dict["hypertext_plater_item"]; HintData hint_data{ id_string, text1, weight, was_displayed, hypertext_text, follow_text, disabled_tags, enabled_tags, true, documentation_link, [item]() { wxGetApp().plater()->canvas3D()->highlight_toolbar_item(item); } }; From ca8f7fbf805621d75579ece7b41db4c6e0150c2f Mon Sep 17 00:00:00 2001 From: David Kocik Date: Mon, 25 Oct 2021 14:19:22 +0200 Subject: [PATCH 5/5] Copying from other config folders: Find if there is more recent config in other folders (alpha / beta / release). If yes, ask user, make snapshot, copy files. if there is no current config, ask user and copy recent one. --- src/libslic3r/AppConfig.cpp | 20 ++--- src/libslic3r/AppConfig.hpp | 5 ++ src/libslic3r/PresetBundle.cpp | 55 ++++++++++++++ src/libslic3r/PresetBundle.hpp | 1 + src/slic3r/GUI/GUI_App.cpp | 133 +++++++++++++++++++++++++++++++++ src/slic3r/GUI/GUI_App.hpp | 6 ++ 6 files changed, 210 insertions(+), 10 deletions(-) diff --git a/src/libslic3r/AppConfig.cpp b/src/libslic3r/AppConfig.cpp index d5444edc63..e5af1fa731 100644 --- a/src/libslic3r/AppConfig.cpp +++ b/src/libslic3r/AppConfig.cpp @@ -248,11 +248,11 @@ std::string AppConfig::load() bool recovered = false; try { - ifs.open(AppConfig::config_path()); + ifs.open(AppConfig::loading_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() << + BOOST_LOG_TRIVIAL(info) << "The configuration file " << AppConfig::loading_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); @@ -262,32 +262,32 @@ std::string AppConfig::load() #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(); + std::string backup_path = (boost::format("%1%.bak") % AppConfig::loading_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); + BOOST_LOG_TRIVIAL(error) << format("Both \"%1%\" and \"%2%\" are corrupted. It isn't possible to restore configuration from the backup.", AppConfig::loading_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); + } else if (std::string error_message; copy_file(backup_path, AppConfig::loading_path(), error_message, false) != SUCCESS) { + BOOST_LOG_TRIVIAL(error) << format("Configuration file \"%1%\" is corrupted. Failed to restore from backup \"%2%\": %3%", AppConfig::loading_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); + BOOST_LOG_TRIVIAL(info) << format("Configuration file \"%1%\" was corrupted. It has been succesfully restored from the backup \"%2%\".", AppConfig::loading_path(), backup_path); // Try parse configuration file after restore from backup. try { - ifs.open(AppConfig::config_path()); + ifs.open(AppConfig::loading_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()); + BOOST_LOG_TRIVIAL(info) << format("Failed to parse configuration file \"%1%\" after it has been restored from backup: %2%", AppConfig::loading_path(), ex.what()); } } } else #endif // WIN32 - BOOST_LOG_TRIVIAL(info) << format("Failed to parse configuration file \"%1%\": %2%", AppConfig::config_path(), ex.what()); + BOOST_LOG_TRIVIAL(info) << format("Failed to parse configuration file \"%1%\": %2%", AppConfig::loading_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. diff --git a/src/libslic3r/AppConfig.hpp b/src/libslic3r/AppConfig.hpp index 03c35f35a3..5d9f32ab71 100644 --- a/src/libslic3r/AppConfig.hpp +++ b/src/libslic3r/AppConfig.hpp @@ -148,6 +148,9 @@ public: // Does the config file exist? bool exists(); + void set_loading_path(const std::string& path) { m_loading_path = path; } + std::string loading_path() { return (m_loading_path.empty() ? config_path() : m_loading_path); } + std::vector get_recent_projects() const; void set_recent_projects(const std::vector& recent_projects); @@ -196,6 +199,8 @@ private: Semver m_orig_version; // Whether the existing version is before system profiles & configuration updating bool m_legacy_datadir; + + std::string m_loading_path; }; } // namespace Slic3r diff --git a/src/libslic3r/PresetBundle.cpp b/src/libslic3r/PresetBundle.cpp index feaf0cac7d..56f01bb476 100644 --- a/src/libslic3r/PresetBundle.cpp +++ b/src/libslic3r/PresetBundle.cpp @@ -188,6 +188,61 @@ void PresetBundle::setup_directories() } } +// recursively copy all files and dirs in from_dir to to_dir +static void copy_dir(const boost::filesystem::path& from_dir, const boost::filesystem::path& to_dir) +{ + if(!boost::filesystem::is_directory(from_dir)) + return; + // i assume to_dir.parent surely exists + if (!boost::filesystem::is_directory(to_dir)) + boost::filesystem::create_directory(to_dir); + for (auto& dir_entry : boost::filesystem::directory_iterator(from_dir)) { + if (!boost::filesystem::is_directory(dir_entry.path())) { + std::string em; + CopyFileResult cfr = copy_file(dir_entry.path().string(), (to_dir / dir_entry.path().filename()).string(), em, false); + if (cfr != SUCCESS) { + BOOST_LOG_TRIVIAL(error) << "Error when copying files from " << from_dir << " to " << to_dir << ": " << em; + } + } else { + copy_dir(dir_entry.path(), to_dir / dir_entry.path().filename()); + } + } +} + +void PresetBundle::copy_files(const std::string& from) +{ + boost::filesystem::path data_dir = boost::filesystem::path(Slic3r::data_dir()); + // list of searched paths based on current directory system in setup_directories() + // do not copy cache and snapshots + boost::filesystem::path from_data_dir = boost::filesystem::path(from); + std::initializer_list from_dirs= { + from_data_dir / "vendor", + from_data_dir / "shapes", +#ifdef SLIC3R_PROFILE_USE_PRESETS_SUBDIR + // Store the print/filament/printer presets into a "presets" directory. + data_dir / "presets", + data_dir / "presets" / "print", + data_dir / "presets" / "filament", + data_dir / "presets" / "sla_print", + data_dir / "presets" / "sla_material", + data_dir / "presets" / "printer", + data_dir / "presets" / "physical_printer" +#else + // Store the print/filament/printer presets at the same location as the upstream Slic3r. + from_data_dir / "print", + from_data_dir / "filament", + from_data_dir / "sla_print", + from_data_dir / "sla_material", + from_data_dir / "printer", + from_data_dir / "physical_printer" +#endif + }; + // copy recursively all files + for (const boost::filesystem::path& from_dir : from_dirs) { + copy_dir(from_dir, data_dir / from_dir.filename()); + } +} + PresetsConfigSubstitutions PresetBundle::load_presets(AppConfig &config, ForwardCompatibilitySubstitutionRule substitution_rule, const PresetPreferences& preferred_selection/* = PresetPreferences()*/) { diff --git a/src/libslic3r/PresetBundle.hpp b/src/libslic3r/PresetBundle.hpp index e5e49fb470..a975e37fe3 100644 --- a/src/libslic3r/PresetBundle.hpp +++ b/src/libslic3r/PresetBundle.hpp @@ -24,6 +24,7 @@ public: void reset(bool delete_files); void setup_directories(); + void copy_files(const std::string& from); struct PresetPreferences { std::string printer_model_id;// name of a preferred printer model diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 0b7bf45bca..cd286e45ae 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -743,6 +743,25 @@ bool GUI_App::init_opengl() #endif } +// gets path to PrusaSlicer.ini, returns semver from first line comment +static boost::optional parse_semver_from_ini(std::string path) +{ + std::ifstream stream(path); + std::stringstream buffer; + buffer << stream.rdbuf(); + std::string body = buffer.str(); + size_t end_line = body.find_first_of("\n\r"); + body.resize(end_line); + size_t start = body.find("PrusaSlicer "); + if (start == std::string::npos) + return boost::none; + body = body.substr(start + 12); + size_t end = body.find_first_of(" \n\r"); + if (end < body.size()) + body.resize(end); + return Semver::parse(body); +} + void GUI_App::init_app_config() { // Profiles for the alpha are stored into the PrusaSlicer-alpha directory to not mix with the current release. @@ -791,9 +810,110 @@ void GUI_App::init_app_config() "\n\n" + app_config->config_path() + "\n\n" + error); } } + // Save orig_version here, so its empty if no app_config existed before this run. + m_last_config_version = app_config->orig_version();//parse_semver_from_ini(app_config->config_path()); } } +// returns true if found newer version and user agreed to use it +bool GUI_App::check_older_app_config(Semver current_version, bool backup) +{ + // find other version app config (alpha / beta / release) + std::string config_path = app_config->config_path(); + boost::filesystem::path parent_file_path(config_path); + std::string filename = parent_file_path.filename().string(); + parent_file_path.remove_filename().remove_filename(); + + std::vector candidates; + + if (SLIC3R_APP_KEY "-alpha" != GetAppName()) candidates.emplace_back(parent_file_path / SLIC3R_APP_KEY "-alpha" / filename); + if (SLIC3R_APP_KEY "-beta" != GetAppName()) candidates.emplace_back(parent_file_path / SLIC3R_APP_KEY "-beta" / filename); + if (SLIC3R_APP_KEY != GetAppName()) candidates.emplace_back(parent_file_path / SLIC3R_APP_KEY / filename); + + Semver last_semver = current_version; + for (const auto& candidate : candidates) { + if (boost::filesystem::exists(candidate)) { + // parse + boost::optionalother_semver = parse_semver_from_ini(candidate.string()); + if (other_semver && *other_semver > last_semver) { + last_semver = *other_semver; + m_older_data_dir_path = candidate.parent_path().string(); + } + } + } + if (m_older_data_dir_path.empty()) + return false; + BOOST_LOG_TRIVIAL(info) << "last app config file used: " << m_older_data_dir_path; + // ask about using older data folder + wxRichMessageDialog msg(nullptr, backup ? + wxString::Format(_L("PrusaSlicer detected another configuration folder at %s." + "\nIts version is %s." + "\nLast version you used in current configuration folder is %s." + "\nPlease note that PrusaSlicer uses different folders to save configuration of alpha, beta and full release versions." + "\nWould you like to copy found configuration to your current configuration folder?" + + "\n\nIf you select yes, PrusaSlicer will copy all profiles and other files from found folder to the current one. Overwriting any existing file with matching name." + "\nIf you select no, you will continue with current configuration.") + , m_older_data_dir_path, last_semver.to_string(), current_version.to_string()) + : wxString::Format(_L("PrusaSlicer detected another configuration folder at %s." + "\nIts version is %s." + "\nThere is no configuration file in current configuration folder." + "\nPlease note that PrusaSlicer uses different folders to save configuration of alpha, beta and full release versions." + "\nWould you like to copy found configuration to your current configuration folder?" + + "\n\nIf you select yes, PrusaSlicer will copy all profiles and other files from found folder to the current one." + "\nIf you select no, you will start with clean installation with configuration wizard.") + , m_older_data_dir_path, last_semver.to_string()) + , _L("PrusaSlicer"), wxICON_QUESTION | wxYES_NO); + if (msg.ShowModal() == wxID_YES) { + std::string snapshot_id; + if (backup) { + // configuration snapshot + std::string comment; + if (const Config::Snapshot* snapshot = Config::take_config_snapshot_report_error( + *app_config, + Config::Snapshot::SNAPSHOT_USER, + comment); + snapshot != nullptr) + // Is thos correct? Save snapshot id for later, when new app config is loaded. + snapshot_id = snapshot->id; + else + BOOST_LOG_TRIVIAL(error) << "Failed to take congiguration snapshot: "; + } + + // This will tell later (when config folder structure is sure to exists) to copy files from m_older_data_dir_path + m_init_app_config_from_older = true; + // load app config from older file + app_config->set_loading_path((boost::filesystem::path(m_older_data_dir_path) / filename).string()); + std::string error = app_config->load(); + if (!error.empty()) { + // Error while parsing config file. We'll customize the error message and rethrow to be displayed. + if (is_editor()) { + throw Slic3r::RuntimeError( + _u8L("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" + app_config->config_path() + "\n\n" + error); + } + else { + throw Slic3r::RuntimeError( + _u8L("Error parsing PrusaGCodeViewer config file, it is probably corrupted. " + "Try to manually delete the file to recover from the error.") + + "\n\n" + app_config->config_path() + "\n\n" + error); + } + } + if (!snapshot_id.empty()) + app_config->set("on_snapshot", snapshot_id); + m_app_conf_exists = true; + return true; + } + return false; +} + +void GUI_App::copy_older_config() +{ + preset_bundle->copy_files(m_older_data_dir_path); +} + void GUI_App::init_single_instance_checker(const std::string &name, const std::string &path) { BOOST_LOG_TRIVIAL(debug) << "init wx instance checker " << name << " "<< path; @@ -884,6 +1004,13 @@ bool GUI_App::on_init_inner() } } + if (m_last_config_version) { + if (*m_last_config_version < *Semver::parse(SLIC3R_VERSION)) + check_older_app_config(*m_last_config_version, true); + } else { + check_older_app_config(Semver(), false); + } + app_config->set("version", SLIC3R_VERSION); app_config->save(); @@ -922,12 +1049,18 @@ bool GUI_App::on_init_inner() scrn->SetText(_L("Loading configuration")+ dots); } + + preset_bundle = new PresetBundle(); // just checking for existence of Slic3r::data_dir is not enough : it may be an empty directory // supplied as argument to --datadir; in that case we should still run the wizard preset_bundle->setup_directories(); + + if (m_init_app_config_from_older) + copy_older_config(); + if (is_editor()) { #ifdef __WXMSW__ if (app_config->get("associate_3mf") == "1") diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index cbabd16e7d..90a8776a67 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -337,6 +337,8 @@ public: private: bool on_init_inner(); void init_app_config(); + bool check_older_app_config(Semver current_version, bool backup); + void copy_older_config(); void window_pos_save(wxTopLevelWindow* window, const std::string &name); void window_pos_restore(wxTopLevelWindow* window, const std::string &name, bool default_maximized = false); void window_pos_sanitize(wxTopLevelWindow* window); @@ -344,6 +346,10 @@ private: bool config_wizard_startup(); void check_updates(const bool verbose); + + bool m_init_app_config_from_older { false }; + std::string m_older_data_dir_path; + boost::optional m_last_config_version; }; DECLARE_APP(GUI_App)