Merge branch 'dk_dwnldr_merge'
This commit is contained in:
commit
c9ebaf8986
39 changed files with 9670 additions and 6577 deletions
63
resources/icons/notification_open.svg
Normal file
63
resources/icons/notification_open.svg
Normal file
|
@ -0,0 +1,63 @@
|
|||
<?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"
|
||||
version="1.0"
|
||||
id="close_window"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 100 100"
|
||||
enable-background="new 0 0 100 100"
|
||||
xml:space="preserve"
|
||||
sodipodi:docname="notification_open.svg"
|
||||
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"><metadata
|
||||
id="metadata19"><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="defs17" /><sodipodi:namedview
|
||||
inkscape:document-rotation="0"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1369"
|
||||
id="namedview15"
|
||||
showgrid="false"
|
||||
inkscape:zoom="10.08"
|
||||
inkscape:cx="50"
|
||||
inkscape:cy="50"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="close_window" />
|
||||
<g
|
||||
id="g4">
|
||||
<path
|
||||
fill="#ED6B21"
|
||||
d="M80,92.83H20c-7.08,0-12.83-5.76-12.83-12.84V20c0-7.08,5.76-12.83,12.83-12.83h60 c7.08,0,12.84,5.76,12.84,12.83v60C92.83,87.08,87.08,92.83,80,92.83z M20,12.83c-3.95,0-7.17,3.21-7.17,7.17v60 c0,3.95,3.21,7.17,7.17,7.17h60c3.95,0,7.17-3.21,7.17-7.17V20c0-3.95-3.21-7.17-7.17-7.17H20z"
|
||||
id="path2" />
|
||||
</g>
|
||||
|
||||
|
||||
<g
|
||||
id="open"
|
||||
transform="matrix(4.05,0,0,4.05,15.4645,15.277381)"
|
||||
style="fill:#ed6b21;fill-opacity:1"><path
|
||||
id="path2-7"
|
||||
d="M 1.22,14 V 3 c 0,0 0,-1 1,-1 1,0 4,0 5,0 1,0 1,2 2,2 1,0 4,0 4,0 0,0 1,0 1,1 v 2 h -1 c 0,0 0,0 0,-1 0,-1 -1,-1 -1,-1 h -3.5 c -1,0 -1,-2 -2,-2 -1,0 -3.5,0 -3.5,0 -1,0 -1,1 -1,1 v 9 1 h 1 v 1 c 0,0 0,0 -1,0 -1,0 -1,-1 -1,-1 z"
|
||||
fill="#808080"
|
||||
style="fill:#ed6b21;fill-opacity:1" /><path
|
||||
id="path4"
|
||||
d="M 5,6 C 4.45,6 3.86,6.43 3.68,6.95 l -2.37,7.1 C 1.14,14.57 1.45,15 2,15 h 10 c 0.55,0 1.14,-0.43 1.32,-0.95 l 2.37,-7.1 C 15.86,6.43 15.55,6 15,6 Z"
|
||||
fill="#ed6b21"
|
||||
style="fill:#ed6b21;fill-opacity:1" /></g></svg>
|
After Width: | Height: | Size: 2.4 KiB |
64
resources/icons/notification_open_hover.svg
Normal file
64
resources/icons/notification_open_hover.svg
Normal file
|
@ -0,0 +1,64 @@
|
|||
<?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"
|
||||
version="1.0"
|
||||
id="close_window"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 100 100"
|
||||
enable-background="new 0 0 100 100"
|
||||
xml:space="preserve"
|
||||
sodipodi:docname="notification_open_hover.svg"
|
||||
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"><metadata
|
||||
id="metadata19"><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="defs17" /><sodipodi:namedview
|
||||
inkscape:document-rotation="0"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1369"
|
||||
id="namedview15"
|
||||
showgrid="false"
|
||||
inkscape:zoom="10.08"
|
||||
inkscape:cx="50"
|
||||
inkscape:cy="50"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="close_window" />
|
||||
<g
|
||||
transform="matrix(1.15,0,0,1.15,-7.50075,-7.5)"
|
||||
id="g4">
|
||||
<path
|
||||
fill="#ed6b21"
|
||||
d="M 80,92.83 H 20 C 12.92,92.83 7.17,87.07 7.17,79.99 V 20 C 7.17,12.92 12.93,7.17 20,7.17 h 60 c 7.08,0 12.84,5.76 12.84,12.83 V 80 C 92.83,87.08 87.08,92.83 80,92.83 Z m -60,-80 c -3.95,0 -7.17,3.21 -7.17,7.17 v 60 c 0,3.95 3.21,7.17 7.17,7.17 h 60 c 3.95,0 7.17,-3.21 7.17,-7.17 V 20 c 0,-3.95 -3.21,-7.17 -7.17,-7.17 z"
|
||||
id="path2" />
|
||||
</g>
|
||||
|
||||
|
||||
<g
|
||||
id="open"
|
||||
transform="matrix(4.6575,0,0,4.6575,10.313937,10.113631)"
|
||||
style="fill:#ed6b21;fill-opacity:1"><path
|
||||
id="path2-7"
|
||||
d="M 1.22,14 V 3 c 0,0 0,-1 1,-1 1,0 4,0 5,0 1,0 1,2 2,2 1,0 4,0 4,0 0,0 1,0 1,1 v 2 h -1 c 0,0 0,0 0,-1 0,-1 -1,-1 -1,-1 h -3.5 c -1,0 -1,-2 -2,-2 -1,0 -3.5,0 -3.5,0 -1,0 -1,1 -1,1 v 9 1 h 1 v 1 c 0,0 0,0 -1,0 -1,0 -1,-1 -1,-1 z"
|
||||
fill="#808080"
|
||||
style="fill:#ed6b21;fill-opacity:1" /><path
|
||||
id="path4"
|
||||
d="M 5,6 C 4.45,6 3.86,6.43 3.68,6.95 l -2.37,7.1 C 1.14,14.57 1.45,15 2,15 h 10 c 0.55,0 1.14,-0.43 1.32,-0.95 l 2.37,-7.1 C 15.86,6.43 15.55,6 15,6 Z"
|
||||
fill="#ed6b21"
|
||||
style="fill:#ed6b21;fill-opacity:1" /></g></svg>
|
After Width: | Height: | Size: 2.5 KiB |
75
resources/icons/notification_pause.svg
Normal file
75
resources/icons/notification_pause.svg
Normal file
|
@ -0,0 +1,75 @@
|
|||
<?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="notification_pause.svg"
|
||||
xml:space="preserve"
|
||||
style="enable-background:new 0 0 800 800;"
|
||||
viewBox="0 0 800 800"
|
||||
y="0px"
|
||||
x="0px"
|
||||
id="Layer_1"
|
||||
version="1.1"><metadata
|
||||
id="metadata15"><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="defs13" /><sodipodi:namedview
|
||||
inkscape:current-layer="Layer_1"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:cy="459.92063"
|
||||
inkscape:cx="400"
|
||||
inkscape:zoom="1.26"
|
||||
showgrid="false"
|
||||
id="namedview11"
|
||||
inkscape:window-height="1369"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0"
|
||||
guidetolerance="10"
|
||||
gridtolerance="10"
|
||||
objecttolerance="10"
|
||||
borderopacity="1"
|
||||
bordercolor="#666666"
|
||||
pagecolor="#ffffff"
|
||||
inkscape:document-rotation="0" />
|
||||
<style
|
||||
id="style2"
|
||||
type="text/css">
|
||||
.st0{fill:#ED6B21;}
|
||||
</style>
|
||||
<path
|
||||
id="path4"
|
||||
d="m 317.95173,562.36128 h -60.775 v -330.565 h 60.775 v 330.565 m 48.195,27.88 v -386.24 c 0,-11.22 -9.095,-20.315 -20.315,-20.315 h -116.535 c -11.22,0 -20.315,9.095 -20.315,20.315 v 386.24 c 0,11.22 9.095,20.315 20.315,20.315 h 116.45 c 11.22,0 20.4,-9.095 20.4,-20.315 z"
|
||||
class="st0"
|
||||
style="stroke-width:0.85" />
|
||||
|
||||
<g
|
||||
transform="matrix(0.9775,0,0,0.9775,53.547,53.54775)"
|
||||
id="g4">
|
||||
<path
|
||||
d="M 597.2,701.3 H 110.6 C 53.2,701.3 6.5,654.6 6.5,597.2 V 110.6 C 6.5,53.2 53.2,6.5 110.6,6.5 h 486.6 c 57.4,0 104.1,46.7 104.1,104.1 v 486.6 c 0,57.4 -46.7,104.1 -104.1,104.1 z M 110.6,52.4 c -32,0 -58.2,26 -58.2,58.2 v 486.6 c 0,32 26,58.2 58.2,58.2 h 486.6 c 32,0 58.2,-26 58.2,-58.2 V 110.6 c 0,-32 -26,-58.2 -58.2,-58.2 z"
|
||||
class="st0"
|
||||
id="path2" />
|
||||
</g>
|
||||
<path
|
||||
id="path17"
|
||||
d="m 150.65676,738.12999 c -12.4717,-1.39663 -26.66772,-5.94192 -37.84321,-12.11671 -17.754551,-9.80992 -33.768844,-26.68981 -42.418124,-44.71089 -5.985061,-12.4701 -8.760227,-23.35456 -9.821918,-38.52249 -0.48061,-6.8663 -0.640464,-87.42616 -0.497289,-250.61508 0.195544,-222.88027 0.294923,-240.94223 1.356742,-246.58759 4.2349,-22.51562 13.68014,-40.62012 29.200931,-55.97194 14.237938,-14.082924 31.958648,-23.427941 52.602238,-27.739791 5.87892,-1.227937 14.00696,-1.268146 256.3492,-1.268146 h 250.27778 l 7.08334,1.561512 c 21.30688,4.697075 36.90336,13.216072 51.96052,28.381502 14.67865,14.784203 23.1932,30.350373 27.76125,50.752683 l 1.56791,7.00271 v 250.95239 c 0,242.72256 -0.0418,251.15149 -1.26428,257.0238 -9.30592,44.69034 -45.18963,77.43352 -89.75566,81.90028 -9.17898,0.92002 -488.33076,0.87927 -496.55943,-0.0425 z M 652.87275,692.49 c 19.93824,-6.17834 34.6922,-21.42493 40.00111,-41.33675 l 1.51306,-5.67494 V 399.58544 153.69259 l -1.52571,-5.73412 c -5.66288,-21.28292 -21.4158,-36.89778 -42.2051,-41.83523 -5.63965,-1.33941 -7.66026,-1.3488 -253.17948,-1.17613 l -247.49447,0.17405 -4.72222,1.5953 c -18.05932,6.10093 -31.7315,19.23923 -37.4918,36.0278 -1.04762,3.05333 -2.22128,7.52472 -2.60813,9.93642 -0.47859,2.9836 -0.705,81.91876 -0.70847,246.99889 -0.005,218.14117 0.10226,243.1829 1.05916,248.25397 4.27172,22.63802 22.24346,40.86392 44.80877,45.4425 3.58848,0.72811 49.16893,0.87009 250.95237,0.78171 l 246.56747,-0.10801 z"
|
||||
style="fill:#ed6b21;fill-opacity:1;stroke-width:0.674603" /><path
|
||||
id="path21"
|
||||
d="m 210.06357,199.0892 c 1.10499,-4.08272 3.30912,-7.53117 6.63124,-10.37477 5.42019,-4.63948 2.10678,-4.43387 71.33297,-4.42657 l 62.44927,0.007 3.44194,1.60268 c 4.05635,1.88877 7.75734,5.3977 9.8769,9.36436 l 1.52243,2.84915 v 199.00794 199.00793 l -1.52243,2.84915 c -2.11956,3.96665 -5.82055,7.47559 -9.8769,9.36436 l -3.44194,1.60267 -62.44927,0.007 c -69.78764,0.008 -65.98231,0.26152 -71.72146,-4.79028 -1.69319,-1.4904 -3.87229,-4.37627 -4.9283,-6.52672 l -1.89304,-3.85513 -0.12602,-196.30953 c -0.0987,-153.67069 0.0544,-196.97613 0.70461,-199.37859 z m 77.67539,363.64446 H 318.4334 V 397.11858 231.5035 H 287.73896 257.0445 v 165.61508 165.61508 z"
|
||||
style="fill:#ed6b21;fill-opacity:1;stroke-width:0.674603" /><path
|
||||
style="fill:#ed6b21;stroke-width:0.85"
|
||||
class="st0"
|
||||
d="m 533.78507,563.71023 h -60.775 v -330.565 h 60.775 v 330.565 m 48.195,27.88 v -386.24 c 0,-11.22 -9.095,-20.315 -20.315,-20.315 h -116.535 c -11.22,0 -20.315,9.095 -20.315,20.315 v 386.24 c 0,11.22 9.095,20.315 20.315,20.315 h 116.45 c 11.22,0 20.4,-9.095 20.4,-20.315 z"
|
||||
id="path4-3" /><path
|
||||
style="fill:#ed6b21;fill-opacity:1;stroke-width:0.674603"
|
||||
d="m 425.89691,200.43815 c 1.10499,-4.08272 3.30912,-7.53117 6.63124,-10.37477 5.42019,-4.63948 2.10678,-4.43387 71.33297,-4.42657 l 62.44927,0.007 3.44194,1.60268 c 4.05635,1.88877 7.75734,5.3977 9.8769,9.36436 l 1.52243,2.84915 v 199.00794 199.00793 l -1.52243,2.84915 c -2.11956,3.96665 -5.82055,7.47559 -9.8769,9.36436 l -3.44194,1.60267 -62.44927,0.007 c -69.78764,0.008 -65.98231,0.26152 -71.72146,-4.79028 -1.69319,-1.4904 -3.87229,-4.37627 -4.9283,-6.52672 l -1.89304,-3.85513 -0.12602,-196.30953 c -0.0987,-153.67069 0.0544,-196.97613 0.70461,-199.37859 z m 77.67539,363.64446 h 30.69444 V 398.46753 232.85245 H 503.5723 472.87784 v 165.61508 165.61508 z"
|
||||
id="path21-9" /></svg>
|
After Width: | Height: | Size: 5.6 KiB |
75
resources/icons/notification_pause_hover.svg
Normal file
75
resources/icons/notification_pause_hover.svg
Normal file
|
@ -0,0 +1,75 @@
|
|||
<?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"
|
||||
version="1.1"
|
||||
id="Layer_1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 800 800"
|
||||
style="enable-background:new 0 0 800 800;"
|
||||
xml:space="preserve"
|
||||
sodipodi:docname="notification_pause_hover.svg"
|
||||
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"><metadata
|
||||
id="metadata15"><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="defs13" /><sodipodi:namedview
|
||||
inkscape:document-rotation="0"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1369"
|
||||
id="namedview11"
|
||||
showgrid="false"
|
||||
inkscape:zoom="1.26"
|
||||
inkscape:cx="400"
|
||||
inkscape:cy="459.92063"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="Layer_1" />
|
||||
<style
|
||||
type="text/css"
|
||||
id="style2">
|
||||
.st0{fill:#ED6B21;}
|
||||
</style>
|
||||
<path
|
||||
style="stroke-width:0.9775"
|
||||
class="st0"
|
||||
d="M 322.50986,587.14728 H 252.61861 V 206.99753 h 69.89125 v 380.14975 m 55.42424,32.062 v -444.176 c 0,-12.903 -10.45925,-23.36225 -23.36225,-23.36225 H 220.55661 c -12.903,0 -23.36225,10.45925 -23.36225,23.36225 v 444.176 c 0,12.903 10.45925,23.36225 23.36225,23.36225 H 354.4741 c 12.90301,0 23.46,-10.45925 23.46,-23.36225 z"
|
||||
id="path4" />
|
||||
|
||||
<g
|
||||
id="g4"
|
||||
transform="matrix(1.124125,0,0,1.124125,1.6564125,1.6571625)">
|
||||
<path
|
||||
id="path2"
|
||||
class="st0"
|
||||
d="M 597.2,701.3 H 110.6 C 53.2,701.3 6.5,654.6 6.5,597.2 V 110.6 C 6.5,53.2 53.2,6.5 110.6,6.5 h 486.6 c 57.4,0 104.1,46.7 104.1,104.1 v 486.6 c 0,57.4 -46.7,104.1 -104.1,104.1 z M 110.6,52.4 c -32,0 -58.2,26 -58.2,58.2 v 486.6 c 0,32 26,58.2 58.2,58.2 h 486.6 c 32,0 58.2,-26 58.2,-58.2 V 110.6 c 0,-32 -26,-58.2 -58.2,-58.2 z" />
|
||||
</g>
|
||||
<path
|
||||
style="fill:#ed6b21;fill-opacity:1;stroke-width:0.775793"
|
||||
d="M 113.38564,788.89154 C 99.043181,787.28541 82.717758,782.05833 69.865944,774.95732 49.44821,763.67591 31.031774,744.26404 21.085102,723.5398 14.202281,709.19918 11.01084,696.68205 9.7898958,679.23893 9.2371943,671.34269 9.0533622,578.69885 9.2180135,391.03159 9.4428891,134.71928 9.5571749,113.94803 10.778267,107.45586 15.648402,81.5629 26.510428,60.742725 44.359337,43.088132 60.732966,26.892769 81.111783,16.146 104.85191,11.187372 111.61267,9.7752445 120.95992,9.7290042 399.65349,9.7290042 h 287.81945 l 8.14584,1.7957388 c 24.50291,5.401636 42.43886,15.198483 59.7546,32.638727 16.88044,17.001834 26.67218,34.902929 31.92543,58.36559 l 1.8031,8.05311 v 288.59525 c 0,279.13094 -0.0481,288.82421 -1.45392,295.57737 -10.70181,51.39389 -51.96808,89.04855 -103.21901,94.18532 -10.55583,1.05803 -561.58037,1.01116 -571.04334,-0.0489 z m 577.54838,-52.48599 c 22.92898,-7.10509 39.89603,-24.63867 46.00128,-47.53726 l 1.74002,-6.52619 V 399.5653 116.78853 l -1.75457,-6.59424 c -6.51231,-24.475359 -24.62817,-42.432448 -48.53586,-48.110516 -6.4856,-1.540321 -8.8093,-1.55112 -291.1564,-1.352549 l -284.61864,0.200157 -5.43056,1.834595 c -20.768215,7.01607 -36.491222,22.125115 -43.115567,41.431973 -1.204763,3.51133 -2.554472,8.65343 -2.99935,11.42688 -0.550378,3.43114 -0.81075,94.20657 -0.81474,284.04872 -0.0058,250.86235 0.117599,279.66034 1.218034,285.49207 4.912478,26.03372 25.579979,46.99351 51.530083,52.25887 4.12675,0.83733 56.54427,1.00061 288.59523,0.89897 l 283.55259,-0.12421 z"
|
||||
id="path17" /><path
|
||||
style="fill:#ed6b21;fill-opacity:1;stroke-width:0.775793"
|
||||
d="m 198.47436,169.38465 c 1.27074,-4.69513 3.80549,-8.66084 7.62593,-11.93098 6.23322,-5.33541 2.4228,-5.09896 82.03292,-5.09056 l 71.81666,0.008 3.95823,1.84308 c 4.6648,2.17209 8.92094,6.20736 11.35843,10.76902 l 1.7508,3.27652 v 228.85913 228.85912 l -1.7508,3.27652 c -2.43749,4.56165 -6.69363,8.59693 -11.35843,10.76902 l -3.95823,1.84307 -71.81666,0.008 c -80.25579,0.009 -75.87966,0.30074 -82.47968,-5.50883 -1.94717,-1.71396 -4.45314,-5.03271 -5.66755,-7.50572 l -2.17699,-4.4334 -0.14493,-225.75596 c -0.1135,-176.7213 0.0626,-226.52255 0.8103,-229.28538 z m 89.3267,418.19113 h 35.29861 V 397.11844 206.6611 h -35.29861 -35.29863 v 190.45734 190.45734 z"
|
||||
id="path21" /><path
|
||||
id="path4-3"
|
||||
d="M 538.3432,588.49623 H 468.45195 V 208.34648 h 69.89125 v 380.14975 m 55.42425,32.062 v -444.176 c 0,-12.903 -10.45925,-23.36225 -23.36225,-23.36225 H 436.38995 c -12.903,0 -23.36225,10.45925 -23.36225,23.36225 v 444.176 c 0,12.903 10.45925,23.36225 23.36225,23.36225 h 133.9175 c 12.903,0 23.46,-10.45925 23.46,-23.36225 z"
|
||||
class="st0"
|
||||
style="fill:#ed6b21;stroke-width:0.9775" /><path
|
||||
id="path21-9"
|
||||
d="m 414.3077,170.7336 c 1.27074,-4.69513 3.80549,-8.66084 7.62593,-11.93098 6.23322,-5.33541 2.4228,-5.09896 82.03292,-5.09056 l 71.81666,0.008 3.95823,1.84308 c 4.6648,2.17209 8.92094,6.20736 11.35843,10.76902 l 1.7508,3.27652 v 228.85913 228.85912 l -1.7508,3.27652 c -2.43749,4.56165 -6.69363,8.59693 -11.35843,10.76902 l -3.95823,1.84307 -71.81666,0.008 c -80.25579,0.009 -75.87966,0.30074 -82.47968,-5.50883 -1.94717,-1.71396 -4.45314,-5.03271 -5.66755,-7.50572 l -2.17699,-4.4334 -0.14493,-225.75596 c -0.1135,-176.7213 0.0626,-226.52255 0.8103,-229.28538 z m 89.3267,418.19113 h 35.29861 V 398.46739 208.01005 H 503.6344 468.33577 v 190.45734 190.45734 z"
|
||||
style="fill:#ed6b21;fill-opacity:1;stroke-width:0.775793" /></svg>
|
After Width: | Height: | Size: 5.8 KiB |
75
resources/icons/notification_play.svg
Normal file
75
resources/icons/notification_play.svg
Normal file
|
@ -0,0 +1,75 @@
|
|||
<?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="notification_play.svg"
|
||||
xml:space="preserve"
|
||||
style="enable-background:new 0 0 800 800;"
|
||||
viewBox="0 0 800 800"
|
||||
y="0px"
|
||||
x="0px"
|
||||
id="Layer_1"
|
||||
version="1.1"><metadata
|
||||
id="metadata15"><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="defs13" /><sodipodi:namedview
|
||||
inkscape:current-layer="Layer_1"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:cy="407.44757"
|
||||
inkscape:cx="291.98172"
|
||||
inkscape:zoom="1.7819091"
|
||||
showgrid="false"
|
||||
id="namedview11"
|
||||
inkscape:window-height="1369"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0"
|
||||
guidetolerance="10"
|
||||
gridtolerance="10"
|
||||
objecttolerance="10"
|
||||
borderopacity="1"
|
||||
bordercolor="#666666"
|
||||
pagecolor="#ffffff"
|
||||
inkscape:document-rotation="0" />
|
||||
<style
|
||||
id="style2"
|
||||
type="text/css">
|
||||
.st0{fill:#ED6B21;}
|
||||
</style>
|
||||
|
||||
<path
|
||||
id="path6"
|
||||
d="M 501.14231,400.1595 345.84732,517.11951 v -233.75 l 155.29499,116.79 m 66.63999,0 c 0,-6.12 -2.72,-12.155 -8.15999,-16.235 L 330.12233,211.2895 c -13.43,-10.115 -32.555,-0.51 -32.555,16.235 v 345.35501 c 0,16.745 19.125,26.35 32.555,16.235 l 229.49998,-172.805 c 5.44,-3.995 8.16,-10.03 8.15999,-16.15 z"
|
||||
class="st0"
|
||||
style="stroke-width:0.85" />
|
||||
<g
|
||||
transform="matrix(0.9775,0,0,0.9775,53.547,53.54776)"
|
||||
id="g4">
|
||||
<path
|
||||
d="M 597.2,701.3 H 110.6 C 53.2,701.3 6.5,654.6 6.5,597.2 V 110.6 C 6.5,53.2 53.2,6.5 110.6,6.5 h 486.6 c 57.4,0 104.1,46.7 104.1,104.1 v 486.6 c 0,57.4 -46.7,104.1 -104.1,104.1 z M 110.6,52.4 c -32,0 -58.2,26 -58.2,58.2 v 486.6 c 0,32 26,58.2 58.2,58.2 h 486.6 c 32,0 58.2,-26 58.2,-58.2 V 110.6 c 0,-32 -26,-58.2 -58.2,-58.2 z"
|
||||
class="st0"
|
||||
id="path2" />
|
||||
</g>
|
||||
<path
|
||||
id="path17"
|
||||
d="m 150.65676,738.13001 c -12.4717,-1.39665 -26.66772,-5.9419 -37.84321,-12.1167 C 95.05898,716.20336 79.0447,699.32348 70.39542,681.3024 64.41036,668.8323 61.63518,657.94784 60.5735,642.7799 c -0.48062,-6.86631 -0.64046,-87.42616 -0.4973,-250.61509 0.1956,-222.88026 0.29494,-240.94222 1.35676,-246.58758 4.2349,-22.51562 13.68014,-40.62012 29.20092,-55.971935 14.23795,-14.08294 31.95867,-23.42796 52.60225,-27.7398 5.87892,-1.22794 14.00696,-1.26814 256.3492,-1.26814 h 250.27778 l 7.08334,1.5615 c 21.30688,4.69708 36.90336,13.21608 51.96053,28.3815 14.67865,14.784215 23.1932,30.350375 27.76125,50.752695 l 1.5679,7.0027 v 250.95241 c 0,242.72256 -0.042,251.15147 -1.2643,257.0238 -9.3059,44.69035 -45.18961,77.4335 -89.75565,81.9003 -9.17897,0.92 -488.33076,0.87925 -496.55942,-0.0425 z M 652.87274,692.49 c 19.93824,-6.17835 34.6922,-21.42494 40.00112,-41.33676 l 1.51306,-5.67491 V 399.58544 153.69258 l -1.52572,-5.7341 c -5.66288,-21.28292 -21.4158,-36.89778 -42.2051,-41.83524 -5.63964,-1.3394 -7.66026,-1.3488 -253.17948,-1.17612 l -247.49446,0.174 -4.72222,1.5953 c -18.05932,6.10094 -31.7315,19.23924 -37.4918,36.02779 -1.04762,3.05335 -2.22128,7.52472 -2.60814,9.93642 -0.47858,2.98361 -0.705,81.91877 -0.70846,246.99891 -0.004,218.14116 0.1022,243.18289 1.05916,248.25395 4.27172,22.63803 22.24346,40.86393 44.80876,45.44251 3.58848,0.72811 49.16894,0.8701 250.95238,0.7817 l 246.56746,-0.10799 z"
|
||||
style="fill:#ed6b21;fill-opacity:1;stroke-width:0.6746" /><path
|
||||
id="path19"
|
||||
d="m 300.17824,218.44362 c 2.06443,-4.13128 4.83026,-6.86894 9.40917,-9.3133 3.43647,-1.83451 12.82007,-1.79344 16.52778,0.0724 3.22403,1.6224 232.7236,174.17375 235.79013,177.281 3.35767,3.4022 4.88323,7.0012 5.23218,12.34324 0.27168,4.159 0.0718,5.32914 -1.5138,8.86232 -1.00471,2.23886 -2.78731,4.97342 -3.96129,6.07682 -4.28583,4.02812 -232.27571,175.25818 -235.54572,176.9048 -4.6586,2.34584 -12.1025,2.3876 -16.52928,0.0928 -3.87486,-2.00878 -7.80538,-5.7435 -9.67029,-9.18862 l -1.46069,-2.69842 -0.1736,-178.1462 -0.1736,-178.14618 z m 45.66908,298.67589 c 1.00351,-0.0612 154.99009,-116.48775 154.99009,-117.18534 0,-0.71184 -154.02593,-116.84804 -154.99009,-116.86318 -0.27827,-0.004 -0.50595,52.66168 -0.50595,117.03568 0,64.37401 0.22768,117.0298 0.50595,117.01284 z"
|
||||
style="fill:#ed6b21;fill-opacity:1;stroke-width:0.6746" /><path
|
||||
style="fill:#ed6b21;stroke-width:0.85"
|
||||
class="st0"
|
||||
d="M 501.14231,400.15949 345.84732,517.1195 v -233.75 l 155.29499,116.79 m 66.63999,0 c 0,-6.12 -2.72,-12.155 -8.15999,-16.235 L 330.12233,211.28949 c -13.43,-10.115 -32.555,-0.51 -32.555,16.235 V 572.8795 c 0,16.745 19.125,26.35 32.555,16.235 l 229.49998,-172.805 c 5.44,-3.995 8.16,-10.03 8.15999,-16.15 z"
|
||||
id="path6-2" /><path
|
||||
style="fill:#ed6b21;fill-opacity:1;stroke-width:0.6746"
|
||||
d="m 300.17824,218.44361 c 2.06443,-4.13128 4.83026,-6.86894 9.40917,-9.3133 3.43647,-1.83451 12.82007,-1.79344 16.52778,0.0724 3.22403,1.6224 232.7236,174.17375 235.79013,177.281 3.35767,3.4022 4.88323,7.0012 5.23218,12.34324 0.27168,4.159 0.0718,5.32914 -1.5138,8.86232 -1.00471,2.23886 -2.78731,4.97342 -3.96129,6.07682 -4.28583,4.02812 -232.27571,175.25818 -235.54572,176.9048 -4.6586,2.34584 -12.1025,2.3876 -16.52928,0.0928 -3.87486,-2.00878 -7.80538,-5.7435 -9.67029,-9.18862 l -1.46069,-2.69842 -0.1736,-178.1462 -0.1736,-178.14618 z m 45.66908,298.67589 c 1.00351,-0.0612 154.99009,-116.48775 154.99009,-117.18534 0,-0.71184 -154.02593,-116.84804 -154.99009,-116.86318 -0.27827,-0.004 -0.50595,52.66168 -0.50595,117.03568 0,64.37401 0.22768,117.0298 0.50595,117.01284 z"
|
||||
id="path19-3" /></svg>
|
After Width: | Height: | Size: 5.9 KiB |
75
resources/icons/notification_play_hover.svg
Normal file
75
resources/icons/notification_play_hover.svg
Normal file
|
@ -0,0 +1,75 @@
|
|||
<?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"
|
||||
version="1.1"
|
||||
id="Layer_1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 800 800"
|
||||
style="enable-background:new 0 0 800 800;"
|
||||
xml:space="preserve"
|
||||
sodipodi:docname="notification_play_hover.svg"
|
||||
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"><metadata
|
||||
id="metadata15"><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="defs13" /><sodipodi:namedview
|
||||
inkscape:document-rotation="0"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1369"
|
||||
id="namedview11"
|
||||
showgrid="false"
|
||||
inkscape:zoom="0.89095455"
|
||||
inkscape:cx="422.22291"
|
||||
inkscape:cy="611.44958"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="Layer_1" />
|
||||
<style
|
||||
type="text/css"
|
||||
id="style2">
|
||||
.st0{fill:#ED6B21;}
|
||||
</style>
|
||||
|
||||
<path
|
||||
style="stroke-width:0.9775"
|
||||
class="st0"
|
||||
d="M 511.41243,400.15312 332.8232,534.65714 v -268.8125 l 178.58923,134.3085 m 76.63599,0 c 0,-7.038 -3.128,-13.97825 -9.38399,-18.67025 L 314.73946,182.95262 c -15.4445,-11.63225 -37.43825,-0.5865 -37.43825,18.67025 v 397.15827 c 0,19.25675 21.99375,30.3025 37.43825,18.67025 L 578.66443,418.72564 c 6.256,-4.59425 9.384,-11.5345 9.38399,-18.5725 z"
|
||||
id="path6" />
|
||||
<g
|
||||
id="g4"
|
||||
transform="matrix(1.124125,0,0,1.124125,1.6564125,1.6571725)">
|
||||
<path
|
||||
id="path2"
|
||||
class="st0"
|
||||
d="M 597.2,701.3 H 110.6 C 53.2,701.3 6.5,654.6 6.5,597.2 V 110.6 C 6.5,53.2 53.2,6.5 110.6,6.5 h 486.6 c 57.4,0 104.1,46.7 104.1,104.1 v 486.6 c 0,57.4 -46.7,104.1 -104.1,104.1 z M 110.6,52.4 c -32,0 -58.2,26 -58.2,58.2 v 486.6 c 0,32 26,58.2 58.2,58.2 h 486.6 c 32,0 58.2,-26 58.2,-58.2 V 110.6 c 0,-32 -26,-58.2 -58.2,-58.2 z" />
|
||||
</g>
|
||||
<path
|
||||
style="fill:#ed6b21;fill-opacity:1;stroke-width:0.77579"
|
||||
d="M 113.38564,788.89156 C 99.043182,787.28541 82.717759,782.05837 69.865945,774.95735 49.44819,763.67591 31.031768,744.26405 21.085096,723.53981 14.202277,709.19919 11.01082,696.68206 9.7898876,679.23893 9.2371746,671.34268 9.0533586,578.69885 9.2179926,391.03158 9.4429326,134.71928 9.5571736,113.94803 10.778267,107.45586 15.648402,81.562898 26.510428,60.742723 44.359325,43.088136 60.732967,26.892755 81.111795,16.145982 104.85191,11.187366 111.61267,9.7752352 120.95992,9.7290052 399.65349,9.7290052 h 287.81945 l 8.14584,1.7957248 c 24.50291,5.401642 42.43886,15.198492 59.75461,32.638725 16.88045,17.001847 26.67218,34.902931 31.92544,58.365595 l 1.80308,8.05311 v 288.59527 c 0,279.13094 -0.0483,288.82419 -1.45394,295.57737 -10.70179,51.3939 -51.96805,89.04853 -103.219,94.18535 -10.55582,1.058 -561.58037,1.01113 -571.04333,-0.0489 z m 577.54837,-52.48601 c 22.92898,-7.10511 39.89603,-24.63868 46.00129,-47.53728 l 1.74002,-6.52614 V 399.5653 116.78851 l -1.75458,-6.59421 c -6.51231,-24.475359 -24.62817,-42.432448 -48.53586,-48.110527 -6.48559,-1.54031 -8.8093,-1.55112 -291.1564,-1.352538 l -284.61863,0.2001 -5.43056,1.834595 c -20.768214,7.016081 -36.491221,22.125126 -43.115566,41.43196 -1.204763,3.51135 -2.554472,8.65343 -2.999361,11.42688 -0.550367,3.43115 -0.81075,94.20659 -0.814729,284.04875 -0.0046,250.86233 0.11753,279.66032 1.218034,285.49204 4.912478,26.03373 25.579979,46.99352 51.530072,52.25889 4.12675,0.83732 56.54428,1.00061 288.59524,0.89895 l 283.55258,-0.12419 z"
|
||||
id="path17" /><path
|
||||
style="fill:#ed6b21;fill-opacity:1;stroke-width:0.77579"
|
||||
d="m 280.30386,191.19243 c 2.37409,-4.75097 5.55479,-7.89928 10.82054,-10.7103 3.95194,-2.10968 14.74308,-2.06245 19.00695,0.0833 3.70763,1.86576 267.63214,200.29982 271.15865,203.87315 3.86132,3.91253 5.61571,8.05138 6.017,14.19473 0.31244,4.78285 0.0826,6.12851 -1.74087,10.19167 -1.15541,2.57469 -3.2054,5.71943 -4.55548,6.98834 -4.9287,4.63234 -267.11707,201.54691 -270.87758,203.44052 -5.35739,2.69772 -13.91787,2.74574 -19.00867,0.10672 -4.45609,-2.3101 -8.97619,-6.60503 -11.12083,-10.56691 l -1.6798,-3.10319 -0.19964,-204.86813 -0.19964,-204.8681 z M 332.8233,534.6697 c 1.15403,-0.0704 178.2386,-133.96091 178.2386,-134.76314 0,-0.81862 -177.12982,-134.37525 -178.2386,-134.39266 -0.32001,-0.005 -0.58185,60.56094 -0.58185,134.59104 0,74.03011 0.26184,134.58427 0.58185,134.56476 z"
|
||||
id="path19" /><path
|
||||
id="path6-2"
|
||||
d="M 511.41243,400.15311 332.8232,534.65713 v -268.8125 l 178.58923,134.3085 m 76.63599,0 c 0,-7.038 -3.128,-13.97825 -9.38399,-18.67025 L 314.73946,182.95261 c -15.4445,-11.63225 -37.43825,-0.5865 -37.43825,18.67025 v 397.15827 c 0,19.25675 21.99375,30.3025 37.43825,18.67025 L 578.66443,418.72563 c 6.256,-4.59425 9.384,-11.5345 9.38399,-18.5725 z"
|
||||
class="st0"
|
||||
style="fill:#ed6b21;stroke-width:0.9775" /><path
|
||||
id="path19-3"
|
||||
d="m 280.30386,191.19242 c 2.37409,-4.75097 5.55479,-7.89928 10.82054,-10.7103 3.95194,-2.10968 14.74308,-2.06245 19.00695,0.0833 3.70763,1.86576 267.63214,200.29982 271.15865,203.87315 3.86132,3.91253 5.61571,8.05138 6.017,14.19473 0.31244,4.78285 0.0826,6.12851 -1.74087,10.19167 -1.15541,2.57469 -3.2054,5.71943 -4.55548,6.98834 -4.9287,4.63234 -267.11707,201.54691 -270.87758,203.44052 -5.35739,2.69772 -13.91787,2.74574 -19.00867,0.10672 -4.45609,-2.3101 -8.97619,-6.60503 -11.12083,-10.56691 l -1.6798,-3.10319 -0.19964,-204.86813 -0.19964,-204.8681 z m 52.51944,343.47727 c 1.15403,-0.0704 178.2386,-133.96091 178.2386,-134.76314 0,-0.81862 -177.12982,-134.37525 -178.2386,-134.39266 -0.32001,-0.005 -0.58185,60.56094 -0.58185,134.59104 0,74.03011 0.26184,134.58427 0.58185,134.56476 z"
|
||||
style="fill:#ed6b21;fill-opacity:1;stroke-width:0.77579" /></svg>
|
After Width: | Height: | Size: 6.1 KiB |
|
@ -113,6 +113,9 @@ int CLI::run(int argc, char **argv)
|
|||
std::find(m_transforms.begin(), m_transforms.end(), "cut") == m_transforms.end() &&
|
||||
std::find(m_transforms.begin(), m_transforms.end(), "cut_x") == m_transforms.end() &&
|
||||
std::find(m_transforms.begin(), m_transforms.end(), "cut_y") == m_transforms.end();
|
||||
bool start_downloader = false;
|
||||
bool delete_after_load = false;
|
||||
std::string download_url;
|
||||
bool start_as_gcodeviewer =
|
||||
#ifdef _WIN32
|
||||
false;
|
||||
|
@ -221,6 +224,11 @@ int CLI::run(int argc, char **argv)
|
|||
}
|
||||
if (!start_as_gcodeviewer) {
|
||||
for (const std::string& file : m_input_files) {
|
||||
if (boost::starts_with(file, "prusaslicer://")) {
|
||||
start_downloader = true;
|
||||
download_url = file;
|
||||
continue;
|
||||
}
|
||||
if (!boost::filesystem::exists(file)) {
|
||||
boost::nowide::cerr << "No such file: " << file << std::endl;
|
||||
exit(1);
|
||||
|
@ -478,6 +486,9 @@ int CLI::run(int argc, char **argv)
|
|||
// Models are repaired by default.
|
||||
//for (auto &model : m_models)
|
||||
// model.repair();
|
||||
|
||||
} else if (opt_key == "delete-after-load") {
|
||||
delete_after_load = true;
|
||||
} else {
|
||||
boost::nowide::cerr << "error: option not implemented yet: " << opt_key << std::endl;
|
||||
return 1;
|
||||
|
@ -663,9 +674,12 @@ int CLI::run(int argc, char **argv)
|
|||
params.extra_config = std::move(m_extra_config);
|
||||
params.input_files = std::move(m_input_files);
|
||||
params.start_as_gcodeviewer = start_as_gcodeviewer;
|
||||
params.start_downloader = start_downloader;
|
||||
params.download_url = download_url;
|
||||
params.delete_after_load = delete_after_load;
|
||||
#if ENABLE_GL_CORE_PROFILE
|
||||
params.opengl_version = opengl_version;
|
||||
#if ENABLE_OPENGL_DEBUG_OPTION
|
||||
params.opengl_version = opengl_version;
|
||||
params.opengl_debug = opengl_debug;
|
||||
#endif // ENABLE_OPENGL_DEBUG_OPTION
|
||||
#endif // ENABLE_GL_CORE_PROFILE
|
||||
|
|
|
@ -158,6 +158,12 @@ namespace ImGui
|
|||
const wchar_t SliderFloatEditBtnIcon = 0x2604;
|
||||
const wchar_t SliderFloatEditBtnPressedIcon = 0x2605;
|
||||
const wchar_t ClipboardBtnIcon = 0x2606;
|
||||
const wchar_t PlayButton = 0x2618;
|
||||
const wchar_t PlayHoverButton = 0x2619;
|
||||
const wchar_t PauseButton = 0x261A;
|
||||
const wchar_t PauseHoverButton = 0x261B;
|
||||
const wchar_t OpenButton = 0x261C;
|
||||
const wchar_t OpenHoverButton = 0x261D;
|
||||
|
||||
const wchar_t LegendTravel = 0x2701;
|
||||
const wchar_t LegendWipe = 0x2702;
|
||||
|
@ -173,8 +179,8 @@ namespace ImGui
|
|||
const wchar_t LegendToolMarker = 0x2712;
|
||||
const wchar_t WarningMarkerSmall = 0x2713;
|
||||
const wchar_t ExpandBtn = 0x2714;
|
||||
const wchar_t CollapseBtn = 0x2715;
|
||||
const wchar_t InfoMarkerSmall = 0x2716;
|
||||
const wchar_t CollapseBtn = 0x2715;
|
||||
|
||||
// void MyFunction(const char* name, const MyMatrix44& v);
|
||||
}
|
||||
|
|
|
@ -4632,6 +4632,10 @@ CLITransformConfigDef::CLITransformConfigDef()
|
|||
def->label = L("Scale to Fit");
|
||||
def->tooltip = L("Scale to fit the given volume.");
|
||||
def->set_default_value(new ConfigOptionPoint3(Vec3d(0,0,0)));
|
||||
|
||||
def = this->add("delete-after-load", coString);
|
||||
def->label = L("Delete files after loading");
|
||||
def->tooltip = L("Delete files after loading.");
|
||||
}
|
||||
|
||||
CLIMiscConfigDef::CLIMiscConfigDef()
|
||||
|
|
|
@ -7922,6 +7922,40 @@ mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, cha
|
|||
return n + 1;
|
||||
}
|
||||
|
||||
mz_uint mz_zip_reader_get_filename_from_extra(mz_zip_archive* pZip, mz_uint file_index, char* buffer, mz_uint extra_buf_size)
|
||||
{
|
||||
if (extra_buf_size == 0)
|
||||
return 0;
|
||||
mz_uint nf;
|
||||
mz_uint ne;
|
||||
const mz_uint8* p = mz_zip_get_cdh(pZip, file_index);
|
||||
if (!p)
|
||||
{
|
||||
if (extra_buf_size)
|
||||
buffer[0] = '\0';
|
||||
mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
|
||||
return 0;
|
||||
}
|
||||
nf = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS);
|
||||
ne = MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS);
|
||||
|
||||
int copy = 0;
|
||||
char const* p_nf = p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + nf;
|
||||
char const* e = p_nf + ne + 1;
|
||||
while (p_nf + 4 < e) {
|
||||
mz_uint16 len = ((mz_uint16)p_nf[2]) | ((mz_uint16)p_nf[3] << 8);
|
||||
if (p_nf[0] == '\x75' && p_nf[1] == '\x70' && len >= 5 && p_nf + 4 + len < e && p_nf[4] == '\x01') {
|
||||
mz_uint length = MZ_MIN(len - 5, extra_buf_size - 1);
|
||||
memcpy(buffer, p_nf + 9, length);
|
||||
return length;
|
||||
}
|
||||
else {
|
||||
p_nf += 4 + len;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat)
|
||||
{
|
||||
return mz_zip_file_stat_internal(pZip, file_index, mz_zip_get_cdh(pZip, file_index), pStat, NULL);
|
||||
|
|
|
@ -1166,6 +1166,9 @@ mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, cha
|
|||
int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags);
|
||||
int mz_zip_reader_locate_file_v2(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *file_index);
|
||||
|
||||
/* Retrieves the filename of an archive file entry from EXTRA ID. */
|
||||
mz_uint mz_zip_reader_get_filename_from_extra(mz_zip_archive * pZip, mz_uint file_index, char* buffer, mz_uint extra_buf_size);
|
||||
|
||||
/* Returns detailed information about an archive file entry. */
|
||||
mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat);
|
||||
|
||||
|
|
|
@ -110,6 +110,17 @@
|
|||
<string>Alternate</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleURLName</key>
|
||||
<string>PrusaSlicer Downloads</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>prusaslicer</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>10.12</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
|
|
|
@ -232,6 +232,12 @@ set(SLIC3R_GUI_SOURCES
|
|||
GUI/DesktopIntegrationDialog.hpp
|
||||
GUI/HintNotification.cpp
|
||||
GUI/HintNotification.hpp
|
||||
GUI/FileArchiveDialog.cpp
|
||||
GUI/FileArchiveDialog.hpp
|
||||
GUI/Downloader.cpp
|
||||
GUI/Downloader.hpp
|
||||
GUI/DownloaderFileGet.cpp
|
||||
GUI/DownloaderFileGet.hpp
|
||||
Utils/AppUpdater.cpp
|
||||
Utils/AppUpdater.hpp
|
||||
Utils/Http.cpp
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -392,10 +392,56 @@ struct PageUpdate: ConfigWizardPage
|
|||
{
|
||||
bool version_check;
|
||||
bool preset_update;
|
||||
wxTextCtrl* path_text_ctrl;
|
||||
|
||||
PageUpdate(ConfigWizard *parent);
|
||||
};
|
||||
|
||||
namespace DownloaderUtils {
|
||||
wxString get_downloads_path();
|
||||
|
||||
class Worker : public wxBoxSizer
|
||||
{
|
||||
wxWindow* m_parent {nullptr};
|
||||
wxTextCtrl* m_input_path {nullptr};
|
||||
bool downloader_checked {false};
|
||||
#ifdef __linux__
|
||||
bool perform_registration_linux { false };
|
||||
#endif // __linux__
|
||||
|
||||
bool perform_register();
|
||||
void deregister();
|
||||
|
||||
public:
|
||||
Worker(wxWindow* parent);
|
||||
~Worker(){}
|
||||
|
||||
void allow(bool allow_) { downloader_checked = allow_; }
|
||||
bool is_checked() const { return downloader_checked; }
|
||||
wxString path_name() const { return m_input_path ? m_input_path->GetValue() : wxString(); }
|
||||
|
||||
void set_path_name(wxString name);
|
||||
void set_path_name(const std::string& name);
|
||||
|
||||
bool on_finish();
|
||||
|
||||
#ifdef __linux__
|
||||
bool get_perform_registration_linux() { return perform_registration_linux; }
|
||||
#endif // __linux__
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
struct PageDownloader : ConfigWizardPage
|
||||
{
|
||||
DownloaderUtils::Worker* downloader{ nullptr };
|
||||
|
||||
PageDownloader(ConfigWizard* parent);
|
||||
|
||||
bool on_finish_downloader() const ;
|
||||
};
|
||||
|
||||
struct PageReloadFromDisk : ConfigWizardPage
|
||||
{
|
||||
bool full_pathnames;
|
||||
|
@ -583,7 +629,8 @@ struct ConfigWizard::priv
|
|||
PageMaterials *page_filaments = nullptr;
|
||||
PageMaterials *page_sla_materials = nullptr;
|
||||
PageCustom *page_custom = nullptr;
|
||||
PageUpdate *page_update = nullptr;
|
||||
PageUpdate* page_update = nullptr;
|
||||
PageDownloader* page_downloader = nullptr;
|
||||
PageReloadFromDisk *page_reload_from_disk = nullptr;
|
||||
#ifdef _WIN32
|
||||
PageFilesAssociation* page_files_association = nullptr;
|
||||
|
@ -631,9 +678,9 @@ struct ConfigWizard::priv
|
|||
bool apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater, bool& apply_keeped_changes);
|
||||
// #ys_FIXME_alise
|
||||
void update_presets_in_config(const std::string& section, const std::string& alias_key, bool add);
|
||||
#ifdef __linux__
|
||||
void perform_desktop_integration() const;
|
||||
#endif
|
||||
//#ifdef __linux__
|
||||
// void perform_desktop_integration() const;
|
||||
//#endif
|
||||
bool check_fff_selected(); // Used to decide whether to display Filaments page
|
||||
bool check_sla_selected(); // Used to decide whether to display SLA Materials page
|
||||
|
||||
|
|
|
@ -218,10 +218,9 @@ bool DesktopIntegrationDialog::integration_possible()
|
|||
{
|
||||
return true;
|
||||
}
|
||||
void DesktopIntegrationDialog::perform_desktop_integration()
|
||||
void DesktopIntegrationDialog::perform_desktop_integration(bool perform_downloader)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug) << "performing desktop integration";
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << "performing desktop integration. With downloader integration: " << perform_downloader;
|
||||
// Path to appimage
|
||||
const char *appimage_env = std::getenv("APPIMAGE");
|
||||
std::string excutable_path;
|
||||
|
@ -287,7 +286,7 @@ void DesktopIntegrationDialog::perform_desktop_integration()
|
|||
|
||||
std::string target_dir_icons;
|
||||
std::string target_dir_desktop;
|
||||
|
||||
|
||||
// slicer icon
|
||||
// iterate thru target_candidates to find icons folder
|
||||
for (size_t i = 0; i < target_candidates.size(); ++i) {
|
||||
|
@ -300,20 +299,20 @@ void DesktopIntegrationDialog::perform_desktop_integration()
|
|||
break; // success
|
||||
else
|
||||
target_dir_icons.clear(); // copying failed
|
||||
// if all failed - try creating default home folder
|
||||
if (i == target_candidates.size() - 1) {
|
||||
// create $HOME/.local/share
|
||||
create_path(boost::nowide::narrow(wxFileName::GetHomeDir()), ".local/share/icons" + icon_theme_dirs);
|
||||
// copy icon
|
||||
target_dir_icons = GUI::format("%1%/.local/share",wxFileName::GetHomeDir());
|
||||
std::string icon_path = GUI::format("%1%/icons/PrusaSlicer.png",resources_dir());
|
||||
std::string dest_path = GUI::format("%1%/icons/%2%PrusaSlicer%3%.png", target_dir_icons, icon_theme_path, version_suffix);
|
||||
if (!contains_path_dir(target_dir_icons, "icons")
|
||||
|| !copy_icon(icon_path, dest_path)) {
|
||||
// every attempt failed - icon wont be present
|
||||
target_dir_icons.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
// if all failed - try creating default home folder
|
||||
if (i == target_candidates.size() - 1) {
|
||||
// create $HOME/.local/share
|
||||
create_path(boost::nowide::narrow(wxFileName::GetHomeDir()), ".local/share/icons" + icon_theme_dirs);
|
||||
// copy icon
|
||||
target_dir_icons = GUI::format("%1%/.local/share",wxFileName::GetHomeDir());
|
||||
std::string icon_path = GUI::format("%1%/icons/PrusaSlicer.png",resources_dir());
|
||||
std::string dest_path = GUI::format("%1%/icons/%2%PrusaSlicer%3%.png", target_dir_icons, icon_theme_path, version_suffix);
|
||||
if (!contains_path_dir(target_dir_icons, "icons")
|
||||
|| !copy_icon(icon_path, dest_path)) {
|
||||
// every attempt failed - icon wont be present
|
||||
target_dir_icons.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
if(target_dir_icons.empty()) {
|
||||
|
@ -324,25 +323,25 @@ void DesktopIntegrationDialog::perform_desktop_integration()
|
|||
|
||||
// desktop file
|
||||
// iterate thru target_candidates to find applications folder
|
||||
for (size_t i = 0; i < target_candidates.size(); ++i)
|
||||
{
|
||||
|
||||
std::string desktop_file = GUI::format(
|
||||
"[Desktop Entry]\n"
|
||||
"Name=PrusaSlicer%1%\n"
|
||||
"GenericName=3D Printing Software\n"
|
||||
"Icon=PrusaSlicer%2%\n"
|
||||
"Exec=\"%3%\" %%F\n"
|
||||
"Terminal=false\n"
|
||||
"Type=Application\n"
|
||||
"MimeType=model/stl;application/vnd.ms-3mfdocument;application/prs.wavefront-obj;application/x-amf;\n"
|
||||
"Categories=Graphics;3DGraphics;Engineering;\n"
|
||||
"Keywords=3D;Printing;Slicer;slice;3D;printer;convert;gcode;stl;obj;amf;SLA\n"
|
||||
"StartupNotify=false\n"
|
||||
"StartupWMClass=prusa-slicer\n", name_suffix, version_suffix, excutable_path);
|
||||
|
||||
for (size_t i = 0; i < target_candidates.size(); ++i) {
|
||||
if (contains_path_dir(target_candidates[i], "applications")) {
|
||||
target_dir_desktop = target_candidates[i];
|
||||
// Write slicer desktop file
|
||||
std::string desktop_file = GUI::format(
|
||||
"[Desktop Entry]\n"
|
||||
"Name=PrusaSlicer%1%\n"
|
||||
"GenericName=3D Printing Software\n"
|
||||
"Icon=PrusaSlicer%2%\n"
|
||||
"Exec=\"%3%\" %%F\n"
|
||||
"Terminal=false\n"
|
||||
"Type=Application\n"
|
||||
"MimeType=model/stl;application/vnd.ms-3mfdocument;application/prs.wavefront-obj;application/x-amf;\n"
|
||||
"Categories=Graphics;3DGraphics;Engineering;\n"
|
||||
"Keywords=3D;Printing;Slicer;slice;3D;printer;convert;gcode;stl;obj;amf;SLA\n"
|
||||
"StartupNotify=false\n"
|
||||
"StartupWMClass=prusa-slicer\n", name_suffix, version_suffix, excutable_path);
|
||||
|
||||
std::string path = GUI::format("%1%/applications/PrusaSlicer%2%.desktop", target_dir_desktop, version_suffix);
|
||||
if (create_desktop_file(path, desktop_file)){
|
||||
BOOST_LOG_TRIVIAL(debug) << "PrusaSlicer.desktop file installation success.";
|
||||
|
@ -352,24 +351,24 @@ void DesktopIntegrationDialog::perform_desktop_integration()
|
|||
BOOST_LOG_TRIVIAL(debug) << "Attempt to PrusaSlicer.desktop file installation failed. failed path: " << target_candidates[i];
|
||||
target_dir_desktop.clear();
|
||||
}
|
||||
// if all failed - try creating default home folder
|
||||
if (i == target_candidates.size() - 1) {
|
||||
// create $HOME/.local/share
|
||||
create_path(boost::nowide::narrow(wxFileName::GetHomeDir()), ".local/share/applications");
|
||||
// create desktop file
|
||||
target_dir_desktop = GUI::format("%1%/.local/share",wxFileName::GetHomeDir());
|
||||
std::string path = GUI::format("%1%/applications/PrusaSlicer%2%.desktop", target_dir_desktop, version_suffix);
|
||||
if (contains_path_dir(target_dir_desktop, "applications")) {
|
||||
if (!create_desktop_file(path, desktop_file)) {
|
||||
// Desktop file not written - end desktop integration
|
||||
BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed - could not create desktop file";
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// Desktop file not written - end desktop integration
|
||||
BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed because the application directory was not found.";
|
||||
}
|
||||
// if all failed - try creating default home folder
|
||||
if (i == target_candidates.size() - 1) {
|
||||
// create $HOME/.local/share
|
||||
create_path(boost::nowide::narrow(wxFileName::GetHomeDir()), ".local/share/applications");
|
||||
// create desktop file
|
||||
target_dir_desktop = GUI::format("%1%/.local/share",wxFileName::GetHomeDir());
|
||||
std::string path = GUI::format("%1%/applications/PrusaSlicer%2%.desktop", target_dir_desktop, version_suffix);
|
||||
if (contains_path_dir(target_dir_desktop, "applications")) {
|
||||
if (!create_desktop_file(path, desktop_file)) {
|
||||
// Desktop file not written - end desktop integration
|
||||
BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed - could not create desktop file";
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// Desktop file not written - end desktop integration
|
||||
BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed because the application directory was not found.";
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -398,7 +397,7 @@ void DesktopIntegrationDialog::perform_desktop_integration()
|
|||
}
|
||||
|
||||
// Desktop file
|
||||
std::string desktop_file = GUI::format(
|
||||
std::string desktop_file_viewer = GUI::format(
|
||||
"[Desktop Entry]\n"
|
||||
"Name=Prusa Gcode Viewer%1%\n"
|
||||
"GenericName=3D Printing Software\n"
|
||||
|
@ -410,9 +409,8 @@ void DesktopIntegrationDialog::perform_desktop_integration()
|
|||
"Categories=Graphics;3DGraphics;\n"
|
||||
"Keywords=3D;Printing;Slicer;\n"
|
||||
"StartupNotify=false\n", name_suffix, version_suffix, excutable_path);
|
||||
|
||||
std::string desktop_path = GUI::format("%1%/applications/PrusaSlicerGcodeViewer%2%.desktop", target_dir_desktop, version_suffix);
|
||||
if (create_desktop_file(desktop_path, desktop_file))
|
||||
if (create_desktop_file(desktop_path, desktop_file_viewer))
|
||||
// save path to desktop file
|
||||
app_config->set("desktop_integration_app_viewer_path", desktop_path);
|
||||
else {
|
||||
|
@ -421,6 +419,37 @@ void DesktopIntegrationDialog::perform_desktop_integration()
|
|||
}
|
||||
}
|
||||
|
||||
if (perform_downloader)
|
||||
{
|
||||
std::string desktop_file_downloader = GUI::format(
|
||||
"[Desktop Entry]\n"
|
||||
"Name=PrusaSlicer URL Protocol%1%\n"
|
||||
"Exec=\"%3%\" --single-instance %%u\n"
|
||||
"Icon=PrusaSlicer%4%\n"
|
||||
"Terminal=false\n"
|
||||
"Type=Application\n"
|
||||
"MimeType=x-scheme-handler/prusaslicer;\n"
|
||||
"StartupNotify=false\n"
|
||||
, name_suffix, version_suffix, excutable_path, version_suffix);
|
||||
|
||||
// desktop file for downloader as part of main app
|
||||
std::string desktop_path = GUI::format("%1%/applications/PrusaSlicerURLProtocol%2%.desktop", target_dir_desktop, version_suffix);
|
||||
if (create_desktop_file(desktop_path, desktop_file_downloader)) {
|
||||
// save path to desktop file
|
||||
app_config->set("desktop_integration_URL_path", desktop_path);
|
||||
// finish registration on mime type
|
||||
std::string command = GUI::format("xdg-mime default PrusaSlicerURLProtocol%1%.desktop x-scheme-handler/prusaslicer", version_suffix);
|
||||
BOOST_LOG_TRIVIAL(debug) << "system command: " << command;
|
||||
int r = system(command.c_str());
|
||||
BOOST_LOG_TRIVIAL(debug) << "system result: " << r;
|
||||
|
||||
} else {
|
||||
BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed - could not create URL Protocol desktop file";
|
||||
show_error(nullptr, _L("Performing desktop integration failed - could not create URL Protocol desktop file."));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::DesktopIntegrationSuccess);
|
||||
}
|
||||
void DesktopIntegrationDialog::undo_desktop_intgration()
|
||||
|
@ -453,9 +482,26 @@ void DesktopIntegrationDialog::undo_desktop_intgration()
|
|||
std::remove(path.c_str());
|
||||
}
|
||||
}
|
||||
// URL Protocol
|
||||
path = std::string(app_config->get("desktop_integration_URL_path"));
|
||||
if (!path.empty()) {
|
||||
BOOST_LOG_TRIVIAL(debug) << "removing " << path;
|
||||
std::remove(path.c_str());
|
||||
}
|
||||
wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::UndoDesktopIntegrationSuccess);
|
||||
}
|
||||
|
||||
void DesktopIntegrationDialog::undo_downloader_registration()
|
||||
{
|
||||
const AppConfig *app_config = wxGetApp().app_config;
|
||||
std::string path = std::string(app_config->get("desktop_integration_URL_path"));
|
||||
if (!path.empty()) {
|
||||
BOOST_LOG_TRIVIAL(debug) << "removing " << path;
|
||||
std::remove(path.c_str());
|
||||
}
|
||||
// There is no need to undo xdg-mime default command. It is done automatically when desktop file is deleted.
|
||||
}
|
||||
|
||||
DesktopIntegrationDialog::DesktopIntegrationDialog(wxWindow *parent)
|
||||
: wxDialog(parent, wxID_ANY, _(L("Desktop Integration")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER)
|
||||
{
|
||||
|
@ -481,7 +527,7 @@ DesktopIntegrationDialog::DesktopIntegrationDialog(wxWindow *parent)
|
|||
wxButton *btn_perform = new wxButton(this, wxID_ANY, _L("Perform"));
|
||||
btn_szr->Add(btn_perform, 0, wxALL, 10);
|
||||
|
||||
btn_perform->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { DesktopIntegrationDialog::perform_desktop_integration(); EndModal(wxID_ANY); });
|
||||
btn_perform->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { DesktopIntegrationDialog::perform_desktop_integration(false); EndModal(wxID_ANY); });
|
||||
|
||||
if (can_undo){
|
||||
wxButton *btn_undo = new wxButton(this, wxID_ANY, _L("Undo"));
|
||||
|
|
|
@ -26,9 +26,14 @@ public:
|
|||
// Creates Desktop files and icons for both PrusaSlicer and GcodeViewer.
|
||||
// Stores paths into App Config.
|
||||
// Rewrites if files already existed.
|
||||
static void perform_desktop_integration();
|
||||
// if perform_downloader:
|
||||
// Creates Destktop files for PrusaSlicer downloader feature
|
||||
// Regiters PrusaSlicer to start on prusaslicer:// URL
|
||||
static void perform_desktop_integration(bool perform_downloader);
|
||||
// Deletes Desktop files and icons for both PrusaSlicer and GcodeViewer at paths stored in App Config.
|
||||
static void undo_desktop_intgration();
|
||||
|
||||
static void undo_downloader_registration();
|
||||
private:
|
||||
|
||||
};
|
||||
|
|
245
src/slic3r/GUI/Downloader.cpp
Normal file
245
src/slic3r/GUI/Downloader.cpp
Normal file
|
@ -0,0 +1,245 @@
|
|||
#include "Downloader.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "NotificationManager.hpp"
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
namespace {
|
||||
void open_folder(const std::string& path)
|
||||
{
|
||||
// Code taken from NotificationManager.cpp
|
||||
|
||||
// Execute command to open a file explorer, platform dependent.
|
||||
// FIXME: The const_casts aren't needed in wxWidgets 3.1, remove them when we upgrade.
|
||||
|
||||
#ifdef _WIN32
|
||||
const wxString widepath = from_u8(path);
|
||||
const wchar_t* argv[] = { L"explorer", widepath.GetData(), nullptr };
|
||||
::wxExecute(const_cast<wchar_t**>(argv), wxEXEC_ASYNC, nullptr);
|
||||
#elif __APPLE__
|
||||
const char* argv[] = { "open", path.data(), nullptr };
|
||||
::wxExecute(const_cast<char**>(argv), wxEXEC_ASYNC, nullptr);
|
||||
#else
|
||||
const char* argv[] = { "xdg-open", path.data(), nullptr };
|
||||
|
||||
// Check if we're running in an AppImage container, if so, we need to remove AppImage's env vars,
|
||||
// because they may mess up the environment expected by the file manager.
|
||||
// Mostly this is about LD_LIBRARY_PATH, but we remove a few more too for good measure.
|
||||
if (wxGetEnv("APPIMAGE", nullptr)) {
|
||||
// We're running from AppImage
|
||||
wxEnvVariableHashMap env_vars;
|
||||
wxGetEnvMap(&env_vars);
|
||||
|
||||
env_vars.erase("APPIMAGE");
|
||||
env_vars.erase("APPDIR");
|
||||
env_vars.erase("LD_LIBRARY_PATH");
|
||||
env_vars.erase("LD_PRELOAD");
|
||||
env_vars.erase("UNION_PRELOAD");
|
||||
|
||||
wxExecuteEnv exec_env;
|
||||
exec_env.env = std::move(env_vars);
|
||||
|
||||
wxString owd;
|
||||
if (wxGetEnv("OWD", &owd)) {
|
||||
// This is the original work directory from which the AppImage image was run,
|
||||
// set it as CWD for the child process:
|
||||
exec_env.cwd = std::move(owd);
|
||||
}
|
||||
|
||||
::wxExecute(const_cast<char**>(argv), wxEXEC_ASYNC, nullptr, &exec_env);
|
||||
}
|
||||
else {
|
||||
// Looks like we're NOT running from AppImage, we'll make no changes to the environment.
|
||||
::wxExecute(const_cast<char**>(argv), wxEXEC_ASYNC, nullptr, nullptr);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string filename_from_url(const std::string& url)
|
||||
{
|
||||
// TODO: can it be done with curl?
|
||||
size_t slash = url.find_last_of("/");
|
||||
if (slash == std::string::npos && slash != url.size() - 1)
|
||||
return std::string();
|
||||
return url.substr(slash + 1, url.size() - slash + 1);
|
||||
}
|
||||
}
|
||||
|
||||
Download::Download(int ID, std::string url, wxEvtHandler* evt_handler, const boost::filesystem::path& dest_folder)
|
||||
: m_id(ID)
|
||||
, m_filename(filename_from_url(url))
|
||||
, m_dest_folder(dest_folder)
|
||||
{
|
||||
assert(boost::filesystem::is_directory(dest_folder));
|
||||
m_final_path = dest_folder / m_filename;
|
||||
m_file_get = std::make_shared<FileGet>(ID, std::move(url), m_filename, evt_handler, dest_folder);
|
||||
}
|
||||
|
||||
void Download::start()
|
||||
{
|
||||
m_state = DownloadState::DownloadOngoing;
|
||||
m_file_get->get();
|
||||
}
|
||||
void Download::cancel()
|
||||
{
|
||||
m_state = DownloadState::DownloadStopped;
|
||||
m_file_get->cancel();
|
||||
}
|
||||
void Download::pause()
|
||||
{
|
||||
//assert(m_state == DownloadState::DownloadOngoing);
|
||||
// if instead of assert - it can happen that user clicks on pause several times before the pause happens
|
||||
if (m_state != DownloadState::DownloadOngoing)
|
||||
return;
|
||||
m_state = DownloadState::DownloadPaused;
|
||||
m_file_get->pause();
|
||||
}
|
||||
void Download::resume()
|
||||
{
|
||||
//assert(m_state == DownloadState::DownloadPaused);
|
||||
if (m_state != DownloadState::DownloadPaused)
|
||||
return;
|
||||
m_state = DownloadState::DownloadOngoing;
|
||||
m_file_get->resume();
|
||||
}
|
||||
|
||||
|
||||
Downloader::Downloader()
|
||||
: wxEvtHandler()
|
||||
{
|
||||
//Bind(EVT_DWNLDR_FILE_COMPLETE, [](const wxCommandEvent& evt) {});
|
||||
//Bind(EVT_DWNLDR_FILE_PROGRESS, [](const wxCommandEvent& evt) {});
|
||||
//Bind(EVT_DWNLDR_FILE_ERROR, [](const wxCommandEvent& evt) {});
|
||||
//Bind(EVT_DWNLDR_FILE_NAME_CHANGE, [](const wxCommandEvent& evt) {});
|
||||
|
||||
Bind(EVT_DWNLDR_FILE_COMPLETE, &Downloader::on_complete, this);
|
||||
Bind(EVT_DWNLDR_FILE_PROGRESS, &Downloader::on_progress, this);
|
||||
Bind(EVT_DWNLDR_FILE_ERROR, &Downloader::on_error, this);
|
||||
Bind(EVT_DWNLDR_FILE_NAME_CHANGE, &Downloader::on_name_change, this);
|
||||
Bind(EVT_DWNLDR_FILE_PAUSED, &Downloader::on_paused, this);
|
||||
Bind(EVT_DWNLDR_FILE_CANCELED, &Downloader::on_canceled, this);
|
||||
}
|
||||
|
||||
void Downloader::start_download(const std::string& full_url)
|
||||
{
|
||||
assert(m_initialized);
|
||||
|
||||
// TODO: There is a misterious slash appearing in recieved msg on windows
|
||||
#ifdef _WIN32
|
||||
if (!boost::starts_with(full_url, "prusaslicer://open/?file=")) {
|
||||
#else
|
||||
if (!boost::starts_with(full_url, "prusaslicer://open?file=")) {
|
||||
#endif
|
||||
BOOST_LOG_TRIVIAL(error) << "Could not start download due to wrong URL: " << full_url;
|
||||
// TODO: show error?
|
||||
return;
|
||||
}
|
||||
size_t id = get_next_id();
|
||||
// TODO: still same mistery
|
||||
#ifdef _WIN32
|
||||
std::string escaped_url = FileGet::escape_url(full_url.substr(25));
|
||||
#else
|
||||
std::string escaped_url = FileGet::escape_url(full_url.substr(24));
|
||||
#endif
|
||||
// TODO: enable after testing
|
||||
/*
|
||||
if (!boost::starts_with(escaped_url, "https://media.printables.com/")) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Download won't start. Download URL doesn't point to https://media.printables.com : " << escaped_url;
|
||||
// TODO: show error?
|
||||
return;
|
||||
}
|
||||
*/
|
||||
std::string text(escaped_url);
|
||||
m_downloads.emplace_back(std::make_unique<Download>(id, std::move(escaped_url), this, m_dest_folder));
|
||||
NotificationManager* ntf_mngr = wxGetApp().notification_manager();
|
||||
ntf_mngr->push_download_URL_progress_notification(id, m_downloads.back()->get_filename(), std::bind(&Downloader::user_action_callback, this, std::placeholders::_1, std::placeholders::_2));
|
||||
m_downloads.back()->start();
|
||||
BOOST_LOG_TRIVIAL(debug) << "started download";
|
||||
}
|
||||
|
||||
void Downloader::on_progress(wxCommandEvent& event)
|
||||
{
|
||||
size_t id = event.GetInt();
|
||||
float percent = (float)std::stoi(boost::nowide::narrow(event.GetString())) / 100.f;
|
||||
//BOOST_LOG_TRIVIAL(error) << "progress " << id << ": " << percent;
|
||||
NotificationManager* ntf_mngr = wxGetApp().notification_manager();
|
||||
BOOST_LOG_TRIVIAL(trace) << "Download "<< id << ": " << percent;
|
||||
ntf_mngr->set_download_URL_progress(id, percent);
|
||||
}
|
||||
void Downloader::on_error(wxCommandEvent& event)
|
||||
{
|
||||
size_t id = event.GetInt();
|
||||
set_download_state(event.GetInt(), DownloadState::DownloadError);
|
||||
BOOST_LOG_TRIVIAL(error) << "Download error: " << event.GetString();
|
||||
NotificationManager* ntf_mngr = wxGetApp().notification_manager();
|
||||
ntf_mngr->set_download_URL_error(id, boost::nowide::narrow(event.GetString()));
|
||||
}
|
||||
void Downloader::on_complete(wxCommandEvent& event)
|
||||
{
|
||||
// TODO: is this always true? :
|
||||
// here we open the file itself, notification should get 1.f progress from on progress.
|
||||
set_download_state(event.GetInt(), DownloadState::DownloadDone);
|
||||
wxArrayString paths;
|
||||
paths.Add(event.GetString());
|
||||
wxGetApp().plater()->load_files(paths);
|
||||
}
|
||||
bool Downloader::user_action_callback(DownloaderUserAction action, int id)
|
||||
{
|
||||
for (size_t i = 0; i < m_downloads.size(); ++i) {
|
||||
if (m_downloads[i]->get_id() == id) {
|
||||
switch (action) {
|
||||
case DownloadUserCanceled:
|
||||
m_downloads[i]->cancel();
|
||||
return true;
|
||||
case DownloadUserPaused:
|
||||
m_downloads[i]->pause();
|
||||
return true;
|
||||
case DownloadUserContinued:
|
||||
m_downloads[i]->resume();
|
||||
return true;
|
||||
case DownloadUserOpenedFolder:
|
||||
open_folder(m_downloads[i]->get_dest_folder());
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Downloader::on_name_change(wxCommandEvent& event)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Downloader::on_paused(wxCommandEvent& event)
|
||||
{
|
||||
size_t id = event.GetInt();
|
||||
NotificationManager* ntf_mngr = wxGetApp().notification_manager();
|
||||
ntf_mngr->set_download_URL_paused(id);
|
||||
}
|
||||
|
||||
void Downloader::on_canceled(wxCommandEvent& event)
|
||||
{
|
||||
size_t id = event.GetInt();
|
||||
NotificationManager* ntf_mngr = wxGetApp().notification_manager();
|
||||
ntf_mngr->set_download_URL_canceled(id);
|
||||
}
|
||||
|
||||
void Downloader::set_download_state(int id, DownloadState state)
|
||||
{
|
||||
for (size_t i = 0; i < m_downloads.size(); ++i) {
|
||||
if (m_downloads[i]->get_id() == id) {
|
||||
m_downloads[i]->set_state(state);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
99
src/slic3r/GUI/Downloader.hpp
Normal file
99
src/slic3r/GUI/Downloader.hpp
Normal file
|
@ -0,0 +1,99 @@
|
|||
#ifndef slic3r_Downloader_hpp_
|
||||
#define slic3r_Downloader_hpp_
|
||||
|
||||
#include "DownloaderFileGet.hpp"
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <wx/wx.h>
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
class NotificationManager;
|
||||
|
||||
enum DownloadState
|
||||
{
|
||||
DownloadPending = 0,
|
||||
DownloadOngoing,
|
||||
DownloadStopped,
|
||||
DownloadDone,
|
||||
DownloadError,
|
||||
DownloadPaused,
|
||||
DownloadStateUnknown
|
||||
};
|
||||
|
||||
enum DownloaderUserAction
|
||||
{
|
||||
DownloadUserCanceled,
|
||||
DownloadUserPaused,
|
||||
DownloadUserContinued,
|
||||
DownloadUserOpenedFolder
|
||||
};
|
||||
|
||||
class Download {
|
||||
public:
|
||||
Download(int ID, std::string url, wxEvtHandler* evt_handler, const boost::filesystem::path& dest_folder);
|
||||
void start();
|
||||
void cancel();
|
||||
void pause();
|
||||
void resume();
|
||||
|
||||
int get_id() const { return m_id; }
|
||||
boost::filesystem::path get_final_path() const { return m_final_path; }
|
||||
std::string get_filename() const { return m_filename; }
|
||||
DownloadState get_state() const { return m_state; }
|
||||
void set_state(DownloadState state) { m_state = state; }
|
||||
std::string get_dest_folder() { return m_dest_folder.string(); }
|
||||
private:
|
||||
const int m_id;
|
||||
std::string m_filename;
|
||||
boost::filesystem::path m_final_path;
|
||||
boost::filesystem::path m_dest_folder;
|
||||
std::shared_ptr<FileGet> m_file_get;
|
||||
DownloadState m_state { DownloadState::DownloadPending };
|
||||
};
|
||||
|
||||
class Downloader : public wxEvtHandler {
|
||||
public:
|
||||
Downloader();
|
||||
|
||||
bool get_initialized() { return m_initialized; }
|
||||
void init(const boost::filesystem::path& dest_folder)
|
||||
{
|
||||
m_dest_folder = dest_folder;
|
||||
m_initialized = true;
|
||||
}
|
||||
void start_download(const std::string& full_url);
|
||||
// cancel = false -> just pause
|
||||
bool user_action_callback(DownloaderUserAction action, int id);
|
||||
private:
|
||||
bool m_initialized { false };
|
||||
|
||||
std::vector<std::unique_ptr<Download>> m_downloads;
|
||||
boost::filesystem::path m_dest_folder;
|
||||
|
||||
size_t m_next_id { 0 };
|
||||
size_t get_next_id() { return ++m_next_id; }
|
||||
|
||||
void on_progress(wxCommandEvent& event);
|
||||
void on_error(wxCommandEvent& event);
|
||||
void on_complete(wxCommandEvent& event);
|
||||
void on_name_change(wxCommandEvent& event);
|
||||
void on_paused(wxCommandEvent& event);
|
||||
void on_canceled(wxCommandEvent& event);
|
||||
|
||||
void set_download_state(int id, DownloadState state);
|
||||
/*
|
||||
bool is_in_state(int id, DownloadState state) const;
|
||||
DownloadState get_download_state(int id) const;
|
||||
bool cancel_download(int id);
|
||||
bool pause_download(int id);
|
||||
bool resume_download(int id);
|
||||
bool delete_download(int id);
|
||||
wxString get_path_of(int id) const;
|
||||
wxString get_folder_path_of(int id) const;
|
||||
*/
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
322
src/slic3r/GUI/DownloaderFileGet.cpp
Normal file
322
src/slic3r/GUI/DownloaderFileGet.cpp
Normal file
|
@ -0,0 +1,322 @@
|
|||
#include "DownloaderFileGet.hpp"
|
||||
|
||||
#include <thread>
|
||||
#include <curl/curl.h>
|
||||
#include <boost/nowide/fstream.hpp>
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <iostream>
|
||||
|
||||
#include "format.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
const size_t DOWNLOAD_MAX_CHUNK_SIZE = 10 * 1024 * 1024;
|
||||
const size_t DOWNLOAD_SIZE_LIMIT = 1024 * 1024 * 1024;
|
||||
|
||||
std::string FileGet::escape_url(const std::string& unescaped)
|
||||
{
|
||||
std::string ret_val;
|
||||
CURL* curl = curl_easy_init();
|
||||
if (curl) {
|
||||
int decodelen;
|
||||
char* decoded = curl_easy_unescape(curl, unescaped.c_str(), unescaped.size(), &decodelen);
|
||||
if (decoded) {
|
||||
ret_val = std::string(decoded);
|
||||
curl_free(decoded);
|
||||
}
|
||||
curl_easy_cleanup(curl);
|
||||
}
|
||||
return ret_val;
|
||||
}
|
||||
namespace {
|
||||
unsigned get_current_pid()
|
||||
{
|
||||
#ifdef WIN32
|
||||
return GetCurrentProcessId();
|
||||
#else
|
||||
return ::getpid();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// int = DOWNLOAD ID; string = file path
|
||||
wxDEFINE_EVENT(EVT_DWNLDR_FILE_COMPLETE, wxCommandEvent);
|
||||
// int = DOWNLOAD ID; string = error msg
|
||||
wxDEFINE_EVENT(EVT_DWNLDR_FILE_ERROR, wxCommandEvent);
|
||||
// int = DOWNLOAD ID; string = progress percent
|
||||
wxDEFINE_EVENT(EVT_DWNLDR_FILE_PROGRESS, wxCommandEvent);
|
||||
// int = DOWNLOAD ID; string = name
|
||||
wxDEFINE_EVENT(EVT_DWNLDR_FILE_NAME_CHANGE, wxCommandEvent);
|
||||
// int = DOWNLOAD ID;
|
||||
wxDEFINE_EVENT(EVT_DWNLDR_FILE_PAUSED, wxCommandEvent);
|
||||
// int = DOWNLOAD ID;
|
||||
wxDEFINE_EVENT(EVT_DWNLDR_FILE_CANCELED, wxCommandEvent);
|
||||
|
||||
struct FileGet::priv
|
||||
{
|
||||
const int m_id;
|
||||
std::string m_url;
|
||||
std::string m_filename;
|
||||
std::thread m_io_thread;
|
||||
wxEvtHandler* m_evt_handler;
|
||||
boost::filesystem::path m_dest_folder;
|
||||
boost::filesystem::path m_tmp_path; // path when ongoing download
|
||||
std::atomic_bool m_cancel { false };
|
||||
std::atomic_bool m_pause { false };
|
||||
std::atomic_bool m_stopped { false }; // either canceled or paused - download is not running
|
||||
size_t m_written { 0 };
|
||||
size_t m_absolute_size { 0 };
|
||||
priv(int ID, std::string&& url, const std::string& filename, wxEvtHandler* evt_handler, const boost::filesystem::path& dest_folder);
|
||||
|
||||
void get_perform();
|
||||
};
|
||||
|
||||
FileGet::priv::priv(int ID, std::string&& url, const std::string& filename, wxEvtHandler* evt_handler, const boost::filesystem::path& dest_folder)
|
||||
: m_id(ID)
|
||||
, m_url(std::move(url))
|
||||
, m_filename(filename)
|
||||
, m_evt_handler(evt_handler)
|
||||
, m_dest_folder(dest_folder)
|
||||
{
|
||||
}
|
||||
|
||||
void FileGet::priv::get_perform()
|
||||
{
|
||||
assert(m_evt_handler);
|
||||
assert(!m_url.empty());
|
||||
assert(!m_filename.empty());
|
||||
assert(boost::filesystem::is_directory(m_dest_folder));
|
||||
|
||||
m_stopped = false;
|
||||
|
||||
// open dest file
|
||||
if (m_written == 0)
|
||||
{
|
||||
boost::filesystem::path dest_path = m_dest_folder / m_filename;
|
||||
std::string extension = boost::filesystem::extension(dest_path);
|
||||
std::string just_filename = m_filename.substr(0, m_filename.size() - extension.size());
|
||||
std::string final_filename = just_filename;
|
||||
|
||||
size_t version = 0;
|
||||
while (boost::filesystem::exists(m_dest_folder / (final_filename + extension)) || boost::filesystem::exists(m_dest_folder / (final_filename + extension + "." + std::to_string(get_current_pid()) + ".download")))
|
||||
{
|
||||
++version;
|
||||
final_filename = just_filename + "(" + std::to_string(version) + ")";
|
||||
}
|
||||
m_filename = final_filename + extension;
|
||||
|
||||
m_tmp_path = m_dest_folder / (m_filename + "." + std::to_string(get_current_pid()) + ".download");
|
||||
|
||||
wxCommandEvent* evt = new wxCommandEvent(EVT_DWNLDR_FILE_NAME_CHANGE);
|
||||
evt->SetString(boost::nowide::widen(m_filename));
|
||||
evt->SetInt(m_id);
|
||||
m_evt_handler->QueueEvent(evt);
|
||||
}
|
||||
|
||||
boost::filesystem::path dest_path = m_dest_folder / m_filename;
|
||||
|
||||
wxString temp_path_wstring(m_tmp_path.wstring());
|
||||
|
||||
std::cout << "dest_path: " << dest_path.string() << std::endl;
|
||||
std::cout << "m_tmp_path: " << m_tmp_path.string() << std::endl;
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << GUI::format("Starting download from %1% to %2%. Temp path is %3%",m_url, dest_path, m_tmp_path);
|
||||
|
||||
FILE* file;
|
||||
// open file for writting
|
||||
if (m_written == 0)
|
||||
file = fopen(temp_path_wstring.c_str(), "wb");
|
||||
else
|
||||
file = fopen(temp_path_wstring.c_str(), "a");
|
||||
|
||||
assert(file != NULL);
|
||||
|
||||
std:: string range_string = std::to_string(m_written) + "-";
|
||||
|
||||
size_t written_previously = m_written;
|
||||
size_t written_this_session = 0;
|
||||
Http::get(m_url)
|
||||
.size_limit(DOWNLOAD_SIZE_LIMIT) //more?
|
||||
.set_range(range_string)
|
||||
.on_progress([&](Http::Progress progress, bool& cancel) {
|
||||
// to prevent multiple calls into following ifs (m_cancel / m_pause)
|
||||
if (m_stopped){
|
||||
cancel = true;
|
||||
return;
|
||||
}
|
||||
if (m_cancel) {
|
||||
m_stopped = true;
|
||||
fclose(file);
|
||||
// remove canceled file
|
||||
std::remove(m_tmp_path.string().c_str());
|
||||
m_written = 0;
|
||||
cancel = true;
|
||||
wxCommandEvent* evt = new wxCommandEvent(EVT_DWNLDR_FILE_CANCELED);
|
||||
evt->SetInt(m_id);
|
||||
m_evt_handler->QueueEvent(evt);
|
||||
return;
|
||||
// TODO: send canceled event?
|
||||
}
|
||||
if (m_pause) {
|
||||
m_stopped = true;
|
||||
fclose(file);
|
||||
cancel = true;
|
||||
wxCommandEvent* evt = new wxCommandEvent(EVT_DWNLDR_FILE_PAUSED);
|
||||
evt->SetInt(m_id);
|
||||
m_evt_handler->QueueEvent(evt);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_absolute_size < progress.dltotal) {
|
||||
m_absolute_size = progress.dltotal;
|
||||
}
|
||||
|
||||
if (progress.dlnow != 0) {
|
||||
if (progress.dlnow - written_this_session > DOWNLOAD_MAX_CHUNK_SIZE || progress.dlnow == progress.dltotal) {
|
||||
try
|
||||
{
|
||||
std::string part_for_write = progress.buffer.substr(written_this_session, progress.dlnow);
|
||||
fwrite(part_for_write.c_str(), 1, part_for_write.size(), file);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
// fclose(file); do it?
|
||||
wxCommandEvent* evt = new wxCommandEvent(EVT_DWNLDR_FILE_ERROR);
|
||||
evt->SetString(e.what());
|
||||
evt->SetInt(m_id);
|
||||
m_evt_handler->QueueEvent(evt);
|
||||
cancel = true;
|
||||
return;
|
||||
}
|
||||
written_this_session = progress.dlnow;
|
||||
m_written = written_previously + written_this_session;
|
||||
}
|
||||
wxCommandEvent* evt = new wxCommandEvent(EVT_DWNLDR_FILE_PROGRESS);
|
||||
int percent_total = (written_previously + progress.dlnow) * 100 / m_absolute_size;
|
||||
evt->SetString(std::to_string(percent_total));
|
||||
evt->SetInt(m_id);
|
||||
m_evt_handler->QueueEvent(evt);
|
||||
}
|
||||
|
||||
})
|
||||
.on_error([&](std::string body, std::string error, unsigned http_status) {
|
||||
if (file != NULL)
|
||||
fclose(file);
|
||||
wxCommandEvent* evt = new wxCommandEvent(EVT_DWNLDR_FILE_ERROR);
|
||||
evt->SetString(error);
|
||||
evt->SetInt(m_id);
|
||||
m_evt_handler->QueueEvent(evt);
|
||||
})
|
||||
.on_complete([&](std::string body, unsigned /* http_status */) {
|
||||
|
||||
// TODO: perform a body size check
|
||||
//
|
||||
//size_t body_size = body.size();
|
||||
//if (body_size != expected_size) {
|
||||
// return;
|
||||
//}
|
||||
try
|
||||
{
|
||||
/*
|
||||
if (m_written < body.size())
|
||||
{
|
||||
// this code should never be entered. As there should be on_progress call after last bit downloaded.
|
||||
std::string part_for_write = body.substr(m_written);
|
||||
fwrite(part_for_write.c_str(), 1, part_for_write.size(), file);
|
||||
}
|
||||
*/
|
||||
fclose(file);
|
||||
boost::filesystem::rename(m_tmp_path, dest_path);
|
||||
}
|
||||
catch (const std::exception& /*e*/)
|
||||
{
|
||||
//TODO: report?
|
||||
//error_message = GUI::format("Failed to write and move %1% to %2%", tmp_path, dest_path);
|
||||
wxCommandEvent* evt = new wxCommandEvent(EVT_DWNLDR_FILE_ERROR);
|
||||
evt->SetString("Failed to write and move.");
|
||||
evt->SetInt(m_id);
|
||||
m_evt_handler->QueueEvent(evt);
|
||||
return;
|
||||
}
|
||||
|
||||
wxCommandEvent* evt = new wxCommandEvent(EVT_DWNLDR_FILE_COMPLETE);
|
||||
evt->SetString(dest_path.wstring());
|
||||
evt->SetInt(m_id);
|
||||
m_evt_handler->QueueEvent(evt);
|
||||
})
|
||||
.perform_sync();
|
||||
|
||||
}
|
||||
|
||||
FileGet::FileGet(int ID, std::string url, const std::string& filename, wxEvtHandler* evt_handler, const boost::filesystem::path& dest_folder)
|
||||
: p(new priv(ID, std::move(url), filename, evt_handler, dest_folder))
|
||||
{}
|
||||
|
||||
FileGet::FileGet(FileGet&& other) : p(std::move(other.p)) {}
|
||||
|
||||
FileGet::~FileGet()
|
||||
{
|
||||
if (p && p->m_io_thread.joinable()) {
|
||||
p->m_cancel = true;
|
||||
p->m_io_thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
void FileGet::get()
|
||||
{
|
||||
assert(p);
|
||||
if (p->m_io_thread.joinable()) {
|
||||
// This will stop transfers being done by the thread, if any.
|
||||
// Cancelling takes some time, but should complete soon enough.
|
||||
p->m_cancel = true;
|
||||
p->m_io_thread.join();
|
||||
}
|
||||
p->m_cancel = false;
|
||||
p->m_pause = false;
|
||||
p->m_io_thread = std::thread([this]() {
|
||||
p->get_perform();
|
||||
});
|
||||
}
|
||||
|
||||
void FileGet::cancel()
|
||||
{
|
||||
if(p && p->m_stopped) {
|
||||
if (p->m_io_thread.joinable()) {
|
||||
p->m_cancel = true;
|
||||
p->m_io_thread.join();
|
||||
wxCommandEvent* evt = new wxCommandEvent(EVT_DWNLDR_FILE_CANCELED);
|
||||
evt->SetInt(p->m_id);
|
||||
p->m_evt_handler->QueueEvent(evt);
|
||||
}
|
||||
}
|
||||
|
||||
if (p)
|
||||
p->m_cancel = true;
|
||||
|
||||
}
|
||||
|
||||
void FileGet::pause()
|
||||
{
|
||||
if (p) {
|
||||
p->m_pause = true;
|
||||
}
|
||||
}
|
||||
void FileGet::resume()
|
||||
{
|
||||
assert(p);
|
||||
if (p->m_io_thread.joinable()) {
|
||||
// This will stop transfers being done by the thread, if any.
|
||||
// Cancelling takes some time, but should complete soon enough.
|
||||
p->m_cancel = true;
|
||||
p->m_io_thread.join();
|
||||
}
|
||||
p->m_cancel = false;
|
||||
p->m_pause = false;
|
||||
p->m_io_thread = std::thread([this]() {
|
||||
p->get_perform();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
44
src/slic3r/GUI/DownloaderFileGet.hpp
Normal file
44
src/slic3r/GUI/DownloaderFileGet.hpp
Normal file
|
@ -0,0 +1,44 @@
|
|||
#ifndef slic3r_DownloaderFileGet_hpp_
|
||||
#define slic3r_DownloaderFileGet_hpp_
|
||||
|
||||
#include "../Utils/Http.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <wx/event.h>
|
||||
#include <wx/frame.h>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
class FileGet : public std::enable_shared_from_this<FileGet> {
|
||||
private:
|
||||
struct priv;
|
||||
public:
|
||||
FileGet(int ID, std::string url, const std::string& filename, wxEvtHandler* evt_handler,const boost::filesystem::path& dest_folder);
|
||||
FileGet(FileGet&& other);
|
||||
~FileGet();
|
||||
|
||||
void get();
|
||||
void cancel();
|
||||
void pause();
|
||||
void resume();
|
||||
static std::string escape_url(const std::string& url);
|
||||
private:
|
||||
std::unique_ptr<priv> p;
|
||||
};
|
||||
// int = DOWNLOAD ID; string = file path
|
||||
wxDECLARE_EVENT(EVT_DWNLDR_FILE_COMPLETE, wxCommandEvent);
|
||||
// int = DOWNLOAD ID; string = error msg
|
||||
wxDECLARE_EVENT(EVT_DWNLDR_FILE_PROGRESS, wxCommandEvent);
|
||||
// int = DOWNLOAD ID; string = progress percent
|
||||
wxDECLARE_EVENT(EVT_DWNLDR_FILE_ERROR, wxCommandEvent);
|
||||
// int = DOWNLOAD ID; string = name
|
||||
wxDECLARE_EVENT(EVT_DWNLDR_FILE_NAME_CHANGE, wxCommandEvent);
|
||||
// int = DOWNLOAD ID;
|
||||
wxDECLARE_EVENT(EVT_DWNLDR_FILE_PAUSED, wxCommandEvent);
|
||||
// int = DOWNLOAD ID;
|
||||
wxDECLARE_EVENT(EVT_DWNLDR_FILE_CANCELED, wxCommandEvent);
|
||||
}
|
||||
}
|
||||
#endif
|
370
src/slic3r/GUI/FileArchiveDialog.cpp
Normal file
370
src/slic3r/GUI/FileArchiveDialog.cpp
Normal file
|
@ -0,0 +1,370 @@
|
|||
#include "FileArchiveDialog.hpp"
|
||||
|
||||
#include "I18N.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "GUI.hpp"
|
||||
#include "MainFrame.hpp"
|
||||
#include "ExtraRenderers.hpp"
|
||||
#include "format.hpp"
|
||||
#include <regex>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/nowide/convert.hpp>
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
ArchiveViewModel::ArchiveViewModel(wxWindow* parent)
|
||||
:m_parent(parent)
|
||||
{}
|
||||
ArchiveViewModel::~ArchiveViewModel()
|
||||
{}
|
||||
|
||||
std::shared_ptr<ArchiveViewNode> ArchiveViewModel::AddFile(std::shared_ptr<ArchiveViewNode> parent, const wxString& name, bool container)
|
||||
{
|
||||
std::shared_ptr<ArchiveViewNode> node = std::make_shared<ArchiveViewNode>(ArchiveViewNode(name));
|
||||
node->set_container(container);
|
||||
|
||||
if (parent.get() != nullptr) {
|
||||
parent->get_children().push_back(node);
|
||||
node->set_parent(parent);
|
||||
parent->set_is_folder(true);
|
||||
} else {
|
||||
m_top_children.emplace_back(node);
|
||||
}
|
||||
|
||||
wxDataViewItem child = wxDataViewItem((void*)node.get());
|
||||
wxDataViewItem parent_item= wxDataViewItem((void*)parent.get());
|
||||
ItemAdded(parent_item, child);
|
||||
|
||||
if (parent)
|
||||
m_ctrl->Expand(parent_item);
|
||||
return node;
|
||||
}
|
||||
|
||||
wxString ArchiveViewModel::GetColumnType(unsigned int col) const
|
||||
{
|
||||
if (col == 0)
|
||||
return "bool";
|
||||
return "string";//"DataViewBitmapText";
|
||||
}
|
||||
|
||||
void ArchiveViewModel::Rescale()
|
||||
{
|
||||
// There should be no pictures rendered
|
||||
}
|
||||
|
||||
void ArchiveViewModel::Delete(const wxDataViewItem& item)
|
||||
{
|
||||
assert(item.IsOk());
|
||||
ArchiveViewNode* node = static_cast<ArchiveViewNode*>(item.GetID());
|
||||
assert(node->get_parent() != nullptr);
|
||||
for (std::shared_ptr<ArchiveViewNode> child : node->get_children())
|
||||
{
|
||||
Delete(wxDataViewItem((void*)child.get()));
|
||||
}
|
||||
delete [] node;
|
||||
}
|
||||
void ArchiveViewModel::Clear()
|
||||
{
|
||||
}
|
||||
|
||||
wxDataViewItem ArchiveViewModel::GetParent(const wxDataViewItem& item) const
|
||||
{
|
||||
assert(item.IsOk());
|
||||
ArchiveViewNode* node = static_cast<ArchiveViewNode*>(item.GetID());
|
||||
return wxDataViewItem((void*)node->get_parent().get());
|
||||
}
|
||||
unsigned int ArchiveViewModel::GetChildren(const wxDataViewItem& parent, wxDataViewItemArray& array) const
|
||||
{
|
||||
if (!parent.IsOk()) {
|
||||
for (std::shared_ptr<ArchiveViewNode>child : m_top_children) {
|
||||
array.push_back(wxDataViewItem((void*)child.get()));
|
||||
}
|
||||
return m_top_children.size();
|
||||
}
|
||||
|
||||
ArchiveViewNode* node = static_cast<ArchiveViewNode*>(parent.GetID());
|
||||
for (std::shared_ptr<ArchiveViewNode> child : node->get_children()) {
|
||||
array.push_back(wxDataViewItem((void*)child.get()));
|
||||
}
|
||||
return node->get_children().size();
|
||||
}
|
||||
|
||||
void ArchiveViewModel::GetValue(wxVariant& variant, const wxDataViewItem& item, unsigned int col) const
|
||||
{
|
||||
assert(item.IsOk());
|
||||
ArchiveViewNode* node = static_cast<ArchiveViewNode*>(item.GetID());
|
||||
if (col == 0) {
|
||||
variant = node->get_toggle();
|
||||
} else {
|
||||
variant = node->get_name();
|
||||
}
|
||||
}
|
||||
|
||||
void ArchiveViewModel::untoggle_folders(const wxDataViewItem& item)
|
||||
{
|
||||
assert(item.IsOk());
|
||||
ArchiveViewNode* node = static_cast<ArchiveViewNode*>(item.GetID());
|
||||
node->set_toggle(false);
|
||||
if (node->get_parent().get() != nullptr)
|
||||
untoggle_folders(wxDataViewItem((void*)node->get_parent().get()));
|
||||
}
|
||||
|
||||
bool ArchiveViewModel::SetValue(const wxVariant& variant, const wxDataViewItem& item, unsigned int col)
|
||||
{
|
||||
assert(item.IsOk());
|
||||
ArchiveViewNode* node = static_cast<ArchiveViewNode*>(item.GetID());
|
||||
if (col == 0) {
|
||||
node->set_toggle(variant.GetBool());
|
||||
// if folder recursivelly check all children
|
||||
for (std::shared_ptr<ArchiveViewNode> child : node->get_children()) {
|
||||
SetValue(variant, wxDataViewItem((void*)child.get()), col);
|
||||
}
|
||||
if(!variant.GetBool() && node->get_parent())
|
||||
untoggle_folders(wxDataViewItem((void*)node->get_parent().get()));
|
||||
} else {
|
||||
node->set_name(variant.GetString());
|
||||
}
|
||||
m_parent->Refresh();
|
||||
return true;
|
||||
}
|
||||
bool ArchiveViewModel::IsEnabled(const wxDataViewItem& item, unsigned int col) const
|
||||
{
|
||||
// As of now, all items are always enabled.
|
||||
// Returning false for col 1 would gray out text.
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ArchiveViewModel::IsContainer(const wxDataViewItem& item) const
|
||||
{
|
||||
if(!item.IsOk())
|
||||
return true;
|
||||
ArchiveViewNode* node = static_cast<ArchiveViewNode*>(item.GetID());
|
||||
return node->is_container();
|
||||
}
|
||||
|
||||
ArchiveViewCtrl::ArchiveViewCtrl(wxWindow* parent, wxSize size)
|
||||
: wxDataViewCtrl(parent, wxID_ANY, wxDefaultPosition, size, wxDV_VARIABLE_LINE_HEIGHT | wxDV_ROW_LINES
|
||||
#ifdef _WIN32
|
||||
| wxBORDER_SIMPLE
|
||||
#endif
|
||||
)
|
||||
//, m_em_unit(em_unit(parent))
|
||||
{
|
||||
wxGetApp().UpdateDVCDarkUI(this);
|
||||
|
||||
m_model = new ArchiveViewModel(parent);
|
||||
this->AssociateModel(m_model);
|
||||
m_model->SetAssociatedControl(this);
|
||||
}
|
||||
|
||||
ArchiveViewCtrl::~ArchiveViewCtrl()
|
||||
{
|
||||
if (m_model) {
|
||||
m_model->Clear();
|
||||
m_model->DecRef();
|
||||
}
|
||||
}
|
||||
|
||||
FileArchiveDialog::FileArchiveDialog(wxWindow* parent_window, mz_zip_archive* archive, std::vector<boost::filesystem::path>& selected_paths)
|
||||
: DPIDialog(parent_window, wxID_ANY, _(L("Archive preview")), wxDefaultPosition,
|
||||
wxSize(45 * wxGetApp().em_unit(), 40 * wxGetApp().em_unit()),
|
||||
wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxMAXIMIZE_BOX)
|
||||
, m_selected_paths (selected_paths)
|
||||
{
|
||||
int em = em_unit();
|
||||
|
||||
wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
|
||||
m_avc = new ArchiveViewCtrl(this, wxSize(60 * em, 30 * em));
|
||||
m_avc->AppendToggleColumn(L"\u2714", 0, wxDATAVIEW_CELL_ACTIVATABLE, 6 * em);
|
||||
m_avc->AppendTextColumn("filename", 1);
|
||||
|
||||
|
||||
std::vector<std::shared_ptr<ArchiveViewNode>> stack;
|
||||
|
||||
std::function<void(std::vector<std::shared_ptr<ArchiveViewNode> >&, size_t)> reduce_stack = [] (std::vector<std::shared_ptr<ArchiveViewNode>>& stack, size_t size) {
|
||||
if (size == 0) {
|
||||
stack.clear();
|
||||
return;
|
||||
}
|
||||
while (stack.size() > size)
|
||||
stack.pop_back();
|
||||
};
|
||||
// recursively stores whole structure of file onto function stack and synchoronize with stack object.
|
||||
std::function<size_t(const boost::filesystem::path&, std::vector<std::shared_ptr<ArchiveViewNode>>&)> adjust_stack = [&adjust_stack, &reduce_stack, &avc = m_avc](const boost::filesystem::path& const_file, std::vector<std::shared_ptr<ArchiveViewNode>>& stack)->size_t {
|
||||
boost::filesystem::path file(const_file);
|
||||
size_t struct_size = file.has_parent_path() ? adjust_stack(file.parent_path(), stack) : 0;
|
||||
|
||||
if (stack.size() > struct_size && (file.has_extension() || file.filename().string() != stack[struct_size]->get_name()))
|
||||
{
|
||||
reduce_stack(stack, struct_size);
|
||||
}
|
||||
if (!file.has_extension() && stack.size() == struct_size)
|
||||
stack.push_back(avc->get_model()->AddFile((stack.empty() ? std::shared_ptr<ArchiveViewNode>(nullptr) : stack.back()), GUI::format_wxstr(file.filename().string()), true)); // filename string to wstring?
|
||||
return struct_size + 1;
|
||||
};
|
||||
|
||||
const std::regex pattern_drop(".*[.](stl|obj|amf|3mf|prusa|step|stp)", std::regex::icase);
|
||||
mz_uint num_entries = mz_zip_reader_get_num_files(archive);
|
||||
mz_zip_archive_file_stat stat;
|
||||
std::vector<boost::filesystem::path> filtered_entries;
|
||||
for (mz_uint i = 0; i < num_entries; ++i) {
|
||||
if (mz_zip_reader_file_stat(archive, i, &stat)) {
|
||||
std::string extra(1024, 0);
|
||||
boost::filesystem::path path;
|
||||
size_t extra_size = mz_zip_reader_get_filename_from_extra(archive, i, extra.data(), extra.size());
|
||||
if (extra_size > 0) {
|
||||
path = boost::filesystem::path(extra.substr(0, extra_size));
|
||||
} else {
|
||||
wxString wname = boost::nowide::widen(stat.m_filename);
|
||||
std::string name = GUI::format(wname);
|
||||
path = boost::filesystem::path(name);
|
||||
}
|
||||
assert(!path.empty());
|
||||
if (!path.has_extension())
|
||||
continue;
|
||||
// filter out MACOS specific hidden files
|
||||
if (boost::algorithm::starts_with(path.string(), "__MACOSX"))
|
||||
continue;
|
||||
filtered_entries.emplace_back(std::move(path));
|
||||
}
|
||||
}
|
||||
// sorting files will help adjust_stack function to not create multiple same folders
|
||||
std::sort(filtered_entries.begin(), filtered_entries.end(), [](const boost::filesystem::path& p1, const boost::filesystem::path& p2){ return p1.string() > p2.string(); });
|
||||
for (const boost::filesystem::path& path : filtered_entries)
|
||||
{
|
||||
std::shared_ptr<ArchiveViewNode> parent(nullptr);
|
||||
|
||||
adjust_stack(path, stack);
|
||||
if (!stack.empty())
|
||||
parent = stack.back();
|
||||
if (std::regex_match(path.extension().string(), pattern_drop)) { // this leaves out non-compatible files
|
||||
m_avc->get_model()->AddFile(parent, GUI::format_wxstr(path.filename().string()), false)->set_fullpath(/*std::move(path)*/path); // filename string to wstring?
|
||||
}
|
||||
}
|
||||
wxBoxSizer* btn_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
wxButton* btn_all = new wxButton(this, wxID_ANY, "All");
|
||||
btn_all->Bind(wxEVT_BUTTON, [this](wxCommandEvent& evt) { on_all_button(); });
|
||||
btn_sizer->Add(btn_all, 0, wxLeft);
|
||||
|
||||
wxButton* btn_none = new wxButton(this, wxID_ANY, "None");
|
||||
btn_none->Bind(wxEVT_BUTTON, [this](wxCommandEvent& evt) { on_none_button(); });
|
||||
btn_sizer->Add(btn_none, 0, wxLeft);
|
||||
|
||||
btn_sizer->AddStretchSpacer();
|
||||
wxButton* btn_run = new wxButton(this, wxID_OK, "Open");
|
||||
btn_run->Bind(wxEVT_BUTTON, [this](wxCommandEvent& evt) { on_open_button(); });
|
||||
btn_sizer->Add(btn_run, 0, wxRIGHT);
|
||||
|
||||
wxButton* cancel_btn = new wxButton(this, wxID_CANCEL, "Cancel");
|
||||
cancel_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent& evt) { this->EndModal(wxID_CANCEL); });
|
||||
btn_sizer->Add(cancel_btn, 0, wxRIGHT);
|
||||
|
||||
topSizer->Add(m_avc, 1, wxEXPAND | wxALL, 10);
|
||||
topSizer->Add(btn_sizer, 0, wxEXPAND | wxALL, 10);
|
||||
this->SetMinSize(wxSize(80 * em, 30 * em));
|
||||
this->SetSizer(topSizer);
|
||||
}
|
||||
|
||||
void FileArchiveDialog::on_dpi_changed(const wxRect& suggested_rect)
|
||||
{
|
||||
int em = em_unit();
|
||||
BOOST_LOG_TRIVIAL(error) << "on_dpi_changed";
|
||||
//msw_buttons_rescale(this, em, { wxID_CANCEL, m_save_btn_id, m_move_btn_id, m_continue_btn_id });
|
||||
//for (auto btn : { m_save_btn, m_transfer_btn, m_discard_btn })
|
||||
// if (btn) btn->msw_rescale();
|
||||
|
||||
const wxSize& size = wxSize(70 * em, 30 * em);
|
||||
SetMinSize(size);
|
||||
|
||||
//m_tree->Rescale(em);
|
||||
|
||||
Fit();
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void FileArchiveDialog::on_open_button()
|
||||
{
|
||||
wxDataViewItemArray top_items;
|
||||
m_avc->get_model()->GetChildren(wxDataViewItem(nullptr), top_items);
|
||||
|
||||
std::function<void(ArchiveViewNode*)> deep_fill = [&paths = m_selected_paths, &deep_fill](ArchiveViewNode* node){
|
||||
if (node == nullptr)
|
||||
return;
|
||||
if (node->get_children().empty()) {
|
||||
if (node->get_toggle())
|
||||
paths.emplace_back(node->get_fullpath());
|
||||
} else {
|
||||
for (std::shared_ptr<ArchiveViewNode> child : node->get_children())
|
||||
deep_fill(child.get());
|
||||
}
|
||||
};
|
||||
|
||||
for (const auto& item : top_items)
|
||||
{
|
||||
ArchiveViewNode* node = static_cast<ArchiveViewNode*>(item.GetID());
|
||||
deep_fill(node);
|
||||
}
|
||||
this->EndModal(wxID_OK);
|
||||
}
|
||||
|
||||
void FileArchiveDialog::on_all_button()
|
||||
{
|
||||
|
||||
wxDataViewItemArray top_items;
|
||||
m_avc->get_model()->GetChildren(wxDataViewItem(nullptr), top_items);
|
||||
|
||||
std::function<void(ArchiveViewNode*)> deep_fill = [&deep_fill](ArchiveViewNode* node) {
|
||||
if (node == nullptr)
|
||||
return;
|
||||
node->set_toggle(true);
|
||||
if (!node->get_children().empty()) {
|
||||
for (std::shared_ptr<ArchiveViewNode> child : node->get_children())
|
||||
deep_fill(child.get());
|
||||
}
|
||||
};
|
||||
|
||||
for (const auto& item : top_items)
|
||||
{
|
||||
ArchiveViewNode* node = static_cast<ArchiveViewNode*>(item.GetID());
|
||||
deep_fill(node);
|
||||
// Fix for linux, where Refresh or Update wont help to redraw toggle checkboxes.
|
||||
// It should be enough to call ValueChanged for top items.
|
||||
m_avc->get_model()->ValueChanged(item, 0);
|
||||
}
|
||||
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void FileArchiveDialog::on_none_button()
|
||||
{
|
||||
wxDataViewItemArray top_items;
|
||||
m_avc->get_model()->GetChildren(wxDataViewItem(nullptr), top_items);
|
||||
|
||||
std::function<void(ArchiveViewNode*)> deep_fill = [&deep_fill](ArchiveViewNode* node) {
|
||||
if (node == nullptr)
|
||||
return;
|
||||
node->set_toggle(false);
|
||||
if (!node->get_children().empty()) {
|
||||
for (std::shared_ptr<ArchiveViewNode> child : node->get_children())
|
||||
deep_fill(child.get());
|
||||
}
|
||||
};
|
||||
|
||||
for (const auto& item : top_items)
|
||||
{
|
||||
ArchiveViewNode* node = static_cast<ArchiveViewNode*>(item.GetID());
|
||||
deep_fill(node);
|
||||
// Fix for linux, where Refresh or Update wont help to redraw toggle checkboxes.
|
||||
// It should be enough to call ValueChanged for top items.
|
||||
m_avc->get_model()->ValueChanged(item, 0);
|
||||
}
|
||||
|
||||
this->Refresh();
|
||||
}
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
118
src/slic3r/GUI/FileArchiveDialog.hpp
Normal file
118
src/slic3r/GUI/FileArchiveDialog.hpp
Normal file
|
@ -0,0 +1,118 @@
|
|||
#ifndef slic3r_GUI_FileArchiveDialog_hpp_
|
||||
#define slic3r_GUI_FileArchiveDialog_hpp_
|
||||
|
||||
#include "GUI_Utils.hpp"
|
||||
#include "libslic3r/miniz_extension.hpp"
|
||||
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <wx/wx.h>
|
||||
#include <wx/dataview.h>
|
||||
#include "wxExtensions.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
class ArchiveViewCtrl;
|
||||
|
||||
class ArchiveViewNode
|
||||
{
|
||||
public:
|
||||
ArchiveViewNode(const wxString& name) : m_name(name) {}
|
||||
|
||||
std::vector<std::shared_ptr<ArchiveViewNode>>& get_children() { return m_children; }
|
||||
void set_parent(std::shared_ptr<ArchiveViewNode> parent) { m_parent = parent; }
|
||||
// On Linux, get_parent cannot just return size of m_children. ItemAdded would than crash.
|
||||
std::shared_ptr<ArchiveViewNode> get_parent() const { return m_parent; }
|
||||
bool is_container() const { return m_container; }
|
||||
void set_container(bool is_container) { m_container = is_container; }
|
||||
wxString get_name() const { return m_name; }
|
||||
void set_name(const wxString& name) { m_name = name; }
|
||||
bool get_toggle() const { return m_toggle; }
|
||||
void set_toggle(bool toggle) { m_toggle = toggle; }
|
||||
bool get_is_folder() const { return m_folder; }
|
||||
void set_is_folder(bool is_folder) { m_folder = is_folder; }
|
||||
void set_fullpath(boost::filesystem::path path) { m_fullpath = path; }
|
||||
boost::filesystem::path get_fullpath() const { return m_fullpath; }
|
||||
|
||||
private:
|
||||
wxString m_name;
|
||||
std::shared_ptr<ArchiveViewNode> m_parent { nullptr };
|
||||
std::vector<std::shared_ptr<ArchiveViewNode>> m_children;
|
||||
|
||||
bool m_toggle { false };
|
||||
bool m_folder { false };
|
||||
boost::filesystem::path m_fullpath;
|
||||
bool m_container { false };
|
||||
};
|
||||
|
||||
class ArchiveViewModel : public wxDataViewModel
|
||||
{
|
||||
public:
|
||||
ArchiveViewModel(wxWindow* parent);
|
||||
~ArchiveViewModel();
|
||||
|
||||
/* wxDataViewItem AddFolder(wxDataViewItem& parent, wxString name);
|
||||
wxDataViewItem AddFile(wxDataViewItem& parent, wxString name);*/
|
||||
|
||||
std::shared_ptr<ArchiveViewNode> AddFile(std::shared_ptr<ArchiveViewNode> parent,const wxString& name, bool container);
|
||||
|
||||
wxString GetColumnType(unsigned int col) const override;
|
||||
unsigned int GetColumnCount() const override { return 2; }
|
||||
|
||||
void Rescale();
|
||||
void Delete(const wxDataViewItem& item);
|
||||
void Clear();
|
||||
|
||||
wxDataViewItem GetParent(const wxDataViewItem& item) const override;
|
||||
unsigned int GetChildren(const wxDataViewItem& parent, wxDataViewItemArray& array) const override;
|
||||
|
||||
void SetAssociatedControl(ArchiveViewCtrl* ctrl) { m_ctrl = ctrl; }
|
||||
|
||||
void GetValue(wxVariant& variant, const wxDataViewItem& item, unsigned int col) const override;
|
||||
bool SetValue(const wxVariant& variant, const wxDataViewItem& item, unsigned int col) override;
|
||||
|
||||
void untoggle_folders(const wxDataViewItem& item);
|
||||
|
||||
bool IsEnabled(const wxDataViewItem& item, unsigned int col) const override;
|
||||
bool IsContainer(const wxDataViewItem& item) const override;
|
||||
// Is the container just a header or an item with all columns
|
||||
// In our case it is an item with all columns
|
||||
bool HasContainerColumns(const wxDataViewItem& WXUNUSED(item)) const override { return true; }
|
||||
|
||||
protected:
|
||||
wxWindow* m_parent { nullptr };
|
||||
ArchiveViewCtrl* m_ctrl { nullptr };
|
||||
std::vector<std::shared_ptr<ArchiveViewNode>> m_top_children;
|
||||
};
|
||||
|
||||
class ArchiveViewCtrl : public wxDataViewCtrl
|
||||
{
|
||||
public:
|
||||
ArchiveViewCtrl(wxWindow* parent, wxSize size);
|
||||
~ArchiveViewCtrl();
|
||||
|
||||
ArchiveViewModel* get_model() const {return m_model; }
|
||||
protected:
|
||||
ArchiveViewModel* m_model;
|
||||
};
|
||||
|
||||
|
||||
class FileArchiveDialog : public DPIDialog
|
||||
{
|
||||
public:
|
||||
FileArchiveDialog(wxWindow* parent_window, mz_zip_archive* archive, std::vector<boost::filesystem::path>& selected_paths);
|
||||
|
||||
protected:
|
||||
void on_dpi_changed(const wxRect& suggested_rect) override;
|
||||
|
||||
void on_open_button();
|
||||
void on_all_button();
|
||||
void on_none_button();
|
||||
|
||||
std::vector<boost::filesystem::path>& m_selected_paths;
|
||||
ArchiveViewCtrl* m_avc;
|
||||
};
|
||||
|
||||
} // namespace GU
|
||||
} // namespace Slic3r
|
||||
#endif // slic3r_GUI_FileArchiveDialog_hpp_
|
File diff suppressed because it is too large
Load diff
|
@ -46,6 +46,7 @@ class ObjectList;
|
|||
class ObjectLayers;
|
||||
class Plater;
|
||||
class NotificationManager;
|
||||
class Downloader;
|
||||
struct GUI_InitParams;
|
||||
class GalleryDialog;
|
||||
|
||||
|
@ -165,6 +166,7 @@ private:
|
|||
std::unique_ptr <OtherInstanceMessageHandler> m_other_instance_message_handler;
|
||||
std::unique_ptr <AppUpdater> m_app_updater;
|
||||
std::unique_ptr <wxSingleInstanceChecker> m_single_instance_checker;
|
||||
std::unique_ptr <Downloader> m_downloader;
|
||||
std::string m_instance_hash_string;
|
||||
size_t m_instance_hash_int;
|
||||
|
||||
|
@ -292,6 +294,7 @@ public:
|
|||
void OSXStoreOpenFiles(const wxArrayString &files) override;
|
||||
// wxWidgets override to get an event on open files.
|
||||
void MacOpenFiles(const wxArrayString &fileNames) override;
|
||||
void MacOpenURL(const wxString& url) override;
|
||||
#endif /* __APPLE */
|
||||
|
||||
Sidebar& sidebar();
|
||||
|
@ -304,6 +307,7 @@ public:
|
|||
Model& model();
|
||||
NotificationManager * notification_manager();
|
||||
GalleryDialog * gallery_dialog();
|
||||
Downloader* downloader();
|
||||
|
||||
// Parameters extracted from the command line to be passed to GUI after initialization.
|
||||
GUI_InitParams* init_params { nullptr };
|
||||
|
@ -358,6 +362,10 @@ public:
|
|||
void associate_gcode_files();
|
||||
#endif // __WXMSW__
|
||||
|
||||
|
||||
// URL download - PrusaSlicer gets system call to open prusaslicer:// URL which should contain address of download
|
||||
void start_download(std::string url);
|
||||
|
||||
private:
|
||||
bool on_init_inner();
|
||||
void init_app_config();
|
||||
|
@ -380,6 +388,7 @@ private:
|
|||
void app_version_check(bool from_user);
|
||||
|
||||
bool m_datadir_redefined { false };
|
||||
|
||||
};
|
||||
|
||||
DECLARE_APP(GUI_App)
|
||||
|
|
|
@ -30,6 +30,9 @@ struct GUI_InitParams
|
|||
std::vector<std::string> input_files;
|
||||
|
||||
bool start_as_gcodeviewer;
|
||||
bool start_downloader;
|
||||
bool delete_after_load;
|
||||
std::string download_url;
|
||||
#if ENABLE_GL_CORE_PROFILE
|
||||
std::pair<int, int> opengl_version;
|
||||
#if ENABLE_OPENGL_DEBUG_OPTION
|
||||
|
|
|
@ -96,7 +96,12 @@ static const std::map<const wchar_t, std::string> font_icons_large = {
|
|||
{ImGui::DocumentationButton , "notification_documentation" },
|
||||
{ImGui::DocumentationHoverButton, "notification_documentation_hover"},
|
||||
{ImGui::InfoMarker , "notification_info" },
|
||||
|
||||
{ImGui::PlayButton , "notification_play" },
|
||||
{ImGui::PlayHoverButton , "notification_play_hover" },
|
||||
{ImGui::PauseButton , "notification_pause" },
|
||||
{ImGui::PauseHoverButton , "notification_pause_hover" },
|
||||
{ImGui::OpenButton , "notification_open" },
|
||||
{ImGui::OpenHoverButton , "notification_open_hover" },
|
||||
};
|
||||
|
||||
static const std::map<const wchar_t, std::string> font_icons_extra_large = {
|
||||
|
|
|
@ -373,6 +373,7 @@ bool instance_check(int argc, char** argv, bool app_config_single_instance)
|
|||
namespace GUI {
|
||||
|
||||
wxDEFINE_EVENT(EVT_LOAD_MODEL_OTHER_INSTANCE, LoadFromOtherInstanceEvent);
|
||||
wxDEFINE_EVENT(EVT_START_DOWNLOAD_OTHER_INSTANCE, StartDownloadOtherInstanceEvent);
|
||||
wxDEFINE_EVENT(EVT_INSTANCE_GO_TO_FRONT, InstanceGoToFrontEvent);
|
||||
|
||||
void OtherInstanceMessageHandler::init(wxEvtHandler* callback_evt_handler)
|
||||
|
@ -501,12 +502,20 @@ void OtherInstanceMessageHandler::handle_message(const std::string& message)
|
|||
}
|
||||
|
||||
std::vector<boost::filesystem::path> paths;
|
||||
std::vector<std::string> downloads;
|
||||
// Skip the first argument, it is the path to the slicer executable.
|
||||
auto it = args.begin();
|
||||
for (++ it; it != args.end(); ++ it) {
|
||||
boost::filesystem::path p = MessageHandlerInternal::get_path(*it);
|
||||
if (! p.string().empty())
|
||||
paths.emplace_back(p);
|
||||
// TODO: There is a misterious slash appearing in recieved msg on windows
|
||||
#ifdef _WIN32
|
||||
else if (it->rfind("prusaslicer://open/?file=", 0) == 0)
|
||||
#else
|
||||
else if (it->rfind("prusaslicer://open?file=", 0) == 0)
|
||||
#endif
|
||||
downloads.emplace_back(*it);
|
||||
}
|
||||
if (! paths.empty()) {
|
||||
//wxEvtHandler* evt_handler = wxGetApp().plater(); //assert here?
|
||||
|
@ -514,6 +523,10 @@ void OtherInstanceMessageHandler::handle_message(const std::string& message)
|
|||
wxPostEvent(m_callback_evt_handler, LoadFromOtherInstanceEvent(GUI::EVT_LOAD_MODEL_OTHER_INSTANCE, std::vector<boost::filesystem::path>(std::move(paths))));
|
||||
//}
|
||||
}
|
||||
if (!downloads.empty())
|
||||
{
|
||||
wxPostEvent(m_callback_evt_handler, StartDownloadOtherInstanceEvent(GUI::EVT_START_DOWNLOAD_OTHER_INSTANCE, std::vector<std::string>(std::move(downloads))));
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
@ -545,6 +558,9 @@ namespace MessageHandlerDBusInternal
|
|||
" <method name=\"AnotherInstance\">"
|
||||
" <arg name=\"data\" direction=\"in\" type=\"s\" />"
|
||||
" </method>"
|
||||
" <method name=\"Introspect\">"
|
||||
" <arg name=\"data\" direction=\"out\" type=\"s\" />"
|
||||
" </method>"
|
||||
" </interface>"
|
||||
" </node>";
|
||||
|
||||
|
@ -553,6 +569,7 @@ namespace MessageHandlerDBusInternal
|
|||
dbus_connection_send(connection, reply, NULL);
|
||||
dbus_message_unref(reply);
|
||||
}
|
||||
|
||||
//method AnotherInstance receives message from another PrusaSlicer instance
|
||||
static void handle_method_another_instance(DBusConnection *connection, DBusMessage *request)
|
||||
{
|
||||
|
@ -587,6 +604,9 @@ namespace MessageHandlerDBusInternal
|
|||
} else if (0 == strcmp(our_interface.c_str(), interface_name) && 0 == strcmp("AnotherInstance", member_name)) {
|
||||
handle_method_another_instance(connection, message);
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
} else if (0 == strcmp(our_interface.c_str(), interface_name) && 0 == strcmp("Introspect", member_name)) {
|
||||
respond_to_introspect(connection, message);
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
}
|
||||
|
|
|
@ -43,8 +43,9 @@ class MainFrame;
|
|||
#endif // __linux__
|
||||
|
||||
using LoadFromOtherInstanceEvent = Event<std::vector<boost::filesystem::path>>;
|
||||
using StartDownloadOtherInstanceEvent = Event<std::vector<std::string>>;
|
||||
wxDECLARE_EVENT(EVT_LOAD_MODEL_OTHER_INSTANCE, LoadFromOtherInstanceEvent);
|
||||
|
||||
wxDECLARE_EVENT(EVT_START_DOWNLOAD_OTHER_INSTANCE, StartDownloadOtherInstanceEvent);
|
||||
using InstanceGoToFrontEvent = SimpleEvent;
|
||||
wxDECLARE_EVENT(EVT_INSTANCE_GO_TO_FRONT, InstanceGoToFrontEvent);
|
||||
|
||||
|
|
|
@ -947,6 +947,7 @@ void NotificationManager::ProgressBarNotification::render_bar(ImGuiWrapper& imgu
|
|||
imgui.text(text.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
//------ProgressBarWithCancelNotification----------------
|
||||
|
||||
void NotificationManager::ProgressBarWithCancelNotification::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)
|
||||
|
@ -1060,6 +1061,263 @@ void NotificationManager::ProgressBarWithCancelNotification::render_bar(ImGuiWra
|
|||
ImGui::SetCursorPosY(win_size_y / 2 + win_size_y / 6 - (m_multiline ? 0 : m_line_height / 4));
|
||||
imgui.text(text.c_str());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//------URLDownloadNotification----------------
|
||||
|
||||
void NotificationManager::URLDownloadNotification::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)
|
||||
{
|
||||
if (m_percentage < 0.f || m_percentage >= 1.f) {
|
||||
render_close_button_inner(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
|
||||
if (m_percentage >= 1.f)
|
||||
render_open_button_inner(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
|
||||
} else
|
||||
render_pause_cancel_buttons_inner(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
|
||||
}
|
||||
void NotificationManager::URLDownloadNotification::render_close_button_inner(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
|
||||
{
|
||||
ImVec2 win_size(win_size_x, win_size_y);
|
||||
ImVec2 win_pos(win_pos_x, win_pos_y);
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f));
|
||||
push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
|
||||
push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(.0f, .0f, .0f, .0f));
|
||||
|
||||
|
||||
std::string button_text;
|
||||
button_text = ImGui::CloseNotifButton;
|
||||
|
||||
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_minimize_b_visible ? 2 * m_line_height : 0)),
|
||||
true))
|
||||
{
|
||||
button_text = ImGui::CloseNotifHoverButton;
|
||||
}
|
||||
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.75f);
|
||||
ImGui::SetCursorPosY(win_size.y / 2 - button_size.y);
|
||||
if (imgui.button(button_text.c_str(), button_size.x, button_size.y))
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
//invisible large button
|
||||
ImGui::SetCursorPosX(win_size.x - m_line_height * 2.35f);
|
||||
ImGui::SetCursorPosY(0);
|
||||
if (imgui.button(" ", m_line_height * 2.125, win_size.y - (m_minimize_b_visible ? 2 * m_line_height : 0)))
|
||||
{
|
||||
close();
|
||||
}
|
||||
ImGui::PopStyleColor(5);
|
||||
|
||||
}
|
||||
|
||||
void NotificationManager::URLDownloadNotification::render_pause_cancel_buttons_inner(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
|
||||
{
|
||||
|
||||
render_cancel_button_inner(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
|
||||
render_pause_button_inner(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
|
||||
}
|
||||
void NotificationManager::URLDownloadNotification::render_pause_button_inner(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
|
||||
{
|
||||
ImVec2 win_size(win_size_x, win_size_y);
|
||||
ImVec2 win_pos(win_pos_x, win_pos_y);
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f));
|
||||
push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
|
||||
push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(.0f, .0f, .0f, .0f));
|
||||
|
||||
std::wstring button_text;
|
||||
button_text = (m_download_paused ? ImGui::PlayButton : ImGui::PauseButton);
|
||||
|
||||
if (ImGui::IsMouseHoveringRect(ImVec2(win_pos.x - m_line_height * 5.f, win_pos.y),
|
||||
ImVec2(win_pos.x - m_line_height * 2.5f, win_pos.y + win_size.y),
|
||||
true))
|
||||
{
|
||||
button_text = (m_download_paused ? ImGui::PlayHoverButton : ImGui::PauseHoverButton);
|
||||
}
|
||||
|
||||
ImVec2 button_pic_size = ImGui::CalcTextSize(boost::nowide::narrow(button_text).c_str());
|
||||
ImVec2 button_size(button_pic_size.x * 1.25f, button_pic_size.y * 1.25f);
|
||||
ImGui::SetCursorPosX(win_size.x - m_line_height * 5.0f);
|
||||
ImGui::SetCursorPosY(win_size.y / 2 - button_size.y);
|
||||
if (imgui.button(button_text.c_str(), button_size.x, button_size.y))
|
||||
{
|
||||
trigger_user_action_callback(m_download_paused ? DownloaderUserAction::DownloadUserContinued : DownloaderUserAction::DownloadUserPaused);
|
||||
}
|
||||
|
||||
//invisible large button
|
||||
ImGui::SetCursorPosX(win_size.x - m_line_height * 4.625f);
|
||||
ImGui::SetCursorPosY(0);
|
||||
if (imgui.button(" ", m_line_height * 2.f, win_size.y))
|
||||
{
|
||||
trigger_user_action_callback(m_download_paused ? DownloaderUserAction::DownloadUserContinued : DownloaderUserAction::DownloadUserPaused);
|
||||
}
|
||||
ImGui::PopStyleColor(5);
|
||||
}
|
||||
|
||||
void NotificationManager::URLDownloadNotification::render_open_button_inner(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
|
||||
{
|
||||
ImVec2 win_size(win_size_x, win_size_y);
|
||||
ImVec2 win_pos(win_pos_x, win_pos_y);
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f));
|
||||
push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
|
||||
push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(.0f, .0f, .0f, .0f));
|
||||
|
||||
std::wstring button_text;
|
||||
button_text = ImGui::OpenButton;
|
||||
|
||||
if (ImGui::IsMouseHoveringRect(ImVec2(win_pos.x - m_line_height * 5.f, win_pos.y),
|
||||
ImVec2(win_pos.x - m_line_height * 2.5f, win_pos.y + win_size.y),
|
||||
true))
|
||||
{
|
||||
button_text = ImGui::OpenHoverButton;
|
||||
}
|
||||
|
||||
ImVec2 button_pic_size = ImGui::CalcTextSize(boost::nowide::narrow(button_text).c_str());
|
||||
ImVec2 button_size(button_pic_size.x * 1.25f, button_pic_size.y * 1.25f);
|
||||
ImGui::SetCursorPosX(win_size.x - m_line_height * 5.0f);
|
||||
ImGui::SetCursorPosY(win_size.y / 2 - button_size.y);
|
||||
if (imgui.button(button_text.c_str(), button_size.x, button_size.y))
|
||||
{
|
||||
trigger_user_action_callback(DownloaderUserAction::DownloadUserOpenedFolder);
|
||||
}
|
||||
|
||||
//invisible large button
|
||||
ImGui::SetCursorPosX(win_size.x - m_line_height * 4.625f);
|
||||
ImGui::SetCursorPosY(0);
|
||||
if (imgui.button(" ", m_line_height * 2.f, win_size.y))
|
||||
{
|
||||
trigger_user_action_callback(DownloaderUserAction::DownloadUserOpenedFolder);
|
||||
}
|
||||
ImGui::PopStyleColor(5);
|
||||
}
|
||||
|
||||
void NotificationManager::URLDownloadNotification::render_cancel_button_inner(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
|
||||
{
|
||||
ImVec2 win_size(win_size_x, win_size_y);
|
||||
ImVec2 win_pos(win_pos_x, win_pos_y);
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f));
|
||||
push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
|
||||
push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(.0f, .0f, .0f, .0f));
|
||||
|
||||
|
||||
std::string button_text;
|
||||
button_text = ImGui::CancelButton;
|
||||
|
||||
if (ImGui::IsMouseHoveringRect(ImVec2(win_pos.x - win_size.x / 10.f, win_pos.y),
|
||||
ImVec2(win_pos.x, win_pos.y + win_size.y - (m_minimize_b_visible ? 2 * m_line_height : 0)),
|
||||
true))
|
||||
{
|
||||
button_text = ImGui::CancelHoverButton;
|
||||
}
|
||||
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.75f);
|
||||
ImGui::SetCursorPosY(win_size.y / 2 - button_size.y);
|
||||
if (imgui.button(button_text.c_str(), button_size.x, button_size.y))
|
||||
{
|
||||
trigger_user_action_callback(DownloaderUserAction::DownloadUserCanceled);
|
||||
}
|
||||
|
||||
//invisible large button
|
||||
ImGui::SetCursorPosX(win_size.x - m_line_height * 2.35f);
|
||||
ImGui::SetCursorPosY(0);
|
||||
if (imgui.button(" ", m_line_height * 2.125, win_size.y - (m_minimize_b_visible ? 2 * m_line_height : 0)))
|
||||
{
|
||||
trigger_user_action_callback(DownloaderUserAction::DownloadUserCanceled);
|
||||
}
|
||||
ImGui::PopStyleColor(5);
|
||||
|
||||
}
|
||||
|
||||
void NotificationManager::URLDownloadNotification::trigger_user_action_callback(DownloaderUserAction action)
|
||||
{
|
||||
if (m_user_action_callback) {
|
||||
if (m_user_action_callback(action, m_download_id)) {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void NotificationManager::URLDownloadNotification::render_bar(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
|
||||
{
|
||||
ProgressBarNotification::render_bar(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
|
||||
std::string text;
|
||||
if (m_percentage < 0.f) {
|
||||
text = _u8L("ERROR") + ": " + m_error_message;
|
||||
} else if (m_percentage >= 1.f) {
|
||||
text = _u8L("COMPLETED");
|
||||
} else {
|
||||
std::stringstream stream;
|
||||
stream << std::fixed << std::setprecision(2) << (int)(m_percentage * 100) << "%";
|
||||
text = stream.str();
|
||||
}
|
||||
ImGui::SetCursorPosX(m_left_indentation);
|
||||
ImGui::SetCursorPosY(win_size_y / 2 + win_size_y / 6 - (m_multiline ? 0 : m_line_height / 4));
|
||||
imgui.text(text.c_str());
|
||||
}
|
||||
|
||||
void NotificationManager::URLDownloadNotification::count_spaces()
|
||||
{
|
||||
ProgressBarNotification::count_spaces();
|
||||
m_window_width_offset = m_line_height * 6;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//------PrintHostUploadNotification----------------
|
||||
void NotificationManager::PrintHostUploadNotification::init()
|
||||
|
@ -2162,7 +2420,6 @@ void NotificationManager::upload_job_notification_show_error(int id, const std::
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NotificationManager::push_download_progress_notification(const std::string& text, std::function<bool()> cancel_callback)
|
||||
{
|
||||
// If already exists, change text and reset progress
|
||||
|
@ -2194,6 +2451,81 @@ void NotificationManager::set_download_progress_percentage(float percentage)
|
|||
}
|
||||
}
|
||||
|
||||
void NotificationManager::push_download_URL_progress_notification(size_t id, const std::string& text, std::function<bool(DownloaderUserAction, int)> user_action_callback)
|
||||
{
|
||||
// If already exists
|
||||
for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
|
||||
if (notification->get_type() == NotificationType::URLDownload && dynamic_cast<URLDownloadNotification*>(notification.get())->get_download_id() == id) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// push new one
|
||||
NotificationData data{ NotificationType::URLDownload, NotificationLevel::ProgressBarNotificationLevel, 5, _utf8("Download:") + " " + text };
|
||||
push_notification_data(std::make_unique<NotificationManager::URLDownloadNotification>(data, m_id_provider, m_evt_handler, id, user_action_callback), 0);
|
||||
}
|
||||
|
||||
void NotificationManager::set_download_URL_progress(size_t id, float percentage)
|
||||
{
|
||||
for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
|
||||
if (notification->get_type() == NotificationType::URLDownload) {
|
||||
URLDownloadNotification* ntf = dynamic_cast<URLDownloadNotification*>(notification.get());
|
||||
if (ntf->get_download_id() != id)
|
||||
continue;
|
||||
// if this changes the percentage, it should be shown now
|
||||
float percent_b4 = ntf->get_percentage();
|
||||
ntf->set_percentage(percentage);
|
||||
ntf->set_paused(false);
|
||||
if (ntf->get_percentage() != percent_b4)
|
||||
wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NotificationManager::set_download_URL_paused(size_t id)
|
||||
{
|
||||
for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
|
||||
if (notification->get_type() == NotificationType::URLDownload) {
|
||||
URLDownloadNotification* ntf = dynamic_cast<URLDownloadNotification*>(notification.get());
|
||||
if (ntf->get_download_id() != id)
|
||||
continue;
|
||||
ntf->set_paused(true);
|
||||
wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NotificationManager::set_download_URL_canceled(size_t id)
|
||||
{
|
||||
for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
|
||||
if (notification->get_type() == NotificationType::URLDownload) {
|
||||
URLDownloadNotification* ntf = dynamic_cast<URLDownloadNotification*>(notification.get());
|
||||
if (ntf->get_download_id() != id)
|
||||
continue;
|
||||
ntf->close();
|
||||
wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
void NotificationManager::set_download_URL_error(size_t id, const std::string& text)
|
||||
{
|
||||
for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
|
||||
if (notification->get_type() == NotificationType::URLDownload) {
|
||||
URLDownloadNotification* ntf = dynamic_cast<URLDownloadNotification*>(notification.get());
|
||||
if (ntf->get_download_id() != id)
|
||||
continue;
|
||||
float percent_b4 = ntf->get_percentage();
|
||||
ntf->set_percentage(-1.f);
|
||||
ntf->set_error_message(text);
|
||||
if (ntf->get_percentage() != percent_b4)
|
||||
wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NotificationManager::init_slicing_progress_notification(std::function<bool()> cancel_callback)
|
||||
{
|
||||
for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "Event.hpp"
|
||||
#include "I18N.hpp"
|
||||
#include "Jobs/ProgressIndicator.hpp"
|
||||
#include "Downloader.hpp"
|
||||
|
||||
#include <libslic3r/ObjectID.hpp>
|
||||
#include <libslic3r/Technologies.hpp>
|
||||
|
@ -117,6 +118,8 @@ enum class NotificationType
|
|||
NetfabbFinished,
|
||||
// Short meesage to fill space between start and finish of export
|
||||
ExportOngoing,
|
||||
// Progressbar of download from prusaslicer:// url
|
||||
URLDownload
|
||||
};
|
||||
|
||||
class NotificationManager
|
||||
|
@ -215,6 +218,12 @@ public:
|
|||
// Download App progress
|
||||
void push_download_progress_notification(const std::string& text, std::function<bool()> cancel_callback);
|
||||
void set_download_progress_percentage(float percentage);
|
||||
// Download URL progress notif
|
||||
void push_download_URL_progress_notification(size_t id, const std::string& text, std::function<bool(DownloaderUserAction, int)> user_action_callback);
|
||||
void set_download_URL_progress(size_t id, float percentage);
|
||||
void set_download_URL_paused(size_t id);
|
||||
void set_download_URL_canceled(size_t id);
|
||||
void set_download_URL_error(size_t id, const std::string& text);
|
||||
// slicing progress
|
||||
void init_slicing_progress_notification(std::function<bool()> cancel_callback);
|
||||
void set_slicing_progress_began();
|
||||
|
@ -505,6 +514,62 @@ private:
|
|||
long m_hover_time{ 0 };
|
||||
};
|
||||
|
||||
class URLDownloadNotification : public ProgressBarNotification
|
||||
{
|
||||
public:
|
||||
URLDownloadNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler, size_t download_id, std::function<bool(DownloaderUserAction, int)> user_action_callback)
|
||||
//: ProgressBarWithCancelNotification(n, id_provider, evt_handler, cancel_callback)
|
||||
: ProgressBarNotification(n, id_provider, evt_handler)
|
||||
, m_download_id(download_id)
|
||||
, m_user_action_callback(user_action_callback)
|
||||
{
|
||||
}
|
||||
void set_percentage(float percent) override
|
||||
{
|
||||
m_percentage = percent;
|
||||
if (m_percentage >= 1.f) {
|
||||
m_notification_start = GLCanvas3D::timestamp_now();
|
||||
m_state = EState::Shown;
|
||||
} else
|
||||
m_state = EState::NotFading;
|
||||
}
|
||||
size_t get_download_id() { return m_download_id; }
|
||||
void set_user_action_callback(std::function<bool(DownloaderUserAction, int)> user_action_callback) { m_user_action_callback = user_action_callback; }
|
||||
void set_paused(bool paused) { m_download_paused = paused; }
|
||||
void set_error_message(const std::string& message) { m_error_message = message; }
|
||||
bool compare_text(const std::string& text) const override { return false; };
|
||||
protected:
|
||||
void render_close_button(ImGuiWrapper& imgui,
|
||||
const float win_size_x, const float win_size_y,
|
||||
const float win_pos_x, const float win_pos_y) override;
|
||||
void render_close_button_inner(ImGuiWrapper& imgui,
|
||||
const float win_size_x, const float win_size_y,
|
||||
const float win_pos_x, const float win_pos_y);
|
||||
void render_pause_cancel_buttons_inner(ImGuiWrapper& imgui,
|
||||
const float win_size_x, const float win_size_y,
|
||||
const float win_pos_x, const float win_pos_y);
|
||||
void render_open_button_inner(ImGuiWrapper& imgui,
|
||||
const float win_size_x, const float win_size_y,
|
||||
const float win_pos_x, const float win_pos_y);
|
||||
void render_cancel_button_inner(ImGuiWrapper& imgui,
|
||||
const float win_size_x, const float win_size_y,
|
||||
const float win_pos_x, const float win_pos_y);
|
||||
void render_pause_button_inner(ImGuiWrapper& imgui,
|
||||
const float win_size_x, const float win_size_y,
|
||||
const float win_pos_x, const float win_pos_y);
|
||||
void render_bar(ImGuiWrapper& imgui,
|
||||
const float win_size_x, const float win_size_y,
|
||||
const float win_pos_x, const float win_pos_y) override;
|
||||
void trigger_user_action_callback(DownloaderUserAction action);
|
||||
|
||||
void count_spaces() override;
|
||||
|
||||
size_t m_download_id;
|
||||
std::function<bool(DownloaderUserAction, int)> m_user_action_callback;
|
||||
bool m_download_paused {false};
|
||||
std::string m_error_message;
|
||||
};
|
||||
|
||||
class PrintHostUploadNotification : public ProgressBarNotification
|
||||
{
|
||||
public:
|
||||
|
@ -819,7 +884,8 @@ private:
|
|||
NotificationType::PlaterWarning,
|
||||
NotificationType::ProgressBar,
|
||||
NotificationType::PrintHostUpload,
|
||||
NotificationType::SimplifySuggestion
|
||||
NotificationType::SimplifySuggestion,
|
||||
NotificationType::URLDownload
|
||||
};
|
||||
//prepared (basic) notifications
|
||||
// non-static so its not loaded too early. If static, the translations wont load correctly.
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include <wx/numdlg.h>
|
||||
#include <wx/debug.h>
|
||||
#include <wx/busyinfo.h>
|
||||
#include <wx/stdpaths.h>
|
||||
#ifdef _WIN32
|
||||
#include <wx/richtooltip.h>
|
||||
#include <wx/custombgwin.h>
|
||||
|
@ -50,6 +51,7 @@
|
|||
#include "libslic3r/Utils.hpp"
|
||||
#include "libslic3r/PresetBundle.hpp"
|
||||
#include "libslic3r/ClipperUtils.hpp"
|
||||
#include "libslic3r/miniz_extension.hpp"
|
||||
|
||||
#include "GUI.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
|
@ -98,6 +100,7 @@
|
|||
#include "ProjectDirtyStateManager.hpp"
|
||||
#include "Gizmos/GLGizmoSimplify.hpp" // create suggestion notification
|
||||
#include "Gizmos/GLGizmoCut.hpp"
|
||||
#include "FileArchiveDialog.hpp"
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include "Gizmos/GLGizmosManager.hpp"
|
||||
|
@ -2231,6 +2234,16 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
|||
wxGetApp().mainframe->Raise();
|
||||
this->q->load_files(input_files);
|
||||
});
|
||||
|
||||
this->q->Bind(EVT_START_DOWNLOAD_OTHER_INSTANCE, [this](StartDownloadOtherInstanceEvent& evt) {
|
||||
BOOST_LOG_TRIVIAL(trace) << "Received url from other instance event.";
|
||||
wxGetApp().mainframe->Raise();
|
||||
for (size_t i = 0; i < evt.data.size(); ++i) {
|
||||
wxGetApp().start_download(evt.data[i]);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
this->q->Bind(EVT_INSTANCE_GO_TO_FRONT, [this](InstanceGoToFrontEvent &) {
|
||||
bring_instance_forward();
|
||||
});
|
||||
|
@ -2446,6 +2459,9 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
|||
int answer_convert_from_meters = wxOK_DEFAULT;
|
||||
int answer_convert_from_imperial_units = wxOK_DEFAULT;
|
||||
|
||||
bool in_temp = false;
|
||||
const fs::path temp_path = wxStandardPaths::Get().GetTempDir().utf8_str().data();
|
||||
|
||||
size_t input_files_size = input_files.size();
|
||||
for (size_t i = 0; i < input_files_size; ++i) {
|
||||
#ifdef _WIN32
|
||||
|
@ -2456,6 +2472,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
|||
// Don't make a copy on Posix. Slash is a path separator, back slashes are not accepted as a substitute.
|
||||
const auto &path = input_files[i];
|
||||
#endif // _WIN32
|
||||
in_temp = (path.parent_path() == temp_path);
|
||||
const auto filename = path.filename();
|
||||
if (progress_dlg) {
|
||||
progress_dlg->Update(static_cast<int>(100.0f * static_cast<float>(i) / static_cast<float>(input_files.size())), _L("Loading file") + ": " + from_path(filename));
|
||||
|
@ -2536,7 +2553,8 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
|||
q->update_filament_colors_in_full_config();
|
||||
is_project_file = true;
|
||||
}
|
||||
wxGetApp().app_config->update_config_dir(path.parent_path().string());
|
||||
if (!in_temp)
|
||||
wxGetApp().app_config->update_config_dir(path.parent_path().string());
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -2695,10 +2713,10 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
|||
obj_idxs.insert(obj_idxs.end(), loaded_idxs.begin(), loaded_idxs.end());
|
||||
}
|
||||
|
||||
if (load_model) {
|
||||
if (load_model && !in_temp) {
|
||||
wxGetApp().app_config->update_skein_dir(input_files[input_files.size() - 1].parent_path().make_preferred().string());
|
||||
// XXX: Plater.pm had @loaded_files, but didn't seem to fill them with the filenames...
|
||||
// statusbar()->set_status_text(_L("Loaded"));
|
||||
// statusbar()->set_status_text(_L("Loaded"));
|
||||
}
|
||||
|
||||
// automatic selection of added objects
|
||||
|
@ -2909,9 +2927,10 @@ wxString Plater::priv::get_export_file(GUI::FileType file_type)
|
|||
}
|
||||
|
||||
std::string out_dir = (boost::filesystem::path(output_file).parent_path()).string();
|
||||
|
||||
std::string temp_dir = wxStandardPaths::Get().GetTempDir().utf8_str().data();
|
||||
|
||||
wxFileDialog dlg(q, dlg_title,
|
||||
is_shapes_dir(out_dir) ? from_u8(wxGetApp().app_config->get_last_dir()) : from_path(output_file.parent_path()), from_path(output_file.filename()),
|
||||
out_dir == temp_dir ? from_u8(wxGetApp().app_config->get("last_output_path")) : (is_shapes_dir(out_dir) ? from_u8(wxGetApp().app_config->get_last_dir()) : from_path(output_file.parent_path())), from_path(output_file.filename()),
|
||||
wildcard, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
|
||||
|
||||
if (dlg.ShowModal() != wxID_OK)
|
||||
|
@ -4626,7 +4645,10 @@ void Plater::priv::set_project_filename(const wxString& filename)
|
|||
m_project_filename = from_path(full_path);
|
||||
wxGetApp().mainframe->update_title();
|
||||
|
||||
if (!filename.empty())
|
||||
const fs::path temp_path = wxStandardPaths::Get().GetTempDir().utf8_str().data();
|
||||
bool in_temp = (temp_path == full_path.parent_path().make_preferred());
|
||||
|
||||
if (!filename.empty() && !in_temp)
|
||||
wxGetApp().mainframe->add_to_recent_projects(filename);
|
||||
}
|
||||
|
||||
|
@ -5359,6 +5381,11 @@ Print& Plater::fff_print() { return p->fff_print; }
|
|||
const SLAPrint& Plater::sla_print() const { return p->sla_print; }
|
||||
SLAPrint& Plater::sla_print() { return p->sla_print; }
|
||||
|
||||
bool Plater::is_project_temp() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void Plater::new_project()
|
||||
{
|
||||
if (int saved_project = p->save_project_if_dirty(_L("Creating a new project while the current project is modified.")); saved_project == wxID_CANCEL)
|
||||
|
@ -5563,18 +5590,418 @@ std::vector<size_t> Plater::load_files(const std::vector<std::string>& input_fil
|
|||
return p->load_files(paths, load_model, load_config, imperial_units);
|
||||
}
|
||||
|
||||
enum class LoadType : unsigned char
|
||||
|
||||
class LoadProjectsDialog : public DPIDialog
|
||||
{
|
||||
Unknown,
|
||||
OpenProject,
|
||||
LoadGeometry,
|
||||
LoadConfig
|
||||
int m_action{ 0 };
|
||||
bool m_all { false };
|
||||
wxComboBox* m_combo_project { nullptr };
|
||||
wxComboBox* m_combo_config { nullptr };
|
||||
public:
|
||||
enum class LoadProjectOption : unsigned char
|
||||
{
|
||||
Unknown,
|
||||
AllGeometry,
|
||||
AllNewWindow,
|
||||
OneProject,
|
||||
OneConfig
|
||||
};
|
||||
|
||||
LoadProjectsDialog(const std::vector<fs::path>& paths);
|
||||
|
||||
int get_action() const { return m_action + 1; }
|
||||
bool get_all() const { return m_all; }
|
||||
int get_selected() const
|
||||
{
|
||||
if (m_combo_project && m_combo_project->IsEnabled())
|
||||
return m_combo_project->GetSelection();
|
||||
else if (m_combo_config && m_combo_config->IsEnabled())
|
||||
return m_combo_config->GetSelection();
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
protected:
|
||||
void on_dpi_changed(const wxRect& suggested_rect) override;
|
||||
};
|
||||
|
||||
LoadProjectsDialog::LoadProjectsDialog(const std::vector<fs::path>& paths)
|
||||
: DPIDialog(static_cast<wxWindow*>(wxGetApp().mainframe), wxID_ANY,
|
||||
from_u8((boost::format(_utf8(L("%s - Multiple projects file"))) % SLIC3R_APP_NAME).str()), wxDefaultPosition,
|
||||
wxDefaultSize, wxDEFAULT_DIALOG_STYLE)
|
||||
{
|
||||
SetFont(wxGetApp().normal_font());
|
||||
|
||||
wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
bool contains_projects = !paths.empty();
|
||||
bool instances_allowed = wxGetApp().app_config->get("single_instance") != "1";
|
||||
if (contains_projects)
|
||||
main_sizer->Add(new wxStaticText(this, wxID_ANY,
|
||||
get_wraped_wxString(_L("There are several files being loaded, including Project files.") + "\n" + _L("Select an action to apply to all files."))), 0, wxEXPAND | wxALL, 10);
|
||||
else
|
||||
main_sizer->Add(new wxStaticText(this, wxID_ANY,
|
||||
get_wraped_wxString(_L("There are several files being loaded.") + "\n" + _L("Select an action to apply to all files."))), 0, wxEXPAND | wxALL, 10);
|
||||
|
||||
wxStaticBox* action_stb = new wxStaticBox(this, wxID_ANY, _L("Action"));
|
||||
if (!wxOSX) action_stb->SetBackgroundStyle(wxBG_STYLE_PAINT);
|
||||
action_stb->SetFont(wxGetApp().normal_font());
|
||||
|
||||
if (contains_projects) {
|
||||
wxArrayString filenames;
|
||||
for (const fs::path& path : paths) {
|
||||
filenames.push_back(from_u8(path.filename().string()));
|
||||
}
|
||||
m_combo_project = new wxComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, filenames, wxCB_READONLY);
|
||||
m_combo_project->SetValue(filenames.front());
|
||||
m_combo_project->Enable(false);
|
||||
|
||||
m_combo_config = new wxComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, filenames, wxCB_READONLY);
|
||||
m_combo_config->SetValue(filenames.front());
|
||||
m_combo_config->Enable(false);
|
||||
}
|
||||
wxStaticBoxSizer* stb_sizer = new wxStaticBoxSizer(action_stb, wxVERTICAL);
|
||||
int id = 0;
|
||||
|
||||
// all geometry
|
||||
wxRadioButton* btn = new wxRadioButton(this, wxID_ANY, _L("Import geometry"), wxDefaultPosition, wxDefaultSize, id == 0 ? wxRB_GROUP : 0);
|
||||
btn->SetValue(id == m_action);
|
||||
btn->Bind(wxEVT_RADIOBUTTON, [this, id, contains_projects](wxCommandEvent&) {
|
||||
m_action = id;
|
||||
if (contains_projects) {
|
||||
m_combo_project->Enable(false);
|
||||
m_combo_config->Enable(false);
|
||||
}
|
||||
});
|
||||
stb_sizer->Add(btn, 0, wxEXPAND | wxTOP, 5);
|
||||
id++;
|
||||
// all new window
|
||||
if (instances_allowed) {
|
||||
btn = new wxRadioButton(this, wxID_ANY, _L("Start new PrusaSlicer instance"), wxDefaultPosition, wxDefaultSize, id == 0 ? wxRB_GROUP : 0);
|
||||
btn->SetValue(id == m_action);
|
||||
btn->Bind(wxEVT_RADIOBUTTON, [this, id, contains_projects](wxCommandEvent&) {
|
||||
m_action = id;
|
||||
if (contains_projects) {
|
||||
m_combo_project->Enable(false);
|
||||
m_combo_config->Enable(false);
|
||||
}
|
||||
});
|
||||
stb_sizer->Add(btn, 0, wxEXPAND | wxTOP, 5);
|
||||
}
|
||||
id++; // IMPORTANT TO ALWAYS UP THE ID EVEN IF OPTION IS NOT ADDED!
|
||||
if (contains_projects) {
|
||||
// one project
|
||||
btn = new wxRadioButton(this, wxID_ANY, _L("Select one to load as project"), wxDefaultPosition, wxDefaultSize, id == 0 ? wxRB_GROUP : 0);
|
||||
btn->SetValue(false);
|
||||
btn->Bind(wxEVT_RADIOBUTTON, [this, id](wxCommandEvent&) {
|
||||
m_action = id;
|
||||
m_combo_project->Enable(true);
|
||||
m_combo_config->Enable(false);
|
||||
});
|
||||
stb_sizer->Add(btn, 0, wxEXPAND | wxTOP, 5);
|
||||
stb_sizer->Add(m_combo_project, 0, wxEXPAND | wxTOP, 5);
|
||||
// one config
|
||||
id++;
|
||||
btn = new wxRadioButton(this, wxID_ANY, _L("Select one to load config only"), wxDefaultPosition, wxDefaultSize, id == 0 ? wxRB_GROUP : 0);
|
||||
btn->SetValue(id == m_action);
|
||||
btn->Bind(wxEVT_RADIOBUTTON, [this, id, instances_allowed](wxCommandEvent&) {
|
||||
m_action = id;
|
||||
if (instances_allowed)
|
||||
m_combo_project->Enable(false);
|
||||
m_combo_config->Enable(true);
|
||||
});
|
||||
stb_sizer->Add(btn, 0, wxEXPAND | wxTOP, 5);
|
||||
stb_sizer->Add(m_combo_config, 0, wxEXPAND | wxTOP, 5);
|
||||
}
|
||||
|
||||
|
||||
main_sizer->Add(stb_sizer, 1, wxEXPAND | wxRIGHT | wxLEFT, 10);
|
||||
wxBoxSizer* bottom_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
bottom_sizer->Add(CreateStdDialogButtonSizer(wxOK | wxCANCEL), 0, wxEXPAND | wxLEFT, 5);
|
||||
main_sizer->Add(bottom_sizer, 0, wxEXPAND | wxALL, 10);
|
||||
SetSizer(main_sizer);
|
||||
main_sizer->SetSizeHints(this);
|
||||
|
||||
// Update DarkUi just for buttons
|
||||
wxGetApp().UpdateDlgDarkUI(this, true);
|
||||
}
|
||||
|
||||
void LoadProjectsDialog::on_dpi_changed(const wxRect& suggested_rect)
|
||||
{
|
||||
const int em = em_unit();
|
||||
SetMinSize(wxSize(65 * em, 30 * em));
|
||||
Fit();
|
||||
Refresh();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
bool Plater::preview_zip_archive(const boost::filesystem::path& archive_path)
|
||||
{
|
||||
//std::vector<fs::path> unzipped_paths;
|
||||
std::vector<fs::path> non_project_paths;
|
||||
std::vector<fs::path> project_paths;
|
||||
try
|
||||
{
|
||||
mz_zip_archive archive;
|
||||
mz_zip_zero_struct(&archive);
|
||||
|
||||
if (!open_zip_reader(&archive, archive_path.string())) {
|
||||
std::string err_msg = GUI::format(_utf8("Loading of a zip archive on path %1% has failed."), archive_path.string());
|
||||
throw Slic3r::FileIOError(err_msg);
|
||||
}
|
||||
|
||||
mz_uint num_entries = mz_zip_reader_get_num_files(&archive);
|
||||
|
||||
mz_zip_archive_file_stat stat;
|
||||
|
||||
std::vector<fs::path> selected_paths;
|
||||
|
||||
FileArchiveDialog dlg(static_cast<wxWindow*>(wxGetApp().mainframe), &archive, selected_paths);
|
||||
if (dlg.ShowModal() == wxID_OK)
|
||||
{
|
||||
std::string archive_path_string = archive_path.string();
|
||||
archive_path_string = archive_path_string.substr(0, archive_path_string.size() - 4);
|
||||
|
||||
fs::path archive_dir(wxStandardPaths::Get().GetTempDir().utf8_str().data());
|
||||
|
||||
for (mz_uint i = 0; i < num_entries; ++i) {
|
||||
if (mz_zip_reader_file_stat(&archive, i, &stat)) {
|
||||
wxString wname = boost::nowide::widen(stat.m_filename);
|
||||
std::string name = GUI::format(wname);
|
||||
fs::path archive_path(name);
|
||||
|
||||
std::string extra(1024, 0);
|
||||
size_t extra_size = mz_zip_reader_get_filename_from_extra(&archive, i, extra.data(), extra.size());
|
||||
if (extra_size > 0) {
|
||||
archive_path = fs::path(extra.substr(0, extra_size));
|
||||
name = archive_path.string();
|
||||
}
|
||||
|
||||
if (archive_path.empty())
|
||||
continue;
|
||||
for (const auto& path : selected_paths) {
|
||||
if (path == archive_path) {
|
||||
try
|
||||
{
|
||||
std::replace(name.begin(), name.end(), '\\', '/');
|
||||
// rename if file exists
|
||||
std::string filename = path.filename().string();
|
||||
std::string extension = boost::filesystem::extension(path);
|
||||
std::string just_filename = filename.substr(0, filename.size() - extension.size());
|
||||
std::string final_filename = just_filename;
|
||||
|
||||
size_t version = 0;
|
||||
while (fs::exists(archive_dir / (final_filename + extension)))
|
||||
{
|
||||
++version;
|
||||
final_filename = just_filename + "(" + std::to_string(version) + ")";
|
||||
}
|
||||
filename = final_filename + extension;
|
||||
fs::path final_path = archive_dir / filename;
|
||||
|
||||
std::string buffer((size_t)stat.m_uncomp_size, 0);
|
||||
mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0);
|
||||
if (res == 0) {
|
||||
wxString error_log = GUI::format_wxstr(_L("Failed to unzip file to %1%: %2% "), final_path.string(), mz_zip_get_error_string(mz_zip_get_last_error(&archive)));
|
||||
BOOST_LOG_TRIVIAL(error) << error_log;
|
||||
show_error(nullptr, error_log);
|
||||
continue;
|
||||
}
|
||||
fs::fstream file(final_path, std::ios::out | std::ios::binary | std::ios::trunc);
|
||||
file.write(buffer.c_str(), buffer.size());
|
||||
file.close();
|
||||
if (!fs::exists(final_path)) {
|
||||
wxString error_log = GUI::format_wxstr(_L("Failed to find unzipped file at %1%. Unzipping of file has failed."), final_path.string());
|
||||
BOOST_LOG_TRIVIAL(error) << error_log;
|
||||
show_error(nullptr, error_log);
|
||||
continue;
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(info) << "Unzipped " << final_path;
|
||||
|
||||
if (!boost::algorithm::iends_with(filename, ".3mf") && !boost::algorithm::iends_with(filename, ".amf")) {
|
||||
non_project_paths.emplace_back(final_path);
|
||||
continue;
|
||||
}
|
||||
// if 3mf - read archive headers to find project file
|
||||
if ((boost::algorithm::iends_with(filename, ".3mf") && !is_project_3mf(final_path.string())) ||
|
||||
(boost::algorithm::iends_with(filename, ".amf") && !boost::algorithm::iends_with(filename, ".zip.amf"))) {
|
||||
non_project_paths.emplace_back(final_path);
|
||||
continue;
|
||||
}
|
||||
|
||||
project_paths.emplace_back(final_path);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
// ensure the zip archive is closed and rethrow the exception
|
||||
close_zip_reader(&archive);
|
||||
throw Slic3r::FileIOError(e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
close_zip_reader(&archive);
|
||||
if (non_project_paths.size() + project_paths.size() != selected_paths.size())
|
||||
BOOST_LOG_TRIVIAL(error) << "Decompresing of archive did not retrieve all files. Expected files: "
|
||||
<< selected_paths.size()
|
||||
<< " Decopressed files: "
|
||||
<< non_project_paths.size() + project_paths.size();
|
||||
} else {
|
||||
close_zip_reader(&archive);
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
catch (const Slic3r::FileIOError& e) {
|
||||
// zip reader should be already closed or not even opened
|
||||
GUI::show_error(this, e.what());
|
||||
return false;
|
||||
}
|
||||
// none selected
|
||||
if (project_paths.empty() && non_project_paths.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#if 0
|
||||
// 1 project, 0 models - behave like drag n drop
|
||||
if (project_paths.size() == 1 && non_project_paths.empty())
|
||||
{
|
||||
wxArrayString aux;
|
||||
aux.Add(from_u8(project_paths.front().string()));
|
||||
load_files(aux);
|
||||
//load_files(project_paths, true, true);
|
||||
boost::system::error_code ec;
|
||||
fs::remove(project_paths.front(), ec);
|
||||
if (ec)
|
||||
BOOST_LOG_TRIVIAL(error) << ec.message();
|
||||
return true;
|
||||
}
|
||||
// 1 model (or more and other instances are not allowed), 0 projects - open geometry
|
||||
if (project_paths.empty() && (non_project_paths.size() == 1 || wxGetApp().app_config->get("single_instance") == "1"))
|
||||
{
|
||||
load_files(non_project_paths, true, false);
|
||||
boost::system::error_code ec;
|
||||
fs::remove(non_project_paths.front(), ec);
|
||||
if (ec)
|
||||
BOOST_LOG_TRIVIAL(error) << ec.message();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool delete_after = true;
|
||||
|
||||
LoadProjectsDialog dlg(project_paths);
|
||||
if (dlg.ShowModal() == wxID_OK) {
|
||||
LoadProjectsDialog::LoadProjectOption option = static_cast<LoadProjectsDialog::LoadProjectOption>(dlg.get_action());
|
||||
switch (option)
|
||||
{
|
||||
case LoadProjectsDialog::LoadProjectOption::AllGeometry: {
|
||||
load_files(project_paths, true, false);
|
||||
load_files(non_project_paths, true, false);
|
||||
break;
|
||||
}
|
||||
case LoadProjectsDialog::LoadProjectOption::AllNewWindow: {
|
||||
delete_after = false;
|
||||
for (const fs::path& path : project_paths) {
|
||||
wxString f = from_path(path);
|
||||
start_new_slicer(&f, false);
|
||||
}
|
||||
for (const fs::path& path : non_project_paths) {
|
||||
wxString f = from_path(path);
|
||||
start_new_slicer(&f, false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case LoadProjectsDialog::LoadProjectOption::OneProject: {
|
||||
int pos = dlg.get_selected();
|
||||
assert(pos >= 0 && pos < project_paths.size());
|
||||
if (wxGetApp().can_load_project())
|
||||
load_project(from_path(project_paths[pos]));
|
||||
project_paths.erase(project_paths.begin() + pos);
|
||||
load_files(project_paths, true, false);
|
||||
load_files(non_project_paths, true, false);
|
||||
break;
|
||||
}
|
||||
case LoadProjectsDialog::LoadProjectOption::OneConfig: {
|
||||
int pos = dlg.get_selected();
|
||||
assert(pos >= 0 && pos < project_paths.size());
|
||||
std::vector<fs::path> aux;
|
||||
aux.push_back(project_paths[pos]);
|
||||
load_files(aux, false, true);
|
||||
project_paths.erase(project_paths.begin() + pos);
|
||||
load_files(project_paths, true, false);
|
||||
load_files(non_project_paths, true, false);
|
||||
break;
|
||||
}
|
||||
case LoadProjectsDialog::LoadProjectOption::Unknown:
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!delete_after)
|
||||
return true;
|
||||
#else
|
||||
// 1 project file and some models - behave like drag n drop of 3mf and then load models
|
||||
if (project_paths.size() == 1)
|
||||
{
|
||||
wxArrayString aux;
|
||||
aux.Add(from_u8(project_paths.front().string()));
|
||||
bool loaded3mf = load_files(aux, true);
|
||||
load_files(non_project_paths, true, false);
|
||||
boost::system::error_code ec;
|
||||
if (loaded3mf) {
|
||||
fs::remove(project_paths.front(), ec);
|
||||
if (ec)
|
||||
BOOST_LOG_TRIVIAL(error) << ec.message();
|
||||
}
|
||||
for (const fs::path& path : non_project_paths) {
|
||||
// Delete file from temp file (path variable), it will stay only in app memory.
|
||||
boost::system::error_code ec;
|
||||
fs::remove(path, ec);
|
||||
if (ec)
|
||||
BOOST_LOG_TRIVIAL(error) << ec.message();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// load all projects and all models as geometry
|
||||
load_files(project_paths, true, false);
|
||||
load_files(non_project_paths, true, false);
|
||||
#endif // 0
|
||||
|
||||
|
||||
for (const fs::path& path : project_paths) {
|
||||
// Delete file from temp file (path variable), it will stay only in app memory.
|
||||
boost::system::error_code ec;
|
||||
fs::remove(path, ec);
|
||||
if (ec)
|
||||
BOOST_LOG_TRIVIAL(error) << ec.message();
|
||||
}
|
||||
for (const fs::path& path : non_project_paths) {
|
||||
// Delete file from temp file (path variable), it will stay only in app memory.
|
||||
boost::system::error_code ec;
|
||||
fs::remove(path, ec);
|
||||
if (ec)
|
||||
BOOST_LOG_TRIVIAL(error) << ec.message();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
class ProjectDropDialog : public DPIDialog
|
||||
{
|
||||
int m_action { 0 };
|
||||
public:
|
||||
enum class LoadType : unsigned char
|
||||
{
|
||||
Unknown,
|
||||
OpenProject,
|
||||
LoadGeometry,
|
||||
LoadConfig,
|
||||
OpenWindow
|
||||
};
|
||||
ProjectDropDialog(const std::string& filename);
|
||||
|
||||
int get_action() const { return m_action + 1; }
|
||||
|
@ -5590,17 +6017,21 @@ ProjectDropDialog::ProjectDropDialog(const std::string& filename)
|
|||
{
|
||||
SetFont(wxGetApp().normal_font());
|
||||
|
||||
bool single_instance_only = wxGetApp().app_config->get("single_instance") == "1";
|
||||
wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
const wxString choices[] = { _L("Open as project"),
|
||||
_L("Import geometry only"),
|
||||
_L("Import config only") };
|
||||
wxArrayString choices;
|
||||
choices.reserve(4);
|
||||
choices.Add(_L("Open as project"));
|
||||
choices.Add(_L("Import geometry only"));
|
||||
choices.Add(_L("Import config only"));
|
||||
if (!single_instance_only)
|
||||
choices.Add(_L("Start new PrusaSlicer instance"));
|
||||
|
||||
main_sizer->Add(new wxStaticText(this, wxID_ANY,
|
||||
get_wraped_wxString(_L("Select an action to apply to the file") + ": " + from_u8(filename))), 0, wxEXPAND | wxALL, 10);
|
||||
|
||||
m_action = std::clamp(std::stoi(wxGetApp().app_config->get("drop_project_action")),
|
||||
static_cast<int>(LoadType::OpenProject), static_cast<int>(LoadType::LoadConfig)) - 1;
|
||||
static_cast<int>(LoadType::OpenProject), single_instance_only? static_cast<int>(LoadType::LoadConfig) : static_cast<int>(LoadType::OpenWindow)) - 1;
|
||||
|
||||
wxStaticBox* action_stb = new wxStaticBox(this, wxID_ANY, _L("Action"));
|
||||
if (!wxOSX) action_stb->SetBackgroundStyle(wxBG_STYLE_PAINT);
|
||||
|
@ -5642,9 +6073,9 @@ void ProjectDropDialog::on_dpi_changed(const wxRect& suggested_rect)
|
|||
Refresh();
|
||||
}
|
||||
|
||||
bool Plater::load_files(const wxArrayString& filenames)
|
||||
bool Plater::load_files(const wxArrayString& filenames, bool delete_after_load/*=false*/)
|
||||
{
|
||||
const std::regex pattern_drop(".*[.](stl|obj|amf|3mf|prusa|step|stp)", std::regex::icase);
|
||||
const std::regex pattern_drop(".*[.](stl|obj|amf|3mf|prusa|step|stp|zip)", std::regex::icase);
|
||||
const std::regex pattern_gcode_drop(".*[.](gcode|g)", std::regex::icase);
|
||||
|
||||
std::vector<fs::path> paths;
|
||||
|
@ -5688,53 +6119,61 @@ bool Plater::load_files(const wxArrayString& filenames)
|
|||
for (std::vector<fs::path>::const_reverse_iterator it = paths.rbegin(); it != paths.rend(); ++it) {
|
||||
std::string filename = (*it).filename().string();
|
||||
if (boost::algorithm::iends_with(filename, ".3mf") || boost::algorithm::iends_with(filename, ".amf")) {
|
||||
LoadType load_type = LoadType::Unknown;
|
||||
ProjectDropDialog::LoadType load_type = ProjectDropDialog::LoadType::Unknown;
|
||||
if (!model().objects.empty()) {
|
||||
if ((boost::algorithm::iends_with(filename, ".3mf") && !is_project_3mf(it->string())) ||
|
||||
(boost::algorithm::iends_with(filename, ".amf") && !boost::algorithm::iends_with(filename, ".zip.amf")))
|
||||
load_type = LoadType::LoadGeometry;
|
||||
load_type = ProjectDropDialog::LoadType::LoadGeometry;
|
||||
else {
|
||||
if (wxGetApp().app_config->get("show_drop_project_dialog") == "1") {
|
||||
ProjectDropDialog dlg(filename);
|
||||
if (dlg.ShowModal() == wxID_OK) {
|
||||
int choice = dlg.get_action();
|
||||
load_type = static_cast<LoadType>(choice);
|
||||
load_type = static_cast<ProjectDropDialog::LoadType>(choice);
|
||||
wxGetApp().app_config->set("drop_project_action", std::to_string(choice));
|
||||
}
|
||||
}
|
||||
else
|
||||
load_type = static_cast<LoadType>(std::clamp(std::stoi(wxGetApp().app_config->get("drop_project_action")),
|
||||
static_cast<int>(LoadType::OpenProject), static_cast<int>(LoadType::LoadConfig)));
|
||||
load_type = static_cast<ProjectDropDialog::LoadType>(std::clamp(std::stoi(wxGetApp().app_config->get("drop_project_action")),
|
||||
static_cast<int>(ProjectDropDialog::LoadType::OpenProject), static_cast<int>(ProjectDropDialog::LoadType::LoadConfig)));
|
||||
}
|
||||
}
|
||||
else
|
||||
load_type = LoadType::OpenProject;
|
||||
load_type = ProjectDropDialog::LoadType::OpenProject;
|
||||
|
||||
if (load_type == LoadType::Unknown)
|
||||
if (load_type == ProjectDropDialog::LoadType::Unknown)
|
||||
return false;
|
||||
|
||||
switch (load_type) {
|
||||
case LoadType::OpenProject: {
|
||||
case ProjectDropDialog::LoadType::OpenProject: {
|
||||
if (wxGetApp().can_load_project())
|
||||
load_project(from_path(*it));
|
||||
break;
|
||||
}
|
||||
case LoadType::LoadGeometry: {
|
||||
case ProjectDropDialog::LoadType::LoadGeometry: {
|
||||
Plater::TakeSnapshot snapshot(this, _L("Import Object"));
|
||||
load_files({ *it }, true, false);
|
||||
break;
|
||||
}
|
||||
case LoadType::LoadConfig: {
|
||||
case ProjectDropDialog::LoadType::LoadConfig: {
|
||||
load_files({ *it }, false, true);
|
||||
break;
|
||||
}
|
||||
case LoadType::Unknown : {
|
||||
case ProjectDropDialog::LoadType::OpenWindow: {
|
||||
wxString f = from_path(*it);
|
||||
start_new_slicer(&f, false, delete_after_load);
|
||||
return false; // did not load anything to this instance
|
||||
}
|
||||
case ProjectDropDialog::LoadType::Unknown : {
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
} else if (boost::algorithm::iends_with(filename, ".zip")) {
|
||||
return preview_zip_archive(*it);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -152,6 +152,8 @@ public:
|
|||
void render_project_state_debug_window() const;
|
||||
#endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
|
||||
|
||||
bool is_project_temp() const;
|
||||
|
||||
Sidebar& sidebar();
|
||||
const Model& model() const;
|
||||
Model& model();
|
||||
|
@ -175,9 +177,11 @@ public:
|
|||
// To be called when providing a list of files to the GUI slic3r on command line.
|
||||
std::vector<size_t> load_files(const std::vector<std::string>& input_files, bool load_model = true, bool load_config = true, bool imperial_units = false);
|
||||
// to be called on drag and drop
|
||||
bool load_files(const wxArrayString& filenames);
|
||||
bool load_files(const wxArrayString& filenames, bool delete_after_load = false);
|
||||
void check_selected_presets_visibility(PrinterTechnology loaded_printer_technology);
|
||||
|
||||
bool preview_zip_archive(const boost::filesystem::path& input_file);
|
||||
|
||||
const wxString& get_last_loaded_gcode() const { return m_last_loaded_gcode; }
|
||||
|
||||
void update();
|
||||
|
|
|
@ -10,6 +10,16 @@
|
|||
#include "ButtonsDescription.hpp"
|
||||
#include "OG_CustomCtrl.hpp"
|
||||
#include "GLCanvas3D.hpp"
|
||||
#include "ConfigWizard_private.hpp"
|
||||
|
||||
#include <boost/dll/runtime_symbol_info.hpp>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <wx/msw/registry.h>
|
||||
#endif // WIN32
|
||||
#ifdef __linux__
|
||||
#include "DesktopIntegrationDialog.hpp"
|
||||
#endif //__linux__
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
@ -80,6 +90,14 @@ void PreferencesDialog::show(const std::string& highlight_opt_key /*= std::strin
|
|||
m_use_custom_toolbar_size = get_app_config()->get("use_custom_toolbar_size") == "1";
|
||||
|
||||
if (wxGetApp().is_editor()) {
|
||||
auto app_config = get_app_config();
|
||||
|
||||
downloader->set_path_name(app_config->get("url_downloader_dest"));
|
||||
downloader->allow(!app_config->has("downloader_url_registered") || app_config->get("downloader_url_registered") == "1");
|
||||
|
||||
for (const std::string& opt_key : {"suppress_hyperlinks", "downloader_url_registered"})
|
||||
m_optgroup_other->set_value(opt_key, app_config->get(opt_key) == "1");
|
||||
|
||||
// update colors for color pickers of the labels
|
||||
update_color(m_sys_colour, wxGetApp().get_label_clr_sys());
|
||||
update_color(m_mod_colour, wxGetApp().get_label_clr_modified());
|
||||
|
@ -166,6 +184,25 @@ static void append_enum_option( std::shared_ptr<ConfigOptionsGroup> optgroup,
|
|||
wxGetApp().sidebar().get_searcher().add_key(opt_key, Preset::TYPE_PREFERENCES, optgroup->config_category(), L("Preferences"));
|
||||
}
|
||||
|
||||
static void append_string_option(std::shared_ptr<ConfigOptionsGroup> optgroup,
|
||||
const std::string& opt_key,
|
||||
const std::string& label,
|
||||
const std::string& tooltip,
|
||||
const std::string& def_val,
|
||||
ConfigOptionMode mode = comSimple)
|
||||
{
|
||||
ConfigOptionDef def = { opt_key, coString };
|
||||
def.label = label;
|
||||
def.tooltip = tooltip;
|
||||
def.mode = mode;
|
||||
def.set_default_value(new ConfigOptionString{ def_val });
|
||||
Option option(def, opt_key);
|
||||
optgroup->append_single_option_line(option);
|
||||
|
||||
// fill data to the Search Dialog
|
||||
wxGetApp().sidebar().get_searcher().add_key(opt_key, Preset::TYPE_PREFERENCES, optgroup->config_category(), L("Preferences"));
|
||||
}
|
||||
|
||||
static void append_preferences_option_to_searcer(std::shared_ptr<ConfigOptionsGroup> optgroup,
|
||||
const std::string& opt_key,
|
||||
const wxString& label)
|
||||
|
@ -423,9 +460,9 @@ void PreferencesDialog::build()
|
|||
return;
|
||||
}
|
||||
|
||||
if (opt_key == "suppress_hyperlinks")
|
||||
/* if (opt_key == "suppress_hyperlinks")
|
||||
m_values[opt_key] = boost::any_cast<bool>(value) ? "1" : "";
|
||||
else
|
||||
else*/
|
||||
m_values[opt_key] = boost::any_cast<bool>(value) ? "1" : "0";
|
||||
};
|
||||
|
||||
|
@ -440,14 +477,14 @@ void PreferencesDialog::build()
|
|||
L("Show sidebar collapse/expand button"),
|
||||
L("If enabled, the button for the collapse sidebar will be appeared in top right corner of the 3D Scene"),
|
||||
app_config->get("show_collapse_button") == "1");
|
||||
|
||||
/*
|
||||
append_bool_option(m_optgroup_gui, "suppress_hyperlinks",
|
||||
L("Suppress to open hyperlink in browser"),
|
||||
L("If enabled, PrusaSlicer will not open a hyperlinks in your browser."),
|
||||
//L("If enabled, the descriptions of configuration parameters in settings tabs wouldn't work as hyperlinks. "
|
||||
// "If disabled, the descriptions of configuration parameters in settings tabs will work as hyperlinks."),
|
||||
app_config->get("suppress_hyperlinks") == "1");
|
||||
|
||||
*/
|
||||
append_bool_option(m_optgroup_gui, "color_mapinulation_panel",
|
||||
L("Use colors for axes values in Manipulation panel"),
|
||||
L("If enabled, the axes names and axes values will be colorized according to the axes colors. "
|
||||
|
@ -514,6 +551,37 @@ void PreferencesDialog::build()
|
|||
create_settings_text_color_widget();
|
||||
create_settings_mode_color_widget();
|
||||
|
||||
m_optgroup_other = create_options_tab(_L("Other"), tabs);
|
||||
m_optgroup_other->m_on_change = [this](t_config_option_key opt_key, boost::any value) {
|
||||
|
||||
if (auto it = m_values.find(opt_key); it != m_values.end() && opt_key != "url_downloader_dest") {
|
||||
m_values.erase(it); // we shouldn't change value, if some of those parameters were selected, and then deselected
|
||||
return;
|
||||
}
|
||||
|
||||
if (opt_key == "suppress_hyperlinks")
|
||||
m_values[opt_key] = boost::any_cast<bool>(value) ? "1" : "";
|
||||
else
|
||||
m_values[opt_key] = boost::any_cast<bool>(value) ? "1" : "0"; m_values[opt_key] = boost::any_cast<bool>(value) ? "1" : "0";
|
||||
};
|
||||
|
||||
|
||||
append_bool_option(m_optgroup_other, "suppress_hyperlinks",
|
||||
L("Suppress to open hyperlink in browser"),
|
||||
L("If enabled, PrusaSlicer will not open a hyperlinks in your browser."),
|
||||
//L("If enabled, the descriptions of configuration parameters in settings tabs wouldn't work as hyperlinks. "
|
||||
// "If disabled, the descriptions of configuration parameters in settings tabs will work as hyperlinks."),
|
||||
app_config->get("suppress_hyperlinks") == "1");
|
||||
|
||||
append_bool_option(m_optgroup_other, "downloader_url_registered",
|
||||
L("Allow downloads from Printables.com"),
|
||||
L("If enabled, PrusaSlicer will allow to download from Printables.com"),
|
||||
app_config->get("downloader_url_registered") == "1");
|
||||
|
||||
activate_options_tab(m_optgroup_other);
|
||||
|
||||
create_downloader_path_sizer();
|
||||
|
||||
#if ENABLE_ENVIRONMENT_MAP
|
||||
// Add "Render" tab
|
||||
m_optgroup_render = create_options_tab(L("Render"), tabs);
|
||||
|
@ -587,7 +655,7 @@ std::vector<ConfigOptionsGroup*> PreferencesDialog::optgroups()
|
|||
{
|
||||
std::vector<ConfigOptionsGroup*> out;
|
||||
out.reserve(4);
|
||||
for (ConfigOptionsGroup* opt : { m_optgroup_general.get(), m_optgroup_camera.get(), m_optgroup_gui.get()
|
||||
for (ConfigOptionsGroup* opt : { m_optgroup_general.get(), m_optgroup_camera.get(), m_optgroup_gui.get(), m_optgroup_other.get()
|
||||
#ifdef _WIN32
|
||||
, m_optgroup_dark_mode.get()
|
||||
#endif // _WIN32
|
||||
|
@ -614,6 +682,16 @@ void PreferencesDialog::update_ctrls_alignment()
|
|||
|
||||
void PreferencesDialog::accept(wxEvent&)
|
||||
{
|
||||
if (const auto it = m_values.find("downloader_url_registered"); it != m_values.end())
|
||||
downloader->allow(it->second == "1");
|
||||
if (!downloader->on_finish())
|
||||
return;
|
||||
|
||||
#ifdef __linux__
|
||||
if( downloader->get_perform_registration_linux())
|
||||
DesktopIntegrationDialog::perform_desktop_integration(true);
|
||||
#endif // __linux__
|
||||
|
||||
std::vector<std::string> options_to_recreate_GUI = { "no_defaults", "tabs_as_menu", "sys_menu_enabled" };
|
||||
|
||||
for (const std::string& option : options_to_recreate_GUI) {
|
||||
|
@ -637,7 +715,7 @@ void PreferencesDialog::accept(wxEvent&)
|
|||
}
|
||||
}
|
||||
|
||||
auto app_config = get_app_config();
|
||||
auto app_config = get_app_config();
|
||||
|
||||
m_seq_top_layer_only_changed = false;
|
||||
if (auto it = m_values.find("seq_top_layer_only"); it != m_values.end())
|
||||
|
@ -738,7 +816,7 @@ void PreferencesDialog::revert(wxEvent&)
|
|||
continue;
|
||||
}
|
||||
|
||||
for (auto opt_group : { m_optgroup_general, m_optgroup_camera, m_optgroup_gui
|
||||
for (auto opt_group : { m_optgroup_general, m_optgroup_camera, m_optgroup_gui, m_optgroup_other
|
||||
#ifdef _WIN32
|
||||
, m_optgroup_dark_mode
|
||||
#endif // _WIN32
|
||||
|
@ -970,6 +1048,25 @@ void PreferencesDialog::create_settings_mode_color_widget()
|
|||
append_preferences_option_to_searcer(m_optgroup_gui, opt_key, title);
|
||||
}
|
||||
|
||||
void PreferencesDialog::create_downloader_path_sizer()
|
||||
{
|
||||
wxWindow* parent = m_optgroup_other->parent();
|
||||
|
||||
wxString title = L("Download path");
|
||||
std::string opt_key = "url_downloader_dest";
|
||||
m_blinkers[opt_key] = new BlinkingBitmap(parent);
|
||||
|
||||
downloader = new DownloaderUtils::Worker(parent);
|
||||
|
||||
auto sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
sizer->Add(m_blinkers[opt_key], 0, wxRIGHT, 2);
|
||||
sizer->Add(downloader, 1, wxALIGN_CENTER_VERTICAL);
|
||||
|
||||
m_optgroup_other->sizer->Add(sizer, 0, wxEXPAND | wxTOP, em_unit());
|
||||
|
||||
append_preferences_option_to_searcer(m_optgroup_other, opt_key, title);
|
||||
}
|
||||
|
||||
void PreferencesDialog::init_highlighter(const t_config_option_key& opt_key)
|
||||
{
|
||||
if (m_blinkers.find(opt_key) != m_blinkers.end())
|
||||
|
@ -978,7 +1075,7 @@ void PreferencesDialog::init_highlighter(const t_config_option_key& opt_key)
|
|||
return;
|
||||
}
|
||||
|
||||
for (auto opt_group : { m_optgroup_general, m_optgroup_camera, m_optgroup_gui
|
||||
for (auto opt_group : { m_optgroup_general, m_optgroup_camera, m_optgroup_gui, m_optgroup_other
|
||||
#ifdef _WIN32
|
||||
, m_optgroup_dark_mode
|
||||
#endif // _WIN32
|
||||
|
|
|
@ -28,19 +28,24 @@ namespace GUI {
|
|||
class ConfigOptionsGroup;
|
||||
class OG_CustomCtrl;
|
||||
|
||||
namespace DownloaderUtils {
|
||||
class Worker;
|
||||
}
|
||||
|
||||
class PreferencesDialog : public DPIDialog
|
||||
{
|
||||
std::map<std::string, std::string> m_values;
|
||||
std::shared_ptr<ConfigOptionsGroup> m_optgroup_general;
|
||||
std::shared_ptr<ConfigOptionsGroup> m_optgroup_camera;
|
||||
std::shared_ptr<ConfigOptionsGroup> m_optgroup_gui;
|
||||
std::shared_ptr<ConfigOptionsGroup> m_optgroup_other;
|
||||
#ifdef _WIN32
|
||||
std::shared_ptr<ConfigOptionsGroup> m_optgroup_dark_mode;
|
||||
#endif //_WIN32
|
||||
#if ENABLE_ENVIRONMENT_MAP
|
||||
std::shared_ptr<ConfigOptionsGroup> m_optgroup_render;
|
||||
#endif // ENABLE_ENVIRONMENT_MAP
|
||||
wxSizer* m_icon_size_sizer;
|
||||
wxSizer* m_icon_size_sizer {nullptr};
|
||||
wxSlider* m_icon_size_slider {nullptr};
|
||||
wxRadioButton* m_rb_old_settings_layout_mode {nullptr};
|
||||
wxRadioButton* m_rb_new_settings_layout_mode {nullptr};
|
||||
|
@ -54,6 +59,8 @@ class PreferencesDialog : public DPIDialog
|
|||
wxColourPickerCtrl* m_mode_advanced { nullptr };
|
||||
wxColourPickerCtrl* m_mode_expert { nullptr };
|
||||
|
||||
DownloaderUtils::Worker* downloader{ nullptr };
|
||||
|
||||
wxBookCtrlBase* tabs {nullptr};
|
||||
|
||||
bool isOSX {false};
|
||||
|
@ -88,6 +95,7 @@ protected:
|
|||
void create_settings_mode_widget();
|
||||
void create_settings_text_color_widget();
|
||||
void create_settings_mode_color_widget();
|
||||
void create_downloader_path_sizer();
|
||||
void init_highlighter(const t_config_option_key& opt_key);
|
||||
std::vector<ConfigOptionsGroup*> optgroups();
|
||||
|
||||
|
|
|
@ -144,6 +144,7 @@ struct Http::priv
|
|||
void set_post_body(const fs::path &path);
|
||||
void set_post_body(const std::string &body);
|
||||
void set_put_body(const fs::path &path);
|
||||
void set_range(const std::string& range);
|
||||
|
||||
std::string curl_error(CURLcode curlcode);
|
||||
std::string body_size_error();
|
||||
|
@ -225,7 +226,7 @@ int Http::priv::xfercb(void *userp, curl_off_t dltotal, curl_off_t dlnow, curl_o
|
|||
bool cb_cancel = false;
|
||||
|
||||
if (self->progressfn) {
|
||||
Progress progress(dltotal, dlnow, ultotal, ulnow);
|
||||
Progress progress(dltotal, dlnow, ultotal, ulnow, self->buffer);
|
||||
self->progressfn(progress, cb_cancel);
|
||||
}
|
||||
|
||||
|
@ -313,6 +314,11 @@ void Http::priv::set_put_body(const fs::path &path)
|
|||
}
|
||||
}
|
||||
|
||||
void Http::priv::set_range(const std::string& range)
|
||||
{
|
||||
::curl_easy_setopt(curl, CURLOPT_RANGE, range.c_str());
|
||||
}
|
||||
|
||||
std::string Http::priv::curl_error(CURLcode curlcode)
|
||||
{
|
||||
return (boost::format("%1%:\n%2%\n[Error %3%]")
|
||||
|
@ -370,7 +376,7 @@ void Http::priv::http_perform()
|
|||
if (res == CURLE_ABORTED_BY_CALLBACK) {
|
||||
if (cancel) {
|
||||
// The abort comes from the request being cancelled programatically
|
||||
Progress dummyprogress(0, 0, 0, 0);
|
||||
Progress dummyprogress(0, 0, 0, 0, std::string());
|
||||
bool cancel = true;
|
||||
if (progressfn) { progressfn(dummyprogress, cancel); }
|
||||
} else {
|
||||
|
@ -438,6 +444,12 @@ Http& Http::size_limit(size_t sizeLimit)
|
|||
return *this;
|
||||
}
|
||||
|
||||
Http& Http::set_range(const std::string& range)
|
||||
{
|
||||
if (p) { p->set_range(range); }
|
||||
return *this;
|
||||
}
|
||||
|
||||
Http& Http::header(std::string name, const std::string &value)
|
||||
{
|
||||
if (!p) { return * this; }
|
||||
|
|
|
@ -21,9 +21,10 @@ public:
|
|||
size_t dlnow; // Bytes downloaded so far
|
||||
size_t ultotal; // Total bytes to upload
|
||||
size_t ulnow; // Bytes uploaded so far
|
||||
const std::string& buffer; // reference to buffer containing all data
|
||||
|
||||
Progress(size_t dltotal, size_t dlnow, size_t ultotal, size_t ulnow) :
|
||||
dltotal(dltotal), dlnow(dlnow), ultotal(ultotal), ulnow(ulnow)
|
||||
Progress(size_t dltotal, size_t dlnow, size_t ultotal, size_t ulnow, const std::string& buffer) :
|
||||
dltotal(dltotal), dlnow(dlnow), ultotal(ultotal), ulnow(ulnow), buffer(buffer)
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -65,6 +66,8 @@ public:
|
|||
// Sets a maximum size of the data that can be received.
|
||||
// A value of zero sets the default limit, which is is 5MB.
|
||||
Http& size_limit(size_t sizeLimit);
|
||||
// range of donloaded bytes. example: curl_easy_setopt(curl, CURLOPT_RANGE, "0-199");
|
||||
Http& set_range(const std::string& range);
|
||||
// Sets a HTTP header field.
|
||||
Http& header(std::string name, const std::string &value);
|
||||
// Removes a header field.
|
||||
|
|
|
@ -33,7 +33,7 @@ enum class NewSlicerInstanceType {
|
|||
|
||||
// Start a new Slicer process instance either in a Slicer mode or in a G-code mode.
|
||||
// Optionally load a 3MF, STL or a G-code on start.
|
||||
static void start_new_slicer_or_gcodeviewer(const NewSlicerInstanceType instance_type, const std::vector<wxString> paths_to_open, bool single_instance)
|
||||
static void start_new_slicer_or_gcodeviewer(const NewSlicerInstanceType instance_type, const std::vector<wxString> paths_to_open, bool single_instance, bool delete_after_load)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
wxString path;
|
||||
|
@ -49,6 +49,9 @@ static void start_new_slicer_or_gcodeviewer(const NewSlicerInstanceType instance
|
|||
}
|
||||
if (instance_type == NewSlicerInstanceType::Slicer && single_instance)
|
||||
args.emplace_back(L"--single-instance");
|
||||
if(delete_after_load && !paths_to_open.empty())
|
||||
args.emplace_back(L"--delete-after-load=1");
|
||||
|
||||
args.emplace_back(nullptr);
|
||||
BOOST_LOG_TRIVIAL(info) << "Trying to spawn a new slicer \"" << into_u8(path) << "\"";
|
||||
// Don't call with wxEXEC_HIDE_CONSOLE, PrusaSlicer in GUI mode would just show the splash screen. It would not open the main window though, it would
|
||||
|
@ -77,6 +80,8 @@ static void start_new_slicer_or_gcodeviewer(const NewSlicerInstanceType instance
|
|||
}
|
||||
if (instance_type == NewSlicerInstanceType::Slicer && single_instance)
|
||||
args.emplace_back("--single-instance");
|
||||
if (delete_after_load && !paths_to_open.empty())
|
||||
args.emplace_back("--delete-after-load=1");
|
||||
boost::process::spawn(bin_path, args);
|
||||
// boost::process::spawn() sets SIGCHLD to SIGIGN for the child process, thus if a child PrusaSlicer spawns another
|
||||
// subprocess and the subrocess dies, the child PrusaSlicer will not receive information on end of subprocess
|
||||
|
@ -121,6 +126,8 @@ static void start_new_slicer_or_gcodeviewer(const NewSlicerInstanceType instance
|
|||
}
|
||||
if (instance_type == NewSlicerInstanceType::Slicer && single_instance)
|
||||
args.emplace_back("--single-instance");
|
||||
if (delete_after_load && !paths_to_open.empty())
|
||||
args.emplace_back("--delete-after-load=1");
|
||||
args.emplace_back(nullptr);
|
||||
BOOST_LOG_TRIVIAL(info) << "Trying to spawn a new slicer \"" << args[0] << "\"";
|
||||
if (wxExecute(const_cast<char**>(args.data()), wxEXEC_ASYNC | wxEXEC_MAKE_GROUP_LEADER) <= 0)
|
||||
|
@ -129,26 +136,26 @@ static void start_new_slicer_or_gcodeviewer(const NewSlicerInstanceType instance
|
|||
#endif // Linux or Unix
|
||||
#endif // Win32
|
||||
}
|
||||
static void start_new_slicer_or_gcodeviewer(const NewSlicerInstanceType instance_type, const wxString* path_to_open, bool single_instance)
|
||||
static void start_new_slicer_or_gcodeviewer(const NewSlicerInstanceType instance_type, const wxString* path_to_open, bool single_instance, bool delete_after_load)
|
||||
{
|
||||
std::vector<wxString> paths;
|
||||
if (path_to_open != nullptr)
|
||||
paths.emplace_back(path_to_open->wc_str());
|
||||
start_new_slicer_or_gcodeviewer(instance_type, paths, single_instance);
|
||||
start_new_slicer_or_gcodeviewer(instance_type, paths, single_instance, delete_after_load);
|
||||
}
|
||||
|
||||
void start_new_slicer(const wxString *path_to_open, bool single_instance)
|
||||
void start_new_slicer(const wxString *path_to_open, bool single_instance/*=false*/, bool delete_after_load/*=false*/)
|
||||
{
|
||||
start_new_slicer_or_gcodeviewer(NewSlicerInstanceType::Slicer, path_to_open, single_instance);
|
||||
start_new_slicer_or_gcodeviewer(NewSlicerInstanceType::Slicer, path_to_open, single_instance, delete_after_load);
|
||||
}
|
||||
void start_new_slicer(const std::vector<wxString>& files, bool single_instance)
|
||||
void start_new_slicer(const std::vector<wxString>& files, bool single_instance/*=false*/, bool delete_after_load/*=false*/)
|
||||
{
|
||||
start_new_slicer_or_gcodeviewer(NewSlicerInstanceType::Slicer, files, single_instance);
|
||||
start_new_slicer_or_gcodeviewer(NewSlicerInstanceType::Slicer, files, single_instance, delete_after_load);
|
||||
}
|
||||
|
||||
void start_new_gcodeviewer(const wxString *path_to_open)
|
||||
{
|
||||
start_new_slicer_or_gcodeviewer(NewSlicerInstanceType::GCodeViewer, path_to_open, false);
|
||||
start_new_slicer_or_gcodeviewer(NewSlicerInstanceType::GCodeViewer, path_to_open, false, false);
|
||||
}
|
||||
|
||||
void start_new_gcodeviewer_open_file(wxWindow *parent)
|
||||
|
|
|
@ -11,8 +11,8 @@ namespace Slic3r {
|
|||
namespace GUI {
|
||||
|
||||
// Start a new slicer instance, optionally with a file to open.
|
||||
void start_new_slicer(const wxString *path_to_open = nullptr, bool single_instance = false);
|
||||
void start_new_slicer(const std::vector<wxString>& files, bool single_instance = false);
|
||||
void start_new_slicer(const wxString *path_to_open = nullptr, bool single_instance = false, bool delete_after_load = false);
|
||||
void start_new_slicer(const std::vector<wxString>& files, bool single_instance = false, bool delete_after_load = false);
|
||||
|
||||
// Start a new G-code viewer instance, optionally with a file to open.
|
||||
void start_new_gcodeviewer(const wxString *path_to_open = nullptr);
|
||||
|
|
Loading…
Add table
Reference in a new issue