mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-11-02 20:51:23 -07:00 
			
		
		
		
	Notifications & warning dialog
notifications dialog with warnings produced by slicing is shown before exporting
This commit is contained in:
		
							parent
							
								
									4bfb69eb14
								
							
						
					
					
						commit
						b3f8ae5ca7
					
				
					 21 changed files with 1791 additions and 89 deletions
				
			
		
							
								
								
									
										10
									
								
								resources/icons/cancel.svg
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								resources/icons/cancel.svg
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,10 @@
 | 
			
		|||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<!-- Generator: Adobe Illustrator 23.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
 | 
			
		||||
<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
 | 
			
		||||
	 viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
 | 
			
		||||
<g id="resin">
 | 
			
		||||
	<rect x="4" y="7" fill="#ED6B21" width="8" height="8"/>
 | 
			
		||||
	<path fill="none" stroke="#808080" stroke-linecap="round" stroke-miterlimit="10" d="M4.5,15h6.99c0.28,0,0.5-0.23,0.5-0.5V6
 | 
			
		||||
		c0-1-2-1-2-2s0-1,0-1h1V1.5C11,1.23,10.77,1,10.5,1H5.5C5.23,1,5,1.23,5,1.5V3h1v1c0,1-2,1-2,2v8.5C4,14.77,4.23,15,4.5,15z"/>
 | 
			
		||||
</g>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 671 B  | 
							
								
								
									
										81
									
								
								resources/icons/cross_focus_large.svg
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								resources/icons/cross_focus_large.svg
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,81 @@
 | 
			
		|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
			
		||||
<svg
 | 
			
		||||
   xmlns:dc="http://purl.org/dc/elements/1.1/"
 | 
			
		||||
   xmlns:cc="http://creativecommons.org/ns#"
 | 
			
		||||
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
 | 
			
		||||
   xmlns:svg="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
 | 
			
		||||
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
 | 
			
		||||
   inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
 | 
			
		||||
   sodipodi:docname="cross_megafocus.svg"
 | 
			
		||||
   xml:space="preserve"
 | 
			
		||||
   enable-background="new 0 0 16 16"
 | 
			
		||||
   viewBox="0 0 16 16"
 | 
			
		||||
   y="0px"
 | 
			
		||||
   x="0px"
 | 
			
		||||
   id="Layer_1"
 | 
			
		||||
   version="1.0"><metadata
 | 
			
		||||
   id="metadata16"><rdf:RDF><cc:Work
 | 
			
		||||
       rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
 | 
			
		||||
         rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
 | 
			
		||||
   id="defs14" /><sodipodi:namedview
 | 
			
		||||
   inkscape:current-layer="Layer_1"
 | 
			
		||||
   inkscape:window-maximized="1"
 | 
			
		||||
   inkscape:window-y="-9"
 | 
			
		||||
   inkscape:window-x="-9"
 | 
			
		||||
   inkscape:cy="8"
 | 
			
		||||
   inkscape:cx="8"
 | 
			
		||||
   inkscape:zoom="47.0625"
 | 
			
		||||
   showgrid="false"
 | 
			
		||||
   id="namedview12"
 | 
			
		||||
   inkscape:window-height="1721"
 | 
			
		||||
   inkscape:window-width="3200"
 | 
			
		||||
   inkscape:pageshadow="2"
 | 
			
		||||
   inkscape:pageopacity="0"
 | 
			
		||||
   guidetolerance="10"
 | 
			
		||||
   gridtolerance="10"
 | 
			
		||||
   objecttolerance="10"
 | 
			
		||||
   borderopacity="1"
 | 
			
		||||
   bordercolor="#666666"
 | 
			
		||||
   pagecolor="#ffffff" />
 | 
			
		||||
<g
 | 
			
		||||
   style="opacity:1;fill-opacity:1"
 | 
			
		||||
   transform="matrix(1.1,0,0,1.1,-0.8,-0.8)"
 | 
			
		||||
   id="cross">
 | 
			
		||||
	<g
 | 
			
		||||
   style="fill-opacity:1"
 | 
			
		||||
   id="g4">
 | 
			
		||||
		
 | 
			
		||||
			<line
 | 
			
		||||
   style="fill-opacity:1"
 | 
			
		||||
   id="line2"
 | 
			
		||||
   y2="14"
 | 
			
		||||
   x2="2"
 | 
			
		||||
   y1="2"
 | 
			
		||||
   x1="14"
 | 
			
		||||
   stroke-miterlimit="10"
 | 
			
		||||
   stroke-linecap="round"
 | 
			
		||||
   stroke-width="3"
 | 
			
		||||
   stroke="#ed6b21"
 | 
			
		||||
   fill="none" />
 | 
			
		||||
	</g>
 | 
			
		||||
	<g
 | 
			
		||||
   style="fill-opacity:1"
 | 
			
		||||
   id="g8">
 | 
			
		||||
		
 | 
			
		||||
			<line
 | 
			
		||||
   style="fill-opacity:1"
 | 
			
		||||
   id="line6"
 | 
			
		||||
   y2="14"
 | 
			
		||||
   x2="14"
 | 
			
		||||
   y1="2"
 | 
			
		||||
   x1="2"
 | 
			
		||||
   stroke-miterlimit="10"
 | 
			
		||||
   stroke-linecap="round"
 | 
			
		||||
   stroke-width="3"
 | 
			
		||||
   stroke="#ed6b21"
 | 
			
		||||
   fill="none" />
 | 
			
		||||
	</g>
 | 
			
		||||
</g>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 2 KiB  | 
							
								
								
									
										72
									
								
								resources/icons/timer_dot.svg
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								resources/icons/timer_dot.svg
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,72 @@
 | 
			
		|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
			
		||||
<svg
 | 
			
		||||
   xmlns:dc="http://purl.org/dc/elements/1.1/"
 | 
			
		||||
   xmlns:cc="http://creativecommons.org/ns#"
 | 
			
		||||
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
 | 
			
		||||
   xmlns:svg="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns:xlink="http://www.w3.org/1999/xlink"
 | 
			
		||||
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
 | 
			
		||||
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
 | 
			
		||||
   inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
 | 
			
		||||
   sodipodi:docname="timer_dot.svg"
 | 
			
		||||
   xml:space="preserve"
 | 
			
		||||
   enable-background="new 0 0 16 16"
 | 
			
		||||
   viewBox="0 0 16 16"
 | 
			
		||||
   y="0px"
 | 
			
		||||
   x="0px"
 | 
			
		||||
   id="Layer_1"
 | 
			
		||||
   version="1.0"><metadata
 | 
			
		||||
   id="metadata11"><rdf:RDF><cc:Work
 | 
			
		||||
       rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
 | 
			
		||||
         rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
 | 
			
		||||
   id="defs9"><linearGradient
 | 
			
		||||
     id="linearGradient830"
 | 
			
		||||
     inkscape:collect="always"><stop
 | 
			
		||||
       id="stop826"
 | 
			
		||||
       offset="0"
 | 
			
		||||
       style="stop-color:#000000;stop-opacity:1;" /><stop
 | 
			
		||||
       id="stop828"
 | 
			
		||||
       offset="1"
 | 
			
		||||
       style="stop-color:#000000;stop-opacity:0;" /></linearGradient><radialGradient
 | 
			
		||||
     gradientUnits="userSpaceOnUse"
 | 
			
		||||
     r="3.5"
 | 
			
		||||
     fy="8"
 | 
			
		||||
     fx="8"
 | 
			
		||||
     cy="8"
 | 
			
		||||
     cx="8"
 | 
			
		||||
     id="radialGradient832"
 | 
			
		||||
     xlink:href="#linearGradient830"
 | 
			
		||||
     inkscape:collect="always" /></defs><sodipodi:namedview
 | 
			
		||||
   inkscape:document-rotation="0"
 | 
			
		||||
   inkscape:current-layer="Layer_1"
 | 
			
		||||
   inkscape:window-maximized="1"
 | 
			
		||||
   inkscape:window-y="-11"
 | 
			
		||||
   inkscape:window-x="-11"
 | 
			
		||||
   inkscape:cy="6.66147"
 | 
			
		||||
   inkscape:cx="7.0304602"
 | 
			
		||||
   inkscape:zoom="83.4386"
 | 
			
		||||
   showgrid="false"
 | 
			
		||||
   id="namedview7"
 | 
			
		||||
   inkscape:window-height="2066"
 | 
			
		||||
   inkscape:window-width="3840"
 | 
			
		||||
   inkscape:pageshadow="2"
 | 
			
		||||
   inkscape:pageopacity="0"
 | 
			
		||||
   guidetolerance="10"
 | 
			
		||||
   gridtolerance="10"
 | 
			
		||||
   objecttolerance="10"
 | 
			
		||||
   borderopacity="1"
 | 
			
		||||
   bordercolor="#666666"
 | 
			
		||||
   pagecolor="#ffffff" />
 | 
			
		||||
<g
 | 
			
		||||
   transform="matrix(0.7,0,0,0.7,2.4,2.4)"
 | 
			
		||||
   style="fill:#bf6637;fill-opacity:1;stroke:none;stroke-opacity:1"
 | 
			
		||||
   id="g4">
 | 
			
		||||
	<circle
 | 
			
		||||
   style="fill:#bf6637;fill-opacity:1;stroke:none;stroke-opacity:1"
 | 
			
		||||
   id="circle2"
 | 
			
		||||
   r="3"
 | 
			
		||||
   cy="8"
 | 
			
		||||
   cx="8" />
 | 
			
		||||
</g>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 2.2 KiB  | 
							
								
								
									
										73
									
								
								resources/icons/timer_dot_empty.svg
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								resources/icons/timer_dot_empty.svg
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,73 @@
 | 
			
		|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
			
		||||
<!-- Generator: Adobe Illustrator 23.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
 | 
			
		||||
 | 
			
		||||
<svg
 | 
			
		||||
   xmlns:dc="http://purl.org/dc/elements/1.1/"
 | 
			
		||||
   xmlns:cc="http://creativecommons.org/ns#"
 | 
			
		||||
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
 | 
			
		||||
   xmlns:svg="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns:xlink="http://www.w3.org/1999/xlink"
 | 
			
		||||
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
 | 
			
		||||
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
 | 
			
		||||
   version="1.0"
 | 
			
		||||
   id="Layer_1"
 | 
			
		||||
   x="0px"
 | 
			
		||||
   y="0px"
 | 
			
		||||
   viewBox="0 0 16 16"
 | 
			
		||||
   enable-background="new 0 0 16 16"
 | 
			
		||||
   xml:space="preserve"
 | 
			
		||||
   sodipodi:docname="timer_dot_empty.svg"
 | 
			
		||||
   inkscape:version="0.92.4 (5da689c313, 2019-01-14)"><metadata
 | 
			
		||||
   id="metadata11"><rdf:RDF><cc:Work
 | 
			
		||||
       rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
 | 
			
		||||
         rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
 | 
			
		||||
   id="defs9"><linearGradient
 | 
			
		||||
     inkscape:collect="always"
 | 
			
		||||
     id="linearGradient830"><stop
 | 
			
		||||
       style="stop-color:#000000;stop-opacity:1;"
 | 
			
		||||
       offset="0"
 | 
			
		||||
       id="stop826" /><stop
 | 
			
		||||
       style="stop-color:#000000;stop-opacity:0;"
 | 
			
		||||
       offset="1"
 | 
			
		||||
       id="stop828" /></linearGradient><radialGradient
 | 
			
		||||
     inkscape:collect="always"
 | 
			
		||||
     xlink:href="#linearGradient830"
 | 
			
		||||
     id="radialGradient832"
 | 
			
		||||
     cx="8"
 | 
			
		||||
     cy="8"
 | 
			
		||||
     fx="8"
 | 
			
		||||
     fy="8"
 | 
			
		||||
     r="3.5"
 | 
			
		||||
     gradientUnits="userSpaceOnUse" /></defs><sodipodi:namedview
 | 
			
		||||
   pagecolor="#ffffff"
 | 
			
		||||
   bordercolor="#666666"
 | 
			
		||||
   borderopacity="1"
 | 
			
		||||
   objecttolerance="10"
 | 
			
		||||
   gridtolerance="10"
 | 
			
		||||
   guidetolerance="10"
 | 
			
		||||
   inkscape:pageopacity="0"
 | 
			
		||||
   inkscape:pageshadow="2"
 | 
			
		||||
   inkscape:window-width="3840"
 | 
			
		||||
   inkscape:window-height="2066"
 | 
			
		||||
   id="namedview7"
 | 
			
		||||
   showgrid="false"
 | 
			
		||||
   inkscape:zoom="83.4386"
 | 
			
		||||
   inkscape:cx="7.0304602"
 | 
			
		||||
   inkscape:cy="6.66147"
 | 
			
		||||
   inkscape:window-x="-11"
 | 
			
		||||
   inkscape:window-y="-11"
 | 
			
		||||
   inkscape:window-maximized="1"
 | 
			
		||||
   inkscape:current-layer="Layer_1" />
 | 
			
		||||
<g
 | 
			
		||||
   id="g4"
 | 
			
		||||
   style="fill:#000000;fill-opacity:1;stroke:none;stroke-opacity:1"
 | 
			
		||||
   transform="matrix(0.7,0,0,0.7,2.4,2.4)">
 | 
			
		||||
	<circle
 | 
			
		||||
   cx="8"
 | 
			
		||||
   cy="8"
 | 
			
		||||
   r="3"
 | 
			
		||||
   id="circle2"
 | 
			
		||||
   style="fill:#000000;fill-opacity:1;stroke:none;stroke-opacity:1" />
 | 
			
		||||
</g>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 2.3 KiB  | 
| 
						 | 
				
			
			@ -113,7 +113,12 @@ namespace ImGui
 | 
			
		|||
    const char PrinterSlaIconMarker = 0x6; 
 | 
			
		||||
    const char FilamentIconMarker   = 0x7; 
 | 
			
		||||
    const char MaterialIconMarker   = 0x8;
 | 
			
		||||
 | 
			
		||||
	const char CloseIconMarker      = 0xB;
 | 
			
		||||
	const char CloseIconHoverMarker = 0xC;
 | 
			
		||||
	const char TimerDotMarker       = 0xE;
 | 
			
		||||
	const char TimerDotEmptyMarker  = 0xF;
 | 
			
		||||
	const char WarningMarker        = 0x10;
 | 
			
		||||
	const char ErrorMarker          = 0x11;
 | 
			
		||||
