From ac6a232795c1b78f600423012bb0cf5ead6c2fe1 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 29 Mar 2019 14:36:09 +0100 Subject: [PATCH 1/4] Improved Selection from ObjectList side --- src/slic3r/GUI/GUI_ObjectList.cpp | 102 ++++++++++++++++++---- src/slic3r/GUI/GUI_ObjectList.hpp | 1 + src/slic3r/GUI/GUI_ObjectManipulation.cpp | 6 +- src/slic3r/GUI/Selection.hpp | 2 + src/slic3r/GUI/wxExtensions.cpp | 26 ++++++ src/slic3r/GUI/wxExtensions.hpp | 2 +- 6 files changed, 117 insertions(+), 22 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 224bb802e..4c46e7135 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)); } @@ -2136,11 +2186,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 6c8fdcab7..637596591 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(); 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 200f239c1..6390214cd 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 55544f28e..77d9a4a10 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -1239,6 +1239,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; } From 81809018bc9087cb563fc74591eac4a7309a91f6 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 2 Apr 2019 16:33:52 +0200 Subject: [PATCH 2/4] Selection improvements + "Delete" menu_item for Instances under OSX --- src/slic3r/GUI/GUI_ObjectList.cpp | 158 ++++++++++++++++++++++-------- src/slic3r/GUI/GUI_ObjectList.hpp | 10 ++ 2 files changed, 129 insertions(+), 39 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 4c46e7135..b61b8280f 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -82,13 +82,14 @@ ObjectList::ObjectList(wxWindow* parent) : init_icons(); // describe control behavior - Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, [this](wxEvent& event) { + Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, [this](wxDataViewEvent& event) { #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(); #endif // __APPLE__ + m_last_selected_item = event.GetItem(); selection_changed(); #ifndef __WXMSW__ set_tooltip_for_item(get_mouse_position_in_control()); @@ -1006,8 +1007,7 @@ wxMenuItem* ObjectList::append_menu_item_instance_to_object(wxMenu* menu) void ObjectList::append_menu_items_osx(wxMenu* menu) { - append_menu_item(menu, wxID_ANY, _(L("Delete item")), "", - [this](wxCommandEvent&) { remove(); }, "", menu); + append_menu_item_delete(menu); append_menu_item(menu, wxID_ANY, _(L("Rename")), "", [this](wxCommandEvent&) { rename_item(); }, "", menu); @@ -1062,6 +1062,12 @@ void ObjectList::append_menu_item_change_extruder(wxMenu* menu) const } } +void ObjectList::append_menu_item_delete(wxMenu* menu) +{ + append_menu_item(menu, wxID_ANY, _(L("Delete")), "", + [this](wxCommandEvent&) { remove(); }, "", menu); +} + void ObjectList::create_object_popupmenu(wxMenu *menu) { #ifdef __WXOSX__ @@ -1118,6 +1124,9 @@ void ObjectList::create_part_popupmenu(wxMenu *menu) void ObjectList::create_instance_popupmenu(wxMenu*menu) { +#ifdef __WXOSX__ + append_menu_item_delete(menu); +#endif // __WXOSX__ m_menu_item_split_instances = append_menu_item_instance_to_object(menu); wxGetApp().plater()->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { @@ -1552,7 +1561,7 @@ void ObjectList::split() auto opt_keys = model_object->volumes[id]->config.keys(); if ( !(opt_keys.size() == 1 && opt_keys[0] == "extruder") ) { select_item(m_objects_model->AddSettingsChild(vol_item)); - Collapse(vol_item); + /*Collapse*/Expand(vol_item); } } @@ -1746,7 +1755,7 @@ void ObjectList::add_object_to_list(size_t obj_idx) auto opt_keys = model_object->volumes[id]->config.keys(); if (!opt_keys.empty() && !(opt_keys.size() == 1 && opt_keys[0] == "extruder")) { select_item(m_objects_model->AddSettingsChild(vol_item)); - Collapse(vol_item); + /*Collapse*/Expand(vol_item); } } Expand(item); @@ -1760,7 +1769,7 @@ void ObjectList::add_object_to_list(size_t obj_idx) auto opt_keys = model_object->config.keys(); if (!opt_keys.empty() && !(opt_keys.size() == 1 && opt_keys[0] == "extruder")) { select_item(m_objects_model->AddSettingsChild(item)); - Collapse(item); + /*Collapse*/Expand(item); } #ifndef __WXOSX__ @@ -1940,6 +1949,8 @@ void ObjectList::update_selections() const Selection& selection = wxGetApp().plater()->canvas3D()->get_selection(); wxDataViewItemArray sels; + 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 ) { @@ -1953,24 +1964,6 @@ void ObjectList::update_selections() return; } } -// 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()) { const Selection::ObjectIdxsToInstanceIdxsMap& objects_content = selection.get_content(); @@ -1985,8 +1978,10 @@ void ObjectList::update_selections() bool root_is_selected = false; for (const auto& item:current_sels) - if (item == m_objects_model->GetParent(frst_inst_item)) { + if (item == m_objects_model->GetParent(frst_inst_item) || + item == m_objects_model->GetTopParent(frst_inst_item)) { root_is_selected = true; + sels.Add(item); break; } if (root_is_selected) @@ -2008,6 +2003,7 @@ void ObjectList::update_selections() // (for example, SLA supports or SLA pad). sels.Add(m_objects_model->GetItemByVolumeId(gl_vol->object_idx(), gl_vol->volume_idx())); } + m_selection_mode = smVolume; } else if (selection.is_single_full_instance() || selection.is_multiple_full_instance()) { @@ -2017,7 +2013,7 @@ void ObjectList::update_selections() } else if (selection.is_mixed()) { - auto& objects_content_list = selection.get_content(); + const Selection::ObjectIdxsToInstanceIdxsMap& objects_content_list = selection.get_content(); for (auto idx : selection.get_volume_idxs()) { const auto gl_vol = selection.get_volume(idx); @@ -2050,6 +2046,9 @@ void ObjectList::update_selections() sels.Add(m_objects_model->GetItemByVolumeId(glv_obj_idx, glv_vol_idx)); } } + + if (sels.size() == 0) + m_selection_mode = smUndef; select_items(sels); @@ -2085,29 +2084,34 @@ void ObjectList::update_selections_on_canvas() auto add_to_selection = [this](const wxDataViewItem& item, Selection& selection, int instance_idx, bool as_single_selection) { - if (m_objects_model->GetParent(item) == wxDataViewItem(0)) { - selection.add_object(m_objects_model->GetIdByItem(item), as_single_selection); + const ItemType& type = m_objects_model->GetItemType(item); + if ( type == itInstanceRoot || m_objects_model->GetParent(item) == wxDataViewItem(0) ) { + wxDataViewItem obj_item = type == itInstanceRoot ? m_objects_model->GetParent(item) : item; + selection.add_object(m_objects_model->GetIdByItem(obj_item), as_single_selection); return; } - if (m_objects_model->GetItemType(item) == itVolume) { + if (type == 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, std::max(instance_idx, 0), as_single_selection); } - else if (m_objects_model->GetItemType(item) == itInstance) { + else if (type == 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); } }; + // stores current instance idx before to clear the selection + int instance_idx = selection.get_instance_idx(); + if (sel_cnt == 1) { wxDataViewItem item = GetSelection(); if (m_objects_model->GetItemType(item) & (itSettings|itInstanceRoot)) - add_to_selection(m_objects_model->GetParent(item), selection, -1, true); + add_to_selection(m_objects_model->GetParent(item), selection, instance_idx, true); else - add_to_selection(item, selection, -1, true); + add_to_selection(item, selection, instance_idx, true); wxGetApp().plater()->canvas3D()->update_gizmos_on_off_state(); return; @@ -2116,8 +2120,6 @@ void ObjectList::update_selections_on_canvas() wxDataViewItemArray sels; GetSelections(sels); - // stores current instance idx before to clear the selection - int instance_idx = selection.get_instance_idx(); selection.clear(); for (auto item: sels) add_to_selection(item, selection, instance_idx, false); @@ -2163,22 +2165,79 @@ void ObjectList::select_item_all_children() if (!GetSelection() || m_objects_model->GetItemType(GetSelection()) == itObject) { for (int i = 0; i < m_objects->size(); i++) sels.Add(m_objects_model->GetItemById(i)); + m_selection_mode = smInstance; } else { const auto item = GetSelection(); // Some volume(instance) is selected => select all volumes(instances) inside the current object - if (m_objects_model->GetItemType(item) & (itVolume | itInstance)) { + if (m_objects_model->GetItemType(item) & (itVolume | itInstance)) m_objects_model->GetChildren(m_objects_model->GetParent(item), sels); - } + + m_selection_mode = m_objects_model->GetItemType(item)&itVolume ? smVolume : smInstance; } SetSelections(sels); selection_changed(); } +// update selection mode for non-multiple selection +void ObjectList::update_selection_mode() +{ + m_last_selected_item = wxDataViewItem(0); + + // All items are unselected + if (!GetSelection()) + { + m_selection_mode = smUndef; + return; + } + + const ItemType type = m_objects_model->GetItemType(GetSelection()); + m_selection_mode = type&itSettings ? smUndef : + type&itVolume ? smVolume : smInstance; +} + +// check last selected item. If is it possible to select it +bool ObjectList::check_last_selection() +{ + if (!m_last_selected_item) + return true; + + /* We can't mix Parts and Objects/Instances. + * So, unselect last selected item and show information about it + */ + const ItemType type = m_objects_model->GetItemType(m_last_selected_item); + if (type & itSettings || + type & itVolume && m_selection_mode == smInstance || + !(type & itVolume) && m_selection_mode == smVolume) + { + Unselect(m_last_selected_item); + + // Inform user why selection isn't complited + const wxString item_type = m_selection_mode == smInstance ? _(L("Object or Instance")) : _(L("Part")); + + const wxString msg_str = wxString::Format(_(L("You started your selection with %s Item.")) + "\n" + + _(L("In this mode you can select only another %s Items%s")), item_type, item_type, + m_selection_mode == smInstance ? "." : " " + _(L("of a current Object"))); + + wxMessageDialog dialog(this, _(L("Unsupported selection")) + "\n\n" + msg_str, + _(L("Info")), wxICON_INFORMATION); + dialog.ShowModal(); + + return false; + } + + return true; +} + void ObjectList::fix_multiselection_conflicts() { - if (GetSelectedItemsCount() <= 1) + if (GetSelectedItemsCount() <= 1) { + update_selection_mode(); + return; + } + + if (!check_last_selection()) return; m_prevent_list_events = true; @@ -2186,6 +2245,8 @@ void ObjectList::fix_multiselection_conflicts() wxDataViewItemArray sels; GetSelections(sels); + m_selection_mode = smInstance; + for (const auto item : sels) { if (!IsSelected(item)) // if this item is unselected now (from previous actions) continue; @@ -2205,6 +2266,26 @@ void ObjectList::fix_multiselection_conflicts() for (const auto unsel_item : unsels) Unselect(unsel_item); } + + if (m_objects_model->GetItemType(item) == itVolume) + { + // If some part is selected, unselect all items except of selected parts of the current object + sels.clear(); + + wxDataViewItemArray children; // selected volumes from current parent + m_objects_model->GetChildren(parent, children); + for (const auto child : children) + { + if (IsSelected(child) && m_objects_model->GetItemType(child)&itVolume) + sels.Add(child); + } + + UnselectAll(); + SetSelections(sels); + + m_selection_mode = smVolume; + break; + } } m_prevent_list_events = false; @@ -2489,8 +2570,7 @@ void ObjectList::show_multi_selection_menu() wxMenu* menu = new wxMenu(); #ifdef __WXOSX__ - append_menu_item(menu, wxID_ANY, _(L("Delete items")), "", - [this](wxCommandEvent&) { remove(); }, "", menu); + append_menu_item_delete(); #endif //__WXOSX__ if (extruders_count() > 1) diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index b0ed756f8..0611cc4f1 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -60,6 +60,12 @@ struct ItemForDelete class ObjectList : public wxDataViewCtrl { + enum SELECTION_MODE + { + smUndef, + smVolume, + smInstance + } m_selection_mode {smUndef}; struct dragged_item_data { @@ -135,6 +141,7 @@ class ObjectList : public wxDataViewCtrl bool m_part_settings_changed = false; int m_selected_row = 0; + wxDataViewItem m_last_selected_item {nullptr}; #if 0 FreqSettingsBundle m_freq_settings_fff; @@ -188,6 +195,7 @@ public: void append_menu_item_fix_through_netfabb(wxMenu* menu); void append_menu_item_export_stl(wxMenu* menu) const ; void append_menu_item_change_extruder(wxMenu* menu) const; + void append_menu_item_delete(wxMenu* menu); void create_object_popupmenu(wxMenu *menu); void create_sla_object_popupmenu(wxMenu*menu); void create_part_popupmenu(wxMenu*menu); @@ -258,6 +266,8 @@ public: void select_items(const wxDataViewItemArray& sels); void select_all(); void select_item_all_children(); + void update_selection_mode(); + bool check_last_selection(); // correct current selections to avoid of the possible conflicts void fix_multiselection_conflicts(); From fed0f189d3de2cf4439b7aea429b4422f076450c Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 3 Apr 2019 08:39:36 +0200 Subject: [PATCH 3/4] Fixed typo --- src/slic3r/GUI/GUI_ObjectList.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index b61b8280f..203e76573 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -2570,7 +2570,7 @@ void ObjectList::show_multi_selection_menu() wxMenu* menu = new wxMenu(); #ifdef __WXOSX__ - append_menu_item_delete(); + append_menu_item_delete(menu); #endif //__WXOSX__ if (extruders_count() > 1) From 09054a0bc9de788c646d35e0d5cd10c1b28c8abd Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 4 Apr 2019 15:07:54 +0200 Subject: [PATCH 4/4] Fixed non-consistency of the selection --- src/slic3r/GUI/GUI_ObjectList.cpp | 149 ++++++++++++++++++++---------- src/slic3r/GUI/GUI_ObjectList.hpp | 2 +- 2 files changed, 100 insertions(+), 51 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 203e76573..f495e168a 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -89,7 +89,24 @@ ObjectList::ObjectList(wxWindow* parent) : // manipulator cache with the following call to selection_changed() wxGetApp().obj_manipul()->emulate_kill_focus(); #endif // __APPLE__ - m_last_selected_item = event.GetItem(); + + /* For multiple selection with pressed SHIFT, + * event.GetItem() returns value of a first item in selection list + * instead of real last clicked item. + * So, let check last selected item in such strange way + */ + if (wxGetKeyState(WXK_SHIFT)) + { + wxDataViewItemArray sels; + GetSelections(sels); + if (sels.front() == m_last_selected_item) + m_last_selected_item = sels.back(); + else + m_last_selected_item = event.GetItem(); + } + else + m_last_selected_item = event.GetItem(); + selection_changed(); #ifndef __WXMSW__ set_tooltip_for_item(get_mouse_position_in_control()); @@ -511,7 +528,7 @@ void ObjectList::key_event(wxKeyEvent& event) printf("WXK_BACK\n"); remove(); } - else if (wxGetKeyState(wxKeyCode('A')) && wxGetKeyState(WXK_SHIFT)) + else if (wxGetKeyState(wxKeyCode('A')) && wxGetKeyState(WXK_CONTROL/*WXK_SHIFT*/)) select_item_all_children(); else event.Skip(); @@ -2183,11 +2200,10 @@ void ObjectList::select_item_all_children() // update selection mode for non-multiple selection void ObjectList::update_selection_mode() { - m_last_selected_item = wxDataViewItem(0); - // All items are unselected if (!GetSelection()) { + m_last_selected_item = wxDataViewItem(0); m_selection_mode = smUndef; return; } @@ -2198,33 +2214,55 @@ void ObjectList::update_selection_mode() } // check last selected item. If is it possible to select it -bool ObjectList::check_last_selection() +bool ObjectList::check_last_selection(wxString& msg_str) { if (!m_last_selected_item) return true; + + const bool is_shift_pressed = wxGetKeyState(WXK_SHIFT); /* We can't mix Parts and Objects/Instances. - * So, unselect last selected item and show information about it + * So, show information about it */ const ItemType type = m_objects_model->GetItemType(m_last_selected_item); - if (type & itSettings || + + // check a case of a selection of the Parts from different Objects + bool impossible_multipart_selection = false; + if (type & itVolume && m_selection_mode == smVolume) + { + wxDataViewItemArray sels; + GetSelections(sels); + for (const auto& sel: sels) + if (sel != m_last_selected_item && + m_objects_model->GetParent(sel) != m_objects_model->GetParent(m_last_selected_item)) + { + impossible_multipart_selection = true; + break; + } + } + + if (impossible_multipart_selection || + type & itSettings || type & itVolume && m_selection_mode == smInstance || !(type & itVolume) && m_selection_mode == smVolume) { - Unselect(m_last_selected_item); - // Inform user why selection isn't complited const wxString item_type = m_selection_mode == smInstance ? _(L("Object or Instance")) : _(L("Part")); - const wxString msg_str = wxString::Format(_(L("You started your selection with %s Item.")) + "\n" + - _(L("In this mode you can select only another %s Items%s")), item_type, item_type, - m_selection_mode == smInstance ? "." : " " + _(L("of a current Object"))); + msg_str = wxString::Format( _(L("Unsupported selection")) + "\n\n" + + _(L("You started your selection with %s Item.")) + "\n" + + _(L("In this mode you can select only other %s Items%s")), + item_type, item_type, + m_selection_mode == smInstance ? "." : + " " + _(L("of a current Object"))); - wxMessageDialog dialog(this, _(L("Unsupported selection")) + "\n\n" + msg_str, - _(L("Info")), wxICON_INFORMATION); - dialog.ShowModal(); - - return false; + // Unselect last selected item, if selection is without SHIFT + if (!is_shift_pressed) { + Unselect(m_last_selected_item); + show_info(this, msg_str, _(L("Info"))); + } + + return is_shift_pressed; } return true; @@ -2237,7 +2275,8 @@ void ObjectList::fix_multiselection_conflicts() return; } - if (!check_last_selection()) + wxString msg_string; + if (!check_last_selection(msg_string)) return; m_prevent_list_events = true; @@ -2245,49 +2284,59 @@ void ObjectList::fix_multiselection_conflicts() wxDataViewItemArray sels; GetSelections(sels); - m_selection_mode = smInstance; + if (m_selection_mode == smVolume) + { + // identify correct parent of the initial selected item + const wxDataViewItem& parent = m_objects_model->GetParent(m_last_selected_item == sels.front() ? sels.back() : sels.front()); - for (const auto item : sels) { - if (!IsSelected(item)) // if this item is unselected now (from previous actions) - continue; + sels.clear(); + wxDataViewItemArray children; // selected volumes from current parent + m_objects_model->GetChildren(parent, children); - if (m_objects_model->GetItemType(item) & itSettings) { - Unselect(item); - continue; - } + for (const auto child : children) + if (IsSelected(child) && m_objects_model->GetItemType(child)&itVolume) + sels.Add(child); - const wxDataViewItem& parent = m_objects_model->GetParent(item); - if (parent != wxDataViewItem(0) && IsSelected(parent)) - Unselect(parent); - else + // If some part is selected, unselect all items except of selected parts of the current object + UnselectAll(); + SetSelections(sels); + } + else + { + for (const auto item : sels) { - wxDataViewItemArray unsels; - m_objects_model->GetAllChildren(item, unsels); - for (const auto unsel_item : unsels) - Unselect(unsel_item); - } + if (!IsSelected(item)) // if this item is unselected now (from previous actions) + continue; - if (m_objects_model->GetItemType(item) == itVolume) - { - // If some part is selected, unselect all items except of selected parts of the current object - sels.clear(); - - wxDataViewItemArray children; // selected volumes from current parent - m_objects_model->GetChildren(parent, children); - for (const auto child : children) - { - if (IsSelected(child) && m_objects_model->GetItemType(child)&itVolume) - sels.Add(child); + if (m_objects_model->GetItemType(item) & itSettings) { + Unselect(item); + continue; } - UnselectAll(); - SetSelections(sels); + 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_selection_mode = smVolume; - break; + if (m_objects_model->GetItemType(item) & itVolume) + Unselect(item); + + m_selection_mode = smInstance; } } + if (!msg_string.IsEmpty()) + show_info(this, msg_string, _(L("Info"))); + + if (!IsSelected(m_last_selected_item)) + m_last_selected_item = wxDataViewItem(0); + m_prevent_list_events = false; } diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index 0611cc4f1..43d5dc059 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -267,7 +267,7 @@ public: void select_all(); void select_item_all_children(); void update_selection_mode(); - bool check_last_selection(); + bool check_last_selection(wxString& msg_str); // correct current selections to avoid of the possible conflicts void fix_multiselection_conflicts();