#include "Tab.hpp" #include "Project.hpp" #include "libslic3r/Utils.hpp" #include "libslic3r/Model.hpp" #include "libslic3r/Format/bbs_3mf.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "wxExtensions.hpp" #include "GUI_App.hpp" #include "GUI_ObjectList.hpp" #include "MainFrame.hpp" #include namespace Slic3r { namespace GUI { wxDEFINE_EVENT(EVT_PROJECT_RELOAD, wxCommandEvent); const std::vector license_list = { "BSD License", "Apache License", "GPL License", "LGPL License", "MIT License", "CC License" }; ProjectPanel::ProjectPanel(wxWindow *parent, wxWindowID id, const wxPoint &pos, const wxSize &size, long style) : wxPanel(parent, id, pos, size, style) { m_project_home_url = wxString::Format("file://%s/web/model/index.html", from_u8(resources_dir())); std::string strlang = wxGetApp().app_config->get("language"); if (strlang != "") m_project_home_url = wxString::Format("file://%s/web/model/index.html?lang=%s", from_u8(resources_dir()), strlang); wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL); m_browser = WebView::CreateWebView(this, m_project_home_url); if (m_browser == nullptr) { BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format("load web view of project page failed"); return; } //m_browser->Hide(); main_sizer->Add(m_browser, wxSizerFlags().Expand().Proportion(1)); m_browser->Bind(wxEVT_WEBVIEW_NAVIGATED, &ProjectPanel::on_navigated, this); m_browser->Bind(wxEVT_WEBVIEW_SCRIPT_MESSAGE_RECEIVED, &ProjectPanel::OnScriptMessage, this, m_browser->GetId()); Bind(EVT_PROJECT_RELOAD, &ProjectPanel::on_reload, this); SetSizer(main_sizer); Layout(); Fit(); } ProjectPanel::~ProjectPanel() {} void ProjectPanel::on_reload(wxCommandEvent& evt) { std::string update_type; std::string license; std::string model_name; std::string model_author; std::string cover_file; std::string description; std::map> files; std::string p_name; std::string p_author; std::string p_description; std::string p_cover_file; Model model = wxGetApp().plater()->model(); license = model.model_info->license; model_name = model.model_info->model_name; cover_file = model.model_info->cover_file; description = model.model_info->description; update_type = model.model_info->origin; if (model.design_info != nullptr) model_author = model.design_info->Designer; if (model.profile_info != nullptr) { p_name = model.profile_info->ProfileTile; p_description = model.profile_info->ProfileDescription; p_cover_file = model.profile_info->ProfileCover; p_author = model.profile_info->ProfileUserName; } //file info std::string file_path = encode_path(wxGetApp().plater()->model().get_auxiliary_file_temp_path().c_str()); if (!file_path.empty()) { files = Reload(file_path); } else { clear_model_info(); return; } json j; j["model"]["license"] = license; j["model"]["name"] = wxGetApp().url_encode(model_name); j["model"]["author"] = wxGetApp().url_encode(model_author);; j["model"]["cover_img"] = wxGetApp().url_encode(cover_file); j["model"]["description"] = wxGetApp().url_encode(description); j["model"]["preview_img"] = files["Model Pictures"]; j["model"]["upload_type"] = update_type; j["file"]["BOM"] = files["Bill of Materials"]; j["file"]["Assembly"] = files["Assembly Guide"]; j["file"]["Other"] = files["Others"]; j["profile"]["name"] = wxGetApp().url_encode(p_name); j["profile"]["author"] = wxGetApp().url_encode(p_author); j["profile"]["description"] = wxGetApp().url_encode(p_description); j["profile"]["cover_img"] = wxGetApp().url_encode(p_cover_file); j["profile"]["preview_img"] = files["Profile Pictures"]; json m_Res = json::object(); m_Res["command"] = "show_3mf_info"; m_Res["sequence_id"] = std::to_string(ProjectPanel::m_sequence_id++); m_Res["model"] = j; wxString strJS = wxString::Format("HandleStudio(%s)", m_Res.dump(-1, ' ', false, json::error_handler_t::ignore)); if (m_web_init_completed) { wxGetApp().CallAfter([this, strJS] { RunScript(strJS.ToStdString()); }); } } void ProjectPanel::msw_rescale() { } void ProjectPanel::on_size(wxSizeEvent &event) { event.Skip(); } void ProjectPanel::on_navigated(wxWebViewEvent& event) { event.Skip(); } void ProjectPanel::OnScriptMessage(wxWebViewEvent& evt) { try { wxString strInput = evt.GetString(); json j = json::parse(strInput); wxString strCmd = j["command"]; if (strCmd == "open_3mf_accessory") { wxString accessory_path = j["accessory_path"]; if (!accessory_path.empty()) { std::string decode_path = url_decode(accessory_path.ToStdString()); fs::path path(decode_path); if (fs::exists(path)) { wxLaunchDefaultApplication(path.wstring(), 0); } } } else if (strCmd == "request_3mf_info") { m_web_init_completed = true; } else if (strCmd == "debug_info") { //wxString msg = j["msg"]; //OutputDebugString(wxString::Format("Model_Web: msg = %s \r\n", msg)); //BOOST_LOG_TRIVIAL(info) << wxString::Format("Model_Web: msg = %s", msg); } } catch (std::exception& e) { // wxMessageBox(e.what(), "json Exception", MB_OK); } } void ProjectPanel::update_model_data() { Model model = wxGetApp().plater()->model(); clear_model_info(); //basics info if (model.model_info == nullptr) return; auto event = wxCommandEvent(EVT_PROJECT_RELOAD); event.SetEventObject(this); wxPostEvent(this, event); } void ProjectPanel::clear_model_info() { json m_Res = json::object(); m_Res["command"] = "clear_3mf_info"; m_Res["sequence_id"] = std::to_string(ProjectPanel::m_sequence_id++); wxString strJS = wxString::Format("HandleStudio(%s)", m_Res.dump(-1, ' ', false, json::error_handler_t::ignore)); wxGetApp().CallAfter([this, strJS] { RunScript(strJS.ToStdString()); }); } std::map> ProjectPanel::Reload(wxString aux_path) { std::vector dir_cache; fs::directory_iterator iter_end; wxString m_root_dir; std::map> m_paths_list; const static std::array s_default_folders = { ("Model Pictures"), ("Bill of Materials"), ("Assembly Guide"), ("Others"), //(".thumbnails"), ("Profile Pictures"), }; for (auto folder : s_default_folders) m_paths_list[folder.ToStdString()] = std::vector{}; fs::path new_aux_path(aux_path.ToStdWstring()); 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(); } 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 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()); } return m_paths_list; } // Load from new path for (fs::directory_iterator iter(new_aux_path); iter != iter_end; iter++) { wxString path = iter->path().generic_wstring(); dir_cache.push_back(iter->path()); } for (auto dir : dir_cache) { for (fs::directory_iterator iter(dir); iter != iter_end; iter++) { if (fs::is_directory(iter->path())) continue; json pfile_obj; std::string file_path = iter->path().string(); fs::path file_path_obj = fs::path(iter->path().string()); for (auto folder : s_default_folders) { auto idx = file_path.find(folder.ToStdString()); if (idx != std::string::npos) { wxStructStat strucStat; wxString file_name = encode_path(file_path.c_str()); wxStat(file_name, &strucStat); wxFileOffset filelen = strucStat.st_size; pfile_obj["filename"] = wxGetApp().url_encode(file_path_obj.filename().string().c_str()); pfile_obj["size"] = formatBytes((unsigned long)filelen); //image if (file_path_obj.extension() == ".jpg" || file_path_obj.extension() == ".jpeg" || file_path_obj.extension() == ".png" || file_path_obj.extension() == ".bmp") { wxString base64_str = to_base64(file_path); pfile_obj["filepath"] = base64_str.ToStdString(); m_paths_list[folder.ToStdString()].push_back(pfile_obj); break; } else { pfile_obj["filepath"] = wxGetApp().url_encode(file_path); m_paths_list[folder.ToStdString()].push_back(pfile_obj); break; } } } } } return m_paths_list; } std::string ProjectPanel::formatBytes(unsigned long bytes) { double dValidData = round(double(bytes) / (1024 * 1024) * 1000) / 1000; return wxString::Format("%.2fMB", dValidData).ToStdString(); } wxString ProjectPanel::to_base64(std::string file_path) { std::map base64_format; base64_format[".jpg"] = wxBITMAP_TYPE_JPEG; base64_format[".jpeg"] = wxBITMAP_TYPE_JPEG; base64_format[".png"] = wxBITMAP_TYPE_PNG; base64_format[".bmp"] = wxBITMAP_TYPE_BMP; std::string extension = file_path.substr(file_path.rfind("."), file_path.length()); auto image = new wxImage(encode_path(file_path.c_str())); wxMemoryOutputStream mem; image->SaveFile(mem, base64_format[extension]); wxString km = wxBase64Encode(mem.GetOutputStreamBuffer()->GetBufferStart(), mem.GetSize()); std::wstringstream wss; wss << L"data:image/jpg;base64,"; //wss << wxBase64Encode(km.data(), km.size()); wss << km; wxString base64_str = wss.str(); return base64_str; } void ProjectPanel::RunScript(std::string content) { WebView::RunScript(m_browser, content); } char ProjectPanel::from_hex(char ch) { return isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10; } std::string ProjectPanel::url_decode(string text) { char h; ostringstream escaped; escaped.fill('0'); for (auto i = text.begin(), n = text.end(); i != n; ++i) { string::value_type c = (*i); if (c == '%') { if (i[1] && i[2]) { h = from_hex(i[1]) << 4 | from_hex(i[2]); escaped << h; i += 2; } } else if (c == '+') { escaped << ' '; } else { escaped << c; } } return escaped.str(); } bool ProjectPanel::Show(bool show) { if (show) update_model_data(); return wxPanel::Show(show); } }} // namespace Slic3r::GUI