diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp
index bd7772f80..794390133 100644
--- a/src/libslic3r/Print.cpp
+++ b/src/libslic3r/Print.cpp
@@ -661,6 +661,9 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
 
     // Make a copy of the config, normalize it.
     DynamicPrintConfig config(config_in);
+	config.option("print_settings_id",    true);
+	config.option("filament_settings_id", true);
+	config.option("printer_settings_id",  true);
     config.normalize();
     // Collect changes to print config.
     t_config_option_keys print_diff  = m_config.diff(config);
@@ -688,9 +691,9 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
 		PlaceholderParser &pp = this->placeholder_parser();
 		pp.apply_only(config, placeholder_parser_diff);
         // Set the profile aliases for the PrintBase::output_filename()
-        pp.set("print_preset",    config_in.option("print_settings_id"   )->clone());
-        pp.set("filament_preset", config_in.option("filament_settings_id")->clone());
-        pp.set("printer_preset",  config_in.option("printer_settings_id" )->clone());
+		pp.set("print_preset",    config.option("print_settings_id")->clone());
+		pp.set("filament_preset", config.option("filament_settings_id")->clone());
+		pp.set("printer_preset",  config.option("printer_settings_id")->clone());
     }
 
     // It is also safe to change m_config now after this->invalidate_state_by_config_options() call.
diff --git a/src/libslic3r/PrintBase.hpp b/src/libslic3r/PrintBase.hpp
index e01b678a5..2d47d3567 100644
--- a/src/libslic3r/PrintBase.hpp
+++ b/src/libslic3r/PrintBase.hpp
@@ -272,6 +272,7 @@ public:
             NO_RELOAD_SCENE                 = 0,
             RELOAD_SCENE                    = 1 << 1,
             RELOAD_SLA_SUPPORT_POINTS       = 1 << 2,
+            RELOAD_SLA_PREVIEW              = 1 << 3,
         };
         // Bitmap of FlagBits
         unsigned int    flags;
diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp
index b6c8aad13..0e8e717cc 100644
--- a/src/libslic3r/SLAPrint.cpp
+++ b/src/libslic3r/SLAPrint.cpp
@@ -118,6 +118,9 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConf
 
     // Make a copy of the config, normalize it.
     DynamicPrintConfig config(config_in);
+	config.option("sla_print_settings_id",    true);
+	config.option("sla_material_settings_id", true);
+	config.option("printer_settings_id",      true);
     config.normalize();
     // Collect changes to print config.
     t_config_option_keys print_diff    = m_print_config.diff(config);    
@@ -151,9 +154,9 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConf
 		PlaceholderParser &pp = this->placeholder_parser();
 		pp.apply_config(config);
         // Set the profile aliases for the PrintBase::output_filename()
-		pp.set("print_preset", config_in.option("sla_print_settings_id")->clone());
-		pp.set("material_preset", config_in.option("sla_material_settings_id")->clone());
-		pp.set("printer_preset", config_in.option("printer_settings_id")->clone());
+		pp.set("print_preset",    config.option("sla_print_settings_id")->clone());
+		pp.set("material_preset", config.option("sla_material_settings_id")->clone());
+		pp.set("printer_preset",  config.option("printer_settings_id")->clone());
     }
 
     // It is also safe to change m_config now after this->invalidate_state_by_config_options() call.
@@ -821,7 +824,7 @@ void SLAPrint::process()
 
     // We have the layer polygon collection but we need to unite them into
     // an index where the key is the height level in discrete levels (clipper)
-    auto index_slices = [ilhd](SLAPrintObject& po) {
+    auto index_slices = [this, ilhd](SLAPrintObject& po) {
         po.m_slice_index.clear();
         auto sih = LevelID(scale_(ilhd));
 
@@ -890,6 +893,9 @@ void SLAPrint::process()
                 sr.support_slices_idx = SLAPrintObject::SliceRecord::Idx(i);
             }
         }
+
+        // Using RELOAD_SLA_PREVIEW to tell the Plater to pass the update status to the 3D preview to load the SLA slices.
+        report_status(*this, -2, "", SlicingStatus::RELOAD_SLA_PREVIEW);
     };
 
     // Rasterizing the model objects, and their supports