//    void MyFunction(const char* name, const MyMatrix44& v);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -686,6 +686,7 @@ std::vector<std::pair<coordf_t, std::vector<GCode::LayerToPrint>>> GCode::collec
 | 
			
		|||
    std::sort(ordering.begin(), ordering.end(), [](const OrderingItem &oi1, const OrderingItem &oi2) { return oi1.print_z < oi2.print_z; });
 | 
			
		||||
 | 
			
		||||
    std::vector<std::pair<coordf_t, std::vector<LayerToPrint>>> layers_to_print;
 | 
			
		||||
 | 
			
		||||
    // Merge numerically very close Z values.
 | 
			
		||||
    for (size_t i = 0; i < ordering.size();) {
 | 
			
		||||
        // Find the last layer with roughly the same print_z.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -163,6 +163,8 @@ set(SLIC3R_GUI_SOURCES
 | 
			
		|||
    GUI/InstanceCheck.hpp
 | 
			
		||||
    GUI/Search.cpp
 | 
			
		||||
    GUI/Search.hpp
 | 
			
		||||
    GUI/NotificationManager.cpp
 | 
			
		||||
    GUI/NotificationManager.hpp
 | 
			
		||||
    Utils/Http.cpp
 | 
			
		||||
    Utils/Http.hpp
 | 
			
		||||
    Utils/FixModelByWin10.cpp
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -89,11 +89,13 @@ void BackgroundSlicingProcess::process_fff()
 | 
			
		|||
{
 | 
			
		||||
	assert(m_print == m_fff_print);
 | 
			
		||||
    m_print->process();
 | 
			
		||||
	wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, new wxCommandEvent(m_event_slicing_completed_id));
 | 
			
		||||
	wxCommandEvent evt(m_event_slicing_completed_id);
 | 
			
		||||
	evt.SetInt((int)(m_fff_print->step_state_with_timestamp(PrintStep::psBrim).timestamp));
 | 
			
		||||
	wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, evt.Clone());
 | 
			
		||||
    m_fff_print->export_gcode(m_temp_output_path, m_gcode_preview_data, m_thumbnail_cb);
 | 
			
		||||
 | 
			
		||||
	if (this->set_step_started(bspsGCodeFinalize)) {
 | 
			
		||||
	    if (! m_export_path.empty()) {
 | 
			
		||||
			wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, new wxCommandEvent(m_event_export_began_id));
 | 
			
		||||
	    	//FIXME localize the messages
 | 
			
		||||
	    	// Perform the final post-processing of the export path by applying the print statistics over the file name.
 | 
			
		||||
	    	std::string export_path = m_fff_print->print_statistics().finalize_output_path(m_export_path);
 | 
			
		||||
| 
						 | 
				
			
			@ -124,6 +126,7 @@ void BackgroundSlicingProcess::process_fff()
 | 
			
		|||
	    	run_post_process_scripts(export_path, m_fff_print->config());
 | 
			
		||||
	    	m_print->set_status(100, (boost::format(_utf8(L("G-code file exported to %1%"))) % export_path).str());
 | 
			
		||||
	    } else if (! m_upload_job.empty()) {
 | 
			
		||||
			wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, new wxCommandEvent(m_event_export_began_id));
 | 
			
		||||
			prepare_upload();
 | 
			
		||||
	    } else {
 | 
			
		||||
			m_print->set_status(100, _utf8(L("Slicing complete")));
 | 
			
		||||
| 
						 | 
				
			
			@ -149,6 +152,8 @@ void BackgroundSlicingProcess::process_sla()
 | 
			
		|||
    m_print->process();
 | 
			
		||||
    if (this->set_step_started(bspsGCodeFinalize)) {
 | 
			
		||||
        if (! m_export_path.empty()) {
 | 
			
		||||
			wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, new wxCommandEvent(m_event_export_began_id));
 | 
			
		||||
 | 
			
		||||
            const std::string export_path = m_sla_print->print_statistics().finalize_output_path(m_export_path);
 | 
			
		||||
 | 
			
		||||
            Zipper zipper(export_path);
 | 
			
		||||
| 
						 | 
				
			
			@ -170,6 +175,7 @@ void BackgroundSlicingProcess::process_sla()
 | 
			
		|||
 | 
			
		||||
            m_print->set_status(100, (boost::format(_utf8(L("Masked SLA file exported to %1%"))) % export_path).str());
 | 
			
		||||
        } else if (! m_upload_job.empty()) {
 | 
			
		||||
			wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, new wxCommandEvent(m_event_export_began_id));
 | 
			
		||||
            prepare_upload();
 | 
			
		||||
        } else {
 | 
			
		||||
			m_print->set_status(100, _utf8(L("Slicing complete")));
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -60,6 +60,10 @@ public:
 | 
			
		|||
	// The following wxCommandEvent will be sent to the UI thread / Plater window, when the G-code export is finished.
 | 
			
		||||
	// The wxCommandEvent is sent to the UI thread asynchronously without waiting for the event to be processed.
 | 
			
		||||
	void set_finished_event(int event_id) { m_event_finished_id = event_id; }
 | 
			
		||||
	// The following wxCommandEvent will be sent to the UI thread / Plater window, when the G-code is being exported to
 | 
			
		||||
	// specified path or uploaded.
 | 
			
		||||
	// The wxCommandEvent is sent to the UI thread asynchronously without waiting for the event to be processed.
 | 
			
		||||
	void set_export_began_event(int event_id) { m_event_export_began_id = event_id; }
 | 
			
		||||
 | 
			
		||||
	// Activate either m_fff_print or m_sla_print.
 | 
			
		||||
	// Return true if changed.
 | 
			
		||||
| 
						 | 
				
			
			@ -190,6 +194,9 @@ private:
 | 
			
		|||
	int 						m_event_slicing_completed_id 	= 0;
 | 
			
		||||
	// wxWidgets command ID to be sent to the plater to inform that the task finished.
 | 
			
		||||
	int 						m_event_finished_id  			= 0;
 | 
			
		||||
	// wxWidgets command ID to be sent to the plater to inform that the G-code is being exported.
 | 
			
		||||
	int                         m_event_export_began_id         = 0;
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}; // namespace Slic3r
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -31,6 +31,7 @@
 | 
			
		|||
#include "GUI_ObjectManipulation.hpp"
 | 
			
		||||
#include "Mouse3DController.hpp"
 | 
			
		||||
#include "I18N.hpp"
 | 
			
		||||
#include "NotificationManager.hpp"
 | 
			
		||||
 | 
			
		||||
#if ENABLE_RETINA_GL
 | 
			
		||||
#include "slic3r/Utils/RetinaHelper.hpp"
 | 
			
		||||
