diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm
index 94f01e25d..d2a7a23b1 100644
--- a/lib/Slic3r/GUI/Plater.pm
+++ b/lib/Slic3r/GUI/Plater.pm
@@ -594,7 +594,7 @@ sub new {
         $self->on_process_completed($event->GetInt ? undef : $event->GetString);
     });
     
-# XXX: ???
+# XXX: not done
     {
         my $timer_id = Wx::NewId();
         $self->{apply_config_timer} = Wx::Timer->new($self, $timer_id);
@@ -1179,7 +1179,7 @@ sub reset {
     $self->update;
 }
 
-# XXX: not done
+# XXX: VK: done
 sub increase {
     my ($self, $copies) = @_;
     $copies //= 1;
@@ -1211,7 +1211,7 @@ sub increase {
     $self->schedule_background_process;
 }
 
-# XXX: not done
+# XXX: VK: done
 sub decrease {
     my ($self, $copies_asked) = @_;
     my $copies = $copies_asked // 1;
@@ -1239,7 +1239,7 @@ sub decrease {
     $self->update;
 }
 
-# XXX: not done
+# XXX: VK: done
 sub set_number_of_copies {
     my ($self) = @_;
     # get current number of copies
@@ -1258,8 +1258,8 @@ sub set_number_of_copies {
     }
 }
 
-# XXX: not done (?)
-sub _get_number_from_user {     # XXX: Enrico
+# XXX: VK: removed
+sub _get_number_from_user {
     my ($self, $title, $prompt_message, $error_message, $default, $only_positive) = @_;
     for (;;) {
         my $value = Wx::GetTextFromUser($prompt_message, $title, $default, $self);
@@ -1457,7 +1457,7 @@ sub changescale {
     $self->update;
 }
 
-# XXX: not done
+# XXX: VK: WIP
 sub arrange {
     my ($self) = @_;
     
@@ -2057,7 +2057,7 @@ sub update {
     $self->Thaw;
 }
 
-# XXX: done in sidebar?
+# XXX: YS: done
 # 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"
@@ -2076,7 +2076,7 @@ sub show_preset_comboboxes{
     $self->Layout;
 }
 
-# XXX: not done
+# XXX: YS: done
 # 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.
@@ -2188,7 +2188,7 @@ sub on_config_change {
     $self->schedule_background_process;
 }
 
-# XXX: not done
+# XXX: YS: WIP
 sub item_changed_selection {
     my ($self, $obj_idx) = @_;
 
@@ -2214,7 +2214,7 @@ sub collect_selections {
     return $selections;
 }
 
-# XXX: not done
+# XXX: YS: done, lambda on LEFT_DOWN
 # Called when clicked on the filament preset combo box.
 # When clicked on the icon, show the color picker.
 sub filament_color_box_lmouse_down
@@ -2268,7 +2268,7 @@ sub filament_color_box_lmouse_down
 #	}
 #}
 
-# XXX: not done
+# XXX: YS: done
 sub changed_object_settings {
     my ($self, $obj_idx, $parts_changed, $part_settings_changed) = @_;
     
@@ -2464,7 +2464,7 @@ sub select_object {
     $self->selection_changed(1);
 }
 
-# XXX: not done
+# XXX: YS: WIP
 sub select_object_from_cpp {
     my ($self, $obj_idx, $vol_idx) = @_;
     
diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp
index 88e4c551a..e0cf14a57 100644
--- a/src/libslic3r/Model.cpp
+++ b/src/libslic3r/Model.cpp
@@ -609,6 +609,15 @@ ModelInstance* ModelObject::add_instance(const ModelInstance &other)
     return i;
 }
 
+ModelInstance* ModelObject::add_instance(const Vec3d &offset, const Vec3d &scaling_factor, const Vec3d &rotation)
+{
+    auto *instance = add_instance();
+    instance->set_offset(offset);
+    instance->set_scaling_factor(scaling_factor);
+    instance->set_rotation(rotation);
+    return instance;
+}
+
 void ModelObject::delete_instance(size_t idx)
 {
     ModelInstancePtrs::iterator i = this->instances.begin() + idx;
diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp
index d9abc109a..695de6127 100644
--- a/src/libslic3r/Model.hpp
+++ b/src/libslic3r/Model.hpp
@@ -96,6 +96,7 @@ public:
 
     ModelInstance* add_instance();
     ModelInstance* add_instance(const ModelInstance &instance);
+    ModelInstance* add_instance(const Vec3d &offset, const Vec3d &scaling_factor, const Vec3d &rotation);
     void delete_instance(size_t idx);
     void delete_last_instance();
     void clear_instances();
diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp
index a646ab8fd..5bbeb3f6a 100644
--- a/src/libslic3r/Print.hpp
+++ b/src/libslic3r/Print.hpp
@@ -416,7 +416,8 @@ public:
     const PrintObjectConfig&    default_object_config() const { return m_default_object_config; }
     const PrintRegionConfig&    default_region_config() const { return m_default_region_config; }
     const PrintObjectPtrs&      objects() const { return m_objects; }
-    const PrintObject*          get_object(int idx) const { return m_objects[idx]; }
+    PrintObject*                get_object(size_t idx) { return m_objects[idx]; }
+    const PrintObject*          get_object(size_t idx) const { return m_objects[idx]; }
     const PrintRegionPtrs&      regions() const { return m_regions; }
     const PlaceholderParser&    placeholder_parser() const { return m_placeholder_parser; }
     PlaceholderParser&          placeholder_parser() { return m_placeholder_parser; }
diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp
index 68d4dac67..92e903911 100644
--- a/src/slic3r/GUI/3DScene.cpp
+++ b/src/slic3r/GUI/3DScene.cpp
@@ -572,7 +572,7 @@ void GLVolume::render_VBOs(int color_id, int detection_id, int worldmatrix_id) c
             ::glUniform4fv(color_id, 1, (const GLfloat*)color);
         }
         else
-            ::glColor4f(render_color[0], render_color[1], render_color[2], render_color[3]);
+            ::glColor4fv(render_color);
 
         if (detection_id != -1)
             ::glUniform1i(detection_id, shader_outside_printer_detection_enabled ? 1 : 0);
@@ -591,7 +591,7 @@ void GLVolume::render_VBOs(int color_id, int detection_id, int worldmatrix_id) c
     if (color_id >= 0)
         ::glUniform4fv(color_id, 1, (const GLfloat*)render_color);
     else
-        ::glColor4f(render_color[0], render_color[1], render_color[2], render_color[3]);
+        ::glColor4fv(render_color);
 
     if (detection_id != -1)
         ::glUniform1i(detection_id, shader_outside_printer_detection_enabled ? 1 : 0);
@@ -640,7 +640,7 @@ void GLVolume::render_legacy() const
         ::glDisableClientState(GL_VERTEX_ARRAY);
         ::glDisableClientState(GL_NORMAL_ARRAY);
 
-        ::glColor4f(render_color[0], render_color[1], render_color[2], render_color[3]);
+        ::glColor4fv(render_color);
         render();
 
         ::glEnableClientState(GL_VERTEX_ARRAY);
@@ -649,7 +649,7 @@ void GLVolume::render_legacy() const
         return;
     }
 
-    ::glColor4f(render_color[0], render_color[1], render_color[2], render_color[3]);
+    ::glColor4fv(render_color);
     ::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), indexed_vertex_array.vertices_and_normals_interleaved.data() + 3);
     ::glNormalPointer(GL_FLOAT, 6 * sizeof(float), indexed_vertex_array.vertices_and_normals_interleaved.data());
 
diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index f3fb9b0be..a2067bbf3 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -600,7 +600,8 @@ bool GLCanvas3D::Bed::_are_equal(const Pointfs& bed_1, const Pointfs& bed_2)
 }
 
 GLCanvas3D::Axes::Axes()
-    : origin(0, 0, 0), length(0.0f)
+    : origin(Vec3d::Zero())
+    , length(0.0f)
 {
 }
 
@@ -615,11 +616,11 @@ void GLCanvas3D::Axes::render(bool depth_test) const
     ::glBegin(GL_LINES);
     // draw line for x axis
     ::glColor3f(1.0f, 0.0f, 0.0f);
-    ::glVertex3f((GLfloat)origin(0), (GLfloat)origin(1), (GLfloat)origin(2));
+    ::glVertex3dv(origin.data());
     ::glVertex3f((GLfloat)origin(0) + length, (GLfloat)origin(1), (GLfloat)origin(2));
     // draw line for y axis
     ::glColor3f(0.0f, 1.0f, 0.0f);
-    ::glVertex3f((GLfloat)origin(0), (GLfloat)origin(1), (GLfloat)origin(2));
+    ::glVertex3dv(origin.data());
     ::glVertex3f((GLfloat)origin(0), (GLfloat)origin(1) + length, (GLfloat)origin(2));
     ::glEnd();
     // draw line for Z axis
@@ -629,7 +630,7 @@ void GLCanvas3D::Axes::render(bool depth_test) const
 
     ::glBegin(GL_LINES);
     ::glColor3f(0.0f, 0.0f, 1.0f);
-    ::glVertex3f((GLfloat)origin(0), (GLfloat)origin(1), (GLfloat)origin(2));
+    ::glVertex3dv(origin.data());
     ::glVertex3f((GLfloat)origin(0), (GLfloat)origin(1), (GLfloat)origin(2) + length);
     ::glEnd();
 }
@@ -3234,13 +3235,7 @@ void GLCanvas3D::update_gizmos_data()
 
 #if ENABLE_EXTENDED_SELECTION
     bool enable_move_z = !m_selection.is_wipe_tower();
-    bool enable_rotate_xy = m_selection.is_single_full_object() || m_selection.is_mixed();
-
     m_gizmos.enable_grabber(Gizmos::Move, 2, enable_move_z);
-    for (int i = 0; i < 2; ++i)
-    {
-        m_gizmos.enable_grabber(Gizmos::Rotate, i, enable_rotate_xy);
-    }
 
     if (m_selection.is_single_full_instance())
     {
@@ -4894,7 +4889,7 @@ void GLCanvas3D::_camera_tranform() const
     ::glRotatef(m_camera.phi, 0.0f, 0.0f, 1.0f);          // yaw
 
     Vec3d neg_target = - m_camera.target;
-    ::glTranslatef((GLfloat)neg_target(0), (GLfloat)neg_target(1), (GLfloat)neg_target(2));
+    ::glTranslated(neg_target(0), neg_target(1), neg_target(2));
 }
 
 void GLCanvas3D::_picking_pass() const
@@ -5003,9 +4998,9 @@ void GLCanvas3D::_render_background() const
     ::glVertex2f(1.0f, -1.0f);
 
     if (m_dynamic_background_enabled && _is_any_volume_outside())
-        ::glColor3f(ERROR_BG_COLOR[0], ERROR_BG_COLOR[1], ERROR_BG_COLOR[2]);
+        ::glColor3fv(ERROR_BG_COLOR);
     else
-        ::glColor3f(DEFAULT_BG_COLOR[0], DEFAULT_BG_COLOR[1], DEFAULT_BG_COLOR[2]);
+        ::glColor3fv(DEFAULT_BG_COLOR);
 
     ::glVertex2f(1.0f, 1.0f);
     ::glVertex2f(-1.0f, 1.0f);
@@ -5172,7 +5167,7 @@ void GLCanvas3D::_render_volumes(bool fake_colors) const
         else
         {
             vol->set_render_color();
-            ::glColor4f(vol->render_color[0], vol->render_color[1], vol->render_color[2], vol->render_color[3]);
+            ::glColor4fv(vol->render_color);
         }
 
         vol->render();
diff --git a/src/slic3r/GUI/GLGizmo.cpp b/src/slic3r/GUI/GLGizmo.cpp
index 72bf18de9..ced0ca85b 100644
--- a/src/slic3r/GUI/GLGizmo.cpp
+++ b/src/slic3r/GUI/GLGizmo.cpp
@@ -142,15 +142,15 @@ void GLGizmoBase::Grabber::render(const BoundingBoxf3& box, const float* render_
     if (use_lighting)
         ::glEnable(GL_LIGHTING);
 
-    ::glColor3f((GLfloat)render_color[0], (GLfloat)render_color[1], (GLfloat)render_color[2]);
+    ::glColor3fv(render_color);
 
     ::glPushMatrix();
-    ::glTranslatef((GLfloat)center(0), (GLfloat)center(1), (GLfloat)center(2));
+    ::glTranslated(center(0), center(1), center(2));
 
-    float rad_to_deg = 180.0f / (GLfloat)PI;
-    ::glRotatef((GLfloat)angles(0) * rad_to_deg, 1.0f, 0.0f, 0.0f);
-    ::glRotatef((GLfloat)angles(1) * rad_to_deg, 0.0f, 1.0f, 0.0f);
-    ::glRotatef((GLfloat)angles(2) * rad_to_deg, 0.0f, 0.0f, 1.0f);
+    double rad_to_deg = 180.0 / (double)PI;
+    ::glRotated(angles(0) * rad_to_deg, 1.0, 0.0, 0.0);
+    ::glRotated(angles(1) * rad_to_deg, 0.0, 1.0, 0.0);
+    ::glRotated(angles(2) * rad_to_deg, 0.0, 0.0, 1.0);
 
     // face min x
     ::glPushMatrix();
@@ -568,7 +568,7 @@ void GLGizmoRotate::render_grabber(const BoundingBoxf3& box) const
 
     ::glBegin(GL_LINES);
     ::glVertex3f(0.0f, 0.0f, 0.0f);
-    ::glVertex3f((GLfloat)m_grabbers[0].center(0), (GLfloat)m_grabbers[0].center(1), (GLfloat)m_grabbers[0].center(2));
+    ::glVertex3dv(m_grabbers[0].center.data());
     ::glEnd();
 
     ::memcpy((void*)m_grabbers[0].color, (const void*)m_highlight_color, 3 * sizeof(float));
@@ -577,7 +577,7 @@ void GLGizmoRotate::render_grabber(const BoundingBoxf3& box) const
 
 void GLGizmoRotate::transform_to_local() const
 {
-    ::glTranslatef((GLfloat)m_center(0), (GLfloat)m_center(1), (GLfloat)m_center(2));
+    ::glTranslated(m_center(0), m_center(1), m_center(2));
 
     switch (m_axis)
     {
@@ -965,8 +965,8 @@ void GLGizmoScale3D::render_grabbers_connection(unsigned int id_1, unsigned int
     if ((id_1 < grabbers_count) && (id_2 < grabbers_count))
     {
         ::glBegin(GL_LINES);
-        ::glVertex3f((GLfloat)m_grabbers[id_1].center(0), (GLfloat)m_grabbers[id_1].center(1), (GLfloat)m_grabbers[id_1].center(2));
-        ::glVertex3f((GLfloat)m_grabbers[id_2].center(0), (GLfloat)m_grabbers[id_2].center(1), (GLfloat)m_grabbers[id_2].center(2));
+        ::glVertex3dv(m_grabbers[id_1].center.data());
+        ::glVertex3dv(m_grabbers[id_2].center.data());
         ::glEnd();
     }
 }
@@ -1170,8 +1170,8 @@ void GLGizmoMove3D::on_render(const BoundingBoxf3& box) const
             {
                 ::glColor3fv(AXES_COLOR[i]);
                 ::glBegin(GL_LINES);
-                ::glVertex3f(center(0), center(1), center(2));
-                ::glVertex3f((GLfloat)m_grabbers[i].center(0), (GLfloat)m_grabbers[i].center(1), (GLfloat)m_grabbers[i].center(2));
+                ::glVertex3dv(center.data());
+                ::glVertex3dv(m_grabbers[i].center.data());
                 ::glEnd();
             }
         }
@@ -1184,8 +1184,8 @@ void GLGizmoMove3D::on_render(const BoundingBoxf3& box) const
         // draw axis
         ::glColor3fv(AXES_COLOR[m_hover_id]);
         ::glBegin(GL_LINES);
-        ::glVertex3f(center(0), center(1), center(2));
-        ::glVertex3f((GLfloat)m_grabbers[m_hover_id].center(0), (GLfloat)m_grabbers[m_hover_id].center(1), (GLfloat)m_grabbers[m_hover_id].center(2));
+        ::glVertex3dv(center.data());
+        ::glVertex3dv(m_grabbers[m_hover_id].center.data());
         ::glEnd();
 
         // draw grabber
@@ -1303,7 +1303,7 @@ void GLGizmoFlatten::on_render(const BoundingBoxf3& box) const
 #endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM
             ::glBegin(GL_POLYGON);
             for (const Vec3d& vertex : m_planes[i].vertices)
-                ::glVertex3f((GLfloat)vertex(0), (GLfloat)vertex(1), (GLfloat)vertex(2));
+                ::glVertex3dv(vertex.data());
             ::glEnd();
             ::glPopMatrix();
         }
@@ -1330,7 +1330,7 @@ void GLGizmoFlatten::on_render_for_picking(const BoundingBoxf3& box) const
 #endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM
             ::glBegin(GL_POLYGON);
             for (const Vec3d& vertex : m_planes[i].vertices)
-                ::glVertex3f((GLfloat)vertex(0), (GLfloat)vertex(1), (GLfloat)vertex(2));
+                ::glVertex3dv(vertex.data());
             ::glEnd();
             ::glPopMatrix();
         }
diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp
index ecd1ddea2..505a45125 100644
--- a/src/slic3r/GUI/MainFrame.cpp
+++ b/src/slic3r/GUI/MainFrame.cpp
@@ -58,9 +58,9 @@ wxFrame(NULL, wxID_ANY, SLIC3R_BUILD, wxDefaultPosition, wxDefaultSize, wxDEFAUL
                                  SLIC3R_VERSION +
                                  _(L(" - Remember to check for updates at http://github.com/prusa3d/slic3r/releases")));
 
-	//     m_appController->set_model(m_plater->model);
-	//     m_appController->set_print(m_plater->print);
-	//     m_plater->appController = m_appController;
+    m_appController->set_model(&m_plater->model());
+    m_appController->set_print(&m_plater->print());
+
 	GUI::set_gui_appctl();
 
 	// Make the global status bar and its progress indicator available in C++
diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp
index aa2041a36..ed2646b69 100644
--- a/src/slic3r/GUI/MainFrame.hpp
+++ b/src/slic3r/GUI/MainFrame.hpp
@@ -106,6 +106,8 @@ public:
     void        select_tab(size_t tab) const;
     void        select_view(const std::string& direction);
 
+    AppController* app_controller() { return m_appController; }
+
     std::vector<PresetTab>& get_preset_tabs();
 
     Plater*             m_plater { nullptr };
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index f130ef449..72ef47670 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -31,6 +31,7 @@
 #include "libslic3r/Format/STL.hpp"
 #include "libslic3r/Format/AMF.hpp"
 #include "libslic3r/Format/3mf.hpp"
+#include "slic3r/AppController.hpp"
 #include "GUI.hpp"
 #include "GUI_App.hpp"
 #include "GUI_ObjectList.hpp"
@@ -756,9 +757,6 @@ struct Plater::priv
 
     void remove(size_t obj_idx);
     void reset();
-    void increase(size_t num = 1);
-    void decrease(size_t num = 1);
-    void set_number_of_copies();
     void rotate();
     void mirror(const Axis &axis);
     void scale();
@@ -781,9 +779,6 @@ struct Plater::priv
     void on_layer_editing_toggled(bool enable);
 
     void on_action_add(SimpleEvent&);
-    void on_action_arrange(SimpleEvent&);
-    void on_action_more(SimpleEvent&);
-    void on_action_fewer(SimpleEvent&);
     void on_action_split(SimpleEvent&);
     void on_action_cut(SimpleEvent&);
     void on_action_settings(SimpleEvent&);
@@ -886,7 +881,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) :
     canvas3D->Bind(EVT_GLCANVAS_ARRANGE, [this](SimpleEvent&) { arrange(); });
     canvas3D->Bind(EVT_GLCANVAS_ROTATE_OBJECT, [this](Event<int> &evt) { /*TODO: call rotate */ });
     canvas3D->Bind(EVT_GLCANVAS_SCALE_UNIFORMLY, [this](SimpleEvent&) { scale(); });
-    canvas3D->Bind(EVT_GLCANVAS_INCREASE_OBJECTS, [this](Event<int> &evt) { evt.data == 1 ? increase() : decrease(); });
+    canvas3D->Bind(EVT_GLCANVAS_INCREASE_OBJECTS, [q](Event<int> &evt) { evt.data == 1 ? q->increase() : q->decrease(); });
     canvas3D->Bind(EVT_GLCANVAS_INSTANCE_MOVED, [this](SimpleEvent&) { update(); });
     canvas3D->Bind(EVT_GLCANVAS_WIPETOWER_MOVED, &priv::on_wipetower_moved, this);
     canvas3D->Bind(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, &priv::on_enable_action_buttons, this);
@@ -895,9 +890,9 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) :
     canvas3D->Bind(EVT_GLTOOLBAR_ADD, &priv::on_action_add, this);
     canvas3D->Bind(EVT_GLTOOLBAR_DELETE, [q](SimpleEvent&) { q->remove_selected(); } );
     canvas3D->Bind(EVT_GLTOOLBAR_DELETE_ALL, [this](SimpleEvent&) { reset(); });
-    canvas3D->Bind(EVT_GLTOOLBAR_ARRANGE, &priv::on_action_arrange, this);
-    canvas3D->Bind(EVT_GLTOOLBAR_MORE, &priv::on_action_more, this);
-    canvas3D->Bind(EVT_GLTOOLBAR_FEWER, &priv::on_action_fewer, this);
+    canvas3D->Bind(EVT_GLTOOLBAR_ARRANGE, [this](SimpleEvent&) { arrange(); });
+    canvas3D->Bind(EVT_GLTOOLBAR_MORE, [q](SimpleEvent&) { q->increase(); });
+    canvas3D->Bind(EVT_GLTOOLBAR_FEWER, [q](SimpleEvent&) { q->decrease(); });
     canvas3D->Bind(EVT_GLTOOLBAR_SPLIT, &priv::on_action_split, this);
     canvas3D->Bind(EVT_GLTOOLBAR_CUT, &priv::on_action_cut, this);
     canvas3D->Bind(EVT_GLTOOLBAR_SETTINGS, &priv::on_action_settings, this);
@@ -1161,8 +1156,8 @@ std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs &mode
 
     update();
     _3DScene::zoom_to_volumes(canvas3D);
-    // TODO
-    // $self->object_list_changed;
+    object_list_changed();
+
     // $self->schedule_background_process;
 
     return obj_idxs;
@@ -1388,21 +1383,6 @@ void Plater::priv::reset()
     update();
 }
 
-void Plater::priv::increase(size_t num)
-{
-    // TODO
-}
-
-void Plater::priv::decrease(size_t num)
-{
-    // TODO
-}
-
-void Plater::priv::set_number_of_copies()
-{
-    // TODO
-}
-
 void Plater::priv::rotate()
 {
     // TODO
@@ -1438,7 +1418,14 @@ void Plater::priv::scale()
 
 void Plater::priv::arrange()
 {
-    // TODO
+    // $self->stop_background_process;
+
+    main_frame->app_controller()->arrange_model();
+
+    // ignore arrange failures on purpose: user has visual feedback and we don't need to warn him
+    // when parts don't fit in print bed
+
+    update();
 }
 
 void Plater::priv::split_object()
@@ -1574,21 +1561,6 @@ void Plater::priv::on_action_add(SimpleEvent&)
     load_files(input_paths);
 }
 
-void Plater::priv::on_action_arrange(SimpleEvent&)
-{
-    // TODO
-}
-
-void Plater::priv::on_action_more(SimpleEvent&)
-{
-    // TODO
-}
-
-void Plater::priv::on_action_fewer(SimpleEvent&)
-{
-    // TODO
-}
-
 void Plater::priv::on_action_split(SimpleEvent&)
 {
     // TODO
@@ -1718,7 +1690,10 @@ Plater::~Plater()
 }
 
 Sidebar& Plater::sidebar() { return *p->sidebar; }
-Model&  Plater::model()  { return p->model; }
+Model& Plater::model()  { return p->model; }
+Print& Plater::print()  { return p->print; }
+
+void Plater::load_files(const std::vector<fs::path> &input_files) { p->load_files(input_files); }
 
 void Plater::update(bool force_autocenter) { p->update(force_autocenter); }
 void Plater::remove(size_t obj_idx) { p->remove(obj_idx); }
@@ -1731,7 +1706,69 @@ void Plater::remove_selected()
     }
 }
 
