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/resources/icons/equal.svg b/resources/icons/equal.svg
new file mode 100644
index 00000000000..bce4a24f7c9
--- /dev/null
+++ b/resources/icons/equal.svg
@@ -0,0 +1,15 @@
+
+
+
+
diff --git a/resources/icons/not_equal.svg b/resources/icons/not_equal.svg
new file mode 100644
index 00000000000..bc881443530
--- /dev/null
+++ b/resources/icons/not_equal.svg
@@ -0,0 +1,16 @@
+
+
+
+
diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp
index f535555d00c..b7e966c9e77 100644
--- a/src/libslic3r/Preset.cpp
+++ b/src/libslic3r/Preset.cpp
@@ -616,10 +616,6 @@ PresetCollection::PresetCollection(Preset::Type type, const std::vector m_num_default_presets) {
@@ -1278,6 +1274,18 @@ std::vector PresetCollection::merge_presets(PresetCollection &&othe
return duplicates;
}
+void PresetCollection::update_vendor_ptrs_after_copy(const VendorMap &new_vendors)
+{
+ for (Preset &preset : m_presets)
+ if (preset.vendor != nullptr) {
+ assert(! preset.is_default && ! preset.is_external);
+ // Re-assign a pointer to the vendor structure in the new PresetBundle.
+ auto it = new_vendors.find(preset.vendor->id);
+ assert(it != new_vendors.end());
+ preset.vendor = &it->second;
+ }
+}
+
void PresetCollection::update_map_alias_to_profile_name()
{
m_map_alias_to_profile_name.clear();
diff --git a/src/libslic3r/Preset.hpp b/src/libslic3r/Preset.hpp
index b6d44d58ff5..d81717f0e00 100644
--- a/src/libslic3r/Preset.hpp
+++ b/src/libslic3r/Preset.hpp
@@ -115,13 +115,11 @@ class Preset
TYPE_COUNT,
};
- Preset(Type type, const std::string &name, bool is_default = false) : type(type), is_default(is_default), name(name) {}
-
Type type = TYPE_INVALID;
// The preset represents a "default" set of properties,
// pulled from the default values of the PrintConfig (see PrintConfigDef for their definitions).
- bool is_default;
+ bool is_default = false;
// External preset points to a configuration, which has been loaded but not imported
// into the Slic3r default configuration location.
bool is_external = false;
@@ -233,6 +231,9 @@ class Preset
static std::string remove_invalid_keys(DynamicPrintConfig &config, const DynamicPrintConfig &default_config);
protected:
+ Preset(Type type, const std::string &name, bool is_default = false) : type(type), is_default(is_default), name(name) {}
+ Preset() = default;
+
friend class PresetCollection;
friend class PresetBundle;
};
@@ -256,7 +257,6 @@ class PresetCollection
public:
// Initialize the PresetCollection with the "- default -" preset.
PresetCollection(Preset::Type type, const std::vector &keys, const Slic3r::StaticPrintConfig &defaults, const std::string &default_name = "- default -");
- ~PresetCollection();
typedef std::deque::iterator Iterator;
typedef std::deque::const_iterator ConstIterator;
@@ -460,6 +460,15 @@ class PresetCollection
size_t num_default_presets() { return m_num_default_presets; }
protected:
+ PresetCollection() = default;
+ // Copy constructor and copy operators are not to be used from outside PresetBundle,
+ // as the Profile::vendor points to an instance of VendorProfile stored at parent PresetBundle!
+ PresetCollection(const PresetCollection &other) = default;
+ PresetCollection& operator=(const PresetCollection &other) = default;
+ // After copying a collection with the default operators above, call this function
+ // to adjust Profile::vendor pointers.
+ void update_vendor_ptrs_after_copy(const VendorMap &vendors);
+
// Select a preset, if it exists. If it does not exist, select an invalid (-1) index.
// This is a temporary state, which shall be fixed immediately by the following step.
bool select_preset_by_name_strict(const std::string &name);
@@ -474,10 +483,6 @@ class PresetCollection
void update_map_system_profile_renamed();
private:
- PresetCollection();
- PresetCollection(const PresetCollection &other);
- PresetCollection& operator=(const PresetCollection &other);
-
// Find a preset position in the sorted list of presets.
// The "-- default -- " preset is always the first, so it needs
// to be handled differently.
@@ -507,9 +512,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.
@@ -531,7 +536,7 @@ class PresetCollection
// Path to the directory to store the config files into.
std::string m_dir_path;
- // to access select_preset_by_name_strict()
+ // to access select_preset_by_name_strict() and the default & copy constructors.
friend class PresetBundle;
};
@@ -542,9 +547,17 @@ class PrinterPresetCollection : public PresetCollection
public:
PrinterPresetCollection(Preset::Type type, const std::vector &keys, const Slic3r::StaticPrintConfig &defaults, const std::string &default_name = "- default -") :
PresetCollection(type, keys, defaults, default_name) {}
+
const Preset& default_preset_for(const DynamicPrintConfig &config) const override;
const Preset* find_by_model_id(const std::string &model_id) const;
+
+private:
+ PrinterPresetCollection() = default;
+ PrinterPresetCollection(const PrinterPresetCollection &other) = default;
+ PrinterPresetCollection& operator=(const PrinterPresetCollection &other) = default;
+
+ friend class PresetBundle;
};
namespace PresetUtils {
@@ -634,7 +647,6 @@ class PhysicalPrinterCollection
{
public:
PhysicalPrinterCollection(const std::vector& keys);
- ~PhysicalPrinterCollection() {}
typedef std::deque::iterator Iterator;
typedef std::deque::const_iterator ConstIterator;
@@ -725,7 +737,9 @@ class PhysicalPrinterCollection
const DynamicPrintConfig& default_config() const { return m_default_config; }
private:
- PhysicalPrinterCollection& operator=(const PhysicalPrinterCollection& other);
+ friend class PresetBundle;
+ PhysicalPrinterCollection() = default;
+ PhysicalPrinterCollection& operator=(const PhysicalPrinterCollection& other) = default;
// Find a physical printer position in the sorted list of printers.
// The name of a printer should be unique and case insensitive
diff --git a/src/libslic3r/PresetBundle.cpp b/src/libslic3r/PresetBundle.cpp
index d9b1ed76ef8..bfc1e222c7a 100644
--- a/src/libslic3r/PresetBundle.cpp
+++ b/src/libslic3r/PresetBundle.cpp
@@ -105,8 +105,33 @@ PresetBundle::PresetBundle() :
this->project_config.apply_only(FullPrintConfig::defaults(), s_project_options);
}
-PresetBundle::~PresetBundle()
+PresetBundle::PresetBundle(const PresetBundle &rhs)
{
+ *this = rhs;
+}
+
+PresetBundle& PresetBundle::operator=(const PresetBundle &rhs)
+{
+ prints = rhs.prints;
+ sla_prints = rhs.sla_prints;
+ filaments = rhs.filaments;
+ sla_materials = rhs.sla_materials;
+ printers = rhs.printers;
+ physical_printers = rhs.physical_printers;
+
+ filament_presets = rhs.filament_presets;
+ project_config = rhs.project_config;
+ vendors = rhs.vendors;
+ obsolete_presets = rhs.obsolete_presets;
+
+ // Adjust Preset::vendor pointers to point to the copied vendors map.
+ prints .update_vendor_ptrs_after_copy(this->vendors);
+ sla_prints .update_vendor_ptrs_after_copy(this->vendors);
+ filaments .update_vendor_ptrs_after_copy(this->vendors);
+ sla_materials.update_vendor_ptrs_after_copy(this->vendors);
+ printers .update_vendor_ptrs_after_copy(this->vendors);
+
+ return *this;
}
void PresetBundle::reset(bool delete_files)
diff --git a/src/libslic3r/PresetBundle.hpp b/src/libslic3r/PresetBundle.hpp
index 5d7cc84ba2d..f98b9653320 100644
--- a/src/libslic3r/PresetBundle.hpp
+++ b/src/libslic3r/PresetBundle.hpp
@@ -15,7 +15,8 @@ class PresetBundle
{
public:
PresetBundle();
- ~PresetBundle();
+ PresetBundle(const PresetBundle &rhs);
+ PresetBundle& operator=(const PresetBundle &rhs);
// Remove all the presets but the "-- default --".
// Optionally remove all the files referenced by the presets from the user profile directory.
diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp
index 8e6b1c5ef2c..9c30ac330c5 100644
--- a/src/slic3r/GUI/MainFrame.cpp
+++ b/src/slic3r/GUI/MainFrame.cpp
@@ -37,6 +37,7 @@
#include
#include "GUI_App.hpp"
+#include "UnsavedChangesDialog.hpp"
#ifdef _WIN32
#include
@@ -1190,6 +1191,10 @@ void MainFrame::init_menubar_as_editor()
windowMenu->AppendSeparator();
append_menu_item(windowMenu, wxID_ANY, _L("Open new instance") + "\tCtrl+Shift+I", _L("Open a new PrusaSlicer instance"),
[this](wxCommandEvent&) { start_new_slicer(); }, "", nullptr, [this]() {return m_plater != nullptr && wxGetApp().app_config->get("single_instance") != "1"; }, this);
+
+ windowMenu->AppendSeparator();
+ append_menu_item(windowMenu, wxID_ANY, _L("Compare presets")/* + "\tCtrl+F"*/, _L("Compare presets"),
+ [this](wxCommandEvent&) { diff_dialog.show();}, "compare", nullptr, []() {return true; }, this);
}
// View menu
diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp
index 9504376b45a..c39527409c6 100644
--- a/src/slic3r/GUI/MainFrame.hpp
+++ b/src/slic3r/GUI/MainFrame.hpp
@@ -16,6 +16,7 @@
#include "GUI_Utils.hpp"
#include "Event.hpp"
+#include "UnsavedChangesDialog.hpp"
class wxNotebook;
class wxProgressDialog;
@@ -190,6 +191,7 @@ class MainFrame : public DPIFrame
Plater* m_plater { nullptr };
wxNotebook* m_tabpanel { nullptr };
SettingsDialog m_settings_dialog;
+ DiffPresetDialog diff_dialog;
wxWindow* m_plater_page{ nullptr };
wxProgressDialog* m_progress_dialog { nullptr };
PrintHostQueueDialog* m_printhost_queue_dlg;
diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp
index 60bb9a5aeb6..38d45f1720d 100644
--- a/src/slic3r/GUI/PresetComboBoxes.cpp
+++ b/src/slic3r/GUI/PresetComboBoxes.cpp
@@ -62,12 +62,12 @@ namespace GUI {
* control size calculation methods (virtual) are overridden.
**/
-PresetComboBox::PresetComboBox(wxWindow* parent, Preset::Type preset_type, const wxSize& size) :
+PresetComboBox::PresetComboBox(wxWindow* parent, Preset::Type preset_type, const wxSize& size, PresetBundle* preset_bundle/* = nullptr*/) :
wxBitmapComboBox(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, size, 0, nullptr, wxCB_READONLY),
m_type(preset_type),
m_last_selected(wxNOT_FOUND),
m_em_unit(em_unit(this)),
- m_preset_bundle(wxGetApp().preset_bundle)
+ m_preset_bundle(preset_bundle ? preset_bundle : wxGetApp().preset_bundle)
{
SetFont(wxGetApp().normal_font());
#ifdef _WIN32
@@ -208,6 +208,21 @@ void PresetComboBox::update_selection()
#endif
}
+static std::string suffix(const Preset& preset)
+{
+ return (preset.is_dirty ? Preset::suffix_modified() : "");
+}
+
+static std::string suffix(Preset* preset)
+{
+ return (preset->is_dirty ? Preset::suffix_modified() : "");
+}
+
+wxString PresetComboBox::get_preset_name(const Preset & preset)
+{
+ return from_u8(preset.name/* + suffix(preset)*/);
+}
+
void PresetComboBox::update(std::string select_preset_name)
{
Freeze();
@@ -226,7 +241,7 @@ void PresetComboBox::update(std::string select_preset_name)
for (size_t i = presets.front().is_visible ? 0 : m_collection->num_default_presets(); i < presets.size(); ++i)
{
const Preset& preset = presets[i];
- if (!preset.is_visible || !preset.is_compatible)
+ if (!m_show_all && (!preset.is_visible || !preset.is_compatible))
continue;
// marker used for disable incompatible printer models for the selected physical printer
@@ -246,17 +261,17 @@ void PresetComboBox::update(std::string select_preset_name)
assert(bmp);
if (!is_enabled)
- incomp_presets.emplace(wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), bmp);
+ incomp_presets.emplace(get_preset_name(preset), bmp);
else if (preset.is_default || preset.is_system)
{
- Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), *bmp);
+ Append(get_preset_name(preset), *bmp);
validate_selection(preset.name == select_preset_name);
}
else
{
- nonsys_presets.emplace(wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), std::pair(bmp, is_enabled));
+ nonsys_presets.emplace(get_preset_name(preset), std::pair(bmp, is_enabled));
if (preset.name == select_preset_name || (select_preset_name.empty() && is_enabled))
- selected = wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str());
+ selected = get_preset_name(preset);
}
if (i + 1 == m_collection->num_default_presets())
set_label_marker(Append(separator(L("System presets")), wxNullBitmap));
@@ -329,11 +344,22 @@ bool PresetComboBox::del_physical_printer(const wxString& note_string/* = wxEmpt
return true;
}
+void PresetComboBox::show_all(bool show_all)
+{
+ m_show_all = show_all;
+ update();
+}
+
void PresetComboBox::update()
{
this->update(into_u8(this->GetString(this->GetSelection())));
}
+void PresetComboBox::update_from_bundle()
+{
+ this->update(m_collection->get_selected_preset().name);
+}
+
void PresetComboBox::msw_rescale()
{
m_em_unit = em_unit(this);
@@ -745,6 +771,12 @@ void PlaterPresetComboBox::show_edit_menu()
wxGetApp().plater()->PopupMenu(menu);
}
+wxString PlaterPresetComboBox::get_preset_name(const Preset& preset)
+{
+ std::string name = preset.alias.empty() ? preset.name : preset.alias;
+ return from_u8(name + suffix(preset));
+}
+
// Only the compatible presets are shown.
// If an incompatible preset is selected, it is shown as well.
void PlaterPresetComboBox::update()
@@ -821,17 +853,17 @@ void PlaterPresetComboBox::update()
const std::string name = preset.alias.empty() ? preset.name : preset.alias;
if (preset.is_default || preset.is_system) {
- Append(wxString::FromUTF8((name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), *bmp);
+ Append(get_preset_name(preset), *bmp);
validate_selection(is_selected);
if (is_selected)
- tooltip = wxString::FromUTF8(preset.name.c_str());
+ tooltip = from_u8(preset.name);
}
else
{
- nonsys_presets.emplace(wxString::FromUTF8((name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), bmp);
+ nonsys_presets.emplace(get_preset_name(preset), bmp);
if (is_selected) {
- selected_user_preset = wxString::FromUTF8((name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str());
- tooltip = wxString::FromUTF8(preset.name.c_str());
+ selected_user_preset = get_preset_name(preset);
+ tooltip = from_u8(preset.name);
}
}
if (i + 1 == m_collection->num_default_presets())
@@ -862,7 +894,7 @@ void PlaterPresetComboBox::update()
wxBitmap* bmp = get_bmp(main_icon_name, wide_icons, main_icon_name);
assert(bmp);
- set_label_marker(Append(wxString::FromUTF8((it->get_full_name(preset_name) + (preset->is_dirty ? Preset::suffix_modified() : "")).c_str()), *bmp), LABEL_ITEM_PHYSICAL_PRINTER);
+ set_label_marker(Append(from_u8(it->get_full_name(preset_name) + suffix(preset)), *bmp), LABEL_ITEM_PHYSICAL_PRINTER);
validate_selection(ph_printers.is_selected(it, preset_name));
}
}
@@ -946,6 +978,11 @@ TabPresetComboBox::TabPresetComboBox(wxWindow* parent, Preset::Type preset_type)
});
}
+wxString TabPresetComboBox::get_preset_name(const Preset& preset)
+{
+ return from_u8(preset.name + suffix(preset));
+}
+
// Update the choice UI from the list of presets.
// If show_incompatible, all presets are shown, otherwise only the compatible presets are shown.
// If an incompatible preset is selected, it is shown as well.
@@ -991,7 +1028,7 @@ void TabPresetComboBox::update()
assert(bmp);
if (preset.is_default || preset.is_system) {
- int item_id = Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), *bmp);
+ int item_id = Append(get_preset_name(preset), *bmp);
if (!is_enabled)
set_label_marker(item_id, LABEL_ITEM_DISABLED);
validate_selection(i == idx_selected);
@@ -999,9 +1036,9 @@ void TabPresetComboBox::update()
else
{
std::pair pair(bmp, is_enabled);
- nonsys_presets.emplace(wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), std::pair(bmp, is_enabled));
+ nonsys_presets.emplace(get_preset_name(preset), std::pair(bmp, is_enabled));
if (i == idx_selected)
- selected = wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str());
+ selected = get_preset_name(preset);
}
if (i + 1 == m_collection->num_default_presets())
set_label_marker(Append(separator(L("System presets")), wxNullBitmap));
@@ -1035,7 +1072,7 @@ void TabPresetComboBox::update()
wxBitmap* bmp = get_bmp(main_icon_name, main_icon_name, "", true, true, false);
assert(bmp);
- set_label_marker(Append(wxString::FromUTF8((it->get_full_name(preset_name) + (preset->is_dirty ? Preset::suffix_modified() : "")).c_str()), *bmp), LABEL_ITEM_PHYSICAL_PRINTER);
+ set_label_marker(Append(from_u8(it->get_full_name(preset_name) + suffix(preset)), *bmp), LABEL_ITEM_PHYSICAL_PRINTER);
validate_selection(ph_printers.is_selected(it, preset_name));
}
}
@@ -1082,15 +1119,15 @@ void TabPresetComboBox::update_dirty()
preset_name = PhysicalPrinter::get_preset_name(preset_name);
}
- const Preset* preset = m_collection->find_preset(preset_name, false);
+ Preset* preset = m_collection->find_preset(preset_name, false);
if (preset) {
- std::string new_label = preset->is_dirty ? preset->name + Preset::suffix_modified() : preset->name;
+ std::string new_label = preset->name + suffix(preset);
if (marker == LABEL_ITEM_PHYSICAL_PRINTER)
new_label = ph_printer_name + PhysicalPrinter::separator() + new_label;
if (old_label != new_label)
- SetString(ui_id, wxString::FromUTF8(new_label.c_str()));
+ SetString(ui_id, from_u8(new_label));
}
}
#ifdef __APPLE__
diff --git a/src/slic3r/GUI/PresetComboBoxes.hpp b/src/slic3r/GUI/PresetComboBoxes.hpp
index d3cc6277dbd..6f41c95f4e1 100644
--- a/src/slic3r/GUI/PresetComboBoxes.hpp
+++ b/src/slic3r/GUI/PresetComboBoxes.hpp
@@ -30,8 +30,9 @@ class BitmapCache;
// BitmapComboBox used to presets list on Sidebar and Tabs
class PresetComboBox : public wxBitmapComboBox
{
+ bool m_show_all { false };
public:
- PresetComboBox(wxWindow* parent, Preset::Type preset_type, const wxSize& size = wxDefaultSize);
+ PresetComboBox(wxWindow* parent, Preset::Type preset_type, const wxSize& size = wxDefaultSize, PresetBundle* preset_bundle = nullptr);
~PresetComboBox();
enum LabelItemType {
@@ -58,11 +59,16 @@ class PresetComboBox : public wxBitmapComboBox
bool selection_is_changed_according_to_physical_printers();
void update(std::string select_preset);
+ // select preset which is selected in PreseBundle
+ void update_from_bundle();
void edit_physical_printer();
void add_physical_printer();
bool del_physical_printer(const wxString& note_string = wxEmptyString);
+ virtual wxString get_preset_name(const Preset& preset);
+ Preset::Type get_type() { return m_type; }
+ void show_all(bool show_all);
virtual void update();
virtual void msw_rescale();
@@ -158,6 +164,7 @@ class PlaterPresetComboBox : public PresetComboBox
void show_add_menu();
void show_edit_menu();
+ wxString get_preset_name(const Preset& preset) override;
void update() override;
void msw_rescale() override;
@@ -182,6 +189,7 @@ class TabPresetComboBox : public PresetComboBox
show_incompatible = show_incompatible_presets;
}
+ wxString get_preset_name(const Preset& preset) override;
void update() override;
void update_dirty();
void msw_rescale() override;
diff --git a/src/slic3r/GUI/Search.cpp b/src/slic3r/GUI/Search.cpp
index 2b7b3f3d9d5..03aa11eb69b 100644
--- a/src/slic3r/GUI/Search.cpp
+++ b/src/slic3r/GUI/Search.cpp
@@ -326,6 +326,53 @@ const Option& OptionsSearcher::get_option(const std::string& opt_key) const
return options[it - options.begin()];
}
+static Option create_option(const std::string& opt_key, const wxString& label, Preset::Type type, const GroupAndCategory& gc)
+{
+ wxString suffix;
+ wxString suffix_local;
+ if (gc.category == "Machine limits") {
+ suffix = opt_key.back() == '1' ? L("Stealth") : L("Normal");
+ suffix_local = " " + _(suffix);
+ suffix = " " + suffix;
+ }
+
+ wxString category = gc.category;
+ if (type == Preset::TYPE_PRINTER && category.Contains("Extruder ")) {
+ std::string opt_idx = opt_key.substr(opt_key.find("#") + 1);
+ category = wxString::Format("%s %d", "Extruder", atoi(opt_idx.c_str()) + 1);
+ }
+
+ 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(category, type).ToStdWstring() };
+}
+
+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))
+ return options[it - options.begin()];
+ if (groups_and_categories.find(opt_key) == groups_and_categories.end()) {
+ size_t pos = opt_key.find('#');
+ if (pos == std::string::npos)
+ return options[it - options.begin()];
+
+ std::string zero_opt_key = opt_key.substr(0, pos + 1) + "0";
+
+ if(groups_and_categories.find(zero_opt_key) == groups_and_categories.end())
+ return options[it - options.begin()];
+
+ return create_option(opt_key, label, type, groups_and_categories.at(zero_opt_key));
+ }
+
+ const GroupAndCategory& gc = groups_and_categories.at(opt_key);
+ if (gc.group.IsEmpty() || gc.category.IsEmpty())
+ return options[it - options.begin()];
+
+ return create_option(opt_key, label, type, gc);
+}
+
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 4243595a09d..cdcc98d0613 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) {
@@ -2066,11 +2072,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)
@@ -2105,7 +2118,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();
@@ -2289,7 +2303,7 @@ void TabPrinter::build_fff()
build_preset_description_line(optgroup.get());
- build_unregular_pages();
+ build_unregular_pages(true);
}
void TabPrinter::build_sla()
@@ -2395,7 +2409,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);
}
@@ -2470,7 +2486,7 @@ PageShp TabPrinter::build_kinematics_page()
* but "Machine limits" and "Single extruder MM setup" too
* (These pages can changes according to the another values of a current preset)
* */
-void TabPrinter::build_unregular_pages()
+void TabPrinter::build_unregular_pages(bool from_initial_build/* = false*/)
{
size_t n_before_extruders = 2; // Count of pages before Extruder pages
bool is_marlin_flavor = m_config->option>("gcode_flavor")->value == gcfMarlin;
@@ -2481,18 +2497,6 @@ void TabPrinter::build_unregular_pages()
* */
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
@@ -2504,12 +2508,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 || from_initial_build)) {
auto page = build_kinematics_page();
-#ifdef __WXMSW__
-// layout_page(page);
-#endif
- m_pages.insert(m_pages.begin() + n_before_extruders, page);
+ if (from_initial_build)
+ page->clear();
+ else
+ m_pages.insert(m_pages.begin() + n_before_extruders, page);
}
if (is_marlin_flavor)
@@ -2527,7 +2531,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 (from_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"));
@@ -2536,8 +2541,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 (from_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
@@ -2642,10 +2651,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
@@ -2656,6 +2661,10 @@ void TabPrinter::build_unregular_pages()
Thaw();
m_extruders_count_old = m_extruders_count;
+
+ if (m_printer_technology == ptSLA/*from_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
@@ -3187,6 +3196,9 @@ void Tab::select_preset(std::string preset_name, bool delete_current /*=false*/,
load_current_preset();
}
+
+ if (technology_changed)
+ wxGetApp().mainframe->diff_dialog.update_presets();
}
// If the current preset is dirty, the user is asked whether the changes may be discarded.
@@ -3374,6 +3386,11 @@ void Tab::OnKeyDown(wxKeyEvent& event)
event.Skip();
}
+void Tab::compare_preset()
+{
+ wxGetApp().mainframe->diff_dialog.show(m_type);
+}
+
// 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.
@@ -3443,6 +3460,9 @@ void Tab::save_preset(std::string name /*= ""*/, bool detach)
for (Preset::Type preset_type : dependent)
wxGetApp().get_tab(preset_type)->update_tab_ui();
}
+
+ // update preset comboboxes in DiffPresetDlg
+ wxGetApp().mainframe->diff_dialog.update_presets(m_type);
}
// Called for a currently selected preset.
diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp
index 29588ba20e0..0358fd3150f 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();
@@ -456,7 +458,7 @@ class TabPrinter : public Tab
void update_pages(); // update m_pages according to printer technology
void extruders_count_changed(size_t extruders_count);
PageShp build_kinematics_page();
- void build_unregular_pages();
+ void build_unregular_pages(bool from_initial_build = false);
void on_preset_loaded() override;
void init_options_list() override;
void msw_rescale() override;
diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp
index 68487921d58..2727a298269 100644
--- a/src/slic3r/GUI/UnsavedChangesDialog.cpp
+++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp
@@ -7,6 +7,8 @@
#include
#include
+#include
+
#include "libslic3r/PrintConfig.hpp"
#include "libslic3r/PresetBundle.hpp"
#include "format.hpp"
@@ -22,6 +24,7 @@
//#include "fts_fuzzy_match.h"
#include "BitmapCache.hpp"
+#include "PresetComboBoxes.hpp"
using boost::optional;
@@ -36,7 +39,7 @@ namespace Slic3r {
namespace GUI {
// ----------------------------------------------------------------------------
-// ModelNode: a node inside UnsavedChangesModel
+// ModelNode: a node inside DiffModel
// ----------------------------------------------------------------------------
static const std::map type_icon_names = {
@@ -215,21 +218,15 @@ void ModelNode::UpdateIcons()
// ----------------------------------------------------------------------------
-// UnsavedChangesModel
+// DiffModel
// ----------------------------------------------------------------------------
-UnsavedChangesModel::UnsavedChangesModel(wxWindow* parent) :
+DiffModel::DiffModel(wxWindow* parent) :
m_parent_win(parent)
{
}
-UnsavedChangesModel::~UnsavedChangesModel()
-{
- for (ModelNode* preset_node : m_preset_nodes)
- delete preset_node;
-}
-
-wxDataViewItem UnsavedChangesModel::AddPreset(Preset::Type type, wxString preset_name, PrinterTechnology pt)
+wxDataViewItem DiffModel::AddPreset(Preset::Type type, wxString preset_name, PrinterTechnology pt)
{
// "color" strings
color_string(preset_name, def_text_color());
@@ -245,7 +242,7 @@ wxDataViewItem UnsavedChangesModel::AddPreset(Preset::Type type, wxString preset
return child;
}
-ModelNode* UnsavedChangesModel::AddOption(ModelNode* group_node, wxString option_name, wxString old_value, wxString new_value)
+ModelNode* DiffModel::AddOption(ModelNode* group_node, wxString option_name, wxString old_value, wxString new_value)
{
group_node->Append(std::make_unique(group_node, option_name, old_value, new_value));
ModelNode* option = group_node->GetChildren().back().get();
@@ -256,7 +253,7 @@ ModelNode* UnsavedChangesModel::AddOption(ModelNode* group_node, wxString option
return option;
}
-ModelNode* UnsavedChangesModel::AddOptionWithGroup(ModelNode* category_node, wxString group_name, wxString option_name, wxString old_value, wxString new_value)
+ModelNode* DiffModel::AddOptionWithGroup(ModelNode* category_node, wxString group_name, wxString option_name, wxString old_value, wxString new_value)
{
category_node->Append(std::make_unique(category_node, group_name));
ModelNode* group_node = category_node->GetChildren().back().get();
@@ -265,7 +262,7 @@ ModelNode* UnsavedChangesModel::AddOptionWithGroup(ModelNode* category_node, wxS
return AddOption(group_node, option_name, old_value, new_value);
}
-ModelNode* UnsavedChangesModel::AddOptionWithGroupAndCategory(ModelNode* preset_node, wxString category_name, wxString group_name,
+ModelNode* DiffModel::AddOptionWithGroupAndCategory(ModelNode* preset_node, wxString category_name, wxString group_name,
wxString option_name, wxString old_value, wxString new_value, const std::string category_icon_name)
{
preset_node->Append(std::make_unique(preset_node, category_name, category_icon_name));
@@ -275,7 +272,7 @@ ModelNode* UnsavedChangesModel::AddOptionWithGroupAndCategory(ModelNode* preset_
return AddOptionWithGroup(category_node, group_name, option_name, old_value, new_value);
}
-wxDataViewItem UnsavedChangesModel::AddOption(Preset::Type type, wxString category_name, wxString group_name, wxString option_name,
+wxDataViewItem DiffModel::AddOption(Preset::Type type, wxString category_name, wxString group_name, wxString option_name,
wxString old_value, wxString new_value, const std::string category_icon_name)
{
// "color" strings
@@ -288,7 +285,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 +298,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);
@@ -336,7 +333,7 @@ static void update_parents(ModelNode* node)
}
}
-void UnsavedChangesModel::UpdateItemEnabling(wxDataViewItem item)
+void DiffModel::UpdateItemEnabling(wxDataViewItem item)
{
assert(item.IsOk());
ModelNode* node = static_cast(item.GetID());
@@ -346,14 +343,14 @@ void UnsavedChangesModel::UpdateItemEnabling(wxDataViewItem item)
update_parents(node);
}
-bool UnsavedChangesModel::IsEnabledItem(const wxDataViewItem& item)
+bool DiffModel::IsEnabledItem(const wxDataViewItem& item)
{
assert(item.IsOk());
ModelNode* node = static_cast(item.GetID());
return node->IsToggled();
}
-void UnsavedChangesModel::GetValue(wxVariant& variant, const wxDataViewItem& item, unsigned int col) const
+void DiffModel::GetValue(wxVariant& variant, const wxDataViewItem& item, unsigned int col) const
{
assert(item.IsOk());
@@ -386,11 +383,11 @@ void UnsavedChangesModel::GetValue(wxVariant& variant, const wxDataViewItem& ite
#endif //__linux__
default:
- wxLogError("UnsavedChangesModel::GetValue: wrong column %d", col);
+ wxLogError("DiffModel::GetValue: wrong column %d", col);
}
}
-bool UnsavedChangesModel::SetValue(const wxVariant& variant, const wxDataViewItem& item, unsigned int col)
+bool DiffModel::SetValue(const wxVariant& variant, const wxDataViewItem& item, unsigned int col)
{
assert(item.IsOk());
@@ -440,12 +437,12 @@ bool UnsavedChangesModel::SetValue(const wxVariant& variant, const wxDataViewIte
return true; }
#endif //__linux__
default:
- wxLogError("UnsavedChangesModel::SetValue: wrong column");
+ wxLogError("DiffModel::SetValue: wrong column");
}
return false;
}
-bool UnsavedChangesModel::IsEnabled(const wxDataViewItem& item, unsigned int col) const
+bool DiffModel::IsEnabled(const wxDataViewItem& item, unsigned int col) const
{
assert(item.IsOk());
if (col == colToggle)
@@ -455,7 +452,7 @@ bool UnsavedChangesModel::IsEnabled(const wxDataViewItem& item, unsigned int col
return (static_cast(item.GetID()))->IsToggled();
}
-wxDataViewItem UnsavedChangesModel::GetParent(const wxDataViewItem& item) const
+wxDataViewItem DiffModel::GetParent(const wxDataViewItem& item) const
{
// the invisible root node has no parent
if (!item.IsOk())
@@ -463,14 +460,13 @@ wxDataViewItem UnsavedChangesModel::GetParent(const wxDataViewItem& item) const
ModelNode* node = static_cast(item.GetID());
- // "MyMusic" also has no parent
if (node->IsRoot())
return wxDataViewItem(nullptr);
return wxDataViewItem((void*)node->GetParent());
}
-bool UnsavedChangesModel::IsContainer(const wxDataViewItem& item) const
+bool DiffModel::IsContainer(const wxDataViewItem& item) const
{
// the invisble root node can have children
if (!item.IsOk())
@@ -480,23 +476,19 @@ bool UnsavedChangesModel::IsContainer(const wxDataViewItem& item) const
return node->IsContainer();
}
-unsigned int UnsavedChangesModel::GetChildren(const wxDataViewItem& parent, wxDataViewItemArray& array) const
+unsigned int DiffModel::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();
}
-wxString UnsavedChangesModel::GetColumnType(unsigned int col) const
+wxString DiffModel::GetColumnType(unsigned int col) const
{
switch (col)
{
@@ -520,12 +512,232 @@ static void rescale_children(ModelNode* parent)
}
}
-void UnsavedChangesModel::Rescale()
+void DiffModel::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 DiffModel::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 DiffModel::Clear()
+{
+ while (!m_preset_nodes.empty())
+ Delete(wxDataViewItem(m_preset_nodes.back().get()));
+}
+
+
+static std::string get_pure_opt_key(std::string opt_key)
+{
+ int pos = opt_key.find("#");
+ if (pos > 0)
+ boost::erase_tail(opt_key, opt_key.size() - pos);
+ return opt_key;
+}
+
+// ----------------------------------------------------------------------------
+// DiffViewCtrl
+// ----------------------------------------------------------------------------
+
+DiffViewCtrl::DiffViewCtrl(wxWindow* parent, wxSize size)
+ : wxDataViewCtrl(parent, wxID_ANY, wxDefaultPosition, size, wxBORDER_SIMPLE | wxDV_VARIABLE_LINE_HEIGHT | wxDV_ROW_LINES),
+ m_em_unit(em_unit(parent))
+{
+ model = new DiffModel(parent);
+ this->AssociateModel(model);
+ model->SetAssociatedControl(this);
+
+ this->Bind(wxEVT_DATAVIEW_ITEM_CONTEXT_MENU, &DiffViewCtrl::context_menu, this);
+ this->Bind(wxEVT_DATAVIEW_ITEM_ACTIVATED, &DiffViewCtrl::context_menu, this);
+ this->Bind(wxEVT_DATAVIEW_ITEM_VALUE_CHANGED, &DiffViewCtrl::item_value_changed, this);
+}
+
+void DiffViewCtrl::AppendBmpTextColumn(const wxString& label, unsigned model_column, int width, bool set_expander/* = false*/)
+{
+ m_columns_width.emplace(this->GetColumnCount(), width);
+#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 * m_em_unit, wxALIGN_TOP, wxDATAVIEW_COL_RESIZABLE);
+#endif //__linux__
+ this->AppendColumn(column);
+ if (set_expander)
+ this->SetExpanderColumn(column);
+
+}
+
+void DiffViewCtrl::AppendToggleColumn_(const wxString& label, unsigned model_column, int width)
+{
+ m_columns_width.emplace(this->GetColumnCount(), width);
+ AppendToggleColumn(label, model_column, wxDATAVIEW_CELL_ACTIVATABLE, width * m_em_unit);
+}
+
+void DiffViewCtrl::Rescale(int em /*= 0*/)
+{
+ if (em > 0) {
+ for (auto item : m_columns_width)
+ GetColumn(item.first)->SetWidth(item.second * em);
+ m_em_unit = em;
}
+
+ model->Rescale();
+ Refresh();
+}
+
+
+void DiffViewCtrl::Append( const std::string& opt_key, Preset::Type type,
+ wxString category_name, wxString group_name, wxString option_name,
+ wxString old_value, wxString new_value, const std::string category_icon_name)
+{
+ ItemData item_data = { opt_key, option_name, old_value, new_value, type };
+
+ wxString old_val = get_short_string(item_data.old_val);
+ wxString new_val = get_short_string(item_data.new_val);
+ if (old_val != item_data.old_val || new_val != item_data.new_val)
+ item_data.is_long = true;
+
+ m_items_map.emplace(model->AddOption(type, category_name, group_name, option_name, old_val, new_val, category_icon_name), item_data);
+
+}
+
+void DiffViewCtrl::Clear()
+{
+ model->Clear();
+ m_items_map.clear();
+}
+
+wxString DiffViewCtrl::get_short_string(wxString full_string)
+{
+ int max_len = 30;
+ if (full_string.IsEmpty() || full_string.StartsWith("#") ||
+ (full_string.Find("\n") == wxNOT_FOUND && full_string.Length() < max_len))
+ return full_string;
+
+ m_has_long_strings = true;
+
+ int n_pos = full_string.Find("\n");
+ if (n_pos != wxNOT_FOUND && n_pos < max_len)
+ max_len = n_pos;
+
+ full_string.Truncate(max_len);
+ return full_string + dots;
+}
+
+void DiffViewCtrl::context_menu(wxDataViewEvent& event)
+{
+ if (!m_has_long_strings)
+ return;
+
+ wxDataViewItem item = event.GetItem();
+ if (!item) {
+ wxPoint mouse_pos = wxGetMousePosition() - this->GetScreenPosition();
+ wxDataViewColumn* col = nullptr;
+ this->HitTest(mouse_pos, item, col);
+
+ if (!item)
+ item = this->GetSelection();
+
+ if (!item)
+ return;
+ }
+
+ auto it = m_items_map.find(item);
+ if (it == m_items_map.end() || !it->second.is_long)
+ return;
+ FullCompareDialog(it->second.opt_name, it->second.old_val, it->second.new_val).ShowModal();
+
+#ifdef __WXOSX__
+ wxWindow* parent = this->GetParent();
+ if (parent && parent->IsShown()) {
+ // if this dialog is shown it have to be Hide and show again to be placed on the very Top of windows
+ parent->Hide();
+ parent->Show();
+ }
+#endif // __WXOSX__
+}
+
+void DiffViewCtrl::item_value_changed(wxDataViewEvent& event)
+{
+ if (event.GetColumn() != DiffModel::colToggle)
+ return;
+
+ wxDataViewItem item = event.GetItem();
+
+ model->UpdateItemEnabling(item);
+ Refresh();
+
+ // update an enabling of the "save/move" buttons
+ m_empty_selection = selected_options().empty();
+}
+
+std::vector DiffViewCtrl::unselected_options(Preset::Type type)
+{
+ std::vector ret;
+
+ for (auto item : m_items_map) {
+ if (item.second.opt_key == "extruders_count")
+ continue;
+ if (item.second.type == type && !model->IsEnabledItem(item.first))
+ ret.emplace_back(get_pure_opt_key(item.second.opt_key));
+ }
+
+ return ret;
+}
+
+std::vector DiffViewCtrl::selected_options()
+{
+ std::vector ret;
+
+ for (auto item : m_items_map)
+ if (model->IsEnabledItem(item.first))
+ ret.emplace_back(get_pure_opt_key(item.second.opt_key));
+
+ return ret;
}
@@ -593,35 +805,11 @@ void UnsavedChangesDialog::build(Preset::Type type, PresetCollection* dependent_
m_action_line = new wxStaticText(this, wxID_ANY, "");
m_action_line->SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Bold());
- m_tree = new wxDataViewCtrl(this, wxID_ANY, wxDefaultPosition, wxSize(em * 60, em * 30), 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);
-
- m_tree->AppendToggleColumn(L"\u2714", UnsavedChangesModel::colToggle, wxDATAVIEW_CELL_ACTIVATABLE, (wxLinux ? 8 : 6) * em);
-
- 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, 28 * em);
- append_bmp_text_column(_L("Old Value"), UnsavedChangesModel::colOldValue, 12 * em);
- append_bmp_text_column(_L("New Value"), UnsavedChangesModel::colNewValue, 12 * 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);
+ m_tree = new DiffViewCtrl(this, wxSize(em * 60, em * 30));
+ m_tree->AppendToggleColumn_(L"\u2714" , DiffModel::colToggle, wxLinux ? 8 : 6);
+ m_tree->AppendBmpTextColumn("" , DiffModel::colIconText, 28);
+ m_tree->AppendBmpTextColumn(_L("Old Value"), DiffModel::colOldValue, 12);
+ m_tree->AppendBmpTextColumn(_L("New Value"), DiffModel::colNewValue, 12);
// Add Buttons
wxFont btn_font = this->GetFont().Scaled(1.4f);
@@ -641,7 +829,7 @@ void UnsavedChangesDialog::build(Preset::Type type, PresetCollection* dependent_
close(close_act);
});
if (process_enable)
- (*btn)->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(!m_empty_selection); });
+ (*btn)->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(m_tree->has_selection()); });
(*btn)->Bind(wxEVT_LEAVE_WINDOW, [this](wxMouseEvent& e) { show_info_line(Action::Undef); e.Skip(); });
};
@@ -698,45 +886,6 @@ void UnsavedChangesDialog::build(Preset::Type type, PresetCollection* dependent_
show_info_line(Action::Undef);
}
-void UnsavedChangesDialog::item_value_changed(wxDataViewEvent& event)
-{
- if (event.GetColumn() != UnsavedChangesModel::colToggle)
- return;
-
- wxDataViewItem item = event.GetItem();
-
- m_tree_model->UpdateItemEnabling(item);
- m_tree->Refresh();
-
- // update an enabling of the "save/move" buttons
- m_empty_selection = get_selected_options().empty();
-}
-
-void UnsavedChangesDialog::context_menu(wxDataViewEvent& event)
-{
- if (!m_has_long_strings)
- return;
-
- wxDataViewItem item = event.GetItem();
- if (!item)
- {
- wxPoint mouse_pos = wxGetMousePosition() - m_tree->GetScreenPosition();
- wxDataViewColumn* col = nullptr;
- m_tree->HitTest(mouse_pos, item, col);
-
- if (!item)
- item = m_tree->GetSelection();
-
- if (!item)
- return;
- }
-
- auto it = m_items_map.find(item);
- if (it == m_items_map.end() || !it->second.is_long)
- return;
- FullCompareDialog(it->second.opt_name, it->second.old_val, it->second.new_val).ShowModal();
-}
-
void UnsavedChangesDialog::show_info_line(Action action, std::string preset_name)
{
if (action == Action::Undef && !m_has_long_strings)
@@ -869,12 +1018,15 @@ static int get_id_from_opt_key(std::string opt_key)
return 0;
}
-static std::string get_pure_opt_key(std::string opt_key)
+static wxString get_full_label(std::string opt_key, const DynamicPrintConfig& config)
{
- int pos = opt_key.find("#");
- if (pos > 0)
- boost::erase_tail(opt_key, opt_key.size() - pos);
- return opt_key;
+ 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)
@@ -894,34 +1046,62 @@ static wxString get_string_value(std::string opt_key, const DynamicPrintConfig&
case coInt:
return from_u8((boost::format("%1%") % config.opt_int(opt_key)).str());
case coInts: {
- int val = is_nullable ?
- config.opt(opt_key)->get_at(opt_idx) :
- config.opt(opt_key)->get_at(opt_idx);
- return from_u8((boost::format("%1%") % val).str());
+ if (is_nullable) {
+ auto values = config.opt(opt_key);
+ if (opt_idx < values->size())
+ return from_u8((boost::format("%1%") % values->get_at(opt_idx)).str());
+ }
+ else {
+ auto values = config.opt(opt_key);
+ if (opt_idx < values->size())
+ return from_u8((boost::format("%1%") % values->get_at(opt_idx)).str());
+ }
+ return _L("Undef");
}
case coBool:
return config.opt_bool(opt_key) ? "true" : "false";
case coBools: {
- bool val = is_nullable ?
- config.opt(opt_key)->get_at(opt_idx) :
- config.opt(opt_key)->get_at(opt_idx);
- return val ? "true" : "false";
+ if (is_nullable) {
+ auto values = config.opt(opt_key);
+ if (opt_idx < values->size())
+ return values->get_at(opt_idx) ? "true" : "false";
+ }
+ else {
+ auto values = config.opt(opt_key);
+ if (opt_idx < values->size())
+ return values->get_at(opt_idx) ? "true" : "false";
+ }
+ return _L("Undef");
}
case coPercent:
return from_u8((boost::format("%1%%%") % int(config.optptr(opt_key)->getFloat())).str());
case coPercents: {
- double val = is_nullable ?
- config.opt(opt_key)->get_at(opt_idx) :
- config.opt(opt_key)->get_at(opt_idx);
- return from_u8((boost::format("%1%%%") % int(val)).str());
+ if (is_nullable) {
+ auto values = config.opt(opt_key);
+ if (opt_idx < values->size())
+ return from_u8((boost::format("%1%%%") % values->get_at(opt_idx)).str());
+ }
+ else {
+ auto values = config.opt(opt_key);
+ if (opt_idx < values->size())
+ return from_u8((boost::format("%1%%%") % values->get_at(opt_idx)).str());
+ }
+ return _L("Undef");
}
case coFloat:
return double_to_string(config.opt_float(opt_key));
case coFloats: {
- double val = is_nullable ?
- config.opt(opt_key)->get_at(opt_idx) :
- config.opt(opt_key)->get_at(opt_idx);
- return double_to_string(val);
+ if (is_nullable) {
+ auto values = config.opt(opt_key);
+ if (opt_idx < values->size())
+ return double_to_string(values->get_at(opt_idx));
+ }
+ else {
+ auto values = config.opt(opt_key);
+ if (opt_idx < values->size())
+ return double_to_string(values->get_at(opt_idx));
+ }
+ return _L("Undef");
}
case coString:
return from_u8(config.opt_string(opt_key));
@@ -936,7 +1116,7 @@ static wxString get_string_value(std::string opt_key, const DynamicPrintConfig&
out.RemoveLast(1);
return out;
}
- if (!strings->empty())
+ if (!strings->empty() && opt_idx < (int)strings->values.size())
return from_u8(strings->get_at(opt_idx));
}
break;
@@ -987,23 +1167,6 @@ static wxString get_string_value(std::string opt_key, const DynamicPrintConfig&
return out;
}
-wxString UnsavedChangesDialog::get_short_string(wxString full_string)
-{
- int max_len = 30;
- if (full_string.IsEmpty() || full_string.StartsWith("#") ||
- (full_string.Find("\n") == wxNOT_FOUND && full_string.Length() < size_t(max_len)))
- return full_string;
-
- m_has_long_strings = true;
-
- int n_pos = full_string.Find("\n");
- if (n_pos != wxNOT_FOUND && n_pos < max_len)
- max_len = n_pos;
-
- full_string.Truncate(max_len);
- return full_string + dots;
-}
-
void UnsavedChangesDialog::update(Preset::Type type, PresetCollection* dependent_presets, const std::string& new_selected_preset, const wxString& header)
{
PresetCollection* presets = dependent_presets;
@@ -1065,7 +1228,7 @@ void UnsavedChangesDialog::update_tree(Preset::Type type, PresetCollection* pres
const std::map& category_icon_map = wxGetApp().get_tab(type)->get_category_icon_map();
- m_tree_model->AddPreset(type, from_u8(presets->get_edited_preset().name), old_pt);
+ m_tree->model->AddPreset(type, from_u8(presets->get_edited_preset().name), old_pt);
// Collect dirty options.
const bool deep_compare = (type == Preset::TYPE_PRINTER || type == Preset::TYPE_SLA_MATERIAL);
@@ -1078,9 +1241,7 @@ void UnsavedChangesDialog::update_tree(Preset::Type type, PresetCollection* pres
wxString old_val = from_u8((boost::format("%1%") % old_config.opt("extruder_colour")->values.size()).str());
wxString new_val = from_u8((boost::format("%1%") % new_config.opt("extruder_colour")->values.size()).str());
- ItemData item_data = { "extruders_count", local_label, old_val, new_val, type };
- m_items_map.emplace(m_tree_model->AddOption(type, _L("General"), _L("Capabilities"), local_label, old_val, new_val, category_icon_map.at("General")), item_data);
-
+ m_tree->Append("extruders_count", type, _L("General"), _L("Capabilities"), local_label, old_val, new_val, category_icon_map.at("General"));
}
for (const std::string& opt_key : dirty_options) {
@@ -1092,43 +1253,12 @@ void UnsavedChangesDialog::update_tree(Preset::Type type, PresetCollection* pres
continue;
}
- ItemData item_data = { opt_key, option.label_local, get_string_value(opt_key, old_config), get_string_value(opt_key, new_config), type };
-
- wxString old_val = get_short_string(item_data.old_val);
- wxString new_val = get_short_string(item_data.new_val);
- if (old_val != item_data.old_val || new_val != item_data.new_val)
- item_data.is_long = true;
-
- m_items_map.emplace(m_tree_model->AddOption(type, option.category_local, option.group_local, option.label_local, old_val, new_val, category_icon_map.at(option.category)), item_data);
+ m_tree->Append(opt_key, type, option.category_local, option.group_local, option.label_local,
+ get_string_value(opt_key, old_config), get_string_value(opt_key, new_config), category_icon_map.at(option.category));
}
}
}
-std::vector UnsavedChangesDialog::get_unselected_options(Preset::Type type)
-{
- std::vector ret;
-
- for (auto item : m_items_map) {
- if (item.second.opt_key == "extruders_count")
- continue;
- if (item.second.type == type && !m_tree_model->IsEnabledItem(item.first))
- ret.emplace_back(get_pure_opt_key(item.second.opt_key));
- }
-
- return ret;
-}
-
-std::vector UnsavedChangesDialog::get_selected_options()
-{
- std::vector ret;
-
- for (auto item : m_items_map)
- if (m_tree_model->IsEnabledItem(item.first))
- ret.emplace_back(get_pure_opt_key(item.second.opt_key));
-
- return ret;
-}
-
void UnsavedChangesDialog::on_dpi_changed(const wxRect& suggested_rect)
{
int em = em_unit();
@@ -1137,16 +1267,10 @@ void UnsavedChangesDialog::on_dpi_changed(const wxRect& suggested_rect)
for (auto btn : { m_save_btn, m_transfer_btn, m_discard_btn } )
if (btn) btn->msw_rescale();
- const wxSize& size = wxSize(80 * em, 30 * em);
+ const wxSize& size = wxSize(70 * em, 30 * em);
SetMinSize(size);
- m_tree->GetColumn(UnsavedChangesModel::colToggle )->SetWidth(6 * em);
- m_tree->GetColumn(UnsavedChangesModel::colIconText)->SetWidth(30 * em);
- m_tree->GetColumn(UnsavedChangesModel::colOldValue)->SetWidth(20 * em);
- m_tree->GetColumn(UnsavedChangesModel::colNewValue)->SetWidth(20 * em);
-
- m_tree_model->Rescale();
- m_tree->Refresh();
+ m_tree->Rescale(em);
Fit();
Refresh();
@@ -1157,8 +1281,7 @@ void UnsavedChangesDialog::on_sys_color_changed()
for (auto btn : { m_save_btn, m_transfer_btn, m_discard_btn } )
btn->msw_rescale();
// msw_rescale updates just icons, so use it
- m_tree_model->Rescale();
- m_tree->Refresh();
+ m_tree->Rescale();
Refresh();
}
@@ -1190,16 +1313,45 @@ FullCompareDialog::FullCompareDialog(const wxString& option_name, const wxString
grid_sizer->Add(text, 0, wxALL, border);
};
- auto add_value = [grid_sizer, border, this](wxString label, bool is_colored = false) {
- wxTextCtrl* text = new wxTextCtrl(this, wxID_ANY, label, wxDefaultPosition, wxSize(300, -1), wxTE_MULTILINE | wxTE_READONLY | wxBORDER_NONE | wxTE_RICH);
+ add_header(_L("Old value"));
+ add_header(_L("New value"));
+
+ auto get_set_from_val = [](wxString str) {
+ if (str.Find("\n") == wxNOT_FOUND)
+ str.Replace(" ", "\n");
+
+ std::set str_set;
+
+ wxStringTokenizer strings(str, "\n");
+ while (strings.HasMoreTokens())
+ str_set.emplace(strings.GetNextToken());
+
+ return str_set;
+ };
+
+ std::set old_set = get_set_from_val(old_value);
+ std::set new_set = get_set_from_val(new_value);
+ std::set old_new_diff_set;
+ std::set new_old_diff_set;
+
+ std::set_difference(old_set.begin(), old_set.end(), new_set.begin(), new_set.end(), std::inserter(old_new_diff_set, old_new_diff_set.begin()));
+ std::set_difference(new_set.begin(), new_set.end(), old_set.begin(), old_set.end(), std::inserter(new_old_diff_set, new_old_diff_set.begin()));
+
+ auto add_value = [grid_sizer, border, this](wxString label, const std::set& diff_set, bool is_colored = false) {
+ wxTextCtrl* text = new wxTextCtrl(this, wxID_ANY, label, wxDefaultPosition, wxSize(400, 400), wxTE_MULTILINE | wxTE_READONLY | wxBORDER_NONE | wxTE_RICH);
text->SetStyle(0, label.Len(), wxTextAttr(is_colored ? wxColour(orange) : wxNullColour, wxNullColour, this->GetFont()));
+
+ for (const wxString& str : diff_set) {
+ int pos = label.First(str);
+ if (pos == wxNOT_FOUND)
+ continue;
+ text->SetStyle(pos, pos + (int)str.Len(), wxTextAttr(is_colored ? wxColour(orange) : wxNullColour, wxNullColour, this->GetFont().Bold()));
+ }
+
grid_sizer->Add(text, 1, wxALL | wxEXPAND, border);
};
-
- add_header(_L("Old value"));
- add_header(_L("New value"));
- add_value(old_value);
- add_value(new_value, true);
+ add_value(old_value, old_new_diff_set);
+ add_value(new_value, new_old_diff_set, true);
sizer->Add(grid_sizer, 1, wxEXPAND);
@@ -1215,6 +1367,384 @@ FullCompareDialog::FullCompareDialog(const wxString& option_name, const wxString
}
+static PresetCollection* get_preset_collection(Preset::Type type, PresetBundle* preset_bundle = nullptr) {
+ if (!preset_bundle)
+ preset_bundle = wxGetApp().preset_bundle;
+ return type == Preset::Type::TYPE_PRINT ? &preset_bundle->prints :
+ type == Preset::Type::TYPE_SLA_PRINT ? &preset_bundle->sla_prints :
+ type == Preset::Type::TYPE_FILAMENT ? &preset_bundle->filaments :
+ type == Preset::Type::TYPE_SLA_MATERIAL ? &preset_bundle->sla_materials :
+ type == Preset::Type::TYPE_PRINTER ? &preset_bundle->printers :
+ nullptr;
+}
+
+//------------------------------------------
+// DiffPresetDialog
+//------------------------------------------
+static std::string get_selection(PresetComboBox* preset_combo)
+{
+ return into_u8(preset_combo->GetString(preset_combo->GetSelection()));
+}
+
+DiffPresetDialog::DiffPresetDialog()
+ : DPIDialog(static_cast(wxGetApp().mainframe), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER),
+ m_pr_technology(wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology())
+{
+ 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();
+
+ assert(wxGetApp().preset_bundle);
+
+ m_preset_bundle_left = std::make_unique(*wxGetApp().preset_bundle);
+ m_preset_bundle_right = std::make_unique(*wxGetApp().preset_bundle);
+
+ 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());
+
+ wxBoxSizer* presets_sizer = new wxBoxSizer(wxVERTICAL);
+
+ for (auto new_type : { Preset::TYPE_PRINT, Preset::TYPE_FILAMENT, Preset::TYPE_SLA_PRINT, Preset::TYPE_SLA_MATERIAL, Preset::TYPE_PRINTER })
+ {
+ const PresetCollection* collection = get_preset_collection(new_type);
+ wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
+ PresetComboBox* presets_left;
+ PresetComboBox* presets_right;
+ ScalableButton* equal_bmp = new ScalableButton(this, wxID_ANY, "equal");
+
+ auto add_preset_combobox = [collection, sizer, new_type, em, this](PresetComboBox** cb_, PresetBundle* preset_bundle) {
+ *cb_ = new PresetComboBox(this, new_type, wxSize(em * 35, -1), preset_bundle);
+ PresetComboBox* cb = (*cb_);
+ cb->set_selection_changed_function([this, new_type, preset_bundle, cb](int selection) {
+ if (m_view_type == Preset::TYPE_INVALID) {
+ std::string preset_name = cb->GetString(selection).ToUTF8().data();
+ update_compatibility(Preset::remove_suffix_modified(preset_name), new_type, preset_bundle);
+ }
+ update_tree();
+ });
+ if (collection->get_selected_idx() != (size_t)-1)
+ cb->update(collection->get_selected_preset().name);
+
+ sizer->Add(cb, 1);
+ cb->Show(new_type == Preset::TYPE_PRINTER);
+ };
+ add_preset_combobox(&presets_left, m_preset_bundle_left.get());
+ sizer->Add(equal_bmp, 0, wxRIGHT | wxLEFT | wxALIGN_CENTER_VERTICAL, 5);
+ add_preset_combobox(&presets_right, m_preset_bundle_right.get());
+ presets_sizer->Add(sizer, 1, wxTOP, 5);
+ equal_bmp->Show(new_type == Preset::TYPE_PRINTER);
+
+ m_preset_combos.push_back({ presets_left, equal_bmp, presets_right });
+
+ equal_bmp->Bind(wxEVT_BUTTON, [presets_left, presets_right, this](wxEvent&) {
+ std::string preset_name = get_selection(presets_left);
+ presets_right->update(preset_name);
+ if (m_view_type == Preset::TYPE_INVALID)
+ update_compatibility(Preset::remove_suffix_modified(preset_name), presets_right->get_type(), m_preset_bundle_right.get());
+ update_tree();
+ });
+ }
+
+ m_show_all_presets = new wxCheckBox(this, wxID_ANY, _L("Show all preset (including incompatible)"));
+ m_show_all_presets->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent&) {
+ bool show_all = m_show_all_presets->GetValue();
+ for (auto preset_combos : m_preset_combos) {
+ if (preset_combos.presets_left->get_type() == Preset::TYPE_PRINTER)
+ continue;
+ preset_combos.presets_left->show_all(show_all);
+ preset_combos.presets_right->show_all(show_all);
+ }
+ if (m_view_type == Preset::TYPE_INVALID)
+ update_tree();
+ });
+
+ m_tree = new DiffViewCtrl(this, wxSize(em * 65, em * 40));
+ m_tree->AppendBmpTextColumn("", DiffModel::colIconText, 35);
+ m_tree->AppendBmpTextColumn(_L("Left Preset Value"), DiffModel::colOldValue, 15);
+ m_tree->AppendBmpTextColumn(_L("Right Preset Value"),DiffModel::colNewValue, 15);
+ m_tree->Hide();
+
+ 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_show_all_presets, 0, wxEXPAND | wxALL, border);
+ topSizer->Add(m_bottom_info_line, 0, wxEXPAND | wxALL, 2 * border);
+ topSizer->Add(m_tree, 1, wxEXPAND | wxALL, border);
+
+ this->SetMinSize(wxSize(80 * em, 30 * em));
+ this->SetSizer(topSizer);
+ topSizer->SetSizeHints(this);
+}
+
+void DiffPresetDialog::update_controls_visibility(Preset::Type type /* = Preset::TYPE_INVALID*/)
+{
+ for (auto preset_combos : m_preset_combos) {
+ Preset::Type cb_type = preset_combos.presets_left->get_type();
+ bool show = type != Preset::TYPE_INVALID ? type == cb_type :
+ cb_type == Preset::TYPE_PRINTER ? true :
+ m_pr_technology == ptFFF ? cb_type == Preset::TYPE_PRINT || cb_type == Preset::TYPE_FILAMENT :
+ cb_type == Preset::TYPE_SLA_PRINT || cb_type == Preset::TYPE_SLA_MATERIAL;
+ preset_combos.presets_left->Show(show);
+ preset_combos.equal_bmp->Show(show);
+ preset_combos.presets_right->Show(show);
+
+ if (show) {
+ preset_combos.presets_left->update_from_bundle();
+ preset_combos.presets_right->update_from_bundle();
+ }
+ }
+
+ m_show_all_presets->Show(type != Preset::TYPE_PRINTER);
+}
+
+void DiffPresetDialog::update_bundles_from_app()
+{
+ *m_preset_bundle_left = *wxGetApp().preset_bundle;
+ *m_preset_bundle_right = *wxGetApp().preset_bundle;
+}
+
+void DiffPresetDialog::show(Preset::Type type /* = Preset::TYPE_INVALID*/)
+{
+ this->SetTitle(type == Preset::TYPE_INVALID ? _L("Compare Presets") : format_wxstr(_L("Compare %1% Presets"), wxGetApp().get_tab(type)->name()));
+ m_view_type = type;
+
+ update_bundles_from_app();
+ update_controls_visibility(type);
+ if (type == Preset::TYPE_INVALID)
+ Fit();
+
+ update_tree();
+
+ // if this dialog is shown it have to be Hide and show again to be placed on the very Top of windows
+ if (IsShown())
+ Hide();
+ Show();
+}
+
+void DiffPresetDialog::update_presets(Preset::Type type)
+{
+ m_pr_technology = m_preset_bundle_left.get()->printers.get_edited_preset().printer_technology();
+
+ update_bundles_from_app();
+ update_controls_visibility(type);
+
+ if (type == Preset::TYPE_INVALID)
+ for (auto preset_combos : m_preset_combos) {
+ if (preset_combos.presets_left->get_type() == Preset::TYPE_PRINTER) {
+ preset_combos.presets_left->update_from_bundle ();
+ preset_combos.presets_right->update_from_bundle();
+ break;
+ }
+ }
+ else
+ for (auto preset_combos : m_preset_combos) {
+ if (preset_combos.presets_left->get_type() == type) {
+ preset_combos.presets_left->update();
+ preset_combos.presets_right->update();
+ break;
+ }
+ }
+
+ update_tree();
+}
+
+void DiffPresetDialog::update_tree()
+{
+ Search::OptionsSearcher& searcher = wxGetApp().sidebar().get_searcher();
+ searcher.sort_options_by_opt_key();
+
+ m_tree->Clear();
+ wxString bottom_info = "";
+ bool show_tree = false;
+
+ for (auto preset_combos : m_preset_combos)
+ {
+ if (!preset_combos.presets_left->IsShown())
+ continue;
+ Preset::Type type = preset_combos.presets_left->get_type();
+
+ const PresetCollection* presets = get_preset_collection(type);
+ const Preset* left_preset = presets->find_preset(get_selection(preset_combos.presets_left));
+ const Preset* right_preset = presets->find_preset(get_selection(preset_combos.presets_right));
+ if (!left_preset || !right_preset) {
+ bottom_info = _L("One of the presets doesn't found");
+ preset_combos.equal_bmp->SetBitmap_(ScalableBitmap(this, "question"));
+ preset_combos.equal_bmp->SetToolTip(bottom_info);
+ continue;
+ }
+
+ const DynamicPrintConfig& left_config = left_preset->config;
+ const PrinterTechnology& left_pt = left_preset->printer_technology();
+ const DynamicPrintConfig& right_congig = right_preset->config;
+
+ if (left_pt != right_preset->printer_technology()) {
+ bottom_info = _L("Comparable printer presets has different printer technology");
+ preset_combos.equal_bmp->SetBitmap_(ScalableBitmap(this, "question"));
+ preset_combos.equal_bmp->SetToolTip(bottom_info);
+ continue;
+ }
+
+ // Collect dirty options.
+ const bool deep_compare = (type == Preset::TYPE_PRINTER || type == Preset::TYPE_SLA_MATERIAL);
+ auto dirty_options = type == Preset::TYPE_PRINTER && left_pt == ptFFF &&
+ left_config.opt("extruder_colour")->values.size() < right_congig.opt("extruder_colour")->values.size() ?
+ presets->dirty_options(right_preset, left_preset, deep_compare) :
+ presets->dirty_options(left_preset, right_preset, deep_compare);
+
+ if (dirty_options.empty()) {
+ bottom_info = _L("Presets are the same");
+ preset_combos.equal_bmp->SetBitmap_(ScalableBitmap(this, "equal"));
+ preset_combos.equal_bmp->SetToolTip(bottom_info);
+ continue;
+ }
+
+ show_tree = true;
+ preset_combos.equal_bmp->SetBitmap_(ScalableBitmap(this, "not_equal"));
+ preset_combos.equal_bmp->SetToolTip(_L("Presets are different.\n"
+ "Click this button to select the same as left preset for the right preset."));
+
+ 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->Append("extruders_count", 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->Append(opt_key, 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->Append(opt_key, 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();
+ 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);
+
+ for (auto preset_combos : m_preset_combos) {
+ preset_combos.presets_left->msw_rescale();
+ preset_combos.equal_bmp->msw_rescale();
+ preset_combos.presets_right->msw_rescale();
+ }
+
+ m_tree->Rescale(em);
+
+ Fit();
+ Refresh();
+}
+
+void DiffPresetDialog::on_sys_color_changed()
+{
+ // msw_rescale updates just icons, so use it
+ m_tree->Rescale();
+ Refresh();
+}
+
+void DiffPresetDialog::update_compatibility(const std::string& preset_name, Preset::Type type, PresetBundle* preset_bundle)
+{
+ PresetCollection* presets = get_preset_collection(type, preset_bundle);
+
+ bool print_tab = type == Preset::TYPE_PRINT || type == Preset::TYPE_SLA_PRINT;
+ bool printer_tab = type == Preset::TYPE_PRINTER;
+ bool technology_changed = false;
+
+ if (printer_tab) {
+ const Preset& new_printer_preset = *presets->find_preset(preset_name, true);
+ const PresetWithVendorProfile new_printer_preset_with_vendor_profile = presets->get_preset_with_vendor_profile(new_printer_preset);
+ PrinterTechnology old_printer_technology = presets->get_selected_preset().printer_technology();
+ PrinterTechnology new_printer_technology = new_printer_preset.printer_technology();
+
+ technology_changed = old_printer_technology != new_printer_technology;
+ }
+
+ bool is_selected = presets->select_preset_by_name(preset_name, false);
+
+ // Mark the print & filament enabled if they are compatible with the currently selected preset.
+ // The following method should not discard changes of current print or filament presets on change of a printer profile,
+ // if they are compatible with the current printer.
+ auto update_compatible_type = [](bool technology_changed, bool on_page, bool show_incompatible_presets) {
+ return technology_changed ? PresetSelectCompatibleType::Always :
+ on_page ? PresetSelectCompatibleType::Never :
+ show_incompatible_presets ? PresetSelectCompatibleType::OnlyIfWasCompatible : PresetSelectCompatibleType::Always;
+ };
+ if (print_tab || printer_tab)
+ preset_bundle->update_compatible(
+ update_compatible_type(technology_changed, print_tab, true),
+ update_compatible_type(technology_changed, false, true));
+
+ bool is_left_presets = preset_bundle == m_preset_bundle_left.get();
+ PrinterTechnology pr_tech = preset_bundle->printers.get_selected_preset().printer_technology();
+
+ // update preset comboboxes
+ for (auto preset_combos : m_preset_combos)
+ {
+ PresetComboBox* cb = is_left_presets ? preset_combos.presets_left : preset_combos.presets_right;
+ Preset::Type presets_type = cb->get_type();
+ if (print_tab && (pr_tech == ptFFF && presets_type == Preset::TYPE_FILAMENT ||
+ pr_tech == ptSLA && presets_type == Preset::TYPE_SLA_MATERIAL) ||
+ printer_tab && (pr_tech == ptFFF && (presets_type == Preset::TYPE_PRINT || presets_type == Preset::TYPE_FILAMENT) ||
+ pr_tech == ptSLA && (presets_type == Preset::TYPE_SLA_PRINT || presets_type == Preset::TYPE_SLA_MATERIAL)))
+ cb->update();
+ }
+
+ if (technology_changed &&
+ m_preset_bundle_left.get()->printers.get_selected_preset().printer_technology() ==
+ m_preset_bundle_right.get()->printers.get_selected_preset().printer_technology())
+ {
+ m_pr_technology = m_preset_bundle_left.get()->printers.get_edited_preset().printer_technology();
+ update_controls_visibility();
+ }
+}
}
diff --git a/src/slic3r/GUI/UnsavedChangesDialog.hpp b/src/slic3r/GUI/UnsavedChangesDialog.hpp
index 232802b661a..12c215e568f 100644
--- a/src/slic3r/GUI/UnsavedChangesDialog.hpp
+++ b/src/slic3r/GUI/UnsavedChangesDialog.hpp
@@ -7,7 +7,7 @@
#include "GUI_Utils.hpp"
#include "wxExtensions.hpp"
-#include "libslic3r/Preset.hpp"
+#include "libslic3r/PresetBundle.hpp"
class ScalableButton;
class wxStaticText;
@@ -16,10 +16,11 @@ namespace Slic3r {
namespace GUI{
// ----------------------------------------------------------------------------
-// ModelNode: a node inside UnsavedChangesModel
+// ModelNode: a node inside DiffModel
// ----------------------------------------------------------------------------
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 DiffModel::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);
@@ -107,13 +108,13 @@ class ModelNode
// ----------------------------------------------------------------------------
-// UnsavedChangesModel
+// DiffModel
// ----------------------------------------------------------------------------
-class UnsavedChangesModel : public wxDataViewModel
+class DiffModel : public wxDataViewModel
{
wxWindow* m_parent_win { nullptr };
- std::vector m_preset_nodes;
+ ModelNodePtrArray m_preset_nodes;
wxDataViewCtrl* m_ctrl{ nullptr };
@@ -143,8 +144,8 @@ class UnsavedChangesModel : public wxDataViewModel
colMax
};
- UnsavedChangesModel(wxWindow* parent);
- ~UnsavedChangesModel();
+ DiffModel(wxWindow* parent);
+ ~DiffModel() {}
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;
@@ -173,14 +177,60 @@ class UnsavedChangesModel : public wxDataViewModel
};
+// ----------------------------------------------------------------------------
+// DiffViewCtrl
+// ----------------------------------------------------------------------------
+
+class DiffViewCtrl : public wxDataViewCtrl
+{
+ bool m_has_long_strings{ false };
+ bool m_empty_selection { false };
+ int m_em_unit;
+
+ struct ItemData
+ {
+ std::string opt_key;
+ wxString opt_name;
+ wxString old_val;
+ wxString new_val;
+ Preset::Type type;
+ bool is_long{ false };
+ };
+
+ // tree items related to the options
+ std::map m_items_map;
+ std::map m_columns_width;
+
+public:
+ DiffViewCtrl(wxWindow* parent, wxSize size);
+ ~DiffViewCtrl() {}
+
+ DiffModel* model{ nullptr };
+
+ void AppendBmpTextColumn(const wxString& label, unsigned model_column, int width, bool set_expander = false);
+ void AppendToggleColumn_(const wxString& label, unsigned model_column, int width);
+ void Rescale(int em = 0);
+ void Append(const std::string& opt_key, Preset::Type type, wxString category_name, wxString group_name, wxString option_name,
+ wxString old_value, wxString new_value, const std::string category_icon_name);
+ void Clear();
+
+ wxString get_short_string(wxString full_string);
+ bool has_selection() { return !m_empty_selection; }
+ void context_menu(wxDataViewEvent& event);
+ void item_value_changed(wxDataViewEvent& event);
+ void set_em_unit(int em) { m_em_unit = em; }
+
+ std::vector unselected_options(Preset::Type type);
+ std::vector selected_options();
+};
+
+
//------------------------------------------
// UnsavedChangesDialog
//------------------------------------------
class UnsavedChangesDialog : public DPIDialog
{
- wxDataViewCtrl* m_tree { nullptr };
- UnsavedChangesModel* m_tree_model { nullptr };
-
+ DiffViewCtrl* m_tree { nullptr };
ScalableButton* m_save_btn { nullptr };
ScalableButton* m_transfer_btn { nullptr };
ScalableButton* m_discard_btn { nullptr };
@@ -188,7 +238,6 @@ class UnsavedChangesDialog : public DPIDialog
wxStaticText* m_info_line { nullptr };
wxCheckBox* m_remember_choice { nullptr };
- bool m_empty_selection { false };
bool m_has_long_strings { false };
int m_save_btn_id { wxID_ANY };
int m_move_btn_id { wxID_ANY };
@@ -209,19 +258,6 @@ class UnsavedChangesDialog : public DPIDialog
// selected action after Dialog closing
Action m_exit_action {Action::Undef};
-
- struct ItemData
- {
- std::string opt_key;
- wxString opt_name;
- wxString old_val;
- wxString new_val;
- Preset::Type type;
- bool is_long {false};
- };
- // tree items related to the options
- std::map m_items_map;
-
// preset names which are modified in SavePresetDialog and related types
std::vector> names_and_types;
@@ -230,13 +266,9 @@ class UnsavedChangesDialog : public DPIDialog
UnsavedChangesDialog(Preset::Type type, PresetCollection* dependent_presets, const std::string& new_selected_preset);
~UnsavedChangesDialog() {}
- wxString get_short_string(wxString full_string);
-
void build(Preset::Type type, PresetCollection* dependent_presets, const std::string& new_selected_preset, const wxString& header = "");
void update(Preset::Type type, PresetCollection* dependent_presets, const std::string& new_selected_preset, const wxString& header);
void update_tree(Preset::Type type, PresetCollection *presets);
- void item_value_changed(wxDataViewEvent &event);
- void context_menu(wxDataViewEvent &event);
void show_info_line(Action action, std::string preset_name = "");
void update_config(Action action);
void close(Action action);
@@ -251,8 +283,8 @@ class UnsavedChangesDialog : public DPIDialog
// short version of the previous function, for the case, when just one preset is modified
std::string get_preset_name() { return names_and_types[0].first; }
- std::vector get_unselected_options(Preset::Type type);
- std::vector get_selected_options();
+ std::vector get_unselected_options(Preset::Type type) { return m_tree->unselected_options(type); }
+ std::vector get_selected_options() { return m_tree->selected_options(); }
protected:
void on_dpi_changed(const wxRect& suggested_rect) override;
@@ -270,6 +302,48 @@ class FullCompareDialog : public wxDialog
~FullCompareDialog() {}
};
+
+//------------------------------------------
+// DiffPresetDialog
+//------------------------------------------
+class DiffPresetDialog : public DPIDialog
+{
+ DiffViewCtrl* m_tree { nullptr };
+ wxStaticText* m_top_info_line { nullptr };
+ wxStaticText* m_bottom_info_line { nullptr };
+ wxCheckBox* m_show_all_presets { nullptr };
+
+ Preset::Type m_view_type { Preset::TYPE_INVALID };
+ PrinterTechnology m_pr_technology;
+ std::unique_ptr m_preset_bundle_left;
+ std::unique_ptr m_preset_bundle_right;
+
+ void update_tree();
+ void update_bundles_from_app();
+ void update_controls_visibility(Preset::Type type = Preset::TYPE_INVALID);
+ void update_compatibility(const std::string& preset_name, Preset::Type type, PresetBundle* preset_bundle);
+
+ struct DiffPresets
+ {
+ PresetComboBox* presets_left { nullptr };
+ ScalableButton* equal_bmp { nullptr };
+ PresetComboBox* presets_right { nullptr };
+ };
+
+ std::vector m_preset_combos;
+
+public:
+ DiffPresetDialog();
+ ~DiffPresetDialog() {}
+
+ void show(Preset::Type type = Preset::TYPE_INVALID);
+ void update_presets(Preset::Type type = Preset::TYPE_INVALID);
+
+protected:
+ void on_dpi_changed(const wxRect& suggested_rect) override;
+ void on_sys_color_changed() override;
+};
+
}
}