OrcaSlicer/src/slic3r/GUI/ObjectDataViewModel.cpp
lane.wei e9e4d75877 Update the codes to 01.01.00.10 for the formal release
1. first formal version of macos
2. add the bambu networking plugin install logic
3. auto compute the wipe volume when filament change
4. add the logic of wiping into support
5. refine the GUI layout and icons, improve the gui apperance in lots of
   small places
6. serveral improve to support
7. support AMS auto-mapping
8. disable lots of unstable features: such as params table, media file download, HMS
9. fix serveral kinds of bugs
10. update the document of building
11. ...
2022-07-22 20:35:34 +08:00

2215 lines
70 KiB
C++

#include "ObjectDataViewModel.hpp"
#include "wxExtensions.hpp"
#include "BitmapCache.hpp"
#include "GUI_App.hpp"
#include "GUI_Factories.hpp"
#include "GUI.hpp"
#include "I18N.hpp"
#include "PartPlate.hpp"
#include "libslic3r/Model.hpp"
#include <wx/bmpcbox.h>
#include <wx/dc.h>
namespace Slic3r {
namespace GUI {
wxDEFINE_EVENT(wxCUSTOMEVT_LAST_VOLUME_IS_DELETED, wxCommandEvent);
BitmapCache* m_bitmap_cache = nullptr;
// *****************************************************************************
// ----------------------------------------------------------------------------
// ObjectDataViewModelNode
// ----------------------------------------------------------------------------
void ObjectDataViewModelNode::init_container()
{
#ifdef __WXGTK__
// it's necessary on GTK because of control have to know if this item will be container
// in another case you couldn't to add subitem for this item
// it will be produce "segmentation fault"
m_container = true;
#endif //__WXGTK__
}
static constexpr char LayerRootIcon[] = "blank";
static constexpr char LayerIcon[] = "blank";
static constexpr char WarningIcon[] = "obj_warning";
static constexpr char WarningManifoldIcon[] = "obj_warning";
ObjectDataViewModelNode::ObjectDataViewModelNode(PartPlate* part_plate, wxString name) :
m_parent(nullptr),
m_name(name),
m_type(itPlate),
m_part_plate(part_plate),
m_extruder(wxEmptyString),
m_container(true)
{
m_plate_idx = part_plate ? part_plate->get_index() : -1;
set_action_icon(false);
// BBS
set_color_icon(false);
set_support_icon(false);
init_container();
}
struct InfoItemAtributes {
std::string name;
std::string bmp_name;
};
const std::map<InfoItemType, InfoItemAtributes> INFO_ITEMS{
// info_item Type info_item Name info_item BitmapName
{ InfoItemType::CustomSupports, {L("Support painting"), "toolbar_support" }, },
//{ InfoItemType::CustomSeam, {L("Paint-on seam"), "seam_" }, },
{ InfoItemType::MmuSegmentation, {L("Color painting"), "mmu_segmentation"}, },
//{ InfoItemType::Sinking, {L("Sinking"), "objlist_sinking"}, },
};
ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent,
const wxString& sub_obj_name,
Slic3r::ModelVolumeType type,
const wxBitmap& bmp,
const wxString& extruder,
const int idx/* = -1*/,
const std::string& warning_icon_name /*= std::string*/) :
m_parent(parent),
m_name(sub_obj_name),
m_type(itVolume),
m_volume_type(type),
m_idx(idx),
m_extruder(type == Slic3r::ModelVolumeType::MODEL_PART || type == Slic3r::ModelVolumeType::PARAMETER_MODIFIER ? extruder : ""),
m_warning_icon_name(warning_icon_name)
{
m_bmp = bmp;
set_icons();
init_container();
}
ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent, const InfoItemType info_type) :
m_parent(parent),
m_type(itInfo),
m_info_item_type(info_type),
m_extruder(wxEmptyString),
m_name(_(INFO_ITEMS.at(info_type).name))
{
}
ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent, const ItemType type, const int plate_idx) :
m_parent(parent),
m_type(type),
m_extruder(wxEmptyString),
m_plate_idx(plate_idx)
{
if (type == itSettings) {
m_name = "Settings to modified";
}
else if (type == itInstanceRoot) {
m_name = _(_devL("Instances"));
m_extruder = parent->m_extruder;
}
else if (type == itInstance)
{
m_idx = parent->GetChildCount();
m_name = wxString::Format(_(_devL("Instance %d")), m_idx + 1);
m_extruder = parent->GetParent()->m_extruder;
set_icons();
}
else if (type == itLayerRoot)
{
//BBS do not support layer range edit
m_bmp = create_scaled_bitmap(LayerRootIcon); // FIXME: pass window ptr
m_name = _(L("Layers"));
}
else if (type == itInfo)
assert(false);
if (type & (itInstanceRoot | itLayerRoot))
init_container();
}
ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent,
const t_layer_height_range& layer_range,
const int idx /*= -1 */,
const wxString extruder) :
m_parent(parent),
m_type(itLayer),
m_idx(idx),
m_layer_range(layer_range),
m_extruder(extruder)
{
const int children_cnt = parent->GetChildCount();
if (idx < 0)
m_idx = children_cnt;
else
{
// update indexes for another Laeyr Nodes
for (int i = m_idx; i < children_cnt; i++)
parent->GetNthChild(i)->SetIdx(i + 1);
}
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(LayerIcon); // FIXME: pass window ptr
set_icons();
init_container();
}
#ifndef NDEBUG
bool ObjectDataViewModelNode::valid()
{
// Verify that the object was not deleted yet.
assert(m_idx >= -1);
return m_idx >= -1;
}
#endif /* NDEBUG */
void ObjectDataViewModelNode::set_icons()
{
set_action_icon(false);
// BBS
set_color_icon(false);
set_support_icon(false);
// set extruder bitmap
set_extruder_icon();
}
void ObjectDataViewModelNode::set_extruder_icon()
{
if (m_type & (itInstance | itInstanceRoot | itLayerRoot) ||
((m_type & itVolume) && m_volume_type != Slic3r::ModelVolumeType::MODEL_PART && m_volume_type != Slic3r::ModelVolumeType::PARAMETER_MODIFIER))
return; // don't set colored bitmap for Instance
UpdateExtruderAndColorIcon();
}
void ObjectDataViewModelNode::set_printable_icon(PrintIndicator printable)
{
m_printable = printable;
m_printable_icon = m_printable == piUndef ? m_empty_bmp :
create_scaled_bitmap(m_printable == piPrintable ? "obj_printable" : "obj_unprintable");
}
void ObjectDataViewModelNode::set_action_icon(bool enable)
{
m_action_enable = enable;
auto undo = enable ? "undo" : "dot";
m_action_icon_name = m_type & itPlate ? "dot" :
m_type & itObject ? undo :
m_type & (itVolume | itLayer) ? undo : /*m_type & itInstance*/ "set_separate_obj";
m_action_icon = create_scaled_bitmap(m_action_icon_name); // FIXME: pass window ptr
}
// BBS
void ObjectDataViewModelNode::set_color_icon(bool enable)
{
m_color_enable = enable;
if ((m_type & itObject) && enable)
m_color_icon = create_scaled_bitmap("mmu_segmentation");
else
m_color_icon = create_scaled_bitmap("dot");
}
void ObjectDataViewModelNode::set_support_icon(bool enable)
{
m_support_enable = enable;
if ((m_type & itObject) && enable)
m_support_icon = create_scaled_bitmap("toolbar_support");
else
m_support_icon = create_scaled_bitmap("dot");
}
void ObjectDataViewModelNode::set_warning_icon(const std::string& warning_icon_name)
{
m_warning_icon_name = warning_icon_name;
if (warning_icon_name.empty())
m_bmp = m_empty_bmp;
}
void ObjectDataViewModelNode::update_settings_digest_bitmaps()
{
m_bmp = m_empty_bmp;
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<wxBitmap> bmps;
for (auto& category : m_opt_categories)
bmps.emplace_back(SettingsFactory::get_category_bitmap(category, false));
bmp = m_bitmap_cache->insert(scaled_bitmap_name, bmps);
}
m_bmp = *bmp;
}
bool ObjectDataViewModelNode::update_settings_digest(const std::vector<std::string>& categories)
{
if (m_type != itSettings || m_opt_categories == categories)
return false;
m_opt_categories = categories;
m_name = wxEmptyString;
for (auto& cat : m_opt_categories)
m_name += _(cat) + "; ";
if (!m_name.IsEmpty())
m_name.erase(m_name.Length()-2, 2); // Delete last "; "
update_settings_digest_bitmaps();
return true;
}
void ObjectDataViewModelNode::msw_rescale()
{
if (!m_action_icon_name.empty())
m_action_icon = create_scaled_bitmap(m_action_icon_name);
if (m_printable != piUndef)
m_printable_icon = create_scaled_bitmap(m_printable == piPrintable ? "obj_printable" : "obj_unprintable");
if (!m_opt_categories.empty())
update_settings_digest_bitmaps();
set_extruder_icon();
}
bool ObjectDataViewModelNode::SetValue(const wxVariant& variant, unsigned col)
{
switch (col)
{
case colPrint:
m_printable_icon << variant;
return true;
case colName: {
DataViewBitmapText data;
data << variant;
m_bmp = data.GetBitmap();
m_name = data.GetText();
return true; }
case colFilament: {
DataViewBitmapText data;
data << variant;
m_extruder_bmp = data.GetBitmap();
m_extruder = data.GetText() == "0" ? _(L("default")) : data.GetText();
return true; }
// BBS
case colSupportPaint:
m_support_icon << variant;
break;
case colColorPaint:
m_color_icon << variant;
break;
case colEditing:
m_action_icon << variant;
return true;
default:
printf("MyObjectTreeModel::SetValue: wrong column");
}
return false;
}
void ObjectDataViewModelNode::SetIdx(const int& idx)
{
m_idx = idx;
// update name if this node is instance
if (m_type == itInstance) {
if (m_plate_idx > 0) {
m_name = wxString::Format(_(_devL("[P%d]Instance %d")), m_plate_idx, m_idx + 1);
}
else {
m_name = wxString::Format(_(_devL("Instance %d")), m_idx + 1);
}
}
}
void ObjectDataViewModelNode::SetPlateIdx(const int& idx)
{
m_plate_idx = idx;
}
// BBS
bool ObjectDataViewModelNode::IsTimelapseWipeTower() const
{
if (m_model_object == nullptr)
return false;
if (!(m_type & itObject) && !(m_type & itInstance))
return false;
return m_model_object->is_timelapse_wipe_tower;
}
void ObjectDataViewModelNode::UpdateExtruderAndColorIcon(wxString extruder /*= ""*/)
{
if (m_type == itVolume && m_volume_type != ModelVolumeType::MODEL_PART && m_volume_type != ModelVolumeType::PARAMETER_MODIFIER)
return;
if (extruder.empty())
extruder = m_extruder;
else
m_extruder = extruder; // update extruder
// update color icon
size_t extruder_idx = atoi(extruder.c_str());
if (extruder_idx == 0) {
if (m_type & itObject);
else if (m_type & itVolume && m_volume_type == ModelVolumeType::MODEL_PART) {
extruder_idx = atoi(m_parent->GetExtruder().c_str());
}
else {
m_extruder_bmp = wxNullBitmap;
return;
}
}
if (extruder_idx > 0) --extruder_idx;
// Create the bitmap with color bars.
std::vector<wxBitmap*> bmps = get_extruder_color_icons(false);// use wide icons
if (bmps.empty()) {
m_extruder_bmp = wxNullBitmap;
return;
}
m_extruder_bmp = *bmps[extruder_idx >= bmps.size() ? 0 : extruder_idx];
}
// *****************************************************************************
// ----------------------------------------------------------------------------
// ObjectDataViewModel
// ----------------------------------------------------------------------------
static int get_root_idx(ObjectDataViewModelNode *parent_node, const ItemType root_type)
{
// because of istance_root and layers_root are at the end of the list, so
// start locking from the end
for (int root_idx = parent_node->GetChildCount() - 1; root_idx >= 0; root_idx--)
{
// if there is SettingsItem or VolumeItem, then RootItems don't exist in current ObjectItem
if (parent_node->GetNthChild(root_idx)->GetType() & (itSettings | itVolume))
break;
if (parent_node->GetNthChild(root_idx)->GetType() & root_type)
return root_idx;
}
return -1;
}
ObjectDataViewModel::ObjectDataViewModel()
{
m_bitmap_cache = new Slic3r::GUI::BitmapCache;
m_volume_bmps = MenuFactory::get_volume_bitmaps();
m_warning_bmp = create_scaled_bitmap(WarningIcon);
m_warning_manifold_bmp = create_scaled_bitmap(WarningManifoldIcon);
for (auto item : INFO_ITEMS)
m_info_bmps[item.first] = create_scaled_bitmap(item.second.bmp_name);
m_plate_outside = nullptr;
}
ObjectDataViewModel::~ObjectDataViewModel()
{
for (auto object : m_objects)
delete object;
delete m_bitmap_cache;
m_bitmap_cache = nullptr;
}
void ObjectDataViewModel::Init()
{
PartPlateList& list = wxGetApp().plater()->get_partplate_list();
if (list.get_plate_count() > 0)
AddPlate(list.get_plate(0));
AddOutsidePlate();
}
wxBitmap& ObjectDataViewModel::GetWarningBitmap(const std::string& warning_icon_name)
{
return warning_icon_name.empty() ? m_empty_bmp : warning_icon_name == WarningIcon ? m_warning_bmp : m_warning_manifold_bmp;
}
wxDataViewItem ObjectDataViewModel::AddPlate(PartPlate* part_plate, wxString name, bool refresh)
{
int plate_idx = part_plate ? part_plate->get_index() : -1;
wxString plate_name = name;
if (plate_name == "") {
plate_name = _L("Plate");
plate_name << " " << plate_idx + 1;
}
auto plate_node = new ObjectDataViewModelNode(part_plate, plate_name);
bool is_added = false;
if (plate_idx >= 0) {
for (auto iter = m_plates.begin(); iter != m_plates.end(); iter++) {
if (plate_idx < (*iter)->m_plate_idx || (*iter)->m_plate_idx < 0) {
m_plates.insert(iter, plate_node);
is_added = true;
break;
}
}
}
if (!is_added) {
m_plates.push_back(plate_node);
}
wxDataViewItem plate_item(plate_node);
if (refresh) {
ItemAdded(wxDataViewItem(nullptr), plate_item);
}
for (int obj_idx = 0; obj_idx < m_objects.size(); obj_idx++) {
auto obj_node = m_objects[obj_idx];
if (part_plate && part_plate->contain_instance_totally(obj_idx, 0)) {
ReparentObject(plate_node, obj_node);
}
}
return plate_item;
}
wxDataViewItem ObjectDataViewModel::AddOutsidePlate(bool refresh)
{
wxDataViewItem plate_item = AddPlate(nullptr, _L("Outside"));
m_plate_outside = (ObjectDataViewModelNode*)plate_item.GetID();
return plate_item;
}
wxDataViewItem ObjectDataViewModel::AddObject(ModelObject* model_object, std::string warning_bitmap, bool refresh)
{
// get object node params
wxString name = from_u8(model_object->name);
int extruder = model_object->config.has("extruder") ? model_object->config.extruder() : 1;
int plate_idx = -1;
ObjectDataViewModelNode* plate_node = nullptr;
for (auto plate : m_plates) {
if (plate->m_part_plate != nullptr &&
plate->m_part_plate->contain_instance_totally(model_object, 0))
{
plate_node = plate;
plate_idx = plate->m_part_plate->get_index();
break;
}
}
// create object node
//const wxString extruder_str = extruder == 0 ? _(L("default")) : wxString::Format("%d", extruder);
const wxString extruder_str = wxString::Format("%d", extruder);
auto obj_node = new ObjectDataViewModelNode(name, extruder_str, plate_idx, model_object);
obj_node->SetWarningBitmap(GetWarningBitmap(warning_bitmap), warning_bitmap);
if (plate_node != nullptr) {
obj_node->m_parent = plate_node;
}
else {
obj_node->m_parent = m_plate_outside;
plate_node = m_plate_outside;
}
m_objects.push_back(obj_node);
plate_node->GetChildren().push_back(obj_node);
// notify control
wxDataViewItem child((void*)obj_node);
wxDataViewItem parent((void*)plate_node);
if (refresh) {
ItemAdded(parent, child);
}
return child;
}
wxDataViewItem ObjectDataViewModel::AddVolumeChild( const wxDataViewItem &parent_item,
const wxString &name,
const Slic3r::ModelVolumeType volume_type,
const std::string& warning_icon_name/* = std::string()*/,
const int extruder/* = 0*/,
const bool create_frst_child/* = true*/)
{
ObjectDataViewModelNode *root = static_cast<ObjectDataViewModelNode*>(parent_item.GetID());
if (!root) return wxDataViewItem(0);
// get insertion position according to the existed Layers and/or Instances Items
int insert_position = get_root_idx(root, itLayerRoot);
if (insert_position < 0)
insert_position = get_root_idx(root, itInstanceRoot);
if (create_frst_child && root->m_volumes_cnt == 0)
{
const Slic3r::ModelVolumeType type = Slic3r::ModelVolumeType::MODEL_PART;
const auto node = new ObjectDataViewModelNode(root, root->m_name, type, GetVolumeIcon(type, root->m_warning_icon_name), root->m_extruder, 0, root->m_warning_icon_name);
insert_position < 0 ? root->Append(node) : root->Insert(node, insert_position);
// notify control
const wxDataViewItem child((void*)node);
ItemAdded(parent_item, child);
root->m_volumes_cnt++;
if (insert_position >= 0) insert_position++;
}
wxString extruder_str = extruder == 0 ? _(L("default")) : wxString::Format("%d", extruder);
const auto node = new ObjectDataViewModelNode(root, name, volume_type, GetVolumeIcon(volume_type, warning_icon_name),
extruder == 0 ? root->m_extruder : extruder_str, root->m_volumes_cnt, warning_icon_name);
insert_position < 0 ? root->Append(node) : root->Insert(node, insert_position);
// if part with errors is added, but object wasn't marked, then mark it
if (!warning_icon_name.empty() && warning_icon_name != root->m_warning_icon_name &&
(root->m_warning_icon_name.empty() || root->m_warning_icon_name == WarningManifoldIcon) )
root->SetWarningBitmap(GetWarningBitmap(warning_icon_name), warning_icon_name);
// notify control
const wxDataViewItem child((void*)node);
ItemAdded(parent_item, child);
root->m_volumes_cnt++;
return child;
}
wxDataViewItem ObjectDataViewModel::AddInfoChild(const wxDataViewItem &parent_item, InfoItemType info_type)
{
ObjectDataViewModelNode *root = static_cast<ObjectDataViewModelNode*>(parent_item.GetID());
if (!root) return wxDataViewItem(0);
const auto node = new ObjectDataViewModelNode(root, info_type);
// The new item should be added according to its order in InfoItemType.
// Find last info item with lower index and append after it.
const auto& children = root->GetChildren();
// If SettingsItem exists, it have to be on the first position always
bool is_settings_item = children.size() > 0 && children[0]->GetType() == itSettings;
int idx = is_settings_item ? 0 : -1;
for (size_t i = is_settings_item ? 1 : 0; i < children.size(); ++i) {
if (children[i]->GetType() == itInfo && int(children[i]->GetInfoItemType()) < int(info_type) )
idx = i;
}
root->Insert(node, idx+1);
node->SetBitmap(m_info_bmps.at(info_type));
// notify control
const wxDataViewItem child((void*)node);
ItemAdded(parent_item, child);
return child;
}
wxDataViewItem ObjectDataViewModel::AddSettingsChild(const wxDataViewItem &parent_item)
{
ObjectDataViewModelNode *root = static_cast<ObjectDataViewModelNode*>(parent_item.GetID());
if (!root) return wxDataViewItem(0);
const auto node = new ObjectDataViewModelNode(root, itSettings);
root->Insert(node, 0);
// notify control
const wxDataViewItem child((void*)node);
ItemAdded(parent_item, child);
return child;
}
/* return values:
* true => root_node is created and added to the parent_root
* false => root node alredy exists
*/
static bool append_root_node(ObjectDataViewModelNode *parent_node,
ObjectDataViewModelNode **root_node,
const ItemType root_type)
{
const int inst_root_id = get_root_idx(parent_node, root_type);
*root_node = inst_root_id < 0 ?
new ObjectDataViewModelNode(parent_node, root_type) :
parent_node->GetNthChild(inst_root_id);
if (inst_root_id < 0) {
if ((root_type&itInstanceRoot) ||
( (root_type&itLayerRoot) && get_root_idx(parent_node, itInstanceRoot)<0) )
parent_node->Append(*root_node);
else if (root_type&itLayerRoot)
parent_node->Insert(*root_node, static_cast<unsigned int>(get_root_idx(parent_node, itInstanceRoot)));
return true;
}
return false;
}
wxDataViewItem ObjectDataViewModel::AddRoot(const wxDataViewItem &parent_item, ItemType root_type)
{
ObjectDataViewModelNode *parent_node = static_cast<ObjectDataViewModelNode*>(parent_item.GetID());
if (!parent_node) return wxDataViewItem(0);
// get InstanceRoot node
ObjectDataViewModelNode *root_node { nullptr };
const bool appended = append_root_node(parent_node, &root_node, root_type);
if (!root_node) return wxDataViewItem(0);
const wxDataViewItem root_item((void*)root_node);
if (appended)
ItemAdded(parent_item, root_item);// notify control
return root_item;
}
wxDataViewItem ObjectDataViewModel::AddInstanceRoot(const wxDataViewItem &parent_item)
{
return AddRoot(parent_item, itInstanceRoot);
}
wxDataViewItem ObjectDataViewModel::AddInstanceChild(const wxDataViewItem &parent_item, size_t num)
{
std::vector<bool> print_indicator(num, true);
std::vector<int> plate_indicator(num, -1);
// if InstanceRoot item isn't created for this moment
if (!GetInstanceRootItem(parent_item).IsOk())
// use object's printable state to first instance
print_indicator[0] = IsPrintable(parent_item);
return wxDataViewItem((void*)AddInstanceChild(parent_item, print_indicator, plate_indicator));
}
wxDataViewItem ObjectDataViewModel::AddInstanceChild(const wxDataViewItem& parent_item,
const std::vector<bool>& print_indicator,
const std::vector<int>& plate_indicator)
{
const wxDataViewItem inst_root_item = AddInstanceRoot(parent_item);
if (!inst_root_item) return wxDataViewItem(0);
ObjectDataViewModelNode* inst_root_node = static_cast<ObjectDataViewModelNode*>(inst_root_item.GetID());
// Add instance nodes
ObjectDataViewModelNode *instance_node = nullptr;
size_t counter = 0;
while (counter < print_indicator.size()) {
instance_node = new ObjectDataViewModelNode(inst_root_node, itInstance, plate_indicator[counter]);
instance_node->set_printable_icon(print_indicator[counter] ? piPrintable : piUnprintable);
inst_root_node->Append(instance_node);
// notify control
const wxDataViewItem instance_item((void*)instance_node);
ItemAdded(inst_root_item, instance_item);
++counter;
}
// update object_node printable property
UpdateObjectPrintable(parent_item);
return wxDataViewItem((void*)instance_node);
}
void ObjectDataViewModel::UpdateObjectPrintable(wxDataViewItem parent_item)
{
const wxDataViewItem inst_root_item = GetInstanceRootItem(parent_item);
if (!inst_root_item)
return;
ObjectDataViewModelNode* inst_root_node = static_cast<ObjectDataViewModelNode*>(inst_root_item.GetID());
const size_t child_cnt = inst_root_node->GetChildren().Count();
PrintIndicator obj_pi = piUnprintable;
for (size_t i=0; i < child_cnt; i++)
if (inst_root_node->GetNthChild(i)->IsPrintable() & piPrintable) {
obj_pi = piPrintable;
break;
}
// and set printable state for object_node to piUndef
ObjectDataViewModelNode* obj_node = static_cast<ObjectDataViewModelNode*>(parent_item.GetID());
obj_node->set_printable_icon(obj_pi);
ItemChanged(parent_item);
}
// update printable property for all instances from object
void ObjectDataViewModel::UpdateInstancesPrintable(wxDataViewItem parent_item)
{
const wxDataViewItem inst_root_item = GetInstanceRootItem(parent_item);
if (!inst_root_item)
return;
ObjectDataViewModelNode* obj_node = static_cast<ObjectDataViewModelNode*>(parent_item.GetID());
const PrintIndicator obj_pi = obj_node->IsPrintable();
ObjectDataViewModelNode* inst_root_node = static_cast<ObjectDataViewModelNode*>(inst_root_item.GetID());
const size_t child_cnt = inst_root_node->GetChildren().Count();
for (size_t i=0; i < child_cnt; i++)
{
ObjectDataViewModelNode* inst_node = inst_root_node->GetNthChild(i);
// and set printable state for object_node to piUndef
inst_node->set_printable_icon(obj_pi);
ItemChanged(wxDataViewItem((void*)inst_node));
}
}
bool ObjectDataViewModel::IsPrintable(const wxDataViewItem& item) const
{
ObjectDataViewModelNode* node = static_cast<ObjectDataViewModelNode*>(item.GetID());
if (!node)
return false;
return node->IsPrintable() == piPrintable;
}
wxDataViewItem ObjectDataViewModel::AddLayersRoot(const wxDataViewItem &parent_item)
{
return AddRoot(parent_item, itLayerRoot);
}
wxDataViewItem ObjectDataViewModel::AddLayersChild(const wxDataViewItem &parent_item,
const t_layer_height_range& layer_range,
const int extruder/* = 1*/,
const int index /* = -1*/)
{
ObjectDataViewModelNode *parent_node = static_cast<ObjectDataViewModelNode*>(parent_item.GetID());
if (!parent_node) return wxDataViewItem(0);
// BBS
wxString extruder_str = extruder == 0 ? _(L("default")) : wxString::Format("%d", extruder);
// get LayerRoot node
ObjectDataViewModelNode *layer_root_node;
wxDataViewItem layer_root_item;
if (parent_node->GetType() & itLayerRoot) {
layer_root_node = parent_node;
layer_root_item = parent_item;
}
else {
const int root_idx = get_root_idx(parent_node, itLayerRoot);
if (root_idx < 0) return wxDataViewItem(0);
layer_root_node = parent_node->GetNthChild(root_idx);
layer_root_item = wxDataViewItem((void*)layer_root_node);
}
// Add layer node
ObjectDataViewModelNode *layer_node = new ObjectDataViewModelNode(layer_root_node, layer_range, index, extruder_str);
if (index < 0)
layer_root_node->Append(layer_node);
else
layer_root_node->Insert(layer_node, index);
// notify control
const wxDataViewItem layer_item((void*)layer_node);
ItemAdded(layer_root_item, layer_item);
return layer_item;
}
size_t ObjectDataViewModel::GetItemIndexForFirstVolume(ObjectDataViewModelNode* node_parent)
{
assert(node_parent->m_volumes_cnt > 0);
for (size_t vol_idx = 0; vol_idx < node_parent->GetChildCount(); vol_idx++)
if (node_parent->GetNthChild(vol_idx)->GetType() == itVolume)
return vol_idx;
return -1;
}
wxDataViewItem ObjectDataViewModel::DeletePlate(const int plate_idx)
{
wxDataViewItem ret_item(nullptr);
for (auto plate : m_plates) {
if (plate->m_plate_idx == plate_idx) {
ret_item = Delete(wxDataViewItem(plate));
}
}
return ret_item;
}
wxDataViewItem ObjectDataViewModel::Delete(const wxDataViewItem &item)
{
auto ret_item = wxDataViewItem(0);
ObjectDataViewModelNode *node = static_cast<ObjectDataViewModelNode*>(item.GetID());
if (!node) // happens if item.IsOk()==false
return ret_item;
auto node_parent = node->GetParent();
wxDataViewItem parent(node_parent);
if (node->m_type & itPlate) {
for (auto child : node->GetChildren()) {
assert((child->GetType() & itObject) != 0);
if (child->m_plate_idx == node->m_plate_idx)
ReparentObject(m_plate_outside, child);
}
// FIX: should remove from children collection soon, or will conflict GetChildren
m_plates.erase(std::remove(m_plates.begin(), m_plates.end(), node), m_plates.end());
ItemDeleted(parent, wxDataViewItem(node));
delete node;
return parent;
}
if (node->m_type & itObject) {
auto it = find(m_objects.begin(), m_objects.end(), node);
if (it != m_objects.end())
{
// Delete all sub-items
int i = (*it)->GetChildCount() - 1;
while (i >= 0) {
Delete(wxDataViewItem((*it)->GetNthChild(i)));
i = (*it)->GetChildCount() - 1;
}
m_objects.erase(it);
node_parent->GetChildren().Remove(node);
}
ItemDeleted(parent, wxDataViewItem(node));
delete node;
return parent;
}
// first remove the node from the parent's array of children;
// NOTE: MyObjectTreeModelNodePtrArray is only an array of _pointers_
// thus removing the node from it doesn't result in freeing it
if (node->m_type & (itInstanceRoot | itLayerRoot))
{
// node can be deleted by the Delete, let's check its type while we safely can
bool is_instance_root = (node->m_type & itInstanceRoot);
for (int i = int(node->GetChildCount() - 1); i >= (is_instance_root ? 1 : 0); i--)
Delete(wxDataViewItem(node->GetNthChild(i)));
return parent;
}
auto id = node_parent->GetChildren().Index(node);
auto idx = node->GetIdx();
if (node->m_type & (itVolume | itLayer)) {
node_parent->m_volumes_cnt--;
DeleteSettings(item);
}
node_parent->GetChildren().Remove(node);
if (id > 0) {
if (size_t(id) == node_parent->GetChildCount()) id--;
ret_item = wxDataViewItem(node_parent->GetChildren().Item(id));
}
//update idx value for remaining child-nodes
auto children = node_parent->GetChildren();
for (size_t i = 0; i < node_parent->GetChildCount() && idx >= 0; i++)
{
auto cur_idx = children[i]->GetIdx();
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);
ObjectDataViewModelNode* last_instance_node = node_parent->GetNthChild(0);
PrintIndicator last_instance_printable = last_instance_node->IsPrintable();
node_parent->GetChildren().Remove(last_instance_node);
delete last_instance_node;
ItemDeleted(parent, wxDataViewItem(last_instance_node));
ObjectDataViewModelNode* obj_node = node_parent->GetParent();
obj_node->set_printable_icon(last_instance_printable);
obj_node->GetChildren().Remove(node_parent);
delete node_parent;
ret_item = wxDataViewItem(obj_node);
#ifndef __WXGTK__
if (obj_node->GetChildCount() == 0)
obj_node->m_container = false;
#endif //__WXGTK__
ItemDeleted(ret_item, wxDataViewItem(node_parent));
return ret_item;
}
if (node->m_type & itInstance)
UpdateObjectPrintable(wxDataViewItem(node_parent->GetParent()));
// if there was last layer item, delete this one and layers root item
if (node_parent->GetChildCount() == 0 && node_parent->m_type == itLayerRoot)
{
ObjectDataViewModelNode* obj_node = node_parent->GetParent();
obj_node->GetChildren().Remove(node_parent);
delete node_parent;
ret_item = wxDataViewItem(obj_node);
#ifndef __WXGTK__
if (obj_node->GetChildCount() == 0)
obj_node->m_container = false;
#endif //__WXGTK__
ItemDeleted(ret_item, wxDataViewItem(node_parent));
return ret_item;
}
// if there is last volume item after deleting, delete this last volume too
if (node_parent->GetChildCount() <= 3) // 3??? #ys_FIXME
{
int vol_cnt = 0;
int vol_idx = 0;
for (size_t i = 0; i < node_parent->GetChildCount(); ++i) {
if (node_parent->GetNthChild(i)->GetType() == itVolume) {
vol_idx = i;
vol_cnt++;
}
if (vol_cnt > 1)
break;
}
if (vol_cnt == 1) {
delete node;
ItemDeleted(parent, item);
ObjectDataViewModelNode* last_child_node = node_parent->GetNthChild(vol_idx);
DeleteSettings(wxDataViewItem(last_child_node));
node_parent->GetChildren().Remove(last_child_node);
node_parent->m_volumes_cnt = 0;
delete last_child_node;
#ifndef __WXGTK__
if (node_parent->GetChildCount() == 0)
node_parent->m_container = false;
#endif //__WXGTK__
ItemDeleted(parent, wxDataViewItem(last_child_node));
// BBS: Current object is already removed in model.objects,
// but it is still exists in m_objects at the moment.
// So the object index calculated here is not valid.
#if 0
wxCommandEvent event(wxCUSTOMEVT_LAST_VOLUME_IS_DELETED);
auto it = find(m_objects.begin(), m_objects.end(), node_parent);
event.SetInt(it == m_objects.end() ? -1 : it - m_objects.begin());
wxPostEvent(m_ctrl, event);
#endif
ret_item = parent;
return ret_item;
}
}
// free the node
delete node;
// set m_containet to FALSE if parent has no child
#ifndef __WXGTK__
if (node_parent->GetChildCount() == 0)
node_parent->m_container = false;
#endif //__WXGTK__
ret_item = parent;
// notify control
ItemDeleted(parent, item);
return ret_item;
}
wxDataViewItem ObjectDataViewModel::DeleteLastInstance(const wxDataViewItem &parent_item, size_t num)
{
auto ret_item = wxDataViewItem(0);
ObjectDataViewModelNode *parent_node = static_cast<ObjectDataViewModelNode*>(parent_item.GetID());
if (!parent_node) return ret_item;
const int inst_root_id = get_root_idx(parent_node, itInstanceRoot);
if (inst_root_id < 0) return ret_item;
wxDataViewItemArray items;
ObjectDataViewModelNode *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;
PrintIndicator last_inst_printable = piUndef;
int stop = delete_inst_root_item ? 0 : inst_cnt - num;
for (int i = inst_cnt - 1; i >= stop;--i) {
ObjectDataViewModelNode *last_instance_node = inst_root_node->GetNthChild(i);
if (i==0) last_inst_printable = last_instance_node->IsPrintable();
inst_root_node->GetChildren().Remove(last_instance_node);
delete last_instance_node;
ItemDeleted(inst_root_item, wxDataViewItem(last_instance_node));
}
if (delete_inst_root_item) {
ret_item = parent_item;
parent_node->GetChildren().Remove(inst_root_node);
parent_node->set_printable_icon(last_inst_printable);
ItemDeleted(parent_item, inst_root_item);
ItemChanged(parent_item);
#ifndef __WXGTK__
if (parent_node->GetChildCount() == 0)
parent_node->m_container = false;
#endif //__WXGTK__
}
// update object_node printable property
UpdateObjectPrintable(parent_item);
return ret_item;
}
void ObjectDataViewModel::ResetAll()
{
// BBS
// FIX: should remove from children collection soon, or will conflict GetChildren
while (!m_plates.empty()) {
auto plate = m_plates.front();
Delete(wxDataViewItem(plate));
}
m_plates.clear();
m_plate_outside = nullptr;
m_objects.clear();
AddOutsidePlate();
}
void ObjectDataViewModel::DeleteChildren(wxDataViewItem& parent)
{
ObjectDataViewModelNode *root = static_cast<ObjectDataViewModelNode*>(parent.GetID());
if (!root) // happens if item.IsOk()==false
return;
// first remove the node from the parent's array of children;
// NOTE: MyObjectTreeModelNodePtrArray is only an array of _pointers_
// thus removing the node from it doesn't result in freeing it
auto& children = root->GetChildren();
for (int id = root->GetChildCount() - 1; id >= 0; --id)
{
auto node = children[id];
auto item = wxDataViewItem(node);
children.RemoveAt(id);
if (node->m_type == itVolume)
root->m_volumes_cnt--;
// free the node
delete node;
// notify control
ItemDeleted(parent, item);
}
// set m_containet to FALSE if parent has no child
#ifndef __WXGTK__
root->m_container = false;
#endif //__WXGTK__
}
void ObjectDataViewModel::DeleteVolumeChildren(wxDataViewItem& parent)
{
ObjectDataViewModelNode *root = static_cast<ObjectDataViewModelNode*>(parent.GetID());
if (!root) // happens if item.IsOk()==false
return;
// first remove the node from the parent's array of children;
// NOTE: MyObjectTreeModelNodePtrArray is only an array of _pointers_
// thus removing the node from it doesn't result in freeing it
auto& children = root->GetChildren();
for (int id = root->GetChildCount() - 1; id >= 0; --id)
{
auto node = children[id];
if (node->m_type != itVolume)
continue;
auto item = wxDataViewItem(node);
DeleteSettings(item);
children.RemoveAt(id);
// free the node
delete node;
// notify control
ItemDeleted(parent, item);
}
root->m_volumes_cnt = 0;
// set m_containet to FALSE if parent has no child
#ifndef __WXGTK__
root->m_container = false;
#endif //__WXGTK__
}
void ObjectDataViewModel::DeleteSettings(const wxDataViewItem& parent)
{
ObjectDataViewModelNode *node = static_cast<ObjectDataViewModelNode*>(parent.GetID());
if (!node) return;
#if NEW_OBJECT_SETTING
node->set_action_icon(false);
ItemChanged(parent);
#else
if volume has a "settings"item, than delete it before volume deleting
if (node->GetChildCount() > 0 && node->GetNthChild(0)->GetType() == itSettings) {
auto settings_node = node->GetNthChild(0);
auto settings_item = wxDataViewItem(settings_node);
node->GetChildren().RemoveAt(0);
delete settings_node;
// BBS
if (node->GetChildCount() == 0) {
node->m_container = false;
}
ItemDeleted(parent, settings_item);
}
#endif
}
wxDataViewItem ObjectDataViewModel::GetItemById(int obj_idx)
{
if (size_t(obj_idx) >= m_objects.size())
{
printf("Error! Out of objects range.\n");
return wxDataViewItem(0);
}
return wxDataViewItem(m_objects[obj_idx]);
}
wxDataViewItem ObjectDataViewModel::GetItemByPlateId(int plate_idx)
{
for (auto plate : m_plates) {
if (plate->m_plate_idx == plate_idx)
return wxDataViewItem(plate);
}
return wxDataViewItem(nullptr);
}
wxDataViewItem ObjectDataViewModel::GetItemByVolumeId(int obj_idx, int volume_idx)
{
if (size_t(obj_idx) >= m_objects.size()) {
printf("Error! Out of objects range.\n");
return wxDataViewItem(0);
}
auto parent = m_objects[obj_idx];
if (parent->GetChildCount() == 0 ||
(parent->GetChildCount() == 1 && parent->GetNthChild(0)->GetType() & itSettings )) {
if (volume_idx == 0)
return GetItemById(obj_idx);
printf("Error! Object has no one volume.\n");
return wxDataViewItem(0);
}
for (size_t i = 0; i < parent->GetChildCount(); i++) {
auto child = parent->GetNthChild(i);
if (child->m_idx == volume_idx && child->GetType() & itVolume)
return wxDataViewItem(parent->GetNthChild(i));
}
return wxDataViewItem(0);
}
wxDataViewItem ObjectDataViewModel::GetItemById(const int obj_idx, const int sub_obj_idx, const ItemType parent_type)
{
if (size_t(obj_idx) >= m_objects.size()) {
printf("Error! Out of objects range.\n");
return wxDataViewItem(0);
}
auto item = GetItemByType(wxDataViewItem(m_objects[obj_idx]), parent_type);
if (!item)
return wxDataViewItem(0);
auto parent = static_cast<ObjectDataViewModelNode*>(item.GetID());
for (size_t i = 0; i < parent->GetChildCount(); i++)
if (parent->GetNthChild(i)->m_idx == sub_obj_idx)
return wxDataViewItem(parent->GetNthChild(i));
return wxDataViewItem(0);
}
wxDataViewItem ObjectDataViewModel::GetItemByInstanceId(int obj_idx, int inst_idx)
{
return GetItemById(obj_idx, inst_idx, itInstanceRoot);
}
wxDataViewItem ObjectDataViewModel::GetItemByLayerId(int obj_idx, int layer_idx)
{
return GetItemById(obj_idx, layer_idx, itLayerRoot);
}
wxDataViewItem ObjectDataViewModel::GetItemByLayerRange(const int obj_idx, const t_layer_height_range& layer_range)
{
if (size_t(obj_idx) >= m_objects.size()) {
printf("Error! Out of objects range.\n");
return wxDataViewItem(0);
}
auto item = GetItemByType(wxDataViewItem(m_objects[obj_idx]), itLayerRoot);
if (!item)
return wxDataViewItem(0);
auto parent = static_cast<ObjectDataViewModelNode*>(item.GetID());
for (size_t i = 0; i < parent->GetChildCount(); i++)
if (parent->GetNthChild(i)->m_layer_range == layer_range)
return wxDataViewItem(parent->GetNthChild(i));
return wxDataViewItem(0);
}
int ObjectDataViewModel::GetItemIdByLayerRange(const int obj_idx, const t_layer_height_range& layer_range)
{
wxDataViewItem item = GetItemByLayerRange(obj_idx, layer_range);
if (!item)
return -1;
return GetLayerIdByItem(item);
}
int ObjectDataViewModel::GetIdByItem(const wxDataViewItem& item) const
{
if(!item.IsOk())
return -1;
ObjectDataViewModelNode *node = static_cast<ObjectDataViewModelNode*>(item.GetID());
auto it = find(m_objects.begin(), m_objects.end(), node);
if (it == m_objects.end())
return -1;
return it - m_objects.begin();
}
int ObjectDataViewModel::GetIdByItemAndType(const wxDataViewItem& item, const ItemType type) const
{
wxASSERT(item.IsOk());
ObjectDataViewModelNode *node = static_cast<ObjectDataViewModelNode*>(item.GetID());
if (!node || node->m_type != type)
return -1;
return node->GetIdx();
}
int ObjectDataViewModel::GetObjectIdByItem(const wxDataViewItem& item) const
{
return GetIdByItem(GetObject(item));
}
int ObjectDataViewModel::GetVolumeIdByItem(const wxDataViewItem& item) const
{
return GetIdByItemAndType(item, itVolume);
}
int ObjectDataViewModel::GetInstanceIdByItem(const wxDataViewItem& item) const
{
return GetIdByItemAndType(item, itInstance);
}
int ObjectDataViewModel::GetLayerIdByItem(const wxDataViewItem& item) const
{
return GetIdByItemAndType(item, itLayer);
}
t_layer_height_range ObjectDataViewModel::GetLayerRangeByItem(const wxDataViewItem& item) const
{
wxASSERT(item.IsOk());
ObjectDataViewModelNode *node = static_cast<ObjectDataViewModelNode*>(item.GetID());
if (!node || node->m_type != itLayer)
return { 0.0f, 0.0f };
return node->GetLayerRange();
}
bool ObjectDataViewModel::UpdateColumValues(unsigned col)
{
switch (col)
{
case colPrint:
case colName:
case colEditing:
return true;
case colFilament:
{
wxDataViewItemArray items;
GetAllChildren(wxDataViewItem(nullptr), items);
if (items.IsEmpty()) return false;
for (auto item : items)
UpdateExtruderBitmap(item);
return true;
}
default:
printf("MyObjectTreeModel::SetValue: wrong column");
}
return false;
}
void ObjectDataViewModel::UpdateExtruderBitmap(wxDataViewItem item)
{
if (!item.IsOk())
return;
ObjectDataViewModelNode* node = static_cast<ObjectDataViewModelNode*>(item.GetID());
node->UpdateExtruderAndColorIcon();
}
void ObjectDataViewModel::UpdateItemNames()
{
wxDataViewItemArray changed_items;
for (auto obj_node : m_objects) {
ModelObject* mo = obj_node->m_model_object;
wxString new_obj_name = from_u8(mo->name);
if (obj_node->m_name != new_obj_name) {
obj_node->m_name = new_obj_name;
changed_items.push_back(wxDataViewItem(obj_node));
}
for (auto child_node : obj_node->GetChildren()) {
if ((child_node->GetType() & itVolume) == 0)
continue;
assert(child_node->GetIdx() < mo->volumes.size());
ModelVolume* mv = mo->volumes[child_node->GetIdx()];
wxString new_vol_name = from_u8(mv->name);
if (child_node->m_name != new_vol_name) {
child_node->m_name = new_vol_name;
changed_items.push_back(wxDataViewItem(child_node));
}
}
}
ItemsChanged(changed_items);
}
// BBS: add use_obj_extruder
void ObjectDataViewModel::UpdateVolumesExtruderBitmap(wxDataViewItem obj_item, bool use_obj_extruder)
{
if (!obj_item.IsOk() || GetItemType(obj_item) != itObject)
return;
ObjectDataViewModelNode* obj_node = static_cast<ObjectDataViewModelNode*>(obj_item.GetID());
for (auto child : obj_node->GetChildren())
if (child->GetVolumeType() == ModelVolumeType::MODEL_PART)
child->UpdateExtruderAndColorIcon(use_obj_extruder ? obj_node->GetExtruder() : "");
}
int ObjectDataViewModel::GetDefaultExtruderIdx(wxDataViewItem item)
{
ItemType type = GetItemType(item);
if (type == itObject)
return 0;
if (type == itVolume && GetVolumeType(item) == ModelVolumeType::MODEL_PART) {
wxDataViewItem obj_item = GetParent(item);
int extruder_id = GetExtruderNumber(obj_item);
if (extruder_id > 0) extruder_id--;
return extruder_id;
}
return -1;
}
void ObjectDataViewModel::GetItemInfo(const wxDataViewItem& item, ItemType& type, int& obj_idx, int& idx)
{
wxASSERT(item.IsOk());
type = itUndef;
ObjectDataViewModelNode *node = static_cast<ObjectDataViewModelNode*>(item.GetID());
if (!node ||
node->GetIdx() <-1 ||
( node->GetIdx() == -1 &&
!(node->GetType() & (itObject | itSettings | itInstanceRoot | itLayerRoot | itInfo))
)
)
return;
idx = node->GetIdx();
type = node->GetType();
ObjectDataViewModelNode *parent_node = node->GetParent();
if (!parent_node) return;
// get top parent (Object) node
while (parent_node->m_type != itObject)
parent_node = parent_node->GetParent();
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;
}
int ObjectDataViewModel::GetRowByItem(const wxDataViewItem& item) const
{
if (m_objects.empty())
return -1;
int row_num = 0;
for (size_t i = 0; i < m_objects.size(); i++)
{
row_num++;
if (item == wxDataViewItem(m_objects[i]))
return row_num;
for (size_t j = 0; j < m_objects[i]->GetChildCount(); j++)
{
row_num++;
ObjectDataViewModelNode* cur_node = m_objects[i]->GetNthChild(j);
if (item == wxDataViewItem(cur_node))
return row_num;
if (cur_node->m_type == itVolume && cur_node->GetChildCount() == 1)
row_num++;
if (cur_node->m_type == itInstanceRoot)
{
row_num++;
for (size_t t = 0; t < cur_node->GetChildCount(); t++)
{
row_num++;
if (item == wxDataViewItem(cur_node->GetNthChild(t)))
return row_num;
}
}
}
}
return -1;
}
bool ObjectDataViewModel::InvalidItem(const wxDataViewItem& item)
{
if (!item)
return true;
ObjectDataViewModelNode* node = static_cast<ObjectDataViewModelNode*>(item.GetID());
if (!node || node->invalid())
return true;
return false;
}
wxString ObjectDataViewModel::GetName(const wxDataViewItem &item) const
{
ObjectDataViewModelNode *node = static_cast<ObjectDataViewModelNode*>(item.GetID());
if (!node) // happens if item.IsOk()==false
return wxEmptyString;
return node->m_name;
}
wxBitmap& ObjectDataViewModel::GetBitmap(const wxDataViewItem &item) const
{
ObjectDataViewModelNode *node = static_cast<ObjectDataViewModelNode*>(item.GetID());
return node->m_bmp;
}
wxString ObjectDataViewModel::GetExtruder(const wxDataViewItem& item) const
{
ObjectDataViewModelNode *node = static_cast<ObjectDataViewModelNode*>(item.GetID());
if (!node) // happens if item.IsOk()==false
return wxEmptyString;
return node->m_extruder;
}
int ObjectDataViewModel::GetExtruderNumber(const wxDataViewItem& item) const
{
ObjectDataViewModelNode *node = static_cast<ObjectDataViewModelNode*>(item.GetID());
if (!node) // happens if item.IsOk()==false
return 0;
return atoi(node->m_extruder.c_str());
}
wxString ObjectDataViewModel::GetColumnType(unsigned int col) const
{
if (col == colName || col == colFilament)
return wxT("DataViewBitmapText");
// BBS
if (/*col == colPrint || */col == colEditing)
return wxT("DataViewBitmap");
return wxT("string");
}
void ObjectDataViewModel::GetValue(wxVariant &variant, const wxDataViewItem &item, unsigned int col) const
{
wxASSERT(item.IsOk());
ObjectDataViewModelNode *node = static_cast<ObjectDataViewModelNode*>(item.GetID());
switch (col)
{
case colPrint:
variant << node->m_printable_icon;
break;
case colName:
variant << DataViewBitmapText(node->m_name, node->m_bmp);
break;
case colFilament:
variant << DataViewBitmapText(node->m_extruder, node->m_extruder_bmp);
break;
// BBS
case colSupportPaint:
variant << node->m_support_icon;
break;
case colColorPaint:
variant << node->m_color_icon;
break;
case colEditing:
variant << node->m_action_icon;
break;
default:
;
}
}
bool ObjectDataViewModel::SetValue(const wxVariant &variant, const wxDataViewItem &item, unsigned int col)
{
wxASSERT(item.IsOk());
ObjectDataViewModelNode *node = static_cast<ObjectDataViewModelNode*>(item.GetID());
return node->SetValue(variant, col);
}
bool ObjectDataViewModel::SetValue(const wxVariant &variant, const int item_idx, unsigned int col)
{
if (size_t(item_idx) >= m_objects.size())
return false;
return m_objects[item_idx]->SetValue(variant, col);
}
void ObjectDataViewModel::SetExtruder(const wxString& extruder, wxDataViewItem item)
{
if (!item.IsOk())
return;
// BBS. Don't support to set extruder for plate.
ObjectDataViewModelNode* node = (ObjectDataViewModelNode*)item.GetID();
if (node->GetType() & itPlate) {
return;
}
node->UpdateExtruderAndColorIcon(extruder);
if (node->GetType() == itObject)
UpdateVolumesExtruderBitmap(item, true);
// BBS
ItemChanged(item);
}
bool ObjectDataViewModel::SetName(const wxString& new_name, wxDataViewItem item)
{
if (!item.IsOk())
return false;
// The icon can't be edited so get its old value and reuse it.
wxVariant valueOld;
GetValue(valueOld, item, colName);
DataViewBitmapText bmpText;
bmpText << valueOld;
// But replace the text with the value entered by user.
bmpText.SetText(new_name);
wxVariant value;
value << bmpText;
if (SetValue(value, item, colName)) {
ItemChanged(item);
return true;
}
return false;
}
void ObjectDataViewModel::OnPlateChange(const int plate_idx, wxDataViewItem item)
{
ObjectDataViewModelNode* node = static_cast<ObjectDataViewModelNode*>(item.GetID());
ObjectDataViewModelNode* parent_plate = m_plate_outside;
for (auto plate : m_plates) {
if (plate->m_plate_idx == plate_idx) {
parent_plate = plate;
}
}
ReparentObject(parent_plate, node);
}
void ObjectDataViewModel::AddAllChildren(const wxDataViewItem& parent)
{
ObjectDataViewModelNode* node = static_cast<ObjectDataViewModelNode*>(parent.GetID());
if (!node || node->GetChildCount() == 0)
return;
wxDataViewItemArray array;
const size_t count = node->GetChildCount();
for (size_t pos = 0; pos < count; pos++) {
ObjectDataViewModelNode* child = node->GetChildren().Item(pos);
array.Add(wxDataViewItem((void*)child));
ItemAdded(parent, wxDataViewItem((void*)child));
}
for (const auto& item : array)
AddAllChildren(item);
m_ctrl->Expand(parent);
};
void ObjectDataViewModel::ReparentObject(ObjectDataViewModelNode* plate, ObjectDataViewModelNode* object)
{
assert(plate != nullptr && (plate->m_type & itPlate) != 0);
assert(object != nullptr && (object->m_type & itObject) != 0);
ObjectDataViewModelNode* old_plate = object->m_parent;
assert(old_plate != nullptr && (old_plate->m_type & itPlate) != 0);
if (old_plate == plate)
return;
old_plate->GetChildren().Remove(object);
ItemDeleted(wxDataViewItem(old_plate), wxDataViewItem(object));
object->m_plate_idx = plate->m_plate_idx;
object->m_parent = plate;
plate->Append(object);
ItemAdded(wxDataViewItem(plate), wxDataViewItem(object));
}
wxDataViewItem ObjectDataViewModel::ReorganizeChildren( const int current_volume_id,
const int new_volume_id,
const wxDataViewItem &parent)
{
auto ret_item = wxDataViewItem(0);
if (current_volume_id == new_volume_id)
return ret_item;
wxASSERT(parent.IsOk());
ObjectDataViewModelNode *node_parent = static_cast<ObjectDataViewModelNode*>(parent.GetID());
if (!node_parent) // happens if item.IsOk()==false
return ret_item;
size_t shift = GetItemIndexForFirstVolume(node_parent);
ObjectDataViewModelNode *deleted_node = node_parent->GetNthChild(current_volume_id+shift);
node_parent->GetChildren().Remove(deleted_node);
ItemDeleted(parent, wxDataViewItem(deleted_node));
node_parent->Insert(deleted_node, new_volume_id+shift);
ItemAdded(parent, wxDataViewItem(deleted_node));
// If some item has a children, just to add a deleted item is not enough on Linux
// We should to add all its children separately
AddAllChildren(wxDataViewItem(deleted_node));
//update volume_id value for child-nodes
auto children = node_parent->GetChildren();
int id_frst = current_volume_id < new_volume_id ? current_volume_id : new_volume_id;
int id_last = current_volume_id > new_volume_id ? current_volume_id : new_volume_id;
for (int id = id_frst; id <= id_last; ++id)
children[id+shift]->SetIdx(id);
return wxDataViewItem(node_parent->GetNthChild(new_volume_id+shift));
}
wxDataViewItem ObjectDataViewModel::ReorganizeObjects( const int current_id, const int new_id)
{
if (current_id == new_id)
return wxDataViewItem(nullptr);
ObjectDataViewModelNode* deleted_node = m_objects[current_id];
m_objects.erase(m_objects.begin() + current_id);
ItemDeleted(wxDataViewItem(nullptr), wxDataViewItem(deleted_node));
m_objects.emplace(m_objects.begin() + new_id, deleted_node);
ItemAdded(wxDataViewItem(nullptr), wxDataViewItem(deleted_node));
// If some item has a children, just to add a deleted item is not enough on Linux
// We should to add all its children separately
AddAllChildren(wxDataViewItem(deleted_node));
return wxDataViewItem(deleted_node);
}
bool ObjectDataViewModel::IsEnabled(const wxDataViewItem &item, unsigned int col) const
{
bool ret;
wxASSERT(item.IsOk());
ObjectDataViewModelNode *node = static_cast<ObjectDataViewModelNode*>(item.GetID());
// disable extruder selection for the non "itObject|itVolume" item
ret = !(col == colFilament && node->m_extruder.IsEmpty());
return ret;
}
wxDataViewItem ObjectDataViewModel::GetParent(const wxDataViewItem &item) const
{
// the invisible root node has no parent
if (!item.IsOk())
return wxDataViewItem(0);
ObjectDataViewModelNode *node = static_cast<ObjectDataViewModelNode*>(item.GetID());
assert(node != nullptr && node->valid());
// objects nodes has no parent too
if (node->m_type == itPlate)
return wxDataViewItem(0);
return wxDataViewItem((void*)node->GetParent());
}
wxDataViewItem ObjectDataViewModel::GetObject(const wxDataViewItem& item) const
{
// the invisible root node has no parent
if (!item.IsOk())
return wxDataViewItem(0);
ObjectDataViewModelNode* node = static_cast<ObjectDataViewModelNode*>(item.GetID());
if (node->m_type & itPlate)
return wxDataViewItem(0);
if (node->m_type & itObject)
return item;
ObjectDataViewModelNode* parent_node = node->GetParent();
while (parent_node->m_type != itObject)
parent_node = parent_node->GetParent();
return wxDataViewItem((void*)parent_node);
}
wxDataViewItem ObjectDataViewModel::GetTopParent(const wxDataViewItem &item) const
{
// the invisible root node has no parent
if (!item.IsOk())
return wxDataViewItem(0);
ObjectDataViewModelNode *node = static_cast<ObjectDataViewModelNode*>(item.GetID());
if (node->m_type == itPlate)
return item;
ObjectDataViewModelNode *parent_node = node->GetParent();
while (parent_node->m_type != itPlate)
parent_node = parent_node->GetParent();
return wxDataViewItem((void*)parent_node);
}
bool ObjectDataViewModel::IsContainer(const wxDataViewItem &item) const
{
// the invisible root node can have children
if (!item.IsOk())
return true;
ObjectDataViewModelNode *node = static_cast<ObjectDataViewModelNode*>(item.GetID());
return node->IsContainer();
}
unsigned int ObjectDataViewModel::GetChildren(const wxDataViewItem &parent, wxDataViewItemArray &array) const
{
ObjectDataViewModelNode *node = static_cast<ObjectDataViewModelNode*>(parent.GetID());
if (!node)
{
int node_count = 0;
for (auto plate : m_plates)
array.Add(wxDataViewItem((void*)plate));
node_count += m_plates.size();
/* FIX: should only return first layer children
for (auto object : m_objects) {
if (object->GetParent() == nullptr) {
array.Add(wxDataViewItem((void*)object));
node_count++;
}
}*/
return node_count++;
}
if (node->GetChildCount() == 0)
{
return 0;
}
unsigned int count = node->GetChildren().GetCount();
for (unsigned int pos = 0; pos < count; pos++)
{
ObjectDataViewModelNode *child = node->GetChildren().Item(pos);
array.Add(wxDataViewItem((void*)child));
}
return count;
}
void ObjectDataViewModel::GetAllChildren(const wxDataViewItem &parent, wxDataViewItemArray &array) const
{
ObjectDataViewModelNode *node = static_cast<ObjectDataViewModelNode*>(parent.GetID());
if (!node) {
for (auto plate : m_plates)
array.Add(wxDataViewItem((void*)plate));
}
else if (node->GetChildCount() == 0)
return;
else {
const size_t count = node->GetChildren().GetCount();
for (size_t pos = 0; pos < count; pos++) {
ObjectDataViewModelNode *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);
}
}
bool ObjectDataViewModel::HasInfoItem(InfoItemType type) const
{
for (ObjectDataViewModelNode* obj_node : m_objects)
for (size_t j = 0; j < obj_node->GetChildCount(); j++)
if (obj_node->GetNthChild(j)->GetInfoItemType() == type)
return true;
return false;
}
ItemType ObjectDataViewModel::GetItemType(const wxDataViewItem &item) const
{
if (!item.IsOk())
return itUndef;
ObjectDataViewModelNode *node = static_cast<ObjectDataViewModelNode*>(item.GetID());
return node->m_type < 0 ? itUndef : node->m_type;
}
InfoItemType ObjectDataViewModel::GetInfoItemType(const wxDataViewItem &item) const
{
if (!item.IsOk())
return InfoItemType::Undef;
ObjectDataViewModelNode *node = static_cast<ObjectDataViewModelNode*>(item.GetID());
return node->m_info_item_type;
}
wxDataViewItem ObjectDataViewModel::GetItemByType(const wxDataViewItem &parent_item, ItemType type) const
{
if (!parent_item.IsOk())
return wxDataViewItem(0);
ObjectDataViewModelNode *node = static_cast<ObjectDataViewModelNode*>(parent_item.GetID());
if (node->GetChildCount() == 0)
return wxDataViewItem(0);
for (size_t i = 0; i < node->GetChildCount(); i++) {
if (node->GetNthChild(i)->m_type == type)
return wxDataViewItem((void*)node->GetNthChild(i));
}
return wxDataViewItem(0);
}
wxDataViewItem ObjectDataViewModel::GetSettingsItem(const wxDataViewItem &item) const
{
return GetItemByType(item, itSettings);
}
wxDataViewItem ObjectDataViewModel::GetInstanceRootItem(const wxDataViewItem &item) const
{
return GetItemByType(item, itInstanceRoot);
}
wxDataViewItem ObjectDataViewModel::GetLayerRootItem(const wxDataViewItem &item) const
{
return GetItemByType(item, itLayerRoot);
}
wxDataViewItem ObjectDataViewModel::GetObjectItem(const ModelObject* mo) const
{
for (auto object_node : m_objects) {
if (object_node->m_model_object == mo) {
return wxDataViewItem(object_node);
}
}
return wxDataViewItem(nullptr);
}
wxDataViewItem ObjectDataViewModel::GetVolumeItem(const wxDataViewItem& parent, int vol_idx) const
{
ObjectDataViewModelNode* obj_node = (ObjectDataViewModelNode*)parent.GetID();
// BBS
if (obj_node != nullptr) {
for (auto child : obj_node->GetChildren()) {
if (child->GetType() == itVolume && child->GetIdx() == vol_idx)
return wxDataViewItem(child);
}
}
return wxDataViewItem(nullptr);
}
wxDataViewItem ObjectDataViewModel::GetInfoItemByType(const wxDataViewItem &parent_item, InfoItemType type) const
{
if (! parent_item.IsOk())
return wxDataViewItem(0);
ObjectDataViewModelNode *node = static_cast<ObjectDataViewModelNode*>(parent_item.GetID());
for (size_t i = 0; i < node->GetChildCount(); i++) {
const ObjectDataViewModelNode* child_node = node->GetNthChild(i);
if (child_node->m_type == itInfo && child_node->m_info_item_type == type)
return wxDataViewItem((void*)child_node);
}
return wxDataViewItem(0); // not found
}
bool ObjectDataViewModel::IsSettingsItem(const wxDataViewItem &item) const
{
if (!item.IsOk())
return false;
ObjectDataViewModelNode *node = static_cast<ObjectDataViewModelNode*>(item.GetID());
return node->m_type == itSettings;
}
void ObjectDataViewModel::UpdateSettingsDigest(const wxDataViewItem &item,
const std::vector<std::string>& categories)
{
if (!item.IsOk()) return;
ObjectDataViewModelNode *node = static_cast<ObjectDataViewModelNode*>(item.GetID());
if (!node->update_settings_digest(categories))
return;
ItemChanged(item);
}
void ObjectDataViewModel::SetVolumeType(const wxDataViewItem &item, const Slic3r::ModelVolumeType volume_type)
{
if (!item.IsOk() || GetItemType(item) != itVolume)
return;
ObjectDataViewModelNode *node = static_cast<ObjectDataViewModelNode*>(item.GetID());
node->SetVolumeType(volume_type);
node->SetBitmap(m_volume_bmps[int(volume_type)]);
if (volume_type != Slic3r::ModelVolumeType::MODEL_PART && volume_type != Slic3r::ModelVolumeType::PARAMETER_MODIFIER)
node->SetExtruder(""); // hide extruder
else if (node->GetExtruder().IsEmpty())
node->SetExtruder("default"); // show extruder ans set it to default
node->UpdateExtruderAndColorIcon();
ItemChanged(item);
}
ModelVolumeType ObjectDataViewModel::GetVolumeType(const wxDataViewItem& item)
{
if (!item.IsOk() || GetItemType(item) != itVolume)
return ModelVolumeType::INVALID;
ObjectDataViewModelNode *node = static_cast<ObjectDataViewModelNode*>(item.GetID());
return node->GetVolumeType();
}
wxDataViewItem ObjectDataViewModel::SetPrintableState(
PrintIndicator printable,
int obj_idx,
int subobj_idx /* = -1*/,
ItemType subobj_type/* = itInstance*/)
{
wxDataViewItem item = wxDataViewItem(0);
if (subobj_idx < 0)
item = GetItemById(obj_idx);
else
item = subobj_type&itInstance ? GetItemByInstanceId(obj_idx, subobj_idx) :
GetItemByVolumeId(obj_idx, subobj_idx);
ObjectDataViewModelNode* node = static_cast<ObjectDataViewModelNode*>(item.GetID());
if (!node)
return wxDataViewItem(0);
node->set_printable_icon(printable);
ItemChanged(item);
if (subobj_idx >= 0)
UpdateObjectPrintable(GetItemById(obj_idx));
return item;
}
wxDataViewItem ObjectDataViewModel::SetObjectPrintableState(
PrintIndicator printable,
wxDataViewItem obj_item)
{
ObjectDataViewModelNode* node = static_cast<ObjectDataViewModelNode*>(obj_item.GetID());
if (!node)
return wxDataViewItem(0);
node->set_printable_icon(printable);
ItemChanged(obj_item);
UpdateInstancesPrintable(obj_item);
return obj_item;
}
bool ObjectDataViewModel::IsColorPainted(wxDataViewItem& item) const
{
ObjectDataViewModelNode* node = static_cast<ObjectDataViewModelNode*>(item.GetID());
if (!node)
return false;
return node->m_color_enable;
}
bool ObjectDataViewModel::IsSupportPainted(wxDataViewItem& item) const
{
ObjectDataViewModelNode* node = static_cast<ObjectDataViewModelNode*>(item.GetID());
if (!node)
return false;
return node->m_support_enable;
}
void ObjectDataViewModel::SetColorPaintState(const bool painted, wxDataViewItem obj_item)
{
ObjectDataViewModelNode* node = static_cast<ObjectDataViewModelNode*>(obj_item.GetID());
if (!node)
return;
node->set_color_icon(painted);
ItemChanged(obj_item);
}
void ObjectDataViewModel::SetSupportPaintState(const bool painted, wxDataViewItem obj_item)
{
ObjectDataViewModelNode* node = static_cast<ObjectDataViewModelNode*>(obj_item.GetID());
if (!node)
return;
node->set_support_icon(painted);
ItemChanged(obj_item);
}
void ObjectDataViewModel::Rescale()
{
m_volume_bmps = MenuFactory::get_volume_bitmaps();
m_warning_bmp = create_scaled_bitmap(WarningIcon);
m_warning_manifold_bmp = create_scaled_bitmap(WarningManifoldIcon);
for (auto item : INFO_ITEMS)
m_info_bmps[item.first] = create_scaled_bitmap(item.second.bmp_name);
wxDataViewItemArray all_items;
GetAllChildren(wxDataViewItem(0), all_items);
for (wxDataViewItem item : all_items)
{
if (!item.IsOk())
continue;
ObjectDataViewModelNode *node = static_cast<ObjectDataViewModelNode*>(item.GetID());
node->msw_rescale();
switch (node->m_type)
{
case itObject:
if (node->m_bmp.IsOk()) node->m_bmp = GetWarningBitmap(node->m_warning_icon_name);
break;
case itVolume:
node->m_bmp = GetVolumeIcon(node->m_volume_type, node->m_warning_icon_name);
break;
case itLayerRoot:
node->m_bmp = create_scaled_bitmap(LayerRootIcon);
case itLayer:
node->m_bmp = create_scaled_bitmap(LayerIcon);
case itInfo:
node->m_bmp = m_info_bmps.at(node->m_info_item_type);
break;
default: break;
}
ItemChanged(item);
}
}
wxBitmap ObjectDataViewModel::GetVolumeIcon(const Slic3r::ModelVolumeType vol_type, const std::string& warning_icon_name/* = std::string()*/)
{
if (warning_icon_name.empty())
return m_volume_bmps[static_cast<int>(vol_type)];
std::string scaled_bitmap_name = warning_icon_name + std::to_string(static_cast<int>(vol_type));
scaled_bitmap_name += "-em" + std::to_string(wxGetApp().em_unit()) + (wxGetApp().dark_mode() ? "-dm" : "-lm");
wxBitmap *bmp = m_bitmap_cache->find(scaled_bitmap_name);
if (bmp == nullptr) {
std::vector<wxBitmap> bmps;
bmps.emplace_back(GetWarningBitmap(warning_icon_name));
bmps.emplace_back(m_volume_bmps[static_cast<int>(vol_type)]);
bmp = m_bitmap_cache->insert(scaled_bitmap_name, bmps);
}
return *bmp;
}
void ObjectDataViewModel::AddWarningIcon(const wxDataViewItem& item, const std::string& warning_icon_name)
{
if (!item.IsOk())
return;
ObjectDataViewModelNode *node = static_cast<ObjectDataViewModelNode*>(item.GetID());
if (node->GetType() & itObject) {
node->SetWarningBitmap(GetWarningBitmap(warning_icon_name), warning_icon_name);
return;
}
if (node->GetType() & itVolume) {
node->SetWarningBitmap(GetVolumeIcon(node->GetVolumeType(), warning_icon_name), warning_icon_name);
node->GetParent()->SetWarningBitmap(GetWarningBitmap(warning_icon_name), warning_icon_name);
return;
}
}
void ObjectDataViewModel::DeleteWarningIcon(const wxDataViewItem& item, const bool unmark_object/* = false*/)
{
if (!item.IsOk())
return;
ObjectDataViewModelNode *node = static_cast<ObjectDataViewModelNode*>(item.GetID());
if (!node->GetBitmap().IsOk() || !(node->GetType() & (itVolume | itObject)))
return;
if (node->GetType() & itVolume) {
node->SetWarningBitmap(m_volume_bmps[static_cast<int>(node->volume_type())], "");
return;
}
node->SetWarningBitmap(wxNullBitmap, "");
if (unmark_object)
{
wxDataViewItemArray children;
GetChildren(item, children);
for (const wxDataViewItem& child : children)
DeleteWarningIcon(child);
}
}
bool ObjectDataViewModel::HasWarningIcon(const wxDataViewItem& item) const
{
if (!item.IsOk())
return false;
ObjectDataViewModelNode *node = static_cast<ObjectDataViewModelNode*>(item.GetID());
return node->has_warning_icon();
}
void ObjectDataViewModel::UpdateWarningIcon(const wxDataViewItem& item, const std::string& warning_icon_name)
{
if (warning_icon_name.empty())
DeleteWarningIcon(item, true);
else
AddWarningIcon(item, warning_icon_name);
}
} // namespace GUI
} // namespace Slic3r