diff --git a/src/libslic3r/TriangleSelector.cpp b/src/libslic3r/TriangleSelector.cpp
index 9f04374fd..1462b1a76 100644
--- a/src/libslic3r/TriangleSelector.cpp
+++ b/src/libslic3r/TriangleSelector.cpp
@@ -35,14 +35,15 @@ void TriangleSelector::Triangle::set_division(int sides_to_split, int special_si
 
 void TriangleSelector::select_patch(const Vec3f& hit, int facet_start,
                                     const Vec3f& source, const Vec3f& dir,
-                                    float radius, EnforcerBlockerType new_state)
+                                    float radius, CursorType cursor_type,
+                                    EnforcerBlockerType new_state)
 {
     assert(facet_start < m_orig_size_indices);
     assert(is_approx(dir.norm(), 1.f));
 
     // Save current cursor center, squared radius and camera direction,
     // so we don't have to pass it around.
-    m_cursor = {hit, source, dir, radius*radius};
+    m_cursor = {hit, source, dir, radius*radius, cursor_type};
 
     // In case user changed cursor size since last time, update triangle edge limit.
     if (m_old_cursor_radius != radius) {
@@ -61,7 +62,7 @@ void TriangleSelector::select_patch(const Vec3f& hit, int facet_start,
                 // add neighboring facets to list to be proccessed later
                 for (int n=0; n<3; ++n) {
                     int neighbor_idx = m_mesh->stl.neighbors_start[facet].neighbor[n];
-                    if (neighbor_idx >=0 && faces_camera(neighbor_idx))
+                    if (neighbor_idx >=0 && (m_cursor.type == SPHERE || faces_camera(neighbor_idx)))
                         facets_to_check.push_back(neighbor_idx);
                 }
             }
@@ -206,8 +207,12 @@ void TriangleSelector::split_triangle(int facet_idx)
 // Calculate distance of a point from a line.
 bool TriangleSelector::is_point_inside_cursor(const Vec3f& point) const
 {
-    Vec3f diff = m_cursor.center - point;
-    return (diff - diff.dot(m_cursor.dir) * m_cursor.dir).squaredNorm() < m_cursor.radius_sqr;
+     Vec3f diff = m_cursor.center - point;
+
+     if (m_cursor.type == CIRCLE)
+         return (diff - diff.dot(m_cursor.dir) * m_cursor.dir).squaredNorm() < m_cursor.radius_sqr;
+     else // SPHERE
+         return diff.squaredNorm() < m_cursor.radius_sqr;
 }
 
 
diff --git a/src/libslic3r/TriangleSelector.hpp b/src/libslic3r/TriangleSelector.hpp
index be1b20ed4..899539c8e 100644
--- a/src/libslic3r/TriangleSelector.hpp
+++ b/src/libslic3r/TriangleSelector.hpp
@@ -17,6 +17,11 @@ enum class EnforcerBlockerType : int8_t;
 // to recursively subdivide the triangles and make the selection finer.
 class TriangleSelector {
 public:
+    enum CursorType {
+        CIRCLE,
+        SPHERE
+    };
+
     void set_edge_limit(float edge_limit);
 
     // Create new object on a TriangleMesh. The referenced mesh must
@@ -29,6 +34,7 @@ public:
                       const Vec3f& source, // camera position (mesh coords)
                       const Vec3f& dir,    // direction of the ray (mesh coords)
                       float radius,        // radius of the cursor
+                      CursorType type,     // current type of cursor
                       EnforcerBlockerType new_state);   // enforcer or blocker?
 
     // Get facets currently in the given state.
@@ -127,6 +133,7 @@ protected:
         Vec3f source;
         Vec3f dir;
         float radius_sqr;
+        CursorType type;
     };
 
     Cursor m_cursor;
diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp
index c98b736b7..cfc81f5a7 100644
--- a/src/slic3r/GUI/ConfigWizard.cpp
+++ b/src/slic3r/GUI/ConfigWizard.cpp
@@ -566,20 +566,20 @@ PageMaterials::PageMaterials(ConfigWizard *parent, Materials *materials, wxStrin
     , list_type(new StringList(this))
     , list_vendor(new StringList(this))
     , list_profile(new PresetList(this))
-    , compatible_printers(new wxStaticText(this, wxID_ANY, _(L(""))))
 {
     append_spacer(VERTICAL_SPACING);
 
     const int em = parent->em_unit();
     const int list_h = 30*em;
 
-	list_printer->SetWindowStyle(wxLB_EXTENDED);
 
 	list_printer->SetMinSize(wxSize(23*em, list_h));
     list_type->SetMinSize(wxSize(8*em, list_h));
     list_vendor->SetMinSize(wxSize(13*em, list_h));
     list_profile->SetMinSize(wxSize(23*em, list_h));
 
+
+
     grid = new wxFlexGridSizer(4, em/2, em);
     grid->AddGrowableCol(3, 1);
     grid->AddGrowableRow(1, 1);
@@ -601,17 +601,19 @@ PageMaterials::PageMaterials(ConfigWizard *parent, Materials *materials, wxStrin
     btn_sizer->Add(sel_none);
 
 
+    grid->Add(new wxBoxSizer(wxHORIZONTAL));
     grid->Add(new wxBoxSizer(wxHORIZONTAL));
     grid->Add(new wxBoxSizer(wxHORIZONTAL));
     grid->Add(btn_sizer, 0, wxALIGN_RIGHT);
-    
-    auto* notes_sizer = new wxBoxSizer(wxHORIZONTAL);
-    notes_sizer->Add(compatible_printers);
-    grid->Add(notes_sizer);
-    
 
     append(grid, 1, wxEXPAND);
 
+    append_spacer(VERTICAL_SPACING);
+
+    html_window = new wxHtmlWindow(this, wxID_ANY, wxDefaultPosition,
+        wxSize(60 * em, 20 * em), wxHW_SCROLLBAR_AUTO);
+    append(html_window, 0, wxEXPAND);
+
 	list_printer->Bind(wxEVT_LISTBOX, [this](wxCommandEvent& evt) {
 		update_lists(evt.GetInt(), list_type->GetSelection(), list_vendor->GetSelection());
 		});
@@ -627,28 +629,25 @@ PageMaterials::PageMaterials(ConfigWizard *parent, Materials *materials, wxStrin
 
     sel_all->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { select_all(true); });
     sel_none->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { select_all(false); });
-
+    /*
     Bind(wxEVT_PAINT, [this](wxPaintEvent& evt) {on_paint();});
 
     list_profile->Bind(wxEVT_MOTION, [this](wxMouseEvent& evt) { on_mouse_move_on_profiles(evt); });
     list_profile->Bind(wxEVT_ENTER_WINDOW, [this](wxMouseEvent& evt) { on_mouse_enter_profiles(evt); });
     list_profile->Bind(wxEVT_LEAVE_WINDOW, [this](wxMouseEvent& evt) { on_mouse_leave_profiles(evt); });
-    
+    */
     reload_presets();
+    set_compatible_printers_html_window(std::vector<std::string>(), false);
 }
 void PageMaterials::on_paint()
 {
-    if (first_paint) {
-        first_paint = false;
-        prepare_compatible_printers_label();
-    }
 }
 void PageMaterials::on_mouse_move_on_profiles(wxMouseEvent& evt)
 {
     const wxClientDC dc(list_profile);
     const wxPoint pos = evt.GetLogicalPosition(dc);
     int item = list_profile->HitTest(pos);
-    BOOST_LOG_TRIVIAL(error) << "hit test: " << item;
+    //BOOST_LOG_TRIVIAL(debug) << "hit test: " << item;
     on_material_hovered(item);
 }
 void PageMaterials::on_mouse_enter_profiles(wxMouseEvent& evt)
@@ -666,10 +665,11 @@ void PageMaterials::reload_presets()
 	for (const Preset* printer : materials->printers) {
 		list_printer->append(printer->name, &printer->name);
 	}
-
+    sort_list_data(list_printer, true, false);
     if (list_printer->GetCount() > 0) {
         list_printer->SetSelection(0);
-		sel_printer_prev = wxNOT_FOUND;
+		sel_printer_count_prev = wxNOT_FOUND;
+        sel_printer_item_prev = wxNOT_FOUND;
         sel_type_prev = wxNOT_FOUND;
         sel_vendor_prev = wxNOT_FOUND;
         update_lists(0, 0, 0);
@@ -678,34 +678,105 @@ void PageMaterials::reload_presets()
     presets_loaded = true;
 }
 
-void PageMaterials::prepare_compatible_printers_label()
+void PageMaterials::set_compatible_printers_html_window(const std::vector<std::string>& printer_names, bool all_printers)
 {
-    assert(grid->GetColWidths().size() == 4);
-    compatible_printers_width = grid->GetColWidths()[3];
-    empty_printers_label = "Compatible printers:";
-    for (const Preset* printer : materials->printers) {
-        empty_printers_label += "\n";
+    //Slic3r::GUI::wxGetApp().dark_mode()
+    const auto bgr_clr = 
+#if defined(__APPLE__)
+        wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
+#else
+        wxSystemSettings::GetColour(wxSYS_COLOUR_MENU);
+#endif
+    const auto bgr_clr_str = wxString::Format(wxT("#%02X%02X%02X"), bgr_clr.Red(), bgr_clr.Green(), bgr_clr.Blue());
+    const auto text_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
+    const auto text_clr_str = wxString::Format(wxT("#%02X%02X%02X"), text_clr.Red(), text_clr.Green(), text_clr.Blue());
+    wxString first_line = L"Profiles marked with * are not compatible with all installed printers.";
+    wxString text;
+    if (all_printers) {
+        text = wxString::Format(
+            "<html>"
+            "<style>"
+            "table{border-spacing: 1px;}"
+            "</style>"
+            "<body bgcolor= %s>"
+            "<font color=%s>"
+            "<font size=\"3\">"
+            "%s<br /><br /> All installed printers are compatible with selected profile."
+            "</font>"
+            "</font>"
+            "</body>"
+            "</html>"
+            , bgr_clr_str
+            , text_clr_str
+            , first_line
+            );
+    } else {
+        wxString second_line = L"Compatible printers:";
+        text = wxString::Format(
+            "<html>"
+            "<style>"
+            "table{border-spacing: 1px;}"
+            "</style>"
+            "<body bgcolor= %s>"
+            "<font color=%s>"
+            "<font size=\"3\">"
+            "%s<br /><br />%s"
+            "<table>"
+            "<tr>"
+            , bgr_clr_str
+            , text_clr_str
+            , first_line
+            , second_line);
+        for (int i = 0; i < printer_names.size(); ++i)
+        {
+            text += wxString::Format("<td>%s</td>", boost::nowide::widen(printer_names[i]));
+            if (i % 3 == 2) {
+                text += wxString::Format(
+                    "</tr>"
+                    "<tr>");
+            }
+        }
+        text += wxString::Format(
+            "</tr>"
+            "</table>"
+            "</font>"
+            "</font>"
+            "</body>"
+            "</html>"
+        );
     }
-    clear_compatible_printers_label();
+   
+    wxFont font = get_default_font_for_dpi(this, get_dpi_for_window(this));
+    const int fs = font.GetPointSize();
+    int size[] = { fs,fs,fs,fs,fs,fs,fs };
+    html_window->SetFonts(font.GetFaceName(), font.GetFaceName(), size);
+    html_window->SetPage(text);
 }
 
 void PageMaterials::clear_compatible_printers_label()
 {
-    compatible_printers->SetLabel(boost::nowide::widen(empty_printers_label));
-    compatible_printers->Wrap(compatible_printers_width);
-    Layout();
+    set_compatible_printers_html_window(std::vector<std::string>(), false);
 }
 
 void PageMaterials::on_material_hovered(int sel_material)
 {
-    if ( sel_material == last_hovered_item)
+
+}
+
+void PageMaterials::on_material_highlighted(int sel_material)
+{
+    if (sel_material == last_hovered_item)
         return;
     if (sel_material == -1) {
         clear_compatible_printers_label();
         return;
     }
     last_hovered_item = sel_material;
-    std::string compatible_printers_label = "compatible printers:\n";
+    std::string compatible_printers_label = "Compatible printers:\n";
+    std::vector<std::string> tabs;
+    tabs.push_back(std::string());
+    tabs.push_back(std::string());
+    tabs.push_back(std::string());
     //selected material string
     std::string material_name = list_profile->get_data(sel_material);
     // get material preset
@@ -716,70 +787,16 @@ void PageMaterials::on_material_hovered(int sel_material)
         return;
     }
     //find matching printers
-    bool first = true;
+    std::vector<std::string> names;
     for (const Preset* printer : materials->printers) {
-        bool compatible = false;
         for (const Preset* material : matching_materials) {
             if (is_compatible_with_printer(PresetWithVendorProfile(*material, material->vendor), PresetWithVendorProfile(*printer, printer->vendor))) {
-                if (first)
-                    first = false;
-                else
-                    compatible_printers_label += "\n";//", ";
-                compatible_printers_label += printer->name;
-                compatible = true;
+                names.push_back(printer->name);
                 break;
             }
         }
     }
-    this->compatible_printers->SetLabel(boost::nowide::widen(compatible_printers_label));
-    this->compatible_printers->Wrap(compatible_printers_width);
-}
-
-void PageMaterials::on_material_highlighted(int sel_material)
-{
-	wxWindowUpdateLocker freeze_guard(this);
-	(void)freeze_guard;
-
-    //std::string compatible_printers_label = "compatible printers:\n";
-    //std::string empty_suplement = std::string();
-	//unselect all printers
-	list_printer->SetSelection(wxNOT_FOUND);
-	//selected material string
-	std::string material_name = list_profile->get_data(sel_material);
-    // get material preset
-    const std::vector<const Preset*> matching_materials = materials->get_presets_by_alias(material_name);
-    if (matching_materials.empty())
-        return;
-    //find matching printers
-    //bool first = true;
-    for (const Preset* printer : materials->printers) {
-        bool compatible = false;
-        for (const Preset* material : matching_materials) {
-            if (is_compatible_with_printer(PresetWithVendorProfile(*material, material->vendor), PresetWithVendorProfile(*printer, printer->vendor))) {
-                //select printer
-                int index = list_printer->find(printer->name);
-                list_printer->SetSelection(index);
-                /*if (first) 
-                    first = false;
-                else
-                    compatible_printers_label += "\n";//", ";
-                compatible_printers_label += printer->name;
-                compatible = true;
-                break;*/
-            }
-        } 
-        //if(!compatible)
-        //    empty_suplement += std::string(printer->name.length() + 2, ' ');
-    }
-    // fill rest of label with blanks so it maintains legth
-    //compatible_printers_label += empty_suplement;
-
-    update_lists(0,0,0);
-    list_profile->SetSelection(list_profile->find(material_name));
-
-    //this->compatible_printers->SetLabel(boost::nowide::widen(compatible_printers_label));
-    //this->compatible_printers->Wrap(compatible_printers_width);
-    //Refresh();
+    set_compatible_printers_html_window(names, names.size() == materials->printers.size());
 }
 
 void PageMaterials::update_lists(int sel_printer, int sel_type, int sel_vendor)
@@ -790,7 +807,7 @@ void PageMaterials::update_lists(int sel_printer, int sel_type, int sel_vendor)
 	wxArrayInt sel_printers;
 	int sel_printers_count = list_printer->GetSelections(sel_printers);
 
-	if (sel_printers_count != sel_printer_prev) {
+	if (sel_printers_count != sel_printer_count_prev || (sel_printers_count == 1 && sel_printer_item_prev != sel_printer && sel_printer != -1)) {
         // Refresh type list
 		list_type->Clear();
 		list_type->append(_(L("(All)")), &EMPTY);
@@ -827,6 +844,7 @@ void PageMaterials::update_lists(int sel_printer, int sel_type, int sel_vendor)
                 //clear selection except "ALL"
                 list_printer->SetSelection(wxNOT_FOUND);
                 list_printer->SetSelection(0);
+                sel_printers_count = list_printer->GetSelections(sel_printers);
 
 				materials->filter_presets(nullptr, EMPTY, EMPTY, [this](const Preset* p) {
 					const std::string& type = this->materials->get_type(p);
@@ -835,10 +853,11 @@ void PageMaterials::update_lists(int sel_printer, int sel_type, int sel_vendor)
 					}
 					});
 			}
-
+            sort_list_data(list_type, true, true);
 		}
 
-		sel_printer_prev = sel_printers_count;
+		sel_printer_count_prev = sel_printers_count;
+        sel_printer_item_prev = sel_printer;
 		sel_type = 0;
 		sel_type_prev = wxNOT_FOUND;
 		list_type->SetSelection(sel_type);
@@ -872,6 +891,7 @@ void PageMaterials::update_lists(int sel_printer, int sel_type, int sel_vendor)
 					}
 					});
 			}
