diff --git a/doc/How to build - Windows.md b/doc/How to build - Windows.md index cd5227daf..979ad9b62 100644 --- a/doc/How to build - Windows.md +++ b/doc/How to build - Windows.md @@ -124,45 +124,33 @@ intermediate files, which are not handled correctly by either `b2.exe` or possib # Noob guide (step by step) -Install Visual Studio Community 2019 from -[visualstudio.microsoft.com/vs/](https://visualstudio.microsoft.com/vs/) -Select all workload options for C++ +- Install Visual Studio Community 2019 from [visualstudio.microsoft.com/vs/](https://visualstudio.microsoft.com/vs/) +- Select all workload options for C++ +- Install git for Windows from [gitforwindows.org](https://gitforwindows.org/) + - download and run the exe accepting all defaults +- Download `PrusaSlicer-master.zip` from github + - This example will use the directory c:\PrusaSlicer and unzipped to `c:\PrusaSlicer\PrusaSlicer-master\` so this will be the prefix for all the steps. Substitute your as required prefix. +- Go to the Windows Start Menu and Click on "Visual Studio 2019" folder, then select the ->"x64 Native Tools Command Prompt" to open a command window -Install git for Windows from -[gitforwindows.org](https://gitforwindows.org/) -download and run the exe accepting all defaults + cd c:\PrusaSlicer\PrusaSlicer-master\deps + mkdir build + cd build + cmake .. -G "Visual Studio 16 2019" -DDESTDIR="c:\PrusaSlicer\PrusaSlicer-master" + msbuild /m ALL_BUILD.vcxproj // This took 13.5 minutes on the following machine: core I7-7700K @ 4.2Ghz with 32GB main memory and 20min on an average laptop + cd c:\PrusaSlicer\PrusaSlicer-master\ + mkdir build + cd build + cmake .. -G "Visual Studio 16 2019" -DCMAKE_PREFIX_PATH="c:\PrusaSlicer\PrusaSlicer-master\usr\local" -download PrusaSlicer-master.zip from github -I downloaded this to c:\PrusaSlicer and unzipped to c:\PrusaSlicer\PrusaSlicer-master\ so this will be my prefix for all my steps. Substitute your prefix. +- Open Visual Studio for c++ development (VS asks this the first time you start it) -Go to the Windows Start Menu and Click on "Visual Studio 2019" folder, then select the ->"x64 Native Tools Command Prompt" to open a command window +`Open->Project/Solution` or `File->Open->Project/Solution` (depending on which dialog comes up first) -cd c:\PrusaSlicer\PrusaSlicer-master\deps +- Click on `c:\PrusaSlicer\PrusaSlicer-master\build\PrusaSlicer.sln` -mkdir build +`Debug->Start Debugging` or `Debug->Start Without debugging` -cd build +- PrusaSlicer should start. +- You're up and running! -cmake .. -G "Visual Studio 16 2019" -DDESTDIR="c:\PrusaSlicer\PrusaSlicer-master" - -msbuild /m ALL_BUILD.vcxproj // This took 13.5 minutes on my machine: core I7-7700K @ 4.2Ghz with 32GB main memory and 20min on a average laptop - -cd c:\PrusaSlicer\PrusaSlicer-master\ - -mkdir build - -cd build - -cmake .. -G "Visual Studio 16 2019" -DCMAKE_PREFIX_PATH="c:\PrusaSlicer\PrusaSlicer-master\usr\local" - -open Visual Studio for c++ development (VS asks this the first time you start it) - -Open->Project/Solution or File->Open->Project/Solution (depending on which dialog comes up first) - -click on c:\PrusaSlicer\PrusaSlicer-master\build\PrusaSlicer.sln - -Debug->Start Debugging or Debug->Start Without debugging -PrusaSlicer should start. You're up and running! - - -note: Thanks to @douggorgen for the original guide, as an answer for a issue +Note: Thanks to @douggorgen for the original guide, as an answer for a issue diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp index c181525bd..f7350dace 100644 --- a/src/PrusaSlicer.cpp +++ b/src/PrusaSlicer.cpp @@ -34,6 +34,7 @@ #include "libslic3r/libslic3r.h" #include "libslic3r/Config.hpp" #include "libslic3r/Geometry.hpp" +#include "libslic3r/GCode/PostProcessor.hpp" #include "libslic3r/Model.hpp" #include "libslic3r/ModelArrange.hpp" #include "libslic3r/Print.hpp" @@ -483,6 +484,12 @@ int CLI::run(int argc, char **argv) if (printer_technology == ptFFF) { for (auto* mo : model.objects) fff_print.auto_assign_extruders(mo); + } else { + // The default for "output_filename_format" is good for FDM: "[input_filename_base].gcode" + // Replace it with a reasonable SLA default. + std::string &format = m_print_config.opt_string("output_filename_format", true); + if (format == static_cast(m_print_config.def()->get("output_filename_format")->default_value.get())->value) + format = "[input_filename_base].SL1"; } print->apply(model, m_print_config); std::string err = print->validate(); @@ -513,6 +520,8 @@ int CLI::run(int argc, char **argv) } outfile = outfile_final; } + // Run the post-processing scripts if defined. + run_post_process_scripts(outfile, fff_print.full_print_config()); boost::nowide::cout << "Slicing result exported to " << outfile << std::endl; } catch (const std::exception &ex) { boost::nowide::cerr << ex.what() << std::endl; diff --git a/src/libslic3r/Fill/FillRectilinear.cpp b/src/libslic3r/Fill/FillRectilinear.cpp index e3860af46..f8393cf36 100644 --- a/src/libslic3r/Fill/FillRectilinear.cpp +++ b/src/libslic3r/Fill/FillRectilinear.cpp @@ -1237,7 +1237,7 @@ static void pinch_contours_insert_phony_outer_intersections(std::vector("post_process"); + if (// likely running in SLA mode + post_process == nullptr || + // no post-processing script + post_process->values.empty()) return; + // Store print configuration into environment variables. config.setenv_(); auto gcode_file = boost::filesystem::path(path); if (! boost::filesystem::exists(gcode_file)) throw Slic3r::RuntimeError(std::string("Post-processor can't find exported gcode file")); - for (const std::string &scripts : config.post_process.values) { + for (const std::string &scripts : post_process->values) { std::vector lines; boost::split(lines, scripts, boost::is_any_of("\r\n")); for (std::string script : lines) { diff --git a/src/libslic3r/GCode/PostProcessor.hpp b/src/libslic3r/GCode/PostProcessor.hpp index ce47374cb..a9196aef7 100644 --- a/src/libslic3r/GCode/PostProcessor.hpp +++ b/src/libslic3r/GCode/PostProcessor.hpp @@ -8,7 +8,7 @@ namespace Slic3r { -extern void run_post_process_scripts(const std::string &path, const PrintConfig &config); +extern void run_post_process_scripts(const std::string &path, const DynamicPrintConfig &config); } // namespace Slic3r diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 37575953d..cde517aca 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -991,7 +991,15 @@ const Preset* PresetCollection::get_preset_parent(const Preset& child) const if (it != m_presets.end()) preset = &(*it); } - return (preset == nullptr/* || preset->is_default */|| preset->is_external) ? nullptr : preset; + return + // not found + (preset == nullptr/* || preset->is_default */|| + // this should not happen, user profile should not derive from an external profile + preset->is_external || + // this should not happen, however people are creative, see GH #4996 + preset == &child) ? + nullptr : + preset; } // Return vendor of the first parent profile, for which the vendor is defined, or null if such profile does not exist. diff --git a/src/libslic3r/PresetBundle.cpp b/src/libslic3r/PresetBundle.cpp index b020f874e..f98abcc19 100644 --- a/src/libslic3r/PresetBundle.cpp +++ b/src/libslic3r/PresetBundle.cpp @@ -1095,7 +1095,11 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla namespace pt = boost::property_tree; pt::ptree tree; boost::nowide::ifstream ifs(path); - pt::read_ini(ifs, tree); + try { + pt::read_ini(ifs, tree); + } catch (const boost::property_tree::ini_parser::ini_parser_error &err) { + throw Slic3r::RuntimeError(format("Failed loading config bundle \"%1%\"\nError: \"%2%\" at line %3%", path, err.message(), err.line()).c_str()); + } const VendorProfile *vendor_profile = nullptr; if (flags & (LOAD_CFGBNDLE_SYSTEM | LOAD_CFGBUNDLE_VENDOR_ONLY)) { diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 0e21abdf3..89ae92a9e 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -63,8 +63,10 @@ void PrintConfigDef::init_common_params() def->set_default_value(new ConfigOptionString("")); def = this->add("thumbnails", coPoints); - def->label = L("Picture sizes to be stored into a .gcode and .sl1 files"); + def->label = L("G-code thumbnails"); + def->tooltip = L("Picture sizes to be stored into a .gcode and .sl1 files"); def->mode = comExpert; + def->gui_type = "one_string"; def->set_default_value(new ConfigOptionPoints()); def = this->add("layer_height", coFloat); @@ -1981,7 +1983,7 @@ void PrintConfigDef::init_fff_params() "in order to remove any visible seam. This option requires a single perimeter, " "no infill, no top solid layers and no support material. You can still set " "any number of bottom solid layers as well as skirt/brim loops. " - "It won't work when printing more than an object."); + "It won't work when printing more than one single object."); def->set_default_value(new ConfigOptionBool(false)); def = this->add("standby_temperature_delta", coInt); diff --git a/src/slic3r/Config/Snapshot.cpp b/src/slic3r/Config/Snapshot.cpp index 54d1dea57..900172d3e 100644 --- a/src/slic3r/Config/Snapshot.cpp +++ b/src/slic3r/Config/Snapshot.cpp @@ -264,12 +264,14 @@ bool Snapshot::equal_to_active(const AppConfig &app_config) const boost::filesystem::path path1 = data_dir / subdir; boost::filesystem::path path2 = snapshot_dir / subdir; std::vector files1, files2; - for (auto &dir_entry : boost::filesystem::directory_iterator(path1)) - if (Slic3r::is_ini_file(dir_entry)) - files1.emplace_back(dir_entry.path().filename().string()); - for (auto &dir_entry : boost::filesystem::directory_iterator(path2)) - if (Slic3r::is_ini_file(dir_entry)) - files2.emplace_back(dir_entry.path().filename().string()); + if (boost::filesystem::is_directory(path1)) + for (auto &dir_entry : boost::filesystem::directory_iterator(path1)) + if (Slic3r::is_ini_file(dir_entry)) + files1.emplace_back(dir_entry.path().filename().string()); + if (boost::filesystem::is_directory(path2)) + for (auto &dir_entry : boost::filesystem::directory_iterator(path2)) + if (Slic3r::is_ini_file(dir_entry)) + files2.emplace_back(dir_entry.path().filename().string()); std::sort(files1.begin(), files1.end()); std::sort(files2.begin(), files2.end()); if (files1 != files2) @@ -459,8 +461,11 @@ void SnapshotDB::restore_snapshot(const Snapshot &snapshot, AppConfig &app_confi boost::filesystem::path snapshot_dir = snapshot_db_dir / snapshot.id; // Remove existing ini files and restore the ini files from the snapshot. for (const char *subdir : snapshot_subdirs) { - delete_existing_ini_files(data_dir / subdir); - copy_config_dir_single_level(snapshot_dir / subdir, data_dir / subdir); + boost::filesystem::path src = snapshot_dir / subdir; + boost::filesystem::path dst = data_dir / subdir; + delete_existing_ini_files(dst); + if (boost::filesystem::is_directory(src)) + copy_config_dir_single_level(src, dst); } // Update AppConfig with the selections of the print / sla_print / filament / sla_material / printer profiles // and about the installed printer types and variants. diff --git a/src/slic3r/GUI/AboutDialog.cpp b/src/slic3r/GUI/AboutDialog.cpp index 4ded4ec92..e320dedfd 100644 --- a/src/slic3r/GUI/AboutDialog.cpp +++ b/src/slic3r/GUI/AboutDialog.cpp @@ -297,6 +297,11 @@ AboutDialog::AboutDialog() auto copy_rights_btn = new wxButton(this, m_copy_rights_btn_id, _L("Portions copyright")+dots); buttons->Insert(0, copy_rights_btn, 0, wxLEFT, 5); copy_rights_btn->Bind(wxEVT_BUTTON, &AboutDialog::onCopyrightBtn, this); + + m_copy_version_btn_id = NewControlId(); + auto copy_version_btn = new wxButton(this, m_copy_version_btn_id, _L("Copy Version Info")); + buttons->Insert(1, copy_version_btn, 0, wxLEFT, 5); + copy_version_btn->Bind(wxEVT_BUTTON, &AboutDialog::onCopyToClipboard, this); this->SetEscapeId(wxID_CLOSE); this->Bind(wxEVT_BUTTON, &AboutDialog::onCloseDialog, this, wxID_CLOSE); @@ -348,5 +353,12 @@ void AboutDialog::onCopyrightBtn(wxEvent &) dlg.ShowModal(); } +void AboutDialog::onCopyToClipboard(wxEvent&) +{ + wxTheClipboard->Open(); + wxTheClipboard->SetData(new wxTextDataObject(_L("Version") + " " + std::string(SLIC3R_VERSION))); + wxTheClipboard->Close(); +} + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/AboutDialog.hpp b/src/slic3r/GUI/AboutDialog.hpp index f1e26fde4..8774d8ce8 100644 --- a/src/slic3r/GUI/AboutDialog.hpp +++ b/src/slic3r/GUI/AboutDialog.hpp @@ -60,6 +60,7 @@ class AboutDialog : public DPIDialog wxHtmlWindow* m_html; wxStaticBitmap* m_logo; int m_copy_rights_btn_id { wxID_ANY }; + int m_copy_version_btn_id { wxID_ANY }; public: AboutDialog(); @@ -70,6 +71,7 @@ private: void onLinkClicked(wxHtmlLinkEvent &event); void onCloseDialog(wxEvent &); void onCopyrightBtn(wxEvent &); + void onCopyToClipboard(wxEvent&); }; } // namespace GUI diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp index 0e51ca77c..2cc9f84a3 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -185,7 +185,7 @@ void BackgroundSlicingProcess::process_fff() break; } m_print->set_status(95, _utf8(L("Running post-processing scripts"))); - run_post_process_scripts(export_path, m_fff_print->config()); + run_post_process_scripts(export_path, m_fff_print->full_print_config()); m_print->set_status(100, (boost::format(_utf8(L("G-code file exported to %1%"))) % export_path).str()); } else if (! m_upload_job.empty()) { wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, new wxCommandEvent(m_event_export_began_id)); @@ -538,7 +538,7 @@ void BackgroundSlicingProcess::prepare_upload() if (copy_file(m_temp_output_path, source_path.string(), error_message) != SUCCESS) { throw Slic3r::RuntimeError(_utf8(L("Copying of the temporary G-code to the output G-code failed"))); } - run_post_process_scripts(source_path.string(), m_fff_print->config()); + run_post_process_scripts(source_path.string(), m_fff_print->full_print_config()); m_upload_job.upload_data.upload_path = m_fff_print->print_statistics().finalize_output_path(m_upload_job.upload_data.upload_path.string()); } else { m_upload_job.upload_data.upload_path = m_sla_print->print_statistics().finalize_output_path(m_upload_job.upload_data.upload_path.string()); diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index 899a01369..185f9aa25 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -2,6 +2,7 @@ #include "ConfigManipulation.hpp" #include "I18N.hpp" #include "GUI_App.hpp" +#include "format.hpp" #include "libslic3r/Model.hpp" #include "libslic3r/PresetBundle.hpp" @@ -184,30 +185,21 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con } if (config->option("fill_density")->value == 100) { - auto fill_pattern = config->option>("fill_pattern")->value; - std::string str_fill_pattern = ""; - t_config_enum_values map_names = config->option>("fill_pattern")->get_enum_values(); - for (auto it : map_names) { - if (fill_pattern == it.second) { - str_fill_pattern = it.first; - break; - } - } - if (!str_fill_pattern.empty()) { - const std::vector& external_fill_pattern = config->def()->get("top_fill_pattern")->enum_values; - bool correct_100p_fill = false; - for (const std::string& fill : external_fill_pattern) - { - if (str_fill_pattern == fill) - correct_100p_fill = true; - } + std::string fill_pattern = config->option>("fill_pattern")->serialize(); + const auto &top_fill_pattern_values = config->def()->get("top_fill_pattern")->enum_values; + bool correct_100p_fill = std::find(top_fill_pattern_values.begin(), top_fill_pattern_values.end(), fill_pattern) != top_fill_pattern_values.end(); + if (!correct_100p_fill) { // get fill_pattern name from enum_labels for using this one at dialog_msg - str_fill_pattern = _utf8(config->def()->get("fill_pattern")->enum_labels[fill_pattern]); - if (!correct_100p_fill) { - wxString msg_text = GUI::from_u8((boost::format(_utf8(L("The %1% infill pattern is not supposed to work at 100%% density."))) % str_fill_pattern).str()); + const ConfigOptionDef *fill_pattern_def = config->def()->get("fill_pattern"); + assert(fill_pattern_def != nullptr); + auto it_pattern = std::find(fill_pattern_def->enum_values.begin(), fill_pattern_def->enum_values.end(), fill_pattern); + assert(it_pattern != fill_pattern_def->enum_values.end()); + if (it_pattern != fill_pattern_def->enum_values.end()) { + wxString msg_text = GUI::format_wxstr(_L("The %1% infill pattern is not supposed to work at 100%% density."), + _(fill_pattern_def->enum_labels[it_pattern - fill_pattern_def->enum_values.begin()])); if (is_global_config) - msg_text += "\n\n" + _(L("Shall I switch to rectilinear fill pattern?")); - wxMessageDialog dialog(nullptr, msg_text, _(L("Infill")), + msg_text += "\n\n" + _L("Shall I switch to rectilinear fill pattern?"); + wxMessageDialog dialog(nullptr, msg_text, _L("Infill"), wxICON_WARNING | (is_global_config ? wxYES | wxNO : wxOK) ); DynamicPrintConfig new_conf = *config; auto answer = dialog.ShowModal(); diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index eb55e1535..359d9d97e 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -5,6 +5,7 @@ #include "wxExtensions.hpp" #include "Plater.hpp" #include "MainFrame.hpp" +#include "format.hpp" #include "libslic3r/PrintConfig.hpp" @@ -12,6 +13,7 @@ #include #include #include +#include #include #include "OG_CustomCtrl.hpp" @@ -52,6 +54,16 @@ wxString double_to_string(double const value, const int max_precision /*= 4*/) return s; } +wxString get_thumbnails_string(const std::vector& values) +{ + wxString ret_str; + if (!values.empty()) + for (auto el : values) + ret_str += wxString::Format("%ix%i, ", int(el[0]), int(el[1])); + return ret_str; +} + + Field::~Field() { if (m_on_kill_focus) @@ -304,6 +316,43 @@ void Field::get_value_by_opt_type(wxString& str, const bool check_value/* = true m_value = std::string(str.ToUTF8().data()); break; } + + case coPoints: { + std::vector out_values; + str.Replace(" ", wxEmptyString, true); + if (!str.IsEmpty()) { + bool invalid_val = false; + wxStringTokenizer thumbnails(str, ","); + while (thumbnails.HasMoreTokens()) { + wxString token = thumbnails.GetNextToken(); + double x, y; + wxStringTokenizer thumbnail(token, "x"); + if (thumbnail.HasMoreTokens()) { + wxString x_str = thumbnail.GetNextToken(); + if (x_str.ToDouble(&x) && thumbnail.HasMoreTokens()) { + wxString y_str = thumbnail.GetNextToken(); + if (y_str.ToDouble(&y) && !thumbnail.HasMoreTokens()) { + out_values.push_back(Vec2d(x, y)); + continue; + } + } + } + invalid_val = true; + break; + } + + if (invalid_val) { + wxString text_value; + if (!m_value.empty()) + text_value = get_thumbnails_string(boost::any_cast>(m_value)); + set_value(text_value, true); + show_error(m_parent, format_wxstr(_L("Invalid input format. It must be represented like \"%1%\""),"XxY, XxY, ..." )); + } + } + + m_value = out_values; + break; } + default: break; } @@ -371,6 +420,9 @@ void TextCtrl::BUILD() { text_value = vec->get_at(m_opt_idx); break; } + case coPoints: + text_value = get_thumbnails_string(m_opt.get_default_value()->values); + break; default: break; } diff --git a/src/slic3r/GUI/Field.hpp b/src/slic3r/GUI/Field.hpp index 9a15542de..5b01c92c9 100644 --- a/src/slic3r/GUI/Field.hpp +++ b/src/slic3r/GUI/Field.hpp @@ -37,6 +37,7 @@ using t_change = std::function; wxString double_to_string(double const value, const int max_precision = 4); +wxString get_thumbnails_string(const std::vector& values); class Field { protected: diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 6671c76c9..e621a262c 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3775,7 +3775,7 @@ GLCanvas3D::WipeTowerInfo GLCanvas3D::get_wipe_tower_info() const m_config->opt_float("wipe_tower_y")); wti.m_rotation = (M_PI/180.) * m_config->opt_float("wipe_tower_rotation_angle"); const BoundingBoxf3& bb = vol->bounding_box(); - wti.m_bb_size = Vec2d(bb.size().x(), bb.size().y()); + wti.m_bb = BoundingBoxf{to_2d(bb.min), to_2d(bb.max)}; break; } } diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index b8be40401..6ebf72dd1 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -676,8 +676,8 @@ public: class WipeTowerInfo { protected: Vec2d m_pos = {std::nan(""), std::nan("")}; - Vec2d m_bb_size = {0., 0.}; double m_rotation = 0.; + BoundingBoxf m_bb; friend class GLCanvas3D; public: @@ -688,7 +688,7 @@ public: inline const Vec2d& pos() const { return m_pos; } inline double rotation() const { return m_rotation; } - inline const Vec2d bb_size() const { return m_bb_size; } + inline const Vec2d bb_size() const { return m_bb.size(); } void apply_wipe_tower() const; }; diff --git a/src/slic3r/GUI/GUI.cpp b/src/slic3r/GUI/GUI.cpp index dc7ffe152..dea226012 100644 --- a/src/slic3r/GUI/GUI.cpp +++ b/src/slic3r/GUI/GUI.cpp @@ -201,7 +201,7 @@ void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt } break; case coPoints:{ - if (opt_key.compare("bed_shape") == 0) { + if (opt_key == "bed_shape" || opt_key == "thumbnails") { config.option(opt_key)->values = boost::any_cast>(value); break; } diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 84232e2d3..f2827be40 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -933,13 +933,7 @@ bool GUI_App::on_init_inner() load_current_presets(); mainframe->Show(true); - /* Temporary workaround for the correct behavior of the Scrolled sidebar panel: - * change min hight of object list to the normal min value (15 * wxGetApp().em_unit()) - * after first whole Mainframe updating/layouting - */ - const int list_min_height = 15 * em_unit(); - if (obj_list()->GetMinSize().GetY() > list_min_height) - obj_list()->SetMinSize(wxSize(-1, list_min_height)); + obj_list()->set_min_height(); update_mode(); // update view mode after fix of the object_list size @@ -1150,13 +1144,8 @@ void GUI_App::recreate_GUI(const wxString& msg_name) mainframe->Show(true); dlg.Update(90, _L("Loading of a mode view") + dots); - /* Temporary workaround for the correct behavior of the Scrolled sidebar panel: - * change min hight of object list to the normal min value (15 * wxGetApp().em_unit()) - * after first whole Mainframe updating/layouting - */ - const int list_min_height = 15 * em_unit(); - if (obj_list()->GetMinSize().GetY() > list_min_height) - obj_list()->SetMinSize(wxSize(-1, list_min_height)); + + obj_list()->set_min_height(); update_mode(); // #ys_FIXME_delete_after_testing Do we still need this ? @@ -1582,13 +1571,16 @@ void GUI_App::add_config_menu(wxMenuBar *menu) ConfigSnapshotDialog dlg(Slic3r::GUI::Config::SnapshotDB::singleton(), on_snapshot); dlg.ShowModal(); if (!dlg.snapshot_to_activate().empty()) { - if (!Config::SnapshotDB::singleton().is_on_snapshot(*app_config)) + if (! Config::SnapshotDB::singleton().is_on_snapshot(*app_config)) Config::SnapshotDB::singleton().take_snapshot(*app_config, Config::Snapshot::SNAPSHOT_BEFORE_ROLLBACK); - app_config->set("on_snapshot", - Config::SnapshotDB::singleton().restore_snapshot(dlg.snapshot_to_activate(), *app_config).id); - preset_bundle->load_presets(*app_config); - // Load the currently selected preset into the GUI, update the preset selection box. - load_current_presets(); + try { + app_config->set("on_snapshot", Config::SnapshotDB::singleton().restore_snapshot(dlg.snapshot_to_activate(), *app_config).id); + preset_bundle->load_presets(*app_config); + // Load the currently selected preset into the GUI, update the preset selection box. + load_current_presets(); + } catch (std::exception &ex) { + GUI::show_error(nullptr, _L("Failed to activate configuration snapshot.") + "\n" + into_u8(ex.what())); + } } } break; diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index fdec4a9c2..0229962ea 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -191,7 +191,7 @@ ObjectList::ObjectList(wxWindow* parent) : // Bind(wxEVT_KEY_DOWN, &ObjectList::OnChar, this); { // Accelerators - wxAcceleratorEntry entries[8]; + wxAcceleratorEntry entries[10]; entries[0].Set(wxACCEL_CTRL, (int) 'C', wxID_COPY); entries[1].Set(wxACCEL_CTRL, (int) 'X', wxID_CUT); entries[2].Set(wxACCEL_CTRL, (int) 'V', wxID_PASTE); @@ -200,7 +200,9 @@ ObjectList::ObjectList(wxWindow* parent) : entries[5].Set(wxACCEL_CTRL, (int) 'Y', wxID_REDO); entries[6].Set(wxACCEL_NORMAL, WXK_DELETE, wxID_DELETE); entries[7].Set(wxACCEL_NORMAL, WXK_BACK, wxID_DELETE); - wxAcceleratorTable accel(8, entries); + entries[8].Set(wxACCEL_NORMAL, int('+'), wxID_ADD); + entries[9].Set(wxACCEL_NORMAL, int('-'), wxID_REMOVE); + wxAcceleratorTable accel(10, entries); SetAcceleratorTable(accel); this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->copy(); }, wxID_COPY); @@ -209,6 +211,8 @@ ObjectList::ObjectList(wxWindow* parent) : this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->remove(); }, wxID_DELETE); this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->undo(); }, wxID_UNDO); this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->redo(); }, wxID_REDO); + this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->increase_instances(); }, wxID_ADD); + this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->decrease_instances(); }, wxID_REMOVE); } #else //__WXOSX__ Bind(wxEVT_CHAR, [this](wxKeyEvent& event) { key_event(event); }); // doesn't work on OSX @@ -254,6 +258,18 @@ ObjectList::~ObjectList() { } +void ObjectList::set_min_height() +{ + /* Temporary workaround for the correct behavior of the Scrolled sidebar panel: + * change min hight of object list to the normal min value (35 * wxGetApp().em_unit()) + * after first whole Mainframe updating/layouting + */ + const int list_min_height = 35 * wxGetApp().em_unit(); + if (this->GetMinSize().GetY() > list_min_height) + this->SetMinSize(wxSize(-1, list_min_height)); +} + + void ObjectList::create_objects_ctrl() { /* Temporary workaround for the correct behavior of the Scrolled sidebar panel: @@ -1092,6 +1108,16 @@ void ObjectList::redo() wxGetApp().plater()->redo(); } +void ObjectList::increase_instances() +{ + wxGetApp().plater()->increase_instances(1); +} + +void ObjectList::decrease_instances() +{ + wxGetApp().plater()->decrease_instances(1); +} + #ifndef __WXOSX__ void ObjectList::key_event(wxKeyEvent& event) { @@ -1116,6 +1142,10 @@ void ObjectList::key_event(wxKeyEvent& event) redo(); else if (wxGetKeyState(wxKeyCode('Z')) && wxGetKeyState(WXK_CONTROL)) undo(); + else if (event.GetUnicodeKey() == '+') + increase_instances(); + else if (event.GetUnicodeKey() == '-') + decrease_instances(); else event.Skip(); } diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index c8bdd2d2c..aaea78339 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -207,6 +207,7 @@ public: ObjectList(wxWindow* parent); ~ObjectList(); + void set_min_height(); std::map CATEGORY_ICON; @@ -257,6 +258,8 @@ public: bool paste_from_clipboard(); void undo(); void redo(); + void increase_instances(); + void decrease_instances(); void get_settings_choice(const wxString& category_name); void get_freq_settings_choice(const wxString& bundle_name); diff --git a/src/slic3r/GUI/Jobs/ArrangeJob.cpp b/src/slic3r/GUI/Jobs/ArrangeJob.cpp index ebe6515cb..0f17e6e9f 100644 --- a/src/slic3r/GUI/Jobs/ArrangeJob.cpp +++ b/src/slic3r/GUI/Jobs/ArrangeJob.cpp @@ -30,11 +30,10 @@ public: ArrangePolygon get_arrange_polygon() const { Polygon ap({ - {coord_t(0), coord_t(0)}, - {scaled(m_bb_size(X)), coord_t(0)}, - {scaled(m_bb_size)}, - {coord_t(0), scaled(m_bb_size(Y))}, - {coord_t(0), coord_t(0)}, + {scaled(m_bb.min)}, + {scaled(m_bb.max.x()), scaled(m_bb.min.y())}, + {scaled(m_bb.max)}, + {scaled(m_bb.min.x()), scaled(m_bb.max.y())} }); ArrangePolygon ret; diff --git a/src/slic3r/GUI/KBShortcutsDialog.cpp b/src/slic3r/GUI/KBShortcutsDialog.cpp index 64696d6e2..f25f1eaf5 100644 --- a/src/slic3r/GUI/KBShortcutsDialog.cpp +++ b/src/slic3r/GUI/KBShortcutsDialog.cpp @@ -132,6 +132,7 @@ void KBShortcutsDialog::fill_shortcuts() { ctrl + "5", L("Switch to 3D") }, { ctrl + "6", L("Switch to Preview") }, { ctrl + "J", L("Print host upload queue") }, + { ctrl + "Shift+" + "I", L("Open new instance") }, // View { "0-6", L("Camera view") }, { "E", L("Show/Hide object/instance labels") }, diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 27bdf0c89..843eff25b 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -1191,9 +1191,8 @@ void MainFrame::init_menubar_as_editor() [this](wxCommandEvent&) { m_printhost_queue_dlg->Show(); }, "upload_queue", nullptr, []() {return true; }, this); windowMenu->AppendSeparator(); - append_menu_item(windowMenu, wxID_ANY, _L("Open new instance") + "\tCtrl+I", _L("Open a new PrusaSlicer instance"), + append_menu_item(windowMenu, wxID_ANY, _L("Open new instance") + "\tCtrl+Shift+I", _L("Open a new PrusaSlicer instance"), [this](wxCommandEvent&) { start_new_slicer(); }, "", nullptr, [this]() {return m_plater != nullptr && wxGetApp().app_config->get("single_instance") != "1"; }, this); - } // View menu diff --git a/src/slic3r/GUI/OG_CustomCtrl.cpp b/src/slic3r/GUI/OG_CustomCtrl.cpp index 8469b1a4b..a8ead0aab 100644 --- a/src/slic3r/GUI/OG_CustomCtrl.cpp +++ b/src/slic3r/GUI/OG_CustomCtrl.cpp @@ -178,7 +178,7 @@ wxPoint OG_CustomCtrl::get_pos(const Line& line, Field* field_in/* = nullptr*/) #ifdef __WXMSW__ // when we use 2 monitors with different DPIs, GetTextExtent() return value for the primary display // so, use dc.GetMultiLineTextExtent on Windows - wxPaintDC dc(this); + wxClientDC dc(this); dc.SetFont(m_font); dc.GetMultiLineTextExtent(label, &label_w, &label_h); #else diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp index 9da291984..d597d9cb2 100644 --- a/src/slic3r/GUI/OptionsGroup.cpp +++ b/src/slic3r/GUI/OptionsGroup.cpp @@ -25,20 +25,22 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id) { const t_field& OptionsGroup::build_field(const t_config_option_key& id, const ConfigOptionDef& opt) { // Check the gui_type field first, fall through // is the normal type. - if (opt.gui_type.compare("select") == 0) { - } else if (opt.gui_type.compare("select_open") == 0) { + if (opt.gui_type == "select") { + } else if (opt.gui_type == "select_open") { m_fields.emplace(id, std::move(Choice::Create(this->ctrl_parent(), opt, id))); - } else if (opt.gui_type.compare("color") == 0) { + } else if (opt.gui_type == "color") { m_fields.emplace(id, std::move(ColourPicker::Create(this->ctrl_parent(), opt, id))); - } else if (opt.gui_type.compare("f_enum_open") == 0 || - opt.gui_type.compare("i_enum_open") == 0 || - opt.gui_type.compare("i_enum_closed") == 0) { + } else if (opt.gui_type == "f_enum_open" || + opt.gui_type == "i_enum_open" || + opt.gui_type == "i_enum_closed") { m_fields.emplace(id, std::move(Choice::Create(this->ctrl_parent(), opt, id))); - } else if (opt.gui_type.compare("slider") == 0) { + } else if (opt.gui_type == "slider") { m_fields.emplace(id, std::move(SliderCtrl::Create(this->ctrl_parent(), opt, id))); - } else if (opt.gui_type.compare("i_spin") == 0) { // Spinctrl - } else if (opt.gui_type.compare("legend") == 0) { // StaticText + } else if (opt.gui_type == "i_spin") { // Spinctrl + } else if (opt.gui_type == "legend") { // StaticText m_fields.emplace(id, std::move(StaticText::Create(this->ctrl_parent(), opt, id))); + } else if (opt.gui_type == "one_string") { + m_fields.emplace(id, std::move(TextCtrl::Create(this->ctrl_parent(), opt, id))); } else { switch (opt.type) { case coFloatOrPercent: @@ -837,9 +839,9 @@ boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config } if (config.option(opt_key)->values.empty()) ret = text_value; - else if (opt->gui_flags.compare("serialized") == 0) { + else if (opt->gui_flags == "serialized") { std::vector values = config.option(opt_key)->values; - if (!values.empty() && values[0].compare("") != 0) + if (!values.empty() && !values[0].empty()) for (auto el : values) text_value += el + ";"; ret = text_value; @@ -897,6 +899,8 @@ boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config case coPoints: if (opt_key == "bed_shape") ret = config.option(opt_key)->values; + if (opt_key == "thumbnails") + ret = get_thumbnails_string(config.option(opt_key)->values); else ret = config.option(opt_key)->get_at(idx); break; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 2c9463968..f1c129118 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2399,18 +2399,18 @@ std::vector Plater::priv::load_files(const std::vector& input_ wxGetApp().sidebar().update_ui_from_settings(); }; - if (imperial_units) - convert_from_imperial_units(model); - else if (model.looks_like_imperial_units()) { - wxMessageDialog msg_dlg(q, format_wxstr(_L( - "Some object(s) in file %s looks like saved in inches.\n" - "Should I consider them as a saved in inches and convert them?"), from_path(filename)) + "\n", - _L("The object appears to be saved in inches"), wxICON_WARNING | wxYES | wxNO); - if (msg_dlg.ShowModal() == wxID_YES) + if (!is_project_file) { + if (imperial_units) convert_from_imperial_units(model); - } + else if (model.looks_like_imperial_units()) { + wxMessageDialog msg_dlg(q, format_wxstr(_L( + "Some object(s) in file %s looks like saved in inches.\n" + "Should I consider them as a saved in inches and convert them?"), from_path(filename)) + "\n", + _L("The object appears to be saved in inches"), wxICON_WARNING | wxYES | wxNO); + if (msg_dlg.ShowModal() == wxID_YES) + convert_from_imperial_units(model); + } - if (! is_project_file) { if (model.looks_like_multipart_object()) { wxMessageDialog msg_dlg(q, _L( "This file contains several objects positioned at multiple heights.\n" diff --git a/src/slic3r/GUI/Search.cpp b/src/slic3r/GUI/Search.cpp index 1542067b3..d61fc05e0 100644 --- a/src/slic3r/GUI/Search.cpp +++ b/src/slic3r/GUI/Search.cpp @@ -94,7 +94,7 @@ void OptionsSearcher::append_options(DynamicPrintConfig* config, Preset::Type ty int cnt = 0; - if ( (type == Preset::TYPE_SLA_MATERIAL || type == Preset::TYPE_PRINTER) && opt_key != "bed_shape") + if ( (type == Preset::TYPE_SLA_MATERIAL || type == Preset::TYPE_PRINTER) && opt_key != "bed_shape" && opt_key != "thumbnails") switch (config->option(opt_key)->type()) { case coInts: change_opt_key(opt_key, config, cnt); break; diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index d7465c570..36ad2c02f 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -109,14 +109,14 @@ Tab::Tab(wxNotebook* parent, const wxString& title, Preset::Type type) : m_compatible_printers.type = Preset::TYPE_PRINTER; m_compatible_printers.key_list = "compatible_printers"; m_compatible_printers.key_condition = "compatible_printers_condition"; - m_compatible_printers.dialog_title = _(L("Compatible printers")).ToUTF8(); - m_compatible_printers.dialog_label = _(L("Select the printers this profile is compatible with.")).ToUTF8(); + m_compatible_printers.dialog_title = _L("Compatible printers"); + m_compatible_printers.dialog_label = _L("Select the printers this profile is compatible with."); m_compatible_prints.type = Preset::TYPE_PRINT; m_compatible_prints.key_list = "compatible_prints"; m_compatible_prints.key_condition = "compatible_prints_condition"; - m_compatible_prints.dialog_title = _(L("Compatible print profiles")).ToUTF8(); - m_compatible_prints.dialog_label = _(L("Select the print profiles this profile is compatible with.")).ToUTF8(); + m_compatible_prints.dialog_title = _L("Compatible print profiles"); + m_compatible_prints.dialog_label = _L("Select the print profiles this profile is compatible with."); wxGetApp().tabs_list.push_back(this); @@ -653,7 +653,7 @@ void TabPrinter::init_options_list() for (const auto opt_key : m_config->keys()) { - if (opt_key == "bed_shape") { + if (opt_key == "bed_shape" || opt_key == "thumbnails") { m_options_list.emplace(opt_key, m_opt_status_value); continue; } @@ -2173,6 +2173,11 @@ void TabPrinter::build_fff() optgroup = page->new_optgroup(L("Firmware")); optgroup->append_single_option_line("gcode_flavor"); + + option = optgroup->get_option("thumbnails"); + option.opt.full_width = true; + optgroup->append_single_option_line(option); + optgroup->append_single_option_line("silent_mode"); optgroup->append_single_option_line("remaining_times"); diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index 5fb163aec..c9914e858 100644 --- a/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -136,8 +136,8 @@ protected: ScalableButton *btn = nullptr; std::string key_list; // "compatible_printers" std::string key_condition; - std::string dialog_title; - std::string dialog_label; + wxString dialog_title; + wxString dialog_label; }; PresetDependencies m_compatible_printers; PresetDependencies m_compatible_prints; diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index bb1abcf3d..db0e65da9 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -968,6 +968,9 @@ static wxString get_string_value(std::string opt_key, const DynamicPrintConfig& BedShape shape(*config.option(opt_key)); return shape.get_full_name_with_params(); } + if (opt_key == "thumbnails") + return get_thumbnails_string(config.option(opt_key)->values); + Vec2d val = config.opt(opt_key)->get_at(opt_idx); return from_u8((boost::format("[%1%]") % ConfigOptionPoint(val).serialize()).str()); }