Check unsaved changes ()

* Check Unsaved changes (partially related to )
 + Allow create new project when Plater is empty, but some of presets are modified (related to )
 + 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:
Oleksandra Yushchenko 2021-09-22 12:44:13 +02:00 committed by GitHub
parent 846b868215
commit 8f064dd155
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 462 additions and 178 deletions
src/slic3r/GUI

View file

@ -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();