From 43e6e4f18c89257d2df547253fda600ed093d324 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 16 Jun 2020 12:57:49 +0200 Subject: Code refactoring: - PresetCombpBoxes are extracted to the separate file. - All preset icons are moved to the PresetComboBox from Preset and PresetBundle - First "steps" to add physical printers to the printers list on the sidebar. --- src/slic3r/CMakeLists.txt | 2 + src/slic3r/GUI/GUI_ObjectList.cpp | 3 - src/slic3r/GUI/MainFrame.cpp | 12 - src/slic3r/GUI/Plater.cpp | 212 ++-------- src/slic3r/GUI/Plater.hpp | 41 +- src/slic3r/GUI/Preset.cpp | 410 +++++-------------- src/slic3r/GUI/Preset.hpp | 228 ++++++++--- src/slic3r/GUI/PresetBundle.cpp | 270 +----------- src/slic3r/GUI/PresetBundle.hpp | 22 - src/slic3r/GUI/PresetComboBoxes.cpp | 794 ++++++++++++++++++++++++++++++++++++ src/slic3r/GUI/PresetComboBoxes.hpp | 179 ++++++++ src/slic3r/GUI/Tab.cpp | 46 +-- src/slic3r/GUI/Tab.hpp | 7 +- src/slic3r/GUI/wxExtensions.cpp | 88 ---- src/slic3r/GUI/wxExtensions.hpp | 31 -- 15 files changed, 1304 insertions(+), 1041 deletions(-) create mode 100644 src/slic3r/GUI/PresetComboBoxes.cpp create mode 100644 src/slic3r/GUI/PresetComboBoxes.hpp diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 7e02c0fdd..98389e7da 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -80,6 +80,8 @@ set(SLIC3R_GUI_SOURCES GUI/MainFrame.cpp GUI/MainFrame.hpp GUI/Plater.cpp + GUI/PresetComboBoxes.hpp + GUI/PresetComboBoxes.cpp GUI/Plater.hpp GUI/GUI_ObjectList.cpp GUI/GUI_ObjectList.hpp diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index c11ed66ab..2f201180a 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -88,9 +88,6 @@ ObjectList::ObjectList(wxWindow* parent) : { // Fill CATEGORY_ICON { - // Note: `this` isn't passed to create_scaled_bitmap() here because of bugs in the widget, - // see note in PresetBundle::load_compatible_bitmaps() - // ptFFF CATEGORY_ICON[L("Layers and Perimeters")] = create_scaled_bitmap("layers"); CATEGORY_ICON[L("Infill")] = create_scaled_bitmap("infill"); diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index ef837c200..d4ce21fc0 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -74,11 +74,6 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S SLIC3R_VERSION + _(L(" - Remember to check for updates at http://github.com/prusa3d/PrusaSlicer/releases"))); - /* Load default preset bitmaps before a tabpanel initialization, - * but after filling of an em_unit value - */ - wxGetApp().preset_bundle->load_default_preset_bitmaps(); - // initialize tabpanel and menubar init_tabpanel(); init_menubar(); @@ -540,11 +535,6 @@ void MainFrame::on_dpi_changed(const wxRect &suggested_rect) wxGetApp().update_fonts(); this->SetFont(this->normal_font()); - /* Load default preset bitmaps before a tabpanel initialization, - * but after filling of an em_unit value - */ - wxGetApp().preset_bundle->load_default_preset_bitmaps(); - // update Plater wxGetApp().plater()->msw_rescale(); @@ -586,8 +576,6 @@ void MainFrame::on_sys_color_changed() // update label colors in respect to the system mode wxGetApp().init_label_colours(); - wxGetApp().preset_bundle->load_default_preset_bitmaps(); - // update Plater wxGetApp().plater()->sys_color_changed(); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 8c3a90370..339badc96 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include @@ -77,6 +76,7 @@ #include "../Utils/UndoRedo.hpp" #include "RemovableDriveManager.hpp" #include "InstanceCheck.hpp" +#include "PresetComboBoxes.hpp" #ifdef __APPLE__ #include "Gizmos/GLGizmosManager.hpp" @@ -253,153 +253,6 @@ void SlicedInfo::SetTextAndShow(SlicedInfoIdx idx, const wxString& text, const w info_vec[idx].second->Show(show); } -PresetComboBox::PresetComboBox(wxWindow *parent, Preset::Type preset_type) : -PresetBitmapComboBox(parent, wxSize(15 * wxGetApp().em_unit(), -1)), - preset_type(preset_type), - last_selected(wxNOT_FOUND), - m_em_unit(wxGetApp().em_unit()) -{ - SetFont(wxGetApp().normal_font()); -#ifdef _WIN32 - // Workaround for ignoring CBN_EDITCHANGE events, which are processed after the content of the combo box changes, so that - // the index of the item inside CBN_EDITCHANGE may no more be valid. - EnableTextChangedEvents(false); -#endif /* _WIN32 */ - Bind(wxEVT_COMBOBOX, [this](wxCommandEvent &evt) { - auto selected_item = evt.GetSelection(); - - auto marker = reinterpret_cast(this->GetClientData(selected_item)); - if (marker >= LABEL_ITEM_MARKER && marker < LABEL_ITEM_MAX) { - this->SetSelection(this->last_selected); - evt.StopPropagation(); - if (marker >= LABEL_ITEM_WIZARD_PRINTERS) { - ConfigWizard::StartPage sp = ConfigWizard::SP_WELCOME; - switch (marker) { - case LABEL_ITEM_WIZARD_PRINTERS: sp = ConfigWizard::SP_PRINTERS; break; - case LABEL_ITEM_WIZARD_FILAMENTS: sp = ConfigWizard::SP_FILAMENTS; break; - case LABEL_ITEM_WIZARD_MATERIALS: sp = ConfigWizard::SP_MATERIALS; break; - } - wxTheApp->CallAfter([sp]() { wxGetApp().run_wizard(ConfigWizard::RR_USER, sp); }); - } - } else if ( this->last_selected != selected_item || - wxGetApp().get_tab(this->preset_type)->get_presets()->current_is_dirty() ) { - this->last_selected = selected_item; - evt.SetInt(this->preset_type); - evt.Skip(); - } else { - evt.StopPropagation(); - } - }); - - if (preset_type == Slic3r::Preset::TYPE_FILAMENT) - { - Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent &event) { - PresetBundle* preset_bundle = wxGetApp().preset_bundle; - const Preset* selected_preset = preset_bundle->filaments.find_preset(preset_bundle->filament_presets[extruder_idx]); - // Wide icons are shown if the currently selected preset is not compatible with the current printer, - // and red flag is drown in front of the selected preset. - bool wide_icons = selected_preset != nullptr && !selected_preset->is_compatible; - float scale = m_em_unit*0.1f; - - int shifl_Left = wide_icons ? int(scale * 16 + 0.5) : 0; -#if defined(wxBITMAPCOMBOBOX_OWNERDRAWN_BASED) - shifl_Left += int(scale * 4 + 0.5f); // IMAGE_SPACING_RIGHT = 4 for wxBitmapComboBox -> Space left of image -#endif - int icon_right_pos = shifl_Left + int(scale * (24+4) + 0.5); - int mouse_pos = event.GetLogicalPosition(wxClientDC(this)).x; - if (mouse_pos < shifl_Left || mouse_pos > icon_right_pos ) { - // Let the combo box process the mouse click. - event.Skip(); - return; - } - - // Swallow the mouse click and open the color picker. - - // get current color - DynamicPrintConfig* cfg = wxGetApp().get_tab(Preset::TYPE_PRINTER)->get_config(); - auto colors = static_cast(cfg->option("extruder_colour")->clone()); - wxColour clr(colors->values[extruder_idx]); - if (!clr.IsOk()) - clr = wxColour(0,0,0); // Don't set alfa to transparence - - auto data = new wxColourData(); - data->SetChooseFull(1); - data->SetColour(clr); - - wxColourDialog dialog(this, data); - dialog.CenterOnParent(); - if (dialog.ShowModal() == wxID_OK) - { - colors->values[extruder_idx] = dialog.GetColourData().GetColour().GetAsString(wxC2S_HTML_SYNTAX).ToStdString(); - - DynamicPrintConfig cfg_new = *cfg; - cfg_new.set_key_value("extruder_colour", colors); - - wxGetApp().get_tab(Preset::TYPE_PRINTER)->load_config(cfg_new); - preset_bundle->update_plater_filament_ui(extruder_idx, this); - wxGetApp().plater()->on_config_change(cfg_new); - } - }); - } - - edit_btn = new ScalableButton(parent, wxID_ANY, "cog"); - edit_btn->SetToolTip(_L("Click to edit preset")); - - edit_btn->Bind(wxEVT_BUTTON, ([preset_type, this](wxCommandEvent) - { - Tab* tab = wxGetApp().get_tab(preset_type); - if (!tab) - return; - - int page_id = wxGetApp().tab_panel()->FindPage(tab); - if (page_id == wxNOT_FOUND) - return; - - wxGetApp().tab_panel()->SetSelection(page_id); - - // Switch to Settings NotePad - wxGetApp().mainframe->select_tab(); - - /* In a case of a multi-material printing, for editing another Filament Preset - * it's needed to select this preset for the "Filament settings" Tab - */ - if (preset_type == Preset::TYPE_FILAMENT && wxGetApp().extruders_edited_cnt() > 1) - { - const std::string& selected_preset = GetString(GetSelection()).ToUTF8().data(); - - // Call select_preset() only if there is new preset and not just modified - if ( !boost::algorithm::ends_with(selected_preset, Preset::suffix_modified()) ) - { - const std::string& preset_name = wxGetApp().preset_bundle->filaments.get_preset_name_by_alias(selected_preset); - tab->select_preset(/*selected_preset*/preset_name); - } - } - })); -} - -PresetComboBox::~PresetComboBox() -{ - if (edit_btn) - edit_btn->Destroy(); -} - - -void PresetComboBox::set_label_marker(int item, LabelItemType label_item_type) -{ - this->SetClientData(item, (void*)label_item_type); -} - -void PresetComboBox::check_selection(int selection) -{ - this->last_selected = selection; -} - -void PresetComboBox::msw_rescale() -{ - m_em_unit = wxGetApp().em_unit(); - edit_btn->msw_rescale(); -} - // Frequently changed parameters class FreqChangedParams : public OG_Settings @@ -697,12 +550,12 @@ struct Sidebar::priv ModeSizer *mode_sizer; wxFlexGridSizer *sizer_presets; - PresetComboBox *combo_print; - std::vector combos_filament; + PlaterPresetComboBox *combo_print; + std::vector combos_filament; wxBoxSizer *sizer_filaments; - PresetComboBox *combo_sla_print; - PresetComboBox *combo_sla_material; - PresetComboBox *combo_printer; + PlaterPresetComboBox *combo_sla_print; + PlaterPresetComboBox *combo_sla_material; + PlaterPresetComboBox *combo_printer; wxBoxSizer *sizer_params; FreqChangedParams *frequently_changed_parameters{ nullptr }; @@ -801,10 +654,10 @@ Sidebar::Sidebar(Plater *parent) p->sizer_filaments = new wxBoxSizer(wxVERTICAL); - auto init_combo = [this](PresetComboBox **combo, wxString label, Preset::Type preset_type, bool filament) { + auto init_combo = [this](PlaterPresetComboBox **combo, wxString label, Preset::Type preset_type, bool filament) { auto *text = new wxStaticText(p->presets_panel, wxID_ANY, label + " :"); text->SetFont(wxGetApp().small_font()); - *combo = new PresetComboBox(p->presets_panel, preset_type); + *combo = new PlaterPresetComboBox(p->presets_panel, preset_type); auto combo_and_btn_sizer = new wxBoxSizer(wxHORIZONTAL); combo_and_btn_sizer->Add(*combo, 1, wxEXPAND); @@ -941,8 +794,8 @@ Sidebar::Sidebar(Plater *parent) Sidebar::~Sidebar() {} -void Sidebar::init_filament_combo(PresetComboBox **combo, const int extr_idx) { - *combo = new PresetComboBox(p->presets_panel, Slic3r::Preset::TYPE_FILAMENT); +void Sidebar::init_filament_combo(PlaterPresetComboBox **combo, const int extr_idx) { + *combo = new PlaterPresetComboBox(p->presets_panel, Slic3r::Preset::TYPE_FILAMENT); // # copy icons from first choice // $choice->SetItemBitmap($_, $choices->[0]->GetItemBitmap($_)) for 0..$#presets; @@ -977,18 +830,18 @@ void Sidebar::update_all_preset_comboboxes() // Update the print choosers to only contain the compatible presets, update the dirty flags. if (print_tech == ptFFF) - preset_bundle.prints.update_plater_ui(p->combo_print); + p->combo_print->update(); else { - preset_bundle.sla_prints.update_plater_ui(p->combo_sla_print); - preset_bundle.sla_materials.update_plater_ui(p->combo_sla_material); + p->combo_sla_print->update(); + p->combo_sla_material->update(); } // Update the printer choosers, update the dirty flags. - preset_bundle.printers.update_plater_ui(p->combo_printer); + p->combo_printer->update(); // Update the filament choosers to only contain the compatible presets, update the color preview, // update the dirty flags. if (print_tech == ptFFF) { - for (size_t i = 0; i < p->combos_filament.size(); ++i) - preset_bundle.update_plater_filament_ui(i, p->combos_filament[i]); + for (PlaterPresetComboBox* cb : p->combos_filament) + cb->update(); } } @@ -1010,23 +863,22 @@ void Sidebar::update_presets(Preset::Type preset_type) preset_bundle.set_filament_preset(0, name); } - for (size_t i = 0; i < filament_cnt; i++) { - preset_bundle.update_plater_filament_ui(i, p->combos_filament[i]); - } + for (size_t i = 0; i < filament_cnt; i++) + p->combos_filament[i]->update(); break; } case Preset::TYPE_PRINT: - preset_bundle.prints.update_plater_ui(p->combo_print); + p->combo_print->update(); break; case Preset::TYPE_SLA_PRINT: - preset_bundle.sla_prints.update_plater_ui(p->combo_sla_print); + p->combo_sla_print->update(); break; case Preset::TYPE_SLA_MATERIAL: - preset_bundle.sla_materials.update_plater_ui(p->combo_sla_material); + p->combo_sla_material->update(); break; case Preset::TYPE_PRINTER: @@ -1062,18 +914,14 @@ void Sidebar::msw_rescale() p->mode_sizer->msw_rescale(); - // Rescale preset comboboxes in respect to the current em_unit ... - for (PresetComboBox* combo : std::vector { p->combo_print, + for (PlaterPresetComboBox* combo : std::vector { p->combo_print, p->combo_sla_print, p->combo_sla_material, p->combo_printer } ) combo->msw_rescale(); - for (PresetComboBox* combo : p->combos_filament) + for (PlaterPresetComboBox* combo : p->combos_filament) combo->msw_rescale(); - // ... then refill them and set min size to correct layout of the sidebar - update_all_preset_comboboxes(); - p->frequently_changed_parameters->msw_rescale(); p->object_list->msw_rescale(); p->object_manipulation->msw_rescale(); @@ -1096,12 +944,12 @@ void Sidebar::sys_color_changed() { // Update preset comboboxes in respect to the system color ... // combo->msw_rescale() updates icon on button, so use it - for (PresetComboBox* combo : std::vector{ p->combo_print, + for (PlaterPresetComboBox* combo : std::vector{ p->combo_print, p->combo_sla_print, p->combo_sla_material, p->combo_printer }) combo->msw_rescale(); - for (PresetComboBox* combo : p->combos_filament) + for (PlaterPresetComboBox* combo : p->combos_filament) combo->msw_rescale(); // ... then refill them and set min size to correct layout of the sidebar @@ -1458,7 +1306,7 @@ void Sidebar::update_ui_from_settings() update_sliced_info_sizer(); } -std::vector& Sidebar::combos_filament() +std::vector& Sidebar::combos_filament() { return p->combos_filament; } @@ -3339,7 +3187,7 @@ void Plater::priv::set_current_panel(wxPanel* panel) void Plater::priv::on_select_preset(wxCommandEvent &evt) { auto preset_type = static_cast(evt.GetInt()); - auto *combo = static_cast(evt.GetEventObject()); + auto *combo = static_cast(evt.GetEventObject()); // see https://github.com/prusa3d/PrusaSlicer/issues/3889 // Under OSX: in case of use of a same names written in different case (like "ENDER" and "Ender"), @@ -3368,7 +3216,7 @@ void Plater::priv::on_select_preset(wxCommandEvent &evt) // TODO: ? if (preset_type == Preset::TYPE_FILAMENT && sidebar->is_multifilament()) { // Only update the plater UI for the 2nd and other filaments. - wxGetApp().preset_bundle->update_plater_filament_ui(idx, combo); + combo->update(); } else { wxWindowUpdateLocker noUpdates(sidebar->presets_panel()); @@ -5154,12 +5002,12 @@ void Plater::on_extruders_change(size_t num_extruders) size_t i = choices.size(); while ( i < num_extruders ) { - PresetComboBox* choice/*{ nullptr }*/; + PlaterPresetComboBox* choice/*{ nullptr }*/; sidebar().init_filament_combo(&choice, i); choices.push_back(choice); // initialize selection - wxGetApp().preset_bundle->update_plater_filament_ui(i, choice); + choice->update(); ++i; } diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 5d60e006b..d2acc7632 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -6,14 +6,12 @@ #include #include -#include #include "Preset.hpp" #include "Selection.hpp" #include "libslic3r/BoundingBox.hpp" #include "Jobs/Job.hpp" -#include "wxExtensions.hpp" #include "Search.hpp" class wxButton; @@ -50,46 +48,13 @@ class Mouse3DController; struct Camera; class Bed3D; class GLToolbar; +class PlaterPresetComboBox; using t_optgroups = std::vector >; class Plater; enum class ActionButtonType : int; -class PresetComboBox : public PresetBitmapComboBox -{ -public: - PresetComboBox(wxWindow *parent, Preset::Type preset_type); - ~PresetComboBox(); - - ScalableButton* edit_btn { nullptr }; - - enum LabelItemType { - LABEL_ITEM_MARKER = 0xffffff01, - LABEL_ITEM_WIZARD_PRINTERS, - LABEL_ITEM_WIZARD_FILAMENTS, - LABEL_ITEM_WIZARD_MATERIALS, - - LABEL_ITEM_MAX, - }; - - void set_label_marker(int item, LabelItemType label_item_type = LABEL_ITEM_MARKER); - void set_extruder_idx(const int extr_idx) { extruder_idx = extr_idx; } - int get_extruder_idx() const { return extruder_idx; } - int em_unit() const { return m_em_unit; } - void check_selection(int selection); - - void msw_rescale(); - -private: - typedef std::size_t Marker; - - Preset::Type preset_type; - int last_selected; - int extruder_idx = -1; - int m_em_unit; -}; - class Sidebar : public wxPanel { ConfigOptionMode m_mode; @@ -101,7 +66,7 @@ public: Sidebar &operator=(const Sidebar &) = delete; ~Sidebar(); - void init_filament_combo(PresetComboBox **combo, const int extr_idx); + void init_filament_combo(PlaterPresetComboBox **combo, const int extr_idx); void remove_unused_filament_combos(const size_t current_extruder_count); void update_all_preset_comboboxes(); void update_presets(Slic3r::Preset::Type preset_type); @@ -139,7 +104,7 @@ public: void update_searcher(); void update_ui_from_settings(); - std::vector& combos_filament(); + std::vector& combos_filament(); Search::OptionsSearcher& get_searcher(); std::string& get_search_line(); diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp index d810c399d..883dc438a 100644 --- a/src/slic3r/GUI/Preset.cpp +++ b/src/slic3r/GUI/Preset.cpp @@ -2,9 +2,7 @@ #include "Preset.hpp" #include "AppConfig.hpp" -#include "BitmapCache.hpp" #include "I18N.hpp" -#include "wxExtensions.hpp" #ifdef _MSC_VER #define WIN32_LEAN_AND_MEAN @@ -30,15 +28,9 @@ #include #include -#include -#include -#include -#include - #include "libslic3r/libslic3r.h" #include "libslic3r/Utils.hpp" #include "libslic3r/PlaceholderParser.hpp" -#include "Plater.hpp" using boost::property_tree::ptree; @@ -590,10 +582,7 @@ const std::vector& Preset::sla_printer_options() PresetCollection::PresetCollection(Preset::Type type, const std::vector &keys, const Slic3r::StaticPrintConfig &defaults, const std::string &default_name) : m_type(type), m_edited_preset(type, "", false), - m_idx_selected(0), - m_bitmap_main_frame(new wxBitmap), - m_bitmap_add(new wxBitmap), - m_bitmap_cache(new GUI::BitmapCache) + m_idx_selected(0) { // Insert just the default preset. this->add_default_preset(keys, defaults, default_name); @@ -602,12 +591,6 @@ PresetCollection::PresetCollection(Preset::Type type, const std::vectorget_selected_idx() == size_t(-1)) @@ -1119,279 +1092,15 @@ size_t PresetCollection::update_compatible_internal(const PresetWithVendorProfil // Delete the current preset, activate the first visible preset. //void PresetCollection::delete_current_preset(); -// Update the wxChoice UI component from this list of presets. -// Hide the -void PresetCollection::update_plater_ui(GUI::PresetComboBox *ui) -{ - if (ui == nullptr) - return; - - // Otherwise fill in the list from scratch. - ui->Freeze(); - ui->Clear(); - size_t selected_preset_item = INT_MAX; // some value meaning that no one item is selected - - const Preset &selected_preset = this->get_selected_preset(); - // Show wide icons if the currently selected preset is not compatible with the current printer, - // and draw a red flag in front of the selected preset. - bool wide_icons = ! selected_preset.is_compatible && m_bitmap_incompatible != nullptr; - - /* It's supposed that standard size of an icon is 16px*16px for 100% scaled display. - * So set sizes for solid_colored icons used for filament preset - * and scale them in respect to em_unit value - */ - const float scale_f = ui->em_unit() * 0.1f; - const int icon_height = 16 * scale_f + 0.5f; - const int icon_width = 16 * scale_f + 0.5f; - const int thin_space_icon_width = 4 * scale_f + 0.5f; - const int wide_space_icon_width = 6 * scale_f + 0.5f; - - std::map nonsys_presets; - wxString selected = ""; - wxString tooltip = ""; - if (!this->m_presets.front().is_visible) - ui->set_label_marker(ui->Append(PresetCollection::separator(L("System presets")), wxNullBitmap)); - for (size_t i = this->m_presets.front().is_visible ? 0 : m_num_default_presets; i < this->m_presets.size(); ++ i) { - const Preset &preset = this->m_presets[i]; - if (! preset.is_visible || (! preset.is_compatible && i != m_idx_selected)) - continue; - - std::string bitmap_key = ""; - // !!! Temporary solution, till refactoring: create and use "sla_printer" icon instead of m_bitmap_main_frame - wxBitmap main_bmp = m_bitmap_main_frame ? *m_bitmap_main_frame : wxNullBitmap; - if (m_type == Preset::TYPE_PRINTER && preset.printer_technology()==ptSLA ) { - bitmap_key = "sla_printer"; - main_bmp = create_scaled_bitmap("sla_printer"); - } - - // If the filament preset is not compatible and there is a "red flag" icon loaded, show it left - // to the filament color image. - if (wide_icons) - bitmap_key += preset.is_compatible ? ",cmpt" : ",ncmpt"; - bitmap_key += (preset.is_system || preset.is_default) ? ",syst" : ",nsyst"; - wxBitmap *bmp = m_bitmap_cache->find(bitmap_key); - if (bmp == nullptr) { - // Create the bitmap with color bars. - std::vector bmps; - if (wide_icons) - // Paint a red flag for incompatible presets. - bmps.emplace_back(preset.is_compatible ? m_bitmap_cache->mkclear(icon_width, icon_height) : *m_bitmap_incompatible); - // Paint the color bars. - bmps.emplace_back(m_bitmap_cache->mkclear(thin_space_icon_width, icon_height)); - bmps.emplace_back(main_bmp); - // Paint a lock at the system presets. - bmps.emplace_back(m_bitmap_cache->mkclear(wide_space_icon_width, icon_height)); - bmps.emplace_back((preset.is_system || preset.is_default) ? *m_bitmap_lock : m_bitmap_cache->mkclear(icon_width, icon_height)); - bmp = m_bitmap_cache->insert(bitmap_key, bmps); - } - - const std::string name = preset.alias.empty() ? preset.name : preset.alias; - if (preset.is_default || preset.is_system) { - ui->Append(wxString::FromUTF8((/*preset.*/name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), - (bmp == 0) ? main_bmp : *bmp); - if (i == m_idx_selected || - // just in case: mark selected_preset_item as a first added element - selected_preset_item == INT_MAX) { - selected_preset_item = ui->GetCount() - 1; - tooltip = wxString::FromUTF8(preset.name.c_str()); - } - } - else - { - nonsys_presets.emplace(wxString::FromUTF8((/*preset.*/name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), bmp/*preset.is_compatible*/); - if (i == m_idx_selected) { - selected = wxString::FromUTF8((/*preset.*/name + (preset.is_dirty ? g_suffix_modified : "")).c_str()); - tooltip = wxString::FromUTF8(preset.name.c_str()); - } - } - if (i + 1 == m_num_default_presets) - ui->set_label_marker(ui->Append(PresetCollection::separator(L("System presets")), wxNullBitmap)); - } - if (!nonsys_presets.empty()) - { - ui->set_label_marker(ui->Append(PresetCollection::separator(L("User presets")), wxNullBitmap)); - for (std::map::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) { - ui->Append(it->first, *it->second); - if (it->first == selected || - // just in case: mark selected_preset_item as a first added element - selected_preset_item == INT_MAX) - selected_preset_item = ui->GetCount() - 1; - } - } - if (m_type == Preset::TYPE_PRINTER || m_type == Preset::TYPE_SLA_MATERIAL) { - std::string bitmap_key = ""; - // If the filament preset is not compatible and there is a "red flag" icon loaded, show it left - // to the filament color image. - if (wide_icons) - bitmap_key += "wide,"; - bitmap_key += "edit_preset_list"; - wxBitmap *bmp = m_bitmap_cache->find(bitmap_key); - if (bmp == nullptr) { - // Create the bitmap with color bars. - std::vector bmps; - if (wide_icons) - // Paint a red flag for incompatible presets. - bmps.emplace_back(m_bitmap_cache->mkclear(icon_width, icon_height)); - // Paint the color bars. - bmps.emplace_back(m_bitmap_cache->mkclear(thin_space_icon_width, icon_height)); - bmps.emplace_back(*m_bitmap_main_frame); - // Paint a lock at the system presets. - bmps.emplace_back(m_bitmap_cache->mkclear(wide_space_icon_width, icon_height)); -// bmps.emplace_back(m_bitmap_add ? *m_bitmap_add : wxNullBitmap); - bmps.emplace_back(create_scaled_bitmap("edit_uni")); - bmp = m_bitmap_cache->insert(bitmap_key, bmps); - } - if (m_type == Preset::TYPE_SLA_MATERIAL) - ui->set_label_marker(ui->Append(PresetCollection::separator(L("Add/Remove materials")), *bmp), GUI::PresetComboBox::LABEL_ITEM_WIZARD_MATERIALS); - else - ui->set_label_marker(ui->Append(PresetCollection::separator(L("Add/Remove printers")), *bmp), GUI::PresetComboBox::LABEL_ITEM_WIZARD_PRINTERS); - } - - /* But, if selected_preset_item is still equal to INT_MAX, it means that - * there is no presets added to the list. - * So, select last combobox item ("Add/Remove preset") - */ - if (selected_preset_item == INT_MAX) - selected_preset_item = ui->GetCount() - 1; - - ui->SetSelection(selected_preset_item); - ui->SetToolTip(tooltip.IsEmpty() ? ui->GetString(selected_preset_item) : tooltip); - ui->check_selection(selected_preset_item); - ui->Thaw(); - - // Update control min size after rescale (changed Display DPI under MSW) - if (ui->GetMinWidth() != 20 * ui->em_unit()) - ui->SetMinSize(wxSize(20 * ui->em_unit(), ui->GetSize().GetHeight())); -} - -size_t PresetCollection::update_tab_ui(wxBitmapComboBox *ui, bool show_incompatible, const int em/* = 10*/) -{ - if (ui == nullptr) - return 0; - ui->Freeze(); - ui->Clear(); - size_t selected_preset_item = INT_MAX; // some value meaning that no one item is selected - - /* It's supposed that standard size of an icon is 16px*16px for 100% scaled display. - * So set sizes for solid_colored(empty) icons used for preset - * and scale them in respect to em_unit value - */ - const float scale_f = em * 0.1f; - const int icon_height = 16 * scale_f + 0.5f; - const int icon_width = 16 * scale_f + 0.5f; - - std::map nonsys_presets; - wxString selected = ""; - if (!this->m_presets.front().is_visible) - ui->Append(PresetCollection::separator(L("System presets")), wxNullBitmap); - for (size_t i = this->m_presets.front().is_visible ? 0 : m_num_default_presets; i < this->m_presets.size(); ++i) { - const Preset &preset = this->m_presets[i]; - if (! preset.is_visible || (! show_incompatible && ! preset.is_compatible && i != m_idx_selected)) - continue; - std::string bitmap_key = "tab"; - - // !!! Temporary solution, till refactoring: create and use "sla_printer" icon instead of m_bitmap_main_frame - wxBitmap main_bmp = m_bitmap_main_frame ? *m_bitmap_main_frame : wxNullBitmap; - if (m_type == Preset::TYPE_PRINTER && preset.printer_technology() == ptSLA) { - bitmap_key = "sla_printer"; - main_bmp = create_scaled_bitmap("sla_printer"); - } - - bitmap_key += preset.is_compatible ? ",cmpt" : ",ncmpt"; - bitmap_key += (preset.is_system || preset.is_default) ? ",syst" : ",nsyst"; - wxBitmap *bmp = m_bitmap_cache->find(bitmap_key); - if (bmp == nullptr) { - // Create the bitmap with color bars. - std::vector bmps; - const wxBitmap* tmp_bmp = preset.is_compatible ? m_bitmap_compatible : m_bitmap_incompatible; - bmps.emplace_back((tmp_bmp == 0) ? main_bmp : *tmp_bmp); - // Paint a lock at the system presets. - bmps.emplace_back((preset.is_system || preset.is_default) ? *m_bitmap_lock : m_bitmap_cache->mkclear(icon_width, icon_height)); - bmp = m_bitmap_cache->insert(bitmap_key, bmps); - } - - if (preset.is_default || preset.is_system) { - ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), - (bmp == 0) ? main_bmp : *bmp); - if (i == m_idx_selected || - // just in case: mark selected_preset_item as a first added element - selected_preset_item == INT_MAX) - selected_preset_item = ui->GetCount() - 1; - } - else - { - nonsys_presets.emplace(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), bmp/*preset.is_compatible*/); - if (i == m_idx_selected) - selected = wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()); - } - if (i + 1 == m_num_default_presets) - ui->Append(PresetCollection::separator(L("System presets")), wxNullBitmap); - } - if (!nonsys_presets.empty()) - { - ui->Append(PresetCollection::separator(L("User presets")), wxNullBitmap); - for (std::map::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) { - ui->Append(it->first, *it->second); - if (it->first == selected || - // just in case: mark selected_preset_item as a first added element - selected_preset_item == INT_MAX) - selected_preset_item = ui->GetCount() - 1; - } - } - if (m_type == Preset::TYPE_PRINTER) { - wxBitmap *bmp = m_bitmap_cache->find("edit_printer_list"); - if (bmp == nullptr) { - // Create the bitmap with color bars. - std::vector bmps; - bmps.emplace_back(*m_bitmap_main_frame); -// bmps.emplace_back(m_bitmap_add ? *m_bitmap_add : wxNullBitmap); - bmps.emplace_back(create_scaled_bitmap("edit_uni")); - bmp = m_bitmap_cache->insert("add_printer_tab", bmps); - } - ui->Append(PresetCollection::separator("Add a new printer"), *bmp); - } - - /* But, if selected_preset_item is still equal to INT_MAX, it means that - * there is no presets added to the list. - * So, select last combobox item ("Add/Remove preset") - */ - if (selected_preset_item == INT_MAX) - selected_preset_item = ui->GetCount() - 1; - - ui->SetSelection(selected_preset_item); - ui->SetToolTip(ui->GetString(selected_preset_item)); - ui->Thaw(); - return selected_preset_item; -} - -// Update a dirty floag of the current preset, update the labels of the UI component accordingly. +// Update a dirty flag of the current preset // Return true if the dirty flag changed. -bool PresetCollection::update_dirty_ui(wxBitmapComboBox *ui) +bool PresetCollection::update_dirty() { - wxWindowUpdateLocker noUpdates(ui); - // 1) Update the dirty flag of the current preset. bool was_dirty = this->get_selected_preset().is_dirty; bool is_dirty = current_is_dirty(); this->get_selected_preset().is_dirty = is_dirty; this->get_edited_preset().is_dirty = is_dirty; - // 2) Update the labels. - for (unsigned int ui_id = 0; ui_id < ui->GetCount(); ++ ui_id) { - std::string old_label = ui->GetString(ui_id).utf8_str().data(); - std::string preset_name = Preset::remove_suffix_modified(old_label); - const Preset *preset = this->find_preset(preset_name, false); -// The old_label could be the "----- system presets ------" or the "------- user presets --------" separator. -// assert(preset != nullptr); - if (preset != nullptr) { - std::string new_label = preset->is_dirty ? preset->name + g_suffix_modified : preset->name; - if (old_label != new_label) - ui->SetString(ui_id, wxString::FromUTF8(new_label.c_str())); - } - } -#ifdef __APPLE__ - // wxWidgets on OSX do not upload the text of the combo box line automatically. - // Force it to update by re-selecting. - ui->SetSelection(ui->GetSelection()); -#endif /* __APPLE __ */ + return was_dirty != is_dirty; } @@ -1605,16 +1314,6 @@ std::string PresetCollection::path_from_name(const std::string &new_name) const return (boost::filesystem::path(m_dir_path) / file_name).make_preferred().string(); } -void PresetCollection::clear_bitmap_cache() -{ - m_bitmap_cache->clear(); -} - -wxString PresetCollection::separator(const std::string &label) -{ - return wxString::FromUTF8(PresetCollection::separator_head()) + _(label) + wxString::FromUTF8(PresetCollection::separator_tail()); -} - const Preset& PrinterPresetCollection::default_preset_for(const DynamicPrintConfig &config) const { const ConfigOptionEnumGeneric *opt_printer_technology = config.opt("printer_technology"); @@ -1631,7 +1330,108 @@ const Preset* PrinterPresetCollection::find_by_model_id(const std::string &model return it != cend() ? &*it : nullptr; } +/* +PhysicalPrinter& PhysicalPrinterCollection::load_external_printer( + // Path to the profile source file (a G-code, an AMF or 3MF file, a config file) + const std::string& path, + // Name of the profile, derived from the source file name. + const std::string& name, + // Original name of the profile, extracted from the loaded config. Empty, if the name has not been stored. + const std::string& original_name, + // Config to initialize the preset from. + const DynamicPrintConfig& config, + // Select the preset after loading? + bool select) +{ + // Load the preset over a default preset, so that the missing fields are filled in from the default preset. + DynamicPrintConfig cfg(this->default_printer().config); + cfg.apply_only(config, cfg.keys(), true); + // Is there a preset already loaded with the name stored inside the config? + std::deque::iterator it = this->find_printer_internal(original_name); + bool found = it != m_printers.end() && it->name == original_name; + if (!found) { + // Try to match the original_name against the "renamed_from" profile names of loaded system profiles. + / * + it = this->find_preset_renamed(original_name); + found = it != m_presets.end(); + * / + } + if (found) { + if (profile_print_params_same(it->config, cfg)) { + // The preset exists and it matches the values stored inside config. + if (select) + this->select_printer(it - m_printers.begin()); + return *it; + } + if (profile_host_params_same_or_anonymized(it->config, cfg) == ProfileHostParams::Anonymized) { + // The project being loaded is anonymized. Replace the empty host keys of the loaded profile with the data from the original profile. + // See "Octoprint Settings when Opening a .3MF file" GH issue #3244 + auto opt_update = [it, &cfg](const std::string& opt_key) { + auto opt = it->config.option(opt_key); + if (opt != nullptr) + cfg.set_key_value(opt_key, opt->clone()); + }; + opt_update("print_host"); + opt_update("printhost_apikey"); + opt_update("printhost_cafile"); + } + } + // The external preset does not match an internal preset, load the external preset. + std::string new_name; + for (size_t idx = 0;; ++idx) { + std::string suffix; + if (original_name.empty()) { + if (idx > 0) + suffix = " (" + std::to_string(idx) + ")"; + } + else { + if (idx == 0) + suffix = " (" + original_name + ")"; + else + suffix = " (" + original_name + "-" + std::to_string(idx) + ")"; + } + new_name = name + suffix; + it = this->find_printer_internal(new_name); + if (it == m_printers.end() || it->name != new_name) + // Unique profile name. Insert a new profile. + break; + if (profile_print_params_same(it->config, cfg)) { + // The preset exists and it matches the values stored inside config. + if (select) + this->select_printer(it - m_printers.begin()); + return *it; + } + // Form another profile name. + } + // Insert a new profile. + PhysicalPrinter& printer = this->load_printer(path, new_name, std::move(cfg), select); + + return printer; +} +void PhysicalPrinterCollection::save_printer(const std::string& new_name) +{ + // 1) Find the printer with a new_name or create a new one, + // initialize it with the edited config. + auto it = this->find_printer_internal(new_name); + if (it != m_printers.end() && it->name == new_name) { + // Preset with the same name found. + PhysicalPrinter& printer = *it; + // Overwriting an existing preset. + printer.config = std::move(m_edited_printer.config); + } + else { + // Creating a new printer. + PhysicalPrinter& printer = *m_printers.insert(it, m_edited_printer); + std::string old_name = printer.name; + printer.name = new_name; + } + // 2) Activate the saved preset. + this->select_printer_by_name(new_name, true); + // 3) Store the active preset to disk. + this->get_selected_preset().save(); +} +*/ namespace PresetUtils { const VendorProfile::PrinterModel* system_printer_model(const Preset &preset) { diff --git a/src/slic3r/GUI/Preset.hpp b/src/slic3r/GUI/Preset.hpp index dc0078091..8a8fa024b 100644 --- a/src/slic3r/GUI/Preset.hpp +++ b/src/slic3r/GUI/Preset.hpp @@ -24,11 +24,6 @@ namespace Slic3r { class AppConfig; class PresetBundle; -namespace GUI { - class BitmapCache; - class PresetComboBox; -} - enum ConfigFileType { CONFIG_FILE_TYPE_UNKNOWN, @@ -322,18 +317,6 @@ public: // returns true if the preset was deleted successfully. bool delete_preset(const std::string& name); - // Load default bitmap to be placed at the wxBitmapComboBox of a MainFrame. - void load_bitmap_default(const std::string &file_name); - - // Load "add new printer" bitmap to be placed at the wxBitmapComboBox of a MainFrame. - void load_bitmap_add(const std::string &file_name); - - // Compatible & incompatible marks, to be placed at the wxBitmapComboBox items. - void set_bitmap_compatible (const wxBitmap *bmp) { m_bitmap_compatible = bmp; } - void set_bitmap_incompatible(const wxBitmap *bmp) { m_bitmap_incompatible = bmp; } - void set_bitmap_lock (const wxBitmap *bmp) { m_bitmap_lock = bmp; } - void set_bitmap_lock_open (const wxBitmap *bmp) { m_bitmap_lock_open = bmp; } - // Enable / disable the "- default -" preset. void set_default_suppressed(bool default_suppressed); bool is_default_suppressed() const { return m_default_suppressed; } @@ -446,18 +429,9 @@ public: // Return a sorted list of system preset names. std::vector system_preset_names() const; - // Update the choice UI from the list of presets. - // If show_incompatible, all presets are shown, otherwise only the compatible presets are shown. - // If an incompatible preset is selected, it is shown as well. - size_t update_tab_ui(wxBitmapComboBox *ui, bool show_incompatible, const int em = 10); - // Update the choice UI from the list of presets. - // Only the compatible presets are shown. - // If an incompatible preset is selected, it is shown as well. - void update_plater_ui(GUI::PresetComboBox *ui); - - // Update a dirty floag of the current preset, update the labels of the UI component accordingly. + // Update a dirty flag of the current preset // Return true if the dirty flag changed. - bool update_dirty_ui(wxBitmapComboBox *ui); + bool update_dirty(); // Select a profile by its name. Return true if the selection changed. // Without force, the selection is only updated if the index changes. @@ -467,16 +441,7 @@ public: // Generate a file path from a profile name. Add the ".ini" suffix if it is missing. std::string path_from_name(const std::string &new_name) const; - void clear_bitmap_cache(); - -#ifdef __linux__ - static const char* separator_head() { return "------- "; } - static const char* separator_tail() { return " -------"; } -#else /* __linux__ */ - static const char* separator_head() { return "————— "; } - static const char* separator_tail() { return " —————"; } -#endif /* __linux__ */ - static wxString separator(const std::string &label); + size_t num_default_presets() { return m_num_default_presets; } protected: // Select a preset, if it exists. If it does not exist, select an invalid (-1) index. @@ -547,23 +512,10 @@ private: // Is the "- default -" preset suppressed? bool m_default_suppressed = true; size_t m_num_default_presets = 0; - // Compatible & incompatible marks, to be placed at the wxBitmapComboBox items of a Plater. - // These bitmaps are not owned by PresetCollection, but by a PresetBundle. - const wxBitmap *m_bitmap_compatible = nullptr; - const wxBitmap *m_bitmap_incompatible = nullptr; - const wxBitmap *m_bitmap_lock = nullptr; - const wxBitmap *m_bitmap_lock_open = nullptr; - // Marks placed at the wxBitmapComboBox of a MainFrame. - // These bitmaps are owned by PresetCollection. - wxBitmap *m_bitmap_main_frame; - // "Add printer profile" icon, owned by PresetCollection. - wxBitmap *m_bitmap_add; + // Path to the directory to store the config files into. std::string m_dir_path; - // Caching color bitmaps for the filament combo box. - GUI::BitmapCache *m_bitmap_cache = nullptr; - // to access select_preset_by_name_strict() friend class PresetBundle; }; @@ -585,6 +537,178 @@ namespace PresetUtils { const VendorProfile::PrinterModel* system_printer_model(const Preset &preset); } // namespace PresetUtils + +////////////////////////////////////////////////////////////////////// + +class PhysicalPrinter +{ +public: + PhysicalPrinter(const std::string& name) : name(name) {} + + // Name of the Physical Printer, usually derived form the file name. + std::string name; + // File name of the Physical Printer. + std::string file; + // Name of the related Printer preset + std::string preset_name; + + // Has this profile been loaded? + bool loaded = false; + + // Configuration data, loaded from a file, or set from the defaults. + DynamicPrintConfig config; + + void save() { this->config.save(this->file); } + + // Return a printer technology, return ptFFF if the printer technology is not set. + static PrinterTechnology printer_technology(const DynamicPrintConfig& cfg) { + auto* opt = cfg.option>("printer_technology"); + // The following assert may trigger when importing some legacy profile, + // but it is safer to keep it here to capture the cases where the "printer_technology" key is queried, where it should not. + return (opt == nullptr) ? ptFFF : opt->value; + } + PrinterTechnology printer_technology() const { return printer_technology(this->config); } + + // Sort lexicographically by a preset name. The preset name shall be unique across a single PresetCollection. + bool operator<(const Preset& other) const { return this->name < other.name; } + +protected: + friend class PhysicalPrinterCollection; +}; +/* +// Collections of presets of the same type (one of the Print, Filament or Printer type). +class PhysicalPrinterCollection +{ +public: + // Initialize the PresetCollection with the "- default -" preset. + PhysicalPrinterCollection(const std::vector& keys) : m_idx_selected(0) {} + ~PhysicalPrinterCollection() {} + + typedef std::deque::iterator Iterator; + typedef std::deque::const_iterator ConstIterator; + Iterator begin() { return m_printers.begin(); } + ConstIterator begin() const { return m_printers.cbegin(); } + ConstIterator cbegin() const { return m_printers.cbegin(); } + Iterator end() { return m_printers.end(); } + ConstIterator end() const { return m_printers.cend(); } + ConstIterator cend() const { return m_printers.cend(); } + + void reset(bool delete_files) {}; + + const std::deque& operator()() const { return m_printers; } + + // Load ini files of the particular type from the provided directory path. + void load_printers(const std::string& dir_path, const std::string& subdir){}; + + // Load a preset from an already parsed config file, insert it into the sorted sequence of presets + // and select it, losing previous modifications. + PhysicalPrinter& load_printer(const std::string& path, const std::string& name, const DynamicPrintConfig& config, bool select = true); + PhysicalPrinter& load_printer(const std::string& path, const std::string& name, DynamicPrintConfig&& config, bool select = true); + + PhysicalPrinter& load_external_printer( + // Path to the profile source file (a G-code, an AMF or 3MF file, a config file) + const std::string& path, + // Name of the profile, derived from the source file name. + const std::string& name, + // Original name of the profile, extracted from the loaded config. Empty, if the name has not been stored. + const std::string& original_name, + // Config to initialize the preset from. + const DynamicPrintConfig& config, + // Select the preset after loading? + bool select = true); + + // Save the printer under a new name. If the name is different from the old one, + // a new printer is stored into the list of printers. + // ? New printer is activated. + void save_printer(const std::string& new_name); + + // Delete the current preset, activate the first visible preset. + // returns true if the preset was deleted successfully. + bool delete_current_printer() {return true;} + // Delete the current preset, activate the first visible preset. + // returns true if the preset was deleted successfully. + bool delete_printer(const std::string& name) { return true; } + + // Select a printer. If an invalid index is provided, the first visible printer is selected. + PhysicalPrinter& select_printer(size_t idx); + // Return the selected preset, without the user modifications applied. + PhysicalPrinter& get_selected_preset() { return m_printers[m_idx_selected]; } + const PhysicalPrinter& get_selected_preset() const { return m_printers[m_idx_selected]; } + size_t get_selected_idx() const { return m_idx_selected; } + // Returns the name of the selected preset, or an empty string if no preset is selected. + std::string get_selected_preset_name() const { return (m_idx_selected == size_t(-1)) ? std::string() : this->get_selected_preset().name; } + PhysicalPrinter& get_edited_preset() { return m_edited_printer; } + const PhysicalPrinter& get_edited_preset() const { return m_edited_printer; } + + // Return a preset possibly with modifications. + PhysicalPrinter& default_printer(size_t idx = 0) { return m_printers[idx]; } + const PhysicalPrinter& default_printer(size_t idx = 0) const { return m_printers[idx]; } + + // used to update preset_choice from Tab + const std::deque& get_presets() const { return m_printers; } + size_t get_idx_selected() { return m_idx_selected; } + + // Return a preset by an index. If the preset is active, a temporary copy is returned. + PhysicalPrinter& printer(size_t idx) { return (idx == m_idx_selected) ? m_edited_printer : m_printers[idx]; } + const PhysicalPrinter& printer(size_t idx) const { return const_cast(this)->printer(idx); } + + // Return a preset by its name. If the preset is active, a temporary copy is returned. + // If a preset is not found by its name, null is returned. + PhysicalPrinter* find_printer(const std::string& name, bool first_visible_if_not_found = false); + const PhysicalPrinter* find_printer(const std::string& name, bool first_visible_if_not_found = false) const + { + return const_cast(this)->find_printer(name, first_visible_if_not_found); + } + + // Return number of presets including the "- default -" preset. + size_t size() const { return m_printers.size(); } + + // Select a profile by its name. Return true if the selection changed. + // Without force, the selection is only updated if the index changes. + // With force, the changes are reverted if the new index is the same as the old index. + bool select_printer_by_name(const std::string& name, bool force) {}; + + // Generate a file path from a profile name. Add the ".ini" suffix if it is missing. + std::string path_from_name(const std::string& new_name) const; + +private: +// PhysicalPrinterCollection(); + PhysicalPrinterCollection(const PhysicalPrinterCollection& other); + PhysicalPrinterCollection& operator=(const PhysicalPrinterCollection& other); + + // Find a preset position in the sorted list of presets. + // The "-- default -- " preset is always the first, so it needs + // to be handled differently. + // If a preset does not exist, an iterator is returned indicating where to insert a preset with the same name. + std::deque::iterator find_printer_internal(const std::string& name) + { + PhysicalPrinter key(name); + auto it = std::lower_bound(m_printers.begin()+0, m_printers.end(), key); + return it; + } + std::deque::const_iterator find_printer_internal(const std::string& name) const + { + return const_cast(this)->find_printer_internal(name); + } + + static std::vector dirty_options(const Preset* edited, const Preset* reference, const bool is_printer_type = false); + + // List of presets, starting with the "- default -" preset. + // Use deque to force the container to allocate an object per each entry, + // so that the addresses of the presets don't change during resizing of the container. + std::deque m_printers; + // Initially this printer contains a copy of the selected printer. Later on, this copy may be modified by the user. + PhysicalPrinter m_edited_printer; + // Selected preset. + size_t m_idx_selected; + + // Path to the directory to store the config files into. + std::string m_dir_path; +}; + +////////////////////////////////////////////////////////////////////// +*/ + } // namespace Slic3r #endif /* slic3r_Preset_hpp_ */ diff --git a/src/slic3r/GUI/PresetBundle.cpp b/src/slic3r/GUI/PresetBundle.cpp index ba806a0b2..024884b00 100644 --- a/src/slic3r/GUI/PresetBundle.cpp +++ b/src/slic3r/GUI/PresetBundle.cpp @@ -1,12 +1,10 @@ #include #include "PresetBundle.hpp" -#include "BitmapCache.hpp" #include "Plater.hpp" -#include "I18N.hpp" -#include "wxExtensions.hpp" #include +#include #include #include #include @@ -21,16 +19,13 @@ #include #include -#include #include -#include -#include -#include #include "libslic3r/libslic3r.h" #include "libslic3r/Utils.hpp" #include "libslic3r/Model.hpp" #include "GUI_App.hpp" +#include "libslic3r/CustomGCode.hpp" // Store the print/filament/printer presets into a "presets" subdirectory of the Slic3rPE config dir. @@ -52,12 +47,7 @@ PresetBundle::PresetBundle() : filaments(Preset::TYPE_FILAMENT, Preset::filament_options(), static_cast(FullPrintConfig::defaults())), sla_materials(Preset::TYPE_SLA_MATERIAL, Preset::sla_material_options(), static_cast(SLAFullPrintConfig::defaults())), sla_prints(Preset::TYPE_SLA_PRINT, Preset::sla_print_options(), static_cast(SLAFullPrintConfig::defaults())), - printers(Preset::TYPE_PRINTER, Preset::printer_options(), static_cast(FullPrintConfig::defaults()), "- default FFF -"), - m_bitmapCompatible(new wxBitmap), - m_bitmapIncompatible(new wxBitmap), - m_bitmapLock(new wxBitmap), - m_bitmapLockOpen(new wxBitmap), - m_bitmapCache(new GUI::BitmapCache) + printers(Preset::TYPE_PRINTER, Preset::printer_options(), static_cast(FullPrintConfig::defaults()), "- default FFF -") { if (wxImage::FindHandler(wxBITMAP_TYPE_PNG) == nullptr) wxImage::AddHandler(new wxPNGHandler); @@ -112,16 +102,6 @@ PresetBundle::PresetBundle() : preset.inherits(); } - // Load the default preset bitmaps. - // #ys_FIXME_to_delete we'll load them later, using em_unit() -// this->prints .load_bitmap_default("cog"); -// this->sla_prints .load_bitmap_default("package_green.png"); -// this->filaments .load_bitmap_default("spool.png"); -// this->sla_materials.load_bitmap_default("package_green.png"); -// this->printers .load_bitmap_default("printer_empty.png"); -// this->printers .load_bitmap_add("add.png"); -// this->load_compatible_bitmaps(); - // Re-activate the default presets, so their "edited" preset copies will be updated with the additional configuration values above. this->prints .select_preset(0); this->sla_prints .select_preset(0); @@ -134,20 +114,6 @@ PresetBundle::PresetBundle() : PresetBundle::~PresetBundle() { - assert(m_bitmapCompatible != nullptr); - assert(m_bitmapIncompatible != nullptr); - assert(m_bitmapLock != nullptr); - assert(m_bitmapLockOpen != nullptr); - delete m_bitmapCompatible; - m_bitmapCompatible = nullptr; - delete m_bitmapIncompatible; - m_bitmapIncompatible = nullptr; - delete m_bitmapLock; - m_bitmapLock = nullptr; - delete m_bitmapLockOpen; - m_bitmapLockOpen = nullptr; - delete m_bitmapCache; - m_bitmapCache = nullptr; } void PresetBundle::reset(bool delete_files) @@ -486,36 +452,6 @@ void PresetBundle::export_selections(AppConfig &config) config.set("presets", "printer", printers.get_selected_preset_name()); } -void PresetBundle::load_compatible_bitmaps() -{ - *m_bitmapCompatible = create_scaled_bitmap("flag_green"); - *m_bitmapIncompatible = create_scaled_bitmap("flag_red"); - *m_bitmapLock = create_scaled_bitmap("lock_closed"); - *m_bitmapLockOpen = create_scaled_bitmap("lock_open"); - - prints .set_bitmap_compatible(m_bitmapCompatible); - filaments .set_bitmap_compatible(m_bitmapCompatible); - sla_prints .set_bitmap_compatible(m_bitmapCompatible); - sla_materials.set_bitmap_compatible(m_bitmapCompatible); - - prints .set_bitmap_incompatible(m_bitmapIncompatible); - filaments .set_bitmap_incompatible(m_bitmapIncompatible); - sla_prints .set_bitmap_incompatible(m_bitmapIncompatible); - sla_materials.set_bitmap_incompatible(m_bitmapIncompatible); - - prints .set_bitmap_lock(m_bitmapLock); - filaments .set_bitmap_lock(m_bitmapLock); - sla_prints .set_bitmap_lock(m_bitmapLock); - sla_materials.set_bitmap_lock(m_bitmapLock); - printers .set_bitmap_lock(m_bitmapLock); - - prints .set_bitmap_lock_open(m_bitmapLock); - filaments .set_bitmap_lock_open(m_bitmapLock); - sla_prints .set_bitmap_lock_open(m_bitmapLock); - sla_materials.set_bitmap_lock_open(m_bitmapLock); - printers .set_bitmap_lock_open(m_bitmapLock); -} - DynamicPrintConfig PresetBundle::full_config() const { return (this->printers.get_edited_preset().printer_technology() == ptFFF) ? @@ -886,7 +822,7 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool // 4) Load the project config values (the per extruder wipe matrix etc). this->project_config.apply_only(config, s_project_options); - update_custom_gcode_per_print_z_from_config(GUI::wxGetApp().plater()->model().custom_gcode_per_print_z, &this->project_config); + CustomGCode::update_custom_gcode_per_print_z_from_config(GUI::wxGetApp().plater()->model().custom_gcode_per_print_z, &this->project_config); break; } @@ -1544,207 +1480,11 @@ void PresetBundle::export_configbundle(const std::string &path, bool export_syst // an optional "(modified)" suffix will be removed from the filament name. void PresetBundle::set_filament_preset(size_t idx, const std::string &name) { - if (name.find_first_of(PresetCollection::separator_head()) == 0) - return; - - if (idx >= filament_presets.size()) + if (idx >= filament_presets.size()) filament_presets.resize(idx + 1, filaments.default_preset().name); filament_presets[idx] = Preset::remove_suffix_modified(name); } -void PresetBundle::load_default_preset_bitmaps() -{ - // Clear bitmap cache, before load new scaled default preset bitmaps - m_bitmapCache->clear(); - this->prints.clear_bitmap_cache(); - this->sla_prints.clear_bitmap_cache(); - this->filaments.clear_bitmap_cache(); - this->sla_materials.clear_bitmap_cache(); - this->printers.clear_bitmap_cache(); - - this->prints.load_bitmap_default("cog"); - this->sla_prints.load_bitmap_default("cog"); - this->filaments.load_bitmap_default("spool.png"); - this->sla_materials.load_bitmap_default("resin"); - this->printers.load_bitmap_default("printer"); - this->printers.load_bitmap_add("add.png"); - this->load_compatible_bitmaps(); -} - -void PresetBundle::update_plater_filament_ui(unsigned int idx_extruder, GUI::PresetComboBox *ui) -{ - if (ui == nullptr || this->printers.get_edited_preset().printer_technology() == ptSLA || - this->filament_presets.size() <= idx_extruder ) - return; - - unsigned char rgb[3]; - std::string extruder_color = this->printers.get_edited_preset().config.opt_string("extruder_colour", idx_extruder); - if (!m_bitmapCache->parse_color(extruder_color, rgb)) - // Extruder color is not defined. - extruder_color.clear(); - - // Fill in the list from scratch. - ui->Freeze(); - ui->Clear(); - size_t selected_preset_item = INT_MAX; // some value meaning that no one item is selected - - const Preset *selected_preset = this->filaments.find_preset(this->filament_presets[idx_extruder]); - // Show wide icons if the currently selected preset is not compatible with the current printer, - // and draw a red flag in front of the selected preset. - bool wide_icons = selected_preset != nullptr && ! selected_preset->is_compatible && m_bitmapIncompatible != nullptr; - assert(selected_preset != nullptr); - std::map nonsys_presets; - wxString selected_str = ""; - if (!this->filaments().front().is_visible) - ui->set_label_marker(ui->Append(PresetCollection::separator(L("System presets")), wxNullBitmap)); - - /* It's supposed that standard size of an icon is 16px*16px for 100% scaled display. - * So set sizes for solid_colored icons used for filament preset - * and scale them in respect to em_unit value - */ - const float scale_f = ui->em_unit() * 0.1f; - - // To avoid the errors of number rounding for different combination of monitor configuration, - // let use scaled 8px, as a smallest icon unit - const int icon_unit = 8 * scale_f + 0.5f; - const int normal_icon_width = 2 * icon_unit; //16 * scale_f + 0.5f; - const int thin_icon_width = icon_unit; //8 * scale_f + 0.5f; - const int wide_icon_width = 3 * icon_unit; //24 * scale_f + 0.5f; - - const int space_icon_width = 2 * scale_f + 0.5f; - - // To avoid asserts, each added bitmap to wxBitmapCombobox should be the same size, so - // set a bitmap height to m_bitmapLock->GetHeight() - // - // To avoid asserts, each added bitmap to wxBitmapCombobox should be the same size. - // But for some display scaling (for example 125% or 175%) normal_icon_width differs from icon width. - // So: - // for nonsystem presets set a width of empty bitmap to m_bitmapLock->GetWidth() - // for compatible presets set a width of empty bitmap to m_bitmapIncompatible->GetWidth() - // - // Note, under OSX we should use a Scaled Height/Width because of Retina scale -#ifdef __APPLE__ - const int icon_height = m_bitmapLock->GetScaledHeight(); - const int lock_icon_width = m_bitmapLock->GetScaledWidth(); - const int flag_icon_width = m_bitmapIncompatible->GetScaledWidth(); -#else - const int icon_height = m_bitmapLock->GetHeight(); - const int lock_icon_width = m_bitmapLock->GetWidth(); - const int flag_icon_width = m_bitmapIncompatible->GetWidth(); -#endif - - wxString tooltip = ""; - - for (int i = this->filaments().front().is_visible ? 0 : 1; i < int(this->filaments().size()); ++i) { - const Preset &preset = this->filaments.preset(i); - bool selected = this->filament_presets[idx_extruder] == preset.name; - if (! preset.is_visible || (! preset.is_compatible && ! selected)) - continue; - // Assign an extruder color to the selected item if the extruder color is defined. - std::string filament_rgb = preset.config.opt_string("filament_colour", 0); - std::string extruder_rgb = (selected && !extruder_color.empty()) ? extruder_color : filament_rgb; - bool single_bar = filament_rgb == extruder_rgb; - std::string bitmap_key = single_bar ? filament_rgb : filament_rgb + extruder_rgb; - // If the filament preset is not compatible and there is a "red flag" icon loaded, show it left - // to the filament color image. - if (wide_icons) - bitmap_key += preset.is_compatible ? ",cmpt" : ",ncmpt"; - bitmap_key += (preset.is_system || preset.is_default) ? ",syst" : ",nsyst"; - if (preset.is_dirty) - bitmap_key += ",drty"; - wxBitmap *bitmap = m_bitmapCache->find(bitmap_key); - if (bitmap == nullptr) { - // Create the bitmap with color bars. - std::vector bmps; - if (wide_icons) - // Paint a red flag for incompatible presets. - bmps.emplace_back(preset.is_compatible ? m_bitmapCache->mkclear(flag_icon_width, icon_height) : *m_bitmapIncompatible); - // Paint the color bars. - m_bitmapCache->parse_color(filament_rgb, rgb); - bmps.emplace_back(m_bitmapCache->mksolid(single_bar ? wide_icon_width : normal_icon_width, icon_height, rgb)); - if (! single_bar) { - m_bitmapCache->parse_color(extruder_rgb, rgb); - bmps.emplace_back(m_bitmapCache->mksolid(thin_icon_width, icon_height, rgb)); - } - // Paint a lock at the system presets. - bmps.emplace_back(m_bitmapCache->mkclear(space_icon_width, icon_height)); - bmps.emplace_back((preset.is_system || preset.is_default) ? *m_bitmapLock : m_bitmapCache->mkclear(lock_icon_width, icon_height)); -// (preset.is_dirty ? *m_bitmapLockOpen : *m_bitmapLock) : m_bitmapCache->mkclear(16, 16)); - bitmap = m_bitmapCache->insert(bitmap_key, bmps); - } - - const std::string name = preset.alias.empty() ? preset.name : preset.alias; - if (preset.is_default || preset.is_system) { - ui->Append(wxString::FromUTF8((/*preset.*/name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), - (bitmap == 0) ? wxNullBitmap : *bitmap); - if (selected || - // just in case: mark selected_preset_item as a first added element - selected_preset_item == INT_MAX ) { - selected_preset_item = ui->GetCount() - 1; - tooltip = wxString::FromUTF8(preset.name.c_str()); - } - } - else - { - nonsys_presets.emplace(wxString::FromUTF8((/*preset.*/name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), - (bitmap == 0) ? &wxNullBitmap : bitmap); - if (selected) { - selected_str = wxString::FromUTF8((/*preset.*/name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()); - tooltip = wxString::FromUTF8(preset.name.c_str()); - } - } - if (preset.is_default) - ui->set_label_marker(ui->Append(PresetCollection::separator(L("System presets")), wxNullBitmap)); - } - - if (!nonsys_presets.empty()) - { - ui->set_label_marker(ui->Append(PresetCollection::separator(L("User presets")), wxNullBitmap)); - for (std::map::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) { - ui->Append(it->first, *it->second); - if (it->first == selected_str || - // just in case: mark selected_preset_item as a first added element - selected_preset_item == INT_MAX) { - selected_preset_item = ui->GetCount() - 1; - } - } - } - - std::string bitmap_key = ""; - if (wide_icons) - bitmap_key += "wide,"; - bitmap_key += "edit_preset_list"; - wxBitmap* bmp = m_bitmapCache->find(bitmap_key); - if (bmp == nullptr) { - // Create the bitmap with color bars. - std::vector bmps; - if (wide_icons) - // Paint a red flag for incompatible presets. - bmps.emplace_back(m_bitmapCache->mkclear(flag_icon_width, icon_height)); - // Paint the color bars + a lock at the system presets. - bmps.emplace_back(m_bitmapCache->mkclear(wide_icon_width+space_icon_width, icon_height)); - bmps.emplace_back(create_scaled_bitmap("edit_uni")); - bmp = m_bitmapCache->insert(bitmap_key, bmps); - } - ui->set_label_marker(ui->Append(PresetCollection::separator(L("Add/Remove filaments")), *bmp), GUI::PresetComboBox::LABEL_ITEM_WIZARD_FILAMENTS); - - /* But, if selected_preset_item is still equal to INT_MAX, it means that - * there is no presets added to the list. - * So, select last combobox item ("Add/Remove filaments") - */ - if (selected_preset_item == INT_MAX) - selected_preset_item = ui->GetCount() - 1; - - ui->SetSelection(selected_preset_item); - ui->SetToolTip(tooltip.IsEmpty() ? ui->GetString(selected_preset_item) : tooltip); - ui->check_selection(selected_preset_item); - ui->Thaw(); - - // Update control min size after rescale (changed Display DPI under MSW) - if (ui->GetMinWidth() != 20 * ui->em_unit()) - ui->SetMinSize(wxSize(20 * ui->em_unit(), ui->GetSize().GetHeight())); -} - void PresetBundle::set_default_suppressed(bool default_suppressed) { prints.set_default_suppressed(default_suppressed); diff --git a/src/slic3r/GUI/PresetBundle.hpp b/src/slic3r/GUI/PresetBundle.hpp index bf1bba21d..7d137bb7a 100644 --- a/src/slic3r/GUI/PresetBundle.hpp +++ b/src/slic3r/GUI/PresetBundle.hpp @@ -5,7 +5,6 @@ #include "Preset.hpp" #include -#include #include #include @@ -13,10 +12,6 @@ class wxWindow; namespace Slic3r { -namespace GUI { - class BitmapCache; -}; - // Bundle of Print + Filament + Printer presets. class PresetBundle { @@ -110,9 +105,6 @@ public: // Export a config bundle file containing all the presets and the names of the active presets. void export_configbundle(const std::string &path, bool export_system_settings = false); - // Update a filament selection combo box on the plater for an idx_extruder. - void update_plater_filament_ui(unsigned int idx_extruder, GUI::PresetComboBox *ui); - // Enable / disable the "- default -" preset. void set_default_suppressed(bool default_suppressed); @@ -132,8 +124,6 @@ public: void update_compatible(PresetSelectCompatibleType select_other_print_if_incompatible, PresetSelectCompatibleType select_other_filament_if_incompatible); void update_compatible(PresetSelectCompatibleType select_other_if_incompatible) { this->update_compatible(select_other_if_incompatible, select_other_if_incompatible); } - void load_default_preset_bitmaps(); - // Set the is_visible flag for printer vendors, printer models and printer variants // based on the user configuration. // If the "vendor" section is missing, enable all models and variants of the particular vendor. @@ -163,21 +153,9 @@ private: // If it is not an external config, then the config will be stored into the user profile directory. void load_config_file_config(const std::string &name_or_path, bool is_external, DynamicPrintConfig &&config); void load_config_file_config_bundle(const std::string &path, const boost::property_tree::ptree &tree); - void load_compatible_bitmaps(); DynamicPrintConfig full_fff_config() const; DynamicPrintConfig full_sla_config() const; - - // Indicator, that the preset is compatible with the selected printer. - wxBitmap *m_bitmapCompatible; - // Indicator, that the preset is NOT compatible with the selected printer. - wxBitmap *m_bitmapIncompatible; - // Indicator, that the preset is system and not modified. - wxBitmap *m_bitmapLock; - // Indicator, that the preset is system and user modified. - wxBitmap *m_bitmapLockOpen; - // Caching color bitmaps for the filament combo box. - GUI::BitmapCache *m_bitmapCache; }; } // namespace Slic3r diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp new file mode 100644 index 000000000..380edb48a --- /dev/null +++ b/src/slic3r/GUI/PresetComboBoxes.cpp @@ -0,0 +1,794 @@ +#include "PresetComboBoxes.hpp" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "libslic3r/libslic3r.h" +#include "libslic3r/PrintConfig.hpp" + +#include "GUI.hpp" +#include "GUI_App.hpp" +#include "Plater.hpp" +#include "MainFrame.hpp" +#include "format.hpp" +#include "Tab.hpp" +#include "PresetBundle.hpp" +#include "PrintHostDialogs.hpp" +#include "ConfigWizard.hpp" +#include "../Utils/ASCIIFolding.hpp" +#include "../Utils/PrintHost.hpp" +#include "../Utils/FixModelByWin10.hpp" +#include "../Utils/UndoRedo.hpp" +#include "RemovableDriveManager.hpp" +#include "BitmapCache.hpp" + +using Slic3r::GUI::format_wxstr; + +static const std::pair THUMBNAIL_SIZE_3MF = { 256, 256 }; + +namespace Slic3r { +namespace GUI { + +// --------------------------------- +// *** PresetComboBox *** +// --------------------------------- + +/* For PresetComboBox we use bitmaps that are created from images that are already scaled appropriately for Retina + * (Contrary to the intuition, the `scale` argument for Bitmap's constructor doesn't mean + * "please scale this to such and such" but rather + * "the wxImage is already sized for backing scale such and such". ) + * Unfortunately, the constructor changes the size of wxBitmap too. + * Thus We need to use unscaled size value for bitmaps that we use + * to avoid scaled size of control items. + * For this purpose control drawing methods and + * control size calculation methods (virtual) are overridden. + **/ + +PresetComboBox::PresetComboBox(wxWindow* parent, Preset::Type preset_type, const wxSize& size) : + wxBitmapComboBox(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, size, 0, nullptr, wxCB_READONLY), + m_type(preset_type), + m_last_selected(wxNOT_FOUND), + m_em_unit(wxGetApp().em_unit()), + m_preset_bundle(wxGetApp().preset_bundle), + m_bitmap_cache(new BitmapCache) +{ + SetFont(wxGetApp().normal_font()); +#ifdef _WIN32 + // Workaround for ignoring CBN_EDITCHANGE events, which are processed after the content of the combo box changes, so that + // the index of the item inside CBN_EDITCHANGE may no more be valid. + EnableTextChangedEvents(false); +#endif /* _WIN32 */ + + switch (m_type) + { + case Preset::TYPE_PRINT: { + m_collection = &m_preset_bundle->prints; + m_main_bitmap_name = "cog"; + break; + } + case Preset::TYPE_FILAMENT: { + m_collection = &m_preset_bundle->filaments; + m_main_bitmap_name = "spool"; + break; + } + case Preset::TYPE_SLA_PRINT: { + m_collection = &m_preset_bundle->sla_prints; + m_main_bitmap_name = "cog"; + break; + } + case Preset::TYPE_SLA_MATERIAL: { + m_collection = &m_preset_bundle->sla_materials; + m_main_bitmap_name = "resin"; + break; + } + case Preset::TYPE_PRINTER: { + m_collection = &m_preset_bundle->printers; + m_main_bitmap_name = "printer"; + break; + } + default: break; + } + + m_bitmapCompatible = ScalableBitmap(nullptr, "flag_green"); + m_bitmapIncompatible = ScalableBitmap(nullptr, "flag_red"); + m_bitmapLock = ScalableBitmap(nullptr, "lock_closed"); + + // parameters for an icon's drawing + fill_width_height(); +} + +PresetComboBox::~PresetComboBox() +{ + delete m_bitmap_cache; + m_bitmap_cache = nullptr; +} + +void PresetComboBox::set_label_marker(int item, LabelItemType label_item_type) +{ + this->SetClientData(item, (void*)label_item_type); +} + +void PresetComboBox::msw_rescale() +{ + m_em_unit = wxGetApp().em_unit(); + + m_bitmapLock.msw_rescale(); + m_bitmapIncompatible.msw_rescale(); + m_bitmapCompatible.msw_rescale(); + + // parameters for an icon's drawing + fill_width_height(); + + // update the control to redraw the icons + update(); +} + +void PresetComboBox::fill_width_height() +{ + // To avoid asserts, each added bitmap to wxBitmapCombobox should be the same size, so + // set a bitmap's height to m_bitmapLock->GetHeight() and norm_icon_width to m_bitmapLock->GetWidth() + icon_height = m_bitmapLock.GetBmpHeight(); + norm_icon_width = m_bitmapLock.GetBmpWidth(); + + /* It's supposed that standard size of an icon is 16px*16px for 100% scaled display. + * So set sizes for solid_colored icons used for filament preset + * and scale them in respect to em_unit value + */ + const float scale_f = (float)m_em_unit * 0.1f; + + thin_icon_width = lroundf(8 * scale_f); // analogue to 8px; + wide_icon_width = norm_icon_width + thin_icon_width; + + space_icon_width = lroundf(2 * scale_f); + thin_space_icon_width = 2 * space_icon_width; + wide_space_icon_width = 3 * space_icon_width; +} + +wxString PresetComboBox::separator(const std::string& label) +{ + return wxString::FromUTF8(separator_head()) + _(label) + wxString::FromUTF8(separator_tail()); +} + +#ifdef __APPLE__ +bool PresetComboBox::OnAddBitmap(const wxBitmap& bitmap) +{ + if (bitmap.IsOk()) + { + // we should use scaled! size values of bitmap + int width = (int)bitmap.GetScaledWidth(); + int height = (int)bitmap.GetScaledHeight(); + + if (m_usedImgSize.x < 0) + { + // If size not yet determined, get it from this image. + m_usedImgSize.x = width; + m_usedImgSize.y = height; + + // Adjust control size to vertically fit the bitmap + wxWindow* ctrl = GetControl(); + ctrl->InvalidateBestSize(); + wxSize newSz = ctrl->GetBestSize(); + wxSize sz = ctrl->GetSize(); + if (newSz.y > sz.y) + ctrl->SetSize(sz.x, newSz.y); + else + DetermineIndent(); + } + + wxCHECK_MSG(width == m_usedImgSize.x && height == m_usedImgSize.y, + false, + "you can only add images of same size"); + + return true; + } + + return false; +} + +void PresetComboBox::OnDrawItem(wxDC& dc, + const wxRect& rect, + int item, + int flags) const +{ + const wxBitmap& bmp = *(wxBitmap*)m_bitmaps[item]; + if (bmp.IsOk()) + { + // we should use scaled! size values of bitmap + wxCoord w = bmp.GetScaledWidth(); + wxCoord h = bmp.GetScaledHeight(); + + const int imgSpacingLeft = 4; + + // Draw the image centered + dc.DrawBitmap(bmp, + rect.x + (m_usedImgSize.x - w) / 2 + imgSpacingLeft, + rect.y + (rect.height - h) / 2, + true); + } + + wxString text = GetString(item); + if (!text.empty()) + dc.DrawText(text, + rect.x + m_imgAreaWidth + 1, + rect.y + (rect.height - dc.GetCharHeight()) / 2); +} +#endif + + +// --------------------------------- +// *** PlaterPresetComboBox *** +// --------------------------------- + +PlaterPresetComboBox::PlaterPresetComboBox(wxWindow *parent, Preset::Type preset_type) : + PresetComboBox(parent, preset_type, wxSize(15 * wxGetApp().em_unit(), -1)) +{ + Bind(wxEVT_COMBOBOX, [this](wxCommandEvent &evt) { + auto selected_item = evt.GetSelection(); + + auto marker = reinterpret_cast(this->GetClientData(selected_item)); + if (marker >= LABEL_ITEM_MARKER && marker < LABEL_ITEM_MAX) { + this->SetSelection(this->m_last_selected); + evt.StopPropagation(); + if (marker == LABEL_ITEM_PHYSICAL_PRINTERS) + { + PhysicalPrinterDialog dlg; + dlg.ShowModal(); + return; + } + if (marker >= LABEL_ITEM_WIZARD_PRINTERS) { + ConfigWizard::StartPage sp = ConfigWizard::SP_WELCOME; + switch (marker) { + case LABEL_ITEM_WIZARD_PRINTERS: sp = ConfigWizard::SP_PRINTERS; break; + case LABEL_ITEM_WIZARD_FILAMENTS: sp = ConfigWizard::SP_FILAMENTS; break; + case LABEL_ITEM_WIZARD_MATERIALS: sp = ConfigWizard::SP_MATERIALS; break; + default: break; + } + wxTheApp->CallAfter([sp]() { wxGetApp().run_wizard(ConfigWizard::RR_USER, sp); }); + } + } else if ( this->m_last_selected != selected_item || m_collection->current_is_dirty() ) { + this->m_last_selected = selected_item; + evt.SetInt(this->m_type); + evt.Skip(); + } else { + evt.StopPropagation(); + } + }); + + if (m_type == Preset::TYPE_FILAMENT) + { + Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent &event) { + const Preset* selected_preset = m_collection->find_preset(m_preset_bundle->filament_presets[m_extruder_idx]); + // Wide icons are shown if the currently selected preset is not compatible with the current printer, + // and red flag is drown in front of the selected preset. + bool wide_icons = selected_preset != nullptr && !selected_preset->is_compatible; + float scale = m_em_unit*0.1f; + + int shifl_Left = wide_icons ? int(scale * 16 + 0.5) : 0; +#if defined(wxBITMAPCOMBOBOX_OWNERDRAWN_BASED) + shifl_Left += int(scale * 4 + 0.5f); // IMAGE_SPACING_RIGHT = 4 for wxBitmapComboBox -> Space left of image +#endif + int icon_right_pos = shifl_Left + int(scale * (24+4) + 0.5); + int mouse_pos = event.GetLogicalPosition(wxClientDC(this)).x; + if (mouse_pos < shifl_Left || mouse_pos > icon_right_pos ) { + // Let the combo box process the mouse click. + event.Skip(); + return; + } + + // Swallow the mouse click and open the color picker. + + // get current color + DynamicPrintConfig* cfg = wxGetApp().get_tab(Preset::TYPE_PRINTER)->get_config(); + auto colors = static_cast(cfg->option("extruder_colour")->clone()); + wxColour clr(colors->values[m_extruder_idx]); + if (!clr.IsOk()) + clr = wxColour(0,0,0); // Don't set alfa to transparence + + auto data = new wxColourData(); + data->SetChooseFull(1); + data->SetColour(clr); + + wxColourDialog dialog(this, data); + dialog.CenterOnParent(); + if (dialog.ShowModal() == wxID_OK) + { + colors->values[m_extruder_idx] = dialog.GetColourData().GetColour().GetAsString(wxC2S_HTML_SYNTAX).ToStdString(); + + DynamicPrintConfig cfg_new = *cfg; + cfg_new.set_key_value("extruder_colour", colors); + + wxGetApp().get_tab(Preset::TYPE_PRINTER)->load_config(cfg_new); + this->update(); + wxGetApp().plater()->on_config_change(cfg_new); + } + }); + } + + edit_btn = new ScalableButton(parent, wxID_ANY, "cog"); + edit_btn->SetToolTip(_L("Click to edit preset")); + + edit_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent) + { + Tab* tab = wxGetApp().get_tab(m_type); + if (!tab) + return; + + int page_id = wxGetApp().tab_panel()->FindPage(tab); + if (page_id == wxNOT_FOUND) + return; + + wxGetApp().tab_panel()->SetSelection(page_id); + + // Switch to Settings NotePad + wxGetApp().mainframe->select_tab(); + + /* In a case of a multi-material printing, for editing another Filament Preset + * it's needed to select this preset for the "Filament settings" Tab + */ + if (m_type == Preset::TYPE_FILAMENT && wxGetApp().extruders_edited_cnt() > 1) + { + const std::string& selected_preset = GetString(GetSelection()).ToUTF8().data(); + + // Call select_preset() only if there is new preset and not just modified + if ( !boost::algorithm::ends_with(selected_preset, Preset::suffix_modified()) ) + { + const std::string& preset_name = wxGetApp().preset_bundle->filaments.get_preset_name_by_alias(selected_preset); + tab->select_preset(preset_name); + } + } + }); +} + +PlaterPresetComboBox::~PlaterPresetComboBox() +{ + if (edit_btn) + edit_btn->Destroy(); +} + +// Only the compatible presets are shown. +// If an incompatible preset is selected, it is shown as well. +void PlaterPresetComboBox::update() +{ + if (m_type == Preset::TYPE_FILAMENT && + (m_collection->get_edited_preset().printer_technology() == ptSLA || + m_preset_bundle->filament_presets.size() <= m_extruder_idx) ) + return; + + // Otherwise fill in the list from scratch. + this->Freeze(); + this->Clear(); + size_t selected_preset_item = INT_MAX; // some value meaning that no one item is selected + + const Preset* selected_filament_preset; + std::string extruder_color; + if (m_type == Preset::TYPE_FILAMENT) + { + unsigned char rgb[3]; + extruder_color = m_preset_bundle->printers.get_edited_preset().config.opt_string("extruder_colour", (unsigned int)m_extruder_idx); + if (!m_bitmap_cache->parse_color(extruder_color, rgb)) + // Extruder color is not defined. + extruder_color.clear(); + selected_filament_preset = m_collection->find_preset(m_preset_bundle->filament_presets[m_extruder_idx]); + assert(selected_filament_preset); + } + + const Preset& selected_preset = m_type == Preset::TYPE_FILAMENT ? *selected_filament_preset : m_collection->get_selected_preset(); + // Show wide icons if the currently selected preset is not compatible with the current printer, + // and draw a red flag in front of the selected preset. + bool wide_icons = !selected_preset.is_compatible; + + std::map nonsys_presets; + std::map physical_printers; + + wxString selected = ""; + wxString tooltip = ""; + const std::deque& presets = m_collection->get_presets(); + + if (!presets.front().is_visible) + this->set_label_marker(this->Append(separator(L("System presets")), wxNullBitmap)); + + for (size_t i = presets.front().is_visible ? 0 : m_collection->num_default_presets(); i < presets.size(); ++i) + { + const Preset& preset = presets[i]; + bool is_selected = m_type == Preset::TYPE_FILAMENT ? + m_preset_bundle->filament_presets[m_extruder_idx] == preset.name : + i == m_collection->get_selected_idx(); + + if (!preset.is_visible || (!preset.is_compatible && !is_selected)) + continue; + + std::string bitmap_key, filament_rgb, extruder_rgb; + bool single_bar = false; + if (m_type == Preset::TYPE_PRINTER && preset.printer_technology() == ptSLA) + bitmap_key = "sla_printer"; + else if (m_type == Preset::TYPE_FILAMENT) + { + // Assign an extruder color to the selected item if the extruder color is defined. + filament_rgb = preset.config.opt_string("filament_colour", 0); + extruder_rgb = (selected && !extruder_color.empty()) ? extruder_color : filament_rgb; + single_bar = filament_rgb == extruder_rgb; + + bitmap_key = single_bar ? filament_rgb : filament_rgb + extruder_rgb; + } + wxBitmap main_bmp = create_scaled_bitmap(m_type == Preset::TYPE_PRINTER && preset.printer_technology() == ptSLA ? "sla_printer" : m_main_bitmap_name); + + // If the filament preset is not compatible and there is a "red flag" icon loaded, show it left + // to the filament color image. + if (wide_icons) + bitmap_key += preset.is_compatible ? ",cmpt" : ",ncmpt"; + bitmap_key += (preset.is_system || preset.is_default) ? ",syst" : ",nsyst"; + bitmap_key += "-h" + std::to_string(icon_height); + + wxBitmap* bmp = m_bitmap_cache->find(bitmap_key); + if (bmp == nullptr) { + // Create the bitmap with color bars. + std::vector bmps; + if (wide_icons) + // Paint a red flag for incompatible presets. + bmps.emplace_back(preset.is_compatible ? m_bitmap_cache->mkclear(norm_icon_width, icon_height) : m_bitmapIncompatible.bmp()); + + if (m_type == Preset::TYPE_FILAMENT) + { + unsigned char rgb[3]; + // Paint the color bars. + m_bitmap_cache->parse_color(filament_rgb, rgb); + bmps.emplace_back(m_bitmap_cache->mksolid(single_bar ? wide_icon_width : norm_icon_width, icon_height, rgb)); + if (!single_bar) { + m_bitmap_cache->parse_color(extruder_rgb, rgb); + bmps.emplace_back(m_bitmap_cache->mksolid(thin_icon_width, icon_height, rgb)); + } + // Paint a lock at the system presets. + bmps.emplace_back(m_bitmap_cache->mkclear(space_icon_width, icon_height)); + } + else + { + // Paint the color bars. + bmps.emplace_back(m_bitmap_cache->mkclear(thin_space_icon_width, icon_height)); + bmps.emplace_back(main_bmp); + // Paint a lock at the system presets. + bmps.emplace_back(m_bitmap_cache->mkclear(wide_space_icon_width, icon_height)); + } + bmps.emplace_back((preset.is_system || preset.is_default) ? m_bitmapLock.bmp() : m_bitmap_cache->mkclear(norm_icon_width, icon_height)); + bmp = m_bitmap_cache->insert(bitmap_key, bmps); + } + + const std::string name = preset.alias.empty() ? preset.name : preset.alias; + if (preset.is_default || preset.is_system) { + Append(wxString::FromUTF8((name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), + !bmp ? main_bmp : *bmp); + if (is_selected || + // just in case: mark selected_preset_item as a first added element + selected_preset_item == INT_MAX) { + selected_preset_item = GetCount() - 1; + tooltip = wxString::FromUTF8(preset.name.c_str()); + } + } + else + { + nonsys_presets.emplace(wxString::FromUTF8((name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), bmp); + if (is_selected) { + selected = wxString::FromUTF8((name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()); + tooltip = wxString::FromUTF8(preset.name.c_str()); + } + } + if (i + 1 == m_collection->num_default_presets()) + set_label_marker(Append(separator(L("System presets")), wxNullBitmap)); + } + if (!nonsys_presets.empty()) + { + set_label_marker(Append(separator(L("User presets")), wxNullBitmap)); + for (std::map::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) { + Append(it->first, *it->second); + if (it->first == selected || + // just in case: mark selected_preset_item as a first added element + selected_preset_item == INT_MAX) + selected_preset_item = GetCount() - 1; + } + } + if (!physical_printers.empty()) + { + set_label_marker(Append(separator(L("Physical printers")), wxNullBitmap)); + for (std::map::iterator it = physical_printers.begin(); it != physical_printers.end(); ++it) { + Append(it->first, *it->second); + if (it->first == selected || + // just in case: mark selected_preset_item as a first added element + selected_preset_item == INT_MAX) + selected_preset_item = GetCount() - 1; + } + } + + if (m_type == Preset::TYPE_PRINTER || m_type == Preset::TYPE_SLA_MATERIAL) { + std::string bitmap_key = ""; + // If the filament preset is not compatible and there is a "red flag" icon loaded, show it left + // to the filament color image. + if (wide_icons) + bitmap_key += "wide,"; + bitmap_key += "edit_preset_list"; + bitmap_key += "-h" + std::to_string(icon_height); + + wxBitmap* bmp = m_bitmap_cache->find(bitmap_key); + if (bmp == nullptr) { + // Create the bitmap with color bars.update_plater_ui + std::vector bmps; + if (wide_icons) + // Paint a red flag for incompatible presets. + bmps.emplace_back(m_bitmap_cache->mkclear(norm_icon_width, icon_height)); + // Paint the color bars. + bmps.emplace_back(m_bitmap_cache->mkclear(thin_space_icon_width, icon_height)); + bmps.emplace_back(create_scaled_bitmap(m_main_bitmap_name)); + // Paint a lock at the system presets. + bmps.emplace_back(m_bitmap_cache->mkclear(wide_space_icon_width, icon_height)); + bmps.emplace_back(create_scaled_bitmap("edit_uni")); + bmp = m_bitmap_cache->insert(bitmap_key, bmps); + } + if (m_type == Preset::TYPE_SLA_MATERIAL) + set_label_marker(Append(separator(L("Add/Remove materials")), *bmp), LABEL_ITEM_WIZARD_MATERIALS); + else + set_label_marker(Append(separator(L("Add/Remove printers")), *bmp), LABEL_ITEM_WIZARD_PRINTERS); + } + if (m_type == Preset::TYPE_PRINTER) { + std::string bitmap_key = ""; + if (wide_icons) + bitmap_key += "wide,"; + bitmap_key += "edit_preset_list"; + bitmap_key += "-h" + std::to_string(icon_height); + + wxBitmap* bmp = m_bitmap_cache->find(bitmap_key); + if (bmp == nullptr) { + // Create the bitmap with color bars. + std::vector bmps; + if (wide_icons) + // Paint a red flag for incompatible presets. + bmps.emplace_back(m_bitmap_cache->mkclear(norm_icon_width, icon_height)); + // Paint the color bars. + bmps.emplace_back(m_bitmap_cache->mkclear(thin_space_icon_width, icon_height)); + bmps.emplace_back(create_scaled_bitmap("printer")); + // Paint a lock at the system presets. + bmps.emplace_back(m_bitmap_cache->mkclear(wide_space_icon_width, icon_height)); + bmps.emplace_back(create_scaled_bitmap("edit_uni")); + bmp = m_bitmap_cache->insert(bitmap_key, bmps); + } + set_label_marker(Append(separator(L("Add physical printer")), *bmp), LABEL_ITEM_PHYSICAL_PRINTERS); + } + + /* But, if selected_preset_item is still equal to INT_MAX, it means that + * there is no presets added to the list. + * So, select last combobox item ("Add/Remove preset") + */ + if (selected_preset_item == INT_MAX) + selected_preset_item = GetCount() - 1; + + SetSelection(selected_preset_item); + SetToolTip(tooltip.IsEmpty() ? GetString(selected_preset_item) : tooltip); + m_last_selected = selected_preset_item; + Thaw(); + + // Update control min size after rescale (changed Display DPI under MSW) + if (GetMinWidth() != 20 * m_em_unit) + SetMinSize(wxSize(20 * m_em_unit, GetSize().GetHeight())); +} + +void PlaterPresetComboBox::msw_rescale() +{ + PresetComboBox::msw_rescale(); + edit_btn->msw_rescale(); +} + + +// --------------------------------- +// *** PlaterPresetComboBox *** +// --------------------------------- + +TabPresetComboBox::TabPresetComboBox(wxWindow* parent, Preset::Type preset_type) : + PresetComboBox(parent, preset_type, wxSize(35 * wxGetApp().em_unit(), -1)) +{ + Bind(wxEVT_COMBOBOX, [this](wxCommandEvent& evt) { + // see https://github.com/prusa3d/PrusaSlicer/issues/3889 + // Under OSX: in case of use of a same names written in different case (like "ENDER" and "Ender") + // m_presets_choice->GetSelection() will return first item, because search in PopupListCtrl is case-insensitive. + // So, use GetSelection() from event parameter + auto selected_item = evt.GetSelection(); + + auto marker = reinterpret_cast(this->GetClientData(selected_item)); + if (marker >= LABEL_ITEM_MARKER && marker < LABEL_ITEM_MAX) { + this->SetSelection(this->m_last_selected); + if (marker == LABEL_ITEM_WIZARD_PRINTERS) + wxTheApp->CallAfter([]() { wxGetApp().run_wizard(ConfigWizard::RR_USER, ConfigWizard::SP_PRINTERS); }); + } + else if (m_last_selected != selected_item || m_collection->current_is_dirty()) { + std::string selected_string = this->GetString(selected_item).ToUTF8().data(); + Tab* tab = wxGetApp().get_tab(this->m_type); + assert (tab); + tab->select_preset(selected_string); + } + + evt.StopPropagation(); + }); +} + +// Update the choice UI from the list of presets. +// If show_incompatible, all presets are shown, otherwise only the compatible presets are shown. +// If an incompatible preset is selected, it is shown as well. +void TabPresetComboBox::update() +{ + Freeze(); + Clear(); + size_t selected_preset_item = INT_MAX; // some value meaning that no one item is selected + + const std::deque& presets = m_collection->get_presets(); + + std::map nonsys_presets; + wxString selected = ""; + if (!presets.front().is_visible) + set_label_marker(Append(separator(L("System presets")), wxNullBitmap)); + int idx_selected = m_collection->get_selected_idx(); + for (size_t i = presets.front().is_visible ? 0 : m_collection->num_default_presets(); i < presets.size(); ++i) { + const Preset& preset = presets[i]; + if (!preset.is_visible || (!show_incompatible && !preset.is_compatible && i != idx_selected)) + continue; + + std::string bitmap_key = "tab"; + wxBitmap main_bmp = create_scaled_bitmap(m_type == Preset::TYPE_PRINTER && preset.printer_technology() == ptSLA ? "sla_printer" : m_main_bitmap_name); + if (m_type == Preset::TYPE_PRINTER) { + bitmap_key += "_printer"; + if (preset.printer_technology() == ptSLA) + bitmap_key += "_sla"; + } + bitmap_key += preset.is_compatible ? ",cmpt" : ",ncmpt"; + bitmap_key += (preset.is_system || preset.is_default) ? ",syst" : ",nsyst"; + bitmap_key += "-h" + std::to_string(icon_height); + + wxBitmap* bmp = m_bitmap_cache->find(bitmap_key); + if (bmp == nullptr) { + // Create the bitmap with color bars. + std::vector bmps; + bmps.emplace_back(m_type == Preset::TYPE_PRINTER ? main_bmp : preset.is_compatible ? m_bitmapCompatible.bmp() : m_bitmapIncompatible.bmp()); + // Paint a lock at the system presets. + bmps.emplace_back((preset.is_system || preset.is_default) ? m_bitmapLock.bmp() : m_bitmap_cache->mkclear(norm_icon_width, icon_height)); + bmp = m_bitmap_cache->insert(bitmap_key, bmps); + } + + if (preset.is_default || preset.is_system) { + Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), + (bmp == 0) ? main_bmp : *bmp); + if (i == idx_selected || + // just in case: mark selected_preset_item as a first added element + selected_preset_item == INT_MAX) + selected_preset_item = GetCount() - 1; + } + else + { + nonsys_presets.emplace(wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), bmp); + if (i == idx_selected) + selected = wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()); + } + if (i + 1 == m_collection->num_default_presets()) + set_label_marker(Append(separator(L("System presets")), wxNullBitmap)); + } + if (!nonsys_presets.empty()) + { + set_label_marker(Append(separator(L("User presets")), wxNullBitmap)); + for (std::map::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) { + Append(it->first, *it->second); + if (it->first == selected || + // just in case: mark selected_preset_item as a first added element + selected_preset_item == INT_MAX) + selected_preset_item = GetCount() - 1; + } + } + if (m_type == Preset::TYPE_PRINTER) { + std::string bitmap_key = "edit_preset_list"; + bitmap_key += "-h" + std::to_string(icon_height); + + wxBitmap* bmp = m_bitmap_cache->find(bitmap_key); + if (bmp == nullptr) { + // Create the bitmap with color bars. + std::vector bmps; + bmps.emplace_back(create_scaled_bitmap(m_main_bitmap_name)); + bmps.emplace_back(create_scaled_bitmap("edit_uni")); + bmp = m_bitmap_cache->insert(bitmap_key, bmps); + } + set_label_marker(Append(separator(L("Add/Remove printers")), *bmp), LABEL_ITEM_WIZARD_PRINTERS); + } + + /* But, if selected_preset_item is still equal to INT_MAX, it means that + * there is no presets added to the list. + * So, select last combobox item ("Add/Remove preset") + */ + if (selected_preset_item == INT_MAX) + selected_preset_item = GetCount() - 1; + + SetSelection(selected_preset_item); + SetToolTip(GetString(selected_preset_item)); + Thaw(); + + m_last_selected = selected_preset_item; +} + +void TabPresetComboBox::msw_rescale() +{ + PresetComboBox::msw_rescale(); + wxSize sz = wxSize(35 * m_em_unit, -1); + SetMinSize(sz); + SetSize(sz); +} + +void TabPresetComboBox::update_dirty() +{ + // 1) Update the dirty flag of the current preset. + m_collection->update_dirty(); + + // 2) Update the labels. + wxWindowUpdateLocker noUpdates(this); + for (unsigned int ui_id = 0; ui_id < GetCount(); ++ui_id) { + std::string old_label = GetString(ui_id).utf8_str().data(); + std::string preset_name = Preset::remove_suffix_modified(old_label); + const Preset* preset = m_collection->find_preset(preset_name, false); + if (preset) { + std::string new_label = preset->is_dirty ? preset->name + Preset::suffix_modified() : preset->name; + if (old_label != new_label) + SetString(ui_id, wxString::FromUTF8(new_label.c_str())); + } + } +#ifdef __APPLE__ + // wxWidgets on OSX do not upload the text of the combo box line automatically. + // Force it to update by re-selecting. + SetSelection(GetSelection()); +#endif /* __APPLE __ */ +} + + +//------------------------------------------ +// PhysicalPrinterDialog +//------------------------------------------ + + +PhysicalPrinterDialog::PhysicalPrinterDialog() + : DPIDialog(NULL, wxID_ANY, _L("Search"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) +{ + SetFont(wxGetApp().normal_font()); + SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); + + int border = 10; + int em = em_unit(); + + printer_text = new wxTextCtrl(this, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER); + printer_presets = new PlaterPresetComboBox(this, Preset::TYPE_PRINTER); + + wxStdDialogButtonSizer* btns = this->CreateStdDialogButtonSizer(wxOK | wxCANCEL); + + wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); + + topSizer->Add(printer_text , 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); + topSizer->Add(printer_presets , 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); + topSizer->Add(btns , 0, wxEXPAND | wxALL, border); + + SetSizer(topSizer); + topSizer->SetSizeHints(this); +} + +void PhysicalPrinterDialog::on_dpi_changed(const wxRect& suggested_rect) +{ + const int& em = em_unit(); + + msw_buttons_rescale(this, em, { wxID_OK, wxID_CANCEL }); + + const wxSize& size = wxSize(40 * em, 30 * em); + SetMinSize(size); + + Fit(); + Refresh(); +} + + +}} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/PresetComboBoxes.hpp b/src/slic3r/GUI/PresetComboBoxes.hpp new file mode 100644 index 000000000..63110e432 --- /dev/null +++ b/src/slic3r/GUI/PresetComboBoxes.hpp @@ -0,0 +1,179 @@ +#ifndef slic3r_PresetComboBoxes_hpp_ +#define slic3r_PresetComboBoxes_hpp_ + +#include + +#include +#include +#include + +#include "Preset.hpp" +#include "wxExtensions.hpp" +#include "GUI_Utils.hpp" + +class wxString; +class wxTextCtrl; + +namespace Slic3r { + +namespace GUI { + +class BitmapCache; + + +// --------------------------------- +// *** PresetComboBox *** +// --------------------------------- + +// BitmapComboBox used to presets list on Sidebar and Tabs +class PresetComboBox : public wxBitmapComboBox +{ +public: + PresetComboBox(wxWindow* parent, Preset::Type preset_type, const wxSize& size = wxDefaultSize); + ~PresetComboBox(); + + enum LabelItemType { + LABEL_ITEM_MARKER = 0xffffff01, + LABEL_ITEM_PHYSICAL_PRINTERS, + LABEL_ITEM_WIZARD_PRINTERS, + LABEL_ITEM_WIZARD_FILAMENTS, + LABEL_ITEM_WIZARD_MATERIALS, + + LABEL_ITEM_MAX, + }; + + void set_label_marker(int item, LabelItemType label_item_type = LABEL_ITEM_MARKER); + int em_unit() const { return m_em_unit; } + + virtual void update() {}; + virtual void msw_rescale(); + +protected: + typedef std::size_t Marker; + + Preset::Type m_type; + std::string m_main_bitmap_name; + + PresetBundle* m_preset_bundle {nullptr}; + PresetCollection* m_collection {nullptr}; + + // Caching color bitmaps for the filament combo box. + BitmapCache* m_bitmap_cache {nullptr}; + // Indicator, that the preset is compatible with the selected printer. + ScalableBitmap m_bitmapCompatible; + // Indicator, that the preset is NOT compatible with the selected printer. + ScalableBitmap m_bitmapIncompatible; + // Indicator, that the preset is system and not modified. + ScalableBitmap m_bitmapLock; + + int m_last_selected; + int m_em_unit; + + // parameters for an icon's drawing + int icon_height; + int norm_icon_width; + int thin_icon_width; + int wide_icon_width; + int space_icon_width; + int thin_space_icon_width; + int wide_space_icon_width; + +#ifdef __linux__ + static const char* separator_head() { return "------- "; } + static const char* separator_tail() { return " -------"; } +#else // __linux__ + static const char* separator_head() { return "————— "; } + static const char* separator_tail() { return " —————"; } +#endif // __linux__ + static wxString separator(const std::string& label); + +#ifdef __APPLE__ + /* For PresetComboBox we use bitmaps that are created from images that are already scaled appropriately for Retina + * (Contrary to the intuition, the `scale` argument for Bitmap's constructor doesn't mean + * "please scale this to such and such" but rather + * "the wxImage is already sized for backing scale such and such". ) + * Unfortunately, the constructor changes the size of wxBitmap too. + * Thus We need to use unscaled size value for bitmaps that we use + * to avoid scaled size of control items. + * For this purpose control drawing methods and + * control size calculation methods (virtual) are overridden. + **/ + virtual bool OnAddBitmap(const wxBitmap& bitmap) override; + virtual void OnDrawItem(wxDC& dc, const wxRect& rect, int item, int flags) const override; +#endif + +private: + void fill_width_height(); +}; + + +// --------------------------------- +// *** PlaterPresetComboBox *** +// --------------------------------- + +class PlaterPresetComboBox : public PresetComboBox +{ +public: + PlaterPresetComboBox(wxWindow *parent, Preset::Type preset_type); + ~PlaterPresetComboBox(); + + ScalableButton* edit_btn { nullptr }; + + void set_extruder_idx(const int extr_idx) { m_extruder_idx = extr_idx; } + int get_extruder_idx() const { return m_extruder_idx; } + + void update() override; + void msw_rescale() override; + +private: + int m_extruder_idx = -1; +}; + + +// --------------------------------- +// *** PlaterPresetComboBox *** +// --------------------------------- + +class TabPresetComboBox : public PresetComboBox +{ +public: + TabPresetComboBox(wxWindow *parent, Preset::Type preset_type); + ~TabPresetComboBox() {} + void set_show_incompatible_presets(bool show_incompatible_presets) { + show_incompatible = show_incompatible_presets; + } + + void update() override; + void update_dirty(); + void msw_rescale() override; + +private: + bool show_incompatible{false}; +}; + + +//------------------------------------------ +// PhysicalPrinterDialog +//------------------------------------------ + +class PhysicalPrinterDialog : public DPIDialog +{ + std::string printer_name; + std::string preset_name; + + wxTextCtrl* printer_text { nullptr }; + PresetComboBox* printer_presets; + +public: + PhysicalPrinterDialog(); + ~PhysicalPrinterDialog() {} + +protected: + void on_dpi_changed(const wxRect& suggested_rect) override; + void on_sys_color_changed() override {}; +}; + +} // namespace GUI +} // namespace Slic3r + +#endif diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 84bc5a572..b128ec03d 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -27,6 +27,7 @@ #include #include "wxExtensions.hpp" +#include "PresetComboBoxes.hpp" #include #include "GUI_App.hpp" @@ -160,10 +161,7 @@ void Tab::create_preset_tab() #endif //__WXOSX__ // preset chooser - m_presets_choice = new PresetBitmapComboBox(panel, wxSize(35 * m_em_unit, -1)); - - // search combox -// m_search = new Search::SearchCtrl(panel); + m_presets_choice = new TabPresetComboBox(panel, m_type); auto color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); @@ -278,35 +276,6 @@ void Tab::create_preset_tab() m_treectrl->Bind(wxEVT_TREE_SEL_CHANGED, &Tab::OnTreeSelChange, this); 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(); - - // see https://github.com/prusa3d/PrusaSlicer/issues/3889 - // Under OSX: in case of use of a same names written in different case (like "ENDER" and "Ender") - // m_presets_choice->GetSelection() will return first item, because search in PopupListCtrl is case-insensitive. - // So, use GetSelection() from event parameter - int selected_item = e.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); - } - })); - m_btn_save_preset->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e) { save_preset(); })); m_btn_delete_preset->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e) { delete_preset(); })); m_btn_hide_incompatible_presets->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e) { @@ -778,14 +747,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. @@ -850,12 +819,10 @@ void Tab::msw_rescale() m_em_unit = wxGetApp().em_unit(); 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(); @@ -963,7 +930,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(); } @@ -3360,6 +3327,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(); } diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index 5805809bf..a0bf536c1 100644 --- a/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -39,6 +39,8 @@ namespace Slic3r { namespace GUI { +class TabPresetComboBox; + // Single Tab page containing a{ vsizer } of{ optgroups } // package Slic3r::GUI::Tab::Page; using ConfigOptionsGroupShp = std::shared_ptr; @@ -113,7 +115,7 @@ protected: Preset::Type m_type; std::string m_name; const wxString m_title; - PresetBitmapComboBox* m_presets_choice; + TabPresetComboBox* m_presets_choice; ScalableButton* m_search_btn; ScalableButton* m_btn_save_preset; ScalableButton* m_btn_delete_preset; @@ -206,8 +208,6 @@ protected: bool m_is_nonsys_values{ true }; bool m_postpone_update_ui {false}; - size_t m_selected_preset_item{ 0 }; - void set_type(); int m_em_unit; @@ -320,7 +320,6 @@ public: DynamicPrintConfig* get_config() { return m_config; } PresetCollection* get_presets() { return m_presets; } - size_t get_selected_preset_item() { return m_selected_preset_item; } void on_value_change(const std::string& opt_key, const boost::any& value); diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index ad9f0a121..39b3e154b 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -300,94 +300,6 @@ void wxCheckListBoxComboPopup::OnListBoxSelection(wxCommandEvent& evt) } -namespace Slic3r { -namespace GUI { - -// *** PresetBitmapComboBox *** - -/* For PresetBitmapComboBox we use bitmaps that are created from images that are already scaled appropriately for Retina - * (Contrary to the intuition, the `scale` argument for Bitmap's constructor doesn't mean - * "please scale this to such and such" but rather - * "the wxImage is already sized for backing scale such and such". ) - * Unfortunately, the constructor changes the size of wxBitmap too. - * Thus We need to use unscaled size value for bitmaps that we use - * to avoid scaled size of control items. - * For this purpose control drawing methods and - * control size calculation methods (virtual) are overridden. - **/ - -PresetBitmapComboBox::PresetBitmapComboBox(wxWindow* parent, const wxSize& size) : - wxBitmapComboBox(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, size, 0, nullptr, wxCB_READONLY) -{} - -#ifdef __APPLE__ -bool PresetBitmapComboBox::OnAddBitmap(const wxBitmap& bitmap) -{ - if (bitmap.IsOk()) - { - // we should use scaled! size values of bitmap - int width = (int)bitmap.GetScaledWidth(); - int height = (int)bitmap.GetScaledHeight(); - - if (m_usedImgSize.x < 0) - { - // If size not yet determined, get it from this image. - m_usedImgSize.x = width; - m_usedImgSize.y = height; - - // Adjust control size to vertically fit the bitmap - wxWindow* ctrl = GetControl(); - ctrl->InvalidateBestSize(); - wxSize newSz = ctrl->GetBestSize(); - wxSize sz = ctrl->GetSize(); - if (newSz.y > sz.y) - ctrl->SetSize(sz.x, newSz.y); - else - DetermineIndent(); - } - - wxCHECK_MSG(width == m_usedImgSize.x && height == m_usedImgSize.y, - false, - "you can only add images of same size"); - - return true; - } - - return false; -} - -void PresetBitmapComboBox::OnDrawItem(wxDC& dc, - const wxRect& rect, - int item, - int flags) const -{ - const wxBitmap& bmp = *(wxBitmap*)m_bitmaps[item]; - if (bmp.IsOk()) - { - // we should use scaled! size values of bitmap - wxCoord w = bmp.GetScaledWidth(); - wxCoord h = bmp.GetScaledHeight(); - - const int imgSpacingLeft = 4; - - // Draw the image centered - dc.DrawBitmap(bmp, - rect.x + (m_usedImgSize.x - w) / 2 + imgSpacingLeft, - rect.y + (rect.height - h) / 2, - true); - } - - wxString text = GetString(item); - if (!text.empty()) - dc.DrawText(text, - rect.x + m_imgAreaWidth + 1, - rect.y + (rect.height - dc.GetCharHeight()) / 2); -} -#endif -} -} - - // *** wxDataViewTreeCtrlComboPopup *** const unsigned int wxDataViewTreeCtrlComboPopup::DefaultWidth = 270; diff --git a/src/slic3r/GUI/wxExtensions.hpp b/src/slic3r/GUI/wxExtensions.hpp index 569257e1b..17fe8992c 100644 --- a/src/slic3r/GUI/wxExtensions.hpp +++ b/src/slic3r/GUI/wxExtensions.hpp @@ -95,37 +95,6 @@ public: void OnListBoxSelection(wxCommandEvent& evt); }; -namespace Slic3r { -namespace GUI { -// *** PresetBitmapComboBox *** - -// BitmapComboBox used to presets list on Sidebar and Tabs -class PresetBitmapComboBox: public wxBitmapComboBox -{ -public: - PresetBitmapComboBox(wxWindow* parent, const wxSize& size = wxDefaultSize); - ~PresetBitmapComboBox() {} - -#ifdef __APPLE__ -protected: - /* For PresetBitmapComboBox we use bitmaps that are created from images that are already scaled appropriately for Retina - * (Contrary to the intuition, the `scale` argument for Bitmap's constructor doesn't mean - * "please scale this to such and such" but rather - * "the wxImage is already sized for backing scale such and such". ) - * Unfortunately, the constructor changes the size of wxBitmap too. - * Thus We need to use unscaled size value for bitmaps that we use - * to avoid scaled size of control items. - * For this purpose control drawing methods and - * control size calculation methods (virtual) are overridden. - **/ - virtual bool OnAddBitmap(const wxBitmap& bitmap) override; - virtual void OnDrawItem(wxDC& dc, const wxRect& rect, int item, int flags) const override; -#endif -}; - -} -} - // *** wxDataViewTreeCtrlComboBox *** -- cgit v1.2.3