diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm
index 6baefa545..e897a6353 100644
--- a/lib/Slic3r/GUI/MainFrame.pm
+++ b/lib/Slic3r/GUI/MainFrame.pm
@@ -26,6 +26,14 @@ our $appController;
 our $VALUE_CHANGE_EVENT    = Wx::NewEventType;
 # 2) To inform about a preset selection change or a "modified" status change.
 our $PRESETS_CHANGED_EVENT = Wx::NewEventType;
+# 3) To inform about a change of object selection
+our $OBJECT_SELECTION_CHANGED_EVENT = Wx::NewEventType;
+# 4) To inform about a change of object settings
+our $OBJECT_SETTINGS_CHANGED_EVENT = Wx::NewEventType;
+# 5) To inform about a remove of object 
+our $OBJECT_REMOVE_EVENT = Wx::NewEventType;
+# 6) To inform about a update of the scene 
+our $UPDATE_SCENE_EVENT = Wx::NewEventType;
 
 sub new {
     my ($class, %params) = @_;
@@ -114,6 +122,8 @@ sub new {
 
     $self->update_ui_from_settings;
 
+    Slic3r::GUI::update_mode();
+
     return $self;
 }
 
@@ -133,7 +143,12 @@ sub _init_tabpanel {
     });
     
     if (!$self->{no_plater}) {
-        $panel->AddPage($self->{plater} = Slic3r::GUI::Plater->new($panel), L("Plater"));
+        $panel->AddPage($self->{plater} = Slic3r::GUI::Plater->new($panel,
+            event_object_selection_changed   => $OBJECT_SELECTION_CHANGED_EVENT,
+            event_object_settings_changed    => $OBJECT_SETTINGS_CHANGED_EVENT,
+            event_remove_object              => $OBJECT_REMOVE_EVENT,
+            event_update_scene               => $UPDATE_SCENE_EVENT,
+            ), L("Plater"));
         if (!$self->{no_controller}) {
             $panel->AddPage($self->{controller} = Slic3r::GUI::Controller->new($panel), L("Controller"));
         }
@@ -154,6 +169,10 @@ sub _init_tabpanel {
                 my $value = $event->GetInt();
                 $self->{plater}->on_extruders_change($value);
             }
+            if ($opt_key eq 'printer_technology'){
+                my $value = $event->GetInt();# 0 ~ "ptFFF"; 1 ~ "ptSLA"
+                $self->{plater}->show_preset_comboboxes($value);
+            }
         }
         # don't save while loading for the first time
         $self->config->save($Slic3r::GUI::autosave) if $Slic3r::GUI::autosave && $self->{loaded};        
@@ -166,7 +185,7 @@ sub _init_tabpanel {
 
         my $tab = Slic3r::GUI::get_preset_tab($tab_name);
         if ($self->{plater}) {
-            # Update preset combo boxes (Print settings, Filament, Printer) from their respective tabs.
+            # Update preset combo boxes (Print settings, Filament, Material, Printer) from their respective tabs.
             my $presets = $tab->get_presets;
             if (defined $presets){
                 my $reload_dependent_tabs = $tab->get_dependent_tabs;
@@ -174,7 +193,7 @@ sub _init_tabpanel {
                 $self->{plater}->{"selected_item_$tab_name"} = $tab->get_selected_preset_item;
                 if ($tab_name eq 'printer') {
                     # Printer selected at the Printer tab, update "compatible" marks at the print and filament selectors.
-                    for my $tab_name_other (qw(print filament)) {
+                    for my $tab_name_other (qw(print filament sla_material)) {
                         # If the printer tells us that the print or filament preset has been switched or invalidated,
                         # refresh the print or filament tab page. Otherwise just refresh the combo box.
                         my $update_action = ($reload_dependent_tabs && (first { $_ eq $tab_name_other } (@{$reload_dependent_tabs}))) 
@@ -188,9 +207,43 @@ sub _init_tabpanel {
             }
         }
     });
+
+    # The following event is emited by the C++ Tab implementation on object selection change.
+    EVT_COMMAND($self, -1, $OBJECT_SELECTION_CHANGED_EVENT, sub {
+        my ($self, $event) = @_;
+        my $obj_idx = $event->GetId;
+        my $child = $event->GetInt == 1 ? 1 : undef;
+
+        $self->{plater}->select_object($obj_idx < 0 ? undef: $obj_idx, $child);
+        $self->{plater}->item_changed_selection($obj_idx);
+    });
+
+    # The following event is emited by the C++ GUI implementation on object settings change.
+    EVT_COMMAND($self, -1, $OBJECT_SETTINGS_CHANGED_EVENT, sub {
+        my ($self, $event) = @_;
+
+        my $line = $event->GetString;
+        my ($obj_idx, $parts_changed, $part_settings_changed) = split('',$line);
+
+        $self->{plater}->changed_object_settings($obj_idx, $parts_changed, $part_settings_changed);
+    });
+
+    # The following event is emited by the C++ GUI implementation on object remove.
+    EVT_COMMAND($self, -1, $OBJECT_REMOVE_EVENT, sub {
+        my ($self, $event) = @_;
+        $self->{plater}->remove();
+    });
+
+    # The following event is emited by the C++ GUI implementation on extruder change for object.
+    EVT_COMMAND($self, -1, $UPDATE_SCENE_EVENT, sub {
+        my ($self, $event) = @_;
+        $self->{plater}->update();
+    });
+        
+
     Slic3r::GUI::create_preset_tabs($self->{no_controller}, $VALUE_CHANGE_EVENT, $PRESETS_CHANGED_EVENT);
     $self->{options_tabs} = {};
-    for my $tab_name (qw(print filament printer)) {
+    for my $tab_name (qw(print filament sla_material printer)) {
         $self->{options_tabs}{$tab_name} = Slic3r::GUI::get_preset_tab("$tab_name");
     }
     
@@ -202,8 +255,14 @@ sub _init_tabpanel {
         # load initial config
         my $full_config = wxTheApp->{preset_bundle}->full_config;
         $self->{plater}->on_config_change($full_config);
+
         # Show a correct number of filament fields.
-        $self->{plater}->on_extruders_change(int(@{$full_config->nozzle_diameter}));
+        if (defined $full_config->nozzle_diameter){ # nozzle_diameter is undefined when SLA printer is selected
+            $self->{plater}->on_extruders_change(int(@{$full_config->nozzle_diameter}));
+        }
+
+        # Show correct preset comboboxes according to the printer_technology
+        $self->{plater}->show_preset_comboboxes(($full_config->printer_technology eq "FFF") ? 0 : 1);
     }
 }
 
diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm
index b9ae911db..4055b2b36 100644
--- a/lib/Slic3r/GUI/Plater.pm
+++ b/lib/Slic3r/GUI/Plater.pm
@@ -49,7 +49,7 @@ my $PreventListEvents = 0;
 our $appController;
 
 sub new {
-    my ($class, $parent) = @_;
+    my ($class, $parent, %params) = @_;
     my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
     $self->{config} = Slic3r::Config::new_from_defaults_keys([qw(
         bed_shape complete_objects extruder_clearance_radius skirts skirt_distance brim_width variable_layer_height
@@ -57,6 +57,13 @@ sub new {
         nozzle_diameter single_extruder_multi_material wipe_tower wipe_tower_x wipe_tower_y wipe_tower_width
 	wipe_tower_rotation_angle extruder_colour filament_colour max_print_height printer_model
     )]);
+
+    # store input params
+    $self->{event_object_selection_changed} = $params{event_object_selection_changed};
+    $self->{event_object_settings_changed} = $params{event_object_settings_changed};
+    $self->{event_remove_object} = $params{event_remove_object};
+    $self->{event_update_scene} = $params{event_update_scene};
+
     # C++ Slic3r::Model with Perl extensions in Slic3r/Model.pm
     $self->{model} = Slic3r::Model->new;
     # C++ Slic3r::Print with Perl extensions in Slic3r/Print.pm
@@ -74,7 +81,7 @@ sub new {
     });
     
     # Initialize preview notebook
-    $self->{preview_notebook} = Wx::Notebook->new($self, -1, wxDefaultPosition, [335,335], wxNB_BOTTOM);
+    $self->{preview_notebook} = Wx::Notebook->new($self, -1, wxDefaultPosition, [-1,335], wxNB_BOTTOM);
     
     # Initialize handlers for canvases
     my $on_select_object = sub {
@@ -128,7 +135,9 @@ sub new {
         }
         $_->set_scaling_factor($scale) for @{ $model_object->instances };
         
-        $self->{list}->SetItem($obj_idx, 2, ($model_object->instances->[0]->scaling_factor * 100) . "%");
+        # Set object scale on c++ side
+        Slic3r::GUI::set_object_scale($obj_idx, $model_object->instances->[0]->scaling_factor * 100); 
+
 #        $object->transform_thumbnail($self->{model}, $obj_idx);
     
         #update print and start background processing
@@ -359,37 +368,12 @@ sub new {
 #    }
 
     ### Panel for right column
-    $self->{right_panel} = Wx::Panel->new($self, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
-    
-    ### Scrolled Window for info boxes
-    my $scrolled_window_sizer = $self->{scrolled_window_sizer} = Wx::BoxSizer->new(wxVERTICAL);
-    $scrolled_window_sizer->SetMinSize([310, -1]);
-    my $scrolled_window_panel = $self->{scrolled_window_panel} = Wx::ScrolledWindow->new($self->{right_panel}, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
-    $scrolled_window_panel->SetSizer($scrolled_window_sizer);
-    $scrolled_window_panel->SetScrollbars(1, 1, 1, 1);    
-
-    $self->{list} = Wx::ListView->new($scrolled_window_panel, -1, wxDefaultPosition, wxDefaultSize,
-        wxLC_SINGLE_SEL | wxLC_REPORT | wxBORDER_SUNKEN | wxTAB_TRAVERSAL | wxWANTS_CHARS );
-    $self->{list}->InsertColumn(0, L("Name"), wxLIST_FORMAT_LEFT, 145);
-    $self->{list}->InsertColumn(1, L("Copies"), wxLIST_FORMAT_CENTER, 45);
-    $self->{list}->InsertColumn(2, L("Scale"), wxLIST_FORMAT_CENTER, wxLIST_AUTOSIZE_USEHEADER);
-    EVT_LIST_ITEM_SELECTED($self, $self->{list}, \&list_item_selected);
-    EVT_LIST_ITEM_DESELECTED($self, $self->{list}, \&list_item_deselected);
-    EVT_LIST_ITEM_ACTIVATED($self, $self->{list}, \&list_item_activated);
-    EVT_KEY_DOWN($self->{list}, sub {
-        my ($list, $event) = @_;
-        if ($event->GetKeyCode == WXK_TAB) {
-            $list->Navigate($event->ShiftDown ? &Wx::wxNavigateBackward : &Wx::wxNavigateForward);
-        } elsif ($event->GetKeyCode == WXK_DELETE ||
-                ($event->GetKeyCode == WXK_BACK && &Wx::wxMAC) ) {
-            $self->remove;
-        } else {
-            $event->Skip;
-        }
-    });
+#    $self->{right_panel} = Wx::Panel->new($self, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
+    $self->{right_panel} = Wx::ScrolledWindow->new($self, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
+    $self->{right_panel}->SetScrollbars(0, 1, 1, 1);
     
     # right pane buttons
-    $self->{btn_export_gcode} = Wx::Button->new($self->{right_panel}, -1, L("Export G-code…"), wxDefaultPosition, [-1, 30], wxBU_LEFT);
+    $self->{btn_export_gcode} = Wx::Button->new($self->{right_panel}, -1, L("Export G-code…"), wxDefaultPosition, [-1, 30], wxNO_BORDER);#, wxBU_LEFT);
     $self->{btn_reslice} = Wx::Button->new($self->{right_panel}, -1, L("Slice now"), wxDefaultPosition, [-1, 30], wxBU_LEFT);
     $self->{btn_print} = Wx::Button->new($self->{right_panel}, -1, L("Print…"), wxDefaultPosition, [-1, 30], wxBU_LEFT);
     $self->{btn_send_gcode} = Wx::Button->new($self->{right_panel}, -1, L("Send to printer"), wxDefaultPosition, [-1, 30], wxBU_LEFT);
@@ -399,8 +383,8 @@ sub new {
     $self->{btn_print}->Hide;
     $self->{btn_send_gcode}->Hide;
     
+#       export_gcode    cog_go.png
     my %icons = qw(
-        export_gcode    cog_go.png
         print           arrow_up.png
         send_gcode      arrow_up.png
         reslice         reslice.png
@@ -444,6 +428,7 @@ sub new {
 #    } else {
 #        EVT_BUTTON($self, $self->{btn_add}, sub { $self->add; });
 #        EVT_BUTTON($self, $self->{btn_remove}, sub { $self->remove() }); # explicitly pass no argument to remove
+#        EVT_BUTTON($self, $self->{btn_remove}, sub { Slic3r::GUI::remove_obj() }); # explicitly pass no argument to remove
 #        EVT_BUTTON($self, $self->{btn_reset}, sub { $self->reset; });
 #        EVT_BUTTON($self, $self->{btn_arrange}, sub { $self->arrange; });
 #        EVT_BUTTON($self, $self->{btn_increase}, sub { $self->increase; });
@@ -460,7 +445,7 @@ sub new {
     $_->SetDropTarget(Slic3r::GUI::Plater::DropTarget->new($self))
         for grep defined($_),
             $self, $self->{canvas3D}, $self->{preview3D}, $self->{list};
-#            $self, $self->{canvas}, $self->{canvas3D}, $self->{preview3D}, $self->{list};
+#            $self, $self->{canvas}, $self->{canvas3D}, $self->{preview3D};
     
     EVT_COMMAND($self, -1, $PROGRESS_BAR_EVENT, sub {
         my ($self, $event) = @_;
@@ -504,20 +489,21 @@ sub new {
     {
         my $presets;
         {
-            $presets = $self->{presets_sizer} = Wx::FlexGridSizer->new(3, 2, 1, 2);
+            $presets = $self->{presets_sizer} = Wx::FlexGridSizer->new(4, 2, 1, 2);
             $presets->AddGrowableCol(1, 1);
             $presets->SetFlexibleDirection(wxHORIZONTAL);
             my %group_labels = (
                 print       => L('Print settings'),
                 filament    => L('Filament'),
+                sla_material=> L('SLA material'),
                 printer     => L('Printer'),
             );
-            # UI Combo boxes for a print, multiple filaments, and a printer.
+            # UI Combo boxes for a print, multiple filaments, SLA material and a printer.
             # Initially a single filament combo box is created, but the number of combo boxes for the filament selection may increase,
             # once a printer preset with multiple extruders is activated.
             # $self->{preset_choosers}{$group}[$idx]
             $self->{preset_choosers} = {};
-            for my $group (qw(print filament printer)) {
+            for my $group (qw(print filament sla_material printer)) {
                 my $text = Wx::StaticText->new($self->{right_panel}, -1, "$group_labels{$group}:", wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT);
                 $text->SetFont($Slic3r::GUI::small_font);
                 my $choice = Wx::BitmapComboBox->new($self->{right_panel}, -1, "", wxDefaultPosition, wxDefaultSize, [], wxCB_READONLY);
@@ -538,15 +524,32 @@ sub new {
             $presets->Layout;
         }
 
-        my $frequently_changed_parameters_sizer = Wx::BoxSizer->new(wxHORIZONTAL);
+        my $frequently_changed_parameters_sizer = $self->{frequently_changed_parameters_sizer} = Wx::BoxSizer->new(wxVERTICAL);
         Slic3r::GUI::add_frequently_changed_parameters($self->{right_panel}, $frequently_changed_parameters_sizer, $presets);
 
+        my $expert_mode_part_sizer = Wx::BoxSizer->new(wxVERTICAL);
+        Slic3r::GUI::add_expert_mode_part(  $self->{right_panel}, $expert_mode_part_sizer, 
+                                            $self->{model},
+                                            $self->{event_object_selection_changed},
+                                            $self->{event_object_settings_changed},
+                                            $self->{event_remove_object},
+                                            $self->{event_update_scene});
+#        if ($expert_mode_part_sizer->IsShown(2)==1) 
+#        { 
+#           $expert_mode_part_sizer->Layout;
+#            $expert_mode_part_sizer->Show(2, 0); # ? Why doesn't work
+#            $self->{right_panel}->Layout;
+#       }
+
         my $object_info_sizer;
         {
-            my $box = Wx::StaticBox->new($scrolled_window_panel, -1, L("Info"));
+#            my $box = Wx::StaticBox->new($scrolled_window_panel, -1, L("Info"));
+            my $box = Wx::StaticBox->new($self->{right_panel}, -1, L("Info"));
+            $box->SetFont($Slic3r::GUI::small_bold_font);
             $object_info_sizer = Wx::StaticBoxSizer->new($box, wxVERTICAL);
             $object_info_sizer->SetMinSize([300,-1]);
-            my $grid_sizer = Wx::FlexGridSizer->new(3, 4, 5, 5);
+            #!my $grid_sizer = Wx::FlexGridSizer->new(3, 4, 5, 5);
+            my $grid_sizer = Wx::FlexGridSizer->new(2, 4, 5, 5);
             $grid_sizer->SetFlexibleDirection(wxHORIZONTAL);
             $grid_sizer->AddGrowableCol(1, 1);
             $grid_sizer->AddGrowableCol(3, 1);
@@ -561,28 +564,45 @@ sub new {
             );
             while (my $field = shift @info) {
                 my $label = shift @info;
-                my $text = Wx::StaticText->new($scrolled_window_panel, -1, "$label:", wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
+#                my $text = Wx::StaticText->new($scrolled_window_panel, -1, "$label:", wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
+                my $text = Wx::StaticText->new($self->{right_panel}, -1, "$label:", wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
                 $text->SetFont($Slic3r::GUI::small_font);
-                $grid_sizer->Add($text, 0);
+                #!$grid_sizer->Add($text, 0);
                 
-                $self->{"object_info_$field"} = Wx::StaticText->new($scrolled_window_panel, -1, "", wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
+#                $self->{"object_info_$field"} = Wx::StaticText->new($scrolled_window_panel, -1, "", wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
+                $self->{"object_info_$field"} = Wx::StaticText->new($self->{right_panel}, -1, "", wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
                 $self->{"object_info_$field"}->SetFont($Slic3r::GUI::small_font);
                 if ($field eq 'manifold') {
-                    $self->{object_info_manifold_warning_icon} = Wx::StaticBitmap->new($scrolled_window_panel, -1, Wx::Bitmap->new(Slic3r::var("error.png"), wxBITMAP_TYPE_PNG));
-                    $self->{object_info_manifold_warning_icon}->Hide;
+#                    $self->{object_info_manifold_warning_icon} = Wx::StaticBitmap->new($scrolled_window_panel, -1, Wx::Bitmap->new(Slic3r::var("error.png"), wxBITMAP_TYPE_PNG));
+                    $self->{object_info_manifold_warning_icon} = Wx::StaticBitmap->new($self->{right_panel}, -1, Wx::Bitmap->new(Slic3r::var("error.png"), wxBITMAP_TYPE_PNG));
+                    #$self->{object_info_manifold_warning_icon}->Hide;
+                    $self->{"object_info_manifold_warning_icon_show"} = sub {
+                        if ($self->{object_info_manifold_warning_icon}->IsShown() != $_[0]) {
+                            Slic3r::GUI::set_show_manifold_warning_icon($_[0]);
+                            my $mode = wxTheApp->{app_config}->get("view_mode");
+                            return if ($mode eq "" || $mode eq "simple");
+                            $self->{object_info_manifold_warning_icon}->Show($_[0]); 
+                            $self->Layout
+                        }
+                    };
+                    $self->{"object_info_manifold_warning_icon_show"}->(0);
                     
                     my $h_sizer = Wx::BoxSizer->new(wxHORIZONTAL);
-                    $h_sizer->Add($self->{object_info_manifold_warning_icon}, 0);
-                    $h_sizer->Add($self->{"object_info_$field"}, 0);
-                    $grid_sizer->Add($h_sizer, 0, wxEXPAND);
+                    $h_sizer->Add($text, 0);
+                    $h_sizer->Add($self->{object_info_manifold_warning_icon}, 0, wxLEFT, 2);
+                    $h_sizer->Add($self->{"object_info_$field"}, 0, wxLEFT, 2);
+                    #!$grid_sizer->Add($h_sizer, 0, wxEXPAND);
+                    $object_info_sizer->Add($h_sizer, 0, wxEXPAND|wxTOP, 4);
                 } else {
+                    $grid_sizer->Add($text, 0);
                     $grid_sizer->Add($self->{"object_info_$field"}, 0);
                 }
             }
         }
 
         my $print_info_sizer = $self->{print_info_sizer} = Wx::StaticBoxSizer->new(
-                Wx::StaticBox->new($scrolled_window_panel, -1, L("Sliced Info")), wxVERTICAL);
+#                Wx::StaticBox->new($scrolled_window_panel, -1, L("Sliced Info")), wxVERTICAL);
+                Wx::StaticBox->new($self->{right_panel}, -1, L("Sliced Info")), wxVERTICAL);
         $print_info_sizer->SetMinSize([300,-1]);
         
         my $buttons_sizer = Wx::BoxSizer->new(wxHORIZONTAL);
@@ -592,22 +612,29 @@ sub new {
         $buttons_sizer->Add($self->{btn_reslice}, 0, wxALIGN_RIGHT, 0);
         $buttons_sizer->Add($self->{btn_print}, 0, wxALIGN_RIGHT, 0);
         $buttons_sizer->Add($self->{btn_send_gcode}, 0, wxALIGN_RIGHT, 0);
-        $buttons_sizer->Add($self->{btn_export_gcode}, 0, wxALIGN_RIGHT, 0);
         
-        $scrolled_window_sizer->Add($self->{list}, 1, wxEXPAND, 5);
-        $scrolled_window_sizer->Add($object_info_sizer, 0, wxEXPAND, 0);
-        $scrolled_window_sizer->Add($print_info_sizer, 0, wxEXPAND, 0);
+#        $scrolled_window_sizer->Add($self->{list}, 1, wxEXPAND, 5);
+#        $scrolled_window_sizer->Add($object_info_sizer, 0, wxEXPAND, 0);
+#        $scrolled_window_sizer->Add($print_info_sizer, 0, wxEXPAND, 0);
+        #$buttons_sizer->Add($self->{btn_export_gcode}, 0, wxALIGN_RIGHT, 0);
+
+        ### Sizer for info boxes
+        my $info_sizer = $self->{info_sizer} = Wx::BoxSizer->new(wxVERTICAL);
+        $info_sizer->SetMinSize([318, -1]);        
+        $info_sizer->Add($object_info_sizer, 0, wxEXPAND | wxBOTTOM, 5);
+        $info_sizer->Add($print_info_sizer, 0, wxEXPAND | wxBOTTOM, 5);
 
         my $right_sizer = Wx::BoxSizer->new(wxVERTICAL);
-        $right_sizer->SetMinSize([320,-1]);
+        $self->{right_panel}->SetSizer($right_sizer);
+        $right_sizer->SetMinSize([320, -1]);
         $right_sizer->Add($presets, 0, wxEXPAND | wxTOP, 10) if defined $presets;
-        $right_sizer->Add($frequently_changed_parameters_sizer, 0, wxEXPAND | wxTOP, 0) if defined $frequently_changed_parameters_sizer;
-        $right_sizer->Add($buttons_sizer, 0, wxEXPAND | wxBOTTOM, 5);
-        $right_sizer->Add($scrolled_window_panel, 1, wxEXPAND | wxALL, 1);
+        $right_sizer->Add($frequently_changed_parameters_sizer, 1, wxEXPAND | wxTOP, 0) if defined $frequently_changed_parameters_sizer;
+        $right_sizer->Add($expert_mode_part_sizer, 0, wxEXPAND | wxTOP, 10) if defined $expert_mode_part_sizer;
+        $right_sizer->Add($buttons_sizer, 0, wxEXPAND | wxBOTTOM | wxTOP, 10);
+        $right_sizer->Add($info_sizer, 0, wxEXPAND | wxLEFT, 20);
         # Show the box initially, let it be shown after the slicing is finished.
         $self->print_info_box_show(0);
-
-        $self->{right_panel}->SetSizer($right_sizer);
+        $right_sizer->Add($self->{btn_export_gcode}, 0, wxEXPAND | wxLEFT | wxTOP | wxBOTTOM, 20);
 
         my $hsizer = Wx::BoxSizer->new(wxHORIZONTAL);
         $hsizer->Add($self->{preview_notebook}, 1, wxEXPAND | wxTOP, 1);
@@ -620,6 +647,18 @@ sub new {
         
         $sizer->SetSizeHints($self);
         $self->SetSizer($sizer);
+
+        # Send sizers/buttons to C++
+        Slic3r::GUI::set_objects_from_perl( $self->{right_panel},
+                                $frequently_changed_parameters_sizer,
+                                $expert_mode_part_sizer,
+                                $info_sizer,
+                                $self->{btn_export_gcode},
+                                $self->{btn_export_stl},
+                                $self->{btn_reslice},
+                                $self->{btn_print},
+                                $self->{btn_send_gcode},
+                                $self->{object_info_manifold_warning_icon} );
     }
 
     # Last correct selected item for each preset
@@ -630,6 +669,7 @@ sub new {
     }
 
     $self->update_ui_from_settings();
+    $self->Layout;
     
     return $self;
 }
@@ -710,16 +750,17 @@ sub update_ui_from_settings
     }
 }
 
-# Update preset combo boxes (Print settings, Filament, Printer) from their respective tabs.
+# Update preset combo boxes (Print settings, Filament, Material, Printer) from their respective tabs.
 # Called by 
 #       Slic3r::GUI::Tab::Print::_on_presets_changed
 #       Slic3r::GUI::Tab::Filament::_on_presets_changed
+#       Slic3r::GUI::Tab::Material::_on_presets_changed
 #       Slic3r::GUI::Tab::Printer::_on_presets_changed
 # when the presets are loaded or the user selects another preset.
 # For Print settings and Printer, synchronize the selection index with their tabs.
 # For Filament, synchronize the selection index for a single extruder printer only, otherwise keep the selection.
 sub update_presets {
-    # $group: one of qw(print filament printer)
+    # $group: one of qw(print filament sla_material printer)
     # $presets: PresetCollection
     my ($self, $group, $presets) = @_;
     my @choosers = @{$self->{preset_choosers}{$group}};
@@ -735,6 +776,8 @@ sub update_presets {
         }
     } elsif ($group eq 'print') {
         wxTheApp->{preset_bundle}->print->update_platter_ui($choosers[0]);
+    } elsif ($group eq 'sla_material') {
+        wxTheApp->{preset_bundle}->sla_material->update_platter_ui($choosers[0]);
     } elsif ($group eq 'printer') {
         # Update the print choosers to only contain the compatible presets, update the dirty flags.
         wxTheApp->{preset_bundle}->print->update_platter_ui($self->{preset_choosers}{print}->[0]);
@@ -901,12 +944,10 @@ sub load_model_objects {
     foreach my $obj_idx (@obj_idx) {
         my $object = $self->{objects}[$obj_idx];
         my $model_object = $self->{model}->objects->[$obj_idx];
-        $self->{list}->InsertStringItem($obj_idx, $object->name);
-        $self->{list}->SetItemFont($obj_idx, Wx::Font->new(10, wxDEFAULT, wxNORMAL, wxNORMAL))
-            if $self->{list}->can('SetItemFont');  # legacy code for wxPerl < 0.9918 not supporting SetItemFont()
+
+        # Add object to list on c++ side
+        Slic3r::GUI::add_object_to_list($object->name, $model_object);
     
-        $self->{list}->SetItem($obj_idx, 1, $model_object->instances_count);
-        $self->{list}->SetItem($obj_idx, 2, ($model_object->instances->[0]->scaling_factor * 100) . "%");
 
 #        $self->reset_thumbnail($obj_idx);
     }
@@ -916,8 +957,6 @@ sub load_model_objects {
     # zoom to objects
     Slic3r::GUI::_3DScene::zoom_to_volumes($self->{canvas3D}) if $self->{canvas3D};
     
-    $self->{list}->Update;
-    $self->{list}->Select($obj_idx[-1], 1);
     $self->object_list_changed;
     
     $self->schedule_background_process;
@@ -952,7 +991,8 @@ sub remove {
     splice @{$self->{objects}}, $obj_idx, 1;
     $self->{model}->delete_object($obj_idx);
     $self->{print}->delete_object($obj_idx);
-    $self->{list}->DeleteItem($obj_idx);
+    # Delete object from list on c++ side
+    Slic3r::GUI::delete_object_from_list();
     $self->object_list_changed;
     
     $self->select_object(undef);
@@ -972,7 +1012,8 @@ sub reset {
     @{$self->{objects}} = ();
     $self->{model}->clear_objects;
     $self->{print}->clear_objects;
-    $self->{list}->DeleteAllItems;
+    # Delete all objects from list on c++ side
+    Slic3r::GUI::delete_all_objects_from_list();
     $self->object_list_changed;
     
     $self->select_object(undef);
@@ -994,7 +1035,8 @@ sub increase {
         );
         $self->{print}->objects->[$obj_idx]->add_copy($instance->offset);
     }
-    $self->{list}->SetItem($obj_idx, 1, $model_object->instances_count);
+    # Set conut of object on c++ side
+    Slic3r::GUI::set_object_count($obj_idx, $model_object->instances_count);
     
     # only autoarrange if user has autocentering enabled
     $self->stop_background_process;
@@ -1022,7 +1064,8 @@ sub decrease {
             $model_object->delete_last_instance;
             $self->{print}->objects->[$obj_idx]->delete_last_copy;
         }
-        $self->{list}->SetItem($obj_idx, 1, $model_object->instances_count);
+        # Set conut of object on c++ side
+        Slic3r::GUI::set_object_count($obj_idx, $model_object->instances_count);
     } elsif (defined $copies_asked) {
         # The "decrease" came from the "set number of copies" dialog.
         $self->remove;
@@ -1031,11 +1074,7 @@ sub decrease {
         $self->resume_background_process;
         return;
     }
-    
-    if ($self->{objects}[$obj_idx]) {
-        $self->{list}->Select($obj_idx, 0);
-        $self->{list}->Select($obj_idx, 1);
-    }
+
     $self->update;
     $self->schedule_background_process;
 }
@@ -1148,6 +1187,7 @@ sub rotate {
 #        $model_object->center_around_origin;
 #        $self->reset_thumbnail($obj_idx);
     }
+    Slic3r::GUI::update_rotation_value(deg2rad($angle), $axis == X ? "x" : ($axis == Y ? "y" : "z"));
     
     # update print and start background processing
     $self->{print}->add_model_object($model_object, $obj_idx);
@@ -1238,8 +1278,9 @@ sub changescale {
             $scale = $self->_get_number_from_user(L('Enter the scale % for the selected object:'), L('Scale'), L('Invalid scaling value entered'), $model_instance->scaling_factor*100, 1);
             return if ! defined($scale) || $scale eq '';
         }
-    
-        $self->{list}->SetItem($obj_idx, 2, "$scale%");
+
+        # Set object scale on c++ side
+        Slic3r::GUI::set_object_scale($obj_idx, $scale);
         $scale /= 100;  # turn percent into factor
         
         my $variation = $scale / $model_instance->scaling_factor;
@@ -1682,9 +1723,15 @@ sub on_export_completed {
 # Fill in the "Sliced info" box with the result of the G-code generator.
 sub print_info_box_show {
     my ($self, $show) = @_;
-    my $scrolled_window_panel = $self->{scrolled_window_panel}; 
-    my $scrolled_window_sizer = $self->{scrolled_window_sizer};
-    return if (!$show && ($scrolled_window_sizer->IsShown(2) == $show));
+#    my $scrolled_window_panel = $self->{scrolled_window_panel}; 
+#    my $scrolled_window_sizer = $self->{scrolled_window_sizer};
+#    return if (!$show && ($scrolled_window_sizer->IsShown(2) == $show));
+    my $panel = $self->{right_panel};
+    my $sizer = $self->{info_sizer};
+    return if (!$sizer || !$show && ($sizer->IsShown(1) == $show));
+
+    Slic3r::GUI::set_show_print_info($show);
+    return if (wxTheApp->{app_config}->get("view_mode") eq "simple");
 
     if ($show) {
         my $print_info_sizer = $self->{print_info_sizer};
@@ -1711,17 +1758,23 @@ sub print_info_box_show {
         while ( my $label = shift @info) {
             my $value = shift @info;
             next if $value eq "N/A";
-            my $text = Wx::StaticText->new($scrolled_window_panel, -1, "$label:", wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT);
+#            my $text = Wx::StaticText->new($scrolled_window_panel, -1, "$label:", wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT);
+            my $text = Wx::StaticText->new($panel, -1, "$label:", wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT);
             $text->SetFont($Slic3r::GUI::small_font);
             $grid_sizer->Add($text, 0);            
-            my $field = Wx::StaticText->new($scrolled_window_panel, -1, $value, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
+#            my $field = Wx::StaticText->new($scrolled_window_panel, -1, $value, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
+            my $field = Wx::StaticText->new($panel, -1, $value, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
             $field->SetFont($Slic3r::GUI::small_font);
             $grid_sizer->Add($field, 0);
         }
     }
 
-    $scrolled_window_sizer->Show(2, $show);
-    $scrolled_window_panel->Layout;
+#    $scrolled_window_sizer->Show(2, $show);
+#    $scrolled_window_panel->Layout;
+    $sizer->Show(1, $show);
+
+    $self->Layout;
+    $panel->Refresh;
 }
 
 sub do_print {
@@ -1930,6 +1983,24 @@ sub update {
     $self->{preview3D}->reload_print if $self->{preview3D};
 }
 
+# When a printer technology is changed, the UI needs to be updated to show/hide needed preset combo boxes.
+sub show_preset_comboboxes{
+    my ($self, $showSLA) = @_; #if showSLA is oposite value to "ptFFF"
+
+    my $choices = $self->{preset_choosers}{filament};    
+    my $print_filament_ctrls_cnt = 2 + 2 * ($#$choices+1);
+
+    foreach (0..$print_filament_ctrls_cnt-1){
+        $self->{presets_sizer}->Show($_, !$showSLA);
+    }
+    $self->{presets_sizer}->Show($print_filament_ctrls_cnt  , $showSLA);
+    $self->{presets_sizer}->Show($print_filament_ctrls_cnt+1, $showSLA);
+
+    $self->{frequently_changed_parameters_sizer}->Show(0,!$showSLA);
+
+    $self->Layout;
+}
+
 # When a number of extruders changes, the UI needs to be updated to show a single filament selection combo box per extruder.
 # Also the wxTheApp->{preset_bundle}->filament_presets needs to be resized accordingly
 # and some reasonable default has to be selected for the additional extruders.
@@ -2035,32 +2106,18 @@ sub on_config_change {
     $self->schedule_background_process;
 }
 
-sub list_item_deselected {
-    my ($self, $event) = @_;
-    return if $PreventListEvents;
-    $self->{_lecursor} = Wx::BusyCursor->new();
-    if ($self->{list}->GetFirstSelected == -1) {
-        $self->select_object(undef);
-#        $self->{canvas}->Refresh;
-        Slic3r::GUI::_3DScene::deselect_volumes($self->{canvas3D}) if $self->{canvas3D};
-        Slic3r::GUI::_3DScene::render($self->{canvas3D}) if $self->{canvas3D};
-    }
-    undef $self->{_lecursor};
-}
+sub item_changed_selection{
+    my ($self, $obj_idx) = @_;
 
-sub list_item_selected {
-    my ($self, $event) = @_;
-    return if $PreventListEvents;
-    $self->{_lecursor} = Wx::BusyCursor->new();
-    my $obj_idx = $event->GetIndex;
-    $self->select_object($obj_idx);
 #    $self->{canvas}->Refresh;
     if ($self->{canvas3D}) {
-        my $selections = $self->collect_selections;
-        Slic3r::GUI::_3DScene::update_volumes_selection($self->{canvas3D}, \@$selections);
+        Slic3r::GUI::_3DScene::deselect_volumes($self->{canvas3D});
+        if ($obj_idx >= 0){
+            my $selections = $self->collect_selections;
+            Slic3r::GUI::_3DScene::update_volumes_selection($self->{canvas3D}, \@$selections);
+        }
         Slic3r::GUI::_3DScene::render($self->{canvas3D});
     }
-    undef $self->{_lecursor};
 }
 
 sub collect_selections {
@@ -2072,6 +2129,7 @@ sub collect_selections {
     return $selections;
 }
 
+# doesn't used now
 sub list_item_activated {
     my ($self, $event, $obj_idx) = @_;
     
@@ -2172,6 +2230,31 @@ sub object_settings_dialog {
     }
 }
 
+sub changed_object_settings {
+    my ($self, $obj_idx, $parts_changed, $part_settings_changed) = @_;
+    
+    # update thumbnail since parts may have changed
+    if ($parts_changed) {
+        # recenter and re-align to Z = 0
+        my $model_object = $self->{model}->objects->[$obj_idx];
+        $model_object->center_around_origin;
+        $self->reset_thumbnail($obj_idx);
+    }
+    
+    # update print
+    if ($parts_changed || $part_settings_changed) {
+        $self->stop_background_process;
+        $self->{print}->reload_object($obj_idx);
+        $self->schedule_background_process;
+        $self->{canvas}->reload_scene if $self->{canvas};
+        my $selections = $self->collect_selections;
+        Slic3r::GUI::_3DScene::set_objects_selections($self->{canvas3D}, \@$selections);
+        Slic3r::GUI::_3DScene::reload_scene($self->{canvas3D}, 0);
+    } else {
+        $self->resume_background_process;
+    }
+}
+
 # Called to update various buttons depending on whether there are any objects or
 # whether background processing (export of a G-code, sending to Octoprint, forced background re-slicing) is active.
 sub object_list_changed {
@@ -2273,7 +2356,8 @@ sub selection_changed {
                 $self->{object_info_facets}->SetLabel(sprintf(L('%d (%d shells)'), $model_object->facets_count, $stats->{number_of_parts}));
                 if (my $errors = sum(@$stats{qw(degenerate_facets edges_fixed facets_removed facets_added facets_reversed backwards_edges)})) {
                     $self->{object_info_manifold}->SetLabel(sprintf(L("Auto-repaired (%d errors)"), $errors));
-                    $self->{object_info_manifold_warning_icon}->Show;
+                    #$self->{object_info_manifold_warning_icon}->Show;
+                    $self->{"object_info_manifold_warning_icon_show"}->(1);
                     
                     # we don't show normals_fixed because we never provide normals
 	                # to admesh, so it generates normals for all facets
@@ -2283,7 +2367,8 @@ sub selection_changed {
                     $self->{object_info_manifold_warning_icon}->SetToolTipString($message);
                 } else {
                     $self->{object_info_manifold}->SetLabel(L("Yes"));
-                    $self->{object_info_manifold_warning_icon}->Hide;
+                    #$self->{object_info_manifold_warning_icon}->Hide;
+                    $self->{"object_info_manifold_warning_icon_show"}->(0);
                     $self->{object_info_manifold}->SetToolTipString("");
                     $self->{object_info_manifold_warning_icon}->SetToolTipString("");
                 }
@@ -2292,7 +2377,8 @@ sub selection_changed {
             }
         } else {
             $self->{"object_info_$_"}->SetLabel("") for qw(size volume facets materials manifold);
-            $self->{object_info_manifold_warning_icon}->Hide;
+            #$self->{object_info_manifold_warning_icon}->Hide;
+            $self->{"object_info_manifold_warning_icon_show"}->(0);
             $self->{object_info_manifold}->SetToolTipString("");
             $self->{object_info_manifold_warning_icon}->SetToolTipString("");
         }
@@ -2305,26 +2391,21 @@ sub selection_changed {
 }
 
 sub select_object {
-    my ($self, $obj_idx) = @_;
+    my ($self, $obj_idx, $child) = @_;
 
     # remove current selection
     foreach my $o (0..$#{$self->{objects}}) {
-        $PreventListEvents = 1;
         $self->{objects}->[$o]->selected(0);
-        $self->{list}->Select($o, 0);
-        $PreventListEvents = 0;
     }
-    
+
     if (defined $obj_idx) {
         $self->{objects}->[$obj_idx]->selected(1);
-        # We use this flag to avoid circular event handling
-        # Select() happens to fire a wxEVT_LIST_ITEM_SELECTED on Windows, 
-        # whose event handler calls this method again and again and again
-        $PreventListEvents = 1;
-        $self->{list}->Select($obj_idx, 1);
-        $PreventListEvents = 0;
+        # Select current object in the list on c++ side, if item isn't child
+        if (!defined $child){
+            Slic3r::GUI::select_current_object($obj_idx);}
     } else {
-        # TODO: deselect all in list
+        # Unselect all objects in the list on c++ side
+        Slic3r::GUI::unselect_objects();
     }
     $self->selection_changed(1);
 }
diff --git a/resources/icons/add_object.png b/resources/icons/add_object.png
new file mode 100644
index 000000000..ffc958edc
Binary files /dev/null and b/resources/icons/add_object.png differ
diff --git a/resources/icons/colorchange_add_off.png b/resources/icons/colorchange_add_off.png
new file mode 100644
index 000000000..6ddeccbe0
Binary files /dev/null and b/resources/icons/colorchange_add_off.png differ
diff --git a/resources/icons/colorchange_add_on.png b/resources/icons/colorchange_add_on.png
new file mode 100644
index 000000000..cc800b81e
Binary files /dev/null and b/resources/icons/colorchange_add_on.png differ
diff --git a/resources/icons/colorchange_delete_off.png b/resources/icons/colorchange_delete_off.png
new file mode 100644
index 000000000..c16655271
Binary files /dev/null and b/resources/icons/colorchange_delete_off.png differ
diff --git a/resources/icons/colorchange_delete_on.png b/resources/icons/colorchange_delete_on.png
new file mode 100644
index 000000000..8f27ce9fe
Binary files /dev/null and b/resources/icons/colorchange_delete_on.png differ
diff --git a/resources/icons/disclosure_triangle_close.png b/resources/icons/disclosure_triangle_close.png
new file mode 100644
index 000000000..0660422c7
Binary files /dev/null and b/resources/icons/disclosure_triangle_close.png differ
diff --git a/resources/icons/disclosure_triangle_open.png b/resources/icons/disclosure_triangle_open.png
new file mode 100644
index 000000000..81112f2a2
Binary files /dev/null and b/resources/icons/disclosure_triangle_open.png differ
diff --git a/resources/icons/down_half_circle.png b/resources/icons/down_half_circle.png
new file mode 100644
index 000000000..84aba5c3c
Binary files /dev/null and b/resources/icons/down_half_circle.png differ
diff --git a/resources/icons/erase.png b/resources/icons/erase.png
new file mode 100644
index 000000000..4c4cfd755
Binary files /dev/null and b/resources/icons/erase.png differ
diff --git a/resources/icons/exclamation_mark_.png b/resources/icons/exclamation_mark_.png
new file mode 100644
index 000000000..5fe7c1355
Binary files /dev/null and b/resources/icons/exclamation_mark_.png differ
diff --git a/resources/icons/lambda.png b/resources/icons/lambda.png
new file mode 100644
index 000000000..3be73b142
Binary files /dev/null and b/resources/icons/lambda.png differ
diff --git a/resources/icons/lambda_.png b/resources/icons/lambda_.png
new file mode 100644
index 000000000..8e9d2b0ea
Binary files /dev/null and b/resources/icons/lambda_.png differ
diff --git a/resources/icons/left_half_circle.png b/resources/icons/left_half_circle.png
new file mode 100644
index 000000000..c1e8d3a9c
Binary files /dev/null and b/resources/icons/left_half_circle.png differ
diff --git a/resources/icons/object.png b/resources/icons/object.png
new file mode 100644
index 000000000..c85cbaf2f
Binary files /dev/null and b/resources/icons/object.png differ
diff --git a/resources/icons/one_layer_lock_off.png b/resources/icons/one_layer_lock_off.png
new file mode 100644
index 000000000..7dc8b0611
Binary files /dev/null and b/resources/icons/one_layer_lock_off.png differ
diff --git a/resources/icons/one_layer_lock_on.png b/resources/icons/one_layer_lock_on.png
new file mode 100644
index 000000000..41b7ff173
Binary files /dev/null and b/resources/icons/one_layer_lock_on.png differ
diff --git a/resources/icons/one_layer_unlock_off.png b/resources/icons/one_layer_unlock_off.png
new file mode 100644
index 000000000..c3931f8b9
Binary files /dev/null and b/resources/icons/one_layer_unlock_off.png differ
diff --git a/resources/icons/one_layer_unlock_on.png b/resources/icons/one_layer_unlock_on.png
new file mode 100644
index 000000000..99e0db5c2
Binary files /dev/null and b/resources/icons/one_layer_unlock_on.png differ
diff --git a/resources/icons/right_half_circle.png b/resources/icons/right_half_circle.png
new file mode 100644
index 000000000..9f182b0b4
Binary files /dev/null and b/resources/icons/right_half_circle.png differ
diff --git a/resources/icons/split.png b/resources/icons/split.png
new file mode 100644
index 000000000..c5680ab91
Binary files /dev/null and b/resources/icons/split.png differ
diff --git a/resources/icons/up_half_circle.png b/resources/icons/up_half_circle.png
new file mode 100644
index 000000000..9eea07fda
Binary files /dev/null and b/resources/icons/up_half_circle.png differ
diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt
index fe33514a9..0d1f1bf13 100644
--- a/xs/CMakeLists.txt
+++ b/xs/CMakeLists.txt
@@ -211,6 +211,10 @@ add_library(libslic3r_gui STATIC
     ${LIBDIR}/slic3r/GUI/PresetHints.hpp
     ${LIBDIR}/slic3r/GUI/GUI.cpp
     ${LIBDIR}/slic3r/GUI/GUI.hpp
+    ${LIBDIR}/slic3r/GUI/GUI_ObjectParts.cpp
+    ${LIBDIR}/slic3r/GUI/GUI_ObjectParts.hpp
+    ${LIBDIR}/slic3r/GUI/LambdaObjectDialog.cpp
+    ${LIBDIR}/slic3r/GUI/LambdaObjectDialog.hpp
     ${LIBDIR}/slic3r/GUI/Tab.cpp
     ${LIBDIR}/slic3r/GUI/Tab.hpp
     ${LIBDIR}/slic3r/GUI/TabIface.cpp
diff --git a/xs/src/libslic3r/Config.hpp b/xs/src/libslic3r/Config.hpp
index 7f75f815d..bd98e060a 100644
--- a/xs/src/libslic3r/Config.hpp
+++ b/xs/src/libslic3r/Config.hpp
@@ -49,9 +49,9 @@ enum ConfigOptionType {
     coPercents      = coPercent + coVectorType,
     // a fraction or an absolute value
     coFloatOrPercent = 5,
-    // single 2d point. Currently not used.
+    // single 2d point (Point2f). Currently not used.
     coPoint         = 6,
-    // vector of 2d points. Currently used for the definition of the print bed and for the extruder offsets.
+    // vector of 2d points (Point2f). Currently used for the definition of the print bed and for the extruder offsets.
     coPoints        = coPoint + coVectorType,
     // single boolean value
     coBool          = 7,
@@ -821,12 +821,7 @@ public:
     bool deserialize(const std::string &str, bool append = false) override
     {
         UNUSED(append);
-        const t_config_enum_values &enum_keys_map = ConfigOptionEnum<T>::get_enum_values();
-        auto it = enum_keys_map.find(str);
-        if (it == enum_keys_map.end())
-            return false;
-        this->value = static_cast<T>(it->second);
-        return true;
+        return from_string(str, this->value);
     }
 
     static bool has(T value) 
@@ -838,7 +833,7 @@ public:
     }
 
     // Map from an enum name to an enum integer value.
-    static t_config_enum_names& get_enum_names() 
+    static const t_config_enum_names& get_enum_names() 
     {
         static t_config_enum_names names;
         if (names.empty()) {
@@ -855,7 +850,17 @@ public:
         return names;
     }
     // Map from an enum name to an enum integer value.
-    static t_config_enum_values& get_enum_values();
+    static const t_config_enum_values& get_enum_values();
+
+    static bool from_string(const std::string &str, T &value)
+    {
+        const t_config_enum_values &enum_keys_map = ConfigOptionEnum<T>::get_enum_values();
+        auto it = enum_keys_map.find(str);
+        if (it == enum_keys_map.end())
+            return false;
+        value = static_cast<T>(it->second);
+        return true;
+    }
 };
 
 // Generic enum configuration value.
@@ -900,7 +905,7 @@ public:
     // What type? bool, int, string etc.
     ConfigOptionType                    type            = coNone;
     // Default value of this option. The default value object is owned by ConfigDef, it is released in its destructor.
-    ConfigOption                       *default_value   = nullptr;
+    const ConfigOption                 *default_value   = nullptr;
 
     // Usually empty. 
     // Special values - "i_enum_open", "f_enum_open" to provide combo box for int or float selection,
@@ -958,7 +963,7 @@ public:
     std::vector<std::string>            enum_labels;
     // For enums (when type == coEnum). Maps enum_values to enums.
     // Initialized by ConfigOptionEnum<xxx>::get_enum_values()
-    t_config_enum_values               *enum_keys_map   = nullptr;
+    const t_config_enum_values         *enum_keys_map   = nullptr;
 
     bool has_enum_value(const std::string &value) const {
         for (const std::string &v : enum_values)
diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp
index 748e1fb72..0df1d6466 100644
--- a/xs/src/libslic3r/PrintConfig.cpp
+++ b/xs/src/libslic3r/PrintConfig.cpp
@@ -17,9 +17,51 @@ namespace Slic3r {
 #define L(s) Slic3r::I18N::translate(s)
 
 PrintConfigDef::PrintConfigDef()
+{
+    this->init_common_params();
+    this->init_fff_params();
+    this->init_sla_params();
+}
+
+void PrintConfigDef::init_common_params()
 {
     t_optiondef_map &Options = this->options;
+    ConfigOptionDef* def;
+
+    def = this->add("printer_technology", coEnum);
+    def->label = L("Printer technology");
+    def->tooltip = L("Printer technology");
+    def->cli = "printer-technology=s";
+    def->enum_keys_map = &ConfigOptionEnum<PrinterTechnology>::get_enum_values();
+    def->enum_values.push_back("FFF");
+    def->enum_values.push_back("SLA");
+    def->default_value = new ConfigOptionEnum<PrinterTechnology>(ptFFF);
+
+    def = this->add("bed_shape", coPoints);
+    def->label = L("Bed shape");
+    def->default_value = new ConfigOptionPoints{ Vec2d(0, 0), Vec2d(200, 0), Vec2d(200, 200), Vec2d(0, 200) };
     
+    def = this->add("layer_height", coFloat);
+    def->label = L("Layer height");
+    def->category = L("Layers and Perimeters");
+    def->tooltip = L("This setting controls the height (and thus the total number) of the slices/layers. "
+                   "Thinner layers give better accuracy but take more time to print.");
+    def->sidetext = L("mm");
+    def->cli = "layer-height=f";
+    def->min = 0;
+    def->default_value = new ConfigOptionFloat(0.3);
+
+    def = this->add("max_print_height", coFloat);
+    def->label = L("Max print height");
+    def->tooltip = L("Set this to the maximum height that can be reached by your extruder while printing.");
+    def->sidetext = L("mm");
+    def->cli = "max-print-height=f";
+    def->default_value = new ConfigOptionFloat(200.0);
+}
+
+void PrintConfigDef::init_fff_params()
+{
+    t_optiondef_map &Options = this->options;
     ConfigOptionDef* def;
 
     // Maximum extruder temperature, bumped to 1500 to support printing of glass.
@@ -33,10 +75,6 @@ PrintConfigDef::PrintConfigDef()
     def->cli = "avoid-crossing-perimeters!";
     def->default_value = new ConfigOptionBool(false);
 
-    def = this->add("bed_shape", coPoints);
-	def->label = L("Bed shape");
-    def->default_value = new ConfigOptionPoints { Vec2d(0,0), Vec2d(200,0), Vec2d(200,200), Vec2d(0,200) };
-    
     def = this->add("bed_temperature", coInts);
     def->label = L("Other layers");
     def->tooltip = L("Bed temperature for layers after the first one. "
@@ -922,16 +960,6 @@ PrintConfigDef::PrintConfigDef()
     def->height = 50;
     def->default_value = new ConfigOptionString("");
 
-    def = this->add("layer_height", coFloat);
-    def->label = L("Layer height");
-    def->category = L("Layers and Perimeters");
-    def->tooltip = L("This setting controls the height (and thus the total number) of the slices/layers. "
-                   "Thinner layers give better accuracy but take more time to print.");
-    def->sidetext = L("mm");
-    def->cli = "layer-height=f";
-    def->min = 0;
-    def->default_value = new ConfigOptionFloat(0.3);
-
     def = this->add("remaining_times", coBool);
     def->label = L("Supports remaining times");
     def->tooltip = L("Emit M73 P[percent printed] R[remaining time in minutes] at 1 minute"
@@ -1052,13 +1080,6 @@ PrintConfigDef::PrintConfigDef()
     def->min = 0;
     def->default_value = new ConfigOptionFloats { 0. };
 
-    def = this->add("max_print_height", coFloat);
-    def->label = L("Max print height");
-    def->tooltip = L("Set this to the maximum height that can be reached by your extruder while printing.");
-    def->sidetext = L("mm");
-    def->cli = "max-print-height=f";
-    def->default_value = new ConfigOptionFloat(200.0);
-
     def = this->add("max_print_speed", coFloat);
     def->label = L("Max print speed");
     def->tooltip = L("When setting other speed settings to 0 Slic3r will autocalculate the optimal speed "
@@ -2103,6 +2124,103 @@ PrintConfigDef::PrintConfigDef()
     def->default_value = new ConfigOptionFloat(0);
 }
 
+void PrintConfigDef::init_sla_params()
+{
+    t_optiondef_map &Options = this->options;    
+    ConfigOptionDef* def;
+
+    // SLA Printer settings
+    def = this->add("display_width", coFloat);
+    def->label = L("Display width");
+    def->tooltip = L("Width of the display");
+    def->cli = "display-width=f";
+    def->min = 1;
+    def->default_value = new ConfigOptionFloat(150.);
+
+    def = this->add("display_height", coFloat);
+    def->label = L("Display height");
+    def->tooltip = L("Height of the display");
+    def->cli = "display-height=f";
+    def->min = 1;
+    def->default_value = new ConfigOptionFloat(100.);
+
+    def = this->add("display_pixels_x", coInt);
+    def->full_label = L("Number of pixels in");
+    def->label = ("X");
+    def->tooltip = L("Number of pixels in X");
+    def->cli = "display-pixels-x=i";
+    def->min = 100;
+    def->default_value = new ConfigOptionInt(2000);
+
+    def = this->add("display_pixels_y", coInt);
+    def->label = ("Y");
+    def->tooltip = L("Number of pixels in Y");
+    def->cli = "display-pixels-y=i";
+    def->min = 100;
+    def->default_value = new ConfigOptionInt(1000);
+
+    def = this->add("printer_correction", coFloats);
+    def->full_label = L("Printer scaling correction");
+    def->tooltip  = L("Printer scaling correction");
+    def->min = 0;
+    def->default_value = new ConfigOptionFloats( { 1., 1., 1. } );
+
+    // SLA Material settings.
+    def = this->add("initial_layer_height", coFloat);
+    def->label = L("Initial layer height");
+    def->tooltip = L("Initial layer height");
+    def->sidetext = L("mm");
+    def->cli = "initial-layer-height=f";
+    def->min = 0;
+    def->default_value = new ConfigOptionFloat(0.3);
+
+    def = this->add("exposure_time", coFloat);
+    def->label = L("Exposure time");
+    def->tooltip = L("Exposure time");
+    def->sidetext = L("s");
+    def->cli = "exposure-time=f";
+    def->min = 0;
+    def->default_value = new ConfigOptionFloat(10);
+
+    def = this->add("initial_exposure_time", coFloat);
+    def->label = L("Initial exposure time");
+    def->tooltip = L("Initial exposure time");
+    def->sidetext = L("s");
+    def->cli = "initial-exposure-time=f";
+    def->min = 0;
+    def->default_value = new ConfigOptionFloat(15);
+
+    def = this->add("material_correction_printing", coFloats);
+    def->full_label = L("Correction for expansion when printing");
+    def->tooltip  = L("Correction for expansion when printing");
+    def->min = 0;
+    def->default_value = new ConfigOptionFloats( { 1. , 1., 1. } );
+
+    def = this->add("material_correction_curing", coFloats);
+    def->full_label = L("Correction for expansion after curing");
+    def->tooltip  = L("Correction for expansion after curing");
+    def->min = 0;
+    def->default_value = new ConfigOptionFloats( { 1. , 1., 1. } );
+
+    def = this->add("material_notes", coString);
+    def->label = L("SLA print material notes");
+    def->tooltip = L("You can put your notes regarding the SLA print material here.");
+    def->cli = "material-notes=s";
+    def->multiline = true;
+    def->full_width = true;
+    def->height = 130;
+    def->default_value = new ConfigOptionString("");
+
+    def = this->add("default_sla_material_profile", coString);
+    def->label = L("Default SLA material profile");
+    def->tooltip = L("Default print profile associated with the current printer profile. "
+                   "On selection of the current printer profile, this print profile will be activated.");
+    def->default_value = new ConfigOptionString();
+
+    def = this->add("sla_material_settings_id", coString);
+    def->default_value = new ConfigOptionString("");
+}
+
 void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &value)
 {
     // handle legacy options
@@ -2426,4 +2544,8 @@ StaticPrintConfig::StaticCache<class Slic3r::PrintConfig>       PrintConfig::s_c
 StaticPrintConfig::StaticCache<class Slic3r::HostConfig>        HostConfig::s_cache_HostConfig;
 StaticPrintConfig::StaticCache<class Slic3r::FullPrintConfig>   FullPrintConfig::s_cache_FullPrintConfig;
 
+StaticPrintConfig::StaticCache<class Slic3r::SLAMaterialConfig>  SLAMaterialConfig::s_cache_SLAMaterialConfig;
+StaticPrintConfig::StaticCache<class Slic3r::SLAPrinterConfig>   SLAPrinterConfig::s_cache_SLAPrinterConfig;
+StaticPrintConfig::StaticCache<class Slic3r::SLAFullPrintConfig> SLAFullPrintConfig::s_cache_SLAFullPrintConfig;
+
 }
diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp
index 932ed6054..004228de7 100644
--- a/xs/src/libslic3r/PrintConfig.hpp
+++ b/xs/src/libslic3r/PrintConfig.hpp
@@ -22,6 +22,14 @@
 
 namespace Slic3r {
 
+enum PrinterTechnology
+{
+    // Fused Filament Fabrication
+    ptFFF,
+    // Stereolitography
+    ptSLA,
+};
+
 enum GCodeFlavor {
     gcfRepRap, gcfRepetier, gcfTeacup, gcfMakerWare, gcfMarlin, gcfSailfish, gcfMach3, gcfMachinekit, 
     gcfSmoothie, gcfNoExtrusion,
@@ -48,7 +56,16 @@ enum FilamentType {
     ftPLA, ftABS, ftPET, ftHIPS, ftFLEX, ftSCAFF, ftEDGE, ftNGEN, ftPVA
 };
 
-template<> inline t_config_enum_values& ConfigOptionEnum<GCodeFlavor>::get_enum_values() {
+template<> inline const t_config_enum_values& ConfigOptionEnum<PrinterTechnology>::get_enum_values() {
+    static t_config_enum_values keys_map;
+    if (keys_map.empty()) {
+        keys_map["FFF"]             = ptFFF;
+        keys_map["SLA"]             = ptSLA;
+    }
+    return keys_map;
+}
+
+template<> inline const t_config_enum_values& ConfigOptionEnum<GCodeFlavor>::get_enum_values() {
     static t_config_enum_values keys_map;
     if (keys_map.empty()) {
         keys_map["reprap"]          = gcfRepRap;
@@ -65,7 +82,7 @@ template<> inline t_config_enum_values& ConfigOptionEnum<GCodeFlavor>::get_enum_
     return keys_map;
 }
 
-template<> inline t_config_enum_values& ConfigOptionEnum<PrintHostType>::get_enum_values() {
+template<> inline const t_config_enum_values& ConfigOptionEnum<PrintHostType>::get_enum_values() {
     static t_config_enum_values keys_map;
     if (keys_map.empty()) {
         keys_map["octoprint"]       = htOctoPrint;
@@ -74,7 +91,7 @@ template<> inline t_config_enum_values& ConfigOptionEnum<PrintHostType>::get_enu
     return keys_map;
 }
 
-template<> inline t_config_enum_values& ConfigOptionEnum<InfillPattern>::get_enum_values() {
+template<> inline const t_config_enum_values& ConfigOptionEnum<InfillPattern>::get_enum_values() {
     static t_config_enum_values keys_map;
     if (keys_map.empty()) {
         keys_map["rectilinear"]         = ipRectilinear;
@@ -94,7 +111,7 @@ template<> inline t_config_enum_values& ConfigOptionEnum<InfillPattern>::get_enu
     return keys_map;
 }
 
-template<> inline t_config_enum_values& ConfigOptionEnum<SupportMaterialPattern>::get_enum_values() {
+template<> inline const t_config_enum_values& ConfigOptionEnum<SupportMaterialPattern>::get_enum_values() {
     static t_config_enum_values keys_map;
     if (keys_map.empty()) {
         keys_map["rectilinear"]         = smpRectilinear;
@@ -104,7 +121,7 @@ template<> inline t_config_enum_values& ConfigOptionEnum<SupportMaterialPattern>
     return keys_map;
 }
 
-template<> inline t_config_enum_values& ConfigOptionEnum<SeamPosition>::get_enum_values() {
+template<> inline const t_config_enum_values& ConfigOptionEnum<SeamPosition>::get_enum_values() {
     static t_config_enum_values keys_map;
     if (keys_map.empty()) {
         keys_map["random"]              = spRandom;
@@ -115,7 +132,7 @@ template<> inline t_config_enum_values& ConfigOptionEnum<SeamPosition>::get_enum
     return keys_map;
 }
 
-template<> inline t_config_enum_values& ConfigOptionEnum<FilamentType>::get_enum_values() {
+template<> inline const t_config_enum_values& ConfigOptionEnum<FilamentType>::get_enum_values() {
     static t_config_enum_values keys_map;
     if (keys_map.empty()) {
         keys_map["PLA"]             = ftPLA;
@@ -139,6 +156,11 @@ public:
     PrintConfigDef();
 
     static void handle_legacy(t_config_option_key &opt_key, std::string &value);
+
+private:
+    void init_common_params();
+    void init_fff_params();
+    void init_sla_params();
 };
 
 // The one and only global definition of SLic3r configuration options.
@@ -851,6 +873,73 @@ protected:
     }
 };
 
+class SLAMaterialConfig : public StaticPrintConfig
+{
+    STATIC_PRINT_CONFIG_CACHE(SLAMaterialConfig)
+public:
+    ConfigOptionFloat                       layer_height;
+    ConfigOptionFloat                       initial_layer_height;
+    ConfigOptionFloat                       exposure_time;
+    ConfigOptionFloat                       initial_exposure_time;
+    ConfigOptionFloats                      material_correction_printing;
+    ConfigOptionFloats                      material_correction_curing;
+protected:
+    void initialize(StaticCacheBase &cache, const char *base_ptr)
+    {
+        OPT_PTR(layer_height);
+        OPT_PTR(initial_layer_height);
+        OPT_PTR(exposure_time);
+        OPT_PTR(initial_exposure_time);
+        OPT_PTR(material_correction_printing);
+        OPT_PTR(material_correction_curing);
+    }
+};
+
+class SLAPrinterConfig : public StaticPrintConfig
+{
+    STATIC_PRINT_CONFIG_CACHE(SLAPrinterConfig)
+public:
+    ConfigOptionEnum<PrinterTechnology>     printer_technology;
+    ConfigOptionPoints                      bed_shape;
+    ConfigOptionFloat                       max_print_height;
+    ConfigOptionFloat                       display_width;
+    ConfigOptionFloat                       display_height;
+    ConfigOptionInt                         display_pixels_x;
+    ConfigOptionInt                         display_pixels_y;
+    ConfigOptionFloats                      printer_correction;
+protected:
+    void initialize(StaticCacheBase &cache, const char *base_ptr)
+    {
+        OPT_PTR(printer_technology);
+        OPT_PTR(bed_shape);
+        OPT_PTR(max_print_height);
+        OPT_PTR(display_width);
+        OPT_PTR(display_height);
+        OPT_PTR(display_pixels_x);
+        OPT_PTR(display_pixels_y);
+        OPT_PTR(printer_correction);
+    }
+};
+
+class SLAFullPrintConfig : public SLAPrinterConfig, public SLAMaterialConfig
+{
+    STATIC_PRINT_CONFIG_CACHE_DERIVED(SLAFullPrintConfig)
+    SLAFullPrintConfig() : SLAPrinterConfig(0), SLAMaterialConfig(0) { initialize_cache(); *this = s_cache_SLAFullPrintConfig.defaults(); }
+
+public:
+    // Validate the SLAFullPrintConfig. Returns an empty string on success, otherwise an error message is returned.
+//    std::string                 validate();
+
+protected:
+    // Protected constructor to be called to initialize ConfigCache::m_default.
+    SLAFullPrintConfig(int) : SLAPrinterConfig(0), SLAMaterialConfig(0) {}
+    void initialize(StaticCacheBase &cache, const char *base_ptr)
+    {
+        this->SLAPrinterConfig ::initialize(cache, base_ptr);
+        this->SLAMaterialConfig::initialize(cache, base_ptr);
+    }
+};
+
 #undef STATIC_PRINT_CONFIG_CACHE
 #undef STATIC_PRINT_CONFIG_CACHE_BASE
 #undef STATIC_PRINT_CONFIG_CACHE_DERIVED
diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp
index 2d38d78b3..c9607ccf9 100644
--- a/xs/src/slic3r/GUI/3DScene.cpp
+++ b/xs/src/slic3r/GUI/3DScene.cpp
@@ -258,6 +258,11 @@ const Vec3d& GLVolume::get_origin() const
     return m_origin;
 }
 
+float GLVolume::get_angle_z()
+{
+    return m_angle_z;
+}
+
 void GLVolume::set_origin(const Vec3d& origin)
 {
     if (m_origin != origin)
diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp
index da86c458b..b4aa6ce95 100644
--- a/xs/src/slic3r/GUI/3DScene.hpp
+++ b/xs/src/slic3r/GUI/3DScene.hpp
@@ -323,6 +323,7 @@ public:
     // Sets render color in dependence of current state
     void set_render_color();
 
+    float get_angle_z();
     const Vec3d& get_origin() const;
     void set_origin(const Vec3d& origin);
     void set_angle_z(float angle_z);
diff --git a/xs/src/slic3r/GUI/AppConfig.cpp b/xs/src/slic3r/GUI/AppConfig.cpp
index 2a33cd733..0f77b1953 100644
--- a/xs/src/slic3r/GUI/AppConfig.cpp
+++ b/xs/src/slic3r/GUI/AppConfig.cpp
@@ -233,6 +233,7 @@ void AppConfig::reset_selections()
     if (it != m_storage.end()) {
         it->second.erase("print");
         it->second.erase("filament");
+        it->second.erase("sla_material");
         it->second.erase("printer");
         m_dirty = true;
     }
diff --git a/xs/src/slic3r/GUI/Field.cpp b/xs/src/slic3r/GUI/Field.cpp
index 3c7c817a6..4043a4457 100644
--- a/xs/src/slic3r/GUI/Field.cpp
+++ b/xs/src/slic3r/GUI/Field.cpp
@@ -132,7 +132,11 @@ namespace Slic3r { namespace GUI {
 				break;
 			}
 			double val;
-			str.ToCDouble(&val);
+			if(!str.ToCDouble(&val))
+			{
+				show_error(m_parent, _(L("Input value contains incorrect symbol(s).\nUse, please, only digits")));
+				set_value(double_to_string(val), true);
+			}
 			if (m_opt.min > val || val > m_opt.max)
 			{
 				show_error(m_parent, _(L("Input value is out of range")));
@@ -274,7 +278,7 @@ void CheckBox::BUILD() {
 
 	bool check_value =	m_opt.type == coBool ? 
 						m_opt.default_value->getBool() : m_opt.type == coBools ? 
-						static_cast<ConfigOptionBools*>(m_opt.default_value)->get_at(m_opt_idx) : 
+						static_cast<const ConfigOptionBools*>(m_opt.default_value)->get_at(m_opt_idx) : 
     					false;
 
 	auto temp = new wxCheckBox(m_parent, wxID_ANY, wxString(""), wxDefaultPosition, size); 
@@ -330,9 +334,7 @@ void SpinCtrl::BUILD() {
 		break;
 	}
 
-	const int min_val = m_opt_id == "standby_temperature_delta" ? 
-						-500 : m_opt.min > 0 ? 
-						m_opt.min : 0;
+    const int min_val = m_opt.min == INT_MIN ? 0: m_opt.min;
 	const int max_val = m_opt.max < 2147483647 ? m_opt.max : 2147483647;
 
 	auto temp = new wxSpinCtrl(m_parent, wxID_ANY, text_value, wxDefaultPosition, size,
@@ -558,8 +560,11 @@ boost::any& Choice::get_value()
 // 	boost::any m_value;
 	wxString ret_str = static_cast<wxComboBox*>(window)->GetValue();	
 
-	if (m_opt_id == "support")
-		return m_value = boost::any(ret_str);//ret_str;
+	// options from right panel
+	std::vector <std::string> right_panel_options{ "support", "scale_unit" };
+	for (auto rp_option: right_panel_options)
+		if (m_opt_id == rp_option)
+			return m_value = boost::any(ret_str);
 
 	if (m_opt.type != coEnum)
 		/*m_value = */get_value_by_opt_type(ret_str);
@@ -599,7 +604,7 @@ void ColourPicker::BUILD()
 	if (m_opt.height >= 0) size.SetHeight(m_opt.height);
 	if (m_opt.width >= 0) size.SetWidth(m_opt.width);
 
-	wxString clr(static_cast<ConfigOptionStrings*>(m_opt.default_value)->get_at(m_opt_idx));
+	wxString clr(static_cast<const ConfigOptionStrings*>(m_opt.default_value)->get_at(m_opt_idx));
 	auto temp = new wxColourPickerCtrl(m_parent, wxID_ANY, clr, wxDefaultPosition, size);
 		
 	// 	// recast as a wxWindow to fit the calling convention
@@ -631,7 +636,7 @@ void PointCtrl::BUILD()
 	// 
 	wxSize field_size(40, -1);
 
-	auto default_pt = static_cast<ConfigOptionPoints*>(m_opt.default_value)->values.at(0);
+	auto default_pt = static_cast<const ConfigOptionPoints*>(m_opt.default_value)->values.at(0);
 	double val = default_pt(0);
 	wxString X = val - int(val) == 0 ? wxString::Format(_T("%i"), int(val)) : wxNumberFormatter::ToString(val, 2, wxNumberFormatter::Style_None);
 	val = default_pt(1);
@@ -695,7 +700,7 @@ void StaticText::BUILD()
 	if (m_opt.height >= 0) size.SetHeight(m_opt.height);
 	if (m_opt.width >= 0) size.SetWidth(m_opt.width);
 
-	wxString legend(static_cast<ConfigOptionString*>(m_opt.default_value)->value);
+	wxString legend(static_cast<const ConfigOptionString*>(m_opt.default_value)->value);
 	auto temp = new wxStaticText(m_parent, wxID_ANY, legend, wxDefaultPosition, size);
 	temp->SetFont(bold_font());
 
@@ -705,6 +710,69 @@ void StaticText::BUILD()
 	temp->SetToolTip(get_tooltip_text(legend));
 }
 
+void SliderCtrl::BUILD()
+{
+	auto size = wxSize(wxDefaultSize);
+	if (m_opt.height >= 0) size.SetHeight(m_opt.height);
+	if (m_opt.width >= 0) size.SetWidth(m_opt.width);
+
+	auto temp = new wxBoxSizer(wxHORIZONTAL);
+
+	auto def_val = static_cast<const ConfigOptionInt*>(m_opt.default_value)->value;
+	auto min = m_opt.min == INT_MIN ? 0 : m_opt.min;
+	auto max = m_opt.max == INT_MAX ? 100 : m_opt.max;
+
+	m_slider = new wxSlider(m_parent, wxID_ANY, def_val * m_scale,
+							min * m_scale, max * m_scale,
+							wxDefaultPosition, size);
+ 	wxSize field_size(40, -1);
+
+	m_textctrl = new wxTextCtrl(m_parent, wxID_ANY, wxString::Format("%d", m_slider->GetValue()/m_scale), 
+								wxDefaultPosition, field_size);
+
+	temp->Add(m_slider, 1, wxEXPAND | wxALIGN_CENTER_VERTICAL, 0);
+	temp->Add(m_textctrl, 0, wxALIGN_CENTER_VERTICAL, 0);
+
+	m_slider->Bind(wxEVT_SLIDER, ([this](wxCommandEvent e) {
+		if (!m_disable_change_event){
+			int val = boost::any_cast<int>(get_value());
+			m_textctrl->SetLabel(wxString::Format("%d", val));
+			on_change_field();
+		}
+	}), m_slider->GetId());
+
+	m_textctrl->Bind(wxEVT_TEXT, ([this](wxCommandEvent e) {
+		std::string value = e.GetString().utf8_str().data();
+		if (is_matched(value, "^-?\\d+(\\.\\d*)?$")){
+			m_disable_change_event = true;
+			m_slider->SetValue(stoi(value)*m_scale);
+			m_disable_change_event = false;
+			on_change_field();
+		}
+	}), m_textctrl->GetId());
+
+	m_sizer = dynamic_cast<wxSizer*>(temp);
+}
+
+void SliderCtrl::set_value(const boost::any& value, bool change_event)
+{
+	m_disable_change_event = !change_event;
+
+	m_slider->SetValue(boost::any_cast<int>(value)*m_scale);
+	int val = boost::any_cast<int>(get_value());
+	m_textctrl->SetLabel(wxString::Format("%d", val));
+
+	m_disable_change_event = false;
+}
+
+boost::any& SliderCtrl::get_value()
+{
+// 	int ret_val;
+// 	x_textctrl->GetValue().ToDouble(&val);
+	return m_value = int(m_slider->GetValue()/m_scale);
+}
+
+
 } // GUI
 } // Slic3r
 
diff --git a/xs/src/slic3r/GUI/Field.hpp b/xs/src/slic3r/GUI/Field.hpp
index e19c47d68..e305f1e53 100644
--- a/xs/src/slic3r/GUI/Field.hpp
+++ b/xs/src/slic3r/GUI/Field.hpp
@@ -38,6 +38,7 @@ wxString double_to_string(double const value);
 
 class MyButton : public wxButton
 {
+    bool hidden = false; // never show button if it's hidden ones
 public:
 	MyButton() {}
 	MyButton(wxWindow* parent, wxWindowID id, const wxString& label = wxEmptyString,
@@ -52,6 +53,12 @@ public:
 	// overridden from wxWindow base class
 	virtual bool
 		AcceptsFocusFromKeyboard() const { return false; }
+
+    virtual bool Show(bool show = true) override {
+        if (!show)
+            hidden = true;
+        return wxButton::Show(!hidden);
+	}
 };
 
 class Field {
@@ -189,6 +196,10 @@ public:
 		return false;
 	}
 
+	void	set_side_text_ptr(wxStaticText* side_text) {
+		m_side_text = side_text;
+    }
+
 protected:
 	MyButton*			m_Undo_btn = nullptr;
 	// Bitmap and Tooltip text for m_Undo_btn. The wxButton will be updated only if the new wxBitmap pointer differs from the currently rendered one.
@@ -203,6 +214,8 @@ protected:
 	// Color for Label. The wxColour will be updated only if the new wxColour pointer differs from the currently rendered one.
 	const wxColour*		m_label_color = nullptr;
 
+	wxStaticText*		m_side_text = nullptr;
+
 	// current value
 	boost::any			m_value;
 
@@ -214,7 +227,7 @@ protected:
 inline bool is_bad_field(const t_field& obj) { return obj->getSizer() == nullptr && obj->getWindow() == nullptr; }
 
 /// Covenience function to determine whether this field is a valid window field.
-inline bool is_window_field(const t_field& obj) { return !is_bad_field(obj) && obj->getWindow() != nullptr; }
+inline bool is_window_field(const t_field& obj) { return !is_bad_field(obj) && obj->getWindow() != nullptr && obj->getSizer() == nullptr; }
 
 /// Covenience function to determine whether this field is a valid sizer field.
 inline bool is_sizer_field(const t_field& obj) { return !is_bad_field(obj) && obj->getSizer() != nullptr; }
@@ -408,11 +421,44 @@ public:
 
 	boost::any&		get_value()override { return m_value; }
 
-	void			enable() override { dynamic_cast<wxColourPickerCtrl*>(window)->Enable(); };
-	void			disable() override{ dynamic_cast<wxColourPickerCtrl*>(window)->Disable(); };
+	void			enable() override { dynamic_cast<wxStaticText*>(window)->Enable(); };
+	void			disable() override{ dynamic_cast<wxStaticText*>(window)->Disable(); };
 	wxWindow*		getWindow() override { return window; }
 };
 
+class SliderCtrl : public Field {
+	using Field::Field;
+public:
+	SliderCtrl(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id) {}
+	SliderCtrl(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(parent, opt, id) {}
+	~SliderCtrl() {}
+
+	wxSizer*		m_sizer{ nullptr };
+	wxTextCtrl*		m_textctrl{ nullptr };
+	wxSlider*		m_slider{ nullptr };
+
+	int				m_scale = 10;
+
+	void			BUILD()  override;
+
+	void			set_value(const int value, bool change_event = false);
+	void			set_value(const boost::any& value, bool change_event = false);
+	boost::any&		get_value() override;
+
+	void			enable() override {
+		m_slider->Enable();
+		m_textctrl->Enable();
+		m_textctrl->SetEditable(true);
+	}
+	void			disable() override{
+		m_slider->Disable();
+		m_textctrl->Disable();
+		m_textctrl->SetEditable(false);
+	}
+	wxSizer*		getSizer() override { return m_sizer; }
+	wxWindow*		getWindow() override { return dynamic_cast<wxWindow*>(m_slider); }
+};
+
 } // GUI
 } // Slic3r
 
diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp
index 652b8e899..1202045f1 100644
--- a/xs/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp
@@ -2967,7 +2967,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
 
         m_mouse.set_start_position_2D_as_invalid();
 #endif
-    } 
+    }
     else if (evt.Leaving())
     {
         // to remove hover on objects when the mouse goes out of this canvas
@@ -3214,6 +3214,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
             }
             const Vec3d& size = bb.size();
             m_on_update_geometry_info_callback.call(size(0), size(1), size(2), m_gizmos.get_scale());
+            update_scale_values(size, m_gizmos.get_scale());
+            update_rotation_value(volumes[0]->get_angle_z(), "z");
         }
 
         if ((m_gizmos.get_current_type() != Gizmos::Rotate) && (volumes.size() > 1))
@@ -3314,6 +3316,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
             case Gizmos::Scale:
             {
                 m_on_gizmo_scale_uniformly_callback.call((double)m_gizmos.get_scale());
+                Slic3r::GUI::update_settings_value();
                 break;
             }
             case Gizmos::Rotate:
diff --git a/xs/src/slic3r/GUI/GUI.cpp b/xs/src/slic3r/GUI/GUI.cpp
index 18f63f65e..9e28de04a 100644
--- a/xs/src/slic3r/GUI/GUI.cpp
+++ b/xs/src/slic3r/GUI/GUI.cpp
@@ -38,6 +38,8 @@
 #include <wx/msgdlg.h>
 #include <wx/settings.h>
 #include <wx/display.h>
+#include <wx/collpane.h>
+#include <wx/wupdlock.h>
 
 #include "wxExtensions.hpp"
 
@@ -53,12 +55,15 @@
 #include "PresetBundle.hpp"
 #include "UpdateDialogs.hpp"
 #include "FirmwareDialog.hpp"
+#include "GUI_ObjectParts.hpp"
 
 #include "../Utils/PresetUpdater.hpp"
 #include "../Config/Snapshot.hpp"
 
 #include "3DScene.hpp"
 #include "libslic3r/I18N.hpp"
+#include "Model.hpp"
+#include "LambdaObjectDialog.hpp"
 
 namespace Slic3r { namespace GUI {
 
@@ -123,10 +128,26 @@ wxLocale*	g_wxLocale;
 wxFont		g_small_font;
 wxFont		g_bold_font;
 
-std::shared_ptr<ConfigOptionsGroup>	m_optgroup;
-double m_brim_width = 0.0;
+std::vector <std::shared_ptr<ConfigOptionsGroup>> m_optgroups;
+double		m_brim_width = 0.0;
+size_t		m_label_width = 100;
 wxButton*	g_wiping_dialog_button = nullptr;
 
+//showed/hided controls according to the view mode
+wxWindow	*g_right_panel = nullptr;
+wxBoxSizer	*g_frequently_changed_parameters_sizer = nullptr;
+wxBoxSizer	*g_expert_mode_part_sizer = nullptr;
+wxBoxSizer	*g_scrolled_window_sizer = nullptr;
+wxBoxSizer	*g_object_list_sizer = nullptr;
+wxButton	*g_btn_export_gcode = nullptr;
+wxButton	*g_btn_export_stl = nullptr;
+wxButton	*g_btn_reslice = nullptr;
+wxButton	*g_btn_print = nullptr;
+wxButton	*g_btn_send_gcode = nullptr;
+wxStaticBitmap	*g_manifold_warning_icon = nullptr;
+bool		g_show_print_info = false;
+bool		g_show_manifold_warning_icon = false;
+
 static void init_label_colours()
 {
 	auto luma = get_colour_approx_luma(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
@@ -206,6 +227,39 @@ void set_3DScene(_3DScene *scene)
 	g_3DScene = scene;
 }
 
+void set_objects_from_perl(	wxWindow* parent, wxBoxSizer *frequently_changed_parameters_sizer,
+							wxBoxSizer *expert_mode_part_sizer, wxBoxSizer *scrolled_window_sizer,
+							wxButton *btn_export_gcode,
+							wxButton *btn_export_stl, wxButton *btn_reslice, 
+							wxButton *btn_print, wxButton *btn_send_gcode,
+							wxStaticBitmap *manifold_warning_icon)
+{
+	g_right_panel = parent;
+	g_frequently_changed_parameters_sizer = frequently_changed_parameters_sizer;
+	g_expert_mode_part_sizer = expert_mode_part_sizer;
+	g_scrolled_window_sizer = scrolled_window_sizer;
+	g_btn_export_gcode = btn_export_gcode;
+	g_btn_export_stl = btn_export_stl;
+	g_btn_reslice = btn_reslice;
+	g_btn_print = btn_print;
+	g_btn_send_gcode = btn_send_gcode;
+	g_manifold_warning_icon = manifold_warning_icon;
+}
+
+void set_show_print_info(bool show)
+{
+	g_show_print_info = show;
+}
+
+void set_show_manifold_warning_icon(bool show)
+{
+	g_show_manifold_warning_icon = show;
+}
+
+void set_objects_list_sizer(wxBoxSizer *objects_list_sizer){
+	g_object_list_sizer = objects_list_sizer;
+}
+
 std::vector<Tab *>& get_tabs_list()
 {
 	return g_tabs_list;
@@ -332,10 +386,21 @@ enum ConfigMenuIDs {
 	ConfigMenuTakeSnapshot,
 	ConfigMenuUpdate,
 	ConfigMenuPreferences,
+	ConfigMenuModeSimple,
+	ConfigMenuModeExpert,
 	ConfigMenuLanguage,
 	ConfigMenuFlashFirmware,
 	ConfigMenuCnt,
 };
+	
+ConfigMenuIDs get_view_mode()
+{
+	if (!g_AppConfig->has("view_mode"))
+		return ConfigMenuModeSimple;
+
+	const auto mode = g_AppConfig->get("view_mode");
+	return mode == "expert" ? ConfigMenuModeExpert : ConfigMenuModeSimple;
+}
 
 static wxString dots("…", wxConvUTF8);
 
@@ -353,8 +418,15 @@ void add_config_menu(wxMenuBar *menu, int event_preferences_changed, int event_l
 // 	local_menu->Append(config_id_base + ConfigMenuUpdate, 		_(L("Check for updates")), 					_(L("Check for configuration updates")));
    	local_menu->AppendSeparator();
    	local_menu->Append(config_id_base + ConfigMenuPreferences, 	_(L("Preferences"))+dots+"\tCtrl+,", 		_(L("Application preferences")));
-   	local_menu->Append(config_id_base + ConfigMenuLanguage, 	_(L("Change Application Language")));
+	local_menu->AppendSeparator();
+	auto mode_menu = new wxMenu();
+	mode_menu->AppendRadioItem(config_id_base + ConfigMenuModeSimple,	_(L("&Simple")),					_(L("Simple View Mode")));
+	mode_menu->AppendRadioItem(config_id_base + ConfigMenuModeExpert,	_(L("&Expert")),					_(L("Expert View Mode")));
+	mode_menu->Check(config_id_base + get_view_mode(), true);
+	local_menu->AppendSubMenu(mode_menu,						_(L("&Mode")), 								_(L("Slic3r View Mode")));
    	local_menu->AppendSeparator();
+	local_menu->Append(config_id_base + ConfigMenuLanguage,		_(L("Change Application Language")));
+	local_menu->AppendSeparator();
 	local_menu->Append(config_id_base + ConfigMenuFlashFirmware, _(L("Flash printer firmware")), _(L("Upload a firmware image into an Arduino based printer")));
 	// TODO: for when we're able to flash dictionaries
 	// local_menu->Append(config_id_base + FirmwareMenuDict,  _(L("Flash language file")),    _(L("Upload a language dictionary file into a Prusa printer")));
@@ -421,6 +493,13 @@ void add_config_menu(wxMenuBar *menu, int event_preferences_changed, int event_l
 			break;
 		}
 	});
+	mode_menu->Bind(wxEVT_MENU, [config_id_base](wxEvent& event) {
+		std::string mode =	event.GetId() - config_id_base == ConfigMenuModeExpert ?
+							"expert" : "simple";
+		g_AppConfig->set("view_mode", mode);
+		g_AppConfig->save();
+		update_mode();
+	});
 	menu->Append(local_menu, _(L("&Configuration")));
 }
 
@@ -429,6 +508,26 @@ void add_menus(wxMenuBar *menu, int event_preferences_changed, int event_languag
     add_config_menu(menu, event_preferences_changed, event_language_change);
 }
 
+void open_model(wxWindow *parent, wxArrayString& input_files){
+	t_file_wild_card vec_FILE_WILDCARDS = get_file_wild_card();
+	std::vector<std::string> file_types = { "known", "stl", "obj", "amf", "3mf", "prusa" };
+	wxString MODEL_WILDCARD;
+	for (auto file_type : file_types)
+		MODEL_WILDCARD += vec_FILE_WILDCARDS.at(file_type) + "|";
+
+	auto dlg_title = _(L("Choose one or more files (STL/OBJ/AMF/3MF/PRUSA):"));
+	auto dialog = new wxFileDialog(parent /*? parent : GetTopWindow(g_wxMainFrame)*/, dlg_title, 
+		g_AppConfig->get_last_dir(), "",
+		MODEL_WILDCARD, wxFD_OPEN | wxFD_MULTIPLE | wxFD_FILE_MUST_EXIST);
+	if (dialog->ShowModal() != wxID_OK) {
+		dialog->Destroy();
+		return ;
+	}
+	
+	dialog->GetPaths(input_files);
+	dialog->Destroy();
+}
+
 // This is called when closing the application, when loading a config file or when starting the config wizard
 // to notify the user whether he is aware that some preset changes will be lost.
 bool check_unsaved_changes()
@@ -496,20 +595,33 @@ void open_preferences_dialog(int event_preferences)
 void create_preset_tabs(bool no_controller, int event_value_change, int event_presets_changed)
 {	
 	update_label_colours_from_appconfig();
-	add_created_tab(new TabPrint	(g_wxTabPanel, no_controller));
-	add_created_tab(new TabFilament	(g_wxTabPanel, no_controller));
-	add_created_tab(new TabPrinter	(g_wxTabPanel, no_controller));
-	for (size_t i = 0; i < g_wxTabPanel->GetPageCount(); ++ i) {
-		Tab *tab = dynamic_cast<Tab*>(g_wxTabPanel->GetPage(i));
-		if (! tab)
-			continue;
-		tab->set_event_value_change(wxEventType(event_value_change));
-		tab->set_event_presets_changed(wxEventType(event_presets_changed));
-	}
+	add_created_tab(new TabPrint	    (g_wxTabPanel, no_controller), event_value_change, event_presets_changed);
+	add_created_tab(new TabFilament	    (g_wxTabPanel, no_controller), event_value_change, event_presets_changed);
+	add_created_tab(new TabSLAMaterial  (g_wxTabPanel, no_controller), event_value_change, event_presets_changed);
+	add_created_tab(new TabPrinter	    (g_wxTabPanel, no_controller), event_value_change, event_presets_changed);
+}
+
+std::vector<PresetTab> preset_tabs = {
+    { "print",        nullptr, ptFFF },
+    { "filament",     nullptr, ptFFF },
+    { "sla_material", nullptr, ptSLA }
+};
+const std::vector<PresetTab>& get_preset_tabs() {
+    return preset_tabs;
+}
+
+Tab* get_tab(const std::string& name)
+{
+    std::vector<PresetTab>::iterator it = std::find_if(preset_tabs.begin(), preset_tabs.end(),
+                                                       [name](PresetTab& tab){ return name == tab.name; });
+    return it != preset_tabs.end() ? it->panel : nullptr;
 }
 
 TabIface* get_preset_tab_iface(char *name)
 {
+    Tab* tab = get_tab(name);
+    if (tab) return new TabIface(tab);
+
 	for (size_t i = 0; i < g_wxTabPanel->GetPageCount(); ++ i) {
 		Tab *tab = dynamic_cast<Tab*>(g_wxTabPanel->GetPage(i));
 		if (! tab)
@@ -629,13 +741,28 @@ void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt
 	}
 }
 
-void add_created_tab(Tab* panel)
+void add_created_tab(Tab* panel, int event_value_change, int event_presets_changed)
 {
 	panel->create_preset_tab(g_PresetBundle);
 
 	// Load the currently selected preset into the GUI, update the preset selection box.
 	panel->load_current_preset();
-	g_wxTabPanel->AddPage(panel, panel->title());
+
+    panel->set_event_value_change(wxEventType(event_value_change));
+    panel->set_event_presets_changed(wxEventType(event_presets_changed));
+
+    const wxString& tab_name = panel->GetName();
+    bool add_panel = true;
+
+    auto it = std::find_if( preset_tabs.begin(), preset_tabs.end(), 
+                           [tab_name](PresetTab& tab){return tab.name == tab_name; });
+    if (it != preset_tabs.end()) {
+        it->panel = panel;
+        add_panel = it->technology == g_PresetBundle->printers.get_edited_preset().printer_technology();
+    }
+
+    if (add_panel)
+	    g_wxTabPanel->AddPage(panel, panel->title());
 }
 
 void load_current_presets()
@@ -725,6 +852,22 @@ unsigned get_colour_approx_luma(const wxColour &colour)
 	));
 }
 
+wxWindow* get_right_panel(){
+	return g_right_panel;
+}
+
+wxFrame* get_main_frame() {
+	return g_wxMainFrame;
+}
+
+wxNotebook * get_tab_panel() {
+	return g_wxTabPanel;
+}
+
+const size_t& label_width(){
+	return m_label_width;
+}
+
 void create_combochecklist(wxComboCtrl* comboCtrl, std::string text, std::string items, bool initial_value)
 {
     if (comboCtrl == nullptr)
@@ -793,14 +936,37 @@ wxString from_u8(const std::string &str)
 	return wxString::FromUTF8(str.c_str());
 }
 
+void add_expert_mode_part(	wxWindow* parent, wxBoxSizer* sizer, 
+							Model &model,
+							int event_object_selection_changed,
+							int event_object_settings_changed,
+							int event_remove_object, 
+							int event_update_scene)
+{
+	set_event_object_selection_changed(event_object_selection_changed);
+	set_event_object_settings_changed(event_object_settings_changed);
+	set_event_remove_object(event_remove_object);
+	set_event_update_scene(event_update_scene);
+	set_objects_from_model(model);
+	init_mesh_icons();
+
+// 	wxWindowUpdateLocker noUpdates(parent);
+
+// 	add_objects_list(parent, sizer);
+
+// 	add_collapsible_panes(parent, sizer);
+}
 
 void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFlexGridSizer* preset_sizer)
 {
 	DynamicPrintConfig*	config = &g_PresetBundle->prints.get_edited_preset().config;
-	m_optgroup = std::make_shared<ConfigOptionsGroup>(parent, "", config);
+	std::shared_ptr<ConfigOptionsGroup> optgroup = std::make_shared<ConfigOptionsGroup>(parent, "", config);
 	const wxArrayInt& ar = preset_sizer->GetColWidths();
-	m_optgroup->label_width = ar.IsEmpty() ? 100 : ar.front()-4; // doesn't work
-	m_optgroup->m_on_change = [config](t_config_option_key opt_key, boost::any value){
+	m_label_width = ar.IsEmpty() ? 100 : ar.front()-4;
+	optgroup->label_width = m_label_width;
+
+	//Frequently changed parameters
+	optgroup->m_on_change = [config](t_config_option_key opt_key, boost::any value){
 		TabPrint* tab_print = nullptr;
 		for (size_t i = 0; i < g_wxTabPanel->GetPageCount(); ++i) {
 			Tab *tab = dynamic_cast<Tab*>(g_wxTabPanel->GetPage(i));
@@ -815,7 +981,7 @@ void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFl
 			return;
 
 		if (opt_key == "fill_density"){
-			value = m_optgroup->get_config_value(*config, opt_key);
+			value = m_optgroups[ogFrequentlyChangingParameters]->get_config_value(*config, opt_key);
 			tab_print->set_value(opt_key, value);
 			tab_print->update();
 		}
@@ -838,14 +1004,14 @@ void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFl
 			}
 			else{ //(opt_key == "support")
 				const wxString& selection = boost::any_cast<wxString>(value);
-				
+
 				auto support_material = selection == _("None") ? false : true;
 				new_conf.set_key_value("support_material", new ConfigOptionBool(support_material));
 
 				if (selection == _("Everywhere"))
 					new_conf.set_key_value("support_material_buildplate_only", new ConfigOptionBool(false));
 				else if (selection == _("Support on build plate only"))
-					new_conf.set_key_value("support_material_buildplate_only", new ConfigOptionBool(true));				
+					new_conf.set_key_value("support_material_buildplate_only", new ConfigOptionBool(true));
 			}
 			tab_print->load_config(new_conf);
 		}
@@ -853,10 +1019,10 @@ void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFl
 		tab_print->update_dirty();
 	};
 
-	Option option = m_optgroup->get_option("fill_density");
+	Option option = optgroup->get_option("fill_density");
 	option.opt.sidetext = "";
 	option.opt.full_width = true;
-	m_optgroup->append_single_option_line(option);
+	optgroup->append_single_option_line(option);
 
 	ConfigOptionDef def;
 
@@ -875,7 +1041,7 @@ void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFl
 	def.default_value = new ConfigOptionStrings { selection };
 	option = Option(def, "support");
 	option.opt.full_width = true;
-	m_optgroup->append_single_option_line(option);
+	optgroup->append_single_option_line(option);
 
 	m_brim_width = config->opt_float("brim_width");
 	def.label = L("Brim");
@@ -884,7 +1050,7 @@ void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFl
 	def.gui_type = "";
 	def.default_value = new ConfigOptionBool{ m_brim_width > 0.0 ? true : false };
 	option = Option(def, "brim");
-	m_optgroup->append_single_option_line(option);
+	optgroup->append_single_option_line(option);
 
 
     Line line = { "", "" };
@@ -910,16 +1076,95 @@ void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFl
 			}));
 			return sizer;
 		};
-		m_optgroup->append_line(line);
+		optgroup->append_line(line);
 
+	sizer->Add(optgroup->sizer, 0, wxEXPAND | wxBOTTOM, 2);
 
+	m_optgroups.push_back(optgroup);// ogFrequentlyChangingParameters
 
-	sizer->Add(m_optgroup->sizer, 1, wxEXPAND | wxBOTTOM, 2);
+	// Object List
+	add_objects_list(parent, sizer);
+
+	// Frequently Object Settings
+	add_object_settings(parent, sizer);
 }
 
-ConfigOptionsGroup* get_optgroup()
+void show_frequently_changed_parameters(bool show)
 {
-	return m_optgroup.get();
+	g_frequently_changed_parameters_sizer->Show(show);
+	if (!show) return;
+
+	for (size_t i = 0; i < g_wxTabPanel->GetPageCount(); ++i) {
+		Tab *tab = dynamic_cast<Tab*>(g_wxTabPanel->GetPage(i));
+		if (!tab)
+			continue;
+		tab->update_wiping_button_visibility();
+		break;
+	}
+}
+
+void show_buttons(bool show)
+{
+	g_btn_export_stl->Show(show);
+	g_btn_reslice->Show(show);
+	for (size_t i = 0; i < g_wxTabPanel->GetPageCount(); ++i) {
+		TabPrinter *tab = dynamic_cast<TabPrinter*>(g_wxTabPanel->GetPage(i));
+		if (!tab)
+			continue;
+        if (g_PresetBundle->printers.get_selected_preset().printer_technology() == ptFFF) {
+            g_btn_print->Show(show && !tab->m_config->opt_string("serial_port").empty());
+            g_btn_send_gcode->Show(show && !tab->m_config->opt_string("print_host").empty());
+        }
+		break;
+	}
+}
+
+void show_info_sizer(bool show)
+{
+	g_scrolled_window_sizer->Show(static_cast<size_t>(0), show); 
+	g_scrolled_window_sizer->Show(1, show && g_show_print_info);
+	g_manifold_warning_icon->Show(show && g_show_manifold_warning_icon);
+}
+
+void update_mode()
+{
+	wxWindowUpdateLocker noUpdates(g_right_panel);
+
+	// TODO There is a not the best place of it!
+	//*** Update style of the "Export G-code" button****
+	if (g_btn_export_gcode->GetFont() != bold_font()){
+		g_btn_export_gcode->SetBackgroundColour(wxColour(252, 77, 1));
+		g_btn_export_gcode->SetFont(bold_font());
+	}
+	// ***********************************
+
+	ConfigMenuIDs mode = get_view_mode();
+
+// 	show_frequently_changed_parameters(mode >= ConfigMenuModeRegular);
+// 	g_expert_mode_part_sizer->Show(mode == ConfigMenuModeExpert);
+	g_object_list_sizer->Show(mode == ConfigMenuModeExpert);
+	show_info_sizer(mode == ConfigMenuModeExpert);
+	show_buttons(mode == ConfigMenuModeExpert);
+
+	// TODO There is a not the best place of it!
+	// *** Update showing of the collpane_settings
+// 	show_collpane_settings(mode == ConfigMenuModeExpert);
+	// *************************
+	g_right_panel->GetParent()->Layout();
+	g_right_panel->Layout();
+}
+
+bool is_expert_mode(){
+	return get_view_mode() == ConfigMenuModeExpert;
+}
+
+ConfigOptionsGroup* get_optgroup(size_t i)
+{
+	return m_optgroups[i].get();
+}
+
+std::vector <std::shared_ptr<ConfigOptionsGroup>>& get_optgroups() {
+	return m_optgroups;
 }
 
 wxButton* get_wiping_dialog_button()
diff --git a/xs/src/slic3r/GUI/GUI.hpp b/xs/src/slic3r/GUI/GUI.hpp
index 165288819..69e83eaab 100644
--- a/xs/src/slic3r/GUI/GUI.hpp
+++ b/xs/src/slic3r/GUI/GUI.hpp
@@ -3,8 +3,9 @@
 
 #include <string>
 #include <vector>
-#include "Config.hpp"
+#include "PrintConfig.hpp"
 #include "../../libslic3r/Utils.hpp"
+#include "GUI_ObjectParts.hpp"
 
 #include <wx/intl.h>
 #include <wx/string.h>
@@ -12,7 +13,6 @@
 class wxApp;
 class wxWindow;
 class wxFrame;
-class wxFont;
 class wxMenuBar;
 class wxNotebook;
 class wxComboCtrl;
@@ -24,6 +24,8 @@ class wxBoxSizer;
 class wxFlexGridSizer;
 class wxButton;
 class wxFileDialog;
+class wxStaticBitmap;
+class wxFont;
 
 namespace Slic3r { 
 
@@ -67,19 +69,26 @@ typedef std::map<std::string, std::string> t_file_wild_card;
 inline t_file_wild_card& get_file_wild_card() {
 	static t_file_wild_card FILE_WILDCARDS;
 	if (FILE_WILDCARDS.empty()){
-		FILE_WILDCARDS["known"] = "Known files (*.stl, *.obj, *.amf, *.xml, *.prusa)|*.stl;*.STL;*.obj;*.OBJ;*.amf;*.AMF;*.xml;*.XML;*.prusa;*.PRUSA";
-		FILE_WILDCARDS["stl"] = "STL files (*.stl)|*.stl;*.STL";
-		FILE_WILDCARDS["obj"] = "OBJ files (*.obj)|*.obj;*.OBJ";
-        FILE_WILDCARDS["amf"] = "AMF files (*.amf)|*.zip.amf;*.amf;*.AMF;*.xml;*.XML";
-        FILE_WILDCARDS["3mf"] = "3MF files (*.3mf)|*.3mf;*.3MF;";
-        FILE_WILDCARDS["prusa"] = "Prusa Control files (*.prusa)|*.prusa;*.PRUSA";
-		FILE_WILDCARDS["ini"] = "INI files *.ini|*.ini;*.INI";
+		FILE_WILDCARDS["known"]	= "Known files (*.stl, *.obj, *.amf, *.xml, *.prusa)|*.stl;*.STL;*.obj;*.OBJ;*.amf;*.AMF;*.xml;*.XML;*.prusa;*.PRUSA";
+		FILE_WILDCARDS["stl"]	= "STL files (*.stl)|*.stl;*.STL";
+		FILE_WILDCARDS["obj"]	= "OBJ files (*.obj)|*.obj;*.OBJ";
+        FILE_WILDCARDS["amf"]	= "AMF files (*.amf)|*.zip.amf;*.amf;*.AMF;*.xml;*.XML";
+        FILE_WILDCARDS["3mf"]	= "3MF files (*.3mf)|*.3mf;*.3MF;";
+        FILE_WILDCARDS["prusa"]	= "Prusa Control files (*.prusa)|*.prusa;*.PRUSA";
+		FILE_WILDCARDS["ini"]	= "INI files *.ini|*.ini;*.INI";
 		FILE_WILDCARDS["gcode"] = "G-code files (*.gcode, *.gco, *.g, *.ngc)|*.gcode;*.GCODE;*.gco;*.GCO;*.g;*.G;*.ngc;*.NGC";
-		FILE_WILDCARDS["svg"] = "SVG files *.svg|*.svg;*.SVG";
+		FILE_WILDCARDS["svg"]	= "SVG files *.svg|*.svg;*.SVG";
 	}
 	return FILE_WILDCARDS;
 }
 
+struct PresetTab {
+    std::string       name;
+    Tab*              panel;
+    PrinterTechnology technology;
+};
+
+
 void disable_screensaver();
 void enable_screensaver();
 bool debugged();
@@ -93,10 +102,26 @@ void set_app_config(AppConfig *app_config);
 void set_preset_bundle(PresetBundle *preset_bundle);
 void set_preset_updater(PresetUpdater *updater);
 void set_3DScene(_3DScene *scene);
+void set_objects_from_perl(	wxWindow* parent,
+							wxBoxSizer *frequently_changed_parameters_sizer,
+							wxBoxSizer *expert_mode_part_sizer,
+							wxBoxSizer *scrolled_window_sizer,
+							wxButton *btn_export_gcode,
+							wxButton *btn_export_stl,
+							wxButton *btn_reslice,
+							wxButton *btn_print,
+							wxButton *btn_send_gcode,
+							wxStaticBitmap *manifold_warning_icon);
+void set_show_print_info(bool show);
+void set_show_manifold_warning_icon(bool show);
+void set_objects_list_sizer(wxBoxSizer *objects_list_sizer);
 
-AppConfig*	get_app_config();
-wxApp*		get_app();
-PresetBundle* get_preset_bundle();
+AppConfig*		get_app_config();
+wxApp*			get_app();
+PresetBundle*	get_preset_bundle();
+wxFrame*		get_main_frame();
+wxNotebook *	get_tab_panel();
+wxNotebook*		get_tab_panel();
 
 const wxColour& get_label_clr_modified();
 const wxColour& get_label_clr_sys();
@@ -108,6 +133,14 @@ void set_label_clr_sys(const wxColour& clr);
 const wxFont& small_font();
 const wxFont& bold_font();
 
+void open_model(wxWindow *parent, wxArrayString& input_files);
+
+wxWindow*			get_right_panel();
+const size_t&		label_width();
+
+Tab*         get_tab(const std::string& name);
+const std::vector<PresetTab>& get_preset_tabs();
+
 extern void add_menus(wxMenuBar *menu, int event_preferences_changed, int event_language_change);
 
 // This is called when closing the application, when loading a config file or when starting the config wizard
@@ -130,7 +163,7 @@ void create_preset_tabs(bool no_controller, int event_value_change, int event_pr
 TabIface* get_preset_tab_iface(char *name);
 
 // add it at the end of the tab panel.
-void add_created_tab(Tab* panel);
+void add_created_tab(Tab* panel, int event_value_change, int event_presets_changed);
 // Change option value in config
 void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt_key, const boost::any& value, int opt_index = 0);
 
@@ -150,6 +183,8 @@ void save_language();
 void get_installed_languages(wxArrayString & names, wxArrayLong & identifiers);
 // select language from the list of installed languages
 bool select_language(wxArrayString & names, wxArrayLong & identifiers);
+// update right panel of the Plater according to view mode
+void update_mode();
 
 std::vector<Tab *>& get_tabs_list();
 bool checked_tab(Tab* tab);
@@ -169,13 +204,22 @@ wxString	L_str(const std::string &str);
 // Return wxString from std::string in UTF8
 wxString	from_u8(const std::string &str);
 
-
+void add_expert_mode_part(	wxWindow* parent, wxBoxSizer* sizer, 
+							Model &model,
+							int event_object_selection_changed,
+							int event_object_settings_changed,
+							int event_remove_object, 
+							int event_update_scene);
 void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFlexGridSizer* preset_sizer);
+// Update view mode according to selected menu 
+void update_mode();
+bool is_expert_mode();
 
 // Callback to trigger a configuration update timer on the Plater.
 static PerlCallback g_on_request_update_callback;
  
-ConfigOptionsGroup* get_optgroup();
+ConfigOptionsGroup* get_optgroup(size_t i); 
+std::vector <std::shared_ptr<ConfigOptionsGroup>>& get_optgroups();
 wxButton*			get_wiping_dialog_button();
 
 void add_export_option(wxFileDialog* dlg, const std::string& format);
diff --git a/xs/src/slic3r/GUI/GUI_ObjectParts.cpp b/xs/src/slic3r/GUI/GUI_ObjectParts.cpp
new file mode 100644
index 000000000..50b1a1f5a
--- /dev/null
+++ b/xs/src/slic3r/GUI/GUI_ObjectParts.cpp
@@ -0,0 +1,1644 @@
+#include "GUI.hpp"
+#include "OptionsGroup.hpp"
+#include "PresetBundle.hpp"
+#include "GUI_ObjectParts.hpp"
+#include "Model.hpp"
+#include "wxExtensions.hpp"
+#include "LambdaObjectDialog.hpp"
+#include "../../libslic3r/Utils.hpp"
+
+#include <wx/msgdlg.h>
+#include <boost/filesystem.hpp>
+#include <boost/algorithm/string.hpp>
+#include "Geometry.hpp"
+#include "slic3r/Utils/FixModelByWin10.hpp"
+
+namespace Slic3r
+{
+namespace GUI
+{
+wxSizer		*m_sizer_object_buttons = nullptr;
+wxSizer		*m_sizer_part_buttons = nullptr;
+wxSizer		*m_sizer_object_movers = nullptr;
+wxDataViewCtrl				*m_objects_ctrl = nullptr;
+PrusaObjectDataViewModel	*m_objects_model = nullptr;
+wxCollapsiblePane			*m_collpane_settings = nullptr;
+
+wxIcon		m_icon_modifiermesh;
+wxIcon		m_icon_solidmesh;
+wxIcon		m_icon_manifold_warning;
+wxBitmap	m_bmp_cog;
+wxBitmap	m_bmp_split;
+
+wxSlider*	m_mover_x = nullptr;
+wxSlider*	m_mover_y = nullptr;
+wxSlider*	m_mover_z = nullptr;
+wxButton*	m_btn_move_up = nullptr;
+wxButton*	m_btn_move_down = nullptr;
+Vec3d		m_move_options;
+Vec3d		m_last_coords;
+int			m_selected_object_id = -1;
+
+bool		g_prevent_list_events = false;		// We use this flag to avoid circular event handling Select() 
+												// happens to fire a wxEVT_LIST_ITEM_SELECTED on OSX, whose event handler 
+												// calls this method again and again and again
+bool        g_is_percent_scale = false;         // It indicates if scale unit is percentage
+int         g_rotation_x = 0;                   // Last value of the rotation around the X axis
+int         g_rotation_y = 0;                   // Last value of the rotation around the Y axis
+ModelObjectPtrs*			m_objects;
+std::shared_ptr<DynamicPrintConfig*> m_config;
+std::shared_ptr<DynamicPrintConfig> m_default_config;
+wxBoxSizer*					m_option_sizer = nullptr;
+
+// option groups for settings
+std::vector <std::shared_ptr<ConfigOptionsGroup>> m_og_settings;
+
+int			m_event_object_selection_changed = 0;
+int			m_event_object_settings_changed = 0;
+int			m_event_remove_object = 0;
+int			m_event_update_scene = 0;
+
+bool m_parts_changed = false;
+bool m_part_settings_changed = false;
+
+#ifdef __WXOSX__
+    wxString g_selected_extruder = "";
+#endif //__WXOSX__
+
+// typedef std::map<std::string, std::string> t_category_icon;
+typedef std::map<std::string, wxBitmap> t_category_icon;
+inline t_category_icon& get_category_icon() {
+	static t_category_icon CATEGORY_ICON;
+	if (CATEGORY_ICON.empty()){
+		CATEGORY_ICON[L("Layers and Perimeters")]	= wxBitmap(from_u8(Slic3r::var("layers.png")), wxBITMAP_TYPE_PNG);
+		CATEGORY_ICON[L("Infill")]					= wxBitmap(from_u8(Slic3r::var("infill.png")), wxBITMAP_TYPE_PNG);
+		CATEGORY_ICON[L("Support material")]		= wxBitmap(from_u8(Slic3r::var("building.png")), wxBITMAP_TYPE_PNG);
+		CATEGORY_ICON[L("Speed")]					= wxBitmap(from_u8(Slic3r::var("time.png")), wxBITMAP_TYPE_PNG);
+		CATEGORY_ICON[L("Extruders")]				= wxBitmap(from_u8(Slic3r::var("funnel.png")), wxBITMAP_TYPE_PNG);
+		CATEGORY_ICON[L("Extrusion Width")]			= wxBitmap(from_u8(Slic3r::var("funnel.png")), wxBITMAP_TYPE_PNG);
+// 		CATEGORY_ICON[L("Skirt and brim")]			= wxBitmap(from_u8(Slic3r::var("box.png")), wxBITMAP_TYPE_PNG);
+// 		CATEGORY_ICON[L("Speed > Acceleration")]	= wxBitmap(from_u8(Slic3r::var("time.png")), wxBITMAP_TYPE_PNG);
+		CATEGORY_ICON[L("Advanced")]				= wxBitmap(from_u8(Slic3r::var("wand.png")), wxBITMAP_TYPE_PNG);
+	}
+	return CATEGORY_ICON;
+}
+
+std::vector<std::string> get_options(const bool is_part)
+{
+	PrintRegionConfig reg_config;
+	auto options = reg_config.keys();
+	if (!is_part) {
+		PrintObjectConfig obj_config;
+		std::vector<std::string> obj_options = obj_config.keys();
+		options.insert(options.end(), obj_options.begin(), obj_options.end());
+	}
+	return options;
+}
+
+//				  category ->		vector 			 ( option	;  label )
+typedef std::map< std::string, std::vector< std::pair<std::string, std::string> > > settings_menu_hierarchy;
+void get_options_menu(settings_menu_hierarchy& settings_menu, bool is_part)
+{
+	auto options = get_options(is_part);
+
+    auto extruders_cnt = get_preset_bundle()->printers.get_selected_preset().printer_technology() == ptSLA ? 1 :
+                         get_preset_bundle()->printers.get_edited_preset().config.option<ConfigOptionFloats>("nozzle_diameter")->values.size();
+
+	DynamicPrintConfig config;
+	for (auto& option : options)
+	{
+		auto const opt = config.def()->get(option);
+		auto category = opt->category;
+        if (category.empty() ||
+            (category == "Extruders" && extruders_cnt == 1)) continue;
+
+		std::pair<std::string, std::string> option_label(option, opt->label);
+		std::vector< std::pair<std::string, std::string> > new_category;
+		auto& cat_opt_label = settings_menu.find(category) == settings_menu.end() ? new_category : settings_menu.at(category);
+		cat_opt_label.push_back(option_label);
+		if (cat_opt_label.size() == 1)
+			settings_menu[category] = cat_opt_label;
+	}
+}
+
+void set_event_object_selection_changed(const int& event){
+	m_event_object_selection_changed = event;
+}
+void set_event_object_settings_changed(const int& event){
+	m_event_object_settings_changed = event;
+}
+void set_event_remove_object(const int& event){
+	m_event_remove_object = event;
+}
+void set_event_update_scene(const int& event){
+	m_event_update_scene = event;
+}
+
+void set_objects_from_model(Model &model) {
+	m_objects = &(model.objects);
+}
+
+void init_mesh_icons(){
+	m_icon_modifiermesh = wxIcon(Slic3r::GUI::from_u8(Slic3r::var("lambda.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("plugin.png")), wxBITMAP_TYPE_PNG);
+	m_icon_solidmesh = wxIcon(Slic3r::GUI::from_u8(Slic3r::var("object.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("package.png")), wxBITMAP_TYPE_PNG);
+
+	// init icon for manifold warning
+	m_icon_manifold_warning = wxIcon(Slic3r::GUI::from_u8(Slic3r::var("exclamation_mark_.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("error.png")), wxBITMAP_TYPE_PNG);
+
+	// init bitmap for "Split to sub-objects" context menu
+    m_bmp_split = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("split.png")), wxBITMAP_TYPE_PNG);
+
+	// init bitmap for "Add Settings" context menu
+	m_bmp_cog = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("cog.png")), wxBITMAP_TYPE_PNG);
+}
+
+bool is_parts_changed(){return m_parts_changed;}
+bool is_part_settings_changed(){ return m_part_settings_changed; }
+
+static wxString dots("…", wxConvUTF8);
+
+void set_tooltip_for_item(const wxPoint& pt)
+{
+    wxDataViewItem item;
+    wxDataViewColumn* col;
+    m_objects_ctrl->HitTest(pt, item, col);
+    if (!item) return;
+
+    if (col->GetTitle() == " ")
+        m_objects_ctrl->GetMainWindow()->SetToolTip(_(L("Right button click the icon to change the object settings")));
+    else if (col->GetTitle() == _("Name") &&
+        m_objects_model->GetIcon(item).GetRefData() == m_icon_manifold_warning.GetRefData()) {
+        int obj_idx = m_objects_model->GetIdByItem(item);
+        auto& stats = (*m_objects)[obj_idx]->volumes[0]->mesh.stl.stats;
+        int errors = stats.degenerate_facets + stats.edges_fixed + stats.facets_removed +
+            stats.facets_added + stats.facets_reversed + stats.backwards_edges;
+
+        wxString tooltip = wxString::Format(_(L("Auto-repaired (%d errors):\n")), errors);
+
+        std::map<std::string, int> error_msg;
+        error_msg[L("degenerate facets")] = stats.degenerate_facets;
+        error_msg[L("edges fixed")] = stats.edges_fixed;
+        error_msg[L("facets removed")] = stats.facets_removed;
+        error_msg[L("facets added")] = stats.facets_added;
+        error_msg[L("facets reversed")] = stats.facets_reversed;
+        error_msg[L("backwards edges")] = stats.backwards_edges;
+
+        for (auto error : error_msg)
+        {
+            if (error.second > 0)
+                tooltip += wxString::Format(_("\t%d %s\n"), error.second, error.first);
+        }
+// OR
+//             tooltip += wxString::Format(_(L("%d degenerate facets, %d edges fixed, %d facets removed, "
+//                                             "%d facets added, %d facets reversed, %d backwards edges")),
+//                                             stats.degenerate_facets, stats.edges_fixed, stats.facets_removed,
+//                                             stats.facets_added, stats.facets_reversed, stats.backwards_edges);
+
+        if (is_windows10())
+            tooltip += _(L("Right button click the icon to fix STL through Netfabb"));
+
+        m_objects_ctrl->GetMainWindow()->SetToolTip(tooltip);
+    }
+    else
+        m_objects_ctrl->GetMainWindow()->SetToolTip(""); // hide tooltip
+}
+
+wxPoint get_mouse_position_in_control() {
+    const wxPoint& pt = wxGetMousePosition();
+    wxWindow* win = m_objects_ctrl->GetMainWindow();
+    return wxPoint(pt.x - win->GetScreenPosition().x, 
+                   pt.y - win->GetScreenPosition().y);
+}
+
+bool is_mouse_position_in_control(wxPoint& pt) {
+    pt = get_mouse_position_in_control();
+    const wxSize& cz = m_objects_ctrl->GetSize();
+    if (pt.x > 0 && pt.x < cz.x &&
+        pt.y > 0 && pt.y < cz.y)
+        return true;
+    return false;
+}
+
+wxDataViewColumn* object_ctrl_create_extruder_column(int extruders_count)
+{
+    wxArrayString choices;
+    choices.Add("default");
+    for (int i = 1; i <= extruders_count; ++i)
+        choices.Add(wxString::Format("%d", i));
+    wxDataViewChoiceRenderer *c =
+        new wxDataViewChoiceRenderer(choices, wxDATAVIEW_CELL_EDITABLE, wxALIGN_CENTER_HORIZONTAL);
+    wxDataViewColumn* column = new wxDataViewColumn(_(L("Extruder")), c, 3, 60, wxALIGN_CENTER_HORIZONTAL, wxDATAVIEW_COL_RESIZABLE);
+    return column;
+}
+
+void create_objects_ctrl(wxWindow* win, wxBoxSizer*& objects_sz)
+{
+	m_objects_ctrl = new wxDataViewCtrl(win, wxID_ANY, wxDefaultPosition, wxDefaultSize);
+	m_objects_ctrl->SetInitialSize(wxSize(-1, 150)); // TODO - Set correct height according to the opened/closed objects
+
+	objects_sz = new wxBoxSizer(wxVERTICAL);
+	objects_sz->Add(m_objects_ctrl, 1, wxGROW | wxLEFT, 20);
+
+	m_objects_model = new PrusaObjectDataViewModel;
+	m_objects_ctrl->AssociateModel(m_objects_model);
+#if wxUSE_DRAG_AND_DROP && wxUSE_UNICODE
+	m_objects_ctrl->EnableDragSource(wxDF_UNICODETEXT);
+	m_objects_ctrl->EnableDropTarget(wxDF_UNICODETEXT);
+#endif // wxUSE_DRAG_AND_DROP && wxUSE_UNICODE
+
+	// column 0(Icon+Text) of the view control:
+	m_objects_ctrl->AppendIconTextColumn(_(L("Name")), 0, wxDATAVIEW_CELL_INERT, 120,
+		wxALIGN_LEFT, wxDATAVIEW_COL_RESIZABLE);
+
+	// column 1 of the view control:
+	m_objects_ctrl->AppendTextColumn(_(L("Copy")), 1, wxDATAVIEW_CELL_INERT, 45,
+		wxALIGN_CENTER_HORIZONTAL, wxDATAVIEW_COL_RESIZABLE);
+
+	// column 2 of the view control:
+	m_objects_ctrl->AppendTextColumn(_(L("Scale")), 2, wxDATAVIEW_CELL_INERT, 55,
+		wxALIGN_CENTER_HORIZONTAL, wxDATAVIEW_COL_RESIZABLE);
+
+	// column 3 of the view control:
+    m_objects_ctrl->AppendColumn(object_ctrl_create_extruder_column(4));
+
+	// column 4 of the view control:
+	m_objects_ctrl->AppendBitmapColumn(" ", 4, wxDATAVIEW_CELL_INERT, 25,
+		wxALIGN_CENTER_HORIZONTAL, wxDATAVIEW_COL_RESIZABLE);
+}
+
+// ****** from GUI.cpp
+wxBoxSizer* create_objects_list(wxWindow *win)
+{
+    wxBoxSizer* objects_sz;
+    // create control
+    create_objects_ctrl(win, objects_sz);
+
+    // describe control behavior 
+    m_objects_ctrl->Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, [](wxEvent& event) {
+		object_ctrl_selection_changed();
+#ifndef __WXMSW__
+        set_tooltip_for_item(get_mouse_position_in_control());
+#endif //__WXMSW__        
+	});
+
+    m_objects_ctrl->Bind(wxEVT_DATAVIEW_ITEM_CONTEXT_MENU, [](wxDataViewEvent& event) {
+        object_ctrl_context_menu();
+		event.Skip();
+	});
+
+    m_objects_ctrl->Bind(wxEVT_CHAR, [](wxKeyEvent& event) { object_ctrl_key_event(event); }); // doesn't work on OSX
+
+#ifdef __WXMSW__
+    // Extruder value changed
+	m_objects_ctrl->Bind(wxEVT_CHOICE, [](wxCommandEvent& event) { update_extruder_in_config(event.GetString()); });
+
+    m_objects_ctrl->GetMainWindow()->Bind(wxEVT_MOTION, [](wxMouseEvent& event) {
+         set_tooltip_for_item(event.GetPosition());
+         event.Skip();
+    });
+#else
+    // equivalent to wxEVT_CHOICE on __WXMSW__
+    m_objects_ctrl->Bind(wxEVT_DATAVIEW_ITEM_VALUE_CHANGED, [](wxDataViewEvent& event) { object_ctrl_item_value_change(event); });
+#endif //__WXMSW__
+
+    m_objects_ctrl->Bind(wxEVT_DATAVIEW_ITEM_BEGIN_DRAG,    [](wxDataViewEvent& e) {on_begin_drag(e);});
+    m_objects_ctrl->Bind(wxEVT_DATAVIEW_ITEM_DROP_POSSIBLE, [](wxDataViewEvent& e) {on_drop_possible(e); });
+    m_objects_ctrl->Bind(wxEVT_DATAVIEW_ITEM_DROP,          [](wxDataViewEvent& e) {on_drop(e);});
+	return objects_sz;
+}
+
+wxBoxSizer* create_edit_object_buttons(wxWindow* win)
+{
+	auto sizer = new wxBoxSizer(wxVERTICAL);
+
+	auto btn_load_part = new wxButton(win, wxID_ANY, /*Load */"part" + dots, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT | wxNO_BORDER/*wxBU_LEFT*/);
+	auto btn_load_modifier = new wxButton(win, wxID_ANY, /*Load */"modifier" + dots, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT | wxNO_BORDER/*wxBU_LEFT*/);
+	auto btn_load_lambda_modifier = new wxButton(win, wxID_ANY, /*Load */"generic" + dots, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT | wxNO_BORDER/*wxBU_LEFT*/);
+	auto btn_delete = new wxButton(win, wxID_ANY, "Delete"/*" part"*/, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT | wxNO_BORDER/*wxBU_LEFT*/);
+	auto btn_split = new wxButton(win, wxID_ANY, "Split"/*" part"*/, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT | wxNO_BORDER/*wxBU_LEFT*/);
+	m_btn_move_up = new wxButton(win, wxID_ANY, "", wxDefaultPosition, wxDefaultSize/*wxSize(30, -1)*/, wxBU_LEFT);
+	m_btn_move_down = new wxButton(win, wxID_ANY, "", wxDefaultPosition, wxDefaultSize/*wxSize(30, -1)*/, wxBU_LEFT);
+
+	//*** button's functions
+	btn_load_part->Bind(wxEVT_BUTTON, [win](wxEvent&) {
+		on_btn_load(win);
+	});
+
+	btn_load_modifier->Bind(wxEVT_BUTTON, [win](wxEvent&) {
+		on_btn_load(win, true);
+	});
+
+	btn_load_lambda_modifier->Bind(wxEVT_BUTTON, [win](wxEvent&) {
+		on_btn_load(win, true, true);
+	});
+
+	btn_delete		->Bind(wxEVT_BUTTON, [](wxEvent&) { on_btn_del(); });
+	btn_split		->Bind(wxEVT_BUTTON, [](wxEvent&) { on_btn_split(true); });
+	m_btn_move_up	->Bind(wxEVT_BUTTON, [](wxEvent&) { on_btn_move_up(); });
+	m_btn_move_down	->Bind(wxEVT_BUTTON, [](wxEvent&) { on_btn_move_down(); });
+	//***
+
+	m_btn_move_up->SetMinSize(wxSize(20, -1));
+	m_btn_move_down->SetMinSize(wxSize(20, -1));
+	btn_load_part->SetBitmap(wxBitmap(from_u8(Slic3r::var("brick_add.png")), wxBITMAP_TYPE_PNG));
+	btn_load_modifier->SetBitmap(wxBitmap(from_u8(Slic3r::var("brick_add.png")), wxBITMAP_TYPE_PNG));
+	btn_load_lambda_modifier->SetBitmap(wxBitmap(from_u8(Slic3r::var("brick_add.png")), wxBITMAP_TYPE_PNG));
+	btn_delete->SetBitmap(wxBitmap(from_u8(Slic3r::var("brick_delete.png")), wxBITMAP_TYPE_PNG));
+	btn_split->SetBitmap(wxBitmap(from_u8(Slic3r::var("shape_ungroup.png")), wxBITMAP_TYPE_PNG));
+	m_btn_move_up->SetBitmap(wxBitmap(from_u8(Slic3r::var("bullet_arrow_up.png")), wxBITMAP_TYPE_PNG));
+	m_btn_move_down->SetBitmap(wxBitmap(from_u8(Slic3r::var("bullet_arrow_down.png")), wxBITMAP_TYPE_PNG));
+
+	m_sizer_object_buttons = new wxGridSizer(1, 3, 0, 0);
+	m_sizer_object_buttons->Add(btn_load_part, 0, wxEXPAND);
+	m_sizer_object_buttons->Add(btn_load_modifier, 0, wxEXPAND);
+	m_sizer_object_buttons->Add(btn_load_lambda_modifier, 0, wxEXPAND);
+	m_sizer_object_buttons->Show(false);
+
+	m_sizer_part_buttons = new wxGridSizer(1, 3, 0, 0);
+	m_sizer_part_buttons->Add(btn_delete, 0, wxEXPAND);
+	m_sizer_part_buttons->Add(btn_split, 0, wxEXPAND);
+	{
+		auto up_down_sizer = new wxGridSizer(1, 2, 0, 0);
+		up_down_sizer->Add(m_btn_move_up, 1, wxEXPAND);
+		up_down_sizer->Add(m_btn_move_down, 1, wxEXPAND);
+		m_sizer_part_buttons->Add(up_down_sizer, 0, wxEXPAND);
+	}
+	m_sizer_part_buttons->Show(false);
+
+	btn_load_part->SetFont(Slic3r::GUI::small_font());
+	btn_load_modifier->SetFont(Slic3r::GUI::small_font());
+	btn_load_lambda_modifier->SetFont(Slic3r::GUI::small_font());
+	btn_delete->SetFont(Slic3r::GUI::small_font());
+	btn_split->SetFont(Slic3r::GUI::small_font());
+	m_btn_move_up->SetFont(Slic3r::GUI::small_font());
+	m_btn_move_down->SetFont(Slic3r::GUI::small_font());
+
+	sizer->Add(m_sizer_object_buttons, 0, wxEXPAND | wxLEFT, 20);
+	sizer->Add(m_sizer_part_buttons, 0, wxEXPAND | wxLEFT, 20);
+	return sizer;
+}
+
+void update_after_moving()
+{
+	auto item = m_objects_ctrl->GetSelection();
+	if (!item || m_selected_object_id<0)
+		return;
+
+	auto volume_id = m_objects_model->GetVolumeIdByItem(item);
+	if (volume_id < 0)
+		return;
+
+	Vec3d m = m_move_options;
+	Vec3d l = m_last_coords;
+
+    auto d = Vec3d(m(0) - l(0), m(1) - l(1), m(2) - l(2));
+	auto volume = (*m_objects)[m_selected_object_id]->volumes[volume_id];
+    volume->mesh.translate(d(0), d(1), d(2));
+	m_last_coords = m;
+
+	m_parts_changed = true;
+	parts_changed(m_selected_object_id);
+}
+
+wxSizer* object_movers(wxWindow *win)
+{
+// 	DynamicPrintConfig* config = &get_preset_bundle()->/*full_config();//*/printers.get_edited_preset().config; // TODO get config from Model_volume
+	std::shared_ptr<ConfigOptionsGroup> optgroup = std::make_shared<ConfigOptionsGroup>(win, "Move"/*, config*/);
+	optgroup->label_width = 20;
+	optgroup->m_on_change = [](t_config_option_key opt_key, boost::any value){
+		int val = boost::any_cast<int>(value);
+		bool update = false;
+        if (opt_key == "x" && m_move_options(0) != val){
+			update = true;
+            m_move_options(0) = val;
+		}
+        else if (opt_key == "y" && m_move_options(1) != val){
+			update = true;
+            m_move_options(1) = val;
+		}
+        else if (opt_key == "z" && m_move_options(2) != val){
+			update = true;
+            m_move_options(2) = val;
+		}
+		if (update) update_after_moving();
+	};
+
+	ConfigOptionDef def;
+	def.label = L("X");
+	def.type = coInt;
+	def.gui_type = "slider";
+	def.default_value = new ConfigOptionInt(0);
+
+	Option option = Option(def, "x");
+	option.opt.full_width = true;
+	optgroup->append_single_option_line(option);
+	m_mover_x = dynamic_cast<wxSlider*>(optgroup->get_field("x")->getWindow());
+
+	def.label = L("Y");
+	option = Option(def, "y");
+	optgroup->append_single_option_line(option);
+	m_mover_y = dynamic_cast<wxSlider*>(optgroup->get_field("y")->getWindow());
+
+	def.label = L("Z");
+	option = Option(def, "z");
+	optgroup->append_single_option_line(option);
+	m_mover_z = dynamic_cast<wxSlider*>(optgroup->get_field("z")->getWindow());
+
+	get_optgroups().push_back(optgroup);  // ogObjectMovers
+
+	m_sizer_object_movers = optgroup->sizer;
+	m_sizer_object_movers->Show(false);
+
+	m_move_options = Vec3d(0, 0, 0);
+	m_last_coords = Vec3d(0, 0, 0);
+
+	return optgroup->sizer;
+}
+
+wxBoxSizer* content_settings(wxWindow *win)
+{
+	DynamicPrintConfig* config = &get_preset_bundle()->/*full_config();//*/printers.get_edited_preset().config; // TODO get config from Model_volume
+	std::shared_ptr<ConfigOptionsGroup> optgroup = std::make_shared<ConfigOptionsGroup>(win, "Extruders", config);
+	optgroup->label_width = label_width();
+
+	Option option = optgroup->get_option("extruder");
+	option.opt.default_value = new ConfigOptionInt(1);
+	optgroup->append_single_option_line(option);
+
+	get_optgroups().push_back(optgroup);  // ogObjectSettings
+
+	auto sizer = new wxBoxSizer(wxVERTICAL);
+	sizer->Add(create_edit_object_buttons(win), 0, wxEXPAND, 0); // *** Edit Object Buttons***
+
+	sizer->Add(optgroup->sizer, 1, wxEXPAND | wxLEFT, 20);
+
+	auto add_btn = new wxButton(win, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT | wxNO_BORDER);
+	if (wxMSW) add_btn->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
+	add_btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("add.png")), wxBITMAP_TYPE_PNG));
+	sizer->Add(add_btn, 0, wxALIGN_LEFT | wxLEFT, 20);
+
+	sizer->Add(object_movers(win), 0, wxEXPAND | wxLEFT, 20);
+
+	return sizer;
+}
+
+void add_objects_list(wxWindow* parent, wxBoxSizer* sizer)
+{
+	const auto ol_sizer = create_objects_list(parent);
+	sizer->Add(ol_sizer, 1, wxEXPAND | wxTOP, 20);
+	set_objects_list_sizer(ol_sizer);
+}
+
+Line add_og_to_object_settings(const std::string& option_name, const std::string& sidetext, int def_value = 0)
+{
+	Line line = { _(option_name), "" };
+
+	ConfigOptionDef def;
+	def.type = coInt;
+	def.default_value = new ConfigOptionInt(def_value);
+	def.sidetext = sidetext;
+	def.width = 70;
+
+    if (option_name == "Rotation")
+        def.min = -360;
+
+	const std::string lower_name = boost::algorithm::to_lower_copy(option_name);
+
+	std::vector<std::string> axes{ "x", "y", "z" };
+	for (auto axis : axes) {
+		def.label = boost::algorithm::to_upper_copy(axis);
+		Option option = Option(def, lower_name + "_" + axis);
+		option.opt.full_width = true;
+		line.append_option(option);
+	}
+
+	if (option_name == "Scale")
+	{
+		def.label = L("Units");
+		def.type = coStrings;
+		def.gui_type = "select_open";
+		def.enum_labels.push_back(L("%"));
+		def.enum_labels.push_back(L("mm"));
+		def.default_value = new ConfigOptionStrings{ "%" };
+		def.sidetext = " ";
+
+		Option option = Option(def, lower_name + "_unit");
+		line.append_option(option);
+	}
+
+	return line;
+}
+
+void add_object_settings(wxWindow* parent, wxBoxSizer* sizer)
+{
+	auto optgroup = std::make_shared<ConfigOptionsGroup>(parent, _(L("Object Settings")));
+	optgroup->label_width = 100;
+	optgroup->set_grid_vgap(5);
+
+	optgroup->m_on_change = [](t_config_option_key opt_key, boost::any value){
+		if (opt_key == "scale_unit"){
+			const wxString& selection = boost::any_cast<wxString>(value);
+			std::vector<std::string> axes{ "x", "y", "z" };
+			for (auto axis : axes) {
+				std::string key = "scale_" + axis;
+				get_optgroup(ogFrequentlyObjectSettings)->set_side_text(key, selection);
+			}
+
+            g_is_percent_scale = selection == _("%");
+            update_scale_values();
+		}
+	};
+
+// 	def.label = L("Name");
+// 	def.type = coString;
+// 	def.tooltip = L("Object name");
+// 	def.full_width = true;
+// 	def.default_value = new ConfigOptionString{ "BlaBla_object.stl" };
+// 	optgroup->append_single_option_line(Option(def, "object_name"));
+
+	ConfigOptionDef def;
+
+	def.label = L("Name");
+// 	def.type = coString;
+    def.gui_type = "legend";
+	def.tooltip = L("Object name");
+	def.full_width = true;
+	def.default_value = new ConfigOptionString{ " " };
+	optgroup->append_single_option_line(Option(def, "object_name"));
+
+	optgroup->set_flag(ogSIDE_OPTIONS_VERTICAL);
+	optgroup->sidetext_width = 25;
+
+	optgroup->append_line(add_og_to_object_settings(L("Position"), L("mm")));
+	optgroup->append_line(add_og_to_object_settings(L("Rotation"), "°"));
+	optgroup->append_line(add_og_to_object_settings(L("Scale"), "%"));
+
+	optgroup->set_flag(ogDEFAULT);
+
+	def.label = L("Place on bed");
+	def.type = coBool;
+	def.tooltip = L("Automatic placing of models on printing bed in Y axis");
+	def.gui_type = "";
+	def.sidetext = "";
+	def.default_value = new ConfigOptionBool{ false };
+	optgroup->append_single_option_line(Option(def, "place_on_bed"));
+
+	m_option_sizer = new wxBoxSizer(wxVERTICAL);
+	optgroup->sizer->Add(m_option_sizer, 1, wxEXPAND | wxLEFT, 5);
+
+	sizer->Add(optgroup->sizer, 0, wxEXPAND | wxLEFT | wxTOP, 20);
+
+	optgroup->disable();
+
+	get_optgroups().push_back(optgroup);  // ogFrequentlyObjectSettings
+
+// 	add_current_settings();
+}
+
+
+// add Collapsible Pane to sizer
+wxCollapsiblePane* add_collapsible_pane(wxWindow* parent, wxBoxSizer* sizer_parent, const wxString& name, std::function<wxSizer *(wxWindow *)> content_function)
+{
+#ifdef __WXMSW__
+	auto *collpane = new PrusaCollapsiblePaneMSW(parent, wxID_ANY, name);
+#else
+	auto *collpane = new PrusaCollapsiblePane/*wxCollapsiblePane*/(parent, wxID_ANY, name);
+#endif // __WXMSW__
+	// add the pane with a zero proportion value to the sizer which contains it
+	sizer_parent->Add(collpane, 0, wxGROW | wxALL, 0);
+
+	wxWindow *win = collpane->GetPane();
+
+	wxSizer *sizer = content_function(win);
+
+	wxSizer *sizer_pane = new wxBoxSizer(wxVERTICAL);
+	sizer_pane->Add(sizer, 1, wxGROW | wxEXPAND | wxBOTTOM, 2);
+	win->SetSizer(sizer_pane);
+	// 	sizer_pane->SetSizeHints(win);
+	return collpane;
+}
+
+void add_collapsible_panes(wxWindow* parent, wxBoxSizer* sizer)
+{
+	// *** Objects List ***	
+	auto collpane = add_collapsible_pane(parent, sizer, "Objects List:", create_objects_list);
+	collpane->Bind(wxEVT_COLLAPSIBLEPANE_CHANGED, ([collpane](wxCommandEvent& e){
+		// 		wxWindowUpdateLocker noUpdates(g_right_panel);
+		if (collpane->IsCollapsed()) {
+			m_sizer_object_buttons->Show(false);
+			m_sizer_part_buttons->Show(false);
+			m_sizer_object_movers->Show(false);
+			if (!m_objects_ctrl->HasSelection())
+				m_collpane_settings->Show(false);
+		}
+	}));
+
+	// *** Object/Part Settings ***
+	m_collpane_settings = add_collapsible_pane(parent, sizer, "Object Settings", content_settings);
+}
+
+void show_collpane_settings(bool expert_mode)
+{
+	m_collpane_settings->Show(expert_mode && !m_objects_model->IsEmpty());
+}
+
+void add_object_to_list(const std::string &name, ModelObject* model_object)
+{
+	wxString item_name = name;
+	int scale = model_object->instances[0]->scaling_factor * 100;
+	auto item = m_objects_model->Add(item_name, model_object->instances.size(), scale);
+	m_objects_ctrl->Select(item);
+
+	// Add error icon if detected auto-repaire
+	auto stats = model_object->volumes[0]->mesh.stl.stats;
+	int errors =	stats.degenerate_facets + stats.edges_fixed + stats.facets_removed + 
+					stats.facets_added + stats.facets_reversed + stats.backwards_edges;
+	if (errors > 0)		{
+		const wxDataViewIconText data(item_name, m_icon_manifold_warning);
+		wxVariant variant;
+		variant << data;
+		m_objects_model->SetValue(variant, item, 0);
+	}
+
+    if (model_object->volumes.size() > 1) {
+        for (auto id = 0; id < model_object->volumes.size(); id++)
+            m_objects_model->AddChild(item, 
+                                      model_object->volumes[id]->name, 
+                                      m_icon_solidmesh, 
+                                      model_object->volumes[id]->config.option<ConfigOptionInt>("extruder")->value,
+                                      false);
+        m_objects_ctrl->Expand(item);
+    }
+
+#ifndef __WXOSX__ 
+	object_ctrl_selection_changed();
+#endif //__WXMSW__
+}
+
+void delete_object_from_list()
+{
+	auto item = m_objects_ctrl->GetSelection();
+	if (!item || m_objects_model->GetParent(item) != wxDataViewItem(0))
+		return;
+// 	m_objects_ctrl->Select(m_objects_model->Delete(item));
+	m_objects_model->Delete(item);
+
+// 	if (m_objects_model->IsEmpty())
+// 		m_collpane_settings->Show(false);
+}
+
+void delete_all_objects_from_list()
+{
+	m_objects_model->DeleteAll();
+// 	m_collpane_settings->Show(false);
+}
+
+void set_object_count(int idx, int count)
+{
+	m_objects_model->SetValue(wxString::Format("%d", count), idx, 1);
+	m_objects_ctrl->Refresh();
+}
+
+void set_object_scale(int idx, int scale)
+{
+	m_objects_model->SetValue(wxString::Format("%d%%", scale), idx, 2);
+	m_objects_ctrl->Refresh();
+}
+
+void unselect_objects()
+{
+    if (!m_objects_ctrl->GetSelection())
+        return;
+
+    g_prevent_list_events = true;
+    m_objects_ctrl->UnselectAll();
+    part_selection_changed();
+    g_prevent_list_events = false;
+}
+
+void select_current_object(int idx)
+{
+	g_prevent_list_events = true;
+	m_objects_ctrl->UnselectAll();
+	if (idx < 0) {
+		g_prevent_list_events = false;
+		return;
+	}
+	m_objects_ctrl->Select(m_objects_model->GetItemById(idx));
+	part_selection_changed();
+	g_prevent_list_events = false;
+}
+
+void remove()
+{
+	auto item = m_objects_ctrl->GetSelection();
+	if (!item)
+		return;
+	
+	if (m_objects_model->GetParent(item) == wxDataViewItem(0)) {
+		if (m_event_remove_object > 0) {
+			wxCommandEvent event(m_event_remove_object);
+			get_main_frame()->ProcessWindowEvent(event);
+		}
+// 		delete_object_from_list();
+	}
+	else
+		on_btn_del();
+}
+
+void object_ctrl_selection_changed()
+{
+	if (g_prevent_list_events) return;
+
+	part_selection_changed();
+
+	if (m_event_object_selection_changed > 0) {
+		wxCommandEvent event(m_event_object_selection_changed);
+		event.SetInt(int(m_objects_model->GetParent(m_objects_ctrl->GetSelection()) != wxDataViewItem(0)));
+		event.SetId(m_selected_object_id);
+		get_main_frame()->ProcessWindowEvent(event);
+	}
+
+#ifdef __WXOSX__
+    update_extruder_in_config(g_selected_extruder);
+#endif //__WXOSX__        
+}
+
+void object_ctrl_context_menu()
+{
+    wxDataViewItem item;
+    wxDataViewColumn* col;
+    m_objects_ctrl->HitTest(get_mouse_position_in_control(), item, col);
+    wxString title = col->GetTitle();
+    if (!item) return;
+
+    if (title == " ")
+        show_context_menu();
+// ys_FIXME
+//         else if (title == _("Name") && pt.x >15 &&
+//                     m_objects_model->GetIcon(item).GetRefData() == m_icon_manifold_warning.GetRefData())
+//         {
+//             if (is_windows10())
+//                 fix_through_netfabb();
+//         }
+#ifndef __WXMSW__
+    m_objects_ctrl->GetMainWindow()->SetToolTip(""); // hide tooltip
+#endif //__WXMSW__
+}
+
+void object_ctrl_key_event(wxKeyEvent& event)
+{
+    if (event.GetKeyCode() == WXK_TAB)
+        m_objects_ctrl->Navigate(event.ShiftDown() ? wxNavigationKeyEvent::IsBackward : wxNavigationKeyEvent::IsForward);
+    else if (event.GetKeyCode() == WXK_DELETE
+#ifdef __WXOSX__
+        || event.GetKeyCode() == WXK_BACK
+#endif //__WXOSX__
+        ){
+        printf("WXK_BACK\n");
+        remove();
+    }
+    else
+        event.Skip();
+}
+
+void object_ctrl_item_value_change(wxDataViewEvent& event)
+{
+    if (event.GetColumn() == 3)
+    {
+        wxVariant variant;
+        m_objects_model->GetValue(variant, event.GetItem(), 3);
+#ifdef __WXOSX__
+        g_selected_extruder = variant.GetString();
+#else // --> for Linux
+        update_extruder_in_config(variant.GetString());
+#endif //__WXOSX__  
+    }
+}
+
+//update_optgroup
+void update_settings_list()
+{
+#ifdef __WXGTK__
+    auto parent = get_optgroup(ogFrequentlyObjectSettings)->get_parent();
+#else
+    auto parent = get_optgroup(ogFrequentlyObjectSettings)->parent();
+#endif /* __WXGTK__ */
+    
+// There is a bug related to Ubuntu overlay scrollbars, see https://github.com/prusa3d/Slic3r/issues/898 and https://github.com/prusa3d/Slic3r/issues/952.
+// The issue apparently manifests when Show()ing a window with overlay scrollbars while the UI is frozen. For this reason,
+// we will Thaw the UI prematurely on Linux. This means destroing the no_updates object prematurely.
+#ifdef __linux__
+	std::unique_ptr<wxWindowUpdateLocker> no_updates(new wxWindowUpdateLocker(parent));
+#else
+	wxWindowUpdateLocker noUpdates(parent);
+#endif
+
+	m_option_sizer->Clear(true);
+
+	if (m_config) 
+	{
+        auto extra_column = [](wxWindow* parent, const Line& line)
+		{
+			auto opt_key = (line.get_options())[0].opt_id;  //we assume that we have one option per line
+
+			auto btn = new wxBitmapButton(parent, wxID_ANY, wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("erase.png")), wxBITMAP_TYPE_PNG),
+				wxDefaultPosition, wxDefaultSize, wxBORDER_NONE);
+			btn->Bind(wxEVT_BUTTON, [opt_key](wxEvent &event){
+				(*m_config)->erase(opt_key);
+				wxTheApp->CallAfter([]() { update_settings_list(); });
+			});
+			return btn;
+		};
+
+		std::map<std::string, std::vector<std::string>> cat_options;
+		auto opt_keys = (*m_config)->keys();
+		if (opt_keys.size() == 1 && opt_keys[0] == "extruder")
+			return;
+
+        auto extruders_cnt = get_preset_bundle()->printers.get_selected_preset().printer_technology() == ptSLA ? 1 :
+                             get_preset_bundle()->printers.get_edited_preset().config.option<ConfigOptionFloats>("nozzle_diameter")->values.size();
+
+		for (auto& opt_key : opt_keys) {
+			auto category = (*m_config)->def()->get(opt_key)->category;
+			if (category.empty() ||
+                (category == "Extruders" && extruders_cnt==1)) continue;
+
+			std::vector< std::string > new_category;
+
+			auto& cat_opt = cat_options.find(category) == cat_options.end() ? new_category : cat_options.at(category);
+			cat_opt.push_back(opt_key);
+			if (cat_opt.size() == 1)
+				cat_options[category] = cat_opt;
+		}
+
+
+		m_og_settings.resize(0);
+		for (auto& cat : cat_options) {
+			if (cat.second.size() == 1 && cat.second[0] == "extruder")
+				continue;
+
+			auto optgroup = std::make_shared<ConfigOptionsGroup>(parent, cat.first, *m_config, false, ogDEFAULT, extra_column);
+			optgroup->label_width = 100;
+			optgroup->sidetext_width = 70;
+
+			for (auto& opt : cat.second)
+			{
+				if (opt == "extruder")
+					continue;
+				Option option = optgroup->get_option(opt);
+				option.opt.width = 70;
+				optgroup->append_single_option_line(option);
+			}
+			optgroup->reload_config();
+			m_option_sizer->Add(optgroup->sizer, 0, wxEXPAND | wxALL, 0);
+			m_og_settings.push_back(optgroup);
+		}
+	}
+
+#ifdef __linux__
+	no_updates.reset(nullptr);
+#endif
+
+    get_right_panel()->Layout();
+	get_right_panel()->GetParent()->Layout();
+}
+
+void get_settings_choice(wxMenu *menu, int id, bool is_part)
+{
+	auto category_name = menu->GetLabel(id);
+
+	wxArrayString names;
+	wxArrayInt selections;
+
+	settings_menu_hierarchy settings_menu;
+	get_options_menu(settings_menu, is_part);
+	std::vector< std::pair<std::string, std::string> > *settings_list = nullptr;
+
+	auto opt_keys = (*m_config)->keys();
+
+	for (auto& cat : settings_menu)
+	{
+		if (_(cat.first) == category_name) {
+			int sel = 0;
+			for (auto& pair : cat.second) {
+				names.Add(_(pair.second));
+				if (find(opt_keys.begin(), opt_keys.end(), pair.first) != opt_keys.end())
+					selections.Add(sel);
+				sel++;
+			}
+			settings_list = &cat.second;
+			break;
+		}
+	} 
+
+	if (!settings_list)
+		return;
+
+	if (wxGetMultipleChoices(selections, _(L("Select showing settings")), category_name, names) ==0 )
+		return;
+
+	std::vector <std::string> selected_options;
+	for (auto sel : selections)
+		selected_options.push_back((*settings_list)[sel].first);
+
+	for (auto& setting:(*settings_list) )
+	{
+		auto& opt_key = setting.first;
+		if (find(opt_keys.begin(), opt_keys.end(), opt_key) != opt_keys.end() &&
+			find(selected_options.begin(), selected_options.end(), opt_key) == selected_options.end())
+			(*m_config)->erase(opt_key);
+		
+		if(find(opt_keys.begin(), opt_keys.end(), opt_key) == opt_keys.end() &&
+				find(selected_options.begin(), selected_options.end(), opt_key) != selected_options.end())
+			(*m_config)->set_key_value(opt_key, m_default_config.get()->option(opt_key)->clone());
+	}
+
+	update_settings_list();
+}
+
+bool cur_item_hase_children()
+{
+    wxDataViewItemArray children;
+    if (m_objects_model->GetChildren(m_objects_ctrl->GetSelection(), children) > 0)
+        return true;
+    return false;
+}
+
+wxMenuItem* menu_item_split(wxMenu* menu, int id) {
+    auto menu_item = new wxMenuItem(menu, id, _(L("Split to parts")));
+    menu_item->SetBitmap(m_bmp_split);
+    return menu_item;
+}
+
+wxMenuItem* menu_item_settings(wxMenu* menu, int id) {
+    auto  menu_item = new wxMenuItem(menu, id, _(L("Add settings")));
+    menu_item->SetBitmap(m_bmp_cog);
+
+    auto sub_menu = create_add_settings_popupmenu(false);
+    menu_item->SetSubMenu(sub_menu);
+    return menu_item;
+}
+
+wxMenu *create_add_part_popupmenu()
+{
+	wxMenu *menu = new wxMenu;
+	std::vector<std::string> menu_items = { L("Add part"), L("Add modifier"), L("Add generic") };
+
+	wxWindowID config_id_base = wxWindow::NewControlId(menu_items.size()+2);
+
+	int i = 0;
+	for (auto& item : menu_items) {
+		auto menu_item = new wxMenuItem(menu, config_id_base + i, _(item));
+		menu_item->SetBitmap(i == 0 ? m_icon_solidmesh : m_icon_modifiermesh);
+		menu->Append(menu_item);
+		i++;
+    }
+
+    menu->AppendSeparator();
+    auto menu_item = menu_item_split(menu, config_id_base + i);
+    menu->Append(menu_item);
+    menu_item->Enable(!cur_item_hase_children());
+
+    menu->AppendSeparator();
+    // Append settings popupmenu
+    menu->Append(menu_item_settings(menu, config_id_base + i + 1));
+
+	wxWindow* win = get_tab_panel()->GetPage(0);
+
+	menu->Bind(wxEVT_MENU, [config_id_base, win, menu](wxEvent &event){
+		switch (event.GetId() - config_id_base) {
+		case 0:
+			on_btn_load(win);
+			break;
+		case 1:
+			on_btn_load(win, true);
+			break;
+		case 2:
+			on_btn_load(win, true, true);
+			break;
+		case 3:
+			on_btn_split(false);
+			break;
+		default:{
+			get_settings_choice(menu, event.GetId(), false);
+			break;}
+		}
+	});
+
+	return menu;
+}
+
+wxMenu *create_part_settings_popupmenu()
+{
+    wxMenu *menu = new wxMenu;
+    wxWindowID config_id_base = wxWindow::NewControlId(2);
+
+    menu->Append(menu_item_split(menu, config_id_base));
+
+    menu->AppendSeparator();
+    // Append settings popupmenu
+    menu->Append(menu_item_settings(menu, config_id_base + 1));
+
+    menu->Bind(wxEVT_MENU, [config_id_base, menu](wxEvent &event){
+        switch (event.GetId() - config_id_base) {
+        case 0:
+            on_btn_split(true);
+            break;
+        default:{
+            get_settings_choice(menu, event.GetId(), true);
+            break; }
+        }
+    });
+
+    return menu;
+}
+
+wxMenu *create_add_settings_popupmenu(bool is_part)
+{
+	wxMenu *menu = new wxMenu;
+
+ 	auto categories = get_category_icon();
+
+	settings_menu_hierarchy settings_menu;
+	get_options_menu(settings_menu, is_part);
+
+	for (auto cat : settings_menu)
+	{
+		auto menu_item = new wxMenuItem(menu, wxID_ANY, _(cat.first));
+		menu_item->SetBitmap(categories.find(cat.first) == categories.end() ? 
+								wxNullBitmap : categories.at(cat.first));
+		menu->Append(menu_item);
+	}
+
+	menu->Bind(wxEVT_MENU, [menu](wxEvent &event) {
+		get_settings_choice(menu, event.GetId(), true);
+	});
+
+	return menu;
+}
+
+void show_context_menu()
+{
+    auto item = m_objects_ctrl->GetSelection();
+    if (item)
+    {
+        if (m_objects_model->GetParent(item) == wxDataViewItem(0))				{
+            auto menu = create_add_part_popupmenu();
+            get_tab_panel()->GetPage(0)->PopupMenu(menu);
+        }
+        else {
+            auto menu = create_part_settings_popupmenu();
+            get_tab_panel()->GetPage(0)->PopupMenu(menu);
+        }
+    }
+}
+
+// ******
+
+void load_part(	wxWindow* parent, ModelObject* model_object, 
+				wxArrayString& part_names, const bool is_modifier)
+{
+	wxArrayString input_files;
+	open_model(parent, input_files);
+	for (int i = 0; i < input_files.size(); ++i) {
+		std::string input_file = input_files.Item(i).ToStdString();
+
+		Model model;
+		try {
+			model = Model::read_from_file(input_file);
+		}
+		catch (std::exception &e) {
+			auto msg = _(L("Error! ")) + input_file + " : " + e.what() + ".";
+			show_error(parent, msg);
+			exit(1);
+		}
+
+		for ( auto object : model.objects) {
+			for (auto volume : object->volumes) {
+				auto new_volume = model_object->add_volume(*volume);
+				new_volume->modifier = is_modifier;
+				boost::filesystem::path(input_file).filename().string();
+				new_volume->name = boost::filesystem::path(input_file).filename().string();
+
+				part_names.Add(new_volume->name);
+
+				// apply the same translation we applied to the object
+				new_volume->mesh.translate( model_object->origin_translation(0),
+											model_object->origin_translation(1), 
+											model_object->origin_translation(2) );
+				// set a default extruder value, since user can't add it manually
+				new_volume->config.set_key_value("extruder", new ConfigOptionInt(0));
+
+				m_parts_changed = true;
+			}
+		}
+	}
+}
+
+void load_lambda(	wxWindow* parent, ModelObject* model_object,
+					wxArrayString& part_names, const bool is_modifier)
+{
+	auto dlg = new LambdaObjectDialog(parent);
+	if (dlg->ShowModal() == wxID_CANCEL) {
+		return;
+	}
+
+	std::string name = "lambda-";
+	TriangleMesh mesh;
+
+	auto params = dlg->ObjectParameters();
+	switch (params.type)
+	{
+	case LambdaTypeBox:{
+		mesh = make_cube(params.dim[0], params.dim[1], params.dim[2]);
+		name += "Box";
+		break;}
+	case LambdaTypeCylinder:{
+		mesh = make_cylinder(params.cyl_r, params.cyl_h);
+		name += "Cylinder";
+		break;}
+	case LambdaTypeSphere:{
+		mesh = make_sphere(params.sph_rho);
+		name += "Sphere";
+		break;}
+	case LambdaTypeSlab:{
+		const auto& size = model_object->bounding_box().size();
+        mesh = make_cube(size(0)*1.5, size(1)*1.5, params.slab_h);
+		// box sets the base coordinate at 0, 0, move to center of plate and move it up to initial_z
+        mesh.translate(-size(0)*1.5 / 2.0, -size(1)*1.5 / 2.0, params.slab_z);
+		name += "Slab";
+		break; }
+	default:
+		break;
+	}
+	mesh.repair();
+
+	auto new_volume = model_object->add_volume(mesh);
+	new_volume->modifier = is_modifier;
+	new_volume->name = name;
+	// set a default extruder value, since user can't add it manually
+	new_volume->config.set_key_value("extruder", new ConfigOptionInt(0));
+
+	part_names.Add(name);
+
+	m_parts_changed = true;
+}
+
+void on_btn_load(wxWindow* parent, bool is_modifier /*= false*/, bool is_lambda/* = false*/)
+{
+	auto item = m_objects_ctrl->GetSelection();
+	if (!item)
+		return;
+	int obj_idx = -1;
+	if (m_objects_model->GetParent(item) == wxDataViewItem(0))
+		obj_idx = m_objects_model->GetIdByItem(item);
+	else
+		return;
+
+	if (obj_idx < 0) return;
+	wxArrayString part_names;
+	if (is_lambda)
+		load_lambda(parent, (*m_objects)[obj_idx], part_names, is_modifier);
+	else
+		load_part(parent, (*m_objects)[obj_idx], part_names, is_modifier);
+
+	parts_changed(obj_idx);
+
+	for (int i = 0; i < part_names.size(); ++i)
+		m_objects_ctrl->Select(	m_objects_model->AddChild(item, part_names.Item(i), 
+								is_modifier ? m_icon_modifiermesh : m_icon_solidmesh));
+// 	part_selection_changed();
+#ifdef __WXMSW__
+	object_ctrl_selection_changed();
+#endif //__WXMSW__
+}
+
+void on_btn_del()
+{
+	auto item = m_objects_ctrl->GetSelection();
+	if (!item) return;
+
+	auto volume_id = m_objects_model->GetVolumeIdByItem(item);
+	if (volume_id < 0)
+		return;
+	auto volume = (*m_objects)[m_selected_object_id]->volumes[volume_id];
+
+	// if user is deleting the last solid part, throw error
+	int solid_cnt = 0;
+	for (auto vol : (*m_objects)[m_selected_object_id]->volumes)
+		if (!vol->modifier)
+			++solid_cnt;
+	if (!volume->modifier && solid_cnt == 1) {
+		Slic3r::GUI::show_error(nullptr, _(L("You can't delete the last solid part from this object.")));
+		return;
+	}
+
+	(*m_objects)[m_selected_object_id]->delete_volume(volume_id);
+	m_parts_changed = true;
+
+	parts_changed(m_selected_object_id);
+
+	m_objects_ctrl->Select(m_objects_model->Delete(item));
+	part_selection_changed();
+// #ifdef __WXMSW__
+// 	object_ctrl_selection_changed();
+// #endif //__WXMSW__
+}
+
+void on_btn_split(const bool split_part)
+{
+	auto item = m_objects_ctrl->GetSelection();
+	if (!item || m_selected_object_id<0)
+		return;
+	auto volume_id = m_objects_model->GetVolumeIdByItem(item);
+    ModelVolume* volume;
+    if (volume_id < 0) {
+        if (split_part) return;
+        else
+            volume = (*m_objects)[m_selected_object_id]->volumes[0]; }
+    else
+	    volume = (*m_objects)[m_selected_object_id]->volumes[volume_id];
+ 	DynamicPrintConfig&	config = get_preset_bundle()->printers.get_edited_preset().config;
+    auto nozzle_dmrs_cnt = config.option<ConfigOptionFloats>("nozzle_diameter")->values.size();
+    auto split_rez = volume->split(nozzle_dmrs_cnt);
+    if (split_rez == 1) {
+        wxMessageBox(_(L("The selected object couldn't be split because it contains only one part.")));
+        return;
+    }
+
+    auto model_object = (*m_objects)[m_selected_object_id];
+
+    if (split_part) {
+        auto parent = m_objects_model->GetParent(item);
+        m_objects_model->DeleteChildren(parent);
+
+        for (auto id = 0; id < model_object->volumes.size(); id++)
+            m_objects_model->AddChild(parent, model_object->volumes[id]->name,
+                                      model_object->volumes[id]->modifier ? m_icon_modifiermesh : m_icon_solidmesh,
+                                      model_object->volumes[id]->config.option<ConfigOptionInt>("extruder")->value, 
+                                      false);
+
+        m_objects_ctrl->Expand(parent);
+    }
+    else {
+        for (auto id = 0; id < model_object->volumes.size(); id++)
+            m_objects_model->AddChild(item, model_object->volumes[id]->name, 
+                                      m_icon_solidmesh,
+                                      model_object->volumes[id]->config.option<ConfigOptionInt>("extruder")->value, 
+                                      false);
+        m_objects_ctrl->Expand(item);
+    }
+}
+
+void on_btn_move_up(){
+	auto item = m_objects_ctrl->GetSelection();
+	if (!item)
+		return;
+	auto volume_id = m_objects_model->GetVolumeIdByItem(item);
+	if (volume_id < 0)
+		return;
+	auto& volumes = (*m_objects)[m_selected_object_id]->volumes;
+	if (0 < volume_id && volume_id < volumes.size()) {
+		std::swap(volumes[volume_id - 1], volumes[volume_id]);
+		m_parts_changed = true;
+		m_objects_ctrl->Select(m_objects_model->MoveChildUp(item));
+		part_selection_changed();
+// #ifdef __WXMSW__
+// 		object_ctrl_selection_changed();
+// #endif //__WXMSW__
+	}
+}
+
+void on_btn_move_down(){
+	auto item = m_objects_ctrl->GetSelection();
+	if (!item)
+		return;
+	auto volume_id = m_objects_model->GetVolumeIdByItem(item);
+	if (volume_id < 0)
+		return;
+	auto& volumes = (*m_objects)[m_selected_object_id]->volumes;
+	if (0 <= volume_id && volume_id+1 < volumes.size()) {
+		std::swap(volumes[volume_id + 1], volumes[volume_id]);
+		m_parts_changed = true;
+		m_objects_ctrl->Select(m_objects_model->MoveChildDown(item));
+		part_selection_changed();
+// #ifdef __WXMSW__
+// 		object_ctrl_selection_changed();
+// #endif //__WXMSW__
+	}
+}
+
+void parts_changed(int obj_idx)
+{ 
+	if (m_event_object_settings_changed <= 0) return;
+
+	wxCommandEvent e(m_event_object_settings_changed);
+	auto event_str = wxString::Format("%d %d %d", obj_idx,
+		is_parts_changed() ? 1 : 0,
+		is_part_settings_changed() ? 1 : 0);
+	e.SetString(event_str);
+	get_main_frame()->ProcessWindowEvent(e);
+}
+	
+void update_settings_value()
+{
+	auto og = get_optgroup(ogFrequentlyObjectSettings);
+	if (m_selected_object_id < 0 || m_objects->size() <= m_selected_object_id) {
+		og->set_value("scale_x", 0);
+		og->set_value("scale_y", 0);
+		og->set_value("scale_z", 0);
+        og->disable();
+		return;
+	}
+    g_is_percent_scale = boost::any_cast<wxString>(og->get_value("scale_unit")) == _("%");
+    update_scale_values();
+    update_rotation_values();
+    og->enable();
+}
+
+void part_selection_changed()
+{
+	auto item = m_objects_ctrl->GetSelection();
+	int obj_idx = -1;
+	auto og = get_optgroup(ogFrequentlyObjectSettings);
+	if (item)
+	{
+		bool is_part = false;
+		if (m_objects_model->GetParent(item) == wxDataViewItem(0)) {
+			obj_idx = m_objects_model->GetIdByItem(item);
+			og->set_name(" " + _(L("Object Settings")) + " ");
+			m_config = std::make_shared<DynamicPrintConfig*>(&(*m_objects)[obj_idx]->config);
+		}
+		else {
+			auto parent = m_objects_model->GetParent(item);
+			// Take ID of the parent object to "inform" perl-side which object have to be selected on the scene
+			obj_idx = m_objects_model->GetIdByItem(parent);
+			og->set_name(" " + _(L("Part Settings")) + " ");
+			is_part = true;
+			auto volume_id = m_objects_model->GetVolumeIdByItem(item);
+			m_config = std::make_shared<DynamicPrintConfig*>(&(*m_objects)[obj_idx]->volumes[volume_id]->config);
+		}
+
+		auto config = m_config;
+        og->set_value("object_name", m_objects_model->GetName(item));
+		m_default_config = std::make_shared<DynamicPrintConfig>(*DynamicPrintConfig::new_from_defaults_keys(get_options(is_part)));
+	}
+    else {
+        wxString empty_str = wxEmptyString;
+        og->set_value("object_name", empty_str);
+        m_config = nullptr;
+    }
+
+	update_settings_list();
+
+	m_selected_object_id = obj_idx;
+
+	update_settings_value();
+
+/*	wxWindowUpdateLocker noUpdates(get_right_panel());
+
+	m_move_options = Point3(0, 0, 0);
+	m_last_coords = Point3(0, 0, 0);
+	// reset move sliders
+	std::vector<std::string> opt_keys = {"x", "y", "z"};
+	auto og = get_optgroup(ogObjectMovers);
+	for (auto opt_key: opt_keys)
+		og->set_value(opt_key, int(0));
+
+// 	if (!item || m_selected_object_id < 0){
+	if (m_selected_object_id < 0){
+		m_sizer_object_buttons->Show(false);
+		m_sizer_part_buttons->Show(false);
+		m_sizer_object_movers->Show(false);
+		m_collpane_settings->Show(false);
+		return;
+	}
+
+	m_collpane_settings->Show(true);
+
+	auto volume_id = m_objects_model->GetVolumeIdByItem(item);
+	if (volume_id < 0){
+		m_sizer_object_buttons->Show(true);
+		m_sizer_part_buttons->Show(false);
+		m_sizer_object_movers->Show(false);
+		m_collpane_settings->SetLabelText(_(L("Object Settings")) + ":");
+
+// 		elsif($itemData->{type} eq 'object') {
+// 			# select nothing in 3D preview
+// 
+// 			# attach object config to settings panel
+// 			$self->{optgroup_movers}->disable;
+// 			$self->{staticbox}->SetLabel('Object Settings');
+// 			@opt_keys = (map @{$_->get_keys}, Slic3r::Config::PrintObject->new, Slic3r::Config::PrintRegion->new);
+// 			$config = $self->{model_object}->config;
+// 		}
+
+		return;
+	}
+
+	m_collpane_settings->SetLabelText(_(L("Part Settings")) + ":");
+	
+	m_sizer_object_buttons->Show(false);
+	m_sizer_part_buttons->Show(true);
+	m_sizer_object_movers->Show(true);
+
+	auto bb_size = m_objects[m_selected_object_id]->bounding_box().size();
+	int scale = 10; //??
+
+	m_mover_x->SetMin(-bb_size.x * 4 * scale);
+	m_mover_x->SetMax(bb_size.x * 4 * scale);
+
+	m_mover_y->SetMin(-bb_size.y * 4 * scale);
+	m_mover_y->SetMax(bb_size.y * 4 * scale);
+
+	m_mover_z->SetMin(-bb_size.z * 4 * scale);
+	m_mover_z->SetMax(bb_size.z * 4 * scale);
+
+
+	
+//	my ($config, @opt_keys);
+	m_btn_move_up->Enable(volume_id > 0);
+	m_btn_move_down->Enable(volume_id + 1 < m_objects[m_selected_object_id]->volumes.size());
+
+	// attach volume config to settings panel
+	auto volume = m_objects[m_selected_object_id]->volumes[volume_id];
+
+	if (volume->modifier) 
+		og->enable();
+	else 
+		og->disable();
+
+//	auto config = volume->config;
+
+	// get default values
+// 	@opt_keys = @{Slic3r::Config::PrintRegion->new->get_keys};
+// 	} 
+/*	
+	# get default values
+	my $default_config = Slic3r::Config::new_from_defaults_keys(\@opt_keys);
+
+	# append default extruder
+	push @opt_keys, 'extruder';
+	$default_config->set('extruder', 0);
+	$config->set_ifndef('extruder', 0);
+	$self->{settings_panel}->set_default_config($default_config);
+	$self->{settings_panel}->set_config($config);
+	$self->{settings_panel}->set_opt_keys(\@opt_keys);
+	$self->{settings_panel}->set_fixed_options([qw(extruder)]);
+	$self->{settings_panel}->enable;
+	}
+	 */
+}
+
+void set_extruder_column_hidden(bool hide)
+{
+	m_objects_ctrl->GetColumn(3)->SetHidden(hide);
+}
+
+void update_extruder_in_config(const wxString& selection)
+{
+    if (!m_config || selection.empty())
+        return;
+
+    int extruder = selection.size() > 1 ? 0 : atoi(selection.c_str());
+    (*m_config)->set_key_value("extruder", new ConfigOptionInt(extruder));
+
+    if (m_event_update_scene > 0) {
+        wxCommandEvent e(m_event_update_scene);
+        get_main_frame()->ProcessWindowEvent(e);
+    }
+}
+
+void update_scale_values()
+{
+    update_scale_values((*m_objects)[m_selected_object_id]->instance_bounding_box(0).size(),
+                        (*m_objects)[m_selected_object_id]->instances[0]->scaling_factor);
+}
+
+void update_scale_values(const Vec3d& size, float scaling_factor)
+{
+    auto og = get_optgroup(ogFrequentlyObjectSettings);
+
+    if (g_is_percent_scale) {
+        auto scale = scaling_factor * 100;
+        og->set_value("scale_x", int(scale));
+        og->set_value("scale_y", int(scale));
+        og->set_value("scale_z", int(scale));
+    }
+    else {
+        og->set_value("scale_x", int(size(0) + 0.5));
+        og->set_value("scale_y", int(size(1) + 0.5));
+        og->set_value("scale_z", int(size(2) + 0.5));
+    }
+}
+
+void update_rotation_values()
+{
+    auto og = get_optgroup(ogFrequentlyObjectSettings);
+
+    og->set_value("rotation_x", 0);
+    og->set_value("rotation_y", 0);
+
+    auto rotation_z = (*m_objects)[m_selected_object_id]->instances[0]->rotation;
+    auto deg = int(Geometry::rad2deg(rotation_z));
+//     if (deg > 180) deg -= 360;
+
+    og->set_value("rotation_z", deg);
+}
+
+void update_rotation_value(const double angle, const std::string& axis)
+{
+    auto og = get_optgroup(ogFrequentlyObjectSettings);
+    
+    int deg = int(Geometry::rad2deg(angle));
+//     if (deg>180) deg -= 360;
+
+    og->set_value("rotation_"+axis, deg);
+}
+
+void on_begin_drag(wxDataViewEvent &event)
+{
+    wxDataViewItem item(event.GetItem());
+
+    // only allow drags for item, not containers
+    if (m_objects_model->GetParent(item) == wxDataViewItem(0)) {
+        event.Veto();
+        return;
+    }
+
+    /* Under MSW or OSX, DnD moves an item to the place of another selected item
+     * But under GTK, DnD moves an item between another two items.
+     * And as a result - call EVT_CHANGE_SELECTION to unselect all items.
+     * To prevent such behavior use g_prevent_list_events
+    **/
+    g_prevent_list_events = true;//it's needed for GTK
+
+    wxTextDataObject *obj = new wxTextDataObject;
+    obj->SetText(wxString::Format("%d", m_objects_model->GetVolumeIdByItem(item)));
+    event.SetDataObject(obj);
+    event.SetDragFlags(/*wxDrag_AllowMove*/wxDrag_DefaultMove); // allows both copy and move;
+}
+
+void on_drop_possible(wxDataViewEvent &event)
+{
+    wxDataViewItem item(event.GetItem());
+
+    // only allow drags for item or background, not containers
+    if (item.IsOk() && m_objects_model->GetParent(item) == wxDataViewItem(0) ||
+        event.GetDataFormat() != wxDF_UNICODETEXT)
+        event.Veto();
+}
+
+void on_drop(wxDataViewEvent &event)
+{
+    wxDataViewItem item(event.GetItem());
+
+    // only allow drops for item, not containers
+    if (item.IsOk() && m_objects_model->GetParent(item) == wxDataViewItem(0) ||
+        event.GetDataFormat() != wxDF_UNICODETEXT) {
+        event.Veto();
+        return;
+    }    
+
+    wxTextDataObject obj;
+    obj.SetData(wxDF_UNICODETEXT, event.GetDataSize(), event.GetDataBuffer());
+
+    int from_volume_id = std::stoi(obj.GetText().ToStdString());
+    int to_volume_id = m_objects_model->GetVolumeIdByItem(item);
+
+#ifdef __WXGTK__
+    /* Under GTK, DnD moves an item between another two items.
+     * And event.GetItem() return item, which is under "insertion line"
+     * So, if we move item down we should to decrease the to_volume_id value
+    **/
+    if (to_volume_id > from_volume_id) to_volume_id--;
+#endif // __WXGTK__
+
+    m_objects_ctrl->Select(m_objects_model->ReorganizeChildren(from_volume_id, to_volume_id,
+                                                               m_objects_model->GetParent(item)));
+
+    auto& volumes = (*m_objects)[m_selected_object_id]->volumes;
+    auto delta = to_volume_id < from_volume_id ? -1 : 1;
+    int cnt = 0;
+    for (int id = from_volume_id; cnt < abs(from_volume_id - to_volume_id); id+=delta, cnt++)
+        std::swap(volumes[id], volumes[id +delta]);
+
+    g_prevent_list_events = false;
+}
+
+void update_objects_list_extruder_column(int extruders_count)
+{
+    if (get_preset_bundle()->printers.get_selected_preset().printer_technology() == ptSLA)
+        extruders_count = 1;
+
+    // delete old 3rd column
+    m_objects_ctrl->DeleteColumn(m_objects_ctrl->GetColumn(3));
+    // insert new created 3rd column
+    m_objects_ctrl->InsertColumn(3, object_ctrl_create_extruder_column(extruders_count));
+    // set show/hide for this column 
+    set_extruder_column_hidden(extruders_count <= 1);
+}
+
+} //namespace GUI
+} //namespace Slic3r 
\ No newline at end of file
diff --git a/xs/src/slic3r/GUI/GUI_ObjectParts.hpp b/xs/src/slic3r/GUI/GUI_ObjectParts.hpp
new file mode 100644
index 000000000..40dfb9279
--- /dev/null
+++ b/xs/src/slic3r/GUI/GUI_ObjectParts.hpp
@@ -0,0 +1,126 @@
+#ifndef slic3r_GUI_ObjectParts_hpp_
+#define slic3r_GUI_ObjectParts_hpp_
+
+class wxWindow;
+class wxSizer;
+class wxBoxSizer;
+class wxString;
+class wxArrayString;
+class wxMenu;
+class wxDataViewEvent;
+class wxKeyEvent;
+
+namespace Slic3r {
+class ModelObject;
+class Model;
+
+namespace GUI {
+
+enum ogGroup{
+	ogFrequentlyChangingParameters,
+	ogFrequentlyObjectSettings,
+	ogCurrentSettings
+// 	ogObjectSettings,
+// 	ogObjectMovers,
+// 	ogPartSettings
+};
+
+enum LambdaTypeIDs{
+	LambdaTypeBox,
+	LambdaTypeCylinder,
+	LambdaTypeSphere,
+	LambdaTypeSlab
+};
+
+struct OBJECT_PARAMETERS
+{
+	LambdaTypeIDs	type = LambdaTypeBox;
+	double			dim[3];// = { 1.0, 1.0, 1.0 };
+	int				cyl_r = 1;
+	int				cyl_h = 1;
+	double			sph_rho = 1.0;
+	double			slab_h = 1.0;
+	double			slab_z = 0.0;
+};
+
+void add_collapsible_panes(wxWindow* parent, wxBoxSizer* sizer);
+void add_objects_list(wxWindow* parent, wxBoxSizer* sizer);
+void add_object_settings(wxWindow* parent, wxBoxSizer* sizer);
+void show_collpane_settings(bool expert_mode);
+
+wxMenu *create_add_settings_popupmenu(bool is_part);
+wxMenu *create_add_part_popupmenu();
+wxMenu *create_part_settings_popupmenu();
+
+// Add object to the list
+//void add_object(const std::string &name);
+void add_object_to_list(const std::string &name, ModelObject* model_object);
+// Delete object from the list
+void delete_object_from_list();
+// Delete all objects from the list
+void delete_all_objects_from_list();
+// Set count of object on c++ side
+void set_object_count(int idx, int count);
+// Set object scale on c++ side
+void set_object_scale(int idx, int scale);
+// Unselect all objects in the list on c++ side
+void unselect_objects();
+// Select current object in the list on c++ side
+void select_current_object(int idx);
+// Remove objects/sub-object from the list
+void remove();
+
+void object_ctrl_selection_changed();
+void object_ctrl_context_menu();
+void object_ctrl_key_event(wxKeyEvent& event);
+void object_ctrl_item_value_change(wxDataViewEvent& event);
+void show_context_menu();
+
+void init_mesh_icons();
+void set_event_object_selection_changed(const int& event);
+void set_event_object_settings_changed(const int& event); 
+void set_event_remove_object(const int& event);
+void set_event_update_scene(const int& event);
+void set_objects_from_model(Model &model);
+
+bool is_parts_changed();
+bool is_part_settings_changed();
+
+void load_part(	wxWindow* parent, ModelObject* model_object, 
+				wxArrayString& part_names, const bool is_modifier); 
+
+void load_lambda(wxWindow* parent, ModelObject* model_object, 
+				wxArrayString& part_names, const bool is_modifier);
+
+void on_btn_load(wxWindow* parent, bool is_modifier = false, bool is_lambda = false);
+void on_btn_del();
+void on_btn_split(const bool split_part);
+void on_btn_move_up();
+void on_btn_move_down();
+
+void parts_changed(int obj_idx);
+void part_selection_changed();
+
+void update_settings_value();
+// show/hide "Extruder" column for Objects List
+void set_extruder_column_hidden(bool hide);
+// update extruder in current config
+void update_extruder_in_config(const wxString& selection);
+// update scale values after scale unit changing or "gizmos"
+void update_scale_values();
+void update_scale_values(const Vec3d& size, float scale);
+// update rotation values object selection changing
+void update_rotation_values();
+// update rotation value after "gizmos"
+void update_rotation_value(const double angle, const std::string& axis);
+
+void on_begin_drag(wxDataViewEvent &event);
+void on_drop_possible(wxDataViewEvent &event);
+void on_drop(wxDataViewEvent &event);
+
+// update extruder column for objects_ctrl according to extruders count
+void update_objects_list_extruder_column(int extruders_count);
+
+} //namespace GUI
+} //namespace Slic3r 
+#endif  //slic3r_GUI_ObjectParts_hpp_
\ No newline at end of file
diff --git a/xs/src/slic3r/GUI/LambdaObjectDialog.cpp b/xs/src/slic3r/GUI/LambdaObjectDialog.cpp
new file mode 100644
index 000000000..7543821c0
--- /dev/null
+++ b/xs/src/slic3r/GUI/LambdaObjectDialog.cpp
@@ -0,0 +1,176 @@
+#include "LambdaObjectDialog.hpp"
+
+#include <wx/window.h>
+#include <wx/button.h>
+#include "OptionsGroup.hpp"
+
+namespace Slic3r
+{
+namespace GUI
+{
+static wxString dots("…", wxConvUTF8);
+
+LambdaObjectDialog::LambdaObjectDialog(wxWindow* parent)
+{
+	Create(parent, wxID_ANY, _(L("Lambda Object")),
+		wxDefaultPosition, wxDefaultSize,
+		wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER);
+
+	// instead of double dim[3] = { 1.0, 1.0, 1.0 };
+	object_parameters.dim[0] = 1.0;
+	object_parameters.dim[1] = 1.0;
+	object_parameters.dim[2] = 1.0;
+
+	sizer = new wxBoxSizer(wxVERTICAL);
+
+	// modificator options
+	m_modificator_options_book = new wxChoicebook(	this, wxID_ANY, wxDefaultPosition, 
+													wxDefaultSize, wxCHB_TOP);
+	sizer->Add(m_modificator_options_book, 1, wxEXPAND| wxALL, 10);
+
+	auto optgroup = init_modificator_options_page(_(L("Box")));
+		optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value){
+			int opt_id =	opt_key == "l" ? 0 :
+							opt_key == "w" ? 1 : 
+							opt_key == "h" ? 2 : -1;
+			if (opt_id < 0) return;
+			object_parameters.dim[opt_id] = boost::any_cast<double>(value);
+		};
+
+		ConfigOptionDef def;
+		def.width = 70;
+		def.type = coFloat;
+		def.default_value = new ConfigOptionFloat{ 1.0 };
+		def.label = L("L");
+		Option option(def, "l");
+		optgroup->append_single_option_line(option);
+		
+		def.label = L("W");
+		option = Option(def, "w");
+		optgroup->append_single_option_line(option);
+		
+		def.label = L("H");
+		option = Option(def, "h");
+		optgroup->append_single_option_line(option);
+
+	optgroup = init_modificator_options_page(_(L("Cylinder")));
+		optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value){
+			int val = boost::any_cast<int>(value);
+			if (opt_key == "cyl_r")
+				object_parameters.cyl_r = val;
+			else if (opt_key == "cyl_h")
+				object_parameters.cyl_h = val;
+			else return;
+		};
+
+		def.type = coInt;
+		def.default_value = new ConfigOptionInt{ 1 };
+		def.label = L("Radius");
+		option = Option(def, "cyl_r");
+		optgroup->append_single_option_line(option);
+
+		def.label = L("Height");
+		option = Option(def, "cyl_h");
+		optgroup->append_single_option_line(option);
+
+	optgroup = init_modificator_options_page(_(L("Sphere")));
+		optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value){
+			if (opt_key == "sph_rho")
+				object_parameters.sph_rho = boost::any_cast<double>(value);
+			else return;
+		};
+
+		def.type = coFloat;
+		def.default_value = new ConfigOptionFloat{ 1.0 };
+		def.label = L("Rho");
+		option = Option(def, "sph_rho");
+		optgroup->append_single_option_line(option);
+
+	optgroup = init_modificator_options_page(_(L("Slab")));
+		optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value){
+			double val = boost::any_cast<double>(value);
+			if (opt_key == "slab_z")
+				object_parameters.slab_z = val;
+			else if (opt_key == "slab_h")
+				object_parameters.slab_h = val;
+			else return;
+		};
+
+		def.label = L("H");
+		option = Option(def, "slab_h");
+		optgroup->append_single_option_line(option);
+
+		def.label = L("Initial Z");
+		option = Option(def, "slab_z");
+		optgroup->append_single_option_line(option);
+
+	Bind(wxEVT_CHOICEBOOK_PAGE_CHANGED, ([this](wxCommandEvent e)
+	{
+		auto page_idx = m_modificator_options_book->GetSelection();
+		if (page_idx < 0) return;
+		switch (page_idx)
+		{
+		case 0:
+			object_parameters.type = LambdaTypeBox;
+			break;
+		case 1:
+			object_parameters.type = LambdaTypeCylinder;
+			break;
+		case 2:
+			object_parameters.type = LambdaTypeSphere;
+			break;
+		case 3:
+			object_parameters.type = LambdaTypeSlab;
+			break;
+		default:
+			break;
+		}
+	}));
+
+
+	auto button_sizer = CreateStdDialogButtonSizer(wxOK | wxCANCEL);
+
+	wxButton* btn_OK = static_cast<wxButton*>(FindWindowById(wxID_OK, this));
+	btn_OK->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) {
+		// validate user input
+		if (!CanClose())return;
+		EndModal(wxID_OK);
+		Destroy();
+	});
+
+	wxButton* btn_CANCEL = static_cast<wxButton*>(FindWindowById(wxID_CANCEL, this));
+	btn_CANCEL->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) {
+		// validate user input
+		if (!CanClose())return;
+		EndModal(wxID_CANCEL);
+		Destroy();
+	});
+
+	sizer->Add(button_sizer, 0, wxALIGN_CENTER_HORIZONTAL | wxBOTTOM, 10);
+
+	SetSizer(sizer);
+	sizer->Fit(this);
+	sizer->SetSizeHints(this);
+}
+
+// Called from the constructor.
+// Create a panel for a rectangular / circular / custom bed shape.
+ConfigOptionsGroupShp LambdaObjectDialog::init_modificator_options_page(wxString title){
+
+	auto panel = new wxPanel(m_modificator_options_book);
+
+	ConfigOptionsGroupShp optgroup;
+	optgroup = std::make_shared<ConfigOptionsGroup>(panel, _(L("Add")) + " " +title + " " +dots);
+	optgroup->label_width = 100;
+
+	m_optgroups.push_back(optgroup);
+
+	panel->SetSizerAndFit(optgroup->sizer);
+	m_modificator_options_book->AddPage(panel, title);
+
+	return optgroup;
+}
+
+
+} //namespace GUI
+} //namespace Slic3r 
diff --git a/xs/src/slic3r/GUI/LambdaObjectDialog.hpp b/xs/src/slic3r/GUI/LambdaObjectDialog.hpp
new file mode 100644
index 000000000..a70c12449
--- /dev/null
+++ b/xs/src/slic3r/GUI/LambdaObjectDialog.hpp
@@ -0,0 +1,35 @@
+#ifndef slic3r_LambdaObjectDialog_hpp_
+#define slic3r_LambdaObjectDialog_hpp_
+
+#include "GUI.hpp"
+
+#include <wx/dialog.h>
+#include <wx/sizer.h>
+#include <wx/choicebk.h>
+
+namespace Slic3r
+{
+namespace GUI
+{
+using ConfigOptionsGroupShp = std::shared_ptr<ConfigOptionsGroup>;
+class LambdaObjectDialog : public wxDialog
+{
+	wxChoicebook*	m_modificator_options_book;
+	std::vector <ConfigOptionsGroupShp>	m_optgroups;
+public:
+	LambdaObjectDialog(wxWindow* parent);
+	~LambdaObjectDialog(){}
+
+	bool CanClose() { return true; }	// ???
+	OBJECT_PARAMETERS& ObjectParameters(){ return object_parameters; }
+
+	ConfigOptionsGroupShp init_modificator_options_page(wxString title);
+	
+	// Note whether the window was already closed, so a pending update is not executed.
+	bool m_already_closed = false;
+	OBJECT_PARAMETERS object_parameters;
+	wxBoxSizer* sizer = nullptr;
+};
+} //namespace GUI
+} //namespace Slic3r 
+#endif  //slic3r_LambdaObjectDialog_hpp_
diff --git a/xs/src/slic3r/GUI/OptionsGroup.cpp b/xs/src/slic3r/GUI/OptionsGroup.cpp
index a2d6559a9..453a2f3f7 100644
--- a/xs/src/slic3r/GUI/OptionsGroup.cpp
+++ b/xs/src/slic3r/GUI/OptionsGroup.cpp
@@ -30,6 +30,7 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co
                 opt.gui_type.compare("i_enum_closed") == 0) {
 		m_fields.emplace(id, STDMOVE(Choice::Create<Choice>(parent(), opt, id)));
     } else if (opt.gui_type.compare("slider") == 0) {
+		m_fields.emplace(id, STDMOVE(SliderCtrl::Create<SliderCtrl>(parent(), opt, id)));
     } else if (opt.gui_type.compare("i_spin") == 0) { // Spinctrl
     } else if (opt.gui_type.compare("legend") == 0) { // StaticText
 		m_fields.emplace(id, STDMOVE(StaticText::Create<StaticText>(parent(), opt, id)));
@@ -88,17 +89,23 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co
 		if (!this->m_disabled)
 			this->back_to_sys_value(opt_id);
 	};
-	if (!m_show_modified_btns) {
-		field->m_Undo_btn->Hide();
-		field->m_Undo_to_sys_btn->Hide();
-	}
-//	if (nonsys_btn_icon != nullptr)
-//		field->set_nonsys_btn_icon(*nonsys_btn_icon);
     
 	// assign function objects for callbacks, etc.
     return field;
 }
 
+void OptionsGroup::add_undo_buttuns_to_sizer(wxSizer* sizer, const t_field& field)
+{
+	if (!m_show_modified_btns) {
+		field->m_Undo_btn->Hide();
+		field->m_Undo_to_sys_btn->Hide();
+		return;
+	}
+
+	sizer->Add(field->m_Undo_to_sys_btn, 0, wxALIGN_CENTER_VERTICAL);
+	sizer->Add(field->m_Undo_btn, 0, wxALIGN_CENTER_VERTICAL);
+}
+
 void OptionsGroup::append_line(const Line& line, wxStaticText**	colored_Label/* = nullptr*/) {
 //!    if (line.sizer != nullptr || (line.widget != nullptr && line.full_width > 0)){
 	if ( (line.sizer != nullptr || line.widget != nullptr) && line.full_width){
@@ -133,8 +140,7 @@ void OptionsGroup::append_line(const Line& line, wxStaticText**	colored_Label/*
 		const auto& field = build_field(option);
 
 		auto btn_sizer = new wxBoxSizer(wxHORIZONTAL);
-		btn_sizer->Add(field->m_Undo_to_sys_btn);
-		btn_sizer->Add(field->m_Undo_btn);
+		add_undo_buttuns_to_sizer(btn_sizer, field);
 		tmp_sizer->Add(btn_sizer, 0, wxEXPAND | wxALL, 0);
 		if (is_window_field(field))
 			tmp_sizer->Add(field->getWindow(), 0, wxEXPAND | wxALL, wxOSX ? 0 : 5);
@@ -149,6 +155,18 @@ void OptionsGroup::append_line(const Line& line, wxStaticText**	colored_Label/*
         m_panel->Layout();
 #endif /* __WXGTK__ */
 
+	// if we have an extra column, build it
+	if (extra_column) {
+		if (extra_column) {
+			grid_sizer->Add(extra_column(parent(), line), 0, wxALIGN_CENTER_VERTICAL, 0);
+		}
+		else {
+			// if the callback provides no sizer for the extra cell, put a spacer
+			grid_sizer->AddSpacer(1);
+		}
+	}
+
+
     // Build a label if we have it
 	wxStaticText* label=nullptr;
     if (label_width != 0) {
@@ -163,7 +181,8 @@ void OptionsGroup::append_line(const Line& line, wxStaticText**	colored_Label/*
 							wxDefaultPosition, wxSize(label_width, -1), label_style);
         label->SetFont(label_font);
         label->Wrap(label_width); // avoid a Linux/GTK bug
-		grid_sizer->Add(label, 0, (staticbox ? 0 : wxALIGN_RIGHT | wxRIGHT) | wxALIGN_CENTER_VERTICAL, 5);
+		grid_sizer->Add(label, 0, (staticbox ? 0 : wxALIGN_RIGHT | wxRIGHT) | 
+						(m_flag == ogSIDE_OPTIONS_VERTICAL ? wxTOP : wxALIGN_CENTER_VERTICAL), 5);
 		if (line.label_tooltip.compare("") != 0)
 			label->SetToolTip(line.label_tooltip);
     }
@@ -177,28 +196,35 @@ void OptionsGroup::append_line(const Line& line, wxStaticText**	colored_Label/*
 		return;
 	}
 	
-	// if we have a single option with no sidetext just add it directly to the grid sizer
-	auto sizer = new wxBoxSizer(wxHORIZONTAL);
-	grid_sizer->Add(sizer, 0, wxEXPAND | (staticbox ? wxALL : wxBOTTOM|wxTOP|wxLEFT), staticbox ? 0 : 1);
+	// If we're here, we have more than one option or a single option with sidetext
+    // so we need a horizontal sizer to arrange these things
+	auto sizer = new wxBoxSizer(m_flag == ogSIDE_OPTIONS_VERTICAL ? wxVERTICAL : wxHORIZONTAL);
+	grid_sizer->Add(sizer, 0, wxEXPAND | (staticbox ? wxALL : wxBOTTOM | wxTOP | wxLEFT), staticbox ? 0 : 1);
+	// If we have a single option with no sidetext just add it directly to the grid sizer
 	if (option_set.size() == 1 && option_set.front().opt.sidetext.size() == 0 &&
 		option_set.front().side_widget == nullptr && line.get_extra_widgets().size() == 0) {
 		const auto& option = option_set.front();
 		const auto& field = build_field(option, label);
 
-		sizer->Add(field->m_Undo_to_sys_btn, 0, wxALIGN_CENTER_VERTICAL); 
-		sizer->Add(field->m_Undo_btn, 0, wxALIGN_CENTER_VERTICAL);
+		add_undo_buttuns_to_sizer(sizer, field);
 		if (is_window_field(field)) 
 			sizer->Add(field->getWindow(), option.opt.full_width ? 1 : 0, (option.opt.full_width ? wxEXPAND : 0) |
 							wxBOTTOM | wxTOP | wxALIGN_CENTER_VERTICAL, (wxOSX||!staticbox) ? 0 : 2);
 		if (is_sizer_field(field)) 
-			sizer->Add(field->getSizer(), 0, (option.opt.full_width ? wxEXPAND : 0) | wxALIGN_CENTER_VERTICAL, 0);
+			sizer->Add(field->getSizer(), 1, (option.opt.full_width ? wxEXPAND : 0) | wxALIGN_CENTER_VERTICAL, 0);
 		return;
 	}
 
-    // if we're here, we have more than one option or a single option with sidetext
-    // so we need a horizontal sizer to arrange these things
-	for (auto opt : option_set) {
+    for (auto opt : option_set) {
 		ConfigOptionDef option = opt.opt;
+		wxSizer* sizer_tmp;
+		if (m_flag == ogSIDE_OPTIONS_VERTICAL){
+			auto sz = new wxFlexGridSizer(1, 3, 2, 2);
+			sz->RemoveGrowableCol(2);
+			sizer_tmp = sz;
+		}
+    	else
+    		sizer_tmp = sizer;
 		// add label if any
 		if (option.label != "") {
 			wxString str_label = _(option.label);
@@ -208,34 +234,38 @@ void OptionsGroup::append_line(const Line& line, wxStaticText**	colored_Label/*
 // 								L_str(option.label);
 			label = new wxStaticText(parent(), wxID_ANY, str_label + ":", wxDefaultPosition, wxDefaultSize);
 			label->SetFont(label_font);
-			sizer->Add(label, 0, wxALIGN_CENTER_VERTICAL, 0);
+			sizer_tmp->Add(label, 0, wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL, 0);
 		}
 
 		// add field
 		const Option& opt_ref = opt;
 		auto& field = build_field(opt_ref, label);
-		sizer->Add(field->m_Undo_to_sys_btn, 0, wxALIGN_CENTER_VERTICAL);
-		sizer->Add(field->m_Undo_btn, 0, wxALIGN_CENTER_VERTICAL, 0);
+		add_undo_buttuns_to_sizer(sizer_tmp, field);
 		is_sizer_field(field) ? 
-			sizer->Add(field->getSizer(), 0, wxALIGN_CENTER_VERTICAL, 0) :
-			sizer->Add(field->getWindow(), 0, wxALIGN_CENTER_VERTICAL, 0);
+			sizer_tmp->Add(field->getSizer(), 0, wxALIGN_CENTER_VERTICAL, 0) :
+			sizer_tmp->Add(field->getWindow(), 0, wxALIGN_CENTER_VERTICAL, 0);
 		
 		// add sidetext if any
 		if (option.sidetext != "") {
-			auto sidetext = new wxStaticText(parent(), wxID_ANY, _(option.sidetext), wxDefaultPosition, wxDefaultSize);
+			auto sidetext = new wxStaticText(	parent(), wxID_ANY, _(option.sidetext), wxDefaultPosition, 
+												wxSize(sidetext_width, -1)/*wxDefaultSize*/, wxALIGN_LEFT);
 			sidetext->SetFont(sidetext_font);
-			sizer->Add(sidetext, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 4);
+			sizer_tmp->Add(sidetext, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, m_flag == ogSIDE_OPTIONS_VERTICAL ? 0 : 4);
+			field->set_side_text_ptr(sidetext);
 		}
 
 		// add side widget if any
 		if (opt.side_widget != nullptr) {
-			sizer->Add(opt.side_widget(parent())/*!.target<wxWindow>()*/, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 1);	//! requires verification
+			sizer_tmp->Add(opt.side_widget(parent())/*!.target<wxWindow>()*/, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 1);	//! requires verification
 		}
 
-		if (opt.opt_id != option_set.back().opt_id) //! istead of (opt != option_set.back())
+		if (opt.opt_id != option_set.back().opt_id && m_flag != ogSIDE_OPTIONS_VERTICAL) //! istead of (opt != option_set.back())
 		{
-			sizer->AddSpacer(6);
+			sizer_tmp->AddSpacer(6);
 	    }
+
+		if (m_flag == ogSIDE_OPTIONS_VERTICAL)
+			sizer->Add(sizer_tmp, 0, wxALIGN_RIGHT|wxALL, 0);
 	}
 	// add extra sizers if any
 	for (auto extra_widget : line.get_extra_widgets()) {
@@ -259,7 +289,7 @@ void OptionsGroup::on_change_OG(const t_config_option_key& opt_id, const boost::
 Option ConfigOptionsGroup::get_option(const std::string& opt_key, int opt_index /*= -1*/)
 {
 	if (!m_config->has(opt_key)) {
-		std::cerr << "No " << opt_key << " in ConfigOptionsGroup config.";
+		std::cerr << "No " << opt_key << " in ConfigOptionsGroup config.\n";
 	}
 
 	std::string opt_id = opt_index == -1 ? opt_key : opt_key + "#" + std::to_string(opt_index);
diff --git a/xs/src/slic3r/GUI/OptionsGroup.hpp b/xs/src/slic3r/GUI/OptionsGroup.hpp
index 422e1afd9..4f263f5e3 100644
--- a/xs/src/slic3r/GUI/OptionsGroup.hpp
+++ b/xs/src/slic3r/GUI/OptionsGroup.hpp
@@ -26,9 +26,13 @@
 
 namespace Slic3r { namespace GUI {
 
+enum ogDrawFlag{
+	ogDEFAULT,
+	ogSIDE_OPTIONS_VERTICAL
+};
+
 /// Widget type describes a function object that returns a wxWindow (our widget) and accepts a wxWidget (parent window).
 using widget_t = std::function<wxSizer*(wxWindow*)>;//!std::function<wxWindow*(wxWindow*)>;
-using column_t = std::function<wxSizer*(const Line&)>;
 
 //auto default_label_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT); //GetSystemColour
 //auto modified_label_clr = *new wxColour(254, 189, 101);
@@ -71,10 +75,13 @@ private:
     std::vector<widget_t>	m_extra_widgets;//! {std::vector<widget_t>()};
 };
 
+using column_t = std::function<wxWindow*(wxWindow* parent, const Line&)>;//std::function<wxSizer*(const Line&)>;
+
 using t_optionfield_map = std::map<t_config_option_key, t_field>;
 using t_opt_map = std::map< std::string, std::pair<std::string, int> >;
 
 class OptionsGroup {
+	wxStaticBox*	stb;
 public:
     const bool		staticbox {true};
     const wxString	title {wxString("")};
@@ -88,8 +95,7 @@ public:
 
     wxFont			sidetext_font {wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT) };
     wxFont			label_font {wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT) };
-
-//	std::function<const wxBitmap&()>	nonsys_btn_icon{ nullptr };
+	int				sidetext_width{ -1 };
 
     /// Returns a copy of the pointer of the parent wxWindow.
     /// Accessor function is because users are not allowed to change the parent
@@ -101,6 +107,11 @@ public:
 		return m_parent;
 #endif /* __WXGTK__ */
     }
+#ifdef __WXGTK__
+    wxWindow* get_parent() const {
+        return m_parent;
+    }
+#endif /* __WXGTK__ */
 
 	void		append_line(const Line& line, wxStaticText** colored_Label = nullptr);
     Line		create_single_option_line(const Option& option) const;
@@ -124,23 +135,39 @@ public:
 							return out;
     }
 
+	bool			set_side_text(const t_config_option_key& opt_key, const wxString& side_text) {
+							if (m_fields.find(opt_key) == m_fields.end()) return false;
+							auto st = m_fields.at(opt_key)->m_side_text;
+							if (!st) return false;
+							st->SetLabel(side_text);
+							return true;
+    }
+
+	void			set_name(const wxString& new_name) {
+							stb->SetLabel(new_name);
+    }
+
 	inline void		enable() { for (auto& field : m_fields) field.second->enable(); }
     inline void		disable() { for (auto& field : m_fields) field.second->disable(); }
+	void			set_flag(ogDrawFlag flag) { m_flag = flag; }
+	void			set_grid_vgap(int gap) { m_grid_sizer->SetVGap(gap); }
 
 	void set_show_modified_btns_val(bool show) {
 		m_show_modified_btns = show;
     }
 
-    OptionsGroup(wxWindow* _parent, const wxString& title, bool is_tab_opt=false) : 
-		m_parent(_parent), title(title), m_show_modified_btns(is_tab_opt), staticbox(title!="") {
-		auto stb = new wxStaticBox(_parent, wxID_ANY, title);
+	OptionsGroup(	wxWindow* _parent, const wxString& title, bool is_tab_opt = false, 
+					ogDrawFlag flag = ogDEFAULT, column_t extra_clmn = nullptr) :
+					m_parent(_parent), title(title), m_show_modified_btns(is_tab_opt),
+					staticbox(title!=""), m_flag(flag), extra_column(extra_clmn){
+		stb = new wxStaticBox(_parent, wxID_ANY, title);
 		stb->SetFont(bold_font());
-		sizer = (staticbox ? new wxStaticBoxSizer(stb/*new wxStaticBox(_parent, wxID_ANY, title)*/, wxVERTICAL) : new wxBoxSizer(wxVERTICAL));
+        sizer = (staticbox ? new wxStaticBoxSizer(stb, wxVERTICAL) : new wxBoxSizer(wxVERTICAL));
         auto num_columns = 1U;
         if (label_width != 0) num_columns++;
         if (extra_column != nullptr) num_columns++;
-        m_grid_sizer = new wxFlexGridSizer(0, num_columns, 0,0);
-        static_cast<wxFlexGridSizer*>(m_grid_sizer)->SetFlexibleDirection(wxHORIZONTAL);
+        m_grid_sizer = new wxFlexGridSizer(0, num_columns, 1,0);
+        static_cast<wxFlexGridSizer*>(m_grid_sizer)->SetFlexibleDirection(wxBOTH/*wxHORIZONTAL*/);
         static_cast<wxFlexGridSizer*>(m_grid_sizer)->AddGrowableCol(label_width != 0);
 #ifdef __WXGTK__
         m_panel = new wxPanel( _parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
@@ -164,6 +191,8 @@ protected:
 	// "true" if option is created in preset tabs
 	bool					m_show_modified_btns{ false };
 
+	ogDrawFlag				m_flag{ ogDEFAULT };
+
 	// This panel is needed for correct showing of the ToolTips for Button, StaticText and CheckBox
 	// Tooltips on GTK doesn't work inside wxStaticBoxSizer unless you insert a panel 
 	// inside it before you insert the other controls.
@@ -177,6 +206,7 @@ protected:
 	const t_field&		build_field(const t_config_option_key& id, const ConfigOptionDef& opt, wxStaticText* label = nullptr);
 	const t_field&		build_field(const t_config_option_key& id, wxStaticText* label = nullptr);
 	const t_field&		build_field(const Option& opt, wxStaticText* label = nullptr);
+	void				add_undo_buttuns_to_sizer(wxSizer* sizer, const t_field& field);
 
     virtual void		on_kill_focus (){};
 	virtual void		on_change_OG(const t_config_option_key& opt_id, const boost::any& value);
@@ -186,8 +216,9 @@ protected:
 
 class ConfigOptionsGroup: public OptionsGroup {
 public:
-	ConfigOptionsGroup(wxWindow* parent, const wxString& title, DynamicPrintConfig* _config = nullptr, bool is_tab_opt = false) :
-		OptionsGroup(parent, title, is_tab_opt), m_config(_config) {}
+	ConfigOptionsGroup(	wxWindow* parent, const wxString& title, DynamicPrintConfig* _config = nullptr, 
+						bool is_tab_opt = false, ogDrawFlag flag = ogDEFAULT, column_t extra_clmn = nullptr) :
+		OptionsGroup(parent, title, is_tab_opt, flag, extra_clmn), m_config(_config) {}
 
     /// reference to libslic3r config, non-owning pointer (?).
     DynamicPrintConfig*		m_config {nullptr};
diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp
index af2cd8ff6..6ee92e53d 100644
--- a/xs/src/slic3r/GUI/Preset.cpp
+++ b/xs/src/slic3r/GUI/Preset.cpp
@@ -120,6 +120,11 @@ 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;
+            }
             section.second.get<std::string>("variants", "");
             const auto variants_field = section.second.get<std::string>("variants", "");
             std::vector<std::string> variants;
@@ -177,7 +182,7 @@ void Preset::normalize(DynamicPrintConfig &config)
 {
     auto *nozzle_diameter = dynamic_cast<const ConfigOptionFloats*>(config.option("nozzle_diameter"));
     if (nozzle_diameter != nullptr)
-        // Loaded the Printer settings. Verify, that all extruder dependent values have enough values.
+        // Loaded the FFF Printer settings. Verify, that all extruder dependent values have enough values.
         set_num_extruders(config, (unsigned int)nozzle_diameter->values.size());
     if (config.option("filament_diameter") != nullptr) {
         // This config contains single or multiple filament presets.
@@ -204,12 +209,9 @@ void Preset::normalize(DynamicPrintConfig &config)
     }
 }
 
-// Load a config file, return a C++ class Slic3r::DynamicPrintConfig with $keys initialized from the config file.
-// In case of a "default" config item, return the default values.
-DynamicPrintConfig& Preset::load(const std::vector<std::string> &keys)
+DynamicPrintConfig& Preset::load(const std::vector<std::string> &keys, const StaticPrintConfig &defaults)
 {
     // Set the configuration from the defaults.
-    Slic3r::FullPrintConfig defaults;
     this->config.apply_only(defaults, keys.empty() ? defaults.keys() : keys);
     if (! this->is_default) {
         // Load the preset file, apply preset values on top of defaults.
@@ -260,8 +262,9 @@ bool Preset::is_compatible_with_printer(const Preset &active_printer) const
 {
     DynamicPrintConfig config;
     config.set_key_value("printer_preset", new ConfigOptionString(active_printer.name));
-    config.set_key_value("num_extruders", new ConfigOptionInt(
-        (int)static_cast<const ConfigOptionFloats*>(active_printer.config.option("nozzle_diameter"))->values.size()));
+	const ConfigOption *opt = active_printer.config.option("nozzle_diameter");
+	if (opt)
+		config.set_key_value("num_extruders", new ConfigOptionInt((int)static_cast<const ConfigOptionFloats*>(opt)->values.size()));
     return this->is_compatible_with_printer(active_printer, &config);
 }
 
@@ -328,8 +331,10 @@ const std::vector<std::string>& Preset::printer_options()
     static std::vector<std::string> s_opts;
     if (s_opts.empty()) {
         s_opts = {
-            "bed_shape", "z_offset", "gcode_flavor", "use_relative_e_distances", "serial_port", "serial_speed", "host_type",
-            "print_host", "printhost_apikey", "printhost_cafile", "use_firmware_retraction", "use_volumetric_e", "variable_layer_height",
+            "printer_technology",
+            "bed_shape", "z_offset", "gcode_flavor", "use_relative_e_distances", "serial_port", "serial_speed", 
+            "use_firmware_retraction", "use_volumetric_e", "variable_layer_height",
+            "host_type", "print_host", "printhost_apikey", "printhost_cafile",
             "single_extruder_multi_material", "start_gcode", "end_gcode", "before_layer_gcode", "layer_gcode", "toolchange_gcode",
             "between_objects_gcode", "printer_vendor", "printer_model", "printer_variant", "printer_notes", "cooling_tube_retraction",
             "cooling_tube_length", "parking_pos_retraction", "extra_loading_move", "max_print_height", "default_print_profile", "inherits",
@@ -359,7 +364,39 @@ const std::vector<std::string>& Preset::nozzle_options()
     return s_opts;
 }
 
-PresetCollection::PresetCollection(Preset::Type type, const std::vector<std::string> &keys) :
+const std::vector<std::string>& Preset::sla_printer_options()
+{    
+    static std::vector<std::string> s_opts;
+    if (s_opts.empty()) {
+        s_opts = {
+            "printer_technology",
+            "bed_shape", "max_print_height",
+            "display_width", "display_height", "display_pixels_x", "display_pixels_y",
+            "printer_correction",
+            "printer_notes",
+            "inherits"
+        };
+    }
+    return s_opts;
+}
+
+const std::vector<std::string>& Preset::sla_material_options()
+{    
+    static std::vector<std::string> s_opts;
+    if (s_opts.empty()) {
+        s_opts = {
+            "layer_height", "initial_layer_height",
+            "exposure_time", "initial_exposure_time",
+            "material_correction_printing", "material_correction_curing",
+            "material_notes",
+            "compatible_printers",
+            "compatible_printers_condition", "inherits"
+        };
+    }
+    return s_opts;
+}
+
+PresetCollection::PresetCollection(Preset::Type type, const std::vector<std::string> &keys, const Slic3r::StaticPrintConfig &defaults, const std::string &default_name) :
     m_type(type),
     m_edited_preset(type, "", false),
     m_idx_selected(0),
@@ -367,8 +404,7 @@ PresetCollection::PresetCollection(Preset::Type type, const std::vector<std::str
 	m_bitmap_cache(new GUI::BitmapCache)
 {
     // Insert just the default preset.
-    m_presets.emplace_back(Preset(type, "- default -", true));
-    m_presets.front().load(keys);
+    this->add_default_preset(keys, defaults, default_name);
     m_edited_preset.config.apply(m_presets.front().config);
 }
 
@@ -382,7 +418,7 @@ PresetCollection::~PresetCollection()
 
 void PresetCollection::reset(bool delete_files)
 {
-    if (m_presets.size() > 1) {
+    if (m_presets.size() > m_num_default_presets) {
         if (delete_files) {
             // Erase the preset files.
             for (Preset &preset : m_presets)
@@ -390,11 +426,19 @@ void PresetCollection::reset(bool delete_files)
                     boost::nowide::remove(preset.file.c_str());
         }
         // Don't use m_presets.resize() here as it requires a default constructor for Preset.
-        m_presets.erase(m_presets.begin() + 1, m_presets.end());
+        m_presets.erase(m_presets.begin() + m_num_default_presets, m_presets.end());
         this->select_preset(0);
     }
 }
 
+void PresetCollection::add_default_preset(const std::vector<std::string> &keys, const Slic3r::StaticPrintConfig &defaults, const std::string &preset_name)
+{
+    // Insert just the default preset.
+    m_presets.emplace_back(Preset(this->type(), preset_name, true));
+    m_presets.back().load(keys, defaults);
+    ++ m_num_default_presets;
+}
+
 // Load all presets found in dir_path.
 // Throws an exception on error.
 void PresetCollection::load_presets(const std::string &dir_path, const std::string &subdir)
@@ -417,14 +461,15 @@ void PresetCollection::load_presets(const std::string &dir_path, const std::stri
             try {
                 Preset preset(m_type, name, false);
                 preset.file = dir_entry.path().string();
-                preset.load(keys);
+                //FIXME One should initialize with SLAFullPrintConfig for the SLA profiles!
+                preset.load(keys, static_cast<const HostConfig&>(FullPrintConfig::defaults()));
                 m_presets.emplace_back(preset);
             } catch (const std::runtime_error &err) {
                 errors_cummulative += err.what();
                 errors_cummulative += "\n";
 			}
         }
-    std::sort(m_presets.begin() + 1, m_presets.end());
+    std::sort(m_presets.begin() + m_num_default_presets, m_presets.end());
     this->select_preset(first_visible_idx());
     if (! errors_cummulative.empty())
         throw std::runtime_error(errors_cummulative);
@@ -642,7 +687,7 @@ Preset* PresetCollection::find_preset(const std::string &name, bool first_visibl
 // Return index of the first visible preset. Certainly at least the '- default -' preset shall be visible.
 size_t PresetCollection::first_visible_idx() const
 {
-    size_t idx = m_default_suppressed ? 1 : 0;
+    size_t idx = m_default_suppressed ? m_num_default_presets : 0;
     for (; idx < this->m_presets.size(); ++ idx)
         if (m_presets[idx].is_visible)
             break;
@@ -655,7 +700,7 @@ void PresetCollection::set_default_suppressed(bool default_suppressed)
 {
     if (m_default_suppressed != default_suppressed) {
         m_default_suppressed = default_suppressed;
-        m_presets.front().is_visible = ! default_suppressed || (m_presets.size() > 1 && m_idx_selected > 0);
+        m_presets.front().is_visible = ! default_suppressed || (m_presets.size() > m_num_default_presets && m_idx_selected > 0);
     }
 }
 
@@ -663,9 +708,10 @@ size_t PresetCollection::update_compatible_with_printer_internal(const Preset &a
 {
     DynamicPrintConfig config;
     config.set_key_value("printer_preset", new ConfigOptionString(active_printer.name));
-    config.set_key_value("num_extruders", new ConfigOptionInt(
-        (int)static_cast<const ConfigOptionFloats*>(active_printer.config.option("nozzle_diameter"))->values.size()));
-    for (size_t idx_preset = 1; idx_preset < m_presets.size(); ++ idx_preset) {
+    const ConfigOption *opt = active_printer.config.option("nozzle_diameter");
+    if (opt)
+        config.set_key_value("num_extruders", new ConfigOptionInt((int)static_cast<const ConfigOptionFloats*>(opt)->values.size()));
+    for (size_t idx_preset = m_num_default_presets; idx_preset < m_presets.size(); ++ idx_preset) {
         bool    selected        = idx_preset == m_idx_selected;
         Preset &preset_selected = m_presets[idx_preset];
         Preset &preset_edited   = selected ? m_edited_preset : preset_selected;
@@ -706,7 +752,7 @@ void PresetCollection::update_platter_ui(wxBitmapComboBox *ui)
 	wxString selected = "";
 	if (!this->m_presets.front().is_visible)
 		ui->Append("------- " +_(L("System presets")) + " -------", wxNullBitmap);
-	for (size_t i = this->m_presets.front().is_visible ? 0 : 1; i < this->m_presets.size(); ++i) {
+	for (size_t i = this->m_presets.front().is_visible ? 0 : m_num_default_presets; i < this->m_presets.size(); ++i) {
         const Preset &preset = this->m_presets[i];
         if (! preset.is_visible || (! preset.is_compatible && i != m_idx_selected))
             continue;
@@ -744,7 +790,7 @@ void PresetCollection::update_platter_ui(wxBitmapComboBox *ui)
 			if (i == m_idx_selected)
 				selected = wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str());
 		}
-		if (preset.is_default)
+		if (i + 1 == m_num_default_presets)
 			ui->Append("------- " + _(L("System presets")) + " -------", wxNullBitmap);
 	}
 	if (!nonsys_presets.empty())
@@ -774,7 +820,7 @@ size_t PresetCollection::update_tab_ui(wxBitmapComboBox *ui, bool show_incompati
 	wxString selected = "";
 	if (!this->m_presets.front().is_visible)
 		ui->Append("------- " + _(L("System presets")) + " -------", wxNullBitmap);
-	for (size_t i = this->m_presets.front().is_visible ? 0 : 1; i < this->m_presets.size(); ++i) {
+	for (size_t i = this->m_presets.front().is_visible ? 0 : m_num_default_presets; i < this->m_presets.size(); ++i) {
         const Preset &preset = this->m_presets[i];
         if (! preset.is_visible || (! show_incompatible && ! preset.is_compatible && i != m_idx_selected))
             continue;
@@ -804,7 +850,7 @@ size_t PresetCollection::update_tab_ui(wxBitmapComboBox *ui, bool show_incompati
 			if (i == m_idx_selected)
 				selected = wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str());
 		}
-		if (preset.is_default)
+        if (i + 1 == m_num_default_presets)
 			ui->Append("------- " + _(L("System presets")) + " -------", wxNullBitmap);
     }
 	if (!nonsys_presets.empty())
@@ -852,11 +898,11 @@ bool PresetCollection::update_dirty_ui(wxBitmapComboBox *ui)
     return was_dirty != is_dirty;
 }
 
-std::vector<std::string> PresetCollection::dirty_options(const Preset *edited, const Preset *reference, const bool is_printer_type /*= false*/)
+std::vector<std::string> PresetCollection::dirty_options(const Preset *edited, const Preset *reference, const bool deep_compare /*= false*/)
 {
     std::vector<std::string> changed;
 	if (edited != nullptr && reference != nullptr) {
-        changed = is_printer_type  ? 
+        changed = deep_compare ?
 				reference->config.deep_diff(edited->config) :
 				reference->config.diff(edited->config);
         // The "compatible_printers" option key is handled differently from the others:
@@ -896,7 +942,7 @@ bool PresetCollection::select_preset_by_name(const std::string &name_w_suffix, b
         idx = it - m_presets.begin();
     else {
         // Find the first visible preset.
-        for (size_t i = m_default_suppressed ? 1 : 0; i < m_presets.size(); ++ i)
+        for (size_t i = m_default_suppressed ? m_num_default_presets : 0; i < m_presets.size(); ++ i)
             if (m_presets[i].is_visible) {
                 idx = i;
                 break;
@@ -938,7 +984,7 @@ std::vector<std::string> PresetCollection::merge_presets(PresetCollection &&othe
         if (preset.is_default || preset.is_external)
             continue;
         Preset key(m_type, preset.name);
-        auto it = std::lower_bound(m_presets.begin() + 1, m_presets.end(), key);
+        auto it = std::lower_bound(m_presets.begin() + m_num_default_presets, m_presets.end(), key);
         if (it == m_presets.end() || it->name != preset.name) {
             if (preset.vendor != nullptr) {
                 // Re-assign a pointer to the vendor structure in the new PresetBundle.
diff --git a/xs/src/slic3r/GUI/Preset.hpp b/xs/src/slic3r/GUI/Preset.hpp
index 0d00cae48..821d7dc54 100644
--- a/xs/src/slic3r/GUI/Preset.hpp
+++ b/xs/src/slic3r/GUI/Preset.hpp
@@ -52,6 +52,7 @@ public:
         PrinterModel() {}
         std::string                 id;
         std::string                 name;
+        PrinterTechnology           technology;
         std::vector<PrinterVariant> variants;
         PrinterVariant*       variant(const std::string &name) {
             for (auto &v : this->variants)
@@ -83,6 +84,7 @@ public:
         TYPE_INVALID,
         TYPE_PRINT,
         TYPE_FILAMENT,
+        TYPE_SLA_MATERIAL,
         TYPE_PRINTER,
     };
 
@@ -123,8 +125,7 @@ public:
     DynamicPrintConfig  config;
 
     // Load this profile for the following keys only.
-    // Throws std::runtime_error in case the file cannot be read.
-    DynamicPrintConfig& load(const std::vector<std::string> &keys);
+    DynamicPrintConfig& load(const std::vector<std::string> &keys, const StaticPrintConfig &defaults);
 
     void                save();
 
@@ -149,6 +150,10 @@ public:
     std::string&        compatible_printers_condition() { return Preset::compatible_printers_condition(this->config); }
     const std::string&  compatible_printers_condition() const { return Preset::compatible_printers_condition(const_cast<Preset*>(this)->config); }
 
+    static PrinterTechnology& printer_technology(DynamicPrintConfig &cfg) { return cfg.option<ConfigOptionEnum<PrinterTechnology>>("printer_technology", true)->value; }
+    PrinterTechnology&        printer_technology() { return Preset::printer_technology(this->config); }
+    const PrinterTechnology&  printer_technology() const { return Preset::printer_technology(const_cast<Preset*>(this)->config); }
+
     // Mark this preset as compatible if it is compatible with active_printer.
     bool                update_compatible_with_printer(const Preset &active_printer, const DynamicPrintConfig *extra_config);
 
@@ -167,6 +172,10 @@ public:
     static const std::vector<std::string>&  printer_options();
     // Nozzle options of the printer options.
     static const std::vector<std::string>&  nozzle_options();
+
+    static const std::vector<std::string>&  sla_printer_options();
+    static const std::vector<std::string>&  sla_material_options();
+
 	static void update_suffix_modified();
 
 protected:
@@ -184,15 +193,15 @@ class PresetCollection
 {
 public:
     // Initialize the PresetCollection with the "- default -" preset.
-    PresetCollection(Preset::Type type, const std::vector<std::string> &keys);
+    PresetCollection(Preset::Type type, const std::vector<std::string> &keys, const Slic3r::StaticPrintConfig &defaults, const std::string &default_name = "- default -");
     ~PresetCollection();
 
     typedef std::deque<Preset>::iterator Iterator;
     typedef std::deque<Preset>::const_iterator ConstIterator;
-    Iterator begin() { return m_presets.begin() + 1; }
-    ConstIterator begin() const { return m_presets.begin() + 1; }
-    Iterator end() { return m_presets.end(); }
-    ConstIterator end() const { return m_presets.end(); }
+    Iterator        begin() { return m_presets.begin() + m_num_default_presets; }
+    ConstIterator   begin() const { return m_presets.begin() + m_num_default_presets; }
+    Iterator        end() { return m_presets.end(); }
+    ConstIterator   end() const { return m_presets.end(); }
 
     void            reset(bool delete_files);
 
@@ -200,6 +209,9 @@ public:
     std::string     name() const;
     const std::deque<Preset>& operator()() const { return m_presets; }
 
+    // Add default preset at the start of the collection, increment the m_default_preset counter.
+    void            add_default_preset(const std::vector<std::string> &keys, const Slic3r::StaticPrintConfig &defaults, const std::string &preset_name);
+
     // Load ini files of the particular type from the provided directory path.
     void            load_presets(const std::string &dir_path, const std::string &subdir);
 
@@ -247,6 +259,8 @@ public:
     Preset&         get_selected_preset()       { return m_presets[m_idx_selected]; }
     const Preset&   get_selected_preset() const { return m_presets[m_idx_selected]; }
     int             get_selected_idx()    const { return m_idx_selected; }
+    // Returns the name of the selected preset, or an empty string if no preset is selected.
+    std::string     get_selected_preset_name() const { return (m_idx_selected == -1) ? std::string() : this->get_selected_preset().name; }
     // For the current edited preset, return the parent preset if there is one.
     // If there is no parent preset, nullptr is returned.
     // The parent preset may be a system preset or a user preset, which will be
@@ -283,7 +297,7 @@ public:
     template<typename PreferedCondition>
     size_t          first_compatible_idx(PreferedCondition prefered_condition) const
     {
-        size_t i = m_default_suppressed ? 1 : 0;
+        size_t i = m_default_suppressed ? m_num_default_presets : 0;
         size_t n = this->m_presets.size();
         size_t i_compatible = n;
         for (; i < n; ++ i)
@@ -309,7 +323,8 @@ public:
     const Preset&   first_compatible() const    { return this->preset(this->first_compatible_idx()); }
 
     // Return number of presets including the "- default -" preset.
-    size_t          size() const                { return this->m_presets.size(); }
+    size_t          size() const                { return m_presets.size(); }
+    bool            has_defaults_only() const   { return m_presets.size() <= m_num_default_presets; }
 
     // For Print / Filament presets, disable those, which are not compatible with the printer.
     template<typename PreferedCondition>
@@ -327,11 +342,11 @@ public:
     // Compare the content of get_selected_preset() with get_edited_preset() configs, return true if they differ.
     bool                        current_is_dirty() const { return ! this->current_dirty_options().empty(); }
     // Compare the content of get_selected_preset() with get_edited_preset() configs, return the list of keys where they differ.
-    std::vector<std::string>    current_dirty_options(const bool is_printer_type = false) const
-        { return dirty_options(&this->get_edited_preset(), &this->get_selected_preset(), is_printer_type); }
+    std::vector<std::string>    current_dirty_options(const bool deep_compare = false) const
+        { return dirty_options(&this->get_edited_preset(), &this->get_selected_preset(), deep_compare); }
     // Compare the content of get_selected_preset() with get_edited_preset() configs, return the list of keys where they differ.
-    std::vector<std::string>    current_different_from_parent_options(const bool is_printer_type = false) const
-        { return dirty_options(&this->get_edited_preset(), this->get_selected_preset_parent(), is_printer_type); }
+    std::vector<std::string>    current_different_from_parent_options(const bool deep_compare = false) const
+        { return dirty_options(&this->get_edited_preset(), this->get_selected_preset_parent(), deep_compare); }
 
     // Update the choice UI from the list of presets.
     // If show_incompatible, all presets are shown, otherwise only the compatible presets are shown.
@@ -374,8 +389,16 @@ private:
     std::deque<Preset>::iterator find_preset_internal(const std::string &name)
     {
         Preset key(m_type, name);
-        auto it = std::lower_bound(m_presets.begin() + 1, m_presets.end(), key);
-        return ((it == m_presets.end() || it->name != name) && m_presets.front().name == name) ? m_presets.begin() : it;
+        auto it = std::lower_bound(m_presets.begin() + m_num_default_presets, m_presets.end(), key);
+        if (it == m_presets.end() || it->name != name) {
+            // Preset has not been not found in the sorted list of non-default presets. Try the defaults.
+            for (size_t i = 0; i < m_num_default_presets; ++ i)
+                if (m_presets[i].name == name) {
+                    it = m_presets.begin() + i;
+                    break;
+                }
+        }
+        return it;
     }
     std::deque<Preset>::const_iterator find_preset_internal(const std::string &name) const
         { return const_cast<PresetCollection*>(this)->find_preset_internal(name); }
@@ -395,7 +418,8 @@ private:
     // Selected preset.
     int                     m_idx_selected;
     // Is the "- default -" preset suppressed?
-    bool                    m_default_suppressed = true;
+    bool                    m_default_suppressed  = true;
+    size_t                  m_num_default_presets = 0;
     // Compatible & incompatible marks, to be placed at the wxBitmapComboBox items of a Platter.
     // These bitmaps are not owned by PresetCollection, but by a PresetBundle.
     const wxBitmap         *m_bitmap_compatible   = nullptr;
diff --git a/xs/src/slic3r/GUI/PresetBundle.cpp b/xs/src/slic3r/GUI/PresetBundle.cpp
index 94baa0e0e..cd3924dd0 100644
--- a/xs/src/slic3r/GUI/PresetBundle.cpp
+++ b/xs/src/slic3r/GUI/PresetBundle.cpp
@@ -40,9 +40,10 @@ static std::vector<std::string> s_project_options {
 };
 
 PresetBundle::PresetBundle() :
-    prints(Preset::TYPE_PRINT, Preset::print_options()), 
-    filaments(Preset::TYPE_FILAMENT, Preset::filament_options()), 
-    printers(Preset::TYPE_PRINTER, Preset::printer_options()),
+    prints(Preset::TYPE_PRINT, Preset::print_options(), static_cast<const HostConfig&>(FullPrintConfig::defaults())), 
+    filaments(Preset::TYPE_FILAMENT, Preset::filament_options(), static_cast<const HostConfig&>(FullPrintConfig::defaults())), 
+    sla_materials(Preset::TYPE_SLA_MATERIAL, Preset::sla_material_options(), static_cast<const SLAMaterialConfig&>(SLAFullPrintConfig::defaults())), 
+    printers(Preset::TYPE_PRINTER, Preset::printer_options(), static_cast<const HostConfig&>(FullPrintConfig::defaults()), "- default FFF -"),
     m_bitmapCompatible(new wxBitmap),
     m_bitmapIncompatible(new wxBitmap),
     m_bitmapLock(new wxBitmap),
@@ -69,24 +70,35 @@ PresetBundle::PresetBundle() :
     this->filaments.default_preset().compatible_printers_condition();
     this->filaments.default_preset().inherits();
 
-    this->printers.default_preset().config.optptr("printer_settings_id", true);
-    this->printers.default_preset().config.optptr("printer_vendor", true);
-    this->printers.default_preset().config.optptr("printer_model", true);
-    this->printers.default_preset().config.optptr("printer_variant", true);
-    this->printers.default_preset().config.optptr("default_print_profile", true);
-    this->printers.default_preset().config.option<ConfigOptionStrings>("default_filament_profile", true)->values = { "" };
-	this->printers.default_preset().inherits();
+    this->sla_materials.default_preset().config.optptr("sla_material_settings_id", true);
+    this->sla_materials.default_preset().compatible_printers_condition();
+    this->sla_materials.default_preset().inherits();
+
+    this->printers.add_default_preset(Preset::sla_printer_options(), static_cast<const SLAMaterialConfig&>(SLAFullPrintConfig::defaults()), "- default SLA -");
+    this->printers.preset(1).printer_technology() = ptSLA;
+    for (size_t i = 0; i < 2; ++ i) {
+        Preset &preset = this->printers.preset(i);
+        preset.config.optptr("printer_settings_id", true);
+        preset.config.optptr("printer_vendor", true);
+        preset.config.optptr("printer_model", true);
+        preset.config.optptr("printer_variant", true);
+        preset.config.optptr("default_print_profile", true);
+        preset.config.option<ConfigOptionStrings>("default_filament_profile", true)->values = { "" };
+        preset.inherits();
+    }
 
 	// Load the default preset bitmaps.
-    this->prints   .load_bitmap_default("cog.png");
-    this->filaments.load_bitmap_default("spool.png");
-    this->printers .load_bitmap_default("printer_empty.png");
+    this->prints       .load_bitmap_default("cog.png");
+    this->filaments    .load_bitmap_default("spool.png");
+    this->sla_materials.load_bitmap_default("package_green.png");
+    this->printers     .load_bitmap_default("printer_empty.png");
     this->load_compatible_bitmaps();
 
     // Re-activate the default presets, so their "edited" preset copies will be updated with the additional configuration values above.
-    this->prints   .select_preset(0);
-    this->filaments.select_preset(0);
-    this->printers .select_preset(0);
+    this->prints       .select_preset(0);
+    this->filaments    .select_preset(0);
+    this->sla_materials.select_preset(0);
+    this->printers     .select_preset(0);
 
     this->project_config.apply_only(FullPrintConfig::defaults(), s_project_options);
 }
@@ -113,13 +125,15 @@ void PresetBundle::reset(bool delete_files)
 {
     // Clear the existing presets, delete their respective files.
     this->vendors.clear();
-    this->prints   .reset(delete_files);
-    this->filaments.reset(delete_files);
-    this->printers .reset(delete_files);
+    this->prints       .reset(delete_files);
+    this->filaments    .reset(delete_files);
+    this->sla_materials.reset(delete_files);
+    this->printers     .reset(delete_files);
     this->filament_presets.clear();
-    this->filament_presets.emplace_back(this->filaments.get_selected_preset().name);
+    this->filament_presets.emplace_back(this->filaments.get_selected_preset_name());
     this->obsolete_presets.prints.clear();
     this->obsolete_presets.filaments.clear();
+    this->obsolete_presets.sla_materials.clear();
     this->obsolete_presets.printers.clear();
 }
 
@@ -135,11 +149,13 @@ void PresetBundle::setup_directories()
         data_dir / "presets", 
         data_dir / "presets" / "print", 
         data_dir / "presets" / "filament", 
+        data_dir / "presets" / "sla_material", 
         data_dir / "presets" / "printer" 
 #else
         // Store the print/filament/printer presets at the same location as the upstream Slic3r.
         data_dir / "print", 
         data_dir / "filament", 
+        data_dir / "sla_material", 
         data_dir / "printer" 
 #endif
     };
@@ -175,6 +191,11 @@ void PresetBundle::load_presets(const AppConfig &config)
     } catch (const std::runtime_error &err) {
         errors_cummulative += err.what();
     }
+    try {
+        this->sla_materials.load_presets(dir_user_presets, "sla_material");
+    } catch (const std::runtime_error &err) {
+        errors_cummulative += err.what();
+    }
     try {
         this->printers.load_presets(dir_user_presets, "printer");
     } catch (const std::runtime_error &err) {
@@ -238,13 +259,16 @@ std::string PresetBundle::load_system_presets()
 std::vector<std::string> PresetBundle::merge_presets(PresetBundle &&other)
 {
     this->vendors.insert(other.vendors.begin(), other.vendors.end());
-    std::vector<std::string> duplicate_prints    = this->prints   .merge_presets(std::move(other.prints),    this->vendors);
-    std::vector<std::string> duplicate_filaments = this->filaments.merge_presets(std::move(other.filaments), this->vendors);
-    std::vector<std::string> duplicate_printers  = this->printers .merge_presets(std::move(other.printers),  this->vendors);
-	append(this->obsolete_presets.prints,    std::move(other.obsolete_presets.prints));
-	append(this->obsolete_presets.filaments, std::move(other.obsolete_presets.filaments));
-	append(this->obsolete_presets.printers,  std::move(other.obsolete_presets.printers));
+    std::vector<std::string> duplicate_prints        = this->prints       .merge_presets(std::move(other.prints),        this->vendors);
+    std::vector<std::string> duplicate_filaments     = this->filaments    .merge_presets(std::move(other.filaments),     this->vendors);
+    std::vector<std::string> duplicate_sla_materials = this->sla_materials.merge_presets(std::move(other.sla_materials), this->vendors);
+    std::vector<std::string> duplicate_printers      = this->printers     .merge_presets(std::move(other.printers),      this->vendors);
+	append(this->obsolete_presets.prints,        std::move(other.obsolete_presets.prints));
+	append(this->obsolete_presets.filaments,     std::move(other.obsolete_presets.filaments));
+    append(this->obsolete_presets.sla_materials, std::move(other.obsolete_presets.sla_materials));
+	append(this->obsolete_presets.printers,      std::move(other.obsolete_presets.printers));
 	append(duplicate_prints, std::move(duplicate_filaments));
+    append(duplicate_prints, std::move(duplicate_sla_materials));
     append(duplicate_prints, std::move(duplicate_printers));
     return duplicate_prints;
 }
@@ -275,9 +299,10 @@ void PresetBundle::load_selections(const AppConfig &config)
 	this->load_installed_printers(config);
 
     // Parse the initial print / filament / printer profile names.
-    std::string initial_print_profile_name    = remove_ini_suffix(config.get("presets", "print"));
-    std::string initial_filament_profile_name = remove_ini_suffix(config.get("presets", "filament"));
-	std::string initial_printer_profile_name  = remove_ini_suffix(config.get("presets", "printer"));
+    std::string initial_print_profile_name        = remove_ini_suffix(config.get("presets", "print"));
+    std::string initial_filament_profile_name     = remove_ini_suffix(config.get("presets", "filament"));
+    std::string initial_sla_material_profile_name = remove_ini_suffix(config.get("presets", "sla_material"));
+	std::string initial_printer_profile_name      = remove_ini_suffix(config.get("presets", "printer"));
 
 	// Activate print / filament / printer profiles from the config.
 	// If the printer profile enumerated by the config are not visible, select an alternate preset.
@@ -285,21 +310,24 @@ void PresetBundle::load_selections(const AppConfig &config)
     // will be selected by the following call of this->update_compatible_with_printer(true).
     prints.select_preset_by_name_strict(initial_print_profile_name);
     filaments.select_preset_by_name_strict(initial_filament_profile_name);
+    sla_materials.select_preset_by_name_strict(initial_sla_material_profile_name);
     printers.select_preset_by_name(initial_printer_profile_name, true);
 
-    // Load the names of the other filament profiles selected for a multi-material printer.
-    auto   *nozzle_diameter = dynamic_cast<const ConfigOptionFloats*>(printers.get_selected_preset().config.option("nozzle_diameter"));
-    size_t  num_extruders = nozzle_diameter->values.size();
-    this->filament_presets = { initial_filament_profile_name };
-    for (unsigned int i = 1; i < (unsigned int)num_extruders; ++ i) {
-        char name[64];
-        sprintf(name, "filament_%d", i);
-        if (! config.has("presets", name))
-            break;
-        this->filament_presets.emplace_back(remove_ini_suffix(config.get("presets", name)));
+    if (printers.get_selected_preset().printer_technology() == ptFFF){
+        // Load the names of the other filament profiles selected for a multi-material printer.
+        auto   *nozzle_diameter = dynamic_cast<const ConfigOptionFloats*>(printers.get_selected_preset().config.option("nozzle_diameter"));
+        size_t  num_extruders = nozzle_diameter->values.size();
+        this->filament_presets = { initial_filament_profile_name };
+        for (unsigned int i = 1; i < (unsigned int)num_extruders; ++i) {
+            char name[64];
+            sprintf(name, "filament_%d", i);
+            if (!config.has("presets", name))
+                break;
+            this->filament_presets.emplace_back(remove_ini_suffix(config.get("presets", name)));
+        }
+        // Do not define the missing filaments, so that the update_compatible_with_printer() will use the preferred filaments.
+        this->filament_presets.resize(num_extruders, "");
     }
-    // Do not define the missing filaments, so that the update_compatible_with_printer() will use the preferred filaments.
-    this->filament_presets.resize(num_extruders, "");
 
     // Update visibility of presets based on their compatibility with the active printer.
     // Always try to select a compatible print and filament preset to the current printer preset,
@@ -313,25 +341,33 @@ void PresetBundle::load_selections(const AppConfig &config)
 void PresetBundle::export_selections(AppConfig &config)
 {
     assert(filament_presets.size() >= 1);
-    assert(filament_presets.size() > 1 || filaments.get_selected_preset().name == filament_presets.front());
+    assert(filament_presets.size() > 1 || filaments.get_selected_preset_name() == filament_presets.front());
     config.clear_section("presets");
-    config.set("presets", "print",    prints.get_selected_preset().name);
-    config.set("presets", "filament", filament_presets.front());
+    config.set("presets", "print",        prints.get_selected_preset_name());
+    config.set("presets", "filament",     filament_presets.front());
 	for (int i = 1; i < filament_presets.size(); ++i) {
         char name[64];
         sprintf(name, "filament_%d", i);
         config.set("presets", name, filament_presets[i]);
     }
-    config.set("presets", "printer",  printers.get_selected_preset().name);
+    config.set("presets", "sla_material", sla_materials.get_selected_preset_name());
+    config.set("presets", "printer",  printers.get_selected_preset_name());
 }
 
 void PresetBundle::export_selections(PlaceholderParser &pp)
 {
     assert(filament_presets.size() >= 1);
-    assert(filament_presets.size() > 1 || filaments.get_selected_preset().name == filament_presets.front());
-    pp.set("print_preset",    prints.get_selected_preset().name);
-    pp.set("filament_preset", filament_presets);
-    pp.set("printer_preset",  printers.get_selected_preset().name);
+    assert(filament_presets.size() > 1 || filaments.get_selected_preset_name() == filament_presets.front());
+    switch (printers.get_edited_preset().printer_technology()) {
+    case ptFFF:
+        pp.set("print_preset",        prints.get_selected_preset().name);
+        pp.set("filament_preset",     filament_presets);
+        break;
+    case ptSLA:
+        pp.set("sla_material_preset", sla_materials.get_selected_preset().name);
+        break;
+    }
+    pp.set("printer_preset",      printers.get_selected_preset().name);
 }
 
 bool PresetBundle::load_compatible_bitmaps()
@@ -349,32 +385,43 @@ bool PresetBundle::load_compatible_bitmaps()
     bool loaded_lock_open = m_bitmapLockOpen->LoadFile(
         wxString::FromUTF8(Slic3r::var(path_bitmap_lock_open).c_str()), wxBITMAP_TYPE_PNG);
     if (loaded_compatible) {
-        prints   .set_bitmap_compatible(m_bitmapCompatible);
-        filaments.set_bitmap_compatible(m_bitmapCompatible);
+        prints       .set_bitmap_compatible(m_bitmapCompatible);
+        filaments    .set_bitmap_compatible(m_bitmapCompatible);
+        sla_materials.set_bitmap_compatible(m_bitmapCompatible);
 //        printers .set_bitmap_compatible(m_bitmapCompatible);
     }
     if (loaded_incompatible) {
-        prints   .set_bitmap_incompatible(m_bitmapIncompatible);
-        filaments.set_bitmap_incompatible(m_bitmapIncompatible);
-//        printers .set_bitmap_incompatible(m_bitmapIncompatible);        
+        prints       .set_bitmap_incompatible(m_bitmapIncompatible);
+        filaments    .set_bitmap_incompatible(m_bitmapIncompatible);
+        sla_materials.set_bitmap_incompatible(m_bitmapIncompatible);
+//        printers .set_bitmap_incompatible(m_bitmapIncompatible);
     }
     if (loaded_lock) {
-        prints   .set_bitmap_lock(m_bitmapLock);
-        filaments.set_bitmap_lock(m_bitmapLock);
-        printers .set_bitmap_lock(m_bitmapLock);
+        prints       .set_bitmap_lock(m_bitmapLock);
+        filaments    .set_bitmap_lock(m_bitmapLock);
+        sla_materials.set_bitmap_lock(m_bitmapLock);
+        printers     .set_bitmap_lock(m_bitmapLock);
     }
     if (loaded_lock_open) {
-        prints   .set_bitmap_lock_open(m_bitmapLock);
-        filaments.set_bitmap_lock_open(m_bitmapLock);
-        printers .set_bitmap_lock_open(m_bitmapLock);
+        prints       .set_bitmap_lock_open(m_bitmapLock);
+        filaments    .set_bitmap_lock_open(m_bitmapLock);
+        sla_materials.set_bitmap_lock_open(m_bitmapLock);
+        printers     .set_bitmap_lock_open(m_bitmapLock);
     }
     return loaded_compatible && loaded_incompatible && loaded_lock && loaded_lock_open;
 }
 
 DynamicPrintConfig PresetBundle::full_config() const
+{
+    return (this->printers.get_edited_preset().printer_technology() == ptFFF) ?
+        this->full_fff_config() :
+        this->full_sla_config();
+}
+
+DynamicPrintConfig PresetBundle::full_fff_config() const
 {    
     DynamicPrintConfig out;
-    out.apply(FullPrintConfig());
+    out.apply(FullPrintConfig::defaults());
     out.apply(this->prints.get_edited_preset().config);
     // Add the default filament preset to have the "filament_preset_id" defined.
 	out.apply(this->filaments.default_preset().config);
@@ -466,6 +513,48 @@ DynamicPrintConfig PresetBundle::full_config() const
     return out;
 }
 
+DynamicPrintConfig PresetBundle::full_sla_config() const
+{    
+    DynamicPrintConfig out;
+    out.apply(SLAFullPrintConfig::defaults());
+    out.apply(this->sla_materials.get_edited_preset().config);
+    out.apply(this->printers.get_edited_preset().config);
+    // There are no project configuration values as of now, the project_config is reserved for FFF printers.
+//    out.apply(this->project_config);
+
+    // Collect the "compatible_printers_condition" and "inherits" values over all presets (sla_materials, printers) into a single vector.
+    std::vector<std::string> compatible_printers_condition;
+    std::vector<std::string> inherits;
+    compatible_printers_condition.emplace_back(this->/*prints*/sla_materials.get_edited_preset().compatible_printers_condition());
+    inherits                     .emplace_back(this->/*prints*/sla_materials.get_edited_preset().inherits());
+    inherits                     .emplace_back(this->printers.get_edited_preset().inherits());
+
+    // These two value types clash between the print and filament profiles. They should be renamed.
+    out.erase("compatible_printers");
+    out.erase("compatible_printers_condition");
+    out.erase("inherits");
+    
+    out.option<ConfigOptionString >("sla_material_settings_id", true)->value  = this->sla_materials.get_selected_preset().name;
+    out.option<ConfigOptionString >("printer_settings_id",      true)->value  = this->printers.get_selected_preset().name;
+
+    // Serialize the collected "compatible_printers_condition" and "inherits" fields.
+    // There will be 1 + num_exturders fields for "inherits" and 2 + num_extruders for "compatible_printers_condition" stored.
+    // The vector will not be stored if all fields are empty strings.
+    auto add_if_some_non_empty = [&out](std::vector<std::string> &&values, const std::string &key) {
+        bool nonempty = false;
+        for (const std::string &v : values)
+            if (! v.empty()) {
+                nonempty = true;
+                break;
+            }
+        if (nonempty)
+            out.set_key_value(key, new ConfigOptionStrings(std::move(values)));
+    };
+    add_if_some_non_empty(std::move(compatible_printers_condition), "compatible_printers_condition_cummulative");
+    add_if_some_non_empty(std::move(inherits),                      "inherits_cummulative");
+    return out;
+}
+
 // Load an external config file containing the print, filament and printer presets.
 // Instead of a config file, a G-code may be loaded containing the full set of parameters.
 // In the future the configuration will likely be read from an AMF file as well.
@@ -530,6 +619,8 @@ void PresetBundle::load_config_string(const char* str, const char* source_filena
 // Load a config file from a boost property_tree. This is a private method called from load_config_file.
 void PresetBundle::load_config_file_config(const std::string &name_or_path, bool is_external, DynamicPrintConfig &&config)
 {
+    PrinterTechnology printer_technology = Preset::printer_technology(config);
+
     // The "compatible_printers" field should not have been exported into a config.ini or a G-code anyway, 
     // but some of the alpha versions of Slic3r did.
     {
@@ -541,8 +632,10 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool
         }
     }
 
-    size_t num_extruders = std::min(config.option<ConfigOptionFloats>("nozzle_diameter"  )->values.size(), 
-                                    config.option<ConfigOptionFloats>("filament_diameter")->values.size());
+    size_t num_extruders = (printer_technology == ptFFF) ?
+        std::min(config.option<ConfigOptionFloats>("nozzle_diameter"  )->values.size(), 
+                 config.option<ConfigOptionFloats>("filament_diameter")->values.size()) :
+        0;
     // Make a copy of the "compatible_printers_condition_cummulative" and "inherits_cummulative" vectors, which 
     // accumulate values over all presets (print, filaments, printers).
     // These values will be distributed into their particular presets when loading.
@@ -553,7 +646,8 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool
     compatible_printers_condition_values.resize(num_extruders + 2, std::string());
     inherits_values.resize(num_extruders + 2, std::string());
     // The "default_filament_profile" will be later extracted into the printer profile.
-    config.option<ConfigOptionStrings>("default_filament_profile", true)->values.resize(num_extruders, std::string());
+    if (printer_technology == ptFFF)
+        config.option<ConfigOptionStrings>("default_filament_profile", true)->values.resize(num_extruders, std::string());
 
     // 1) Create a name from the file name.
     // Keep the suffix (.ini, .gcode, .amf, .3mf etc) to differentiate it from the normal profiles.
@@ -562,81 +656,83 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool
     // 2) If the loading succeeded, split and load the config into print / filament / printer settings.
     // First load the print and printer presets.
     for (size_t i_group = 0; i_group < 2; ++ i_group) {
-        PresetCollection &presets = (i_group == 0) ? this->prints : this->printers;
+        PresetCollection &presets = (i_group == 0) ? ((printer_technology == ptFFF) ? this->prints : this->sla_materials) : this->printers;
         // Split the "compatible_printers_condition" and "inherits" values one by one from a single vector to the print & printer profiles.
         size_t idx = (i_group == 0) ? 0 : num_extruders + 1;
         inherits                      = inherits_values[idx];
         compatible_printers_condition = compatible_printers_condition_values[idx];
 		if (is_external)
             presets.load_external_preset(name_or_path, name,
-                config.opt_string((i_group == 0) ? "print_settings_id" : "printer_settings_id", true), 
+                config.opt_string((i_group == 0) ? ((printer_technology == ptFFF) ? "print_settings_id" : "sla_material_id") :  "printer_settings_id", true),
                 config);
         else
             presets.load_preset(presets.path_from_name(name), name, config).save();
     }
 
-    // 3) Now load the filaments. If there are multiple filament presets, split them and load them.
-    auto old_filament_profile_names = config.option<ConfigOptionStrings>("filament_settings_id", true);
-	old_filament_profile_names->values.resize(num_extruders, std::string());
+    if (Preset::printer_technology(config) == ptFFF) {
+        // 3) Now load the filaments. If there are multiple filament presets, split them and load them.
+        auto old_filament_profile_names = config.option<ConfigOptionStrings>("filament_settings_id", true);
+    	old_filament_profile_names->values.resize(num_extruders, std::string());
 
-    if (num_extruders <= 1) {
-        // Split the "compatible_printers_condition" and "inherits" from the cummulative vectors to separate filament presets.
-        inherits                      = inherits_values[1];
-        compatible_printers_condition = compatible_printers_condition_values[1];
-        if (is_external)
-            this->filaments.load_external_preset(name_or_path, name, old_filament_profile_names->values.front(), config);
-        else
-            this->filaments.load_preset(this->filaments.path_from_name(name), name, config).save();
-        this->filament_presets.clear();
-        this->filament_presets.emplace_back(name);
-    } else {
-        // Split the filament presets, load each of them separately.
-        std::vector<DynamicPrintConfig> configs(num_extruders, this->filaments.default_preset().config);
-        // loop through options and scatter them into configs.
-        for (const t_config_option_key &key : this->filaments.default_preset().config.keys()) {
-            const ConfigOption *other_opt = config.option(key);
-            if (other_opt == nullptr)
-                continue;
-            if (other_opt->is_scalar()) {
-                for (size_t i = 0; i < configs.size(); ++ i)
-                    configs[i].option(key, false)->set(other_opt);
-            } else if (key != "compatible_printers") {
-                for (size_t i = 0; i < configs.size(); ++ i)
-                    static_cast<ConfigOptionVectorBase*>(configs[i].option(key, false))->set_at(other_opt, 0, i);
-            }
-        }
-        // Load the configs into this->filaments and make them active.
-        this->filament_presets.clear();
-        for (size_t i = 0; i < configs.size(); ++ i) {
-            DynamicPrintConfig &cfg = configs[i];
+        if (num_extruders <= 1) {
             // Split the "compatible_printers_condition" and "inherits" from the cummulative vectors to separate filament presets.
-            cfg.opt_string("compatible_printers_condition", true) = compatible_printers_condition_values[i + 1];
-            cfg.opt_string("inherits", true)                      = inherits_values[i + 1];
-            // Load all filament presets, but only select the first one in the preset dialog.
-            Preset *loaded = nullptr;
+            inherits                      = inherits_values[1];
+            compatible_printers_condition = compatible_printers_condition_values[1];
             if (is_external)
-                loaded = &this->filaments.load_external_preset(name_or_path, name,
-                    (i < old_filament_profile_names->values.size()) ? old_filament_profile_names->values[i] : "",
-                    std::move(cfg), i == 0);
-            else {
-                // Used by the config wizard when creating a custom setup.
-                // Therefore this block should only be called for a single extruder.
-                char suffix[64];
-                if (i == 0)
-                    suffix[0] = 0;
-                else
-                    sprintf(suffix, "%d", i);
-                std::string new_name = name + suffix;
-                loaded = &this->filaments.load_preset(this->filaments.path_from_name(new_name),
-                    new_name, std::move(cfg), i == 0);
-                loaded->save();
+                this->filaments.load_external_preset(name_or_path, name, old_filament_profile_names->values.front(), config);
+            else
+                this->filaments.load_preset(this->filaments.path_from_name(name), name, config).save();
+            this->filament_presets.clear();
+            this->filament_presets.emplace_back(name);
+        } else {
+            // Split the filament presets, load each of them separately.
+            std::vector<DynamicPrintConfig> configs(num_extruders, this->filaments.default_preset().config);
+            // loop through options and scatter them into configs.
+            for (const t_config_option_key &key : this->filaments.default_preset().config.keys()) {
+                const ConfigOption *other_opt = config.option(key);
+                if (other_opt == nullptr)
+                    continue;
+                if (other_opt->is_scalar()) {
+                    for (size_t i = 0; i < configs.size(); ++ i)
+                        configs[i].option(key, false)->set(other_opt);
+                } else if (key != "compatible_printers") {
+                    for (size_t i = 0; i < configs.size(); ++ i)
+                        static_cast<ConfigOptionVectorBase*>(configs[i].option(key, false))->set_at(other_opt, 0, i);
+                }
+            }
+            // Load the configs into this->filaments and make them active.
+            this->filament_presets.clear();
+            for (size_t i = 0; i < configs.size(); ++ i) {
+                DynamicPrintConfig &cfg = configs[i];
+                // Split the "compatible_printers_condition" and "inherits" from the cummulative vectors to separate filament presets.
+                cfg.opt_string("compatible_printers_condition", true) = compatible_printers_condition_values[i + 1];
+                cfg.opt_string("inherits", true)                      = inherits_values[i + 1];
+                // Load all filament presets, but only select the first one in the preset dialog.
+                Preset *loaded = nullptr;
+                if (is_external)
+                    loaded = &this->filaments.load_external_preset(name_or_path, name,
+                        (i < old_filament_profile_names->values.size()) ? old_filament_profile_names->values[i] : "",
+                        std::move(cfg), i == 0);
+                else {
+                    // Used by the config wizard when creating a custom setup.
+                    // Therefore this block should only be called for a single extruder.
+                    char suffix[64];
+                    if (i == 0)
+                        suffix[0] = 0;
+                    else
+                        sprintf(suffix, "%d", i);
+                    std::string new_name = name + suffix;
+                    loaded = &this->filaments.load_preset(this->filaments.path_from_name(new_name),
+                        new_name, std::move(cfg), i == 0);
+                    loaded->save();
+                }
+                this->filament_presets.emplace_back(loaded->name);
             }
-            this->filament_presets.emplace_back(loaded->name);
         }
-    }
 
-    // 4) Load the project config values (the per extruder wipe matrix etc).
-    this->project_config.apply_only(config, s_project_options);
+        // 4) Load the project config values (the per extruder wipe matrix etc).
+        this->project_config.apply_only(config, s_project_options);
+    }
 
     this->update_compatible_with_printer(false);
 }
@@ -691,9 +787,10 @@ void PresetBundle::load_config_file_config_bundle(const std::string &path, const
         collection_dst.load_preset(path, preset_name_dst, std::move(preset_src->config), activate).is_external = true;
         return preset_name_dst;
     };
-    load_one(this->prints,    tmp_bundle.prints,    tmp_bundle.prints   .get_selected_preset().name, true);
-    load_one(this->filaments, tmp_bundle.filaments, tmp_bundle.filaments.get_selected_preset().name, true);
-    load_one(this->printers,  tmp_bundle.printers,  tmp_bundle.printers .get_selected_preset().name, true);
+    load_one(this->prints,        tmp_bundle.prints,        tmp_bundle.prints       .get_selected_preset().name, true);
+    load_one(this->filaments,     tmp_bundle.filaments,     tmp_bundle.filaments    .get_selected_preset().name, true);
+    load_one(this->sla_materials, tmp_bundle.sla_materials, tmp_bundle.sla_materials.get_selected_preset().name, true);
+    load_one(this->printers,      tmp_bundle.printers,      tmp_bundle.printers     .get_selected_preset().name, true);
     this->update_multi_material_filament_presets();
     for (size_t i = 1; i < std::min(tmp_bundle.filament_presets.size(), this->filament_presets.size()); ++ i)
         this->filament_presets[i] = load_one(this->filaments, tmp_bundle.filaments, tmp_bundle.filament_presets[i], false);
@@ -817,6 +914,7 @@ static void flatten_configbundle_hierarchy(boost::property_tree::ptree &tree)
 {
     flatten_configbundle_hierarchy(tree, "print");
     flatten_configbundle_hierarchy(tree, "filament");
+    flatten_configbundle_hierarchy(tree, "sla_material");
     flatten_configbundle_hierarchy(tree, "printer");
 }
 
@@ -853,9 +951,11 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla
     // Parse the obsolete preset names, to be deleted when upgrading from the old configuration structure.
     std::vector<std::string> loaded_prints;
     std::vector<std::string> loaded_filaments;
+    std::vector<std::string> loaded_sla_materials;
     std::vector<std::string> loaded_printers;
     std::string              active_print;
     std::vector<std::string> active_filaments;
+    std::string              active_sla_material;
     std::string              active_printer;
     size_t                   presets_loaded = 0;
     for (const auto &section : tree) {
@@ -870,6 +970,10 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla
             presets = &this->filaments;
             loaded  = &loaded_filaments;
             preset_name = section.first.substr(9);
+        } else if (boost::starts_with(section.first, "sla_material:")) {
+            presets = &this->sla_materials;
+            loaded  = &loaded_sla_materials;
+            preset_name = section.first.substr(9);
         } else if (boost::starts_with(section.first, "printer:")) {
             presets = &this->printers;
             loaded  = &loaded_printers;
@@ -886,6 +990,8 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla
                             active_filaments.resize(idx + 1, std::string());
                         active_filaments[idx] = kvp.second.data();
                     }
+                } else if (kvp.first == "sla_material") {
+                    active_sla_material = kvp.second.data();
                 } else if (kvp.first == "printer") {
                     active_printer = kvp.second.data();
                 }
@@ -899,6 +1005,8 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla
                     dst = &this->obsolete_presets.prints;
                 else if (kvp.first == "filament")
                     dst = &this->obsolete_presets.filaments;
+                else if (kvp.first == "sla_material")
+                    dst = &this->obsolete_presets.sla_materials;
                 else if (kvp.first == "printer")
                     dst = &this->obsolete_presets.printers;
                 if (dst)
@@ -999,6 +1107,8 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla
     if ((flags & LOAD_CFGBNDLE_SYSTEM) == 0) {
         if (! active_print.empty()) 
             prints.select_preset_by_name(active_print, true);
+        if (! active_sla_material.empty()) 
+            sla_materials.select_preset_by_name(active_sla_material, true);
         if (! active_printer.empty())
             printers.select_preset_by_name(active_printer, true);
         // Activate the first filament preset.
@@ -1015,6 +1125,9 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla
 
 void PresetBundle::update_multi_material_filament_presets()
 {
+    if (printers.get_edited_preset().printer_technology() != ptFFF)
+        return;
+
     // Verify and select the filament presets.
     auto   *nozzle_diameter = static_cast<const ConfigOptionFloats*>(printers.get_edited_preset().config.option("nozzle_diameter"));
     size_t  num_extruders   = nozzle_diameter->values.size();
@@ -1055,36 +1168,51 @@ void PresetBundle::update_multi_material_filament_presets()
 void PresetBundle::update_compatible_with_printer(bool select_other_if_incompatible)
 {
     const Preset                   &printer_preset             = this->printers.get_edited_preset();
-    const std::string              &prefered_print_profile     = printer_preset.config.opt_string("default_print_profile");
-    const std::vector<std::string> &prefered_filament_profiles = printer_preset.config.option<ConfigOptionStrings>("default_filament_profile")->values;
-    prefered_print_profile.empty() ?
-        this->prints.update_compatible_with_printer(printer_preset, select_other_if_incompatible) :
-        this->prints.update_compatible_with_printer(printer_preset, select_other_if_incompatible,
-            [&prefered_print_profile](const std::string& profile_name){ return profile_name == prefered_print_profile; });
-    prefered_filament_profiles.empty() ?
-        this->filaments.update_compatible_with_printer(printer_preset, select_other_if_incompatible) :
-        this->filaments.update_compatible_with_printer(printer_preset, select_other_if_incompatible,
-            [&prefered_filament_profiles](const std::string& profile_name)
-                { return std::find(prefered_filament_profiles.begin(), prefered_filament_profiles.end(), profile_name) != prefered_filament_profiles.end(); });
-    if (select_other_if_incompatible) {
-        // Verify validity of the current filament presets.
-        this->filament_presets.front() = this->filaments.get_edited_preset().name;
-        for (size_t idx = 1; idx < this->filament_presets.size(); ++ idx) {
-            std::string &filament_name = this->filament_presets[idx];
-            Preset      *preset        = this->filaments.find_preset(filament_name, false);
-            if (preset == nullptr || ! preset->is_compatible) {
-                // Pick a compatible profile. If there are prefered_filament_profiles, use them.
-                if (prefered_filament_profiles.empty())
-                    filament_name = this->filaments.first_compatible().name;
-                else {
-                    const std::string &preferred = (idx < prefered_filament_profiles.size()) ? 
-                        prefered_filament_profiles[idx] : prefered_filament_profiles.front();
-                    filament_name = this->filaments.first_compatible(
-                        [&preferred](const std::string& profile_name){ return profile_name == preferred; }).name;
+
+    switch (printers.get_edited_preset().printer_technology()) {
+    case ptFFF:
+    {
+        const std::string              &prefered_print_profile     = printer_preset.config.opt_string("default_print_profile");
+        const std::vector<std::string> &prefered_filament_profiles = printer_preset.config.option<ConfigOptionStrings>("default_filament_profile")->values;
+        prefered_print_profile.empty() ?
+            this->prints.update_compatible_with_printer(printer_preset, select_other_if_incompatible) :
+            this->prints.update_compatible_with_printer(printer_preset, select_other_if_incompatible,
+                [&prefered_print_profile](const std::string& profile_name){ return profile_name == prefered_print_profile; });
+        prefered_filament_profiles.empty() ?
+            this->filaments.update_compatible_with_printer(printer_preset, select_other_if_incompatible) :
+            this->filaments.update_compatible_with_printer(printer_preset, select_other_if_incompatible,
+                [&prefered_filament_profiles](const std::string& profile_name)
+                    { return std::find(prefered_filament_profiles.begin(), prefered_filament_profiles.end(), profile_name) != prefered_filament_profiles.end(); });
+        if (select_other_if_incompatible) {
+            // Verify validity of the current filament presets.
+            this->filament_presets.front() = this->filaments.get_edited_preset().name;
+            for (size_t idx = 1; idx < this->filament_presets.size(); ++ idx) {
+                std::string &filament_name = this->filament_presets[idx];
+                Preset      *preset        = this->filaments.find_preset(filament_name, false);
+                if (preset == nullptr || ! preset->is_compatible) {
+                    // Pick a compatible profile. If there are prefered_filament_profiles, use them.
+                    if (prefered_filament_profiles.empty())
+                        filament_name = this->filaments.first_compatible().name;
+                    else {
+                        const std::string &preferred = (idx < prefered_filament_profiles.size()) ? 
+                            prefered_filament_profiles[idx] : prefered_filament_profiles.front();
+                        filament_name = this->filaments.first_compatible(
+                            [&preferred](const std::string& profile_name){ return profile_name == preferred; }).name;
+                    }
                 }
             }
         }
     }
+    case ptSLA:
+    {
+        const std::string              &prefered_print_profile     = printer_preset.config.opt_string("default_print_profile");
+        const std::vector<std::string> &prefered_filament_profiles = printer_preset.config.option<ConfigOptionStrings>("default_filament_profile")->values;
+        prefered_print_profile.empty() ?
+            this->sla_materials.update_compatible_with_printer(printer_preset, select_other_if_incompatible) :
+			this->sla_materials.update_compatible_with_printer(printer_preset, select_other_if_incompatible,
+                [&prefered_print_profile](const std::string& profile_name){ return profile_name == prefered_print_profile; });        
+    }
+    }
 }
 
 void PresetBundle::export_configbundle(const std::string &path) //, const DynamicPrintConfig &settings
@@ -1111,6 +1239,7 @@ void PresetBundle::export_configbundle(const std::string &path) //, const Dynami
     // Export the names of the active presets.
     c << std::endl << "[presets]" << std::endl;
     c << "print = " << this->prints.get_selected_preset().name << std::endl;
+    c << "sla_material = " << this->sla_materials.get_selected_preset().name << std::endl;
     c << "printer = " << this->printers.get_selected_preset().name << std::endl;
     for (size_t i = 0; i < this->filament_presets.size(); ++ i) {
         char suffix[64];
@@ -1170,7 +1299,7 @@ bool PresetBundle::parse_color(const std::string &scolor, unsigned char *rgb_out
 
 void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, wxBitmapComboBox *ui)
 {
-    if (ui == nullptr)
+    if (ui == nullptr || this->printers.get_edited_preset().printer_technology() == ptSLA)
         return;
 
     unsigned char rgb[3];
@@ -1265,6 +1394,7 @@ void PresetBundle::set_default_suppressed(bool default_suppressed)
 {
     prints.set_default_suppressed(default_suppressed);
     filaments.set_default_suppressed(default_suppressed);
+    sla_materials.set_default_suppressed(default_suppressed);
     printers.set_default_suppressed(default_suppressed);
 }
 
diff --git a/xs/src/slic3r/GUI/PresetBundle.hpp b/xs/src/slic3r/GUI/PresetBundle.hpp
index a5c5682f9..68ec534da 100644
--- a/xs/src/slic3r/GUI/PresetBundle.hpp
+++ b/xs/src/slic3r/GUI/PresetBundle.hpp
@@ -40,6 +40,7 @@ public:
 
     PresetCollection            prints;
     PresetCollection            filaments;
+    PresetCollection            sla_materials;
     PresetCollection            printers;
     // Filament preset names for a multi-extruder or multi-material print.
     // extruders.size() should be the same as printers.get_edited_preset().config.nozzle_diameter.size()
@@ -57,12 +58,13 @@ public:
     struct ObsoletePresets {
         std::vector<std::string> prints;
         std::vector<std::string> filaments;
+        std::vector<std::string> sla_materials;
         std::vector<std::string> printers;
     };
     ObsoletePresets             obsolete_presets;
 
     bool                        has_defauls_only() const 
-        { return prints.size() <= 1 && filaments.size() <= 1 && printers.size() <= 1; }
+        { return prints.has_defaults_only() && filaments.has_defaults_only() && printers.has_defaults_only(); }
 
     DynamicPrintConfig          full_config() const;
 
@@ -146,6 +148,9 @@ private:
     void                        load_config_file_config_bundle(const std::string &path, const boost::property_tree::ptree &tree);
     bool                        load_compatible_bitmaps();
 
+    DynamicPrintConfig          full_fff_config() const;
+    DynamicPrintConfig          full_sla_config() const;
+
     // Indicator, that the preset is compatible with the selected printer.
     wxBitmap                            *m_bitmapCompatible;
     // Indicator, that the preset is NOT compatible with the selected printer.
diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp
index 6505e1092..5daf2784d 100644
--- a/xs/src/slic3r/GUI/Tab.cpp
+++ b/xs/src/slic3r/GUI/Tab.cpp
@@ -214,7 +214,7 @@ void Tab::load_initial_data()
 	m_tt_non_system = m_presets->get_selected_preset_parent() ? &m_tt_value_unlock : &m_ttg_white_bullet_ns;
 }
 
-PageShp Tab::add_options_page(const wxString& title, const std::string& icon, bool is_extruder_pages/* = false*/)
+Slic3r::GUI::PageShp Tab::add_options_page(const wxString& title, const std::string& icon, bool is_extruder_pages /*= false*/)
 {
 	// Index of icon in an icon list $self->{icons}.
 	auto icon_idx = 0;
@@ -238,7 +238,8 @@ PageShp Tab::add_options_page(const wxString& title, const std::string& icon, bo
 	page->SetScrollbars(1, 1, 1, 1);
 	page->Hide();
 	m_hsizer->Add(page.get(), 1, wxEXPAND | wxLEFT, 5);
-	if (!is_extruder_pages) 
+
+    if (!is_extruder_pages) 
 		m_pages.push_back(page);
 
 	page->set_config(m_config);
@@ -313,10 +314,10 @@ void Tab::update_changed_ui()
 	if (m_postpone_update_ui) 
 		return;
 
-	const bool is_printer_type = (name() == "printer");
-	auto dirty_options = m_presets->current_dirty_options(is_printer_type);
-	auto nonsys_options = m_presets->current_different_from_parent_options(is_printer_type);
-	if (is_printer_type){
+	const bool deep_compare = (m_name == "printer" || m_name == "sla_material");
+	auto dirty_options = m_presets->current_dirty_options(deep_compare);
+	auto nonsys_options = m_presets->current_different_from_parent_options(deep_compare);
+    if (name() == "printer"){
 		TabPrinter* tab = static_cast<TabPrinter*>(this);
 		if (tab->m_initial_extruders_count != tab->m_extruders_count)
 			dirty_options.emplace_back("extruders_count");
@@ -397,7 +398,7 @@ void Tab::init_options_list()
 }
 
 template<class T>
-void add_correct_opts_to_options_list(const std::string &opt_key, std::map<std::string, int>& map, TabPrinter *tab, const int& value)
+void add_correct_opts_to_options_list(const std::string &opt_key, std::map<std::string, int>& map, Tab *tab, const int& value)
 {
 	T *opt_cur = static_cast<T*>(tab->m_config->option(opt_key));
 	for (int i = 0; i < opt_cur->values.size(); i++)
@@ -429,6 +430,30 @@ void TabPrinter::init_options_list()
 	m_options_list.emplace("extruders_count", m_opt_status_value);
 }
 
+void TabSLAMaterial::init_options_list()
+{
+    if (!m_options_list.empty())
+        m_options_list.clear();
+
+    for (const auto opt_key : m_config->keys())
+    {
+        if (opt_key == "compatible_printers"){
+            m_options_list.emplace(opt_key, m_opt_status_value);
+            continue;
+        }
+        switch (m_config->option(opt_key)->type())
+        {
+        case coInts:	add_correct_opts_to_options_list<ConfigOptionInts		>(opt_key, m_options_list, this, m_opt_status_value);	break;
+        case coBools:	add_correct_opts_to_options_list<ConfigOptionBools		>(opt_key, m_options_list, this, m_opt_status_value);	break;
+        case coFloats:	add_correct_opts_to_options_list<ConfigOptionFloats		>(opt_key, m_options_list, this, m_opt_status_value);	break;
+        case coStrings:	add_correct_opts_to_options_list<ConfigOptionStrings	>(opt_key, m_options_list, this, m_opt_status_value);	break;
+        case coPercents:add_correct_opts_to_options_list<ConfigOptionPercents	>(opt_key, m_options_list, this, m_opt_status_value);	break;
+        case coPoints:	add_correct_opts_to_options_list<ConfigOptionPoints		>(opt_key, m_options_list, this, m_opt_status_value);	break;
+        default:		m_options_list.emplace(opt_key, m_opt_status_value);		break;
+        }
+    }
+}
+
 void Tab::get_sys_and_mod_flags(const std::string& opt_key, bool& sys_page, bool& modified_page)
 {
 	auto opt = m_options_list.find(opt_key);
@@ -649,12 +674,21 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value)
 			int val = boost::any_cast<size_t>(value);
 			event.SetInt(val);
 		}
+
+        if (opt_key == "printer_technology")
+        {
+            int val = boost::any_cast<PrinterTechnology>(value);
+            event.SetInt(val);
+            g_wxMainFrame->ProcessWindowEvent(event);
+            return;
+        }
+        
 		g_wxMainFrame->ProcessWindowEvent(event);
 	}
 	if (opt_key == "fill_density")
 	{
-		boost::any val = get_optgroup()->get_config_value(*m_config, opt_key);
-		get_optgroup()->set_value(opt_key, val);
+		boost::any val = get_optgroup(ogFrequentlyChangingParameters)->get_config_value(*m_config, opt_key);
+		get_optgroup(ogFrequentlyChangingParameters)->set_value(opt_key, val);
 	}
 	if (opt_key == "support_material" || opt_key == "support_material_buildplate_only")
 	{
@@ -663,12 +697,12 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value)
 								m_config->opt_bool("support_material_buildplate_only") ?
 									_("Support on build plate only") :
 									_("Everywhere");
-		get_optgroup()->set_value("support", new_selection);
+		get_optgroup(ogFrequentlyChangingParameters)->set_value("support", new_selection);
 	}
 	if (opt_key == "brim_width")
 	{
 		bool val = m_config->opt_float("brim_width") > 0.0 ? true : false;
-		get_optgroup()->set_value("brim", val);
+		get_optgroup(ogFrequentlyChangingParameters)->set_value("brim", val);
 	}
 
     if (opt_key == "wipe_tower" || opt_key == "single_extruder_multi_material" || opt_key == "extruders_count" )
@@ -677,17 +711,16 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value)
 	update();
 }
 
-
 // Show/hide the 'purging volumes' button
 void Tab::update_wiping_button_visibility() {
+    if (get_preset_bundle()->printers.get_selected_preset().printer_technology() == ptSLA)
+        return; // ys_FIXME
     bool wipe_tower_enabled = dynamic_cast<ConfigOptionBool*>(  (m_preset_bundle->prints.get_edited_preset().config  ).option("wipe_tower"))->value;
     bool multiple_extruders = dynamic_cast<ConfigOptionFloats*>((m_preset_bundle->printers.get_edited_preset().config).option("nozzle_diameter"))->values.size() > 1;
     bool single_extruder_mm = dynamic_cast<ConfigOptionBool*>(  (m_preset_bundle->printers.get_edited_preset().config).option("single_extruder_multi_material"))->value;
 
-    if (wipe_tower_enabled && multiple_extruders && single_extruder_mm)
-        get_wiping_dialog_button()->Show();
-    else get_wiping_dialog_button()->Hide();
-
+	get_wiping_dialog_button()->Show(wipe_tower_enabled && multiple_extruders && single_extruder_mm);
+	
     (get_wiping_dialog_button()->GetParent())->Layout();
 }
 
@@ -754,18 +787,18 @@ void Tab::update_preset_description_line()
 
 void Tab::update_frequently_changed_parameters()
 {
-	boost::any value = get_optgroup()->get_config_value(*m_config, "fill_density");
-	get_optgroup()->set_value("fill_density", value);
+	boost::any value = get_optgroup(ogFrequentlyChangingParameters)->get_config_value(*m_config, "fill_density");
+	get_optgroup(ogFrequentlyChangingParameters)->set_value("fill_density", value);
 
 	wxString new_selection = !m_config->opt_bool("support_material") ?
 							_("None") :
 							m_config->opt_bool("support_material_buildplate_only") ?
 								_("Support on build plate only") :
 								_("Everywhere");
-	get_optgroup()->set_value("support", new_selection);
+	get_optgroup(ogFrequentlyChangingParameters)->set_value("support", new_selection);
 
 	bool val = m_config->opt_float("brim_width") > 0.0 ? true : false;
-	get_optgroup()->set_value("brim", val);
+	get_optgroup(ogFrequentlyChangingParameters)->set_value("brim", val);
 
 	update_wiping_button_visibility();
 }
@@ -1007,6 +1040,9 @@ void TabPrint::reload_config(){
 
 void TabPrint::update()
 {
+    if (get_preset_bundle()->printers.get_selected_preset().printer_technology() == ptSLA)
+        return; // ys_FIXME
+
 	Freeze();
 
 	double fill_density = m_config->option<ConfigOptionPercent>("fill_density")->value;
@@ -1368,6 +1404,9 @@ void TabFilament::reload_config(){
 
 void TabFilament::update()
 {
+    if (get_preset_bundle()->printers.get_selected_preset().printer_technology() == ptSLA)
+        return; // ys_FIXME
+
 	Freeze();
 	wxString text = from_u8(PresetHints::cooling_description(m_presets->get_edited_preset()));
 	m_cooling_description_line->SetText(text);
@@ -1413,6 +1452,17 @@ void TabPrinter::build()
 	m_presets = &m_preset_bundle->printers;
 	load_initial_data();
 
+    m_printer_technology = m_presets->get_selected_preset().printer_technology();
+
+    m_presets->get_selected_preset().printer_technology() == ptSLA ? build_sla() : build_fff();
+
+//     on_value_change("printer_technology", m_printer_technology); // to update show/hide preset ComboBoxes
+}
+
+void TabPrinter::build_fff()
+{
+    if (!m_pages.empty())
+        m_pages.resize(0);
 	// to avoid redundant memory allocation / deallocation during extruders count changing
 	m_pages.reserve(30);
 
@@ -1428,7 +1478,7 @@ void TabPrinter::build()
 		Line line{ _(L("Bed shape")), "" };
 		line.widget = [this](wxWindow* parent){
 			auto btn = new wxButton(parent, wxID_ANY, _(L(" Set "))+dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT);
-			//			btn->SetFont(Slic3r::GUI::small_font);
+			btn->SetFont(Slic3r::GUI::small_font());
 			btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("printer_empty.png")), wxBITMAP_TYPE_PNG));
 
 			auto sizer = new wxBoxSizer(wxHORIZONTAL);
@@ -1496,7 +1546,7 @@ void TabPrinter::build()
 			auto serial_test = [this](wxWindow* parent){
 				auto btn = m_serial_test_btn = new wxButton(parent, wxID_ANY,
 					_(L("Test")), wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT);
-//				btn->SetFont($Slic3r::GUI::small_font);
+				btn->SetFont(Slic3r::GUI::small_font());
 				btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("wrench.png")), wxBITMAP_TYPE_PNG));
 				auto sizer = new wxBoxSizer(wxHORIZONTAL);
 				sizer->Add(btn);
@@ -1699,6 +1749,76 @@ void TabPrinter::build()
 		update_serial_ports();
 }
 
+void TabPrinter::build_sla()
+{
+    if (!m_pages.empty())
+        m_pages.resize(0);
+    auto page = add_options_page(_(L("General")), "printer_empty.png");
+    auto optgroup = page->new_optgroup(_(L("Size and coordinates")));
+
+    Line line{ _(L("Bed shape")), "" };
+    line.widget = [this](wxWindow* parent){
+        auto btn = new wxButton(parent, wxID_ANY, _(L(" Set ")) + dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT);
+        //			btn->SetFont(Slic3r::GUI::small_font);
+        btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("printer_empty.png")), wxBITMAP_TYPE_PNG));
+
+        auto sizer = new wxBoxSizer(wxHORIZONTAL);
+        sizer->Add(btn);
+
+        btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e)
+        {
+            auto dlg = new BedShapeDialog(this);
+            dlg->build_dialog(m_config->option<ConfigOptionPoints>("bed_shape"));
+            if (dlg->ShowModal() == wxID_OK){
+                load_key_value("bed_shape", dlg->GetValue());
+                update_changed_ui();
+            }
+        }));
+
+        return sizer;
+    };
+    optgroup->append_line(line, &m_colored_Label);
+    optgroup->append_single_option_line("max_print_height");
+
+    optgroup = page->new_optgroup(_(L("Display")));
+    optgroup->append_single_option_line("display_width");
+    optgroup->append_single_option_line("display_height");
+
+    auto option = optgroup->get_option("display_pixels_x");
+    line = { _(option.opt.full_label), "" };
+    line.append_option(option);
+    line.append_option(optgroup->get_option("display_pixels_y"));
+    optgroup->append_line(line);
+
+    optgroup = page->new_optgroup(_(L("Corrections")));
+    line = Line{ m_config->def()->get("printer_correction")->full_label, "" };
+    std::vector<std::string> axes{ "X", "Y", "Z" };
+    int id = 0;
+    for (auto& axis : axes) {
+        auto opt = optgroup->get_option("printer_correction", id);
+        opt.opt.label = axis;
+        line.append_option(opt);
+        ++id;
+    }
+    optgroup->append_line(line);
+
+    page = add_options_page(_(L("Notes")), "note.png");
+    optgroup = page->new_optgroup(_(L("Notes")), 0);
+    option = optgroup->get_option("printer_notes");
+    option.opt.full_width = true;
+    option.opt.height = 250;
+    optgroup->append_single_option_line(option);
+
+    page = add_options_page(_(L("Dependencies")), "wrench.png");
+    optgroup = page->new_optgroup(_(L("Profile dependencies")));
+    line = Line{ "", "" };
+    line.full_width = 1;
+    line.widget = [this](wxWindow* parent) {
+        return description_line_widget(parent, &m_parent_preset_description_line);
+    };
+    optgroup->append_line(line);
+}
+
 void TabPrinter::update_serial_ports(){
 	Field *field = get_field("serial_port");
 	Choice *choice = static_cast<Choice *>(field);
@@ -1712,6 +1832,7 @@ void TabPrinter::extruders_count_changed(size_t extruders_count){
 	build_extruder_pages();
 	reload_config();
 	on_value_change("extruders_count", extruders_count);
+    update_objects_list_extruder_column(extruders_count);
 }
 
 void TabPrinter::append_option_line(ConfigOptionsGroupShp optgroup, const std::string opt_key)
@@ -1890,7 +2011,38 @@ void TabPrinter::on_preset_loaded()
 	extruders_count_changed(extruders_count);
 }
 
-void TabPrinter::update(){
+void TabPrinter::update_pages()
+{
+    // update m_pages ONLY if printer technology is changed
+    if (m_presets->get_edited_preset().printer_technology() == m_printer_technology)
+        return;
+
+    // hide all old pages
+    for (auto& el : m_pages)
+        el.get()->Hide();
+
+    // set m_pages to m_pages_(technology before changing)
+    m_printer_technology == ptFFF ? m_pages.swap(m_pages_fff) : m_pages.swap(m_pages_sla);
+
+    // build Tab according to the technology, if it's not exist jet OR
+    // set m_pages_(technology after changing) to m_pages
+    if (m_presets->get_edited_preset().printer_technology() == ptFFF)
+        m_pages_fff.empty() ? build_fff() : m_pages.swap(m_pages_fff);
+    else 
+        m_pages_sla.empty() ? build_sla() : m_pages.swap(m_pages_sla);
+
+    rebuild_page_tree(true);
+
+    on_value_change("printer_technology", m_presets->get_edited_preset().printer_technology()); // to update show/hide preset ComboBoxes
+}
+
+void TabPrinter::update()
+{
+    m_presets->get_edited_preset().printer_technology() == ptFFF ? update_fff() : update_sla();
+}
+
+void TabPrinter::update_fff()
+{
 	Freeze();
 
 	bool en;
@@ -1993,20 +2145,25 @@ void TabPrinter::update(){
 	Thaw();
 }
 
+void TabPrinter::update_sla(){ ; }
+
 // Initialize the UI from the current preset
 void Tab::load_current_preset()
 {
 	auto preset = m_presets->get_edited_preset();
 
 	(preset.is_default || preset.is_system) ? m_btn_delete_preset->Disable() : m_btn_delete_preset->Enable(true);
-	update();
-	// For the printer profile, generate the extruder pages.
-	on_preset_loaded();
-	// Reload preset pages with the new configuration values.
-	reload_config();
+
+    update();
+    // For the printer profile, generate the extruder pages.
+    if (preset.printer_technology() == ptFFF)
+        on_preset_loaded();
+    // Reload preset pages with the new configuration values.
+    reload_config();
+
 	m_bmp_non_system = m_presets->get_selected_preset_parent() ? &m_bmp_value_unlock : &m_bmp_white_bullet;
 	m_ttg_non_system = m_presets->get_selected_preset_parent() ? &m_ttg_value_unlock : &m_ttg_white_bullet_ns;
-	m_tt_non_system = m_presets->get_selected_preset_parent() ? &m_tt_value_unlock : &m_ttg_white_bullet_ns;
+	m_tt_non_system = m_presets->get_selected_preset_parent()  ? &m_tt_value_unlock  : &m_ttg_white_bullet_ns;
 
 	m_undo_to_sys_btn->Enable(!preset.is_default);
 
@@ -2019,6 +2176,27 @@ void Tab::load_current_preset()
 		if (!checked_tab(this))
 			return;
 		update_tab_ui();
+
+        // update show/hide tabs
+        if (m_name == "printer"){
+            PrinterTechnology& printer_technology = m_presets->get_edited_preset().printer_technology();
+            if (printer_technology != static_cast<TabPrinter*>(this)->m_printer_technology)
+            {
+                for (auto& tab : get_preset_tabs()){
+                    if (tab.technology != printer_technology)
+                    {
+                        int page_id = get_tab_panel()->FindPage(tab.panel);
+                        get_tab_panel()->GetPage(page_id)->Show(false);
+                        get_tab_panel()->RemovePage(page_id);
+                    }
+                    else
+                        get_tab_panel()->InsertPage(get_tab_panel()->FindPage(this), tab.panel, tab.panel->title());
+                }
+
+                static_cast<TabPrinter*>(this)->m_printer_technology = printer_technology;
+            }
+        }
+
 		on_presets_changed();
 
 		if (name() == "print")
@@ -2036,7 +2214,7 @@ void Tab::load_current_preset()
 }
 
 //Regerenerate content of the page tree.
-void Tab::rebuild_page_tree()
+void Tab::rebuild_page_tree(bool tree_sel_change_event /*= false*/)
 {
 	Freeze();
 	// get label of the currently selected item
@@ -2051,9 +2229,9 @@ void Tab::rebuild_page_tree()
 		m_treectrl->SetItemTextColour(itemId, p->get_item_colour());
 		if (p->title() == selected) {
 			if (!(p->title() == _(L("Machine limits")) || p->title() == _(L("Single extruder MM setup")))) // These Pages have to be updated inside OnTreeSelChange
-				m_disable_tree_sel_changed_event = 1;
+				m_disable_tree_sel_changed_event = !tree_sel_change_event;
 			m_treectrl->SelectItem(itemId);
-			m_disable_tree_sel_changed_event = 0;
+			m_disable_tree_sel_changed_event = false;
 			have_selection = 1;
 		}
 	}
@@ -2068,48 +2246,53 @@ void Tab::rebuild_page_tree()
 // Called by the UI combo box when the user switches profiles.
 // Select a preset by a name.If !defined(name), then the default preset is selected.
 // If the current profile is modified, user is asked to save the changes.
-void Tab::select_preset(const std::string& preset_name /*= ""*/)
+void Tab::select_preset(std::string preset_name /*= ""*/)
 {
-	std::string name = preset_name;
-	auto force = false;
-	auto presets = m_presets;
 	// If no name is provided, select the "-- default --" preset.
-	if (name.empty())
-		name= presets->default_preset().name;
-	auto current_dirty = presets->current_is_dirty();
-	auto canceled = false;
-	auto printer_tab = presets->name().compare("printer")==0;
+	if (preset_name.empty())
+		preset_name = m_presets->default_preset().name;
+	auto current_dirty = m_presets->current_is_dirty();
+	auto printer_tab   = m_presets->name() == "printer";
+	auto canceled      = false;
 	m_reload_dependent_tabs = {};
-	if (!force && current_dirty && !may_discard_current_dirty_preset()) {
+	if (current_dirty && !may_discard_current_dirty_preset()) {
 		canceled = true;
-	} else if(printer_tab) {
+	} else if (printer_tab) {
 		// Before switching the printer to a new one, verify, whether the currently active print and filament
 		// are compatible with the new printer.
 		// If they are not compatible and the current print or filament are dirty, let user decide
 		// whether to discard the changes or keep the current printer selection.
-		auto new_printer_preset = presets->find_preset(name, true);
-		auto print_presets = &m_preset_bundle->prints;
-		bool print_preset_dirty = print_presets->current_is_dirty();
-		bool print_preset_compatible = print_presets->get_edited_preset().is_compatible_with_printer(*new_printer_preset);
-		canceled = !force && print_preset_dirty && !print_preset_compatible &&
-			!may_discard_current_dirty_preset(print_presets, name);
-		auto filament_presets = &m_preset_bundle->filaments;
-		bool filament_preset_dirty = filament_presets->current_is_dirty();
-		bool filament_preset_compatible = filament_presets->get_edited_preset().is_compatible_with_printer(*new_printer_preset);
-		if (!canceled && !force) {
-			canceled = filament_preset_dirty && !filament_preset_compatible &&
-				!may_discard_current_dirty_preset(filament_presets, name);
+		//
+		// With the introduction of the SLA printer types, we need to support switching between
+		// the FFF and SLA printers.
+		const Preset 		&new_printer_preset     = *m_presets->find_preset(preset_name, true);
+		PrinterTechnology    old_printer_technology = m_presets->get_edited_preset().printer_technology();
+		PrinterTechnology    new_printer_technology = new_printer_preset.printer_technology();
+		struct PresetUpdate {
+			std::string          name;
+			PresetCollection 	*presets;
+			PrinterTechnology    technology;
+			bool    	         old_preset_dirty;
+			bool         	     new_preset_compatible;
+		};
+		std::vector<PresetUpdate> updates = {
+			{ "print",			&m_preset_bundle->prints,			ptFFF },
+			{ "filament",		&m_preset_bundle->filaments,		ptFFF },
+ 			{ "sla_material",	&m_preset_bundle->sla_materials,	ptSLA }
+		};
+		for (PresetUpdate &pu : updates) {
+			pu.old_preset_dirty      = (old_printer_technology == pu.technology) && pu.presets->current_is_dirty();
+			pu.new_preset_compatible = (new_printer_technology == pu.technology) && pu.presets->get_edited_preset().is_compatible_with_printer(new_printer_preset);
+			if (! canceled)
+				canceled = pu.old_preset_dirty && ! pu.new_preset_compatible && ! may_discard_current_dirty_preset(pu.presets, preset_name);
 		}
-		if (!canceled) {
-			if (!print_preset_compatible) {
+		if (! canceled) {
+			for (PresetUpdate &pu : updates) {
 				// The preset will be switched to a different, compatible preset, or the '-- default --'.
-				m_reload_dependent_tabs.push_back("print");
-				if (print_preset_dirty) print_presets->discard_current_changes();
-			}
-			if (!filament_preset_compatible) {
-				// The preset will be switched to a different, compatible preset, or the '-- default --'.
-				m_reload_dependent_tabs.push_back("filament");
-				if (filament_preset_dirty) filament_presets->discard_current_changes();
+                if (pu.technology == new_printer_technology)
+				    m_reload_dependent_tabs.emplace_back(pu.name);
+				if (pu.old_preset_dirty)
+					pu.presets->discard_current_changes();
 			}
 		}
 	}
@@ -2118,19 +2301,20 @@ void Tab::select_preset(const std::string& preset_name /*= ""*/)
 		// Trigger the on_presets_changed event so that we also restore the previous value in the plater selector,
 		// if this action was initiated from the platter.
 		on_presets_changed();
-	}
-	else {
-		if (current_dirty) presets->discard_current_changes() ;
-		presets->select_preset_by_name(name, force);
+	} else {
+		if (current_dirty)
+			m_presets->discard_current_changes() ;
+		m_presets->select_preset_by_name(preset_name, false);
 		// Mark the print & filament enabled if they are compatible with the currently selected preset.
 		// The following method should not discard changes of current print or filament presets on change of a printer profile,
 		// if they are compatible with the current printer.
 		if (current_dirty || printer_tab)
 			m_preset_bundle->update_compatible_with_printer(true);
 		// Initialize the UI from the current preset.
+        if (printer_tab)
+            static_cast<TabPrinter*>(this)->update_pages();
 		load_current_preset();
 	}
-
 }
 
 // If the current preset is dirty, the user is asked whether the changes may be discarded.
@@ -2772,5 +2956,71 @@ void SavePresetWindow::accept()
 	}
 }
 
+void TabSLAMaterial::build()
+{
+    m_presets = &m_preset_bundle->sla_materials;
+    load_initial_data();
+
+    auto page = add_options_page(_(L("Material")), "package_green.png");
+
+    auto optgroup = page->new_optgroup(_(L("Layers")));
+    optgroup->append_single_option_line("layer_height");
+    optgroup->append_single_option_line("initial_layer_height");
+
+    optgroup = page->new_optgroup(_(L("Exposure")));
+    optgroup->append_single_option_line("exposure_time");
+    optgroup->append_single_option_line("initial_exposure_time");
+
+    optgroup = page->new_optgroup(_(L("Corrections")));
+    optgroup->label_width = 190;
+    std::vector<std::string> corrections = { "material_correction_printing", "material_correction_curing" };
+    std::vector<std::string> axes{ "X", "Y", "Z" };
+    for (auto& opt_key : corrections){
+        auto line = Line{ m_config->def()->get(opt_key)->full_label, "" };
+        int id = 0;
+        for (auto& axis : axes) {
+            auto opt = optgroup->get_option(opt_key, id);
+            opt.opt.label = axis;
+            opt.opt.width = 60;
+            line.append_option(opt);
+            ++id;
+        }
+        optgroup->append_line(line);
+    }
+
+    page = add_options_page(_(L("Notes")), "note.png");
+    optgroup = page->new_optgroup(_(L("Notes")), 0);
+    optgroup->label_width = 0;
+    Option option = optgroup->get_option("material_notes");
+    option.opt.full_width = true;
+    option.opt.height = 250;
+    optgroup->append_single_option_line(option);
+
+    page = add_options_page(_(L("Dependencies")), "wrench.png");
+    optgroup = page->new_optgroup(_(L("Profile dependencies")));
+    auto line = Line { _(L("Compatible printers")), "" };
+    line.widget = [this](wxWindow* parent){
+        return compatible_printers_widget(parent, &m_compatible_printers_checkbox, &m_compatible_printers_btn);
+    };
+    optgroup->append_line(line, &m_colored_Label);
+
+    option = optgroup->get_option("compatible_printers_condition");
+    option.opt.full_width = true;
+    optgroup->append_single_option_line(option);
+
+    line = Line{ "", "" };
+    line.full_width = 1;
+    line.widget = [this](wxWindow* parent) {
+        return description_line_widget(parent, &m_parent_preset_description_line);
+    };
+    optgroup->append_line(line);
+}
+
+void TabSLAMaterial::update()
+{
+    if (get_preset_bundle()->printers.get_selected_preset().printer_technology() == ptFFF)
+        return; // ys_FIXME
+}
+
 } // GUI
 } // Slic3r
diff --git a/xs/src/slic3r/GUI/Tab.hpp b/xs/src/slic3r/GUI/Tab.hpp
index 230fe659e..a1214465e 100644
--- a/xs/src/slic3r/GUI/Tab.hpp
+++ b/xs/src/slic3r/GUI/Tab.hpp
@@ -218,8 +218,8 @@ public:
 	
 	void		create_preset_tab(PresetBundle *preset_bundle);
 	void		load_current_preset();
-	void		rebuild_page_tree();
-	void		select_preset(const std::string& preset_name = "");
+	void        rebuild_page_tree(bool tree_sel_change_event = false);
+	void		select_preset(std::string preset_name = "");
 	bool		may_discard_current_dirty_preset(PresetCollection* presets = nullptr, const std::string& new_printer_name = "");
 	wxSizer*	compatible_printers_widget(wxWindow* parent, wxCheckBox** checkbox, wxButton** btn);
 
@@ -267,11 +267,11 @@ public:
 
 	void			on_value_change(const std::string& opt_key, const boost::any& value);
 
+    void            update_wiping_button_visibility();
 protected:
 	void			on_presets_changed();
 	void			update_preset_description_line();
 	void			update_frequently_changed_parameters();
-    void            update_wiping_button_visibility();
 	void			update_tab_presets(wxComboCtrl* ui, bool show_incompatible);
 	void			fill_icon_descriptions();
 	void			set_tooltips_text();
@@ -319,6 +319,9 @@ class TabPrinter : public Tab
 	bool		m_use_silent_mode = false;
 	void		append_option_line(ConfigOptionsGroupShp optgroup, const std::string opt_key);
 	bool		m_rebuild_kinematics_page = false;
+
+    std::vector<PageShp>			m_pages_fff;
+    std::vector<PageShp>			m_pages_sla;
 public:
 	wxButton*	m_serial_test_btn;
 	wxButton*	m_print_host_test_btn;
@@ -329,12 +332,19 @@ public:
 	size_t		m_initial_extruders_count;
 	size_t		m_sys_extruders_count;
 
+    PrinterTechnology               m_printer_technology = ptFFF;
+
 	TabPrinter() {}
 	TabPrinter(wxNotebook* parent, bool no_controller) : Tab(parent, _(L("Printer Settings")), "printer", no_controller) {}
 	~TabPrinter(){}
 
 	void		build() override;
-	void		update() override;
+    void		build_fff();
+    void		build_sla();
+    void		update() override;
+    void		update_fff();
+    void		update_sla();
+    void        update_pages(); // update m_pages according to printer technology
 	void		update_serial_ports();
 	void		extruders_count_changed(size_t extruders_count);
 	PageShp		build_kinematics_page();
@@ -343,6 +353,19 @@ public:
 	void		init_options_list() override;
 };
 
+class TabSLAMaterial : public Tab
+{
+public:
+    TabSLAMaterial() {}
+    TabSLAMaterial(wxNotebook* parent, bool no_controller) :
+		Tab(parent, _(L("SLA Material Settings")), "sla_material", no_controller) {}
+    ~TabSLAMaterial(){}
+
+	void		build() override;
+	void		update() override;
+    void		init_options_list() override;
+};
+
 class SavePresetWindow :public wxDialog
 {
 public:
diff --git a/xs/src/slic3r/GUI/wxExtensions.cpp b/xs/src/slic3r/GUI/wxExtensions.cpp
index 5949efb37..7fcad9e65 100644
--- a/xs/src/slic3r/GUI/wxExtensions.cpp
+++ b/xs/src/slic3r/GUI/wxExtensions.cpp
@@ -1,5 +1,13 @@
 #include "wxExtensions.hpp"
 
+#include "GUI.hpp"
+#include "../../libslic3r/Utils.hpp"
+
+#include <wx/sizer.h>
+#include <wx/statline.h>
+#include <wx/dcclient.h>
+#include <wx/numformatter.h>
+
 const unsigned int wxCheckListBoxComboPopup::DefaultWidth = 200;
 const unsigned int wxCheckListBoxComboPopup::DefaultHeight = 200;
 const unsigned int wxCheckListBoxComboPopup::DefaultItemHeight = 18;
@@ -182,3 +190,1216 @@ void wxDataViewTreeCtrlComboPopup::OnDataViewTreeCtrlSelection(wxCommandEvent& e
 	auto selected = GetItemText(GetSelection());
 	cmb->SetText(selected);
 }
+
+// ----------------------------------------------------------------------------
+// ***  PrusaCollapsiblePane  ***    
+// ----------------------------------------------------------------------------
+void PrusaCollapsiblePane::OnStateChange(const wxSize& sz)
+{
+#ifdef __WXOSX__
+	wxCollapsiblePane::OnStateChange(sz);
+#else
+	SetSize(sz);
+
+	if (this->HasFlag(wxCP_NO_TLW_RESIZE))
+	{
+		// the user asked to explicitly handle the resizing itself...
+		return;
+	}
+
+	auto top = GetParent(); //right_panel
+	if (!top)
+		return;
+
+	wxSizer *sizer = top->GetSizer();
+	if (!sizer)
+		return;
+
+	const wxSize newBestSize = sizer->ComputeFittingClientSize(top);
+	top->SetMinClientSize(newBestSize);
+
+	wxWindowUpdateLocker noUpdates_p(top->GetParent());
+	// we shouldn't attempt to resize a maximized window, whatever happens
+	// 	if (!top->IsMaximized())
+	// 		top->SetClientSize(newBestSize);
+	top->GetParent()->Layout();
+	top->Refresh();
+#endif //__WXOSX__
+}
+
+// ----------------------------------------------------------------------------
+// ***  PrusaCollapsiblePaneMSW  ***    used only #ifdef __WXMSW__
+// ----------------------------------------------------------------------------
+#ifdef __WXMSW__
+bool PrusaCollapsiblePaneMSW::Create(wxWindow *parent, wxWindowID id, const wxString& label, 
+	const wxPoint& pos, const wxSize& size, long style, const wxValidator& val, const wxString& name)
+{
+	if (!wxControl::Create(parent, id, pos, size, style, val, name))
+		return false;
+	m_pStaticLine = NULL;
+	m_strLabel = label;
+
+	// sizer containing the expand button and possibly a static line
+	m_sz = new wxBoxSizer(wxHORIZONTAL);
+
+	m_bmp_close.LoadFile(Slic3r::GUI::from_u8(Slic3r::var("disclosure_triangle_close.png")), wxBITMAP_TYPE_PNG);
+	m_bmp_open.LoadFile(Slic3r::GUI::from_u8(Slic3r::var("disclosure_triangle_open.png")), wxBITMAP_TYPE_PNG);
+
+	m_pDisclosureTriangleButton = new wxButton(this, wxID_ANY, m_strLabel, wxPoint(0, 0),
+		wxDefaultSize, wxBU_EXACTFIT | wxNO_BORDER);
+	UpdateBtnBmp();
+	m_pDisclosureTriangleButton->Bind(wxEVT_BUTTON, [this](wxCommandEvent& event)
+	{
+		if (event.GetEventObject() != m_pDisclosureTriangleButton)
+		{
+			event.Skip();
+			return;
+		}
+
+		Collapse(!IsCollapsed());
+
+		// this change was generated by the user - send the event
+		wxCollapsiblePaneEvent ev(this, GetId(), IsCollapsed());
+		GetEventHandler()->ProcessEvent(ev);
+	});
+
+	m_sz->Add(m_pDisclosureTriangleButton, 0, wxLEFT | wxTOP | wxBOTTOM, GetBorder());
+
+	// do not set sz as our sizers since we handle the pane window without using sizers
+	m_pPane = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize,
+		wxTAB_TRAVERSAL | wxNO_BORDER, wxT("wxCollapsiblePanePane"));
+
+	wxColour& clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
+	m_pDisclosureTriangleButton->SetBackgroundColour(clr);
+	this->SetBackgroundColour(clr);
+	m_pPane->SetBackgroundColour(clr);
+
+	// start as collapsed:
+	m_pPane->Hide();
+
+	return true;
+}
+
+void PrusaCollapsiblePaneMSW::UpdateBtnBmp()
+{
+	if (IsCollapsed())
+		m_pDisclosureTriangleButton->SetBitmap(m_bmp_close);
+	else{
+		m_pDisclosureTriangleButton->SetBitmap(m_bmp_open);
+		// To updating button bitmap it's needed to lost focus on this button, so
+		// we set focus to mainframe 
+		//GetParent()->GetParent()->GetParent()->SetFocus();
+		//or to pane
+		GetPane()->SetFocus();
+	}
+	Layout();
+}
+
+void PrusaCollapsiblePaneMSW::SetLabel(const wxString &label)
+{
+	m_strLabel = label;
+	m_pDisclosureTriangleButton->SetLabel(m_strLabel);
+	Layout();
+}
+
+bool PrusaCollapsiblePaneMSW::Layout()
+{
+	if (!m_pDisclosureTriangleButton || !m_pPane || !m_sz)
+		return false;     // we need to complete the creation first!
+
+	wxSize oursz(GetSize());
+
+	// move & resize the button and the static line
+	m_sz->SetDimension(0, 0, oursz.GetWidth(), m_sz->GetMinSize().GetHeight());
+	m_sz->Layout();
+
+	if (IsExpanded())
+	{
+		// move & resize the container window
+		int yoffset = m_sz->GetSize().GetHeight() + GetBorder();
+		m_pPane->SetSize(0, yoffset,
+			oursz.x, oursz.y - yoffset);
+
+		// this is very important to make the pane window layout show correctly
+		m_pPane->Layout();
+	}
+
+	return true;
+}
+
+void PrusaCollapsiblePaneMSW::Collapse(bool collapse)
+{
+	// optimization
+	if (IsCollapsed() == collapse)
+		return;
+
+	InvalidateBestSize();
+
+	// update our state
+	m_pPane->Show(!collapse);
+
+	// update button bitmap
+	UpdateBtnBmp();
+
+	OnStateChange(GetBestSize());
+}
+#endif //__WXMSW__
+
+// *****************************************************************************
+// ----------------------------------------------------------------------------
+// PrusaObjectDataViewModelNode
+// ----------------------------------------------------------------------------
+
+void PrusaObjectDataViewModelNode::set_object_action_icon() {
+	m_action_icon = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("add_object.png")), wxBITMAP_TYPE_PNG);
+}
+void  PrusaObjectDataViewModelNode::set_part_action_icon() {
+	m_action_icon = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("cog.png")), wxBITMAP_TYPE_PNG);
+}
+
+// *****************************************************************************
+// ----------------------------------------------------------------------------
+// PrusaObjectDataViewModel
+// ----------------------------------------------------------------------------
+
+wxDataViewItem PrusaObjectDataViewModel::Add(wxString &name)
+{
+	auto root = new PrusaObjectDataViewModelNode(name);
+	m_objects.push_back(root);
+	// notify control
+	wxDataViewItem child((void*)root);
+	wxDataViewItem parent((void*)NULL);
+	ItemAdded(parent, child);
+	return child;
+}
+
+wxDataViewItem PrusaObjectDataViewModel::Add(wxString &name, int instances_count, int scale)
+{
+	auto root = new PrusaObjectDataViewModelNode(name, instances_count, scale);
+	m_objects.push_back(root);
+	// notify control
+	wxDataViewItem child((void*)root);
+	wxDataViewItem parent((void*)NULL);
+	ItemAdded(parent, child);
+	return child;
+}
+
+wxDataViewItem PrusaObjectDataViewModel::AddChild(	const wxDataViewItem &parent_item,
+													const wxString &name,
+													const wxIcon& icon,
+                                                    const int extruder/* = 0*/,
+                                                    const bool create_frst_child/* = true*/)
+{
+	PrusaObjectDataViewModelNode *root = (PrusaObjectDataViewModelNode*)parent_item.GetID();
+	if (!root) return wxDataViewItem(0);
+
+    wxString extruder_str = extruder == 0 ? "default" : wxString::Format("%d", extruder);
+
+    if (root->GetChildren().Count() == 0 && create_frst_child)
+	{
+		auto icon_solid_mesh = wxIcon(Slic3r::GUI::from_u8(Slic3r::var("object.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("package.png")), wxBITMAP_TYPE_PNG);
+		auto node = new PrusaObjectDataViewModelNode(root, root->m_name, icon_solid_mesh, extruder_str, 0);
+		root->Append(node);
+		// notify control
+		wxDataViewItem child((void*)node);
+		ItemAdded(parent_item, child);
+	}
+
+	auto volume_id = root->GetChildCount();
+	auto node = new PrusaObjectDataViewModelNode(root, name, icon, extruder_str, volume_id);
+	root->Append(node);
+	// notify control
+	wxDataViewItem child((void*)node);
+	ItemAdded(parent_item, child);
+	return child;
+}
+
+wxDataViewItem PrusaObjectDataViewModel::Delete(const wxDataViewItem &item)
+{
+	auto ret_item = wxDataViewItem(0);
+	PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID();
+	if (!node)      // happens if item.IsOk()==false
+		return ret_item;
+
+	auto node_parent = node->GetParent();
+	wxDataViewItem parent(node_parent);
+
+	// first remove the node from the parent's array of children;
+	// NOTE: MyObjectTreeModelNodePtrArray is only an array of _pointers_
+	//       thus removing the node from it doesn't result in freeing it
+	if (node_parent){
+		auto id = node_parent->GetChildren().Index(node);
+		auto v_id = node->GetVolumeId();
+		node_parent->GetChildren().Remove(node);
+		if (id > 0){ 
+			if(id == node_parent->GetChildCount()) id--;
+			ret_item = wxDataViewItem(node_parent->GetChildren().Item(id));
+		}
+
+		//update volume_id value for remaining child-nodes
+		auto children = node_parent->GetChildren();
+		for (size_t i = 0; i < node_parent->GetChildCount(); i++)
+		{
+			auto volume_id = children[i]->GetVolumeId();
+			if (volume_id > v_id)
+				children[i]->SetVolumeId(volume_id-1);
+		}
+	}
+	else
+	{
+		auto it = find(m_objects.begin(), m_objects.end(), node);
+		auto id = it - m_objects.begin();
+		if (it != m_objects.end())
+			m_objects.erase(it);
+		if (id > 0){ 
+			if(id == m_objects.size()) id--;
+			ret_item = wxDataViewItem(m_objects[id]);
+		}
+	}
+	// free the node
+	delete node;
+
+	// set m_containet to FALSE if parent has no child
+	if (node_parent && node_parent->GetChildCount() == 0){
+#ifndef __WXGTK__
+		node_parent->m_container = false;
+#endif //__WXGTK__
+		ret_item = parent;
+	}
+
+	// notify control
+	ItemDeleted(parent, item);
+	return ret_item;
+}
+
+void PrusaObjectDataViewModel::DeleteAll()
+{
+	while (!m_objects.empty())
+	{
+		auto object = m_objects.back();
+// 		object->RemoveAllChildren();
+		Delete(wxDataViewItem(object));	
+	}
+}
+
+void PrusaObjectDataViewModel::DeleteChildren(wxDataViewItem& parent)
+{
+    PrusaObjectDataViewModelNode *root = (PrusaObjectDataViewModelNode*)parent.GetID();
+    if (!root)      // happens if item.IsOk()==false
+        return;
+
+    // first remove the node from the parent's array of children;
+    // NOTE: MyObjectTreeModelNodePtrArray is only an array of _pointers_
+    //       thus removing the node from it doesn't result in freeing it
+    auto& children = root->GetChildren();
+    for (int id = root->GetChildCount() - 1; id >= 0; --id)
+    {
+        auto node = children[id];
+        auto item = wxDataViewItem(node);
+        children.RemoveAt(id);
+
+        // free the node
+        delete node;
+
+        // notify control
+        ItemDeleted(parent, item);
+    }
+
+    // set m_containet to FALSE if parent has no child
+#ifndef __WXGTK__
+        root->m_container = false;
+#endif //__WXGTK__
+}
+
+wxDataViewItem PrusaObjectDataViewModel::GetItemById(int obj_idx)
+{
+	if (obj_idx >= m_objects.size())
+	{
+		printf("Error! Out of objects range.\n");
+		return wxDataViewItem(0);
+	}
+	return wxDataViewItem(m_objects[obj_idx]);
+}
+
+
+int PrusaObjectDataViewModel::GetIdByItem(wxDataViewItem& item)
+{
+	wxASSERT(item.IsOk());
+
+	PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID();
+	auto it = find(m_objects.begin(), m_objects.end(), node);
+	if (it == m_objects.end())
+		return -1;
+
+	return it - m_objects.begin();
+}
+
+int PrusaObjectDataViewModel::GetVolumeIdByItem(wxDataViewItem& item)
+{
+	wxASSERT(item.IsOk());
+
+	PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID();
+	if (!node)      // happens if item.IsOk()==false
+		return -1;
+	return node->GetVolumeId();
+}
+
+wxString PrusaObjectDataViewModel::GetName(const wxDataViewItem &item) const
+{
+	PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID();
+	if (!node)      // happens if item.IsOk()==false
+		return wxEmptyString;
+
+	return node->m_name;
+}
+
+wxString PrusaObjectDataViewModel::GetCopy(const wxDataViewItem &item) const
+{
+	PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID();
+	if (!node)      // happens if item.IsOk()==false
+		return wxEmptyString;
+
+	return node->m_copy;
+}
+
+wxString PrusaObjectDataViewModel::GetScale(const wxDataViewItem &item) const
+{
+	PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID();
+	if (!node)      // happens if item.IsOk()==false
+		return wxEmptyString;
+
+	return node->m_scale;
+}
+
+wxIcon& PrusaObjectDataViewModel::GetIcon(const wxDataViewItem &item) const
+{
+    PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID();
+    return node->m_icon;
+}
+
+void PrusaObjectDataViewModel::GetValue(wxVariant &variant, const wxDataViewItem &item, unsigned int col) const
+{
+	wxASSERT(item.IsOk());
+
+	PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID();
+	switch (col)
+	{
+	case 0:{
+		const wxDataViewIconText data(node->m_name, node->m_icon);
+		variant << data;
+		break;}
+	case 1:
+		variant = node->m_copy;
+		break;
+	case 2:
+		variant = node->m_scale;
+		break;
+	case 3:
+		variant = node->m_extruder;
+		break;
+	case 4:
+		variant << node->m_action_icon;
+		break;
+	default:
+		;
+	}
+}
+
+bool PrusaObjectDataViewModel::SetValue(const wxVariant &variant, const wxDataViewItem &item, unsigned int col)
+{
+	wxASSERT(item.IsOk());
+
+	PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID();
+	return node->SetValue(variant, col);
+}
+
+bool PrusaObjectDataViewModel::SetValue(const wxVariant &variant, const int item_idx, unsigned int col)
+{
+	if (item_idx < 0 || item_idx >= m_objects.size())
+		return false;
+
+	return m_objects[item_idx]->SetValue(variant, col);
+}
+
+wxDataViewItem PrusaObjectDataViewModel::MoveChildUp(const wxDataViewItem &item)
+{
+	auto ret_item = wxDataViewItem(0);
+	wxASSERT(item.IsOk());
+	PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID();
+	if (!node)      // happens if item.IsOk()==false
+		return ret_item;
+
+	auto node_parent = node->GetParent();
+	if (!node_parent) // If isn't part, but object
+		return ret_item;
+
+	auto volume_id = node->GetVolumeId();
+	if (0 < volume_id && volume_id < node_parent->GetChildCount()){
+		node_parent->SwapChildrens(volume_id - 1, volume_id);
+		ret_item = wxDataViewItem(node_parent->GetNthChild(volume_id - 1));
+		ItemChanged(item);
+		ItemChanged(ret_item);
+	}
+	else
+		ret_item = wxDataViewItem(node_parent->GetNthChild(0));
+	return ret_item;
+}
+
+wxDataViewItem PrusaObjectDataViewModel::MoveChildDown(const wxDataViewItem &item)
+{
+	auto ret_item = wxDataViewItem(0);
+	wxASSERT(item.IsOk());
+	PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID();
+	if (!node)      // happens if item.IsOk()==false
+		return ret_item;
+
+	auto node_parent = node->GetParent();
+	if (!node_parent) // If isn't part, but object
+		return ret_item;
+
+	auto volume_id = node->GetVolumeId();
+	if (0 <= volume_id && volume_id+1 < node_parent->GetChildCount()){
+		node_parent->SwapChildrens(volume_id + 1, volume_id);
+		ret_item = wxDataViewItem(node_parent->GetNthChild(volume_id + 1));
+		ItemChanged(item);
+		ItemChanged(ret_item);
+	}
+	else
+		ret_item = wxDataViewItem(node_parent->GetNthChild(node_parent->GetChildCount()-1));
+	return ret_item;
+}
+
+wxDataViewItem PrusaObjectDataViewModel::ReorganizeChildren(int current_volume_id, int new_volume_id, const wxDataViewItem &parent)
+{
+    auto ret_item = wxDataViewItem(0);
+    if (current_volume_id == new_volume_id)
+        return ret_item;
+    wxASSERT(parent.IsOk());
+    PrusaObjectDataViewModelNode *node_parent = (PrusaObjectDataViewModelNode*)parent.GetID();
+    if (!node_parent)      // happens if item.IsOk()==false
+        return ret_item;
+
+    PrusaObjectDataViewModelNode *deleted_node = node_parent->GetNthChild(current_volume_id);
+    node_parent->GetChildren().Remove(deleted_node);
+    ItemDeleted(parent, wxDataViewItem(deleted_node));
+    node_parent->Insert(deleted_node, new_volume_id);
+    ItemAdded(parent, wxDataViewItem(deleted_node));
+
+    //update volume_id value for child-nodes
+    auto children = node_parent->GetChildren();
+    int id_frst = current_volume_id < new_volume_id ? current_volume_id : new_volume_id;
+    int id_last = current_volume_id > new_volume_id ? current_volume_id : new_volume_id;
+    for (int id = id_frst; id <= id_last; ++id)
+        children[id]->SetVolumeId(id);
+
+    return wxDataViewItem(node_parent->GetNthChild(new_volume_id));
+}
+
+// bool MyObjectTreeModel::IsEnabled(const wxDataViewItem &item, unsigned int col) const
+// {
+// 
+// }
+
+wxDataViewItem PrusaObjectDataViewModel::GetParent(const wxDataViewItem &item) const
+{
+	// the invisible root node has no parent
+	if (!item.IsOk())
+		return wxDataViewItem(0);
+
+	PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID();
+
+	// objects nodes has no parent too
+	if (find(m_objects.begin(), m_objects.end(),node) != m_objects.end())
+		return wxDataViewItem(0);
+
+	return wxDataViewItem((void*)node->GetParent());
+}
+
+bool PrusaObjectDataViewModel::IsContainer(const wxDataViewItem &item) const
+{
+	// the invisible root node can have children
+	if (!item.IsOk())
+		return true;
+
+	PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID();
+	return node->IsContainer();
+}
+
+unsigned int PrusaObjectDataViewModel::GetChildren(const wxDataViewItem &parent, wxDataViewItemArray &array) const
+{
+	PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)parent.GetID();
+	if (!node)
+	{
+		for (auto object : m_objects)
+			array.Add(wxDataViewItem((void*)object));
+		return m_objects.size();
+	}
+
+	if (node->GetChildCount() == 0)
+	{
+		return 0;
+	}
+
+	unsigned int count = node->GetChildren().GetCount();
+	for (unsigned int pos = 0; pos < count; pos++)
+	{
+		PrusaObjectDataViewModelNode *child = node->GetChildren().Item(pos);
+		array.Add(wxDataViewItem((void*)child));
+	}
+
+	return count;
+}
+
+// ************************************** EXPERIMENTS ***************************************
+PrusaDoubleSlider::PrusaDoubleSlider(   wxWindow *parent,
+                                        wxWindowID id,
+                                        int lowerValue, 
+                                        int higherValue, 
+                                        int minValue, 
+                                        int maxValue,
+                                        const wxPoint& pos,
+                                        const wxSize& size,
+                                        long style,
+                                        const wxValidator& val,
+                                        const wxString& name) : 
+    wxControl(parent, id, pos, size, wxWANTS_CHARS | wxBORDER_NONE),
+    m_lower_value(lowerValue), m_higher_value (higherValue), 
+    m_min_value(minValue), m_max_value(maxValue),
+    m_style(style == wxSL_HORIZONTAL || style == wxSL_VERTICAL ? style: wxSL_HORIZONTAL)
+{
+#ifndef __WXOSX__ // SetDoubleBuffered exists on Win and Linux/GTK, but is missing on OSX
+    SetDoubleBuffered(true);
+#endif //__WXOSX__
+
+    m_bmp_thumb_higher = wxBitmap(style == wxSL_HORIZONTAL ? Slic3r::GUI::from_u8(Slic3r::var("right_half_circle.png")) :
+                                                             Slic3r::GUI::from_u8(Slic3r::var("up_half_circle.png")), wxBITMAP_TYPE_PNG);
+    m_bmp_thumb_lower  = wxBitmap(style == wxSL_HORIZONTAL ? Slic3r::GUI::from_u8(Slic3r::var("left_half_circle.png")) :
+                                                             Slic3r::GUI::from_u8(Slic3r::var("down_half_circle.png")), wxBITMAP_TYPE_PNG);
+    m_thumb_size = m_bmp_thumb_lower.GetSize();
+
+    m_bmp_add_tick_on  = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("colorchange_add_on.png")), wxBITMAP_TYPE_PNG);
+    m_bmp_add_tick_off = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("colorchange_add_off.png")), wxBITMAP_TYPE_PNG);
+    m_bmp_del_tick_on  = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("colorchange_delete_on.png")), wxBITMAP_TYPE_PNG);
+    m_bmp_del_tick_off = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("colorchange_delete_off.png")), wxBITMAP_TYPE_PNG);
+    m_tick_icon_dim = m_bmp_add_tick_on.GetSize().x;
+
+    m_bmp_one_layer_lock_on    = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("one_layer_lock_on.png")), wxBITMAP_TYPE_PNG);
+    m_bmp_one_layer_lock_off   = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("one_layer_lock_off.png")), wxBITMAP_TYPE_PNG);
+    m_bmp_one_layer_unlock_on  = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("one_layer_unlock_on.png")), wxBITMAP_TYPE_PNG);
+    m_bmp_one_layer_unlock_off = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("one_layer_unlock_off.png")), wxBITMAP_TYPE_PNG);
+    m_lock_icon_dim = m_bmp_one_layer_lock_on.GetSize().x;
+
+    m_selection = ssUndef;
+
+    // slider events
+    Bind(wxEVT_PAINT,       &PrusaDoubleSlider::OnPaint,    this);
+    Bind(wxEVT_LEFT_DOWN,   &PrusaDoubleSlider::OnLeftDown, this);
+    Bind(wxEVT_MOTION,      &PrusaDoubleSlider::OnMotion,   this);
+    Bind(wxEVT_LEFT_UP,     &PrusaDoubleSlider::OnLeftUp,   this);
+    Bind(wxEVT_MOUSEWHEEL,  &PrusaDoubleSlider::OnWheel,    this);
+    Bind(wxEVT_ENTER_WINDOW,&PrusaDoubleSlider::OnEnterWin, this);
+    Bind(wxEVT_LEAVE_WINDOW,&PrusaDoubleSlider::OnLeaveWin, this);
+    Bind(wxEVT_KEY_DOWN,    &PrusaDoubleSlider::OnKeyDown,  this);
+    Bind(wxEVT_KEY_UP,      &PrusaDoubleSlider::OnKeyUp,    this);
+    Bind(wxEVT_RIGHT_DOWN,  &PrusaDoubleSlider::OnRightDown,this);
+    Bind(wxEVT_RIGHT_UP,    &PrusaDoubleSlider::OnRightUp,  this);
+
+    // control's view variables
+    SLIDER_MARGIN     = 4 + (style == wxSL_HORIZONTAL ? m_bmp_thumb_higher.GetWidth() : m_bmp_thumb_higher.GetHeight());
+
+    DARK_ORANGE_PEN   = wxPen(wxColour(253, 84, 2));
+    ORANGE_PEN        = wxPen(wxColour(253, 126, 66));
+    LIGHT_ORANGE_PEN  = wxPen(wxColour(254, 177, 139));
+
+    DARK_GREY_PEN     = wxPen(wxColour(128, 128, 128));
+    GREY_PEN          = wxPen(wxColour(164, 164, 164));
+    LIGHT_GREY_PEN    = wxPen(wxColour(204, 204, 204));
+
+    line_pens = { &DARK_GREY_PEN, &GREY_PEN, &LIGHT_GREY_PEN };
+    segm_pens = { &DARK_ORANGE_PEN, &ORANGE_PEN, &LIGHT_ORANGE_PEN };
+}
+
+int PrusaDoubleSlider::GetActiveValue() const
+{
+    return m_selection == ssLower ?
+    m_lower_value : m_selection == ssHigher ?
+                m_higher_value : -1;
+}
+
+wxSize PrusaDoubleSlider::DoGetBestSize() const
+{
+    const wxSize size = wxControl::DoGetBestSize();
+    if (size.x > 1 && size.y > 1)
+        return size;
+    const int new_size = is_horizontal() ? 80 : 120;
+    return wxSize(new_size, new_size);
+}
+
+void PrusaDoubleSlider::SetLowerValue(const int lower_val)
+{
+    m_lower_value = lower_val;
+    Refresh();
+    Update();
+}
+
+void PrusaDoubleSlider::SetHigherValue(const int higher_val)
+{
+    m_higher_value = higher_val;
+    Refresh();
+    Update();
+}
+
+void PrusaDoubleSlider::SetMaxValue(const int max_value)
+{
+    m_max_value = max_value;
+    Refresh();
+    Update();
+}
+
+void PrusaDoubleSlider::draw_scroll_line(wxDC& dc, const int lower_pos, const int higher_pos)
+{
+    int width;
+    int height;
+    get_size(&width, &height);
+
+    wxCoord line_beg_x = is_horizontal() ? SLIDER_MARGIN : width*0.5 - 1;
+    wxCoord line_beg_y = is_horizontal() ? height*0.5 - 1 : SLIDER_MARGIN;
+    wxCoord line_end_x = is_horizontal() ? width - SLIDER_MARGIN + 1 : width*0.5 - 1;
+    wxCoord line_end_y = is_horizontal() ? height*0.5 - 1 : height - SLIDER_MARGIN + 1;
+
+    wxCoord segm_beg_x = is_horizontal() ? lower_pos : width*0.5 - 1;
+    wxCoord segm_beg_y = is_horizontal() ? height*0.5 - 1 : lower_pos-1;
+    wxCoord segm_end_x = is_horizontal() ? higher_pos : width*0.5 - 1;
+    wxCoord segm_end_y = is_horizontal() ? height*0.5 - 1 : higher_pos-1;
+
+    for (int id = 0; id < line_pens.size(); id++)
+    {
+        dc.SetPen(*line_pens[id]);
+        dc.DrawLine(line_beg_x, line_beg_y, line_end_x, line_end_y);
+        dc.SetPen(*segm_pens[id]);
+        dc.DrawLine(segm_beg_x, segm_beg_y, segm_end_x, segm_end_y);
+        if (is_horizontal())
+            line_beg_y = line_end_y = segm_beg_y = segm_end_y += 1;
+        else
+            line_beg_x = line_end_x = segm_beg_x = segm_end_x += 1;
+    }
+}
+
+double PrusaDoubleSlider::get_scroll_step()
+{
+    const wxSize sz = get_size();
+    const int& slider_len = m_style == wxSL_HORIZONTAL ? sz.x : sz.y;
+    return double(slider_len - SLIDER_MARGIN * 2) / (m_max_value - m_min_value);
+}
+
+// get position on the slider line from entered value
+wxCoord PrusaDoubleSlider::get_position_from_value(const int value)
+{
+    const double step = get_scroll_step();
+    const int val = is_horizontal() ? value : m_max_value - value;
+    return wxCoord(SLIDER_MARGIN + int(val*step + 0.5));
+}
+
+wxSize PrusaDoubleSlider::get_size()
+{
+    int w, h;
+    get_size(&w, &h);
+    return wxSize(w, h);
+}
+
+void PrusaDoubleSlider::get_size(int *w, int *h)
+{
+    GetSize(w, h);
+    is_horizontal() ? *w -= m_lock_icon_dim : *h -= m_lock_icon_dim;
+}
+
+void PrusaDoubleSlider::get_lower_and_higher_position(int& lower_pos, int& higher_pos)
+{
+    const double step = get_scroll_step();
+    if (is_horizontal()) {
+        lower_pos = SLIDER_MARGIN + int(m_lower_value*step + 0.5);
+        higher_pos = SLIDER_MARGIN + int(m_higher_value*step + 0.5);
+    }
+    else {
+        lower_pos = SLIDER_MARGIN + int((m_max_value - m_lower_value)*step + 0.5);
+        higher_pos = SLIDER_MARGIN + int((m_max_value - m_higher_value)*step + 0.5);
+    }
+}
+
+void PrusaDoubleSlider::draw_focus_rect()
+{
+    if (!m_is_focused) 
+        return;
+    const wxSize sz = GetSize();
+    wxPaintDC dc(this);
+    const wxPen pen = wxPen(wxColour(128, 128, 10), 1, wxPENSTYLE_DOT);
+    dc.SetPen(pen);
+    dc.SetBrush(wxBrush(wxColour(0, 0, 0), wxBRUSHSTYLE_TRANSPARENT));
+    dc.DrawRectangle(1, 1, sz.x - 2, sz.y - 2);
+}
+
+void PrusaDoubleSlider::render()
+{
+    SetBackgroundColour(GetParent()->GetBackgroundColour());
+    draw_focus_rect();
+
+    wxPaintDC dc(this);
+    wxFont font = dc.GetFont();
+    const wxFont smaller_font = font.Smaller();
+    dc.SetFont(smaller_font);
+
+    const wxCoord lower_pos = get_position_from_value(m_lower_value);
+    const wxCoord higher_pos = get_position_from_value(m_higher_value);
+
+    // draw line
+    draw_scroll_line(dc, lower_pos, higher_pos);
+
+//     //lower slider:
+//     draw_thumb(dc, lower_pos, ssLower);
+//     //higher slider:
+//     draw_thumb(dc, higher_pos, ssHigher);
+
+    // draw both sliders
+    draw_thumbs(dc, lower_pos, higher_pos);
+
+    //draw color print ticks
+    draw_ticks(dc);
+
+    //draw color print ticks
+    draw_one_layer_icon(dc);
+}
+
+void PrusaDoubleSlider::draw_action_icon(wxDC& dc, const wxPoint pt_beg, const wxPoint pt_end)
+{
+    const int tick = m_selection == ssLower ? m_lower_value : m_higher_value;
+    wxBitmap* icon = m_is_action_icon_focesed ? &m_bmp_add_tick_off : &m_bmp_add_tick_on;
+    if (m_ticks.find(tick) != m_ticks.end())
+        icon = m_is_action_icon_focesed ? &m_bmp_del_tick_off : &m_bmp_del_tick_on;
+
+    wxCoord x_draw, y_draw;
+    is_horizontal() ? x_draw = pt_beg.x - 0.5*m_tick_icon_dim : y_draw = pt_beg.y - 0.5*m_tick_icon_dim;
+    if (m_selection == ssLower)
+        is_horizontal() ? y_draw = pt_end.y + 3 : x_draw = pt_beg.x - m_tick_icon_dim-2;
+    else
+        is_horizontal() ? y_draw = pt_beg.y - m_tick_icon_dim-2 : x_draw = pt_end.x + 3;
+
+    dc.DrawBitmap(*icon, x_draw, y_draw);
+
+    //update rect of the tick action icon
+    m_rect_tick_action = wxRect(x_draw, y_draw, m_tick_icon_dim, m_tick_icon_dim);
+}
+
+void PrusaDoubleSlider::draw_info_line_with_icon(wxDC& dc, const wxPoint& pos, const SelectedSlider selection)
+{
+    if (m_selection == selection) {
+        //draw info line
+        dc.SetPen(DARK_ORANGE_PEN);
+        const wxPoint pt_beg = is_horizontal() ? wxPoint(pos.x, pos.y - m_thumb_size.y) : wxPoint(pos.x - m_thumb_size.x, pos.y - 1);
+        const wxPoint pt_end = is_horizontal() ? wxPoint(pos.x, pos.y + m_thumb_size.y) : wxPoint(pos.x + m_thumb_size.x, pos.y - 1);
+        dc.DrawLine(pt_beg, pt_end);
+
+        //draw action icon
+        draw_action_icon(dc, pt_beg, pt_end);
+    }
+}
+
+wxString PrusaDoubleSlider::get_label(const SelectedSlider& selection) const
+{
+    const int value = selection == ssLower ? m_lower_value : m_higher_value;
+
+    if (m_label_koef == 1.0 && m_values.empty())
+        return wxString::Format("%d", value);
+
+    const wxString str = m_values.empty() ? 
+                         wxNumberFormatter::ToString(m_label_koef*value, 2, wxNumberFormatter::Style_None) :
+                         wxNumberFormatter::ToString(m_values[value], 2, wxNumberFormatter::Style_None);
+    return wxString::Format("%s\n(%d)", str, value);
+}
+
+void PrusaDoubleSlider::draw_thumb_text(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection) const
+{
+    if (m_is_one_layer && selection != m_selection || !selection) 
+        return;
+    wxCoord text_width, text_height;
+    const wxString label = get_label(selection);
+    dc.GetMultiLineTextExtent(label, &text_width, &text_height);
+    wxPoint text_pos;
+    if (selection ==ssLower)
+        text_pos = is_horizontal() ? wxPoint(pos.x + 1, pos.y + m_thumb_size.x) :
+                           wxPoint(pos.x + m_thumb_size.x+1, pos.y - 0.5*text_height - 1);
+    else
+        text_pos = is_horizontal() ? wxPoint(pos.x - text_width - 1, pos.y - m_thumb_size.x - text_height) :
+                    wxPoint(pos.x - text_width - 1 - m_thumb_size.x, pos.y - 0.5*text_height + 1);
+    dc.DrawText(label, text_pos);
+}
+
+void PrusaDoubleSlider::draw_thumb_item(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection)
+{
+    wxCoord x_draw, y_draw;
+    if (selection == ssLower) {
+        if (is_horizontal()) {
+            x_draw = pos.x - m_thumb_size.x;
+            y_draw = pos.y - int(0.5*m_thumb_size.y);
+        }
+        else {
+            x_draw = pos.x - int(0.5*m_thumb_size.x);
+            y_draw = pos.y;
+        }
+    }
+    else{
+        if (is_horizontal()) {
+            x_draw = pos.x;
+            y_draw = pos.y - int(0.5*m_thumb_size.y);
+        }
+        else {
+            x_draw = pos.x - int(0.5*m_thumb_size.x);
+            y_draw = pos.y - m_thumb_size.y;
+        }
+    }
+    dc.DrawBitmap(selection == ssLower ? m_bmp_thumb_lower : m_bmp_thumb_higher, x_draw, y_draw);
+
+    // Update thumb rect
+    update_thumb_rect(x_draw, y_draw, selection);
+}
+
+void PrusaDoubleSlider::draw_thumb(wxDC& dc, const wxCoord& pos_coord, const SelectedSlider& selection)
+{
+    //calculate thumb position on slider line
+    int width, height;
+    get_size(&width, &height);
+    const wxPoint pos = is_horizontal() ? wxPoint(pos_coord, height*0.5) : wxPoint(0.5*width, pos_coord);
+
+    // Draw thumb
+    draw_thumb_item(dc, pos, selection);
+
+    // Draw info_line
+    draw_info_line_with_icon(dc, pos, selection);
+
+    // Draw thumb text
+    draw_thumb_text(dc, pos, selection);
+}
+
+void PrusaDoubleSlider::draw_thumbs(wxDC& dc, const wxCoord& lower_pos, const wxCoord& higher_pos)
+{
+    //calculate thumb position on slider line
+    int width, height;
+    get_size(&width, &height);
+    const wxPoint pos_l = is_horizontal() ? wxPoint(lower_pos, height*0.5) : wxPoint(0.5*width, lower_pos);
+    const wxPoint pos_h = is_horizontal() ? wxPoint(higher_pos, height*0.5) : wxPoint(0.5*width, higher_pos);
+
+    // Draw lower thumb
+    draw_thumb_item(dc, pos_l, ssLower);
+    // Draw lower info_line
+    draw_info_line_with_icon(dc, pos_l, ssLower);
+
+    // Draw higher thumb
+    draw_thumb_item(dc, pos_h, ssHigher);
+    // Draw higher info_line
+    draw_info_line_with_icon(dc, pos_h, ssHigher);
+    // Draw higher thumb text
+    draw_thumb_text(dc, pos_h, ssHigher);
+
+    // Draw lower thumb text
+    draw_thumb_text(dc, pos_l, ssLower);
+}
+
+void PrusaDoubleSlider::draw_ticks(wxDC& dc)
+{
+    dc.SetPen(DARK_GREY_PEN);
+    int height, width;
+    get_size(&width, &height);
+    const wxCoord mid = is_horizontal() ? 0.5*height : 0.5*width;
+    for (auto tick : m_ticks)
+    {
+        const wxCoord pos = get_position_from_value(tick);
+
+        is_horizontal() ?   dc.DrawLine(pos, mid-14, pos, mid-9) :
+                            dc.DrawLine(mid - 14, pos - 1, mid - 9, pos - 1);
+        is_horizontal() ?   dc.DrawLine(pos, mid+14, pos, mid+9) :
+                            dc.DrawLine(mid + 14, pos - 1, mid + 9, pos - 1);
+    }
+}
+
+void PrusaDoubleSlider::draw_one_layer_icon(wxDC& dc)
+{
+    wxBitmap* icon = m_is_one_layer ?
+                     m_is_one_layer_icon_focesed ? &m_bmp_one_layer_lock_off : &m_bmp_one_layer_lock_on :
+                     m_is_one_layer_icon_focesed ? &m_bmp_one_layer_unlock_off : &m_bmp_one_layer_unlock_on;
+
+    int width, height;
+    get_size(&width, &height);
+
+    wxCoord x_draw, y_draw;
+    is_horizontal() ? x_draw = width-2 : x_draw = 0.5*width - 0.5*m_lock_icon_dim;
+    is_horizontal() ? y_draw = 0.5*height - 0.5*m_lock_icon_dim : y_draw = height-2;
+
+    dc.DrawBitmap(*icon, x_draw, y_draw);
+
+    //update rect of the lock/unlock icon
+    m_rect_one_layer_icon = wxRect(x_draw, y_draw, m_lock_icon_dim, m_lock_icon_dim);
+}
+
+void PrusaDoubleSlider::update_thumb_rect(const wxCoord& begin_x, const wxCoord& begin_y, const SelectedSlider& selection)
+{
+    const wxRect& rect = wxRect(begin_x, begin_y, m_thumb_size.x, m_thumb_size.y);
+    if (selection == ssLower)
+        m_rect_lower_thumb = rect;
+    else
+        m_rect_higher_thumb = rect;
+}
+
+int PrusaDoubleSlider::get_value_from_position(const wxCoord x, const wxCoord y)
+{
+    const int height = get_size().y;
+    const double step = get_scroll_step();
+    
+    if (is_horizontal()) 
+        return int(double(x - SLIDER_MARGIN) / step + 0.5);
+    else 
+        return int(m_min_value + double(height - SLIDER_MARGIN - y) / step + 0.5);
+}
+
+void PrusaDoubleSlider::detect_selected_slider(const wxPoint& pt, const bool is_mouse_wheel /*= false*/)
+{
+    if (is_mouse_wheel)
+    {
+        if (is_horizontal()) {
+            m_selection = pt.x <= m_rect_lower_thumb.GetRight() ? ssLower :
+                          pt.x >= m_rect_higher_thumb.GetLeft() ? ssHigher : ssUndef;
+        }
+        else {
+            m_selection = pt.y >= m_rect_lower_thumb.GetTop() ? ssLower :
+                          pt.y <= m_rect_higher_thumb.GetBottom() ? ssHigher : ssUndef;            
+        }
+        return;
+    }
+
+    m_selection = is_point_in_rect(pt, m_rect_lower_thumb) ? ssLower :
+                  is_point_in_rect(pt, m_rect_higher_thumb) ? ssHigher : ssUndef;
+}
+
+bool PrusaDoubleSlider::is_point_in_rect(const wxPoint& pt, const wxRect& rect)
+{
+    if (rect.GetLeft() <= pt.x && pt.x <= rect.GetRight() && 
+        rect.GetTop()  <= pt.y && pt.y <= rect.GetBottom())
+        return true;
+    return false;
+}
+
+void PrusaDoubleSlider::OnLeftDown(wxMouseEvent& event)
+{
+    this->CaptureMouse();
+    wxClientDC dc(this);
+    wxPoint pos = event.GetLogicalPosition(dc);
+    if (is_point_in_rect(pos, m_rect_tick_action)) {
+        action_tick(taOnIcon);
+        return;
+    }
+
+    m_is_left_down = true;
+    if (is_point_in_rect(pos, m_rect_one_layer_icon)){
+        m_is_one_layer = !m_is_one_layer;
+        m_selection == ssLower ? correct_lower_value() : correct_higher_value();
+        if (!m_selection) m_selection = ssHigher;
+    }
+    else
+        detect_selected_slider(pos);
+
+    Refresh();
+    Update();
+    event.Skip();
+}
+
+void PrusaDoubleSlider::correct_lower_value()
+{
+    if (m_lower_value < m_min_value)
+        m_lower_value = m_min_value;
+    else if (m_lower_value > m_max_value)
+        m_lower_value = m_max_value;
+    
+    if (m_lower_value >= m_higher_value && m_lower_value <= m_max_value || m_is_one_layer)
+        m_higher_value = m_lower_value;
+}
+
+void PrusaDoubleSlider::correct_higher_value()
+{
+    if (m_higher_value > m_max_value)
+        m_higher_value = m_max_value;
+    else if (m_higher_value < m_min_value)
+        m_higher_value = m_min_value;
+    
+    if (m_higher_value <= m_lower_value && m_higher_value >= m_min_value || m_is_one_layer)
+        m_lower_value = m_higher_value;
+}
+
+void PrusaDoubleSlider::OnMotion(wxMouseEvent& event)
+{
+    const wxClientDC dc(this);
+    const wxPoint pos = event.GetLogicalPosition(dc);
+    m_is_one_layer_icon_focesed = is_point_in_rect(pos, m_rect_one_layer_icon);
+    if (!m_is_left_down && !m_is_one_layer){
+        m_is_action_icon_focesed = is_point_in_rect(pos, m_rect_tick_action);
+    }
+    else if (m_is_left_down || m_is_right_down){
+        if (m_selection == ssLower) {
+            m_lower_value = get_value_from_position(pos.x, pos.y);
+            correct_lower_value();
+        }
+        else if (m_selection == ssHigher) {
+            m_higher_value = get_value_from_position(pos.x, pos.y);
+            correct_higher_value();
+        }
+    }
+    Refresh();
+    Update();
+    event.Skip();
+}
+
+void PrusaDoubleSlider::OnLeftUp(wxMouseEvent& event)
+{
+    this->ReleaseMouse();
+    m_is_left_down = false;
+    Refresh();
+    Update();
+    event.Skip();
+
+    wxCommandEvent e(wxEVT_SCROLL_CHANGED);
+    e.SetEventObject(this);
+    ProcessWindowEvent(e);
+}
+
+void PrusaDoubleSlider::enter_window(wxMouseEvent& event, const bool enter)
+{
+    m_is_focused = enter;
+    Refresh();
+    Update();
+    event.Skip();
+}
+
+// "condition" have to be true for:
+//    -  value increase (if wxSL_VERTICAL)
+//    -  value decrease (if wxSL_HORIZONTAL) 
+void PrusaDoubleSlider::move_current_thumb(const bool condition)
+{
+    m_is_one_layer = wxGetKeyState(WXK_CONTROL);
+    int delta = condition ? -1 : 1;
+    if (is_horizontal())
+        delta *= -1;
+
+    if (m_selection == ssLower) {
+        m_lower_value -= delta;
+        correct_lower_value();
+    }
+    else if (m_selection == ssHigher) {
+        m_higher_value -= delta;
+        correct_higher_value();
+    }
+    Refresh();
+    Update();
+
+    wxCommandEvent e(wxEVT_SCROLL_CHANGED);
+    e.SetEventObject(this);
+    ProcessWindowEvent(e);
+}
+
+void PrusaDoubleSlider::action_tick(const TicksAction action)
+{
+    if (m_selection == ssUndef)
+        return;
+
+    const int tick = m_selection == ssLower ? m_lower_value : m_higher_value;
+
+    if (action == taOnIcon && !m_ticks.insert(tick).second)
+        m_ticks.erase(tick);
+    else {
+        const auto it = m_ticks.find(tick);
+        if (it == m_ticks.end() && action == taAdd)
+            m_ticks.insert(tick);
+        else if (it != m_ticks.end() && action == taDel)
+            m_ticks.erase(tick);
+        else
+            return;
+    }
+
+    Refresh();
+    Update();
+}
+
+void PrusaDoubleSlider::OnWheel(wxMouseEvent& event)
+{
+    wxClientDC dc(this);
+    wxPoint pos = event.GetLogicalPosition(dc);
+    detect_selected_slider(pos, true);
+
+    if (m_selection == ssUndef)
+        return;
+
+    move_current_thumb(event.GetWheelRotation() > 0);
+}
+
+void PrusaDoubleSlider::OnKeyDown(wxKeyEvent &event)
+{
+    const int key = event.GetKeyCode();
+    if (key == '+' || key == WXK_NUMPAD_ADD)
+        action_tick(taAdd);
+    else if (key == '-' || key == 390 || key == WXK_DELETE || key == WXK_BACK)
+        action_tick(taDel);
+    else if (is_horizontal())
+    {
+        if (key == WXK_LEFT || key == WXK_RIGHT)
+            move_current_thumb(key == WXK_LEFT); 
+        else if (key == WXK_UP || key == WXK_DOWN){
+            m_selection = key == WXK_UP ? ssHigher : ssLower;
+            Refresh();
+        }
+    }
+    else {
+        if (key == WXK_LEFT || key == WXK_RIGHT) {
+            m_selection = key == WXK_LEFT ? ssHigher : ssLower;
+            Refresh();
+        }
+        else if (key == WXK_UP || key == WXK_DOWN)
+            move_current_thumb(key == WXK_UP);
+    }
+}
+
+void PrusaDoubleSlider::OnKeyUp(wxKeyEvent &event)
+{
+    if (event.GetKeyCode() == WXK_CONTROL)
+        m_is_one_layer = false;
+    Refresh();
+    Update();
+    event.Skip();
+}
+
+void PrusaDoubleSlider::OnRightDown(wxMouseEvent& event)
+{
+    this->CaptureMouse();
+    const wxClientDC dc(this);
+    detect_selected_slider(event.GetLogicalPosition(dc));
+    if (!m_selection)
+        return;
+
+    if (m_selection == ssLower)
+        m_higher_value = m_lower_value;
+    else
+        m_lower_value = m_higher_value;
+
+    m_is_right_down = m_is_one_layer = true;
+
+    Refresh();
+    Update();
+    event.Skip();
+}
+
+void PrusaDoubleSlider::OnRightUp(wxMouseEvent& event)
+{
+    this->ReleaseMouse();
+    m_is_right_down = m_is_one_layer = false;
+
+    Refresh();
+    Update();
+    event.Skip();
+}
+
+// *****************************************************************************
diff --git a/xs/src/slic3r/GUI/wxExtensions.hpp b/xs/src/slic3r/GUI/wxExtensions.hpp
index 3667c7905..064ce1038 100644
--- a/xs/src/slic3r/GUI/wxExtensions.hpp
+++ b/xs/src/slic3r/GUI/wxExtensions.hpp
@@ -4,6 +4,14 @@
 #include <wx/checklst.h>
 #include <wx/combo.h>
 #include <wx/dataview.h>
+#include <wx/dc.h>
+#include <wx/collpane.h>
+#include <wx/wupdlock.h>
+#include <wx/button.h>
+#include <wx/slider.h>
+
+#include <vector>
+#include <set>
 
 class wxCheckListBoxComboPopup : public wxCheckListBox, public wxComboPopup
 {
@@ -65,4 +73,568 @@ public:
 	void				SetItemsCnt(int cnt) { m_cnt_open_items = cnt; }
 };
 
+
+
+// ***  PrusaCollapsiblePane  *** 
+// ----------------------------------------------------------------------------
+class PrusaCollapsiblePane : public wxCollapsiblePane
+{
+public:
+	PrusaCollapsiblePane() {}
+	PrusaCollapsiblePane(wxWindow *parent,
+		wxWindowID winid,
+		const wxString& label,
+		const wxPoint& pos = wxDefaultPosition,
+		const wxSize& size = wxDefaultSize,
+		long style = wxCP_DEFAULT_STYLE,
+		const wxValidator& val = wxDefaultValidator,
+		const wxString& name = wxCollapsiblePaneNameStr)
+	{
+		Create(parent, winid, label, pos, size, style, val, name);
+	}
+	~PrusaCollapsiblePane() {}
+
+	void OnStateChange(const wxSize& sz); //override/hide of OnStateChange from wxCollapsiblePane
+	virtual bool Show(bool show = true) override {
+		wxCollapsiblePane::Show(show);
+		OnStateChange(GetBestSize());
+		return true;
+	}
+};
+
+
+// ***  PrusaCollapsiblePaneMSW  ***  used only #ifdef __WXMSW__
+// ----------------------------------------------------------------------------
+#ifdef __WXMSW__
+class PrusaCollapsiblePaneMSW : public PrusaCollapsiblePane//wxCollapsiblePane
+{
+	wxButton*	m_pDisclosureTriangleButton = nullptr;
+	wxBitmap	m_bmp_close;
+	wxBitmap	m_bmp_open;
+public:
+	PrusaCollapsiblePaneMSW() {}
+	PrusaCollapsiblePaneMSW(	wxWindow *parent,
+							wxWindowID winid,
+							const wxString& label,
+							const wxPoint& pos = wxDefaultPosition,
+							const wxSize& size = wxDefaultSize,
+							long style = wxCP_DEFAULT_STYLE,
+							const wxValidator& val = wxDefaultValidator,
+							const wxString& name = wxCollapsiblePaneNameStr)
+	{
+		Create(parent, winid, label, pos, size, style, val, name);
+	}
+
+	~PrusaCollapsiblePaneMSW() {}
+
+	bool Create(wxWindow *parent,
+				wxWindowID id,
+				const wxString& label,
+				const wxPoint& pos,
+				const wxSize& size,
+				long style,
+				const wxValidator& val,
+				const wxString& name);
+
+	void UpdateBtnBmp();
+	void SetLabel(const wxString &label) override;
+	bool Layout() override;
+	void Collapse(bool collapse) override;
+};
+#endif //__WXMSW__
+
+// *****************************************************************************
+// ----------------------------------------------------------------------------
+// PrusaObjectDataViewModelNode: a node inside PrusaObjectDataViewModel
+// ----------------------------------------------------------------------------
+
+class PrusaObjectDataViewModelNode;
+WX_DEFINE_ARRAY_PTR(PrusaObjectDataViewModelNode*, MyObjectTreeModelNodePtrArray);
+
+class PrusaObjectDataViewModelNode
+{
+	PrusaObjectDataViewModelNode*	m_parent;
+	MyObjectTreeModelNodePtrArray   m_children;
+    wxIcon                          m_empty_icon; 
+public:
+	PrusaObjectDataViewModelNode(const wxString &name, int instances_count=1, int scale=100) {
+		m_parent	= NULL;
+		m_name		= name;
+		m_copy		= wxString::Format("%d", instances_count);
+		m_scale		= wxString::Format("%d%%", scale);
+		m_type		= "object";
+		m_volume_id	= -1;
+#ifdef __WXGTK__
+        // it's necessary on GTK because of control have to know if this item will be container
+        // in another case you couldn't to add subitem for this item
+        // it will be produce "segmentation fault"
+        m_container = true;
+#endif  //__WXGTK__
+		set_object_action_icon();
+	}
+
+	PrusaObjectDataViewModelNode(	PrusaObjectDataViewModelNode* parent,
+									const wxString& sub_obj_name, 
+									const wxIcon& icon, 
+                                    const wxString& extruder, 
+									const int volume_id=-1) {
+		m_parent	= parent;
+		m_name		= sub_obj_name;
+		m_copy		= wxEmptyString;
+		m_scale		= wxEmptyString;
+		m_icon		= icon;
+		m_type		= "volume";
+		m_volume_id = volume_id;
+        m_extruder  = extruder;
+		set_part_action_icon();
+	}
+
+	~PrusaObjectDataViewModelNode()
+	{
+		// free all our children nodes
+		size_t count = m_children.GetCount();
+		for (size_t i = 0; i < count; i++)
+		{
+			PrusaObjectDataViewModelNode *child = m_children[i];
+			delete child;
+		}
+	}
+	
+	wxString				m_name;
+	wxIcon&					m_icon = m_empty_icon;
+	wxString				m_copy;
+	wxString				m_scale;
+	std::string				m_type;
+	int						m_volume_id;
+	bool					m_container = false;
+	wxString				m_extruder = "default";
+	wxBitmap				m_action_icon;
+
+	bool IsContainer() const
+	{
+		return m_container;
+	}
+
+	PrusaObjectDataViewModelNode* GetParent()
+	{
+		return m_parent;
+	}
+	MyObjectTreeModelNodePtrArray& GetChildren()
+	{
+		return m_children;
+	}
+	PrusaObjectDataViewModelNode* GetNthChild(unsigned int n)
+	{
+		return m_children.Item(n);
+	}
+	void Insert(PrusaObjectDataViewModelNode* child, unsigned int n)
+	{
+		m_children.Insert(child, n);
+	}
+	void Append(PrusaObjectDataViewModelNode* child)
+	{
+		if (!m_container)
+			m_container = true;
+		m_children.Add(child);
+	}
+	void RemoveAllChildren()
+	{
+		if (GetChildCount() == 0)
+			return;
+		for (size_t id = GetChildCount() - 1; id >= 0; --id)
+		{
+			if (m_children.Item(id)->GetChildCount() > 0)
+				m_children[id]->RemoveAllChildren();
+			auto node = m_children[id];
+			m_children.RemoveAt(id);
+			delete node;
+		}
+	}
+
+	size_t GetChildCount() const
+	{
+		return m_children.GetCount();
+	}
+
+	bool SetValue(const wxVariant &variant, unsigned int col)
+	{
+		switch (col)
+		{
+		case 0:{
+			wxDataViewIconText data;
+			data << variant;
+			m_icon = data.GetIcon();
+			m_name = data.GetText();
+			return true;}
+		case 1:
+			m_copy = variant.GetString();
+			return true;
+		case 2:
+			m_scale = variant.GetString();
+			return true;
+		case 3:
+			m_extruder = variant.GetString();
+			return true;
+		case 4:
+			m_action_icon << variant;
+			return true;
+		default:
+			printf("MyObjectTreeModel::SetValue: wrong column");
+		}
+		return false;
+	}
+	void SetIcon(const wxIcon &icon)
+	{
+		m_icon = icon;
+	}
+	
+	void SetType(const std::string& type){
+		m_type = type;
+	}	
+	const std::string& GetType(){
+		return m_type;
+	}
+
+	void SetVolumeId(const int& volume_id){
+		m_volume_id = volume_id;
+	}
+	const int& GetVolumeId(){
+		return m_volume_id;
+	}
+
+	// use this function only for childrens
+	void AssignAllVal(PrusaObjectDataViewModelNode& from_node)
+	{
+		// ! Don't overwrite other values because of equality of this values for all children --
+		m_name = from_node.m_name;
+		m_icon = from_node.m_icon;
+		m_volume_id = from_node.m_volume_id;
+		m_extruder = from_node.m_extruder;
+	}
+
+	bool SwapChildrens(int frst_id, int scnd_id) {
+		if (GetChildCount() < 2 || 
+			frst_id < 0 || frst_id >= GetChildCount() || 
+			scnd_id < 0 || scnd_id >= GetChildCount())
+			return false;
+
+		PrusaObjectDataViewModelNode new_scnd = *GetNthChild(frst_id);
+		PrusaObjectDataViewModelNode new_frst = *GetNthChild(scnd_id);
+
+		new_scnd.m_volume_id = m_children.Item(scnd_id)->m_volume_id;
+		new_frst.m_volume_id = m_children.Item(frst_id)->m_volume_id;
+
+		m_children.Item(frst_id)->AssignAllVal(new_frst);
+		m_children.Item(scnd_id)->AssignAllVal(new_scnd);
+		return true;
+	}
+
+	// Set action icons for node
+	void set_object_action_icon();
+	void set_part_action_icon();
+};
+
+// ----------------------------------------------------------------------------
+// PrusaObjectDataViewModel
+// ----------------------------------------------------------------------------
+
+class PrusaObjectDataViewModel :public wxDataViewModel
+{
+	std::vector<PrusaObjectDataViewModelNode*> m_objects;
+public:
+	PrusaObjectDataViewModel(){}
+	~PrusaObjectDataViewModel()
+	{
+		for (auto object : m_objects)
+			delete object;		
+	}
+
+	wxDataViewItem Add(wxString &name);
+	wxDataViewItem Add(wxString &name, int instances_count, int scale);
+	wxDataViewItem AddChild(const wxDataViewItem &parent_item, 
+							const wxString &name, 
+                            const wxIcon& icon,
+                            const int = 0,
+                            const bool create_frst_child = true);
+	wxDataViewItem Delete(const wxDataViewItem &item);
+	void DeleteAll();
+    void DeleteChildren(wxDataViewItem& parent);
+	wxDataViewItem GetItemById(int obj_idx);
+	int GetIdByItem(wxDataViewItem& item);
+	int GetVolumeIdByItem(wxDataViewItem& item);
+	bool IsEmpty() { return m_objects.empty(); }
+
+	// helper method for wxLog
+
+	wxString GetName(const wxDataViewItem &item) const;
+	wxString GetCopy(const wxDataViewItem &item) const;
+	wxString GetScale(const wxDataViewItem &item) const;
+	wxIcon&  GetIcon(const wxDataViewItem &item) const;
+
+	// helper methods to change the model
+
+	virtual unsigned int GetColumnCount() const override { return 3;}
+	virtual wxString GetColumnType(unsigned int col) const override{ return wxT("string"); }
+
+	virtual void GetValue(wxVariant &variant,
+		const wxDataViewItem &item, unsigned int col) const override;
+	virtual bool SetValue(const wxVariant &variant,
+		const wxDataViewItem &item, unsigned int col) override;
+	bool SetValue(const wxVariant &variant, const int item_idx, unsigned int col);
+
+	wxDataViewItem MoveChildUp(const wxDataViewItem &item);
+	wxDataViewItem MoveChildDown(const wxDataViewItem &item);
+    // For parent move child from cur_volume_id place to new_volume_id 
+    // Remaining items will moved up/down accordingly
+    wxDataViewItem ReorganizeChildren(int cur_volume_id, 
+                                      int new_volume_id,
+                                      const wxDataViewItem &parent);
+
+// 	virtual bool IsEnabled(const wxDataViewItem &item,
+// 		unsigned int col) const override;
+
+	virtual wxDataViewItem GetParent(const wxDataViewItem &item) const override;
+	virtual bool IsContainer(const wxDataViewItem &item) const override;
+	virtual unsigned int GetChildren(const wxDataViewItem &parent,
+		wxDataViewItemArray &array) const override;
+
+	// Is the container just a header or an item with all columns
+	// In our case it is an item with all columns 
+	virtual bool HasContainerColumns(const wxDataViewItem& WXUNUSED(item)) const override {	return true; }
+};
+
+
+
+// ----------------------------------------------------------------------------
+// MyCustomRenderer
+// ----------------------------------------------------------------------------
+
+class MyCustomRenderer : public wxDataViewCustomRenderer
+{
+public:
+	// This renderer can be either activatable or editable, for demonstration
+	// purposes. In real programs, you should select whether the user should be
+	// able to activate or edit the cell and it doesn't make sense to switch
+	// between the two -- but this is just an example, so it doesn't stop us.
+	explicit MyCustomRenderer(wxDataViewCellMode mode)
+		: wxDataViewCustomRenderer("string", mode, wxALIGN_CENTER)
+	{ }
+
+	virtual bool Render(wxRect rect, wxDC *dc, int state) override/*wxOVERRIDE*/
+	{
+		dc->SetBrush(*wxLIGHT_GREY_BRUSH);
+		dc->SetPen(*wxTRANSPARENT_PEN);
+
+		rect.Deflate(2);
+		dc->DrawRoundedRectangle(rect, 5);
+
+		RenderText(m_value,
+			0, // no offset
+			wxRect(dc->GetTextExtent(m_value)).CentreIn(rect),
+			dc,
+			state);
+		return true;
+	}
+
+		virtual bool ActivateCell(const wxRect& WXUNUSED(cell),
+		wxDataViewModel *WXUNUSED(model),
+		const wxDataViewItem &WXUNUSED(item),
+		unsigned int WXUNUSED(col),
+		const wxMouseEvent *mouseEvent) override/*wxOVERRIDE*/
+	{
+		wxString position;
+		if (mouseEvent)
+			position = wxString::Format("via mouse at %d, %d", mouseEvent->m_x, mouseEvent->m_y);
+		else
+			position = "from keyboard";
+//		wxLogMessage("MyCustomRenderer ActivateCell() %s", position);
+		return false;
+	}
+
+		virtual wxSize GetSize() const override/*wxOVERRIDE*/
+	{
+		return wxSize(60, 20);
+	}
+
+		virtual bool SetValue(const wxVariant &value) override/*wxOVERRIDE*/
+	{
+		m_value = value.GetString();
+		return true;
+	}
+
+		virtual bool GetValue(wxVariant &WXUNUSED(value)) const override/*wxOVERRIDE*/{ return true; }
+
+		virtual bool HasEditorCtrl() const override/*wxOVERRIDE*/{ return true; }
+
+		virtual wxWindow*
+		CreateEditorCtrl(wxWindow* parent,
+		wxRect labelRect,
+		const wxVariant& value) override/*wxOVERRIDE*/
+	{
+		wxTextCtrl* text = new wxTextCtrl(parent, wxID_ANY, value,
+		labelRect.GetPosition(),
+		labelRect.GetSize(),
+		wxTE_PROCESS_ENTER);
+		text->SetInsertionPointEnd();
+
+		return text;
+	}
+
+		virtual bool
+			GetValueFromEditorCtrl(wxWindow* ctrl, wxVariant& value) override/*wxOVERRIDE*/
+	{
+		wxTextCtrl* text = wxDynamicCast(ctrl, wxTextCtrl);
+		if (!text)
+			return false;
+
+		value = text->GetValue();
+
+		return true;
+	}
+
+private:
+	wxString m_value;
+};
+// ******************************* EXPERIMENTS **********************************************
+enum SelectedSlider {
+    ssUndef,
+    ssLower,
+    ssHigher
+};
+enum TicksAction{
+    taOnIcon,
+    taAdd,
+    taDel
+};
+class PrusaDoubleSlider : public wxControl
+{
+public:
+    PrusaDoubleSlider(
+        wxWindow *parent,
+        wxWindowID id,
+        int lowerValue, 
+        int higherValue, 
+        int minValue, 
+        int maxValue,
+        const wxPoint& pos = wxDefaultPosition,
+        const wxSize& size = wxDefaultSize,
+        long style = wxSL_HORIZONTAL,
+        const wxValidator& val = wxDefaultValidator,
+        const wxString& name = wxEmptyString);
+
+    int GetLowerValue() const {
+        return m_lower_value;
+    }
+    int GetHigherValue() const {
+        return m_higher_value;
+    }
+    int GetActiveValue() const;
+    wxSize DoGetBestSize() const override;
+    void SetLowerValue(const int lower_val);
+    void SetHigherValue(const int higher_val);
+    void SetMaxValue(const int max_value);
+    void SetKoefForLabels(const double koef) {
+        m_label_koef = koef;
+    }
+    void SetSliderValues(const std::vector<double>& values) {
+        m_values = values;
+    }
+
+    void OnPaint(wxPaintEvent& ){ render();}
+    void OnLeftDown(wxMouseEvent& event);
+    void OnMotion(wxMouseEvent& event);
+    void OnLeftUp(wxMouseEvent& event);
+    void OnEnterWin(wxMouseEvent& event){ enter_window(event, true); }
+    void OnLeaveWin(wxMouseEvent& event){ enter_window(event, false); }
+    void OnWheel(wxMouseEvent& event);
+    void OnKeyDown(wxKeyEvent &event);
+    void OnKeyUp(wxKeyEvent &event);
+    void OnRightDown(wxMouseEvent& event);
+    void OnRightUp(wxMouseEvent& event);
+
+protected:
+ 
+    void    render();
+    void    draw_focus_rect();
+    void    draw_action_icon(wxDC& dc, const wxPoint pt_beg, const wxPoint pt_end);
+    void    draw_scroll_line(wxDC& dc, const int lower_pos, const int higher_pos);
+    void    draw_thumb(wxDC& dc, const wxCoord& pos_coord, const SelectedSlider& selection);
+    void    draw_thumbs(wxDC& dc, const wxCoord& lower_pos, const wxCoord& higher_pos);
+    void    draw_ticks(wxDC& dc);
+    void    draw_one_layer_icon(wxDC& dc);
+    void    draw_thumb_item(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection);
+    void    draw_info_line_with_icon(wxDC& dc, const wxPoint& pos, SelectedSlider selection);
+    void    draw_thumb_text(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection) const;
+
+    void    update_thumb_rect(const wxCoord& begin_x, const wxCoord& begin_y, const SelectedSlider& selection);
+    void    detect_selected_slider(const wxPoint& pt, const bool is_mouse_wheel = false);
+    void    correct_lower_value();
+    void    correct_higher_value();
+    void    move_current_thumb(const bool condition);
+    void    action_tick(const TicksAction action);
+    void    enter_window(wxMouseEvent& event, const bool enter);
+
+    bool    is_point_in_rect(const wxPoint& pt, const wxRect& rect);
+    bool    is_horizontal() const { return m_style == wxSL_HORIZONTAL; }
+
+    double      get_scroll_step();
+    wxString    get_label(const SelectedSlider& selection) const;
+    void        get_lower_and_higher_position(int& lower_pos, int& higher_pos);
+    int         get_value_from_position(const wxCoord x, const wxCoord y);
+    wxCoord     get_position_from_value(const int value);
+    wxSize      get_size();
+    void        get_size(int *w, int *h);
+
+private:
+    int         m_min_value;
+    int         m_max_value;
+    int         m_lower_value;
+    int         m_higher_value;
+    wxBitmap    m_bmp_thumb_higher;
+    wxBitmap    m_bmp_thumb_lower;
+    wxBitmap    m_bmp_add_tick_on;
+    wxBitmap    m_bmp_add_tick_off;
+    wxBitmap    m_bmp_del_tick_on;
+    wxBitmap    m_bmp_del_tick_off;
+    wxBitmap    m_bmp_one_layer_lock_on;
+    wxBitmap    m_bmp_one_layer_lock_off;
+    wxBitmap    m_bmp_one_layer_unlock_on;
+    wxBitmap    m_bmp_one_layer_unlock_off;
+    SelectedSlider  m_selection;
+    bool        m_is_left_down = false;
+    bool        m_is_right_down = false;
+    bool        m_is_one_layer = false;
+    bool        m_is_focused = false;
+    bool        m_is_action_icon_focesed = false;
+    bool        m_is_one_layer_icon_focesed = false;
+
+    wxRect      m_rect_lower_thumb;
+    wxRect      m_rect_higher_thumb;
+    wxRect      m_rect_tick_action;
+    wxRect      m_rect_one_layer_icon;
+    wxSize      m_thumb_size;
+    int         m_tick_icon_dim;
+    int         m_lock_icon_dim = 16;
+    long        m_style;
+    float       m_label_koef = 1.0;
+
+// control's view variables
+    wxCoord SLIDER_MARGIN; // margin around slider
+
+    wxPen   DARK_ORANGE_PEN;
+    wxPen   ORANGE_PEN;
+    wxPen   LIGHT_ORANGE_PEN;
+
+    wxPen   DARK_GREY_PEN;
+    wxPen   GREY_PEN;
+    wxPen   LIGHT_GREY_PEN;
+
+    std::vector<wxPen*> line_pens;
+    std::vector<wxPen*> segm_pens;
+    std::set<int>       m_ticks;
+    std::vector<double> m_values;
+};
+// ******************************************************************************************
+
+
 #endif // slic3r_GUI_wxExtensions_hpp_
diff --git a/xs/src/slic3r/Utils/PresetUpdater.cpp b/xs/src/slic3r/Utils/PresetUpdater.cpp
index 6e23ab421..2e423dc5e 100644
--- a/xs/src/slic3r/Utils/PresetUpdater.cpp
+++ b/xs/src/slic3r/Utils/PresetUpdater.cpp
@@ -447,6 +447,7 @@ void PresetUpdater::priv::perform_updates(Updates &&updates, bool snapshot) cons
 
 			for (const auto &name : bundle.obsolete_presets.prints)    { obsolete_remover("print", name); }
 			for (const auto &name : bundle.obsolete_presets.filaments) { obsolete_remover("filament", name); }
+			for (const auto &name : bundle.obsolete_presets.filaments) { obsolete_remover("sla_material", name); }
 			for (const auto &name : bundle.obsolete_presets.printers)  { obsolete_remover("printer", name); }
 		}
 	}
diff --git a/xs/xsp/GUI.xsp b/xs/xsp/GUI.xsp
index c6eead1ad..b9183af1d 100644
--- a/xs/xsp/GUI.xsp
+++ b/xs/xsp/GUI.xsp
@@ -87,6 +87,80 @@ void add_frequently_changed_parameters(SV *ui_parent, SV *ui_sizer, SV *ui_p_siz
                                                            (wxBoxSizer*)wxPli_sv_2_object(aTHX_ ui_sizer, "Wx::BoxSizer"),
                                                            (wxFlexGridSizer*)wxPli_sv_2_object(aTHX_ ui_p_sizer, "Wx::FlexGridSizer")); %};
 
+void add_expert_mode_part(  SV *ui_parent, SV *ui_sizer, 
+                            Model *model,
+                            int event_object_selection_changed,
+                            int event_object_settings_changed,
+                            int event_remove_object, 
+                            int event_update_scene)
+    %code%{ Slic3r::GUI::add_expert_mode_part(  (wxWindow*)wxPli_sv_2_object(aTHX_ ui_parent, "Wx::Window"),
+                                                (wxBoxSizer*)wxPli_sv_2_object(aTHX_ ui_sizer, "Wx::BoxSizer"), 
+                                                *model,  
+                                                event_object_selection_changed,
+                                                event_object_settings_changed,
+                                                event_remove_object, 
+                                                event_update_scene); %};
+
+void set_objects_from_perl( SV *ui_parent, 
+                            SV *frequently_changed_parameters_sizer,
+                            SV *expert_mode_part_sizer,
+                            SV *scrolled_window_sizer,
+                            SV *btn_export_gcode,
+                            SV *btn_export_stl,
+                            SV *btn_reslice,
+                            SV *btn_print,
+                            SV *btn_send_gcode,
+                            SV *manifold_warning_icon)
+    %code%{ Slic3r::GUI::set_objects_from_perl(
+                            (wxWindow *)wxPli_sv_2_object(aTHX_ ui_parent, "Wx::Window"),
+                            (wxBoxSizer *)wxPli_sv_2_object(aTHX_ frequently_changed_parameters_sizer, "Wx::BoxSizer"),
+                            (wxBoxSizer *)wxPli_sv_2_object(aTHX_ expert_mode_part_sizer, "Wx::BoxSizer"),
+                            (wxBoxSizer *)wxPli_sv_2_object(aTHX_ scrolled_window_sizer, "Wx::BoxSizer"),
+                            (wxButton *)wxPli_sv_2_object(aTHX_ btn_export_gcode, "Wx::Button"),
+                            (wxButton *)wxPli_sv_2_object(aTHX_ btn_export_stl, "Wx::Button"),
+                            (wxButton *)wxPli_sv_2_object(aTHX_ btn_reslice, "Wx::Button"),
+                            (wxButton *)wxPli_sv_2_object(aTHX_ btn_print, "Wx::Button"),
+                            (wxButton *)wxPli_sv_2_object(aTHX_ btn_send_gcode, "Wx::Button"),
+                            (wxStaticBitmap *)wxPli_sv_2_object(aTHX_ manifold_warning_icon, "Wx::StaticBitmap")); %};
+
+void set_show_print_info(bool show)
+    %code%{ Slic3r::GUI::set_show_print_info(show); %};
+
+void set_show_manifold_warning_icon(bool show)
+    %code%{ Slic3r::GUI::set_show_manifold_warning_icon(show); %};
+
+void update_mode()
+    %code%{ Slic3r::GUI::update_mode(); %};
+
+void add_object_to_list(const char *name, SV *object_model)
+    %code%{ Slic3r::GUI::add_object_to_list(
+                    name, 
+                    (ModelObject *)wxPli_sv_2_object(aTHX_ object_model, "Slic3r::Model::Object") ); %};
+
+void delete_object_from_list()
+    %code%{ Slic3r::GUI::delete_object_from_list(); %};
+
+void delete_all_objects_from_list()
+    %code%{ Slic3r::GUI::delete_all_objects_from_list(); %};
+
+void set_object_count(int idx, int count)
+    %code%{ Slic3r::GUI::set_object_count(idx, count); %};
+
+void set_object_scale(int idx, int scale)
+    %code%{ Slic3r::GUI::set_object_scale(idx, scale); %};
+
+void unselect_objects()
+    %code%{ Slic3r::GUI::unselect_objects(); %};
+
+void select_current_object(int idx)
+    %code%{ Slic3r::GUI::select_current_object(idx); %};
+
+void remove_obj()
+    %code%{ Slic3r::GUI::remove(); %};
+
+void update_rotation_value(double angle, const char *axis)
+    %code%{ Slic3r::GUI::update_rotation_value(angle, axis); %};
+
 std::string fold_utf8_to_ascii(const char *src)
     %code%{ RETVAL = Slic3r::fold_utf8_to_ascii(src); %};
 
diff --git a/xs/xsp/GUI_Preset.xsp b/xs/xsp/GUI_Preset.xsp
index 2c63db10c..99d23a142 100644
--- a/xs/xsp/GUI_Preset.xsp
+++ b/xs/xsp/GUI_Preset.xsp
@@ -32,7 +32,6 @@
 %name{Slic3r::GUI::PresetCollection} class PresetCollection {
 
     Ref<Preset>             preset(size_t idx) %code%{ RETVAL = &THIS->preset(idx); %};
-    Ref<Preset>             default_preset() %code%{ RETVAL = &THIS->default_preset(); %};
     size_t                  size() const;
     size_t                  num_visible() const;
     std::string             name() const;
@@ -133,6 +132,7 @@ PresetCollection::arrayref()
 
     Ref<PresetCollection>       print()    %code%{ RETVAL = &THIS->prints;   %};
     Ref<PresetCollection>       filament() %code%{ RETVAL = &THIS->filaments; %};
+    Ref<PresetCollection>       sla_material() %code%{ RETVAL = &THIS->sla_materials; %};
     Ref<PresetCollection>       printer()  %code%{ RETVAL = &THIS->printers;  %};
     Ref<DynamicPrintConfig>     project_config() %code%{ RETVAL = &THIS->project_config; %};