From 40569787319264acb5799479618866111db98aa3 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 26 Jan 2018 01:44:34 +0100 Subject: [PATCH] Implementation of C++ to Perl callbacks from Browse & Test buttons. --- lib/Slic3r/GUI/MainFrame.pm | 61 ++++++++++++++++++++++++++++++++-- xs/src/slic3r/GUI/GUI.cpp | 23 ++++++++----- xs/src/slic3r/GUI/GUI.hpp | 6 ++-- xs/src/slic3r/GUI/Tab.cpp | 53 ++++++++++++++++------------- xs/src/slic3r/GUI/Tab.hpp | 27 +++++++++++---- xs/src/slic3r/GUI/TabIface.cpp | 3 +- xs/src/slic3r/GUI/TabIface.hpp | 1 + xs/xsp/GUI.xsp | 10 ++++-- xs/xsp/GUI_Tab.xsp | 1 + 9 files changed, 140 insertions(+), 45 deletions(-) diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm index 642ab2627..1d1cf1479 100644 --- a/lib/Slic3r/GUI/MainFrame.pm +++ b/lib/Slic3r/GUI/MainFrame.pm @@ -23,6 +23,10 @@ our $last_config; our $VALUE_CHANGE_EVENT = Wx::NewEventType; # 2) To inform about a preset selection change or a "modified" status change. our $PRESETS_CHANGED_EVENT = Wx::NewEventType; +# 3) To inform about a click on Browse button +our $BUTTON_BROWSE_EVENT = Wx::NewEventType; +# 4) To inform about a click on Test button +our $BUTTON_TEST_EVENT = Wx::NewEventType; sub new { my ($class, %params) = @_; @@ -113,7 +117,6 @@ sub _init_tabpanel { } } $self->{options_tabs} = {}; - for my $tab_name (qw(print filament printer)) { my $tab; $tab = $self->{options_tabs}{$tab_name} = ("Slic3r::GUI::Tab::" . ucfirst $tab_name)->new( @@ -203,7 +206,61 @@ sub _init_tabpanel { $self->{plater}->on_config_change($tab->get_config); } }); - Slic3r::GUI::create_preset_tabs(wxTheApp->{preset_bundle}, wxTheApp->{app_config}, $self->{no_controller}, $VALUE_CHANGE_EVENT, $PRESETS_CHANGED_EVENT); + # The following event is emited by the C++ Tab implementation , + # when the Browse button was clicked + EVT_COMMAND($self, -1, $BUTTON_BROWSE_EVENT, sub { + my ($self, $event) = @_; + my $msg = $event->GetString; + print "BUTTON_BROWSE_EVENT: ", $msg, "\n"; + + # look for devices + my $entries; + { + my $res = Net::Bonjour->new('http'); + $res->discover; + $entries = [ $res->entries ]; + } + if (@{$entries}) { + my $dlg = Slic3r::GUI::BonjourBrowser->new($self, $entries); + my $tab = Slic3r::GUI::get_preset_tab("printer"); + $tab->load_key_value('octoprint_host', $dlg->GetValue . ":" . $dlg->GetPort) + if $dlg->ShowModal == wxID_OK; + } else { + Wx::MessageDialog->new($self, 'No Bonjour device found', 'Device Browser', wxOK | wxICON_INFORMATION)->ShowModal; + } + }); + # The following event is emited by the C++ Tab implementation , + # when the Test button was clicked + EVT_COMMAND($self, -1, $BUTTON_TEST_EVENT, sub { + my ($self, $event) = @_; + my $msg = $event->GetString; + print "BUTTON_TEST_EVENT: ", $msg, "\n"; + + my $ua = LWP::UserAgent->new; + $ua->timeout(10); + + my $config = Slic3r::GUI::get_preset_tab("printer")->get_config; + my $res = $ua->get( + "http://" . $config->octoprint_host . "/api/version", + 'X-Api-Key' => $config->octoprint_apikey, + ); + if ($res->is_success) { + Slic3r::GUI::show_info($self, "Connection to OctoPrint works correctly.", "Success!"); + } else { + Slic3r::GUI::show_error($self, + "I wasn't able to connect to OctoPrint (" . $res->status_line . "). " + . "Check hostname and OctoPrint version (at least 1.1.0 is required)."); + } + }); + # A variable to inform C++ Tab implementation about disabling of Browse button + $self->{is_disabled_button_browse} = (!eval "use Net::Bonjour; 1") ? 1 : 0 ; + # A variable to inform C++ Tab implementation about user_agent + $self->{is_user_agent} = (eval "use LWP::UserAgent; 1") ? 1 : 0 ; + Slic3r::GUI::create_preset_tabs(wxTheApp->{preset_bundle}, wxTheApp->{app_config}, + $self->{no_controller}, $self->{is_disabled_button_browse}, + $self->{is_user_agent}, + $VALUE_CHANGE_EVENT, $PRESETS_CHANGED_EVENT, + $BUTTON_BROWSE_EVENT, $BUTTON_TEST_EVENT); $self->{options_tabs2}{print} = Slic3r::GUI::get_preset_tab("print"); $self->{options_tabs2}{filament} = Slic3r::GUI::get_preset_tab("filament"); $self->{options_tabs2}{printer} = Slic3r::GUI::get_preset_tab("printer"); diff --git a/xs/src/slic3r/GUI/GUI.cpp b/xs/src/slic3r/GUI/GUI.cpp index 959a3b4bc..f7365ccf7 100644 --- a/xs/src/slic3r/GUI/GUI.cpp +++ b/xs/src/slic3r/GUI/GUI.cpp @@ -186,12 +186,15 @@ void add_debug_menu(wxMenuBar *menu) #endif } -void create_preset_tabs(PresetBundle *preset_bundle, AppConfig *app_config, - bool no_controller, int event_value_change, int event_presets_changed) +void create_preset_tabs(PresetBundle *preset_bundle, AppConfig *app_config, + bool no_controller, bool is_disabled_button_browse, bool is_user_agent, + int event_value_change, int event_presets_changed, + int event_button_browse, int event_button_test) { - add_created_tab(new TabPrint (g_wxTabPanel), preset_bundle, app_config, no_controller); - add_created_tab(new TabFilament(g_wxTabPanel), preset_bundle, app_config, no_controller); - add_created_tab(new TabPrinter (g_wxTabPanel), preset_bundle, app_config, no_controller); + add_created_tab(new TabPrint (g_wxTabPanel, no_controller), preset_bundle, app_config); + add_created_tab(new TabFilament (g_wxTabPanel, no_controller), preset_bundle, app_config); + add_created_tab(new TabPrinter (g_wxTabPanel, no_controller, is_disabled_button_browse, is_user_agent), + preset_bundle, app_config); g_wxTabPanel->Bind(wxEVT_NOTEBOOK_PAGE_CHANGED, ([](wxCommandEvent e){ Tab* panel = (Tab*)g_wxTabPanel->GetCurrentPage(); if (panel->GetName().compare("Print Settings")==0 || @@ -204,6 +207,11 @@ void create_preset_tabs(PresetBundle *preset_bundle, AppConfig *app_config, continue; tab->set_event_value_change(wxEventType(event_value_change)); tab->set_event_presets_changed(wxEventType(event_presets_changed)); + if (tab->name() == "printer"){ + TabPrinter* tab_printer = static_cast(tab); + tab_printer->set_event_button_browse(wxEventType(event_button_browse)); + tab_printer->set_event_button_test(wxEventType(event_button_test)); + } } } @@ -298,9 +306,8 @@ void change_opt_value(DynamicPrintConfig& config, t_config_option_key opt_key, b } } -void add_created_tab(Tab* panel, PresetBundle *preset_bundle, AppConfig *app_config, bool no_controller) +void add_created_tab(Tab* panel, PresetBundle *preset_bundle, AppConfig *app_config) { - panel->m_no_controller = no_controller; panel->m_show_btn_incompatible_presets = app_config->get("show_incompatible_presets").empty(); panel->create_preset_tab(preset_bundle); @@ -315,7 +322,7 @@ void show_error(wxWindow* parent, std::string message){ } void show_info(wxWindow* parent, std::string message, std::string title){ - auto msg_wingow = new wxMessageDialog(parent, message, title.empty() ? "Notise" : title, wxOK | wxICON_INFORMATION); + auto msg_wingow = new wxMessageDialog(parent, message, title.empty() ? "Notice" : title, wxOK | wxICON_INFORMATION); msg_wingow->ShowModal(); } diff --git a/xs/src/slic3r/GUI/GUI.hpp b/xs/src/slic3r/GUI/GUI.hpp index 9b2d17a7a..76c8de46b 100644 --- a/xs/src/slic3r/GUI/GUI.hpp +++ b/xs/src/slic3r/GUI/GUI.hpp @@ -37,11 +37,13 @@ void set_tab_panel(wxNotebook *tab_panel); void add_debug_menu(wxMenuBar *menu); // Create a new preset tab (print, filament and printer), void create_preset_tabs(PresetBundle *preset_bundle, AppConfig *app_config, - bool no_controller, int event_value_change, int event_presets_changed); + bool no_controller, bool is_disabled_button_browse, bool is_user_agent, + int event_value_change, int event_presets_changed, + int event_button_browse, int event_button_test); TabIface* get_preset_tab_iface(char *name); // add it at the end of the tab panel. -void add_created_tab(Tab* panel, PresetBundle *preset_bundle, AppConfig *app_config, bool no_controller); +void add_created_tab(Tab* panel, PresetBundle *preset_bundle, AppConfig *app_config); // Change option value in config void change_opt_value(DynamicPrintConfig& config, t_config_option_key opt_key, boost::any value); diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp index aa713c350..18af116c6 100644 --- a/xs/src/slic3r/GUI/Tab.cpp +++ b/xs/src/slic3r/GUI/Tab.cpp @@ -74,7 +74,7 @@ void Tab::create_preset_tab(PresetBundle *preset_bundle) m_treectrl = new wxTreeCtrl(panel, wxID_ANY, wxDefaultPosition, wxSize(185, -1), wxTR_NO_BUTTONS | wxTR_HIDE_ROOT | wxTR_SINGLE | wxTR_NO_LINES | wxBORDER_SUNKEN | wxWANTS_CHARS); m_left_sizer->Add(m_treectrl, 1, wxEXPAND); - m_icons = new wxImageList(16, 16, true, 1/*, 1*/); + m_icons = new wxImageList(16, 16, true, 1); // Index of the last icon inserted into $self->{icons}. m_icon_count = -1; m_treectrl->AssignImageList(m_icons); @@ -97,7 +97,6 @@ void Tab::create_preset_tab(PresetBundle *preset_bundle) })); // Initialize the DynamicPrintConfig by default keys/values. - // Possible %params keys: no_controller build(); rebuild_page_tree(); update(); @@ -112,7 +111,7 @@ PageShp Tab::add_options_page(wxString title, std::string icon, bool is_extruder catch (std::out_of_range e) { icon_idx = -1; } if (icon_idx == -1) { // Add a new icon to the icon list. - const auto img_icon = new wxIcon(wxString::FromUTF8(Slic3r::var(/*"" + */icon).c_str()), wxBITMAP_TYPE_PNG); + const auto img_icon = new wxIcon(wxString::FromUTF8(Slic3r::var(icon).c_str()), wxBITMAP_TYPE_PNG); m_icons->Add(*img_icon); icon_idx = ++m_icon_count; // $icon_idx = $self->{icon_count} + 1; $self->{icon_count} = $icon_idx; m_icon_index[icon] = icon_idx; @@ -533,7 +532,7 @@ void TabPrint::update() Freeze(); if ( m_config->opt_bool("spiral_vase") && - !(m_config->opt_int("perimeters") == 1 && m_config->opt_int("top_solid_layers") == 0 && /*m_config->opt_float("fill_density") == 0*/ + !(m_config->opt_int("perimeters") == 1 && m_config->opt_int("top_solid_layers") == 0 && m_config->option("fill_density")->value == 0)) { std::string msg_text = "The Spiral Vase mode requires:\n" "- one perimeter\n" @@ -543,7 +542,7 @@ void TabPrint::update() "- no ensure_vertical_shell_thickness\n" "\nShall I adjust those settings in order to enable Spiral Vase?"; auto dialog = new wxMessageDialog(parent(), msg_text, wxT("Spiral Vase"), wxICON_WARNING | wxYES | wxNO); - DynamicPrintConfig new_conf = *m_config;//new DynamicPrintConfig; + DynamicPrintConfig new_conf = *m_config; if (dialog->ShowModal() == wxID_YES) { new_conf.set_key_value("perimeters", new ConfigOptionInt(1)); new_conf.set_key_value("top_solid_layers", new ConfigOptionInt(0)); @@ -1028,26 +1027,30 @@ void TabPrinter::build() }; line.append_option(serial_port); - line.append_option(/*serial_speed*/optgroup->get_option("serial_speed")); + line.append_option(optgroup->get_option("serial_speed")); line.append_widget(serial_test); optgroup->append_line(line); } optgroup = page->new_optgroup("OctoPrint upload"); // # append two buttons to the Host line - auto octoprint_host_browse = [] (wxWindow* parent) { + auto octoprint_host_browse = [this] (wxWindow* parent) { auto btn = new wxButton(parent, wxID_ANY, "Browse\u2026", wxDefaultPosition, wxDefaultSize, wxBU_LEFT); // btn->SetFont($Slic3r::GUI::small_font); btn->SetBitmap(wxBitmap(wxString::FromUTF8(Slic3r::var("zoom.png").c_str()), wxBITMAP_TYPE_PNG)); auto sizer = new wxBoxSizer(wxHORIZONTAL); sizer->Add(btn); -// if (!eval "use Net::Bonjour; 1") { -// btn->Disable; -// } + if (m_is_disabled_button_browse) + btn->Disable(); - btn->Bind(wxEVT_BUTTON, [parent](wxCommandEvent e){ - // # look for devices + btn->Bind(wxEVT_BUTTON, [this, parent](wxCommandEvent e){ + if (m_event_button_browse > 0){ + wxCommandEvent event(m_event_button_browse); + event.SetString("Button BROWSE was clicked!"); + g_wxMainFrame->ProcessWindowEvent(event); + } +// // # look for devices // auto entries; // { // my $res = Net::Bonjour->new('http'); @@ -1060,23 +1063,28 @@ void TabPrinter::build() // if $dlg->ShowModal == wxID_OK; // } // else { - auto msg_window = new wxMessageDialog(parent, "No Bonjour device found", "Device Browser", wxOK | wxICON_INFORMATION); - msg_window->ShowModal(); -// } +// auto msg_window = new wxMessageDialog(parent, "No Bonjour device found", "Device Browser", wxOK | wxICON_INFORMATION); +// msg_window->ShowModal(); +// } }); return sizer; }; auto octoprint_host_test = [this](wxWindow* parent) { - auto btn = m_octoprint_host_test_btn = new wxButton(parent, wxID_ANY, - "Test", wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT); + auto btn = m_octoprint_host_test_btn = new wxButton(parent, wxID_ANY, "Test", + wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT); // btn->SetFont($Slic3r::GUI::small_font); btn->SetBitmap(wxBitmap(wxString::FromUTF8(Slic3r::var("wrench.png").c_str()), wxBITMAP_TYPE_PNG)); auto sizer = new wxBoxSizer(wxHORIZONTAL); sizer->Add(btn); - btn->Bind(wxEVT_BUTTON, [parent](wxCommandEvent e) { + btn->Bind(wxEVT_BUTTON, [this, parent](wxCommandEvent e) { + if (m_event_button_test > 0){ + wxCommandEvent event(m_event_button_test); + event.SetString("Button TEST was clicked!"); + g_wxMainFrame->ProcessWindowEvent(event); + } // my $ua = LWP::UserAgent->new; // $ua->timeout(10); // @@ -1259,7 +1267,7 @@ void TabPrinter::update(){ } en = !m_config->opt_string("octoprint_host").empty(); - if ( en/*&& eval "use LWP::UserAgent; 1"*/) + if ( en && m_is_user_agent) m_octoprint_host_test_btn->Enable(); else m_octoprint_host_test_btn->Disable(); @@ -1384,7 +1392,7 @@ void Tab::rebuild_page_tree() // If the current profile is modified, user is asked to save the changes. void Tab::select_preset(std::string preset_name /*= ""*/) { - std::string name = preset_name/*.ToStdString()*/; + std::string name = preset_name; auto force = false; auto presets = m_presets; // If no name is provided, select the "-- default --" preset. @@ -1442,7 +1450,7 @@ void Tab::select_preset(std::string preset_name /*= ""*/) if (current_dirty || printer_tab) m_preset_bundle->update_compatible_with_printer(true); // Initialize the UI from the current preset. - load_current_preset(/*\@reload_dependent_tabs*/); + load_current_preset(); } } @@ -1527,7 +1535,6 @@ void Tab::save_preset(std::string name /*= ""*/) if (name.empty()) { auto preset = m_presets->get_selected_preset(); auto default_name = preset.is_default ? "Untitled" : preset.name; -// $default_name = ~s / \.[iI][nN][iI]$//; bool have_extention = boost::iends_with(default_name, ".ini"); if (have_extention) { @@ -1731,7 +1738,7 @@ ConfigOptionsGroupShp Page::new_optgroup(std::string title, int noncommon_label_ optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value){ //! This function will be called from OptionGroup. //! Using of CallAfter is redundant. - //! And in some cases it causes undate() function to be recalled again + //! And in some cases it causes update() function to be recalled again //! wxTheApp->CallAfter([this, opt_key, value]() { static_cast(GetParent())->update_dirty(); static_cast(GetParent())->on_value_change(opt_key, value); diff --git a/xs/src/slic3r/GUI/Tab.hpp b/xs/src/slic3r/GUI/Tab.hpp index a2cfad931..0934d5adc 100644 --- a/xs/src/slic3r/GUI/Tab.hpp +++ b/xs/src/slic3r/GUI/Tab.hpp @@ -96,6 +96,7 @@ protected: std::vector m_pages; // $self->{pages} = []; bool m_disable_tree_sel_changed_event; bool m_show_incompatible_presets; + bool m_no_controller; std::vector m_reload_dependent_tabs = {}; @@ -105,15 +106,14 @@ protected: public: PresetBundle* m_preset_bundle; - bool m_no_controller; bool m_show_btn_incompatible_presets; PresetCollection* m_presets; DynamicPrintConfig* m_config; public: Tab() {} - Tab(wxNotebook* parent, const char *title, const char* name) : - m_parent(parent), m_title(title), m_name(name) { + Tab(wxNotebook* parent, const char *title, const char* name, bool no_controller) : + m_parent(parent), m_title(title), m_name(name), m_no_controller(no_controller) { Create(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBK_LEFT | wxTAB_TRAVERSAL); } ~Tab(){} @@ -175,7 +175,8 @@ class TabPrint : public Tab { public: TabPrint() {} - TabPrint(wxNotebook* parent) : Tab(parent, "Print Settings", "print") {} + TabPrint(wxNotebook* parent, bool no_controller) : + Tab(parent, "Print Settings", "print", no_controller) {} ~TabPrint(){} ogStaticText* m_recommended_thin_wall_thickness_description_line; @@ -194,7 +195,8 @@ class TabFilament : public Tab ogStaticText* m_cooling_description_line; public: TabFilament() {} - TabFilament(wxNotebook* parent) : Tab(parent, "Filament Settings", "filament") {} + TabFilament(wxNotebook* parent, bool no_controller) : + Tab(parent, "Filament Settings", "filament", no_controller) {} ~TabFilament(){} void build() override; @@ -206,6 +208,11 @@ public: //Slic3r::GUI::Tab::Printer; class TabPrinter : public Tab { + bool m_is_disabled_button_browse; + bool m_is_user_agent; + // similar event by clicking Buttons "Browse" & "Test" + wxEventType m_event_button_browse = 0; + wxEventType m_event_button_test = 0; public: wxButton* m_serial_test_btn; wxButton* m_octoprint_host_test_btn; @@ -213,9 +220,11 @@ public: size_t m_extruders_count; std::vector m_extruder_pages; -public: TabPrinter() {} - TabPrinter(wxNotebook* parent) : Tab(parent, "Printer Settings", "printer") {} + TabPrinter(wxNotebook* parent, bool no_controller, bool is_disabled_btn_browse, bool is_user_agent) : + Tab(parent, "Printer Settings", "printer", no_controller), + m_is_disabled_button_browse(is_disabled_btn_browse), + m_is_user_agent(is_user_agent) {} ~TabPrinter(){} void build() override; @@ -224,6 +233,10 @@ public: void extruders_count_changed(size_t extruders_count); void build_extruder_pages(); void on_preset_loaded() override; + + // Set the events to the callbacks posted to the main frame window (currently implemented in Perl). + void set_event_button_browse(wxEventType evt) { m_event_button_browse = evt; } + void set_event_button_test(wxEventType evt) { m_event_button_test = evt; } }; class SavePresetWindow :public wxDialog diff --git a/xs/src/slic3r/GUI/TabIface.cpp b/xs/src/slic3r/GUI/TabIface.cpp index 97dac63fa..c4e41a41c 100644 --- a/xs/src/slic3r/GUI/TabIface.cpp +++ b/xs/src/slic3r/GUI/TabIface.cpp @@ -9,8 +9,9 @@ void TabIface::update_ui_from_settings() { m_tab->update_ui_from_settings();} void TabIface::select_preset(char* name) { m_tab->select_preset(name);} char* TabIface::title() { return (char*)m_tab->title().ToStdString().data();} void TabIface::load_config(DynamicPrintConfig* config) { m_tab->load_config(*config);} +void TabIface::load_key_value(char* opt_key, char* value){ m_tab->load_key_value(opt_key, static_cast(value)); } bool TabIface::current_preset_is_dirty() { return m_tab->current_preset_is_dirty();} -DynamicPrintConfig* TabIface::get_config() { return m_tab->get_config();} +DynamicPrintConfig* TabIface::get_config() { return m_tab->get_config(); } PresetCollection* TabIface::get_presets() { return m_tab->get_presets(); } std::vector TabIface::get_dependent_tabs() { return m_tab->get_dependent_tabs(); } diff --git a/xs/src/slic3r/GUI/TabIface.hpp b/xs/src/slic3r/GUI/TabIface.hpp index 12ceb93c0..2b5bfce9a 100644 --- a/xs/src/slic3r/GUI/TabIface.hpp +++ b/xs/src/slic3r/GUI/TabIface.hpp @@ -20,6 +20,7 @@ public: void select_preset(char* name); char* title(); void load_config(DynamicPrintConfig* config); + void load_key_value(char* opt_key, char* value); bool current_preset_is_dirty(); DynamicPrintConfig* get_config(); PresetCollection* TabIface::get_presets(); diff --git a/xs/xsp/GUI.xsp b/xs/xsp/GUI.xsp index b81754dec..c273013db 100644 --- a/xs/xsp/GUI.xsp +++ b/xs/xsp/GUI.xsp @@ -35,8 +35,14 @@ void set_tab_panel(SV *ui) void add_debug_menu(SV *ui) %code%{ Slic3r::GUI::add_debug_menu((wxMenuBar*)wxPli_sv_2_object(aTHX_ ui, "Wx::MenuBar")); %}; -void create_preset_tabs(PresetBundle *preset_bundle, AppConfig *app_config, bool no_controller, int event_value_change, int event_presets_changed) - %code%{ Slic3r::GUI::create_preset_tabs(preset_bundle, app_config, no_controller, event_value_change, event_presets_changed); %}; +void create_preset_tabs(PresetBundle *preset_bundle, AppConfig *app_config, + bool no_controller, bool is_disabled_button_browse, bool is_user_agent, + int event_value_change, int event_presets_changed, + int event_button_browse, int event_button_test) + %code%{ Slic3r::GUI::create_preset_tabs(preset_bundle, app_config, no_controller, + is_disabled_button_browse, is_user_agent, + event_value_change, event_presets_changed, + event_button_browse, event_button_test); %}; Ref get_preset_tab(char *name) %code%{ RETVAL=Slic3r::GUI::get_preset_tab_iface(name); %}; diff --git a/xs/xsp/GUI_Tab.xsp b/xs/xsp/GUI_Tab.xsp index d71dfa3d9..7dddbbff2 100644 --- a/xs/xsp/GUI_Tab.xsp +++ b/xs/xsp/GUI_Tab.xsp @@ -14,6 +14,7 @@ void select_preset(char* name); void load_config(DynamicPrintConfig* config); bool current_preset_is_dirty(); + void load_key_value(char* opt_key, char* value); char* title(); Ref get_config(); Ref get_presets();