From 329ad97a842ce6f1436fd202c854c6b41f8d6422 Mon Sep 17 00:00:00 2001 From: Matthias Urlichs Date: Wed, 4 Dec 2019 09:31:21 +0100 Subject: [PATCH 01/17] Add int() and % (modulo) --- src/libslic3r/PlaceholderParser.cpp | 35 +++++++++++++++++++++++++++++ t/custom_gcode.t | 3 +++ 2 files changed, 38 insertions(+) diff --git a/src/libslic3r/PlaceholderParser.cpp b/src/libslic3r/PlaceholderParser.cpp index 0cfd97415..c92be2845 100644 --- a/src/libslic3r/PlaceholderParser.cpp +++ b/src/libslic3r/PlaceholderParser.cpp @@ -332,6 +332,21 @@ namespace client return expr(); } + expr unary_integer(const Iterator start_pos) const + { + switch (this->type) { + case TYPE_INT : + return expr(this->i(), start_pos, this->it_range.end()); + case TYPE_DOUBLE: + return expr((int)(this->d()), start_pos, this->it_range.end()); + default: + this->throw_exception("Cannot convert to integer."); + } + assert(false); + // Suppress compiler warnings. + return expr(); + } + expr unary_not(const Iterator start_pos) const { switch (this->type) { @@ -415,6 +430,22 @@ namespace client return *this; } + expr &operator%=(const expr &rhs) + { + this->throw_if_not_numeric("Cannot divide a non-numeric type."); + rhs.throw_if_not_numeric("Cannot divide with a non-numeric type."); + if ((this->type == TYPE_INT) ? (rhs.i() == 0) : (rhs.d() == 0.)) + rhs.throw_exception("Division by zero"); + if (this->type == TYPE_DOUBLE || rhs.type == TYPE_DOUBLE) { + double d = std::fmod(this->as_d(), rhs.as_d()); + this->data.d = d; + this->type = TYPE_DOUBLE; + } else + this->data.i %= rhs.i(); + this->it_range = boost::iterator_range(this->it_range.begin(), rhs.it_range.end()); + return *this; + } + static void to_string2(expr &self, std::string &out) { out = self.to_string(); @@ -1087,6 +1118,7 @@ namespace client unary_expression(_r1) [_val = _1] >> *( (lit('*') > unary_expression(_r1) ) [_val *= _1] | (lit('/') > unary_expression(_r1) ) [_val /= _1] + | (lit('%') > unary_expression(_r1) ) [_val %= _1] ); multiplicative_expression.name("multiplicative_expression"); @@ -1107,6 +1139,8 @@ namespace client { out = value.unary_minus(out.it_range.begin()); } static void not_(expr &value, expr &out) { out = value.unary_not(out.it_range.begin()); } + static void to_int(expr &value, expr &out) + { out = value.unary_integer(out.it_range.begin()); } }; unary_expression = iter_pos[px::bind(&FactorActions::set_start_pos, _1, _val)] >> ( scalar_variable_reference(_r1) [ _val = _1 ] @@ -1118,6 +1152,7 @@ namespace client [ px::bind(&expr::min, _val, _2) ] | (kw["max"] > '(' > conditional_expression(_r1) [_val = _1] > ',' > conditional_expression(_r1) > ')') [ px::bind(&expr::max, _val, _2) ] + | (kw["int"] > '(' > unary_expression(_r1) ) [ px::bind(&FactorActions::to_int, _1, _val) ] | (strict_double > iter_pos) [ px::bind(&FactorActions::double_, _1, _2, _val) ] | (int_ > iter_pos) [ px::bind(&FactorActions::int_, _1, _2, _val) ] | (kw[bool_] > iter_pos) [ px::bind(&FactorActions::bool_, _1, _2, _val) ] diff --git a/t/custom_gcode.t b/t/custom_gcode.t index 4c1c1b108..5ffd9b7f4 100644 --- a/t/custom_gcode.t +++ b/t/custom_gcode.t @@ -67,6 +67,8 @@ use Slic3r::Test; is $parser->process('{2*3/6}'), '1', 'math: 2*3/6'; is $parser->process('{2*3/12}'), '0', 'math: 2*3/12'; ok abs($parser->process('{2.*3/12}') - 0.5) < 1e-7, 'math: 2.*3/12'; + is $parser->process('{10%2.5}') '0', 'math: 10 % 2.5'; + is $parser->process('{11/2.5-1}') '1', 'math: 11 % 2.5'; is $parser->process('{2*(3-12)}'), '-18', 'math: 2*(3-12)'; is $parser->process('{2*foo*(3-12)}'), '0', 'math: 2*foo*(3-12)'; is $parser->process('{2*bar*(3-12)}'), '-36', 'math: 2*bar*(3-12)'; @@ -75,6 +77,7 @@ use Slic3r::Test; is $parser->process('{max(12, 14)}'), '14', 'math: max(12, 14)'; is $parser->process('{min(13.4, -1238.1)}'), '-1238.1', 'math: min(13.4, -1238.1)'; is $parser->process('{max(13.4, -1238.1)}'), '13.4', 'math: max(13.4, -1238.1)'; + is $parser->process('{int(13.4)}'), '13', 'math: int(13.4)'; # Test the boolean expression parser. is $parser->evaluate_boolean_expression('12 == 12'), 1, 'boolean expression parser: 12 == 12'; From 7efca35c277bee219cb298c3fa0d19c494c8c73f Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 21 Jan 2020 09:55:36 +0100 Subject: [PATCH 02/17] Don't store "print_host", "printhost_apikey", "printhost_cafile" into the G-code. --- src/libslic3r/GCode.cpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index bd22c29e5..517b5281b 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -34,6 +35,8 @@ #include "miniz_extension.hpp" +using namespace std::literals::string_view_literals; + #if 0 // Enable debugging and asserts, even in the release build. #define DEBUG @@ -2268,8 +2271,20 @@ void GCode::apply_print_config(const PrintConfig &print_config) void GCode::append_full_config(const Print &print, std::string &str) { const DynamicPrintConfig &cfg = print.full_print_config(); + // Sorted list of config keys, which shall not be stored into the G-code. Initializer list. + static constexpr auto banned_keys = { + "compatible_printers"sv, + "compatible_prints"sv, + "print_host"sv, + "printhost_apikey"sv, + "printhost_cafile"sv + }; + assert(std::is_sorted(banned_keys.begin(), banned_keys.end())); + auto is_banned = [](const std::string &key) { + return std::binary_search(banned_keys.begin(), banned_keys.end(), key); + }; for (const std::string &key : cfg.keys()) - if (key != "compatible_prints" && key != "compatible_printers" && ! cfg.option(key)->is_nil()) + if (! is_banned(key) && ! cfg.option(key)->is_nil()) str += "; " + key + " = " + cfg.opt_serialize(key) + "\n"; } From d8325cfc06370e7383e622405ab40345b3df30c9 Mon Sep 17 00:00:00 2001 From: Italo Soares Date: Sat, 21 Dec 2019 20:10:07 -0300 Subject: [PATCH 03/17] Fixed some translations for better understanding --- .../localization/pt_br/PrusaSlicer_pt_br.po | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/resources/localization/pt_br/PrusaSlicer_pt_br.po b/resources/localization/pt_br/PrusaSlicer_pt_br.po index e54f17ec3..647a9dd4f 100644 --- a/resources/localization/pt_br/PrusaSlicer_pt_br.po +++ b/resources/localization/pt_br/PrusaSlicer_pt_br.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2019-09-09 16:39+0200\n" -"PO-Revision-Date: 2019-11-18 16:39-0300\n" +"PO-Revision-Date: 2019-12-21 19:55-0300\n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -1432,7 +1432,7 @@ msgid "" "You will lose content of the plater." msgstr "" "Alterar a linguagem fará com que o aplicativo reinicie.\n" -"Você irá perder conteúdo no prato." +"Você irá perder conteúdo na bandeja." #: src/slic3r/GUI/GUI_App.cpp:877 msgid "Do you want to proceed?" @@ -2302,7 +2302,7 @@ msgstr "(Re)fatiar" #: src/slic3r/GUI/KBShortcutsDialog.cpp:116 msgid "Select Plater Tab" -msgstr "Selecione a guia de prato" +msgstr "Selecione a guia de bandeja" #: src/slic3r/GUI/KBShortcutsDialog.cpp:118 msgid "Select Print Settings Tab" @@ -2466,7 +2466,7 @@ msgstr "Desmarcar Gizmo/limpar seleção" #: src/slic3r/GUI/KBShortcutsDialog.cpp:166 msgid "Plater Shortcuts" -msgstr "Atalhos do prato" +msgstr "Atalhos da bandeja" #: src/slic3r/GUI/KBShortcutsDialog.cpp:181 #: src/slic3r/GUI/KBShortcutsDialog.cpp:193 @@ -2546,7 +2546,7 @@ msgstr "baseado no Slic3r" #: src/slic3r/GUI/MainFrame.cpp:189 msgid "Plater" -msgstr "Prato" +msgstr "Bandeja" #: src/slic3r/GUI/MainFrame.cpp:400 msgid "&New Project" @@ -2635,7 +2635,7 @@ msgstr "Exportar &G-code" #: src/slic3r/GUI/MainFrame.cpp:469 msgid "Export current plate as G-code" -msgstr "Exporte o prato atual como o G-code" +msgstr "Exporte a bandeja atual como o G-code" #: src/slic3r/GUI/MainFrame.cpp:473 src/slic3r/GUI/MainFrame.cpp:720 msgid "S&end G-code" @@ -2643,31 +2643,31 @@ msgstr "E&nviar G-code" #: src/slic3r/GUI/MainFrame.cpp:473 msgid "Send to print current plate as G-code" -msgstr "Enviar para imprimir prato atual como G-code" +msgstr "Enviar para imprimir a bandeja atual como G-code" #: src/slic3r/GUI/MainFrame.cpp:478 msgid "Export plate as &STL" -msgstr "Exportar prato como &STL" +msgstr "Exportar bandeja como &STL" #: src/slic3r/GUI/MainFrame.cpp:478 msgid "Export current plate as STL" -msgstr "Exporte o prato atual como STL" +msgstr "Exporte a bandeja atual como STL" #: src/slic3r/GUI/MainFrame.cpp:481 msgid "Export plate as STL &including supports" -msgstr "Exportar prato como STL &incluindo suportes" +msgstr "Exportar bandeja como STL incluindo suportes" #: src/slic3r/GUI/MainFrame.cpp:481 msgid "Export current plate as STL including supports" -msgstr "Exporte o prato atual como o STL que inclui suportes" +msgstr "Exporte a bandeja atual como o STL que inclui suportes" #: src/slic3r/GUI/MainFrame.cpp:484 msgid "Export plate as &AMF" -msgstr "Exportar prato como &AMF" +msgstr "Exportar bandeja como &AMF" #: src/slic3r/GUI/MainFrame.cpp:484 msgid "Export current plate as AMF" -msgstr "Exporte o prato atual como o AMF" +msgstr "Exporte a bandeja atual como o AMF" #: src/slic3r/GUI/MainFrame.cpp:488 msgid "Export &toolpaths as OBJ" @@ -2804,11 +2804,11 @@ msgstr "Colar área de transferência" #: src/slic3r/GUI/MainFrame.cpp:590 msgid "&Plater Tab" -msgstr "&Prato" +msgstr "&Bandeja" #: src/slic3r/GUI/MainFrame.cpp:590 msgid "Show the plater" -msgstr "Mostrar o prato" +msgstr "Mostrar a bandeja" #: src/slic3r/GUI/MainFrame.cpp:597 msgid "P&rint Settings Tab" @@ -6198,7 +6198,7 @@ msgstr "Distância entre cópias" #: src/libslic3r/PrintConfig.cpp:352 msgid "Distance used for the auto-arrange feature of the plater." -msgstr "Distância usada para o recurso de organizar automaticamente o prato." +msgstr "Distância usada para o recurso de organizar automaticamente a bandeja." #: src/libslic3r/PrintConfig.cpp:359 msgid "Elephant foot compensation" @@ -6395,8 +6395,8 @@ msgid "" msgstr "" "Defina isso para o raio de folga em torno de sua extrusora. Se a extrusora " "não estiver centralizada, escolha o maior valor para a segurança. Essa " -"config. é usada para verificar colisões e exibir a visualização gráfica no " -"prato." +"config. é usada para verificar colisões e exibir a visualização gráfica na " +"bandeja." #: src/libslic3r/PrintConfig.cpp:505 msgid "Extruder Color" From 053818d0285aeec186cdc9aa874133c877a3924d Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 21 Jan 2020 09:44:18 +0100 Subject: [PATCH 04/17] Fix MPFR being built as shared library --- deps/MPFR/MPFR.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/MPFR/MPFR.cmake b/deps/MPFR/MPFR.cmake index 17ac283ea..cda7eeea9 100644 --- a/deps/MPFR/MPFR.cmake +++ b/deps/MPFR/MPFR.cmake @@ -21,7 +21,7 @@ else () ExternalProject_Add(dep_MPFR URL http://ftp.vim.org/ftp/gnu/mpfr/mpfr-3.1.6.tar.bz2 https://www.mpfr.org/mpfr-3.1.6/mpfr-3.1.6.tar.bz2 # mirrors are allowed BUILD_IN_SOURCE ON - CONFIGURE_COMMAND ./configure --prefix=${DESTDIR}/usr/local --with-gmp=${DESTDIR}/usr/local --with-pic + CONFIGURE_COMMAND ./configure --prefix=${DESTDIR}/usr/local --enable-shared=no --enable-static=yes --with-gmp=${DESTDIR}/usr/local --with-pic BUILD_COMMAND make -j INSTALL_COMMAND make install DEPENDS dep_GMP From c627114830a5d9411033aa8535b2d505ae7536ee Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 21 Jan 2020 10:14:25 +0100 Subject: [PATCH 05/17] Fixed missing "&" in Brazilian Portugal translation. --- resources/localization/pt_br/PrusaSlicer_pt_br.po | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/localization/pt_br/PrusaSlicer_pt_br.po b/resources/localization/pt_br/PrusaSlicer_pt_br.po index 647a9dd4f..30c0687a5 100644 --- a/resources/localization/pt_br/PrusaSlicer_pt_br.po +++ b/resources/localization/pt_br/PrusaSlicer_pt_br.po @@ -2655,7 +2655,7 @@ msgstr "Exporte a bandeja atual como STL" #: src/slic3r/GUI/MainFrame.cpp:481 msgid "Export plate as STL &including supports" -msgstr "Exportar bandeja como STL incluindo suportes" +msgstr "Exportar bandeja como STL &incluindo suportes" #: src/slic3r/GUI/MainFrame.cpp:481 msgid "Export current plate as STL including supports" From 858e936e52db89f75c5946acb392e9b112b89c00 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 20 Jan 2020 11:20:18 +0100 Subject: [PATCH 06/17] Fix incorrect filename case in test_3mf --- tests/libslic3r/test_3mf.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/libslic3r/test_3mf.cpp b/tests/libslic3r/test_3mf.cpp index 5f1c74c36..6b60182b5 100644 --- a/tests/libslic3r/test_3mf.cpp +++ b/tests/libslic3r/test_3mf.cpp @@ -26,7 +26,7 @@ SCENARIO("Export+Import geometry to/from 3mf file cycle", "[3mf]") { GIVEN("world vertices coordinates before save") { // load a model from stl file Model src_model; - std::string src_file = std::string(TEST_DATA_DIR) + "/test_3mf/prusa.stl"; + std::string src_file = std::string(TEST_DATA_DIR) + "/test_3mf/Prusa.stl"; load_stl(src_file.c_str(), &src_model); src_model.add_default_instances(); From 6eaf48ebd8cc9f6436ccd9a4e1b24899c87e7e5e Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 20 Jan 2020 15:08:19 +0100 Subject: [PATCH 07/17] Implemented check of color change event respecting to mode + unresolved ticks are marked with error_tick icon + some code refactoring --- resources/icons/error_tick.svg | 12 + src/slic3r/GUI/wxExtensions.cpp | 583 +++++++++++++++++++------------- src/slic3r/GUI/wxExtensions.hpp | 48 ++- 3 files changed, 391 insertions(+), 252 deletions(-) create mode 100644 resources/icons/error_tick.svg diff --git a/resources/icons/error_tick.svg b/resources/icons/error_tick.svg new file mode 100644 index 000000000..6467d29fb --- /dev/null +++ b/resources/icons/error_tick.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index 7689e31cb..8f43f6c30 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -2325,7 +2325,7 @@ DoubleSlider::DoubleSlider( wxWindow *parent, m_cog_icon_dim = int((float)m_bmp_cog.bmp().GetSize().x / scale_factor); m_selection = ssUndef; - m_pause_print_msg = _utf8(L("Place bearings in slots and resume")); + m_ticks.set_pause_print_msg(_utf8(L("Place bearings in slots and resume"))); // slider events Bind(wxEVT_PAINT, &DoubleSlider::OnPaint, this); @@ -2539,13 +2539,13 @@ Slic3r::Model::CustomGCodeInfo DoubleSlider::GetTicksValues() const const int val_size = m_values.size(); if (!m_values.empty()) - for (const TICK_CODE& tick : m_ticks) { + for (const TICK_CODE& tick : m_ticks.ticks) { if (tick.tick > val_size) break; values.emplace_back(t_custom_code{m_values[tick.tick], tick.gcode, tick.extruder, tick.color}); } - custom_gcode_per_print_z.mode = m_mode; + custom_gcode_per_print_z.mode = m_force_mode_apply ? m_mode : m_ticks.mode; return custom_gcode_per_print_z; } @@ -2554,13 +2554,13 @@ void DoubleSlider::SetTicksValues(const Slic3r::Model::CustomGCodeInfo& custom_g { if (m_values.empty()) { - m_ticks_mode = m_mode; + m_ticks.mode = m_mode; return; } const bool was_empty = m_ticks.empty(); - m_ticks.clear(); + m_ticks.ticks.clear(); const std::vector& heights = custom_gcode_per_print_z.gcodes; for (auto h : heights) { auto it = std::lower_bound(m_values.begin(), m_values.end(), h.print_z - epsilon()); @@ -2568,14 +2568,14 @@ void DoubleSlider::SetTicksValues(const Slic3r::Model::CustomGCodeInfo& custom_g if (it == m_values.end()) continue; - m_ticks.emplace(TICK_CODE{int(it-m_values.begin()), h.gcode, h.extruder, h.color}); + m_ticks.ticks.emplace(TICK_CODE{int(it-m_values.begin()), h.gcode, h.extruder, h.color}); } if (!was_empty && m_ticks.empty()) // Switch to the "Feature type"/"Tool" from the very beginning of a new object slicing after deleting of the old one post_ticks_changed_event(); - m_ticks_mode = custom_gcode_per_print_z.mode; + m_ticks.mode = custom_gcode_per_print_z.mode; Refresh(); Update(); @@ -2649,7 +2649,7 @@ void DoubleSlider::draw_action_icon(wxDC& dc, const wxPoint pt_beg, const wxPoin return; wxBitmap* icon = m_is_action_icon_focesed ? &m_bmp_add_tick_off.bmp() : &m_bmp_add_tick_on.bmp(); - if (m_ticks.find(TICK_CODE{tick}) != m_ticks.end()) + if (m_ticks.ticks.find(TICK_CODE{tick}) != m_ticks.ticks.end()) icon = m_is_action_icon_focesed ? &m_bmp_del_tick_off.bmp() : &m_bmp_del_tick_on.bmp(); wxCoord x_draw, y_draw; @@ -2792,7 +2792,7 @@ void DoubleSlider::draw_ticks(wxDC& dc) int height, width; get_size(&width, &height); const wxCoord mid = is_horizontal() ? 0.5*height : 0.5*width; - for (auto tick : m_ticks) + for (auto tick : m_ticks.ticks) { const wxCoord pos = get_position_from_value(tick.tick); @@ -2801,11 +2801,23 @@ void DoubleSlider::draw_ticks(wxDC& dc) is_horizontal() ? dc.DrawLine(pos, mid+14, pos, mid+9) : dc.DrawLine(mid + 14, pos/* - 1*/, mid + 9, pos/* - 1*/); + wxBitmap icon = wxNullBitmap; + // Draw icon for "Pause print" or "Custom Gcode" if (tick.gcode != Slic3r::ColorChangeCode && tick.gcode != Slic3r::ToolChangeCode) + icon = create_scaled_bitmap(this, tick.gcode == Slic3r::PausePrintCode ? "pause_print" : "edit_gcode"); + else { - wxBitmap icon = create_scaled_bitmap(this, tick.gcode == Slic3r::PausePrintCode ? "pause_print" : "edit_gcode"); + if ((tick.gcode == Slic3r::ColorChangeCode && ( + (m_ticks.mode == t_mode::SingleExtruder && m_mode == t_mode::MultiExtruder ) || + (m_ticks.mode == t_mode::MultiExtruder && m_mode == t_mode::SingleExtruder) )) || + (tick.gcode == Slic3r::ToolChangeCode && + (m_ticks.mode == t_mode::MultiAsSingle && m_mode != t_mode::MultiAsSingle ) )) + icon = create_scaled_bitmap(this, "error_tick"); + } + if (!icon.IsNull()) + { wxCoord x_draw, y_draw; is_horizontal() ? x_draw = pos - 0.5 * m_tick_icon_dim : y_draw = pos - 0.5 * m_tick_icon_dim; is_horizontal() ? y_draw = mid + 22 : x_draw = mid + m_thumb_size.x + 3; @@ -2820,7 +2832,7 @@ std::string DoubleSlider::get_color_for_tool_change_tick(std::set::co const int current_extruder = it->extruder == 0 ? std::max(m_only_extruder, 1) : it->extruder; auto it_n = it; - while (it_n != m_ticks.begin()) { + while (it_n != m_ticks.ticks.begin()) { --it_n; if (it_n->gcode == Slic3r::ColorChangeCode && it_n->extruder == current_extruder) return it_n->color; @@ -2834,7 +2846,7 @@ std::string DoubleSlider::get_color_for_color_change_tick(std::set::c const int def_extruder = std::max(1, m_only_extruder); auto it_n = it; bool is_tool_change = false; - while (it_n != m_ticks.begin()) { + while (it_n != m_ticks.ticks.begin()) { --it_n; if (it_n->gcode == Slic3r::ToolChangeCode) { is_tool_change = true; @@ -2881,9 +2893,9 @@ void DoubleSlider::draw_colored_band(wxDC& dc) const int default_color_idx = m_mode==t_mode::MultiAsSingle ? std::max(m_only_extruder - 1, 0) : 0; draw_band(dc, wxColour(Slic3r::GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config()[default_color_idx]), main_band); - std::set::const_iterator tick_it = m_ticks.begin(); + std::set::const_iterator tick_it = m_ticks.ticks.begin(); - while (tick_it != m_ticks.end()) + while (tick_it != m_ticks.ticks.end()) { if ( (m_mode == t_mode::SingleExtruder && tick_it->gcode == Slic3r::ColorChangeCode ) || (m_mode == t_mode::MultiAsSingle && (tick_it->gcode == Slic3r::ToolChangeCode || tick_it->gcode == Slic3r::ColorChangeCode)) ) @@ -2993,7 +3005,7 @@ bool DoubleSlider::is_point_in_rect(const wxPoint& pt, const wxRect& rect) int DoubleSlider::is_point_near_tick(const wxPoint& pt) { - for (auto tick : m_ticks) { + for (auto tick : m_ticks.ticks) { const wxCoord pos = get_position_from_value(tick.tick); if (is_horizontal()) { @@ -3030,8 +3042,13 @@ void DoubleSlider::OnLeftDown(wxMouseEvent& event) wxClientDC dc(this); wxPoint pos = event.GetLogicalPosition(dc); - if (is_point_in_rect(pos, m_rect_tick_action) && m_is_enabled_tick_manipulation) { - action_tick(taOnIcon); + if (is_point_in_rect(pos, m_rect_tick_action) && m_is_enabled_tick_manipulation) + { + const auto it = m_ticks.ticks.find(TICK_CODE{ m_selection == ssLower ? m_lower_value : m_higher_value }); + if (it == m_ticks.ticks.end()) + m_force_add_tick = true; + else + m_force_delete_tick = true; return; } @@ -3054,12 +3071,13 @@ void DoubleSlider::OnLeftDown(wxMouseEvent& event) m_selection == ssLower ? correct_lower_value() : correct_higher_value(); if (!m_selection) m_selection = ssHigher; - m_ticks.clear(); + m_ticks.ticks.clear(); post_ticks_changed_event(); } else if (is_point_in_rect(pos, m_rect_cog_icon) && m_mode == t_mode::MultiExtruder) { // show dialog for set extruder sequence - m_edit_extruder_sequence = true; + m_force_edit_extruder_sequence = true; + return; } else detect_selected_slider(pos); @@ -3127,8 +3145,8 @@ wxString DoubleSlider::get_tooltip(IconFocus icon_focus) else if (m_is_action_icon_focesed) { const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; - const auto tick_code_it = m_ticks.find(TICK_CODE{tick}); - tooltip = tick_code_it == m_ticks.end() ? (m_mode == t_mode::MultiAsSingle ? + const auto tick_code_it = m_ticks.ticks.find(TICK_CODE{tick}); + tooltip = tick_code_it == m_ticks.ticks.end() ? (m_mode == t_mode::MultiAsSingle ? _(L("For add change extruder use left mouse button click")) : _(L("For add color change use left mouse button click")) ) + "\n" + _(L("For add another code use right mouse button click")) : @@ -3211,10 +3229,8 @@ void DoubleSlider::append_change_extruder_menu_item(wxMenu* menu) if (m_mode == t_mode::MultiAsSingle) append_menu_item(change_extruder_menu, wxID_ANY, item_name, "", - [this, i](wxCommandEvent&) { change_extruder(i); }, "", menu, + [this, i](wxCommandEvent&) { add_code_as_tick(Slic3r::ToolChangeCode, i); }, "", menu, [is_active_extruder]() { return !is_active_extruder; }, Slic3r::GUI::wxGetApp().plater()); -// append_menu_radio_item(change_extruder_menu, wxID_ANY, item_name, "", -// [this, i](wxCommandEvent&) { change_extruder(i); }, menu)->Check(i == initial_extruder); } const wxString change_extruder_menu_name = m_mode == t_mode::MultiAsSingle ? _(L("Change extruder")) : _(L("Change extruder (N/A)")); @@ -3245,13 +3261,13 @@ void DoubleSlider::append_add_color_change_menu_item(wxMenu* menu) (is_used_extruder ? " (" + _(L("used")) + ")" : ""); append_menu_item(add_color_change_menu, wxID_ANY, item_name, "", - [this, i](wxCommandEvent&) { add_code(Slic3r::ColorChangeCode, i); }, "", menu, + [this, i](wxCommandEvent&) { add_code_as_tick(Slic3r::ColorChangeCode, i); }, "", menu, [is_used_extruder]() { return is_used_extruder; }, Slic3r::GUI::wxGetApp().plater()); } const wxString menu_name = from_u8((boost::format(_utf8(L("Add color change (%1%) for:"))) % Slic3r::ColorChangeCode).str()); wxMenuItem* add_color_change_menu_item = menu->AppendSubMenu(add_color_change_menu, menu_name, ""); - add_color_change_menu_item->SetBitmap(create_scaled_bitmap(nullptr, "colorchange_add_m")); + add_color_change_menu_item->SetBitmap(create_scaled_bitmap(this, "colorchange_add_m")); } } @@ -3262,28 +3278,21 @@ void DoubleSlider::OnLeftUp(wxMouseEvent& event) this->ReleaseMouse(); m_is_left_down = false; - if (m_show_context_menu) + if (m_force_delete_tick) { - if (m_mode == t_mode::SingleExtruder) - add_code(Slic3r::ColorChangeCode); - else - { - wxMenu menu; - - if (m_mode == t_mode::MultiAsSingle) - append_change_extruder_menu_item(&menu); - else - append_add_color_change_menu_item(&menu); - - Slic3r::GUI::wxGetApp().plater()->PopupMenu(&menu); - } - - m_show_context_menu = false; + delete_current_tick(); + m_force_delete_tick = false; } - - if (m_edit_extruder_sequence) { + else + if (m_force_add_tick) + { + add_current_tick(); + m_force_add_tick = false; + } + else + if (m_force_edit_extruder_sequence) { edit_extruder_sequence(); - m_edit_extruder_sequence = false; + m_force_edit_extruder_sequence = false; } Refresh(); @@ -3329,45 +3338,6 @@ void DoubleSlider::move_current_thumb(const bool condition) ProcessWindowEvent(e); } -void DoubleSlider::action_tick(const TicksAction action) -{ - if (m_selection == ssUndef) - return; - - const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; - - const auto it = m_ticks.find(TICK_CODE{tick}); - - if (it != m_ticks.end()) // erase this tick - { - if (action == taAdd) - return; - m_ticks.erase(TICK_CODE{tick}); - - post_ticks_changed_event(it->gcode); - Refresh(); - Update(); - return; - } - - if (action == taDel) - return; - if (action == taAdd) - { - // OnChar() is called immediately after OnKeyDown(), which can cause call of add_code() twice. - // To avoid this case we should suppress second add_code() call. - if (m_suppress_add_code) - return; - m_suppress_add_code = true; - if (m_mode == t_mode::SingleExtruder) // if (m_mode != t_mode::MultiExtruder) - add_code(Slic3r::ColorChangeCode); - m_suppress_add_code = false; - return; - } - - m_show_context_menu = true; -} - void DoubleSlider::OnWheel(wxMouseEvent& event) { // Set nearest to the mouse thumb as a selected, if there is not selected thumb @@ -3391,10 +3361,18 @@ void DoubleSlider::OnWheel(wxMouseEvent& event) void DoubleSlider::OnKeyDown(wxKeyEvent &event) { const int key = event.GetKeyCode(); - if (key == WXK_NUMPAD_ADD) - action_tick(taAdd); - else if (key == 390 || key == WXK_DELETE || key == WXK_BACK) - action_tick(taDel); + if (key == WXK_NUMPAD_ADD) { + // OnChar() is called immediately after OnKeyDown(), which can cause call of add_tick() twice. + // To avoid this case we should suppress second add_tick() call. + m_ticks.suppress_plus(true); + add_current_tick(true); + } + else if (key == 390 || key == WXK_DELETE || key == WXK_BACK) { + // OnChar() is called immediately after OnKeyDown(), which can cause call of delete_tick() twice. + // To avoid this case we should suppress second delete_tick() call. + m_ticks.suppress_minus(true); + delete_current_tick(); + } else if (is_horizontal()) { if (key == WXK_LEFT || key == WXK_RIGHT) @@ -3428,10 +3406,14 @@ void DoubleSlider::OnKeyUp(wxKeyEvent &event) void DoubleSlider::OnChar(wxKeyEvent& event) { const int key = event.GetKeyCode(); - if (key == '+') - action_tick(taAdd); - else if (key == '-') - action_tick(taDel); + if (key == '+' && !m_ticks.suppressed_plus()) { + add_current_tick(true); + m_ticks.suppress_plus(false); + } + else if (key == '-' && !m_ticks.suppressed_minus()) { + delete_current_tick(); + m_ticks.suppress_minus(false); + } } void DoubleSlider::OnRightDown(wxMouseEvent& event) @@ -3446,8 +3428,8 @@ void DoubleSlider::OnRightDown(wxMouseEvent& event) { const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; // if on this Z doesn't exist tick - auto it = m_ticks.find(TICK_CODE{ tick }); - if (it == m_ticks.end()) + auto it = m_ticks.ticks.find(TICK_CODE{ tick }); + if (it == m_ticks.ticks.end()) { // show context menu on OnRightUp() m_show_context_menu = true; @@ -3484,8 +3466,8 @@ int DoubleSlider::get_extruder_for_tick(int tick) if (m_ticks.empty()) return default_initial_extruder; - auto it = m_ticks.lower_bound(TICK_CODE{tick}); - while (it != m_ticks.begin()) { + auto it = m_ticks.ticks.lower_bound(TICK_CODE{tick}); + while (it != m_ticks.ticks.begin()) { --it; if(it->gcode == Slic3r::ToolChangeCode) return it->extruder; @@ -3522,16 +3504,17 @@ std::set DoubleSlider::get_used_extruders_for_tick(int tick) return {default_initial_extruder}; std::set used_extruders; - auto it_start = m_ticks.lower_bound(TICK_CODE{tick}); + const std::set& ticks = m_ticks.ticks; + auto it_start = ticks.lower_bound(TICK_CODE{tick}); auto it = it_start; - if (it == m_ticks.begin() && it->gcode == Slic3r::ToolChangeCode) { + if (it == ticks.begin() && it->gcode == Slic3r::ToolChangeCode) { used_extruders.emplace(it->extruder); if (tick < it->tick) used_extruders.emplace(default_initial_extruder); } - while (it != m_ticks.begin()) { + while (it != ticks.begin()) { --it; if(it->gcode == Slic3r::ToolChangeCode) { @@ -3540,11 +3523,11 @@ std::set DoubleSlider::get_used_extruders_for_tick(int tick) } } - if (it == m_ticks.begin() && used_extruders.empty()) + if (it == ticks.begin() && used_extruders.empty()) used_extruders.emplace(default_initial_extruder); it = it_start; - while (it != m_ticks.end()) { + while (it != ticks.end()) { if(it->gcode == Slic3r::ToolChangeCode) used_extruders.emplace(it->extruder); ++it; @@ -3565,7 +3548,7 @@ void DoubleSlider::OnRightUp(wxMouseEvent& event) if (m_mode == t_mode::SingleExtruder) append_menu_item(&menu, wxID_ANY, _(L("Add color change")) + " (M600)", "", - [this](wxCommandEvent&) { add_code(Slic3r::ColorChangeCode); }, "colorchange_add_m", &menu, + [this](wxCommandEvent&) { add_code_as_tick(Slic3r::ColorChangeCode); }, "colorchange_add_m", &menu, [](){return true;}, this); else { @@ -3574,11 +3557,11 @@ void DoubleSlider::OnRightUp(wxMouseEvent& event) } append_menu_item(&menu, wxID_ANY, _(L("Add pause print")) + " (M601)", "", - [this](wxCommandEvent&) { add_code(Slic3r::PausePrintCode); }, "pause_print", &menu, + [this](wxCommandEvent&) { add_code_as_tick(Slic3r::PausePrintCode); }, "pause_print", &menu, []() {return true; }, this); append_menu_item(&menu, wxID_ANY, _(L("Add custom G-code")), "", - [this](wxCommandEvent&) { add_code(""); }, "edit_gcode", &menu, + [this](wxCommandEvent&) { add_code_as_tick(""); }, "edit_gcode", &menu, []() {return true; }, this); Slic3r::GUI::wxGetApp().plater()->PopupMenu(&menu); @@ -3588,7 +3571,7 @@ void DoubleSlider::OnRightUp(wxMouseEvent& event) else if (m_show_edit_menu) { wxMenu menu; - std::set::iterator it = m_ticks.find(TICK_CODE{ m_selection == ssLower ? m_lower_value : m_higher_value }); + std::set::iterator it = m_ticks.ticks.find(TICK_CODE{ m_selection == ssLower ? m_lower_value : m_higher_value }); const bool is_color_change = it->gcode == Slic3r::ColorChangeCode; append_menu_item(&menu, wxID_ANY, it->gcode == Slic3r::ColorChangeCode ? _(L("Edit color")) : @@ -3599,7 +3582,7 @@ void DoubleSlider::OnRightUp(wxMouseEvent& event) append_menu_item(&menu, wxID_ANY, it->gcode == Slic3r::ColorChangeCode ? _(L("Delete color change")) : it->gcode == Slic3r::PausePrintCode ? _(L("Delete pause print")) : _(L("Delete custom G-code")), "", - [this](wxCommandEvent&) { action_tick(taDel); }, "colorchange_del_f", &menu); + [this](wxCommandEvent&) { delete_current_tick();}, "colorchange_del_f", &menu); Slic3r::GUI::wxGetApp().plater()->PopupMenu(&menu); @@ -3656,123 +3639,100 @@ static std::string get_pause_print_msg(const std::string& msg_in, double height) return into_u8(dlg.GetValue()); } -void DoubleSlider::add_code(std::string code, int selected_extruder/* = -1*/) +void DoubleSlider::add_code_as_tick(std::string code, int selected_extruder/* = -1*/) { + if (m_selection == ssUndef) + return; const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; - // if on this Z doesn't exist tick - auto it = m_ticks.find(TICK_CODE{ tick }); - if (it != m_ticks.end()) + + if (m_ticks.ticks.find(TICK_CODE{ tick }) != m_ticks.ticks.end() || // if on this Z doesn't exist tick + !check_ticks_changed_event(code)) return; - std::string color; const int extruder = selected_extruder > 0 ? selected_extruder : std::max(1, m_only_extruder); - if (code == Slic3r::ColorChangeCode) - { - std::vector colors = Slic3r::GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config(); - - if (m_ticks.empty()) - color = colors[extruder-1]; - else - { - auto before_tick_it = std::lower_bound(m_ticks.begin(), m_ticks.end(), TICK_CODE{ tick }); - while (before_tick_it != m_ticks.begin()) { - --before_tick_it; - if (before_tick_it->gcode == Slic3r::ColorChangeCode && before_tick_it->extruder == extruder) { - color = before_tick_it->color; - break; - } - } - - if (color.empty()) - color = colors[extruder-1]; - } - - color = get_new_color(color); - if (color.empty()) - return; - } - else if (code == Slic3r::PausePrintCode) - { - /* PausePrintCode doesn't need a color, so - * this field is used for save a short message shown on Printer display - * */ - color = get_pause_print_msg(m_pause_print_msg, m_values[tick]); - if (color.empty()) - return; - m_pause_print_msg = color; - } - else if (code.empty()) - { - code = get_custom_code(m_custom_gcode, m_values[tick]); - if (code.empty()) - return; - m_custom_gcode = code; - } - - m_ticks.emplace(TICK_CODE{tick, code, extruder, color}); + if (!m_ticks.add_tick(tick, code, extruder, m_values[tick])) + return; post_ticks_changed_event(code); - Refresh(); - Update(); +} + +void DoubleSlider::add_current_tick(bool call_from_keyboard /*= false*/) +{ + if (m_selection == ssUndef) + return; + const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; + auto it = m_ticks.ticks.find(TICK_CODE{ tick }); + + if (it != m_ticks.ticks.end() || // this tick is already exist + !check_ticks_changed_event(m_mode == t_mode::MultiAsSingle ? Slic3r::ToolChangeCode : Slic3r::ColorChangeCode)) + return; + + if (m_mode == t_mode::SingleExtruder) + add_code_as_tick(Slic3r::ColorChangeCode); + else + { + wxMenu menu; + + if (m_mode == t_mode::MultiAsSingle) + append_change_extruder_menu_item(&menu); + else + append_add_color_change_menu_item(&menu); + + wxPoint pos = wxDefaultPosition; + if (call_from_keyboard) + { + int width, height; + get_size(&width, &height); + + const wxCoord coord = 0.75 * (is_horizontal() ? height : width); + this->GetPosition(&width, &height); + + pos = is_horizontal() ? + wxPoint(get_position_from_value(tick), height + coord) : + wxPoint(width + coord, get_position_from_value(tick)); + } + + Slic3r::GUI::wxGetApp().plater()->PopupMenu(&menu, pos); + } +} + +void DoubleSlider::delete_current_tick() +{ + if (m_selection == ssUndef) + return; + auto it = m_ticks.ticks.find(TICK_CODE{ m_selection == ssLower ? m_lower_value : m_higher_value }); + + if (it != m_ticks.ticks.end()) + { + if (!check_ticks_changed_event(it->gcode)) + return; + + const std::string code = it->gcode; + m_ticks.ticks.erase(it); + post_ticks_changed_event(code); + } } void DoubleSlider::edit_tick() { const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; - // if on this Z exists tick - std::set::iterator it = m_ticks.find(TICK_CODE{ tick }); - if (it != m_ticks.end()) - { - std::string edited_value; - if (it->gcode == Slic3r::ColorChangeCode) - edited_value = get_new_color(it->color); - else if (it->gcode == Slic3r::PausePrintCode) - edited_value = get_pause_print_msg(it->color, m_values[it->tick]); - else - edited_value = get_custom_code(it->gcode, m_values[it->tick]); + const std::set::iterator it = m_ticks.ticks.find(TICK_CODE{ tick }); - if (edited_value.empty()) - return; + if (it == m_ticks.ticks.end() || // if on this Z exists tick + !check_ticks_changed_event(it->gcode)) + return; - TICK_CODE changed_tick = *it; - if (it->gcode == Slic3r::ColorChangeCode || it->gcode == Slic3r::PausePrintCode) { - if (it->color == edited_value) - return; - changed_tick.color = edited_value; - } - else { - if (it->gcode == edited_value) - return; - changed_tick.gcode = edited_value; - } - - m_ticks.erase(it); - m_ticks.emplace(changed_tick); - - post_ticks_changed_event(changed_tick.gcode); - } -} - -void DoubleSlider::change_extruder(int extruder) -{ - const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; - - std::vector colors = Slic3r::GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config(); - - // if on this Y doesn't exist tick - if (m_ticks.find(TICK_CODE{tick}) == m_ticks.end()) - { - m_ticks.emplace(TICK_CODE{tick, Slic3r::ToolChangeCode, extruder, extruder == 0 ? "" : colors[extruder-1]}); - - post_ticks_changed_event(Slic3r::ToolChangeCode); - Refresh(); - Update(); - } + const std::string code = it->gcode; + if (m_ticks.edit_tick(it, m_values[it->tick])) + post_ticks_changed_event(code); } void DoubleSlider::edit_extruder_sequence() { + if (!check_ticks_changed_event(Slic3r::ToolChangeCode)) + return; + Slic3r::GUI::ExtruderSequenceDialog dlg(m_extruders_sequence); if (dlg.ShowModal() != wxID_OK) return; @@ -3783,13 +3743,7 @@ void DoubleSlider::edit_extruder_sequence() m_extruders_sequence = from_dlg_val; - auto it = m_ticks.begin(); - while (it != m_ticks.end()) { - if (it->gcode == Slic3r::ToolChangeCode) - it = m_ticks.erase(it); - else - ++it; - } + m_ticks.erase_all_ticks_with_code(Slic3r::ToolChangeCode); int tick = 0; double value = 0.0; @@ -3800,8 +3754,8 @@ void DoubleSlider::edit_extruder_sequence() while (tick <= m_max_value) { - int cur_extruder = m_extruders_sequence.extruders[extruder]; - m_ticks.emplace(TICK_CODE{tick, Slic3r::ToolChangeCode, cur_extruder + 1, colors[cur_extruder]}); + const int cur_extruder = m_extruders_sequence.extruders[extruder]; + m_ticks.ticks.emplace(TICK_CODE{tick, Slic3r::ToolChangeCode, cur_extruder + 1, colors[cur_extruder]}); extruder++; if (extruder == extr_cnt) @@ -3825,40 +3779,183 @@ void DoubleSlider::edit_extruder_sequence() void DoubleSlider::post_ticks_changed_event(const std::string& gcode /*= ""*/) { - if ( m_ticks_mode == m_mode || - (gcode != Slic3r::ColorChangeCode && gcode != Slic3r::ToolChangeCode) ) - { - wxPostEvent(this->GetParent(), wxCommandEvent(wxCUSTOMEVT_TICKSCHANGED)); - return; - } - - if (m_ticks_mode == t_mode::SingleExtruder && m_mode == t_mode::MultiAsSingle) - { - } - - if (m_ticks_mode == t_mode::SingleExtruder && m_mode == t_mode::MultiExtruder) - { - } - - if (m_ticks_mode == t_mode::MultiAsSingle && m_mode == t_mode::SingleExtruder) - { - } - - if (m_ticks_mode == t_mode::MultiAsSingle && m_mode == t_mode::MultiExtruder) - { - } - - if (m_ticks_mode == t_mode::MultiExtruder && m_mode == t_mode::SingleExtruder) - { - } - - if (m_ticks_mode == t_mode::MultiExtruder && m_mode == t_mode::MultiAsSingle) - { - } + m_force_mode_apply = (gcode.empty() || gcode == Slic3r::ColorChangeCode || gcode == Slic3r::ToolChangeCode); wxPostEvent(this->GetParent(), wxCommandEvent(wxCUSTOMEVT_TICKSCHANGED)); } +bool DoubleSlider::check_ticks_changed_event(const std::string& gcode) +{ + if ( m_ticks.mode == m_mode || + (gcode != Slic3r::ColorChangeCode && gcode != Slic3r::ToolChangeCode) || + (m_ticks.mode == t_mode::SingleExtruder && m_mode == t_mode::MultiAsSingle) || // All ColorChanges will be applied for 1st extruder + (m_ticks.mode == t_mode::MultiExtruder && m_mode == t_mode::MultiAsSingle) ) // Just mark ColorChanges for all unused extruders + return true; + + if ((m_ticks.mode == t_mode::SingleExtruder && m_mode == t_mode::MultiExtruder ) || + (m_ticks.mode == t_mode::MultiExtruder && m_mode == t_mode::SingleExtruder) ) + { + if (!m_ticks.has_tick_with_code(Slic3r::ColorChangeCode)) + return true; + + wxString message = (m_ticks.mode == t_mode::SingleExtruder ? + _(L("The last color change data was saved for a single extruder printer profile.")) : + _(L("The last color change data was saved for a multiple extruder printer profile.")) + ) + "\n" + + _(L("Your current changes will cause a deletion of all saved color changes.")) + "\n\n\t" + + _(L("Are you sure you want to continue?")); + + wxMessageDialog msg(this, message, _(L("Notice")), wxYES_NO); + if (msg.ShowModal() == wxID_YES) { + m_ticks.erase_all_ticks_with_code(Slic3r::ColorChangeCode); + post_ticks_changed_event(Slic3r::ColorChangeCode); + } + return false; + } + // m_ticks_mode == t_mode::MultiAsSingle + if( m_ticks.has_tick_with_code(Slic3r::ToolChangeCode) ) + { + wxString message = m_mode == t_mode::SingleExtruder ? ( + _(L("The last color change data was saved for a multi extruder printing.")) + "\n\n" + + _(L("Select YES if you want to delete all saved tool changes, \n\t" + "NO if you want all tool changes switch to color changes, \n\t" + "or CANCEL for do nothing")) + "\n\n\t" + + _(L("Do you want to delete all saved tool changes?")) + ) : ( // t_mode::MultiExtruder + _(L("The last color change data was saved for a multi extruder printing with tool changes for whole print.")) + "\n\n" + + _(L("Your current changes will cause a deletion of all saved tool changes.")) + "\n\n\t" + + _(L("Are you sure you want to continue?")) ) ; + + wxMessageDialog msg(this, message, _(L("Notice")), wxYES_NO | (m_mode == t_mode::SingleExtruder ? wxCANCEL : 0)); + const int answer = msg.ShowModal(); + if (answer == wxID_YES) { + m_ticks.erase_all_ticks_with_code(Slic3r::ToolChangeCode); + post_ticks_changed_event(Slic3r::ToolChangeCode); + } + else if (m_mode == t_mode::SingleExtruder && answer == wxID_NO) { + m_ticks.switch_code(Slic3r::ToolChangeCode, Slic3r::ColorChangeCode); + post_ticks_changed_event(Slic3r::ColorChangeCode); + } + return false; + } + + return true; +} + +bool DoubleSlider::TICK_CODE_INFO::add_tick(const int tick, std::string& code, const int extruder, double print_z) +{ + std::string color; + if (code.empty()) // custom Gcode + { + code = get_custom_code(custom_gcode, print_z); + if (code.empty()) + return false; + custom_gcode = code; + } + else if (code == Slic3r::PausePrintCode) + { + /* PausePrintCode doesn't need a color, so + * this field is used for save a short message shown on Printer display + * */ + color = get_pause_print_msg(pause_print_msg, print_z); + if (color.empty()) + return false; + pause_print_msg = color; + } + else + { + std::vector colors = Slic3r::GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config(); + color = colors[extruder - 1]; + + if (code == Slic3r::ColorChangeCode) + { + if (!ticks.empty()) + { + auto before_tick_it = std::lower_bound(ticks.begin(), ticks.end(), TICK_CODE{ tick }); + while (before_tick_it != ticks.begin()) { + --before_tick_it; + if (before_tick_it->gcode == Slic3r::ColorChangeCode && before_tick_it->extruder == extruder) { + color = before_tick_it->color; + break; + } + } + } + + color = get_new_color(color); + if (color.empty()) + return false; + } + } + + ticks.emplace(TICK_CODE{ tick, code, extruder, color }); + return true; +} + +bool DoubleSlider::TICK_CODE_INFO::edit_tick(std::set::iterator it, double print_z) +{ + std::string edited_value; + if (it->gcode == Slic3r::ColorChangeCode) + edited_value = get_new_color(it->color); + else if (it->gcode == Slic3r::PausePrintCode) + edited_value = get_pause_print_msg(it->color, print_z); + else + edited_value = get_custom_code(it->gcode, print_z); + + if (edited_value.empty()) + return false; + + TICK_CODE changed_tick = *it; + if (it->gcode == Slic3r::ColorChangeCode || it->gcode == Slic3r::PausePrintCode) { + if (it->color == edited_value) + return false; + changed_tick.color = edited_value; + } + else { + if (it->gcode == edited_value) + return false; + changed_tick.gcode = edited_value; + } + + ticks.erase(it); + ticks.emplace(changed_tick); + + return true; +} + +void DoubleSlider::TICK_CODE_INFO::switch_code(const std::string& code_from, const std::string& code_to) +{ + for (auto it{ ticks.begin() }, end{ ticks.end() }; it != end; ) + if (it->gcode == code_from) + { + TICK_CODE tick = *it; + tick.gcode = code_to; + tick.extruder = 1; + ticks.erase(it); + it = ticks.emplace(tick).first; + } + else + ++it; +} + +void DoubleSlider::TICK_CODE_INFO::erase_all_ticks_with_code(const std::string& gcode) +{ + for (auto it{ ticks.begin() }, end{ ticks.end() }; it != end; ) { + if (it->gcode == gcode) + it = ticks.erase(it); + else + ++it; + } +} + +bool DoubleSlider::TICK_CODE_INFO::has_tick_with_code(const std::string& gcode) +{ + for (const TICK_CODE& tick : ticks) + if (tick.gcode == gcode) + return true; + + return false; +} + // ---------------------------------------------------------------------------- // LockButton diff --git a/src/slic3r/GUI/wxExtensions.hpp b/src/slic3r/GUI/wxExtensions.hpp index 5b9f6db0b..8cae495ef 100644 --- a/src/slic3r/GUI/wxExtensions.hpp +++ b/src/slic3r/GUI/wxExtensions.hpp @@ -844,9 +844,13 @@ public: void OnChar(wxKeyEvent &event); void OnRightDown(wxMouseEvent& event); void OnRightUp(wxMouseEvent& event); - void add_code(std::string code, int selected_extruder = -1); + + void add_code_as_tick(std::string code, int selected_extruder = -1); + // add default action for tick, when press "+" + void add_current_tick(bool call_from_keyboard = false); + // delete current tick, when press "-" + void delete_current_tick(); void edit_tick(); - void change_extruder(int extruder); void edit_extruder_sequence(); struct TICK_CODE @@ -882,7 +886,6 @@ protected: void correct_lower_value(); void correct_higher_value(); void move_current_thumb(const bool condition); - void action_tick(const TicksAction action); void enter_window(wxMouseEvent& event, const bool enter); private: @@ -906,6 +909,7 @@ private: std::set get_used_extruders_for_tick(int tick); void post_ticks_changed_event(const std::string& gcode = ""); + bool check_ticks_changed_event(const std::string& gcode); void append_change_extruder_menu_item(wxMenu*); void append_add_color_change_menu_item(wxMenu*); @@ -937,11 +941,11 @@ private: bool m_is_enabled_tick_manipulation = true; bool m_show_context_menu = false; bool m_show_edit_menu = false; - bool m_edit_extruder_sequence = false; - bool m_suppress_add_code = false; + bool m_force_edit_extruder_sequence = false; + bool m_force_mode_apply = true; + bool m_force_add_tick = false; + bool m_force_delete_tick = false; t_mode m_mode = t_mode::SingleExtruder; - std::string m_custom_gcode = ""; - std::string m_pause_print_msg; int m_only_extruder = -1; wxRect m_rect_lower_thumb; @@ -972,8 +976,34 @@ private: std::vector m_line_pens; std::vector m_segm_pens; std::vector m_values; - std::set m_ticks; - t_mode m_ticks_mode; + + struct TICK_CODE_INFO + { + std::set ticks; + t_mode mode = t_mode::SingleExtruder; + + bool empty() const { return ticks.empty(); } + void set_pause_print_msg(const std::string& message) { pause_print_msg = message; } + + bool add_tick (const int tick, std::string &code, int extruder, double print_z); + bool edit_tick (std::set::iterator it, double print_z); + void switch_code(const std::string& code_from, const std::string& code_to); + void erase_all_ticks_with_code (const std::string& gcode); + bool has_tick_with_code (const std::string& gcode); + + void suppress_plus (bool suppress) { m_suppress_plus = suppress;} + void suppress_minus(bool suppress) { m_suppress_minus = suppress;} + bool suppressed_plus () { return m_suppress_plus ; } + bool suppressed_minus() { return m_suppress_minus; } + + private: + + std::string custom_gcode = ""; + std::string pause_print_msg = ""; + bool m_suppress_plus = false; + bool m_suppress_minus = false; + } + m_ticks; public: struct ExtrudersSequence From 05ea01bdcccb3d8bc5d46b71c20a30e25352cea8 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 21 Jan 2020 12:10:02 +0100 Subject: [PATCH 08/17] Ported PlaceholderParser unit tests to C++. --- src/libslic3r/PlaceholderParser.cpp | 3 +- src/libslic3r/PlaceholderParser.hpp | 2 +- t/custom_gcode.t | 69 +------------------ tests/libslic3r/CMakeLists.txt | 1 + tests/libslic3r/test_placeholder_parser.cpp | 74 +++++++++++++++++++++ 5 files changed, 79 insertions(+), 70 deletions(-) create mode 100644 tests/libslic3r/test_placeholder_parser.cpp diff --git a/src/libslic3r/PlaceholderParser.cpp b/src/libslic3r/PlaceholderParser.cpp index c92be2845..bbf32a141 100644 --- a/src/libslic3r/PlaceholderParser.cpp +++ b/src/libslic3r/PlaceholderParser.cpp @@ -1152,7 +1152,8 @@ namespace client [ px::bind(&expr::min, _val, _2) ] | (kw["max"] > '(' > conditional_expression(_r1) [_val = _1] > ',' > conditional_expression(_r1) > ')') [ px::bind(&expr::max, _val, _2) ] - | (kw["int"] > '(' > unary_expression(_r1) ) [ px::bind(&FactorActions::to_int, _1, _val) ] + //FIXME this is likley not correct + | (kw["int"] > '(' > unary_expression(_r1) /* > ')' */ ) [ px::bind(&FactorActions::to_int, _1, _val) ] | (strict_double > iter_pos) [ px::bind(&FactorActions::double_, _1, _2, _val) ] | (int_ > iter_pos) [ px::bind(&FactorActions::int_, _1, _2, _val) ] | (kw[bool_] > iter_pos) [ px::bind(&FactorActions::bool_, _1, _2, _val) ] diff --git a/src/libslic3r/PlaceholderParser.hpp b/src/libslic3r/PlaceholderParser.hpp index 14fdc5c28..d744dba22 100644 --- a/src/libslic3r/PlaceholderParser.hpp +++ b/src/libslic3r/PlaceholderParser.hpp @@ -41,7 +41,7 @@ public: // Fill in the template using a macro processing language. // Throws std::runtime_error on syntax or runtime error. - std::string process(const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override = nullptr) const; + std::string process(const std::string &templ, unsigned int current_extruder_id = 0, const DynamicConfig *config_override = nullptr) const; // Evaluate a boolean expression using the full expressive power of the PlaceholderParser boolean expression syntax. // Throws std::runtime_error on syntax or runtime error. diff --git a/t/custom_gcode.t b/t/custom_gcode.t index 5ffd9b7f4..44f952318 100644 --- a/t/custom_gcode.t +++ b/t/custom_gcode.t @@ -1,4 +1,4 @@ -use Test::More tests => 81; +use Test::More tests => 41; use strict; use warnings; @@ -45,73 +45,6 @@ use Slic3r::Test; #========================================================== -{ - my $parser = Slic3r::GCode::PlaceholderParser->new; - my $config = Slic3r::Config::new_from_defaults; - $config->set('printer_notes', ' PRINTER_VENDOR_PRUSA3D PRINTER_MODEL_MK2 '); - $config->set('nozzle_diameter', [0.6,0.6,0.6,0.6]); - $parser->apply_config($config); - $parser->set('foo' => 0); - $parser->set('bar' => 2); - $parser->set('num_extruders' => 4); - is $parser->process('[temperature_[foo]]'), - $config->temperature->[0], - "nested config options (legacy syntax)"; - is $parser->process('{temperature[foo]}'), - $config->temperature->[0], - "array reference"; - is $parser->process("test [ temperature_ [foo] ] \n hu"), - "test " . $config->temperature->[0] . " \n hu", - "whitespaces and newlines are maintained"; - is $parser->process('{2*3}'), '6', 'math: 2*3'; - is $parser->process('{2*3/6}'), '1', 'math: 2*3/6'; - is $parser->process('{2*3/12}'), '0', 'math: 2*3/12'; - ok abs($parser->process('{2.*3/12}') - 0.5) < 1e-7, 'math: 2.*3/12'; - is $parser->process('{10%2.5}') '0', 'math: 10 % 2.5'; - is $parser->process('{11/2.5-1}') '1', 'math: 11 % 2.5'; - is $parser->process('{2*(3-12)}'), '-18', 'math: 2*(3-12)'; - is $parser->process('{2*foo*(3-12)}'), '0', 'math: 2*foo*(3-12)'; - is $parser->process('{2*bar*(3-12)}'), '-36', 'math: 2*bar*(3-12)'; - ok abs($parser->process('{2.5*bar*(3-12)}') - -45) < 1e-7, 'math: 2.5*bar*(3-12)'; - is $parser->process('{min(12, 14)}'), '12', 'math: min(12, 14)'; - is $parser->process('{max(12, 14)}'), '14', 'math: max(12, 14)'; - is $parser->process('{min(13.4, -1238.1)}'), '-1238.1', 'math: min(13.4, -1238.1)'; - is $parser->process('{max(13.4, -1238.1)}'), '13.4', 'math: max(13.4, -1238.1)'; - is $parser->process('{int(13.4)}'), '13', 'math: int(13.4)'; - - # Test the boolean expression parser. - is $parser->evaluate_boolean_expression('12 == 12'), 1, 'boolean expression parser: 12 == 12'; - is $parser->evaluate_boolean_expression('12 != 12'), 0, 'boolean expression parser: 12 != 12'; - is $parser->evaluate_boolean_expression('"has some PATTERN embedded" =~ /.*PATTERN.*/'), 1, 'boolean expression parser: regex matches'; - is $parser->evaluate_boolean_expression('"has some PATTERN embedded" =~ /.*PTRN.*/'), 0, 'boolean expression parser: regex does not match'; - is $parser->evaluate_boolean_expression('foo + 2 == bar'), 1, 'boolean expression parser: accessing variables, equal'; - is $parser->evaluate_boolean_expression('foo + 3 == bar'), 0, 'boolean expression parser: accessing variables, not equal'; - - is $parser->evaluate_boolean_expression('(12 == 12) and (13 != 14)'), 1, 'boolean expression parser: (12 == 12) and (13 != 14)'; - is $parser->evaluate_boolean_expression('(12 == 12) && (13 != 14)'), 1, 'boolean expression parser: (12 == 12) && (13 != 14)'; - is $parser->evaluate_boolean_expression('(12 == 12) or (13 == 14)'), 1, 'boolean expression parser: (12 == 12) or (13 == 14)'; - is $parser->evaluate_boolean_expression('(12 == 12) || (13 == 14)'), 1, 'boolean expression parser: (12 == 12) || (13 == 14)'; - is $parser->evaluate_boolean_expression('(12 == 12) and not (13 == 14)'), 1, 'boolean expression parser: (12 == 12) and not (13 == 14)'; - is $parser->evaluate_boolean_expression('(12 == 12) ? (1 - 1 == 0) : (2 * 2 == 3)'), 1, 'boolean expression parser: ternary true'; - is $parser->evaluate_boolean_expression('(12 == 21/2) ? (1 - 1 == 0) : (2 * 2 == 3)'), 0, 'boolean expression parser: ternary false'; - is $parser->evaluate_boolean_expression('(12 == 13) ? (1 - 1 == 3) : (2 * 2 == 4)'), 1, 'boolean expression parser: ternary false'; - is $parser->evaluate_boolean_expression('(12 == 2 * 6) ? (1 - 1 == 3) : (2 * 2 == 4)'), 0, 'boolean expression parser: ternary true'; - is $parser->evaluate_boolean_expression('12 < 3'), 0, 'boolean expression parser: lower than - false'; - is $parser->evaluate_boolean_expression('12 < 22'), 1, 'boolean expression parser: lower than - true'; - is $parser->evaluate_boolean_expression('12 > 3'), 1, 'boolean expression parser: greater than - true'; - is $parser->evaluate_boolean_expression('12 > 22'), 0, 'boolean expression parser: greater than - false'; - is $parser->evaluate_boolean_expression('12 <= 3'), 0, 'boolean expression parser: lower than or equal- false'; - is $parser->evaluate_boolean_expression('12 <= 22'), 1, 'boolean expression parser: lower than or equal - true'; - is $parser->evaluate_boolean_expression('12 >= 3'), 1, 'boolean expression parser: greater than or equal - true'; - is $parser->evaluate_boolean_expression('12 >= 22'), 0, 'boolean expression parser: greater than or equal - false'; - is $parser->evaluate_boolean_expression('12 <= 12'), 1, 'boolean expression parser: lower than or equal (same values) - true'; - is $parser->evaluate_boolean_expression('12 >= 12'), 1, 'boolean expression parser: greater than or equal (same values) - true'; - - is $parser->evaluate_boolean_expression('printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.6 and num_extruders>1'), 1, 'complex expression'; - is $parser->evaluate_boolean_expression('printer_notes=~/.*PRINTER_VEwerfNDOR_PRUSA3D.*/ or printer_notes=~/.*PRINTertER_MODEL_MK2.*/ or (nozzle_diameter[0]==0.6 and num_extruders>1)'), 1, 'complex expression2'; - is $parser->evaluate_boolean_expression('printer_notes=~/.*PRINTER_VEwerfNDOR_PRUSA3D.*/ or printer_notes=~/.*PRINTertER_MODEL_MK2.*/ or (nozzle_diameter[0]==0.3 and num_extruders>1)'), 0, 'complex expression3'; -} - { my $config = Slic3r::Config::new_from_defaults; $config->set('output_filename_format', 'ts_[travel_speed]_lh_[layer_height].gcode'); diff --git a/tests/libslic3r/CMakeLists.txt b/tests/libslic3r/CMakeLists.txt index 02764589b..e34a40f34 100644 --- a/tests/libslic3r/CMakeLists.txt +++ b/tests/libslic3r/CMakeLists.txt @@ -7,6 +7,7 @@ add_executable(${_TEST_NAME}_tests test_config.cpp test_elephant_foot_compensation.cpp test_geometry.cpp + test_placeholder_parser.cpp test_polygon.cpp test_stl.cpp ) diff --git a/tests/libslic3r/test_placeholder_parser.cpp b/tests/libslic3r/test_placeholder_parser.cpp new file mode 100644 index 000000000..5802862b7 --- /dev/null +++ b/tests/libslic3r/test_placeholder_parser.cpp @@ -0,0 +1,74 @@ +#include + +#include "libslic3r/PlaceholderParser.hpp" +#include "libslic3r/PrintConfig.hpp" + +using namespace Slic3r; + +SCENARIO("Placeholder parser scripting", "[PlaceholderParser]") { + PlaceholderParser parser; + auto config = DynamicPrintConfig::full_print_config(); + + config.set_deserialize( { + { "printer_notes", " PRINTER_VENDOR_PRUSA3D PRINTER_MODEL_MK2 " }, + { "nozzle_diameter", "0.6;0.6;0.6;0.6" }, + { "temperature", "357;359;363;378" } + }); + parser.apply_config(config); + parser.set("foo", 0); + parser.set("bar", 2); + parser.set("num_extruders", 4); + + SECTION("nested config options (legacy syntax)") { REQUIRE(parser.process("[temperature_[foo]]") == "357"); } + SECTION("array reference") { REQUIRE(parser.process("{temperature[foo]}") == "357"); } + SECTION("whitespaces and newlines are maintained") { REQUIRE(parser.process("test [ temperature_ [foo] ] \n hu") == "test 357 \n hu"); } + + // Test the math expressions. + SECTION("math: 2*3") { REQUIRE(parser.process("{2*3}") == "6"); } + SECTION("math: 2*3/6") { REQUIRE(parser.process("{2*3/6}") == "1"); } + SECTION("math: 2*3/12") { REQUIRE(parser.process("{2*3/12}") == "0"); } + SECTION("math: 2.*3/12") { REQUIRE(std::stod(parser.process("{2.*3/12}")) == Approx(0.5)); } +// SECTION("math: 10 % 2.5") { REQUIRE(parser.process("{10%2.5}") == "0"); } +// SECTION("math: 11 / 2.5") { REQUIRE(parser.process("{11/2.5-1}") == "1"); } + SECTION("math: 2*(3-12)") { REQUIRE(parser.process("{2*(3-12)}") == "-18"); } + SECTION("math: 2*foo*(3-12)") { REQUIRE(parser.process("{2*foo*(3-12)}") == "0"); } + SECTION("math: 2*bar*(3-12)") { REQUIRE(parser.process("{2*bar*(3-12)}") == "-36"); } + SECTION("math: 2.5*bar*(3-12)") { REQUIRE(std::stod(parser.process("{2.5*bar*(3-12)}")) == Approx(-45)); } + SECTION("math: min(12, 14)") { REQUIRE(parser.process("{min(12, 14)}") == "12"); } + SECTION("math: max(12, 14)") { REQUIRE(parser.process("{max(12, 14)}") == "14"); } + SECTION("math: min(13.4, -1238.1)") { REQUIRE(std::stod(parser.process("{min(13.4, -1238.1)}")) == Approx(-1238.1)); } + SECTION("math: max(13.4, -1238.1)") { REQUIRE(std::stod(parser.process("{max(13.4, -1238.1)}")) == Approx(13.4)); } +// SECTION("math: int(13.4)") { REQUIRE(parser.process("{int(13.4)}") == "13"); } + + // Test the boolean expression parser. + auto boolean_expression = [&parser](const std::string& templ) { return parser.evaluate_boolean_expression(templ, parser.config()); }; + + SECTION("boolean expression parser: 12 == 12") { REQUIRE(boolean_expression("12 == 12")); } + SECTION("boolean expression parser: 12 != 12") { REQUIRE(! boolean_expression("12 != 12")); } + SECTION("boolean expression parser: regex matches") { REQUIRE(boolean_expression("\"has some PATTERN embedded\" =~ /.*PATTERN.*/")); } + SECTION("boolean expression parser: regex does not match") { REQUIRE(! boolean_expression("\"has some PATTERN embedded\" =~ /.*PTRN.*/")); } + SECTION("boolean expression parser: accessing variables, equal") { REQUIRE(boolean_expression("foo + 2 == bar")); } + SECTION("boolean expression parser: accessing variables, not equal") { REQUIRE(! boolean_expression("foo + 3 == bar")); } + SECTION("boolean expression parser: (12 == 12) and (13 != 14)") { REQUIRE(boolean_expression("(12 == 12) and (13 != 14)")); } + SECTION("boolean expression parser: (12 == 12) && (13 != 14)") { REQUIRE(boolean_expression("(12 == 12) && (13 != 14)")); } + SECTION("boolean expression parser: (12 == 12) or (13 == 14)") { REQUIRE(boolean_expression("(12 == 12) or (13 == 14)")); } + SECTION("boolean expression parser: (12 == 12) || (13 == 14)") { REQUIRE(boolean_expression("(12 == 12) || (13 == 14)")); } + SECTION("boolean expression parser: (12 == 12) and not (13 == 14)") { REQUIRE(boolean_expression("(12 == 12) and not (13 == 14)")); } + SECTION("boolean expression parser: ternary true") { REQUIRE(boolean_expression("(12 == 12) ? (1 - 1 == 0) : (2 * 2 == 3)")); } + SECTION("boolean expression parser: ternary false") { REQUIRE(! boolean_expression("(12 == 21/2) ? (1 - 1 == 0) : (2 * 2 == 3)")); } + SECTION("boolean expression parser: ternary false 2") { REQUIRE(boolean_expression("(12 == 13) ? (1 - 1 == 3) : (2 * 2 == 4)")); } + SECTION("boolean expression parser: ternary true 2") { REQUIRE(! boolean_expression("(12 == 2 * 6) ? (1 - 1 == 3) : (2 * 2 == 4)")); } + SECTION("boolean expression parser: lower than - false") { REQUIRE(! boolean_expression("12 < 3")); } + SECTION("boolean expression parser: lower than - true") { REQUIRE(boolean_expression("12 < 22")); } + SECTION("boolean expression parser: greater than - true") { REQUIRE(boolean_expression("12 > 3")); } + SECTION("boolean expression parser: greater than - false") { REQUIRE(! boolean_expression("12 > 22")); } + SECTION("boolean expression parser: lower than or equal- false") { REQUIRE(! boolean_expression("12 <= 3")); } + SECTION("boolean expression parser: lower than or equal - true") { REQUIRE(boolean_expression("12 <= 22")); } + SECTION("boolean expression parser: greater than or equal - true") { REQUIRE(boolean_expression("12 >= 3")); } + SECTION("boolean expression parser: greater than or equal - false") { REQUIRE(! boolean_expression("12 >= 22")); } + SECTION("boolean expression parser: lower than or equal (same values) - true") { REQUIRE(boolean_expression("12 <= 12")); } + SECTION("boolean expression parser: greater than or equal (same values) - true") { REQUIRE(boolean_expression("12 >= 12")); } + SECTION("complex expression") { REQUIRE(boolean_expression("printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.6 and num_extruders>1")); } + SECTION("complex expression2") { REQUIRE(boolean_expression("printer_notes=~/.*PRINTER_VEwerfNDOR_PRUSA3D.*/ or printer_notes=~/.*PRINTertER_MODEL_MK2.*/ or (nozzle_diameter[0]==0.6 and num_extruders>1)")); } + SECTION("complex expression3") { REQUIRE(! boolean_expression("printer_notes=~/.*PRINTER_VEwerfNDOR_PRUSA3D.*/ or printer_notes=~/.*PRINTertER_MODEL_MK2.*/ or (nozzle_diameter[0]==0.3 and num_extruders>1)")); } +} From fa3d138e91021dabbf3710ceb77c833f34e879b9 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 21 Jan 2020 13:13:52 +0100 Subject: [PATCH 09/17] Fix of the previous commit: Fixed number of unit tests to be executed. --- t/custom_gcode.t | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/t/custom_gcode.t b/t/custom_gcode.t index 44f952318..1bb52b618 100644 --- a/t/custom_gcode.t +++ b/t/custom_gcode.t @@ -1,4 +1,4 @@ -use Test::More tests => 41; +use Test::More tests => 38; use strict; use warnings; From b08788cf96de313a2702ae0f3ef0298a8ade226e Mon Sep 17 00:00:00 2001 From: David Kocik Date: Thu, 2 Jan 2020 11:42:48 +0100 Subject: [PATCH 10/17] typo at Unmounting successful message --- src/slic3r/GUI/Plater.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index b388ad1d2..80a3646f0 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5051,7 +5051,7 @@ void Plater::drive_ejected_callback() if (RemovableDriveManager::get_instance().get_did_eject()) { RemovableDriveManager::get_instance().set_did_eject(false); - wxString message = "Unmounting succesesful. The device " + RemovableDriveManager::get_instance().get_last_save_name() + "(" + RemovableDriveManager::get_instance().get_last_save_path() + ")" + " can now be safely removed from the computer."; + wxString message = "Unmounting successful. The device " + RemovableDriveManager::get_instance().get_last_save_name() + "(" + RemovableDriveManager::get_instance().get_last_save_path() + ")" + " can now be safely removed from the computer."; wxMessageBox(message); } p->show_action_buttons(false); From 7e97576e564a32ac00d9432ca5df71c503458f4e Mon Sep 17 00:00:00 2001 From: David Kocik Date: Thu, 2 Jan 2020 16:30:28 +0100 Subject: [PATCH 11/17] button for exporting gcode to harddrive --- src/slic3r/GUI/AppConfig.cpp | 25 +++++++++- src/slic3r/GUI/AppConfig.hpp | 6 ++- src/slic3r/GUI/Plater.cpp | 60 +++++++++++++++--------- src/slic3r/GUI/Plater.hpp | 3 +- src/slic3r/GUI/RemovableDriveManager.cpp | 6 ++- src/slic3r/GUI/RemovableDriveManager.hpp | 3 +- 6 files changed, 74 insertions(+), 29 deletions(-) diff --git a/src/slic3r/GUI/AppConfig.cpp b/src/slic3r/GUI/AppConfig.cpp index 12302a5dc..a410f3ad8 100644 --- a/src/slic3r/GUI/AppConfig.cpp +++ b/src/slic3r/GUI/AppConfig.cpp @@ -78,6 +78,9 @@ void AppConfig::set_defaults() if (get("remember_output_path").empty()) set("remember_output_path", "1"); + if (get("remember_output_path_removable").empty()) + set("remember_output_path_removable", "1"); + if (get("use_custom_toolbar_size").empty()) set("use_custom_toolbar_size", "0"); @@ -388,7 +391,7 @@ void AppConfig::update_skein_dir(const std::string &dir) { this->set("recent", "skein_directory", dir); } - +/* std::string AppConfig::get_last_output_dir(const std::string &alt) const { @@ -406,6 +409,26 @@ void AppConfig::update_last_output_dir(const std::string &dir) { this->set("", "last_output_path", dir); } +*/ +std::string AppConfig::get_last_output_dir(const std::string& alt, const bool removable) const +{ + std::string s1 = (removable ? "last_output_path_removable" : "last_output_path"); + std::string s2 = (removable ? "remember_output_path_removable" : "remember_output_path"); + const auto it = m_storage.find(""); + if (it != m_storage.end()) { + const auto it2 = it->second.find(s1); + const auto it3 = it->second.find(s2); + if (it2 != it->second.end() && it3 != it->second.end() && !it2->second.empty() && it3->second == "1") + return it2->second; + } + return alt; +} + +void AppConfig::update_last_output_dir(const std::string& dir, const bool removable) +{ + this->set("", (removable ? "last_output_path_removable" : "last_output_path"), dir); +} + void AppConfig::reset_selections() { diff --git a/src/slic3r/GUI/AppConfig.hpp b/src/slic3r/GUI/AppConfig.hpp index b432367b6..32f1c32c8 100644 --- a/src/slic3r/GUI/AppConfig.hpp +++ b/src/slic3r/GUI/AppConfig.hpp @@ -102,8 +102,10 @@ public: void update_config_dir(const std::string &dir); void update_skein_dir(const std::string &dir); - std::string get_last_output_dir(const std::string &alt) const; - void update_last_output_dir(const std::string &dir); + //std::string get_last_output_dir(const std::string &alt) const; + //void update_last_output_dir(const std::string &dir); + std::string get_last_output_dir(const std::string& alt, const bool removable = false) const; + void update_last_output_dir(const std::string &dir, const bool removable = false); // reset the current print / filament / printer selections, so that // the PresetBundle::load_selections(const AppConfig &config) call will select diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 80a3646f0..c7f374085 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -701,6 +701,7 @@ struct Sidebar::priv wxButton *btn_reslice; ScalableButton *btn_send_gcode; ScalableButton *btn_remove_device; + ScalableButton* btn_export_gcode_removable; //exports to NON-removable drives (but appears only if removable drive is connected) priv(Plater *plater) : plater(plater) {} ~priv(); @@ -866,6 +867,7 @@ Sidebar::Sidebar(Plater *parent) init_scalable_btn(&p->btn_send_gcode , "export_gcode", _(L("Send to printer"))); init_scalable_btn(&p->btn_remove_device, "cross" , _(L("Remove device"))); + init_scalable_btn(&p->btn_export_gcode_removable, "export_gcode", _(L("Export to hard drive"))); // regular buttons "Slice now" and "Export G-code" @@ -887,6 +889,7 @@ Sidebar::Sidebar(Plater *parent) complect_btns_sizer->Add(p->btn_export_gcode, 1, wxEXPAND); complect_btns_sizer->Add(p->btn_send_gcode); complect_btns_sizer->Add(p->btn_remove_device); + complect_btns_sizer->Add(p->btn_export_gcode_removable); btns_sizer->Add(p->btn_reslice, 0, wxEXPAND | wxTOP, margin_5); btns_sizer->Add(complect_btns_sizer, 0, wxEXPAND | wxTOP, margin_5); @@ -897,7 +900,7 @@ Sidebar::Sidebar(Plater *parent) SetSizer(sizer); // Events - p->btn_export_gcode->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->export_gcode(); }); + p->btn_export_gcode->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->export_gcode(true); }); p->btn_reslice->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { const bool export_gcode_after_slicing = wxGetKeyState(WXK_SHIFT); @@ -909,6 +912,7 @@ Sidebar::Sidebar(Plater *parent) }); p->btn_send_gcode->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->send_gcode(); }); p->btn_remove_device->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->eject_drive(); }); + p->btn_export_gcode_removable->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->export_gcode(false); }); } Sidebar::~Sidebar() {} @@ -1056,6 +1060,7 @@ void Sidebar::msw_rescale() p->btn_send_gcode->msw_rescale(); p->btn_remove_device->msw_rescale(); + p->btn_export_gcode_removable->msw_rescale(); const int scaled_height = p->btn_remove_device->GetBitmap().GetHeight() + 4; p->btn_export_gcode->SetMinSize(wxSize(-1, scaled_height)); p->btn_reslice ->SetMinSize(wxSize(-1, scaled_height)); @@ -1289,12 +1294,14 @@ void Sidebar::enable_buttons(bool enable) p->btn_export_gcode->Enable(enable); p->btn_send_gcode->Enable(enable); p->btn_remove_device->Enable(enable); + p->btn_export_gcode_removable->Enable(enable); } -bool Sidebar::show_reslice(bool show) const { return p->btn_reslice->Show(show); } -bool Sidebar::show_export(bool show) const { return p->btn_export_gcode->Show(show); } -bool Sidebar::show_send(bool show) const { return p->btn_send_gcode->Show(show); } -bool Sidebar::show_disconnect(bool show)const { return p->btn_remove_device->Show(show); } +bool Sidebar::show_reslice(bool show) const { return p->btn_reslice->Show(show); } +bool Sidebar::show_export(bool show) const { return p->btn_export_gcode->Show(show); } +bool Sidebar::show_send(bool show) const { return p->btn_send_gcode->Show(show); } +bool Sidebar::show_disconnect(bool show) const { return p->btn_remove_device->Show(show); } +bool Sidebar::show_export_removable(bool show)const { return p->btn_export_gcode_removable->Show(show); } bool Sidebar::is_multifilament() { @@ -3596,12 +3603,8 @@ void Plater::priv::on_process_completed(wxCommandEvent &evt) if(!canceled && RemovableDriveManager::get_instance().get_is_writing()) { - //if (!RemovableDriveManager::get_instance().is_last_drive_removed()) - //{ - RemovableDriveManager::get_instance().set_is_writing(false); - show_action_buttons(false); - //} - + RemovableDriveManager::get_instance().set_is_writing(false); + show_action_buttons(false); } } @@ -4160,14 +4163,16 @@ void Plater::priv::show_action_buttons(const bool is_ready_to_slice) const const auto prin_host_opt = config->option("print_host"); const bool send_gcode_shown = prin_host_opt != nullptr && !prin_host_opt->value.empty(); - bool disconnect_shown = !RemovableDriveManager::get_instance().is_last_drive_removed() ; // #dk_FIXME + bool disconnect_shown = !RemovableDriveManager::get_instance().is_last_drive_removed(); + bool export_removable_shown = RemovableDriveManager::get_instance().get_drives_count() > 0; // when a background processing is ON, export_btn and/or send_btn are showing if (wxGetApp().app_config->get("background_processing") == "1") { - if (sidebar->show_reslice(false) | - sidebar->show_export(true) | - sidebar->show_send(send_gcode_shown) | - sidebar->show_disconnect(disconnect_shown)) + if (sidebar->show_reslice(false) | + sidebar->show_export(true) | + sidebar->show_send(send_gcode_shown) | + sidebar->show_disconnect(disconnect_shown) | + sidebar->show_export_removable(export_removable_shown)) sidebar->Layout(); } else @@ -4175,7 +4180,8 @@ void Plater::priv::show_action_buttons(const bool is_ready_to_slice) const if (sidebar->show_reslice(is_ready_to_slice) | sidebar->show_export(!is_ready_to_slice) | sidebar->show_send(send_gcode_shown && !is_ready_to_slice) | - sidebar->show_disconnect(disconnect_shown && !is_ready_to_slice)) + sidebar->show_disconnect(disconnect_shown && !is_ready_to_slice) | + sidebar->show_export_removable(export_removable_shown && !is_ready_to_slice)) sidebar->Layout(); } } @@ -4684,7 +4690,7 @@ void Plater::cut(size_t obj_idx, size_t instance_idx, coordf_t z, bool keep_uppe } } -void Plater::export_gcode() +void Plater::export_gcode(bool prefer_removable) { if (p->model.objects.empty()) return; @@ -4706,11 +4712,19 @@ void Plater::export_gcode() } default_output_file = fs::path(Slic3r::fold_utf8_to_ascii(default_output_file.string())); auto start_dir = wxGetApp().app_config->get_last_output_dir(default_output_file.parent_path().string()); - if (GUI::RemovableDriveManager::get_instance().update()) + bool removable_drives_connected = GUI::RemovableDriveManager::get_instance().update(); + if(prefer_removable) { - if (!RemovableDriveManager::get_instance().is_path_on_removable_drive(start_dir)) + if(removable_drives_connected) { - start_dir = RemovableDriveManager::get_instance().get_drive_path(); + auto start_dir_removable = wxGetApp().app_config->get_last_output_dir(default_output_file.parent_path().string(), true); + if (RemovableDriveManager::get_instance().is_path_on_removable_drive(start_dir_removable)) + { + start_dir = start_dir_removable; + }else + { + start_dir = RemovableDriveManager::get_instance().get_drive_path(); + } } } wxFileDialog dlg(this, (printer_technology() == ptFFF) ? _(L("Save G-code file as:")) : _(L("Save SL1 file as:")), @@ -4723,7 +4737,7 @@ void Plater::export_gcode() fs::path output_path; if (dlg.ShowModal() == wxID_OK) { fs::path path = into_path(dlg.GetPath()); - wxGetApp().app_config->update_last_output_dir(path.parent_path().string()); + wxGetApp().app_config->update_last_output_dir(path.parent_path().string(), RemovableDriveManager::get_instance().is_path_on_removable_drive(path.parent_path().string())); output_path = std::move(path); } if (! output_path.empty()) @@ -4739,7 +4753,7 @@ void Plater::export_gcode() { RemovableDriveManager::get_instance().set_is_writing(true); RemovableDriveManager::get_instance().erase_callbacks(); - RemovableDriveManager::get_instance().add_callback(std::bind(&Plater::drive_ejected_callback, this)); + RemovableDriveManager::get_instance().add_remove_callback(std::bind(&Plater::drive_ejected_callback, this)); } } diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 1bea07795..f9891a252 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -120,6 +120,7 @@ public: bool show_export(bool show) const; bool show_send(bool show) const; bool show_disconnect(bool show)const; + bool show_export_removable(bool show) const; bool is_multifilament(); void update_mode(); @@ -186,7 +187,7 @@ public: void cut(size_t obj_idx, size_t instance_idx, coordf_t z, bool keep_upper = true, bool keep_lower = true, bool rotate_lower = false); - void export_gcode(); + void export_gcode(bool prefer_removable = true); void export_stl(bool extended = false, bool selection_only = false); void export_amf(); void export_3mf(const boost::filesystem::path& output_path = boost::filesystem::path()); diff --git a/src/slic3r/GUI/RemovableDriveManager.cpp b/src/slic3r/GUI/RemovableDriveManager.cpp index 5041e2120..99da30ed8 100644 --- a/src/slic3r/GUI/RemovableDriveManager.cpp +++ b/src/slic3r/GUI/RemovableDriveManager.cpp @@ -503,7 +503,7 @@ void RemovableDriveManager::check_and_notify() } } } -void RemovableDriveManager::add_callback(std::function callback) +void RemovableDriveManager::add_remove_callback(std::function callback) { m_callbacks.push_back(callback); } @@ -587,4 +587,8 @@ void RemovableDriveManager::set_did_eject(const bool b) { m_did_eject = b; } +size_t RemovableDriveManager::get_drives_count() +{ + return m_current_drives.size(); +} }}//namespace Slicer::Gui diff --git a/src/slic3r/GUI/RemovableDriveManager.hpp b/src/slic3r/GUI/RemovableDriveManager.hpp index 1767490d1..b5b5c0952 100644 --- a/src/slic3r/GUI/RemovableDriveManager.hpp +++ b/src/slic3r/GUI/RemovableDriveManager.hpp @@ -45,7 +45,7 @@ public: std::vector get_all_drives(); bool is_path_on_removable_drive(const std::string &path); // callback will notify only if device with last save path was removed - void add_callback(std::function callback); + void add_remove_callback(std::function callback); // erases all callbacks added by add_callback() void erase_callbacks(); // marks one of the eveices in vector as last used @@ -59,6 +59,7 @@ public: bool get_did_eject(); void set_did_eject(const bool b); std::string get_drive_name(const std::string& path); + size_t get_drives_count(); private: RemovableDriveManager(); void search_for_drives(); From 53f04b4bfdddb6fbc533ffe9cfcafeda1243aea7 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Fri, 3 Jan 2020 10:33:44 +0100 Subject: [PATCH 12/17] callback for showing action buttons when device is connected/disconnected --- src/slic3r/GUI/Plater.cpp | 4 ++++ src/slic3r/GUI/RemovableDriveManager.cpp | 17 +++++++++++++++-- src/slic3r/GUI/RemovableDriveManager.hpp | 8 +++++++- 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index c7f374085..d54083e36 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2199,6 +2199,9 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) // Initialize the Undo / Redo stack with a first snapshot. this->take_snapshot(_(L("New Project"))); + + //void Plater::priv::show_action_buttons(const bool is_ready_to_slice) const + RemovableDriveManager::get_instance().set_drive_count_changed_callback(std::bind(&Plater::priv::show_action_buttons, this, std::placeholders::_1)); } Plater::priv::~priv() @@ -4159,6 +4162,7 @@ void Plater::priv::update_object_menu() void Plater::priv::show_action_buttons(const bool is_ready_to_slice) const { + RemovableDriveManager::get_instance().set_plater_ready_to_slice(is_ready_to_slice); wxWindowUpdateLocker noUpdater(sidebar); const auto prin_host_opt = config->option("print_host"); const bool send_gcode_shown = prin_host_opt != nullptr && !prin_host_opt->value.empty(); diff --git a/src/slic3r/GUI/RemovableDriveManager.cpp b/src/slic3r/GUI/RemovableDriveManager.cpp index 99da30ed8..74197d677 100644 --- a/src/slic3r/GUI/RemovableDriveManager.cpp +++ b/src/slic3r/GUI/RemovableDriveManager.cpp @@ -414,7 +414,8 @@ RemovableDriveManager::RemovableDriveManager(): m_last_save_name(""), m_last_save_path_verified(false), m_is_writing(false), - m_did_eject(false) + m_did_eject(false), + m_plater_ready_to_slice(true) #if __APPLE__ , m_rdmmm(new RDMMMWrapper()) #endif @@ -495,6 +496,10 @@ std::vector RemovableDriveManager::get_all_drives() } void RemovableDriveManager::check_and_notify() { + if(m_drive_count_changed_callback) + { + m_drive_count_changed_callback(m_plater_ready_to_slice); + } if(m_callbacks.size() != 0 && m_drives_count > m_current_drives.size() && m_last_save_path_verified && !is_drive_mounted(m_last_save_path)) { for (auto it = m_callbacks.begin(); it != m_callbacks.end(); ++it) @@ -507,10 +512,18 @@ void RemovableDriveManager::add_remove_callback(std::function callback) { m_callbacks.push_back(callback); } -void RemovableDriveManager::erase_callbacks() +void RemovableDriveManager::erase_callbacks() { m_callbacks.clear(); } +void RemovableDriveManager::set_drive_count_changed_callback(std::function callback) +{ + m_drive_count_changed_callback = callback; +} +void RemovableDriveManager::set_plater_ready_to_slice(bool b) +{ + m_plater_ready_to_slice = b; +} void RemovableDriveManager::set_last_save_path(const std::string& path) { m_last_save_path_verified = false; diff --git a/src/slic3r/GUI/RemovableDriveManager.hpp b/src/slic3r/GUI/RemovableDriveManager.hpp index b5b5c0952..8ba2bc64d 100644 --- a/src/slic3r/GUI/RemovableDriveManager.hpp +++ b/src/slic3r/GUI/RemovableDriveManager.hpp @@ -46,8 +46,12 @@ public: bool is_path_on_removable_drive(const std::string &path); // callback will notify only if device with last save path was removed void add_remove_callback(std::function callback); - // erases all callbacks added by add_callback() + // erases all remove callbacks added by add_remove_callback() void erase_callbacks(); + //drive_count_changed callback is called on every added or removed device + void set_drive_count_changed_callback(std::function callback); + //thi serves to set correct value for drive_count_changed callback + void set_plater_ready_to_slice(bool b); // marks one of the eveices in vector as last used void set_last_save_path(const std::string &path); void verify_last_save_path(); @@ -71,6 +75,7 @@ private: std::vector m_current_drives; std::vector> m_callbacks; + std::function m_drive_count_changed_callback; size_t m_drives_count; long m_last_update; std::string m_last_save_path; @@ -78,6 +83,7 @@ private: std::string m_last_save_name; bool m_is_writing;//on device bool m_did_eject; + bool m_plater_ready_to_slice; #if _WIN32 //registers for notifications by creating invisible window void register_window(); From 787a6264b1738cf55ed8045afa9d1c9100c65bb5 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Fri, 10 Jan 2020 16:25:32 +0100 Subject: [PATCH 13/17] changed button usage: save to hd is now save to sd card --- src/slic3r/GUI/Plater.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index d54083e36..7fd272d0a 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -701,7 +701,7 @@ struct Sidebar::priv wxButton *btn_reslice; ScalableButton *btn_send_gcode; ScalableButton *btn_remove_device; - ScalableButton* btn_export_gcode_removable; //exports to NON-removable drives (but appears only if removable drive is connected) + ScalableButton* btn_export_gcode_removable; //exports to removable drives (appears only if removable drive is connected) priv(Plater *plater) : plater(plater) {} ~priv(); @@ -867,7 +867,7 @@ Sidebar::Sidebar(Plater *parent) init_scalable_btn(&p->btn_send_gcode , "export_gcode", _(L("Send to printer"))); init_scalable_btn(&p->btn_remove_device, "cross" , _(L("Remove device"))); - init_scalable_btn(&p->btn_export_gcode_removable, "export_gcode", _(L("Export to hard drive"))); + init_scalable_btn(&p->btn_export_gcode_removable, "export_gcode", _(L("Export to SD card/ USB thumb drive"))); // regular buttons "Slice now" and "Export G-code" @@ -888,8 +888,9 @@ Sidebar::Sidebar(Plater *parent) auto* complect_btns_sizer = new wxBoxSizer(wxHORIZONTAL); complect_btns_sizer->Add(p->btn_export_gcode, 1, wxEXPAND); complect_btns_sizer->Add(p->btn_send_gcode); - complect_btns_sizer->Add(p->btn_remove_device); complect_btns_sizer->Add(p->btn_export_gcode_removable); + complect_btns_sizer->Add(p->btn_remove_device); + btns_sizer->Add(p->btn_reslice, 0, wxEXPAND | wxTOP, margin_5); btns_sizer->Add(complect_btns_sizer, 0, wxEXPAND | wxTOP, margin_5); @@ -900,7 +901,7 @@ Sidebar::Sidebar(Plater *parent) SetSizer(sizer); // Events - p->btn_export_gcode->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->export_gcode(true); }); + p->btn_export_gcode->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->export_gcode(false); }); p->btn_reslice->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { const bool export_gcode_after_slicing = wxGetKeyState(WXK_SHIFT); @@ -912,7 +913,7 @@ Sidebar::Sidebar(Plater *parent) }); p->btn_send_gcode->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->send_gcode(); }); p->btn_remove_device->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->eject_drive(); }); - p->btn_export_gcode_removable->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->export_gcode(false); }); + p->btn_export_gcode_removable->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->export_gcode(true); }); } Sidebar::~Sidebar() {} @@ -4175,8 +4176,8 @@ void Plater::priv::show_action_buttons(const bool is_ready_to_slice) const if (sidebar->show_reslice(false) | sidebar->show_export(true) | sidebar->show_send(send_gcode_shown) | - sidebar->show_disconnect(disconnect_shown) | - sidebar->show_export_removable(export_removable_shown)) + sidebar->show_export_removable(export_removable_shown) | + sidebar->show_disconnect(disconnect_shown)) sidebar->Layout(); } else @@ -4184,8 +4185,8 @@ void Plater::priv::show_action_buttons(const bool is_ready_to_slice) const if (sidebar->show_reslice(is_ready_to_slice) | sidebar->show_export(!is_ready_to_slice) | sidebar->show_send(send_gcode_shown && !is_ready_to_slice) | - sidebar->show_disconnect(disconnect_shown && !is_ready_to_slice) | - sidebar->show_export_removable(export_removable_shown && !is_ready_to_slice)) + sidebar->show_export_removable(export_removable_shown && !is_ready_to_slice) | + sidebar->show_disconnect(disconnect_shown && !is_ready_to_slice)) sidebar->Layout(); } } From 2c1bedf50365e44578cb2861f0965b88062c6260 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Mon, 20 Jan 2020 16:39:59 +0100 Subject: [PATCH 14/17] new graphics for export button --- resources/icons/export_to_sd.svg | 145 +++++++++++++++++++++++++++++++ src/slic3r/GUI/Plater.cpp | 2 +- 2 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 resources/icons/export_to_sd.svg diff --git a/resources/icons/export_to_sd.svg b/resources/icons/export_to_sd.svg new file mode 100644 index 000000000..c836b00a1 --- /dev/null +++ b/resources/icons/export_to_sd.svg @@ -0,0 +1,145 @@ + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 7fd272d0a..6906614f8 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -867,7 +867,7 @@ Sidebar::Sidebar(Plater *parent) init_scalable_btn(&p->btn_send_gcode , "export_gcode", _(L("Send to printer"))); init_scalable_btn(&p->btn_remove_device, "cross" , _(L("Remove device"))); - init_scalable_btn(&p->btn_export_gcode_removable, "export_gcode", _(L("Export to SD card/ USB thumb drive"))); + init_scalable_btn(&p->btn_export_gcode_removable, "export_to_sd", _(L("Export to SD card/ USB thumb drive"))); // regular buttons "Slice now" and "Export G-code" From 8cf2a978076cad1a1cecb8b62e3d1df55586df78 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Tue, 21 Jan 2020 10:23:50 +0100 Subject: [PATCH 15/17] removable drive manager bug fixes --- src/slic3r/GUI/Plater.cpp | 6 ++-- src/slic3r/GUI/RemovableDriveManager.cpp | 38 ++++++++++++++++++++---- src/slic3r/GUI/RemovableDriveManager.hpp | 4 +++ 3 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 6906614f8..15820c68a 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5060,8 +5060,8 @@ void Plater::send_gcode() void Plater::eject_drive() { RemovableDriveManager::get_instance().update(0, true); - //RemovableDriveManager::get_instance().erase_callbacks(); - //RemovableDriveManager::get_instance().add_callback(std::bind(&Plater::drive_ejected_callback, this)); + RemovableDriveManager::get_instance().erase_callbacks(); + RemovableDriveManager::get_instance().add_remove_callback(std::bind(&Plater::drive_ejected_callback, this)); RemovableDriveManager::get_instance().eject_drive(RemovableDriveManager::get_instance().get_last_save_path()); } @@ -5070,7 +5070,7 @@ void Plater::drive_ejected_callback() if (RemovableDriveManager::get_instance().get_did_eject()) { RemovableDriveManager::get_instance().set_did_eject(false); - wxString message = "Unmounting successful. The device " + RemovableDriveManager::get_instance().get_last_save_name() + "(" + RemovableDriveManager::get_instance().get_last_save_path() + ")" + " can now be safely removed from the computer."; + wxString message = "Unmounting successful. The device " + RemovableDriveManager::get_instance().get_ejected_name() + "(" + RemovableDriveManager::get_instance().get_ejected_path() + ")" + " can now be safely removed from the computer."; wxMessageBox(message); } p->show_action_buttons(false); diff --git a/src/slic3r/GUI/RemovableDriveManager.cpp b/src/slic3r/GUI/RemovableDriveManager.cpp index 74197d677..76e028994 100644 --- a/src/slic3r/GUI/RemovableDriveManager.cpp +++ b/src/slic3r/GUI/RemovableDriveManager.cpp @@ -109,6 +109,8 @@ void RemovableDriveManager::eject_drive(const std::string &path) CloseHandle(handle); m_did_eject = true; m_current_drives.erase(it); + m_ejected_path = m_last_save_path; + m_ejected_name = m_last_save_name; break; } } @@ -373,7 +375,8 @@ void RemovableDriveManager::eject_drive(const std::string &path) m_did_eject = true; m_current_drives.erase(it); - + m_ejected_path = m_last_save_path; + m_ejected_name = m_last_save_name; break; } @@ -415,7 +418,9 @@ RemovableDriveManager::RemovableDriveManager(): m_last_save_path_verified(false), m_is_writing(false), m_did_eject(false), - m_plater_ready_to_slice(true) + m_plater_ready_to_slice(true), + m_ejected_path(""), + m_ejected_name("") #if __APPLE__ , m_rdmmm(new RDMMMWrapper()) #endif @@ -452,7 +457,10 @@ bool RemovableDriveManager::update(const long time,const bool check) search_for_drives(); if (m_drives_count != m_current_drives.size()) { - if (check)check_and_notify(); + if (check) + { + check_and_notify(); + } m_drives_count = m_current_drives.size(); } return !m_current_drives.empty(); @@ -500,7 +508,8 @@ void RemovableDriveManager::check_and_notify() { m_drive_count_changed_callback(m_plater_ready_to_slice); } - if(m_callbacks.size() != 0 && m_drives_count > m_current_drives.size() && m_last_save_path_verified && !is_drive_mounted(m_last_save_path)) + std::cout << m_callbacks.size() << m_last_save_path_verified << !is_drive_mounted(m_last_save_path) << std::endl; + if(m_callbacks.size() != 0 && m_drives_count > m_current_drives.size() /*&& m_last_save_path_verified */&& !is_drive_mounted(m_last_save_path)) { for (auto it = m_callbacks.begin(); it != m_callbacks.end(); ++it) { @@ -526,8 +535,17 @@ void RemovableDriveManager::set_plater_ready_to_slice(bool b) } void RemovableDriveManager::set_last_save_path(const std::string& path) { - m_last_save_path_verified = false; - m_last_save_path = path; + if(m_last_save_path_verified)// if old path is on drive + { + if(get_drive_from_path(path) != "") //and new is too, rewrite the path + { + m_last_save_path_verified = false; + m_last_save_path = path; + }//else do nothing + }else + { + m_last_save_path = path; + } } void RemovableDriveManager::verify_last_save_path() { @@ -604,4 +622,12 @@ size_t RemovableDriveManager::get_drives_count() { return m_current_drives.size(); } +std::string RemovableDriveManager::get_ejected_path() +{ + return m_ejected_path; +} +std::string RemovableDriveManager::get_ejected_name() +{ + return m_ejected_name; +} }}//namespace Slicer::Gui diff --git a/src/slic3r/GUI/RemovableDriveManager.hpp b/src/slic3r/GUI/RemovableDriveManager.hpp index 8ba2bc64d..6bd90e98c 100644 --- a/src/slic3r/GUI/RemovableDriveManager.hpp +++ b/src/slic3r/GUI/RemovableDriveManager.hpp @@ -64,6 +64,8 @@ public: void set_did_eject(const bool b); std::string get_drive_name(const std::string& path); size_t get_drives_count(); + std::string get_ejected_path(); + std::string get_ejected_name(); private: RemovableDriveManager(); void search_for_drives(); @@ -84,6 +86,8 @@ private: bool m_is_writing;//on device bool m_did_eject; bool m_plater_ready_to_slice; + std::string m_ejected_path; + std::string m_ejected_name; #if _WIN32 //registers for notifications by creating invisible window void register_window(); From 4c46bece96e7dc84768084213a8a14154ac025b8 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Tue, 21 Jan 2020 13:06:10 +0100 Subject: [PATCH 16/17] removable drive manager bug fixes linux --- src/slic3r/GUI/RemovableDriveManager.cpp | 42 ++++-------------------- 1 file changed, 7 insertions(+), 35 deletions(-) diff --git a/src/slic3r/GUI/RemovableDriveManager.cpp b/src/slic3r/GUI/RemovableDriveManager.cpp index 76e028994..4ed6d36a4 100644 --- a/src/slic3r/GUI/RemovableDriveManager.cpp +++ b/src/slic3r/GUI/RemovableDriveManager.cpp @@ -241,35 +241,10 @@ void RemovableDriveManager::search_for_drives() //search /media/* folder search_path("/media/*", "/media"); - //search /Volumes/* folder (OSX) //search_path("/Volumes/*", "/Volumes"); std::string path(std::getenv("USER")); std::string pp(path); - //std::cout << "user: "<< path << "\n"; - //if program is run with sudo, we have to search for all users - // but do we want that? - /* - if(path == "root"){ - while (true) { - passwd* entry = getpwent(); - if (!entry) { - break; - } - path = entry->pw_name; - pp = path; - //search /media/USERNAME/* folder - pp = "/media/"+pp; - path = "/media/" + path + "/*"; - search_path(path, pp); - //search /run/media/USERNAME/* folder - path = "/run" + path; - pp = "/run"+pp; - search_path(path, pp); - } - endpwent(); - }else - */ { //search /media/USERNAME/* folder pp = "/media/"+pp; @@ -312,7 +287,6 @@ void RemovableDriveManager::inspect_file(const std::string &path, const std::str { //free space boost::filesystem::space_info si = boost::filesystem::space(path); - //std::cout << "Free space: " << fs_si.free << "Available space: " << fs_si.available << " " << path << '\n'; if(si.available != 0) { //user id @@ -355,7 +329,7 @@ void RemovableDriveManager::eject_drive(const std::string &path) i++; } } - std::cout<<"Ejecting "<<(*it).name<<" from "<< correct_path<<"\n"; + //std::cout<<"Ejecting "<<(*it).name<<" from "<< correct_path<<"\n"; // there is no usable command in c++ so terminal command is used instead // but neither triggers "succesful safe removal messege" std::string command = ""; @@ -388,7 +362,7 @@ bool RemovableDriveManager::is_path_on_removable_drive(const std::string &path) if (m_current_drives.empty()) return false; std::size_t found = path.find_last_of("/"); - std::string new_path = path.substr(0,found); + std::string new_path = found == path.size() - 1 ? path.substr(0, found) : path; for (auto it = m_current_drives.begin(); it != m_current_drives.end(); ++it) { if(compare_filesystem_id(new_path, (*it).path)) @@ -399,7 +373,7 @@ bool RemovableDriveManager::is_path_on_removable_drive(const std::string &path) std::string RemovableDriveManager::get_drive_from_path(const std::string& path) { std::size_t found = path.find_last_of("/"); - std::string new_path = path.substr(0, found); + std::string new_path = found == path.size() - 1 ? path.substr(0, found) : path; //check if same filesystem for (auto it = m_current_drives.begin(); it != m_current_drives.end(); ++it) { @@ -508,7 +482,6 @@ void RemovableDriveManager::check_and_notify() { m_drive_count_changed_callback(m_plater_ready_to_slice); } - std::cout << m_callbacks.size() << m_last_save_path_verified << !is_drive_mounted(m_last_save_path) << std::endl; if(m_callbacks.size() != 0 && m_drives_count > m_current_drives.size() /*&& m_last_save_path_verified */&& !is_drive_mounted(m_last_save_path)) { for (auto it = m_callbacks.begin(); it != m_callbacks.end(); ++it) @@ -575,16 +548,15 @@ std::string RemovableDriveManager::get_drive_name(const std::string& path) } bool RemovableDriveManager::is_last_drive_removed() { - //std::cout<<"is last: "< Date: Thu, 2 Jan 2020 11:28:37 +0100 Subject: [PATCH 17/17] Throwing exceptions with text after copy file check failure and renaming from .tmp failure --- src/libslic3r/utils.cpp | 10 +++++----- src/slic3r/GUI/BackgroundSlicingProcess.cpp | 17 ++++++++++++++++- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/libslic3r/utils.cpp b/src/libslic3r/utils.cpp index 36653cdaf..f91d32d28 100644 --- a/src/libslic3r/utils.cpp +++ b/src/libslic3r/utils.cpp @@ -449,7 +449,7 @@ int copy_file(const std::string &from, const std::string &to, const bool with_ch ret_val = check_copy(from, to_temp); if (ret_val == 0 && rename_file(to_temp, to)) - ret_val = -1; + ret_val = -3; } return ret_val; } @@ -460,11 +460,11 @@ int check_copy(const std::string &origin, const std::string ©) std::ifstream f2(copy, std::ifstream::in | std::ifstream::binary | std::ifstream::ate); if (f1.fail() || f2.fail()) - return -1; + return -2; std::streampos fsize = f1.tellg(); if (fsize != f2.tellg()) - return -1; + return -2; f1.seekg(0, std::ifstream::beg); f2.seekg(0, std::ifstream::beg); @@ -481,12 +481,12 @@ int check_copy(const std::string &origin, const std::string ©) if (origin_cnt != copy_cnt || (origin_cnt > 0 && std::memcmp(buffer_origin.data(), buffer_copy.data(), origin_cnt) != 0)) // Files are different. - return -1; + return -2; fsize -= origin_cnt; } while (f1.good() && f2.good()); // All data has been read and compared equal. - return (f1.eof() && f2.eof() && fsize == 0) ? 0 : -1; + return (f1.eof() && f2.eof() && fsize == 0) ? 0 : -2; } // Ignore system and hidden files, which may be created by the DropBox synchronisation process. diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp index 8af721f9d..548a19f77 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -100,8 +100,23 @@ void BackgroundSlicingProcess::process_fff() //FIXME localize the messages // Perform the final post-processing of the export path by applying the print statistics over the file name. std::string export_path = m_fff_print->print_statistics().finalize_output_path(m_export_path); - if (copy_file(m_temp_output_path, export_path, GUI::RemovableDriveManager::get_instance().is_path_on_removable_drive(export_path)) != 0) + GUI::RemovableDriveManager::get_instance().update(); + bool with_check = GUI::RemovableDriveManager::get_instance().is_path_on_removable_drive(export_path); + int copy_ret_val = copy_file(m_temp_output_path, export_path, with_check); + if (with_check && copy_ret_val == -2) + { + std::string err_msg = "Copying of the temporary G-code to the output G-code failed. There might be problem with target device, please try exporting again or using different device. The corrupted output G-code is at " + export_path + ".tmp."; + throw std::runtime_error(_utf8(L(err_msg))); + } + else if (copy_ret_val == -3) + { + std::string err_msg = "Renaming of the G-code after copying to the selected destination folder has failed. Current path is " + export_path + ".tmp. Please try exporting again."; + throw std::runtime_error(_utf8(L(err_msg))); + } + else if ( copy_ret_val != 0) + { throw std::runtime_error(_utf8(L("Copying of the temporary G-code to the output G-code failed. Maybe the SD card is write locked?"))); + } m_print->set_status(95, _utf8(L("Running post-processing scripts"))); run_post_process_scripts(export_path, m_fff_print->config()); m_print->set_status(100, (boost::format(_utf8(L("G-code file exported to %1%"))) % export_path).str());