diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 14773b593b..5a20756cef 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -283,7 +283,7 @@ void ObjectList::show_context_menu() const auto item = GetSelection(); if (item) { - if (m_objects_model->IsSettingsItem(item)) + if (!(m_objects_model->GetItemType(item) & (itObject | itVolume))) return; const auto menu = m_objects_model->GetParent(item) == wxDataViewItem(0) ? create_add_part_popupmenu() : @@ -672,7 +672,7 @@ void ObjectList::load_subobject(bool is_modifier /*= false*/, bool is_lambda/* = parts_changed(obj_idx); for (int i = 0; i < part_names.size(); ++i) { - const wxDataViewItem sel_item = m_objects_model->AddChild(item, part_names.Item(i), + const wxDataViewItem sel_item = m_objects_model->AddVolumeChild(item, part_names.Item(i), is_modifier ? m_bmp_modifiermesh : m_bmp_solidmesh); if (i == part_names.size() - 1) @@ -822,7 +822,7 @@ void ObjectList::load_lambda(const std::string& type_name) m_parts_changed = true; parts_changed(m_selected_object_id); - select_item(m_objects_model->AddChild(GetSelection(), + select_item(m_objects_model->AddVolumeChild(GetSelection(), name, m_bmp_modifiermesh)); #ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME selection_changed(); @@ -896,14 +896,19 @@ bool ObjectList::del_subobject_from_object(const int obj_idx, const int idx, con if (vol->is_model_part()) ++solid_cnt; if (volume->is_model_part() && solid_cnt == 1) { - Slic3r::GUI::show_error(nullptr, _(L("You can't delete the last solid part from this object."))); + Slic3r::GUI::show_error(nullptr, _(L("You can't delete the last solid part from object."))); return false; } (*m_objects)[obj_idx]->delete_volume(idx); } - else if (type == itInstance) + else if (type == itInstance) { + if ((*m_objects)[obj_idx]->instances.size() == 1) { + Slic3r::GUI::show_error(nullptr, _(L("You can't delete the last intance from object."))); + return false; + } (*m_objects)[obj_idx]->delete_instance(idx); + } else return false; @@ -934,7 +939,7 @@ void ObjectList::split(const bool split_part) m_objects_model->DeleteChildren(parent); for (auto id = 0; id < model_object->volumes.size(); id++) - m_objects_model->AddChild(parent, model_object->volumes[id]->name, + m_objects_model->AddVolumeChild(parent, model_object->volumes[id]->name, model_object->volumes[id]->is_modifier() ? m_bmp_modifiermesh : m_bmp_solidmesh, model_object->volumes[id]->config.has("extruder") ? model_object->volumes[id]->config.option("extruder")->value : 0, @@ -944,7 +949,7 @@ void ObjectList::split(const bool split_part) } else { for (auto id = 0; id < model_object->volumes.size(); id++) - m_objects_model->AddChild(item, model_object->volumes[id]->name, + m_objects_model->AddVolumeChild(item, model_object->volumes[id]->name, m_bmp_solidmesh, model_object->volumes[id]->config.has("extruder") ? model_object->volumes[id]->config.option("extruder")->value : 0, @@ -1034,7 +1039,7 @@ void ObjectList::part_selection_changed() m_config = &(*m_objects)[obj_idx]->volumes[volume_id]->config; } } - else { + else if (m_objects_model->GetItemType(item) == itVolume){ og_name = _(L("Part manipulation")); is_part = true; const auto volume_id = m_objects_model->GetVolumeIdByItem(item); @@ -1088,7 +1093,7 @@ void ObjectList::add_object_to_list(size_t obj_idx) if (model_object->volumes.size() > 1) { for (auto id = 0; id < model_object->volumes.size(); id++) - m_objects_model->AddChild(item, + m_objects_model->AddVolumeChild(item, model_object->volumes[id]->name, m_bmp_solidmesh, model_object->volumes[id]->config.option("extruder")->value, @@ -1128,6 +1133,16 @@ void ObjectList::delete_all_objects_from_list() part_selection_changed(); } +void ObjectList::increase_object_instances(const size_t obj_idx, const size_t num) +{ + select_item(m_objects_model->AddInstanceChild(m_objects_model->GetItemById(obj_idx), num)); +} + +void ObjectList::decrease_object_instances(const size_t obj_idx, const size_t num) +{ + select_item(m_objects_model->DeleteLastInstance(m_objects_model->GetItemById(obj_idx), num)); +} + void ObjectList::set_object_count(int idx, int count) { m_objects_model->SetValue(wxString::Format("%d", count), idx, 1); @@ -1231,15 +1246,21 @@ void ObjectList::update_selections_on_canvas() return; } - auto parent = m_objects_model->GetParent(item); - const int obj_idx = m_objects_model->GetIdByItem(parent); - const int vol_idx = m_objects_model->GetVolumeIdByItem(item); - selection.add_volume(obj_idx, vol_idx, as_single_selection); + if (m_objects_model->GetItemType(item) == itVolume) { + const int obj_idx = m_objects_model->GetIdByItem(m_objects_model->GetParent(item)); + const int vol_idx = m_objects_model->GetVolumeIdByItem(item); + selection.add_volume(obj_idx, vol_idx, as_single_selection); + } + else if (m_objects_model->GetItemType(item) == itInstance) { + const int obj_idx = m_objects_model->GetIdByItem(m_objects_model->GetTopParent(item)); + const int inst_idx = m_objects_model->GetInstanceIdByItem(item); + selection.add_instance(obj_idx, inst_idx, as_single_selection); + } }; if (sel_cnt == 1) { wxDataViewItem item = GetSelection(); - if (m_objects_model->IsSettingsItem(item)) + if (m_objects_model->GetItemType(item) & (itSettings|itInstanceRoot)) add_to_selection(m_objects_model->GetParent(item), selection, true); else add_to_selection(item, selection, true); diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index 418e1e8779..2325734bd7 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -112,6 +112,10 @@ public: void delete_volume_from_list(const size_t obj_idx, const size_t vol_idx); // Delete all objects from the list void delete_all_objects_from_list(); + // Increase instances count + void increase_object_instances(const size_t obj_idx, const size_t num); + // Decrease instances count + void decrease_object_instances(const size_t obj_idx, const size_t num); // Set count of object on c++ side void set_object_count(int idx, int count); diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp index ed2646b697..f298dc034e 100644 --- a/src/slic3r/GUI/MainFrame.hpp +++ b/src/slic3r/GUI/MainFrame.hpp @@ -30,11 +30,11 @@ class Tab; enum QuickSlice { - qsUndef, - qsReslice, - qsSaveAs, - qsExportSVG, - qsExportPNG + qsUndef = 0, + qsReslice = 1, + qsSaveAs = 2, + qsExportSVG = 4, + qsExportPNG = 8 }; struct PresetTab { diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 9f0cdc89b8..7ef58d8bbc 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1747,6 +1747,7 @@ void Plater::increase(size_t num) } sidebar().obj_list()->set_object_count(*obj_idx, model_object->instances.size()); + sidebar().obj_list()->increase_object_instances(*obj_idx, num); if (p->get_config("autocenter") == "1") { p->arrange(); @@ -1771,6 +1772,7 @@ void Plater::decrease(size_t num) p->print.get_object(*obj_idx)->delete_last_copy(); } sidebar().obj_list()->set_object_count(*obj_idx, model_object->instances.size()); + sidebar().obj_list()->decrease_object_instances(*obj_idx, num); } else { remove(*obj_idx); } diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index 4b57540c2e..cd5f339e0b 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -429,7 +429,7 @@ wxDataViewItem PrusaObjectDataViewModel::Add(const wxString &name, const int ins return child; } -wxDataViewItem PrusaObjectDataViewModel::AddChild( const wxDataViewItem &parent_item, +wxDataViewItem PrusaObjectDataViewModel::AddVolumeChild(const wxDataViewItem &parent_item, const wxString &name, const wxBitmap& icon, const int extruder/* = 0*/, @@ -440,8 +440,7 @@ wxDataViewItem PrusaObjectDataViewModel::AddChild( const wxDataViewItem &parent_ const wxString extruder_str = extruder == 0 ? "default" : wxString::Format("%d", extruder); - if (create_frst_child && (root->GetChildren().Count() == 0 || - (root->GetChildren().Count() == 1 && root->GetNthChild(0)->m_type == itSettings))) + if (create_frst_child && root->m_volumes_cnt == 0) { const auto icon_solid_mesh = wxIcon(Slic3r::GUI::from_u8(Slic3r::var("object.png")), wxBITMAP_TYPE_PNG); const auto node = new PrusaObjectDataViewModelNode(root, root->m_name, icon_solid_mesh, extruder_str, 0); @@ -449,16 +448,17 @@ wxDataViewItem PrusaObjectDataViewModel::AddChild( const wxDataViewItem &parent_ // notify control const wxDataViewItem child((void*)node); ItemAdded(parent_item, child); + + root->m_volumes_cnt++; } - const auto volume_id = root->GetChildCount() > 0 && root->GetNthChild(0)->m_type == itSettings ? - root->GetChildCount() - 1 : root->GetChildCount(); - - const auto node = new PrusaObjectDataViewModelNode(root, name, icon, extruder_str, volume_id); + const auto node = new PrusaObjectDataViewModelNode(root, name, icon, extruder_str, root->m_volumes_cnt); root->Append(node); // notify control const wxDataViewItem child((void*)node); - ItemAdded(parent_item, child); + ItemAdded(parent_item, child); + root->m_volumes_cnt++; + return child; } @@ -467,7 +467,7 @@ wxDataViewItem PrusaObjectDataViewModel::AddSettingsChild(const wxDataViewItem & PrusaObjectDataViewModelNode *root = (PrusaObjectDataViewModelNode*)parent_item.GetID(); if (!root) return wxDataViewItem(0); - const auto node = new PrusaObjectDataViewModelNode(root); + const auto node = new PrusaObjectDataViewModelNode(root, itSettings); root->Insert(node, 0); // notify control const wxDataViewItem child((void*)node); @@ -475,6 +475,55 @@ wxDataViewItem PrusaObjectDataViewModel::AddSettingsChild(const wxDataViewItem & return child; } +int get_istances_root_idx(PrusaObjectDataViewModelNode *parent_node) +{ + int inst_root_id = -1; + int stop_search_i = parent_node->GetChildCount(); + if (stop_search_i > 2) stop_search_i = 2; + for (int i = 0; i < stop_search_i; ++i) + if (parent_node->GetNthChild(i)->m_type & itInstanceRoot) { + inst_root_id = i; + break; + } + return inst_root_id; +} + +wxDataViewItem PrusaObjectDataViewModel::AddInstanceChild(const wxDataViewItem &parent_item, size_t num) +{ + PrusaObjectDataViewModelNode *parent_node = (PrusaObjectDataViewModelNode*)parent_item.GetID(); + if (!parent_node) return wxDataViewItem(0); + + // Check and create/get instances root node + const int inst_root_id = get_istances_root_idx(parent_node); + + PrusaObjectDataViewModelNode *inst_root_node = inst_root_id < 0 ? + new PrusaObjectDataViewModelNode(parent_node, itInstanceRoot) : + parent_node->GetNthChild(inst_root_id); + const wxDataViewItem inst_root_item((void*)inst_root_node); + + if (inst_root_id < 0) { + const unsigned insert_pos = parent_node->GetChildCount() == 0 || parent_node->GetNthChild(0)->m_type != itSettings ? 0 : 1; + parent_node->Insert(inst_root_node, insert_pos); + // notify control + ItemAdded(parent_item, inst_root_item); + num++; + } + + // Add instance nodes + PrusaObjectDataViewModelNode *instance_node = nullptr; + size_t counter = 0; + while (counter < num){ + instance_node = new PrusaObjectDataViewModelNode(inst_root_node, itInstance); + inst_root_node->Append(instance_node); + // notify control + const wxDataViewItem instance_item((void*)instance_node); + ItemAdded(inst_root_item, instance_item); + ++counter; + } + + return wxDataViewItem((void*)instance_node); +} + wxDataViewItem PrusaObjectDataViewModel::Delete(const wxDataViewItem &item) { auto ret_item = wxDataViewItem(0); @@ -505,6 +554,30 @@ wxDataViewItem PrusaObjectDataViewModel::Delete(const wxDataViewItem &item) if (cur_idx > idx) children[i]->SetIdx(cur_idx-1); } + + // if there is last instance item, delete both of it and instance root item + if (node_parent->GetChildCount() == 1 && node_parent->GetNthChild(0)->m_type == itInstance) + { + delete node; + ItemDeleted(parent, item); + + PrusaObjectDataViewModelNode *last_instance_node = node_parent->GetNthChild(0); + node_parent->GetChildren().Remove(last_instance_node); + delete last_instance_node; + ItemDeleted(parent, wxDataViewItem(last_instance_node)); + + PrusaObjectDataViewModelNode *obj_node = node_parent->GetParent(); + obj_node->GetChildren().Remove(node_parent); + delete node_parent; + ret_item = wxDataViewItem(obj_node); + ItemDeleted(ret_item, wxDataViewItem(node_parent)); + +#ifndef __WXGTK__ + if (obj_node->GetChildCount() == 0) + obj_node->m_container = false; +#endif //__WXGTK__ + return ret_item; + } } else { @@ -534,6 +607,54 @@ wxDataViewItem PrusaObjectDataViewModel::Delete(const wxDataViewItem &item) return ret_item; } +wxDataViewItem PrusaObjectDataViewModel::DeleteLastInstance(const wxDataViewItem &parent_item, size_t num) +{ + auto ret_item = wxDataViewItem(0); + PrusaObjectDataViewModelNode *parent_node = (PrusaObjectDataViewModelNode*)parent_item.GetID(); + if (!parent_node) return ret_item; + + const int inst_root_id = get_istances_root_idx(parent_node); + if (inst_root_id < 0) return ret_item; + + wxDataViewItemArray items; + PrusaObjectDataViewModelNode *inst_root_node = parent_node->GetNthChild(inst_root_id); + const wxDataViewItem inst_root_item((void*)inst_root_node); + + const int inst_cnt = inst_root_node->GetChildCount(); + const bool delete_inst_root_item = inst_cnt - num < 2 ? true : false; + + int stop = delete_inst_root_item ? 0 : inst_cnt - num; + for (int i = inst_cnt - 1; i >= stop;--i) { + PrusaObjectDataViewModelNode *last_instance_node = inst_root_node->GetNthChild(i); + inst_root_node->GetChildren().Remove(last_instance_node); + delete last_instance_node; + ItemDeleted(inst_root_item, wxDataViewItem(last_instance_node)); + } + +// for (int i = delete_inst_root_item ? 0 : inst_cnt - num; i < inst_cnt; ++i) +// items.Add(wxDataViewItem(inst_root_node->GetNthChild(i))); +// +// if (delete_inst_root_item) +// inst_root_node->GetChildren().RemoveAt(0, inst_cnt); +// else { +// ret_item = wxDataViewItem(inst_root_node->GetNthChild(inst_cnt - num - 1)); +// inst_root_node->GetChildren().RemoveAt(inst_cnt - num, num); +// } + +// ItemsDeleted(inst_root_item, items); + if (delete_inst_root_item) { + ret_item = parent_item; + parent_node->GetChildren().Remove(inst_root_node); + ItemDeleted(parent_item, inst_root_item); +#ifndef __WXGTK__ + if (parent_node->GetChildCount() == 0) + parent_node->m_container = false; +#endif //__WXGTK__ + } + + return ret_item; +} + void PrusaObjectDataViewModel::DeleteAll() { while (!m_objects.empty()) @@ -620,36 +741,49 @@ int PrusaObjectDataViewModel::GetIdByItem(const wxDataViewItem& item) return it - m_objects.begin(); } -int PrusaObjectDataViewModel::GetVolumeIdByItem(const wxDataViewItem& item) +int PrusaObjectDataViewModel::GetIdByItemAndType(const wxDataViewItem& item, const ItemType type) const { wxASSERT(item.IsOk()); PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); - if (!node || node->m_type != itVolume) + if (!node || node->m_type != type) return -1; return node->GetIdx(); } +int PrusaObjectDataViewModel::GetVolumeIdByItem(const wxDataViewItem& item) const +{ + return GetIdByItemAndType(item, itVolume); +} + +int PrusaObjectDataViewModel::GetInstanceIdByItem(const wxDataViewItem& item) const +{ + return GetIdByItemAndType(item, itInstance); +} + void PrusaObjectDataViewModel::GetItemInfo(const wxDataViewItem& item, ItemType& type, int& obj_idx, int& idx) { wxASSERT(item.IsOk()); type = itUndef; PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); - if (!node) return; + if (!node || node->GetIdx() < 0 && node->GetType() != itObject) + return; - type = node->GetType(); idx = node->GetIdx(); + type = node->GetType(); PrusaObjectDataViewModelNode *parent_node = node->GetParent(); if (!parent_node) return; if (type == itInstance) parent_node = node->GetParent()->GetParent(); - if (!parent_node) return; + if (!parent_node || parent_node->m_type != itObject) { type = itUndef; return; } auto it = find(m_objects.begin(), m_objects.end(), parent_node); if (it != m_objects.end()) obj_idx = it - m_objects.begin(); + else + type = itUndef; } wxString PrusaObjectDataViewModel::GetName(const wxDataViewItem &item) const @@ -819,12 +953,32 @@ wxDataViewItem PrusaObjectDataViewModel::GetParent(const wxDataViewItem &item) c PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); // objects nodes has no parent too - if (find(m_objects.begin(), m_objects.end(),node) != m_objects.end()) + if (node->m_type == itObject) return wxDataViewItem(0); return wxDataViewItem((void*)node->GetParent()); } +wxDataViewItem PrusaObjectDataViewModel::GetTopParent(const wxDataViewItem &item) const +{ + // the invisible root node has no parent + if (!item.IsOk()) + return wxDataViewItem(0); + + PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); + if (node->m_type == itObject) + return item; + + PrusaObjectDataViewModelNode *parent_node = node->GetParent(); + while (parent_node->m_type != itObject) + { + node = parent_node; + parent_node = node->GetParent(); + } + + return wxDataViewItem((void*)parent_node); +} + bool PrusaObjectDataViewModel::IsContainer(const wxDataViewItem &item) const { // the invisible root node can have children diff --git a/src/slic3r/GUI/wxExtensions.hpp b/src/slic3r/GUI/wxExtensions.hpp index f3c417426f..c1ff4074e2 100644 --- a/src/slic3r/GUI/wxExtensions.hpp +++ b/src/slic3r/GUI/wxExtensions.hpp @@ -195,12 +195,12 @@ DECLARE_VARIANT_OBJECT(PrusaDataViewBitmapText) // ---------------------------------------------------------------------------- enum ItemType{ - itUndef, - itObject, - itVolume, - itInstanceRoot, - itInstance, - itSettings + itUndef = 0, + itObject = 1, + itVolume = 2, + itInstanceRoot = 4, + itInstance = 8, + itSettings = 16 }; class PrusaObjectDataViewModelNode; @@ -211,6 +211,7 @@ class PrusaObjectDataViewModelNode PrusaObjectDataViewModelNode* m_parent; MyObjectTreeModelNodePtrArray m_children; wxBitmap m_empty_bmp; + size_t m_volumes_cnt = 0; std::vector< std::string > m_opt_categories; public: PrusaObjectDataViewModelNode(const wxString &name, const int instances_count=1) { @@ -248,7 +249,7 @@ public: set_part_action_icon(); } - PrusaObjectDataViewModelNode( PrusaObjectDataViewModelNode* parent, const ItemType type = itSettings) : + PrusaObjectDataViewModelNode( PrusaObjectDataViewModelNode* parent, const ItemType type) : m_parent(parent), m_copy(wxEmptyString), m_type(type), @@ -261,8 +262,8 @@ public: m_name = "Instances"; } else if (type == itInstance) { - m_name = wxString::Format("Instances_%d", parent->GetChildCount() + 1); m_idx = parent->GetChildCount(); + m_name = wxString::Format("Instance_%d", m_idx+1); } } @@ -370,7 +371,11 @@ public: void SetIdx(const int& idx) { m_idx = idx; + // update name if this node is instance + if (m_type == itInstance) + m_name = wxString::Format("Instance_%d", m_idx + 1); } + int GetIdx() const { return m_idx; } @@ -407,6 +412,8 @@ public: void set_object_action_icon(); void set_part_action_icon(); bool update_settings_digest(const std::vector& categories); +private: + friend class PrusaObjectDataViewModel; }; // ---------------------------------------------------------------------------- @@ -422,19 +429,23 @@ public: wxDataViewItem Add(const wxString &name); wxDataViewItem Add(const wxString &name, const int instances_count); - wxDataViewItem AddChild(const wxDataViewItem &parent_item, + wxDataViewItem AddVolumeChild(const wxDataViewItem &parent_item, const wxString &name, const wxBitmap& icon, const int extruder = 0, const bool create_frst_child = true); wxDataViewItem AddSettingsChild(const wxDataViewItem &parent_item); + wxDataViewItem AddInstanceChild(const wxDataViewItem &parent_item, size_t num); wxDataViewItem Delete(const wxDataViewItem &item); + wxDataViewItem DeleteLastInstance(const wxDataViewItem &parent_item, size_t num); void DeleteAll(); void DeleteChildren(wxDataViewItem& parent); wxDataViewItem GetItemById(int obj_idx); wxDataViewItem GetItemByVolumeId(int obj_idx, int volume_idx); int GetIdByItem(const wxDataViewItem& item); - int GetVolumeIdByItem(const wxDataViewItem& item); + int GetIdByItemAndType(const wxDataViewItem& item, const ItemType type) const; + int GetVolumeIdByItem(const wxDataViewItem& item) const; + int GetInstanceIdByItem(const wxDataViewItem& item) const; void GetItemInfo(const wxDataViewItem& item, ItemType& type, int& obj_idx, int& idx); bool IsEmpty() { return m_objects.empty(); } @@ -466,6 +477,8 @@ public: virtual bool IsEnabled(const wxDataViewItem &item, unsigned int col) const override; virtual wxDataViewItem GetParent(const wxDataViewItem &item) const override; + // get object item + wxDataViewItem GetTopParent(const wxDataViewItem &item) const; virtual bool IsContainer(const wxDataViewItem &item) const override; virtual unsigned int GetChildren(const wxDataViewItem &parent, wxDataViewItemArray &array) const override;