-void Plater::load_files(const std::vector<fs::path> &input_files) { p->load_files(input_files); }
+void Plater::increase(size_t num)
+{
+    const auto obj_idx = p->selected_object();
+    if (! obj_idx) { return; }
+
+    auto *model_object = p->model.objects[*obj_idx];
+    auto *model_instance = model_object->instances[model_object->instances.size() - 1];
+
+    // $self->stop_background_process;
+
+    float offset = 10.0;
+    for (size_t i = 0; i < num; i++, offset += 10.0) {
+        Vec3d offset_vec = model_instance->get_offset() + Vec3d(offset, offset, 0.0);
+        auto *new_instance = model_object->add_instance(offset_vec, model_instance->get_scaling_factor(), model_instance->get_rotation());
+        p->print.get_object(*obj_idx)->add_copy(Slic3r::to_2d(offset_vec));
+    }
+
+    sidebar().obj_list()->set_object_count(*obj_idx, model_object->instances.size());
+
+    if (p->get_config("autocenter") == "1") {
+        p->arrange();
+    } else {
+        p->update();
+    }
+
+    p->selection_changed();
+
+    // $self->schedule_background_process;
+}
+
+void Plater::decrease(size_t num)
+{
+    const auto obj_idx = p->selected_object();
+    if (! obj_idx) { return; }
+
+    auto *model_object = p->model.objects[*obj_idx];
+    if (model_object->instances.size() > num) {
+        for (size_t i = 0; i < num; i++) {
+            model_object->delete_last_instance();
+            p->print.get_object(*obj_idx)->delete_last_copy();
+        }
+        sidebar().obj_list()->set_object_count(*obj_idx, model_object->instances.size());
+    } else {
+        remove(*obj_idx);
+    }
+
+    p->update();
+}
+
+void Plater::set_number_of_copies(size_t num)
+{
+    const auto obj_idx = p->selected_object();
+    if (! obj_idx) { return; }
+
+    auto *model_object = p->model.objects[*obj_idx];
+
+    auto diff = (ptrdiff_t)num - (ptrdiff_t)model_object->instances.size();
+    if (diff > 0) {
+        increase(diff);
+    } else if (diff < 0) {
+        decrease(-diff);
+    }
+}
 
 fs::path Plater::export_gcode(const fs::path &output_path)
 {
diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp
index bcc295043..b25b43b7d 100644
--- a/src/slic3r/GUI/Plater.hpp
+++ b/src/slic3r/GUI/Plater.hpp
@@ -17,6 +17,7 @@ class wxGLCanvas;
 namespace Slic3r {
 
 class Model;
+class Print;
 
 namespace GUI {
 
@@ -92,13 +93,17 @@ public:
     ~Plater();
 
     Sidebar& sidebar();
-    Model&  model();
+    Model& model();
+    Print& print();
+
+    void load_files(const std::vector<boost::filesystem::path> &input_files);
 
     void update(bool force_autocenter = false);
     void remove(size_t obj_idx);
     void remove_selected();
-
-    void load_files(const std::vector<boost::filesystem::path> &input_files);
+    void increase(size_t num = 1);
+    void decrease(size_t num = 1);
+    void set_number_of_copies(size_t num);
 
     // Note: empty path means "use the default"
     boost::filesystem::path export_gcode(const boost::filesystem::path &output_path = boost::filesystem::path());