Project dirty state manager -> presets dirty state

This commit is contained in:
enricoturri1966 2021-04-06 16:29:05 +02:00
parent 5d4b7c03b6
commit edbb1d0f69
11 changed files with 206 additions and 18 deletions

View File

@ -617,11 +617,17 @@ const std::vector<std::string>& Preset::sla_printer_options()
PresetCollection::PresetCollection(Preset::Type type, const std::vector<std::string> &keys, const Slic3r::StaticPrintConfig &defaults, const std::string &default_name) :
m_type(type),
m_edited_preset(type, "", false),
#if ENABLE_PROJECT_DIRTY_STATE
m_saved_preset(type, "", false),
#endif // ENABLE_PROJECT_DIRTY_STATE
m_idx_selected(0)
{
// Insert just the default preset.
this->add_default_preset(keys, defaults, default_name);
m_edited_preset.config.apply(m_presets.front().config);
#if ENABLE_PROJECT_DIRTY_STATE
update_saved_preset_from_current_preset();
#endif // ENABLE_PROJECT_DIRTY_STATE
}
void PresetCollection::reset(bool delete_files)
@ -798,6 +804,9 @@ std::pair<Preset*, bool> PresetCollection::load_external_preset(
// The source config may contain keys from many possible preset types. Just copy those that relate to this preset.
this->get_edited_preset().config.apply_only(combined_config, keys, true);
this->update_dirty();
#if ENABLE_PROJECT_DIRTY_STATE
update_saved_preset_from_current_preset();
#endif // ENABLE_PROJECT_DIRTY_STATE
assert(this->get_edited_preset().is_dirty);
return std::make_pair(&(*it), this->get_edited_preset().is_dirty);
}
@ -1208,6 +1217,9 @@ Preset& PresetCollection::select_preset(size_t idx)
idx = first_visible_idx();
m_idx_selected = idx;
m_edited_preset = m_presets[idx];
#if ENABLE_PROJECT_DIRTY_STATE
update_saved_preset_from_current_preset();
#endif // ENABLE_PROJECT_DIRTY_STATE
bool default_visible = ! m_default_suppressed || m_idx_selected < m_num_default_presets;
for (size_t i = 0; i < m_num_default_presets; ++i)
m_presets[i].is_visible = default_visible;

View File

@ -346,6 +346,11 @@ public:
Preset& get_edited_preset() { return m_edited_preset; }
const Preset& get_edited_preset() const { return m_edited_preset; }
#if ENABLE_PROJECT_DIRTY_STATE
// Return the last saved preset.
const Preset& get_saved_preset() const { return m_saved_preset; }
#endif // ENABLE_PROJECT_DIRTY_STATE
// Return vendor of the first parent profile, for which the vendor is defined, or null if such profile does not exist.
PresetWithVendorProfile get_preset_with_vendor_profile(const Preset &preset) const;
PresetWithVendorProfile get_edited_preset_with_vendor_profile() const { return this->get_preset_with_vendor_profile(this->get_edited_preset()); }
@ -365,7 +370,15 @@ public:
// Return a preset by an index. If the preset is active, a temporary copy is returned.
Preset& preset(size_t idx) { return (idx == m_idx_selected) ? m_edited_preset : m_presets[idx]; }
const Preset& preset(size_t idx) const { return const_cast<PresetCollection*>(this)->preset(idx); }
#if ENABLE_PROJECT_DIRTY_STATE
void discard_current_changes() {
m_presets[m_idx_selected].reset_dirty();
m_edited_preset = m_presets[m_idx_selected];
update_saved_preset_from_current_preset();
}
#else
void discard_current_changes() { m_presets[m_idx_selected].reset_dirty(); m_edited_preset = m_presets[m_idx_selected]; }
#endif // ENABLE_PROJECT_DIRTY_STATE
// Return a preset by its name. If the preset is active, a temporary copy is returned.
// If a preset is not found by its name, null is returned.
@ -440,6 +453,16 @@ public:
std::vector<std::string> current_different_from_parent_options(const bool deep_compare = false) const
{ return dirty_options(&this->get_edited_preset(), this->get_selected_preset_parent(), deep_compare); }
#if ENABLE_PROJECT_DIRTY_STATE
// Compare the content of get_saved_preset() with get_edited_preset() configs, return true if they differ.
bool saved_is_dirty() const { return !this->saved_dirty_options().empty(); }
// Compare the content of get_saved_preset() with get_edited_preset() configs, return the list of keys where they differ.
std::vector<std::string> saved_dirty_options(const bool deep_compare = false) const
{ return dirty_options(&this->get_edited_preset(), &this->get_saved_preset(), deep_compare); }
// Copy edited preset into saved preset.
void update_saved_preset_from_current_preset() { m_saved_preset = m_edited_preset; }
#endif // ENABLE_PROJECT_DIRTY_STATE
// Return a sorted list of system preset names.
// Used for validating the "inherits" flag when importing user's config bundles.
// Returns names of all system presets including the former names of these presets.
@ -527,6 +550,11 @@ private:
std::map<std::string, std::string> m_map_system_profile_renamed;
// Initially this preset contains a copy of the selected preset. Later on, this copy may be modified by the user.
Preset m_edited_preset;
#if ENABLE_PROJECT_DIRTY_STATE
// Contains a copy of the last saved selected preset.
Preset m_saved_preset;
#endif // ENABLE_PROJECT_DIRTY_STATE
// Selected preset.
size_t m_idx_selected;
// Is the "- default -" preset suppressed?

