diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index f5c8235ed..09cd38468 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -1097,14 +1097,6 @@ size_t PresetCollection::update_compatible_internal(const PresetWithVendorProfil return m_idx_selected; } -// Save the preset under a new name. If the name is different from the old one, -// a new preset is stored into the list of presets. -// All presets are marked as not modified and the new preset is activated. -//void PresetCollection::save_current_preset(const std::string &new_name); - -// Delete the current preset, activate the first visible preset. -//void PresetCollection::delete_current_preset(); - // Update a dirty flag of the current preset // Return true if the dirty flag changed. bool PresetCollection::update_dirty() diff --git a/src/libslic3r/PresetBundle.hpp b/src/libslic3r/PresetBundle.hpp index c22599e38..e5e49fb47 100644 --- a/src/libslic3r/PresetBundle.hpp +++ b/src/libslic3r/PresetBundle.hpp @@ -143,7 +143,7 @@ public: const std::string& get_preset_name_by_alias(const Preset::Type& preset_type, const std::string& alias) const; - // Save current preset of a required type under a new name. If the name is different from the old one, + // Save current preset of a provided type under a new name. If the name is different from the old one, // Unselected option would be reverted to the beginning values void save_changes_for_preset(const std::string& new_name, Preset::Type type, const std::vector& unselected_options); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index e2aa6fb7b..5953c5660 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -23,6 +23,7 @@ #include "slic3r/GUI/3DBed.hpp" #include "slic3r/GUI/Plater.hpp" #include "slic3r/GUI/MainFrame.hpp" +#include "slic3r/Utils/UndoRedo.hpp" #include "GUI_App.hpp" #include "GUI_ObjectList.hpp" @@ -6427,7 +6428,7 @@ void GLCanvas3D::_update_selection_from_hover() // the selection is going to be modified (Add) if (!contains_all) { - wxGetApp().plater()->take_snapshot(_(L("Selection-Add from rectangle"))); + wxGetApp().plater()->take_snapshot(_(L("Selection-Add from rectangle")), UndoRedo::SnapshotType::Selection); selection_changed = true; } } @@ -6442,7 +6443,7 @@ void GLCanvas3D::_update_selection_from_hover() // the selection is going to be modified (Remove) if (contains_any) { - wxGetApp().plater()->take_snapshot(_(L("Selection-Remove from rectangle"))); + wxGetApp().plater()->take_snapshot(_(L("Selection-Remove from rectangle")), UndoRedo::SnapshotType::Selection); selection_changed = true; } } diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 44f04ef5f..b94110427 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -10,6 +10,7 @@ #include "BitmapComboBox.hpp" #include "GalleryDialog.hpp" #include "MainFrame.hpp" +#include "slic3r/Utils/UndoRedo.hpp" #include "OptionsGroup.hpp" #include "Tab.hpp" @@ -3469,7 +3470,7 @@ void ObjectList::update_selections_on_canvas() volume_idxs = selection.get_missing_volume_idxs_from(volume_idxs); if (volume_idxs.size() > 0) { - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Selection-Remove from list"))); + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Selection-Remove from list")), UndoRedo::SnapshotType::Selection); selection.remove_volumes(mode, volume_idxs); } } @@ -3481,7 +3482,7 @@ void ObjectList::update_selections_on_canvas() // OR there is no single selection if (selection.get_mode() == mode || !single_selection) volume_idxs = selection.get_unselected_volume_idxs_from(volume_idxs); - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Selection-Add from list"))); + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Selection-Add from list")), UndoRedo::SnapshotType::Selection); selection.add_volumes(mode, volume_idxs, single_selection); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp index aac4d6ff7..be8fc331d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp @@ -8,6 +8,7 @@ #include "slic3r/GUI/GUI_App.hpp" #include "slic3r/GUI/Camera.hpp" #include "slic3r/GUI/Plater.hpp" +#include "slic3r/Utils/UndoRedo.hpp" #include "libslic3r/Model.hpp" #include "libslic3r/PresetBundle.hpp" #include "libslic3r/TriangleMesh.hpp" @@ -42,14 +43,14 @@ void GLGizmoPainterBase::activate_internal_undo_redo_stack(bool activate) if (activate && !m_internal_stack_active) { if (std::string str = this->get_gizmo_entering_text(); last_snapshot_name != str) - Plater::TakeSnapshot(plater, str); + Plater::TakeSnapshot(plater, str, UndoRedo::SnapshotType::EnteringGizmo); plater->enter_gizmos_stack(); m_internal_stack_active = true; } if (!activate && m_internal_stack_active) { plater->leave_gizmos_stack(); if (std::string str = this->get_gizmo_leaving_text(); last_snapshot_name != str) - Plater::TakeSnapshot(plater, str); + Plater::TakeSnapshot(plater, str, UndoRedo::SnapshotType::LeavingGizmoWithAction); m_internal_stack_active = false; } } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index a50c503b9..b7a6d89fa 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -4,6 +4,7 @@ #include "slic3r/GUI/Camera.hpp" #include "slic3r/GUI/Gizmos/GLGizmosCommon.hpp" #include "slic3r/GUI/MainFrame.hpp" +#include "slic3r/Utils/UndoRedo.hpp" #include diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 6404dbb6d..6cef0933a 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1530,25 +1530,25 @@ struct Plater::priv void arrange() { - m->take_snapshot(_(L("Arrange"))); + m->take_snapshot(_L("Arrange")); start(m_arrange_id); } void fill_bed() { - m->take_snapshot(_(L("Fill bed"))); + m->take_snapshot(_L("Fill bed")); start(m_fill_bed_id); } void optimize_rotation() { - m->take_snapshot(_(L("Optimize Rotation"))); + m->take_snapshot(_L("Optimize Rotation")); start(m_rotoptimize_id); } void import_sla_arch() { - m->take_snapshot(_(L("Import SLA archive"))); + m->take_snapshot(_L("Import SLA archive")); start(m_sla_import_id); } @@ -1677,8 +1677,9 @@ struct Plater::priv void enter_gizmos_stack(); void leave_gizmos_stack(); - void take_snapshot(const std::string& snapshot_name); - void take_snapshot(const wxString& snapshot_name) { this->take_snapshot(std::string(snapshot_name.ToUTF8().data())); } + void take_snapshot(const std::string& snapshot_name, UndoRedo::SnapshotType snapshot_type = UndoRedo::SnapshotType::Action); + void take_snapshot(const wxString& snapshot_name, UndoRedo::SnapshotType snapshot_type = UndoRedo::SnapshotType::Action) + { this->take_snapshot(std::string(snapshot_name.ToUTF8().data()), snapshot_type); } int get_active_snapshot_index(); void undo(); @@ -2062,7 +2063,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) } // Initialize the Undo / Redo stack with a first snapshot. - this->take_snapshot(_L("New Project")); + this->take_snapshot(_L("New Project"), UndoRedo::SnapshotType::ProjectSeparator); this->q->Bind(EVT_LOAD_MODEL_OTHER_INSTANCE, [this](LoadFromOtherInstanceEvent& evt) { BOOST_LOG_TRIVIAL(trace) << "Received load from other instance event."; @@ -2818,7 +2819,7 @@ void Plater::priv::delete_all_objects_from_model() void Plater::priv::reset() { - Plater::TakeSnapshot snapshot(q, _L("Reset Project")); + Plater::TakeSnapshot snapshot(q, _L("Reset Project"), UndoRedo::SnapshotType::ProjectSeparator); clear_warnings(); @@ -4660,12 +4661,13 @@ int Plater::priv::get_active_snapshot_index() return it - ss_stack.begin(); } -void Plater::priv::take_snapshot(const std::string& snapshot_name) +void Plater::priv::take_snapshot(const std::string& snapshot_name, const UndoRedo::SnapshotType snapshot_type) { if (m_prevent_snapshots > 0) return; assert(m_prevent_snapshots >= 0); UndoRedo::SnapshotData snapshot_data; + snapshot_data.snapshot_type = snapshot_type; snapshot_data.printer_technology = this->printer_technology; if (this->view3D->is_layers_editing_enabled()) snapshot_data.flags |= UndoRedo::SnapshotData::VARIABLE_LAYER_EDITING_ACTIVE; @@ -4951,7 +4953,7 @@ void Plater::new_project() } p->select_view_3D("3D"); - take_snapshot(_L("New Project")); + take_snapshot(_L("New Project"), UndoRedo::SnapshotType::ProjectSeparator); Plater::SuppressSnapshots suppress(this); reset(); reset_project_dirty_initial_presets(); @@ -4976,7 +4978,7 @@ void Plater::load_project(const wxString& filename) return; // Take the Undo / Redo snapshot. - Plater::TakeSnapshot snapshot(this, _L("Load Project") + ": " + wxString::FromUTF8(into_path(filename).stem().string().c_str())); + Plater::TakeSnapshot snapshot(this, _L("Load Project") + ": " + wxString::FromUTF8(into_path(filename).stem().string().c_str()), UndoRedo::SnapshotType::ProjectSeparator); p->reset(); @@ -5992,6 +5994,8 @@ void Plater::eject_drive() void Plater::take_snapshot(const std::string &snapshot_name) { p->take_snapshot(snapshot_name); } void Plater::take_snapshot(const wxString &snapshot_name) { p->take_snapshot(snapshot_name); } +void Plater::take_snapshot(const std::string &snapshot_name, UndoRedo::SnapshotType snapshot_type) { p->take_snapshot(snapshot_name, snapshot_type); } +void Plater::take_snapshot(const wxString &snapshot_name, UndoRedo::SnapshotType snapshot_type) { p->take_snapshot(snapshot_name, snapshot_type); } void Plater::suppress_snapshots() { p->suppress_snapshots(); } void Plater::allow_snapshots() { p->allow_snapshots(); } void Plater::undo() { p->undo(); } diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index c03681a34..8a8255514 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -37,6 +37,7 @@ using ModelInstancePtrs = std::vector; namespace UndoRedo { class Stack; + enum class SnapshotType : unsigned char; struct Snapshot; } @@ -240,6 +241,9 @@ public: void take_snapshot(const std::string &snapshot_name); void take_snapshot(const wxString &snapshot_name); + void take_snapshot(const std::string &snapshot_name, UndoRedo::SnapshotType snapshot_type); + void take_snapshot(const wxString &snapshot_name, UndoRedo::SnapshotType snapshot_type); + void undo(); void redo(); void undo_to(int selection); @@ -391,6 +395,12 @@ public: m_plater->take_snapshot(snapshot_name); m_plater->suppress_snapshots(); } + TakeSnapshot(Plater *plater, const wxString &snapshot_name, UndoRedo::SnapshotType snapshot_type) : m_plater(plater) + { + m_plater->take_snapshot(snapshot_name, snapshot_type); + m_plater->suppress_snapshots(); + } + ~TakeSnapshot() { m_plater->allow_snapshots(); diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 4a9c7cd56..3ab9b7bdc 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -10,6 +10,7 @@ #include "Gizmos/GLGizmoBase.hpp" #include "Camera.hpp" #include "Plater.hpp" +#include "slic3r/Utils/UndoRedo.hpp" #include "libslic3r/LocalesUtils.hpp" #include "libslic3r/Model.hpp" @@ -162,7 +163,7 @@ void Selection::add(unsigned int volume_idx, bool as_single_selection, bool chec needs_reset |= is_any_modifier() && !volume->is_modifier; if (!already_contained || needs_reset) { - wxGetApp().plater()->take_snapshot(_L("Selection-Add")); + wxGetApp().plater()->take_snapshot(_L("Selection-Add"), UndoRedo::SnapshotType::Selection); if (needs_reset) clear(); @@ -203,7 +204,7 @@ void Selection::remove(unsigned int volume_idx) if (!contains_volume(volume_idx)) return; - wxGetApp().plater()->take_snapshot(_L("Selection-Remove")); + wxGetApp().plater()->take_snapshot(_L("Selection-Remove"), UndoRedo::SnapshotType::Selection); GLVolume* volume = (*m_volumes)[volume_idx]; @@ -235,7 +236,7 @@ void Selection::add_object(unsigned int object_idx, bool as_single_selection) (as_single_selection && matches(volume_idxs))) return; - wxGetApp().plater()->take_snapshot(_L("Selection-Add Object")); + wxGetApp().plater()->take_snapshot(_L("Selection-Add Object"), UndoRedo::SnapshotType::Selection); // resets the current list if needed if (as_single_selection) @@ -254,7 +255,7 @@ void Selection::remove_object(unsigned int object_idx) if (!m_valid) return; - wxGetApp().plater()->take_snapshot(_L("Selection-Remove Object")); + wxGetApp().plater()->take_snapshot(_L("Selection-Remove Object"), UndoRedo::SnapshotType::Selection); do_remove_object(object_idx); @@ -272,7 +273,7 @@ void Selection::add_instance(unsigned int object_idx, unsigned int instance_idx, (as_single_selection && matches(volume_idxs))) return; - wxGetApp().plater()->take_snapshot(_L("Selection-Add Instance")); + wxGetApp().plater()->take_snapshot(_L("Selection-Add Instance"), UndoRedo::SnapshotType::Selection); // resets the current list if needed if (as_single_selection) @@ -291,7 +292,7 @@ void Selection::remove_instance(unsigned int object_idx, unsigned int instance_i if (!m_valid) return; - wxGetApp().plater()->take_snapshot(_L("Selection-Remove Instance")); + wxGetApp().plater()->take_snapshot(_L("Selection-Remove Instance"), UndoRedo::SnapshotType::Selection); do_remove_instance(object_idx, instance_idx); @@ -388,7 +389,7 @@ void Selection::add_all() if ((unsigned int)m_list.size() == count) return; - wxGetApp().plater()->take_snapshot(_(L("Selection-Add All"))); + wxGetApp().plater()->take_snapshot(_(L("Selection-Add All")), UndoRedo::SnapshotType::Selection); m_mode = Instance; clear(); @@ -413,7 +414,7 @@ void Selection::remove_all() // Not taking the snapshot with non-empty Redo stack will likely be more confusing than losing the Redo stack. // Let's wait for user feedback. // if (!wxGetApp().plater()->can_redo()) - wxGetApp().plater()->take_snapshot(_L("Selection-Remove All")); + wxGetApp().plater()->take_snapshot(_L("Selection-Remove All"), UndoRedo::SnapshotType::Selection); m_mode = Instance; clear(); diff --git a/src/slic3r/Utils/UndoRedo.cpp b/src/slic3r/Utils/UndoRedo.cpp index a19788ab9..f5d418fa7 100644 --- a/src/slic3r/Utils/UndoRedo.cpp +++ b/src/slic3r/Utils/UndoRedo.cpp @@ -556,6 +556,12 @@ public: // Snapshot history (names with timestamps). const std::vector& snapshots() const { return m_snapshots; } + const Snapshot& snapshot(size_t time) const { + const auto it = std::lower_bound(m_snapshots.cbegin(), m_snapshots.cend(), UndoRedo::Snapshot(time)); + assert(it != m_snapshots.end() && it->timestamp == time); + return *it; + } + // Timestamp of the active snapshot. size_t active_snapshot_time() const { return m_active_snapshot_time; } bool temp_snapshot_active() const { return m_snapshots.back().timestamp == m_active_snapshot_time && ! m_snapshots.back().is_topmost_captured(); } @@ -1097,6 +1103,7 @@ bool Stack::redo(Slic3r::Model& model, Slic3r::GUI::GLGizmosManager& gizmos, siz const Selection& Stack::selection_deserialized() const { return pimpl->selection_deserialized(); } const std::vector& Stack::snapshots() const { return pimpl->snapshots(); } +const Snapshot& Stack::snapshot(size_t time) const { return pimpl->snapshot(time); } size_t Stack::active_snapshot_time() const { return pimpl->active_snapshot_time(); } bool Stack::temp_snapshot_active() const { return pimpl->temp_snapshot_active(); } diff --git a/src/slic3r/Utils/UndoRedo.hpp b/src/slic3r/Utils/UndoRedo.hpp index e120c4f79..d29c0e979 100644 --- a/src/slic3r/Utils/UndoRedo.hpp +++ b/src/slic3r/Utils/UndoRedo.hpp @@ -24,6 +24,23 @@ namespace GUI { namespace UndoRedo { +enum class SnapshotType : unsigned char { + // Some action modifying project state. + Action, + // Selection change at the Plater. + Selection, + // New project, Reset project, Load project ... + ProjectSeparator, + // Entering a Gizmo, which opens a secondary Undo / Redo stack. + EnteringGizmo, + // Leaving a Gizmo, which closes a secondary Undo / Redo stack. + // No action modifying a project state was done between EnteringGizmo / LeavingGizmo. + LeavingGizmoNoAction, + // Leaving a Gizmo, which closes a secondary Undo / Redo stack. + // Some action modifying a project state was done between EnteringGizmo / LeavingGizmo. + LeavingGizmoWithAction, +}; + // Data structure to be stored with each snapshot. // Storing short data (bit masks, ints) with each snapshot instead of being serialized into the Undo / Redo stack // is likely cheaper in term of both the runtime and memory allocation. @@ -34,6 +51,7 @@ struct SnapshotData // Constructor is defined in .cpp due to the forward declaration of enum PrinterTechnology. SnapshotData(); + SnapshotType snapshot_type; PrinterTechnology printer_technology; // Bitmap of Flags (see the Flags enum). unsigned int flags; @@ -122,10 +140,13 @@ public: // There is one additional snapshot taken at the very end, which indicates the current unnamed state. const std::vector& snapshots() const; + const Snapshot& snapshot(size_t time) const; + // Timestamp of the active snapshot. One of the snapshots of this->snapshots() shall have Snapshot::timestamp equal to this->active_snapshot_time(). - // The snapshot time indicates start of an operation, which is finished at the time of the following snapshot, therefore - // the active snapshot is the successive snapshot. The same logic applies to the time_to_load parameter of undo() and redo() operations. + // The active snapshot may be a special placeholder "@@@ Topmost @@@" indicating an uncaptured current state, + // or the active snapshot may be an active state to which the application state was undoed or redoed. size_t active_snapshot_time() const; + const Snapshot& active_snapshot() const { return this->snapshot(this->active_snapshot_time()); } // Temporary snapshot is active if the topmost snapshot is active and it has not been captured yet. // In that case the Undo action will capture the last snapshot. bool temp_snapshot_active() const;