+            sort_list_data(list_vendor, true, false);
 		}
 
 		sel_type_prev = sel_type;
@@ -905,7 +925,6 @@ void PageMaterials::update_lists(int sel_printer, int sel_type, int sel_vendor)
 					//size_t printer_counter = materials->get_printer_counter(p);
 					int cur_i = list_profile->find(p->alias);
 					if (cur_i == wxNOT_FOUND)
-						//cur_i = list_profile->append(p->alias + " " + std::to_string(printer_counter)/*+ (omnipresent ? "" : " ONLY SOME PRINTERS")*/, &p->alias);
 						cur_i = list_profile->append(p->alias + (materials->get_omnipresent(p) ? "" : " *"), &p->alias);
 					else
 						was_checked = list_profile->IsChecked(cur_i);
@@ -925,12 +944,103 @@ void PageMaterials::update_lists(int sel_printer, int sel_type, int sel_vendor)
 						wizard_p()->appconfig_new.set(section, p->name, "1");
 					});
 			}
+            sort_list_data(list_profile);
 		}
 
 		sel_vendor_prev = sel_vendor;
 	}
 }
 
+void PageMaterials::sort_list_data(StringList* list, bool add_All_item, bool material_type_ordering)
+{
+// get data from list
+// sort data
+// first should be <all>
+// then prusa profiles
+// then the rest
+// in alphabetical order
+    
+    std::vector<std::reference_wrapper<const std::string>> prusa_profiles;
+    std::vector<std::reference_wrapper<const std::string>> other_profiles;
+    for (int i = 0 ; i < list->size(); ++i) {
+        const std::string& data = list->get_data(i);
+        if (data == EMPTY) // do not sort <all> item
+            continue;
+        if (!material_type_ordering && data.find("Prusa") != std::string::npos)
+            prusa_profiles.push_back(data);
+        else 
+            other_profiles.push_back(data);
+    }
+    if(material_type_ordering) {
+        
+        const ConfigOptionDef* def = print_config_def.get("filament_type");
+        std::vector<std::string>enum_values = def->enum_values;
+        int end_of_sorted = 0;
+        for (size_t vals = 0; vals < enum_values.size(); vals++) {
+            for (size_t profs = end_of_sorted; profs < other_profiles.size(); profs++)
+            {
+                // find instead compare because PET vs PETG
+                if (other_profiles[profs].get().find(enum_values[vals]) != std::string::npos) {
+                    //swap
+                    if(profs != end_of_sorted) {
+                        std::reference_wrapper<const std::string> aux = other_profiles[end_of_sorted];
+                        other_profiles[end_of_sorted] = other_profiles[profs];
+                        other_profiles[profs] = aux;
+                    }
+                    end_of_sorted++;
+                    break;
+                }
+            }
+        }
+    } else {
+        std::sort(prusa_profiles.begin(), prusa_profiles.end(), [](std::reference_wrapper<const std::string> a, std::reference_wrapper<const std::string> b) {
+            return a.get() < b.get();
+            });
+        std::sort(other_profiles.begin(), other_profiles.end(), [](std::reference_wrapper<const std::string> a, std::reference_wrapper<const std::string> b) {
+            return a.get() < b.get();
+            });
+    }
+    
+    list->Clear();
+    if (add_All_item)
+        list->append(_(L("(All)")), &EMPTY);
+    for (const auto& item : prusa_profiles)
+        list->append(item, &const_cast<std::string&>(item.get()));
+    for (const auto& item : other_profiles)
+        list->append(item, &const_cast<std::string&>(item.get()));
+}     
+
+void PageMaterials::sort_list_data(PresetList* list)
+{
+    // sort data
+    // then prusa profiles
+    // then the rest
+    // in alphabetical order
+    std::vector<std::reference_wrapper<const std::string>> prusa_profiles;
+    std::vector<std::reference_wrapper<const std::string>> other_profiles;
+    for (int i = 0; i < list->size(); ++i) {
+        const std::string& data = list->get_data(i);
+        if (data == EMPTY) // do not sort <all> item
+            continue;
+        if (data.find("Prusa") != std::string::npos)
+            prusa_profiles.push_back(data);
+        else
+            other_profiles.push_back(data);
+    }
+    std::sort(prusa_profiles.begin(), prusa_profiles.end(), [](std::reference_wrapper<const std::string> a, std::reference_wrapper<const std::string> b) {
+        return a.get() < b.get();
+        });
+    std::sort(other_profiles.begin(), other_profiles.end(), [](std::reference_wrapper<const std::string> a, std::reference_wrapper<const std::string> b) {
+        return a.get() < b.get();
+        });
+    list->Clear();
+    for (const auto& item : prusa_profiles)
+        list->append(item, &const_cast<std::string&>(item.get()));
+    for (const auto& item : other_profiles)
+        list->append(item, &const_cast<std::string&>(item.get()));
+
+}
+
 void PageMaterials::select_material(int i)
 {
     const bool checked = list_profile->IsChecked(i);
@@ -959,7 +1069,8 @@ void PageMaterials::clear()
     list_type->Clear();
     list_vendor->Clear();
     list_profile->Clear();
-	sel_printer_prev = wxNOT_FOUND;
+	sel_printer_count_prev = wxNOT_FOUND;
+    sel_printer_item_prev = wxNOT_FOUND;
     sel_type_prev = wxNOT_FOUND;
     sel_vendor_prev = wxNOT_FOUND;
     presets_loaded = false;
@@ -1546,7 +1657,7 @@ const std::string Materials::UNKNOWN = "(Unknown)";
 
 void Materials::push(const Preset *preset)
 {
-    presets.emplace_back(preset, 0);
+    presets.emplace_back(preset);
     types.insert(technology & T_FFF
         ? Materials::get_filament_type(preset)
         : Materials::get_material_type(preset));
@@ -1562,6 +1673,7 @@ void Materials::clear()
     presets.clear();
     types.clear();
 	printers.clear();
+    compatibility_counter.clear();
 }
 
 const std::string& Materials::appconfig_section() const
@@ -1855,13 +1967,45 @@ void ConfigWizard::priv::update_materials(Technology technology)
 							if (!filament.alias.empty())
 								aliases_fff[filament.alias].insert(filament.name); 
 						} 
