diff options
Diffstat (limited to 'src/slic3r/GUI/Tab.cpp')
-rw-r--r-- | src/slic3r/GUI/Tab.cpp | 2413 |
1 files changed, 1425 insertions, 988 deletions
diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index adfae9024..ccb82b011 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1,15 +1,18 @@ // #include "libslic3r/GCodeSender.hpp" #include "slic3r/Utils/Serial.hpp" #include "Tab.hpp" -#include "PresetBundle.hpp" #include "PresetHints.hpp" +#include "libslic3r/PresetBundle.hpp" #include "libslic3r/Utils.hpp" +#include "libslic3r/Model.hpp" #include "slic3r/Utils/Http.hpp" #include "slic3r/Utils/PrintHost.hpp" #include "BonjourDialog.hpp" #include "WipeTowerDialog.hpp" #include "ButtonsDescription.hpp" +#include "Search.hpp" +#include "OG_CustomCtrl.hpp" #include <wx/app.h> #include <wx/button.h> @@ -24,19 +27,75 @@ #include <wx/filedlg.h> #include <boost/algorithm/string/predicate.hpp> +#include <boost/algorithm/string/replace.hpp> #include "wxExtensions.hpp" +#include "PresetComboBoxes.hpp" #include <wx/wupdlock.h> #include "GUI_App.hpp" #include "GUI_ObjectList.hpp" -#include "ConfigWizard.hpp" +#include "Plater.hpp" +#include "MainFrame.hpp" +#include "format.hpp" +#include "PhysicalPrinterDialog.hpp" +#include "UnsavedChangesDialog.hpp" +#include "SavePresetDialog.hpp" + +#ifdef WIN32 + #include <commctrl.h> +#endif // WIN32 namespace Slic3r { namespace GUI { -wxDEFINE_EVENT(EVT_TAB_VALUE_CHANGED, wxCommandEvent); -wxDEFINE_EVENT(EVT_TAB_PRESETS_CHANGED, SimpleEvent); +void Tab::Highlighter::set_timer_owner(wxEvtHandler* owner, int timerid/* = wxID_ANY*/) +{ + m_timer.SetOwner(owner, timerid); +} + +void Tab::Highlighter::init(std::pair<OG_CustomCtrl*, bool*> params) +{ + if (m_timer.IsRunning()) + invalidate(); + if (!params.first || !params.second) + return; + + m_timer.Start(300, false); + + m_custom_ctrl = params.first; + m_show_blink_ptr = params.second; + + *m_show_blink_ptr = true; + m_custom_ctrl->Refresh(); +} + +void Tab::Highlighter::invalidate() +{ + m_timer.Stop(); + + if (m_custom_ctrl && m_show_blink_ptr) { + *m_show_blink_ptr = false; + m_custom_ctrl->Refresh(); + m_show_blink_ptr = nullptr; + m_custom_ctrl = nullptr; + } + + m_blink_counter = 0; +} + +void Tab::Highlighter::blink() +{ + if (m_custom_ctrl && m_show_blink_ptr) { + *m_show_blink_ptr = !*m_show_blink_ptr; + m_custom_ctrl->Refresh(); + } + else + return; + + if ((++m_blink_counter) == 11) + invalidate(); +} Tab::Tab(wxNotebook* parent, const wxString& title, Preset::Type type) : m_parent(parent), m_title(title), m_type(type) @@ -47,27 +106,33 @@ Tab::Tab(wxNotebook* parent, const wxString& title, Preset::Type type) : m_compatible_printers.type = Preset::TYPE_PRINTER; m_compatible_printers.key_list = "compatible_printers"; m_compatible_printers.key_condition = "compatible_printers_condition"; - m_compatible_printers.dialog_title = _(L("Compatible printers")).ToUTF8(); - m_compatible_printers.dialog_label = _(L("Select the printers this profile is compatible with.")).ToUTF8(); + m_compatible_printers.dialog_title = _L("Compatible printers"); + m_compatible_printers.dialog_label = _L("Select the printers this profile is compatible with."); m_compatible_prints.type = Preset::TYPE_PRINT; m_compatible_prints.key_list = "compatible_prints"; m_compatible_prints.key_condition = "compatible_prints_condition"; - m_compatible_prints.dialog_title = _(L("Compatible print profiles")).ToUTF8(); - m_compatible_prints.dialog_label = _(L("Select the print profiles this profile is compatible with.")).ToUTF8(); + m_compatible_prints.dialog_title = _L("Compatible print profiles"); + m_compatible_prints.dialog_label = _L("Select the print profiles this profile is compatible with."); wxGetApp().tabs_list.push_back(this); - m_em_unit = wxGetApp().em_unit(); + m_em_unit = em_unit(m_parent); //wxGetApp().em_unit(); m_config_manipulation = get_config_manipulation(); Bind(wxEVT_SIZE, ([this](wxSizeEvent &evt) { - for (auto page : m_pages) - if (! page.get()->IsShown()) - page->layout_valid = false; + //for (auto page : m_pages) + // if (! page.get()->IsShown()) + // page->layout_valid = false; evt.Skip(); })); + + m_highlighter.set_timer_owner(this, 0); + this->Bind(wxEVT_TIMER, [this](wxTimerEvent&) + { + m_highlighter.blink(); + }); } void Tab::set_type() @@ -112,7 +177,18 @@ void Tab::create_preset_tab() #endif //__WXOSX__ // preset chooser - m_presets_choice = new PresetBitmapComboBox(panel, wxSize(35 * m_em_unit, -1)); + m_presets_choice = new TabPresetComboBox(panel, m_type); + m_presets_choice->set_selection_changed_function([this](int selection) { + if (!m_presets_choice->selection_is_changed_according_to_physical_printers()) + { + if (m_type == Preset::TYPE_PRINTER && !m_presets_choice->is_selected_physical_printer()) + m_preset_bundle->physical_printers.unselect_printer(); + + // select preset + std::string preset_name = m_presets_choice->GetString(selection).ToUTF8().data(); + select_preset(Preset::remove_suffix_modified(preset_name)); + } + }); auto color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); @@ -122,6 +198,8 @@ void Tab::create_preset_tab() 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) + add_scaled_button(panel, &m_btn_edit_ph_printer, "cog"); m_show_incompatible_presets = false; add_scaled_bitmap(this, m_bmp_show_incompatible_presets, "flag_red"); @@ -132,22 +210,22 @@ void Tab::create_preset_tab() // 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"))); - m_btn_delete_preset->Disable(); + m_btn_delete_preset->Hide(); add_scaled_button(panel, &m_question_btn, "question"); - m_question_btn->SetToolTip(_(L("Hover the cursor over buttons to find more information \n" "or click this button."))); - // Determine the theme color of OS (dark or light) - auto luma = wxGetApp().get_colour_approx_luma(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); + add_scaled_button(panel, &m_search_btn, "search"); + m_search_btn->SetToolTip(format_wxstr(_L("Search in settings [%1%]"), "Ctrl+F")); + // Bitmaps to be shown on the "Revert to system" aka "Lock to system" button next to each input field. - add_scaled_bitmap(this, m_bmp_value_lock , luma >= 128 ? "lock_closed" : "lock_closed_white"); + add_scaled_bitmap(this, m_bmp_value_lock , "lock_closed"); add_scaled_bitmap(this, m_bmp_value_unlock, "lock_open"); m_bmp_non_system = &m_bmp_white_bullet; // Bitmaps to be shown on the "Undo user changes" button next to each input field. add_scaled_bitmap(this, m_bmp_value_revert, "undo"); - add_scaled_bitmap(this, m_bmp_white_bullet, luma >= 128 ? "dot" : "dot_white"); + add_scaled_bitmap(this, m_bmp_white_bullet, "dot"); fill_icon_descriptions(); set_tooltips_text(); @@ -169,6 +247,7 @@ void Tab::create_preset_tab() } } })); + m_search_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent) { wxGetApp().plater()->search(false); }); // Colors for ui "decoration" m_sys_label_clr = wxGetApp().get_label_clr_sys(); @@ -186,13 +265,19 @@ void Tab::create_preset_tab() m_hsizer->Add(m_btn_save_preset, 0, wxALIGN_CENTER_VERTICAL); m_hsizer->AddSpacer(int(4 * scale_factor)); m_hsizer->Add(m_btn_delete_preset, 0, wxALIGN_CENTER_VERTICAL); - m_hsizer->AddSpacer(int(16 * scale_factor)); + if (m_btn_edit_ph_printer) { + m_hsizer->AddSpacer(int(4 * scale_factor)); + m_hsizer->Add(m_btn_edit_ph_printer, 0, wxALIGN_CENTER_VERTICAL); + } + m_hsizer->AddSpacer(int(/*16*/8 * scale_factor)); m_hsizer->Add(m_btn_hide_incompatible_presets, 0, wxALIGN_CENTER_VERTICAL); - m_hsizer->AddSpacer(int(64 * scale_factor)); + m_hsizer->AddSpacer(int(8 * scale_factor)); + m_hsizer->Add(m_question_btn, 0, wxALIGN_CENTER_VERTICAL); + m_hsizer->AddSpacer(int(32 * scale_factor)); m_hsizer->Add(m_undo_to_sys_btn, 0, wxALIGN_CENTER_VERTICAL); m_hsizer->Add(m_undo_btn, 0, wxALIGN_CENTER_VERTICAL); m_hsizer->AddSpacer(int(32 * scale_factor)); - m_hsizer->Add(m_question_btn, 0, wxALIGN_CENTER_VERTICAL); + m_hsizer->Add(m_search_btn, 0, wxALIGN_CENTER_VERTICAL); // 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 @@ -219,33 +304,46 @@ void Tab::create_preset_tab() m_treectrl->AssignImageList(m_icons); m_treectrl->AddRoot("root"); m_treectrl->SetIndent(0); - m_disable_tree_sel_changed_event = 0; - m_treectrl->Bind(wxEVT_TREE_SEL_CHANGED, &Tab::OnTreeSelChange, this); + // Delay processing of the following handler until the message queue is flushed. + // This helps to process all the cursor key events on Windows in the tree control, + // so that the cursor jumps to the last item. + m_treectrl->Bind(wxEVT_TREE_SEL_CHANGED, [this](wxTreeEvent&) { +#ifdef __linux__ + // Events queue is opposite On Linux. wxEVT_SET_FOCUS invokes after wxEVT_TREE_SEL_CHANGED, + // and a result wxEVT_KILL_FOCUS doesn't invoke for the TextCtrls. + // see https://github.com/prusa3d/PrusaSlicer/issues/5720 + // So, call SetFocus explicitly for this control before changing of the selection + m_treectrl->SetFocus(); +#endif + if (!m_disable_tree_sel_changed_event && !m_pages.empty()) { + if (m_page_switch_running) + m_page_switch_planned = true; + else { + m_page_switch_running = true; + do { + m_page_switch_planned = false; + m_treectrl->Update(); + } while (this->tree_sel_change_delayed()); + m_page_switch_running = false; + } + } + }); + m_treectrl->Bind(wxEVT_KEY_DOWN, &Tab::OnKeyDown, this); - m_presets_choice->Bind(wxEVT_COMBOBOX, ([this](wxCommandEvent e) { - //! Because of The MSW and GTK version of wxBitmapComboBox derived from wxComboBox, - //! but the OSX version derived from wxOwnerDrawnCombo, instead of: - //! select_preset(m_presets_choice->GetStringSelection().ToUTF8().data()); - //! we doing next: - int selected_item = m_presets_choice->GetSelection(); - if (m_selected_preset_item == size_t(selected_item) && !m_presets->current_is_dirty()) - return; - if (selected_item >= 0) { - std::string selected_string = m_presets_choice->GetString(selected_item).ToUTF8().data(); - if (selected_string.find(PresetCollection::separator_head()) == 0 - /*selected_string == "------- System presets -------" || - selected_string == "------- User presets -------"*/) { - m_presets_choice->SetSelection(m_selected_preset_item); - if (wxString::FromUTF8(selected_string.c_str()) == PresetCollection::separator(L("Add a new printer"))) - wxTheApp->CallAfter([]() { wxGetApp().run_wizard(ConfigWizard::RR_USER); }); - return; - } - m_selected_preset_item = selected_item; - select_preset(selected_string); - } - })); + // Initialize the page. +#ifdef __WXOSX__ + auto page_parent = m_tmp_panel; +#else + auto page_parent = this; +#endif + + m_page_view = new wxScrolledWindow(page_parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); + m_page_sizer = new wxBoxSizer(wxVERTICAL); + m_page_view->SetSizer(m_page_sizer); + m_page_view->SetScrollbars(1, 20, 1, 2); + m_hsizer->Add(m_page_view, 1, wxEXPAND | wxLEFT, 5); m_btn_save_preset->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e) { save_preset(); })); m_btn_delete_preset->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e) { delete_preset(); })); @@ -253,15 +351,23 @@ void Tab::create_preset_tab() toggle_show_hide_incompatible(); })); - // Fill cache for mode bitmaps - m_mode_bitmap_cache.reserve(3); - m_mode_bitmap_cache.push_back(ScalableBitmap(this, "mode_simple" , mode_icon_px_size())); - m_mode_bitmap_cache.push_back(ScalableBitmap(this, "mode_advanced", mode_icon_px_size())); - m_mode_bitmap_cache.push_back(ScalableBitmap(this, "mode_expert" , mode_icon_px_size())); + if (m_btn_edit_ph_printer) + m_btn_edit_ph_printer->Bind(wxEVT_BUTTON, [this](wxCommandEvent e) { + if (m_preset_bundle->physical_printers.has_selection()) + m_presets_choice->edit_physical_printer(); + else + m_presets_choice->add_physical_printer(); + }); // Initialize the DynamicPrintConfig by default keys/values. build(); + + // ys_FIXME: Following should not be needed, the function will be called later + // (update_mode->update_visibility->rebuild_page_tree). This does not work, during the + // second call of rebuild_page_tree m_treectrl->GetFirstVisibleItem(); returns zero + // for some unknown reason (and the page is not refreshed until user does a selection). rebuild_page_tree(); + m_completed = true; } @@ -271,7 +377,7 @@ void Tab::add_scaled_button(wxWindow* parent, const wxString& label/* = wxEmptyString*/, long style /*= wxBU_EXACTFIT | wxNO_BORDER*/) { - *btn = new ScalableButton(parent, wxID_ANY, icon_name, label, wxDefaultSize, wxDefaultPosition, style); + *btn = new ScalableButton(parent, wxID_ANY, icon_name, label, wxDefaultSize, wxDefaultPosition, style, true); m_scaled_buttons.push_back(*btn); } @@ -305,6 +411,11 @@ Slic3r::GUI::PageShp Tab::add_options_page(const wxString& title, const std::str icon_idx = ++m_icon_count; m_icon_index[icon] = icon_idx; } + + if (m_category_icon.find(title) == m_category_icon.end()) { + // Add new category to the category_to_icon list. + m_category_icon[title] = icon; + } } // Initialize the page. #ifdef __WXOSX__ @@ -312,16 +423,12 @@ Slic3r::GUI::PageShp Tab::add_options_page(const wxString& title, const std::str #else auto panel = this; #endif - PageShp page(new Page(panel, title, icon_idx, m_mode_bitmap_cache)); + PageShp page(new Page(m_page_view, title, icon_idx)); // page->SetBackgroundStyle(wxBG_STYLE_SYSTEM); #ifdef __WINDOWS__ // page->SetDoubleBuffered(true); #endif //__WINDOWS__ - page->SetScrollbars(1, 20, 1, 2); - page->Hide(); - m_hsizer->Add(page.get(), 1, wxEXPAND | wxLEFT, 5); - if (!is_extruder_pages) m_pages.push_back(page); @@ -329,21 +436,61 @@ Slic3r::GUI::PageShp Tab::add_options_page(const wxString& title, const std::str return page; } +// Names of categories is save in English always. We translate them only for UI. +// But category "Extruder n" can't be translated regularly (using _()), so +// just for this category we should splite the title and translate "Extruder" word separately +wxString Tab::translate_category(const wxString& title, Preset::Type preset_type) +{ + if (preset_type == Preset::TYPE_PRINTER && title.Contains("Extruder ")) { + return _("Extruder") + title.SubString(8, title.Last()); + } + return _(title); +} + void Tab::OnActivate() { -#ifdef __WXOSX__ wxWindowUpdateLocker noUpdates(this); - +#ifdef __WXOSX__ +// wxWindowUpdateLocker noUpdates(this); auto size = GetSizer()->GetSize(); m_tmp_panel->GetSizer()->SetMinSize(size.x + m_size_move, size.y); Fit(); m_size_move *= -1; #endif // __WXOSX__ + +#ifdef __WXMSW__ + // Workaround for tooltips over Tree Controls displayed over excessively long + // tree control items, stealing the window focus. + // + // In case the Tab was reparented from the MainFrame to the floating dialog, + // the tooltip created by the Tree Control before reparenting is not reparented, + // but it still points to the MainFrame. If the tooltip pops up, the MainFrame + // is incorrectly focussed, stealing focus from the floating dialog. + // + // The workaround is to delete the tooltip control. + // Vojtech tried to reparent the tooltip control, but it did not work, + // and if the Tab was later reparented back to MainFrame, the tooltip was displayed + // at an incorrect position, therefore it is safer to just discard the tooltip control + // altogether. + HWND hwnd_tt = TreeView_GetToolTips(m_treectrl->GetHandle()); + if (hwnd_tt) { + HWND hwnd_toplevel = find_toplevel_parent(m_treectrl)->GetHandle(); + HWND hwnd_parent = ::GetParent(hwnd_tt); + if (hwnd_parent != hwnd_toplevel) { + ::DestroyWindow(hwnd_tt); + TreeView_SetToolTips(m_treectrl->GetHandle(), nullptr); + } + } +#endif + + // create controls on active page + activate_selected_page([](){}); + m_hsizer->Layout(); + Refresh(); } void Tab::update_labels_colour() { -// Freeze(); //update options "decoration" for (const auto opt : m_options_list) { @@ -358,20 +505,17 @@ void Tab::update_labels_colour() else color = &m_modified_label_clr; } - if (opt.first == "bed_shape" || opt.first == "compatible_prints" || opt.first == "compatible_printers") { - wxStaticText* label = (m_colored_Labels.find(opt.first) == m_colored_Labels.end()) ? nullptr : m_colored_Labels.at(opt.first); - if (label) { - label->SetForegroundColour(*color); - label->Refresh(true); - } + if (opt.first == "bed_shape" || opt.first == "filament_ramming_parameters" || + opt.first == "compatible_prints" || opt.first == "compatible_printers" ) { + if (m_colored_Label_colors.find(opt.first) != m_colored_Label_colors.end()) + m_colored_Label_colors.at(opt.first) = *color; continue; } Field* field = get_field(opt.first); if (field == nullptr) continue; - field->set_label_colour_force(color); + field->set_label_colour(color); } -// Thaw(); auto cur_item = m_treectrl->GetFirstVisibleItem(); if (!cur_item || !m_treectrl->IsVisible(cur_item)) @@ -380,7 +524,7 @@ void Tab::update_labels_colour() auto title = m_treectrl->GetItemText(cur_item); for (auto page : m_pages) { - if (page->title() != title) + if (translate_category(page->title(), m_type) != title) continue; const wxColor *clr = !page->m_is_nonsys_values ? &m_sys_label_clr : @@ -394,42 +538,32 @@ void Tab::update_labels_colour() } } -// Update UI according to changes -void Tab::update_changed_ui() +void Tab::decorate() { - if (m_postpone_update_ui) - return; - - const bool deep_compare = (m_type == Slic3r::Preset::TYPE_PRINTER || m_type == Slic3r::Preset::TYPE_SLA_MATERIAL); - auto dirty_options = m_presets->current_dirty_options(deep_compare); - auto nonsys_options = m_presets->current_different_from_parent_options(deep_compare); - if (m_type == Slic3r::Preset::TYPE_PRINTER) { - TabPrinter* tab = static_cast<TabPrinter*>(this); - if (tab->m_initial_extruders_count != tab->m_extruders_count) - dirty_options.emplace_back("extruders_count"); - if (tab->m_sys_extruders_count != tab->m_extruders_count) - nonsys_options.emplace_back("extruders_count"); - } + for (const auto opt : m_options_list) + { + Field* field = nullptr; + wxColour* colored_label_clr = nullptr; - for (auto& it : m_options_list) - it.second = m_opt_status_value; + if (opt.first == "bed_shape" || opt.first == "filament_ramming_parameters" || + opt.first == "compatible_prints" || opt.first == "compatible_printers") + colored_label_clr = (m_colored_Label_colors.find(opt.first) == m_colored_Label_colors.end()) ? nullptr : &m_colored_Label_colors.at(opt.first); - for (auto opt_key : dirty_options) m_options_list[opt_key] &= ~osInitValue; - for (auto opt_key : nonsys_options) m_options_list[opt_key] &= ~osSystemValue; + if (!colored_label_clr) { + field = get_field(opt.first); + if (!field) + continue; + } -// Freeze(); - //update options "decoration" - for (const auto opt : m_options_list) - { bool is_nonsys_value = false; bool is_modified_value = true; - const ScalableBitmap *sys_icon = &m_bmp_value_lock; - const ScalableBitmap *icon = &m_bmp_value_revert; + const ScalableBitmap* sys_icon = &m_bmp_value_lock; + const ScalableBitmap* icon = &m_bmp_value_revert; - const wxColour *color = m_is_default_preset ? &m_default_text_clr : &m_sys_label_clr; + const wxColour* color = m_is_default_preset ? &m_default_text_clr : &m_sys_label_clr; - const wxString *sys_tt = &m_tt_value_lock; - const wxString *tt = &m_tt_value_revert; + const wxString* sys_tt = &m_tt_value_lock; + const wxString* tt = &m_tt_value_revert; // value isn't equal to system value if ((opt.second & osSystemValue) == 0) { @@ -449,17 +583,12 @@ void Tab::update_changed_ui() icon = &m_bmp_white_bullet; tt = &m_tt_white_bullet; } - if (opt.first == "bed_shape" || opt.first == "compatible_prints" || opt.first == "compatible_printers") { - wxStaticText* label = (m_colored_Labels.find(opt.first) == m_colored_Labels.end()) ? nullptr : m_colored_Labels.at(opt.first); - if (label) { - label->SetForegroundColour(*color); - label->Refresh(true); - } + + if (colored_label_clr) { + *colored_label_clr = *color; continue; } - - Field* field = get_field(opt.first); - if (field == nullptr) continue; + field->m_is_nonsys_value = is_nonsys_value; field->m_is_modified_value = is_modified_value; field->set_undo_bitmap(icon); @@ -468,7 +597,35 @@ void Tab::update_changed_ui() field->set_undo_to_sys_tooltip(sys_tt); field->set_label_colour(color); } -// Thaw(); + + if (m_active_page) + m_active_page->refresh(); +} + +// Update UI according to changes +void Tab::update_changed_ui() +{ + if (m_postpone_update_ui) + return; + + const bool deep_compare = (m_type == Slic3r::Preset::TYPE_PRINTER || m_type == Slic3r::Preset::TYPE_SLA_MATERIAL); + auto dirty_options = m_presets->current_dirty_options(deep_compare); + auto nonsys_options = m_presets->current_different_from_parent_options(deep_compare); + if (m_type == Slic3r::Preset::TYPE_PRINTER) { + TabPrinter* tab = static_cast<TabPrinter*>(this); + if (tab->m_initial_extruders_count != tab->m_extruders_count) + dirty_options.emplace_back("extruders_count"); + if (tab->m_sys_extruders_count != tab->m_extruders_count) + nonsys_options.emplace_back("extruders_count"); + } + + for (auto& it : m_options_list) + it.second = m_opt_status_value; + + for (auto opt_key : dirty_options) m_options_list[opt_key] &= ~osInitValue; + for (auto opt_key : nonsys_options) m_options_list[opt_key] &= ~osSystemValue; + + decorate(); wxTheApp->CallAfter([this]() { if (parent()) //To avoid a crash, parent should be exist for a moment of a tree updating @@ -500,7 +657,7 @@ void TabPrinter::init_options_list() for (const auto opt_key : m_config->keys()) { - if (opt_key == "bed_shape") { + if (opt_key == "bed_shape" || opt_key == "thumbnails") { m_options_list.emplace(opt_key, m_opt_status_value); continue; } @@ -527,6 +684,21 @@ void TabPrinter::msw_rescale() for (auto page : pages) page->msw_rescale(); + if (m_reset_to_filament_color) + m_reset_to_filament_color->msw_rescale(); + + Layout(); +} + +void TabPrinter::sys_color_changed() +{ + Tab::sys_color_changed(); + + // update missed options_groups + const std::vector<PageShp>& pages = m_printer_technology == ptFFF ? m_pages_sla : m_pages_fff; + for (auto page : pages) + page->sys_color_changed(); + Layout(); } @@ -579,17 +751,20 @@ void Tab::update_changed_tree_ui() auto title = m_treectrl->GetItemText(cur_item); for (auto page : m_pages) { - if (page->title() != title) + if (translate_category(page->title(), m_type) != title) continue; bool sys_page = true; bool modified_page = false; - if (title == _("General")) { + if (page->title() == "General") { std::initializer_list<const char*> optional_keys{ "extruders_count", "bed_shape" }; for (auto &opt_key : optional_keys) { get_sys_and_mod_flags(opt_key, sys_page, modified_page); } } - if (title == _("Dependencies")) { + if (m_type == Preset::TYPE_FILAMENT && page->title() == "Advanced") { + get_sys_and_mod_flags("filament_ramming_parameters", sys_page, modified_page); + } + if (page->title() == "Dependencies") { if (m_type == Slic3r::Preset::TYPE_PRINTER) { sys_page = m_presets->get_selected_preset_parent() != nullptr; modified_page = false; @@ -603,8 +778,8 @@ void Tab::update_changed_tree_ui() { if (!sys_page && modified_page) break; - for (t_opt_map::iterator it = group->m_opt_map.begin(); it != group->m_opt_map.end(); ++it) { - const std::string& opt_key = it->first; + for (const auto &kvp : group->opt_map()) { + const std::string& opt_key = kvp.first; get_sys_and_mod_flags(opt_key, sys_page, modified_page); } } @@ -642,6 +817,8 @@ void Tab::update_undo_buttons() void Tab::on_roll_back_value(const bool to_sys /*= true*/) { + if (!m_active_page) return; + int os; if (to_sys) { if (!m_is_nonsys_values) return; @@ -654,49 +831,47 @@ void Tab::on_roll_back_value(const bool to_sys /*= true*/) m_postpone_update_ui = true; - auto selection = m_treectrl->GetItemText(m_treectrl->GetSelection()); - for (auto page : m_pages) - if (page->title() == selection) { - for (auto group : page->m_optgroups) { - if (group->title == _("Capabilities")) { - if ((m_options_list["extruders_count"] & os) == 0) - to_sys ? group->back_to_sys_value("extruders_count") : group->back_to_initial_value("extruders_count"); - } - if (group->title == _("Size and coordinates")) { - if ((m_options_list["bed_shape"] & os) == 0) { - to_sys ? group->back_to_sys_value("bed_shape") : group->back_to_initial_value("bed_shape"); - load_key_value("bed_shape", true/*some value*/, true); - } - - } - if (group->title == _("Profile dependencies")) { - // "compatible_printers" option doesn't exists in Printer Settimgs Tab - if (m_type != Preset::TYPE_PRINTER && (m_options_list["compatible_printers"] & os) == 0) { - to_sys ? group->back_to_sys_value("compatible_printers") : group->back_to_initial_value("compatible_printers"); - load_key_value("compatible_printers", true/*some value*/, true); - - bool is_empty = m_config->option<ConfigOptionStrings>("compatible_printers")->values.empty(); - m_compatible_printers.checkbox->SetValue(is_empty); - is_empty ? m_compatible_printers.btn->Disable() : m_compatible_printers.btn->Enable(); - } - // "compatible_prints" option exists only in Filament Settimgs and Materials Tabs - if ((m_type == Preset::TYPE_FILAMENT || m_type == Preset::TYPE_SLA_MATERIAL) && (m_options_list["compatible_prints"] & os) == 0) { - to_sys ? group->back_to_sys_value("compatible_prints") : group->back_to_initial_value("compatible_prints"); - load_key_value("compatible_prints", true/*some value*/, true); - - bool is_empty = m_config->option<ConfigOptionStrings>("compatible_prints")->values.empty(); - m_compatible_prints.checkbox->SetValue(is_empty); - is_empty ? m_compatible_prints.btn->Disable() : m_compatible_prints.btn->Enable(); - } - } - for (auto kvp : group->m_opt_map) { - const std::string& opt_key = kvp.first; - if ((m_options_list[opt_key] & os) == 0) - to_sys ? group->back_to_sys_value(opt_key) : group->back_to_initial_value(opt_key); - } + for (auto group : m_active_page->m_optgroups) { + if (group->title == "Capabilities") { + if ((m_options_list["extruders_count"] & os) == 0) + to_sys ? group->back_to_sys_value("extruders_count") : group->back_to_initial_value("extruders_count"); + } + if (group->title == "Size and coordinates") { + if ((m_options_list["bed_shape"] & os) == 0) { + to_sys ? group->back_to_sys_value("bed_shape") : group->back_to_initial_value("bed_shape"); + load_key_value("bed_shape", true/*some value*/, true); } - break; } + if (group->title == "Toolchange parameters with single extruder MM printers") { + if ((m_options_list["filament_ramming_parameters"] & os) == 0) + to_sys ? group->back_to_sys_value("filament_ramming_parameters") : group->back_to_initial_value("filament_ramming_parameters"); + } + if (group->title == "Profile dependencies") { + // "compatible_printers" option doesn't exists in Printer Settimgs Tab + if (m_type != Preset::TYPE_PRINTER && (m_options_list["compatible_printers"] & os) == 0) { + to_sys ? group->back_to_sys_value("compatible_printers") : group->back_to_initial_value("compatible_printers"); + load_key_value("compatible_printers", true/*some value*/, true); + + bool is_empty = m_config->option<ConfigOptionStrings>("compatible_printers")->values.empty(); + m_compatible_printers.checkbox->SetValue(is_empty); + is_empty ? m_compatible_printers.btn->Disable() : m_compatible_printers.btn->Enable(); + } + // "compatible_prints" option exists only in Filament Settimgs and Materials Tabs + if ((m_type == Preset::TYPE_FILAMENT || m_type == Preset::TYPE_SLA_MATERIAL) && (m_options_list["compatible_prints"] & os) == 0) { + to_sys ? group->back_to_sys_value("compatible_prints") : group->back_to_initial_value("compatible_prints"); + load_key_value("compatible_prints", true/*some value*/, true); + + bool is_empty = m_config->option<ConfigOptionStrings>("compatible_prints")->values.empty(); + m_compatible_prints.checkbox->SetValue(is_empty); + is_empty ? m_compatible_prints.btn->Disable() : m_compatible_prints.btn->Enable(); + } + } + for (const auto &kvp : group->opt_map()) { + const std::string& opt_key = kvp.first; + if ((m_options_list[opt_key] & os) == 0) + to_sys ? group->back_to_sys_value(opt_key) : group->back_to_initial_value(opt_key); + } + } m_postpone_update_ui = false; update_changed_ui(); @@ -706,14 +881,14 @@ void Tab::on_roll_back_value(const bool to_sys /*= true*/) // comparing the selected preset config with $self->{config}. void Tab::update_dirty() { - m_presets->update_dirty_ui(m_presets_choice); + m_presets_choice->update_dirty(); on_presets_changed(); update_changed_ui(); } void Tab::update_tab_ui() { - m_selected_preset_item = m_presets->update_tab_ui(m_presets_choice, m_show_incompatible_presets, m_em_unit); + m_presets_choice->update(); } // Load a provied DynamicConfig into the tab, modifying the active preset. @@ -736,10 +911,8 @@ void Tab::load_config(const DynamicPrintConfig& config) // Reload current $self->{config} (aka $self->{presets}->edited_preset->config) into the UI fields. void Tab::reload_config() { -// Freeze(); - for (auto page : m_pages) - page->reload_config(); -// Thaw(); + if (m_active_page) + m_active_page->reload_config(); } void Tab::update_mode() @@ -758,16 +931,12 @@ void Tab::update_visibility() { Freeze(); // There is needed Freeze/Thaw to avoid a flashing after Show/Layout - // m_detach_preset_btn will be shown always after call page->update_visibility() - // So let save a "show state" of m_detach_preset_btn before update_visibility - bool was_shown = m_detach_preset_btn->IsShown(); - for (auto page : m_pages) - page->update_visibility(m_mode); - update_page_tree_visibility(); + page->update_visibility(m_mode, page.get() == m_active_page); + rebuild_page_tree(); - // update visibility for detach_preset_btn - m_detach_preset_btn->Show(was_shown); + if (this->m_type == Preset::TYPE_SLA_PRINT) + update_description_lines(); Layout(); Thaw(); @@ -775,22 +944,21 @@ void Tab::update_visibility() void Tab::msw_rescale() { - m_em_unit = wxGetApp().em_unit(); + m_em_unit = em_unit(m_parent); m_mode_sizer->msw_rescale(); + m_presets_choice->msw_rescale(); - m_presets_choice->SetSize(35 * m_em_unit, -1); m_treectrl->SetMinSize(wxSize(20 * m_em_unit, -1)); - update_tab_ui(); - // rescale buttons and cached bitmaps for (const auto btn : m_scaled_buttons) btn->msw_rescale(); for (const auto bmp : m_scaled_bitmaps) bmp->msw_rescale(); - for (ScalableBitmap& bmp : m_mode_bitmap_cache) - bmp.msw_rescale(); + + if (m_detach_preset_btn) + m_detach_preset_btn->msw_rescale(); // rescale icons for tree_ctrl for (ScalableBitmap& bmp : m_scaled_icons_list) @@ -803,34 +971,86 @@ void Tab::msw_rescale() m_treectrl->AssignImageList(m_icons); // rescale options_groups - for (auto page : m_pages) - page->msw_rescale(); + if (m_active_page) + m_active_page->msw_rescale(); + + Layout(); +} + +void Tab::sys_color_changed() +{ + update_tab_ui(); + m_presets_choice->msw_rescale(); + + // update buttons and cached bitmaps + for (const auto btn : m_scaled_buttons) + btn->msw_rescale(); + for (const auto bmp : m_scaled_bitmaps) + bmp->msw_rescale(); + + // update icons for tree_ctrl + for (ScalableBitmap& bmp : m_scaled_icons_list) + bmp.msw_rescale(); + // recreate and set new ImageList for tree_ctrl + m_icons->RemoveAll(); + m_icons = new wxImageList(m_scaled_icons_list.front().bmp().GetWidth(), m_scaled_icons_list.front().bmp().GetHeight()); + for (ScalableBitmap& bmp : m_scaled_icons_list) + m_icons->Add(bmp.bmp()); + m_treectrl->AssignImageList(m_icons); + + // Colors for ui "decoration" + m_sys_label_clr = wxGetApp().get_label_clr_sys(); + m_modified_label_clr = wxGetApp().get_label_clr_modified(); + update_labels_colour(); + + // update options_groups + if (m_active_page) + m_active_page->msw_rescale(); Layout(); } Field* Tab::get_field(const t_config_option_key& opt_key, int opt_index/* = -1*/) const { + return m_active_page ? m_active_page->get_field(opt_key, opt_index) : nullptr; +} + +std::pair<OG_CustomCtrl*, bool*> Tab::get_custom_ctrl_with_blinking_ptr(const t_config_option_key& opt_key, int opt_index/* = -1*/) +{ + if (!m_active_page) + return {nullptr, nullptr}; + + std::pair<OG_CustomCtrl*, bool*> ret = {nullptr, nullptr}; + + for (auto opt_group : m_active_page->m_optgroups) { + ret = opt_group->get_custom_ctrl_with_blinking_ptr(opt_key, opt_index); + if (ret.first && ret.second) + break; + } + return ret; +} + +Field* Tab::get_field(const t_config_option_key& opt_key, Page** selected_page, int opt_index/* = -1*/) +{ Field* field = nullptr; for (auto page : m_pages) { field = page->get_field(opt_key, opt_index); - if (field != nullptr) + if (field != nullptr) { + *selected_page = page.get(); return field; + } } return field; } -// Set a key/value pair on this page. Return true if the value has been modified. -// Currently used for distributing extruders_count over preset pages of Slic3r::GUI::Tab::Printer -// after a preset is loaded. -bool Tab::set_value(const t_config_option_key& opt_key, const boost::any& value) { - bool changed = false; - for(auto page: m_pages) { - if (page->set_value(opt_key, value)) - changed = true; - } - return changed; -} +void Tab::toggle_option(const std::string& opt_key, bool toggle, int opt_index/* = -1*/) +{ + if (!m_active_page) + return; + Field* field = m_active_page->get_field(opt_key, opt_index); + if (field) + field->toggle(toggle); +}; // To be called by custom widgets, load a value into a config, // update the preset selection boxes (the dirty flags) @@ -844,7 +1064,7 @@ void Tab::load_key_value(const std::string& opt_key, const boost::any& value, bo // Don't select another profile if this profile happens to become incompatible. m_preset_bundle->update_compatible(PresetSelectCompatibleType::Never); } - m_presets->update_dirty_ui(m_presets_choice); + m_presets_choice->update_dirty(); on_presets_changed(); update(); } @@ -883,7 +1103,7 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value) if (opt_key == "pad_around_object") { for (PageShp &pg : m_pages) { - Field * fld = pg->get_field(opt_key); + Field * fld = pg->get_field(opt_key); /// !!! ysFIXME ???? if (fld) fld->set_value(value, false); } } @@ -925,6 +1145,65 @@ void Tab::update_wiping_button_visibility() { } } +void Tab::activate_option(const std::string& opt_key, const wxString& category) +{ + wxString page_title = translate_category(category, m_type); + + auto cur_item = m_treectrl->GetFirstVisibleItem(); + if (!cur_item || !m_treectrl->IsVisible(cur_item)) + return; + + while (cur_item) { + auto title = m_treectrl->GetItemText(cur_item); + if (page_title != title) { + cur_item = m_treectrl->GetNextVisible(cur_item); + continue; + } + + m_treectrl->SelectItem(cur_item); + break; + } + + // we should to activate a tab with searched option, if it doesn't. + wxGetApp().mainframe->select_tab(this); + Field* field = get_field(opt_key); + + // focused selected field + if (field) + field->getWindow()->SetFocus(); + else if (category == "Single extruder MM setup") { + // When we show and hide "Single extruder MM setup" page, + // related options are still in the search list + // So, let's hightlighte a "single_extruder_multi_material" option, + // as a "way" to show hidden page again + field = get_field("single_extruder_multi_material"); + if (field) + field->getWindow()->SetFocus(); + } + + m_highlighter.init(get_custom_ctrl_with_blinking_ptr(opt_key)); +} + +void Tab::apply_searcher() +{ + wxGetApp().sidebar().get_searcher().apply(m_config, m_type, m_mode); +} + +void Tab::cache_config_diff(const std::vector<std::string>& selected_options) +{ + m_cache_config.apply_only(m_presets->get_edited_preset().config, selected_options); +} + +void Tab::apply_config_from_cache() +{ + if (!m_cache_config.empty()) { + m_presets->get_edited_preset().config.apply(m_cache_config); + m_cache_config.clear(); + + update_dirty(); + } +} + // Call a callback to update the selection of presets on the plater: // To update the content of the selection boxes, @@ -939,14 +1218,16 @@ void Tab::on_presets_changed() // Instead of PostEvent (EVT_TAB_PRESETS_CHANGED) just call update_presets wxGetApp().plater()->sidebar().update_presets(m_type); - update_preset_description_line(); // Printer selected at the Printer tab, update "compatible" marks at the print and filament selectors. for (auto t: m_dependent_tabs) { + Tab* tab = wxGetApp().get_tab(t); // If the printer tells us that the print or filament/sla_material preset has been switched or invalidated, // refresh the print or filament/sla_material tab page. - wxGetApp().get_tab(t)->load_current_preset(); + // But if there are options, moved from the previously selected preset, update them to edited preset + tab->apply_config_from_cache(); + tab->load_current_preset(); } // clear m_dependent_tabs after first update from select_preset() // to avoid needless preset loading from update() function @@ -960,7 +1241,8 @@ void Tab::build_preset_description_line(ConfigOptionsGroup* optgroup) }; auto detach_preset_btn = [this](wxWindow* parent) { - add_scaled_button(parent, &m_detach_preset_btn, "lock_open_sys", _(L("Detach from system preset")), wxBU_LEFT | wxBU_EXACTFIT); + m_detach_preset_btn = new ScalableButton(parent, wxID_ANY, "lock_open_sys", _L("Detach from system preset"), + wxDefaultSize, wxDefaultPosition, wxBU_LEFT | wxBU_EXACTFIT, true); ScalableButton* btn = m_detach_preset_btn; btn->SetFont(Slic3r::GUI::wxGetApp().normal_font()); @@ -1013,7 +1295,9 @@ void Tab::update_preset_description_line() } else if (parent == nullptr) { description_line = _(L("Current preset is inherited from the default preset.")); } else { - description_line = _(L("Current preset is inherited from")) + ":\n\t" + parent->name; + std::string name = parent->name; + boost::replace_all(name, "&", "&&"); + description_line = _(L("Current preset is inherited from")) + ":\n\t" + from_u8(name); } if (preset.is_default || preset.is_system) @@ -1105,28 +1389,31 @@ void TabPrint::build() m_presets = &m_preset_bundle->prints; load_initial_data(); - auto page = add_options_page(_(L("Layers and perimeters")), "layers"); - auto optgroup = page->new_optgroup(_(L("Layer height"))); - optgroup->append_single_option_line("layer_height"); - optgroup->append_single_option_line("first_layer_height"); + auto page = add_options_page(L("Layers and perimeters"), "layers"); + wxString category_path = "layers-and-perimeters_1748#"; + auto optgroup = page->new_optgroup(L("Layer height")); + optgroup->append_single_option_line("layer_height", category_path + "layer-height"); + optgroup->append_single_option_line("first_layer_height", category_path + "first-layer-height"); - optgroup = page->new_optgroup(_(L("Vertical shells"))); - optgroup->append_single_option_line("perimeters"); - optgroup->append_single_option_line("spiral_vase"); + optgroup = page->new_optgroup(L("Vertical shells")); + optgroup->append_single_option_line("perimeters", category_path + "perimeters"); + optgroup->append_single_option_line("spiral_vase", category_path + "spiral-vase"); Line line { "", "" }; line.full_width = 1; + line.label_path = category_path + "recommended-thin-wall-thickness"; line.widget = [this](wxWindow* parent) { return description_line_widget(parent, &m_recommended_thin_wall_thickness_description_line); }; optgroup->append_line(line); - optgroup = page->new_optgroup(_(L("Horizontal shells"))); - line = { _(L("Solid layers")), "" }; + optgroup = page->new_optgroup(L("Horizontal shells")); + line = { L("Solid layers"), "" }; + line.label_path = category_path + "solid-layers-top-bottom"; line.append_option(optgroup->get_option("top_solid_layers")); line.append_option(optgroup->get_option("bottom_solid_layers")); optgroup->append_line(line); - line = { _(L("Minimum shell thickness")), "" }; + line = { L("Minimum shell thickness"), "" }; line.append_option(optgroup->get_option("top_solid_min_thickness")); line.append_option(optgroup->get_option("bottom_solid_min_thickness")); optgroup->append_line(line); @@ -1137,74 +1424,86 @@ void TabPrint::build() }; optgroup->append_line(line); - optgroup = page->new_optgroup(_(L("Quality (slower slicing)"))); - optgroup->append_single_option_line("extra_perimeters"); - optgroup->append_single_option_line("ensure_vertical_shell_thickness"); - optgroup->append_single_option_line("avoid_crossing_perimeters"); - optgroup->append_single_option_line("thin_walls"); - optgroup->append_single_option_line("overhangs"); - - optgroup = page->new_optgroup(_(L("Advanced"))); - optgroup->append_single_option_line("seam_position"); - optgroup->append_single_option_line("external_perimeters_first"); - - page = add_options_page(_(L("Infill")), "infill"); - optgroup = page->new_optgroup(_(L("Infill"))); - optgroup->append_single_option_line("fill_density"); - optgroup->append_single_option_line("fill_pattern"); - optgroup->append_single_option_line("top_fill_pattern"); - optgroup->append_single_option_line("bottom_fill_pattern"); - - optgroup = page->new_optgroup(_(L("Reducing printing time"))); - optgroup->append_single_option_line("infill_every_layers"); - optgroup->append_single_option_line("infill_only_where_needed"); - - optgroup = page->new_optgroup(_(L("Advanced"))); - optgroup->append_single_option_line("solid_infill_every_layers"); - optgroup->append_single_option_line("fill_angle"); - optgroup->append_single_option_line("solid_infill_below_area"); + optgroup = page->new_optgroup(L("Quality (slower slicing)")); + optgroup->append_single_option_line("extra_perimeters", category_path + "extra-perimeters-if-needed"); + optgroup->append_single_option_line("ensure_vertical_shell_thickness", category_path + "ensure-vertical-shell-thickness"); + optgroup->append_single_option_line("avoid_crossing_perimeters", category_path + "avoid-crossing-perimeters"); + optgroup->append_single_option_line("avoid_crossing_perimeters_max_detour", category_path + "avoid_crossing_perimeters_max_detour"); + optgroup->append_single_option_line("thin_walls", category_path + "detect-thin-walls"); + optgroup->append_single_option_line("overhangs", category_path + "detect-bridging-perimeters"); + + optgroup = page->new_optgroup(L("Advanced")); + optgroup->append_single_option_line("seam_position", category_path + "seam-position"); + optgroup->append_single_option_line("external_perimeters_first", category_path + "external-perimeters-first"); + + page = add_options_page(L("Infill"), "infill"); + category_path = "infill_42#"; + optgroup = page->new_optgroup(L("Infill")); + optgroup->append_single_option_line("fill_density", category_path + "fill-density"); + optgroup->append_single_option_line("fill_pattern", category_path + "fill-pattern"); + optgroup->append_single_option_line("infill_anchor", category_path + "fill-pattern"); + optgroup->append_single_option_line("infill_anchor_max", category_path + "fill-pattern"); + optgroup->append_single_option_line("top_fill_pattern", category_path + "top-fill-pattern"); + optgroup->append_single_option_line("bottom_fill_pattern", category_path + "bottom-fill-pattern"); + + optgroup = page->new_optgroup(L("Ironing")); + optgroup->append_single_option_line("ironing"); + optgroup->append_single_option_line("ironing_type"); + optgroup->append_single_option_line("ironing_flowrate"); + optgroup->append_single_option_line("ironing_spacing"); + + optgroup = page->new_optgroup(L("Reducing printing time")); + optgroup->append_single_option_line("infill_every_layers", category_path + "combine-infill-every-x-layers"); + optgroup->append_single_option_line("infill_only_where_needed", category_path + "only-infill-where-needed"); + + optgroup = page->new_optgroup(L("Advanced")); + optgroup->append_single_option_line("solid_infill_every_layers", category_path + "solid-infill-every-x-layers"); + optgroup->append_single_option_line("fill_angle", category_path + "fill-angle"); + optgroup->append_single_option_line("solid_infill_below_area", category_path + "solid-infill-threshold-area"); optgroup->append_single_option_line("bridge_angle"); optgroup->append_single_option_line("only_retract_when_crossing_perimeters"); optgroup->append_single_option_line("infill_first"); - page = add_options_page(_(L("Skirt and brim")), "skirt+brim"); - optgroup = page->new_optgroup(_(L("Skirt"))); - optgroup->append_single_option_line("skirts"); - optgroup->append_single_option_line("skirt_distance"); - optgroup->append_single_option_line("skirt_height"); - optgroup->append_single_option_line("draft_shield"); - optgroup->append_single_option_line("min_skirt_length"); - - optgroup = page->new_optgroup(_(L("Brim"))); - optgroup->append_single_option_line("brim_width"); - - page = add_options_page(_(L("Support material")), "support"); - optgroup = page->new_optgroup(_(L("Support material"))); - optgroup->append_single_option_line("support_material"); - optgroup->append_single_option_line("support_material_auto"); - optgroup->append_single_option_line("support_material_threshold"); - optgroup->append_single_option_line("support_material_enforce_layers"); - - optgroup = page->new_optgroup(_(L("Raft"))); - optgroup->append_single_option_line("raft_layers"); + page = add_options_page(L("Skirt and brim"), "skirt+brim"); + category_path = "skirt-and-brim_133969#"; + optgroup = page->new_optgroup(L("Skirt")); + optgroup->append_single_option_line("skirts", category_path + "skirt"); + optgroup->append_single_option_line("skirt_distance", category_path + "skirt"); + optgroup->append_single_option_line("skirt_height", category_path + "skirt"); + optgroup->append_single_option_line("draft_shield", category_path + "skirt"); + optgroup->append_single_option_line("min_skirt_length", category_path + "skirt"); + + optgroup = page->new_optgroup(L("Brim")); + optgroup->append_single_option_line("brim_width", category_path + "brim"); + + page = add_options_page(L("Support material"), "support"); + category_path = "support-material_1698#"; + optgroup = page->new_optgroup(L("Support material")); + optgroup->append_single_option_line("support_material", category_path + "generate-support-material"); + optgroup->append_single_option_line("support_material_auto", category_path + "auto-generated-supports"); + optgroup->append_single_option_line("support_material_threshold", category_path + "overhang-threshold"); + optgroup->append_single_option_line("support_material_enforce_layers", category_path + "enforce-support-for-the-first"); + + optgroup = page->new_optgroup(L("Raft")); + optgroup->append_single_option_line("raft_layers", category_path + "raft-layers"); // # optgroup->append_single_option_line(get_option_("raft_contact_distance"); - optgroup = page->new_optgroup(_(L("Options for support material and raft"))); - optgroup->append_single_option_line("support_material_contact_distance"); - optgroup->append_single_option_line("support_material_pattern"); - optgroup->append_single_option_line("support_material_with_sheath"); - optgroup->append_single_option_line("support_material_spacing"); - optgroup->append_single_option_line("support_material_angle"); - optgroup->append_single_option_line("support_material_interface_layers"); - optgroup->append_single_option_line("support_material_interface_spacing"); - optgroup->append_single_option_line("support_material_interface_contact_loops"); - optgroup->append_single_option_line("support_material_buildplate_only"); - optgroup->append_single_option_line("support_material_xy_spacing"); - optgroup->append_single_option_line("dont_support_bridges"); - optgroup->append_single_option_line("support_material_synchronize_layers"); - - page = add_options_page(_(L("Speed")), "time"); - optgroup = page->new_optgroup(_(L("Speed for print moves"))); + optgroup = page->new_optgroup(L("Options for support material and raft")); + optgroup->append_single_option_line("support_material_contact_distance", category_path + "contact-z-distance"); + optgroup->append_single_option_line("support_material_pattern", category_path + "pattern"); + optgroup->append_single_option_line("support_material_with_sheath", category_path + "with-sheath-around-the-support"); + optgroup->append_single_option_line("support_material_spacing", category_path + "pattern-spacing-0-inf"); + optgroup->append_single_option_line("support_material_angle", category_path + "pattern-angle"); + optgroup->append_single_option_line("support_material_interface_layers", category_path + "interface-layers"); + optgroup->append_single_option_line("support_material_interface_spacing", category_path + "interface-pattern-spacing"); + optgroup->append_single_option_line("support_material_interface_contact_loops", category_path + "interface-loops"); + optgroup->append_single_option_line("support_material_buildplate_only", category_path + "support-on-build-plate-only"); + optgroup->append_single_option_line("support_material_xy_spacing", category_path + "xy-separation-between-an-object-and-its-support"); + optgroup->append_single_option_line("dont_support_bridges", category_path + "dont-support-bridges"); + optgroup->append_single_option_line("support_material_synchronize_layers", category_path + "synchronize-with-object-layers"); + + page = add_options_page(L("Speed"), "time"); + optgroup = page->new_optgroup(L("Speed for print moves")); optgroup->append_single_option_line("perimeter_speed"); optgroup->append_single_option_line("small_perimeter_speed"); optgroup->append_single_option_line("external_perimeter_speed"); @@ -1215,41 +1514,42 @@ void TabPrint::build() optgroup->append_single_option_line("support_material_interface_speed"); optgroup->append_single_option_line("bridge_speed"); optgroup->append_single_option_line("gap_fill_speed"); + optgroup->append_single_option_line("ironing_speed"); - optgroup = page->new_optgroup(_(L("Speed for non-print moves"))); + optgroup = page->new_optgroup(L("Speed for non-print moves")); optgroup->append_single_option_line("travel_speed"); - optgroup = page->new_optgroup(_(L("Modifiers"))); + optgroup = page->new_optgroup(L("Modifiers")); optgroup->append_single_option_line("first_layer_speed"); - optgroup = page->new_optgroup(_(L("Acceleration control (advanced)"))); + optgroup = page->new_optgroup(L("Acceleration control (advanced)")); optgroup->append_single_option_line("perimeter_acceleration"); optgroup->append_single_option_line("infill_acceleration"); optgroup->append_single_option_line("bridge_acceleration"); optgroup->append_single_option_line("first_layer_acceleration"); optgroup->append_single_option_line("default_acceleration"); - optgroup = page->new_optgroup(_(L("Autospeed (advanced)"))); - optgroup->append_single_option_line("max_print_speed"); - optgroup->append_single_option_line("max_volumetric_speed"); + optgroup = page->new_optgroup(L("Autospeed (advanced)")); + optgroup->append_single_option_line("max_print_speed", "max-volumetric-speed_127176"); + optgroup->append_single_option_line("max_volumetric_speed", "max-volumetric-speed_127176"); #ifdef HAS_PRESSURE_EQUALIZER optgroup->append_single_option_line("max_volumetric_extrusion_rate_slope_positive"); optgroup->append_single_option_line("max_volumetric_extrusion_rate_slope_negative"); #endif /* HAS_PRESSURE_EQUALIZER */ - page = add_options_page(_(L("Multiple Extruders")), "funnel"); - optgroup = page->new_optgroup(_(L("Extruders"))); + page = add_options_page(L("Multiple Extruders"), "funnel"); + optgroup = page->new_optgroup(L("Extruders")); optgroup->append_single_option_line("perimeter_extruder"); optgroup->append_single_option_line("infill_extruder"); optgroup->append_single_option_line("solid_infill_extruder"); optgroup->append_single_option_line("support_material_extruder"); optgroup->append_single_option_line("support_material_interface_extruder"); - optgroup = page->new_optgroup(_(L("Ooze prevention"))); + optgroup = page->new_optgroup(L("Ooze prevention")); optgroup->append_single_option_line("ooze_prevention"); optgroup->append_single_option_line("standby_temperature_delta"); - optgroup = page->new_optgroup(_(L("Wipe tower"))); + optgroup = page->new_optgroup(L("Wipe tower")); optgroup->append_single_option_line("wipe_tower"); optgroup->append_single_option_line("wipe_tower_x"); optgroup->append_single_option_line("wipe_tower_y"); @@ -1259,11 +1559,11 @@ void TabPrint::build() optgroup->append_single_option_line("wipe_tower_no_sparse_layers"); optgroup->append_single_option_line("single_extruder_multi_material_priming"); - optgroup = page->new_optgroup(_(L("Advanced"))); + optgroup = page->new_optgroup(L("Advanced")); optgroup->append_single_option_line("interface_shells"); - page = add_options_page(_(L("Advanced")), "wrench"); - optgroup = page->new_optgroup(_(L("Extrusion width"))); + page = add_options_page(L("Advanced"), "wrench"); + optgroup = page->new_optgroup(L("Extrusion width")); optgroup->append_single_option_line("extrusion_width"); optgroup->append_single_option_line("first_layer_extrusion_width"); optgroup->append_single_option_line("perimeter_extrusion_width"); @@ -1273,53 +1573,53 @@ void TabPrint::build() optgroup->append_single_option_line("top_infill_extrusion_width"); optgroup->append_single_option_line("support_material_extrusion_width"); - optgroup = page->new_optgroup(_(L("Overlap"))); + optgroup = page->new_optgroup(L("Overlap")); optgroup->append_single_option_line("infill_overlap"); - optgroup = page->new_optgroup(_(L("Flow"))); + optgroup = page->new_optgroup(L("Flow")); optgroup->append_single_option_line("bridge_flow_ratio"); - optgroup = page->new_optgroup(_(L("Slicing"))); + optgroup = page->new_optgroup(L("Slicing")); optgroup->append_single_option_line("slice_closing_radius"); optgroup->append_single_option_line("resolution"); optgroup->append_single_option_line("xy_size_compensation"); - optgroup->append_single_option_line("elefant_foot_compensation"); + optgroup->append_single_option_line("elefant_foot_compensation", "elephant-foot-compensation_114487"); - optgroup = page->new_optgroup(_(L("Other"))); + optgroup = page->new_optgroup(L("Other")); optgroup->append_single_option_line("clip_multipart_objects"); - page = add_options_page(_(L("Output options")), "output+page_white"); - optgroup = page->new_optgroup(_(L("Sequential printing"))); - optgroup->append_single_option_line("complete_objects"); - line = { _(L("Extruder clearance (mm)")), "" }; + page = add_options_page(L("Output options"), "output+page_white"); + optgroup = page->new_optgroup(L("Sequential printing")); + optgroup->append_single_option_line("complete_objects", "sequential-printing_124589"); + line = { L("Extruder clearance"), "" }; line.append_option(optgroup->get_option("extruder_clearance_radius")); line.append_option(optgroup->get_option("extruder_clearance_height")); optgroup->append_line(line); - optgroup = page->new_optgroup(_(L("Output file"))); + optgroup = page->new_optgroup(L("Output file")); optgroup->append_single_option_line("gcode_comments"); optgroup->append_single_option_line("gcode_label_objects"); Option option = optgroup->get_option("output_filename_format"); option.opt.full_width = true; optgroup->append_single_option_line(option); - optgroup = page->new_optgroup(_(L("Post-processing scripts")), 0); + optgroup = page->new_optgroup(L("Post-processing scripts"), 0); option = optgroup->get_option("post_process"); option.opt.full_width = true; option.opt.height = 5;//50; optgroup->append_single_option_line(option); - page = add_options_page(_(L("Notes")), "note.png"); - optgroup = page->new_optgroup(_(L("Notes")), 0); + page = add_options_page(L("Notes"), "note.png"); + optgroup = page->new_optgroup(L("Notes"), 0); option = optgroup->get_option("notes"); option.opt.full_width = true; option.opt.height = 25;//250; optgroup->append_single_option_line(option); - page = add_options_page(_(L("Dependencies")), "wrench.png"); - optgroup = page->new_optgroup(_(L("Profile dependencies"))); + page = add_options_page(L("Dependencies"), "wrench.png"); + optgroup = page->new_optgroup(L("Profile dependencies")); - create_line_with_widget(optgroup.get(), "compatible_printers", [this](wxWindow* parent) { + create_line_with_widget(optgroup.get(), "compatible_printers", wxEmptyString, [this](wxWindow* parent) { return compatible_widget_create(parent, m_compatible_printers); }); @@ -1337,27 +1637,46 @@ void TabPrint::reload_config() Tab::reload_config(); } +void TabPrint::update_description_lines() +{ + Tab::update_description_lines(); + + if (m_preset_bundle->printers.get_selected_preset().printer_technology() == ptSLA) + return; + + if (m_active_page && m_active_page->title() == "Layers and perimeters" && + m_recommended_thin_wall_thickness_description_line && m_top_bottom_shell_thickness_explanation) + { + m_recommended_thin_wall_thickness_description_line->SetText( + from_u8(PresetHints::recommended_thin_wall_thickness(*m_preset_bundle))); + m_top_bottom_shell_thickness_explanation->SetText( + from_u8(PresetHints::top_bottom_shell_thickness_explanation(*m_preset_bundle))); + } +} + +void TabPrint::toggle_options() +{ + if (!m_active_page) return; + + m_config_manipulation.toggle_print_fff_options(m_config); +} + void TabPrint::update() { if (m_preset_bundle->printers.get_selected_preset().printer_technology() == ptSLA) return; // ys_FIXME m_update_cnt++; -// Freeze(); m_config_manipulation.update_print_fff_config(m_config, true); - m_recommended_thin_wall_thickness_description_line->SetText( - from_u8(PresetHints::recommended_thin_wall_thickness(*m_preset_bundle))); - m_top_bottom_shell_thickness_explanation->SetText( - from_u8(PresetHints::top_bottom_shell_thickness_explanation(*m_preset_bundle))); + update_description_lines(); Layout(); -// Thaw(); m_update_cnt--; if (m_update_cnt==0) { - m_config_manipulation.toggle_print_fff_options(m_config); + toggle_options(); // update() could be called during undo/redo execution // Update of objectList can cause a crash in this case (because m_objects doesn't match ObjectList) @@ -1368,19 +1687,18 @@ void TabPrint::update() } } -void TabPrint::OnActivate() +void TabPrint::clear_pages() { - m_recommended_thin_wall_thickness_description_line->SetText( - from_u8(PresetHints::recommended_thin_wall_thickness(*m_preset_bundle))); - m_top_bottom_shell_thickness_explanation->SetText( - from_u8(PresetHints::top_bottom_shell_thickness_explanation(*m_preset_bundle))); - Tab::OnActivate(); + Tab::clear_pages(); + + m_recommended_thin_wall_thickness_description_line = nullptr; + m_top_bottom_shell_thickness_explanation = nullptr; } void TabFilament::add_filament_overrides_page() { - PageShp page = add_options_page(_(L("Filament Overrides")), "wrench"); - ConfigOptionsGroupShp optgroup = page->new_optgroup(_(L("Retraction"))); + PageShp page = add_options_page(L("Filament Overrides"), "wrench"); + ConfigOptionsGroupShp optgroup = page->new_optgroup(L("Retraction")); auto append_single_option_line = [optgroup, this](const std::string& opt_key, int opt_index) { @@ -1434,12 +1752,11 @@ void TabFilament::add_filament_overrides_page() void TabFilament::update_filament_overrides_page() { - const auto page_it = std::find_if(m_pages.begin(), m_pages.end(), [](const PageShp page) {return page->title() == _(L("Filament Overrides")); }); - if (page_it == m_pages.end()) + if (!m_active_page || m_active_page->title() != "Filament Overrides") return; - PageShp page = *page_it; + Page* page = m_active_page; - const auto og_it = std::find_if(page->m_optgroups.begin(), page->m_optgroups.end(), [](const ConfigOptionsGroupShp og) {return og->title == _(L("Retraction")); }); + const auto og_it = std::find_if(page->m_optgroups.begin(), page->m_optgroups.end(), [](const ConfigOptionsGroupShp og) { return og->title == "Retraction"; }); if (og_it == page->m_optgroups.end()) return; ConfigOptionsGroupShp optgroup = *og_it; @@ -1481,27 +1798,41 @@ void TabFilament::build() m_presets = &m_preset_bundle->filaments; load_initial_data(); - auto page = add_options_page(_(L("Filament")), "spool.png"); - auto optgroup = page->new_optgroup(_(L("Filament"))); + auto page = add_options_page(L("Filament"), "spool.png"); + auto optgroup = page->new_optgroup(L("Filament")); optgroup->append_single_option_line("filament_colour"); optgroup->append_single_option_line("filament_diameter"); optgroup->append_single_option_line("extrusion_multiplier"); optgroup->append_single_option_line("filament_density"); optgroup->append_single_option_line("filament_cost"); + optgroup->append_single_option_line("filament_spool_weight"); - optgroup = page->new_optgroup(_(L("Temperature")) + wxString(" °C", wxConvUTF8)); - Line line = { _(L("Extruder")), "" }; + optgroup->m_on_change = [this, optgroup](t_config_option_key opt_key, boost::any value) + { + update_dirty(); + if (opt_key == "filament_spool_weight") { + // Change of this option influences for an update of "Sliced Info" + wxGetApp().sidebar().update_sliced_info_sizer(); + wxGetApp().sidebar().Layout(); + } + else + on_value_change(opt_key, value); + }; + + optgroup = page->new_optgroup(L("Temperature")); + Line line = { L("Nozzle"), "" }; line.append_option(optgroup->get_option("first_layer_temperature")); line.append_option(optgroup->get_option("temperature")); optgroup->append_line(line); - line = { _(L("Bed")), "" }; + line = { L("Bed"), "" }; line.append_option(optgroup->get_option("first_layer_bed_temperature")); line.append_option(optgroup->get_option("bed_temperature")); optgroup->append_line(line); - page = add_options_page(_(L("Cooling")), "cooling"); - optgroup = page->new_optgroup(_(L("Enable"))); + page = add_options_page(L("Cooling"), "cooling"); + wxString category_path = "cooling_127569#"; + optgroup = page->new_optgroup(L("Enable")); optgroup->append_single_option_line("fan_always_on"); optgroup->append_single_option_line("cooling"); @@ -1512,30 +1843,32 @@ void TabFilament::build() }; optgroup->append_line(line); - optgroup = page->new_optgroup(_(L("Fan settings"))); - line = { _(L("Fan speed")), "" }; + optgroup = page->new_optgroup(L("Fan settings")); + line = { L("Fan speed"), "" }; + line.label_path = category_path + "fan-settings"; line.append_option(optgroup->get_option("min_fan_speed")); line.append_option(optgroup->get_option("max_fan_speed")); optgroup->append_line(line); - optgroup->append_single_option_line("bridge_fan_speed"); - optgroup->append_single_option_line("disable_fan_first_layers"); + optgroup->append_single_option_line("bridge_fan_speed", category_path + "fan-settings"); + optgroup->append_single_option_line("disable_fan_first_layers", category_path + "fan-settings"); + optgroup->append_single_option_line("full_fan_speed_layer", category_path + "fan-settings"); - optgroup = page->new_optgroup(_(L("Cooling thresholds")), 25); - optgroup->append_single_option_line("fan_below_layer_time"); - optgroup->append_single_option_line("slowdown_below_layer_time"); - optgroup->append_single_option_line("min_print_speed"); + optgroup = page->new_optgroup(L("Cooling thresholds"), 25); + optgroup->append_single_option_line("fan_below_layer_time", category_path + "cooling-thresholds"); + optgroup->append_single_option_line("slowdown_below_layer_time", category_path + "cooling-thresholds"); + optgroup->append_single_option_line("min_print_speed", category_path + "cooling-thresholds"); - page = add_options_page(_(L("Advanced")), "wrench"); - optgroup = page->new_optgroup(_(L("Filament properties"))); + page = add_options_page(L("Advanced"), "wrench"); + optgroup = page->new_optgroup(L("Filament properties")); // Set size as all another fields for a better alignment Option option = optgroup->get_option("filament_type"); option.opt.width = Field::def_width(); optgroup->append_single_option_line(option); optgroup->append_single_option_line("filament_soluble"); - optgroup = page->new_optgroup(_(L("Print speed override"))); - optgroup->append_single_option_line("filament_max_volumetric_speed"); + optgroup = page->new_optgroup(L("Print speed override")); + optgroup->append_single_option_line("filament_max_volumetric_speed", "max-volumetric-speed_127176"); line = { "", "" }; line.full_width = 1; @@ -1544,10 +1877,10 @@ void TabFilament::build() }; optgroup->append_line(line); - optgroup = page->new_optgroup(_(L("Wipe tower parameters"))); + optgroup = page->new_optgroup(L("Wipe tower parameters")); optgroup->append_single_option_line("filament_minimal_purge_on_wipe_tower"); - optgroup = page->new_optgroup(_(L("Toolchange parameters with single extruder MM printers"))); + optgroup = page->new_optgroup(L("Toolchange parameters with single extruder MM printers")); optgroup->append_single_option_line("filament_loading_speed_start"); optgroup->append_single_option_line("filament_loading_speed"); optgroup->append_single_option_line("filament_unloading_speed_start"); @@ -1559,22 +1892,22 @@ void TabFilament::build() optgroup->append_single_option_line("filament_cooling_initial_speed"); optgroup->append_single_option_line("filament_cooling_final_speed"); - line = optgroup->create_single_option_line("filament_ramming_parameters");// { _(L("Ramming")), "" }; - line.widget = [this](wxWindow* parent) { + create_line_with_widget(optgroup.get(), "filament_ramming_parameters", wxEmptyString, [this](wxWindow* parent) { auto ramming_dialog_btn = new wxButton(parent, wxID_ANY, _(L("Ramming settings"))+dots, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); ramming_dialog_btn->SetFont(Slic3r::GUI::wxGetApp().normal_font()); + ramming_dialog_btn->SetSize(ramming_dialog_btn->GetBestSize()); auto sizer = new wxBoxSizer(wxHORIZONTAL); sizer->Add(ramming_dialog_btn); - ramming_dialog_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e) - { + ramming_dialog_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent& e) { RammingDialog dlg(this,(m_config->option<ConfigOptionStrings>("filament_ramming_parameters"))->get_at(0)); - if (dlg.ShowModal() == wxID_OK) - (m_config->option<ConfigOptionStrings>("filament_ramming_parameters"))->get_at(0) = dlg.get_parameters(); - })); + if (dlg.ShowModal() == wxID_OK) { + load_key_value("filament_ramming_parameters", dlg.get_parameters()); + update_changed_ui(); + } + }); return sizer; - }; - optgroup->append_line(line); + }); add_filament_overrides_page(); @@ -1583,30 +1916,32 @@ void TabFilament::build() const int gcode_field_height = 15; // 150 const int notes_field_height = 25; // 250 - page = add_options_page(_(L("Custom G-code")), "cog"); - optgroup = page->new_optgroup(_(L("Start G-code")), 0); + page = add_options_page(L("Custom G-code"), "cog"); + optgroup = page->new_optgroup(L("Start G-code"), 0); option = optgroup->get_option("start_filament_gcode"); option.opt.full_width = true; + option.opt.is_code = true; option.opt.height = gcode_field_height;// 150; optgroup->append_single_option_line(option); - optgroup = page->new_optgroup(_(L("End G-code")), 0); + optgroup = page->new_optgroup(L("End G-code"), 0); option = optgroup->get_option("end_filament_gcode"); option.opt.full_width = true; + option.opt.is_code = true; option.opt.height = gcode_field_height;// 150; optgroup->append_single_option_line(option); - page = add_options_page(_(L("Notes")), "note.png"); - optgroup = page->new_optgroup(_(L("Notes")), 0); + page = add_options_page(L("Notes"), "note.png"); + optgroup = page->new_optgroup(L("Notes"), 0); optgroup->label_width = 0; option = optgroup->get_option("filament_notes"); option.opt.full_width = true; option.opt.height = notes_field_height;// 250; optgroup->append_single_option_line(option); - page = add_options_page(_(L("Dependencies")), "wrench.png"); - optgroup = page->new_optgroup(_(L("Profile dependencies"))); - create_line_with_widget(optgroup.get(), "compatible_printers", [this](wxWindow* parent) { + page = add_options_page(L("Dependencies"), "wrench.png"); + optgroup = page->new_optgroup(L("Profile dependencies")); + create_line_with_widget(optgroup.get(), "compatible_printers", wxEmptyString, [this](wxWindow* parent) { return compatible_widget_create(parent, m_compatible_printers); }); @@ -1614,7 +1949,7 @@ void TabFilament::build() option.opt.full_width = true; optgroup->append_single_option_line(option); - create_line_with_widget(optgroup.get(), "compatible_prints", [this](wxWindow* parent) { + create_line_with_widget(optgroup.get(), "compatible_prints", wxEmptyString, [this](wxWindow* parent) { return compatible_widget_create(parent, m_compatible_prints); }); @@ -1644,6 +1979,40 @@ void TabFilament::update_volumetric_flow_preset_hints() m_volumetric_speed_description_line->SetText(text); } +void TabFilament::update_description_lines() +{ + Tab::update_description_lines(); + + if (!m_active_page) + return; + + if (m_active_page->title() == "Cooling" && m_cooling_description_line) + m_cooling_description_line->SetText(from_u8(PresetHints::cooling_description(m_presets->get_edited_preset()))); + if (m_active_page->title() == "Advanced" && m_volumetric_speed_description_line) + this->update_volumetric_flow_preset_hints(); +} + +void TabFilament::toggle_options() +{ + if (!m_active_page) + return; + + if (m_active_page->title() == "Cooling") + { + bool cooling = m_config->opt_bool("cooling", 0); + bool fan_always_on = cooling || m_config->opt_bool("fan_always_on", 0); + + for (auto el : { "max_fan_speed", "fan_below_layer_time", "slowdown_below_layer_time", "min_print_speed" }) + toggle_option(el, cooling); + + for (auto el : { "min_fan_speed", "disable_fan_first_layers", "full_fan_speed_layer" }) + toggle_option(el, fan_always_on); + } + + if (m_active_page->title() == "Filament Overrides") + update_filament_overrides_page(); +} + void TabFilament::update() { if (m_preset_bundle->printers.get_selected_preset().printer_technology() == ptSLA) @@ -1651,21 +2020,10 @@ void TabFilament::update() m_update_cnt++; - wxString text = from_u8(PresetHints::cooling_description(m_presets->get_edited_preset())); - m_cooling_description_line->SetText(text); - this->update_volumetric_flow_preset_hints(); + update_description_lines(); Layout(); - bool cooling = m_config->opt_bool("cooling", 0); - bool fan_always_on = cooling || m_config->opt_bool("fan_always_on", 0); - - for (auto el : { "max_fan_speed", "fan_below_layer_time", "slowdown_below_layer_time", "min_print_speed" }) - get_field(el)->toggle(cooling); - - for (auto el : { "min_fan_speed", "disable_fan_first_layers" }) - get_field(el)->toggle(fan_always_on); - - update_filament_overrides_page(); + toggle_options(); m_update_cnt--; @@ -1673,15 +2031,17 @@ void TabFilament::update() wxGetApp().mainframe->on_config_changed(m_config); } -void TabFilament::OnActivate() +void TabFilament::clear_pages() { - this->update_volumetric_flow_preset_hints(); - Tab::OnActivate(); + Tab::clear_pages(); + + m_volumetric_speed_description_line = nullptr; + m_cooling_description_line = nullptr; } -wxSizer* Tab::description_line_widget(wxWindow* parent, ogStaticText* *StaticText) +wxSizer* Tab::description_line_widget(wxWindow* parent, ogStaticText* *StaticText, wxString text /*= wxEmptyString*/) { - *StaticText = new ogStaticText(parent, ""); + *StaticText = new ogStaticText(parent, text); // auto font = (new wxSystemSettings)->GetFont(wxSYS_DEFAULT_GUI_FONT); (*StaticText)->SetFont(wxGetApp().normal_font()); @@ -1696,132 +2056,6 @@ bool Tab::current_preset_is_dirty() return m_presets->current_is_dirty(); } -void TabPrinter::build_printhost(ConfigOptionsGroup *optgroup) -{ - const PrinterTechnology tech = m_presets->get_selected_preset().printer_technology(); - - // Only offer the host type selection for FFF, for SLA it's always the SL1 printer (at the moment) - if (tech == ptFFF) { - optgroup->append_single_option_line("host_type"); - } - - auto printhost_browse = [=](wxWindow* parent) { - add_scaled_button(parent, &m_printhost_browse_btn, "browse", _(L("Browse")) + " "+ dots, wxBU_LEFT | wxBU_EXACTFIT); - ScalableButton* btn = m_printhost_browse_btn; - btn->SetFont(Slic3r::GUI::wxGetApp().normal_font()); - - auto sizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(btn); - - btn->Bind(wxEVT_BUTTON, [=](wxCommandEvent &e) { - BonjourDialog dialog(parent, tech); - if (dialog.show_and_lookup()) { - optgroup->set_value("print_host", std::move(dialog.get_selected()), true); - optgroup->get_field("print_host")->field_changed(); - } - }); - - return sizer; - }; - - auto print_host_test = [this](wxWindow* parent) { - add_scaled_button(parent, &m_print_host_test_btn, "test", _(L("Test")), wxBU_LEFT | wxBU_EXACTFIT); - ScalableButton* btn = m_print_host_test_btn; - btn->SetFont(Slic3r::GUI::wxGetApp().normal_font()); - auto sizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(btn); - - btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent &e) { - std::unique_ptr<PrintHost> host(PrintHost::get_print_host(m_config)); - if (! host) { - const wxString text = _(L("Could not get a valid Printer Host reference")); - show_error(this, text); - return; - } - wxString msg; - if (host->test(msg)) { - show_info(this, host->get_test_ok_msg(), _(L("Success!"))); - } else { - show_error(this, host->get_test_failed_msg(msg)); - } - }); - - return sizer; - }; - - // Set a wider width for a better alignment - Option option = optgroup->get_option("print_host"); - option.opt.width = Field::def_width_wider(); - Line host_line = optgroup->create_single_option_line(option); - host_line.append_widget(printhost_browse); - host_line.append_widget(print_host_test); - optgroup->append_line(host_line); - option = optgroup->get_option("printhost_apikey"); - option.opt.width = Field::def_width_wider(); - optgroup->append_single_option_line(option); - - const auto ca_file_hint = _utf8(L("HTTPS CA file is optional. It is only needed if you use HTTPS with a self-signed certificate.")); - - if (Http::ca_file_supported()) { - option = optgroup->get_option("printhost_cafile"); - option.opt.width = Field::def_width_wider(); - Line cafile_line = optgroup->create_single_option_line(option); - - auto printhost_cafile_browse = [this, optgroup] (wxWindow* parent) { - auto btn = new wxButton(parent, wxID_ANY, " " + _(L("Browse"))+" " +dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT); - btn->SetFont(Slic3r::GUI::wxGetApp().normal_font()); - btn->SetBitmap(create_scaled_bitmap("browse")); - auto sizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(btn); - - btn->Bind(wxEVT_BUTTON, [this, optgroup] (wxCommandEvent e) { - static const auto filemasks = _(L("Certificate files (*.crt, *.pem)|*.crt;*.pem|All files|*.*")); - wxFileDialog openFileDialog(this, _(L("Open CA certificate file")), "", "", filemasks, wxFD_OPEN | wxFD_FILE_MUST_EXIST); - if (openFileDialog.ShowModal() != wxID_CANCEL) { - optgroup->set_value("printhost_cafile", std::move(openFileDialog.GetPath()), true); - optgroup->get_field("printhost_cafile")->field_changed(); - } - }); - - return sizer; - }; - - cafile_line.append_widget(printhost_cafile_browse); - optgroup->append_line(cafile_line); - - Line cafile_hint { "", "" }; - cafile_hint.full_width = 1; - cafile_hint.widget = [this, ca_file_hint](wxWindow* parent) { - auto txt = new wxStaticText(parent, wxID_ANY, ca_file_hint); - auto sizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(txt); - return sizer; - }; - optgroup->append_line(cafile_hint); - } else { - Line line { "", "" }; - line.full_width = 1; - - line.widget = [ca_file_hint] (wxWindow* parent) { - std::string info = _utf8(L("HTTPS CA File")) + ":\n\t" + - (boost::format(_utf8(L("On this system, %s uses HTTPS certificates from the system Certificate Store or Keychain."))) % SLIC3R_APP_NAME).str() + - "\n\t" + _utf8(L("To use a custom CA file, please import your CA file into Certificate Store / Keychain.")); - - auto txt = new wxStaticText(parent, wxID_ANY, from_u8((boost::format("%1%\n\n\t%2%") % info % ca_file_hint).str())); -/* % (boost::format(_utf8(L("HTTPS CA File:\n\ - \tOn this system, %s uses HTTPS certificates from the system Certificate Store or Keychain.\n\ - \tTo use a custom CA file, please import your CA file into Certificate Store / Keychain."))) % SLIC3R_APP_NAME).str() - % std::string(ca_file_hint.ToUTF8())).str())); -*/ txt->SetFont(Slic3r::GUI::wxGetApp().normal_font()); - auto sizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(txt, 1, wxEXPAND); - return sizer; - }; - - optgroup->append_line(line); - } -} - void TabPrinter::build() { m_presets = &m_preset_bundle->printers; @@ -1832,6 +2066,27 @@ void TabPrinter::build() m_presets->get_selected_preset().printer_technology() == ptSLA ? build_sla() : build_fff(); } +void TabPrinter::build_print_host_upload_group(Page* page) +{ + ConfigOptionsGroupShp optgroup = page->new_optgroup(L("Print Host upload")); + + wxString description_line_text = _L("" + "Note: All parameters from this group are moved to the Physical Printer settings (see changelog).\n\n" + "A new Physical Printer profile is created by clicking on the \"cog\" icon right of the Printer profiles combo box, " + "by selecting the \"Add physical printer\" item in the Printer combo box. The Physical Printer profile editor opens " + "also when clicking on the \"cog\" icon in the Printer settings tab. The Physical Printer profiles are being stored " + "into PrusaSlicer/physical_printer directory."); + + Line line = { "", "" }; + line.full_width = 1; + line.widget = [this, description_line_text](wxWindow* parent) { + return description_line_widget(parent, m_presets->get_selected_preset().printer_technology() == ptFFF ? + &m_fff_print_host_upload_description_line : &m_sla_print_host_upload_description_line, + description_line_text); + }; + optgroup->append_line(line); +} + void TabPrinter::build_fff() { if (!m_pages.empty()) @@ -1847,17 +2102,17 @@ void TabPrinter::build_fff() m_sys_extruders_count = parent_preset == nullptr ? 0 : static_cast<const ConfigOptionFloats*>(parent_preset->config.option("nozzle_diameter"))->values.size(); - auto page = add_options_page(_(L("General")), "printer"); - auto optgroup = page->new_optgroup(_(L("Size and coordinates"))); + auto page = add_options_page(L("General"), "printer"); + auto optgroup = page->new_optgroup(L("Size and coordinates")); - create_line_with_widget(optgroup.get(), "bed_shape", [this](wxWindow* parent) { + create_line_with_widget(optgroup.get(), "bed_shape", "custom-svg-and-png-bed-textures_124612", [this](wxWindow* parent) { return create_bed_shape_widget(parent); }); optgroup->append_single_option_line("max_print_height"); optgroup->append_single_option_line("z_offset"); - optgroup = page->new_optgroup(_(L("Capabilities"))); + optgroup = page->new_optgroup(L("Capabilities")); ConfigOptionDef def; def.type = coInt, def.set_default_value(new ConfigOptionInt(1)); @@ -1919,59 +2174,15 @@ void TabPrinter::build_fff() }); }; + build_print_host_upload_group(page.get()); -#if 0 - if (!m_no_controller) - { - optgroup = page->new_optgroup(_(L("USB/Serial connection"))); - line = {_(L("Serial port")), ""}; - Option serial_port = optgroup->get_option("serial_port"); - serial_port.side_widget = ([this](wxWindow* parent) { - auto btn = new wxBitmapButton(parent, wxID_ANY, wxBitmap(from_u8(Slic3r::var("arrow_rotate_clockwise.png")), wxBITMAP_TYPE_PNG), - wxDefaultPosition, wxDefaultSize, wxBORDER_NONE); - btn->SetToolTip(_(L("Rescan serial ports"))); - auto sizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(btn); - - btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent e) {update_serial_ports(); }); - return sizer; - }); - auto serial_test = [this](wxWindow* parent) { - auto btn = m_serial_test_btn = new wxButton(parent, wxID_ANY, - _(L("Test")), wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT); - btn->SetFont(Slic3r::GUI::small_font()); - btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("wrench.png")), wxBITMAP_TYPE_PNG)); - auto sizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(btn); - - btn->Bind(wxEVT_BUTTON, [this, parent](wxCommandEvent e) { - auto sender = Slic3r::make_unique<GCodeSender>(); - auto res = sender->connect( - m_config->opt_string("serial_port"), - m_config->opt_int("serial_speed") - ); - if (res && sender->wait_connected()) { - show_info(parent, _(L("Connection to printer works correctly.")), _(L("Success!"))); - } - else { - show_error(parent, _(L("Connection failed."))); - } - }); - return sizer; - }; - - line.append_option(serial_port); - line.append_option(optgroup->get_option("serial_speed")); - line.append_widget(serial_test); - optgroup->append_line(line); - } -#endif + optgroup = page->new_optgroup(L("Firmware")); + optgroup->append_single_option_line("gcode_flavor"); - optgroup = page->new_optgroup(_(L("Print Host upload"))); - build_printhost(optgroup.get()); + option = optgroup->get_option("thumbnails"); + option.opt.full_width = true; + optgroup->append_single_option_line(option); - optgroup = page->new_optgroup(_(L("Firmware"))); - optgroup->append_single_option_line("gcode_flavor"); optgroup->append_single_option_line("silent_mode"); optgroup->append_single_option_line("remaining_times"); @@ -1990,7 +2201,7 @@ void TabPrinter::build_fff() }); }; - optgroup = page->new_optgroup(_(L("Advanced"))); + optgroup = page->new_optgroup(L("Advanced")); optgroup->append_single_option_line("use_relative_e_distances"); optgroup->append_single_option_line("use_firmware_retraction"); optgroup->append_single_option_line("use_volumetric_e"); @@ -1998,81 +2209,100 @@ void TabPrinter::build_fff() const int gcode_field_height = 15; // 150 const int notes_field_height = 25; // 250 - page = add_options_page(_(L("Custom G-code")), "cog"); - optgroup = page->new_optgroup(_(L("Start G-code")), 0); + page = add_options_page(L("Custom G-code"), "cog"); + optgroup = page->new_optgroup(L("Start G-code"), 0); option = optgroup->get_option("start_gcode"); option.opt.full_width = true; + option.opt.is_code = true; option.opt.height = gcode_field_height;//150; optgroup->append_single_option_line(option); - optgroup = page->new_optgroup(_(L("End G-code")), 0); + optgroup = page->new_optgroup(L("End G-code"), 0); option = optgroup->get_option("end_gcode"); option.opt.full_width = true; + option.opt.is_code = true; option.opt.height = gcode_field_height;//150; optgroup->append_single_option_line(option); - optgroup = page->new_optgroup(_(L("Before layer change G-code")), 0); + optgroup = page->new_optgroup(L("Before layer change G-code"), 0); option = optgroup->get_option("before_layer_gcode"); option.opt.full_width = true; + option.opt.is_code = true; option.opt.height = gcode_field_height;//150; optgroup->append_single_option_line(option); - optgroup = page->new_optgroup(_(L("After layer change G-code")), 0); + optgroup = page->new_optgroup(L("After layer change G-code"), 0); option = optgroup->get_option("layer_gcode"); option.opt.full_width = true; + option.opt.is_code = true; option.opt.height = gcode_field_height;//150; optgroup->append_single_option_line(option); - optgroup = page->new_optgroup(_(L("Tool change G-code")), 0); + optgroup = page->new_optgroup(L("Tool change G-code"), 0); option = optgroup->get_option("toolchange_gcode"); option.opt.full_width = true; + option.opt.is_code = true; option.opt.height = gcode_field_height;//150; optgroup->append_single_option_line(option); - optgroup = page->new_optgroup(_(L("Between objects G-code (for sequential printing)")), 0); + optgroup = page->new_optgroup(L("Between objects G-code (for sequential printing)"), 0); option = optgroup->get_option("between_objects_gcode"); option.opt.full_width = true; + option.opt.is_code = true; + option.opt.height = gcode_field_height;//150; + optgroup->append_single_option_line(option); + + optgroup = page->new_optgroup(L("Color Change G-code"), 0); + option = optgroup->get_option("color_change_gcode"); + option.opt.is_code = true; + option.opt.height = gcode_field_height;//150; + optgroup->append_single_option_line(option); + + optgroup = page->new_optgroup(L("Pause Print G-code"), 0); + option = optgroup->get_option("pause_print_gcode"); + option.opt.is_code = true; option.opt.height = gcode_field_height;//150; optgroup->append_single_option_line(option); - page = add_options_page(_(L("Notes")), "note.png"); - optgroup = page->new_optgroup(_(L("Notes")), 0); + optgroup = page->new_optgroup(L("Template Custom G-code"), 0); + option = optgroup->get_option("template_custom_gcode"); + option.opt.is_code = true; + option.opt.height = gcode_field_height;//150; + optgroup->append_single_option_line(option); + + page = add_options_page(L("Notes"), "note.png"); + optgroup = page->new_optgroup(L("Notes"), 0); option = optgroup->get_option("printer_notes"); option.opt.full_width = true; option.opt.height = notes_field_height;//250; optgroup->append_single_option_line(option); - page = add_options_page(_(L("Dependencies")), "wrench.png"); - optgroup = page->new_optgroup(_(L("Profile dependencies"))); + page = add_options_page(L("Dependencies"), "wrench.png"); + optgroup = page->new_optgroup(L("Profile dependencies")); build_preset_description_line(optgroup.get()); build_unregular_pages(); - -#if 0 - if (!m_no_controller) - update_serial_ports(); -#endif } void TabPrinter::build_sla() { if (!m_pages.empty()) m_pages.resize(0); - auto page = add_options_page(_(L("General")), "printer"); - auto optgroup = page->new_optgroup(_(L("Size and coordinates"))); + auto page = add_options_page(L("General"), "printer"); + auto optgroup = page->new_optgroup(L("Size and coordinates")); - create_line_with_widget(optgroup.get(), "bed_shape", [this](wxWindow* parent) { + create_line_with_widget(optgroup.get(), "bed_shape", "custom-svg-and-png-bed-textures_124612", [this](wxWindow* parent) { return create_bed_shape_widget(parent); }); optgroup->append_single_option_line("max_print_height"); - optgroup = page->new_optgroup(_(L("Display"))); + optgroup = page->new_optgroup(L("Display")); optgroup->append_single_option_line("display_width"); optgroup->append_single_option_line("display_height"); auto option = optgroup->get_option("display_pixels_x"); - Line line = { _(option.opt.full_label), "" }; + Line line = { option.opt.full_label, "" }; line.append_option(option); line.append_option(optgroup->get_option("display_pixels_y")); optgroup->append_line(line); @@ -2082,15 +2312,15 @@ void TabPrinter::build_sla() optgroup->append_single_option_line("display_mirror_x"); optgroup->append_single_option_line("display_mirror_y"); - optgroup = page->new_optgroup(_(L("Tilt"))); - line = { _(L("Tilt time")), "" }; + optgroup = page->new_optgroup(L("Tilt")); + line = { L("Tilt time"), "" }; line.append_option(optgroup->get_option("fast_tilt_time")); line.append_option(optgroup->get_option("slow_tilt_time")); optgroup->append_line(line); optgroup->append_single_option_line("area_fill"); - optgroup = page->new_optgroup(_(L("Corrections"))); - line = Line{ _(m_config->def()->get("relative_correction")->full_label), "" }; + optgroup = page->new_optgroup(L("Corrections")); + line = Line{ m_config->def()->get("relative_correction")->full_label, "" }; // std::vector<std::string> axes{ "X", "Y", "Z" }; std::vector<std::string> axes{ "XY", "Z" }; int id = 0; @@ -2106,37 +2336,29 @@ void TabPrinter::build_sla() optgroup->append_single_option_line("elefant_foot_min_width"); optgroup->append_single_option_line("gamma_correction"); - optgroup = page->new_optgroup(_(L("Exposure"))); + optgroup = page->new_optgroup(L("Exposure")); optgroup->append_single_option_line("min_exposure_time"); optgroup->append_single_option_line("max_exposure_time"); optgroup->append_single_option_line("min_initial_exposure_time"); optgroup->append_single_option_line("max_initial_exposure_time"); - optgroup = page->new_optgroup(_(L("Print Host upload"))); - build_printhost(optgroup.get()); + build_print_host_upload_group(page.get()); const int notes_field_height = 25; // 250 - page = add_options_page(_(L("Notes")), "note.png"); - optgroup = page->new_optgroup(_(L("Notes")), 0); + page = add_options_page(L("Notes"), "note.png"); + optgroup = page->new_optgroup(L("Notes"), 0); option = optgroup->get_option("printer_notes"); option.opt.full_width = true; option.opt.height = notes_field_height;//250; optgroup->append_single_option_line(option); - page = add_options_page(_(L("Dependencies")), "wrench.png"); - optgroup = page->new_optgroup(_(L("Profile dependencies"))); + page = add_options_page(L("Dependencies"), "wrench.png"); + optgroup = page->new_optgroup(L("Profile dependencies")); build_preset_description_line(optgroup.get()); } -void TabPrinter::update_serial_ports() -{ - Field *field = get_field("serial_port"); - Choice *choice = static_cast<Choice *>(field); - choice->set_values(Utils::scan_serial_ports()); -} - void TabPrinter::extruders_count_changed(size_t extruders_count) { bool is_count_changed = false; @@ -2164,7 +2386,7 @@ void TabPrinter::extruders_count_changed(size_t extruders_count) void TabPrinter::append_option_line(ConfigOptionsGroupShp optgroup, const std::string opt_key) { auto option = optgroup->get_option(opt_key, 0); - auto line = Line{ _(option.opt.full_label), "" }; + auto line = Line{ option.opt.full_label, "" }; line.append_option(option); if (m_use_silent_mode) line.append_option(optgroup->get_option(opt_key, 1)); @@ -2173,18 +2395,27 @@ void TabPrinter::append_option_line(ConfigOptionsGroupShp optgroup, const std::s PageShp TabPrinter::build_kinematics_page() { - auto page = add_options_page(_(L("Machine limits")), "cog", true); + auto page = add_options_page(L("Machine limits"), "cog", true); - if (m_use_silent_mode) { + auto optgroup = page->new_optgroup(L("General")); + { + optgroup->append_single_option_line("machine_limits_usage"); + Line line { "", "" }; + line.full_width = 1; + line.widget = [this](wxWindow* parent) { + return description_line_widget(parent, &m_machine_limits_description_line); + }; + optgroup->append_line(line); + } + + if (m_use_silent_mode) { // Legend for OptionsGroups auto optgroup = page->new_optgroup(""); - optgroup->set_show_modified_btns_val(false); - optgroup->label_width = 23;// 230; auto line = Line{ "", "" }; ConfigOptionDef def; def.type = coString; - def.width = 15; + def.width = Field::def_width(); def.gui_type = "legend"; def.mode = comAdvanced; def.tooltip = L("Values in this column are for Normal mode"); @@ -2202,24 +2433,24 @@ PageShp TabPrinter::build_kinematics_page() } std::vector<std::string> axes{ "x", "y", "z", "e" }; - auto optgroup = page->new_optgroup(_(L("Maximum feedrates"))); + optgroup = page->new_optgroup(L("Maximum feedrates")); for (const std::string &axis : axes) { append_option_line(optgroup, "machine_max_feedrate_" + axis); } - optgroup = page->new_optgroup(_(L("Maximum accelerations"))); + optgroup = page->new_optgroup(L("Maximum accelerations")); for (const std::string &axis : axes) { append_option_line(optgroup, "machine_max_acceleration_" + axis); } append_option_line(optgroup, "machine_max_acceleration_extruding"); append_option_line(optgroup, "machine_max_acceleration_retracting"); - optgroup = page->new_optgroup(_(L("Jerk limits"))); + optgroup = page->new_optgroup(L("Jerk limits")); for (const std::string &axis : axes) { append_option_line(optgroup, "machine_max_jerk_" + axis); } - optgroup = page->new_optgroup(_(L("Minimum feedrates"))); + optgroup = page->new_optgroup(L("Minimum feedrates")); append_option_line(optgroup, "machine_min_extruding_rate"); append_option_line(optgroup, "machine_min_travel_rate"); @@ -2247,18 +2478,18 @@ void TabPrinter::build_unregular_pages() /* 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) +/* 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 - if (m_pages[i]->title().find(_(L("Machine limits"))) != std::string::npos) { + if (m_pages[i]->title().find(L("Machine limits")) != std::string::npos) { if (!is_marlin_flavor || m_rebuild_kinematics_page) m_pages.erase(m_pages.begin() + i); else @@ -2269,7 +2500,7 @@ void TabPrinter::build_unregular_pages() if (existed_page < n_before_extruders && is_marlin_flavor) { auto page = build_kinematics_page(); #ifdef __WXMSW__ - layout_page(page); +// layout_page(page); #endif m_pages.insert(m_pages.begin() + n_before_extruders, page); } @@ -2283,7 +2514,7 @@ void TabPrinter::build_unregular_pages() { // if we have a single extruder MM setup, add a page with configuration options: for (size_t i = 0; i < m_pages.size(); ++i) // first make sure it's not there already - if (m_pages[i]->title().find(_(L("Single extruder MM setup"))) != std::string::npos) { + if (m_pages[i]->title().find(L("Single extruder MM setup")) != std::string::npos) { m_pages.erase(m_pages.begin() + i); break; } @@ -2291,8 +2522,8 @@ void TabPrinter::build_unregular_pages() } if (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"))); + auto page = add_options_page(L("Single extruder MM setup"), "printer", true); + auto optgroup = page->new_optgroup(L("Single extruder multimaterial parameters")); optgroup->append_single_option_line("cooling_tube_retraction"); optgroup->append_single_option_line("cooling_tube_length"); optgroup->append_single_option_line("parking_pos_retraction"); @@ -2305,12 +2536,12 @@ void TabPrinter::build_unregular_pages() // Build missed extruder pages for (auto extruder_idx = m_extruders_count_old; extruder_idx < m_extruders_count; ++extruder_idx) { //# build page - const wxString& page_name = wxString::Format(_(L("Extruder %d")), int(extruder_idx + 1)); + const wxString& page_name = wxString::Format("Extruder %d", int(extruder_idx + 1)); auto page = add_options_page(page_name, "funnel", true); m_pages.insert(m_pages.begin() + n_before_extruders + extruder_idx, page); - auto optgroup = page->new_optgroup(_(L("Size"))); - optgroup->append_single_option_line("nozzle_diameter", extruder_idx); + auto optgroup = page->new_optgroup(L("Size")); + optgroup->append_single_option_line("nozzle_diameter", wxEmptyString, extruder_idx); optgroup->m_on_change = [this, extruder_idx](const t_config_option_key& opt_key, boost::any value) { @@ -2347,41 +2578,42 @@ void TabPrinter::build_unregular_pages() update(); }; - optgroup = page->new_optgroup(_(L("Layer height limits"))); - optgroup->append_single_option_line("min_layer_height", extruder_idx); - optgroup->append_single_option_line("max_layer_height", extruder_idx); + optgroup = page->new_optgroup(L("Layer height limits")); + optgroup->append_single_option_line("min_layer_height", wxEmptyString, extruder_idx); + optgroup->append_single_option_line("max_layer_height", wxEmptyString, extruder_idx); - optgroup = page->new_optgroup(_(L("Position (for multi-extruder printers)"))); - optgroup->append_single_option_line("extruder_offset", extruder_idx); + optgroup = page->new_optgroup(L("Position (for multi-extruder printers)")); + optgroup->append_single_option_line("extruder_offset", wxEmptyString, extruder_idx); - optgroup = page->new_optgroup(_(L("Retraction"))); - optgroup->append_single_option_line("retract_length", extruder_idx); - optgroup->append_single_option_line("retract_lift", extruder_idx); - Line line = { _(L("Only lift Z")), "" }; + optgroup = page->new_optgroup(L("Retraction")); + optgroup->append_single_option_line("retract_length", wxEmptyString, extruder_idx); + optgroup->append_single_option_line("retract_lift", wxEmptyString, extruder_idx); + Line line = { L("Only lift Z"), "" }; line.append_option(optgroup->get_option("retract_lift_above", extruder_idx)); line.append_option(optgroup->get_option("retract_lift_below", extruder_idx)); optgroup->append_line(line); - optgroup->append_single_option_line("retract_speed", extruder_idx); - optgroup->append_single_option_line("deretract_speed", extruder_idx); - optgroup->append_single_option_line("retract_restart_extra", extruder_idx); - optgroup->append_single_option_line("retract_before_travel", extruder_idx); - optgroup->append_single_option_line("retract_layer_change", extruder_idx); - optgroup->append_single_option_line("wipe", extruder_idx); - optgroup->append_single_option_line("retract_before_wipe", extruder_idx); + optgroup->append_single_option_line("retract_speed", wxEmptyString, extruder_idx); + optgroup->append_single_option_line("deretract_speed", wxEmptyString, extruder_idx); + optgroup->append_single_option_line("retract_restart_extra", wxEmptyString, extruder_idx); + optgroup->append_single_option_line("retract_before_travel", wxEmptyString, extruder_idx); + optgroup->append_single_option_line("retract_layer_change", wxEmptyString, extruder_idx); + optgroup->append_single_option_line("wipe", wxEmptyString, extruder_idx); + optgroup->append_single_option_line("retract_before_wipe", wxEmptyString, extruder_idx); - optgroup = page->new_optgroup(_(L("Retraction when tool is disabled (advanced settings for multi-extruder setups)"))); - optgroup->append_single_option_line("retract_length_toolchange", extruder_idx); - optgroup->append_single_option_line("retract_restart_extra_toolchange", extruder_idx); + optgroup = page->new_optgroup(L("Retraction when tool is disabled (advanced settings for multi-extruder setups)")); + optgroup->append_single_option_line("retract_length_toolchange", wxEmptyString, extruder_idx); + optgroup->append_single_option_line("retract_restart_extra_toolchange", wxEmptyString, extruder_idx); - optgroup = page->new_optgroup(_(L("Preview"))); + optgroup = page->new_optgroup(L("Preview")); auto reset_to_filament_color = [this, extruder_idx](wxWindow* parent) { - add_scaled_button(parent, &m_reset_to_filament_color, "undo", - _(L("Reset to Filament Color")), wxBU_LEFT | wxBU_EXACTFIT); + m_reset_to_filament_color = new ScalableButton(parent, wxID_ANY, "undo", _L("Reset to Filament Color"), + wxDefaultSize, wxDefaultPosition, wxBU_LEFT | wxBU_EXACTFIT, true); ScalableButton* btn = m_reset_to_filament_color; btn->SetFont(Slic3r::GUI::wxGetApp().normal_font()); + btn->SetSize(btn->GetBestSize()); auto sizer = new wxBoxSizer(wxHORIZONTAL); sizer->Add(btn); @@ -2400,12 +2632,12 @@ void TabPrinter::build_unregular_pages() return sizer; }; - line = optgroup->create_single_option_line("extruder_colour", extruder_idx); + 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); +// layout_page(page); #endif } @@ -2421,6 +2653,9 @@ void TabPrinter::build_unregular_pages() // Reload preset pages with current configuration values reload_config(); + + // apply searcher with current configuration + apply_searcher(); } // this gets executed after preset is loaded and before GUI fields are updated @@ -2429,7 +2664,6 @@ void TabPrinter::on_preset_loaded() // update the extruders count field auto *nozzle_diameter = dynamic_cast<const ConfigOptionFloats*>(m_config->option("nozzle_diameter")); size_t extruders_count = nozzle_diameter->values.size(); - set_value("extruders_count", int(extruders_count)); // update the GUI field according to the number of nozzle diameters supplied extruders_count_changed(extruders_count); } @@ -2441,9 +2675,8 @@ void TabPrinter::update_pages() if (new_printer_technology == m_printer_technology) return; - // hide all old pages - for (auto& el : m_pages) - el.get()->Hide(); + //clear all active pages before switching + clear_pages(); // set m_pages to m_pages_(technology before changing) m_printer_technology == ptFFF ? m_pages.swap(m_pages_fff) : m_pages.swap(m_pages_sla); @@ -2473,95 +2706,89 @@ void TabPrinter::update_pages() rebuild_page_tree(); } -void TabPrinter::update() +void TabPrinter::reload_config() { - m_update_cnt++; - m_presets->get_edited_preset().printer_technology() == ptFFF ? update_fff() : update_sla(); - m_update_cnt--; + Tab::reload_config(); - if (m_update_cnt == 0) - wxGetApp().mainframe->on_config_changed(m_config); + // "extruders_count" doesn't update from the update_config(), + // so update it implicitly + if (m_active_page && m_active_page->title() == "General") + m_active_page->set_value("extruders_count", int(m_extruders_count)); } -void TabPrinter::update_fff() +void TabPrinter::activate_selected_page(std::function<void()> throw_if_canceled) { -// Freeze(); + Tab::activate_selected_page(throw_if_canceled); - bool en; - auto serial_speed = get_field("serial_speed"); - if (serial_speed != nullptr) { - en = !m_config->opt_string("serial_port").empty(); - get_field("serial_speed")->toggle(en); - if (m_config->opt_int("serial_speed") != 0 && en) - m_serial_test_btn->Enable(); - else - m_serial_test_btn->Disable(); - } - - { - std::unique_ptr<PrintHost> host(PrintHost::get_print_host(m_config)); - m_print_host_test_btn->Enable(!m_config->opt_string("print_host").empty() && host->can_test()); - m_printhost_browse_btn->Enable(host->has_auto_discovery()); - } - - bool have_multiple_extruders = m_extruders_count > 1; - get_field("toolchange_gcode")->toggle(have_multiple_extruders); - get_field("single_extruder_multi_material")->toggle(have_multiple_extruders); + // "extruders_count" doesn't update from the update_config(), + // so update it implicitly + if (m_active_page && m_active_page->title() == "General") + m_active_page->set_value("extruders_count", int(m_extruders_count)); +} - bool is_marlin_flavor = m_config->option<ConfigOptionEnum<GCodeFlavor>>("gcode_flavor")->value == gcfMarlin; +void TabPrinter::clear_pages() +{ + Tab::clear_pages(); + m_reset_to_filament_color = nullptr; +} - { - Field *sm = get_field("silent_mode"); - if (! is_marlin_flavor) - // Disable silent mode for non-marlin firmwares. - get_field("silent_mode")->toggle(false); - if (is_marlin_flavor) - sm->enable(); - else - sm->disable(); - } +void TabPrinter::toggle_options() +{ + if (!m_active_page || m_presets->get_edited_preset().printer_technology() == ptSLA) + return; - if (m_use_silent_mode != m_config->opt_bool("silent_mode")) { - m_rebuild_kinematics_page = true; - m_use_silent_mode = m_config->opt_bool("silent_mode"); + bool have_multiple_extruders = m_extruders_count > 1; + if (m_active_page->title() == "Custom G-code") + toggle_option("toolchange_gcode", have_multiple_extruders); + if (m_active_page->title() == "General") { + toggle_option("single_extruder_multi_material", have_multiple_extruders); + + bool is_marlin_flavor = m_config->option<ConfigOptionEnum<GCodeFlavor>>("gcode_flavor")->value == gcfMarlin; + // Disable silent mode for non-marlin firmwares. + toggle_option("silent_mode", is_marlin_flavor); } - for (size_t i = 0; i < m_extruders_count; ++i) { + wxString extruder_number; + long val; + if (m_active_page->title().StartsWith("Extruder ", &extruder_number) && extruder_number.ToLong(&val) && + val > 0 && (size_t)val <= m_extruders_count) + { + size_t i = size_t(val - 1); bool have_retract_length = m_config->opt_float("retract_length", i) > 0; // when using firmware retraction, firmware decides retraction length bool use_firmware_retraction = m_config->opt_bool("use_firmware_retraction"); - get_field("retract_length", i)->toggle(!use_firmware_retraction); + toggle_option("retract_length", !use_firmware_retraction, i); // user can customize travel length if we have retraction length or we"re using // firmware retraction - get_field("retract_before_travel", i)->toggle(have_retract_length || use_firmware_retraction); + toggle_option("retract_before_travel", have_retract_length || use_firmware_retraction, i); // user can customize other retraction options if retraction is enabled bool retraction = (have_retract_length || use_firmware_retraction); std::vector<std::string> vec = { "retract_lift", "retract_layer_change" }; for (auto el : vec) - get_field(el, i)->toggle(retraction); + toggle_option(el, retraction, i); // retract lift above / below only applies if using retract lift vec.resize(0); vec = { "retract_lift_above", "retract_lift_below" }; for (auto el : vec) - get_field(el, i)->toggle(retraction && m_config->opt_float("retract_lift", i) > 0); + toggle_option(el, retraction && (m_config->opt_float("retract_lift", i) > 0), i); // some options only apply when not using firmware retraction vec.resize(0); vec = { "retract_speed", "deretract_speed", "retract_before_wipe", "retract_restart_extra", "wipe" }; for (auto el : vec) - get_field(el, i)->toggle(retraction && !use_firmware_retraction); + toggle_option(el, retraction && !use_firmware_retraction, i); bool wipe = m_config->opt_bool("wipe", i); - get_field("retract_before_wipe", i)->toggle(wipe); + toggle_option("retract_before_wipe", wipe, i); if (use_firmware_retraction && wipe) { wxMessageDialog dialog(parent(), _(L("The Wipe option is not available when using the Firmware Retraction mode.\n" - "\nShall I disable it in order to enable Firmware Retraction?")), + "\nShall I disable it in order to enable Firmware Retraction?")), _(L("Firmware Retraction")), wxICON_WARNING | wxYES | wxNO); DynamicPrintConfig new_conf = *m_config; @@ -2577,14 +2804,45 @@ void TabPrinter::update_fff() load_config(new_conf); } - get_field("retract_length_toolchange", i)->toggle(have_multiple_extruders); + toggle_option("retract_length_toolchange", have_multiple_extruders, i); bool toolchange_retraction = m_config->opt_float("retract_length_toolchange", i) > 0; - get_field("retract_restart_extra_toolchange", i)->toggle - (have_multiple_extruders && toolchange_retraction); + toggle_option("retract_restart_extra_toolchange", have_multiple_extruders && toolchange_retraction, i); } -// Thaw(); + if (m_active_page->title() == "Machine limits" && m_machine_limits_description_line) { + assert(m_config->option<ConfigOptionEnum<GCodeFlavor>>("gcode_flavor")->value == gcfMarlin); + const auto *machine_limits_usage = m_config->option<ConfigOptionEnum<MachineLimitsUsage>>("machine_limits_usage"); + bool enabled = machine_limits_usage->value != MachineLimitsUsage::Ignore; + bool silent_mode = m_config->opt_bool("silent_mode"); + int max_field = silent_mode ? 2 : 1; + for (const std::string &opt : Preset::machine_limits_options()) + for (int i = 0; i < max_field; ++ i) + toggle_option(opt, enabled, i); + update_machine_limits_description(machine_limits_usage->value); + } +} + +void TabPrinter::update() +{ + m_update_cnt++; + m_presets->get_edited_preset().printer_technology() == ptFFF ? update_fff() : update_sla(); + m_update_cnt--; + + Layout(); + + if (m_update_cnt == 0) + wxGetApp().mainframe->on_config_changed(m_config); +} + +void TabPrinter::update_fff() +{ + if (m_use_silent_mode != m_config->opt_bool("silent_mode")) { + m_rebuild_kinematics_page = true; + m_use_silent_mode = m_config->opt_bool("silent_mode"); + } + + toggle_options(); } void TabPrinter::update_sla() @@ -2604,7 +2862,7 @@ void Tab::load_current_preset() { const Preset& preset = m_presets->get_edited_preset(); - (preset.is_default || preset.is_system) ? m_btn_delete_preset->Disable() : m_btn_delete_preset->Enable(true); + update_btns_enabling(); update(); if (m_type == Slic3r::Preset::TYPE_PRINTER) { @@ -2639,6 +2897,13 @@ void Tab::load_current_preset() const PrinterTechnology printer_technology = m_presets->get_edited_preset().printer_technology(); if (printer_technology != static_cast<TabPrinter*>(this)->m_printer_technology) { + // The change of the technology requires to remove some of unrelated Tabs + // During this action, wxNoteBook::RemovePage invoke wxEVT_NOTEBOOK_PAGE_CHANGED + // and as a result a function select_active_page() is called fron Tab::OnActive() + // But we don't need it. So, to avoid activation of the page, set m_active_page to NULL + // till unusable Tabs will be deleted + Page* tmp_page = m_active_page; + m_active_page = nullptr; for (auto tab : wxGetApp().tabs_list) { if (tab->type() == Preset::TYPE_PRINTER) // Printer tab is shown every time continue; @@ -2657,10 +2922,11 @@ void Tab::load_current_preset() } } static_cast<TabPrinter*>(this)->m_printer_technology = printer_technology; + m_active_page = tmp_page; } on_presets_changed(); if (printer_technology == ptFFF) { - static_cast<TabPrinter*>(this)->m_initial_extruders_count = static_cast<TabPrinter*>(this)->m_extruders_count; + static_cast<TabPrinter*>(this)->m_initial_extruders_count = static_cast<const ConfigOptionFloats*>(m_presets->get_selected_preset().config.option("nozzle_diameter"))->values.size(); //static_cast<TabPrinter*>(this)->m_extruders_count; const Preset* parent_preset = m_presets->get_selected_preset_parent(); static_cast<TabPrinter*>(this)->m_sys_extruders_count = parent_preset == nullptr ? 0 : static_cast<const ConfigOptionFloats*>(parent_preset->config.option("nozzle_diameter"))->values.size(); @@ -2690,61 +2956,57 @@ void Tab::rebuild_page_tree() const auto selected = sel_item ? m_treectrl->GetItemText(sel_item) : ""; const auto rootItem = m_treectrl->GetRootItem(); - auto have_selection = 0; + wxTreeItemId item; + + // Delete/Append events invoke wxEVT_TREE_SEL_CHANGED event. + // To avoid redundant clear/activate functions call + // suppress activate page before page_tree rebuilding + m_disable_tree_sel_changed_event = true; m_treectrl->DeleteChildren(rootItem); + for (auto p : m_pages) { - auto itemId = m_treectrl->AppendItem(rootItem, p->title(), p->iconID()); + if (!p->get_show()) + continue; + auto itemId = m_treectrl->AppendItem(rootItem, translate_category(p->title(), m_type), p->iconID()); m_treectrl->SetItemTextColour(itemId, p->get_item_colour()); - if (p->title() == selected) { - m_treectrl->SelectItem(itemId); - have_selection = 1; - } + if (translate_category(p->title(), m_type) == selected) + item = itemId; } - - if (!have_selection) { + if (!item) { // this is triggered on first load, so we don't disable the sel change event - auto item = m_treectrl->GetFirstVisibleItem(); - if (item) { - m_treectrl->SelectItem(item); - } + item = m_treectrl->GetFirstVisibleItem(); } + + // allow activate page before selection of a page_tree item + m_disable_tree_sel_changed_event = false; + if (item) + m_treectrl->SelectItem(item); } -void Tab::update_page_tree_visibility() +void Tab::update_btns_enabling() { - const auto sel_item = m_treectrl->GetSelection(); - const auto selected = sel_item ? m_treectrl->GetItemText(sel_item) : ""; - const auto rootItem = m_treectrl->GetRootItem(); - - auto have_selection = 0; - m_treectrl->DeleteChildren(rootItem); - for (auto p : m_pages) - { - if (!p->get_show()) - continue; - auto itemId = m_treectrl->AppendItem(rootItem, p->title(), p->iconID()); - m_treectrl->SetItemTextColour(itemId, p->get_item_colour()); - if (p->title() == selected) { - m_treectrl->SelectItem(itemId); - have_selection = 1; - } - } + // we can delete any preset from the physical printer + // and any user preset + const Preset& preset = m_presets->get_edited_preset(); + m_btn_delete_preset->Show(m_type == Preset::TYPE_PRINTER && m_preset_bundle->physical_printers.has_selection() || + !preset.is_default && !preset.is_system); - if (!have_selection) { - // this is triggered on first load, so we don't disable the sel change event - auto item = m_treectrl->GetFirstVisibleItem(); - if (item) { - m_treectrl->SelectItem(item); - } - } + if (m_btn_edit_ph_printer) + m_btn_edit_ph_printer->SetToolTip( m_preset_bundle->physical_printers.has_selection() ? + _L("Edit physical printer") : _L("Add physical printer")); +} +void Tab::update_preset_choice() +{ + m_presets_choice->update(); + update_btns_enabling(); } // Called by the UI combo box when the user switches profiles, and also to delete the current profile. // Select a preset by a name.If !defined(name), then the default preset is selected. // If the current profile is modified, user is asked to save the changes. -void Tab::select_preset(std::string preset_name, bool delete_current) +void Tab::select_preset(std::string preset_name, bool delete_current /*=false*/, const std::string& last_selected_ph_printer_name/* =""*/) { if (preset_name.empty()) { if (delete_current) { @@ -2770,7 +3032,7 @@ void Tab::select_preset(std::string preset_name, bool delete_current) bool canceled = false; bool technology_changed = false; m_dependent_tabs.clear(); - if (current_dirty && ! may_discard_current_dirty_preset()) { + if (current_dirty && ! may_discard_current_dirty_preset(nullptr, preset_name)) { canceled = true; } else if (print_tab) { // Before switching the print profile to a new one, verify, whether the currently active filament or SLA material @@ -2852,7 +3114,21 @@ void Tab::select_preset(std::string preset_name, bool delete_current) } if (canceled) { + if (m_type == Preset::TYPE_PRINTER) { + if (!last_selected_ph_printer_name.empty() && + m_presets->get_edited_preset().name == PhysicalPrinter::get_preset_name(last_selected_ph_printer_name)) { + // If preset selection was canceled and previously was selected physical printer, we should select it back + m_preset_bundle->physical_printers.select_printer(last_selected_ph_printer_name); + } + if (m_preset_bundle->physical_printers.has_selection()) { + // If preset selection was canceled and physical printer was selected + // we must disable selection marker for the physical printers + m_preset_bundle->physical_printers.unselect_printer(); + } + } + update_tab_ui(); + // Trigger the on_presets_changed event so that we also restore the previous value in the plater selector, // if this action was initiated from the plater. on_presets_changed(); @@ -2894,6 +3170,14 @@ void Tab::select_preset(std::string preset_name, bool delete_current) else if (printer_technology == ptSLA && m_dependent_tabs.front() != Preset::Type::TYPE_SLA_PRINT) m_dependent_tabs = { Preset::Type::TYPE_SLA_PRINT, Preset::Type::TYPE_SLA_MATERIAL }; } + + // check and apply extruders count for printer preset + if (m_type == Preset::TYPE_PRINTER) + static_cast<TabPrinter*>(this)->apply_extruder_cnt_from_cache(); + + // check if there is something in the cache to move to the new selected preset + apply_config_from_cache(); + load_current_preset(); } } @@ -2903,41 +3187,56 @@ void Tab::select_preset(std::string preset_name, bool delete_current) bool Tab::may_discard_current_dirty_preset(PresetCollection* presets /*= nullptr*/, const std::string& new_printer_name /*= ""*/) { if (presets == nullptr) presets = m_presets; - // Display a dialog showing the dirty options in a human readable form. - const Preset& old_preset = presets->get_edited_preset(); - std::string type_name = presets->name(); - wxString tab = " "; - wxString name = old_preset.is_default ? - from_u8((boost::format(_utf8(L("Default preset (%s)"))) % _utf8(type_name)).str()) : - from_u8((boost::format(_utf8(L("Preset (%s)"))) % _utf8(type_name)).str()) + "\n" + tab + old_preset.name; - - // Collect descriptions of the dirty options. - wxString changes; - for (const std::string &opt_key : presets->current_dirty_options()) { - const ConfigOptionDef &opt = m_config->def()->options.at(opt_key); - /*std::string*/wxString name = ""; - if (! opt.category.empty()) - name += _(opt.category) + " > "; - name += !opt.full_label.empty() ? - _(opt.full_label) : - _(opt.label); - changes += tab + /*from_u8*/(name) + "\n"; - } - // Show a confirmation dialog with the list of dirty options. - wxString message = name + "\n\n"; - if (new_printer_name.empty()) - message += _(L("has the following unsaved changes:")); - else { - message += (m_type == Slic3r::Preset::TYPE_PRINTER) ? - _(L("is not compatible with printer")) : - _(L("is not compatible with print profile")); - message += wxString("\n") + tab + from_u8(new_printer_name) + "\n\n"; - message += _(L("and it has the following unsaved changes:")); + + UnsavedChangesDialog dlg(m_type, presets, new_printer_name); + if (wxGetApp().app_config->get("default_action_on_select_preset") == "none" && dlg.ShowModal() == wxID_CANCEL) + return false; + + if (dlg.save_preset()) // save selected changes + { + const std::vector<std::string>& unselected_options = dlg.get_unselected_options(presets->type()); + const std::string& name = dlg.get_preset_name(); + + if (m_type == presets->type()) // save changes for the current preset from this tab + { + // revert unselected options to the old values + presets->get_edited_preset().config.apply_only(presets->get_selected_preset().config, unselected_options); + save_preset(name); + } + else + { + m_preset_bundle->save_changes_for_preset(name, presets->type(), unselected_options); + + // If filament preset is saved for multi-material printer preset, + // there are cases when filament comboboxs are updated for old (non-modified) colors, + // but in full_config a filament_colors option aren't. + if (presets->type() == Preset::TYPE_FILAMENT && wxGetApp().extruders_edited_cnt() > 1) + wxGetApp().plater()->force_filament_colors_update(); + } } - wxMessageDialog confirm(parent(), - message + "\n" + changes + "\n\n" + _(L("Discard changes and continue anyway?")), - _(L("Unsaved Changes")), wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION); - return confirm.ShowModal() == wxID_YES; + else if (dlg.transfer_changes()) // move selected changes + { + std::vector<std::string> selected_options = dlg.get_selected_options(); + if (m_type == presets->type()) // move changes for the current preset from this tab + { + if (m_type == Preset::TYPE_PRINTER) { + auto it = std::find(selected_options.begin(), selected_options.end(), "extruders_count"); + if (it != selected_options.end()) { + // erase "extruders_count" option from the list + selected_options.erase(it); + // cache the extruders count + static_cast<TabPrinter*>(this)->cache_extruder_cnt(); + } + } + + // copy selected options to the cache from edited preset + cache_config_diff(selected_options); + } + else + wxGetApp().get_tab(presets->type())->cache_config_diff(selected_options); + } + + return true; } // If we are switching from the FFF-preset to the SLA, we should to control the printed objects if they have a part(s). @@ -2955,14 +3254,48 @@ bool Tab::may_switch_to_SLA_preset() return true; } -void Tab::OnTreeSelChange(wxTreeEvent& event) +void Tab::clear_pages() { - if (m_disable_tree_sel_changed_event) + // invalidated highlighter, if any exists + m_highlighter.invalidate(); + m_page_sizer->Clear(true); + // clear pages from the controlls + for (auto p : m_pages) + p->clear(); + + // nulling pointers + m_parent_preset_description_line = nullptr; + m_detach_preset_btn = nullptr; + + m_compatible_printers.checkbox = nullptr; + m_compatible_printers.btn = nullptr; + + m_compatible_prints.checkbox = nullptr; + m_compatible_prints.btn = nullptr; +} + +void Tab::update_description_lines() +{ + if (m_active_page && m_active_page->title() == "Dependencies" && m_parent_preset_description_line) + update_preset_description_line(); +} + +void Tab::activate_selected_page(std::function<void()> throw_if_canceled) +{ + if (!m_active_page) return; -// There is a bug related to Ubuntu overlay scrollbars, see https://github.com/prusa3d/PrusaSlicer/issues/898 and https://github.com/prusa3d/PrusaSlicer/issues/952. -// The issue apparently manifests when Show()ing a window with overlay scrollbars while the UI is frozen. For this reason, -// we will Thaw the UI prematurely on Linux. This means destroing the no_updates object prematurely. + m_active_page->activate(m_mode, throw_if_canceled); + update_changed_ui(); + update_description_lines(); + toggle_options(); +} + +bool Tab::tree_sel_change_delayed() +{ + // There is a bug related to Ubuntu overlay scrollbars, see https://github.com/prusa3d/PrusaSlicer/issues/898 and https://github.com/prusa3d/PrusaSlicer/issues/952. + // The issue apparently manifests when Show()ing a window with overlay scrollbars while the UI is frozen. For this reason, + // we will Thaw the UI prematurely on Linux. This means destroing the no_updates object prematurely. #ifdef __linux__ std::unique_ptr<wxWindowUpdateLocker> no_updates(new wxWindowUpdateLocker(this)); #else @@ -2970,44 +3303,60 @@ void Tab::OnTreeSelChange(wxTreeEvent& event) * so on Window is no needed to call a Freeze/Thaw functions. * But under OSX (builds compiled with MacOSX10.14.sdk) wxStaticBitmap rendering is broken without Freeze/Thaw call. */ -#ifdef __WXOSX__ - wxWindowUpdateLocker noUpdates(this); -#endif +//#ifdef __WXOSX__ // Use Freeze/Thaw to avoid flickering during clear/activate new page + wxWindowUpdateLocker noUpdates(this); +//#endif #endif - if (m_pages.empty()) - return; - Page* page = nullptr; const auto sel_item = m_treectrl->GetSelection(); const auto selection = sel_item ? m_treectrl->GetItemText(sel_item) : ""; for (auto p : m_pages) - if (p->title() == selection) + if (translate_category(p->title(), m_type) == selection) { page = p.get(); m_is_nonsys_values = page->m_is_nonsys_values; m_is_modified_values = page->m_is_modified_values; break; } - if (page == nullptr) return; + if (page == nullptr || m_active_page == page) + return false; - for (auto& el : m_pages) -// if (el.get()->IsShown()) { - el.get()->Hide(); -// break; -// } + // clear pages from the controls + m_active_page = page; + + auto throw_if_canceled = std::function<void()>([this](){ +#ifdef WIN32 + wxCheckForInterrupt(m_treectrl); + if (m_page_switch_planned) + throw UIBuildCanceled(); +#endif // WIN32 + }); - #ifdef __linux__ - no_updates.reset(nullptr); - #endif + try { + clear_pages(); + throw_if_canceled(); + + if (wxGetApp().mainframe!=nullptr && wxGetApp().mainframe->is_active_and_shown_tab(this)) + activate_selected_page(throw_if_canceled); + + #ifdef __linux__ + no_updates.reset(nullptr); + #endif + + update_undo_buttons(); + throw_if_canceled(); - update_undo_buttons(); - page->Show(); -// if (! page->layout_valid) { - page->layout_valid = true; m_hsizer->Layout(); + throw_if_canceled(); Refresh(); -// } + } catch (const UIBuildCanceled&) { + if (m_active_page) + m_active_page->clear(); + return true; + } + + return false; } void Tab::OnKeyDown(wxKeyEvent& event) @@ -3022,7 +3371,7 @@ void Tab::OnKeyDown(wxKeyEvent& event) // This removes the "dirty" flag of the preset, possibly creates a new preset under a new name, // and activates the new preset. // Wizard calls save_preset with a name "My Settings", otherwise no name is provided and this method -// opens a Slic3r::GUI::SavePresetWindow dialog. +// opens a Slic3r::GUI::SavePresetDialog dialog. void Tab::save_preset(std::string name /*= ""*/, bool detach) { // since buttons(and choices too) don't get focus on Mac, we set focus manually @@ -3030,59 +3379,11 @@ void Tab::save_preset(std::string name /*= ""*/, bool detach) // focus currently.is there anything better than this ? //! m_treectrl->OnSetFocus(); - std::string suffix = detach ? _utf8(L("Detached")) : _CTX_utf8(L_CONTEXT("Copy", "PresetName"), "PresetName"); - if (name.empty()) { - const Preset &preset = m_presets->get_selected_preset(); - auto default_name = preset.is_default ? "Untitled" : -// preset.is_system ? (boost::format(_CTX_utf8(L_CONTEXT("%1% - Copy", "PresetName"), "PresetName")) % preset.name).str() : - preset.is_system ? (boost::format(("%1% - %2%")) % preset.name % suffix).str() : - preset.name; - - bool have_extention = boost::iends_with(default_name, ".ini"); - if (have_extention) { - size_t len = default_name.length()-4; - default_name.resize(len); - } - //[map $_->name, grep !$_->default && !$_->external, @{$self->{presets}}], - std::vector<std::string> values; - for (size_t i = 0; i < m_presets->size(); ++i) { - const Preset &preset = m_presets->preset(i); - if (preset.is_default || preset.is_system || preset.is_external) - continue; - values.push_back(preset.name); - } - - SavePresetWindow dlg(parent()); - dlg.build(title(), default_name, values); + SavePresetDialog dlg(m_parent, m_type, detach ? _u8L("Detached") : ""); if (dlg.ShowModal() != wxID_OK) return; name = dlg.get_name(); - if (name == "") { - show_error(this, _(L("The supplied name is empty. It can't be saved."))); - return; - } - const Preset *existing = m_presets->find_preset(name, false); - if (existing && (existing->is_default || existing->is_system)) { - show_error(this, _(L("Cannot overwrite a system profile."))); - return; - } - if (existing && (existing->is_external)) { - show_error(this, _(L("Cannot overwrite an external profile."))); - return; - } - if (existing && name != preset.name) - { - wxString msg_text = GUI::from_u8((boost::format(_utf8(L("Preset with name \"%1%\" already exists."))) % name).str()); - msg_text += "\n" + _(L("Replace?")); - wxMessageDialog dialog(nullptr, msg_text, _(L("Warning")), wxICON_WARNING | wxYES | wxNO); - - if (dialog.ShowModal() == wxID_NO) - return; - - // Remove the preset from the list. - m_presets->delete_preset(name); - } } // Save the preset into Slic3r::data_dir / presets / section_name / preset_name.ini @@ -3095,7 +3396,7 @@ void Tab::save_preset(std::string name /*= ""*/, bool detach) // Update the selection boxes at the plater. on_presets_changed(); // If current profile is saved, "delete preset" button have to be enabled - m_btn_delete_preset->Enable(true); + m_btn_delete_preset->Show(); if (m_type == Preset::TYPE_PRINTER) static_cast<TabPrinter*>(this)->m_initial_extruders_count = static_cast<TabPrinter*>(this)->m_extruders_count; @@ -3113,28 +3414,28 @@ void Tab::save_preset(std::string name /*= ""*/, bool detach) wxGetApp().plater()->force_filament_colors_update(); { - // Profile compatiblity is updated first when the profile is saved. - // Update profile selection combo boxes at the depending tabs to reflect modifications in profile compatibility. - std::vector<Preset::Type> dependent; - switch (m_type) { - case Preset::TYPE_PRINT: - dependent = { Preset::TYPE_FILAMENT }; - break; - case Preset::TYPE_SLA_PRINT: - dependent = { Preset::TYPE_SLA_MATERIAL }; - break; - case Preset::TYPE_PRINTER: + // Profile compatiblity is updated first when the profile is saved. + // Update profile selection combo boxes at the depending tabs to reflect modifications in profile compatibility. + std::vector<Preset::Type> dependent; + switch (m_type) { + case Preset::TYPE_PRINT: + dependent = { Preset::TYPE_FILAMENT }; + break; + case Preset::TYPE_SLA_PRINT: + dependent = { Preset::TYPE_SLA_MATERIAL }; + break; + case Preset::TYPE_PRINTER: if (static_cast<const TabPrinter*>(this)->m_printer_technology == ptFFF) dependent = { Preset::TYPE_PRINT, Preset::TYPE_FILAMENT }; else dependent = { Preset::TYPE_SLA_PRINT, Preset::TYPE_SLA_MATERIAL }; - break; + break; default: - break; - } - for (Preset::Type preset_type : dependent) - wxGetApp().get_tab(preset_type)->update_tab_ui(); - } + break; + } + for (Preset::Type preset_type : dependent) + wxGetApp().get_tab(preset_type)->update_tab_ui(); + } } // Called for a currently selected preset. @@ -3144,13 +3445,73 @@ void Tab::delete_preset() // Don't let the user delete the ' - default - ' configuration. std::string action = current_preset.is_external ? _utf8(L("remove")) : _utf8(L("delete")); // TRN remove/delete - const wxString msg = from_u8((boost::format(_utf8(L("Are you sure you want to %1% the selected preset?"))) % action).str()); + + PhysicalPrinterCollection& physical_printers = m_preset_bundle->physical_printers; + wxString msg; + if (m_presets_choice->is_selected_physical_printer()) + { + PhysicalPrinter& printer = physical_printers.get_selected_printer(); + if (printer.preset_names.size() == 1) { + if (m_presets_choice->del_physical_printer(_L("It's a last preset for this physical printer."))) + Layout(); + return; + } + + msg = format_wxstr(_L("Are you sure you want to delete \"%1%\" preset from the physical printer \"%2%\"?"), current_preset.name, printer.name); + } + else + { + if (m_type == Preset::TYPE_PRINTER && !physical_printers.empty()) + { + // Check preset for delete in physical printers + // Ask a customer about next action, if there is a printer with just one preset and this preset is equal to delete + std::vector<std::string> ph_printers = physical_printers.get_printers_with_preset(current_preset.name); + std::vector<std::string> ph_printers_only = physical_printers.get_printers_with_only_preset(current_preset.name); + + if (!ph_printers.empty()) { + msg += _L("The physical printer(s) below is based on the preset, you are going to delete."); + for (const std::string& printer : ph_printers) + msg += "\n \"" + from_u8(printer) + "\","; + msg.RemoveLast(); + msg += "\n" + _L("Note, that selected preset will be deleted from this/those printer(s) too.")+ "\n\n"; + } + + if (!ph_printers_only.empty()) { + msg += _L("The physical printer(s) below is based only on the preset, you are going to delete."); + for (const std::string& printer : ph_printers_only) + msg += "\n \"" + from_u8(printer) + "\","; + msg.RemoveLast(); + msg += "\n" + _L("Note, that this/those printer(s) will be deleted after deleting of the selected preset.") + "\n\n"; + } + } + + msg += from_u8((boost::format(_u8L("Are you sure you want to %1% the selected preset?")) % action).str()); + } + action = current_preset.is_external ? _utf8(L("Remove")) : _utf8(L("Delete")); // TRN Remove/Delete wxString title = from_u8((boost::format(_utf8(L("%1% Preset"))) % action).str()); //action + _(L(" Preset")); if (current_preset.is_default || wxID_YES != wxMessageDialog(parent(), msg, title, wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION).ShowModal()) return; + + // if we just delete preset from the physical printer + if (m_presets_choice->is_selected_physical_printer()) { + PhysicalPrinter& printer = physical_printers.get_selected_printer(); + + // just delete this preset from the current physical printer + printer.delete_preset(m_presets->get_edited_preset().name); + // select first from the possible presets for this printer + physical_printers.select_printer(printer); + + this->select_preset(physical_printers.get_selected_printer_preset_name()); + return; + } + + // delete selected preset from printers and printer, if it's needed + if (m_type == Preset::TYPE_PRINTER && !physical_printers.empty()) + physical_printers.delete_preset_from_printers(current_preset.name); + // Select will handle of the preset dependencies, of saving & closing the depending profiles, and // finally of deleting the preset. this->select_preset("", true); @@ -3159,6 +3520,7 @@ void Tab::delete_preset() void Tab::toggle_show_hide_incompatible() { m_show_incompatible_presets = !m_show_incompatible_presets; + m_presets_choice->set_show_incompatible_presets(m_show_incompatible_presets); update_show_hide_incompatible_button(); update_tab_ui(); } @@ -3192,13 +3554,16 @@ void Tab::update_ui_from_settings() } } -void Tab::create_line_with_widget(ConfigOptionsGroup* optgroup, const std::string& opt_key, widget_t widget) +void Tab::create_line_with_widget(ConfigOptionsGroup* optgroup, const std::string& opt_key, const wxString& path, widget_t widget) { Line line = optgroup->create_single_option_line(opt_key); line.widget = widget; + line.label_path = path; - m_colored_Labels[opt_key] = nullptr; - optgroup->append_line(line, &m_colored_Labels[opt_key]); + m_colored_Label_colors[opt_key] = m_default_text_clr; + line.full_Label_color = &m_colored_Label_colors[opt_key]; + + optgroup->append_line(line); } // Return a callback to create a Tab widget to mark the preferences as compatible / incompatible to the current printer. @@ -3206,8 +3571,10 @@ wxSizer* Tab::compatible_widget_create(wxWindow* parent, PresetDependencies &dep { deps.checkbox = new wxCheckBox(parent, wxID_ANY, _(L("All"))); deps.checkbox->SetFont(Slic3r::GUI::wxGetApp().normal_font()); - add_scaled_button(parent, &deps.btn, "printer_white", from_u8((boost::format(" %s %s") % _utf8(L("Set")) % std::string(dots.ToUTF8())).str()), wxBU_LEFT | wxBU_EXACTFIT); + deps.btn = new ScalableButton(parent, wxID_ANY, "printer", from_u8((boost::format(" %s %s") % _utf8(L("Set")) % std::string(dots.ToUTF8())).str()), + wxDefaultSize, wxDefaultPosition, wxBU_LEFT | wxBU_EXACTFIT, true); deps.btn->SetFont(Slic3r::GUI::wxGetApp().normal_font()); + deps.btn->SetSize(deps.btn->GetBestSize()); auto sizer = new wxBoxSizer(wxHORIZONTAL); sizer->Add((deps.checkbox), 0, wxALIGN_CENTER_VERTICAL); @@ -3269,18 +3636,20 @@ wxSizer* Tab::compatible_widget_create(wxWindow* parent, PresetDependencies &dep this->update_changed_ui(); } })); + return sizer; } // Return a callback to create a TabPrinter widget to edit bed shape wxSizer* TabPrinter::create_bed_shape_widget(wxWindow* parent) { - ScalableButton* btn; - add_scaled_button(parent, &btn, "printer_white", " " + _(L("Set")) + " " + dots, wxBU_LEFT | wxBU_EXACTFIT); + ScalableButton* btn = new ScalableButton(parent, wxID_ANY, "printer", " " + _(L("Set")) + " " + dots, + wxDefaultSize, wxDefaultPosition, wxBU_LEFT | wxBU_EXACTFIT, true); btn->SetFont(wxGetApp().normal_font()); + btn->SetSize(btn->GetBestSize()); auto sizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(btn); + sizer->Add(btn, 0, wxALIGN_CENTER_VERTICAL); btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e) { @@ -3302,15 +3671,67 @@ wxSizer* TabPrinter::create_bed_shape_widget(wxWindow* parent) } })); + // may be it is not a best place, but + // add information about Category/Grope for "bed_custom_texture" and "bed_custom_model" as a copy from "bed_shape" option + { + Search::OptionsSearcher& searcher = wxGetApp().sidebar().get_searcher(); + const Search::GroupAndCategory& gc = searcher.get_group_and_category("bed_shape"); + searcher.add_key("bed_custom_texture", gc.group, gc.category); + searcher.add_key("bed_custom_model", gc.group, gc.category); + } + return sizer; } +void TabPrinter::cache_extruder_cnt() +{ + if (m_presets->get_edited_preset().printer_technology() == ptSLA) + return; + + m_cache_extruder_count = m_extruders_count; +} + +void TabPrinter::apply_extruder_cnt_from_cache() +{ + if (m_presets->get_edited_preset().printer_technology() == ptSLA) + return; + + if (m_cache_extruder_count > 0) { + m_presets->get_edited_preset().set_num_extruders(m_cache_extruder_count); + m_cache_extruder_count = 0; + } +} + +void TabPrinter::update_machine_limits_description(const MachineLimitsUsage usage) +{ + wxString text; + switch (usage) { + case MachineLimitsUsage::EmitToGCode: + text = _L("Machine limits will be emitted to G-code and used to estimate print time."); + break; + case MachineLimitsUsage::TimeEstimateOnly: + text = _L("Machine limits will NOT be emitted to G-code, however they will be used to estimate print time, " + "which may therefore not be accurate as the printer may apply a different set of machine limits."); + break; + case MachineLimitsUsage::Ignore: + text = _L("Machine limits are not set, therefore the print time estimate may not be accurate."); + break; + default: assert(false); + } + m_machine_limits_description_line->SetText(text); +} + void Tab::compatible_widget_reload(PresetDependencies &deps) { + Field* field = this->get_field(deps.key_condition); + if (!field) + return; + bool has_any = ! m_config->option<ConfigOptionStrings>(deps.key_list)->values.empty(); has_any ? deps.btn->Enable() : deps.btn->Disable(); deps.checkbox->SetValue(! has_any); - this->get_field(deps.key_condition)->toggle(! has_any); + + field->toggle(! has_any); } void Tab::fill_icon_descriptions() @@ -3371,27 +3792,70 @@ void Tab::set_tooltips_text() "Click to reset current value to the last saved preset.")); } +Page::Page(wxWindow* parent, const wxString& title, int iconID) : + m_parent(parent), + m_title(title), + m_iconID(iconID) +{ + m_vsizer = (wxBoxSizer*)parent->GetSizer(); + m_item_color = &wxGetApp().get_label_clr_default(); +} + void Page::reload_config() { for (auto group : m_optgroups) group->reload_config(); } -void Page::update_visibility(ConfigOptionMode mode) +void Page::update_visibility(ConfigOptionMode mode, bool update_contolls_visibility) { bool ret_val = false; - for (auto group : m_optgroups) - ret_val = group->update_visibility(mode) || ret_val; + for (auto group : m_optgroups) { + ret_val = (update_contolls_visibility ? + group->update_visibility(mode) : // update visibility for all controlls in group + group->is_visible(mode) // just detect visibility for the group + ) || ret_val; + } m_show = ret_val; } +void Page::activate(ConfigOptionMode mode, std::function<void()> throw_if_canceled) +{ + for (auto group : m_optgroups) { + if (!group->activate(throw_if_canceled)) + continue; + m_vsizer->Add(group->sizer, 0, wxEXPAND | (group->is_legend_line() ? (wxLEFT|wxTOP) : wxALL), 10); + group->update_visibility(mode); + group->reload_config(); + throw_if_canceled(); + } +} + +void Page::clear() +{ + for (auto group : m_optgroups) + group->clear(); +} + void Page::msw_rescale() { for (auto group : m_optgroups) group->msw_rescale(); } +void Page::sys_color_changed() +{ + for (auto group : m_optgroups) + group->sys_color_changed(); +} + +void Page::refresh() +{ + for (auto group : m_optgroups) + group->refresh(); +} + Field* Page::get_field(const t_config_option_key& opt_key, int opt_index /*= -1*/) const { Field* field = nullptr; @@ -3407,7 +3871,7 @@ bool Page::set_value(const t_config_option_key& opt_key, const boost::any& value bool changed = false; for(auto optgroup: m_optgroups) { if (optgroup->set_value(opt_key, value)) - changed = 1 ; + changed = true ; } return changed; } @@ -3415,29 +3879,16 @@ bool Page::set_value(const t_config_option_key& opt_key, const boost::any& value // package Slic3r::GUI::Tab::Page; ConfigOptionsGroupShp Page::new_optgroup(const wxString& title, int noncommon_label_width /*= -1*/) { - auto extra_column = [this](wxWindow* parent, const Line& line) - { - std::string bmp_name; - const std::vector<Option>& options = line.get_options(); - int mode_id = int(options[0].opt.mode); - const wxBitmap& bitmap = options.size() == 0 || options[0].opt.gui_type == "legend" ? wxNullBitmap : - m_mode_bitmap_cache[mode_id].bmp(); - auto bmp = new wxStaticBitmap(parent, wxID_ANY, bitmap); - bmp->SetClientData((void*)&m_mode_bitmap_cache[mode_id]); - - bmp->SetBackgroundStyle(wxBG_STYLE_PAINT); - return bmp; - }; - //! config_ have to be "right" - ConfigOptionsGroupShp optgroup = std::make_shared<ConfigOptionsGroup>(this, title, m_config, true, extra_column); + ConfigOptionsGroupShp optgroup = std::make_shared<ConfigOptionsGroup>(m_parent, title, m_config, true); + optgroup->set_config_category(m_title.ToStdString()); if (noncommon_label_width >= 0) optgroup->label_width = noncommon_label_width; #ifdef __WXOSX__ - auto tab = GetParent()->GetParent(); + auto tab = parent()->GetParent()->GetParent();// GetParent()->GetParent(); #else - auto tab = GetParent(); + auto tab = parent()->GetParent();// GetParent(); #endif optgroup->m_on_change = [this, tab](t_config_option_key opt_key, boost::any value) { //! This function will be called from OptionGroup. @@ -3471,79 +3922,19 @@ ConfigOptionsGroupShp Page::new_optgroup(const wxString& title, int noncommon_la ctrl->SetBitmap(reinterpret_cast<ScalableBitmap*>(ctrl->GetClientData())->bmp()); }; - vsizer()->Add(optgroup->sizer, 0, wxEXPAND | wxALL, 10); m_optgroups.push_back(optgroup); return optgroup; } -void SavePresetWindow::build(const wxString& title, const std::string& default_name, std::vector<std::string> &values) -{ - // TRN Preset - auto text = new wxStaticText(this, wxID_ANY, from_u8((boost::format(_utf8(L("Save %s as:"))) % into_u8(title)).str()), - wxDefaultPosition, wxDefaultSize); - m_combo = new wxComboBox(this, wxID_ANY, from_u8(default_name), - wxDefaultPosition, wxDefaultSize, 0, 0, wxTE_PROCESS_ENTER); - for (auto value : values) - m_combo->Append(from_u8(value)); - auto buttons = CreateStdDialogButtonSizer(wxOK | wxCANCEL); - - auto sizer = new wxBoxSizer(wxVERTICAL); - sizer->Add(text, 0, wxEXPAND | wxALL, 10); - sizer->Add(m_combo, 0, wxEXPAND | wxLEFT | wxRIGHT, 10); - sizer->Add(buttons, 0, wxALIGN_CENTER_HORIZONTAL | wxALL, 10); - - wxButton* btn = static_cast<wxButton*>(FindWindowById(wxID_OK, this)); - btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { accept(); }); - m_combo->Bind(wxEVT_TEXT_ENTER, [this](wxCommandEvent&) { accept(); }); - - SetSizer(sizer); - sizer->SetSizeHints(this); -} - -void SavePresetWindow::accept() -{ - m_chosen_name = normalize_utf8_nfc(m_combo->GetValue().ToUTF8()); - if (!m_chosen_name.empty()) { - const char* unusable_symbols = "<>[]:/\\|?*\""; - bool is_unusable_symbol = false; - bool is_unusable_suffix = false; - const std::string unusable_suffix = PresetCollection::get_suffix_modified();//"(modified)"; - for (size_t i = 0; i < std::strlen(unusable_symbols); i++) { - if (m_chosen_name.find_first_of(unusable_symbols[i]) != std::string::npos) { - is_unusable_symbol = true; - break; - } - } - if (m_chosen_name.find(unusable_suffix) != std::string::npos) - is_unusable_suffix = true; - - if (is_unusable_symbol) { - show_error(this,_(L("The supplied name is not valid;")) + "\n" + - _(L("the following characters are not allowed:")) + " " + unusable_symbols); - } - else if (is_unusable_suffix) { - show_error(this,_(L("The supplied name is not valid;")) + "\n" + - _(L("the following suffix is not allowed:")) + "\n\t" + - wxString::FromUTF8(unusable_suffix.c_str())); - } - else if (m_chosen_name == "- default -") { - show_error(this, _(L("The supplied name is not available."))); - } - else { - EndModal(wxID_OK); - } - } -} - void TabSLAMaterial::build() { m_presets = &m_preset_bundle->sla_materials; load_initial_data(); - auto page = add_options_page(_(L("Material")), "resin"); + auto page = add_options_page(L("Material"), "resin"); - auto optgroup = page->new_optgroup(_(L("Material"))); + auto optgroup = page->new_optgroup(L("Material")); optgroup->append_single_option_line("bottle_cost"); optgroup->append_single_option_line("bottle_volume"); optgroup->append_single_option_line("bottle_weight"); @@ -3575,19 +3966,19 @@ void TabSLAMaterial::build() wxGetApp().sidebar().Layout(); }; - optgroup = page->new_optgroup(_(L("Layers"))); + optgroup = page->new_optgroup(L("Layers")); optgroup->append_single_option_line("initial_layer_height"); - optgroup = page->new_optgroup(_(L("Exposure"))); + optgroup = page->new_optgroup(L("Exposure")); optgroup->append_single_option_line("exposure_time"); optgroup->append_single_option_line("initial_exposure_time"); - optgroup = page->new_optgroup(_(L("Corrections"))); + optgroup = page->new_optgroup(L("Corrections")); std::vector<std::string> corrections = {"material_correction"}; // std::vector<std::string> axes{ "X", "Y", "Z" }; std::vector<std::string> axes{ "XY", "Z" }; for (auto& opt_key : corrections) { - auto line = Line{ _(m_config->def()->get(opt_key)->full_label), "" }; + auto line = Line{ m_config->def()->get(opt_key)->full_label, "" }; int id = 0; for (auto& axis : axes) { auto opt = optgroup->get_option(opt_key, id); @@ -3598,18 +3989,18 @@ void TabSLAMaterial::build() optgroup->append_line(line); } - page = add_options_page(_(L("Notes")), "note.png"); - optgroup = page->new_optgroup(_(L("Notes")), 0); + page = add_options_page(L("Notes"), "note.png"); + optgroup = page->new_optgroup(L("Notes"), 0); optgroup->label_width = 0; Option option = optgroup->get_option("material_notes"); option.opt.full_width = true; option.opt.height = 25;//250; optgroup->append_single_option_line(option); - page = add_options_page(_(L("Dependencies")), "wrench.png"); - optgroup = page->new_optgroup(_(L("Profile dependencies"))); + page = add_options_page(L("Dependencies"), "wrench.png"); + optgroup = page->new_optgroup(L("Profile dependencies")); - create_line_with_widget(optgroup.get(), "compatible_printers", [this](wxWindow* parent) { + create_line_with_widget(optgroup.get(), "compatible_printers", wxEmptyString, [this](wxWindow* parent) { return compatible_widget_create(parent, m_compatible_printers); }); @@ -3617,7 +4008,7 @@ void TabSLAMaterial::build() option.opt.full_width = true; optgroup->append_single_option_line(option); - create_line_with_widget(optgroup.get(), "compatible_prints", [this](wxWindow* parent) { + create_line_with_widget(optgroup.get(), "compatible_prints", wxEmptyString, [this](wxWindow* parent) { return compatible_widget_create(parent, m_compatible_prints); }); @@ -3655,23 +4046,24 @@ void TabSLAPrint::build() m_presets = &m_preset_bundle->sla_prints; load_initial_data(); - auto page = add_options_page(_(L("Layers and perimeters")), "layers"); + auto page = add_options_page(L("Layers and perimeters"), "layers"); - auto optgroup = page->new_optgroup(_(L("Layers"))); + auto optgroup = page->new_optgroup(L("Layers")); optgroup->append_single_option_line("layer_height"); optgroup->append_single_option_line("faded_layers"); - page = add_options_page(_(L("Supports")), "support"/*"sla_supports"*/); - optgroup = page->new_optgroup(_(L("Supports"))); + page = add_options_page(L("Supports"), "support"/*"sla_supports"*/); + optgroup = page->new_optgroup(L("Supports")); optgroup->append_single_option_line("supports_enable"); - optgroup = page->new_optgroup(_(L("Support head"))); + optgroup = page->new_optgroup(L("Support head")); optgroup->append_single_option_line("support_head_front_diameter"); optgroup->append_single_option_line("support_head_penetration"); optgroup->append_single_option_line("support_head_width"); - optgroup = page->new_optgroup(_(L("Support pillar"))); + optgroup = page->new_optgroup(L("Support pillar")); optgroup->append_single_option_line("support_pillar_diameter"); + optgroup->append_single_option_line("support_small_pillar_diameter_percent"); optgroup->append_single_option_line("support_max_bridges_on_pillar"); optgroup->append_single_option_line("support_pillar_connection_mode"); @@ -3683,20 +4075,26 @@ void TabSLAPrint::build() optgroup->append_single_option_line("support_base_safety_distance"); // Mirrored parameter from Pad page for toggling elevation on the same page - optgroup->append_single_option_line("pad_around_object"); optgroup->append_single_option_line("support_object_elevation"); - optgroup = page->new_optgroup(_(L("Connection of the support sticks and junctions"))); + Line line{ "", "" }; + line.full_width = 1; + line.widget = [this](wxWindow* parent) { + return description_line_widget(parent, &m_support_object_elevation_description_line); + }; + optgroup->append_line(line); + + optgroup = page->new_optgroup(L("Connection of the support sticks and junctions")); optgroup->append_single_option_line("support_critical_angle"); optgroup->append_single_option_line("support_max_bridge_length"); optgroup->append_single_option_line("support_max_pillar_link_distance"); - optgroup = page->new_optgroup(_(L("Automatic generation"))); + optgroup = page->new_optgroup(L("Automatic generation")); optgroup->append_single_option_line("support_points_density_relative"); optgroup->append_single_option_line("support_points_minimal_distance"); - page = add_options_page(_(L("Pad")), "pad"); - optgroup = page->new_optgroup(_(L("Pad"))); + page = add_options_page(L("Pad"), "pad"); + optgroup = page->new_optgroup(L("Pad")); optgroup->append_single_option_line("pad_enable"); optgroup->append_single_option_line("pad_wall_thickness"); optgroup->append_single_option_line("pad_wall_height"); @@ -3713,27 +4111,27 @@ void TabSLAPrint::build() optgroup->append_single_option_line("pad_object_connector_width"); optgroup->append_single_option_line("pad_object_connector_penetration"); - page = add_options_page(_(L("Hollowing")), "hollowing"); - optgroup = page->new_optgroup(_(L("Hollowing"))); + page = add_options_page(L("Hollowing"), "hollowing"); + optgroup = page->new_optgroup(L("Hollowing")); optgroup->append_single_option_line("hollowing_enable"); optgroup->append_single_option_line("hollowing_min_thickness"); optgroup->append_single_option_line("hollowing_quality"); optgroup->append_single_option_line("hollowing_closing_distance"); - page = add_options_page(_(L("Advanced")), "wrench"); - optgroup = page->new_optgroup(_(L("Slicing"))); + page = add_options_page(L("Advanced"), "wrench"); + optgroup = page->new_optgroup(L("Slicing")); optgroup->append_single_option_line("slice_closing_radius"); - page = add_options_page(_(L("Output options")), "output+page_white"); - optgroup = page->new_optgroup(_(L("Output file"))); + page = add_options_page(L("Output options"), "output+page_white"); + optgroup = page->new_optgroup(L("Output file")); Option option = optgroup->get_option("output_filename_format"); option.opt.full_width = true; optgroup->append_single_option_line(option); - page = add_options_page(_(L("Dependencies")), "wrench"); - optgroup = page->new_optgroup(_(L("Profile dependencies"))); + page = add_options_page(L("Dependencies"), "wrench"); + optgroup = page->new_optgroup(L("Profile dependencies")); - create_line_with_widget(optgroup.get(), "compatible_printers", [this](wxWindow* parent) { + create_line_with_widget(optgroup.get(), "compatible_printers", wxEmptyString, [this](wxWindow* parent) { return compatible_widget_create(parent, m_compatible_printers); }); @@ -3751,6 +4149,34 @@ void TabSLAPrint::reload_config() Tab::reload_config(); } +void TabSLAPrint::update_description_lines() +{ + Tab::update_description_lines(); + + if (m_active_page && m_active_page->title() == "Supports") + { + bool is_visible = m_config->def()->get("support_object_elevation")->mode <= m_mode; + if (m_support_object_elevation_description_line) + { + m_support_object_elevation_description_line->Show(is_visible); + if (is_visible) + { + bool elev = !m_config->opt_bool("pad_enable") || !m_config->opt_bool("pad_around_object"); + m_support_object_elevation_description_line->SetText(elev ? "" : + from_u8((boost::format(_u8L("\"%1%\" is disabled because \"%2%\" is on in \"%3%\" category.\n" + "To enable \"%1%\", please switch off \"%2%\"")) + % _L("Object elevation") % _L("Pad around object") % _L("Pad")).str())); + } + } + } +} + +void TabSLAPrint::toggle_options() +{ + if (m_active_page) + m_config_manipulation.toggle_print_sla_options(m_config); +} + void TabSLAPrint::update() { if (m_preset_bundle->printers.get_selected_preset().printer_technology() == ptFFF) @@ -3759,10 +4185,14 @@ void TabSLAPrint::update() m_update_cnt++; m_config_manipulation.update_print_sla_config(m_config, true); + + update_description_lines(); + Layout(); + m_update_cnt--; if (m_update_cnt == 0) { - m_config_manipulation.toggle_print_sla_options(m_config); + toggle_options(); // update() could be called during undo/redo execution // Update of objectList can cause a crash in this case (because m_objects doesn't match ObjectList) @@ -3773,6 +4203,13 @@ void TabSLAPrint::update() } } +void TabSLAPrint::clear_pages() +{ + Tab::clear_pages(); + + m_support_object_elevation_description_line = nullptr; +} + ConfigManipulation Tab::get_config_manipulation() { auto load_config = [this]() @@ -3783,15 +4220,15 @@ ConfigManipulation Tab::get_config_manipulation() update(); }; - auto get_field_ = [this](const t_config_option_key& opt_key, int opt_index) { - return get_field(opt_key, opt_index); + auto cb_toggle_field = [this](const t_config_option_key& opt_key, bool toggle, int opt_index) { + return toggle_option(opt_key, toggle, opt_index); }; auto cb_value_change = [this](const std::string& opt_key, const boost::any& value) { return on_value_change(opt_key, value); }; - return ConfigManipulation(load_config, get_field_, cb_value_change); + return ConfigManipulation(load_config, cb_toggle_field, cb_value_change); } |