2021-04-06 08:00:17 +00:00
|
|
|
#include "libslic3r/libslic3r.h"
|
2021-04-07 10:58:14 +00:00
|
|
|
|
2021-04-06 08:00:17 +00:00
|
|
|
#include "ProjectDirtyStateManager.hpp"
|
|
|
|
#include "ImGuiWrapper.hpp"
|
|
|
|
#include "GUI_App.hpp"
|
2021-04-06 11:17:29 +00:00
|
|
|
#include "MainFrame.hpp"
|
2021-04-07 10:58:14 +00:00
|
|
|
#include "I18N.hpp"
|
|
|
|
#include "Plater.hpp"
|
|
|
|
#include "../Utils/UndoRedo.hpp"
|
|
|
|
|
|
|
|
#include <boost/algorithm/string/predicate.hpp>
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
#include <assert.h>
|
2021-04-06 08:00:17 +00:00
|
|
|
|
|
|
|
#if ENABLE_PROJECT_DIRTY_STATE
|
|
|
|
|
|
|
|
namespace Slic3r {
|
|
|
|
namespace GUI {
|
|
|
|
|
2021-04-07 10:58:14 +00:00
|
|
|
enum class EStackType
|
|
|
|
{
|
|
|
|
Main,
|
|
|
|
Gizmo
|
|
|
|
};
|
|
|
|
|
|
|
|
static const UndoRedo::Snapshot* get_active_snapshot(const UndoRedo::Stack& stack) {
|
|
|
|
const std::vector<UndoRedo::Snapshot>& snapshots = stack.snapshots();
|
|
|
|
const size_t active_snapshot_time = stack.active_snapshot_time();
|
|
|
|
const auto it = std::lower_bound(snapshots.begin(), snapshots.end(), UndoRedo::Snapshot(active_snapshot_time));
|
|
|
|
const int idx = it - snapshots.begin() - 1;
|
2021-04-07 12:26:04 +00:00
|
|
|
const Slic3r::UndoRedo::Snapshot* ret = (0 <= idx && (size_t)idx < snapshots.size() - 1) ?
|
2021-04-07 10:58:14 +00:00
|
|
|
&snapshots[idx] : nullptr;
|
|
|
|
|
|
|
|
assert(ret != nullptr);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2021-04-15 13:19:03 +00:00
|
|
|
static const UndoRedo::Snapshot* get_last_saveable_snapshot(EStackType type, const UndoRedo::Stack& stack, const ProjectDirtyStateManager::DirtyState::Gizmos& gizmos) {
|
|
|
|
auto is_gizmo_with_modifications = [&gizmos, &stack](const UndoRedo::Snapshot& snapshot) {
|
|
|
|
if (boost::starts_with(snapshot.name, _utf8("Entering"))) {
|
|
|
|
if (gizmos.current)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
std::string topmost_redo;
|
|
|
|
wxGetApp().plater()->undo_redo_topmost_string_getter(false, topmost_redo);
|
|
|
|
if (boost::starts_with(topmost_redo, _utf8("Leaving"))) {
|
|
|
|
const std::vector<UndoRedo::Snapshot>& snapshots = stack.snapshots();
|
|
|
|
const UndoRedo::Snapshot* leaving_snapshot = &(*std::lower_bound(snapshots.begin(), snapshots.end(), UndoRedo::Snapshot(snapshot.timestamp + 1)));
|
|
|
|
if (gizmos.is_used_and_modified(*leaving_snapshot))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
|
|
|
|
auto skip_main = [&gizmos, is_gizmo_with_modifications](const UndoRedo::Snapshot& snapshot) {
|
|
|
|
if (snapshot.name == _utf8("New Project"))
|
|
|
|
return true;
|
|
|
|
else if (snapshot.name == _utf8("Reset Project"))
|
|
|
|
return true;
|
|
|
|
else if (boost::starts_with(snapshot.name, _utf8("Load Project:")))
|
|
|
|
return true;
|
|
|
|
else if (boost::starts_with(snapshot.name, _utf8("Selection")))
|
|
|
|
return true;
|
|
|
|
else if (boost::starts_with(snapshot.name, _utf8("Entering"))) {
|
|
|
|
if (!is_gizmo_with_modifications(snapshot))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else if (boost::starts_with(snapshot.name, _utf8("Leaving"))) {
|
|
|
|
if (!gizmos.is_used_and_modified(snapshot))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
|
|
|
|
auto skip_gizmo = [&gizmos](const UndoRedo::Snapshot& snapshot) {
|
|
|
|
// put here any needed condition to skip the snapshot
|
|
|
|
return false;
|
2021-04-07 10:58:14 +00:00
|
|
|
};
|
|
|
|
|
2021-04-09 06:26:48 +00:00
|
|
|
const UndoRedo::Snapshot* curr = get_active_snapshot(stack);
|
2021-04-07 10:58:14 +00:00
|
|
|
const std::vector<UndoRedo::Snapshot>& snapshots = stack.snapshots();
|
2021-04-09 06:26:48 +00:00
|
|
|
size_t shift = 1;
|
2021-04-15 13:19:03 +00:00
|
|
|
while (curr->timestamp > 0 && ((type == EStackType::Main && skip_main(*curr)) || (type == EStackType::Gizmo && skip_gizmo(*curr)))) {
|
2021-04-09 06:26:48 +00:00
|
|
|
const UndoRedo::Snapshot* temp = curr;
|
|
|
|
curr = &(*std::lower_bound(snapshots.begin(), snapshots.end(), UndoRedo::Snapshot(curr->timestamp - shift)));
|
|
|
|
shift = (curr == temp) ? shift + 1 : 1;
|
2021-04-07 10:58:14 +00:00
|
|
|
}
|
2021-04-15 13:19:03 +00:00
|
|
|
return curr->timestamp > 0 ? curr : nullptr;
|
2021-04-07 10:58:14 +00:00
|
|
|
}
|
|
|
|
|
2021-04-09 06:26:48 +00:00
|
|
|
static std::string extract_gizmo_name(const std::string& s) {
|
|
|
|
static const std::array<std::string, 2> prefixes = { _utf8("Entering"), _utf8("Leaving") };
|
|
|
|
|
|
|
|
std::string ret;
|
|
|
|
for (const std::string& prefix : prefixes) {
|
|
|
|
if (boost::starts_with(s, prefix))
|
|
|
|
ret = s.substr(prefix.length() + 1);
|
|
|
|
|
|
|
|
if (!ret.empty())
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2021-04-15 13:19:03 +00:00
|
|
|
void ProjectDirtyStateManager::DirtyState::Gizmos::add_used(const UndoRedo::Snapshot& snapshot)
|
|
|
|
{
|
|
|
|
const std::string name = extract_gizmo_name(snapshot.name);
|
|
|
|
auto it = used.find(name);
|
|
|
|
if (it == used.end())
|
|
|
|
it = used.insert({ name, { {} } }).first;
|
|
|
|
|
|
|
|
it->second.modified_timestamps.push_back(snapshot.timestamp);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ProjectDirtyStateManager::DirtyState::Gizmos::remove_obsolete_used(const Slic3r::UndoRedo::Stack& main_stack)
|
|
|
|
{
|
|
|
|
const std::vector<UndoRedo::Snapshot>& snapshots = main_stack.snapshots();
|
|
|
|
for (auto& [name, gizmo] : used) {
|
|
|
|
auto it = gizmo.modified_timestamps.begin();
|
|
|
|
while (it != gizmo.modified_timestamps.end()) {
|
|
|
|
size_t timestamp = *it;
|
|
|
|
auto snapshot_it = std::find_if(snapshots.begin(), snapshots.end(), [timestamp](const Slic3r::UndoRedo::Snapshot& snapshot) { return snapshot.timestamp == timestamp; });
|
|
|
|
if (snapshot_it == snapshots.end())
|
|
|
|
it = gizmo.modified_timestamps.erase(it);
|
|
|
|
else
|
|
|
|
++it;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
|
|
|
|
bool ProjectDirtyStateManager::DirtyState::Gizmos::any_used_modified() const
|
|
|
|
{
|
|
|
|
for (auto& [name, gizmo] : used) {
|
|
|
|
if (!gizmo.modified_timestamps.empty())
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
#endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
|
|
|
|
|
|
|
|
bool ProjectDirtyStateManager::DirtyState::Gizmos::is_used_and_modified(const UndoRedo::Snapshot& snapshot) const
|
|
|
|
{
|
|
|
|
for (auto& [name, gizmo] : used) {
|
|
|
|
for (size_t i : gizmo.modified_timestamps) {
|
|
|
|
if (i == snapshot.timestamp)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ProjectDirtyStateManager::update_from_undo_redo_stack(UpdateType type, const Slic3r::UndoRedo::Stack& main_stack, const Slic3r::UndoRedo::Stack& active_stack)
|
2021-04-06 11:17:29 +00:00
|
|
|
{
|
|
|
|
if (!wxGetApp().initialized())
|
|
|
|
return;
|
|
|
|
|
2021-04-07 10:58:14 +00:00
|
|
|
if (&main_stack == &active_stack)
|
2021-04-15 13:19:03 +00:00
|
|
|
update_from_undo_redo_main_stack(type, main_stack);
|
2021-04-07 10:58:14 +00:00
|
|
|
else
|
2021-04-15 13:19:03 +00:00
|
|
|
update_from_undo_redo_gizmo_stack(type, active_stack);
|
2021-04-07 10:58:14 +00:00
|
|
|
|
2021-04-06 11:17:29 +00:00
|
|
|
wxGetApp().mainframe->update_title();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ProjectDirtyStateManager::update_from_presets()
|
|
|
|
{
|
2021-04-06 14:29:05 +00:00
|
|
|
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();
|
2021-04-06 11:17:29 +00:00
|
|
|
wxGetApp().mainframe->update_title();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ProjectDirtyStateManager::reset_after_save()
|
|
|
|
{
|
2021-04-07 10:58:14 +00:00
|
|
|
const Plater* plater = wxGetApp().plater();
|
|
|
|
const UndoRedo::Stack& main_stack = plater->undo_redo_stack_main();
|
|
|
|
const UndoRedo::Stack& active_stack = plater->undo_redo_stack_active();
|
|
|
|
|
|
|
|
if (&main_stack == &active_stack) {
|
|
|
|
const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(main_stack);
|
2021-04-15 13:19:03 +00:00
|
|
|
const UndoRedo::Snapshot* saveable_snapshot = get_last_saveable_snapshot(EStackType::Main, main_stack, m_state.gizmos);
|
|
|
|
assert(saveable_snapshot != nullptr);
|
|
|
|
m_last_save.main = saveable_snapshot->timestamp;
|
2021-04-07 10:58:14 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(active_stack);
|
2021-04-15 13:19:03 +00:00
|
|
|
const UndoRedo::Snapshot* saveable_snapshot = get_last_saveable_snapshot(EStackType::Gizmo, active_stack, m_state.gizmos);
|
2021-04-09 06:26:48 +00:00
|
|
|
const UndoRedo::Snapshot* main_active_snapshot = get_active_snapshot(main_stack);
|
|
|
|
if (boost::starts_with(main_active_snapshot->name, _utf8("Entering"))) {
|
2021-04-15 13:19:03 +00:00
|
|
|
if (m_state.gizmos.current) {
|
2021-04-09 06:26:48 +00:00
|
|
|
m_last_save.main = main_active_snapshot->timestamp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-15 13:19:03 +00:00
|
|
|
m_last_save.gizmo = saveable_snapshot->timestamp;
|
2021-04-07 10:58:14 +00:00
|
|
|
}
|
|
|
|
|
2021-04-09 06:26:48 +00:00
|
|
|
reset_initial_presets();
|
|
|
|
m_state.reset();
|
2021-04-06 11:17:29 +00:00
|
|
|
wxGetApp().mainframe->update_title();
|
|
|
|
}
|
|
|
|
|
2021-04-06 14:29:05 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-06 08:00:17 +00:00
|
|
|
#if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
|
|
|
|
void ProjectDirtyStateManager::render_debug_window() const
|
|
|
|
{
|
2021-04-07 10:58:14 +00:00
|
|
|
ImGuiWrapper& imgui = *wxGetApp().imgui();
|
|
|
|
|
2021-04-06 08:00:17 +00:00
|
|
|
auto color = [](bool value) {
|
|
|
|
return value ? ImVec4(1.0f, 0.49f, 0.216f, 1.0f) : ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
|
|
|
|
};
|
2021-04-15 13:19:03 +00:00
|
|
|
auto bool_to_text = [](bool value) {
|
2021-04-06 08:00:17 +00:00
|
|
|
return value ? "true" : "false";
|
|
|
|
};
|
2021-04-15 13:19:03 +00:00
|
|
|
auto append_bool_item = [color, bool_to_text, &imgui](const std::string& name, bool value) {
|
2021-04-07 10:58:14 +00:00
|
|
|
imgui.text_colored(color(value), name);
|
|
|
|
ImGui::SameLine();
|
2021-04-15 13:19:03 +00:00
|
|
|
imgui.text_colored(color(value), bool_to_text(value));
|
2021-04-07 10:58:14 +00:00
|
|
|
};
|
2021-04-15 13:19:03 +00:00
|
|
|
auto append_int_item = [&imgui](const std::string& name, int value) {
|
2021-04-09 06:26:48 +00:00
|
|
|
imgui.text(name);
|
|
|
|
ImGui::SameLine();
|
|
|
|
imgui.text(std::to_string(value));
|
|
|
|
};
|
2021-04-15 13:19:03 +00:00
|
|
|
auto append_snapshot_item = [&imgui](const std::string& label, const UndoRedo::Snapshot* snapshot) {
|
|
|
|
imgui.text(label);
|
|
|
|
ImGui::SameLine(100);
|
|
|
|
if (snapshot != nullptr)
|
|
|
|
imgui.text(snapshot->name + " (" + std::to_string(snapshot->timestamp) + ")");
|
|
|
|
else
|
|
|
|
imgui.text("-");
|
|
|
|
};
|
2021-04-06 08:00:17 +00:00
|
|
|
|
2021-04-15 13:19:03 +00:00
|
|
|
imgui.begin(std::string("Project dirty state statistics"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
|
2021-04-06 08:00:17 +00:00
|
|
|
|
2021-04-15 13:19:03 +00:00
|
|
|
if (ImGui::CollapsingHeader("Dirty state", ImGuiTreeNodeFlags_DefaultOpen)) {
|
2021-04-09 06:26:48 +00:00
|
|
|
append_bool_item("Overall:", is_dirty());
|
|
|
|
ImGui::Separator();
|
|
|
|
append_bool_item("Plater:", m_state.plater);
|
|
|
|
append_bool_item("Presets:", m_state.presets);
|
2021-04-15 13:19:03 +00:00
|
|
|
append_bool_item("Current gizmo:", m_state.gizmos.current);
|
2021-04-09 06:26:48 +00:00
|
|
|
}
|
|
|
|
|
2021-04-15 13:19:03 +00:00
|
|
|
if (ImGui::CollapsingHeader("Last save timestamps", ImGuiTreeNodeFlags_DefaultOpen)) {
|
2021-04-09 06:26:48 +00:00
|
|
|
append_int_item("Main:", m_last_save.main);
|
|
|
|
append_int_item("Gizmo:", m_last_save.gizmo);
|
|
|
|
}
|
2021-04-06 08:00:17 +00:00
|
|
|
|
2021-04-15 13:19:03 +00:00
|
|
|
if (ImGui::CollapsingHeader("Main snapshots", ImGuiTreeNodeFlags_DefaultOpen)) {
|
|
|
|
const UndoRedo::Stack& stack = wxGetApp().plater()->undo_redo_stack_main();
|
|
|
|
const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(stack);
|
|
|
|
append_snapshot_item("Active:", active_snapshot);
|
|
|
|
const UndoRedo::Snapshot* last_saveable_snapshot = get_last_saveable_snapshot(EStackType::Main, stack, m_state.gizmos);
|
|
|
|
append_snapshot_item("Last saveable:", last_saveable_snapshot);
|
|
|
|
if (ImGui::CollapsingHeader("Main undo/redo stack", ImGuiTreeNodeFlags_DefaultOpen)) {
|
|
|
|
const std::vector<UndoRedo::Snapshot>& snapshots = stack.snapshots();
|
|
|
|
for (const UndoRedo::Snapshot& snapshot : snapshots) {
|
|
|
|
bool active = active_snapshot->timestamp == snapshot.timestamp;
|
|
|
|
imgui.text_colored(color(active), snapshot.name);
|
|
|
|
ImGui::SameLine(150);
|
|
|
|
imgui.text_colored(color(active), " (" + std::to_string(snapshot.timestamp) + ")");
|
|
|
|
if (&snapshot == last_saveable_snapshot) {
|
|
|
|
ImGui::SameLine();
|
|
|
|
imgui.text_colored(color(active), " (S)");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_state.gizmos.any_used_modified()) {
|
|
|
|
if (ImGui::CollapsingHeader("Gizmos", ImGuiTreeNodeFlags_DefaultOpen)) {
|
|
|
|
ImGui::Indent(10.0f);
|
|
|
|
for (const auto& [name, gizmo] : m_state.gizmos.used) {
|
|
|
|
if (!gizmo.modified_timestamps.empty()) {
|
|
|
|
if (ImGui::CollapsingHeader(name.c_str(), ImGuiTreeNodeFlags_DefaultOpen)) {
|
|
|
|
for (size_t i : gizmo.modified_timestamps) {
|
|
|
|
imgui.text(std::to_string(i));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ImGui::Unindent(10.0f);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-06 08:00:17 +00:00
|
|
|
imgui.end();
|
|
|
|
}
|
|
|
|
#endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
|
|
|
|
|
2021-04-15 13:19:03 +00:00
|
|
|
void ProjectDirtyStateManager::update_from_undo_redo_main_stack(UpdateType type, const Slic3r::UndoRedo::Stack& stack)
|
2021-04-07 10:58:14 +00:00
|
|
|
{
|
|
|
|
m_state.plater = false;
|
|
|
|
|
|
|
|
const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(stack);
|
2021-04-15 13:19:03 +00:00
|
|
|
|
|
|
|
if (type == UpdateType::TakeSnapshot)
|
|
|
|
m_state.gizmos.remove_obsolete_used(stack);
|
|
|
|
|
2021-04-07 10:58:14 +00:00
|
|
|
if (active_snapshot->name == _utf8("New Project") ||
|
|
|
|
active_snapshot->name == _utf8("Reset Project") ||
|
|
|
|
boost::starts_with(active_snapshot->name, _utf8("Load Project:")))
|
|
|
|
return;
|
|
|
|
|
2021-04-09 06:26:48 +00:00
|
|
|
size_t search_timestamp = 0;
|
2021-04-15 13:19:03 +00:00
|
|
|
if (boost::starts_with(active_snapshot->name, _utf8("Entering"))) {
|
|
|
|
m_state.gizmos.current = false;
|
|
|
|
m_last_save.gizmo = 0;
|
|
|
|
search_timestamp = m_last_save.main;
|
|
|
|
}
|
|
|
|
else if (boost::starts_with(active_snapshot->name, _utf8("Leaving"))) {
|
|
|
|
if (m_state.gizmos.current)
|
|
|
|
m_state.gizmos.add_used(*active_snapshot);
|
|
|
|
m_state.gizmos.current = false;
|
2021-04-09 06:26:48 +00:00
|
|
|
m_last_save.gizmo = 0;
|
|
|
|
search_timestamp = m_last_save.main;
|
|
|
|
}
|
|
|
|
|
2021-04-15 13:19:03 +00:00
|
|
|
const UndoRedo::Snapshot* last_saveable_snapshot = get_last_saveable_snapshot(EStackType::Main, stack, m_state.gizmos);
|
|
|
|
m_state.plater = (last_saveable_snapshot != nullptr && last_saveable_snapshot->timestamp != m_last_save.main);
|
2021-04-07 10:58:14 +00:00
|
|
|
}
|
|
|
|
|
2021-04-15 13:19:03 +00:00
|
|
|
void ProjectDirtyStateManager::update_from_undo_redo_gizmo_stack(UpdateType type, const Slic3r::UndoRedo::Stack& stack)
|
2021-04-07 10:58:14 +00:00
|
|
|
{
|
2021-04-15 13:19:03 +00:00
|
|
|
m_state.gizmos.current = false;
|
2021-04-07 10:58:14 +00:00
|
|
|
|
|
|
|
const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(stack);
|
2021-04-09 06:26:48 +00:00
|
|
|
if (active_snapshot->name == "Gizmos-Initial") {
|
2021-04-15 13:19:03 +00:00
|
|
|
m_state.gizmos.current = (m_last_save.gizmo != 0);
|
2021-04-07 10:58:14 +00:00
|
|
|
return;
|
2021-04-09 06:26:48 +00:00
|
|
|
}
|
2021-04-07 10:58:14 +00:00
|
|
|
|
2021-04-15 13:19:03 +00:00
|
|
|
const UndoRedo::Snapshot* last_saveable_snapshot = get_last_saveable_snapshot(EStackType::Gizmo, stack, m_state.gizmos);
|
|
|
|
m_state.gizmos.current = (last_saveable_snapshot != nullptr && last_saveable_snapshot->timestamp != m_last_save.gizmo);
|
2021-04-07 10:58:14 +00:00
|
|
|
}
|
|
|
|
|
2021-04-06 08:00:17 +00:00
|
|
|
} // namespace GUI
|
|
|
|
} // namespace Slic3r
|
|
|
|
|
|
|
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
|
|
|
|