From 441cf62ad374ee2c4a7eaec0e4a57c2ba666e9f4 Mon Sep 17 00:00:00 2001
From: David Kocik <kocikdav@gmail.com>
Date: Mon, 22 Mar 2021 22:36:09 +0100
Subject: [PATCH 1/8] fix of notification states and upload progress bar
 notification fadeout

---
 src/slic3r/GUI/NotificationManager.cpp | 33 ++++++++++++++++++--------
 src/slic3r/GUI/NotificationManager.hpp |  3 +++
 2 files changed, 26 insertions(+), 10 deletions(-)

diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp
index e83a0014c..533bcb8dc 100644
--- a/src/slic3r/GUI/NotificationManager.cpp
+++ b/src/slic3r/GUI/NotificationManager.cpp
@@ -169,13 +169,16 @@ void NotificationManager::PopNotification::render(GLCanvas3D& canvas, float init
 	imgui.set_next_window_pos(win_pos.x, win_pos.y, ImGuiCond_Always, 1.0f, 0.0f);
 	imgui.set_next_window_size(m_window_width, m_window_height, ImGuiCond_Always);
 
-	// find if hovered
-	if (m_state == EState::Hovered) 
-		m_state = EState::Shown;
-
+	
+	// find if hovered FIXME:  do it only in update state?
+	if (m_state == EState::Hovered) {
+		m_state = EState::Unknown;
+		init(); 
+	}
+	
 	if (mouse_pos.x < win_pos.x && mouse_pos.x > win_pos.x - m_window_width && mouse_pos.y > win_pos.y && mouse_pos.y < win_pos.y + m_window_height) {
 		ImGui::SetNextWindowFocus();
-		m_state = EState::Hovered;
+		set_hovered();
 	}
 	
 	// color change based on fading out
@@ -300,8 +303,8 @@ void NotificationManager::PopNotification::init()
 	if (m_lines_count == 3)
 		m_multiline = true;
 	m_notification_start = GLCanvas3D::timestamp_now();
-	//if (m_state != EState::Hidden)
-	//	m_state = EState::Shown;
+	if (m_state == EState::Unknown)
+		m_state = EState::Shown;
 }
 void NotificationManager::PopNotification::set_next_window_size(ImGuiWrapper& imgui)
 { 
@@ -579,9 +582,10 @@ bool NotificationManager::PopNotification::update_state(bool paused, const int64
 	// reset timers - hovered state is set in render 
 	if (m_state == EState::Hovered) { 
 		m_current_fade_opacity = 1.0f;
-		m_notification_start = now;
+		m_state = EState::Unknown;
+		init();
 	// Timers when not fading
-	} else if (m_state != EState::FadingOut && m_data.duration != 0 && !paused) {
+	} else if (m_state != EState::NotFading && m_state != EState::FadingOut && m_data.duration != 0 && !paused) {
 		int64_t up_time = now - m_notification_start;
 		if (up_time >= m_data.duration * 1000) {
 			m_state					= EState::FadingOut;
@@ -787,6 +791,8 @@ void NotificationManager::ProgressBarNotification::init()
 	PopNotification::init();
 	m_lines_count++;
 	m_endlines.push_back(m_endlines.back());
+	if(m_state == EState::Shown)
+		m_state = EState::NotFading;
 }
 void NotificationManager::ProgressBarNotification::count_spaces()
 {
@@ -826,12 +832,19 @@ void NotificationManager::ProgressBarNotification::render_bar(ImGuiWrapper& imgu
 	ImGui::GetWindowDrawList()->AddLine(lineStart, midPoint, IM_COL32((int)(orange_color.x * 255), (int)(orange_color.y * 255), (int)(orange_color.z * 255), (1.0f * 255.f)), m_line_height * 0.2f);
 }
 //------PrintHostUploadNotification----------------
+void NotificationManager::PrintHostUploadNotification::init()
+{
+	ProgressBarNotification::init();
+	if (m_state == EState::NotFading && m_uj_state == UploadJobState::PB_COMPLETED)
+		m_state = EState::Shown;
+}
 void NotificationManager::PrintHostUploadNotification::set_percentage(float percent)
 {
 	m_percentage = percent;
 	if (percent >= 1.0f) {
 		m_uj_state = UploadJobState::PB_COMPLETED;
 		m_has_cancel_button = false;
+		init();
 	} else if (percent < 0.0f) {
 		error();
 	} else {
@@ -1123,7 +1136,7 @@ void NotificationManager::push_exporting_finished_notification(const std::string
 void  NotificationManager::push_upload_job_notification(int id, float filesize, const std::string& filename, const std::string& host, float percentage)
 {
 	std::string text = PrintHostUploadNotification::get_upload_job_text(id, filename, host);
-	NotificationData data{ NotificationType::PrintHostUpload, NotificationLevel::ProgressBarNotification, 0, text };
+	NotificationData data{ NotificationType::PrintHostUpload, NotificationLevel::ProgressBarNotification, 10, text };
 	push_notification_data(std::make_unique<NotificationManager::PrintHostUploadNotification>(data, m_id_provider, m_evt_handler, 0, id, filesize), 0);
 }
 void NotificationManager::set_upload_job_notification_percentage(int id, const std::string& filename, const std::string& host, float percentage)
diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp
index 4f3900aeb..4224694c4 100644
--- a/src/slic3r/GUI/NotificationManager.hpp
+++ b/src/slic3r/GUI/NotificationManager.hpp
@@ -207,6 +207,7 @@ private:
 			Unknown,		  // NOT initialized
 			Hidden,
 			Shown,			  // Requesting Render at some time if duration != 0
+			NotFading,		  // Never jumps to state Fading out even if duration says so
 			FadingOut,        // Requesting Render at some time
 			ClosePending,     // Requesting Render
 			Finished,         // Requesting Render
@@ -237,6 +238,7 @@ private:
 		int64_t 		       next_render() const { return is_finished() ? 0 : m_next_render; }
 		EState                 get_state()  const { return m_state; }
 		bool				   is_hovered() const { return m_state == EState::Hovered; } 
+		void				   set_hovered() { if (m_state != EState::Finished || m_state != EState::ClosePending || m_state != EState::Hidden || m_state != EState::Unknown) m_state = EState::Hovered; }
 	
 		// Call after every size change
 		virtual void init();
@@ -401,6 +403,7 @@ private:
 		{
 			m_has_cancel_button = true;
 		}
+		virtual void        init() override;
 		static std::string	get_upload_job_text(int id, const std::string& filename, const std::string& host) { return "[" + std::to_string(id) + "] " + filename + " -> " + host; }
 		virtual void		set_percentage(float percent) override;
 		void				cancel() { m_uj_state = UploadJobState::PB_CANCELLED; m_has_cancel_button = false; }

From 78e61eddf80830749de2d472b75a7dbe3043e2bd Mon Sep 17 00:00:00 2001
From: David Kocik <kocikdav@gmail.com>
Date: Tue, 23 Mar 2021 09:43:09 +0100
Subject: [PATCH 2/8] typo fix

---
 src/slic3r/GUI/NotificationManager.hpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp
index 4224694c4..95532c94e 100644
--- a/src/slic3r/GUI/NotificationManager.hpp
+++ b/src/slic3r/GUI/NotificationManager.hpp
@@ -238,7 +238,7 @@ private:
 		int64_t 		       next_render() const { return is_finished() ? 0 : m_next_render; }
 		EState                 get_state()  const { return m_state; }
 		bool				   is_hovered() const { return m_state == EState::Hovered; } 
-		void				   set_hovered() { if (m_state != EState::Finished || m_state != EState::ClosePending || m_state != EState::Hidden || m_state != EState::Unknown) m_state = EState::Hovered; }
+		void				   set_hovered() { if (m_state != EState::Finished && m_state != EState::ClosePending && m_state != EState::Hidden && m_state != EState::Unknown) m_state = EState::Hovered; }
 	
 		// Call after every size change
 		virtual void init();

From 9118de4e3ce0d14451eadf816117ddc333a4327c Mon Sep 17 00:00:00 2001
From: David Kocik <kocikdav@gmail.com>
Date: Tue, 23 Mar 2021 14:46:55 +0100
Subject: [PATCH 3/8] Upload notification text fix

---
 src/slic3r/GUI/NotificationManager.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp
index 533bcb8dc..6c5918fde 100644
--- a/src/slic3r/GUI/NotificationManager.cpp
+++ b/src/slic3r/GUI/NotificationManager.cpp
@@ -859,7 +859,7 @@ void NotificationManager::PrintHostUploadNotification::render_bar(ImGuiWrapper&
 	case Slic3r::GUI::NotificationManager::PrintHostUploadNotification::UploadJobState::PB_PROGRESS:
 	{
 		ProgressBarNotification::render_bar(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
-		float uploaded = m_file_size / 100 * m_percentage;
+		float uploaded = m_file_size * m_percentage;
 		std::stringstream stream;
 		stream << std::fixed << std::setprecision(2) << (int)(m_percentage * 100) << "% - " << uploaded << " of " << m_file_size << "MB uploaded";
 		text = stream.str();

From c140974bf40b34345191a79d85410ea62898c2b7 Mon Sep 17 00:00:00 2001
From: David Kocik <kocikdav@gmail.com>
Date: Fri, 26 Mar 2021 09:18:07 +0100
Subject: [PATCH 4/8] Changed ToolpathOuside error notification from plater to
 slicing error  notification type so it is grayed out correctly

---
 src/slic3r/GUI/GLCanvas3D.cpp          | 40 ++++++++++++++++++--------
 src/slic3r/GUI/NotificationManager.cpp |  8 ++++++
 src/slic3r/GUI/NotificationManager.hpp |  1 +
 3 files changed, 37 insertions(+), 12 deletions(-)

diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index 2149f21e7..fa3d96420 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -6314,31 +6314,47 @@ std::vector<float> GLCanvas3D::_parse_colors(const std::vector<std::string>& col
 #if ENABLE_WARNING_TEXTURE_REMOVAL
 void GLCanvas3D::_set_warning_notification(EWarning warning, bool state)
 {
+    enum ErrorType{
+        PLATER_WARNING,
+        PLATER_ERROR,
+        SLICING_ERROR
+    };
     std::string text;
-    bool error = false;
+    ErrorType error = ErrorType::PLATER_WARNING;
     switch (warning) {
     case EWarning::ObjectOutside:      text = _u8L("An object outside the print area was detected."); break;
-    case EWarning::ToolpathOutside:    text = _u8L("A toolpath outside the print area was detected."); error = true; break;
-    case EWarning::SlaSupportsOutside: text = _u8L("SLA supports outside the print area were detected."); error = true; break;
+    case EWarning::ToolpathOutside:    text = _u8L("A toolpath outside the print area was detected."); error = ErrorType::SLICING_ERROR; break;
+    case EWarning::SlaSupportsOutside: text = _u8L("SLA supports outside the print area were detected."); error = ErrorType::PLATER_ERROR; break;
     case EWarning::SomethingNotShown:  text = _u8L("Some objects are not visible."); break;
     case EWarning::ObjectClashed:
         text = _u8L("An object outside the print area was detected.\n"
             "Resolve the current problem to continue slicing.");
-        error = true;
+        error = ErrorType::PLATER_ERROR;
         break;
 }
     auto& notification_manager = *wxGetApp().plater()->get_notification_manager();
-    if (state) {
-        if (error)
-            notification_manager.push_plater_error_notification(text);
-        else
+    switch (error)
+    {
+    case PLATER_WARNING:
+        if (state)
             notification_manager.push_plater_warning_notification(text);
-    }
-    else {
-        if (error)
-            notification_manager.close_plater_error_notification(text);
         else
             notification_manager.close_plater_warning_notification(text);
+        break;
+    case PLATER_ERROR:
+        if (state)
+            notification_manager.push_plater_error_notification(text);
+        else
+            notification_manager.close_plater_error_notification(text);
+        break;
+    case SLICING_ERROR:
+        if (state)
+            notification_manager.push_slicing_error_notification(text);
+        else
+            notification_manager.close_slicing_error_notification(text);
+        break;
+    default:
+        break;
     }
 }
 #else
diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp
index 6c5918fde..144c89a1e 100644
--- a/src/slic3r/GUI/NotificationManager.cpp
+++ b/src/slic3r/GUI/NotificationManager.cpp
@@ -1072,6 +1072,14 @@ void NotificationManager::close_slicing_errors_and_warnings()
 		}
 	}
 }
+void NotificationManager::close_slicing_error_notification(const std::string& text)
+{
+	for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
+		if (notification->get_type() == NotificationType::SlicingError && notification->compare_text(_u8L("ERROR:") + "\n" + text)) {
+			notification->close();
+		}
+	}
+}
 void NotificationManager::push_slicing_complete_notification(int timestamp, bool large)
 {
 	std::string hypertext;
diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp
index 95532c94e..74ebedde2 100644
--- a/src/slic3r/GUI/NotificationManager.hpp
+++ b/src/slic3r/GUI/NotificationManager.hpp
@@ -125,6 +125,7 @@ public:
 //	void set_slicing_warning_gray(const std::string& text, bool g);
 	// immediately stops showing slicing errors
 	void close_slicing_errors_and_warnings();
+	void close_slicing_error_notification(const std::string& text);
 	// Release those slicing warnings, which refer to an ObjectID, which is not in the list.
 	// living_oids is expected to be sorted.
 	void remove_slicing_warnings_of_released_objects(const std::vector<ObjectID>& living_oids);

From fb645c63941743a8752b216bb04a3da465c8efca Mon Sep 17 00:00:00 2001
From: David Kocik <kocikdav@gmail.com>
Date: Mon, 29 Mar 2021 18:25:44 +0200
Subject: [PATCH 5/8] two line text for upload progress bar notification

---
 src/slic3r/GUI/NotificationManager.cpp | 56 ++++++++++++++++++--------
 1 file changed, 40 insertions(+), 16 deletions(-)

diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp
index 144c89a1e..980927d98 100644
--- a/src/slic3r/GUI/NotificationManager.cpp
+++ b/src/slic3r/GUI/NotificationManager.cpp
@@ -327,8 +327,8 @@ void NotificationManager::PopNotification::render_text(ImGuiWrapper& imgui, cons
 		if (m_multiline) {
 			
 			int last_end = 0;
-			float starting_y = m_line_height/2;//10;
-			float shift_y = m_line_height;// -m_line_height / 20;
+			float starting_y = m_line_height/2;
+			float shift_y = m_line_height;
 			for (size_t i = 0; i < m_lines_count; i++) {
 			    std::string line = m_text1.substr(last_end , m_endlines[i] - last_end);
 				if(i < m_lines_count - 1)
@@ -789,8 +789,16 @@ bool NotificationManager::ExportFinishedNotification::on_text_click()
 void NotificationManager::ProgressBarNotification::init()
 {
 	PopNotification::init();
-	m_lines_count++;
-	m_endlines.push_back(m_endlines.back());
+	//m_lines_count++;
+	if(m_lines_count >= 2) {
+		m_lines_count = 3;
+		m_multiline = true;
+		while (m_endlines.size() < 3)
+			m_endlines.push_back(m_endlines.back());
+	} else {
+		m_lines_count = 2;
+		m_endlines.push_back(m_endlines.back());
+	}
 	if(m_state == EState::Shown)
 		m_state = EState::NotFading;
 }
@@ -813,20 +821,36 @@ void NotificationManager::ProgressBarNotification::count_spaces()
 void NotificationManager::ProgressBarNotification::render_text(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
 {
 	// line1 - we do not print any more text than what fits on line 1. Line 2 is bar.
-	ImGui::SetCursorPosX(m_left_indentation);
-	ImGui::SetCursorPosY(win_size_y / 2 - win_size_y / 6 - m_line_height / 2);
-	imgui.text(m_text1.substr(0, m_endlines[0]).c_str());
-	if (m_has_cancel_button)
-	render_cancel_button(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
-	render_bar(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
+	if (m_multiline) {
+		// two lines text, one line bar
+		ImGui::SetCursorPosX(m_left_indentation);
+		ImGui::SetCursorPosY(m_line_height / 4);
+		imgui.text(m_text1.substr(0, m_endlines[0]).c_str());
+		ImGui::SetCursorPosX(m_left_indentation);
+		ImGui::SetCursorPosY(m_line_height + m_line_height / 4);
+		std::string line = m_text1.substr(m_endlines[0] + (m_text1[m_endlines[0]] == '\n' || m_text1[m_endlines[0]] == ' ' ? 1 : 0), m_endlines[1] - m_endlines[0] - (m_text1[m_endlines[0]] == '\n' || m_text1[m_endlines[0]] == ' ' ? 1 : 0));
+		imgui.text(line.c_str());
+		if (m_has_cancel_button)
+			render_cancel_button(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
+		render_bar(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
+	} else {
+		//one line text, one line bar
+		ImGui::SetCursorPosX(m_left_indentation);
+		ImGui::SetCursorPosY(/*win_size_y / 2 - win_size_y / 6 -*/ m_line_height / 4);
+		imgui.text(m_text1.substr(0, m_endlines[0]).c_str());
+		if (m_has_cancel_button)
+			render_cancel_button(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
+		render_bar(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
+	}
+
 	
 }
 void NotificationManager::ProgressBarNotification::render_bar(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
 {
 	ImVec4 orange_color			= ImVec4(.99f, .313f, .0f, 1.0f);
 	ImVec4 gray_color			= ImVec4(.34f, .34f, .34f, 1.0f);
-	ImVec2 lineEnd				= ImVec2(win_pos_x - m_window_width_offset, win_pos_y + win_size_y / 2 + m_line_height / 4);
-	ImVec2 lineStart			= ImVec2(win_pos_x - win_size_x + m_left_indentation, win_pos_y + win_size_y / 2 + m_line_height / 4);
+	ImVec2 lineEnd				= ImVec2(win_pos_x - m_window_width_offset, win_pos_y + win_size_y / 2 + (m_multiline ? m_line_height / 2 : 0));
+	ImVec2 lineStart			= ImVec2(win_pos_x - win_size_x + m_left_indentation, win_pos_y + win_size_y / 2 + (m_multiline ? m_line_height / 2 : 0));
 	ImVec2 midPoint				= ImVec2(lineStart.x + (lineEnd.x - lineStart.x) * m_percentage, lineStart.y);
 	ImGui::GetWindowDrawList()->AddLine(lineStart, lineEnd, IM_COL32((int)(gray_color.x * 255), (int)(gray_color.y * 255), (int)(gray_color.z * 255), (1.0f * 255.f)), m_line_height * 0.2f);
 	ImGui::GetWindowDrawList()->AddLine(lineStart, midPoint, IM_COL32((int)(orange_color.x * 255), (int)(orange_color.y * 255), (int)(orange_color.z * 255), (1.0f * 255.f)), m_line_height * 0.2f);
@@ -864,23 +888,23 @@ void NotificationManager::PrintHostUploadNotification::render_bar(ImGuiWrapper&
 		stream << std::fixed << std::setprecision(2) << (int)(m_percentage * 100) << "% - " << uploaded << " of " << m_file_size << "MB uploaded";
 		text = stream.str();
 		ImGui::SetCursorPosX(m_left_indentation);
-		ImGui::SetCursorPosY(win_size_y / 2 + win_size_y / 6 /*- m_line_height / 4 * 3*/);
+		ImGui::SetCursorPosY(win_size_y / 2 + win_size_y / 6 - (m_multiline ? 0 : m_line_height / 4));
 		break;
 	}
 	case Slic3r::GUI::NotificationManager::PrintHostUploadNotification::UploadJobState::PB_ERROR:
 		text = _u8L("ERROR");
 		ImGui::SetCursorPosX(m_left_indentation);
-		ImGui::SetCursorPosY(win_size_y / 2 + win_size_y / 6 - m_line_height / 2);
+		ImGui::SetCursorPosY(win_size_y / 2 + win_size_y / 6 - (m_multiline ? m_line_height / 4 : m_line_height / 2));
 		break;
 	case Slic3r::GUI::NotificationManager::PrintHostUploadNotification::UploadJobState::PB_CANCELLED:
 		text = _u8L("CANCELED");
 		ImGui::SetCursorPosX(m_left_indentation);
-		ImGui::SetCursorPosY(win_size_y / 2 + win_size_y / 6 - m_line_height / 2);
+		ImGui::SetCursorPosY(win_size_y / 2 + win_size_y / 6 - (m_multiline ? m_line_height / 4 : m_line_height / 2));
 		break;
 	case Slic3r::GUI::NotificationManager::PrintHostUploadNotification::UploadJobState::PB_COMPLETED:
 		text = _u8L("COMPLETED");
 		ImGui::SetCursorPosX(m_left_indentation);
-		ImGui::SetCursorPosY(win_size_y / 2 + win_size_y / 6 - m_line_height / 2);
+		ImGui::SetCursorPosY(win_size_y / 2 + win_size_y / 6 - (m_multiline ? m_line_height / 4 : m_line_height / 2));
 		break;
 	}
 	

From d7b385f14455cde0928e7bea90a91612afb1554d Mon Sep 17 00:00:00 2001
From: David Kocik <kocikdav@gmail.com>
Date: Tue, 30 Mar 2021 21:03:59 +0200
Subject: [PATCH 6/8] compare upload notification by id and not show id in text

---
 src/slic3r/GUI/NotificationManager.cpp | 44 +++++++++++++++++---------
 src/slic3r/GUI/NotificationManager.hpp |  6 ++--
 2 files changed, 33 insertions(+), 17 deletions(-)

diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp
index 980927d98..005b3c11f 100644
--- a/src/slic3r/GUI/NotificationManager.cpp
+++ b/src/slic3r/GUI/NotificationManager.cpp
@@ -552,7 +552,7 @@ void NotificationManager::PopNotification::update(const NotificationData& n)
     m_text2          = n.text2;
 	init();
 }
-bool NotificationManager::PopNotification::compare_text(const std::string& text)
+bool NotificationManager::PopNotification::compare_text(const std::string& text) const 
 {
 	std::wstring wt1 = boost::nowide::widen(m_text1);
 	std::wstring wt2 = boost::nowide::widen(text);
@@ -1167,39 +1167,53 @@ void NotificationManager::push_exporting_finished_notification(const std::string
 
 void  NotificationManager::push_upload_job_notification(int id, float filesize, const std::string& filename, const std::string& host, float percentage)
 {
+	// find if upload with same id was not already in notification
+	// done by compare_jon_id not compare_text thus has to be performed here
+	for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
+		if (notification->get_type() == NotificationType::PrintHostUpload && dynamic_cast<PrintHostUploadNotification*>(notification.get())->compare_job_id(id)) {
+			return;
+		}
+	}
 	std::string text = PrintHostUploadNotification::get_upload_job_text(id, filename, host);
 	NotificationData data{ NotificationType::PrintHostUpload, NotificationLevel::ProgressBarNotification, 10, text };
 	push_notification_data(std::make_unique<NotificationManager::PrintHostUploadNotification>(data, m_id_provider, m_evt_handler, 0, id, filesize), 0);
 }
 void NotificationManager::set_upload_job_notification_percentage(int id, const std::string& filename, const std::string& host, float percentage)
 {
-	std::string text = PrintHostUploadNotification::get_upload_job_text(id, filename, host);
 	for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
-		if (notification->get_type() == NotificationType::PrintHostUpload && notification->compare_text(text)) {
-			dynamic_cast<PrintHostUploadNotification*>(notification.get())->set_percentage(percentage);
-			wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0);
+		if (notification->get_type() == NotificationType::PrintHostUpload) {
+			PrintHostUploadNotification* phun = dynamic_cast<PrintHostUploadNotification*>(notification.get());
+			if (phun->compare_job_id(id)) {
+				phun->set_percentage(percentage);
+				wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0);
+				break;
+			}
 		}
 	}
 }
 void NotificationManager::upload_job_notification_show_canceled(int id, const std::string& filename, const std::string& host)
 {
-	std::string text = PrintHostUploadNotification::get_upload_job_text(id, filename, host);
 	for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
-		if (notification->get_type() == NotificationType::PrintHostUpload && notification->compare_text(text)) {
-			dynamic_cast<PrintHostUploadNotification*>(notification.get())->cancel();
-			wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0);
-			break;
+		if (notification->get_type() == NotificationType::PrintHostUpload) {
+			PrintHostUploadNotification* phun = dynamic_cast<PrintHostUploadNotification*>(notification.get());
+			if (phun->compare_job_id(id)) {
+				phun->cancel();
+				wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0);
+				break;
+			}
 		}
 	}
 }
 void NotificationManager::upload_job_notification_show_error(int id, const std::string& filename, const std::string& host)
 {
-	std::string text = PrintHostUploadNotification::get_upload_job_text(id, filename, host);
 	for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
-		if (notification->get_type() == NotificationType::PrintHostUpload && notification->compare_text(text)) {
-			dynamic_cast<PrintHostUploadNotification*>(notification.get())->error();
-			wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0);
-			break;
+		if (notification->get_type() == NotificationType::PrintHostUpload) {
+			PrintHostUploadNotification* phun = dynamic_cast<PrintHostUploadNotification*>(notification.get());
+			if(phun->compare_job_id(id)) {
+				phun->error();
+				wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0);
+				break;
+			}
 		}
 	}
 }
diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp
index 74ebedde2..1cf780988 100644
--- a/src/slic3r/GUI/NotificationManager.hpp
+++ b/src/slic3r/GUI/NotificationManager.hpp
@@ -232,7 +232,7 @@ private:
 		const NotificationData get_data() const { return m_data; }
 		const bool             is_gray() const { return m_is_gray; }
 		void                   set_gray(bool g) { m_is_gray = g; }
-		bool                   compare_text(const std::string& text);
+		virtual bool           compare_text(const std::string& text) const;
         void                   hide(bool h) { if (is_finished()) return; m_state = h ? EState::Hidden : EState::Unknown; }
 		// sets m_next_render with time of next mandatory rendering. Delta is time since last render.
 		bool                   update_state(bool paused, const int64_t delta);
@@ -405,10 +405,12 @@ private:
 			m_has_cancel_button = true;
 		}
 		virtual void        init() override;
-		static std::string	get_upload_job_text(int id, const std::string& filename, const std::string& host) { return "[" + std::to_string(id) + "] " + filename + " -> " + host; }
+		static std::string	get_upload_job_text(int id, const std::string& filename, const std::string& host) { return /*"[" + std::to_string(id) + "] " + */filename + " -> " + host; }
 		virtual void		set_percentage(float percent) override;
 		void				cancel() { m_uj_state = UploadJobState::PB_CANCELLED; m_has_cancel_button = false; }
 		void				error()  { m_uj_state = UploadJobState::PB_ERROR;     m_has_cancel_button = false; }
+		bool				compare_job_id(const int other_id) const { return m_job_id == other_id; }
+		virtual bool		compare_text(const std::string& text) const override { return false; }
 	protected:
 		virtual void render_bar(ImGuiWrapper& imgui,
 								const float win_size_x, const float win_size_y,

From 429675db2d52e79d8e6c29152acefb9f9e7879cf Mon Sep 17 00:00:00 2001
From: David Kocik <kocikdav@gmail.com>
Date: Sun, 11 Apr 2021 11:47:20 +0200
Subject: [PATCH 7/8] Error appearance of upload notification and dividing
 lines with lesser impotance of spaces

---
 src/slic3r/GUI/NotificationManager.cpp | 185 ++++++++++++++++++-------
 src/slic3r/GUI/NotificationManager.hpp |  27 ++--
 2 files changed, 157 insertions(+), 55 deletions(-)

diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp
index 005b3c11f..7f61ad7f3 100644
--- a/src/slic3r/GUI/NotificationManager.cpp
+++ b/src/slic3r/GUI/NotificationManager.cpp
@@ -188,22 +188,8 @@ void NotificationManager::PopNotification::render(GLCanvas3D& canvas, float init
 		fading_pop = true;
 	}
 	
-	// background color
-	if (m_is_gray) {
-		ImVec4 backcolor(0.7f, 0.7f, 0.7f, 0.5f);
-		Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_state == EState::FadingOut, m_current_fade_opacity);
-	}
-	else if (m_data.level == NotificationLevel::ErrorNotification) {
-		ImVec4 backcolor = ImGui::GetStyleColorVec4(ImGuiCol_WindowBg);
-		backcolor.x += 0.3f;
-		Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_state == EState::FadingOut, m_current_fade_opacity);
-	}
-	else if (m_data.level == NotificationLevel::WarningNotification) {
-		ImVec4 backcolor = ImGui::GetStyleColorVec4(ImGuiCol_WindowBg);
-		backcolor.x += 0.3f;
-		backcolor.y += 0.15f;
-		Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_state == EState::FadingOut, m_current_fade_opacity);
-	}
+	bool bgrnd_color_pop = push_background_color();
+	
 	
 	// name of window indentifies window - has to be unique string
 	if (m_id == 0)
@@ -222,13 +208,34 @@ void NotificationManager::PopNotification::render(GLCanvas3D& canvas, float init
 	}
 	imgui.end();
 
-	if (m_is_gray || m_data.level == NotificationLevel::ErrorNotification || m_data.level == NotificationLevel::WarningNotification)
+	if (bgrnd_color_pop)
 		ImGui::PopStyleColor();
 
 	if (fading_pop)
 		ImGui::PopStyleColor(2);
 }
-
+bool NotificationManager::PopNotification::push_background_color()
+{
+	if (m_is_gray) {
+		ImVec4 backcolor(0.7f, 0.7f, 0.7f, 0.5f);
+		Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_state == EState::FadingOut, m_current_fade_opacity);
+		return true;
+	}
+	if (m_data.level == NotificationLevel::ErrorNotification) {
+		ImVec4 backcolor = ImGui::GetStyleColorVec4(ImGuiCol_WindowBg);
+		backcolor.x += 0.3f;
+		Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_state == EState::FadingOut, m_current_fade_opacity);
+		return true;
+	}
+	if (m_data.level == NotificationLevel::WarningNotification) {
+		ImVec4 backcolor = ImGui::GetStyleColorVec4(ImGuiCol_WindowBg);
+		backcolor.x += 0.3f;
+		backcolor.y += 0.15f;
+		Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_state == EState::FadingOut, m_current_fade_opacity);
+		return true;
+	}
+	return false;
+}
 void NotificationManager::PopNotification::count_spaces()
 {
 	//determine line width 
@@ -245,34 +252,28 @@ void NotificationManager::PopNotification::count_spaces()
 	m_window_width = m_line_height * 25;
 }
  
-void NotificationManager::PopNotification::init()
+void NotificationManager::PopNotification::count_lines()
 {
-	// Do not init closing notification
-	if (is_finished())
-		return;
-    
-	std::string text          = m_text1 + " " + m_hypertext;
-    size_t      last_end      = 0;
-    m_lines_count = 0;
+	std::string text		= m_text1 + " " + m_hypertext;
+	size_t      last_end	= 0;
+	m_lines_count			= 0;
 
-	count_spaces();
-
-	// count lines
 	m_endlines.clear();
 	while (last_end < text.length() - 1)
 	{
-        size_t next_hard_end = text.find_first_of('\n', last_end);
-        if (next_hard_end != std::string::npos && ImGui::CalcTextSize(text.substr(last_end, next_hard_end - last_end).c_str()).x < m_window_width - m_window_width_offset) {
+		size_t next_hard_end = text.find_first_of('\n', last_end);
+		if (next_hard_end != std::string::npos && ImGui::CalcTextSize(text.substr(last_end, next_hard_end - last_end).c_str()).x < m_window_width - m_window_width_offset) {
 			//next line is ended by '/n'
 			m_endlines.push_back(next_hard_end);
 			last_end = next_hard_end + 1;
-		} else {
+		}
+		else {
 			// find next suitable endline
 			if (ImGui::CalcTextSize(text.substr(last_end).c_str()).x >= m_window_width - m_window_width_offset) {
 				// more than one line till end
-                size_t next_space = text.find_first_of(' ', last_end);
+				size_t next_space = text.find_first_of(' ', last_end);
 				if (next_space > 0) {
-                    size_t next_space_candidate = text.find_first_of(' ', next_space + 1);
+					size_t next_space_candidate = text.find_first_of(' ', next_space + 1);
 					while (next_space_candidate > 0 && ImGui::CalcTextSize(text.substr(last_end, next_space_candidate - last_end).c_str()).x < m_window_width - m_window_width_offset) {
 						next_space = next_space_candidate;
 						next_space_candidate = text.find_first_of(' ', next_space + 1);
@@ -286,7 +287,8 @@ void NotificationManager::PopNotification::init()
 						}
 						m_endlines.push_back(last_end + letter_count);
 						last_end += letter_count;
-					} else {
+					}
+					else {
 						m_endlines.push_back(next_space);
 						last_end = next_space + 1;
 					}
@@ -300,6 +302,17 @@ void NotificationManager::PopNotification::init()
 		}
 		m_lines_count++;
 	}
+}
+
+void NotificationManager::PopNotification::init()
+{
+	// Do not init closing notification
+	if (is_finished())
+		return;
+
+	count_spaces();
+	count_lines();
+	
 	if (m_lines_count == 3)
 		m_multiline = true;
 	m_notification_start = GLCanvas3D::timestamp_now();
@@ -802,22 +815,64 @@ void NotificationManager::ProgressBarNotification::init()
 	if(m_state == EState::Shown)
 		m_state = EState::NotFading;
 }
-void NotificationManager::ProgressBarNotification::count_spaces()
-{
-	//determine line width 
-	m_line_height = ImGui::CalcTextSize("A").y;
 
-	m_left_indentation = m_line_height;
-	if (m_data.level == NotificationLevel::ErrorNotification || m_data.level == NotificationLevel::WarningNotification) {
-		std::string text;
-		text = (m_data.level == NotificationLevel::ErrorNotification ? ImGui::ErrorMarker : ImGui::WarningMarker);
-		float picture_width = ImGui::CalcTextSize(text.c_str()).x;
-		m_left_indentation = picture_width + m_line_height / 2;
+
+void NotificationManager::ProgressBarNotification::count_lines()
+{
+	std::string text		= m_text1 + " " + m_hypertext;
+	size_t      last_end	= 0;
+	m_lines_count			= 0;
+
+	m_endlines.clear();
+	while (last_end < text.length() - 1)
+	{
+		size_t next_hard_end = text.find_first_of('\n', last_end);
+		if (next_hard_end != std::string::npos && ImGui::CalcTextSize(text.substr(last_end, next_hard_end - last_end).c_str()).x < m_window_width - m_window_width_offset) {
+			//next line is ended by '/n'
+			m_endlines.push_back(next_hard_end);
+			last_end = next_hard_end + 1;
+		}
+		else {
+			// find next suitable endline
+			if (ImGui::CalcTextSize(text.substr(last_end).c_str()).x >= m_window_width - m_window_width_offset) {
+				// more than one line till end
+				size_t next_space = text.find_first_of(' ', last_end);
+				if (next_space > 0) {
+					size_t next_space_candidate = text.find_first_of(' ', next_space + 1);
+					while (next_space_candidate > 0 && ImGui::CalcTextSize(text.substr(last_end, next_space_candidate - last_end).c_str()).x < m_window_width - m_window_width_offset) {
+						next_space = next_space_candidate;
+						next_space_candidate = text.find_first_of(' ', next_space + 1);
+					}
+					// when one word longer than line. Or the last space is too early.
+					if (ImGui::CalcTextSize(text.substr(last_end, next_space - last_end).c_str()).x > m_window_width - m_window_width_offset ||
+						ImGui::CalcTextSize(text.substr(last_end, next_space - last_end).c_str()).x < (m_window_width - m_window_width_offset) / 4 * 3
+						) {
+						float width_of_a = ImGui::CalcTextSize("a").x;
+						int letter_count = (int)((m_window_width - m_window_width_offset) / width_of_a);
+						while (last_end + letter_count < text.size() && ImGui::CalcTextSize(text.substr(last_end, letter_count).c_str()).x < m_window_width - m_window_width_offset) {
+							letter_count++;
+						}
+						m_endlines.push_back(last_end + letter_count);
+						last_end += letter_count;
+					}
+					else {
+						m_endlines.push_back(next_space);
+						last_end = next_space + 1;
+					}
+				}
+			}
+			else {
+				m_endlines.push_back(text.length());
+				last_end = text.length();
+			}
+
+		}
+		m_lines_count++;
 	}
-	m_window_width_offset = m_line_height * (m_has_cancel_button ? 6 : 4);
-	m_window_width = m_line_height * 25;
 }
 
+
+
 void NotificationManager::ProgressBarNotification::render_text(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
 {
 	// line1 - we do not print any more text than what fits on line 1. Line 2 is bar.
@@ -862,6 +917,32 @@ void NotificationManager::PrintHostUploadNotification::init()
 	if (m_state == EState::NotFading && m_uj_state == UploadJobState::PB_COMPLETED)
 		m_state = EState::Shown;
 }
+void NotificationManager::PrintHostUploadNotification::count_spaces()
+{
+	//determine line width 
+	m_line_height = ImGui::CalcTextSize("A").y;
+
+	m_left_indentation = m_line_height;
+	if (m_uj_state == UploadJobState::PB_ERROR) {
+		std::string text;
+		text = (m_data.level == NotificationLevel::ErrorNotification ? ImGui::ErrorMarker : ImGui::WarningMarker);
+		float picture_width = ImGui::CalcTextSize(text.c_str()).x;
+		m_left_indentation = picture_width + m_line_height / 2;
+	}
+	m_window_width_offset = m_line_height * 6; //(m_has_cancel_button ? 6 : 4);
+	m_window_width = m_line_height * 25;
+}
+bool NotificationManager::PrintHostUploadNotification::push_background_color()
+{
+
+	if (m_uj_state == UploadJobState::PB_ERROR) {
+		ImVec4 backcolor = ImGui::GetStyleColorVec4(ImGuiCol_WindowBg);
+		backcolor.x += 0.3f;
+		Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_state == EState::FadingOut, m_current_fade_opacity);
+		return true;
+	}
+	return false;
+}
 void NotificationManager::PrintHostUploadNotification::set_percentage(float percent)
 {
 	m_percentage = percent;
@@ -911,6 +992,16 @@ void NotificationManager::PrintHostUploadNotification::render_bar(ImGuiWrapper&
 	imgui.text(text.c_str());
 
 }
+void NotificationManager::PrintHostUploadNotification::render_left_sign(ImGuiWrapper& imgui)
+{
+	if (m_uj_state == UploadJobState::PB_ERROR) {
+		std::string text;
+		text = ImGui::ErrorMarker;
+		ImGui::SetCursorPosX(m_line_height / 3);
+		ImGui::SetCursorPosY(m_window_height / 2 - m_line_height);
+		imgui.text(text.c_str());
+	}
+}
 void NotificationManager::PrintHostUploadNotification::render_cancel_button(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
 {
 	ImVec2 win_size(win_size_x, win_size_y);
diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp
index 1cf780988..17657948e 100644
--- a/src/slic3r/GUI/NotificationManager.hpp
+++ b/src/slic3r/GUI/NotificationManager.hpp
@@ -240,11 +240,9 @@ private:
 		EState                 get_state()  const { return m_state; }
 		bool				   is_hovered() const { return m_state == EState::Hovered; } 
 		void				   set_hovered() { if (m_state != EState::Finished && m_state != EState::ClosePending && m_state != EState::Hidden && m_state != EState::Unknown) m_state = EState::Hovered; }
-	
+	protected:
 		// Call after every size change
 		virtual void init();
-		// Part of init() 
-		virtual void count_spaces();
 		// Calculetes correct size but not se it in imgui!
 		virtual void set_next_window_size(ImGuiWrapper& imgui);
 		virtual void render_text(ImGuiWrapper& imgui,
@@ -258,13 +256,20 @@ private:
 		                              const std::string text,
 		                              bool more = false);
 		// Left sign could be error or warning sign
-		void         render_left_sign(ImGuiWrapper& imgui);
+		virtual void render_left_sign(ImGuiWrapper& imgui);
 		virtual void render_minimize_button(ImGuiWrapper& imgui,
 			                                const float win_pos_x, const float win_pos_y);
 		// Hypertext action, returns true if notification should close.
 		// Action is stored in NotificationData::callback as std::function<bool(wxEvtHandler*)>
 		virtual bool on_text_click();
-	protected:
+	
+		// Part of init(), counts horizontal spacing like left indentation 
+		virtual void count_spaces();
+		// Part of init(), counts end lines
+		virtual void count_lines();
+		// returns true if PopStyleColor should be called later to pop this push
+		virtual bool push_background_color();
+
 		const NotificationData m_data;
 		// For reusing ImGUI windows.
 		NotificationIDProvider &m_id_provider;
@@ -367,7 +372,8 @@ private:
 		virtual void set_percentage(float percent) { m_percentage = percent; }
 	protected:
 		virtual void init() override;
-		virtual void count_spaces() override;
+		virtual void count_lines() override;
+		
 		virtual void render_text(ImGuiWrapper& imgui,
 									const float win_size_x, const float win_size_y,
 									const float win_pos_x, const float win_pos_y) override;
@@ -378,6 +384,8 @@ private:
 									const float win_size_x, const float win_size_y,
 									const float win_pos_x, const float win_pos_y)
 		{}
+		virtual void render_minimize_button(ImGuiWrapper& imgui,
+			const float win_pos_x, const float win_pos_y) override {}
 		float				m_percentage;
 		
 		bool				m_has_cancel_button {false};
@@ -404,20 +412,23 @@ private:
 		{
 			m_has_cancel_button = true;
 		}
-		virtual void        init() override;
 		static std::string	get_upload_job_text(int id, const std::string& filename, const std::string& host) { return /*"[" + std::to_string(id) + "] " + */filename + " -> " + host; }
 		virtual void		set_percentage(float percent) override;
 		void				cancel() { m_uj_state = UploadJobState::PB_CANCELLED; m_has_cancel_button = false; }
-		void				error()  { m_uj_state = UploadJobState::PB_ERROR;     m_has_cancel_button = false; }
+		void				error()  { m_uj_state = UploadJobState::PB_ERROR;     m_has_cancel_button = false; init(); }
 		bool				compare_job_id(const int other_id) const { return m_job_id == other_id; }
 		virtual bool		compare_text(const std::string& text) const override { return false; }
 	protected:
+		virtual void        init() override;
+		virtual void count_spaces() override;
+		virtual bool push_background_color() override;
 		virtual void render_bar(ImGuiWrapper& imgui,
 								const float win_size_x, const float win_size_y,
 								const float win_pos_x, const float win_pos_y) override;
 		virtual void render_cancel_button(ImGuiWrapper& imgui,
 											const float win_size_x, const float win_size_y,
 											const float win_pos_x, const float win_pos_y) override;
+		virtual void render_left_sign(ImGuiWrapper& imgui) override;
 		// Identifies job in cancel callback
 		int					m_job_id;
 		// Size of uploaded size to be displayed in MB

From 453884f908d744609915b6e269c464cae3f4c827 Mon Sep 17 00:00:00 2001
From: David Kocik <kocikdav@gmail.com>
Date: Tue, 13 Apr 2021 08:39:07 +0200
Subject: [PATCH 8/8] Check of correct suffix during PrintHostSend dialog.

---
 src/slic3r/GUI/PrintHostDialogs.cpp | 20 ++++++++++++++++++--
 1 file changed, 18 insertions(+), 2 deletions(-)

diff --git a/src/slic3r/GUI/PrintHostDialogs.cpp b/src/slic3r/GUI/PrintHostDialogs.cpp
index f69db2ea3..762450c53 100644
--- a/src/slic3r/GUI/PrintHostDialogs.cpp
+++ b/src/slic3r/GUI/PrintHostDialogs.cpp
@@ -68,8 +68,10 @@ PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, bool can_start_pr
             combo_groups->SetValue(recent_group);
     }
 
-    btn_sizer->Add(CreateStdDialogButtonSizer(wxOK | wxCANCEL));
-
+    auto* szr = CreateStdDialogButtonSizer(wxOK | wxCANCEL);
+    auto* btn_ok = szr->GetAffirmativeButton();
+    btn_sizer->Add(szr);
+    
     wxString recent_path = from_u8(app_config->get("recent", CONFIG_KEY_PATH));
     if (recent_path.Length() > 0 && recent_path[recent_path.Length() - 1] != '/') {
         recent_path += '/';
@@ -82,6 +84,20 @@ PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, bool can_start_pr
     txt_filename->SetValue(recent_path);
     txt_filename->SetFocus();
     
+    wxString suffix = recent_path.substr(recent_path.find_last_of('.'));
+
+    btn_ok->Bind(wxEVT_BUTTON, [this, suffix](wxCommandEvent&) {
+        wxString path = txt_filename->GetValue();
+        // .gcode suffix control
+        if (!path.Lower().EndsWith(suffix.Lower()))
+        {
+            wxMessageDialog msg_wingow(this, wxString::Format(_L("Upload filename doesn't end with \"%s\". Do you wish to continue?"), suffix), wxString(SLIC3R_APP_NAME), wxYES | wxNO);
+            if (msg_wingow.ShowModal() == wxID_NO)
+                return;
+        }
+        EndDialog(wxID_OK);
+        });
+
     Fit();
     CenterOnParent();