diff --git a/resources/icons/printers/PrusaResearch_SL1.png b/resources/icons/printers/PrusaResearch_SL1.png
index 281d7a620..3c15f95ea 100644
Binary files a/resources/icons/printers/PrusaResearch_SL1.png and b/resources/icons/printers/PrusaResearch_SL1.png differ
diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp
index 3471395c2..c4f10db36 100644
--- a/src/slic3r/GUI/ConfigWizard.cpp
+++ b/src/slic3r/GUI/ConfigWizard.cpp
@@ -5,6 +5,7 @@
 #include <unordered_map>
 #include <boost/format.hpp>
 #include <boost/log/trivial.hpp>
+#include <boost/algorithm/string/predicate.hpp>
 
 #include <wx/settings.h>
 #include <wx/stattext.h>
@@ -13,6 +14,10 @@
 #include <wx/statbmp.h>
 #include <wx/checkbox.h>
 #include <wx/statline.h>
+#include <wx/dataview.h>
+#include <wx/notebook.h>
+#include <wx/display.h>
+#include <wx/debug.h>
 
 #include "libslic3r/Utils.hpp"
 #include "PresetBundle.hpp"
@@ -28,886 +33,1064 @@ namespace GUI {
 
 struct PrinterPickerEvent : public wxEvent
 {
-	std::string vendor_id;
-	std::string model_id;
-	std::string variant_name;
-	bool enable;
+    std::string vendor_id;
+    std::string model_id;
+    std::string variant_name;
+    bool enable;
 
-	PrinterPickerEvent(wxEventType eventType, int winid, std::string vendor_id, std::string model_id, std::string variant_name, bool enable) :
-		wxEvent(winid, eventType),
-		vendor_id(std::move(vendor_id)),
-		model_id(std::move(model_id)),
-		variant_name(std::move(variant_name)),
-		enable(enable)
-	{}
+    PrinterPickerEvent(wxEventType eventType, int winid, std::string vendor_id, std::string model_id, std::string variant_name, bool enable)
+        : wxEvent(winid, eventType)
+        , vendor_id(std::move(vendor_id))
+        , model_id(std::move(model_id))
+        , variant_name(std::move(variant_name))
+        , enable(enable)
+    {}
 
-	virtual wxEvent *Clone() const
-	{
-		return new PrinterPickerEvent(*this);
-	}
+    virtual wxEvent *Clone() const
+    {
+        return new PrinterPickerEvent(*this);
+    }
 };
 
 wxDEFINE_EVENT(EVT_PRINTER_PICK, PrinterPickerEvent);
 
-PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, const AppConfig &appconfig_vendors) :
-	wxPanel(parent),
-	vendor_id(vendor.id),
-	variants_checked(0)
+PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxString title, size_t max_cols, const AppConfig &appconfig_vendors, const ModelFilter &filter)
+    : wxPanel(parent)
+    , vendor_id(vendor.id)
 {
-	const auto &models = vendor.models;
+    const auto &models = vendor.models;
 
-	auto *sizer = new wxBoxSizer(wxVERTICAL);
+    auto *sizer = new wxBoxSizer(wxVERTICAL);
 
-	auto *printer_grid = new wxFlexGridSizer(models.size(), 0, 20);
-	printer_grid->SetFlexibleDirection(wxVERTICAL | wxHORIZONTAL);
-	sizer->Add(printer_grid);
+    const auto font_title = GetFont().MakeBold().Scaled(1.3);
+    const auto font_name = GetFont().MakeBold();
+    const auto font_alt_nozzle = GetFont().Scaled(0.9);
 
-	auto namefont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
-	namefont.SetWeight(wxFONTWEIGHT_BOLD);
+    // wxGrid appends widgets by rows, but we need to construct them in columns.
+    // These vectors are used to hold the elements so that they can be appended in the right order.
+    std::vector<wxStaticText*> titles;
+    std::vector<wxStaticBitmap*> bitmaps;
+    std::vector<wxPanel*> variants_panels;
 
-	// wxGrid appends widgets by rows, but we need to construct them in columns.
-	// These vectors are used to hold the elements so that they can be appended in the right order.
-	std::vector<wxStaticText*> titles;
-	std::vector<wxStaticBitmap*> bitmaps;
-	std::vector<wxPanel*> variants_panels;
+    for (const auto &model : models) {
+        if (! filter(model)) { continue; }
 
-	for (const auto &model : models) {
-		wxBitmap bitmap(GUI::from_u8(Slic3r::var((boost::format("printers/%1%_%2%.png") % vendor.id % model.id).str())), wxBITMAP_TYPE_PNG);
+        wxBitmap bitmap(GUI::from_u8(Slic3r::var((boost::format("printers/%1%_%2%.png") % vendor.id % model.id).str())), wxBITMAP_TYPE_PNG);
 
-		auto *title = new wxStaticText(this, wxID_ANY, model.name, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
-		title->SetFont(namefont);
-		title->Wrap(std::max((int)MODEL_MIN_WRAP, bitmap.GetWidth()));
-		titles.push_back(title);
+        auto *title = new wxStaticText(this, wxID_ANY, model.name, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
+        title->SetFont(font_name);
+        title->Wrap(std::max((int)MODEL_MIN_WRAP, bitmap.GetWidth()));
+        titles.push_back(title);
 
-		auto *bitmap_widget = new wxStaticBitmap(this, wxID_ANY, bitmap);
-		bitmaps.push_back(bitmap_widget);
+        auto *bitmap_widget = new wxStaticBitmap(this, wxID_ANY, bitmap);
+        bitmaps.push_back(bitmap_widget);
 
-		auto *variants_panel = new wxPanel(this);
-		auto *variants_sizer = new wxBoxSizer(wxVERTICAL);
-		variants_panel->SetSizer(variants_sizer);
-		const auto model_id = model.id;
+        auto *variants_panel = new wxPanel(this);
+        auto *variants_sizer = new wxBoxSizer(wxVERTICAL);
+        variants_panel->SetSizer(variants_sizer);
+        const auto model_id = model.id;
 
-		bool default_variant = true;   // Mark the first variant as default in the GUI
-		for (const auto &variant : model.variants) {
-            const auto label = wxString::Format("%s %s %s %s", variant.name, _(L("mm")), _(L("nozzle")),
-                (default_variant ? "(" + _(L("default")) + ")" : wxString()));
-			default_variant = false;
-			auto *cbox = new Checkbox(variants_panel, label, model_id, variant.name);
-			const size_t idx = cboxes.size();
-			cboxes.push_back(cbox);
-			bool enabled = appconfig_vendors.get_variant("PrusaResearch", model_id, variant.name);
-			variants_checked += enabled;
-			cbox->SetValue(enabled);
-			variants_sizer->Add(cbox, 0, wxBOTTOM, 3);
-			cbox->Bind(wxEVT_CHECKBOX, [this, idx](wxCommandEvent &event) {
-				if (idx >= this->cboxes.size()) { return; }
-				this->on_checkbox(this->cboxes[idx], event.IsChecked());
-			});
-		}
+        for (size_t i = 0; i < model.variants.size(); i++) {
+            const auto &variant = model.variants[i];
 
-		variants_panels.push_back(variants_panel);
-	}
+            const auto label = model.technology == ptFFF
+                ? wxString::Format("%s %s %s", variant.name, _(L("mm")), _(L("nozzle")))
+                : from_u8(model.name);
 
-	for (auto title : titles)       { printer_grid->Add(title, 0, wxBOTTOM, 3); }
-	for (auto bitmap : bitmaps)     { printer_grid->Add(bitmap, 0, wxBOTTOM, 20); }
-	for (auto vp : variants_panels) { printer_grid->Add(vp); }
+            if (i == 1) {
+                auto *alt_label = new wxStaticText(variants_panel, wxID_ANY, _(L("Alternate nozzles:")));
+                alt_label->SetFont(font_alt_nozzle);
+                variants_sizer->Add(alt_label, 0, wxBOTTOM, 3);
+            }
 
-	auto *all_none_sizer = new wxBoxSizer(wxHORIZONTAL);
-	auto *sel_all = new wxButton(this, wxID_ANY, _(L("Select all")));
-	auto *sel_none = new wxButton(this, wxID_ANY, _(L("Select none")));
-	sel_all->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &event) { this->select_all(true); });
-	sel_none->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &event) { this->select_all(false); });
-	all_none_sizer->AddStretchSpacer();
-	all_none_sizer->Add(sel_all);
-	all_none_sizer->Add(sel_none);
-	sizer->AddStretchSpacer();
-	sizer->Add(all_none_sizer, 0, wxEXPAND);
+            auto *cbox = new Checkbox(variants_panel, label, model_id, variant.name);
+            i == 0 ? cboxes.push_back(cbox) : cboxes_alt.push_back(cbox);
 
-	SetSizer(sizer);
+            bool enabled = appconfig_vendors.get_variant("PrusaResearch", model_id, variant.name);
+            cbox->SetValue(enabled);
+
+            variants_sizer->Add(cbox, 0, wxBOTTOM, 3);
+
+            cbox->Bind(wxEVT_CHECKBOX, [this, cbox](wxCommandEvent &event) {
+                on_checkbox(cbox, event.IsChecked());
+            });
+        }
+
+        variants_panels.push_back(variants_panel);
+    }
+
+    const size_t cols = std::min(max_cols, titles.size());
+
+    auto *printer_grid = new wxFlexGridSizer(cols, 0, 20);
+    printer_grid->SetFlexibleDirection(wxVERTICAL | wxHORIZONTAL);
+
+    if (titles.size() > 0) {
+        const size_t odd_items = titles.size() % cols;
+
+        for (size_t i = 0; i < titles.size() - odd_items; i += cols) {
+            for (size_t j = i; j < i + cols; j++) { printer_grid->Add(titles[j], 0, wxBOTTOM, 3); }
+            for (size_t j = i; j < i + cols; j++) { printer_grid->Add(bitmaps[j], 0, wxBOTTOM, 20); }
+            for (size_t j = i; j < i + cols; j++) { printer_grid->Add(variants_panels[j]); }
+
+            // Add separator space
+            if (i > 0) {
+                for (size_t j = i; j < i + cols; j++) { printer_grid->Add(1, 100); }
+            }
+        }
+
+        if (odd_items > 0) {
+            for (size_t i = 0; i < cols; i++) { printer_grid->Add(1, 100); }
+
+            const size_t rem = titles.size() - odd_items;
+
+            for (size_t i = rem; i < titles.size(); i++) { printer_grid->Add(titles[i], 0, wxBOTTOM, 3); }
+            for (size_t i = 0; i < cols - odd_items; i++) { printer_grid->AddSpacer(1); }
+            for (size_t i = rem; i < titles.size(); i++) { printer_grid->Add(bitmaps[i], 0, wxBOTTOM, 20); }
+            for (size_t i = 0; i < cols - odd_items; i++) { printer_grid->AddSpacer(1); }
+            for (size_t i = rem; i < titles.size(); i++) { printer_grid->Add(variants_panels[i]); }
+        }
+    }
+
+    auto *title_sizer = new wxBoxSizer(wxHORIZONTAL);
+    auto *title_widget = new wxStaticText(this, wxID_ANY, title);
+    title_widget->SetFont(font_title);
+    title_sizer->Add(title_widget);
+    title_sizer->AddStretchSpacer();
+
+    if (titles.size() > 1) {
+        // It only makes sense to add the All / None buttons if there's multiple printers
+
+        auto *sel_all = new wxButton(this, wxID_ANY, _(L("All")));
+        auto *sel_none = new wxButton(this, wxID_ANY, _(L("None")));
+        sel_all->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &event) { this->select_all(true); });
+        sel_none->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &event) { this->select_all(false); });
+        title_sizer->Add(sel_all, 0, wxRIGHT, BTN_SPACING);
+        title_sizer->Add(sel_none);
+    }
+
+    sizer->Add(title_sizer, 0, wxEXPAND | wxBOTTOM, BTN_SPACING);
+    sizer->Add(printer_grid);
+
+    // auto *all_none_sizer = new wxBoxSizer(wxHORIZONTAL);
+    // auto *sel_all = new wxButton(this, wxID_ANY, _(L("Select all")));
+    // auto *sel_none = new wxButton(this, wxID_ANY, _(L("Select none")));
+    // sel_all->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &event) { this->select_all(true); });
+    // sel_none->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &event) { this->select_all(false); });
+    // all_none_sizer->AddStretchSpacer();
+    // all_none_sizer->Add(sel_all);
+    // all_none_sizer->Add(sel_none);
+    // sizer->AddStretchSpacer();
+    // sizer->Add(all_none_sizer, 0, wxEXPAND);
+
+    SetSizer(sizer);
 }
 
+PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxString title, size_t max_cols, const AppConfig &appconfig_vendors)
+    : PrinterPicker(parent, vendor, std::move(title), max_cols, appconfig_vendors, [](const VendorProfile::PrinterModel&) { return true; })
+{}
+
 void PrinterPicker::select_all(bool select)
 {
-	for (const auto &cb : cboxes) {
-		if (cb->GetValue() != select) {
-			cb->SetValue(select);
-			on_checkbox(cb, select);
-		}
-	}
+    for (const auto &cb : cboxes) {
+        if (cb->GetValue() != select) {
+            cb->SetValue(select);
+            on_checkbox(cb, select);
+        }
+    }
+
+    // Alt nozzles are de-selected if this is an all-deselect, left intact otherwise
+    if (! select) {
+        for (const auto &cb : cboxes_alt) {
+            if (cb->GetValue()) {
+                cb->SetValue(false);
+                on_checkbox(cb, false);
+            }
+        }
+    }
 }
 
 void PrinterPicker::select_one(size_t i, bool select)
 {
-	if (i < cboxes.size() && cboxes[i]->GetValue() != select) {
-		cboxes[i]->SetValue(select);
-		on_checkbox(cboxes[i], select);
-	}
+    if (i < cboxes.size() && cboxes[i]->GetValue() != select) {
+        cboxes[i]->SetValue(select);
+        on_checkbox(cboxes[i], select);
+    }
 }
 
 void PrinterPicker::on_checkbox(const Checkbox *cbox, bool checked)
 {
-	variants_checked += checked ? 1 : -1;
-	PrinterPickerEvent evt(EVT_PRINTER_PICK, GetId(), vendor_id, cbox->model, cbox->variant, checked);
-	AddPendingEvent(evt);
+    PrinterPickerEvent evt(EVT_PRINTER_PICK, GetId(), vendor_id, cbox->model, cbox->variant, checked);
+    AddPendingEvent(evt);
 }
 
 
 // Wizard page base
 
-ConfigWizardPage::ConfigWizardPage(ConfigWizard *parent, wxString title, wxString shortname) :
-	wxPanel(parent->p->hscroll),
-	parent(parent),
-	shortname(std::move(shortname)),
-	p_prev(nullptr),
-	p_next(nullptr)
+ConfigWizardPage::ConfigWizardPage(ConfigWizard *parent, wxString title, wxString shortname, unsigned indent)
+    : wxPanel(parent->p->hscroll)
+    , parent(parent)
+    , shortname(std::move(shortname))
+    , indent(indent)
 {
-	auto *sizer = new wxBoxSizer(wxVERTICAL);
+    auto *sizer = new wxBoxSizer(wxVERTICAL);
 
-	auto *text = new wxStaticText(this, wxID_ANY, std::move(title), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
-	auto font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
-	font.SetWeight(wxFONTWEIGHT_BOLD);
-	font.SetPointSize(14);
-	text->SetFont(font);
-	sizer->Add(text, 0, wxALIGN_LEFT, 0);
-	sizer->AddSpacer(10);
+    auto *text = new wxStaticText(this, wxID_ANY, std::move(title), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
+    const auto font = GetFont().MakeBold().Scaled(1.5);
+    text->SetFont(font);
+    sizer->Add(text, 0, wxALIGN_LEFT, 0);
+    sizer->AddSpacer(10);
 
-	content = new wxBoxSizer(wxVERTICAL);
-	sizer->Add(content, 1);
+    content = new wxBoxSizer(wxVERTICAL);
+    sizer->Add(content, 1);
 
-	SetSizer(sizer);
+    SetSizer(sizer);
 
-	this->Hide();
+    this->Hide();
 
-	Bind(wxEVT_SIZE, [this](wxSizeEvent &event) {
-		this->Layout();
-		event.Skip();
-	});
+    Bind(wxEVT_SIZE, [this](wxSizeEvent &event) {
+        this->Layout();
+        event.Skip();
+    });
 }
 
 ConfigWizardPage::~ConfigWizardPage() {}
 
-ConfigWizardPage* ConfigWizardPage::chain(ConfigWizardPage *page)
-{
-	if (p_next != nullptr) { p_next->p_prev = nullptr; }
-	p_next = page;
-	if (page != nullptr) {
-		if (page->p_prev != nullptr) { page->p_prev->p_next = nullptr; }
-		page->p_prev = this;
-	}
-
-	return page;
-}
-
 void ConfigWizardPage::append_text(wxString text)
 {
-	auto *widget = new wxStaticText(this, wxID_ANY, text, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
-	widget->Wrap(WRAP_WIDTH);
-	widget->SetMinSize(wxSize(WRAP_WIDTH, -1));
-	append(widget);
+    auto *widget = new wxStaticText(this, wxID_ANY, text, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
+    widget->Wrap(WRAP_WIDTH);
+    widget->SetMinSize(wxSize(WRAP_WIDTH, -1));
+    append(widget);
 }
 
 void ConfigWizardPage::append_spacer(int space)
 {
-	content->AddSpacer(space);
+    content->AddSpacer(space);
 }
 
 bool ConfigWizardPage::Show(bool show)
 {
-	if (extra_buttons() != nullptr) { extra_buttons()->Show(show); }
-	return wxPanel::Show(show);
+    if (extra_buttons() != nullptr) { extra_buttons()->Show(show); }
+    return wxPanel::Show(show);
 }
 
-void ConfigWizardPage::enable_next(bool enable) { parent->p->enable_next(enable); }
-
 
 // Wizard pages
 
-PageWelcome::PageWelcome(ConfigWizard *parent, bool check_first_variant) :
-	ConfigWizardPage(parent, wxString::Format(_(L("Welcome to the Slic3r %s")), ConfigWizard::name()), _(L("Welcome"))),
-	printer_picker(nullptr),
-	others_buttons(new wxPanel(parent)),
-	cbox_reset(nullptr)
+PageWelcome::PageWelcome(ConfigWizard *parent)
+    : ConfigWizardPage(parent, wxString::Format(_(L("Welcome to the Slic3r %s")), ConfigWizard::name()), _(L("Welcome")))
+    , cbox_reset(nullptr)
 {
-	if (wizard_p()->run_reason == ConfigWizard::RR_DATA_EMPTY) {
-		wxString::Format(_(L("Run %s")), ConfigWizard::name());
-		append_text(wxString::Format(
-			_(L("Hello, welcome to Slic3r Prusa Edition! This %s helps you with the initial configuration; just a few settings and you will be ready to print.")),
-			ConfigWizard::name())
-		);
-	} else {
-		cbox_reset = new wxCheckBox(this, wxID_ANY, _(L("Remove user profiles - install from scratch (a snapshot will be taken beforehand)")));
-		append(cbox_reset);
-	}
+    if (wizard_p()->run_reason == ConfigWizard::RR_DATA_EMPTY) {
+        wxString::Format(_(L("Run %s")), ConfigWizard::name());
+        append_text(wxString::Format(
+            _(L("Hello, welcome to Slic3r Prusa Edition! This %s helps you with the initial configuration; just a few settings and you will be ready to print.")),
+            ConfigWizard::name())
+        );
+    } else {
+        cbox_reset = new wxCheckBox(this, wxID_ANY, _(L("Remove user profiles - install from scratch (a snapshot will be taken beforehand)")));
+        append(cbox_reset);
+    }
 
-	const auto &vendors = wizard_p()->vendors;
-	const auto vendor_prusa = vendors.find("PrusaResearch");
-
-	if (vendor_prusa != vendors.cend()) {
-		AppConfig &appconfig_vendors = this->wizard_p()->appconfig_vendors;
-
-		printer_picker = new PrinterPicker(this, vendor_prusa->second, appconfig_vendors);
-		if (check_first_variant) {
-			// Select the default (first) model/variant on the Prusa vendor
-			printer_picker->select_one(0, true);
-		}
-		printer_picker->Bind(EVT_PRINTER_PICK, [this, &appconfig_vendors](const PrinterPickerEvent &evt) {
-			appconfig_vendors.set_variant(evt.vendor_id, evt.model_id, evt.variant_name, evt.enable);
-			this->on_variant_checked();
-		});
-
-		append(printer_picker);
-	}
-
-	const size_t num_other_vendors = vendors.size() - (vendor_prusa != vendors.cend());
-	auto *sizer = new wxBoxSizer(wxHORIZONTAL);
-	auto *other_vendors = new wxButton(others_buttons, wxID_ANY, _(L("Other vendors")));
-	other_vendors->Enable(num_other_vendors > 0);
-	auto *custom_setup = new wxButton(others_buttons, wxID_ANY, _(L("Custom setup")));
-
-	sizer->Add(other_vendors);
-	sizer->AddSpacer(BTN_SPACING);
-	sizer->Add(custom_setup);
-
-	other_vendors->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &event) { this->wizard_p()->on_other_vendors(); });
-	custom_setup->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &event) { this->wizard_p()->on_custom_setup(); });
-
-	others_buttons->SetSizer(sizer);
+    Show();
 }
 
-void PageWelcome::on_page_set()
+
+PagePrinters::PagePrinters(ConfigWizard *parent, wxString title, wxString shortname, const VendorProfile &vendor, unsigned indent, Technology technology)
+    : ConfigWizardPage(parent, std::move(title), std::move(shortname), indent)
 {
-	chain(wizard_p()->page_update);
-	on_variant_checked();
+    enum {
+        COL_SIZE = 200,
+    };
+
+    bool check_first_variant = wizard_p()->check_first_variant();
+
+    AppConfig &appconfig_vendors = this->wizard_p()->appconfig_vendors;
+
+    const auto families = vendor.families();
+    for (const auto &family : families) {
+        const auto filter = [&](const VendorProfile::PrinterModel &model) {
+            return (model.technology == ptFFF && technology & T_FFF
+                    || model.technology == ptSLA && technology & T_SLA)
+                && model.family == family;
+        };
+
+        if (std::find_if(vendor.models.begin(), vendor.models.end(), filter) == vendor.models.end()) {
+            continue;
+        }
+
+        const auto picker_title = family.empty() ? wxString() : wxString::Format(_(L("%s Family")), family);
+        auto *picker = new PrinterPicker(this, vendor, picker_title, MAX_COLS, appconfig_vendors, filter);
+
+        if (check_first_variant) {
+            // Select the default (first) model/variant on the Prusa vendor
+            picker->select_one(0, true);
+            check_first_variant = false;
+        }
+
+        picker->Bind(EVT_PRINTER_PICK, [this, &appconfig_vendors](const PrinterPickerEvent &evt) {
+            appconfig_vendors.set_variant(evt.vendor_id, evt.model_id, evt.variant_name, evt.enable);
+        });
+
+        append(new wxStaticLine(this));
+
+        append(picker);
+        printer_pickers.push_back(picker);
+    }
 }
 
-void PageWelcome::on_variant_checked()
+void PagePrinters::select_all(bool select)
 {
-	enable_next(printer_picker != nullptr ? printer_picker->variants_checked > 0 : false);
+    for (auto picker : printer_pickers) {
+        picker->select_all(select);
+    }
 }
 
-PageUpdate::PageUpdate(ConfigWizard *parent) :
-	ConfigWizardPage(parent, _(L("Automatic updates")), _(L("Updates"))),
-	version_check(true),
-	preset_update(true)
+
+PageCustom::PageCustom(ConfigWizard *parent)
+    : ConfigWizardPage(parent, _(L("Custom Printer Setup")), _(L("Custom Printer")))
 {
-	const AppConfig *app_config = GUI::get_app_config();
-	auto boldfont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
-	boldfont.SetWeight(wxFONTWEIGHT_BOLD);
+    cb_custom = new wxCheckBox(this, wxID_ANY, _(L("Define a custom printer profile")));
+    tc_profile_name = new wxTextCtrl(this, wxID_ANY, "My Settings");
+    auto *label = new wxStaticText(this, wxID_ANY, _(L("Custom profile name:")));
 
-	auto *box_slic3r = new wxCheckBox(this, wxID_ANY, _(L("Check for application updates")));
-	box_slic3r->SetValue(app_config->get("version_check") == "1");
-	append(box_slic3r);
-	append_text(_(L("If enabled, Slic3r checks for new versions of Slic3r PE online. When a new version becomes available a notification is displayed at the next application startup (never during program usage). This is only a notification mechanisms, no automatic installation is done.")));
+    cb_custom->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent &event) {
+        wizard_p()->on_custom_setup(custom_wanted());
+    });
 
-	append_spacer(VERTICAL_SPACING);
-
-	auto *box_presets = new wxCheckBox(this, wxID_ANY, _(L("Update built-in Presets automatically")));
-	box_presets->SetValue(app_config->get("preset_update") == "1");
-	append(box_presets);
-	append_text(_(L("If enabled, Slic3r downloads updates of built-in system presets in the background. These updates are downloaded into a separate temporary location. When a new preset version becomes available it is offered at application startup.")));
-	const auto text_bold = _(L("Updates are never applied without user's consent and never overwrite user's customized settings."));
-	auto *label_bold = new wxStaticText(this, wxID_ANY, text_bold);
-	label_bold->SetFont(boldfont);
-	label_bold->Wrap(WRAP_WIDTH);
-	append(label_bold);
-	append_text(_(L("Additionally a backup snapshot of the whole configuration is created before an update is applied.")));
-
-	box_slic3r->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent &event) { this->version_check = event.IsChecked(); });
-	box_presets->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent &event) { this->preset_update = event.IsChecked(); });
+    append(cb_custom);
+    append(label);
+    append(tc_profile_name);
 }
 
-PageVendors::PageVendors(ConfigWizard *parent) :
-	ConfigWizardPage(parent, _(L("Other Vendors")), _(L("Other Vendors")))
+PageUpdate::PageUpdate(ConfigWizard *parent)
+    : ConfigWizardPage(parent, _(L("Automatic updates")), _(L("Updates")))
+    , version_check(true)
+    , preset_update(true)
 {
-	append_text(_(L("Pick another vendor supported by Slic3r PE:")));
+    const AppConfig *app_config = GUI::get_app_config();
+    auto boldfont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
+    boldfont.SetWeight(wxFONTWEIGHT_BOLD);
 
-	auto boldfont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
-	boldfont.SetWeight(wxFONTWEIGHT_BOLD);
+    auto *box_slic3r = new wxCheckBox(this, wxID_ANY, _(L("Check for application updates")));
+    box_slic3r->SetValue(app_config->get("version_check") == "1");
+    append(box_slic3r);
+    append_text(_(L("If enabled, Slic3r checks for new versions of Slic3r PE online. When a new version becomes available a notification is displayed at the next application startup (never during program usage). This is only a notification mechanisms, no automatic installation is done.")));
 
-	AppConfig &appconfig_vendors = this->wizard_p()->appconfig_vendors;
-	wxArrayString choices_vendors;
+    append_spacer(VERTICAL_SPACING);
 
-	for (const auto vendor_pair : wizard_p()->vendors) {
-		const auto &vendor = vendor_pair.second;
-		if (vendor.id == "PrusaResearch") { continue; }
+    auto *box_presets = new wxCheckBox(this, wxID_ANY, _(L("Update built-in Presets automatically")));
+    box_presets->SetValue(app_config->get("preset_update") == "1");
+    append(box_presets);
+    append_text(_(L("If enabled, Slic3r downloads updates of built-in system presets in the background. These updates are downloaded into a separate temporary location. When a new preset version becomes available it is offered at application startup.")));
+    const auto text_bold = _(L("Updates are never applied without user's consent and never overwrite user's customized settings."));
+    auto *label_bold = new wxStaticText(this, wxID_ANY, text_bold);
+    label_bold->SetFont(boldfont);
+    label_bold->Wrap(WRAP_WIDTH);
+    append(label_bold);
+    append_text(_(L("Additionally a backup snapshot of the whole configuration is created before an update is applied.")));
 
-		auto *picker = new PrinterPicker(this, vendor, appconfig_vendors);
-		picker->Hide();
-		pickers.push_back(picker);
-		choices_vendors.Add(vendor.name);
-
-		picker->Bind(EVT_PRINTER_PICK, [this, &appconfig_vendors](const PrinterPickerEvent &evt) {
-			appconfig_vendors.set_variant(evt.vendor_id, evt.model_id, evt.variant_name, evt.enable);
-			this->on_variant_checked();
-		});
-	}
-
-	auto *vendor_picker = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, choices_vendors);
-	if (choices_vendors.GetCount() > 0) {
-		vendor_picker->SetSelection(0);
-		on_vendor_pick(0);
-	}
-
-	vendor_picker->Bind(wxEVT_CHOICE, [this](wxCommandEvent &evt) {
-		this->on_vendor_pick(evt.GetInt());
-	});
-
-	append(vendor_picker);
-	for (PrinterPicker *picker : pickers) { this->append(picker); }
+    box_slic3r->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent &event) { this->version_check = event.IsChecked(); });
+    box_presets->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent &event) { this->preset_update = event.IsChecked(); });
 }
 
-void PageVendors::on_page_set()
+PageVendors::PageVendors(ConfigWizard *parent)
+    : ConfigWizardPage(parent, _(L("Other Vendors")), _(L("Other Vendors")))
 {
-	on_variant_checked();
+    append_text(_(L("Pick another vendor supported by Slic3r PE:")));
+
+    auto boldfont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
+    boldfont.SetWeight(wxFONTWEIGHT_BOLD);
+
+    AppConfig &appconfig_vendors = this->wizard_p()->appconfig_vendors;
+    wxArrayString choices_vendors;
+
+    for (const auto vendor_pair : wizard_p()->vendors) {
+        const auto &vendor = vendor_pair.second;
+        if (vendor.id == "PrusaResearch") { continue; }
+
+        auto *picker = new PrinterPicker(this, vendor, "", MAX_COLS, appconfig_vendors);
+        picker->Hide();
+        pickers.push_back(picker);
+        choices_vendors.Add(vendor.name);
+
+        picker->Bind(EVT_PRINTER_PICK, [this, &appconfig_vendors](const PrinterPickerEvent &evt) {
+            appconfig_vendors.set_variant(evt.vendor_id, evt.model_id, evt.variant_name, evt.enable);
+        });
+    }
+
+    auto *vendor_picker = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, choices_vendors);
+    if (choices_vendors.GetCount() > 0) {
+        vendor_picker->SetSelection(0);
+        on_vendor_pick(0);
+    }
+
+    vendor_picker->Bind(wxEVT_CHOICE, [this](wxCommandEvent &evt) {
+        this->on_vendor_pick(evt.GetInt());
+    });
+
+    append(vendor_picker);
+    for (PrinterPicker *picker : pickers) { this->append(picker); }
 }
 
 void PageVendors::on_vendor_pick(size_t i)
 {
-	for (PrinterPicker *picker : pickers) { picker->Hide(); }
-	if (i < pickers.size()) {
-		pickers[i]->Show();
-		wizard_p()->layout_fit();
-	}
+    for (PrinterPicker *picker : pickers) { picker->Hide(); }
+    if (i < pickers.size()) {
+        pickers[i]->Show();
+        parent->Layout();
+    }
 }
 
-void PageVendors::on_variant_checked()
+PageFirmware::PageFirmware(ConfigWizard *parent)
+    : ConfigWizardPage(parent, _(L("Firmware Type")), _(L("Firmware")), 1)
+    , gcode_opt(*print_config_def.get("gcode_flavor"))
+    , gcode_picker(nullptr)
 {
-	size_t variants_checked = 0;
-	for (const PrinterPicker *picker : pickers) { variants_checked += picker->variants_checked; }
-	enable_next(variants_checked > 0);
-}
+    append_text(_(L("Choose the type of firmware used by your printer.")));
+    append_text(gcode_opt.tooltip);
 
-PageFirmware::PageFirmware(ConfigWizard *parent) :
-	ConfigWizardPage(parent, _(L("Firmware Type")), _(L("Firmware"))),
-	gcode_opt(*print_config_def.get("gcode_flavor")),
-	gcode_picker(nullptr)
-{
-	append_text(_(L("Choose the type of firmware used by your printer.")));
-	append_text(gcode_opt.tooltip);
+    wxArrayString choices;
+    choices.Alloc(gcode_opt.enum_labels.size());
+    for (const auto &label : gcode_opt.enum_labels) {
+        choices.Add(label);
+    }
 
-	wxArrayString choices;
-	choices.Alloc(gcode_opt.enum_labels.size());
-	for (const auto &label : gcode_opt.enum_labels) {
-		choices.Add(label);
-	}
+    gcode_picker = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, choices);
+    const auto &enum_values = gcode_opt.enum_values;
+    auto needle = enum_values.cend();
+    if (gcode_opt.default_value != nullptr) {
+        needle = std::find(enum_values.cbegin(), enum_values.cend(), gcode_opt.default_value->serialize());
+    }
+    if (needle != enum_values.cend()) {
+        gcode_picker->SetSelection(needle - enum_values.cbegin());
+    } else {
+        gcode_picker->SetSelection(0);
+    }
 
-	gcode_picker = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, choices);
-	const auto &enum_values = gcode_opt.enum_values;
-	auto needle = enum_values.cend();
-	if (gcode_opt.default_value != nullptr) {
-		needle = std::find(enum_values.cbegin(), enum_values.cend(), gcode_opt.default_value->serialize());
-	}
-	if (needle != enum_values.cend()) {
-		gcode_picker->SetSelection(needle - enum_values.cbegin());
-	} else {
-		gcode_picker->SetSelection(0);
-	}
-
-	append(gcode_picker);
+    append(gcode_picker);
 }
 
 void PageFirmware::apply_custom_config(DynamicPrintConfig &config)
 {
-	auto sel = gcode_picker->GetSelection();
-	if (sel >= 0 && sel < gcode_opt.enum_labels.size()) {
-		auto *opt = new ConfigOptionEnum<GCodeFlavor>(static_cast<GCodeFlavor>(sel));
-		config.set_key_value("gcode_flavor", opt);
-	}
+    auto sel = gcode_picker->GetSelection();
+    if (sel >= 0 && (size_t)sel < gcode_opt.enum_labels.size()) {
+        auto *opt = new ConfigOptionEnum<GCodeFlavor>(static_cast<GCodeFlavor>(sel));
+        config.set_key_value("gcode_flavor", opt);
+    }
 }
 
-PageBedShape::PageBedShape(ConfigWizard *parent) :
-	ConfigWizardPage(parent, _(L("Bed Shape and Size")), _(L("Bed Shape"))),
-	shape_panel(new BedShapePanel(this))
+PageBedShape::PageBedShape(ConfigWizard *parent)
+    : ConfigWizardPage(parent, _(L("Bed Shape and Size")), _(L("Bed Shape")), 1)
+    , shape_panel(new BedShapePanel(this))
 {
-	append_text(_(L("Set the shape of your printer's bed.")));
+    append_text(_(L("Set the shape of your printer's bed.")));
 
-	shape_panel->build_panel(wizard_p()->custom_config->option<ConfigOptionPoints>("bed_shape"));
-	append(shape_panel);
+    shape_panel->build_panel(wizard_p()->custom_config->option<ConfigOptionPoints>("bed_shape"));
+    append(shape_panel);
 }
 
 void PageBedShape::apply_custom_config(DynamicPrintConfig &config)
 {
-	const auto points(shape_panel->GetValue());
-	auto *opt = new ConfigOptionPoints(points);
-	config.set_key_value("bed_shape", opt);
+    const auto points(shape_panel->GetValue());
+    auto *opt = new ConfigOptionPoints(points);
+    config.set_key_value("bed_shape", opt);
 }
 
-PageDiameters::PageDiameters(ConfigWizard *parent) :
-	ConfigWizardPage(parent, _(L("Filament and Nozzle Diameters")), _(L("Print Diameters"))),
-	spin_nozzle(new wxSpinCtrlDouble(this, wxID_ANY)),
-	spin_filam(new wxSpinCtrlDouble(this, wxID_ANY))
+PageDiameters::PageDiameters(ConfigWizard *parent)
+    : ConfigWizardPage(parent, _(L("Filament and Nozzle Diameters")), _(L("Print Diameters")), 1)
+    , spin_nozzle(new wxSpinCtrlDouble(this, wxID_ANY))
+    , spin_filam(new wxSpinCtrlDouble(this, wxID_ANY))
 {
-	spin_nozzle->SetDigits(2);
-	spin_nozzle->SetIncrement(0.1);
-	const auto &def_nozzle = *print_config_def.get("nozzle_diameter");
-	auto *default_nozzle = dynamic_cast<const ConfigOptionFloats*>(def_nozzle.default_value);
-	spin_nozzle->SetValue(default_nozzle != nullptr && default_nozzle->size() > 0 ? default_nozzle->get_at(0) : 0.5);
+    spin_nozzle->SetDigits(2);
+    spin_nozzle->SetIncrement(0.1);
+    const auto &def_nozzle = *print_config_def.get("nozzle_diameter");
+    auto *default_nozzle = dynamic_cast<const ConfigOptionFloats*>(def_nozzle.default_value);
+    spin_nozzle->SetValue(default_nozzle != nullptr && default_nozzle->size() > 0 ? default_nozzle->get_at(0) : 0.5);
 
-	spin_filam->SetDigits(2);
-	spin_filam->SetIncrement(0.25);
-	const auto &def_filam = *print_config_def.get("filament_diameter");
-	auto *default_filam = dynamic_cast<const ConfigOptionFloats*>(def_filam.default_value);
-	spin_filam->SetValue(default_filam != nullptr && default_filam->size() > 0 ? default_filam->get_at(0) : 3.0);
+    spin_filam->SetDigits(2);
+    spin_filam->SetIncrement(0.25);
+    const auto &def_filam = *print_config_def.get("filament_diameter");
+    auto *default_filam = dynamic_cast<const ConfigOptionFloats*>(def_filam.default_value);
+    spin_filam->SetValue(default_filam != nullptr && default_filam->size() > 0 ? default_filam->get_at(0) : 3.0);
 
-	append_text(_(L("Enter the diameter of your printer's hot end nozzle.")));
+    append_text(_(L("Enter the diameter of your printer's hot end nozzle.")));
 
-	auto *sizer_nozzle = new wxFlexGridSizer(3, 5, 5);
-	auto *text_nozzle = new wxStaticText(this, wxID_ANY, _(L("Nozzle Diameter:")));
-	auto *unit_nozzle = new wxStaticText(this, wxID_ANY, _(L("mm")));
-	sizer_nozzle->AddGrowableCol(0, 1);
-	sizer_nozzle->Add(text_nozzle, 0, wxALIGN_CENTRE_VERTICAL);
-	sizer_nozzle->Add(spin_nozzle);
-	sizer_nozzle->Add(unit_nozzle, 0, wxALIGN_CENTRE_VERTICAL);
-	append(sizer_nozzle);
+    auto *sizer_nozzle = new wxFlexGridSizer(3, 5, 5);
+    auto *text_nozzle = new wxStaticText(this, wxID_ANY, _(L("Nozzle Diameter:")));
+    auto *unit_nozzle = new wxStaticText(this, wxID_ANY, _(L("mm")));
+    sizer_nozzle->AddGrowableCol(0, 1);
+    sizer_nozzle->Add(text_nozzle, 0, wxALIGN_CENTRE_VERTICAL);
+    sizer_nozzle->Add(spin_nozzle);
+    sizer_nozzle->Add(unit_nozzle, 0, wxALIGN_CENTRE_VERTICAL);
+    append(sizer_nozzle);
 
-	append_spacer(VERTICAL_SPACING);
+    append_spacer(VERTICAL_SPACING);
 
-	append_text(_(L("Enter the diameter of your filament.")));
-	append_text(_(L("Good precision is required, so use a caliper and do multiple measurements along the filament, then compute the average.")));
+    append_text(_(L("Enter the diameter of your filament.")));
+    append_text(_(L("Good precision is required, so use a caliper and do multiple measurements along the filament, then compute the average.")));
 
-	auto *sizer_filam = new wxFlexGridSizer(3, 5, 5);
-	auto *text_filam = new wxStaticText(this, wxID_ANY, _(L("Filament Diameter:")));
-	auto *unit_filam = new wxStaticText(this, wxID_ANY, _(L("mm")));
-	sizer_filam->AddGrowableCol(0, 1);
-	sizer_filam->Add(text_filam, 0, wxALIGN_CENTRE_VERTICAL);
-	sizer_filam->Add(spin_filam);
-	sizer_filam->Add(unit_filam, 0, wxALIGN_CENTRE_VERTICAL);
-	append(sizer_filam);
+    auto *sizer_filam = new wxFlexGridSizer(3, 5, 5);
+    auto *text_filam = new wxStaticText(this, wxID_ANY, _(L("Filament Diameter:")));
+    auto *unit_filam = new wxStaticText(this, wxID_ANY, _(L("mm")));
+    sizer_filam->AddGrowableCol(0, 1);
+    sizer_filam->Add(text_filam, 0, wxALIGN_CENTRE_VERTICAL);
+    sizer_filam->Add(spin_filam);
+    sizer_filam->Add(unit_filam, 0, wxALIGN_CENTRE_VERTICAL);
+    append(sizer_filam);
 }
 
 void PageDiameters::apply_custom_config(DynamicPrintConfig &config)
 {
-	auto *opt_nozzle = new ConfigOptionFloats(1, spin_nozzle->GetValue());
-	config.set_key_value("nozzle_diameter", opt_nozzle);
-	auto *opt_filam = new ConfigOptionFloats(1, spin_filam->GetValue());
-	config.set_key_value("filament_diameter", opt_filam);
+    auto *opt_nozzle = new ConfigOptionFloats(1, spin_nozzle->GetValue());
+    config.set_key_value("nozzle_diameter", opt_nozzle);
+    auto *opt_filam = new ConfigOptionFloats(1, spin_filam->GetValue());
+    config.set_key_value("filament_diameter", opt_filam);
 }
 
