diff --git a/src/slic3r/GUI/GUI_Factories.cpp b/src/slic3r/GUI/GUI_Factories.cpp index 4fec12d14..3671ee130 100644 --- a/src/slic3r/GUI/GUI_Factories.cpp +++ b/src/slic3r/GUI/GUI_Factories.cpp @@ -689,7 +689,7 @@ wxMenuItem* MenuFactory::append_menu_item_fix_through_netfabb(wxMenu* menu) return nullptr; wxMenuItem* menu_item = append_menu_item(menu, wxID_ANY, _L("Fix through the Netfabb"), "", [](wxCommandEvent&) { obj_list()->fix_through_netfabb(); }, "", menu, - []() {return plater()->can_fix_through_netfabb(); }, plater()); + []() {return plater()->can_fix_through_netfabb(); }, m_parent); return menu_item; } @@ -698,7 +698,7 @@ wxMenuItem* MenuFactory::append_menu_item_simplify(wxMenu* menu) { wxMenuItem* menu_item = append_menu_item(menu, wxID_ANY, _L("Simplify model"), "", [](wxCommandEvent&) { obj_list()->simplify(); }, "", menu, - []() {return plater()->can_simplify(); }, plater()); + []() {return plater()->can_simplify(); }, m_parent); menu->AppendSeparator(); return menu_item; @@ -779,7 +779,7 @@ void MenuFactory::append_menu_item_change_extruder(wxMenu* menu) } append_submenu(menu, extruder_selection_menu, wxID_ANY, name, _L("Use another extruder"), - "edit_uni"/* : "change_extruder"*/, []() {return true; }, GUI::wxGetApp().plater()); + "edit_uni"/* : "change_extruder"*/, []() {return true; }, m_parent); // menu->AppendSubMenu(extruder_selection_menu, name); } @@ -1061,6 +1061,7 @@ wxMenu* MenuFactory::multi_selection_menu() wxMenu* menu = new MenuWithSeparators(); + append_menu_item_fix_through_netfabb(menu); append_menu_item_reload_from_disk(menu); append_menu_items_convert_unit(menu); if (obj_list()->can_merge_to_multipart_object()) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 6eadb9681..da4a842d4 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -19,6 +19,7 @@ #include "Selection.hpp" #include "format.hpp" #include "NotificationManager.hpp" +#include "MsgDialog.hpp" #include #include @@ -369,7 +370,7 @@ void ObjectList::get_selection_indexes(std::vector& obj_idxs, std::vector()); + std::sort(obj_idxs.begin(), obj_idxs.end(), std::less()); obj_idxs.erase(std::unique(obj_idxs.begin(), obj_idxs.end()), obj_idxs.end()); } @@ -4013,13 +4014,124 @@ void ObjectList::rename_item() void ObjectList::fix_through_netfabb() { - int obj_idx, vol_idx; - get_selected_item_indexes(obj_idx, vol_idx); + // Do not fix anything when a gizmo is open. There might be issues with updates + // and what is worse, the snapshot time would refer to the internal stack. + if (!wxGetApp().plater()->canvas3D()->get_gizmos_manager().check_gizmos_closed_except(GLGizmosManager::Undefined)) + return; - wxGetApp().plater()->fix_through_netfabb(obj_idx, vol_idx); - - update_item_error_icon(obj_idx, vol_idx); - update_info_items(obj_idx); + // model_name + std::vector succes_models; + // model_name failing reason + std::vector> failed_models; + + std::vector obj_idxs, vol_idxs; + get_selection_indexes(obj_idxs, vol_idxs); + + std::vector model_names; + + // clear selections from the non-broken models if any exists + // and than fill names of models to repairing + if (vol_idxs.empty()) { + for (int i = int(obj_idxs.size())-1; i >= 0; --i) + if (object(obj_idxs[i])->get_mesh_errors_count() == 0) + obj_idxs.erase(obj_idxs.begin()+i); + for (int obj_idx : obj_idxs) + model_names.push_back(object(obj_idx)->name); + } + else { + ModelObject* obj = object(obj_idxs.front()); + for (int i = int(vol_idxs.size()) - 1; i >= 0; --i) + if (obj->get_mesh_errors_count(vol_idxs[i]) == 0) + vol_idxs.erase(vol_idxs.begin() + i); + for (int vol_idx : vol_idxs) + model_names.push_back(obj->volumes[vol_idx]->name); + } + + auto plater = wxGetApp().plater(); + + auto fix_and_update_progress = [this, plater, model_names](const int obj_idx, const int vol_idx, + int model_idx, + wxProgressDialog& progress_dlg, + std::vector& succes_models, + std::vector>& failed_models) + { + const std::string& model_name = model_names[model_idx]; + wxString msg = _L("Repairing model"); + if (model_names.size() == 1) + msg += ": " + from_u8(model_name) + "\n"; + else { + msg += ":\n"; + for (size_t i = 0; i < model_names.size(); ++i) + msg += (i == model_idx ? " > " : " ") + from_u8(model_names[i]) + "\n"; + msg += "\n"; + } + + plater->clear_before_change_mesh(obj_idx); + std::string res; + if (!fix_model_by_win10_sdk_gui(*(object(obj_idx)), vol_idx, progress_dlg, msg, res)) + return false; + wxGetApp().plater()->changed_mesh(obj_idx); + + plater->changed_mesh(obj_idx); + + if (res.empty()) + succes_models.push_back(model_name); + else + failed_models.push_back({ model_name, res }); + + update_item_error_icon(obj_idx, vol_idx); + update_info_items(obj_idx); + + return true; + }; + + Plater::TakeSnapshot snapshot(plater, _L("Fix through NetFabb")); + + // Open a progress dialog. + wxProgressDialog progress_dlg(_L("Fixing through NetFabb"), "", 100, + nullptr, // ! parent of the wxProgressDialog should be nullptr to avoid flickering during the model fixing + wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT); + int model_idx{ 0 }; + if (vol_idxs.empty()) { + int vol_idx{ -1 }; + for (int obj_idx : obj_idxs) { + if (object(obj_idx)->get_mesh_errors_count(vol_idx) == 0) + continue; + if (!fix_and_update_progress(obj_idx, vol_idx, model_idx, progress_dlg, succes_models, failed_models)) + break; + model_idx++; + } + } + else { + int obj_idx{ obj_idxs.front() }; + for (int vol_idx : vol_idxs) { + if (!fix_and_update_progress(obj_idx, vol_idx, model_idx, progress_dlg, succes_models, failed_models)) + break; + model_idx++; + } + } + // Close the progress dialog + progress_dlg.Update(100, ""); + + // Show info message + wxString msg; + wxString bullet_suf = "\n - "; + if (!succes_models.empty()) { + msg = _L_PLURAL("Folowing model is repaired successfully", "Folowing models are repaired successfully", succes_models.size()) + ":"; + for (auto& model : succes_models) + msg += bullet_suf + from_u8(model); + msg += "\n\n"; + } + if (!failed_models.empty()) { + msg += _L_PLURAL("Folowing model repair failed", "Folowing models repair failed", failed_models.size()) + ":\n"; + for (auto& model : failed_models) + msg += bullet_suf + from_u8(model.first) + ": " + _(model.second); + } + if (msg.IsEmpty()) + msg = _L("Repairing was canceled"); + // !!! Use wxMessageDialog instead of MessageDialog here + // It will not be "dark moded" but the Application will not lose a focus after model repairing + wxMessageDialog(nullptr, msg, _L("Model Repair by the Netfabb service"), wxICON_INFORMATION | wxOK).ShowModal(); } void ObjectList::simplify() diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index e78ffe566..c7728e551 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1722,7 +1722,6 @@ struct Plater::priv #endif // ENABLE_RELOAD_FROM_DISK_REPLACE_FILE void replace_with_stl(); void reload_all_from_disk(); - void fix_through_netfabb(const int obj_idx, const int vol_idx = -1); void create_simplify_notification(const std::vector& obj_ids); void set_current_panel(wxPanel* panel); @@ -3664,27 +3663,6 @@ void Plater::priv::reload_all_from_disk() } } -void Plater::priv::fix_through_netfabb(const int obj_idx, const int vol_idx/* = -1*/) -{ - if (obj_idx < 0) - return; - - // Do not fix anything when a gizmo is open. There might be issues with updates - // and what is worse, the snapshot time would refer to the internal stack. - if (! q->canvas3D()->get_gizmos_manager().check_gizmos_closed_except(GLGizmosManager::Undefined)) - return; - - // size_t snapshot_time = undo_redo_stack().active_snapshot_time(); - Plater::TakeSnapshot snapshot(q, _L("Fix through NetFabb")); - - q->clear_before_change_mesh(obj_idx); - ModelObject* mo = model.objects[obj_idx]; - fix_model_by_win10_sdk_gui(*mo, vol_idx); - q->changed_mesh(obj_idx); - // workaround to fix the issue, when PrusaSlicer lose a focus after model fixing - q->SetFocus(); -} - void Plater::priv::create_simplify_notification(const std::vector& obj_ids) { const uint32_t triangles_to_suggest_simplify = 1000000; @@ -4168,7 +4146,8 @@ void Plater::priv::on_right_click(RBtnEvent& evt) const bool is_some_full_instances = get_selection().is_single_full_instance() || get_selection().is_single_full_object() || get_selection().is_multiple_full_instance(); - menu = is_some_full_instances ? menus.object_menu() : menus.part_menu(); + menu = is_some_full_instances ? menus.object_menu() : + get_selection().is_single_volume() ? menus.part_menu() : menus.multi_selection_menu(); } } @@ -4521,11 +4500,22 @@ bool Plater::priv::can_delete_all() const bool Plater::priv::can_fix_through_netfabb() const { - int obj_idx = get_selected_object_idx(); - if (obj_idx < 0) - return false; + std::vector obj_idxs, vol_idxs; + sidebar->obj_list()->get_selection_indexes(obj_idxs, vol_idxs); - return model.objects[obj_idx]->get_mesh_errors_count() > 0; + if (vol_idxs.empty()) { + for (auto obj_idx : obj_idxs) + if (model.objects[obj_idx]->get_mesh_errors_count() > 0) + return true; + return false; + } + + int obj_idx = obj_idxs.front(); + for (auto vol_idx : vol_idxs) + if (model.objects[obj_idx]->get_mesh_errors_count(vol_idx) > 0) + return true; + + return false; } @@ -6468,7 +6458,6 @@ void Plater::suppress_background_process(const bool stop_background_process) this->p->suppressed_backround_processing_update = true; } -void Plater::fix_through_netfabb(const int obj_idx, const int vol_idx/* = -1*/) { p->fix_through_netfabb(obj_idx, vol_idx); } void Plater::mirror(Axis axis) { p->mirror(axis); } void Plater::split_object() { p->split_object(); } void Plater::split_volume() { p->split_volume(); } diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 4a98797e5..e37f790fc 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -235,7 +235,6 @@ public: void schedule_background_process(bool schedule = true); bool is_background_process_update_scheduled() const; void suppress_background_process(const bool stop_background_process) ; - void fix_through_netfabb(const int obj_idx, const int vol_idx = -1); void send_gcode(); void eject_drive(); diff --git a/src/slic3r/Utils/FixModelByWin10.cpp b/src/slic3r/Utils/FixModelByWin10.cpp index 2fbd0d937..931d2f449 100644 --- a/src/slic3r/Utils/FixModelByWin10.cpp +++ b/src/slic3r/Utils/FixModelByWin10.cpp @@ -318,7 +318,10 @@ public: const char* what() const throw() { return "Model repair has been canceled"; } }; -void fix_model_by_win10_sdk_gui(ModelObject &model_object, int volume_idx) +// returt FALSE, if fixing was canceled +// fix_result is empty, if fixing finished successfully +// fix_result containes a message if fixing failed +bool fix_model_by_win10_sdk_gui(ModelObject &model_object, int volume_idx, wxProgressDialog& progress_dialog, const wxString& msg_header, std::string& fix_result) { std::mutex mutex; std::condition_variable condition; @@ -337,11 +340,6 @@ void fix_model_by_win10_sdk_gui(ModelObject &model_object, int volume_idx) else volumes.emplace_back(model_object.volumes[volume_idx]); - // Open a progress dialog. - wxProgressDialog progress_dialog( - _L("Model fixing"), - _L("Exporting model") + "...", - 100, nullptr, wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT); // ! parent of the wxProgressDialog should be nullptr to avoid flickering during the model fixing // Executing the calculation in a background thread, so that the COM context could be created with its own threading model. // (It seems like wxWidgets initialize the COM contex as single threaded and we need a multi-threaded context). bool success = false; @@ -423,21 +421,23 @@ void fix_model_by_win10_sdk_gui(ModelObject &model_object, int volume_idx) }); while (! finished) { condition.wait_for(lock, std::chrono::milliseconds(500), [&progress]{ return progress.updated; }); - if (! progress_dialog.Update(progress.percent, _(progress.message))) + // decrease progress.percent value to avoid closing of the progress dialog + if (!progress_dialog.Update(progress.percent-1, msg_header + _(progress.message))) canceled = true; + else + progress_dialog.Fit(); progress.updated = false; } if (canceled) { // Nothing to show. } else if (success) { - Slic3r::GUI::MessageDialog dlg(nullptr, _L("Model repaired successfully"), _L("Model Repair by the Netfabb service"), wxICON_INFORMATION | wxOK); - dlg.ShowModal(); + fix_result = ""; } else { - Slic3r::GUI::MessageDialog dlg(nullptr, _L("Model repair failed:") + " \n" + _(progress.message), _L("Model Repair by the Netfabb service"), wxICON_ERROR | wxOK); - dlg.ShowModal(); + fix_result = progress.message; } worker_thread.join(); + return !canceled; } } // namespace Slic3r diff --git a/src/slic3r/Utils/FixModelByWin10.hpp b/src/slic3r/Utils/FixModelByWin10.hpp index 8e4766467..4f0acd90a 100644 --- a/src/slic3r/Utils/FixModelByWin10.hpp +++ b/src/slic3r/Utils/FixModelByWin10.hpp @@ -12,12 +12,14 @@ class Print; #ifdef HAS_WIN10SDK extern bool is_windows10(); -extern void fix_model_by_win10_sdk_gui(ModelObject &model_object, int volume_idx); +// returt false, if fixing was canceled +extern bool fix_model_by_win10_sdk_gui(ModelObject &model_object, int volume_idx, wxProgressDialog& progress_dlg, const wxString& msg_header, std::string& fix_result); #else /* HAS_WIN10SDK */ inline bool is_windows10() { return false; } -inline void fix_model_by_win10_sdk_gui(ModelObject &, int) {} +// returt false, if fixing was canceled +inline bool fix_model_by_win10_sdk_gui(ModelObject&, int, wxProgressDialog&, const wxString&, std::string&) { return false; } #endif /* HAS_WIN10SDK */