Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/prusa3d/PrusaSlicer.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYuSanka <yusanka@gmail.com>2020-01-23 17:07:31 +0300
committerYuSanka <yusanka@gmail.com>2020-01-23 18:11:21 +0300
commit5ff8ae955be3dfcae2387fe04386ee40ec868598 (patch)
tree495b376960bdf9dac2eac31275100ea44813ff38
parent1ed313ab7983bed9a1911da153ce2e3e0b473d6f (diff)
Implemented editing for extruder of existing ToolChangeCode tick
+ Code refactoring: DoubleSlider is extracted from wxExtensions
-rw-r--r--resources/localization/list.txt1
-rw-r--r--src/libslic3r/CustomGCode.hpp5
-rw-r--r--src/libslic3r/GCodeWriter.cpp1
-rw-r--r--src/libslic3r/GCodeWriter.hpp5
-rw-r--r--src/slic3r/CMakeLists.txt2
-rw-r--r--src/slic3r/GUI/DoubleSlider.cpp1766
-rw-r--r--src/slic3r/GUI/DoubleSlider.hpp337
-rw-r--r--src/slic3r/GUI/ExtruderSequenceDialog.hpp2
-rw-r--r--src/slic3r/GUI/GLCanvas3D.cpp3
-rw-r--r--src/slic3r/GUI/GUI_Preview.cpp6
-rw-r--r--src/slic3r/GUI/GUI_Preview.hpp7
-rw-r--r--src/slic3r/GUI/Plater.cpp1
-rw-r--r--src/slic3r/GUI/wxExtensions.cpp1721
-rw-r--r--src/slic3r/GUI/wxExtensions.hpp312
14 files changed, 2128 insertions, 2041 deletions
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 <algorithm>
#include <iomanip>
#include <iostream>
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 <wx/button.h>
+#include <wx/dialog.h>
+#include <wx/sizer.h>
+#include <wx/slider.h>
+#include <wx/menu.h>
+#include <wx/bmpcbox.h>
+#include <wx/statline.h>
+#include <wx/dcclient.h>
+#include <wx/numformatter.h>
+#include <wx/colordlg.h>
+
+#include <cmath>
+#include <boost/algorithm/string/replace.hpp>
+
+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<t_custom_code>& 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<t_custom_code>& 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<TickCode>::const_iterator it) const
+{
+ const int current_extruder = it->extruder == 0 ? std::max<int>(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<TickCode>::const_iterator it) const
+{
+ const int def_extruder = std::max<int>(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<int>(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<TickCode>::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<int, 2> 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<int> 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<int, 2> Control::get_active_extruders_for_tick(int tick) const
+{
+ int default_initial_extruder = m_mode == t_mode::MultiAsSingle ? std::max<int>(1, m_only_extruder) : 1;
+ std::array<int, 2> 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<int> 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<int> 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<unsigned>& 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<int> used_extruders;
+ const std::set<TickCode>& 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<TickCode>::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<int>(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<TickCode>::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<std::string> 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<std::string> 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<TickCode>::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<TickCode>::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 <wx/wx.h>
+#include <wx/window.h>
+#include <wx/control.h>
+#include <wx/dc.h>
+#include <wx/slider.h>
+
+#include <vector>
+#include <set>
+
+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<TickCode> 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<TickCode>::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<TickCode>::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<size_t> 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<double>& 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<TickCode>::const_iterator it) const;
+ std::string get_color_for_color_change_tick(std::set<TickCode>::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<int, 2> 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<int> 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<double> 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<wxPen*> m_line_pens;
+ std::vector<wxPen*> 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 <float.h>
#include <algorithm>
#include <cmath>
+#include "DoubleSlider.hpp"
#if ENABLE_RENDER_STATISTICS
#include <chrono>
#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 <wx/notebook.h>
#include <wx/glcanvas.h>
@@ -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 <wx/glcanvas.h> // 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<t_custom_code>& 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<t_custom_code>& 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<TICK_CODE>::const_iterator it) const
-{
- const int current_extruder = it->extruder == 0 ? std::max<int>(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<TICK_CODE>::const_iterator it) const
-{
- const int def_extruder = std::max<int>(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<int>(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<TICK_CODE>::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<int>(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<int> 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<int> 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<int> 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<unsigned>& 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<int> used_extruders;
- const std::set<TICK_CODE>& 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<TICK_CODE>::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<int>(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<TICK_CODE>::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<std::string> 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<std::string> 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<TICK_CODE>::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<TICK_CODE>::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 <wx/combo.h>
#include <wx/dataview.h>
#include <wx/dc.h>
-#include <wx/collpane.h>
#include <wx/wupdlock.h>
#include <wx/button.h>
#include <wx/sizer.h>
-#include <wx/slider.h>
#include <wx/menu.h>
#include <wx/wx.h>
#include <vector>
#include <set>
#include <functional>
-#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<void(wxCommandEvent& event)> cb, wxEvtHandler* event_handler);
+void enable_menu_item(wxUpdateUIEvent& evt, std::function<bool()> 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<int>& 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);
@@ -743,311 +742,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<double>& 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<TICK_CODE>::const_iterator it) const;
- std::string get_color_for_color_change_tick(std::set<TICK_CODE>::const_iterator it) const;
- int get_extruder_for_tick(int tick);
- std::set<int> 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<wxPen*> m_line_pens;
- std::vector<wxPen*> m_segm_pens;
- std::vector<double> m_values;
-
- struct TICK_CODE_INFO
- {
- std::set<TICK_CODE> 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<TICK_CODE>::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<TICK_CODE>::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<size_t> 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
// ----------------------------------------------------------------------------