File Archive Dialog (first prototype)

This commit is contained in:
David Kocik 2022-06-10 10:43:53 +02:00
parent 212d741d30
commit a98efb954c
6 changed files with 424 additions and 64 deletions

View File

@ -4,6 +4,7 @@
#include <vector>
#include <string>
#include <boost/nowide/convert.hpp>
#include <boost/dll/runtime_symbol_info.hpp>
#include <wx/event.h>
#include <wx/cmdline.h>
@ -155,7 +156,7 @@ DownloadFrame::DownloadFrame(const wxString& title, const wxPoint& pos, const wx
data_sizer->Add(m_dataview, 1, wxEXPAND | wxBOTTOM);
m_dest_folder = boost::filesystem::path("C:\\Users\\User\\Downloads");
m_dest_folder = boost::dll::program_location().parent_path();//boost::filesystem::path("C:\\Users\\User\\Downloads");
//m_log_label = new wxStaticText(this, wxID_ANY, "Log:");

View File

@ -6,6 +6,7 @@
#endif //WIN32
#include <boost/nowide/convert.hpp>
#include <boost/dll/runtime_symbol_info.hpp>
#include <wx/utils.h>
namespace Downloader {
@ -229,7 +230,9 @@ bool SlicerSend::start_with_path(const wxString& path) const
// "C:\\Users\\User\\Downloads\\PrusaSlicer-2.4.2+win64-202204251110\\prusa-slicer.exe "
std::string escaped = escape_strings_cstyle({ boost::nowide::narrow(path) });
//return execute_command(boost::nowide::widen(escaped));
return execute_command("C:\\Users\\User\\Downloads\\PrusaSlicer-2.4.2+win64-202204251110\\prusa-slicer.exe " + boost::nowide::widen(escaped));
std::string binary = (boost::dll::program_location().parent_path() / "prusa-slicer.exe").string() + " ";
//return execute_command("C:\\Users\\User\\Downloads\\PrusaSlicer-2.4.2+win64-202204251110\\prusa-slicer.exe " + boost::nowide::widen(escaped));
return execute_command(boost::nowide::widen(binary) + boost::nowide::widen(escaped));
}
bool SlicerSend::start_or_send(const wxString& path) const

View File

@ -1,22 +1,294 @@
#include "FileArchiveDialog.hpp"
//#include "UnsavedChangesDialog.hpp"
#include "I18N.hpp"
#include "GUI_App.hpp"
#include "GUI.hpp"
#include "MainFrame.hpp"
#include "ExtraRenderers.hpp"
namespace Slic3r {
namespace GUI {
FileArchiveDialog::FileArchiveDialog(const std::vector<std::string>& files)
ArchiveViewModel::ArchiveViewModel(wxWindow* parent)
:m_parent(parent)
{}
ArchiveViewModel::~ArchiveViewModel()
{}
//wxDataViewItem ArchiveViewModel::AddFolder(wxDataViewItem& parent, wxString name)
//{
// // TODO
// return wxDataViewItem(nullptr);
//}
//wxDataViewItem ArchiveViewModel::AddFile(wxDataViewItem& parent, wxString name)
//{
// ArchiveViewNode* node = new ArchiveViewNode(name);
//
// wxDataViewItem child((void*)node);
//
// ItemAdded(parent, child);
// m_ctrl->Expand(parent);
// return child;
//}
ArchiveViewNode* ArchiveViewModel::AddFile(ArchiveViewNode* parent, wxString name)
{
ArchiveViewNode* node = new ArchiveViewNode(name);
if (parent != nullptr) {
parent->get_children().push_back(node);
node->set_parent(parent);
parent->set_is_folder(true);
} else {
m_top_children.push_back(node);
}
wxDataViewItem child((void*)node);
wxDataViewItem parent_item = wxDataViewItem((void*)parent);
ItemAdded(parent_item, child);
if (parent)
m_ctrl->Expand(parent_item);
return node;
}
wxString ArchiveViewModel::GetColumnType(unsigned int col) const
{
if (col == 0)
return "bool";
return "string";//"DataViewBitmapText";
}
void ArchiveViewModel::Rescale()
{
// TODO
}
void ArchiveViewModel::Delete(const wxDataViewItem& item)
{
// TODO
assert(item.IsOk());
ArchiveViewNode* node = static_cast<ArchiveViewNode*>(item.GetID());
assert(node->get_parent() != nullptr);
for (ArchiveViewNode* child : node->get_children())
{
Delete(wxDataViewItem((void*)child));
}
delete [] node;
}
void ArchiveViewModel::Clear()
{
// TODO
}
wxDataViewItem ArchiveViewModel::GetParent(const wxDataViewItem& item) const
{
assert(item.IsOk());
ArchiveViewNode* node = static_cast<ArchiveViewNode*>(item.GetID());
return wxDataViewItem(node->get_parent());
}
unsigned int ArchiveViewModel::GetChildren(const wxDataViewItem& parent, wxDataViewItemArray& array) const
{
if (!parent.IsOk()) {
for (ArchiveViewNode* child : m_top_children) {
array.push_back(wxDataViewItem((void*)child));
}
return m_top_children.size();
}
ArchiveViewNode* node = static_cast<ArchiveViewNode*>(parent.GetID());
for (ArchiveViewNode* child : node->get_children()) {
array.push_back(wxDataViewItem((void*)child));
}
return node->get_children().size();
}
void ArchiveViewModel::GetValue(wxVariant& variant, const wxDataViewItem& item, unsigned int col) const
{
assert(item.IsOk());
ArchiveViewNode* node = static_cast<ArchiveViewNode*>(item.GetID());
if (col == 0) {
variant = node->get_toggle();
} else {
variant = node->get_name();
}
}
void ArchiveViewModel::untoggle_folders(const wxDataViewItem& item)
{
assert(item.IsOk());
ArchiveViewNode* node = static_cast<ArchiveViewNode*>(item.GetID());
node->set_toggle(false);
if (node->get_parent())
untoggle_folders(wxDataViewItem((void*)node->get_parent()));
}
bool ArchiveViewModel::SetValue(const wxVariant& variant, const wxDataViewItem& item, unsigned int col)
{
assert(item.IsOk());
ArchiveViewNode* node = static_cast<ArchiveViewNode*>(item.GetID());
if (col == 0) {
node->set_toggle(variant.GetBool());
// if folder recursivelly check all children
for (ArchiveViewNode* child : node->get_children()) {
SetValue(variant, wxDataViewItem((void*)child), col);
}
if(!variant.GetBool() && node->get_parent())
untoggle_folders(wxDataViewItem((void*)node->get_parent()));
} else {
node->set_name(variant.GetString());
}
m_parent->Refresh();
return true;
}
bool ArchiveViewModel::IsEnabled(const wxDataViewItem& item, unsigned int col) const
{
return true;
}
bool ArchiveViewModel::IsContainer(const wxDataViewItem& item) const
{
if(!item.IsOk())
return true;
ArchiveViewNode* node = static_cast<ArchiveViewNode*>(item.GetID());
return node->is_container();
}
ArchiveViewCtrl::ArchiveViewCtrl(wxWindow* parent, wxSize size)
: wxDataViewCtrl(parent, wxID_ANY, wxDefaultPosition, size, wxDV_VARIABLE_LINE_HEIGHT | wxDV_ROW_LINES
#ifdef _WIN32
| wxBORDER_SIMPLE
#endif
)
//, m_em_unit(em_unit(parent))
{
wxGetApp().UpdateDVCDarkUI(this);
m_model = new ArchiveViewModel(parent);
this->AssociateModel(m_model);
m_model->SetAssociatedControl(this);
}
ArchiveViewCtrl::~ArchiveViewCtrl()
{
if (m_model) {
m_model->Clear();
m_model->DecRef();
}
}
FileArchiveDialog::FileArchiveDialog( mz_zip_archive* archive, std::vector<boost::filesystem::path>& selected_paths)
: DPIDialog(static_cast<wxWindow*>(wxGetApp().mainframe), wxID_ANY, _(L("Archive preview")), wxDefaultPosition,
wxSize(45 * wxGetApp().em_unit(), 40 * wxGetApp().em_unit()),
wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxMAXIMIZE_BOX)
, m_selected_paths (selected_paths)
{
}
int em = em_unit();
wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL);
m_avc = new ArchiveViewCtrl(this, wxSize(60 * em, 30 * em));
m_avc->AppendToggleColumn(L"\u2714", 0, wxDATAVIEW_CELL_ACTIVATABLE, 6 * em);
m_avc->AppendTextColumn("filename", 1);
std::vector<ArchiveViewNode*> stack;
std::function<void(std::vector<ArchiveViewNode*>&, size_t)> reduce_stack = [] (std::vector<ArchiveViewNode*>& stack, size_t size) {
if (size == 0) {
stack.clear();
return;
}
while (stack.size() > size)
stack.pop_back();
};
// recursively stores whole structure of file onto function stack and synchoronize with stack object.
std::function<size_t(boost::filesystem::path&, std::vector<ArchiveViewNode*>&)> adjust_stack = [&adjust_stack, &reduce_stack, &avc = m_avc](boost::filesystem::path& file, std::vector<ArchiveViewNode*>& stack)->size_t {
size_t struct_size = file.has_parent_path() ? adjust_stack(file.parent_path(), stack) : 0;
if (stack.size() > struct_size && (file.has_extension() || file.filename().string() != stack[struct_size]->get_name()))
{
reduce_stack(stack, struct_size);
}
if (!file.has_extension() && stack.size() == struct_size)
stack.push_back(avc->get_model()->AddFile(stack.empty() ? nullptr : stack.back(), boost::nowide::widen(file.filename().string())));
return struct_size + 1;
};
mz_uint num_entries = mz_zip_reader_get_num_files(archive);
mz_zip_archive_file_stat stat;
for (mz_uint i = 0; i < num_entries; ++i) {
if (mz_zip_reader_file_stat(archive, i, &stat)) {
std::string name(stat.m_filename);
//std::replace(name.begin(), name.end(), '\\', '/');
boost::filesystem::path path(name);
if (!path.has_extension())
continue;
ArchiveViewNode* parent = nullptr;
adjust_stack(path, stack);
if (!stack.empty())
parent = stack.back();
m_avc->get_model()->AddFile(parent, boost::nowide::widen(path.filename().string()))->set_fullpath(std::move(path));
}
}
wxBoxSizer* btn_sizer = new wxBoxSizer(wxHORIZONTAL);
wxButton* btn_run = new wxButton(this, wxID_OK, "Open");
btn_run->Bind(wxEVT_BUTTON, [this](wxCommandEvent& evt) { on_open_button(); });
btn_sizer->Add(btn_run, 0, wxLEFT | wxRIGHT);
//ScalableButton* cancel_btn = new ScalableButton(this, wxID_CANCEL, "cross", _L("Cancel"), wxDefaultSize, wxDefaultPosition, wxBORDER_DEFAULT, true, 24);
//btn_sizer->Add(cancel_btn, 1, wxLEFT | wxRIGHT, 5);
//cancel_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { this->EndModal(wxID_CANCEL); });
topSizer->Add(m_avc, 1, wxEXPAND | wxALL, 10);
topSizer->Add(btn_sizer, 0, wxEXPAND | wxALL, 10);
this->SetMinSize(wxSize(80 * em, 30 * em));
this->SetSizer(topSizer);
}
void FileArchiveDialog::on_dpi_changed(const wxRect& suggested_rect)
{
int em = em_unit();
//msw_buttons_rescale(this, em, { wxID_CANCEL, m_save_btn_id, m_move_btn_id, m_continue_btn_id });
//for (auto btn : { m_save_btn, m_transfer_btn, m_discard_btn })
// if (btn) btn->msw_rescale();
const wxSize& size = wxSize(70 * em, 30 * em);
SetMinSize(size);
//m_tree->Rescale(em);
Fit();
Refresh();
}
void FileArchiveDialog::on_open_button()
{
wxDataViewItemArray top_items;
m_avc->get_model()->GetChildren(wxDataViewItem(nullptr), top_items);
std::function<void(ArchiveViewNode*)> deep_fill = [&paths = m_selected_paths, &deep_fill](ArchiveViewNode* node){
if (node == nullptr)
return;
if (node->get_children().empty()) {
if (node->get_toggle())
paths.emplace_back(node->get_fullpath());
} else {
for (ArchiveViewNode* child : node->get_children())
deep_fill(child);
}
};
for (const auto& item : top_items)
{
ArchiveViewNode* node = static_cast<ArchiveViewNode*>(item.GetID());
deep_fill(node);
}
this->EndModal(wxID_OK);
}
} // namespace GUI

