From 524efcfcca6480e3635e09e8d70b77ce0ae5e762 Mon Sep 17 00:00:00 2001 From: SoftFever Date: Tue, 28 May 2024 00:18:13 +0800 Subject: [PATCH] support cura:// and thingiverse --- src/libslic3r/Utils.hpp | 6 ++++ src/slic3r/GUI/DownloaderFileGet.cpp | 47 ++++++++++++++++++++++------ src/slic3r/GUI/Preferences.cpp | 28 ++++++++++++----- src/slic3r/GUI/Preferences.hpp | 2 +- 4 files changed, 66 insertions(+), 17 deletions(-) diff --git a/src/libslic3r/Utils.hpp b/src/libslic3r/Utils.hpp index 8a7d2467a2e..a642d6d3505 100644 --- a/src/libslic3r/Utils.hpp +++ b/src/libslic3r/Utils.hpp @@ -239,6 +239,12 @@ inline bool is_thingiverse_link(const std::string& url) { const std::regex url_regex("(http|https)://www.thingiverse.com", std::regex_constants::icase); return std::regex_match(url, url_regex); } + +// sanitize a string to be used as a filename +inline std::string sanitize_filename(const std::string &filename){ + const std::regex special_chars("[/\\\\:*?\"<>|]"); + return std::regex_replace(filename, special_chars, "_"); +} // File path / name / extension splitting utilities, working with UTF-8, // to be published to Perl. namespace PerlUtils { diff --git a/src/slic3r/GUI/DownloaderFileGet.cpp b/src/slic3r/GUI/DownloaderFileGet.cpp index 2389d379dba..469774653e0 100644 --- a/src/slic3r/GUI/DownloaderFileGet.cpp +++ b/src/slic3r/GUI/DownloaderFileGet.cpp @@ -15,6 +15,7 @@ #include "format.hpp" #include "GUI.hpp" #include "I18N.hpp" +#include "libslic3r/Utils.hpp" namespace Slic3r { namespace GUI { @@ -124,7 +125,15 @@ FileGet::priv::priv(int ID, std::string&& url, const std::string& filename, wxEv , m_dest_folder(dest_folder) { } - +std::string extract_remote_filename(const std::string& str) { + std::regex r("filename=\"([^\"]*)\""); + std::smatch match; + if (std::regex_search(str.begin(), str.end(), match, r)) { + return match.str(1); + } else { + return ""; + } +} void FileGet::priv::get_perform() { assert(m_evt_handler); @@ -135,10 +144,11 @@ void FileGet::priv::get_perform() m_stopped = false; // open dest file + std::string extension; if (m_written == 0) { boost::filesystem::path dest_path = m_dest_folder / m_filename; - std::string extension = boost::filesystem::extension(dest_path); + extension = dest_path.extension().string(); std::string just_filename = m_filename.substr(0, m_filename.size() - extension.size()); std::string final_filename = just_filename; // Find unsed filename @@ -164,10 +174,10 @@ void FileGet::priv::get_perform() m_evt_handler->QueueEvent(evt); return; } - - m_filename = final_filename + extension; - m_tmp_path = m_dest_folder / (m_filename + "." + std::to_string(get_current_pid()) + ".download"); + m_filename = sanitize_filename(final_filename + extension); + + m_tmp_path = m_dest_folder / (m_filename + "." + std::to_string(get_current_pid()) + ".download"); wxCommandEvent* evt = new wxCommandEvent(EVT_DWNLDR_FILE_NAME_CHANGE); evt->SetString(boost::nowide::widen(m_filename)); @@ -175,7 +185,9 @@ void FileGet::priv::get_perform() m_evt_handler->QueueEvent(evt); } - boost::filesystem::path dest_path = m_dest_folder / m_filename; + boost::filesystem::path dest_path; + if(!extension.empty()) + dest_path = m_dest_folder / m_filename; wxString temp_path_wstring(m_tmp_path.wstring()); @@ -208,6 +220,19 @@ void FileGet::priv::get_perform() Http::get(m_url) .size_limit(DOWNLOAD_SIZE_LIMIT) //more? .set_range(range_string) + .on_header_callback([&](std::string header) { + if(dest_path.empty()) { + std::string filename = extract_remote_filename(header); + if (!filename.empty()) { + m_filename = filename; + dest_path = m_dest_folder / m_filename; + wxCommandEvent* evt = new wxCommandEvent(EVT_DWNLDR_FILE_NAME_CHANGE); + evt->SetString(boost::nowide::widen(m_filename)); + evt->SetInt(m_id); + m_evt_handler->QueueEvent(evt); + } + } + }) .on_progress([&](Http::Progress progress, bool& cancel) { // to prevent multiple calls into following ifs (m_cancel / m_pause) if (m_stopped){ @@ -292,14 +317,18 @@ void FileGet::priv::get_perform() //} try { - /* + // Orca: thingiverse need this if (m_written < body.size()) { // this code should never be entered. As there should be on_progress call after last bit downloaded. std::string part_for_write = body.substr(m_written); fwrite(part_for_write.c_str(), 1, part_for_write.size(), file); - } - */ + + wxCommandEvent* evt = new wxCommandEvent(EVT_DWNLDR_FILE_PROGRESS); + evt->SetString(std::to_string(100)); + evt->SetInt(m_id); + m_evt_handler->QueueEvent(evt); + } fclose(file); boost::filesystem::rename(m_tmp_path, dest_path); } diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp index 7ce8a7353ff..a70b45dd4a1 100644 --- a/src/slic3r/GUI/Preferences.cpp +++ b/src/slic3r/GUI/Preferences.cpp @@ -746,7 +746,7 @@ wxBoxSizer *PreferencesDialog::create_item_checkbox(wxString title, wxWindow *pa } wxBoxSizer* PreferencesDialog::create_item_button( - wxString title, wxString title2, wxWindow* parent, wxString tooltip, wxString tooltip2, std::function onclick) + wxString title, wxString title2, wxWindow* parent, wxString tooltip, wxString tooltip2, std::function onclick, bool button_on_left/* = false*/) { wxBoxSizer *m_sizer_checkbox = new wxBoxSizer(wxHORIZONTAL); @@ -776,8 +776,13 @@ wxBoxSizer* PreferencesDialog::create_item_button( m_button_download->Bind(wxEVT_BUTTON, [this, onclick](auto &e) { onclick(); }); - m_sizer_checkbox->Add(m_staticTextPath, 0, wxALIGN_CENTER_VERTICAL | wxALL, FromDIP(5)); - m_sizer_checkbox->Add(m_button_download, 0, wxALL, FromDIP(5)); + if (button_on_left) { + m_sizer_checkbox->Add(m_button_download, 0, wxALL, FromDIP(5)); + m_sizer_checkbox->Add(m_staticTextPath, 0, wxALIGN_CENTER_VERTICAL | wxALL, FromDIP(5)); + } else { + m_sizer_checkbox->Add(m_staticTextPath, 0, wxALIGN_CENTER_VERTICAL | wxALL, FromDIP(5)); + m_sizer_checkbox->Add(m_button_download, 0, wxALL, FromDIP(5)); + } return m_sizer_checkbox; } @@ -1059,6 +1064,7 @@ wxWindow* PreferencesDialog::create_general_page() #ifdef _WIN32 auto title_associate_file = create_item_title(_L("Associate files to OrcaSlicer"), page, _L("Associate files to OrcaSlicer")); + auto title_associate_url = create_item_title(_L("Associate web links to OrcaSlicer"), page, _L("Associate URLs to OrcaSlicer")); // associate file auto item_associate_3mf = create_item_checkbox(_L("Associate .3mf files to OrcaSlicer"), page, @@ -1076,12 +1082,18 @@ wxWindow* PreferencesDialog::create_general_page() auto associate_url_prusaslicer = create_item_button(_L("Current association: ") + reg_bin, _L("Associate prusaslicer://"), page, reg_bin.empty() ? _L("Not associated to any application") : reg_bin, _L("Associate OrcaSlicer with prusaslicer:// links so that Orca can open models from Printable.com"), - []() { wxGetApp().associate_url(L"prusaslicer"); }); + []() { wxGetApp().associate_url(L"prusaslicer"); }, false); wxGetApp().check_url_association(L"bambustudio", reg_bin); auto associate_url_bambustudio = create_item_button(_L("Current association: ") + reg_bin, _L("Associate bambustudio://"), page, reg_bin.empty() ? _L("Not associated to any application") : reg_bin, _L("Associate OrcaSlicer with bambustudio:// links so that Orca can open models from makerworld.com"), - []() { wxGetApp().associate_url(L"bambustudio"); }); + []() { wxGetApp().associate_url(L"bambustudio"); }, false); + + wxGetApp().check_url_association(L"cura", reg_bin); + auto associate_url_cura = create_item_button(_L("Current association: ") + reg_bin, _L("Associate cura://"), page, + reg_bin.empty() ? _L("Not associated to any application") : reg_bin, + _L("Associate OrcaSlicer with cura:// links so that Orca can open models from thingiverse.com"), + []() { wxGetApp().associate_url(L"cura"); }, false); #endif // auto title_modelmall = create_item_title(_L("Online Models"), page, _L("Online Models")); @@ -1147,8 +1159,10 @@ wxWindow* PreferencesDialog::create_general_page() sizer_page->Add(item_associate_step, 0, wxTOP, FromDIP(3)); #endif // _WIN32 #if !defined(__APPLE__) - sizer_page->Add(associate_url_prusaslicer, 0, wxTOP | wxEXPAND, FromDIP(20)); - sizer_page->Add(associate_url_bambustudio, 0, wxTOP | wxEXPAND, FromDIP(20)); + sizer_page->Add(title_associate_url, 0, wxTOP| wxEXPAND, FromDIP(20)); + sizer_page->Add(associate_url_prusaslicer, 0, wxTOP, FromDIP(3)); + sizer_page->Add(associate_url_bambustudio, 0, wxTOP, FromDIP(3)); + sizer_page->Add(associate_url_cura, 0, wxTOP, FromDIP(3)); #endif // auto item_title_modelmall = sizer_page->Add(title_modelmall, 0, wxTOP | wxEXPAND, FromDIP(20)); // auto item_item_modelmall = sizer_page->Add(item_modelmall, 0, wxTOP, FromDIP(3)); diff --git a/src/slic3r/GUI/Preferences.hpp b/src/slic3r/GUI/Preferences.hpp index 63351961860..6cf6deaf668 100644 --- a/src/slic3r/GUI/Preferences.hpp +++ b/src/slic3r/GUI/Preferences.hpp @@ -111,7 +111,7 @@ class PreferencesDialog : public DPIDialog wxBoxSizer *create_item_checkbox(wxString title, wxWindow *parent, wxString tooltip, int padding_left, std::string param); wxBoxSizer *create_item_darkmode_checkbox(wxString title, wxWindow *parent, wxString tooltip, int padding_left, std::string param); void set_dark_mode(); - wxBoxSizer *create_item_button(wxString title, wxString title2, wxWindow *parent, wxString tooltip, wxString tooltip2, std::function onclick); + wxBoxSizer *create_item_button(wxString title, wxString title2, wxWindow *parent, wxString tooltip, wxString tooltip2, std::function onclick, bool button_on_left = false); wxWindow* create_item_downloads(wxWindow* parent, int padding_left, std::string param); wxBoxSizer *create_item_input(wxString title, wxString title2, wxWindow *parent, wxString tooltip, std::string param, std::function onchange = {}); wxBoxSizer *create_item_backup_input(wxString title, wxWindow *parent, wxString tooltip, std::string param);