View File

@ -907,7 +907,7 @@ bool GUI_App::on_init_inner()
#if ENABLE_PROJECT_DIRTY_STATE
if (plater_ != nullptr) {
// plater_->reset_project_initial_presets();
plater_->reset_project_dirty_initial_presets();
plater_->update_project_dirty_from_presets();
}
#endif // ENABLE_PROJECT_DIRTY_STATE
@ -1673,7 +1673,11 @@ void GUI_App::add_config_menu(wxMenuBar *menu)
break;
case ConfigMenuTakeSnapshot:
// Take a configuration snapshot.
#if ENABLE_PROJECT_DIRTY_STATE
if (check_and_save_current_preset_changes()) {
#else
if (check_unsaved_changes()) {
#endif // ENABLE_PROJECT_DIRTY_STATE
wxTextEntryDialog dlg(nullptr, _L("Taking configuration snapshot"), _L("Snapshot name"));
// set current normal font for dialog children,
@ -1688,7 +1692,11 @@ void GUI_App::add_config_menu(wxMenuBar *menu)
}
break;
case ConfigMenuSnapshots:
#if ENABLE_PROJECT_DIRTY_STATE
if (check_and_save_current_preset_changes()) {
#else
if (check_unsaved_changes()) {
#endif // ENABLE_PROJECT_DIRTY_STATE
std::string on_snapshot;
if (Config::SnapshotDB::singleton().is_on_snapshot(*app_config))
on_snapshot = app_config->get("on_snapshot");
@ -1789,8 +1797,57 @@ void GUI_App::add_config_menu(wxMenuBar *menu)
menu->Append(local_menu, _L("&Configuration"));
}
#if ENABLE_PROJECT_DIRTY_STATE
bool GUI_App::has_unsaved_preset_changes() const
{
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->saved_preset_is_dirty())
return true;
}
return false;
}
bool GUI_App::has_current_preset_changes() const
{
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())
return true;
}
return false;
}
void GUI_App::update_saved_preset_from_current_preset()
{
PrinterTechnology printer_technology = preset_bundle->printers.get_edited_preset().printer_technology();
for (Tab* tab : tabs_list) {
if (tab->supports_printer_technology(printer_technology))
tab->update_saved_preset_from_current_preset();
}
}
std::vector<std::pair<unsigned int, std::string>> GUI_App::get_selected_presets() const
{
std::vector<std::pair<unsigned int, std::string>> ret;
PrinterTechnology printer_technology = preset_bundle->printers.get_edited_preset().printer_technology();
for (Tab* tab : tabs_list) {
if (tab->supports_printer_technology(printer_technology)) {
const PresetCollection* presets = tab->get_presets();
ret.push_back({ static_cast<unsigned int>(presets->type()), presets->get_selected_preset_name() });
}
}
return ret;
}
#endif // ENABLE_PROJECT_DIRTY_STATE
// 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.
#if ENABLE_PROJECT_DIRTY_STATE
bool GUI_App::check_and_save_current_preset_changes(const wxString& header)
{
if (this->plater()->model().objects.empty() && has_current_preset_changes()) {
#else
bool GUI_App::check_unsaved_changes(const wxString &header)
{
PrinterTechnology printer_technology = preset_bundle->printers.get_edited_preset().printer_technology();
@ -1802,8 +1859,8 @@ bool GUI_App::check_unsaved_changes(const wxString &header)
break;
}
if (has_unsaved_changes)
{
if (has_unsaved_changes) {
#endif // ENABLE_PROJECT_DIRTY_STATE
UnsavedChangesDialog dlg(header);
if (wxGetApp().app_config->get("default_action_on_close_application") == "none" && dlg.ShowModal() == wxID_CANCEL)
return false;

View File

@ -209,7 +209,15 @@ public:
void update_mode();
void add_config_menu(wxMenuBar *menu);
bool check_unsaved_changes(const wxString &header = wxString());
#if ENABLE_PROJECT_DIRTY_STATE
bool has_unsaved_preset_changes() const;
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());
#else
bool check_unsaved_changes(const wxString& header = wxString());
#endif // ENABLE_PROJECT_DIRTY_STATE
bool check_print_host_queue();
bool checked_tab(Tab* tab);
void load_current_presets(bool check_printer_presets = true);

View File

@ -209,9 +209,11 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S
#if ENABLE_PROJECT_DIRTY_STATE
if (m_plater != nullptr)
m_plater->save_project_if_dirty();
#endif // ENABLE_PROJECT_DIRTY_STATE
if (event.CanVeto() && !wxGetApp().check_and_save_current_preset_changes()) {
#else
if (event.CanVeto() && !wxGetApp().check_unsaved_changes()) {
#endif // ENABLE_PROJECT_DIRTY_STATE
event.Veto();
return;
}
@ -1559,7 +1561,11 @@ void MainFrame::export_config()
// Load a config file containing a Print, Filament & Printer preset.
void MainFrame::load_config_file()
{
#if ENABLE_PROJECT_DIRTY_STATE
if (!wxGetApp().check_and_save_current_preset_changes())
#else
if (!wxGetApp().check_unsaved_changes())
#endif // ENABLE_PROJECT_DIRTY_STATE
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(),
@ -1588,7 +1594,11 @@ bool MainFrame::load_config_file(const std::string &path)
void MainFrame::export_configbundle(bool export_physical_printers /*= false*/)
{
#if ENABLE_PROJECT_DIRTY_STATE
if (!wxGetApp().check_and_save_current_preset_changes())
#else
if (!wxGetApp().check_unsaved_changes())
#endif // ENABLE_PROJECT_DIRTY_STATE
return;
// validate current configuration in case it's dirty
auto err = wxGetApp().preset_bundle->full_config().validate();
@ -1620,7 +1630,11 @@ 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 ENABLE_PROJECT_DIRTY_STATE
if (!wxGetApp().check_and_save_current_preset_changes())
#else
if (!wxGetApp().check_unsaved_changes())
#endif // ENABLE_PROJECT_DIRTY_STATE
return;
if (file.IsEmpty()) {
wxFileDialog dlg(this, _L("Select configuration to load:"),

View File

@ -1525,14 +1525,18 @@ struct Plater::priv
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);
if (dlg.ShowModal() == wxID_CANCEL)
return false;
int res = dlg.ShowModal();
if (res == wxID_YES)
mainframe->save_project_as(wxGetApp().plater()->get_project_filename());
else if (res == wxID_CANCEL)
return false;
}
}
return true;
}
void reset_project_dirty_after_save() { dirty_state.reset_after_save(); }
void reset_project_dirty_initial_presets() { dirty_state.reset_initial_presets(); }
#if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
void render_project_state_debug_window() const { dirty_state.render_debug_window(); }
#endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
@ -4284,8 +4288,13 @@ 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 ENABLE_PROJECT_DIRTY_STATE
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)))
#else
if (! wxGetApp().check_unsaved_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)))
#endif // ENABLE_PROJECT_DIRTY_STATE
// Don't switch the profiles.
return;
}
@ -4466,6 +4475,7 @@ bool Plater::is_project_dirty() const { return p->is_project_dirty(); }
void Plater::update_project_dirty_from_presets() { p->update_project_dirty_from_presets(); }
bool Plater::save_project_if_dirty() { return p->save_project_if_dirty(); }
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
void Plater::render_project_state_debug_window() const { p->render_project_state_debug_window(); }
#endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
@ -4490,7 +4500,7 @@ void Plater::new_project()
take_snapshot(_L("New Project"));
Plater::SuppressSnapshots suppress(this);
reset();
// reset_project_initial_presets();
reset_project_dirty_initial_presets();
update_project_dirty_from_presets();
#else
wxPostEvent(p->view3D->get_wxglcanvas(), SimpleEvent(EVT_GLTOOLBAR_DELETE_ALL));
@ -4530,7 +4540,7 @@ void Plater::load_project(const wxString& filename)
#if ENABLE_PROJECT_DIRTY_STATE
if (!res.empty()) {
p->set_project_filename(filename);
// reset_project_initial_presets();
reset_project_dirty_initial_presets();
update_project_dirty_from_presets();
}
#else