View File

@ -2,29 +2,110 @@
#define slic3r_GUI_FileArchiveDialog_hpp_
#include "GUI_Utils.hpp"
#include "libslic3r/miniz_extension.hpp"
#include <wx/wx.h>
namespace Slic3r {
namespace GUI {
class ArchiveViewCtrl;
class ArchiveViewNode
{
public:
ArchiveViewNode(const wxString& name) : m_name(name) {}
std::vector<ArchiveViewNode*>& get_children() { return m_children; }
void set_parent(ArchiveViewNode* parent) {m_parent = parent; }
ArchiveViewNode* get_parent() const { return m_parent; }
bool is_container() const { return !m_children.empty(); }
wxString get_name() const { return m_name; }
void set_name(const wxString& name) { m_name = name; }
bool get_toggle() const {return m_toggle; }
void set_toggle(bool toggle) {m_toggle = toggle; }
bool get_is_folder() const { return m_folder; }
void set_is_folder(bool is_folder) { m_folder = is_folder; }
void set_fullpath(boost::filesystem::path path) { m_fullpath = path; }
boost::filesystem::path get_fullpath() const { return m_fullpath; }
private:
private:
wxString m_name;
ArchiveViewNode* m_parent { nullptr };
std::vector<ArchiveViewNode*> m_children; // MEMORY!
bool m_toggle { false };
bool m_folder { false };
boost::filesystem::path m_fullpath;
};
class ArchiveViewModel : public wxDataViewModel
{
public:
ArchiveViewModel(wxWindow* parent);
~ArchiveViewModel();
/* wxDataViewItem AddFolder(wxDataViewItem& parent, wxString name);
wxDataViewItem AddFile(wxDataViewItem& parent, wxString name);*/
ArchiveViewNode* AddFile(ArchiveViewNode* parent, wxString name);
wxString GetColumnType(unsigned int col) const override;
unsigned int GetColumnCount() const override { return 2; }
void Rescale();
void Delete(const wxDataViewItem& item);
void Clear();
wxDataViewItem GetParent(const wxDataViewItem& item) const override;
unsigned int GetChildren(const wxDataViewItem& parent, wxDataViewItemArray& array) const override;
void SetAssociatedControl(ArchiveViewCtrl* ctrl) { m_ctrl = ctrl; }
void GetValue(wxVariant& variant, const wxDataViewItem& item, unsigned int col) const override;
bool SetValue(const wxVariant& variant, const wxDataViewItem& item, unsigned int col) override;
void untoggle_folders(const wxDataViewItem& item);
bool IsEnabled(const wxDataViewItem& item, unsigned int col) const override;
bool IsContainer(const wxDataViewItem& item) const override;
// Is the container just a header or an item with all columns
// In our case it is an item with all columns
bool HasContainerColumns(const wxDataViewItem& WXUNUSED(item)) const override { return true; }
protected:
wxWindow* m_parent { nullptr };
ArchiveViewCtrl* m_ctrl { nullptr };
std::vector<ArchiveViewNode*> m_top_children;
};
class ArchiveViewCtrl : public wxDataViewCtrl
{
public:
ArchiveViewCtrl(wxWindow* parent, wxSize size);
~ArchiveViewCtrl();
ArchiveViewModel* get_model() const {return m_model; }
protected:
ArchiveViewModel* m_model;
};
class FileArchiveDialog : public DPIDialog
{
public:
FileArchiveDialog(const std::vector<std::string>& files);
FileArchiveDialog(mz_zip_archive* archive, std::vector<boost::filesystem::path>& selected_paths);
protected:
void on_dpi_changed(const wxRect& suggested_rect) override;
void on_open_button();
std::vector<boost::filesystem::path>& m_selected_paths;
ArchiveViewCtrl* m_avc;
};
} // namespace GU
} // namespace Slic3r
#endif // slic3r_GUI_FileArchiveDialog_hpp_

