1 #include <algorithm>
2 #include <sstream>
3 #include "WipeTowerDialog.hpp"
4 #include "BitmapCache.hpp"
5 #include "GUI.hpp"
6 #include "I18N.hpp"
7 #include "GUI_App.hpp"
8 
9 #include <wx/sizer.h>
10 
scale(const int val)11 int scale(const int val) { return val * Slic3r::GUI::wxGetApp().em_unit(); }
ITEM_WIDTH()12 int ITEM_WIDTH() { return scale(6); }
13 
RammingDialog(wxWindow * parent,const std::string & parameters)14 RammingDialog::RammingDialog(wxWindow* parent,const std::string& parameters)
15 : wxDialog(parent, wxID_ANY, _(L("Ramming customization")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE/* | wxRESIZE_BORDER*/)
16 {
17     m_panel_ramming  = new RammingPanel(this,parameters);
18 
19     // Not found another way of getting the background colours of RammingDialog, RammingPanel and Chart correct than setting
20     // them all explicitely. Reading the parent colour yielded colour that didn't really match it, no wxSYS_COLOUR_... matched
21     // colour used for the dialog. Same issue (and "solution") here : https://forums.wxwidgets.org/viewtopic.php?f=1&t=39608
22     // Whoever can fix this, feel free to do so.
23     this->           SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_FRAMEBK));
24     m_panel_ramming->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_FRAMEBK));
25     m_panel_ramming->Show(true);
26     this->Show();
27 
28     auto main_sizer = new wxBoxSizer(wxVERTICAL);
29     main_sizer->Add(m_panel_ramming, 1, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, 5);
30     main_sizer->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxALIGN_CENTER_HORIZONTAL | wxTOP | wxBOTTOM, 10);
31     SetSizer(main_sizer);
32     main_sizer->SetSizeHints(this);
33 
34     this->Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent& e) { EndModal(wxCANCEL); });
35 
36     this->Bind(wxEVT_BUTTON,[this](wxCommandEvent&) {
37         m_output_data = m_panel_ramming->get_parameters();
38         EndModal(wxID_OK);
39         },wxID_OK);
40     this->Show();
41     wxMessageDialog(this,_(L("Ramming denotes the rapid extrusion just before a tool change in a single-extruder MM printer. Its purpose is to "
42                    "properly shape the end of the unloaded filament so it does not prevent insertion of the new filament and can itself "
43                    "be reinserted later. This phase is important and different materials can require different extrusion speeds to get "
44                    "the good shape. For this reason, the extrusion rates during ramming are adjustable.\n\nThis is an expert-level "
45                    "setting, incorrect adjustment will likely lead to jams, extruder wheel grinding into filament etc.")),_(L("Warning")),wxOK|wxICON_EXCLAMATION).ShowModal();
46 }
47 
48 
49 
50 
51 
RammingPanel(wxWindow * parent,const std::string & parameters)52 RammingPanel::RammingPanel(wxWindow* parent, const std::string& parameters)
53 : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize/*,wxPoint(50,50), wxSize(800,350),wxBORDER_RAISED*/)
54 {
55 	auto sizer_chart = new wxBoxSizer(wxVERTICAL);
56 	auto sizer_param = new wxBoxSizer(wxVERTICAL);
57 
58 	std::stringstream stream{ parameters };
59 	stream >> m_ramming_line_width_multiplicator >> m_ramming_step_multiplicator;
60 	int ramming_speed_size = 0;
61 	float dummy = 0.f;
62 	while (stream >> dummy)
63 		++ramming_speed_size;
64 	stream.clear();
65 	stream.get();
66 
67 	std::vector<std::pair<float, float>> buttons;
68 	float x = 0.f;
69 	float y = 0.f;
70 	while (stream >> x >> y)
71 		buttons.push_back(std::make_pair(x, y));
72 
73 	m_chart = new Chart(this, wxRect(scale(1),scale(1),scale(48),scale(36)), buttons, ramming_speed_size, 0.25f, scale(1));
74     m_chart->SetBackgroundColour(parent->GetBackgroundColour()); // see comment in RammingDialog constructor
75  	sizer_chart->Add(m_chart, 0, wxALL, 5);
76 
77     m_widget_time						= new wxSpinCtrlDouble(this,wxID_ANY,wxEmptyString,wxDefaultPosition,wxSize(ITEM_WIDTH(), -1),wxSP_ARROW_KEYS,0.,5.0,3.,0.5);
78     m_widget_volume							  = new wxSpinCtrl(this,wxID_ANY,wxEmptyString,wxDefaultPosition,wxSize(ITEM_WIDTH(), -1),wxSP_ARROW_KEYS,0,10000,0);
79     m_widget_ramming_line_width_multiplicator = new wxSpinCtrl(this,wxID_ANY,wxEmptyString,wxDefaultPosition,wxSize(ITEM_WIDTH(), -1),wxSP_ARROW_KEYS,10,200,100);
80     m_widget_ramming_step_multiplicator		  = new wxSpinCtrl(this,wxID_ANY,wxEmptyString,wxDefaultPosition,wxSize(ITEM_WIDTH(), -1),wxSP_ARROW_KEYS,10,200,100);
81 
82 	auto gsizer_param = new wxFlexGridSizer(2, 5, 15);
83 	gsizer_param->Add(new wxStaticText(this, wxID_ANY, wxString(_(L("Total ramming time")) + " (" + _(L("s")) + "):")), 0, wxALIGN_CENTER_VERTICAL);
84 	gsizer_param->Add(m_widget_time);
85 	gsizer_param->Add(new wxStaticText(this, wxID_ANY, wxString(_(L("Total rammed volume")) + " (" + _(L("mm")) + wxString("³):", wxConvUTF8))), 0, wxALIGN_CENTER_VERTICAL);
86 	gsizer_param->Add(m_widget_volume);
87 	gsizer_param->AddSpacer(20);
88 	gsizer_param->AddSpacer(20);
89 	gsizer_param->Add(new wxStaticText(this, wxID_ANY, wxString(_(L("Ramming line width")) + " (%):")), 0, wxALIGN_CENTER_VERTICAL);
90 	gsizer_param->Add(m_widget_ramming_line_width_multiplicator);
91 	gsizer_param->Add(new wxStaticText(this, wxID_ANY, wxString(_(L("Ramming line spacing")) + " (%):")), 0, wxALIGN_CENTER_VERTICAL);
92 	gsizer_param->Add(m_widget_ramming_step_multiplicator);
93 
94 	sizer_param->Add(gsizer_param, 0, wxTOP, scale(10));
95 
96     m_widget_time->SetValue(m_chart->get_time());
97     m_widget_time->SetDigits(2);
98     m_widget_volume->SetValue(m_chart->get_volume());
99     m_widget_volume->Disable();
100     m_widget_ramming_line_width_multiplicator->SetValue(m_ramming_line_width_multiplicator);
101     m_widget_ramming_step_multiplicator->SetValue(m_ramming_step_multiplicator);
102 
103     m_widget_ramming_step_multiplicator->Bind(wxEVT_TEXT,[this](wxCommandEvent&) { line_parameters_changed(); });
104     m_widget_ramming_line_width_multiplicator->Bind(wxEVT_TEXT,[this](wxCommandEvent&) { line_parameters_changed(); });
105 
106 	auto sizer = new wxBoxSizer(wxHORIZONTAL);
107 	sizer->Add(sizer_chart, 0, wxALL, 5);
108 	sizer->Add(sizer_param, 0, wxALL, 10);
109 
110 	sizer->SetSizeHints(this);
111 	SetSizer(sizer);
112 
113     m_widget_time->Bind(wxEVT_TEXT,[this](wxCommandEvent&) {m_chart->set_xy_range(m_widget_time->GetValue(),-1);});
114     m_widget_time->Bind(wxEVT_CHAR,[](wxKeyEvent&){});      // do nothing - prevents the user to change the value
115     m_widget_volume->Bind(wxEVT_CHAR,[](wxKeyEvent&){});    // do nothing - prevents the user to change the value
116     Bind(EVT_WIPE_TOWER_CHART_CHANGED,[this](wxCommandEvent&) {m_widget_volume->SetValue(m_chart->get_volume()); m_widget_time->SetValue(m_chart->get_time());} );
117     Refresh(true); // erase background
118 }
119 
line_parameters_changed()120 void RammingPanel::line_parameters_changed() {
121     m_ramming_line_width_multiplicator = m_widget_ramming_line_width_multiplicator->GetValue();
122     m_ramming_step_multiplicator = m_widget_ramming_step_multiplicator->GetValue();
123 }
124 
get_parameters()125 std::string RammingPanel::get_parameters()
126 {
127     std::vector<float> speeds = m_chart->get_ramming_speed(0.25f);
128     std::vector<std::pair<float,float>> buttons = m_chart->get_buttons();
129     std::stringstream stream;
130     stream << m_ramming_line_width_multiplicator << " " << m_ramming_step_multiplicator;
131     for (const float& speed_value : speeds)
132         stream << " " << speed_value;
133     stream << "|";
134     for (const auto& button : buttons)
135         stream << " " << button.first << " " << button.second;
136     return stream.str();
137 }
138 
139 
140 // Parent dialog for purging volume adjustments - it fathers WipingPanel widget (that contains all controls) and a button to toggle simple/advanced mode:
WipingDialog(wxWindow * parent,const std::vector<float> & matrix,const std::vector<float> & extruders,const std::vector<std::string> & extruder_colours)141 WipingDialog::WipingDialog(wxWindow* parent, const std::vector<float>& matrix, const std::vector<float>& extruders, const std::vector<std::string>& extruder_colours)
142 : wxDialog(parent, wxID_ANY, _(L("Wipe tower - Purging volume adjustment")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE/* | wxRESIZE_BORDER*/)
143 {
144     auto widget_button = new wxButton(this,wxID_ANY,"-",wxPoint(0,0),wxDefaultSize);
145     m_panel_wiping  = new WipingPanel(this,matrix,extruders, extruder_colours, widget_button);
146 
147     auto main_sizer = new wxBoxSizer(wxVERTICAL);
148 
149 	// set min sizer width according to extruders count
150 	const auto sizer_width = (int)((sqrt(matrix.size()) + 2.8)*ITEM_WIDTH());
151 	main_sizer->SetMinSize(wxSize(sizer_width, -1));
152 
153     main_sizer->Add(m_panel_wiping, 0, wxEXPAND | wxALL, 5);
154 	main_sizer->Add(widget_button, 0, wxALIGN_CENTER_HORIZONTAL | wxCENTER | wxBOTTOM, 5);
155     main_sizer->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxALIGN_CENTER_HORIZONTAL | wxBOTTOM, 10);
156     SetSizer(main_sizer);
157     main_sizer->SetSizeHints(this);
158 
159     this->Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent& e) { EndModal(wxCANCEL); });
160 
161     this->Bind(wxEVT_BUTTON,[this](wxCommandEvent&) {                 // if OK button is clicked..
162         m_output_matrix    = m_panel_wiping->read_matrix_values();    // ..query wiping panel and save returned values
163         m_output_extruders = m_panel_wiping->read_extruders_values(); // so they can be recovered later by calling get_...()
164         EndModal(wxID_OK);
165         },wxID_OK);
166 
167     this->Show();
168 }
169 
170 // This function allows to "play" with sizers parameters (like align or border)
format_sizer(wxSizer * sizer,wxPanel * page,wxGridSizer * grid_sizer,const wxString & info,const wxString & table_title,int table_lshift)171 void WipingPanel::format_sizer(wxSizer* sizer, wxPanel* page, wxGridSizer* grid_sizer, const wxString& info, const wxString& table_title, int table_lshift/*=0*/)
172 {
173     wxSize text_size = GetTextExtent(info);
174     auto info_str = new wxStaticText(page, wxID_ANY, info ,wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER);
175     info_str->Wrap(int(0.6*text_size.x));
176 	sizer->Add( info_str, 0, wxEXPAND);
177 	auto table_sizer = new wxBoxSizer(wxVERTICAL);
178 	sizer->Add(table_sizer, 0, wxALIGN_CENTER | wxCENTER, table_lshift);
179 	table_sizer->Add(new wxStaticText(page, wxID_ANY, table_title), 0, wxALIGN_CENTER | wxTOP, 50);
180 	table_sizer->Add(grid_sizer, 0, wxALIGN_CENTER | wxTOP, 10);
181 }
182 
183 // This panel contains all control widgets for both simple and advanced mode (these reside in separate sizers)
WipingPanel(wxWindow * parent,const std::vector<float> & matrix,const std::vector<float> & extruders,const std::vector<std::string> & extruder_colours,wxButton * widget_button)184 WipingPanel::WipingPanel(wxWindow* parent, const std::vector<float>& matrix, const std::vector<float>& extruders, const std::vector<std::string>& extruder_colours, wxButton* widget_button)
185 : wxPanel(parent,wxID_ANY, wxDefaultPosition, wxDefaultSize/*,wxBORDER_RAISED*/)
186 {
187     m_widget_button = widget_button;    // pointer to the button in parent dialog
188     m_widget_button->Bind(wxEVT_BUTTON,[this](wxCommandEvent&){ toggle_advanced(true); });
189 
190     m_number_of_extruders = (int)(sqrt(matrix.size())+0.001);
191 
192     for (const std::string& color : extruder_colours) {
193         unsigned char rgb[3];
194         Slic3r::GUI::BitmapCache::parse_color(color, rgb);
195         m_colours.push_back(wxColor(rgb[0], rgb[1], rgb[2]));
196     }
197 
198 	// Create two switched panels with their own sizers
199     m_sizer_simple          = new wxBoxSizer(wxVERTICAL);
200     m_sizer_advanced        = new wxBoxSizer(wxVERTICAL);
201 	m_page_simple			= new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
202 	m_page_advanced			= new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
203 	m_page_simple->SetSizer(m_sizer_simple);
204 	m_page_advanced->SetSizer(m_sizer_advanced);
205 
206     auto gridsizer_simple   = new wxGridSizer(3, 5, 10);
207     m_gridsizer_advanced = new wxGridSizer(m_number_of_extruders+1, 5, 1);
208 
209 	// First create controls for advanced mode and assign them to m_page_advanced:
210 	for (unsigned int i = 0; i < m_number_of_extruders; ++i) {
211 		edit_boxes.push_back(std::vector<wxTextCtrl*>(0));
212 
213 		for (unsigned int j = 0; j < m_number_of_extruders; ++j) {
214 			edit_boxes.back().push_back(new wxTextCtrl(m_page_advanced, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(ITEM_WIDTH(), -1)));
215 			if (i == j)
216 				edit_boxes[i][j]->Disable();
217 			else
218 				edit_boxes[i][j]->SetValue(wxString("") << int(matrix[m_number_of_extruders*j + i]));
219 		}
220 	}
221 
222     const int clr_icon_side = edit_boxes.front().front()->GetSize().y;
223     const auto icon_size = wxSize(clr_icon_side, clr_icon_side);
224 
225 	m_gridsizer_advanced->Add(new wxStaticText(m_page_advanced, wxID_ANY, wxString("")));
226 	for (unsigned int i = 0; i < m_number_of_extruders; ++i) {
227         auto hsizer = new wxBoxSizer(wxHORIZONTAL);
228         hsizer->AddSpacer(20);
229         hsizer->Add(new wxStaticText(m_page_advanced, wxID_ANY, wxString("") << i + 1), 0, wxALIGN_CENTER);
230         wxWindow* w = new wxWindow(m_page_advanced, wxID_ANY, wxDefaultPosition, icon_size, wxBORDER_SIMPLE);
231         w->SetCanFocus(false);
232         w->SetBackgroundColour(m_colours[i]);
233         hsizer->AddStretchSpacer();
234         hsizer->Add(w);
235 		m_gridsizer_advanced->Add(hsizer, 1, wxEXPAND);
236     }
237 	for (unsigned int i = 0; i < m_number_of_extruders; ++i) {
238         auto hsizer = new wxBoxSizer(wxHORIZONTAL);
239         wxWindow* w = new wxWindow(m_page_advanced, wxID_ANY, wxDefaultPosition, icon_size, wxBORDER_SIMPLE);
240         w->SetCanFocus(false);
241         w->SetBackgroundColour(m_colours[i]);
242         hsizer->AddSpacer(20);
243         hsizer->Add(new wxStaticText(m_page_advanced, wxID_ANY, wxString("") << i + 1), 0, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL);
244         hsizer->AddStretchSpacer();
245         hsizer->Add(w);
246         m_gridsizer_advanced->Add(hsizer, 1, wxEXPAND);
247 
248     for (unsigned int j = 0; j < m_number_of_extruders; ++j)
249         m_gridsizer_advanced->Add(edit_boxes[j][i], 0);
250     }
251 
252 	// collect and format sizer
253 	format_sizer(m_sizer_advanced, m_page_advanced, m_gridsizer_advanced,
254 		_(L("Here you can adjust required purging volume (mm³) for any given pair of tools.")),
255 		_(L("Extruder changed to")));
256 
257 	// Hide preview page before new page creating
258 	// It allows to do that from a beginning of the main panel
259 	m_page_advanced->Hide();
260 
261 	// Now the same for simple mode:
262 	gridsizer_simple->Add(new wxStaticText(m_page_simple, wxID_ANY, wxString("")), 0, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL);
263 	gridsizer_simple->Add(new wxStaticText(m_page_simple, wxID_ANY, wxString(_(L("unloaded")))), 0, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL);
264     gridsizer_simple->Add(new wxStaticText(m_page_simple,wxID_ANY,wxString(_(L("loaded")))), 0, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL);
265 
266 	for (unsigned int i=0;i<m_number_of_extruders;++i) {
267         m_old.push_back(new wxSpinCtrl(m_page_simple,wxID_ANY,wxEmptyString,wxDefaultPosition, wxSize(ITEM_WIDTH(), -1),wxSP_ARROW_KEYS|wxALIGN_RIGHT,0,300,extruders[2*i]));
268         m_new.push_back(new wxSpinCtrl(m_page_simple,wxID_ANY,wxEmptyString,wxDefaultPosition, wxSize(ITEM_WIDTH(), -1),wxSP_ARROW_KEYS|wxALIGN_RIGHT,0,300,extruders[2*i+1]));
269 
270         auto hsizer = new wxBoxSizer(wxHORIZONTAL);
271         wxWindow* w = new wxWindow(m_page_simple, wxID_ANY, wxDefaultPosition, icon_size, wxBORDER_SIMPLE);
272         w->SetCanFocus(false);
273         w->SetBackgroundColour(m_colours[i]);
274         hsizer->Add(w, wxALIGN_CENTER_VERTICAL);
275         hsizer->AddSpacer(10);
276         hsizer->Add(new wxStaticText(m_page_simple, wxID_ANY, wxString(_(L("Tool #"))) << i + 1 << ": "), 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
277 
278         gridsizer_simple->Add(hsizer, 1, wxEXPAND | wxALIGN_CENTER_VERTICAL);
279         gridsizer_simple->Add(m_old.back(),0);
280         gridsizer_simple->Add(m_new.back(),0);
281 	}
282 
283 	// collect and format sizer
284 	format_sizer(m_sizer_simple, m_page_simple, gridsizer_simple,
285 		_(L("Total purging volume is calculated by summing two values below, depending on which tools are loaded/unloaded.")),
286 		_(L("Volume to purge (mm³) when the filament is being")), 50);
287 
288 	m_sizer = new wxBoxSizer(wxVERTICAL);
289 	m_sizer->Add(m_page_simple, 0, wxEXPAND | wxALL, 25);
290 	m_sizer->Add(m_page_advanced, 0, wxEXPAND | wxALL, 25);
291 
292 	m_sizer->SetSizeHints(this);
293 	SetSizer(m_sizer);
294 
295     toggle_advanced(); // to show/hide what is appropriate
296 
297     m_page_advanced->Bind(wxEVT_PAINT,[this](wxPaintEvent&) {
298                                               wxPaintDC dc(m_page_advanced);
299                                               int y_pos = 0.5 * (edit_boxes[0][0]->GetPosition().y + edit_boxes[0][edit_boxes.size()-1]->GetPosition().y + edit_boxes[0][edit_boxes.size()-1]->GetSize().y);
300                                               wxString label = _(L("From"));
301                                               int text_width = 0;
302                                               int text_height = 0;
303                                               dc.GetTextExtent(label,&text_width,&text_height);
304                                               int xpos = m_gridsizer_advanced->GetPosition().x;
305                                               dc.DrawRotatedText(label,xpos-text_height,y_pos + text_width/2.f,90);
306     });
307 }
308 
309 
310 
311 
312 // Reads values from the (advanced) wiping matrix:
read_matrix_values()313 std::vector<float> WipingPanel::read_matrix_values() {
314     if (!m_advanced)
315         fill_in_matrix();
316     std::vector<float> output;
317     for (unsigned int i=0;i<m_number_of_extruders;++i) {
318         for (unsigned int j=0;j<m_number_of_extruders;++j) {
319             double val = 0.;
320             edit_boxes[j][i]->GetValue().ToDouble(&val);
321             output.push_back((float)val);
322         }
323     }
324     return output;
325 }
326 
327 // Reads values from simple mode to save them for next time:
read_extruders_values()328 std::vector<float> WipingPanel::read_extruders_values() {
329     std::vector<float> output;
330     for (unsigned int i=0;i<m_number_of_extruders;++i) {
331         output.push_back(m_old[i]->GetValue());
332         output.push_back(m_new[i]->GetValue());
333     }
334     return output;
335 }
336 
337 // This updates the "advanced" matrix based on values from "simple" mode
fill_in_matrix()338 void WipingPanel::fill_in_matrix() {
339     for (unsigned i=0;i<m_number_of_extruders;++i) {
340         for (unsigned j=0;j<m_number_of_extruders;++j) {
341             if (i==j) continue;
342                 edit_boxes[j][i]->SetValue(wxString("")<< (m_old[i]->GetValue() + m_new[j]->GetValue()));
343         }
344     }
345 }
346 
347 
348 
349 // Function to check if simple and advanced settings are matching
advanced_matches_simple()350 bool WipingPanel::advanced_matches_simple() {
351     for (unsigned i=0;i<m_number_of_extruders;++i) {
352         for (unsigned j=0;j<m_number_of_extruders;++j) {
353             if (i==j) continue;
354             if (edit_boxes[j][i]->GetValue() != (wxString("")<< (m_old[i]->GetValue() + m_new[j]->GetValue())))
355                 return false;
356         }
357     }
358     return true;
359 }
360 
361 
362 // Switches the dialog from simple to advanced mode and vice versa
toggle_advanced(bool user_action)363 void WipingPanel::toggle_advanced(bool user_action) {
364     if (m_advanced && !advanced_matches_simple() && user_action) {
365         if (wxMessageDialog(this,wxString(_(L("Switching to simple settings will discard changes done in the advanced mode!\n\nDo you want to proceed?"))),
366                             wxString(_(L("Warning"))),wxYES_NO|wxICON_EXCLAMATION).ShowModal() != wxID_YES)
367             return;
368     }
369     if (user_action)
370         m_advanced = !m_advanced;                // user demands a change -> toggle
371     else
372         m_advanced = !advanced_matches_simple(); // if called from constructor, show what is appropriate
373 
374     (m_advanced ? m_page_advanced : m_page_simple)->Show();
375 	(!m_advanced ? m_page_advanced : m_page_simple)->Hide();
376 
377     m_widget_button->SetLabel(m_advanced ? _(L("Show simplified settings")) : _(L("Show advanced settings")));
378     if (m_advanced)
379         if (user_action) fill_in_matrix();  // otherwise keep values loaded from config
380 
381    m_sizer->Layout();
382    Refresh();
383 }
384