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);