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:
Diffstat (limited to 'src/slic3r/GUI/BonjourDialog.cpp')
-rw-r--r--src/slic3r/GUI/BonjourDialog.cpp200
1 files changed, 200 insertions, 0 deletions
diff --git a/src/slic3r/GUI/BonjourDialog.cpp b/src/slic3r/GUI/BonjourDialog.cpp
new file mode 100644
index 000000000..11cfea642
--- /dev/null
+++ b/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();
+ }
+}
+
+
+}