diff --git a/src/slic3r.cpp b/src/slic3r.cpp
index f88b84ce7..b60c5b1dd 100644
--- a/src/slic3r.cpp
+++ b/src/slic3r.cpp
@@ -68,7 +68,7 @@ int CLI::run(int argc, char **argv)
     // load config files supplied via --load
 	for (auto const &file : load_configs) {
         if (! boost::filesystem::exists(file)) {
-            if (m_config.opt_bool("ignore_nonexistent_file")) {
+            if (m_config.opt_bool("ignore_nonexistent_config")) {
                 continue;
             } else {
                 boost::nowide::cerr << "No such file: " << file << std::endl;
@@ -132,9 +132,9 @@ int CLI::run(int argc, char **argv)
 
     // Initialize full print configs for both the FFF and SLA technologies.
     FullPrintConfig    fff_print_config;
-    SLAFullPrintConfig sla_print_config;
+//    SLAFullPrintConfig sla_print_config;
     fff_print_config.apply(m_print_config, true);
-    sla_print_config.apply(m_print_config, true);
+//    sla_print_config.apply(m_print_config, true);
     
     // Loop through transform options.
     for (auto const &opt_key : m_transforms) {
@@ -530,7 +530,7 @@ bool CLI::setup(int argc, char **argv)
 	for (auto const &opt_key : opt_order) {
 		if (cli_actions_config_def.has(opt_key))
 			m_actions.emplace_back(opt_key);
-		if (cli_transform_config_def.has(opt_key))
+		else if (cli_transform_config_def.has(opt_key))
 			m_transforms.emplace_back(opt_key);
 	}
 
diff --git a/src/slic3r/GUI/BonjourDialog.cpp b/src/slic3r/GUI/BonjourDialog.cpp
index 68e353ebe..ec6b2f0c9 100644
--- a/src/slic3r/GUI/BonjourDialog.cpp
+++ b/src/slic3r/GUI/BonjourDialog.cpp
@@ -10,8 +10,10 @@
 #include <wx/listctrl.h>
 #include <wx/stattext.h>
 #include <wx/timer.h>
+#include <wx/wupdlock.h>
 
 #include "slic3r/GUI/GUI.hpp"
+#include "slic3r/GUI/GUI_App.hpp"
 #include "slic3r/GUI/I18N.hpp"
 #include "slic3r/Utils/Bonjour.hpp"
 
@@ -49,31 +51,36 @@ struct LifetimeGuard
 	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)
+BonjourDialog::BonjourDialog(wxWindow *parent, Slic3r::PrinterTechnology tech)
+	: wxDialog(parent, wxID_ANY, _(L("Network lookup")), wxDefaultPosition, wxDefaultSize, wxRESIZE_BORDER)
+	, list(new wxListView(this, wxID_ANY))
+	, replies(new ReplySet)
+	, label(new wxStaticText(this, wxID_ANY, ""))
+	, timer(new wxTimer())
+	, timer_state(0)
+	, tech(tech)
 {
+	const int em = GUI::wxGetApp().em_unit();
+	list->SetMinSize(wxSize(80 * em, 30 * em));
+
 	wxBoxSizer *vsizer = new wxBoxSizer(wxVERTICAL);
 
-	vsizer->Add(label, 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, 10);
+	vsizer->Add(label, 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, em);
 
 	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);
+	list->AppendColumn(_(L("Address")), wxLIST_FORMAT_LEFT, 5 * em);
+	list->AppendColumn(_(L("Hostname")), wxLIST_FORMAT_LEFT, 10 * em);
+	list->AppendColumn(_(L("Service name")), wxLIST_FORMAT_LEFT, 20 * em);
+	if (tech == ptFFF) {
+		list->AppendColumn(_(L("OctoPrint version")), wxLIST_FORMAT_LEFT, 5 * em);
+	}
 
-	vsizer->Add(list, 1, wxEXPAND | wxALL, 10);
+	vsizer->Add(list, 1, wxEXPAND | wxALL, em);
 
 	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);
+	button_sizer->Add(new wxButton(this, wxID_OK, "OK"), 0, wxALL, em);
+	button_sizer->Add(new wxButton(this, wxID_CANCEL, "Cancel"), 0, wxALL, em);
 	// ^ Note: The Ok/Cancel labels are translated by wxWidgets
 
 	vsizer->Add(button_sizer, 0, wxALIGN_CENTER);
@@ -110,7 +117,11 @@ bool BonjourDialog::show_and_lookup()
 	// so that both threads can access it safely.
 	auto dguard = std::make_shared<LifetimeGuard>(this);
 
+	// Note: More can be done here when we support discovery of hosts other than Octoprint and SL1
+	Bonjour::TxtKeys txt_keys { "version", "model" };
+
 	bonjour = std::move(Bonjour("octoprint")
+		.set_txt_keys(std::move(txt_keys))
 		.set_retries(3)
 		.set_timeout(4)
 		.on_reply([dguard](BonjourReply &&reply) {
@@ -157,9 +168,20 @@ void BonjourDialog::on_reply(BonjourReplyEvent &e)
 		return;
 	}
 
+	// Filter replies based on selected technology
+	const auto model = e.reply.txt_data.find("model");
+	const bool sl1 = model != e.reply.txt_data.end() && model->second == "SL1";
+	if (tech == ptFFF && sl1 || tech == ptSLA && !sl1) {
+		return;
+	}
+
 	replies->insert(std::move(e.reply));
 
 	auto selected = get_selected();
+
+	wxWindowUpdateLocker freeze_guard(this);
+	(void)freeze_guard;
+
 	list->DeleteAllItems();
 
 	// The whole list is recreated so that we benefit from it already being sorted in the set.
@@ -168,12 +190,20 @@ void BonjourDialog::on_reply(BonjourReplyEvent &e)
 		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);
+
+		if (tech == ptFFF) {
+			const auto it = reply.txt_data.find("version");
+			if (it != reply.txt_data.end()) {
+				list->SetItem(item, 3, GUI::from_u8(it->second));
+			}
+		}
 	}
 
-	for (int i = 0; i < 4; i++) {
-		this->list->SetColumnWidth(i, wxLIST_AUTOSIZE);
-		if (this->list->GetColumnWidth(i) < 100) { this->list->SetColumnWidth(i, 100); }
+	const int em = GUI::wxGetApp().em_unit();
+
+	for (int i = 0; i < list->GetColumnCount(); i++) {
+		list->SetColumnWidth(i, wxLIST_AUTOSIZE);
+		if (list->GetColumnWidth(i) < 10 * em) { list->SetColumnWidth(i, 10 * em); }
 	}
 
 	if (!selected.IsEmpty()) {
diff --git a/src/slic3r/GUI/BonjourDialog.hpp b/src/slic3r/GUI/BonjourDialog.hpp
index e3f53790b..a9a33d522 100644
--- a/src/slic3r/GUI/BonjourDialog.hpp
+++ b/src/slic3r/GUI/BonjourDialog.hpp
@@ -5,6 +5,8 @@
 
 #include <wx/dialog.h>
 
+#include "libslic3r/PrintConfig.hpp"
+
 class wxListView;
 class wxStaticText;
 class wxTimer;
@@ -21,7 +23,7 @@ class ReplySet;
 class BonjourDialog: public wxDialog
 {
 public:
-	BonjourDialog(wxWindow *parent);
+	BonjourDialog(wxWindow *parent, Slic3r::PrinterTechnology);
 	BonjourDialog(BonjourDialog &&) = delete;
 	BonjourDialog(const BonjourDialog &) = delete;
 	BonjourDialog &operator=(BonjourDialog &&) = delete;
@@ -37,6 +39,7 @@ private:
 	std::shared_ptr<Bonjour> bonjour;
 	std::unique_ptr<wxTimer> timer;
 	unsigned timer_state;
+	Slic3r::PrinterTechnology tech;
 
 	void on_reply(BonjourReplyEvent &);
 	void on_timer(wxTimerEvent &);
diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index 87e004ee7..f76764c5f 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -3045,6 +3045,7 @@ void GLCanvas3D::Gizmos::do_render_overlay(const GLCanvas3D& canvas, const GLCan
     float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
 
     float height = get_total_overlay_height();
+    float width = get_total_overlay_width();
 #if ENABLE_SVG_ICONS
     float scaled_border = m_overlay_border * m_overlay_scale * inv_zoom;
 #else
@@ -3056,7 +3057,7 @@ void GLCanvas3D::Gizmos::do_render_overlay(const GLCanvas3D& canvas, const GLCan
 
     float left = top_x;
     float top = top_y;
-    float right = left + get_total_overlay_width() * inv_zoom;
+    float right = left + width * inv_zoom;
     float bottom = top - height * inv_zoom;
 
     // renders background
@@ -3164,24 +3165,24 @@ void GLCanvas3D::Gizmos::do_render_overlay(const GLCanvas3D& canvas, const GLCan
 #if ENABLE_SVG_ICONS
         float u_icon_size = m_overlay_icons_size * m_overlay_scale * inv_tex_width;
         float v_icon_size = m_overlay_icons_size * m_overlay_scale * inv_tex_height;
-        float top = sprite_id * v_icon_size;
-        float left = state * u_icon_size;
-        float bottom = top + v_icon_size;
-        float right = left + u_icon_size;
+        float v_top = sprite_id * v_icon_size;
+        float u_left = state * u_icon_size;
+        float v_bottom = v_top + v_icon_size;
+        float u_right = u_left + u_icon_size;
 #else
         float uv_icon_size = (float)m_icons_texture.metadata.icon_size * inv_texture_size;
-        float top = sprite_id * uv_icon_size;
-        float left = state * uv_icon_size;
-        float bottom = top + uv_icon_size;
-        float right = left + uv_icon_size;
+        float v_top = sprite_id * uv_icon_size;
+        float u_left = state * uv_icon_size;
+        float v_bottom = v_top + uv_icon_size;
+        float u_right = u_left + uv_icon_size;
 #endif // ENABLE_SVG_ICONS
 
-        GLTexture::render_sub_texture(icons_texture_id, top_x, top_x + scaled_icons_size, top_y - scaled_icons_size, top_y, { { left, bottom }, { right, bottom }, { right, top }, { left, top } });
+        GLTexture::render_sub_texture(icons_texture_id, top_x, top_x + scaled_icons_size, top_y - scaled_icons_size, top_y, { { u_left, v_bottom }, { u_right, v_bottom }, { u_right, v_top }, { u_left, v_top } });
 #if ENABLE_IMGUI
         if (it->second->get_state() == GLGizmoBase::On) {
             float toolbar_top = (float)cnv_h - canvas.m_view_toolbar.get_height();
 #if ENABLE_SVG_ICONS
-            it->second->render_input_window(2.0f * m_overlay_border + m_overlay_icons_size, 0.5f * cnv_h - top_y * zoom, toolbar_top, selection);
+            it->second->render_input_window(width, 0.5f * cnv_h - top_y * zoom, toolbar_top, selection);
 #else
             it->second->render_input_window(2.0f * m_overlay_border + icon_size * zoom, 0.5f * cnv_h - top_y * zoom, toolbar_top, selection);
 #endif // ENABLE_SVG_ICONS
diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp
index f6ccbbd7d..7eef82063 100644
--- a/src/slic3r/GUI/GUI_ObjectList.cpp
+++ b/src/slic3r/GUI/GUI_ObjectList.cpp
@@ -44,12 +44,12 @@ static PrinterTechnology printer_technology()
 }
 
 // Config from current edited printer preset
-DynamicPrintConfig& printer_config()
+static DynamicPrintConfig& printer_config()
 {
     return wxGetApp().preset_bundle->printers.get_edited_preset().config;
 }
 
-int extruders_count()
+static int extruders_count()
 {
     return printer_technology() == ptSLA ? 1 :
         printer_config().option<ConfigOptionFloats>("nozzle_diameter")->values.size();
diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp
index 553978972..6773d1513 100644
--- a/src/slic3r/GUI/GUI_Preview.cpp
+++ b/src/slic3r/GUI/GUI_Preview.cpp
@@ -752,10 +752,11 @@ void Preview::load_print_as_fff()
 
     if (IsShown())
     {
-        if (gcode_preview_data_valid)
+        if (gcode_preview_data_valid) {
             // Load the real G-code preview.
             m_canvas->load_gcode_preview(*m_gcode_preview_data, colors);
-        else
+            m_loaded = true;
+        } else
             // Load the initial preview based on slices, not the final G-code.
             m_canvas->load_preview(colors, color_print_values);
         show_hide_ui_elements(gcode_preview_data_valid ? "full" : "simple");
@@ -767,7 +768,6 @@ void Preview::load_print_as_fff()
             m_canvas_widget->Refresh();
         } else
             update_sliders(zs);
-        m_loaded = true;
     }
 }
 
diff --git a/src/slic3r/GUI/MsgDialog.cpp b/src/slic3r/GUI/MsgDialog.cpp
index 176d19fb4..961888455 100644
--- a/src/slic3r/GUI/MsgDialog.cpp
+++ b/src/slic3r/GUI/MsgDialog.cpp
@@ -24,12 +24,11 @@ namespace GUI {
 
 
 MsgDialog::MsgDialog(wxWindow *parent, const wxString &title, const wxString &headline, wxWindowID button_id) :
-// 	MsgDialog(parent, title, headline, wxBitmap(from_u8(Slic3r::var("Slic3r_192px.png")), wxBITMAP_TYPE_PNG), button_id)
 	MsgDialog(parent, title, headline, create_scaled_bitmap("Slic3r_192px.png"), button_id)
 {}
 
 MsgDialog::MsgDialog(wxWindow *parent, const wxString &title, const wxString &headline, wxBitmap bitmap, wxWindowID button_id) :
-	wxDialog(parent, wxID_ANY, title),
+	wxDialog(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, wxRESIZE_BORDER),
 	boldfont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)),
 	content_sizer(new wxBoxSizer(wxVERTICAL)),
 	btn_sizer(new wxBoxSizer(wxHORIZONTAL))
@@ -70,7 +69,6 @@ MsgDialog::~MsgDialog() {}
 
 ErrorDialog::ErrorDialog(wxWindow *parent, const wxString &msg)
 	: MsgDialog(parent, _(L("Slic3r error")), _(L("Slic3r has encountered an error")),
-// 		wxBitmap(from_u8(Slic3r::var("Slic3r_192px_grayscale.png")), wxBITMAP_TYPE_PNG),
         create_scaled_bitmap("Slic3r_192px_grayscale.png"),
 		wxID_NONE)
 	, msg(msg)
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index 85349a52a..26c84e06b 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -2495,8 +2495,10 @@ void Plater::priv::on_select_preset(wxCommandEvent &evt)
 
 void Plater::priv::on_slicing_update(SlicingStatusEvent &evt)
 {
-    this->statusbar()->set_progress(evt.status.percent);
-    this->statusbar()->set_status_text(_(L(evt.status.text)) + wxString::FromUTF8("…"));
+    if (evt.status.percent >= -1) {
+        this->statusbar()->set_progress(evt.status.percent);
+        this->statusbar()->set_status_text(_(L(evt.status.text)) + wxString::FromUTF8("…"));
+    }
     if (evt.status.flags & PrintBase::SlicingStatus::RELOAD_SCENE) {
         switch (this->printer_technology) {
         case ptFFF:
@@ -2514,6 +2516,10 @@ void Plater::priv::on_slicing_update(SlicingStatusEvent &evt)
         // Update SLA gizmo  (reload_scene calls update_gizmos_data)
         q->canvas3D()->reload_scene(true);
     }
+    if (evt.status.flags & PrintBase::SlicingStatus::RELOAD_SLA_PREVIEW) {
+        // Update the SLA preview
+        this->preview->reload_print();
+    }
 }
 
 void Plater::priv::on_slicing_completed(wxCommandEvent &)
diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp
index 6c7f246a6..ae33443c3 100644
--- a/src/slic3r/GUI/Tab.cpp
+++ b/src/slic3r/GUI/Tab.cpp
@@ -74,7 +74,7 @@ void Tab::set_type()
 void Tab::create_preset_tab()
 {
 #ifdef __WINDOWS__
-    SetDoubleBuffered(true);
+//     SetDoubleBuffered(true);
 #endif //__WINDOWS__
 
     m_preset_bundle = wxGetApp().preset_bundle;
@@ -1612,25 +1612,21 @@ bool Tab::current_preset_is_dirty()
 
 void TabPrinter::build_printhost(ConfigOptionsGroup *optgroup)
 {
-	const bool sla = m_presets->get_selected_preset().printer_technology() == ptSLA;
+	const PrinterTechnology tech = m_presets->get_selected_preset().printer_technology();
 
 	// Only offer the host type selection for FFF, for SLA it's always the SL1 printer (at the moment)
-	if (! sla) {
+	if (tech == ptFFF) {
 		optgroup->append_single_option_line("host_type");
 	}
 
-	auto printhost_browse = [this, optgroup] (wxWindow* parent) {
-
-		// TODO: SLA Bonjour
-
+	auto printhost_browse = [=](wxWindow* parent) {
 		auto btn = m_printhost_browse_btn = new wxButton(parent, wxID_ANY, _(L(" Browse "))+dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT);
-// 		btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("zoom.png")), wxBITMAP_TYPE_PNG));
         btn->SetBitmap(create_scaled_bitmap("zoom.png"));
 		auto sizer = new wxBoxSizer(wxHORIZONTAL);
 		sizer->Add(btn);
 
-		btn->Bind(wxEVT_BUTTON, [this, parent, optgroup](wxCommandEvent &e) {
-			BonjourDialog dialog(parent);
+		btn->Bind(wxEVT_BUTTON, [=](wxCommandEvent &e) {
+			BonjourDialog dialog(parent, tech);
 			if (dialog.show_and_lookup()) {
 				optgroup->set_value("print_host", std::move(dialog.get_selected()), true);
 				optgroup->get_field("print_host")->field_changed();
@@ -1643,7 +1639,6 @@ void TabPrinter::build_printhost(ConfigOptionsGroup *optgroup)
 	auto print_host_test = [this](wxWindow* parent) {
 		auto btn = m_print_host_test_btn = new wxButton(parent, wxID_ANY, _(L("Test")), 
 			wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT);
-// 		btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("wrench.png")), wxBITMAP_TYPE_PNG));
         btn->SetBitmap(create_scaled_bitmap("wrench.png"));
 		auto sizer = new wxBoxSizer(wxHORIZONTAL);
 		sizer->Add(btn);
diff --git a/src/slic3r/Utils/Bonjour.cpp b/src/slic3r/Utils/Bonjour.cpp
index 4953cfc64..28b3b2228 100644
--- a/src/slic3r/Utils/Bonjour.cpp
+++ b/src/slic3r/Utils/Bonjour.cpp
@@ -5,6 +5,7 @@
 #include <array>
 #include <vector>
 #include <string>
+#include <map>
 #include <thread>
 #include <boost/optional.hpp>
 #include <boost/system/error_code.hpp>
@@ -33,7 +34,9 @@ namespace Slic3r {
 // the implementations has been tested with AFL.
 
 
-// Relevant RFC: https://www.ietf.org/rfc/rfc6762.txt
+// Relevant RFCs:
+//    https://tools.ietf.org/html/rfc6762.txt
+//    https://tools.ietf.org/html/rfc6763.txt
 
 
 struct DnsName: public std::string
@@ -156,9 +159,9 @@ struct DnsQuestion
 	uint16_t type;
 	uint16_t qclass;
 
-	DnsQuestion() :
-		type(0),
-		qclass(0)
+	DnsQuestion()
+		: type(0)
+		, qclass(0)
 	{}
 
 	static optional<DnsQuestion> decode(const std::vector<char> &buffer, size_t &offset)
@@ -187,10 +190,10 @@ struct DnsResource
 	uint32_t ttl;
 	std::vector<char> data;
 
-	DnsResource() :
-		type(0),
-		rclass(0),
-		ttl(0)
+	DnsResource()
+		: type(0)
+		, rclass(0)
+		, ttl(0)
 	{}
 
 	static optional<DnsResource> decode(const std::vector<char> &buffer, size_t &offset, size_t &dataoffset)
@@ -310,9 +313,9 @@ struct DnsRR_TXT
 		TAG = 0x10,
 	};
 
-	std::vector<std::string> values;
+	BonjourReply::TxtData data;
 
-	static optional<DnsRR_TXT> decode(const DnsResource &rr)
+	static optional<DnsRR_TXT> decode(const DnsResource &rr, const Bonjour::TxtKeys &txt_keys)
 	{
 		const size_t size = rr.data.size();
 		if (size < 2) {
@@ -328,11 +331,21 @@ struct DnsRR_TXT
 			}
 			++it;
 
-			std::string value(val_size, ' ');
-			std::copy(it, it + val_size, value.begin());
-			res.values.push_back(std::move(value));
+			const auto it_end = it + val_size;
+			const auto it_eq = std::find(it, it_end, '=');
+			if (it_eq > it && it_eq < it_end - 1) {
+				std::string key(it_eq - it, ' ');
+				std::copy(it, it_eq, key.begin());
 
-			it += val_size;
+				if (txt_keys.find(key) != txt_keys.end() || key == "path") {
+					// This key-value has been requested for
+					std::string value(it_end - it_eq - 1, ' ');
+					std::copy(it_eq + 1, it_end, value.begin());
+					res.data.insert(std::make_pair(std::move(key), std::move(value)));
+				}
+			}
+
+			it = it_end;
 		}
 
 		return std::move(res);
@@ -389,7 +402,7 @@ struct DnsMessage
 
 	DnsSDMap sdmap;
 
-	static optional<DnsMessage> decode(const std::vector<char> &buffer)
+	static optional<DnsMessage> decode(const std::vector<char> &buffer, const Bonjour::TxtKeys &txt_keys)
 	{
 		const auto size = buffer.size();
 		if (size < DnsHeader::SIZE + DnsQuestion::MIN_SIZE || size > MAX_SIZE) {
@@ -414,14 +427,15 @@ struct DnsMessage
 			if (!rr) {
 				return boost::none;
 			} else {
-				res.parse_rr(buffer, std::move(*rr), dataoffset);
+				res.parse_rr(buffer, std::move(*rr), dataoffset, txt_keys);
 			}
 		}
 
 		return std::move(res);
 	}
+
 private:
-	void parse_rr(const std::vector<char> &buffer, DnsResource &&rr, size_t dataoffset)
+	void parse_rr(const std::vector<char> &buffer, DnsResource &&rr, size_t dataoffset, const Bonjour::TxtKeys &txt_keys)
 	{
 		switch (rr.type) {
 			case DnsRR_A::TAG: DnsRR_A::decode(this->rr_a, rr); break;
@@ -432,7 +446,7 @@ private:
 				break;
 			}
 			case DnsRR_TXT::TAG: {
-				auto txt = DnsRR_TXT::decode(rr);
+				auto txt = DnsRR_TXT::decode(rr, txt_keys);
 				if (txt) { this->sdmap.insert_txt(std::move(rr.name), std::move(*txt)); }
 				break;
 			}
@@ -442,26 +456,28 @@ 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: [";
+	os << boost::format("DnsMessage(ID: %1%, Q: %2%, A: %3%, AAAA: %4%, services: [")
+		% msg.header.id
+		% (msg.question ? msg.question->name.c_str() : "none")
+		% (msg.rr_a ? msg.rr_a->ip.to_string() : "none")
+		% (msg.rr_aaaa ? msg.rr_aaaa->ip.to_string() : "none");
 
-		enum { SRV_PRINT_MAX = 3 };
-		unsigned i = 0;
-		for (const auto &sdpair : msg.sdmap) {
-			os << sdpair.first << ", ";
+	enum { SRV_PRINT_MAX = 3 };
+	unsigned i = 0;
+	for (const auto &sdpair : msg.sdmap) {
+		if (i > 0) { os << ", "; }
 
-			if (++i >= SRV_PRINT_MAX) {
-				os << "...";
-				break;
-			}
+		if (i < SRV_PRINT_MAX) {
+			os << sdpair.first;
+		} else {
+			os << "...";
+			break;
 		}
 
-		os << "])";
+		i++;
+	}
 
-	return os;
+	return os << "])";
 }
 
 
@@ -525,8 +541,9 @@ optional<BonjourRequest> BonjourRequest::make(const std::string &service, const
 struct Bonjour::priv
 {
 	const std::string service;
-	const std::string protocol;
-	const std::string service_dn;
+	std::string protocol;
+	std::string service_dn;
+	TxtKeys txt_keys;
 	unsigned timeout;
 	unsigned retries;
 
@@ -535,19 +552,18 @@ struct Bonjour::priv
 	Bonjour::ReplyFn replyfn;
 	Bonjour::CompleteFn completefn;
 
-	priv(std::string service, std::string protocol);
+	priv(std::string &&service);
 
 	std::string strip_service_dn(const std::string &service_name) const;
 	void udp_receive(udp::endpoint from, size_t bytes);
 	void lookup_perform();
 };
 
-Bonjour::priv::priv(std::string service, std::string protocol) :
-	service(std::move(service)),
-	protocol(std::move(protocol)),
-	service_dn((boost::format("_%1%._%2%.local") % this->service % this->protocol).str()),
-	timeout(10),
-	retries(1)
+Bonjour::priv::priv(std::string &&service)
+	: service(std::move(service))
+	, protocol("tcp")
+	, timeout(10)
+	, retries(1)
 {
 	buffer.resize(DnsMessage::MAX_SIZE);
 }
@@ -573,13 +589,13 @@ void Bonjour::priv::udp_receive(udp::endpoint from, size_t bytes)
 	}
 
 	buffer.resize(bytes);
-	const auto dns_msg = DnsMessage::decode(buffer);
+	auto dns_msg = DnsMessage::decode(buffer, txt_keys);
 	if (dns_msg) {
 		asio::ip::address ip = from.address();
 		if (dns_msg->rr_a) { ip = dns_msg->rr_a->ip; }
 		else if (dns_msg->rr_aaaa) { ip = dns_msg->rr_aaaa->ip; }
 
-		for (const auto &sdpair : dns_msg->sdmap) {
+		for (auto &sdpair : dns_msg->sdmap) {
 			if (! sdpair.second.srv) {
 				continue;
 			}
@@ -590,20 +606,12 @@ void Bonjour::priv::udp_receive(udp::endpoint from, size_t bytes)
 			std::string path;
 			std::string version;
 
+			BonjourReply::TxtData txt_data;
 			if (sdpair.second.txt) {
-				static const std::string tag_path = "path=";
-				static const std::string tag_version = "version=";
-
-				for (const auto &value : sdpair.second.txt->values) {
-					if (value.size() > tag_path.size() && value.compare(0, tag_path.size(), tag_path) == 0) {
-						path = std::move(value.substr(tag_path.size()));
-					} else if (value.size() > tag_version.size() && value.compare(0, tag_version.size(), tag_version) == 0) {
-						version = std::move(value.substr(tag_version.size()));
-					}
-				}
+				txt_data = std::move(sdpair.second.txt->data);
 			}
 
-			BonjourReply reply(ip, srv.port, std::move(service_name), srv.hostname, std::move(path), std::move(version));
+			BonjourReply reply(ip, srv.port, std::move(service_name), srv.hostname, std::move(txt_data));
 			replyfn(std::move(reply));
 		}
 	}
@@ -611,6 +619,8 @@ void Bonjour::priv::udp_receive(udp::endpoint from, size_t bytes)
 
 void Bonjour::priv::lookup_perform()
 {
+	service_dn = (boost::format("_%1%._%2%.local") % service % protocol).str();
+
 	const auto brq = BonjourRequest::make(service, protocol);
 	if (!brq) {
 		return;
@@ -671,21 +681,29 @@ void Bonjour::priv::lookup_perform()
 
 // API - public part
 
-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(path.empty() ? std::move(std::string("/")) : std::move(path)),
-	version(version.empty() ? std::move(std::string("Unknown")) : std::move(version))
+BonjourReply::BonjourReply(boost::asio::ip::address ip, uint16_t port, std::string service_name, std::string hostname, BonjourReply::TxtData txt_data)
+	: ip(std::move(ip))
+	, port(port)
+	, service_name(std::move(service_name))
+	, hostname(std::move(hostname))
+	, txt_data(std::move(txt_data))
 {
 	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, '/'); }
+
+	std::string path = this->path();
+	if (path[0] != '/') { path.insert(0, 1, '/'); }
 	full_address = proto + ip.to_string() + port_suffix;
-	if (this->path != "/") { full_address += path; }
+	if (path != "/") { full_address += path; }
+	txt_data["path"] = std::move(path);
+}
+
+std::string BonjourReply::path() const
+{
+	const auto it = txt_data.find("path");
+	return it != txt_data.end() ? it->second : std::string("/");
 }
 
 bool BonjourReply::operator==(const BonjourReply &other) const
@@ -707,14 +725,22 @@ bool BonjourReply::operator<(const BonjourReply &other) const
 
 std::ostream& operator<<(std::ostream &os, const BonjourReply &reply)
 {
-	os << "BonjourReply(" << reply.ip.to_string() << ", " << reply.service_name << ", "
-		<< reply.hostname << ", " << reply.path << ", " << reply.version << ")";
-	return os;
+	os << boost::format("BonjourReply(%1%, %2%, %3%, %4%")
+		% reply.ip.to_string()
+		% reply.service_name
+		% reply.hostname
+		% reply.full_address;
+
+	for (const auto &kv : reply.txt_data) {
+		os << boost::format(", %1%=%2%") % kv.first % kv.second;
+	}
+
+	return os << ')';
 }
 
 
-Bonjour::Bonjour(std::string service, std::string protocol) :
-	p(new priv(std::move(service), std::move(protocol)))
+Bonjour::Bonjour(std::string service)
+	: p(new priv(std::move(service)))
 {}
 
 Bonjour::Bonjour(Bonjour &&other) : p(std::move(other.p)) {}
@@ -726,6 +752,18 @@ Bonjour::~Bonjour()
 	}
 }
 
+Bonjour& Bonjour::set_protocol(std::string protocol)
+{
+	if (p) { p->protocol = std::move(protocol); }
+	return *this;
+}
+
+Bonjour& Bonjour::set_txt_keys(TxtKeys txt_keys)
+{
+	if (p) { p->txt_keys = std::move(txt_keys); }
+	return *this;
+}
+
 Bonjour& Bonjour::set_timeout(unsigned timeout)
 {
 	if (p) { p->timeout = timeout; }
diff --git a/src/slic3r/Utils/Bonjour.hpp b/src/slic3r/Utils/Bonjour.hpp
index 63f34638c..e61cd1833 100644
--- a/src/slic3r/Utils/Bonjour.hpp
+++ b/src/slic3r/Utils/Bonjour.hpp
@@ -4,6 +4,8 @@
 #include <cstdint>
 #include <memory>
 #include <string>
+#include <set>
+#include <unordered_map>
 #include <functional>
 #include <boost/asio/ip/address.hpp>
 
@@ -13,16 +15,24 @@ namespace Slic3r {
 
 struct BonjourReply
 {
+	typedef std::unordered_map<std::string, std::string> TxtData;
+
 	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;
+
+	TxtData txt_data;
 
 	BonjourReply() = delete;
-	BonjourReply(boost::asio::ip::address ip, uint16_t port, std::string service_name, std::string hostname, std::string path, std::string version);
+	BonjourReply(boost::asio::ip::address ip,
+		uint16_t port,
+		std::string service_name,
+		std::string hostname,
+		TxtData txt_data);
+
+	std::string path() const;
 
 	bool operator==(const BonjourReply &other) const;
 	bool operator<(const BonjourReply &other) const;
@@ -39,11 +49,17 @@ public:
 	typedef std::shared_ptr<Bonjour> Ptr;
 	typedef std::function<void(BonjourReply &&)> ReplyFn;
 	typedef std::function<void()> CompleteFn;
+	typedef std::set<std::string> TxtKeys;
 
-	Bonjour(std::string service, std::string protocol = "tcp");
+	Bonjour(std::string service);
 	Bonjour(Bonjour &&other);
 	~Bonjour();
 
+	// Set requested service protocol, "tcp" by default
+	Bonjour& set_protocol(std::string protocol);
+	// Set which TXT key-values should be collected
+	// Note that "path" is always collected
+	Bonjour& set_txt_keys(TxtKeys txt_keys);
 	Bonjour& set_timeout(unsigned timeout);
 	Bonjour& set_retries(unsigned retries);
 	// ^ Note: By default there is 1 retry (meaning 1 broadcast is sent).