diff --git a/resources/images/extruder_sync.svg b/resources/images/extruder_sync.svg
new file mode 100644
index 0000000000..4e18766368
--- /dev/null
+++ b/resources/images/extruder_sync.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/slic3r/GUI/Search.cpp b/src/slic3r/GUI/Search.cpp
index 1babc30334..cb77f13256 100644
--- a/src/slic3r/GUI/Search.cpp
+++ b/src/slic3r/GUI/Search.cpp
@@ -109,7 +109,7 @@ void OptionsSearcher::append_options(DynamicPrintConfig *config, Preset::Type ty
int cnt = 0;
- if ((type == Preset::TYPE_SLA_MATERIAL || type == Preset::TYPE_PRINTER) && opt_key != "printable_area")
+ if ((type == Preset::TYPE_SLA_MATERIAL || type == Preset::TYPE_PRINTER || type == Preset::TYPE_PRINT) && opt_key != "printable_area")
switch (config->option(opt_key)->type()) {
case coInts: change_opt_key(opt_key, config, cnt); break;
case coBools: change_opt_key(opt_key, config, cnt); break;
@@ -122,6 +122,9 @@ void OptionsSearcher::append_options(DynamicPrintConfig *config, Preset::Type ty
default: break;
}
+ if (type == Preset::TYPE_FILAMENT && filament_options_with_variant.find(opt_key) != filament_options_with_variant.end())
+ opt_key += "#0";
+
wxString label = opt.full_label.empty() ? opt.label : opt.full_label;
std::string key = get_key(opt_key, type);
@@ -329,12 +332,31 @@ const Option &OptionsSearcher::get_option(size_t pos_in_filter) const
return options[found[pos_in_filter].option_idx];
}
-const Option &OptionsSearcher::get_option(const std::string &opt_key, Preset::Type type) const
+const Option &OptionsSearcher::get_option(const std::string &opt_key, Preset::Type type, int &variant_index) const
{
- auto it = std::lower_bound(options.begin(), options.end(), Option({boost::nowide::widen(get_key(opt_key, type))}));
+ std::string opt_key2 = opt_key;
+ if (auto n = opt_key.find('#'); n != std::string::npos) {
+ variant_index = std::atoi(opt_key.c_str() + n + 1);
+ opt_key2 = opt_key.substr(0, n);
+ }
+ auto it = std::lower_bound(options.begin(), options.end(), Option({boost::nowide::widen(get_key(opt_key2, type))}));
// BBS: return the 0th option when not found in searcher caused by mode difference
// assert(it != options.end());
- if (it == options.end()) return options[0];
+ if (it == options.end()) { variant_index = -2 ; return options[0]; }
+ if (it->opt_key() == opt_key2) {
+ variant_index = -1;
+ } else {
+ it = std::lower_bound(it, options.end(), Option({boost::nowide::widen(get_key(opt_key2 + "#", type))}));
+ if (it == options.end() || it->opt_key().compare(0, opt_key2.length(), opt_key2) != 0) {
+ variant_index = -2; // Not found
+ return options[0];
+ }
+ auto it2 = it;
+ ++it2;
+ if (it2 != options.end() && it2->opt_key().compare(0, opt_key2.length(), opt_key2) == 0
+ && printer_options_with_variant_1.find(opt_key2) == printer_options_with_variant_1.end())
+ variant_index = -2;
+ }
return options[it - options.begin()];
}
diff --git a/src/slic3r/GUI/Search.hpp b/src/slic3r/GUI/Search.hpp
index 7b05478311..bdb4da83c4 100644
--- a/src/slic3r/GUI/Search.hpp
+++ b/src/slic3r/GUI/Search.hpp
@@ -130,7 +130,7 @@ public:
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, Preset::Type type) const;
+ const Option & get_option(const std::string &opt_key, Preset::Type type, int &variant_index) const;
Option get_option(const std::string &opt_key, const wxString &label, Preset::Type type) const;
const std::vector &found_options() { return found; }
diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp
index c2b67579ea..69cb82e09f 100644
--- a/src/slic3r/GUI/Tab.cpp
+++ b/src/slic3r/GUI/Tab.cpp
@@ -514,7 +514,30 @@ void Tab::create_preset_tab()
reload_config();
update_changed_ui();
});
- m_main_sizer->Add(m_extruder_switch, 0, wxALIGN_CENTER | wxTOP, m_em_unit);
+ m_extruder_sync_box = new wxPanel(panel, wxID_ANY);
+ m_extruder_sync_box->SetBackgroundColour(panel->GetBackgroundColour());
+ m_extruder_sync_box->SetToolTip(_L("Synchronization of different extruder drives or nozzle volume types is not supported."));
+ m_extruder_sync = new ScalableButton(m_extruder_sync_box, wxID_ANY, "extruder_sync");
+ m_extruder_sync->SetToolTip(_L("Synchronize the modification of parameters to the corresponding parameters of another extruder."));
+ m_extruder_sync->Bind(wxEVT_BUTTON, [this](auto &evt) {
+ evt.Skip();
+ sync_excluder();
+ });
+
+ auto sync_box_sizer = new wxBoxSizer(wxHORIZONTAL);
+ sync_box_sizer->Add(m_extruder_sync, 1, wxEXPAND);
+ m_extruder_sync_box->SetSizer(sync_box_sizer);
+
+ m_variant_sizer = new wxBoxSizer(wxHORIZONTAL);
+ auto right_sizer = new wxBoxSizer(wxHORIZONTAL);
+
+ m_variant_sizer->AddStretchSpacer(1);
+ m_variant_sizer->Add(m_extruder_switch, 0, wxALIGN_CENTER, 0);
+ m_variant_sizer->Add(right_sizer, 1, wxALIGN_CENTER);
+ right_sizer->AddStretchSpacer(1);
+ right_sizer->Add(m_extruder_sync_box, 0, wxALIGN_CENTER | wxRIGHT, m_em_unit);
+
+ m_main_sizer->Add(m_variant_sizer, 0, wxEXPAND | wxTOP, m_em_unit);
} else if (dynamic_cast(this)) {
m_variant_combo = new MultiSwitchButton(panel);
m_variant_combo->Bind(wxCUSTOMEVT_MULTISWITCH_SELECTION, [this](auto &evt) {
@@ -527,8 +550,15 @@ void Tab::create_preset_tab()
m_active_page->update_visibility(m_mode, true);
m_page_view->GetParent()->Layout();
});
- m_variant_combo->Hide();
- m_main_sizer->Add(m_variant_combo, 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, m_em_unit);
+
+
+ wxBoxSizer *combo_sizer = new wxBoxSizer(wxHORIZONTAL);
+ combo_sizer->Add(m_variant_combo, 1, wxEXPAND);
+ wxBoxSizer *top_sizer = new wxBoxSizer(wxHORIZONTAL);
+ top_sizer->Add(combo_sizer, 1, wxEXPAND | wxLEFT, m_em_unit);
+ m_variant_sizer = new wxBoxSizer(wxVERTICAL);
+ m_variant_sizer->Add(top_sizer, 0, wxLEFT, m_em_unit);
+ m_main_sizer->Add(m_variant_sizer, 0, wxEXPAND | wxTOP, m_em_unit);
}
this->SetSizer(m_main_sizer);
@@ -1596,6 +1626,8 @@ void Tab::sys_color_changed()
bmp->msw_rescale();
if (m_detach_preset_btn)
m_detach_preset_btn->msw_rescale();
+ if (m_extruder_sync)
+ m_extruder_sync->msw_rescale();
// update icons for tree_ctrl
for (ScalableBitmap& bmp : m_scaled_icons_list)
@@ -6723,9 +6755,10 @@ bool Tab::tree_sel_change_delayed(wxCommandEvent& event)
// update_undo_buttons();
this->OnActivate();
m_parent->set_active_tab(this);
- wxWindow *variant_ctrl = m_extruder_switch ? (wxWindow *) m_extruder_switch : m_variant_combo;
- if (variant_ctrl) {
- m_main_sizer->Show(variant_ctrl, variant_ctrl->IsThisEnabled() && !m_active_page->m_opt_id_map.empty() && !m_active_page->title().StartsWith("Extruder "));
+ if (m_variant_sizer) {
+ wxWindow *variant_ctrl = m_extruder_switch ? (wxWindow *) m_extruder_switch : m_variant_combo;
+ m_main_sizer->Show(m_variant_sizer, variant_ctrl->IsThisEnabled() && !m_active_page->m_opt_id_map.empty() && !m_active_page->title().StartsWith("Extruder "));
+ if (m_extruder_sync) m_extruder_sync->Show(variant_ctrl->IsShown());
GetParent()->Layout();
}
@@ -6738,9 +6771,10 @@ bool Tab::tree_sel_change_delayed(wxCommandEvent& event)
return false;
m_active_page = page;
- wxWindow *variant_ctrl = m_extruder_switch ? (wxWindow *) m_extruder_switch : m_variant_combo;
- if (variant_ctrl) {
- m_main_sizer->Show(variant_ctrl, variant_ctrl->IsThisEnabled() && !m_active_page->m_opt_id_map.empty() && !m_active_page->title().StartsWith("Extruder"));
+ if (m_variant_sizer) {
+ wxWindow *variant_ctrl = m_extruder_switch ? (wxWindow *) m_extruder_switch : m_variant_combo;
+ m_main_sizer->Show(m_variant_sizer, variant_ctrl->IsThisEnabled() && !m_active_page->m_opt_id_map.empty() && !m_active_page->title().StartsWith("Extruder"));
+ if (m_extruder_sync) m_extruder_sync->Show(variant_ctrl->IsShown());
GetParent()->Layout();
}
@@ -7549,8 +7583,10 @@ void Tab::update_extruder_variants(int extruder_id)
m_extruder_switch->SetSelection(selection_index);
m_extruder_switch->Enable(true);
+ m_extruder_sync->Enable(true);
} else {
m_extruder_switch->Enable(false);
+ m_extruder_sync->Enable(false);
}
} else if (m_variant_combo) {
if (extruder_id >= 0)
@@ -7566,9 +7602,10 @@ void Tab::update_extruder_variants(int extruder_id)
m_variant_combo->Enable(options.size() > 1);
}
switch_excluder(extruder_id);
- wxWindow *variant_ctrl = m_extruder_switch ? (wxWindow *) m_extruder_switch : m_variant_combo;
- if (variant_ctrl) {
- m_main_sizer->Show(variant_ctrl, variant_ctrl->IsThisEnabled() && m_active_page && !m_active_page->m_opt_id_map.empty() && !m_active_page->title().StartsWith("Extruder "));
+ if (m_variant_sizer) {
+ wxWindow *variant_ctrl = m_extruder_switch ? (wxWindow *) m_extruder_switch : m_variant_combo;
+ m_main_sizer->Show(m_variant_sizer, variant_ctrl->IsThisEnabled() && m_active_page && !m_active_page->m_opt_id_map.empty() && !m_active_page->title().StartsWith("Extruder "));
+ if (m_extruder_sync) m_extruder_sync->Show(variant_ctrl->IsShown());
GetParent()->Layout();
}
}
@@ -7665,6 +7702,60 @@ NozzleVolumeType Tab::get_actual_nozzle_volume_type(int extruder_id)
return m_actual_nozzle_volumes[extruder_id];
}
+bool Tab::get_extruder_sync_enable_state(int extruder_id)
+{
+ Preset& printer_preset = m_preset_bundle->printers.get_edited_preset();
+ auto nozzle_volumes = m_preset_bundle->project_config.option("nozzle_volume_type");
+ auto extruders = printer_preset.config.option("extruder_type");
+ if (nozzle_volumes->values.size() < 2 || extruders->values.size() < 2) {
+ return false;
+ }
+
+ NozzleVolumeType left_nozzle = NozzleVolumeType(nozzle_volumes->values[0]);
+ NozzleVolumeType right_nozzle = NozzleVolumeType(nozzle_volumes->values[1]);
+ ExtruderType left_extruder = ExtruderType(extruders->values[0]);
+ ExtruderType right_extruder = ExtruderType(extruders->values[1]);
+
+ if (left_extruder != right_extruder) {
+ return false;
+ }
+
+ if (left_nozzle == right_nozzle) {
+ return true;
+ }
+
+ // TODO: Orca: Support hybrid
+ //if (left_nozzle != NozzleVolumeType::nvtHybrid && right_nozzle != NozzleVolumeType::nvtHybrid) {
+ // return false;
+ //}
+
+ //if (left_nozzle == NozzleVolumeType::nvtHybrid && right_nozzle == NozzleVolumeType::nvtHybrid) {
+ // return true;
+ //}
+
+ //// Hybrid rules
+ //auto current_nozzle = get_actual_nozzle_volume_type(extruder_id);
+ //if (left_nozzle != NozzleVolumeType::nvtHybrid) {
+ // if (extruder_id == 0) {
+ // return true;
+ // }
+ // if (extruder_id == 1 && current_nozzle == left_nozzle) {
+ // return true;
+ // }
+ // return false;
+ //}
+ //if (right_nozzle != NozzleVolumeType::nvtHybrid) {
+ // if (extruder_id == 1) {
+ // return true;
+ // }
+ // if (extruder_id == 0 && current_nozzle == right_nozzle) {
+ // return true;
+ // }
+ // return false;
+ //}
+ return false;
+}
+
void Tab::switch_excluder(int extruder_id)
{
Preset & printer_preset = m_preset_bundle->printers.get_edited_preset();
@@ -7673,12 +7764,17 @@ void Tab::switch_excluder(int extruder_id)
if (!m_variant_combo && (extruder_id >= (int)nozzle_volumes->size() || extruder_id >= (int)extruders->size()))
extruder_id = 0;
- if (m_extruder_switch && m_type != Preset::TYPE_PRINTER) {
+ if (m_extruder_switch) {
int current_extruder = get_current_active_extruder();
- if (extruder_id == -1)
- extruder_id = current_extruder;
- else if (extruder_id != current_extruder)
- return;
+ bool sync_enable = get_extruder_sync_enable_state(current_extruder);
+ m_extruder_sync->Enable(m_extruder_switch->IsThisEnabled() && sync_enable);
+ m_extruder_sync->Show();
+ if (m_type != Preset::TYPE_PRINTER) {
+ if (extruder_id == -1)
+ extruder_id = current_extruder;
+ else if (extruder_id != current_extruder)
+ return;
+ }
} else if (m_variant_combo) {
int current_variant = m_variant_combo->GetSelection();
if (current_variant < 0)
@@ -7732,6 +7828,82 @@ void Tab::switch_excluder(int extruder_id)
}
}
+void Tab::sync_excluder()
+{
+ Preset & printer_preset = m_preset_bundle->printers.get_edited_preset();
+ auto nozzle_volumes = m_preset_bundle->project_config.option("nozzle_volume_type");
+ auto extruders = printer_preset.config.option("extruder_type");
+ auto get_index_for_extruder =
+ [this, &extruders, &nozzle_volumes, variant_keys = extruder_variant_keys[m_type >= Preset::TYPE_COUNT ? Preset::TYPE_PRINT : m_type]](int extruder_id, NozzleVolumeType nozzle_type) {
+ return m_config->get_index_for_extruder(extruder_id + 1, variant_keys.first,
+ ExtruderType(extruders->values[extruder_id]), nozzle_type, variant_keys.second);
+ };
+ int active_index = get_current_active_extruder();
+ auto active_nozzle = get_actual_nozzle_volume_type(active_index);
+ int from_index = get_index_for_extruder(active_index, active_nozzle);
+ int dest_index = get_index_for_extruder(1 - active_index, active_nozzle);
+ auto from_str = std::to_string(from_index);
+ auto dest_str = std::to_string(dest_index);
+ auto dirty_options = m_presets->current_dirty_options(true);
+ DynamicConfig config_origin, config_to_apply;
+ for (int i = 0; i < dirty_options.size(); ++i) {
+ auto &opt = dirty_options[i];
+ auto n= opt.find('#');
+ if (n == std::string::npos)
+ continue;
+ auto field = m_active_page->get_field(opt.substr(0, n), from_index + 256);
+ auto line = m_active_page->get_line(opt.substr(0, n), from_index + 256);
+ if (field == nullptr || line == nullptr)
+ continue;
+ ++n;
+ bool dirty = opt.substr(n) == from_str;
+ while (i + 1 < dirty_options.size() && dirty_options[i + 1].compare(0, n, opt, 0, n) == 0) {
+ dirty |= dirty_options[i + 1].substr(n) == from_str;
+ ++i;
+ }
+ if (dirty) {
+ auto key = opt.substr(0, n - 1);
+ auto option = dynamic_cast(m_config->option(key));
+ auto option2 = dynamic_cast(option->clone());
+ option2->set_at(option, dest_index, from_index);
+ if (*option == *option2) {
+ delete option2;
+ continue;
+ }
+ config_origin.set_key_value(key, option->clone());
+ config_to_apply.set_key_value(key, option2);
+ }
+ }
+ if (config_to_apply.empty()) {
+ MessageDialog md(wxGetApp().plater(), _L("No modifications need to be copied."), _L("Copy paramters"), wxICON_INFORMATION | wxOK);
+ md.ShowModal();
+ return;
+ }
+
+ std::string pt = m_preset_bundle->printers.get_edited_preset().get_printer_type(m_preset_bundle);
+ std::string active_nozzle_name = DevPrinterConfigUtil::get_toolhead_display_name(pt, active_index, ToolHeadComponent::Nozzle, ToolHeadNameCase::LowerCase);
+ std::string other_nozzle_name = DevPrinterConfigUtil::get_toolhead_display_name(pt, 1 - active_index, ToolHeadComponent::Nozzle, ToolHeadNameCase::LowerCase);
+ wxString title = wxString::Format(_L("Modify paramters of %s"), _L(active_nozzle_name));
+ wxString header = wxString::Format(_L("Do you want to modify the following parameters of the %s to that of the %s?"),
+ _L(active_nozzle_name), _L(other_nozzle_name));
+ UnsavedChangesDialog dlg(title, header, &config_origin, from_index, dest_index, active_index == 0, active_nozzle);
+ dlg.ShowModal();
+ if (dlg.transfer_changes()) {
+ m_config->apply(config_to_apply);
+ auto &applying_keys = const_cast(m_config_manipulation.applying_keys());
+ if (m_type > Preset::TYPE_COUNT)
+ applying_keys = config_to_apply.keys();
+ reload_config();
+ if (m_type > Preset::TYPE_COUNT)
+ applying_keys.clear();
+ update_changed_ui();
+ update();
+ if (m_active_page)
+ m_active_page->update_visibility(m_mode, true);
+ m_page_view->GetParent()->Layout();
+ }
+}
+
void Tab::compatible_widget_reload(PresetDependencies &deps)
{
Field* field = this->get_field(deps.key_condition);
diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp
index 1d6002aaf0..b5ce4775a5 100644
--- a/src/slic3r/GUI/Tab.hpp
+++ b/src/slic3r/GUI/Tab.hpp
@@ -308,8 +308,11 @@ public:
int m_update_cnt = 0;
ModeSwitchButton *m_mode_view = nullptr;
+ wxSizer * m_variant_sizer = nullptr;
MultiSwitchButton * m_extruder_switch = nullptr;
MultiSwitchButton * m_variant_combo = nullptr;
+ ScalableButton *m_extruder_sync = nullptr;
+ wxPanel * m_extruder_sync_box = nullptr;
std::vector m_actual_nozzle_volumes;
public:
@@ -436,8 +439,10 @@ public:
void update_extruder_variants(int extruder_id = -1);
void switch_excluder(int extruder_id = -1);
+ void sync_excluder();
void parse_extruder_selection(int selection, int &extruder_id, NozzleVolumeType &nozzle_type);
int calculate_selection_index_for_extruder(int extruder_id, NozzleVolumeType nozzle_type);
+ bool get_extruder_sync_enable_state(int extruder_id);
int get_current_active_extruder();
std::vector generate_extruder_options();
diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp
index 78c1f16b6c..3dbc0385ca 100644
--- a/src/slic3r/GUI/UnsavedChangesDialog.cpp
+++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp
@@ -802,6 +802,21 @@ UnsavedChangesDialog::UnsavedChangesDialog(const wxString &caption, const wxStri
wxGetApp().UpdateDlgDarkUI(this);
}
+UnsavedChangesDialog::UnsavedChangesDialog(const wxString &caption, const wxString &header, DynamicConfig *config, int from, int to, bool left_to_right, NozzleVolumeType nozzle)
+ : DPIDialog(static_cast(wxGetApp().mainframe),
+ wxID_ANY,
+ caption,
+ wxDefaultPosition,
+ wxDefaultSize,
+ wxCAPTION | wxCLOSE_BOX)
+ , m_buttons(ActionButtons::SAVE | ActionButtons::DONT_SAVE)
+{
+ SyncExtruderParams params { config, from, to, left_to_right, nozzle };
+ build(Preset::TYPE_PRINT, reinterpret_cast(¶ms), "SyncExtruderParams", header);
+ this->CenterOnScreen();
+ wxGetApp().UpdateDlgDarkUI(this);
+}
+
UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type, PresetCollection *dependent_presets, const std::string &new_selected_preset, bool no_transfer)
: m_new_selected_preset_name(new_selected_preset)
, DPIDialog(static_cast(wxGetApp().mainframe),
@@ -856,6 +871,12 @@ void UnsavedChangesDialog::build(Preset::Type type, PresetCollection *dependent_
m_sizer_main->Add(0, 0, 0, wxTOP, 12);
+ SyncExtruderParams *params = nullptr;
+ if (new_selected_preset == "SyncExtruderParams") {
+ params = reinterpret_cast(dependent_presets);
+ dependent_presets = nullptr;
+ }
+
m_panel_tab = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(UNSAVE_CHANGE_DIALOG_SCROLL_WINDOW_SIZE.x, -1), wxTAB_TRAVERSAL);
m_panel_tab->SetBackgroundColour(GREY200);
wxBoxSizer *m_sizer_tab = new wxBoxSizer(wxVERTICAL);
@@ -865,7 +886,7 @@ void UnsavedChangesDialog::build(Preset::Type type, PresetCollection *dependent_
wxBoxSizer *m_sizer_top = new wxBoxSizer(wxHORIZONTAL);
- //m_sizer_top->Add(0, 0, 0, wxLEFT, UNSAVE_CHANGE_DIALOG_FIRST_VALUE_WIDTH);
+ // m_sizer_top->Add(0, 0, 0, wxLEFT, UNSAVE_CHANGE_DIALOG_FIRST_VALUE_WIDTH);
auto m_panel_temp = new wxPanel(m_table_top, wxID_ANY, wxDefaultPosition, wxSize(UNSAVE_CHANGE_DIALOG_FIRST_VALUE_WIDTH, -1), wxTAB_TRAVERSAL);
wxBoxSizer *top_title_temp_v = new wxBoxSizer(wxVERTICAL);
top_title_temp_v->SetMinSize(wxSize(UNSAVE_CHANGE_DIALOG_VALUE_WIDTH, -1));
@@ -880,7 +901,6 @@ void UnsavedChangesDialog::build(Preset::Type type, PresetCollection *dependent_
m_panel_temp->Layout();
m_sizer_top->Add(m_panel_temp, 1, wxALIGN_CENTER, 0);
-
title_block_middle = new wxPanel(m_table_top, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
title_block_middle->SetBackgroundColour(wxColour(172, 172, 172));
@@ -889,10 +909,11 @@ void UnsavedChangesDialog::build(Preset::Type type, PresetCollection *dependent_
wxBoxSizer *top_title_oldv = new wxBoxSizer(wxVERTICAL);
wxBoxSizer *top_title_oldv_h = new wxBoxSizer(wxHORIZONTAL);
- static_oldv_title = new wxStaticText(m_panel_oldv, wxID_ANY, _L("Old Value"), wxDefaultPosition, wxDefaultSize, 0);
+ std::string ucd_pt = wxGetApp().preset_bundle->printers.get_edited_preset().get_printer_type(wxGetApp().preset_bundle);
+ static_oldv_title = new wxStaticText(m_panel_oldv, wxID_ANY, params ? _L(DevPrinterConfigUtil::get_toolhead_display_name(ucd_pt, DEPUTY_EXTRUDER_ID, ToolHeadComponent::Nozzle, ToolHeadNameCase::SentenceCase)) + ": " + get_nozzle_volume_type_name(params->nozzle) : _L("Old Value"), wxDefaultPosition, wxDefaultSize, 0);
static_oldv_title->SetFont(::Label::Body_13);
static_oldv_title->Wrap(-1);
- static_oldv_title->SetForegroundColour(*wxWHITE);
+ static_oldv_title->SetForegroundColour(params && params->left_to_right ? wxGetApp().get_label_clr_modified() : *wxWHITE);
top_title_oldv_h->Add(static_oldv_title, 0, wxALIGN_CENTER | wxBOTTOM | wxTOP, 5);
top_title_oldv->Add(top_title_oldv_h, 1, wxALIGN_CENTER, 0);
m_panel_oldv->SetSizer(top_title_oldv);
@@ -908,10 +929,11 @@ void UnsavedChangesDialog::build(Preset::Type type, PresetCollection *dependent_
wxBoxSizer *top_title_newv = new wxBoxSizer(wxVERTICAL);
wxBoxSizer *top_title_newv_h = new wxBoxSizer(wxHORIZONTAL);
- static_newv_title = new wxStaticText(m_panel_newv, wxID_ANY, _L("New Value"), wxDefaultPosition, wxDefaultSize, 0);
+ static_newv_title = new wxStaticText(m_panel_newv, wxID_ANY, params ? _L(DevPrinterConfigUtil::get_toolhead_display_name(ucd_pt, MAIN_EXTRUDER_ID, ToolHeadComponent::Nozzle, ToolHeadNameCase::SentenceCase)) + ": " + get_nozzle_volume_type_name(params->nozzle) : _L("New Value"),
+ wxDefaultPosition, wxDefaultSize, 0);
static_newv_title->SetFont(::Label::Body_13);
static_newv_title->Wrap(-1);
- static_newv_title->SetForegroundColour(*wxWHITE);
+ static_newv_title->SetForegroundColour(params && !params->left_to_right ? wxGetApp().get_label_clr_modified() : *wxWHITE);
top_title_newv_h->Add(static_newv_title, 0, wxALIGN_CENTER | wxBOTTOM | wxTOP, 5);
@@ -1000,14 +1022,15 @@ void UnsavedChangesDialog::build(Preset::Type type, PresetCollection *dependent_
if (!m_transfer_btn && (ActionButtons::KEEP & m_buttons))
add_btn(&m_transfer_btn, m_move_btn_id, "menu_paste", Action::Transfer, _L("Transfer"), true);
+ bool is_copy = new_selected_preset == "SyncExtruderParams";
{ // "Don't save" / "Discard" button
std::string btn_icon = (ActionButtons::DONT_SAVE & m_buttons) ? "" : (dependent_presets || (ActionButtons::KEEP & m_buttons)) ? "blank_16" : "exit";
- wxString btn_label = (ActionButtons::DONT_SAVE & m_buttons) ? _L("Don't save") : _L("Discard");
+ wxString btn_label = (ActionButtons::DONT_SAVE & m_buttons) ? (is_copy ? _L("No") : _L("Don't save")) : _L("Discard");
add_btn(&m_discard_btn, m_continue_btn_id, btn_icon, Action::Discard, btn_label, false);
}
// "Save" button
- if (ActionButtons::SAVE & m_buttons) add_btn(&m_save_btn, m_save_btn_id, "save", Action::Save, _L("Save"), false);
+ if (ActionButtons::SAVE & m_buttons) add_btn(&m_save_btn, m_save_btn_id, "save", is_copy ? Action::Transfer : Action::Save, is_copy ? _L("Yes") : _L("Save"), false);
if (dependent_presets != nullptr) {
const wxString previous_profile = from_u8(dependent_presets->get_edited_preset().name);
@@ -1057,9 +1080,17 @@ void UnsavedChangesDialog::build(Preset::Type type, PresetCollection *dependent_
Fit();
Centre(wxBOTH);
-
- update(type, dependent_presets, new_selected_preset, header);
-
+ if (params) {
+ if (params->left_to_right)
+ update_tree(type, params->config, params->from, params->to);
+ else
+ update_tree(type, params->config, params->to, params->from);
+ m_action_line->SetLabel(header);
+ m_action_line->Wrap(UNSAVE_CHANGE_DIALOG_SCROLL_WINDOW_SIZE.x);
+ update_list(params);
+ } else {
+ update(type, dependent_presets, new_selected_preset, header);
+ }
//SetSizer(topSizer);
//topSizer->SetSizeHints(this);
@@ -1248,9 +1279,10 @@ static wxString get_string_value(std::string opt_key, const DynamicPrintConfig&
if (!option) {
return _L("N/A");
}
+ auto opt_vector = dynamic_cast(option);
if (option->is_scalar() && config.option(opt_key)->is_nil() ||
- option->is_vector() && dynamic_cast(config.option(opt_key))->is_nil(opt_idx))
+ option->is_vector() && opt_vector && opt_idx >= 0 && opt_idx < opt_vector->size() && opt_vector->is_nil(opt_idx))
return _L("N/A");
wxString out;
@@ -1472,46 +1504,55 @@ void UnsavedChangesDialog::update(Preset::Type type, PresetCollection* dependent
update_list();
}
-void UnsavedChangesDialog::update_list()
+void UnsavedChangesDialog::update_list(SyncExtruderParams *params)
{
+ if (!m_scrolledWindow) {
+ Layout();
+ Fit();
+ return;
+ }
+
std::map> class_g_list;
std::map> class_c_list;
+ std::vector category_list;
// group
for (auto i = 0; i < m_presetitems.size(); i++) {
- if (class_g_list.count(m_presetitems[i].group_name) <= 0) {
+ auto name = m_presetitems[i].category_name + ":" + m_presetitems[i].group_name;
+ if (class_g_list.count(name) <= 0) {
std::vector vp;
vp.push_back(m_presetitems[i]);
- class_g_list.emplace(m_presetitems[i].group_name, vp);
+ class_g_list.emplace(name, vp);
} else {
//for (auto iter = class_g_list.begin(); iter != class_g_list.end(); iter++) iter->second.push_back(m_presetitems[i]);
- class_g_list[m_presetitems[i].group_name].push_back(m_presetitems[i]);
+ class_g_list[name].push_back(m_presetitems[i]);
}
}
// category
for (auto i = 0; i < m_presetitems.size(); i++) {
+ auto name = m_presetitems[i].category_name + ":" + m_presetitems[i].group_name;
if (class_c_list.count(m_presetitems[i].category_name) <= 0) {
std::vector vp;
- vp.push_back(m_presetitems[i].group_name);
+ vp.push_back(name);
class_c_list.emplace(m_presetitems[i].category_name, vp);
+ category_list.push_back(m_presetitems[i].category_name);
} else {
/*for (auto iter = class_c_list.begin(); iter != class_c_list.end(); iter++)
iter->second.push_back(m_presetitems[i].group_name);*/
//class_c_list[m_presetitems[i].category_name].push_back(m_presetitems[i].group_name);
std::vector::iterator it;
- it = find(class_c_list[m_presetitems[i].category_name].begin(), class_c_list[m_presetitems[i].category_name].end(), m_presetitems[i].group_name);
+ it = find(class_c_list[m_presetitems[i].category_name].begin(), class_c_list[m_presetitems[i].category_name].end(), name);
if (it == class_c_list[m_presetitems[i].category_name].end()) {
- class_c_list[m_presetitems[i].category_name].push_back(m_presetitems[i].group_name);
+ class_c_list[m_presetitems[i].category_name].push_back(name);
}
}
}
-
auto m_listsizer = new wxBoxSizer(wxVERTICAL);
- for (auto iter = class_c_list.begin(); iter != class_c_list.end(); iter++) {
-
+ for (auto category : category_list) {
+ auto iter = class_c_list.find(category);
//category
auto panel_category = new wxPanel(m_scrolledWindow, wxID_ANY, wxDefaultPosition, wxSize(-1, UNSAVE_CHANGE_DIALOG_ITEM_HEIGHT), wxTAB_TRAVERSAL);
panel_category->SetBackgroundColour(GREY300);
@@ -1554,7 +1595,7 @@ void UnsavedChangesDialog::update_list()
wxBoxSizer *sizer_left_v = new wxBoxSizer(wxVERTICAL);
- auto text_left = new wxStaticText(panel_left, wxID_ANY, gname, wxDefaultPosition, wxSize(-1, -1), 0);
+ auto text_left = new wxStaticText(panel_left, wxID_ANY, class_g_list[gname][0].group_name, wxDefaultPosition, wxSize(-1, -1), 0);
text_left->SetFont(::Label::Head_13);
text_left->Wrap(-1);
text_left->SetForegroundColour(GREY700);
@@ -1601,7 +1642,7 @@ void UnsavedChangesDialog::update_list()
auto text_oldv = new wxStaticText(panel_oldv, wxID_ANY, data.old_value, wxDefaultPosition, wxDefaultSize, wxST_ELLIPSIZE_END);
text_oldv->SetFont(::Label::Body_13);
text_oldv->Wrap(-1);
- text_oldv->SetForegroundColour(GREY700);
+ text_oldv->SetForegroundColour(params && params->left_to_right ? wxGetApp().get_label_clr_modified() : GREY700);
sizer_old_v->Add(text_oldv, 0, wxALIGN_CENTER|wxLEFT|wxRIGHT, 5);
panel_oldv->SetSizer(sizer_old_v);
@@ -1615,7 +1656,7 @@ void UnsavedChangesDialog::update_list()
auto text_newv = new wxStaticText(panel_newv, wxID_ANY, data.new_value, wxDefaultPosition, wxDefaultSize, wxST_ELLIPSIZE_END);
text_newv->SetFont(::Label::Body_13);
text_newv->Wrap(-1);
- text_newv->SetForegroundColour(GREY700);
+ text_newv->SetForegroundColour(params && !params->left_to_right ? wxGetApp().get_label_clr_modified() : GREY700);
sizer_new_v->Add(text_newv, 0, wxALIGN_CENTER|wxLEFT|wxRIGHT, 5);
@@ -1640,13 +1681,14 @@ void UnsavedChangesDialog::update_list()
}
m_scrolledWindow->SetSizer(m_listsizer);
- // m_scrolledWindow->Layout();
- wxSize text_size = m_action_line->GetTextExtent(m_action_line->GetLabel());
+ m_scrolledWindow->Layout();
+ /*wxSize text_size = m_action_line->GetTextExtent(m_action_line->GetLabel());
int width = UNSAVE_CHANGE_DIALOG_ACTION_LINE_SIZE.GetWidth();
// +2: Ensure that there is at least one line and that the content contains '\n'
- int rows = int(text_size.GetWidth() / width) + 2;
+ int rows = int(text_size.GetWidth() / width) + 2;
int height = rows * text_size.GetHeight();
m_action_line->SetMinSize(wxSize(width, height));
+ m_action_line->Wrap(UNSAVE_CHANGE_DIALOG_ACTION_LINE_SIZE.GetWidth());*/
Layout();
Fit();
}
@@ -1662,6 +1704,23 @@ std::string UnsavedChangesDialog::subreplace(std::string resource_str, std::stri
return dst_str;
}
+void UnsavedChangesDialog::update_tree(Preset::Type type, DynamicConfig * config, int from, int to)
+{
+ Search::OptionsSearcher &searcher = wxGetApp().sidebar().get_searcher();
+ searcher.sort_options_by_key();
+
+ for (const std::string &opt_key : config->keys()) {
+ int variant_index = -2;
+ const Search::Option &option = searcher.get_option(opt_key, type, variant_index);
+ auto category = option.category_local;
+ auto opt = dynamic_cast(config->option(opt_key));
+ std::string value_from = opt->vserialize()[from];
+ std::string value_to = opt->vserialize()[to];
+ PresetItem pi = {type, opt_key, category, option.group_local, option.label_local, into_u8(value_from), into_u8(value_to)};
+ m_presetitems.push_back(pi);
+ }
+}
+
void UnsavedChangesDialog::update_tree(Preset::Type type, PresetCollection* presets_)
{
Search::OptionsSearcher& searcher = wxGetApp().sidebar().get_searcher();
@@ -1693,8 +1752,7 @@ void UnsavedChangesDialog::update_tree(Preset::Type type, PresetCollection* pres
//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_FILAMENT || type == Preset::TYPE_SLA_MATERIAL);
+ const bool deep_compare = (type == Preset::TYPE_PRINTER || type == Preset::TYPE_PRINT || type == Preset::TYPE_FILAMENT || type == Preset::TYPE_SLA_MATERIAL);
auto dirty_options = presets->current_dirty_options(deep_compare);
// process changes of extruders count
@@ -1714,18 +1772,32 @@ void UnsavedChangesDialog::update_tree(Preset::Type type, PresetCollection* pres
}
}
+ auto variant_key = Preset::get_iot_type_string(type) + "_extruder_variant";
+ auto id_key = Preset::get_iot_type_string(type) + "_extruder_id";
+ auto extruder_variant = dynamic_cast(old_config.option(variant_key));
+ auto extruder_id = dynamic_cast(old_config.option(id_key));
+
for (const std::string& opt_key : dirty_options) {
- const std::string lookup_key = get_pure_opt_key(opt_key);
- Search::Option option = searcher.get_option(lookup_key, type);
- if (get_pure_opt_key(option.opt_key()) != lookup_key)
- option = searcher.get_option(opt_key, get_full_label(opt_key, new_config), type);
- if (get_pure_opt_key(option.opt_key()) != lookup_key) {
- // When the found option is not the requested one.
- // This can happen for dirty_options such as:
- // "default_print_profile", "printer_model", "printer_settings_id",
- // because they do not exist in the searcher.
+ int variant_index = -2;
+ const Search::Option &option = searcher.get_option(opt_key, type, variant_index);
+ if (option.opt_key() != opt_key && variant_index < -1) {
+ // 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;
}
+ auto category = option.category_local;
+ if (variant_index >= 0) {
+ if (printer_options_with_variant_2.count(opt_key.substr(0, opt_key.find_last_of('#'))) > 0)
+ variant_index /= 2;
+ if (boost::nowide::narrow(category).find("Extruder ") == 0)
+ category = category.substr(0, 8);
+ if (extruder_id)
+ category = category + (wxString(" {") + (extruder_id->values[variant_index] == 1 ? _L("Left: ") : _L("Right: "))
+ + L(extruder_variant->values[variant_index]) + "}");
+ else
+ category = category + (wxString(" {") + L(extruder_variant->values[variant_index]) + "}");
+ }
/*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));*/
@@ -1733,7 +1805,7 @@ void UnsavedChangesDialog::update_tree(Preset::Type type, PresetCollection* pres
//PresetItem pi = {opt_key, type, 1983};
//m_presetitems.push_back()
- PresetItem pi = {type, opt_key, option.category_local, option.group_local, option.label_local, get_string_value(opt_key, old_config), get_string_value(opt_key, new_config)};
+ PresetItem pi = {type, opt_key, category, option.group_local, option.label_local, get_string_value(opt_key, old_config), get_string_value(opt_key, new_config)};
m_presetitems.push_back(pi);
}
@@ -2273,7 +2345,7 @@ void DiffPresetDialog::update_tree()
wxString right_val = get_string_value(opt_key, right_congig);
const std::string lookup_key = get_pure_opt_key(opt_key);
- Search::Option option = searcher.get_option(lookup_key, type);
+ Search::Option option = searcher.get_option(lookup_key, get_full_label(lookup_key, left_config), type);
if (get_pure_opt_key(option.opt_key()) != lookup_key)
option = searcher.get_option(opt_key, get_full_label(opt_key, left_config), type);
if (get_pure_opt_key(option.opt_key()) != lookup_key) {
diff --git a/src/slic3r/GUI/UnsavedChangesDialog.hpp b/src/slic3r/GUI/UnsavedChangesDialog.hpp
index 8a9d9e680c..b25e852c6b 100644
--- a/src/slic3r/GUI/UnsavedChangesDialog.hpp
+++ b/src/slic3r/GUI/UnsavedChangesDialog.hpp
@@ -327,19 +327,30 @@ private:
public:
+ struct SyncExtruderParams
+ {
+ DynamicConfig *config;
+ int from;
+ int to;
+ bool left_to_right;
+ NozzleVolumeType nozzle;
+ };
+
// show unsaved changes when preset is switching
UnsavedChangesDialog(Preset::Type type, PresetCollection* dependent_presets, const std::string& new_selected_preset, bool no_transfer = false);
// show unsaved changes for all another cases
UnsavedChangesDialog(const wxString& caption, const wxString& header, const std::string& app_config_key, int act_buttons);
+ UnsavedChangesDialog(const wxString &caption, const wxString &header, DynamicConfig *config, int from, int to, bool left_to_right, NozzleVolumeType nozzle);
~UnsavedChangesDialog() override = default;
int ShowModal();
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_list();
+ void update_list(SyncExtruderParams *params = nullptr);
std::string subreplace(std::string resource_str, std::string sub_str, std::string new_str);
void update_tree(Preset::Type type, PresetCollection *presets);
+ void update_tree(Preset::Type type, DynamicConfig *config, int from, int to);
void show_info_line(Action action, std::string preset_name = "");
void update_config(Action action);
void close(Action action);