From 98c8bfe6f4209e0c2597770dfd290880f297a8ab Mon Sep 17 00:00:00 2001 From: Tibor Djurica Potpara Date: Tue, 27 Aug 2024 21:09:45 +0200 Subject: [PATCH] wayland build --- deps/+GLEW/GLEW.cmake | 3 +- deps/+wxWidgets/wxWidgets.cmake | 6 +- src/PrusaSlicer.cpp | 37 ++--- src/slic3r/GUI/PhysicalPrinterDialog.cpp | 49 +++--- src/slic3r/GUI/Plater.cpp | 199 ++++++++++++----------- 5 files changed, 145 insertions(+), 149 deletions(-) diff --git a/deps/+GLEW/GLEW.cmake b/deps/+GLEW/GLEW.cmake index c2db9117b63..280caec26b9 100644 --- a/deps/+GLEW/GLEW.cmake +++ b/deps/+GLEW/GLEW.cmake @@ -5,4 +5,5 @@ add_cmake_project( SOURCE_SUBDIR build/cmake CMAKE_ARGS -DBUILD_UTILS=OFF -) \ No newline at end of file + -DGLEW_EGL=ON +) diff --git a/deps/+wxWidgets/wxWidgets.cmake b/deps/+wxWidgets/wxWidgets.cmake index 77c12ee72f1..9aa11938d9a 100644 --- a/deps/+wxWidgets/wxWidgets.cmake +++ b/deps/+wxWidgets/wxWidgets.cmake @@ -28,8 +28,8 @@ else () endif () add_cmake_project(wxWidgets - URL https://github.com/prusa3d/wxWidgets/archive/323a465e577e03f330e2e6a4c78e564d125340cb.zip - URL_HASH SHA256=B538E4AD3CC93117932F4DED70C476D6650F9C70A9D4055A08F3693864C47465 + URL https://github.com/wxWidgets/wxWidgets/releases/download/v3.2.5/wxWidgets-3.2.5.tar.bz2 + URL_HASH SHA256=0AD86A3AD3E2E519B6A705248FC9226E3A09BBF069C6C692A02ACF7C2D1C6B51 CMAKE_ARGS "-DCMAKE_DEBUG_POSTFIX:STRING=" -DwxBUILD_PRECOMP=ON @@ -50,7 +50,7 @@ add_cmake_project(wxWidgets -DwxUSE_EXPAT=sys -DwxUSE_LIBSDL=OFF -DwxUSE_XTEST=OFF - -DwxUSE_GLCANVAS_EGL=OFF + -DwxUSE_GLCANVAS_EGL=ON -DwxUSE_WEBREQUEST=OFF ${_wx_webview} ${_wx_secretstore} diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp index ccabc07485d..a0676c15fb5 100644 --- a/src/PrusaSlicer.cpp +++ b/src/PrusaSlicer.cpp @@ -85,13 +85,6 @@ int CLI::run(int argc, char **argv) // Save the thread ID of the main thread. save_main_thread_id(); -#ifdef __WXGTK__ - // On Linux, wxGTK has no support for Wayland, and the app crashes on - // startup if gtk3 is used. This env var has to be set explicitly to - // instruct the window manager to fall back to X server mode. - ::setenv("GDK_BACKEND", "x11", /* replace */ true); -#endif - // Switch boost::filesystem to utf8. try { boost::nowide::nowide_filesystem(); @@ -117,7 +110,7 @@ int CLI::run(int argc, char **argv) m_extra_config.apply(m_config, true); m_extra_config.normalize_fdm(); - + PrinterTechnology printer_technology = get_printer_technology(m_config); bool start_gui = m_actions.empty() && @@ -173,7 +166,7 @@ int CLI::run(int argc, char **argv) m_print_config.apply(config); } - bool has_config_from_profiles = m_profiles_sharing.empty() && + bool has_config_from_profiles = m_profiles_sharing.empty() && (!m_config.opt_string("print-profile").empty() || !m_config.option("material-profile")->values.empty() || !m_config.opt_string("printer-profile").empty() ); @@ -351,7 +344,7 @@ int CLI::run(int argc, char **argv) // Initialize full print configs for both the FFF and SLA technologies. FullPrintConfig fff_print_config; SLAFullPrintConfig sla_print_config; - + // Synchronize the default parameters and the ones received on the command line. if (printer_technology == ptFFF) { fff_print_config.apply(m_print_config, true); @@ -359,17 +352,17 @@ int CLI::run(int argc, char **argv) } else { assert(printer_technology == ptSLA); sla_print_config.output_filename_format.value = "[input_filename_base].sl1"; - + // The default bed shape should reflect the default display parameters // and not the fff defaults. double w = sla_print_config.display_width.getFloat(); double h = sla_print_config.display_height.getFloat(); sla_print_config.bed_shape.values = { Vec2d(0, 0), Vec2d(w, 0), Vec2d(w, h), Vec2d(0, h) }; - + sla_print_config.apply(m_print_config, true); m_print_config.apply(sla_print_config, true); } - + { std::string validity = m_print_config.validate(); if (! validity.empty()) { @@ -377,7 +370,7 @@ int CLI::run(int argc, char **argv) return 1; } } - + // Loop through transform options. bool user_center_specified = false; arr2::ArrangeBed bed = arr2::to_arrange_bed(get_bed_shape(m_print_config)); @@ -406,10 +399,10 @@ int CLI::run(int argc, char **argv) model.objects.begin(), model.objects.end(), [](ModelObject* o){ return o->instances.empty(); } ); - + int dups = m_config.opt_int("duplicate"); if (!all_objects_have_instances) model.add_default_instances(); - + try { if (dups > 1) { // if all input objects have defined position(s) apply duplication to the whole model @@ -879,7 +872,7 @@ bool CLI::setup(int argc, char **argv) set_data_dir(get_default_datadir()); } else set_data_dir(provided_datadir); - + //FIXME Validating at this stage most likely does not make sense, as the config is not fully initialized yet. if (!validity.empty()) { boost::nowide::cerr << "error: " << validity << std::endl; @@ -1019,8 +1012,8 @@ bool CLI::processed_profiles_sharing() if (ret.empty()) boost::nowide::cerr << "Printer_model '" << m_config.opt_string("printer_model") << - "' with printer_variant '" << m_config.opt_string("printer_variant") << - "' wasn't found among installed printers." << std::endl << + "' with printer_variant '" << m_config.opt_string("printer_variant") << + "' wasn't found among installed printers." << std::endl << "Or the request can be wrong." << std::endl; } */ @@ -1033,7 +1026,7 @@ bool CLI::processed_profiles_sharing() if (ret.empty()) boost::nowide::cerr << "Printer profile '" << m_config.opt_string("printer-profile") << - "' wasn't found among installed printers." << std::endl << + "' wasn't found among installed printers." << std::endl << "Or the request can be wrong." << std::endl; } else { @@ -1077,9 +1070,9 @@ bool CLI::processed_profiles_sharing() bool CLI::check_and_load_input_profiles(PrinterTechnology& printer_technology) { Slic3r::DynamicPrintConfig config = {}; - std::string ret = Slic3r::load_full_print_config(m_config.opt_string("print-profile"), + std::string ret = Slic3r::load_full_print_config(m_config.opt_string("print-profile"), m_config.option("material-profile")->values, - m_config.opt_string("printer-profile"), + m_config.opt_string("printer-profile"), config, printer_technology); if (!ret.empty()) { boost::nowide::cerr << ret << std::endl; diff --git a/src/slic3r/GUI/PhysicalPrinterDialog.cpp b/src/slic3r/GUI/PhysicalPrinterDialog.cpp index 11ca1249c9b..b4577f7492a 100644 --- a/src/slic3r/GUI/PhysicalPrinterDialog.cpp +++ b/src/slic3r/GUI/PhysicalPrinterDialog.cpp @@ -17,7 +17,7 @@ #include #include #include -#if wxUSE_SECRETSTORE +#if wxUSE_SECRETSTORE #include #endif #include @@ -76,14 +76,14 @@ PresetForPrinter::PresetForPrinter(PhysicalPrinterDialog* parent, const std::str if (m_parent->get_printer()->has_empty_config()) { // update Print Host upload from the selected preset m_parent->get_printer()->update_from_preset(*preset); - // update values in parent (PhysicalPrinterDialog) - } - + // update values in parent (PhysicalPrinterDialog) + } + // update PrinterTechnology if it was changed if (m_presets_list->set_printer_technology(preset->printer_technology())) m_parent->set_printer_technology(preset->printer_technology()); - else + else m_parent->update(true); update_full_printer_name(); @@ -136,7 +136,7 @@ std::string PresetForPrinter::get_preset_name() void PresetForPrinter::SuppressDelete() { m_delete_preset_btn->Enable(false); - + // this case means that now we have only one related preset for the printer // So, allow any selection m_presets_list->set_printer_technology(ptAny); @@ -162,7 +162,7 @@ namespace { bool is_secret_store_ok() { -#if wxUSE_SECRETSTORE +#if wxUSE_SECRETSTORE wxSecretStore store = wxSecretStore::GetDefault(); wxString errmsg; if (!store.IsOk(&errmsg)) { @@ -176,7 +176,7 @@ bool is_secret_store_ok() } bool save_secret(const std::string& id, const std::string& opt, const std::string& usr, const std::string& psswd) { -#if wxUSE_SECRETSTORE +#if wxUSE_SECRETSTORE wxSecretStore store = wxSecretStore::GetDefault(); wxString errmsg; if (!store.IsOk(&errmsg)) { @@ -198,7 +198,7 @@ bool save_secret(const std::string& id, const std::string& opt, const std::strin #else BOOST_LOG_TRIVIAL(error) << "wxUSE_SECRETSTORE not supported. Cannot save password to the system store."; return false; -#endif // wxUSE_SECRETSTORE +#endif // wxUSE_SECRETSTORE } bool load_secret(const std::string& id, const std::string& opt, std::string& usr, std::string& psswd) { @@ -226,7 +226,7 @@ bool load_secret(const std::string& id, const std::string& opt, std::string& usr #else BOOST_LOG_TRIVIAL(error) << "wxUSE_SECRETSTORE not supported. Cannot load password from the system store."; return false; -#endif // wxUSE_SECRETSTORE +#endif // wxUSE_SECRETSTORE } } @@ -258,7 +258,7 @@ PhysicalPrinterDialog::PhysicalPrinterDialog(wxWindow* parent, wxString printer_ m_add_preset_btn = new ScalableButton(this, wxID_ANY, "add_copies"); m_add_preset_btn->SetFont(wxGetApp().normal_font()); - m_add_preset_btn->SetToolTip(_L("Add preset for this printer device")); + m_add_preset_btn->SetToolTip(_L("Add preset for this printer device")); m_add_preset_btn->Bind(wxEVT_BUTTON, &PhysicalPrinterDialog::AddPreset, this); m_printer_name = new ::TextInput(this,printer_name); @@ -326,7 +326,7 @@ PhysicalPrinterDialog::PhysicalPrinterDialog(wxWindow* parent, wxString printer_ topSizer->Add(nameSizer , 0, wxEXPAND | wxLEFT | wxRIGHT, BORDER_W); topSizer->Add(m_presets_sizer , 0, wxEXPAND | wxLEFT | wxRIGHT, BORDER_W); topSizer->Add(m_optgroup->sizer , 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, BORDER_W); - topSizer->Add(btns , 0, wxEXPAND | wxALL, BORDER_W); + topSizer->Add(btns , 0, wxEXPAND | wxALL, BORDER_W); SetSizer(topSizer); topSizer->SetSizeHints(this); @@ -390,7 +390,7 @@ void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgr return sizer; }; - auto printhost_browse = [=](wxWindow* parent) + auto printhost_browse = [=](wxWindow* parent) { auto sizer = create_sizer_with_btn(parent, &m_printhost_browse_btn, "browse", _L("Browse") + " " + dots); m_printhost_browse_btn->Bind(wxEVT_BUTTON, [=](wxCommandEvent& e) { @@ -510,7 +510,7 @@ void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgr m_optgroup->append_line(cafile_hint); } else { - + Line line{ "", "" }; line.full_width = 1; @@ -529,7 +529,7 @@ void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgr m_optgroup->append_line(line); } - // Text line with info how passwords and api keys are stored + // Text line with info how passwords and api keys are stored if (is_secret_store_ok()) { Line line{ "", "" }; line.full_width = 1; @@ -561,7 +561,7 @@ void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgr m_optgroup->append_line(line); } - for (const std::string& opt_key : std::vector{ "printhost_user", "printhost_password" }) { + for (const std::string& opt_key : std::vector{ "printhost_user", "printhost_password" }) { option = m_optgroup->get_option(opt_key); option.opt.width = Field::def_width_wider(); m_optgroup->append_single_option_line(option); @@ -606,7 +606,8 @@ void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgr // Always fill in the "printhost_port" combo box from the config and select it. { Choice* choice = dynamic_cast(m_optgroup->get_field("printhost_port")); - choice->set_values({ m_config->opt_string("printhost_port") }); + std::vector vals { m_config->opt_string("printhost_port") }; + choice->set_values(vals); choice->set_selection(); } @@ -637,7 +638,7 @@ void PhysicalPrinterDialog::update(bool printer_change) AuthorizationType auth_type = m_config->option>("printhost_authorization_type")->value; m_optgroup->show_field("printhost_apikey", auth_type == AuthorizationType::atKeyPassword); for (const char* opt_key : { "printhost_user", "printhost_password" }) - m_optgroup->show_field(opt_key, auth_type == AuthorizationType::atUserPassword); + m_optgroup->show_field(opt_key, auth_type == AuthorizationType::atUserPassword); } else { m_optgroup->hide_field("printhost_authorization_type"); m_optgroup->show_field("printhost_apikey", true); @@ -666,7 +667,7 @@ void PhysicalPrinterDialog::update(bool printer_change) m_stored_host = temp_host; } } - + m_last_host_type = opt->value; } else { @@ -702,7 +703,7 @@ void PhysicalPrinterDialog::update_host_type(bool printer_change) bool supported { true }; wxString label; } link, connect; - // allowed models are: all MINI, all MK3 and newer, MK2.5 and MK2.5S + // allowed models are: all MINI, all MK3 and newer, MK2.5 and MK2.5S auto model_supports_prusalink = [](const std::string& model) { return model.size() >= 2 && (( boost::starts_with(model, "MK") && model[2] > '2' && model[2] <= '9') @@ -711,7 +712,7 @@ void PhysicalPrinterDialog::update_host_type(bool printer_change) || boost::starts_with(model, "XL") ); }; - // allowed models are: all MK3/S and MK2.5/S. + // allowed models are: all MK3/S and MK2.5/S. // Since 2.6.2 also MINI, which makes list of supported printers same for both services. // Lets keep these 2 functions separated for now. auto model_supports_prusaconnect = [](const std::string& model) { @@ -727,7 +728,7 @@ void PhysicalPrinterDialog::update_host_type(bool printer_change) for (PresetForPrinter* prstft : m_presets) { std::string preset_name = prstft->get_preset_name(); if (Preset* preset = wxGetApp().preset_bundle->printers.find_preset(preset_name)) { - std::string model_id = preset->config.opt_string("printer_model"); + std::string model_id = preset->config.opt_string("printer_model"); if (preset->vendor) { if (preset->vendor->name == "Prusa Research") { const std::vector& models = preset->vendor->models; @@ -824,7 +825,7 @@ void PhysicalPrinterDialog::update_full_printer_names() if (pos != std::string::npos) { wxString str = printer_name.SubString(pos, 1); printer_name.Remove(pos, 1); - InfoDialog(this, format_wxstr("%1%: \"%2%\" ", _L("Unexpected character"), str), + InfoDialog(this, format_wxstr("%1%: \"%2%\" ", _L("Unexpected character"), str), _L("The following characters are not allowed in the name") + ": " + unusable_symbols).ShowModal(); m_printer_name->SetValue(printer_name); m_printer_name->GetTextCtrl()->SetInsertionPointEnd(); @@ -896,7 +897,7 @@ void PhysicalPrinterDialog::OnOK(wxEvent& event) } } } - + PhysicalPrinterCollection& printers = wxGetApp().preset_bundle->physical_printers; const PhysicalPrinter* existing = printers.find_printer(into_u8(printer_name), false); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index bbaf4c5d731..d66331efc7f 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -52,7 +52,7 @@ #include #include #include -#if wxUSE_SECRETSTORE +#if wxUSE_SECRETSTORE #include #endif @@ -273,7 +273,7 @@ struct Plater::priv std::unique_ptr preset_archive_database; ProjectDirtyStateManager dirty_state; - + BackgroundSlicingProcess background_process; bool suppressed_backround_processing_update { false }; @@ -346,7 +346,7 @@ struct Plater::priv get_app_config()->set(act_key, res == wxID_YES ? "1" : "0"); } - } + } else res = (act == "1") ? wxID_YES : wxID_NO; @@ -504,7 +504,7 @@ struct Plater::priv // Update notification manager with the current state of warnings produced by the background process (slicing). void actualize_slicing_warnings(const PrintBase &print); void actualize_object_warnings(const PrintBase& print); - // Displays dialog window with list of warnings. + // Displays dialog window with list of warnings. // Returns true if user clicks OK. // Returns true if current_warnings vector is empty without showning the dialog bool warnings_dialog(); @@ -567,7 +567,7 @@ struct Plater::priv std::string last_output_dir_path; bool inside_snapshot_capture() { return m_prevent_snapshots != 0; } bool process_completed_with_error { false }; - + private: bool layers_height_allowed() const; @@ -592,7 +592,7 @@ struct Plater::priv // vector of all warnings generated by last slicing std::vector> current_warnings; bool show_warning_dialog { false }; - + }; const std::regex Plater::priv::pattern_bundle(".*[.](amf|amf[.]xml|zip[.]amf|3mf|prusa)", std::regex::icase); @@ -613,8 +613,8 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) "extruder_colour", "filament_colour", "material_colour", "max_print_height", "printer_model", "printer_notes", "printer_technology", // These values are necessary to construct SlicingParameters by the Canvas3D variable layer height editor. "layer_height", "first_layer_height", "min_layer_height", "max_layer_height", - "brim_width", "perimeters", "perimeter_extruder", "fill_density", "infill_extruder", "top_solid_layers", - "support_material", "support_material_extruder", "support_material_interface_extruder", + "brim_width", "perimeters", "perimeter_extruder", "fill_density", "infill_extruder", "top_solid_layers", + "support_material", "support_material_extruder", "support_material_interface_extruder", "support_material_contact_distance", "support_material_bottom_contact_distance", "raft_layers" })) , sidebar(new Sidebar(q)) @@ -805,7 +805,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) } }); this->q->Bind(EVT_REMOVABLE_DRIVES_CHANGED, [this, q](RemovableDrivesChangedEvent &) { - q->show_action_buttons(); + q->show_action_buttons(); // Close notification ExportingFinished but only if last export was to removable notification_manager->device_ejected(); }); @@ -818,14 +818,14 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) } else { // at startup, show only notification notification_manager->push_notification(NotificationType::WifiConfigFileDetected , NotificationManager::NotificationLevel::ImportantNotificationLevel - // TRN Text of notification when Slicer starts and usb stick with printer settings ini file is present + // TRN Text of notification when Slicer starts and usb stick with printer settings ini file is present , _u8L("Printer configuration file detected on removable media.") - // TRN Text of hypertext of notification when Slicer starts and usb stick with printer settings ini file is present + // TRN Text of hypertext of notification when Slicer starts and usb stick with printer settings ini file is present , _u8L("Write Wi-Fi credentials."), [evt/*, CONFIG_FILE_NAME*/](wxEvtHandler* evt_hndlr){ wxGetApp().open_wifi_config_dialog(true, evt.GetString()); return true;}); } - + }); // Start the background thread and register this window as a target for update events. @@ -842,7 +842,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) // Reset the "dirty project" flag. m_undo_redo_stack_main.mark_current_as_saved(); dirty_state.update_from_undo_redo_stack(false); - + this->q->Bind(EVT_LOAD_MODEL_OTHER_INSTANCE, [this](LoadFromOtherInstanceEvent& evt) { BOOST_LOG_TRIVIAL(trace) << "Received load from other instance event."; wxArrayString input_files; @@ -864,7 +864,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) for (size_t i = 0; i < evt.data.size(); ++i) { wxGetApp().start_download(evt.data[i]); } - }); + }); this->q->Bind(EVT_LOGIN_OTHER_INSTANCE, [this](LoginOtherInstanceEvent& evt) { BOOST_LOG_TRIVIAL(trace) << "Received login from other instance event."; user_account->on_login_code_recieved(evt.data); @@ -882,7 +882,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) } user_account->on_login_code_recieved(dialog_msg); }); - + this->q->Bind(EVT_UA_LOGGEDOUT, [this](UserAccountSuccessEvent& evt) { user_account->clear(); std::string text = _u8L("Logged out from Prusa Account."); @@ -914,7 +914,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) this->main_frame->refresh_account_menu(); wxGetApp().update_wizard_login_page(); this->show_action_buttons(this->ready_to_slice); - + } else { // data were corrupt and username was not retrieved // procced as if EVT_UA_RESET was recieved @@ -928,7 +928,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) // Update sidebar printer status sidebar->update_printer_presets_combobox(); } - + }); this->q->Bind(EVT_UA_RESET, [this](UserAccountFailEvent& evt) { BOOST_LOG_TRIVIAL(error) << "Reseting Prusa Account communication. Error message: " << evt.data; @@ -972,7 +972,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) }); this->q->Bind(EVT_UA_AVATAR_SUCCESS, [this](UserAccountSuccessEvent& evt) { boost::filesystem::path path = user_account->get_avatar_path(true); - FILE* file; + FILE* file; file = boost::nowide::fopen(path.generic_string().c_str(), "wb"); if (file == NULL) { BOOST_LOG_TRIVIAL(error) << "Failed to create file to store avatar picture at: " << path; @@ -981,7 +981,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) fwrite(evt.data.c_str(), 1, evt.data.size(), file); fclose(file); this->main_frame->refresh_account_menu(true); - }); + }); this->q->Bind(EVT_UA_PRUSACONNECT_PRINTER_DATA_SUCCESS, [this](UserAccountSuccessEvent& evt) { this->user_account->set_current_printer_data(evt.data); wxGetApp().handle_connect_request_printer_select_inner(evt.data); @@ -996,7 +996,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) this->q->Bind(EVT_UA_REFRESH_TIME, [this](UserAccountTimeEvent& evt) { this->user_account->set_refresh_time(evt.data); - }); + }); } wxGetApp().other_instance_message_handler()->init(this->q); @@ -1168,9 +1168,9 @@ std::vector Plater::priv::load_files(const std::vector& input_ Slic3r::ScopeGuard([&progress_dlg](){ if (progress_dlg) progress_dlg->Destroy(); progress_dlg = nullptr; }); #else wxProgressDialog progress_dlg_stack(loading, "", 100, find_toplevel_parent(q), wxPD_APP_MODAL | wxPD_AUTO_HIDE); - wxProgressDialog* progress_dlg = &progress_dlg_stack; + wxProgressDialog* progress_dlg = &progress_dlg_stack; #endif - + wxBusyCursor busy; @@ -1181,7 +1181,7 @@ std::vector Plater::priv::load_files(const std::vector& input_ int answer_convert_from_imperial_units = wxOK_DEFAULT; int answer_consider_as_multi_part_objects = wxOK_DEFAULT; - bool in_temp = false; + bool in_temp = false; const fs::path temp_path = wxStandardPaths::Get().GetTempDir().utf8_str().data(); size_t input_files_size = input_files.size(); @@ -1221,7 +1221,7 @@ std::vector Plater::priv::load_files(const std::vector& input_ #ifdef __linux__ // On Linux Constructor of the ProgressDialog calls DisableOtherWindows() function which causes a disabling of all children of the find_toplevel_parent(q) // And a destructor of the ProgressDialog calls ReenableOtherWindows() function which revert previously disabled children. - // But if printer technology will be changes during project loading, + // But if printer technology will be changes during project loading, // then related SLA Print and Materials Settings or FFF Print and Filaments Settings will be unparent from the wxNoteBook // and that is why they will never be enabled after destruction of the ProgressDialog. // So, distroy progress_gialog if we are loading project file @@ -1294,8 +1294,8 @@ std::vector Plater::priv::load_files(const std::vector& input_ // For exporting from the amf/3mf we shouldn't check printer_presets for the containing information about "Print Host upload" wxGetApp().load_current_presets(false); - // Update filament colors for the MM-printer profile in the full config - // to avoid black (default) colors for Extruders in the ObjectList, + // Update filament colors for the MM-printer profile in the full config + // to avoid black (default) colors for Extruders in the ObjectList, // when for extruder colors are used filament colors q->update_filament_colors_in_full_config(); is_project_file = true; @@ -1357,7 +1357,7 @@ std::vector Plater::priv::load_files(const std::vector& input_ int answer = dlg.ShowModal(); if (dlg.IsCheckBoxChecked()) answer_convert_from_meters = answer; - else + else convert_model_if(model, answer == wxID_YES); } convert_model_if(model, answer_convert_from_meters == wxID_YES); @@ -1379,7 +1379,7 @@ std::vector Plater::priv::load_files(const std::vector& input_ int answer = dlg.ShowModal(); if (dlg.IsCheckBoxChecked()) answer_convert_from_imperial_units = answer; - else + else convert_model_if(model, answer == wxID_YES); } convert_model_if(model, answer_convert_from_imperial_units == wxID_YES); @@ -1437,7 +1437,7 @@ std::vector Plater::priv::load_files(const std::vector& input_ std::string preset_name = wxGetApp().preset_bundle->get_preset_name_by_alias_invisible(Preset::Type::TYPE_SLA_MATERIAL, Preset::remove_suffix_modified(material.first)); Preset* prst = wxGetApp().preset_bundle->sla_materials.find_preset(preset_name, false); - if (!prst) { //did not find compatible profile + if (!prst) { //did not find compatible profile // try find alias of material comaptible with another print profile - if exists, use the print profile auto& prints = wxGetApp().preset_bundle->sla_prints; std::string edited_print_name = prints.get_edited_preset().name; @@ -1529,7 +1529,7 @@ std::vector Plater::priv::load_files(const std::vector& input_ // this is required because the selected object changed and the flatten on face an sla support gizmos need to be updated accordingly view3D->get_canvas3d()->update_gizmos_on_off_state(); } - + GLGizmoSimplify::add_simplify_suggestion_notification( obj_idxs, model.objects, *notification_manager); @@ -1572,7 +1572,7 @@ std::vector Plater::priv::load_model_objects(const ModelObjectPtrs& mode #endif /* AUTOPLACEMENT_ON_LOAD */ } - for (size_t i = 0; i < object->instances.size() + for (size_t i = 0; i < object->instances.size() && !object->is_cut() // don't apply scaled_down functionality to cut objects ; ++i) { ModelInstance* instance = object->instances[i]; @@ -1725,7 +1725,7 @@ wxString Plater::priv::get_export_file(GUI::FileType file_type) std::string out_dir = (boost::filesystem::path(output_file).parent_path()).string(); std::string temp_dir = wxStandardPaths::Get().GetTempDir().utf8_str().data(); - + wxFileDialog dlg(q, dlg_title, out_dir == temp_dir ? from_u8(wxGetApp().app_config->get("last_output_path")) : (is_shapes_dir(out_dir) ? from_u8(wxGetApp().app_config->get_last_dir()) : from_path(output_file.parent_path())), from_path(output_file.filename()), wildcard, wxFD_SAVE | wxFD_OVERWRITE_PROMPT); @@ -1831,10 +1831,10 @@ bool Plater::priv::delete_object_from_model(size_t obj_idx) // show warning message that "cut consistancy" will not be supported any more ModelObject* obj = model.objects[obj_idx]; if (obj->is_cut()) { - InfoDialog dialog(q, _L("Delete object which is a part of cut object"), - _L("You try to delete an object which is a part of a cut object.") + "\n" + + InfoDialog dialog(q, _L("Delete object which is a part of cut object"), + _L("You try to delete an object which is a part of a cut object.") + "\n" + _L("This action will break a cut information.\n" - "After that PrusaSlicer can't guarantee model consistency"), + "After that PrusaSlicer can't guarantee model consistency"), false, wxYES | wxCANCEL | wxCANCEL_DEFAULT | wxICON_WARNING); dialog.SetButtonLabel(wxID_YES, _L("Delete object")); if (dialog.ShowModal() == wxID_CANCEL) @@ -1929,7 +1929,7 @@ void Plater::priv::split_object() int obj_idx = get_selected_object_idx(); if (obj_idx == -1) return; - + // we clone model object because split_object() adds the split volumes // into the same model object, thus causing duplicates when we call load_model_objects() Model new_model = model; @@ -2132,7 +2132,7 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool if (invalidated == Print::APPLY_STATUS_UNCHANGED && !background_process.empty()) { if (printer_technology == ptFFF) { // Object manipulation with gizmos may end up in a null transformation. - // In this case, we need to trigger the completion of the sequential print clearance contours evaluation + // In this case, we need to trigger the completion of the sequential print clearance contours evaluation GLCanvas3D* canvas = view3D->get_canvas3d(); if (canvas->is_sequential_print_clearance_evaluating()) { GLCanvas3D::ContoursList contours; @@ -2145,7 +2145,7 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool if (!err.empty()) return return_state; } - + if (! this->delayed_error_message.empty()) // Reusing the old state. return_state |= UPDATE_BACKGROUND_PROCESS_INVALID; @@ -2158,8 +2158,8 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool actualize_slicing_warnings(*this->background_process.current_print()); actualize_object_warnings(*this->background_process.current_print()); show_warning_dialog = false; - process_completed_with_error = false; - } + process_completed_with_error = false; + } if (invalidated != Print::APPLY_STATUS_UNCHANGED && was_running && ! this->background_process.running() && (return_state & UPDATE_BACKGROUND_PROCESS_RESTART) == 0) { @@ -2230,7 +2230,7 @@ bool Plater::priv::restart_background_process(unsigned int state) void Plater::priv::export_gcode(fs::path output_path, bool output_path_on_removable_media, PrintHostJob upload_job) { wxCHECK_RET(!(output_path.empty() && upload_job.empty()), "export_gcode: output_path and upload_job empty"); - + if (model.objects.empty()) return; @@ -2278,7 +2278,7 @@ void Plater::priv::update_fff_scene() if (this->preview != nullptr) this->preview->reload_print(); // In case this was MM print, wipe tower bounding box on 3D tab might need redrawing with exact depth: - view3D->reload_scene(true); + view3D->reload_scene(true); } void Plater::priv::update_sla_scene() @@ -2851,7 +2851,7 @@ void Plater::priv::on_slicing_update(SlicingStatusEvent &evt) , "https://help.prusa3d.com/" ); BOOST_LOG_TRIVIAL(warning) << message_notif; - notification_manager->push_slicing_warning_notification(message_notif, false, 0, 0, "https://help.prusa3d.com/", + notification_manager->push_slicing_warning_notification(message_notif, false, 0, 0, "https://help.prusa3d.com/", [](wxEvtHandler* evnthndlr) { wxGetApp().open_browser_with_warning_dialog("https://help.prusa3d.com/article/template-filaments_467599"); return false; } ); add_warning({ PrintStateBase::WarningLevel::CRITICAL, true, message_dial, 0}, 0); @@ -2890,7 +2890,7 @@ void Plater::priv::on_slicing_update(SlicingStatusEvent &evt) int warning_step = evt.status.warning_step; PrintStateBase::StateWithWarnings state; if (evt.status.flags & PrintBase::SlicingStatus::UPDATE_PRINT_STEP_WARNINGS) { - state = this->printer_technology == ptFFF ? + state = this->printer_technology == ptFFF ? this->fff_print.step_state_with_warnings(static_cast(warning_step)) : this->sla_print.step_state_with_warnings(static_cast(warning_step)); } else if (this->printer_technology == ptFFF) { @@ -2927,7 +2927,7 @@ void Plater::priv::on_slicing_completed(wxCommandEvent & evt) void Plater::priv::on_export_began(wxCommandEvent& evt) { if (show_warning_dialog) - warnings_dialog(); + warnings_dialog(); } void Plater::priv::on_slicing_began() { @@ -2942,7 +2942,7 @@ void Plater::priv::add_warning(const Slic3r::PrintStateBase::Warning& warning, s if (warning.message_id == it.first.message_id) { if (warning.message_id != 0 || (warning.message_id == 0 && warning.message == it.first.message)) return; - } + } } current_warnings.emplace_back(std::pair(warning, oid)); } @@ -2988,7 +2988,7 @@ bool Plater::priv::warnings_dialog() for (auto const& it : current_critical_warnings) { size_t next_n = it.first.message.find_first_of('\n', 0); text += "\n"; - if (next_n != std::string::npos) + if (next_n != std::string::npos) text += it.first.message.substr(0, next_n); else text += it.first.message; @@ -3055,7 +3055,7 @@ void Plater::priv::on_process_completed(SlicingProcessCompletedEvent &evt) else this->update_sla_scene(); } - + if (evt.cancelled()) { if (wxGetApp().get_mode() == comSimple) sidebar->set_btn_label(ActionButtonType::Reslice, "Slice now"); @@ -3141,7 +3141,7 @@ void Plater::priv::on_right_click(RBtnEvent& evt) if (evt.data.second) return; - // Each context menu respects to the selected item in ObjectList, + // Each context menu respects to the selected item in ObjectList, // so this selection should be updated before menu creation wxGetApp().obj_list()->update_selections(); @@ -3150,8 +3150,8 @@ void Plater::priv::on_right_click(RBtnEvent& evt) // else { const Selection& selection = get_selection(); // show "Object menu" for each one or several FullInstance instead of FullObject - const bool is_some_full_instances = selection.is_single_full_instance() || - selection.is_single_full_object() || + const bool is_some_full_instances = selection.is_single_full_instance() || + selection.is_single_full_object() || selection.is_multiple_full_instance(); const bool is_part = selection.is_single_volume_or_modifier() && ! selection.is_any_connector(); if (is_some_full_instances) @@ -3160,7 +3160,7 @@ void Plater::priv::on_right_click(RBtnEvent& evt) const GLVolume* gl_volume = selection.get_first_volume(); const ModelVolume *model_volume = get_model_volume(*gl_volume, selection.get_model()->objects); menu = (model_volume != nullptr && model_volume->is_text()) ? menus.text_part_menu() : - (model_volume != nullptr && model_volume->is_svg()) ? menus.svg_part_menu() : + (model_volume != nullptr && model_volume->is_svg()) ? menus.svg_part_menu() : menus.part_menu(); } else menu = menus.multi_selection_menu(); @@ -3562,7 +3562,7 @@ bool Plater::priv::can_simplify() const const int obj_idx = get_selected_object_idx(); // is object for simplification selected // cut object can't be simplify - if (obj_idx < 0 || model.objects[obj_idx]->is_cut()) + if (obj_idx < 0 || model.objects[obj_idx]->is_cut()) return false; // is already opened? @@ -3578,7 +3578,7 @@ bool Plater::priv::can_increase_instances() const || q->canvas3D()->get_gizmos_manager().is_in_editing_mode()) return false; - // Disallow arrange and add instance when emboss gizmo is opend + // Disallow arrange and add instance when emboss gizmo is opend // Prevent strobo effect during editing emboss parameters. if (q->canvas3D()->get_gizmos_manager().get_current_type() == GLGizmosManager::Emboss) return false; @@ -3603,7 +3603,7 @@ bool Plater::priv::can_decrease_instances(int obj_idx /*= -1*/) const return false; } - return obj_idx < (int)model.objects.size() && + return obj_idx < (int)model.objects.size() && (model.objects[obj_idx]->instances.size() > 1) && !sidebar->obj_list()->has_selected_cut_object(); } @@ -3643,9 +3643,9 @@ bool Plater::priv::can_show_upload_to_connect() const if (parent && parent->vendor) { vendor_id = parent->vendor->id; } - } + } return vendor_id.compare(0, 5, "Prusa") == 0; -} +} void Plater::priv::show_action_buttons(const bool ready_to_slice_) const { @@ -3672,7 +3672,7 @@ void Plater::priv::show_action_buttons(const bool ready_to_slice_) const else { RemovableDriveManager::RemovableDrivesStatus removable_media_status; - if (! ready_to_slice) + if (! ready_to_slice) removable_media_status = wxGetApp().removable_drive_manager()->status(); if (sidebar->show_reslice(ready_to_slice) | sidebar->show_export(!ready_to_slice) | @@ -3806,7 +3806,7 @@ void Plater::priv::undo_redo_to(std::vector::const_iterator if (printer_technology_changed) { // Switching the printer technology when jumping forwards / backwards in time. Switch to the last active printer profile of the other type. std::string s_pt = (it_snapshot->snapshot_data.printer_technology == ptFFF) ? "FFF" : "SLA"; - if (!wxGetApp().check_and_save_current_preset_changes(_L("Undo / Redo is processing"), + if (!wxGetApp().check_and_save_current_preset_changes(_L("Undo / Redo is processing"), // format_wxstr(_L("%1% printer was active at the time the target Undo / Redo snapshot was taken. Switching to %1% printer requires reloading of %1% presets."), s_pt))) format_wxstr(_L("Switching the printer technology from %1% to %2%.\n" "Some %1% presets were modified, which will be lost after switching the printer technology."), s_pt =="FFF" ? "SLA" : "FFF", s_pt), false)) @@ -3927,7 +3927,7 @@ void Plater::priv::update_after_undo_redo(const UndoRedo::Snapshot& snapshot, bo view3D->set_as_dirty(); } - // this->update() above was called with POSTPONE_VALIDATION_ERROR_MESSAGE, so that if an error message was generated when updating the back end, it would not open immediately, + // this->update() above was called with POSTPONE_VALIDATION_ERROR_MESSAGE, so that if an error message was generated when updating the back end, it would not open immediately, // but it would be saved to be show later. Let's do it now. We do not want to display the message box earlier, because on Windows & OSX the message box takes over the message // queue pump, which in turn executes the rendering function before a full update after the Undo / Redo jump. this->show_delayed_error_message(); @@ -4003,7 +4003,7 @@ void Plater::new_project() if (int saved_project = p->save_project_if_dirty(_L("Creating a new project while the current project is modified.")); saved_project == wxID_CANCEL) return; else { - wxString header = _L("Creating a new project while some presets are modified.") + "\n" + + wxString header = _L("Creating a new project while some presets are modified.") + "\n" + (saved_project == wxID_YES ? _L("You can keep presets modifications to the new project or discard them") : _L("You can keep presets modifications to the new project, discard them or save changes as new presets.\n" "Note, if changes will be saved then new project wouldn't keep them")); @@ -4048,7 +4048,8 @@ void Plater::load_project(const wxString& filename) p->reset(); - if (! load_files({ into_path(filename) }).empty()) { + std::vector input_files { into_path(filename) }; + if (! load_files(input_files).empty()) { // At least one file was loaded. p->set_project_filename(filename); // Save the names of active presets and project specific config into ProjectDirtyStateManager. @@ -4423,13 +4424,13 @@ class LoadProjectsDialog : public DPIDialog int get_action() const { return m_action + 1; } bool get_all() const { return m_all; } - int get_selected() const - { - if (m_combo_project && m_combo_project->IsEnabled()) - return m_combo_project->GetSelection(); - else if (m_combo_config && m_combo_config->IsEnabled()) - return m_combo_config->GetSelection(); - else + int get_selected() const + { + if (m_combo_project && m_combo_project->IsEnabled()) + return m_combo_project->GetSelection(); + else if (m_combo_config && m_combo_config->IsEnabled()) + return m_combo_config->GetSelection(); + else return -1; } protected: @@ -4449,14 +4450,14 @@ LoadProjectsDialog::LoadProjectsDialog(const std::vector& paths) if (contains_projects) main_sizer->Add(new wxStaticText(this, wxID_ANY, get_wraped_wxString(_L("There are several files being loaded, including Project files.") + "\n" + _L("Select an action to apply to all files."))), 0, wxEXPAND | wxALL, 10); - else + else main_sizer->Add(new wxStaticText(this, wxID_ANY, get_wraped_wxString(_L("There are several files being loaded.") + "\n" + _L("Select an action to apply to all files."))), 0, wxEXPAND | wxALL, 10); wxStaticBox* action_stb = new wxStaticBox(this, wxID_ANY, _L("Action")); if (!wxOSX) action_stb->SetBackgroundStyle(wxBG_STYLE_PAINT); action_stb->SetFont(wxGetApp().normal_font()); - + if (contains_projects) { wxArrayString filenames; for (const fs::path& path : paths) { @@ -4465,14 +4466,14 @@ LoadProjectsDialog::LoadProjectsDialog(const std::vector& paths) m_combo_project = new wxComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, filenames, wxCB_READONLY); m_combo_project->SetValue(filenames.front()); m_combo_project->Enable(false); - + m_combo_config = new wxComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, filenames, wxCB_READONLY); m_combo_config->SetValue(filenames.front()); m_combo_config->Enable(false); } wxStaticBoxSizer* stb_sizer = new wxStaticBoxSizer(action_stb, wxVERTICAL); int id = 0; - + // all geometry wxRadioButton* btn = new wxRadioButton(this, wxID_ANY, _L("Import 3D models"), wxDefaultPosition, wxDefaultSize, id == 0 ? wxRB_GROUP : 0); btn->SetValue(id == m_action); @@ -4568,11 +4569,11 @@ bool Plater::preview_zip_archive(const boost::filesystem::path& archive_path) std::vector> selected_paths; FileArchiveDialog dlg(static_cast(wxGetApp().mainframe), &archive, selected_paths); if (dlg.ShowModal() == wxID_OK) - { + { std::string archive_path_string = archive_path.string(); archive_path_string = archive_path_string.substr(0, archive_path_string.size() - 4); fs::path archive_dir(wxStandardPaths::Get().GetTempDir().utf8_str().data()); - + for (auto& path_w_size : selected_paths) { const fs::path& path = path_w_size.first; size_t size = path_w_size.second; @@ -4594,7 +4595,7 @@ bool Plater::preview_zip_archive(const boost::filesystem::path& archive_path) if (archive_path.empty()) continue; - if (path != archive_path) + if (path != archive_path) continue; // decompressing try @@ -4615,7 +4616,7 @@ bool Plater::preview_zip_archive(const boost::filesystem::path& archive_path) filename = final_filename + extension; fs::path final_path = archive_dir / filename; std::string buffer((size_t)stat.m_uncomp_size, 0); - // Decompress action. We already has correct file index in stat structure. + // Decompress action. We already has correct file index in stat structure. mz_bool res = mz_zip_reader_extract_to_mem(&archive, stat.m_file_index, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0); if (res == 0) { // TRN: First argument = path to file, second argument = error description @@ -4660,15 +4661,15 @@ bool Plater::preview_zip_archive(const boost::filesystem::path& archive_path) } close_zip_reader(&archive); if (non_project_paths.size() + project_paths.size() != selected_paths.size()) - BOOST_LOG_TRIVIAL(error) << "Decompresing of archive did not retrieve all files. Expected files: " - << selected_paths.size() - << " Decopressed files: " + BOOST_LOG_TRIVIAL(error) << "Decompresing of archive did not retrieve all files. Expected files: " + << selected_paths.size() + << " Decopressed files: " << non_project_paths.size() + project_paths.size(); } else { close_zip_reader(&archive); return false; } - + } catch (const Slic3r::FileIOError& e) { // zip reader should be already closed or not even opened @@ -4759,7 +4760,7 @@ bool Plater::preview_zip_archive(const boost::filesystem::path& archive_path) if (!delete_after) return true; -#else +#else // 1 project file and some models - behave like drag n drop of 3mf and then load models if (project_paths.size() == 1) { @@ -4787,7 +4788,7 @@ bool Plater::preview_zip_archive(const boost::filesystem::path& archive_path) load_files(project_paths, true, false); load_files(non_project_paths, true, false); #endif // 0 - + for (const fs::path& path : project_paths) { // Delete file from temp file (path variable), it will stay only in app memory. @@ -4994,7 +4995,7 @@ bool Plater::load_files(const wxArrayString& filenames, bool delete_after_load/* return true; } else if (boost::algorithm::iends_with(filename, ".zip")) { return preview_zip_archive(*it); - + } } @@ -5280,8 +5281,8 @@ void Plater::convert_unit(ConversionType conv_type) if (obj_idxs.empty() && volume_idxs.empty()) return; - // We will remove object indexes after convertion - // So, resort object indexes descending to avoid the crash after remove + // We will remove object indexes after convertion + // So, resort object indexes descending to avoid the crash after remove std::sort(obj_idxs.begin(), obj_idxs.end(), std::greater()); TakeSnapshot snapshot(this, conv_type == ConversionType::CONV_FROM_INCH ? _L("Convert from imperial units") : @@ -5296,7 +5297,7 @@ void Plater::convert_unit(ConversionType conv_type) remove(obj_idx); } p->load_model_objects(objects); - + Selection& selection = p->view3D->get_canvas3d()->get_selection(); size_t last_obj_idx = p->model.objects.size() - 1; @@ -5498,7 +5499,7 @@ void Plater::export_gcode(bool prefer_removable) // is_path_on_removable_drive() is called with the "true" parameter to update its internal database as the user may have shuffled the external drives // while the dialog was open. appconfig.update_last_output_dir(output_path.parent_path().string(), path_on_removable_media); - + } } @@ -5694,7 +5695,7 @@ std::string create_unique_3mf_filepath(const std::string &file, const SvgFiles s is_unique = false; break; } - } + } } while (!is_unique); return path_in_3mf; } @@ -5746,7 +5747,7 @@ void publish(Model &model) { "If you hit 'NO', all SVGs in the project will not be editable any more."), _L("Private protection"), wxYES_NO | wxICON_QUESTION); if (dialog.ShowModal() == wxID_NO){ - for (ModelObject *object : model.objects) + for (ModelObject *object : model.objects) for (ModelVolume *volume : object->volumes) if (volume->emboss_shape.has_value()) volume->emboss_shape.reset(); @@ -5765,7 +5766,7 @@ void publish(Model &model) { // check whether original filename is already in: filename = get_file_name(svgfile->path); } - svgfile->path_in_3mf = create_unique_3mf_filepath(filename, svgfiles); + svgfile->path_in_3mf = create_unique_3mf_filepath(filename, svgfiles); } } } @@ -5851,9 +5852,9 @@ void Plater::export_toolpaths_to_obj() const return; wxString path = p->get_export_file(FT_OBJ); - if (path.empty()) + if (path.empty()) return; - + wxBusyCursor wait; p->preview->get_canvas3d()->export_toolpaths_to_obj(into_u8(path).c_str()); } @@ -5982,7 +5983,7 @@ bool load_secret(const std::string& id, const std::string& opt, std::string& usr #else BOOST_LOG_TRIVIAL(error) << "wxUSE_SECRETSTORE not supported. Cannot load password from the system store."; return false; -#endif // wxUSE_SECRETSTORE +#endif // wxUSE_SECRETSTORE } } void Plater::connect_gcode() @@ -5994,7 +5995,7 @@ void Plater::connect_gcode() if (dialog.ShowModal() != wxID_OK) { return; } - } + } if (dialog_msg.empty()) { show_error(this, _L("Failed to select a printer.")); return; @@ -6023,7 +6024,7 @@ void Plater::connect_gcode() show_error(this, msg); return; } - + PhysicalPrinter ph_printer("connect_temp_printer", wxGetApp().preset_bundle->physical_printers.default_config(), *selected_printer_preset); ph_printer.config.set_key_value("host_type", new ConfigOptionEnum(htPrusaConnectNew)); @@ -6281,7 +6282,7 @@ void Plater::on_config_change(const DynamicPrintConfig &config) if (opt_key == "material_colour") { update_scheduled = true; // update should be scheduled (for update 3DScene) } - + p->config->set_key_value(opt_key, config.option(opt_key)->clone()); if (opt_key == "printer_technology") { const PrinterTechnology printer_technology = config.opt_enum(opt_key); @@ -6357,7 +6358,7 @@ void Plater::force_filament_colors_update() DynamicPrintConfig* config = p->config; const auto& extruders_filaments = wxGetApp().preset_bundle->extruders_filaments; - if (extruders_filaments.size() > 1 && + if (extruders_filaments.size() > 1 && p->config->option("filament_colour")->values.size() == extruders_filaments.size()) { std::vector filament_colors; @@ -6718,7 +6719,7 @@ void Plater::changed_object(ModelObject &object){ // update print p->schedule_background_process(); - + // Check outside bed get_current_canvas3D()->requires_check_outside_state(); }