diff --git a/resources/i18n/zh_cn/BambuStudio.mo b/resources/i18n/zh_cn/BambuStudio.mo index d05f269dd..a83927ce7 100644 Binary files a/resources/i18n/zh_cn/BambuStudio.mo and b/resources/i18n/zh_cn/BambuStudio.mo differ diff --git a/resources/images/back123.png b/resources/images/back123.png new file mode 100644 index 000000000..8bad28387 Binary files /dev/null and b/resources/images/back123.png differ diff --git a/resources/images/config_title.svg b/resources/images/config_title.svg new file mode 100644 index 000000000..989e2ceb5 --- /dev/null +++ b/resources/images/config_title.svg @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + diff --git a/resources/images/dalian_logo.svg b/resources/images/dalian_logo.svg new file mode 100644 index 000000000..faf1886be --- /dev/null +++ b/resources/images/dalian_logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/resources/images/ui_title.svg b/resources/images/ui_title.svg new file mode 100644 index 000000000..a727bb013 --- /dev/null +++ b/resources/images/ui_title.svg @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/images/version_title.svg b/resources/images/version_title.svg new file mode 100644 index 000000000..74139d7a2 --- /dev/null +++ b/resources/images/version_title.svg @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + diff --git a/src/libslic3r/PresetBundle.cpp b/src/libslic3r/PresetBundle.cpp index 50301422e..7f755bbe6 100644 --- a/src/libslic3r/PresetBundle.cpp +++ b/src/libslic3r/PresetBundle.cpp @@ -391,6 +391,9 @@ std::vector PresetBundle::get_current_project_embedded_presets() auto printer_presets = this->printers.get_project_embedded_presets(); if (!printer_presets.empty()) std::copy(printer_presets.begin(), printer_presets.end(), std::back_inserter(project_presets)); + auto config_presets = this->configs.get_project_embedded_presets(); + if (!config_presets.empty()) + std::copy(config_presets.begin(), config_presets.end(), std::back_inserter(project_presets)); BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(" finished, returned project_presets count %1%")%project_presets.size(); return project_presets; @@ -409,6 +412,7 @@ void PresetBundle::reset_project_embedded_presets() bool printer_reselect = this->printers.reset_project_embedded_presets(); bool filament_reselect = this->filaments.reset_project_embedded_presets(); bool print_reselect = this->prints.reset_project_embedded_presets(); + bool config_reselect = this->configs.reset_project_embedded_presets(); if (printer_reselect) { if (!prefer_printer.empty()) @@ -419,7 +423,7 @@ void PresetBundle::reset_project_embedded_presets() //this->update_multi_material_filament_presets(); this->update_compatible(PresetSelectCompatibleType::Never); } - else if (filament_reselect || print_reselect) { + else if (filament_reselect || print_reselect || config_reselect) { //Preset& current_printer = this->printers.get_selected_preset(); /*if (filament_reselect) { const std::vector &prefered_filament_profiles = current_printer.config.option("default_filament_profile")->values; @@ -577,8 +581,7 @@ PresetsConfigSubstitutions PresetBundle::load_user_presets(std::string user, For std::string config_selected_preset_name = configs.get_selected_preset().name; this->configs.load_presets(dir_user_presets, PRESET_CONFIG_NAME, substitutions, substitution_rule); configs.select_preset_by_name(config_selected_preset_name, false); - } - catch (const std::runtime_error& err) { + }catch (const std::runtime_error& err) { errors_cummulative += err.what(); } if (!errors_cummulative.empty()) throw Slic3r::RuntimeError(errors_cummulative); @@ -1396,15 +1399,18 @@ std::vector PresetBundle::merge_presets(PresetBundle &&other) std::vector duplicate_filaments = this->filaments .merge_presets(std::move(other.filaments), this->vendors); std::vector duplicate_sla_materials = this->sla_materials.merge_presets(std::move(other.sla_materials), this->vendors); std::vector duplicate_printers = this->printers .merge_presets(std::move(other.printers), this->vendors); + std::vector duplicate_configs = this->configs .merge_presets(std::move(other.configs), this->vendors); append(this->obsolete_presets.prints, std::move(other.obsolete_presets.prints)); append(this->obsolete_presets.sla_prints, std::move(other.obsolete_presets.sla_prints)); append(this->obsolete_presets.filaments, std::move(other.obsolete_presets.filaments)); append(this->obsolete_presets.sla_materials, std::move(other.obsolete_presets.sla_materials)); append(this->obsolete_presets.printers, std::move(other.obsolete_presets.printers)); + append(this->obsolete_presets.configs, std::move(other.obsolete_presets.configs)); append(duplicate_prints, std::move(duplicate_sla_prints)); append(duplicate_prints, std::move(duplicate_filaments)); append(duplicate_prints, std::move(duplicate_sla_materials)); append(duplicate_prints, std::move(duplicate_printers)); + append(duplicate_configs, std::move(duplicate_configs)); return duplicate_prints; } @@ -1415,11 +1421,14 @@ void PresetBundle::update_system_maps() this->filaments .update_map_system_profile_renamed(); this->sla_materials.update_map_system_profile_renamed(); this->printers .update_map_system_profile_renamed(); + //xiamian+ + this->configs .update_map_system_profile_renamed(); this->prints .update_map_alias_to_profile_name(); this->sla_prints .update_map_alias_to_profile_name(); this->filaments .update_map_alias_to_profile_name(); this->sla_materials.update_map_alias_to_profile_name(); + this->configs .update_map_alias_to_profile_name(); } static inline std::string remove_ini_suffix(const std::string &name) @@ -1439,6 +1448,12 @@ void PresetBundle::load_installed_printers(const AppConfig &config) for (auto &preset : printers) preset.set_visible_from_appconfig(config); } +void PresetBundle::load_installed_configs(const AppConfig& config) +{ + this->update_system_maps(); + for (auto& preset : configs) + preset.set_visible_from_appconfig(config); +} const std::string& PresetBundle::get_preset_name_by_alias( const Preset::Type& preset_type, const std::string& alias) const { @@ -1449,6 +1464,7 @@ const std::string& PresetBundle::get_preset_name_by_alias( const Preset::Type& p const PresetCollection& presets = preset_type == Preset::TYPE_PRINT ? prints : preset_type == Preset::TYPE_SLA_PRINT ? sla_prints : preset_type == Preset::TYPE_FILAMENT ? filaments : + preset_type == Preset::TYPE_CONFIG ? configs : sla_materials; return presets.get_preset_name_by_alias(alias); @@ -1480,10 +1496,13 @@ const int PresetBundle::get_required_hrc_by_filament_type(const std::string& fil void PresetBundle::save_changes_for_preset(const std::string& new_name, Preset::Type type, const std::vector& unselected_options, bool save_to_project) { - PresetCollection& presets = type == Preset::TYPE_PRINT ? prints : - type == Preset::TYPE_SLA_PRINT ? sla_prints : - type == Preset::TYPE_FILAMENT ? filaments : - type == Preset::TYPE_SLA_MATERIAL ? sla_materials : printers; + PresetCollection& presets = type == Preset::TYPE_PRINT ? prints : + type == Preset::TYPE_SLA_PRINT ? sla_prints : + type == Preset::TYPE_FILAMENT ? filaments : + type == Preset::TYPE_SLA_MATERIAL ? sla_materials : + type == Preset::TYPE_CONFIG ? configs : + printers; + // if we want to save just some from selected options if (!unselected_options.empty()) { @@ -1589,6 +1608,7 @@ void PresetBundle::load_selections(AppConfig &config, const PresetPreferences& p BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": enter, preferred printer_model_id %1%")%preferred_selection.printer_model_id; // Update visibility of presets based on application vendor / model / variant configuration. this->load_installed_printers(config); + this->load_installed_configs(config); // Update visibility of filament and sla material presets this->load_installed_filaments(config); @@ -1631,6 +1651,7 @@ void PresetBundle::load_selections(AppConfig &config, const PresetPreferences& p filaments.select_preset_by_name_strict(initial_filament_profile_name); sla_prints.select_preset_by_name_strict(initial_sla_print_profile_name); sla_materials.select_preset_by_name_strict(initial_sla_material_profile_name); + configs.select_preset_by_name_strict(initial_config_profile_name); // Load the names of the other filament profiles selected for a multi-material printer. // Load it even if the current printer technology is SLA. @@ -4043,6 +4064,7 @@ void PresetBundle::set_default_suppressed(bool default_suppressed) sla_prints.set_default_suppressed(default_suppressed); sla_materials.set_default_suppressed(default_suppressed); printers.set_default_suppressed(default_suppressed); + configs.set_default_suppressed(default_suppressed); } } // namespace Slic3r diff --git a/src/libslic3r/PresetBundle.hpp b/src/libslic3r/PresetBundle.hpp index a26c58121..a41dcca5e 100644 --- a/src/libslic3r/PresetBundle.hpp +++ b/src/libslic3r/PresetBundle.hpp @@ -227,6 +227,7 @@ public: // based on the user configuration. // If the "vendor" section is missing, enable all models and variants of the particular vendor. void load_installed_printers(const AppConfig &config); + void load_installed_configs(const AppConfig &config); const std::string& get_preset_name_by_alias(const Preset::Type& preset_type, const std::string& alias) const; diff --git a/src/slic3r/GUI/CreatePresetsDialog.cpp b/src/slic3r/GUI/CreatePresetsDialog.cpp new file mode 100644 index 000000000..27034b1de --- /dev/null +++ b/src/slic3r/GUI/CreatePresetsDialog.cpp @@ -0,0 +1,5081 @@ +#include "CreatePresetsDialog.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "libslic3r/PresetBundle.hpp" +#include "I18N.hpp" +#include "GUI_App.hpp" +#include "MsgDialog.hpp" +#include "FileHelp.hpp" +#include "Tab.hpp" +#include "MainFrame.hpp" + +#define NAME_OPTION_COMBOBOX_SIZE wxSize(FromDIP(200), FromDIP(24)) +#define FILAMENT_PRESET_COMBOBOX_SIZE wxSize(FromDIP(300), FromDIP(24)) +#define OPTION_SIZE wxSize(FromDIP(100), FromDIP(24)) +#define PRINTER_LIST_SIZE wxSize(-1, FromDIP(100)) +#define FILAMENT_LIST_SIZE wxSize(FromDIP(560), FromDIP(100)) +#define FILAMENT_OPTION_SIZE wxSize(FromDIP(-1), FromDIP(30)) +#define PRESET_TEMPLATE_SIZE wxSize(FromDIP(-1), FromDIP(100)) +#define PRINTER_SPACE_SIZE wxSize(FromDIP(80), FromDIP(24)) +#define ORIGIN_TEXT_SIZE wxSize(FromDIP(10), FromDIP(24)) +#define PRINTER_PRESET_VENDOR_SIZE wxSize(FromDIP(150), FromDIP(24)) +#define PRINTER_PRESET_MODEL_SIZE wxSize(FromDIP(280), FromDIP(24)) +#define STATIC_TEXT_COLOUR wxColour("#363636") +#define PRINTER_LIST_COLOUR wxColour("#EEEEEE") +#define FILAMENT_OPTION_COLOUR wxColour("#D9D9D9") +//#define SELECT_ALL_OPTION_COLOUR wxColour("#00AE42") +#define SELECT_ALL_OPTION_COLOUR wxColour("#009FF3") +#define DEFAULT_PROMPT_TEXT_COLOUR wxColour("#ACACAC") + +namespace Slic3r { +namespace GUI { + +static const std::vector filament_vendors = {"Polymaker", "OVERTURE", "Kexcelled", "HATCHBOX", "eSUN", "SUNLU", "Prusament", "Creality", "Protopasta", + "Anycubic", "Basf", "ELEGOO", "INLAND", "FLASHFORGE", "AMOLEN", "MIKA3D", "3DXTECH", "Duramic", + "Priline", "Eryone", "3Dgunius", "Novamaker", "Justmaker", "Giantarm", "iProspect"}; + +static const std::vector filament_types = {"PLA", "PLA+", "PLA Tough", "PETG", "ABS", "ASA", "FLEX", "HIPS", "PA", "PACF", + "NYLON", "PVA", "PC", "PCABS", "PCTG", "PCCF", "PP", "PEI", "PET", "PETG", + "PETGCF", "PTBA", "PTBA90A", "PEEK", "TPU93A", "TPU75D", "TPU", "TPU92A", "TPU98A", "Misc", + "TPE", "GLAZE", "Nylon", "CPE", "METAL", "ABST", "Carbon Fiber"}; + +static const std::vector printer_vendors = {"Anycubic", "Artillery", "BIBO", "BIQU", "Creality ENDER", "Creality CR", "Creality SERMOON", + "FLSun", "gCreate", "Geeetech", "INAT", "Infinity3D", "Jubilee", "LNL3D", + "LulzBot", "MakerGear", "Original Prusa", "Papapiu", "Print4Taste", "RatRig", "Rigid3D", + "Snapmaker", "Sovol", "TriLAB", "Trimaker", "Ultimaker", "Voron", "Zonestar"}; + +static const std::unordered_map> printer_model_map = + {{"Anycubic", {"Kossel Linear Plus", "Kossel Pulley(Linear)", "Mega Zero", "i3 Mega", "Predator"}}, + {"Artillery", {"sidewinder X1", "Genius", "Hornet"}}, + {"BIBO", {"BIBO2 Touch"}}, + {"BIQU", {"BX"}}, + {"Creality ENDER", {"Ender-3", "Ender-3 BLTouch", "Ender-3 Pro", "Ender-3 Neo", + "Ender-3 V2 Neo", "Ender-3 S1 Plus", "Ender-3 Max", "Ender-3 Max Neo", + "Ender-4", "Ender-5 Pro", "Ender-5 Pro", + "Ender-7", "Ender-2", "Ender-2 Pro"}}, + {"Creality CR", {"CR-5 Pro", "CR-5 Pro H", "CR-10 SMART", "CR-10 SMART Pro", "CR-10 Mini", + "CR-10", "CR-10 v3", "CR-10 S", "CR-10 v2", "CR-10 v2", + "CR-10 S Pro", "CR-10 S Pro v2", "CR-10 S4", "CR-10 S5", "CR-20", "CR-20 Pro", "CR-200B", + "CR-8"}}, + {"Creality SERMOON",{"Sermoon-D1", "Sermoon-V1", "Sermoon-V1 Pro"}}, + {"FLSun", {"FLSun QQs Pro", "FLSun Q5"}}, + {"gCreate", {"gMax 1.5XT Plus", "gMax 2", "gMax 2 Pro", "gMax 2 Dual 2in1", "gMax 2 Dual Chimera"}}, + {"Geeetech", {"Thunder", "Thunder Pro", "Mizar s", "Mizar Pro", "Mizar", "Mizar Max", + "Mizar M", "A10 Pro", "A10 M", "A10 T", "A20", "A20 M", + "A20T", "A30 Pro", "A30 M", "A30 T", "E180", "Me Ducer", + "Me creator", "Me Creator2", "GiantArmD200", "l3 ProB", "l3 Prow", "l3 ProC"}}, + {"INAT", {"Proton X Rail", "Proton x Rod", "Proton XE-750"}}, + {"Infinity3D", {"DEV-200", "DEV-350"}}, + {"Jubilee", {"Jubilee"}}, + {"LNL3D", {"D3 v2", "D3 Vulcan", "D5", "D6"}}, + {"LulzBot", {"Mini Aero", "Taz6 Aero"}}, + {"MakerGear", {"Micro", "M2(V4 Hotend)", "M2 Dual", "M3-single Extruder", "M3-Independent Dual Rev.0", "M3-Independent Dual Rev.0(Duplication Mode)", + "M3-Independent Dual Rev.1", "M3-Independent Dual Rev.1(Duplication Mode)", "ultra One", "Ultra One (DuplicationMode)"}}, + {"Original Prusa", {"MK4", "SL1S SPEED", "MMU3"}}, + {"Papapiu", {"N1s"}}, + {"Print4Taste", {"mycusini 2.0"}}, + {"RatRig", {"V-core-3 300mm", "V-Core-3 400mm", "V-Core-3 500mm", "V-Minion"}}, + {"Rigid3D", {"Zero2", "Zero3"}}, + {"Snapmaker", {"A250", "A350"}}, + {"Sovol", {"SV06", "SV06 PLUS", "SV05", "SV04", "SV03 / SV03 BLTOUCH", "SVO2 / SV02 BLTOUCH", "SVO1 / SV01 BLToUCH", "SV01 PRO"}}, + {"TriLAB", {"AzteQ Industrial","AzteQ Dynamic", "DeltiQ 2", "DeltiQ 2 Plus", "DeltiQ 2 + FlexPrint 2", "DeltiQ 2 Plus + FlexPrint 2", "DeltiQ 2 +FlexPrint", + "DeltiQ 2 Plus + FlexPrint", "DeltiQ M", "DeltiQ L", "DeltiQ XL"}}, + {"Trimaker", {"Nebula cloud", "Nebula", "Cosmos ll"}}, + {"Ultimaker", {"Ultimaker 2"}}, + {"Voron", {"v2 250mm3", "v2 300mm3", "v2 350mm3", "v1 250mm3", "v1 300mm3", "v1 350mm3", + "Zero 120mm3", "Switchwire"}}, + {"Zonestar", {"Z5", "Z6", "Z5x", "Z8", "Z9"}}}; + +static std::vector nozzle_diameter_vec = {"0.4", "0.2", "0.25", "0.3", "0.35", "0.5", "0.6", "0.75", "0.8", "1.0", "1.2"}; +static std::unordered_map nozzle_diameter_map = {{"0.2", 0.2}, {"0.25", 0.25}, {"0.3", 0.3}, {"0.35", 0.35}, + {"0.4", 0.4}, {"0.5", 0.5}, {"0.6", 0.6}, {"0.75", 0.75}, + {"0.8", 0.8}, {"1.0", 1.0}, {"1.2", 1.2}}; + +static std::set cannot_input_key = {9, 10, 13, 33, 35, 36, 37, 38, 40, 41, 42, 44, 46, 47, 59, 60, 62, 63, 64, 92, 94, 95, 124, 126}; + +static std::set special_key = {'\n', '\t', '\r', '\v', '@', ';'}; + +static std::string remove_special_key(const std::string &str) +{ + std::string res_str; + for (char c : str) { + if (special_key.find(c) == special_key.end()) { + res_str.push_back(c); + } + } + return res_str; +} + +static bool str_is_all_digit(const std::string &str) { + for (const char &c : str) { + if (!std::isdigit(c)) return false; + } + return true; +} + +static bool delete_filament_preset_by_name(std::string delete_preset_name, std::string &selected_preset_name) +{ + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format("select preset, name %1%") % delete_preset_name; + if (delete_preset_name.empty()) return false; + + // Find an alternate preset to be selected after the current preset is deleted. + PresetCollection &m_presets = wxGetApp().preset_bundle->filaments; + if (delete_preset_name == selected_preset_name) { + const std::deque &presets = m_presets.get_presets(); + size_t idx_current = m_presets.get_idx_selected(); + + // Find the visible preset. + size_t idx_new = idx_current; + if (idx_current > presets.size()) idx_current = presets.size(); + if (idx_current < 0) idx_current = 0; + if (idx_new < presets.size()) + for (; idx_new < presets.size() && (presets[idx_new].name == delete_preset_name || !presets[idx_new].is_visible); ++idx_new) + ; + if (idx_new == presets.size()) + for (idx_new = idx_current - 1; idx_new > 0 && (presets[idx_new].name == delete_preset_name || !presets[idx_new].is_visible); --idx_new) + ; + selected_preset_name = presets[idx_new].name; + BOOST_LOG_TRIVIAL(info) << boost::format("cause by delete current ,choose the next visible, idx %1%, name %2%") % idx_new % selected_preset_name; + } + + try { + // BBS delete preset + Preset *need_delete_preset = m_presets.find_preset(delete_preset_name); + if (!need_delete_preset) BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" can't find delete preset and name: %1%") % delete_preset_name; + if (!need_delete_preset->setting_id.empty()) { + BOOST_LOG_TRIVIAL(info) << "delete preset = " << need_delete_preset->name << ", setting_id = " << need_delete_preset->setting_id; + m_presets.set_sync_info_and_save(need_delete_preset->name, need_delete_preset->setting_id, "delete", 0); + wxGetApp().delete_preset_from_cloud(need_delete_preset->setting_id); + } else { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" can't preset setting id is empty and name: %1%") % delete_preset_name; + } + if (m_presets.get_edited_preset().name == delete_preset_name) { + m_presets.discard_current_changes(); + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format("delete preset dirty and cancelled"); + } + m_presets.delete_preset(need_delete_preset->name); + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " preset has been delete from filaments, and preset name is: " << delete_preset_name; + } catch (const std::exception &ex) { + // FIXME add some error reporting! + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format("found exception when delete: %1% and preset name: %%") % ex.what() % delete_preset_name; + return false; + } + + return true; +} + +static std::string get_curr_time() +{ + std::chrono::system_clock::time_point now = std::chrono::system_clock::now(); + + std::time_t time = std::chrono::system_clock::to_time_t(now); + + std::tm local_time = *std::localtime(&time); + std::ostringstream time_stream; + time_stream << std::put_time(&local_time, "%Y_%m_%d_%H_%M_%S"); + + std::string current_time = time_stream.str(); + return current_time; +} + +static std::string get_curr_timestmp() +{ + std::time_t currentTime = std::time(nullptr); + std::ostringstream oss; + oss << currentTime; + std::string timestampString = oss.str(); + return timestampString; +} + +static void get_filament_compatible_printer(Preset* preset, vector& printers) +{ + auto compatible_printers = dynamic_cast(preset->config.option("compatible_printers")); + if (compatible_printers == nullptr) return; + for (const std::string &printer_name : compatible_printers->values) { + printers.push_back(printer_name); + } +} + +static wxBoxSizer* create_checkbox(wxWindow* parent, Preset* preset, wxString& preset_name, std::vector>& preset_checkbox) +{ + wxBoxSizer *sizer = new wxBoxSizer(wxHORIZONTAL); + ::CheckBox * checkbox = new ::CheckBox(parent); + sizer->Add(checkbox, 0, 0, 0); + preset_checkbox.push_back(std::make_pair(checkbox, preset)); + wxStaticText *preset_name_str = new wxStaticText(parent, wxID_ANY, preset_name); + wxToolTip * toolTip = new wxToolTip(preset_name); + preset_name_str->SetToolTip(toolTip); + sizer->Add(preset_name_str, 0, wxLEFT, 5); + return sizer; +} + +static wxBoxSizer *create_checkbox(wxWindow *parent, std::string &compatible_printer, Preset* preset, std::unordered_map<::CheckBox *, std::pair> &ptinter_compatible_filament_preset) +{ + wxBoxSizer *sizer = new wxBoxSizer(wxHORIZONTAL); + ::CheckBox *checkbox = new ::CheckBox(parent); + sizer->Add(checkbox, 0, 0, 0); + ptinter_compatible_filament_preset[checkbox] = std::make_pair(compatible_printer, preset); + wxStaticText *preset_name_str = new wxStaticText(parent, wxID_ANY, wxString::FromUTF8(compatible_printer)); + sizer->Add(preset_name_str, 0, wxLEFT, 5); + return sizer; +} + +static wxBoxSizer *create_checkbox(wxWindow *parent, wxString &preset_name, std::vector> &preset_checkbox) +{ + wxBoxSizer *sizer = new wxBoxSizer(wxHORIZONTAL); + ::CheckBox *checkbox = new ::CheckBox(parent); + sizer->Add(checkbox, 0, 0, 0); + preset_checkbox.push_back(std::make_pair(checkbox, into_u8(preset_name))); + wxStaticText *preset_name_str = new wxStaticText(parent, wxID_ANY, preset_name); + sizer->Add(preset_name_str, 0, wxLEFT, 5); + return sizer; +} + +static wxArrayString get_exist_vendor_choices(VendorMap& vendors) +{ + wxArrayString choices; + PresetBundle temp_preset_bundle; + temp_preset_bundle.load_system_models_from_json(ForwardCompatibilitySubstitutionRule::EnableSystemSilent); + PresetBundle *preset_bundle = wxGetApp().preset_bundle; + + VendorProfile users_models = preset_bundle->get_custom_vendor_models(); + + vendors = temp_preset_bundle.vendors; + + if (!users_models.models.empty()) { + vendors[users_models.name] = users_models; + } + + for (const pair &vendor : vendors) { + if (vendor.second.models.empty() || vendor.second.id.empty()) continue; + choices.Add(vendor.first); + } + return choices; +} + +static std::string get_machine_name(const std::string &preset_name) +{ + size_t index_at = preset_name.find_last_of("@"); + if (std::string::npos == index_at) { + return ""; + } else { + return preset_name.substr(index_at + 1); + } +} + +static std::string get_filament_name(std::string &preset_name) +{ + size_t index_at = preset_name.find_last_of("@"); + if (std::string::npos == index_at) { + return preset_name; + } else { + return preset_name.substr(0, index_at - 1); + } +} + +static wxBoxSizer *create_preset_tree(wxWindow *parent, std::pair>> printer_and_preset) +{ + wxTreeCtrl *treeCtrl = new wxTreeCtrl(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTR_DEFAULT_STYLE | wxNO_BORDER); + wxColour backgroundColor = parent->GetBackgroundColour(); + treeCtrl->SetBackgroundColour(backgroundColor); + + wxString printer_name = wxString::FromUTF8(printer_and_preset.first); + wxTreeItemId rootId = treeCtrl->AddRoot(printer_name); + int row = 1; + for (std::shared_ptr preset : printer_and_preset.second) { + wxString preset_name = wxString::FromUTF8(preset->name); + wxTreeItemId childId1 = treeCtrl->AppendItem(rootId, preset_name); + row++; + } + + treeCtrl->Expand(rootId); + wxBoxSizer *sizer = new wxBoxSizer(wxVERTICAL); + treeCtrl->SetMinSize(wxSize(-1, row * 22)); + treeCtrl->SetMaxSize(wxSize(-1, row * 22)); + sizer->Add(treeCtrl, 0, wxEXPAND | wxALL, 0); + + return sizer; +} + +static std::string get_vendor_name(std::string& preset_name) +{ + if (preset_name.empty()) return ""; + std::string vendor_name = preset_name.substr(preset_name.find_first_not_of(' ')); //remove the name prefix space + size_t index_at = vendor_name.find(" "); + if (std::string::npos == index_at) { + return vendor_name; + } else { + vendor_name = vendor_name.substr(0, index_at); + return vendor_name; + } +} + +static wxBoxSizer *create_select_filament_preset_checkbox(wxWindow * parent, + std::string & compatible_printer, + std::vector presets, + std::unordered_map<::CheckBox *, std::pair> &machine_filament_preset) +{ + wxBoxSizer *horizontal_sizer = new wxBoxSizer(wxHORIZONTAL); + wxBoxSizer *checkbox_sizer = new wxBoxSizer(wxVERTICAL); + ::CheckBox *checkbox = new ::CheckBox(parent); + checkbox_sizer->Add(checkbox, 0, wxEXPAND | wxRIGHT, 5); + + wxBoxSizer *combobox_sizer = new wxBoxSizer(wxVERTICAL); + wxStaticText *machine_name_str = new wxStaticText(parent, wxID_ANY, wxString::FromUTF8(compatible_printer)); + ComboBox * combobox = new ComboBox(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(200, 24), 0, nullptr, wxCB_READONLY); + combobox->SetBackgroundColor(PRINTER_LIST_COLOUR); + combobox->SetBorderColor(*wxWHITE); + combobox->SetLabel(_L("Select filament preset")); + combobox->Bind(wxEVT_COMBOBOX, [combobox, checkbox, presets, &machine_filament_preset, compatible_printer](wxCommandEvent &e) { + combobox->SetLabelColor(*wxBLACK); + wxString preset_name = combobox->GetStringSelection(); + checkbox->SetValue(true); + for (Preset *preset : presets) { + if (preset_name == wxString::FromUTF8(preset->name)) { + machine_filament_preset[checkbox] = std::make_pair(compatible_printer, preset); + } + } + e.Skip(); + }); + combobox_sizer->Add(machine_name_str, 0, wxEXPAND, 0); + combobox_sizer->Add(combobox, 0, wxEXPAND | wxTOP, 5); + + wxArrayString choices; + for (Preset *preset : presets) { + choices.Add(wxString::FromUTF8(preset->name)); + } + combobox->Set(choices); + + horizontal_sizer->Add(checkbox_sizer); + horizontal_sizer->Add(combobox_sizer); + return horizontal_sizer; +} + +static wxString get_curr_radio_type(std::vector> &radio_btns) +{ + for (std::pair radio_string : radio_btns) { + if (radio_string.first->GetValue()) { + return radio_string.second; + } + } + return ""; +} + +static std::string calculate_md5(const std::string &input) +{ + unsigned char digest[MD5_DIGEST_LENGTH]; + std::string md5; + + EVP_MD_CTX *mdContext = EVP_MD_CTX_new(); + EVP_DigestInit(mdContext, EVP_md5()); + EVP_DigestUpdate(mdContext, input.c_str(), input.length()); + EVP_DigestFinal(mdContext, digest, nullptr); + EVP_MD_CTX_free(mdContext); + + char hexDigest[MD5_DIGEST_LENGTH * 2 + 1]; + for (int i = 0; i < MD5_DIGEST_LENGTH; ++i) { sprintf(hexDigest + (i * 2), "%02x", digest[i]); } + hexDigest[MD5_DIGEST_LENGTH * 2] = '\0'; + + md5 = std::string(hexDigest); + return md5; +} + +static std::string get_filament_id(std::string vendor_typr_serial) +{ + std::unordered_map> filament_id_to_filament_name; + + // temp filament presets + PresetBundle temp_preset_bundle; + temp_preset_bundle.load_system_filaments_json(Slic3r::ForwardCompatibilitySubstitutionRule::EnableSilent); + std::string dir_user_presets = wxGetApp().app_config->get("preset_folder"); + if (dir_user_presets.empty()) { + temp_preset_bundle.load_user_presets(DEFAULT_USER_FOLDER_NAME, ForwardCompatibilitySubstitutionRule::EnableSilent); + } else { + temp_preset_bundle.load_user_presets(dir_user_presets, ForwardCompatibilitySubstitutionRule::EnableSilent); + } + const std::deque &filament_presets = temp_preset_bundle.filaments.get_presets(); + + for (const Preset &preset : filament_presets) { + std::string preset_name = preset.name; + size_t index_at = preset_name.find_first_of('@'); + if (index_at == std::string::npos) { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " filament preset name has no @ and name is: " << preset_name; + continue; + } + std::string filament_name = preset_name.substr(0, index_at - 1); + if (filament_name == vendor_typr_serial && preset.filament_id != "null") + return preset.filament_id; + filament_id_to_filament_name[preset.filament_id].insert(filament_name); + } + // global filament presets + PresetBundle * preset_bundle = wxGetApp().preset_bundle; + std::map> temp_filament_id_to_presets = preset_bundle->filaments.get_filament_presets(); + for (std::pair> filament_id_to_presets : temp_filament_id_to_presets) { + if (filament_id_to_presets.first.empty()) continue; + for (const Preset *preset : filament_id_to_presets.second) { + std::string preset_name = preset->name; + size_t index_at = preset_name.find_first_of('@'); + if (index_at == std::string::npos) { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " filament preset name has no @ and name is: " << preset_name; + continue; + } + std::string filament_name = preset_name.substr(0, index_at - 1); + if (filament_name == vendor_typr_serial && preset->filament_id != "null") + return preset->filament_id; + filament_id_to_filament_name[preset->filament_id].insert(filament_name); + } + } + + std::string user_filament_id = "P" + calculate_md5(vendor_typr_serial).substr(0, 7); + + while (filament_id_to_filament_name.find(user_filament_id) != filament_id_to_filament_name.end()) {//find same filament id + bool have_same_filament_name = false; + for (const std::string &name : filament_id_to_filament_name.find(user_filament_id)->second) { + if (name == vendor_typr_serial) { + have_same_filament_name = true; + break; + } + } + if (have_same_filament_name) { + break; + } + else { //Different names correspond to the same filament id + user_filament_id = "P" + calculate_md5(vendor_typr_serial + get_curr_time()).substr(0, 7); + } + } + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " filament name is: " << vendor_typr_serial << "and create filament_id is: " << user_filament_id; + return user_filament_id; +} + +static json get_config_json(const Preset* preset) { + json j; + // record the headers + j[BBL_JSON_KEY_VERSION] = preset->version.to_string(); + j[BBL_JSON_KEY_NAME] = preset->name; + j[BBL_JSON_KEY_FROM] = ""; + + DynamicPrintConfig config = preset->config; + + // record all the key-values + for (const std::string &opt_key : config.keys()) { + const ConfigOption *opt = config.option(opt_key); + if (opt->is_scalar()) { + if (opt->type() == coString) + // keep \n, \r, \t + j[opt_key] = (dynamic_cast(opt))->value; + else + j[opt_key] = opt->serialize(); + } else { + const ConfigOptionVectorBase *vec = static_cast(opt); + std::vector string_values = vec->vserialize(); + + json j_array(string_values); + j[opt_key] = j_array; + } + } + + return j; +} + +static char* read_json_file(const std::string &preset_path) +{ + FILE *json_file = boost::nowide::fopen(boost::filesystem::path(preset_path).make_preferred().string().c_str(), "rb"); + if (json_file == NULL) { + BOOST_LOG_TRIVIAL(info) << "Failed to open JSON file: " << preset_path; + return NULL; + } + fseek(json_file, 0, SEEK_END); // seek to end + long file_size = ftell(json_file); // get file size + fseek(json_file, 0, SEEK_SET); // seek to start + + char * json_contents = (char *) malloc(file_size); + if (json_contents == NULL) { + BOOST_LOG_TRIVIAL(info) << "Failed to allocate memory for JSON file "; + fclose(json_file); + return NULL; + } + + fread(json_contents, 1, file_size, json_file); + fclose(json_file); + + return json_contents; +} + +static std::string get_printer_nozzle_diameter(std::string printer_name) { + + size_t index = printer_name.find(" nozzle"); + if (std::string::npos == index) { + return ""; + } + std::string nozzle = printer_name.substr(0, index); + size_t last_space_index = nozzle.find_last_of(" "); + if (std::string::npos == index) { + return ""; + } + return nozzle.substr(last_space_index + 1); +} + +static void adjust_dialog_in_screen(DPIDialog* dialog) { + wxSize screen_size = wxGetDisplaySize(); + int pos_x, pos_y, size_x, size_y, screen_width, screen_height, dialog_x, dialog_y; + pos_x = dialog->GetPosition().x; + pos_y = dialog->GetPosition().y; + size_x = dialog->GetSize().x; + size_y = dialog->GetSize().y; + screen_width = screen_size.GetWidth(); + screen_height = screen_size.GetHeight(); + dialog_x = pos_x; + dialog_y = pos_y; + if (pos_x + size_x > screen_width) { + int exceed_x = pos_x + size_x - screen_width; + dialog_x -= exceed_x; + } + if (pos_y + size_y > screen_height - 50) { + int exceed_y = pos_y + size_y - screen_height + 50; + dialog_y -= exceed_y; + } + if (pos_x != dialog_x || pos_y != dialog_y) { dialog->SetPosition(wxPoint(dialog_x, dialog_y)); } +} + +CreateFilamentPresetDialog::CreateFilamentPresetDialog(wxWindow *parent) + : DPIDialog(parent ? parent : nullptr, wxID_ANY, _L("Create Filament"), wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX | wxCENTRE) +{ + m_create_type.base_filament = _L("Create Based on Current Filament"); + m_create_type.base_filament_preset = _L("Copy Current Filament Preset "); + get_all_filament_presets(); + + this->SetBackgroundColour(*wxWHITE); + this->SetSize(wxSize(FromDIP(600), FromDIP(480))); + + std::string icon_path = (boost::format("%1%/images/BambuStudioTitle.ico") % resources_dir()).str(); + SetIcon(wxIcon(encode_path(icon_path.c_str()), wxBITMAP_TYPE_ICO)); + + wxBoxSizer *m_main_sizer = new wxBoxSizer(wxVERTICAL); + // top line + auto m_line_top = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(-1, 1), wxTAB_TRAVERSAL); + m_line_top->SetBackgroundColour(wxColour(0xA6, 0xa9, 0xAA)); + m_main_sizer->Add(m_line_top, 0, wxEXPAND, 0); + m_main_sizer->Add(0, 0, 0, wxTOP, FromDIP(5)); + + wxStaticText *basic_infomation = new wxStaticText(this, wxID_ANY, _L("Basic Information")); + basic_infomation->SetFont(Label::Head_16); + m_main_sizer->Add(basic_infomation, 0, wxLEFT, FromDIP(10)); + + m_main_sizer->Add(create_item(FilamentOptionType::VENDOR), 0, wxEXPAND | wxALL, FromDIP(5)); + m_main_sizer->Add(create_item(FilamentOptionType::TYPE), 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(5)); + m_main_sizer->Add(create_item(FilamentOptionType::SERIAL), 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(5)); + + // divider line + auto line_divider = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(-1, 1), wxTAB_TRAVERSAL); + line_divider->SetBackgroundColour(wxColour(0xA6, 0xa9, 0xAA)); + m_main_sizer->Add(line_divider, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(10)); + m_main_sizer->Add(0, 0, 0, wxTOP, FromDIP(5)); + + wxStaticText *presets_infomation = new wxStaticText(this, wxID_ANY, _L("Add Filament Preset under this filament")); + presets_infomation->SetFont(Label::Head_16); + m_main_sizer->Add(presets_infomation, 0, wxLEFT | wxRIGHT, FromDIP(15)); + + m_main_sizer->Add(create_item(FilamentOptionType::FILAMENT_PRESET), 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(5)); + + m_filament_preset_text = new wxStaticText(this, wxID_ANY, _L("We could create the filament presets for your following printer:"), wxDefaultPosition, wxDefaultSize); + m_main_sizer->Add(m_filament_preset_text, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(15)); + + m_scrolled_preset_panel = new wxScrolledWindow(this, wxID_ANY); + m_scrolled_preset_panel->SetMaxSize(wxSize(-1, FromDIP(350))); + m_scrolled_preset_panel->SetBackgroundColour(*wxWHITE); + m_scrolled_preset_panel->SetScrollRate(5, 5); + m_scrolled_sizer = new wxBoxSizer(wxVERTICAL); + m_scrolled_sizer->Add(create_item(FilamentOptionType::PRESET_FOR_PRINTER), 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(5)); + m_scrolled_sizer->Add(0, 0, 0, wxTOP, FromDIP(5)); + m_scrolled_preset_panel->SetSizerAndFit(m_scrolled_sizer); + m_main_sizer->Add(m_scrolled_preset_panel, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(10)); + m_main_sizer->Add(create_button_item(), 0, wxEXPAND | wxALL, FromDIP(10)); + + get_all_visible_printer_name(); + select_curr_radiobox(m_create_type_btns, 0); + + this->SetSizer(m_main_sizer); + + Layout(); + Fit(); + + wxGetApp().UpdateDlgDarkUI(this); +} + +CreateFilamentPresetDialog::~CreateFilamentPresetDialog() +{ + for (std::pair preset : m_all_presets_map) { + Preset *p = preset.second; + if (p) { + delete p; + p = nullptr; + } + } +} + +void CreateFilamentPresetDialog::on_dpi_changed(const wxRect &suggested_rect) { + + m_button_create->SetMinSize(wxSize(FromDIP(58), FromDIP(24))); + m_button_create->SetMaxSize(wxSize(FromDIP(58), FromDIP(24))); + m_button_create->SetCornerRadius(FromDIP(12)); + m_button_cancel->SetMinSize(wxSize(FromDIP(58), FromDIP(24))); + m_button_cancel->SetMaxSize(wxSize(FromDIP(58), FromDIP(24))); + m_button_cancel->SetCornerRadius(FromDIP(12)); + + Layout(); +} + +bool CreateFilamentPresetDialog::is_check_box_selected() +{ + for (const std::pair<::CheckBox *, std::pair> &checkbox_preset : m_filament_preset) { + if (checkbox_preset.first->GetValue()) { return true; } + } + + for (const std::pair<::CheckBox *, std::pair> &checkbox_preset : m_machint_filament_preset) { + if (checkbox_preset.first->GetValue()) { return true; } + } + + return false; +} + +wxBoxSizer *CreateFilamentPresetDialog::create_item(FilamentOptionType option_type) +{ + + wxSizer *item = nullptr; + switch (option_type) { + case VENDOR: return create_vendor_item(); + case TYPE: return create_type_item(); + case SERIAL: return create_serial_item(); + case FILAMENT_PRESET: return create_filament_preset_item(); + case PRESET_FOR_PRINTER: return create_filament_preset_for_printer_item(); + default: return nullptr; + } +} +wxBoxSizer *CreateFilamentPresetDialog::create_vendor_item() +{ + wxBoxSizer *horizontal_sizer = new wxBoxSizer(wxHORIZONTAL); + + wxBoxSizer * optionSizer = new wxBoxSizer(wxVERTICAL); + wxStaticText *static_vendor_text = new wxStaticText(this, wxID_ANY, _L("Vendor"), wxDefaultPosition, wxDefaultSize); + optionSizer->Add(static_vendor_text, 0, wxEXPAND | wxALL, 0); + optionSizer->SetMinSize(OPTION_SIZE); + horizontal_sizer->Add(optionSizer, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(5)); + + wxArrayString choices; + for (const wxString &vendor : filament_vendors) { + choices.push_back(vendor); + } + + wxBoxSizer *vendor_sizer = new wxBoxSizer(wxHORIZONTAL); + m_filament_vendor_combobox = new ComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, NAME_OPTION_COMBOBOX_SIZE, 0, nullptr, wxCB_READONLY); + m_filament_vendor_combobox->SetLabel(_L("Select Vendor")); + m_filament_vendor_combobox->SetLabelColor(DEFAULT_PROMPT_TEXT_COLOUR); + m_filament_vendor_combobox->Set(choices); + m_filament_vendor_combobox->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent &e) { + m_filament_vendor_combobox->SetLabelColor(*wxBLACK); + e.Skip(); + }); + vendor_sizer->Add(m_filament_vendor_combobox, 0, wxEXPAND | wxALL, 0); + wxBoxSizer *textInputSizer = new wxBoxSizer(wxVERTICAL); + m_filament_custom_vendor_input = new TextInput(this, wxEmptyString, wxEmptyString, wxEmptyString, wxDefaultPosition, NAME_OPTION_COMBOBOX_SIZE, wxTE_PROCESS_ENTER); + m_filament_custom_vendor_input->GetTextCtrl()->SetMaxLength(50); + m_filament_custom_vendor_input->SetSize(NAME_OPTION_COMBOBOX_SIZE); + textInputSizer->Add(m_filament_custom_vendor_input, 0, wxEXPAND | wxALL, 0); + m_filament_custom_vendor_input->GetTextCtrl()->SetHint(_L("Input Custom Vendor")); + m_filament_custom_vendor_input->GetTextCtrl()->Bind(wxEVT_CHAR, [this](wxKeyEvent &event) { + int key = event.GetKeyCode(); + if (cannot_input_key.find(key) != cannot_input_key.end()) { + event.Skip(false); + return; + } + event.Skip(); + }); + m_filament_custom_vendor_input->Hide(); + vendor_sizer->Add(textInputSizer, 0, wxEXPAND | wxALIGN_CENTER_VERTICAL, FromDIP(10)); + + wxBoxSizer *comboBoxSizer = new wxBoxSizer(wxVERTICAL); + wxBoxSizer *checkbox_sizer = new wxBoxSizer(wxHORIZONTAL); + m_can_not_find_vendor_checkbox = new ::CheckBox(this); + + checkbox_sizer->Add(m_can_not_find_vendor_checkbox, 0, wxALIGN_CENTER, 0); + checkbox_sizer->Add(0, 0, 0, wxEXPAND | wxRIGHT, FromDIP(5)); + + wxStaticText *m_can_not_find_vendor_text = new wxStaticText(this, wxID_ANY, _L("Can't find vendor I want"), wxDefaultPosition, wxDefaultSize, 0); + m_can_not_find_vendor_text->SetFont(::Label::Body_13); + + wxSize size = m_can_not_find_vendor_text->GetTextExtent(_L("Can't find vendor I want")); + m_can_not_find_vendor_text->SetMinSize(wxSize(size.x + FromDIP(4), -1)); + m_can_not_find_vendor_text->Wrap(-1); + checkbox_sizer->Add(m_can_not_find_vendor_text, 0, wxALIGN_CENTER, 0); + + m_can_not_find_vendor_checkbox->Bind(wxEVT_TOGGLEBUTTON, [this](wxCommandEvent &e) { + bool value = m_can_not_find_vendor_checkbox->GetValue(); + if (value) { + m_can_not_find_vendor_checkbox->SetValue(true); + m_filament_vendor_combobox->Hide(); + m_filament_custom_vendor_input->Show(); + } else { + m_can_not_find_vendor_checkbox->SetValue(false); + m_filament_vendor_combobox->Show(); + m_filament_custom_vendor_input->Hide(); + } + Refresh(); + Layout(); + Fit(); + }); + + comboBoxSizer->Add(vendor_sizer, 0, wxEXPAND | wxTOP, FromDIP(5)); + comboBoxSizer->Add(checkbox_sizer, 0, wxEXPAND | wxTOP, FromDIP(5)); + horizontal_sizer->Add(comboBoxSizer, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(5)); + + return horizontal_sizer; + +} + +wxBoxSizer *CreateFilamentPresetDialog::create_type_item() +{ + wxBoxSizer *horizontal_sizer = new wxBoxSizer(wxHORIZONTAL); + + wxBoxSizer * optionSizer = new wxBoxSizer(wxVERTICAL); + wxStaticText *static_type_text = new wxStaticText(this, wxID_ANY, _L("Type"), wxDefaultPosition, wxDefaultSize); + optionSizer->Add(static_type_text, 0, wxEXPAND | wxALL, 0); + optionSizer->SetMinSize(OPTION_SIZE); + horizontal_sizer->Add(optionSizer, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(5)); + + wxArrayString filament_type; + for (const wxString &filament : m_system_filament_types_set) { + filament_type.Add(filament); + } + + wxBoxSizer *comboBoxSizer = new wxBoxSizer(wxVERTICAL); + m_filament_type_combobox = new ComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, NAME_OPTION_COMBOBOX_SIZE, 0, nullptr, wxCB_READONLY); + m_filament_type_combobox->SetLabel(_L("Select Type")); + m_filament_type_combobox->SetLabelColor(DEFAULT_PROMPT_TEXT_COLOUR); + m_filament_type_combobox->Set(filament_type); + comboBoxSizer->Add(m_filament_type_combobox, 0, wxEXPAND | wxALL, 0); + horizontal_sizer->Add(comboBoxSizer, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(5)); + + m_filament_type_combobox->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent &e) { + m_filament_type_combobox->SetLabelColor(*wxBLACK); + const wxString &curr_create_type = curr_create_filament_type(); + clear_filament_preset_map(); + if (curr_create_type == m_create_type.base_filament) { + wxArrayString filament_preset_choice = get_filament_preset_choices(); + m_filament_preset_combobox->Set(filament_preset_choice); + m_filament_preset_combobox->SetLabel(_L("Select Filament Preset")); + m_filament_preset_combobox->SetLabelColor(DEFAULT_PROMPT_TEXT_COLOUR); + + } else if (curr_create_type == m_create_type.base_filament_preset) { + get_filament_presets_by_machine(); + } + m_scrolled_preset_panel->SetSizerAndFit(m_scrolled_sizer); + + update_dialog_size(); + e.Skip(); + }); + + return horizontal_sizer; +} + +wxBoxSizer *CreateFilamentPresetDialog::create_serial_item() +{ + wxBoxSizer *horizontal_sizer = new wxBoxSizer(wxHORIZONTAL); + + wxBoxSizer * optionSizer = new wxBoxSizer(wxVERTICAL); + wxStaticText *static_serial_text = new wxStaticText(this, wxID_ANY, _L("Serial"), wxDefaultPosition, wxDefaultSize); + optionSizer->Add(static_serial_text, 0, wxEXPAND | wxALL, 0); + optionSizer->SetMinSize(OPTION_SIZE); + horizontal_sizer->Add(optionSizer, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(5)); + + wxBoxSizer *comboBoxSizer = new wxBoxSizer(wxVERTICAL); + m_filament_serial_input = new TextInput(this, "", "", "", wxDefaultPosition, NAME_OPTION_COMBOBOX_SIZE, wxTE_PROCESS_ENTER); + m_filament_serial_input->GetTextCtrl()->SetMaxLength(50); + comboBoxSizer->Add(m_filament_serial_input, 0, wxEXPAND | wxALL, 0); + m_filament_serial_input->GetTextCtrl()->Bind(wxEVT_CHAR, [this](wxKeyEvent &event) { + int key = event.GetKeyCode(); + if (cannot_input_key.find(key) != cannot_input_key.end()) { + event.Skip(false); + return; + } + event.Skip(); + }); + + wxStaticText *static_eg_text = new wxStaticText(this, wxID_ANY, _L("e.g. Basic, Matte, Silk, Marble"), wxDefaultPosition, wxDefaultSize); + static_eg_text->SetForegroundColour(wxColour("#6B6B6B")); + static_eg_text->SetFont(::Label::Body_12); + comboBoxSizer->Add(static_eg_text, 0, wxEXPAND | wxTOP, FromDIP(5)); + horizontal_sizer->Add(comboBoxSizer, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(5)); + + return horizontal_sizer; +} + +wxBoxSizer *CreateFilamentPresetDialog::create_filament_preset_item() +{ + wxBoxSizer *horizontal_sizer = new wxBoxSizer(wxHORIZONTAL); + + wxBoxSizer * optionSizer = new wxBoxSizer(wxVERTICAL); + wxStaticText *static_filament_preset_text = new wxStaticText(this, wxID_ANY, _L("Filament Preset"), wxDefaultPosition, wxDefaultSize); + optionSizer->Add(static_filament_preset_text, 0, wxEXPAND | wxALL, 0); + optionSizer->SetMinSize(OPTION_SIZE); + horizontal_sizer->Add(optionSizer, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(10)); + + wxBoxSizer * comboBoxSizer = new wxBoxSizer(wxVERTICAL); + comboBoxSizer->Add(create_radio_item(m_create_type.base_filament, this, wxEmptyString, m_create_type_btns), 0, wxEXPAND | wxALL, 0); + + m_filament_preset_combobox = new ComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, FILAMENT_PRESET_COMBOBOX_SIZE, 0, nullptr, wxCB_READONLY); + m_filament_preset_combobox->SetLabel(_L("Select Filament Preset")); + m_filament_preset_combobox->SetLabelColor(DEFAULT_PROMPT_TEXT_COLOUR); + + + m_filament_preset_combobox->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent &e) { + m_filament_preset_combobox->SetLabelColor(*wxBLACK); + wxString filament_type = m_filament_preset_combobox->GetStringSelection(); + std::unordered_map>::iterator iter = m_filament_choice_map.find(m_public_name_to_filament_id_map[filament_type]); + + m_scrolled_preset_panel->Freeze(); + m_filament_presets_sizer->Clear(true); + m_filament_preset.clear(); + + std::vector> printer_name_to_filament_preset; + if (iter != m_filament_choice_map.end()) { + std::unordered_map nozzle_diameter = nozzle_diameter_map; + for (Preset* preset : iter->second) { + auto compatible_printers = preset->config.option("compatible_printers", true); + if (!compatible_printers || compatible_printers->values.empty()) { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "there is a preset has no compatible printers and the preset name is: " << preset->name; + continue; + } + for (std::string &compatible_printer_name : compatible_printers->values) { + if (m_visible_printers.find(compatible_printer_name) == m_visible_printers.end()) { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "there is a comppatible printer no exist: " << compatible_printer_name + << "and the preset name is: " << preset->name; + continue; + } + std::string nozzle = get_printer_nozzle_diameter(compatible_printer_name); + if (nozzle_diameter[nozzle] == 0) { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " compatible printer nozzle encounter exception and name is: " << compatible_printer_name; + continue; + } + printer_name_to_filament_preset.push_back(std::make_pair(compatible_printer_name,preset)); + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "show compatible printer name: " << compatible_printer_name << "and preset name is: " << preset; + } + } + } else { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " not find filament_id corresponding to the type: and the type is" << filament_type; + } + sort_printer_by_nozzle(printer_name_to_filament_preset); + for (std::pair printer_to_preset : printer_name_to_filament_preset) + m_filament_presets_sizer->Add(create_checkbox(m_filament_preset_panel, printer_to_preset.first, printer_to_preset.second, m_filament_preset), 0, + wxEXPAND | wxTOP | wxLEFT, FromDIP(5)); + m_scrolled_preset_panel->SetSizerAndFit(m_scrolled_sizer); + m_scrolled_preset_panel->Thaw(); + + update_dialog_size(); + e.Skip(); + }); + + comboBoxSizer->Add(m_filament_preset_combobox, 0, wxEXPAND | wxTOP, FromDIP(5)); + + comboBoxSizer->Add(create_radio_item(m_create_type.base_filament_preset, this, wxEmptyString, m_create_type_btns), 0, wxEXPAND | wxTOP, FromDIP(10)); + + horizontal_sizer->Add(comboBoxSizer, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(10)); + + horizontal_sizer->Add(0, 0, 0, wxLEFT, FromDIP(30)); + + return horizontal_sizer; + +} + +wxBoxSizer *CreateFilamentPresetDialog::create_filament_preset_for_printer_item() +{ + wxBoxSizer *vertical_sizer = new wxBoxSizer(wxVERTICAL); + m_filament_preset_panel = new wxPanel(m_scrolled_preset_panel, wxID_ANY); + m_filament_preset_panel->SetBackgroundColour(PRINTER_LIST_COLOUR); + m_filament_preset_panel->SetSize(PRINTER_LIST_SIZE); + m_filament_presets_sizer = new wxGridSizer(3, FromDIP(5), FromDIP(5)); + m_filament_preset_panel->SetSizer(m_filament_presets_sizer); + vertical_sizer->Add(m_filament_preset_panel, 0, wxEXPAND | wxTOP | wxALIGN_CENTER_HORIZONTAL, FromDIP(5)); + + return vertical_sizer; +} + +wxBoxSizer *CreateFilamentPresetDialog::create_button_item() +{ + wxBoxSizer *bSizer_button = new wxBoxSizer(wxHORIZONTAL); + bSizer_button->Add(0, 0, 1, wxEXPAND, 0); + + StateColor btn_bg_green(std::pair(wxColour(27, 136, 68), StateColor::Pressed), std::pair(wxColour(61, 203, 115), StateColor::Hovered), + std::pair(wxColour(0, 174, 66), StateColor::Normal)); + + m_button_create = new Button(this, _L("Create")); + m_button_create->SetBackgroundColor(btn_bg_green); + m_button_create->SetBorderColor(*wxWHITE); + m_button_create->SetTextColor(wxColour(0xFFFFFE)); + m_button_create->SetFont(Label::Body_12); + m_button_create->SetSize(wxSize(FromDIP(58), FromDIP(24))); + m_button_create->SetMinSize(wxSize(FromDIP(58), FromDIP(24))); + m_button_create->SetCornerRadius(FromDIP(12)); + bSizer_button->Add(m_button_create, 0, wxRIGHT, FromDIP(10)); + + m_button_create->Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent &e) { + //get vendor name + wxString vendor_str = m_filament_vendor_combobox->GetLabel(); + std::string vendor_name; + + if (!m_can_not_find_vendor_checkbox->GetValue()) { + if (_L("Select Vendor") == vendor_str) { + MessageDialog dlg(this, _L("Vendor is not selected, please reselect vendor."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), + wxYES | wxYES_DEFAULT | wxCENTRE); + dlg.ShowModal(); + return; + } else { + vendor_name = into_u8(vendor_str); + } + } else { + if (m_filament_custom_vendor_input->GetTextCtrl()->GetValue().empty()) { + MessageDialog dlg(this, _L("Custom vendor is not input, please input custom vendor."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), + wxYES | wxYES_DEFAULT | wxCENTRE); + dlg.ShowModal(); + return; + } else { + vendor_name = into_u8(m_filament_custom_vendor_input->GetTextCtrl()->GetValue()); + if (vendor_name == "Bambu" || vendor_name == "Generic") { + MessageDialog dlg(this, _L("\"Bambu\" or \"Generic\" can not be used as a Vendor for custom filaments."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), + wxYES | wxYES_DEFAULT | wxCENTRE); + dlg.ShowModal(); + return; + } + } + } + + //get fialment type name + wxString type_str = m_filament_type_combobox->GetLabel(); + std::string type_name; + if (_L("Select Type") == type_str) { + MessageDialog dlg(this, _L("Filament type is not selected, please reselect type."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxYES | wxYES_DEFAULT | wxCENTRE); + dlg.ShowModal(); + return; + } else { + type_name = into_u8(type_str); + } + //get filament serial + wxString serial_str = m_filament_serial_input->GetTextCtrl()->GetValue(); + std::string serial_name; + if (serial_str.empty()) { + MessageDialog dlg(this, _L("Filament serial is not inputed, please input serial."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), + wxYES | wxYES_DEFAULT | wxCENTRE); + dlg.ShowModal(); + return; + } else { + serial_name = into_u8(serial_str); + } + vendor_name = remove_special_key(vendor_name); + serial_name = remove_special_key(serial_name); + + if (vendor_name.empty() || serial_name.empty()) { + MessageDialog dlg(this, _L("There may be escape characters in the vendor or serial input of filament. Please delete and re-enter."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), + wxYES | wxYES_DEFAULT | wxCENTRE); + dlg.ShowModal(); + return; + } + boost::algorithm::trim(vendor_name); + boost::algorithm::trim(serial_name); + if (vendor_name.empty() || serial_name.empty()) { + MessageDialog dlg(this, _L("All inputs in the custom vendor or serial are spaces. Please re-enter."), + wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxYES | wxYES_DEFAULT | wxCENTRE); + dlg.ShowModal(); + return; + } + if (m_can_not_find_vendor_checkbox->GetValue() && str_is_all_digit(vendor_name)) { + MessageDialog dlg(this, _L("The vendor can not be a number. Please re-enter."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), + wxYES | wxYES_DEFAULT | wxCENTRE); + dlg.ShowModal(); + return; + } + + if (!is_check_box_selected()) { + MessageDialog dlg(this, _L("You have not selected a printer or preset yet. Please select at least one."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), + wxYES | wxYES_DEFAULT | wxCENTRE); + dlg.ShowModal(); + return; + } + + std::string filament_preset_name = vendor_name + " " + (type_name == "PLA-AERO" ? "PLA Aero" : type_name) + " " + serial_name; + PresetBundle *preset_bundle = wxGetApp().preset_bundle; + if (preset_bundle->filaments.is_alias_exist(filament_preset_name)) { + MessageDialog dlg(this, + wxString::Format(_L("The Filament name %s you created already exists. \nIf you continue creating, the preset created will be displayed with its " + "full name. Do you want to continue?"), + from_u8(filament_preset_name)), + wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxYES_NO | wxYES_DEFAULT | wxCENTRE); + if (wxID_YES != dlg.ShowModal()) { return; } + } + + std::string user_filament_id = get_filament_id(filament_preset_name); + + const wxString &curr_create_type = curr_create_filament_type(); + + if (curr_create_type == m_create_type.base_filament) { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":clone filament create type filament "; + for (const std::pair<::CheckBox *, std::pair> &checkbox_preset : m_filament_preset) { + if (checkbox_preset.first->GetValue()) { + std::string compatible_printer_name = checkbox_preset.second.first; + std::vector failures; + Preset const *const checked_preset = checkbox_preset.second.second; + DynamicConfig dynamic_config; + dynamic_config.set_key_value("filament_vendor", new ConfigOptionStrings({vendor_name})); + dynamic_config.set_key_value("compatible_printers", new ConfigOptionStrings({compatible_printer_name})); + dynamic_config.set_key_value("filament_type", new ConfigOptionStrings({type_name})); + bool res = preset_bundle->filaments.clone_presets_for_filament(checked_preset, failures, filament_preset_name, user_filament_id, dynamic_config, + compatible_printer_name); + if (!res) { + std::string failure_names; + for (std::string &failure : failures) { failure_names += failure + "\n"; } + MessageDialog dlg(this, _L("Some existing presets have failed to be created, as follows:\n") + from_u8(failure_names) + _L("\nDo you want to rewrite it?"), + wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxYES_NO | wxYES_DEFAULT | wxCENTRE); + if (dlg.ShowModal() == wxID_YES) { + res = preset_bundle->filaments.clone_presets_for_filament(checked_preset, failures, filament_preset_name, user_filament_id, dynamic_config, + compatible_printer_name, true); + BOOST_LOG_TRIVIAL(info) << "clone filament have failures rewritten is successful? " << res; + } + } + BOOST_LOG_TRIVIAL(info) << "clone filament no failures is successful? " << res; + } + } + } else if (curr_create_type == m_create_type.base_filament_preset) { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":clone filament presets create type filament preset"; + for (const std::pair<::CheckBox *, std::pair> &checkbox_preset : m_machint_filament_preset) { + if (checkbox_preset.first->GetValue()) { + std::string compatible_printer_name = checkbox_preset.second.first; + std::vector failures; + Preset const *const checked_preset = checkbox_preset.second.second; + DynamicConfig dynamic_config; + dynamic_config.set_key_value("filament_vendor", new ConfigOptionStrings({vendor_name})); + dynamic_config.set_key_value("compatible_printers", new ConfigOptionStrings({compatible_printer_name})); + dynamic_config.set_key_value("filament_type", new ConfigOptionStrings({type_name})); + bool res = preset_bundle->filaments.clone_presets_for_filament(checked_preset, failures, filament_preset_name, user_filament_id, dynamic_config, + compatible_printer_name); + if (!res) { + std::string failure_names; + for (std::string &failure : failures) { failure_names += failure + "\n"; } + MessageDialog dlg(this, _L("Some existing presets have failed to be created, as follows:\n") + from_u8(failure_names) + _L("\nDo you want to rewrite it?"), + wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxYES_NO | wxYES_DEFAULT | wxCENTRE); + if (wxID_YES == dlg.ShowModal()) { + res = preset_bundle->filaments.clone_presets_for_filament(checked_preset, failures, filament_preset_name, user_filament_id, dynamic_config, + compatible_printer_name, true); + BOOST_LOG_TRIVIAL(info) << "clone filament presets have failures rewritten is successful? " << res; + } + } + BOOST_LOG_TRIVIAL(info) << "clone filament presets no failures is successful? " << res << " old preset is: " << checked_preset->name + << " compatible_printer_name is: " << compatible_printer_name; + } + } + } + preset_bundle->update_compatible(PresetSelectCompatibleType::Always); + EndModal(wxID_OK); + }); + + StateColor btn_bg_white(std::pair(wxColour(206, 206, 206), StateColor::Pressed), std::pair(wxColour(238, 238, 238), StateColor::Hovered), + std::pair(*wxWHITE, StateColor::Normal)); + + m_button_cancel = new Button(this, _L("Cancel")); + m_button_cancel->SetBackgroundColor(btn_bg_white); + m_button_cancel->SetBorderColor(wxColour(38, 46, 48)); + m_button_cancel->SetFont(Label::Body_12); + m_button_cancel->SetSize(wxSize(FromDIP(58), FromDIP(24))); + m_button_cancel->SetMinSize(wxSize(FromDIP(58), FromDIP(24))); + m_button_cancel->SetCornerRadius(FromDIP(12)); + bSizer_button->Add(m_button_cancel, 0, wxRIGHT, FromDIP(10)); + + m_button_cancel->Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent &e) { + EndModal(wxID_CANCEL); + }); + + return bSizer_button; +} + +wxArrayString CreateFilamentPresetDialog::get_filament_preset_choices() +{ + wxArrayString choices; + // get fialment type name + wxString type_str = m_filament_type_combobox->GetLabel(); + std::string type_name; + if (_L("Select Type") == type_str) { + /*MessageDialog dlg(this, _L("Filament type is not selected, please reselect type."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxYES | wxYES_DEFAULT | wxCENTRE); + dlg.ShowModal();*/ + return choices; + } else { + type_name = into_u8(type_str); + } + + for (std::pair filament_presets : m_all_presets_map) { + Preset *preset = filament_presets.second; + auto inherit = preset->config.option("inherits"); + if (inherit && !inherit->value.empty()) { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " inherit user preset is:" << preset->name << " and inherits is: " << inherit->value; + continue; + } + auto fila_type = preset->config.option("filament_type"); + if (!fila_type || fila_type->values.empty() || type_name != fila_type->values[0]) continue; + m_filament_choice_map[preset->filament_id].push_back(preset); + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " base user preset is:" << preset->name; + } + + int suffix = 0; + for (const pair> &preset : m_filament_choice_map) { + if (preset.second.empty()) continue; + std::set preset_name_set; + for (Preset* filament_preset : preset.second) { + std::string preset_name = filament_preset->name; + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " filament_id: " << filament_preset->filament_id << " preset name: " << filament_preset->name; + size_t index_at = preset_name.find(" @"); + if (std::string::npos != index_at) { + std::string cur_preset_name = preset_name.substr(0, index_at); + preset_name_set.insert(from_u8(cur_preset_name)); + } + } + assert(1 == preset_name_set.size()); + if (preset_name_set.size() > 1) { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " the same filament has different filament(vendor type serial)"; + } + for (const wxString& public_name : preset_name_set) { + if (m_public_name_to_filament_id_map.find(public_name) != m_public_name_to_filament_id_map.end()) { + suffix++; + m_public_name_to_filament_id_map[public_name + "_" + std::to_string(suffix)] = preset.first; + choices.Add(public_name + "_" + std::to_string(suffix)); + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " add filament choice: " << choices.back(); + } else { + m_public_name_to_filament_id_map[public_name] = preset.first; + choices.Add(public_name); + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " add filament choice: " << choices.back(); + } + } + } + + return choices; +} + +wxBoxSizer *CreateFilamentPresetDialog::create_radio_item(wxString title, wxWindow *parent, wxString tooltip, std::vector> &radiobox_list) +{ + wxBoxSizer *horizontal_sizer = new wxBoxSizer(wxHORIZONTAL); + RadioBox * radiobox = new RadioBox(parent); + horizontal_sizer->Add(radiobox, 0, wxEXPAND | wxALL, 0); + horizontal_sizer->Add(0, 0, 0, wxEXPAND | wxLEFT, FromDIP(5)); + radiobox_list.push_back(std::make_pair(radiobox, title)); + int btn_idx = radiobox_list.size() - 1; + radiobox->Bind(wxEVT_LEFT_DOWN, [this, &radiobox_list, btn_idx](wxMouseEvent &e) { select_curr_radiobox(radiobox_list, btn_idx); }); + + wxStaticText *text = new wxStaticText(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize); + text->Bind(wxEVT_LEFT_DOWN, [this, &radiobox_list, btn_idx](wxMouseEvent &e) { select_curr_radiobox(radiobox_list, btn_idx); }); + horizontal_sizer->Add(text, 0, wxEXPAND | wxLEFT, 0); + + radiobox->SetToolTip(tooltip); + text->SetToolTip(tooltip); + return horizontal_sizer; +} + +void CreateFilamentPresetDialog::select_curr_radiobox(std::vector> &radiobox_list, int btn_idx) +{ + int len = radiobox_list.size(); + for (int i = 0; i < len; ++i) { + if (i == btn_idx) { + radiobox_list[i].first->SetValue(true); + const wxString &curr_selected_type = radiobox_list[i].second; + this->Freeze(); + if (curr_selected_type == m_create_type.base_filament) { + m_filament_preset_text->SetLabel(_L("We could create the filament presets for your following printer:")); + m_filament_preset_combobox->Show(); + if (_L("Select Type") != m_filament_type_combobox->GetLabel()) { + clear_filament_preset_map(); + wxArrayString filament_preset_choice = get_filament_preset_choices(); + m_filament_preset_combobox->Set(filament_preset_choice); + m_filament_preset_combobox->SetLabel(_L("Select Filament Preset")); + m_filament_preset_combobox->SetLabelColor(DEFAULT_PROMPT_TEXT_COLOUR); + } + } else if (curr_selected_type == m_create_type.base_filament_preset) { + m_filament_preset_text->SetLabel(_L("We would rename the presets as \"Vendor Type Serial @printer you selected\". \nTo add preset for more prinetrs, Please go to printer selection")); + m_filament_preset_combobox->Hide(); + if (_L("Select Type") != m_filament_type_combobox->GetLabel()) { + + clear_filament_preset_map(); + get_filament_presets_by_machine(); + + } + } + m_scrolled_preset_panel->SetSizerAndFit(m_scrolled_sizer); + this->Thaw(); + } else { + radiobox_list[i].first->SetValue(false); + } + } + update_dialog_size(); +} + +wxString CreateFilamentPresetDialog::curr_create_filament_type() +{ + wxString curr_filament_type; + for (const std::pair &printer_radio : m_create_type_btns) { + if (printer_radio.first->GetValue()) { + curr_filament_type = printer_radio.second; + } + } + return curr_filament_type; +} + +void CreateFilamentPresetDialog::get_filament_presets_by_machine() +{ + wxArrayString choices; + // get fialment type name + wxString type_str = m_filament_type_combobox->GetLabel(); + std::string type_name; + if (_L("Select Type") == type_str) { + /*MessageDialog dlg(this, _L("Filament type is not selected, please reselect type."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxYES | wxYES_DEFAULT | + wxCENTRE); dlg.ShowModal();*/ + return; + } else { + type_name = into_u8(type_str); + } + + std::unordered_map nozzle_diameter = nozzle_diameter_map; + std::unordered_map> machine_name_to_presets; + PresetBundle * preset_bundle = wxGetApp().preset_bundle; + for (std::pair filament_preset : m_all_presets_map) { + Preset * preset = filament_preset.second; + auto compatible_printers = preset->config.option("compatible_printers", true); + if (!compatible_printers || compatible_printers->values.empty()) { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "there is a preset has no compatible printers and the preset name is: " << preset->name; + continue; + } + for (std::string &compatible_printer_name : compatible_printers->values) { + if (m_visible_printers.find(compatible_printer_name) == m_visible_printers.end()) { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " compatable printer is not visible and preset name is: " << preset->name; + continue; + } + Preset * inherit_preset = nullptr; + auto inherit = dynamic_cast(preset->config.option(BBL_JSON_KEY_INHERITS,false)); + if (inherit && !inherit->value.empty()) { + std::string inherits_value = inherit->value; + inherit_preset = preset_bundle->filaments.find_preset(inherits_value, false, true); + } + ConfigOptionStrings *filament_types; + if (!inherit_preset) { + filament_types = dynamic_cast(preset->config.option("filament_type")); + } else { + filament_types = dynamic_cast(inherit_preset->config.option("filament_type")); + } + + if (filament_types && filament_types->values.empty()) continue; + const std::string filament_type = filament_types->values[0]; + if (filament_type != type_name) { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " preset type is not selected type and preset name is: " << preset->name; + continue; + } + std::string nozzle = get_printer_nozzle_diameter(compatible_printer_name); + if (nozzle_diameter[nozzle] == 0) { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " compatible printer nozzle encounter exception and name is: " << compatible_printer_name; + continue; + } + machine_name_to_presets[compatible_printer_name].push_back(preset); + } + } + std::vector>> printer_name_to_filament_presets; + for (std::pair> machine_filament_presets : machine_name_to_presets) { + printer_name_to_filament_presets.push_back(machine_filament_presets); + } + sort_printer_by_nozzle(printer_name_to_filament_presets); + m_filament_preset_panel->Freeze(); + for (std::pair> machine_filament_presets : printer_name_to_filament_presets) { + std::string compatible_printer = machine_filament_presets.first; + std::vector &presets = machine_filament_presets.second; + m_filament_presets_sizer->Add(create_select_filament_preset_checkbox(m_filament_preset_panel, compatible_printer, presets, m_machint_filament_preset), 0, wxEXPAND | wxALL, FromDIP(5)); + } + m_filament_preset_panel->Thaw(); +} + +void CreateFilamentPresetDialog::get_all_filament_presets() +{ + // temp filament presets + PresetBundle temp_preset_bundle; + std::string dir_user_presets = wxGetApp().app_config->get("preset_folder"); + if (dir_user_presets.empty()) { + temp_preset_bundle.load_user_presets(DEFAULT_USER_FOLDER_NAME, ForwardCompatibilitySubstitutionRule::EnableSilent); + } else { + temp_preset_bundle.load_user_presets(dir_user_presets, ForwardCompatibilitySubstitutionRule::EnableSilent); + } + const std::deque &filament_presets = temp_preset_bundle.filaments.get_presets(); + + for (const Preset &preset : filament_presets) { + if (preset.filament_id.empty() || "null" == preset.filament_id) continue; + std::string filament_preset_name = preset.name; + Preset *filament_preset = new Preset(preset); + m_all_presets_map[filament_preset_name] = filament_preset; + } + // global filament presets + PresetBundle * preset_bundle = wxGetApp().preset_bundle; + const std::deque &temp_filament_presets = preset_bundle->filaments.get_presets(); + for (const Preset& preset : temp_filament_presets) { + if (preset.filament_id.empty() || "null" == preset.filament_id) continue; + auto filament_type = preset.config.option("filament_type"); + if (filament_type && filament_type->values.size()) + m_system_filament_types_set.insert(filament_type->values[0]); + if (!preset.is_visible) continue; + std::string filament_preset_name = preset.name; + Preset *filament_preset = new Preset(preset); + m_all_presets_map[filament_preset_name] = filament_preset; + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " loaded preset name is: " << filament_preset->name; + } +} + +void CreateFilamentPresetDialog::get_all_visible_printer_name() +{ + PresetBundle *preset_bundle = wxGetApp().preset_bundle; + for (const Preset &printer_preset : preset_bundle->printers.get_presets()) { + if (!printer_preset.is_visible) continue; + assert(m_visible_printers.find(printer_preset.name) == m_visible_printers.end()); + m_visible_printers.insert(printer_preset.name); + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " entry, and visible printer is: " << printer_preset.name; + } + +} + +void CreateFilamentPresetDialog::update_dialog_size() +{ + this->Freeze(); + m_filament_preset_panel->SetSizerAndFit(m_filament_presets_sizer); + int width = m_filament_preset_panel->GetSize().GetWidth(); + int height = m_filament_preset_panel->GetSize().GetHeight(); + m_scrolled_preset_panel->SetMinSize(wxSize(std::min(1400, width + FromDIP(26)), std::min(600, height + FromDIP(18)))); + m_scrolled_preset_panel->SetMaxSize(wxSize(std::min(1400, width + FromDIP(26)), std::min(600, height + FromDIP(18)))); + m_scrolled_preset_panel->SetSize(wxSize(std::min(1500, width + FromDIP(26)), std::min(600, height + FromDIP(18)))); + Layout(); + Fit(); + Refresh(); + adjust_dialog_in_screen(this); + this->Thaw(); +} + +template +void CreateFilamentPresetDialog::sort_printer_by_nozzle(std::vector> &printer_name_to_filament_preset) +{ + std::unordered_map nozzle_diameter = nozzle_diameter_map; + std::sort(printer_name_to_filament_preset.begin(), printer_name_to_filament_preset.end(), + [&nozzle_diameter](const std::pair &a, const std::pair &b) { + size_t nozzle_index_a = a.first.find(" nozzle"); + size_t nozzle_index_b = b.first.find(" nozzle"); + if (nozzle_index_a == std::string::npos || nozzle_index_b == std::string::npos) return a.first < b.first; + std::string nozzle_str_a; + std::string nozzle_str_b; + try { + nozzle_str_a = a.first.substr(0, nozzle_index_a); + nozzle_str_b = b.first.substr(0, nozzle_index_b); + size_t last_space_index = nozzle_str_a.find_last_of(" "); + nozzle_str_a = nozzle_str_a.substr(last_space_index + 1); + last_space_index = nozzle_str_b.find_last_of(" "); + nozzle_str_b = nozzle_str_b.substr(last_space_index + 1); + } catch (...) { + BOOST_LOG_TRIVIAL(info) << "substr filed, and printer name is: " << a.first << " and " << b.first; + return a.first < b.first; + } + float nozzle_a, nozzle_b; + try { + nozzle_a = nozzle_diameter[nozzle_str_a]; + nozzle_b = nozzle_diameter[nozzle_str_b]; + assert(nozzle_a != 0 && nozzle_b != 0); + } catch (...) { + BOOST_LOG_TRIVIAL(info) << "find nozzle filed, and nozzle is: " << nozzle_str_a << "mm and " << nozzle_str_b << "mm"; + return a.first < b.first; + } + float diff_nozzle_a = std::abs(nozzle_a - 0.4); + float diff_nozzle_b = std::abs(nozzle_b - 0.4); + if (nozzle_a == nozzle_b) return a.first < b.first; + if (diff_nozzle_a == diff_nozzle_b) return nozzle_a < nozzle_b; + + return diff_nozzle_a < diff_nozzle_b; + }); +} + +void CreateFilamentPresetDialog::clear_filament_preset_map() +{ + m_filament_choice_map.clear(); + m_filament_preset.clear(); + m_machint_filament_preset.clear(); + m_public_name_to_filament_id_map.clear(); + m_filament_preset_panel->Freeze(); + m_filament_presets_sizer->Clear(true); + m_filament_preset_panel->Thaw(); +} + +CreatePrinterPresetDialog::CreatePrinterPresetDialog(wxWindow *parent) +: DPIDialog(parent ? parent : nullptr, wxID_ANY, _L("Create Printer/Nozzle"), wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX | wxCENTER) +{ + m_create_type.create_printer = _L("Create Printer"); + m_create_type.create_nozzle = _L("Create Nozzle for Existing Printer"); + m_create_type.base_template = _L("Create from Template"); + m_create_type.base_curr_printer = _L("Create Based on Current Printer"); + this->SetBackgroundColour(*wxWHITE); + SetSizeHints(wxDefaultSize, wxDefaultSize); + + std::string icon_path = (boost::format("%1%/images/BambuStudioTitle.ico") % resources_dir()).str(); + SetIcon(wxIcon(encode_path(icon_path.c_str()), wxBITMAP_TYPE_ICO)); + + wxBoxSizer *m_main_sizer = new wxBoxSizer(wxVERTICAL); + // top line + auto m_line_top = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(-1, 2), wxTAB_TRAVERSAL); + m_line_top->SetBackgroundColour(wxColour(0xA6, 0xa9, 0xAA)); + m_main_sizer->Add(m_line_top, 0, wxEXPAND, 0); + m_main_sizer->Add(0, 0, 0, wxTOP, FromDIP(5)); + m_main_sizer->Add(create_step_switch_item(), 0, wxEXPAND | wxALL, FromDIP(5)); + + wxBoxSizer *page_sizer = new wxBoxSizer(wxHORIZONTAL); + + m_page1 = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize); + m_page1->SetBackgroundColour(*wxWHITE); + m_page1->SetScrollRate(5, 5); + m_page2 = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize);\ + m_page2->SetBackgroundColour(*wxWHITE); + + create_printer_page1(m_page1); + create_printer_page2(m_page2); + m_page2->Hide(); + + page_sizer->Add(m_page1, 1, wxEXPAND, 0); + page_sizer->Add(m_page2, 1, wxEXPAND, 0); + m_main_sizer->Add(page_sizer, 0, wxEXPAND | wxRIGHT, FromDIP(10)); + select_curr_radiobox(m_create_type_btns, 0); + select_curr_radiobox(m_create_presets_btns, 0); + + m_main_sizer->Add(0, 0, 0, wxTOP, FromDIP(10)); + + this->SetSizer(m_main_sizer); + + Layout(); + Fit(); + + wxSize screen_size = wxGetDisplaySize(); + int dialogX = (screen_size.GetWidth() - GetSize().GetWidth()) / 2; + int dialogY = (screen_size.GetHeight() - GetSize().GetHeight()) / 2; + SetPosition(wxPoint(dialogX, dialogY)); + + wxGetApp().UpdateDlgDarkUI(this); +} + +CreatePrinterPresetDialog::~CreatePrinterPresetDialog() +{ + clear_preset_combobox(); + if (m_printer_preset) { + delete m_printer_preset; + m_printer_preset = nullptr; + } +} + +void CreatePrinterPresetDialog::on_dpi_changed(const wxRect &suggested_rect) { + m_button_OK->SetMinSize(wxSize(FromDIP(58), FromDIP(24))); + m_button_OK->SetMaxSize(wxSize(FromDIP(58), FromDIP(24))); + m_button_OK->SetCornerRadius(FromDIP(12)); + m_button_create->SetMinSize(wxSize(FromDIP(58), FromDIP(24))); + m_button_create->SetMaxSize(wxSize(FromDIP(58), FromDIP(24))); + m_button_create->SetCornerRadius(FromDIP(12)); + m_button_page1_cancel->SetMinSize(wxSize(FromDIP(58), FromDIP(24))); + m_button_page1_cancel->SetMaxSize(wxSize(FromDIP(58), FromDIP(24))); + m_button_page1_cancel->SetCornerRadius(FromDIP(12)); + m_button_page2_cancel->SetMinSize(wxSize(FromDIP(58), FromDIP(24))); + m_button_page2_cancel->SetMaxSize(wxSize(FromDIP(58), FromDIP(24))); + m_button_page2_cancel->SetCornerRadius(FromDIP(12)); + m_button_page2_back->SetMinSize(wxSize(FromDIP(58), FromDIP(24))); + m_button_page2_back->SetMaxSize(wxSize(FromDIP(58), FromDIP(24))); + m_button_page2_back->SetCornerRadius(FromDIP(12)); + Layout(); +} + +wxBoxSizer *CreatePrinterPresetDialog::create_step_switch_item() +{ + wxBoxSizer *step_switch_sizer = new wxBoxSizer(wxVERTICAL); + + std::string wiki_url = "https://wiki.bambulab.com/en/software/bambu-studio/3rd-party-printer-profile"; + wxHyperlinkCtrl *m_download_hyperlink = new wxHyperlinkCtrl(this, wxID_ANY, _L("wiki"), wiki_url, wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE); + step_switch_sizer->Add(m_download_hyperlink, 0, wxRIGHT | wxALIGN_RIGHT, FromDIP(5)); + + wxBoxSizer *horizontal_sizer = new wxBoxSizer(wxHORIZONTAL); + wxPanel * step_switch_panel = new wxPanel(this); + step_switch_panel->SetBackgroundColour(*wxWHITE); + horizontal_sizer->Add(0, 0, 1, wxEXPAND,0); + m_step_1 = new wxStaticBitmap(step_switch_panel, wxID_ANY, create_scaled_bitmap("step_1", nullptr, FromDIP(20)), wxDefaultPosition, wxDefaultSize); + horizontal_sizer->Add(m_step_1, 0, wxEXPAND | wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, FromDIP(3)); + wxStaticText *static_create_printer_text = new wxStaticText(step_switch_panel, wxID_ANY, m_create_type.create_printer, wxDefaultPosition, wxDefaultSize); + horizontal_sizer->Add(static_create_printer_text, 0, wxEXPAND | wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, FromDIP(3)); + auto divider_line = new wxPanel(step_switch_panel, wxID_ANY, wxDefaultPosition, wxSize(FromDIP(50), 1)); + divider_line->SetBackgroundColour(PRINTER_LIST_COLOUR); + horizontal_sizer->Add(divider_line, 0, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, FromDIP(3)); + m_step_2 = new wxStaticBitmap(step_switch_panel, wxID_ANY, create_scaled_bitmap("step_2_ready", nullptr, FromDIP(20)), wxDefaultPosition, wxDefaultSize); + horizontal_sizer->Add(m_step_2, 0, wxEXPAND | wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, FromDIP(3)); + wxStaticText *static_import_presets_text = new wxStaticText(step_switch_panel, wxID_ANY, _L("Import Preset"), wxDefaultPosition, wxDefaultSize); + horizontal_sizer->Add(static_import_presets_text, 0, wxEXPAND | wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, FromDIP(3)); + horizontal_sizer->Add(0, 0, 1, wxEXPAND, 0); + + step_switch_panel->SetSizer(horizontal_sizer); + + step_switch_sizer->Add(step_switch_panel, 0, wxBOTTOM | wxALIGN_CENTER_HORIZONTAL, FromDIP(10)); + + auto line_top = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(-1, 1), wxTAB_TRAVERSAL); + line_top->SetBackgroundColour(PRINTER_LIST_COLOUR); + + step_switch_sizer->Add(line_top, 0, wxEXPAND | wxALL, FromDIP(10)); + + return step_switch_sizer; +} + +void CreatePrinterPresetDialog::create_printer_page1(wxWindow *parent) +{ + this->SetBackgroundColour(*wxWHITE); + + m_page1_sizer = new wxBoxSizer(wxVERTICAL); + + m_page1_sizer->Add(create_type_item(parent), 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(5)); + m_page1_sizer->Add(create_printer_item(parent), 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(5)); + m_page1_sizer->Add(create_nozzle_diameter_item(parent), 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(5)); + m_printer_info_panel = new wxPanel(parent); + m_printer_info_panel->SetBackgroundColour(*wxWHITE); + m_printer_info_sizer = new wxBoxSizer(wxVERTICAL); + m_printer_info_sizer->Add(create_bed_shape_item(m_printer_info_panel), 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(5)); + m_printer_info_sizer->Add(create_bed_size_item(m_printer_info_panel), 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(5)); + m_printer_info_sizer->Add(create_origin_item(m_printer_info_panel), 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(5)); + m_printer_info_sizer->Add(create_hot_bed_stl_item(m_printer_info_panel), 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(5)); + m_printer_info_sizer->Add(create_hot_bed_svg_item(m_printer_info_panel), 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(5)); + m_printer_info_sizer->Add(create_max_print_height_item(m_printer_info_panel), 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(5)); + m_printer_info_panel->SetSizer(m_printer_info_sizer); + m_page1_sizer->Add(m_printer_info_panel, 0, wxEXPAND, 0); + m_page1_sizer->Add(create_page1_btns_item(parent), 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(5)); + + parent->SetSizerAndFit(m_page1_sizer); + Layout(); + + wxGetApp().UpdateDlgDarkUI(this); +} + +wxBoxSizer *CreatePrinterPresetDialog::create_type_item(wxWindow *parent) +{ + wxBoxSizer *horizontal_sizer = new wxBoxSizer(wxHORIZONTAL); + + wxBoxSizer * optionSizer = new wxBoxSizer(wxVERTICAL); + wxStaticText *static_serial_text = new wxStaticText(parent, wxID_ANY, _L("Create Type"), wxDefaultPosition, wxDefaultSize); + optionSizer->Add(static_serial_text, 0, wxEXPAND | wxALL, 0); + optionSizer->SetMinSize(OPTION_SIZE); + horizontal_sizer->Add(optionSizer, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(10)); + + wxBoxSizer *radioBoxSizer = new wxBoxSizer(wxVERTICAL); + + radioBoxSizer->Add(create_radio_item(m_create_type.create_printer, parent, wxEmptyString, m_create_type_btns), 0, wxEXPAND | wxALL, 0); + radioBoxSizer->Add(create_radio_item(m_create_type.create_nozzle, parent, wxEmptyString, m_create_type_btns), 0, wxEXPAND | wxTOP, FromDIP(10)); + horizontal_sizer->Add(radioBoxSizer, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(10)); + + return horizontal_sizer; +} + +wxBoxSizer *CreatePrinterPresetDialog::create_printer_item(wxWindow *parent) +{ + wxBoxSizer *horizontal_sizer = new wxBoxSizer(wxHORIZONTAL); + + wxBoxSizer * optionSizer = new wxBoxSizer(wxVERTICAL); + wxStaticText *static_vendor_text = new wxStaticText(parent, wxID_ANY, _L("Printer"), wxDefaultPosition, wxDefaultSize); + optionSizer->Add(static_vendor_text, 0, wxEXPAND | wxALL, 0); + optionSizer->SetMinSize(OPTION_SIZE); + horizontal_sizer->Add(optionSizer, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(10)); + + wxBoxSizer *vertical_sizer = new wxBoxSizer(wxVERTICAL); + wxBoxSizer *comboBoxSizer = new wxBoxSizer(wxHORIZONTAL); + m_select_vendor = new ComboBox(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, NAME_OPTION_COMBOBOX_SIZE, 0, nullptr, wxCB_READONLY); + m_select_vendor->SetValue(_L("Select Vendor")); + m_select_vendor->SetLabelColor(DEFAULT_PROMPT_TEXT_COLOUR); + wxArrayString printer_vendor; + for (const std::string &vendor : printer_vendors) { + printer_vendor.Add(vendor); + } + m_select_vendor->Set(printer_vendor); + m_select_vendor->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent e) { + m_select_vendor->SetLabelColor(*wxBLACK); + std::string curr_selected_vendor = into_u8(m_select_vendor->GetStringSelection()); + std::unordered_map>::const_iterator iter = printer_model_map.find(curr_selected_vendor); + if (iter != printer_model_map.end()) + { + std::vector vendor_model = iter->second; + wxArrayString model_choice; + for (const std::string &model : vendor_model) { + model_choice.Add(model); + } + m_select_model->Set(model_choice); + if (!model_choice.empty()) { + m_select_model->SetSelection(0); + m_select_model->SetLabelColor(*wxBLACK); + } + } else { + MessageDialog dlg(this, _L("The model is not fond, place reselect vendor."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxYES | wxYES_DEFAULT | wxCENTRE); + dlg.ShowModal(); + } + e.Skip(); + }); + + comboBoxSizer->Add(m_select_vendor, 0, wxEXPAND | wxALL, 0); + + m_select_model = new ComboBox(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, NAME_OPTION_COMBOBOX_SIZE, 0, nullptr, wxCB_READONLY); + comboBoxSizer->Add(m_select_model, 0, wxEXPAND | wxLEFT, FromDIP(5)); + m_select_model->SetValue(_L("Select Model")); + m_select_model->SetLabelColor(DEFAULT_PROMPT_TEXT_COLOUR); + m_select_model->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent e) { + m_select_model->SetLabelColor(*wxBLACK); + e.Skip(); + }); + + m_select_printer = new ComboBox(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, PRINTER_PRESET_MODEL_SIZE, 0, nullptr, wxCB_READONLY); + comboBoxSizer->Add(m_select_printer, 0, wxEXPAND | wxALL, 0); + m_select_printer->SetValue(_L("Select Printer")); + m_select_printer->SetLabelColor(DEFAULT_PROMPT_TEXT_COLOUR); + m_select_printer->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent e) { + m_select_printer->SetLabelColor(*wxBLACK); + + e.Skip(); + }); + m_select_printer->Hide(); + + m_custom_vendor_text_ctrl = new wxTextCtrl(parent, wxID_ANY, "", wxDefaultPosition, NAME_OPTION_COMBOBOX_SIZE); + m_custom_vendor_text_ctrl->SetHint(_L("Input Custom Vendor")); + m_custom_vendor_text_ctrl->Bind(wxEVT_CHAR, [this](wxKeyEvent &event) { + int key = event.GetKeyCode(); + if (cannot_input_key.find(key) != cannot_input_key.end()) { // "@" can not be inputed + event.Skip(false); + return; + } + event.Skip(); + }); + comboBoxSizer->Add(m_custom_vendor_text_ctrl, 0, wxEXPAND | wxALL, 0); + m_custom_vendor_text_ctrl->Hide(); + m_custom_model_text_ctrl = new wxTextCtrl(parent, wxID_ANY, "", wxDefaultPosition, NAME_OPTION_COMBOBOX_SIZE); + m_custom_model_text_ctrl->SetHint(_L("Input Custom Model")); + m_custom_model_text_ctrl->Bind(wxEVT_CHAR, [this](wxKeyEvent &event) { + int key = event.GetKeyCode(); + if (cannot_input_key.find(key) != cannot_input_key.end()) { // "@" can not be inputed + event.Skip(false); + return; + } + event.Skip(); + }); + comboBoxSizer->Add(m_custom_model_text_ctrl, 0, wxEXPAND | wxLEFT, FromDIP(5)); + m_custom_model_text_ctrl->Hide(); + + vertical_sizer->Add(comboBoxSizer, 0, wxEXPAND, 0); + + wxBoxSizer *checkbox_sizer = new wxBoxSizer(wxHORIZONTAL); + m_can_not_find_vendor_combox = new ::CheckBox(parent); + + checkbox_sizer->Add(m_can_not_find_vendor_combox, 0, wxALIGN_CENTER, 0); + checkbox_sizer->Add(0, 0, 0, wxEXPAND | wxRIGHT, FromDIP(5)); + + m_can_not_find_vendor_text = new wxStaticText(parent, wxID_ANY, _L("Can't find my printer model"), wxDefaultPosition, wxDefaultSize, 0); + m_can_not_find_vendor_text->SetFont(::Label::Body_13); + + wxSize size = m_can_not_find_vendor_text->GetTextExtent(_L("Can't find my printer model")); + m_can_not_find_vendor_text->SetMinSize(wxSize(size.x + FromDIP(4), -1)); + m_can_not_find_vendor_text->Wrap(-1); + checkbox_sizer->Add(m_can_not_find_vendor_text, 0, wxALIGN_CENTER, 0); + + m_can_not_find_vendor_combox->Bind(wxEVT_TOGGLEBUTTON, [this](wxCommandEvent &e) { + bool value = m_can_not_find_vendor_combox->GetValue(); + if (value) { + m_can_not_find_vendor_combox->SetValue(true); + m_custom_vendor_text_ctrl->Show(); + m_custom_model_text_ctrl->Show(); + m_select_vendor->Hide(); + m_select_model->Hide(); + } else { + m_can_not_find_vendor_combox->SetValue(false); + m_custom_vendor_text_ctrl->Hide(); + m_custom_model_text_ctrl->Hide(); + m_select_vendor->Show(); + m_select_model->Show(); + } + Refresh(); + Layout(); + m_page1->SetSizerAndFit(m_page1_sizer); + Fit(); + }); + + vertical_sizer->Add(checkbox_sizer, 0, wxEXPAND | wxTOP, FromDIP(5)); + + horizontal_sizer->Add(vertical_sizer, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(10)); + + return horizontal_sizer; + +} + +wxBoxSizer *CreatePrinterPresetDialog::create_nozzle_diameter_item(wxWindow *parent) +{ + wxBoxSizer *horizontal_sizer = new wxBoxSizer(wxHORIZONTAL); + + wxBoxSizer * optionSizer = new wxBoxSizer(wxVERTICAL); + wxStaticText *static_type_text = new wxStaticText(parent, wxID_ANY, _L("Nozzle Diameter"), wxDefaultPosition, wxDefaultSize); + optionSizer->Add(static_type_text, 0, wxEXPAND | wxALL, 0); + optionSizer->SetMinSize(OPTION_SIZE); + horizontal_sizer->Add(optionSizer, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(10)); + + wxBoxSizer *comboBoxSizer = new wxBoxSizer(wxVERTICAL); + m_nozzle_diameter = new ComboBox(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, OPTION_SIZE, 0, nullptr, wxCB_READONLY); + wxArrayString nozzle_diameters; + for (const std::string nozzle : nozzle_diameter_vec) { + nozzle_diameters.Add(nozzle + " mm"); + } + m_nozzle_diameter->Set(nozzle_diameters); + m_nozzle_diameter->SetSelection(0); + comboBoxSizer->Add(m_nozzle_diameter, 0, wxEXPAND | wxALL, 0); + horizontal_sizer->Add(comboBoxSizer, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(10)); + horizontal_sizer->Add(0, 0, 0, wxEXPAND | wxLEFT, FromDIP(200)); + + return horizontal_sizer; +} + +wxBoxSizer *CreatePrinterPresetDialog::create_bed_shape_item(wxWindow *parent) +{ + wxBoxSizer *horizontal_sizer = new wxBoxSizer(wxHORIZONTAL); + + wxBoxSizer * optionSizer = new wxBoxSizer(wxVERTICAL); + wxStaticText *static_type_text = new wxStaticText(parent, wxID_ANY, _L("Bed Shape"), wxDefaultPosition, wxDefaultSize); + optionSizer->Add(static_type_text, 0, wxEXPAND | wxALL, 0); + optionSizer->SetMinSize(OPTION_SIZE); + horizontal_sizer->Add(optionSizer, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(10)); + + wxBoxSizer * bed_shape_sizer = new wxBoxSizer(wxVERTICAL); + wxStaticText *static_bed_shape_text = new wxStaticText(parent, wxID_ANY, _L("Rectangle"), wxDefaultPosition, wxDefaultSize); + bed_shape_sizer->Add(static_bed_shape_text, 0, wxEXPAND | wxALL, 0); + horizontal_sizer->Add(bed_shape_sizer, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(10)); + + return horizontal_sizer; +} + +wxBoxSizer *CreatePrinterPresetDialog::create_bed_size_item(wxWindow *parent) +{ + wxBoxSizer *horizontal_sizer = new wxBoxSizer(wxHORIZONTAL); + + wxBoxSizer * optionSizer = new wxBoxSizer(wxVERTICAL); + wxStaticText *static_type_text = new wxStaticText(parent, wxID_ANY, _L("Printable Space"), wxDefaultPosition, wxDefaultSize); + optionSizer->Add(static_type_text, 0, wxEXPAND | wxALL, 0); + optionSizer->SetMinSize(OPTION_SIZE); + horizontal_sizer->Add(optionSizer, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(10)); + + wxBoxSizer * length_sizer = new wxBoxSizer(wxVERTICAL); + wxStaticText *static_length_text = new wxStaticText(parent, wxID_ANY, "X", wxDefaultPosition, wxDefaultSize); + static_length_text->SetMinSize(ORIGIN_TEXT_SIZE); + static_length_text->SetSize(ORIGIN_TEXT_SIZE); + length_sizer->Add(static_length_text, 0, wxEXPAND | wxALL, 0); + horizontal_sizer->Add(length_sizer, 0, wxEXPAND | wxLEFT | wxTOP | wxALIGN_CENTER_VERTICAL, FromDIP(10)); + wxBoxSizer *length_input_sizer = new wxBoxSizer(wxVERTICAL); + m_bed_size_x_input = new TextInput(parent, "200", "mm", wxEmptyString, wxDefaultPosition, PRINTER_SPACE_SIZE, wxTE_CENTRE | wxTE_PROCESS_ENTER); + wxTextValidator validator(wxFILTER_DIGITS); + m_bed_size_x_input->GetTextCtrl()->SetValidator(validator); + length_input_sizer->Add(m_bed_size_x_input, 0, wxEXPAND | wxALL, 0); + horizontal_sizer->Add(length_input_sizer, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(5)); + + wxBoxSizer * width_sizer = new wxBoxSizer(wxVERTICAL); + wxStaticText *static_width_text = new wxStaticText(parent, wxID_ANY, "Y", wxDefaultPosition, wxDefaultSize); + static_width_text->SetMinSize(ORIGIN_TEXT_SIZE); + static_width_text->SetSize(ORIGIN_TEXT_SIZE); + width_sizer->Add(static_width_text, 0, wxEXPAND | wxALL, 0); + horizontal_sizer->Add(width_sizer, 0, wxEXPAND | wxLEFT | wxTOP | wxALIGN_CENTER_VERTICAL, FromDIP(10)); + wxBoxSizer *width_input_sizer = new wxBoxSizer(wxVERTICAL); + m_bed_size_y_input = new TextInput(parent, "200", "mm", wxEmptyString, wxDefaultPosition, PRINTER_SPACE_SIZE, wxTE_CENTRE | wxTE_PROCESS_ENTER); + m_bed_size_y_input->GetTextCtrl()->SetValidator(validator); + width_input_sizer->Add(m_bed_size_y_input, 0, wxEXPAND | wxALL, 0); + horizontal_sizer->Add(width_input_sizer, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(5)); + + return horizontal_sizer; + +} + +wxBoxSizer *CreatePrinterPresetDialog::create_origin_item(wxWindow *parent) +{ + wxBoxSizer *horizontal_sizer = new wxBoxSizer(wxHORIZONTAL); + + wxBoxSizer * optionSizer = new wxBoxSizer(wxVERTICAL); + wxStaticText *static_type_text = new wxStaticText(parent, wxID_ANY, _L("Origin"), wxDefaultPosition, wxDefaultSize); + optionSizer->Add(static_type_text, 0, wxEXPAND | wxALL, 0); + optionSizer->SetMinSize(OPTION_SIZE); + horizontal_sizer->Add(optionSizer, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(10)); + + wxBoxSizer * length_sizer = new wxBoxSizer(wxVERTICAL); + wxStaticText *static_origin_x_text = new wxStaticText(parent, wxID_ANY, "X", wxDefaultPosition, wxDefaultSize); + static_origin_x_text->SetMinSize(ORIGIN_TEXT_SIZE); + static_origin_x_text->SetSize(ORIGIN_TEXT_SIZE); + length_sizer->Add(static_origin_x_text, 0, wxEXPAND | wxALL, 0); + horizontal_sizer->Add(length_sizer, 0, wxEXPAND | wxLEFT | wxTOP | wxALIGN_CENTER_VERTICAL, FromDIP(10)); + wxBoxSizer *length_input_sizer = new wxBoxSizer(wxVERTICAL); + m_bed_origin_x_input = new TextInput(parent, "0", "mm", wxEmptyString, wxDefaultPosition, PRINTER_SPACE_SIZE, wxTE_CENTRE | wxTE_PROCESS_ENTER); + wxTextValidator validator(wxFILTER_DIGITS); + m_bed_origin_x_input->GetTextCtrl()->SetValidator(validator); + length_input_sizer->Add(m_bed_origin_x_input, 0, wxEXPAND | wxALL, 0); + horizontal_sizer->Add(length_input_sizer, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(5)); + + wxBoxSizer * width_sizer = new wxBoxSizer(wxVERTICAL); + wxStaticText *static_origin_y_text = new wxStaticText(parent, wxID_ANY, "Y", wxDefaultPosition, wxDefaultSize); + static_origin_y_text->SetMinSize(ORIGIN_TEXT_SIZE); + static_origin_y_text->SetSize(ORIGIN_TEXT_SIZE); + width_sizer->Add(static_origin_y_text, 0, wxEXPAND | wxALL, 0); + horizontal_sizer->Add(width_sizer, 0, wxEXPAND | wxLEFT | wxTOP | wxALIGN_CENTER_VERTICAL, FromDIP(10)); + wxBoxSizer *width_input_sizer = new wxBoxSizer(wxVERTICAL); + m_bed_origin_y_input = new TextInput(parent, "0", "mm", wxEmptyString, wxDefaultPosition, PRINTER_SPACE_SIZE, wxTE_CENTRE | wxTE_PROCESS_ENTER); + m_bed_origin_y_input->GetTextCtrl()->SetValidator(validator); + width_input_sizer->Add(m_bed_origin_y_input, 0, wxEXPAND | wxALL, 0); + horizontal_sizer->Add(width_input_sizer, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(5)); + + return horizontal_sizer; +} + +wxBoxSizer *CreatePrinterPresetDialog::create_hot_bed_stl_item(wxWindow *parent) +{ + wxBoxSizer *horizontal_sizer = new wxBoxSizer(wxHORIZONTAL); + + wxBoxSizer * optionSizer = new wxBoxSizer(wxVERTICAL); + wxStaticText *static_type_text = new wxStaticText(parent, wxID_ANY, _L("Hot Bed STL"), wxDefaultPosition, wxDefaultSize); + optionSizer->Add(static_type_text, 0, wxEXPAND | wxALL, 0); + optionSizer->SetMinSize(OPTION_SIZE); + horizontal_sizer->Add(optionSizer, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(10)); + + wxBoxSizer *hot_bed_stl_sizer = new wxBoxSizer(wxVERTICAL); + + StateColor flush_bg_col(std::pair(wxColour(219, 253, 231), StateColor::Pressed), std::pair(wxColour(238, 238, 238), StateColor::Hovered), + std::pair(wxColour(238, 238, 238), StateColor::Normal)); + + StateColor flush_bd_col(std::pair(wxColour(0, 174, 66), StateColor::Pressed), std::pair(wxColour(0, 174, 66), StateColor::Hovered), + std::pair(wxColour(172, 172, 172), StateColor::Normal)); + + m_button_bed_stl = new Button(parent, _L("Load stl")); + m_button_bed_stl->Bind(wxEVT_BUTTON, ([this](wxCommandEvent &e) { load_model_stl(); })); + m_button_bed_stl->SetFont(Label::Body_10); + + m_button_bed_stl->SetPaddingSize(wxSize(FromDIP(30), FromDIP(8))); + m_button_bed_stl->SetFont(Label::Body_13); + m_button_bed_stl->SetCornerRadius(FromDIP(8)); + m_button_bed_stl->SetBackgroundColor(flush_bg_col); + m_button_bed_stl->SetBorderColor(flush_bd_col); + hot_bed_stl_sizer->Add(m_button_bed_stl, 0, wxEXPAND | wxALL, 0); + + horizontal_sizer->Add(hot_bed_stl_sizer, 0, wxEXPAND | wxLEFT | wxALIGN_CENTER_VERTICAL, FromDIP(10)); + + m_upload_stl_tip_text = new wxStaticText(parent, wxID_ANY, "", wxDefaultPosition, wxDefaultSize); + m_upload_stl_tip_text->SetLabelText(_L("Empty")); + horizontal_sizer->Add(m_upload_stl_tip_text, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(10)); + return horizontal_sizer; +} + +wxBoxSizer *CreatePrinterPresetDialog::create_hot_bed_svg_item(wxWindow *parent) +{ + wxBoxSizer *horizontal_sizer = new wxBoxSizer(wxHORIZONTAL); + + wxBoxSizer * optionSizer = new wxBoxSizer(wxVERTICAL); + wxStaticText *static_type_text = new wxStaticText(parent, wxID_ANY, _L("Hot Bed SVG"), wxDefaultPosition, wxDefaultSize); + optionSizer->Add(static_type_text, 0, wxEXPAND | wxALL, 0); + optionSizer->SetMinSize(OPTION_SIZE); + horizontal_sizer->Add(optionSizer, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(10)); + + wxBoxSizer *hot_bed_stl_sizer = new wxBoxSizer(wxVERTICAL); + + StateColor flush_bg_col(std::pair(wxColour(219, 253, 231), StateColor::Pressed), std::pair(wxColour(238, 238, 238), StateColor::Hovered), + std::pair(wxColour(238, 238, 238), StateColor::Normal)); + + StateColor flush_bd_col(std::pair(wxColour(0, 174, 66), StateColor::Pressed), std::pair(wxColour(0, 174, 66), StateColor::Hovered), + std::pair(wxColour(172, 172, 172), StateColor::Normal)); + + m_button_bed_svg = new Button(parent, _L("Load svg")); + m_button_bed_svg->Bind(wxEVT_BUTTON, ([this](wxCommandEvent &e) { load_texture(); })); + m_button_bed_svg->SetFont(Label::Body_10); + + m_button_bed_svg->SetPaddingSize(wxSize(FromDIP(30), FromDIP(8))); + m_button_bed_svg->SetFont(Label::Body_13); + m_button_bed_svg->SetCornerRadius(FromDIP(8)); + m_button_bed_svg->SetBackgroundColor(flush_bg_col); + m_button_bed_svg->SetBorderColor(flush_bd_col); + hot_bed_stl_sizer->Add(m_button_bed_svg, 0, wxEXPAND | wxALL, 0); + + horizontal_sizer->Add(hot_bed_stl_sizer, 0, wxEXPAND | wxLEFT | wxALIGN_CENTER_VERTICAL, FromDIP(10)); + + m_upload_svg_tip_text = new wxStaticText(parent, wxID_ANY, "", wxDefaultPosition, wxDefaultSize); + m_upload_svg_tip_text->SetLabelText(_L("Empty")); + horizontal_sizer->Add(m_upload_svg_tip_text, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(10)); + return horizontal_sizer; +} + +wxBoxSizer *CreatePrinterPresetDialog::create_max_print_height_item(wxWindow *parent) +{ + wxBoxSizer * horizontal_sizer = new wxBoxSizer(wxHORIZONTAL); + + wxBoxSizer * optionSizer = new wxBoxSizer(wxVERTICAL); + wxStaticText *static_type_text = new wxStaticText(parent, wxID_ANY, _L("Max Print Height"), wxDefaultPosition, wxDefaultSize); + optionSizer->Add(static_type_text, 0, wxEXPAND | wxALL, 0); + optionSizer->SetMinSize(OPTION_SIZE); + horizontal_sizer->Add(optionSizer, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(10)); + + wxBoxSizer *hight_input_sizer = new wxBoxSizer(wxVERTICAL); + m_print_height_input = new TextInput(parent, "200", "mm", wxEmptyString, wxDefaultPosition, PRINTER_SPACE_SIZE, wxTE_CENTRE | wxTE_PROCESS_ENTER); + wxTextValidator validator(wxFILTER_DIGITS); + m_print_height_input->GetTextCtrl()->SetValidator(validator); + hight_input_sizer->Add(m_print_height_input, 0, wxEXPAND | wxLEFT, FromDIP(5)); + horizontal_sizer->Add(hight_input_sizer, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(5)); + + return horizontal_sizer; +} + +wxBoxSizer *CreatePrinterPresetDialog::create_page1_btns_item(wxWindow *parent) +{ + wxBoxSizer *bSizer_button = new wxBoxSizer(wxHORIZONTAL); + bSizer_button->Add(0, 0, 1, wxEXPAND, 0); + + StateColor btn_bg_green(std::pair(wxColour(27, 136, 68), StateColor::Pressed), std::pair(wxColour(61, 203, 115), StateColor::Hovered), + std::pair(wxColour(0, 174, 66), StateColor::Normal)); + + m_button_OK = new Button(parent, _L("OK")); + m_button_OK->SetBackgroundColor(btn_bg_green); + m_button_OK->SetBorderColor(*wxWHITE); + m_button_OK->SetTextColor(wxColour(0xFFFFFE)); + m_button_OK->SetFont(Label::Body_12); + m_button_OK->SetSize(wxSize(FromDIP(58), FromDIP(24))); + m_button_OK->SetMinSize(wxSize(FromDIP(58), FromDIP(24))); + m_button_OK->SetCornerRadius(FromDIP(12)); + bSizer_button->Add(m_button_OK, 0, wxRIGHT, FromDIP(10)); + + m_button_OK->Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent &e) { + if (!validate_input_valid()) return; + data_init(); + show_page2(); + }); + + StateColor btn_bg_white(std::pair(wxColour(206, 206, 206), StateColor::Pressed), std::pair(wxColour(238, 238, 238), StateColor::Hovered), + std::pair(*wxWHITE, StateColor::Normal)); + + m_button_page1_cancel = new Button(parent, _L("Cancel")); + m_button_page1_cancel->SetBackgroundColor(btn_bg_white); + m_button_page1_cancel->SetBorderColor(wxColour(38, 46, 48)); + m_button_page1_cancel->SetFont(Label::Body_12); + m_button_page1_cancel->SetSize(wxSize(FromDIP(58), FromDIP(24))); + m_button_page1_cancel->SetMinSize(wxSize(FromDIP(58), FromDIP(24))); + m_button_page1_cancel->SetCornerRadius(FromDIP(12)); + bSizer_button->Add(m_button_page1_cancel, 0, wxRIGHT, FromDIP(10)); + + m_button_page1_cancel->Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent &e) { EndModal(wxID_CANCEL); }); + + return bSizer_button; +} +static std::string last_directory = ""; +void CreatePrinterPresetDialog::load_texture() { + wxFileDialog dialog(this, _L("Choose a file to import bed texture from (PNG/SVG):"), last_directory, "", file_wildcards(FT_TEX), wxFD_OPEN | wxFD_FILE_MUST_EXIST); + + if (dialog.ShowModal() != wxID_OK) + return; + + m_custom_texture = ""; + m_upload_svg_tip_text->SetLabelText(_L("Empty")); + last_directory = dialog.GetDirectory().ToUTF8().data(); + std::string file_name = dialog.GetPath().ToUTF8().data(); + if (!boost::algorithm::iends_with(file_name, ".png") && !boost::algorithm::iends_with(file_name, ".svg")) { + show_error(this, _L("Invalid file format.")); + return; + } + bool try_ok; + if (Utils::is_file_too_large(file_name, try_ok)) { + if (try_ok) { + m_upload_svg_tip_text->SetLabelText(wxString::Format(_L("The file exceeds %d MB, please import again."), STL_SVG_MAX_FILE_SIZE_MB)); + } else { + m_upload_svg_tip_text->SetLabelText(_L("Exception in obtaining file size, please import again.")); + } + return; + } + m_custom_texture = file_name; + wxGCDC dc; + auto text = wxControl::Ellipsize(_L(boost::filesystem::path(file_name).filename().string()), dc, wxELLIPSIZE_END, FromDIP(200)); + m_upload_svg_tip_text->SetLabelText(text); +} + +void CreatePrinterPresetDialog::load_model_stl() +{ + wxFileDialog dialog(this, _L("Choose an STL file to import bed model from:"), last_directory, "", file_wildcards(FT_STL), wxFD_OPEN | wxFD_FILE_MUST_EXIST); + + if (dialog.ShowModal() != wxID_OK) + return; + + m_custom_model = ""; + m_upload_stl_tip_text->SetLabelText(_L("Empty")); + last_directory = dialog.GetDirectory().ToUTF8().data(); + std::string file_name = dialog.GetPath().ToUTF8().data(); + if (!boost::algorithm::iends_with(file_name, ".stl")) { + show_error(this, _L("Invalid file format.")); + return; + } + bool try_ok; + if (Utils::is_file_too_large(file_name, try_ok)) { + if (try_ok) { + m_upload_stl_tip_text->SetLabelText(wxString::Format(_L("The file exceeds %d MB, please import again."), STL_SVG_MAX_FILE_SIZE_MB)); + } + else { + m_upload_stl_tip_text->SetLabelText(_L("Exception in obtaining file size, please import again.")); + } + return; + } + m_custom_model = file_name; + wxGCDC dc; + auto text = wxControl::Ellipsize(_L(boost::filesystem::path(file_name).filename().string()), dc, wxELLIPSIZE_END, FromDIP(200)); + m_upload_stl_tip_text->SetLabelText(text); +} + +bool CreatePrinterPresetDialog::load_system_and_user_presets_with_curr_model(PresetBundle &temp_preset_bundle, bool just_template) +{ + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " is load template: "<< just_template; + std::string selected_vendor_id; + std::string preset_path; + if (m_printer_preset) { + delete m_printer_preset; + m_printer_preset = nullptr; + } + + std::string curr_selected_model = into_u8(m_printer_model->GetStringSelection()); + int nozzle_index = curr_selected_model.find_first_of("@"); + std::string select_model = curr_selected_model.substr(0, nozzle_index - 1); + for (const Slic3r::VendorProfile::PrinterModel &model : m_printer_preset_vendor_selected.models) { + if (model.name == select_model) { + m_printer_preset_model_selected = model; + break; + } + } + if (m_printer_preset_vendor_selected.id.empty() || m_printer_preset_model_selected.id.empty()) { + BOOST_LOG_TRIVIAL(info) << "selected id is not found"; + MessageDialog dlg(this, _L("Preset path is not found, please reselect vendor."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxYES_NO | wxYES_DEFAULT | wxCENTRE); + dlg.ShowModal(); + return false; + } + + bool is_custom_vendor = false; + if (PRESET_CUSTOM_VENDOR == m_printer_preset_vendor_selected.name || PRESET_CUSTOM_VENDOR == m_printer_preset_vendor_selected.id) { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " select custom vendor "; + is_custom_vendor = true; + temp_preset_bundle = *(wxGetApp().preset_bundle); + } else { + selected_vendor_id = m_printer_preset_vendor_selected.id; + + if (boost::filesystem::exists(boost::filesystem::path(Slic3r::data_dir()) / PRESET_SYSTEM_DIR / selected_vendor_id)) { + preset_path = (boost::filesystem::path(Slic3r::data_dir()) / PRESET_SYSTEM_DIR).string(); + } else if (boost::filesystem::exists(boost::filesystem::path(Slic3r::resources_dir()) / "profiles" / selected_vendor_id)) { + preset_path = (boost::filesystem::path(Slic3r::resources_dir()) / "profiles").string(); + } + + if (preset_path.empty()) { + BOOST_LOG_TRIVIAL(info) << "Preset path is not found"; + MessageDialog dlg(this, _L("Preset path is not found, please reselect vendor."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), + wxYES_NO | wxYES_DEFAULT | wxCENTRE); + dlg.ShowModal(); + return false; + } + + try { + temp_preset_bundle.load_vendor_configs_from_json(preset_path, selected_vendor_id, PresetBundle::LoadConfigBundleAttribute::LoadSystem, + ForwardCompatibilitySubstitutionRule::EnableSilent); + } catch (...) { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "load vendor fonfigs form json failed"; + MessageDialog dlg(this, _L("The printer model was not found, please reselect."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), + wxYES_NO | wxYES_DEFAULT | wxCENTRE); + dlg.ShowModal(); + return false; + } + + if (!just_template) { + std::string dir_user_presets = wxGetApp().app_config->get("preset_folder"); + if (dir_user_presets.empty()) { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "default user presets path"; + temp_preset_bundle.load_user_presets(DEFAULT_USER_FOLDER_NAME, ForwardCompatibilitySubstitutionRule::EnableSilent); + } else { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "user presets path"; + temp_preset_bundle.load_user_presets(dir_user_presets, ForwardCompatibilitySubstitutionRule::EnableSilent); + } + } + } + //get model varient + std::string model_varient = into_u8(m_printer_model->GetStringSelection()); + size_t index_at = model_varient.find(" @ "); + size_t index_nozzle = model_varient.find("nozzle"); + std::string varient; + if (index_at != std::string::npos && index_nozzle != std::string::npos) { + varient = model_varient.substr(index_at + 3, index_nozzle - index_at - 4); + } else { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "get nozzle failed"; + MessageDialog dlg(this, _L("The nozzle diameter is not fond, place reselect."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxYES_NO | wxYES_DEFAULT | wxCENTRE); + dlg.ShowModal(); + return false; + } + + const Preset *temp_printer_preset = is_custom_vendor ? temp_preset_bundle.printers.find_custom_preset_by_model_and_variant(m_printer_preset_model_selected.id, varient) : + temp_preset_bundle.printers.find_system_preset_by_model_and_variant(m_printer_preset_model_selected.id, varient); + + if (temp_printer_preset) { + m_printer_preset = new Preset(*temp_printer_preset); + } else { + MessageDialog dlg(this, _L("The printer preset is not fond, place reselect."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxYES_NO | wxYES_DEFAULT | wxCENTRE); + dlg.ShowModal(); + return false; + } + + if (!just_template) { + temp_preset_bundle.printers.select_preset_by_name(m_printer_preset->name, true); + temp_preset_bundle.update_compatible(PresetSelectCompatibleType::Always); + } else { + selected_vendor_id = PRESET_TEMPLATE_DIR; + preset_path.clear(); + if (boost::filesystem::exists(boost::filesystem::path(Slic3r::resources_dir()) / PRESET_PROFILES_TEMOLATE_DIR / selected_vendor_id)) { + preset_path = (boost::filesystem::path(Slic3r::resources_dir()) / PRESET_PROFILES_TEMOLATE_DIR).string(); + } + if (preset_path.empty()) { + BOOST_LOG_TRIVIAL(info) << "Preset path is not found"; + MessageDialog dlg(this, _L("Preset path is not found, please reselect vendor."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), + wxYES_NO | wxYES_DEFAULT | wxCENTRE); + dlg.ShowModal(); + return false; + } + try { + temp_preset_bundle.load_vendor_configs_from_json(preset_path, selected_vendor_id, PresetBundle::LoadConfigBundleAttribute::LoadSystem, + ForwardCompatibilitySubstitutionRule::EnableSilent); + } catch (...) { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "load template vendor configs form json failed"; + MessageDialog dlg(this, _L("The printer model was not found, please reselect."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), + wxYES_NO | wxYES_DEFAULT | wxCENTRE); + dlg.ShowModal(); + return false; + } + } + + return true; +} + +void CreatePrinterPresetDialog::generate_process_presets_data(std::vector presets, std::string nozzle) +{ + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " entry, and nozzle is: " << nozzle; + std::unordered_map nozzle_diameter_map_ = nozzle_diameter_map; + for (const Preset *preset : presets) { + float nozzle_dia = nozzle_diameter_map_[nozzle]; + assert(nozzle_dia != 0); + + auto layer_height = dynamic_cast(const_cast(preset)->config.option("layer_height", true)); + if (layer_height) + layer_height->value = nozzle_dia / 2; + else + BOOST_LOG_TRIVIAL(info) << "process template has no layer_height"; + + auto initial_layer_print_height = dynamic_cast(const_cast(preset)->config.option("initial_layer_print_height", true)); + if (initial_layer_print_height) + initial_layer_print_height->value = nozzle_dia / 2; + else + BOOST_LOG_TRIVIAL(info) << "process template has no initial_layer_print_height"; + + auto line_width = dynamic_cast(const_cast(preset)->config.option("line_width", true)); + if (line_width) + line_width->value = nozzle_dia; + else + BOOST_LOG_TRIVIAL(info) << "process template has no line_width"; + + auto initial_layer_line_width = dynamic_cast(const_cast(preset)->config.option("initial_layer_line_width", true)); + if (initial_layer_line_width) + initial_layer_line_width->value = nozzle_dia; + else + BOOST_LOG_TRIVIAL(info) << "process template has no initial_layer_line_width"; + + auto outer_wall_line_width = dynamic_cast(const_cast(preset)->config.option("outer_wall_line_width", true)); + if (outer_wall_line_width) + outer_wall_line_width->value = nozzle_dia; + else + BOOST_LOG_TRIVIAL(info) << "process template has no outer_wall_line_width"; + + auto inner_wall_line_width = dynamic_cast(const_cast(preset)->config.option("inner_wall_line_width", true)); + if (inner_wall_line_width) + inner_wall_line_width->value = nozzle_dia; + else + BOOST_LOG_TRIVIAL(info) << "process template has no inner_wall_line_width"; + + auto top_surface_line_width = dynamic_cast(const_cast(preset)->config.option("top_surface_line_width", true)); + if (top_surface_line_width) + top_surface_line_width->value = nozzle_dia; + else + BOOST_LOG_TRIVIAL(info) << "process template has no top_surface_line_width"; + + auto sparse_infill_line_width = dynamic_cast(const_cast(preset)->config.option("sparse_infill_line_width", true)); + if (sparse_infill_line_width) + sparse_infill_line_width->value = nozzle_dia; + else + BOOST_LOG_TRIVIAL(info) << "process template has no sparse_infill_line_width"; + + auto internal_solid_infill_line_width = dynamic_cast(const_cast(preset)->config.option("internal_solid_infill_line_width", true)); + if (internal_solid_infill_line_width) + internal_solid_infill_line_width->value = nozzle_dia; + else + BOOST_LOG_TRIVIAL(info) << "process template has no internal_solid_infill_line_width"; + + auto support_line_width = dynamic_cast(const_cast(preset)->config.option("support_line_width", true)); + if (support_line_width) + support_line_width->value = nozzle_dia; + else + BOOST_LOG_TRIVIAL(info) << "process template has no support_line_width"; + + auto wall_loops = dynamic_cast(const_cast(preset)->config.option("wall_loops", true)); + if (wall_loops) + wall_loops->value = std::max(2, (int) std::ceil(2 * 0.4 / nozzle_dia)); + else + BOOST_LOG_TRIVIAL(info) << "process template has no wall_loops"; + + auto top_shell_layers = dynamic_cast(const_cast(preset)->config.option("top_shell_layers", true)); + if (top_shell_layers) + top_shell_layers->value = std::max(5, (int) std::ceil(5 * 0.4 / nozzle_dia)); + else + BOOST_LOG_TRIVIAL(info) << "process template has no top_shell_layers"; + + auto bottom_shell_layers = dynamic_cast(const_cast(preset)->config.option("bottom_shell_layers", true)); + if (bottom_shell_layers) + bottom_shell_layers->value = std::max(3, (int) std::ceil(3 * 0.4 / nozzle_dia)); + else + BOOST_LOG_TRIVIAL(info) << "process template has no bottom_shell_layers"; + } +} + +void CreatePrinterPresetDialog::update_preset_list_size() +{ + m_scrolled_preset_window->Freeze(); + m_preset_template_panel->SetSizerAndFit(m_filament_sizer); + m_preset_template_panel->SetMinSize(wxSize(FromDIP(660), -1)); + m_preset_template_panel->SetSize(wxSize(FromDIP(660), -1)); + int width = m_preset_template_panel->GetSize().GetWidth(); + int height = m_preset_template_panel->GetSize().GetHeight(); + m_scrolled_preset_window->SetMinSize(wxSize(std::min(1500, width + FromDIP(26)), std::min(600, height))); + m_scrolled_preset_window->SetMaxSize(wxSize(std::min(1500, width + FromDIP(26)), std::min(600, height))); + m_scrolled_preset_window->SetSize(wxSize(std::min(1500, width + FromDIP(26)), std::min(600, height))); + m_page2->SetSizerAndFit(m_page2_sizer); + Layout(); + Fit(); + Refresh(); + adjust_dialog_in_screen(this); + m_scrolled_preset_window->Thaw(); +} + +wxBoxSizer *CreatePrinterPresetDialog::create_radio_item(wxString title, wxWindow *parent, wxString tooltip, std::vector> &radiobox_list) +{ + wxBoxSizer *horizontal_sizer = new wxBoxSizer(wxHORIZONTAL); + RadioBox * radiobox = new RadioBox(parent); + horizontal_sizer->Add(radiobox, 0, wxEXPAND | wxALL, 0); + horizontal_sizer->Add(0, 0, 0, wxEXPAND | wxLEFT, FromDIP(5)); + radiobox_list.push_back(std::make_pair(radiobox,title)); + int btn_idx = radiobox_list.size() - 1; + radiobox->Bind(wxEVT_LEFT_DOWN, [this, &radiobox_list, btn_idx](wxMouseEvent &e) { + select_curr_radiobox(radiobox_list, btn_idx); + }); + + wxStaticText *text = new wxStaticText(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize); + text->Bind(wxEVT_LEFT_DOWN, [this, &radiobox_list, btn_idx](wxMouseEvent &e) { + select_curr_radiobox(radiobox_list, btn_idx); + }); + horizontal_sizer->Add(text, 0, wxEXPAND | wxLEFT, 0); + + radiobox->SetToolTip(tooltip); + text->SetToolTip(tooltip); + return horizontal_sizer; + +} + +void CreatePrinterPresetDialog::select_curr_radiobox(std::vector> &radiobox_list, int btn_idx) +{ + int len = radiobox_list.size(); + for (int i = 0; i < len; ++i) { + if (i == btn_idx) { + radiobox_list[i].first->SetValue(true); + wxString curr_selected_type = radiobox_list[i].second; + this->Freeze(); + if (curr_selected_type == m_create_type.base_template) { + if (m_printer_model->GetValue() == _L("Select Model")) { + m_filament_preset_template_sizer->Clear(true); + m_filament_preset.clear(); + m_process_preset_template_sizer->Clear(true); + m_process_preset.clear(); + } else { + update_presets_list(true); + } + m_page2->SetSizerAndFit(m_page2_sizer); + } else if (curr_selected_type == m_create_type.base_curr_printer) { + if (m_printer_model->GetValue() == _L("Select Model")) { + m_filament_preset_template_sizer->Clear(true); + m_filament_preset.clear(); + m_process_preset_template_sizer->Clear(true); + m_process_preset.clear(); + } else { + update_presets_list(); + } + m_page2->SetSizerAndFit(m_page2_sizer); + } else if (curr_selected_type == m_create_type.create_printer) { + m_select_printer->Hide(); + m_can_not_find_vendor_combox->Show(); + m_can_not_find_vendor_text->Show(); + m_printer_info_panel->Show(); + if (m_can_not_find_vendor_combox->GetValue()) { + m_custom_vendor_text_ctrl->Show(); + m_custom_model_text_ctrl->Show(); + m_select_vendor->Hide(); + m_select_model->Hide(); + } else { + m_select_vendor->Show(); + m_select_model->Show(); + } + m_page1->SetSizerAndFit(m_page1_sizer); + } else if (curr_selected_type == m_create_type.create_nozzle) { + set_current_visible_printer(); + m_select_vendor->Hide(); + m_select_model->Hide(); + m_can_not_find_vendor_combox->Hide(); + m_can_not_find_vendor_text->Hide(); + m_custom_vendor_text_ctrl->Hide(); + m_custom_model_text_ctrl->Hide(); + m_printer_info_panel->Hide(); + m_select_printer->Show(); + m_page1->SetSizerAndFit(m_page1_sizer); + } + this->Thaw(); + } else { + radiobox_list[i].first->SetValue(false); + } + } + + update_preset_list_size(); +} + +void CreatePrinterPresetDialog::create_printer_page2(wxWindow *parent) +{ + this->SetBackgroundColour(*wxWHITE); + + m_page2_sizer = new wxBoxSizer(wxVERTICAL); + + m_page2_sizer->Add(create_printer_preset_item(parent), 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(5)); + m_page2_sizer->Add(create_presets_item(parent), 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(5)); + m_page2_sizer->Add(create_presets_template_item(parent), 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(5)); + m_page2_sizer->Add(create_page2_btns_item(parent), 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(5)); + + parent->SetSizerAndFit(m_page2_sizer); + Layout(); + Fit(); + + wxGetApp().UpdateDlgDarkUI(this); +} + +wxBoxSizer *CreatePrinterPresetDialog::create_printer_preset_item(wxWindow *parent) +{ + wxBoxSizer *horizontal_sizer = new wxBoxSizer(wxHORIZONTAL); + + wxBoxSizer * optionSizer = new wxBoxSizer(wxVERTICAL); + wxStaticText *static_vendor_text = new wxStaticText(parent, wxID_ANY, _L("Printer Preset"), wxDefaultPosition, wxDefaultSize); + optionSizer->Add(static_vendor_text, 0, wxEXPAND | wxALL, 0); + optionSizer->SetMinSize(OPTION_SIZE); + horizontal_sizer->Add(optionSizer, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(10)); + + wxBoxSizer * vertical_sizer = new wxBoxSizer(wxVERTICAL); + wxStaticText *combobox_title = new wxStaticText(parent, wxID_ANY, m_create_type.base_curr_printer, wxDefaultPosition, wxDefaultSize, 0); + combobox_title->SetFont(::Label::Body_13); + auto size = combobox_title->GetTextExtent(m_create_type.base_curr_printer); + combobox_title->SetMinSize(wxSize(size.x + FromDIP(4), -1)); + combobox_title->Wrap(-1); + vertical_sizer->Add(combobox_title, 0, wxEXPAND | wxALL, 0); + + wxBoxSizer *comboBox_sizer = new wxBoxSizer(wxHORIZONTAL); + m_printer_vendor = new ComboBox(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, PRINTER_PRESET_VENDOR_SIZE, 0, nullptr, wxCB_READONLY); + m_printer_vendor->SetValue(_L("Select Vendor")); + m_printer_vendor->SetLabelColor(DEFAULT_PROMPT_TEXT_COLOUR); + comboBox_sizer->Add(m_printer_vendor, 0, wxEXPAND, 0); + m_printer_model = new ComboBox(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, PRINTER_PRESET_MODEL_SIZE, 0, nullptr, wxCB_READONLY); + m_printer_model->SetLabelColor(DEFAULT_PROMPT_TEXT_COLOUR); + m_printer_model->SetValue(_L("Select Model")); + + comboBox_sizer->Add(m_printer_model, 0, wxEXPAND | wxLEFT, FromDIP(10)); + vertical_sizer->Add(comboBox_sizer, 0, wxEXPAND | wxTOP, FromDIP(5)); + + horizontal_sizer->Add(vertical_sizer, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(10)); + + return horizontal_sizer; + +} + +wxBoxSizer *CreatePrinterPresetDialog::create_presets_item(wxWindow *parent) +{ + wxBoxSizer *horizontal_sizer = new wxBoxSizer(wxHORIZONTAL); + + wxBoxSizer * optionSizer = new wxBoxSizer(wxVERTICAL); + wxStaticText *static_serial_text = new wxStaticText(parent, wxID_ANY, _L("Presets"), wxDefaultPosition, wxDefaultSize); + optionSizer->Add(static_serial_text, 0, wxEXPAND | wxALL, 0); + optionSizer->SetMinSize(OPTION_SIZE); + horizontal_sizer->Add(optionSizer, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(10)); + + wxBoxSizer *radioBoxSizer = new wxBoxSizer(wxVERTICAL); + + radioBoxSizer->Add(create_radio_item(m_create_type.base_template, parent, wxEmptyString, m_create_presets_btns), 0, wxEXPAND | wxALL, 0); + radioBoxSizer->Add(create_radio_item(m_create_type.base_curr_printer, parent, wxEmptyString, m_create_presets_btns), 0, wxEXPAND | wxTOP, FromDIP(10)); + horizontal_sizer->Add(radioBoxSizer, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(10)); + + return horizontal_sizer; +} + +wxBoxSizer *CreatePrinterPresetDialog::create_presets_template_item(wxWindow *parent) +{ + wxBoxSizer *vertical_sizer = new wxBoxSizer(wxVERTICAL); + + m_scrolled_preset_window = new wxScrolledWindow(parent); + m_scrolled_preset_window->SetScrollRate(5, 5); + m_scrolled_preset_window->SetBackgroundColour(*wxWHITE); + //m_scrolled_preset_window->SetMinSize(wxSize(FromDIP(1500), FromDIP(-1))); + m_scrolled_preset_window->SetMaxSize(wxSize(FromDIP(1500), FromDIP(-1))); + m_scrolled_preset_window->SetSize(wxSize(FromDIP(1500), FromDIP(-1))); + m_scrooled_preset_sizer = new wxBoxSizer(wxHORIZONTAL); + + m_preset_template_panel = new wxPanel(m_scrolled_preset_window); + m_preset_template_panel->SetSize(wxSize(-1, -1)); + m_preset_template_panel->SetBackgroundColour(PRINTER_LIST_COLOUR); + m_preset_template_panel->SetMinSize(wxSize(FromDIP(660), -1)); + m_filament_sizer = new wxBoxSizer(wxVERTICAL); + wxStaticText *static_filament_preset_text = new wxStaticText(m_preset_template_panel, wxID_ANY, _L("Filament Preset Template"), wxDefaultPosition, wxDefaultSize); + m_filament_sizer->Add(static_filament_preset_text, 0, wxEXPAND | wxALL, FromDIP(5)); + m_filament_preset_panel = new wxPanel(m_preset_template_panel); + m_filament_preset_template_sizer = new wxGridSizer(3, FromDIP(5), FromDIP(5)); + m_filament_preset_panel->SetSize(PRESET_TEMPLATE_SIZE); + m_filament_preset_panel->SetSizer(m_filament_preset_template_sizer); + m_filament_sizer->Add(m_filament_preset_panel, 0, wxEXPAND | wxALL, FromDIP(5)); + + wxBoxSizer *hori_filament_btn_sizer = new wxBoxSizer(wxHORIZONTAL); + wxPanel * filament_btn_panel = new wxPanel(m_preset_template_panel); + filament_btn_panel->SetBackgroundColour(FILAMENT_OPTION_COLOUR); + wxStaticText *filament_sel_all_text = new wxStaticText(filament_btn_panel, wxID_ANY, _L("Select All"), wxDefaultPosition, wxDefaultSize); + filament_sel_all_text->SetForegroundColour(SELECT_ALL_OPTION_COLOUR); + filament_sel_all_text->Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent &e) { + select_all_preset_template(m_filament_preset); + e.Skip(); + }); + wxStaticText *filament_desel_all_text = new wxStaticText(filament_btn_panel, wxID_ANY, _L("Deselect All"), wxDefaultPosition, wxDefaultSize); + filament_desel_all_text->SetForegroundColour(SELECT_ALL_OPTION_COLOUR); + filament_desel_all_text->Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent &e) { + deselect_all_preset_template(m_filament_preset); + e.Skip(); + }); + hori_filament_btn_sizer->Add(filament_sel_all_text, 0, wxEXPAND | wxALL, FromDIP(5)); + hori_filament_btn_sizer->Add(filament_desel_all_text, 0, wxEXPAND | wxALL, FromDIP(5)); + filament_btn_panel->SetSizer(hori_filament_btn_sizer); + m_filament_sizer->Add(filament_btn_panel, 0, wxEXPAND, 0); + + wxPanel *split_panel = new wxPanel(m_preset_template_panel, wxID_ANY, wxDefaultPosition, wxSize(-1, FromDIP(10))); + split_panel->SetBackgroundColour(wxColour(*wxWHITE)); + m_filament_sizer->Add(split_panel, 0, wxEXPAND, 0); + + wxStaticText *static_process_preset_text = new wxStaticText(m_preset_template_panel, wxID_ANY, _L("Process Preset Template"), wxDefaultPosition, wxDefaultSize); + m_filament_sizer->Add(static_process_preset_text, 0, wxEXPAND | wxALL, FromDIP(5)); + m_process_preset_panel = new wxPanel(m_preset_template_panel); + m_process_preset_panel->SetSize(PRESET_TEMPLATE_SIZE); + m_process_preset_template_sizer = new wxGridSizer(3, FromDIP(5), FromDIP(5)); + m_process_preset_panel->SetSizer(m_process_preset_template_sizer); + m_filament_sizer->Add(m_process_preset_panel, 0, wxEXPAND | wxALL, FromDIP(5)); + + + wxBoxSizer *hori_process_btn_sizer = new wxBoxSizer(wxHORIZONTAL); + wxPanel * process_btn_panel = new wxPanel(m_preset_template_panel); + process_btn_panel->SetBackgroundColour(FILAMENT_OPTION_COLOUR); + wxStaticText *process_sel_all_text = new wxStaticText(process_btn_panel, wxID_ANY, _L("Select All"), wxDefaultPosition, wxDefaultSize); + process_sel_all_text->SetForegroundColour(SELECT_ALL_OPTION_COLOUR); + process_sel_all_text->Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent &e) { + select_all_preset_template(m_process_preset); + e.Skip(); + }); + wxStaticText *process_desel_all_text = new wxStaticText(process_btn_panel, wxID_ANY, _L("Deselect All"), wxDefaultPosition, wxDefaultSize); + process_desel_all_text->SetForegroundColour(SELECT_ALL_OPTION_COLOUR); + process_desel_all_text->Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent &e) { + deselect_all_preset_template(m_process_preset); + e.Skip(); + }); + hori_process_btn_sizer->Add(process_sel_all_text, 0, wxEXPAND | wxALL, FromDIP(5)); + hori_process_btn_sizer->Add(process_desel_all_text, 0, wxEXPAND | wxALL, FromDIP(5)); + process_btn_panel->SetSizer(hori_process_btn_sizer); + m_filament_sizer->Add(process_btn_panel, 0, wxEXPAND, 0); + + m_preset_template_panel->SetSizer(m_filament_sizer); + m_scrooled_preset_sizer->Add(m_preset_template_panel, 0, wxEXPAND | wxALL, 0); + m_scrolled_preset_window->SetSizerAndFit(m_scrooled_preset_sizer); + vertical_sizer->Add(m_scrolled_preset_window, 0, wxEXPAND | wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, FromDIP(10)); + + return vertical_sizer; +} + +wxBoxSizer *CreatePrinterPresetDialog::create_page2_btns_item(wxWindow *parent) +{ + wxBoxSizer *bSizer_button = new wxBoxSizer(wxHORIZONTAL); + bSizer_button->Add(0, 0, 1, wxEXPAND, 0); + + StateColor btn_bg_green(std::pair(wxColour(27, 136, 68), StateColor::Pressed), std::pair(wxColour(61, 203, 115), StateColor::Hovered), + std::pair(wxColour(0, 174, 66), StateColor::Normal)); + + StateColor btn_bg_white(std::pair(wxColour(206, 206, 206), StateColor::Pressed), std::pair(wxColour(238, 238, 238), StateColor::Hovered), + std::pair(*wxWHITE, StateColor::Normal)); + + m_button_page2_back = new Button(parent, _L("Back Page 1")); + m_button_page2_back->SetBackgroundColor(btn_bg_white); + m_button_page2_back->SetBorderColor(wxColour(38, 46, 48)); + m_button_page2_back->SetFont(Label::Body_12); + m_button_page2_back->SetSize(wxSize(FromDIP(58), FromDIP(24))); + m_button_page2_back->SetMinSize(wxSize(FromDIP(58), FromDIP(24))); + m_button_page2_back->SetCornerRadius(FromDIP(12)); + bSizer_button->Add(m_button_page2_back, 0, wxRIGHT, FromDIP(10)); + + m_button_page2_back->Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent &e) { show_page1(); }); + + m_button_create = new Button(parent, _L("Create")); + m_button_create->SetBackgroundColor(btn_bg_green); + m_button_create->SetBorderColor(*wxWHITE); + m_button_create->SetTextColor(wxColour(0xFFFFFE)); + m_button_create->SetFont(Label::Body_12); + m_button_create->SetSize(wxSize(FromDIP(58), FromDIP(24))); + m_button_create->SetMinSize(wxSize(FromDIP(58), FromDIP(24))); + m_button_create->SetCornerRadius(FromDIP(12)); + bSizer_button->Add(m_button_create, 0, wxRIGHT, FromDIP(10)); + + m_button_create->Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent &e) { + + PresetBundle *preset_bundle = wxGetApp().preset_bundle; + const wxString curr_selected_printer_type = curr_create_printer_type(); + const wxString curr_selected_preset_type = curr_create_preset_type(); + + // Confirm if the printer preset exists + if (!m_printer_preset) { + MessageDialog dlg(this, _L("You have not yet chosen which printer preset to create based on. Please choose the vendor and model of the printer"), + wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxYES | wxYES_DEFAULT | wxCENTRE); + dlg.ShowModal(); + return; + } + + if (!save_printable_area_config(m_printer_preset)) { + MessageDialog dlg(this, _L("You have entered an illegal input in the printable area section on the first page. Please check before creating it."), + wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxYES | wxYES_DEFAULT | wxCENTRE); + dlg.ShowModal(); + show_page1(); + return; + } + + // create preset name + std::string printer_preset_name; + std::string printer_model_name; + std::string printer_nozzle_name; + std::string nozzle_diameter = into_u8(m_nozzle_diameter->GetStringSelection()); + size_t index_mm = nozzle_diameter.find("mm"); + if (std::string::npos != index_mm) { + nozzle_diameter.replace(index_mm, 2, "nozzle"); + } + if (curr_selected_printer_type == m_create_type.create_printer) { + if (m_can_not_find_vendor_combox->GetValue()) { + std::string custom_vendor = into_u8(m_custom_vendor_text_ctrl->GetValue()); + std::string custom_model = into_u8(m_custom_model_text_ctrl->GetValue()); + if (custom_vendor.empty() || custom_model.empty()) { + MessageDialog dlg(this, _L("The custom printer or model is not inputed, place input."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), + wxYES | wxYES_DEFAULT | wxCENTRE); + dlg.ShowModal(); + show_page1(); + return; + } + custom_vendor = remove_special_key(custom_vendor); + custom_model = remove_special_key(custom_model); + boost::algorithm::trim(custom_vendor); + boost::algorithm::trim(custom_model); + + printer_preset_name = custom_vendor + " " + custom_model + " " + nozzle_diameter; + printer_model_name = custom_vendor + " " + custom_model; + } else { + std::string vender_name = into_u8(m_select_vendor->GetStringSelection()); + std::string model_name = into_u8(m_select_model->GetStringSelection()); + printer_preset_name = vender_name + " " + model_name + " " + nozzle_diameter; + printer_model_name = vender_name + " " + model_name; + + } + } else if (curr_selected_printer_type == m_create_type.create_nozzle) { + std::string selected_printer_preset_name = into_u8(m_select_printer->GetStringSelection()); + std::unordered_map>::iterator itor = m_printer_name_to_preset.find(selected_printer_preset_name); + assert(m_printer_name_to_preset.end() != itor); + if (m_printer_name_to_preset.end() != itor) { + std::shared_ptr printer_preset = itor->second; + try{ + printer_model_name = printer_preset->config.opt_string("printer_model", true); + printer_preset_name = printer_model_name + " " + nozzle_diameter; + } + catch (...) { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " get config printer_model or , and the name is: " << selected_printer_preset_name; + } + + } else { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " don't get printer preset, and the name is: " << selected_printer_preset_name; + } + } + printer_nozzle_name = nozzle_diameter.substr(0, nozzle_diameter.find(" nozzle")); + + // Confirm if the printer preset has a duplicate name + if (!rewritten && preset_bundle->printers.find_preset(printer_preset_name)) { + MessageDialog dlg(this, + _L("The printer preset you created already has a preset with the same name. Do you want to overwrite it?\n\tYes: Overwrite the printer preset with the " + "same name, and filament and process presets with the same preset name will be recreated \nand filament and process presets without the same preset name will be reserve.\n\tCancel: Do not create a preset, return to the " + "creation interface."), + wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxYES | wxCANCEL | wxYES_DEFAULT | wxCENTRE); + int res = dlg.ShowModal(); + if (res == wxID_YES) { + rewritten = true; + } else { + return; + } + } + + // Confirm if the filament preset is exist + bool filament_preset_is_exist = false; + std::vector selected_filament_presets; + for (std::pair<::CheckBox *, Preset const *> filament_preset : m_filament_preset) { + if (filament_preset.first->GetValue()) { selected_filament_presets.push_back(filament_preset.second); } + if (!filament_preset_is_exist && preset_bundle->filaments.find_preset(filament_preset.second->alias + " @ " + printer_preset_name) != nullptr) { + filament_preset_is_exist = true; + } + } + if (selected_filament_presets.empty() && !filament_preset_is_exist) { + MessageDialog dlg(this, _L("You need to select at least one filament preset."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxYES | wxYES_DEFAULT | wxCENTRE); + dlg.ShowModal(); + return; + } + + // Confirm if the process preset is exist + bool process_preset_is_exist = false; + std::vector selected_process_presets; + for (std::pair<::CheckBox *, Preset const *> process_preset : m_process_preset) { + if (process_preset.first->GetValue()) { selected_process_presets.push_back(process_preset.second); } + if (!process_preset_is_exist && preset_bundle->prints.find_preset(process_preset.second->alias + " @" + printer_preset_name) != nullptr) { + process_preset_is_exist = true; + } + } + if (selected_process_presets.empty() && !process_preset_is_exist) { + MessageDialog dlg(this, _L("You need to select at least one process preset."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxYES | wxYES_DEFAULT | wxCENTRE); + dlg.ShowModal(); + return; + } + + std::vector successful_preset_names; + if (curr_selected_preset_type == m_create_type.base_template) { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " base template"; + /****************************** clone filament preset ********************************/ + std::vector failures; + if (!selected_filament_presets.empty()) { + bool create_preset_result = preset_bundle->filaments.clone_presets_for_printer(selected_filament_presets, failures, printer_preset_name, get_filament_id, rewritten); + if (!create_preset_result) { + std::string message; + for (const std::string &failure : failures) { message += "\t" + failure + "\n"; } + MessageDialog dlg(this, _L("Create filament presets failed. As follows:\n") + from_u8(message) + _L("\nDo you want to rewrite it?"), + wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), + wxYES | wxYES_DEFAULT | wxCENTRE); + int res = dlg.ShowModal(); + if (wxID_YES == res) { + create_preset_result = preset_bundle->filaments.clone_presets_for_printer(selected_filament_presets, failures, printer_preset_name, + get_filament_id, true); + } else { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " printer preset no same preset but filament has same preset, user cancel create the printer preset"; + return; + } + } + // save created successfully preset name + for (Preset const *sucessful_preset : selected_filament_presets) + successful_preset_names.push_back(sucessful_preset->name.substr(0, sucessful_preset->name.find(" @")) + " @" + printer_preset_name); + } + + /****************************** clone process preset ********************************/ + failures.clear(); + if (!selected_process_presets.empty()) { + generate_process_presets_data(selected_process_presets, printer_nozzle_name); + bool create_preset_result = preset_bundle->prints.clone_presets_for_printer(selected_process_presets, failures, printer_preset_name, + get_filament_id, rewritten); + if (!create_preset_result) { + std::string message; + for (const std::string &failure : failures) { message += "\t" + failure + "\n"; } + MessageDialog dlg(this, _L("Create process presets failed. As follows:\n") + from_u8(message) + _L("\nDo you want to rewrite it?"), + wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), + wxYES | wxYES_DEFAULT | wxCENTRE); + int res = dlg.ShowModal(); + if (wxID_YES == res) { + create_preset_result = preset_bundle->prints.clone_presets_for_printer(selected_process_presets, failures, printer_preset_name, get_filament_id, true); + } else { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " printer preset no same preset but process has same preset, user cancel create the printer preset"; + return; + } + } + } + } else if (curr_selected_preset_type == m_create_type.base_curr_printer) { // create printer and based on printer + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " base curr printer"; + /****************************** clone filament preset ********************************/ + std::vector failures; + if (!selected_filament_presets.empty()) { + bool create_preset_result = preset_bundle->filaments.clone_presets_for_printer(selected_filament_presets, failures, printer_preset_name, get_filament_id, rewritten); + if (!create_preset_result) { + std::string message; + for (const std::string& failure : failures) { + message += "\t" + failure + "\n"; + } + MessageDialog dlg(this, _L("Create filament presets failed. As follows:\n") + from_u8(message) + _L("\nDo you want to rewrite it?"), + wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), + wxYES | wxYES_DEFAULT | wxCENTRE); + int res = dlg.ShowModal(); + if (wxID_YES == res) { + create_preset_result = preset_bundle->filaments.clone_presets_for_printer(selected_filament_presets, failures, printer_preset_name, get_filament_id, true); + } else { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " printer preset no same preset but filament has same preset, user cancel create the printer preset"; + return; + } + } + } + + /****************************** clone process preset ********************************/ + failures.clear(); + if (!selected_process_presets.empty()) { + bool create_preset_result = preset_bundle->prints.clone_presets_for_printer(selected_process_presets, failures, printer_preset_name, get_filament_id, rewritten); + if (!create_preset_result) { + std::string message; + for (const std::string& failure : failures) { + message += "\t" + failure + "\n"; + } + MessageDialog dlg(this, _L("Create process presets failed. As follows:\n") + from_u8(message) + _L("\nDo you want to rewrite it?"), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxYES | wxYES_DEFAULT | wxCENTRE); + int res = dlg.ShowModal(); + if (wxID_YES == res) { + create_preset_result = preset_bundle->prints.clone_presets_for_printer(selected_process_presets, failures, printer_preset_name, get_filament_id, true); + } else { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " printer preset no same preset but filament has same preset, user cancel create the printer preset"; + return; + } + } + // save created successfully preset name + for (Preset const *sucessful_preset : selected_filament_presets) + successful_preset_names.push_back(sucessful_preset->name.substr(0, sucessful_preset->name.find(" @")) + " @" + printer_preset_name); + } + } + + /****************************** clone printer preset ********************************/ + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":creater printer "; + try { + auto printer_model = dynamic_cast(m_printer_preset->config.option("printer_model", true)); + if (printer_model) + printer_model->value = printer_model_name; + + auto printer_variant = dynamic_cast(m_printer_preset->config.option("printer_variant", true)); + if (printer_variant) + printer_variant->value = printer_nozzle_name; + + auto nozzle_diameter = dynamic_cast(m_printer_preset->config.option("nozzle_diameter", true)); + if (nozzle_diameter) { + std::unordered_map::const_iterator iter = nozzle_diameter_map.find(printer_nozzle_name); + if (nozzle_diameter_map.end() != iter) { + nozzle_diameter->values = {iter->second}; + } + } + } + catch (...) { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " bisic info is not rewritten, may be printer_model, printer_variant, or nozzle_diameter"; + } + preset_bundle->printers.save_current_preset(printer_preset_name, true, false, m_printer_preset); + preset_bundle->update_compatible(PresetSelectCompatibleType::Always); + EndModal(wxID_OK); + + }); + + m_button_page2_cancel = new Button(parent, _L("Cancel")); + m_button_page2_cancel->SetBackgroundColor(btn_bg_white); + m_button_page2_cancel->SetBorderColor(wxColour(38, 46, 48)); + m_button_page2_cancel->SetFont(Label::Body_12); + m_button_page2_cancel->SetSize(wxSize(FromDIP(58), FromDIP(24))); + m_button_page2_cancel->SetMinSize(wxSize(FromDIP(58), FromDIP(24))); + m_button_page2_cancel->SetCornerRadius(FromDIP(12)); + bSizer_button->Add(m_button_page2_cancel, 0, wxRIGHT, FromDIP(10)); + + m_button_page2_cancel->Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent &e) { EndModal(wxID_CANCEL); }); + + return bSizer_button; +} + +void CreatePrinterPresetDialog::show_page1() +{ + m_step_1->SetBitmap(create_scaled_bitmap("step_1", nullptr, FromDIP(20))); + m_step_2->SetBitmap(create_scaled_bitmap("step_2_ready", nullptr, FromDIP(20))); + m_page1->Show(); + m_page2->Hide(); + Refresh(); + Layout(); + Fit(); +} + +void CreatePrinterPresetDialog::show_page2() +{ + m_step_1->SetBitmap(create_scaled_bitmap("step_is_ok", nullptr, FromDIP(20))); + m_step_2->SetBitmap(create_scaled_bitmap("step_2", nullptr, FromDIP(20))); + m_page2->Show(); + m_page1->Hide(); + Refresh(); + Layout(); + Fit(); +} + +bool CreatePrinterPresetDialog::data_init() +{ + std::string nozzle_type = into_u8(m_nozzle_diameter->GetStringSelection()); + size_t index_mm = nozzle_type.find(" mm"); + if (std::string::npos != index_mm) { + nozzle_type = nozzle_type.substr(0, index_mm); + } + float nozzle = nozzle_diameter_map[nozzle_type]; + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " entry and nozzle type is: " << nozzle_type << " and nozzle is: " << nozzle; + + VendorMap vendors; + wxArrayString exist_vendor_choice = get_exist_vendor_choices(vendors); + m_printer_vendor->Set(exist_vendor_choice); + + m_printer_model->Bind(wxEVT_COMBOBOX, &CreatePrinterPresetDialog::on_preset_model_value_change, this); + + m_printer_vendor->Bind(wxEVT_COMBOBOX, [this, vendors, nozzle](wxCommandEvent e) { + m_printer_vendor->SetLabelColor(*wxBLACK); + + std::string curr_selected_vendor = into_u8(m_printer_vendor->GetStringSelection()); + auto iterator = vendors.find(curr_selected_vendor); + if (iterator != vendors.end()) { + m_printer_preset_vendor_selected = iterator->second; + } else { + MessageDialog dlg(this, _L("Vendor is not found, please reselect."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxYES_NO | wxYES_DEFAULT | wxCENTRE); + dlg.ShowModal(); + return; + } + + wxArrayString printer_preset_model = printer_preset_sort_with_nozzle_diameter(m_printer_preset_vendor_selected, nozzle); + if (printer_preset_model.size() == 0) { + MessageDialog dlg(this, _L("Current vendor has no models, please reselect."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxYES | wxYES_DEFAULT | wxCENTRE); + dlg.ShowModal(); + return; + } + m_printer_model->Set(printer_preset_model); + if (!printer_preset_model.empty()) { + m_printer_model->SetSelection(0); + wxCommandEvent e; + on_preset_model_value_change(e); + update_preset_list_size(); + } + rewritten = false; + e.Skip(); + + }); + return true; + +} + +void CreatePrinterPresetDialog::set_current_visible_printer() +{ + //The entire process of creating a custom printer only needs to be done once + if (m_printer_name_to_preset.size() > 0) return; + PresetBundle *preset_bundle = wxGetApp().preset_bundle; + const std::deque &printer_presets = preset_bundle->printers.get_presets(); + wxArrayString printer_choice; + m_printer_name_to_preset.clear(); + for (const Preset &printer_preset : printer_presets) { + if (printer_preset.is_system || !printer_preset.is_visible) continue; + if (preset_bundle->printers.get_preset_base(printer_preset)->name != printer_preset.name) continue; + printer_choice.push_back(from_u8(printer_preset.name)); + m_printer_name_to_preset[printer_preset.name] = std::make_shared(printer_preset); + } + m_select_printer->Set(printer_choice); +} + +wxArrayString CreatePrinterPresetDialog::printer_preset_sort_with_nozzle_diameter(const VendorProfile &vendor_profile, float nozzle_diameter) +{ + std::vector> preset_sort; + + for (const Slic3r::VendorProfile::PrinterModel &model : vendor_profile.models) { + std::string model_name = model.name; + for (const Slic3r::VendorProfile::PrinterVariant &variant : model.variants) { + try { + float variant_diameter = std::stof(variant.name); + preset_sort.push_back(std::make_pair(variant_diameter, model_name + " @ " + variant.name + " nozzle")); + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "nozzle: " << variant_diameter << "model: " << preset_sort.back().second; + } + catch (...) { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " prase varient fialed and the model_name is: " << model_name; + continue; + } + } + } + + std::sort(preset_sort.begin(), preset_sort.end(), [](const std::pair &a, const std::pair &b) { return a.first < b.first; }); + + int index_nearest_nozzle = -1; + float nozzle_diameter_diff = 1; + for (int i = 0; i < preset_sort.size(); ++i) { + float curr_nozzle_diameter_diff = std::abs(nozzle_diameter - preset_sort[i].first); + if (curr_nozzle_diameter_diff < nozzle_diameter_diff) { + index_nearest_nozzle = i; + nozzle_diameter_diff = curr_nozzle_diameter_diff; + if (curr_nozzle_diameter_diff == 0) break; + } + } + wxArrayString printer_preset_model_selection; + int right_index = index_nearest_nozzle + 1; + while (index_nearest_nozzle >= 0 || right_index < preset_sort.size()) { + if (index_nearest_nozzle >= 0 && right_index < preset_sort.size()) { + float left_nozzle_diff = std::abs(nozzle_diameter - preset_sort[index_nearest_nozzle].first); + float right_nozzle_diff = std::abs(nozzle_diameter - preset_sort[right_index].first); + bool left_is_little = left_nozzle_diff < right_nozzle_diff; + if (left_is_little) { + printer_preset_model_selection.Add(from_u8(preset_sort[index_nearest_nozzle].second)); + index_nearest_nozzle--; + } else { + printer_preset_model_selection.Add(from_u8(preset_sort[right_index].second)); + right_index++; + } + } else if (index_nearest_nozzle >= 0) { + printer_preset_model_selection.Add(from_u8(preset_sort[index_nearest_nozzle].second)); + index_nearest_nozzle--; + } else if (right_index < preset_sort.size()) { + printer_preset_model_selection.Add(from_u8(preset_sort[right_index].second)); + right_index++; + } + } + return printer_preset_model_selection; +} + +void CreatePrinterPresetDialog::select_all_preset_template(std::vector> &preset_templates) +{ + for (std::pair<::CheckBox *, Preset const *> filament_preset : preset_templates) { + filament_preset.first->SetValue(true); + } +} + +void CreatePrinterPresetDialog::deselect_all_preset_template(std::vector> &preset_templates) +{ + for (std::pair<::CheckBox *, Preset const *> filament_preset : preset_templates) { + filament_preset.first->SetValue(false); + } +} + +void CreatePrinterPresetDialog::update_presets_list(bool just_template) +{ + PresetBundle temp_preset_bundle; + if (!load_system_and_user_presets_with_curr_model(temp_preset_bundle, just_template)) return; + + const std::deque &filament_presets = temp_preset_bundle.filaments.get_presets(); + const std::deque &process_presets = temp_preset_bundle.prints.get_presets(); + + // clear filament preset window sizer + m_preset_template_panel->Freeze(); + clear_preset_combobox(); + + // update filament preset window sizer + for (const Preset &filament_preset : filament_presets) { + if (filament_preset.is_compatible) { + if (filament_preset.is_default) continue; + Preset *temp_filament = new Preset(filament_preset); + wxString filament_name = wxString::FromUTF8(temp_filament->name); + m_filament_preset_template_sizer->Add(create_checkbox(m_filament_preset_panel, temp_filament, filament_name, m_filament_preset), 0, + wxEXPAND, FromDIP(5)); + } + } + + for (const Preset &process_preset : process_presets) { + if (process_preset.is_compatible) { + if (process_preset.is_default) continue; + + Preset *temp_process = new Preset(process_preset); + wxString process_name = wxString::FromUTF8(temp_process->name); + m_process_preset_template_sizer->Add(create_checkbox(m_process_preset_panel, temp_process, process_name, m_process_preset), 0, wxEXPAND, + FromDIP(5)); + } + } + m_preset_template_panel->Thaw(); +} + +void CreatePrinterPresetDialog::clear_preset_combobox() +{ + for (std::pair<::CheckBox *, Preset *> preset : m_filament_preset) { + if (preset.second) { + delete preset.second; + preset.second = nullptr; + } + } + m_filament_preset.clear(); + m_filament_preset_template_sizer->Clear(true); + + for (std::pair<::CheckBox *, Preset *> preset : m_process_preset) { + if (preset.second) { + delete preset.second; + preset.second = nullptr; + } + } + m_process_preset.clear(); + m_process_preset_template_sizer->Clear(true); +} + +bool CreatePrinterPresetDialog::save_printable_area_config(Preset *preset) +{ + const wxString curr_selected_printer_type = curr_create_printer_type(); + DynamicPrintConfig &config = preset->config; + + if (curr_selected_printer_type == m_create_type.create_printer) { + double x = 0; + m_bed_size_x_input->GetTextCtrl()->GetValue().ToDouble(&x); + double y = 0; + m_bed_size_y_input->GetTextCtrl()->GetValue().ToDouble(&y); + double dx = 0; + m_bed_origin_x_input->GetTextCtrl()->GetValue().ToDouble(&dx); + double dy = 0; + m_bed_origin_y_input->GetTextCtrl()->GetValue().ToDouble(&dy); + // range check begin + if (x == 0 || y == 0) { return false; } + double x0 = 0.0; + double y0 = 0.0; + double x1 = x; + double y1 = y; + if (dx >= x || dy >= y) { return false; } + x0 -= dx; + x1 -= dx; + y0 -= dy; + y1 -= dy; + // range check end + std::vector points = {Vec2d(x0, y0), Vec2d(x1, y0), Vec2d(x1, y1), Vec2d(x0, y1)}; + config.set_key_value("printable_area", new ConfigOptionPoints(points)); + + double max_print_height = 0; + m_print_height_input->GetTextCtrl()->GetValue().ToDouble(&max_print_height); + config.set("printable_height", max_print_height); + + Utils::slash_to_back_slash(m_custom_texture); + Utils::slash_to_back_slash(m_custom_model); + config.set("bed_custom_model", m_custom_model); + config.set("bed_custom_texture", m_custom_texture); + } else if(m_create_type.create_nozzle){ + std::string selected_printer_preset_name = into_u8(m_select_printer->GetStringSelection()); + std::unordered_map>::iterator itor = m_printer_name_to_preset.find(selected_printer_preset_name); + assert(m_printer_name_to_preset.end() != itor); + if (m_printer_name_to_preset.end() != itor) { + std::shared_ptr printer_preset = itor->second; + std::vector keys = {"printable_area", "printable_height", "bed_custom_model", "bed_custom_texture"}; + config.apply_only(printer_preset->config, keys, true); + } + } + return true; +} + +bool CreatePrinterPresetDialog::check_printable_area() { + double x = 0; + m_bed_size_x_input->GetTextCtrl()->GetValue().ToDouble(&x); + double y = 0; + m_bed_size_y_input->GetTextCtrl()->GetValue().ToDouble(&y); + double dx = 0; + m_bed_origin_x_input->GetTextCtrl()->GetValue().ToDouble(&dx); + double dy = 0; + m_bed_origin_y_input->GetTextCtrl()->GetValue().ToDouble(&dy); + // range check begin + if (x == 0 || y == 0) { + return false; + } + double x0 = 0.0; + double y0 = 0.0; + double x1 = x; + double y1 = y; + if (dx >= x || dy >= y) { + return false; + } + return true; +} + +bool CreatePrinterPresetDialog::validate_input_valid() +{ + const wxString curr_selected_printer_type = curr_create_printer_type(); + if (curr_selected_printer_type == m_create_type.create_printer) { + std::string vendor_name, model_name; + if (m_can_not_find_vendor_combox->GetValue()) { + vendor_name = into_u8(m_custom_vendor_text_ctrl->GetValue()); + model_name = into_u8(m_custom_model_text_ctrl->GetValue()); + + } else { + vendor_name = into_u8(m_select_vendor->GetStringSelection()); + model_name = into_u8(m_select_model->GetStringSelection()); + } + if ((vendor_name.empty() || model_name.empty())) { + MessageDialog dlg(this, _L("You have not selected the vendor and model or inputed the custom vendor and model."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), + wxYES | wxYES_DEFAULT | wxCENTRE); + dlg.ShowModal(); + return false; + } + + vendor_name = remove_special_key(vendor_name); + model_name = remove_special_key(model_name); + if (vendor_name.empty() || model_name.empty()) { + MessageDialog dlg(this, _L("There may be escape characters in the custom printer vendor or model. Please delete and re-enter."), + wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxYES | wxYES_DEFAULT | wxCENTRE); + dlg.ShowModal(); + return false; + } + boost::algorithm::trim(vendor_name); + boost::algorithm::trim(model_name); + if (vendor_name.empty() || model_name.empty()) { + MessageDialog dlg(this, _L("All inputs in the custom printer vendor or model are spaces. Please re-enter."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), + wxYES | wxYES_DEFAULT | wxCENTRE); + dlg.ShowModal(); + return false; + } + + if (check_printable_area() == false) { + MessageDialog dlg(this, _L("Please check bed printable shape and origin input."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxYES | wxYES_DEFAULT | wxCENTRE); + dlg.ShowModal(); + return false; + } + } else if (curr_selected_printer_type == m_create_type.create_nozzle) { + wxString printer_name = m_select_printer->GetStringSelection(); + if (printer_name.empty()) { + MessageDialog dlg(this, _L("You have not yet selected the printer to replace the nozzle, please choose."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), + wxYES | wxYES_DEFAULT | wxCENTRE); + dlg.ShowModal(); + return false; + } + } + + return true; +} + +void CreatePrinterPresetDialog::on_preset_model_value_change(wxCommandEvent &e) +{ + m_printer_model->SetLabelColor(*wxBLACK); + if (m_printer_preset_vendor_selected.models.empty()) { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " selected vendor has no models, and the vendor is: " << m_printer_preset_vendor_selected.id; + return; + } + + wxString curr_selected_preset_type = curr_create_preset_type(); + if (curr_selected_preset_type == m_create_type.base_curr_printer) { + update_presets_list(); + } else if (curr_selected_preset_type == m_create_type.base_template) { + update_presets_list(true); + } + rewritten = false; + + update_preset_list_size(); + + e.Skip(); +} + +wxString CreatePrinterPresetDialog::curr_create_preset_type() +{ + wxString curr_selected_preset_type; + for (const std::pair &presets_radio : m_create_presets_btns) { + if (presets_radio.first->GetValue()) { + curr_selected_preset_type = presets_radio.second; + } + } + return curr_selected_preset_type; +} + +wxString CreatePrinterPresetDialog::curr_create_printer_type() +{ + wxString curr_selected_printer_type; + for (const std::pair &printer_radio : m_create_type_btns) { + if (printer_radio.first->GetValue()) { curr_selected_printer_type = printer_radio.second; } + } + return curr_selected_printer_type; +} + +CreatePresetSuccessfulDialog::CreatePresetSuccessfulDialog(wxWindow *parent, const SuccessType &create_success_type) + : DPIDialog(parent ? parent : nullptr, wxID_ANY, PRINTER == create_success_type ? _L("Create Printer Successful") : _L("Create Filament Successful"), wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX) +{ + this->SetBackgroundColour(*wxWHITE); + this->SetSize(wxSize(FromDIP(450), FromDIP(200))); + std::string icon_path = (boost::format("%1%/images/BambuStudioTitle.ico") % resources_dir()).str(); + SetIcon(wxIcon(encode_path(icon_path.c_str()), wxBITMAP_TYPE_ICO)); + + wxBoxSizer *m_main_sizer = new wxBoxSizer(wxVERTICAL); + // top line + auto m_line_top = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(-1, 1), wxTAB_TRAVERSAL); + m_line_top->SetBackgroundColour(wxColour(0xA6, 0xa9, 0xAA)); + m_main_sizer->Add(m_line_top, 0, wxEXPAND, 0); + m_main_sizer->Add(0, 0, 0, wxTOP, FromDIP(5)); + + wxBoxSizer *horizontal_sizer = new wxBoxSizer(wxHORIZONTAL); + horizontal_sizer->Add(0, 0, 0, wxLEFT, FromDIP(30)); + + wxBoxSizer *success_bitmap_sizer = new wxBoxSizer(wxVERTICAL); + wxStaticBitmap *success_bitmap = new wxStaticBitmap(this,wxID_ANY, create_scaled_bitmap("create_success", nullptr, FromDIP(24))); + success_bitmap_sizer->Add(success_bitmap, 0, wxEXPAND, 0); + horizontal_sizer->Add(success_bitmap_sizer, 0, wxEXPAND | wxALL, FromDIP(5)); + + wxBoxSizer *success_text_sizer = new wxBoxSizer(wxVERTICAL); + wxStaticText *success_text; + wxStaticText *next_step_text; + bool sync_user_preset_need_enabled = wxGetApp().getAgent() && wxGetApp().app_config->get("sync_user_preset") == "false"; + switch (create_success_type) { + case PRINTER: + success_text = new wxStaticText(this, wxID_ANY, _L("Printer Created")); + next_step_text = new wxStaticText(this, wxID_ANY, _L("Please go to printer settings to edit your presets")); + break; + case FILAMENT: + success_text = new wxStaticText(this, wxID_ANY, _L("Filament Created")); + wxString prompt_text = _L("Please go to filament setting to edit your presets if you need.\nPlease note that nozzle temperature, hot bed temperature, and maximum " + "volumetric speed has a significant impact on printing quality. Please set them carefully."); + wxString sync_text = sync_user_preset_need_enabled ? _L("Studio has detected that your user presets synchronization function is not enabled, which may result in unsuccessful Filament settings on " + "the Device page. \nClick \"Sync user presets\" to enable the synchronization function.") : ""; + next_step_text = new wxStaticText(this, wxID_ANY, prompt_text + "\n\n" + sync_text); + break; + } + success_text->SetFont(Label::Head_18); + success_text_sizer->Add(success_text, 0, wxEXPAND, 0); + success_text_sizer->Add(next_step_text, 0, wxEXPAND | wxTOP, FromDIP(5)); + horizontal_sizer->Add(success_text_sizer, 0, wxEXPAND | wxALL, FromDIP(5)); + horizontal_sizer->Add(0, 0, 0, wxLEFT, FromDIP(60)); + + m_main_sizer->Add(horizontal_sizer, 0, wxALL, FromDIP(5)); + + wxBoxSizer *btn_sizer = new wxBoxSizer(wxHORIZONTAL); + btn_sizer->Add(0, 0, 1, wxEXPAND, 0); + switch (create_success_type) { + case PRINTER: + m_button_ok = new Button(this, _L("Printer Setting")); + break; + case FILAMENT: m_button_ok = sync_user_preset_need_enabled ? new Button(this, _L("Sync user presets")) : new Button(this, _L("OK")); + break; + } + StateColor btn_bg_green(std::pair(wxColour(27, 136, 68), StateColor::Pressed), std::pair(wxColour(61, 203, 115), StateColor::Hovered), + std::pair(wxColour(0, 174, 66), StateColor::Normal)); + + StateColor btn_bg_white(std::pair(wxColour(206, 206, 206), StateColor::Pressed), std::pair(wxColour(238, 238, 238), StateColor::Hovered), + std::pair(*wxWHITE, StateColor::Normal)); + m_button_ok->SetBackgroundColor(btn_bg_green); + m_button_ok->SetBorderColor(wxColour(*wxWHITE)); + m_button_ok->SetTextColor(wxColour(*wxWHITE)); + m_button_ok->SetFont(Label::Body_12); + m_button_ok->SetSize(wxSize(FromDIP(58), FromDIP(24))); + m_button_ok->SetMinSize(wxSize(FromDIP(58), FromDIP(24))); + m_button_ok->SetCornerRadius(FromDIP(12)); + btn_sizer->Add(m_button_ok, 0, wxRIGHT, FromDIP(10)); + + m_button_ok->Bind(wxEVT_LEFT_DOWN, [this, sync_user_preset_need_enabled](wxMouseEvent &e) { + if (sync_user_preset_need_enabled) { + wxGetApp().app_config->set("sync_user_preset", "true"); + wxGetApp().start_sync_user_preset(); + } + EndModal(wxID_OK); + }); + + if (PRINTER == create_success_type || sync_user_preset_need_enabled) { + m_button_cancel = new Button(this, _L("Cancel")); + m_button_cancel->SetBackgroundColor(btn_bg_white); + m_button_cancel->SetBorderColor(wxColour(38, 46, 48)); + m_button_cancel->SetTextColor(wxColour(38, 46, 48)); + m_button_cancel->SetFont(Label::Body_12); + m_button_cancel->SetSize(wxSize(FromDIP(58), FromDIP(24))); + m_button_cancel->SetMinSize(wxSize(FromDIP(58), FromDIP(24))); + m_button_cancel->SetCornerRadius(FromDIP(12)); + btn_sizer->Add(m_button_cancel, 0, wxRIGHT, FromDIP(10)); + m_button_cancel->Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent &e) { EndModal(wxID_CANCEL); }); + } + + m_main_sizer->Add(btn_sizer, 0, wxEXPAND | wxALL, FromDIP(15)); + m_main_sizer->Add(0, 0, 0, wxTOP, FromDIP(10)); + + SetSizer(m_main_sizer); + Layout(); + Fit(); + wxGetApp().UpdateDlgDarkUI(this); +} + +CreatePresetSuccessfulDialog::~CreatePresetSuccessfulDialog() {} + +void CreatePresetSuccessfulDialog::on_dpi_changed(const wxRect &suggested_rect) { + m_button_ok->SetMinSize(wxSize(FromDIP(58), FromDIP(24))); + m_button_ok->SetMaxSize(wxSize(FromDIP(58), FromDIP(24))); + m_button_ok->SetCornerRadius(FromDIP(12)); + m_button_cancel->SetMinSize(wxSize(FromDIP(58), FromDIP(24))); + m_button_cancel->SetMaxSize(wxSize(FromDIP(58), FromDIP(24))); + m_button_cancel->SetCornerRadius(FromDIP(12)); + Layout(); +} + +ExportConfigsDialog::ExportConfigsDialog(wxWindow *parent) + : DPIDialog(parent ? parent : nullptr, wxID_ANY, _L("Export Preset Bundle"), wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX) +{ + m_exprot_type.preset_bundle = _L("Printer preset bundle(.bbscfg)"); + m_exprot_type.filament_bundle = _L("Filament preset bundle(.bbsflmt)"); + m_exprot_type.printer_preset = _L("Printer presets(.zip)"); + m_exprot_type.filament_preset = _L("Filament presets(.zip)"); + m_exprot_type.process_preset = _L("Process presets(.zip)"); + + this->SetBackgroundColour(*wxWHITE); + this->SetSize(wxSize(FromDIP(600), FromDIP(600))); + + std::string icon_path = (boost::format("%1%/images/BambuStudioTitle.ico") % resources_dir()).str(); + SetIcon(wxIcon(encode_path(icon_path.c_str()), wxBITMAP_TYPE_ICO)); + + m_main_sizer = new wxBoxSizer(wxVERTICAL); + // top line + auto m_line_top = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(-1, 1), wxTAB_TRAVERSAL); + m_line_top->SetBackgroundColour(wxColour(0xA6, 0xa9, 0xAA)); + m_main_sizer->Add(m_line_top, 0, wxEXPAND, 0); + m_main_sizer->Add(0, 0, 0, wxTOP, FromDIP(5)); + + m_main_sizer->Add(create_export_config_item(this), 0, wxEXPAND | wxALL, FromDIP(5)); + m_main_sizer->Add(create_select_printer(this), 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(5)); + m_main_sizer->Add(create_button_item(this), 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(5)); + + data_init(); + + this->SetSizer(m_main_sizer); + + this->Layout(); + this->Fit(); + + wxGetApp().UpdateDlgDarkUI(this); + +} + +ExportConfigsDialog::~ExportConfigsDialog() +{ + for (std::pair printer_preset : m_printer_presets) { + Preset *preset = printer_preset.second; + if (preset) { + delete preset; + preset = nullptr; + } + } + for (std::pair> filament_presets : m_filament_presets) { + for (Preset* preset : filament_presets.second) { + if (preset) { + delete preset; + preset = nullptr; + } + } + } + for (std::pair> filament_presets : m_process_presets) { + for (Preset *preset : filament_presets.second) { + if (preset) { + delete preset; + preset = nullptr; + } + } + } + for (std::pair>> filament_preset : m_filament_name_to_presets) { + for (std::pair printer_name_preset : filament_preset.second) { + Preset *preset = printer_name_preset.second; + if (preset) { + delete preset; + preset = nullptr; + } + } + } + + // Delete the Temp folder + boost::filesystem::path temp_folder(data_dir() + "/" + PRESET_USER_DIR + "/" + "Temp"); + if (boost::filesystem::exists(temp_folder)) boost::filesystem::remove_all(temp_folder); +} + +void ExportConfigsDialog::on_dpi_changed(const wxRect &suggested_rect) { + m_button_ok->SetMinSize(wxSize(FromDIP(58), FromDIP(24))); + m_button_ok->SetMaxSize(wxSize(FromDIP(58), FromDIP(24))); + m_button_ok->SetCornerRadius(FromDIP(12)); + m_button_cancel->SetMinSize(wxSize(FromDIP(58), FromDIP(24))); + m_button_cancel->SetMaxSize(wxSize(FromDIP(58), FromDIP(24))); + m_button_cancel->SetCornerRadius(FromDIP(12)); + Layout(); +} + +void ExportConfigsDialog::show_export_result(const ExportCase &export_case) +{ + MessageDialog *msg_dlg = nullptr; + switch (export_case) { + case ExportCase::INITIALIZE_FAIL: + msg_dlg = new MessageDialog(this, _L("initialize fail"), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxYES | wxYES_DEFAULT | wxCENTRE); + break; + case ExportCase::ADD_FILE_FAIL: + msg_dlg = new MessageDialog(this, _L("add file fail"), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxYES | wxYES_DEFAULT | wxCENTRE); + break; + case ExportCase::ADD_BUNDLE_STRUCTURE_FAIL: + msg_dlg = new MessageDialog(this, _L("add bundle structure file fail"), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxYES | wxYES_DEFAULT | wxCENTRE); + break; + case ExportCase::FINALIZE_FAIL: + msg_dlg = new MessageDialog(this, _L("finalize fail"), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxYES | wxYES_DEFAULT | wxCENTRE); + break; + case ExportCase::OPEN_ZIP_WRITTEN_FILE: + msg_dlg = new MessageDialog(this, _L("open zip written fail"), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxYES | wxYES_DEFAULT | wxCENTRE); + break; + case ExportCase::EXPORT_SUCCESS: + msg_dlg = new MessageDialog(this, _L("Export successful"), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxYES | wxYES_DEFAULT | wxCENTRE); + break; + } + + if (msg_dlg) { + msg_dlg->ShowModal(); + delete msg_dlg; + msg_dlg = nullptr; + } +} + +bool ExportConfigsDialog::has_check_box_selected() +{ + for (std::pair<::CheckBox *, Preset *> checkbox_preset : m_preset) { + if (checkbox_preset.first->GetValue()) return true; + } + for (std::pair<::CheckBox *, std::string> checkbox_filament_name : m_printer_name) { + if (checkbox_filament_name.first->GetValue()) return true; + } + + return false; +} + +bool ExportConfigsDialog::preset_is_not_compatible_bbl_printer(Preset *preset) +{ + if (preset->type != Preset::Type::TYPE_PRINT && preset->type != Preset::Type::TYPE_FILAMENT) return true; + PresetBundle * preset_bundle = wxGetApp().preset_bundle; + vector printers; + get_filament_compatible_printer(preset, printers); + if (printers.empty()) return true; + Preset *printer_preset = preset_bundle->printers.find_preset(printers[0], false); + if (!printer_preset) return true; + if (!printer_preset->is_bbl_vendor_preset(preset_bundle)) return true; + return false; +} + +bool ExportConfigsDialog::earse_preset_fields_for_safe(Preset *preset) +{ + if (preset->type != Preset::Type::TYPE_PRINTER) return true; + + boost::filesystem::path file_path(data_dir() + "/" + PRESET_USER_DIR + "/" + "Temp" + "/" + (preset->name + ".json")); + preset->file = file_path.make_preferred().string(); + + DynamicPrintConfig &config = preset->config; + config.erase("print_host"); + config.erase("print_host_webui"); + config.erase("printhost_apikey"); + config.erase("printhost_cafile"); + config.erase("printhost_user"); + config.erase("printhost_password"); + config.erase("printhost_port"); + + preset->save(nullptr); + return true; +} + +std::string ExportConfigsDialog::initial_file_path(const wxString &path, const std::string &sub_file_path) +{ + std::string export_path = into_u8(path); + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "initial file path and path is:" << export_path << " and sub path is: " << sub_file_path; + boost::filesystem::path printer_export_path = (boost::filesystem::path(export_path) / sub_file_path).make_preferred(); + if (boost::filesystem::exists(printer_export_path)) { + MessageDialog dlg(this, wxString::Format(_L("The '%s' folder already exists in the current directory. Do you want to clear it and rebuild it.\nIf not, a time suffix will be " + "added, and you can modify the name after creation."), sub_file_path), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxYES_NO | wxYES_DEFAULT | wxCENTRE); + int res = dlg.ShowModal(); + if (wxID_YES == res) { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "Same path exists, delete and need to rebuild, and path is: " << printer_export_path.string(); + try { + boost::filesystem::remove_all(printer_export_path); + } catch (...) { + MessageDialog dlg(this, _L(wxString::Format("The file: %s \nin the directory may have been opened by another program. \nPlease close it and try again.", + encode_path(printer_export_path.string().c_str()))), + wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), + wxYES | wxYES_DEFAULT | wxCENTRE); + dlg.ShowModal(); + return "initial_failed"; + } + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "delete path"; + boost::filesystem::create_directories(printer_export_path); + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "create path"; + export_path = printer_export_path.string(); + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "Same path exists, delete and rebuild, and path is: " << export_path; + } else if (wxID_NO == res) { + export_path = printer_export_path.string(); + std::string export_path_with_time; + boost::filesystem::path *printer_export_path_with_time = nullptr; + do { + if (printer_export_path_with_time) { + delete printer_export_path_with_time; + printer_export_path_with_time = nullptr; + } + export_path_with_time = export_path + " " + get_curr_time(); + printer_export_path_with_time = new boost::filesystem::path(export_path_with_time); + } while (boost::filesystem::exists(*printer_export_path_with_time)); + export_path = export_path_with_time; + boost::filesystem::create_directories(*printer_export_path_with_time); + if (printer_export_path_with_time) { + delete printer_export_path_with_time; + printer_export_path_with_time = nullptr; + } + } else { + return ""; + } + } else { + boost::filesystem::create_directories(printer_export_path); + export_path = printer_export_path.string(); + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "Same path exists, delete and rebuild, and path is: " << export_path; + } + return export_path; +} + +std::string ExportConfigsDialog::initial_file_name(const wxString &path, const std::string file_name) +{ + std::string export_path = into_u8(path); + boost::filesystem::path printer_export_path = (boost::filesystem::path(export_path) / file_name).make_preferred(); + if (boost::filesystem::exists(printer_export_path)) { + MessageDialog dlg(this, wxString::Format(_L("The '%s' folder already exists in the current directory. Do you want to clear it and rebuild it.\nIf not, a time suffix will be " + "added, and you can modify the name after creation."), file_name), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxYES_NO | wxYES_DEFAULT | wxCENTRE); + int res = dlg.ShowModal(); + if (wxID_YES == res) { + try { + boost::filesystem::remove_all(printer_export_path); + } + catch(...) { + MessageDialog dlg(this, + _L(wxString::Format("The file: %s \nmay have been opened by another program. \nPlease close it and try again.", + encode_path(printer_export_path.string().c_str()))), + wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxYES | wxYES_DEFAULT | wxCENTRE); + dlg.ShowModal(); + return "initial_failed"; + } + export_path = printer_export_path.string(); + } else if (wxID_NO == res) { + export_path = printer_export_path.string(); + export_path = export_path.substr(0, export_path.find(".zip")); + std::string export_path_with_time; + boost::filesystem::path *printer_export_path_with_time = nullptr; + do { + if (printer_export_path_with_time) { + delete printer_export_path_with_time; + printer_export_path_with_time = nullptr; + } + export_path_with_time = export_path + " " + get_curr_time() + ".zip"; + printer_export_path_with_time = new boost::filesystem::path(export_path_with_time); + } while (boost::filesystem::exists(*printer_export_path_with_time)); + export_path = export_path_with_time; + if (printer_export_path_with_time) { + delete printer_export_path_with_time; + printer_export_path_with_time = nullptr; + } + } else { + return ""; + } + } else { + export_path = printer_export_path.string(); + } + return export_path; +} + +wxBoxSizer *ExportConfigsDialog::create_export_config_item(wxWindow *parent) +{ + wxBoxSizer *horizontal_sizer = new wxBoxSizer(wxHORIZONTAL); + + wxBoxSizer * optionSizer = new wxBoxSizer(wxVERTICAL); + wxStaticText *static_serial_text = new wxStaticText(parent, wxID_ANY, _L("Presets"), wxDefaultPosition, wxDefaultSize); + optionSizer->Add(static_serial_text, 0, wxEXPAND | wxALL, 0); + optionSizer->SetMinSize(OPTION_SIZE); + horizontal_sizer->Add(optionSizer, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(10)); + + wxBoxSizer *radioBoxSizer = new wxBoxSizer(wxVERTICAL); + + radioBoxSizer->Add(create_radio_item(m_exprot_type.preset_bundle, parent, wxEmptyString, m_export_type_btns), 0, wxEXPAND | wxALL, 0); + radioBoxSizer->Add(0, 0, 0, wxTOP, FromDIP(6)); + wxStaticText *static_export_printer_preset_bundle_text = new wxStaticText(parent, wxID_ANY, _L("Printer and all the filament&process presets that belong to the printer. \nCan be shared with others."), wxDefaultPosition, wxDefaultSize); + static_export_printer_preset_bundle_text->SetFont(Label::Body_12); + static_export_printer_preset_bundle_text->SetForegroundColour(wxColour("#6B6B6B")); + radioBoxSizer->Add(static_export_printer_preset_bundle_text, 0, wxEXPAND | wxLEFT, FromDIP(22)); + radioBoxSizer->Add(create_radio_item(m_exprot_type.filament_bundle, parent, wxEmptyString, m_export_type_btns), 0, wxEXPAND | wxTOP, FromDIP(10)); + wxStaticText *static_export_filament_preset_bundle_text = new wxStaticText(parent, wxID_ANY, _L("User's fillment preset set. \nCan be shared with others."), wxDefaultPosition, wxDefaultSize); + static_export_filament_preset_bundle_text->SetFont(Label::Body_12); + static_export_filament_preset_bundle_text->SetForegroundColour(wxColour("#6B6B6B")); + radioBoxSizer->Add(static_export_filament_preset_bundle_text, 0, wxEXPAND | wxLEFT, FromDIP(22)); + radioBoxSizer->Add(create_radio_item(m_exprot_type.printer_preset, parent, wxEmptyString, m_export_type_btns), 0, wxEXPAND | wxTOP, FromDIP(10)); + radioBoxSizer->Add(create_radio_item(m_exprot_type.filament_preset, parent, wxEmptyString, m_export_type_btns), 0, wxEXPAND | wxTOP, FromDIP(10)); + radioBoxSizer->Add(create_radio_item(m_exprot_type.process_preset, parent, wxEmptyString, m_export_type_btns), 0, wxEXPAND | wxTOP, FromDIP(10)); + horizontal_sizer->Add(radioBoxSizer, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(10)); + + return horizontal_sizer; +} + +wxBoxSizer *ExportConfigsDialog::create_radio_item(wxString title, wxWindow *parent, wxString tooltip, std::vector> &radiobox_list) +{ + wxBoxSizer *horizontal_sizer = new wxBoxSizer(wxHORIZONTAL); + RadioBox * radiobox = new RadioBox(parent); + horizontal_sizer->Add(radiobox, 0, wxEXPAND | wxALL, 0); + horizontal_sizer->Add(0, 0, 0, wxEXPAND | wxLEFT, FromDIP(5)); + radiobox_list.push_back(std::make_pair(radiobox, title)); + int btn_idx = radiobox_list.size() - 1; + radiobox->Bind(wxEVT_LEFT_DOWN, [this, &radiobox_list, btn_idx](wxMouseEvent &e) { + select_curr_radiobox(radiobox_list, btn_idx); + }); + + wxStaticText *text = new wxStaticText(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize); + text->Bind(wxEVT_LEFT_DOWN, [this, &radiobox_list, btn_idx](wxMouseEvent &e) { + select_curr_radiobox(radiobox_list, btn_idx); + }); + horizontal_sizer->Add(text, 0, wxEXPAND | wxLEFT, 0); + + radiobox->SetToolTip(tooltip); + text->SetToolTip(tooltip); + return horizontal_sizer; +} + +mz_bool ExportConfigsDialog::initial_zip_archive(mz_zip_archive &zip_archive, const std::string &file_path) +{ + mz_zip_zero_struct(&zip_archive); + mz_bool status; + + // Initialize the ZIP file to write to the structure, using memory storage + + std::string export_dir = encode_path(file_path.c_str()); + status = mz_zip_writer_init_file(&zip_archive, export_dir.c_str(), 0); + return status; +} + +ExportConfigsDialog::ExportCase ExportConfigsDialog::save_zip_archive_to_file(mz_zip_archive &zip_archive) +{ + // Complete writing of ZIP file + mz_bool status = mz_zip_writer_finalize_archive(&zip_archive); + if (MZ_FALSE == status) { + BOOST_LOG_TRIVIAL(info) << "Failed to finalize ZIP archive"; + mz_zip_writer_end(&zip_archive); + return ExportCase::FINALIZE_FAIL; + } + + // Release ZIP file to write structure and related resources + mz_zip_writer_end(&zip_archive); + + return ExportCase::CASE_COUNT; +} + +ExportConfigsDialog::ExportCase ExportConfigsDialog::save_presets_to_zip(const std::string &export_file, const std::vector> &config_paths) +{ + mz_zip_archive zip_archive; + mz_bool status = initial_zip_archive(zip_archive, export_file); + + if (MZ_FALSE == status) { + BOOST_LOG_TRIVIAL(info) << "Failed to initialize ZIP archive"; + return ExportCase::INITIALIZE_FAIL; + } + + for (std::pair config_path : config_paths) { + std::string preset_name = config_path.first; + + // Add a file to the ZIP file + status = mz_zip_writer_add_file(&zip_archive, (preset_name).c_str(), encode_path(config_path.second.c_str()).c_str(), NULL, 0, MZ_DEFAULT_COMPRESSION); + // status = mz_zip_writer_add_mem(&zip_archive, ("printer/" + printer_preset->name + ".json").c_str(), json_contents, strlen(json_contents), MZ_DEFAULT_COMPRESSION); + if (MZ_FALSE == status) { + BOOST_LOG_TRIVIAL(info) << preset_name << " Filament preset failed to add file to ZIP archive"; + mz_zip_writer_end(&zip_archive); + return ExportCase::ADD_FILE_FAIL; + } + BOOST_LOG_TRIVIAL(info) << "Printer preset json add successful: " << preset_name; + } + return save_zip_archive_to_file(zip_archive); +} + +void ExportConfigsDialog::select_curr_radiobox(std::vector> &radiobox_list, int btn_idx) +{ + int len = radiobox_list.size(); + for (int i = 0; i < len; ++i) { + if (i == btn_idx) { + radiobox_list[i].first->SetValue(true); + const wxString &export_type = radiobox_list[i].second; + m_preset_sizer->Clear(true); + m_printer_name.clear(); + m_preset.clear(); + PresetBundle *preset_bundle = wxGetApp().preset_bundle; + this->Freeze(); + if (export_type == m_exprot_type.preset_bundle) { + for (std::pair preset : m_printer_presets) { + std::string preset_name = preset.first; + //printer preset mast have user's filament or process preset or printer preset is user preset + if (m_filament_presets.find(preset_name) == m_filament_presets.end() && m_process_presets.find(preset_name) == m_process_presets.end() && preset.second->is_system) continue; + wxString printer_name = wxString::FromUTF8(preset_name); + m_preset_sizer->Add(create_checkbox(m_presets_window, preset.second, printer_name, m_preset), 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, FromDIP(5)); + } + m_serial_text->SetLabel(_L("Only display printer names with changes to printer, filament, and process presets.")); + }else if (export_type == m_exprot_type.filament_bundle) { + for (std::pair>> filament_name_to_preset : m_filament_name_to_presets) { + if (filament_name_to_preset.second.empty()) continue; + bool all_preset_is_compatible_third_printer = true; + for (std::pair filament_preset : filament_name_to_preset.second) { + if (!preset_is_not_compatible_bbl_printer(filament_preset.second)) + all_preset_is_compatible_third_printer = false; + } + if (all_preset_is_compatible_third_printer) continue; + wxString filament_name = wxString::FromUTF8(filament_name_to_preset.first); + m_preset_sizer->Add(create_checkbox(m_presets_window, filament_name, m_printer_name), 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, FromDIP(5)); + } + m_serial_text->SetLabel(_L("Only display the filament names with changes to filament presets.")); + } else if (export_type == m_exprot_type.printer_preset) { + for (std::pair preset : m_printer_presets) { + if (preset.second->is_system) continue; + wxString printer_name = wxString::FromUTF8(preset.first); + m_preset_sizer->Add(create_checkbox(m_presets_window, preset.second, printer_name, m_preset), 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, + FromDIP(5)); + } + m_serial_text->SetLabel(_L("Only printer names with user printer presets will be displayed, and each preset you choose will be exported as a zip.")); + } else if (export_type == m_exprot_type.filament_preset) { + for (std::pair>> filament_name_to_preset : m_filament_name_to_presets) { + if (filament_name_to_preset.second.empty()) continue; + bool all_preset_is_compatible_third_printer = true; + for (std::pair filament_preset : filament_name_to_preset.second) { + if (!preset_is_not_compatible_bbl_printer(filament_preset.second)) + all_preset_is_compatible_third_printer = false; + } + if (all_preset_is_compatible_third_printer) continue; + wxString filament_name = wxString::FromUTF8(filament_name_to_preset.first); + m_preset_sizer->Add(create_checkbox(m_presets_window, filament_name, m_printer_name), 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, FromDIP(5)); + } + m_serial_text->SetLabel(_L("Only the filament names with user filament presets will be displayed, \nand all user filament presets in each filament name you select will be exported as a zip.")); + } else if (export_type == m_exprot_type.process_preset) { + for (std::pair> presets : m_process_presets) { + Preset * printer_preset = preset_bundle->printers.find_preset(presets.first, false); + if (!printer_preset) continue; + if (!printer_preset->is_system) continue; + if (preset_bundle->printers.get_preset_base(*printer_preset) != printer_preset) continue; + for (Preset *preset : presets.second) { + if (!preset->is_system) { + wxString printer_name = wxString::FromUTF8(presets.first); + m_preset_sizer->Add(create_checkbox(m_presets_window, printer_name, m_printer_name), 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, FromDIP(5)); + break; + } + } + + } + m_serial_text->SetLabel(_L("Only printer names with changed process presets will be displayed, \nand all user process presets in each printer name you select will be exported as a zip.")); + } + //m_presets_window->SetSizerAndFit(m_preset_sizer); + m_presets_window->Layout(); + m_presets_window->Fit(); + int width = m_presets_window->GetSize().GetWidth(); + int height = m_presets_window->GetSize().GetHeight(); + m_scrolled_preset_window->SetMinSize(wxSize(std::min(1200, width), std::min(600, height))); + m_scrolled_preset_window->SetMaxSize(wxSize(std::min(1200, width), std::min(600, height))); + m_scrolled_preset_window->SetSize(wxSize(std::min(1200, width), std::min(600, height))); + this->SetSizerAndFit(m_main_sizer); + Layout(); + Fit(); + Refresh(); + adjust_dialog_in_screen(this); + this->Thaw(); + } else { + radiobox_list[i].first->SetValue(false); + } + } +} + +ExportConfigsDialog::ExportCase ExportConfigsDialog::archive_preset_bundle_to_file(const wxString &path) +{ + std::string export_path = initial_file_path(path, "Printer config bundle"); + if (export_path.empty() || "initial_failed" == export_path) return ExportCase::EXPORT_CANCEL; + BOOST_LOG_TRIVIAL(info) << "Export printer preset bundle"; + + for (std::pair<::CheckBox *, Preset *> checkbox_preset : m_preset) { + if (checkbox_preset.first->GetValue()) { + Preset *printer_preset = checkbox_preset.second; + std::string printer_preset_name_ = printer_preset->name; + + json bundle_structure; + NetworkAgent *agent = wxGetApp().getAgent(); + std::string clock = get_curr_timestmp(); + if (agent) { + bundle_structure["version"] = agent->get_version(); + bundle_structure["bundle_id"] = agent->get_user_id() + "_" + printer_preset_name_ + "_" + clock; + } else { + bundle_structure["version"] = ""; + bundle_structure["bundle_id"] = "offline_" + printer_preset_name_ + "_" + clock; + } + bundle_structure["bundle_type"] = "printer config bundle"; + bundle_structure["printer_preset_name"] = printer_preset_name_; + json printer_config = json::array(); + json filament_configs = json::array(); + json process_configs = json::array(); + + mz_zip_archive zip_archive; + mz_bool status = initial_zip_archive(zip_archive, export_path + "/" + printer_preset->name + ".bbscfg"); + if (MZ_FALSE == status) { + BOOST_LOG_TRIVIAL(info) << "Failed to initialize ZIP archive"; + return ExportCase::INITIALIZE_FAIL; + } + + boost::filesystem::path pronter_file_path = boost::filesystem::path(printer_preset->file); + std::string preset_path = pronter_file_path.make_preferred().string(); + if (preset_path.empty()) { + BOOST_LOG_TRIVIAL(info) << "Export printer preset: " << printer_preset->name << " skip because of the preset file path is empty."; + continue; + } + + // Add a file to the ZIP file + std::string printer_config_file_name = "printer/" + pronter_file_path.filename().string(); + status = mz_zip_writer_add_file(&zip_archive, printer_config_file_name.c_str(), encode_path(preset_path.c_str()).c_str(), NULL, 0, MZ_DEFAULT_COMPRESSION); + //status = mz_zip_writer_add_mem(&zip_archive, ("printer/" + printer_preset->name + ".json").c_str(), json_contents, strlen(json_contents), MZ_DEFAULT_COMPRESSION); + if (MZ_FALSE == status) { + BOOST_LOG_TRIVIAL(info) << printer_preset->name << " Failed to add file to ZIP archive"; + mz_zip_writer_end(&zip_archive); + return ExportCase::ADD_FILE_FAIL; + } + printer_config.push_back(printer_config_file_name); + BOOST_LOG_TRIVIAL(info) << "Printer preset json add successful: " << printer_preset->name; + + const std::string printer_preset_name = printer_preset->name; + std::unordered_map>::iterator iter = m_filament_presets.find(printer_preset_name); + if (m_filament_presets.end() != iter) { + for (Preset *preset : iter->second) { + boost::filesystem::path filament_file_path = boost::filesystem::path(preset->file); + std::string filament_preset_path = filament_file_path.make_preferred().string(); + if (filament_preset_path.empty()) { + BOOST_LOG_TRIVIAL(info) << "Export filament preset: " << preset->name << " skip because of the preset file path is empty."; + continue; + } + + std::string filament_config_file_name = "filament/" + filament_file_path.filename().string(); + status = mz_zip_writer_add_file(&zip_archive, filament_config_file_name.c_str(), encode_path(filament_preset_path.c_str()).c_str(), NULL, 0, MZ_DEFAULT_COMPRESSION); + if (MZ_FALSE == status) { + BOOST_LOG_TRIVIAL(info) << preset->name << " Failed to add file to ZIP archive"; + mz_zip_writer_end(&zip_archive); + return ExportCase::ADD_FILE_FAIL; + } + filament_configs.push_back(filament_config_file_name); + BOOST_LOG_TRIVIAL(info) << "Filament preset json add successful."; + } + } + + iter = m_process_presets.find(printer_preset_name); + if (m_process_presets.end() != iter) { + for (Preset *preset : iter->second) { + boost::filesystem::path process_file_path = boost::filesystem::path(preset->file); + std::string process_preset_path = process_file_path.make_preferred().string(); + if (process_preset_path.empty()) { + BOOST_LOG_TRIVIAL(info) << "Export process preset: " << preset->name << " skip because of the preset file path is empty."; + continue; + } + + std::string process_config_file_name = "process/" + process_file_path.filename().string(); + status = mz_zip_writer_add_file(&zip_archive, process_config_file_name.c_str(), encode_path(process_preset_path.c_str()).c_str(), NULL, 0, MZ_DEFAULT_COMPRESSION); + if (MZ_FALSE == status) { + BOOST_LOG_TRIVIAL(info) << preset->name << " Failed to add file to ZIP archive"; + mz_zip_writer_end(&zip_archive); + return ExportCase::ADD_FILE_FAIL; + } + process_configs.push_back(process_config_file_name); + BOOST_LOG_TRIVIAL(info) << "Process preset json add successful: "; + } + } + + bundle_structure["printer_config"] = printer_config; + bundle_structure["filament_config"] = filament_configs; + bundle_structure["process_config"] = process_configs; + + std::string bundle_structure_str = bundle_structure.dump(); + status = mz_zip_writer_add_mem(&zip_archive, BUNDLE_STRUCTURE_JSON_NAME, bundle_structure_str.data(), bundle_structure_str.size(), MZ_DEFAULT_COMPRESSION); + if (MZ_FALSE == status) { + BOOST_LOG_TRIVIAL(info) << " Failed to add file: " << BUNDLE_STRUCTURE_JSON_NAME; + mz_zip_writer_end(&zip_archive); + return ExportCase::ADD_BUNDLE_STRUCTURE_FAIL; + } + BOOST_LOG_TRIVIAL(info) << " Success to add file: " << BUNDLE_STRUCTURE_JSON_NAME; + + ExportCase save_result = save_zip_archive_to_file(zip_archive); + if (ExportCase::CASE_COUNT != save_result) return save_result; + } + } + BOOST_LOG_TRIVIAL(info) << "ZIP archive created successfully"; + + return ExportCase::EXPORT_SUCCESS; +} + +ExportConfigsDialog::ExportCase ExportConfigsDialog::archive_filament_bundle_to_file(const wxString &path) +{ + std::string export_path = initial_file_path(path, "Filament bundle"); + if (export_path.empty() || "initial_failed" == export_path) return ExportCase::EXPORT_CANCEL; + BOOST_LOG_TRIVIAL(info) << "Export filament preset bundle"; + + for (std::pair<::CheckBox *, std::string> checkbox_filament_name : m_printer_name) { + if (checkbox_filament_name.first->GetValue()) { + std::string filament_name = checkbox_filament_name.second; + + json bundle_structure; + NetworkAgent *agent = wxGetApp().getAgent(); + std::string clock = get_curr_timestmp(); + if (agent) { + bundle_structure["version"] = agent->get_version(); + bundle_structure["bundle_id"] = agent->get_user_id() + "_" + filament_name + "_" + clock; + } else { + bundle_structure["version"] = ""; + bundle_structure["bundle_id"] = "offline_" + filament_name + "_" + clock; + } + bundle_structure["bundle_type"] = "filament config bundle"; + bundle_structure["filament_name"] = filament_name; + std::unordered_map vendor_structure; + + mz_zip_archive zip_archive; + mz_bool status = initial_zip_archive(zip_archive, export_path + "/" + filament_name + ".bbsflmt"); + if (MZ_FALSE == status) { + BOOST_LOG_TRIVIAL(info) << "Failed to initialize ZIP archive"; + return ExportCase::INITIALIZE_FAIL; + } + + std::unordered_map>>::iterator iter = m_filament_name_to_presets.find(filament_name); + if (m_filament_name_to_presets.end() == iter) { + BOOST_LOG_TRIVIAL(info) << "Filament name do not find, filament name:" << filament_name; + continue; + } + std::set> vendor_to_filament_name; + for (std::pair printer_name_to_preset : iter->second) { + std::string printer_vendor = printer_name_to_preset.first; + if (printer_vendor.empty()) continue; + Preset * filament_preset = printer_name_to_preset.second; + if (preset_is_not_compatible_bbl_printer(filament_preset)) continue; + if (vendor_to_filament_name.find(std::make_pair(printer_vendor, filament_preset->name)) != vendor_to_filament_name.end()) continue; + vendor_to_filament_name.insert(std::make_pair(printer_vendor, filament_preset->name)); + std::string preset_path = boost::filesystem::path(filament_preset->file).make_preferred().string(); + if (preset_path.empty()) { + BOOST_LOG_TRIVIAL(info) << "Export printer preset: " << filament_preset->name << " skip because of the preset file path is empty."; + continue; + } + // Add a file to the ZIP file + std::string file_name = printer_vendor + "/" + filament_preset->name + ".json"; + status = mz_zip_writer_add_file(&zip_archive, file_name.c_str(), encode_path(preset_path.c_str()).c_str(), NULL, 0, MZ_DEFAULT_COMPRESSION); + // status = mz_zip_writer_add_mem(&zip_archive, ("printer/" + printer_preset->name + ".json").c_str(), json_contents, strlen(json_contents), MZ_DEFAULT_COMPRESSION); + if (MZ_FALSE == status) { + BOOST_LOG_TRIVIAL(info) << filament_preset->name << " Failed to add file to ZIP archive"; + mz_zip_writer_end(&zip_archive); + return ExportCase::ADD_FILE_FAIL; + } + std::unordered_map::iterator iter = vendor_structure.find(printer_vendor); + if (vendor_structure.end() == iter) { + json j = json::array(); + j.push_back(file_name); + vendor_structure[printer_vendor] = j; + } else { + iter->second.push_back(file_name); + } + BOOST_LOG_TRIVIAL(info) << "Filament preset json add successful: " << filament_preset->name; + } + + for (const std::pair& vendor_name_to_json : vendor_structure) { + json j; + std::string printer_vendor = vendor_name_to_json.first; + j["vendor"] = printer_vendor; + j["filament_path"] = vendor_name_to_json.second; + bundle_structure["printer_vendor"].push_back(j); + } + + std::string bundle_structure_str = bundle_structure.dump(); + status = mz_zip_writer_add_mem(&zip_archive, BUNDLE_STRUCTURE_JSON_NAME, bundle_structure_str.data(), bundle_structure_str.size(), MZ_DEFAULT_COMPRESSION); + if (MZ_FALSE == status) { + BOOST_LOG_TRIVIAL(info) << " Failed to add file: " << BUNDLE_STRUCTURE_JSON_NAME; + mz_zip_writer_end(&zip_archive); + return ExportCase::ADD_BUNDLE_STRUCTURE_FAIL; + } + BOOST_LOG_TRIVIAL(info) << " Success to add file: " << BUNDLE_STRUCTURE_JSON_NAME; + + // Complete writing of ZIP file + ExportCase save_result = save_zip_archive_to_file(zip_archive); + if (ExportCase::CASE_COUNT != save_result) return save_result; + } + } + BOOST_LOG_TRIVIAL(info) << "ZIP archive created successfully"; + + return ExportCase::EXPORT_SUCCESS; +} + +ExportConfigsDialog::ExportCase ExportConfigsDialog::archive_printer_preset_to_file(const wxString &path) +{ + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "start exprot printer presets"; + std::string export_file = "Printer presets.zip"; + export_file = initial_file_name(path, export_file); + if (export_file.empty() || "initial_failed" == export_file) return ExportCase::EXPORT_CANCEL; + + std::vector> config_paths; + + for (std::pair<::CheckBox *, Preset *> checkbox_preset : m_preset) { + if (checkbox_preset.first->GetValue()) { + Preset * printer_preset = checkbox_preset.second; + std::string preset_path = boost::filesystem::path(printer_preset->file).make_preferred().string(); + if (preset_path.empty()) { + BOOST_LOG_TRIVIAL(info) << "Export printer preset: " << printer_preset->name << " skip because of the preset file path is empty."; + continue; + } + std::string preset_name = printer_preset->name + ".json"; + config_paths.push_back(std::make_pair(preset_name, preset_path)); + } + } + + ExportCase save_result = save_presets_to_zip(export_file, config_paths); + if (ExportCase::CASE_COUNT != save_result) return save_result; + + BOOST_LOG_TRIVIAL(info) << "ZIP archive created successfully"; + + return ExportCase::EXPORT_SUCCESS; + +} + +ExportConfigsDialog::ExportCase ExportConfigsDialog::archive_filament_preset_to_file(const wxString &path) +{ + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "start exprot filament presets"; + std::string export_file = "Filament presets.zip"; + export_file = initial_file_name(path, export_file); + if (export_file.empty() || "initial_failed" == export_file) return ExportCase::EXPORT_CANCEL; + + std::vector> config_paths; + + std::set filament_presets; + for (std::pair<::CheckBox *, std::string> checkbox_preset : m_printer_name) { + if (checkbox_preset.first->GetValue()) { + std::string filament_name = checkbox_preset.second; + + std::unordered_map>>::iterator iter = m_filament_name_to_presets.find(filament_name); + if (m_filament_name_to_presets.end() == iter) { + BOOST_LOG_TRIVIAL(info) << "Filament name do not find, filament name:" << filament_name; + continue; + } + for (std::pair printer_name_preset : iter->second) { + Preset * filament_preset = printer_name_preset.second; + if (preset_is_not_compatible_bbl_printer(filament_preset)) continue; + if (filament_presets.find(filament_preset->name) != filament_presets.end()) continue; + filament_presets.insert(filament_preset->name); + std::string preset_path = boost::filesystem::path(filament_preset->file).make_preferred().string(); + if (preset_path.empty()) { + BOOST_LOG_TRIVIAL(info) << "Export filament preset: " << filament_preset->name << " skip because of the filament file path is empty."; + continue; + } + + std::string preset_name = filament_preset->name + ".json"; + config_paths.push_back(std::make_pair(preset_name, preset_path)); + } + } + } + + ExportCase save_result = save_presets_to_zip(export_file, config_paths); + if (ExportCase::CASE_COUNT != save_result) return save_result; + + BOOST_LOG_TRIVIAL(info) << "ZIP archive created successfully"; + + return ExportCase::EXPORT_SUCCESS; +} + +ExportConfigsDialog::ExportCase ExportConfigsDialog::archive_process_preset_to_file(const wxString &path) +{ + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "start exprot process presets"; + std::string export_file = "Process presets.zip"; + export_file = initial_file_name(path, export_file); + if (export_file.empty() || "initial_failed" == export_file) return ExportCase::EXPORT_CANCEL; + + std::vector> config_paths; + + std::set process_presets; + for (std::pair<::CheckBox *, std::string> checkbox_preset : m_printer_name) { + if (checkbox_preset.first->GetValue()) { + std::string printer_name = checkbox_preset.second; + std::unordered_map>::iterator iter = m_process_presets.find(printer_name); + if (m_process_presets.end() != iter) { + for (Preset *process_preset : iter->second) { + if (preset_is_not_compatible_bbl_printer(process_preset)) continue; + if (process_presets.find(process_preset->name) != process_presets.end()) continue; + process_presets.insert(process_preset->name); + std::string preset_path = boost::filesystem::path(process_preset->file).make_preferred().string(); + if (preset_path.empty()) { + BOOST_LOG_TRIVIAL(info) << "Export process preset: " << process_preset->name << " skip because of the preset file path is empty."; + continue; + } + + std::string preset_name = process_preset->name + ".json"; + config_paths.push_back(std::make_pair(preset_name, preset_path)); + } + } + } + } + + ExportCase save_result = save_presets_to_zip(export_file, config_paths); + if (ExportCase::CASE_COUNT != save_result) return save_result; + + BOOST_LOG_TRIVIAL(info) << "ZIP archive created successfully"; + + return ExportCase::EXPORT_SUCCESS; +} + +wxBoxSizer *ExportConfigsDialog::create_button_item(wxWindow* parent) +{ + wxBoxSizer *bSizer_button = new wxBoxSizer(wxHORIZONTAL); + bSizer_button->Add(0, 0, 1, wxEXPAND, 0); + + StateColor btn_bg_green(std::pair(wxColour(27, 136, 68), StateColor::Pressed), std::pair(wxColour(61, 203, 115), StateColor::Hovered), + std::pair(wxColour(0, 174, 66), StateColor::Normal)); + + m_button_ok = new Button(this, _L("OK")); + m_button_ok->SetBackgroundColor(btn_bg_green); + m_button_ok->SetBorderColor(*wxWHITE); + m_button_ok->SetTextColor(wxColour(0xFFFFFE)); + m_button_ok->SetFont(Label::Body_12); + m_button_ok->SetSize(wxSize(FromDIP(58), FromDIP(24))); + m_button_ok->SetMinSize(wxSize(FromDIP(58), FromDIP(24))); + m_button_ok->SetCornerRadius(FromDIP(12)); + bSizer_button->Add(m_button_ok, 0, wxRIGHT, FromDIP(10)); + + m_button_ok->Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent &e) { + if (!has_check_box_selected()) { + MessageDialog dlg(this, _L("Please select at least one printer or filament."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), + wxYES | wxYES_DEFAULT | wxCENTRE); + dlg.ShowModal(); + return; + } + + wxDirDialog dlg(this, _L("Choose a directory"), from_u8(wxGetApp().app_config->get_last_dir()), wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST); + wxString path; + if (dlg.ShowModal() == wxID_OK) path = dlg.GetPath(); + ExportCase export_case = ExportCase::EXPORT_CANCEL; + if (!path.IsEmpty()) { + wxGetApp().app_config->update_config_dir(into_u8(path)); + wxGetApp().app_config->save(); + const wxString curr_radio_type = get_curr_radio_type(m_export_type_btns); + + if (curr_radio_type == m_exprot_type.preset_bundle) { + export_case = archive_preset_bundle_to_file(path); + } else if (curr_radio_type == m_exprot_type.filament_bundle) { + export_case = archive_filament_bundle_to_file(path); + } else if (curr_radio_type == m_exprot_type.printer_preset) { + export_case = archive_printer_preset_to_file(path); + } else if (curr_radio_type == m_exprot_type.filament_preset) { + export_case = archive_filament_preset_to_file(path); + } else if (curr_radio_type == m_exprot_type.process_preset) { + export_case = archive_process_preset_to_file(path); + } + } else { + return; + } + show_export_result(export_case); + if (ExportCase::EXPORT_SUCCESS != export_case) return; + + EndModal(wxID_OK); + }); + + StateColor btn_bg_white(std::pair(wxColour(206, 206, 206), StateColor::Pressed), std::pair(wxColour(238, 238, 238), StateColor::Hovered), + std::pair(*wxWHITE, StateColor::Normal)); + + m_button_cancel = new Button(this, _L("Cancel")); + m_button_cancel->SetBackgroundColor(btn_bg_white); + m_button_cancel->SetBorderColor(wxColour(38, 46, 48)); + m_button_cancel->SetFont(Label::Body_12); + m_button_cancel->SetSize(wxSize(FromDIP(58), FromDIP(24))); + m_button_cancel->SetMinSize(wxSize(FromDIP(58), FromDIP(24))); + m_button_cancel->SetCornerRadius(FromDIP(12)); + bSizer_button->Add(m_button_cancel, 0, wxRIGHT, FromDIP(10)); + + m_button_cancel->Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent &e) { EndModal(wxID_CANCEL); }); + + return bSizer_button; +} + +wxBoxSizer *ExportConfigsDialog::create_select_printer(wxWindow *parent) +{ + wxBoxSizer *horizontal_sizer = new wxBoxSizer(wxVERTICAL); + + wxBoxSizer * optionSizer = new wxBoxSizer(wxVERTICAL); + m_serial_text = new wxStaticText(parent, wxID_ANY, _L("Please select a type you want to export"), wxDefaultPosition, wxDefaultSize); + optionSizer->Add(m_serial_text, 0, wxEXPAND | wxALL, 0); + optionSizer->SetMinSize(OPTION_SIZE); + horizontal_sizer->Add(optionSizer, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(10)); + m_scrolled_preset_window = new wxScrolledWindow(parent); + m_scrolled_preset_window->SetScrollRate(5, 5); + m_scrolled_preset_window->SetBackgroundColour(PRINTER_LIST_COLOUR); + m_scrolled_preset_window->SetMaxSize(wxSize(FromDIP(660), FromDIP(400))); + m_scrolled_preset_window->SetSize(wxSize(FromDIP(660), FromDIP(400))); + wxBoxSizer *scrolled_window = new wxBoxSizer(wxHORIZONTAL); + + m_presets_window = new wxPanel(m_scrolled_preset_window, wxID_ANY); + m_presets_window->SetBackgroundColour(PRINTER_LIST_COLOUR); + wxBoxSizer *select_printer_sizer = new wxBoxSizer(wxVERTICAL); + + m_preset_sizer = new wxGridSizer(3, FromDIP(5), FromDIP(5)); + select_printer_sizer->Add(m_preset_sizer, 0, wxEXPAND, FromDIP(5)); + m_presets_window->SetSizer(select_printer_sizer); + scrolled_window->Add(m_presets_window, 0, wxEXPAND, 0); + m_scrolled_preset_window->SetSizerAndFit(scrolled_window); + + horizontal_sizer->Add(m_scrolled_preset_window, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(10)); + + return horizontal_sizer; +} + +void ExportConfigsDialog::data_init() +{ + // Delete the Temp folder + boost::filesystem::path folder(data_dir() + "/" + PRESET_USER_DIR + "/" + "Temp"); + if (boost::filesystem::exists(folder)) boost::filesystem::remove_all(folder); + + boost::system::error_code ec; + boost::filesystem::path user_folder(data_dir() + "/" + PRESET_USER_DIR); + bool temp_folder_exist = true; + if (!boost::filesystem::exists(user_folder)) { + if (!boost::filesystem::create_directories(user_folder, ec)) { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " create directory failed: " << user_folder << " "< & printer_presets = preset_bundle.printers.get_presets(); + for (const Preset &printer_preset : printer_presets) { + + std::string preset_name = printer_preset.name; + if (!printer_preset.is_visible || printer_preset.is_default || printer_preset.is_project_embedded) continue; + if (preset_bundle.printers.select_preset_by_name(preset_name, false)) { + preset_bundle.update_compatible(PresetSelectCompatibleType::Always); + + const std::deque &filament_presets = preset_bundle.filaments.get_presets(); + for (const Preset &filament_preset : filament_presets) { + if (filament_preset.is_system || filament_preset.is_default || filament_preset.is_project_embedded) continue; + if (filament_preset.is_compatible) { + Preset *new_filament_preset = new Preset(filament_preset); + m_filament_presets[preset_name].push_back(new_filament_preset); + } + } + + const std::deque &process_presets = preset_bundle.prints.get_presets(); + for (const Preset &process_preset : process_presets) { + if (process_preset.is_system || process_preset.is_default || process_preset.is_project_embedded) continue; + if (process_preset.is_compatible) { + Preset *new_prpcess_preset = new Preset(process_preset); + m_process_presets[preset_name].push_back(new_prpcess_preset); + } + } + + Preset *new_printer_preset = new Preset(printer_preset); + earse_preset_fields_for_safe(new_printer_preset); + m_printer_presets[preset_name] = new_printer_preset; + } + } + const std::deque &filament_presets = preset_bundle.filaments.get_presets(); + for (const Preset &filament_preset : filament_presets) { + if (filament_preset.is_system || filament_preset.is_default) continue; + Preset *new_filament_preset = new Preset(filament_preset); + const Preset *base_filament_preset = preset_bundle.filaments.get_preset_base(*new_filament_preset); + + std::string filament_preset_name = base_filament_preset->name; + std::string machine_name = get_machine_name(filament_preset_name); + m_filament_name_to_presets[get_filament_name(filament_preset_name)].push_back(std::make_pair(get_vendor_name(machine_name), new_filament_preset)); + } +} + +EditFilamentPresetDialog::EditFilamentPresetDialog(wxWindow *parent, FilamentInfomation *filament_info) + : DPIDialog(parent ? parent : nullptr, wxID_ANY, _L("Edit Filament"), wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX) + , m_filament_id("") + , m_filament_name("") + , m_vendor_name("") + , m_filament_type("") + , m_filament_serial("") +{ + m_preset_tree_creater = new PresetTree(this); + + this->SetBackgroundColour(*wxWHITE); + this->SetMinSize(wxSize(FromDIP(600), -1)); + + std::string icon_path = (boost::format("%1%/images/BambuStudioTitle.ico") % resources_dir()).str(); + SetIcon(wxIcon(encode_path(icon_path.c_str()), wxBITMAP_TYPE_ICO)); + + m_main_sizer = new wxBoxSizer(wxVERTICAL); + // top line + auto m_line_top = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(-1, 1), wxTAB_TRAVERSAL); + m_line_top->SetBackgroundColour(wxColour(0xA6, 0xa9, 0xAA)); + m_main_sizer->Add(m_line_top, 0, wxEXPAND, 0); + m_main_sizer->Add(0, 0, 0, wxTOP, FromDIP(5)); + + wxStaticText* basic_infomation = new wxStaticText(this, wxID_ANY, _L("Basic Information")); + basic_infomation->SetFont(Label::Head_16); + + m_main_sizer->Add(basic_infomation, 0, wxALL, FromDIP(10)); + m_filament_id = filament_info->filament_id; + //std::string filament_name = filament_info->filament_name; + bool get_filament_presets = get_same_filament_id_presets(m_filament_id); + // get filament vendor, type, serial, and name + if (get_filament_presets && !m_printer_compatible_presets.empty()) { + std::shared_ptr preset; + for (std::pair>> pair : m_printer_compatible_presets) { + for (std::shared_ptr fialment_preset : pair.second) { + if (fialment_preset->inherits().empty()) { + preset = fialment_preset; + break; + } + } + } + if (!preset.get()) preset = m_printer_compatible_presets.begin()->second[0]; + m_filament_name = get_filament_name(preset->name); + auto vendor_names = dynamic_cast(preset->config.option("filament_vendor")); + if (vendor_names && !vendor_names->values.empty()) m_vendor_name = vendor_names->values[0]; + auto filament_types = dynamic_cast(preset->config.option("filament_type")); + if (filament_types && !filament_types->values.empty()) m_filament_type = filament_types->values[0]; + std::string filament_type = m_filament_type == "PLA-AERO" ? "PLA Aero" : m_filament_type; + size_t index = m_filament_name.find(filament_type); + if (std::string::npos != index && index + filament_type.size() < m_filament_name.size()) { + m_filament_serial = m_filament_name.substr(index + filament_type.size()); + if (m_filament_serial.size() > 2 && m_filament_serial[0] == ' ') { + m_filament_serial = m_filament_serial.substr(1); + } + } + } + + m_main_sizer->Add(create_filament_basic_info(), 0, wxEXPAND | wxALL, 0); + + // divider line + auto line_divider = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(-1, 1), wxTAB_TRAVERSAL); + line_divider->SetBackgroundColour(wxColour(0xA6, 0xa9, 0xAA)); + m_main_sizer->Add(line_divider, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(10)); + m_main_sizer->Add(0, 0, 0, wxTOP, FromDIP(5)); + + wxStaticText *presets_infomation = new wxStaticText(this, wxID_ANY, _L("Filament presets under this filament")); + presets_infomation->SetFont(Label::Head_16); + m_main_sizer->Add(presets_infomation, 0, wxLEFT | wxRIGHT, FromDIP(10)); + + m_main_sizer->Add(create_add_filament_btn(), 0, wxEXPAND | wxALL, 0); + m_main_sizer->Add(create_preset_tree_sizer(), 0, wxEXPAND | wxALL, 0); + m_note_text = new wxStaticText(this, wxID_ANY, _L("Note: If the only preset under this filament is deleted, the filament will be deleted after exiting the dialog.")); + m_main_sizer->Add(m_note_text, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM | wxALIGN_CENTER_VERTICAL, FromDIP(10)); + m_note_text->Hide(); + m_main_sizer->Add(create_button_sizer(), 0, wxEXPAND | wxALL, 0); + + update_preset_tree(); + + this->SetSizer(m_main_sizer); + this->Layout(); + this->Fit(); + wxGetApp().UpdateDlgDarkUI(this); +} +EditFilamentPresetDialog::~EditFilamentPresetDialog() {} + +void EditFilamentPresetDialog::on_dpi_changed(const wxRect &suggested_rect) { + /*m_add_filament_btn->Rescale(); + m_del_filament_btn->SetMinSize(wxSize(FromDIP(58), FromDIP(24))); + m_del_filament_btn->SetMaxSize(wxSize(FromDIP(58), FromDIP(24))); + m_del_filament_btn->SetCornerRadius(FromDIP(12)); + m_ok_btn->SetMinSize(wxSize(FromDIP(58), FromDIP(24))); + m_ok_btn->SetMaxSize(wxSize(FromDIP(58), FromDIP(24))); + m_ok_btn->SetCornerRadius(FromDIP(12));*/ + Layout(); +} + +bool EditFilamentPresetDialog::get_same_filament_id_presets(std::string filament_id) +{ + PresetBundle *preset_bundle = wxGetApp().preset_bundle; + const std::deque &filament_presets = preset_bundle->filaments.get_presets(); + + m_printer_compatible_presets.clear(); + for (Preset const &preset : filament_presets) { + if (preset.is_system || preset.filament_id != filament_id) continue; + std::shared_ptr new_preset = std::make_shared(preset); + std::vector printers; + get_filament_compatible_printer(new_preset.get(), printers); + for (const std::string &printer_name : printers) { + m_printer_compatible_presets[printer_name].push_back(new_preset); + } + } + if (m_printer_compatible_presets.empty()) { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " no filament presets "; + return false; + } + + return true; +} + +void EditFilamentPresetDialog::update_preset_tree() +{ + this->Freeze(); + m_preset_tree_sizer->Clear(true); + for (std::pair>> printer_and_presets : m_printer_compatible_presets) { + m_preset_tree_sizer->Add(m_preset_tree_creater->get_preset_tree(printer_and_presets), 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, 5); + } + if (m_printer_compatible_presets.size() == 1 && m_printer_compatible_presets.begin()->second.size() == 1) { + m_note_text->Show(); + } else { + m_note_text->Hide(); + } + + m_preset_tree_panel->SetSizerAndFit(m_preset_tree_sizer); + int width = m_preset_tree_panel->GetSize().GetWidth(); + int height = m_preset_tree_panel->GetSize().GetHeight(); + if (width < m_note_text->GetSize().GetWidth()) { + width = m_note_text->GetSize().GetWidth(); + m_preset_tree_panel->SetMinSize(wxSize(width, -1)); + } + int width_extend = 0; + int height_extend = 0; + if (width > 1000) height_extend = 22; + if (height > 400) width_extend = 22; + m_preset_tree_window->SetMinSize(wxSize(std::min(1000, width + width_extend), std::min(400, height + height_extend))); + m_preset_tree_window->SetMaxSize(wxSize(std::min(1000, width + width_extend), std::min(400, height + height_extend))); + m_preset_tree_window->SetSize(wxSize(std::min(1000, width + width_extend), std::min(400, height + height_extend))); + this->SetSizerAndFit(m_main_sizer); + + this->Layout(); + this->Fit(); + this->Refresh(); + wxGetApp().UpdateDlgDarkUI(this); + adjust_dialog_in_screen(this); + this->Thaw(); +} + +void EditFilamentPresetDialog::delete_preset() +{ + if (m_selected_printer.empty()) return; + if (m_need_delete_preset_index < 0) return; + std::unordered_map>>::iterator iter = m_printer_compatible_presets.find(m_selected_printer); + if (m_printer_compatible_presets.end() == iter) { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " can not find printer and printer name is: " << m_selected_printer; + return; + } + std::vector>& filament_presets = iter->second; + if (m_need_delete_preset_index >= filament_presets.size()) { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " index error and selected printer is: " << m_selected_printer << " and index: " << m_need_delete_preset_index; + return; + } + std::shared_ptr need_delete_preset = filament_presets[m_need_delete_preset_index]; + // is selecetd filament preset + if (need_delete_preset->name == wxGetApp().preset_bundle->filaments.get_selected_preset_name()) { + wxGetApp().get_tab(need_delete_preset->type)->delete_preset(); + // is preset exist? exist: not delete + Preset *delete_preset = wxGetApp().preset_bundle->filaments.find_preset(need_delete_preset->name, false); + if (delete_preset) { + m_selected_printer.clear(); + m_need_delete_preset_index = -1; + return; + } + } else { + Preset *filament_preset = wxGetApp().preset_bundle->filaments.find_preset(need_delete_preset->name); + + // is root preset ? + bool is_base_preset = false; + if (filament_preset && wxGetApp().preset_bundle->filaments.get_preset_base(*filament_preset) == filament_preset) { + is_base_preset = true; + int count = 0; + wxString presets; + for (auto &preset2 : wxGetApp().preset_bundle->filaments) + if (preset2.inherits() == filament_preset->name) { + ++count; + presets += "\n - " + from_u8(preset2.name); + } + wxString msg; + if (count > 0) { + msg = _L("Presets inherited by other presets can not be deleted"); + msg += "\n"; + msg += _L_PLURAL("The following presets inherits this preset.", "The following preset inherits this preset.", count); + wxString title = _L("Delete Preset"); + MessageDialog(this, msg + presets, title, wxOK | wxICON_ERROR).ShowModal(); + m_selected_printer.clear(); + m_need_delete_preset_index = -1; + return; + } + } + wxString msg; + if (is_base_preset) { + msg = _L("Are you sure to delete the selected preset? \nIf the preset corresponds to a filament currently in use on your printer, please reset the filament information for that slot."); + } else { + msg = _L("Are you sure to delete the selected preset?"); + } + if (wxID_YES != MessageDialog(this, msg, _L("Delete preset"), wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION).ShowModal()) { + m_selected_printer.clear(); + m_need_delete_preset_index = -1; + return; + } + + // delete preset + std::string next_selected_preset_name = wxGetApp().preset_bundle->filaments.get_selected_preset().name; + bool delete_result = delete_filament_preset_by_name(need_delete_preset->name, next_selected_preset_name); + BOOST_LOG_TRIVIAL(info) << __LINE__ << " filament preset name: " << need_delete_preset->name << (delete_result ? " delete successful" : " delete failed"); + + wxGetApp().preset_bundle->filaments.select_preset_by_name(next_selected_preset_name, true); + for (size_t i = 0; i < wxGetApp().preset_bundle->filament_presets.size(); ++i) { + auto preset = wxGetApp().preset_bundle->filaments.find_preset(wxGetApp().preset_bundle->filament_presets[i]); + if (preset == nullptr) wxGetApp().preset_bundle->filament_presets[i] = wxGetApp().preset_bundle->filaments.get_selected_preset_name(); + } + } + + // remove preset shared_ptr from m_printer_compatible_presets + int last_index = filament_presets.size() - 1; + if (m_need_delete_preset_index != last_index) { + std::swap(filament_presets[m_need_delete_preset_index], filament_presets[last_index]); + } + filament_presets.pop_back(); + if (filament_presets.empty()) m_printer_compatible_presets.erase(iter); + + update_preset_tree(); + + m_selected_printer.clear(); + m_need_delete_preset_index = -1; +} + +void EditFilamentPresetDialog::edit_preset() +{ + if (m_selected_printer.empty()) return; + if (m_need_edit_preset_index < 0) return; + std::unordered_map>>::iterator iter = m_printer_compatible_presets.find(m_selected_printer); + if (m_printer_compatible_presets.end() == iter) { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " can not find printer and printer name is: " << m_selected_printer; + return; + } + std::vector> &filament_presets = iter->second; + if (m_need_edit_preset_index >= filament_presets.size()) { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " index error and selected printer is: " << m_selected_printer << " and index: " << m_need_edit_preset_index; + return; + } + + // edit preset + m_need_edit_preset = filament_presets[m_need_edit_preset_index]; + wxGetApp().params_dialog()->set_editing_filament_id(m_filament_id); + + EndModal(wxID_EDIT); +} + +wxBoxSizer *EditFilamentPresetDialog::create_filament_basic_info() +{ + wxBoxSizer *basic_info_sizer = new wxBoxSizer(wxVERTICAL); + + wxBoxSizer *vendor_sizer = new wxBoxSizer(wxHORIZONTAL); + wxBoxSizer *type_sizer = new wxBoxSizer(wxHORIZONTAL); + wxBoxSizer *serial_sizer = new wxBoxSizer(wxHORIZONTAL); + + //vendor + wxBoxSizer * vendor_key_sizer = new wxBoxSizer(wxVERTICAL); + wxStaticText *static_vendor_text = new wxStaticText(this, wxID_ANY, _L("Vendor"), wxDefaultPosition, wxDefaultSize); + vendor_key_sizer->Add(static_vendor_text, 0, wxEXPAND | wxALL, 0); + vendor_key_sizer->SetMinSize(OPTION_SIZE); + vendor_sizer->Add(vendor_key_sizer, 0, wxEXPAND | wxLEFT | wxBOTTOM | wxALIGN_CENTER_VERTICAL, FromDIP(10)); + + wxBoxSizer *vendor_value_sizer = new wxBoxSizer(wxVERTICAL); + wxStaticText *vendor_text = new wxStaticText(this, wxID_ANY, from_u8(m_vendor_name), wxDefaultPosition, wxDefaultSize); + vendor_value_sizer->Add(vendor_text, 0, wxEXPAND | wxALL, 0); + vendor_sizer->Add(vendor_value_sizer, 0, wxEXPAND | wxLEFT | wxBOTTOM | wxALIGN_CENTER_VERTICAL, FromDIP(10)); + + //type + wxBoxSizer * type_key_sizer = new wxBoxSizer(wxVERTICAL); + wxStaticText *static_type_text = new wxStaticText(this, wxID_ANY, _L("Type"), wxDefaultPosition, wxDefaultSize); + type_key_sizer->Add(static_type_text, 0, wxEXPAND | wxALL, 0); + type_key_sizer->SetMinSize(OPTION_SIZE); + type_sizer->Add(type_key_sizer, 0, wxEXPAND | wxLEFT | wxBOTTOM | wxALIGN_CENTER_VERTICAL, FromDIP(10)); + + wxBoxSizer * type_value_sizer = new wxBoxSizer(wxVERTICAL); + wxStaticText *type_text = new wxStaticText(this, wxID_ANY, from_u8(m_filament_type), wxDefaultPosition, wxDefaultSize); + type_value_sizer->Add(type_text, 0, wxEXPAND | wxALL, 0); + type_sizer->Add(type_value_sizer, 0, wxEXPAND | wxLEFT | wxBOTTOM | wxALIGN_CENTER_VERTICAL, FromDIP(10)); + + //serial + wxBoxSizer * serial_key_sizer = new wxBoxSizer(wxVERTICAL); + wxStaticText *static_serial_text = new wxStaticText(this, wxID_ANY, _L("Serial"), wxDefaultPosition, wxDefaultSize); + serial_key_sizer->Add(static_serial_text, 0, wxEXPAND | wxALL, 0); + serial_key_sizer->SetMinSize(OPTION_SIZE); + serial_sizer->Add(serial_key_sizer, 0, wxEXPAND | wxLEFT | wxBOTTOM | wxALIGN_CENTER_VERTICAL, FromDIP(10)); + + wxBoxSizer * serial_value_sizer = new wxBoxSizer(wxVERTICAL); + wxString full_filamnet_serial = from_u8(m_filament_serial); + wxString show_filament_serial = full_filamnet_serial; + if (m_filament_serial.size() > 40) { + show_filament_serial = from_u8(m_filament_serial.substr(0, 20)) + "..."; + } + wxStaticText *serial_text = new wxStaticText(this, wxID_ANY, show_filament_serial, wxDefaultPosition, wxDefaultSize); + wxToolTip * toolTip = new wxToolTip(full_filamnet_serial); + serial_text->SetToolTip(toolTip); + serial_value_sizer->Add(serial_text, 0, wxEXPAND | wxALL, 0); + serial_sizer->Add(serial_value_sizer, 0, wxEXPAND | wxLEFT | wxBOTTOM | wxALIGN_CENTER_VERTICAL, FromDIP(10)); + + basic_info_sizer->Add(vendor_sizer, 0, wxEXPAND | wxALL, 0); + basic_info_sizer->Add(type_sizer, 0, wxEXPAND | wxALL, 0); + basic_info_sizer->Add(serial_sizer, 0, wxEXPAND | wxALL, 0); + + return basic_info_sizer; +} + +wxBoxSizer *EditFilamentPresetDialog::create_add_filament_btn() +{ + wxBoxSizer *add_filament_btn_sizer = new wxBoxSizer(wxHORIZONTAL); + m_add_filament_btn = new Button(this, _L("+ Add Preset")); + m_add_filament_btn->SetFont(Label::Body_10); + m_add_filament_btn->SetPaddingSize(wxSize(FromDIP(8), FromDIP(3))); + m_add_filament_btn->SetCornerRadius(FromDIP(8)); + + StateColor flush_bg_col(std::pair(wxColour(219, 253, 231), StateColor::Pressed), std::pair(wxColour(238, 238, 238), StateColor::Hovered), + std::pair(wxColour(238, 238, 238), StateColor::Normal)); + + StateColor flush_fg_col(std::pair(wxColour(107, 107, 106), StateColor::Pressed), std::pair(wxColour(107, 107, 106), StateColor::Hovered), + std::pair(wxColour(107, 107, 106), StateColor::Normal)); + + StateColor flush_bd_col(std::pair(wxColour(0, 174, 66), StateColor::Pressed), std::pair(wxColour(0, 174, 66), StateColor::Hovered), + std::pair(wxColour(172, 172, 172), StateColor::Normal)); + + m_add_filament_btn->SetBackgroundColor(flush_bg_col); + m_add_filament_btn->SetBorderColor(flush_bd_col); + m_add_filament_btn->SetTextColor(flush_fg_col); + add_filament_btn_sizer->Add(m_add_filament_btn, 0, wxEXPAND | wxALL, FromDIP(10)); + + m_add_filament_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent &e) { + CreatePresetForPrinterDialog dlg(nullptr, m_filament_type, m_filament_id, m_vendor_name, m_filament_name); + int res = dlg.ShowModal(); + if (res == wxID_OK) { + if (get_same_filament_id_presets(m_filament_id)) { + update_preset_tree(); + } + } + }); + + return add_filament_btn_sizer; +} + +wxBoxSizer *EditFilamentPresetDialog::create_preset_tree_sizer() +{ + wxBoxSizer *filament_preset_tree_sizer = new wxBoxSizer(wxHORIZONTAL); + m_preset_tree_window = new wxScrolledWindow(this); + m_preset_tree_window->SetScrollRate(5, 5); + m_preset_tree_window->SetBackgroundColour(PRINTER_LIST_COLOUR); + m_preset_tree_window->SetMinSize(wxSize(-1, FromDIP(400))); + m_preset_tree_window->SetMaxSize(wxSize(-1, FromDIP(300))); + m_preset_tree_window->SetSize(wxSize(-1, FromDIP(300))); + m_preset_tree_panel = new wxPanel(m_preset_tree_window); + m_preset_tree_sizer = new wxBoxSizer(wxVERTICAL); + m_preset_tree_panel->SetSizer(m_preset_tree_sizer); + m_preset_tree_panel->SetMinSize(wxSize(580, -1)); + m_preset_tree_panel->SetBackgroundColour(PRINTER_LIST_COLOUR); + wxBoxSizer* m_preset_tree_window_sizer = new wxBoxSizer(wxVERTICAL); + m_preset_tree_window_sizer->Add(m_preset_tree_panel, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(10)); + m_preset_tree_window->SetSizerAndFit(m_preset_tree_window_sizer); + filament_preset_tree_sizer->Add(m_preset_tree_window, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(10)); + + return filament_preset_tree_sizer; +} + +wxBoxSizer *EditFilamentPresetDialog::create_button_sizer() +{ + wxBoxSizer *bSizer_button = new wxBoxSizer(wxHORIZONTAL); + + m_del_filament_btn = new Button(this, _L("Delete Filament")); + m_del_filament_btn->SetBackgroundColor(*wxRED); + m_del_filament_btn->SetBorderColor(*wxWHITE); + m_del_filament_btn->SetTextColor(wxColour(0xFFFFFE)); + m_del_filament_btn->SetFont(Label::Body_12); + m_del_filament_btn->SetSize(wxSize(FromDIP(58), FromDIP(24))); + m_del_filament_btn->SetMinSize(wxSize(FromDIP(58), FromDIP(24))); + m_del_filament_btn->SetCornerRadius(FromDIP(12)); + bSizer_button->Add(m_del_filament_btn, 0, wxLEFT | wxBOTTOM, FromDIP(10)); + + bSizer_button->Add(0, 0, 1, wxEXPAND, 0); + + StateColor btn_bg_green(std::pair(wxColour(27, 136, 68), StateColor::Pressed), std::pair(wxColour(61, 203, 115), StateColor::Hovered), + std::pair(wxColour(0, 174, 66), StateColor::Normal)); + + m_ok_btn = new Button(this, _L("OK")); + m_ok_btn->SetBackgroundColor(btn_bg_green); + m_ok_btn->SetBorderColor(*wxWHITE); + m_ok_btn->SetTextColor(wxColour(0xFFFFFE)); + m_ok_btn->SetFont(Label::Body_12); + m_ok_btn->SetSize(wxSize(FromDIP(58), FromDIP(24))); + m_ok_btn->SetMinSize(wxSize(FromDIP(58), FromDIP(24))); + m_ok_btn->SetCornerRadius(FromDIP(12)); + bSizer_button->Add(m_ok_btn, 0, wxRIGHT | wxBOTTOM, FromDIP(10)); + + StateColor btn_bg_white(std::pair(wxColour(206, 206, 206), StateColor::Pressed), std::pair(wxColour(238, 238, 238), StateColor::Hovered), + std::pair(*wxWHITE, StateColor::Normal)); + + m_del_filament_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent &e) { + WarningDialog dlg(this, _L("All the filament presets belong to this filament would be deleted. \nIf you are using this filament on your printer, please reset the filament information for that slot."), _L("Delete filament"), wxYES | wxCANCEL | wxCANCEL_DEFAULT | wxCENTRE); + int res = dlg.ShowModal(); + if (wxID_YES == res) { + PresetBundle *preset_bundle = wxGetApp().preset_bundle; + std::set> inherit_preset_names; + std::set> root_preset_names; + for (std::pair>> printer_and_preset : m_printer_compatible_presets) { + for (std::shared_ptr preset : printer_and_preset.second) { + if (preset->inherits().empty()) { + root_preset_names.insert(preset); + } else { + inherit_preset_names.insert(preset); + } + } + } + // delete inherit preset first + std::string next_selected_preset_name = wxGetApp().preset_bundle->filaments.get_selected_preset().name; + for (std::shared_ptr preset : inherit_preset_names) { + bool delete_result = delete_filament_preset_by_name(preset->name, next_selected_preset_name); + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " inherit filament name: " << preset->name << (delete_result ? " delete successful" : " delete failed"); + } + for (std::shared_ptr preset : root_preset_names) { + bool delete_result = delete_filament_preset_by_name(preset->name, next_selected_preset_name); + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " root filament name: " << preset->name << (delete_result ? " delete successful" : " delete failed"); + } + m_printer_compatible_presets.clear(); + wxGetApp().preset_bundle->filaments.select_preset_by_name(next_selected_preset_name,true); + + for (size_t i = 0; i < wxGetApp().preset_bundle->filament_presets.size(); ++i) { + auto preset = wxGetApp().preset_bundle->filaments.find_preset(wxGetApp().preset_bundle->filament_presets[i]); + if (preset == nullptr) wxGetApp().preset_bundle->filament_presets[i] = wxGetApp().preset_bundle->filaments.get_selected_preset_name(); + } + EndModal(wxID_OK); + } + e.Skip(); + })); + + m_ok_btn->Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent &e) { EndModal(wxID_OK); }); + + return bSizer_button; + +} + +CreatePresetForPrinterDialog::CreatePresetForPrinterDialog(wxWindow *parent, std::string filament_type, std::string filament_id, std::string filament_vendor, std::string filament_name) + : DPIDialog(parent ? parent : nullptr, wxID_ANY, _L("Add Preset"), wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX) + , m_filament_id(filament_id) + , m_filament_name(filament_name) + , m_filament_vendor(filament_vendor) + , m_filament_type(filament_type) +{ + m_preset_bundle = std::make_shared(*(wxGetApp().preset_bundle)); + get_visible_printer_and_compatible_filament_presets(); + + this->SetBackgroundColour(*wxWHITE); + + std::string icon_path = (boost::format("%1%/images/BambuStudioTitle.ico") % resources_dir()).str(); + SetIcon(wxIcon(encode_path(icon_path.c_str()), wxBITMAP_TYPE_ICO)); + + wxBoxSizer *main_sizer = new wxBoxSizer(wxVERTICAL); + // top line + auto m_line_top = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(-1, 1), wxTAB_TRAVERSAL); + m_line_top->SetBackgroundColour(wxColour(0xA6, 0xa9, 0xAA)); + main_sizer->Add(m_line_top, 0, wxEXPAND, 0); + main_sizer->Add(0, 0, 0, wxTOP, FromDIP(5)); + + wxStaticText *basic_infomation = new wxStaticText(this, wxID_ANY, _L("Add preset for new printer")); + basic_infomation->SetFont(Label::Head_16); + main_sizer->Add(basic_infomation, 0, wxALL, FromDIP(10)); + + main_sizer->Add(create_selected_printer_preset_sizer(), 0, wxALL, FromDIP(10)); + main_sizer->Add(create_selected_filament_preset_sizer(), 0, wxALL, FromDIP(10)); + main_sizer->Add(create_button_sizer(), 0, wxEXPAND | wxALL, FromDIP(10)); + + this->SetSizer(main_sizer); + this->Layout(); + this->Fit(); + wxGetApp().UpdateDlgDarkUI(this); +} + +CreatePresetForPrinterDialog::~CreatePresetForPrinterDialog() {} + +void CreatePresetForPrinterDialog::on_dpi_changed(const wxRect &suggested_rect) { + m_ok_btn->SetMinSize(wxSize(FromDIP(58), FromDIP(24))); + m_ok_btn->SetMaxSize(wxSize(FromDIP(58), FromDIP(24))); + m_ok_btn->SetCornerRadius(FromDIP(12)); + m_cancel_btn->SetMinSize(wxSize(FromDIP(58), FromDIP(24))); + m_cancel_btn->SetMaxSize(wxSize(FromDIP(58), FromDIP(24))); + m_cancel_btn->SetCornerRadius(FromDIP(12)); + Layout(); +} + +void CreatePresetForPrinterDialog::get_visible_printer_and_compatible_filament_presets() +{ + const std::deque &printer_presets = m_preset_bundle->printers.get_presets(); + m_printer_compatible_filament_presets.clear(); + for (const Preset &printer_preset : printer_presets) { + if (printer_preset.is_visible) { + if (m_preset_bundle->printers.get_preset_base(printer_preset) != &printer_preset) continue; + if (m_preset_bundle->printers.select_preset_by_name(printer_preset.name, true)) { + m_preset_bundle->update_compatible(PresetSelectCompatibleType::Always); + const std::deque &filament_presets = m_preset_bundle->filaments.get_presets(); + for (const Preset &filament_preset : filament_presets) { + if (filament_preset.is_default || !filament_preset.is_compatible || filament_preset.is_project_embedded) continue; + ConfigOptionStrings *filament_types; + const Preset * filament_preset_base = m_preset_bundle->filaments.get_preset_base(filament_preset); + if (filament_preset_base == &filament_preset) { + filament_types = dynamic_cast(const_cast(&filament_preset)->config.option("filament_type")); + } else { + filament_types = dynamic_cast(const_cast(filament_preset_base)->config.option("filament_type")); + } + + if (filament_types && filament_types->values.empty()) continue; + const std::string filament_type = filament_types->values[0]; + std::string filament_type_ = m_filament_type == "PLA Aero" ? "PLA-AERO" : m_filament_type; + if (filament_type == filament_type_) { + m_printer_compatible_filament_presets[printer_preset.name].push_back(std::make_shared(filament_preset)); + } + } + } + } + } +} + +wxBoxSizer *CreatePresetForPrinterDialog::create_selected_printer_preset_sizer() +{ + wxBoxSizer *select_preseter_preset_sizer = new wxBoxSizer(wxVERTICAL); + wxStaticText *printer_text = new wxStaticText(this, wxID_ANY, _L("Printer"), wxDefaultPosition, wxDefaultSize); + select_preseter_preset_sizer->Add(printer_text, 0, wxEXPAND | wxALL, 0); + m_selected_printer = new ComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, PRINTER_PRESET_MODEL_SIZE, 0, nullptr, wxCB_READONLY); + select_preseter_preset_sizer->Add(m_selected_printer, 0, wxEXPAND | wxTOP, FromDIP(5)); + + wxArrayString printer_choices; + for (std::pair>> printer_to_filament_presets : m_printer_compatible_filament_presets) { + auto compatible_printer_name = printer_to_filament_presets.first; + if (compatible_printer_name.empty()) { + BOOST_LOG_TRIVIAL(info)<<__FUNCTION__ << " a printer has no name"; + continue; + } + wxString printer_name = from_u8(compatible_printer_name); + printer_choices.push_back(printer_name); + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " entry, and visible printer is: " << compatible_printer_name; + } + m_selected_printer->Set(printer_choices); + + return select_preseter_preset_sizer; +} + +wxBoxSizer *CreatePresetForPrinterDialog::create_selected_filament_preset_sizer() +{ + wxBoxSizer * select_filament_preset_sizer = new wxBoxSizer(wxVERTICAL); + wxStaticText *printer_text = new wxStaticText(this, wxID_ANY, _L("Copy preset from filament"), wxDefaultPosition, wxDefaultSize); + select_filament_preset_sizer->Add(printer_text, 0, wxEXPAND | wxALL, 0); + m_selected_filament = new ComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, PRINTER_PRESET_MODEL_SIZE, 0, nullptr, wxCB_READONLY); + select_filament_preset_sizer->Add(m_selected_filament, 0, wxEXPAND | wxTOP, FromDIP(5)); + + m_selected_printer->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent &e) { + wxString printer_name = m_selected_printer->GetStringSelection(); + std::unordered_map>>::iterator filament_iter = m_printer_compatible_filament_presets.find(into_u8(printer_name)); + if (m_printer_compatible_filament_presets.end() != filament_iter) { + filament_choice_to_filament_preset.clear(); + wxArrayString filament_choices; + for (std::shared_ptr filament_preset : filament_iter->second) { + wxString filament_name = wxString::FromUTF8(filament_preset->name); + filament_choice_to_filament_preset[filament_name] = filament_preset; + filament_choices.push_back(filament_name); + } + m_selected_filament->Set(filament_choices); + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " count of compatible filament presets :" << filament_choices.size(); + if (filament_choices.size()) { m_selected_filament->SetSelection(0); } + } else { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "printer preset not find compatible filament presets"; + } + }); + + return select_filament_preset_sizer; +} + +wxBoxSizer *CreatePresetForPrinterDialog::create_button_sizer() +{ + wxBoxSizer *bSizer_button = new wxBoxSizer(wxHORIZONTAL); + + bSizer_button->Add(0, 0, 1, wxEXPAND, 0); + + StateColor btn_bg_green(std::pair(wxColour(27, 136, 68), StateColor::Pressed), std::pair(wxColour(61, 203, 115), StateColor::Hovered), + std::pair(wxColour(0, 174, 66), StateColor::Normal)); + + m_ok_btn = new Button(this, _L("OK")); + m_ok_btn->SetBackgroundColor(btn_bg_green); + m_ok_btn->SetBorderColor(*wxWHITE); + m_ok_btn->SetTextColor(wxColour(0xFFFFFE)); + m_ok_btn->SetFont(Label::Body_12); + m_ok_btn->SetSize(wxSize(FromDIP(58), FromDIP(24))); + m_ok_btn->SetMinSize(wxSize(FromDIP(58), FromDIP(24))); + m_ok_btn->SetCornerRadius(FromDIP(12)); + bSizer_button->Add(m_ok_btn, 0, wxRIGHT | wxBOTTOM, FromDIP(10)); + + StateColor btn_bg_white(std::pair(wxColour(206, 206, 206), StateColor::Pressed), std::pair(wxColour(238, 238, 238), StateColor::Hovered), + std::pair(*wxWHITE, StateColor::Normal)); + + m_cancel_btn = new Button(this, _L("Cancel")); + m_cancel_btn->SetBackgroundColor(btn_bg_white); + m_cancel_btn->SetBorderColor(wxColour(38, 46, 48)); + m_cancel_btn->SetFont(Label::Body_12); + m_cancel_btn->SetSize(wxSize(FromDIP(58), FromDIP(24))); + m_cancel_btn->SetMinSize(wxSize(FromDIP(58), FromDIP(24))); + m_cancel_btn->SetCornerRadius(FromDIP(12)); + bSizer_button->Add(m_cancel_btn, 0, wxRIGHT | wxBOTTOM, FromDIP(10)); + + m_ok_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent &e) { + wxString selected_printer_name = m_selected_printer->GetStringSelection(); + std::string printer_name = into_u8(selected_printer_name); + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " add preset: get compatible printer name:"; + + wxString filament_preset_name = m_selected_filament->GetStringSelection(); + std::unordered_map>::iterator iter = filament_choice_to_filament_preset.find(filament_preset_name); + if (filament_choice_to_filament_preset.end() != iter) { + std::shared_ptr filament_preset = iter->second; + PresetBundle * preset_bundle = wxGetApp().preset_bundle; + std::vector failures; + DynamicConfig dynamic_config; + dynamic_config.set_key_value("filament_vendor", new ConfigOptionStrings({m_filament_vendor})); + dynamic_config.set_key_value("compatible_printers", new ConfigOptionStrings({printer_name})); + dynamic_config.set_key_value("filament_type", new ConfigOptionStrings({m_filament_type})); + bool res = preset_bundle->filaments.clone_presets_for_filament(filament_preset.get(), failures, m_filament_name, m_filament_id, dynamic_config, printer_name); + if (!res) { + std::string failure_names; + for (std::string &failure : failures) { failure_names += failure + "\n"; } + MessageDialog dlg(this, _L("Some existing presets have failed to be created, as follows:\n") + from_u8(failure_names) + _L("\nDo you want to rewrite it?"), + wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxYES_NO | wxYES_DEFAULT | wxCENTRE); + if (dlg.ShowModal() == wxID_YES) { + res = preset_bundle->filaments.clone_presets_for_filament(filament_preset.get(), failures, m_filament_name, m_filament_id, dynamic_config, printer_name, true); + BOOST_LOG_TRIVIAL(info) << "clone filament have failures rewritten is successful? " << res; + } else { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "have same name preset and not rewritten"; + return; + } + } else { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "create filament preset successful and name is:" << m_filament_name + " @" + printer_name; + } + + } else { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "filament choice not find filament preset and choice is:" << filament_preset_name; + MessageDialog dlg(this, _L("The filament choice not find filament preset, please reselect it"), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), + wxYES | wxYES_DEFAULT | wxCENTRE); + dlg.ShowModal(); + return; + } + + EndModal(wxID_OK); + }); + m_cancel_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent &e) { EndModal(wxID_CANCEL); }); + + return bSizer_button; +} + +PresetTree::PresetTree(EditFilamentPresetDialog * dialog) +: m_parent_dialog(dialog) +{} + +wxPanel *PresetTree::get_preset_tree(std::pair>> printer_and_presets) +{ + wxBoxSizer *sizer = new wxBoxSizer(wxVERTICAL); + wxPanel * parent = m_parent_dialog->get_preset_tree_panel(); + wxPanel * panel = new wxPanel(parent); + wxColour backgroundColor = parent->GetBackgroundColour(); + panel->SetBackgroundColour(backgroundColor); + const std::string &printer_name = printer_and_presets.first; + sizer->Add(get_root_item(panel, printer_name), 0, wxEXPAND, 0); + int child_count = printer_and_presets.second.size(); + for (int i = 0; i < child_count; i++) { + if (i == child_count - 1) { + sizer->Add(get_child_item(panel, printer_and_presets.second[i], printer_name, i, true), 0, wxEXPAND, 0); + } else { + sizer->Add(get_child_item(panel, printer_and_presets.second[i], printer_name, i, false), 0, wxEXPAND, 0); + } + } + panel->SetSizerAndFit(sizer); + return panel; +} + +wxPanel *PresetTree::get_root_item(wxPanel *parent, const std::string &printer_name) +{ + wxBoxSizer *sizer = new wxBoxSizer(wxHORIZONTAL); + wxPanel * panel = new wxPanel(parent); + wxColour backgroundColor = parent->GetBackgroundColour(); + panel->SetBackgroundColour(backgroundColor); + wxStaticText *preset_name = new wxStaticText(panel, wxID_ANY, from_u8(printer_name)); + preset_name->SetFont(Label::Body_11); + preset_name->SetForegroundColour(*wxBLACK); + sizer->Add(preset_name, 0, wxEXPAND | wxALL, 5); + panel->SetSizer(sizer); + + return panel; +} + +wxPanel *PresetTree::get_child_item(wxPanel *parent, std::shared_ptr preset, std::string printer_name, int preset_index, bool is_last) +{ + wxBoxSizer *sizer = new wxBoxSizer(wxHORIZONTAL); + wxPanel * panel = new wxPanel(parent); + wxColour backgroundColor = parent->GetBackgroundColour(); + panel->SetBackgroundColour(backgroundColor); + sizer->Add(0, 0, 0, wxLEFT, 10); + wxPanel *line_left = new wxPanel(panel, wxID_ANY, wxDefaultPosition, is_last ? wxSize(1, 12) : wxSize(1, -1)); + line_left->SetBackgroundColour(*wxBLACK); + sizer->Add(line_left, 0, is_last ? wxALL : wxEXPAND | wxALL, 0); + wxPanel *line_right = new wxPanel(panel, wxID_ANY, wxDefaultPosition, wxSize(10, 1)); + line_right->SetBackgroundColour(*wxBLACK); + sizer->Add(line_right, 0, wxALL | wxALIGN_CENTER_VERTICAL, 0); + sizer->Add(0, 0, 0, wxLEFT, 5); + wxStaticText *preset_name = new wxStaticText(panel, wxID_ANY, from_u8(preset->name)); + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " create child item: " << preset->name; + preset_name->SetFont(Label::Body_10); + preset_name->SetForegroundColour(*wxBLACK); + sizer->Add(preset_name, 0, wxEXPAND | wxALL, 5); + bool base_id_error = false; + if (preset->inherits() == "" && preset->base_id != "") base_id_error = true; + if (base_id_error) { + std::string wiki_url = "https://wiki.bambulab.com/en/software/bambu-studio/custom-filament-issue"; + wxHyperlinkCtrl *m_download_hyperlink = new wxHyperlinkCtrl(panel, wxID_ANY, _L("[Delete Required]"), wiki_url, wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE); + m_download_hyperlink->SetFont(Label::Body_10); + sizer->Add(m_download_hyperlink, 0, wxEXPAND | wxALL, 5); + } + sizer->Add(0, 0, 1, wxEXPAND, 0); + + StateColor flush_bg_col(std::pair(wxColour(219, 253, 231), StateColor::Pressed), std::pair(wxColour(238, 238, 238), StateColor::Hovered), + std::pair(wxColour(238, 238, 238), StateColor::Normal)); + + StateColor flush_fg_col(std::pair(wxColour(107, 107, 106), StateColor::Pressed), std::pair(wxColour(107, 107, 106), StateColor::Hovered), + std::pair(wxColour(107, 107, 106), StateColor::Normal)); + + StateColor flush_bd_col(std::pair(wxColour(0, 174, 66), StateColor::Pressed), std::pair(wxColour(0, 174, 66), StateColor::Hovered), + std::pair(wxColour(172, 172, 172), StateColor::Normal)); + + StateColor btn_bg_green(std::pair(wxColour(27, 136, 68), StateColor::Pressed), std::pair(wxColour(61, 203, 115), StateColor::Hovered), + std::pair(wxColour(0, 174, 66), StateColor::Normal)); + + Button *edit_preset_btn = new Button(panel, _L("Edit Preset")); + edit_preset_btn->SetFont(Label::Body_10); + edit_preset_btn->SetPaddingSize(wxSize(8, 3)); + edit_preset_btn->SetCornerRadius(8); + edit_preset_btn->SetBackgroundColor(flush_bg_col); + edit_preset_btn->SetBorderColor(flush_bd_col); + edit_preset_btn->SetTextColor(flush_fg_col); + //edit_preset_btn->Hide(); + sizer->Add(edit_preset_btn, 0, wxALL | wxALIGN_CENTER_VERTICAL, 0); + sizer->Add(0, 0, 0, wxLEFT, 5); + + Button *del_preset_btn = new Button(panel, _L("Delete Preset")); + del_preset_btn->SetFont(Label::Body_10); + del_preset_btn->SetPaddingSize(wxSize(8, 3)); + del_preset_btn->SetCornerRadius(8); + if (base_id_error) { + del_preset_btn->SetBackgroundColor(btn_bg_green); + del_preset_btn->SetBorderColor(btn_bg_green); + del_preset_btn->SetTextColor(wxColour(0xFFFFFE)); + } else { + del_preset_btn->SetBackgroundColor(flush_bg_col); + del_preset_btn->SetBorderColor(flush_bd_col); + del_preset_btn->SetTextColor(flush_fg_col); + } + + //del_preset_btn->Hide(); + sizer->Add(del_preset_btn, 0, wxALL | wxALIGN_CENTER_VERTICAL, 0); + + edit_preset_btn->Bind(wxEVT_BUTTON, [this, printer_name, preset_index](wxCommandEvent &e) { + wxGetApp().CallAfter([this, printer_name, preset_index]() { edit_preset(printer_name, preset_index); }); + }); + del_preset_btn->Bind(wxEVT_BUTTON, [this, printer_name, preset_index](wxCommandEvent &e) { + wxGetApp().CallAfter([this, printer_name, preset_index]() { delete_preset(printer_name, preset_index); }); + }); + + panel->SetSizer(sizer); + + return panel; +} + +void PresetTree::delete_preset(std::string printer_name, int need_delete_preset_index) +{ + m_parent_dialog->set_printer_name(printer_name); + m_parent_dialog->set_need_delete_preset_index(need_delete_preset_index); + m_parent_dialog->delete_preset(); +} + +void PresetTree::edit_preset(std::string printer_name, int need_edit_preset_index) +{ + m_parent_dialog->set_printer_name(printer_name); + m_parent_dialog->set_need_edit_preset_index(need_edit_preset_index); + m_parent_dialog->edit_preset(); +} + +} // namespace GUI + +} // namespace Slic3r \ No newline at end of file diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 8d8615644..1bce721ef 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -4556,11 +4556,16 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv return; const Size cnv_size = wxGetApp().plater()->get_current_canvas3D()->get_canvas_size(); - + //Plater s_panel = wxGetApp().plater(); + //GLCanvas3D* s_panel = wxGetApp().plater()->get_current_canvas3D(); + //wxPanel* s_panel = p->status_panel; ImGuiWrapper& imgui = *wxGetApp().imgui(); + //ImGuiWrapper& imgui2 = *wxGetApp().imgui(); + //wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL); //BBS: GUI refactor: move to the right imgui.set_next_window_pos(float(canvas_width - right_margin * m_scale), 0.0f, ImGuiCond_Always, 1.0f, 0.0f); + //imgui2.set_next_window_pos(float(canvas_width - right_margin * m_scale), 1.0f, ImGuiCond_Always, 2.0f, 1.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0,0.0)); ImGui::PushStyleColor(ImGuiCol_Separator, ImVec4(1.0f,1.0f,1.0f,0.6f)); @@ -4673,16 +4678,16 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv // BBS render column item { - if(callback && !checkbox && !visible) + if (callback && !checkbox && !visible) ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(172 / 255.0f, 172 / 255.0f, 172 / 255.0f, 1.00f)); float dummy_size = type == EItemType::None ? window_padding * 3 : ImGui::GetStyle().ItemSpacing.x + icon_size; ImGui::SameLine(dummy_size); imgui.text(columns_offsets[0].first); - - for (auto i = 1; i < columns_offsets.size(); i++) { - ImGui::SameLine(columns_offsets[i].second); - imgui.text(columns_offsets[i].first); - } + for (auto i = 1; i < columns_offsets.size(); i++) { + ImGui::SameLine(columns_offsets[i].second); + imgui.text(columns_offsets[i].first); + } + if (callback && !checkbox && !visible) ImGui::PopStyleColor(1); } @@ -4863,46 +4868,84 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv ImGui::PopStyleColor(3); ImGui::PopStyleVar(1); ImGui::SameLine(); - imgui.bold_text(_u8L("Color Scheme")); - push_combo_style(); - //xiamian+ - //wxBoxSizer* moreOptionsSizer = new wxBoxSizer(wxVERTICAL); + imgui.bold_text(_u8L("More parameters")); + //push_combo_style(); + + ////xiamian+ + ////wxBoxSizer* moreOptionsSizer = new wxBoxSizer(wxVERTICAL); - ImGui::SameLine(); - const char* view_type_value = view_type_items_str[m_view_type_sel].c_str(); - ImGuiComboFlags flags = 0; - if (ImGui::BBLBeginCombo("", view_type_value, flags)) { - ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 0.0f); - for (int i = 0; i < view_type_items_str.size(); i++) { - const bool is_selected = (m_view_type_sel == i); - if (ImGui::BBLSelectable(view_type_items_str[i].c_str(), is_selected)) { - m_fold = false; - m_view_type_sel = i; - set_view_type(view_type_items[m_view_type_sel]); - reset_visible(view_type_items[m_view_type_sel]); - // update buffers' render paths - refresh_render_paths(false, false); - update_moves_slider(); - wxGetApp().plater()->get_current_canvas3D()->set_as_dirty(); - } - if (is_selected) { - ImGui::SetItemDefaultFocus(); - } - } - ImGui::PopStyleVar(1); - ImGui::EndCombo(); - } - pop_combo_style(); - ImGui::SameLine(); - ImGui::Dummy({ window_padding, window_padding }); + //ImGui::SameLine(); + //const char* view_type_value = view_type_items_str[m_view_type_sel].c_str(); + //ImGuiComboFlags flags = 0; + //if (ImGui::BBLBeginCombo("", view_type_value, flags)) { + // ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 0.0f); + // //select controller + // for (int i = 0; i < view_type_items_str.size(); i++) { + // const bool is_selected = (m_view_type_sel == i); + // if (ImGui::BBLSelectable(view_type_items_str[i].c_str(), is_selected)) { + // m_fold = false; + // m_view_type_sel = i; + // set_view_type(view_type_items[m_view_type_sel]); + // reset_visible(view_type_items[m_view_type_sel]); + // // update buffers' render paths + // refresh_render_paths(false, false); + // update_moves_slider(); + // wxGetApp().plater()->get_current_canvas3D()->set_as_dirty(); + // } + // if (is_selected) { + // ImGui::SetItemDefaultFocus(); + // } + // } + // ImGui::PopStyleVar(1); + // ImGui::EndCombo(); + //} + //pop_combo_style(); + - if (m_fold) { + //ImGui::Dummy({ window_padding, window_padding }); + + /* if (m_fold) { legend_height = ImGui::GetStyle().WindowPadding.y + ImGui::GetFrameHeight() + window_padding * 2.5; imgui.end(); ImGui::PopStyleColor(6); ImGui::PopStyleVar(2); return; - } + }*/ + + //ImGui::Dummy({ window_padding, window_padding }); + + //imgui.bold_text(_u8L("Color Scheme")); + + //push_combo_style(); + //ImGui::SameLine(); + //const char* view_type_value = view_type_items_str[m_view_type_sel].c_str(); + //ImGuiComboFlags flags = 0; + //if (ImGui::BBLBeginCombo("", view_type_value, flags)) { + // ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 0.0f); + // //select controller + // for (int i = 0; i < view_type_items_str.size(); i++) { + // const bool is_selected = (m_view_type_sel == i); + // if (ImGui::BBLSelectable(view_type_items_str[i].c_str(), is_selected)) { + // m_fold = false; + // m_view_type_sel = i; + // set_view_type(view_type_items[m_view_type_sel]); + // reset_visible(view_type_items[m_view_type_sel]); + // // update buffers' render paths + // refresh_render_paths(false, false); + // update_moves_slider(); + // wxGetApp().plater()->get_current_canvas3D()->set_as_dirty(); + // } + // if (is_selected) { + // ImGui::SetItemDefaultFocus(); + // } + // } + // ImGui::PopStyleVar(1); + // ImGui::EndCombo(); + //} + //pop_combo_style(); + + //ImGui::SameLine(); + //ImGui::Dummy({ window_padding, window_padding }); // data used to properly align items in columns when showing time std::vector offsets; @@ -4939,199 +4982,16 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv double koef = imperial_units ? GizmoObjectManipulation::in_to_mm : 1000.0; double unit_conver = imperial_units ? GizmoObjectManipulation::oz_to_g : 1; - - // used filament statistics - for (size_t extruder_id : m_extruder_ids) { - if (m_print_statistics.model_volumes_per_extruder.find(extruder_id) == m_print_statistics.model_volumes_per_extruder.end()) { - model_used_filaments_m.push_back(0.0); - model_used_filaments_g.push_back(0.0); - } - else { - double volume = m_print_statistics.model_volumes_per_extruder.at(extruder_id); - auto [model_used_filament_m, model_used_filament_g] = get_used_filament_from_volume(volume, extruder_id); - model_used_filaments_m.push_back(model_used_filament_m); - model_used_filaments_g.push_back(model_used_filament_g); - total_model_used_filament_m += model_used_filament_m; - total_model_used_filament_g += model_used_filament_g; - displayed_columns |= ColumnData::Model; - } - } - - for (size_t extruder_id : m_extruder_ids) { - if (m_print_statistics.wipe_tower_volumes_per_extruder.find(extruder_id) == m_print_statistics.wipe_tower_volumes_per_extruder.end()) { - wipe_tower_used_filaments_m.push_back(0.0); - wipe_tower_used_filaments_g.push_back(0.0); - } - else { - double volume = m_print_statistics.wipe_tower_volumes_per_extruder.at(extruder_id); - auto [wipe_tower_used_filament_m, wipe_tower_used_filament_g] = get_used_filament_from_volume(volume, extruder_id); - wipe_tower_used_filaments_m.push_back(wipe_tower_used_filament_m); - wipe_tower_used_filaments_g.push_back(wipe_tower_used_filament_g); - total_wipe_tower_used_filament_m += wipe_tower_used_filament_m; - total_wipe_tower_used_filament_g += wipe_tower_used_filament_g; - displayed_columns |= ColumnData::WipeTower; - } - } - - for (size_t extruder_id : m_extruder_ids) { - if (m_print_statistics.flush_per_filament.find(extruder_id) == m_print_statistics.flush_per_filament.end()) { - flushed_filaments_m.push_back(0.0); - flushed_filaments_g.push_back(0.0); - } - else { - double volume = m_print_statistics.flush_per_filament.at(extruder_id); - auto [flushed_filament_m, flushed_filament_g] = get_used_filament_from_volume(volume, extruder_id); - flushed_filaments_m.push_back(flushed_filament_m); - flushed_filaments_g.push_back(flushed_filament_g); - total_flushed_filament_m += flushed_filament_m; - total_flushed_filament_g += flushed_filament_g; - displayed_columns |= ColumnData::Flushed; - } - } - - for (size_t extruder_id : m_extruder_ids) { - if (m_print_statistics.support_volumes_per_extruder.find(extruder_id) == m_print_statistics.support_volumes_per_extruder.end()) { - support_used_filaments_m.push_back(0.0); - support_used_filaments_g.push_back(0.0); - } - else { - double volume = m_print_statistics.support_volumes_per_extruder.at(extruder_id); - auto [used_filament_m, used_filament_g] = get_used_filament_from_volume(volume, extruder_id); - support_used_filaments_m.push_back(used_filament_m); - support_used_filaments_g.push_back(used_filament_g); - total_support_used_filament_m += used_filament_m; - total_support_used_filament_g += used_filament_g; - displayed_columns |= ColumnData::Support; - } - } - - - // extrusion paths section -> title - ImGui::Dummy({ window_padding, window_padding }); - ImGui::SameLine(); - switch (m_view_type) - { - case EViewType::FeatureType: - { - // calculate offsets to align time/percentage data - char buffer[64]; - for (size_t i = 0; i < m_roles.size(); ++i) { - ExtrusionRole role = m_roles[i]; - if (role < erCount) { - labels.push_back(_u8L(ExtrusionEntity::role_to_string(role))); - auto [time, percent] = role_time_and_percent(role); - times.push_back((time > 0.0f) ? short_time(get_time_dhms(time)) : ""); - if (percent == 0) - ::sprintf(buffer, "0%%"); - else - percent > 0.001 ? ::sprintf(buffer, "%.1f%%", percent * 100) : ::sprintf(buffer, "<0.1%%"); - percents.push_back(buffer); - auto [model_used_filament_m, model_used_filament_g] = used_filament_per_role(role); - //model_used_filaments_m.push_back(model_used_filament_m); - //model_used_filaments_g.push_back(model_used_filament_g); - memset(&buffer, 0, sizeof(buffer)); - ::sprintf(buffer, imperial_units ? "%.2f in" : "%.2f m", model_used_filament_m); - used_filaments_m.push_back(buffer); - - memset(&buffer, 0, sizeof(buffer)); - ::sprintf(buffer, "%.2f g", model_used_filament_g); - used_filaments_g.push_back(buffer); - } - } - - //BBS: get travel time and percent - { - auto [time, percent] = move_time_and_percent(EMoveType::Travel); - travel_time = (time > 0.0f) ? short_time(get_time_dhms(time)) : ""; - if (percent == 0) - ::sprintf(buffer, "0%%"); - else - percent > 0.001 ? ::sprintf(buffer, "%.1f%%", percent * 100) : ::sprintf(buffer, "<0.1%%"); - travel_percent = buffer; - } - - offsets = calculate_offsets({ {_u8L("Line Type"), labels}, {_u8L("Time"), times}, {_u8L("Percent"), percents}, {_u8L("Used filament"), used_filaments_m}, {"", used_filaments_g}, {_u8L("Display"), {""}}}, icon_size); - append_headers({{_u8L("Line Type"), offsets[0]}, {_u8L("Time"), offsets[1]}, {_u8L("Percent"), offsets[2]}, {_u8L("Used filament"), offsets[3]}, {"", offsets[4]}, {_u8L("Display"), offsets[5]}}); - break; - } - case EViewType::Height: { imgui.title(_u8L("Layer Height (mm)")); break; } - case EViewType::Width: { imgui.title(_u8L("Line Width (mm)")); break; } - case EViewType::Feedrate: - { - imgui.title(_u8L("Speed (mm/s)")); - break; - } - - case EViewType::FanSpeed: { imgui.title(_u8L("Fan Speed (%)")); break; } - case EViewType::Temperature: { imgui.title(_u8L("Temperature (°C)")); break; } - case EViewType::VolumetricRate: { imgui.title(_u8L("Volumetric flow rate (mm³/s)")); break; } - case EViewType::LayerTime: { imgui.title(_u8L("Layer Time (s)")); break; } - - case EViewType::Tool: - { - // calculate used filaments data - for (size_t extruder_id : m_extruder_ids) { - if (m_print_statistics.model_volumes_per_extruder.find(extruder_id) == m_print_statistics.model_volumes_per_extruder.end()) - continue; - double volume = m_print_statistics.model_volumes_per_extruder.at(extruder_id); - - auto [model_used_filament_m, model_used_filament_g] = get_used_filament_from_volume(volume, extruder_id); - model_used_filaments_m.push_back(model_used_filament_m); - model_used_filaments_g.push_back(model_used_filament_g); - } - - offsets = calculate_offsets({ { "Extruder NNN", {""}}}, icon_size); - append_headers({ {_u8L("Filament"), offsets[0]}, {_u8L("Used filament"), offsets[1]} }); - break; - } - case EViewType::ColorPrint: - { - std::vector total_filaments; - char buffer[64]; - ::sprintf(buffer, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", ps.total_used_filament / /*1000*/koef, ps.total_weight / unit_conver); - total_filaments.push_back(buffer); - - - std::vector>> title_columns; - if (displayed_columns & ColumnData::Model) { - title_columns.push_back({ _u8L("Filament"), {""} }); - title_columns.push_back({ _u8L("Model"), total_filaments }); - } - if (displayed_columns & ColumnData::Support) { - title_columns.push_back({ _u8L("Support"), total_filaments }); - } - if (displayed_columns & ColumnData::Flushed) { - title_columns.push_back({ _u8L("Flushed"), total_filaments }); - } - if (displayed_columns & ColumnData::WipeTower) { - title_columns.push_back({ _u8L("Tower"), total_filaments }); - } - if ((displayed_columns & ~ColumnData::Model) > 0) { - title_columns.push_back({ _u8L("Total"), total_filaments }); - } - auto offsets_ = calculate_offsets(title_columns, icon_size); - std::vector> title_offsets; - for (int i = 0; i < offsets_.size(); i++) { - title_offsets.push_back({ title_columns[i].first, offsets_[i] }); - color_print_offsets[title_columns[i].first] = offsets_[i]; - } - append_headers(title_offsets); - - break; - } - default: { break; } - } - auto append_option_item = [this, append_item](EMoveType type, std::vector offsets) { auto append_option_item_with_type = [this, offsets, append_item](EMoveType type, const Color& color, const std::string& label, bool visible) { - append_item(EItemType::Rect, color, {{ label , offsets[0] }}, true, visible, [this, type, visible]() { + append_item(EItemType::Rect, color, { { label , offsets[0] } }, true, visible, [this, type, visible]() { m_buffers[buffer_id(type)].visible = !m_buffers[buffer_id(type)].visible; // update buffers' render paths refresh_render_paths(false, false); update_moves_slider(); wxGetApp().plater()->get_current_canvas3D()->set_as_dirty(); }); - }; + }; const bool visible = m_buffers[buffer_id(type)].visible; if (type == EMoveType::Travel) { //BBS: only display travel time in FeatureType view @@ -5147,544 +5007,797 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv append_option_item_with_type(type, Options_Colors[(int)EOptionsColors::ToolChanges], _u8L("Filament Changes"), visible); else if (type == EMoveType::Wipe) append_option_item_with_type(type, Wipe_Color, _u8L("Wipe"), visible); - }; + }; - // extrusion paths section -> items - switch (m_view_type) - { - case EViewType::FeatureType: - { - for (size_t i = 0; i < m_roles.size(); ++i) { - ExtrusionRole role = m_roles[i]; - if (role >= erCount) - continue; - const bool visible = is_visible(role); - std::vector> columns_offsets; - columns_offsets.push_back({ labels[i], offsets[0] }); - columns_offsets.push_back({ times[i], offsets[1] }); - columns_offsets.push_back({ percents[i], offsets[2] }); - columns_offsets.push_back({ used_filaments_m[i], offsets[3] }); - columns_offsets.push_back({ used_filaments_g[i], offsets[4] }); - append_item(EItemType::Rect, Extrusion_Role_Colors[static_cast(role)], columns_offsets, - true, visible, [this, role, visible]() { - m_extrusions.role_visibility_flags = visible ? m_extrusions.role_visibility_flags & ~(1 << role) : m_extrusions.role_visibility_flags | (1 << role); + if (!m_fold) { + //ImGui::Dummy(ImVec2(0.0f, ImGui::GetFontSize() * 0.1)); + ImGui::Dummy({ window_padding, window_padding }); + ImGui::Spacing(); + ImGui::Dummy({ window_padding, window_padding }); + //ImGui::Spacing(); + //ImGui::Dummy({ window_padding, window_padding }); + //imgui.bold_text(_u8L("Color Scheme")); + ImGui::SameLine(); + imgui.text(_u8L("Color Scheme")); + + push_combo_style(); + ImGui::SameLine(); + const char* view_type_value = view_type_items_str[m_view_type_sel].c_str(); + ImGuiComboFlags flags = 0; + if (ImGui::BBLBeginCombo("", view_type_value, flags)) { + ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 0.0f); + //select controller + for (int i = 0; i < view_type_items_str.size(); i++) { + const bool is_selected = (m_view_type_sel == i); + if (ImGui::BBLSelectable(view_type_items_str[i].c_str(), is_selected)) { + m_fold = false; + m_view_type_sel = i; + set_view_type(view_type_items[m_view_type_sel]); + reset_visible(view_type_items[m_view_type_sel]); // update buffers' render paths refresh_render_paths(false, false); update_moves_slider(); wxGetApp().plater()->get_current_canvas3D()->set_as_dirty(); - }); + } + if (is_selected) { + ImGui::SetItemDefaultFocus(); + } + } + ImGui::PopStyleVar(1); + ImGui::EndCombo(); + } + pop_combo_style(); + //ImGui::Dummy({ window_padding, window_padding }); + ImGui::SameLine(); + ImGui::Dummy({ window_padding, window_padding }); + ImGui::Dummy({ window_padding, window_padding }); + // used filament statistics + for (size_t extruder_id : m_extruder_ids) { + if (m_print_statistics.model_volumes_per_extruder.find(extruder_id) == m_print_statistics.model_volumes_per_extruder.end()) { + model_used_filaments_m.push_back(0.0); + model_used_filaments_g.push_back(0.0); + } + else { + double volume = m_print_statistics.model_volumes_per_extruder.at(extruder_id); + auto [model_used_filament_m, model_used_filament_g] = get_used_filament_from_volume(volume, extruder_id); + model_used_filaments_m.push_back(model_used_filament_m); + model_used_filaments_g.push_back(model_used_filament_g); + total_model_used_filament_m += model_used_filament_m; + total_model_used_filament_g += model_used_filament_g; + displayed_columns |= ColumnData::Model; + } } - for(auto item : options_items) { - if (item != EMoveType::Travel) { - append_option_item(item, offsets); - } else { - //BBS: show travel time in FeatureType view - const bool visible = m_buffers[buffer_id(item)].visible; + for (size_t extruder_id : m_extruder_ids) { + if (m_print_statistics.wipe_tower_volumes_per_extruder.find(extruder_id) == m_print_statistics.wipe_tower_volumes_per_extruder.end()) { + wipe_tower_used_filaments_m.push_back(0.0); + wipe_tower_used_filaments_g.push_back(0.0); + } + else { + double volume = m_print_statistics.wipe_tower_volumes_per_extruder.at(extruder_id); + auto [wipe_tower_used_filament_m, wipe_tower_used_filament_g] = get_used_filament_from_volume(volume, extruder_id); + wipe_tower_used_filaments_m.push_back(wipe_tower_used_filament_m); + wipe_tower_used_filaments_g.push_back(wipe_tower_used_filament_g); + total_wipe_tower_used_filament_m += wipe_tower_used_filament_m; + total_wipe_tower_used_filament_g += wipe_tower_used_filament_g; + displayed_columns |= ColumnData::WipeTower; + } + } + + for (size_t extruder_id : m_extruder_ids) { + if (m_print_statistics.flush_per_filament.find(extruder_id) == m_print_statistics.flush_per_filament.end()) { + flushed_filaments_m.push_back(0.0); + flushed_filaments_g.push_back(0.0); + } + else { + double volume = m_print_statistics.flush_per_filament.at(extruder_id); + auto [flushed_filament_m, flushed_filament_g] = get_used_filament_from_volume(volume, extruder_id); + flushed_filaments_m.push_back(flushed_filament_m); + flushed_filaments_g.push_back(flushed_filament_g); + total_flushed_filament_m += flushed_filament_m; + total_flushed_filament_g += flushed_filament_g; + displayed_columns |= ColumnData::Flushed; + } + } + + for (size_t extruder_id : m_extruder_ids) { + if (m_print_statistics.support_volumes_per_extruder.find(extruder_id) == m_print_statistics.support_volumes_per_extruder.end()) { + support_used_filaments_m.push_back(0.0); + support_used_filaments_g.push_back(0.0); + } + else { + double volume = m_print_statistics.support_volumes_per_extruder.at(extruder_id); + auto [used_filament_m, used_filament_g] = get_used_filament_from_volume(volume, extruder_id); + support_used_filaments_m.push_back(used_filament_m); + support_used_filaments_g.push_back(used_filament_g); + total_support_used_filament_m += used_filament_m; + total_support_used_filament_g += used_filament_g; + displayed_columns |= ColumnData::Support; + } + } + + + // extrusion paths section -> title + ImGui::Dummy({ window_padding, window_padding }); + ImGui::SameLine(); + switch (m_view_type) + { + case EViewType::FeatureType: + { + // calculate offsets to align time/percentage data + char buffer[64]; + for (size_t i = 0; i < m_roles.size(); ++i) { + ExtrusionRole role = m_roles[i]; + if (role < erCount) { + labels.push_back(_u8L(ExtrusionEntity::role_to_string(role))); + auto [time, percent] = role_time_and_percent(role); + times.push_back((time > 0.0f) ? short_time(get_time_dhms(time)) : ""); + if (percent == 0) + ::sprintf(buffer, "0%%"); + else + percent > 0.001 ? ::sprintf(buffer, "%.1f%%", percent * 100) : ::sprintf(buffer, "<0.1%%"); + percents.push_back(buffer); + auto [model_used_filament_m, model_used_filament_g] = used_filament_per_role(role); + //model_used_filaments_m.push_back(model_used_filament_m); + //model_used_filaments_g.push_back(model_used_filament_g); + memset(&buffer, 0, sizeof(buffer)); + ::sprintf(buffer, imperial_units ? "%.2f in" : "%.2f m", model_used_filament_m); + used_filaments_m.push_back(buffer); + + memset(&buffer, 0, sizeof(buffer)); + ::sprintf(buffer, "%.2f g", model_used_filament_g); + used_filaments_g.push_back(buffer); + } + } + + //BBS: get travel time and percent + { + auto [time, percent] = move_time_and_percent(EMoveType::Travel); + travel_time = (time > 0.0f) ? short_time(get_time_dhms(time)) : ""; + if (percent == 0) + ::sprintf(buffer, "0%%"); + else + percent > 0.001 ? ::sprintf(buffer, "%.1f%%", percent * 100) : ::sprintf(buffer, "<0.1%%"); + travel_percent = buffer; + } + + offsets = calculate_offsets({ {_u8L("Line Type"), labels}, {_u8L("Time"), times}, {_u8L("Percent"), percents}, {_u8L("Used filament"), used_filaments_m}, {"", used_filaments_g}, {_u8L("Display"), {""}} }, icon_size); + append_headers({ {_u8L("Line Type"), offsets[0]}, {_u8L("Time"), offsets[1]}, {_u8L("Percent"), offsets[2]}, {_u8L("Used filament"), offsets[3]}, {"", offsets[4]}, {_u8L("Display"), offsets[5]} }); + break; + } + case EViewType::Height: { imgui.title(_u8L("Layer Height (mm)")); break; } + case EViewType::Width: { imgui.title(_u8L("Line Width (mm)")); break; } + case EViewType::Feedrate: + { + imgui.title(_u8L("Speed (mm/s)")); + break; + } + + case EViewType::FanSpeed: { imgui.title(_u8L("Fan Speed (%)")); break; } + case EViewType::Temperature: { imgui.title(_u8L("Temperature (°C)")); break; } + case EViewType::VolumetricRate: { imgui.title(_u8L("Volumetric flow rate (mm³/s)")); break; } + case EViewType::LayerTime: { imgui.title(_u8L("Layer Time (s)")); break; } + + case EViewType::Tool: + { + // calculate used filaments data + for (size_t extruder_id : m_extruder_ids) { + if (m_print_statistics.model_volumes_per_extruder.find(extruder_id) == m_print_statistics.model_volumes_per_extruder.end()) + continue; + double volume = m_print_statistics.model_volumes_per_extruder.at(extruder_id); + + auto [model_used_filament_m, model_used_filament_g] = get_used_filament_from_volume(volume, extruder_id); + model_used_filaments_m.push_back(model_used_filament_m); + model_used_filaments_g.push_back(model_used_filament_g); + } + + offsets = calculate_offsets({ { "Extruder NNN", {""}} }, icon_size); + append_headers({ {_u8L("Filament"), offsets[0]}, {_u8L("Used filament"), offsets[1]} }); + break; + } + case EViewType::ColorPrint: + { + std::vector total_filaments; + char buffer[64]; + ::sprintf(buffer, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", ps.total_used_filament / /*1000*/koef, ps.total_weight / unit_conver); + total_filaments.push_back(buffer); + + + std::vector>> title_columns; + if (displayed_columns & ColumnData::Model) { + title_columns.push_back({ _u8L("Filament"), {""} }); + title_columns.push_back({ _u8L("Model"), total_filaments }); + } + if (displayed_columns & ColumnData::Support) { + title_columns.push_back({ _u8L("Support"), total_filaments }); + } + if (displayed_columns & ColumnData::Flushed) { + title_columns.push_back({ _u8L("Flushed"), total_filaments }); + } + if (displayed_columns & ColumnData::WipeTower) { + title_columns.push_back({ _u8L("Tower"), total_filaments }); + } + if ((displayed_columns & ~ColumnData::Model) > 0) { + title_columns.push_back({ _u8L("Total"), total_filaments }); + } + auto offsets_ = calculate_offsets(title_columns, icon_size); + std::vector> title_offsets; + for (int i = 0; i < offsets_.size(); i++) { + title_offsets.push_back({ title_columns[i].first, offsets_[i] }); + color_print_offsets[title_columns[i].first] = offsets_[i]; + } + append_headers(title_offsets); + + break; + } + default: { break; } + } + + //auto append_option_item = [this, append_item](EMoveType type, std::vector offsets) { + // auto append_option_item_with_type = [this, offsets, append_item](EMoveType type, const Color& color, const std::string& label, bool visible) { + // append_item(EItemType::Rect, color, { { label , offsets[0] } }, true, visible, [this, type, visible]() { + // m_buffers[buffer_id(type)].visible = !m_buffers[buffer_id(type)].visible; + // // update buffers' render paths + // refresh_render_paths(false, false); + // update_moves_slider(); + // wxGetApp().plater()->get_current_canvas3D()->set_as_dirty(); + // }); + // }; + // const bool visible = m_buffers[buffer_id(type)].visible; + // if (type == EMoveType::Travel) { + // //BBS: only display travel time in FeatureType view + // append_option_item_with_type(type, Travel_Colors[0], _u8L("Travel"), visible); + // } + // else if (type == EMoveType::Seam) + // append_option_item_with_type(type, Options_Colors[(int)EOptionsColors::Seams], _u8L("Seams"), visible); + // else if (type == EMoveType::Retract) + // append_option_item_with_type(type, Options_Colors[(int)EOptionsColors::Retractions], _u8L("Retract"), visible); + // else if (type == EMoveType::Unretract) + // append_option_item_with_type(type, Options_Colors[(int)EOptionsColors::Unretractions], _u8L("Unretract"), visible); + // else if (type == EMoveType::Tool_change) + // append_option_item_with_type(type, Options_Colors[(int)EOptionsColors::ToolChanges], _u8L("Filament Changes"), visible); + // else if (type == EMoveType::Wipe) + // append_option_item_with_type(type, Wipe_Color, _u8L("Wipe"), visible); + // }; + + // extrusion paths section -> items + switch (m_view_type) + { + case EViewType::FeatureType: + { + for (size_t i = 0; i < m_roles.size(); ++i) { + ExtrusionRole role = m_roles[i]; + if (role >= erCount) + continue; + const bool visible = is_visible(role); std::vector> columns_offsets; - columns_offsets.push_back({ _u8L("Travel"), offsets[0] }); - columns_offsets.push_back({ travel_time, offsets[1] }); - columns_offsets.push_back({ travel_percent, offsets[2] }); - append_item(EItemType::Rect, Travel_Colors[0], columns_offsets, true, visible, [this, item, visible]() { + columns_offsets.push_back({ labels[i], offsets[0] }); + columns_offsets.push_back({ times[i], offsets[1] }); + columns_offsets.push_back({ percents[i], offsets[2] }); + columns_offsets.push_back({ used_filaments_m[i], offsets[3] }); + columns_offsets.push_back({ used_filaments_g[i], offsets[4] }); + append_item(EItemType::Rect, Extrusion_Role_Colors[static_cast(role)], columns_offsets, + true, visible, [this, role, visible]() { + m_extrusions.role_visibility_flags = visible ? m_extrusions.role_visibility_flags & ~(1 << role) : m_extrusions.role_visibility_flags | (1 << role); + // update buffers' render paths + refresh_render_paths(false, false); + update_moves_slider(); + wxGetApp().plater()->get_current_canvas3D()->set_as_dirty(); + }); + } + + for (auto item : options_items) { + if (item != EMoveType::Travel) { + append_option_item(item, offsets); + } + else { + //BBS: show travel time in FeatureType view + const bool visible = m_buffers[buffer_id(item)].visible; + std::vector> columns_offsets; + columns_offsets.push_back({ _u8L("Travel"), offsets[0] }); + columns_offsets.push_back({ travel_time, offsets[1] }); + columns_offsets.push_back({ travel_percent, offsets[2] }); + append_item(EItemType::Rect, Travel_Colors[0], columns_offsets, true, visible, [this, item, visible]() { m_buffers[buffer_id(item)].visible = !m_buffers[buffer_id(item)].visible; // update buffers' render paths refresh_render_paths(false, false); update_moves_slider(); wxGetApp().plater()->get_current_canvas3D()->set_as_dirty(); - }); + }); + } } + break; } - break; - } - case EViewType::Height: { append_range(m_extrusions.ranges.height, 2); break; } - case EViewType::Width: { append_range(m_extrusions.ranges.width, 2); break; } - case EViewType::Feedrate: { - append_range(m_extrusions.ranges.feedrate, 0); - ImGui::Spacing(); - ImGui::Dummy({ window_padding, window_padding }); - ImGui::SameLine(); - offsets = calculate_offsets({ { _u8L("Options"), { _u8L("Travel")}}, { _u8L("Display"), {""}} }, icon_size); - append_headers({ {_u8L("Options"), offsets[0] }, { _u8L("Display"), offsets[1]} }); - const bool travel_visible = m_buffers[buffer_id(EMoveType::Travel)].visible; - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 3.0f)); - append_item(EItemType::None, Travel_Colors[0], { {_u8L("travel"), offsets[0] }}, true, travel_visible, [this, travel_visible]() { - m_buffers[buffer_id(EMoveType::Travel)].visible = !m_buffers[buffer_id(EMoveType::Travel)].visible; - // update buffers' render paths, and update m_tools.m_tool_colors and m_extrusions.ranges - refresh(*m_gcode_result, wxGetApp().plater()->get_extruder_colors_from_plater_config(m_gcode_result)); - update_moves_slider(); - wxGetApp().plater()->get_current_canvas3D()->set_as_dirty(); - }); - ImGui::PopStyleVar(1); - break; - } - case EViewType::FanSpeed: { append_range(m_extrusions.ranges.fan_speed, 0); break; } - case EViewType::Temperature: { append_range(m_extrusions.ranges.temperature, 0); break; } - case EViewType::LayerTime: { append_range(m_extrusions.ranges.layer_duration, 1); break; } - case EViewType::VolumetricRate: { append_range(m_extrusions.ranges.volumetric_rate, 2); break; } - case EViewType::Tool: - { - // shows only extruders actually used - char buf[64]; - size_t i = 0; - for (unsigned char extruder_id : m_extruder_ids) { - ::sprintf(buf, imperial_units ? "%.2f in %.2f g" : "%.2f m %.2f g", model_used_filaments_m[i], model_used_filaments_g[i]); - append_item(EItemType::Rect, m_tools.m_tool_colors[extruder_id], { { _u8L("Extruder") + " " + std::to_string(extruder_id + 1), offsets[0]}, {buf, offsets[1]} }); - i++; + case EViewType::Height: { append_range(m_extrusions.ranges.height, 2); break; } + case EViewType::Width: { append_range(m_extrusions.ranges.width, 2); break; } + case EViewType::Feedrate: { + append_range(m_extrusions.ranges.feedrate, 0); + ImGui::Spacing(); + ImGui::Dummy({ window_padding, window_padding }); + ImGui::SameLine(); + offsets = calculate_offsets({ { _u8L("Options"), { _u8L("Travel")}}, { _u8L("Display"), {""}} }, icon_size); + append_headers({ {_u8L("Options"), offsets[0] }, { _u8L("Display"), offsets[1]} }); + const bool travel_visible = m_buffers[buffer_id(EMoveType::Travel)].visible; + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 3.0f)); + append_item(EItemType::None, Travel_Colors[0], { {_u8L("travel"), offsets[0] } }, true, travel_visible, [this, travel_visible]() { + m_buffers[buffer_id(EMoveType::Travel)].visible = !m_buffers[buffer_id(EMoveType::Travel)].visible; + // update buffers' render paths, and update m_tools.m_tool_colors and m_extrusions.ranges + refresh(*m_gcode_result, wxGetApp().plater()->get_extruder_colors_from_plater_config(m_gcode_result)); + update_moves_slider(); + wxGetApp().plater()->get_current_canvas3D()->set_as_dirty(); + }); + ImGui::PopStyleVar(1); + break; } - break; - } - case EViewType::ColorPrint: - { - //BBS: replace model custom gcode with current plate custom gcode - const std::vector& custom_gcode_per_print_z = wxGetApp().is_editor() ? wxGetApp().plater()->model().get_curr_plate_custom_gcodes().gcodes : m_custom_gcode_per_print_z; - size_t total_items = 1; - // BBS: no ColorChange type, use ToolChange - //for (size_t extruder_id : m_extruder_ids) { - // total_items += color_print_ranges(extruder_id, custom_gcode_per_print_z).size(); - //} + case EViewType::FanSpeed: { append_range(m_extrusions.ranges.fan_speed, 0); break; } + case EViewType::Temperature: { append_range(m_extrusions.ranges.temperature, 0); break; } + case EViewType::LayerTime: { append_range(m_extrusions.ranges.layer_duration, 1); break; } + case EViewType::VolumetricRate: { append_range(m_extrusions.ranges.volumetric_rate, 2); break; } + case EViewType::Tool: + { + // shows only extruders actually used + char buf[64]; + size_t i = 0; + for (unsigned char extruder_id : m_extruder_ids) { + ::sprintf(buf, imperial_units ? "%.2f in %.2f g" : "%.2f m %.2f g", model_used_filaments_m[i], model_used_filaments_g[i]); + append_item(EItemType::Rect, m_tools.m_tool_colors[extruder_id], { { _u8L("Extruder") + " " + std::to_string(extruder_id + 1), offsets[0]}, {buf, offsets[1]} }); + i++; + } + break; + } + case EViewType::ColorPrint: + { + //BBS: replace model custom gcode with current plate custom gcode + const std::vector& custom_gcode_per_print_z = wxGetApp().is_editor() ? wxGetApp().plater()->model().get_curr_plate_custom_gcodes().gcodes : m_custom_gcode_per_print_z; + size_t total_items = 1; + // BBS: no ColorChange type, use ToolChange + //for (size_t extruder_id : m_extruder_ids) { + // total_items += color_print_ranges(extruder_id, custom_gcode_per_print_z).size(); + //} - const bool need_scrollable = static_cast(total_items) * (icon_size + ImGui::GetStyle().ItemSpacing.y) > child_height; + const bool need_scrollable = static_cast(total_items) * (icon_size + ImGui::GetStyle().ItemSpacing.y) > child_height; - // add scrollable region, if needed - if (need_scrollable) - ImGui::BeginChild("color_prints", { -1.0f, child_height }, false); + // add scrollable region, if needed + if (need_scrollable) + ImGui::BeginChild("color_prints", { -1.0f, child_height }, false); - // shows only extruders actually used - size_t i = 0; - for (auto extruder_idx : m_extruder_ids) { - const bool filament_visible = m_tools.m_tool_visibles[extruder_idx]; - if (i < model_used_filaments_m.size() && i < model_used_filaments_g.size()) { - std::vector> columns_offsets; - columns_offsets.push_back({ std::to_string(extruder_idx + 1), color_print_offsets[_u8L("Filament")]}); + // shows only extruders actually used + size_t i = 0; + for (auto extruder_idx : m_extruder_ids) { + const bool filament_visible = m_tools.m_tool_visibles[extruder_idx]; + if (i < model_used_filaments_m.size() && i < model_used_filaments_g.size()) { + std::vector> columns_offsets; + columns_offsets.push_back({ std::to_string(extruder_idx + 1), color_print_offsets[_u8L("Filament")] }); - char buf[64]; - float column_sum_m = 0.0f; - float column_sum_g = 0.0f; - if (displayed_columns & ColumnData::Model) { - if ((displayed_columns & ~ColumnData::Model) > 0) - ::sprintf(buf, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", model_used_filaments_m[i], model_used_filaments_g[i] / unit_conver); - else - ::sprintf(buf, imperial_units ? "%.2f in %.2f oz" : "%.2f m %.2f g", model_used_filaments_m[i], model_used_filaments_g[i] / unit_conver); - columns_offsets.push_back({ buf, color_print_offsets[_u8L("Model")] }); - column_sum_m += model_used_filaments_m[i]; - column_sum_g += model_used_filaments_g[i]; - } - if (displayed_columns & ColumnData::Support) { - ::sprintf(buf, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", support_used_filaments_m[i], support_used_filaments_g[i] / unit_conver); - columns_offsets.push_back({ buf, color_print_offsets[_u8L("Support")] }); - column_sum_m += support_used_filaments_m[i]; - column_sum_g += support_used_filaments_g[i]; - } - if (displayed_columns & ColumnData::Flushed) { - ::sprintf(buf, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", flushed_filaments_m[i], flushed_filaments_g[i] / unit_conver); - columns_offsets.push_back({ buf, color_print_offsets[_u8L("Flushed")]}); - column_sum_m += flushed_filaments_m[i]; - column_sum_g += flushed_filaments_g[i]; - } - if (displayed_columns & ColumnData::WipeTower) { - ::sprintf(buf, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", wipe_tower_used_filaments_m[i], wipe_tower_used_filaments_g[i] / unit_conver); - columns_offsets.push_back({ buf, color_print_offsets[_u8L("Tower")] }); - column_sum_m += wipe_tower_used_filaments_m[i]; - column_sum_g += wipe_tower_used_filaments_g[i]; - } - if ((displayed_columns & ~ColumnData::Model) > 0) { - ::sprintf(buf, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", column_sum_m, column_sum_g / unit_conver); - columns_offsets.push_back({ buf, color_print_offsets[_u8L("Total")] }); - } + char buf[64]; + float column_sum_m = 0.0f; + float column_sum_g = 0.0f; + if (displayed_columns & ColumnData::Model) { + if ((displayed_columns & ~ColumnData::Model) > 0) + ::sprintf(buf, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", model_used_filaments_m[i], model_used_filaments_g[i] / unit_conver); + else + ::sprintf(buf, imperial_units ? "%.2f in %.2f oz" : "%.2f m %.2f g", model_used_filaments_m[i], model_used_filaments_g[i] / unit_conver); + columns_offsets.push_back({ buf, color_print_offsets[_u8L("Model")] }); + column_sum_m += model_used_filaments_m[i]; + column_sum_g += model_used_filaments_g[i]; + } + if (displayed_columns & ColumnData::Support) { + ::sprintf(buf, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", support_used_filaments_m[i], support_used_filaments_g[i] / unit_conver); + columns_offsets.push_back({ buf, color_print_offsets[_u8L("Support")] }); + column_sum_m += support_used_filaments_m[i]; + column_sum_g += support_used_filaments_g[i]; + } + if (displayed_columns & ColumnData::Flushed) { + ::sprintf(buf, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", flushed_filaments_m[i], flushed_filaments_g[i] / unit_conver); + columns_offsets.push_back({ buf, color_print_offsets[_u8L("Flushed")] }); + column_sum_m += flushed_filaments_m[i]; + column_sum_g += flushed_filaments_g[i]; + } + if (displayed_columns & ColumnData::WipeTower) { + ::sprintf(buf, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", wipe_tower_used_filaments_m[i], wipe_tower_used_filaments_g[i] / unit_conver); + columns_offsets.push_back({ buf, color_print_offsets[_u8L("Tower")] }); + column_sum_m += wipe_tower_used_filaments_m[i]; + column_sum_g += wipe_tower_used_filaments_g[i]; + } + if ((displayed_columns & ~ColumnData::Model) > 0) { + ::sprintf(buf, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", column_sum_m, column_sum_g / unit_conver); + columns_offsets.push_back({ buf, color_print_offsets[_u8L("Total")] }); + } - append_item(EItemType::Rect, m_tools.m_tool_colors[extruder_idx], columns_offsets, false, filament_visible, [this, extruder_idx]() { + append_item(EItemType::Rect, m_tools.m_tool_colors[extruder_idx], columns_offsets, false, filament_visible, [this, extruder_idx]() { m_tools.m_tool_visibles[extruder_idx] = !m_tools.m_tool_visibles[extruder_idx]; // update buffers' render paths refresh_render_paths(false, false); update_moves_slider(); wxGetApp().plater()->get_current_canvas3D()->set_as_dirty(); - }); - } - i++; - } - - if (need_scrollable) - ImGui::EndChild(); - - // Sum of all rows - char buf[64]; - if (m_extruder_ids.size() > 1) { - // Separator - ImGuiWindow* window = ImGui::GetCurrentWindow(); - const ImRect separator(ImVec2(window->Pos.x + window_padding * 3, window->DC.CursorPos.y), ImVec2(window->Pos.x + window->Size.x - window_padding * 3, window->DC.CursorPos.y + 1.0f)); - ImGui::ItemSize(ImVec2(0.0f, 0.0f)); - const bool item_visible = ImGui::ItemAdd(separator, 0); - window->DrawList->AddLine(separator.Min, ImVec2(separator.Max.x, separator.Min.y), ImGui::GetColorU32(ImGuiCol_Separator)); - - std::vector> columns_offsets; - columns_offsets.push_back({ _u8L("Total"), color_print_offsets[_u8L("Filament")]}); - if (displayed_columns & ColumnData::Model) { - if ((displayed_columns & ~ColumnData::Model) > 0) - ::sprintf(buf, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", total_model_used_filament_m, total_model_used_filament_g / unit_conver); - else - ::sprintf(buf, imperial_units ? "%.2f in %.2f oz" : "%.2f m %.2f g", total_model_used_filament_m, total_model_used_filament_g / unit_conver); - columns_offsets.push_back({ buf, color_print_offsets[_u8L("Model")] }); - } - if (displayed_columns & ColumnData::Support) { - ::sprintf(buf, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", total_support_used_filament_m, total_support_used_filament_g / unit_conver); - columns_offsets.push_back({ buf, color_print_offsets[_u8L("Support")] }); - } - if (displayed_columns & ColumnData::Flushed) { - ::sprintf(buf, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", total_flushed_filament_m, total_flushed_filament_g / unit_conver); - columns_offsets.push_back({ buf, color_print_offsets[_u8L("Flushed")] }); - } - if (displayed_columns & ColumnData::WipeTower) { - ::sprintf(buf, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", total_wipe_tower_used_filament_m, total_wipe_tower_used_filament_g / unit_conver); - columns_offsets.push_back({ buf, color_print_offsets[_u8L("Tower")] }); - } - if ((displayed_columns & ~ColumnData::Model) > 0) { - ::sprintf(buf, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", total_model_used_filament_m + total_support_used_filament_m + total_flushed_filament_m + total_wipe_tower_used_filament_m, - (total_model_used_filament_g + total_support_used_filament_g + total_flushed_filament_g + total_wipe_tower_used_filament_g) / unit_conver); - columns_offsets.push_back({ buf, color_print_offsets[_u8L("Total")] }); - } - append_item(EItemType::None, m_tools.m_tool_colors[0], columns_offsets); - } - - //BBS display filament change times - ImGui::Dummy({window_padding, window_padding}); - ImGui::SameLine(); - imgui.text(_u8L("Filament change times") + ":"); - ImGui::SameLine(); - ::sprintf(buf, "%d", m_print_statistics.total_filamentchanges); - imgui.text(buf); - - //BBS display cost - ImGui::Dummy({ window_padding, window_padding }); - ImGui::SameLine(); - imgui.text(_u8L("Cost")+":"); - ImGui::SameLine(); - ::sprintf(buf, "%.2f", ps.total_cost); - imgui.text(buf); - - break; - } - default: { break; } - } - - // partial estimated printing time section - if (m_view_type == EViewType::ColorPrint) { - using Times = std::pair; - using TimesList = std::vector>; - - // helper structure containig the data needed to render the time items - struct PartialTime - { - enum class EType : unsigned char - { - Print, - ColorChange, - Pause - }; - EType type; - int extruder_id; - Color color1; - Color color2; - Times times; - std::pair used_filament {0.0f, 0.0f}; - }; - using PartialTimes = std::vector; - - auto generate_partial_times = [this, get_used_filament_from_volume](const TimesList& times, const std::vector& used_filaments) { - PartialTimes items; - - //BBS: replace model custom gcode with current plate custom gcode - std::vector custom_gcode_per_print_z = wxGetApp().is_editor() ? wxGetApp().plater()->model().get_curr_plate_custom_gcodes().gcodes : m_custom_gcode_per_print_z; - std::vector last_color(m_extruders_count); - for (size_t i = 0; i < m_extruders_count; ++i) { - last_color[i] = m_tools.m_tool_colors[i]; - } - int last_extruder_id = 1; - int color_change_idx = 0; - for (const auto& time_rec : times) { - switch (time_rec.first) - { - case CustomGCode::PausePrint: { - auto it = std::find_if(custom_gcode_per_print_z.begin(), custom_gcode_per_print_z.end(), [time_rec](const CustomGCode::Item& item) { return item.type == time_rec.first; }); - if (it != custom_gcode_per_print_z.end()) { - items.push_back({ PartialTime::EType::Print, it->extruder, last_color[it->extruder - 1], Color(), time_rec.second }); - items.push_back({ PartialTime::EType::Pause, it->extruder, Color(), Color(), time_rec.second }); - custom_gcode_per_print_z.erase(it); - } - break; + }); } - case CustomGCode::ColorChange: { - auto it = std::find_if(custom_gcode_per_print_z.begin(), custom_gcode_per_print_z.end(), [time_rec](const CustomGCode::Item& item) { return item.type == time_rec.first; }); - if (it != custom_gcode_per_print_z.end()) { - items.push_back({ PartialTime::EType::Print, it->extruder, last_color[it->extruder - 1], Color(), time_rec.second, get_used_filament_from_volume(used_filaments[color_change_idx++], it->extruder-1) }); - items.push_back({ PartialTime::EType::ColorChange, it->extruder, last_color[it->extruder - 1], decode_color(it->color), time_rec.second }); - last_color[it->extruder - 1] = decode_color(it->color); - last_extruder_id = it->extruder; - custom_gcode_per_print_z.erase(it); - } + i++; + } + + if (need_scrollable) + ImGui::EndChild(); + + // Sum of all rows + char buf[64]; + if (m_extruder_ids.size() > 1) { + // Separator + ImGuiWindow* window = ImGui::GetCurrentWindow(); + const ImRect separator(ImVec2(window->Pos.x + window_padding * 3, window->DC.CursorPos.y), ImVec2(window->Pos.x + window->Size.x - window_padding * 3, window->DC.CursorPos.y + 1.0f)); + ImGui::ItemSize(ImVec2(0.0f, 0.0f)); + const bool item_visible = ImGui::ItemAdd(separator, 0); + window->DrawList->AddLine(separator.Min, ImVec2(separator.Max.x, separator.Min.y), ImGui::GetColorU32(ImGuiCol_Separator)); + + std::vector> columns_offsets; + columns_offsets.push_back({ _u8L("Total"), color_print_offsets[_u8L("Filament")] }); + if (displayed_columns & ColumnData::Model) { + if ((displayed_columns & ~ColumnData::Model) > 0) + ::sprintf(buf, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", total_model_used_filament_m, total_model_used_filament_g / unit_conver); else - items.push_back({ PartialTime::EType::Print, last_extruder_id, last_color[last_extruder_id - 1], Color(), time_rec.second, get_used_filament_from_volume(used_filaments[color_change_idx++], last_extruder_id -1) }); - - break; + ::sprintf(buf, imperial_units ? "%.2f in %.2f oz" : "%.2f m %.2f g", total_model_used_filament_m, total_model_used_filament_g / unit_conver); + columns_offsets.push_back({ buf, color_print_offsets[_u8L("Model")] }); } - default: { break; } + if (displayed_columns & ColumnData::Support) { + ::sprintf(buf, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", total_support_used_filament_m, total_support_used_filament_g / unit_conver); + columns_offsets.push_back({ buf, color_print_offsets[_u8L("Support")] }); } + if (displayed_columns & ColumnData::Flushed) { + ::sprintf(buf, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", total_flushed_filament_m, total_flushed_filament_g / unit_conver); + columns_offsets.push_back({ buf, color_print_offsets[_u8L("Flushed")] }); + } + if (displayed_columns & ColumnData::WipeTower) { + ::sprintf(buf, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", total_wipe_tower_used_filament_m, total_wipe_tower_used_filament_g / unit_conver); + columns_offsets.push_back({ buf, color_print_offsets[_u8L("Tower")] }); + } + if ((displayed_columns & ~ColumnData::Model) > 0) { + ::sprintf(buf, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", total_model_used_filament_m + total_support_used_filament_m + total_flushed_filament_m + total_wipe_tower_used_filament_m, + (total_model_used_filament_g + total_support_used_filament_g + total_flushed_filament_g + total_wipe_tower_used_filament_g) / unit_conver); + columns_offsets.push_back({ buf, color_print_offsets[_u8L("Total")] }); + } + append_item(EItemType::None, m_tools.m_tool_colors[0], columns_offsets); } - return items; - }; - - auto append_color_change = [&imgui](const Color& color1, const Color& color2, const std::array& offsets, const Times& times) { - imgui.text(_u8L("Color change")); + //BBS display filament change times + ImGui::Dummy({ window_padding, window_padding }); ImGui::SameLine(); - - float icon_size = ImGui::GetTextLineHeight(); - ImDrawList* draw_list = ImGui::GetWindowDrawList(); - ImVec2 pos = ImGui::GetCursorScreenPos(); - pos.x -= 0.5f * ImGui::GetStyle().ItemSpacing.x; - - draw_list->AddRectFilled({ pos.x + 1.0f, pos.y + 1.0f }, { pos.x + icon_size - 1.0f, pos.y + icon_size - 1.0f }, - ImGui::GetColorU32({ color1[0], color1[1], color1[2], 1.0f })); - pos.x += icon_size; - draw_list->AddRectFilled({ pos.x + 1.0f, pos.y + 1.0f }, { pos.x + icon_size - 1.0f, pos.y + icon_size - 1.0f }, - ImGui::GetColorU32({ color2[0], color2[1], color2[2], 1.0f })); - - ImGui::SameLine(offsets[0]); - imgui.text(short_time(get_time_dhms(times.second - times.first))); - }; - - auto append_print = [&imgui, imperial_units](const Color& color, const std::array& offsets, const Times& times, std::pair used_filament) { - imgui.text(_u8L("Print")); + imgui.text(_u8L("Filament change times") + ":"); ImGui::SameLine(); + ::sprintf(buf, "%d", m_print_statistics.total_filamentchanges); + imgui.text(buf); - float icon_size = ImGui::GetTextLineHeight(); - ImDrawList* draw_list = ImGui::GetWindowDrawList(); - ImVec2 pos = ImGui::GetCursorScreenPos(); - pos.x -= 0.5f * ImGui::GetStyle().ItemSpacing.x; + //BBS display cost + ImGui::Dummy({ window_padding, window_padding }); + ImGui::SameLine(); + imgui.text(_u8L("Cost") + ":"); + ImGui::SameLine(); + ::sprintf(buf, "%.2f", ps.total_cost); + imgui.text(buf); - draw_list->AddRectFilled({ pos.x + 1.0f, pos.y + 1.0f }, { pos.x + icon_size - 1.0f, pos.y + icon_size - 1.0f }, - ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f })); + break; + } + default: { break; } + } - ImGui::SameLine(offsets[0]); - imgui.text(short_time(get_time_dhms(times.second))); - ImGui::SameLine(offsets[1]); - imgui.text(short_time(get_time_dhms(times.first))); - if (used_filament.first > 0.0f) { - char buffer[64]; - ImGui::SameLine(offsets[2]); - ::sprintf(buffer, imperial_units ? "%.2f in" : "%.2f m", used_filament.first); - imgui.text(buffer); + // partial estimated printing time section + if (m_view_type == EViewType::ColorPrint) { + using Times = std::pair; + using TimesList = std::vector>; - ImGui::SameLine(offsets[3]); - ::sprintf(buffer, "%.2f g", used_filament.second); - imgui.text(buffer); - } - }; - - PartialTimes partial_times = generate_partial_times(time_mode.custom_gcode_times, m_print_statistics.volumes_per_color_change); - if (!partial_times.empty()) { - labels.clear(); - times.clear(); - - for (const PartialTime& item : partial_times) { - switch (item.type) + // helper structure containig the data needed to render the time items + struct PartialTime + { + enum class EType : unsigned char { - case PartialTime::EType::Print: { labels.push_back(_u8L("Print")); break; } - case PartialTime::EType::Pause: { labels.push_back(_u8L("Pause")); break; } - case PartialTime::EType::ColorChange: { labels.push_back(_u8L("Color change")); break; } - } - times.push_back(short_time(get_time_dhms(item.times.second))); - } + Print, + ColorChange, + Pause + }; + EType type; + int extruder_id; + Color color1; + Color color2; + Times times; + std::pair used_filament{ 0.0f, 0.0f }; + }; + using PartialTimes = std::vector; - std::string longest_used_filament_string; - for (const PartialTime& item : partial_times) { - if (item.used_filament.first > 0.0f) { + auto generate_partial_times = [this, get_used_filament_from_volume](const TimesList& times, const std::vector& used_filaments) { + PartialTimes items; + + //BBS: replace model custom gcode with current plate custom gcode + std::vector custom_gcode_per_print_z = wxGetApp().is_editor() ? wxGetApp().plater()->model().get_curr_plate_custom_gcodes().gcodes : m_custom_gcode_per_print_z; + std::vector last_color(m_extruders_count); + for (size_t i = 0; i < m_extruders_count; ++i) { + last_color[i] = m_tools.m_tool_colors[i]; + } + int last_extruder_id = 1; + int color_change_idx = 0; + for (const auto& time_rec : times) { + switch (time_rec.first) + { + case CustomGCode::PausePrint: { + auto it = std::find_if(custom_gcode_per_print_z.begin(), custom_gcode_per_print_z.end(), [time_rec](const CustomGCode::Item& item) { return item.type == time_rec.first; }); + if (it != custom_gcode_per_print_z.end()) { + items.push_back({ PartialTime::EType::Print, it->extruder, last_color[it->extruder - 1], Color(), time_rec.second }); + items.push_back({ PartialTime::EType::Pause, it->extruder, Color(), Color(), time_rec.second }); + custom_gcode_per_print_z.erase(it); + } + break; + } + case CustomGCode::ColorChange: { + auto it = std::find_if(custom_gcode_per_print_z.begin(), custom_gcode_per_print_z.end(), [time_rec](const CustomGCode::Item& item) { return item.type == time_rec.first; }); + if (it != custom_gcode_per_print_z.end()) { + items.push_back({ PartialTime::EType::Print, it->extruder, last_color[it->extruder - 1], Color(), time_rec.second, get_used_filament_from_volume(used_filaments[color_change_idx++], it->extruder - 1) }); + items.push_back({ PartialTime::EType::ColorChange, it->extruder, last_color[it->extruder - 1], decode_color(it->color), time_rec.second }); + last_color[it->extruder - 1] = decode_color(it->color); + last_extruder_id = it->extruder; + custom_gcode_per_print_z.erase(it); + } + else + items.push_back({ PartialTime::EType::Print, last_extruder_id, last_color[last_extruder_id - 1], Color(), time_rec.second, get_used_filament_from_volume(used_filaments[color_change_idx++], last_extruder_id - 1) }); + + break; + } + default: { break; } + } + } + + return items; + }; + + auto append_color_change = [&imgui](const Color& color1, const Color& color2, const std::array& offsets, const Times& times) { + imgui.text(_u8L("Color change")); + ImGui::SameLine(); + + float icon_size = ImGui::GetTextLineHeight(); + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + ImVec2 pos = ImGui::GetCursorScreenPos(); + pos.x -= 0.5f * ImGui::GetStyle().ItemSpacing.x; + + draw_list->AddRectFilled({ pos.x + 1.0f, pos.y + 1.0f }, { pos.x + icon_size - 1.0f, pos.y + icon_size - 1.0f }, + ImGui::GetColorU32({ color1[0], color1[1], color1[2], 1.0f })); + pos.x += icon_size; + draw_list->AddRectFilled({ pos.x + 1.0f, pos.y + 1.0f }, { pos.x + icon_size - 1.0f, pos.y + icon_size - 1.0f }, + ImGui::GetColorU32({ color2[0], color2[1], color2[2], 1.0f })); + + ImGui::SameLine(offsets[0]); + imgui.text(short_time(get_time_dhms(times.second - times.first))); + }; + + auto append_print = [&imgui, imperial_units](const Color& color, const std::array& offsets, const Times& times, std::pair used_filament) { + imgui.text(_u8L("Print")); + ImGui::SameLine(); + + float icon_size = ImGui::GetTextLineHeight(); + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + ImVec2 pos = ImGui::GetCursorScreenPos(); + pos.x -= 0.5f * ImGui::GetStyle().ItemSpacing.x; + + draw_list->AddRectFilled({ pos.x + 1.0f, pos.y + 1.0f }, { pos.x + icon_size - 1.0f, pos.y + icon_size - 1.0f }, + ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f })); + + ImGui::SameLine(offsets[0]); + imgui.text(short_time(get_time_dhms(times.second))); + ImGui::SameLine(offsets[1]); + imgui.text(short_time(get_time_dhms(times.first))); + if (used_filament.first > 0.0f) { char buffer[64]; - ::sprintf(buffer, imperial_units ? "%.2f in" : "%.2f m", item.used_filament.first); - if (::strlen(buffer) > longest_used_filament_string.length()) - longest_used_filament_string = buffer; + ImGui::SameLine(offsets[2]); + ::sprintf(buffer, imperial_units ? "%.2f in" : "%.2f m", used_filament.first); + imgui.text(buffer); + + ImGui::SameLine(offsets[3]); + ::sprintf(buffer, "%.2f g", used_filament.second); + imgui.text(buffer); } + }; + + PartialTimes partial_times = generate_partial_times(time_mode.custom_gcode_times, m_print_statistics.volumes_per_color_change); + if (!partial_times.empty()) { + labels.clear(); + times.clear(); + + for (const PartialTime& item : partial_times) { + switch (item.type) + { + case PartialTime::EType::Print: { labels.push_back(_u8L("Print")); break; } + case PartialTime::EType::Pause: { labels.push_back(_u8L("Pause")); break; } + case PartialTime::EType::ColorChange: { labels.push_back(_u8L("Color change")); break; } + } + times.push_back(short_time(get_time_dhms(item.times.second))); + } + + std::string longest_used_filament_string; + for (const PartialTime& item : partial_times) { + if (item.used_filament.first > 0.0f) { + char buffer[64]; + ::sprintf(buffer, imperial_units ? "%.2f in" : "%.2f m", item.used_filament.first); + if (::strlen(buffer) > longest_used_filament_string.length()) + longest_used_filament_string = buffer; + } + } + + //offsets = calculate_offsets(labels, times, { _u8L("Event"), _u8L("Remaining time"), _u8L("Duration"), longest_used_filament_string }, 2.0f * icon_size); + + //ImGui::Spacing(); + //append_headers({ _u8L("Event"), _u8L("Remaining time"), _u8L("Duration"), _u8L("Used filament") }, offsets); + //const bool need_scrollable = static_cast(partial_times.size()) * (icon_size + ImGui::GetStyle().ItemSpacing.y) > child_height; + //if (need_scrollable) + // // add scrollable region + // ImGui::BeginChild("events", { -1.0f, child_height }, false); + + //for (const PartialTime& item : partial_times) { + // switch (item.type) + // { + // case PartialTime::EType::Print: { + // append_print(item.color1, offsets, item.times, item.used_filament); + // break; + // } + // case PartialTime::EType::Pause: { + // imgui.text(_u8L("Pause")); + // ImGui::SameLine(offsets[0]); + // imgui.text(short_time(get_time_dhms(item.times.second - item.times.first))); + // break; + // } + // case PartialTime::EType::ColorChange: { + // append_color_change(item.color1, item.color2, offsets, item.times); + // break; + // } + // } + //} + + //if (need_scrollable) + // ImGui::EndChild(); } - - //offsets = calculate_offsets(labels, times, { _u8L("Event"), _u8L("Remaining time"), _u8L("Duration"), longest_used_filament_string }, 2.0f * icon_size); - - //ImGui::Spacing(); - //append_headers({ _u8L("Event"), _u8L("Remaining time"), _u8L("Duration"), _u8L("Used filament") }, offsets); - //const bool need_scrollable = static_cast(partial_times.size()) * (icon_size + ImGui::GetStyle().ItemSpacing.y) > child_height; - //if (need_scrollable) - // // add scrollable region - // ImGui::BeginChild("events", { -1.0f, child_height }, false); - - //for (const PartialTime& item : partial_times) { - // switch (item.type) - // { - // case PartialTime::EType::Print: { - // append_print(item.color1, offsets, item.times, item.used_filament); - // break; - // } - // case PartialTime::EType::Pause: { - // imgui.text(_u8L("Pause")); - // ImGui::SameLine(offsets[0]); - // imgui.text(short_time(get_time_dhms(item.times.second - item.times.first))); - // break; - // } - // case PartialTime::EType::ColorChange: { - // append_color_change(item.color1, item.color2, offsets, item.times); - // break; - // } - // } - //} - - //if (need_scrollable) - // ImGui::EndChild(); } - } - // travel paths section - if (m_buffers[buffer_id(EMoveType::Travel)].visible) { - switch (m_view_type) - { - case EViewType::Feedrate: - case EViewType::Tool: - case EViewType::ColorPrint: { - break; + // travel paths section + if (m_buffers[buffer_id(EMoveType::Travel)].visible) { + switch (m_view_type) + { + case EViewType::Feedrate: + case EViewType::Tool: + case EViewType::ColorPrint: { + break; + } + default: { + // BBS GUI:refactor + // title + //ImGui::Spacing(); + //imgui.title(_u8L("Travel")); + //// items + //append_item(EItemType::Line, Travel_Colors[0], _u8L("Movement")); + //append_item(EItemType::Line, Travel_Colors[1], _u8L("Extrusion")); + //append_item(EItemType::Line, Travel_Colors[2], _u8L("Retraction")); + + break; + } + } } - default: { - // BBS GUI:refactor - // title - //ImGui::Spacing(); - //imgui.title(_u8L("Travel")); - //// items - //append_item(EItemType::Line, Travel_Colors[0], _u8L("Movement")); - //append_item(EItemType::Line, Travel_Colors[1], _u8L("Extrusion")); - //append_item(EItemType::Line, Travel_Colors[2], _u8L("Retraction")); - break; + // wipe paths section + //if (m_buffers[buffer_id(EMoveType::Wipe)].visible) { + // switch (m_view_type) + // { + // case EViewType::Feedrate: + // case EViewType::Tool: + // case EViewType::ColorPrint: { break; } + // default: { + // // title + // ImGui::Spacing(); + // ImGui::Dummy({ window_padding, window_padding }); + // ImGui::SameLine(); + // imgui.title(_u8L("Wipe")); + + // // items + // append_item(EItemType::Line, Wipe_Color, { {_u8L("Wipe"), 0} }); + + // break; + // } + // } + //} + + auto any_option_available = [this]() { + auto available = [this](EMoveType type) { + const TBuffer& buffer = m_buffers[buffer_id(type)]; + return buffer.visible && buffer.has_data(); + }; + + return available(EMoveType::Color_change) || + available(EMoveType::Custom_GCode) || + available(EMoveType::Pause_Print) || + available(EMoveType::Retract) || + available(EMoveType::Tool_change) || + available(EMoveType::Unretract) || + available(EMoveType::Seam); + }; + + //auto add_option = [this, append_item](EMoveType move_type, EOptionsColors color, const std::string& text) { + // const TBuffer& buffer = m_buffers[buffer_id(move_type)]; + // if (buffer.visible && buffer.has_data()) + // append_item(EItemType::Circle, Options_Colors[static_cast(color)], text); + //}; + + /* BBS GUI refactor */ + // options section + //if (any_option_available()) { + // // title + // ImGui::Spacing(); + // imgui.title(_u8L("Options")); + + // // items + // add_option(EMoveType::Retract, EOptionsColors::Retractions, _u8L("Retractions")); + // add_option(EMoveType::Unretract, EOptionsColors::Unretractions, _u8L("Deretractions")); + // add_option(EMoveType::Seam, EOptionsColors::Seams, _u8L("Seams")); + // add_option(EMoveType::Tool_change, EOptionsColors::ToolChanges, _u8L("Tool changes")); + // add_option(EMoveType::Color_change, EOptionsColors::ColorChanges, _u8L("Color changes")); + // add_option(EMoveType::Pause_Print, EOptionsColors::PausePrints, _u8L("Print pauses")); + // add_option(EMoveType::Custom_GCode, EOptionsColors::CustomGCodes, _u8L("Custom G-codes")); + //} + + + // settings section + bool has_settings = false; + has_settings |= !m_settings_ids.print.empty(); + has_settings |= !m_settings_ids.printer.empty(); + bool has_filament_settings = true; + has_filament_settings &= !m_settings_ids.filament.empty(); + for (const std::string& fs : m_settings_ids.filament) { + has_filament_settings &= !fs.empty(); } - } - } + has_settings |= has_filament_settings; + //BBS: add only gcode mode + bool show_settings = m_only_gcode_in_preview; //wxGetApp().is_gcode_viewer(); + show_settings &= (m_view_type == EViewType::FeatureType || m_view_type == EViewType::Tool); + show_settings &= has_settings; + if (show_settings) { + auto calc_offset = [this]() { + float ret = 0.0f; + if (!m_settings_ids.printer.empty()) + ret = std::max(ret, ImGui::CalcTextSize((_u8L("Printer") + std::string(":")).c_str()).x); + if (!m_settings_ids.print.empty()) + ret = std::max(ret, ImGui::CalcTextSize((_u8L("Print settings") + std::string(":")).c_str()).x); + if (!m_settings_ids.filament.empty()) { + for (unsigned char i : m_extruder_ids) { + ret = std::max(ret, ImGui::CalcTextSize((_u8L("Filament") + " " + std::to_string(i + 1) + ":").c_str()).x); + } + } + if (ret > 0.0f) + ret += 2.0f * ImGui::GetStyle().ItemSpacing.x; + return ret; + }; - // wipe paths section - //if (m_buffers[buffer_id(EMoveType::Wipe)].visible) { - // switch (m_view_type) - // { - // case EViewType::Feedrate: - // case EViewType::Tool: - // case EViewType::ColorPrint: { break; } - // default: { - // // title - // ImGui::Spacing(); - // ImGui::Dummy({ window_padding, window_padding }); - // ImGui::SameLine(); - // imgui.title(_u8L("Wipe")); + ImGui::Spacing(); + imgui.title(_u8L("Settings")); - // // items - // append_item(EItemType::Line, Wipe_Color, { {_u8L("Wipe"), 0} }); + float offset = calc_offset(); - // break; - // } - // } - //} - - auto any_option_available = [this]() { - auto available = [this](EMoveType type) { - const TBuffer& buffer = m_buffers[buffer_id(type)]; - return buffer.visible && buffer.has_data(); - }; - - return available(EMoveType::Color_change) || - available(EMoveType::Custom_GCode) || - available(EMoveType::Pause_Print) || - available(EMoveType::Retract) || - available(EMoveType::Tool_change) || - available(EMoveType::Unretract) || - available(EMoveType::Seam); - }; - - //auto add_option = [this, append_item](EMoveType move_type, EOptionsColors color, const std::string& text) { - // const TBuffer& buffer = m_buffers[buffer_id(move_type)]; - // if (buffer.visible && buffer.has_data()) - // append_item(EItemType::Circle, Options_Colors[static_cast(color)], text); - //}; - - /* BBS GUI refactor */ - // options section - //if (any_option_available()) { - // // title - // ImGui::Spacing(); - // imgui.title(_u8L("Options")); - - // // items - // add_option(EMoveType::Retract, EOptionsColors::Retractions, _u8L("Retractions")); - // add_option(EMoveType::Unretract, EOptionsColors::Unretractions, _u8L("Deretractions")); - // add_option(EMoveType::Seam, EOptionsColors::Seams, _u8L("Seams")); - // add_option(EMoveType::Tool_change, EOptionsColors::ToolChanges, _u8L("Tool changes")); - // add_option(EMoveType::Color_change, EOptionsColors::ColorChanges, _u8L("Color changes")); - // add_option(EMoveType::Pause_Print, EOptionsColors::PausePrints, _u8L("Print pauses")); - // add_option(EMoveType::Custom_GCode, EOptionsColors::CustomGCodes, _u8L("Custom G-codes")); - //} - - - // settings section - bool has_settings = false; - has_settings |= !m_settings_ids.print.empty(); - has_settings |= !m_settings_ids.printer.empty(); - bool has_filament_settings = true; - has_filament_settings &= !m_settings_ids.filament.empty(); - for (const std::string& fs : m_settings_ids.filament) { - has_filament_settings &= !fs.empty(); - } - has_settings |= has_filament_settings; - //BBS: add only gcode mode - bool show_settings = m_only_gcode_in_preview; //wxGetApp().is_gcode_viewer(); - show_settings &= (m_view_type == EViewType::FeatureType || m_view_type == EViewType::Tool); - show_settings &= has_settings; - if (show_settings) { - auto calc_offset = [this]() { - float ret = 0.0f; - if (!m_settings_ids.printer.empty()) - ret = std::max(ret, ImGui::CalcTextSize((_u8L("Printer") + std::string(":")).c_str()).x); - if (!m_settings_ids.print.empty()) - ret = std::max(ret, ImGui::CalcTextSize((_u8L("Print settings") + std::string(":")).c_str()).x); + if (!m_settings_ids.printer.empty()) { + imgui.text(_u8L("Printer") + ":"); + ImGui::SameLine(offset); + imgui.text(m_settings_ids.printer); + } + if (!m_settings_ids.print.empty()) { + imgui.text(_u8L("Print settings") + ":"); + ImGui::SameLine(offset); + imgui.text(m_settings_ids.print); + } if (!m_settings_ids.filament.empty()) { for (unsigned char i : m_extruder_ids) { - ret = std::max(ret, ImGui::CalcTextSize((_u8L("Filament") + " " + std::to_string(i + 1) + ":").c_str()).x); - } - } - if (ret > 0.0f) - ret += 2.0f * ImGui::GetStyle().ItemSpacing.x; - return ret; - }; - - ImGui::Spacing(); - imgui.title(_u8L("Settings")); - - float offset = calc_offset(); - - if (!m_settings_ids.printer.empty()) { - imgui.text(_u8L("Printer") + ":"); - ImGui::SameLine(offset); - imgui.text(m_settings_ids.printer); - } - if (!m_settings_ids.print.empty()) { - imgui.text(_u8L("Print settings") + ":"); - ImGui::SameLine(offset); - imgui.text(m_settings_ids.print); - } - if (!m_settings_ids.filament.empty()) { - for (unsigned char i : m_extruder_ids) { - if (i < static_cast(m_settings_ids.filament.size()) && !m_settings_ids.filament[i].empty()) { - std::string txt = _u8L("Filament"); - txt += (m_extruder_ids.size() == 1) ? ":" : " " + std::to_string(i + 1); - imgui.text(txt); - ImGui::SameLine(offset); - imgui.text(m_settings_ids.filament[i]); + if (i < static_cast(m_settings_ids.filament.size()) && !m_settings_ids.filament[i].empty()) { + std::string txt = _u8L("Filament"); + txt += (m_extruder_ids.size() == 1) ? ":" : " " + std::to_string(i + 1); + imgui.text(txt); + ImGui::SameLine(offset); + imgui.text(m_settings_ids.filament[i]); + } } } } } - // total estimated printing time section if (show_estimated) { + ImGui::Dummy({ window_padding, window_padding }); ImGui::Spacing(); std::string time_title = m_view_type == EViewType::FeatureType ? _u8L("Total Estimation") : _u8L("Time Estimation"); auto can_show_mode_button = [this](PrintEstimatedStatistics::ETimeMode mode) { @@ -5713,6 +5826,8 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv ImGui::Dummy({ window_padding, window_padding }); ImGui::SameLine(); imgui.title(time_title); + //auto timecText = new wxStaticText(s_panel, wxID_ANY, wxString::FromUTF8(time_title), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); + //auto timecText = new wxStaticText(s_panel, wxID_ANY, time_title, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); std::string total_filament_str = _u8L("Total Filament"); std::string model_filament_str = _u8L("Model Filament"); std::string cost_str = _u8L("Cost"); @@ -5796,6 +5911,7 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv imgui.text(total_str + ":"); ImGui::SameLine(max_len); imgui.text(short_time(get_time_dhms(time_mode.time))); + //imgui.text(get_time_dhms(time_mode.time)); //xiamian+ ImGui::Dummy({ window_padding, window_padding }); ImGui::SameLine(); @@ -5818,6 +5934,10 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv //oss << std::put_time(new_local_tm, "%Y-%m-%d %H:%M:%S"); oss << std::put_time(new_local_tm, "%Y-%m-%d %H:%M"); imgui.text(oss.str()); + ImGui::SameLine(max_len); + std::string abc = " "; + imgui.text(abc); + //ImGui::Spacing(); //shangmian+ auto show_mode_button = [this, &imgui, can_show_mode_button](const wxString& label, PrintEstimatedStatistics::ETimeMode mode) { if (can_show_mode_button(mode)) { @@ -5844,6 +5964,7 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv } default : { assert(false); break; } } + ImGui::Dummy({ window_padding, window_padding }); } if (m_view_type == EViewType::ColorPrint) { diff --git a/src/slic3r/GUI/ParamsPanel.cpp b/src/slic3r/GUI/ParamsPanel.cpp index 8cbef04c8..ac23cfac2 100644 --- a/src/slic3r/GUI/ParamsPanel.cpp +++ b/src/slic3r/GUI/ParamsPanel.cpp @@ -414,6 +414,9 @@ void ParamsPanel::create_layout() // m_left_sizer->Add( m_filament_sizer, 1, wxEXPAND, 5 ); m_left_sizer->Add( m_tab_filament, 0, wxEXPAND ); } + if (m_tab_config) { + m_left_sizer->Add(m_tab_config, 0, wxEXPAND); + } if (m_tab_printer) { //m_printer_sizer = new wxBoxSizer( wxVERTICAL ); @@ -481,6 +484,10 @@ void ParamsPanel::refresh_tabs() case Preset::TYPE_PRINTER: m_tab_printer = tab; break; + case Preset::TYPE_CONFIG: + m_tab_config = tab; + //m_tab_config = NULL; + break; default: break; } @@ -509,7 +516,7 @@ void ParamsPanel::OnActivate() BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": first time opened, set current tab to print"); // BBS: open/close tab //m_current_tab = m_tab_print; - set_active_tab(m_tab_print ? m_tab_print : m_tab_filament); + set_active_tab(m_tab_print ? m_tab_print : m_tab_filament ? m_tab_filament : m_tab_config); } Tab* cur_tab = dynamic_cast (m_current_tab); if (cur_tab) @@ -587,7 +594,8 @@ void ParamsPanel::set_active_tab(wxPanel* tab) {m_tab_print_layer, nullptr}, {m_tab_print_plate, nullptr}, {m_tab_filament, m_staticline_filament}, - {m_tab_printer, m_staticline_printer}})) { + {m_tab_printer, m_staticline_printer}, + {m_tab_config, m_staticline_config}})) { if (!t.first) continue; t.first->Show(tab == t.first); if (!t.second) continue; @@ -658,7 +666,7 @@ void ParamsPanel::msw_rescale() ((SwitchButton* )m_mode_region)->Rescale(); if (m_mode_view) ((SwitchButton* )m_mode_view)->Rescale(); - for (auto tab : {m_tab_print, m_tab_print_plate, m_tab_print_object, m_tab_print_part, m_tab_print_layer, m_tab_filament, m_tab_printer}) { + for (auto tab : {m_tab_print, m_tab_print_plate, m_tab_print_object, m_tab_print_part, m_tab_print_layer, m_tab_filament, m_tab_printer, m_tab_config}) { if (tab) dynamic_cast(tab)->msw_rescale(); } //((Button*)m_export_to_file)->Rescale(); @@ -799,6 +807,12 @@ void ParamsPanel::delete_subwindows() m_staticline_printer = nullptr; } + if (m_staticline_config) + { + delete m_staticline_config; + m_staticline_config = nullptr; + } + if (m_export_to_file) { delete m_export_to_file; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 4585b7e16..7c7fe618b 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1382,6 +1382,17 @@ void Sidebar::update_presets(Preset::Type preset_type) } break; } + case Preset::TYPE_CONFIG: + //wxGetApp().mainframe->m_param_panel; + //p->combo_print->update(); + { + /* Tab* config_tab = wxGetApp().get_tab(Preset::TYPE_CONFIG); + if (config_tab) { + config_tab->get_combo_box()->update(); + }*/ + p->combo_config->update(); + break; + } case Preset::TYPE_SLA_PRINT: ;// p->combo_sla_print->update(); break; @@ -1538,6 +1549,7 @@ void Sidebar::msw_rescale() // //p->combo_printer // } ) // combo->msw_rescale(); + p->combo_config->msw_rescale(); p->combo_printer->msw_rescale(); for (PlaterPresetComboBox* combo : p->combos_filament) combo->msw_rescale(); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 9f1942059..225c7ccb9 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -966,7 +966,8 @@ void Tab::update_changed_tree_ui() } } if (page->title() == "Dependencies") { - if (m_type == Slic3r::Preset::TYPE_PRINTER) { + //xiamian+ + if (m_type == Slic3r::Preset::TYPE_PRINTER || m_type == Slic3r::Preset::TYPE_CONFIG) { sys_page = m_presets->get_selected_preset_parent() != nullptr; modified_page = false; } else { @@ -1923,304 +1924,271 @@ void TabPrint::build() m_presets = &m_preset_bundle->prints; load_initial_data(); - - auto page = add_options_page(L("Quality"), "empty"); - //xiamian- - //auto page = add_options_page(L("Quality"), "empty"); - //auto optgroup = page->new_optgroup(L("Layer height"), L"param_layer_height"); - auto optgroup = page->new_optgroup("", L"param_layer_height"); - //optgroup->append_single_option_line("layer_height", "layer-height"); - //optgroup->append_single_option_line("initial_layer_print_height", "layer-height"); - //xiamian- - //optgroup = page->new_optgroup(L("Line width"), L"param_line_width"); - //optgroup->append_single_option_line("line_width","parameter/line-width"); - //optgroup->append_single_option_line("initial_layer_line_width","parameter/line-width"); - //optgroup->append_single_option_line("outer_wall_line_width","parameter/line-width"); - //optgroup->append_single_option_line("inner_wall_line_width","parameter/line-width"); - //optgroup->append_single_option_line("top_surface_line_width","parameter/line-width"); - //optgroup->append_single_option_line("sparse_infill_line_width","parameter/line-width"); - //optgroup->append_single_option_line("internal_solid_infill_line_width","parameter/line-width"); - //optgroup->append_single_option_line("support_line_width","parameter/line-width"); - //xiamian- - //optgroup = page->new_optgroup(L("Seam"), L"param_seam"); - //optgroup->append_single_option_line("seam_position", "Seam"); - optgroup->append_single_option_line("seam_gap", "Seam"); - //optgroup->append_single_option_line("seam_slope_type"); - optgroup->append_single_option_line("seam_slope_conditional"); - optgroup->append_single_option_line("scarf_angle_threshold"); - optgroup->append_single_option_line("seam_slope_start_height"); - optgroup->append_single_option_line("seam_slope_entire_loop"); - optgroup->append_single_option_line("seam_slope_min_length"); - optgroup->append_single_option_line("seam_slope_steps"); - optgroup->append_single_option_line("seam_slope_inner_walls"); - optgroup->append_single_option_line("wipe_speed", "Seam"); - //xiamian- - //optgroup = page->new_optgroup(L("Precision"), L"param_precision"); - //optgroup->append_single_option_line("slice_closing_radius"); - //optgroup->append_single_option_line("resolution","acr-move"); - //optgroup->append_single_option_line("enable_arc_fitting", "acr-move"); - //optgroup->append_single_option_line("xy_hole_compensation", "xy-hole-contour-compensation"); - //optgroup->append_single_option_line("xy_contour_compensation", "xy-hole-contour-compensation"); - //optgroup->append_single_option_line("elefant_foot_compensation", "parameter/elephant-foot"); - //optgroup->append_single_option_line("precise_z_height"); - //xiamian- - //optgroup = page->new_optgroup(L("Ironing"), L"param_ironing"); - //optgroup->append_single_option_line("ironing_type", "parameter/ironing"); - optgroup->append_single_option_line("ironing_pattern"); - optgroup->append_single_option_line("ironing_speed"); - optgroup->append_single_option_line("ironing_flow"); - optgroup->append_single_option_line("ironing_spacing"); - optgroup->append_single_option_line("ironing_direction"); - //xiamian- - //optgroup = page->new_optgroup(L("Wall generator"), L"param_wall"); - optgroup->append_single_option_line("wall_generator", "wall-generator"); - optgroup->append_single_option_line("wall_transition_angle"); - optgroup->append_single_option_line("wall_transition_filter_deviation"); - optgroup->append_single_option_line("wall_transition_length"); - optgroup->append_single_option_line("wall_distribution_count"); - optgroup->append_single_option_line("min_bead_width"); - optgroup->append_single_option_line("min_feature_size"); - //xiamian- - //optgroup = page->new_optgroup(L("Advanced"), L"param_advanced"); - optgroup->append_single_option_line("wall_sequence"); - //optgroup->append_single_option_line("is_infill_first"); - //optgroup->append_single_option_line("bridge_flow","parameter/bridge"); - //optgroup->append_single_option_line("thick_bridges","parameter/bridge"); - //optgroup->append_single_option_line("top_solid_infill_flow_ratio"); - //optgroup->append_single_option_line("initial_layer_flow_ratio"); - //optgroup->append_single_option_line("top_one_wall_type"); - //optgroup->append_single_option_line("top_area_threshold"); - //optgroup->append_single_option_line("only_one_wall_first_layer"); - //optgroup->append_single_option_line("detect_overhang_wall"); - //optgroup->append_single_option_line("smooth_speed_discontinuity_area"); - //optgroup->append_single_option_line("smooth_coefficient"); - //optgroup->append_single_option_line("reduce_crossing_wall"); - //optgroup->append_single_option_line("max_travel_detour_distance"); + auto page = add_options_page(L("Quality"), "empty"); + auto optgroup = page->new_optgroup("", L"param_layer_height"); + optgroup->append_single_option_line("layer_height", "layer-height"); + //optgroup->append_single_option_line("initial_layer_print_height", "layer-height"); + + //optgroup = page->new_optgroup(L("Line width"), L"param_line_width"); + //optgroup->append_single_option_line("line_width", "parameter/line-width"); + //optgroup->append_single_option_line("initial_layer_line_width", "parameter/line-width"); + //optgroup->append_single_option_line("outer_wall_line_width", "parameter/line-width"); + //optgroup->append_single_option_line("inner_wall_line_width", "parameter/line-width"); + //optgroup->append_single_option_line("top_surface_line_width", "parameter/line-width"); + //optgroup->append_single_option_line("sparse_infill_line_width", "parameter/line-width"); + //optgroup->append_single_option_line("internal_solid_infill_line_width", "parameter/line-width"); + //optgroup->append_single_option_line("support_line_width", "parameter/line-width"); + + //optgroup = page->new_optgroup(L("Seam"), L"param_seam"); + //optgroup->append_single_option_line("seam_position", "Seam"); + //optgroup->append_single_option_line("seam_gap", "Seam"); + //optgroup->append_single_option_line("seam_slope_type"); + //optgroup->append_single_option_line("seam_slope_conditional"); + //optgroup->append_single_option_line("scarf_angle_threshold"); + //optgroup->append_single_option_line("seam_slope_start_height"); + //optgroup->append_single_option_line("seam_slope_entire_loop"); + //optgroup->append_single_option_line("seam_slope_min_length"); + //optgroup->append_single_option_line("seam_slope_steps"); + //optgroup->append_single_option_line("seam_slope_inner_walls"); + //optgroup->append_single_option_line("wipe_speed", "Seam"); + + //optgroup = page->new_optgroup(L("Precision"), L"param_precision"); + //optgroup->append_single_option_line("slice_closing_radius"); + //optgroup->append_single_option_line("resolution", "acr-move"); + //optgroup->append_single_option_line("enable_arc_fitting", "acr-move"); + //optgroup->append_single_option_line("xy_hole_compensation", "xy-hole-contour-compensation"); + //optgroup->append_single_option_line("xy_contour_compensation", "xy-hole-contour-compensation"); + //optgroup->append_single_option_line("elefant_foot_compensation", "parameter/elephant-foot"); + //optgroup->append_single_option_line("precise_z_height"); + + //optgroup = page->new_optgroup(L("Ironing"), L"param_ironing"); + //optgroup->append_single_option_line("ironing_type", "parameter/ironing"); + //optgroup->append_single_option_line("ironing_pattern"); + //optgroup->append_single_option_line("ironing_speed"); + //optgroup->append_single_option_line("ironing_flow"); + //optgroup->append_single_option_line("ironing_spacing"); + //optgroup->append_single_option_line("ironing_direction"); + + //optgroup = page->new_optgroup(L("Wall generator"), L"param_wall"); + //optgroup->append_single_option_line("wall_generator", "wall-generator"); + optgroup->append_single_option_line("wall_transition_angle"); + optgroup->append_single_option_line("wall_transition_filter_deviation"); + optgroup->append_single_option_line("wall_transition_length"); + optgroup->append_single_option_line("wall_distribution_count"); + optgroup->append_single_option_line("min_bead_width"); + optgroup->append_single_option_line("min_feature_size"); + + //optgroup = page->new_optgroup(L("Advanced"), L"param_advanced"); + //optgroup->append_single_option_line("wall_sequence"); + //optgroup->append_single_option_line("is_infill_first"); + //optgroup->append_single_option_line("bridge_flow", "parameter/bridge"); + //optgroup->append_single_option_line("thick_bridges", "parameter/bridge"); + //optgroup->append_single_option_line("top_solid_infill_flow_ratio"); + //optgroup->append_single_option_line("initial_layer_flow_ratio"); + optgroup->append_single_option_line("top_one_wall_type"); + optgroup->append_single_option_line("top_area_threshold"); + optgroup->append_single_option_line("only_one_wall_first_layer"); + optgroup->append_single_option_line("detect_overhang_wall"); + //optgroup->append_single_option_line("smooth_speed_discontinuity_area"); + //optgroup->append_single_option_line("smooth_coefficient"); + //optgroup->append_single_option_line("reduce_crossing_wall"); + optgroup->append_single_option_line("max_travel_detour_distance"); - optgroup->append_single_option_line("support_base_pattern_spacing", "support#base-pattern"); - optgroup->append_single_option_line("support_interface_top_layers", "support#base-pattern"); - optgroup->append_single_option_line("support_interface_bottom_layers", "support#base-pattern"); - optgroup->append_single_option_line("support_interface_spacing", "support#base-pattern"); - page = add_options_page(L("Strength"), "empty"); - //xiamian+ - optgroup = page->new_optgroup("", L"param_wall"); - //xiamian- - //optgroup = page->new_optgroup(L("Walls"), L"param_wall"); - //optgroup->append_single_option_line("wall_loops","wall-generator"); - optgroup->append_single_option_line("detect_thin_wall","wall-generator"); - - //optgroup = page->new_optgroup(L("Top/bottom shells"), L"param_shell"); - optgroup->append_single_option_line("interface_shells"); - optgroup->append_single_option_line("top_surface_pattern", "fill-patterns#Infill of the top surface and bottom surface"); - //optgroup->append_single_option_line("top_shell_layers"); - //optgroup->append_single_option_line("top_shell_thickness"); - optgroup->append_single_option_line("bottom_surface_pattern", "fill-patterns#Infill of the top surface and bottom surface"); - //optgroup->append_single_option_line("bottom_shell_layers"); - //optgroup->append_single_option_line("bottom_shell_thickness"); - optgroup->append_single_option_line("internal_solid_infill_pattern"); - - //optgroup = page->new_optgroup(L("Sparse infill"), L"param_infill"); - //optgroup->append_single_option_line("sparse_infill_density"); - optgroup->append_single_option_line("sparse_infill_pattern", "fill-patterns#infill types and their properties of sparse"); - //optgroup->append_single_option_line("sparse_infill_anchor"); - //optgroup->append_single_option_line("sparse_infill_anchor_max"); - optgroup->append_single_option_line("filter_out_gap_fill"); + optgroup = page->new_optgroup("", L"param_wall"); + optgroup->append_single_option_line("wall_loops", "wall-generator"); + //optgroup->append_single_option_line("detect_thin_wall", "wall-generator"); - //optgroup = page->new_optgroup(L("Advanced"), L"param_advanced"); - //optgroup->append_single_option_line("infill_wall_overlap","parameter/strength-advance-settings"); - //optgroup->append_single_option_line("infill_direction","parameter/strength-advance-settings"); - optgroup->append_single_option_line("bridge_angle","parameter/strength-advance-settings"); - optgroup->append_single_option_line("minimum_sparse_infill_area","parameter/strength-advance-settings"); - optgroup->append_single_option_line("infill_combination","parameter/strength-advance-settings"); - //optgroup->append_single_option_line("detect_narrow_internal_solid_infill","parameter/strength-advance-settings"); - //optgroup->append_single_option_line("ensure_vertical_shell_thickness","parameter/strength-advance-settings"); - //optgroup->append_single_option_line("internal_bridge_support_thickness","parameter/strength-advance-settings"); + //optgroup = page->new_optgroup(L("Top/bottom shells"), L"param_shell"); + optgroup->append_single_option_line("interface_shells"); + //optgroup->append_single_option_line("top_surface_pattern", "fill-patterns#Infill of the top surface and bottom surface"); + optgroup->append_single_option_line("top_shell_layers"); + optgroup->append_single_option_line("top_shell_thickness"); + //optgroup->append_single_option_line("bottom_surface_pattern", "fill-patterns#Infill of the top surface and bottom surface"); + optgroup->append_single_option_line("bottom_shell_layers"); + optgroup->append_single_option_line("bottom_shell_thickness"); + //optgroup->append_single_option_line("internal_solid_infill_pattern"); + + //optgroup = page->new_optgroup(L("Sparse infill"), L"param_infill"); + optgroup->append_single_option_line("sparse_infill_density"); + //optgroup->append_single_option_line("sparse_infill_pattern", "fill-patterns#infill types and their properties of sparse"); + //optgroup->append_single_option_line("sparse_infill_anchor"); + //optgroup->append_single_option_line("sparse_infill_anchor_max"); + //optgroup->append_single_option_line("filter_out_gap_fill"); + + //optgroup = page->new_optgroup(L("Advanced"), L"param_advanced"); + //optgroup->append_single_option_line("infill_wall_overlap", "parameter/strength-advance-settings"); + //optgroup->append_single_option_line("infill_direction", "parameter/strength-advance-settings"); + //optgroup->append_single_option_line("bridge_angle", "parameter/strength-advance-settings"); + //optgroup->append_single_option_line("minimum_sparse_infill_area", "parameter/strength-advance-settings"); + //optgroup->append_single_option_line("infill_combination", "parameter/strength-advance-settings"); + //optgroup->append_single_option_line("detect_narrow_internal_solid_infill", "parameter/strength-advance-settings"); + //optgroup->append_single_option_line("ensure_vertical_shell_thickness", "parameter/strength-advance-settings"); + //optgroup->append_single_option_line("internal_bridge_support_thickness","parameter/strength-advance-settings"); page = add_options_page(L("Speed"), "empty"); - //xiamian+ - optgroup = page->new_optgroup("", L"param_speed",15); - //xiamian- - //optgroup = page->new_optgroup(L("Initial layer speed"), L"param_speed_first", 15); - optgroup->append_single_option_line("default_print_speed"); - optgroup->append_single_option_line("initial_layer_speed"); - optgroup->append_single_option_line("initial_layer_infill_speed"); - //optgroup = page->new_optgroup(L("Other layers speed"), L"param_speed", 15); - optgroup->append_single_option_line("outer_wall_speed"); - optgroup->append_single_option_line("inner_wall_speed"); - //optgroup->append_single_option_line("small_perimeter_speed"); - //optgroup->append_single_option_line("small_perimeter_threshold"); - optgroup->append_single_option_line("sparse_infill_speed"); - optgroup->append_single_option_line("internal_solid_infill_speed"); - optgroup->append_single_option_line("top_surface_speed"); - //optgroup->append_single_option_line("enable_overhang_speed", "slow-down-for-overhang"); - //Line line = { L("Overhang speed"), L("This is the speed for various overhang degrees. Overhang degrees are expressed as a percentage of line width. 0 speed means no slowing down for the overhang degree range and wall speed is used") }; - //line.label_path = "slow-down-for-overhang"; - //line.append_option(optgroup->get_option("overhang_1_4_speed")); - //line.append_option(optgroup->get_option("overhang_2_4_speed")); - //line.append_option(optgroup->get_option("overhang_3_4_speed")); - //line.append_option(optgroup->get_option("overhang_4_4_speed")); - //optgroup->append_line(line); - //optgroup->append_single_option_line("overhang_totally_speed"); - optgroup->append_single_option_line("bridge_speed"); - optgroup->append_single_option_line("gap_infill_speed"); - optgroup->append_single_option_line("support_speed"); - optgroup->append_single_option_line("support_interface_speed"); + optgroup = page->new_optgroup("", L"param_speed_first", 15); + //optgroup->append_single_option_line("initial_layer_speed"); + //optgroup->append_single_option_line("initial_layer_infill_speed"); + + //optgroup = page->new_optgroup(L("Other layers speed"), L"param_speed", 15); + //optgroup->append_single_option_line("outer_wall_speed"); + //optgroup->append_single_option_line("inner_wall_speed"); + //optgroup->append_single_option_line("small_perimeter_speed"); + //optgroup->append_single_option_line("small_perimeter_threshold"); + //optgroup->append_single_option_line("sparse_infill_speed"); + //optgroup->append_single_option_line("internal_solid_infill_speed"); + //optgroup->append_single_option_line("top_surface_speed"); + //optgroup->append_single_option_line("enable_overhang_speed", "slow-down-for-overhang"); + //Line line = { L("Overhang speed"), L("This is the speed for various overhang degrees. Overhang degrees are expressed as a percentage of line width. 0 speed means no slowing down for the overhang degree range and wall speed is used") }; + //line.label_path = "slow-down-for-overhang"; + //line.append_option(optgroup->get_option("overhang_1_4_speed")); + //line.append_option(optgroup->get_option("overhang_2_4_speed")); + //line.append_option(optgroup->get_option("overhang_3_4_speed")); + //line.append_option(optgroup->get_option("overhang_4_4_speed")); + //optgroup->append_line(line); + //optgroup->append_single_option_line("overhang_totally_speed"); + //optgroup->append_single_option_line("bridge_speed"); + //optgroup->append_single_option_line("gap_infill_speed"); + //optgroup->append_single_option_line("support_speed"); + //optgroup->append_single_option_line("support_interface_speed"); - //optgroup = page->new_optgroup(L("Travel speed"), L"param_travel_speed", 15); - optgroup->append_single_option_line("travel_speed"); + //optgroup = page->new_optgroup(L("Travel speed"), L"param_travel_speed", 15); + //optgroup->append_single_option_line("travel_speed"); - //optgroup = page->new_optgroup(L("Acceleration"), L"param_acceleration", 15); - optgroup->append_single_option_line("default_acceleration"); - //optgroup->append_single_option_line("initial_layer_acceleration"); - //optgroup->append_single_option_line("outer_wall_acceleration"); - //optgroup->append_single_option_line("inner_wall_acceleration"); - //optgroup->append_single_option_line("top_surface_acceleration"); - //optgroup->append_single_option_line("sparse_infill_acceleration"); - //optgroup->append_single_option_line("accel_to_decel_enable"); - //optgroup->append_single_option_line("accel_to_decel_factor"); + //optgroup = page->new_optgroup(L("Acceleration"), L"param_acceleration", 15); + //optgroup->append_single_option_line("default_acceleration"); + //optgroup->append_single_option_line("initial_layer_acceleration"); + //optgroup->append_single_option_line("outer_wall_acceleration"); + //optgroup->append_single_option_line("inner_wall_acceleration"); + //optgroup->append_single_option_line("top_surface_acceleration"); + //optgroup->append_single_option_line("sparse_infill_acceleration"); + //optgroup->append_single_option_line("accel_to_decel_enable"); + //optgroup->append_single_option_line("accel_to_decel_factor"); - //optgroup = page->new_optgroup(L("Jerk(XY)"), L"param_acceleration", 15); - optgroup->append_single_option_line("default_jerk"); - optgroup->append_single_option_line("outer_wall_jerk"); - optgroup->append_single_option_line("inner_wall_jerk"); - optgroup->append_single_option_line("infill_jerk"); - optgroup->append_single_option_line("top_surface_jerk"); - optgroup->append_single_option_line("initial_layer_jerk"); - optgroup->append_single_option_line("travel_jerk"); + //optgroup = page->new_optgroup(L("Jerk(XY)"), L"param_acceleration", 15); + optgroup->append_single_option_line("default_jerk"); + optgroup->append_single_option_line("outer_wall_jerk"); + optgroup->append_single_option_line("inner_wall_jerk"); + optgroup->append_single_option_line("infill_jerk"); + optgroup->append_single_option_line("top_surface_jerk"); + optgroup->append_single_option_line("initial_layer_jerk"); + optgroup->append_single_option_line("travel_jerk"); -#ifdef HAS_PRESSURE_EQUALIZER - optgroup->append_single_option_line("max_volumetric_extrusion_rate_slope_positive"); - optgroup->append_single_option_line("max_volumetric_extrusion_rate_slope_negative"); -#endif /* HAS_PRESSURE_EQUALIZER */ +//#ifdef HAS_PRESSURE_EQUALIZER +// optgroup->append_single_option_line("max_volumetric_extrusion_rate_slope_positive"); +// optgroup->append_single_option_line("max_volumetric_extrusion_rate_slope_negative"); +//#endif /* HAS_PRESSURE_EQUALIZER */ page = add_options_page(L("Support"), "support"); - //xiamian+ optgroup = page->new_optgroup("", L"param_support"); - //ximian- - //optgroup = page->new_optgroup(L("Support"), L"param_support"); - //optgroup->append_single_option_line("enable_support", "support"); - optgroup->append_single_option_line("support_type", "support#support-types"); - optgroup->append_single_option_line("support_style", "support#support-styles"); - optgroup->append_single_option_line("support_threshold_angle", "support#threshold-angle"); - optgroup->append_single_option_line("support_on_build_plate_only"); - optgroup->append_single_option_line("support_critical_regions_only"); - optgroup->append_single_option_line("support_remove_small_overhang"); - //optgroup->append_single_option_line("enforce_support_layers"); + optgroup->append_single_option_line("enable_support", "support"); + optgroup->append_single_option_line("support_type", "support#support-types"); + optgroup->append_single_option_line("support_style", "support#support-styles"); + optgroup->append_single_option_line("support_threshold_angle", "support#threshold-angle"); + optgroup->append_single_option_line("support_on_build_plate_only"); + optgroup->append_single_option_line("support_critical_regions_only"); + optgroup->append_single_option_line("support_remove_small_overhang"); + //optgroup->append_single_option_line("enforce_support_layers"); - //optgroup = page->new_optgroup(L("Raft"), L"param_raft"); - optgroup->append_single_option_line("raft_layers"); - optgroup->append_single_option_line("raft_contact_distance"); - optgroup->append_single_option_line("raft_first_layer_density"); + //optgroup = page->new_optgroup(L("Raft"), L"param_raft"); + //optgroup->append_single_option_line("raft_layers"); + //optgroup->append_single_option_line("raft_contact_distance"); + //optgroup->append_single_option_line("raft_first_layer_density"); - //optgroup = page->new_optgroup(L("Support filament"), L"param_support_filament"); - optgroup->append_single_option_line("support_filament", "support#support-filament"); - optgroup->append_single_option_line("support_interface_filament", "support#support-filament"); - optgroup->append_single_option_line("support_interface_not_for_body", "support#support-filament"); + //optgroup = page->new_optgroup(L("Support filament"), L"param_support_filament"); + //optgroup->append_single_option_line("support_filament", "support#support-filament"); + //optgroup->append_single_option_line("support_interface_filament", "support#support-filament"); + //optgroup->append_single_option_line("support_interface_not_for_body", "support#support-filament"); - //optgroup = page->new_optgroup(L("Options for support material and raft")); + //optgroup = page->new_optgroup(L("Options for support material and raft")); - //BBS - //optgroup = page->new_optgroup(L("Advanced"), L"param_advanced"); - optgroup->append_single_option_line("raft_first_layer_expansion"); // not only for raft, but for support too - optgroup->append_single_option_line("tree_support_wall_count"); - //optgroup->append_single_option_line("support_top_z_distance", "support#top-z-distance"); - //optgroup->append_single_option_line("support_bottom_z_distance", "support#bottom-z-distance"); - optgroup->append_single_option_line("support_base_pattern", "support#base-pattern"); - //optgroup->append_single_option_line("support_base_pattern_spacing", "support#base-pattern"); - //optgroup->append_single_option_line("support_angle"); - //optgroup->append_single_option_line("support_interface_top_layers", "support#base-pattern"); - //optgroup->append_single_option_line("support_interface_bottom_layers", "support#base-pattern"); - optgroup->append_single_option_line("support_interface_pattern", "support#base-pattern"); - //optgroup->append_single_option_line("support_interface_spacing", "support#base-pattern"); - optgroup->append_single_option_line("support_bottom_interface_spacing"); - //optgroup->append_single_option_line("support_expansion", "support#base-pattern"); - //optgroup->append_single_option_line("support_interface_loop_pattern"); + //BBS + //optgroup = page->new_optgroup(L("Advanced"), L"param_advanced"); + //optgroup->append_single_option_line("raft_first_layer_expansion"); // not only for raft, but for support too + //optgroup->append_single_option_line("tree_support_wall_count"); + //optgroup->append_single_option_line("support_top_z_distance", "support#top-z-distance"); + //optgroup->append_single_option_line("support_bottom_z_distance", "support#bottom-z-distance"); + //optgroup->append_single_option_line("support_base_pattern", "support#base-pattern"); + //optgroup->append_single_option_line("support_base_pattern_spacing", "support#base-pattern"); + //optgroup->append_single_option_line("support_angle"); + //optgroup->append_single_option_line("support_interface_top_layers", "support#base-pattern"); + //optgroup->append_single_option_line("support_interface_bottom_layers", "support#base-pattern"); + //optgroup->append_single_option_line("support_interface_pattern", "support#base-pattern"); + //optgroup->append_single_option_line("support_interface_spacing", "support#base-pattern"); + //optgroup->append_single_option_line("support_bottom_interface_spacing"); + //optgroup->append_single_option_line("support_expansion", "support#base-pattern"); + //optgroup->append_single_option_line("support_interface_loop_pattern"); - optgroup->append_single_option_line("support_object_xy_distance", "support"); - //optgroup->append_single_option_line("support_object_first_layer_gap", "support"); - optgroup->append_single_option_line("bridge_no_support", "support#base-pattern"); - optgroup->append_single_option_line("max_bridge_length", "support#base-pattern"); - optgroup->append_single_option_line("independent_support_layer_height", "support"); + //optgroup->append_single_option_line("support_object_xy_distance", "support"); + //optgroup->append_single_option_line("support_object_first_layer_gap", "support"); + //optgroup->append_single_option_line("bridge_no_support", "support#base-pattern"); + //optgroup->append_single_option_line("max_bridge_length", "support#base-pattern"); + //optgroup->append_single_option_line("independent_support_layer_height", "support"); - //optgroup = page->new_optgroup(L("Tree Support"), L"param_advanced"); - optgroup->append_single_option_line("tree_support_branch_distance", "support#tree-support-only-options"); - optgroup->append_single_option_line("tree_support_branch_diameter", "support#tree-support-only-options"); - optgroup->append_single_option_line("tree_support_branch_angle", "support#tree-support-only-options"); - //xiamian+ - page = add_options_page(L("Configuration"), "empty"); - optgroup = page->new_optgroup("", L"param_layer_height"); - optgroup->append_single_option_line("wall_loops", "wall-generator"); - optgroup->append_single_option_line("layer_height", "layer-height"); - optgroup->append_single_option_line("seam_position", "Seam"); - optgroup->append_single_option_line("top_shell_layers"); - optgroup->append_single_option_line("top_shell_thickness"); - optgroup->append_single_option_line("bottom_shell_layers"); - optgroup->append_single_option_line("bottom_shell_thickness"); - optgroup->append_single_option_line("sparse_infill_density"); - optgroup->append_single_option_line("enable_support", "support"); + //optgroup = page->new_optgroup(L("Tree Support"), L"param_advanced"); + //optgroup->append_single_option_line("tree_support_branch_distance", "support#tree-support-only-options"); + //optgroup->append_single_option_line("tree_support_branch_diameter", "support#tree-support-only-options"); + //optgroup->append_single_option_line("tree_support_branch_angle", "support#tree-support-only-options"); page = add_options_page(L("Others"), "advanced"); - //xiamian+ optgroup = page->new_optgroup("", L"param_adhension"); - //xiamian- - //optgroup = page->new_optgroup(L("Bed adhension"), L"param_adhension"); - //optgroup->append_single_option_line("skirt_loops"); - //optgroup->append_single_option_line("skirt_height"); - //optgroup->append_single_option_line("skirt_distance"); - //optgroup->append_single_option_line("draft_shield"); - //optgroup->append_single_option_line("brim_type", "auto-brim"); - //optgroup->append_single_option_line("brim_width", "auto-brim#manual"); - //optgroup->append_single_option_line("brim_object_gap", "auto-brim#brim-object-gap"); + //optgroup->append_single_option_line("skirt_loops"); + //optgroup->append_single_option_line("skirt_height"); + //optgroup->append_single_option_line("skirt_distance"); + //optgroup->append_single_option_line("draft_shield"); + //optgroup->append_single_option_line("brim_type", "auto-brim"); + //optgroup->append_single_option_line("brim_width", "auto-brim#manual"); + //optgroup->append_single_option_line("brim_object_gap", "auto-brim#brim-object-gap"); - //optgroup = page->new_optgroup(L("Prime tower"), L"param_tower"); - //optgroup->append_single_option_line("enable_prime_tower","parameter/prime-tower"); - //optgroup->append_single_option_line("prime_tower_width","parameter/prime-tower"); - //optgroup->append_single_option_line("prime_volume","parameter/prime-tower"); - //optgroup->append_single_option_line("prime_tower_brim_width","parameter/prime-tower"); + //optgroup = page->new_optgroup(L("Prime tower"), L"param_tower"); + //optgroup->append_single_option_line("enable_prime_tower", "parameter/prime-tower"); + //optgroup->append_single_option_line("prime_tower_width", "parameter/prime-tower"); + //optgroup->append_single_option_line("prime_volume", "parameter/prime-tower"); + //optgroup->append_single_option_line("prime_tower_brim_width", "parameter/prime-tower"); - //optgroup = page->new_optgroup(L("Flush options"), L"param_flush"); - //optgroup->append_single_option_line("flush_into_infill", "reduce-wasting-during-filament-change#wipe-into-infill"); - //optgroup->append_single_option_line("flush_into_objects", "reduce-wasting-during-filament-change#wipe-into-object"); - //optgroup->append_single_option_line("flush_into_support", "reduce-wasting-during-filament-change#wipe-into-support-enabled-by-default"); + //optgroup = page->new_optgroup(L("Flush options"), L"param_flush"); + //optgroup->append_single_option_line("flush_into_infill", "reduce-wasting-during-filament-change#wipe-into-infill"); + //optgroup->append_single_option_line("flush_into_objects", "reduce-wasting-during-filament-change#wipe-into-object"); + //optgroup->append_single_option_line("flush_into_support", "reduce-wasting-during-filament-change#wipe-into-support-enabled-by-default"); - //optgroup = page->new_optgroup(L("Special mode"), L"param_special"); - optgroup->append_single_option_line("slicing_mode"); - optgroup->append_single_option_line("print_sequence", "sequent-print"); - optgroup->append_single_option_line("spiral_mode", "spiral-vase"); - optgroup->append_single_option_line("spiral_mode_smooth", "spiral-vase#smooth"); - optgroup->append_single_option_line("spiral_mode_max_xy_smoothing", "spiral-vase#max-xy-smoothing"); - optgroup->append_single_option_line("timelapse_type", "Timelapse"); + //optgroup = page->new_optgroup(L("Special mode"), L"param_special"); + //optgroup->append_single_option_line("slicing_mode"); + //optgroup->append_single_option_line("print_sequence", "sequent-print"); + //optgroup->append_single_option_line("spiral_mode", "spiral-vase"); + //optgroup->append_single_option_line("spiral_mode_smooth", "spiral-vase#smooth"); + //optgroup->append_single_option_line("spiral_mode_max_xy_smoothing", "spiral-vase#max-xy-smoothing"); + //optgroup->append_single_option_line("timelapse_type", "Timelapse"); - optgroup->append_single_option_line("fuzzy_skin", "parameter/fuzzy-skin"); - optgroup->append_single_option_line("fuzzy_skin_point_distance"); - optgroup->append_single_option_line("fuzzy_skin_thickness"); + //optgroup->append_single_option_line("fuzzy_skin", "parameter/fuzzy-skin"); + //optgroup->append_single_option_line("fuzzy_skin_point_distance"); + //optgroup->append_single_option_line("fuzzy_skin_thickness"); - //optgroup = page->new_optgroup(L("Advanced"), L"advanced"); - // optgroup->append_single_option_line("mmu_segmented_region_max_width"); - optgroup->append_single_option_line("mmu_segmented_region_interlocking_depth"); + //optgroup = page->new_optgroup(L("Advanced"), L"advanced"); + // optgroup->append_single_option_line("mmu_segmented_region_max_width"); + optgroup->append_single_option_line("mmu_segmented_region_interlocking_depth"); - //optgroup = page->new_optgroup(L("G-code output"), L"param_gcode"); - optgroup->append_single_option_line("reduce_infill_retraction"); - optgroup->append_single_option_line("gcode_add_line_number"); - optgroup->append_single_option_line("exclude_object"); - Option option = optgroup->get_option("filename_format"); - option.opt.full_width = true; - optgroup->append_single_option_line(option); + optgroup = page->new_optgroup(L("G-code output"), L"param_gcode"); + optgroup->append_single_option_line("reduce_infill_retraction"); + optgroup->append_single_option_line("gcode_add_line_number"); + optgroup->append_single_option_line("exclude_object"); + Option option = optgroup->get_option("filename_format"); + option.opt.full_width = true; + optgroup->append_single_option_line(option); - optgroup = page->new_optgroup(L("Post-processing scripts"), L"param_gcode", 0); - option = optgroup->get_option("post_process"); - option.opt.full_width = true; - option.opt.is_code = true; - option.opt.height = 15; - optgroup->append_single_option_line(option); + optgroup = page->new_optgroup(L("Post-processing scripts"), L"param_gcode", 0); + option = optgroup->get_option("post_process"); + option.opt.full_width = true; + option.opt.is_code = true; + option.opt.height = 15; + optgroup->append_single_option_line(option); - optgroup = page->new_optgroup(L("Notes"),"note"); - optgroup->label_width = 0; - option = optgroup->get_option("process_notes"); - option.opt.full_width = true; - option.opt.height = 25; - optgroup->append_single_option_line(option); + optgroup = page->new_optgroup(L("Notes"), "note"); + optgroup->label_width = 0; + option = optgroup->get_option("process_notes"); + option.opt.full_width = true; + option.opt.height = 25; + optgroup->append_single_option_line(option); #if 0 //page = add_options_page(L("Dependencies"), "advanced.png"); @@ -3184,7 +3152,7 @@ void TabFilament::build() line.append_option(optgroup->get_option("complete_print_exhaust_fan_speed")); optgroup->append_line(line); //BBS - add_filament_overrides_page(); + // add_filament_overrides_page(); #if 0 //page = add_options_page(L("Advanced"), "advanced"); // optgroup = page->new_optgroup(L("Wipe tower parameters")); @@ -4281,48 +4249,152 @@ void TabPrinter::update_fff() void TabPrinter::update_sla() { ; } + void TabConfig::build() { m_presets = &m_preset_bundle->configs; load_initial_data(); + auto page = add_options_page(L("Quality"), "spool"); + auto optgroup = page->new_optgroup("", L"param_layer_height"); + optgroup->append_single_option_line("initial_layer_print_height", "layer-height"); - //auto page = add_options_page(L("Qualityttttttt"), "empty"); - //auto optgroup = page->new_optgroup("", L"param_layer_height"); - //optgroup->append_single_option_line("initial_layer_print_height", "layer-height"); - //optgroup->append_single_option_line("initial_layer_line_width"); - //optgroup->append_single_option_line("outer_wall_line_width", "parameter/line-width"); - //optgroup->append_single_option_line("inner_wall_line_width", "parameter/line-width"); - //optgroup->append_single_option_line("top_surface_line_width", "parameter/line-width"); - //optgroup->append_single_option_line("sparse_infill_line_width", "parameter/line-width"); - //optgroup->append_single_option_line("support_line_width", "parameter/line-width"); - //optgroup->append_single_option_line("resolution", "acr-move"); - //optgroup->append_single_option_line("enable_arc_fitting", "acr-move"); - //optgroup->append_single_option_line("wall_generator", "wall-generator"); - //optgroup->append_single_option_line("wall_sequence"); + optgroup->append_single_option_line("initial_layer_line_width"); + optgroup->append_single_option_line("outer_wall_line_width", "parameter/line-width"); + optgroup->append_single_option_line("inner_wall_line_width", "parameter/line-width"); + optgroup->append_single_option_line("top_surface_line_width", "parameter/line-width"); + optgroup->append_single_option_line("sparse_infill_line_width", "parameter/line-width"); + optgroup->append_single_option_line("support_line_width", "parameter/line-width"); - /*page = add_options_page(L("Strength"), "empty"); - optgroup = page->new_optgroup("", L"param_wall"); - optgroup->append_single_option_line("detect_thin_wall", "wall-generator");*/ + optgroup->append_single_option_line("seam_gap", "Seam"); + optgroup->append_single_option_line("seam_slope_conditional"); + optgroup->append_single_option_line("scarf_angle_threshold"); + optgroup->append_single_option_line("seam_slope_start_height"); + optgroup->append_single_option_line("seam_slope_entire_loop"); + optgroup->append_single_option_line("seam_slope_min_length"); + optgroup->append_single_option_line("seam_slope_steps"); + optgroup->append_single_option_line("seam_slope_inner_walls"); + optgroup->append_single_option_line("wipe_speed", "Seam"); - auto page = add_options_page(L("Filament"), "spool"); - //BBS - auto optgroup = page->new_optgroup(L("Basic information"), L"param_information"); - // Set size as all another fields for a better alignment - //Option option = optgroup->get_option("filament_type"); + optgroup->append_single_option_line("resolution", "acr-move"); + optgroup->append_single_option_line("enable_arc_fitting", "acr-move"); - page = add_options_page(L("Cooling"), "empty"); + optgroup->append_single_option_line("ironing_pattern"); + optgroup->append_single_option_line("ironing_speed"); + optgroup->append_single_option_line("ironing_flow"); + optgroup->append_single_option_line("ironing_spacing"); + optgroup->append_single_option_line("ironing_direction"); - //line = { "", "" }; - //line.full_width = 1; - //line.widget = [this](wxWindow* parent) { - // return description_line_widget(parent, &m_cooling_description_line); - //}; - //optgroup->append_line(line); - optgroup = page->new_optgroup(L("Cooling for specific layer"), L"param_cooling"); + optgroup->append_single_option_line("wall_generator", "wall-generator"); + optgroup->append_single_option_line("wall_transition_angle"); + optgroup->append_single_option_line("wall_transition_filter_deviation"); + optgroup->append_single_option_line("wall_transition_length"); + optgroup->append_single_option_line("wall_distribution_count"); + optgroup->append_single_option_line("min_bead_width"); + optgroup->append_single_option_line("min_feature_size"); - page = add_options_page(L("Advanced"), "advanced"); - optgroup = page->new_optgroup(L("Filament start G-code"), L"param_gcode", 0); + optgroup->append_single_option_line("wall_sequence"); + optgroup->append_single_option_line("top_one_wall_type"); + optgroup->append_single_option_line("top_area_threshold"); + optgroup->append_single_option_line("only_one_wall_first_layer"); + optgroup->append_single_option_line("detect_overhang_wall"); + optgroup->append_single_option_line("max_travel_detour_distance"); + optgroup->append_single_option_line("support_base_pattern_spacing", "support#base-pattern"); + optgroup->append_single_option_line("support_interface_top_layers", "support#base-pattern"); + optgroup->append_single_option_line("support_interface_bottom_layers", "support#base-pattern"); + //you cuo wu -- zan ding + //optgroup->append_single_option_line("support_interface_spacing", "support#base-pattern"); + + + page = add_options_page(L("Strength"), "empty"); + optgroup = page->new_optgroup("", L"param_wall"); + optgroup->append_single_option_line("top_surface_pattern", "fill-patterns#Infill of the top surface and bottom surface"); + optgroup->append_single_option_line("bottom_surface_pattern", "fill-patterns#Infill of the top surface and bottom surface"); + optgroup->append_single_option_line("internal_solid_infill_pattern"); + + optgroup->append_single_option_line("sparse_infill_pattern", "fill-patterns#infill types and their properties of sparse"); + optgroup->append_single_option_line("filter_out_gap_fill"); + optgroup->append_single_option_line("bridge_angle", "parameter/strength-advance-settings"); + optgroup->append_single_option_line("minimum_sparse_infill_area", "parameter/strength-advance-settings"); + optgroup->append_single_option_line("infill_combination", "parameter/strength-advance-settings"); + + page = add_options_page(L("Speed"), "empty"); + optgroup = page->new_optgroup("", L"param_speed_first", 15); + optgroup->append_single_option_line("default_print_speed"); + optgroup->append_single_option_line("initial_layer_speed"); + optgroup->append_single_option_line("initial_layer_infill_speed"); + + optgroup->append_single_option_line("outer_wall_speed"); + optgroup->append_single_option_line("inner_wall_speed"); + optgroup->append_single_option_line("sparse_infill_speed"); + optgroup->append_single_option_line("internal_solid_infill_speed"); + optgroup->append_single_option_line("top_surface_speed"); + optgroup->append_single_option_line("bridge_speed"); + optgroup->append_single_option_line("gap_infill_speed"); + optgroup->append_single_option_line("support_speed"); + optgroup->append_single_option_line("support_interface_speed"); + + optgroup->append_single_option_line("travel_speed"); + + optgroup->append_single_option_line("default_acceleration"); + optgroup->append_single_option_line("accel_to_decel_enable"); + optgroup->append_single_option_line("accel_to_decel_factor"); + + /* optgroup->append_single_option_line("default_jerk"); + optgroup->append_single_option_line("outer_wall_jerk"); + optgroup->append_single_option_line("inner_wall_jerk"); + optgroup->append_single_option_line("infill_jerk"); + optgroup->append_single_option_line("top_surface_jerk"); + optgroup->append_single_option_line("initial_layer_jerk"); + optgroup->append_single_option_line("travel_jerk");*/ + + page = add_options_page(L("Support"), "support"); + optgroup = page->new_optgroup("", L"param_support"); + optgroup->append_single_option_line("support_type", "support#support-types"); + optgroup->append_single_option_line("support_style", "support#support-styles"); + optgroup->append_single_option_line("support_threshold_angle", "support#threshold-angle"); + optgroup->append_single_option_line("support_on_build_plate_only"); + optgroup->append_single_option_line("support_critical_regions_only"); + optgroup->append_single_option_line("support_remove_small_overhang"); + + optgroup->append_single_option_line("raft_layers"); + optgroup->append_single_option_line("raft_contact_distance"); + optgroup->append_single_option_line("raft_first_layer_density"); + + optgroup->append_single_option_line("support_filament", "support#support-filament"); + optgroup->append_single_option_line("support_interface_filament", "support#support-filament"); + optgroup->append_single_option_line("support_interface_not_for_body", "support#support-filament"); + + optgroup->append_single_option_line("raft_first_layer_expansion"); // not only for raft, but for support too + optgroup->append_single_option_line("tree_support_wall_count"); + optgroup->append_single_option_line("support_base_pattern", "support#base-pattern"); + optgroup->append_single_option_line("support_interface_pattern", "support#base-pattern"); + optgroup->append_single_option_line("support_bottom_interface_spacing"); + optgroup->append_single_option_line("support_object_xy_distance", "support"); + optgroup->append_single_option_line("bridge_no_support", "support#base-pattern"); + optgroup->append_single_option_line("max_bridge_length", "support#base-pattern"); + optgroup->append_single_option_line("independent_support_layer_height", "support"); + + optgroup->append_single_option_line("tree_support_branch_distance", "support#tree-support-only-options"); + optgroup->append_single_option_line("tree_support_branch_diameter", "support#tree-support-only-options"); + optgroup->append_single_option_line("tree_support_branch_angle", "support#tree-support-only-options"); + + page = add_options_page(L("Others"), "advanced"); + optgroup = page->new_optgroup("", L"param_adhension"); + optgroup->append_single_option_line("slicing_mode"); + optgroup->append_single_option_line("fibre_feed_rate"); + optgroup->append_single_option_line("print_sequence", "sequent-print"); + optgroup->append_single_option_line("spiral_mode", "spiral-vase"); + optgroup->append_single_option_line("spiral_mode_smooth", "spiral-vase#smooth"); + optgroup->append_single_option_line("spiral_mode_max_xy_smoothing", "spiral-vase#max-xy-smoothing"); + optgroup->append_single_option_line("timelapse_type", "Timelapse"); + + optgroup->append_single_option_line("fuzzy_skin", "parameter/fuzzy-skin"); + optgroup->append_single_option_line("fuzzy_skin_point_distance"); + optgroup->append_single_option_line("fuzzy_skin_thickness"); + } + + void TabConfig::reload_config() { //this->compatible_widget_reload(m_compatible_printers); @@ -4333,8 +4405,8 @@ void TabConfig::update_description_lines() { Tab::update_description_lines(); - //if (!m_active_page) - // return; + if (!m_active_page) + return; //if (m_active_page->title() == "Cooling" && m_cooling_description_line) // m_cooling_description_line->SetText(from_u8(PresetHints::cooling_description(m_presets->get_edited_preset()))); @@ -4348,15 +4420,16 @@ void TabConfig::toggle_options() { bool is_BBL_printer = false; if (m_preset_bundle) { - is_BBL_printer = - m_preset_bundle->configs.get_edited_preset().is_bbl_vendor_preset( - m_preset_bundle); + is_BBL_printer = m_preset_bundle->configs.get_edited_preset().is_bbl_vendor_preset(m_preset_bundle); } - if (m_active_page->title() == "Quality") { + + /* const Preset& preset = m_preset_bundle->configs.get_edited_preset(); + bool is_BBL = preset.is_system;*/ + //if (m_active_page->title() == "Quality") { //toggle_line("printable_area", !is_configed_by_BBL);//all printer can entry and view data //toggle_option("single_extruder_multi_material", have_multiple_extruders); //BBS: gcode_flavore of BBL printer can't be edited and changed - toggle_option("gcode_flavor", !is_BBL_printer); + //toggle_option("gcode_flavor", !is_BBL_printer); //toggle_option("thumbnail_size", !is_BBL_printer); //toggle_option("printer_structure", !is_BBL_printer); //toggle_option("use_relative_e_distances", !is_BBL_printer); @@ -4370,21 +4443,37 @@ void TabConfig::toggle_options() { ////BBS: extruder clearance of BBL printer can't be edited. //for (auto el : { "extruder_clearance_max_radius", "extruder_clearance_height_to_rod", "extruder_clearance_height_to_lid" }) // toggle_option(el, !is_BBL_printer); - } + //} - if (m_active_page->title() == "Strength") { - //toggle_line("time_lapse_gcode", m_preset_bundle->configs.get_edited_preset().config.opt_enum("printer_structure") == PrinterStructure::psI3); + /* if (m_active_page->title() == "Strength") { + toggle_line("time_lapse_gcode", m_preset_bundle->configs.get_edited_preset().config.opt_enum("printer_structure") == PrinterStructure::psI3); toggle_option("thumbnail_size", !is_BBL_printer); - } + }*/ } void TabConfig::update() { + //m_update_cnt++; + //update_description_lines(); + ////BBS: GUI refactor + ////Layout(); + //m_parent->Layout(); + + m_update_cnt++; + update_description_lines(); //BBS: GUI refactor //Layout(); m_parent->Layout(); + + toggle_options(); + + m_update_cnt--; + + if (m_update_cnt == 0) + wxGetApp().mainframe->on_config_changed(m_config); } void TabConfig::clear_pages() { + Tab::clear_pages(); } void Tab::update_ui_items_related_on_parent_preset(const Preset* selected_preset_parent) { @@ -4816,7 +4905,7 @@ bool Tab::select_preset(std::string preset_name, bool delete_current /*=false*/, if (current_dirty || delete_current || print_tab || printer_tab) m_preset_bundle->update_compatible( update_compatible_type(technology_changed, print_tab, (print_tab ? this : wxGetApp().get_tab(Preset::TYPE_PRINT))->m_show_incompatible_presets), - update_compatible_type(technology_changed, false, wxGetApp().get_tab(Preset::TYPE_FILAMENT)->m_show_incompatible_presets)); + update_compatible_type(technology_changed, false, wxGetApp().get_tab(Preset::TYPE_FILAMENT)->m_show_incompatible_presets)); // Initialize the UI from the current preset. if (printer_tab) static_cast(this)->update_pages(); diff --git a/src/slic3r/GUI/Widgets/Button.cpp b/src/slic3r/GUI/Widgets/Button.cpp new file mode 100644 index 000000000..aa817f509 --- /dev/null +++ b/src/slic3r/GUI/Widgets/Button.cpp @@ -0,0 +1,336 @@ +#include "Button.hpp" +#include "Label.hpp" + +#include +#include + +BEGIN_EVENT_TABLE(Button, StaticBox) + +EVT_LEFT_DOWN(Button::mouseDown) +EVT_LEFT_UP(Button::mouseReleased) +EVT_MOUSE_CAPTURE_LOST(Button::mouseCaptureLost) +EVT_KEY_DOWN(Button::keyDownUp) +EVT_KEY_UP(Button::keyDownUp) + +// catch paint events +EVT_PAINT(Button::paintEvent) + +END_EVENT_TABLE() + +/* + * Called by the system of by wxWidgets when the panel needs + * to be redrawn. You can also trigger this call by + * calling Refresh()/Update(). + */ + +Button::Button() + : paddingSize(10, 8) +{ + background_color = StateColor( + std::make_pair(0xF0F0F1, (int) StateColor::Disabled), + std::make_pair(0x37EE7C, (int) StateColor::Hovered | StateColor::Checked), + std::make_pair(0x00AE42, (int) StateColor::Checked), + //std::make_pair(0x009FF3, (int) StateColor::Checked), + std::make_pair(*wxLIGHT_GREY, (int) StateColor::Hovered), + std::make_pair(*wxWHITE, (int) StateColor::Normal)); + text_color = StateColor( + std::make_pair(*wxLIGHT_GREY, (int) StateColor::Disabled), + std::make_pair(*wxBLACK, (int) StateColor::Normal)); +} + +Button::Button(wxWindow* parent, wxString text, wxString icon, long style, int iconSize, wxWindowID btn_id) + : Button() +{ + Create(parent, text, icon, style, iconSize, btn_id); +} + +bool Button::Create(wxWindow* parent, wxString text, wxString icon, long style, int iconSize, wxWindowID btn_id) +{ + StaticBox::Create(parent, btn_id, wxDefaultPosition, wxDefaultSize, style); + state_handler.attach({&text_color}); + state_handler.update_binds(); + //BBS set default font + SetFont(Label::Body_14); + wxWindow::SetLabel(text); + if (!icon.IsEmpty()) { + //BBS set button icon default size to 20 + this->active_icon = ScalableBitmap(this, icon.ToStdString(), iconSize > 0 ? iconSize : 20); + } + messureSize(); + return true; +} + +void Button::SetLabel(const wxString& label) +{ + wxWindow::SetLabel(label); + messureSize(); + Refresh(); +} + +bool Button::SetFont(const wxFont& font) +{ + wxWindow::SetFont(font); + messureSize(); + Refresh(); + return true; +} + +void Button::SetIcon(const wxString& icon) +{ + if (!icon.IsEmpty()) { + //BBS set button icon default size to 20 + this->active_icon = ScalableBitmap(this, icon.ToStdString(), this->active_icon.px_cnt()); + } + else + { + this->active_icon = ScalableBitmap(); + } + Refresh(); +} + +void Button::SetInactiveIcon(const wxString &icon) +{ + if (!icon.IsEmpty()) { + // BBS set button icon default size to 20 + this->inactive_icon = ScalableBitmap(this, icon.ToStdString(), this->active_icon.px_cnt()); + } else { + this->inactive_icon = ScalableBitmap(); + } + Refresh(); +} + +void Button::SetMinSize(const wxSize& size) +{ + minSize = size; + messureSize(); +} + +void Button::SetPaddingSize(const wxSize& size) +{ + paddingSize = size; + messureSize(); +} + +void Button::SetTextColor(StateColor const& color) +{ + text_color = color; + state_handler.update_binds(); + Refresh(); +} + +void Button::SetTextColorNormal(wxColor const &color) +{ + text_color.setColorForStates(color, 0); + Refresh(); +} + +bool Button::Enable(bool enable) +{ + bool result = wxWindow::Enable(enable); + if (result) { + wxCommandEvent e(EVT_ENABLE_CHANGED); + e.SetEventObject(this); + GetEventHandler()->ProcessEvent(e); + } + return result; +} + +void Button::SetCanFocus(bool canFocus) { this->canFocus = canFocus; } + +void Button::SetValue(bool state) +{ + if (GetValue() == state) return; + state_handler.set_state(state ? StateHandler::Checked : 0, StateHandler::Checked); +} + +bool Button::GetValue() const { return state_handler.states() & StateHandler::Checked; } + +void Button::SetCenter(bool isCenter) +{ + this->isCenter = isCenter; +} + +void Button::Rescale() +{ + if (this->active_icon.bmp().IsOk()) + this->active_icon.msw_rescale(); + + if (this->inactive_icon.bmp().IsOk()) + this->inactive_icon.msw_rescale(); + + messureSize(); +} + +void Button::paintEvent(wxPaintEvent& evt) +{ + // depending on your system you may need to look at double-buffered dcs + wxPaintDC dc(this); + render(dc); +} + +/* + * Here we do the actual rendering. I put it in a separate + * method so that it can work no matter what type of DC + * (e.g. wxPaintDC or wxClientDC) is used. + */ +void Button::render(wxDC& dc) +{ + StaticBox::render(dc); + int states = state_handler.states(); + wxSize size = GetSize(); + dc.SetBrush(*wxTRANSPARENT_BRUSH); + // calc content size + wxSize szIcon; + wxSize szContent = textSize.GetSize(); + + ScalableBitmap icon; + if (m_selected || ((states & (int)StateColor::State::Hovered) != 0)) + icon = active_icon; + else + icon = inactive_icon; + int padding = 5; + if (icon.bmp().IsOk()) { + if (szContent.y > 0) { + //BBS norrow size between text and icon + szContent.x += padding; + } + szIcon = icon.GetBmpSize(); + szContent.x += szIcon.x; + if (szIcon.y > szContent.y) + szContent.y = szIcon.y; + if (szContent.x > size.x) { + int d = std::min(padding, szContent.x - size.x); + padding -= d; + szContent.x -= d; + } + } + // move to center + wxRect rcContent = { {0, 0}, size }; + if (isCenter) { + wxSize offset = (size - szContent) / 2; + if (offset.x < 0) offset.x = 0; + rcContent.Deflate(offset.x, offset.y); + } + // start draw + wxPoint pt = rcContent.GetLeftTop(); + if (icon.bmp().IsOk()) { + pt.y += (rcContent.height - szIcon.y) / 2; + dc.DrawBitmap(icon.bmp(), pt); + //BBS norrow size between text and icon + pt.x += szIcon.x + padding; + pt.y = rcContent.y; + } + auto text = GetLabel(); + if (!text.IsEmpty()) { + if (pt.x + textSize.width > size.x) + text = wxControl::Ellipsize(text, dc, wxELLIPSIZE_END, size.x - pt.x); + pt.y += (rcContent.height - textSize.height) / 2; + dc.SetTextForeground(text_color.colorForStates(states)); +#if 0 + dc.SetBrush(*wxLIGHT_GREY); + dc.SetPen(wxPen(*wxLIGHT_GREY)); + dc.DrawRectangle(pt, textSize.GetSize()); +#endif +#ifdef __WXOSX__ + pt.y -= textSize.x / 2; +#endif + dc.DrawText(text, pt); + } +} + +void Button::messureSize() +{ + wxClientDC dc(this); + dc.GetTextExtent(GetLabel(), &textSize.width, &textSize.height, &textSize.x, &textSize.y); + wxSize szContent = textSize.GetSize(); + if (this->active_icon.bmp().IsOk()) { + if (szContent.y > 0) { + //BBS norrow size between text and icon + szContent.x += 5; + } + wxSize szIcon = this->active_icon.GetBmpSize(); + szContent.x += szIcon.x; + if (szIcon.y > szContent.y) + szContent.y = szIcon.y; + } + wxSize size = szContent + paddingSize * 2; + if (minSize.GetHeight() > 0) + size.SetHeight(minSize.GetHeight()); + + if (minSize.GetWidth() > size.GetWidth()) + wxWindow::SetMinSize(minSize); + else + wxWindow::SetMinSize(size); +} + +void Button::mouseDown(wxMouseEvent& event) +{ + event.Skip(); + pressedDown = true; + if (canFocus) + SetFocus(); + if (!HasCapture()) + CaptureMouse(); +} + +void Button::mouseReleased(wxMouseEvent& event) +{ + event.Skip(); + if (pressedDown) { + pressedDown = false; + if (HasCapture()) + ReleaseMouse(); + if (wxRect({0, 0}, GetSize()).Contains(event.GetPosition())) + sendButtonEvent(); + } +} + +void Button::mouseCaptureLost(wxMouseCaptureLostEvent &event) +{ + wxMouseEvent evt; + mouseReleased(evt); +} + +void Button::keyDownUp(wxKeyEvent &event) +{ + if (event.GetKeyCode() == WXK_SPACE || event.GetKeyCode() == WXK_RETURN) { + wxMouseEvent evt(event.GetEventType() == wxEVT_KEY_UP ? wxEVT_LEFT_UP : wxEVT_LEFT_DOWN); + event.SetEventObject(this); + GetEventHandler()->ProcessEvent(evt); + return; + } + if (event.GetEventType() == wxEVT_KEY_DOWN && + (event.GetKeyCode() == WXK_TAB || event.GetKeyCode() == WXK_LEFT || event.GetKeyCode() == WXK_RIGHT + || event.GetKeyCode() == WXK_UP || event.GetKeyCode() == WXK_DOWN)) + HandleAsNavigationKey(event); + else + event.Skip(); +} + +void Button::sendButtonEvent() +{ + wxCommandEvent event(wxEVT_COMMAND_BUTTON_CLICKED, GetId()); + event.SetEventObject(this); + GetEventHandler()->ProcessEvent(event); +} + +#ifdef __WIN32__ + +WXLRESULT Button::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) +{ + if (nMsg == WM_GETDLGCODE) { return DLGC_WANTMESSAGE; } + if (nMsg == WM_KEYDOWN) { + wxKeyEvent event(CreateKeyEvent(wxEVT_KEY_DOWN, wParam, lParam)); + switch (wParam) { + case WXK_RETURN: { // WXK_RETURN key is handled by default button + GetEventHandler()->ProcessEvent(event); + return 0; + } + } + } + return wxWindow::MSWWindowProc(nMsg, wParam, lParam); +} + +#endif + +bool Button::AcceptsFocus() const { return canFocus; } diff --git a/src/slic3r/GUI/Widgets/SwitchButton.cpp b/src/slic3r/GUI/Widgets/SwitchButton.cpp new file mode 100644 index 000000000..f5656062e --- /dev/null +++ b/src/slic3r/GUI/Widgets/SwitchButton.cpp @@ -0,0 +1,157 @@ +#include "SwitchButton.hpp" +#include "Label.hpp" +#include "StaticBox.hpp" + +#include "../wxExtensions.hpp" +#include "../Utils/MacDarkMode.hpp" + +#include +#include +#include + +SwitchButton::SwitchButton(wxWindow* parent, wxWindowID id) + : wxBitmapToggleButton(parent, id, wxNullBitmap, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE | wxBU_EXACTFIT) + , m_on(this, "toggle_on", 16) + , m_off(this, "toggle_off", 16) + , text_color(std::pair{0xfffffe, (int) StateColor::Checked}, std::pair{0x6B6B6B, (int) StateColor::Normal}) + , track_color(0xD9D9D9) + , thumb_color(std::pair{0x009FF3, (int) StateColor::Checked}, std::pair{0xD9D9D9, (int) StateColor::Normal}) +{ + SetBackgroundColour(StaticBox::GetParentBackgroundColor(parent)); + Bind(wxEVT_TOGGLEBUTTON, [this](auto& e) { update(); e.Skip(); }); + SetFont(Label::Body_12); + Rescale(); +} + +void SwitchButton::SetLabels(wxString const& lbl_on, wxString const& lbl_off) +{ + labels[0] = lbl_on; + labels[1] = lbl_off; + Rescale(); +} + +void SwitchButton::SetTextColor(StateColor const& color) +{ + text_color = color; +} + +void SwitchButton::SetTextColor2(StateColor const &color) +{ + text_color2 = color; +} + +void SwitchButton::SetTrackColor(StateColor const& color) +{ + track_color = color; +} + +void SwitchButton::SetThumbColor(StateColor const& color) +{ + thumb_color = color; +} + +void SwitchButton::SetValue(bool value) +{ + if (value != GetValue()) + wxBitmapToggleButton::SetValue(value); + update(); +} + +void SwitchButton::Rescale() +{ + if (labels[0].IsEmpty()) { + m_on.msw_rescale(); + m_off.msw_rescale(); + } + else { + SetBackgroundColour(StaticBox::GetParentBackgroundColor(GetParent())); +#ifdef __WXOSX__ + auto scale = Slic3r::GUI::mac_max_scaling_factor(); + int BS = (int) scale; +#else + constexpr int BS = 1; +#endif + wxSize thumbSize; + wxSize trackSize; + wxClientDC dc(this); +#ifdef __WXOSX__ + dc.SetFont(dc.GetFont().Scaled(scale)); +#endif + wxSize textSize[2]; + { + textSize[0] = dc.GetTextExtent(labels[0]); + textSize[1] = dc.GetTextExtent(labels[1]); + } + float fontScale = 0; + { + thumbSize = textSize[0]; + auto size = textSize[1]; + if (size.x > thumbSize.x) thumbSize.x = size.x; + else size.x = thumbSize.x; + thumbSize.x += BS * 12; + thumbSize.y += BS * 6; + trackSize.x = thumbSize.x + size.x + BS * 10; + trackSize.y = thumbSize.y + BS * 2; + auto maxWidth = GetMaxWidth(); +#ifdef __WXOSX__ + maxWidth *= scale; +#endif + if (trackSize.x > maxWidth) { + fontScale = float(maxWidth) / trackSize.x; + thumbSize.x -= (trackSize.x - maxWidth) / 2; + trackSize.x = maxWidth; + } + } + for (int i = 0; i < 2; ++i) { + wxMemoryDC memdc(&dc); +#ifdef __WXMSW__ + wxBitmap bmp(trackSize.x, trackSize.y); + memdc.SelectObject(bmp); + memdc.SetBackground(wxBrush(GetBackgroundColour())); + memdc.Clear(); +#else + wxImage image(trackSize); + image.InitAlpha(); + memset(image.GetAlpha(), 0, trackSize.GetWidth() * trackSize.GetHeight()); + wxBitmap bmp(std::move(image)); + memdc.SelectObject(bmp); +#endif + memdc.SetFont(dc.GetFont()); + if (fontScale) { + memdc.SetFont(dc.GetFont().Scaled(fontScale)); + textSize[0] = memdc.GetTextExtent(labels[0]); + textSize[1] = memdc.GetTextExtent(labels[1]); + } + auto state = i == 0 ? StateColor::Enabled : (StateColor::Checked | StateColor::Enabled); + { +#ifdef __WXMSW__ + wxGCDC dc2(memdc); +#else + wxDC &dc2(memdc); +#endif + dc2.SetBrush(wxBrush(track_color.colorForStates(state))); + dc2.SetPen(wxPen(track_color.colorForStates(state))); + dc2.DrawRoundedRectangle(wxRect({0, 0}, trackSize), trackSize.y / 2); + dc2.SetBrush(wxBrush(thumb_color.colorForStates(StateColor::Checked | StateColor::Enabled))); + dc2.SetPen(wxPen(thumb_color.colorForStates(StateColor::Checked | StateColor::Enabled))); + dc2.DrawRoundedRectangle(wxRect({ i == 0 ? BS : (trackSize.x - thumbSize.x - BS), BS}, thumbSize), thumbSize.y / 2); + } + memdc.SetTextForeground(text_color.colorForStates(state ^ StateColor::Checked)); + memdc.DrawText(labels[0], {BS + (thumbSize.x - textSize[0].x) / 2, BS + (thumbSize.y - textSize[0].y) / 2}); + memdc.SetTextForeground(text_color2.count() == 0 ? text_color.colorForStates(state) : text_color2.colorForStates(state)); + memdc.DrawText(labels[1], {trackSize.x - thumbSize.x - BS + (thumbSize.x - textSize[1].x) / 2, BS + (thumbSize.y - textSize[1].y) / 2}); + memdc.SelectObject(wxNullBitmap); +#ifdef __WXOSX__ + bmp = wxBitmap(bmp.ConvertToImage(), -1, scale); +#endif + (i == 0 ? m_off : m_on).bmp() = bmp; + } + } + SetSize(m_on.GetBmpSize()); + update(); +} + +void SwitchButton::update() +{ + SetBitmap((GetValue() ? m_on : m_off).bmp()); +}