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

github.com/supermerill/SuperSlicer.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/xs/src
diff options
context:
space:
mode:
authorVojtech Kral <vojtech@kral.hk>2018-03-15 20:06:26 +0300
committerbubnikv <bubnikv@gmail.com>2018-03-15 20:06:26 +0300
commitc88d2780ced7bb91e79e2e9a5ef4a58506d7d175 (patch)
treed9305d5b066ce7aa6beffcf65848971890fd041a /xs/src
parent8d4b6035728e0bcf241c65c48c16cbccf4ae71c5 (diff)
Octoprint (#796)
* Octoprint: GUI for CA file, improvements * Octoprint: Add GUI for Bonjour lookup, bugfixes * Octoprint: Bonjour browser: Cleanup Perl interaction * Octoprint: Bonjour: Perform several broadcast, UI fixes * Octoprint: Add files to localization list * Http: Disable CA File setting on SSL backends that don't support it
Diffstat (limited to 'xs/src')
-rw-r--r--xs/src/slic3r/GUI/BonjourDialog.cpp200
-rw-r--r--xs/src/slic3r/GUI/BonjourDialog.hpp49
-rw-r--r--xs/src/slic3r/GUI/Field.cpp20
-rw-r--r--xs/src/slic3r/GUI/Field.hpp42
-rw-r--r--xs/src/slic3r/GUI/GUI.cpp24
-rw-r--r--xs/src/slic3r/GUI/GUI.hpp5
-rw-r--r--xs/src/slic3r/GUI/OptionsGroup.hpp4
-rw-r--r--xs/src/slic3r/GUI/Tab.cpp119
-rw-r--r--xs/src/slic3r/GUI/Tab.hpp14
-rw-r--r--xs/src/slic3r/Utils/Bonjour.cpp155
-rw-r--r--xs/src/slic3r/Utils/Bonjour.hpp20
-rw-r--r--xs/src/slic3r/Utils/Http.cpp46
-rw-r--r--xs/src/slic3r/Utils/Http.hpp1
-rw-r--r--xs/src/slic3r/Utils/OctoPrint.cpp42
-rw-r--r--xs/src/slic3r/Utils/OctoPrint.hpp7
15 files changed, 549 insertions, 199 deletions
diff --git a/xs/src/slic3r/GUI/BonjourDialog.cpp b/xs/src/slic3r/GUI/BonjourDialog.cpp
new file mode 100644
index 000000000..34fac9a91
--- /dev/null
+++ b/xs/src/slic3r/GUI/BonjourDialog.cpp
@@ -0,0 +1,200 @@
+#include "slic3r/Utils/Bonjour.hpp" // On Windows, boost needs to be included before wxWidgets headers
+
+#include "BonjourDialog.hpp"
+
+#include <set>
+#include <mutex>
+
+#include <wx/sizer.h>
+#include <wx/button.h>
+#include <wx/listctrl.h>
+#include <wx/stattext.h>
+#include <wx/timer.h>
+
+#include "slic3r/GUI/GUI.hpp"
+#include "slic3r/Utils/Bonjour.hpp"
+
+
+namespace Slic3r {
+
+
+struct BonjourReplyEvent : public wxEvent
+{
+ BonjourReply reply;
+
+ BonjourReplyEvent(wxEventType eventType, int winid, BonjourReply &&reply) :
+ wxEvent(winid, eventType),
+ reply(std::move(reply))
+ {}
+
+ virtual wxEvent *Clone() const
+ {
+ return new BonjourReplyEvent(*this);
+ }
+};
+
+wxDEFINE_EVENT(EVT_BONJOUR_REPLY, BonjourReplyEvent);
+
+wxDECLARE_EVENT(EVT_BONJOUR_COMPLETE, wxCommandEvent);
+wxDEFINE_EVENT(EVT_BONJOUR_COMPLETE, wxCommandEvent);
+
+class ReplySet: public std::set<BonjourReply> {};
+
+struct LifetimeGuard
+{
+ std::mutex mutex;
+ BonjourDialog *dialog;
+
+ LifetimeGuard(BonjourDialog *dialog) : dialog(dialog) {}
+};
+
+
+BonjourDialog::BonjourDialog(wxWindow *parent) :
+ wxDialog(parent, wxID_ANY, _(L("Network lookup"))),
+ list(new wxListView(this, wxID_ANY, wxDefaultPosition, wxSize(800, 300))),
+ replies(new ReplySet),
+ label(new wxStaticText(this, wxID_ANY, "")),
+ timer(new wxTimer()),
+ timer_state(0)
+{
+ wxBoxSizer *vsizer = new wxBoxSizer(wxVERTICAL);
+
+ vsizer->Add(label, 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, 10);
+
+ list->SetSingleStyle(wxLC_SINGLE_SEL);
+ list->SetSingleStyle(wxLC_SORT_DESCENDING);
+ list->AppendColumn(_(L("Address")), wxLIST_FORMAT_LEFT, 50);
+ list->AppendColumn(_(L("Hostname")), wxLIST_FORMAT_LEFT, 100);
+ list->AppendColumn(_(L("Service name")), wxLIST_FORMAT_LEFT, 200);
+ list->AppendColumn(_(L("OctoPrint version")), wxLIST_FORMAT_LEFT, 50);
+
+ vsizer->Add(list, 1, wxEXPAND | wxALL, 10);
+
+ wxBoxSizer *button_sizer = new wxBoxSizer(wxHORIZONTAL);
+ button_sizer->Add(new wxButton(this, wxID_OK, "OK"), 0, wxALL, 10);
+ button_sizer->Add(new wxButton(this, wxID_CANCEL, "Cancel"), 0, wxALL, 10);
+ // ^ Note: The Ok/Cancel labels are translated by wxWidgets
+
+ vsizer->Add(button_sizer, 0, wxALIGN_CENTER);
+ SetSizerAndFit(vsizer);
+
+ Bind(EVT_BONJOUR_REPLY, &BonjourDialog::on_reply, this);
+
+ Bind(EVT_BONJOUR_COMPLETE, [this](wxCommandEvent &) {
+ this->timer_state = 0;
+ });
+
+ Bind(wxEVT_TIMER, &BonjourDialog::on_timer, this);
+}
+
+BonjourDialog::~BonjourDialog()
+{
+ // Needed bacuse of forward defs
+}
+
+bool BonjourDialog::show_and_lookup()
+{
+ Show(); // Because we need GetId() to work before ShowModal()
+
+ timer->Stop();
+ timer->SetOwner(this);
+ timer_state = 1;
+ timer->Start(1000);
+ wxTimerEvent evt_dummy;
+ on_timer(evt_dummy);
+
+ // The background thread needs to queue messages for this dialog
+ // and for that it needs a valid pointer to it (mandated by the wxWidgets API).
+ // Here we put the pointer under a shared_ptr and protect it by a mutex,
+ // so that both threads can access it safely.
+ auto dguard = std::make_shared<LifetimeGuard>(this);
+
+ bonjour = std::move(Bonjour("octoprint")
+ .set_retries(3)
+ .set_timeout(4)
+ .on_reply([dguard](BonjourReply &&reply) {
+ std::lock_guard<std::mutex> lock_guard(dguard->mutex);
+ auto dialog = dguard->dialog;
+ if (dialog != nullptr) {
+ auto evt = new BonjourReplyEvent(EVT_BONJOUR_REPLY, dialog->GetId(), std::move(reply));
+ wxQueueEvent(dialog, evt);
+ }
+ })
+ .on_complete([dguard]() {
+ std::lock_guard<std::mutex> lock_guard(dguard->mutex);
+ auto dialog = dguard->dialog;
+ if (dialog != nullptr) {
+ auto evt = new wxCommandEvent(EVT_BONJOUR_COMPLETE, dialog->GetId());
+ wxQueueEvent(dialog, evt);
+ }
+ })
+ .lookup()
+ );
+
+ bool res = ShowModal() == wxID_OK && list->GetFirstSelected() >= 0;
+ {
+ // Tell the background thread the dialog is going away...
+ std::lock_guard<std::mutex> lock_guard(dguard->mutex);
+ dguard->dialog = nullptr;
+ }
+ return res;
+}
+
+wxString BonjourDialog::get_selected() const
+{
+ auto sel = list->GetFirstSelected();
+ return sel >= 0 ? list->GetItemText(sel) : wxString();
+}
+
+
+// Private
+
+void BonjourDialog::on_reply(BonjourReplyEvent &e)
+{
+ if (replies->find(e.reply) != replies->end()) {
+ // We already have this reply
+ return;
+ }
+
+ replies->insert(std::move(e.reply));
+
+ auto selected = get_selected();
+ list->DeleteAllItems();
+
+ // The whole list is recreated so that we benefit from it already being sorted in the set.
+ // (And also because wxListView's sorting API is bananas.)
+ for (const auto &reply : *replies) {
+ auto item = list->InsertItem(0, reply.full_address);
+ list->SetItem(item, 1, reply.hostname);
+ list->SetItem(item, 2, reply.service_name);
+ list->SetItem(item, 3, reply.version);
+ }
+
+ for (int i = 0; i < 4; i++) {
+ this->list->SetColumnWidth(i, wxLIST_AUTOSIZE);
+ if (this->list->GetColumnWidth(i) < 100) { this->list->SetColumnWidth(i, 100); }
+ }
+
+ if (!selected.IsEmpty()) {
+ // Attempt to preserve selection
+ auto hit = list->FindItem(-1, selected);
+ if (hit >= 0) { list->SetItemState(hit, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED); }
+ }
+}
+
+void BonjourDialog::on_timer(wxTimerEvent &)
+{
+ const auto search_str = _(L("Searching for devices"));
+
+ if (timer_state > 0) {
+ const std::string dots(timer_state, '.');
+ label->SetLabel(wxString::Format("%s %s", search_str, dots));
+ timer_state = (timer_state) % 3 + 1;
+ } else {
+ label->SetLabel(wxString::Format("%s: %s", search_str, _(L("Finished."))));
+ timer->Stop();
+ }
+}
+
+
+}
diff --git a/xs/src/slic3r/GUI/BonjourDialog.hpp b/xs/src/slic3r/GUI/BonjourDialog.hpp
new file mode 100644
index 000000000..e3f53790b
--- /dev/null
+++ b/xs/src/slic3r/GUI/BonjourDialog.hpp
@@ -0,0 +1,49 @@
+#ifndef slic3r_BonjourDialog_hpp_
+#define slic3r_BonjourDialog_hpp_
+
+#include <memory>
+
+#include <wx/dialog.h>
+
+class wxListView;
+class wxStaticText;
+class wxTimer;
+class wxTimerEvent;
+
+
+namespace Slic3r {
+
+class Bonjour;
+class BonjourReplyEvent;
+class ReplySet;
+
+
+class BonjourDialog: public wxDialog
+{
+public:
+ BonjourDialog(wxWindow *parent);
+ BonjourDialog(BonjourDialog &&) = delete;
+ BonjourDialog(const BonjourDialog &) = delete;
+ BonjourDialog &operator=(BonjourDialog &&) = delete;
+ BonjourDialog &operator=(const BonjourDialog &) = delete;
+ ~BonjourDialog();
+
+ bool show_and_lookup();
+ wxString get_selected() const;
+private:
+ wxListView *list;
+ std::unique_ptr<ReplySet> replies;
+ wxStaticText *label;
+ std::shared_ptr<Bonjour> bonjour;
+ std::unique_ptr<wxTimer> timer;
+ unsigned timer_state;
+
+ void on_reply(BonjourReplyEvent &);
+ void on_timer(wxTimerEvent &);
+};
+
+
+
+}
+
+#endif
diff --git a/xs/src/slic3r/GUI/Field.cpp b/xs/src/slic3r/GUI/Field.cpp
index aed7ba12f..c2fc5e4e4 100644
--- a/xs/src/slic3r/GUI/Field.cpp
+++ b/xs/src/slic3r/GUI/Field.cpp
@@ -261,7 +261,7 @@ void SpinCtrl::BUILD() {
// # when it was changed from the text control, so the on_change callback
// # gets the old one, and on_kill_focus resets the control to the old value.
// # As a workaround, we get the new value from $event->GetString and store
-// # here temporarily so that we can return it from $self->get_value
+// # here temporarily so that we can return it from $self->get_value
std::string value = e.GetString().utf8_str().data();
if (is_matched(value, "^\\d+$"))
tmp_value = std::stoi(value);
@@ -365,9 +365,9 @@ void Choice::set_selection()
}
}
-void Choice::set_value(const std::string value) //! Redundant?
+void Choice::set_value(const std::string value, bool change_event) //! Redundant?
{
- m_disable_change_event = true;
+ m_disable_change_event = !change_event;
size_t idx=0;
for (auto el : m_opt.enum_values)
@@ -384,9 +384,9 @@ void Choice::set_value(const std::string value) //! Redundant?
m_disable_change_event = false;
}
-void Choice::set_value(boost::any value)
+void Choice::set_value(boost::any value, bool change_event)
{
- m_disable_change_event = true;
+ m_disable_change_event = !change_event;
switch (m_opt.type){
case coInt:
@@ -429,7 +429,7 @@ void Choice::set_values(const std::vector<std::string> values)
return;
m_disable_change_event = true;
-// # it looks that Clear() also clears the text field in recent wxWidgets versions,
+// # it looks that Clear() also clears the text field in recent wxWidgets versions,
// # but we want to preserve it
auto ww = dynamic_cast<wxComboBox*>(window);
auto value = ww->GetValue();
@@ -541,9 +541,9 @@ void PointCtrl::BUILD()
y_textctrl->SetToolTip(get_tooltip_text(X+", "+Y));
}
-void PointCtrl::set_value(const Pointf value)
+void PointCtrl::set_value(const Pointf value, bool change_event)
{
- m_disable_change_event = true;
+ m_disable_change_event = !change_event;
double val = value.x;
x_textctrl->SetValue(val - int(val) == 0 ? wxString::Format(_T("%i"), int(val)) : wxNumberFormatter::ToString(val, 2, wxNumberFormatter::Style_None));
@@ -553,7 +553,7 @@ void PointCtrl::set_value(const Pointf value)
m_disable_change_event = false;
}
-void PointCtrl::set_value(boost::any value)
+void PointCtrl::set_value(boost::any value, bool change_event)
{
Pointf pt;
Pointf *ptf = boost::any_cast<Pointf>(&value);
@@ -579,7 +579,7 @@ void PointCtrl::set_value(boost::any value)
// return;
// }
// }
- set_value(pt);
+ set_value(pt, change_event);
}
boost::any PointCtrl::get_value()
diff --git a/xs/src/slic3r/GUI/Field.hpp b/xs/src/slic3r/GUI/Field.hpp
index db8ad4c9f..2ddb5d9f8 100644
--- a/xs/src/slic3r/GUI/Field.hpp
+++ b/xs/src/slic3r/GUI/Field.hpp
@@ -78,7 +78,7 @@ public:
/// Sets a value for this control.
/// subclasses should overload with a specific version
/// Postcondition: Method does not fire the on_change event.
- virtual void set_value(boost::any value) = 0;
+ virtual void set_value(boost::any value, bool change_event) = 0;
/// Gets a boost::any representing this control.
/// subclasses should overload with a specific version
@@ -134,13 +134,13 @@ public:
void BUILD();
wxWindow* window {nullptr};
- virtual void set_value(std::string value) {
- m_disable_change_event = true;
+ virtual void set_value(std::string value, bool change_event = false) {
+ m_disable_change_event = !change_event;
dynamic_cast<wxTextCtrl*>(window)->SetValue(wxString(value));
m_disable_change_event = false;
}
- virtual void set_value(boost::any value) {
- m_disable_change_event = true;
+ virtual void set_value(boost::any value, bool change_event = false) {
+ m_disable_change_event = !change_event;
dynamic_cast<wxTextCtrl*>(window)->SetValue(boost::any_cast<wxString>(value));
m_disable_change_event = false;
}
@@ -161,13 +161,13 @@ public:
wxWindow* window{ nullptr };
void BUILD() override;
- void set_value(const bool value) {
- m_disable_change_event = true;
+ void set_value(const bool value, bool change_event = false) {
+ m_disable_change_event = !change_event;
dynamic_cast<wxCheckBox*>(window)->SetValue(value);
m_disable_change_event = false;
}
- void set_value(boost::any value) {
- m_disable_change_event = true;
+ void set_value(boost::any value, bool change_event = false) {
+ m_disable_change_event = !change_event;
dynamic_cast<wxCheckBox*>(window)->SetValue(boost::any_cast<bool>(value));
m_disable_change_event = false;
}
@@ -189,13 +189,13 @@ public:
wxWindow* window{ nullptr };
void BUILD() override;
- void set_value(const std::string value) {
- m_disable_change_event = true;
+ void set_value(const std::string value, bool change_event = false) {
+ m_disable_change_event = !change_event;
dynamic_cast<wxSpinCtrl*>(window)->SetValue(value);
m_disable_change_event = false;
}
- void set_value(boost::any value) {
- m_disable_change_event = true;
+ void set_value(boost::any value, bool change_event = false) {
+ m_disable_change_event = !change_event;
dynamic_cast<wxSpinCtrl*>(window)->SetValue(boost::any_cast<int>(value));
m_disable_change_event = false;
}
@@ -218,8 +218,8 @@ public:
void BUILD() override;
void set_selection();
- void set_value(const std::string value);
- void set_value(boost::any value);
+ void set_value(const std::string value, bool change_event = false);
+ void set_value(boost::any value, bool change_event = false);
void set_values(const std::vector<std::string> values);
boost::any get_value() override;
@@ -237,13 +237,13 @@ public:
wxWindow* window{ nullptr };
void BUILD() override;
- void set_value(const std::string value) {
- m_disable_change_event = true;
+ void set_value(const std::string value, bool change_event = false) {
+ m_disable_change_event = !change_event;
dynamic_cast<wxColourPickerCtrl*>(window)->SetColour(value);
m_disable_change_event = false;
}
- void set_value(boost::any value) {
- m_disable_change_event = true;
+ void set_value(boost::any value, bool change_event = false) {
+ m_disable_change_event = !change_event;
dynamic_cast<wxColourPickerCtrl*>(window)->SetColour(boost::any_cast<wxString>(value));
m_disable_change_event = false;
}
@@ -267,8 +267,8 @@ public:
void BUILD() override;
- void set_value(const Pointf value);
- void set_value(boost::any value);
+ void set_value(const Pointf value, bool change_event = false);
+ void set_value(boost::any value, bool change_event = false);
boost::any get_value() override;
void enable() override {
diff --git a/xs/src/slic3r/GUI/GUI.cpp b/xs/src/slic3r/GUI/GUI.cpp
index 262d41a79..0410b7969 100644
--- a/xs/src/slic3r/GUI/GUI.cpp
+++ b/xs/src/slic3r/GUI/GUI.cpp
@@ -358,24 +358,17 @@ void open_preferences_dialog(int event_preferences)
dlg->ShowModal();
}
-void create_preset_tabs(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)
+void create_preset_tabs(bool no_controller, int event_value_change, int event_presets_changed)
{
add_created_tab(new TabPrint (g_wxTabPanel, no_controller));
add_created_tab(new TabFilament (g_wxTabPanel, no_controller));
- add_created_tab(new TabPrinter (g_wxTabPanel, no_controller, is_disabled_button_browse, is_user_agent));
+ add_created_tab(new TabPrinter (g_wxTabPanel, no_controller));
for (size_t i = 0; i < g_wxTabPanel->GetPageCount(); ++ i) {
Tab *tab = dynamic_cast<Tab*>(g_wxTabPanel->GetPage(i));
if (! tab)
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<TabPrinter*>(tab);
- tab_printer->set_event_button_browse(wxEventType(event_button_browse));
- tab_printer->set_event_button_test(wxEventType(event_button_test));
- }
}
}
@@ -591,19 +584,6 @@ wxString from_u8(const std::string &str)
return wxString::FromUTF8(str.c_str());
}
-wxWindow *get_widget_by_id(int id)
-{
- if (g_wxMainFrame == nullptr) {
- throw std::runtime_error("Main frame not set");
- }
-
- wxWindow *window = g_wxMainFrame->FindWindow(id);
- if (window == nullptr) {
- throw std::runtime_error((boost::format("Could not find widget by ID: %1%") % id).str());
- }
-
- return window;
-}
void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFlexGridSizer* preset_sizer)
{
diff --git a/xs/src/slic3r/GUI/GUI.hpp b/xs/src/slic3r/GUI/GUI.hpp
index 2baa10cb9..084b6de46 100644
--- a/xs/src/slic3r/GUI/GUI.hpp
+++ b/xs/src/slic3r/GUI/GUI.hpp
@@ -86,9 +86,7 @@ void add_debug_menu(wxMenuBar *menu, int event_language_change);
void open_preferences_dialog(int event_preferences);
// Create a new preset tab (print, filament and printer),
-void create_preset_tabs(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);
+void create_preset_tabs(bool no_controller, int event_value_change, int event_presets_changed);
TabIface* get_preset_tab_iface(char *name);
// add it at the end of the tab panel.
@@ -127,7 +125,6 @@ wxString L_str(const std::string &str);
// Return wxString from std::string in UTF8
wxString from_u8(const std::string &str);
-wxWindow *get_widget_by_id(int id);
void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFlexGridSizer* preset_sizer);
diff --git a/xs/src/slic3r/GUI/OptionsGroup.hpp b/xs/src/slic3r/GUI/OptionsGroup.hpp
index 42db22225..aa0563866 100644
--- a/xs/src/slic3r/GUI/OptionsGroup.hpp
+++ b/xs/src/slic3r/GUI/OptionsGroup.hpp
@@ -97,9 +97,9 @@ public:
if (m_fields.find(id) == m_fields.end()) return nullptr;
return m_fields.at(id).get();
}
- bool set_value(t_config_option_key id, boost::any value) {
+ bool set_value(t_config_option_key id, boost::any value, bool change_event = false) {
if (m_fields.find(id) == m_fields.end()) return false;
- m_fields.at(id)->set_value(value);
+ m_fields.at(id)->set_value(value, change_event);
return true;
}
boost::any get_value(t_config_option_key id) {
diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp
index e2dfa6f27..d0f9f0ce3 100644
--- a/xs/src/slic3r/GUI/Tab.cpp
+++ b/xs/src/slic3r/GUI/Tab.cpp
@@ -3,6 +3,9 @@
#include "PresetBundle.hpp"
#include "PresetHints.hpp"
#include "../../libslic3r/Utils.hpp"
+#include "slic3r/Utils/Http.hpp"
+#include "slic3r/Utils/OctoPrint.hpp"
+#include "BonjourDialog.hpp"
#include <wx/app.h>
#include <wx/button.h>
@@ -14,6 +17,7 @@
#include <wx/treectrl.h>
#include <wx/imaglist.h>
#include <wx/settings.h>
+#include <wx/filedlg.h>
#include <boost/algorithm/string/predicate.hpp>
@@ -1102,39 +1106,18 @@ void TabPrinter::build()
}
optgroup = page->new_optgroup(_(L("OctoPrint upload")));
- // # append two buttons to the Host line
- auto octoprint_host_browse = [this] (wxWindow* parent) {
+
+ auto octoprint_host_browse = [this, optgroup] (wxWindow* parent) {
auto btn = new wxButton(parent, wxID_ANY, _(L(" Browse "))+"\u2026", wxDefaultPosition, wxDefaultSize, wxBU_LEFT);
-// btn->SetFont($Slic3r::GUI::small_font);
btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("zoom.png")), wxBITMAP_TYPE_PNG));
auto sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(btn);
- if (m_is_disabled_button_browse)
- btn->Disable();
-
- 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);
+ btn->Bind(wxEVT_BUTTON, [this, parent, optgroup](wxCommandEvent e) {
+ BonjourDialog dialog(parent);
+ if (dialog.show_and_lookup()) {
+ optgroup->set_value("octoprint_host", std::move(dialog.get_selected()), true);
}
-// // # look for devices
-// auto entries;
-// {
-// my $res = Net::Bonjour->new('http');
-// $res->discover;
-// $entries = [$res->entries];
-// }
-// if (@{$entries}) {
-// my $dlg = Slic3r::GUI::BonjourBrowser->new($self, $entries);
-// $self->_load_key_value('octoprint_host', $dlg->GetValue . ":".$dlg->GetPort)
-// if $dlg->ShowModal == wxID_OK;
-// }
-// else {
-// auto msg_window = new wxMessageDialog(parent, "No Bonjour device found", "Device Browser", wxOK | wxICON_INFORMATION);
-// msg_window->ShowModal();
-// }
});
return sizer;
@@ -1143,33 +1126,23 @@ void TabPrinter::build()
auto octoprint_host_test = [this](wxWindow* parent) {
auto btn = m_octoprint_host_test_btn = new wxButton(parent, wxID_ANY, _(L("Test")),
wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT);
-// btn->SetFont($Slic3r::GUI::small_font);
btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("wrench.png")), wxBITMAP_TYPE_PNG));
auto sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(btn);
- 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);
+ btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent e) {
+ OctoPrint octoprint(m_config);
+ wxString msg;
+ if (octoprint.test(msg)) {
+ show_info(this, _(L("Connection to OctoPrint works correctly.")), _(L("Success!")));
+ } else {
+ const auto text = wxString::Format("%s: %s\n\n%s",
+ _(L("Could not connect to OctoPrint")), msg, _(L("Note: OctoPrint version at least 1.1.0 is required."))
+ );
+ show_error(this, text);
}
-// my $ua = LWP::UserAgent->new;
-// $ua->timeout(10);
-//
-// my $res = $ua->get(
-// "http://".$self->{config}->octoprint_host . "/api/version",
-// 'X-Api-Key' = > $self->{config}->octoprint_apikey,
-// );
-// if ($res->is_success) {
-// show_info(parent, "Connection to OctoPrint works correctly.", "Success!");
-// }
-// else {
-// show_error(parent,
-// "I wasn't able to connect to OctoPrint (".$res->status_line . "). "
-// . "Check hostname and OctoPrint version (at least 1.1.0 is required).");
-// }
- });
+ });
+
return sizer;
};
@@ -1179,6 +1152,45 @@ void TabPrinter::build()
optgroup->append_line(host_line);
optgroup->append_single_option_line("octoprint_apikey");
+ if (Http::ca_file_supported()) {
+
+ Line cafile_line = optgroup->create_single_option_line("octoprint_cafile");
+
+ auto octoprint_cafile_browse = [this, optgroup] (wxWindow* parent) {
+ auto btn = new wxButton(parent, wxID_ANY, _(L(" Browse "))+"\u2026", wxDefaultPosition, wxDefaultSize, wxBU_LEFT);
+ btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("zoom.png")), wxBITMAP_TYPE_PNG));
+ auto sizer = new wxBoxSizer(wxHORIZONTAL);
+ sizer->Add(btn);
+
+ btn->Bind(wxEVT_BUTTON, [this, optgroup] (wxCommandEvent e){
+ static const auto filemasks = _(L("Certificate files (*.crt, *.pem)|*.crt;*.pem|All files|*.*"));
+ wxFileDialog openFileDialog(this, _(L("Open CA certificate file")), "", "", filemasks, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
+ if (openFileDialog.ShowModal() != wxID_CANCEL) {
+ optgroup->set_value("octoprint_cafile", std::move(openFileDialog.GetPath()), true);
+ }
+ });
+
+ return sizer;
+ };
+
+ cafile_line.append_widget(octoprint_cafile_browse);
+ optgroup->append_line(cafile_line);
+
+ auto octoprint_cafile_hint = [this, optgroup] (wxWindow* parent) {
+ auto txt = new wxStaticText(parent, wxID_ANY,
+ _(L("HTTPS CA file is optional. It is only needed if you use HTTPS with a self-signed certificate.")));
+ auto sizer = new wxBoxSizer(wxHORIZONTAL);
+ sizer->Add(txt);
+ return sizer;
+ };
+
+ Line cafile_hint { "", "" };
+ cafile_hint.full_width = 1;
+ cafile_hint.widget = std::move(octoprint_cafile_hint);
+ optgroup->append_line(cafile_hint);
+
+ }
+
optgroup = page->new_optgroup(_(L("Firmware")));
optgroup->append_single_option_line("gcode_flavor");
@@ -1337,13 +1349,8 @@ void TabPrinter::update(){
m_serial_test_btn->Disable();
}
- en = !m_config->opt_string("octoprint_host").empty();
- if ( en && m_is_user_agent)
- m_octoprint_host_test_btn->Enable();
- else
- m_octoprint_host_test_btn->Disable();
- get_field("octoprint_apikey")->toggle(en);
-
+ m_octoprint_host_test_btn->Enable(!m_config->opt_string("octoprint_host").empty());
+
bool have_multiple_extruders = m_extruders_count > 1;
get_field("toolchange_gcode")->toggle(have_multiple_extruders);
get_field("single_extruder_multi_material")->toggle(have_multiple_extruders);
diff --git a/xs/src/slic3r/GUI/Tab.hpp b/xs/src/slic3r/GUI/Tab.hpp
index e2dc51ee4..4f65f1475 100644
--- a/xs/src/slic3r/GUI/Tab.hpp
+++ b/xs/src/slic3r/GUI/Tab.hpp
@@ -214,11 +214,6 @@ 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;
@@ -228,10 +223,7 @@ public:
std::vector<PageShp> m_extruder_pages;
TabPrinter() {}
- TabPrinter(wxNotebook* parent, bool no_controller, bool is_disabled_btn_browse, bool is_user_agent) :
- Tab(parent, _(L("Printer Settings")), "printer", no_controller),
- m_is_disabled_button_browse(is_disabled_btn_browse),
- m_is_user_agent(is_user_agent) {}
+ TabPrinter(wxNotebook* parent, bool no_controller) : Tab(parent, _(L("Printer Settings")), "printer", no_controller) {}
~TabPrinter(){}
void build() override;
@@ -240,10 +232,6 @@ 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/Utils/Bonjour.cpp b/xs/src/slic3r/Utils/Bonjour.cpp
index 6107e2c60..09d9b5873 100644
--- a/xs/src/slic3r/Utils/Bonjour.cpp
+++ b/xs/src/slic3r/Utils/Bonjour.cpp
@@ -1,9 +1,7 @@
#include "Bonjour.hpp"
-#include <iostream> // XXX
#include <cstdint>
#include <algorithm>
-#include <unordered_map>
#include <array>
#include <vector>
#include <string>
@@ -23,16 +21,18 @@ namespace asio = boost::asio;
using boost::asio::ip::udp;
-// TODO: Fuzzing test (done without TXT)
-// FIXME: check char retype to unsigned
-
-
namespace Slic3r {
// Minimal implementation of a MDNS/DNS-SD client
// This implementation is extremely simple, only the bits that are useful
-// for very basic MDNS discovery are present.
+// for basic MDNS discovery of OctoPi devices are present.
+// However, the bits that are present are implemented with security in mind.
+// Only fully correct DNS replies are allowed through.
+// While decoding the decoder will bail the moment it encounters anything fishy.
+// At least that's the idea. To help prove this is actually the case,
+// the implementations has been tested with AFL.
+
struct DnsName: public std::string
{
@@ -48,8 +48,7 @@ struct DnsName: public std::string
return boost::none;
}
- // Check for recursion depth to prevent parsing names that are nested too deeply
- // or end up cyclic:
+ // Check for recursion depth to prevent parsing names that are nested too deeply or end up cyclic:
if (depth >= MAX_RECURSION) {
return boost::none;
}
@@ -443,6 +442,30 @@ private:
}
};
+std::ostream& operator<<(std::ostream &os, const DnsMessage &msg)
+{
+ os << "DnsMessage(ID: " << msg.header.id << ", "
+ << "Q: " << (msg.question ? msg.question->name.c_str() : "none") << ", "
+ << "A: " << (msg.rr_a ? msg.rr_a->ip.to_string() : "none") << ", "
+ << "AAAA: " << (msg.rr_aaaa ? msg.rr_aaaa->ip.to_string() : "none") << ", "
+ << "services: [";
+
+ enum { SRV_PRINT_MAX = 3 };
+ unsigned i = 0;
+ for (const auto &sdpair : msg.sdmap) {
+ os << sdpair.first << ", ";
+
+ if (++i >= SRV_PRINT_MAX) {
+ os << "...";
+ break;
+ }
+ }
+
+ os << "])";
+
+ return os;
+}
+
struct BonjourRequest
{
@@ -515,6 +538,7 @@ struct Bonjour::priv
const std::string protocol;
const std::string service_dn;
unsigned timeout;
+ unsigned retries;
uint16_t rq_id;
std::vector<char> buffer;
@@ -524,6 +548,7 @@ struct Bonjour::priv
priv(std::string service, std::string protocol);
+ std::string strip_service_dn(const std::string &service_name) const;
void udp_receive(udp::endpoint from, size_t bytes);
void lookup_perform();
};
@@ -533,11 +558,26 @@ Bonjour::priv::priv(std::string service, std::string protocol) :
protocol(std::move(protocol)),
service_dn((boost::format("_%1%._%2%.local") % this->service % this->protocol).str()),
timeout(10),
+ retries(1),
rq_id(0)
{
buffer.resize(DnsMessage::MAX_SIZE);
}
+std::string Bonjour::priv::strip_service_dn(const std::string &service_name) const
+{
+ if (service_name.size() <= service_dn.size()) {
+ return service_name;
+ }
+
+ auto needle = service_name.rfind(service_dn);
+ if (needle == service_name.size() - service_dn.size()) {
+ return service_name.substr(0, needle - 1);
+ } else {
+ return service_name;
+ }
+}
+
void Bonjour::priv::udp_receive(udp::endpoint from, size_t bytes)
{
if (bytes == 0 || !replyfn) {
@@ -557,7 +597,10 @@ void Bonjour::priv::udp_receive(udp::endpoint from, size_t bytes)
}
const auto &srv = *sdpair.second.srv;
- BonjourReply reply(ip, sdpair.first, srv.hostname);
+ auto service_name = strip_service_dn(sdpair.first);
+
+ std::string path;
+ std::string version;
if (sdpair.second.txt) {
static const std::string tag_path = "path=";
@@ -565,13 +608,14 @@ void Bonjour::priv::udp_receive(udp::endpoint from, size_t bytes)
for (const auto &value : sdpair.second.txt->values) {
if (value.size() > tag_path.size() && value.compare(0, tag_path.size(), tag_path) == 0) {
- reply.path = value.substr(tag_path.size());
+ path = std::move(value.substr(tag_path.size()));
} else if (value.size() > tag_version.size() && value.compare(0, tag_version.size(), tag_version) == 0) {
- reply.version = value.substr(tag_version.size());
+ version = std::move(value.substr(tag_version.size()));
}
}
}
+ BonjourReply reply(ip, srv.port, std::move(service_name), srv.hostname, std::move(path), std::move(version));
replyfn(std::move(reply));
}
}
@@ -595,15 +639,26 @@ void Bonjour::priv::lookup_perform()
udp::endpoint mcast(BonjourRequest::MCAST_IP4, BonjourRequest::MCAST_PORT);
socket.send_to(asio::buffer(brq->data), mcast);
- bool timeout = false;
+ bool expired = false;
+ bool retry = false;
asio::deadline_timer timer(io_service);
- timer.expires_from_now(boost::posix_time::seconds(10));
- timer.async_wait([=, &timeout](const error_code &error) {
- timeout = true;
- if (self->completefn) {
- self->completefn();
+ retries--;
+ std::function<void(const error_code &)> timer_handler = [&](const error_code &error) {
+ if (retries == 0 || error) {
+ expired = true;
+ if (self->completefn) {
+ self->completefn();
+ }
+ } else {
+ retry = true;
+ retries--;
+ timer.expires_from_now(boost::posix_time::seconds(timeout));
+ timer.async_wait(timer_handler);
}
- });
+ };
+
+ timer.expires_from_now(boost::posix_time::seconds(timeout));
+ timer.async_wait(timer_handler);
udp::endpoint recv_from;
const auto recv_handler = [&](const error_code &error, size_t bytes) {
@@ -612,8 +667,11 @@ void Bonjour::priv::lookup_perform()
socket.async_receive_from(asio::buffer(buffer, buffer.size()), recv_from, recv_handler);
while (io_service.run_one()) {
- if (timeout) {
+ if (expired) {
socket.cancel();
+ } else if (retry) {
+ retry = false;
+ socket.send_to(asio::buffer(brq->data), mcast);
} else {
buffer.resize(DnsMessage::MAX_SIZE);
socket.async_receive_from(asio::buffer(buffer, buffer.size()), recv_from, recv_handler);
@@ -626,13 +684,39 @@ void Bonjour::priv::lookup_perform()
// API - public part
-BonjourReply::BonjourReply(boost::asio::ip::address ip, std::string service_name, std::string hostname) :
+BonjourReply::BonjourReply(boost::asio::ip::address ip, uint16_t port, std::string service_name, std::string hostname, std::string path, std::string version) :
ip(std::move(ip)),
+ port(port),
service_name(std::move(service_name)),
hostname(std::move(hostname)),
- path("/"),
- version("Unknown")
-{}
+ path(path.empty() ? std::move(std::string("/")) : std::move(path)),
+ version(version.empty() ? std::move(std::string("Unknown")) : std::move(version))
+{
+ std::string proto;
+ std::string port_suffix;
+ if (port == 443) { proto = "https://"; }
+ if (port != 443 && port != 80) { port_suffix = std::to_string(port).insert(0, 1, ':'); }
+ if (this->path[0] != '/') { this->path.insert(0, 1, '/'); }
+ full_address = proto + ip.to_string() + port_suffix;
+ if (this->path != "/") { full_address += path; }
+}
+
+bool BonjourReply::operator==(const BonjourReply &other) const
+{
+ return this->full_address == other.full_address
+ && this->service_name == other.service_name;
+}
+
+bool BonjourReply::operator<(const BonjourReply &other) const
+{
+ if (this->ip != other.ip) {
+ // So that the common case doesn't involve string comparison
+ return this->ip < other.ip;
+ } else {
+ auto cmp = this->full_address.compare(other.full_address);
+ return cmp != 0 ? cmp < 0 : this->service_name < other.service_name;
+ }
+}
std::ostream& operator<<(std::ostream &os, const BonjourReply &reply)
{
@@ -641,6 +725,7 @@ std::ostream& operator<<(std::ostream &os, const BonjourReply &reply)
return os;
}
+
Bonjour::Bonjour(std::string service, std::string protocol) :
p(new priv(std::move(service), std::move(protocol)))
{}
@@ -660,6 +745,12 @@ Bonjour& Bonjour::set_timeout(unsigned timeout)
return *this;
}
+Bonjour& Bonjour::set_retries(unsigned retries)
+{
+ if (p && retries > 0) { p->retries = retries; }
+ return *this;
+}
+
Bonjour& Bonjour::on_reply(ReplyFn fn)
{
if (p) { p->replyfn = std::move(fn); }
@@ -677,7 +768,7 @@ Bonjour::Ptr Bonjour::lookup()
auto self = std::make_shared<Bonjour>(std::move(*this));
if (self->p) {
- auto io_thread = std::thread([self](){
+ auto io_thread = std::thread([self]() {
self->p->lookup_perform();
});
self->p->io_thread = std::move(io_thread);
@@ -687,18 +778,4 @@ Bonjour::Ptr Bonjour::lookup()
}
-void Bonjour::pokus() // XXX
-{
- auto bonjour = Bonjour("octoprint")
- .set_timeout(15)
- .on_reply([](BonjourReply &&reply) {
- std::cerr << "BonjourReply: " << reply << std::endl;
- })
- .on_complete([](){
- std::cerr << "MDNS lookup complete" << std::endl;
- })
- .lookup();
-}
-
-
}
diff --git a/xs/src/slic3r/Utils/Bonjour.hpp b/xs/src/slic3r/Utils/Bonjour.hpp
index 285625c04..63f34638c 100644
--- a/xs/src/slic3r/Utils/Bonjour.hpp
+++ b/xs/src/slic3r/Utils/Bonjour.hpp
@@ -1,26 +1,31 @@
#ifndef slic3r_Bonjour_hpp_
#define slic3r_Bonjour_hpp_
+#include <cstdint>
#include <memory>
#include <string>
#include <functional>
-// #include <ostream>
#include <boost/asio/ip/address.hpp>
namespace Slic3r {
-// TODO: reply data structure
struct BonjourReply
{
boost::asio::ip::address ip;
+ uint16_t port;
std::string service_name;
std::string hostname;
+ std::string full_address;
std::string path;
std::string version;
- BonjourReply(boost::asio::ip::address ip, std::string service_name, std::string hostname);
+ BonjourReply() = delete;
+ BonjourReply(boost::asio::ip::address ip, uint16_t port, std::string service_name, std::string hostname, std::string path, std::string version);
+
+ bool operator==(const BonjourReply &other) const;
+ bool operator<(const BonjourReply &other) const;
};
std::ostream& operator<<(std::ostream &, const BonjourReply &);
@@ -32,7 +37,7 @@ private:
struct priv;
public:
typedef std::shared_ptr<Bonjour> Ptr;
- typedef std::function<void(BonjourReply &&reply)> ReplyFn;
+ typedef std::function<void(BonjourReply &&)> ReplyFn;
typedef std::function<void()> CompleteFn;
Bonjour(std::string service, std::string protocol = "tcp");
@@ -40,12 +45,15 @@ public:
~Bonjour();
Bonjour& set_timeout(unsigned timeout);
+ Bonjour& set_retries(unsigned retries);
+ // ^ Note: By default there is 1 retry (meaning 1 broadcast is sent).
+ // Timeout is per one retry, ie. total time spent listening = retries * timeout.
+ // If retries > 1, then care needs to be taken as more than one reply from the same service may be received.
+
Bonjour& on_reply(ReplyFn fn);
Bonjour& on_complete(CompleteFn fn);
Ptr lookup();
-
- static void pokus(); // XXX: remove
private:
std::unique_ptr<priv> p;
};
diff --git a/xs/src/slic3r/Utils/Http.cpp b/xs/src/slic3r/Utils/Http.cpp
index 45a350a59..de28904e2 100644
--- a/xs/src/slic3r/Utils/Http.cpp
+++ b/xs/src/slic3r/Utils/Http.cpp
@@ -3,7 +3,6 @@
#include <cstdlib>
#include <functional>
#include <thread>
-#include <iostream>
#include <tuple>
#include <boost/format.hpp>
@@ -45,7 +44,9 @@ struct Http::priv
priv(const std::string &url);
~priv();
+ static bool ca_file_supported(::CURL *curl);
static size_t writecb(void *data, size_t size, size_t nmemb, void *userp);
+ std::string curl_error(CURLcode curlcode);
std::string body_size_error();
void http_perform();
};
@@ -71,6 +72,29 @@ Http::priv::~priv()
::curl_slist_free_all(headerlist);
}
+bool Http::priv::ca_file_supported(::CURL *curl)
+{
+#ifdef _WIN32
+ bool res = false;
+#else
+ bool res = true;
+#endif
+
+ if (curl == nullptr) { return res; }
+
+#if LIBCURL_VERSION_MAJOR >= 7 && LIBCURL_VERSION_MINOR >= 48
+ ::curl_tlssessioninfo *tls;
+ if (::curl_easy_getinfo(curl, CURLINFO_TLS_SSL_PTR, &tls) == CURLE_OK) {
+ if (tls->backend == CURLSSLBACKEND_SCHANNEL || tls->backend == CURLSSLBACKEND_DARWINSSL) {
+ // With Windows and OS X native SSL support, cert files cannot be set
+ res = false;
+ }
+ }
+#endif
+
+ return res;
+}
+
size_t Http::priv::writecb(void *data, size_t size, size_t nmemb, void *userp)
{
auto self = static_cast<priv*>(userp);
@@ -88,6 +112,14 @@ size_t Http::priv::writecb(void *data, size_t size, size_t nmemb, void *userp)
return realsize;
}
+std::string Http::priv::curl_error(CURLcode curlcode)
+{
+ return (boost::format("%1% (%2%)")
+ % ::curl_easy_strerror(curlcode)
+ % curlcode
+ ).str();
+}
+
std::string Http::priv::body_size_error()
{
return (boost::format("HTTP body data size exceeded limit (%1% bytes)") % limit).str();
@@ -121,7 +153,7 @@ void Http::priv::http_perform()
if (res == CURLE_WRITE_ERROR) {
error = std::move(body_size_error());
} else {
- error = ::curl_easy_strerror(res);
+ error = std::move(curl_error(res));
};
if (errorfn) {
@@ -180,7 +212,7 @@ Http& Http::remove_header(std::string name)
Http& Http::ca_file(const std::string &name)
{
- if (p) {
+ if (p && priv::ca_file_supported(p->curl)) {
::curl_easy_setopt(p->curl, CURLOPT_CAINFO, name.c_str());
}
@@ -257,5 +289,13 @@ Http Http::post(std::string url)
return http;
}
+bool Http::ca_file_supported()
+{
+ ::CURL *curl = ::curl_easy_init();
+ bool res = priv::ca_file_supported(curl);
+ if (curl != nullptr) { ::curl_easy_cleanup(curl); }
+ return res;
+}
+
}
diff --git a/xs/src/slic3r/Utils/Http.hpp b/xs/src/slic3r/Utils/Http.hpp
index c591e17c5..6ac5fcce1 100644
--- a/xs/src/slic3r/Utils/Http.hpp
+++ b/xs/src/slic3r/Utils/Http.hpp
@@ -41,6 +41,7 @@ public:
Ptr perform();
void perform_sync();
+ static bool ca_file_supported();
private:
Http(const std::string &url);
diff --git a/xs/src/slic3r/Utils/OctoPrint.cpp b/xs/src/slic3r/Utils/OctoPrint.cpp
index 58530833b..5bf51f470 100644
--- a/xs/src/slic3r/Utils/OctoPrint.cpp
+++ b/xs/src/slic3r/Utils/OctoPrint.cpp
@@ -20,16 +20,19 @@ OctoPrint::OctoPrint(DynamicPrintConfig *config) :
cafile(config->opt_string("octoprint_cafile"))
{}
-std::string OctoPrint::test() const
+bool OctoPrint::test(wxString &msg) const
{
// Since the request is performed synchronously here,
- // it is ok to refer to `res` from within the closure
- std::string res;
+ // it is ok to refer to `msg` from within the closure
- auto http = Http::get(std::move(make_url("api/version")));
+ bool res = true;
+
+ auto url = std::move(make_url("api/version"));
+ auto http = Http::get(std::move(url));
set_auth(http);
http.on_error([&](std::string, std::string error, unsigned status) {
- res = format_error(error, status);
+ res = false;
+ msg = format_error(error, status);
})
.perform_sync();
@@ -43,21 +46,26 @@ void OctoPrint::send_gcode(int windowId, int completeEvt, int errorEvt, const st
http.form_add("print", print ? "true" : "false")
.form_add_file("file", filename)
.on_complete([=](std::string body, unsigned status) {
- wxWindow *window = GUI::get_widget_by_id(windowId);
+ wxWindow *window = wxWindow::FindWindowById(windowId);
+ if (window == nullptr) { return; }
+
wxCommandEvent* evt = new wxCommandEvent(completeEvt);
- evt->SetString("G-code file successfully uploaded to the OctoPrint server");
+ evt->SetString(_(L("G-code file successfully uploaded to the OctoPrint server")));
evt->SetInt(100);
wxQueueEvent(window, evt);
})
.on_error([=](std::string body, std::string error, unsigned status) {
- wxWindow *window = GUI::get_widget_by_id(windowId);
+ wxWindow *window = wxWindow::FindWindowById(windowId);
+ if (window == nullptr) { return; }
wxCommandEvent* evt_complete = new wxCommandEvent(completeEvt);
evt_complete->SetInt(100);
wxQueueEvent(window, evt_complete);
wxCommandEvent* evt_error = new wxCommandEvent(errorEvt);
- evt_error->SetString(wxString::Format("Error while uploading to the OctoPrint server: %s", format_error(error, status)));
+ evt_error->SetString(wxString::Format("%s: %s",
+ _(L("Error while uploading to the OctoPrint server")),
+ format_error(error, status)));
wxQueueEvent(window, evt_error);
})
.perform();
@@ -85,19 +93,15 @@ std::string OctoPrint::make_url(const std::string &path) const
}
}
-std::string OctoPrint::format_error(std::string error, unsigned status)
+wxString OctoPrint::format_error(std::string error, unsigned status)
{
- if (status != 0) {
- std::string res{"HTTP "};
- res.append(std::to_string(status));
+ const wxString wxerror = error;
- if (status == 401) {
- res.append(": Invalid API key");
- }
-
- return std::move(res);
+ if (status != 0) {
+ return wxString::Format("HTTP %u: %s", status,
+ (status == 401 ? _(L("Invalid API key")) : wxerror));
} else {
- return std::move(error);
+ return std::move(wxerror);
}
}
diff --git a/xs/src/slic3r/Utils/OctoPrint.hpp b/xs/src/slic3r/Utils/OctoPrint.hpp
index eca3baa63..1f544295c 100644
--- a/xs/src/slic3r/Utils/OctoPrint.hpp
+++ b/xs/src/slic3r/Utils/OctoPrint.hpp
@@ -2,8 +2,8 @@
#define slic3r_OctoPrint_hpp_
#include <string>
+#include <wx/string.h>
-// #include "Http.hpp" // XXX: ?
namespace Slic3r {
@@ -16,8 +16,7 @@ class OctoPrint
public:
OctoPrint(DynamicPrintConfig *config);
- std::string test() const;
- // XXX: style
+ bool test(wxString &curl_msg) const;
void send_gcode(int windowId, int completeEvt, int errorEvt, const std::string &filename, bool print = false) const;
private:
std::string host;
@@ -26,7 +25,7 @@ private:
void set_auth(Http &http) const;
std::string make_url(const std::string &path) const;
- static std::string format_error(std::string error, unsigned status);
+ static wxString format_error(std::string error, unsigned status);
};