From 3ca3a544a87cc569b69351a77996c287763388a5 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Wed, 4 Nov 2020 09:23:47 +0100 Subject: [PATCH] New Export Finished notification showing path and opening containing folder. Fix of #4917. Fixed wrongly grayed eject button in File menu. Hopefully fix of ctrl shortcut of tooltips at sidebar. --- resources/icons/notification_eject_sd.svg | 75 +++++ .../icons/notification_eject_sd_hover.svg | 76 +++++ src/imgui/imconfig.h | 2 + src/libslic3r/Exception.hpp | 1 + src/libslic3r/Zipper.cpp | 4 +- src/libslic3r/utils.cpp | 14 +- src/slic3r/GUI/BackgroundSlicingProcess.cpp | 28 +- src/slic3r/GUI/BackgroundSlicingProcess.hpp | 2 + src/slic3r/GUI/ImGuiWrapper.cpp | 4 +- src/slic3r/GUI/NotificationManager.cpp | 275 ++++++++++++++++-- src/slic3r/GUI/NotificationManager.hpp | 71 ++++- src/slic3r/GUI/Plater.cpp | 91 +++--- src/slic3r/GUI/RemovableDriveManager.cpp | 1 - 13 files changed, 552 insertions(+), 92 deletions(-) create mode 100644 resources/icons/notification_eject_sd.svg create mode 100644 resources/icons/notification_eject_sd_hover.svg diff --git a/resources/icons/notification_eject_sd.svg b/resources/icons/notification_eject_sd.svg new file mode 100644 index 000000000..bea6c0977 --- /dev/null +++ b/resources/icons/notification_eject_sd.svg @@ -0,0 +1,75 @@ + +image/svg+xml + + + + + + + diff --git a/resources/icons/notification_eject_sd_hover.svg b/resources/icons/notification_eject_sd_hover.svg new file mode 100644 index 000000000..23d2480c6 --- /dev/null +++ b/resources/icons/notification_eject_sd_hover.svg @@ -0,0 +1,76 @@ + +image/svg+xml + + + + + + + diff --git a/src/imgui/imconfig.h b/src/imgui/imconfig.h index 4a1d1faa0..fc635dfac 100644 --- a/src/imgui/imconfig.h +++ b/src/imgui/imconfig.h @@ -121,6 +121,8 @@ namespace ImGui const char MinimalizeHoverMarker = 0xF; const char WarningMarker = 0x10; const char ErrorMarker = 0x11; + const char EjectMarker = 0x12; + const char EjectHoverMarker = 0x13; // void MyFunction(const char* name, const MyMatrix44& v); } diff --git a/src/libslic3r/Exception.hpp b/src/libslic3r/Exception.hpp index 2bef204ad..287905533 100644 --- a/src/libslic3r/Exception.hpp +++ b/src/libslic3r/Exception.hpp @@ -20,6 +20,7 @@ SLIC3R_DERIVE_EXCEPTION(OutOfRange, LogicError); SLIC3R_DERIVE_EXCEPTION(IOError, CriticalException); SLIC3R_DERIVE_EXCEPTION(FileIOError, IOError); SLIC3R_DERIVE_EXCEPTION(HostNetworkError, IOError); +SLIC3R_DERIVE_EXCEPTION(ExportError, CriticalException); // Runtime exception produced by Slicer. Such exception cancels the slicing process and it shall be shown in notifications. SLIC3R_DERIVE_EXCEPTION(SlicingError, Exception); #undef SLIC3R_DERIVE_EXCEPTION diff --git a/src/libslic3r/Zipper.cpp b/src/libslic3r/Zipper.cpp index 7a95829cd..cebafa633 100644 --- a/src/libslic3r/Zipper.cpp +++ b/src/libslic3r/Zipper.cpp @@ -25,12 +25,12 @@ public: std::string formatted_errorstr() const { return L("Error with zip archive") + " " + m_zipname + ": " + - get_errorstr() + "!"; + get_errorstr(); } SLIC3R_NORETURN void blow_up() const { - throw Slic3r::RuntimeError(formatted_errorstr()); + throw Slic3r::ExportError(formatted_errorstr()); } bool is_alive() diff --git a/src/libslic3r/utils.cpp b/src/libslic3r/utils.cpp index 886dcf46a..2c55c5a0e 100644 --- a/src/libslic3r/utils.cpp +++ b/src/libslic3r/utils.cpp @@ -429,24 +429,20 @@ CopyFileResult copy_file_inner(const std::string& from, const std::string& to, s // the copy_file() function will fail appropriately and we don't want the permission() // calls to cause needless failures on permissionless filesystems (ie. FATs on SD cards etc.) // or when the target file doesn't exist. - - //This error code is ignored boost::system::error_code ec; - boost::filesystem::permissions(target, perms, ec); - //if (ec) - // BOOST_LOG_TRIVIAL(error) << "Copy file permisions before copy error message: " << ec.message(); - // This error code is passed up + if (ec) + BOOST_LOG_TRIVIAL(error) << "boost::filesystem::permisions before copy error message (this could be irrelevant message based on file system): " << ec.message(); ec.clear(); boost::filesystem::copy_file(source, target, boost::filesystem::copy_option::overwrite_if_exists, ec); if (ec) { error_message = ec.message(); return FAIL_COPY_FILE; } - //ec.clear(); + ec.clear(); boost::filesystem::permissions(target, perms, ec); - //if (ec) - // BOOST_LOG_TRIVIAL(error) << "Copy file permisions after copy error message: " << ec.message(); + if (ec) + BOOST_LOG_TRIVIAL(error) << "boost::filesystem::permisions after copy error message (this could be irrelevant message based on file system): " << ec.message(); return SUCCESS; } diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp index 36187f81e..2f3c40ace 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -53,6 +53,24 @@ bool SlicingProcessCompletedEvent::critical_error() const return true; } +bool SlicingProcessCompletedEvent::invalidate_plater() const +{ + if (critical_error()) + { + try { + this->rethrow_exception(); + } + catch (const Slic3r::ExportError&) { + // Exception thrown by copying file does not ivalidate plater + return false; + } + catch (...) { + } + return true; + } + return false; +} + std::string SlicingProcessCompletedEvent::format_error_message() const { std::string error; @@ -142,19 +160,19 @@ void BackgroundSlicingProcess::process_fff() switch (copy_ret_val) { case SUCCESS: break; // no error case FAIL_COPY_FILE: - throw Slic3r::RuntimeError((boost::format(_utf8(L("Copying of the temporary G-code to the output G-code failed. Maybe the SD card is write locked?\nError message: %1%"))) % error_message).str()); + throw Slic3r::ExportError((boost::format(_utf8(L("Copying of the temporary G-code to the output G-code failed. Maybe the SD card is write locked?\nError message: %1%"))) % error_message).str()); break; case FAIL_FILES_DIFFERENT: - throw Slic3r::RuntimeError((boost::format(_utf8(L("Copying of the temporary G-code to the output G-code failed. There might be problem with target device, please try exporting again or using different device. The corrupted output G-code is at %1%.tmp."))) % export_path).str()); + throw Slic3r::ExportError((boost::format(_utf8(L("Copying of the temporary G-code to the output G-code failed. There might be problem with target device, please try exporting again or using different device. The corrupted output G-code is at %1%.tmp."))) % export_path).str()); break; case FAIL_RENAMING: - throw Slic3r::RuntimeError((boost::format(_utf8(L("Renaming of the G-code after copying to the selected destination folder has failed. Current path is %1%.tmp. Please try exporting again."))) % export_path).str()); + throw Slic3r::ExportError((boost::format(_utf8(L("Renaming of the G-code after copying to the selected destination folder has failed. Current path is %1%.tmp. Please try exporting again."))) % export_path).str()); break; case FAIL_CHECK_ORIGIN_NOT_OPENED: - throw Slic3r::RuntimeError((boost::format(_utf8(L("Copying of the temporary G-code has finished but the original code at %1% couldn't be opened during copy check. The output G-code is at %2%.tmp."))) % m_temp_output_path % export_path).str()); + throw Slic3r::ExportError((boost::format(_utf8(L("Copying of the temporary G-code has finished but the original code at %1% couldn't be opened during copy check. The output G-code is at %2%.tmp."))) % m_temp_output_path % export_path).str()); break; case FAIL_CHECK_TARGET_NOT_OPENED: - throw Slic3r::RuntimeError((boost::format(_utf8(L("Copying of the temporary G-code has finished but the exported code couldn't be opened during copy check. The output G-code is at %1%.tmp."))) % export_path).str()); + throw Slic3r::ExportError((boost::format(_utf8(L("Copying of the temporary G-code has finished but the exported code couldn't be opened during copy check. The output G-code is at %1%.tmp."))) % export_path).str()); break; default: throw Slic3r::RuntimeError(_utf8(L("Unknown error occured during exporting G-code."))); diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.hpp b/src/slic3r/GUI/BackgroundSlicingProcess.hpp index 1b2687e63..e6314068e 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.hpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.hpp @@ -57,6 +57,8 @@ public: bool error() const { return m_status == Error; } // Unhandled error produced by stdlib or a Win32 structured exception, or unhandled Slic3r's own critical exception. bool critical_error() const; + // Critical errors does invalidate plater except CopyFileError. + bool invalidate_plater() const; // Only valid if error() void rethrow_exception() const { assert(this->error()); assert(m_exception); std::rethrow_exception(m_exception); } // Produce a human readable message to be displayed by a notification or a message box. diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index d759f4b9a..e468ffbb5 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -49,7 +49,9 @@ static const std::map font_icons = { {ImGui::MinimalizeMarker , "notification_minimalize" }, {ImGui::MinimalizeHoverMarker , "notification_minimalize_hover" }, {ImGui::WarningMarker , "notification_warning" }, - {ImGui::ErrorMarker , "notification_error" } + {ImGui::ErrorMarker , "notification_error" }, + {ImGui::EjectMarker , "notification_eject_sd" }, + {ImGui::EjectHoverMarker , "notification_eject_sd_hover" }, }; const ImVec4 ImGuiWrapper::COL_GREY_DARK = { 0.333f, 0.333f, 0.333f, 1.0f }; diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index 8516a4419..a1bb48d2b 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -1,6 +1,7 @@ #include "NotificationManager.hpp" #include "GUI_App.hpp" +#include "GUI.hpp" #include "Plater.hpp" #include "GLCanvas3D.hpp" #include "ImGuiWrapper.hpp" @@ -33,6 +34,56 @@ namespace Notifications_Internal{ else ImGui::PushStyleColor(idx, col); } + + void open_folder(const std::string& path) + { + // Code taken from desktop_open_datadir_folder() + + // Execute command to open a file explorer, platform dependent. + // FIXME: The const_casts aren't needed in wxWidgets 3.1, remove them when we upgrade. + +#ifdef _WIN32 + const wxString widepath = from_u8(path); + const wchar_t* argv[] = { L"explorer", widepath.GetData(), nullptr }; + ::wxExecute(const_cast(argv), wxEXEC_ASYNC, nullptr); +#elif __APPLE__ + const char* argv[] = { "open", path.data(), nullptr }; + ::wxExecute(const_cast(argv), wxEXEC_ASYNC, nullptr); +#else + const char* argv[] = { "xdg-open", path.data(), nullptr }; + + // Check if we're running in an AppImage container, if so, we need to remove AppImage's env vars, + // because they may mess up the environment expected by the file manager. + // Mostly this is about LD_LIBRARY_PATH, but we remove a few more too for good measure. + if (wxGetEnv("APPIMAGE", nullptr)) { + // We're running from AppImage + wxEnvVariableHashMap env_vars; + wxGetEnvMap(&env_vars); + + env_vars.erase("APPIMAGE"); + env_vars.erase("APPDIR"); + env_vars.erase("LD_LIBRARY_PATH"); + env_vars.erase("LD_PRELOAD"); + env_vars.erase("UNION_PRELOAD"); + + wxExecuteEnv exec_env; + exec_env.env = std::move(env_vars); + + wxString owd; + if (wxGetEnv("OWD", &owd)) { + // This is the original work directory from which the AppImage image was run, + // set it as CWD for the child process: + exec_env.cwd = std::move(owd); + } + + ::wxExecute(const_cast(argv), wxEXEC_ASYNC, nullptr, &exec_env); + } + else { + // Looks like we're NOT running from AppImage, we'll make no changes to the environment. + ::wxExecute(const_cast(argv), wxEXEC_ASYNC, nullptr, nullptr); + } +#endif + } } #if 1 @@ -183,6 +234,7 @@ NotificationManager::PopNotification::RenderResult NotificationManager::PopNotif render_left_sign(imgui); render_text(imgui, win_size.x, win_size.y, win_pos.x, win_pos.y); render_close_button(imgui, win_size.x, win_size.y, win_pos.x, win_pos.y); + m_minimize_b_visible = false; if (m_multiline && m_lines_count > 3) render_minimize_button(imgui, win_pos.x, win_pos.y); } else { @@ -205,12 +257,8 @@ NotificationManager::PopNotification::RenderResult NotificationManager::PopNotif ImGui::PopStyleColor(); return ret_val; } -void NotificationManager::PopNotification::init() +void NotificationManager::PopNotification::count_spaces() { - std::string text = m_text1 + " " + m_hypertext; - int last_end = 0; - m_lines_count = 0; - //determine line width m_line_height = ImGui::CalcTextSize("A").y; @@ -221,8 +269,16 @@ void NotificationManager::PopNotification::init() float picture_width = ImGui::CalcTextSize(text.c_str()).x; m_left_indentation = picture_width + m_line_height / 2; } - m_window_width_offset = m_left_indentation + m_line_height * 2; + m_window_width_offset = m_left_indentation + m_line_height * 3.f; m_window_width = m_line_height * 25; +} +void NotificationManager::PopNotification::init() +{ + std::string text = m_text1 + " " + m_hypertext; + int last_end = 0; + m_lines_count = 0; + + count_spaces(); // count lines m_endlines.clear(); @@ -233,10 +289,9 @@ void NotificationManager::PopNotification::init() //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 - 3.5f * m_line_height) {// m_window_width_offset) { + if (ImGui::CalcTextSize(text.substr(last_end).c_str()).x >= m_window_width - m_window_width_offset) { // more than one line till end int next_space = text.find_first_of(' ', last_end); if (next_space > 0) { @@ -245,8 +300,19 @@ void NotificationManager::PopNotification::init() next_space = next_space_candidate; next_space_candidate = text.find_first_of(' ', next_space + 1); } - m_endlines.push_back(next_space); - last_end = next_space + 1; + // when one word longer than line. + if (ImGui::CalcTextSize(text.substr(last_end, next_space - last_end).c_str()).x > m_window_width - m_window_width_offset) { + 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 { @@ -257,6 +323,8 @@ void NotificationManager::PopNotification::init() } m_lines_count++; } + if (m_lines_count == 3) + m_multiline = true; m_initialized = true; } void NotificationManager::PopNotification::set_next_window_size(ImGuiWrapper& imgui) @@ -285,7 +353,8 @@ void NotificationManager::PopNotification::render_text(ImGuiWrapper& imgui, cons float shift_y = m_line_height;// -m_line_height / 20; for (size_t i = 0; i < m_lines_count; i++) { std::string line = m_text1.substr(last_end , m_endlines[i] - last_end); - last_end = m_endlines[i] + 1; + if(i < m_lines_count - 1) + last_end = m_endlines[i] + (m_text1[m_endlines[i]] == '\n' || m_text1[m_endlines[i]] == ' ' ? 1 : 0); ImGui::SetCursorPosX(x_offset); ImGui::SetCursorPosY(starting_y + i * shift_y); imgui.text(line.c_str()); @@ -303,7 +372,7 @@ void NotificationManager::PopNotification::render_text(ImGuiWrapper& imgui, cons 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()); // line2 - std::string line = m_text1.substr(m_endlines[0] + 1, m_endlines[1] - m_endlines[0] - 1); + 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)); if (ImGui::CalcTextSize(line.c_str()).x > m_window_width - m_window_width_offset - ImGui::CalcTextSize((".." + _u8L("More")).c_str()).x) { line = line.substr(0, line.length() - 6); @@ -326,7 +395,7 @@ void NotificationManager::PopNotification::render_text(ImGuiWrapper& imgui, cons 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()); // line2 - std::string line = m_text1.substr(m_endlines[0] + 1); + std::string line = m_text1.substr(m_endlines[0] + (m_text1[m_endlines[0]] == '\n' || m_text1[m_endlines[0]] == ' ' ? 1 : 0)); cursor_y = win_size.y / 2 + win_size.y / 6 - m_line_height / 2; ImGui::SetCursorPosX(x_offset); ImGui::SetCursorPosY(cursor_y); @@ -375,8 +444,7 @@ void NotificationManager::PopNotification::render_hypertext(ImGuiWrapper& imgui, set_next_window_size(imgui); } else { - on_text_click(); - m_close_pending = true; + m_close_pending = on_text_click(); } } ImGui::PopStyleColor(); @@ -407,7 +475,7 @@ void NotificationManager::PopNotification::render_hypertext(ImGuiWrapper& imgui, void NotificationManager::PopNotification::render_close_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); - ImVec2 win_pos(win_pos_x, win_pos_y); + ImVec2 win_pos(win_pos_x, win_pos_y); ImVec4 orange_color = ImGui::GetStyleColorVec4(ImGuiCol_Button); orange_color.w = 0.8f; ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f)); @@ -422,7 +490,7 @@ void NotificationManager::PopNotification::render_close_button(ImGuiWrapper& img button_text = ImGui::CloseIconMarker; if (ImGui::IsMouseHoveringRect(ImVec2(win_pos.x - win_size.x / 10.f, win_pos.y), - ImVec2(win_pos.x, win_pos.y + win_size.y - (m_multiline? 2 * m_line_height : 0)), + ImVec2(win_pos.x, win_pos.y + win_size.y - ( m_minimize_b_visible ? 2 * m_line_height : 0)), true)) { button_text = ImGui::CloseIconHoverMarker; @@ -435,11 +503,10 @@ void NotificationManager::PopNotification::render_close_button(ImGuiWrapper& img { m_close_pending = true; } - //invisible large button - ImGui::SetCursorPosX(win_size.x - win_size.x / 10.f); + ImGui::SetCursorPosX(win_size.x - m_line_height * 2.125); ImGui::SetCursorPosY(0); - if (imgui.button(" ", win_size.x / 10.f, win_size.y - (m_multiline ? 2 * m_line_height : 0))) + if (imgui.button(" ", m_line_height * 2.125, win_size.y - ( m_minimize_b_visible ? 2 * m_line_height : 0))) { m_close_pending = true; } @@ -540,15 +607,12 @@ void NotificationManager::PopNotification::render_minimize_button(ImGuiWrapper& ImGui::PopStyleColor(); ImGui::PopStyleColor(); ImGui::PopStyleColor(); + m_minimize_b_visible = true; } -void NotificationManager::PopNotification::on_text_click() +bool NotificationManager::PopNotification::on_text_click() { + bool ret = true; switch (m_data.type) { - case NotificationType::ExportToRemovableFinished : - assert(m_evt_handler != nullptr); - if (m_evt_handler != nullptr) - wxPostEvent(m_evt_handler, EjectDriveNotificationClickedEvent(EVT_EJECT_DRIVE_NOTIFICAION_CLICKED)); - break; case NotificationType::SlicingComplete : //wxGetApp().plater()->export_gcode(false); assert(m_evt_handler != nullptr); @@ -567,6 +631,7 @@ void NotificationManager::PopNotification::on_text_click() default: break; } + return ret; } void NotificationManager::PopNotification::update(const NotificationData& n) { @@ -633,6 +698,127 @@ void NotificationManager::SlicingCompleteLargeNotification::set_large(bool l) m_hypertext = l ? _u8L("Export G-Code.") : std::string(); m_hidden = !l; } +//---------------ExportFinishedNotification----------- +void NotificationManager::ExportFinishedNotification::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; + } + //TODO count this properly + m_window_width_offset = m_left_indentation + m_line_height * (m_to_removable ? 5.f : 3.f); + m_window_width = m_line_height * 25; +} + +void NotificationManager::ExportFinishedNotification::render_text(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); + ImVec2 win_pos(win_pos_x, win_pos_y); + float x_offset = m_left_indentation; + std::string fulltext = m_text1 + m_hypertext; //+ m_text2; + ImVec2 text_size = ImGui::CalcTextSize(fulltext.c_str()); + // Lines are always at least two and m_multiline is always true for ExportFinishedNotification. + // First line has "Export Finished" text and than hyper text open folder. + // Following lines are path to gcode. + int last_end = 0; + float starting_y = m_line_height / 2;//10; + float shift_y = m_line_height;// -m_line_height / 20; + 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) + last_end = m_endlines[i] + (m_text1[m_endlines[i]] == '\n' || m_text1[m_endlines[i]] == ' ' ? 1 : 0); + ImGui::SetCursorPosX(x_offset); + ImGui::SetCursorPosY(starting_y + i * shift_y); + imgui.text(line.c_str()); + //hyperlink text + if ( i == 0 ) { + render_hypertext(imgui, x_offset + ImGui::CalcTextSize(m_text1.substr(0, last_end).c_str()).x + ImGui::CalcTextSize(" ").x, starting_y, _u8L("Open Folder.")); + } + } + +} + +void NotificationManager::ExportFinishedNotification::render_close_button(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y) +{ + PopNotification::render_close_button(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y); + if(m_to_removable) + render_eject_button(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y); +} + +void NotificationManager::ExportFinishedNotification::render_eject_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); + ImVec2 win_pos(win_pos_x, win_pos_y); + ImVec4 orange_color = ImGui::GetStyleColorVec4(ImGuiCol_Button); + orange_color.w = 0.8f; + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f)); + Notifications_Internal::push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_fading_out, m_current_fade_opacity); + Notifications_Internal::push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_fading_out, m_current_fade_opacity); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(.0f, .0f, .0f, .0f)); + + std::string button_text; + button_text = ImGui::EjectMarker; + + if (ImGui::IsMouseHoveringRect(ImVec2(win_pos.x - m_line_height * 4.5f, win_pos.y), + ImVec2(win_pos.x - m_line_height * 2.5f, win_pos.y + win_size.y), + true)) + { + button_text = ImGui::EjectHoverMarker; + // tooltip + long time_now = wxGetLocalTime(); + if (m_hover_time > 0 && m_hover_time < time_now) { + ImGui::PushStyleColor(ImGuiCol_PopupBg, ImGuiWrapper::COL_WINDOW_BACKGROUND); + ImGui::BeginTooltip(); + imgui.text(_u8L("Eject drive")); + ImGui::EndTooltip(); + ImGui::PopStyleColor(); + } + if (m_hover_time == 0) + m_hover_time = time_now; + } else + m_hover_time = 0; + + ImVec2 button_pic_size = ImGui::CalcTextSize(button_text.c_str()); + ImVec2 button_size(button_pic_size.x * 1.25f, button_pic_size.y * 1.25f); + ImGui::SetCursorPosX(win_size.x - m_line_height * 4.f); + ImGui::SetCursorPosY(win_size.y / 2 - button_size.y / 2); + if (imgui.button(button_text.c_str(), button_size.x, button_size.y)) + { + assert(m_evt_handler != nullptr); + if (m_evt_handler != nullptr) + wxPostEvent(m_evt_handler, EjectDriveNotificationClickedEvent(EVT_EJECT_DRIVE_NOTIFICAION_CLICKED)); + m_close_pending = true; + } + + //invisible large button + ImGui::SetCursorPosX(win_size.x - m_line_height * 4.625f); + ImGui::SetCursorPosY(0); + if (imgui.button(" ", m_line_height * 2.f, win_size.y)) + { + assert(m_evt_handler != nullptr); + if (m_evt_handler != nullptr) + wxPostEvent(m_evt_handler, EjectDriveNotificationClickedEvent(EVT_EJECT_DRIVE_NOTIFICAION_CLICKED)); + m_close_pending = true; + } + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); +} +bool NotificationManager::ExportFinishedNotification::on_text_click() +{ + Notifications_Internal::open_folder(m_export_dir_path); + return false; +} //------NotificationManager-------- NotificationManager::NotificationManager(wxEvtHandler* evt_handler) : m_evt_handler(evt_handler) @@ -789,6 +975,13 @@ void NotificationManager::remove_slicing_warnings_of_released_objects(const std: notification->close(); } } +void NotificationManager::push_exporting_finished_notification(GLCanvas3D& canvas, std::string path, std::string dir_path, bool on_removable) +{ + close_notification_of_type(NotificationType::ExportFinished); + NotificationData data{ NotificationType::ExportFinished, NotificationLevel::RegularNotification, 0, _u8L("Exporting finished.") +"\n"+ path }; + push_notification_data(std::make_unique(data, m_id_provider, m_evt_handler, on_removable, path, dir_path), + canvas, 0); +} bool NotificationManager::push_notification_data(const NotificationData ¬ification_data, GLCanvas3D& canvas, int timestamp) { return push_notification_data(std::make_unique(notification_data, m_id_provider, m_evt_handler), canvas, timestamp); @@ -822,7 +1015,7 @@ void NotificationManager::render_notifications(GLCanvas3D& canvas, float overlay bool hovered = false; sort_notifications(); // iterate thru notifications and render them / erease them - for (auto it = m_pop_notifications.begin(); it != m_pop_notifications.end();) { + for (auto it = m_pop_notifications.begin(); it != m_pop_notifications.end();) { if ((*it)->get_finished()) { it = m_pop_notifications.erase(it); } else { @@ -931,5 +1124,31 @@ bool NotificationManager::has_slicing_error_notification() }); } +void NotificationManager::new_export_began(bool on_removable) +{ + close_notification_of_type(NotificationType::ExportFinished); + // If we want to hold information of ejecting removable on later export finished notifications + /* + for (std::unique_ptr& notification : m_pop_notifications) { + if (notification->get_type() == NotificationType::ExportToRemovableFinished) { + if (!on_removable) { + const NotificationData old_data = notification->get_data(); + notification->update( {old_data.type, old_data.level ,old_data.duration, std::string(), old_data.hypertext} ); + } else { + notification->close(); + } + return; + } + } + */ +} +void NotificationManager::device_ejected() +{ + for (std::unique_ptr& notification : m_pop_notifications) { + if (notification->get_type() == NotificationType::ExportFinished && dynamic_cast(notification.get())->m_to_removable) + notification->close(); + } +} + }//namespace GUI }//namespace Slic3r diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index 49de00a9f..0550dab9b 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -32,7 +32,11 @@ enum class NotificationType SlicingComplete, // SlicingNotPossible, // Notification on end of export to a removable media, with hyperling to eject the external media. - ExportToRemovableFinished, + // Obsolete by ExportFinished +// ExportToRemovableFinished, + // Notification on end of export, with hyperling to see folder and eject if export was to external media. + // Own subclass. + ExportFinished, // Works on OSX only. //FIXME Do we want to have it on Linux and Windows? Is it possible to get the Disconnect event on Windows? Mouse3dDisconnected, @@ -115,15 +119,21 @@ public: // Called when the side bar changes its visibility, as the "slicing complete" notification supplements // the "slicing info" normally shown at the side bar. void set_slicing_complete_large(bool large); + // Exporting finished, show this information with path, button to open containing folder and if ejectable - eject button + void push_exporting_finished_notification(GLCanvas3D& canvas, std::string path, std::string dir_path, bool on_removable); + // Close old notification ExportFinished. + void new_export_began(bool on_removable); + // finds ExportFinished notification and closes it if it was to removable device + void device_ejected(); // renders notifications in queue and deletes expired ones void render_notifications(GLCanvas3D& canvas, float overlay_width); // finds and closes all notifications of given type void close_notification_of_type(const NotificationType type); // Which view is active? Plater or G-code preview? Hide warnings in G-code preview. void set_in_preview(bool preview); - // Move to left to avoid colision with variable layer height gizmo + // Move to left to avoid colision with variable layer height gizmo. void set_move_from_overlay(bool move) { m_move_from_overlay = move; } - + private: // duration 0 means not disapearing struct NotificationData { @@ -169,7 +179,7 @@ private: void close() { m_close_pending = true; } // data from newer notification of same type void update(const NotificationData& n); - bool get_finished() const { return m_finished; } + bool get_finished() const { return m_finished || m_close_pending; } // returns top after movement float get_top() const { return m_top_y; } //returns top in actual frame @@ -187,25 +197,29 @@ private: protected: // Call after every size change 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, const float win_size_x, const float win_size_y, const float win_pos_x , const float win_pos_y); - void render_close_button(ImGuiWrapper& imgui, + virtual void render_close_button(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x , const float win_pos_y); void render_countdown(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x , const float win_pos_y); - void render_hypertext(ImGuiWrapper& imgui, + virtual void render_hypertext(ImGuiWrapper& imgui, const float text_x, const float text_y, const std::string text, bool more = false); + // Left sign could be error or warning sign void render_left_sign(ImGuiWrapper& imgui); - void render_minimize_button(ImGuiWrapper& imgui, + virtual void render_minimize_button(ImGuiWrapper& imgui, const float win_pos_x, const float win_pos_y); - void on_text_click(); + // Hypertext action, returns if close notification + virtual bool on_text_click(); const NotificationData m_data; @@ -236,7 +250,9 @@ private: // Will go to m_finished next render bool m_close_pending { false }; // variables to count positions correctly + // all space without text float m_window_width_offset; + // Space on left side without text float m_left_indentation; // Total size of notification window - varies based on monitor float m_window_height { 56.0f }; @@ -252,6 +268,8 @@ private: bool m_is_gray { false }; //if multiline = true, notification is showing all lines(>2) bool m_multiline { false }; + // True if minimized button is rendered, helps to decide where is area for invisible close button + bool m_minimize_b_visible { false }; int m_lines_count{ 1 }; // Target for wxWidgets events sent by clicking on the hyperlink available at some notifications. wxEvtHandler* m_evt_handler; @@ -270,7 +288,6 @@ private: const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y) override; - bool m_is_large; bool m_has_print_info { false }; std::string m_print_info { std::string() }; @@ -284,6 +301,40 @@ private: int warning_step; }; + class ExportFinishedNotification : public PopNotification + { + public: + ExportFinishedNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler, bool to_removable,const std::string& export_path,const std::string& export_dir_path) + : PopNotification(n, id_provider, evt_handler) + , m_to_removable(to_removable) + , m_export_path(export_path) + , m_export_dir_path(export_dir_path) + { + m_multiline = true; + } + bool m_to_removable; + std::string m_export_path; + std::string m_export_dir_path; + protected: + // Reserves space on right for more buttons + virtual void count_spaces() 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; + // Renders also button to open directory with exported path and eject removable media + virtual void render_close_button(ImGuiWrapper& imgui, + const float win_size_x, const float win_size_y, + const float win_pos_x, const float win_pos_y) override; + void render_eject_button(ImGuiWrapper& imgui, + 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 + { m_minimize_b_visible = false; } + virtual bool on_text_click() override; + // local time of last hover for showing tooltip + long m_hover_time { 0 }; + }; + //pushes notification into the queue of notifications that are rendered //can be used to create custom notification bool push_notification_data(const NotificationData& notification_data, GLCanvas3D& canvas, int timestamp); @@ -314,7 +365,7 @@ private: //prepared (basic) notifications const std::vector basic_notifications = { // {NotificationType::SlicingNotPossible, NotificationLevel::RegularNotification, 10, _u8L("Slicing is not possible.")}, - {NotificationType::ExportToRemovableFinished, NotificationLevel::ImportantNotification, 0, _u8L("Exporting finished."), _u8L("Eject drive.") }, +// {NotificationType::ExportToRemovableFinished, NotificationLevel::ImportantNotification, 0, _u8L("Exporting finished."), _u8L("Eject drive.") }, {NotificationType::Mouse3dDisconnected, NotificationLevel::RegularNotification, 10, _u8L("3D Mouse disconnected.") }, // {NotificationType::Mouse3dConnected, NotificationLevel::RegularNotification, 5, _u8L("3D Mouse connected.") }, // {NotificationType::NewPresetsAviable, NotificationLevel::ImportantNotification, 20, _u8L("New Presets are available."), _u8L("See here.") }, diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index f819702e4..331f29fa0 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -578,7 +578,7 @@ struct Sidebar::priv wxButton *btn_export_gcode; wxButton *btn_reslice; ScalableButton *btn_send_gcode; - ScalableButton *btn_eject_device; + //ScalableButton *btn_eject_device; ScalableButton* btn_export_gcode_removable; //exports to removable drives (appears only if removable drive is connected) bool is_collapsed {false}; @@ -750,13 +750,14 @@ Sidebar::Sidebar(Plater *parent) (*btn)->Hide(); }; - init_scalable_btn(&p->btn_send_gcode , "export_gcode", _L("Send to printer") + "\tCtrl+Shift+G"); - init_scalable_btn(&p->btn_eject_device, "eject_sd" , _L("Remove device") + "\tCtrl+T"); - init_scalable_btn(&p->btn_export_gcode_removable, "export_to_sd", _L("Export to SD card / Flash drive") + "\tCtrl+U"); + init_scalable_btn(&p->btn_send_gcode , "export_gcode", _L("Send to printer ") + GUI::shortkey_ctrl_prefix() + "Shift+G"); +// init_scalable_btn(&p->btn_eject_device, "eject_sd" , _L("Remove device ") + GUI::shortkey_ctrl_prefix() + "T"); + init_scalable_btn(&p->btn_export_gcode_removable, "export_to_sd", _L("Export to SD card / Flash drive ") + GUI::shortkey_ctrl_prefix() + "U"); // regular buttons "Slice now" and "Export G-code" - const int scaled_height = p->btn_eject_device->GetBitmapHeight() + 4; +// const int scaled_height = p->btn_eject_device->GetBitmapHeight() + 4; + const int scaled_height = p->btn_export_gcode_removable->GetBitmapHeight() + 4; auto init_btn = [this](wxButton **btn, wxString label, const int button_height) { *btn = new wxButton(this, wxID_ANY, label, wxDefaultPosition, wxSize(-1, button_height), wxBU_EXACTFIT); @@ -774,7 +775,7 @@ Sidebar::Sidebar(Plater *parent) complect_btns_sizer->Add(p->btn_export_gcode, 1, wxEXPAND); complect_btns_sizer->Add(p->btn_send_gcode); complect_btns_sizer->Add(p->btn_export_gcode_removable); - complect_btns_sizer->Add(p->btn_eject_device); +// complect_btns_sizer->Add(p->btn_eject_device); btns_sizer->Add(p->btn_reslice, 0, wxEXPAND | wxTOP, margin_5); @@ -797,7 +798,7 @@ Sidebar::Sidebar(Plater *parent) p->plater->select_view_3D("Preview"); }); p->btn_send_gcode->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->send_gcode(); }); - p->btn_eject_device->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->eject_drive(); }); +// p->btn_eject_device->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->eject_drive(); }); p->btn_export_gcode_removable->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->export_gcode(true); }); } @@ -940,9 +941,9 @@ void Sidebar::msw_rescale() p->object_info->msw_rescale(); p->btn_send_gcode->msw_rescale(); - p->btn_eject_device->msw_rescale(); +// p->btn_eject_device->msw_rescale(); p->btn_export_gcode_removable->msw_rescale(); - const int scaled_height = p->btn_eject_device->GetBitmap().GetHeight() + 4; + const int scaled_height = p->btn_export_gcode_removable->GetBitmap().GetHeight() + 4; p->btn_export_gcode->SetMinSize(wxSize(-1, scaled_height)); p->btn_reslice ->SetMinSize(wxSize(-1, scaled_height)); @@ -965,7 +966,7 @@ void Sidebar::sys_color_changed() // btn...->msw_rescale() updates icon on button, so use it p->btn_send_gcode->msw_rescale(); - p->btn_eject_device->msw_rescale(); +// p->btn_eject_device->msw_rescale(); p->btn_export_gcode_removable->msw_rescale(); p->scrolled->Layout(); @@ -1268,7 +1269,7 @@ void Sidebar::enable_buttons(bool enable) p->btn_reslice->Enable(enable); p->btn_export_gcode->Enable(enable); p->btn_send_gcode->Enable(enable); - p->btn_eject_device->Enable(enable); +// p->btn_eject_device->Enable(enable); p->btn_export_gcode_removable->Enable(enable); } @@ -1276,8 +1277,8 @@ bool Sidebar::show_reslice(bool show) const { return p->btn_reslice->Sh bool Sidebar::show_export(bool show) const { return p->btn_export_gcode->Show(show); } bool Sidebar::show_send(bool show) const { return p->btn_send_gcode->Show(show); } bool Sidebar::show_export_removable(bool show) const { return p->btn_export_gcode_removable->Show(show); } -bool Sidebar::show_eject(bool show) const { return p->btn_eject_device->Show(show); } -bool Sidebar::get_eject_shown() const { return p->btn_eject_device->IsShown(); } +//bool Sidebar::show_eject(bool show) const { return p->btn_eject_device->Show(show); } +//bool Sidebar::get_eject_shown() const { return p->btn_eject_device->IsShown(); } bool Sidebar::is_multifilament() { @@ -1468,6 +1469,13 @@ bool PlaterDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString &fi return true; } +// State to manage showing after export notifications and device ejecting +enum ExportingStatus{ + NOT_EXPORTING, + EXPORTING_TO_REMOVABLE, + EXPORTING_TO_LOCAL +}; + // Plater / private struct Plater::priv { @@ -1770,8 +1778,9 @@ struct Plater::priv // Caching last value of show_action_buttons parameter for show_action_buttons(), so that a callback which does not know this state will not override it. mutable bool ready_to_slice = { false }; // Flag indicating that the G-code export targets a removable device, therefore the show_action_buttons() needs to be called at any case when the background processing finishes. - bool writing_to_removable_device { false }; - bool show_ExportToRemovableFinished_notification { false }; + ExportingStatus exporting_status { NOT_EXPORTING }; + std::string last_output_path; + std::string last_output_dir_path; bool inside_snapshot_capture() { return m_prevent_snapshots != 0; } bool process_completed_with_error { false }; private: @@ -2043,9 +2052,8 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) }); this->q->Bind(EVT_REMOVABLE_DRIVES_CHANGED, [this, q](RemovableDrivesChangedEvent &) { this->show_action_buttons(this->ready_to_slice); - if (!this->sidebar->get_eject_shown()) { - notification_manager->close_notification_of_type(NotificationType::ExportToRemovableFinished); - } + // Close notification ExportingFinished but only if last export was to removable + notification_manager->device_ejected(); }); // Start the background thread and register this window as a target for update events. wxGetApp().removable_drive_manager()->init(this->q); @@ -2912,6 +2920,7 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool const wxString invalid_str = _L("Invalid data"); for (auto btn : {ActionButtonType::abReslice, ActionButtonType::abSendGCode, ActionButtonType::abExport}) sidebar->set_btn_label(btn, invalid_str); + process_completed_with_error = true; } else { @@ -3511,9 +3520,7 @@ void Plater::priv::on_slicing_completed(wxCommandEvent & evt) void Plater::priv::on_export_began(wxCommandEvent& evt) { if (show_warning_dialog) - warnings_dialog(); - if (this->writing_to_removable_device) - this->show_ExportToRemovableFinished_notification = true; + warnings_dialog(); } void Plater::priv::on_slicing_began() { @@ -3591,10 +3598,14 @@ void Plater::priv::on_process_completed(SlicingProcessCompletedEvent &evt) } else notification_manager->push_slicing_error_notification(message, *q->get_current_canvas3D()); this->statusbar()->set_status_text(from_u8(message)); - const wxString invalid_str = _L("Invalid data"); - for (auto btn : { ActionButtonType::abReslice, ActionButtonType::abSendGCode, ActionButtonType::abExport }) - sidebar->set_btn_label(btn, invalid_str); - process_completed_with_error = true; + if (evt.invalidate_plater()) + { + const wxString invalid_str = _L("Invalid data"); + for (auto btn : { ActionButtonType::abReslice, ActionButtonType::abSendGCode, ActionButtonType::abExport }) + sidebar->set_btn_label(btn, invalid_str); + process_completed_with_error = true; + } + } if (evt.cancelled()) this->statusbar()->set_status_text(_L("Cancelled")); @@ -3629,13 +3640,14 @@ void Plater::priv::on_process_completed(SlicingProcessCompletedEvent &evt) show_action_buttons(false); } // If writing to removable drive was scheduled, show notification with eject button - if (this->writing_to_removable_device && this->show_ExportToRemovableFinished_notification) { + if (exporting_status == ExportingStatus::EXPORTING_TO_REMOVABLE && !this->process_completed_with_error) { show_action_buttons(false); - notification_manager->push_notification(NotificationType::ExportToRemovableFinished, *q->get_current_canvas3D()); - } + notification_manager->push_exporting_finished_notification(*q->get_current_canvas3D(), last_output_path, last_output_dir_path, true); + wxGetApp().removable_drive_manager()->set_exporting_finished(true); + }else if (exporting_status == ExportingStatus::EXPORTING_TO_LOCAL && !this->process_completed_with_error) + notification_manager->push_exporting_finished_notification(*q->get_current_canvas3D(), last_output_path, last_output_dir_path, false); } - this->show_ExportToRemovableFinished_notification = false; - this->writing_to_removable_device = false; + exporting_status = ExportingStatus::NOT_EXPORTING; } void Plater::priv::on_layer_editing_toggled(bool enable) @@ -4306,8 +4318,8 @@ void Plater::priv::show_action_buttons(const bool ready_to_slice) const if (sidebar->show_reslice(false) | sidebar->show_export(true) | sidebar->show_send(send_gcode_shown) | - sidebar->show_export_removable(removable_media_status.has_removable_drives) | - sidebar->show_eject(removable_media_status.has_eject)) + sidebar->show_export_removable(removable_media_status.has_removable_drives)) +// sidebar->show_eject(removable_media_status.has_eject)) sidebar->Layout(); } else @@ -4318,8 +4330,8 @@ void Plater::priv::show_action_buttons(const bool ready_to_slice) const if (sidebar->show_reslice(ready_to_slice) | sidebar->show_export(!ready_to_slice) | sidebar->show_send(send_gcode_shown && !ready_to_slice) | - sidebar->show_export_removable(!ready_to_slice && removable_media_status.has_removable_drives) | - sidebar->show_eject(!ready_to_slice && removable_media_status.has_eject)) + sidebar->show_export_removable(!ready_to_slice && removable_media_status.has_removable_drives)) +// sidebar->show_eject(!ready_to_slice && removable_media_status.has_eject)) sidebar->Layout(); } } @@ -4958,7 +4970,7 @@ void Plater::export_gcode(bool prefer_removable) if (p->model.objects.empty()) return; - if (p->process_completed_with_error)//here + if (p->process_completed_with_error) return; // If possible, remove accents from accented latin characters. @@ -5003,7 +5015,10 @@ void Plater::export_gcode(bool prefer_removable) if (! output_path.empty()) { bool path_on_removable_media = removable_drive_manager.set_and_verify_last_save_path(output_path.string()); - p->writing_to_removable_device = path_on_removable_media; + p->notification_manager->new_export_began(path_on_removable_media); + p->exporting_status = path_on_removable_media ? ExportingStatus::EXPORTING_TO_REMOVABLE : ExportingStatus::EXPORTING_TO_LOCAL; + p->last_output_path = output_path.string(); + p->last_output_dir_path = output_path.parent_path().string(); p->export_gcode(output_path, path_on_removable_media, PrintHostJob()); // Storing a path to AppConfig either as path to removable media or a path to internal media. // is_path_on_removable_drive() is called with the "true" parameter to update its internal database as the user may have shuffled the external drives @@ -5223,6 +5238,10 @@ void Plater::export_toolpaths_to_obj() const void Plater::reslice() { + // There is "invalid data" button instead "slice now" + if (p->process_completed_with_error) + return; + // Stop arrange and (or) optimize rotation tasks. this->stop_jobs(); diff --git a/src/slic3r/GUI/RemovableDriveManager.cpp b/src/slic3r/GUI/RemovableDriveManager.cpp index d865fe347..0f6f5255e 100644 --- a/src/slic3r/GUI/RemovableDriveManager.cpp +++ b/src/slic3r/GUI/RemovableDriveManager.cpp @@ -391,7 +391,6 @@ bool RemovableDriveManager::set_and_verify_last_save_path(const std::string &pat #ifndef REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS this->update(); #endif // REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS - m_last_save_path = this->get_removable_drive_from_path(path); m_exporting_finished = false; return ! m_last_save_path.empty();