diff --git a/resources/icons/notification_cancel.svg b/resources/icons/notification_cancel.svg
new file mode 100644
index 0000000000..d849e24c61
--- /dev/null
+++ b/resources/icons/notification_cancel.svg
@@ -0,0 +1,67 @@
+
+
diff --git a/resources/icons/notification_cancel_hover.svg b/resources/icons/notification_cancel_hover.svg
new file mode 100644
index 0000000000..746d053e48
--- /dev/null
+++ b/resources/icons/notification_cancel_hover.svg
@@ -0,0 +1,67 @@
+
+
diff --git a/src/imgui/imconfig.h b/src/imgui/imconfig.h
index d52294acd2..1ee719288c 100644
--- a/src/imgui/imconfig.h
+++ b/src/imgui/imconfig.h
@@ -123,6 +123,8 @@ namespace ImGui
const char ErrorMarker = 0x11;
const char EjectButton = 0x12;
const char EjectHoverButton = 0x13;
+ const char CancelButton = 0x14;
+ const char CancelHoverButton = 0x15;
// void MyFunction(const char* name, const MyMatrix44& v);
}
diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp
index 4cab8f3db9..6c226cd74d 100644
--- a/src/slic3r/GUI/3DScene.cpp
+++ b/src/slic3r/GUI/3DScene.cpp
@@ -845,6 +845,57 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M
return contained_min_one;
}
+bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, bool& partlyOut, bool& fullyOut)
+{
+ if (config == nullptr)
+ return false;
+
+ const ConfigOptionPoints* opt = dynamic_cast(config->option("bed_shape"));
+ if (opt == nullptr)
+ return false;
+
+ BoundingBox bed_box_2D = get_extents(Polygon::new_scale(opt->values));
+ BoundingBoxf3 print_volume(Vec3d(unscale(bed_box_2D.min(0)), unscale(bed_box_2D.min(1)), 0.0), Vec3d(unscale(bed_box_2D.max(0)), unscale(bed_box_2D.max(1)), config->opt_float("max_print_height")));
+ // Allow the objects to protrude below the print bed
+ print_volume.min(2) = -1e10;
+ print_volume.min(0) -= BedEpsilon;
+ print_volume.min(1) -= BedEpsilon;
+ print_volume.max(0) += BedEpsilon;
+ print_volume.max(1) += BedEpsilon;
+
+ bool contained_min_one = false;
+
+ partlyOut = false;
+ fullyOut = false;
+ for (GLVolume* volume : this->volumes)
+ {
+ if ((volume == nullptr) || volume->is_modifier || (volume->is_wipe_tower && !volume->shader_outside_printer_detection_enabled) || ((volume->composite_id.volume_id < 0) && !volume->shader_outside_printer_detection_enabled))
+ continue;
+
+ const BoundingBoxf3& bb = volume->transformed_convex_hull_bounding_box();
+ bool contained = print_volume.contains(bb);
+
+ volume->is_outside = !contained;
+ if (!volume->printable)
+ continue;
+
+ if (contained)
+ contained_min_one = true;
+
+ if (volume->is_outside) {
+ if (print_volume.intersects(bb))
+ partlyOut = true;
+ else
+ fullyOut = true;
+ }
+ }
+ /*
+ if (out_state != nullptr)
+ *out_state = state;
+ */
+ return contained_min_one;
+}
+
void GLVolumeCollection::reset_outside_state()
{
for (GLVolume* volume : this->volumes)
diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp
index e4d9c60672..2ae2a36b29 100644
--- a/src/slic3r/GUI/3DScene.hpp
+++ b/src/slic3r/GUI/3DScene.hpp
@@ -569,6 +569,7 @@ public:
// returns true if all the volumes are completely contained in the print volume
// returns the containment state in the given out_state, if non-null
bool check_outside_state(const DynamicPrintConfig* config, ModelInstanceEPrintVolumeState* out_state);
+ bool check_outside_state(const DynamicPrintConfig* config, bool& partlyOut, bool& fullyOut);
void reset_outside_state();
void update_colors_by_extruder(const DynamicPrintConfig* config);
diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index a45f61cabd..0a2b5cd655 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -641,6 +641,7 @@ void GLCanvas3D::WarningTexture::activate(WarningTexture::Warning warning, bool
error = true;
break;
}
+ BOOST_LOG_TRIVIAL(error) << state << " : " << text ;
auto ¬ification_manager = *wxGetApp().plater()->get_notification_manager();
if (state) {
if(error)
@@ -1620,9 +1621,6 @@ void GLCanvas3D::render()
wxGetApp().plater()->init_environment_texture();
#endif // ENABLE_ENVIRONMENT_MAP
- m_render_timer.Stop();
- m_extra_frame_requested_delayed = std::numeric_limits::max();
-
const Size& cnv_size = get_canvas_size();
// Probably due to different order of events on Linux/GTK2, when one switched from 3D scene
// to preview, this was called before canvas had its final size. It reported zero width
@@ -1754,7 +1752,7 @@ void GLCanvas3D::render()
m_tooltip.render(m_mouse.position, *this);
wxGetApp().plater()->get_mouse3d_controller().render_settings_dialog(*this);
- wxGetApp().plater()->get_notification_manager()->render_notifications(get_overlay_window_width());
+ wxGetApp().plater()->get_notification_manager()->render_notifications(*this, get_overlay_window_width());
wxGetApp().imgui()->render();
@@ -2238,24 +2236,24 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
// checks for geometry outside the print volume to render it accordingly
if (!m_volumes.empty()) {
- ModelInstanceEPrintVolumeState state;
-
- const bool contained_min_one = m_volumes.check_outside_state(m_config, &state);
+ bool partlyOut = false;
+ bool fullyOut = false;
+ const bool contained_min_one = m_volumes.check_outside_state(m_config, partlyOut, fullyOut);
#if ENABLE_WARNING_TEXTURE_REMOVAL
- _set_warning_notification(EWarning::ObjectClashed, state == ModelInstancePVS_Partly_Outside);
- _set_warning_notification(EWarning::ObjectOutside, state == ModelInstancePVS_Fully_Outside);
- if (printer_technology != ptSLA || state == ModelInstancePVS_Inside)
+ _set_warning_notification(EWarning::ObjectClashed, partlyOut);
+ _set_warning_notification(EWarning::ObjectOutside, fullyOut);
+ if (printer_technology != ptSLA || !contained_min_one)
_set_warning_notification(EWarning::SlaSupportsOutside, false);
#else
- _set_warning_texture(WarningTexture::ObjectClashed, state == ModelInstancePVS_Partly_Outside);
- _set_warning_texture(WarningTexture::ObjectOutside, state == ModelInstancePVS_Fully_Outside);
- if(printer_technology != ptSLA || state == ModelInstancePVS_Inside)
+ _set_warning_texture(WarningTexture::ObjectClashed, partlyOut);
+ _set_warning_texture(WarningTexture::ObjectOutside, fullyOut);
+ if(printer_technology != ptSLA || !contained_min_one)
_set_warning_texture(WarningTexture::SlaSupportsOutside, false);
#endif // ENABLE_WARNING_TEXTURE_REMOVAL
post_event(Event(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS,
- contained_min_one && !m_model->objects.empty() && state != ModelInstancePVS_Partly_Outside));
+ contained_min_one && !m_model->objects.empty() && !partlyOut));
}
else {
#if ENABLE_WARNING_TEXTURE_REMOVAL
@@ -2442,13 +2440,13 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt)
if (!m_initialized)
return;
- // FIXME
m_dirty |= m_main_toolbar.update_items_state();
m_dirty |= m_undoredo_toolbar.update_items_state();
m_dirty |= wxGetApp().plater()->get_view_toolbar().update_items_state();
m_dirty |= wxGetApp().plater()->get_collapse_toolbar().update_items_state();
bool mouse3d_controller_applied = wxGetApp().plater()->get_mouse3d_controller().apply(wxGetApp().plater()->get_camera());
m_dirty |= mouse3d_controller_applied;
+ m_dirty |= wxGetApp().plater()->get_notification_manager()->update_notifications(*this);
if (!m_dirty)
return;
@@ -2982,30 +2980,39 @@ void GLCanvas3D::on_timer(wxTimerEvent& evt)
void GLCanvas3D::on_render_timer(wxTimerEvent& evt)
{
- // If slicer is not top window -> restart timer with one second to try again
- wxWindow* p = dynamic_cast(wxGetApp().plater());
- while (p->GetParent() != nullptr)
- p = p->GetParent();
- wxTopLevelWindow* top_level_wnd = dynamic_cast(p);
- if (!top_level_wnd->IsActive()) {
- request_extra_frame_delayed(1000);
- return;
- }
- //render();
- m_dirty = true;
- wxWakeUpIdle();
+ // no need to do anything here
+ // right after this event is recieved, idle event is fired
+
+ //m_dirty = true;
+ //wxWakeUpIdle();
}
-void GLCanvas3D::request_extra_frame_delayed(int miliseconds)
+
+void GLCanvas3D::schedule_extra_frame(int miliseconds)
{
+ // Schedule idle event right now
+ if (miliseconds == 0)
+ {
+ // We want to wakeup idle evnt but most likely this is call inside render cycle so we need to wait
+ if (m_in_render)
+ miliseconds = 33;
+ else {
+ m_dirty = true;
+ wxWakeUpIdle();
+ return;
+ }
+ }
+ // Start timer
int64_t now = timestamp_now();
+ // Timer is not running
if (! m_render_timer.IsRunning()) {
m_extra_frame_requested_delayed = miliseconds;
m_render_timer.StartOnce(miliseconds);
m_render_timer_start = now;
+ // Timer is running - restart only if new period is shorter than remaning period
} else {
const int64_t remaining_time = (m_render_timer_start + m_extra_frame_requested_delayed) - now;
- if (miliseconds < remaining_time) {
+ if (miliseconds + 20 < remaining_time) {
m_render_timer.Stop();
m_extra_frame_requested_delayed = miliseconds;
m_render_timer.StartOnce(miliseconds);
diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp
index 10294931fe..f4d862b66c 100644
--- a/src/slic3r/GUI/GLCanvas3D.hpp
+++ b/src/slic3r/GUI/GLCanvas3D.hpp
@@ -743,7 +743,8 @@ public:
void msw_rescale();
void request_extra_frame() { m_extra_frame_requested = true; }
- void request_extra_frame_delayed(int miliseconds);
+
+ void schedule_extra_frame(int miliseconds);
int get_main_toolbar_item_id(const std::string& name) const { return m_main_toolbar.get_item_id(name); }
void force_main_toolbar_left_action(int item_id) { m_main_toolbar.force_left_action(item_id, *this); }
diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp
index 2fde30cd13..ecc5f46a7c 100644
--- a/src/slic3r/GUI/GUI_App.cpp
+++ b/src/slic3r/GUI/GUI_App.cpp
@@ -1978,6 +1978,11 @@ wxNotebook* GUI_App::tab_panel() const
return mainframe->m_tabpanel;
}
+NotificationManager* GUI_App::notification_manager()
+{
+ return plater_->get_notification_manager();
+}
+
// extruders count from selected printer preset
int GUI_App::extruders_cnt() const
{
diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp
index 5572c50712..f1ee0746a0 100644
--- a/src/slic3r/GUI/GUI_App.hpp
+++ b/src/slic3r/GUI/GUI_App.hpp
@@ -43,6 +43,7 @@ class ObjectSettings;
class ObjectList;
class ObjectLayers;
class Plater;
+class NotificationManager;
struct GUI_InitParams;
@@ -226,14 +227,14 @@ public:
void MacOpenFiles(const wxArrayString &fileNames) override;
#endif /* __APPLE */
- Sidebar& sidebar();
- ObjectManipulation* obj_manipul();
- ObjectSettings* obj_settings();
- ObjectList* obj_list();
- ObjectLayers* obj_layers();
- Plater* plater();
- Model& model();
-
+ Sidebar& sidebar();
+ ObjectManipulation* obj_manipul();
+ ObjectSettings* obj_settings();
+ ObjectList* obj_list();
+ ObjectLayers* obj_layers();
+ Plater* plater();
+ Model& model();
+ NotificationManager* notification_manager();
// Parameters extracted from the command line to be passed to GUI after initialization.
const GUI_InitParams* init_params { nullptr };
diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp
index 1ed4b492fd..db7af046bf 100644
--- a/src/slic3r/GUI/ImGuiWrapper.cpp
+++ b/src/slic3r/GUI/ImGuiWrapper.cpp
@@ -51,7 +51,9 @@ static const std::map font_icons_large = {
{ImGui::EjectButton , "notification_eject_sd" },
{ImGui::EjectHoverButton , "notification_eject_sd_hover" },
{ImGui::WarningMarker , "notification_warning" },
- {ImGui::ErrorMarker , "notification_error" }
+ {ImGui::ErrorMarker , "notification_error" },
+ {ImGui::CancelButton , "notification_cancel" },
+ {ImGui::CancelHoverButton , "notification_cancel_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 a6fd9cfd3c..266814e09c 100644
--- a/src/slic3r/GUI/NotificationManager.cpp
+++ b/src/slic3r/GUI/NotificationManager.cpp
@@ -5,6 +5,7 @@
#include "Plater.hpp"
#include "GLCanvas3D.hpp"
#include "ImGuiWrapper.hpp"
+#include "PrintHostDialogs.hpp"
#include "wxExtensions.hpp"
@@ -22,9 +23,6 @@ static constexpr float SPACE_RIGHT_PANEL = 10.0f;
static constexpr float FADING_OUT_DURATION = 2.0f;
// Time in Miliseconds after next render when fading out is requested
static constexpr int FADING_OUT_TIMEOUT = 100;
-// If timeout is changed to higher than 1 second, substract_time call should be revorked
-//static constexpr int MAX_TIMEOUT_MILISECONDS = 1000;
-//static constexpr int MAX_TIMEOUT_SECONDS = 1;
namespace Slic3r {
namespace GUI {
@@ -131,35 +129,29 @@ void NotificationManager::NotificationIDProvider::release_id(int) {}
NotificationManager::PopNotification::PopNotification(const NotificationData &n, NotificationIDProvider &id_provider, wxEvtHandler* evt_handler) :
m_data (n)
, m_id_provider (id_provider)
- , m_remaining_time (n.duration)
- , m_last_remaining_time (n.duration)
- , m_counting_down (n.duration != 0)
, m_text1 (n.text1)
, m_hypertext (n.hypertext)
, m_text2 (n.text2)
, m_evt_handler (evt_handler)
, m_notification_start (GLCanvas3D::timestamp_now())
-{
- //init();
-}
+{}
void NotificationManager::PopNotification::render(GLCanvas3D& canvas, float initial_y, bool move_from_overlay, float overlay_width)
{
- if (!m_initialized)
+
+ if (m_state == EState::Unknown)
init();
- if (m_hidden) {
+ if (m_state == EState::Hidden) {
m_top_y = initial_y - GAP_WIDTH;
return;
}
- if (m_fading_out)
- m_last_render_fading = GLCanvas3D::timestamp_now();
-
- Size cnv_size = canvas.get_canvas_size();
+ Size cnv_size = canvas.get_canvas_size();
ImGuiWrapper& imgui = *wxGetApp().imgui();
- ImVec2 mouse_pos = ImGui::GetMousePos();
- float right_gap = SPACE_RIGHT_PANEL + (move_from_overlay ? overlay_width + m_line_height * 5 : 0);
+ ImVec2 mouse_pos = ImGui::GetMousePos();
+ float right_gap = SPACE_RIGHT_PANEL + (move_from_overlay ? overlay_width + m_line_height * 5 : 0);
+ bool fading_pop = false;
if (m_line_height != ImGui::CalcTextSize("A").y)
init();
@@ -174,54 +166,46 @@ void NotificationManager::PopNotification::render(GLCanvas3D& canvas, float init
imgui.set_next_window_size(m_window_width, m_window_height, ImGuiCond_Always);
// find if hovered
- m_hovered = false;
+ if (m_state == EState::Hovered)
+ m_state = EState::Shown;
+
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_hovered = true;
+ m_state = EState::Hovered;
}
-
+
// color change based on fading out
- bool fading_pop = false;
- if (m_fading_out) {
- Notifications_Internal::push_style_color(ImGuiCol_WindowBg, ImGui::GetStyleColorVec4(ImGuiCol_WindowBg), m_fading_out, m_current_fade_opacity);
- Notifications_Internal::push_style_color(ImGuiCol_Text, ImGui::GetStyleColorVec4(ImGuiCol_Text), m_fading_out, m_current_fade_opacity);
+ if (m_state == EState::FadingOut) {
+ Notifications_Internal::push_style_color(ImGuiCol_WindowBg, ImGui::GetStyleColorVec4(ImGuiCol_WindowBg), true, m_current_fade_opacity);
+ Notifications_Internal::push_style_color(ImGuiCol_Text, ImGui::GetStyleColorVec4(ImGuiCol_Text), true, m_current_fade_opacity);
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_fading_out, m_current_fade_opacity);
+ 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_fading_out, m_current_fade_opacity);
+ 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_fading_out, m_current_fade_opacity);
+ Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_state == EState::FadingOut, m_current_fade_opacity);
}
-
- // name of window - probably indentifies window and is shown so last_end add whitespaces according to id
+
+ // name of window indentifies window - has to be unique string
if (m_id == 0)
m_id = m_id_provider.allocate_id();
std::string name = "!!Ntfctn" + std::to_string(m_id);
+
if (imgui.begin(name, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar)) {
ImVec2 win_size = ImGui::GetWindowSize();
- //FIXME: dont forget to us this for texts
- //GUI::format(_utf8(L()));
-
- /*
- //countdown numbers
- ImGui::SetCursorPosX(15);
- ImGui::SetCursorPosY(15);
- imgui.text(std::to_string(m_remaining_time).c_str());
- */
-
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);
@@ -253,6 +237,7 @@ void NotificationManager::PopNotification::count_spaces()
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;
@@ -306,7 +291,7 @@ void NotificationManager::PopNotification::init()
}
if (m_lines_count == 3)
m_multiline = true;
- m_initialized = true;
+ m_state = EState::Shown;
}
void NotificationManager::PopNotification::set_next_window_size(ImGuiWrapper& imgui)
{
@@ -375,12 +360,14 @@ 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] + (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);
- imgui.text(line.c_str());
- cursor_x = x_offset + ImGui::CalcTextSize(line.c_str()).x;
+ if (m_text1.length() > m_endlines[0]) {
+ 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);
+ imgui.text(line.c_str());
+ cursor_x = x_offset + ImGui::CalcTextSize(line.c_str()).x;
+ }
} else {
ImGui::SetCursorPosX(x_offset);
ImGui::SetCursorPosY(cursor_y);
@@ -423,8 +410,8 @@ void NotificationManager::PopNotification::render_hypertext(ImGuiWrapper& imgui,
m_multiline = true;
set_next_window_size(imgui);
}
- else {
- m_close_pending = on_text_click();
+ else if (on_text_click()) {
+ close();
}
}
ImGui::PopStyleColor();
@@ -432,12 +419,12 @@ void NotificationManager::PopNotification::render_hypertext(ImGuiWrapper& imgui,
ImGui::PopStyleColor();
//hover color
- ImVec4 orange_color = ImVec4(.99f, .313f, .0f, 1.0f);//ImGui::GetStyleColorVec4(ImGuiCol_Button);
+ ImVec4 orange_color = ImVec4(.99f, .313f, .0f, 1.0f);
if (ImGui::IsItemHovered(ImGuiHoveredFlags_RectOnly))
orange_color.y += 0.2f;
//text
- Notifications_Internal::push_style_color(ImGuiCol_Text, orange_color, m_fading_out, m_current_fade_opacity);
+ Notifications_Internal::push_style_color(ImGuiCol_Text, orange_color, m_state == EState::FadingOut, m_current_fade_opacity);
ImGui::SetCursorPosX(text_x);
ImGui::SetCursorPosY(text_y);
imgui.text(text.c_str());
@@ -448,7 +435,7 @@ void NotificationManager::PopNotification::render_hypertext(ImGuiWrapper& imgui,
lineEnd.y -= 2;
ImVec2 lineStart = lineEnd;
lineStart.x = ImGui::GetItemRectMin().x;
- ImGui::GetWindowDrawList()->AddLine(lineStart, lineEnd, IM_COL32((int)(orange_color.x * 255), (int)(orange_color.y * 255), (int)(orange_color.z * 255), (int)(orange_color.w * 255.f * (m_fading_out ? m_current_fade_opacity : 1.f))));
+ ImGui::GetWindowDrawList()->AddLine(lineStart, lineEnd, IM_COL32((int)(orange_color.x * 255), (int)(orange_color.y * 255), (int)(orange_color.z * 255), (int)(orange_color.w * 255.f * (m_state == EState::FadingOut ? m_current_fade_opacity : 1.f))));
}
@@ -458,12 +445,11 @@ void NotificationManager::PopNotification::render_close_button(ImGuiWrapper& img
ImVec2 win_pos(win_pos_x, win_pos_y);
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);
+ Notifications_Internal::push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
+ Notifications_Internal::push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(.0f, .0f, .0f, .0f));
- //button - if part if treggered
std::string button_text;
button_text = ImGui::CloseNotifButton;
@@ -479,7 +465,7 @@ void NotificationManager::PopNotification::render_close_button(ImGuiWrapper& img
ImGui::SetCursorPosY(win_size.y / 2 - button_size.y);
if (imgui.button(button_text.c_str(), button_size.x, button_size.y))
{
- m_close_pending = true;
+ close();
}
//invisible large button
@@ -487,7 +473,7 @@ void NotificationManager::PopNotification::render_close_button(ImGuiWrapper& img
ImGui::SetCursorPosY(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;
+ close();
}
ImGui::PopStyleColor();
ImGui::PopStyleColor();
@@ -510,9 +496,9 @@ void NotificationManager::PopNotification::render_minimize_button(ImGuiWrapper&
{
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f));
- Notifications_Internal::push_style_color(ImGuiCol_ButtonActive, ImGui::GetStyleColorVec4(ImGuiCol_WindowBg), m_fading_out, m_current_fade_opacity);
- 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);
+ Notifications_Internal::push_style_color(ImGuiCol_ButtonActive, ImGui::GetStyleColorVec4(ImGuiCol_WindowBg), m_state == EState::FadingOut, m_current_fade_opacity);
+ Notifications_Internal::push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
+ Notifications_Internal::push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
//button - if part if treggered
@@ -564,71 +550,62 @@ bool NotificationManager::PopNotification::compare_text(const std::string& text)
return false;
}
-void NotificationManager::PopNotification::update_state()
+bool NotificationManager::PopNotification::update_state(bool paused, const int64_t delta)
{
- if (!m_initialized)
- init();
m_next_render = std::numeric_limits::max();
- if (m_hidden) {
- m_state = EState::Hidden;
- return;
+ if (m_state == EState::Unknown) {
+ init();
+ return true;
+ }
+
+ if (m_state == EState::Hidden) {
+ return false;
}
int64_t now = GLCanvas3D::timestamp_now();
- if (m_hovered) {
- // reset fading
- m_fading_out = false;
+ // reset timers - hovered state is set in render
+ if (m_state == EState::Hovered) {
m_current_fade_opacity = 1.0f;
- m_remaining_time = m_data.duration;
m_notification_start = now;
- }
-
-
-
- if (m_counting_down) {
+ // Timers when not fading
+ } else if (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;
+ m_fading_start = now;
+ } else {
+ m_next_render = m_data.duration * 1000 - up_time;
+ }
+ }
+ // Timers when fading
+ if (m_state == EState::FadingOut && !paused) {
+ int64_t curr_time = now - m_fading_start;
+ int64_t next_render = FADING_OUT_TIMEOUT - delta;
+ m_current_fade_opacity = std::clamp(1.0f - 0.001f * static_cast(curr_time) / FADING_OUT_DURATION, 0.0f, 1.0f);
+ if (m_current_fade_opacity <= 0.0f) {
+ m_state = EState::Finished;
+ return true;
+ } else if (next_render <= 20) {
+ m_next_render = FADING_OUT_TIMEOUT;
+ return true;
+ } else {
+ m_next_render = next_render;
+ return false;
+ }
+ }
- if (m_fading_out && m_current_fade_opacity <= 0.0f)
- m_finished = true;
- else if (!m_fading_out && /*m_remaining_time <=0*/up_time >= m_data.duration * 1000) {
- m_fading_out = true;
- m_fading_start = now;
- m_last_render_fading = now;
- } else if (!m_fading_out) {
- m_next_render = m_data.duration * 1000 - up_time;//std::min(/*m_data.duration * 1000 - up_time*/m_remaining_time * 1000, MAX_TIMEOUT_MILISECONDS);
- }
-
+ if (m_state == EState::Finished) {
+ return true;
}
-
- if (m_finished) {
+
+ if (m_state == EState::ClosePending) {
m_state = EState::Finished;
- m_next_render = 0;
- return;
- }
- if (m_close_pending) {
- m_finished = true;
- m_state = EState::ClosePending;
- m_next_render = 0;
- return;
- }
- if (m_fading_out) {
- if (!m_paused) {
- m_state = EState::FadingOutStatic;
- int64_t curr_time = now - m_fading_start;
- int64_t no_render_time = now - m_last_render_fading;
- m_current_fade_opacity = std::clamp(1.0f - 0.001f * static_cast(curr_time) / FADING_OUT_DURATION, 0.0f, 1.0f);
- auto next_render = FADING_OUT_TIMEOUT - no_render_time;
- if (next_render <= 0) {
- //m_last_render_fading = GLCanvas3D::timestamp_now();
- m_state = EState::FadingOutRender;
- m_next_render = 0;
- } else
- m_next_render = next_render;
- }
+ return true;
}
+ return false;
}
NotificationManager::SlicingCompleteLargeNotification::SlicingCompleteLargeNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler, bool large) :
@@ -672,9 +649,10 @@ void NotificationManager::SlicingCompleteLargeNotification::set_print_info(const
void NotificationManager::SlicingCompleteLargeNotification::set_large(bool l)
{
m_is_large = l;
- m_counting_down = !l;
+ //FIXME this information should not be lost (change m_data?)
+// m_counting_down = !l;
m_hypertext = l ? _u8L("Export G-Code.") : std::string();
- m_hidden = !l;
+ m_state = l ? EState::Shown : EState::Hidden;
}
//---------------ExportFinishedNotification-----------
void NotificationManager::ExportFinishedNotification::count_spaces()
@@ -733,8 +711,8 @@ void NotificationManager::ExportFinishedNotification::render_eject_button(ImGuiW
ImVec2 win_pos(win_pos_x, win_pos_y);
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);
+ Notifications_Internal::push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
+ Notifications_Internal::push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(.0f, .0f, .0f, .0f));
std::string button_text;
@@ -768,7 +746,7 @@ void NotificationManager::ExportFinishedNotification::render_eject_button(ImGuiW
assert(m_evt_handler != nullptr);
if (m_evt_handler != nullptr)
wxPostEvent(m_evt_handler, EjectDriveNotificationClickedEvent(EVT_EJECT_DRIVE_NOTIFICAION_CLICKED));
- m_close_pending = true;
+ close();
}
//invisible large button
@@ -779,7 +757,7 @@ void NotificationManager::ExportFinishedNotification::render_eject_button(ImGuiW
assert(m_evt_handler != nullptr);
if (m_evt_handler != nullptr)
wxPostEvent(m_evt_handler, EjectDriveNotificationClickedEvent(EVT_EJECT_DRIVE_NOTIFICAION_CLICKED));
- m_close_pending = true;
+ close();
}
ImGui::PopStyleColor();
ImGui::PopStyleColor();
@@ -799,30 +777,157 @@ void NotificationManager::ProgressBarNotification::init()
m_lines_count++;
m_endlines.push_back(m_endlines.back());
}
+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;
+ }
+ 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)
{
- PopNotification::render_text(imgui, win_size_x, win_size_y, win_pos_x, 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);
+
}
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);
- float invisible_length = 0;//((float)(m_data.duration - m_remaining_time) / (float)m_data.duration * win_size_x);
- //invisible_length -= win_size_x / ((float)m_data.duration * 60.f) * (60 - m_countdown_frame);
- ImVec2 lineEnd = ImVec2(win_pos_x - invisible_length - m_window_width_offset, win_pos_y + win_size_y/2 + m_line_height / 2);
- ImVec2 lineStart = ImVec2(win_pos_x - win_size_x + m_left_indentation, win_pos_y + win_size_y/2 + m_line_height / 2);
- ImGui::GetWindowDrawList()->AddLine(lineStart, lineEnd, 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.7f);
- /*
- //countdown line
- ImVec4 orange_color = ImGui::GetStyleColorVec4(ImGuiCol_Button);
- float invisible_length = ((float)(m_data.duration - m_remaining_time) / (float)m_data.duration * win_size_x);
- invisible_length -= win_size_x / ((float)m_data.duration * 60.f) * (60 - m_countdown_frame);
- ImVec2 lineEnd = ImVec2(win_pos_x - invisible_length, win_pos_y + win_size_y - 5);
- ImVec2 lineStart = ImVec2(win_pos_x - win_size_x, win_pos_y + win_size_y - 5);
- ImGui::GetWindowDrawList()->AddLine(lineStart, lineEnd, IM_COL32((int)(orange_color.x * 255), (int)(orange_color.y * 255), (int)(orange_color.z * 255), (int)(orange_color.picture_width * 255.f * (m_fading_out ? m_current_fade_opacity : 1.f))), 2.f);
- if (!m_paused)
- m_countdown_frame++;
- */
+ 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 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);
+}
+//------PrintHostUploadNotification----------------
+void NotificationManager::PrintHostUploadNotification::set_percentage(float percent)
+{
+ if (m_uj_state == UploadJobState::PB_CANCELLED)
+ return;
+ m_percentage = percent;
+ if (percent >= 1.0f) {
+ m_uj_state = UploadJobState::PB_COMPLETED;
+ m_has_cancel_button = false;
+ } else if (percent < 0.0f) {
+ error();
+ } else {
+ m_uj_state = UploadJobState::PB_PROGRESS;
+ m_has_cancel_button = true;
+ }
+}
+void NotificationManager::PrintHostUploadNotification::render_bar(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
+{
+ std::string text;
+ switch (m_uj_state) {
+ 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;
+ std::stringstream stream;
+ 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*/);
+ 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);
+ 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);
+ 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);
+ break;
+ }
+
+ 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);
+ ImVec2 win_pos(win_pos_x, win_pos_y);
+ 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_state == EState::FadingOut, m_current_fade_opacity);
+ Notifications_Internal::push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
+ ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(.0f, .0f, .0f, .0f));
+
+ std::string button_text;
+ button_text = ImGui::CancelButton;
+
+ if (ImGui::IsMouseHoveringRect(ImVec2(win_pos.x - m_line_height * 5.f, win_pos.y),
+ ImVec2(win_pos.x - m_line_height * 2.5f, win_pos.y + win_size.y),
+ true))
+ {
+ button_text = ImGui::CancelHoverButton;
+ // 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("Cancel upload") + " " + GUI::shortkey_ctrl_prefix() + "T");
+ 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 * 5.0f);
+ ImGui::SetCursorPosY(win_size.y / 2 - button_size.y);
+ if (imgui.button(button_text.c_str(), button_size.x, button_size.y))
+ {
+ assert(m_evt_handler != nullptr);
+ if (m_evt_handler != nullptr) {
+ auto evt = new PrintHostQueueDialog::Event(GUI::EVT_PRINTHOST_CANCEL, m_job_id, m_job_id);
+ wxQueueEvent(m_evt_handler, evt);
+ }
+ }
+
+ //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) {
+ auto evt = new PrintHostQueueDialog::Event(GUI::EVT_PRINTHOST_CANCEL, m_job_id, m_job_id);
+ wxQueueEvent(m_evt_handler, evt);
+ }
+ }
+ ImGui::PopStyleColor();
+ ImGui::PopStyleColor();
+ ImGui::PopStyleColor();
+ ImGui::PopStyleColor();
+ ImGui::PopStyleColor();
+
}
//------NotificationManager--------
NotificationManager::NotificationManager(wxEvtHandler* evt_handler) :
@@ -881,12 +986,7 @@ void NotificationManager::push_plater_error_notification(const std::string& text
{
push_notification_data({ NotificationType::PlaterError, NotificationLevel::ErrorNotification, 0, _u8L("ERROR:") + "\n" + text }, 0);
}
-void NotificationManager::push_plater_warning_notification(const std::string& text)
-{
- push_notification_data({ NotificationType::PlaterWarning, NotificationLevel::WarningNotification, 0, _u8L("WARNING:") + "\n" + text }, 0);
- // dissaper if in preview
- set_in_preview(m_in_preview);
-}
+
void NotificationManager::close_plater_error_notification(const std::string& text)
{
for (std::unique_ptr ¬ification : m_pop_notifications) {
@@ -895,11 +995,32 @@ void NotificationManager::close_plater_error_notification(const std::string& tex
}
}
}
+
+void NotificationManager::push_plater_warning_notification(const std::string& text)
+{
+ // Find if was not hidden
+ for (std::unique_ptr& notification : m_pop_notifications) {
+ if (notification->get_type() == NotificationType::PlaterWarning && notification->compare_text(_u8L("WARNING:") + "\n" + text)) {
+ if (notification->get_state() == PopNotification::EState::Hidden) {
+ //dynamic_cast(notification.get())->show();
+ return;
+ }
+ }
+ }
+
+ NotificationData data{ NotificationType::PlaterWarning, NotificationLevel::WarningNotification, 0, _u8L("WARNING:") + "\n" + text };
+
+ auto notification = std::make_unique(data, m_id_provider, m_evt_handler);
+ push_notification_data(std::move(notification), 0);
+ // dissaper if in preview
+ set_in_preview(m_in_preview);
+}
+
void NotificationManager::close_plater_warning_notification(const std::string& text)
{
for (std::unique_ptr ¬ification : m_pop_notifications) {
if (notification->get_type() == NotificationType::PlaterWarning && notification->compare_text(_u8L("WARNING:") + "\n" + text)) {
- notification->close();
+ dynamic_cast(notification.get())->real_close();
}
}
}
@@ -997,23 +1118,50 @@ void NotificationManager::push_exporting_finished_notification(const std::string
NotificationData data{ NotificationType::ExportFinished, NotificationLevel::RegularNotification, on_removable ? 0 : 20, _u8L("Exporting finished.") + "\n" + path };
push_notification_data(std::make_unique(data, m_id_provider, m_evt_handler, on_removable, path, dir_path), 0);
}
-void NotificationManager::push_progress_bar_notification(const std::string& text, float percentage)
+
+void NotificationManager::push_upload_job_notification(wxEvtHandler* evt_handler, int id, float filesize, const std::string& filename, const std::string& host, float percentage)
{
- NotificationData data{ NotificationType::ProgressBar, NotificationLevel::ProgressBarNotification, 0, text };
- push_notification_data(std::make_unique(data, m_id_provider, m_evt_handler, 0), 0);
+ std::string text = PrintHostUploadNotification::get_upload_job_text(id, filename, host);
+ NotificationData data{ NotificationType::PrintHostUpload, NotificationLevel::ProgressBarNotification, 0, text };
+ push_notification_data(std::make_unique(data, m_id_provider, evt_handler, 0, id, filesize), 0);
}
-void NotificationManager::set_progress_bar_percentage(const std::string& text, float percentage)
+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);
bool found = false;
for (std::unique_ptr& notification : m_pop_notifications) {
if (notification->get_type() == NotificationType::ProgressBar && notification->compare_text(text)) {
- dynamic_cast(notification.get())->set_percentage(percentage);
- wxGetApp().plater()->get_current_canvas3D()->request_extra_frame();
+ dynamic_cast(notification.get())->set_percentage(percentage);
+ wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0);
found = true;
}
}
+ /*
if (!found) {
- push_progress_bar_notification(text, percentage);
+ push_upload_job_notification(id, filename, host, percentage);
+ }
+ */
+}
+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& notification : m_pop_notifications) {
+ if (notification->get_type() == NotificationType::PrintHostUpload && notification->compare_text(text)) {
+ dynamic_cast(notification.get())->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& notification : m_pop_notifications) {
+ if (notification->get_type() == NotificationType::PrintHostUpload && notification->compare_text(text)) {
+ dynamic_cast(notification.get())->error();
+ wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0);
+ break;
+ }
}
}
bool NotificationManager::push_notification_data(const NotificationData& notification_data, int timestamp)
@@ -1035,20 +1183,19 @@ bool NotificationManager::push_notification_data(std::unique_ptractivate_existing(notification.get())) {
m_pop_notifications.back()->update(notification->get_data());
- canvas.request_extra_frame_delayed(33);
+ canvas.schedule_extra_frame(0);
return false;
} else {
m_pop_notifications.emplace_back(std::move(notification));
- canvas.request_extra_frame_delayed(33);
+ canvas.schedule_extra_frame(0);
return true;
}
}
-void NotificationManager::render_notifications(float overlay_width)
+void NotificationManager::render_notifications(GLCanvas3D& canvas, float overlay_width)
{
sort_notifications();
-
- GLCanvas3D& canvas = *wxGetApp().plater()->get_current_canvas3D();
+
float last_y = 0.0f;
for (const auto& notification : m_pop_notifications) {
@@ -1059,7 +1206,49 @@ void NotificationManager::render_notifications(float overlay_width)
}
}
- update_notifications();
+ m_last_render = GLCanvas3D::timestamp_now();
+}
+
+bool NotificationManager::update_notifications(GLCanvas3D& canvas)
+{
+ // no update if not top window
+ wxWindow* p = dynamic_cast(wxGetApp().plater());
+ while (p->GetParent() != nullptr)
+ p = p->GetParent();
+ wxTopLevelWindow* top_level_wnd = dynamic_cast(p);
+ if (!top_level_wnd->IsActive())
+ return false;
+
+ // next_render() returns numeric_limits::max if no need for frame
+ const int64_t max = std::numeric_limits::max();
+ int64_t next_render = max;
+ const int64_t time_since_render = GLCanvas3D::timestamp_now() - m_last_render;
+ bool request_render = false;
+ // During render, each notification detects if its currently hovered and changes its state to EState::Hovered
+ // If any notification is hovered, all restarts its countdown
+ bool hover = false;
+ for (const std::unique_ptr& notification : m_pop_notifications) {
+ if (notification->is_hovered()) {
+ hover = true;
+ break;
+ }
+ }
+ // update state of all notif and erase finished
+ for (auto it = m_pop_notifications.begin(); it != m_pop_notifications.end();) {
+ std::unique_ptr& notification = *it;
+ request_render |= notification->update_state(hover, time_since_render);
+ next_render = std::min(next_render, notification->next_render());
+ if (notification->get_state() == PopNotification::EState::Finished)
+ it = m_pop_notifications.erase(it);
+ else
+ ++it;
+ }
+
+ // request next frame in future
+ if (next_render < max)
+ canvas.schedule_extra_frame(int(next_render));
+
+ return request_render;
}
void NotificationManager::sort_notifications()
@@ -1080,7 +1269,8 @@ bool NotificationManager::activate_existing(const NotificationManager::PopNotifi
const std::string &new_text = notification->get_data().text1;
for (auto it = m_pop_notifications.begin(); it != m_pop_notifications.end(); ++it) {
if ((*it)->get_type() == new_type && !(*it)->is_finished()) {
- if (new_type == NotificationType::CustomNotification || new_type == NotificationType::PlaterWarning) {
+ if (std::find(m_multiple_types.begin(), m_multiple_types.end(), new_type) != m_multiple_types.end()) {
+ //if (new_type == NotificationType::CustomNotification || new_type == NotificationType::PlaterWarning || new_type == NotificationType::ProgressBar) {
if (!(*it)->compare_text(new_text))
continue;
} else if (new_type == NotificationType::SlicingWarning) {
@@ -1112,103 +1302,6 @@ void NotificationManager::set_in_preview(bool preview)
}
}
-void NotificationManager::update_notifications()
-{
- // no update if not top window
- wxWindow* p = dynamic_cast(wxGetApp().plater());
- while (p->GetParent() != nullptr)
- p = p->GetParent();
- wxTopLevelWindow* top_level_wnd = dynamic_cast(p);
- if (!top_level_wnd->IsActive())
- return;
-
- //static size_t last_size = m_pop_notifications.size();
-
- //request frames
- int64_t next_render = std::numeric_limits::max();
- for (auto it = m_pop_notifications.begin(); it != m_pop_notifications.end();) {
- std::unique_ptr& notification = *it;
- notification->set_paused(m_hovered);
- notification->update_state();
- next_render = std::min(next_render, notification->next_render());
- if (notification->get_state() == PopNotification::EState::Finished)
- it = m_pop_notifications.erase(it);
- else {
-
- ++it;
- }
- }
- /*
- m_requires_update = false;
- for (const std::unique_ptr& notification : m_pop_notifications) {
- if (notification->requires_update()) {
- m_requires_update = true;
- break;
- }
- }
- */
- // update hovering state
- m_hovered = false;
- for (const std::unique_ptr& notification : m_pop_notifications) {
- if (notification->is_hovered()) {
- m_hovered = true;
- break;
- }
- }
-
- /*
- // Reuire render if some notification was just deleted.
- size_t curr_size = m_pop_notifications.size();
- m_requires_render = m_hovered || (last_size != curr_size);
- last_size = curr_size;
-
- // Ask notification if it needs render
- if (!m_requires_render) {
- for (const std::unique_ptr& notification : m_pop_notifications) {
- if (notification->requires_render()) {
- m_requires_render = true;
- break;
- }
- }
- }
- // Make sure there will be update after last notification erased
- if (m_requires_render)
- m_requires_update = true;
- */
-
-
- if (next_render == 0)
- wxGetApp().plater()->get_current_canvas3D()->request_extra_frame_delayed(33); //few milliseconds to get from GLCanvas::render
- else if (next_render < std::numeric_limits::max())
- wxGetApp().plater()->get_current_canvas3D()->request_extra_frame_delayed(int(next_render));
-
- /*
- // actualizate timers
- wxWindow* p = dynamic_cast(wxGetApp().plater());
- while (p->GetParent() != nullptr)
- p = p->GetParent();
- wxTopLevelWindow* top_level_wnd = dynamic_cast(p);
- if (!top_level_wnd->IsActive())
- return;
-
- {
- // Control the fade-out.
- // time in seconds
- long now = wxGetLocalTime();
- // Pausing fade-out when the mouse is over some notification.
- if (!m_hovered && m_last_time < now) {
- if (now - m_last_time >= MAX_TIMEOUT_SECONDS) {
- for (auto& notification : m_pop_notifications) {
- //if (notification->get_state() != PopNotification::EState::Static)
- notification->substract_remaining_time(MAX_TIMEOUT_SECONDS);
- }
- m_last_time = now;
- }
- }
- }
- */
-}
-
bool NotificationManager::has_slicing_error_notification()
{
return std::any_of(m_pop_notifications.begin(), m_pop_notifications.end(), [](auto &n) {
diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp
index 62c4ea845e..4b32a716ff 100644
--- a/src/slic3r/GUI/NotificationManager.hpp
+++ b/src/slic3r/GUI/NotificationManager.hpp
@@ -66,6 +66,8 @@ enum class NotificationType
PlaterWarning,
// Progress bar instead of text.
ProgressBar,
+ // Progress bar with info from Print Host Upload Queue dialog.
+ PrintHostUpload,
// Notification, when Color Change G-code is empty and user try to add color change on DoubleSlider.
EmptyColorChangeCode,
// Notification that custom supports/seams were deleted after mesh repair.
@@ -140,21 +142,24 @@ public:
// Exporting finished, show this information with path, button to open containing folder and if ejectable - eject button
void push_exporting_finished_notification(const std::string& path, const std::string& dir_path, bool on_removable);
// notification with progress bar
- void push_progress_bar_notification(const std::string& text, float percentage = 0);
- void set_progress_bar_percentage(const std::string& text, float percentage);
+ void push_upload_job_notification(wxEvtHandler* evt_handler, int id, float filesize, const std::string& filename, const std::string& host, float percentage = 0);
+ void set_upload_job_notification_percentage(int id, const std::string& filename, const std::string& host, float percentage);
+ void upload_job_notification_show_canceled(int id, const std::string& filename, const std::string& host);
+ void upload_job_notification_show_error(int id, const std::string& filename, const std::string& host);
// 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(float overlay_width);
+ 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.
void set_move_from_overlay(bool move) { m_move_from_overlay = move; }
-
+ // perform update_state on each notification and ask for more frames if needed, return true for render needed
+ bool update_notifications(GLCanvas3D& canvas);
private:
// duration 0 means not disapearing
struct NotificationData {
@@ -192,23 +197,24 @@ private:
enum class EState
{
- Unknown,
+ Unknown, // NOT initialized
Hidden,
- FadingOutRender, // Requesting Render
- FadingOutStatic,
+ Shown, // Requesting Render at some time if duration != 0
+ FadingOut, // Requesting Render at some time
ClosePending, // Requesting Render
Finished, // Requesting Render
+ Hovered, // Followed by Shown
+ Paused
};
PopNotification(const NotificationData &n, NotificationIDProvider &id_provider, wxEvtHandler* evt_handler);
virtual ~PopNotification() { if (m_id) m_id_provider.release_id(m_id); }
void render(GLCanvas3D& canvas, float initial_y, bool move_from_overlay, float overlay_width);
// close will dissapear notification on next render
- void close() { m_close_pending = true; }
+ virtual void close() { m_state = EState::ClosePending; }
// data from newer notification of same type
void update(const NotificationData& n);
- bool is_finished() const { return m_finished || m_close_pending; }
- bool is_hovered() const { return m_hovered; }
+ bool is_finished() const { return m_state == EState::ClosePending || m_state == EState::Finished; }
// returns top after movement
float get_top() const { return m_top_y; }
//returns top in actual frame
@@ -216,23 +222,17 @@ private:
const NotificationType get_type() const { return m_data.type; }
const NotificationData get_data() const { return m_data; }
const bool is_gray() const { return m_is_gray; }
- // Call equals one second down
- void substract_remaining_time(int seconds) { m_remaining_time -= seconds; }
void set_gray(bool g) { m_is_gray = g; }
- void set_paused(bool p) { m_paused = p; }
bool compare_text(const std::string& text);
- void hide(bool h) { m_hidden = h; }
- // sets m_next_render with time of next mandatory rendering
- void update_state();
- int64_t next_render() const { return m_next_render; }
- /*
- bool requires_render() const { return m_state == EState::FadingOutRender || m_state == EState::ClosePending || m_state == EState::Finished; }
- bool requires_update() const { return m_state != EState::Hidden; }
- */
- EState get_state() const { return m_state; }
- protected:
+ void hide(bool h) { 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);
+ 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; }
+
// Call after every size change
- void init();
+ virtual void init();
// Part of init()
virtual void count_spaces();
// Calculetes correct size but not se it in imgui!
@@ -254,45 +254,36 @@ private:
// Hypertext action, returns true if notification should close.
// Action is stored in NotificationData::callback as std::function
virtual bool on_text_click();
-
+ protected:
const NotificationData m_data;
-
// For reusing ImGUI windows.
NotificationIDProvider &m_id_provider;
+ int m_id{ 0 };
+ // State for rendering
EState m_state { EState::Unknown };
- int m_id { 0 };
- bool m_initialized { false };
+ // Time values for rendering fade-out
+
+ int64_t m_fading_start{ 0LL };
+
+ // first appereance of notification or last hover;
+ int64_t m_notification_start;
+ // time to next must-do render
+ int64_t m_next_render{ std::numeric_limits::max() };
+ float m_current_fade_opacity{ 1.0f };
+
+ // Notification data
+
// Main text
std::string m_text1;
// Clickable text
std::string m_hypertext;
// Aditional text after hypertext - currently not used
std::string m_text2;
- // Countdown variables
- long m_remaining_time;
- bool m_counting_down;
- long m_last_remaining_time;
- bool m_paused { false };
- int m_countdown_frame { 0 };
- bool m_fading_out { false };
- int64_t m_fading_start { 0LL };
- // time of last done render when fading
- int64_t m_last_render_fading { 0LL };
- // first appereance of notification or last hover;
- int64_t m_notification_start;
- // time to next must-do render
- int64_t m_next_render { std::numeric_limits::max() };
- float m_current_fade_opacity { 1.0f };
- // If hidden the notif is alive but not visible to user
- bool m_hidden { false };
- // m_finished = true - does not render, marked to delete
- bool m_finished { false };
- // Will go to m_finished next render
- bool m_close_pending { false };
- bool m_hovered { false };
- // variables to count positions correctly
+
+ // inner variables to position notification window, texts and buttons correctly
+
// all space without text
float m_window_width_offset;
// Space on left side without text
@@ -302,9 +293,7 @@ private:
float m_window_width { 450.0f };
//Distance from bottom of notifications to top of this notification
float m_top_y { 0.0f };
-
- // Height of text
- // Used as basic scaling unit!
+ // Height of text - Used as basic scaling unit!
float m_line_height;
std::vector m_endlines;
// Gray are f.e. eorrors when its uknown if they are still valid
@@ -344,21 +333,78 @@ private:
int warning_step;
};
+ class PlaterWarningNotification : public PopNotification
+ {
+ public:
+ PlaterWarningNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler) : PopNotification(n, id_provider, evt_handler) {}
+ virtual void close() { m_state = EState::Hidden; }
+ void real_close() { m_state = EState::ClosePending; }
+ void show() { m_state = EState::Unknown; }
+ };
+
+
class ProgressBarNotification : public PopNotification
{
public:
+
ProgressBarNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler, float percentage) : PopNotification(n, id_provider, evt_handler) { set_percentage(percentage); }
- void set_percentage(float percent) { m_percentage = percent; if (percent >= 1.0f) m_progress_complete = true; else m_progress_complete = false; }
+ virtual void set_percentage(float percent) { m_percentage = percent; }
protected:
- virtual void init();
+ virtual void init() override;
+ 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);
- 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);
- bool m_progress_complete{ false };
- float m_percentage;
+ const float win_size_x, const float win_size_y,
+ const float win_pos_x, const float win_pos_y);
+ 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);
+ 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)
+ {}
+ float m_percentage;
+
+ bool m_has_cancel_button {false};
+ // local time of last hover for showing tooltip
+
+ };
+
+
+
+ class PrintHostUploadNotification : public ProgressBarNotification
+ {
+ public:
+ enum class UploadJobState
+ {
+ PB_PROGRESS,
+ PB_ERROR,
+ PB_CANCELLED,
+ PB_COMPLETED
+ };
+ PrintHostUploadNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler, float percentage, int job_id, float filesize)
+ :ProgressBarNotification(n, id_provider, evt_handler, percentage)
+ , m_job_id(job_id)
+ , m_file_size(filesize)
+ {
+ m_has_cancel_button = true;
+ }
+ 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);
+ 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; }
+ protected:
+ 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);
+ 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);
+ // Identifies job in cancel callback
+ int m_job_id;
+ // Size of uploaded size to be displayed in MB
+ float m_file_size;
+ long m_hover_time{ 0 };
+ UploadJobState m_uj_state{ UploadJobState::PB_PROGRESS };
};
class ExportFinishedNotification : public PopNotification
@@ -405,32 +451,25 @@ private:
void sort_notifications();
// If there is some error notification active, then the "Export G-code" notification after the slicing is finished is suppressed.
bool has_slicing_error_notification();
- // perform update_state on each notification and ask for more frames if needed
- void update_notifications();
-
+
// Target for wxWidgets events sent by clicking on the hyperlink available at some notifications.
wxEvtHandler* m_evt_handler;
// Cache of IDs to identify and reuse ImGUI windows.
NotificationIDProvider m_id_provider;
std::deque> m_pop_notifications;
- // Last render time in seconds for fade out control.
- long m_last_time { 0 };
- // When mouse hovers over some notification, the fade-out of all notifications is suppressed.
- bool m_hovered { false };
//timestamps used for slicing finished - notification could be gone so it needs to be stored here
std::unordered_set m_used_timestamps;
// True if G-code preview is active. False if the Plater is active.
bool m_in_preview { false };
// True if the layer editing is enabled in Plater, so that the notifications are shifted left of it.
bool m_move_from_overlay { false };
-
+ // Timestamp of last rendering
+ int64_t m_last_render { 0LL };
+ // Notification types that can be shown multiple types at once (compared by text)
+ const std::vector m_multiple_types = { NotificationType::CustomNotification, NotificationType::PlaterWarning, NotificationType::ProgressBar, NotificationType::PrintHostUpload };
//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::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.") },
{NotificationType::PresetUpdateAvailable, NotificationLevel::ImportantNotification, 20, _u8L("Configuration update is available."), _u8L("See more."),
[](wxEvtHandler* evnthndlr) {
if (evnthndlr != nullptr)
diff --git a/src/slic3r/GUI/PrintHostDialogs.cpp b/src/slic3r/GUI/PrintHostDialogs.cpp
index a094b70e9a..c8df141e9f 100644
--- a/src/slic3r/GUI/PrintHostDialogs.cpp
+++ b/src/slic3r/GUI/PrintHostDialogs.cpp
@@ -25,6 +25,7 @@
#include "wxExtensions.hpp"
#include "MainFrame.hpp"
#include "libslic3r/AppConfig.hpp"
+#include "NotificationManager.hpp"
namespace fs = boost::filesystem;
@@ -280,6 +281,8 @@ void PrintHostQueueDialog::append_job(const PrintHostJob &job)
job_list->AppendItem(fields, static_cast(ST_NEW));
// Both strings are UTF-8 encoded.
upload_names.emplace_back(job.printhost->get_host(), job.upload_data.upload_path.string());
+
+ //wxGetApp().notification_manager()->push_upload_job_notification(this, job_list->GetItemCount(), 0, job.upload_data.upload_path.string(), job.printhost->get_host());
}
void PrintHostQueueDialog::on_dpi_changed(const wxRect &suggested_rect)
@@ -345,6 +348,14 @@ void PrintHostQueueDialog::on_progress(Event &evt)
}
on_list_select();
+
+ if (evt.progress > 0)
+ {
+ wxVariant nm, hst;
+ job_list->GetValue(nm, evt.job_id, COL_FILENAME);
+ job_list->GetValue(hst, evt.job_id, COL_HOST);
+ wxGetApp().notification_manager()->set_upload_job_notification_percentage(evt.job_id + 1, boost::nowide::narrow(nm.GetString()), boost::nowide::narrow(hst.GetString()), 100 / evt.progress);
+ }
}
void PrintHostQueueDialog::on_error(Event &evt)
@@ -360,6 +371,11 @@ void PrintHostQueueDialog::on_error(Event &evt)
on_list_select();
GUI::show_error(nullptr, errormsg);
+
+ wxVariant nm, hst;
+ job_list->GetValue(nm, evt.job_id, COL_FILENAME);
+ job_list->GetValue(hst, evt.job_id, COL_HOST);
+ wxGetApp().notification_manager()->upload_job_notification_show_error(evt.job_id + 1, boost::nowide::narrow(nm.GetString()), boost::nowide::narrow(hst.GetString()));
}
void PrintHostQueueDialog::on_cancel(Event &evt)
@@ -370,6 +386,11 @@ void PrintHostQueueDialog::on_cancel(Event &evt)
job_list->SetValue(wxVariant(0), evt.job_id, COL_PROGRESS);
on_list_select();
+
+ wxVariant nm, hst;
+ job_list->GetValue(nm, evt.job_id, COL_FILENAME);
+ job_list->GetValue(hst, evt.job_id, COL_HOST);
+ wxGetApp().notification_manager()->upload_job_notification_show_canceled(evt.job_id + 1, boost::nowide::narrow(nm.GetString()), boost::nowide::narrow(hst.GetString()));
}
void PrintHostQueueDialog::get_active_jobs(std::vector>& ret)