#include "AuxiliaryDataViewModel.hpp" #include "libslic3r/libslic3r.h" #include "libslic3r/Model.hpp" #include "libslic3r/Format/bbs_3mf.hpp" #include #include const static std::array s_default_folders = { _L("Model Pictures"), _L("Bill of Materials"), _L("Assembly Guide"), _L("Others") }; AuxiliaryModel::AuxiliaryModel() { m_root = nullptr; } void AuxiliaryModel::Init(wxString aux_path) { m_root = new AuxiliaryModelNode(); m_root_dir = aux_path; if (wxDirExists(m_root_dir)) { fs::path path_to_del(m_root_dir.ToStdWstring()); try { fs::remove_all(path_to_del); } catch (...) { BOOST_LOG_TRIVIAL(error) << "Failed removing the auxiliary directory " << m_root_dir.c_str(); } } fs::path top_dir_path(m_root_dir.ToStdWstring()); fs::create_directory(top_dir_path); for (auto folder : s_default_folders) CreateFolder(folder); } AuxiliaryModel::~AuxiliaryModel() { if (wxDirExists(m_root_dir)) { fs::path path_to_del(m_root_dir.ToStdWstring()); try { fs::remove_all(path_to_del); } catch (...) { BOOST_LOG_TRIVIAL(error) << "Failed removing the auxiliary directory " << m_root_dir.c_str(); } m_root_dir = ""; } delete m_root; m_root = nullptr; } void AuxiliaryModel::Reload(wxString aux_path) { fs::path new_aux_path(aux_path.ToStdWstring()); // Clean try { fs::remove_all(fs::path(m_root_dir.ToStdWstring())); } catch (...) { BOOST_LOG_TRIVIAL(error) << "Failed removing the auxiliary directory " << m_root_dir.c_str(); } if (m_root) { delete m_root; m_root = nullptr; } Cleared(); // Create new root. m_root = new AuxiliaryModelNode(); m_root_dir = aux_path; // Check new path. If not exist, create a new one. if (!fs::exists(new_aux_path)) { fs::create_directory(new_aux_path); // Create default folders if they are not loaded wxDataViewItemArray default_items; for (auto folder : s_default_folders) { wxString folder_path = aux_path + "\\" + folder; if (fs::exists(folder_path.ToStdWstring())) continue; fs::create_directory(folder_path.ToStdWstring()); AuxiliaryModelNode *node = new AuxiliaryModelNode(m_root, folder_path, true); default_items.Add(wxDataViewItem(node)); } ItemsAdded(wxDataViewItem(nullptr), default_items); return; } // Load from new path std::map dir_cache; fs::directory_iterator iter_end; wxDataViewItemArray items; for (fs::directory_iterator iter(new_aux_path); iter != iter_end; iter++) { wxString path = iter->path().generic_wstring(); AuxiliaryModelNode* node = new AuxiliaryModelNode(m_root, path, fs::is_directory(iter->path())); items.Add(wxDataViewItem(node)); if (node->IsContainer()) { dir_cache.insert({ iter->path(), node }); } } ItemsAdded(wxDataViewItem(nullptr), items); items.Clear(); for (auto dir : dir_cache) { for (fs::directory_iterator iter(dir.first); iter != iter_end; iter++) { if (fs::is_directory(iter->path())) continue; wxString file_path = iter->path().generic_wstring(); AuxiliaryModelNode* file = new AuxiliaryModelNode(dir.second, file_path, false); items.Add(wxDataViewItem(file)); } ItemsAdded(wxDataViewItem(dir.second), items); } // Create default folders if they are not loaded wxDataViewItemArray default_items; for (auto folder : s_default_folders) { wxString folder_path = aux_path + "\\" + folder; if (fs::exists(folder_path.ToStdWstring())) continue; fs::create_directory(folder_path.ToStdWstring()); AuxiliaryModelNode* node = new AuxiliaryModelNode(m_root, folder_path, true); default_items.Add(wxDataViewItem(node)); } ItemsAdded(wxDataViewItem(nullptr), default_items); } int AuxiliaryModel::Compare(const wxDataViewItem& item1, const wxDataViewItem& item2, unsigned int column, bool ascending) const { wxASSERT(item1.IsOk() && item2.IsOk()); // should never happen if (IsContainer(item1) && IsContainer(item2)) { wxVariant value1, value2; GetValue(value1, item1, 0); GetValue(value2, item2, 0); wxString str1 = value1.GetString(); wxString str2 = value2.GetString(); int res = str1.Cmp(str2); if (res) return res; // items must be different wxUIntPtr litem1 = (wxUIntPtr)item1.GetID(); wxUIntPtr litem2 = (wxUIntPtr)item2.GetID(); return litem1 - litem2; } return wxDataViewModel::Compare(item1, item2, column, ascending); } void AuxiliaryModel::GetValue(wxVariant& variant, const wxDataViewItem& item, unsigned int col) const { wxASSERT(item.IsOk()); AuxiliaryModelNode* node = (AuxiliaryModelNode*)item.GetID(); switch (col) { case 0: variant = node->name; break; default: wxLogError("AuxiliaryModel::GetValue: wrong column %d", col); } } bool AuxiliaryModel::SetValue(const wxVariant& variant, const wxDataViewItem& item, unsigned int col) { wxASSERT(item.IsOk()); AuxiliaryModelNode* node = (AuxiliaryModelNode*)item.GetID(); switch (col) { case 0: node->name = variant.GetString(); return true; default: wxLogError("AuxiliaryModel::SetValue: wrong column"); } return false; } bool AuxiliaryModel::IsEnabled(const wxDataViewItem& item, unsigned int col) const { return true; } wxDataViewItem AuxiliaryModel::GetParent(const wxDataViewItem& item) const { AuxiliaryModelNode* node = (AuxiliaryModelNode*)item.GetID(); return wxDataViewItem(GetParent(node)); } bool AuxiliaryModel::IsContainer(const wxDataViewItem& item) const { // the invisible root node can have children // (in our model always "MyMusic") if (!item.IsOk()) return true; AuxiliaryModelNode* node = (AuxiliaryModelNode*)item.GetID(); return node->IsContainer(); } static unsigned int count = 0; unsigned int AuxiliaryModel::GetChildren(const wxDataViewItem& parent, wxDataViewItemArray& array) const { if (m_root == nullptr) return 0; AuxiliaryModelNode* node = (AuxiliaryModelNode*)parent.GetID(); if (!node) { node = m_root; } count = node->GetChildren().GetCount(); for (unsigned int pos = 0; pos < count; pos++) { AuxiliaryModelNode* child = node->GetChildren().Item(pos); array.Add(wxDataViewItem((void*)child)); } return count; } wxDataViewItem AuxiliaryModel::CreateFolder(wxString name) { wxString folder_name = name; if (folder_name == wxEmptyString) { folder_name = _L("New Folder"); for (int i = 1; i <= 1000; i++) { bool exist = false; for (AuxiliaryModelNode* node : m_root->GetChildren()) { if (!node->IsContainer()) continue; if (node->name == folder_name) { exist = true; break; } } if (!exist) break; folder_name = _L("New Folder"); folder_name << "(" << i << ")"; } } else { for (AuxiliaryModelNode* node : m_root->GetChildren()) { if (!node->IsContainer()) continue; if (node->name == folder_name) { return wxDataViewItem(nullptr); } } } // Create folder in file system fs::path bfs_path((m_root_dir + "\\" + folder_name).ToStdWstring()); if (fs::exists(bfs_path)) { try { bool is_done = fs::remove_all(bfs_path); if (!is_done) return wxDataViewItem(nullptr); } catch (...) { BOOST_LOG_TRIVIAL(error) << "Failed removing the auxiliary directory " << m_root_dir.c_str(); } } fs::create_directory(bfs_path); // Create model node AuxiliaryModelNode* folder = new AuxiliaryModelNode(m_root, bfs_path.generic_wstring(), true); // Notify wxDataViewCtrl to update ui wxDataViewItem folder_item(folder); ItemAdded(wxDataViewItem(NULL), folder_item); return folder_item; } wxDataViewItemArray AuxiliaryModel::ImportFile(AuxiliaryModelNode* sel, wxArrayString file_paths) { if (sel == nullptr) { sel = m_root; } wxDataViewItemArray added_items; AuxiliaryModelNode* parent = sel->IsContainer() ? sel : sel->GetParent(); for (wxString file_path : file_paths) { bool exists = false; for (AuxiliaryModelNode* node : parent->GetChildren()) { if (node->path == file_path) { exists = true; break; } } if (exists) continue; // Copy imported file to project temp directory fs::path src_bfs_path(file_path.ToStdWstring()); wxString dir_path = m_root_dir; if (sel != m_root) dir_path += "\\" + sel->name; dir_path += "\\" + src_bfs_path.filename().generic_wstring(); boost::system::error_code ec; if (!fs::copy_file(src_bfs_path, fs::path(dir_path.ToStdWstring()), fs::copy_option::overwrite_if_exists, ec)) continue; // Update model data AuxiliaryModelNode* file = new AuxiliaryModelNode(parent, dir_path, false); // Notify wxDataViewCtrl to update ui wxDataViewItem file_item(file); if (parent == m_root) parent = nullptr; Slic3r::put_other_changes(); wxDataViewItem parent_item(parent); ItemAdded(parent_item, file_item); added_items.push_back(file_item); } return added_items; } void AuxiliaryModel::Delete(const wxDataViewItem& item) { AuxiliaryModelNode* node = (AuxiliaryModelNode*)item.GetID(); if (!node) // happens if item.IsOk()==false return; bool is_done = false; if (node->IsContainer()) { fs::path bfs_path((m_root_dir + "\\" + node->name).ToStdWstring()); try { is_done = fs::remove_all(bfs_path); } catch (...) { BOOST_LOG_TRIVIAL(error) << "Failed removing the auxiliary directory " << m_root_dir.c_str(); } } else { fs::path bfs_path(node->path.ToStdWstring()); is_done = fs::remove(bfs_path); } if (!is_done) return; node->GetParent()->GetChildren().Remove(node); Slic3r::put_other_changes(); wxDataViewItem parent_item = GetParent(item); ItemDeleted(parent_item, item); delete node; } void AuxiliaryModel::MoveItem(const wxDataViewItem& dropped_item, const wxDataViewItem& dragged_item) { AuxiliaryModelNode* dropped = (AuxiliaryModelNode*)dropped_item.GetID(); AuxiliaryModelNode* dragged = (AuxiliaryModelNode*)dragged_item.GetID(); if (dragged == nullptr || dragged->IsContainer()) return; AuxiliaryModelNode* target_folder = nullptr; if (dropped == nullptr) { target_folder = m_root; } else if (dropped->IsContainer()) { target_folder = dropped; } else { target_folder = dropped->GetParent(); } if (dragged->GetParent() == target_folder) return; for (AuxiliaryModelNode* node : target_folder->GetChildren()) { if (node->path == dragged->path) return; } // Generate new path wxString new_path = m_root_dir; if (target_folder != m_root) new_path += "\\" + target_folder->name; new_path += "\\" + dragged->name; // Perform file movement in file system fs::path bfs_new_path(new_path.ToStdWstring()); fs::path bfs_old_path(dragged->path.ToStdWstring()); boost::system::error_code err; fs::rename(bfs_old_path, bfs_new_path, err); if (err.failed()) return; // Reparent dragged node wxDataViewItem old_parent_item = this->GetParent(dragged_item); this->Reparent(dragged, target_folder); dragged->path = new_path; // Notify wxDataViewCtrl to update ui Slic3r::put_other_changes(); ItemDeleted(old_parent_item, wxDataViewItem(dragged)); ItemAdded(wxDataViewItem(target_folder == m_root ? nullptr : target_folder), wxDataViewItem(dragged)); } bool AuxiliaryModel::IsOrphan(const wxDataViewItem& item) { AuxiliaryModelNode* node = (AuxiliaryModelNode*)item.GetID(); return node->GetParent() != m_root; } bool AuxiliaryModel::Rename(const wxDataViewItem& item, const wxString& name) { AuxiliaryModelNode* node = (AuxiliaryModelNode*)item.GetID(); AuxiliaryModelNode* parent = node->GetParent(); if (node->IsContainer()) return false; if (!parent->IsContainer()) return false; for (AuxiliaryModelNode* cur_node : parent->GetChildren()) { if (cur_node->name == name) return false; } boost::system::error_code err; fs::path old_path((m_root_dir + "\\" + parent->name + "\\" + node->name).ToStdWstring()); fs::path new_path((m_root_dir + "\\" + parent->name + "\\" + name).ToStdWstring()); fs::rename(old_path, new_path, err); if (err.failed()) return false; Slic3r::put_other_changes(); node->name = name; node->path = m_root_dir + "\\" + parent->name + "\\" + name; return true; } AuxiliaryModelNode* AuxiliaryModel::GetParent(AuxiliaryModelNode* node) const { if (node == m_root || node->GetParent() == m_root) return nullptr; return node->GetParent(); } void AuxiliaryModel::Reparent(AuxiliaryModelNode* node, AuxiliaryModelNode* new_parent) { if (node->IsContainer()) return; node->GetParent()->GetChildren().Remove(node); node->SetParent(new_parent); new_parent->Append(node); }