diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp
index 8bc9744f1..0cd39b092 100644
--- a/src/slic3r/GUI/Mouse3DController.cpp
+++ b/src/slic3r/GUI/Mouse3DController.cpp
@@ -368,6 +368,8 @@ void Mouse3DController::render_settings_dialog(GLCanvas3D& canvas) const
 
 void Mouse3DController::connected(std::string device_name)
 {
+    assert(! m_connected);
+    assert(m_device_str.empty());
 	m_device_str = device_name;
     // Copy the parameters for m_device_str into the current parameters.
     if (auto it_params = m_params_by_device.find(m_device_str); it_params != m_params_by_device.end()) {
@@ -380,13 +382,13 @@ void Mouse3DController::connected(std::string device_name)
 void Mouse3DController::disconnected()
 {
     // Copy the current parameters for m_device_str into the parameter database.
-    assert(! m_device_str.empty());
-    if (! m_device_str.empty()) {
+    assert(m_connected == ! m_device_str.empty());
+    if (m_connected) {
         tbb::mutex::scoped_lock lock(m_params_ui_mutex);
         m_params_by_device[m_device_str] = m_params_ui;
+	    m_device_str.clear();
+	    m_connected = false;
     }
-    m_device_str.clear();
-    m_connected = false;
 }
 
 bool Mouse3DController::handle_input(const DataPacketAxis& packet)
diff --git a/src/slic3r/GUI/Mouse3DHandlerMac.mm b/src/slic3r/GUI/Mouse3DHandlerMac.mm
index d40948cd1..eaf580908 100644
--- a/src/slic3r/GUI/Mouse3DHandlerMac.mm
+++ b/src/slic3r/GUI/Mouse3DHandlerMac.mm
@@ -152,6 +152,8 @@ static void DeviceAdded(uint32_t unused)
 static void DeviceRemoved(uint32_t unused)
 {
   BOOST_LOG_TRIVIAL(info) << "3dx device removed\n";
+  assert(m_connected);
+  assert(! m_device_str.empty());
   mouse_3d_controller->disconnected();
 }
 
@@ -214,6 +216,8 @@ void Mouse3DController::shutdown()
     CleanupConnexionHandlers();
     unload_driver();
   }
+  // Copy the current parameters to parameter database, mark the device as disconnected.
+  this->disconnected();
   mouse_3d_controller = nullptr;
 }
 
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index c20764067..d7d6f419d 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -1509,6 +1509,7 @@ struct Plater::priv
             ret.poly.contour = std::move(p);
             ret.translation  = scaled(m_pos);
             ret.rotation     = m_rotation;
+            ret.priority++;
             return ret;
         }
     } wipetower;
@@ -1572,18 +1573,23 @@ struct Plater::priv
         // the current bed width.
         static const constexpr double LOGICAL_BED_GAP = 1. / 5.;
 
-        ArrangePolygons m_selected, m_unselected;
+        ArrangePolygons m_selected, m_unselected, m_unprintable;
 
         // clear m_selected and m_unselected, reserve space for next usage
         void clear_input() {
             const Model &model = plater().model;
 
-            size_t count = 0; // To know how much space to reserve
-            for (auto obj : model.objects) count += obj->instances.size();
+            size_t count = 0, cunprint = 0; // To know how much space to reserve
+            for (auto obj : model.objects)
+                for (auto mi : obj->instances)
+                    mi->printable ? count++ : cunprint++;
+            
             m_selected.clear();
             m_unselected.clear();
+            m_unprintable.clear();
             m_selected.reserve(count + 1 /* for optional wti */);
             m_unselected.reserve(count + 1 /* for optional wti */);
+            m_unprintable.reserve(cunprint /* for optional wti */);
         }
 
         // Stride between logical beds
@@ -1612,8 +1618,10 @@ struct Plater::priv
             clear_input();
 
             for (ModelObject *obj: plater().model.objects)
-                for (ModelInstance *mi : obj->instances)
-                    m_selected.emplace_back(get_arrange_poly(mi));
+                for (ModelInstance *mi : obj->instances) {
+                    ArrangePolygons & cont = mi->printable ? m_selected : m_unprintable;
+                    cont.emplace_back(get_arrange_poly(mi));
+                }
 
             auto& wti = plater().updated_wipe_tower();
             if (wti) m_selected.emplace_back(get_arrange_poly(&wti));