View File

@ -5454,76 +5454,78 @@ std::vector<size_t> Plater::load_files(const std::vector<std::string>& input_fil
return p->load_files(paths, load_model, load_config, imperial_units);
}
void Plater::preview_archive(const boost::filesystem::path& archive_path)
bool Plater::preview_archive(const boost::filesystem::path& archive_path)
{
BOOST_LOG_TRIVIAL(error) << "preview zip archive: " << archive_path;
mz_zip_archive archive;
mz_zip_zero_struct(&archive);
if (!open_zip_reader(&archive, archive_path.string()))
return ;
return false;
mz_uint num_entries = mz_zip_reader_get_num_files(&archive);
//BOOST_LOG_TRIVIAL(error) << "entries: " << num_entries;
mz_uint num_entries = mz_zip_reader_get_num_files(&archive);
mz_zip_archive_file_stat stat;
//bool config_found = false;
//for (mz_uint i = 0; i < num_entries; ++i) {
// if (mz_zip_reader_file_stat(&archive, i, &stat)) {
// std::string name(stat.m_filename);
// //std::replace(name.begin(), name.end(), '\\', '/');
std::vector<boost::filesystem::path> selected_paths;
std::vector<std::string> unzipped_paths;
FileArchiveDialog dlg(&archive, selected_paths);
if (dlg.ShowModal() == wxID_OK)
{
std::string archive_path_string = archive_path.string();
archive_path_string = archive_path_string.substr(0, archive_path_string.size() - 4);
/* boost::filesystem::path archive_dir(archive_path_string);
if (!boost::filesystem::is_directory(archive_dir) &&
!boost::filesystem::create_directory(archive_dir)) {
close_zip_reader(&archive);
return;
}*/
// BOOST_LOG_TRIVIAL(error) << name;
//
// }
//}
std::string archive_path_string = archive_path.string();
archive_path_string = archive_path_string.substr(0, archive_path_string.size() - 4);
boost::filesystem::path archive_dir(archive_path_string);
boost::filesystem::path archive_dir("C:\\Users\\User\\AppData\\Local\\Temp");
//m_result = {filename="C:\\Users\\User\\AppData\\Local\\Temp\\.7596.gcode.tmp" id=3 moves={ size=9639 } ...}
for (mz_uint i = 0; i < num_entries; ++i) {
if (mz_zip_reader_file_stat(&archive, i, &stat)) {
std::string name(stat.m_filename);
for (const auto& path : selected_paths) {
if (path.string() == name) {
try
{
std::replace(name.begin(), name.end(), '\\', '/');
// rename if file exists
std::string filename = path.filename().string();
std::string extension = boost::filesystem::extension(path);
std::string just_filename = filename.substr(0, filename.size() - extension.size());
std::string final_filename = just_filename;
size_t version = 0;
while (boost::filesystem::exists(archive_dir / (final_filename + extension)))
{
++version;
final_filename = just_filename + "(" + std::to_string(version) + ")";
}
filename = final_filename + extension;
if (!boost::filesystem::is_directory(archive_dir) &&
!boost::filesystem::create_directory(archive_dir))
return;
std::replace(archive_path_string.begin(), archive_path_string.end(), '\\', '/');
std::vector<std::string> files;
// we first loop the entries to read from the archive the .model file only, in order to extract the version from it
for (mz_uint i = 0; i < num_entries; ++i) {
if (mz_zip_reader_file_stat(&archive, i, &stat)) {
std::string name(stat.m_filename);
std::replace(name.begin(), name.end(), '\\', '/');
files.push_back(name);
if (boost::algorithm::iends_with(name, ".stl")) {
try
{
std::string final_path = archive_path_string + "/" + name;
bool res = mz_zip_reader_extract_file_to_file(&archive, stat.m_filename, final_path.c_str(), 0);
//bool res = mz_zip_reader_extract_file_to_callback(&archive, stat.m_filename, [](void* pOpaque, mz_uint64 file_ofs, const void* pBuf, size_t n)->size_t {}, &data, 0);
}
catch (const std::exception& e)
{
// ensure the zip archive is closed and rethrow the exception
close_zip_reader(&archive);
throw Slic3r::FileIOError(e.what());
std::string final = (archive_dir / filename).string();
unzipped_paths.emplace_back(final);
bool res = mz_zip_reader_extract_file_to_file(&archive, stat.m_filename, final.c_str(), 0);
}
catch (const std::exception& e)
{
// ensure the zip archive is closed and rethrow the exception
close_zip_reader(&archive);
throw Slic3r::FileIOError(e.what());
}
}
}
}
}
}
FileArchiveDialog dlg(files);
dlg.ShowModal();
close_zip_reader(&archive);
return !load_files(unzipped_paths).empty();
}
enum class LoadType : unsigned char
@ -5699,7 +5701,8 @@ bool Plater::load_files(const wxArrayString& filenames)
return true;
} else if (boost::algorithm::iends_with(filename, ".zip")) {
preview_archive(*it);
return preview_archive(*it);
}
}

View File

@ -177,7 +177,7 @@ public:
bool load_files(const wxArrayString& filenames);
void check_selected_presets_visibility(PrinterTechnology loaded_printer_technology);
void preview_archive(const boost::filesystem::path& input_file);
bool preview_archive(const boost::filesystem::path& input_file);
const wxString& get_last_loaded_gcode() const { return m_last_loaded_gcode; }