mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-30 20:21:12 -06:00 
			
		
		
		
	Check unsaved changes (#6991)
* Check Unsaved changes (partially related to #5903) + Allow create new project when Plater is empty, but some of presets are modified (related to #5903) + When creating new project allow Keep or Discard modification from previous project + Added check of changes: * before any load project (including DnD and "Load From Recent Projects") * before preset updater * when configuration is changing from the ConfigWizard + Dialog caption is added for each check + Create/Destroy ConfigWizard every time when it's called * Check Unsaved changes: Next Improvements + For dialog "Save project changes" added a reason of saving and name of the current project (or "Untitled") + UnsavedChangesDialog: Headers are extended to better explain the reason + Preferences: Fixed tooltiops for "Always ask for unsaved changes when..." + Suppress "Remember my choice" checkbox for actions which are not frequently used * Fixed behavior of the application when try to save changed project but "Cancel" button is selected in "Save file as..." dialog * Check unsaved changes: Improvements for Config Wizard - Check all cases when presets should be updated + Fixed info line for Materials pages. Text of the info relates to the printer technology now * Improved suggested name for a project when Application is closing * Fixed Linux/OSX build warnings
This commit is contained in:
		
							parent
							
								
									846b868215
								
							
						
					
					
						commit
						8f064dd155
					
				
					 17 changed files with 462 additions and 178 deletions
				
			
		|  | @ -139,6 +139,9 @@ void AppConfig::set_defaults() | |||
|         if (get("default_action_on_select_preset").empty()) | ||||
|             set("default_action_on_select_preset", "none");     // , "transfer", "discard" or "save" 
 | ||||
| 
 | ||||
|         if (get("default_action_on_new_project").empty()) | ||||
|             set("default_action_on_new_project", "none");       // , "keep(transfer)", "discard" or "save" 
 | ||||
| 
 | ||||
|         if (get("color_mapinulation_panel").empty()) | ||||
|             set("color_mapinulation_panel", "0"); | ||||
| 
 | ||||
|  |  | |||
|  | @ -41,6 +41,7 @@ | |||
| #include "format.hpp" | ||||
| #include "MsgDialog.hpp" | ||||
| #include "libslic3r/libslic3r.h" | ||||
| #include "UnsavedChangesDialog.hpp" | ||||
| 
 | ||||
| #if defined(__linux__) && defined(__WXGTK3__) | ||||
| #define wxLinux_gtk3 true | ||||
|  | @ -741,10 +742,10 @@ void PageMaterials::set_compatible_printers_html_window(const std::vector<std::s | |||
|     const auto bgr_clr_str = wxString::Format(wxT("#%02X%02X%02X"), bgr_clr.Red(), bgr_clr.Green(), bgr_clr.Blue()); | ||||
|     const auto text_clr = wxGetApp().get_label_clr_default();//wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
 | ||||
|     const auto text_clr_str = wxString::Format(wxT("#%02X%02X%02X"), text_clr.Red(), text_clr.Green(), text_clr.Blue()); | ||||
|     wxString first_line = _L("Filaments marked with <b>*</b> are <b>not</b> compatible with some installed printers."); | ||||
|     wxString first_line = format_wxstr(_L("%1% marked with <b>*</b> are <b>not</b> compatible with some installed printers."), materials->technology == T_FFF ? _L("Filaments") : _L("SLA materials")); | ||||
|     wxString text; | ||||
|     if (all_printers) { | ||||
|         wxString second_line = _L("All installed printers are compatible with the selected filament."); | ||||
|         wxString second_line = format_wxstr(_L("All installed printers are compatible with the selected %1%."), materials->technology == T_FFF ? _L("filament") : _L("SLA material")); | ||||
|         text = wxString::Format( | ||||
|             "<html>" | ||||
|             "<style>" | ||||
|  | @ -764,7 +765,7 @@ void PageMaterials::set_compatible_printers_html_window(const std::vector<std::s | |||
|             , second_line | ||||
|             ); | ||||
|     } else { | ||||
|         wxString second_line = _L("Only the following installed printers are compatible with the selected filament:"); | ||||
|         wxString second_line = printer_names.empty() ? "" : format_wxstr(_L("Only the following installed printers are compatible with the selected %1%:"), materials->technology == T_FFF ? _L("filament") : _L("SLA material")); | ||||
|         text = wxString::Format( | ||||
|             "<html>" | ||||
|             "<style>" | ||||
|  | @ -2473,8 +2474,16 @@ static std::string get_first_added_preset(const std::map<std::string, std::strin | |||
|     return *diff.begin(); | ||||
| } | ||||
| 
 | ||||
| bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater) | ||||
| bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater, bool& apply_keeped_changes) | ||||
| { | ||||
|     wxString header, caption = _L("Configuration is editing from ConfigWizard"); | ||||
|     bool check_unsaved_preset_changes = page_welcome->reset_user_profile(); | ||||
|     if (check_unsaved_preset_changes) | ||||
|         header = _L("All user presets will be deleted."); | ||||
|     int act_btns = UnsavedChangesDialog::ActionButtons::KEEP; | ||||
|     if (!check_unsaved_preset_changes) | ||||
|         act_btns |= UnsavedChangesDialog::ActionButtons::SAVE; | ||||
| 
 | ||||
|     const auto enabled_vendors = appconfig_new.vendors(); | ||||
| 
 | ||||
|     // Install bundles from resources if needed:
 | ||||
|  | @ -2500,6 +2509,9 @@ bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese | |||
|             install_bundles.emplace_back(pair.first); | ||||
|         } | ||||
|     } | ||||
|     if (!check_unsaved_preset_changes) | ||||
|         if ((check_unsaved_preset_changes = install_bundles.size() > 0)) | ||||
|             header = _L_PLURAL("New vendor was installed and one of its printer will be activated", "New vendors were installed and one of theirs printer will be activated", install_bundles.size()); | ||||
| 
 | ||||
| #ifdef __linux__ | ||||
|     // Desktop integration on Linux
 | ||||
|  | @ -2531,6 +2543,10 @@ bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese | |||
|     if (snapshot && ! take_config_snapshot_cancel_on_error(*app_config, snapshot_reason, "", _u8L("Continue with applying configuration changes?"))) | ||||
|         return false; | ||||
| 
 | ||||
|     if (check_unsaved_preset_changes && | ||||
|         !wxGetApp().check_and_keep_current_preset_changes(caption, header, act_btns, &apply_keeped_changes)) | ||||
|         return false; | ||||
| 
 | ||||
|     if (install_bundles.size() > 0) { | ||||
|         // Install bundles from resources.
 | ||||
|         // Don't create snapshot - we've already done that above if applicable.
 | ||||
|  | @ -2586,17 +2602,52 @@ bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // if unsaved changes was not cheched till this moment
 | ||||
|     if (!check_unsaved_preset_changes) { | ||||
|         if ((check_unsaved_preset_changes = !preferred_model.empty())) { | ||||
|             header = _L("A new Printer was installed and it will be activated."); | ||||
|             if (!wxGetApp().check_and_keep_current_preset_changes(caption, header, act_btns, &apply_keeped_changes)) | ||||
|                 return false; | ||||
|         } | ||||
|         else if ((check_unsaved_preset_changes = enabled_vendors_old != enabled_vendors)) { | ||||
|             header = _L("Some Printers were uninstalled."); | ||||
|             if (!wxGetApp().check_and_keep_current_preset_changes(caption, header, act_btns, &apply_keeped_changes)) | ||||
|                 return false; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     std::string first_added_filament, first_added_sla_material; | ||||
|     auto apply_section = [this, app_config](const std::string& section_name, std::string& first_added_preset) { | ||||
|     auto get_first_added_material_preset = [this, app_config](const std::string& section_name, std::string& first_added_preset) { | ||||
|         if (appconfig_new.has_section(section_name)) { | ||||
|             // get first of new added preset names
 | ||||
|             const std::map<std::string, std::string>& old_presets = app_config->has_section(section_name) ? app_config->get_section(section_name) : std::map<std::string, std::string>(); | ||||
|             first_added_preset = get_first_added_preset(old_presets, appconfig_new.get_section(section_name)); | ||||
|             app_config->set_section(section_name, appconfig_new.get_section(section_name)); | ||||
|         } | ||||
|     }; | ||||
|     apply_section(AppConfig::SECTION_FILAMENTS, first_added_filament); | ||||
|     apply_section(AppConfig::SECTION_MATERIALS, first_added_sla_material); | ||||
|     get_first_added_material_preset(AppConfig::SECTION_FILAMENTS, first_added_filament); | ||||
|     get_first_added_material_preset(AppConfig::SECTION_MATERIALS, first_added_sla_material); | ||||
| 
 | ||||
|     // if unsaved changes was not cheched till this moment
 | ||||
|     if (!check_unsaved_preset_changes) { | ||||
|         if ((check_unsaved_preset_changes = !first_added_filament.empty() || !first_added_sla_material.empty())) { | ||||
|             header = format_wxstr(_L("A new %1% was installed and it will be activated."), !first_added_filament.empty() ? _L("Filament") : _L("SLA material")); | ||||
|             if (!wxGetApp().check_and_keep_current_preset_changes(caption, header, act_btns, &apply_keeped_changes)) | ||||
|                 return false; | ||||
|         } | ||||
|         else { | ||||
|             bool is_filaments_changed = app_config->get_section(AppConfig::SECTION_FILAMENTS) != appconfig_new.get_section(AppConfig::SECTION_FILAMENTS); | ||||
|             bool is_sla_materials_changed = app_config->get_section(AppConfig::SECTION_MATERIALS) != appconfig_new.get_section(AppConfig::SECTION_MATERIALS); | ||||
|             if ((check_unsaved_preset_changes = is_filaments_changed || is_sla_materials_changed)) { | ||||
|                 header = format_wxstr(_L("Some %1% were uninstalled."), is_filaments_changed ? _L("Filaments") : _L("SLA materials")); | ||||
|                 if (!wxGetApp().check_and_keep_current_preset_changes(caption, header, act_btns, &apply_keeped_changes)) | ||||
|                     return false; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // apply materials in app_config
 | ||||
|     for (const std::string& section_name : {AppConfig::SECTION_FILAMENTS, AppConfig::SECTION_MATERIALS}) | ||||
|         app_config->set_section(section_name, appconfig_new.get_section(section_name)); | ||||
| 
 | ||||
|     app_config->set_vendors(appconfig_new); | ||||
| 
 | ||||
|  | @ -2624,10 +2675,16 @@ bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese | |||
| 
 | ||||
|     page_mode->serialize_mode(app_config); | ||||
| 
 | ||||
|     preset_bundle->load_presets(*app_config, ForwardCompatibilitySubstitutionRule::EnableSilentDisableSystem,  | ||||
|                                 {preferred_model, preferred_variant, first_added_filament, first_added_sla_material}); | ||||
|     if (check_unsaved_preset_changes) | ||||
|         preset_bundle->load_presets(*app_config, ForwardCompatibilitySubstitutionRule::EnableSilentDisableSystem,  | ||||
|                                     {preferred_model, preferred_variant, first_added_filament, first_added_sla_material}); | ||||
| 
 | ||||
|     if (page_custom->custom_wanted()) { | ||||
|         // if unsaved changes was not cheched till this moment
 | ||||
|         if (!check_unsaved_preset_changes &&  | ||||
|             !wxGetApp().check_and_keep_current_preset_changes(caption, _L("Custom printer was installed and it will be activated."), act_btns, &apply_keeped_changes)) | ||||
|             return false; | ||||
| 
 | ||||
|         page_firmware->apply_custom_config(*custom_config); | ||||
|         page_bed->apply_custom_config(*custom_config); | ||||
|         page_diams->apply_custom_config(*custom_config); | ||||
|  | @ -2851,8 +2908,13 @@ bool ConfigWizard::run(RunReason reason, StartPage start_page) | |||
|     p->set_start_page(start_page); | ||||
| 
 | ||||
|     if (ShowModal() == wxID_OK) { | ||||
|         if (! p->apply_config(app.app_config, app.preset_bundle, app.preset_updater)) | ||||
|         bool apply_keeped_changes = false; | ||||
|         if (! p->apply_config(app.app_config, app.preset_bundle, app.preset_updater, apply_keeped_changes)) | ||||
|             return false; | ||||
| 
 | ||||
|         if (apply_keeped_changes) | ||||
|             app.apply_keeped_preset_modifications(); | ||||
| 
 | ||||
|         app.app_config->set_legacy_datadir(false); | ||||
|         app.update_mode(); | ||||
|         app.obj_manipul()->update_ui_from_settings(); | ||||
|  |  | |||
|  | @ -611,7 +611,7 @@ struct ConfigWizard::priv | |||
| 
 | ||||
|     bool on_bnt_finish(); | ||||
|     bool check_and_install_missing_materials(Technology technology, const std::string &only_for_model_id = std::string()); | ||||
|     bool apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater); | ||||
|     bool apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater, bool& apply_keeped_changes); | ||||
|     // #ys_FIXME_alise
 | ||||
|     void update_presets_in_config(const std::string& section, const std::string& alias_key, bool add); | ||||
| #ifdef __linux__ | ||||
|  |  | |||
|  | @ -323,15 +323,19 @@ wxWindow* BitmapChoiceRenderer::CreateEditorCtrl(wxWindow* parent, wxRect labelR | |||
| 
 | ||||
|     c_editor->SetSelection(atoi(data.GetText().c_str())); | ||||
| 
 | ||||
|     // to avoid event propagation to other sidebar items
 | ||||
|     c_editor->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent& evt) { | ||||
|             evt.StopPropagation(); | ||||
|      | ||||
| #ifdef __linux__ | ||||
|             // FinishEditing grabs new selection and triggers config update. We better call
 | ||||
|             // it explicitly, automatic update on KILL_FOCUS didn't work on Linux.
 | ||||
|             this->FinishEditing(); | ||||
| #endif | ||||
|     c_editor->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent& evt) { | ||||
|         // to avoid event propagation to other sidebar items
 | ||||
|         evt.StopPropagation(); | ||||
|         // FinishEditing grabs new selection and triggers config update. We better call
 | ||||
|         // it explicitly, automatic update on KILL_FOCUS didn't work on Linux.
 | ||||
|         this->FinishEditing(); | ||||
|     }); | ||||
| #else | ||||
|     // to avoid event propagation to other sidebar items
 | ||||
|     c_editor->Bind(wxEVT_COMBOBOX, [](wxCommandEvent& evt) { evt.StopPropagation(); }); | ||||
| #endif | ||||
| 
 | ||||
|     return c_editor; | ||||
| } | ||||
|  |  | |||
|  | @ -694,7 +694,6 @@ GUI_App::GUI_App(EAppMode mode) | |||
|     , m_app_mode(mode) | ||||
|     , m_em_unit(10) | ||||
|     , m_imgui(new ImGuiWrapper()) | ||||
|     , m_wizard(nullptr) | ||||
| 	, m_removable_drive_manager(std::make_unique<RemovableDriveManager>()) | ||||
| 	, m_other_instance_message_handler(std::make_unique<OtherInstanceMessageHandler>()) | ||||
| { | ||||
|  | @ -1333,8 +1332,6 @@ void GUI_App::recreate_GUI(const wxString& msg_name) | |||
| 
 | ||||
|     dlg.Update(30, _L("Recreating") + dots); | ||||
|     old_main_frame->Destroy(); | ||||
|     // For this moment ConfigWizard is deleted, invalidate it.
 | ||||
|     m_wizard = nullptr; | ||||
| 
 | ||||
|     dlg.Update(80, _L("Loading of current presets") + dots); | ||||
|     m_printhost_job_queue.reset(new PrintHostJobQueue(mainframe->printhost_queue_dlg())); | ||||
|  | @ -1409,8 +1406,6 @@ void GUI_App::update_ui_from_settings() | |||
|         m_force_colors_update = false; | ||||
|         mainframe->force_color_changed(); | ||||
|         mainframe->diff_dialog.force_color_changed(); | ||||
|         if (m_wizard) | ||||
|             m_wizard->force_color_changed(); | ||||
|     } | ||||
| #endif | ||||
|     mainframe->update_ui_from_settings(); | ||||
|  | @ -1871,8 +1866,9 @@ void GUI_App::add_config_menu(wxMenuBar *menu) | |||
| #endif | ||||
|         case ConfigMenuTakeSnapshot: | ||||
|             // Take a configuration snapshot.
 | ||||
|             if (check_and_save_current_preset_changes()) { | ||||
|                 wxTextEntryDialog dlg(nullptr, _L("Taking configuration snapshot"), _L("Snapshot name")); | ||||
|             if (wxString action_name = _L("Taking a configuration snapshot"); | ||||
|                 check_and_save_current_preset_changes(action_name, _L("Some presets are modified and the unsaved changes will not be captured by the configuration snapshot."), false, true)) { | ||||
|                 wxTextEntryDialog dlg(nullptr, action_name, _L("Snapshot name")); | ||||
|                 UpdateDlgDarkUI(&dlg); | ||||
|                  | ||||
|                 // set current normal font for dialog children, 
 | ||||
|  | @ -1888,7 +1884,7 @@ void GUI_App::add_config_menu(wxMenuBar *menu) | |||
|             } | ||||
|             break; | ||||
|         case ConfigMenuSnapshots: | ||||
|             if (check_and_save_current_preset_changes()) { | ||||
|             if (check_and_save_current_preset_changes(_L("Loading a configuration snapshot"), "", false)) { | ||||
|                 std::string on_snapshot; | ||||
|                 if (Config::SnapshotDB::singleton().is_on_snapshot(*app_config)) | ||||
|                     on_snapshot = app_config->get("on_snapshot"); | ||||
|  | @ -1910,9 +1906,6 @@ void GUI_App::add_config_menu(wxMenuBar *menu) | |||
| 
 | ||||
|                         // Load the currently selected preset into the GUI, update the preset selection box.
 | ||||
|                         load_current_presets(); | ||||
| 
 | ||||
|                         // update config wizard in respect to the new config
 | ||||
|                         update_wizard_from_config(); | ||||
|                     } catch (std::exception &ex) { | ||||
|                         GUI::show_error(nullptr, _L("Failed to activate configuration snapshot.") + "\n" + into_u8(ex.what())); | ||||
|                     } | ||||
|  | @ -2081,13 +2074,28 @@ std::vector<std::pair<unsigned int, std::string>> GUI_App::get_selected_presets( | |||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| // This is called when closing the application, when loading a config file or when starting the config wizard
 | ||||
| // to notify the user whether he is aware that some preset changes will be lost.
 | ||||
| bool GUI_App::check_and_save_current_preset_changes(const wxString& header, const wxString& caption) | ||||
| // To notify the user whether he is aware that some preset changes will be lost,
 | ||||
| // UnsavedChangesDialog: "Discard / Save / Cancel"
 | ||||
| // This is called when:
 | ||||
| // - Close Application & Current project isn't saved
 | ||||
| // - Load Project      & Current project isn't saved
 | ||||
| // - Undo / Redo with change of print technologie
 | ||||
| // - Loading snapshot
 | ||||
| // - Loading config_file/bundle
 | ||||
| // UnsavedChangesDialog: "Don't save / Save / Cancel"
 | ||||
| // This is called when:
 | ||||
| // - Exporting config_bundle
 | ||||
| // - Taking snapshot
 | ||||
| bool GUI_App::check_and_save_current_preset_changes(const wxString& caption, const wxString& header, bool remember_choice/* = true*/, bool dont_save_insted_of_discard/* = false*/) | ||||
| { | ||||
|     if (/*this->plater()->model().objects.empty() && */has_current_preset_changes()) { | ||||
|         UnsavedChangesDialog dlg(header, caption); | ||||
|         if (wxGetApp().app_config->get("default_action_on_close_application") == "none" && dlg.ShowModal() == wxID_CANCEL) | ||||
|     if (has_current_preset_changes()) { | ||||
|         const std::string app_config_key = remember_choice ? "default_action_on_close_application" : ""; | ||||
|         int act_buttons = UnsavedChangesDialog::ActionButtons::SAVE; | ||||
|         if (dont_save_insted_of_discard) | ||||
|             act_buttons |= UnsavedChangesDialog::ActionButtons::DONT_SAVE; | ||||
|         UnsavedChangesDialog dlg(caption, header, app_config_key, act_buttons); | ||||
|         std::string act = app_config_key.empty() ? "none" : wxGetApp().app_config->get(app_config_key); | ||||
|         if (act == "none" && dlg.ShowModal() == wxID_CANCEL) | ||||
|             return false; | ||||
| 
 | ||||
|         if (dlg.save_preset())  // save selected changes
 | ||||
|  | @ -2095,18 +2103,122 @@ bool GUI_App::check_and_save_current_preset_changes(const wxString& header, cons | |||
|             for (const std::pair<std::string, Preset::Type>& nt : dlg.get_names_and_types()) | ||||
|                 preset_bundle->save_changes_for_preset(nt.first, nt.second, dlg.get_unselected_options(nt.second)); | ||||
| 
 | ||||
|             load_current_presets(false); | ||||
| 
 | ||||
|             // if we saved changes to the new presets, we should to 
 | ||||
|             // synchronize config.ini with the current selections.
 | ||||
|             preset_bundle->export_selections(*app_config); | ||||
| 
 | ||||
|             wxMessageBox(_L_PLURAL("The preset modifications are successfully saved",  | ||||
|                                    "The presets modifications are successfully saved", dlg.get_names_and_types().size())); | ||||
|             MessageDialog(nullptr, _L_PLURAL("The preset modifications are successfully saved",  | ||||
|                                              "The presets modifications are successfully saved", dlg.get_names_and_types().size())).ShowModal(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| void GUI_App::apply_keeped_preset_modifications() | ||||
| { | ||||
|     PrinterTechnology printer_technology = preset_bundle->printers.get_edited_preset().printer_technology(); | ||||
|     for (Tab* tab : tabs_list) { | ||||
|         if (tab->supports_printer_technology(printer_technology)) | ||||
|             tab->apply_config_from_cache(); | ||||
|     } | ||||
|     load_current_presets(false); | ||||
| } | ||||
| 
 | ||||
| // This is called when creating new project or load another project
 | ||||
| // OR close ConfigWizard
 | ||||
| // to ask the user what should we do with unsaved changes for presets.
 | ||||
| // New Project          => Current project is saved    => UnsavedChangesDialog: "Keep / Discard / Cancel"
 | ||||
| //                      => Current project isn't saved => UnsavedChangesDialog: "Keep / Discard / Save / Cancel"
 | ||||
| // Close ConfigWizard   => Current project is saved    => UnsavedChangesDialog: "Keep / Discard / Save / Cancel"
 | ||||
| // Note: no_nullptr postponed_apply_of_keeped_changes indicates that thie function is called after ConfigWizard is closed
 | ||||
| bool GUI_App::check_and_keep_current_preset_changes(const wxString& caption, const wxString& header, int action_buttons, bool* postponed_apply_of_keeped_changes/* = nullptr*/) | ||||
| { | ||||
|     if (has_current_preset_changes()) { | ||||
|         bool is_called_from_configwizard = postponed_apply_of_keeped_changes != nullptr; | ||||
| 
 | ||||
|         const std::string app_config_key = is_called_from_configwizard ? "" : "default_action_on_new_project"; | ||||
|         UnsavedChangesDialog dlg(caption, header, app_config_key, action_buttons); | ||||
|         std::string act = app_config_key.empty() ? "none" : wxGetApp().app_config->get(app_config_key); | ||||
|         if (act == "none" && dlg.ShowModal() == wxID_CANCEL) | ||||
|             return false; | ||||
| 
 | ||||
|         auto reset_modifications = [this, is_called_from_configwizard]() { | ||||
|             if (is_called_from_configwizard) | ||||
|                 return; // no need to discared changes. It will be done fromConfigWizard closing
 | ||||
| 
 | ||||
|             PrinterTechnology printer_technology = preset_bundle->printers.get_edited_preset().printer_technology(); | ||||
|             for (const Tab* const tab : tabs_list) { | ||||
|                 if (tab->supports_printer_technology(printer_technology) && tab->current_preset_is_dirty()) | ||||
|                     tab->m_presets->discard_current_changes(); | ||||
|             } | ||||
|             load_current_presets(false); | ||||
|         }; | ||||
| 
 | ||||
|         if (dlg.discard()) | ||||
|             reset_modifications(); | ||||
|         else  // save selected changes
 | ||||
|         { | ||||
|             const auto& preset_names_and_types = dlg.get_names_and_types(); | ||||
|             if (dlg.save_preset()) { | ||||
|                 for (const std::pair<std::string, Preset::Type>& nt : preset_names_and_types) | ||||
|                     preset_bundle->save_changes_for_preset(nt.first, nt.second, dlg.get_unselected_options(nt.second)); | ||||
| 
 | ||||
|                 // if we saved changes to the new presets, we should to 
 | ||||
|                 // synchronize config.ini with the current selections.
 | ||||
|                 preset_bundle->export_selections(*app_config); | ||||
| 
 | ||||
|                 wxString text = _L_PLURAL("The preset modifications are successfully saved", | ||||
|                     "The presets modifications are successfully saved", preset_names_and_types.size()); | ||||
|                 if (!is_called_from_configwizard) | ||||
|                     text += "\n\n" + _L("For new project all modifications will be reseted"); | ||||
| 
 | ||||
|                 MessageDialog(nullptr, text).ShowModal(); | ||||
|                 reset_modifications(); | ||||
|             } | ||||
|             else if (dlg.transfer_changes() && (dlg.has_unselected_options() || is_called_from_configwizard)) { | ||||
|                 // execute this part of code only if not all modifications are keeping to the new project 
 | ||||
|                 // OR this function is called when ConfigWizard is closed and "Keep modifications" is selected
 | ||||
|                 for (const std::pair<std::string, Preset::Type>& nt : preset_names_and_types) { | ||||
|                     Preset::Type type = nt.second; | ||||
|                     Tab* tab = get_tab(type); | ||||
|                     std::vector<std::string> selected_options = dlg.get_selected_options(type); | ||||
|                     if (type == Preset::TYPE_PRINTER) { | ||||
|                         auto it = std::find(selected_options.begin(), selected_options.end(), "extruders_count"); | ||||
|                         if (it != selected_options.end()) { | ||||
|                             // erase "extruders_count" option from the list
 | ||||
|                             selected_options.erase(it); | ||||
|                             // cache the extruders count
 | ||||
|                             static_cast<TabPrinter*>(tab)->cache_extruder_cnt(); | ||||
|                         } | ||||
|                     } | ||||
|                     tab->cache_config_diff(selected_options); | ||||
|                     if (!is_called_from_configwizard) | ||||
|                         tab->m_presets->discard_current_changes(); | ||||
|                 } | ||||
|                 if (is_called_from_configwizard) | ||||
|                     *postponed_apply_of_keeped_changes = true; | ||||
|                 else | ||||
|                     apply_keeped_preset_modifications(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| bool GUI_App::can_load_project() | ||||
| { | ||||
|     int saved_project = plater()->save_project_if_dirty(_L("Loading a new project while the current project is modified.")); | ||||
|     if (saved_project == wxID_CANCEL || | ||||
|         (plater()->is_project_dirty() && saved_project == wxID_NO &&  | ||||
|          !check_and_save_current_preset_changes(_L("Project is loading"), _L("Loading a new project while some presets are modified.")))) | ||||
|         return false; | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| bool GUI_App::check_print_host_queue() | ||||
| { | ||||
|     wxString dirty; | ||||
|  | @ -2164,17 +2276,6 @@ void GUI_App::load_current_presets(bool check_printer_presets_ /*= true*/) | |||
| 		} | ||||
| } | ||||
| 
 | ||||
| void GUI_App::update_wizard_from_config() | ||||
| { | ||||
|     if (!m_wizard) | ||||
|         return; | ||||
|     // If ConfigWizard was created before changing of the configuration,
 | ||||
|     // we have to destroy it to have possibility to create it again in respect to the new config's parameters
 | ||||
|     m_wizard->Reparent(nullptr); | ||||
|     m_wizard->Destroy(); | ||||
|     m_wizard = nullptr; | ||||
| } | ||||
| 
 | ||||
| bool GUI_App::OnExceptionInMainLoop() | ||||
| { | ||||
|     generic_exception_handle(); | ||||
|  | @ -2336,19 +2437,12 @@ bool GUI_App::run_wizard(ConfigWizard::RunReason reason, ConfigWizard::StartPage | |||
|     wxCHECK_MSG(mainframe != nullptr, false, "Internal error: Main frame not created / null"); | ||||
| 
 | ||||
|     if (reason == ConfigWizard::RR_USER) { | ||||
|         wxString header = _L("Updates to Configuration Wizard may cause an another preset selection and lost of preset modification as a result.\n" | ||||
|                              "So, check unsaved changes and save them if necessary.") + "\n"; | ||||
|         if (!check_and_save_current_preset_changes(header, _L("ConfigWizard is opening")) || | ||||
|             preset_updater->config_update(app_config->orig_version(), PresetUpdater::UpdateParams::FORCED_BEFORE_WIZARD) == PresetUpdater::R_ALL_CANCELED) | ||||
|         if (preset_updater->config_update(app_config->orig_version(), PresetUpdater::UpdateParams::FORCED_BEFORE_WIZARD) == PresetUpdater::R_ALL_CANCELED) | ||||
|             return false; | ||||
|     } | ||||
| 
 | ||||
|     if (! m_wizard) { | ||||
|         wxBusyCursor wait; | ||||
|         m_wizard = new ConfigWizard(mainframe); | ||||
|     } | ||||
| 
 | ||||
|     const bool res = m_wizard->run(reason, start_page); | ||||
|     auto wizard = new ConfigWizard(mainframe); | ||||
|     const bool res = wizard->run(reason, start_page); | ||||
| 
 | ||||
|     if (res) { | ||||
|         load_current_presets(); | ||||
|  |  | |||
|  | @ -150,7 +150,6 @@ private: | |||
| 
 | ||||
|     std::unique_ptr<ImGuiWrapper> m_imgui; | ||||
|     std::unique_ptr<PrintHostJobQueue> m_printhost_job_queue; | ||||
|     ConfigWizard* m_wizard;    // Managed by wxWindow tree
 | ||||
| 	std::unique_ptr <OtherInstanceMessageHandler> m_other_instance_message_handler; | ||||
|     std::unique_ptr <wxSingleInstanceChecker> m_single_instance_checker; | ||||
|     std::string m_instance_hash_string; | ||||
|  | @ -246,11 +245,13 @@ public: | |||
|     bool            has_current_preset_changes() const; | ||||
|     void            update_saved_preset_from_current_preset(); | ||||
|     std::vector<std::pair<unsigned int, std::string>> get_selected_presets() const; | ||||
|     bool            check_and_save_current_preset_changes(const wxString& header = wxString(), const wxString& caption = wxString()); | ||||
|     bool            check_and_save_current_preset_changes(const wxString& caption, const wxString& header, bool remember_choice = true, bool use_dont_save_insted_of_discard = false); | ||||
|     void            apply_keeped_preset_modifications(); | ||||
|     bool            check_and_keep_current_preset_changes(const wxString& caption, const wxString& header, int action_buttons, bool* postponed_apply_of_keeped_changes = nullptr); | ||||
|     bool            can_load_project(); | ||||
|     bool            check_print_host_queue(); | ||||
|     bool            checked_tab(Tab* tab); | ||||
|     void            load_current_presets(bool check_printer_presets = true); | ||||
|     void            update_wizard_from_config(); | ||||
| 
 | ||||
|     wxString        current_language_code() const { return m_wxLocale->GetCanonicalName(); } | ||||
| 	// Translate the language code to a code, for which Prusa Research maintains translations. Defaults to "en_US".
 | ||||
|  |  | |||
|  | @ -4071,7 +4071,7 @@ void ObjectList::fix_through_netfabb() | |||
|             msg += ": " + from_u8(model_name) + "\n"; | ||||
|         else { | ||||
|             msg += ":\n"; | ||||
|             for (size_t i = 0; i < model_names.size(); ++i) | ||||
|             for (int i = 0; i < int(model_names.size()); ++i) | ||||
|                 msg += (i == model_idx ? " > " : "   ") + from_u8(model_names[i]) + "\n"; | ||||
|             msg += "\n"; | ||||
|         } | ||||
|  |  | |||
|  | @ -222,13 +222,14 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S | |||
|         } | ||||
| 
 | ||||
|         if (m_plater != nullptr) { | ||||
|             int saved_project = m_plater->save_project_if_dirty(); | ||||
|             int saved_project = m_plater->save_project_if_dirty(_L("Closing PrusaSlicer. Current project is modified.")); | ||||
|             if (saved_project == wxID_CANCEL) { | ||||
|                 event.Veto(); | ||||
|                 return; | ||||
|             } | ||||
|             // check unsaved changes only if project wasn't saved
 | ||||
|             else if (saved_project == wxID_NO && event.CanVeto() && !wxGetApp().check_and_save_current_preset_changes()) { | ||||
|             else if (saved_project == wxID_NO && event.CanVeto() &&  | ||||
|                      !wxGetApp().check_and_save_current_preset_changes(_L("PrusaSlicer is closing"), _L("Closing PrusaSlicer while some presets are modified."))) { | ||||
|                 event.Veto(); | ||||
|                 return; | ||||
|             } | ||||
|  | @ -820,7 +821,10 @@ bool MainFrame::is_active_and_shown_tab(Tab* tab) | |||
| 
 | ||||
| bool MainFrame::can_start_new_project() const | ||||
| { | ||||
|     return (m_plater != nullptr) && (!m_plater->get_project_filename(".3mf").IsEmpty() || GetTitle().StartsWith('*') || !m_plater->model().objects.empty()); | ||||
|     return m_plater && (!m_plater->get_project_filename(".3mf").IsEmpty() ||  | ||||
|                         GetTitle().StartsWith('*')|| | ||||
|                         wxGetApp().has_current_preset_changes() ||  | ||||
|                         !m_plater->model().objects.empty() ); | ||||
| } | ||||
| 
 | ||||
| bool MainFrame::can_save() const | ||||
|  | @ -852,13 +856,14 @@ void MainFrame::save_project() | |||
|     save_project_as(m_plater->get_project_filename(".3mf")); | ||||
| } | ||||
| 
 | ||||
| void MainFrame::save_project_as(const wxString& filename) | ||||
| bool MainFrame::save_project_as(const wxString& filename) | ||||
| { | ||||
|     bool ret = (m_plater != nullptr) ? m_plater->export_3mf(into_path(filename)) : false; | ||||
|     if (ret) { | ||||
| //        wxGetApp().update_saved_preset_from_current_preset();
 | ||||
|         m_plater->reset_project_dirty_after_save(); | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| bool MainFrame::can_export_model() const | ||||
|  | @ -1151,8 +1156,10 @@ void MainFrame::init_menubar_as_editor() | |||
|         Bind(wxEVT_MENU, [this](wxCommandEvent& evt) { | ||||
|             size_t file_id = evt.GetId() - wxID_FILE1; | ||||
|             wxString filename = m_recent_projects.GetHistoryFile(file_id); | ||||
|             if (wxFileExists(filename)) | ||||
|                 m_plater->load_project(filename); | ||||
|             if (wxFileExists(filename)) { | ||||
|                 if (wxGetApp().can_load_project()) | ||||
|                     m_plater->load_project(filename); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 //wxMessageDialog msg(this, _L("The selected project is no longer available.\nDo you want to remove it from the recent projects list?"), _L("Error"), wxYES_NO | wxYES_DEFAULT);
 | ||||
|  | @ -1772,7 +1779,7 @@ void MainFrame::export_config() | |||
| // Load a config file containing a Print, Filament & Printer preset.
 | ||||
| void MainFrame::load_config_file() | ||||
| { | ||||
|     if (!wxGetApp().check_and_save_current_preset_changes()) | ||||
|     if (!wxGetApp().check_and_save_current_preset_changes(_L("Loading of a configuration file"), "", false)) | ||||
|         return; | ||||
|     wxFileDialog dlg(this, _L("Select configuration to load:"), | ||||
|         !m_last_config.IsEmpty() ? get_dir_name(m_last_config) : wxGetApp().app_config->get_last_dir(), | ||||
|  | @ -1803,7 +1810,8 @@ bool MainFrame::load_config_file(const std::string &path) | |||
| 
 | ||||
| void MainFrame::export_configbundle(bool export_physical_printers /*= false*/) | ||||
| { | ||||
|     if (!wxGetApp().check_and_save_current_preset_changes()) | ||||
|     if (!wxGetApp().check_and_save_current_preset_changes(_L("Exporting configuration bundle"), | ||||
|                                                           _L("Some presets are modified and the unsaved changes will not be exported into configuration bundle."), false, true)) | ||||
|         return; | ||||
|     // validate current configuration in case it's dirty
 | ||||
|     auto err = wxGetApp().preset_bundle->full_config().validate(); | ||||
|  | @ -1835,7 +1843,7 @@ void MainFrame::export_configbundle(bool export_physical_printers /*= false*/) | |||
| // but that behavior was not documented and likely buggy.
 | ||||
| void MainFrame::load_configbundle(wxString file/* = wxEmptyString, const bool reset_user_profile*/) | ||||
| { | ||||
|     if (!wxGetApp().check_and_save_current_preset_changes()) | ||||
|     if (!wxGetApp().check_and_save_current_preset_changes(_L("Loading of a configuration bundle"), "", false)) | ||||
|         return; | ||||
|     if (file.IsEmpty()) { | ||||
|         wxFileDialog dlg(this, _L("Select configuration to load:"), | ||||
|  |  | |||
|  | @ -190,7 +190,7 @@ public: | |||
|     bool can_save() const; | ||||
|     bool can_save_as() const; | ||||
|     void save_project(); | ||||
|     void save_project_as(const wxString& filename = wxString()); | ||||
|     bool save_project_as(const wxString& filename = wxString()); | ||||
| 
 | ||||
|     void        add_to_recent_projects(const wxString& filename); | ||||
|     void        technology_changed(); | ||||
|  |  | |||
|  | @ -1575,16 +1575,22 @@ struct Plater::priv | |||
| 
 | ||||
|     bool is_project_dirty() const { return dirty_state.is_dirty(); } | ||||
|     void update_project_dirty_from_presets() { dirty_state.update_from_presets(); } | ||||
|     int save_project_if_dirty() { | ||||
|     int save_project_if_dirty(const wxString& reason) { | ||||
|         int res = wxID_NO; | ||||
|         if (dirty_state.is_dirty()) { | ||||
|             MainFrame* mainframe = wxGetApp().mainframe; | ||||
|             if (mainframe->can_save_as()) { | ||||
|                 //wxMessageDialog dlg(mainframe, _L("Do you want to save the changes to the current project ?"), wxString(SLIC3R_APP_NAME), wxYES_NO | wxCANCEL);
 | ||||
|                 MessageDialog dlg(mainframe, _L("Do you want to save the changes to the current project ?"), wxString(SLIC3R_APP_NAME), wxYES_NO | wxCANCEL); | ||||
|                 res = dlg.ShowModal(); | ||||
|                 wxString suggested_project_name; | ||||
|                 wxString project_name = suggested_project_name = get_project_filename(".3mf"); | ||||
|                 if (suggested_project_name.IsEmpty()) { | ||||
|                     fs::path output_file = get_export_file_path(FT_3MF); | ||||
|                     suggested_project_name = output_file.empty() ? _L("Untitled") : from_u8(output_file.stem().string()); | ||||
|                 } | ||||
|                 res = MessageDialog(mainframe, reason + "\n" + format_wxstr(_L("Do you want to save the changes to \"%1%\"?"), suggested_project_name),  | ||||
|                                     wxString(SLIC3R_APP_NAME), wxYES_NO | wxCANCEL).ShowModal(); | ||||
|                 if (res == wxID_YES) | ||||
|                     mainframe->save_project_as(wxGetApp().plater()->get_project_filename()); | ||||
|                     if (!mainframe->save_project_as(project_name)) | ||||
|                         res = wxID_CANCEL; | ||||
|             } | ||||
|         } | ||||
|         return res; | ||||
|  | @ -1644,6 +1650,7 @@ struct Plater::priv | |||
|     std::vector<size_t> load_files(const std::vector<fs::path>& input_files, bool load_model, bool load_config, bool used_inches = false); | ||||
|     std::vector<size_t> load_model_objects(const ModelObjectPtrs& model_objects, bool allow_negative_z = false); | ||||
| 
 | ||||
|     fs::path get_export_file_path(GUI::FileType file_type); | ||||
|     wxString get_export_file(GUI::FileType file_type); | ||||
| 
 | ||||
|     const Selection& get_selection() const; | ||||
|  | @ -2599,22 +2606,8 @@ std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs& mode | |||
|     return obj_idxs; | ||||
| } | ||||
| 
 | ||||
| wxString Plater::priv::get_export_file(GUI::FileType file_type) | ||||
| fs::path Plater::priv::get_export_file_path(GUI::FileType file_type) | ||||
| { | ||||
|     wxString wildcard; | ||||
|     switch (file_type) { | ||||
|         case FT_STL: | ||||
|         case FT_AMF: | ||||
|         case FT_3MF: | ||||
|         case FT_GCODE: | ||||
|         case FT_OBJ: | ||||
|             wildcard = file_wildcards(file_type); | ||||
|         break; | ||||
|         default: | ||||
|             wildcard = file_wildcards(FT_MODEL); | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     // Update printbility state of each of the ModelInstances.
 | ||||
|     this->update_print_volume_state(); | ||||
| 
 | ||||
|  | @ -2639,7 +2632,31 @@ wxString Plater::priv::get_export_file(GUI::FileType file_type) | |||
|         if (output_file.empty() && !model.objects.empty()) | ||||
|             // Find the file name of the first object.
 | ||||
|             output_file = this->model.objects[0]->get_export_filename(); | ||||
| 
 | ||||
|         if (output_file.empty()) | ||||
|             // Use _L("Untitled") name
 | ||||
|             output_file = into_path(_L("Untitled")); | ||||
|     } | ||||
|     return output_file; | ||||
| } | ||||
| 
 | ||||
| wxString Plater::priv::get_export_file(GUI::FileType file_type) | ||||
| { | ||||
|     wxString wildcard; | ||||
|     switch (file_type) { | ||||
|         case FT_STL: | ||||
|         case FT_AMF: | ||||
|         case FT_3MF: | ||||
|         case FT_GCODE: | ||||
|         case FT_OBJ: | ||||
|             wildcard = file_wildcards(file_type); | ||||
|         break; | ||||
|         default: | ||||
|             wildcard = file_wildcards(FT_MODEL); | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     fs::path output_file = get_export_file_path(file_type); | ||||
| 
 | ||||
|     wxString dlg_title; | ||||
|     switch (file_type) { | ||||
|  | @ -4714,8 +4731,10 @@ void Plater::priv::undo_redo_to(std::vector<UndoRedo::Snapshot>::const_iterator | |||
|     if (printer_technology_changed) { | ||||
|         // Switching the printer technology when jumping forwards / backwards in time. Switch to the last active printer profile of the other type.
 | ||||
|         std::string s_pt = (it_snapshot->snapshot_data.printer_technology == ptFFF) ? "FFF" : "SLA"; | ||||
|         if (!wxGetApp().check_and_save_current_preset_changes(format_wxstr(_L( | ||||
|             "%1% printer was active at the time the target Undo / Redo snapshot was taken. Switching to %1% printer requires reloading of %1% presets."), s_pt))) | ||||
|         if (!wxGetApp().check_and_save_current_preset_changes(_L("Undo / Redo is processing"),  | ||||
| //            format_wxstr(_L("%1% printer was active at the time the target Undo / Redo snapshot was taken. Switching to %1% printer requires reloading of %1% presets."), s_pt)))
 | ||||
|             format_wxstr(_L("Switching the printer technology from %1% to %2%.\n" | ||||
|                             "Some %1% presets were modified, which will be lost after switching the printer technology."), s_pt =="FFF" ? "SLA" : "FFF", s_pt), false)) | ||||
|             // Don't switch the profiles.
 | ||||
|             return; | ||||
|     } | ||||
|  | @ -4893,7 +4912,7 @@ Plater::Plater(wxWindow *parent, MainFrame *main_frame) | |||
| 
 | ||||
| bool Plater::is_project_dirty() const { return p->is_project_dirty(); } | ||||
| void Plater::update_project_dirty_from_presets() { p->update_project_dirty_from_presets(); } | ||||
| int  Plater::save_project_if_dirty() { return p->save_project_if_dirty(); } | ||||
| int  Plater::save_project_if_dirty(const wxString& reason) { return p->save_project_if_dirty(reason); } | ||||
| void Plater::reset_project_dirty_after_save() { p->reset_project_dirty_after_save(); } | ||||
| void Plater::reset_project_dirty_initial_presets() { p->reset_project_dirty_initial_presets(); } | ||||
| #if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW | ||||
|  | @ -4910,8 +4929,20 @@ SLAPrint&       Plater::sla_print()         { return p->sla_print; } | |||
| 
 | ||||
| void Plater::new_project() | ||||
| { | ||||
|     if (p->save_project_if_dirty() == wxID_CANCEL) | ||||
|     if (int saved_project = p->save_project_if_dirty(_L("Creating a new project while the current project is modified.")); saved_project == wxID_CANCEL) | ||||
|         return; | ||||
|     else { | ||||
|         wxString header = _L("Creating a new project while some presets are modified.") + "\n" +  | ||||
|                           (saved_project == wxID_YES ? _L("You can keep presets modifications to the new project or discard them") : | ||||
|                           _L("You can keep presets modifications to the new project, discard them or save changes as new presets.\n" | ||||
|                              "Note, if changes will be saved than new project wouldn't keep them")); | ||||
|         using ab = UnsavedChangesDialog::ActionButtons; | ||||
|         int act_buttons = ab::KEEP; | ||||
|         if (saved_project == wxID_NO) | ||||
|             act_buttons |= ab::SAVE; | ||||
|         if (!wxGetApp().check_and_keep_current_preset_changes(_L("New Project is creating"), header, act_buttons)) | ||||
|             return; | ||||
|     } | ||||
| 
 | ||||
|     p->select_view_3D("3D"); | ||||
|     take_snapshot(_L("New Project")); | ||||
|  | @ -4923,7 +4954,7 @@ void Plater::new_project() | |||
| 
 | ||||
| void Plater::load_project() | ||||
| { | ||||
|     if (p->save_project_if_dirty() == wxID_CANCEL) | ||||
|     if (!wxGetApp().can_load_project()) | ||||
|         return; | ||||
| 
 | ||||
|     // Ask user for a project file name.
 | ||||
|  | @ -5219,7 +5250,8 @@ bool Plater::load_files(const wxArrayString& filenames) | |||
| 
 | ||||
|             switch (load_type) { | ||||
|             case LoadType::OpenProject: { | ||||
|                 load_project(from_path(*it)); | ||||
|                 if (wxGetApp().can_load_project()) | ||||
|                     load_project(from_path(*it)); | ||||
|                 break; | ||||
|             } | ||||
|             case LoadType::LoadGeometry: { | ||||
|  |  | |||
|  | @ -140,7 +140,7 @@ public: | |||
| 
 | ||||
|     bool is_project_dirty() const; | ||||
|     void update_project_dirty_from_presets(); | ||||
|     int  save_project_if_dirty(); | ||||
|     int  save_project_if_dirty(const wxString& reason); | ||||
|     void reset_project_dirty_after_save(); | ||||
|     void reset_project_dirty_initial_presets(); | ||||
| #if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW | ||||
|  |  | |||
|  | @ -73,7 +73,7 @@ void PreferencesDialog::build(size_t selected_tab) | |||
| 	// Add "General" tab
 | ||||
| 	m_optgroup_general = create_options_tab(_L("General"), tabs); | ||||
| 	m_optgroup_general->m_on_change = [this](t_config_option_key opt_key, boost::any value) { | ||||
| 		if (opt_key == "default_action_on_close_application" || opt_key == "default_action_on_select_preset") | ||||
| 		if (opt_key == "default_action_on_close_application" || opt_key == "default_action_on_select_preset" || opt_key == "default_action_on_new_project") | ||||
| 			m_values[opt_key] = boost::any_cast<bool>(value) ? "none" : "discard"; | ||||
| 		else | ||||
| 		    m_values[opt_key] = boost::any_cast<bool>(value) ? "1" : "0"; | ||||
|  | @ -186,19 +186,28 @@ void PreferencesDialog::build(size_t selected_tab) | |||
| 		option = Option(def, "single_instance"); | ||||
| 		m_optgroup_general->append_single_option_line(option); | ||||
| 
 | ||||
| 		def.label = L("Ask for unsaved changes when closing application"); | ||||
| 		def.label = L("Ask for unsaved changes when closing application or loading new project"); | ||||
| 		def.type = coBool; | ||||
| 		def.tooltip = L("When closing the application, always ask for unsaved changes"); | ||||
| 		def.tooltip = L("Always ask for unsaved changes, when: \n" | ||||
| 						"- Closing PrusaSlicer while some presets are modified,\n" | ||||
| 						"- Loading a new project while some presets are modified"); | ||||
| 		def.set_default_value(new ConfigOptionBool{ app_config->get("default_action_on_close_application") == "none" }); | ||||
| 		option = Option(def, "default_action_on_close_application"); | ||||
| 		m_optgroup_general->append_single_option_line(option); | ||||
| 
 | ||||
| 		def.label = L("Ask for unsaved changes when selecting new preset"); | ||||
| 		def.type = coBool; | ||||
| 		def.tooltip = L("Always ask for unsaved changes when selecting new preset"); | ||||
| 		def.tooltip = L("Always ask for unsaved changes when selecting new preset or resetting a preset"); | ||||
| 		def.set_default_value(new ConfigOptionBool{ app_config->get("default_action_on_select_preset") == "none" }); | ||||
| 		option = Option(def, "default_action_on_select_preset"); | ||||
| 		m_optgroup_general->append_single_option_line(option); | ||||
| 
 | ||||
| 		def.label = L("Ask for unsaved changes when creating new project"); | ||||
| 		def.type = coBool; | ||||
| 		def.tooltip = L("Always ask for unsaved changes when creating new project"); | ||||
| 		def.set_default_value(new ConfigOptionBool{ app_config->get("default_action_on_new_project") == "none" }); | ||||
| 		option = Option(def, "default_action_on_new_project"); | ||||
| 		m_optgroup_general->append_single_option_line(option); | ||||
| 	} | ||||
| #ifdef _WIN32 | ||||
| 	else { | ||||
|  |  | |||
|  | @ -1216,12 +1216,20 @@ void Tab::cache_config_diff(const std::vector<std::string>& selected_options) | |||
| 
 | ||||
| void Tab::apply_config_from_cache() | ||||
| { | ||||
|     bool was_applied = false; | ||||
|     // check and apply extruders count for printer preset
 | ||||
|     if (m_type == Preset::TYPE_PRINTER) | ||||
|         was_applied = static_cast<TabPrinter*>(this)->apply_extruder_cnt_from_cache(); | ||||
| 
 | ||||
|     if (!m_cache_config.empty()) { | ||||
|         m_presets->get_edited_preset().config.apply(m_cache_config); | ||||
|         m_cache_config.clear(); | ||||
| 
 | ||||
|         update_dirty(); | ||||
|         was_applied = true; | ||||
|     } | ||||
| 
 | ||||
|     if (was_applied) | ||||
|         update_dirty(); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  | @ -3322,10 +3330,6 @@ void Tab::select_preset(std::string preset_name, bool delete_current /*=false*/, | |||
|                 m_dependent_tabs = { Preset::Type::TYPE_SLA_PRINT, Preset::Type::TYPE_SLA_MATERIAL }; | ||||
|         } | ||||
| 
 | ||||
|         // check and apply extruders count for printer preset
 | ||||
|         if (m_type == Preset::TYPE_PRINTER) | ||||
|             static_cast<TabPrinter*>(this)->apply_extruder_cnt_from_cache(); | ||||
| 
 | ||||
|         // check if there is something in the cache to move to the new selected preset
 | ||||
|         apply_config_from_cache(); | ||||
| 
 | ||||
|  | @ -3862,15 +3866,17 @@ void TabPrinter::cache_extruder_cnt() | |||
|     m_cache_extruder_count = m_extruders_count; | ||||
| } | ||||
| 
 | ||||
| void TabPrinter::apply_extruder_cnt_from_cache() | ||||
| bool TabPrinter::apply_extruder_cnt_from_cache() | ||||
| { | ||||
|     if (m_presets->get_edited_preset().printer_technology() == ptSLA) | ||||
|         return; | ||||
|         return false; | ||||
| 
 | ||||
|     if (m_cache_extruder_count > 0) { | ||||
|         m_presets->get_edited_preset().set_num_extruders(m_cache_extruder_count); | ||||
|         m_cache_extruder_count = 0; | ||||
|         return true; | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| bool Tab::validate_custom_gcodes() | ||||
|  |  | |||
|  | @ -469,7 +469,7 @@ public: | |||
| 
 | ||||
| 	wxSizer*	create_bed_shape_widget(wxWindow* parent); | ||||
| 	void		cache_extruder_cnt(); | ||||
| 	void		apply_extruder_cnt_from_cache(); | ||||
| 	bool		apply_extruder_cnt_from_cache(); | ||||
| }; | ||||
| 
 | ||||
| class TabSLAMaterial : public Tab | ||||
|  |  | |||
|  | @ -727,14 +727,21 @@ void DiffViewCtrl::item_value_changed(wxDataViewEvent& event) | |||
|     m_empty_selection = selected_options().empty(); | ||||
| } | ||||
| 
 | ||||
| std::vector<std::string> DiffViewCtrl::unselected_options(Preset::Type type) | ||||
| bool DiffViewCtrl::has_unselected_options() | ||||
| { | ||||
|     for (auto item : m_items_map) | ||||
|         if (!model->IsEnabledItem(item.first)) | ||||
|             return true; | ||||
| 
 | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| std::vector<std::string> DiffViewCtrl::options(Preset::Type type, bool selected) | ||||
| { | ||||
|     std::vector<std::string> ret; | ||||
| 
 | ||||
|     for (auto item : m_items_map) { | ||||
|         if (item.second.opt_key == "extruders_count") | ||||
|             continue; | ||||
|         if (item.second.type == type && !model->IsEnabledItem(item.first)) | ||||
|         if (item.second.type == type && model->IsEnabledItem(item.first) == selected) | ||||
|             ret.emplace_back(get_pure_opt_key(item.second.opt_key)); | ||||
|     } | ||||
| 
 | ||||
|  | @ -757,20 +764,24 @@ std::vector<std::string> DiffViewCtrl::selected_options() | |||
| //          UnsavedChangesDialog
 | ||||
| //------------------------------------------
 | ||||
| 
 | ||||
| UnsavedChangesDialog::UnsavedChangesDialog(const wxString& header, const wxString& caption/* = wxString()*/) | ||||
|     : DPIDialog(static_cast<wxWindow*>(wxGetApp().mainframe), wxID_ANY, (caption.IsEmpty() ? _L("PrusaSlicer is closing") : caption) + ": " + _L("Unsaved Changes"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) | ||||
| { | ||||
|     m_app_config_key = "default_action_on_close_application"; | ||||
| static std::string none{"none"}; | ||||
| 
 | ||||
| UnsavedChangesDialog::UnsavedChangesDialog(const wxString& caption, const wxString& header,  | ||||
|                                            const std::string& app_config_key, int act_buttons) | ||||
|     : DPIDialog(static_cast<wxWindow*>(wxGetApp().mainframe), wxID_ANY, caption + ": " + _L("Unsaved Changes"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER), | ||||
|     m_app_config_key(app_config_key), | ||||
|     m_buttons(act_buttons) | ||||
| { | ||||
|     build(Preset::TYPE_INVALID, nullptr, "", header); | ||||
| 
 | ||||
|     const std::string& def_action = wxGetApp().app_config->get(m_app_config_key); | ||||
|     if (def_action == "none") | ||||
|     const std::string& def_action = m_app_config_key.empty() ? none : wxGetApp().app_config->get(m_app_config_key); | ||||
|     if (def_action == none) | ||||
|         this->CenterOnScreen(); | ||||
|     else { | ||||
|         m_exit_action = def_action == ActSave ? Action::Save : Action::Discard; | ||||
|         if (m_exit_action == Action::Save) | ||||
|             save(nullptr); | ||||
|         m_exit_action = def_action == ActTransfer   ? Action::Transfer  : | ||||
|                         def_action == ActSave       ? Action::Save      : Action::Discard; | ||||
|         if (m_exit_action != Action::Discard) | ||||
|             save(nullptr, m_exit_action == Action::Save); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | @ -782,7 +793,7 @@ UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type, PresetCollection* | |||
|     build(type, dependent_presets, new_selected_preset); | ||||
| 
 | ||||
|     const std::string& def_action = wxGetApp().app_config->get(m_app_config_key); | ||||
|     if (def_action == "none") { | ||||
|     if (def_action == none) { | ||||
|         if (wxGetApp().mainframe->is_dlg_layout() && wxGetApp().mainframe->m_settings_dialog.HasFocus()) | ||||
|             this->SetPosition(wxGetApp().mainframe->m_settings_dialog.GetPosition()); | ||||
|         this->CenterOnScreen(); | ||||
|  | @ -833,7 +844,8 @@ void UnsavedChangesDialog::build(Preset::Type type, PresetCollection* dependent_ | |||
| 
 | ||||
|         (*btn)->Bind(wxEVT_BUTTON, [this, close_act, dependent_presets](wxEvent&) { | ||||
|             update_config(close_act); | ||||
|             if (close_act == Action::Save && !save(dependent_presets)) | ||||
|             bool save_names_and_types = close_act == Action::Save || (close_act == Action::Transfer && ActionButtons::KEEP & m_buttons); | ||||
|             if (save_names_and_types && !save(dependent_presets, close_act == Action::Save)) | ||||
|                 return; | ||||
|             close(close_act); | ||||
|         }); | ||||
|  | @ -842,13 +854,26 @@ void UnsavedChangesDialog::build(Preset::Type type, PresetCollection* dependent_ | |||
|         (*btn)->Bind(wxEVT_LEAVE_WINDOW, [this](wxMouseEvent& e) { show_info_line(Action::Undef); e.Skip(); }); | ||||
|     }; | ||||
| 
 | ||||
|     const PresetCollection* switched_presets = type == Preset::TYPE_INVALID ? nullptr : wxGetApp().get_tab(type)->get_presets(); | ||||
|     if (dependent_presets && switched_presets && (type == dependent_presets->type() ? | ||||
|         dependent_presets->get_edited_preset().printer_technology() == dependent_presets->find_preset(new_selected_preset)->printer_technology() : | ||||
|         switched_presets->get_edited_preset().printer_technology() == switched_presets->find_preset(new_selected_preset)->printer_technology())) | ||||
|         add_btn(&m_transfer_btn, m_move_btn_id, "paste_menu", Action::Transfer, _L("Transfer")); | ||||
|     add_btn(&m_discard_btn, m_continue_btn_id, dependent_presets ? "switch_presets" : "exit", Action::Discard, _L("Discard"), false); | ||||
|     add_btn(&m_save_btn, m_save_btn_id, "save", Action::Save, _L("Save")); | ||||
|     // "Transfer" / "Keep" button
 | ||||
|     if (ActionButtons::TRANSFER & m_buttons) { | ||||
|         const PresetCollection* switched_presets = type == Preset::TYPE_INVALID ? nullptr : wxGetApp().get_tab(type)->get_presets(); | ||||
|         if (dependent_presets && switched_presets && (type == dependent_presets->type() ? | ||||
|             dependent_presets->get_edited_preset().printer_technology() == dependent_presets->find_preset(new_selected_preset)->printer_technology() : | ||||
|             switched_presets->get_edited_preset().printer_technology() == switched_presets->find_preset(new_selected_preset)->printer_technology())) | ||||
|             add_btn(&m_transfer_btn, m_move_btn_id, "paste_menu", Action::Transfer, switched_presets->get_edited_preset().name == new_selected_preset ? _L("Keep") : _L("Transfer")); | ||||
|     } | ||||
|     if (!m_transfer_btn && (ActionButtons::KEEP & m_buttons)) | ||||
|         add_btn(&m_transfer_btn, m_move_btn_id, "paste_menu", Action::Transfer, _L("Keep")); | ||||
| 
 | ||||
|     { // "Don't save" / "Discard" button
 | ||||
|         std::string btn_icon    = (ActionButtons::DONT_SAVE & m_buttons) ? "" : (dependent_presets || (ActionButtons::KEEP & m_buttons)) ? "switch_presets" : "exit"; | ||||
|         wxString    btn_label   = (ActionButtons::DONT_SAVE & m_buttons) ? _L("Don't save") : _L("Discard"); | ||||
|         add_btn(&m_discard_btn, m_continue_btn_id, btn_icon, Action::Discard, btn_label, false); | ||||
|     } | ||||
| 
 | ||||
|     // "Save" button
 | ||||
|     if (ActionButtons::SAVE & m_buttons)  | ||||
|         add_btn(&m_save_btn, m_save_btn_id, "save", Action::Save, _L("Save")); | ||||
| 
 | ||||
|     ScalableButton* cancel_btn = new ScalableButton(this, wxID_CANCEL, "cross", _L("Cancel"), wxDefaultSize, wxDefaultPosition, wxBORDER_DEFAULT, true, 24); | ||||
|     buttons->Add(cancel_btn, 1, wxLEFT|wxRIGHT, 5); | ||||
|  | @ -859,34 +884,42 @@ void UnsavedChangesDialog::build(Preset::Type type, PresetCollection* dependent_ | |||
|     m_info_line->SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Bold()); | ||||
|     m_info_line->Hide(); | ||||
| 
 | ||||
|     m_remember_choice = new wxCheckBox(this, wxID_ANY, _L("Remember my choice")); | ||||
|     m_remember_choice->SetValue(wxGetApp().app_config->get(m_app_config_key) != "none"); | ||||
|     m_remember_choice->Bind(wxEVT_CHECKBOX, [type, this](wxCommandEvent& evt) | ||||
|     { | ||||
|         if (!evt.IsChecked()) | ||||
|             return; | ||||
|         wxString preferences_item = type == Preset::TYPE_INVALID ? _L("Ask for unsaved changes when closing application") :  | ||||
|                                                                    _L("Ask for unsaved changes when selecting new preset"); | ||||
|         wxString msg = | ||||
|             _L("PrusaSlicer will remember your action.") + "\n\n" + | ||||
|             (type == Preset::TYPE_INVALID ? | ||||
|                 _L("You will not be asked about the unsaved changes the next time you close PrusaSlicer.") : | ||||
|                 _L("You will not be asked about the unsaved changes the next time you switch a preset.")) + "\n\n" + | ||||
|                 format_wxstr(_L("Visit \"Preferences\" and check \"%1%\"\nto be asked about unsaved changes again."), preferences_item); | ||||
|     if (!m_app_config_key.empty()) { | ||||
|         m_remember_choice = new wxCheckBox(this, wxID_ANY, _L("Remember my choice")); | ||||
|         m_remember_choice->SetValue(wxGetApp().app_config->get(m_app_config_key) != none); | ||||
|         m_remember_choice->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent& evt) | ||||
|         { | ||||
|             if (!evt.IsChecked()) | ||||
|                 return; | ||||
|             wxString preferences_item = m_app_config_key == "default_action_on_new_project"     ? _L("Ask for unsaved changes when creating new project") :  | ||||
|                                         m_app_config_key == "default_action_on_select_preset"   ? _L("Ask for unsaved changes when selecting new preset") : | ||||
|                                                                                                   _L("Ask for unsaved changes when ??closing application??") ; | ||||
|             wxString action = m_app_config_key == "default_action_on_new_project"   ? _L("You will not be asked about the unsaved changes the next time you create new project") :  | ||||
|                               m_app_config_key == "default_action_on_select_preset" ? _L("You will not be asked about the unsaved changes the next time you switch a preset") : | ||||
|                                                                                       _L("You will not be asked about the unsaved changes the next time you: \n" | ||||
|                                                                                             "- close the application,\n" | ||||
|                                                                                             "- load project,\n" | ||||
|                                                                                             "- process Undo / Redo with change of print technologie,\n" | ||||
|                                                                                             "- take/load snapshot,\n" | ||||
|                                                                                             "- load config file/bundle,\n" | ||||
|                                                                                             "- export config_bundle") ; | ||||
|             wxString msg = _L("PrusaSlicer will remember your action.") + "\n\n" + action + "\n\n" + | ||||
|                            format_wxstr(_L("Visit \"Preferences\" and check \"%1%\"\nto be asked about unsaved changes again."), preferences_item); | ||||
|      | ||||
|         //wxMessageDialog dialog(nullptr, msg, _L("PrusaSlicer: Don't ask me again"), wxOK | wxCANCEL | wxICON_INFORMATION);
 | ||||
|         MessageDialog dialog(nullptr, msg, _L("PrusaSlicer: Don't ask me again"), wxOK | wxCANCEL | wxICON_INFORMATION); | ||||
|         if (dialog.ShowModal() == wxID_CANCEL) | ||||
|             m_remember_choice->SetValue(false); | ||||
|     }); | ||||
|             MessageDialog dialog(nullptr, msg, _L("PrusaSlicer: Don't ask me again"), wxOK | wxCANCEL | wxICON_INFORMATION); | ||||
|             if (dialog.ShowModal() == wxID_CANCEL) | ||||
|                 m_remember_choice->SetValue(false); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); | ||||
| 
 | ||||
|     topSizer->Add(m_action_line,0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); | ||||
|     topSizer->Add(m_tree,       1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); | ||||
|     topSizer->Add(m_info_line,  0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, 2*border); | ||||
|     topSizer->Add(buttons,      0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); | ||||
|     topSizer->Add(m_remember_choice, 0, wxEXPAND | wxALL, border); | ||||
|     topSizer->Add(buttons,      0, wxEXPAND | wxALL, border); | ||||
|     if (m_remember_choice) | ||||
|         topSizer->Add(m_remember_choice, 0, wxEXPAND | wxLEFT | wxBOTTOM | wxRIGHT, border); | ||||
| 
 | ||||
|     update(type, dependent_presets, new_selected_preset, header); | ||||
| 
 | ||||
|  | @ -905,10 +938,12 @@ void UnsavedChangesDialog::show_info_line(Action action, std::string preset_name | |||
|         if (action == Action::Undef) | ||||
|             text = _L("Some fields are too long to fit. Right mouse click reveals the full text."); | ||||
|         else if (action == Action::Discard) | ||||
|             text = _L("All settings changes will be discarded."); | ||||
|             text = ActionButtons::DONT_SAVE & m_buttons ? _L("All settings changes will not be saved") :_L("All settings changes will be discarded."); | ||||
|         else { | ||||
|             if (preset_name.empty()) | ||||
|                 text = action == Action::Save ? _L("Save the selected options.") : _L("Transfer the selected settings to the newly selected preset."); | ||||
|                 text = action == Action::Save           ? _L("Save the selected options.") :  | ||||
|                        ActionButtons::KEEP & m_buttons  ? _L("Keep the selected settings.") : | ||||
|                                                           _L("Transfer the selected settings to the newly selected preset."); | ||||
|             else | ||||
|                 text = format_wxstr( | ||||
|                     action == Action::Save ? | ||||
|  | @ -927,7 +962,7 @@ void UnsavedChangesDialog::show_info_line(Action action, std::string preset_name | |||
| 
 | ||||
| void UnsavedChangesDialog::update_config(Action action) | ||||
| { | ||||
|     if (!m_remember_choice->GetValue()) | ||||
|     if (!m_remember_choice || !m_remember_choice->GetValue()) | ||||
|         return; | ||||
| 
 | ||||
|     std::string act = action == Action::Transfer ? ActTransfer : | ||||
|  | @ -941,7 +976,7 @@ void UnsavedChangesDialog::close(Action action) | |||
|     this->EndModal(wxID_CLOSE); | ||||
| } | ||||
| 
 | ||||
| bool UnsavedChangesDialog::save(PresetCollection* dependent_presets) | ||||
| bool UnsavedChangesDialog::save(PresetCollection* dependent_presets, bool show_save_preset_dialog/* = true*/) | ||||
| { | ||||
|     names_and_types.clear(); | ||||
| 
 | ||||
|  | @ -979,7 +1014,7 @@ bool UnsavedChangesDialog::save(PresetCollection* dependent_presets) | |||
|             } | ||||
| 
 | ||||
| 
 | ||||
|         if (!types_for_save.empty()) { | ||||
|         if (show_save_preset_dialog && !types_for_save.empty()) { | ||||
|             SavePresetDialog save_dlg(this, types_for_save); | ||||
|             if (save_dlg.ShowModal() != wxID_OK) { | ||||
|                 m_exit_action = Action::Discard; | ||||
|  | @ -1164,16 +1199,24 @@ void UnsavedChangesDialog::update(Preset::Type type, PresetCollection* dependent | |||
|     PresetCollection* presets = dependent_presets; | ||||
| 
 | ||||
|     // activate buttons and labels
 | ||||
|     m_save_btn      ->Bind(wxEVT_ENTER_WINDOW, [this, presets]              (wxMouseEvent& e) { show_info_line(Action::Save, presets ? presets->get_selected_preset().name : ""); e.Skip(); }); | ||||
|     if (m_save_btn) | ||||
|         m_save_btn    ->Bind(wxEVT_ENTER_WINDOW, [this, presets]                           (wxMouseEvent& e) { show_info_line(Action::Save, presets ? presets->get_selected_preset().name : ""); e.Skip(); }); | ||||
|     if (m_transfer_btn) { | ||||
|         bool is_empty_name = type != dependent_presets->type(); | ||||
|         m_transfer_btn  ->Bind(wxEVT_ENTER_WINDOW, [this, new_selected_preset, is_empty_name]  (wxMouseEvent& e) { show_info_line(Action::Transfer, is_empty_name ? "" : new_selected_preset); e.Skip(); }); | ||||
|         bool is_empty_name = dependent_presets && type != dependent_presets->type(); | ||||
|         m_transfer_btn->Bind(wxEVT_ENTER_WINDOW, [this, new_selected_preset, is_empty_name](wxMouseEvent& e) { show_info_line(Action::Transfer, is_empty_name ? "" : new_selected_preset); e.Skip(); }); | ||||
|     } | ||||
|     m_discard_btn  ->Bind(wxEVT_ENTER_WINDOW, [this]                       (wxMouseEvent& e) { show_info_line(Action::Discard); e.Skip(); }); | ||||
| 
 | ||||
|     if (m_discard_btn) | ||||
|         m_discard_btn ->Bind(wxEVT_ENTER_WINDOW, [this]                                    (wxMouseEvent& e) { show_info_line(Action::Discard); e.Skip(); }); | ||||
| 
 | ||||
|     if (type == Preset::TYPE_INVALID) { | ||||
|         m_action_line->SetLabel(header + "\n" + _L("The following presets were modified:")); | ||||
|         PrinterTechnology printer_technology = wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology(); | ||||
|         int presets_cnt = 0; | ||||
|         for (Tab* tab : wxGetApp().tabs_list) | ||||
|             if (tab->supports_printer_technology(printer_technology) && tab->current_preset_is_dirty()) | ||||
|                 presets_cnt++; | ||||
|         m_action_line->SetLabel((header.IsEmpty() ? "" : header + "\n\n") + //_L("The following presets were modified:"));
 | ||||
|                                 + _L_PLURAL("The following preset was modified", | ||||
|                                             "The following presets were modified", presets_cnt)); | ||||
|     } | ||||
|     else { | ||||
|         wxString action_msg; | ||||
|  |  | |||
|  | @ -220,8 +220,9 @@ public: | |||
|     void        context_menu(wxDataViewEvent& event); | ||||
|     void        item_value_changed(wxDataViewEvent& event); | ||||
|     void        set_em_unit(int em) { m_em_unit = em; } | ||||
|     bool        has_unselected_options(); | ||||
| 
 | ||||
|     std::vector<std::string> unselected_options(Preset::Type type); | ||||
|     std::vector<std::string> options(Preset::Type type, bool selected); | ||||
|     std::vector<std::string> selected_options(); | ||||
| }; | ||||
| 
 | ||||
|  | @ -261,10 +262,22 @@ class UnsavedChangesDialog : public DPIDialog | |||
|     Action m_exit_action {Action::Undef}; | ||||
|     // preset names which are modified in SavePresetDialog and related types
 | ||||
|     std::vector<std::pair<std::string, Preset::Type>>  names_and_types; | ||||
|     // additional action buttons used in dialog
 | ||||
|     int m_buttons { ActionButtons::TRANSFER | ActionButtons::SAVE }; | ||||
| 
 | ||||
| public: | ||||
|     UnsavedChangesDialog(const wxString& header, const wxString& caption = wxString()); | ||||
|     // Discard and Cancel buttons are always but next buttons are optional
 | ||||
|     enum ActionButtons { | ||||
|         TRANSFER  = 1, | ||||
|         KEEP      = 2, | ||||
|         SAVE      = 4, | ||||
|         DONT_SAVE = 8, | ||||
|     }; | ||||
| 
 | ||||
|     // show unsaved changes when preset is switching
 | ||||
|     UnsavedChangesDialog(Preset::Type type, PresetCollection* dependent_presets, const std::string& new_selected_preset); | ||||
|     // show unsaved changes for all another cases
 | ||||
|     UnsavedChangesDialog(const wxString& caption, const wxString& header, const std::string& app_config_key, int act_buttons); | ||||
|     ~UnsavedChangesDialog() {} | ||||
| 
 | ||||
|     void build(Preset::Type type, PresetCollection* dependent_presets, const std::string& new_selected_preset, const wxString& header = ""); | ||||
|  | @ -273,7 +286,8 @@ public: | |||
|     void show_info_line(Action action, std::string preset_name = ""); | ||||
|     void update_config(Action action); | ||||
|     void close(Action action); | ||||
|     bool save(PresetCollection* dependent_presets); | ||||
|     // save information about saved presets and their types to names_and_types and show SavePresetDialog to set the names for new presets
 | ||||
|     bool save(PresetCollection* dependent_presets, bool show_save_preset_dialog = true); | ||||
| 
 | ||||
|     bool save_preset() const        { return m_exit_action == Action::Save;     } | ||||
|     bool transfer_changes() const   { return m_exit_action == Action::Transfer; } | ||||
|  | @ -284,8 +298,10 @@ public: | |||
|     // short version of the previous function, for the case, when just one preset is modified
 | ||||
|     std::string get_preset_name() { return names_and_types[0].first; } | ||||
| 
 | ||||
|     std::vector<std::string> get_unselected_options(Preset::Type type)  { return m_tree->unselected_options(type); } | ||||
|     std::vector<std::string> get_unselected_options(Preset::Type type)  { return m_tree->options(type, false); } | ||||
|     std::vector<std::string> get_selected_options  (Preset::Type type)  { return m_tree->options(type, true); } | ||||
|     std::vector<std::string> get_selected_options()                     { return m_tree->selected_options(); } | ||||
|     bool                     has_unselected_options()                   { return m_tree->has_unselected_options(); } | ||||
| 
 | ||||
| protected: | ||||
|     void on_dpi_changed(const wxRect& suggested_rect) override; | ||||
|  |  | |||
|  | @ -720,8 +720,13 @@ void PresetUpdater::slic3r_update_notify() | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void reload_configs_update_gui() | ||||
| static bool reload_configs_update_gui() | ||||
| { | ||||
| 	wxString header = _L("Configuration Updates causes a lost of preset modification.\n" | ||||
| 						 "So, check unsaved changes and save them if necessary."); | ||||
| 	if (!GUI::wxGetApp().check_and_save_current_preset_changes(_L("Updater is processing"), header, false )) | ||||
| 		return false; | ||||
| 
 | ||||
| 	// Reload global configuration
 | ||||
| 	auto* app_config = GUI::wxGetApp().app_config; | ||||
| 	// System profiles should not trigger any substitutions, user profiles may trigger substitutions, but these substitutions
 | ||||
|  | @ -730,7 +735,8 @@ static void reload_configs_update_gui() | |||
| 	GUI::wxGetApp().preset_bundle->load_presets(*app_config, ForwardCompatibilitySubstitutionRule::EnableSilentDisableSystem); | ||||
| 	GUI::wxGetApp().load_current_presets(); | ||||
| 	GUI::wxGetApp().plater()->set_bed_shape(); | ||||
| 	GUI::wxGetApp().update_wizard_from_config(); | ||||
| 
 | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver& old_slic3r_version, UpdateParams params) const | ||||
|  | @ -803,9 +809,9 @@ PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver& old_slic3 | |||
| 			const auto res = dlg.ShowModal(); | ||||
| 			if (res == wxID_OK) { | ||||
| 				BOOST_LOG_TRIVIAL(info) << "User wants to update..."; | ||||
| 				if (! p->perform_updates(std::move(updates))) | ||||
| 				if (! p->perform_updates(std::move(updates)) || | ||||
| 					! reload_configs_update_gui()) | ||||
| 					return R_INCOMPAT_EXIT; | ||||
| 				reload_configs_update_gui(); | ||||
| 				return R_UPDATE_INSTALLED; | ||||
| 			} | ||||
| 			else { | ||||
|  | @ -833,9 +839,9 @@ PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver& old_slic3 | |||
| 			const auto res = dlg.ShowModal(); | ||||
| 			if (res == wxID_OK) { | ||||
| 				BOOST_LOG_TRIVIAL(debug) << "User agreed to perform the update"; | ||||
| 				if (! p->perform_updates(std::move(updates))) | ||||
| 				if (! p->perform_updates(std::move(updates)) || | ||||
| 					! reload_configs_update_gui()) | ||||
| 					return R_ALL_CANCELED; | ||||
| 				reload_configs_update_gui(); | ||||
| 				return R_UPDATE_INSTALLED; | ||||
| 			} | ||||
| 			else { | ||||
|  | @ -886,8 +892,8 @@ void PresetUpdater::on_update_notification_confirm() | |||
| 	const auto res = dlg.ShowModal(); | ||||
| 	if (res == wxID_OK) { | ||||
| 		BOOST_LOG_TRIVIAL(debug) << "User agreed to perform the update"; | ||||
| 		if (p->perform_updates(std::move(p->waiting_updates))) { | ||||
| 			reload_configs_update_gui(); | ||||
| 		if (p->perform_updates(std::move(p->waiting_updates)) && | ||||
| 			reload_configs_update_gui()) { | ||||
| 			p->has_waiting_updates = false; | ||||
| 		} | ||||
| 	} | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Oleksandra Yushchenko
						Oleksandra Yushchenko