@@ -1648,9 +1656,12 @@ struct Plater::priv
                 for (size_t i = 0; i < inst_sel.size(); ++i) {
                     ArrangePolygon &&ap = get_arrange_poly(mo->instances[i]);
 
-                    inst_sel[i] ?
-                        m_selected.emplace_back(std::move(ap)) :
-                        m_unselected.emplace_back(std::move(ap));
+                    ArrangePolygons &cont = mo->instances[i]->printable ?
+                                                (inst_sel[i] ? m_selected :
+                                                               m_unselected) :
+                                                m_unprintable;
+                    
+                    cont.emplace_back(std::move(ap));
                 }
             }
 
@@ -1682,16 +1693,35 @@ struct Plater::priv
     public:
         using PlaterJob::PlaterJob;
 
-        int status_range() const override { return int(m_selected.size()); }
+        int status_range() const override
+        {
+            return int(m_selected.size() + m_unprintable.size());
+        }
 
         void process() override;
 
         void finalize() override {
             // Ignore the arrange result if aborted.
             if (was_canceled()) return;
-
+            
+            // Unprintable items go to the last virtual bed
+            int beds = 0;
+            
             // Apply the arrange result to all selected objects
-            for (ArrangePolygon &ap : m_selected) ap.apply();
+            for (ArrangePolygon &ap : m_selected) {
+                beds = std::max(ap.bed_idx, beds);
+                ap.apply();
+            }
+            
+            // Get the virtual beds from the unselected items
+            for (ArrangePolygon &ap : m_unselected)
+                beds = std::max(ap.bed_idx, beds);
+            
+            // Move the unprintable items to the last virtual bed.
+            for (ArrangePolygon &ap : m_unprintable) {
+                ap.bed_idx += beds + 1;
+                ap.apply();
+            }
 
             plater().update();
         }
@@ -2846,16 +2876,21 @@ void Plater::priv::ArrangeJob::process() {
     }
 
     coord_t min_d = scaled(dist);
-    auto count = unsigned(m_selected.size());
+    auto count = unsigned(m_selected.size() + m_unprintable.size());
     arrangement::BedShapeHint bedshape = plater().get_bed_shape_hint();
-
+    
+    auto stopfn = [this]() { return was_canceled(); };
+    
     try {
         arrangement::arrange(m_selected, m_unselected, min_d, bedshape,
-                             [this, count](unsigned st) {
-                                 if (st > 0) // will not finalize after last one
-                                    update_status(int(count - st), arrangestr);
-                             },
-                             [this]() { return was_canceled(); });
+            [this, count](unsigned st) {
+                st += m_unprintable.size();
+                if (st > 0) update_status(int(count - st), arrangestr);
+            }, stopfn);
+        arrangement::arrange(m_unprintable, {}, min_d, bedshape,
+            [this, count](unsigned st) {
+                if (st > 0) update_status(int(count - st), arrangestr);
+            }, stopfn);
     } catch (std::exception & /*e*/) {
         GUI::show_error(plater().q,
                         _(L("Could not arrange model objects! "
diff --git a/src/slic3r/GUI/RemovableDriveManager.cpp b/src/slic3r/GUI/RemovableDriveManager.cpp
index 2264cc954..0c4c417b2 100644
--- a/src/slic3r/GUI/RemovableDriveManager.cpp
+++ b/src/slic3r/GUI/RemovableDriveManager.cpp
@@ -103,7 +103,10 @@ void RemovableDriveManager::eject_drive()
 			return;
 		}
 		CloseHandle(handle);
-		m_drive_data_last_eject = *it_drive_data;
+		assert(m_callback_evt_handler);
+		if (m_callback_evt_handler) 
+			wxPostEvent(m_callback_evt_handler, RemovableDriveEjectEvent(EVT_REMOVABLE_DRIVE_EJECTED, std::move(*it_drive_data)));
+		m_current_drives.erase(it_drive_data);
 	}
 }
 
@@ -348,7 +351,10 @@ void RemovableDriveManager::eject_drive()
 			return;
 		}
 
