Downloader feature - Downloads from Printables.com

Custom URL Registration:
 - Windows - writes to registers.
 - Linux - desktop integration file.
 - Macos - info.plist.in creates registration and is controlled only via app config.
Registration is first made in Config Wizard. Or is triggered from Preferences. Path to downloads folder can be set.
URL link starts new instance of PS which sends data to running instance via SingleInstance structures if exists.
New progress notification is introduced with pause and stop buttons.
Downloader writes downloaded data by chunks.
Support for zip files is introduced. Zip files can be opened, downloaded or drag'n'droped in PS. Archive dialog is opened. Then if more than 1 project is selected, only geometry is loaded.
Opening of 3mf project now supports openning project in new PS instance.
This commit is contained in:
David Kocik 2023-01-05 15:00:37 +01:00
parent e70c4849ba
commit ce38e57ec4
37 changed files with 9616 additions and 6577 deletions
src/slic3r/GUI

View file

@ -27,6 +27,7 @@
#include <wx/numdlg.h>
#include <wx/debug.h>
#include <wx/busyinfo.h>
#include <wx/stdpaths.h>
#ifdef _WIN32
#include <wx/richtooltip.h>
#include <wx/custombgwin.h>
@ -50,6 +51,7 @@
#include "libslic3r/Utils.hpp"
#include "libslic3r/PresetBundle.hpp"
#include "libslic3r/ClipperUtils.hpp"
#include "libslic3r/miniz_extension.hpp"
#include "GUI.hpp"
#include "GUI_App.hpp"
@ -98,6 +100,7 @@
#include "ProjectDirtyStateManager.hpp"
#include "Gizmos/GLGizmoSimplify.hpp" // create suggestion notification
#include "Gizmos/GLGizmoCut.hpp"
#include "FileArchiveDialog.hpp"
#ifdef __APPLE__
#include "Gizmos/GLGizmosManager.hpp"
@ -2231,6 +2234,16 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
wxGetApp().mainframe->Raise();
this->q->load_files(input_files);
});
this->q->Bind(EVT_START_DOWNLOAD_OTHER_INSTANCE, [this](StartDownloadOtherInstanceEvent& evt) {
BOOST_LOG_TRIVIAL(trace) << "Received url from other instance event.";
wxGetApp().mainframe->Raise();
for (size_t i = 0; i < evt.data.size(); ++i) {
wxGetApp().start_download(evt.data[i]);
}
});
this->q->Bind(EVT_INSTANCE_GO_TO_FRONT, [this](InstanceGoToFrontEvent &) {
bring_instance_forward();
});
@ -2446,6 +2459,9 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
int answer_convert_from_meters = wxOK_DEFAULT;
int answer_convert_from_imperial_units = wxOK_DEFAULT;
bool in_temp = false;
const fs::path temp_path = wxStandardPaths::Get().GetTempDir().utf8_str().data();
size_t input_files_size = input_files.size();
for (size_t i = 0; i < input_files_size; ++i) {
#ifdef _WIN32
@ -2456,6 +2472,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
// Don't make a copy on Posix. Slash is a path separator, back slashes are not accepted as a substitute.
const auto &path = input_files[i];
#endif // _WIN32
in_temp = (path.parent_path() == temp_path);
const auto filename = path.filename();
if (progress_dlg) {
progress_dlg->Update(static_cast<int>(100.0f * static_cast<float>(i) / static_cast<float>(input_files.size())), _L("Loading file") + ": " + from_path(filename));
@ -2536,7 +2553,8 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
q->update_filament_colors_in_full_config();
is_project_file = true;
}
wxGetApp().app_config->update_config_dir(path.parent_path().string());
if (!in_temp)
wxGetApp().app_config->update_config_dir(path.parent_path().string());
}
}
else {
@ -2695,10 +2713,10 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
obj_idxs.insert(obj_idxs.end(), loaded_idxs.begin(), loaded_idxs.end());
}
if (load_model) {
if (load_model && !in_temp) {
wxGetApp().app_config->update_skein_dir(input_files[input_files.size() - 1].parent_path().make_preferred().string());
// XXX: Plater.pm had @loaded_files, but didn't seem to fill them with the filenames...
// statusbar()->set_status_text(_L("Loaded"));
// statusbar()->set_status_text(_L("Loaded"));
}
// automatic selection of added objects
@ -2909,9 +2927,10 @@ wxString Plater::priv::get_export_file(GUI::FileType file_type)
}
std::string out_dir = (boost::filesystem::path(output_file).parent_path()).string();
std::string temp_dir = wxStandardPaths::Get().GetTempDir().utf8_str().data();
wxFileDialog dlg(q, dlg_title,
is_shapes_dir(out_dir) ? from_u8(wxGetApp().app_config->get_last_dir()) : from_path(output_file.parent_path()), from_path(output_file.filename()),
out_dir == temp_dir ? from_u8(wxGetApp().app_config->get("last_output_path")) : (is_shapes_dir(out_dir) ? from_u8(wxGetApp().app_config->get_last_dir()) : from_path(output_file.parent_path())), from_path(output_file.filename()),
wildcard, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
if (dlg.ShowModal() != wxID_OK)
@ -4626,7 +4645,10 @@ void Plater::priv::set_project_filename(const wxString& filename)
m_project_filename = from_path(full_path);
wxGetApp().mainframe->update_title();
if (!filename.empty())
const fs::path temp_path = wxStandardPaths::Get().GetTempDir().utf8_str().data();
bool in_temp = (temp_path == full_path.parent_path().make_preferred());
if (!filename.empty() && !in_temp)
wxGetApp().mainframe->add_to_recent_projects(filename);
}
@ -5359,6 +5381,11 @@ Print& Plater::fff_print() { return p->fff_print; }
const SLAPrint& Plater::sla_print() const { return p->sla_print; }
SLAPrint& Plater::sla_print() { return p->sla_print; }
bool Plater::is_project_temp() const
{
return false;
}
void Plater::new_project()
{
if (int saved_project = p->save_project_if_dirty(_L("Creating a new project while the current project is modified.")); saved_project == wxID_CANCEL)
@ -5563,18 +5590,408 @@ 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);
}
enum class LoadType : unsigned char
class LoadProjectsDialog : public DPIDialog
{
Unknown,
OpenProject,
LoadGeometry,
LoadConfig
int m_action{ 0 };
bool m_all { false };
wxComboBox* m_combo_project { nullptr };
wxComboBox* m_combo_config { nullptr };
public:
enum class LoadProjectOption : unsigned char
{
Unknown,
AllGeometry,
AllNewWindow,
OneProject,
OneConfig
};
LoadProjectsDialog(const std::vector<fs::path>& paths);
int get_action() const { return m_action + 1; }
bool get_all() const { return m_all; }
int get_selected() const
{
if (m_combo_project && m_combo_project->IsEnabled())
return m_combo_project->GetSelection();
else if (m_combo_config && m_combo_config->IsEnabled())
return m_combo_config->GetSelection();
else
return -1;
}
protected:
void on_dpi_changed(const wxRect& suggested_rect) override;
};
LoadProjectsDialog::LoadProjectsDialog(const std::vector<fs::path>& paths)
: DPIDialog(static_cast<wxWindow*>(wxGetApp().mainframe), wxID_ANY,
from_u8((boost::format(_utf8(L("%s - Multiple projects file"))) % SLIC3R_APP_NAME).str()), wxDefaultPosition,
wxDefaultSize, wxDEFAULT_DIALOG_STYLE)
{
SetFont(wxGetApp().normal_font());
wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL);
bool contains_projects = !paths.empty();
bool instances_allowed = wxGetApp().app_config->get("single_instance") != "1";
if (contains_projects)
main_sizer->Add(new wxStaticText(this, wxID_ANY,
get_wraped_wxString(_L("There are several files being loaded, including Project files.") + "\n" + _L("Select an action to apply to all files."))), 0, wxEXPAND | wxALL, 10);
else
main_sizer->Add(new wxStaticText(this, wxID_ANY,
get_wraped_wxString(_L("There are several files being loaded.") + "\n" + _L("Select an action to apply to all files."))), 0, wxEXPAND | wxALL, 10);
wxStaticBox* action_stb = new wxStaticBox(this, wxID_ANY, _L("Action"));
if (!wxOSX) action_stb->SetBackgroundStyle(wxBG_STYLE_PAINT);
action_stb->SetFont(wxGetApp().normal_font());
if (contains_projects) {
wxArrayString filenames;
for (const fs::path& path : paths) {
filenames.push_back(from_u8(path.filename().string()));
}
m_combo_project = new wxComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, filenames, wxCB_READONLY);
m_combo_project->SetValue(filenames.front());
m_combo_project->Enable(false);
m_combo_config = new wxComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, filenames, wxCB_READONLY);
m_combo_config->SetValue(filenames.front());
m_combo_config->Enable(false);
}
wxStaticBoxSizer* stb_sizer = new wxStaticBoxSizer(action_stb, wxVERTICAL);
int id = 0;
// all geometry
wxRadioButton* btn = new wxRadioButton(this, wxID_ANY, _L("Import geometry"), wxDefaultPosition, wxDefaultSize, id == 0 ? wxRB_GROUP : 0);
btn->SetValue(id == m_action);
btn->Bind(wxEVT_RADIOBUTTON, [this, id, contains_projects](wxCommandEvent&) {
m_action = id;
if (contains_projects) {
m_combo_project->Enable(false);
m_combo_config->Enable(false);
}
});
stb_sizer->Add(btn, 0, wxEXPAND | wxTOP, 5);
id++;
// all new window
if (instances_allowed) {
btn = new wxRadioButton(this, wxID_ANY, _L("Start new PrusaSlicer instance"), wxDefaultPosition, wxDefaultSize, id == 0 ? wxRB_GROUP : 0);
btn->SetValue(id == m_action);
btn->Bind(wxEVT_RADIOBUTTON, [this, id, contains_projects](wxCommandEvent&) {
m_action = id;
if (contains_projects) {
m_combo_project->Enable(false);
m_combo_config->Enable(false);
}
});
stb_sizer->Add(btn, 0, wxEXPAND | wxTOP, 5);
}
id++; // IMPORTANT TO ALWAYS UP THE ID EVEN IF OPTION IS NOT ADDED!
if (contains_projects) {
// one project
btn = new wxRadioButton(this, wxID_ANY, _L("Select one to load as project"), wxDefaultPosition, wxDefaultSize, id == 0 ? wxRB_GROUP : 0);
btn->SetValue(false);
btn->Bind(wxEVT_RADIOBUTTON, [this, id](wxCommandEvent&) {
m_action = id;
m_combo_project->Enable(true);
m_combo_config->Enable(false);
});
stb_sizer->Add(btn, 0, wxEXPAND | wxTOP, 5);
stb_sizer->Add(m_combo_project, 0, wxEXPAND | wxTOP, 5);
// one config
id++;
btn = new wxRadioButton(this, wxID_ANY, _L("Select one to load config only"), wxDefaultPosition, wxDefaultSize, id == 0 ? wxRB_GROUP : 0);
btn->SetValue(id == m_action);
btn->Bind(wxEVT_RADIOBUTTON, [this, id, instances_allowed](wxCommandEvent&) {
m_action = id;
if (instances_allowed)
m_combo_project->Enable(false);
m_combo_config->Enable(true);
});
stb_sizer->Add(btn, 0, wxEXPAND | wxTOP, 5);
stb_sizer->Add(m_combo_config, 0, wxEXPAND | wxTOP, 5);
}
main_sizer->Add(stb_sizer, 1, wxEXPAND | wxRIGHT | wxLEFT, 10);
wxBoxSizer* bottom_sizer = new wxBoxSizer(wxHORIZONTAL);
bottom_sizer->Add(CreateStdDialogButtonSizer(wxOK | wxCANCEL), 0, wxEXPAND | wxLEFT, 5);
main_sizer->Add(bottom_sizer, 0, wxEXPAND | wxALL, 10);
SetSizer(main_sizer);
main_sizer->SetSizeHints(this);
// Update DarkUi just for buttons
wxGetApp().UpdateDlgDarkUI(this, true);
}
void LoadProjectsDialog::on_dpi_changed(const wxRect& suggested_rect)
{
const int em = em_unit();
SetMinSize(wxSize(65 * em, 30 * em));
Fit();
Refresh();
}
bool Plater::preview_zip_archive(const boost::filesystem::path& archive_path)
{
//std::vector<fs::path> unzipped_paths;
std::vector<fs::path> non_project_paths;
std::vector<fs::path> project_paths;
try
{
mz_zip_archive archive;
mz_zip_zero_struct(&archive);
if (!open_zip_reader(&archive, archive_path.string())) {
std::string err_msg = GUI::format(_utf8("Loading of a zip archive on path %1% has failed."), archive_path.string());
throw Slic3r::FileIOError(err_msg);
}
mz_uint num_entries = mz_zip_reader_get_num_files(&archive);
mz_zip_archive_file_stat stat;
std::vector<fs::path> selected_paths;
FileArchiveDialog dlg(static_cast<wxWindow*>(wxGetApp().mainframe), &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);
fs::path archive_dir(wxStandardPaths::Get().GetTempDir().utf8_str().data());
for (mz_uint i = 0; i < num_entries; ++i) {
if (mz_zip_reader_file_stat(&archive, i, &stat)) {
wxString wname = boost::nowide::widen(stat.m_filename);
std::string name = GUI::format(wname);
fs::path archive_path(name);
for (const auto& path : selected_paths) {
if (path == archive_path) {
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 (fs::exists(archive_dir / (final_filename + extension)))
{
++version;
final_filename = just_filename + "(" + std::to_string(version) + ")";
}
filename = final_filename + extension;
fs::path final_path = archive_dir / filename;
std::string buffer((size_t)stat.m_uncomp_size, 0);
mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0);
if (res == 0) {
wxString error_log = GUI::format_wxstr(_L("Failed to unzip file to %1%: %2% "), final_path.string(), mz_zip_get_error_string(mz_zip_get_last_error(&archive)));
BOOST_LOG_TRIVIAL(error) << error_log;
show_error(nullptr, error_log);
continue;
}
fs::fstream file(final_path, std::ios::out | std::ios::binary | std::ios::trunc);
file.write(buffer.c_str(), buffer.size());
file.close();
if (!fs::exists(final_path)) {
wxString error_log = GUI::format_wxstr(_L("Failed to find unzipped file at %1%. Unzipping of file has failed."), final_path.string());
BOOST_LOG_TRIVIAL(error) << error_log;
show_error(nullptr, error_log);
continue;
}
BOOST_LOG_TRIVIAL(info) << "Unzipped " << final_path;
if (!boost::algorithm::iends_with(filename, ".3mf") && !boost::algorithm::iends_with(filename, ".amf")) {
non_project_paths.emplace_back(final_path);
continue;
}
// if 3mf - read archive headers to find project file
if ((boost::algorithm::iends_with(filename, ".3mf") && !is_project_3mf(final_path.string())) ||
(boost::algorithm::iends_with(filename, ".amf") && !boost::algorithm::iends_with(filename, ".zip.amf"))) {
non_project_paths.emplace_back(final_path);
continue;
}
project_paths.emplace_back(final_path);
}
catch (const std::exception& e)
{
// ensure the zip archive is closed and rethrow the exception
close_zip_reader(&archive);
throw Slic3r::FileIOError(e.what());
}
}
}
}
}
close_zip_reader(&archive);
if (non_project_paths.size() + project_paths.size() != selected_paths.size())
BOOST_LOG_TRIVIAL(error) << "Decompresing of archive did not retrieve all files. Expected files: "
<< selected_paths.size()
<< " Decopressed files: "
<< non_project_paths.size() + project_paths.size();
} else {
close_zip_reader(&archive);
return false;
}
}
catch (const Slic3r::FileIOError& e) {
// zip reader should be already closed or not even opened
GUI::show_error(this, e.what());
return false;
}
// none selected
if (project_paths.empty() && non_project_paths.empty())
{
return false;
}
#if 0
// 1 project, 0 models - behave like drag n drop
if (project_paths.size() == 1 && non_project_paths.empty())
{
wxArrayString aux;
aux.Add(from_u8(project_paths.front().string()));
load_files(aux);
//load_files(project_paths, true, true);
boost::system::error_code ec;
fs::remove(project_paths.front(), ec);
if (ec)
BOOST_LOG_TRIVIAL(error) << ec.message();
return true;
}
// 1 model (or more and other instances are not allowed), 0 projects - open geometry
if (project_paths.empty() && (non_project_paths.size() == 1 || wxGetApp().app_config->get("single_instance") == "1"))
{
load_files(non_project_paths, true, false);
boost::system::error_code ec;
fs::remove(non_project_paths.front(), ec);
if (ec)
BOOST_LOG_TRIVIAL(error) << ec.message();
return true;
}
bool delete_after = true;
LoadProjectsDialog dlg(project_paths);
if (dlg.ShowModal() == wxID_OK) {
LoadProjectsDialog::LoadProjectOption option = static_cast<LoadProjectsDialog::LoadProjectOption>(dlg.get_action());
switch (option)
{
case LoadProjectsDialog::LoadProjectOption::AllGeometry: {
load_files(project_paths, true, false);
load_files(non_project_paths, true, false);
break;
}
case LoadProjectsDialog::LoadProjectOption::AllNewWindow: {
delete_after = false;
for (const fs::path& path : project_paths) {
wxString f = from_path(path);
start_new_slicer(&f, false);
}
for (const fs::path& path : non_project_paths) {
wxString f = from_path(path);
start_new_slicer(&f, false);
}
break;
}
case LoadProjectsDialog::LoadProjectOption::OneProject: {
int pos = dlg.get_selected();
assert(pos >= 0 && pos < project_paths.size());
if (wxGetApp().can_load_project())
load_project(from_path(project_paths[pos]));
project_paths.erase(project_paths.begin() + pos);
load_files(project_paths, true, false);
load_files(non_project_paths, true, false);
break;
}
case LoadProjectsDialog::LoadProjectOption::OneConfig: {
int pos = dlg.get_selected();
assert(pos >= 0 && pos < project_paths.size());
std::vector<fs::path> aux;
aux.push_back(project_paths[pos]);
load_files(aux, false, true);
project_paths.erase(project_paths.begin() + pos);
load_files(project_paths, true, false);
load_files(non_project_paths, true, false);
break;
}
case LoadProjectsDialog::LoadProjectOption::Unknown:
default:
assert(false);
break;
}
}
if (!delete_after)
return true;
#else
// 1 project file and some models - behave like drag n drop of 3mf and then load models
if (project_paths.size() == 1)
{
wxArrayString aux;
aux.Add(from_u8(project_paths.front().string()));
bool loaded3mf = load_files(aux, true);
load_files(non_project_paths, true, false);
boost::system::error_code ec;
if (loaded3mf) {
fs::remove(project_paths.front(), ec);
if (ec)
BOOST_LOG_TRIVIAL(error) << ec.message();
}
for (const fs::path& path : non_project_paths) {
// Delete file from temp file (path variable), it will stay only in app memory.
boost::system::error_code ec;
fs::remove(path, ec);
if (ec)
BOOST_LOG_TRIVIAL(error) << ec.message();
}
return true;
}
// load all projects and all models as geometry
load_files(project_paths, true, false);
load_files(non_project_paths, true, false);
#endif // 0
for (const fs::path& path : project_paths) {
// Delete file from temp file (path variable), it will stay only in app memory.
boost::system::error_code ec;
fs::remove(path, ec);
if (ec)
BOOST_LOG_TRIVIAL(error) << ec.message();
}
for (const fs::path& path : non_project_paths) {
// Delete file from temp file (path variable), it will stay only in app memory.
boost::system::error_code ec;
fs::remove(path, ec);
if (ec)
BOOST_LOG_TRIVIAL(error) << ec.message();
}
return true;
}
class ProjectDropDialog : public DPIDialog
{
int m_action { 0 };
public:
enum class LoadType : unsigned char
{
Unknown,
OpenProject,
LoadGeometry,
LoadConfig,
OpenWindow
};
ProjectDropDialog(const std::string& filename);
int get_action() const { return m_action + 1; }
@ -5590,17 +6007,21 @@ ProjectDropDialog::ProjectDropDialog(const std::string& filename)
{
SetFont(wxGetApp().normal_font());
bool single_instance_only = wxGetApp().app_config->get("single_instance") == "1";
wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL);
const wxString choices[] = { _L("Open as project"),
_L("Import geometry only"),
_L("Import config only") };
wxArrayString choices;
choices.reserve(4);
choices.Add(_L("Open as project"));
choices.Add(_L("Import geometry only"));
choices.Add(_L("Import config only"));
if (!single_instance_only)
choices.Add(_L("Start new PrusaSlicer instance"));
main_sizer->Add(new wxStaticText(this, wxID_ANY,
get_wraped_wxString(_L("Select an action to apply to the file") + ": " + from_u8(filename))), 0, wxEXPAND | wxALL, 10);
m_action = std::clamp(std::stoi(wxGetApp().app_config->get("drop_project_action")),
static_cast<int>(LoadType::OpenProject), static_cast<int>(LoadType::LoadConfig)) - 1;
static_cast<int>(LoadType::OpenProject), single_instance_only? static_cast<int>(LoadType::LoadConfig) : static_cast<int>(LoadType::OpenWindow)) - 1;
wxStaticBox* action_stb = new wxStaticBox(this, wxID_ANY, _L("Action"));
if (!wxOSX) action_stb->SetBackgroundStyle(wxBG_STYLE_PAINT);
@ -5642,9 +6063,9 @@ void ProjectDropDialog::on_dpi_changed(const wxRect& suggested_rect)
Refresh();
}
bool Plater::load_files(const wxArrayString& filenames)
bool Plater::load_files(const wxArrayString& filenames, bool delete_after_load/*=false*/)
{
const std::regex pattern_drop(".*[.](stl|obj|amf|3mf|prusa|step|stp)", std::regex::icase);
const std::regex pattern_drop(".*[.](stl|obj|amf|3mf|prusa|step|stp|zip)", std::regex::icase);
const std::regex pattern_gcode_drop(".*[.](gcode|g)", std::regex::icase);
std::vector<fs::path> paths;
@ -5688,53 +6109,61 @@ bool Plater::load_files(const wxArrayString& filenames)
for (std::vector<fs::path>::const_reverse_iterator it = paths.rbegin(); it != paths.rend(); ++it) {
std::string filename = (*it).filename().string();
if (boost::algorithm::iends_with(filename, ".3mf") || boost::algorithm::iends_with(filename, ".amf")) {
LoadType load_type = LoadType::Unknown;
ProjectDropDialog::LoadType load_type = ProjectDropDialog::LoadType::Unknown;
if (!model().objects.empty()) {
if ((boost::algorithm::iends_with(filename, ".3mf") && !is_project_3mf(it->string())) ||
(boost::algorithm::iends_with(filename, ".amf") && !boost::algorithm::iends_with(filename, ".zip.amf")))
load_type = LoadType::LoadGeometry;
load_type = ProjectDropDialog::LoadType::LoadGeometry;
else {
if (wxGetApp().app_config->get("show_drop_project_dialog") == "1") {
ProjectDropDialog dlg(filename);
if (dlg.ShowModal() == wxID_OK) {
int choice = dlg.get_action();
load_type = static_cast<LoadType>(choice);
load_type = static_cast<ProjectDropDialog::LoadType>(choice);
wxGetApp().app_config->set("drop_project_action", std::to_string(choice));
}
}
else
load_type = static_cast<LoadType>(std::clamp(std::stoi(wxGetApp().app_config->get("drop_project_action")),
static_cast<int>(LoadType::OpenProject), static_cast<int>(LoadType::LoadConfig)));
load_type = static_cast<ProjectDropDialog::LoadType>(std::clamp(std::stoi(wxGetApp().app_config->get("drop_project_action")),
static_cast<int>(ProjectDropDialog::LoadType::OpenProject), static_cast<int>(ProjectDropDialog::LoadType::LoadConfig)));
}
}
else
load_type = LoadType::OpenProject;
load_type = ProjectDropDialog::LoadType::OpenProject;
if (load_type == LoadType::Unknown)
if (load_type == ProjectDropDialog::LoadType::Unknown)
return false;
switch (load_type) {
case LoadType::OpenProject: {
case ProjectDropDialog::LoadType::OpenProject: {
if (wxGetApp().can_load_project())
load_project(from_path(*it));
break;
}
case LoadType::LoadGeometry: {
case ProjectDropDialog::LoadType::LoadGeometry: {
Plater::TakeSnapshot snapshot(this, _L("Import Object"));
load_files({ *it }, true, false);
break;
}
case LoadType::LoadConfig: {
case ProjectDropDialog::LoadType::LoadConfig: {
load_files({ *it }, false, true);
break;
}
case LoadType::Unknown : {
case ProjectDropDialog::LoadType::OpenWindow: {
wxString f = from_path(*it);
start_new_slicer(&f, false, delete_after_load);
return false; // did not load anything to this instance
}
case ProjectDropDialog::LoadType::Unknown : {
assert(false);
break;
}
}
return true;
} else if (boost::algorithm::iends_with(filename, ".zip")) {
return preview_zip_archive(*it);
}
}