-PageTemperatures::PageTemperatures(ConfigWizard *parent) :
-	ConfigWizardPage(parent, _(L("Extruder and Bed Temperatures")), _(L("Temperatures"))),
-	spin_extr(new wxSpinCtrlDouble(this, wxID_ANY)),
-	spin_bed(new wxSpinCtrlDouble(this, wxID_ANY))
+PageTemperatures::PageTemperatures(ConfigWizard *parent)
+    : ConfigWizardPage(parent, _(L("Extruder and Bed Temperatures")), _(L("Temperatures")), 1)
+    , spin_extr(new wxSpinCtrlDouble(this, wxID_ANY))
+    , spin_bed(new wxSpinCtrlDouble(this, wxID_ANY))
 {
-	spin_extr->SetIncrement(5.0);
-	const auto &def_extr = *print_config_def.get("temperature");
-	spin_extr->SetRange(def_extr.min, def_extr.max);
-	auto *default_extr = dynamic_cast<const ConfigOptionInts*>(def_extr.default_value);
-	spin_extr->SetValue(default_extr != nullptr && default_extr->size() > 0 ? default_extr->get_at(0) : 200);
+    spin_extr->SetIncrement(5.0);
+    const auto &def_extr = *print_config_def.get("temperature");
+    spin_extr->SetRange(def_extr.min, def_extr.max);
+    auto *default_extr = dynamic_cast<const ConfigOptionInts*>(def_extr.default_value);
+    spin_extr->SetValue(default_extr != nullptr && default_extr->size() > 0 ? default_extr->get_at(0) : 200);
 
-	spin_bed->SetIncrement(5.0);
-	const auto &def_bed = *print_config_def.get("bed_temperature");
-	spin_bed->SetRange(def_bed.min, def_bed.max);
-	auto *default_bed = dynamic_cast<const ConfigOptionInts*>(def_bed.default_value);
-	spin_bed->SetValue(default_bed != nullptr && default_bed->size() > 0 ? default_bed->get_at(0) : 0);
+    spin_bed->SetIncrement(5.0);
+    const auto &def_bed = *print_config_def.get("bed_temperature");
+    spin_bed->SetRange(def_bed.min, def_bed.max);
+    auto *default_bed = dynamic_cast<const ConfigOptionInts*>(def_bed.default_value);
+    spin_bed->SetValue(default_bed != nullptr && default_bed->size() > 0 ? default_bed->get_at(0) : 0);
 
-	append_text(_(L("Enter the temperature needed for extruding your filament.")));
-	append_text(_(L("A rule of thumb is 160 to 230 °C for PLA, and 215 to 250 °C for ABS.")));
+    append_text(_(L("Enter the temperature needed for extruding your filament.")));
+    append_text(_(L("A rule of thumb is 160 to 230 °C for PLA, and 215 to 250 °C for ABS.")));
 
-	auto *sizer_extr = new wxFlexGridSizer(3, 5, 5);
-	auto *text_extr = new wxStaticText(this, wxID_ANY, _(L("Extrusion Temperature:")));
-	auto *unit_extr = new wxStaticText(this, wxID_ANY, _(L("°C")));
-	sizer_extr->AddGrowableCol(0, 1);
-	sizer_extr->Add(text_extr, 0, wxALIGN_CENTRE_VERTICAL);
-	sizer_extr->Add(spin_extr);
-	sizer_extr->Add(unit_extr, 0, wxALIGN_CENTRE_VERTICAL);
-	append(sizer_extr);
+    auto *sizer_extr = new wxFlexGridSizer(3, 5, 5);
+    auto *text_extr = new wxStaticText(this, wxID_ANY, _(L("Extrusion Temperature:")));
+    auto *unit_extr = new wxStaticText(this, wxID_ANY, _(L("°C")));
+    sizer_extr->AddGrowableCol(0, 1);
+    sizer_extr->Add(text_extr, 0, wxALIGN_CENTRE_VERTICAL);
+    sizer_extr->Add(spin_extr);
+    sizer_extr->Add(unit_extr, 0, wxALIGN_CENTRE_VERTICAL);
+    append(sizer_extr);
 
-	append_spacer(VERTICAL_SPACING);
+    append_spacer(VERTICAL_SPACING);
 
-	append_text(_(L("Enter the bed temperature needed for getting your filament to stick to your heated bed.")));
-	append_text(_(L("A rule of thumb is 60 °C for PLA and 110 °C for ABS. Leave zero if you have no heated bed.")));
+    append_text(_(L("Enter the bed temperature needed for getting your filament to stick to your heated bed.")));
+    append_text(_(L("A rule of thumb is 60 °C for PLA and 110 °C for ABS. Leave zero if you have no heated bed.")));
 
-	auto *sizer_bed = new wxFlexGridSizer(3, 5, 5);
-	auto *text_bed = new wxStaticText(this, wxID_ANY, _(L("Bed Temperature:")));
-	auto *unit_bed = new wxStaticText(this, wxID_ANY, _(L("°C")));
-	sizer_bed->AddGrowableCol(0, 1);
-	sizer_bed->Add(text_bed, 0, wxALIGN_CENTRE_VERTICAL);
-	sizer_bed->Add(spin_bed);
-	sizer_bed->Add(unit_bed, 0, wxALIGN_CENTRE_VERTICAL);
-	append(sizer_bed);
+    auto *sizer_bed = new wxFlexGridSizer(3, 5, 5);
+    auto *text_bed = new wxStaticText(this, wxID_ANY, _(L("Bed Temperature:")));
+    auto *unit_bed = new wxStaticText(this, wxID_ANY, _(L("°C")));
+    sizer_bed->AddGrowableCol(0, 1);
+    sizer_bed->Add(text_bed, 0, wxALIGN_CENTRE_VERTICAL);
+    sizer_bed->Add(spin_bed);
+    sizer_bed->Add(unit_bed, 0, wxALIGN_CENTRE_VERTICAL);
+    append(sizer_bed);
 }
 
 void PageTemperatures::apply_custom_config(DynamicPrintConfig &config)
 {
-	auto *opt_extr = new ConfigOptionInts(1, spin_extr->GetValue());
-	config.set_key_value("temperature", opt_extr);
-	auto *opt_extr1st = new ConfigOptionInts(1, spin_extr->GetValue());
-	config.set_key_value("first_layer_temperature", opt_extr1st);
-	auto *opt_bed = new ConfigOptionInts(1, spin_bed->GetValue());
-	config.set_key_value("bed_temperature", opt_bed);
-	auto *opt_bed1st = new ConfigOptionInts(1, spin_bed->GetValue());
-	config.set_key_value("first_layer_bed_temperature", opt_bed1st);
+    auto *opt_extr = new ConfigOptionInts(1, spin_extr->GetValue());
+    config.set_key_value("temperature", opt_extr);
+    auto *opt_extr1st = new ConfigOptionInts(1, spin_extr->GetValue());
+    config.set_key_value("first_layer_temperature", opt_extr1st);
+    auto *opt_bed = new ConfigOptionInts(1, spin_bed->GetValue());
+    config.set_key_value("bed_temperature", opt_bed);
+    auto *opt_bed1st = new ConfigOptionInts(1, spin_bed->GetValue());
+    config.set_key_value("first_layer_bed_temperature", opt_bed1st);
 }
 
 
 // Index
 
-ConfigWizardIndex::ConfigWizardIndex(wxWindow *parent) :
-	wxPanel(parent),
-	bg(GUI::from_u8(Slic3r::var("Slic3r_192px_transparent.png")), wxBITMAP_TYPE_PNG),
-	bullet_black(GUI::from_u8(Slic3r::var("bullet_black.png")), wxBITMAP_TYPE_PNG),
-	bullet_blue(GUI::from_u8(Slic3r::var("bullet_blue.png")), wxBITMAP_TYPE_PNG),
-	bullet_white(GUI::from_u8(Slic3r::var("bullet_white.png")), wxBITMAP_TYPE_PNG)
+ConfigWizardIndex::ConfigWizardIndex(wxWindow *parent)
+    : wxPanel(parent)
+    , bg(GUI::from_u8(Slic3r::var("Slic3r_192px_transparent.png")), wxBITMAP_TYPE_PNG)
+    , bullet_black(GUI::from_u8(Slic3r::var("bullet_black.png")), wxBITMAP_TYPE_PNG)
+    , bullet_blue(GUI::from_u8(Slic3r::var("bullet_blue.png")), wxBITMAP_TYPE_PNG)
+    , bullet_white(GUI::from_u8(Slic3r::var("bullet_white.png")), wxBITMAP_TYPE_PNG)
+    , item_active(0)
+    , item_hover(-1)
+    , last_page((size_t)-1)
 {
-	SetMinSize(bg.GetSize());
+    SetMinSize(bg.GetSize());
 
-	wxClientDC dc(this);
-	text_height = dc.GetCharHeight();
+    const wxSize size = GetTextExtent("m");
+    em = size.x;
+    em_h = size.y;
 
-	// Add logo bitmap.
-	// This could be done in on_paint() along with the index labels, but I've found it tricky
-	// to get the bitmap rendered well on all platforms with transparent background.
-	// In some cases it didn't work at all. And so wxStaticBitmap is used here instead,
-	// because it has all the platform quirks figured out.
-	auto *sizer = new wxBoxSizer(wxVERTICAL);
-	auto *logo = new wxStaticBitmap(this, wxID_ANY, bg);
-	sizer->AddStretchSpacer();
-	sizer->Add(logo);
-	SetSizer(sizer);
+    // Add logo bitmap.
+    // This could be done in on_paint() along with the index labels, but I've found it tricky
+    // to get the bitmap rendered well on all platforms with transparent background.
+    // In some cases it didn't work at all. And so wxStaticBitmap is used here instead,
+    // because it has all the platform quirks figured out.
+    auto *sizer = new wxBoxSizer(wxVERTICAL);
+    auto *logo = new wxStaticBitmap(this, wxID_ANY, bg);
+    sizer->AddStretchSpacer();
+    sizer->Add(logo);
+    SetSizer(sizer);
 
-	Bind(wxEVT_PAINT, &ConfigWizardIndex::on_paint, this);
+    Bind(wxEVT_PAINT, &ConfigWizardIndex::on_paint, this);
+    Bind(wxEVT_MOTION, &ConfigWizardIndex::on_mouse_move, this);
+
+    Bind(wxEVT_LEAVE_WINDOW, [this](wxMouseEvent &evt) {
+        if (item_hover != -1) {
+            item_hover = -1;
+            Refresh();
+        }
+        evt.Skip();
+    });
+
+    Bind(wxEVT_LEFT_UP, [this](wxMouseEvent &evt) {
+        if (item_hover >= 0) { go_to(item_hover); }
+    });
 }
 
-void ConfigWizardIndex::load_items(ConfigWizardPage *firstpage)
+wxDECLARE_EVENT(EVT_INDEX_PAGE, wxCommandEvent);
+
+void ConfigWizardIndex::add_page(ConfigWizardPage *page)
 {
-	items.clear();
-	item_active = items.cend();
-
-	for (auto *page = firstpage; page != nullptr; page = page->page_next()) {
-		items.emplace_back(page->shortname);
-	}
-
-	Refresh();
+    last_page = items.size();
+    items.emplace_back(Item { page->shortname, page->indent, page });
+    Refresh();
 }
 