-		m_drive_data_last_eject = *it_drive_data;
+		assert(m_callback_evt_handler);
+		if (m_callback_evt_handler) 
+			wxPostEvent(m_callback_evt_handler, RemovableDriveEjectEvent(EVT_REMOVABLE_DRIVE_EJECTED, std::move(*it_drive_data)));
+		m_current_drives.erase(it_drive_data);
 	}
 }
 
@@ -473,16 +479,9 @@ void RemovableDriveManager::update()
 	tbb::mutex::scoped_lock lock(m_drives_mutex);
 	std::sort(current_drives.begin(), current_drives.end());
 	if (current_drives != m_current_drives) {
-		if (! m_drive_data_last_eject.empty() && std::find(current_drives.begin(), current_drives.end(), m_drive_data_last_eject) == current_drives.end()) {
-			assert(m_callback_evt_handler);
-			if (m_callback_evt_handler) 
-				wxPostEvent(m_callback_evt_handler, RemovableDriveEjectEvent(EVT_REMOVABLE_DRIVE_EJECTED, std::move(m_drive_data_last_eject)));
-			m_drive_data_last_eject.clear();
-		} else {
-			assert(m_callback_evt_handler);
-			if (m_callback_evt_handler)
-				wxPostEvent(m_callback_evt_handler, RemovableDrivesChangedEvent(EVT_REMOVABLE_DRIVES_CHANGED));
-		}
+		assert(m_callback_evt_handler);
+		if (m_callback_evt_handler)
+			wxPostEvent(m_callback_evt_handler, RemovableDrivesChangedEvent(EVT_REMOVABLE_DRIVES_CHANGED));
 	}
 	m_current_drives = std::move(current_drives);
 }
diff --git a/src/slic3r/GUI/RemovableDriveManager.hpp b/src/slic3r/GUI/RemovableDriveManager.hpp
index 3b808d9d7..8b661d19c 100644
--- a/src/slic3r/GUI/RemovableDriveManager.hpp
+++ b/src/slic3r/GUI/RemovableDriveManager.hpp
@@ -65,6 +65,8 @@ public:
 	// Verify whether the path provided is on removable media. If so, save the path for further eject and return true, otherwise return false.
 	bool 		set_and_verify_last_save_path(const std::string &path);
 	// Eject drive of a file set by set_and_verify_last_save_path().
+	// On Unix / OSX, the function blocks and sends out the EVT_REMOVABLE_DRIVE_EJECTED event on success.
+	// On Windows, the function does not block, and the eject is detected in the background thread.
 	void 		eject_drive();
 
 	struct RemovableDrivesStatus {
@@ -99,10 +101,6 @@ private:
 	// m_current_drives is guarded by m_drives_mutex
 	// sorted ascending by path
 	std::vector<DriveData> 	m_current_drives;
-	// When user requested an eject, the drive to be forcefuly ejected is stored here, so the next update will
-	// recognize that the eject was finished with success and an eject event is sent out.
-	// guarded with m_drives_mutex
-	DriveData 				m_drive_data_last_eject;
 	mutable tbb::mutex 		m_drives_mutex;
 
 	// Returns drive path (same as path in DriveData) if exists otherwise empty string.
diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp
index 9a6313e70..9e49dc5bd 100644
--- a/src/slic3r/GUI/Tab.cpp
+++ b/src/slic3r/GUI/Tab.cpp
@@ -2861,10 +2861,10 @@ void Tab::select_preset(std::string preset_name, bool delete_current)
         // 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.
-        auto update_compatible_type = [](bool technology_changed, bool on_page, bool show_incompatible_presets) {
-        	return technology_changed ? PresetSelectCompatibleType::Always :
-        	       on_page            ? PresetSelectCompatibleType::Never  : 
-        	       (show_incompatible_presets ? PresetSelectCompatibleType::OnlyIfWasCompatible : PresetSelectCompatibleType::Always);
+        auto update_compatible_type = [delete_current](bool technology_changed, bool on_page, bool show_incompatible_presets) {
+        	return (delete_current || technology_changed) ? PresetSelectCompatibleType::Always :
+        	       on_page                                ? PresetSelectCompatibleType::Never  :
+        	       show_incompatible_presets              ? PresetSelectCompatibleType::OnlyIfWasCompatible : PresetSelectCompatibleType::Always;
         };
         if (current_dirty || delete_current || print_tab || printer_tab)
             m_preset_bundle->update_compatible(