diff --git a/resources/localization/list.txt b/resources/localization/list.txt index 4d66c35f6c..1e3f94b9f8 100644 --- a/resources/localization/list.txt +++ b/resources/localization/list.txt @@ -78,16 +78,15 @@ src/slic3r/GUI/GUI_ObjectSettings.cpp src/slic3r/GUI/GUI_Preview.cpp src/slic3r/GUI/HintNotification.cpp src/slic3r/GUI/ImGuiWrapper.cpp -src/slic3r/GUI/Jobs/ArrangeJob.cpp src/slic3r/GUI/Jobs/ArrangeJob2.cpp src/slic3r/GUI/Jobs/EmbossJob.cpp -src/slic3r/GUI/Jobs/FillBedJob.cpp src/slic3r/GUI/Jobs/PlaterWorker.hpp src/slic3r/GUI/Jobs/RotoptimizeJob.hpp src/slic3r/GUI/Jobs/RotoptimizeJob.cpp src/slic3r/GUI/Jobs/SLAImportDialog.hpp src/slic3r/GUI/Jobs/SLAImportJob.cpp src/slic3r/GUI/KBShortcutsDialog.cpp +src/slic3r/GUI/BulkExportDialog.cpp src/slic3r/GUI/LoginDialog.cpp src/slic3r/GUI/MainFrame.cpp src/slic3r/GUI/Mouse3DController.cpp diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index d7587a26c4..3e1a8eed41 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -151,6 +151,8 @@ set(SLIC3R_GUI_SOURCES GUI/EditGCodeDialog.cpp GUI/SavePresetDialog.hpp GUI/SavePresetDialog.cpp + GUI/BulkExportDialog.hpp + GUI/BulkExportDialog.cpp GUI/PhysicalPrinterDialog.hpp GUI/PhysicalPrinterDialog.cpp GUI/GUI_Factories.cpp diff --git a/src/slic3r/GUI/BulkExportDialog.cpp b/src/slic3r/GUI/BulkExportDialog.cpp new file mode 100644 index 0000000000..452123fc6d --- /dev/null +++ b/src/slic3r/GUI/BulkExportDialog.cpp @@ -0,0 +1,219 @@ +///|/ Copyright (c) Prusa Research 2020 - 2023 Oleksandra Iushchenko @YuSanka, David Kocík @kocikdav, Enrico Turri @enricoturri1966, Vojtěch Bubník @bubnikv +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ +#include "BulkExportDialog.hpp" + +#include +#include +#include +#include + +#include +#include +#include + +#include "libslic3r/PresetBundle.hpp" + +#include "GUI.hpp" +#include "GUI_App.hpp" +#include "format.hpp" +#include "Tab.hpp" + +using Slic3r::GUI::format_wxstr; + +namespace Slic3r { +namespace GUI { + +constexpr auto BORDER_W = 10; + +//----------------------------------------------- +// BulkExportDialog::Item +//----------------------------------------------- + + +void BulkExportDialog::Item::init_input_name_ctrl(wxBoxSizer* input_path_sizer, const std::string path) +{ +#ifdef _WIN32 + long style = wxBORDER_SIMPLE; +#else + long style = 0L; +#endif + m_text_ctrl = new wxTextCtrl(m_parent, wxID_ANY, from_u8(path), wxDefaultPosition, wxSize(45 * wxGetApp().em_unit(), -1), style); + wxGetApp().UpdateDarkUI(m_text_ctrl); + m_text_ctrl->Bind(wxEVT_TEXT, [this](wxCommandEvent&) { update(); }); + + input_path_sizer->Add(m_text_ctrl, 1, wxEXPAND, BORDER_W); +} + +BulkExportDialog::Item::Item(wxWindow* parent, wxBoxSizer* sizer, PrintToExport& pte): + m_parent(parent), + m_print_to_export(&pte), + m_valid_bmp(new wxStaticBitmap(m_parent, wxID_ANY, *get_bmp_bundle("tick_mark"))), + m_valid_label(new wxStaticText(m_parent, wxID_ANY, "")) +{ + m_valid_label->SetFont(wxGetApp().bold_font()); + + wxBoxSizer* input_path_sizer = new wxBoxSizer(wxHORIZONTAL); + input_path_sizer->Add(m_valid_bmp, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, BORDER_W); + init_input_name_ctrl(input_path_sizer, pte.output_path.filename().string()); + + sizer->Add(input_path_sizer,0, wxEXPAND | wxTOP, BORDER_W); + sizer->Add(m_valid_label, 0, wxEXPAND | wxLEFT, 3*BORDER_W); + + update(); +} + +void BulkExportDialog::Item::update() +{ + m_path = into_u8(m_text_ctrl->GetValue()); + + m_valid_type = ValidationType::Valid; + wxString info_line; + + const char* unusable_symbols = "<>[]:/\\|?*\""; + + for (size_t i = 0; i < std::strlen(unusable_symbols); i++) { + if (m_path.find_first_of(unusable_symbols[i]) != std::string::npos) { + info_line = _L("The following characters are not allowed in the name") + ": " + unusable_symbols; + m_valid_type = ValidationType::NoValid; + break; + } + } + + bool existing = false;// is_existing(m_path); + if (m_valid_type == ValidationType::Valid && existing) { + info_line = _L("This name is already used, use another."); + m_valid_type = ValidationType::Warning; + } + + if (m_valid_type == ValidationType::Valid && m_path.empty()) { + info_line = _L("The name cannot be empty."); + m_valid_type = ValidationType::NoValid; + } + +#ifdef __WXMSW__ + const int max_path_length = MAX_PATH; +#else + const int max_path_length = 255; +#endif + + if (m_valid_type == ValidationType::Valid && m_path.length() >= max_path_length) { + info_line = _L("The name is too long."); + m_valid_type = ValidationType::NoValid; + } + + if (m_valid_type == ValidationType::Valid && m_path.find_first_of(' ') == 0) { + info_line = _L("The name cannot start with space character."); + m_valid_type = ValidationType::NoValid; + } + + if (m_valid_type == ValidationType::Valid && m_path.find_last_of(' ') == m_path.length()-1) { + info_line = _L("The name cannot end with space character."); + m_valid_type = ValidationType::NoValid; + } + + m_valid_label->SetLabel(info_line); + m_valid_label->Show(!info_line.IsEmpty()); + + update_valid_bmp(); + + if (is_valid()) { + boost::filesystem::path field = m_print_to_export->output_path.parent_path(); + m_print_to_export->output_path = field / m_path; + } + + m_parent->Layout(); +} + +void BulkExportDialog::Item::update_valid_bmp() +{ + std::string bmp_name = m_valid_type == ValidationType::Warning ? "exclamation_manifold" : + m_valid_type == ValidationType::NoValid ? "exclamation" : "tick_mark" ; + m_valid_bmp->SetBitmap(*get_bmp_bundle(bmp_name)); +} + +//----------------------------------------------- +// BulkExportDialog +//----------------------------------------------- + +BulkExportDialog::BulkExportDialog(wxWindow* parent, std::vector& exports) + : DPIDialog(parent, wxID_ANY, exports.size() == 1 ? _L("Save bed") : _L("Save beds"), + wxDefaultPosition, wxSize(45 * wxGetApp().em_unit(), 5 * wxGetApp().em_unit()), wxDEFAULT_DIALOG_STYLE | wxICON_WARNING) +{ + this->SetFont(wxGetApp().normal_font()); + +#ifndef __WXMSW__ + SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); +#endif // __WXMSW__ + + wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); + + m_sizer = new wxBoxSizer(wxVERTICAL); + + for (PrintToExport& exp : exports) + AddItem(exp); + + // Add dialog's buttons + wxStdDialogButtonSizer* btns = this->CreateStdDialogButtonSizer(wxOK | wxCANCEL); + wxButton* btnOK = static_cast(this->FindWindowById(wxID_OK, this)); + btnOK->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(enable_ok_btn()); }); + + topSizer->Add(m_sizer, 0, wxEXPAND | wxALL, BORDER_W); + + topSizer->Add(btns, 0, wxEXPAND | wxALL, BORDER_W); + + SetSizer(topSizer); + topSizer->SetSizeHints(this); + + this->CenterOnScreen(); + +#ifdef _WIN32 + wxGetApp().UpdateDlgDarkUI(this); +#endif +} + +BulkExportDialog::~BulkExportDialog() +{ + for (auto item : m_items) + delete item; +} + +void BulkExportDialog::AddItem(PrintToExport& pte) +{ + m_items.emplace_back(new Item{ this, m_sizer, pte }); +} + +bool BulkExportDialog::enable_ok_btn() const +{ + for (const Item* item : m_items) + if (!item->is_valid()) + return false; + + return true; +} + +bool BulkExportDialog::Layout() +{ + const bool ret = DPIDialog::Layout(); + this->Fit(); + return ret; +} + +void BulkExportDialog::on_dpi_changed(const wxRect& suggested_rect) +{ + const int& em = em_unit(); + + msw_buttons_rescale(this, em, { wxID_OK, wxID_CANCEL }); + + for (Item* item : m_items) + item->update_valid_bmp(); + + const wxSize& size = wxSize(65 * em, 35 * em); + SetMinSize(size); + + Fit(); + Refresh(); +} + +}} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/BulkExportDialog.hpp b/src/slic3r/GUI/BulkExportDialog.hpp new file mode 100644 index 0000000000..cc526000b8 --- /dev/null +++ b/src/slic3r/GUI/BulkExportDialog.hpp @@ -0,0 +1,94 @@ +///|/ Copyright (c) Prusa Research 2020 - 2023 Oleksandra Iushchenko @YuSanka, David Kocík @kocikdav, Lukáš Matěna @lukasmatena +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ +#pragma once + +//#include + +#include +#include "wxExtensions.hpp" +#include "GUI_Utils.hpp" + +class wxString; +class wxStaticText; +class wxTextCtrl; +class wxStaticBitmap; + +namespace Slic3r { + +class Print; +struct GCodeProcessorResult; + +namespace GUI { + +struct PrintToExport { + std::reference_wrapper print; + std::reference_wrapper processor_result; + boost::filesystem::path output_path; +}; + +class BulkExportDialog : public DPIDialog +{ +public: + struct Item + { + enum class ValidationType + { + Valid, + NoValid, + Warning + }; + + // Item as a separate control(f.e. as a part of ConfigWizard to check name of the new custom priter) + Item(wxWindow* parent, wxBoxSizer* sizer, PrintToExport& path); + + void update_valid_bmp(); + bool is_valid() const { return m_valid_type != ValidationType::NoValid; } + + private: + std::string m_path; + PrintToExport* m_print_to_export { nullptr }; + + ValidationType m_valid_type {ValidationType::NoValid}; + wxWindow* m_parent {nullptr}; + wxStaticBitmap* m_valid_bmp {nullptr}; + wxTextCtrl* m_text_ctrl {nullptr}; + wxStaticText* m_valid_label {nullptr}; + + + void init_input_name_ctrl(wxBoxSizer *input_name_sizer, std::string path); + void update(); + }; + +private: + std::vector m_items; + + std::vector* m_exports {nullptr}; + + wxBoxSizer* m_sizer {nullptr}; + wxStaticText* m_label {nullptr}; + + std::string m_ph_printer_name; + std::string m_old_preset_name; + wxString m_info_line_extention{wxEmptyString}; + +public: + + BulkExportDialog(wxWindow* parent, std::vector& exports); + ~BulkExportDialog() override; + bool Layout() override; + + void AddItem(PrintToExport& pte); + +protected: + void on_dpi_changed(const wxRect& suggested_rect) override; + void on_sys_color_changed() override {} + +private: + bool enable_ok_btn() const; +}; + +} + +} diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 55006fb22b..8e1a974863 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -133,6 +133,7 @@ #include "WebViewPanel.hpp" #include "ConfigWizardWebViewPage.hpp" #include "PresetArchiveDatabase.hpp" +#include "BulkExportDialog.hpp" #ifdef __APPLE__ #include "Gizmos/GLGizmosManager.hpp" @@ -5922,11 +5923,6 @@ void Plater::export_gcode_to_path( appconfig.update_last_output_dir(output_path.parent_path().string(), path_on_removable_media); } -struct PrintToExport{ std::reference_wrapper print; - std::reference_wrapper processor_result; - fs::path output_path; -}; - void Plater::export_all_gcodes(bool prefer_removable) { const auto optional_default_output_file{this->get_default_output_file()}; if (!optional_default_output_file) { @@ -5958,11 +5954,10 @@ void Plater::export_all_gcodes(bool prefer_removable) { prints_to_export.push_back({*print, this->p->gcode_results[print_index], output_file}); } - //BulkExportDialog dialog{prints_to_export}; - //if (dialog.ShowModal() != wxID_OK) { - // return; - //} - //prints_to_export = dialog.get_prints_to_export(); + BulkExportDialog dialog(nullptr, prints_to_export); + if (dialog.ShowModal() != wxID_OK) { + return; + } bool path_on_removable_media{false}; for (const PrintToExport &print_to_export : prints_to_export) {