-void ConfigWizardIndex::set_active(ConfigWizardPage *page)
+void ConfigWizardIndex::add_label(wxString label, unsigned indent)
 {
-	item_active = std::find(items.cbegin(), items.cend(), page->shortname);
-	Refresh();
+    items.emplace_back(Item { std::move(label), indent, nullptr });
+    Refresh();
+}
+
+ConfigWizardPage* ConfigWizardIndex::active_page() const
+{
+    if (item_active >= items.size()) { return nullptr; }
+
+    return items[item_active].page;
+}
+
+void ConfigWizardIndex::go_prev()
+{
+    // Search for a preceiding item that is a page (not a label, ie. page != nullptr)
+
+    for (size_t i = item_active; i > 0; i--) {
+        if (items[i - 1].page != nullptr) {
+            go_to(i - 1);
+            return;
+        }
+    }
+}
+
+void ConfigWizardIndex::go_next()
+{
+    // Search for a next item that is a page (not a label, ie. page != nullptr)
+
+    for (size_t i = item_active + 1; i < items.size(); i++) {
+        if (items[i].page != nullptr) {
+            go_to(i);
+            return;
+        }
+    }
+}
+
+void ConfigWizardIndex::go_to(size_t i)
+{
+    if (i < items.size() && items[i].page != nullptr) {
+        auto *former_active = active_page();
+        if (former_active != nullptr) { former_active->Hide(); }
+
+        item_active = i;
+        items[i].page->Show();
+
+        wxCommandEvent evt(EVT_INDEX_PAGE, GetId());
+        AddPendingEvent(evt);
+
+        Refresh();
+    }
+}
+
+void ConfigWizardIndex::go_to(ConfigWizardPage *page)
+{
+    if (page == nullptr) { return; }
+
+    for (size_t i = 0; i < items.size(); i++) {
+        if (items[i].page == page) { go_to(i); }
+    }
+}
+
+void ConfigWizardIndex::clear()
+{
+    auto *former_active = active_page();
+    if (former_active != nullptr) { former_active->Hide(); }
+
+    items.clear();
+    item_active = 0;
 }
 
 void ConfigWizardIndex::on_paint(wxPaintEvent & evt)
 {
-	enum {
-		MARGIN = 10,
-		SPACING = 5,
-	};
+    const auto size = GetClientSize();
+    if (size.GetHeight() == 0 || size.GetWidth() == 0) { return; }
 
-	const auto size = GetClientSize();
-	if (size.GetHeight() == 0 || size.GetWidth() == 0) { return; }
+    wxPaintDC dc(this);
 
-	wxPaintDC dc(this);
+    const auto bullet_w = bullet_black.GetSize().GetWidth();
+    const auto bullet_h = bullet_black.GetSize().GetHeight();
+    const int yoff_icon = bullet_h < em_h ? (em_h - bullet_h) / 2 : 0;
+    const int yoff_text = bullet_h > em_h ? (bullet_h - em_h) / 2 : 0;
+    const int yinc = item_height();
 
-	const auto bullet_w = bullet_black.GetSize().GetWidth();
-	const auto bullet_h = bullet_black.GetSize().GetHeight();
-	const int yoff_icon = bullet_h < text_height ? (text_height - bullet_h) / 2 : 0;
-	const int yoff_text = bullet_h > text_height ? (bullet_h - text_height) / 2 : 0;
-	const int yinc = std::max(bullet_h, text_height) + SPACING;
+    unsigned y = 0;
+    for (size_t i = 0; i < items.size(); i++) {
+        const Item& item = items[i];
+        unsigned x = em/2 + item.indent * em;
 
-	unsigned y = 0;
-	for (auto it = items.cbegin(); it != items.cend(); ++it) {
-		if (it < item_active)  { dc.DrawBitmap(bullet_black, MARGIN, y + yoff_icon, false); }
-		if (it == item_active) { dc.DrawBitmap(bullet_blue,  MARGIN, y + yoff_icon, false); }
-		if (it > item_active)  { dc.DrawBitmap(bullet_white, MARGIN, y + yoff_icon, false); }
-		dc.DrawText(*it, MARGIN + bullet_w + SPACING, y + yoff_text);
-		y += yinc;
-	}
+        if (i == item_active || item_hover >= 0 && i == (size_t)item_hover) {
+            dc.DrawBitmap(bullet_blue,  x, y + yoff_icon, false);
+        }
+        else if (i < item_active)  { dc.DrawBitmap(bullet_black, x, y + yoff_icon, false); }
+        else if (i > item_active)  { dc.DrawBitmap(bullet_white, x, y + yoff_icon, false); }
+
+        dc.DrawText(item.label, x + bullet_w + em/2, y + yoff_text);
+        y += yinc;
+    }
 }
 
+void ConfigWizardIndex::on_mouse_move(wxMouseEvent &evt)
+{
+    const wxClientDC dc(this);
+    const wxPoint pos = evt.GetLogicalPosition(dc);
+
+    const ssize_t item_hover_new = pos.y / item_height();
+
+    if (item_hover_new < items.size() && item_hover_new != item_hover) {
+        item_hover = item_hover_new;
+        Refresh();
+    }
+
+    evt.Skip();
+}
 
 
 // priv
 
 static const std::unordered_map<std::string, std::pair<std::string, std::string>> legacy_preset_map {{
-	{ "Original Prusa i3 MK2.ini",                           std::make_pair("MK2S", "0.4") },
-	{ "Original Prusa i3 MK2 MM Single Mode.ini",            std::make_pair("MK2SMM", "0.4") },
-	{ "Original Prusa i3 MK2 MM Single Mode 0.6 nozzle.ini", std::make_pair("MK2SMM", "0.6") },
-	{ "Original Prusa i3 MK2 MultiMaterial.ini",             std::make_pair("MK2SMM", "0.4") },
-	{ "Original Prusa i3 MK2 MultiMaterial 0.6 nozzle.ini",  std::make_pair("MK2SMM", "0.6") },
-	{ "Original Prusa i3 MK2 0.25 nozzle.ini",               std::make_pair("MK2S", "0.25") },
-	{ "Original Prusa i3 MK2 0.6 nozzle.ini",                std::make_pair("MK2S", "0.6") },
-	{ "Original Prusa i3 MK3.ini",                           std::make_pair("MK3",  "0.4") },
+    { "Original Prusa i3 MK2.ini",                           std::make_pair("MK2S", "0.4") },
+    { "Original Prusa i3 MK2 MM Single Mode.ini",            std::make_pair("MK2SMM", "0.4") },
+    { "Original Prusa i3 MK2 MM Single Mode 0.6 nozzle.ini", std::make_pair("MK2SMM", "0.6") },
+    { "Original Prusa i3 MK2 MultiMaterial.ini",             std::make_pair("MK2SMM", "0.4") },
+    { "Original Prusa i3 MK2 MultiMaterial 0.6 nozzle.ini",  std::make_pair("MK2SMM", "0.6") },
+    { "Original Prusa i3 MK2 0.25 nozzle.ini",               std::make_pair("MK2S", "0.25") },
+    { "Original Prusa i3 MK2 0.6 nozzle.ini",                std::make_pair("MK2S", "0.6") },
+    { "Original Prusa i3 MK3.ini",                           std::make_pair("MK3",  "0.4") },
 }};
 
+void ConfigWizard::priv::load_pages(bool custom_setup)
+{
+    const auto former_active = index->active_item();
+
+    index->clear();
+
+    index->add_page(page_welcome);
+    index->add_page(page_fff);
+    index->add_page(page_msla);
+    index->add_page(page_custom);
+
+    if (custom_setup) {
+        index->add_page(page_firmware);
+        index->add_page(page_bed);
+        index->add_page(page_diams);
+        index->add_page(page_temps);
+    }
+
+    index->add_page(page_update);
+
+    index->go_to(former_active);   // Will restore the active item/page if possible
+
+    q->Layout();
+}
+
+bool ConfigWizard::priv::check_first_variant() const
+{
+    return run_reason == RR_DATA_EMPTY || run_reason == RR_DATA_LEGACY;
+}
+
 void ConfigWizard::priv::load_vendors()
 {
-	const auto vendor_dir = fs::path(Slic3r::data_dir()) / "vendor";
-	const auto rsrc_vendor_dir = fs::path(resources_dir()) / "profiles";
+    const auto vendor_dir = fs::path(Slic3r::data_dir()) / "vendor";
+    const auto rsrc_vendor_dir = fs::path(resources_dir()) / "profiles";
 
-	// Load vendors from the "vendors" directory in datadir
+    // Load vendors from the "vendors" directory in datadir
     for (auto &dir_entry : boost::filesystem::directory_iterator(vendor_dir))
-		if (Slic3r::is_ini_file(dir_entry)) {
-			try {
-				auto vp = VendorProfile::from_ini(dir_entry.path());
-				vendors[vp.id] = std::move(vp);
-			}
-			catch (const std::exception& e) {
-				BOOST_LOG_TRIVIAL(error) << boost::format("Error loading vendor bundle %1%: %2%") % dir_entry.path() % e.what();
-			}
-		}
+        if (Slic3r::is_ini_file(dir_entry)) {
+            try {
+                auto vp = VendorProfile::from_ini(dir_entry.path());
+                vendors[vp.id] = std::move(vp);
+            }
+            catch (const std::exception& e) {
+                BOOST_LOG_TRIVIAL(error) << boost::format("Error loading vendor bundle %1%: %2%") % dir_entry.path() % e.what();
+            }
+        }
 
-	// Additionally load up vendors from the application resources directory, but only those not seen in the datadir
+    // Additionally load up vendors from the application resources directory, but only those not seen in the datadir
     for (auto &dir_entry : boost::filesystem::directory_iterator(rsrc_vendor_dir))
-		if (Slic3r::is_ini_file(dir_entry)) {
-			const auto id = dir_entry.path().stem().string();
-			if (vendors.find(id) == vendors.end()) {
-				try {
-					auto vp = VendorProfile::from_ini(dir_entry.path());
-					vendors_rsrc[vp.id] = dir_entry.path().filename().string();
-					vendors[vp.id] = std::move(vp);
-				}
-				catch (const std::exception& e) {
-					BOOST_LOG_TRIVIAL(error) << boost::format("Error loading vendor bundle %1%: %2%") % dir_entry.path() % e.what();
-				}
-			}
-		}
+        if (Slic3r::is_ini_file(dir_entry)) {
+            const auto id = dir_entry.path().stem().string();
+            if (vendors.find(id) == vendors.end()) {
+                try {
+                    auto vp = VendorProfile::from_ini(dir_entry.path());
+                    vendors_rsrc[vp.id] = dir_entry.path().filename().string();
+                    vendors[vp.id] = std::move(vp);
+                }
+                catch (const std::exception& e) {
+                    BOOST_LOG_TRIVIAL(error) << boost::format("Error loading vendor bundle %1%: %2%") % dir_entry.path() % e.what();
+                }
+            }
+        }
 
-	// Load up the set of vendors / models / variants the user has had enabled up till now
-	const AppConfig *app_config = GUI::get_app_config();
-	if (! app_config->legacy_datadir()) {
-		appconfig_vendors.set_vendors(*app_config);
-	} else {
-		// In case of legacy datadir, try to guess the preference based on the printer preset files that are present
-		const auto printer_dir = fs::path(Slic3r::data_dir()) / "printer";
-	    for (auto &dir_entry : boost::filesystem::directory_iterator(printer_dir))
-	    	if (Slic3r::is_ini_file(dir_entry)) {
-				auto needle = legacy_preset_map.find(dir_entry.path().filename().string());
-				if (needle == legacy_preset_map.end()) { continue; }
+    // Load up the set of vendors / models / variants the user has had enabled up till now
+    const AppConfig *app_config = GUI::get_app_config();
+    if (! app_config->legacy_datadir()) {
+        appconfig_vendors.set_vendors(*app_config);
+    } else {
+        // In case of legacy datadir, try to guess the preference based on the printer preset files that are present
+        const auto printer_dir = fs::path(Slic3r::data_dir()) / "printer";
+        for (auto &dir_entry : boost::filesystem::directory_iterator(printer_dir))
+            if (Slic3r::is_ini_file(dir_entry)) {
+                auto needle = legacy_preset_map.find(dir_entry.path().filename().string());
+                if (needle == legacy_preset_map.end()) { continue; }
 
-				const auto &model = needle->second.first;
-				const auto &variant = needle->second.second;
-				appconfig_vendors.set_variant("PrusaResearch", model, variant, true);
-			}
-	}
-}
-
-void ConfigWizard::priv::index_refresh()
-{
-	index->load_items(page_welcome);
+                const auto &model = needle->second.first;
+                const auto &variant = needle->second.second;
+                appconfig_vendors.set_variant("PrusaResearch", model, variant, true);
+            }
+    }
 }
 
 void ConfigWizard::priv::add_page(ConfigWizardPage *page)
 {
-	hscroll_sizer->Add(page, 0, wxEXPAND);
-
-	auto *extra_buttons = page->extra_buttons();
-	if (extra_buttons != nullptr) {
-		btnsizer->Prepend(extra_buttons, 0);
-	}
-}
-
-void ConfigWizard::priv::set_page(ConfigWizardPage *page)
-{
-	if (page == nullptr) { return; }
-	if (page_current != nullptr) { page_current->Hide(); }
-	page_current = page;
-	enable_next(true);
-
-	page->on_page_set();
-	index->load_items(page_welcome);
-	index->set_active(page);
-	page->Show();
-
-	btn_prev->Enable(page->page_prev() != nullptr);
-	btn_next->Show(page->page_next() != nullptr);
-	btn_finish->Show(page->page_next() == nullptr);
-
-	layout_fit();
-}
-
-void ConfigWizard::priv::layout_fit()
-{
-	q->Layout();
-	q->Fit();
+    hscroll_sizer->Add(page, 0, wxEXPAND);
 }
 
 void ConfigWizard::priv::enable_next(bool enable)
 {
-	btn_next->Enable(enable);
-	btn_finish->Enable(enable);
+    btn_next->Enable(enable);
+    btn_finish->Enable(enable);
 }
 
