diff --git a/resources/localization/list.txt b/resources/localization/list.txt index 93ee2f441..fc33097df 100644 --- a/resources/localization/list.txt +++ b/resources/localization/list.txt @@ -46,6 +46,7 @@ src/slic3r/GUI/Tab.hpp src/slic3r/GUI/UpdateDialogs.cpp src/slic3r/GUI/WipeTowerDialog.cpp src/slic3r/GUI/wxExtensions.cpp +src/slic3r/GUI/DoubleSlider.cpp src/slic3r/GUI/ExtruderSequenceDialog.cpp src/slic3r/Utils/Duet.cpp src/slic3r/Utils/OctoPrint.cpp diff --git a/src/libslic3r/CustomGCode.hpp b/src/libslic3r/CustomGCode.hpp index 4f1be3f31..5ab4c76ef 100644 --- a/src/libslic3r/CustomGCode.hpp +++ b/src/libslic3r/CustomGCode.hpp @@ -8,6 +8,11 @@ namespace Slic3r { class DynamicPrintConfig; +// Additional Codes which can be set by user using DoubleSlider +static constexpr char ColorChangeCode[] = "M600"; +static constexpr char PausePrintCode[] = "M601"; +static constexpr char ToolChangeCode[] = "tool_change"; + namespace CustomGCode { struct Item diff --git a/src/libslic3r/GCodeWriter.cpp b/src/libslic3r/GCodeWriter.cpp index 4c53048dc..38a1c3ebe 100644 --- a/src/libslic3r/GCodeWriter.cpp +++ b/src/libslic3r/GCodeWriter.cpp @@ -1,4 +1,5 @@ #include "GCodeWriter.hpp" +#include "CustomGCode.hpp" #include #include #include diff --git a/src/libslic3r/GCodeWriter.hpp b/src/libslic3r/GCodeWriter.hpp index abeaf0024..3a57c8bd2 100644 --- a/src/libslic3r/GCodeWriter.hpp +++ b/src/libslic3r/GCodeWriter.hpp @@ -10,11 +10,6 @@ namespace Slic3r { -// Additional Codes which can be set by user using DoubleSlider -static constexpr char ColorChangeCode[] = "M600"; -static constexpr char PausePrintCode[] = "M601"; -static constexpr char ToolChangeCode[] = "tool_change"; - class GCodeWriter { public: GCodeConfig config; diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 8f38cb21b..9e4fb84eb 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -142,6 +142,8 @@ set(SLIC3R_GUI_SOURCES GUI/PrintHostDialogs.hpp GUI/Mouse3DController.cpp GUI/Mouse3DController.hpp + GUI/DoubleSlider.cpp + GUI/DoubleSlider.hpp Utils/Http.cpp Utils/Http.hpp Utils/FixModelByWin10.cpp diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp new file mode 100644 index 000000000..a47d6706c --- /dev/null +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -0,0 +1,1766 @@ +#include "wxExtensions.hpp" +#include "GUI.hpp" +#include "GUI_App.hpp" +#include "I18N.hpp" +#include "ExtruderSequenceDialog.hpp" +#include "libslic3r/Print.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace Slic3r { + +using GUI::from_u8; +using GUI::into_u8; + +namespace DoubleSlider { + +wxDEFINE_EVENT(wxCUSTOMEVT_TICKSCHANGED, wxEvent); + +Control::Control( wxWindow *parent, + wxWindowID id, + int lowerValue, + int higherValue, + int minValue, + int maxValue, + const wxPoint& pos, + const wxSize& size, + long style, + const wxValidator& val, + const wxString& name) : + wxControl(parent, id, pos, size, wxWANTS_CHARS | wxBORDER_NONE), + m_lower_value(lowerValue), + m_higher_value (higherValue), + m_min_value(minValue), + m_max_value(maxValue), + m_style(style == wxSL_HORIZONTAL || style == wxSL_VERTICAL ? style: wxSL_HORIZONTAL) +{ +#ifdef __WXOSX__ + is_osx = true; +#endif //__WXOSX__ + if (!is_osx) + SetDoubleBuffered(true);// SetDoubleBuffered exists on Win and Linux/GTK, but is missing on OSX + + const float scale_factor = get_svg_scale_factor(this); + + m_bmp_thumb_higher = (style == wxSL_HORIZONTAL ? ScalableBitmap(this, "right_half_circle.png") : ScalableBitmap(this, "thumb_up")); + m_bmp_thumb_lower = (style == wxSL_HORIZONTAL ? ScalableBitmap(this, "left_half_circle.png" ) : ScalableBitmap(this, "thumb_down")); + m_thumb_size = m_bmp_thumb_lower.bmp().GetSize()*(1.0/scale_factor); + + m_bmp_add_tick_on = ScalableBitmap(this, "colorchange_add"); + m_bmp_add_tick_off = ScalableBitmap(this, "colorchange_add_f"); + m_bmp_del_tick_on = ScalableBitmap(this, "colorchange_del"); + m_bmp_del_tick_off = ScalableBitmap(this, "colorchange_del_f"); + m_tick_icon_dim = int((float)m_bmp_add_tick_on.bmp().GetSize().x / scale_factor); + + m_bmp_one_layer_lock_on = ScalableBitmap(this, "lock_closed"); + m_bmp_one_layer_lock_off = ScalableBitmap(this, "lock_closed_f"); + m_bmp_one_layer_unlock_on = ScalableBitmap(this, "lock_open"); + m_bmp_one_layer_unlock_off = ScalableBitmap(this, "lock_open_f"); + m_lock_icon_dim = int((float)m_bmp_one_layer_lock_on.bmp().GetSize().x / scale_factor); + + m_bmp_revert = ScalableBitmap(this, "undo"); + m_revert_icon_dim = int((float)m_bmp_revert.bmp().GetSize().x / scale_factor); + m_bmp_cog = ScalableBitmap(this, "cog"); + m_cog_icon_dim = int((float)m_bmp_cog.bmp().GetSize().x / scale_factor); + + m_selection = ssUndef; + m_ticks.set_pause_print_msg(_utf8(L("Place bearings in slots and resume"))); + + // slider events + this->Bind(wxEVT_PAINT, &Control::OnPaint, this); + this->Bind(wxEVT_CHAR, &Control::OnChar, this); + this->Bind(wxEVT_LEFT_DOWN, &Control::OnLeftDown, this); + this->Bind(wxEVT_MOTION, &Control::OnMotion, this); + this->Bind(wxEVT_LEFT_UP, &Control::OnLeftUp, this); + this->Bind(wxEVT_MOUSEWHEEL, &Control::OnWheel, this); + this->Bind(wxEVT_ENTER_WINDOW,&Control::OnEnterWin, this); + this->Bind(wxEVT_LEAVE_WINDOW,&Control::OnLeaveWin, this); + this->Bind(wxEVT_KEY_DOWN, &Control::OnKeyDown, this); + this->Bind(wxEVT_KEY_UP, &Control::OnKeyUp, this); + this->Bind(wxEVT_RIGHT_DOWN, &Control::OnRightDown,this); + this->Bind(wxEVT_RIGHT_UP, &Control::OnRightUp, this); + + // control's view variables + SLIDER_MARGIN = 4 + GUI::wxGetApp().em_unit(); + + DARK_ORANGE_PEN = wxPen(wxColour(237, 107, 33)); + ORANGE_PEN = wxPen(wxColour(253, 126, 66)); + LIGHT_ORANGE_PEN = wxPen(wxColour(254, 177, 139)); + + DARK_GREY_PEN = wxPen(wxColour(128, 128, 128)); + GREY_PEN = wxPen(wxColour(164, 164, 164)); + LIGHT_GREY_PEN = wxPen(wxColour(204, 204, 204)); + + m_line_pens = { &DARK_GREY_PEN, &GREY_PEN, &LIGHT_GREY_PEN }; + m_segm_pens = { &DARK_ORANGE_PEN, &ORANGE_PEN, &LIGHT_ORANGE_PEN }; + + const wxFont& font = GetFont(); + m_font = is_osx ? font.Smaller().Smaller() : font.Smaller(); +} + +void Control::msw_rescale() +{ + const wxFont& font = GUI::wxGetApp().normal_font(); + m_font = is_osx ? font.Smaller().Smaller() : font.Smaller(); + + m_bmp_thumb_higher.msw_rescale(); + m_bmp_thumb_lower .msw_rescale(); + m_thumb_size = m_bmp_thumb_lower.bmp().GetSize(); + + m_bmp_add_tick_on .msw_rescale(); + m_bmp_add_tick_off.msw_rescale(); + m_bmp_del_tick_on .msw_rescale(); + m_bmp_del_tick_off.msw_rescale(); + m_tick_icon_dim = m_bmp_add_tick_on.bmp().GetSize().x; + + m_bmp_one_layer_lock_on .msw_rescale(); + m_bmp_one_layer_lock_off .msw_rescale(); + m_bmp_one_layer_unlock_on .msw_rescale(); + m_bmp_one_layer_unlock_off.msw_rescale(); + m_lock_icon_dim = m_bmp_one_layer_lock_on.bmp().GetSize().x; + + m_bmp_revert.msw_rescale(); + m_revert_icon_dim = m_bmp_revert.bmp().GetSize().x; + m_bmp_cog.msw_rescale(); + m_cog_icon_dim = m_bmp_cog.bmp().GetSize().x; + + SLIDER_MARGIN = 4 + GUI::wxGetApp().em_unit(); + + SetMinSize(get_min_size()); + GetParent()->Layout(); +} + +int Control::GetActiveValue() const +{ + return m_selection == ssLower ? + m_lower_value : m_selection == ssHigher ? + m_higher_value : -1; +} + +wxSize Control::get_min_size() const +{ + const int min_side = GUI::wxGetApp().em_unit() * ( is_horizontal() ? (is_osx ? 8 : 6) : 10 ); + + return wxSize(min_side, min_side); +} + +wxSize Control::DoGetBestSize() const +{ + const wxSize size = wxControl::DoGetBestSize(); + if (size.x > 1 && size.y > 1) + return size; + return get_min_size(); +} + +void Control::SetLowerValue(const int lower_val) +{ + m_selection = ssLower; + m_lower_value = lower_val; + correct_lower_value(); + Refresh(); + Update(); + + wxCommandEvent e(wxEVT_SCROLL_CHANGED); + e.SetEventObject(this); + ProcessWindowEvent(e); +} + +void Control::SetHigherValue(const int higher_val) +{ + m_selection = ssHigher; + m_higher_value = higher_val; + correct_higher_value(); + Refresh(); + Update(); + + wxCommandEvent e(wxEVT_SCROLL_CHANGED); + e.SetEventObject(this); + ProcessWindowEvent(e); +} + +void Control::SetSelectionSpan(const int lower_val, const int higher_val) +{ + m_lower_value = std::max(lower_val, m_min_value); + m_higher_value = std::max(std::min(higher_val, m_max_value), m_lower_value); + if (m_lower_value < m_higher_value) + m_is_one_layer = false; + + Refresh(); + Update(); + + wxCommandEvent e(wxEVT_SCROLL_CHANGED); + e.SetEventObject(this); + ProcessWindowEvent(e); +} + +void Control::SetMaxValue(const int max_value) +{ + m_max_value = max_value; + Refresh(); + Update(); +} + +void Control::draw_scroll_line(wxDC& dc, const int lower_pos, const int higher_pos) +{ + int width; + int height; + get_size(&width, &height); + + wxCoord line_beg_x = is_horizontal() ? SLIDER_MARGIN : width*0.5 - 1; + wxCoord line_beg_y = is_horizontal() ? height*0.5 - 1 : SLIDER_MARGIN; + wxCoord line_end_x = is_horizontal() ? width - SLIDER_MARGIN + 1 : width*0.5 - 1; + wxCoord line_end_y = is_horizontal() ? height*0.5 - 1 : height - SLIDER_MARGIN + 1; + + wxCoord segm_beg_x = is_horizontal() ? lower_pos : width*0.5 - 1; + wxCoord segm_beg_y = is_horizontal() ? height*0.5 - 1 : lower_pos/*-1*/; + wxCoord segm_end_x = is_horizontal() ? higher_pos : width*0.5 - 1; + wxCoord segm_end_y = is_horizontal() ? height*0.5 - 1 : higher_pos-1; + + for (size_t id = 0; id < m_line_pens.size(); id++) + { + dc.SetPen(*m_line_pens[id]); + dc.DrawLine(line_beg_x, line_beg_y, line_end_x, line_end_y); + dc.SetPen(*m_segm_pens[id]); + dc.DrawLine(segm_beg_x, segm_beg_y, segm_end_x, segm_end_y); + if (is_horizontal()) + line_beg_y = line_end_y = segm_beg_y = segm_end_y += 1; + else + line_beg_x = line_end_x = segm_beg_x = segm_end_x += 1; + } +} + +double Control::get_scroll_step() +{ + const wxSize sz = get_size(); + const int& slider_len = m_style == wxSL_HORIZONTAL ? sz.x : sz.y; + return double(slider_len - SLIDER_MARGIN * 2) / (m_max_value - m_min_value); +} + +// get position on the slider line from entered value +wxCoord Control::get_position_from_value(const int value) +{ + const double step = get_scroll_step(); + const int val = is_horizontal() ? value : m_max_value - value; + return wxCoord(SLIDER_MARGIN + int(val*step + 0.5)); +} + +wxSize Control::get_size() +{ + int w, h; + get_size(&w, &h); + return wxSize(w, h); +} + +void Control::get_size(int *w, int *h) +{ + GetSize(w, h); + is_horizontal() ? *w -= m_lock_icon_dim : *h -= m_lock_icon_dim; +} + +double Control::get_double_value(const SelectedSlider& selection) +{ + if (m_values.empty() || m_lower_value<0) + return 0.0; + if (m_values.size() <= m_higher_value) { + correct_higher_value(); + return m_values.back(); + } + return m_values[selection == ssLower ? m_lower_value : m_higher_value]; +} + +using t_custom_code = CustomGCode::Item; +CustomGCode::Info Control::GetTicksValues() const +{ + CustomGCode::Info custom_gcode_per_print_z; + std::vector& values = custom_gcode_per_print_z.gcodes; + + const int val_size = m_values.size(); + if (!m_values.empty()) + for (const TickCode& 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_force_mode_apply ? m_mode : m_ticks.mode; + + return custom_gcode_per_print_z; +} + +void Control::SetTicksValues(const CustomGCode::Info& custom_gcode_per_print_z) +{ + if (m_values.empty()) + { + m_ticks.mode = m_mode; + return; + } + + const bool was_empty = m_ticks.empty(); + + 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()); + + if (it == m_values.end()) + continue; + + m_ticks.ticks.emplace(TickCode{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; + + Refresh(); + Update(); +} + +void Control::get_lower_and_higher_position(int& lower_pos, int& higher_pos) +{ + const double step = get_scroll_step(); + if (is_horizontal()) { + lower_pos = SLIDER_MARGIN + int(m_lower_value*step + 0.5); + higher_pos = SLIDER_MARGIN + int(m_higher_value*step + 0.5); + } + else { + lower_pos = SLIDER_MARGIN + int((m_max_value - m_lower_value)*step + 0.5); + higher_pos = SLIDER_MARGIN + int((m_max_value - m_higher_value)*step + 0.5); + } +} + +void Control::draw_focus_rect() +{ + if (!m_is_focused) + return; + const wxSize sz = GetSize(); + wxPaintDC dc(this); + const wxPen pen = wxPen(wxColour(128, 128, 10), 1, wxPENSTYLE_DOT); + dc.SetPen(pen); + dc.SetBrush(wxBrush(wxColour(0, 0, 0), wxBRUSHSTYLE_TRANSPARENT)); + dc.DrawRectangle(1, 1, sz.x - 2, sz.y - 2); +} + +void Control::render() +{ + SetBackgroundColour(GetParent()->GetBackgroundColour()); + draw_focus_rect(); + + wxPaintDC dc(this); + dc.SetFont(m_font); + + const wxCoord lower_pos = get_position_from_value(m_lower_value); + const wxCoord higher_pos = get_position_from_value(m_higher_value); + + // draw colored band on the background of a scroll line + // and only in a case of no-empty m_values + draw_colored_band(dc); + + // draw line + draw_scroll_line(dc, lower_pos, higher_pos); + + //draw color print ticks + draw_ticks(dc); + + // draw both sliders + draw_thumbs(dc, lower_pos, higher_pos); + + //draw lock/unlock + draw_one_layer_icon(dc); + + //draw revert bitmap (if it's shown) + draw_revert_icon(dc); + + //draw cog bitmap (if it's shown) + draw_cog_icon(dc); +} + +void Control::draw_action_icon(wxDC& dc, const wxPoint pt_beg, const wxPoint pt_end) +{ + const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; + + // suppress add tick on first layer + if (tick == 0) + return; + + wxBitmap* icon = m_is_action_icon_focesed ? &m_bmp_add_tick_off.bmp() : &m_bmp_add_tick_on.bmp(); + if (m_ticks.ticks.find(TickCode{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; + is_horizontal() ? x_draw = pt_beg.x - 0.5*m_tick_icon_dim : y_draw = pt_beg.y - 0.5*m_tick_icon_dim; + if (m_selection == ssLower) + is_horizontal() ? y_draw = pt_end.y + 3 : x_draw = pt_beg.x - m_tick_icon_dim-2; + else + is_horizontal() ? y_draw = pt_beg.y - m_tick_icon_dim-2 : x_draw = pt_end.x + 3; + + dc.DrawBitmap(*icon, x_draw, y_draw); + + //update rect of the tick action icon + m_rect_tick_action = wxRect(x_draw, y_draw, m_tick_icon_dim, m_tick_icon_dim); +} + +void Control::draw_info_line_with_icon(wxDC& dc, const wxPoint& pos, const SelectedSlider selection) +{ + if (m_selection == selection) { + //draw info line + dc.SetPen(DARK_ORANGE_PEN); + const wxPoint pt_beg = is_horizontal() ? wxPoint(pos.x, pos.y - m_thumb_size.y) : wxPoint(pos.x - m_thumb_size.x, pos.y/* - 1*/); + const wxPoint pt_end = is_horizontal() ? wxPoint(pos.x, pos.y + m_thumb_size.y) : wxPoint(pos.x + m_thumb_size.x, pos.y/* - 1*/); + dc.DrawLine(pt_beg, pt_end); + + //draw action icon + if (m_is_enabled_tick_manipulation) + draw_action_icon(dc, pt_beg, pt_end); + } +} + +wxString Control::get_label(const SelectedSlider& selection) const +{ + const int value = selection == ssLower ? m_lower_value : m_higher_value; + + if (m_label_koef == 1.0 && m_values.empty()) + return wxString::Format("%d", value); + if (value >= m_values.size()) + return "ErrVal"; + + const wxString str = m_values.empty() ? + wxNumberFormatter::ToString(m_label_koef*value, 2, wxNumberFormatter::Style_None) : + wxNumberFormatter::ToString(m_values[value], 2, wxNumberFormatter::Style_None); + return wxString::Format("%s\n(%d)", str, m_values.empty() ? value : value+1); +} + +void Control::draw_thumb_text(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection) const +{ + if ( selection == ssUndef || + ((m_is_one_layer || m_higher_value==m_lower_value) && selection != m_selection) ) + return; + wxCoord text_width, text_height; + const wxString label = get_label(selection); + dc.GetMultiLineTextExtent(label, &text_width, &text_height); + wxPoint text_pos; + if (selection ==ssLower) + text_pos = is_horizontal() ? wxPoint(pos.x + 1, pos.y + m_thumb_size.x) : + wxPoint(pos.x + m_thumb_size.x+1, pos.y - 0.5*text_height - 1); + else + text_pos = is_horizontal() ? wxPoint(pos.x - text_width - 1, pos.y - m_thumb_size.x - text_height) : + wxPoint(pos.x - text_width - 1 - m_thumb_size.x, pos.y - 0.5*text_height + 1); + dc.DrawText(label, text_pos); +} + +void Control::draw_thumb_item(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection) +{ + wxCoord x_draw, y_draw; + if (selection == ssLower) { + if (is_horizontal()) { + x_draw = pos.x - m_thumb_size.x; + y_draw = pos.y - int(0.5*m_thumb_size.y); + } + else { + x_draw = pos.x - int(0.5*m_thumb_size.x); + y_draw = pos.y - int(0.5*m_thumb_size.y); + } + } + else{ + if (is_horizontal()) { + x_draw = pos.x; + y_draw = pos.y - int(0.5*m_thumb_size.y); + } + else { + x_draw = pos.x - int(0.5*m_thumb_size.x); + y_draw = pos.y - int(0.5*m_thumb_size.y); + } + } + dc.DrawBitmap(selection == ssLower ? m_bmp_thumb_lower.bmp() : m_bmp_thumb_higher.bmp(), x_draw, y_draw); + + // Update thumb rect + update_thumb_rect(x_draw, y_draw, selection); +} + +void Control::draw_thumb(wxDC& dc, const wxCoord& pos_coord, const SelectedSlider& selection) +{ + //calculate thumb position on slider line + int width, height; + get_size(&width, &height); + const wxPoint pos = is_horizontal() ? wxPoint(pos_coord, height*0.5) : wxPoint(0.5*width, pos_coord); + + // Draw thumb + draw_thumb_item(dc, pos, selection); + + // Draw info_line + draw_info_line_with_icon(dc, pos, selection); + + // Draw thumb text + draw_thumb_text(dc, pos, selection); +} + +void Control::draw_thumbs(wxDC& dc, const wxCoord& lower_pos, const wxCoord& higher_pos) +{ + //calculate thumb position on slider line + int width, height; + get_size(&width, &height); + const wxPoint pos_l = is_horizontal() ? wxPoint(lower_pos, height*0.5) : wxPoint(0.5*width, lower_pos); + const wxPoint pos_h = is_horizontal() ? wxPoint(higher_pos, height*0.5) : wxPoint(0.5*width, higher_pos); + + // Draw lower thumb + draw_thumb_item(dc, pos_l, ssLower); + // Draw lower info_line + draw_info_line_with_icon(dc, pos_l, ssLower); + + // Draw higher thumb + draw_thumb_item(dc, pos_h, ssHigher); + // Draw higher info_line + draw_info_line_with_icon(dc, pos_h, ssHigher); + // Draw higher thumb text + draw_thumb_text(dc, pos_h, ssHigher); + + // Draw lower thumb text + draw_thumb_text(dc, pos_l, ssLower); +} + +void Control::draw_ticks(wxDC& dc) +{ + if (!m_is_enabled_tick_manipulation) + return; + + dc.SetPen(m_is_enabled_tick_manipulation ? DARK_GREY_PEN : LIGHT_GREY_PEN ); + int height, width; + get_size(&width, &height); + const wxCoord mid = is_horizontal() ? 0.5*height : 0.5*width; + for (auto tick : m_ticks.ticks) + { + const wxCoord pos = get_position_from_value(tick.tick); + + is_horizontal() ? dc.DrawLine(pos, mid-14, pos, mid-9) : + dc.DrawLine(mid - 14, pos/* - 1*/, mid - 9, pos/* - 1*/); + 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 != ColorChangeCode && tick.gcode != ToolChangeCode) + icon = create_scaled_bitmap(this, tick.gcode == PausePrintCode ? "pause_print" : "edit_gcode"); + else + { + if ((tick.gcode == 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 == 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; + + dc.DrawBitmap(icon, x_draw, y_draw); + } + } +} + +std::string Control::get_color_for_tool_change_tick(std::set::const_iterator it) const +{ + const int current_extruder = it->extruder == 0 ? std::max(m_only_extruder, 1) : it->extruder; + + auto it_n = it; + while (it_n != m_ticks.ticks.begin()) { + --it_n; + if (it_n->gcode == ColorChangeCode && it_n->extruder == current_extruder) + return it_n->color; + } + + return it->color; +} + +std::string Control::get_color_for_color_change_tick(std::set::const_iterator it) const +{ + const int def_extruder = std::max(1, m_only_extruder); + auto it_n = it; + bool is_tool_change = false; + while (it_n != m_ticks.ticks.begin()) { + --it_n; + if (it_n->gcode == ToolChangeCode) { + is_tool_change = true; + if (it_n->extruder == it->extruder) + return it->color; + break; + } + } + if (!is_tool_change && it->extruder == def_extruder) + return it->color; + + return ""; +} + +void Control::draw_colored_band(wxDC& dc) +{ + if (!m_is_enabled_tick_manipulation) + return; + + int height, width; + get_size(&width, &height); + + const wxCoord mid = is_horizontal() ? 0.5 * height : 0.5 * width; + + wxRect main_band = is_horizontal() ? + wxRect(SLIDER_MARGIN, lround(mid - 0.375 * m_thumb_size.y), + width - 2 * SLIDER_MARGIN + 1, lround(0.75 * m_thumb_size.y)) : + wxRect(lround(mid - 0.375 * m_thumb_size.x), SLIDER_MARGIN, + lround(0.75 * m_thumb_size.x), height - 2 * SLIDER_MARGIN + 1); + + auto draw_band = [](wxDC& dc, const wxColour& clr, const wxRect& band_rc) + { + dc.SetPen(clr); + dc.SetBrush(clr); + dc.DrawRectangle(band_rc); + }; + + // don't color a band for MultiExtruder mode + if (m_ticks.empty() || m_mode == t_mode::MultiExtruder) + { + draw_band(dc, GetParent()->GetBackgroundColour(), main_band); + return; + } + + const int default_color_idx = m_mode==t_mode::MultiAsSingle ? std::max(m_only_extruder - 1, 0) : 0; + draw_band(dc, wxColour(GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config()[default_color_idx]), main_band); + + std::set::const_iterator tick_it = m_ticks.ticks.begin(); + + while (tick_it != m_ticks.ticks.end()) + { + if ( (m_mode == t_mode::SingleExtruder && tick_it->gcode == ColorChangeCode ) || + (m_mode == t_mode::MultiAsSingle && (tick_it->gcode == ToolChangeCode || tick_it->gcode == ColorChangeCode)) ) + { + const wxCoord pos = get_position_from_value(tick_it->tick); + is_horizontal() ? main_band.SetLeft(SLIDER_MARGIN + pos) : + main_band.SetBottom(pos - 1); + + const std::string clr_str = m_mode == t_mode::SingleExtruder ? tick_it->color : + tick_it->gcode == ToolChangeCode ? + get_color_for_tool_change_tick(tick_it) : + get_color_for_color_change_tick(tick_it); + + if (!clr_str.empty()) + draw_band(dc, wxColour(clr_str), main_band); + } + ++tick_it; + } +} + +void Control::draw_one_layer_icon(wxDC& dc) +{ + const wxBitmap& icon = m_is_one_layer ? + m_is_one_layer_icon_focesed ? m_bmp_one_layer_lock_off.bmp() : m_bmp_one_layer_lock_on.bmp() : + m_is_one_layer_icon_focesed ? m_bmp_one_layer_unlock_off.bmp() : m_bmp_one_layer_unlock_on.bmp(); + + int width, height; + get_size(&width, &height); + + wxCoord x_draw, y_draw; + is_horizontal() ? x_draw = width-2 : x_draw = 0.5*width - 0.5*m_lock_icon_dim; + is_horizontal() ? y_draw = 0.5*height - 0.5*m_lock_icon_dim : y_draw = height-2; + + dc.DrawBitmap(icon, x_draw, y_draw); + + //update rect of the lock/unlock icon + m_rect_one_layer_icon = wxRect(x_draw, y_draw, m_lock_icon_dim, m_lock_icon_dim); +} + +void Control::draw_revert_icon(wxDC& dc) +{ + if (m_ticks.empty() || !m_is_enabled_tick_manipulation) + return; + + int width, height; + get_size(&width, &height); + + wxCoord x_draw, y_draw; + is_horizontal() ? x_draw = width-2 : x_draw = 0.25*SLIDER_MARGIN; + is_horizontal() ? y_draw = 0.25*SLIDER_MARGIN: y_draw = height-2; + + dc.DrawBitmap(m_bmp_revert.bmp(), x_draw, y_draw); + + //update rect of the lock/unlock icon + m_rect_revert_icon = wxRect(x_draw, y_draw, m_revert_icon_dim, m_revert_icon_dim); +} + +void Control::draw_cog_icon(wxDC& dc) +{ + if (m_mode != t_mode::MultiExtruder) + return; + + int width, height; + get_size(&width, &height); + + wxCoord x_draw, y_draw; + is_horizontal() ? x_draw = width-2 : x_draw = width - m_cog_icon_dim - 2; + is_horizontal() ? y_draw = height - m_cog_icon_dim - 2 : y_draw = height-2; + + dc.DrawBitmap(m_bmp_cog.bmp(), x_draw, y_draw); + + //update rect of the lock/unlock icon + m_rect_cog_icon = wxRect(x_draw, y_draw, m_cog_icon_dim, m_cog_icon_dim); +} + +void Control::update_thumb_rect(const wxCoord& begin_x, const wxCoord& begin_y, const SelectedSlider& selection) +{ + const wxRect& rect = wxRect(begin_x, begin_y, m_thumb_size.x, int(m_thumb_size.y*0.5)); + if (selection == ssLower) + m_rect_lower_thumb = rect; + else + m_rect_higher_thumb = rect; +} + +int Control::get_value_from_position(const wxCoord x, const wxCoord y) +{ + const int height = get_size().y; + const double step = get_scroll_step(); + + if (is_horizontal()) + return int(double(x - SLIDER_MARGIN) / step + 0.5); + + return int(m_min_value + double(height - SLIDER_MARGIN - y) / step + 0.5); +} + +void Control::detect_selected_slider(const wxPoint& pt) +{ + m_selection = is_point_in_rect(pt, m_rect_lower_thumb) ? ssLower : + is_point_in_rect(pt, m_rect_higher_thumb) ? ssHigher : ssUndef; +} + +bool Control::is_point_in_rect(const wxPoint& pt, const wxRect& rect) +{ + return rect.GetLeft() <= pt.x && pt.x <= rect.GetRight() && + rect.GetTop() <= pt.y && pt.y <= rect.GetBottom(); +} + +int Control::is_point_near_tick(const wxPoint& pt) +{ + for (auto tick : m_ticks.ticks) { + const wxCoord pos = get_position_from_value(tick.tick); + + if (is_horizontal()) { + if (pos - 4 <= pt.x && pt.x <= pos + 4) + return tick.tick; + } + else { + if (pos - 4 <= pt.y && pt.y <= pos + 4) + return tick.tick; + } + } + return -1; +} + +void Control::ChangeOneLayerLock() +{ + m_is_one_layer = !m_is_one_layer; + m_selection == ssLower ? correct_lower_value() : correct_higher_value(); + if (!m_selection) m_selection = ssHigher; + + Refresh(); + Update(); + + wxCommandEvent e(wxEVT_SCROLL_CHANGED); + e.SetEventObject(this); + ProcessWindowEvent(e); +} + +void Control::OnLeftDown(wxMouseEvent& event) +{ + if (HasCapture()) + return; + this->CaptureMouse(); + + wxClientDC dc(this); + wxPoint pos = event.GetLogicalPosition(dc); + if (is_point_in_rect(pos, m_rect_tick_action) && m_is_enabled_tick_manipulation) + { + const auto it = m_ticks.ticks.find(TickCode{ 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; + } + + m_is_left_down = true; + if (is_point_in_rect(pos, m_rect_one_layer_icon)) { + // switch on/off one layer mode + m_is_one_layer = !m_is_one_layer; + if (!m_is_one_layer) { + SetLowerValue(m_min_value); + SetHigherValue(m_max_value); + } + m_selection == ssLower ? correct_lower_value() : correct_higher_value(); + if (!m_selection) m_selection = ssHigher; + } + else if (is_point_in_rect(pos, m_rect_revert_icon) && m_is_enabled_tick_manipulation) { + // discard all color changes + SetLowerValue(m_min_value); + SetHigherValue(m_max_value); + + m_selection == ssLower ? correct_lower_value() : correct_higher_value(); + if (!m_selection) m_selection = ssHigher; + + 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_force_edit_extruder_sequence = true; + return; + } + else + detect_selected_slider(pos); + + if (!m_selection) { + const int tick_val = is_point_near_tick(pos); + /* Set current thumb position to the nearest tick (if it is) + * OR to a value corresponding to the mouse click + * */ + const int mouse_val = tick_val >= 0 && m_is_enabled_tick_manipulation ? tick_val : + get_value_from_position(pos.x, pos.y); + if (mouse_val >= 0) + { + // if (abs(mouse_val - m_lower_value) < abs(mouse_val - m_higher_value)) { + if ( mouse_val <= m_lower_value ) { + SetLowerValue(mouse_val); + correct_lower_value(); + m_selection = ssLower; + } + else { + SetHigherValue(mouse_val); + correct_higher_value(); + m_selection = ssHigher; + } + } + } + + Refresh(); + Update(); + event.Skip(); +} + +void Control::correct_lower_value() +{ + if (m_lower_value < m_min_value) + m_lower_value = m_min_value; + else if (m_lower_value > m_max_value) + m_lower_value = m_max_value; + + if ((m_lower_value >= m_higher_value && m_lower_value <= m_max_value) || m_is_one_layer) + m_higher_value = m_lower_value; +} + +void Control::correct_higher_value() +{ + if (m_higher_value > m_max_value) + m_higher_value = m_max_value; + else if (m_higher_value < m_min_value) + m_higher_value = m_min_value; + + if ((m_higher_value <= m_lower_value && m_higher_value >= m_min_value) || m_is_one_layer) + m_lower_value = m_higher_value; +} + +wxString Control::get_tooltip(IconFocus icon_focus) +{ + wxString tooltip(wxEmptyString); + if (m_is_one_layer_icon_focesed) + tooltip = _(L("One layer mode")); + + if (icon_focus == ifRevert) + tooltip = _(L("Discard all custom changes")); + if (icon_focus == ifCog) + tooltip = _(L("Set extruder sequence for whole print")); + 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.ticks.find(TickCode{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")) : + tick_code_it->gcode == ColorChangeCode ? ( m_mode == t_mode::SingleExtruder ? + _(L("For Delete color change use left mouse button click\n" + "For Edit color use right mouse button click")) : + from_u8((boost::format(_utf8(L("Delete color change for Extruder %1%"))) % tick_code_it->extruder).str()) ): + tick_code_it->gcode == PausePrintCode ? + _(L("Delete pause")) : + tick_code_it->gcode == ToolChangeCode ? + from_u8((boost::format(_utf8(L("Delete extruder change to \"%1%\""))) % tick_code_it->extruder).str()) : + from_u8((boost::format(_utf8(L("For Delete \"%1%\" code use left mouse button click\n" + "For Edit \"%1%\" code use right mouse button click"))) % tick_code_it->gcode ).str()); + } + + return tooltip; +} + +void Control::OnMotion(wxMouseEvent& event) +{ + bool action = false; + + const wxClientDC dc(this); + const wxPoint pos = event.GetLogicalPosition(dc); + + m_is_one_layer_icon_focesed = is_point_in_rect(pos, m_rect_one_layer_icon); + IconFocus icon_focus = ifNone; + + if (!m_is_left_down && !m_is_one_layer) { + m_is_action_icon_focesed = is_point_in_rect(pos, m_rect_tick_action); + if (!m_ticks.empty() && is_point_in_rect(pos, m_rect_revert_icon)) + icon_focus = ifRevert; + else if (is_point_in_rect(pos, m_rect_cog_icon)) + icon_focus = ifCog; + } + else if (m_is_left_down || m_is_right_down) { + if (m_selection == ssLower) { + int current_value = m_lower_value; + m_lower_value = get_value_from_position(pos.x, pos.y); + correct_lower_value(); + action = (current_value != m_lower_value); + } + else if (m_selection == ssHigher) { + int current_value = m_higher_value; + m_higher_value = get_value_from_position(pos.x, pos.y); + correct_higher_value(); + action = (current_value != m_higher_value); + } + } + Refresh(); + Update(); + event.Skip(); + + // Set tooltips with information for each icon + this->SetToolTip(get_tooltip(icon_focus)); + + if (action) + { + wxCommandEvent e(wxEVT_SCROLL_CHANGED); + e.SetEventObject(this); + e.SetString("moving"); + ProcessWindowEvent(e); + } +} + +void Control::append_change_extruder_menu_item(wxMenu* menu, bool switch_current_code/* = false*/) +{ + const int extruders_cnt = GUI::wxGetApp().extruders_edited_cnt(); + if (extruders_cnt > 1) + { + std::array active_extruders = get_active_extruders_for_tick(m_selection == ssLower ? m_lower_value : m_higher_value); + + wxMenu* change_extruder_menu = new wxMenu(); + + for (int i = 1; i <= extruders_cnt; i++) + { + const bool is_active_extruder = i == active_extruders[0] || i == active_extruders[1]; + const wxString item_name = wxString::Format(_(L("Extruder %d")), i) + + (is_active_extruder ? " (" + _(L("active")) + ")" : ""); + + if (m_mode == t_mode::MultiAsSingle) + append_menu_item(change_extruder_menu, wxID_ANY, item_name, "", + [this, i](wxCommandEvent&) { add_code_as_tick(ToolChangeCode, i); }, "", menu, + [is_active_extruder]() { return !is_active_extruder; }, GUI::wxGetApp().plater()); + } + + const wxString change_extruder_menu_name = m_mode == t_mode::MultiAsSingle ? + (switch_current_code ? _(L("Switch code to Change extruder")) : _(L("Change extruder")) ) : + _(L("Change extruder (N/A)")); + + wxMenuItem* change_extruder_menu_item = menu->AppendSubMenu(change_extruder_menu, change_extruder_menu_name, _(L("Use another extruder"))); + change_extruder_menu_item->SetBitmap(create_scaled_bitmap(this, active_extruders[1] > 0 ? "edit_uni" : "change_extruder")); + + GUI::wxGetApp().plater()->Bind(wxEVT_UPDATE_UI, [this, change_extruder_menu_item](wxUpdateUIEvent& evt) { + enable_menu_item(evt, [this]() {return m_mode == t_mode::MultiAsSingle; }, change_extruder_menu_item, this); }, + change_extruder_menu_item->GetId()); + } +} + +void Control::append_add_color_change_menu_item(wxMenu* menu, bool switch_current_code/* = false*/) +{ + const int extruders_cnt = GUI::wxGetApp().extruders_edited_cnt(); + if (extruders_cnt > 1) + { + std::set used_extruders_for_tick = get_used_extruders_for_tick(m_selection == ssLower ? m_lower_value : m_higher_value); + + wxMenu* add_color_change_menu = new wxMenu(); + + for (int i = 1; i <= extruders_cnt; i++) + { + const bool is_used_extruder = used_extruders_for_tick.empty() ? true : // #ys_FIXME till used_extruders_for_tick doesn't filled correct for mmMultiExtruder + used_extruders_for_tick.find(i) != used_extruders_for_tick.end(); + const wxString item_name = wxString::Format(_(L("Extruder %d")), i) + + (is_used_extruder ? " (" + _(L("used")) + ")" : ""); + + append_menu_item(add_color_change_menu, wxID_ANY, item_name, "", + [this, i](wxCommandEvent&) { add_code_as_tick(ColorChangeCode, i); }, "", menu, + [is_used_extruder]() { return is_used_extruder; }, GUI::wxGetApp().plater()); + } + + const wxString menu_name = switch_current_code ? + from_u8((boost::format(_utf8(L("Switch code to Color change (%1%) for:"))) % ColorChangeCode).str()) : + from_u8((boost::format(_utf8(L("Add color change (%1%) for:"))) % 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(this, "colorchange_add_m")); + } +} + +void Control::OnLeftUp(wxMouseEvent& event) +{ + if (!HasCapture()) + return; + this->ReleaseMouse(); + m_is_left_down = false; + + if (m_force_delete_tick) + { + delete_current_tick(); + m_force_delete_tick = false; + } + 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_force_edit_extruder_sequence = false; + } + + Refresh(); + Update(); + event.Skip(); + + wxCommandEvent e(wxEVT_SCROLL_CHANGED); + e.SetEventObject(this); + ProcessWindowEvent(e); +} + +void Control::enter_window(wxMouseEvent& event, const bool enter) +{ + m_is_focused = enter; + Refresh(); + Update(); + event.Skip(); +} + +// "condition" have to be true for: +// - value increase (if wxSL_VERTICAL) +// - value decrease (if wxSL_HORIZONTAL) +void Control::move_current_thumb(const bool condition) +{ +// m_is_one_layer = wxGetKeyState(WXK_CONTROL); + int delta = condition ? -1 : 1; + if (is_horizontal()) + delta *= -1; + + if (m_selection == ssLower) { + m_lower_value -= delta; + correct_lower_value(); + } + else if (m_selection == ssHigher) { + m_higher_value -= delta; + correct_higher_value(); + } + Refresh(); + Update(); + + wxCommandEvent e(wxEVT_SCROLL_CHANGED); + e.SetEventObject(this); + ProcessWindowEvent(e); +} + +void Control::OnWheel(wxMouseEvent& event) +{ + // Set nearest to the mouse thumb as a selected, if there is not selected thumb + if (m_selection == ssUndef) + { + const wxPoint& pt = event.GetLogicalPosition(wxClientDC(this)); + + if (is_horizontal()) + m_selection = abs(pt.x - m_rect_lower_thumb.GetRight()) <= + abs(pt.x - m_rect_higher_thumb.GetLeft()) ? + ssLower : ssHigher; + else + m_selection = abs(pt.y - m_rect_lower_thumb.GetTop()) <= + abs(pt.y - m_rect_higher_thumb.GetBottom()) ? + ssLower : ssHigher; + } + + move_current_thumb(event.GetWheelRotation() > 0); +} + +void Control::OnKeyDown(wxKeyEvent &event) +{ + const int key = event.GetKeyCode(); + 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) + move_current_thumb(key == WXK_LEFT); + else if (key == WXK_UP || key == WXK_DOWN) { + m_selection = key == WXK_UP ? ssHigher : ssLower; + Refresh(); + } + } + else { + if (key == WXK_LEFT || key == WXK_RIGHT) { + m_selection = key == WXK_LEFT ? ssHigher : ssLower; + Refresh(); + } + else if (key == WXK_UP || key == WXK_DOWN) + move_current_thumb(key == WXK_UP); + } + + event.Skip(); // !Needed to have EVT_CHAR generated as well +} + +void Control::OnKeyUp(wxKeyEvent &event) +{ + if (event.GetKeyCode() == WXK_CONTROL) + m_is_one_layer = false; + Refresh(); + Update(); + event.Skip(); +} + +void Control::OnChar(wxKeyEvent& event) +{ + const int key = event.GetKeyCode(); + 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 Control::OnRightDown(wxMouseEvent& event) +{ + if (HasCapture()) return; + this->CaptureMouse(); + + const wxClientDC dc(this); + + wxPoint pos = event.GetLogicalPosition(dc); + if (is_point_in_rect(pos, m_rect_tick_action) && m_is_enabled_tick_manipulation) + { + const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; + if (m_ticks.ticks.find(TickCode{ tick }) == m_ticks.ticks.end()) // if on this Z doesn't exist tick + // show context menu on OnRightUp() + m_show_context_menu = true; + else + // show "Edit" and "Delete" menu on OnRightUp() + m_show_edit_menu = true; + return; + } + + detect_selected_slider(event.GetLogicalPosition(dc)); + if (!m_selection) + return; + + if (m_selection == ssLower) + m_higher_value = m_lower_value; + else + m_lower_value = m_higher_value; + + // set slider to "one layer" mode + m_is_right_down = m_is_one_layer = true; + + Refresh(); + Update(); + event.Skip(); +} + +// Get active extruders for tick. +// Means one current extruder for not existing tick OR +// 2 extruders - for existing tick (extruder before ToolChangeCode and extruder of current existing tick) +// Use those values to disable selection of active extruders +std::array Control::get_active_extruders_for_tick(int tick) const +{ + int default_initial_extruder = m_mode == t_mode::MultiAsSingle ? std::max(1, m_only_extruder) : 1; + std::array extruders = { default_initial_extruder, -1 }; + if (m_ticks.empty()) + return extruders; + + auto it = m_ticks.ticks.lower_bound(TickCode{tick}); + + if (it->tick == tick) // current tick exists + extruders[1] = it->extruder; + + while (it != m_ticks.ticks.begin()) { + --it; + if(it->gcode == ToolChangeCode) { + extruders[0] = it->extruder; + break; + } + } + + return extruders; +} + +// Get used extruders for tick. +// Means all extruders(toools) will be used during printing from current tick to the end +std::set Control::get_used_extruders_for_tick(int tick) const +{ + if (m_mode == t_mode::MultiExtruder) + { + // #ys_FIXME: get tool ordering from _correct_ place + const ToolOrdering& tool_ordering = GUI::wxGetApp().plater()->fff_print().get_tool_ordering(); + + if (tool_ordering.empty()) + return {}; + + std::set used_extruders; + + auto it_layer_tools = std::lower_bound(tool_ordering.begin(), tool_ordering.end(), LayerTools(m_values[tick])); + for (; it_layer_tools != tool_ordering.end(); ++it_layer_tools) + { + const std::vector& extruders = it_layer_tools->extruders; + for (const auto& extruder : extruders) + used_extruders.emplace(extruder+1); + } + + return used_extruders; + } + + const int default_initial_extruder = m_mode == t_mode::MultiAsSingle ? std::max(m_only_extruder, 1) : 1; + if (m_ticks.empty()) + return {default_initial_extruder}; + + std::set used_extruders; + const std::set& ticks = m_ticks.ticks; + + auto it_start = ticks.lower_bound(TickCode{tick}); + auto it = it_start; + if (it == ticks.begin() && it->gcode == ToolChangeCode && + tick != it->tick ) // In case of switch of ToolChange to ColorChange, when tick exists, + // we shouldn't change color for extruder, which will be deleted + { + used_extruders.emplace(it->extruder); + if (tick < it->tick) + used_extruders.emplace(default_initial_extruder); + } + + while (it != ticks.begin()) { + --it; + if (it->gcode == ToolChangeCode && tick != it->tick) { + used_extruders.emplace(it->extruder); + break; + } + } + + if (it == ticks.begin() && used_extruders.empty()) + used_extruders.emplace(default_initial_extruder); + + for (it = it_start; it != ticks.end(); ++it) + if (it->gcode == ToolChangeCode && tick != it->tick) + used_extruders.emplace(it->extruder); + + return used_extruders; +} + +void Control::OnRightUp(wxMouseEvent& event) +{ + if (!HasCapture()) + return; + this->ReleaseMouse(); + m_is_right_down = m_is_one_layer = false; + + if (m_show_context_menu) { + wxMenu menu; + + if (m_mode == t_mode::SingleExtruder) + append_menu_item(&menu, wxID_ANY, _(L("Add color change")) + " (M600)", "", + [this](wxCommandEvent&) { add_code_as_tick(ColorChangeCode); }, "colorchange_add_m", &menu, + [](){return true;}, this); + else + { + append_change_extruder_menu_item(&menu); + append_add_color_change_menu_item(&menu); + } + + append_menu_item(&menu, wxID_ANY, _(L("Add pause print")) + " (M601)", "", + [this](wxCommandEvent&) { add_code_as_tick(PausePrintCode); }, "pause_print", &menu, + []() {return true; }, this); + + append_menu_item(&menu, wxID_ANY, _(L("Add custom G-code")), "", + [this](wxCommandEvent&) { add_code_as_tick(""); }, "edit_gcode", &menu, + []() {return true; }, this); + + GUI::wxGetApp().plater()->PopupMenu(&menu); + + m_show_context_menu = false; + } + else if (m_show_edit_menu) { + wxMenu menu; + + std::set::iterator it = m_ticks.ticks.find(TickCode{ m_selection == ssLower ? m_lower_value : m_higher_value }); + + if (it->gcode == ToolChangeCode) { + if (m_mode == t_mode::MultiAsSingle) + append_change_extruder_menu_item(&menu); + append_add_color_change_menu_item(&menu, true); + } + else + append_menu_item(&menu, wxID_ANY, it->gcode == ColorChangeCode ? _(L("Edit color")) : + it->gcode == PausePrintCode ? _(L("Edit pause print message")) : + _(L("Edit custom G-code")), "", + [this](wxCommandEvent&) { edit_tick(); }, "edit_uni", &menu); + + if (it->gcode == ColorChangeCode && m_mode == t_mode::MultiAsSingle) + append_change_extruder_menu_item(&menu, true); + + append_menu_item(&menu, wxID_ANY, it->gcode == ColorChangeCode ? _(L("Delete color change")) : + it->gcode == ToolChangeCode ? _(L("Delete tool change")) : + it->gcode == PausePrintCode ? _(L("Delete pause print")) : + _(L("Delete custom G-code")), "", + [this](wxCommandEvent&) { delete_current_tick();}, "colorchange_del_f", &menu); + + GUI::wxGetApp().plater()->PopupMenu(&menu); + + m_show_edit_menu = false; + } + + Refresh(); + Update(); + event.Skip(); +} + +static std::string get_new_color(const std::string& color) +{ + wxColour clr(color); + if (!clr.IsOk()) + clr = wxColour(0, 0, 0); // Don't set alfa to transparence + + auto data = new wxColourData(); + data->SetChooseFull(1); + data->SetColour(clr); + + wxColourDialog dialog(nullptr, data); + dialog.CenterOnParent(); + if (dialog.ShowModal() == wxID_OK) + return dialog.GetColourData().GetColour().GetAsString(wxC2S_HTML_SYNTAX).ToStdString(); + return ""; +} + +static std::string get_custom_code(const std::string& code_in, double height) +{ + wxString msg_text = from_u8(_utf8(L("Enter custom G-code used on current layer"))) + ":"; + wxString msg_header = from_u8((boost::format(_utf8(L("Custom Gcode on current layer (%1% mm)."))) % height).str()); + + // get custom gcode + wxTextEntryDialog dlg(nullptr, msg_text, msg_header, code_in, + wxTextEntryDialogStyle | wxTE_MULTILINE); + if (dlg.ShowModal() != wxID_OK || dlg.GetValue().IsEmpty()) + return ""; + + return dlg.GetValue().ToStdString(); +} + +static std::string get_pause_print_msg(const std::string& msg_in, double height) +{ + wxString msg_text = from_u8(_utf8(L("Enter short message shown on Printer display during pause print"))) + ":"; + wxString msg_header = from_u8((boost::format(_utf8(L("Message for pause print on current layer (%1% mm)."))) % height).str()); + + // get custom gcode + wxTextEntryDialog dlg(nullptr, msg_text, msg_header, from_u8(msg_in), + wxTextEntryDialogStyle); + if (dlg.ShowModal() != wxID_OK || dlg.GetValue().IsEmpty()) + return ""; + + return into_u8(dlg.GetValue()); +} + +void Control::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 ( !check_ticks_changed_event(code) ) + return; + + const int extruder = selected_extruder > 0 ? selected_extruder : std::max(1, m_only_extruder); + const auto it = m_ticks.ticks.find(TickCode{ tick }); + + if ( it == m_ticks.ticks.end() ) { + // try to add tick + if (!m_ticks.add_tick(tick, code, extruder, m_values[tick])) + return; + } + else if (code == ToolChangeCode || code == ColorChangeCode) { + // try to switch tick code to ToolChangeCode or ColorChangeCode accordingly + if (!m_ticks.switch_code_for_tick(it, code, extruder)) + return; + } + else + return; + + post_ticks_changed_event(code); +} + +void Control::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(TickCode{ tick }); + + if (it != m_ticks.ticks.end() || // this tick is already exist + !check_ticks_changed_event(m_mode == t_mode::MultiAsSingle ? ToolChangeCode : ColorChangeCode)) + return; + + if (m_mode == t_mode::SingleExtruder) + add_code_as_tick(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; + /* Menu position will be calculated from mouse click position, but... + * if function is called from keyboard (pressing "+"), we should to calculate it + * */ + 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)); + } + + GUI::wxGetApp().plater()->PopupMenu(&menu, pos); + } +} + +void Control::delete_current_tick() +{ + if (m_selection == ssUndef) + return; + + auto it = m_ticks.ticks.find(TickCode{ m_selection == ssLower ? m_lower_value : m_higher_value }); + if (it == m_ticks.ticks.end() || + !check_ticks_changed_event(it->gcode)) + return; + + const std::string code = it->gcode; + m_ticks.ticks.erase(it); + post_ticks_changed_event(code); +} + +void Control::edit_tick() +{ + const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; + const std::set::iterator it = m_ticks.ticks.find(TickCode{ tick }); + + if (it == m_ticks.ticks.end() || + !check_ticks_changed_event(it->gcode)) + return; + + const std::string code = it->gcode; + if (m_ticks.edit_tick(it, m_values[it->tick])) + post_ticks_changed_event(code); +} + +void Control::edit_extruder_sequence() +{ + if (!check_ticks_changed_event(ToolChangeCode)) + return; + + GUI::ExtruderSequenceDialog dlg(m_extruders_sequence); + if (dlg.ShowModal() != wxID_OK) + return; + + const ExtrudersSequence& from_dlg_val = dlg.GetValue(); + if (m_extruders_sequence == from_dlg_val) + return; + + m_extruders_sequence = from_dlg_val; + + m_ticks.erase_all_ticks_with_code(ToolChangeCode); + + int tick = 0; + double value = 0.0; + int extruder = 0; + const int extr_cnt = m_extruders_sequence.extruders.size(); + + std::vector colors = GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config(); + + while (tick <= m_max_value) + { + const int cur_extruder = m_extruders_sequence.extruders[extruder]; + m_ticks.ticks.emplace(TickCode{tick, ToolChangeCode, cur_extruder + 1, colors[cur_extruder]}); + + extruder++; + if (extruder == extr_cnt) + extruder = 0; + if (m_extruders_sequence.is_mm_intervals) + { + value += m_extruders_sequence.interval_by_mm; + auto val_it = std::lower_bound(m_values.begin(), m_values.end(), value - epsilon()); + + if (val_it == m_values.end()) + break; + + tick = val_it - m_values.begin(); + } + else + tick += m_extruders_sequence.interval_by_layers; + } + + post_ticks_changed_event(ToolChangeCode); +} + +void Control::post_ticks_changed_event(const std::string& gcode /*= ""*/) +{ + m_force_mode_apply = (gcode.empty() || gcode == ColorChangeCode || gcode == ToolChangeCode); + + wxPostEvent(this->GetParent(), wxCommandEvent(wxCUSTOMEVT_TICKSCHANGED)); +} + +bool Control::check_ticks_changed_event(const std::string& gcode) +{ + if ( m_ticks.mode == m_mode || + (gcode != ColorChangeCode && gcode != 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(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(ColorChangeCode); + post_ticks_changed_event(ColorChangeCode); + } + return false; + } + // m_ticks_mode == t_mode::MultiAsSingle + if( m_ticks.has_tick_with_code(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(ToolChangeCode); + post_ticks_changed_event(ToolChangeCode); + } + else if (m_mode == t_mode::SingleExtruder && answer == wxID_NO) { + m_ticks.switch_code(ToolChangeCode, ColorChangeCode); + post_ticks_changed_event(ColorChangeCode); + } + return false; + } + + return true; +} + + +std::string TickCodeInfo::get_color_for_tick(TickCode tick, const std::string& code, const int extruder) +{ + std::vector colors = GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config(); + std::string color = colors[extruder - 1]; + + if (code == ColorChangeCode) + { + if (!ticks.empty()) + { + auto before_tick_it = std::lower_bound(ticks.begin(), ticks.end(), tick ); + while (before_tick_it != ticks.begin()) { + --before_tick_it; + if (before_tick_it->gcode == ColorChangeCode && before_tick_it->extruder == extruder) { + color = before_tick_it->color; + break; + } + } + } + + color = get_new_color(color); + } + return color; +} + +bool TickCodeInfo::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 == 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 + { + color = get_color_for_tick(TickCode{ tick }, code, extruder); + if (color.empty()) + return false; + } + + ticks.emplace(TickCode{ tick, code, extruder, color }); + return true; +} + +bool TickCodeInfo::edit_tick(std::set::iterator it, double print_z) +{ + std::string edited_value; + if (it->gcode == ColorChangeCode) + edited_value = get_new_color(it->color); + else if (it->gcode == 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; + + TickCode changed_tick = *it; + if (it->gcode == ColorChangeCode || it->gcode == 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 TickCodeInfo::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) + { + TickCode tick = *it; + tick.gcode = code_to; + tick.extruder = 1; + ticks.erase(it); + it = ticks.emplace(tick).first; + } + else + ++it; +} + +bool TickCodeInfo::switch_code_for_tick(std::set::iterator it, const std::string& code_to, const int extruder) +{ + const std::string color = get_color_for_tick(*it, code_to, extruder); + if (color.empty()) + return false; + + TickCode changed_tick = *it; + changed_tick.gcode = code_to; + changed_tick.extruder = extruder; + changed_tick.color = color; + + ticks.erase(it); + ticks.emplace(changed_tick); + + return true; +} + +void TickCodeInfo::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 TickCodeInfo::has_tick_with_code(const std::string& gcode) +{ + for (const TickCode& tick : ticks) + if (tick.gcode == gcode) + return true; + + return false; +} + +} // DoubleSlider + +} // Slic3r + + diff --git a/src/slic3r/GUI/DoubleSlider.hpp b/src/slic3r/GUI/DoubleSlider.hpp new file mode 100644 index 000000000..02a98ebb2 --- /dev/null +++ b/src/slic3r/GUI/DoubleSlider.hpp @@ -0,0 +1,337 @@ +#ifndef slic3r_GUI_DoubleSlider_hpp_ +#define slic3r_GUI_DoubleSlider_hpp_ + +#include "libslic3r/CustomGCode.hpp" +#include "wxExtensions.hpp" + +#include +#include +#include +#include +#include + +#include +#include + +class wxMenu; + +namespace Slic3r { + +namespace DoubleSlider { + +/* For exporting GCode in GCodeWriter is used XYZF_NUM(val) = PRECISION(val, 3) for XYZ values. + * So, let use same value as a permissible error for layer height. + */ +static double epsilon() { return 0.0011;} + +// custom message the slider sends to its parent to notify a tick-change: +wxDECLARE_EVENT(wxCUSTOMEVT_TICKSCHANGED, wxEvent); + +enum SelectedSlider { + ssUndef, + ssLower, + ssHigher +}; + +enum IconFocus { + ifNone, + ifRevert, + ifCog +}; + +using t_mode = CustomGCode::Mode; + +struct TickCode +{ + bool operator<(const TickCode& other) const { return other.tick > this->tick; } + bool operator>(const TickCode& other) const { return other.tick < this->tick; } + + int tick = 0; + std::string gcode = ColorChangeCode; + int extruder = 0; + std::string color; +}; + +class TickCodeInfo +{ + std::string custom_gcode; + std::string pause_print_msg; + bool m_suppress_plus = false; + bool m_suppress_minus = false; + + std::string get_color_for_tick(TickCode tick, const std::string& code, const int extruder); + +public: + 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); + bool switch_code_for_tick(std::set::iterator it, const std::string& code_to, const int extruder); + 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; } +}; + + +struct ExtrudersSequence +{ + bool is_mm_intervals = true; + double interval_by_mm = 3.0; + int interval_by_layers = 10; + std::vector extruders = { 0 }; + + bool operator==(const ExtrudersSequence& other) const + { + return (other.is_mm_intervals == this->is_mm_intervals ) && + (other.interval_by_mm == this->interval_by_mm ) && + (other.interval_by_layers == this->interval_by_layers ) && + (other.extruders == this->extruders ) ; + } + bool operator!=(const ExtrudersSequence& other) const + { + return (other.is_mm_intervals != this->is_mm_intervals ) && + (other.interval_by_mm != this->interval_by_mm ) && + (other.interval_by_layers != this->interval_by_layers ) && + (other.extruders != this->extruders ) ; + } + + void add_extruder(size_t pos) + { + extruders.insert(extruders.begin() + pos+1, size_t(0)); + } + + void delete_extruder(size_t pos) + { + if (extruders.size() == 1) + return;// last item can't be deleted + extruders.erase(extruders.begin() + pos); + } +}; + +class Control : public wxControl +{ +public: + Control( + wxWindow *parent, + wxWindowID id, + int lowerValue, + int higherValue, + int minValue, + int maxValue, + const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxDefaultSize, + long style = wxSL_VERTICAL, + const wxValidator& val = wxDefaultValidator, + const wxString& name = wxEmptyString); + ~Control() {} + + void msw_rescale(); + + int GetMinValue() const { return m_min_value; } + int GetMaxValue() const { return m_max_value; } + double GetMinValueD() { return m_values.empty() ? 0. : m_values[m_min_value]; } + double GetMaxValueD() { return m_values.empty() ? 0. : m_values[m_max_value]; } + int GetLowerValue() const { return m_lower_value; } + int GetHigherValue() const { return m_higher_value; } + int GetActiveValue() const; + double GetLowerValueD() { return get_double_value(ssLower); } + double GetHigherValueD() { return get_double_value(ssHigher); } + wxSize DoGetBestSize() const override; + wxSize get_min_size() const ; + + // Set low and high slider position. If the span is non-empty, disable the "one layer" mode. + void SetLowerValue (const int lower_val); + void SetHigherValue(const int higher_val); + void SetSelectionSpan(const int lower_val, const int higher_val); + + void SetMaxValue(const int max_value); + void SetKoefForLabels(const double koef) { m_label_koef = koef; } + void SetSliderValues(const std::vector& values) { m_values = values; } + void ChangeOneLayerLock(); + + CustomGCode::Info GetTicksValues() const; + void SetTicksValues(const Slic3r::CustomGCode::Info &custom_gcode_per_print_z); + + void EnableTickManipulation(bool enable = true) { m_is_enabled_tick_manipulation = enable; } + void DisableTickManipulation() { EnableTickManipulation(false); } + + void SetManipulationMode(t_mode mode) { m_mode = mode; } + t_mode GetManipulationMode() const { return m_mode; } + void SetModeAndOnlyExtruder(const bool is_one_extruder_printed_model, const int only_extruder) + { + m_mode = !is_one_extruder_printed_model ? t_mode::MultiExtruder : + only_extruder < 0 ? t_mode::SingleExtruder : + t_mode::MultiAsSingle; + m_only_extruder = only_extruder; + } + + bool is_horizontal() const { return m_style == wxSL_HORIZONTAL; } + bool is_one_layer() const { return m_is_one_layer; } + bool is_lower_at_min() const { return m_lower_value == m_min_value; } + bool is_higher_at_max() const { return m_higher_value == m_max_value; } + bool is_full_span() const { return this->is_lower_at_min() && this->is_higher_at_max(); } + + void OnPaint(wxPaintEvent& ) { render();} + void OnLeftDown(wxMouseEvent& event); + void OnMotion(wxMouseEvent& event); + void OnLeftUp(wxMouseEvent& event); + void OnEnterWin(wxMouseEvent& event) { enter_window(event, true); } + void OnLeaveWin(wxMouseEvent& event) { enter_window(event, false); } + void OnWheel(wxMouseEvent& event); + void OnKeyDown(wxKeyEvent &event); + void OnKeyUp(wxKeyEvent &event); + void OnChar(wxKeyEvent &event); + void OnRightDown(wxMouseEvent& event); + void OnRightUp(wxMouseEvent& event); + + 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 edit_extruder_sequence(); + + ExtrudersSequence m_extruders_sequence; + +protected: + + void render(); + void draw_focus_rect(); + void draw_action_icon(wxDC& dc, const wxPoint pt_beg, const wxPoint pt_end); + void draw_scroll_line(wxDC& dc, const int lower_pos, const int higher_pos); + void draw_thumb(wxDC& dc, const wxCoord& pos_coord, const SelectedSlider& selection); + void draw_thumbs(wxDC& dc, const wxCoord& lower_pos, const wxCoord& higher_pos); + void draw_ticks(wxDC& dc); + void draw_colored_band(wxDC& dc); + void draw_one_layer_icon(wxDC& dc); + void draw_revert_icon(wxDC& dc); + void draw_cog_icon(wxDC &dc); + void draw_thumb_item(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection); + void draw_info_line_with_icon(wxDC& dc, const wxPoint& pos, SelectedSlider selection); + void draw_thumb_text(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection) const; + + void update_thumb_rect(const wxCoord& begin_x, const wxCoord& begin_y, const SelectedSlider& selection); + void detect_selected_slider(const wxPoint& pt); + void correct_lower_value(); + void correct_higher_value(); + void move_current_thumb(const bool condition); + void enter_window(wxMouseEvent& event, const bool enter); + +private: + + bool is_point_in_rect(const wxPoint& pt, const wxRect& rect); + int is_point_near_tick(const wxPoint& pt); + + double get_scroll_step(); + wxString get_label(const SelectedSlider& selection) const; + void get_lower_and_higher_position(int& lower_pos, int& higher_pos); + int get_value_from_position(const wxCoord x, const wxCoord y); + wxCoord get_position_from_value(const int value); + wxSize get_size(); + void get_size(int *w, int *h); + double get_double_value(const SelectedSlider& selection); + wxString get_tooltip(IconFocus icon_focus); + + std::string get_color_for_tool_change_tick(std::set::const_iterator it) const; + std::string get_color_for_color_change_tick(std::set::const_iterator it) const; + + // Get active extruders for tick. + // Means one current extruder for not existing tick OR + // 2 extruders - for existing tick (extruder before ToolChangeCode and extruder of current existing tick) + // Use those values to disable selection of active extruders + std::array get_active_extruders_for_tick(int tick) const; + + // Get used extruders for tick. + // Means all extruders(toools) will be used during printing from current tick to the end + std::set get_used_extruders_for_tick(int tick) const; + + 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*, bool switch_current_code = false); + void append_add_color_change_menu_item(wxMenu*, bool switch_current_code = false); + + bool is_osx { false }; + wxFont m_font; + int m_min_value; + int m_max_value; + int m_lower_value; + int m_higher_value; + ScalableBitmap m_bmp_thumb_higher; + ScalableBitmap m_bmp_thumb_lower; + ScalableBitmap m_bmp_add_tick_on; + ScalableBitmap m_bmp_add_tick_off; + ScalableBitmap m_bmp_del_tick_on; + ScalableBitmap m_bmp_del_tick_off; + ScalableBitmap m_bmp_one_layer_lock_on; + ScalableBitmap m_bmp_one_layer_lock_off; + ScalableBitmap m_bmp_one_layer_unlock_on; + ScalableBitmap m_bmp_one_layer_unlock_off; + ScalableBitmap m_bmp_revert; + ScalableBitmap m_bmp_cog; + SelectedSlider m_selection; + bool m_is_left_down = false; + bool m_is_right_down = false; + bool m_is_one_layer = false; + bool m_is_focused = false; + bool m_is_action_icon_focesed = false; + bool m_is_one_layer_icon_focesed = false; + bool m_is_enabled_tick_manipulation = true; + bool m_show_context_menu = false; + bool m_show_edit_menu = 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; + int m_only_extruder = -1; + + wxRect m_rect_lower_thumb; + wxRect m_rect_higher_thumb; + wxRect m_rect_tick_action; + wxRect m_rect_one_layer_icon; + wxRect m_rect_revert_icon; + wxRect m_rect_cog_icon; + wxSize m_thumb_size; + int m_tick_icon_dim; + int m_lock_icon_dim; + int m_revert_icon_dim; + int m_cog_icon_dim; + long m_style; + float m_label_koef = 1.0; + + std::vector m_values; + TickCodeInfo m_ticks; + +// control's view variables + wxCoord SLIDER_MARGIN; // margin around slider + + wxPen DARK_ORANGE_PEN; + wxPen ORANGE_PEN; + wxPen LIGHT_ORANGE_PEN; + + wxPen DARK_GREY_PEN; + wxPen GREY_PEN; + wxPen LIGHT_GREY_PEN; + + std::vector m_line_pens; + std::vector m_segm_pens; +}; + +} // DoubleSlider; + +} // Slic3r + + + +#endif // slic3r_GUI_DoubleSlider_hpp_ diff --git a/src/slic3r/GUI/ExtruderSequenceDialog.hpp b/src/slic3r/GUI/ExtruderSequenceDialog.hpp index 3efd9e3a2..97d57ef12 100644 --- a/src/slic3r/GUI/ExtruderSequenceDialog.hpp +++ b/src/slic3r/GUI/ExtruderSequenceDialog.hpp @@ -2,7 +2,7 @@ #define slic3r_GUI_ExtruderSequenceDialog_hpp_ #include "GUI_Utils.hpp" -#include "wxExtensions.hpp" +#include "DoubleSlider.hpp" class wxTextCtrl; class wxFlexGridSizer; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 09d6eda2e..d847b297e 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -60,6 +60,7 @@ #include #include #include +#include "DoubleSlider.hpp" #if ENABLE_RENDER_STATISTICS #include #endif // ENABLE_RENDER_STATISTICS @@ -910,7 +911,7 @@ void GLCanvas3D::LegendTexture::fill_color_print_legend_items( const GLCanvas3D { if (custom_code.gcode != ColorChangeCode) continue; - auto lower_b = std::lower_bound(print_zs.begin(), print_zs.end(), custom_code.print_z - DoubleSlider::epsilon()); + auto lower_b = std::lower_bound(print_zs.begin(), print_zs.end(), custom_code.print_z - Slic3r::DoubleSlider::epsilon()); if (lower_b == print_zs.end()) continue; diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index c60547abc..e42f8ed21 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -10,7 +10,7 @@ #include "GLCanvas3DManager.hpp" #include "GLCanvas3D.hpp" #include "PresetBundle.hpp" -#include "wxExtensions.hpp" +#include "DoubleSlider.hpp" #include #include @@ -584,7 +584,7 @@ void Preview::update_view_type(bool slice_completed) void Preview::create_double_slider() { - m_slider = new DoubleSlider(this, wxID_ANY, 0, 0, 0, 100); + m_slider = new DoubleSlider::Control(this, wxID_ANY, 0, 0, 0, 100); m_slider->EnableTickManipulation(wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptFFF); m_double_slider_sizer->Add(m_slider, 0, wxEXPAND, 0); @@ -595,7 +595,7 @@ void Preview::create_double_slider() m_slider->Bind(wxEVT_SCROLL_CHANGED, &Preview::on_sliders_scroll_changed, this); - Bind(wxCUSTOMEVT_TICKSCHANGED, [this](wxEvent&) { + Bind(DoubleSlider::wxCUSTOMEVT_TICKSCHANGED, [this](wxEvent&) { Model& model = wxGetApp().plater()->model(); model.custom_gcode_per_print_z = m_slider->GetTicksValues(); m_schedule_background_process(); diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp index ef311e307..cc8f15325 100644 --- a/src/slic3r/GUI/GUI_Preview.hpp +++ b/src/slic3r/GUI/GUI_Preview.hpp @@ -15,7 +15,6 @@ class wxChoice; class wxComboCtrl; class wxBitmapComboBox; class wxCheckBox; -class DoubleSlider; namespace Slic3r { @@ -25,6 +24,10 @@ class BackgroundSlicingProcess; class GCodePreviewData; class Model; +namespace DoubleSlider { + class Control; +}; + namespace GUI { class GLCanvas3D; @@ -103,7 +106,7 @@ class Preview : public wxPanel bool m_loaded; bool m_enabled; - DoubleSlider* m_slider {nullptr}; + DoubleSlider::Control* m_slider {nullptr}; public: Preview(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, Model* model, DynamicPrintConfig* config, diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 8baa0657e..f5df8fc4f 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -84,6 +84,7 @@ #include // Needs to be last because reasons :-/ #include "WipeTowerDialog.hpp" +#include "libslic3r/CustomGCode.hpp" using boost::optional; namespace fs = boost::filesystem; diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index 626d6f392..356dd458f 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -30,7 +30,6 @@ using Slic3r::GUI::from_u8; using Slic3r::GUI::into_u8; -wxDEFINE_EVENT(wxCUSTOMEVT_TICKSCHANGED, wxEvent); wxDEFINE_EVENT(wxCUSTOMEVT_LAST_VOLUME_IS_DELETED, wxCommandEvent); #ifndef __WXGTK__// msw_menuitem_bitmaps is used for MSW and OSX @@ -408,7 +407,7 @@ int em_unit(wxWindow* win) return Slic3r::GUI::wxGetApp().em_unit(); } -static float get_svg_scale_factor(wxWindow *win) +float get_svg_scale_factor(wxWindow *win) { #ifdef __APPLE__ // Note: win->GetContentScaleFactor() is not used anymore here because it tends to @@ -2276,1724 +2275,6 @@ bool BitmapChoiceRenderer::GetValueFromEditorCtrl(wxWindow* ctrl, wxVariant& val return true; } -// ---------------------------------------------------------------------------- -// DoubleSlider -// ---------------------------------------------------------------------------- -DoubleSlider::DoubleSlider( wxWindow *parent, - wxWindowID id, - int lowerValue, - int higherValue, - int minValue, - int maxValue, - const wxPoint& pos, - const wxSize& size, - long style, - const wxValidator& val, - const wxString& name) : - wxControl(parent, id, pos, size, wxWANTS_CHARS | wxBORDER_NONE), - m_lower_value(lowerValue), m_higher_value (higherValue), - m_min_value(minValue), m_max_value(maxValue), - m_style(style == wxSL_HORIZONTAL || style == wxSL_VERTICAL ? style: wxSL_HORIZONTAL) -{ -#ifdef __WXOSX__ - is_osx = true; -#endif //__WXOSX__ - if (!is_osx) - SetDoubleBuffered(true);// SetDoubleBuffered exists on Win and Linux/GTK, but is missing on OSX - - const float scale_factor = get_svg_scale_factor(this); - - m_bmp_thumb_higher = (style == wxSL_HORIZONTAL ? ScalableBitmap(this, "right_half_circle.png") : ScalableBitmap(this, "thumb_up")); - m_bmp_thumb_lower = (style == wxSL_HORIZONTAL ? ScalableBitmap(this, "left_half_circle.png" ) : ScalableBitmap(this, "thumb_down")); - m_thumb_size = m_bmp_thumb_lower.bmp().GetSize()*(1.0/scale_factor); - - m_bmp_add_tick_on = ScalableBitmap(this, "colorchange_add"); - m_bmp_add_tick_off = ScalableBitmap(this, "colorchange_add_f"); - m_bmp_del_tick_on = ScalableBitmap(this, "colorchange_del"); - m_bmp_del_tick_off = ScalableBitmap(this, "colorchange_del_f"); - m_tick_icon_dim = int((float)m_bmp_add_tick_on.bmp().GetSize().x / scale_factor); - - m_bmp_one_layer_lock_on = ScalableBitmap(this, "lock_closed"); - m_bmp_one_layer_lock_off = ScalableBitmap(this, "lock_closed_f"); - m_bmp_one_layer_unlock_on = ScalableBitmap(this, "lock_open"); - m_bmp_one_layer_unlock_off = ScalableBitmap(this, "lock_open_f"); - m_lock_icon_dim = int((float)m_bmp_one_layer_lock_on.bmp().GetSize().x / scale_factor); - - m_bmp_revert = ScalableBitmap(this, "undo"); - m_revert_icon_dim = int((float)m_bmp_revert.bmp().GetSize().x / scale_factor); - m_bmp_cog = ScalableBitmap(this, "cog"); - m_cog_icon_dim = int((float)m_bmp_cog.bmp().GetSize().x / scale_factor); - - m_selection = ssUndef; - m_ticks.set_pause_print_msg(_utf8(L("Place bearings in slots and resume"))); - - // slider events - Bind(wxEVT_PAINT, &DoubleSlider::OnPaint, this); - Bind(wxEVT_CHAR, &DoubleSlider::OnChar, this); - Bind(wxEVT_LEFT_DOWN, &DoubleSlider::OnLeftDown, this); - Bind(wxEVT_MOTION, &DoubleSlider::OnMotion, this); - Bind(wxEVT_LEFT_UP, &DoubleSlider::OnLeftUp, this); - Bind(wxEVT_MOUSEWHEEL, &DoubleSlider::OnWheel, this); - Bind(wxEVT_ENTER_WINDOW,&DoubleSlider::OnEnterWin, this); - Bind(wxEVT_LEAVE_WINDOW,&DoubleSlider::OnLeaveWin, this); - Bind(wxEVT_KEY_DOWN, &DoubleSlider::OnKeyDown, this); - Bind(wxEVT_KEY_UP, &DoubleSlider::OnKeyUp, this); - Bind(wxEVT_RIGHT_DOWN, &DoubleSlider::OnRightDown,this); - Bind(wxEVT_RIGHT_UP, &DoubleSlider::OnRightUp, this); - - // control's view variables - SLIDER_MARGIN = 4 + Slic3r::GUI::wxGetApp().em_unit(); - - DARK_ORANGE_PEN = wxPen(wxColour(237, 107, 33)); - ORANGE_PEN = wxPen(wxColour(253, 126, 66)); - LIGHT_ORANGE_PEN = wxPen(wxColour(254, 177, 139)); - - DARK_GREY_PEN = wxPen(wxColour(128, 128, 128)); - GREY_PEN = wxPen(wxColour(164, 164, 164)); - LIGHT_GREY_PEN = wxPen(wxColour(204, 204, 204)); - - m_line_pens = { &DARK_GREY_PEN, &GREY_PEN, &LIGHT_GREY_PEN }; - m_segm_pens = { &DARK_ORANGE_PEN, &ORANGE_PEN, &LIGHT_ORANGE_PEN }; - - const wxFont& font = GetFont(); - m_font = is_osx ? font.Smaller().Smaller() : font.Smaller(); -} - -void DoubleSlider::msw_rescale() -{ - const wxFont& font = Slic3r::GUI::wxGetApp().normal_font(); - m_font = is_osx ? font.Smaller().Smaller() : font.Smaller(); - - m_bmp_thumb_higher.msw_rescale(); - m_bmp_thumb_lower .msw_rescale(); - m_thumb_size = m_bmp_thumb_lower.bmp().GetSize(); - - m_bmp_add_tick_on .msw_rescale(); - m_bmp_add_tick_off.msw_rescale(); - m_bmp_del_tick_on .msw_rescale(); - m_bmp_del_tick_off.msw_rescale(); - m_tick_icon_dim = m_bmp_add_tick_on.bmp().GetSize().x; - - m_bmp_one_layer_lock_on .msw_rescale(); - m_bmp_one_layer_lock_off .msw_rescale(); - m_bmp_one_layer_unlock_on .msw_rescale(); - m_bmp_one_layer_unlock_off.msw_rescale(); - m_lock_icon_dim = m_bmp_one_layer_lock_on.bmp().GetSize().x; - - m_bmp_revert.msw_rescale(); - m_revert_icon_dim = m_bmp_revert.bmp().GetSize().x; - m_bmp_cog.msw_rescale(); - m_cog_icon_dim = m_bmp_cog.bmp().GetSize().x; - - SLIDER_MARGIN = 4 + Slic3r::GUI::wxGetApp().em_unit(); - - SetMinSize(get_min_size()); - GetParent()->Layout(); -} - -int DoubleSlider::GetActiveValue() const -{ - return m_selection == ssLower ? - m_lower_value : m_selection == ssHigher ? - m_higher_value : -1; -} - -wxSize DoubleSlider::get_min_size() const -{ - const int min_side = is_horizontal() ? - (is_osx ? 8 : 6) * Slic3r::GUI::wxGetApp().em_unit() : - /*(is_osx ? 10 : 8)*/10 * Slic3r::GUI::wxGetApp().em_unit(); - - return wxSize(min_side, min_side); -} - -wxSize DoubleSlider::DoGetBestSize() const -{ - const wxSize size = wxControl::DoGetBestSize(); - if (size.x > 1 && size.y > 1) - return size; - return get_min_size(); -} - -void DoubleSlider::SetLowerValue(const int lower_val) -{ - m_selection = ssLower; - m_lower_value = lower_val; - correct_lower_value(); - Refresh(); - Update(); - - wxCommandEvent e(wxEVT_SCROLL_CHANGED); - e.SetEventObject(this); - ProcessWindowEvent(e); -} - -void DoubleSlider::SetHigherValue(const int higher_val) -{ - m_selection = ssHigher; - m_higher_value = higher_val; - correct_higher_value(); - Refresh(); - Update(); - - wxCommandEvent e(wxEVT_SCROLL_CHANGED); - e.SetEventObject(this); - ProcessWindowEvent(e); -} - -void DoubleSlider::SetSelectionSpan(const int lower_val, const int higher_val) -{ - m_lower_value = std::max(lower_val, m_min_value); - m_higher_value = std::max(std::min(higher_val, m_max_value), m_lower_value); - if (m_lower_value < m_higher_value) - m_is_one_layer = false; - - Refresh(); - Update(); - - wxCommandEvent e(wxEVT_SCROLL_CHANGED); - e.SetEventObject(this); - ProcessWindowEvent(e); -} - -void DoubleSlider::SetMaxValue(const int max_value) -{ - m_max_value = max_value; - Refresh(); - Update(); -} - -void DoubleSlider::draw_scroll_line(wxDC& dc, const int lower_pos, const int higher_pos) -{ - int width; - int height; - get_size(&width, &height); - - wxCoord line_beg_x = is_horizontal() ? SLIDER_MARGIN : width*0.5 - 1; - wxCoord line_beg_y = is_horizontal() ? height*0.5 - 1 : SLIDER_MARGIN; - wxCoord line_end_x = is_horizontal() ? width - SLIDER_MARGIN + 1 : width*0.5 - 1; - wxCoord line_end_y = is_horizontal() ? height*0.5 - 1 : height - SLIDER_MARGIN + 1; - - wxCoord segm_beg_x = is_horizontal() ? lower_pos : width*0.5 - 1; - wxCoord segm_beg_y = is_horizontal() ? height*0.5 - 1 : lower_pos/*-1*/; - wxCoord segm_end_x = is_horizontal() ? higher_pos : width*0.5 - 1; - wxCoord segm_end_y = is_horizontal() ? height*0.5 - 1 : higher_pos-1; - - for (size_t id = 0; id < m_line_pens.size(); id++) - { - dc.SetPen(*m_line_pens[id]); - dc.DrawLine(line_beg_x, line_beg_y, line_end_x, line_end_y); - dc.SetPen(*m_segm_pens[id]); - dc.DrawLine(segm_beg_x, segm_beg_y, segm_end_x, segm_end_y); - if (is_horizontal()) - line_beg_y = line_end_y = segm_beg_y = segm_end_y += 1; - else - line_beg_x = line_end_x = segm_beg_x = segm_end_x += 1; - } -} - -double DoubleSlider::get_scroll_step() -{ - const wxSize sz = get_size(); - const int& slider_len = m_style == wxSL_HORIZONTAL ? sz.x : sz.y; - return double(slider_len - SLIDER_MARGIN * 2) / (m_max_value - m_min_value); -} - -// get position on the slider line from entered value -wxCoord DoubleSlider::get_position_from_value(const int value) -{ - const double step = get_scroll_step(); - const int val = is_horizontal() ? value : m_max_value - value; - return wxCoord(SLIDER_MARGIN + int(val*step + 0.5)); -} - -wxSize DoubleSlider::get_size() -{ - int w, h; - get_size(&w, &h); - return wxSize(w, h); -} - -void DoubleSlider::get_size(int *w, int *h) -{ - GetSize(w, h); - is_horizontal() ? *w -= m_lock_icon_dim : *h -= m_lock_icon_dim; -} - -double DoubleSlider::get_double_value(const SelectedSlider& selection) -{ - if (m_values.empty() || m_lower_value<0) - return 0.0; - if (m_values.size() <= m_higher_value) { - correct_higher_value(); - return m_values.back(); - } - return m_values[selection == ssLower ? m_lower_value : m_higher_value]; -} - -using t_custom_code = Slic3r::CustomGCode::Item; -Slic3r::CustomGCode::Info DoubleSlider::GetTicksValues() const -{ - Slic3r::CustomGCode::Info custom_gcode_per_print_z; - std::vector& values = custom_gcode_per_print_z.gcodes; - - const int val_size = m_values.size(); - if (!m_values.empty()) - 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_force_mode_apply ? m_mode : m_ticks.mode; - - return custom_gcode_per_print_z; -} - -void DoubleSlider::SetTicksValues(const Slic3r::CustomGCode::Info& custom_gcode_per_print_z) -{ - if (m_values.empty()) - { - m_ticks.mode = m_mode; - return; - } - - const bool was_empty = m_ticks.empty(); - - 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()); - - if (it == m_values.end()) - continue; - - 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; - - Refresh(); - Update(); -} - -void DoubleSlider::get_lower_and_higher_position(int& lower_pos, int& higher_pos) -{ - const double step = get_scroll_step(); - if (is_horizontal()) { - lower_pos = SLIDER_MARGIN + int(m_lower_value*step + 0.5); - higher_pos = SLIDER_MARGIN + int(m_higher_value*step + 0.5); - } - else { - lower_pos = SLIDER_MARGIN + int((m_max_value - m_lower_value)*step + 0.5); - higher_pos = SLIDER_MARGIN + int((m_max_value - m_higher_value)*step + 0.5); - } -} - -void DoubleSlider::draw_focus_rect() -{ - if (!m_is_focused) - return; - const wxSize sz = GetSize(); - wxPaintDC dc(this); - const wxPen pen = wxPen(wxColour(128, 128, 10), 1, wxPENSTYLE_DOT); - dc.SetPen(pen); - dc.SetBrush(wxBrush(wxColour(0, 0, 0), wxBRUSHSTYLE_TRANSPARENT)); - dc.DrawRectangle(1, 1, sz.x - 2, sz.y - 2); -} - -void DoubleSlider::render() -{ - SetBackgroundColour(GetParent()->GetBackgroundColour()); - draw_focus_rect(); - - wxPaintDC dc(this); - dc.SetFont(m_font); - - const wxCoord lower_pos = get_position_from_value(m_lower_value); - const wxCoord higher_pos = get_position_from_value(m_higher_value); - - // draw colored band on the background of a scroll line - // and only in a case of no-empty m_values - draw_colored_band(dc); - - // draw line - draw_scroll_line(dc, lower_pos, higher_pos); - - //draw color print ticks - draw_ticks(dc); - - // draw both sliders - draw_thumbs(dc, lower_pos, higher_pos); - - //draw lock/unlock - draw_one_layer_icon(dc); - - //draw revert bitmap (if it's shown) - draw_revert_icon(dc); - - //draw cog bitmap (if it's shown) - draw_cog_icon(dc); -} - -void DoubleSlider::draw_action_icon(wxDC& dc, const wxPoint pt_beg, const wxPoint pt_end) -{ - const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; - - // suppress add tick on first layer - if (tick == 0) - return; - - wxBitmap* icon = m_is_action_icon_focesed ? &m_bmp_add_tick_off.bmp() : &m_bmp_add_tick_on.bmp(); - 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; - is_horizontal() ? x_draw = pt_beg.x - 0.5*m_tick_icon_dim : y_draw = pt_beg.y - 0.5*m_tick_icon_dim; - if (m_selection == ssLower) - is_horizontal() ? y_draw = pt_end.y + 3 : x_draw = pt_beg.x - m_tick_icon_dim-2; - else - is_horizontal() ? y_draw = pt_beg.y - m_tick_icon_dim-2 : x_draw = pt_end.x + 3; - - dc.DrawBitmap(*icon, x_draw, y_draw); - - //update rect of the tick action icon - m_rect_tick_action = wxRect(x_draw, y_draw, m_tick_icon_dim, m_tick_icon_dim); -} - -void DoubleSlider::draw_info_line_with_icon(wxDC& dc, const wxPoint& pos, const SelectedSlider selection) -{ - if (m_selection == selection) { - //draw info line - dc.SetPen(DARK_ORANGE_PEN); - const wxPoint pt_beg = is_horizontal() ? wxPoint(pos.x, pos.y - m_thumb_size.y) : wxPoint(pos.x - m_thumb_size.x, pos.y/* - 1*/); - const wxPoint pt_end = is_horizontal() ? wxPoint(pos.x, pos.y + m_thumb_size.y) : wxPoint(pos.x + m_thumb_size.x, pos.y/* - 1*/); - dc.DrawLine(pt_beg, pt_end); - - //draw action icon - if (m_is_enabled_tick_manipulation) - draw_action_icon(dc, pt_beg, pt_end); - } -} - -wxString DoubleSlider::get_label(const SelectedSlider& selection) const -{ - const int value = selection == ssLower ? m_lower_value : m_higher_value; - - if (m_label_koef == 1.0 && m_values.empty()) - return wxString::Format("%d", value); - if (value >= m_values.size()) - return "ErrVal"; - - const wxString str = m_values.empty() ? - wxNumberFormatter::ToString(m_label_koef*value, 2, wxNumberFormatter::Style_None) : - wxNumberFormatter::ToString(m_values[value], 2, wxNumberFormatter::Style_None); - return wxString::Format("%s\n(%d)", str, m_values.empty() ? value : value+1); -} - -void DoubleSlider::draw_thumb_text(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection) const -{ - if ( selection == ssUndef || - ((m_is_one_layer || m_higher_value==m_lower_value) && selection != m_selection) ) - return; - wxCoord text_width, text_height; - const wxString label = get_label(selection); - dc.GetMultiLineTextExtent(label, &text_width, &text_height); - wxPoint text_pos; - if (selection ==ssLower) - text_pos = is_horizontal() ? wxPoint(pos.x + 1, pos.y + m_thumb_size.x) : - wxPoint(pos.x + m_thumb_size.x+1, pos.y - 0.5*text_height - 1); - else - text_pos = is_horizontal() ? wxPoint(pos.x - text_width - 1, pos.y - m_thumb_size.x - text_height) : - wxPoint(pos.x - text_width - 1 - m_thumb_size.x, pos.y - 0.5*text_height + 1); - dc.DrawText(label, text_pos); -} - -void DoubleSlider::draw_thumb_item(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection) -{ - wxCoord x_draw, y_draw; - if (selection == ssLower) { - if (is_horizontal()) { - x_draw = pos.x - m_thumb_size.x; - y_draw = pos.y - int(0.5*m_thumb_size.y); - } - else { - x_draw = pos.x - int(0.5*m_thumb_size.x); - y_draw = pos.y - int(0.5*m_thumb_size.y); - } - } - else{ - if (is_horizontal()) { - x_draw = pos.x; - y_draw = pos.y - int(0.5*m_thumb_size.y); - } - else { - x_draw = pos.x - int(0.5*m_thumb_size.x); - y_draw = pos.y - int(0.5*m_thumb_size.y); - } - } - dc.DrawBitmap(selection == ssLower ? m_bmp_thumb_lower.bmp() : m_bmp_thumb_higher.bmp(), x_draw, y_draw); - - // Update thumb rect - update_thumb_rect(x_draw, y_draw, selection); -} - -void DoubleSlider::draw_thumb(wxDC& dc, const wxCoord& pos_coord, const SelectedSlider& selection) -{ - //calculate thumb position on slider line - int width, height; - get_size(&width, &height); - const wxPoint pos = is_horizontal() ? wxPoint(pos_coord, height*0.5) : wxPoint(0.5*width, pos_coord); - - // Draw thumb - draw_thumb_item(dc, pos, selection); - - // Draw info_line - draw_info_line_with_icon(dc, pos, selection); - - // Draw thumb text - draw_thumb_text(dc, pos, selection); -} - -void DoubleSlider::draw_thumbs(wxDC& dc, const wxCoord& lower_pos, const wxCoord& higher_pos) -{ - //calculate thumb position on slider line - int width, height; - get_size(&width, &height); - const wxPoint pos_l = is_horizontal() ? wxPoint(lower_pos, height*0.5) : wxPoint(0.5*width, lower_pos); - const wxPoint pos_h = is_horizontal() ? wxPoint(higher_pos, height*0.5) : wxPoint(0.5*width, higher_pos); - - // Draw lower thumb - draw_thumb_item(dc, pos_l, ssLower); - // Draw lower info_line - draw_info_line_with_icon(dc, pos_l, ssLower); - - // Draw higher thumb - draw_thumb_item(dc, pos_h, ssHigher); - // Draw higher info_line - draw_info_line_with_icon(dc, pos_h, ssHigher); - // Draw higher thumb text - draw_thumb_text(dc, pos_h, ssHigher); - - // Draw lower thumb text - draw_thumb_text(dc, pos_l, ssLower); -} - -void DoubleSlider::draw_ticks(wxDC& dc) -{ - if (!m_is_enabled_tick_manipulation) - return; - - dc.SetPen(m_is_enabled_tick_manipulation ? DARK_GREY_PEN : LIGHT_GREY_PEN ); - int height, width; - get_size(&width, &height); - const wxCoord mid = is_horizontal() ? 0.5*height : 0.5*width; - for (auto tick : m_ticks.ticks) - { - const wxCoord pos = get_position_from_value(tick.tick); - - is_horizontal() ? dc.DrawLine(pos, mid-14, pos, mid-9) : - dc.DrawLine(mid - 14, pos/* - 1*/, mid - 9, pos/* - 1*/); - 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 - { - 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; - - dc.DrawBitmap(icon, x_draw, y_draw); - } - } -} - -std::string DoubleSlider::get_color_for_tool_change_tick(std::set::const_iterator it) const -{ - const int current_extruder = it->extruder == 0 ? std::max(m_only_extruder, 1) : it->extruder; - - auto it_n = it; - while (it_n != m_ticks.ticks.begin()) { - --it_n; - if (it_n->gcode == Slic3r::ColorChangeCode && it_n->extruder == current_extruder) - return it_n->color; - } - - return it->color; -} - -std::string DoubleSlider::get_color_for_color_change_tick(std::set::const_iterator it) const -{ - const int def_extruder = std::max(1, m_only_extruder); - auto it_n = it; - bool is_tool_change = false; - while (it_n != m_ticks.ticks.begin()) { - --it_n; - if (it_n->gcode == Slic3r::ToolChangeCode) { - is_tool_change = true; - if (it_n->extruder == it->extruder) - return it->color; - break; - } - } - if (!is_tool_change && it->extruder == def_extruder) - return it->color; - - return ""; -} - -void DoubleSlider::draw_colored_band(wxDC& dc) -{ - if (!m_is_enabled_tick_manipulation) - return; - - int height, width; - get_size(&width, &height); - - const wxCoord mid = is_horizontal() ? 0.5 * height : 0.5 * width; - - wxRect main_band = is_horizontal() ? - wxRect(SLIDER_MARGIN, lround(mid - 0.375 * m_thumb_size.y), - width - 2 * SLIDER_MARGIN + 1, lround(0.75 * m_thumb_size.y)) : - wxRect(lround(mid - 0.375 * m_thumb_size.x), SLIDER_MARGIN, - lround(0.75 * m_thumb_size.x), height - 2 * SLIDER_MARGIN + 1); - - auto draw_band = [](wxDC& dc, const wxColour& clr, const wxRect& band_rc) { - dc.SetPen(clr); - dc.SetBrush(clr); - dc.DrawRectangle(band_rc); - }; - - // don't color a band for MultiExtruder mode - if (m_ticks.empty() || m_mode == t_mode::MultiExtruder) - { - draw_band(dc, GetParent()->GetBackgroundColour(), main_band); - return; - } - - 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.ticks.begin(); - - 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)) ) - { - const wxCoord pos = get_position_from_value(tick_it->tick); - is_horizontal() ? main_band.SetLeft(SLIDER_MARGIN + pos) : - main_band.SetBottom(pos - 1); - - const std::string clr_str = m_mode == t_mode::SingleExtruder ? tick_it->color : - tick_it->gcode == Slic3r::ToolChangeCode ? - get_color_for_tool_change_tick(tick_it) : - get_color_for_color_change_tick(tick_it); - - if (!clr_str.empty()) - draw_band(dc, wxColour(clr_str), main_band); - } - ++tick_it; - } -} - -void DoubleSlider::draw_one_layer_icon(wxDC& dc) -{ - const wxBitmap& icon = m_is_one_layer ? - m_is_one_layer_icon_focesed ? m_bmp_one_layer_lock_off.bmp() : m_bmp_one_layer_lock_on.bmp() : - m_is_one_layer_icon_focesed ? m_bmp_one_layer_unlock_off.bmp() : m_bmp_one_layer_unlock_on.bmp(); - - int width, height; - get_size(&width, &height); - - wxCoord x_draw, y_draw; - is_horizontal() ? x_draw = width-2 : x_draw = 0.5*width - 0.5*m_lock_icon_dim; - is_horizontal() ? y_draw = 0.5*height - 0.5*m_lock_icon_dim : y_draw = height-2; - - dc.DrawBitmap(icon, x_draw, y_draw); - - //update rect of the lock/unlock icon - m_rect_one_layer_icon = wxRect(x_draw, y_draw, m_lock_icon_dim, m_lock_icon_dim); -} - -void DoubleSlider::draw_revert_icon(wxDC& dc) -{ - if (m_ticks.empty() || !m_is_enabled_tick_manipulation) - return; - - int width, height; - get_size(&width, &height); - - wxCoord x_draw, y_draw; - is_horizontal() ? x_draw = width-2 : x_draw = 0.25*SLIDER_MARGIN; - is_horizontal() ? y_draw = 0.25*SLIDER_MARGIN: y_draw = height-2; - - dc.DrawBitmap(m_bmp_revert.bmp(), x_draw, y_draw); - - //update rect of the lock/unlock icon - m_rect_revert_icon = wxRect(x_draw, y_draw, m_revert_icon_dim, m_revert_icon_dim); -} - -void DoubleSlider::draw_cog_icon(wxDC& dc) -{ - if (m_mode != t_mode::MultiExtruder) - return; - - int width, height; - get_size(&width, &height); - - wxCoord x_draw, y_draw; - is_horizontal() ? x_draw = width-2 : x_draw = width - m_cog_icon_dim - 2; - is_horizontal() ? y_draw = height - m_cog_icon_dim - 2 : y_draw = height-2; - - dc.DrawBitmap(m_bmp_cog.bmp(), x_draw, y_draw); - - //update rect of the lock/unlock icon - m_rect_cog_icon = wxRect(x_draw, y_draw, m_cog_icon_dim, m_cog_icon_dim); -} - -void DoubleSlider::update_thumb_rect(const wxCoord& begin_x, const wxCoord& begin_y, const SelectedSlider& selection) -{ - const wxRect& rect = wxRect(begin_x, begin_y, m_thumb_size.x, int(m_thumb_size.y*0.5)); - if (selection == ssLower) - m_rect_lower_thumb = rect; - else - m_rect_higher_thumb = rect; -} - -int DoubleSlider::get_value_from_position(const wxCoord x, const wxCoord y) -{ - const int height = get_size().y; - const double step = get_scroll_step(); - - if (is_horizontal()) - return int(double(x - SLIDER_MARGIN) / step + 0.5); - - return int(m_min_value + double(height - SLIDER_MARGIN - y) / step + 0.5); -} - -void DoubleSlider::detect_selected_slider(const wxPoint& pt) -{ - m_selection = is_point_in_rect(pt, m_rect_lower_thumb) ? ssLower : - is_point_in_rect(pt, m_rect_higher_thumb) ? ssHigher : ssUndef; -} - -bool DoubleSlider::is_point_in_rect(const wxPoint& pt, const wxRect& rect) -{ - return rect.GetLeft() <= pt.x && pt.x <= rect.GetRight() && - rect.GetTop() <= pt.y && pt.y <= rect.GetBottom(); -} - -int DoubleSlider::is_point_near_tick(const wxPoint& pt) -{ - for (auto tick : m_ticks.ticks) { - const wxCoord pos = get_position_from_value(tick.tick); - - if (is_horizontal()) { - if (pos - 4 <= pt.x && pt.x <= pos + 4) - return tick.tick; - } - else { - if (pos - 4 <= pt.y && pt.y <= pos + 4) - return tick.tick; - } - } - return -1; -} - -void DoubleSlider::ChangeOneLayerLock() -{ - m_is_one_layer = !m_is_one_layer; - m_selection == ssLower ? correct_lower_value() : correct_higher_value(); - if (!m_selection) m_selection = ssHigher; - - Refresh(); - Update(); - - wxCommandEvent e(wxEVT_SCROLL_CHANGED); - e.SetEventObject(this); - ProcessWindowEvent(e); -} - -void DoubleSlider::OnLeftDown(wxMouseEvent& event) -{ - if (HasCapture()) - return; - this->CaptureMouse(); - - wxClientDC dc(this); - wxPoint pos = event.GetLogicalPosition(dc); - 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; - } - - m_is_left_down = true; - if (is_point_in_rect(pos, m_rect_one_layer_icon)) { - // switch on/off one layer mode - m_is_one_layer = !m_is_one_layer; - if (!m_is_one_layer) { - SetLowerValue(m_min_value); - SetHigherValue(m_max_value); - } - m_selection == ssLower ? correct_lower_value() : correct_higher_value(); - if (!m_selection) m_selection = ssHigher; - } - else if (is_point_in_rect(pos, m_rect_revert_icon) && m_is_enabled_tick_manipulation) { - // discard all color changes - SetLowerValue(m_min_value); - SetHigherValue(m_max_value); - - m_selection == ssLower ? correct_lower_value() : correct_higher_value(); - if (!m_selection) m_selection = ssHigher; - - 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_force_edit_extruder_sequence = true; - return; - } - else - detect_selected_slider(pos); - - if (!m_selection) { - const int tick_val = is_point_near_tick(pos); - /* Set current thumb position to the nearest tick (if it is) - * OR to a value corresponding to the mouse click - * */ - const int mouse_val = tick_val >= 0 && m_is_enabled_tick_manipulation ? tick_val : - get_value_from_position(pos.x, pos.y); - if (mouse_val >= 0) - { - // if (abs(mouse_val - m_lower_value) < abs(mouse_val - m_higher_value)) { - if ( mouse_val <= m_lower_value ) { - SetLowerValue(mouse_val); - correct_lower_value(); - m_selection = ssLower; - } - else { - SetHigherValue(mouse_val); - correct_higher_value(); - m_selection = ssHigher; - } - } - } - - Refresh(); - Update(); - event.Skip(); -} - -void DoubleSlider::correct_lower_value() -{ - if (m_lower_value < m_min_value) - m_lower_value = m_min_value; - else if (m_lower_value > m_max_value) - m_lower_value = m_max_value; - - if ((m_lower_value >= m_higher_value && m_lower_value <= m_max_value) || m_is_one_layer) - m_higher_value = m_lower_value; -} - -void DoubleSlider::correct_higher_value() -{ - if (m_higher_value > m_max_value) - m_higher_value = m_max_value; - else if (m_higher_value < m_min_value) - m_higher_value = m_min_value; - - if ((m_higher_value <= m_lower_value && m_higher_value >= m_min_value) || m_is_one_layer) - m_lower_value = m_higher_value; -} - -wxString DoubleSlider::get_tooltip(IconFocus icon_focus) -{ - wxString tooltip(wxEmptyString); - if (m_is_one_layer_icon_focesed) - tooltip = _(L("One layer mode")); - - if (icon_focus == ifRevert) - tooltip = _(L("Discard all custom changes")); - if (icon_focus == ifCog) - tooltip = _(L("Set extruder sequence for whole print")); - 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.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")) : - tick_code_it->gcode == Slic3r::ColorChangeCode ? ( m_mode == t_mode::SingleExtruder ? - _(L("For Delete color change use left mouse button click\n" - "For Edit color use right mouse button click")) : - from_u8((boost::format(_utf8(L("Delete color change for Extruder %1%"))) % tick_code_it->extruder).str()) ): - tick_code_it->gcode == Slic3r::PausePrintCode ? - _(L("Delete pause")) : - tick_code_it->gcode == Slic3r::ToolChangeCode ? - from_u8((boost::format(_utf8(L("Delete extruder change to \"%1%\""))) % tick_code_it->extruder).str()) : - from_u8((boost::format(_utf8(L("For Delete \"%1%\" code use left mouse button click\n" - "For Edit \"%1%\" code use right mouse button click"))) % tick_code_it->gcode ).str()); - } - - return tooltip; -} - -void DoubleSlider::OnMotion(wxMouseEvent& event) -{ - bool action = false; - - const wxClientDC dc(this); - const wxPoint pos = event.GetLogicalPosition(dc); - - m_is_one_layer_icon_focesed = is_point_in_rect(pos, m_rect_one_layer_icon); - IconFocus icon_focus = ifNone; - - if (!m_is_left_down && !m_is_one_layer) { - m_is_action_icon_focesed = is_point_in_rect(pos, m_rect_tick_action); - if (!m_ticks.empty() && is_point_in_rect(pos, m_rect_revert_icon)) - icon_focus = ifRevert; - else if (is_point_in_rect(pos, m_rect_cog_icon)) - icon_focus = ifCog; - } - else if (m_is_left_down || m_is_right_down) { - if (m_selection == ssLower) { - int current_value = m_lower_value; - m_lower_value = get_value_from_position(pos.x, pos.y); - correct_lower_value(); - action = (current_value != m_lower_value); - } - else if (m_selection == ssHigher) { - int current_value = m_higher_value; - m_higher_value = get_value_from_position(pos.x, pos.y); - correct_higher_value(); - action = (current_value != m_higher_value); - } - } - Refresh(); - Update(); - event.Skip(); - - // Set tooltips with information for each icon - this->SetToolTip(get_tooltip(icon_focus)); - - if (action) - { - wxCommandEvent e(wxEVT_SCROLL_CHANGED); - e.SetEventObject(this); - e.SetString("moving"); - ProcessWindowEvent(e); - } -} - -void DoubleSlider::append_change_extruder_menu_item(wxMenu* menu, bool switch_current_code/* = false*/) -{ - const int extruders_cnt = Slic3r::GUI::wxGetApp().extruders_edited_cnt(); - if (extruders_cnt > 1) - { - const int initial_extruder = std::max(1 , get_extruder_for_tick(m_selection == ssLower ? m_lower_value : m_higher_value)); - - wxMenu* change_extruder_menu = new wxMenu(); - - for (int i = 1; i <= extruders_cnt; i++) - { - const bool is_active_extruder = i == initial_extruder; - const wxString item_name = wxString::Format(_(L("Extruder %d")), i) + - (is_active_extruder ? " (" + _(L("active")) + ")" : ""); - - if (m_mode == t_mode::MultiAsSingle) - append_menu_item(change_extruder_menu, wxID_ANY, item_name, "", - [this, i](wxCommandEvent&) { add_code_as_tick(Slic3r::ToolChangeCode, i); }, "", menu, - [is_active_extruder]() { return !is_active_extruder; }, Slic3r::GUI::wxGetApp().plater()); - } - - const wxString change_extruder_menu_name = m_mode == t_mode::MultiAsSingle ? - (switch_current_code ? _(L("Switch code to Change extruder")) : _(L("Change extruder")) ) : - _(L("Change extruder (N/A)")); - - wxMenuItem* change_extruder_menu_item = menu->AppendSubMenu(change_extruder_menu, change_extruder_menu_name, _(L("Use another extruder"))); - change_extruder_menu_item->SetBitmap(create_scaled_bitmap(this, "change_extruder")); - - Slic3r::GUI::wxGetApp().plater()->Bind(wxEVT_UPDATE_UI, [this, change_extruder_menu_item](wxUpdateUIEvent& evt) { - enable_menu_item(evt, [this]() {return m_mode == t_mode::MultiAsSingle; }, change_extruder_menu_item, this); }, - change_extruder_menu_item->GetId()); - } -} - -void DoubleSlider::append_add_color_change_menu_item(wxMenu* menu, bool switch_current_code/* = false*/) -{ - const int extruders_cnt = Slic3r::GUI::wxGetApp().extruders_edited_cnt(); - if (extruders_cnt > 1) - { - std::set used_extruders_for_tick = get_used_extruders_for_tick(m_selection == ssLower ? m_lower_value : m_higher_value); - - wxMenu* add_color_change_menu = new wxMenu(); - - for (int i = 1; i <= extruders_cnt; i++) - { - const bool is_used_extruder = used_extruders_for_tick.empty() ? true : // #ys_FIXME till used_extruders_for_tick doesn't filled correct for mmMultiExtruder - used_extruders_for_tick.find(i) != used_extruders_for_tick.end(); - const wxString item_name = wxString::Format(_(L("Extruder %d")), i) + - (is_used_extruder ? " (" + _(L("used")) + ")" : ""); - - append_menu_item(add_color_change_menu, wxID_ANY, item_name, "", - [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 = switch_current_code ? - from_u8((boost::format(_utf8(L("Switch code to Color change (%1%) for:"))) % Slic3r::ColorChangeCode).str()) : - 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(this, "colorchange_add_m")); - } -} - -void DoubleSlider::OnLeftUp(wxMouseEvent& event) -{ - if (!HasCapture()) - return; - this->ReleaseMouse(); - m_is_left_down = false; - - if (m_force_delete_tick) - { - delete_current_tick(); - m_force_delete_tick = false; - } - 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_force_edit_extruder_sequence = false; - } - - Refresh(); - Update(); - event.Skip(); - - wxCommandEvent e(wxEVT_SCROLL_CHANGED); - e.SetEventObject(this); - ProcessWindowEvent(e); -} - -void DoubleSlider::enter_window(wxMouseEvent& event, const bool enter) -{ - m_is_focused = enter; - Refresh(); - Update(); - event.Skip(); -} - -// "condition" have to be true for: -// - value increase (if wxSL_VERTICAL) -// - value decrease (if wxSL_HORIZONTAL) -void DoubleSlider::move_current_thumb(const bool condition) -{ -// m_is_one_layer = wxGetKeyState(WXK_CONTROL); - int delta = condition ? -1 : 1; - if (is_horizontal()) - delta *= -1; - - if (m_selection == ssLower) { - m_lower_value -= delta; - correct_lower_value(); - } - else if (m_selection == ssHigher) { - m_higher_value -= delta; - correct_higher_value(); - } - Refresh(); - Update(); - - wxCommandEvent e(wxEVT_SCROLL_CHANGED); - e.SetEventObject(this); - ProcessWindowEvent(e); -} - -void DoubleSlider::OnWheel(wxMouseEvent& event) -{ - // Set nearest to the mouse thumb as a selected, if there is not selected thumb - if (m_selection == ssUndef) - { - const wxPoint& pt = event.GetLogicalPosition(wxClientDC(this)); - - if (is_horizontal()) - m_selection = abs(pt.x - m_rect_lower_thumb.GetRight()) <= - abs(pt.x - m_rect_higher_thumb.GetLeft()) ? - ssLower : ssHigher; - else - m_selection = abs(pt.y - m_rect_lower_thumb.GetTop()) <= - abs(pt.y - m_rect_higher_thumb.GetBottom()) ? - ssLower : ssHigher; - } - - move_current_thumb(event.GetWheelRotation() > 0); -} - -void DoubleSlider::OnKeyDown(wxKeyEvent &event) -{ - const int key = event.GetKeyCode(); - 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) - move_current_thumb(key == WXK_LEFT); - else if (key == WXK_UP || key == WXK_DOWN) { - m_selection = key == WXK_UP ? ssHigher : ssLower; - Refresh(); - } - } - else { - if (key == WXK_LEFT || key == WXK_RIGHT) { - m_selection = key == WXK_LEFT ? ssHigher : ssLower; - Refresh(); - } - else if (key == WXK_UP || key == WXK_DOWN) - move_current_thumb(key == WXK_UP); - } - - event.Skip(); // !Needed to have EVT_CHAR generated as well -} - -void DoubleSlider::OnKeyUp(wxKeyEvent &event) -{ - if (event.GetKeyCode() == WXK_CONTROL) - m_is_one_layer = false; - Refresh(); - Update(); - event.Skip(); -} - -void DoubleSlider::OnChar(wxKeyEvent& event) -{ - const int key = event.GetKeyCode(); - 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) -{ - if (HasCapture()) return; - this->CaptureMouse(); - - const wxClientDC dc(this); - - wxPoint pos = event.GetLogicalPosition(dc); - if (is_point_in_rect(pos, m_rect_tick_action) && m_is_enabled_tick_manipulation) - { - const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; - if (m_ticks.ticks.find(TICK_CODE{ tick }) == m_ticks.ticks.end()) // if on this Z doesn't exist tick - // show context menu on OnRightUp() - m_show_context_menu = true; - else - // show "Edit" and "Delete" menu on OnRightUp() - m_show_edit_menu = true; - return; - } - - detect_selected_slider(event.GetLogicalPosition(dc)); - if (!m_selection) - return; - - if (m_selection == ssLower) - m_higher_value = m_lower_value; - else - m_lower_value = m_higher_value; - - // set slider to "one layer" mode - m_is_right_down = m_is_one_layer = true; - - Refresh(); - Update(); - event.Skip(); -} - -int DoubleSlider::get_extruder_for_tick(int tick) -{ - int default_initial_extruder = m_mode == t_mode::MultiAsSingle ? m_only_extruder : 0; - if (m_ticks.empty()) - return default_initial_extruder; - - 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; - } - - return default_initial_extruder; -} - -std::set DoubleSlider::get_used_extruders_for_tick(int tick) -{ - if (m_mode == t_mode::MultiExtruder) - { - // #ys_FIXME: get tool ordering from _correct_ place - const Slic3r::ToolOrdering& tool_ordering = Slic3r::GUI::wxGetApp().plater()->fff_print().get_tool_ordering(); - - if (tool_ordering.empty()) - return {}; - - std::set used_extruders; - - auto it_layer_tools = std::lower_bound(tool_ordering.begin(), tool_ordering.end(), Slic3r::LayerTools(m_values[tick])); - for (; it_layer_tools != tool_ordering.end(); ++it_layer_tools) - { - const std::vector& extruders = it_layer_tools->extruders; - for (const auto& extruder : extruders) - used_extruders.emplace(extruder+1); - } - - return used_extruders; - } - - const int default_initial_extruder = m_mode == t_mode::MultiAsSingle ? std::max(m_only_extruder, 1) : 1; - if (m_ticks.empty()) - return {default_initial_extruder}; - - std::set used_extruders; - const std::set& ticks = m_ticks.ticks; - - auto it_start = ticks.lower_bound(TICK_CODE{tick}); - auto it = it_start; - if (it == ticks.begin() && it->gcode == Slic3r::ToolChangeCode && - tick != it->tick ) // In case of switch of ToolChange to ColorChange, when tick exists, - // we shouldn't change color for extruder, which will be deleted - { - used_extruders.emplace(it->extruder); - if (tick < it->tick) - used_extruders.emplace(default_initial_extruder); - } - - while (it != ticks.begin()) { - --it; - if (it->gcode == Slic3r::ToolChangeCode && tick != it->tick) { - used_extruders.emplace(it->extruder); - break; - } - } - - if (it == ticks.begin() && used_extruders.empty()) - used_extruders.emplace(default_initial_extruder); - - for (it = it_start; it != ticks.end(); ++it) - if (it->gcode == Slic3r::ToolChangeCode && tick != it->tick) - used_extruders.emplace(it->extruder); - - return used_extruders; -} - -void DoubleSlider::OnRightUp(wxMouseEvent& event) -{ - if (!HasCapture()) - return; - this->ReleaseMouse(); - m_is_right_down = m_is_one_layer = false; - - if (m_show_context_menu) { - wxMenu menu; - - if (m_mode == t_mode::SingleExtruder) - append_menu_item(&menu, wxID_ANY, _(L("Add color change")) + " (M600)", "", - [this](wxCommandEvent&) { add_code_as_tick(Slic3r::ColorChangeCode); }, "colorchange_add_m", &menu, - [](){return true;}, this); - else - { - append_change_extruder_menu_item(&menu); - append_add_color_change_menu_item(&menu); - } - - append_menu_item(&menu, wxID_ANY, _(L("Add pause print")) + " (M601)", "", - [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_as_tick(""); }, "edit_gcode", &menu, - []() {return true; }, this); - - Slic3r::GUI::wxGetApp().plater()->PopupMenu(&menu); - - m_show_context_menu = false; - } - else if (m_show_edit_menu) { - wxMenu menu; - - 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; - - if (it->gcode == Slic3r::ToolChangeCode) - append_add_color_change_menu_item(&menu, true); - else - append_menu_item(&menu, wxID_ANY, it->gcode == Slic3r::ColorChangeCode ? _(L("Edit color")) : - it->gcode == Slic3r::PausePrintCode ? _(L("Edit pause print message")) : - _(L("Edit custom G-code")), "", - [this](wxCommandEvent&) { edit_tick(); }, "edit_uni", &menu); - - if (it->gcode == Slic3r::ColorChangeCode && m_mode == t_mode::MultiAsSingle) - append_change_extruder_menu_item(&menu, true); - - append_menu_item(&menu, wxID_ANY, it->gcode == Slic3r::ColorChangeCode ? _(L("Delete color change")) : - it->gcode == Slic3r::ToolChangeCode ? _(L("Delete tool change")) : - it->gcode == Slic3r::PausePrintCode ? _(L("Delete pause print")) : - _(L("Delete custom G-code")), "", - [this](wxCommandEvent&) { delete_current_tick();}, "colorchange_del_f", &menu); - - Slic3r::GUI::wxGetApp().plater()->PopupMenu(&menu); - - m_show_edit_menu = false; - } - - Refresh(); - Update(); - event.Skip(); -} - -static std::string get_new_color(const std::string& color) -{ - wxColour clr(color); - if (!clr.IsOk()) - clr = wxColour(0, 0, 0); // Don't set alfa to transparence - - auto data = new wxColourData(); - data->SetChooseFull(1); - data->SetColour(clr); - - wxColourDialog dialog(nullptr, data); - dialog.CenterOnParent(); - if (dialog.ShowModal() == wxID_OK) - return dialog.GetColourData().GetColour().GetAsString(wxC2S_HTML_SYNTAX).ToStdString(); - return ""; -} - -static std::string get_custom_code(const std::string& code_in, double height) -{ - wxString msg_text = from_u8(_utf8(L("Enter custom G-code used on current layer"))) + ":"; - wxString msg_header = from_u8((boost::format(_utf8(L("Custom Gcode on current layer (%1% mm)."))) % height).str()); - - // get custom gcode - wxTextEntryDialog dlg(nullptr, msg_text, msg_header, code_in, - wxTextEntryDialogStyle | wxTE_MULTILINE); - if (dlg.ShowModal() != wxID_OK || dlg.GetValue().IsEmpty()) - return ""; - - return dlg.GetValue().ToStdString(); -} - -static std::string get_pause_print_msg(const std::string& msg_in, double height) -{ - wxString msg_text = from_u8(_utf8(L("Enter short message shown on Printer display during pause print"))) + ":"; - wxString msg_header = from_u8((boost::format(_utf8(L("Message for pause print on current layer (%1% mm)."))) % height).str()); - - // get custom gcode - wxTextEntryDialog dlg(nullptr, msg_text, msg_header, from_u8(msg_in), - wxTextEntryDialogStyle); - if (dlg.ShowModal() != wxID_OK || dlg.GetValue().IsEmpty()) - return ""; - - return into_u8(dlg.GetValue()); -} - -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 ( !check_ticks_changed_event(code) ) - return; - - const int extruder = selected_extruder > 0 ? selected_extruder : std::max(1, m_only_extruder); - const auto it = m_ticks.ticks.find(TICK_CODE{ tick }); - - if ( it == m_ticks.ticks.end() ) { - // try to add tick - if (!m_ticks.add_tick(tick, code, extruder, m_values[tick])) - return; - } - else if (code == Slic3r::ToolChangeCode || code == Slic3r::ColorChangeCode) { - // try to switch tick code to ToolChangeCode or ColorChangeCode accordingly - if (!m_ticks.switch_code_for_tick(it, code, extruder)) - return; - } - else - return; - - post_ticks_changed_event(code); -} - -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; - /* Menu position will be calculated from mouse click position, but... - * if function is called from keyboard (pressing "+"), we should to calculate it - * */ - 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() || - !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; - const std::set::iterator it = m_ticks.ticks.find(TICK_CODE{ tick }); - - if (it == m_ticks.ticks.end() || - !check_ticks_changed_event(it->gcode)) - return; - - 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; - - const ExtrudersSequence& from_dlg_val = dlg.GetValue(); - if (m_extruders_sequence == from_dlg_val) - return; - - m_extruders_sequence = from_dlg_val; - - m_ticks.erase_all_ticks_with_code(Slic3r::ToolChangeCode); - - int tick = 0; - double value = 0.0; - int extruder = 0; - const int extr_cnt = m_extruders_sequence.extruders.size(); - - std::vector colors = Slic3r::GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config(); - - while (tick <= m_max_value) - { - 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) - extruder = 0; - if (m_extruders_sequence.is_mm_intervals) - { - value += m_extruders_sequence.interval_by_mm; - auto val_it = std::lower_bound(m_values.begin(), m_values.end(), value - epsilon()); - - if (val_it == m_values.end()) - break; - - tick = val_it - m_values.begin(); - } - else - tick += m_extruders_sequence.interval_by_layers; - } - - post_ticks_changed_event(Slic3r::ToolChangeCode); -} - -void DoubleSlider::post_ticks_changed_event(const std::string& gcode /*= ""*/) -{ - 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; -} - - -std::string DoubleSlider::TICK_CODE_INFO::get_color_for_tick(TICK_CODE tick, const std::string& code, const int extruder) -{ - std::vector colors = Slic3r::GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config(); - std::string color = colors[extruder - 1]; - - if (code == Slic3r::ColorChangeCode) - { - if (!ticks.empty()) - { - auto before_tick_it = std::lower_bound(ticks.begin(), ticks.end(), 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); - } - return color; -} - -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 - { - color = get_color_for_tick(TICK_CODE{ tick }, code, extruder); - 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; -} - -bool DoubleSlider::TICK_CODE_INFO::switch_code_for_tick(std::set::iterator it, const std::string& code_to, const int extruder) -{ - const std::string color = get_color_for_tick(*it, code_to, extruder); - if (color.empty()) - return false; - - TICK_CODE changed_tick = *it; - changed_tick.gcode = code_to; - changed_tick.extruder = extruder; - changed_tick.color = color; - - ticks.erase(it); - ticks.emplace(changed_tick); - - return true; -} - -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 322358ffe..a6f8862ac 100644 --- a/src/slic3r/GUI/wxExtensions.hpp +++ b/src/slic3r/GUI/wxExtensions.hpp @@ -5,19 +5,15 @@ #include #include #include -#include #include #include #include -#include #include #include #include #include #include -#include "libslic3r/Model.hpp" -#include "libslic3r/GCodeWriter.hpp" namespace Slic3r { enum class ModelVolumeType : int; @@ -49,12 +45,15 @@ wxMenuItem* append_menu_radio_item(wxMenu* menu, int id, const wxString& string, wxMenuItem* append_menu_check_item(wxMenu* menu, int id, const wxString& string, const wxString& description, std::function cb, wxEvtHandler* event_handler); +void enable_menu_item(wxUpdateUIEvent& evt, std::function const cb_condition, wxMenuItem* item, wxWindow* win); + class wxDialog; class wxBitmapComboBox; void edit_tooltip(wxString& tooltip); void msw_buttons_rescale(wxDialog* dlg, const int em_unit, const std::vector& btn_ids); int em_unit(wxWindow* win); +float get_svg_scale_factor(wxWindow* win); wxBitmap create_scaled_bitmap(wxWindow *win, const std::string& bmp_name, const int px_cnt = 16, const bool is_horizontal = false, const bool grayscale = false); @@ -742,311 +741,6 @@ private: }; -// ---------------------------------------------------------------------------- -// DoubleSlider -// ---------------------------------------------------------------------------- - -// custom message the slider sends to its parent to notify a tick-change: -wxDECLARE_EVENT(wxCUSTOMEVT_TICKSCHANGED, wxEvent); - -enum SelectedSlider { - ssUndef, - ssLower, - ssHigher -}; -enum TicksAction{ - taOnIcon, - taAdd, - taDel -}; - -class DoubleSlider : public wxControl -{ - enum IconFocus { - ifNone, - ifRevert, - ifCog - }; -public: - DoubleSlider( - wxWindow *parent, - wxWindowID id, - int lowerValue, - int higherValue, - int minValue, - int maxValue, - const wxPoint& pos = wxDefaultPosition, - const wxSize& size = wxDefaultSize, - long style = wxSL_VERTICAL, - const wxValidator& val = wxDefaultValidator, - const wxString& name = wxEmptyString); - ~DoubleSlider() {} - - using t_mode = Slic3r::CustomGCode::Mode; - - /* For exporting GCode in GCodeWriter is used XYZF_NUM(val) = PRECISION(val, 3) for XYZ values. - * So, let use same value as a permissible error for layer height. - */ - static double epsilon() { return 0.0011;} - - void msw_rescale(); - - int GetMinValue() const { return m_min_value; } - int GetMaxValue() const { return m_max_value; } - double GetMinValueD() { return m_values.empty() ? 0. : m_values[m_min_value]; } - double GetMaxValueD() { return m_values.empty() ? 0. : m_values[m_max_value]; } - int GetLowerValue() const { return m_lower_value; } - int GetHigherValue() const { return m_higher_value; } - int GetActiveValue() const; - wxSize get_min_size() const ; - double GetLowerValueD() { return get_double_value(ssLower); } - double GetHigherValueD() { return get_double_value(ssHigher); } - wxSize DoGetBestSize() const override; - void SetLowerValue(const int lower_val); - void SetHigherValue(const int higher_val); - // Set low and high slider position. If the span is non-empty, disable the "one layer" mode. - void SetSelectionSpan(const int lower_val, const int higher_val); - void SetMaxValue(const int max_value); - void SetKoefForLabels(const double koef) { m_label_koef = koef; } - void SetSliderValues(const std::vector& values) { m_values = values; } - void ChangeOneLayerLock(); - Slic3r::CustomGCode::Info GetTicksValues() const; - void SetTicksValues(const Slic3r::CustomGCode::Info &custom_gcode_per_print_z); - void EnableTickManipulation(bool enable = true) { m_is_enabled_tick_manipulation = enable; } - void DisableTickManipulation() { EnableTickManipulation(false); } - - void SetManipulationMode(t_mode mode) { m_mode = mode; } - t_mode GetManipulationMode() const { return m_mode; } - - void SetModeAndOnlyExtruder(const bool is_one_extruder_printed_model, const int only_extruder) - { - m_mode = !is_one_extruder_printed_model ? t_mode::MultiExtruder : - only_extruder < 0 ? t_mode::SingleExtruder : - t_mode::MultiAsSingle; - m_only_extruder = only_extruder; - } - - bool is_horizontal() const { return m_style == wxSL_HORIZONTAL; } - bool is_one_layer() const { return m_is_one_layer; } - bool is_lower_at_min() const { return m_lower_value == m_min_value; } - bool is_higher_at_max() const { return m_higher_value == m_max_value; } - bool is_full_span() const { return this->is_lower_at_min() && this->is_higher_at_max(); } - - void OnPaint(wxPaintEvent& ) { render();} - void OnLeftDown(wxMouseEvent& event); - void OnMotion(wxMouseEvent& event); - void OnLeftUp(wxMouseEvent& event); - void OnEnterWin(wxMouseEvent& event) { enter_window(event, true); } - void OnLeaveWin(wxMouseEvent& event) { enter_window(event, false); } - void OnWheel(wxMouseEvent& event); - void OnKeyDown(wxKeyEvent &event); - void OnKeyUp(wxKeyEvent &event); - void OnChar(wxKeyEvent &event); - void OnRightDown(wxMouseEvent& event); - void OnRightUp(wxMouseEvent& event); - - 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 edit_extruder_sequence(); - - struct TICK_CODE - { - bool operator<(const TICK_CODE& other) const { return other.tick > this->tick; } - bool operator>(const TICK_CODE& other) const { return other.tick < this->tick; } - - int tick = 0; - std::string gcode = Slic3r::ColorChangeCode; - int extruder = 0; - std::string color; - }; - -protected: - - void render(); - void draw_focus_rect(); - void draw_action_icon(wxDC& dc, const wxPoint pt_beg, const wxPoint pt_end); - void draw_scroll_line(wxDC& dc, const int lower_pos, const int higher_pos); - void draw_thumb(wxDC& dc, const wxCoord& pos_coord, const SelectedSlider& selection); - void draw_thumbs(wxDC& dc, const wxCoord& lower_pos, const wxCoord& higher_pos); - void draw_ticks(wxDC& dc); - void draw_colored_band(wxDC& dc); - void draw_one_layer_icon(wxDC& dc); - void draw_revert_icon(wxDC& dc); - void draw_cog_icon(wxDC &dc); - void draw_thumb_item(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection); - void draw_info_line_with_icon(wxDC& dc, const wxPoint& pos, SelectedSlider selection); - void draw_thumb_text(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection) const; - - void update_thumb_rect(const wxCoord& begin_x, const wxCoord& begin_y, const SelectedSlider& selection); - void detect_selected_slider(const wxPoint& pt); - void correct_lower_value(); - void correct_higher_value(); - void move_current_thumb(const bool condition); - void enter_window(wxMouseEvent& event, const bool enter); - -private: - - bool is_point_in_rect(const wxPoint& pt, const wxRect& rect); - int is_point_near_tick(const wxPoint& pt); - - double get_scroll_step(); - wxString get_label(const SelectedSlider& selection) const; - void get_lower_and_higher_position(int& lower_pos, int& higher_pos); - int get_value_from_position(const wxCoord x, const wxCoord y); - wxCoord get_position_from_value(const int value); - wxSize get_size(); - void get_size(int *w, int *h); - double get_double_value(const SelectedSlider& selection); - wxString get_tooltip(IconFocus icon_focus); - - std::string get_color_for_tool_change_tick(std::set::const_iterator it) const; - std::string get_color_for_color_change_tick(std::set::const_iterator it) const; - int get_extruder_for_tick(int tick); - 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*, bool switch_current_code = false); - void append_add_color_change_menu_item(wxMenu*, bool switch_current_code = false); - - bool is_osx { false }; - wxFont m_font; - int m_min_value; - int m_max_value; - int m_lower_value; - int m_higher_value; - ScalableBitmap m_bmp_thumb_higher; - ScalableBitmap m_bmp_thumb_lower; - ScalableBitmap m_bmp_add_tick_on; - ScalableBitmap m_bmp_add_tick_off; - ScalableBitmap m_bmp_del_tick_on; - ScalableBitmap m_bmp_del_tick_off; - ScalableBitmap m_bmp_one_layer_lock_on; - ScalableBitmap m_bmp_one_layer_lock_off; - ScalableBitmap m_bmp_one_layer_unlock_on; - ScalableBitmap m_bmp_one_layer_unlock_off; - ScalableBitmap m_bmp_revert; - ScalableBitmap m_bmp_cog; - SelectedSlider m_selection; - bool m_is_left_down = false; - bool m_is_right_down = false; - bool m_is_one_layer = false; - bool m_is_focused = false; - bool m_is_action_icon_focesed = false; - bool m_is_one_layer_icon_focesed = false; - bool m_is_enabled_tick_manipulation = true; - bool m_show_context_menu = false; - bool m_show_edit_menu = 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; - int m_only_extruder = -1; - - wxRect m_rect_lower_thumb; - wxRect m_rect_higher_thumb; - wxRect m_rect_tick_action; - wxRect m_rect_one_layer_icon; - wxRect m_rect_revert_icon; - wxRect m_rect_cog_icon; - wxSize m_thumb_size; - int m_tick_icon_dim; - int m_lock_icon_dim; - int m_revert_icon_dim; - int m_cog_icon_dim; - long m_style; - float m_label_koef = 1.0; - -// control's view variables - wxCoord SLIDER_MARGIN; // margin around slider - - wxPen DARK_ORANGE_PEN; - wxPen ORANGE_PEN; - wxPen LIGHT_ORANGE_PEN; - - wxPen DARK_GREY_PEN; - wxPen GREY_PEN; - wxPen LIGHT_GREY_PEN; - - std::vector m_line_pens; - std::vector m_segm_pens; - std::vector m_values; - - 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); - bool switch_code_for_tick (std::set::iterator it, const std::string& code_to, const int extruder); - 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; - - std::string get_color_for_tick(TICK_CODE tick, const std::string& code, const int extruder); - } - m_ticks; - -public: - struct ExtrudersSequence - { - bool is_mm_intervals = true; - double interval_by_mm = 3.0; - int interval_by_layers = 10; - std::vector extruders = { 0 }; - - bool operator==(const ExtrudersSequence& other) const - { - return (other.is_mm_intervals == this->is_mm_intervals ) && - (other.interval_by_mm == this->interval_by_mm ) && - (other.interval_by_layers == this->interval_by_layers ) && - (other.extruders == this->extruders ) ; - } - bool operator!=(const ExtrudersSequence& other) const - { - return (other.is_mm_intervals != this->is_mm_intervals ) && - (other.interval_by_mm != this->interval_by_mm ) && - (other.interval_by_layers != this->interval_by_layers ) && - (other.extruders != this->extruders ) ; - } - - void add_extruder(size_t pos) - { - extruders.insert(extruders.begin() + pos+1, size_t(0)); - } - - void delete_extruder(size_t pos) - { - if (extruders.size() == 1) - return;// last item can't be deleted - extruders.erase(extruders.begin() + pos); - } - } - m_extruders_sequence; -}; - - // ---------------------------------------------------------------------------- // LockButton // ----------------------------------------------------------------------------