View File

@ -135,6 +135,7 @@ public:
void update_project_dirty_from_presets();
bool save_project_if_dirty();
void reset_project_dirty_after_save();
void reset_project_dirty_initial_presets();
#if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
void render_project_state_debug_window() const;
#endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW

View File

@ -19,15 +19,32 @@ void ProjectDirtyStateManager::update_from_undo_redo_stack(const Slic3r::UndoRed
void ProjectDirtyStateManager::update_from_presets()
{
m_state.presets = false;
std::vector<std::pair<unsigned int, std::string>> selected_presets = wxGetApp().get_selected_presets();
for (const auto& [type, name] : selected_presets) {
m_state.presets |= !m_initial_presets[type].empty() && m_initial_presets[type] != name;
}
m_state.presets |= wxGetApp().has_unsaved_preset_changes();
wxGetApp().mainframe->update_title();
}
void ProjectDirtyStateManager::reset_after_save()
{
reset_initial_presets();
m_state.reset();
wxGetApp().mainframe->update_title();
}
void ProjectDirtyStateManager::reset_initial_presets()
{
m_initial_presets = std::array<std::string, Preset::TYPE_COUNT>();
std::vector<std::pair<unsigned int, std::string>> selected_presets = wxGetApp().get_selected_presets();
for (const auto& [type, name] : selected_presets) {
m_initial_presets[type] = name;
}
}
#if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
void ProjectDirtyStateManager::render_debug_window() const
{

View File

@ -1,6 +1,8 @@
#ifndef slic3r_ProjectDirtyStateManager_hpp_
#define slic3r_ProjectDirtyStateManager_hpp_
#include "libslic3r/Preset.hpp"
#if ENABLE_PROJECT_DIRTY_STATE
namespace Slic3r {
@ -25,12 +27,15 @@ class ProjectDirtyStateManager
DirtyState m_state;
// keeps track of initial selected presets
std::array<std::string, Preset::TYPE_COUNT> m_initial_presets;
public:
bool is_dirty() const { return m_state.is_dirty(); }
void update_from_undo_redo_stack(const Slic3r::UndoRedo::Stack& main_stack, const Slic3r::UndoRedo::Stack& active_stack);
void update_from_presets();
void reset_after_save();
void reset_initial_presets();
#if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
void render_debug_window() const;
#endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW

View File

@ -2113,10 +2113,16 @@ wxSizer* Tab::description_line_widget(wxWindow* parent, ogStaticText* *StaticTex
return sizer;
}
#if ENABLE_PROJECT_DIRTY_STATE
bool Tab::saved_preset_is_dirty() const { return m_presets->saved_is_dirty(); }
void Tab::update_saved_preset_from_current_preset() { m_presets->update_saved_preset_from_current_preset(); }
bool Tab::current_preset_is_dirty() const { return m_presets->current_is_dirty(); }
#else
bool Tab::current_preset_is_dirty()
{
return m_presets->current_is_dirty();
}
#endif // ENABLE_PROJECT_DIRTY_STATE
void TabPrinter::build()
{

View File

@ -270,7 +270,11 @@ public:
Preset::Type type() const { return m_type; }
// The tab is already constructed.
bool completed() const { return m_completed; }
#if ENABLE_PROJECT_DIRTY_STATE
virtual bool supports_printer_technology(const PrinterTechnology tech) const = 0;
#else
virtual bool supports_printer_technology(const PrinterTechnology tech) = 0;
#endif // ENABLE_PROJECT_DIRTY_STATE
void create_preset_tab();
void add_scaled_button(wxWindow* parent, ScalableButton** btn, const std::string& icon_name,
@ -333,7 +337,13 @@ public:
Field* get_field(const t_config_option_key &opt_key, Page** selected_page, int opt_index = -1);
void toggle_option(const std::string& opt_key, bool toggle, int opt_index = -1);
wxSizer* description_line_widget(wxWindow* parent, ogStaticText** StaticText, wxString text = wxEmptyString);
#if ENABLE_PROJECT_DIRTY_STATE
bool current_preset_is_dirty() const;
bool saved_preset_is_dirty() const;
void update_saved_preset_from_current_preset();
#else
bool current_preset_is_dirty();
#endif // ENABLE_PROJECT_DIRTY_STATE
DynamicPrintConfig* get_config() { return m_config; }
PresetCollection* get_presets() { return m_presets; }
@ -387,7 +397,11 @@ public:
void toggle_options() override;
void update() override;
void clear_pages() override;
#if ENABLE_PROJECT_DIRTY_STATE
bool supports_printer_technology(const PrinterTechnology tech) const override { return tech == ptFFF; }
#else
bool supports_printer_technology(const PrinterTechnology tech) override { return tech == ptFFF; }
#endif // ENABLE_PROJECT_DIRTY_STATE
private:
ogStaticText* m_recommended_thin_wall_thickness_description_line = nullptr;
@ -417,7 +431,11 @@ public:
void toggle_options() override;
void update() override;
void clear_pages() override;
#if ENABLE_PROJECT_DIRTY_STATE
bool supports_printer_technology(const PrinterTechnology tech) const override { return tech == ptFFF; }
#else
bool supports_printer_technology(const PrinterTechnology tech) override { return tech == ptFFF; }
#endif // ENABLE_PROJECT_DIRTY_STATE
};
class TabPrinter : public Tab
@ -471,7 +489,11 @@ public:
void init_options_list() override;
void msw_rescale() override;
void sys_color_changed() override;
#if ENABLE_PROJECT_DIRTY_STATE
bool supports_printer_technology(const PrinterTechnology /* tech */) const override { return true; }
#else
bool supports_printer_technology(const PrinterTechnology /* tech */) override { return true; }
#endif // ENABLE_PROJECT_DIRTY_STATE
wxSizer* create_bed_shape_widget(wxWindow* parent);
void cache_extruder_cnt();
@ -491,7 +513,11 @@ public:
void toggle_options() override {};
void update() override;
void init_options_list() override;
#if ENABLE_PROJECT_DIRTY_STATE
bool supports_printer_technology(const PrinterTechnology tech) const override { return tech == ptSLA; }
#else
bool supports_printer_technology(const PrinterTechnology tech) override { return tech == ptSLA; }
#endif // ENABLE_PROJECT_DIRTY_STATE
};
class TabSLAPrint : public Tab
@ -510,7 +536,11 @@ public:
void toggle_options() override;
void update() override;
void clear_pages() override;
#if ENABLE_PROJECT_DIRTY_STATE
bool supports_printer_technology(const PrinterTechnology tech) const override { return tech == ptSLA; }
#else
bool supports_printer_technology(const PrinterTechnology tech) override { return tech == ptSLA; }
#endif // ENABLE_PROJECT_DIRTY_STATE
};
} // GUI