diff --git a/resources/gallery/system/OTHER_recycling_symbol.png b/resources/gallery/system/OTHER_recycling_symbol.png new file mode 100644 index 0000000000..6fb36423bc Binary files /dev/null and b/resources/gallery/system/OTHER_recycling_symbol.png differ diff --git a/resources/gallery/system/OTHER_recycling_symbol.stl b/resources/gallery/system/OTHER_recycling_symbol.stl new file mode 100644 index 0000000000..be78639318 Binary files /dev/null and b/resources/gallery/system/OTHER_recycling_symbol.stl differ diff --git a/resources/gallery/system/PETG_recycling_symbol.png b/resources/gallery/system/PETG_recycling_symbol.png new file mode 100644 index 0000000000..1e4b55f7a8 Binary files /dev/null and b/resources/gallery/system/PETG_recycling_symbol.png differ diff --git a/resources/gallery/system/PETG_recycling_symbol.stl b/resources/gallery/system/PETG_recycling_symbol.stl new file mode 100644 index 0000000000..8e24f93eab Binary files /dev/null and b/resources/gallery/system/PETG_recycling_symbol.stl differ diff --git a/resources/gallery/system/box.png b/resources/gallery/system/box.png new file mode 100644 index 0000000000..4dd65eae86 Binary files /dev/null and b/resources/gallery/system/box.png differ diff --git a/resources/gallery/system/box.stl b/resources/gallery/system/box.stl new file mode 100644 index 0000000000..311e550dd0 Binary files /dev/null and b/resources/gallery/system/box.stl differ diff --git a/resources/gallery/system/bunny.png b/resources/gallery/system/bunny.png new file mode 100644 index 0000000000..611211855a Binary files /dev/null and b/resources/gallery/system/bunny.png differ diff --git a/resources/gallery/system/bunny.stl b/resources/gallery/system/bunny.stl new file mode 100644 index 0000000000..94a9566c3c Binary files /dev/null and b/resources/gallery/system/bunny.stl differ diff --git a/resources/gallery/system/cylinder.stl b/resources/gallery/system/cylinder.stl new file mode 100644 index 0000000000..b4e159192e Binary files /dev/null and b/resources/gallery/system/cylinder.stl differ diff --git a/resources/gallery/system/pepa_razitko4_scaled.stl b/resources/gallery/system/pepa_razitko4_scaled.stl new file mode 100644 index 0000000000..676ba9e575 Binary files /dev/null and b/resources/gallery/system/pepa_razitko4_scaled.stl differ diff --git a/resources/gallery/system/pyramid.png b/resources/gallery/system/pyramid.png new file mode 100644 index 0000000000..d07ddf5866 Binary files /dev/null and b/resources/gallery/system/pyramid.png differ diff --git a/resources/gallery/system/pyramid.stl b/resources/gallery/system/pyramid.stl new file mode 100644 index 0000000000..83269cd620 Binary files /dev/null and b/resources/gallery/system/pyramid.stl differ diff --git a/resources/gallery/system/sphere.png b/resources/gallery/system/sphere.png new file mode 100644 index 0000000000..6d9cc9de26 Binary files /dev/null and b/resources/gallery/system/sphere.png differ diff --git a/resources/gallery/system/sphere.stl b/resources/gallery/system/sphere.stl new file mode 100644 index 0000000000..ca1ef24f5c Binary files /dev/null and b/resources/gallery/system/sphere.stl differ diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp index 3f43c3ebdb..45072b1073 100644 --- a/src/PrusaSlicer.cpp +++ b/src/PrusaSlicer.cpp @@ -661,6 +661,7 @@ bool CLI::setup(int argc, char **argv) set_resources_dir(path_resources.string()); set_var_dir((path_resources / "icons").string()); set_local_dir((path_resources / "localization").string()); + set_gallery_dir((path_resources / "gallery").string()); // Parse all command line options into a DynamicConfig. // If any option is unsupported, print usage and abort immediately. diff --git a/src/libslic3r/Utils.hpp b/src/libslic3r/Utils.hpp index 0979d7f764..5c3ed684a2 100644 --- a/src/libslic3r/Utils.hpp +++ b/src/libslic3r/Utils.hpp @@ -45,6 +45,11 @@ void set_local_dir(const std::string &path); // Return a full path to the localization directory. const std::string& localization_dir(); +// Set a path with shapes gallery files. +void set_gallery_dir(const std::string &path); +// Return a full path to the gallery directory. +const std::string& gallery_dir(); + // Set a path with preset files. void set_data_dir(const std::string &path); // Return a full path to the GUI resource files. @@ -91,6 +96,9 @@ extern bool is_plain_file(const boost::filesystem::directory_entry &path); extern bool is_ini_file(const boost::filesystem::directory_entry &path); extern bool is_idx_file(const boost::filesystem::directory_entry &path); extern bool is_gcode_file(const std::string &path); +extern bool is_img_file(const std::string& path); +extern bool is_stl_file(const boost::filesystem::directory_entry& path); +extern bool is_stl_file(const std::string& path); // File path / name / extension splitting utilities, working with UTF-8, // to be published to Perl. diff --git a/src/libslic3r/utils.cpp b/src/libslic3r/utils.cpp index dfc4f5a3eb..f40e7de892 100644 --- a/src/libslic3r/utils.cpp +++ b/src/libslic3r/utils.cpp @@ -175,6 +175,18 @@ const std::string& localization_dir() return g_local_dir; } +static std::string g_gallery_dir; + +void set_gallery_dir(const std::string &dir) +{ + g_gallery_dir = dir; +} + +const std::string& gallery_dir() +{ + return g_gallery_dir; +} + // Translate function callback, to call wxWidgets translate function to convert non-localized UTF8 string to a localized one. Slic3r::I18N::translate_fn_type Slic3r::I18N::translate_fn = nullptr; @@ -744,6 +756,21 @@ bool is_gcode_file(const std::string &path) boost::iends_with(path, ".g") || boost::iends_with(path, ".ngc"); } +bool is_img_file(const std::string &path) +{ + return boost::iends_with(path, ".png") || boost::iends_with(path, ".svg"); +} + +bool is_stl_file(const boost::filesystem::directory_entry& dir_entry) +{ + return is_plain_file(dir_entry) && strcasecmp(dir_entry.path().extension().string().c_str(), ".stl") == 0; +} + +bool is_stl_file(const std::string &path) +{ + return boost::iends_with(path, ".stl"); +} + } // namespace Slic3r #ifdef WIN32 diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 76fd8d9891..64d09674cd 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -101,6 +101,8 @@ set(SLIC3R_GUI_SOURCES GUI/GUI_Factories.hpp GUI/GUI_ObjectList.cpp GUI/GUI_ObjectList.hpp + GUI/GalleryDialog.cpp + GUI/GalleryDialog.hpp GUI/GUI_ObjectManipulation.cpp GUI/GUI_ObjectManipulation.hpp GUI/GUI_ObjectSettings.cpp diff --git a/src/slic3r/GUI/GUI_Factories.cpp b/src/slic3r/GUI/GUI_Factories.cpp index c4782615ce..1dfa0eabb6 100644 --- a/src/slic3r/GUI/GUI_Factories.cpp +++ b/src/slic3r/GUI/GUI_Factories.cpp @@ -433,6 +433,12 @@ wxMenu* MenuFactory::append_submenu_add_generic(wxMenu* menu, ModelVolumeType ty [type, item](wxCommandEvent&) { obj_list()->load_generic_subobject(item, type); }, "", menu); } + if (wxGetApp().get_mode() == comExpert && type != ModelVolumeType::INVALID) { + sub_menu->AppendSeparator(); + append_menu_item(sub_menu, wxID_ANY, _L("Gallery"), "", + [type](wxCommandEvent&) { obj_list()->load_subobject(type, true); }, "", menu); + } + return sub_menu; } diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 443ef7815e..c9ad247786 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -8,6 +8,7 @@ #include "I18N.hpp" #include "Plater.hpp" #include "BitmapComboBox.hpp" +#include "GalleryDialog.hpp" #if ENABLE_PROJECT_DIRTY_STATE #include "MainFrame.hpp" #endif // ENABLE_PROJECT_DIRTY_STATE @@ -1337,7 +1338,7 @@ bool ObjectList::is_instance_or_object_selected() return selection.is_single_full_instance() || selection.is_single_full_object(); } -void ObjectList::load_subobject(ModelVolumeType type) +void ObjectList::load_subobject(ModelVolumeType type, bool from_galery/* = false*/) { wxDataViewItem item = GetSelection(); // we can add volumes for Object or Instance @@ -1351,10 +1352,14 @@ void ObjectList::load_subobject(ModelVolumeType type) if (m_objects_model->GetItemType(item)&itInstance) item = m_objects_model->GetItemById(obj_idx); + std::vector volumes; + load_part((*m_objects)[obj_idx], volumes, type, from_galery); + + if (volumes.empty()) + return; + take_snapshot(_L("Load Part")); - std::vector volumes; - load_part((*m_objects)[obj_idx], volumes, type); wxDataViewItemArray items = reorder_volumes_and_get_selection(obj_idx, [volumes](const ModelVolume* volume) { return std::find(volumes.begin(), volumes.end(), volume) != volumes.end(); }); @@ -1371,12 +1376,22 @@ void ObjectList::load_subobject(ModelVolumeType type) selection_changed(); } -void ObjectList::load_part(ModelObject* model_object, std::vector& added_volumes, ModelVolumeType type) +void ObjectList::load_part(ModelObject* model_object, std::vector& added_volumes, ModelVolumeType type, bool from_galery/* = false*/) { wxWindow* parent = wxGetApp().tab_panel()->GetPage(0); wxArrayString input_files; - wxGetApp().import_model(parent, input_files); + + if (from_galery) { + GalleryDialog dlg(this); + if (dlg.ShowModal() == wxID_CANCEL) + return; + dlg.get_input_files(input_files); + if (input_files.IsEmpty()) + return; + } + else + wxGetApp().import_model(parent, input_files); wxProgressDialog dlg(_L("Loading") + dots, "", 100, wxGetApp().plater(), wxPD_AUTO_HIDE); wxBusyCursor busy; diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index 36b399e515..590bbf429c 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -238,8 +238,8 @@ public: void show_settings(const wxDataViewItem settings_item); bool is_instance_or_object_selected(); - void load_subobject(ModelVolumeType type); - void load_part(ModelObject* model_object, std::vector &added_volumes, ModelVolumeType type); + void load_subobject(ModelVolumeType type, bool from_galery = false); + void load_part(ModelObject* model_object, std::vector &added_volumes, ModelVolumeType type, bool from_galery = false); void load_generic_subobject(const std::string& type_name, const ModelVolumeType type); void load_shape_object(const std::string &type_name); void load_mesh_object(const TriangleMesh &mesh, const wxString &name, bool center = true); diff --git a/src/slic3r/GUI/GalleryDialog.cpp b/src/slic3r/GUI/GalleryDialog.cpp new file mode 100644 index 0000000000..bba7b2436a --- /dev/null +++ b/src/slic3r/GUI/GalleryDialog.cpp @@ -0,0 +1,404 @@ +#include "GalleryDialog.hpp" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "GUI.hpp" +#include "GUI_App.hpp" +#include "format.hpp" +#include "wxExtensions.hpp" +#include "I18N.hpp" +#include "libslic3r/Utils.hpp" +#include "libslic3r/AppConfig.hpp" +#include +#include "Notebook.hpp" + +namespace Slic3r { +namespace GUI { + +#define BORDER_W 10 +#define IMG_PX_CNT 64 + +namespace fs = boost::filesystem; + +// Gallery::DropTarget +class GalleryDropTarget : public wxFileDropTarget +{ +public: + GalleryDropTarget(GalleryDialog* gallery_dlg) : gallery_dlg(gallery_dlg) { this->SetDefaultAction(wxDragCopy); } + + bool OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filenames) override; + +private: + GalleryDialog* gallery_dlg {nullptr}; +}; + +bool GalleryDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filenames) +{ +#ifdef WIN32 + // hides the system icon + this->MSWUpdateDragImageOnLeave(); +#endif // WIN32 + return gallery_dlg ? gallery_dlg->load_files(filenames) : false; +} + + +GalleryDialog::GalleryDialog(wxWindow* parent) : + DPIDialog(parent, wxID_ANY, _L("Shapes Gallery"), wxDefaultPosition, wxSize(45 * wxGetApp().em_unit(), -1), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) +{ +#ifndef _WIN32 + SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); +#endif + SetFont(wxGetApp().normal_font()); + + wxStaticText* label_top = new wxStaticText(this, wxID_ANY, _L("Select shape from the gallery") + ":"); + + m_list_ctrl = new wxListCtrl(this, wxID_ANY, wxDefaultPosition, wxSize(55 * wxGetApp().em_unit(), 35 * wxGetApp().em_unit()), + wxLC_ICON | wxLC_NO_HEADER | wxLC_ALIGN_TOP | wxSIMPLE_BORDER); + m_list_ctrl->Bind(wxEVT_LIST_ITEM_SELECTED, &GalleryDialog::select, this); + m_list_ctrl->Bind(wxEVT_LIST_ITEM_DESELECTED, &GalleryDialog::deselect, this); + m_list_ctrl->Bind(wxEVT_LIST_ITEM_ACTIVATED, [this](wxListEvent& event) { + m_selected_items.clear(); + select(event); + this->EndModal(wxID_OK); + }); + this->Bind(wxEVT_SIZE, [this](wxSizeEvent& event) { + event.Skip(); + m_list_ctrl->Arrange(); + }); + + wxStdDialogButtonSizer* buttons = this->CreateStdDialogButtonSizer(wxOK | wxCANCEL); + + auto add_btn = [this, buttons]( size_t pos, int& ID, wxString title, wxString tooltip, + void (GalleryDialog::* method)(wxEvent&), + std::function enable_fn = []() {return true; }) { + ID = NewControlId(); + wxButton* btn = new wxButton(this, ID, title); + btn->SetToolTip(tooltip); + btn->Bind(wxEVT_UPDATE_UI, [enable_fn](wxUpdateUIEvent& evt) { evt.Enable(enable_fn()); }); + buttons->Insert(pos, btn, 0, wxRIGHT, BORDER_W); + this->Bind(wxEVT_BUTTON, method, this, ID); + }; + + auto enable_del_fn = [this]() { + if (m_selected_items.empty()) + return false; + for (const Item& item : m_selected_items) + if (item.is_system) + return false; + return true; + }; + + add_btn(0, ID_BTN_ADD_CUSTOM_SHAPE, _L("Add"), _L("Add one or more custom shapes"), &GalleryDialog::add_custom_shapes); + add_btn(1, ID_BTN_DEL_CUSTOM_SHAPE, _L("Delete"), _L("Delete one or more custom shape. You can't delete system shapes"), &GalleryDialog::del_custom_shapes, enable_del_fn); + add_btn(2, ID_BTN_REPLACE_CUSTOM_PNG, _L("Replace PNG"), _L("Replace PNG for custom shape. You can't raplace PNG for system shape"),&GalleryDialog::replace_custom_png, [this]() { return (m_selected_items.size() == 1 && !m_selected_items[0].is_system); }); + buttons->InsertStretchSpacer(3, 2* BORDER_W); + + load_label_icon_list(); + + wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); + + topSizer->Add(label_top , 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, BORDER_W); + topSizer->Add(m_list_ctrl, 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, BORDER_W); + topSizer->Add(buttons , 0, wxEXPAND | wxALL, BORDER_W); + + SetSizer(topSizer); + topSizer->SetSizeHints(this); + + wxGetApp().UpdateDlgDarkUI(this); + this->CenterOnScreen(); + + this->SetDropTarget(new GalleryDropTarget(this)); +} + +GalleryDialog::~GalleryDialog() +{ +} + +void GalleryDialog::on_dpi_changed(const wxRect& suggested_rect) +{ + const int& em = em_unit(); + + msw_buttons_rescale(this, em, { ID_BTN_ADD_CUSTOM_SHAPE, ID_BTN_DEL_CUSTOM_SHAPE, ID_BTN_REPLACE_CUSTOM_PNG, wxID_OK, wxID_CANCEL }); + + wxSize size = wxSize(55 * em, 35 * em); + m_list_ctrl->SetMinSize(size); + m_list_ctrl->SetSize(size); + + Fit(); + Refresh(); +} + +static void add_border(wxImage& image) +{ + const wxColour& clr = wxGetApp().get_color_hovered_btn_label(); + + auto px_data = (uint8_t*)image.GetData(); + auto a_data = (uint8_t*)image.GetAlpha(); + + int width = image.GetWidth(); + int height = image.GetHeight(); + int border_width = 2; + + for (size_t x = 0; x < width; ++x) { + for (size_t y = 0; y < height; ++y) { + if (x < border_width || y < border_width || + x >= (width - border_width) || y >= (height - border_width)) { + const size_t idx = (x + y * width); + const size_t idx_rgb = (x + y * width) * 3; + px_data[idx_rgb] = clr.Red(); + px_data[idx_rgb + 1] = clr.Green(); + px_data[idx_rgb + 2] = clr.Blue(); + if (a_data) + a_data[idx] = 255u; + } + } + } +} + +static void add_def_img(wxImageList* img_list, bool is_system, std::string stl_path) +{ + wxBitmap bmp = create_scaled_bitmap("cog", nullptr, IMG_PX_CNT, true); + + if (is_system) { + wxImage image = bmp.ConvertToImage(); + if (image.IsOk() && image.GetWidth() != 0 && image.GetHeight() != 0) { + add_border(image); + bmp = wxBitmap(std::move(image)); + } + } + img_list->Add(bmp); +}; + +static fs::path get_dir(bool sys_dir) +{ + return fs::absolute(fs::path(gallery_dir()) / (sys_dir ? "system" : "custom")).make_preferred(); +} + +static bool custom_exists() +{ + return fs::exists(fs::absolute(fs::path(gallery_dir()) / "custom").make_preferred()); +} + +static std::string get_dir_path(bool sys_dir) +{ + fs::path dir = get_dir(sys_dir); +#ifdef __WXMSW__ + return dir.string() + "\\"; +#else + return dir.string() + "/"; +#endif +} + +static std::string name_from_path(fs::path path) +{ + std::string filename = path.filename().string(); + filename.erase(filename.size() - 4); // Remove the extention suffix. + return filename; +} + +void GalleryDialog::load_label_icon_list() +{ + // load names from files + auto add_files_from_gallery = [](std::vector& items, bool sys_dir, std::string& dir_path) + { + fs::path dir = get_dir(sys_dir); + dir_path = get_dir_path(sys_dir); + + for (auto& dir_entry : fs::directory_iterator(dir)) + if (is_stl_file(dir_entry)) { + std::string name = name_from_path(dir_entry.path()); + Item item = Item{ name, sys_dir }; + items.push_back(item); + } + }; + + std::string m_sys_dir_path, m_cust_dir_path; + std::vector list_items; + add_files_from_gallery(list_items, true, m_sys_dir_path); + if (custom_exists()) + add_files_from_gallery(list_items, false, m_cust_dir_path); + + // Make an image list containing large icons + + int px_cnt = (int)(em_unit() * IMG_PX_CNT * 0.1f + 0.5f); + m_image_list = new wxImageList(px_cnt, px_cnt); + + std::string ext = ".png"; + + for (const auto& item : list_items) { + std::string img_name = (item.is_system ? m_sys_dir_path : m_cust_dir_path) + item.name + ext; + std::string stl_name = (item.is_system ? m_sys_dir_path : m_cust_dir_path) + item.name + ".stl"; + if (!fs::exists(img_name)) { + add_def_img(m_image_list, item.is_system, stl_name); + continue; + } + + wxImage image; + if (!image.LoadFile(from_u8(img_name), wxBITMAP_TYPE_PNG) || + image.GetWidth() == 0 || image.GetHeight() == 0) { + add_def_img(m_image_list, item.is_system, stl_name); + continue; + } + image.Rescale(px_cnt, px_cnt, wxIMAGE_QUALITY_BILINEAR); + + if (item.is_system) + add_border(image); + wxBitmap bmp = wxBitmap(std::move(image)); + m_image_list->Add(bmp); + } + + m_list_ctrl->SetImageList(m_image_list, wxIMAGE_LIST_NORMAL); + + int img_cnt = m_image_list->GetImageCount(); + for (int i = 0; i < img_cnt; i++) { + m_list_ctrl->InsertItem(i, from_u8(list_items[i].name), i); + if (list_items[i].is_system) + m_list_ctrl->SetItemFont(i, m_list_ctrl->GetItemFont(i).Bold()); + } +} + +void GalleryDialog::get_input_files(wxArrayString& input_files) +{ + for (const Item& item : m_selected_items) + input_files.Add(from_u8(get_dir_path(item.is_system) + item.name + ".stl")); +} + +void GalleryDialog::add_custom_shapes(wxEvent& event) +{ + wxArrayString input_files; + wxFileDialog dialog(this, _L("Choose one or more files (STL):"), + from_u8(wxGetApp().app_config->get_last_dir()), "", + file_wildcards(FT_STL), wxFD_OPEN | wxFD_MULTIPLE | wxFD_FILE_MUST_EXIST); + + if (dialog.ShowModal() == wxID_OK) + dialog.GetPaths(input_files); + + if (input_files.IsEmpty()) + return; + + load_files(input_files); +} + +void GalleryDialog::del_custom_shapes(wxEvent& event) +{ + auto dest_dir = get_dir(false); + + for (const Item& item : m_selected_items) { + std::string filename = item.name + ".stl"; + + if (!fs::exists(dest_dir / filename)) + continue; + try { + fs::remove(dest_dir / filename); + } + catch (fs::filesystem_error const& e) { + std::cerr << e.what() << '\n'; + return; + } + } + + update(); +} + +void GalleryDialog::replace_custom_png(wxEvent& event) +{ +} + +void GalleryDialog::select(wxListEvent& event) +{ + int idx = event.GetIndex(); + Item item { into_u8(m_list_ctrl->GetItemText(idx)), m_list_ctrl->GetItemFont(idx).GetWeight() == wxFONTWEIGHT_BOLD }; + + m_selected_items.push_back(item); +} + +void GalleryDialog::deselect(wxListEvent& event) +{ + if (m_list_ctrl->GetSelectedItemCount() == 0) { + m_selected_items.clear(); + return; + } + + std::string name = into_u8(m_list_ctrl->GetItemText(event.GetIndex())); + m_selected_items.erase(std::remove_if(m_selected_items.begin(), m_selected_items.end(), [name](Item item) { return item.name == name; })); +} + +void GalleryDialog::update() +{ + m_selected_items.clear(); + m_image_list->RemoveAll(); + m_list_ctrl->ClearAll(); + load_label_icon_list(); +} + +bool GalleryDialog::load_files(const wxArrayString& input_files) +{ + auto dest_dir = get_dir(false); + + try { + if (!fs::exists(dest_dir)) + if (!fs::create_directory(dest_dir)) { + std::cerr << "Unable to create destination directory" << dest_dir.string() << '\n' ; + return false; + } + } + catch (fs::filesystem_error const& e) { + std::cerr << e.what() << '\n'; + return false; + } + + // Iterate through the source directory + for (size_t i = 0; i < input_files.size(); ++i) { + std::string input_file = into_u8(input_files.Item(i)); + + try { + fs::path current = fs::path(input_file); + if (!fs::exists(dest_dir / current.filename())) + fs::copy_file(current, dest_dir / current.filename()); + else { + std::string filename = name_from_path(current); + + int file_idx = 0; + for (auto& dir_entry : fs::directory_iterator(dest_dir)) + if (is_stl_file(dir_entry)) { + std::string name = name_from_path(dir_entry.path()); + if (filename == name) { + if (file_idx == 0) + file_idx++; + continue; + } + + if (name.find(filename) != 0 || + name[filename.size()] != ' ' || name[filename.size()+1] != '(' || name[name.size()-1] != ')') + continue; + std::string idx_str = name.substr(filename.size() + 2, name.size() - filename.size() - 3); + if (int cur_idx = atoi(idx_str.c_str()); file_idx <= cur_idx) + file_idx = cur_idx+1; + } + if (file_idx > 0) { + filename += " (" + std::to_string(file_idx) + ").stl"; + fs::copy_file(current, dest_dir / filename); + } + } + } + catch (fs::filesystem_error const& e) { + std::cerr << e.what() << '\n'; + return false; + } + } + + update(); + return true; +} + +}} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/GalleryDialog.hpp b/src/slic3r/GUI/GalleryDialog.hpp new file mode 100644 index 0000000000..cfc56987f6 --- /dev/null +++ b/src/slic3r/GUI/GalleryDialog.hpp @@ -0,0 +1,58 @@ +#ifndef slic3r_GalleryDialog_hpp_ +#define slic3r_GalleryDialog_hpp_ + +#include "GUI_Utils.hpp" + +class wxListCtrl; +class wxImageList; + +namespace Slic3r { + +namespace GUI { + + +//------------------------------------------ +// GalleryDialog +//------------------------------------------ + +class GalleryDialog : public DPIDialog +{ + wxListCtrl* m_list_ctrl { nullptr }; + wxImageList* m_image_list { nullptr }; + + struct Item { + std::string name; + bool is_system; + }; + std::vector m_selected_items; + + int ID_BTN_ADD_CUSTOM_SHAPE; + int ID_BTN_DEL_CUSTOM_SHAPE; + int ID_BTN_REPLACE_CUSTOM_PNG; + + void load_label_icon_list(); + void add_custom_shapes(wxEvent& event); + void del_custom_shapes(wxEvent& event); + void replace_custom_png(wxEvent& event); + void select(wxListEvent& event); + void deselect(wxListEvent& event); + + void update(); + +public: + GalleryDialog(wxWindow* parent); + ~GalleryDialog(); + + void get_input_files(wxArrayString& input_files); + bool load_files(const wxArrayString& input_files); + +protected: + void on_dpi_changed(const wxRect& suggested_rect) override; + void on_sys_color_changed() override {}; +}; + + +} // namespace GUI +} // namespace Slic3r + +#endif //slic3r_GalleryDialog_hpp_