-void ConfigWizard::priv::on_other_vendors()
+void ConfigWizard::priv::on_custom_setup(bool custom_wanted)
 {
-	page_welcome
-		->chain(page_vendors)
-		->chain(page_update);
-	set_page(page_vendors);
-}
-
-void ConfigWizard::priv::on_custom_setup()
-{
-	page_welcome->chain(page_firmware);
-	page_temps->chain(page_update);
-	set_page(page_firmware);
+    load_pages(custom_wanted);
 }
 
 void ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater)
 {
-	const bool is_custom_setup = page_welcome->page_next() == page_firmware;
+    const auto enabled_vendors = appconfig_vendors.vendors();
 
-	if (! is_custom_setup) {
-		const auto enabled_vendors = appconfig_vendors.vendors();
+    // Install bundles from resources if needed:
+    std::vector<std::string> install_bundles;
+    for (const auto &vendor_rsrc : vendors_rsrc) {
+        const auto vendor = enabled_vendors.find(vendor_rsrc.first);
+        if (vendor == enabled_vendors.end()) { continue; }
 
-		// Install bundles from resources if needed:
-		std::vector<std::string> install_bundles;
-		for (const auto &vendor_rsrc : vendors_rsrc) {
-			const auto vendor = enabled_vendors.find(vendor_rsrc.first);
-			if (vendor == enabled_vendors.end()) { continue; }
+        size_t size_sum = 0;
+        for (const auto &model : vendor->second) { size_sum += model.second.size(); }
+        if (size_sum == 0) { continue; }
 
-			size_t size_sum = 0;
-			for (const auto &model : vendor->second) { size_sum += model.second.size(); }
-			if (size_sum == 0) { continue; }
+        // This vendor needs to be installed
+        install_bundles.emplace_back(vendor_rsrc.second);
+    }
 
-			// This vendor needs to be installed
-			install_bundles.emplace_back(vendor_rsrc.second);
-		}
+    // Decide whether to create snapshot based on run_reason and the reset profile checkbox
+    bool snapshot = true;
+    switch (run_reason) {
+        case ConfigWizard::RR_DATA_EMPTY:    snapshot = false; break;
+        case ConfigWizard::RR_DATA_LEGACY:   snapshot = true; break;
+        case ConfigWizard::RR_DATA_INCOMPAT: snapshot = false; break;      // In this case snapshot is done by PresetUpdater with the appropriate reason
+        case ConfigWizard::RR_USER:          snapshot = page_welcome->reset_user_profile(); break;
+    }
+    if (install_bundles.size() > 0) {
+        // Install bundles from resources.
+        updater->install_bundles_rsrc(std::move(install_bundles), snapshot);
+    } else {
+        BOOST_LOG_TRIVIAL(info) << "No bundles need to be installed from resources";
+    }
 
-		// Decide whether to create snapshot based on run_reason and the reset profile checkbox
-		bool snapshot = true;
-		switch (run_reason) {
-			case ConfigWizard::RR_DATA_EMPTY:    snapshot = false; break;
-			case ConfigWizard::RR_DATA_LEGACY:   snapshot = true; break;
-			case ConfigWizard::RR_DATA_INCOMPAT: snapshot = false; break;      // In this case snapshot is done by PresetUpdater with the appropriate reason
-			case ConfigWizard::RR_USER:          snapshot = page_welcome->reset_user_profile(); break;
-		}
-		if (install_bundles.size() > 0) {
-			// Install bundles from resources.
-			updater->install_bundles_rsrc(std::move(install_bundles), snapshot);
-		} else {
-			BOOST_LOG_TRIVIAL(info) << "No bundles need to be installed from resources";
-		}
+    if (page_welcome->reset_user_profile()) {
+        BOOST_LOG_TRIVIAL(info) << "Resetting user profiles...";
+        preset_bundle->reset(true);
+    }
 
-		if (page_welcome->reset_user_profile()) {
-			BOOST_LOG_TRIVIAL(info) << "Resetting user profiles...";
-			preset_bundle->reset(true);
-		}
+    app_config->set_vendors(appconfig_vendors);
+    app_config->set("version_check", page_update->version_check ? "1" : "0");
+    app_config->set("preset_update", page_update->preset_update ? "1" : "0");
+    app_config->reset_selections();
+    preset_bundle->load_presets(*app_config);
 
-		app_config->set_vendors(appconfig_vendors);
-		app_config->set("version_check", page_update->version_check ? "1" : "0");
-		app_config->set("preset_update", page_update->preset_update ? "1" : "0");
-		app_config->reset_selections();
-		preset_bundle->load_presets(*app_config);
-	} else {
-		for (ConfigWizardPage *page = page_firmware; page != nullptr; page = page->page_next()) {
-			page->apply_custom_config(*custom_config);
-		}
-		preset_bundle->load_config("My Settings", *custom_config);
-	}
-	// Update the selections from the compatibilty.
-	preset_bundle->export_selections(*app_config);
+
+    if (page_custom->custom_wanted()) {
+        page_firmware->apply_custom_config(*custom_config);
+        page_bed->apply_custom_config(*custom_config);
+        page_diams->apply_custom_config(*custom_config);
+        page_temps->apply_custom_config(*custom_config);
+
+        const std::string profile_name = page_custom->profile_name();
+        preset_bundle->load_config(profile_name, *custom_config);
+    }
+
+    // Update the selections from the compatibilty.
+    preset_bundle->export_selections(*app_config);
 }
 
 // Public
 
-ConfigWizard::ConfigWizard(wxWindow *parent, RunReason reason) :
-	wxDialog(parent, wxID_ANY, _(name().ToStdString()), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER),
-	p(new priv(this))
+ConfigWizard::ConfigWizard(wxWindow *parent, RunReason reason)
+    : wxDialog(parent, wxID_ANY, _(name().ToStdString()), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
+    , p(new priv(this))
 {
-	p->run_reason = reason;
+    p->run_reason = reason;
 
-	p->load_vendors();
-	p->custom_config.reset(DynamicPrintConfig::new_from_defaults_keys({
-		"gcode_flavor", "bed_shape", "nozzle_diameter", "filament_diameter", "temperature", "bed_temperature",
-	}));
+    p->load_vendors();
+    p->custom_config.reset(DynamicPrintConfig::new_from_defaults_keys({
+        "gcode_flavor", "bed_shape", "nozzle_diameter", "filament_diameter", "temperature", "bed_temperature",
+    }));
 
-	p->index = new ConfigWizardIndex(this);
+    p->index = new ConfigWizardIndex(this);
 
-	auto *vsizer = new wxBoxSizer(wxVERTICAL);
-	auto *topsizer = new wxBoxSizer(wxHORIZONTAL);
-	auto *hline = new wxStaticLine(this);
-	p->btnsizer = new wxBoxSizer(wxHORIZONTAL);
+    auto *vsizer = new wxBoxSizer(wxVERTICAL);
+    auto *topsizer = new wxBoxSizer(wxHORIZONTAL);
+    auto *hline = new wxStaticLine(this);
+    p->btnsizer = new wxBoxSizer(wxHORIZONTAL);
 
-	// Initially we _do not_ SetScrollRate in order to figure out the overall width of the Wizard  without scrolling.
-	// Later, we compare that to the size of the current screen and set minimum width based on that (see below).
-	p->hscroll = new wxScrolledWindow(this);
-	p->hscroll_sizer = new wxBoxSizer(wxHORIZONTAL);
-	p->hscroll->SetSizer(p->hscroll_sizer);
+    // Initially we _do not_ SetScrollRate in order to figure out the overall width of the Wizard  without scrolling.
+    // Later, we compare that to the size of the current screen and set minimum width based on that (see below).
+    p->hscroll = new wxScrolledWindow(this);
+    p->hscroll_sizer = new wxBoxSizer(wxHORIZONTAL);
+    p->hscroll->SetSizer(p->hscroll_sizer);
 
-	topsizer->Add(p->index, 0, wxEXPAND);
-	topsizer->AddSpacer(INDEX_MARGIN);
-	topsizer->Add(p->hscroll, 1, wxEXPAND);
+    topsizer->Add(p->index, 0, wxEXPAND);
+    topsizer->AddSpacer(INDEX_MARGIN);
+    topsizer->Add(p->hscroll, 1, wxEXPAND);
 
-	p->btn_prev = new wxButton(this, wxID_ANY, _(L("< &Back")));
-	p->btn_next = new wxButton(this, wxID_ANY, _(L("&Next >")));
-	p->btn_finish = new wxButton(this, wxID_APPLY, _(L("&Finish")));
-	p->btn_cancel = new wxButton(this, wxID_CANCEL);
-	p->btnsizer->AddStretchSpacer();
-	p->btnsizer->Add(p->btn_prev, 0, wxLEFT, BTN_SPACING);
-	p->btnsizer->Add(p->btn_next, 0, wxLEFT, BTN_SPACING);
-	p->btnsizer->Add(p->btn_finish, 0, wxLEFT, BTN_SPACING);
-	p->btnsizer->Add(p->btn_cancel, 0, wxLEFT, BTN_SPACING);
+    auto *btn_sel_all = new wxButton(this, wxID_ANY, _(L("Select all printers")));
+    p->btnsizer->Add(btn_sel_all);
 
-	p->add_page(p->page_welcome  = new PageWelcome(this, reason == RR_DATA_EMPTY || reason == RR_DATA_LEGACY));
-	p->add_page(p->page_update   = new PageUpdate(this));
-	p->add_page(p->page_vendors  = new PageVendors(this));
-	p->add_page(p->page_firmware = new PageFirmware(this));
-	p->add_page(p->page_bed      = new PageBedShape(this));
-	p->add_page(p->page_diams    = new PageDiameters(this));
-	p->add_page(p->page_temps    = new PageTemperatures(this));
-	p->index_refresh();
+    p->btn_prev = new wxButton(this, wxID_ANY, _(L("< &Back")));
+    p->btn_next = new wxButton(this, wxID_ANY, _(L("&Next >")));
+    p->btn_finish = new wxButton(this, wxID_APPLY, _(L("&Finish")));
+    p->btn_cancel = new wxButton(this, wxID_CANCEL);
+    p->btnsizer->AddStretchSpacer();
+    p->btnsizer->Add(p->btn_prev, 0, wxLEFT, BTN_SPACING);
+    p->btnsizer->Add(p->btn_next, 0, wxLEFT, BTN_SPACING);
+    p->btnsizer->Add(p->btn_finish, 0, wxLEFT, BTN_SPACING);
+    p->btnsizer->Add(p->btn_cancel, 0, wxLEFT, BTN_SPACING);
 
-	p->page_welcome->chain(p->page_update);
-	p->page_firmware
-		->chain(p->page_bed)
-		->chain(p->page_diams)
-		->chain(p->page_temps);
+    const auto &vendors = p->vendors;
+    const auto vendor_prusa_it = vendors.find("PrusaResearch");
+    wxCHECK_RET(vendor_prusa_it != vendors.cend(), "Vendor PrusaResearch not found");
+    const VendorProfile &vendor_prusa = vendor_prusa_it->second;
 
-	vsizer->Add(topsizer, 1, wxEXPAND | wxALL, DIALOG_MARGIN);
-	vsizer->Add(hline, 0, wxEXPAND);
-	vsizer->Add(p->btnsizer, 0, wxEXPAND | wxALL, DIALOG_MARGIN);
+    p->add_page(p->page_welcome = new PageWelcome(this));
 
-	p->set_page(p->page_welcome);
-	SetSizer(vsizer);
-	SetSizerAndFit(vsizer);
+    p->page_fff = new PagePrinters(this, _(L("Prusa FFF Technology Printers")), "Prusa FFF", vendor_prusa, 0, PagePrinters::T_FFF);
+    p->add_page(p->page_fff);
 
-	// We can now enable scrolling on hscroll
-	p->hscroll->SetScrollRate(30, 30);
-	// Compare current ("ideal") wizard size with the size of the current screen.
-	// If the screen is smaller, resize wizrad to match, which will enable scrollbars.
-	auto wizard_size = GetSize();
-	unsigned width, height;
-	if (GUI::get_current_screen_size(this, width, height)) {
-		wizard_size.SetWidth(std::min(wizard_size.GetWidth(), (int)(width - 2 * DIALOG_MARGIN)));
-		wizard_size.SetHeight(std::min(wizard_size.GetHeight(), (int)(height - 2 * DIALOG_MARGIN)));
-		SetMinSize(wizard_size);
-	}
-	Fit();
+    p->page_msla = new PagePrinters(this, _(L("Prusa MSLA Technology Printers")), "Prusa MSLA", vendor_prusa, 0, PagePrinters::T_SLA);
+    p->add_page(p->page_msla);
 
-	p->btn_prev->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &evt) { this->p->go_prev(); });
-	p->btn_next->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &evt) { this->p->go_next(); });
-	p->btn_finish->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &evt) { this->EndModal(wxID_OK); });
+    p->add_page(p->page_custom   = new PageCustom(this));
+    p->add_page(p->page_update   = new PageUpdate(this));
+    p->add_page(p->page_vendors  = new PageVendors(this));
+    p->add_page(p->page_firmware = new PageFirmware(this));
+    p->add_page(p->page_bed      = new PageBedShape(this));
+    p->add_page(p->page_diams    = new PageDiameters(this));
+    p->add_page(p->page_temps    = new PageTemperatures(this));
+
+    p->load_pages(false);
+
+    vsizer->Add(topsizer, 1, wxEXPAND | wxALL, DIALOG_MARGIN);
+    vsizer->Add(hline, 0, wxEXPAND);
+    vsizer->Add(p->btnsizer, 0, wxEXPAND | wxALL, DIALOG_MARGIN);
+
+    SetSizer(vsizer);
+    SetSizerAndFit(vsizer);
+
+    // We can now enable scrolling on hscroll
+    p->hscroll->SetScrollRate(30, 30);
+
+    // XXX: set size right away on windows, create util function
+    Bind(wxEVT_CREATE, [this](wxWindowCreateEvent &event) {
+        CallAfter([this]() {
+            // Clamp the Wizard size based on screen dimensions
+            // Note: Using EVT_SHOW + CallAfter because any sooner than this
+            // on some Linux boxes wxDisplay::GetFromWindow() returns 0 no matter what.
+
+            const auto idx = wxDisplay::GetFromWindow(this);
+            wxDisplay display(idx != wxNOT_FOUND ? idx : 0u);
+
+            const auto disp_rect = display.GetClientArea();
+            wxRect window_rect(
+                disp_rect.x + disp_rect.width / 20,
+                disp_rect.y + disp_rect.height / 20,
+                9*disp_rect.width / 10,
+                9*disp_rect.height / 10);
+
+            SetSize(window_rect);
+        });
+    });
+
+    p->btn_prev->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &) { this->p->index->go_prev(); });
+    p->btn_next->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &) { this->p->index->go_next(); });
+    p->btn_finish->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &) { this->EndModal(wxID_OK); });
+    p->btn_finish->Hide();
+
+    btn_sel_all->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &) {
+        p->page_fff->select_all(true);
+        p->page_msla->select_all(true);
+        p->index->go_to(p->page_update);
+    });
+
+    p->index->Bind(EVT_INDEX_PAGE, [this](const wxCommandEvent &) {
+        const bool is_last = p->index->active_is_last();
+        p->btn_next->Show(! is_last);
+        p->btn_finish->Show(is_last);
+
+        Layout();
+    });
 }
 
 ConfigWizard::~ConfigWizard() {}
 
 bool ConfigWizard::run(PresetBundle *preset_bundle, const PresetUpdater *updater)
 {
-	BOOST_LOG_TRIVIAL(info) << "Running ConfigWizard, reason: " << p->run_reason;
-	if (ShowModal() == wxID_OK) {
-		auto *app_config = GUI::get_app_config();
-		p->apply_config(app_config, preset_bundle, updater);
-		app_config->set_legacy_datadir(false);
-		BOOST_LOG_TRIVIAL(info) << "ConfigWizard applied";
-		return true;
-	} else {
-		BOOST_LOG_TRIVIAL(info) << "ConfigWizard cancelled";
-		return false;
-	}
+    BOOST_LOG_TRIVIAL(info) << "Running ConfigWizard, reason: " << p->run_reason;
+    if (ShowModal() == wxID_OK) {
+        auto *app_config = GUI::get_app_config();
+        p->apply_config(app_config, preset_bundle, updater);
+        app_config->set_legacy_datadir(false);
+        BOOST_LOG_TRIVIAL(info) << "ConfigWizard applied";
+        return true;
+    } else {
+        BOOST_LOG_TRIVIAL(info) << "ConfigWizard cancelled";
+        return false;
+    }
 }
 
 
 const wxString& ConfigWizard::name(const bool from_menu/* = false*/)
 {
-	// A different naming convention is used for the Wizard on Windows vs. OSX & GTK.
+    // A different naming convention is used for the Wizard on Windows vs. OSX & GTK.
 #if WIN32
     static const wxString config_wizard_name = L("Configuration Wizard");
     static const wxString config_wizard_name_menu = L("Configuration &Wizard");
 #else
-	static const wxString config_wizard_name =  L("Configuration Assistant");
+    static const wxString config_wizard_name =  L("Configuration Assistant");
     static const wxString config_wizard_name_menu = L("Configuration &Assistant");
 #endif
-	return from_menu ? config_wizard_name_menu : config_wizard_name;
+    return from_menu ? config_wizard_name_menu : config_wizard_name;
 }
 
 }