-						filaments.add_printer_counter(&filament);
 						filaments.add_printer(&printer);
                     }
 				}
 				
             }
         }
+        // count compatible printers
+        for (const auto& preset : filaments.presets) {
+
+            const auto filter = [preset](const std::pair<std::string, size_t> element) {
+                return preset->alias == element.first;
+            };
+            if (std::find_if(filaments.compatibility_counter.begin(), filaments.compatibility_counter.end(), filter) != filaments.compatibility_counter.end()) {
+                continue;
+            }
+            std::vector<size_t> idx_with_same_alias;
+            for (size_t i = 0; i < filaments.presets.size(); ++i) {
+                if (preset->alias == filaments.presets[i]->alias)
+                    idx_with_same_alias.push_back(i);
+            }
+            size_t counter = 0;
+            for (const auto& printer : filaments.printers) {
+                if (!(*printer).is_visible || (*printer).printer_technology() != ptFFF)
+                    continue;
+                bool compatible = false;
+                // Test otrher materials with same alias
+                for (size_t i = 0; i < idx_with_same_alias.size() && !compatible; ++i) {
+                    const Preset& prst = *(filaments.presets[idx_with_same_alias[i]]);
+                    const Preset& prntr = *printer;
+                    if (is_compatible_with_printer(PresetWithVendorProfile(prst, prst.vendor), PresetWithVendorProfile(prntr, prntr.vendor))) {
+                        compatible = true;
+                        break;
+                    }
+                }
+                if (compatible)
+                    counter++;
+            }
+            filaments.compatibility_counter.emplace_back(preset->alias, counter);
+        }
     }
 
     if (any_sla_selected && (technology & T_SLA)) {
@@ -1887,12 +2031,44 @@ void ConfigWizard::priv::update_materials(Technology technology)
                             if (!material.alias.empty())
                                 aliases_sla[material.alias].insert(material.name);
                         }
-                        sla_materials.add_printer_counter(&material);
                         sla_materials.add_printer(&printer);
                     }
                 }
             }
         }
+        // count compatible printers        
+        for (const auto& preset : sla_materials.presets) {
+            
+            const auto filter = [preset](const std::pair<std::string, size_t> element) {
+                return preset->alias == element.first;
+            };
+            if (std::find_if(sla_materials.compatibility_counter.begin(), sla_materials.compatibility_counter.end(), filter) != sla_materials.compatibility_counter.end()) {
+                continue;
+            }
+            std::vector<size_t> idx_with_same_alias;
+            for (size_t i = 0; i < sla_materials.presets.size(); ++i) {
+                if(preset->alias == sla_materials.presets[i]->alias)
+                    idx_with_same_alias.push_back(i);
+            }
+            size_t counter = 0;
+            for (const auto& printer : sla_materials.printers) {
+                if (!(*printer).is_visible || (*printer).printer_technology() != ptSLA)
+                    continue;
+                bool compatible = false;
+                // Test otrher materials with same alias
+                for (size_t i = 0; i < idx_with_same_alias.size() && !compatible; ++i) {
+                    const Preset& prst = *(sla_materials.presets[idx_with_same_alias[i]]);
+                    const Preset& prntr = *printer;
+                    if (is_compatible_with_printer(PresetWithVendorProfile(prst, prst.vendor), PresetWithVendorProfile(prntr, prntr.vendor))) {
+                        compatible = true;
+                        break;
+                    }
+                }
+                if (compatible)
+                    counter++;
+            }
+            sla_materials.compatibility_counter.emplace_back(preset->alias, counter);
+        }
     }
 }
 
