494 lines
14 KiB
C++
494 lines
14 KiB
C++
|
#include "AuxiliaryDataViewModel.hpp"
|
||
|
#include "libslic3r/libslic3r.h"
|
||
|
#include "libslic3r/Model.hpp"
|
||
|
#include "libslic3r/Format/bbs_3mf.hpp"
|
||
|
|
||
|
#include <boost/log/trivial.hpp>
|
||
|
|
||
|
#include <wx/log.h>
|
||
|
|
||
|
const static std::array<wxString, 4> 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<fs::path, AuxiliaryModelNode *> 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);
|
||
|
}
|