diff --git a/src/slic3r/GUI/ConfigWizard.hpp b/src/slic3r/GUI/ConfigWizard.hpp
index c7fba76ee..c9ee05529 100644
--- a/src/slic3r/GUI/ConfigWizard.hpp
+++ b/src/slic3r/GUI/ConfigWizard.hpp
@@ -16,30 +16,30 @@ namespace GUI {
 class ConfigWizard: public wxDialog
 {
 public:
-	// Why is the Wizard run
-	enum RunReason {
-		RR_DATA_EMPTY,                  // No or empty datadir
-		RR_DATA_LEGACY,                 // Pre-updating datadir
-		RR_DATA_INCOMPAT,               // Incompatible datadir - Slic3r downgrade situation
-		RR_USER,                        // User requested the Wizard from the menus
-	};
+    // Why is the Wizard run
+    enum RunReason {
+        RR_DATA_EMPTY,                  // No or empty datadir
+        RR_DATA_LEGACY,                 // Pre-updating datadir
+        RR_DATA_INCOMPAT,               // Incompatible datadir - Slic3r downgrade situation
+        RR_USER,                        // User requested the Wizard from the menus
+    };
 
-	ConfigWizard(wxWindow *parent, RunReason run_reason);
-	ConfigWizard(ConfigWizard &&) = delete;
-	ConfigWizard(const ConfigWizard &) = delete;
-	ConfigWizard &operator=(ConfigWizard &&) = delete;
-	ConfigWizard &operator=(const ConfigWizard &) = delete;
-	~ConfigWizard();
+    ConfigWizard(wxWindow *parent, RunReason run_reason);
+    ConfigWizard(ConfigWizard &&) = delete;
+    ConfigWizard(const ConfigWizard &) = delete;
+    ConfigWizard &operator=(ConfigWizard &&) = delete;
+    ConfigWizard &operator=(const ConfigWizard &) = delete;
+    ~ConfigWizard();
 
-	// Run the Wizard. Return whether it was completed.
-	bool run(PresetBundle *preset_bundle, const PresetUpdater *updater);
+    // Run the Wizard. Return whether it was completed.
+    bool run(PresetBundle *preset_bundle, const PresetUpdater *updater);
 
-	static const wxString& name(const bool from_menu = false);
+    static const wxString& name(const bool from_menu = false);
 private:
-	struct priv;
-	std::unique_ptr<priv> p;
+    struct priv;
+    std::unique_ptr<priv> p;
 
-	friend struct ConfigWizardPage;
+    friend struct ConfigWizardPage;
 };
 
 
diff --git a/src/slic3r/GUI/ConfigWizard_private.hpp b/src/slic3r/GUI/ConfigWizard_private.hpp
index 2c8f23cd3..c31911429 100644
--- a/src/slic3r/GUI/ConfigWizard_private.hpp
+++ b/src/slic3r/GUI/ConfigWizard_private.hpp
@@ -6,6 +6,7 @@
 #include <vector>
 #include <set>
 #include <unordered_map>
+#include <functional>
 #include <boost/filesystem.hpp>
 
 #include <wx/sizer.h>
@@ -13,6 +14,7 @@
 #include <wx/button.h>
 #include <wx/choice.h>
 #include <wx/spinctrl.h>
+#include <wx/textctrl.h>
 
 #include "libslic3r/PrintConfig.hpp"
 #include "slic3r/Utils/PresetUpdater.hpp"
@@ -26,211 +28,270 @@ namespace Slic3r {
 namespace GUI {
 
 enum {
-	WRAP_WIDTH = 500,
-	MODEL_MIN_WRAP = 150,
+    WRAP_WIDTH = 500,
+    MODEL_MIN_WRAP = 150,
 
-	DIALOG_MARGIN = 15,
-	INDEX_MARGIN = 40,
-	BTN_SPACING = 10,
-	INDENT_SPACING = 30,
-	VERTICAL_SPACING = 10,
+    DIALOG_MARGIN = 15,
+    INDEX_MARGIN = 40,
+    BTN_SPACING = 10,
+    INDENT_SPACING = 30,
+    VERTICAL_SPACING = 10,
+
+    MAX_COLS = 4,
+    ROW_SPACING = 75,
 };
 
+typedef std::function<bool(const VendorProfile::PrinterModel&)> ModelFilter;
+
 struct PrinterPicker: wxPanel
 {
-	struct Checkbox : wxCheckBox
-	{
-		Checkbox(wxWindow *parent, const wxString &label, const std::string &model, const std::string &variant) :
-			wxCheckBox(parent, wxID_ANY, label),
-			model(model),
-			variant(variant)
-		{}
+    struct Checkbox : wxCheckBox
+    {
+        Checkbox(wxWindow *parent, const wxString &label, const std::string &model, const std::string &variant) :
+            wxCheckBox(parent, wxID_ANY, label),
+            model(model),
+            variant(variant)
+        {}
 
-		std::string model;
-		std::string variant;
-	};
+        std::string model;
+        std::string variant;
+    };
 
-	const std::string vendor_id;
-	std::vector<Checkbox*> cboxes;
-	unsigned variants_checked;
+    const std::string vendor_id;
+    std::vector<Checkbox*> cboxes;
+    std::vector<Checkbox*> cboxes_alt;
 
-	PrinterPicker(wxWindow *parent, const VendorProfile &vendor, const AppConfig &appconfig_vendors);
+    PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxString title, size_t max_cols, const AppConfig &appconfig_vendors, const ModelFilter &filter);
+    PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxString title, size_t max_cols, const AppConfig &appconfig_vendors);
 
-	void select_all(bool select);
-	void select_one(size_t i, bool select);
-	void on_checkbox(const Checkbox *cbox, bool checked);
+    void select_all(bool select);
+    void select_one(size_t i, bool select);
+    void on_checkbox(const Checkbox *cbox, bool checked);
 };
 
 struct ConfigWizardPage: wxPanel
 {
-	ConfigWizard *parent;
-	const wxString shortname;
-	wxBoxSizer *content;
+    ConfigWizard *parent;
+    const wxString shortname;
+    wxBoxSizer *content;
+    const unsigned indent;
 
-	ConfigWizardPage(ConfigWizard *parent, wxString title, wxString shortname);
+    ConfigWizardPage(ConfigWizard *parent, wxString title, wxString shortname, unsigned indent = 0);
+    virtual ~ConfigWizardPage();
 
-	virtual ~ConfigWizardPage();
+    template<class T>
+    void append(T *thing, int proportion = 0, int flag = wxEXPAND|wxTOP|wxBOTTOM, int border = 10)
+    {
+        content->Add(thing, proportion, flag, border);
+    }
 
-	ConfigWizardPage* page_prev() const { return p_prev; }
-	ConfigWizardPage* page_next() const { return p_next; }
-	ConfigWizardPage* chain(ConfigWizardPage *page);
+    void append_text(wxString text);
+    void append_spacer(int space);
 
-	template<class T>
-	void append(T *thing, int proportion = 0, int flag = wxEXPAND|wxTOP|wxBOTTOM, int border = 10)
-	{
-		content->Add(thing, proportion, flag, border);
-	}
+    ConfigWizard::priv *wizard_p() const { return parent->p.get(); }
 
-	void append_text(wxString text);
-	void append_spacer(int space);
-
-	ConfigWizard::priv *wizard_p() const { return parent->p.get(); }
-
-	virtual bool Show(bool show = true);
-	virtual bool Hide() { return Show(false); }
-	virtual wxPanel* extra_buttons() { return nullptr; }
-	virtual void on_page_set() {}
-	virtual void apply_custom_config(DynamicPrintConfig &config) {}
-
-	void enable_next(bool enable);
-private:
-	ConfigWizardPage *p_prev;
-	ConfigWizardPage *p_next;
+    virtual bool Show(bool show = true);
+    virtual bool Hide() { return Show(false); }
+    virtual wxPanel* extra_buttons() { return nullptr; }    // XXX
+    virtual void apply_custom_config(DynamicPrintConfig &config) {}
 };
 
 struct PageWelcome: ConfigWizardPage
 {
-	PrinterPicker *printer_picker;
-	wxPanel *others_buttons;
-	wxCheckBox *cbox_reset;
+    wxCheckBox *cbox_reset;
 
-	PageWelcome(ConfigWizard *parent, bool check_first_variant);
+    PageWelcome(ConfigWizard *parent);
 
-	virtual wxPanel* extra_buttons() { return others_buttons; }
-	virtual void on_page_set();
+    bool reset_user_profile() const { return cbox_reset != nullptr ? cbox_reset->GetValue() : false; }
+};
+
+struct PagePrinters: ConfigWizardPage
+{
+    enum Technology {
+        // Bitflag equivalent of PrinterTechnology
+        T_FFF = 0x1,
+        T_SLA = 0x2,
+        T_Any = ~0,
+    };
+
+    std::vector<PrinterPicker *> printer_pickers;
+
+    PagePrinters(ConfigWizard *parent, wxString title, wxString shortname, const VendorProfile &vendor, unsigned indent, Technology technology);
+
+    void select_all(bool select);
+};
+
+struct PageCustom: ConfigWizardPage
+{
+    PageCustom(ConfigWizard *parent);
+
+    bool custom_wanted() const { return cb_custom->GetValue(); }
+    std::string profile_name() const { return into_u8(tc_profile_name->GetValue()); }
+
+private:
+    wxCheckBox *cb_custom;
+    wxTextCtrl *tc_profile_name;
 
-	bool reset_user_profile() const { return cbox_reset != nullptr ? cbox_reset->GetValue() : false; }
-	void on_variant_checked();
 };
 
 struct PageUpdate: ConfigWizardPage
 {
-	bool version_check;
-	bool preset_update;
+    bool version_check;
+    bool preset_update;
 
-	PageUpdate(ConfigWizard *parent);
+    PageUpdate(ConfigWizard *parent);
 };
 
 struct PageVendors: ConfigWizardPage
 {
-	std::vector<PrinterPicker*> pickers;
+    std::vector<PrinterPicker*> pickers;
 
-	PageVendors(ConfigWizard *parent);
+    PageVendors(ConfigWizard *parent);
 
-	virtual void on_page_set();
-
-	void on_vendor_pick(size_t i);
-	void on_variant_checked();
+    void on_vendor_pick(size_t i);
 };
 
 struct PageFirmware: ConfigWizardPage
 {
-	const ConfigOptionDef &gcode_opt;
-	wxChoice *gcode_picker;
+    const ConfigOptionDef &gcode_opt;
+    wxChoice *gcode_picker;
 
-	PageFirmware(ConfigWizard *parent);
-	virtual void apply_custom_config(DynamicPrintConfig &config);
+    PageFirmware(ConfigWizard *parent);
+    virtual void apply_custom_config(DynamicPrintConfig &config);
 };
 
 struct PageBedShape: ConfigWizardPage
 {
-	BedShapePanel *shape_panel;
+    BedShapePanel *shape_panel;
 
-	PageBedShape(ConfigWizard *parent);
-	virtual void apply_custom_config(DynamicPrintConfig &config);
+    PageBedShape(ConfigWizard *parent);
+    virtual void apply_custom_config(DynamicPrintConfig &config);
 };
 
 struct PageDiameters: ConfigWizardPage
 {
-	wxSpinCtrlDouble *spin_nozzle;
-	wxSpinCtrlDouble *spin_filam;
+    wxSpinCtrlDouble *spin_nozzle;
+    wxSpinCtrlDouble *spin_filam;
 
-	PageDiameters(ConfigWizard *parent);
-	virtual void apply_custom_config(DynamicPrintConfig &config);
+    PageDiameters(ConfigWizard *parent);
+    virtual void apply_custom_config(DynamicPrintConfig &config);
 };
 
 struct PageTemperatures: ConfigWizardPage
 {
-	wxSpinCtrlDouble *spin_extr;
-	wxSpinCtrlDouble *spin_bed;
+    wxSpinCtrlDouble *spin_extr;
+    wxSpinCtrlDouble *spin_bed;
 
-	PageTemperatures(ConfigWizard *parent);
-	virtual void apply_custom_config(DynamicPrintConfig &config);
+    PageTemperatures(ConfigWizard *parent);
+    virtual void apply_custom_config(DynamicPrintConfig &config);
 };
 
 
 class ConfigWizardIndex: public wxPanel
 {
 public:
-	ConfigWizardIndex(wxWindow *parent);
+    ConfigWizardIndex(wxWindow *parent);
 
-	void load_items(ConfigWizardPage *firstpage);
-	void set_active(ConfigWizardPage *page);
+    void add_page(ConfigWizardPage *page);
+    void add_label(wxString label, unsigned indent = 0);
+
+    size_t active_item() const { return item_active; }
+    ConfigWizardPage* active_page() const;
+    bool active_is_last() const { return item_active < items.size() && item_active == last_page; }
+
+    void go_prev();
+    void go_next();
+    void go_to(size_t i);
+    void go_to(ConfigWizardPage *page);
+
+    void clear();
+
+    // XXX
+    // void load_items(ConfigWizardPage *firstpage);
+    // void set_active(ConfigWizardPage *page);
 private:
-	const wxBitmap bg;
-	const wxBitmap bullet_black;
-	const wxBitmap bullet_blue;
-	const wxBitmap bullet_white;
-	int text_height;
+    // enum {
+    //  // Units in em
+    //  MARGIN = 1,
+    //  SPACING = 1,
+    // };
 
-	std::vector<wxString> items;
-	std::vector<wxString>::const_iterator item_active;
+    struct Item
+    {
+        wxString label;
+        unsigned indent;
+        ConfigWizardPage *page;     // nullptr page => label-only item
 
-	void on_paint(wxPaintEvent &evt);
+        // bool operator==(const wxString &label) const { return this->label == label; }
+        bool operator==(ConfigWizardPage *page) const { return this->page == page; }
+    };
+
+    int em;
+    int em_h;
+
+    const wxBitmap bg;
+    const wxBitmap bullet_black;
+    const wxBitmap bullet_blue;
+    const wxBitmap bullet_white;
+
+    std::vector<Item> items;
+    // std::vector<Item>::const_iterator item_active;
+    size_t item_active;
+    ssize_t item_hover;
+    size_t last_page;
+
+    int item_height() const { return std::max(bullet_black.GetSize().GetHeight(), em) + em; }
+
+    void on_paint(wxPaintEvent &evt);
+    void on_mouse_move(wxMouseEvent &evt);
 };
 
+wxDEFINE_EVENT(EVT_INDEX_PAGE, wxCommandEvent);
+
 struct ConfigWizard::priv
 {
-	ConfigWizard *q;
-	ConfigWizard::RunReason run_reason;
-	AppConfig appconfig_vendors;
-	std::unordered_map<std::string, VendorProfile> vendors;
-	std::unordered_map<std::string, std::string> vendors_rsrc;
-	std::unique_ptr<DynamicPrintConfig> custom_config;
+    ConfigWizard *q;
+    ConfigWizard::RunReason run_reason;
+    AppConfig appconfig_vendors;
+    std::unordered_map<std::string, VendorProfile> vendors;
+    std::unordered_map<std::string, std::string> vendors_rsrc;
+    std::unique_ptr<DynamicPrintConfig> custom_config;
 
-	wxScrolledWindow *hscroll = nullptr;
-	wxBoxSizer *hscroll_sizer = nullptr;
-	wxBoxSizer *btnsizer = nullptr;
-	ConfigWizardPage *page_current = nullptr;
-	ConfigWizardIndex *index = nullptr;
-	wxButton *btn_prev = nullptr;
-	wxButton *btn_next = nullptr;
-	wxButton *btn_finish = nullptr;
-	wxButton *btn_cancel = nullptr;
+    wxScrolledWindow *hscroll = nullptr;
+    wxBoxSizer *hscroll_sizer = nullptr;
+    wxBoxSizer *btnsizer = nullptr;
+    ConfigWizardPage *page_current = nullptr;
+    ConfigWizardIndex *index = nullptr;
+    wxButton *btn_prev = nullptr;
+    wxButton *btn_next = nullptr;
+    wxButton *btn_finish = nullptr;
+    wxButton *btn_cancel = nullptr;
 
-	PageWelcome      *page_welcome = nullptr;
-	PageUpdate       *page_update = nullptr;
-	PageVendors      *page_vendors = nullptr;
-	PageFirmware     *page_firmware = nullptr;
-	PageBedShape     *page_bed = nullptr;
-	PageDiameters    *page_diams = nullptr;
-	PageTemperatures *page_temps = nullptr;
+    PageWelcome      *page_welcome = nullptr;
+    PagePrinters     *page_fff = nullptr;
+    PagePrinters     *page_msla = nullptr;
+    PageCustom       *page_custom = nullptr;
+    PageUpdate       *page_update = nullptr;
+    PageVendors      *page_vendors = nullptr;   // XXX: ?
 
-	priv(ConfigWizard *q) : q(q) {}
+    // Custom setup pages
+    PageFirmware     *page_firmware = nullptr;
+    PageBedShape     *page_bed = nullptr;
+    PageDiameters    *page_diams = nullptr;
+    PageTemperatures *page_temps = nullptr;
 
-	void load_vendors();
-	void add_page(ConfigWizardPage *page);
-	void index_refresh();
-	void set_page(ConfigWizardPage *page);
-	void layout_fit();
-	void go_prev() { if (page_current != nullptr) { set_page(page_current->page_prev()); } }
-	void go_next() { if (page_current != nullptr) { set_page(page_current->page_next()); } }
-	void enable_next(bool enable);
+    priv(ConfigWizard *q) : q(q) {}
 
-	void on_other_vendors();
-	void on_custom_setup();
+    void load_pages(bool custom_setup);
 
-	void apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater);
+    bool check_first_variant() const;
+    void load_vendors();
+    void add_page(ConfigWizardPage *page);
+    void enable_next(bool enable);
+
+    void on_custom_setup(bool custom_wanted);
+
+    void apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater);
 };
 
 
