diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index e46cd6c82..94c9577df 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -1339,108 +1339,178 @@ 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; +// ------------------------- +// *** PhysicalPrinter *** +// ------------------------- + +const std::vector& PhysicalPrinter::printer_options() +{ + static std::vector s_opts; + if (s_opts.empty()) { + s_opts = { + "preset_name", + "printer_technology", + "host_type", + "print_host", + "printhost_apikey", + "printhost_cafile", + "login", + "password" + }; + } + return s_opts; } -void PhysicalPrinterCollection::save_printer(const std::string& new_name) +const std::string& PhysicalPrinter::get_preset_name() +{ + return config.opt_string("preset_name"); +} + +void PhysicalPrinter::update_from_preset(const Preset& preset) +{ + config.apply_only(preset.config, printer_options(), false); + // add preset name to the options list + config.set_key_value("preset_name", new ConfigOptionString(preset.name)); +} + +void PhysicalPrinter::update_from_config(const DynamicPrintConfig& new_config) +{ + config.apply_only(new_config, printer_options(), false); +} + +PhysicalPrinter::PhysicalPrinter(const std::string& name, const Preset& preset) : + name(name) +{ + update_from_preset(preset); +} + + +// ----------------------------------- +// *** PhysicalPrinterCollection *** +// ----------------------------------- + +PhysicalPrinterCollection::PhysicalPrinterCollection( const std::vector& keys) +{ +} + +// Load all presets found in dir_path. +// Throws an exception on error. +void PhysicalPrinterCollection::load_printers(const std::string& dir_path, const std::string& subdir) +{ + boost::filesystem::path dir = boost::filesystem::canonical(boost::filesystem::path(dir_path) / subdir).make_preferred(); + m_dir_path = dir.string(); + std::string errors_cummulative; + // Store the loaded printers into a new vector, otherwise the binary search for already existing presets would be broken. + std::deque printers_loaded; + for (auto& dir_entry : boost::filesystem::directory_iterator(dir)) + if (Slic3r::is_ini_file(dir_entry)) { + std::string name = dir_entry.path().filename().string(); + // Remove the .ini suffix. + name.erase(name.size() - 4); + if (this->find_printer(name, false)) { + // This happens when there's is a preset (most likely legacy one) with the same name as a system preset + // that's already been loaded from a bundle. + BOOST_LOG_TRIVIAL(warning) << "Preset already present, not loading: " << name; + continue; + } + try { + PhysicalPrinter printer(name); + printer.file = dir_entry.path().string(); + // Load the preset file, apply preset values on top of defaults. + try { + DynamicPrintConfig config; + config.load_from_ini(printer.file); + printer.update_from_config(config); + printer.loaded = true; + } + catch (const std::ifstream::failure& err) { + throw std::runtime_error(std::string("The selected preset cannot be loaded: ") + printer.file + "\n\tReason: " + err.what()); + } + catch (const std::runtime_error& err) { + throw std::runtime_error(std::string("Failed loading the preset file: ") + printer.file + "\n\tReason: " + err.what()); + } + printers_loaded.emplace_back(printer); + } + catch (const std::runtime_error& err) { + errors_cummulative += err.what(); + errors_cummulative += "\n"; + } + } + m_printers.insert(m_printers.end(), std::make_move_iterator(printers_loaded.begin()), std::make_move_iterator(printers_loaded.end())); + std::sort(m_printers.begin(), m_printers.end()); +//! this->select_preset(first_visible_idx()); + if (!errors_cummulative.empty()) + throw std::runtime_error(errors_cummulative); +} + +PhysicalPrinter* PhysicalPrinterCollection::find_printer( const std::string& name, bool first_visible_if_not_found) +{ + PhysicalPrinter key(name); + auto it = this->find_printer_internal(name); + // Ensure that a temporary copy is returned if the preset found is currently selected. + return (it != m_printers.end() && it->name == key.name) ? &this->printer(it - m_printers.begin()) : + first_visible_if_not_found ? &this->printer(0) : nullptr; +} + +// Generate a file path from a profile name. Add the ".ini" suffix if it is missing. +std::string PhysicalPrinterCollection::path_from_name(const std::string& new_name) const +{ + std::string file_name = boost::iends_with(new_name, ".ini") ? new_name : (new_name + ".ini"); + return (boost::filesystem::path(m_dir_path) / file_name).make_preferred().string(); +} + +void PhysicalPrinterCollection::save_printer(const PhysicalPrinter& edited_printer) { // 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; + auto it = this->find_printer_internal(edited_printer.name); + if (it != m_printers.end() && it->name == edited_printer.name) { + // Printer with the same name found. // Overwriting an existing preset. - printer.config = std::move(m_edited_printer.config); + it->config = std::move(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; + it = m_printers.insert(it, edited_printer); } - // 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(); + assert(it != m_printers.end()); + + // 2) Save printer + PhysicalPrinter& printer = *it; + if (printer.file.empty()) + printer.file = this->path_from_name(printer.name); + printer.save(); + + // update idx_selected + m_idx_selected = it - m_printers.begin(); } -*/ + +bool PhysicalPrinterCollection::delete_printer(const std::string& name) +{ + auto it = this->find_printer_internal(name); + + const PhysicalPrinter& printer = *it; + if (it == m_printers.end()) + return false; + + // Erase the preset file. + boost::nowide::remove(printer.file.c_str()); + m_printers.erase(it); + return true; +} + +PhysicalPrinter& PhysicalPrinterCollection::select_printer_by_name(const std::string& name) +{ + auto it = this->find_printer_internal(name); + assert(it != m_printers.end()); + + // update idx_selected + m_idx_selected = it - m_printers.begin(); + return *it; +} + + namespace PresetUtils { const VendorProfile::PrinterModel* system_printer_model(const Preset &preset) { diff --git a/src/libslic3r/Preset.hpp b/src/libslic3r/Preset.hpp index b0af2f142..c08a1a0fb 100644 --- a/src/libslic3r/Preset.hpp +++ b/src/libslic3r/Preset.hpp @@ -535,14 +535,14 @@ namespace PresetUtils { class PhysicalPrinter { public: - PhysicalPrinter(const std::string& name) : name(name) {} + PhysicalPrinter() {} + PhysicalPrinter(const std::string& name) : name(name){} + PhysicalPrinter(const std::string& name, const Preset& preset); // 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; @@ -550,7 +550,13 @@ public: // Configuration data, loaded from a file, or set from the defaults. DynamicPrintConfig config; + static const std::vector& printer_options(); + const std::string& get_preset_name(); + void save() { this->config.save(this->file); } + void save_to(const std::string& file_name) const { this->config.save(file_name); } + void update_from_preset(const Preset& preset); + void update_from_config(const DynamicPrintConfig &new_config); // Return a printer technology, return ptFFF if the printer technology is not set. static PrinterTechnology printer_technology(const DynamicPrintConfig& cfg) { @@ -562,18 +568,22 @@ public: 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; } + bool operator<(const PhysicalPrinter& 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). + + +// --------------------------------- +// *** PhysicalPrinterCollection *** +// --------------------------------- + +// Collections of physical printers class PhysicalPrinterCollection { public: - // Initialize the PresetCollection with the "- default -" preset. - PhysicalPrinterCollection(const std::vector& keys) : m_idx_selected(0) {} + PhysicalPrinterCollection(const std::vector& keys); ~PhysicalPrinterCollection() {} typedef std::deque::iterator Iterator; @@ -585,63 +595,39 @@ public: ConstIterator end() const { return m_printers.cend(); } ConstIterator cend() const { return m_printers.cend(); } + bool empty() const {return m_printers.empty(); } + 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); + void load_printers(const std::string& dir_path, const std::string& subdir); // 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); + // New printer is activated. + void save_printer(const PhysicalPrinter& printer); // 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; } + bool delete_printer(const std::string& name); - // 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]; } + PhysicalPrinter& get_selected_printer() { return m_printers[m_idx_selected]; } + const PhysicalPrinter& get_selected_printer() 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; } + std::string get_selected_printer_name() const { return (m_idx_selected == size_t(-1)) ? std::string() : this->get_selected_printer().name; } + DynamicPrintConfig* get_selected_printer_config() { return (m_idx_selected == size_t(-1)) ? nullptr : &(this->get_selected_printer().config); } - // 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]; } + // select printer with name and return reference on it + PhysicalPrinter& select_printer_by_name(const std::string& name); + bool has_selection() const { return m_idx_selected != size_t(-1); } + void unselect_printer() { m_idx_selected = size_t(-1); } - // 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]; } + // Return a printer by an index. If the printer is active, a temporary copy is returned. + PhysicalPrinter& printer(size_t idx) { return 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. @@ -652,20 +638,10 @@ public: 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. @@ -674,8 +650,8 @@ private: // 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); + PhysicalPrinter printer(name); + auto it = std::lower_bound(m_printers.begin(), m_printers.end(), printer); return it; } std::deque::const_iterator find_printer_internal(const std::string& name) const @@ -683,23 +659,18 @@ private: 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. + // List of printers // 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; + + // Selected printer. + size_t m_idx_selected = size_t(-1); // Path to the directory to store the config files into. std::string m_dir_path; }; -////////////////////////////////////////////////////////////////////// -*/ } // namespace Slic3r diff --git a/src/libslic3r/PresetBundle.cpp b/src/libslic3r/PresetBundle.cpp index 9da7731a4..7c4fa1cb2 100644 --- a/src/libslic3r/PresetBundle.cpp +++ b/src/libslic3r/PresetBundle.cpp @@ -41,7 +41,8 @@ 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 -") + printers(Preset::TYPE_PRINTER, Preset::printer_options(), static_cast(FullPrintConfig::defaults()), "- default FFF -"), + physical_printers(PhysicalPrinter::printer_options()) { // The following keys are handled by the UI, they do not have a counterpart in any StaticPrintConfig derived classes, // therefore they need to be handled differently. As they have no counterpart in StaticPrintConfig, they are not being @@ -139,14 +140,16 @@ void PresetBundle::setup_directories() data_dir / "presets" / "filament", data_dir / "presets" / "sla_print", data_dir / "presets" / "sla_material", - data_dir / "presets" / "printer" + data_dir / "presets" / "printer", + data_dir / "presets" / "physical_printer" #else // Store the print/filament/printer presets at the same location as the upstream Slic3r. data_dir / "print", data_dir / "filament", data_dir / "sla_print", data_dir / "sla_material", - data_dir / "printer" + data_dir / "printer", + data_dir / "physical_printer" #endif }; for (const boost::filesystem::path &path : paths) { @@ -196,6 +199,11 @@ void PresetBundle::load_presets(AppConfig &config, const std::string &preferred_ } catch (const std::runtime_error &err) { errors_cummulative += err.what(); } + try { + this->physical_printers.load_printers(dir_user_presets, "physical_printer"); + } catch (const std::runtime_error &err) { + errors_cummulative += err.what(); + } this->update_multi_material_filament_presets(); this->update_compatible(PresetSelectCompatibleType::Never); if (! errors_cummulative.empty()) @@ -422,6 +430,14 @@ void PresetBundle::load_selections(AppConfig &config, const std::string &preferr // exist. this->update_compatible(PresetSelectCompatibleType::Always); this->update_multi_material_filament_presets(); + + // Parse the initial physical printer name. + std::string initial_physical_printer_name = remove_ini_suffix(config.get("extras", "physical_printer")); + + // Activate physical printer from the config + const PhysicalPrinter* initial_physical_printer = physical_printers.find_printer(initial_physical_printer_name); + if (initial_physical_printer) + physical_printers.select_printer_by_name(initial_physical_printer_name); } // Export selections (current print, current filaments, current printer) into config.ini @@ -441,6 +457,8 @@ void PresetBundle::export_selections(AppConfig &config) config.set("presets", "sla_print", sla_prints.get_selected_preset_name()); config.set("presets", "sla_material", sla_materials.get_selected_preset_name()); config.set("presets", "printer", printers.get_selected_preset_name()); + + config.set("extras", "physical_printer", physical_printers.get_selected_printer_name()); } DynamicPrintConfig PresetBundle::full_config() const diff --git a/src/libslic3r/PresetBundle.hpp b/src/libslic3r/PresetBundle.hpp index 19d4093d6..2906584d3 100644 --- a/src/libslic3r/PresetBundle.hpp +++ b/src/libslic3r/PresetBundle.hpp @@ -39,6 +39,7 @@ public: PresetCollection& materials(PrinterTechnology pt) { return pt == ptFFF ? this->filaments : this->sla_materials; } const PresetCollection& materials(PrinterTechnology pt) const { return pt == ptFFF ? this->filaments : this->sla_materials; } PrinterPresetCollection printers; + PhysicalPrinterCollection physical_printers; // Filament preset names for a multi-extruder or multi-material print. // extruders.size() should be the same as printers.get_edited_preset().config.nozzle_diameter.size() std::vector filament_presets; diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index d6a23d75d..93ef170fb 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -130,6 +130,26 @@ void PrintConfigDef::init_common_params() def->min = 0; def->mode = comAdvanced; def->set_default_value(new ConfigOptionFloat(0.2)); + + // Options used by physical printers + + def = this->add("login", coString); + def->label = L("Login"); +// def->tooltip = L(""); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionString("")); + + def = this->add("password", coString); + def->label = L("Password"); +// def->tooltip = L(""); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionString("")); + + def = this->add("preset_name", coString); + def->label = L("Printer preset name"); + def->tooltip = L("Related printer preset name"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionString("")); } void PrintConfigDef::init_fff_params() diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index a78683bd4..048e17926 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -942,8 +942,6 @@ void Sidebar::msw_rescale() 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 (PlaterPresetComboBox* combo : std::vector{ p->combo_print, p->combo_sla_print, p->combo_sla_material, @@ -952,12 +950,8 @@ void Sidebar::sys_color_changed() 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->object_list->sys_color_changed(); p->object_manipulation->sys_color_changed(); -// p->object_settings->msw_rescale(); p->object_layers->sys_color_changed(); // btn...->msw_rescale() updates icon on button, so use it @@ -3208,12 +3202,23 @@ void Plater::priv::on_select_preset(wxCommandEvent &evt) //! instead of //! combo->GetStringSelection().ToUTF8().data()); - const std::string preset_name = wxGetApp().preset_bundle->get_preset_name_by_alias(preset_type, + std::string preset_name = wxGetApp().preset_bundle->get_preset_name_by_alias(preset_type, Preset::remove_suffix_modified(combo->GetString(selection).ToUTF8().data())); if (preset_type == Preset::TYPE_FILAMENT) { wxGetApp().preset_bundle->set_filament_preset(idx, preset_name); } + + if (preset_type == Preset::TYPE_PRINTER) { + if(combo->is_selected_physical_printer()) { + // Select related printer preset on the Printer Settings Tab + const std::string printer_name = combo->GetString(selection).ToUTF8().data(); + PhysicalPrinter& printer = wxGetApp().preset_bundle->physical_printers.select_printer_by_name(printer_name); + preset_name = wxGetApp().preset_bundle->get_preset_name_by_alias(preset_type, printer.get_preset_name()); + } + else + wxGetApp().preset_bundle->physical_printers.unselect_printer(); + } // TODO: ? if (preset_type == Preset::TYPE_FILAMENT && sidebar->is_multifilament()) { @@ -3974,7 +3979,12 @@ void Plater::priv::show_action_buttons(const bool ready_to_slice) const this->ready_to_slice = ready_to_slice; wxWindowUpdateLocker noUpdater(sidebar); - const auto prin_host_opt = config->option("print_host"); + + DynamicPrintConfig* selected_printer_config = wxGetApp().preset_bundle->physical_printers.get_selected_printer_config(); + if (!selected_printer_config) + selected_printer_config = config; + + const auto prin_host_opt = selected_printer_config->option("print_host"); const bool send_gcode_shown = prin_host_opt != nullptr && !prin_host_opt->value.empty(); // when a background processing is ON, export_btn and/or send_btn are showing @@ -4893,7 +4903,9 @@ void Plater::send_gcode() { if (p->model.objects.empty()) { return; } - PrintHostJob upload_job(p->config); + // if physical_printer is selected, send gcode for this printer + DynamicPrintConfig* physical_printer_config = wxGetApp().preset_bundle->physical_printers.get_selected_printer_config(); + PrintHostJob upload_job(physical_printer_config ? physical_printer_config : p->config); if (upload_job.empty()) { return; } // Obtain default output path diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp index 6c38c866d..ce25d5690 100644 --- a/src/slic3r/GUI/PresetComboBoxes.cpp +++ b/src/slic3r/GUI/PresetComboBoxes.cpp @@ -31,6 +31,7 @@ #include "../Utils/UndoRedo.hpp" #include "RemovableDriveManager.hpp" #include "BitmapCache.hpp" +#include "BonjourDialog.hpp" using Slic3r::GUI::format_wxstr; @@ -241,8 +242,9 @@ PlaterPresetComboBox::PlaterPresetComboBox(wxWindow *parent, Preset::Type preset evt.StopPropagation(); if (marker == LABEL_ITEM_PHYSICAL_PRINTERS) { - PhysicalPrinterDialog dlg(_L("New Physical Printer"), this->m_last_selected); - dlg.ShowModal(); + PhysicalPrinterDialog dlg(wxEmptyString); + if (dlg.ShowModal() == wxID_OK) + this->update(); return; } if (marker >= LABEL_ITEM_WIZARD_PRINTERS) { @@ -255,7 +257,7 @@ PlaterPresetComboBox::PlaterPresetComboBox(wxWindow *parent, Preset::Type preset } wxTheApp->CallAfter([sp]() { wxGetApp().run_wizard(ConfigWizard::RR_USER, sp); }); } - } else if ( this->m_last_selected != selected_item || m_collection->current_is_dirty() ) { + } else if (marker == LABEL_ITEM_PHYSICAL_PRINTER || this->m_last_selected != selected_item || m_collection->current_is_dirty() ) { this->m_last_selected = selected_item; evt.SetInt(this->m_type); evt.Skip(); @@ -319,6 +321,16 @@ PlaterPresetComboBox::PlaterPresetComboBox(wxWindow *parent, Preset::Type preset edit_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent) { + // In a case of a physical printer, for its editing open PhysicalPrinterDialog + if (m_type == Preset::TYPE_PRINTER && this->is_selected_physical_printer()) + { + PhysicalPrinterDialog dlg(this->GetString(this->GetSelection())); + if (dlg.ShowModal() == wxID_OK) { + update(); + return; + } + } + Tab* tab = wxGetApp().get_tab(m_type); if (!tab) return; @@ -355,6 +367,13 @@ PlaterPresetComboBox::~PlaterPresetComboBox() edit_btn->Destroy(); } +bool PlaterPresetComboBox::is_selected_physical_printer() +{ + auto selected_item = this->GetSelection(); + auto marker = reinterpret_cast(this->GetClientData(selected_item)); + return marker == LABEL_ITEM_PHYSICAL_PRINTER; +} + // Only the compatible presets are shown. // If an incompatible preset is selected, it is shown as well. void PlaterPresetComboBox::update() @@ -388,7 +407,6 @@ void PlaterPresetComboBox::update() bool wide_icons = !selected_preset.is_compatible; std::map nonsys_presets; - std::map physical_printers; wxString selected = ""; wxString tooltip = ""; @@ -400,9 +418,11 @@ void PlaterPresetComboBox::update() 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(); + bool is_selected = m_type == Preset::TYPE_FILAMENT ? + m_preset_bundle->filament_presets[m_extruder_idx] == preset.name : + // The case, when some physical printer is selected + m_type == Preset::TYPE_PRINTER && m_preset_bundle->physical_printers.has_selection() ? false : + i == m_collection->get_selected_idx(); if (!preset.is_visible || (!preset.is_compatible && !is_selected)) continue; @@ -495,17 +515,6 @@ void PlaterPresetComboBox::update() 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 = ""; @@ -536,8 +545,45 @@ void PlaterPresetComboBox::update() 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 (m_type == Preset::TYPE_PRINTER) + { + // add Physical printers, if any exists + if (!m_preset_bundle->physical_printers.empty()) { + set_label_marker(Append(separator(L("Physical printers")), wxNullBitmap)); + const PhysicalPrinterCollection& ph_printers = m_preset_bundle->physical_printers; + + for (PhysicalPrinterCollection::ConstIterator it = ph_printers.begin(); it != ph_printers.end(); ++it) { + std::string bitmap_key = it->printer_technology() == ptSLA ? "sla_printer" : m_main_bitmap_name; + if (wide_icons) + bitmap_key += "wide,"; + 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(it->printer_technology() == ptSLA ? "sla_printer" : m_main_bitmap_name)); + // Paint a lock at the system presets. + bmps.emplace_back(m_bitmap_cache->mkclear(wide_space_icon_width+norm_icon_width, icon_height)); + bmp = m_bitmap_cache->insert(bitmap_key, bmps); + } + + set_label_marker(Append(wxString::FromUTF8((it->name).c_str()), *bmp), LABEL_ITEM_PHYSICAL_PRINTER); + if (ph_printers.has_selection() && it->name == ph_printers.get_selected_printer_name() || + // just in case: mark selected_preset_item as a first added element + selected_preset_item == INT_MAX) + selected_preset_item = GetCount() - 1; + } + } + + // add LABEL_ITEM_PHYSICAL_PRINTERS + std::string bitmap_key; if (wide_icons) bitmap_key += "wide,"; bitmap_key += "edit_preset_list"; @@ -589,10 +635,10 @@ void PlaterPresetComboBox::msw_rescale() // *** TabPresetComboBox *** // --------------------------------- -TabPresetComboBox::TabPresetComboBox(wxWindow* parent, Preset::Type preset_type, bool is_from_physical_printer/* = false*/) : +TabPresetComboBox::TabPresetComboBox(wxWindow* parent, Preset::Type preset_type) : PresetComboBox(parent, preset_type, wxSize(35 * wxGetApp().em_unit(), -1)) { - Bind(wxEVT_COMBOBOX, [this, is_from_physical_printer](wxCommandEvent& evt) { + 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. @@ -603,21 +649,17 @@ TabPresetComboBox::TabPresetComboBox(wxWindow* parent, Preset::Type preset_type, if (marker >= LABEL_ITEM_MARKER && marker < LABEL_ITEM_MAX) { this->SetSelection(this->m_last_selected); if (marker == LABEL_ITEM_WIZARD_PRINTERS) - wxTheApp->CallAfter([this, is_from_physical_printer]() { + wxTheApp->CallAfter([this]() { wxGetApp().run_wizard(ConfigWizard::RR_USER, ConfigWizard::SP_PRINTERS); - if (is_from_physical_printer) + + // update combobox if its parent is a PhysicalPrinterDialog + PhysicalPrinterDialog* parent = dynamic_cast(this->GetParent()); + if (parent != nullptr) update(); }); } - else if ( is_from_physical_printer) { - // do nothing - } - 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); - } + else if (on_selection_changed && (m_last_selected != selected_item || m_collection->current_is_dirty()) ) + on_selection_changed(selected_item); evt.StopPropagation(); }); @@ -760,35 +802,212 @@ void TabPresetComboBox::update_dirty() //------------------------------------------ -PhysicalPrinterDialog::PhysicalPrinterDialog(const wxString& printer_name, int last_selected_preset) +PhysicalPrinterDialog::PhysicalPrinterDialog(wxString printer_name) : DPIDialog(NULL, wxID_ANY, _L("PhysicalPrinter"), 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, printer_name, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER); - printer_presets = new TabPresetComboBox(this, Preset::TYPE_PRINTER, true); - printer_presets->update(); + m_printer_presets = new TabPresetComboBox(this, Preset::TYPE_PRINTER); + m_printer_presets->set_selection_changed_function([this](int selection) { + std::string selected_string = Preset::remove_suffix_modified(m_printer_presets->GetString(selection).ToUTF8().data()); + Preset* preset = wxGetApp().preset_bundle->printers.find_preset(selected_string); + assert(preset); + Preset& edited_preset = wxGetApp().preset_bundle->printers.get_edited_preset(); + if (preset->name == edited_preset.name) + preset = &edited_preset; + m_printer.update_from_preset(*preset); + + // update values + m_optgroup->reload_config(); + update_octoprint_visible(); + }); + m_printer_presets->update(); + + wxString preset_name = m_printer_presets->GetString(m_printer_presets->GetSelection()); + + if (printer_name.IsEmpty()) + printer_name = preset_name + " - "+_L("Physical Printer"); + m_printer_name = new wxTextCtrl(this, wxID_ANY, printer_name, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER); + + PhysicalPrinterCollection& printers = wxGetApp().preset_bundle->physical_printers; + PhysicalPrinter* printer = printers.find_printer(into_u8(printer_name)); + if (!printer) { + const Preset& preset = wxGetApp().preset_bundle->printers.get_edited_preset(); + printer = new PhysicalPrinter(into_u8(printer_name), preset); + } + assert(printer); + m_printer = *printer; + + m_config = &m_printer.config; + + m_optgroup = new ConfigOptionsGroup(this, _L("Print Host upload"), m_config); + build_printhost_settings(m_optgroup); + m_optgroup->reload_config(); wxStdDialogButtonSizer* btns = this->CreateStdDialogButtonSizer(wxOK | wxCANCEL); + wxButton* btnOK = static_cast(this->FindWindowById(wxID_OK, this)); + btnOK->Bind(wxEVT_BUTTON, &PhysicalPrinterDialog::OnOK, this); 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); + topSizer->Add(m_printer_name , 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); + topSizer->Add(m_printer_presets , 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); + topSizer->Add(m_optgroup->sizer , 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); + topSizer->Add(btns , 0, wxEXPAND | wxALL, border); SetSizer(topSizer); topSizer->SetSizeHints(this); } +void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgroup) +{ + m_optgroup->append_single_option_line("host_type"); + + auto create_sizer_with_btn = [this](wxWindow* parent, ScalableButton** btn, const std::string& icon_name, const wxString& label) { + *btn = new ScalableButton(parent, wxID_ANY, icon_name, label, wxDefaultSize, wxDefaultPosition, wxBU_LEFT | wxBU_EXACTFIT); + (*btn)->SetFont(wxGetApp().normal_font()); + + auto sizer = new wxBoxSizer(wxHORIZONTAL); + sizer->Add(*btn); + return sizer; + }; + + auto printhost_browse = [=](wxWindow* parent) + { + auto sizer = create_sizer_with_btn(parent, &m_printhost_browse_btn, "browse", _L("Browse") + " " + dots); + m_printhost_browse_btn->Bind(wxEVT_BUTTON, [=](wxCommandEvent& e) { + BonjourDialog dialog(this, Preset::printer_technology(m_printer.config)); + if (dialog.show_and_lookup()) { + m_optgroup->set_value("print_host", std::move(dialog.get_selected()), true); + m_optgroup->get_field("print_host")->field_changed(); + } + }); + + return sizer; + }; + + auto print_host_test = [=](wxWindow* parent) { + auto sizer = create_sizer_with_btn(parent, &m_printhost_test_btn, "test", _L("Test")); + + m_printhost_test_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent& e) { + std::unique_ptr host(PrintHost::get_print_host(m_config)); + if (!host) { + const wxString text = _L("Could not get a valid Printer Host reference"); + show_error(this, text); + return; + } + wxString msg; + if (host->test(msg)) { + show_info(this, host->get_test_ok_msg(), _L("Success!")); + } + else { + show_error(this, host->get_test_failed_msg(msg)); + } + }); + + return sizer; + }; + + // Set a wider width for a better alignment + Option option = m_optgroup->get_option("print_host"); + option.opt.width = Field::def_width_wider(); + Line host_line = m_optgroup->create_single_option_line(option); + host_line.append_widget(printhost_browse); + host_line.append_widget(print_host_test); + m_optgroup->append_line(host_line); + option = m_optgroup->get_option("printhost_apikey"); + option.opt.width = Field::def_width_wider(); + m_optgroup->append_single_option_line(option); + + const auto ca_file_hint = _u8L("HTTPS CA file is optional. It is only needed if you use HTTPS with a self-signed certificate."); + + if (Http::ca_file_supported()) { + option = m_optgroup->get_option("printhost_cafile"); + option.opt.width = Field::def_width_wider(); + Line cafile_line = m_optgroup->create_single_option_line(option); + + auto printhost_cafile_browse = [=](wxWindow* parent) { + auto sizer = create_sizer_with_btn(parent, &m_printhost_cafile_browse_btn, "browse", _L("Browse") + " " + dots); + m_printhost_cafile_browse_btn->Bind(wxEVT_BUTTON, [this, m_optgroup](wxCommandEvent e) { + static const auto filemasks = _L("Certificate files (*.crt, *.pem)|*.crt;*.pem|All files|*.*"); + wxFileDialog openFileDialog(this, _L("Open CA certificate file"), "", "", filemasks, wxFD_OPEN | wxFD_FILE_MUST_EXIST); + if (openFileDialog.ShowModal() != wxID_CANCEL) { + m_optgroup->set_value("printhost_cafile", std::move(openFileDialog.GetPath()), true); + m_optgroup->get_field("printhost_cafile")->field_changed(); + } + }); + + return sizer; + }; + + cafile_line.append_widget(printhost_cafile_browse); + m_optgroup->append_line(cafile_line); + + Line cafile_hint{ "", "" }; + cafile_hint.full_width = 1; + cafile_hint.widget = [this, ca_file_hint](wxWindow* parent) { + auto txt = new wxStaticText(parent, wxID_ANY, ca_file_hint); + auto sizer = new wxBoxSizer(wxHORIZONTAL); + sizer->Add(txt); + return sizer; + }; + m_optgroup->append_line(cafile_hint); + } + else { + Line line{ "", "" }; + line.full_width = 1; + + line.widget = [ca_file_hint](wxWindow* parent) { + std::string info = _u8L("HTTPS CA File") + ":\n\t" + + (boost::format(_u8L("On this system, %s uses HTTPS certificates from the system Certificate Store or Keychain.")) % SLIC3R_APP_NAME).str() + + "\n\t" + _u8L("To use a custom CA file, please import your CA file into Certificate Store / Keychain."); + + auto txt = new wxStaticText(parent, wxID_ANY, from_u8((boost::format("%1%\n\n\t%2%") % info % ca_file_hint).str())); + txt->SetFont(wxGetApp().normal_font()); + auto sizer = new wxBoxSizer(wxHORIZONTAL); + sizer->Add(txt, 1, wxEXPAND); + return sizer; + }; + + m_optgroup->append_line(line); + } + + for (const std::string& opt_key : std::vector{ "login", "password" }) { + option = m_optgroup->get_option(opt_key); + option.opt.width = Field::def_width_wider(); + m_optgroup->append_single_option_line(option); + } + + update_octoprint_visible(); +} + +void PhysicalPrinterDialog::update_octoprint_visible() +{ + const PrinterTechnology tech = Preset::printer_technology(m_printer.config); + // Only offer the host type selection for FFF, for SLA it's always the SL1 printer (at the moment) + Field* host_type = m_optgroup->get_field("host_type"); + if (tech == ptFFF) + host_type->enable(); + else { + host_type->set_value(int(PrintHostType::htOctoPrint), false); + host_type->disable(); + } +} + void PhysicalPrinterDialog::on_dpi_changed(const wxRect& suggested_rect) { const int& em = em_unit(); + m_printhost_browse_btn->msw_rescale(); + m_printhost_test_btn->msw_rescale(); + if (m_printhost_cafile_browse_btn) + m_printhost_cafile_browse_btn->msw_rescale(); + + m_optgroup->msw_rescale(); + msw_buttons_rescale(this, em, { wxID_OK, wxID_CANCEL }); const wxSize& size = wxSize(40 * em, 30 * em); @@ -798,5 +1017,46 @@ void PhysicalPrinterDialog::on_dpi_changed(const wxRect& suggested_rect) Refresh(); } +void PhysicalPrinterDialog::OnOK(wxEvent& event) +{ + wxString printer_name = m_printer_name->GetValue(); + if (printer_name.IsEmpty()) { + show_error(this, _L("The supplied name is empty. It can't be saved.")); + return; + } + + PhysicalPrinterCollection& printers = wxGetApp().preset_bundle->physical_printers; + const PhysicalPrinter* existing = printers.find_printer(into_u8(printer_name)); + if (existing && into_u8(printer_name) != printers.get_selected_printer_name()) + { + wxString msg_text = from_u8((boost::format(_u8L("Printer with name \"%1%\" already exists.")) % printer_name).str()); + msg_text += "\n" + _L("Replace?"); + wxMessageDialog dialog(nullptr, msg_text, _L("Warning"), wxICON_WARNING | wxYES | wxNO); + + if (dialog.ShowModal() == wxID_NO) + return; + + // Remove the printer from the list. + printers.delete_printer(into_u8(printer_name)); + } + + //upadte printer name, if it was changed + m_printer.name = into_u8(printer_name); + + // save new physical printer + printers.save_printer(m_printer); + + // update selection on the tab only when it was changed + if (m_printer.get_preset_name() != wxGetApp().preset_bundle->printers.get_selected_preset_name()) { + Tab* tab = wxGetApp().get_tab(Preset::TYPE_PRINTER); + if (tab) { + wxString preset_name = m_printer_presets->GetString(m_printer_presets->GetSelection()); + tab->select_preset(into_u8(preset_name)); + } + } + + event.Skip(); +} + }} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/PresetComboBoxes.hpp b/src/slic3r/GUI/PresetComboBoxes.hpp index 38b98d658..e1597bcfc 100644 --- a/src/slic3r/GUI/PresetComboBoxes.hpp +++ b/src/slic3r/GUI/PresetComboBoxes.hpp @@ -13,6 +13,7 @@ class wxString; class wxTextCtrl; +class ScalableButton; namespace Slic3r { @@ -33,7 +34,8 @@ public: ~PresetComboBox(); enum LabelItemType { - LABEL_ITEM_MARKER = 0xffffff01, + LABEL_ITEM_PHYSICAL_PRINTER = 0xffffff01, + LABEL_ITEM_MARKER, LABEL_ITEM_PHYSICAL_PRINTERS, LABEL_ITEM_WIZARD_PRINTERS, LABEL_ITEM_WIZARD_FILAMENTS, @@ -121,11 +123,13 @@ public: void set_extruder_idx(const int extr_idx) { m_extruder_idx = extr_idx; } int get_extruder_idx() const { return m_extruder_idx; } + bool is_selected_physical_printer(); + void update() override; void msw_rescale() override; private: - int m_extruder_idx = -1; + int m_extruder_idx = -1; }; @@ -135,8 +139,11 @@ private: class TabPresetComboBox : public PresetComboBox { + bool show_incompatible {false}; + std::function on_selection_changed { nullptr }; + public: - TabPresetComboBox(wxWindow *parent, Preset::Type preset_type, bool is_from_physical_printer = false); + TabPresetComboBox(wxWindow *parent, Preset::Type preset_type); ~TabPresetComboBox() {} void set_show_incompatible_presets(bool show_incompatible_presets) { show_incompatible = show_incompatible_presets; @@ -146,25 +153,33 @@ public: void update_dirty(); void msw_rescale() override; -private: - bool show_incompatible{false}; + void set_selection_changed_function(std::function sel_changed) { on_selection_changed = sel_changed; } }; //------------------------------------------ // PhysicalPrinterDialog //------------------------------------------ - +class ConfigOptionsGroup; class PhysicalPrinterDialog : public DPIDialog { - std::string printer_name; - std::string preset_name; + PhysicalPrinter m_printer; + DynamicPrintConfig* m_config { nullptr }; - wxTextCtrl* printer_text { nullptr }; - PresetComboBox* printer_presets; + wxTextCtrl* m_printer_name { nullptr }; + TabPresetComboBox* m_printer_presets { nullptr }; + ConfigOptionsGroup* m_optgroup { nullptr }; + + ScalableButton* m_printhost_browse_btn; + ScalableButton* m_printhost_test_btn; + ScalableButton* m_printhost_cafile_browse_btn {nullptr}; + + void build_printhost_settings(ConfigOptionsGroup* optgroup); + void update_octoprint_visible(); + void OnOK(wxEvent& event); public: - PhysicalPrinterDialog(const wxString& printer_name, int last_selected_preset); + PhysicalPrinterDialog(wxString printer_name); ~PhysicalPrinterDialog() {} protected: diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index bb19e139d..da5d51dc5 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -161,6 +161,13 @@ void Tab::create_preset_tab() // preset chooser m_presets_choice = new TabPresetComboBox(panel, m_type); + m_presets_choice->set_selection_changed_function([this](int selection) { + // unselect pthysical printer, if it was selected + m_preset_bundle->physical_printers.unselect_printer(); + // select preset + std::string selected_string = m_presets_choice->GetString(selection).ToUTF8().data(); + select_preset(selected_string); + }); auto color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); @@ -3022,6 +3029,9 @@ void Tab::select_preset(std::string preset_name, bool delete_current) if (canceled) { update_tab_ui(); + // unselect physical printer selection to the correct synchronization of the printer presets between Tab and Plater + if (m_type == Preset::TYPE_PRINTER) + m_preset_bundle->physical_printers.unselect_printer(); // Trigger the on_presets_changed event so that we also restore the previous value in the plater selector, // if this action was initiated from the plater. on_presets_changed();