From ec75b760b8e7277ed90f0e4cd7113190db4607bf Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Mon, 9 Mar 2020 10:36:55 +0100
Subject: [PATCH 1/5] Fix of saving the 3DConnexion status on OSX. Fixes 2.2.0
 RC2 - 3Dconnexion settings not saved / Zoom digital (#3791)

---
 src/slic3r/GUI/Mouse3DController.cpp | 10 ++++++----
 src/slic3r/GUI/Mouse3DHandlerMac.mm  |  4 ++++
 2 files changed, 10 insertions(+), 4 deletions(-)

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;
 }
 

From e83fb4582c1de8477fd10d8675e3b0a1e5ef9767 Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Mon, 9 Mar 2020 10:56:51 +0100
Subject: [PATCH 2/5] Reworked the eject on unix systems to wait for the return
 of the system call to the command line utility and send out the unmount
 wxWidgets event immediately. Hopefully improves 2.2.0-RC Eject is very slow
 in Linux (#3795)

---
 src/slic3r/GUI/RemovableDriveManager.cpp | 10 ++++++++--
 src/slic3r/GUI/RemovableDriveManager.hpp |  4 ++++
 2 files changed, 12 insertions(+), 2 deletions(-)

diff --git a/src/slic3r/GUI/RemovableDriveManager.cpp b/src/slic3r/GUI/RemovableDriveManager.cpp
index 2264cc954..5054d061e 100644
--- a/src/slic3r/GUI/RemovableDriveManager.cpp
+++ b/src/slic3r/GUI/RemovableDriveManager.cpp
@@ -348,7 +348,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,12 +476,15 @@ 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) {
+#ifdef WIN32
 		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 {
+		} else 
+#endif // WIN32
+		{
 			assert(m_callback_evt_handler);
 			if (m_callback_evt_handler)
 				wxPostEvent(m_callback_evt_handler, RemovableDrivesChangedEvent(EVT_REMOVABLE_DRIVES_CHANGED));
diff --git a/src/slic3r/GUI/RemovableDriveManager.hpp b/src/slic3r/GUI/RemovableDriveManager.hpp
index 3b808d9d7..7ada13e5a 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 {
@@ -102,7 +104,9 @@ private:
 	// 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
+#ifdef WIN32
 	DriveData 				m_drive_data_last_eject;
+#endif // WIN32
 	mutable tbb::mutex 		m_drives_mutex;
 
 	// Returns drive path (same as path in DriveData) if exists otherwise empty string.

From aac691c0e7aa19e697d00ac0e92bf4bdb8d121f5 Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Mon, 9 Mar 2020 11:47:20 +0100
Subject: [PATCH 3/5] Forced SD card / USB drive eject now sends out the event
 immediately on Windows as well as on Unix platforms including OSX.

---
 src/slic3r/GUI/RemovableDriveManager.cpp | 21 +++++++--------------
 src/slic3r/GUI/RemovableDriveManager.hpp |  6 ------
 2 files changed, 7 insertions(+), 20 deletions(-)

diff --git a/src/slic3r/GUI/RemovableDriveManager.cpp b/src/slic3r/GUI/RemovableDriveManager.cpp
index 5054d061e..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);
 	}
 }
 
@@ -476,19 +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) {
-#ifdef WIN32
-		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 
-#endif // WIN32
-		{
-			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 7ada13e5a..8b661d19c 100644
--- a/src/slic3r/GUI/RemovableDriveManager.hpp
+++ b/src/slic3r/GUI/RemovableDriveManager.hpp
@@ -101,12 +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
-#ifdef WIN32
-	DriveData 				m_drive_data_last_eject;
-#endif // WIN32
 	mutable tbb::mutex 		m_drives_mutex;
 
 	// Returns drive path (same as path in DriveData) if exists otherwise empty string.

From cbd80685c28398d0f8174f6c3a707ba1d5b7aa08 Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Mon, 9 Mar 2020 12:29:40 +0100
Subject: [PATCH 4/5] Fix of Print settings dropdown list messed up after
 deleting an entry (#3800)

---
 src/slic3r/GUI/Tab.cpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

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(

From c489663126828fde0d27ac405aad9f3c82f135d8 Mon Sep 17 00:00:00 2001
From: tamasmeszaros <meszaros.q@gmail.com>
Date: Mon, 9 Mar 2020 14:05:54 +0100
Subject: [PATCH 5/5] Add arrange support for unprintable items

---
 src/slic3r/GUI/Plater.cpp | 71 +++++++++++++++++++++++++++++----------
 1 file changed, 53 insertions(+), 18 deletions(-)

diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index 48eb86743..e1f75d07d 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();
         }
@@ -2838,16 +2868,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! "