diff --git a/src/slic3r/GUI/GUI.cpp b/src/slic3r/GUI/GUI.cpp
index 8e80d64c3..a15f50032 100644
--- a/src/slic3r/GUI/GUI.cpp
+++ b/src/slic3r/GUI/GUI.cpp
@@ -355,21 +355,6 @@ boost::filesystem::path into_path(const wxString &str)
 	return boost::filesystem::path(str.wx_str());
 }
 
-bool get_current_screen_size(wxWindow *window, unsigned &width, unsigned &height)
-{
-	const auto idx = wxDisplay::GetFromWindow(window);
-	if (idx == wxNOT_FOUND) {
-		return false;
-	}
-
-	wxDisplay display(idx);
-	const auto disp_size = display.GetClientArea();
-	width = disp_size.GetWidth();
-	height = disp_size.GetHeight();
-
-	return true;
-}
-
 void about()
 {
     AboutDialog dlg;
diff --git a/src/slic3r/GUI/GUI.hpp b/src/slic3r/GUI/GUI.hpp
index f066c82a8..4074c2afc 100644
--- a/src/slic3r/GUI/GUI.hpp
+++ b/src/slic3r/GUI/GUI.hpp
@@ -71,9 +71,6 @@ wxString	from_path(const boost::filesystem::path &path);
 // boost path from wxString
 boost::filesystem::path	into_path(const wxString &str);
 
-// Returns the dimensions of the screen on which the main frame is displayed
-bool get_current_screen_size(wxWindow *window, unsigned &width, unsigned &height);
-
 // Display an About dialog
 extern void about();
 // Ask the destop to open the datadir using the default file explorer.
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index 774e61024..f5471cdff 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -12,7 +12,6 @@
 
 #include <wx/sizer.h>
 #include <wx/stattext.h>
-#include <wx/notebook.h>
 #include <wx/button.h>
 #include <wx/bmpcbox.h>
 #include <wx/statbox.h>
diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp
index ee182a01a..dedd902ef 100644
--- a/src/slic3r/GUI/Preset.cpp
+++ b/src/slic3r/GUI/Preset.cpp
@@ -11,6 +11,7 @@
     #include <Windows.h>
 #endif /* _MSC_VER */
 
+#include <algorithm>
 #include <fstream>
 #include <stdexcept>
 #include <boost/format.hpp>
@@ -128,11 +129,14 @@ VendorProfile VendorProfile::from_ini(const ptree &tree, const boost::filesystem
             VendorProfile::PrinterModel model;
             model.id = section.first.substr(printer_model_key.size());
             model.name = section.second.get<std::string>("name", model.id);
+
             auto technology_field = section.second.get<std::string>("technology", "FFF");
             if (! ConfigOptionEnum<PrinterTechnology>::from_string(technology_field, model.technology)) {
                 BOOST_LOG_TRIVIAL(error) << boost::format("Vendor bundle: `%1%`: Invalid printer technology field: `%2%`") % id % technology_field;
                 model.technology = ptFFF;
             }
+
+            model.family = section.second.get<std::string>("family", model.id);
 #if 0
 			// Remove SLA printers from the initial alpha.
 			if (model.technology == ptSLA)
@@ -157,6 +161,20 @@ VendorProfile VendorProfile::from_ini(const ptree &tree, const boost::filesystem
     return res;
 }
 
+std::vector<std::string> VendorProfile::families() const
+{
+    std::vector<std::string> res;
+    unsigned num_familiies = 0;
+
+    for (auto &model : models) {
+        if (!model.family.empty() && std::find(res.begin(), res.end(), model.family) == res.end()) {
+            res.push_back(model.family);
+            num_familiies++;
+        }
+    }
+
+    return res;
+}
 
 // Suffix to be added to a modified preset name in the combo box.
 static std::string g_suffix_modified = " (modified)";
diff --git a/src/slic3r/GUI/Preset.hpp b/src/slic3r/GUI/Preset.hpp
index 73a921cf7..074e665c9 100644
--- a/src/slic3r/GUI/Preset.hpp
+++ b/src/slic3r/GUI/Preset.hpp
@@ -54,13 +54,16 @@ public:
         std::string                 id;
         std::string                 name;
         PrinterTechnology           technology;
+        std::string                 family;
         std::vector<PrinterVariant> variants;
+
         PrinterVariant*       variant(const std::string &name) {
             for (auto &v : this->variants)
                 if (v.name == name)
                     return &v;
             return nullptr;
         }
+
         const PrinterVariant* variant(const std::string &name) const { return const_cast<PrinterModel*>(this)->variant(name); }
     };
     std::vector<PrinterModel>          models;
@@ -72,6 +75,7 @@ public:
     static VendorProfile from_ini(const boost::property_tree::ptree &tree, const boost::filesystem::path &path, bool load_all=true);
 
     size_t      num_variants() const { size_t n = 0; for (auto &model : models) n += model.variants.size(); return n; }
+    std::vector<std::string> families() const;
 
     bool        operator< (const VendorProfile &rhs) const { return this->id <  rhs.id; }
     bool        operator==(const VendorProfile &rhs) const { return this->id == rhs.id; }