diff --git a/resources/icons/compare.svg b/resources/icons/compare.svg
new file mode 100644
index 00000000000..fcb458f7c46
--- /dev/null
+++ b/resources/icons/compare.svg
@@ -0,0 +1,30 @@
+
+
+
diff --git a/src/libslic3r/Preset.hpp b/src/libslic3r/Preset.hpp
index b6d44d58ff5..d35a32d2aa6 100644
--- a/src/libslic3r/Preset.hpp
+++ b/src/libslic3r/Preset.hpp
@@ -507,9 +507,9 @@ class PresetCollection
{ return const_cast(this)->find_preset_renamed(name); }
size_t update_compatible_internal(const PresetWithVendorProfile &active_printer, const PresetWithVendorProfile *active_print, PresetSelectCompatibleType unselect_if_incompatible);
-
+public:
static std::vector dirty_options(const Preset *edited, const Preset *reference, const bool is_printer_type = false);
-
+private:
// Type of this PresetCollection: TYPE_PRINT, TYPE_FILAMENT or TYPE_PRINTER.
Preset::Type m_type;
// List of presets, starting with the "- default -" preset.
diff --git a/src/slic3r/GUI/Search.cpp b/src/slic3r/GUI/Search.cpp
index d61fc05e02f..82496da3896 100644
--- a/src/slic3r/GUI/Search.cpp
+++ b/src/slic3r/GUI/Search.cpp
@@ -329,6 +329,31 @@ const Option& OptionsSearcher::get_option(const std::string& opt_key) const
return options[it - options.begin()];
}
+Option OptionsSearcher::get_option(const std::string& opt_key, const wxString& label, Preset::Type type) const
+{
+ auto it = std::lower_bound(options.begin(), options.end(), Option({ boost::nowide::widen(opt_key) }));
+ if(it->opt_key == boost::nowide::widen(opt_key) ||
+ groups_and_categories.find(opt_key) == groups_and_categories.end())
+ return options[it - options.begin()];
+
+ const GroupAndCategory& gc = groups_and_categories.at(opt_key);
+ if (gc.group.IsEmpty() || gc.category.IsEmpty())
+ return options[it - options.begin()];
+
+ wxString suffix;
+ wxString suffix_local;
+ if (gc.category == "Machine limits") {
+ suffix = opt_key.back() == '1' ? L("Stealth") : L("Normal");
+ suffix_local = " " + _(suffix);
+ suffix = " " + suffix;
+ }
+
+ return Option{boost::nowide::widen(opt_key), type,
+ (label + suffix).ToStdWstring(), (_(label) + suffix_local).ToStdWstring(),
+ gc.group.ToStdWstring(), _(gc.group).ToStdWstring(),
+ gc.category.ToStdWstring(), GUI::Tab::translate_category(gc.category, type).ToStdWstring() };
+}
+
void OptionsSearcher::add_key(const std::string& opt_key, const wxString& group, const wxString& category)
{
groups_and_categories[opt_key] = GroupAndCategory{group, category};
diff --git a/src/slic3r/GUI/Search.hpp b/src/slic3r/GUI/Search.hpp
index f8c9dffa6aa..1f2909564db 100644
--- a/src/slic3r/GUI/Search.hpp
+++ b/src/slic3r/GUI/Search.hpp
@@ -117,6 +117,7 @@ class OptionsSearcher
const FoundOption& operator[](const size_t pos) const noexcept { return found[pos]; }
const Option& get_option(size_t pos_in_filter) const;
const Option& get_option(const std::string& opt_key) const;
+ Option get_option(const std::string& opt_key, const wxString& label, Preset::Type type) const;
const std::vector& found_options() { return found; }
const GroupAndCategory& get_group_and_category (const std::string& opt_key) { return groups_and_categories[opt_key]; }
diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp
index aec2f87542b..a0e0e554b4a 100644
--- a/src/slic3r/GUI/Tab.cpp
+++ b/src/slic3r/GUI/Tab.cpp
@@ -196,6 +196,7 @@ void Tab::create_preset_tab()
m_scaled_buttons.reserve(6);
m_scaled_buttons.reserve(2);
+ add_scaled_button(panel, &m_btn_compare_preset, "compare");
add_scaled_button(panel, &m_btn_save_preset, "save");
add_scaled_button(panel, &m_btn_delete_preset, "cross");
if (m_type == Preset::Type::TYPE_PRINTER)
@@ -207,6 +208,7 @@ void Tab::create_preset_tab()
add_scaled_button(panel, &m_btn_hide_incompatible_presets, m_bmp_hide_incompatible_presets.name());
+ m_btn_compare_preset->SetToolTip(_L("Compare this preset with some another"));
// TRN "Save current Settings"
m_btn_save_preset->SetToolTip(from_u8((boost::format(_utf8(L("Save current %s"))) % m_title).str()));
m_btn_delete_preset->SetToolTip(_(L("Delete this preset")));
@@ -271,6 +273,9 @@ void Tab::create_preset_tab()
m_hsizer->Add(m_undo_btn, 0, wxALIGN_CENTER_VERTICAL);
m_hsizer->AddSpacer(int(32 * scale_factor));
m_hsizer->Add(m_search_btn, 0, wxALIGN_CENTER_VERTICAL);
+ m_hsizer->AddSpacer(int(8*scale_factor));
+ m_hsizer->Add(m_btn_compare_preset, 0, wxALIGN_CENTER_VERTICAL);
+ m_hsizer->AddSpacer(int(16*scale_factor));
// m_hsizer->AddStretchSpacer(32);
// StretchSpacer has a strange behavior under OSX, so
// There is used just additional sizer for m_mode_sizer with right alignment
@@ -338,6 +343,7 @@ void Tab::create_preset_tab()
m_page_view->SetScrollbars(1, 20, 1, 2);
m_hsizer->Add(m_page_view, 1, wxEXPAND | wxLEFT, 5);
+ m_btn_compare_preset->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e) { compare_preset(); }));
m_btn_save_preset->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e) { save_preset(); }));
m_btn_delete_preset->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e) { delete_preset(); }));
m_btn_hide_incompatible_presets->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e) {
@@ -2057,11 +2063,18 @@ bool Tab::current_preset_is_dirty()
void TabPrinter::build()
{
m_presets = &m_preset_bundle->printers;
- load_initial_data();
-
m_printer_technology = m_presets->get_selected_preset().printer_technology();
- m_presets->get_selected_preset().printer_technology() == ptSLA ? build_sla() : build_fff();
+ // For DiffPresetDialog we use options list which is saved in Searcher class.
+ // Options for the Searcher is added in the moment of pages creation.
+ // So, build first of all printer pages for non-selected printer technology...
+ std::string def_preset_name = "- default " + std::string(m_printer_technology == ptSLA ? "FFF" : "SLA") + " -";
+ m_config = &m_presets->find_preset(def_preset_name)->config;
+ m_printer_technology == ptSLA ? build_fff() : build_sla();
+
+ // ... and than for selected printer technology
+ load_initial_data();
+ m_printer_technology == ptSLA ? build_sla() : build_fff();
}
void TabPrinter::build_print_host_upload_group(Page* page)
@@ -2096,7 +2109,8 @@ void TabPrinter::build_fff()
m_initial_extruders_count = m_extruders_count = nozzle_diameter->values.size();
wxGetApp().sidebar().update_objects_list_extruder_column(m_initial_extruders_count);
- const Preset* parent_preset = m_presets->get_selected_preset_parent();
+ const Preset* parent_preset = m_printer_technology == ptSLA ? nullptr // just for first build, if SLA printer preset is selected
+ : m_presets->get_selected_preset_parent();
m_sys_extruders_count = parent_preset == nullptr ? 0 :
static_cast(parent_preset->config.option("nozzle_diameter"))->values.size();
@@ -2386,7 +2400,9 @@ void TabPrinter::append_option_line(ConfigOptionsGroupShp optgroup, const std::s
auto option = optgroup->get_option(opt_key, 0);
auto line = Line{ option.opt.full_label, "" };
line.append_option(option);
- if (m_use_silent_mode)
+ if (m_use_silent_mode
+ || m_printer_technology == ptSLA // just for first build, if SLA printer preset is selected
+ )
line.append_option(optgroup->get_option(opt_key, 1));
optgroup->append_line(line);
}
@@ -2466,24 +2482,15 @@ void TabPrinter::build_unregular_pages()
size_t n_before_extruders = 2; // Count of pages before Extruder pages
bool is_marlin_flavor = m_config->option>("gcode_flavor")->value == gcfMarlin;
+ // just for first build, if SLA printer preset is selected
+ bool just_initial_build = m_printer_technology == ptSLA;
+
/* ! Freeze/Thaw in this function is needed to avoid call OnPaint() for erased pages
* and be cause of application crash, when try to change Preset in moment,
* when one of unregular pages is selected.
* */
Freeze();
-#ifdef __WXMSW__
- /* Workaround for correct layout of controls inside the created page:
- * In some _strange_ way we should we should imitate page resizing.
- */
-/* auto layout_page = [this](PageShp page)
- {
- const wxSize& sz = page->GetSize();
- page->SetSize(sz.x + 1, sz.y + 1);
- page->SetSize(sz);
- };*/
-#endif //__WXMSW__
-
// Add/delete Kinematics page according to is_marlin_flavor
size_t existed_page = 0;
for (size_t i = n_before_extruders; i < m_pages.size(); ++i) // first make sure it's not there already
@@ -2495,12 +2502,12 @@ void TabPrinter::build_unregular_pages()
break;
}
- if (existed_page < n_before_extruders && is_marlin_flavor) {
+ if (existed_page < n_before_extruders && (is_marlin_flavor || just_initial_build)) {
auto page = build_kinematics_page();
-#ifdef __WXMSW__
-// layout_page(page);
-#endif
- m_pages.insert(m_pages.begin() + n_before_extruders, page);
+ if (just_initial_build)
+ page->clear();
+ else
+ m_pages.insert(m_pages.begin() + n_before_extruders, page);
}
if (is_marlin_flavor)
@@ -2518,7 +2525,8 @@ void TabPrinter::build_unregular_pages()
}
m_has_single_extruder_MM_page = false;
}
- if (m_extruders_count > 1 && m_config->opt_bool("single_extruder_multi_material") && !m_has_single_extruder_MM_page) {
+ if (just_initial_build ||
+ (m_extruders_count > 1 && m_config->opt_bool("single_extruder_multi_material") && !m_has_single_extruder_MM_page)) {
// create a page, but pretend it's an extruder page, so we can add it to m_pages ourselves
auto page = add_options_page(L("Single extruder MM setup"), "printer", true);
auto optgroup = page->new_optgroup(L("Single extruder multimaterial parameters"));
@@ -2527,8 +2535,12 @@ void TabPrinter::build_unregular_pages()
optgroup->append_single_option_line("parking_pos_retraction");
optgroup->append_single_option_line("extra_loading_move");
optgroup->append_single_option_line("high_current_on_filament_swap");
- m_pages.insert(m_pages.end() - n_after_single_extruder_MM, page);
- m_has_single_extruder_MM_page = true;
+ if (just_initial_build)
+ page->clear();
+ else {
+ m_pages.insert(m_pages.end() - n_after_single_extruder_MM, page);
+ m_has_single_extruder_MM_page = true;
+ }
}
// Build missed extruder pages
@@ -2633,10 +2645,6 @@ void TabPrinter::build_unregular_pages()
line = optgroup->create_single_option_line("extruder_colour", wxEmptyString, extruder_idx);
line.append_widget(reset_to_filament_color);
optgroup->append_line(line);
-
-#ifdef __WXMSW__
-// layout_page(page);
-#endif
}
// # remove extra pages
@@ -2647,6 +2655,10 @@ void TabPrinter::build_unregular_pages()
Thaw();
m_extruders_count_old = m_extruders_count;
+
+ if (just_initial_build)
+ return; // next part of code is no needed to execute at this moment
+
rebuild_page_tree();
// Reload preset pages with current configuration values
@@ -3365,6 +3377,12 @@ void Tab::OnKeyDown(wxKeyEvent& event)
event.Skip();
}
+void Tab::compare_preset()
+{
+ DiffPresetDialog dlg(m_type);
+ dlg.ShowModal();
+}
+
// Save the current preset into file.
// This removes the "dirty" flag of the preset, possibly creates a new preset under a new name,
// and activates the new preset.
diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp
index 29588ba20e0..4b0a77cb96e 100644
--- a/src/slic3r/GUI/Tab.hpp
+++ b/src/slic3r/GUI/Tab.hpp
@@ -112,6 +112,7 @@ class Tab: public wxPanel
const wxString m_title;
TabPresetComboBox* m_presets_choice;
ScalableButton* m_search_btn;
+ ScalableButton* m_btn_compare_preset;
ScalableButton* m_btn_save_preset;
ScalableButton* m_btn_delete_preset;
ScalableButton* m_btn_edit_ph_printer {nullptr};
@@ -290,6 +291,7 @@ class Tab: public wxPanel
void OnTreeSelChange(wxTreeEvent& event);
void OnKeyDown(wxKeyEvent& event);
+ void compare_preset();
void save_preset(std::string name = std::string(), bool detach = false);
void delete_preset();
void toggle_show_hide_incompatible();
diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp
index b4b38b4bd2a..9cc71ae68f8 100644
--- a/src/slic3r/GUI/UnsavedChangesDialog.cpp
+++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp
@@ -22,6 +22,7 @@
//#include "fts_fuzzy_match.h"
#include "BitmapCache.hpp"
+#include "PresetComboBoxes.hpp"
using boost::optional;
@@ -223,12 +224,6 @@ UnsavedChangesModel::UnsavedChangesModel(wxWindow* parent) :
{
}
-UnsavedChangesModel::~UnsavedChangesModel()
-{
- for (ModelNode* preset_node : m_preset_nodes)
- delete preset_node;
-}
-
wxDataViewItem UnsavedChangesModel::AddPreset(Preset::Type type, wxString preset_name, PrinterTechnology pt)
{
// "color" strings
@@ -288,7 +283,7 @@ wxDataViewItem UnsavedChangesModel::AddOption(Preset::Type type, wxString catego
make_string_bold(group_name);
// add items
- for (ModelNode* preset : m_preset_nodes)
+ for (std::unique_ptr& preset : m_preset_nodes)
if (preset->type() == type)
{
for (std::unique_ptr &category : preset->GetChildren())
@@ -301,7 +296,7 @@ wxDataViewItem UnsavedChangesModel::AddOption(Preset::Type type, wxString catego
return wxDataViewItem((void*)AddOptionWithGroup(category.get(), group_name, option_name, old_value, new_value));
}
- return wxDataViewItem((void*)AddOptionWithGroupAndCategory(preset, category_name, group_name, option_name, old_value, new_value, category_icon_name));
+ return wxDataViewItem((void*)AddOptionWithGroupAndCategory(preset.get(), category_name, group_name, option_name, old_value, new_value, category_icon_name));
}
return wxDataViewItem(nullptr);
@@ -463,7 +458,6 @@ wxDataViewItem UnsavedChangesModel::GetParent(const wxDataViewItem& item) const
ModelNode* node = static_cast(item.GetID());
- // "MyMusic" also has no parent
if (node->IsRoot())
return wxDataViewItem(nullptr);
@@ -482,17 +476,13 @@ bool UnsavedChangesModel::IsContainer(const wxDataViewItem& item) const
unsigned int UnsavedChangesModel::GetChildren(const wxDataViewItem& parent, wxDataViewItemArray& array) const
{
- ModelNode* node = (ModelNode*)parent.GetID();
- if (!node) {
- for (auto preset_node : m_preset_nodes)
- array.Add(wxDataViewItem((void*)preset_node));
- return m_preset_nodes.size();
- }
+ ModelNode* parent_node = (ModelNode*)parent.GetID();
- for (std::unique_ptr &child : node->GetChildren())
+ const ModelNodePtrArray& children = parent_node ? parent_node->GetChildren() : m_preset_nodes;
+ for (const std::unique_ptr& child : children)
array.Add(wxDataViewItem((void*)child.get()));
- return node->GetChildCount();
+ return array.size();
}
@@ -522,10 +512,57 @@ static void rescale_children(ModelNode* parent)
void UnsavedChangesModel::Rescale()
{
- for (ModelNode* node : m_preset_nodes) {
+ for (std::unique_ptr &node : m_preset_nodes) {
node->UpdateIcons();
- rescale_children(node);
+ rescale_children(node.get());
+ }
+}
+
+wxDataViewItem UnsavedChangesModel::Delete(const wxDataViewItem& item)
+{
+ auto ret_item = wxDataViewItem(nullptr);
+ ModelNode* node = static_cast(item.GetID());
+ if (!node) // happens if item.IsOk()==false
+ return ret_item;
+
+ // first remove the node from the parent's array of children;
+ // NOTE: m_preset_nodes is only a vector of _pointers_
+ // thus removing the node from it doesn't result in freeing it
+ ModelNodePtrArray& children = node->GetChildren();
+ // Delete all children
+ while (!children.empty())
+ Delete(wxDataViewItem(children.back().get()));
+
+ auto node_parent = node->GetParent();
+ wxDataViewItem parent(node_parent);
+
+ ModelNodePtrArray& parents_children = node_parent ? node_parent->GetChildren() : m_preset_nodes;
+ auto it = find_if(parents_children.begin(), parents_children.end(),
+ [node](std::unique_ptr& child) { return child.get() == node; });
+ assert(it != parents_children.end());
+ it = parents_children.erase(it);
+
+ if (it != parents_children.end())
+ ret_item = wxDataViewItem(it->get());
+
+ // set m_container to FALSE if parent has no child
+ if (node_parent) {
+#ifndef __WXGTK__
+ if (node_parent->GetChildCount() == 0)
+ node_parent->m_container = false;
+#endif //__WXGTK__
+ ret_item = parent;
}
+
+ // notify control
+ ItemDeleted(parent, item);
+ return ret_item;
+}
+
+void UnsavedChangesModel::Clear()
+{
+ while (!m_preset_nodes.empty())
+ Delete(wxDataViewItem(m_preset_nodes.back().get()));
}
@@ -877,6 +914,17 @@ static std::string get_pure_opt_key(std::string opt_key)
return opt_key;
}
+static wxString get_full_label(std::string opt_key, const DynamicPrintConfig& config)
+{
+ opt_key = get_pure_opt_key(opt_key);
+
+ if (config.option(opt_key)->is_nil())
+ return _L("N/A");
+
+ const ConfigOptionDef* opt = config.def()->get(opt_key);
+ return opt->full_label.empty() ? opt->label : opt->full_label;
+}
+
static wxString get_string_value(std::string opt_key, const DynamicPrintConfig& config)
{
int opt_idx = get_id_from_opt_key(opt_key);
@@ -1213,6 +1261,225 @@ FullCompareDialog::FullCompareDialog(const wxString& option_name, const wxString
}
+static const PresetCollection* get_preset_collection(Preset::Type type_) {
+ return type_ == Preset::Type::TYPE_PRINT ? &wxGetApp().preset_bundle->prints :
+ type_ == Preset::Type::TYPE_SLA_PRINT ? &wxGetApp().preset_bundle->sla_prints :
+ type_ == Preset::Type::TYPE_FILAMENT ? &wxGetApp().preset_bundle->filaments :
+ type_ == Preset::Type::TYPE_SLA_MATERIAL ? &wxGetApp().preset_bundle->sla_materials :
+ type_ == Preset::Type::TYPE_PRINTER ? &wxGetApp().preset_bundle->printers :
+ nullptr;
+}
+
+//------------------------------------------
+// DiffPresetDialog
+//------------------------------------------
+DiffPresetDialog::DiffPresetDialog(Preset::Type type/* = Preset::Type::TYPE_INVALID*/)
+ : DPIDialog(static_cast(wxGetApp().mainframe), wxID_ANY, format_wxstr(_L("Compare %1% Presets"), type), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER),
+ m_type(type)
+{
+ wxColour bgr_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
+ SetBackgroundColour(bgr_clr);
+
+#if ENABLE_WX_3_1_3_DPI_CHANGED_EVENT && defined(__WXMSW__)
+ // ys_FIXME! temporary workaround for correct font scaling
+ // Because of from wxWidgets 3.1.3 auto rescaling is implemented for the Fonts,
+ // From the very beginning set dialog font to the wxSYS_DEFAULT_GUI_FONT
+ this->SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
+#endif // ENABLE_WX_3_1_3_DPI_CHANGED_EVENT
+
+ int border = 10;
+ int em = em_unit();
+
+ m_top_info_line = new wxStaticText(this, wxID_ANY, "Select presets to compare");
+ m_top_info_line->SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Bold());
+
+ m_bottom_info_line = new wxStaticText(this, wxID_ANY, "");
+ m_bottom_info_line->SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Bold());
+
+ const PresetCollection* collection = get_preset_collection(type);
+ wxBoxSizer* presets_sizer = new wxBoxSizer(wxHORIZONTAL);
+
+ auto add_preset_combobox = [collection, presets_sizer, type, em, this](PresetComboBox** cb) {
+ *cb = new PresetComboBox(this, type, wxSize(em * 35, -1));
+ (*cb)->set_selection_changed_function([this](int selection) { update_tree(); });
+ (*cb)->update(collection->get_selected_preset().name);
+
+ presets_sizer->Add(*cb, 1, wxRIGHT|wxLEFT, 5);
+ };
+
+ add_preset_combobox(&m_presets_left);
+ add_preset_combobox(&m_presets_right);
+
+ m_tree = new wxDataViewCtrl(this, wxID_ANY, wxDefaultPosition, wxSize(em * 65, em * 40), wxBORDER_SIMPLE | wxDV_VARIABLE_LINE_HEIGHT | wxDV_ROW_LINES);
+ m_tree_model = new UnsavedChangesModel(this);
+ m_tree->AssociateModel(m_tree_model);
+ m_tree_model->SetAssociatedControl(m_tree);
+
+ auto append_bmp_text_column = [this](const wxString& label, unsigned model_column, int width, bool set_expander = false)
+ {
+#ifdef __linux__
+ wxDataViewIconTextRenderer* rd = new wxDataViewIconTextRenderer();
+#ifdef SUPPORTS_MARKUP
+ rd->EnableMarkup(true);
+#endif
+ wxDataViewColumn* column = new wxDataViewColumn(label, rd, model_column, width, wxALIGN_TOP, wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_CELL_INERT);
+#else
+ wxDataViewColumn* column = new wxDataViewColumn(label, new BitmapTextRenderer(true, wxDATAVIEW_CELL_INERT), model_column, width, wxALIGN_TOP, wxDATAVIEW_COL_RESIZABLE);
+#endif //__linux__
+ m_tree->AppendColumn(column);
+ if (set_expander)
+ m_tree->SetExpanderColumn(column);
+ };
+
+ append_bmp_text_column("", UnsavedChangesModel::colIconText, 35 * em);
+ append_bmp_text_column(_L("Left Preset Value"), UnsavedChangesModel::colOldValue, 15 * em);
+ append_bmp_text_column(_L("Right Preset Value"), UnsavedChangesModel::colNewValue, 15 * em);
+
+// m_tree->Bind(wxEVT_DATAVIEW_ITEM_VALUE_CHANGED, &UnsavedChangesDialog::item_value_changed, this);
+// m_tree->Bind(wxEVT_DATAVIEW_ITEM_CONTEXT_MENU, &UnsavedChangesDialog::context_menu, this);
+
+ wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL);
+
+ topSizer->Add(m_top_info_line, 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, 2 * border);
+ topSizer->Add(presets_sizer, 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border);
+ topSizer->Add(m_bottom_info_line, 0, wxEXPAND | wxALL, 2 * border);
+ topSizer->Add(m_tree, 1, wxEXPAND | wxALL, border);
+
+ update_tree();
+
+ this->SetMinSize(wxSize(80 * em, 30 * em));
+ this->SetSizer(topSizer);
+ topSizer->SetSizeHints(this);
+}
+
+void DiffPresetDialog::update_tree()
+{
+ Search::OptionsSearcher& searcher = wxGetApp().sidebar().get_searcher();
+ searcher.sort_options_by_opt_key();
+
+ m_tree_model->Clear();
+ wxString bottom_info = "";
+
+ // list of the presets with unsaved changes
+ std::vector presets_list;
+ if (m_type == Preset::TYPE_INVALID)
+ {
+ PrinterTechnology printer_technology = wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology();
+
+ for (Tab* tab : wxGetApp().tabs_list)
+ if (tab->supports_printer_technology(printer_technology) && tab->current_preset_is_dirty())
+ presets_list.emplace_back(tab->get_presets());
+ }
+ else
+ presets_list.emplace_back(get_preset_collection(m_type));
+
+ // Display a dialog showing the dirty options in a human readable form.
+ for (const PresetCollection* presets : presets_list)
+ {
+ const Preset* left_preset = presets->find_preset(into_u8(m_presets_left ->GetString(m_presets_left ->GetSelection())));
+ const Preset* right_preset = presets->find_preset(into_u8(m_presets_right->GetString(m_presets_right->GetSelection())));
+ if (!left_preset || !right_preset) {
+ bottom_info = _L("One of the presets doesn't found");
+ continue;
+ }
+
+ const DynamicPrintConfig& left_config = left_preset->config;
+ const PrinterTechnology& left_pt = left_preset->printer_technology();
+ const DynamicPrintConfig& right_congig = right_preset->config;
+
+ Preset::Type type = presets->type();
+
+ if (left_pt != right_preset->printer_technology()) {
+ bottom_info = _L("Comparable presets has different printer technology");
+ continue;
+ }
+
+ // Collect dirty options.
+ const bool deep_compare = (type == Preset::TYPE_PRINTER || type == Preset::TYPE_SLA_MATERIAL);
+ auto dirty_options = presets->dirty_options(left_preset, right_preset, deep_compare);
+
+ if (dirty_options.empty()) {
+ bottom_info = _L("Presets are the same");
+ continue;
+ }
+
+ m_tree_model->AddPreset(type, "\"" + from_u8(left_preset->name) + "\" vs \"" + from_u8(right_preset->name) + "\"", left_pt);
+
+ const std::map& category_icon_map = wxGetApp().get_tab(type)->get_category_icon_map();
+
+ // process changes of extruders count
+ if (type == Preset::TYPE_PRINTER && left_pt == ptFFF &&
+ left_config.opt("extruder_colour")->values.size() != right_congig.opt("extruder_colour")->values.size()) {
+ wxString local_label = _L("Extruders count");
+ wxString left_val = from_u8((boost::format("%1%") % left_config.opt("extruder_colour")->values.size()).str());
+ wxString right_val = from_u8((boost::format("%1%") % right_congig.opt("extruder_colour")->values.size()).str());
+
+ m_tree_model->AddOption(type, _L("General"), _L("Capabilities"), local_label, left_val, right_val, category_icon_map.at("General"));
+ }
+
+ for (const std::string& opt_key : dirty_options) {
+ wxString left_val = get_string_value(opt_key, left_config);
+ wxString right_val = get_string_value(opt_key, right_congig);
+
+ Search::Option option = searcher.get_option(opt_key, get_full_label(opt_key, left_config), type);
+ if (option.opt_key != boost::nowide::widen(opt_key)) {
+ // temporary solution, just for testing
+ m_tree_model->AddOption(type, _L("Undef category"), _L("Undef group"), opt_key, left_val, right_val, "question");
+ // When founded option isn't the correct one.
+ // It can be for dirty_options: "default_print_profile", "printer_model", "printer_settings_id",
+ // because of they don't exist in searcher
+ continue;
+ }
+ m_tree_model->AddOption(type, option.category_local, option.group_local, option.label_local, left_val, right_val, category_icon_map.at(option.category));
+ }
+ }
+
+ bool tree_was_shown = m_tree->IsShown();
+ bool show_tree = bottom_info.IsEmpty();
+ m_tree->Show(show_tree);
+ if (!show_tree)
+ m_bottom_info_line->SetLabel(bottom_info);
+ m_bottom_info_line->Show(!show_tree);
+
+ if (tree_was_shown == m_tree->IsShown())
+ Layout();
+ else {
+ Fit();
+ Refresh();
+ }
+}
+
+void DiffPresetDialog::on_dpi_changed(const wxRect&)
+{
+ int em = em_unit();
+
+ msw_buttons_rescale(this, em, { wxID_CANCEL});
+
+ const wxSize& size = wxSize(80 * em, 30 * em);
+ SetMinSize(size);
+
+ m_tree->GetColumn(UnsavedChangesModel::colIconText)->SetWidth(35 * em);
+ m_tree->GetColumn(UnsavedChangesModel::colOldValue)->SetWidth(15 * em);
+ m_tree->GetColumn(UnsavedChangesModel::colNewValue)->SetWidth(15 * em);
+
+ m_tree_model->Rescale();
+ m_tree->Refresh();
+
+ Fit();
+ Refresh();
+}
+
+void DiffPresetDialog::on_sys_color_changed()
+{
+ // msw_rescale updates just icons, so use it
+ m_tree_model->Rescale();
+ m_tree->Refresh();
+
+ Refresh();
+}
+
+
+
}
diff --git a/src/slic3r/GUI/UnsavedChangesDialog.hpp b/src/slic3r/GUI/UnsavedChangesDialog.hpp
index 232802b661a..b5d5e0e5a14 100644
--- a/src/slic3r/GUI/UnsavedChangesDialog.hpp
+++ b/src/slic3r/GUI/UnsavedChangesDialog.hpp
@@ -20,6 +20,7 @@ namespace GUI{
// ----------------------------------------------------------------------------
class ModelNode;
+class PresetComboBox;
using ModelNodePtrArray = std::vector>;
// On all of 3 different platforms Bitmap+Text icon column looks different
@@ -42,17 +43,6 @@ class ModelNode
wxString m_old_color;
wxString m_new_color;
- // TODO/FIXME:
- // the GTK version of wxDVC (in particular wxDataViewCtrlInternal::ItemAdded)
- // needs to know in advance if a node is or _will be_ a container.
- // Thus implementing:
- // bool IsContainer() const
- // { return m_children.size()>0; }
- // doesn't work with wxGTK when UnsavedChangesModel::AddToClassical is called
- // AND the classical node was removed (a new node temporary without children
- // would be added to the control)
- bool m_container {true};
-
#ifdef __linux__
wxIcon get_bitmap(const wxString& color);
#else
@@ -75,6 +65,17 @@ class ModelNode
wxString m_old_value;
wxString m_new_value;
+ // TODO/FIXME:
+ // the GTK version of wxDVC (in particular wxDataViewCtrlInternal::ItemAdded)
+ // needs to know in advance if a node is or _will be_ a container.
+ // Thus implementing:
+ // bool IsContainer() const
+ // { return m_children.size()>0; }
+ // doesn't work with wxGTK when UnsavedChangesModel::AddToClassical is called
+ // AND the classical node was removed (a new node temporary without children
+ // would be added to the control)
+ bool m_container {true};
+
// preset(root) node
ModelNode(Preset::Type preset_type, wxWindow* parent_win, const wxString& text, const std::string& icon_name);
@@ -113,7 +114,7 @@ class ModelNode
class UnsavedChangesModel : public wxDataViewModel
{
wxWindow* m_parent_win { nullptr };
- std::vector m_preset_nodes;
+ ModelNodePtrArray m_preset_nodes;
wxDataViewCtrl* m_ctrl{ nullptr };
@@ -144,7 +145,7 @@ class UnsavedChangesModel : public wxDataViewModel
};
UnsavedChangesModel(wxWindow* parent);
- ~UnsavedChangesModel();
+ ~UnsavedChangesModel() {}
void SetAssociatedControl(wxDataViewCtrl* ctrl) { m_ctrl = ctrl; }
@@ -159,6 +160,9 @@ class UnsavedChangesModel : public wxDataViewModel
wxString GetColumnType(unsigned int col) const override;
void Rescale();
+ wxDataViewItem Delete(const wxDataViewItem& item);
+ void Clear();
+
wxDataViewItem GetParent(const wxDataViewItem& item) const override;
unsigned int GetChildren(const wxDataViewItem& parent, wxDataViewItemArray& array) const override;
@@ -270,6 +274,34 @@ class FullCompareDialog : public wxDialog
~FullCompareDialog() {}
};
+
+//------------------------------------------
+// DiffPresetDialog
+//------------------------------------------
+class DiffPresetDialog : public DPIDialog
+{
+ wxDataViewCtrl* m_tree{ nullptr };
+ UnsavedChangesModel* m_tree_model{ nullptr };
+
+ PresetComboBox* m_presets_left { nullptr };
+ PresetComboBox* m_presets_right { nullptr };
+
+ wxStaticText* m_top_info_line { nullptr };
+ wxStaticText* m_bottom_info_line{ nullptr };
+
+ Preset::Type m_type;
+
+ void update_tree();
+
+public:
+ DiffPresetDialog(Preset::Type type = Preset::Type::TYPE_INVALID/**/);
+ ~DiffPresetDialog() {}
+
+protected:
+ void on_dpi_changed(const wxRect& suggested_rect) override;
+ void on_sys_color_changed() override;
+};
+
}
}