diff --git a/src/slic3r/GUI/ConfigWizard_private.hpp b/src/slic3r/GUI/ConfigWizard_private.hpp
index 260eeb22c..850f8fd46 100644
--- a/src/slic3r/GUI/ConfigWizard_private.hpp
+++ b/src/slic3r/GUI/ConfigWizard_private.hpp
@@ -19,6 +19,7 @@
 #include <wx/listbox.h>
 #include <wx/checklst.h>
 #include <wx/radiobut.h>
+#include <wx/html/htmlwin.h>
 
 #include "libslic3r/PrintConfig.hpp"
 #include "libslic3r/PresetBundle.hpp"
@@ -86,9 +87,9 @@ struct Materials
 {
     Technology technology;
     // use vector for the presets to purpose of save of presets sorting in the bundle
-	// bool is true if material is present in all printers (omnipresent)
-	// size_t is counter of printers compatible with material
-    std::vector<std::pair<const Preset*, size_t>> presets;
+    std::vector<const Preset*> presets;
+    // String is alias of material, size_t number of compatible counters 
+    std::vector<std::pair<std::string, size_t>> compatibility_counter;
     std::set<std::string> types;
 	std::set<const Preset*> printers;
 
@@ -100,7 +101,7 @@ struct Materials
     bool containts(const Preset *preset) const {
         //return std::find(presets.begin(), presets.end(), preset) != presets.end(); 
 		return std::find_if(presets.begin(), presets.end(),
-			[preset](const std::pair<const Preset*, bool>& element) { return element.first == preset; }) != presets.end();
+			[preset](const Preset* element) { return element == preset; }) != presets.end();
 
     }
 	
@@ -111,42 +112,35 @@ struct Materials
     const std::vector<const Preset*> get_presets_by_alias(const std::string name) {
         std::vector<const Preset*> ret_vec;
         for (auto it = presets.begin(); it != presets.end(); ++it) {
-            if ((*it).first->alias == name)
-                ret_vec.push_back((*it).first);
+            if ((*it)->alias == name)
+                ret_vec.push_back((*it));
         }
         return ret_vec;
     }
 
-	void add_printer_counter(const Preset* preset) {
-		for (auto it = presets.begin(); it != presets.end(); ++it) {
-			if ((*it).first->alias == preset->alias)
-				(*it).second += 1;
-		}
-	}
+	
 
 	size_t get_printer_counter(const Preset* preset) {
-		size_t highest = 0;
-		for (auto it : presets) {
-			if (it.first->alias == preset->alias && it.second > highest)
-				highest = it.second;
-		}
-		return highest;
+		for (auto it : compatibility_counter) {
+			if (it.first == preset->alias)
+                return it.second;
+        }
+		return 0;
 	}
 
     const std::string& appconfig_section() const;
     const std::string& get_type(const Preset *preset) const;
     const std::string& get_vendor(const Preset *preset) const;
 	
-
 	template<class F> void filter_presets(const Preset* printer, const std::string& type, const std::string& vendor, F cb) {
 		for (auto preset : presets) {
-			const Preset& prst = *(preset.first);
+			const Preset& prst = *(preset);
 			const Preset& prntr = *printer;
 		      if ((printer == nullptr || is_compatible_with_printer(PresetWithVendorProfile(prst, prst.vendor), PresetWithVendorProfile(prntr, prntr.vendor))) &&
-			    (type.empty() || get_type(preset.first) == type) &&
-				(vendor.empty() || get_vendor(preset.first) == vendor)) {
+			    (type.empty() || get_type(preset) == type) &&
+				(vendor.empty() || get_vendor(preset) == vendor)) {
 
-				cb(preset.first);
+				cb(preset);
 			}
 		}
 	}
@@ -325,11 +319,12 @@ struct PageMaterials: ConfigWizardPage
     Materials *materials;
     StringList *list_printer, *list_type, *list_vendor;
     PresetList *list_profile;
-    int sel_printer_prev, sel_type_prev, sel_vendor_prev;
+    int sel_printer_count_prev, sel_printer_item_prev, sel_type_prev, sel_vendor_prev;
     bool presets_loaded;
 
     wxFlexGridSizer *grid;
-    wxStaticText *compatible_printers;
+    wxHtmlWindow* html_window;
+
     int compatible_printers_width = { 100 };
     std::string empty_printers_label;
     bool first_paint = { false };
@@ -345,9 +340,12 @@ struct PageMaterials: ConfigWizardPage
     void select_material(int i);
     void select_all(bool select);
     void clear();
-    void prepare_compatible_printers_label();
+    void set_compatible_printers_html_window(const std::vector<std::string>& printer_names, bool all_printers = false);
     void clear_compatible_printers_label();
 
+    void sort_list_data(StringList* list, bool add_All_item, bool material_type_ordering);
+    void sort_list_data(PresetList* list);
+
     void on_paint();
     void on_mouse_move_on_profiles(wxMouseEvent& evt);
     void on_mouse_enter_profiles(wxMouseEvent& evt);
diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp
index 0e3378e9a..a6914f768 100644
--- a/src/slic3r/GUI/GCodeViewer.cpp
+++ b/src/slic3r/GUI/GCodeViewer.cpp
@@ -22,6 +22,8 @@
 #include <GL/glew.h>
 #include <boost/log/trivial.hpp>
 #include <boost/nowide/cstdio.hpp>
+#include <wx/progdlg.h>
+#include <wx/numformatter.h>
 
 #include <array>
 #include <algorithm>
@@ -861,6 +863,8 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
     if (m_moves_count == 0)
         return;
 
+    unsigned int progress_count = 0;
+    static const unsigned int progress_threshold = 1000;
     wxProgressDialog progress_dialog(_L("Generating toolpaths"), "...",
         100, wxGetApp().plater(), wxPD_AUTO_HIDE | wxPD_APP_MODAL);
 
@@ -1243,10 +1247,13 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
         if (i == 0)
             continue;
 
-        progress_dialog.Update(int(100.0f * float(i) / (2.0f * float(m_moves_count))),
-            _L("Generating vertex buffer") + " (" + wxNumberFormatter::ToString((long)i, wxNumberFormatter::Style_None) + "/" +
-            wxNumberFormatter::ToString((long)m_moves_count, wxNumberFormatter::Style_None) + ")");
-        progress_dialog.Fit();
+        ++progress_count;
+        if (progress_count % progress_threshold == 0) {
+            progress_dialog.Update(int(100.0f * float(i) / (2.0f * float(m_moves_count))),
+                _L("Generating vertex buffer") + ": " + wxNumberFormatter::ToString(100.0 * double(i) / double(m_moves_count), 0, wxNumberFormatter::Style_None) + "%");
+            progress_dialog.Fit();
+            progress_count = 0;
+        }
 
         const GCodeProcessor::MoveVertex& prev = gcode_result.moves[i - 1];
         const GCodeProcessor::MoveVertex& curr = gcode_result.moves[i];
@@ -1314,10 +1321,13 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
         if (i == 0)
             continue;
 
-        progress_dialog.Update(int(100.0f * float(m_moves_count + i) / (2.0f * float(m_moves_count))),
-            _L("Generating index buffers") + " (" + wxNumberFormatter::ToString((long)i, wxNumberFormatter::Style_None) + "/" +
-            wxNumberFormatter::ToString((long)m_moves_count, wxNumberFormatter::Style_None) + ")");
-        progress_dialog.Fit();
+        ++progress_count;
+        if (progress_count % progress_threshold == 0) {
+            progress_dialog.Update(int(100.0f * float(m_moves_count + i) / (2.0f * float(m_moves_count))),
+                _L("Generating index buffers") + ": " + wxNumberFormatter::ToString(100.0 * double(i) / double(m_moves_count), 0, wxNumberFormatter::Style_None) + "%");
+            progress_dialog.Fit();
+            progress_count = 0;
+        }
 
         const GCodeProcessor::MoveVertex& prev = gcode_result.moves[i - 1];
         const GCodeProcessor::MoveVertex& curr = gcode_result.moves[i];
diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index a3b4be9a7..c138b937c 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -628,8 +628,8 @@ void GLCanvas3D::WarningTexture::activate(WarningTexture::Warning warning, bool
     bool error = false;
     switch (warning) {
     case ObjectOutside: text = L("An object outside the print area was detected."); break;
-    case ToolpathOutside: text = L("A toolpath outside the print area was detected."); break;
-    case SlaSupportsOutside: text = L("SLA supports outside the print area were detected."); break;
+    case ToolpathOutside: text = L("A toolpath outside the print area was detected."); error = true; break;
+    case SlaSupportsOutside: text = L("SLA supports outside the print area were detected."); error = true; break;
     case SomethingNotShown: text = L("Some objects are not visible."); break;
     case ObjectClashed:
         text = L( "An object outside the print area was detected.\n"
@@ -644,7 +644,7 @@ void GLCanvas3D::WarningTexture::activate(WarningTexture::Warning warning, bool
             wxGetApp().plater()->get_notification_manager()->push_plater_warning_notification(text, *(wxGetApp().plater()->get_current_canvas3D()));
     } else {
         if (error)
-            wxGetApp().plater()->get_notification_manager()->close_plater_error_notification();
+            wxGetApp().plater()->get_notification_manager()->close_plater_error_notification(text);
         else
             wxGetApp().plater()->get_notification_manager()->close_plater_warning_notification(text);
     }
diff --git a/src/slic3r/GUI/GUI_Utils.hpp b/src/slic3r/GUI/GUI_Utils.hpp
index 355283349..249ae74f6 100644
--- a/src/slic3r/GUI/GUI_Utils.hpp
+++ b/src/slic3r/GUI/GUI_Utils.hpp
@@ -118,7 +118,7 @@ public:
         this->Bind(EVT_DPI_CHANGED_SLICER, [this](const DpiChangedEvent& evt) {
             m_scale_factor = (float)evt.dpi / (float)DPI_DEFAULT;
 
-            m_new_font_point_size = get_default_font_for_dpi(evt.dpi).GetPointSize();
+            m_new_font_point_size = get_default_font_for_dpi(this, evt.dpi).GetPointSize();
 
             if (!m_can_rescale)
                 return;
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp
index 6b3456b60..04288c13f 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp
@@ -42,6 +42,7 @@ bool GLGizmoFdmSupports::on_init()
     m_desc["clipping_of_view"] = _L("Clipping of view") + ": ";
     m_desc["reset_direction"]  = _L("Reset direction");
     m_desc["cursor_size"]      = _L("Cursor size") + ": ";
+    m_desc["cursor_type"]      = _L("Cursor type") + ": ";
     m_desc["enforce_caption"]  = _L("Left mouse button") + ": ";
     m_desc["enforce"]          = _L("Enforce supports");
     m_desc["block_caption"]    = _L("Right mouse button") + " ";
@@ -66,7 +67,7 @@ void GLGizmoFdmSupports::on_render() const
         render_triangles(selection);
 
     m_c->object_clipper()->render_cut();
-    render_cursor_circle();
+    render_cursor();
 
     glsafe(::glDisable(GL_BLEND));
 }
@@ -78,16 +79,26 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
     if (! m_c->selection_info()->model_object())
         return;
 
-    const float approx_height = m_imgui->scaled(18.0f);
+    const float approx_height = m_imgui->scaled(14.0f);
     y = std::min(y, bottom_limit - approx_height);
     m_imgui->set_next_window_pos(x, y, ImGuiCond_Always);
 
     if (! m_setting_angle) {
         m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse);
 
+        std::vector<std::string> cursor_types;
+        cursor_types.push_back(_L("Circle").ToUTF8().data());
+        cursor_types.push_back(_L("Sphere").ToUTF8().data());
+
         // First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that:
-        const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x, m_imgui->calc_text_size(m_desc.at("reset_direction")).x) + m_imgui->scaled(1.5f);
+        const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x,
+                                                    m_imgui->calc_text_size(m_desc.at("reset_direction")).x)
+                                              + m_imgui->scaled(1.5f);
         const float cursor_slider_left = m_imgui->calc_text_size(m_desc.at("cursor_size")).x + m_imgui->scaled(1.f);
+        const float cursor_type_combo_left  = m_imgui->calc_text_size(m_desc.at("cursor_type")).x + m_imgui->scaled(1.f);
+        const float cursor_type_combo_width = std::max(m_imgui->calc_text_size(wxString::FromUTF8(cursor_types[0])).x,
+                                                       m_imgui->calc_text_size(wxString::FromUTF8(cursor_types[1])).x)
+                                                 + m_imgui->scaled(2.5f);
         const float button_width = m_imgui->calc_text_size(m_desc.at("remove_all")).x + m_imgui->scaled(1.f);
         const float minimal_slider_width = m_imgui->scaled(4.f);
 
@@ -103,6 +114,7 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
         float window_width = minimal_slider_width + std::max(cursor_slider_left, clipping_slider_left);
         window_width = std::max(window_width, total_text_max);
         window_width = std::max(window_width, button_width);
+        window_width = std::max(window_width, cursor_type_combo_left + cursor_type_combo_width);
 
         auto draw_text_with_caption = [this, &caption_max](const wxString& caption, const wxString& text) {
             m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, caption);
@@ -139,8 +151,8 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
         const float max_tooltip_width = ImGui::GetFontSize() * 20.0f;
 
         m_imgui->text(m_desc.at("cursor_size"));
-        ImGui::SameLine(clipping_slider_left);
-        ImGui::PushItemWidth(window_width - clipping_slider_left);
+        ImGui::SameLine(cursor_slider_left);
+        ImGui::PushItemWidth(window_width - cursor_slider_left);
         ImGui::SliderFloat(" ", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f");
         if (ImGui::IsItemHovered()) {
             ImGui::BeginTooltip();
@@ -150,6 +162,23 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
             ImGui::EndTooltip();
         }
 
+
+        m_imgui->text(m_desc.at("cursor_type"));
+        ImGui::SameLine(window_width - cursor_type_combo_width - m_imgui->scaled(0.5f));
+        ImGui::PushItemWidth(cursor_type_combo_width);
+        int selection = int(m_cursor_type);
+        m_imgui->combo("", cursor_types, selection);
+        m_cursor_type = TriangleSelector::CursorType(selection);
+        if (ImGui::IsItemHovered()) {
+            ImGui::BeginTooltip();
+            ImGui::PushTextWrapPos(max_tooltip_width);
+            ImGui::TextUnformatted(_L("Sphere paints all facets inside, regardless of their orientation.\n\n"
+                                      "Circle ignores facets facing away from the camera.").ToUTF8().data());
+            ImGui::PopTextWrapPos();
+            ImGui::EndTooltip();
+        }
+
+
         ImGui::Separator();
         if (m_c->object_clipper()->get_position() == 0.f)
             m_imgui->text(m_desc.at("clipping_of_view"));
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp
index 939d3c48a..c421e63de 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp
@@ -21,6 +21,14 @@ GLGizmoPainterBase::GLGizmoPainterBase(GLCanvas3D& parent, const std::string& ic
     : GLGizmoBase(parent, icon_filename, sprite_id)
 {
     m_clipping_plane.reset(new ClippingPlane());
+    // Make sphere and save it into a vertex buffer.
+    const TriangleMesh sphere_mesh = make_sphere(1., (2*M_PI)/24.);
+    for (size_t i=0; i<sphere_mesh.its.vertices.size(); ++i)
+        m_vbo_sphere.push_geometry(sphere_mesh.its.vertices[i].cast<double>(),
+                                    sphere_mesh.stl.facet_start[i].normal.cast<double>());
+    for (const stl_triangle_vertex_indices& indices : sphere_mesh.its.indices)
+        m_vbo_sphere.push_triangle(indices(0), indices(1), indices(2));
+    m_vbo_sphere.finalize_geometry(true);
 }
 
 
@@ -117,6 +125,16 @@ void GLGizmoPainterBase::render_triangles(const Selection& selection) const
 }
 
 
+void GLGizmoPainterBase::render_cursor() const
+{
+    if (m_cursor_type == TriangleSelector::SPHERE)
+        render_cursor_sphere();
+    else
+        render_cursor_circle();
+}
+
+
+
 void GLGizmoPainterBase::render_cursor_circle() const
 {
     const Camera& camera = wxGetApp().plater()->get_camera();
@@ -162,17 +180,61 @@ void GLGizmoPainterBase::render_cursor_circle() const
 }
 
 
+void GLGizmoPainterBase::render_cursor_sphere() const
+{
+    Vec2d mouse_position(m_parent.get_local_mouse_position()(0), m_parent.get_local_mouse_position()(1));
 
-bool GLGizmoPainterBase::is_mesh_point_clipped(const Vec3d& point) const
+    const ModelObject* mo = m_c->selection_info()->model_object();
+    const Selection& selection = m_parent.get_selection();
+    const ModelInstance* mi = mo->instances[selection.get_instance_idx()];
+    const Camera& camera = wxGetApp().plater()->get_camera();
+
+    // Precalculate transformations of individual meshes.
+    std::vector<Transform3d> trafo_matrices;
+    for (const ModelVolume* mv : mo->volumes) {
+        if (mv->is_model_part())
+            trafo_matrices.emplace_back(mi->get_transformation().get_matrix() * mv->get_matrix());
+    }
+    update_raycast_cache(mouse_position, camera, trafo_matrices);
+    if (m_rr.mesh_id == -1)
+        return;
+
+    const Transform3d& complete_matrix = trafo_matrices[m_rr.mesh_id];
+    const Transform3d complete_scaling_matrix_inverse = Geometry::Transformation(complete_matrix).get_matrix(true, true, false, true).inverse();
+    const bool is_left_handed = Geometry::Transformation(complete_matrix).is_left_handed();
+
+    glsafe(::glPushMatrix());
+    glsafe(::glMultMatrixd(complete_matrix.data()));
+    // Inverse matrix of the instance scaling is applied so that the mark does not scale with the object.
+    glsafe(::glTranslatef(m_rr.hit(0), m_rr.hit(1), m_rr.hit(2)));
+    glsafe(::glMultMatrixd(complete_scaling_matrix_inverse.data()));
+    glsafe(::glScaled(m_cursor_radius, m_cursor_radius, m_cursor_radius));
+
+    if (is_left_handed)
+        glFrontFace(GL_CW);
+
+    float render_color[4] = { 0.f, 0.f, 0.f, 0.25f };
+    if (m_button_down == Button::Left)
+        render_color[2] = 1.f;
+    else if (m_button_down == Button::Right)
+        render_color[0] = 1.f;
+    glsafe(::glColor4fv(render_color));
+
+    m_vbo_sphere.render();
+
+    if (is_left_handed)
+        glFrontFace(GL_CCW);
+
+    glsafe(::glPopMatrix());
+}
+
+
+bool GLGizmoPainterBase::is_mesh_point_clipped(const Vec3d& point, const Transform3d& trafo) const
 {
     if (m_c->object_clipper()->get_position() == 0.)
         return false;
 
     auto sel_info = m_c->selection_info();
-    int active_inst = m_c->selection_info()->get_active_instance();
-    const ModelInstance* mi = sel_info->model_object()->instances[active_inst];
-    const Transform3d& trafo = mi->get_transformation().get_matrix();
-
     Vec3d transformed_point =  trafo * point;
     transformed_point(2) += sel_info->get_sla_shift();
     return m_c->object_clipper()->get_clipping_plane()->is_point_clipped(transformed_point);
@@ -241,104 +303,52 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
         // add several positions from between into the list, so there
         // are no gaps in the painted region.
         {
-            if (m_last_mouse_position == Vec2d::Zero())
-                m_last_mouse_position = mouse_position;
+            if (m_last_mouse_click == Vec2d::Zero())
+                m_last_mouse_click = mouse_position;
             // resolution describes minimal distance limit using circle radius
             // as a unit (e.g., 2 would mean the patches will be touching).
             double resolution = 0.7;
             double diameter_px =  resolution  * m_cursor_radius * camera.get_zoom();
-            int patches_in_between = int(((mouse_position - m_last_mouse_position).norm() - diameter_px) / diameter_px);
+            int patches_in_between = int(((mouse_position - m_last_mouse_click).norm() - diameter_px) / diameter_px);
             if (patches_in_between > 0) {
-                Vec2d diff = (mouse_position - m_last_mouse_position)/(patches_in_between+1);
+                Vec2d diff = (mouse_position - m_last_mouse_click)/(patches_in_between+1);
                 for (int i=1; i<=patches_in_between; ++i)
-                    mouse_positions.emplace_back(m_last_mouse_position + i*diff);
+                    mouse_positions.emplace_back(m_last_mouse_click + i*diff);
             }
         }
-        m_last_mouse_position = Vec2d::Zero(); // only actual hits should be saved
+        m_last_mouse_click = Vec2d::Zero(); // only actual hits should be saved
+
+        // Precalculate transformations of individual meshes.
+        std::vector<Transform3d> trafo_matrices;
+        for (const ModelVolume* mv : mo->volumes) {
+            if (mv->is_model_part())
+                trafo_matrices.emplace_back(instance_trafo * mv->get_matrix());
+        }
 
         // Now "click" into all the prepared points and spill paint around them.
         for (const Vec2d& mp : mouse_positions) {
-            std::vector<std::vector<std::pair<Vec3f, size_t>>> hit_positions_and_facet_ids;
-            bool clipped_mesh_was_hit = false;
-
-            Vec3f normal =  Vec3f::Zero();
-            Vec3f hit = Vec3f::Zero();
-            size_t facet = 0;
-            Vec3f closest_hit = Vec3f::Zero();
-            double closest_hit_squared_distance = std::numeric_limits<double>::max();
-            size_t closest_facet = 0;
-            int closest_hit_mesh_id = -1;
-
-            // Transformations of individual meshes
-            std::vector<Transform3d> trafo_matrices;
-
-            int mesh_id = -1;
-            // Cast a ray on all meshes, pick the closest hit and save it for the respective mesh
-            for (const ModelVolume* mv : mo->volumes) {
-                if (! mv->is_model_part())
-                    continue;
-
-                ++mesh_id;
-
-                trafo_matrices.push_back(instance_trafo * mv->get_matrix());
-                hit_positions_and_facet_ids.push_back(std::vector<std::pair<Vec3f, size_t>>());
-
-                if (m_c->raycaster()->raycasters()[mesh_id]->unproject_on_mesh(
-                           mp,
-                           trafo_matrices[mesh_id],
-                           camera,
-                           hit,
-                           normal,
-                           m_clipping_plane.get(),
-                           &facet))
-                {
-                    // In case this hit is clipped, skip it.
-                    if (is_mesh_point_clipped(hit.cast<double>())) {
-                        clipped_mesh_was_hit = true;
-                        continue;
-                    }
-
-                    // Is this hit the closest to the camera so far?
-                    double hit_squared_distance = (camera.get_position()-trafo_matrices[mesh_id]*hit.cast<double>()).squaredNorm();
-                    if (hit_squared_distance < closest_hit_squared_distance) {
-                        closest_hit_squared_distance = hit_squared_distance;
-                        closest_facet = facet;
-                        closest_hit_mesh_id = mesh_id;
-                        closest_hit = hit;
-                    }
-                }
-            }
+            update_raycast_cache(mp, camera, trafo_matrices);
 
             bool dragging_while_painting = (action == SLAGizmoEventType::Dragging && m_button_down != Button::None);
 
             // The mouse button click detection is enabled when there is a valid hit
             // or when the user clicks the clipping plane. Missing the object entirely
             // shall not capture the mouse.
-            if (closest_hit_mesh_id != -1 || clipped_mesh_was_hit) {
+            if (m_rr.mesh_id != -1 || m_rr.clipped_mesh_was_hit) {
                 if (m_button_down == Button::None)
                     m_button_down = ((action == SLAGizmoEventType::LeftDown) ? Button::Left : Button::Right);
             }
 
-            if (closest_hit_mesh_id == -1) {
+            if (m_rr.mesh_id == -1) {
                 // In case we have no valid hit, we can return. The event will
                 // be stopped in following two cases:
                 //  1. clicking the clipping plane
                 //  2. dragging while painting (to prevent scene rotations and moving the object)
-                return clipped_mesh_was_hit
+                return m_rr.clipped_mesh_was_hit
                     || dragging_while_painting;
             }
 
-            // Find respective mesh id.
-            mesh_id = -1;
-            for (const ModelVolume* mv : mo->volumes) {
-                if (! mv->is_model_part())
-                    continue;
-                ++mesh_id;
-                if (mesh_id == closest_hit_mesh_id)
-                    break;
-            }
-
-            const Transform3d& trafo_matrix = trafo_matrices[mesh_id];
+            const Transform3d& trafo_matrix = trafo_matrices[m_rr.mesh_id];
 
             // Calculate how far can a point be from the line (in mesh coords).
             // FIXME: The scaling of the mesh can be non-uniform.
@@ -348,12 +358,12 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
 
             // Calculate direction from camera to the hit (in mesh coords):
             Vec3f camera_pos = (trafo_matrix.inverse() * camera.get_position()).cast<float>();
-            Vec3f dir = (closest_hit - camera_pos).normalized();
+            Vec3f dir = (m_rr.hit - camera_pos).normalized();
 
-            assert(mesh_id < int(m_triangle_selectors.size()));
-            m_triangle_selectors[mesh_id]->select_patch(closest_hit, closest_facet, camera_pos,
-                                              dir, limit, new_state);
-            m_last_mouse_position = mouse_position;
+            assert(m_rr.mesh_id < int(m_triangle_selectors.size()));
+            m_triangle_selectors[m_rr.mesh_id]->select_patch(m_rr.hit, m_rr.facet, camera_pos,
+                                              dir, limit, m_cursor_type, new_state);
+            m_last_mouse_click = mouse_position;
         }
 
         return true;
@@ -389,7 +399,7 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
         update_model_object();
 
         m_button_down = Button::None;
-        m_last_mouse_position = Vec2d::Zero();
+        m_last_mouse_click = Vec2d::Zero();
         return true;
     }
 
@@ -398,6 +408,56 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
 
 
 
+void GLGizmoPainterBase::update_raycast_cache(const Vec2d& mouse_position,
+                                              const Camera& camera,
+                                              const std::vector<Transform3d>& trafo_matrices) const
+{
+    if (m_rr.mouse_position == mouse_position) {
+        // Same query as last time - the answer is already in the cache.
+        return;
+    }
+
+    bool clipped_mesh_was_hit{false};
+    Vec3f normal =  Vec3f::Zero();
+    Vec3f hit = Vec3f::Zero();
+    size_t facet = 0;
+    Vec3f closest_hit = Vec3f::Zero();
+    double closest_hit_squared_distance = std::numeric_limits<double>::max();
+    size_t closest_facet = 0;
+    int closest_hit_mesh_id = -1;
+
+    // Cast a ray on all meshes, pick the closest hit and save it for the respective mesh
+    for (int mesh_id = 0; mesh_id < int(trafo_matrices.size()); ++mesh_id) {
+
+        if (m_c->raycaster()->raycasters()[mesh_id]->unproject_on_mesh(
+                   mouse_position,
+                   trafo_matrices[mesh_id],
+                   camera,
+                   hit,
+                   normal,
+                   m_clipping_plane.get(),
+                   &facet))
+        {
+            // In case this hit is clipped, skip it.
+            if (is_mesh_point_clipped(hit.cast<double>(), trafo_matrices[mesh_id])) {
+                clipped_mesh_was_hit = true;
+                continue;
+            }
+
+            // Is this hit the closest to the camera so far?
+            double hit_squared_distance = (camera.get_position()-trafo_matrices[mesh_id]*hit.cast<double>()).squaredNorm();
+            if (hit_squared_distance < closest_hit_squared_distance) {
+                closest_hit_squared_distance = hit_squared_distance;
+                closest_facet = facet;
+                closest_hit_mesh_id = mesh_id;
+                closest_hit = hit;
+            }
+        }
+    }
+
+    m_rr = {mouse_position, closest_hit_mesh_id, closest_hit, closest_facet, clipped_mesh_was_hit};
+}
+
 bool GLGizmoPainterBase::on_is_activable() const
 {
     const Selection& selection = m_parent.get_selection();
@@ -504,13 +564,13 @@ void TriangleSelectorGUI::render(ImGuiWrapper* imgui)
     m_iva_blockers.finalize_geometry(true);
 
     if (m_iva_enforcers.has_VBOs()) {
-        ::glColor4f(0.f, 0.f, 1.f, 0.3f);
+        ::glColor4f(0.f, 0.f, 1.f, 0.4f);
         m_iva_enforcers.render();
     }
 
 
     if (m_iva_blockers.has_VBOs()) {
-        ::glColor4f(1.f, 0.f, 0.f, 0.3f);
+        ::glColor4f(1.f, 0.f, 0.f, 0.4f);
         m_iva_blockers.render();
     }
 
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp
index b3e2b65f1..8a9e87124 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp
@@ -21,6 +21,7 @@ namespace GUI {
 
 enum class SLAGizmoEventType : unsigned char;
 class ClippingPlane;
+struct Camera;
 
 enum class PainterGizmoType {
     FDM_SUPPORTS,
@@ -67,10 +68,13 @@ public:
 
 protected:
     void render_triangles(const Selection& selection) const;
+    void render_cursor() const;
     void render_cursor_circle() const;
+    void render_cursor_sphere() const;
     virtual void update_model_object() const = 0;
     virtual void update_from_model_object() = 0;
     void activate_internal_undo_redo_stack(bool activate);
+    void set_cursor_type(TriangleSelector::CursorType);
 
     float m_cursor_radius = 2.f;
     static constexpr float CursorRadiusMin  = 0.4f; // cannot be zero
@@ -80,16 +84,22 @@ protected:
     // For each model-part volume, store status and division of the triangles.
     std::vector<std::unique_ptr<TriangleSelectorGUI>> m_triangle_selectors;
 
+    TriangleSelector::CursorType m_cursor_type = TriangleSelector::SPHERE;
+
 
 private:
-    bool is_mesh_point_clipped(const Vec3d& point) const;
+    bool is_mesh_point_clipped(const Vec3d& point, const Transform3d& trafo) const;
+    void update_raycast_cache(const Vec2d& mouse_position,
+                              const Camera& camera,
+                              const std::vector<Transform3d>& trafo_matrices) const;
 
     float m_clipping_plane_distance = 0.f;
     std::unique_ptr<ClippingPlane> m_clipping_plane;
+    GLIndexedVertexArray m_vbo_sphere;
 
     bool m_internal_stack_active = false;
     bool m_schedule_update = false;
-    Vec2d m_last_mouse_position = Vec2d::Zero();
+    Vec2d m_last_mouse_click = Vec2d::Zero();
 
     enum class Button {
         None,
@@ -100,6 +110,18 @@ private:
     Button m_button_down = Button::None;
     EState m_old_state = Off; // to be able to see that the gizmo has just been closed (see on_set_state)
 
+    // Following cache holds result of a raycast query. The queries are asked
+    // during rendering the sphere cursor and painting, this saves repeated
+    // raycasts when the mouse position is the same as before.
+    struct RaycastResult {
+        Vec2d mouse_position;
+        int mesh_id;
+        Vec3f hit;
+        size_t facet;
+        bool clipped_mesh_was_hit;
+    };
+    mutable RaycastResult m_rr;
+
 protected:
     void on_set_state() override;
     void on_start_dragging() override {}
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp
index d0edfba13..0cbfaeedc 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp
@@ -25,6 +25,7 @@ bool GLGizmoSeam::on_init()
     m_desc["clipping_of_view"] = _L("Clipping of view") + ": ";
     m_desc["reset_direction"]  = _L("Reset direction");
     m_desc["cursor_size"]      = _L("Cursor size") + ": ";
+    m_desc["cursor_type"]      = _L("Cursor type") + ": ";
     m_desc["enforce_caption"]  = _L("Left mouse button") + ": ";
     m_desc["enforce"]          = _L("Enforce seam");
     m_desc["block_caption"]    = _L("Right mouse button") + " ";
@@ -55,7 +56,7 @@ void GLGizmoSeam::on_render() const
     render_triangles(selection);
 
     m_c->object_clipper()->render_cut();
-    render_cursor_circle();
+    render_cursor();
 
     glsafe(::glDisable(GL_BLEND));
 }
@@ -67,16 +68,26 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit)
     if (! m_c->selection_info()->model_object())
         return;
 
-    const float approx_height = m_imgui->scaled(18.0f);
+    const float approx_height = m_imgui->scaled(14.0f);
     y = std::min(y, bottom_limit - approx_height);
     m_imgui->set_next_window_pos(x, y, ImGuiCond_Always);
 
+    std::vector<std::string> cursor_types;
+    cursor_types.push_back(_L("Circle").ToUTF8().data());
+    cursor_types.push_back(_L("Sphere").ToUTF8().data());
+
 
     m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse);
 
     // First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that:
-    const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x, m_imgui->calc_text_size(m_desc.at("reset_direction")).x) + m_imgui->scaled(1.5f);
-    const float cursor_slider_left = m_imgui->calc_text_size(m_desc.at("cursor_size")).x + m_imgui->scaled(1.f);
+    const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x,
+                                                m_imgui->calc_text_size(m_desc.at("reset_direction")).x)
+                                           + m_imgui->scaled(1.5f);
+    const float cursor_size_slider_left = m_imgui->calc_text_size(m_desc.at("cursor_size")).x + m_imgui->scaled(1.f);
+    const float cursor_type_combo_left  = m_imgui->calc_text_size(m_desc.at("cursor_type")).x + m_imgui->scaled(1.f);
+    const float cursor_type_combo_width = std::max(m_imgui->calc_text_size(wxString::FromUTF8(cursor_types[0])).x,
+                                                   m_imgui->calc_text_size(wxString::FromUTF8(cursor_types[1])).x)
+                                             + m_imgui->scaled(2.5f);
     const float button_width = m_imgui->calc_text_size(m_desc.at("remove_all")).x + m_imgui->scaled(1.f);
     const float minimal_slider_width = m_imgui->scaled(4.f);
 
@@ -89,9 +100,10 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit)
     caption_max += m_imgui->scaled(1.f);
     total_text_max += m_imgui->scaled(1.f);
 
-    float window_width = minimal_slider_width + std::max(cursor_slider_left, clipping_slider_left);
+    float window_width = minimal_slider_width + std::max(cursor_size_slider_left, clipping_slider_left);
     window_width = std::max(window_width, total_text_max);
     window_width = std::max(window_width, button_width);
+    window_width = std::max(window_width, cursor_type_combo_left + cursor_type_combo_width);
 
     auto draw_text_with_caption = [this, &caption_max](const wxString& caption, const wxString& text) {
         static const ImVec4 ORANGE(1.0f, 0.49f, 0.22f, 1.0f);
@@ -123,8 +135,8 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit)
     const float max_tooltip_width = ImGui::GetFontSize() * 20.0f;
 
     m_imgui->text(m_desc.at("cursor_size"));
-    ImGui::SameLine(clipping_slider_left);
-    ImGui::PushItemWidth(window_width - clipping_slider_left);
+    ImGui::SameLine(cursor_size_slider_left);
+    ImGui::PushItemWidth(window_width - cursor_size_slider_left);
     ImGui::SliderFloat(" ", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f");
     if (ImGui::IsItemHovered()) {
         ImGui::BeginTooltip();
@@ -134,6 +146,24 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit)
         ImGui::EndTooltip();
     }
 
+
+    m_imgui->text(m_desc.at("cursor_type"));
+    ImGui::SameLine(window_width - cursor_type_combo_width - m_imgui->scaled(0.5f));
+    ImGui::PushItemWidth(cursor_type_combo_width);
+    int selection = int(m_cursor_type);
+    m_imgui->combo("", cursor_types, selection);
+    m_cursor_type = TriangleSelector::CursorType(selection);
+    if (ImGui::IsItemHovered()) {
+        ImGui::BeginTooltip();
+        ImGui::PushTextWrapPos(max_tooltip_width);
+        ImGui::TextUnformatted(_L("Sphere paints all facets inside, regardless of their orientation.\n\n"
+                                  "Circle ignores facets facing away from the camera.").ToUTF8().data());
+        ImGui::PopTextWrapPos();
+        ImGui::EndTooltip();
+    }
+
+
+
     ImGui::Separator();
     if (m_c->object_clipper()->get_position() == 0.f)
         m_imgui->text(m_desc.at("clipping_of_view"));
diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp
index 47962f4b2..b9e7c7a6f 100644
--- a/src/slic3r/GUI/NotificationManager.cpp
+++ b/src/slic3r/GUI/NotificationManager.cpp
@@ -56,8 +56,7 @@ NotificationManager::PopNotification::~PopNotification()
 }
 NotificationManager::PopNotification::RenderResult NotificationManager::PopNotification::render(GLCanvas3D& canvas, const float& initial_y)
 {
-	if (!m_initialized)
-	{
+	if (!m_initialized) {
 		init();
 	}
 	if (m_finished)
@@ -362,7 +361,7 @@ void NotificationManager::PopNotification::render_hypertext(ImGuiWrapper& imgui,
 	ImGui::PopStyleColor();
 
 	//hover color
-	ImVec4 orange_color = ImGui::GetStyleColorVec4(ImGuiCol_Button);
+	ImVec4 orange_color = ImVec4(.99f, .313f, .0f, 1.0f);//ImGui::GetStyleColorVec4(ImGuiCol_Button);
 	if (ImGui::IsItemHovered(ImGuiHoveredFlags_RectOnly))
 		orange_color.y += 0.2f;
 
@@ -682,11 +681,13 @@ void NotificationManager::push_plater_error_notification(const std::string& text
 void NotificationManager::push_plater_warning_notification(const std::string& text, GLCanvas3D& canvas)
 {
 	push_notification_data({ NotificationType::PlaterWarning, NotificationLevel::WarningNotification, 0,  _u8L("WARNING:") + "\n" + text }, canvas, 0);
+	// dissaper if in preview
+	set_in_preview(m_in_preview);
 }
-void NotificationManager::close_plater_error_notification()
+void NotificationManager::close_plater_error_notification(const std::string& text)
 {
 	for (PopNotification* notification : m_pop_notifications) {
-		if (notification->get_type() == NotificationType::PlaterError) {
+		if (notification->get_type() == NotificationType::PlaterError && notification->compare_text(_u8L("ERROR:") + "\n" + text)) {
 			notification->close();
 		}
 	}
diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp
index a11d08394..c2cbc242c 100644
--- a/src/slic3r/GUI/NotificationManager.hpp
+++ b/src/slic3r/GUI/NotificationManager.hpp
@@ -220,7 +220,8 @@ public:
 	void compare_warning_oids(const std::vector<size_t>& living_oids);
 	void push_plater_error_notification(const std::string& text, GLCanvas3D& canvas);
 	void push_plater_warning_notification(const std::string& text, GLCanvas3D& canvas);
-	void close_plater_error_notification();
+	// Closes error or warning of same text
+	void close_plater_error_notification(const std::string& text);
 	void close_plater_warning_notification(const std::string& text);
 	// creates special notification slicing complete
 	// if large = true prints printing time and export button 
@@ -250,7 +251,7 @@ private:
 	bool                         m_hovered { false };
 	//timestamps used for slining finished - notification could be gone so it needs to be stored here
 	std::unordered_set<int>      m_used_timestamps;
-    bool                         m_in_preview;
+	bool                         m_in_preview { false };
 
 	//prepared (basic) notifications
 	const std::vector<NotificationData> basic_notifications = {
diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp
index e9a445bfa..e3ee9481c 100644
--- a/src/slic3r/GUI/Tab.cpp
+++ b/src/slic3r/GUI/Tab.cpp
@@ -38,6 +38,10 @@
 #include "PhysicalPrinterDialog.hpp"
 #include "UnsavedChangesDialog.hpp"
 
+#ifdef WIN32
+	#include <commctrl.h>
+#endif // WIN32
+
 namespace Slic3r {
 namespace GUI {
 
@@ -432,6 +436,31 @@ void Tab::OnActivate()
     m_size_move *= -1;
 #endif // __WXOSX__
 
+#ifdef __WXMSW__
+    // Workaround for tooltips over Tree Controls displayed over excessively long
+    // tree control items, stealing the window focus.
+    //
+    // In case the Tab was reparented from the MainFrame to the floating dialog,
+    // the tooltip created by the Tree Control before reparenting is not reparented, 
+    // but it still points to the MainFrame. If the tooltip pops up, the MainFrame 
+    // is incorrectly focussed, stealing focus from the floating dialog.
+    //
+    // The workaround is to delete the tooltip control.
+    // Vojtech tried to reparent the tooltip control, but it did not work,
+    // and if the Tab was later reparented back to MainFrame, the tooltip was displayed
+    // at an incorrect position, therefore it is safer to just discard the tooltip control
+    // altogether.
+    HWND hwnd_tt = TreeView_GetToolTips(m_treectrl->GetHandle());
+    if (hwnd_tt) {
+	    HWND hwnd_toplevel 	= find_toplevel_parent(m_treectrl)->GetHandle();
+	    HWND hwnd_parent 	= ::GetParent(hwnd_tt);
+	    if (hwnd_parent != hwnd_toplevel) {
+	    	::DestroyWindow(hwnd_tt);
+			TreeView_SetToolTips(m_treectrl->GetHandle(), nullptr);
+	    }
+    }
+#endif
+
     // create controls on active page
     activate_selected_page([](){});
 //    m_active_page->Show();
diff --git a/src/slic3r/Utils/Process.cpp b/src/slic3r/Utils/Process.cpp
index 375c10a6a..d2a326221 100644
--- a/src/slic3r/Utils/Process.cpp
+++ b/src/slic3r/Utils/Process.cpp
@@ -56,11 +56,17 @@ static void start_new_slicer_or_gcodeviewer(const NewSlicerInstanceType instance
     boost::filesystem::path bin_path = into_path(wxStandardPaths::Get().GetExecutablePath());
 	#if defined(__APPLE__)
 	{
-		bin_path =	bin_path.parent_path() / ((instance_type == NewSlicerInstanceType::Slicer) ? "PrusaSlicer" : "PrusaGCodeViewer");
+		// Maybe one day we will be able to run PrusaGCodeViewer, but for now the Apple notarization 
+		// process refuses Apps with multiple binaries and Vojtech does not know any workaround.
+		// ((instance_type == NewSlicerInstanceType::Slicer) ? "PrusaSlicer" : "PrusaGCodeViewer");
+		// Just run PrusaSlicer and give it a --gcodeviewer parameter.
+		bin_path =	bin_path.parent_path() / "PrusaSlicer";
 		// On Apple the wxExecute fails, thus we use boost::process instead.
 		BOOST_LOG_TRIVIAL(info) << "Trying to spawn a new slicer \"" << bin_path.string() << "\"";
 		try {
             std::vector<std::string> args;
+            if (instance_type == NewSlicerInstanceType::GCodeViewer)
+            	args.emplace_back("--gcodeviewer");
             if (path_to_open)
                 args.emplace_back(into_u8(*path_to_open));
             boost::process::spawn(bin_path, args);