From e002f0066fd96f38a5241a429f2228278de28f1d Mon Sep 17 00:00:00 2001 From: Oleksandra Yushchenko Date: Mon, 15 Mar 2021 10:04:45 +0100 Subject: [PATCH] Ys code refactoring (#6227) * GUI_ObjectList code refactoring: The MenuFactory structure contains functions related to the context menu and bitmaps used to different volume types. The SettingsFactory structure contains functions to getting overridden options, its bundles and bitmaps used to setting categories. Fixed bugs/crashes: 1. Add object -> Add Settings from 3D scene -> Right click on object => Part's Settings list instead of object's (Same behavior if something else but Object is selected in ObjectList) 2. Add settings to the part -> Change part type to the "Support Blocker/Enforcer" -> Settings disappears (it's OK) but => Save Project -> Open project => Support Blocker/Enforcer has a settings 3. Add part for object -> Change type of part -> Change monitor DPI -> old type icon appears 4. Select all instances in ObjectList -> Context menu in 3D scene -> Add Settings -> Select some category -> Crash * ObjectLayers: Fixed a crash on re-scaling, when some layer range is selected * Fixed OSX build * Added menu item "Split to Objects" for multipart objects + Fixed bug: Add 2 parts, Add some settings for one part Delete part without settings => Single part object without settings, but settings are applied for the object. + Next refactoring: use same menu for Plater and ObjectList --- resources/localization/list.txt | 1 + src/libslic3r/Model.cpp | 79 +- src/slic3r/CMakeLists.txt | 2 + src/slic3r/GUI/GUI_Factories.cpp | 1027 ++++++++++++++++++++++ src/slic3r/GUI/GUI_Factories.hpp | 104 +++ src/slic3r/GUI/GUI_ObjectLayers.cpp | 19 +- src/slic3r/GUI/GUI_ObjectList.cpp | 1074 ++---------------------- src/slic3r/GUI/GUI_ObjectList.hpp | 91 +- src/slic3r/GUI/GUI_ObjectSettings.cpp | 3 +- src/slic3r/GUI/MainFrame.cpp | 2 - src/slic3r/GUI/ObjectDataViewModel.cpp | 54 +- src/slic3r/GUI/ObjectDataViewModel.hpp | 10 +- src/slic3r/GUI/Plater.cpp | 310 +------ src/slic3r/GUI/Plater.hpp | 15 + 14 files changed, 1376 insertions(+), 1415 deletions(-) create mode 100644 src/slic3r/GUI/GUI_Factories.cpp create mode 100644 src/slic3r/GUI/GUI_Factories.hpp diff --git a/resources/localization/list.txt b/resources/localization/list.txt index 64d50591a..a2618b44e 100644 --- a/resources/localization/list.txt +++ b/resources/localization/list.txt @@ -28,6 +28,7 @@ src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp src/slic3r/GUI/GUI.cpp src/slic3r/GUI/GUI_App.cpp src/slic3r/GUI/GUI_Init.cpp +src/slic3r/GUI/GUI_Factories.cpp src/slic3r/GUI/GUI_ObjectLayers.cpp src/slic3r/GUI/GUI_ObjectList.cpp src/slic3r/GUI/GUI_ObjectManipulation.cpp diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 5f806955e..d48443181 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1302,52 +1302,54 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b void ModelObject::split(ModelObjectPtrs* new_objects) { - if (this->volumes.size() > 1) { - // We can't split meshes if there's more than one volume, because - // we can't group the resulting meshes by object afterwards - new_objects->emplace_back(this); - return; - } - - ModelVolume* volume = this->volumes.front(); - TriangleMeshPtrs meshptrs = volume->mesh().split(); - size_t counter = 1; - for (TriangleMesh *mesh : meshptrs) { + for (ModelVolume* volume : this->volumes) { + if (volume->type() != ModelVolumeType::MODEL_PART) + continue; - // FIXME: crashes if not satisfied - if (mesh->facets_count() < 3) continue; + TriangleMeshPtrs meshptrs = volume->mesh().split(); + size_t counter = 1; + for (TriangleMesh* mesh : meshptrs) { - mesh->repair(); - - // XXX: this seems to be the only real usage of m_model, maybe refactor this so that it's not needed? - ModelObject* new_object = m_model->add_object(); - new_object->name = this->name + (meshptrs.size() > 1 ? "_" + std::to_string(counter++) : ""); + // FIXME: crashes if not satisfied + if (mesh->facets_count() < 3) continue; - // Don't copy the config's ID. - new_object->config.assign_config(this->config); - assert(new_object->config.id().valid()); - assert(new_object->config.id() != this->config.id()); - new_object->instances.reserve(this->instances.size()); - for (const ModelInstance *model_instance : this->instances) - new_object->add_instance(*model_instance); - ModelVolume* new_vol = new_object->add_volume(*volume, std::move(*mesh)); + mesh->repair(); - for (ModelInstance* model_instance : new_object->instances) - { - Vec3d shift = model_instance->get_transformation().get_matrix(true) * new_vol->get_offset(); - model_instance->set_offset(model_instance->get_offset() + shift); + // XXX: this seems to be the only real usage of m_model, maybe refactor this so that it's not needed? + ModelObject* new_object = m_model->add_object(); + if (meshptrs.size() == 1) { + new_object->name = volume->name; + // Don't copy the config's ID. + new_object->config.assign_config(this->config.size() > 0 ? this->config : volume->config); + } + else { + new_object->name = this->name + (meshptrs.size() > 1 ? "_" + std::to_string(counter++) : ""); + // Don't copy the config's ID. + new_object->config.assign_config(this->config); + } + assert(new_object->config.id().valid()); + assert(new_object->config.id() != this->config.id()); + new_object->instances.reserve(this->instances.size()); + for (const ModelInstance* model_instance : this->instances) + new_object->add_instance(*model_instance); + ModelVolume* new_vol = new_object->add_volume(*volume, std::move(*mesh)); + + for (ModelInstance* model_instance : new_object->instances) + { + Vec3d shift = model_instance->get_transformation().get_matrix(true) * new_vol->get_offset(); + model_instance->set_offset(model_instance->get_offset() + shift); + } + + new_vol->set_offset(Vec3d::Zero()); + // reset the source to disable reload from disk + new_vol->source = ModelVolume::Source(); + new_objects->emplace_back(new_object); + delete mesh; } - - new_vol->set_offset(Vec3d::Zero()); - // reset the source to disable reload from disk - new_vol->source = ModelVolume::Source(); - new_objects->emplace_back(new_object); - delete mesh; } - - return; } + void ModelObject::merge() { if (this->volumes.size() == 1) { @@ -1738,6 +1740,7 @@ size_t ModelVolume::split(unsigned int max_extruders) this->object->volumes[ivolume]->translate(offset); this->object->volumes[ivolume]->name = name + "_" + std::to_string(idx + 1); this->object->volumes[ivolume]->config.set_deserialize("extruder", auto_extruder_id(max_extruders, extruder_counter)); + this->object->volumes[ivolume]->m_is_splittable = 0; delete mesh; ++ idx; } diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 40d74159d..6436de2ca 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -93,6 +93,8 @@ set(SLIC3R_GUI_SOURCES GUI/SavePresetDialog.cpp GUI/PhysicalPrinterDialog.hpp GUI/PhysicalPrinterDialog.cpp + GUI/GUI_Factories.cpp + GUI/GUI_Factories.hpp GUI/GUI_ObjectList.cpp GUI/GUI_ObjectList.hpp GUI/GUI_ObjectManipulation.cpp diff --git a/src/slic3r/GUI/GUI_Factories.cpp b/src/slic3r/GUI/GUI_Factories.cpp new file mode 100644 index 000000000..d8518a6c8 --- /dev/null +++ b/src/slic3r/GUI/GUI_Factories.cpp @@ -0,0 +1,1027 @@ +#include "libslic3r/libslic3r.h" +#include "libslic3r/PresetBundle.hpp" +#include "libslic3r/Model.hpp" + +#include "GUI_Factories.hpp" +#include "GUI_ObjectList.hpp" +#include "GUI_App.hpp" +#include "I18N.hpp" +#include "Plater.hpp" +#include "ObjectDataViewModel.hpp" + +#include "OptionsGroup.hpp" +#include "GLCanvas3D.hpp" +#include "Selection.hpp" +#include "format.hpp" + +#include +#include "slic3r/Utils/FixModelByWin10.hpp" + +namespace Slic3r +{ +namespace GUI +{ + +static PrinterTechnology printer_technology() +{ + return wxGetApp().preset_bundle->printers.get_selected_preset().printer_technology(); +} + +static int extruders_count() +{ + return wxGetApp().extruders_edited_cnt(); +} + +static bool is_improper_category(const std::string& category, const int extruders_cnt, const bool is_object_settings = true) +{ + return category.empty() || + (extruders_cnt == 1 && (category == "Extruders" || category == "Wipe options")) || + (!is_object_settings && category == "Support material"); +} + + +//------------------------------------- +// SettingsFactory +//------------------------------------- + +// pt_FFF +static SettingsFactory::Bundle FREQ_SETTINGS_BUNDLE_FFF = +{ + { L("Layers and Perimeters"), { "layer_height" , "perimeters", "top_solid_layers", "bottom_solid_layers" } }, + { L("Infill") , { "fill_density", "fill_pattern" } }, + { L("Support material") , { "support_material", "support_material_auto", "support_material_threshold", + "support_material_pattern", "support_material_interface_pattern", "support_material_buildplate_only", + "support_material_spacing" } }, + { L("Wipe options") , { "wipe_into_infill", "wipe_into_objects" } } +}; + +// pt_SLA +static SettingsFactory::Bundle FREQ_SETTINGS_BUNDLE_SLA = +{ + { L("Pad and Support") , { "supports_enable", "pad_enable" } } +}; + +std::vector SettingsFactory::get_options(const bool is_part) +{ + if (printer_technology() == ptSLA) { + SLAPrintObjectConfig full_sla_config; + auto options = full_sla_config.keys(); + options.erase(find(options.begin(), options.end(), "layer_height")); + return options; + } + + PrintRegionConfig reg_config; + auto options = reg_config.keys(); + if (!is_part) { + PrintObjectConfig obj_config; + std::vector obj_options = obj_config.keys(); + options.insert(options.end(), obj_options.begin(), obj_options.end()); + } + return options; +} + +SettingsFactory::Bundle SettingsFactory::get_bundle(const DynamicPrintConfig* config, bool is_object_settings) +{ + auto opt_keys = config->keys(); + if (opt_keys.empty()) + return Bundle(); + + // update options list according to print technology + auto full_current_opts = get_options(!is_object_settings); + for (int i = opt_keys.size() - 1; i >= 0; --i) + if (find(full_current_opts.begin(), full_current_opts.end(), opt_keys[i]) == full_current_opts.end()) + opt_keys.erase(opt_keys.begin() + i); + + if (opt_keys.empty()) + return Bundle(); + + const int extruders_cnt = wxGetApp().extruders_edited_cnt(); + + Bundle bundle; + for (auto& opt_key : opt_keys) + { + auto category = config->def()->get(opt_key)->category; + if (is_improper_category(category, extruders_cnt, is_object_settings)) + continue; + + std::vector< std::string > new_category; + + auto& cat_opt = bundle.find(category) == bundle.end() ? new_category : bundle.at(category); + cat_opt.push_back(opt_key); + if (cat_opt.size() == 1) + bundle[category] = cat_opt; + } + + return bundle; +} + +// Fill CategoryItem +std::map SettingsFactory::CATEGORY_ICON = +{ +// settings category name related bitmap name + // ptFFF + { L("Layers and Perimeters"), "layers" }, + { L("Infill") , "infill" }, + { L("Ironing") , "ironing" }, + { L("Fuzzy Skin") , "fuzzy_skin" }, + { L("Support material") , "support" }, + { L("Speed") , "time" }, + { L("Extruders") , "funnel" }, + { L("Extrusion Width") , "funnel" }, + { L("Wipe options") , "funnel" }, + { L("Skirt and brim") , "skirt+brim" }, +// { L("Speed > Acceleration") , "time" }, + { L("Advanced") , "wrench" }, + // ptSLA , + { L("Supports") , "support" }, + { L("Pad") , "pad" }, + { L("Hollowing") , "hollowing" } +}; + +wxBitmap SettingsFactory::get_category_bitmap(const std::string& category_name) +{ + if (CATEGORY_ICON.find(category_name) == CATEGORY_ICON.end()) + return wxNullBitmap; + return create_scaled_bitmap(CATEGORY_ICON.at(category_name)); +} + + +//------------------------------------- +// MenuFactory +//------------------------------------- + +// Note: id accords to type of the sub-object (adding volume), so sequence of the menu items is important +std::vector> MenuFactory::ADD_VOLUME_MENU_ITEMS = { +// menu_item Name menu_item bitmap name + {L("Add part"), "add_part" }, // ~ModelVolumeType::MODEL_PART + {L("Add modifier"), "add_modifier"}, // ~ModelVolumeType::PARAMETER_MODIFIER + {L("Add support enforcer"), "support_enforcer"}, // ~ModelVolumeType::SUPPORT_ENFORCER + {L("Add support blocker"), "support_blocker"} // ~ModelVolumeType::SUPPORT_BLOCKER +}; + +static Plater* plater() +{ + return wxGetApp().plater(); +} + +static ObjectList* obj_list() +{ + return wxGetApp().obj_list(); +} + +static ObjectDataViewModel* list_model() +{ + return wxGetApp().obj_list()->GetModel(); +} + +static const Selection& get_selection() +{ + return plater()->canvas3D()->get_selection(); +} + +// category -> vector ( option ; label ) +typedef std::map< std::string, std::vector< std::pair > > FullSettingsHierarchy; +static void get_full_settings_hierarchy(FullSettingsHierarchy& settings_menu, const bool is_part) +{ + auto options = SettingsFactory::get_options(is_part); + + const int extruders_cnt = extruders_count(); + + DynamicPrintConfig config; + for (auto& option : options) + { + auto const opt = config.def()->get(option); + auto category = opt->category; + if (is_improper_category(category, extruders_cnt, !is_part)) + continue; + + const std::string& label = !opt->full_label.empty() ? opt->full_label : opt->label; + std::pair option_label(option, label); + std::vector< std::pair > new_category; + auto& cat_opt_label = settings_menu.find(category) == settings_menu.end() ? new_category : settings_menu.at(category); + cat_opt_label.push_back(option_label); + if (cat_opt_label.size() == 1) + settings_menu[category] = cat_opt_label; + } +} + +static wxMenu* create_settings_popupmenu(wxMenu* parent_menu, const bool is_object_settings, wxDataViewItem item/*, ModelConfig& config*/) +{ + wxMenu* menu = new wxMenu; + + FullSettingsHierarchy categories; + get_full_settings_hierarchy(categories, !is_object_settings); + + auto get_selected_options_for_category = [categories, item](const wxString& category_name) { + wxArrayString names; + wxArrayInt selections; + + std::vector< std::pair > category_options; + for (auto& cat : categories) { + if (_(cat.first) == category_name) { + ModelConfig& config = obj_list()->get_item_config(item); + auto opt_keys = config.keys(); + + int sel = 0; + for (const std::pair& pair : cat.second) { + names.Add(_(pair.second)); + if (find(opt_keys.begin(), opt_keys.end(), pair.first) != opt_keys.end()) + selections.Add(sel); + sel++; + category_options.push_back(std::make_pair(pair.first, false)); + } + break; + } + } + + if (!category_options.empty() && + wxGetSelectedChoices(selections, _L("Select showing settings"), category_name, names) != -1) { + for (auto sel : selections) + category_options[sel].second = true; + } + return category_options; + +#if 0 + if (selections.size() > 0) + { + // Add selected items to the "Quick menu" + SettingsFactory::Bundle& freq_settings = printer_technology() == ptSLA ? + m_freq_settings_sla : m_freq_settings_fff; + bool changed_existing = false; + + std::vector tmp_freq_cat = {}; + + for (auto& cat : freq_settings) + { + if (_(cat.first) == category_name) + { + std::vector& freq_settings_category = cat.second; + freq_settings_category.clear(); + freq_settings_category.reserve(selection_cnt); + for (auto sel : selections) + freq_settings_category.push_back((*settings_list)[sel].first); + + changed_existing = true; + break; + } + } + + if (!changed_existing) + { + // Create new "Quick menu" item + for (auto& cat : settings_menu) + { + if (_(cat.first) == category_name) + { + freq_settings[cat.first] = std::vector{}; + + std::vector& freq_settings_category = freq_settings.find(cat.first)->second; + freq_settings_category.reserve(selection_cnt); + for (auto sel : selections) + freq_settings_category.push_back((*settings_list)[sel].first); + break; + } + } + } + } +#endif + }; + + for (auto cat : categories) { + append_menu_item(menu, wxID_ANY, _(cat.first), "", + [menu, item, get_selected_options_for_category](wxCommandEvent& event) { + std::vector< std::pair > category_options = get_selected_options_for_category(menu->GetLabel(event.GetId())); + obj_list()->add_category_to_settings_from_selection(category_options, item); + }, SettingsFactory::get_category_bitmap(cat.first), parent_menu, + []() { return true; }, plater()); + } + + return menu; +} + +static void create_freq_settings_popupmenu(wxMenu* menu, const bool is_object_settings, wxDataViewItem item) +{ + // Add default settings bundles + const SettingsFactory::Bundle& bundle = printer_technology() == ptFFF ? FREQ_SETTINGS_BUNDLE_FFF : FREQ_SETTINGS_BUNDLE_SLA; + + const int extruders_cnt = extruders_count(); + + for (auto& category : bundle) { + if (is_improper_category(category.first, extruders_cnt, is_object_settings)) + continue; + + append_menu_item(menu, wxID_ANY, _(category.first), "", + [menu, item, is_object_settings, bundle](wxCommandEvent& event) { + wxString category_name = menu->GetLabel(event.GetId()); + std::vector options; + for (auto& category : bundle) + if (category_name == _(category.first)) { + options = category.second; + break; + } + if (options.empty()) + return; + // Because of we couldn't edited layer_height for ItVolume from settings list, + // correct options according to the selected item type : remove "layer_height" option + if (!is_object_settings && category_name == _("Layers and Perimeters")) { + const auto layer_height_it = std::find(options.begin(), options.end(), "layer_height"); + if (layer_height_it != options.end()) + options.erase(layer_height_it); + } + + obj_list()->add_category_to_settings_from_frequent(options, item); + }, + SettingsFactory::get_category_bitmap(category.first), menu, + []() { return true; }, plater()); + } +#if 0 + // Add "Quick" settings bundles + const SettingsFactory::Bundle& bundle_quick = printer_technology() == ptFFF ? m_freq_settings_fff : m_freq_settings_sla; + + for (auto& category : bundle_quick) { + if (is_improper_category(category.first, extruders_cnt)) + continue; + + append_menu_item(menu, wxID_ANY, from_u8((boost::format(_utf8(L("Quick Add Settings (%s)"))) % _(it.first)).str()), "", + [menu, item, is_object_settings, bundle](wxCommandEvent& event) { + wxString category_name = menu->GetLabel(event.GetId()); + std::vector options; + for (auto& category : bundle) + if (category_name == from_u8((boost::format(_L("Quick Add Settings (%s)")) % _(category.first)).str())) { + options = category.second; + break; + } + if (options.empty()) + return; + // Because of we couldn't edited layer_height for ItVolume from settings list, + // correct options according to the selected item type : remove "layer_height" option + if (!is_object_settings) { + const auto layer_height_it = std::find(options.begin(), options.end(), "layer_height"); + if (layer_height_it != options.end()) + options.erase(layer_height_it); + } + obj_list()->add_category_to_settings_from_frequent(options, item); + }, + SettingsFactory::get_category_bitmap(category.first), menu, + [this]() { return true; }, plater()); + } +#endif +} + +std::vector MenuFactory::get_volume_bitmaps() +{ + std::vector volume_bmps; + volume_bmps.reserve(ADD_VOLUME_MENU_ITEMS.size()); + for (auto item : ADD_VOLUME_MENU_ITEMS) + volume_bmps.push_back(create_scaled_bitmap(item.second)); + return volume_bmps; +} + +void MenuFactory::append_menu_item_delete(wxMenu* menu) +{ + append_menu_item(menu, wxID_ANY, _L("Delete") + "\tDel", _L("Remove the selected object"), + [](wxCommandEvent&) { plater()->remove_selected(); }, "delete", nullptr, + []() { return plater()->can_delete(); }, m_parent); + + menu->AppendSeparator(); + +} + +wxMenu* MenuFactory::append_submenu_add_generic(wxMenu* menu, ModelVolumeType type) { + auto sub_menu = new wxMenu; + + if (wxGetApp().get_mode() == comExpert && type != ModelVolumeType::INVALID) { + append_menu_item(sub_menu, wxID_ANY, _L("Load") + " " + dots, "", + [type](wxCommandEvent&) { obj_list()->load_subobject(type); }, "", menu); + sub_menu->AppendSeparator(); + } + + for (auto& item : { L("Box"), L("Cylinder"), L("Sphere"), L("Slab") }) + { + if (type == ModelVolumeType::INVALID && strncmp(item, "Slab", 4) == 0) + continue; + append_menu_item(sub_menu, wxID_ANY, _(item), "", + [type, item](wxCommandEvent&) { obj_list()->load_generic_subobject(item, type); }, "", menu); + } + + return sub_menu; +} + +void MenuFactory::append_menu_items_add_volume(wxMenu* menu) +{ + // Update "add" items(delete old & create new) settings popupmenu + for (auto& item : ADD_VOLUME_MENU_ITEMS) { + const auto settings_id = menu->FindItem(_(item.first)); + if (settings_id != wxNOT_FOUND) + menu->Destroy(settings_id); + } + + const ConfigOptionMode mode = wxGetApp().get_mode(); + + if (mode == comAdvanced) { + append_menu_item(menu, wxID_ANY, _(ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::MODEL_PART)].first), "", + [](wxCommandEvent&) { obj_list()->load_subobject(ModelVolumeType::MODEL_PART); }, + ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::MODEL_PART)].second, nullptr, + []() { return obj_list()->is_instance_or_object_selected(); }, m_parent); + } + if (mode == comSimple) { + append_menu_item(menu, wxID_ANY, _(ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::SUPPORT_ENFORCER)].first), "", + [](wxCommandEvent&) { obj_list()->load_generic_subobject(L("Box"), ModelVolumeType::SUPPORT_ENFORCER); }, + ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::SUPPORT_ENFORCER)].second, nullptr, + []() { return obj_list()->is_instance_or_object_selected(); }, m_parent); + append_menu_item(menu, wxID_ANY, _(ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::SUPPORT_BLOCKER)].first), "", + [](wxCommandEvent&) { obj_list()->load_generic_subobject(L("Box"), ModelVolumeType::SUPPORT_BLOCKER); }, + ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::SUPPORT_BLOCKER)].second, nullptr, + []() { return obj_list()->is_instance_or_object_selected(); }, m_parent); + + return; + } + + for (size_t type = (mode == comExpert ? 0 : 1); type < ADD_VOLUME_MENU_ITEMS.size(); type++) + { + auto& item = ADD_VOLUME_MENU_ITEMS[type]; + + wxMenu* sub_menu = append_submenu_add_generic(menu, ModelVolumeType(type)); + append_submenu(menu, sub_menu, wxID_ANY, _(item.first), "", item.second, + []() { return obj_list()->is_instance_or_object_selected(); }, m_parent); + } +} + +wxMenuItem* MenuFactory::append_menu_item_layers_editing(wxMenu* menu) +{ + return append_menu_item(menu, wxID_ANY, _L("Height range Modifier"), "", + [](wxCommandEvent&) { obj_list()->layers_editing(); }, "edit_layers_all", menu, + []() { return obj_list()->is_instance_or_object_selected(); }, m_parent); +} + +wxMenuItem* MenuFactory::append_menu_item_settings(wxMenu* menu_) +{ + MenuWithSeparators* menu = dynamic_cast(menu_); + + const wxString menu_name = _L("Add settings"); + // Delete old items from settings popupmenu + auto settings_id = menu->FindItem(menu_name); + if (settings_id != wxNOT_FOUND) + menu->Destroy(settings_id); + + for (auto& it : FREQ_SETTINGS_BUNDLE_FFF) + { + settings_id = menu->FindItem(_(it.first)); + if (settings_id != wxNOT_FOUND) + menu->Destroy(settings_id); + } + for (auto& it : FREQ_SETTINGS_BUNDLE_SLA) + { + settings_id = menu->FindItem(_(it.first)); + if (settings_id != wxNOT_FOUND) + menu->Destroy(settings_id); + } +#if 0 + for (auto& it : m_freq_settings_fff) + { + settings_id = menu->FindItem(from_u8((boost::format(_utf8(L("Quick Add Settings (%s)"))) % _(it.first)).str())); + if (settings_id != wxNOT_FOUND) + menu->Destroy(settings_id); + } + for (auto& it : m_freq_settings_sla) + { + settings_id = menu->FindItem(from_u8((boost::format(_utf8(L("Quick Add Settings (%s)"))) % _(it.first)).str())); + if (settings_id != wxNOT_FOUND) + menu->Destroy(settings_id); + } +#endif + menu->DestroySeparators(); // delete old separators + + // If there are selected more then one instance but not all of them + // don't add settings menu items + const Selection& selection = get_selection(); + if ((selection.is_multiple_full_instance() && !selection.is_single_full_object()) || + selection.is_multiple_volume() || selection.is_mixed()) // more than one volume(part) is selected on the scene + return nullptr; + + const auto sel_vol = obj_list()->get_selected_model_volume(); + if (sel_vol && sel_vol->type() >= ModelVolumeType::SUPPORT_ENFORCER) + return nullptr; + + const ConfigOptionMode mode = wxGetApp().get_mode(); + if (mode == comSimple) + return nullptr; + + // Create new items for settings popupmenu + + if (printer_technology() == ptFFF || + (menu->GetMenuItems().size() > 0 && !menu->GetMenuItems().back()->IsSeparator())) + menu->SetFirstSeparator(); + + // detect itemm for adding of the setting + ObjectList* object_list = obj_list(); + ObjectDataViewModel* obj_model = list_model(); + + const wxDataViewItem sel_item = // when all instances in object are selected + object_list->GetSelectedItemsCount() > 1 && selection.is_single_full_object() ? + obj_model->GetItemById(selection.get_object_idx()) : + object_list->GetSelection(); + if (!sel_item) + return nullptr; + + // If we try to add settings for object/part from 3Dscene, + // for the second try there is selected ItemSettings in ObjectList. + // So, check if selected item isn't SettingsItem. And get a SettingsItem's parent item, if yes + wxDataViewItem item = obj_model->GetItemType(sel_item) & itSettings ? obj_model->GetParent(sel_item) : sel_item; + const ItemType item_type = obj_model->GetItemType(item); + const bool is_object_settings = !(item_type& itVolume || item_type & itLayer); + + // Add frequently settings + create_freq_settings_popupmenu(menu, is_object_settings, item); + + if (mode == comAdvanced) + return nullptr; + + menu->SetSecondSeparator(); + + // Add full settings list + auto menu_item = new wxMenuItem(menu, wxID_ANY, menu_name); + menu_item->SetBitmap(create_scaled_bitmap("cog")); + menu_item->SetSubMenu(create_settings_popupmenu(menu, is_object_settings, item)); + + return menu->Append(menu_item); +} + +wxMenuItem* MenuFactory::append_menu_item_change_type(wxMenu* menu) +{ + return append_menu_item(menu, wxID_ANY, _L("Change type"), "", + [](wxCommandEvent&) { obj_list()->change_part_type(); }, "", menu, + []() { + wxDataViewItem item = obj_list()->GetSelection(); + return item.IsOk() || obj_list()->GetModel()->GetItemType(item) == itVolume; + }, m_parent); +} + +wxMenuItem* MenuFactory::append_menu_item_instance_to_object(wxMenu* menu) +{ + wxMenuItem* menu_item = append_menu_item(menu, wxID_ANY, _L("Set as a Separated Object"), "", + [](wxCommandEvent&) { obj_list()->split_instances(); }, "", menu); + + /* New behavior logic: + * 1. Split Object to several separated object, if ALL instances are selected + * 2. Separate selected instances from the initial object to the separated object, + * if some (not all) instances are selected + */ + m_parent->Bind(wxEVT_UPDATE_UI, [](wxUpdateUIEvent& evt) + { + const Selection& selection = plater()->canvas3D()->get_selection(); + evt.SetText(selection.is_single_full_object() ? + _L("Set as a Separated Objects") : _L("Set as a Separated Object")); + + evt.Enable(plater()->can_set_instance_to_object()); + }, menu_item->GetId()); + + return menu_item; +} + +wxMenuItem* MenuFactory::append_menu_item_printable(wxMenu* menu) +{ + return append_menu_check_item(menu, wxID_ANY, _L("Printable"), "", [](wxCommandEvent&) { + const Selection& selection = plater()->canvas3D()->get_selection(); + wxDataViewItem item; + if (obj_list()->GetSelectedItemsCount() > 1 && selection.is_single_full_object()) + item = obj_list()->GetModel()->GetItemById(selection.get_object_idx()); + else + item = obj_list()->GetSelection(); + + if (item) + obj_list()->toggle_printable_state(item); + }, menu); +} + +void MenuFactory::append_menu_items_osx(wxMenu* menu) +{ + append_menu_item(menu, wxID_ANY, _L("Rename"), "", + [](wxCommandEvent&) { obj_list()->rename_item(); }, "", menu); + + menu->AppendSeparator(); +} + +wxMenuItem* MenuFactory::append_menu_item_fix_through_netfabb(wxMenu* menu) +{ + if (!is_windows10()) + 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()); + menu->AppendSeparator(); + + return menu_item; +} + +void MenuFactory::append_menu_item_export_stl(wxMenu* menu) +{ + append_menu_item(menu, wxID_ANY, _L("Export as STL") + dots, "", + [](wxCommandEvent&) { plater()->export_stl(false, true); }, "", nullptr, + []() { + const Selection& selection = plater()->canvas3D()->get_selection(); + return selection.is_single_full_instance() || selection.is_single_full_object(); + }, m_parent); + menu->AppendSeparator(); +} + +void MenuFactory::append_menu_item_reload_from_disk(wxMenu* menu) +{ + append_menu_item(menu, wxID_ANY, _L("Reload from disk"), _L("Reload the selected volumes from disk"), + [](wxCommandEvent&) { plater()->reload_from_disk(); }, "", menu, + []() { return plater()->can_reload_from_disk(); }, m_parent); +} + +void MenuFactory::append_menu_item_change_extruder(wxMenu* menu) +{ + const std::vector names = { _L("Change extruder"), _L("Set extruder for selected items") }; + // Delete old menu item + for (const wxString& name : names) { + const int item_id = menu->FindItem(name); + if (item_id != wxNOT_FOUND) + menu->Destroy(item_id); + } + + const int extruders_cnt = extruders_count(); + if (extruders_cnt <= 1) + return; + + wxDataViewItemArray sels; + obj_list()->GetSelections(sels); + if (sels.IsEmpty()) + return; + + std::vector icons = get_extruder_color_icons(true); + wxMenu* extruder_selection_menu = new wxMenu(); + const wxString& name = sels.Count() == 1 ? names[0] : names[1]; + + int initial_extruder = -1; // negative value for multiple object/part selection + if (sels.Count() == 1) { + const ModelConfig& config = obj_list()->get_item_config(sels[0]); + initial_extruder = config.has("extruder") ? config.extruder() : 0; + } + + for (int i = 0; i <= extruders_cnt; i++) + { + bool is_active_extruder = i == initial_extruder; + int icon_idx = i == 0 ? 0 : i - 1; + + const wxString& item_name = (i == 0 ? _L("Default") : wxString::Format(_L("Extruder %d"), i)) + + (is_active_extruder ? " (" + _L("active") + ")" : ""); + + append_menu_item(extruder_selection_menu, wxID_ANY, item_name, "", + [i](wxCommandEvent&) { obj_list()->set_extruder_for_selected_items(i); }, *icons[icon_idx], menu, + [is_active_extruder]() { return !is_active_extruder; }, m_parent); + + } + + menu->AppendSubMenu(extruder_selection_menu, name); +} + +void MenuFactory::append_menu_item_scale_selection_to_fit_print_volume(wxMenu* menu) +{ + append_menu_item(menu, wxID_ANY, _L("Scale to print volume"), _L("Scale the selected object to fit the print volume"), + [](wxCommandEvent&) { plater()->scale_selection_to_fit_print_volume(); }, "", menu); +} + +void MenuFactory::append_menu_items_convert_unit(wxMenu* menu, int insert_pos/* = 1*/) +{ + std::vector obj_idxs, vol_idxs; + obj_list()->get_selection_indexes(obj_idxs, vol_idxs); + if (obj_idxs.empty() && vol_idxs.empty()) + return; + + auto volume_respects_conversion = [](ModelVolume* volume, ConversionType conver_type) + { + return (conver_type == ConversionType::CONV_FROM_INCH && volume->source.is_converted_from_inches) || + (conver_type == ConversionType::CONV_TO_INCH && !volume->source.is_converted_from_inches) || + (conver_type == ConversionType::CONV_FROM_METER && volume->source.is_converted_from_meters) || + (conver_type == ConversionType::CONV_TO_METER && !volume->source.is_converted_from_meters); + }; + + auto can_append = [obj_idxs, vol_idxs, volume_respects_conversion](ConversionType conver_type) + { + ModelObjectPtrs objects; + for (int obj_idx : obj_idxs) { + ModelObject* object = obj_list()->object(obj_idx); + if (vol_idxs.empty()) { + for (ModelVolume* volume : object->volumes) + if (volume_respects_conversion(volume, conver_type)) + return false; + } + else { + for (int vol_idx : vol_idxs) + if (volume_respects_conversion(object->volumes[vol_idx], conver_type)) + return false; + } + } + return true; + }; + + std::vector> items = { + {ConversionType::CONV_FROM_INCH , _L("Convert from imperial units") }, + {ConversionType::CONV_TO_INCH , _L("Revert conversion from imperial units") }, + {ConversionType::CONV_FROM_METER, _L("Convert from meters") }, + {ConversionType::CONV_TO_METER , _L("Revert conversion from meters") } }; + + for (auto item : items) { + int menu_id = menu->FindItem(item.second); + if (can_append(item.first)) { + // Add menu item if it doesn't exist + if (menu_id == wxNOT_FOUND) + append_menu_item(menu, wxID_ANY, item.second, item.second, + [item](wxCommandEvent&) { plater()->convert_unit(item.first); }, "", menu, + []() { return true; }, m_parent, insert_pos); + } + else if (menu_id != wxNOT_FOUND) { + // Delete menu item + menu->Destroy(menu_id); + } + } +} + +void MenuFactory::append_menu_item_merge_to_multipart_object(wxMenu* menu) +{ + menu->AppendSeparator(); + append_menu_item(menu, wxID_ANY, _L("Merge"), _L("Merge objects to the one multipart object"), + [](wxCommandEvent&) { obj_list()->merge(true); }, "", menu, + []() { return obj_list()->can_merge_to_multipart_object(); }, m_parent); +} +/* +void MenuFactory::append_menu_item_merge_to_single_object(wxMenu* menu) +{ + menu->AppendSeparator(); + append_menu_item(menu, wxID_ANY, _L("Merge"), _L("Merge objects to the one single object"), + [](wxCommandEvent&) { obj_list()->merge(false); }, "", menu, + []() { return obj_list()->can_merge_to_single_object(); }, m_parent); +} +*/ +void MenuFactory::append_menu_items_mirror(wxMenu* menu) +{ + wxMenu* mirror_menu = new wxMenu(); + if (!mirror_menu) + return; + + append_menu_item(mirror_menu, wxID_ANY, _L("Along X axis"), _L("Mirror the selected object along the X axis"), + [](wxCommandEvent&) { plater()->mirror(X); }, "mark_X", menu); + append_menu_item(mirror_menu, wxID_ANY, _L("Along Y axis"), _L("Mirror the selected object along the Y axis"), + [](wxCommandEvent&) { plater()->mirror(Y); }, "mark_Y", menu); + append_menu_item(mirror_menu, wxID_ANY, _L("Along Z axis"), _L("Mirror the selected object along the Z axis"), + [](wxCommandEvent&) { plater()->mirror(Z); }, "mark_Z", menu); + + append_submenu(menu, mirror_menu, wxID_ANY, _L("Mirror"), _L("Mirror the selected object"), "", + []() { return plater()->can_mirror(); }, m_parent); +} + +MenuFactory::MenuFactory() +{ + for (int i = 0; i < mtCount; i++) { + items_increase[i] = nullptr; + items_decrease[i] = nullptr; + items_set_number_of_copies[i] = nullptr; + } +} + +void MenuFactory::create_default_menu() +{ + wxMenu* sub_menu = append_submenu_add_generic(&m_default_menu, ModelVolumeType::INVALID); + append_submenu(&m_default_menu, sub_menu, wxID_ANY, _L("Add Shape"), "", "add_part", + []() {return true; }, m_parent); +} + +void MenuFactory::create_common_object_menu(wxMenu* menu) +{ +#ifdef __WXOSX__ + append_menu_items_osx(menu); +#endif // __WXOSX__ + append_menu_items_instance_manipulation(menu); + // Delete menu was moved to be after +/- instace to make it more difficult to be selected by mistake. + append_menu_item_delete(menu); + append_menu_item_instance_to_object(menu); + menu->AppendSeparator(); + + wxMenuItem* menu_item_printable = append_menu_item_printable(menu); + menu->AppendSeparator(); + + append_menu_item_reload_from_disk(menu); + append_menu_item_export_stl(menu); + // "Scale to print volume" makes a sense just for whole object + append_menu_item_scale_selection_to_fit_print_volume(menu); + + append_menu_item_fix_through_netfabb(menu); + append_menu_items_mirror(menu); + + m_parent->Bind(wxEVT_UPDATE_UI, [](wxUpdateUIEvent& evt) { + const Selection& selection = get_selection(); + int instance_idx = selection.get_instance_idx(); + evt.Enable(selection.is_single_full_instance() || selection.is_single_full_object()); + if (instance_idx != -1) { + evt.Check(obj_list()->object(selection.get_object_idx())->instances[instance_idx]->printable); + plater()->set_current_canvas_as_dirty();//view3D->set_as_dirty(); + } + }, menu_item_printable->GetId()); +} + +void MenuFactory::create_object_menu() +{ + create_common_object_menu(&m_object_menu); + wxMenu* split_menu = new wxMenu(); + if (!split_menu) + return; + + append_menu_item(split_menu, wxID_ANY, _L("To objects"), _L("Split the selected object into individual objects"), + [](wxCommandEvent&) { plater()->split_object(); }, "split_object_SMALL", &m_object_menu, + []() { return plater()->can_split(true); }, m_parent); + append_menu_item(split_menu, wxID_ANY, _L("To parts"), _L("Split the selected object into individual parts"), + [](wxCommandEvent&) { plater()->split_volume(); }, "split_parts_SMALL", &m_object_menu, + []() { return plater()->can_split(false); }, m_parent); + + append_submenu(&m_object_menu, split_menu, wxID_ANY, _L("Split"), _L("Split the selected object"), "", + []() { return plater()->can_split(true) && wxGetApp().get_mode() > comSimple; }, m_parent); + m_object_menu.AppendSeparator(); + + // Layers Editing for object + append_menu_item_layers_editing(&m_object_menu); + m_object_menu.AppendSeparator(); + + // "Add (volumes)" popupmenu will be added later in append_menu_items_add_volume() +} + +void MenuFactory::create_sla_object_menu() +{ + create_common_object_menu(&m_sla_object_menu); + append_menu_item(&m_sla_object_menu, wxID_ANY, _L("Split"), _L("Split the selected object into individual objects"), + [](wxCommandEvent&) { plater()->split_object(); }, "split_object_SMALL", nullptr, + []() { return plater()->can_split(true); }, m_parent); + + m_sla_object_menu.AppendSeparator(); + + // Add the automatic rotation sub-menu + append_menu_item(&m_sla_object_menu, wxID_ANY, _L("Optimize orientation"), _L("Optimize the rotation of the object for better print results."), + [](wxCommandEvent&) { plater()->optimize_rotation(); }); +} + +void MenuFactory::create_part_menu() +{ + wxMenu* menu = &m_part_menu; +#ifdef __WXOSX__ + append_menu_items_osx(menu); +#endif // __WXOSX__ + append_menu_item_delete(menu); + append_menu_item_reload_from_disk(menu); + append_menu_item_export_stl(menu); + append_menu_item_fix_through_netfabb(menu); + append_menu_items_mirror(menu); + + append_menu_item(menu, wxID_ANY, _L("Split"), _L("Split the selected object into individual parts"), + [](wxCommandEvent&) { plater()->split_volume(); }, "split_parts_SMALL", nullptr, + []() { return plater()->can_split(false); }, m_parent); + + menu->AppendSeparator(); + append_menu_item_change_type(menu); + +} + +void MenuFactory::init(wxWindow* parent) +{ + m_parent = parent; + + create_default_menu(); + create_object_menu(); + create_sla_object_menu(); + create_part_menu(); + + // create "Instance to Object" menu item + append_menu_item_instance_to_object(&m_instance_menu); +} + +wxMenu* MenuFactory::default_menu() +{ + return &m_default_menu; +} + +wxMenu* MenuFactory::object_menu() +{ + append_menu_items_convert_unit(&m_object_menu, 11); + append_menu_item_settings(&m_object_menu); + append_menu_item_change_extruder(&m_object_menu); + update_menu_items_instance_manipulation(mtObjectFFF); + + return &m_object_menu; +} + +wxMenu* MenuFactory::sla_object_menu() +{ + append_menu_items_convert_unit(&m_sla_object_menu, 11); + append_menu_item_settings(&m_sla_object_menu); + update_menu_items_instance_manipulation(mtObjectSLA); + + return &m_sla_object_menu; +} + +wxMenu* MenuFactory::part_menu() +{ + append_menu_items_convert_unit(&m_part_menu, 2); + append_menu_item_settings(&m_part_menu); + append_menu_item_change_extruder(&m_part_menu); + + return &m_part_menu; +} + +wxMenu* MenuFactory::instance_menu() +{ + return &m_instance_menu; +} + +wxMenu* MenuFactory::layer_menu() +{ + MenuWithSeparators* menu = new MenuWithSeparators(); + append_menu_item_settings(menu); + + return menu; +} + +wxMenu* MenuFactory::multi_selection_menu() +{ + wxDataViewItemArray sels; + obj_list()->GetSelections(sels); + + for (const wxDataViewItem& item : sels) + if (!(list_model()->GetItemType(item) & (itVolume | itObject | itInstance))) + // show this menu only for Objects(Instances mixed with Objects)/Volumes selection + return nullptr; + + wxMenu* menu = new MenuWithSeparators(); + + append_menu_item_reload_from_disk(menu); + append_menu_items_convert_unit(menu); + if (obj_list()->can_merge_to_multipart_object()) + append_menu_item_merge_to_multipart_object(menu); + if (extruders_count() > 1) + append_menu_item_change_extruder(menu); + + return menu; +} + +void MenuFactory::append_menu_items_instance_manipulation(wxMenu* menu) +{ + MenuType type = menu == &m_object_menu ? mtObjectFFF : mtObjectSLA; + + items_increase[type] = append_menu_item(menu, wxID_ANY, _L("Add instance") + "\t+", _L("Add one more instance of the selected object"), + [](wxCommandEvent&) { plater()->increase_instances(); }, "add_copies", nullptr, + []() { return plater()->can_increase_instances(); }, m_parent); + items_decrease[type] = append_menu_item(menu, wxID_ANY, _L("Remove instance") + "\t-", _L("Remove one instance of the selected object"), + [](wxCommandEvent&) { plater()->decrease_instances(); }, "remove_copies", nullptr, + []() { return plater()->can_decrease_instances(); }, m_parent); + items_set_number_of_copies[type] = append_menu_item(menu, wxID_ANY, _L("Set number of instances") + dots, _L("Change the number of instances of the selected object"), + [](wxCommandEvent&) { plater()->set_number_of_copies(); }, "number_of_copies", nullptr, + []() { return plater()->can_increase_instances(); }, m_parent); + + append_menu_item(menu, wxID_ANY, _L("Fill bed with instances") + dots, _L("Fill the remaining area of bed with instances of the selected object"), + [](wxCommandEvent&) { plater()->fill_bed_with_instances(); }, "", nullptr, + []() { return plater()->can_increase_instances(); }, m_parent); + +} + +void MenuFactory::update_menu_items_instance_manipulation(MenuType type) +{ + wxMenu* menu = type == mtObjectFFF ? &m_object_menu : type == mtObjectSLA ? &m_sla_object_menu : nullptr; + if (menu) + return; + // Remove/Prepend "increase/decrease instances" menu items according to the view mode. + // Suppress to show those items for a Simple mode + if (wxGetApp().get_mode() == comSimple) { + if (menu->FindItem(_L("Add instance")) != wxNOT_FOUND) + { + // Detach an items from the menu, but don't delete them + // so that they can be added back later + // (after switching to the Advanced/Expert mode) + menu->Remove(items_increase[type]); + menu->Remove(items_decrease[type]); + menu->Remove(items_set_number_of_copies[type]); + } + } + else { + if (menu->FindItem(_L("Add instance")) == wxNOT_FOUND) + { + // Prepend items to the menu, if those aren't not there + menu->Prepend(items_set_number_of_copies[type]); + menu->Prepend(items_decrease[type]); + menu->Prepend(items_increase[type]); + } + } +} + +void MenuFactory::update_object_menu() +{ + append_menu_items_add_volume(&m_object_menu); +} + +void MenuFactory::msw_rescale() +{ + for (MenuWithSeparators* menu : { &m_object_menu, &m_sla_object_menu, &m_part_menu, &m_default_menu }) + msw_rescale_menu(dynamic_cast(menu)); +} + +} //namespace GUI +} //namespace Slic3r diff --git a/src/slic3r/GUI/GUI_Factories.hpp b/src/slic3r/GUI/GUI_Factories.hpp new file mode 100644 index 000000000..3cc5759a1 --- /dev/null +++ b/src/slic3r/GUI/GUI_Factories.hpp @@ -0,0 +1,104 @@ +#ifndef slic3r_GUI_Factories_hpp_ +#define slic3r_GUI_Factories_hpp_ + +#include +#include +#include + +#include + +#include "libslic3r/PrintConfig.hpp" +#include "wxExtensions.hpp" + +class wxMenu; +class wxMenuItem; + +namespace Slic3r { + +enum class ModelVolumeType : int; + +namespace GUI { + +struct SettingsFactory +{ +// category -> vector ( option ) + typedef std::map> Bundle; + static std::map CATEGORY_ICON; + + static wxBitmap get_category_bitmap(const std::string& category_name); + static Bundle get_bundle(const DynamicPrintConfig* config, bool is_object_settings); + static std::vector get_options(bool is_part); +}; + +class MenuFactory +{ +public: + static std::vector> ADD_VOLUME_MENU_ITEMS; + static std::vector get_volume_bitmaps(); + + MenuFactory(); + ~MenuFactory() = default; + + void init(wxWindow* parent); + void update_object_menu(); + void msw_rescale(); + + wxMenu* default_menu(); + wxMenu* object_menu(); + wxMenu* sla_object_menu(); + wxMenu* part_menu(); + wxMenu* instance_menu(); + wxMenu* layer_menu(); + wxMenu* multi_selection_menu(); + +private: + enum MenuType { + mtObjectFFF = 0, + mtObjectSLA, + mtCount + }; + + wxWindow* m_parent {nullptr}; + + MenuWithSeparators m_object_menu; + MenuWithSeparators m_part_menu; + MenuWithSeparators m_sla_object_menu; + MenuWithSeparators m_default_menu; + MenuWithSeparators m_instance_menu; + + // Removed/Prepended Items according to the view mode + std::array items_increase; + std::array items_decrease; + std::array items_set_number_of_copies; + + void create_default_menu(); + void create_common_object_menu(wxMenu *menu); + void create_object_menu(); + void create_sla_object_menu(); + void create_part_menu(); + + wxMenu* append_submenu_add_generic(wxMenu* menu, ModelVolumeType type); + void append_menu_items_add_volume(wxMenu* menu); + wxMenuItem* append_menu_item_layers_editing(wxMenu* menu); + wxMenuItem* append_menu_item_settings(wxMenu* menu); + wxMenuItem* append_menu_item_change_type(wxMenu* menu); + wxMenuItem* append_menu_item_instance_to_object(wxMenu* menu); + wxMenuItem* append_menu_item_printable(wxMenu* menu); + void append_menu_items_osx(wxMenu* menu); + wxMenuItem* append_menu_item_fix_through_netfabb(wxMenu* menu); + void append_menu_item_export_stl(wxMenu* menu); + void append_menu_item_reload_from_disk(wxMenu* menu); + void append_menu_item_change_extruder(wxMenu* menu); + void append_menu_item_delete(wxMenu* menu); + void append_menu_item_scale_selection_to_fit_print_volume(wxMenu* menu); + void append_menu_items_convert_unit(wxMenu* menu, int insert_pos = 1); // Add "Conver/Revert..." menu items (from/to inches/meters) after "Reload From Disk" + void append_menu_item_merge_to_multipart_object(wxMenu *menu); +// void append_menu_item_merge_to_single_object(wxMenu *menu); + void append_menu_items_mirror(wxMenu *menu); + void append_menu_items_instance_manipulation(wxMenu *menu); + void update_menu_items_instance_manipulation(MenuType type); +}; + +}} + +#endif //slic3r_GUI_Factories_hpp_ diff --git a/src/slic3r/GUI/GUI_ObjectLayers.cpp b/src/slic3r/GUI/GUI_ObjectLayers.cpp index 8768f39ff..725a396c3 100644 --- a/src/slic3r/GUI/GUI_ObjectLayers.cpp +++ b/src/slic3r/GUI/GUI_ObjectLayers.cpp @@ -260,16 +260,15 @@ void ObjectLayers::msw_rescale() editor->msw_rescale(); } - const std::vector btns = {2, 3}; // del_btn, add_btn - for (auto btn : btns) - { - wxSizerItem* b_item = item->GetSizer()->GetItem(btn); - if (b_item->IsWindow()) { - auto button = dynamic_cast(b_item->GetWindow()); - if (button != nullptr) - button->msw_rescale(); - } - } + if (item->GetSizer()->GetItemCount() > 2) // if there are Add/Del buttons + for (size_t btn : {2, 3}) { // del_btn, add_btn + wxSizerItem* b_item = item->GetSizer()->GetItem(btn); + if (b_item->IsWindow()) { + auto button = dynamic_cast(b_item->GetWindow()); + if (button != nullptr) + button->msw_rescale(); + } + } } } m_grid_sizer->Layout(); diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 57f1841e3..f37276beb 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -1,6 +1,7 @@ #include "libslic3r/libslic3r.h" #include "libslic3r/PresetBundle.hpp" #include "GUI_ObjectList.hpp" +#include "GUI_Factories.hpp" #include "GUI_ObjectManipulation.hpp" #include "GUI_ObjectLayers.hpp" #include "GUI_App.hpp" @@ -31,32 +32,6 @@ namespace GUI wxDEFINE_EVENT(EVT_OBJ_LIST_OBJECT_SELECT, SimpleEvent); -// pt_FFF -static SettingsBundle FREQ_SETTINGS_BUNDLE_FFF = -{ - { L("Layers and Perimeters"), { "layer_height" , "perimeters", "top_solid_layers", "bottom_solid_layers" } }, - { L("Infill") , { "fill_density", "fill_pattern" } }, - { L("Support material") , { "support_material", "support_material_auto", "support_material_threshold", - "support_material_pattern", "support_material_interface_pattern", "support_material_buildplate_only", - "support_material_spacing" } }, - { L("Wipe options") , { "wipe_into_infill", "wipe_into_objects" } } -}; - -// pt_SLA -static SettingsBundle FREQ_SETTINGS_BUNDLE_SLA = -{ - { L("Pad and Support") , { "supports_enable", "pad_enable" } } -}; - -// Note: id accords to type of the sub-object (adding volume), so sequence of the menu items is important -static std::vector> ADD_VOLUME_MENU_ITEMS = { -// menu_item Name menu_item bitmap name - {L("Add part"), "add_part" }, // ~ModelVolumeType::MODEL_PART - {L("Add modifier"), "add_modifier"}, // ~ModelVolumeType::PARAMETER_MODIFIER - {L("Add support enforcer"), "support_enforcer"}, // ~ModelVolumeType::SUPPORT_ENFORCER - {L("Add support blocker"), "support_blocker"} // ~ModelVolumeType::SUPPORT_BLOCKER -}; - static PrinterTechnology printer_technology() { return wxGetApp().preset_bundle->printers.get_selected_preset().printer_technology(); @@ -86,35 +61,11 @@ static void take_snapshot(const wxString& snapshot_name) } ObjectList::ObjectList(wxWindow* parent) : - wxDataViewCtrl(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxDV_MULTIPLE), - m_parent(parent) + wxDataViewCtrl(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxDV_MULTIPLE) { - // Fill CATEGORY_ICON - { - // ptFFF - CATEGORY_ICON[L("Layers and Perimeters")] = create_scaled_bitmap("layers"); - CATEGORY_ICON[L("Infill")] = create_scaled_bitmap("infill"); - CATEGORY_ICON[L("Ironing")] = create_scaled_bitmap("ironing"); - CATEGORY_ICON[L("Fuzzy Skin")] = create_scaled_bitmap("fuzzy_skin"); - CATEGORY_ICON[L("Support material")] = create_scaled_bitmap("support"); - CATEGORY_ICON[L("Speed")] = create_scaled_bitmap("time"); - CATEGORY_ICON[L("Extruders")] = create_scaled_bitmap("funnel"); - CATEGORY_ICON[L("Extrusion Width")] = create_scaled_bitmap("funnel"); - CATEGORY_ICON[L("Wipe options")] = create_scaled_bitmap("funnel"); - CATEGORY_ICON[L("Skirt and brim")] = create_scaled_bitmap("skirt+brim"); -// CATEGORY_ICON[L("Speed > Acceleration")] = create_scaled_bitmap("time"); - CATEGORY_ICON[L("Advanced")] = create_scaled_bitmap("wrench"); - // ptSLA - CATEGORY_ICON[L("Supports")] = create_scaled_bitmap("support"/*"sla_supports"*/); - CATEGORY_ICON[L("Pad")] = create_scaled_bitmap("pad"); - CATEGORY_ICON[L("Hollowing")] = create_scaled_bitmap("hollowing"); - } - // create control create_objects_ctrl(); - init_icons(); - // describe control behavior Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, [this](wxDataViewEvent& event) { // detect the current mouse position here, to pass it to list_manipulation() method @@ -125,11 +76,6 @@ ObjectList::ObjectList(wxWindow* parent) : #endif #ifndef __APPLE__ - // On Windows and Linux, forces a kill focus emulation on the object manipulator fields because this event handler is called - // before the kill focus event handler on the object manipulator when changing selection in the list, invalidating the object - // manipulator cache with the following call to selection_changed() -// wxGetApp().obj_manipul()->emulate_kill_focus(); // It's not necessury anymore #ys_FIXME delete after testing - // On Windows and Linux: // It's not invoked KillFocus event for "temporary" panels (like "Manipulation panel", "Settings", "Layer ranges"), // if we change selection in object list. @@ -347,16 +293,6 @@ void ObjectList::create_objects_ctrl() } } -void ObjectList::create_popup_menus() -{ - // create popup menus for object and part - create_object_popupmenu(&m_menu_object); - create_part_popupmenu(&m_menu_part); - create_sla_object_popupmenu(&m_menu_sla_object); - create_instance_popupmenu(&m_menu_instance); - create_default_popupmenu(&m_menu_default); -} - void ObjectList::get_selected_item_indexes(int& obj_idx, int& vol_idx, const wxDataViewItem& input_item/* = wxDataViewItem(nullptr)*/) { const wxDataViewItem item = input_item == wxDataViewItem(nullptr) ? GetSelection() : input_item; @@ -381,10 +317,14 @@ void ObjectList::get_selection_indexes(std::vector& obj_idxs, std::vectorGetItemType(sels[0]) & itVolume) { + if ( m_objects_model->GetItemType(sels[0]) & itVolume || + (sels.Count()==1 && m_objects_model->GetItemType(m_objects_model->GetParent(sels[0])) & itVolume) ) { for (wxDataViewItem item : sels) { obj_idxs.emplace_back(m_objects_model->GetIdByItem(m_objects_model->GetTopParent(item))); + if (sels.Count() == 1 && m_objects_model->GetItemType(m_objects_model->GetParent(item)) & itVolume) + item = m_objects_model->GetParent(item); + assert(m_objects_model->GetItemType(item) & itVolume); vol_idxs.emplace_back(m_objects_model->GetVolumeIdByItem(item)); } @@ -392,8 +332,6 @@ void ObjectList::get_selection_indexes(std::vector& obj_idxs, std::vectorGetItemType(item); - assert(type & (itObject | itInstance | itInstanceRoot)); - obj_idxs.emplace_back(type & itObject ? m_objects_model->GetIdByItem(item) : m_objects_model->GetIdByItem(m_objects_model->GetTopParent(item))); } @@ -633,75 +571,6 @@ void ObjectList::update_name_in_model(const wxDataViewItem& item) const (*m_objects)[obj_idx]->volumes[volume_id]->name = m_objects_model->GetName(item).ToUTF8().data(); } -void ObjectList::init_icons() -{ - m_bmp_solidmesh = ScalableBitmap(this, ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::MODEL_PART) ].second); - m_bmp_modifiermesh = ScalableBitmap(this, ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::PARAMETER_MODIFIER)].second); - m_bmp_support_enforcer = ScalableBitmap(this, ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::SUPPORT_ENFORCER) ].second); - m_bmp_support_blocker = ScalableBitmap(this, ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::SUPPORT_BLOCKER) ].second); - - m_bmp_vector.reserve(4); // bitmaps for different types of parts - m_bmp_vector.push_back(&m_bmp_solidmesh.bmp()); - m_bmp_vector.push_back(&m_bmp_modifiermesh.bmp()); - m_bmp_vector.push_back(&m_bmp_support_enforcer.bmp()); - m_bmp_vector.push_back(&m_bmp_support_blocker.bmp()); - - - // Set volumes default bitmaps for the model - m_objects_model->SetVolumeBitmaps(m_bmp_vector); - - // init icon for manifold warning - m_bmp_manifold_warning = ScalableBitmap(this, "exclamation"); - // Set warning bitmap for the model - m_objects_model->SetWarningBitmap(&m_bmp_manifold_warning.bmp()); - - // init bitmap for "Add Settings" context menu - m_bmp_cog = ScalableBitmap(this, "cog"); -} - -void ObjectList::msw_rescale_icons() -{ - m_bmp_vector.clear(); - m_bmp_vector.reserve(4); // bitmaps for different types of parts - for (ScalableBitmap* bitmap : { &m_bmp_solidmesh, // Add part - &m_bmp_modifiermesh, // Add modifier - &m_bmp_support_enforcer, // Add support enforcer - &m_bmp_support_blocker }) // Add support blocker - { - bitmap->msw_rescale(); - m_bmp_vector.push_back(& bitmap->bmp()); - } - // Set volumes default bitmaps for the model - m_objects_model->SetVolumeBitmaps(m_bmp_vector); - - m_bmp_manifold_warning.msw_rescale(); - // Set warning bitmap for the model - m_objects_model->SetWarningBitmap(&m_bmp_manifold_warning.bmp()); - - m_bmp_cog.msw_rescale(); - - - // Update CATEGORY_ICON according to new scale - { - // ptFFF - CATEGORY_ICON[L("Layers and Perimeters")] = create_scaled_bitmap("layers"); - CATEGORY_ICON[L("Infill")] = create_scaled_bitmap("infill"); - CATEGORY_ICON[L("Ironing")] = create_scaled_bitmap("ironing"); - CATEGORY_ICON[L("Support material")] = create_scaled_bitmap("support"); - CATEGORY_ICON[L("Speed")] = create_scaled_bitmap("time"); - CATEGORY_ICON[L("Extruders")] = create_scaled_bitmap("funnel"); - CATEGORY_ICON[L("Extrusion Width")] = create_scaled_bitmap("funnel"); - CATEGORY_ICON[L("Wipe options")] = create_scaled_bitmap("funnel"); - CATEGORY_ICON[L("Skirt and brim")] = create_scaled_bitmap("skirt+brim"); -// CATEGORY_ICON[L("Speed > Acceleration")] = create_scaled_bitmap("time"); - CATEGORY_ICON[L("Advanced")] = create_scaled_bitmap("wrench"); - // ptSLA - CATEGORY_ICON[L("Supports")] = create_scaled_bitmap("support"/*"sla_supports"*/); - CATEGORY_ICON[L("Pad")] = create_scaled_bitmap("pad"); - } -} - - void ObjectList::selection_changed() { if (m_prevent_list_events) return; @@ -821,7 +690,7 @@ void ObjectList::paste_settings_into_list() assert(!config_cache.empty()); auto keys = config_cache.keys(); - auto part_options = get_options(true); + auto part_options = SettingsFactory::get_options(true); for (const std::string& opt_key: keys) { if (item_type & (itVolume | itLayer) && @@ -867,9 +736,7 @@ void ObjectList::paste_volumes_into_list(int obj_idx, const ModelVolumePtrs& vol } select_items(items); -//#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME selection_changed(); -//#endif //no __WXOSX__ //__WXMSW__ } void ObjectList::paste_objects_into_list(const std::vector& object_idxs) @@ -887,9 +754,7 @@ void ObjectList::paste_objects_into_list(const std::vector& object_idxs) wxGetApp().plater()->changed_objects(object_idxs); select_items(items); -//#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME selection_changed(); -//#endif //no __WXOSX__ //__WXMSW__ } #ifdef __WXOSX__ @@ -1002,39 +867,35 @@ void ObjectList::list_manipulation(const wxPoint& mouse_pos, bool evt_context_me void ObjectList::show_context_menu(const bool evt_context_menu) { + wxMenu* menu {nullptr}; + Plater* plater = wxGetApp().plater(); + if (multiple_selection()) { if (selected_instances_of_same_object()) - wxGetApp().plater()->PopupMenu(&m_menu_instance); + menu = plater->instance_menu(); else - show_multi_selection_menu(); - - return; + menu = plater->multi_selection_menu(); } + else { + const auto item = GetSelection(); + if (item) + { + const ItemType type = m_objects_model->GetItemType(item); + if (!(type & (itObject | itVolume | itLayer | itInstance))) + return; - const auto item = GetSelection(); - wxMenu* menu {nullptr}; - if (item) - { - const ItemType type = m_objects_model->GetItemType(item); - if (!(type & (itObject | itVolume | itLayer | itInstance))) - return; - - menu = type & itInstance ? &m_menu_instance : - type & itLayer ? &m_menu_layer : - m_objects_model->GetParent(item) != wxDataViewItem(nullptr) ? &m_menu_part : - printer_technology() == ptFFF ? &m_menu_object : &m_menu_sla_object; - - if (type & (itObject | itVolume)) - append_menu_items_convert_unit(menu); - if (!(type & itInstance)) - append_menu_item_settings(menu); + menu = type & itInstance ? plater->instance_menu() : + type & itLayer ? plater->layer_menu() : + m_objects_model->GetParent(item) != wxDataViewItem(nullptr) ? plater->part_menu() : + printer_technology() == ptFFF ? plater->object_menu() : plater->sla_object_menu(); + } + else if (evt_context_menu) + menu = plater->default_menu(); } - else if (evt_context_menu) - menu = &m_menu_default; if (menu) - wxGetApp().plater()->PopupMenu(menu); + plater->PopupMenu(menu); } void ObjectList::extruder_editing() @@ -1274,15 +1135,6 @@ void ObjectList::OnDrop(wxDataViewEvent &event) return; } -// It looks like a fixed in current version of the wxWidgets -// #ifdef __WXGTK__ -// /* Under GTK, DnD moves an item between another two items. -// * And event.GetItem() return item, which is under "insertion line" -// * So, if we move item down we should to decrease the to_volume_id value -// **/ -// if (to_volume_id > from_volume_id) to_volume_id--; -// #endif // __WXGTK__ - take_snapshot(_((m_dragged_data.type() == itVolume) ? L("Volumes in Object reordered") : L("Object reordered"))); if (m_dragged_data.type() & itVolume) @@ -1320,207 +1172,34 @@ void ObjectList::OnDrop(wxDataViewEvent &event) wxGetApp().plater()->set_current_canvas_as_dirty(); } - -// Context Menu - -std::vector ObjectList::get_options(const bool is_part) +void ObjectList::add_category_to_settings_from_selection(const std::vector< std::pair >& category_options, wxDataViewItem item) { - if (printer_technology() == ptSLA) { - SLAPrintObjectConfig full_sla_config; - auto options = full_sla_config.keys(); - options.erase(find(options.begin(), options.end(), "layer_height")); - return options; - } - - PrintRegionConfig reg_config; - auto options = reg_config.keys(); - if (!is_part) { - PrintObjectConfig obj_config; - std::vector obj_options = obj_config.keys(); - options.insert(options.end(), obj_options.begin(), obj_options.end()); - } - return options; -} - -const std::vector& ObjectList::get_options_for_bundle(const wxString& bundle_name) -{ - const SettingsBundle& bundle = printer_technology() == ptSLA ? - FREQ_SETTINGS_BUNDLE_SLA : FREQ_SETTINGS_BUNDLE_FFF; - - for (auto& it : bundle) - { - if (bundle_name == _(it.first)) - return it.second; - } -#if 0 - // if "Quick menu" is selected - SettingsBundle& bundle_quick = printer_technology() == ptSLA ? - m_freq_settings_sla: m_freq_settings_fff; - - for (auto& it : bundle_quick) - { - if ( bundle_name == from_u8((boost::format(_utf8(L("Quick Add Settings (%s)"))) % _(it.first)).str()) ) - return it.second; - } -#endif - - static std::vector empty; - return empty; -} - -static bool improper_category(const std::string& category, const int extruders_cnt, const bool is_object_settings = true) -{ - return category.empty() || - (extruders_cnt == 1 && (category == "Extruders" || category == "Wipe options" )) || - (!is_object_settings && category == "Support material"); -} - -static bool is_object_item(ItemType item_type) -{ - return item_type & itObject || item_type & itInstance || - // multi-selection in ObjectList, but full_object in Selection - (item_type == itUndef && scene_selection().is_single_full_object()); -} - -void ObjectList::get_options_menu(settings_menu_hierarchy& settings_menu, const bool is_part) -{ - auto options = get_options(is_part); - - const int extruders_cnt = extruders_count(); - - DynamicPrintConfig config; - for (auto& option : options) - { - auto const opt = config.def()->get(option); - auto category = opt->category; - if (improper_category(category, extruders_cnt, !is_part)) - continue; - - const std::string& label = !opt->full_label.empty() ? opt->full_label : opt->label; - std::pair option_label(option, label); - std::vector< std::pair > new_category; - auto& cat_opt_label = settings_menu.find(category) == settings_menu.end() ? new_category : settings_menu.at(category); - cat_opt_label.push_back(option_label); - if (cat_opt_label.size() == 1) - settings_menu[category] = cat_opt_label; - } -} - -void ObjectList::get_settings_choice(const wxString& category_name) -{ - wxArrayString names; - wxArrayInt selections; - - /* If we try to add settings for object/part from 3Dscene, - * for the second try there is selected ItemSettings in ObjectList. - * So, check if selected item isn't SettingsItem. And get a SettingsItem's parent item, if yes - */ - const wxDataViewItem selected_item = GetSelection(); - wxDataViewItem item = m_objects_model->GetItemType(selected_item) & itSettings ? m_objects_model->GetParent(selected_item) : selected_item; + if (category_options.empty()) + return; const ItemType item_type = m_objects_model->GetItemType(item); - settings_menu_hierarchy settings_menu; - const bool is_part = item_type & (itVolume | itLayer); - get_options_menu(settings_menu, is_part); - std::vector< std::pair > *settings_list = nullptr; - if (!m_config) m_config = &get_item_config(item); assert(m_config); auto opt_keys = m_config->keys(); - for (auto& cat : settings_menu) - { - if (_(cat.first) == category_name) { - int sel = 0; - for (auto& pair : cat.second) { - names.Add(_(pair.second)); - if (find(opt_keys.begin(), opt_keys.end(), pair.first) != opt_keys.end()) - selections.Add(sel); - sel++; - } - settings_list = &cat.second; - break; - } - } - - if (!settings_list) - return; - - if (wxGetSelectedChoices(selections, _(L("Select showing settings")), category_name, names) == -1) - return; - - const int selection_cnt = selections.size(); -#if 0 - if (selection_cnt > 0) - { - // Add selected items to the "Quick menu" - SettingsBundle& freq_settings = printer_technology() == ptSLA ? - m_freq_settings_sla : m_freq_settings_fff; - bool changed_existing = false; - - std::vector tmp_freq_cat = {}; - - for (auto& cat : freq_settings) - { - if (_(cat.first) == category_name) - { - std::vector& freq_settings_category = cat.second; - freq_settings_category.clear(); - freq_settings_category.reserve(selection_cnt); - for (auto sel : selections) - freq_settings_category.push_back((*settings_list)[sel].first); - - changed_existing = true; - break; - } - } - - if (!changed_existing) - { - // Create new "Quick menu" item - for (auto& cat : settings_menu) - { - if (_(cat.first) == category_name) - { - freq_settings[cat.first] = std::vector {}; - - std::vector& freq_settings_category = freq_settings.find(cat.first)->second; - freq_settings_category.reserve(selection_cnt); - for (auto sel : selections) - freq_settings_category.push_back((*settings_list)[sel].first); - break; - } - } - } - } -#endif - - const wxString snapshot_text = item_type & itLayer ? _(L("Add Settings for Layers")) : - item_type & itVolume ? _(L("Add Settings for Sub-object")) : - _(L("Add Settings for Object")); + const wxString snapshot_text = item_type & itLayer ? _L("Add Settings for Layers") : + item_type & itVolume ? _L("Add Settings for Sub-object") : + _L("Add Settings for Object"); take_snapshot(snapshot_text); - std::vector selected_options; - selected_options.reserve(selection_cnt); - for (auto sel : selections) - selected_options.push_back((*settings_list)[sel].first); - const DynamicPrintConfig& from_config = printer_technology() == ptFFF ? wxGetApp().preset_bundle->prints.get_edited_preset().config : wxGetApp().preset_bundle->sla_prints.get_edited_preset().config; - for (auto& setting : (*settings_list)) - { - auto& opt_key = setting.first; - if (find(opt_keys.begin(), opt_keys.end(), opt_key) != opt_keys.end() && - find(selected_options.begin(), selected_options.end(), opt_key) == selected_options.end()) + for (auto& opt : category_options) { + auto& opt_key = opt.first; + if (find(opt_keys.begin(), opt_keys.end(), opt_key) != opt_keys.end() && !opt.second) m_config->erase(opt_key); - if (find(opt_keys.begin(), opt_keys.end(), opt_key) == opt_keys.end() && - find(selected_options.begin(), selected_options.end(), opt_key) != selected_options.end()) { + if (find(opt_keys.begin(), opt_keys.end(), opt_key) == opt_keys.end() && opt.second) { const ConfigOption* option = from_config.option(opt_key); if (!option) { // if current option doesn't exist in prints.get_edited_preset(), @@ -1531,48 +1210,25 @@ void ObjectList::get_settings_choice(const wxString& category_name) } } - // Add settings item for object/sub-object and show them if (!(item_type & (itObject | itVolume | itLayer))) item = m_objects_model->GetTopParent(item); show_settings(add_settings_item(item, &m_config->get())); } -void ObjectList::get_freq_settings_choice(const wxString& bundle_name) +void ObjectList::add_category_to_settings_from_frequent(const std::vector& options, wxDataViewItem item) { - std::vector options = get_options_for_bundle(bundle_name); - const Selection& selection = scene_selection(); - const wxDataViewItem sel_item = // when all instances in object are selected - GetSelectedItemsCount() > 1 && selection.is_single_full_object() ? - m_objects_model->GetItemById(selection.get_object_idx()) : - GetSelection(); - - /* If we try to add settings for object/part from 3Dscene, - * for the second try there is selected ItemSettings in ObjectList. - * So, check if selected item isn't SettingsItem. And get a SettingsItem's parent item, if yes - */ - wxDataViewItem item = m_objects_model->GetItemType(sel_item) & itSettings ? m_objects_model->GetParent(sel_item) : sel_item; const ItemType item_type = m_objects_model->GetItemType(item); - /* Because of we couldn't edited layer_height for ItVolume from settings list, - * correct options according to the selected item type : - * remove "layer_height" option - */ - if ((item_type & itVolume) && bundle_name == _("Layers and Perimeters")) { - const auto layer_height_it = std::find(options.begin(), options.end(), "layer_height"); - if (layer_height_it != options.end()) - options.erase(layer_height_it); - } - if (!m_config) m_config = &get_item_config(item); assert(m_config); auto opt_keys = m_config->keys(); - const wxString snapshot_text = item_type & itLayer ? _(L("Add Settings Bundle for Height range")) : - item_type & itVolume ? _(L("Add Settings Bundle for Sub-object")) : - _(L("Add Settings Bundle for Object")); + const wxString snapshot_text = item_type & itLayer ? _L("Add Settings Bundle for Height range") : + item_type & itVolume ? _L("Add Settings Bundle for Sub-object") : + _L("Add Settings Bundle for Object"); take_snapshot(snapshot_text); const DynamicPrintConfig& from_config = wxGetApp().preset_bundle->prints.get_edited_preset().config; @@ -1607,520 +1263,12 @@ void ObjectList::show_settings(const wxDataViewItem settings_item) update_selections_on_canvas(); } -wxMenu* ObjectList::append_submenu_add_generic(wxMenu* menu, const ModelVolumeType type) { - auto sub_menu = new wxMenu; - - if (wxGetApp().get_mode() == comExpert && type != ModelVolumeType::INVALID) { - append_menu_item(sub_menu, wxID_ANY, _(L("Load")) + " " + dots, "", - [this, type](wxCommandEvent&) { load_subobject(type); }, "", menu); - sub_menu->AppendSeparator(); - } - - for (auto& item : { L("Box"), L("Cylinder"), L("Sphere"), L("Slab") }) - { - if (type == ModelVolumeType::INVALID && strncmp(item, "Slab", 4) == 0) - continue; - append_menu_item(sub_menu, wxID_ANY, _(item), "", - [this, type, item](wxCommandEvent&) { load_generic_subobject(item, type); }, "", menu); - } - - return sub_menu; -} - -void ObjectList::append_menu_items_add_volume(wxMenu* menu) -{ - // Update "add" items(delete old & create new) settings popupmenu - for (auto& item : ADD_VOLUME_MENU_ITEMS){ - const auto settings_id = menu->FindItem(_(item.first)); - if (settings_id != wxNOT_FOUND) - menu->Destroy(settings_id); - } - - const ConfigOptionMode mode = wxGetApp().get_mode(); - - wxWindow* parent = wxGetApp().plater(); - - if (mode == comAdvanced) { - append_menu_item(menu, wxID_ANY, _(ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::MODEL_PART)].first), "", - [this](wxCommandEvent&) { load_subobject(ModelVolumeType::MODEL_PART); }, - ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::MODEL_PART)].second, nullptr, - [this]() { return is_instance_or_object_selected(); }, parent); - } - if (mode == comSimple) { - append_menu_item(menu, wxID_ANY, _(ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::SUPPORT_ENFORCER)].first), "", - [this](wxCommandEvent&) { load_generic_subobject(L("Box"), ModelVolumeType::SUPPORT_ENFORCER); }, - ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::SUPPORT_ENFORCER)].second, nullptr, - [this]() { return is_instance_or_object_selected(); }, parent); - append_menu_item(menu, wxID_ANY, _(ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::SUPPORT_BLOCKER)].first), "", - [this](wxCommandEvent&) { load_generic_subobject(L("Box"), ModelVolumeType::SUPPORT_BLOCKER); }, - ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::SUPPORT_BLOCKER)].second, nullptr, - [this]() { return is_instance_or_object_selected(); }, parent); - - return; - } - - for (size_t type = (mode == comExpert ? 0 : 1) ; type < ADD_VOLUME_MENU_ITEMS.size(); type++) - { - auto& item = ADD_VOLUME_MENU_ITEMS[type]; - - wxMenu* sub_menu = append_submenu_add_generic(menu, ModelVolumeType(type)); - append_submenu(menu, sub_menu, wxID_ANY, _(item.first), "", item.second, - [this]() { return is_instance_or_object_selected(); }, parent); - } -} - -wxMenuItem* ObjectList::append_menu_item_split(wxMenu* menu) -{ - return append_menu_item(menu, wxID_ANY, _(L("Split to parts")), "", - [this](wxCommandEvent&) { split(); }, "split_parts_SMALL", menu, - [this]() { return is_splittable(); }, wxGetApp().plater()); -} - bool ObjectList::is_instance_or_object_selected() { const Selection& selection = scene_selection(); return selection.is_single_full_instance() || selection.is_single_full_object(); } -wxMenuItem* ObjectList::append_menu_item_layers_editing(wxMenu* menu, wxWindow* parent) -{ - return append_menu_item(menu, wxID_ANY, _(L("Height range Modifier")), "", - [this](wxCommandEvent&) { layers_editing(); }, "edit_layers_all", menu, - [this]() { return is_instance_or_object_selected(); }, parent); -} - -wxMenuItem* ObjectList::append_menu_item_settings(wxMenu* menu_) -{ - MenuWithSeparators* menu = dynamic_cast(menu_); - - const wxString menu_name = _(L("Add settings")); - // Delete old items from settings popupmenu - auto settings_id = menu->FindItem(menu_name); - if (settings_id != wxNOT_FOUND) - menu->Destroy(settings_id); - - for (auto& it : FREQ_SETTINGS_BUNDLE_FFF) - { - settings_id = menu->FindItem(_(it.first)); - if (settings_id != wxNOT_FOUND) - menu->Destroy(settings_id); - } - for (auto& it : FREQ_SETTINGS_BUNDLE_SLA) - { - settings_id = menu->FindItem(_(it.first)); - if (settings_id != wxNOT_FOUND) - menu->Destroy(settings_id); - } -#if 0 - for (auto& it : m_freq_settings_fff) - { - settings_id = menu->FindItem(from_u8((boost::format(_utf8(L("Quick Add Settings (%s)"))) % _(it.first)).str())); - if (settings_id != wxNOT_FOUND) - menu->Destroy(settings_id); - } - for (auto& it : m_freq_settings_sla) - { - settings_id = menu->FindItem(from_u8((boost::format(_utf8(L("Quick Add Settings (%s)"))) % _(it.first)).str())); - if (settings_id != wxNOT_FOUND) - menu->Destroy(settings_id); - } -#endif - menu->DestroySeparators(); // delete old separators - - // If there are selected more then one instance but not all of them - // don't add settings menu items - const Selection& selection = scene_selection(); - if ((selection.is_multiple_full_instance() && !selection.is_single_full_object()) || - selection.is_multiple_volume() || selection.is_mixed() ) // more than one volume(part) is selected on the scene - return nullptr; - - const auto sel_vol = get_selected_model_volume(); - if (sel_vol && sel_vol->type() >= ModelVolumeType::SUPPORT_ENFORCER) - return nullptr; - - const ConfigOptionMode mode = wxGetApp().get_mode(); - if (mode == comSimple) - return nullptr; - - // Create new items for settings popupmenu - - if (printer_technology() == ptFFF || - (menu->GetMenuItems().size() > 0 && !menu->GetMenuItems().back()->IsSeparator())) - menu->SetFirstSeparator(); - - // Add frequently settings - const ItemType item_type = m_objects_model->GetItemType(GetSelection()); - if (item_type == itUndef && !selection.is_single_full_object()) - return nullptr; - const bool is_object_settings = is_object_item(item_type); - create_freq_settings_popupmenu(menu, is_object_settings); - - if (mode == comAdvanced) - return nullptr; - - menu->SetSecondSeparator(); - - // Add full settings list - auto menu_item = new wxMenuItem(menu, wxID_ANY, menu_name); - menu_item->SetBitmap(m_bmp_cog.bmp()); - - menu_item->SetSubMenu(create_settings_popupmenu(menu)); - - return menu->Append(menu_item); -} - -wxMenuItem* ObjectList::append_menu_item_change_type(wxMenu* menu, wxWindow* parent/* = nullptr*/) -{ - return append_menu_item(menu, wxID_ANY, _(L("Change type")), "", - [this](wxCommandEvent&) { change_part_type(); }, "", menu, - [this]() { - wxDataViewItem item = GetSelection(); - return item.IsOk() || m_objects_model->GetItemType(item) == itVolume; - }, parent); -} - -wxMenuItem* ObjectList::append_menu_item_instance_to_object(wxMenu* menu, wxWindow* parent) -{ - wxMenuItem* menu_item = append_menu_item(menu, wxID_ANY, _(L("Set as a Separated Object")), "", - [this](wxCommandEvent&) { split_instances(); }, "", menu); - - /* New behavior logic: - * 1. Split Object to several separated object, if ALL instances are selected - * 2. Separate selected instances from the initial object to the separated object, - * if some (not all) instances are selected - */ - parent->Bind(wxEVT_UPDATE_UI, [](wxUpdateUIEvent& evt) - { - const Selection& selection = wxGetApp().plater()->canvas3D()->get_selection(); - evt.SetText(selection.is_single_full_object() ? - _(L("Set as a Separated Objects")) : _(L("Set as a Separated Object"))); - - evt.Enable(wxGetApp().plater()->can_set_instance_to_object()); - }, menu_item->GetId()); - - return menu_item; -} - -wxMenuItem* ObjectList::append_menu_item_printable(wxMenu* menu, wxWindow* /*parent*/) -{ - return append_menu_check_item(menu, wxID_ANY, _(L("Printable")), "", [this](wxCommandEvent&) { - const Selection& selection = wxGetApp().plater()->canvas3D()->get_selection(); - wxDataViewItem item; - if (GetSelectedItemsCount() > 1 && selection.is_single_full_object()) - item = m_objects_model->GetItemById(selection.get_object_idx()); - else - item = GetSelection(); - - if (item) - toggle_printable_state(item); - }, menu); -} - -void ObjectList::append_menu_items_osx(wxMenu* menu) -{ - append_menu_item(menu, wxID_ANY, _(L("Rename")), "", - [this](wxCommandEvent&) { rename_item(); }, "", menu); - - menu->AppendSeparator(); -} - -wxMenuItem* ObjectList::append_menu_item_fix_through_netfabb(wxMenu* menu) -{ - if (!is_windows10()) - return nullptr; - Plater* plater = wxGetApp().plater(); - wxMenuItem* menu_item = append_menu_item(menu, wxID_ANY, _(L("Fix through the Netfabb")), "", - [this](wxCommandEvent&) { fix_through_netfabb(); }, "", menu, - [plater]() {return plater->can_fix_through_netfabb(); }, plater); - menu->AppendSeparator(); - - return menu_item; -} - -void ObjectList::append_menu_item_export_stl(wxMenu* menu) const -{ - append_menu_item(menu, wxID_ANY, _(L("Export as STL")) + dots, "", - [](wxCommandEvent&) { wxGetApp().plater()->export_stl(false, true); }, "", menu); - menu->AppendSeparator(); -} - -void ObjectList::append_menu_item_reload_from_disk(wxMenu* menu) const -{ - append_menu_item(menu, wxID_ANY, _(L("Reload from disk")), _(L("Reload the selected volumes from disk")), - [](wxCommandEvent&) { wxGetApp().plater()->reload_from_disk(); }, "", menu, - []() { return wxGetApp().plater()->can_reload_from_disk(); }, wxGetApp().plater()); -} - -void ObjectList::append_menu_item_change_extruder(wxMenu* menu) -{ - const std::vector names = {_(L("Change extruder")), _(L("Set extruder for selected items")) }; - // Delete old menu item - for (const wxString& name : names) { - const int item_id = menu->FindItem(name); - if (item_id != wxNOT_FOUND) - menu->Destroy(item_id); - } - - const int extruders_cnt = extruders_count(); - if (extruders_cnt <= 1) - return; - - wxDataViewItemArray sels; - GetSelections(sels); - if (sels.IsEmpty()) - return; - - std::vector icons = get_extruder_color_icons(true); - wxMenu* extruder_selection_menu = new wxMenu(); - const wxString& name = sels.Count()==1 ? names[0] : names[1]; - - int initial_extruder = -1; // negative value for multiple object/part selection - if (sels.Count()==1) { - const ModelConfig &config = get_item_config(sels[0]); - initial_extruder = config.has("extruder") ? config.extruder() : 0; - } - - for (int i = 0; i <= extruders_cnt; i++) - { - bool is_active_extruder = i == initial_extruder; - int icon_idx = i == 0 ? 0 : i - 1; - - const wxString& item_name = (i == 0 ? _(L("Default")) : wxString::Format(_(L("Extruder %d")), i)) + - (is_active_extruder ? " (" + _(L("active")) + ")" : ""); - - append_menu_item(extruder_selection_menu, wxID_ANY, item_name, "", - [this, i](wxCommandEvent&) { set_extruder_for_selected_items(i); }, *icons[icon_idx], menu, - [is_active_extruder]() { return !is_active_extruder; }, GUI::wxGetApp().plater()); - - } - - menu->AppendSubMenu(extruder_selection_menu, name); -} - -void ObjectList::append_menu_item_delete(wxMenu* menu) -{ - append_menu_item(menu, wxID_ANY, _(L("Delete")), "", - [this](wxCommandEvent&) { remove(); }, "", menu); -} - -void ObjectList::append_menu_item_scale_selection_to_fit_print_volume(wxMenu* menu) -{ - append_menu_item(menu, wxID_ANY, _(L("Scale to print volume")), _(L("Scale the selected object to fit the print volume")), - [](wxCommandEvent&) { wxGetApp().plater()->scale_selection_to_fit_print_volume(); }, "", menu); -} - -void ObjectList::append_menu_items_convert_unit(wxMenu* menu, int insert_pos/* = 1*/) -{ - std::vector obj_idxs, vol_idxs; - get_selection_indexes(obj_idxs, vol_idxs); - if (obj_idxs.empty() && vol_idxs.empty()) - return; - - auto volume_respects_conversion = [](ModelVolume* volume, ConversionType conver_type) - { - return (conver_type == ConversionType::CONV_FROM_INCH && volume->source.is_converted_from_inches) || - (conver_type == ConversionType::CONV_TO_INCH && !volume->source.is_converted_from_inches) || - (conver_type == ConversionType::CONV_FROM_METER && volume->source.is_converted_from_meters) || - (conver_type == ConversionType::CONV_TO_METER && !volume->source.is_converted_from_meters); - }; - - auto can_append = [this, obj_idxs, vol_idxs, volume_respects_conversion](ConversionType conver_type) - { - ModelObjectPtrs objects; - for (int obj_idx : obj_idxs) { - ModelObject* object = (*m_objects)[obj_idx]; - if (vol_idxs.empty()) { - for (ModelVolume* volume : object->volumes) - if (volume_respects_conversion(volume, conver_type)) - return false; - } - else { - for (int vol_idx : vol_idxs) - if (volume_respects_conversion(object->volumes[vol_idx], conver_type)) - return false; - } - } - return true; - }; - - std::vector> items = { - {ConversionType::CONV_FROM_INCH , _L("Convert from imperial units") }, - {ConversionType::CONV_TO_INCH , _L("Revert conversion from imperial units") }, - {ConversionType::CONV_FROM_METER, _L("Convert from meters") }, - {ConversionType::CONV_TO_METER , _L("Revert conversion from meters") } }; - - for (auto item : items) { - int menu_id = menu->FindItem(item.second); - if (can_append(item.first)) { - // Add menu item if it doesn't exist - if (menu_id == wxNOT_FOUND) - append_menu_item(menu, wxID_ANY, item.second, item.second, - [item](wxCommandEvent&) { wxGetApp().plater()->convert_unit(item.first); }, "", menu, - []() {return true; }, nullptr, insert_pos); - } - else if (menu_id != wxNOT_FOUND) { - // Delete menu item - menu->Destroy(menu_id); - } - } -} - -void ObjectList::append_menu_item_merge_to_multipart_object(wxMenu* menu) -{ - menu->AppendSeparator(); - append_menu_item(menu, wxID_ANY, _L("Merge"), _L("Merge objects to the one multipart object"), - [this](wxCommandEvent&) { merge(true); }, "", menu, - [this]() { return this->can_merge_to_multipart_object(); }, wxGetApp().plater()); -} - -void ObjectList::append_menu_item_merge_to_single_object(wxMenu* menu) -{ - menu->AppendSeparator(); - append_menu_item(menu, wxID_ANY, _L("Merge"), _L("Merge objects to the one single object"), - [this](wxCommandEvent&) { merge(false); }, "", menu, - [this]() { return this->can_merge_to_single_object(); }, wxGetApp().plater()); -} - -void ObjectList::create_object_popupmenu(wxMenu *menu) -{ -#ifdef __WXOSX__ - append_menu_items_osx(menu); -#endif // __WXOSX__ - - append_menu_item_reload_from_disk(menu); - append_menu_item_export_stl(menu); - append_menu_item_fix_through_netfabb(menu); - append_menu_item_scale_selection_to_fit_print_volume(menu); - - // Split object to parts - append_menu_item_split(menu); -// menu->AppendSeparator(); - - // Merge multipart object to the single object -// append_menu_item_merge_to_single_object(menu); - menu->AppendSeparator(); - - // Layers Editing for object - append_menu_item_layers_editing(menu, wxGetApp().plater()); - menu->AppendSeparator(); - - // rest of a object_menu will be added later in: - // - append_menu_items_add_volume() -> for "Add (volumes)" - // - append_menu_item_settings() -> for "Add (settings)" -} - -void ObjectList::create_sla_object_popupmenu(wxMenu *menu) -{ -#ifdef __WXOSX__ - append_menu_items_osx(menu); -#endif // __WXOSX__ - - append_menu_item_reload_from_disk(menu); - append_menu_item_export_stl(menu); - append_menu_item_fix_through_netfabb(menu); - // rest of a object_sla_menu will be added later in: - // - append_menu_item_settings() -> for "Add (settings)" -} - -void ObjectList::create_part_popupmenu(wxMenu *menu) -{ -#ifdef __WXOSX__ - append_menu_items_osx(menu); -#endif // __WXOSX__ - - append_menu_item_reload_from_disk(menu); - append_menu_item_export_stl(menu); - append_menu_item_fix_through_netfabb(menu); - - append_menu_item_split(menu); - - // Append change part type - menu->AppendSeparator(); - append_menu_item_change_type(menu); - - // rest of a object_sla_menu will be added later in: - // - append_menu_item_settings() -> for "Add (settings)" -} - -void ObjectList::create_instance_popupmenu(wxMenu*menu) -{ - m_menu_item_split_instances = append_menu_item_instance_to_object(menu, wxGetApp().plater()); -} - -void ObjectList::create_default_popupmenu(wxMenu*menu) -{ - wxMenu* sub_menu = append_submenu_add_generic(menu, ModelVolumeType::INVALID); - append_submenu(menu, sub_menu, wxID_ANY, _(L("Add Shape")), "", "add_part", - [](){return true;}, this); -} - -wxMenu* ObjectList::create_settings_popupmenu(wxMenu *parent_menu) -{ - wxMenu *menu = new wxMenu; - - settings_menu_hierarchy settings_menu; - - /* If we try to add settings for object/part from 3Dscene, - * for the second try there is selected ItemSettings in ObjectList. - * So, check if selected item isn't SettingsItem. And get a SettingsItem's parent item, if yes - */ - const wxDataViewItem selected_item = GetSelection(); - wxDataViewItem item = m_objects_model->GetItemType(selected_item) & itSettings ? m_objects_model->GetParent(selected_item) : selected_item; - - get_options_menu(settings_menu, !is_object_item(m_objects_model->GetItemType(item))); - - for (auto cat : settings_menu) { - append_menu_item(menu, wxID_ANY, _(cat.first), "", - [this, menu](wxCommandEvent& event) { get_settings_choice(menu->GetLabel(event.GetId())); }, - CATEGORY_ICON.find(cat.first) == CATEGORY_ICON.end() ? wxNullBitmap : CATEGORY_ICON.at(cat.first), parent_menu, - []() { return true; }, wxGetApp().plater()); - } - - return menu; -} - -void ObjectList::create_freq_settings_popupmenu(wxMenu *menu, const bool is_object_settings/* = true*/) -{ - // Add default settings bundles - const SettingsBundle& bundle = printer_technology() == ptFFF ? - FREQ_SETTINGS_BUNDLE_FFF : FREQ_SETTINGS_BUNDLE_SLA; - - const int extruders_cnt = extruders_count(); - - for (auto& it : bundle) { - if (improper_category(it.first, extruders_cnt, is_object_settings)) - continue; - - append_menu_item(menu, wxID_ANY, _(it.first), "", - [this, menu](wxCommandEvent& event) { get_freq_settings_choice(menu->GetLabel(event.GetId())); }, - CATEGORY_ICON.find(it.first) == CATEGORY_ICON.end() ? wxNullBitmap : CATEGORY_ICON.at(it.first), menu, - []() { return true; }, wxGetApp().plater()); - } -#if 0 - // Add "Quick" settings bundles - const SettingsBundle& bundle_quick = printer_technology() == ptFFF ? - m_freq_settings_fff : m_freq_settings_sla; - - for (auto& it : bundle_quick) { - if (improper_category(it.first, extruders_cnt)) - continue; - - append_menu_item(menu, wxID_ANY, from_u8((boost::format(_utf8(L("Quick Add Settings (%s)"))) % _(it.first)).str()), "", - [menu, this](wxCommandEvent& event) { get_freq_settings_choice(menu->GetLabel(event.GetId())); }, - CATEGORY_ICON.find(it.first) == CATEGORY_ICON.end() ? wxNullBitmap : CATEGORY_ICON.at(it.first), menu, - [this]() { return true; }, wxGetApp().plater()); - } -#endif -} - -void ObjectList::update_opt_keys(t_config_option_keys& opt_keys, const bool is_object) -{ - auto full_current_opts = get_options(!is_object); - for (int i = opt_keys.size()-1; i >= 0; --i) - if (find(full_current_opts.begin(), full_current_opts.end(), opt_keys[i]) == full_current_opts.end()) - opt_keys.erase(opt_keys.begin() + i); -} - void ObjectList::load_subobject(ModelVolumeType type) { wxDataViewItem item = GetSelection(); @@ -2152,9 +1300,7 @@ void ObjectList::load_subobject(ModelVolumeType type) if (sel_item) select_item(sel_item); -//#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME selection_changed(); -//#endif //no __WXOSX__ //__WXMSW__ } void ObjectList::load_part( ModelObject* model_object, @@ -2297,9 +1443,7 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const Mode const auto object_item = m_objects_model->GetTopParent(GetSelection()); select_item([this, object_item, name, type, new_volume]() { return m_objects_model->AddVolumeChild(object_item, name, type, new_volume->get_mesh_errors_count() > 0); }); -//#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME selection_changed(); -//#endif //no __WXOSX__ //__WXMSW__ } void ObjectList::load_shape_object(const std::string& type_name) @@ -2503,6 +1647,8 @@ bool ObjectList::del_subobject_from_object(const int obj_idx, const int idx, con wxString extruder = object->config.has("extruder") ? wxString::Format("%d", object->config.extruder()) : _L("default"); m_objects_model->SetExtruder(extruder, obj_item); } + // add settings to the object, if it has them + add_settings_item(obj_item, &object->config.get()); } } } @@ -2863,11 +2009,27 @@ bool ObjectList::get_volume_by_item(const wxDataViewItem& item, ModelVolume*& vo return true; } -bool ObjectList::is_splittable() +bool ObjectList::is_splittable(bool to_objects) { const wxDataViewItem item = GetSelection(); if (!item) return false; + if (to_objects) + { + ItemType type = m_objects_model->GetItemType(item); + if (type == itVolume) + return false; + if (type == itObject || m_objects_model->GetItemType(m_objects_model->GetParent(item)) == itObject) { + auto obj_idx = get_selected_obj_idx(); + if (obj_idx < 0) + return false; + if ((*m_objects)[obj_idx]->volumes.size() > 1) + return true; + return (*m_objects)[obj_idx]->volumes[0]->is_splittable(); + } + return false; + } + ModelVolume* volume; if (!get_volume_by_item(item, volume) || !volume) return false; @@ -3044,37 +2206,6 @@ void ObjectList::part_selection_changed() panel.Thaw(); } -SettingsBundle ObjectList::get_item_settings_bundle(const DynamicPrintConfig* config, const bool is_object_settings) -{ - auto opt_keys = config->keys(); - if (opt_keys.empty()) - return SettingsBundle(); - - update_opt_keys(opt_keys, is_object_settings); // update options list according to print technology - - if (opt_keys.empty()) - return SettingsBundle(); - - const int extruders_cnt = wxGetApp().extruders_edited_cnt(); - - SettingsBundle bundle; - for (auto& opt_key : opt_keys) - { - auto category = config->def()->get(opt_key)->category; - if (improper_category(category, extruders_cnt, is_object_settings)) - continue; - - std::vector< std::string > new_category; - - auto& cat_opt = bundle.find(category) == bundle.end() ? new_category : bundle.at(category); - cat_opt.push_back(opt_key); - if (cat_opt.size() == 1) - bundle[category] = cat_opt; - } - - return bundle; -} - // Add new SettingsItem for parent_item if it doesn't exist, or just update a digest according to new config wxDataViewItem ObjectList::add_settings_item(wxDataViewItem parent_item, const DynamicPrintConfig* config) { @@ -3084,7 +2215,13 @@ wxDataViewItem ObjectList::add_settings_item(wxDataViewItem parent_item, const D return ret; const bool is_object_settings = m_objects_model->GetItemType(parent_item) == itObject; - SettingsBundle cat_options = get_item_settings_bundle(config, is_object_settings); + if (!is_object_settings) { + ModelVolumeType volume_type = m_objects_model->GetVolumeType(parent_item); + if (volume_type == ModelVolumeType::SUPPORT_BLOCKER || volume_type == ModelVolumeType::SUPPORT_ENFORCER) + return ret; + } + + SettingsFactory::Bundle cat_options = SettingsFactory::get_bundle(config, is_object_settings); if (cat_options.empty()) return ret; @@ -3687,13 +2824,12 @@ void ObjectList::update_selections() if ( ( m_selection_mode & (smSettings|smLayer|smLayerRoot) ) == 0) m_selection_mode = smInstance; - // We doesn't update selection if SettingsItem for the current object/part is selected -// if (GetSelectedItemsCount() == 1 && m_objects_model->GetItemType(GetSelection()) == itSettings ) + // We doesn't update selection if itSettings | itLayerRoot | itLayer Item for the current object/part is selected if (GetSelectedItemsCount() == 1 && m_objects_model->GetItemType(GetSelection()) & (itSettings | itLayerRoot | itLayer)) { const auto item = GetSelection(); if (selection.is_single_full_object()) { - if (m_objects_model->GetObjectIdByItem(item) == selection.get_object_idx()) + if (m_objects_model->GetItemType(m_objects_model->GetParent(item)) & itObject) return; sels.Add(m_objects_model->GetItemById(selection.get_object_idx())); } @@ -4161,9 +3297,15 @@ void ObjectList::fix_multiselection_conflicts() ModelVolume* ObjectList::get_selected_model_volume() { - auto item = GetSelection(); - if (!item || m_objects_model->GetItemType(item) != itVolume) + wxDataViewItem item = GetSelection(); + if (!item) return nullptr; + if (m_objects_model->GetItemType(item) != itVolume) { + if (m_objects_model->GetItemType(m_objects_model->GetParent(item)) == itVolume) + item = m_objects_model->GetParent(item); + else + return nullptr; + } const auto vol_idx = m_objects_model->GetVolumeIdByItem(item); const auto obj_idx = get_selected_obj_idx(); @@ -4206,8 +3348,10 @@ void ObjectList::change_part_type() take_snapshot(_(L("Change Part Type"))); - const auto item = GetSelection(); volume->set_type(new_type); + wxDataViewItem item = GetSelection(); + if (m_objects_model->GetItemType(item) != itVolume && m_objects_model->GetItemType(m_objects_model->GetParent(item)) == itVolume) + item = m_objects_model->GetParent(item); m_objects_model->SetVolumeType(item, new_type); changed_object(get_selected_obj_idx()); @@ -4351,11 +3495,6 @@ void ObjectList::update_object_list_by_printer_technology() m_prevent_canvas_selection_update = false; } -void ObjectList::update_object_menu() -{ - append_menu_items_add_volume(&m_menu_object); -} - void ObjectList::instances_to_separated_object(const int obj_idx, const std::set& inst_idxs) { if ((*m_objects)[obj_idx]->instances.size() == inst_idxs.size()) @@ -4528,41 +3667,17 @@ void ObjectList::msw_rescale() GetColumn(colExtruder)->SetWidth( 8 * em); GetColumn(colEditing )->SetWidth( 3 * em); - // rescale all icons, used by ObjectList - msw_rescale_icons(); - // rescale/update existing items with bitmaps m_objects_model->Rescale(); - // rescale menus - for (MenuWithSeparators* menu : { &m_menu_object, - &m_menu_part, - &m_menu_sla_object, - &m_menu_instance, - &m_menu_layer, - &m_menu_default}) - msw_rescale_menu(menu); - Layout(); } void ObjectList::sys_color_changed() { - // msw_rescale_icons() updates icons, so use it - msw_rescale_icons(); - // update existing items with bitmaps m_objects_model->Rescale(); - // msw_rescale_menu updates just icons, so use it - for (MenuWithSeparators* menu : { &m_menu_object, - &m_menu_part, - &m_menu_sla_object, - &m_menu_instance, - &m_menu_layer, - &m_menu_default}) - msw_rescale_menu(menu); - Layout(); } @@ -4607,33 +3722,6 @@ void ObjectList::OnEditingDone(wxDataViewEvent &event) plater->set_current_canvas_as_dirty(); } -void ObjectList::show_multi_selection_menu() -{ - wxDataViewItemArray sels; - GetSelections(sels); - - for (const wxDataViewItem& item : sels) - if (!(m_objects_model->GetItemType(item) & (itVolume | itObject | itInstance))) - // show this menu only for Objects(Instances mixed with Objects)/Volumes selection - return; - - wxMenu* menu = new wxMenu(); - - if (extruders_count() > 1) - append_menu_item_change_extruder(menu); - - append_menu_item(menu, wxID_ANY, _(L("Reload from disk")), _(L("Reload the selected volumes from disk")), - [](wxCommandEvent&) { wxGetApp().plater()->reload_from_disk(); }, "", menu, []() { - return wxGetApp().plater()->can_reload_from_disk(); - }, wxGetApp().plater()); - - append_menu_items_convert_unit(menu); - if (can_merge_to_multipart_object()) - append_menu_item_merge_to_multipart_object(menu); - - wxGetApp().plater()->PopupMenu(menu); -} - void ObjectList::extruder_selection() { wxArrayString choices; diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index 0846def53..35a8fdee0 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -31,18 +31,11 @@ class TriangleMesh; enum class ModelVolumeType : int; // FIXME: broken build on mac os because of this is missing: -typedef std::vector t_config_option_keys; - -typedef std::map> SettingsBundle; - -// category -> vector ( option ; label ) -typedef std::map< std::string, std::vector< std::pair > > settings_menu_hierarchy; - -typedef std::vector ModelVolumePtrs; - -typedef double coordf_t; -typedef std::pair t_layer_height_range; -typedef std::map t_layer_config_ranges; +typedef std::vector t_config_option_keys; +typedef std::vector ModelVolumePtrs; +typedef double coordf_t; +typedef std::pair t_layer_height_range; +typedef std::map t_layer_config_ranges; namespace GUI { @@ -106,7 +99,7 @@ public: private: SELECTION_MODE m_selection_mode {smUndef}; - int m_selected_layers_range_idx; + int m_selected_layers_range_idx {-1}; Clipboard m_clipboard; @@ -147,23 +140,6 @@ private: } m_dragged_data; wxBoxSizer *m_sizer {nullptr}; - wxWindow *m_parent {nullptr}; - - ScalableBitmap m_bmp_modifiermesh; - ScalableBitmap m_bmp_solidmesh; - ScalableBitmap m_bmp_support_enforcer; - ScalableBitmap m_bmp_support_blocker; - ScalableBitmap m_bmp_manifold_warning; - ScalableBitmap m_bmp_cog; - - MenuWithSeparators m_menu_object; - MenuWithSeparators m_menu_part; - MenuWithSeparators m_menu_sla_object; - MenuWithSeparators m_menu_instance; - MenuWithSeparators m_menu_layer; - MenuWithSeparators m_menu_default; - wxMenuItem* m_menu_item_settings { nullptr }; - wxMenuItem* m_menu_item_split_instances { nullptr }; ObjectDataViewModel *m_objects_model{ nullptr }; ModelConfig *m_config {nullptr}; @@ -185,7 +161,6 @@ private: // update_settings_items - updating canvas selection is undesirable, // because it would turn off the gizmos (mainly a problem for the SLA gizmo) - int m_selected_row = 0; wxDataViewItem m_last_selected_item {nullptr}; #ifdef __WXMSW__ // Workaround for entering the column editing mode on Windows. Simulate keyboard enter when another column of the active line is selected. @@ -193,8 +168,8 @@ private: #endif /* __MSW__ */ #if 0 - SettingsBundle m_freq_settings_fff; - SettingsBundle m_freq_settings_sla; + SettingsFactory::Bundle m_freq_settings_fff; + SettingsFactory::Bundle m_freq_settings_sla; #endif size_t m_items_count { size_t(-1) }; @@ -212,8 +187,6 @@ public: void set_min_height(); void update_min_height(); - std::map CATEGORY_ICON; - ObjectDataViewModel* GetModel() const { return m_objects_model; } ModelConfig* config() const { return m_config; } std::vector* objects() const { return m_objects; } @@ -221,7 +194,6 @@ public: ModelObject* object(const int obj_idx) const ; void create_objects_ctrl(); - void create_popup_menus(); void update_objects_list_extruder_column(size_t extruders_count); void update_extruder_colors(); // show/hide "Extruder" column for Objects List @@ -232,9 +204,6 @@ public: void update_name_in_model(const wxDataViewItem& item) const; void update_extruder_values_for_items(const size_t max_extruder); - void init_icons(); - void msw_rescale_icons(); - // Get obj_idx and vol_idx values for the selected (by default) or an adjusted item void get_selected_item_indexes(int& obj_idx, int& vol_idx, const wxDataViewItem& item = wxDataViewItem(0)); void get_selection_indexes(std::vector& obj_idxs, std::vector& vol_idxs); @@ -264,39 +233,11 @@ public: void increase_instances(); void decrease_instances(); - void get_settings_choice(const wxString& category_name); - void get_freq_settings_choice(const wxString& bundle_name); + void add_category_to_settings_from_selection(const std::vector< std::pair >& category_options, wxDataViewItem item); + void add_category_to_settings_from_frequent(const std::vector& category_options, wxDataViewItem item); void show_settings(const wxDataViewItem settings_item); bool is_instance_or_object_selected(); - wxMenu* append_submenu_add_generic(wxMenu* menu, const ModelVolumeType type); - void append_menu_items_add_volume(wxMenu* menu); - wxMenuItem* append_menu_item_split(wxMenu* menu); - wxMenuItem* append_menu_item_layers_editing(wxMenu* menu, wxWindow* parent); - wxMenuItem* append_menu_item_settings(wxMenu* menu); - wxMenuItem* append_menu_item_change_type(wxMenu* menu, wxWindow* parent = nullptr); - wxMenuItem* append_menu_item_instance_to_object(wxMenu* menu, wxWindow* parent); - wxMenuItem* append_menu_item_printable(wxMenu* menu, wxWindow* parent); - void append_menu_items_osx(wxMenu* menu); - wxMenuItem* append_menu_item_fix_through_netfabb(wxMenu* menu); - void append_menu_item_export_stl(wxMenu* menu) const; - void append_menu_item_reload_from_disk(wxMenu* menu) const; - void append_menu_item_change_extruder(wxMenu* menu); - void append_menu_item_delete(wxMenu* menu); - void append_menu_item_scale_selection_to_fit_print_volume(wxMenu* menu); - void append_menu_items_convert_unit(wxMenu* menu, int insert_pos = 1); // Add "Conver/Revert..." menu items (from/to inches/meters) after "Reload From Disk" - void append_menu_item_merge_to_multipart_object(wxMenu *menu); - void append_menu_item_merge_to_single_object(wxMenu *menu); - void create_object_popupmenu(wxMenu *menu); - void create_sla_object_popupmenu(wxMenu*menu); - void create_part_popupmenu(wxMenu*menu); - void create_instance_popupmenu(wxMenu*menu); - void create_default_popupmenu(wxMenu *menu); - wxMenu* create_settings_popupmenu(wxMenu *parent_menu); - void create_freq_settings_popupmenu(wxMenu *parent_menu, const bool is_object_settings = true); - - void update_opt_keys(t_config_option_keys& t_optopt_keys, const bool is_object); - void load_subobject(ModelVolumeType type); void load_part(ModelObject* model_object, std::vector> &volumes_info, ModelVolumeType type); void load_generic_subobject(const std::string& type_name, const ModelVolumeType type); @@ -318,7 +259,7 @@ public: DynamicPrintConfig get_default_layer_config(const int obj_idx); bool get_volume_by_item(const wxDataViewItem& item, ModelVolume*& volume); - bool is_splittable(); + bool is_splittable(bool to_objects); bool selected_instances_of_same_object(); bool can_split_instances(); bool can_merge_to_multipart_object() const; @@ -328,7 +269,6 @@ public: wxBoxSizer* get_sizer() {return m_sizer;} int get_selected_obj_idx() const; ModelConfig& get_item_config(const wxDataViewItem& item) const; - SettingsBundle get_item_settings_bundle(const DynamicPrintConfig* config, const bool is_object_settings); void changed_object(const int obj_idx = -1) const; void part_selection_changed(); @@ -404,11 +344,9 @@ public: void change_part_type(); void last_volume_is_deleted(const int obj_idx); - void update_settings_items(); void update_and_show_object_settings_item(); void update_settings_item_and_selection(wxDataViewItem item, wxDataViewItemArray& selections); void update_object_list_by_printer_technology(); - void update_object_menu(); void instances_to_separated_object(const int obj_idx, const std::set& inst_idx); void instances_to_separated_objects(const int obj_idx); @@ -433,7 +371,7 @@ public: void update_printable_state(int obj_idx, int instance_idx); void toggle_printable_state(wxDataViewItem item); - void show_multi_selection_menu(); + void set_extruder_for_selected_items(const int extruder) const ; private: #ifdef __WXOSX__ @@ -454,11 +392,6 @@ private: #endif /* __WXMSW__ */ void OnEditingDone(wxDataViewEvent &event); void extruder_selection(); - void set_extruder_for_selected_items(const int extruder) const ; - - std::vector get_options(const bool is_part); - const std::vector& get_options_for_bundle(const wxString& bundle_name); - void get_options_menu(settings_menu_hierarchy& settings_menu, const bool is_part); }; diff --git a/src/slic3r/GUI/GUI_ObjectSettings.cpp b/src/slic3r/GUI/GUI_ObjectSettings.cpp index 2501ea499..e6689b43c 100644 --- a/src/slic3r/GUI/GUI_ObjectSettings.cpp +++ b/src/slic3r/GUI/GUI_ObjectSettings.cpp @@ -1,5 +1,6 @@ #include "GUI_ObjectSettings.hpp" #include "GUI_ObjectList.hpp" +#include "GUI_Factories.hpp" #include "OptionsGroup.hpp" #include "GUI_App.hpp" @@ -83,7 +84,7 @@ bool ObjectSettings::update_settings_list() return false; const bool is_object_settings = objects_model->GetItemType(objects_model->GetParent(item)) == itObject; - SettingsBundle cat_options = objects_ctrl->get_item_settings_bundle(&config->get(), is_object_settings); + SettingsFactory::Bundle cat_options = SettingsFactory::get_bundle(&config->get(), is_object_settings); if (!cat_options.empty()) { diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index e9d4efcba..f50c7e356 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -562,8 +562,6 @@ void MainFrame::init_tabpanel() wxGetApp().plater_ = m_plater; - wxGetApp().obj_list()->create_popup_menus(); - if (wxGetApp().is_editor()) create_preset_tabs(); diff --git a/src/slic3r/GUI/ObjectDataViewModel.cpp b/src/slic3r/GUI/ObjectDataViewModel.cpp index 7de37fb48..80655352b 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.cpp +++ b/src/slic3r/GUI/ObjectDataViewModel.cpp @@ -2,7 +2,7 @@ #include "wxExtensions.hpp" #include "BitmapCache.hpp" #include "GUI_App.hpp" -#include "GUI_ObjectList.hpp" +#include "GUI_Factories.hpp" #include "I18N.hpp" #include "libslic3r/Model.hpp" @@ -44,8 +44,9 @@ void ObjectDataViewModelNode::init_container() #endif //__WXGTK__ } -#define LAYER_ROOT_ICON "edit_layers_all" -#define LAYER_ICON "edit_layers_some" +static constexpr char LayerRootIcon[] = "edit_layers_all"; +static constexpr char LayerIcon[] = "edit_layers_some"; +static constexpr char WarningIcon[] = "exclamation"; ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent, const ItemType type) : m_parent(parent), @@ -65,7 +66,7 @@ ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent } else if (type == itLayerRoot) { - m_bmp = create_scaled_bitmap(LAYER_ROOT_ICON); // FIXME: pass window ptr + m_bmp = create_scaled_bitmap(LayerRootIcon); // FIXME: pass window ptr m_name = _(L("Layers")); } @@ -94,7 +95,7 @@ ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent } const std::string label_range = (boost::format(" %.2f-%.2f ") % layer_range.first % layer_range.second).str(); m_name = _(L("Range")) + label_range + "(" + _(L("mm")) + ")"; - m_bmp = create_scaled_bitmap(LAYER_ICON); // FIXME: pass window ptr + m_bmp = create_scaled_bitmap(LayerIcon); // FIXME: pass window ptr set_action_and_extruder_icons(); init_container(); @@ -140,17 +141,14 @@ void ObjectDataViewModelNode::update_settings_digest_bitmaps() { m_bmp = m_empty_bmp; - std::map& categories_icon = Slic3r::GUI::wxGetApp().obj_list()->CATEGORY_ICON; - std::string scaled_bitmap_name = m_name.ToUTF8().data(); scaled_bitmap_name += "-em" + std::to_string(wxGetApp().em_unit()) + (wxGetApp().dark_mode() ? "-dm" : ""); wxBitmap *bmp = m_bitmap_cache->find(scaled_bitmap_name); if (bmp == nullptr) { std::vector bmps; - for (auto& cat : m_opt_categories) - bmps.emplace_back( categories_icon.find(cat) == categories_icon.end() ? - wxNullBitmap : categories_icon.at(cat)); + for (auto& category : m_opt_categories) + bmps.emplace_back(SettingsFactory::get_category_bitmap(category)); bmp = m_bitmap_cache->insert(scaled_bitmap_name, bmps); } @@ -249,6 +247,9 @@ static int get_root_idx(ObjectDataViewModelNode *parent_node, const ItemType roo ObjectDataViewModel::ObjectDataViewModel() { m_bitmap_cache = new Slic3r::GUI::BitmapCache; + + m_volume_bmps = MenuFactory::get_volume_bitmaps(); + m_warning_bmp = create_scaled_bitmap(WarningIcon); } ObjectDataViewModel::~ObjectDataViewModel() @@ -267,7 +268,7 @@ wxDataViewItem ObjectDataViewModel::Add(const wxString &name, auto root = new ObjectDataViewModelNode(name, extruder_str); // Add error icon if detected auto-repaire if (has_errors) - root->m_bmp = *m_warning_bmp; + root->m_bmp = m_warning_bmp; m_objects.push_back(root); // notify control @@ -317,7 +318,7 @@ wxDataViewItem ObjectDataViewModel::AddVolumeChild( const wxDataViewItem &parent // if part with errors is added, but object wasn't marked, then mark it if (!obj_errors && has_errors) - root->SetBitmap(*m_warning_bmp); + root->SetBitmap(m_warning_bmp); // notify control const wxDataViewItem child((void*)node); @@ -1434,10 +1435,20 @@ void ObjectDataViewModel::SetVolumeType(const wxDataViewItem &item, const Slic3r return; ObjectDataViewModelNode *node = static_cast(item.GetID()); - node->SetBitmap(*m_volume_bmps[int(type)]); + node->SetVolumeType(type); + node->SetBitmap(m_volume_bmps[int(type)]); ItemChanged(item); } +ModelVolumeType ObjectDataViewModel::GetVolumeType(const wxDataViewItem& item) +{ + if (!item.IsOk() || GetItemType(item) != itVolume) + return ModelVolumeType::INVALID; + + ObjectDataViewModelNode *node = static_cast(item.GetID()); + return node->GetVolumeType(); +} + wxDataViewItem ObjectDataViewModel::SetPrintableState( PrintIndicator printable, int obj_idx, @@ -1480,6 +1491,9 @@ wxDataViewItem ObjectDataViewModel::SetObjectPrintableState( void ObjectDataViewModel::Rescale() { + m_volume_bmps = MenuFactory::get_volume_bitmaps(); + m_warning_bmp = create_scaled_bitmap(WarningIcon); + wxDataViewItemArray all_items; GetAllChildren(wxDataViewItem(0), all_items); @@ -1494,15 +1508,15 @@ void ObjectDataViewModel::Rescale() switch (node->m_type) { case itObject: - if (node->m_bmp.IsOk()) node->m_bmp = *m_warning_bmp; + if (node->m_bmp.IsOk()) node->m_bmp = m_warning_bmp; break; case itVolume: node->m_bmp = GetVolumeIcon(node->m_volume_type, node->m_bmp.GetWidth() != node->m_bmp.GetHeight()); break; case itLayerRoot: - node->m_bmp = create_scaled_bitmap(LAYER_ROOT_ICON); + node->m_bmp = create_scaled_bitmap(LayerRootIcon); case itLayer: - node->m_bmp = create_scaled_bitmap(LAYER_ICON); + node->m_bmp = create_scaled_bitmap(LayerIcon); break; default: break; } @@ -1514,7 +1528,7 @@ void ObjectDataViewModel::Rescale() wxBitmap ObjectDataViewModel::GetVolumeIcon(const Slic3r::ModelVolumeType vol_type, const bool is_marked/* = false*/) { if (!is_marked) - return *m_volume_bmps[static_cast(vol_type)]; + return m_volume_bmps[static_cast(vol_type)]; std::string scaled_bitmap_name = "warning" + std::to_string(static_cast(vol_type)); scaled_bitmap_name += "-em" + std::to_string(Slic3r::GUI::wxGetApp().em_unit()); @@ -1523,8 +1537,8 @@ wxBitmap ObjectDataViewModel::GetVolumeIcon(const Slic3r::ModelVolumeType vol_ty if (bmp == nullptr) { std::vector bmps; - bmps.emplace_back(*m_warning_bmp); - bmps.emplace_back(*m_volume_bmps[static_cast(vol_type)]); + bmps.emplace_back(m_warning_bmp); + bmps.emplace_back(m_volume_bmps[static_cast(vol_type)]); bmp = m_bitmap_cache->insert(scaled_bitmap_name, bmps); } @@ -1543,7 +1557,7 @@ void ObjectDataViewModel::DeleteWarningIcon(const wxDataViewItem& item, const bo return; if (node->GetType() & itVolume) { - node->SetBitmap(*m_volume_bmps[static_cast(node->volume_type())]); + node->SetBitmap(m_volume_bmps[static_cast(node->volume_type())]); return; } diff --git a/src/slic3r/GUI/ObjectDataViewModel.hpp b/src/slic3r/GUI/ObjectDataViewModel.hpp index 17ad2047f..c23ec195b 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.hpp +++ b/src/slic3r/GUI/ObjectDataViewModel.hpp @@ -171,13 +171,14 @@ public: } bool SetValue(const wxVariant &variant, unsigned int col); - + void SetVolumeType(ModelVolumeType type) { m_volume_type = type; } void SetBitmap(const wxBitmap &icon) { m_bmp = icon; } const wxBitmap& GetBitmap() const { return m_bmp; } const wxString& GetName() const { return m_name; } ItemType GetType() const { return m_type; } void SetIdx(const int& idx); int GetIdx() const { return m_idx; } + ModelVolumeType GetVolumeType() { return m_volume_type; } t_layer_height_range GetLayerRange() const { return m_layer_range; } PrintIndicator IsPrintable() const { return m_printable; } @@ -241,8 +242,8 @@ wxDECLARE_EVENT(wxCUSTOMEVT_LAST_VOLUME_IS_DELETED, wxCommandEvent); class ObjectDataViewModel :public wxDataViewModel { std::vector m_objects; - std::vector m_volume_bmps; - wxBitmap* m_warning_bmp { nullptr }; + std::vector m_volume_bmps; + wxBitmap m_warning_bmp; wxDataViewCtrl* m_ctrl { nullptr }; @@ -348,9 +349,8 @@ public: void UpdateObjectPrintable(wxDataViewItem parent_item); void UpdateInstancesPrintable(wxDataViewItem parent_item); - void SetVolumeBitmaps(const std::vector& volume_bmps) { m_volume_bmps = volume_bmps; } - void SetWarningBitmap(wxBitmap* bitmap) { m_warning_bmp = bitmap; } void SetVolumeType(const wxDataViewItem &item, const Slic3r::ModelVolumeType type); + ModelVolumeType GetVolumeType(const wxDataViewItem &item); wxDataViewItem SetPrintableState( PrintIndicator printable, int obj_idx, int subobj_idx = -1, ItemType subobj_type = itInstance); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 493958589..15e89168a 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -50,6 +50,7 @@ #include "GUI_ObjectManipulation.hpp" #include "GUI_ObjectLayers.hpp" #include "GUI_Utils.hpp" +#include "GUI_Factories.hpp" #include "wxExtensions.hpp" #include "MainFrame.hpp" #include "format.hpp" @@ -1323,7 +1324,7 @@ void Sidebar::update_mode() p->object_list->unselect_objects(); p->object_list->update_selections(); - p->object_list->update_object_menu(); +// p->object_list->update_object_menu(); Layout(); } @@ -1405,23 +1406,7 @@ struct Plater::priv Plater *q; MainFrame *main_frame; - // Object popup menu - MenuWithSeparators object_menu; - // Part popup menu - MenuWithSeparators part_menu; - // SLA-Object popup menu - MenuWithSeparators sla_object_menu; - // Default popup menu (when nothing is selected on 3DScene) - MenuWithSeparators default_menu; - - // Removed/Prepended Items according to the view mode - std::vector items_increase; - std::vector items_decrease; - std::vector items_set_number_of_copies; - enum MenuIdentifier { - miObjectFFF=0, - miObjectSLA - }; + MenuFactory menus; // Data Slic3r::DynamicPrintConfig *config; // FIXME: leak? @@ -1670,7 +1655,6 @@ struct Plater::priv void on_update_geometry(Vec3dsEvent<2>&); void on_3dcanvas_mouse_dragging_finished(SimpleEvent&); - void update_object_menu(); void show_action_buttons(const bool is_ready_to_slice) const; // Set the bed shape to a single closed 2D polygon(array of two element arrays), @@ -1691,12 +1675,11 @@ struct Plater::priv bool can_set_instance_to_object() const; bool can_mirror() const; bool can_reload_from_disk() const; + bool can_split(bool to_objects) const; void generate_thumbnail(ThumbnailData& data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool show_bed, bool transparent_background); void generate_thumbnails(ThumbnailsList& thumbnails, const Vec2ds& sizes, bool printable_only, bool parts_only, bool show_bed, bool transparent_background); - void msw_rescale_object_menu(); - void bring_instance_forward() const; // returns the path to project file with the given extension (none if extension == wxEmptyString) @@ -1713,13 +1696,6 @@ struct Plater::priv bool inside_snapshot_capture() { return m_prevent_snapshots != 0; } bool process_completed_with_error { false }; private: - bool init_object_menu(); - bool init_common_menu(wxMenu* menu, const bool is_part = false); - bool complit_init_object_menu(); - bool complit_init_sla_object_menu(); - bool complit_init_part_menu(); - - bool can_split() const; bool layers_height_allowed() const; void update_fff_scene(); @@ -1829,7 +1805,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) hsizer->Add(sidebar, 0, wxEXPAND | wxLEFT | wxRIGHT, 0); q->SetSizer(hsizer); - init_object_menu(); + menus.init(q); // Events: @@ -2725,19 +2701,19 @@ void Plater::priv::split_object() Model new_model = model; ModelObject* current_model_object = new_model.objects[obj_idx]; - if (current_model_object->volumes.size() > 1) - { - Slic3r::GUI::warning_catcher(q, _L("The selected object can't be split because it contains more than one volume/material.")); - return; - } - wxBusyCursor wait; ModelObjectPtrs new_objects; current_model_object->split(&new_objects); if (new_objects.size() == 1) - Slic3r::GUI::warning_catcher(q, _L("The selected object couldn't be split because it contains only one part.")); + // #ysFIXME use notification + Slic3r::GUI::warning_catcher(q, _L("The selected object couldn't be split because it contains only one solid part.")); else { + if (current_model_object->volumes.size() != new_objects.size()) + notification_manager->push_notification(NotificationType::CustomNotification, + NotificationManager::NotificationLevel::RegularNotification, + _u8L("All non-solid parts (modifiers) was deleted")); + Plater::TakeSnapshot snapshot(q, _L("Split to Objects")); remove(obj_idx); @@ -3732,70 +3708,25 @@ void Plater::priv::on_right_click(RBtnEvent& evt) wxMenu* menu = nullptr; - if (obj_idx == -1) // no one or several object are selected - { + if (obj_idx == -1) { // no one or several object are selected if (evt.data.second) // right button was clicked on empty space - menu = &default_menu; + menu = menus.default_menu(); else - { - sidebar->obj_list()->show_multi_selection_menu(); - return; - } + menu = menus.multi_selection_menu(); } - else - { + else { // If in 3DScene is(are) selected volume(s), but right button was clicked on empty space if (evt.data.second) - return; - - int menu_item_convert_unit_position = 11; + return; if (printer_technology == ptSLA) - menu = &sla_object_menu; - else - { + menu = menus.sla_object_menu(); + else { // show "Object menu" for each one or several FullInstance instead of FullObject 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 ? &object_menu : &part_menu; - if (!is_some_full_instances) - menu_item_convert_unit_position = 2; - } - - sidebar->obj_list()->append_menu_items_convert_unit(menu, menu_item_convert_unit_position); - sidebar->obj_list()->append_menu_item_settings(menu); - - if (printer_technology != ptSLA) - sidebar->obj_list()->append_menu_item_change_extruder(menu); - - if (menu != &part_menu) - { - /* Remove/Prepend "increase/decrease instances" menu items according to the view mode. - * Suppress to show those items for a Simple mode - */ - const MenuIdentifier id = printer_technology == ptSLA ? miObjectSLA : miObjectFFF; - if (wxGetApp().get_mode() == comSimple) { - if (menu->FindItem(_L("Add instance")) != wxNOT_FOUND) - { - /* Detach an items from the menu, but don't delete them - * so that they can be added back later - * (after switching to the Advanced/Expert mode) - */ - menu->Remove(items_increase[id]); - menu->Remove(items_decrease[id]); - menu->Remove(items_set_number_of_copies[id]); - } - } - else { - if (menu->FindItem(_L("Add instance")) == wxNOT_FOUND) - { - // Prepend items to the menu, if those aren't not there - menu->Prepend(items_set_number_of_copies[id]); - menu->Prepend(items_decrease[id]); - menu->Prepend(items_increase[id]); - } - } + menu = is_some_full_instances ? menus.object_menu() : menus.part_menu(); } } @@ -3842,26 +3773,6 @@ void Plater::priv::on_3dcanvas_mouse_dragging_finished(SimpleEvent&) } } -bool Plater::priv::init_object_menu() -{ - items_increase.reserve(2); - items_decrease.reserve(2); - items_set_number_of_copies.reserve(2); - - init_common_menu(&object_menu); - complit_init_object_menu(); - - init_common_menu(&sla_object_menu); - complit_init_sla_object_menu(); - - init_common_menu(&part_menu, true); - complit_init_part_menu(); - - sidebar->obj_list()->create_default_popupmenu(&default_menu); - - return true; -} - void Plater::priv::generate_thumbnail(ThumbnailData& data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool show_bed, bool transparent_background) { view3D->get_canvas3d()->render_thumbnail(data, w, h, printable_only, parts_only, show_bed, transparent_background); @@ -3880,12 +3791,6 @@ void Plater::priv::generate_thumbnails(ThumbnailsList& thumbnails, const Vec2ds& } } -void Plater::priv::msw_rescale_object_menu() -{ - for (MenuWithSeparators* menu : { &object_menu, &sla_object_menu, &part_menu, &default_menu }) - msw_rescale_menu(dynamic_cast(menu)); -} - wxString Plater::priv::get_project_filename(const wxString& extension) const { return m_project_filename.empty() ? "" : m_project_filename + extension; @@ -3914,144 +3819,6 @@ void Plater::priv::set_project_filename(const wxString& filename) wxGetApp().mainframe->add_to_recent_projects(filename); } -bool Plater::priv::init_common_menu(wxMenu* menu, const bool is_part/* = false*/) -{ - if (is_part) { - append_menu_item(menu, wxID_ANY, _L("Delete") + "\tDel", _L("Remove the selected object"), - [this](wxCommandEvent&) { q->remove_selected(); }, "delete", nullptr, [this]() { return can_delete(); }, q); - - append_menu_item(menu, wxID_ANY, _L("Reload from disk"), _L("Reload the selected volumes from disk"), - [this](wxCommandEvent&) { q->reload_from_disk(); }, "", menu, [this]() { return can_reload_from_disk(); }, q); - - sidebar->obj_list()->append_menu_item_export_stl(menu); - } - else { - wxMenuItem* item_increase = append_menu_item(menu, wxID_ANY, _L("Add instance") + "\t+", _L("Add one more instance of the selected object"), - [this](wxCommandEvent&) { q->increase_instances(); }, "add_copies", nullptr, [this]() { return can_increase_instances(); }, q); - wxMenuItem* item_decrease = append_menu_item(menu, wxID_ANY, _L("Remove instance") + "\t-", _L("Remove one instance of the selected object"), - [this](wxCommandEvent&) { q->decrease_instances(); }, "remove_copies", nullptr, [this]() { return can_decrease_instances(); }, q); - wxMenuItem* item_set_number_of_copies = append_menu_item(menu, wxID_ANY, _L("Set number of instances") + dots, _L("Change the number of instances of the selected object"), - [this](wxCommandEvent&) { q->set_number_of_copies(); }, "number_of_copies", nullptr, [this]() { return can_increase_instances(); }, q); - append_menu_item(menu, wxID_ANY, _L("Fill bed with instances") + dots, _L("Fill the remaining area of bed with instances of the selected object"), - [this](wxCommandEvent&) { q->fill_bed_with_instances(); }, "", nullptr, [this]() { return can_increase_instances(); }, q); - - - items_increase.push_back(item_increase); - items_decrease.push_back(item_decrease); - items_set_number_of_copies.push_back(item_set_number_of_copies); - - // Delete menu was moved to be after +/- instace to make it more difficult to be selected by mistake. - append_menu_item(menu, wxID_ANY, _L("Delete") + "\tDel", _L("Remove the selected object"), - [this](wxCommandEvent&) { q->remove_selected(); }, "delete", nullptr, [this]() { return can_delete(); }, q); - - menu->AppendSeparator(); - sidebar->obj_list()->append_menu_item_instance_to_object(menu, q); - menu->AppendSeparator(); - - wxMenuItem* menu_item_printable = sidebar->obj_list()->append_menu_item_printable(menu, q); - menu->AppendSeparator(); - - append_menu_item(menu, wxID_ANY, _L("Reload from disk"), _L("Reload the selected object from disk"), - [this](wxCommandEvent&) { reload_from_disk(); }, "", nullptr, [this]() { return can_reload_from_disk(); }, q); - - append_menu_item(menu, wxID_ANY, _L("Export as STL") + dots, _L("Export the selected object as STL file"), - [this](wxCommandEvent&) { q->export_stl(false, true); }, "", nullptr, - [this]() { - const Selection& selection = get_selection(); - return selection.is_single_full_instance() || selection.is_single_full_object(); - }, q); - - menu->AppendSeparator(); - - // "Scale to print volume" makes a sense just for whole object - sidebar->obj_list()->append_menu_item_scale_selection_to_fit_print_volume(menu); - - q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { - const Selection& selection = get_selection(); - int instance_idx = selection.get_instance_idx(); - evt.Enable(selection.is_single_full_instance() || selection.is_single_full_object()); - if (instance_idx != -1) - { - evt.Check(model.objects[selection.get_object_idx()]->instances[instance_idx]->printable); - view3D->set_as_dirty(); - } - }, menu_item_printable->GetId()); - } - - sidebar->obj_list()->append_menu_item_fix_through_netfabb(menu); - - wxMenu* mirror_menu = new wxMenu(); - if (mirror_menu == nullptr) - return false; - - append_menu_item(mirror_menu, wxID_ANY, _L("Along X axis"), _L("Mirror the selected object along the X axis"), - [this](wxCommandEvent&) { mirror(X); }, "mark_X", menu); - append_menu_item(mirror_menu, wxID_ANY, _L("Along Y axis"), _L("Mirror the selected object along the Y axis"), - [this](wxCommandEvent&) { mirror(Y); }, "mark_Y", menu); - append_menu_item(mirror_menu, wxID_ANY, _L("Along Z axis"), _L("Mirror the selected object along the Z axis"), - [this](wxCommandEvent&) { mirror(Z); }, "mark_Z", menu); - - append_submenu(menu, mirror_menu, wxID_ANY, _L("Mirror"), _L("Mirror the selected object"), "", - [this]() { return can_mirror(); }, q); - - return true; -} - -bool Plater::priv::complit_init_object_menu() -{ - wxMenu* split_menu = new wxMenu(); - if (split_menu == nullptr) - return false; - - append_menu_item(split_menu, wxID_ANY, _L("To objects"), _L("Split the selected object into individual objects"), - [this](wxCommandEvent&) { split_object(); }, "split_object_SMALL", &object_menu, [this]() { return can_split(); }, q); - append_menu_item(split_menu, wxID_ANY, _L("To parts"), _L("Split the selected object into individual sub-parts"), - [this](wxCommandEvent&) { split_volume(); }, "split_parts_SMALL", &object_menu, [this]() { return can_split(); }, q); - - append_submenu(&object_menu, split_menu, wxID_ANY, _L("Split"), _L("Split the selected object"), "", - [this]() { return can_split() && wxGetApp().get_mode() > comSimple; }, q); - object_menu.AppendSeparator(); - - // Layers Editing for object - sidebar->obj_list()->append_menu_item_layers_editing(&object_menu, q); - object_menu.AppendSeparator(); - - // "Add (volumes)" popupmenu will be added later in append_menu_items_add_volume() - - return true; -} - -bool Plater::priv::complit_init_sla_object_menu() -{ - append_menu_item(&sla_object_menu, wxID_ANY, _L("Split"), _L("Split the selected object into individual objects"), - [this](wxCommandEvent&) { split_object(); }, "split_object_SMALL", nullptr, [this]() { return can_split(); }, q); - - sla_object_menu.AppendSeparator(); - - // Add the automatic rotation sub-menu - append_menu_item( - &sla_object_menu, wxID_ANY, _(L("Optimize orientation")), - _(L("Optimize the rotation of the object for better print results.")), - [this](wxCommandEvent &) { - m_ui_jobs.optimize_rotation(); - }); - - return true; -} - -bool Plater::priv::complit_init_part_menu() -{ - append_menu_item(&part_menu, wxID_ANY, _L("Split"), _L("Split the selected object into individual sub-parts"), - [this](wxCommandEvent&) { split_volume(); }, "split_parts_SMALL", nullptr, [this]() { return can_split(); }, q); - - part_menu.AppendSeparator(); - - auto obj_list = sidebar->obj_list(); - obj_list->append_menu_item_change_type(&part_menu, q); - - return true; -} - void Plater::priv::set_current_canvas_as_dirty() { if (current_panel == view3D) @@ -4201,9 +3968,9 @@ bool Plater::priv::can_set_instance_to_object() const return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()) && (model.objects[obj_idx]->instances.size() > 1); } -bool Plater::priv::can_split() const +bool Plater::priv::can_split(bool to_objects) const { - return sidebar->obj_list()->is_splittable(); + return sidebar->obj_list()->is_splittable(to_objects); } bool Plater::priv::layers_height_allowed() const @@ -4319,12 +4086,12 @@ bool Plater::priv::can_decrease_instances() const bool Plater::priv::can_split_to_objects() const { - return can_split(); + return q->can_split(true); } bool Plater::priv::can_split_to_volumes() const { - return (printer_technology != ptSLA) && can_split(); + return (printer_technology != ptSLA) && q->can_split(false); } bool Plater::priv::can_arrange() const @@ -4337,11 +4104,6 @@ bool Plater::priv::can_layers_editing() const return layers_height_allowed(); } -void Plater::priv::update_object_menu() -{ - sidebar->obj_list()->append_menu_items_add_volume(&object_menu); -} - void Plater::priv::show_action_buttons(const bool ready_to_slice) const { // Cache this value, so that the callbacks from the RemovableDriveManager may repeat that value when calling show_action_buttons(). @@ -6061,9 +5823,12 @@ void Plater::suppress_background_process(const bool stop_background_process) } void Plater::fix_through_netfabb(const int obj_idx, const int vol_idx/* = -1*/) { p->fix_through_netfabb(obj_idx, vol_idx); } - -void Plater::update_object_menu() { p->update_object_menu(); } -void Plater::show_action_buttons(const bool ready_to_slice) const { p->show_action_buttons(ready_to_slice); } +void Plater::mirror(Axis axis) { p->mirror(axis); } +void Plater::split_object() { p->split_object(); } +void Plater::split_volume() { p->split_volume(); } +void Plater::optimize_rotation() { p->m_ui_jobs.optimize_rotation();} +void Plater::update_object_menu() { p->menus.update_object_menu(); } +void Plater::show_action_buttons(const bool ready_to_slice) const { p->show_action_buttons(ready_to_slice); } void Plater::copy_selection_to_clipboard() { @@ -6120,7 +5885,7 @@ void Plater::msw_rescale() p->sidebar->msw_rescale(); - p->msw_rescale_object_menu(); + p->menus.msw_rescale(); Layout(); GetParent()->Layout(); @@ -6132,7 +5897,7 @@ void Plater::sys_color_changed() p->sidebar->sys_color_changed(); // msw_rescale_menu updates just icons, so use it - p->msw_rescale_object_menu(); + p->menus.msw_rescale(); Layout(); GetParent()->Layout(); @@ -6297,6 +6062,8 @@ bool Plater::can_copy_to_clipboard() const bool Plater::can_undo() const { return p->undo_redo_stack().has_undo_snapshot(); } bool Plater::can_redo() const { return p->undo_redo_stack().has_redo_snapshot(); } bool Plater::can_reload_from_disk() const { return p->can_reload_from_disk(); } +bool Plater::can_mirror() const { return p->can_mirror(); } +bool Plater::can_split(bool to_objects) const { return p->can_split(to_objects); } const UndoRedo::Stack& Plater::undo_redo_stack_main() const { return p->undo_redo_stack_main(); } void Plater::clear_undo_redo_stack_main() { p->undo_redo_stack_main().clear(); } void Plater::enter_gizmos_stack() { p->enter_gizmos_stack(); } @@ -6338,6 +6105,15 @@ void Plater::bring_instance_forward() p->bring_instance_forward(); } +wxMenu* Plater::object_menu() { return p->menus.object_menu(); } +wxMenu* Plater::part_menu() { return p->menus.part_menu(); } +wxMenu* Plater::sla_object_menu() { return p->menus.sla_object_menu(); } +wxMenu* Plater::default_menu() { return p->menus.default_menu(); } +wxMenu* Plater::instance_menu() { return p->menus.instance_menu(); } +wxMenu* Plater::layer_menu() { return p->menus.layer_menu(); } +wxMenu* Plater::multi_selection_menu() { return p->menus.multi_selection_menu(); } + + SuppressBackgroundProcessingUpdate::SuppressBackgroundProcessingUpdate() : m_was_scheduled(wxGetApp().plater()->is_background_process_update_scheduled()) { diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 82afdcc3d..ff81dad26 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -271,6 +271,10 @@ public: void copy_selection_to_clipboard(); void paste_from_clipboard(); void search(bool plater_is_active); + void mirror(Axis axis); + void split_object(); + void split_volume(); + void optimize_rotation(); bool can_delete() const; bool can_delete_all() const; @@ -287,6 +291,8 @@ public: bool can_undo() const; bool can_redo() const; bool can_reload_from_disk() const; + bool can_mirror() const; + bool can_split(bool to_objects) const; void msw_rescale(); void sys_color_changed(); @@ -375,6 +381,15 @@ public: bool PopupMenu(wxMenu *menu, const wxPoint& pos = wxDefaultPosition); bool PopupMenu(wxMenu *menu, int x, int y) { return this->PopupMenu(menu, wxPoint(x, y)); } + // get same Plater/ObjectList menus + wxMenu* object_menu(); + wxMenu* part_menu(); + wxMenu* sla_object_menu(); + wxMenu* default_menu(); + wxMenu* instance_menu(); + wxMenu* layer_menu(); + wxMenu* multi_selection_menu(); + private: struct priv; std::unique_ptr p;