diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 7148be513..550337ab7 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -1639,16 +1639,20 @@ void ObjectList::part_selection_changed() bool update_and_show_manipulations = false; bool update_and_show_settings = false; - if (multiple_selection()) { + const auto item = GetSelection(); + + if ( multiple_selection() || item && m_objects_model->GetItemType(item) == itInstanceRoot ) + { og_name = _(L("Group manipulation")); - update_and_show_manipulations = true; + + const Selection& selection = wxGetApp().plater()->canvas3D()->get_selection(); + // don't show manipulation panel for case of all Object's parts selection + update_and_show_manipulations = !selection.is_single_full_instance(); } else { - const auto item = GetSelection(); if (item) { - bool is_part = false; if (m_objects_model->GetParent(item) == wxDataViewItem(0)) { obj_idx = m_objects_model->GetIdByItem(item); og_name = _(L("Object manipulation")); @@ -1666,7 +1670,6 @@ void ObjectList::part_selection_changed() } else { og_name = _(L("Part Settings to modify")); - is_part = true; auto main_parent = m_objects_model->GetParent(parent); obj_idx = m_objects_model->GetIdByItem(main_parent); const auto volume_id = m_objects_model->GetVolumeIdByItem(parent); @@ -1676,7 +1679,6 @@ void ObjectList::part_selection_changed() } 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); m_config = &(*m_objects)[obj_idx]->volumes[volume_id]->config; update_and_show_manipulations = true; @@ -1924,6 +1926,15 @@ bool ObjectList::multiple_selection() const return GetSelectedItemsCount() > 1; } +bool ObjectList::is_selected(const ItemType type) const +{ + const wxDataViewItem& item = GetSelection(); + if (item) + return m_objects_model->GetItemType(item) == type; + + return false; +} + void ObjectList::update_selections() { const Selection& selection = wxGetApp().plater()->canvas3D()->get_selection(); @@ -1942,25 +1953,64 @@ void ObjectList::update_selections() return; } } - - if (selection.is_single_full_object()) +// if (selection.is_single_full_object() && selection.get_instance_idx() != -1) +// { +// sels.Add(m_objects_model->GetItemById(selection.get_object_idx())); +// } +// else if (selection.is_single_volume() || selection.is_modifier() || +// selection.is_multiple_volume() || selection.is_multiple_full_object()) +// { +// for (auto idx : selection.get_volume_idxs()) { +// const auto gl_vol = selection.get_volume(idx); +// if (selection.is_multiple_full_object()) +// sels.Add(m_objects_model->GetItemById(gl_vol->object_idx())); +// else if (gl_vol->volume_idx() >= 0) +// // Only add GLVolumes with non-negative volume_ids. GLVolumes with negative volume ids +// // are not associated with ModelVolumes, but they are temporarily generated by the backend +// // (for example, SLA supports or SLA pad). +// sels.Add(m_objects_model->GetItemByVolumeId(gl_vol->object_idx(), gl_vol->volume_idx())); +// } +// } + else if (selection.is_single_full_object() || selection.is_multiple_full_object()) { - sels.Add(m_objects_model->GetItemById(selection.get_object_idx())); + const Selection::ObjectIdxsToInstanceIdxsMap& objects_content = selection.get_content(); + for (const auto& object : objects_content) { + if (object.second.size() == 1) // object with 1 instance + sels.Add(m_objects_model->GetItemById(object.first)); + else if (object.second.size() > 1) // object with several instances + { + wxDataViewItemArray current_sels; + GetSelections(current_sels); + const wxDataViewItem frst_inst_item = m_objects_model->GetItemByInstanceId(object.first, 0); + + bool root_is_selected = false; + for (const auto& item:current_sels) + if (item == m_objects_model->GetParent(frst_inst_item)) { + root_is_selected = true; + break; + } + if (root_is_selected) + continue; + + const Selection::InstanceIdxsList& instances = object.second; + for (const auto& inst : instances) + sels.Add(m_objects_model->GetItemByInstanceId(object.first, inst)); + } + } } - else if (selection.is_single_volume() || selection.is_modifier() || - selection.is_multiple_volume() || selection.is_multiple_full_object()) { + else if (selection.is_single_volume() || selection.is_modifier() || selection.is_multiple_volume()) + { for (auto idx : selection.get_volume_idxs()) { const auto gl_vol = selection.get_volume(idx); - if (selection.is_multiple_full_object()) - sels.Add(m_objects_model->GetItemById(gl_vol->object_idx())); - else if (gl_vol->volume_idx() >= 0) + if (gl_vol->volume_idx() >= 0) // Only add GLVolumes with non-negative volume_ids. GLVolumes with negative volume ids // are not associated with ModelVolumes, but they are temporarily generated by the backend // (for example, SLA supports or SLA pad). sels.Add(m_objects_model->GetItemByVolumeId(gl_vol->object_idx(), gl_vol->volume_idx())); } } - else if (selection.is_single_full_instance() || selection.is_multiple_full_instance()) { + else if (selection.is_single_full_instance() || selection.is_multiple_full_instance()) + { for (auto idx : selection.get_instance_idxs()) { sels.Add(m_objects_model->GetItemByInstanceId(selection.get_object_idx(), idx)); } @@ -2137,11 +2187,25 @@ void ObjectList::fix_multiselection_conflicts() wxDataViewItemArray sels; GetSelections(sels); - for (auto item : sels) { - if (m_objects_model->GetItemType(item) & (itSettings|itInstanceRoot)) + for (const auto item : sels) { + if (!IsSelected(item)) // if this item is unselected now (from previous actions) + continue; + + if (m_objects_model->GetItemType(item) & itSettings) { Unselect(item); - else if (m_objects_model->GetParent(item) != wxDataViewItem(0)) - Unselect(m_objects_model->GetParent(item)); + continue; + } + + const wxDataViewItem& parent = m_objects_model->GetParent(item); + if (parent != wxDataViewItem(0) && IsSelected(parent)) + Unselect(parent); + else + { + wxDataViewItemArray unsels; + m_objects_model->GetAllChildren(item, unsels); + for (const auto unsel_item : unsels) + Unselect(unsel_item); + } } m_prevent_list_events = false; diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index 4ddc6ea71..b0ed756f8 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -251,6 +251,7 @@ public: void init_objects(); bool multiple_selection() const ; + bool is_selected(const ItemType type) const; void update_selections(); void update_selections_on_canvas(); void select_item(const wxDataViewItem& item); diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 61e98d542..48bb1f690 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -161,6 +161,8 @@ void ObjectManipulation::update_settings_value(const Selection& selection) m_new_move_label_string = L("Position"); m_new_rotate_label_string = L("Rotation"); m_new_scale_label_string = L("Scale factors"); + + ObjectList* obj_list = wxGetApp().obj_list(); if (selection.is_single_full_instance()) { // all volumes in the selection belongs to the same instance, any of them contains the needed instance data, so we take the first one @@ -187,7 +189,7 @@ void ObjectManipulation::update_settings_value(const Selection& selection) m_new_enabled = true; } - else if (selection.is_single_full_object()) + else if (selection.is_single_full_object() && obj_list->is_selected(itObject)) { m_cache.instance.reset(); @@ -212,7 +214,7 @@ void ObjectManipulation::update_settings_value(const Selection& selection) m_new_size = (volume->get_volume_transformation().get_matrix(true, true) * volume->bounding_box.size()).cwiseAbs(); m_new_enabled = true; } - else if (wxGetApp().obj_list()->multiple_selection()) + else if (obj_list->multiple_selection() || obj_list->is_selected(itInstanceRoot)) { reset_settings_value(); m_new_move_label_string = L("Translate"); diff --git a/src/slic3r/GUI/Selection.hpp b/src/slic3r/GUI/Selection.hpp index 4b2a72d7c..883fb5400 100644 --- a/src/slic3r/GUI/Selection.hpp +++ b/src/slic3r/GUI/Selection.hpp @@ -133,10 +133,12 @@ private: const Transform3d& get_instance_full_matrix() const { return m_instance.full_matrix; } }; +public: typedef std::map VolumesCache; typedef std::set InstanceIdxsList; typedef std::map ObjectIdxsToInstanceIdxsMap; +private: struct Cache { // Cache of GLVolume derived transformation matrices, valid during mouse dragging. diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index 09096cb05..0671bb62c 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -1238,6 +1238,32 @@ unsigned int PrusaObjectDataViewModel::GetChildren(const wxDataViewItem &parent, return count; } +void PrusaObjectDataViewModel::GetAllChildren(const wxDataViewItem &parent, wxDataViewItemArray &array) const +{ + PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)parent.GetID(); + if (!node) { + for (auto object : m_objects) + array.Add(wxDataViewItem((void*)object)); + } + else if (node->GetChildCount() == 0) + return; + else { + const size_t count = node->GetChildren().GetCount(); + for (size_t pos = 0; pos < count; pos++) { + PrusaObjectDataViewModelNode *child = node->GetChildren().Item(pos); + array.Add(wxDataViewItem((void*)child)); + } + } + + wxDataViewItemArray new_array = array; + for (const auto item : new_array) + { + wxDataViewItemArray children; + GetAllChildren(item, children); + WX_APPEND_ARRAY(array, children); + } +} + ItemType PrusaObjectDataViewModel::GetItemType(const wxDataViewItem &item) const { if (!item.IsOk()) diff --git a/src/slic3r/GUI/wxExtensions.hpp b/src/slic3r/GUI/wxExtensions.hpp index 27da67deb..72221962c 100644 --- a/src/slic3r/GUI/wxExtensions.hpp +++ b/src/slic3r/GUI/wxExtensions.hpp @@ -511,7 +511,7 @@ public: virtual bool IsContainer(const wxDataViewItem &item) const override; virtual unsigned int GetChildren(const wxDataViewItem &parent, wxDataViewItemArray &array) const override; - + void GetAllChildren(const wxDataViewItem &parent,wxDataViewItemArray &array) const; // Is the container just a header or an item with all columns // In our case it is an item with all columns virtual bool HasContainerColumns(const wxDataViewItem& WXUNUSED(item)) const override { return true; }