| 
						 | 
				
			
			@ -651,19 +652,45 @@ void GLCanvas3D::WarningTexture::activate(WarningTexture::Warning warning, bool
 | 
			
		|||
 | 
			
		||||
        m_warnings.emplace_back(warning);
 | 
			
		||||
        std::sort(m_warnings.begin(), m_warnings.end());
 | 
			
		||||
 | 
			
		||||
		std::string text;
 | 
			
		||||
		switch (warning) {
 | 
			
		||||
			case ObjectOutside: text = L("An object outside the print area was detected."); break;
 | 
			
		||||
			case ToolpathOutside: text = L("A toolpath outside the print area was detected."); break;
 | 
			
		||||
			case SlaSupportsOutside: text = L("SLA supports outside the print area were detected."); break;
 | 
			
		||||
			case SomethingNotShown: text = L("Some objects are not visible."); break;
 | 
			
		||||
			case ObjectClashed: wxGetApp().plater()->get_notification_manager()->push_plater_error_notification(L("An object outside the print area was detected.\n"
 | 
			
		||||
																												 "Resolve the current problem to continue slicing."), 
 | 
			
		||||
				                                                                                                *(wxGetApp().plater()->get_current_canvas3D()));
 | 
			
		||||
				break;
 | 
			
		||||
		}
 | 
			
		||||
		if (!text.empty())
 | 
			
		||||
			wxGetApp().plater()->get_notification_manager()->push_plater_warning_notification(text, *(wxGetApp().plater()->get_current_canvas3D()));
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
        if (it == m_warnings.end()) // deactivating something that is not active is an easy task
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        m_warnings.erase(it);
 | 
			
		||||
        if (m_warnings.empty()) { // nothing remains to be shown
 | 
			
		||||
 | 
			
		||||
		std::string text;
 | 
			
		||||
		switch (warning) {
 | 
			
		||||
		case ObjectOutside: text = L("An object outside the print area was detected."); break;
 | 
			
		||||
		case ToolpathOutside: text = L("A toolpath outside the print area was detected."); break;
 | 
			
		||||
		case SlaSupportsOutside: text = L("SLA supports outside the print area were detected."); break;
 | 
			
		||||
		case SomethingNotShown: text = L("Some objects are not visibl.e"); break;
 | 
			
		||||
		case ObjectClashed: wxGetApp().plater()->get_notification_manager()->close_plater_error_notification(); break;
 | 
			
		||||
		}
 | 
			
		||||
		if (!text.empty())
 | 
			
		||||
			wxGetApp().plater()->get_notification_manager()->close_plater_warning_notification(text);
 | 
			
		||||
 | 
			
		||||
        /*if (m_warnings.empty()) { // nothing remains to be shown
 | 
			
		||||
            reset();
 | 
			
		||||
            m_msg_text = "";// save information for rescaling
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        }*/
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
    // Look at the end of our vector and generate proper texture.
 | 
			
		||||
    std::string text;
 | 
			
		||||
    bool red_colored = false;
 | 
			
		||||
| 
						 | 
				
			
			@ -685,6 +712,7 @@ void GLCanvas3D::WarningTexture::activate(WarningTexture::Warning warning, bool
 | 
			
		|||
    // save information for rescaling
 | 
			
		||||
    m_msg_text = text;
 | 
			
		||||
    m_is_colored_red = red_colored;
 | 
			
		||||
	*/
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -2074,6 +2102,8 @@ void GLCanvas3D::render()
 | 
			
		|||
 | 
			
		||||
    std::string tooltip;
 | 
			
		||||
 | 
			
		||||
	
 | 
			
		||||
 | 
			
		||||
	// Negative coordinate means out of the window, likely because the window was deactivated.
 | 
			
		||||
	// In that case the tooltip should be hidden.
 | 
			
		||||
    if (m_mouse.position.x() >= 0. && m_mouse.position.y() >= 0.) 
 | 
			
		||||
| 
						 | 
				
			
			@ -2103,6 +2133,8 @@ 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(*this);
 | 
			
		||||
 | 
			
		||||
    wxGetApp().imgui()->render();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -3418,6 +3450,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
 | 
			
		|||
#ifdef SLIC3R_DEBUG_MOUSE_EVENTS
 | 
			
		||||
        printf((format_mouse_event_debug_message(evt) + " - Consumed by ImGUI\n").c_str());
 | 
			
		||||
#endif /* SLIC3R_DEBUG_MOUSE_EVENTS */
 | 
			
		||||
		 m_dirty = true;
 | 
			
		||||
        // do not return if dragging or tooltip not empty to allow for tooltip update
 | 
			
		||||
        if (!m_mouse.dragging && m_tooltip.is_empty())
 | 
			
		||||
            return;
 | 
			
		||||
| 
						 | 
				
			
			@ -3811,7 +3844,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
 | 
			
		|||
            m_gizmos.reset_all_states();
 | 
			
		||||
 | 
			
		||||
        // Only refresh if picking is enabled, in that case the objects may get highlighted if the mouse cursor hovers over.
 | 
			
		||||
        if (m_picking_enabled)
 | 
			
		||||
        //if (m_picking_enabled)
 | 
			
		||||
            m_dirty = true;
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -54,6 +54,7 @@
 | 
			
		|||
#include "Mouse3DController.hpp"
 | 
			
		||||
#include "RemovableDriveManager.hpp"
 | 
			
		||||
#include "InstanceCheck.hpp"
 | 
			
		||||
#include "NotificationManager.hpp"
 | 
			
		||||
 | 
			
		||||
#ifdef __WXMSW__
 | 
			
		||||
#include <dbt.h>
 | 
			
		||||
| 
						 | 
				
			
			@ -384,7 +385,7 @@ bool GUI_App::on_init_inner()
 | 
			
		|||
    // supplied as argument to --datadir; in that case we should still run the wizard
 | 
			
		||||
    preset_bundle->setup_directories();
 | 
			
		||||
 | 
			
		||||
#ifdef __WXMSW__
 | 
			
		||||
#ifdef __WXMSW__ 
 | 
			
		||||
    associate_3mf_files();
 | 
			
		||||
#endif // __WXMSW__
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -392,6 +393,11 @@ bool GUI_App::on_init_inner()
 | 
			
		|||
    Bind(EVT_SLIC3R_VERSION_ONLINE, [this](const wxCommandEvent &evt) {
 | 
			
		||||
        app_config->set("version_online", into_u8(evt.GetString()));
 | 
			
		||||
        app_config->save();
 | 
			
		||||
		if(this->plater_ != nullptr) {
 | 
			
		||||
			if (*Semver::parse(SLIC3R_VERSION) < * Semver::parse(into_u8(evt.GetString()))) {
 | 
			
		||||
				this->plater_->get_notification_manager()->push_notification(NotificationType::NewAppAviable, *(this->plater_->get_current_canvas3D()));
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    // initialize label colors and fonts
 | 
			
		||||
| 
						 | 
				
			
			@ -1439,7 +1445,7 @@ void GUI_App::check_updates(const bool verbose)
 | 
			
		|||
	
 | 
			
		||||
	PresetUpdater::UpdateResult updater_result;
 | 
			
		||||
	try {
 | 
			
		||||
		updater_result = preset_updater->config_update(app_config->orig_version());
 | 
			
		||||
		updater_result = preset_updater->config_update(app_config->orig_version(), verbose);
 | 
			
		||||
		if (updater_result == PresetUpdater::R_INCOMPAT_EXIT) {
 | 
			
		||||
			mainframe->Close();
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -194,12 +194,15 @@ public:
 | 
			
		|||
    Plater*             plater();
 | 
			
		||||
    Model&      		model();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    AppConfig*      app_config{ nullptr };
 | 
			
		||||
    PresetBundle*   preset_bundle{ nullptr };
 | 
			
		||||
    PresetUpdater*  preset_updater{ nullptr };
 | 
			
		||||
    MainFrame*      mainframe{ nullptr };
 | 
			
		||||
    Plater*         plater_{ nullptr };
 | 
			
		||||
 | 
			
		||||
	PresetUpdater* get_preset_updater() { return preset_updater; }
 | 
			
		||||
 | 
			
		||||
    wxNotebook*     tab_panel() const ;
 | 
			
		||||
    int             extruders_cnt() const;
 | 
			
		||||
    int             extruders_edited_cnt() const;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -37,11 +37,17 @@ namespace GUI {
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
static const std::map<const char, std::string> font_icons = {
 | 
			
		||||
    {ImGui::PrintIconMarker     , "cog"        },
 | 
			
		||||
    {ImGui::PrinterIconMarker   , "printer"    },
 | 
			
		||||
    {ImGui::PrinterSlaIconMarker, "sla_printer"},
 | 
			
		||||
    {ImGui::FilamentIconMarker  , "spool"      },
 | 
			
		||||
    {ImGui::MaterialIconMarker  , "resin"      }
 | 
			
		||||
    {ImGui::PrintIconMarker     , "cog"               },
 | 
			
		||||
    {ImGui::PrinterIconMarker   , "printer"           },
 | 
			
		||||
    {ImGui::PrinterSlaIconMarker, "sla_printer"       },
 | 
			
		||||
    {ImGui::FilamentIconMarker  , "spool"             },
 | 
			
		||||
    {ImGui::MaterialIconMarker  , "resin"             },
 | 
			
		||||
	{ImGui::CloseIconMarker     , "cross"             },
 | 
			
		||||
	{ImGui::CloseIconHoverMarker, "cross_focus_large" },
 | 
			
		||||
	{ImGui::TimerDotMarker      , "timer_dot"         },
 | 
			
		||||
    {ImGui::TimerDotEmptyMarker , "timer_dot_empty"   },
 | 
			
		||||
	{ImGui::WarningMarker       , "flag_green"        },
 | 
			
		||||
    {ImGui::ErrorMarker         , "flag_red"          }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
ImGuiWrapper::ImGuiWrapper()
 | 
			
		||||
| 
						 | 
				
			
			@ -265,6 +271,11 @@ void ImGuiWrapper::set_next_window_bg_alpha(float alpha)
 | 
			
		|||
    ImGui::SetNextWindowBgAlpha(alpha);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ImGuiWrapper::set_next_window_size(float x, float y, ImGuiCond cond)
 | 
			
		||||
{
 | 
			
		||||
	ImGui::SetNextWindowSize(ImVec2(x, y), cond);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ImGuiWrapper::begin(const std::string &name, int flags)
 | 
			
		||||
{
 | 
			
		||||
    return ImGui::Begin(name.c_str(), nullptr, (ImGuiWindowFlags)flags);
 | 
			
		||||
| 
						 | 
				
			
			@ -296,12 +307,23 @@ bool ImGuiWrapper::button(const wxString &label)
 | 
			
		|||
    return ImGui::Button(label_utf8.c_str());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ImGuiWrapper::button(const wxString& label, float width, float height)
 | 
			
		||||
{
 | 
			
		||||
	auto label_utf8 = into_u8(label);
 | 
			
		||||
	return ImGui::Button(label_utf8.c_str(), ImVec2(width, height));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ImGuiWrapper::radio_button(const wxString &label, bool active)
 | 
			
		||||
{
 | 
			
		||||
    auto label_utf8 = into_u8(label);
 | 
			
		||||
    return ImGui::RadioButton(label_utf8.c_str(), active);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ImGuiWrapper::image_button()
 | 
			
		||||
{
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ImGuiWrapper::input_double(const std::string &label, const double &value, const std::string &format)
 | 
			
		||||
{
 | 
			
		||||
    return ImGui::InputDouble(label.c_str(), const_cast<double*>(&value), 0.0f, 0.0f, format.c_str());
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -57,6 +57,7 @@ public:
 | 
			
		|||
 | 
			
		||||
    void set_next_window_pos(float x, float y, int flag, float pivot_x = 0.0f, float pivot_y = 0.0f);
 | 
			
		||||
    void set_next_window_bg_alpha(float alpha);
 | 
			
		||||
	void set_next_window_size(float x, float y, ImGuiCond cond);
 | 
			
		||||
 | 
			
		||||
    bool begin(const std::string &name, int flags = 0);
 | 
			
		||||
    bool begin(const wxString &name, int flags = 0);
 | 
			
		||||
| 
						 | 
				
			
			@ -65,7 +66,9 @@ public:
 | 
			
		|||
    void end();
 | 
			
		||||
 | 
			
		||||
    bool button(const wxString &label);
 | 
			
		||||
	bool button(const wxString& label, float width, float height);
 | 
			
		||||
    bool radio_button(const wxString &label, bool active);
 | 
			
		||||
	bool image_button();
 | 
			
		||||
    bool input_double(const std::string &label, const double &value, const std::string &format = "%.3f");
 | 
			
		||||
    bool input_double(const wxString &label, const double &value, const std::string &format = "%.3f");
 | 
			
		||||
    bool input_vec3(const std::string &label, const Vec3d &value, float width, const std::string &format = "%.3f");
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,6 +7,7 @@
 | 
			
		|||
#include "AppConfig.hpp"
 | 
			
		||||
#include "GLCanvas3D.hpp"
 | 
			
		||||
#include "Plater.hpp"
 | 
			
		||||
#include "NotificationManager.hpp"
 | 
			
		||||
 | 
			
		||||
#include <wx/glcanvas.h>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -403,6 +404,8 @@ void Mouse3DController::disconnected()
 | 
			
		|||
        m_params_by_device[m_device_str] = m_params_ui;
 | 
			
		||||
	    m_device_str.clear();
 | 
			
		||||
	    m_connected = false;
 | 
			
		||||
		wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::Mouse3dDisconnected, *(wxGetApp().plater()->get_current_canvas3D()));
 | 
			
		||||
 | 
			
		||||
        wxGetApp().plater()->CallAfter([]() {
 | 
			
		||||
        	Plater *plater = wxGetApp().plater();
 | 
			
		||||
        	if (plater != nullptr) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										918
									
								
								src/slic3r/GUI/NotificationManager.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										918
									
								
								src/slic3r/GUI/NotificationManager.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,918 @@
 | 
			
		|||
#include "NotificationManager.hpp"
 | 
			
		||||
 | 
			
		||||
#include "GUI_App.hpp"
 | 
			
		||||
#include "Plater.hpp"
 | 
			
		||||
#include "GLCanvas3D.hpp"
 | 
			
		||||
#include "ImGuiWrapper.hpp"
 | 
			
		||||
 | 
			
		||||
#include "wxExtensions.hpp"
 | 
			
		||||
 | 
			
		||||
#include <boost/algorithm/string.hpp>
 | 
			
		||||
#include <boost/log/trivial.hpp>
 | 
			
		||||
#include <wx/glcanvas.h>
 | 
			
		||||
#include <iostream>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#define NOTIFICATION_MAX_MOVE 3.0f
 | 
			
		||||
 | 
			
		||||
#define GAP_WIDTH 10.0f
 | 
			
		||||
#define SPACE_RIGHT_PANEL 10.0f
 | 
			
		||||
 | 
			
		||||
namespace Slic3r {
 | 
			
		||||
namespace GUI {
 | 
			
		||||
 | 
			
		||||
wxDEFINE_EVENT(EVT_EJECT_DRIVE_NOTIFICAION_CLICKED, EjectDriveNotificationClickedEvent);
 | 
			
		||||
wxDEFINE_EVENT(EVT_EXPORT_GCODE_NOTIFICAION_CLICKED, ExportGcodeNotificationClickedEvent);
 | 
			
		||||
wxDEFINE_EVENT(EVT_PRESET_UPDATE_AVIABLE_CLICKED, PresetUpdateAviableClickedEvent);
 | 
			
		||||
 | 
			
		||||
namespace Notifications_Internal{
 | 
			
		||||
	void push_style_color(ImGuiCol idx, const ImVec4& col, bool fading_out, float current_fade_opacity)
 | 
			
		||||
	{
 | 
			
		||||
		if (fading_out)
 | 
			
		||||
			ImGui::PushStyleColor(idx, ImVec4(col.x, col.y, col.z, col.w * current_fade_opacity));
 | 
			
		||||
		else
 | 
			
		||||
			ImGui::PushStyleColor(idx, col);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
//ScalableBitmap bmp_icon;
 | 
			
		||||
//------PopNotification--------
 | 
			
		||||
NotificationManager::PopNotification::PopNotification(const NotificationData &n, const int id, wxEvtHandler* evt_handler) :
 | 
			
		||||
	  m_data                (n)
 | 
			
		||||
	, m_id                  (id)    
 | 
			
		||||
	, 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)
 | 
			
		||||
{
 | 
			
		||||
	init();
 | 
			
		||||
}
 | 
			
		||||
NotificationManager::PopNotification::~PopNotification()
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
NotificationManager::PopNotification::RenderResult NotificationManager::PopNotification::render(GLCanvas3D& canvas, const float& initial_y)
 | 
			
		||||
{
 | 
			
		||||
	if (m_finished)
 | 
			
		||||
		return RenderResult::Finished;
 | 
			
		||||
	if (m_close_pending) {
 | 
			
		||||
		// request of extra frame will be done in caller function by ret val ClosePending
 | 
			
		||||
		m_finished = true;
 | 
			
		||||
		return RenderResult::ClosePending;
 | 
			
		||||
	}
 | 
			
		||||
	if (m_hidden) {
 | 
			
		||||
		m_top_y = initial_y - GAP_WIDTH;
 | 
			
		||||
		return RenderResult::Static;
 | 
			
		||||
	}
 | 
			
		||||
	RenderResult    ret_val = m_counting_down ? RenderResult::Countdown : RenderResult::Static;
 | 
			
		||||
	Size            cnv_size = canvas.get_canvas_size();
 | 
			
		||||
	ImGuiWrapper&   imgui = *wxGetApp().imgui();
 | 
			
		||||
	bool            shown = true;
 | 
			
		||||
	std::string     name;
 | 
			
		||||
	ImVec2          mouse_pos = ImGui::GetMousePos();
 | 
			
		||||
 | 
			
		||||
	if (m_line_height != ImGui::CalcTextSize("A").y)
 | 
			
		||||
		init();
 | 
			
		||||
	
 | 
			
		||||
	set_next_window_size(imgui);
 | 
			
		||||
 | 
			
		||||
	//top y of window
 | 
			
		||||
	m_top_y = initial_y + m_window_height;
 | 
			
		||||
	//top right position
 | 
			
		||||
	ImVec2 win_pos(1.0f * (float)cnv_size.get_width() - SPACE_RIGHT_PANEL, 1.0f * (float)cnv_size.get_height() - m_top_y);
 | 
			
		||||
	imgui.set_next_window_pos(win_pos.x, win_pos.y, ImGuiCond_Always, 1.0f, 0.0f);
 | 
			
		||||
	imgui.set_next_window_size(m_window_width, m_window_height, ImGuiCond_Always);
 | 
			
		||||
 | 
			
		||||
	//find if hovered
 | 
			
		||||
	if (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();
 | 
			
		||||
		ret_val = RenderResult::Hovered;
 | 
			
		||||
		//reset fading
 | 
			
		||||
		m_fading_out = false;
 | 
			
		||||
		m_current_fade_opacity = 1.f;
 | 
			
		||||
		m_remaining_time = m_data.duration;
 | 
			
		||||
		m_countdown_frame = 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (m_counting_down && m_remaining_time < 0)
 | 
			
		||||
		m_close_pending = true;
 | 
			
		||||
 | 
			
		||||
	if (m_close_pending) {
 | 
			
		||||
		// request of extra frame will be done in caller function by ret val ClosePending
 | 
			
		||||
		m_finished = true;
 | 
			
		||||
		return RenderResult::ClosePending;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// color change based on fading out
 | 
			
		||||
	bool fading_pop = false;
 | 
			
		||||
	if (m_fading_out) {
 | 
			
		||||
		if (!m_paused)
 | 
			
		||||
			m_current_fade_opacity -= 1.f / ((m_fading_time + 1.f) * 60.f);
 | 
			
		||||
		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);
 | 
			
		||||
		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);
 | 
			
		||||
	} 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);
 | 
			
		||||
	} 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);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	//name of window - probably indentifies window and is shown so last_end add whitespaces according to id
 | 
			
		||||
	for (size_t i = 0; i < m_id; i++)
 | 
			
		||||
		name += " ";
 | 
			
		||||
	if (imgui.begin(name, &shown, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar )) {
 | 
			
		||||
		if (shown) {
 | 
			
		||||
			
 | 
			
		||||
			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());
 | 
			
		||||
			*/
 | 
			
		||||
			if(m_counting_down)
 | 
			
		||||
				render_countdown(imgui, win_size.x, win_size.y, win_pos.x, win_pos.y);
 | 
			
		||||
			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);
 | 
			
		||||
			if (m_multiline && m_lines_count > 3)
 | 
			
		||||
				render_minimize_button(imgui, win_pos.x, win_pos.y);
 | 
			
		||||
		} else {
 | 
			
		||||
			// the user clicked on the [X] button ( ImGuiWindowFlags_NoTitleBar means theres no [X] button)
 | 
			
		||||
			m_close_pending = true;
 | 
			
		||||
			canvas.set_as_dirty();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	imgui.end();
 | 
			
		||||
	
 | 
			
		||||
	if (fading_pop) {
 | 
			
		||||
		ImGui::PopStyleColor();
 | 
			
		||||
		ImGui::PopStyleColor();
 | 
			
		||||
	}
 | 
			
		||||
	if (m_is_gray)
 | 
			
		||||
		ImGui::PopStyleColor();
 | 
			
		||||
	else if (m_data.level == NotificationLevel::ErrorNotification)
 | 
			
		||||
		ImGui::PopStyleColor();
 | 
			
		||||
	else if (m_data.level == NotificationLevel::WarningNotification)
 | 
			
		||||
		ImGui::PopStyleColor();
 | 
			
		||||
	return ret_val;
 | 
			
		||||
}
 | 
			
		||||
void NotificationManager::PopNotification::init()
 | 
			
		||||
{
 | 
			
		||||
	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;
 | 
			
		||||
 | 
			
		||||
	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_left_indentation + m_line_height * 2;
 | 
			
		||||
	m_window_width = m_line_height * 25;
 | 
			
		||||
	
 | 
			
		||||
	// count lines
 | 
			
		||||
	m_endlines.clear();
 | 
			
		||||
	while (last_end < text.length() - 1)
 | 
			
		||||
	{
 | 
			
		||||
		int next_hard_end = text.find_first_of('\n', last_end);
 | 
			
		||||
		if (next_hard_end > 0 && ImGui::CalcTextSize(text.substr(last_end, next_hard_end - last_end).c_str()).x < m_window_width - m_window_width_offset) {
 | 
			
		||||
			//next line is ended by '/n'
 | 
			
		||||
			m_endlines.push_back(next_hard_end);
 | 
			
		||||
			last_end = next_hard_end + 1;
 | 
			
		||||
		}
 | 
			
		||||
		else {
 | 
			
		||||
			// find next suitable endline
 | 
			
		||||
			if (ImGui::CalcTextSize(text.substr(last_end).c_str()).x >= m_window_width - 3.5f * m_line_height) {// m_window_width_offset) {
 | 
			
		||||
				// more than one line till end
 | 
			
		||||
				int next_space = text.find_first_of(' ', last_end);
 | 
			
		||||
				if (next_space > 0) {
 | 
			
		||||
					int next_space_candidate = text.find_first_of(' ', next_space + 1);
 | 
			
		||||
					while (next_space_candidate > 0 && ImGui::CalcTextSize(text.substr(last_end, next_space_candidate - last_end).c_str()).x < m_window_width - m_window_width_offset) {
 | 
			
		||||
						next_space = next_space_candidate;
 | 
			
		||||
						next_space_candidate = text.find_first_of(' ', next_space + 1);
 | 
			
		||||
					}
 | 
			
		||||
					m_endlines.push_back(next_space);
 | 
			
		||||
					last_end = next_space + 1;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			else {
 | 
			
		||||
				m_endlines.push_back(text.length());
 | 
			
		||||
				last_end = text.length();
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
		m_lines_count++;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
void NotificationManager::PopNotification::set_next_window_size(ImGuiWrapper& imgui)
 | 
			
		||||
{ 
 | 
			
		||||
	if (m_multiline) {
 | 
			
		||||
		m_window_height = m_lines_count * m_line_height;
 | 
			
		||||
	}else
 | 
			
		||||
	{
 | 
			
		||||
		m_window_height = 2 * m_line_height;
 | 
			
		||||
	}
 | 
			
		||||
	m_window_height += 1 * m_line_height; // top and bottom
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void NotificationManager::PopNotification::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());
 | 
			
		||||
	// text posistions are calculated by lines count
 | 
			
		||||
	// large texts has "more" button or are displayed whole
 | 
			
		||||
	// smaller texts are divided as one liners and two liners
 | 
			
		||||
	if (m_lines_count > 2) {
 | 
			
		||||
		if (m_multiline) {
 | 
			
		||||
			
 | 
			
		||||
			int last_end = 0;
 | 
			
		||||
			float starting_y = m_line_height/2;//10;
 | 
			
		||||
			float shift_y = m_line_height;// -m_line_height / 20;
 | 
			
		||||
			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;
 | 
			
		||||
				ImGui::SetCursorPosX(x_offset);
 | 
			
		||||
				ImGui::SetCursorPosY(starting_y + i * shift_y);
 | 
			
		||||
				imgui.text(line.c_str());
 | 
			
		||||
			}
 | 
			
		||||
			//hyperlink text
 | 
			
		||||
			if (!m_hypertext.empty())
 | 
			
		||||
			{
 | 
			
		||||
				render_hypertext(imgui, x_offset + ImGui::CalcTextSize(m_text1.substr(m_endlines[m_lines_count - 2] + 1, m_endlines[m_lines_count - 1] - m_endlines[m_lines_count - 2] - 1).c_str()).x, starting_y + (m_lines_count - 1) * shift_y, m_hypertext);
 | 
			
		||||
			}
 | 
			
		||||
			
 | 
			
		||||
			
 | 
			
		||||
		} else {
 | 
			
		||||
			// line1
 | 
			
		||||
			ImGui::SetCursorPosX(x_offset);
 | 
			
		||||
			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);
 | 
			
		||||
			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);
 | 
			
		||||
				line += "..";
 | 
			
		||||
			}else
 | 
			
		||||
				line += "  ";
 | 
			
		||||
			ImGui::SetCursorPosX(x_offset);
 | 
			
		||||
			ImGui::SetCursorPosY(win_size.y / 2 + win_size.y / 6 - m_line_height / 2);
 | 
			
		||||
			imgui.text(line.c_str());
 | 
			
		||||
			// "More" hypertext
 | 
			
		||||
			render_hypertext(imgui, x_offset + ImGui::CalcTextSize(line.c_str()).x, win_size.y / 2 + win_size.y / 6 - m_line_height / 2, _u8L("More"), true);
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		//text 1
 | 
			
		||||
		float cursor_y = win_size.y / 2 - text_size.y / 2;
 | 
			
		||||
		float cursor_x = x_offset;
 | 
			
		||||
		if(m_lines_count > 1) {
 | 
			
		||||
			// line1
 | 
			
		||||
			ImGui::SetCursorPosX(x_offset);
 | 
			
		||||
			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);
 | 
			
		||||
			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);
 | 
			
		||||
			imgui.text(m_text1.c_str());
 | 
			
		||||
			cursor_x = x_offset + ImGui::CalcTextSize(m_text1.c_str()).x;
 | 
			
		||||
		}
 | 
			
		||||
		//hyperlink text
 | 
			
		||||
		if (!m_hypertext.empty())
 | 
			
		||||
		{
 | 
			
		||||
			render_hypertext(imgui, cursor_x + 4, cursor_y, m_hypertext);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		//notification text 2
 | 
			
		||||
		//text 2 is suposed to be after the hyperlink - currently it is not used
 | 
			
		||||
		/*
 | 
			
		||||
		if (!m_text2.empty())
 | 
			
		||||
		{
 | 
			
		||||
			ImVec2 part_size = ImGui::CalcTextSize(m_hypertext.c_str());
 | 
			
		||||
			ImGui::SetCursorPosX(win_size.x / 2 + text_size.x / 2 - part_size.x + 8 - x_offset);
 | 
			
		||||
			ImGui::SetCursorPosY(cursor_y);
 | 
			
		||||
			imgui.text(m_text2.c_str());
 | 
			
		||||
		}
 | 
			
		||||
		*/
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void NotificationManager::PopNotification::render_hypertext(ImGuiWrapper& imgui, const float text_x, const float text_y, const std::string text, bool more)
 | 
			
		||||
{
 | 
			
		||||
	//invisible button
 | 
			
		||||
	ImVec2 part_size = ImGui::CalcTextSize(text.c_str());
 | 
			
		||||
	ImGui::SetCursorPosX(text_x -4);
 | 
			
		||||
	ImGui::SetCursorPosY(text_y -5);
 | 
			
		||||
	ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f));
 | 
			
		||||
	ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f));
 | 
			
		||||
	ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(.0f, .0f, .0f, .0f));
 | 
			
		||||
	if (imgui.button("   ", part_size.x + 6, part_size.y + 10))
 | 
			
		||||
	{
 | 
			
		||||
		if (more)
 | 
			
		||||
		{
 | 
			
		||||
			m_multiline = true;
 | 
			
		||||
			set_next_window_size(imgui);
 | 
			
		||||
		}
 | 
			
		||||
		else {
 | 
			
		||||
			on_text_click();
 | 
			
		||||
			m_close_pending = true;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	ImGui::PopStyleColor();
 | 
			
		||||
	ImGui::PopStyleColor();
 | 
			
		||||
	ImGui::PopStyleColor();
 | 
			
		||||
 | 
			
		||||
	//hover color
 | 
			
		||||
	ImVec4 orange_color = ImGui::GetStyleColorVec4(ImGuiCol_Button);
 | 
			
		||||
	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);
 | 
			
		||||
	ImGui::SetCursorPosX(text_x);
 | 
			
		||||
	ImGui::SetCursorPosY(text_y);
 | 
			
		||||
	imgui.text(text.c_str());
 | 
			
		||||
	ImGui::PopStyleColor();
 | 
			
		||||
 | 
			
		||||
	//underline
 | 
			
		||||
	ImVec2 lineEnd = ImGui::GetItemRectMax();
 | 
			
		||||
	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))));
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
	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));
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	//button - if part if treggered
 | 
			
		||||
	std::string button_text;
 | 
			
		||||
	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)),
 | 
			
		||||
		                           true))
 | 
			
		||||
	{
 | 
			
		||||
		button_text = ImGui::CloseIconHoverMarker;
 | 
			
		||||
	}
 | 
			
		||||
	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 * 2.25f);
 | 
			
		||||
	ImGui::SetCursorPosY(win_size.y / 2 - button_size.y/2);
 | 
			
		||||
	if (imgui.button(button_text.c_str(), button_size.x, button_size.y))
 | 
			
		||||
	{
 | 
			
		||||
		m_close_pending = true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	//invisible large button
 | 
			
		||||
	ImGui::SetCursorPosX(win_size.x - win_size.x / 10.f);
 | 
			
		||||
	ImGui::SetCursorPosY(0);
 | 
			
		||||
	if (imgui.button(" ", win_size.x / 10.f, win_size.y - (m_multiline ? 2 * m_line_height : 0)))
 | 
			
		||||
	{
 | 
			
		||||
		m_close_pending = true;
 | 
			
		||||
	}
 | 
			
		||||
	ImGui::PopStyleColor();
 | 
			
		||||
	ImGui::PopStyleColor();
 | 
			
		||||
	ImGui::PopStyleColor();
 | 
			
		||||
	ImGui::PopStyleColor();
 | 
			
		||||
	ImGui::PopStyleColor();
 | 
			
		||||
}
 | 
			
		||||
void NotificationManager::PopNotification::render_countdown(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);
 | 
			
		||||
	
 | 
			
		||||
	//countdown dots
 | 
			
		||||
	std::string dot_text;
 | 
			
		||||
	dot_text = m_remaining_time <= (float)m_data.duration / 4 * 3 ? ImGui::TimerDotEmptyMarker : ImGui::TimerDotMarker;
 | 
			
		||||
	ImGui::SetCursorPosX(win_size.x - m_line_height);
 | 
			
		||||
	//ImGui::SetCursorPosY(win_size.y / 2 - 24);
 | 
			
		||||
	ImGui::SetCursorPosY(0);
 | 
			
		||||
	imgui.text(dot_text.c_str());
 | 
			
		||||
 | 
			
		||||
	dot_text = m_remaining_time < m_data.duration / 2 ? ImGui::TimerDotEmptyMarker : ImGui::TimerDotMarker;
 | 
			
		||||
	ImGui::SetCursorPosX(win_size.x - m_line_height);
 | 
			
		||||
	//ImGui::SetCursorPosY(win_size.y / 2 - 9);
 | 
			
		||||
	ImGui::SetCursorPosY(win_size.y / 2 - m_line_height / 2);
 | 
			
		||||
	imgui.text(dot_text.c_str());
 | 
			
		||||
 | 
			
		||||
	dot_text = m_remaining_time <= m_data.duration / 4 ? ImGui::TimerDotEmptyMarker : ImGui::TimerDotMarker;
 | 
			
		||||
	ImGui::SetCursorPosX(win_size.x - m_line_height);
 | 
			
		||||
	//ImGui::SetCursorPosY(win_size.y / 2 + 6);
 | 
			
		||||
	ImGui::SetCursorPosY(win_size.y - m_line_height);
 | 
			
		||||
	imgui.text(dot_text.c_str());
 | 
			
		||||
	*/
 | 
			
		||||
	if (!m_fading_out && m_remaining_time <= m_data.duration / 4) {
 | 
			
		||||
		m_fading_out = true;
 | 
			
		||||
		m_fading_time = m_remaining_time;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	if (m_last_remaining_time != m_remaining_time) {
 | 
			
		||||
		m_last_remaining_time = m_remaining_time;
 | 
			
		||||
		m_countdown_frame = 0;
 | 
			
		||||
	}
 | 
			
		||||
	/*
 | 
			
		||||
	//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++;
 | 
			
		||||
		*/
 | 
			
		||||
}
 | 
			
		||||
void NotificationManager::PopNotification::render_left_sign(ImGuiWrapper& imgui)
 | 
			
		||||
{
 | 
			
		||||
	if (m_data.level == NotificationLevel::ErrorNotification || m_data.level == NotificationLevel::WarningNotification) {
 | 
			
		||||
		std::string text;
 | 
			
		||||
		text = (m_data.level == NotificationLevel::ErrorNotification ? ImGui::ErrorMarker : ImGui::WarningMarker);
 | 
			
		||||
		ImGui::SetCursorPosX(m_line_height / 3);
 | 
			
		||||
		ImGui::SetCursorPosY(m_window_height / 2 - m_line_height / 2);
 | 
			
		||||
		imgui.text(text.c_str());
 | 
			
		||||
	} 
 | 
			
		||||
}
 | 
			
		||||
void NotificationManager::PopNotification::render_minimize_button(ImGuiWrapper& imgui, const float win_pos_x, const float 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_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);
 | 
			
		||||
 | 
			
		||||
	
 | 
			
		||||
	//button - if part if treggered
 | 
			
		||||
	std::string button_text;
 | 
			
		||||
	button_text = ImGui::CloseIconMarker;
 | 
			
		||||
	if (ImGui::IsMouseHoveringRect(ImVec2(win_pos_x - m_window_width / 10.f, win_pos_y + m_window_height - 2 * m_line_height + 1),
 | 
			
		||||
		ImVec2(win_pos_x, win_pos_y + m_window_height),
 | 
			
		||||
		true)) 
 | 
			
		||||
	{
 | 
			
		||||
		button_text = ImGui::CloseIconHoverMarker;
 | 
			
		||||
	}
 | 
			
		||||
	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(m_window_width - m_line_height * 2.25f);
 | 
			
		||||
	ImGui::SetCursorPosY(m_window_height - button_size.y - 5);
 | 
			
		||||
	if (imgui.button(button_text.c_str(), button_size.x, button_size.y))
 | 
			
		||||
	{
 | 
			
		||||
		m_multiline = false;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	ImGui::PopStyleColor();
 | 
			
		||||
	ImGui::PopStyleColor();
 | 
			
		||||
	ImGui::PopStyleColor();
 | 
			
		||||
	ImGui::PopStyleColor();
 | 
			
		||||
	ImGui::PopStyleColor();
 | 
			
		||||
}
 | 
			
		||||
void NotificationManager::PopNotification::on_text_click()
 | 
			
		||||
{
 | 
			
		||||
	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);
 | 
			
		||||
		if (m_evt_handler != nullptr)
 | 
			
		||||
			wxPostEvent(m_evt_handler, ExportGcodeNotificationClickedEvent(EVT_EXPORT_GCODE_NOTIFICAION_CLICKED));
 | 
			
		||||
		break;
 | 
			
		||||
	case NotificationType::PresetUpdateAviable :
 | 
			
		||||
		//wxGetApp().plater()->export_gcode(false);
 | 
			
		||||
		assert(m_evt_handler != nullptr);
 | 
			
		||||
		if (m_evt_handler != nullptr)
 | 
			
		||||
			wxPostEvent(m_evt_handler, PresetUpdateAviableClickedEvent(EVT_PRESET_UPDATE_AVIABLE_CLICKED));
 | 
			
		||||
		break;
 | 
			
		||||
	case NotificationType::NewAppAviable:
 | 
			
		||||
		wxLaunchDefaultBrowser("https://github.com/prusa3d/PrusaSlicer/releases");
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
void NotificationManager::PopNotification::update(const NotificationData& n)
 | 
			
		||||
{
 | 
			
		||||
	m_text1          = n.text1;
 | 
			
		||||
	m_hypertext      = n.hypertext;
 | 
			
		||||
    m_text2          = n.text2;
 | 
			
		||||
	init();
 | 
			
		||||
}
 | 
			
		||||
bool NotificationManager::PopNotification::compare_text(const std::string& text)
 | 
			
		||||
{
 | 
			
		||||
	std::string t1(m_text1);
 | 
			
		||||
	std::string t2(text);
 | 
			
		||||
	t1.erase(std::remove_if(t1.begin(), t1.end(), ::isspace), t1.end());
 | 
			
		||||
	t2.erase(std::remove_if(t2.begin(), t2.end(), ::isspace), t2.end());
 | 
			
		||||
	if (t1.compare(t2) == 0)
 | 
			
		||||
		return true;
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
NotificationManager::SlicingCompleteLargeNotification::SlicingCompleteLargeNotification(const NotificationData& n, const int id, wxEvtHandler* evt_handler, bool large) :
 | 
			
		||||
	  NotificationManager::PopNotification(n, id, evt_handler)
 | 
			
		||||
{
 | 
			
		||||
	set_large(large);
 | 
			
		||||
}
 | 
			
		||||
void NotificationManager::SlicingCompleteLargeNotification::render_text(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
 | 
			
		||||
{
 | 
			
		||||
	if (!m_is_large)
 | 
			
		||||
		PopNotification::render_text(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
 | 
			
		||||
	else {
 | 
			
		||||
		ImVec2 win_size(win_size_x, win_size_y);
 | 
			
		||||
		ImVec2 win_pos(win_pos_x, win_pos_y);
 | 
			
		||||
 | 
			
		||||
		ImVec2 text1_size = ImGui::CalcTextSize(m_text1.c_str());
 | 
			
		||||
		float x_offset = m_left_indentation;
 | 
			
		||||
		std::string fulltext = m_text1 + m_hypertext + m_text2;
 | 
			
		||||
		ImVec2 text_size = ImGui::CalcTextSize(fulltext.c_str());
 | 
			
		||||
		float cursor_y = win_size.y / 2 - text_size.y / 2;
 | 
			
		||||
		if (m_has_print_info) {
 | 
			
		||||
			x_offset = 20;
 | 
			
		||||
			cursor_y = win_size.y / 2 + win_size.y / 6 - text_size.y / 2;
 | 
			
		||||
			ImGui::SetCursorPosX(x_offset);
 | 
			
		||||
			ImGui::SetCursorPosY(cursor_y);
 | 
			
		||||
			imgui.text(m_print_info.c_str());
 | 
			
		||||
			cursor_y = win_size.y / 2 - win_size.y / 6 - text_size.y / 2;
 | 
			
		||||
		}
 | 
			
		||||
		ImGui::SetCursorPosX(x_offset);
 | 
			
		||||
		ImGui::SetCursorPosY(cursor_y);
 | 
			
		||||
		imgui.text(m_text1.c_str());
 | 
			
		||||
 | 
			
		||||
		render_hypertext(imgui, x_offset + text1_size.x + 4, cursor_y, m_hypertext);
 | 
			
		||||
		
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
void NotificationManager::SlicingCompleteLargeNotification::set_print_info(std::string info)
 | 
			
		||||
{
 | 
			
		||||
	m_print_info = info;
 | 
			
		||||
	m_has_print_info = true;
 | 
			
		||||
	if(m_is_large)
 | 
			
		||||
		m_lines_count = 2;
 | 
			
		||||
}
 | 
			
		||||
void NotificationManager::SlicingCompleteLargeNotification::set_large(bool l)
 | 
			
		||||
{
 | 
			
		||||
	m_is_large = l;
 | 
			
		||||
	m_counting_down = !l;
 | 
			
		||||
	m_hypertext = l ? _u8L("Export G-Code.") : std::string();
 | 
			
		||||
	m_hidden = !l;
 | 
			
		||||
}
 | 
			
		||||
//------NotificationManager--------
 | 
			
		||||
NotificationManager::NotificationManager(wxEvtHandler* evt_handler) :
 | 
			
		||||
	m_evt_handler(evt_handler)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
NotificationManager::~NotificationManager()
 | 
			
		||||
{
 | 
			
		||||
	for (PopNotification* notification : m_pop_notifications)
 | 
			
		||||
	{
 | 
			
		||||
		delete notification;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
void NotificationManager::push_notification(const NotificationType type, GLCanvas3D& canvas, int timestamp)
 | 
			
		||||
{
 | 
			
		||||
	auto it = std::find_if(basic_notifications.begin(), basic_notifications.end(),
 | 
			
		||||
		boost::bind(&NotificationData::type, _1) == type);	
 | 
			
		||||
	if (it != basic_notifications.end())
 | 
			
		||||
		push_notification_data( *it, canvas, timestamp);
 | 
			
		||||
}
 | 
			
		||||
void NotificationManager::push_notification(const std::string& text, GLCanvas3D& canvas, int timestamp)
 | 
			
		||||
{
 | 
			
		||||
	push_notification_data({ NotificationType::CustomNotification, NotificationLevel::RegularNotification, 10, text }, canvas, timestamp );
 | 
			
		||||
}
 | 
			
		||||
void NotificationManager::push_notification(const std::string& text, NotificationManager::NotificationLevel level, GLCanvas3D& canvas, int timestamp)
 | 
			
		||||
{
 | 
			
		||||
	switch (level)
 | 
			
		||||
	{
 | 
			
		||||
	case Slic3r::GUI::NotificationManager::NotificationLevel::RegularNotification:
 | 
			
		||||
		push_notification_data({ NotificationType::CustomNotification, level, 10, text }, canvas, timestamp);
 | 
			
		||||
		break;
 | 
			
		||||
	case Slic3r::GUI::NotificationManager::NotificationLevel::ErrorNotification:
 | 
			
		||||
		push_notification_data({ NotificationType::CustomNotification, level, 0, text }, canvas, timestamp);
 | 
			
		||||
 | 
			
		||||
		break;
 | 
			
		||||
	case Slic3r::GUI::NotificationManager::NotificationLevel::ImportantNotification:
 | 
			
		||||
		push_notification_data({ NotificationType::CustomNotification, level, 0, text }, canvas, timestamp);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
void NotificationManager::push_slicing_error_notification(const std::string& text, GLCanvas3D& canvas)
 | 
			
		||||
{
 | 
			
		||||
	set_all_slicing_errors_gray(false);
 | 
			
		||||
	push_notification_data({ NotificationType::SlicingError, NotificationLevel::ErrorNotification, 0,  _u8L("ERROR:") + "\n" + text }, canvas, 0);
 | 
			
		||||
	close_notification_of_type(NotificationType::SlicingComplete);
 | 
			
		||||
}
 | 
			
		||||
void NotificationManager::push_slicing_warning_notification(const std::string& text, bool gray, GLCanvas3D& canvas, size_t oid, int warning_step)
 | 
			
		||||
{
 | 
			
		||||
	NotificationData data { NotificationType::SlicingWarning, NotificationLevel::WarningNotification, 0,  _u8L("WARNING:") + "\n" + text };
 | 
			
		||||
 | 
			
		||||
	NotificationManager::SlicingWarningNotification* notification = new NotificationManager::SlicingWarningNotification(data, m_next_id++, m_evt_handler);
 | 
			
		||||
	notification->set_object_id(oid);
 | 
			
		||||
	notification->set_warning_step(warning_step);
 | 
			
		||||
	if 
 | 
			
		||||
		(push_notification_data(notification, canvas, 0)) {
 | 
			
		||||
		notification->set_gray(gray);		
 | 
			
		||||
	}
 | 
			
		||||
	else {
 | 
			
		||||
		delete notification;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
}
 | 
			
		||||
void NotificationManager::push_plater_error_notification(const std::string& text, GLCanvas3D& canvas)
 | 
			
		||||
{
 | 
			
		||||
	push_notification_data({ NotificationType::PlaterError, NotificationLevel::ErrorNotification, 0,  _u8L("ERROR:") + "\n" + text }, canvas, 0);
 | 
			
		||||
}
 | 
			
		||||
void NotificationManager::push_plater_warning_notification(const std::string& text, GLCanvas3D& canvas)
 | 
			
		||||
{
 | 
			
		||||
	push_notification_data({ NotificationType::PlaterWarning, NotificationLevel::WarningNotification, 0,  _u8L("WARNING:") + "\n" + text }, canvas, 0);
 | 
			
		||||
}
 | 
			
		||||
void NotificationManager::close_plater_error_notification()
 | 
			
		||||
{
 | 
			
		||||
	for (PopNotification* notification : m_pop_notifications) {
 | 
			
		||||
		if (notification->get_type() == NotificationType::PlaterError) {
 | 
			
		||||
			notification->close();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
void NotificationManager::close_plater_warning_notification(const std::string& text)
 | 
			
		||||
{
 | 
			
		||||
	for (PopNotification* notification : m_pop_notifications) {
 | 
			
		||||
		if (notification->get_type() == NotificationType::PlaterWarning && notification->compare_text(_u8L("WARNING:") + "\n" + text)) {
 | 
			
		||||
			notification->close();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
void NotificationManager::set_all_slicing_errors_gray(bool g)
 | 
			
		||||
{
 | 
			
		||||
	for (PopNotification* notification : m_pop_notifications) {
 | 
			
		||||
		if (notification->get_type() == NotificationType::SlicingError) {
 | 
			
		||||
			notification->set_gray(g);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
void NotificationManager::set_all_slicing_warnings_gray(bool g)
 | 
			
		||||
{
 | 
			
		||||
	for (PopNotification* notification : m_pop_notifications) {
 | 
			
		||||
		if (notification->get_type() == NotificationType::SlicingWarning) {
 | 
			
		||||
			notification->set_gray(g);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
void NotificationManager::set_slicing_warning_gray(const std::string& text, bool g)
 | 
			
		||||
{
 | 
			
		||||
	for (PopNotification* notification : m_pop_notifications) {
 | 
			
		||||
		if (notification->get_type() == NotificationType::SlicingWarning && notification->compare_text(text)) {
 | 
			
		||||
			notification->set_gray(g);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
void NotificationManager::close_slicing_errors_and_warnings()
 | 
			
		||||
{
 | 
			
		||||
	for (PopNotification* notification : m_pop_notifications) {
 | 
			
		||||
		if (notification->get_type() == NotificationType::SlicingError || notification->get_type() == NotificationType::SlicingWarning) {
 | 
			
		||||
			notification->close();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
void NotificationManager::push_slicing_complete_notification(GLCanvas3D& canvas, int timestamp, bool large)
 | 
			
		||||
{
 | 
			
		||||
	std::string hypertext;
 | 
			
		||||
	int         time = 10;
 | 
			
		||||
	if(large)
 | 
			
		||||
	{
 | 
			
		||||
		hypertext = _u8L("Export G-Code.");
 | 
			
		||||
		time = 0;
 | 
			
		||||
	}
 | 
			
		||||
	NotificationData data{ NotificationType::SlicingComplete, NotificationLevel::RegularNotification, time,  _u8L("Slicing finished."), hypertext };
 | 
			
		||||
 | 
			
		||||
	NotificationManager::SlicingCompleteLargeNotification* notification = new NotificationManager::SlicingCompleteLargeNotification(data, m_next_id++, m_evt_handler, large);
 | 
			
		||||
	if (push_notification_data(notification, canvas, timestamp)) {
 | 
			
		||||
	} else {
 | 
			
		||||
		delete notification;
 | 
			
		||||
	}	
 | 
			
		||||
}
 | 
			
		||||
void NotificationManager::set_slicing_complete_print_time(std::string info)
 | 
			
		||||
{
 | 
			
		||||
	for (PopNotification* notification : m_pop_notifications) {
 | 
			
		||||
		if (notification->get_type() == NotificationType::SlicingComplete) {
 | 
			
		||||
			dynamic_cast<SlicingCompleteLargeNotification*>(notification)->set_print_info(info);
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
void NotificationManager::set_slicing_complete_large(bool large)
 | 
			
		||||
{
 | 
			
		||||
	for (PopNotification* notification : m_pop_notifications) {
 | 
			
		||||
		if (notification->get_type() == NotificationType::SlicingComplete) {
 | 
			
		||||
			dynamic_cast<SlicingCompleteLargeNotification*>(notification)->set_large(large);
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
void NotificationManager::close_notification_of_type(const NotificationType type)
 | 
			
		||||
{
 | 
			
		||||
	for (PopNotification* notification : m_pop_notifications) {
 | 
			
		||||
		if (notification->get_type() == type) {
 | 
			
		||||
			notification->close();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
void NotificationManager::compare_warning_oids(const std::vector<size_t>& living_oids)
 | 
			
		||||
{
 | 
			
		||||
	for (PopNotification* notification : m_pop_notifications) {
 | 
			
		||||
		if (notification->get_type() == NotificationType::SlicingWarning) {
 | 
			
		||||
			auto w = dynamic_cast<SlicingWarningNotification*>(notification);
 | 
			
		||||
			bool found = false;
 | 
			
		||||
			for (size_t oid : living_oids) {
 | 
			
		||||
				if (w->get_object_id() == oid) {
 | 
			
		||||
					found = true;
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if (!found)
 | 
			
		||||
				notification->close();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
bool NotificationManager::push_notification_data(const NotificationData ¬ification_data,  GLCanvas3D& canvas, int timestamp)
 | 
			
		||||
{
 | 
			
		||||
	PopNotification* n = new PopNotification(notification_data, m_next_id++, m_evt_handler);
 | 
			
		||||
	bool r = push_notification_data(n, canvas, timestamp);
 | 
			
		||||
	if (!r)
 | 
			
		||||
		delete n;
 | 
			
		||||
	return r;
 | 
			
		||||
}
 | 
			
		||||
bool NotificationManager::push_notification_data(NotificationManager::PopNotification* notification, GLCanvas3D& canvas, int timestamp)
 | 
			
		||||
{
 | 
			
		||||
	// if timestamped notif, push only new one
 | 
			
		||||
	if (timestamp != 0) {
 | 
			
		||||
		if (m_used_timestamps.find(timestamp) == m_used_timestamps.end()) {
 | 
			
		||||
			m_used_timestamps.insert(timestamp);
 | 
			
		||||
		} else {
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if (!this->find_older(notification)) {
 | 
			
		||||
			m_pop_notifications.emplace_back(notification);
 | 
			
		||||
		canvas.request_extra_frame();
 | 
			
		||||
		return true;
 | 
			
		||||
	} else {
 | 
			
		||||
		m_pop_notifications.back()->update(notification->get_data());
 | 
			
		||||
		canvas.request_extra_frame();
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
void NotificationManager::render_notifications(GLCanvas3D& canvas)
 | 
			
		||||
{
 | 
			
		||||
	float    last_x = 0.0f;
 | 
			
		||||
	float    current_height = 0.0f;
 | 
			
		||||
	bool     request_next_frame = false;
 | 
			
		||||
	bool     render_main = false;
 | 
			
		||||
	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();) {
 | 
			
		||||
		if ((*it)->get_finished()) {
 | 
			
		||||
			delete (*it);
 | 
			
		||||
			it = m_pop_notifications.erase(it);
 | 
			
		||||
		} else {
 | 
			
		||||
			(*it)->set_paused(m_hovered);
 | 
			
		||||
			PopNotification::RenderResult res = (*it)->render(canvas, last_x);
 | 
			
		||||
			if (res != PopNotification::RenderResult::Finished) {
 | 
			
		||||
				last_x = (*it)->get_top() + GAP_WIDTH;
 | 
			
		||||
				current_height = std::max(current_height, (*it)->get_current_top());
 | 
			
		||||
				render_main = true;
 | 
			
		||||
			}
 | 
			
		||||
			if (res == PopNotification::RenderResult::Countdown || res == PopNotification::RenderResult::ClosePending || res == PopNotification::RenderResult::Finished)
 | 
			
		||||
				request_next_frame = true;
 | 
			
		||||
			if (res == PopNotification::RenderResult::Hovered)
 | 
			
		||||
				hovered = true;
 | 
			
		||||
			++it;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	m_hovered = hovered;
 | 
			
		||||
 | 
			
		||||
	//actualizate timers and request frame if needed
 | 
			
		||||
	wxWindow* p = dynamic_cast<wxWindow*> (wxGetApp().plater());
 | 
			
		||||
	while (p->GetParent())
 | 
			
		||||
		p = p->GetParent();
 | 
			
		||||
	wxTopLevelWindow* top_level_wnd = dynamic_cast<wxTopLevelWindow*>(p);
 | 
			
		||||
	if (!top_level_wnd->IsActive())
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (!m_hovered && m_last_time < wxGetLocalTime())
 | 
			
		||||
	{
 | 
			
		||||
		if (wxGetLocalTime() - m_last_time == 1)
 | 
			
		||||
		{
 | 
			
		||||
			for(auto notification : m_pop_notifications)
 | 
			
		||||
			{
 | 
			
		||||
				notification->substract_remaining_time();
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		m_last_time = wxGetLocalTime();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (request_next_frame)
 | 
			
		||||
		canvas.request_extra_frame();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void NotificationManager::sort_notifications()
 | 
			
		||||
{
 | 
			
		||||
	std::sort(m_pop_notifications.begin(), m_pop_notifications.end(), [](PopNotification* n1, PopNotification* n2) {
 | 
			
		||||
		int n1l = (int)n1->get_data().level;
 | 
			
		||||
		int n2l = (int)n2->get_data().level;
 | 
			
		||||
		if (n1l == n2l && n1->get_is_gray() && !n2->get_is_gray())
 | 
			
		||||
			return true;
 | 
			
		||||
		return (n1l < n2l);
 | 
			
		||||
		});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool NotificationManager::find_older(NotificationManager::PopNotification* notification)
 | 
			
		||||
{
 | 
			
		||||
	NotificationType type = notification->get_type();
 | 
			
		||||
	std::string text = notification->get_data().text1;
 | 
			
		||||
	for (auto it = m_pop_notifications.begin(); it != m_pop_notifications.end(); ++it) {
 | 
			
		||||
		if((*it)->get_type() == type && !(*it)->get_finished()) {
 | 
			
		||||
			if (type == NotificationType::CustomNotification || type == NotificationType::PlaterWarning) {
 | 
			
		||||
				if (!(*it)->compare_text(text))
 | 
			
		||||
					continue;
 | 
			
		||||
			}else if (type == NotificationType::SlicingWarning) {
 | 
			
		||||
				auto w1 = dynamic_cast<SlicingWarningNotification*>(notification);
 | 
			
		||||
				auto w2 = dynamic_cast<SlicingWarningNotification*>(*it);
 | 
			
		||||
				if (w1 != nullptr && w2 != nullptr) {
 | 
			
		||||
					if (!(*it)->compare_text(text) || w1->get_object_id() != w2->get_object_id()) {
 | 
			
		||||
						continue;
 | 
			
		||||
					}
 | 
			
		||||
				} else {
 | 
			
		||||
					continue;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (it != m_pop_notifications.end() - 1)
 | 
			
		||||
				std::rotate(it, it + 1, m_pop_notifications.end());
 | 
			
		||||
			return true;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void NotificationManager::dpi_changed()
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}//namespace GUI
 | 
			
		||||
}//namespace Slic3r
 | 
			
		||||
							
								
								
									
										268
									
								
								src/slic3r/GUI/NotificationManager.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										268
									
								
								src/slic3r/GUI/NotificationManager.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,268 @@
 | 
			
		|||
#ifndef slic3r_GUI_NotificationManager_hpp_
 | 
			
		||||
#define slic3r_GUI_NotificationManager_hpp_
 | 
			
		||||
 | 
			
		||||
#include "Event.hpp"
 | 
			
		||||
#include "I18N.hpp"
 | 
			
		||||
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <deque>
 | 
			
		||||
#include <unordered_set>
 | 
			
		||||
 | 
			
		||||
namespace Slic3r {
 | 
			
		||||
namespace GUI {
 | 
			
		||||
 | 
			
		||||
using EjectDriveNotificationClickedEvent = SimpleEvent;
 | 
			
		||||
wxDECLARE_EVENT(EVT_EJECT_DRIVE_NOTIFICAION_CLICKED, EjectDriveNotificationClickedEvent);
 | 
			
		||||
using ExportGcodeNotificationClickedEvent = SimpleEvent;
 | 
			
		||||
wxDECLARE_EVENT(EVT_EXPORT_GCODE_NOTIFICAION_CLICKED, ExportGcodeNotificationClickedEvent);
 | 
			
		||||
using PresetUpdateAviableClickedEvent = SimpleEvent;
 | 
			
		||||
wxDECLARE_EVENT(EVT_PRESET_UPDATE_AVIABLE_CLICKED, PresetUpdateAviableClickedEvent);
 | 
			
		||||
 | 
			
		||||
class GLCanvas3D;
 | 
			
		||||
class ImGuiWrapper;
 | 
			
		||||
 | 
			
		||||
enum class NotificationType
 | 
			
		||||
{
 | 
			
		||||
	CustomNotification,
 | 
			
		||||
	SlicingComplete,
 | 
			
		||||
	SlicingNotPossible,
 | 
			
		||||
	ExportToRemovableFinished,
 | 
			
		||||
	Mouse3dDisconnected,
 | 
			
		||||
	Mouse3dConnected,
 | 
			
		||||
	NewPresetsAviable,
 | 
			
		||||
	NewAppAviable,
 | 
			
		||||
	PresetUpdateAviable,
 | 
			
		||||
	LoadingFailed,
 | 
			
		||||
	ValidateError, // currently not used - instead Slicing error is used for both slicing and validate errors
 | 
			
		||||
	SlicingError,
 | 
			
		||||
	SlicingWarning,
 | 
			
		||||
	PlaterError,
 | 
			
		||||
	PlaterWarning,
 | 
			
		||||
	ApplyError
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
class NotificationManager
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	enum class NotificationLevel : int
 | 
			
		||||
	{
 | 
			
		||||
		ErrorNotification =     4,
 | 
			
		||||
		WarningNotification =   3,
 | 
			
		||||
		ImportantNotification = 2,
 | 
			
		||||
		RegularNotification   = 1,
 | 
			
		||||
	};
 | 
			
		||||
	// duration 0 means not disapearing
 | 
			
		||||
	struct NotificationData {
 | 
			
		||||
		NotificationType    type;
 | 
			
		||||
		NotificationLevel   level;
 | 
			
		||||
		const int           duration;
 | 
			
		||||
		const std::string   text1;
 | 
			
		||||
		const std::string   hypertext = std::string();
 | 
			
		||||
		const std::string   text2     = std::string();
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	//Pop notification - shows only once to user.
 | 
			
		||||
	class PopNotification
 | 
			
		||||
	{
 | 
			
		||||
	public:
 | 
			
		||||
		enum class RenderResult
 | 
			
		||||
		{
 | 
			
		||||
			Finished,
 | 
			
		||||
			ClosePending,
 | 
			
		||||
			Static,
 | 
			
		||||
			Countdown,
 | 
			
		||||
			Hovered
 | 
			
		||||
		};
 | 
			
		||||
		 PopNotification(const NotificationData &n, const int id, wxEvtHandler* evt_handler);
 | 
			
		||||
		virtual ~PopNotification();
 | 
			
		||||
		RenderResult           render(GLCanvas3D& canvas, const float& initial_y);
 | 
			
		||||
		// close will dissapear notification on next render
 | 
			
		||||
		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; }
 | 
			
		||||
		// returns top after movement
 | 
			
		||||
		float                  get_top() const { return m_top_y; }
 | 
			
		||||
		//returns top in actual frame
 | 
			
		||||
		float                  get_current_top() const { return m_top_y; }
 | 
			
		||||
		const NotificationType get_type() const { return m_data.type; }
 | 
			
		||||
		const NotificationData get_data() const { return m_data;  }
 | 
			
		||||
		const bool             get_is_gray() const { return m_is_gray; }
 | 
			
		||||
		// Call equals one second down
 | 
			
		||||
		void                   substract_remaining_time() { m_remaining_time--; }
 | 
			
		||||
		void                   set_gray(bool g) { m_is_gray = g; }
 | 
			
		||||
		void                   set_paused(bool p) { m_paused = p; }
 | 
			
		||||
		bool                   compare_text(const std::string& text);
 | 
			
		||||
	protected:
 | 
			
		||||
		// Call after every size change
 | 
			
		||||
		void         init();
 | 
			
		||||
		// 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,
 | 
			
		||||
			                             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,
 | 
			
		||||
			                          const float text_x, const float text_y,
 | 
			
		||||
		                              const std::string text,
 | 
			
		||||
		                              bool more = false);
 | 
			
		||||
		void         render_left_sign(ImGuiWrapper& imgui);
 | 
			
		||||
		void         render_minimize_button(ImGuiWrapper& imgui,
 | 
			
		||||
			                                const float win_pos_x, const float win_pos_y);
 | 
			
		||||
		void         on_text_click();
 | 
			
		||||
 | 
			
		||||
		const NotificationData m_data;
 | 
			
		||||
 | 
			
		||||
		int              m_id;
 | 
			
		||||
		// 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 };
 | 
			
		||||
		// total time left when fading beggins
 | 
			
		||||
		float            m_fading_time{ 0.0f }; 
 | 
			
		||||
		float            m_current_fade_opacity{ 1.f };
 | 
			
		||||
		// 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 }; 
 | 
			
		||||
		// variables to count positions correctly
 | 
			
		||||
		float            m_window_width_offset;
 | 
			
		||||
		float            m_left_indentation;
 | 
			
		||||
		// Total size of notification window - varies based on monitor
 | 
			
		||||
		float            m_window_height        { 56.0f };  
 | 
			
		||||
		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!
 | 
			
		||||
		float            m_line_height;
 | 
			
		||||
		std::vector<int> m_endlines;
 | 
			
		||||
		// Gray are f.e. eorrors when its uknown if they are still valid
 | 
			
		||||
		bool             m_is_gray              { false };
 | 
			
		||||
		//if multiline = true, notification is showing all lines(>2)
 | 
			
		||||
		bool             m_multiline            { false };
 | 
			
		||||
		int              m_lines_count{ 1 };
 | 
			
		||||
		wxEvtHandler*    m_evt_handler;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	class SlicingCompleteLargeNotification : public PopNotification
 | 
			
		||||
	{
 | 
			
		||||
	public:
 | 
			
		||||
		SlicingCompleteLargeNotification(const NotificationData& n, const int id, wxEvtHandler* evt_handler, bool largeds);
 | 
			
		||||
		void set_large(bool l);
 | 
			
		||||
		bool get_large() { return m_is_large; }
 | 
			
		||||
 | 
			
		||||
		void set_print_info(std::string info);
 | 
			
		||||
	protected:
 | 
			
		||||
		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;
 | 
			
		||||
 | 
			
		||||
		bool        m_is_large;
 | 
			
		||||
		bool        m_has_print_info { false };
 | 
			
		||||
		std::string m_print_info { std::string() };
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	class SlicingWarningNotification : public PopNotification
 | 
			
		||||
	{
 | 
			
		||||
	public:
 | 
			
		||||
		SlicingWarningNotification(const NotificationData& n, const int id, wxEvtHandler* evt_handler) : PopNotification(n, id, evt_handler) {}
 | 
			
		||||
		void         set_object_id(size_t id) { object_id = id; }
 | 
			
		||||
		const size_t get_object_id() { return object_id; }
 | 
			
		||||
		void         set_warning_step(int ws) { warning_step = ws; }
 | 
			
		||||
		const int    get_warning_step() { return warning_step; }
 | 
			
		||||
	protected:
 | 
			
		||||
		size_t object_id;
 | 
			
		||||
		int    warning_step;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	NotificationManager(wxEvtHandler* evt_handler);
 | 
			
		||||
	~NotificationManager();
 | 
			
		||||
 | 
			
		||||
	
 | 
			
		||||
	// only type means one of basic_notification (see below)
 | 
			
		||||
	void push_notification(const NotificationType type, GLCanvas3D& canvas, int timestamp = 0);
 | 
			
		||||
	// only text means Undefined type
 | 
			
		||||
	void push_notification(const std::string& text, GLCanvas3D& canvas, int timestamp = 0);
 | 
			
		||||
	void push_notification(const std::string& text, NotificationLevel level, GLCanvas3D& canvas, int timestamp = 0);
 | 
			
		||||
	// creates Slicing Error notification with custom text
 | 
			
		||||
	void push_slicing_error_notification(const std::string& text, GLCanvas3D& canvas);
 | 
			
		||||
	// creates Slicing Warning notification with custom text
 | 
			
		||||
	void push_slicing_warning_notification(const std::string& text, bool gray, GLCanvas3D& canvas, size_t oid, int warning_step);
 | 
			
		||||
	// marks slicing errors as gray
 | 
			
		||||
	void set_all_slicing_errors_gray(bool g);
 | 
			
		||||
	// marks slicing warings as gray
 | 
			
		||||
	void set_all_slicing_warnings_gray(bool g);
 | 
			
		||||
	void set_slicing_warning_gray(const std::string& text, bool g);
 | 
			
		||||
	// imidietly stops showing slicing errors
 | 
			
		||||
	void close_slicing_errors_and_warnings();
 | 
			
		||||
	void compare_warning_oids(const std::vector<size_t>& living_oids);
 | 
			
		||||
	void push_plater_error_notification(const std::string& text, GLCanvas3D& canvas);
 | 
			
		||||
	void push_plater_warning_notification(const std::string& text, GLCanvas3D& canvas);
 | 
			
		||||
	void close_plater_error_notification();
 | 
			
		||||
	void close_plater_warning_notification(const std::string& text);
 | 
			
		||||
	// creates special notification slicing complete
 | 
			
		||||
	// if large = true prints printing time and export button 
 | 
			
		||||
	void push_slicing_complete_notification(GLCanvas3D& canvas, int timestamp, bool large);
 | 
			
		||||
	void set_slicing_complete_print_time(std::string info);
 | 
			
		||||
	void set_slicing_complete_large(bool large);
 | 
			
		||||
	// renders notifications in queue and deletes expired ones
 | 
			
		||||
	void render_notifications(GLCanvas3D& canvas);
 | 
			
		||||
	// finds and closes all notifications of given type
 | 
			
		||||
	void close_notification_of_type(const NotificationType type);
 | 
			
		||||
	void dpi_changed();
 | 
			
		||||
private:
 | 
			
		||||
	//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);
 | 
			
		||||
	bool push_notification_data(NotificationManager::PopNotification* notification, GLCanvas3D& canvas, int timestamp);
 | 
			
		||||
	//finds older notification of same type and moves it to the end of queue. returns true if found
 | 
			
		||||
	bool find_older(NotificationManager::PopNotification* notification);
 | 
			
		||||
	void sort_notifications();
 | 
			
		||||
 | 
			
		||||
	wxEvtHandler*                m_evt_handler;
 | 
			
		||||
	std::deque<PopNotification*> m_pop_notifications;
 | 
			
		||||
	int                          m_next_id { 1 };
 | 
			
		||||
	long                         m_last_time { 0 };
 | 
			
		||||
	bool                         m_hovered { false };
 | 
			
		||||
	//timestamps used for slining finished - notification could be gone so it needs to be stored here
 | 
			
		||||
	std::unordered_set<int>      m_used_timestamps;
 | 
			
		||||
 | 
			
		||||
	//prepared (basic) notifications
 | 
			
		||||
	const std::vector<NotificationData> 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::PresetUpdateAviable, NotificationLevel::ImportantNotification, 20,  _u8L("Configuration update is available."),  _u8L("See more.")},
 | 
			
		||||
		{NotificationType::NewAppAviable, NotificationLevel::ImportantNotification, 20,  _u8L("New version is available."),  _u8L("See Releases page.")},
 | 
			
		||||
		//{NotificationType::NewAppAviable, NotificationLevel::ImportantNotification, 20,  _u8L("New vesion of PrusaSlicer is available.",  _u8L("Download page.") },
 | 
			
		||||
		//{NotificationType::LoadingFailed, NotificationLevel::RegularNotification, 20,  _u8L("Loading of model has Failed") },
 | 
			
		||||
		//{NotificationType::DeviceEjected, NotificationLevel::RegularNotification, 10,  _u8L("Removable device has been safely ejected")} // if we want changeble text (like here name of device), we need to do it as CustomNotification
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}//namespace GUI
 | 
			
		||||
}//namespace Slic3r
 | 
			
		||||
 | 
			
		||||
#endif //slic3r_GUI_NotificationManager_hpp_
 | 
			
		||||
| 
						 | 
				
			
			@ -75,8 +75,10 @@
 | 
			
		|||
#include "../Utils/PrintHost.hpp"
 | 
			
		||||
#include "../Utils/FixModelByWin10.hpp"
 | 
			
		||||
#include "../Utils/UndoRedo.hpp"
 | 
			
		||||
#include "../Utils/PresetUpdater.hpp"
 | 
			
		||||
#include "RemovableDriveManager.hpp"
 | 
			
		||||
#include "InstanceCheck.hpp"
 | 
			
		||||
#include "NotificationManager.hpp"
 | 
			
		||||
 | 
			
		||||
#ifdef __APPLE__
 | 
			
		||||
#include "Gizmos/GLGizmosManager.hpp"
 | 
			
		||||
| 
						 | 
				
			
			@ -102,6 +104,7 @@ wxDEFINE_EVENT(EVT_SCHEDULE_BACKGROUND_PROCESS,     SimpleEvent);
 | 
			
		|||
wxDEFINE_EVENT(EVT_SLICING_UPDATE,                  SlicingStatusEvent);
 | 
			
		||||
wxDEFINE_EVENT(EVT_SLICING_COMPLETED,               wxCommandEvent);
 | 
			
		||||
wxDEFINE_EVENT(EVT_PROCESS_COMPLETED,               wxCommandEvent);
 | 
			
		||||
wxDEFINE_EVENT(EVT_EXPORT_BEGAN,                    wxCommandEvent);
 | 
			
		||||
 | 
			
		||||
// Sidebar widgets
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -716,7 +719,7 @@ struct Sidebar::priv
 | 
			
		|||
    wxButton *btn_export_gcode;
 | 
			
		||||
    wxButton *btn_reslice;
 | 
			
		||||
    ScalableButton *btn_send_gcode;
 | 
			
		||||
    ScalableButton *btn_remove_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};
 | 
			
		||||
| 
						 | 
				
			
			@ -889,12 +892,12 @@ Sidebar::Sidebar(Plater *parent)
 | 
			
		|||
    };
 | 
			
		||||
 | 
			
		||||
    init_scalable_btn(&p->btn_send_gcode   , "export_gcode", _L("Send to printer") + "\tCtrl+Shift+G");
 | 
			
		||||
    init_scalable_btn(&p->btn_remove_device, "eject_sd"       , _L("Remove device") + "\tCtrl+T");
 | 
			
		||||
    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");
 | 
			
		||||
 | 
			
		||||
    // regular buttons "Slice now" and "Export G-code" 
 | 
			
		||||
 | 
			
		||||
    const int scaled_height = p->btn_remove_device->GetBitmapHeight() + 4;
 | 
			
		||||
    const int scaled_height = p->btn_eject_device->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);
 | 
			
		||||
| 
						 | 
				
			
			@ -912,7 +915,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_remove_device);
 | 
			
		||||
    complect_btns_sizer->Add(p->btn_eject_device);
 | 
			
		||||
	
 | 
			
		||||
 | 
			
		||||
    btns_sizer->Add(p->btn_reslice, 0, wxEXPAND | wxTOP, margin_5);
 | 
			
		||||
| 
						 | 
				
			
			@ -935,7 +938,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_remove_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); });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1083,9 +1086,9 @@ void Sidebar::msw_rescale()
 | 
			
		|||
    p->object_info->msw_rescale();
 | 
			
		||||
 | 
			
		||||
    p->btn_send_gcode->msw_rescale();
 | 
			
		||||
    p->btn_remove_device->msw_rescale();
 | 
			
		||||
    p->btn_eject_device->msw_rescale();
 | 
			
		||||
	p->btn_export_gcode_removable->msw_rescale();
 | 
			
		||||
    const int scaled_height = p->btn_remove_device->GetBitmap().GetHeight() + 4;
 | 
			
		||||
    const int scaled_height = p->btn_eject_device->GetBitmap().GetHeight() + 4;
 | 
			
		||||
    p->btn_export_gcode->SetMinSize(wxSize(-1, scaled_height));
 | 
			
		||||
    p->btn_reslice     ->SetMinSize(wxSize(-1, scaled_height));
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1114,7 +1117,7 @@ void Sidebar::sys_color_changed()
 | 
			
		|||
 | 
			
		||||
    // btn...->msw_rescale() updates icon on button, so use it
 | 
			
		||||
    p->btn_send_gcode->msw_rescale();
 | 
			
		||||
    p->btn_remove_device->msw_rescale();
 | 
			
		||||
    p->btn_eject_device->msw_rescale();
 | 
			
		||||
    p->btn_export_gcode_removable->msw_rescale();
 | 
			
		||||
 | 
			
		||||
    p->scrolled->Layout();
 | 
			
		||||
| 
						 | 
				
			
			@ -1350,6 +1353,12 @@ void Sidebar::update_sliced_info_sizer()
 | 
			
		|||
                    new_label += format_wxstr("\n   - %1%", _L("normal mode"));
 | 
			
		||||
                    info_text += format_wxstr("\n%1%", ps.estimated_normal_print_time);
 | 
			
		||||
                    fill_labels(ps.estimated_normal_custom_gcode_print_times, new_label, info_text);
 | 
			
		||||
 | 
			
		||||
					// uncomment next line to not disappear slicing finished notif when colapsing sidebar before time estimate
 | 
			
		||||
					//if (p->plater->is_sidebar_collapsed())
 | 
			
		||||
					p->plater->get_notification_manager()->set_slicing_complete_large(p->plater->is_sidebar_collapsed());
 | 
			
		||||
					p->plater->get_notification_manager()->set_slicing_complete_print_time("Estimated printing time: " + ps.estimated_normal_print_time);
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
                if (ps.estimated_silent_print_time != "N/A") {
 | 
			
		||||
                    new_label += format_wxstr("\n   - %1%", _L("stealth mode"));
 | 
			
		||||
| 
						 | 
				
			
			@ -1385,15 +1394,16 @@ 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_remove_device->Enable(enable);
 | 
			
		||||
    p->btn_eject_device->Enable(enable);
 | 
			
		||||
	p->btn_export_gcode_removable->Enable(enable);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Sidebar::show_reslice(bool show)         const { return p->btn_reslice->Show(show); }
 | 
			
		||||
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_disconnect(bool show)      const { return p->btn_remove_device->Show(show); }
 | 
			
		||||
bool Sidebar::show_export_removable(bool show)const { return p->btn_export_gcode_removable->Show(show); }
 | 
			
		||||
bool Sidebar::show_reslice(bool show)          const { return p->btn_reslice->Show(show); }
 | 
			
		||||
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::is_multifilament()
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -1591,6 +1601,7 @@ struct Plater::priv
 | 
			
		|||
    GLToolbar view_toolbar;
 | 
			
		||||
    GLToolbar collapse_toolbar;
 | 
			
		||||
    Preview *preview;
 | 
			
		||||
	NotificationManager* notification_manager;
 | 
			
		||||
 | 
			
		||||
    BackgroundSlicingProcess    background_process;
 | 
			
		||||
    bool suppressed_backround_processing_update { false };
 | 
			
		||||
| 
						 | 
				
			
			@ -1775,7 +1786,17 @@ struct Plater::priv
 | 
			
		|||
    void on_slicing_update(SlicingStatusEvent&);
 | 
			
		||||
    void on_slicing_completed(wxCommandEvent&);
 | 
			
		||||
    void on_process_completed(wxCommandEvent&);
 | 
			
		||||
	void on_export_began(wxCommandEvent&);
 | 
			
		||||
    void on_layer_editing_toggled(bool enable);
 | 
			
		||||
	void on_slicing_began();
 | 
			
		||||
 | 
			
		||||
	void clear_warnings();
 | 
			
		||||
	void add_warning(const Slic3r::PrintStateBase::Warning &warning, size_t oid);
 | 
			
		||||
	void actualizate_warnings(const Model& model, size_t print_oid);
 | 
			
		||||
	// Displays dialog window with list of warnings. 
 | 
			
		||||
	// Returns true if user clicks OK.
 | 
			
		||||
	// Returns true if current_warnings vector is empty without showning the dialog
 | 
			
		||||
	bool warnings_dialog();
 | 
			
		||||
 | 
			
		||||
    void on_action_add(SimpleEvent&);
 | 
			
		||||
    void on_action_split_objects(SimpleEvent&);
 | 
			
		||||
| 
						 | 
				
			
			@ -1826,7 +1847,7 @@ struct Plater::priv
 | 
			
		|||
    // 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                        inside_snapshot_capture() { return m_prevent_snapshots != 0; }
 | 
			
		||||
 | 
			
		||||
	bool                        process_completed_with_error { false };
 | 
			
		||||
private:
 | 
			
		||||
    bool init_object_menu();
 | 
			
		||||
    bool init_common_menu(wxMenu* menu, const bool is_part = false);
 | 
			
		||||
| 
						 | 
				
			
			@ -1854,6 +1875,11 @@ private:
 | 
			
		|||
                                                              * */
 | 
			
		||||
    std::string 				m_last_fff_printer_profile_name;
 | 
			
		||||
    std::string 				m_last_sla_printer_profile_name;
 | 
			
		||||
 | 
			
		||||
	// vector of all warnings generated by last slicing
 | 
			
		||||
	std::vector<std::pair<Slic3r::PrintStateBase::Warning, size_t>> current_warnings;
 | 
			
		||||
	bool show_warning_dialog { false };
 | 
			
		||||
	
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const std::regex Plater::priv::pattern_bundle(".*[.](amf|amf[.]xml|zip[.]amf|3mf|prusa)", std::regex::icase);
 | 
			
		||||
| 
						 | 
				
			
			@ -1899,6 +1925,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
 | 
			
		|||
        });
 | 
			
		||||
    background_process.set_slicing_completed_event(EVT_SLICING_COMPLETED);
 | 
			
		||||
    background_process.set_finished_event(EVT_PROCESS_COMPLETED);
 | 
			
		||||
	background_process.set_export_began_event(EVT_EXPORT_BEGAN);
 | 
			
		||||
    // Default printer technology for default config.
 | 
			
		||||
    background_process.select_technology(this->printer_technology);
 | 
			
		||||
    // Register progress callback from the Print class to the Plater.
 | 
			
		||||
| 
						 | 
				
			
			@ -2010,8 +2037,9 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
 | 
			
		|||
    preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, [this](wxKeyEvent& evt) { preview->move_double_slider(evt); });
 | 
			
		||||
    preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_EDIT_COLOR_CHANGE, [this](wxKeyEvent& evt) { preview->edit_double_slider(evt); });
 | 
			
		||||
 | 
			
		||||
    q->Bind(EVT_SLICING_COMPLETED, &priv::on_slicing_completed, this);
 | 
			
		||||
	q->Bind(EVT_SLICING_COMPLETED, &priv::on_slicing_completed, this);
 | 
			
		||||
    q->Bind(EVT_PROCESS_COMPLETED, &priv::on_process_completed, this);
 | 
			
		||||
	q->Bind(EVT_EXPORT_BEGAN, &priv::on_export_began, this);
 | 
			
		||||
    q->Bind(EVT_GLVIEWTOOLBAR_3D, [q](SimpleEvent&) { q->select_view_3D("3D"); });
 | 
			
		||||
    q->Bind(EVT_GLVIEWTOOLBAR_PREVIEW, [q](SimpleEvent&) { q->select_view_3D("Preview"); });
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -2038,16 +2066,27 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
 | 
			
		|||
    });
 | 
			
		||||
#endif /* _WIN32 */
 | 
			
		||||
 | 
			
		||||
    this->q->Bind(EVT_REMOVABLE_DRIVE_EJECTED, [this](RemovableDriveEjectEvent &evt) {
 | 
			
		||||
	notification_manager = new NotificationManager(this->q);
 | 
			
		||||
	this->q->Bind(EVT_EJECT_DRIVE_NOTIFICAION_CLICKED, [this](EjectDriveNotificationClickedEvent&) { this->q->eject_drive(); });
 | 
			
		||||
	this->q->Bind(EVT_EXPORT_GCODE_NOTIFICAION_CLICKED, [this](ExportGcodeNotificationClickedEvent&) { this->q->export_gcode(true); });
 | 
			
		||||
	this->q->Bind(EVT_PRESET_UPDATE_AVIABLE_CLICKED, [this](PresetUpdateAviableClickedEvent&) {  wxGetApp().get_preset_updater()->on_update_notification_confirm(); });
 | 
			
		||||
 | 
			
		||||
	this->q->Bind(EVT_REMOVABLE_DRIVE_EJECTED, [this, q](RemovableDriveEjectEvent &evt) {
 | 
			
		||||
		if (evt.data.second) {
 | 
			
		||||
			this->show_action_buttons(this->ready_to_slice);
 | 
			
		||||
			Slic3r::GUI::show_info(this->q, format_wxstr(_L("Unmounting successful. The device %s(%s) can now be safely removed from the computer."),
 | 
			
		||||
				evt.data.first.name, evt.data.first.path));
 | 
			
		||||
		} else
 | 
			
		||||
			Slic3r::GUI::show_info(this->q, format_wxstr(_L("Ejecting of device %s(%s) has failed."),
 | 
			
		||||
				evt.data.first.name, evt.data.first.path));
 | 
			
		||||
			notification_manager->push_notification(format(_L("Unmounting successful. The device %s(%s) can now be safely removed from the computer."),evt.data.first.name, evt.data.first.path),
 | 
			
		||||
				                                    NotificationManager::NotificationLevel::RegularNotification, *q->get_current_canvas3D());
 | 
			
		||||
		} else {
 | 
			
		||||
			notification_manager->push_notification(format(_L("Ejecting of device %s(%s) has failed."), evt.data.first.name, evt.data.first.path),
 | 
			
		||||
				                                    NotificationManager::NotificationLevel::ErrorNotification, *q->get_current_canvas3D());
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
    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);
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
    this->q->Bind(EVT_REMOVABLE_DRIVES_CHANGED, [this](RemovableDrivesChangedEvent &) { this->show_action_buttons(this->ready_to_slice); });
 | 
			
		||||
    // Start the background thread and register this window as a target for update events.
 | 
			
		||||
    wxGetApp().removable_drive_manager()->init(this->q);
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
| 
						 | 
				
			
			@ -2675,6 +2714,8 @@ void Plater::priv::reset()
 | 
			
		|||
{
 | 
			
		||||
    Plater::TakeSnapshot snapshot(q, _L("Reset Project"));
 | 
			
		||||
 | 
			
		||||
	clear_warnings();
 | 
			
		||||
 | 
			
		||||
    set_project_filename(wxEmptyString);
 | 
			
		||||
 | 
			
		||||
    // Prevent toolpaths preview from rendering while we modify the Print object
 | 
			
		||||
| 
						 | 
				
			
			@ -2844,22 +2885,13 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool
 | 
			
		|||
		// The state of the Print changed, and it is non-zero. Let's validate it and give the user feedback on errors.
 | 
			
		||||
        std::string err = this->background_process.validate();
 | 
			
		||||
        if (err.empty()) {
 | 
			
		||||
			notification_manager->set_all_slicing_errors_gray(true);
 | 
			
		||||
            if (invalidated != Print::APPLY_STATUS_UNCHANGED && this->background_processing_enabled())
 | 
			
		||||
                return_state |= UPDATE_BACKGROUND_PROCESS_RESTART;
 | 
			
		||||
        } else {
 | 
			
		||||
            // The print is not valid.
 | 
			
		||||
            // Only show the error message immediately, if the top level parent of this window is active.
 | 
			
		||||
            auto p = dynamic_cast<wxWindow*>(this->q);
 | 
			
		||||
            while (p->GetParent())
 | 
			
		||||
                p = p->GetParent();
 | 
			
		||||
            auto *top_level_wnd = dynamic_cast<wxTopLevelWindow*>(p);
 | 
			
		||||
            if (! postpone_error_messages && top_level_wnd && top_level_wnd->IsActive()) {
 | 
			
		||||
                // The error returned from the Print needs to be translated into the local language.
 | 
			
		||||
                GUI::show_error(this->q, err);
 | 
			
		||||
            } else {
 | 
			
		||||
                // Show the error message once the main window gets activated.
 | 
			
		||||
                this->delayed_error_message = err;
 | 
			
		||||
            }
 | 
			
		||||
			// The print is not valid.
 | 
			
		||||
			// Show error as notification.
 | 
			
		||||
			notification_manager->push_slicing_error_notification(err, *q->get_current_canvas3D());
 | 
			
		||||
            return_state |= UPDATE_BACKGROUND_PROCESS_INVALID;
 | 
			
		||||
        }
 | 
			
		||||
    } else if (! this->delayed_error_message.empty()) {
 | 
			
		||||
| 
						 | 
				
			
			@ -2867,6 +2899,14 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool
 | 
			
		|||
        return_state |= UPDATE_BACKGROUND_PROCESS_INVALID;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	//actualizate warnings
 | 
			
		||||
	if (invalidated != Print::APPLY_STATUS_UNCHANGED) {
 | 
			
		||||
		actualizate_warnings(this->q->model(), this->background_process.current_print()->id().id);
 | 
			
		||||
		notification_manager->set_all_slicing_warnings_gray(true);
 | 
			
		||||
		show_warning_dialog = false;
 | 
			
		||||
		process_completed_with_error = false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
    if (invalidated != Print::APPLY_STATUS_UNCHANGED && was_running && ! this->background_process.running() &&
 | 
			
		||||
        (return_state & UPDATE_BACKGROUND_PROCESS_RESTART) == 0) {
 | 
			
		||||
        // The background processing was killed and it will not be restarted.
 | 
			
		||||
| 
						 | 
				
			
			@ -2929,6 +2969,8 @@ bool Plater::priv::restart_background_process(unsigned int state)
 | 
			
		|||
                this->statusbar()->set_status_text(_L("Cancelling"));
 | 
			
		||||
                this->background_process.stop();
 | 
			
		||||
            });
 | 
			
		||||
			if (!show_warning_dialog)
 | 
			
		||||
				on_slicing_began();
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -2955,6 +2997,7 @@ void Plater::priv::export_gcode(fs::path output_path, bool output_path_on_remova
 | 
			
		|||
    if ((state & priv::UPDATE_BACKGROUND_PROCESS_INVALID) != 0)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
	show_warning_dialog = true;
 | 
			
		||||
    if (! output_path.empty()) {
 | 
			
		||||
        background_process.schedule_export(output_path.string(), output_path_on_removable_media);
 | 
			
		||||
    } else {
 | 
			
		||||
| 
						 | 
				
			
			@ -3433,11 +3476,20 @@ void Plater::priv::on_slicing_update(SlicingStatusEvent &evt)
 | 
			
		|||
                state = print_object->step_state_with_warnings(static_cast<SLAPrintObjectStep>(warning_step));
 | 
			
		||||
        }
 | 
			
		||||
        // Now process state.warnings.
 | 
			
		||||
		for (auto const& warning : state.warnings) {
 | 
			
		||||
			if (warning.current) {
 | 
			
		||||
				notification_manager->push_slicing_warning_notification(warning.message, false, *q->get_current_canvas3D(), object_id.id, warning_step);
 | 
			
		||||
				add_warning(warning, object_id.id);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Plater::priv::on_slicing_completed(wxCommandEvent &)
 | 
			
		||||
void Plater::priv::on_slicing_completed(wxCommandEvent & evt)
 | 
			
		||||
{
 | 
			
		||||
	//notification_manager->push_notification(NotificationType::SlicingComplete, *q->get_current_canvas3D(), evt.GetInt());
 | 
			
		||||
	notification_manager->push_slicing_complete_notification(*q->get_current_canvas3D(), evt.GetInt(), is_sidebar_collapsed());
 | 
			
		||||
 | 
			
		||||
    switch (this->printer_technology) {
 | 
			
		||||
    case ptFFF:
 | 
			
		||||
        this->update_fff_scene();
 | 
			
		||||
| 
						 | 
				
			
			@ -3450,8 +3502,63 @@ void Plater::priv::on_slicing_completed(wxCommandEvent &)
 | 
			
		|||
        break;
 | 
			
		||||
    default: break;
 | 
			
		||||
    }
 | 
			
		||||
} 
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
void Plater::priv::on_export_began(wxCommandEvent& evt)
 | 
			
		||||
{
 | 
			
		||||
	if (show_warning_dialog)
 | 
			
		||||
		warnings_dialog();
 | 
			
		||||
}
 | 
			
		||||
void Plater::priv::on_slicing_began()
 | 
			
		||||
{
 | 
			
		||||
	clear_warnings();
 | 
			
		||||
	notification_manager->close_notification_of_type(NotificationType::SlicingComplete);
 | 
			
		||||
}
 | 
			
		||||
void Plater::priv::add_warning(const Slic3r::PrintStateBase::Warning& warning, size_t oid)
 | 
			
		||||
{
 | 
			
		||||
	for (auto const& it : current_warnings) {
 | 
			
		||||
		if (warning.message_id == it.first.message_id) {
 | 
			
		||||
			if (warning.message_id != 0 || (warning.message_id == 0 && warning.message == it.first.message))
 | 
			
		||||
				return;
 | 
			
		||||
		} 
 | 
			
		||||
	}
 | 
			
		||||
	current_warnings.emplace_back(std::pair<Slic3r::PrintStateBase::Warning, size_t>(warning, oid));
 | 
			
		||||
}
 | 
			
		||||
void Plater::priv::actualizate_warnings(const Model& model, size_t print_oid)
 | 
			
		||||
{
 | 
			
		||||
	std::vector<size_t> living_oids;
 | 
			
		||||
	living_oids.push_back(model.id().id);
 | 
			
		||||
	living_oids.push_back(print_oid);
 | 
			
		||||
	for (auto it = model.objects.begin(); it != model.objects.end(); ++it) {
 | 
			
		||||
		living_oids.push_back((*it)->id().id);
 | 
			
		||||
	}
 | 
			
		||||
	notification_manager->compare_warning_oids(living_oids);
 | 
			
		||||
}
 | 
			
		||||
void Plater::priv::clear_warnings()
 | 
			
		||||
{
 | 
			
		||||
	notification_manager->close_slicing_errors_and_warnings();
 | 
			
		||||
	this->current_warnings.clear();
 | 
			
		||||
}
 | 
			
		||||
bool Plater::priv::warnings_dialog()
 | 
			
		||||
{
 | 
			
		||||
	if (current_warnings.empty())
 | 
			
		||||
		return true;
 | 
			
		||||
	std::string text = _u8L("There are active warnings concerning sliced models:\n");
 | 
			
		||||
	bool empt = true;
 | 
			
		||||
	for (auto const& it : current_warnings) {
 | 
			
		||||
		int next_n = it.first.message.find_first_of('\n', 0);
 | 
			
		||||
		text += "\n";
 | 
			
		||||
		if (next_n != std::string::npos)
 | 
			
		||||
			text += it.first.message.substr(0, next_n);
 | 
			
		||||
		else
 | 
			
		||||
			text += it.first.message;
 | 
			
		||||
	}
 | 
			
		||||
	//text += "\n\nDo you still wish to export?";
 | 
			
		||||
	wxMessageDialog msg_wingow(this->q, text, wxString(SLIC3R_APP_NAME " ") + _L("generated warnings"), wxOK);
 | 
			
		||||
	const auto res = msg_wingow.ShowModal();
 | 
			
		||||
	return res == wxID_OK;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
void Plater::priv::on_process_completed(wxCommandEvent &evt)
 | 
			
		||||
{
 | 
			
		||||
    // Stop the background task, wait until the thread goes into the "Idle" state.
 | 
			
		||||
| 
						 | 
				
			
			@ -3470,14 +3577,13 @@ void Plater::priv::on_process_completed(wxCommandEvent &evt)
 | 
			
		|||
    if (error) {
 | 
			
		||||
        wxString message = evt.GetString();
 | 
			
		||||
        if (message.IsEmpty())
 | 
			
		||||
            message = _L("Export failed");
 | 
			
		||||
        if (q->m_tracking_popup_menu)
 | 
			
		||||
        	// We don't want to pop-up a message box when tracking a pop-up menu.
 | 
			
		||||
        	// We postpone the error message instead.
 | 
			
		||||
            q->m_tracking_popup_menu_error_message = message;
 | 
			
		||||
        else
 | 
			
		||||
	        show_error(q, message);
 | 
			
		||||
            message = _L("Export failed.");
 | 
			
		||||
		notification_manager->push_slicing_error_notification(boost::nowide::narrow(message), *q->get_current_canvas3D());
 | 
			
		||||
        this->statusbar()->set_status_text(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 (canceled)
 | 
			
		||||
        this->statusbar()->set_status_text(_L("Cancelled"));
 | 
			
		||||
| 
						 | 
				
			
			@ -3503,18 +3609,21 @@ void Plater::priv::on_process_completed(wxCommandEvent &evt)
 | 
			
		|||
    default: break;
 | 
			
		||||
    }
 | 
			
		||||
	
 | 
			
		||||
 | 
			
		||||
    if (canceled) {
 | 
			
		||||
        if (wxGetApp().get_mode() == comSimple)
 | 
			
		||||
            sidebar->set_btn_label(ActionButtonType::abReslice, "Slice now");
 | 
			
		||||
        show_action_buttons(true);
 | 
			
		||||
    }
 | 
			
		||||
    else if (this->writing_to_removable_device || wxGetApp().get_mode() == comSimple)
 | 
			
		||||
    else if (wxGetApp().get_mode() == comSimple)
 | 
			
		||||
	{
 | 
			
		||||
		wxGetApp().removable_drive_manager()->set_exporting_finished(true);
 | 
			
		||||
		show_action_buttons(false);
 | 
			
		||||
	}
 | 
			
		||||
    this->writing_to_removable_device = false;
 | 
			
		||||
	else if (this->writing_to_removable_device)
 | 
			
		||||
	{
 | 
			
		||||
		show_action_buttons(false);
 | 
			
		||||
		notification_manager->push_notification(NotificationType::ExportToRemovableFinished, *q->get_current_canvas3D());
 | 
			
		||||
	}
 | 
			
		||||
	this->writing_to_removable_device = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Plater::priv::on_layer_editing_toggled(bool enable)
 | 
			
		||||
| 
						 | 
				
			
			@ -4156,7 +4265,7 @@ void Plater::priv::show_action_buttons(const bool ready_to_slice) const
 | 
			
		|||
			sidebar->show_export(true) |
 | 
			
		||||
			sidebar->show_send(send_gcode_shown) |
 | 
			
		||||
			sidebar->show_export_removable(removable_media_status.has_removable_drives) |
 | 
			
		||||
			sidebar->show_disconnect(removable_media_status.has_eject))
 | 
			
		||||
			sidebar->show_eject(removable_media_status.has_eject))
 | 
			
		||||
            sidebar->Layout();
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
| 
						 | 
				
			
			@ -4168,7 +4277,7 @@ void Plater::priv::show_action_buttons(const bool ready_to_slice) const
 | 
			
		|||
            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_disconnect(!ready_to_slice && removable_media_status.has_eject))
 | 
			
		||||
            sidebar->show_eject(!ready_to_slice && removable_media_status.has_eject))
 | 
			
		||||
            sidebar->Layout();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -4731,6 +4840,9 @@ void Plater::export_gcode(bool prefer_removable)
 | 
			
		|||
    if (p->model.objects.empty())
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
	if (p->process_completed_with_error)//here
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
    // If possible, remove accents from accented latin characters.
 | 
			
		||||
    // This function is useful for generating file names to be processed by legacy firmwares.
 | 
			
		||||
    fs::path default_output_file;
 | 
			
		||||
| 
						 | 
				
			
			@ -4990,7 +5102,6 @@ void Plater::export_toolpaths_to_obj() const
 | 
			
		|||
    p->preview->get_canvas3d()->export_toolpaths_to_obj(into_u8(path).c_str());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void Plater::reslice()
 | 
			
		||||
{
 | 
			
		||||
    // Stop arrange and (or) optimize rotation tasks.
 | 
			
		||||
| 
						 | 
				
			
			@ -5676,6 +5787,16 @@ Mouse3DController& Plater::get_mouse3d_controller()
 | 
			
		|||
    return p->mouse3d_controller;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const NotificationManager* Plater::get_notification_manager() const
 | 
			
		||||
{
 | 
			
		||||
	return p->notification_manager;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
NotificationManager* Plater::get_notification_manager()
 | 
			
		||||
{
 | 
			
		||||
	return p->notification_manager;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Plater::can_delete() const { return p->can_delete(); }
 | 
			
		||||
bool Plater::can_delete_all() const { return p->can_delete_all(); }
 | 
			
		||||
bool Plater::can_increase_instances() const { return p->can_increase_instances(); }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -47,6 +47,7 @@ class ObjectLayers;
 | 
			
		|||
class ObjectList;
 | 
			
		||||
class GLCanvas3D;
 | 
			
		||||
class Mouse3DController;
 | 
			
		||||
class NotificationManager;
 | 
			
		||||
struct Camera;
 | 
			
		||||
class Bed3D;
 | 
			
		||||
class GLToolbar;
 | 
			
		||||
| 
						 | 
				
			
			@ -130,8 +131,9 @@ public:
 | 
			
		|||
    bool                    show_reslice(bool show) const;
 | 
			
		||||
	bool                    show_export(bool show) const;
 | 
			
		||||
	bool                    show_send(bool show) const;
 | 
			
		||||
    bool                    show_disconnect(bool show)const;
 | 
			
		||||
    bool                    show_eject(bool show)const;
 | 
			
		||||
	bool                    show_export_removable(bool show) const;
 | 
			
		||||
	bool                    get_eject_shown() const;
 | 
			
		||||
    bool                    is_multifilament();
 | 
			
		||||
    void                    update_mode();
 | 
			
		||||
    bool                    is_collapsed();
 | 
			
		||||
| 
						 | 
				
			
			@ -338,6 +340,9 @@ public:
 | 
			
		|||
    Mouse3DController& get_mouse3d_controller();
 | 
			
		||||
 | 
			
		||||
	void set_bed_shape() const;
 | 
			
		||||
    
 | 
			
		||||
	const NotificationManager* get_notification_manager() const;
 | 
			
		||||
	NotificationManager* get_notification_manager();
 | 
			
		||||
 | 
			
		||||
    // ROII wrapper for suppressing the Undo / Redo snapshot to be taken.
 | 
			
		||||
	class SuppressSnapshots
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -27,6 +27,7 @@
 | 
			
		|||
#include "slic3r/GUI/GUI_App.hpp"
 | 
			
		||||
#include "slic3r/GUI/Plater.hpp"
 | 
			
		||||
#include "slic3r/GUI/format.hpp"
 | 
			
		||||
#include "slic3r/GUI/NotificationManager.hpp"
 | 
			
		||||
#include "slic3r/Utils/Http.hpp"
 | 
			
		||||
#include "slic3r/Config/Version.hpp"
 | 
			
		||||
#include "slic3r/Config/Snapshot.hpp"
 | 
			
		||||
| 
						 | 
				
			
			@ -154,6 +155,9 @@ struct PresetUpdater::priv
 | 
			
		|||
	bool cancel;
 | 
			
		||||
	std::thread thread;
 | 
			
		||||
 | 
			
		||||
	bool has_waiting_updates { false };
 | 
			
		||||
	Updates waiting_updates;
 | 
			
		||||
 | 
			
		||||
	priv();
 | 
			
		||||
 | 
			
		||||
	void set_download_prefs(AppConfig *app_config);
 | 
			
		||||
| 
						 | 
				
			
			@ -165,6 +169,7 @@ struct PresetUpdater::priv
 | 
			
		|||
	void check_install_indices() const;
 | 
			
		||||
	Updates get_config_updates(const Semver& old_slic3r_version) const;
 | 
			
		||||
	void perform_updates(Updates &&updates, bool snapshot = true) const;
 | 
			
		||||
	void set_waiting_updates(Updates u);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
PresetUpdater::priv::priv()
 | 
			
		||||
| 
						 | 
				
			
			@ -326,7 +331,15 @@ void PresetUpdater::priv::sync_config(const VendorMap vendors)
 | 
			
		|||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			Slic3r::rename_file(idx_path_temp, idx_path);
 | 
			
		||||
			index = std::move(new_index);
 | 
			
		||||
			//if we rename path we need to change it in Index object too or create the object again
 | 
			
		||||
			//index = std::move(new_index);
 | 
			
		||||
			try {
 | 
			
		||||
				index.load(idx_path);
 | 
			
		||||
			}
 | 
			
		||||
			catch (const std::exception& /* err */) {
 | 
			
		||||
				BOOST_LOG_TRIVIAL(error) << format("Could not load downloaded index %1% for vendor %2%: invalid index?", idx_path, vendor.name);
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			if (cancel)
 | 
			
		||||
				return;
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -632,6 +645,12 @@ void PresetUpdater::priv::perform_updates(Updates &&updates, bool snapshot) cons
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void PresetUpdater::priv::set_waiting_updates(Updates u)
 | 
			
		||||
{
 | 
			
		||||
	waiting_updates = u;
 | 
			
		||||
	has_waiting_updates = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PresetUpdater::PresetUpdater() :
 | 
			
		||||
	p(new priv())
 | 
			
		||||
{}
 | 
			
		||||
| 
						 | 
				
			
			@ -690,9 +709,9 @@ void PresetUpdater::slic3r_update_notify()
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver &old_slic3r_version) const
 | 
			
		||||
PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver& old_slic3r_version, bool no_notification) const
 | 
			
		||||
{
 | 
			
		||||
	if (! p->enabled_config_update) { return R_NOOP; }
 | 
			
		||||
 	if (! p->enabled_config_update) { return R_NOOP; }
 | 
			
		||||
 | 
			
		||||
	auto updates = p->get_config_updates(old_slic3r_version);
 | 
			
		||||
	if (updates.incompats.size() > 0) {
 | 
			
		||||
| 
						 | 
				
			
			@ -779,30 +798,38 @@ PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver &old_slic3
 | 
			
		|||
		}
 | 
			
		||||
 | 
			
		||||
		// regular update
 | 
			
		||||
		BOOST_LOG_TRIVIAL(info) << format("Update of %1% bundles available. Asking for confirmation ...", updates.updates.size());
 | 
			
		||||
		if (no_notification) {
 | 
			
		||||
			BOOST_LOG_TRIVIAL(info) << format("Update of %1% bundles available. Asking for confirmation ...", p->waiting_updates.updates.size());
 | 
			
		||||
 | 
			
		||||
		std::vector<GUI::MsgUpdateConfig::Update> updates_msg;
 | 
			
		||||
		for (const auto &update : updates.updates) {
 | 
			
		||||
			std::string changelog_url = update.version.config_version.prerelease() == nullptr ? update.changelog_url : std::string();
 | 
			
		||||
			updates_msg.emplace_back(update.vendor, update.version.config_version, update.version.comment, std::move(changelog_url));
 | 
			
		||||
		}
 | 
			
		||||
			std::vector<GUI::MsgUpdateConfig::Update> updates_msg;
 | 
			
		||||
			for (const auto& update : updates.updates) {
 | 
			
		||||
				std::string changelog_url = update.version.config_version.prerelease() == nullptr ? update.changelog_url : std::string();
 | 
			
		||||
				updates_msg.emplace_back(update.vendor, update.version.config_version, update.version.comment, std::move(changelog_url));
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		GUI::MsgUpdateConfig dlg(updates_msg);
 | 
			
		||||
			GUI::MsgUpdateConfig dlg(updates_msg);
 | 
			
		||||
 | 
			
		||||
		const auto res = dlg.ShowModal();
 | 
			
		||||
		if (res == wxID_OK) {
 | 
			
		||||
			BOOST_LOG_TRIVIAL(debug) << "User agreed to perform the update";
 | 
			
		||||
			p->perform_updates(std::move(updates));
 | 
			
		||||
			const auto res = dlg.ShowModal();
 | 
			
		||||
			if (res == wxID_OK) {
 | 
			
		||||
				BOOST_LOG_TRIVIAL(debug) << "User agreed to perform the update";
 | 
			
		||||
				p->perform_updates(std::move(updates));
 | 
			
		||||
 | 
			
		||||
			// Reload global configuration
 | 
			
		||||
			auto *app_config = GUI::wxGetApp().app_config;
 | 
			
		||||
			GUI::wxGetApp().preset_bundle->load_presets(*app_config);
 | 
			
		||||
			GUI::wxGetApp().load_current_presets();
 | 
			
		||||
			return R_UPDATE_INSTALLED;
 | 
			
		||||
				// Reload global configuration
 | 
			
		||||
				auto* app_config = GUI::wxGetApp().app_config;
 | 
			
		||||
				GUI::wxGetApp().preset_bundle->load_presets(*app_config);
 | 
			
		||||
				GUI::wxGetApp().load_current_presets();
 | 
			
		||||
				return R_UPDATE_INSTALLED;
 | 
			
		||||
			}
 | 
			
		||||
			else {
 | 
			
		||||
				BOOST_LOG_TRIVIAL(info) << "User refused the update";
 | 
			
		||||
				return R_UPDATE_REJECT;
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			BOOST_LOG_TRIVIAL(info) << "User refused the update";
 | 
			
		||||
			return R_UPDATE_REJECT;
 | 
			
		||||
			p->set_waiting_updates(updates);
 | 
			
		||||
			GUI::wxGetApp().plater()->get_notification_manager()->push_notification(GUI::NotificationType::PresetUpdateAviable, *(GUI::wxGetApp().plater()->get_current_canvas3D()));
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		// MsgUpdateConfig will show after the notificaation is clicked
 | 
			
		||||
	} else {
 | 
			
		||||
		BOOST_LOG_TRIVIAL(info) << "No configuration updates available.";
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -825,5 +852,37 @@ void PresetUpdater::install_bundles_rsrc(std::vector<std::string> bundles, bool
 | 
			
		|||
	p->perform_updates(std::move(updates), snapshot);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void PresetUpdater::on_update_notification_confirm()
 | 
			
		||||
{
 | 
			
		||||
	if (!p->has_waiting_updates)
 | 
			
		||||
		return;
 | 
			
		||||
	BOOST_LOG_TRIVIAL(info) << format("Update of %1% bundles available. Asking for confirmation ...", p->waiting_updates.updates.size());
 | 
			
		||||
 | 
			
		||||
	std::vector<GUI::MsgUpdateConfig::Update> updates_msg;
 | 
			
		||||
	for (const auto& update : p->waiting_updates.updates) {
 | 
			
		||||
		std::string changelog_url = update.version.config_version.prerelease() == nullptr ? update.changelog_url : std::string();
 | 
			
		||||
		updates_msg.emplace_back(update.vendor, update.version.config_version, update.version.comment, std::move(changelog_url));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	GUI::MsgUpdateConfig dlg(updates_msg);
 | 
			
		||||
 | 
			
		||||
	const auto res = dlg.ShowModal();
 | 
			
		||||
	if (res == wxID_OK) {
 | 
			
		||||
		BOOST_LOG_TRIVIAL(debug) << "User agreed to perform the update";
 | 
			
		||||
		p->perform_updates(std::move(p->waiting_updates));
 | 
			
		||||
 | 
			
		||||
		// Reload global configuration
 | 
			
		||||
		auto* app_config = GUI::wxGetApp().app_config;
 | 
			
		||||
		GUI::wxGetApp().preset_bundle->load_presets(*app_config);
 | 
			
		||||
		GUI::wxGetApp().load_current_presets();
 | 
			
		||||
		p->has_waiting_updates = false;
 | 
			
		||||
		//return R_UPDATE_INSTALLED;
 | 
			
		||||
	}
 | 
			
		||||
	else {
 | 
			
		||||
		BOOST_LOG_TRIVIAL(info) << "User refused the update";
 | 
			
		||||
		//return R_UPDATE_REJECT;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -35,16 +35,20 @@ public:
 | 
			
		|||
		R_INCOMPAT_CONFIGURED,
 | 
			
		||||
		R_UPDATE_INSTALLED,
 | 
			
		||||
		R_UPDATE_REJECT,
 | 
			
		||||
		R_UPDATE_NOTIFICATION
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	// If updating is enabled, check if updates are available in cache, if so, ask about installation.
 | 
			
		||||
	// A false return value implies Slic3r should exit due to incompatibility of configuration.
 | 
			
		||||
	// Providing old slic3r version upgrade profiles on upgrade of an application even in case
 | 
			
		||||
	// that the config index installed from the Internet is equal to the index contained in the installation package.
 | 
			
		||||
	UpdateResult config_update(const Semver &old_slic3r_version) const;
 | 
			
		||||
	// no_notification = force modal textbox, otherwise some cases only shows notification
 | 
			
		||||
	UpdateResult config_update(const Semver &old_slic3r_version, bool no_notification) const;
 | 
			
		||||
 | 
			
		||||
	// "Update" a list of bundles from resources (behaves like an online update).
 | 
			
		||||
	void install_bundles_rsrc(std::vector<std::string> bundles, bool snapshot = true) const;
 | 
			
		||||
 | 
			
		||||
	void on_update_notification_confirm();
 | 
			
		||||
private:
 | 
			
		||||
	struct priv;
 | 
			
		||||
	std::unique_ptr<priv> p;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue