From b0f54e5709a98549dc4811c555658bbca48dc197 Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Wed, 30 Jan 2019 15:22:17 +0100 Subject: [PATCH] ConfigWizard refactoring --- .../icons/printers/PrusaResearch_SL1.png | Bin 41541 -> 55962 bytes src/slic3r/GUI/ConfigWizard.cpp | 1485 +++++++++-------- src/slic3r/GUI/ConfigWizard.hpp | 38 +- src/slic3r/GUI/ConfigWizard_private.hpp | 319 ++-- src/slic3r/GUI/GUI.cpp | 15 - src/slic3r/GUI/GUI.hpp | 3 - src/slic3r/GUI/Plater.cpp | 1 - src/slic3r/GUI/Preset.cpp | 18 + src/slic3r/GUI/Preset.hpp | 4 + 9 files changed, 1065 insertions(+), 818 deletions(-) diff --git a/resources/icons/printers/PrusaResearch_SL1.png b/resources/icons/printers/PrusaResearch_SL1.png index 281d7a62026fa00c912eee93650f290c9bc8bdf4..3c15f95eae07c3e7bc77028317dbe0f111156982 100644 GIT binary patch literal 55962 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 A2 z@T$9h9mVzQ7y`Kd9^aob1prX@-FIKs@AtK}R%xw`QYs_rWZv6#rPLn+^e1IeEL!U* z%QNIz24iyMd5$!ufP&HnL>$H~v4&<{O=uYmmbv)?*4EbO+_`i5kw+e}xD8^Dc)R1j z3)&!W@qTFX5PoZG>vn;5@xccldJ|A51pd=rB=;@|V3bnHr6H^qq@Y*@*(@`R*3fK{ zXh!CG^E0*X>(_rNe({&M1_%Srv0KYBT zGkJ?Uip|YU_U4x!@LqmzaB#~vep<;s=b*4DA|;fG;wmxhZ7;dNx#fW6%!(4s|)Ll6;4e?I6n&YU^J zue@???#dPEojWJ`gIr5Qx+0Rj&-bVg^Ez9!Xz?MidwF;H^y$+dI0!|=T)ZfqJN>#^ zv}kdM&>+J;%r*3#xK4`}Ek4F*)A1H9TC`}T4 zd$ljUOgDRaV{@iC=8)0=qzqHGWo8?<2oq!-!%O%4IkARrhfU>P!KK(PEn7JrOo z@DCw_vS7;+1e+p6OO|Ot)Nm-Swkt@=YYy>-8_a_S(Zbl(l>T=f8k+5Bn&N$VrUfGroBomU5# zJeE6=oA{=mc=go>e;jN1g$ww?Bc{>M_xS8{{rUdohkYQA<<8|xuf6tY*)Wfw>2kiu zXP?9Q$IAPgUmhW>KL+|^xv%o5VEr-BAItrgM*{1Qf&N$?fINJxKL+|^c`)*jvHlq7 zkL7{M!^HYypg)!eDGv?nkAePJ9B829r2J}{~^uYa+ALyn%$q_`-8>n|4% zy}GkY`BCyApcCdAMxe~vHr&OJ6V5x9}fN&0sSHJfb8|#1^S=(IR1<0_^-b6 z+ZW&4y-y16e2*V}4(IxImu~~;0P1`_Jre}UAb9BUO(GOTeW00SJbd1~*&~2}1cdDY zPTLATV1V4tA$@?{zv<(!+`G{&A0T(R+xw37JBYfx@ity} z066)vXMggU?d^9j_&q(O$#nVPEsoyNBj8`ZdE=G8;hhf}K{q@|#Jzsp(oe5XNDn=Z zAo-9-!ovgL`g0FRog3=oQIz|O^*atP{`^HO0m%Hjzw6~sU3p{Y%1sJ_?o4h3`VkZL z=-<3STiy815&4K9&Iid&c6rnuYLDC6>8*$WAeEG6fI$ceAV9WfuE!!+KnLsFHy$AC z^ZjwFKgh7Tb=&4XV*R#l^pD{OPDH+THHb?W@r!!^(49R0!+%tlZ@o2?dvHiKj=nZs z)*7mmvVI*A_4?IINHT}#@DfoX3Yk4T7({UfF=qE*agvN^Uz~3G5kufGEDpcJ$%YW< z`k}D(L#Xw!-tzF{@VC|vM%?&wj5vHSj9^d#QujlVSfBRfkW7{jgQSd6s;%y%gAhOh zTF*VIogqhJIrmIH$gsI}+va}7VeY^g{eey3q)Hem5rDO%g^#RYEBx27{rh)$De6H4Tq3=hd)UE|*>kL?G7peaX2? zod6=DB0;Lia=BEI<0pdfN(2H%wa#jupc*QKNOQXDd6*E?Hc|*Z7^*7uGPGIm!_NGn%1vsk@4nvjc7gus zXYnJ)554x)7hiw#uYYy(*mpnk)926ud+)sQAN-%&$3F3?t&KaJh$_&}b*##05;=H5 zLZseY5h?D&Fzjq^Z}05noGUoYtWZ=19+N(iVI1->j>E8AE?2A7$&;tfoja>$q9Upy zqN;TW)xQCl*hdWe9R@t>`u~51qU{Ps0gH{ zlS(du*qF`xuEW}pBUJ>Xi3U;K-MRy@htCziPQLWoYxiES-+_((TkqkmV|aFbKWzS~ zPrmZsf6LWh{LR;XY4OQ_b4xa#dipnZn1CGuI&t>+74j-Mb$K)&$_M~ za~@EJQX;}s07{)wNHY!haTu1X{r&xYFXdaFdivbia{w%Lg+WM4DWXn>med&3s^RZv zR^*Q59%KCuozW5h`wo8mH~@eBEAQi}CtlfefMWx`L4hF z`tM#odFu51w)S0te!fSYd|9zWR=YeR5FB%6M3e$RM8;u!@9l56`)oE-h`XDaSqesh z5bRRQ*>|p9`|4M|;>CqjQB~>Z^K+-qJpJ_39^s8}&q6m~z$T2v0-rwH6cJ<0I3?=8C z?d|cxmG@7aSmptu84)6cln4)BEk}AJ>t6lDCtiB#C42OMj(dys+Xea`Kac&Z@%imy z^ZvEB@cOsm6|+~K=|A_s{PSP>*S_lqpRqHdPn_FoTOT|7!{77XPyF*YS6i>lZ<(ZZ zzQ>O}6Q{+8m4`dYaU2`aqaq_1Rhe0o!l0{~W5aM8{OrK$j zgREnT^((QqOGujPa=8?d*?jKq03>8)ma6iPU@3W6oj-m0cYf=)-hJ!sq>_ihb2h<9 zyLfGT<^IN7e|TkYXI0AnIIKz`;DrnoLd*iQv zb#r+3nV-LmzjzMa{Pg$#vA2KXAOHSu{?SkT&~y0SO?2%W`pk3B{=5I|i+|_hQ}d^f zf9SrOoYC@w^UHQ|bvYtHfr1EDE1|{82@z;wq(Basr9cE?G4D4wHhj!uH+mT>vKCz` z5U;}{d^YP}eChd%@4xS3iHOc@mNvH*y`YdH+KGu!4uTRb97kz>;ACh|=%gY*sbLdQ ztlg=)agALIfiPCYYY?J>s*(vniGV;#%?e39?V1fRs0PN}{W30N9P@JMHOB{QQC z^yPB7ckSBc?Y;eX-^cEla9O@+X2x|6wgZ0}sT~6RL)e_PEQ=fe0uRML>Z~p2R|DT<2{lf9tzdXY;FTC)> zU;K@~@#f{f&C|cUiSK&~pP2#hFaGWue|z)eXP&qvpdTN1{?w)488Pf`q;Aoxr4c%t zZzk;#VG$t3)WuAq8&q(YxX~t7tg(XJl%qGI=lMb z^Z(#~_`Cn?^H;aG$8p;dW*dvNaqQUfGqc&oqU(Fbtm|M_B&F1!N_`$3{g!UbbR$Uz zij)Dp^$F4?S`xL6qUgK9^Sqy#CGBh|d2h8$mXOOzEK1qg*uU`ZRrFngMlhMhWI&i% zb$?~z(Wt1IjJcGQOC;+pxC-Q=nsjR{V?ohQgQ6umwFo9H$p-O~*bDn?q2l3rBvAsA zTnNSfxYzgdz1=;FoK}0I?~~lUGLF}l%l++PwY%Kgi6PHqHt*-hH@80a+|#Gd9k){( zoNYwvV%Yb-%X|6idtc8NzrN}FEUS5nxc2@#+q>KEej{cJs?cE|gmrF`4>7;)TJD|? zbGKN(1EBxMr?CM4{9C{EU;ooj{PHKx{wE*9XJ&x(uYB^AzqWhz*DuY^p0SN5pZPCe z`uczK);DI~_Dl!7bs0Yo=;HXTgt9hwE`87c{j=Y8s^2lSamd4}BfFt^UZ(x+G+U6M z3Kp~7McNDpZz^6oI0nF2|a z!c1RAp3mkdY~g{gedWEGp!Z~VbVuc|QJ?9yy;V)OLV=QmHkFk5Ug^+VTr9>-iR>|D~Xyb;^0GWc@2yDd3%zxBU8C3|*l z|9R}5we79bICFY)zS(6)fQpp_HA+gh)Lg7P+r4-8#JN+acSi2}{`)a9M;6h2g`FqVH^`f*|N(5{Wwwde?0nTUj?~nr|#NX4)^h`Nr19Y~J^MjSPyI z2s(%z8Az34eER3%wSvw<^B$$hyRhj3>uWq0?Tum0W}ul@RP>7DQR z)VFKDb?&)u-8%DB>KDr#HtV|06X&1(_}1~`Aw_1zG`i4u@4d_FXV0D(^RQQRduO>< zmKQGWuHIYj?yXkY_kAeC{_$Zv;X^;HjI;Ut#K6^9RvPHW{Zy9KTcE~bG2722FbYD2 z5K<8eQK|V^#v{pk18_H3zYCyW{JDSl_CI;@)nmuoufO&l{;Mnafu|39qQHssKltgl ze(JTihm+6GZY?!&zVmLb1;&%}lq?N9dslY{lsNwE$2Yp!YJXLh*G_D8 zpqAzC+i(5BmtMbgb-A;*v%0#o+|2`wPB(O0i{qGEFZmea#IXg(onhEtmDuCz+TOJu z*hn(7K%_2popI5T#!~hO7X{%&KvbpCdjTBH);&UUhgiQSK!5dP_;VdU_xZ2Aar*fM z@OQq3Up|2!dIGOByvG;`eUe+V)6Vz&v8Q+S-R)hLEscI$^^tX4rL40o{Sb~!VHcr< z!J(Q*Qc|y*T~Njme)(9k8h$r)a0@3avND)LH*M)wC_C4>E*+bn+M-AvrJrrd_RjXD z#n#g$d*sox>@Ke%B?>NYUn!*=%KwN6RZ}uGGf`EMF;)ol(LH}`)}8DxZtuT0V9yrC zY$*GyJkI*XLOL#YcXqy^JMX@*=zi}veq}rK-78nleEsbw&p-Rtdsp5WMmzW9iRZp$ z>-4F?^WM(YMLM>y9(la@&fCBHmERtE4h53>Fr&MYy=`L55QBxYYv($%Ff*6Dn6t$X$sy_E^qv-+3onZZ50sTi#qJ#XWFRVWM zg*Wt*&(9Q>uHk2{;WJO-`%dBF+n4_Gn`h6w{A_;<&gl6bKlW^XBFX!rjF2ew-1l?Q z4lEKOJ}L%C08D1c9z>TE@Uk~R(WexiK|u+Dnj}ODHV#zrL{|=Z|C(;B~v$Io3Jc2@hV!g(%;BCas)Jc^12iSP(?otdTTYl)}`DK5mAbwsD#+_(3Z z``AshJ+LsRuG1hww2Z_4wZ*ucr&wOQv~}u~thRO7DchH2xyxL3uU@=*`NH1W(`C7z zclWO@_e0Y1t+(^F?YYY6W9nwBG8TuLbPzJSWRzr~BH2u5Tbpk5jM=>F`;CQ<$9p-x z*mjrH3F`YTU1pkv(rd;joD z|JUiyoIU_L^3`{L_VeY~i_aalQhx1S{Mx$!o;dZ=E&arl88u`hQcyz`DN&4K!Qx&R zMQb4q5bi1vAUsq75hbM{r34_tr4qTlsDNHnO=y6E(zEm>`Vq(Hiw$RD5!1z(&&+xo zhI!v7L>X6x83AL6qf;PU0R%~497DWmcp@ZR9V!~;<4W@wtG(qumT9pYjugpLk)eYn zAH`jV;l$Q#b8BOV&S5vMT->?3pO>*Wj#n;S`ak~G&kJk@Xbh+4^HY+Z?kp}`>&G&m z%W`m2p@N`8!jKi9ix;(+ne-$jdhO@LH7iv~ zH~}hi351+n%olwMGDT59E%LZL*>#H(Cr)U}5_9F;FnD2rsv_Rvi) zi9 zqnjbkPHt_;FoJZ#r7;@ipz8rO74>Cc3F$jzD+71E!(x#*7nvo4VHj3p;qgSpFx#9* z%0lYID936b%@(BRWzE1$Cm91JNqaB!CbJ4WO1F1i}%HUU^;nDX$I(i@`FORHITQ8)RiG7QATH+NG75)-q$Chi&zMI@ zZV6X+V7mO!SsWKUdG_>w_nqUF-~aOJ(t&mdFh&4c=esq~uYUc_pZe9vFT6Org?Re; z9-n=V=Vycv%FwE;Yn2WyJTW?{ce8*YYfTeX2Q}+K0va?IrHE64N(d>nxF~ASn5vS8 zQgUe0(Mt)8YL`lRb9eW`kli&8%mme7Qlx~2!-Em@`lwZGQ3=5^?H2)t1}sDYWf+SJ z)I{gX@`k zXrc%Z5&?=NoYHX;%ENI^>E)j{q#@%=~FL#<*n~~?v8v$se5kQ0@Op(m`l#CR23u*-187!rE5CTUO z&k^pWj9!M4OC3Ek#`hevluzttI(U-2_b8mkX-2a`A z{h2M?3DB!E3gBoxJfzJQ^4FeIND)~< z_t0hZA>9}EVF^Gn1E5$RVqS=taN7Y!zhhzONn~Z)m z-1~Hg8ohEPDS!c{&;%q>i2z1Kn7PPoZU|>d2oW$T5}Cw1jU>&35(!P9M6DnL5G^O- zszA`fED$OeJbiW9zaPi9?ew+T@on^hert9N(eLSOcd>Of&3Bkq87$BU(klr!=-J&# zsEC?cQdMccL=^y8tw2UGrrwODr>mDy>qkN^cz&STDd zd9^+6Z|^SmE^lAEx_f1JwLM0L2vh?O4L1)3q_VcCUCNcCELjvKGPU%x`R3MfD93TA z)u&V}!6C$AW}|Ods06GB6PTbPiK-L`YJB$5lMnPw@4y-T&(5OD;~%`c``bJCC##Ka zKEHGgp96mUV|Y^ZnU_BCGx*X^{Q8$qee2p)<<+-u8|dfyARzT>vIfuy2NV*7fFcf| zNUiFe^kd6iOKX7=&1}(Xn76zi2vS??ItJ*VQ6z|6*9)Uwxi*>#3{aSf04SbR0dvjE zh^txlk+Q%eltnd(Xe~Ug{#vU#CBviD7nwpRg6bKAM0i4P1|kB0nyCsz=JU>XRkEng zQeU)}t}hS~6M>k4QNo8ju2w7SH({nJiKr71K@%!9bjskWigYy48BuaXuVw~vwem>a zyx&9zm5wZ>MB3qurH@9A4RAnY3sOI-GCGm2B4zJm^w-e2d2%%U8=@iW(OG*2t zB|Nz&1`ukxjzc$NXO4487^AF)Scww7f@2{7G1Nw&yi55bvK}W4-d@I+#d67(|9in|cZ6=e#YZ@0KB2)>L5OGyci((ckqNqhj z3Rl)}goQ};5GAqzBCYyU)^(!blu%k92)rgC86M%`9<2zK6cvRw1)Ky%R{|bU=w3p? z(0l=?s3oXSHFfiTw&?rWe6~m)%|mB2j~lIH3@E`Uq@=Vg`LdThM#oA+Hx?H&lJH{6 z9%XafKO^(i=%iJ;x+*wzRH!?|edF}ry-cf4spxFSq|-r$2RS zcD}1aT{jP4s?WUiiNC!3r$6<(>GUUWiNN^LK*!WCS0E~CHF_?VY~B21o$e4>4~(g? z#6-KEHftPg0<~462S<<0aHwcPH5jyoyeaB%qX|@;iI~&8vRuH{IAtwO!ynRt0IFS2 zdIU*TF*Boerc|L+t49nWo?F~QL`7M*3^5Z8(PW}ZOPW&GbzMq*ml8{-rY;PIQX)tM zpe^VaOr)tGjS;)MmD6izt`vZ+u=rjE01ccQgfj0>GCsLgC6YOP1Dl2!sj zp(Cue@FHvK$5ljz-i*BPbf03RK8s9ZFLMstgfF zai|+K={S0zIw2CQgSi@w6zcj;JQ&E|ma%kz^?R_i%OSXLo_ppe|1AFMFaE!u$E&X$ zPxWrTava~Wh1dS@>$(lr0H6YG>SpWb zgIah1Fc1`qQs6bLMv5dd3KrO_aAxt8YOz2U0j6tCp6(U&W@K9D8F1*c+ z64eI+^+8%OcfxbLXKNVU1n#f>{>!Z_-tc*-=f@#jeb~U zGCOKpt?DXbvKuBx^iY#8uA?ES;SMDM6TqZ43Z=Fl6VfUWL`4!{N=9Mv;t}r7sa2xT zg;A9oBf`sMkjT{iaLQDXtOQ>XRc#$yYGYC&B1+D=j77wwuHrzzBe~b!qL`f$0bl5~Q&Hq2dK( zv&yF4*Ht*(i$_$iD{voHr5o|Nui>=}pu+vkw|@Dxo$Xugw0pZi2kLoLhg8&RD5hQX zhV=D@b@E;}DN7pXcr)0kR;lf&64(JooTO{RFa4hT5a$e@gx}@ z?w)gQaIc|r)hYa6Jyl@pAI%7Ocqx^*<~~G>tQ?y%qGV-s2?~Oo_p{Y%-%B=O?L8Wd zl)BD3v()A|h)^mie~RmKa@r%BAJQ0#()<)B3dN%qxUl*hky6T;H79nE7r+$0suA}G z$YGy+v0IY+xi;8{&wUL)@)UmM4>#X<`IQsLKe`X&Edu>;1?qYdZSm{I+alK?bWFi& zAf~DUvbv$H+`s?@RMg(3B2a2F+axGfk>yDuoJ~GJ*=P^a6EXbhUyoOgZZ{7bb;!DY--_ z)I>YNfTR$WYK;SdW+JtSPxWmAh?)`-icFO&ZDE1xg;VMxIYtpMB`etv{G+-WL1mau z?VTQ2(zMF0n=$u!&BqZx`zD}U^IPcEbq7Gd>4$5e*SBKo-q=DU0e}*r5CqsLYSz0(dNN*xZW{M7B zh}QV}(6wSUJlN`?MPlN2w$iMs4*-Y|N(hm3 zz@eyp=O7SDof#Za!ZYb&=0R7Dh*Cz?3hYir!a1_ITGyqdwkB7qsu1+>l1sRvXSheH zj2@LxQj^L^FJ9dDmIG3sQrce*+1d5;PG$p>Xi~8{9hs;>y|HbC1cC<@6(T@Qqt+0s zRR6eI;A`7px}d119ntc-2$9;$Zek3y?H?f8?*Sj?0n2Rxovo?oq;o2&YmdZ$iV3WR zJCtTts7liSBh-WnZC;w!OBBHbm0Da1C6y8)AwkKbnS!<)d3A5KTrzxCf7csY1U1MQ zAqW+TaA8oWCJ`Piq*jwjRZ~iR*QJyk7*|YYUFrbj@SO9i8%mjv<6h1(L@JgD)0Pem zLU&6DWIx0 zmqCOSXeR)|8<#>5h}mMkKdfr9S(w01G(?>mq5+193iK}Fe)lZ{ZYmES>$eH?gTL2s zKX~Kd63LpNq3flEfM#JfSCFmZ4igIsH5k{domO^D0_wCN2x=uBVuH&-FX?IVH`b1g zh$ujf-o@lVNglJ8G1c}kqTwayRo7VwMg$O64P<&mlp6Shb_O#QNfM<7g-S>cC}a#> z!N)*Bs-$!YC};v`Xhb#0DGY6S32FBD(cS6be*yLS9fTKe(B)0Yqh_K^A!Ic8R+Dw2)YjE^+hEYcca$wy zbMQ?<)ja4P&LHEkhu6l>A>vp)8b+v@G-HWVOg}`7QZ=|s7}P}IC1Ub$!B9amRTqE} zL6qViNs|f`ssg14$KnRdM4^Tl)CeKTa0!l%OuBUS%19-_)!5Q>zLf}riD`U&e!9`6 z*I%G2%q-^;(X*|;GsOY~GSSxCh`ZNTdu0XP%^n^a*6&iIUl)xz$eY%ui5;%mv|WQ; zjW#Qkf{9)WNJNV>RMVv0+o)P!M6JdgJ)k^b)QCtMV~q&5PS(`HRdpT$Y|Cr`pw|T* zg{TQo1x&22!si9_0?@?NV6Ymq$vKY(RZ3(aC{oDL!<3l_O_T&$1nO?oX}VrayMq)3%LmOukF*U_f&O8nt~-*Z zRbc~WB|dI1e)xe^-d`=6v+jws*49hO{%dg^j4D7dZtWC z8EFYhOvTjGku-`~U9gF$p%pK}s2NvQ<<6uz)7{6Bu;@yT2*5m0f+P!2AQTG-Su3_6 z!s-&Nr=Vt-)$mm%C*lCxoXqW;#BM%;`i8owWcvnlfw!@+bv`Xj8-C_}XMH)gM+N+rqh_lIm)p zT7&CV0oGQ%F-34{!Cw?FW6timtNDOZ0%od6FgnAdlmd5+66&M|QM2<-aiT~#Ygfg_ zYN*ZU=!^(iuTIRSZb?E_4*B{YVsxxoJdFqdfh0i5TA!p-g6?1k_d~CL^ZBMXY~xDQD|NG4&zvo z7N~0K+*wMVW|5Y+s;k~r9&vjIm1U`QQW4c4OL6N=MJH#zal#Hrnuwb2g#;Lkc8LW* zO+cvDh6#}Mu)hiT?(!Dj{XQQk)<2q$erxjKU8=7OL^hSwh!Y_IwbFO_frPwjc1Lw8 zB`6-%Z)|?pp%ez!r3z^2s`e3uiit~z7VDYp;`T=+q9rq6sG%DL5g7A01WLHrT($2z zMVLept?olh5!C)OL&?r0(yN+Cr63}iYBZPRkj;Jlg*)uT3g5Zfy*PBH@(L|Z-!!flatLNhtHpi=95+j3{^d%+UyOGKrPlMBwGAYAuUcm z`CYYhmp3cmprlw1;ITG>Qq%-k8v6#MCd_r-g-0wbx}LOJv{QtLo~XMvozDn z6sVpO&rnqN9FLC7pTJYmqU)zN6Y=KA-Ru0KN91buJT$dHU+G*o?hD`C(}0?9YiMj zz+^KYjWm;Run@&j%ZZx7Jx$?g3Ulh*U%R!`7cIE6&eKfMn@P~tr$%9=AOr|eg-KX- zU=fX8UP+jN+R?1J4Q6RRpKtgu!b7#w)QcKO@p=f#hD8#Mwa`dE(?g~s!6A&UM_xVE z>4J)>M#SJ=+W8nmrnuIDuks*en!0aE?xS9Bnb8}HqwiAI{1#wZd5w*1f}ltWu@WMZ zB36|$4ypQVx_9-(6!C*-wX-R7NH7vi=+{l+4H?yij3{K( z)}z&Ksnp1cqz-NQRnKEVsWBL4Wig0rf-%CQL~=9H2r*hJrFbB%B1!aH8yhKhr8tl> z;36G-VI3$X93r9t4{2+8DO%xNfG~EIh_4WZwPK|SKrPPbVStjxNW%U7eeb(mjH2OY zOo5=dDhLs4>FNgt_}kL!_Y3Q-_u}oW26H=dJ^v+8ll;nBZdR{&3mG19CJ)>94AxRM z)bYZaMm<{hBnYT`rMlMNzOJ>KqPCMn%g{_(zm}A`sNCG9t(n^Z>Jk;u78a7pW{6f% z)S{du3zVubl>}n7TwYx5&SzS3WF)eO)rEY;v{rY>lnP#FlP5kWT729N44btfR%@0x zG3KWC0y*bQj0kV@m!Yj~rj*8>YxWF2`*Fqxk*?-n9P7^JIJ$qGmYG2HQNz^Mx@U@t9!xS4Dq)y- z`5H`t)K(r2LDaozh(XL}fQXlQ-}N&!DPvi!0#QT-X5xlrl7*;MEB!;<$2RP%R}jsd znL_1S)-hGn)F86uwvHnn;(d+%d_Hr^ZWDR|Y z9JxQyVi0Teq5W~lS1l2%?CUi66n<$#jt4=Ri91q5AJCe-#G!e1iqssQIM_@Gt(5sN z!U9GpC-fe@}?je`Nq zstF<}N(N|MH;zRmiRuBjI8??`xo2RoA|3v)8B5B5h`<-mxguoBcv1Q=oQWflfv= zl$27OtwAe97{Vf00Y#wNbp_&%r~#A7_dS@}YW(qf`=TboCg)rZpCViMrVTl>v|J5s z?Iu*RSv3A8CJpg{x#)*4_Y&(5H@tYGtfMQybpO^0T&^Dt(_m<smFDx8+w`NQ9J74QRjvB2SAAHEkTAA*3P{v?gz3YI}IH|q5&Ge%+ zlG&hNGe?tsa{xH52UJUM0<}}5wx}tAkmRbh-y?w#b(PFKVhHIp!IDCiwc{T8f&@j< zNFs?yYGx+UhF~qwK4LmeG1mG@rW3A*IbnUB2&>UnE19SRU2Co2WW}w`zDF18_b9&p z(1E_z!_w{-f!1gOs8;xz**eJ@(6tXr)Wxb%1B2d75r7y*A0oVNJ2mV&*iGdFs)}gT zvQZL1N|k5XjM(eGSl_Rq60^D-LF*J53>PUPnL<;Fcwr8@s2fBnq>o^U7(7;C-X~pT z)tqEy;*vCaghcVR{Ty7g_ScCnflS23iJ3FCt`AjXZPX~0?=Pf)X@BLplABdUDzVp5 z7b!VT2YUUX1O14s+3U6E*1E1*MMYV?s7bE7H4OkjZQ5MJeE>_a)CGY*=r=1? z8eNycV4);kV?AnD8dBEH_nQ7DiQ*pNY=?v z$`QNd3fTpsGe-)Cx(5_?;3ph8*(sM+OP^G#nhMHlV6axrAv=eHXx%st6TjL%TD+rX z?W5MYQN1nfQJaM}wYN?|t$kL&8FgI^VQqXQA>tHMF$4<4F`{VF891lRi5amhv~i^P z%;r6HrtWZI#0%9}s10vVP>$CnF7OGb@5^*@gpSzV=vPi0N$H9~8Yx^47Mr zUJX66M$!i|@*c9>71kdSpySYGUK5>UvJ+5kn#m&s)&a)`)RqOW3Vm&WLcvC99J7y( zfL@nFS_|vsA<|a&3TJCD=4%V5s?VvsV?E9Ukf5-wNds-mlmSAKn3G*V9HWro3q?1q z5Y{W^oz3Q5h!x1p%T~!!GDFQ4h=rJ{=rq{cY$bIK%Y#AA+Pa`_QA0{>%Mq*ttj#Y8 zB3yQM21cFYnm4+hCk5Kdt3Q}Y_HgCSvHs`){Xi<|WI}uGP}Nv0GA&e8H{=n#A9Rnw zIu_Uo z_6y)z9xv?C2T08{})DM}a{XG};b z`Se!*!s+frZ`Vrq*2Vn%`3=9i7t7F2SLBe#!9APKOg^?bKR=((JG-Qd*RSOZLn}Hx zn9Z&aIkvuy?h&oQx&W0KbcDFMUSwM|jYAPl!UXs_JJ&9!2=2P$*L{^c!TQ6d>E6WB z*0m;Sj7FfwMkWh07BVfY5^(K0zXpZgChaFb+l*wrq;sJ+CYp^ zD@0YSrj$7G*K3G72$D*I=9s&Tu45j(BezVC3H!2~l{_0`lX5bfGd9 z^e6jtVkR@j*JC-u0;x+$ROr>{7MtQ!Xg*CH0(D4Eq!_Xi!?;c>wV*AfjJDhhPcGt9 zfJr0jIvw>Vnr4wlvP9{&vHmD%bgAtWagc6r_BJ4<4qC%(sJ0I1LezDJT6~-b)2s@N z0m4No20&4ilz^xd%=QYh)JCRJ+6o&0*wkhX=vCH=pjspHw11{1IhWJ40B6jU;M6owdOMxC?F z&Gv?|-2WpF%@7*_0F~8H zSBV8RpincBo3P{`1-U(}KMFu!WAvuhGiyBf&^yzsb)X% zN5A+d{1N^W!!Hd3ZKGkd8*aP1)gmQIWRa}O%F1h8&N+LpIfozC+9x8ik`$>bvNB6r zC?+FM?ua;N@44ohmodgPz?;%cKU)wz3R-jnvMIeA1L~PCK^Wk)GPoD<)5n+p;_`=I z7+#os%j-*gxzCT%H{(g{uckvxGp_gGkX|q(TFpmFxBw;?uo|edFtog->$b3S@RrhN z%Sb39O-D7xegj{B*U9OC!ya{A)2mt^x8D`2Nwgw#w@&p&t>18l(YJ2szsa(WOM&&U z;f|r-mPW~6@_->ksMe}m@yV}V>WOb=se&5EUde<45r{TS%V-Ty%#4r`9JskQ{A{<| zt?UYx!@92Ghu5|K{KbFvy;I}qRn|SX2fScd4P?TJkqBuJ=7{Ur*Ja#s6YFuVo{CyS zM06P$-2i@$3Cl4-xmhiZzFn;8UuQPuS03A`(vchR7OY>_*55iqZ>vv~z9Dz?p6Y9Z z{$u03T?rw{IdiVL5T1@Gz+bVJOf;olK}1rOs^pZ7Z)y&!AVNZ=qgde-o6+FD9>?vR zmtog6p?tUa^J(7guAgT<-XHeCmH7cV8N--0LYj$}RZsSj%qOl(`CbI9rUcKf4{+Sv zch0Dm$$TYmzkTh}d1}MMI!c{5iqX6!>({dNx6#mhC9l`o7d76ai8trQrF+@CNH-P8 zY>Fo=Avq`2T$n&yYd zV;HyK296)0?S+9=7Djc5RvOeic`6MCBeUriIf6Oiz?|7Y>;PCZwxv8g zKYw`Q^Ht1V&(|x>hmppB5?~=v=XofulJwDhn`!erhm2$>MK%!nqJP$VL$6x;Lbj4# zMc5yY0V~ByX=4L*t3!9hAP{de&hafZ^xNxL7=I;hIePS4SKclgB8fB$SbkMB=JpS{ zu@Cg!)ZsCvWrPRCfhObxSRy1!MF5dt1k-$ba#nblrV9}J$l2v?mNO1}l(I^UnQP{h z=^3NhKs(S1{NCwi@L;`~g+uzBNQrI^ie&LV)9>!vZ~f2(9Dln90Ne^{j;>wAT%*nr zUHGQ8>r4wy^G!ysxAm)R{aZKm*W%{-oQK5;^$sYHO*|0HUiFamkys51)^tSgbdZQ6 z8KT87d3Gq zz-(UKcbRLm&9L6r!w%ThLq@Ji%)8y*;5nzBynJywV0V~bT-IH|o>L_2JR@SB>*{3u za;i^fy%egi9jKWRH`)iS$tB6qrt!5#Q&+%fpUjNHF*oIcA_9QvFpC}_ZGfWHC2xoJ z2L5fy%-4ACU;8KjQd|Gl4gEJ=dQF??*yTBTr`N`lue4C579hX`3!y2ms~yotFwLtB z>-KAI7wp(u-eIT^ef{=tIRxiZRUNb)=x z$|hrNGS(phtyb&JgA+e>-A4>jFE0-RlHS9wkOZv;j039)1S40hGJA@=sCZ$j^E?LD z(>^Wwr&n5^GSJ_8BWkI$Ou9!$PgT3s-Uj#+jLfD5n@fIz)=BG>?b|p1n=eNG3ak4y z!<$!a{qdJy{*Qll|NZgXpO5!Lg0h7^=cLQ!j^O&OPoYvsJ(S5BoKxHfA*pG?A=6MiLyq_LU%(U=y@PCxqL z@xcH903ZNKL_t)z&;V*AXzWCmI{6&We(<|RfKIgo~$qD`tzsLZl^!}@V&gceERen_~--0>bzORgkuGigf;Z3FT zZmRNzt{Yo__U!q;`kP1J;#>K(mo09ktsXirZkNz;NM|p0>$V`!5|iup;G?g!4Ji-- zYEDdvVAECP&G)yEpu@cuO=|QedV=P$Vy z4$6=KdJ`OK(YD#3=owgKn}FAUO#A*0ALt^V4L9CuE-0QqEu&CF zVP;KLMH`RZW^LJMHb|(4)2%;zu-c5gf8T%a`{!rp4kj_XlUB+wnrtX6^b{rt#M%u) zP1MZVFlFOV#!VSk?jy-%mL&ueEj#x@$_3Wmv}f{me%V5`Kp*znU(Ys=#N^9)*!mG& z9gl#Jp;W&9A${Ac0QhJ3^{>9|_wmiFyNBme8v*e82LG)~Z|7OPia;Qx){H;$FI)P$ zs7&-&sfp1!8K#cV&-%q|NX_c{bv5|Ybl?%)eFX24n7@`O0P+~ zfLmVZICP-fI#|^w6@2ySX(1JX3CRI#H#AXFmdF~27F){}9mz$Vzxl!5)15+tIc@dj zv;B{LzRw)~laEH$0;?nj%asS+a~^30`7#ZA+F%?ag@7Pg%9`4AAf0vxyVyv#9k_F> ztE0~uYw~^lF5Ld0H{DYHP|09{WRSaHc$&28TnqV4pkq!|Fo+q& zn!-{yp2qG|)BZIG#6Z8gj4vMT9)F>i*N1iD{A5*~f|QyOp{O{WIi{D3!~CW4WGD~H zNbEyUj7ra#6X}eam|3HAgg^q(=QSzWgFG~vH>_ni9SqbP%{M9d@z!L&qqgLqTqH^Q z8L^;2e_N6%FR3wgP!!n37t8VJoNyR2$9D$a_WFKpgj6U)bt8 zw5#*}`f!*-7F%9@FpR&ro`0%5*~I&6I~_TgC$}b8RAdcJL7y<&!@kBymMhF z8jDtj6T^mDH<1g1Zfh^F9L89WWpjp$d3ZUVe0rrH{^I53q$&!<%WA6oP0j6xXU{4= z7jT`AcGJ5j`Cu&f${Oy)Dziq^WLDJKqDEvNpWuNoNw7p3xhM~R<%ZnqM{rS&!A-Tb z3*2;v-j7Nx3W9u{(|9A++h^9j0eyE=NjJqKu48HXw1la5P0)+sxZ~xPD0Zp69D5}a z%!Ze{ok)W>J-2{-smQdUMx=*8GZ<>d6tg)-FXAdArn-AF*T33DlZtd{1AMf?1L1=a z_X@7AaWUsyalVC*({tf(0zZ5?U)Jkua`IDx+rraQPS(TaUWfU58n%~laYcVIX)O80 zOMI}JKNw?g#eB#*&8WnBuC=mDn6)IEMv4+r1mtF3${mdVF`<`8<`VK{suZg=M2`%X zQBo?c;SBNyZXmVMVc)!O|K>3Q-_iBj2<6pv3oL*ohqGC?)iTs4=(15og+5~4BSFZW zgE=%yhf)SJla(n2AmtK@GF8K^j5s(lY1iGw7dY(pUd&zXaT#aJJN_fCWzQJN4^-m)!KbynYQMZ>nHS^)gL=cqZA@TS%m_ zh1f?#O-Hc`Ae^GYqn4a0N@$MP!ZSm24z+Cj7 z>|zxfGFKE(hm4|HEQ@MhO(){o?4I)yd;!I{V)6Z)&uR^FGD|OkifE-5ETwN?IgW0@ z5*LgY3_y19K>K7p&BP79oq62rOE{QE^{-#+K7Y&WJ8tVsRI{|ZZ$@R^x$2dAo4T{I z1P&3)^z~Mx?u8d|85Kj+tRpd^j+nQ|jbZH=Mxx>0fe-@-!5OH%e&gV11BNU3+#t5X9P`;|C>$_twcm;>vTVzG0H?zy;$2KT=jEyhZX#f!8?Atd1GCpxoBKU} zWG-8|mtkHBHeMGd*b?(C4dp(HyKNZ}y(-&zc(0pV@>^TWWBrF12Eg(N`nlbLdEC^P z?_PPsNo@}-ghNE$dVYm%POc;baw**bF|9p5-1x`a@j=Oz!pv)Ej(P}Phb}bl8(+JC zf@Z`aXPZ$qSJVLqM`X-bbE)~^TJO``=3=?AToY@kHv5vzh_w1AN%{U!?p4W*I zX8V#=L3qhx5=>_1mR)6<4bhN*n!+mCG-GZT;aj=zt6k3H77< znD4l)^Oukyzgl>CrGug#o^XzvsbBz{?jjM15R`-gqr)OL(8nj^KY91$qan(CEiY1L zph^eXHT(dp5^6nBT}7VQ9EUj*Q{eI{{Q3f^nB$@0Pv7OqidK^;2j&SiOjeA66=SOR zN^E?0X;VUn&;jKsa-Xmq#`LCdZDz(aGnqB6_c0BDpYBq>eYwgrgcOpk-L(?_&MdW0-G*Kl^uIw*+6fR0<3fiA*Av+nth%Ia?{R zA#(53t&U-Os?qRL6pR>PNk`6LOf##O*_3W6)Qo~COhDF0XXG4U8D~@hj-VNVo97YHgs1`U`4~MH+hH1cMVh6t_ zuatX03}`o{r5PYbm=jiKsImfHW|^7jRS-FlN?IC>VGt8$t+>?im?A@AA|nO>?yzvC zEnqh@l%C8O-GH=CPJa^<)wkcTFBW@rF6dJI?H?Cn(oI^XJKxs+LKwOZe+qLZfK|af z#~~v)R8<8^85K26iQrLoJw-CflqHA^sJ8hOjmCOYl7n1`)`9AtRF*J9q9e#!+1?wv zh6$UEe#dfJuhzYmveW*>9@oTujLIfGy@=Z& zIz#Jto0&w5*-T3IJ`}t5aiasaOA_)ouq^Xd+PaniWU=5^*6kkCt*_d%X;1e~pVcfR zLj_QjK$-!is!mnAIfGHWFpY;mu|!6jC;;Y7Q>zjE8XYv$>pflR_V^weJoxxx=a3}G z-ht_&=xx$d5@Kfq_Q=YBQC%8%Rb$>CFga%0>?Rbcq%7%zRazL5o>x{M+%sLzx$Em%#!{coTnKmu(ja+{hTmN=xg&mUD$VN*C^wvKg zP5t<9H&wJb4l1;d6kN@idYhJ?%5IG3W}LUYlr3aMnun5G13 z;`R6Gm{;_EdsD7y4b7#bji$4w6#Do+!eLH&V?n1BYT=o&n90o&rWBieIz3UOfCfur z7SKc{Rnew1obB242j* zpM#CW+U6Xn#0%=m- zwul9>Vnk+ZPxUbXXcp|YfXtDUz+gAcNGhmVS(R0tZWVps%)lNfL>ST(M!~7l&XN?_ zj9#<=bBR?<3jk8&vg;Rr-3nXG+m_wla{s(-qrIW_{vEOP#lY_92z2L3aH++xr(X-* z9{`X{Fa=d919PBCfm(s{dk=sAo%!Xnkin^JT9`Z<8n zklE0;sT7b|z1+y2kjzZ)5T~Q}6{K(~mDN7~GB0Lq?{CToAokbuKmFwUSBJ+Z)gH}u zQDayqv3FIYxikWU!kPC5e?V?jhl-U7B57S^OABoLxEXIb{+ovG0w?dUMM8!^gaBqK zh=dUpn1P@bA6pb6SVjieFai>x9BE_169bB6ShX^YPR^Od2e-tOvcY`Qh9GohKZVhI zf#)DMCVpJs5x@S+){THshpKk}Nz%GT43u;mV2DUs zUd_1N<$gc!=DfzU=RT}x>%>aI5u19w(!q$qZBzWTlx-Zr1zrjrvl8Qg!F^@9HhF5D8s~ZIg-C>?Kp0^n3noT$!c9Fu zSu>r(#_7eTX{0;7PN71%2+N$AkTqO;f{?6@fwV@!ZauT+9=_u6dW-rdg#4x-{jLHk9Nb__ZKwhtpFVPgkX2GpSSvb3|2!Az`Y-T(BK*wxziy1c^1v($6|3 za=c~JH-D&gQk#mR*m%BU1r7qaW4HxfOY=~YQPf}VL3 zcmZuZ>#-L3N~`X-xBfoj9AD*DfAvn^4jwuf^~ew9jH;ZyXhz5i0`HxjfARdO7k+%N z{NTa8b4y|um}KKp<7NgB5sHK7B)DU1s{83`f{5!1J638svbfK>y+fY>%=(zfUSC`g z$X*J{$iOhfI3Oldn|!Tw35Q%~oI6jfhQ&;nXoDqvLfHwU@jUWu-mS1c-Pq}12b)e# z#++-J=XXwwo8d*~6~Jk}tTR|<5U}E_noJVjBnDDTV73(1(PsZ0o&2%hj=PTkn|;Jz z^Cx^qZ2h%s>B-#P18uA$77Vr|g%{sf?SL;2`o(oVnQ(HY!B8?!hk7#BjUxg0xbosR z!d+!Zh&aGzn3C(9HxWuC8Du&kEfP%j4rE z!W`O3GB81tYj)OzieSP(ChIQaYOh^tWr(rWplmRAFXyAz3dFpLnVPGaRPQ+qI-p7r z3sk|9E_*8#g(~Zy5vg4;;MLFTV1ZMWu?QWy5lK7(%Itth&0KpYL@k zJc<15s_ryA|Kg&I6t$bp^Sxk!B`J#b6+4Yxqh66yTvWHl{aL zQ_EE0T~e>TpB8HwMWyxfKY5dBOZ{wN%ee~;G=s?Ghe`gkLXAUER##B1tfYGFEfdYKIjPl~T zUQg*2G2u}7BJIU4pY8IpPBU4hLX_fHk)Q4AoWKAZfQphhDR{7{_qTa6m{Tub22?i% z_ePu*tq4*xF=y6n=t-x!S+O#Wu3prVZ&%DyF`HJ z=MK;6Mj!8;o;`j3;_3Cin$6;V&VA|tl#`WbNJGt1=pazG`>5v|uGhBOt}b>Nk(~At zO4S*$HN)U_z51K0{VpSN2xBs+`CcJys0XpgROy~du?@;GP#wNwNXWk)iv8CwK!4Zc z^;X7UFOGjzXm@E?A9JV%;A>xJ1ZvJtuj28pR^Y+NvO-i9x9v(WZGux`7C|NRkgA3R zA~091Mu3RKVa9Hs3ORL%rbLI5C0zOazE*M(r(KAJWq-JjMfRF!M%?TJ6pjE z27?*gF_^n^C@Tj`6RIPfXf0O@pkQVZmnqaf$k+RrUL5i&aoA%D4C~RPGK<*|JThW6 zjwYR*Z8Tja6p@M~vxsXm8iR<4ou;A+l%}sr*pj1KF2$NQS`Vbr0XP7}0;v8a&+Tg$ zQoiPTQ*8Y={LH-h-!HkhU;TM)V+;c&&(w4PN37PXQ`fyz8bcOP8f6|*4FNVq%#jrg za1I@2FyMg#4OAgu*bNBD3@bLQj7-}2Sd1gm8DNHKN`&J8RH70AG%Bn1z#`_cAPbOD zjotd);Y1YB{p(6r>$y=rQmn%y-vTG!(1Tko%L4Bz#dW9Mot z7>!q3)VnTdeQOteT8+fc9w%L*j{ z8E0iQ<@J;yU1BO?UcqAEc5P>A2Wc-H5)*Jp%&9rm^d7TXf=s0{qRk>jt!P7{kx)QH z2nWFpn}Jss_2mIG5(dXaLkzHj(xEIlRIG%PL5?XgtPoZ>nr&C6c?J}SRk6Vkh7>JQ zY;YF1Ej1PjFl(bX7C=BdPZ!YF4Se@+HM?(}!0{H^`c1oi0l~g@HJXwd!~WLy3P(z@ zTAMqOuvS7&2+7P2=BB_*rV=2n5SEhxMKZ`J0U?^1+|{FScc@_m0EtR4z?4N}z=2xG z{S7RTDk)Pcr3ghT5-Eh3fk@R@Mn17bts`?DZM{*YQpp5>>Ig>~igV>QAXdbAv3iL_ zZcB3QlMxIbJm)kRC989~HI5=1homdxWW&Cw*Ua0b88c>bNleuxVU8b$$1>_0x!yus zZ?SlrF|J!6!n=!rx+Z#u$sP?p)G^^`=4Mu-%4`YnyD~sK&6Pbnpd|=e+e9VWhj*mJl_p{NqCy1 zUoNT{O>l-sBKp!B`z6oqAL8}jc=bSK31oiRS|ZM_=-*!>8l!S~DgtPw0~jsu%R^9d#*IE1Pr5RW_rc~i&Y%WKcuZ{6j;g|>dDU3atkc`I3)r%XCc zDQ9C}ia=yGGct1_h&FyYp|Is=6{DFv#FWH&`Pl z5(PCWZ}A3~!BR(OePoLYQb@kEKf9{k0!-`qz415~k`OYL2)Yv}?pdRhVrJG5zqg78 zB;N!>$9Kurj{$|Ss8K7FoiD6~1f92%!nT${Fl^onM9^qFLJ@-ubbp)G{o2H-`T8bV1`VPzc35K>4P8ipZM zBO;;#%9Pn1%YG9K9p5Ee2X1AHIQHQesGoK-z9D3`a`?>*rJFyOtCt|^sB=TKy=jCb z>mC;XtXF%I3;WN|#u>C&r_Gy2lM!A56j5?IdpZCoUDnVFZG2`Et0}aMfERDmPf3~y zsI}HweJt}lpPinLV|jV;^8WqvHzaPky}l#1eq%p6rgT7NpZ1_e*0@s=yM1A+3Q~D& zR8g=+AY$>N00eK$Vl7ch57`?)Sj!%38ZXC)`&fHqXN4|hYoM9&ZR_u5-FGaP18A|% zAeiliX7P>LEun2gcCIUU^Pd_@NQ7B~afOIr7l0z%iD-TubTcOknC2iY>4cN+9+?%Q zyNJ2gOs#ACm((M(MP}k``<5R`26!n>-B28-QKqKH^J`TLR;t33%$LLcBdqg zg2~a%wM|DXp-h;B2{t2wFoQ_YJK?_>(DGH91hg@rMDvQuWn|Lv7ozEc>c$;+u3A&W zMJtuxFu#Rl-XPe~>hZV2uf7L~ULWbz8{?KYU4U zNhHGHEz~i>`sY&v^^b-v;HrSA*;Iv^Qcb}Lp+;=C+Ydkd=m+2b{wJS&^5FdZ^z?Kb zhTVSu7XVlM$fFEc12C(9^sRELIns+lnduA@}P z^{&dMy}nI(@8;gll|;gmh;)OI)~*0arYBr%GEHrKSCx&0 zQ@YbKJaQ$Qp_pkt(i55}78vG8uN(#$L#Ple?-dNnDGmt+V_cg?Oo}Q{{{DOK|M4IH z@gM%9fAr2f@2uD3IF6a|`R9-R#>+7pO`ICS0?z`{aJhI;ejriMJZ=bEV z)erS$(0W$TVIY)qoh;z1v)n0A(rj0!>x=mkaTUmlh7q|qV7)?;Ny}zn1H7^Wfr=(+Z^H|<#p8IyQuuXz)u6$`l5I3qdr1j!2*b4tZ5^y3qo}Y8Vd@bFETaI%?Lu-R%@{rN9G$9nrGfAGhD`lo;PgCG20y#7o z%Rm3~KY#l4nbJr%vtt?ecq_iXPQ=$=Z=bE-aSq3PkTO8EW@2WUMZ4M$v#t)9vzTgB zMB#cPB&Os54HcsS^IQXaLl}rChxtBcm^m~UN1BpTX*!TBKFDmI>#NV}78>u{U2mVQ_W}iQJl+A;IU^1) zA3wVO?9tDjJjdnk-N^UoyPA7o9L8a771yRyoeA)lTF1 z?z`{4|MB+yk4``QxSXHwq(|4!FP=X6?6XHd{_&3=eeuO^w~MSKq%^a+&Jlz-41>G> zj*Q_qS#P1O0;3|fNFjC1~+ev4zag)#Y_O+>o8G}8?Y!DA<=0g zu}+Q?#~I^5K0F4fJeE;<9a4r?64xSto@jCzOLY=}dpjM5js4c~GfM%#Vb`LfPT2tF<{*)ROVdH3 zB4NrgBCbfT^AlSSHV!YYFMsm0 zpZ@U2fA#s}FP>dqULU4?twGD0-T{!k586K@L3;1uW*AE;!_lw*u5II2uD8(Em(>iY zVN)o%`NfNii>J>&fAqyW47qlpZ@j#lP~rc zAOGGTz5CDpo2UQwU#|C$?qOb`64=*Bnfqu`D#B8xKsw}F-~dK|RoTnrwBys20D45j zd#98na#3XOv5f#k>uxGRw<^%Qr@%@xnDxSY5``Qn%_xFq*Z_zMS%Jcmc=@t+_4P!_ zSb4eH^NOdt^}3GlmV57B`{@&*o^Bs}^1XL|?+5qa|Gn*l_v$d_IPT~A^G9F&`~UdE zAO7gCuCDjfJV#8VHRWC+_lLdP0QZ~Dv}7HV-_mkOhz!kl9@86AaKzhf>$jWjKxWn& ziagZa^WFZhe)ieF_}~A}?|ty$cC|j)o@`D}$8o(HH}}u(ZMU1v$@);QpV@dZQwf?oOTRXknuzBP&|XAZNs2 z*2Md1Y-hpIRtT8UsQCbab6Hng=XiQRS)ZKFQFqg%Iw|MYhJ|wD&XwlXJke+#O47VI ziWj))S-iwpX>b;Nu=#%fCegFM(?*Z2*6U*bn;p*bW zkAC#SfAequ-$!3QkF=B#u9{{8vSx-ClG0SU8A(b_KErRwRo$A+%pki>%zLBO+h*%` zyhBF~F0iRC7iwjw(r!9jL63G;G=)^PcL?LI!v7Rqhif03f1W{_rXvyAu|J6 zhOuTFTWX@oD4@c5a{uJv`|rH_ZmG7ZzPYzuosB2w53w2!*uOZ`m!CiV^k<*{=tqC~ zvrm6~n6CG8$cnick+nwF!B=x+t>I+^0Srr?w47eN)h-5r1#|Z1MK5i=ZMF_1Wj9E| zB|8-81!gf%jhGNx^l2S1HPE6W0-g$1J%9H6CqMZ#YCY8YHy&T?x4ZZ6edO=(nHEX9j+APqhmi=h~uAhan|rn{K|P-#dM$jyoucVjE0 zWZfO2mhrmWf8pozJ3sj3KmV`a|HprJj$w|#w8ym5w9~YYI=d;2gBKPX$C0!$48v+| zK9q4atkz{*nHQF#VYJoya5#*sGhu+m<1at^^4W_QPoIAF#ZP|n=`Viv>Eo9#F9|3T z7^P{NYtFTdLn3R|!ApA6)B|Zg3{jIXGfy3XX#lL%pt_qU(MgGKMBH+Fy=AuEQnv;? zTx#n;i)cI2x?v2-R0&xMZh|s3xWScyVZEwToubZC1!kw#!!zR9`uu$R{`uy;v;D>N z^65P7hjr{zv!yYSl>-%M5F1rQc5tNAphQ+vI#HaX;cS$p+~Ee&++3oY8LfB|hmny* z!#cP&h zA40MkhiA{9B^*$4uE#jU(R7WXwqznL!O+?Y=O|_HVKvoAs0@Q}R0=rSvT##RJVM#} zJhOlB>)-Hdw=)xx+?^#;*%HT4MuepS4j8gTL@Ar8syWCCp;*>=L@5|lwt4pc^Qk_$ zd~teuGWpg$au|k+UI7P$h%~1gNksrT z=S-S1K{rPz5>kS6jw`vb=;if`d#Cr;Cm%h1{Dp?|d-qP)tAlDC#5GNGy?1(gwYyN} z<>jRrw=%FdM}K_p{<&YfyVW|) znX8knmErM=XXoc5g>;O%hH%5 z+QBal+Y$CZG2y?(5%V|Lp82G2BQor9NU|kaGHKHUgdr`81O%Ybd%xvw=bWm{oDX?U z-Sd_PUh{PWy=5TK_uYHes;sPE?&f7euIjo7#mAp~c{7c$ZR4F*560mtIT=wF(wtZDZ^1-FpgVMQt;)1(cNR zFEKdW`Dn27VdWYXDJ2mRk(@2j8iQ=55L(!+hyt`f#TW;J0W%|FjL~~v6a@g-H+CIn zwhv~6Io7t=i)wsaSUl5H!s~$0PjGCRm{twkv)V*CAsSZ`Kd8{QXt`*TO`wbx#I z;qs-?WQ309^J$DRHgPx{efYtzUi;ds|Ncim1k@ygsw`3JhMe5K{V73fZ`s^j6M^}1 z0a0R#P-_!o6vwD4QVh!7`IKWl9t{SA5X!P{I3aDFJ2$JBQFXCcCQ(8X?OEnTZbFAN5bPF-5+aSKh`C2zfrT9?uXa0DyvuzH=7}B!@bqwH`=Qq24ASO zMIw9%EOP7itzur1BVUH12%wx$FI>GGM#cJ>_38c|2qUO3NfTRHbQQHa&$H>isH$>I z5);Q*G0R-!E;EP@2El^}$JvawH#he7XNv|W8)vTV{BkfJjYeY?Aav%WQBWds=q%E} z-h-L*$PkgG8ur7)`w~5q{VF0cMHAI+&Ymg}6-8l}_0D_mZNyBF8^?R^nHSv_abIUq zKj^D}(36VSzg(}_k-HYuw*^ENCe*GE- z2M&^^$@*Fd!F%s~a6~t6UEAE=EJ{zHz{x2%LWyh&VpBKOvQah7O$ZoKVyYV!0kp7k zUe=>wSS%N3w>G}^#_PZMpMQJnU;tf$ zXvan-!kM`h;mr2R9mB75N*fh>dl4NB22B&~`=ThF^NYozC`uw|nno2ufZo2*KE|;! zyT7|0V)xU{{C(+!tLU+o>&L#sXAD`N*H_;XlGVFY1#9L7x|%MuS`m^Yt&NApEJWey zbe9nXlA_+dcPoV8UGPPrqKH~JRAFQ(9HyorRAnJ_h^`zA*S5ApQ6M021kftQB+v-5 zLgd7(D4bGVj3?yaqZ@a27j=WwFd(S&G#HKTRh$cg$;uCLXRhw5lx0N&tbAM^lXiKl zrj$&ycOp62kwg?I7|V(PfFcreDvH7))tEVi5JI?n_wM=g;~1l=#u%;qiwS$p;cmJ@ z?b_x6!U6NaBaRn%puOU;=Jf{{CWMSqpqqmgvV>LHfe->GMh~rqhX4xW^@%TwWxZ&c z%9TMh0fHlCO{5A5M5LPXM|1O1AxQ`02!efJ3`hqyEg>@W;Ot0 zi$O#TfgnOr6e0;Nzr47vwf0mVE==3-JSaL}8D zQ*prO(E}+1k2SCVW^{CK)jUO3N>epULIebd-uvC%o$+|W%7rTt#9IiWXrQ)W(6AhQ z<;7RdT{s_Oyz%L$H?DmwtO!WtMMMCti(Czq1eg`EP^B73oNFu(8NCuHxOoyyY1Y&Z zl(0!M_e9)8OwFh)IdSklHcP9x+9!Kx6VK*XH9_0QY$nK4O8HL!5K(46RC-Q3gQKx^ zhWJg>5Yccrv;#s2Hhw9kyx!nGVDfRMr;67PNg4n^3;P1|ng9T>swhy4atT14bCJ`T z?K8vScs8G_3ZgPcU_lh;gDNpgYU0{xyuUMxoYp2A*Yy+cJqR%;Q50-*;}SYiVn8b; z*(6Z}W;y@D#WQEm?$4$vrt8-~0Y@eP>lmFcQj45vI2_jVDX_TiT{6NJ5$6H`m_5-9 zix!|_=@HslS52JTDRwS%iZPaDKr5A$S9+U1hbbjH1rSqAA~GBfo2F6Kj9wOt#dth6 zUMFhvj~>W=@a59e!|NHs=08oC=lKvzDMBlv0y#nz5jWO1QsVi1p`yVBP;kznBe06> zNJUv7xP}E>aER4%&I++|#&Yl6&(Jck)(6bG=vIlUo*TzJuY zA7gAircG^wX;R$G?L?GP>Yck%jrv#xJImuyh4srdX+Y;^e;TN-u^E{Ln)i26wX(F1 ztRbzv9?mSs_vPPjsiML<*m5Cw^x1QDG>?|@K5mdiP+Qd^gSI3hLUzCfgIzJh$t&eAWyVpMY zuxe^@B;2K+Di9D02oRHt3KX13nl9@PuU+@vsp|e>S&Nb{fE;*a>kXZvCyFAei3s9@ zuajAj$Vxmra$UZUWFGAdi_*Od8n($m+&JDmN@kr@1ze%_mFp*jV4%npUqo!0#-IxT zm>s4limVO8$wc*+F3bD$)bjdK;gSV>-R+&)tle)rz(>24*7ML^NzU|j`JbU?0Y(v{0ET)A=?5f+R2d_FBl166%> zIGHc1Tet4201KmXSrl5=p)8RoR+XjaIdFgmu#u2Dmr*O!jjaK|xzM+YtZG98m`E-R%harTH*AG}5j~)8Q0kj;V--OqX-J!lf z9mQ-a2hf)!b{EMaib`T@n{sh zXXb{55r=D=6Y$O%DRz#v{lfCX4lft)K{h>1D1 ze3SLgrfIU|HgmWw10phh%@Q49g*~uKxgestu8X3ms>+PNH%8^AIM}-oV%B56}LC`UBfes0mF{v<-I3fhXgz6MPkq^5Lvf;&qdS*mZ zNwctb4n^=-3%CWtIp-FOMNt&q`>Lu;uQlKvW3*_Q7-LzMX3;lu5FE#Xs)w2HF;P_? z7Yy?t!*SRV4X~2({l_@=vssaIpqPj?>h+CCR8k7A7^*8-L=_V^fSwT1G(!ZSf)u>> zUJ;U#Hj=6WA#lCn>(?*&D_b@4Ttyn8s8C0atSs8ZhFO|sNkmPgI#Cjr000%V5h?EN z(N4W!9G8BG8bAUe5w;DHbX zQi^8t7Eu5c1VtjE#0&r%n;U~r?%cT>n?^7p1kfM=;xust0OdGxaUFL8?0Skq0#0?}5vwPE~0zViy91^HVL||dI8ed2bk^6zW_KwDv z&Uv#r8q6>>l6kZ}SPzQ`qtR%jU5vYk3kwFsn_ri}lJqI2GHFK<8Xp7G-b%u&C-m@D4>K5jwHD=BnmWTAxfD zd4K-G-Fvr}^$Zk+DS-&0kY{xQ;z_(tOV}%sBZ&1LO+wz$aI)onv7FzH)zm2js6qC~ zB>|~oC_})iA>}%yMYC)~SQ?;2zU}wKXz}X~9a_tRzS{@@j2$f!COb_HdswPJk!5Zj za{T}z3NsOvWohP}q9~Tjr7=2M>lkJ;??5j&9x{vP51(n_Jw1Jf+2qouX2pOn7bl)}%u&In7RT@+lR@ERh^urB5K2z&pxN$-Km?05Nft!dVsbHQeFf74-Y(>56B@WVfJymcunJhD~C@ zR1RHAjYvXp%&ba?F1IQ$7RL^JGc%DlCN_ZHG!6DllZfD4+gDu=dO!)Ws;c$%_0FD% zsyY}9oO5R0v8$Phy)RAj*kjIsX$`vwy%_ojpWpk;`g1!>rei%vtEI1phr`CsID-}E zi{*5_JhOfVoQonrj#Y^$*I@kg#?58bRNR*SkrcgnE5hsmoN^#306i$8Q*dSJB2!F2 z4n3$yblQ-_fC*3-m{p9lde)RP4`cdjJIzTTqDbIvipb-gOv9Z0jTic0$A20W&K#rATOb86TC|y;cccQ8&F6>vN&ZBn#=*W-8TN`KG z-MgPuy6c@ID3L@dMOlb~4z~7|vk1egZq_D~Kt4&^t{u>d^r?zs=lQ2LIa-`^U8L86 zch3iO8md~n&e8F)c=yq0Bq9c57K;TDef;sq&bc#Z&R952Rt#`uU1@o^y%d0Smfs8@ zA0nOJIPA2qx{8%jIIZ+`csQ-Z!F12#QUttw07 zuYUEF{r#Q!-fd1(1Wu}e4waFc(!~OSRKfaOgu)l=qqVb9C;{GF#%gCVD$mhm-9wDc zj>_Dt3IuR2a#5$yBm$t+)EF$TWnj_IrJfZ zJNs6nmKD@Gg8`Rb2&|(W2_UB!NG+URjY^Fnl;6V#nrTYMt-eTt-YlB z+CUktz&Zhoc{D2$JCgSxsyjGCMnmgcSPRg@VB z7KkT)&IZkq0=kHpm`S`6!G>ug*n^Fv*EoM418*i{jyj-=%vmySxDxUhNey4dWsQ-F+F*|_Ht{N5mgdNY=q#GqP zIA`N;{cUNnPa*WbeJwFYI@DtoEp|TMGp5&}>cgSWDRg2bt*B$(s*({vRm0>ke2v}M z+Ad~pa*^Qh(9v@Lgz*ST=;`|z-XApe?|OLv+TPFU`!Vf3^}vSU_Do zWO8Ppvy^~x+g5q|pT*Dsl)7pE zGoi$+_~Nm+3UkHB^oE@;bh_2lKs>zvID3VgyT>#Ma-n|KRh3_s?J=R&OFMs;Q&aI< z^|eHy4I!-T;|u1m%;9$T!m(G^RQ^h`WJ*iAH&M~gCa5f~$+A-Pz?><3g6a0M1>yo} zmr+D^P z<=V)S@BoH~DWpk>Lu4m5F_&v+{p#GU| zAmn-UPGQW`Gy=1G1v{x&E*;(i4*VM%XGJ+_TqoHW=zncXvM!c~Ymap9HJMz|y|7-| zr!DyAP@0@Y@=!x1Rx`mcGrYVOWszDgf^x9i(V71-eU-jlcoN`&rlOF}GmZLFPs~2< zH4r)O)9yXzN1EP#z;FD3n?p zpr>Z~g(RWM2b@6l7{r@FRljo`9Fzl?J#|e~f7q$XHFe-C$NqsoZT#*olvILkuz9`^ zP|vVz?dSieGdXl#=(&HWKD&3IQt;~Tbx|)ZJWP*{odUDDX&St(ok zLv-H5G7iOq;~t5OPmqD{bW-$QS|ay8&dQoFFv0wPmjos*{y8Z0U2d(ZqBnylRf$t~ zOd+Aj+;;}-p;bTqx?W?)9$=&B+CE@Yg2z3M?pSS ztT@>rR6*7*+S_uK$|_G3n6x38l#%BVejuq%2_^%~6Yrn4n{&~7OR)B-OwBsCZ@iDi zta)>@1P2FlCR*-kT-kKa0_?V|u808_7>sD~f~@0#0`sAZbB4ofJwHW`M3V)W05GvU z)>(#~rC`Z} zO0b%MzjQ}%D8`TjqTS=Yd})Ky&&K{9cn!vb6M(CAMHHb`V?ko zL@oD_DDL_v6Nau|78yx4RGCjZLBH0_*wLuj@#3q8Yw75ay94K$Rf+5}Qzy_2 z`!!J{T%=K&QhTtP)Iv@^yA4Xo62l=wOb2?sw>(tpye%P?@v$=a6^|U+N|GmE8X){3 z7_Dk(GD!m9*QIG@Wzn^8n*C5i{UNl+EI}YrKA>*Y3=G~IH++?;{1FU#2>^qNGJchI zvwwlFwY!^FJy2c}dff4y-w^sFUM(13D2fG3=PP6@sb);TPuV{J`fPju&8kGd>C@tl zf4c8cM>?9dpzrxaflu*oSN=;{sqhP{Nci*L+IhEdq^W|J?ev6XK~RFX0++bsxtGZk zU0x%!(C}`qr6o6`A$eMy!W2tG!o^kWmelypy@xmLcwbFw z_@^NIljCn$OKg1R@%f|4`CK;A1Nj6pC@(DJ2K9iuZUT^5LqV!)r=w;FNXDca2~Qe; z!GdJtf4E*edTwr({%KOp{1YIva%d`FVJIT3D}aCuR9owc7Of&D0hv>rkqd&|zDt$L zyc7Nc+BJ~mGqkric&+xp$nD08Fu*$z-lq5IXH|g73!ZnxqbGuAu(;hA7Qkx<%HK8k z5lh{tFvoC74O7Z}Yas4Nn;utQwz2L}#ews$bDNC$8^hMw7RR%3C^$ata2@5(OL zOaAs~)fOxEftPNv?umRazpKa^Eo2ryEUOeDk5gY-cLQ@y=J zGYvVJS*tEYF=k_&d7NnG=jXDrcS{7<9nPkTu#xBmeRIQkoB8UVkVc4I!sofMvmbgD zd%wzcV(&>t(b*Bjrh z@z87b-J>g9hFTCO0yzK{UJ^s1XglCGm8M_|srvoGi}a6HWMLh(qAO2EGL7)-nrLLK zPCcR=@NRQtdE)#c2#{>?YJ;UBKR*}0v`TLwGsdZSv*fo5yY<0Oojqee#13~cp=iw1 z@kKuw_xe|FI~c4=kQ7kg*5)brY((?7HP7qlm|Wh=7g2Ma{0sdspz>>CrkBJ#Jmutn zM9j1ZUUKq+kWr!_UBJhu#TM^{=#wLeNYtPytw}BypFD<*Mo?I*p-=L#I2mPEiZ*eP z_F8C9F&NeILUV=4r9a8R@7s1C@QrDNstL%*cK^&NVnJV9A6UoTrUcx@K505EdP{O6 z1-+PaJyX53N60ry~;2pr*|Q%5ZdDFTC1>p&jMr z&sQE>BG7F;J-Od{PLDhx*akX}$a7 zMR%Igoa|_s5hv%lPwv)BEzVV--7&|*dGqcc{xPOgk(WPnRWB7b4z!B|QkInBEgoy> zkGRf^6LLQCZK93~qlgZjKqS8ZjE19Oot-dU#+f{MOf=#$x~}KClK<{(9wi@eOi%=k zijo{D76F7AvnQ#e(ptE&CmP)Fe}8F1Pa+>>)PE<oM~ zrbbz}LPctp4^XqLR%|nTpAbOI*Ld8?megG5WK&PIAi^$bjpxt)!8MO{F;??hoqBXAZw0 z`wC)lz$j%!8&3_+ayvMPr~W(S{a3y474EZjMzKtdM@#yr0~jT2kN6^R;kcxSbAR>= z4SfDDfxjddMPa0E-+-SrwhVVx)_c8Br}m{%nEMtl*xJF}IwCHvQFvwHrVRvQDaPX1 z+Irz1KoOM={a_z4Ao$1AKM7%Oeco%zu4au|1ewRz{U zA%e$EUGN(REoTG*Gml#0NA1kj-*(c5VlUcyf*f3T-m%yS#wpW4u&a;oK8Nu--0>3L z^MKh3JVmT5Tj8bydZ4I9JRc&;vfhiM#r4ZN8p=jf-NL(9MD}JGEq=I05nDS&wrdxa zC39FWuQCNGnBM7@%{MvD>{}v_HCN+FTfFeWYHI=(jxE&=X4z>)H8aT?5TN3!T(^J{ z)~thez!j$$==6N=L;{uaHSDuMU8<{TZb@kt_NoBK)>Piwg9+Gzu>e5!j*iGk+UIzd zTnT%i-;3!+cVF%208a2*|LE{2^|NO;R+QJuGUxVR1_HV_v3FnEBjoZInWd#;&yD;R zb_Q|hh#z;8_p^(pKxbWrC=`*(TVa{9GWWCTEIsfkw_$GXyhvJaL}>pVQ=#SKrWKLT zJXPqAln!R~${ufL=iRB~Ru@hJbNZ3r_KD_%>aJ#YuZ39pR7*mP4=hFw=w~MT8F^UF zo5kr~xxG212E4aYYWJ@Zq##)?I2Nz6*K&TAgFT5>T3=qS7dF?Oziq?aZPi(|OR-aL z!7&@CrMdzoM5tWeW;=8g*4xuA$xfY9+G+y@rGJdE<3=YpdOIb9MIkOsBC~Zu$>7t^ zG%X^*d5yQ?^=zf$8a!|?XGL71CWXxnZ@@}QLg1BhHkb{Q0nJ|}y3 z{U(XkSD3;V9?9i(^1Eah&y|ZOD(WjOGh1lqAx8i zVJxCg8z1gX(rM%@c4%o_oR*K9Yj!ALU)TU2v%FD zRD4&fj+TS+O!FIWsjUCRLc|_{L?*x6t~kz{KkLp>`+nw(;>@Zk0CK!YF>=XvkYelo zH}MGfn;U_E!?C|~QL3NIVwakRG4)n6y}<2yks@dXLl{ZLV*xPOfIC;VBM+*1Ibm!h~v8dsL9+T>9dX?x^YQXAo?=ngXQ~^d_ywpIVM@nE*(92izk$Kl=tVO=M#P=iHZ<34 zO_1$U0y7azfFCyoOXp59)u`16$45t^%<+?WWq!NQUPkY&eF8nr8RPH;g*82s@eW;s zA5+Uk`^R8_;-@dQBfk44f55zoz7&AB8}#&Fkc=&mEVe>-=Hv!yfKJ&wzGU2IgL<0t zlyN%Ps~y_JvdM3mnuZu_)ph-wz=sXucE3qOP}Eq}oY`ni{rh!;l7!v!w}-uA@yLO9 zlv{TfZQ?y%^d@N~z1LN}!8j}?b{3Xl1boq?VO&-g(IE7IrzE`#+H~OCZspJm|m3pKhkcm=DQn?i2e4#VD8z-2L(2Nn>NJ= zE_AnwZ3{h~pqM=`8Mo~HUhJg+3;sgAc?d;$m%#=5Fx%lmQX(F0Z+i zDpYC3&f;R$N=Sp1>E7$GT|*Mu_4ZCvffCx#hKO@A8)TuBOZtaB^Vg30fBvNBZ~b^C zjl!)$6joO@bc^-*8UI|TX73roBxn2yfpNcW8#2E+fWfh2uMHOczEFU8n3*2ZmX&X) z{$Ga;tHs_%S8GZ7173i^qB8O$G96yOHoIZ+o$}M2odM|5^ulXy5Pq{?IJ32x~`P!E$tJ%Fd`?glEbfNScT@RM2 z4?pNiAa{x~XkBpJG^D!o%Wv2Q*fPi8JtR<$$_jdxcYJtgaa;jSByVeL6SI=>+~bI) za#Bo8ph;To2^HZalHxj4KkAJZUsHET<}4YA5fm=e(!{r2Dh_k$fD~{*nX&#*rJA9M zpNa3T0RadQ+kEa?Su15u0;Ug>eNZfCU_xs8gSR5_d}Lov?j))(;;7xFD82Q7r^rH& z;lUeJT!WDf{IvK&A#b(HHeP_=F~vLGI0(jya@&t*7X>`&S{g?!uuD zfQ!Q>Ab{v})Z^QCwdJ8&A2!}`c-xX#Lm<6Dr~9j`tASFrsMRo#R)wJI0z;pEO9v-1slqVc1k3)UY7B?#a0OJaU-z)DP)-=x)v^w7MH?8I0t@nf?d}x*y zM7vAky}`Ejz=6(KY;n$}{{&cK!7)Rv4_O{2vg*&F`%`P42vjG&wH=N}#!9GDssg|# zJ#|newliMaOgj%=APR-T{efV)m2OsGxW72;4rcSl)gjT3*Wz!-QO*7n>i&4A-m$1J z0*?0~wG1g><=<8cR|4^@l8Ft31+%E!r|VmY=JKD$q%X0>2y;W|66P9sq#9mXgbQ z>f+&RyR+wHuCF9oJoOqPPmy-LQ*D!Xg*IU<0scQO?x`(L4=(m30?1~yVJe4kQo0m6 ztAu+g(*;VHf0N?9=ZiTI?4PQf9MtrirN{ROXv6xJKMMP#1Ee8dd|MTas#9&|9G7v+j{70$b%x(^6#jQBY)Z^53 z7ju!vxT)Esq?Te%SLAG)kfNExU;1N+RTpdPfYel>e*=df;jtUO&T`O<;#;^%;rQ7~ z;YZpa=E=Ba_hrsFC+PQJ<#uk$3dPa4iQ;32Fel~)N9dF9)AgCO)2}%@7E?h1iIujNR`F1q(5tKhwS%l6Jv;gK%6j{UEzW;%*C`>fY`s-OX24%JA`k23U;~Qe+8~ z6TEK@7xDfy+!5q19^rlPZ||oyqw)Nc#cMBwCqgaueuonE;;%R4$g`sOfi zCP05s++qSYXGm$PR2K#;`mq>-%jZ45sR9$P(7%<(iH ze&Y$*{nH>Y80bdQJ?-Xc^VRFuUIrxe*Spgx_6W4q`jLoK)k`zuH(4taX)|TXb%|H@Q)PD$Zkty-oTbJ;&W@b7 zx}LpvdF#kU047l;(bxrutR%&FDX>wK+9#X`Dug2Q+7OrkT>nB8_YgEl?0~ZY2}vj)?X76 zPl%?fyPEy$V4f5YKWp@ElLO)iD}v8GSIiEk z9~(JVAc8!jgIS_QGS7-SIJp^o9^(r#VVTMghSfuQO?7Qss^gPn!#Fu8)G^Sfc>*K=&ss`8S3GPa%Q#PC&v`OxsCX4 zwBADgw(!omSsc4%i$zIfnSJwFqxkWO4r)291~SsZ9B){P?BCqX<7XE+c(iq3+tB5Q z(rc;yw9r_A8gk3?vSXy?&PkkOyIad<2D(stRrjc2l9TORnB)0UFq>aW(#9{jZ#U6* z{oJ>M@hswc{6vu^#c_cQ(9yrE$TUs;dv%r~8q+ZN(zBzD*UjC{>U{MugZ#SnZ z;sqw(?F(u@Kct|l;1WDd<*p8hU-*pIJOZhr2XFn#E?Tn7TNKCudd&8lx3|dNOKIHx z*4=o)ZAA;GXq>GCtJypOh}UOR)&b>B_kw<+@mrb^0CY3}{x5~gz=eB*ET6Rk2RiMz z$fS}#eQd^d59%ABbTKU}SSkN_tCCsmnF5g$da{eb)GA+5=!#<2?sreh-*W8kY(E+G z!ajvr%@bp>*qFRek$qMnr#V}h-}doT$<@lt=8*tj_*22tM2>fK$;!NWim?6lc6T3l zO7!Bk$CUu>`=tV2-_UffjJr~SvV6Ig?;jf9u*2Z>CZ_~dWYdO?SNB1Ot;gd_NFvtYDmsUa6W#mRjpi->#(Uiskw%B=p-bAExpXZ(~J&G9(KX)&{#4D^Uc zYuenm07kMAVwUwH@(mDa%S&3P0fkALY_A4qs3^CsjXaGbr{Fnx}$!zk6&)!{hP+> z`Zfwv<7zt%C8NXf`L}1{I#F9>b!~?BvM)%3sW!NXD#<3KYpNATH|Ps|yF&lL560u6 z3RfHY(`IEMmuUre^@$4Utz=ah?$!>=S+l2aq7!N^=mGg`Py-W94{ ze!a$e1J!U97$W>ywu~Ewy2-8elGr#kd%W!^3=k+b)`~K&)Gzp(E6_D&ynP36J-DaN z1L0Nd_rX2@M%!5{^!|OSv=(&Me)h90SL-$W&CgRxqBzWFqz_hB@zGiCQo(%Fi #include #include +#include #include #include @@ -13,6 +14,10 @@ #include #include #include +#include +#include +#include +#include #include "libslic3r/Utils.hpp" #include "PresetBundle.hpp" @@ -28,886 +33,1064 @@ namespace GUI { struct PrinterPickerEvent : public wxEvent { - std::string vendor_id; - std::string model_id; - std::string variant_name; - bool enable; + std::string vendor_id; + std::string model_id; + std::string variant_name; + bool enable; - PrinterPickerEvent(wxEventType eventType, int winid, std::string vendor_id, std::string model_id, std::string variant_name, bool enable) : - wxEvent(winid, eventType), - vendor_id(std::move(vendor_id)), - model_id(std::move(model_id)), - variant_name(std::move(variant_name)), - enable(enable) - {} + PrinterPickerEvent(wxEventType eventType, int winid, std::string vendor_id, std::string model_id, std::string variant_name, bool enable) + : wxEvent(winid, eventType) + , vendor_id(std::move(vendor_id)) + , model_id(std::move(model_id)) + , variant_name(std::move(variant_name)) + , enable(enable) + {} - virtual wxEvent *Clone() const - { - return new PrinterPickerEvent(*this); - } + virtual wxEvent *Clone() const + { + return new PrinterPickerEvent(*this); + } }; wxDEFINE_EVENT(EVT_PRINTER_PICK, PrinterPickerEvent); -PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, const AppConfig &appconfig_vendors) : - wxPanel(parent), - vendor_id(vendor.id), - variants_checked(0) +PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxString title, size_t max_cols, const AppConfig &appconfig_vendors, const ModelFilter &filter) + : wxPanel(parent) + , vendor_id(vendor.id) { - const auto &models = vendor.models; + const auto &models = vendor.models; - auto *sizer = new wxBoxSizer(wxVERTICAL); + auto *sizer = new wxBoxSizer(wxVERTICAL); - auto *printer_grid = new wxFlexGridSizer(models.size(), 0, 20); - printer_grid->SetFlexibleDirection(wxVERTICAL | wxHORIZONTAL); - sizer->Add(printer_grid); + const auto font_title = GetFont().MakeBold().Scaled(1.3); + const auto font_name = GetFont().MakeBold(); + const auto font_alt_nozzle = GetFont().Scaled(0.9); - auto namefont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); - namefont.SetWeight(wxFONTWEIGHT_BOLD); + // wxGrid appends widgets by rows, but we need to construct them in columns. + // These vectors are used to hold the elements so that they can be appended in the right order. + std::vector titles; + std::vector bitmaps; + std::vector variants_panels; - // wxGrid appends widgets by rows, but we need to construct them in columns. - // These vectors are used to hold the elements so that they can be appended in the right order. - std::vector titles; - std::vector bitmaps; - std::vector variants_panels; + for (const auto &model : models) { + if (! filter(model)) { continue; } - for (const auto &model : models) { - wxBitmap bitmap(GUI::from_u8(Slic3r::var((boost::format("printers/%1%_%2%.png") % vendor.id % model.id).str())), wxBITMAP_TYPE_PNG); + wxBitmap bitmap(GUI::from_u8(Slic3r::var((boost::format("printers/%1%_%2%.png") % vendor.id % model.id).str())), wxBITMAP_TYPE_PNG); - auto *title = new wxStaticText(this, wxID_ANY, model.name, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); - title->SetFont(namefont); - title->Wrap(std::max((int)MODEL_MIN_WRAP, bitmap.GetWidth())); - titles.push_back(title); + auto *title = new wxStaticText(this, wxID_ANY, model.name, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); + title->SetFont(font_name); + title->Wrap(std::max((int)MODEL_MIN_WRAP, bitmap.GetWidth())); + titles.push_back(title); - auto *bitmap_widget = new wxStaticBitmap(this, wxID_ANY, bitmap); - bitmaps.push_back(bitmap_widget); + auto *bitmap_widget = new wxStaticBitmap(this, wxID_ANY, bitmap); + bitmaps.push_back(bitmap_widget); - auto *variants_panel = new wxPanel(this); - auto *variants_sizer = new wxBoxSizer(wxVERTICAL); - variants_panel->SetSizer(variants_sizer); - const auto model_id = model.id; + auto *variants_panel = new wxPanel(this); + auto *variants_sizer = new wxBoxSizer(wxVERTICAL); + variants_panel->SetSizer(variants_sizer); + const auto model_id = model.id; - bool default_variant = true; // Mark the first variant as default in the GUI - for (const auto &variant : model.variants) { - const auto label = wxString::Format("%s %s %s %s", variant.name, _(L("mm")), _(L("nozzle")), - (default_variant ? "(" + _(L("default")) + ")" : wxString())); - default_variant = false; - auto *cbox = new Checkbox(variants_panel, label, model_id, variant.name); - const size_t idx = cboxes.size(); - cboxes.push_back(cbox); - bool enabled = appconfig_vendors.get_variant("PrusaResearch", model_id, variant.name); - variants_checked += enabled; - cbox->SetValue(enabled); - variants_sizer->Add(cbox, 0, wxBOTTOM, 3); - cbox->Bind(wxEVT_CHECKBOX, [this, idx](wxCommandEvent &event) { - if (idx >= this->cboxes.size()) { return; } - this->on_checkbox(this->cboxes[idx], event.IsChecked()); - }); - } + for (size_t i = 0; i < model.variants.size(); i++) { + const auto &variant = model.variants[i]; - variants_panels.push_back(variants_panel); - } + const auto label = model.technology == ptFFF + ? wxString::Format("%s %s %s", variant.name, _(L("mm")), _(L("nozzle"))) + : from_u8(model.name); - for (auto title : titles) { printer_grid->Add(title, 0, wxBOTTOM, 3); } - for (auto bitmap : bitmaps) { printer_grid->Add(bitmap, 0, wxBOTTOM, 20); } - for (auto vp : variants_panels) { printer_grid->Add(vp); } + if (i == 1) { + auto *alt_label = new wxStaticText(variants_panel, wxID_ANY, _(L("Alternate nozzles:"))); + alt_label->SetFont(font_alt_nozzle); + variants_sizer->Add(alt_label, 0, wxBOTTOM, 3); + } - auto *all_none_sizer = new wxBoxSizer(wxHORIZONTAL); - auto *sel_all = new wxButton(this, wxID_ANY, _(L("Select all"))); - auto *sel_none = new wxButton(this, wxID_ANY, _(L("Select none"))); - sel_all->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &event) { this->select_all(true); }); - sel_none->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &event) { this->select_all(false); }); - all_none_sizer->AddStretchSpacer(); - all_none_sizer->Add(sel_all); - all_none_sizer->Add(sel_none); - sizer->AddStretchSpacer(); - sizer->Add(all_none_sizer, 0, wxEXPAND); + auto *cbox = new Checkbox(variants_panel, label, model_id, variant.name); + i == 0 ? cboxes.push_back(cbox) : cboxes_alt.push_back(cbox); - SetSizer(sizer); + bool enabled = appconfig_vendors.get_variant("PrusaResearch", model_id, variant.name); + cbox->SetValue(enabled); + + variants_sizer->Add(cbox, 0, wxBOTTOM, 3); + + cbox->Bind(wxEVT_CHECKBOX, [this, cbox](wxCommandEvent &event) { + on_checkbox(cbox, event.IsChecked()); + }); + } + + variants_panels.push_back(variants_panel); + } + + const size_t cols = std::min(max_cols, titles.size()); + + auto *printer_grid = new wxFlexGridSizer(cols, 0, 20); + printer_grid->SetFlexibleDirection(wxVERTICAL | wxHORIZONTAL); + + if (titles.size() > 0) { + const size_t odd_items = titles.size() % cols; + + for (size_t i = 0; i < titles.size() - odd_items; i += cols) { + for (size_t j = i; j < i + cols; j++) { printer_grid->Add(titles[j], 0, wxBOTTOM, 3); } + for (size_t j = i; j < i + cols; j++) { printer_grid->Add(bitmaps[j], 0, wxBOTTOM, 20); } + for (size_t j = i; j < i + cols; j++) { printer_grid->Add(variants_panels[j]); } + + // Add separator space + if (i > 0) { + for (size_t j = i; j < i + cols; j++) { printer_grid->Add(1, 100); } + } + } + + if (odd_items > 0) { + for (size_t i = 0; i < cols; i++) { printer_grid->Add(1, 100); } + + const size_t rem = titles.size() - odd_items; + + for (size_t i = rem; i < titles.size(); i++) { printer_grid->Add(titles[i], 0, wxBOTTOM, 3); } + for (size_t i = 0; i < cols - odd_items; i++) { printer_grid->AddSpacer(1); } + for (size_t i = rem; i < titles.size(); i++) { printer_grid->Add(bitmaps[i], 0, wxBOTTOM, 20); } + for (size_t i = 0; i < cols - odd_items; i++) { printer_grid->AddSpacer(1); } + for (size_t i = rem; i < titles.size(); i++) { printer_grid->Add(variants_panels[i]); } + } + } + + auto *title_sizer = new wxBoxSizer(wxHORIZONTAL); + auto *title_widget = new wxStaticText(this, wxID_ANY, title); + title_widget->SetFont(font_title); + title_sizer->Add(title_widget); + title_sizer->AddStretchSpacer(); + + if (titles.size() > 1) { + // It only makes sense to add the All / None buttons if there's multiple printers + + auto *sel_all = new wxButton(this, wxID_ANY, _(L("All"))); + auto *sel_none = new wxButton(this, wxID_ANY, _(L("None"))); + sel_all->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &event) { this->select_all(true); }); + sel_none->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &event) { this->select_all(false); }); + title_sizer->Add(sel_all, 0, wxRIGHT, BTN_SPACING); + title_sizer->Add(sel_none); + } + + sizer->Add(title_sizer, 0, wxEXPAND | wxBOTTOM, BTN_SPACING); + sizer->Add(printer_grid); + + // auto *all_none_sizer = new wxBoxSizer(wxHORIZONTAL); + // auto *sel_all = new wxButton(this, wxID_ANY, _(L("Select all"))); + // auto *sel_none = new wxButton(this, wxID_ANY, _(L("Select none"))); + // sel_all->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &event) { this->select_all(true); }); + // sel_none->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &event) { this->select_all(false); }); + // all_none_sizer->AddStretchSpacer(); + // all_none_sizer->Add(sel_all); + // all_none_sizer->Add(sel_none); + // sizer->AddStretchSpacer(); + // sizer->Add(all_none_sizer, 0, wxEXPAND); + + SetSizer(sizer); } +PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxString title, size_t max_cols, const AppConfig &appconfig_vendors) + : PrinterPicker(parent, vendor, std::move(title), max_cols, appconfig_vendors, [](const VendorProfile::PrinterModel&) { return true; }) +{} + void PrinterPicker::select_all(bool select) { - for (const auto &cb : cboxes) { - if (cb->GetValue() != select) { - cb->SetValue(select); - on_checkbox(cb, select); - } - } + for (const auto &cb : cboxes) { + if (cb->GetValue() != select) { + cb->SetValue(select); + on_checkbox(cb, select); + } + } + + // Alt nozzles are de-selected if this is an all-deselect, left intact otherwise + if (! select) { + for (const auto &cb : cboxes_alt) { + if (cb->GetValue()) { + cb->SetValue(false); + on_checkbox(cb, false); + } + } + } } void PrinterPicker::select_one(size_t i, bool select) { - if (i < cboxes.size() && cboxes[i]->GetValue() != select) { - cboxes[i]->SetValue(select); - on_checkbox(cboxes[i], select); - } + if (i < cboxes.size() && cboxes[i]->GetValue() != select) { + cboxes[i]->SetValue(select); + on_checkbox(cboxes[i], select); + } } void PrinterPicker::on_checkbox(const Checkbox *cbox, bool checked) { - variants_checked += checked ? 1 : -1; - PrinterPickerEvent evt(EVT_PRINTER_PICK, GetId(), vendor_id, cbox->model, cbox->variant, checked); - AddPendingEvent(evt); + PrinterPickerEvent evt(EVT_PRINTER_PICK, GetId(), vendor_id, cbox->model, cbox->variant, checked); + AddPendingEvent(evt); } // Wizard page base -ConfigWizardPage::ConfigWizardPage(ConfigWizard *parent, wxString title, wxString shortname) : - wxPanel(parent->p->hscroll), - parent(parent), - shortname(std::move(shortname)), - p_prev(nullptr), - p_next(nullptr) +ConfigWizardPage::ConfigWizardPage(ConfigWizard *parent, wxString title, wxString shortname, unsigned indent) + : wxPanel(parent->p->hscroll) + , parent(parent) + , shortname(std::move(shortname)) + , indent(indent) { - auto *sizer = new wxBoxSizer(wxVERTICAL); + auto *sizer = new wxBoxSizer(wxVERTICAL); - auto *text = new wxStaticText(this, wxID_ANY, std::move(title), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); - auto font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); - font.SetWeight(wxFONTWEIGHT_BOLD); - font.SetPointSize(14); - text->SetFont(font); - sizer->Add(text, 0, wxALIGN_LEFT, 0); - sizer->AddSpacer(10); + auto *text = new wxStaticText(this, wxID_ANY, std::move(title), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); + const auto font = GetFont().MakeBold().Scaled(1.5); + text->SetFont(font); + sizer->Add(text, 0, wxALIGN_LEFT, 0); + sizer->AddSpacer(10); - content = new wxBoxSizer(wxVERTICAL); - sizer->Add(content, 1); + content = new wxBoxSizer(wxVERTICAL); + sizer->Add(content, 1); - SetSizer(sizer); + SetSizer(sizer); - this->Hide(); + this->Hide(); - Bind(wxEVT_SIZE, [this](wxSizeEvent &event) { - this->Layout(); - event.Skip(); - }); + Bind(wxEVT_SIZE, [this](wxSizeEvent &event) { + this->Layout(); + event.Skip(); + }); } ConfigWizardPage::~ConfigWizardPage() {} -ConfigWizardPage* ConfigWizardPage::chain(ConfigWizardPage *page) -{ - if (p_next != nullptr) { p_next->p_prev = nullptr; } - p_next = page; - if (page != nullptr) { - if (page->p_prev != nullptr) { page->p_prev->p_next = nullptr; } - page->p_prev = this; - } - - return page; -} - void ConfigWizardPage::append_text(wxString text) { - auto *widget = new wxStaticText(this, wxID_ANY, text, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); - widget->Wrap(WRAP_WIDTH); - widget->SetMinSize(wxSize(WRAP_WIDTH, -1)); - append(widget); + auto *widget = new wxStaticText(this, wxID_ANY, text, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); + widget->Wrap(WRAP_WIDTH); + widget->SetMinSize(wxSize(WRAP_WIDTH, -1)); + append(widget); } void ConfigWizardPage::append_spacer(int space) { - content->AddSpacer(space); + content->AddSpacer(space); } bool ConfigWizardPage::Show(bool show) { - if (extra_buttons() != nullptr) { extra_buttons()->Show(show); } - return wxPanel::Show(show); + if (extra_buttons() != nullptr) { extra_buttons()->Show(show); } + return wxPanel::Show(show); } -void ConfigWizardPage::enable_next(bool enable) { parent->p->enable_next(enable); } - // Wizard pages -PageWelcome::PageWelcome(ConfigWizard *parent, bool check_first_variant) : - ConfigWizardPage(parent, wxString::Format(_(L("Welcome to the Slic3r %s")), ConfigWizard::name()), _(L("Welcome"))), - printer_picker(nullptr), - others_buttons(new wxPanel(parent)), - cbox_reset(nullptr) +PageWelcome::PageWelcome(ConfigWizard *parent) + : ConfigWizardPage(parent, wxString::Format(_(L("Welcome to the Slic3r %s")), ConfigWizard::name()), _(L("Welcome"))) + , cbox_reset(nullptr) { - if (wizard_p()->run_reason == ConfigWizard::RR_DATA_EMPTY) { - wxString::Format(_(L("Run %s")), ConfigWizard::name()); - append_text(wxString::Format( - _(L("Hello, welcome to Slic3r Prusa Edition! This %s helps you with the initial configuration; just a few settings and you will be ready to print.")), - ConfigWizard::name()) - ); - } else { - cbox_reset = new wxCheckBox(this, wxID_ANY, _(L("Remove user profiles - install from scratch (a snapshot will be taken beforehand)"))); - append(cbox_reset); - } + if (wizard_p()->run_reason == ConfigWizard::RR_DATA_EMPTY) { + wxString::Format(_(L("Run %s")), ConfigWizard::name()); + append_text(wxString::Format( + _(L("Hello, welcome to Slic3r Prusa Edition! This %s helps you with the initial configuration; just a few settings and you will be ready to print.")), + ConfigWizard::name()) + ); + } else { + cbox_reset = new wxCheckBox(this, wxID_ANY, _(L("Remove user profiles - install from scratch (a snapshot will be taken beforehand)"))); + append(cbox_reset); + } - const auto &vendors = wizard_p()->vendors; - const auto vendor_prusa = vendors.find("PrusaResearch"); - - if (vendor_prusa != vendors.cend()) { - AppConfig &appconfig_vendors = this->wizard_p()->appconfig_vendors; - - printer_picker = new PrinterPicker(this, vendor_prusa->second, appconfig_vendors); - if (check_first_variant) { - // Select the default (first) model/variant on the Prusa vendor - printer_picker->select_one(0, true); - } - printer_picker->Bind(EVT_PRINTER_PICK, [this, &appconfig_vendors](const PrinterPickerEvent &evt) { - appconfig_vendors.set_variant(evt.vendor_id, evt.model_id, evt.variant_name, evt.enable); - this->on_variant_checked(); - }); - - append(printer_picker); - } - - const size_t num_other_vendors = vendors.size() - (vendor_prusa != vendors.cend()); - auto *sizer = new wxBoxSizer(wxHORIZONTAL); - auto *other_vendors = new wxButton(others_buttons, wxID_ANY, _(L("Other vendors"))); - other_vendors->Enable(num_other_vendors > 0); - auto *custom_setup = new wxButton(others_buttons, wxID_ANY, _(L("Custom setup"))); - - sizer->Add(other_vendors); - sizer->AddSpacer(BTN_SPACING); - sizer->Add(custom_setup); - - other_vendors->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &event) { this->wizard_p()->on_other_vendors(); }); - custom_setup->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &event) { this->wizard_p()->on_custom_setup(); }); - - others_buttons->SetSizer(sizer); + Show(); } -void PageWelcome::on_page_set() + +PagePrinters::PagePrinters(ConfigWizard *parent, wxString title, wxString shortname, const VendorProfile &vendor, unsigned indent, Technology technology) + : ConfigWizardPage(parent, std::move(title), std::move(shortname), indent) { - chain(wizard_p()->page_update); - on_variant_checked(); + enum { + COL_SIZE = 200, + }; + + bool check_first_variant = wizard_p()->check_first_variant(); + + AppConfig &appconfig_vendors = this->wizard_p()->appconfig_vendors; + + const auto families = vendor.families(); + for (const auto &family : families) { + const auto filter = [&](const VendorProfile::PrinterModel &model) { + return (model.technology == ptFFF && technology & T_FFF + || model.technology == ptSLA && technology & T_SLA) + && model.family == family; + }; + + if (std::find_if(vendor.models.begin(), vendor.models.end(), filter) == vendor.models.end()) { + continue; + } + + const auto picker_title = family.empty() ? wxString() : wxString::Format(_(L("%s Family")), family); + auto *picker = new PrinterPicker(this, vendor, picker_title, MAX_COLS, appconfig_vendors, filter); + + if (check_first_variant) { + // Select the default (first) model/variant on the Prusa vendor + picker->select_one(0, true); + check_first_variant = false; + } + + picker->Bind(EVT_PRINTER_PICK, [this, &appconfig_vendors](const PrinterPickerEvent &evt) { + appconfig_vendors.set_variant(evt.vendor_id, evt.model_id, evt.variant_name, evt.enable); + }); + + append(new wxStaticLine(this)); + + append(picker); + printer_pickers.push_back(picker); + } } -void PageWelcome::on_variant_checked() +void PagePrinters::select_all(bool select) { - enable_next(printer_picker != nullptr ? printer_picker->variants_checked > 0 : false); + for (auto picker : printer_pickers) { + picker->select_all(select); + } } -PageUpdate::PageUpdate(ConfigWizard *parent) : - ConfigWizardPage(parent, _(L("Automatic updates")), _(L("Updates"))), - version_check(true), - preset_update(true) + +PageCustom::PageCustom(ConfigWizard *parent) + : ConfigWizardPage(parent, _(L("Custom Printer Setup")), _(L("Custom Printer"))) { - const AppConfig *app_config = GUI::get_app_config(); - auto boldfont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); - boldfont.SetWeight(wxFONTWEIGHT_BOLD); + cb_custom = new wxCheckBox(this, wxID_ANY, _(L("Define a custom printer profile"))); + tc_profile_name = new wxTextCtrl(this, wxID_ANY, "My Settings"); + auto *label = new wxStaticText(this, wxID_ANY, _(L("Custom profile name:"))); - auto *box_slic3r = new wxCheckBox(this, wxID_ANY, _(L("Check for application updates"))); - box_slic3r->SetValue(app_config->get("version_check") == "1"); - append(box_slic3r); - append_text(_(L("If enabled, Slic3r checks for new versions of Slic3r PE online. When a new version becomes available a notification is displayed at the next application startup (never during program usage). This is only a notification mechanisms, no automatic installation is done."))); + cb_custom->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent &event) { + wizard_p()->on_custom_setup(custom_wanted()); + }); - append_spacer(VERTICAL_SPACING); - - auto *box_presets = new wxCheckBox(this, wxID_ANY, _(L("Update built-in Presets automatically"))); - box_presets->SetValue(app_config->get("preset_update") == "1"); - append(box_presets); - append_text(_(L("If enabled, Slic3r downloads updates of built-in system presets in the background. These updates are downloaded into a separate temporary location. When a new preset version becomes available it is offered at application startup."))); - const auto text_bold = _(L("Updates are never applied without user's consent and never overwrite user's customized settings.")); - auto *label_bold = new wxStaticText(this, wxID_ANY, text_bold); - label_bold->SetFont(boldfont); - label_bold->Wrap(WRAP_WIDTH); - append(label_bold); - append_text(_(L("Additionally a backup snapshot of the whole configuration is created before an update is applied."))); - - box_slic3r->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent &event) { this->version_check = event.IsChecked(); }); - box_presets->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent &event) { this->preset_update = event.IsChecked(); }); + append(cb_custom); + append(label); + append(tc_profile_name); } -PageVendors::PageVendors(ConfigWizard *parent) : - ConfigWizardPage(parent, _(L("Other Vendors")), _(L("Other Vendors"))) +PageUpdate::PageUpdate(ConfigWizard *parent) + : ConfigWizardPage(parent, _(L("Automatic updates")), _(L("Updates"))) + , version_check(true) + , preset_update(true) { - append_text(_(L("Pick another vendor supported by Slic3r PE:"))); + const AppConfig *app_config = GUI::get_app_config(); + auto boldfont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); + boldfont.SetWeight(wxFONTWEIGHT_BOLD); - auto boldfont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); - boldfont.SetWeight(wxFONTWEIGHT_BOLD); + auto *box_slic3r = new wxCheckBox(this, wxID_ANY, _(L("Check for application updates"))); + box_slic3r->SetValue(app_config->get("version_check") == "1"); + append(box_slic3r); + append_text(_(L("If enabled, Slic3r checks for new versions of Slic3r PE online. When a new version becomes available a notification is displayed at the next application startup (never during program usage). This is only a notification mechanisms, no automatic installation is done."))); - AppConfig &appconfig_vendors = this->wizard_p()->appconfig_vendors; - wxArrayString choices_vendors; + append_spacer(VERTICAL_SPACING); - for (const auto vendor_pair : wizard_p()->vendors) { - const auto &vendor = vendor_pair.second; - if (vendor.id == "PrusaResearch") { continue; } + auto *box_presets = new wxCheckBox(this, wxID_ANY, _(L("Update built-in Presets automatically"))); + box_presets->SetValue(app_config->get("preset_update") == "1"); + append(box_presets); + append_text(_(L("If enabled, Slic3r downloads updates of built-in system presets in the background. These updates are downloaded into a separate temporary location. When a new preset version becomes available it is offered at application startup."))); + const auto text_bold = _(L("Updates are never applied without user's consent and never overwrite user's customized settings.")); + auto *label_bold = new wxStaticText(this, wxID_ANY, text_bold); + label_bold->SetFont(boldfont); + label_bold->Wrap(WRAP_WIDTH); + append(label_bold); + append_text(_(L("Additionally a backup snapshot of the whole configuration is created before an update is applied."))); - auto *picker = new PrinterPicker(this, vendor, appconfig_vendors); - picker->Hide(); - pickers.push_back(picker); - choices_vendors.Add(vendor.name); - - picker->Bind(EVT_PRINTER_PICK, [this, &appconfig_vendors](const PrinterPickerEvent &evt) { - appconfig_vendors.set_variant(evt.vendor_id, evt.model_id, evt.variant_name, evt.enable); - this->on_variant_checked(); - }); - } - - auto *vendor_picker = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, choices_vendors); - if (choices_vendors.GetCount() > 0) { - vendor_picker->SetSelection(0); - on_vendor_pick(0); - } - - vendor_picker->Bind(wxEVT_CHOICE, [this](wxCommandEvent &evt) { - this->on_vendor_pick(evt.GetInt()); - }); - - append(vendor_picker); - for (PrinterPicker *picker : pickers) { this->append(picker); } + box_slic3r->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent &event) { this->version_check = event.IsChecked(); }); + box_presets->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent &event) { this->preset_update = event.IsChecked(); }); } -void PageVendors::on_page_set() +PageVendors::PageVendors(ConfigWizard *parent) + : ConfigWizardPage(parent, _(L("Other Vendors")), _(L("Other Vendors"))) { - on_variant_checked(); + append_text(_(L("Pick another vendor supported by Slic3r PE:"))); + + auto boldfont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); + boldfont.SetWeight(wxFONTWEIGHT_BOLD); + + AppConfig &appconfig_vendors = this->wizard_p()->appconfig_vendors; + wxArrayString choices_vendors; + + for (const auto vendor_pair : wizard_p()->vendors) { + const auto &vendor = vendor_pair.second; + if (vendor.id == "PrusaResearch") { continue; } + + auto *picker = new PrinterPicker(this, vendor, "", MAX_COLS, appconfig_vendors); + picker->Hide(); + pickers.push_back(picker); + choices_vendors.Add(vendor.name); + + picker->Bind(EVT_PRINTER_PICK, [this, &appconfig_vendors](const PrinterPickerEvent &evt) { + appconfig_vendors.set_variant(evt.vendor_id, evt.model_id, evt.variant_name, evt.enable); + }); + } + + auto *vendor_picker = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, choices_vendors); + if (choices_vendors.GetCount() > 0) { + vendor_picker->SetSelection(0); + on_vendor_pick(0); + } + + vendor_picker->Bind(wxEVT_CHOICE, [this](wxCommandEvent &evt) { + this->on_vendor_pick(evt.GetInt()); + }); + + append(vendor_picker); + for (PrinterPicker *picker : pickers) { this->append(picker); } } void PageVendors::on_vendor_pick(size_t i) { - for (PrinterPicker *picker : pickers) { picker->Hide(); } - if (i < pickers.size()) { - pickers[i]->Show(); - wizard_p()->layout_fit(); - } + for (PrinterPicker *picker : pickers) { picker->Hide(); } + if (i < pickers.size()) { + pickers[i]->Show(); + parent->Layout(); + } } -void PageVendors::on_variant_checked() +PageFirmware::PageFirmware(ConfigWizard *parent) + : ConfigWizardPage(parent, _(L("Firmware Type")), _(L("Firmware")), 1) + , gcode_opt(*print_config_def.get("gcode_flavor")) + , gcode_picker(nullptr) { - size_t variants_checked = 0; - for (const PrinterPicker *picker : pickers) { variants_checked += picker->variants_checked; } - enable_next(variants_checked > 0); -} + append_text(_(L("Choose the type of firmware used by your printer."))); + append_text(gcode_opt.tooltip); -PageFirmware::PageFirmware(ConfigWizard *parent) : - ConfigWizardPage(parent, _(L("Firmware Type")), _(L("Firmware"))), - gcode_opt(*print_config_def.get("gcode_flavor")), - gcode_picker(nullptr) -{ - append_text(_(L("Choose the type of firmware used by your printer."))); - append_text(gcode_opt.tooltip); + wxArrayString choices; + choices.Alloc(gcode_opt.enum_labels.size()); + for (const auto &label : gcode_opt.enum_labels) { + choices.Add(label); + } - wxArrayString choices; - choices.Alloc(gcode_opt.enum_labels.size()); - for (const auto &label : gcode_opt.enum_labels) { - choices.Add(label); - } + gcode_picker = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, choices); + const auto &enum_values = gcode_opt.enum_values; + auto needle = enum_values.cend(); + if (gcode_opt.default_value != nullptr) { + needle = std::find(enum_values.cbegin(), enum_values.cend(), gcode_opt.default_value->serialize()); + } + if (needle != enum_values.cend()) { + gcode_picker->SetSelection(needle - enum_values.cbegin()); + } else { + gcode_picker->SetSelection(0); + } - gcode_picker = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, choices); - const auto &enum_values = gcode_opt.enum_values; - auto needle = enum_values.cend(); - if (gcode_opt.default_value != nullptr) { - needle = std::find(enum_values.cbegin(), enum_values.cend(), gcode_opt.default_value->serialize()); - } - if (needle != enum_values.cend()) { - gcode_picker->SetSelection(needle - enum_values.cbegin()); - } else { - gcode_picker->SetSelection(0); - } - - append(gcode_picker); + append(gcode_picker); } void PageFirmware::apply_custom_config(DynamicPrintConfig &config) { - auto sel = gcode_picker->GetSelection(); - if (sel >= 0 && sel < gcode_opt.enum_labels.size()) { - auto *opt = new ConfigOptionEnum(static_cast(sel)); - config.set_key_value("gcode_flavor", opt); - } + auto sel = gcode_picker->GetSelection(); + if (sel >= 0 && (size_t)sel < gcode_opt.enum_labels.size()) { + auto *opt = new ConfigOptionEnum(static_cast(sel)); + config.set_key_value("gcode_flavor", opt); + } } -PageBedShape::PageBedShape(ConfigWizard *parent) : - ConfigWizardPage(parent, _(L("Bed Shape and Size")), _(L("Bed Shape"))), - shape_panel(new BedShapePanel(this)) +PageBedShape::PageBedShape(ConfigWizard *parent) + : ConfigWizardPage(parent, _(L("Bed Shape and Size")), _(L("Bed Shape")), 1) + , shape_panel(new BedShapePanel(this)) { - append_text(_(L("Set the shape of your printer's bed."))); + append_text(_(L("Set the shape of your printer's bed."))); - shape_panel->build_panel(wizard_p()->custom_config->option("bed_shape")); - append(shape_panel); + shape_panel->build_panel(wizard_p()->custom_config->option("bed_shape")); + append(shape_panel); } void PageBedShape::apply_custom_config(DynamicPrintConfig &config) { - const auto points(shape_panel->GetValue()); - auto *opt = new ConfigOptionPoints(points); - config.set_key_value("bed_shape", opt); + const auto points(shape_panel->GetValue()); + auto *opt = new ConfigOptionPoints(points); + config.set_key_value("bed_shape", opt); } -PageDiameters::PageDiameters(ConfigWizard *parent) : - ConfigWizardPage(parent, _(L("Filament and Nozzle Diameters")), _(L("Print Diameters"))), - spin_nozzle(new wxSpinCtrlDouble(this, wxID_ANY)), - spin_filam(new wxSpinCtrlDouble(this, wxID_ANY)) +PageDiameters::PageDiameters(ConfigWizard *parent) + : ConfigWizardPage(parent, _(L("Filament and Nozzle Diameters")), _(L("Print Diameters")), 1) + , spin_nozzle(new wxSpinCtrlDouble(this, wxID_ANY)) + , spin_filam(new wxSpinCtrlDouble(this, wxID_ANY)) { - spin_nozzle->SetDigits(2); - spin_nozzle->SetIncrement(0.1); - const auto &def_nozzle = *print_config_def.get("nozzle_diameter"); - auto *default_nozzle = dynamic_cast(def_nozzle.default_value); - spin_nozzle->SetValue(default_nozzle != nullptr && default_nozzle->size() > 0 ? default_nozzle->get_at(0) : 0.5); + spin_nozzle->SetDigits(2); + spin_nozzle->SetIncrement(0.1); + const auto &def_nozzle = *print_config_def.get("nozzle_diameter"); + auto *default_nozzle = dynamic_cast(def_nozzle.default_value); + spin_nozzle->SetValue(default_nozzle != nullptr && default_nozzle->size() > 0 ? default_nozzle->get_at(0) : 0.5); - spin_filam->SetDigits(2); - spin_filam->SetIncrement(0.25); - const auto &def_filam = *print_config_def.get("filament_diameter"); - auto *default_filam = dynamic_cast(def_filam.default_value); - spin_filam->SetValue(default_filam != nullptr && default_filam->size() > 0 ? default_filam->get_at(0) : 3.0); + spin_filam->SetDigits(2); + spin_filam->SetIncrement(0.25); + const auto &def_filam = *print_config_def.get("filament_diameter"); + auto *default_filam = dynamic_cast(def_filam.default_value); + spin_filam->SetValue(default_filam != nullptr && default_filam->size() > 0 ? default_filam->get_at(0) : 3.0); - append_text(_(L("Enter the diameter of your printer's hot end nozzle."))); + append_text(_(L("Enter the diameter of your printer's hot end nozzle."))); - auto *sizer_nozzle = new wxFlexGridSizer(3, 5, 5); - auto *text_nozzle = new wxStaticText(this, wxID_ANY, _(L("Nozzle Diameter:"))); - auto *unit_nozzle = new wxStaticText(this, wxID_ANY, _(L("mm"))); - sizer_nozzle->AddGrowableCol(0, 1); - sizer_nozzle->Add(text_nozzle, 0, wxALIGN_CENTRE_VERTICAL); - sizer_nozzle->Add(spin_nozzle); - sizer_nozzle->Add(unit_nozzle, 0, wxALIGN_CENTRE_VERTICAL); - append(sizer_nozzle); + auto *sizer_nozzle = new wxFlexGridSizer(3, 5, 5); + auto *text_nozzle = new wxStaticText(this, wxID_ANY, _(L("Nozzle Diameter:"))); + auto *unit_nozzle = new wxStaticText(this, wxID_ANY, _(L("mm"))); + sizer_nozzle->AddGrowableCol(0, 1); + sizer_nozzle->Add(text_nozzle, 0, wxALIGN_CENTRE_VERTICAL); + sizer_nozzle->Add(spin_nozzle); + sizer_nozzle->Add(unit_nozzle, 0, wxALIGN_CENTRE_VERTICAL); + append(sizer_nozzle); - append_spacer(VERTICAL_SPACING); + append_spacer(VERTICAL_SPACING); - append_text(_(L("Enter the diameter of your filament."))); - append_text(_(L("Good precision is required, so use a caliper and do multiple measurements along the filament, then compute the average."))); + append_text(_(L("Enter the diameter of your filament."))); + append_text(_(L("Good precision is required, so use a caliper and do multiple measurements along the filament, then compute the average."))); - auto *sizer_filam = new wxFlexGridSizer(3, 5, 5); - auto *text_filam = new wxStaticText(this, wxID_ANY, _(L("Filament Diameter:"))); - auto *unit_filam = new wxStaticText(this, wxID_ANY, _(L("mm"))); - sizer_filam->AddGrowableCol(0, 1); - sizer_filam->Add(text_filam, 0, wxALIGN_CENTRE_VERTICAL); - sizer_filam->Add(spin_filam); - sizer_filam->Add(unit_filam, 0, wxALIGN_CENTRE_VERTICAL); - append(sizer_filam); + auto *sizer_filam = new wxFlexGridSizer(3, 5, 5); + auto *text_filam = new wxStaticText(this, wxID_ANY, _(L("Filament Diameter:"))); + auto *unit_filam = new wxStaticText(this, wxID_ANY, _(L("mm"))); + sizer_filam->AddGrowableCol(0, 1); + sizer_filam->Add(text_filam, 0, wxALIGN_CENTRE_VERTICAL); + sizer_filam->Add(spin_filam); + sizer_filam->Add(unit_filam, 0, wxALIGN_CENTRE_VERTICAL); + append(sizer_filam); } void PageDiameters::apply_custom_config(DynamicPrintConfig &config) { - auto *opt_nozzle = new ConfigOptionFloats(1, spin_nozzle->GetValue()); - config.set_key_value("nozzle_diameter", opt_nozzle); - auto *opt_filam = new ConfigOptionFloats(1, spin_filam->GetValue()); - config.set_key_value("filament_diameter", opt_filam); + auto *opt_nozzle = new ConfigOptionFloats(1, spin_nozzle->GetValue()); + config.set_key_value("nozzle_diameter", opt_nozzle); + auto *opt_filam = new ConfigOptionFloats(1, spin_filam->GetValue()); + config.set_key_value("filament_diameter", opt_filam); } -PageTemperatures::PageTemperatures(ConfigWizard *parent) : - ConfigWizardPage(parent, _(L("Extruder and Bed Temperatures")), _(L("Temperatures"))), - spin_extr(new wxSpinCtrlDouble(this, wxID_ANY)), - spin_bed(new wxSpinCtrlDouble(this, wxID_ANY)) +PageTemperatures::PageTemperatures(ConfigWizard *parent) + : ConfigWizardPage(parent, _(L("Extruder and Bed Temperatures")), _(L("Temperatures")), 1) + , spin_extr(new wxSpinCtrlDouble(this, wxID_ANY)) + , spin_bed(new wxSpinCtrlDouble(this, wxID_ANY)) { - spin_extr->SetIncrement(5.0); - const auto &def_extr = *print_config_def.get("temperature"); - spin_extr->SetRange(def_extr.min, def_extr.max); - auto *default_extr = dynamic_cast(def_extr.default_value); - spin_extr->SetValue(default_extr != nullptr && default_extr->size() > 0 ? default_extr->get_at(0) : 200); + spin_extr->SetIncrement(5.0); + const auto &def_extr = *print_config_def.get("temperature"); + spin_extr->SetRange(def_extr.min, def_extr.max); + auto *default_extr = dynamic_cast(def_extr.default_value); + spin_extr->SetValue(default_extr != nullptr && default_extr->size() > 0 ? default_extr->get_at(0) : 200); - spin_bed->SetIncrement(5.0); - const auto &def_bed = *print_config_def.get("bed_temperature"); - spin_bed->SetRange(def_bed.min, def_bed.max); - auto *default_bed = dynamic_cast(def_bed.default_value); - spin_bed->SetValue(default_bed != nullptr && default_bed->size() > 0 ? default_bed->get_at(0) : 0); + spin_bed->SetIncrement(5.0); + const auto &def_bed = *print_config_def.get("bed_temperature"); + spin_bed->SetRange(def_bed.min, def_bed.max); + auto *default_bed = dynamic_cast(def_bed.default_value); + spin_bed->SetValue(default_bed != nullptr && default_bed->size() > 0 ? default_bed->get_at(0) : 0); - append_text(_(L("Enter the temperature needed for extruding your filament."))); - append_text(_(L("A rule of thumb is 160 to 230 °C for PLA, and 215 to 250 °C for ABS."))); + append_text(_(L("Enter the temperature needed for extruding your filament."))); + append_text(_(L("A rule of thumb is 160 to 230 °C for PLA, and 215 to 250 °C for ABS."))); - auto *sizer_extr = new wxFlexGridSizer(3, 5, 5); - auto *text_extr = new wxStaticText(this, wxID_ANY, _(L("Extrusion Temperature:"))); - auto *unit_extr = new wxStaticText(this, wxID_ANY, _(L("°C"))); - sizer_extr->AddGrowableCol(0, 1); - sizer_extr->Add(text_extr, 0, wxALIGN_CENTRE_VERTICAL); - sizer_extr->Add(spin_extr); - sizer_extr->Add(unit_extr, 0, wxALIGN_CENTRE_VERTICAL); - append(sizer_extr); + auto *sizer_extr = new wxFlexGridSizer(3, 5, 5); + auto *text_extr = new wxStaticText(this, wxID_ANY, _(L("Extrusion Temperature:"))); + auto *unit_extr = new wxStaticText(this, wxID_ANY, _(L("°C"))); + sizer_extr->AddGrowableCol(0, 1); + sizer_extr->Add(text_extr, 0, wxALIGN_CENTRE_VERTICAL); + sizer_extr->Add(spin_extr); + sizer_extr->Add(unit_extr, 0, wxALIGN_CENTRE_VERTICAL); + append(sizer_extr); - append_spacer(VERTICAL_SPACING); + append_spacer(VERTICAL_SPACING); - append_text(_(L("Enter the bed temperature needed for getting your filament to stick to your heated bed."))); - append_text(_(L("A rule of thumb is 60 °C for PLA and 110 °C for ABS. Leave zero if you have no heated bed."))); + append_text(_(L("Enter the bed temperature needed for getting your filament to stick to your heated bed."))); + append_text(_(L("A rule of thumb is 60 °C for PLA and 110 °C for ABS. Leave zero if you have no heated bed."))); - auto *sizer_bed = new wxFlexGridSizer(3, 5, 5); - auto *text_bed = new wxStaticText(this, wxID_ANY, _(L("Bed Temperature:"))); - auto *unit_bed = new wxStaticText(this, wxID_ANY, _(L("°C"))); - sizer_bed->AddGrowableCol(0, 1); - sizer_bed->Add(text_bed, 0, wxALIGN_CENTRE_VERTICAL); - sizer_bed->Add(spin_bed); - sizer_bed->Add(unit_bed, 0, wxALIGN_CENTRE_VERTICAL); - append(sizer_bed); + auto *sizer_bed = new wxFlexGridSizer(3, 5, 5); + auto *text_bed = new wxStaticText(this, wxID_ANY, _(L("Bed Temperature:"))); + auto *unit_bed = new wxStaticText(this, wxID_ANY, _(L("°C"))); + sizer_bed->AddGrowableCol(0, 1); + sizer_bed->Add(text_bed, 0, wxALIGN_CENTRE_VERTICAL); + sizer_bed->Add(spin_bed); + sizer_bed->Add(unit_bed, 0, wxALIGN_CENTRE_VERTICAL); + append(sizer_bed); } void PageTemperatures::apply_custom_config(DynamicPrintConfig &config) { - auto *opt_extr = new ConfigOptionInts(1, spin_extr->GetValue()); - config.set_key_value("temperature", opt_extr); - auto *opt_extr1st = new ConfigOptionInts(1, spin_extr->GetValue()); - config.set_key_value("first_layer_temperature", opt_extr1st); - auto *opt_bed = new ConfigOptionInts(1, spin_bed->GetValue()); - config.set_key_value("bed_temperature", opt_bed); - auto *opt_bed1st = new ConfigOptionInts(1, spin_bed->GetValue()); - config.set_key_value("first_layer_bed_temperature", opt_bed1st); + auto *opt_extr = new ConfigOptionInts(1, spin_extr->GetValue()); + config.set_key_value("temperature", opt_extr); + auto *opt_extr1st = new ConfigOptionInts(1, spin_extr->GetValue()); + config.set_key_value("first_layer_temperature", opt_extr1st); + auto *opt_bed = new ConfigOptionInts(1, spin_bed->GetValue()); + config.set_key_value("bed_temperature", opt_bed); + auto *opt_bed1st = new ConfigOptionInts(1, spin_bed->GetValue()); + config.set_key_value("first_layer_bed_temperature", opt_bed1st); } // Index -ConfigWizardIndex::ConfigWizardIndex(wxWindow *parent) : - wxPanel(parent), - bg(GUI::from_u8(Slic3r::var("Slic3r_192px_transparent.png")), wxBITMAP_TYPE_PNG), - bullet_black(GUI::from_u8(Slic3r::var("bullet_black.png")), wxBITMAP_TYPE_PNG), - bullet_blue(GUI::from_u8(Slic3r::var("bullet_blue.png")), wxBITMAP_TYPE_PNG), - bullet_white(GUI::from_u8(Slic3r::var("bullet_white.png")), wxBITMAP_TYPE_PNG) +ConfigWizardIndex::ConfigWizardIndex(wxWindow *parent) + : wxPanel(parent) + , bg(GUI::from_u8(Slic3r::var("Slic3r_192px_transparent.png")), wxBITMAP_TYPE_PNG) + , bullet_black(GUI::from_u8(Slic3r::var("bullet_black.png")), wxBITMAP_TYPE_PNG) + , bullet_blue(GUI::from_u8(Slic3r::var("bullet_blue.png")), wxBITMAP_TYPE_PNG) + , bullet_white(GUI::from_u8(Slic3r::var("bullet_white.png")), wxBITMAP_TYPE_PNG) + , item_active(0) + , item_hover(-1) + , last_page((size_t)-1) { - SetMinSize(bg.GetSize()); + SetMinSize(bg.GetSize()); - wxClientDC dc(this); - text_height = dc.GetCharHeight(); + const wxSize size = GetTextExtent("m"); + em = size.x; + em_h = size.y; - // Add logo bitmap. - // This could be done in on_paint() along with the index labels, but I've found it tricky - // to get the bitmap rendered well on all platforms with transparent background. - // In some cases it didn't work at all. And so wxStaticBitmap is used here instead, - // because it has all the platform quirks figured out. - auto *sizer = new wxBoxSizer(wxVERTICAL); - auto *logo = new wxStaticBitmap(this, wxID_ANY, bg); - sizer->AddStretchSpacer(); - sizer->Add(logo); - SetSizer(sizer); + // Add logo bitmap. + // This could be done in on_paint() along with the index labels, but I've found it tricky + // to get the bitmap rendered well on all platforms with transparent background. + // In some cases it didn't work at all. And so wxStaticBitmap is used here instead, + // because it has all the platform quirks figured out. + auto *sizer = new wxBoxSizer(wxVERTICAL); + auto *logo = new wxStaticBitmap(this, wxID_ANY, bg); + sizer->AddStretchSpacer(); + sizer->Add(logo); + SetSizer(sizer); - Bind(wxEVT_PAINT, &ConfigWizardIndex::on_paint, this); + Bind(wxEVT_PAINT, &ConfigWizardIndex::on_paint, this); + Bind(wxEVT_MOTION, &ConfigWizardIndex::on_mouse_move, this); + + Bind(wxEVT_LEAVE_WINDOW, [this](wxMouseEvent &evt) { + if (item_hover != -1) { + item_hover = -1; + Refresh(); + } + evt.Skip(); + }); + + Bind(wxEVT_LEFT_UP, [this](wxMouseEvent &evt) { + if (item_hover >= 0) { go_to(item_hover); } + }); } -void ConfigWizardIndex::load_items(ConfigWizardPage *firstpage) +wxDECLARE_EVENT(EVT_INDEX_PAGE, wxCommandEvent); + +void ConfigWizardIndex::add_page(ConfigWizardPage *page) { - items.clear(); - item_active = items.cend(); - - for (auto *page = firstpage; page != nullptr; page = page->page_next()) { - items.emplace_back(page->shortname); - } - - Refresh(); + last_page = items.size(); + items.emplace_back(Item { page->shortname, page->indent, page }); + Refresh(); } -void ConfigWizardIndex::set_active(ConfigWizardPage *page) +void ConfigWizardIndex::add_label(wxString label, unsigned indent) { - item_active = std::find(items.cbegin(), items.cend(), page->shortname); - Refresh(); + items.emplace_back(Item { std::move(label), indent, nullptr }); + Refresh(); +} + +ConfigWizardPage* ConfigWizardIndex::active_page() const +{ + if (item_active >= items.size()) { return nullptr; } + + return items[item_active].page; +} + +void ConfigWizardIndex::go_prev() +{ + // Search for a preceiding item that is a page (not a label, ie. page != nullptr) + + for (size_t i = item_active; i > 0; i--) { + if (items[i - 1].page != nullptr) { + go_to(i - 1); + return; + } + } +} + +void ConfigWizardIndex::go_next() +{ + // Search for a next item that is a page (not a label, ie. page != nullptr) + + for (size_t i = item_active + 1; i < items.size(); i++) { + if (items[i].page != nullptr) { + go_to(i); + return; + } + } +} + +void ConfigWizardIndex::go_to(size_t i) +{ + if (i < items.size() && items[i].page != nullptr) { + auto *former_active = active_page(); + if (former_active != nullptr) { former_active->Hide(); } + + item_active = i; + items[i].page->Show(); + + wxCommandEvent evt(EVT_INDEX_PAGE, GetId()); + AddPendingEvent(evt); + + Refresh(); + } +} + +void ConfigWizardIndex::go_to(ConfigWizardPage *page) +{ + if (page == nullptr) { return; } + + for (size_t i = 0; i < items.size(); i++) { + if (items[i].page == page) { go_to(i); } + } +} + +void ConfigWizardIndex::clear() +{ + auto *former_active = active_page(); + if (former_active != nullptr) { former_active->Hide(); } + + items.clear(); + item_active = 0; } void ConfigWizardIndex::on_paint(wxPaintEvent & evt) { - enum { - MARGIN = 10, - SPACING = 5, - }; + const auto size = GetClientSize(); + if (size.GetHeight() == 0 || size.GetWidth() == 0) { return; } - const auto size = GetClientSize(); - if (size.GetHeight() == 0 || size.GetWidth() == 0) { return; } + wxPaintDC dc(this); - wxPaintDC dc(this); + const auto bullet_w = bullet_black.GetSize().GetWidth(); + const auto bullet_h = bullet_black.GetSize().GetHeight(); + const int yoff_icon = bullet_h < em_h ? (em_h - bullet_h) / 2 : 0; + const int yoff_text = bullet_h > em_h ? (bullet_h - em_h) / 2 : 0; + const int yinc = item_height(); - const auto bullet_w = bullet_black.GetSize().GetWidth(); - const auto bullet_h = bullet_black.GetSize().GetHeight(); - const int yoff_icon = bullet_h < text_height ? (text_height - bullet_h) / 2 : 0; - const int yoff_text = bullet_h > text_height ? (bullet_h - text_height) / 2 : 0; - const int yinc = std::max(bullet_h, text_height) + SPACING; + unsigned y = 0; + for (size_t i = 0; i < items.size(); i++) { + const Item& item = items[i]; + unsigned x = em/2 + item.indent * em; - unsigned y = 0; - for (auto it = items.cbegin(); it != items.cend(); ++it) { - if (it < item_active) { dc.DrawBitmap(bullet_black, MARGIN, y + yoff_icon, false); } - if (it == item_active) { dc.DrawBitmap(bullet_blue, MARGIN, y + yoff_icon, false); } - if (it > item_active) { dc.DrawBitmap(bullet_white, MARGIN, y + yoff_icon, false); } - dc.DrawText(*it, MARGIN + bullet_w + SPACING, y + yoff_text); - y += yinc; - } + if (i == item_active || item_hover >= 0 && i == (size_t)item_hover) { + dc.DrawBitmap(bullet_blue, x, y + yoff_icon, false); + } + else if (i < item_active) { dc.DrawBitmap(bullet_black, x, y + yoff_icon, false); } + else if (i > item_active) { dc.DrawBitmap(bullet_white, x, y + yoff_icon, false); } + + dc.DrawText(item.label, x + bullet_w + em/2, y + yoff_text); + y += yinc; + } } +void ConfigWizardIndex::on_mouse_move(wxMouseEvent &evt) +{ + const wxClientDC dc(this); + const wxPoint pos = evt.GetLogicalPosition(dc); + + const ssize_t item_hover_new = pos.y / item_height(); + + if (item_hover_new < items.size() && item_hover_new != item_hover) { + item_hover = item_hover_new; + Refresh(); + } + + evt.Skip(); +} // priv static const std::unordered_map> legacy_preset_map {{ - { "Original Prusa i3 MK2.ini", std::make_pair("MK2S", "0.4") }, - { "Original Prusa i3 MK2 MM Single Mode.ini", std::make_pair("MK2SMM", "0.4") }, - { "Original Prusa i3 MK2 MM Single Mode 0.6 nozzle.ini", std::make_pair("MK2SMM", "0.6") }, - { "Original Prusa i3 MK2 MultiMaterial.ini", std::make_pair("MK2SMM", "0.4") }, - { "Original Prusa i3 MK2 MultiMaterial 0.6 nozzle.ini", std::make_pair("MK2SMM", "0.6") }, - { "Original Prusa i3 MK2 0.25 nozzle.ini", std::make_pair("MK2S", "0.25") }, - { "Original Prusa i3 MK2 0.6 nozzle.ini", std::make_pair("MK2S", "0.6") }, - { "Original Prusa i3 MK3.ini", std::make_pair("MK3", "0.4") }, + { "Original Prusa i3 MK2.ini", std::make_pair("MK2S", "0.4") }, + { "Original Prusa i3 MK2 MM Single Mode.ini", std::make_pair("MK2SMM", "0.4") }, + { "Original Prusa i3 MK2 MM Single Mode 0.6 nozzle.ini", std::make_pair("MK2SMM", "0.6") }, + { "Original Prusa i3 MK2 MultiMaterial.ini", std::make_pair("MK2SMM", "0.4") }, + { "Original Prusa i3 MK2 MultiMaterial 0.6 nozzle.ini", std::make_pair("MK2SMM", "0.6") }, + { "Original Prusa i3 MK2 0.25 nozzle.ini", std::make_pair("MK2S", "0.25") }, + { "Original Prusa i3 MK2 0.6 nozzle.ini", std::make_pair("MK2S", "0.6") }, + { "Original Prusa i3 MK3.ini", std::make_pair("MK3", "0.4") }, }}; +void ConfigWizard::priv::load_pages(bool custom_setup) +{ + const auto former_active = index->active_item(); + + index->clear(); + + index->add_page(page_welcome); + index->add_page(page_fff); + index->add_page(page_msla); + index->add_page(page_custom); + + if (custom_setup) { + index->add_page(page_firmware); + index->add_page(page_bed); + index->add_page(page_diams); + index->add_page(page_temps); + } + + index->add_page(page_update); + + index->go_to(former_active); // Will restore the active item/page if possible + + q->Layout(); +} + +bool ConfigWizard::priv::check_first_variant() const +{ + return run_reason == RR_DATA_EMPTY || run_reason == RR_DATA_LEGACY; +} + void ConfigWizard::priv::load_vendors() { - const auto vendor_dir = fs::path(Slic3r::data_dir()) / "vendor"; - const auto rsrc_vendor_dir = fs::path(resources_dir()) / "profiles"; + const auto vendor_dir = fs::path(Slic3r::data_dir()) / "vendor"; + const auto rsrc_vendor_dir = fs::path(resources_dir()) / "profiles"; - // Load vendors from the "vendors" directory in datadir + // Load vendors from the "vendors" directory in datadir for (auto &dir_entry : boost::filesystem::directory_iterator(vendor_dir)) - if (Slic3r::is_ini_file(dir_entry)) { - try { - auto vp = VendorProfile::from_ini(dir_entry.path()); - vendors[vp.id] = std::move(vp); - } - catch (const std::exception& e) { - BOOST_LOG_TRIVIAL(error) << boost::format("Error loading vendor bundle %1%: %2%") % dir_entry.path() % e.what(); - } - } + if (Slic3r::is_ini_file(dir_entry)) { + try { + auto vp = VendorProfile::from_ini(dir_entry.path()); + vendors[vp.id] = std::move(vp); + } + catch (const std::exception& e) { + BOOST_LOG_TRIVIAL(error) << boost::format("Error loading vendor bundle %1%: %2%") % dir_entry.path() % e.what(); + } + } - // Additionally load up vendors from the application resources directory, but only those not seen in the datadir + // Additionally load up vendors from the application resources directory, but only those not seen in the datadir for (auto &dir_entry : boost::filesystem::directory_iterator(rsrc_vendor_dir)) - if (Slic3r::is_ini_file(dir_entry)) { - const auto id = dir_entry.path().stem().string(); - if (vendors.find(id) == vendors.end()) { - try { - auto vp = VendorProfile::from_ini(dir_entry.path()); - vendors_rsrc[vp.id] = dir_entry.path().filename().string(); - vendors[vp.id] = std::move(vp); - } - catch (const std::exception& e) { - BOOST_LOG_TRIVIAL(error) << boost::format("Error loading vendor bundle %1%: %2%") % dir_entry.path() % e.what(); - } - } - } + if (Slic3r::is_ini_file(dir_entry)) { + const auto id = dir_entry.path().stem().string(); + if (vendors.find(id) == vendors.end()) { + try { + auto vp = VendorProfile::from_ini(dir_entry.path()); + vendors_rsrc[vp.id] = dir_entry.path().filename().string(); + vendors[vp.id] = std::move(vp); + } + catch (const std::exception& e) { + BOOST_LOG_TRIVIAL(error) << boost::format("Error loading vendor bundle %1%: %2%") % dir_entry.path() % e.what(); + } + } + } - // Load up the set of vendors / models / variants the user has had enabled up till now - const AppConfig *app_config = GUI::get_app_config(); - if (! app_config->legacy_datadir()) { - appconfig_vendors.set_vendors(*app_config); - } else { - // In case of legacy datadir, try to guess the preference based on the printer preset files that are present - const auto printer_dir = fs::path(Slic3r::data_dir()) / "printer"; - for (auto &dir_entry : boost::filesystem::directory_iterator(printer_dir)) - if (Slic3r::is_ini_file(dir_entry)) { - auto needle = legacy_preset_map.find(dir_entry.path().filename().string()); - if (needle == legacy_preset_map.end()) { continue; } + // Load up the set of vendors / models / variants the user has had enabled up till now + const AppConfig *app_config = GUI::get_app_config(); + if (! app_config->legacy_datadir()) { + appconfig_vendors.set_vendors(*app_config); + } else { + // In case of legacy datadir, try to guess the preference based on the printer preset files that are present + const auto printer_dir = fs::path(Slic3r::data_dir()) / "printer"; + for (auto &dir_entry : boost::filesystem::directory_iterator(printer_dir)) + if (Slic3r::is_ini_file(dir_entry)) { + auto needle = legacy_preset_map.find(dir_entry.path().filename().string()); + if (needle == legacy_preset_map.end()) { continue; } - const auto &model = needle->second.first; - const auto &variant = needle->second.second; - appconfig_vendors.set_variant("PrusaResearch", model, variant, true); - } - } -} - -void ConfigWizard::priv::index_refresh() -{ - index->load_items(page_welcome); + const auto &model = needle->second.first; + const auto &variant = needle->second.second; + appconfig_vendors.set_variant("PrusaResearch", model, variant, true); + } + } } void ConfigWizard::priv::add_page(ConfigWizardPage *page) { - hscroll_sizer->Add(page, 0, wxEXPAND); - - auto *extra_buttons = page->extra_buttons(); - if (extra_buttons != nullptr) { - btnsizer->Prepend(extra_buttons, 0); - } -} - -void ConfigWizard::priv::set_page(ConfigWizardPage *page) -{ - if (page == nullptr) { return; } - if (page_current != nullptr) { page_current->Hide(); } - page_current = page; - enable_next(true); - - page->on_page_set(); - index->load_items(page_welcome); - index->set_active(page); - page->Show(); - - btn_prev->Enable(page->page_prev() != nullptr); - btn_next->Show(page->page_next() != nullptr); - btn_finish->Show(page->page_next() == nullptr); - - layout_fit(); -} - -void ConfigWizard::priv::layout_fit() -{ - q->Layout(); - q->Fit(); + hscroll_sizer->Add(page, 0, wxEXPAND); } void ConfigWizard::priv::enable_next(bool enable) { - btn_next->Enable(enable); - btn_finish->Enable(enable); + btn_next->Enable(enable); + btn_finish->Enable(enable); } -void ConfigWizard::priv::on_other_vendors() +void ConfigWizard::priv::on_custom_setup(bool custom_wanted) { - page_welcome - ->chain(page_vendors) - ->chain(page_update); - set_page(page_vendors); -} - -void ConfigWizard::priv::on_custom_setup() -{ - page_welcome->chain(page_firmware); - page_temps->chain(page_update); - set_page(page_firmware); + load_pages(custom_wanted); } void ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater) { - const bool is_custom_setup = page_welcome->page_next() == page_firmware; + const auto enabled_vendors = appconfig_vendors.vendors(); - if (! is_custom_setup) { - const auto enabled_vendors = appconfig_vendors.vendors(); + // Install bundles from resources if needed: + std::vector install_bundles; + for (const auto &vendor_rsrc : vendors_rsrc) { + const auto vendor = enabled_vendors.find(vendor_rsrc.first); + if (vendor == enabled_vendors.end()) { continue; } - // Install bundles from resources if needed: - std::vector install_bundles; - for (const auto &vendor_rsrc : vendors_rsrc) { - const auto vendor = enabled_vendors.find(vendor_rsrc.first); - if (vendor == enabled_vendors.end()) { continue; } + size_t size_sum = 0; + for (const auto &model : vendor->second) { size_sum += model.second.size(); } + if (size_sum == 0) { continue; } - size_t size_sum = 0; - for (const auto &model : vendor->second) { size_sum += model.second.size(); } - if (size_sum == 0) { continue; } + // This vendor needs to be installed + install_bundles.emplace_back(vendor_rsrc.second); + } - // This vendor needs to be installed - install_bundles.emplace_back(vendor_rsrc.second); - } + // Decide whether to create snapshot based on run_reason and the reset profile checkbox + bool snapshot = true; + switch (run_reason) { + case ConfigWizard::RR_DATA_EMPTY: snapshot = false; break; + case ConfigWizard::RR_DATA_LEGACY: snapshot = true; break; + case ConfigWizard::RR_DATA_INCOMPAT: snapshot = false; break; // In this case snapshot is done by PresetUpdater with the appropriate reason + case ConfigWizard::RR_USER: snapshot = page_welcome->reset_user_profile(); break; + } + if (install_bundles.size() > 0) { + // Install bundles from resources. + updater->install_bundles_rsrc(std::move(install_bundles), snapshot); + } else { + BOOST_LOG_TRIVIAL(info) << "No bundles need to be installed from resources"; + } - // Decide whether to create snapshot based on run_reason and the reset profile checkbox - bool snapshot = true; - switch (run_reason) { - case ConfigWizard::RR_DATA_EMPTY: snapshot = false; break; - case ConfigWizard::RR_DATA_LEGACY: snapshot = true; break; - case ConfigWizard::RR_DATA_INCOMPAT: snapshot = false; break; // In this case snapshot is done by PresetUpdater with the appropriate reason - case ConfigWizard::RR_USER: snapshot = page_welcome->reset_user_profile(); break; - } - if (install_bundles.size() > 0) { - // Install bundles from resources. - updater->install_bundles_rsrc(std::move(install_bundles), snapshot); - } else { - BOOST_LOG_TRIVIAL(info) << "No bundles need to be installed from resources"; - } + if (page_welcome->reset_user_profile()) { + BOOST_LOG_TRIVIAL(info) << "Resetting user profiles..."; + preset_bundle->reset(true); + } - if (page_welcome->reset_user_profile()) { - BOOST_LOG_TRIVIAL(info) << "Resetting user profiles..."; - preset_bundle->reset(true); - } + app_config->set_vendors(appconfig_vendors); + app_config->set("version_check", page_update->version_check ? "1" : "0"); + app_config->set("preset_update", page_update->preset_update ? "1" : "0"); + app_config->reset_selections(); + preset_bundle->load_presets(*app_config); - app_config->set_vendors(appconfig_vendors); - app_config->set("version_check", page_update->version_check ? "1" : "0"); - app_config->set("preset_update", page_update->preset_update ? "1" : "0"); - app_config->reset_selections(); - preset_bundle->load_presets(*app_config); - } else { - for (ConfigWizardPage *page = page_firmware; page != nullptr; page = page->page_next()) { - page->apply_custom_config(*custom_config); - } - preset_bundle->load_config("My Settings", *custom_config); - } - // Update the selections from the compatibilty. - preset_bundle->export_selections(*app_config); + + if (page_custom->custom_wanted()) { + page_firmware->apply_custom_config(*custom_config); + page_bed->apply_custom_config(*custom_config); + page_diams->apply_custom_config(*custom_config); + page_temps->apply_custom_config(*custom_config); + + const std::string profile_name = page_custom->profile_name(); + preset_bundle->load_config(profile_name, *custom_config); + } + + // Update the selections from the compatibilty. + preset_bundle->export_selections(*app_config); } // Public -ConfigWizard::ConfigWizard(wxWindow *parent, RunReason reason) : - wxDialog(parent, wxID_ANY, _(name().ToStdString()), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER), - p(new priv(this)) +ConfigWizard::ConfigWizard(wxWindow *parent, RunReason reason) + : wxDialog(parent, wxID_ANY, _(name().ToStdString()), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) + , p(new priv(this)) { - p->run_reason = reason; + p->run_reason = reason; - p->load_vendors(); - p->custom_config.reset(DynamicPrintConfig::new_from_defaults_keys({ - "gcode_flavor", "bed_shape", "nozzle_diameter", "filament_diameter", "temperature", "bed_temperature", - })); + p->load_vendors(); + p->custom_config.reset(DynamicPrintConfig::new_from_defaults_keys({ + "gcode_flavor", "bed_shape", "nozzle_diameter", "filament_diameter", "temperature", "bed_temperature", + })); - p->index = new ConfigWizardIndex(this); + p->index = new ConfigWizardIndex(this); - auto *vsizer = new wxBoxSizer(wxVERTICAL); - auto *topsizer = new wxBoxSizer(wxHORIZONTAL); - auto *hline = new wxStaticLine(this); - p->btnsizer = new wxBoxSizer(wxHORIZONTAL); + auto *vsizer = new wxBoxSizer(wxVERTICAL); + auto *topsizer = new wxBoxSizer(wxHORIZONTAL); + auto *hline = new wxStaticLine(this); + p->btnsizer = new wxBoxSizer(wxHORIZONTAL); - // Initially we _do not_ SetScrollRate in order to figure out the overall width of the Wizard without scrolling. - // Later, we compare that to the size of the current screen and set minimum width based on that (see below). - p->hscroll = new wxScrolledWindow(this); - p->hscroll_sizer = new wxBoxSizer(wxHORIZONTAL); - p->hscroll->SetSizer(p->hscroll_sizer); + // Initially we _do not_ SetScrollRate in order to figure out the overall width of the Wizard without scrolling. + // Later, we compare that to the size of the current screen and set minimum width based on that (see below). + p->hscroll = new wxScrolledWindow(this); + p->hscroll_sizer = new wxBoxSizer(wxHORIZONTAL); + p->hscroll->SetSizer(p->hscroll_sizer); - topsizer->Add(p->index, 0, wxEXPAND); - topsizer->AddSpacer(INDEX_MARGIN); - topsizer->Add(p->hscroll, 1, wxEXPAND); + topsizer->Add(p->index, 0, wxEXPAND); + topsizer->AddSpacer(INDEX_MARGIN); + topsizer->Add(p->hscroll, 1, wxEXPAND); - p->btn_prev = new wxButton(this, wxID_ANY, _(L("< &Back"))); - p->btn_next = new wxButton(this, wxID_ANY, _(L("&Next >"))); - p->btn_finish = new wxButton(this, wxID_APPLY, _(L("&Finish"))); - p->btn_cancel = new wxButton(this, wxID_CANCEL); - p->btnsizer->AddStretchSpacer(); - p->btnsizer->Add(p->btn_prev, 0, wxLEFT, BTN_SPACING); - p->btnsizer->Add(p->btn_next, 0, wxLEFT, BTN_SPACING); - p->btnsizer->Add(p->btn_finish, 0, wxLEFT, BTN_SPACING); - p->btnsizer->Add(p->btn_cancel, 0, wxLEFT, BTN_SPACING); + auto *btn_sel_all = new wxButton(this, wxID_ANY, _(L("Select all printers"))); + p->btnsizer->Add(btn_sel_all); - p->add_page(p->page_welcome = new PageWelcome(this, reason == RR_DATA_EMPTY || reason == RR_DATA_LEGACY)); - p->add_page(p->page_update = new PageUpdate(this)); - p->add_page(p->page_vendors = new PageVendors(this)); - p->add_page(p->page_firmware = new PageFirmware(this)); - p->add_page(p->page_bed = new PageBedShape(this)); - p->add_page(p->page_diams = new PageDiameters(this)); - p->add_page(p->page_temps = new PageTemperatures(this)); - p->index_refresh(); + p->btn_prev = new wxButton(this, wxID_ANY, _(L("< &Back"))); + p->btn_next = new wxButton(this, wxID_ANY, _(L("&Next >"))); + p->btn_finish = new wxButton(this, wxID_APPLY, _(L("&Finish"))); + p->btn_cancel = new wxButton(this, wxID_CANCEL); + p->btnsizer->AddStretchSpacer(); + p->btnsizer->Add(p->btn_prev, 0, wxLEFT, BTN_SPACING); + p->btnsizer->Add(p->btn_next, 0, wxLEFT, BTN_SPACING); + p->btnsizer->Add(p->btn_finish, 0, wxLEFT, BTN_SPACING); + p->btnsizer->Add(p->btn_cancel, 0, wxLEFT, BTN_SPACING); - p->page_welcome->chain(p->page_update); - p->page_firmware - ->chain(p->page_bed) - ->chain(p->page_diams) - ->chain(p->page_temps); + const auto &vendors = p->vendors; + const auto vendor_prusa_it = vendors.find("PrusaResearch"); + wxCHECK_RET(vendor_prusa_it != vendors.cend(), "Vendor PrusaResearch not found"); + const VendorProfile &vendor_prusa = vendor_prusa_it->second; - vsizer->Add(topsizer, 1, wxEXPAND | wxALL, DIALOG_MARGIN); - vsizer->Add(hline, 0, wxEXPAND); - vsizer->Add(p->btnsizer, 0, wxEXPAND | wxALL, DIALOG_MARGIN); + p->add_page(p->page_welcome = new PageWelcome(this)); - p->set_page(p->page_welcome); - SetSizer(vsizer); - SetSizerAndFit(vsizer); + p->page_fff = new PagePrinters(this, _(L("Prusa FFF Technology Printers")), "Prusa FFF", vendor_prusa, 0, PagePrinters::T_FFF); + p->add_page(p->page_fff); - // We can now enable scrolling on hscroll - p->hscroll->SetScrollRate(30, 30); - // Compare current ("ideal") wizard size with the size of the current screen. - // If the screen is smaller, resize wizrad to match, which will enable scrollbars. - auto wizard_size = GetSize(); - unsigned width, height; - if (GUI::get_current_screen_size(this, width, height)) { - wizard_size.SetWidth(std::min(wizard_size.GetWidth(), (int)(width - 2 * DIALOG_MARGIN))); - wizard_size.SetHeight(std::min(wizard_size.GetHeight(), (int)(height - 2 * DIALOG_MARGIN))); - SetMinSize(wizard_size); - } - Fit(); + p->page_msla = new PagePrinters(this, _(L("Prusa MSLA Technology Printers")), "Prusa MSLA", vendor_prusa, 0, PagePrinters::T_SLA); + p->add_page(p->page_msla); - p->btn_prev->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &evt) { this->p->go_prev(); }); - p->btn_next->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &evt) { this->p->go_next(); }); - p->btn_finish->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &evt) { this->EndModal(wxID_OK); }); + p->add_page(p->page_custom = new PageCustom(this)); + p->add_page(p->page_update = new PageUpdate(this)); + p->add_page(p->page_vendors = new PageVendors(this)); + p->add_page(p->page_firmware = new PageFirmware(this)); + p->add_page(p->page_bed = new PageBedShape(this)); + p->add_page(p->page_diams = new PageDiameters(this)); + p->add_page(p->page_temps = new PageTemperatures(this)); + + p->load_pages(false); + + vsizer->Add(topsizer, 1, wxEXPAND | wxALL, DIALOG_MARGIN); + vsizer->Add(hline, 0, wxEXPAND); + vsizer->Add(p->btnsizer, 0, wxEXPAND | wxALL, DIALOG_MARGIN); + + SetSizer(vsizer); + SetSizerAndFit(vsizer); + + // We can now enable scrolling on hscroll + p->hscroll->SetScrollRate(30, 30); + + // XXX: set size right away on windows, create util function + Bind(wxEVT_CREATE, [this](wxWindowCreateEvent &event) { + CallAfter([this]() { + // Clamp the Wizard size based on screen dimensions + // Note: Using EVT_SHOW + CallAfter because any sooner than this + // on some Linux boxes wxDisplay::GetFromWindow() returns 0 no matter what. + + const auto idx = wxDisplay::GetFromWindow(this); + wxDisplay display(idx != wxNOT_FOUND ? idx : 0u); + + const auto disp_rect = display.GetClientArea(); + wxRect window_rect( + disp_rect.x + disp_rect.width / 20, + disp_rect.y + disp_rect.height / 20, + 9*disp_rect.width / 10, + 9*disp_rect.height / 10); + + SetSize(window_rect); + }); + }); + + p->btn_prev->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &) { this->p->index->go_prev(); }); + p->btn_next->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &) { this->p->index->go_next(); }); + p->btn_finish->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &) { this->EndModal(wxID_OK); }); + p->btn_finish->Hide(); + + btn_sel_all->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &) { + p->page_fff->select_all(true); + p->page_msla->select_all(true); + p->index->go_to(p->page_update); + }); + + p->index->Bind(EVT_INDEX_PAGE, [this](const wxCommandEvent &) { + const bool is_last = p->index->active_is_last(); + p->btn_next->Show(! is_last); + p->btn_finish->Show(is_last); + + Layout(); + }); } ConfigWizard::~ConfigWizard() {} bool ConfigWizard::run(PresetBundle *preset_bundle, const PresetUpdater *updater) { - BOOST_LOG_TRIVIAL(info) << "Running ConfigWizard, reason: " << p->run_reason; - if (ShowModal() == wxID_OK) { - auto *app_config = GUI::get_app_config(); - p->apply_config(app_config, preset_bundle, updater); - app_config->set_legacy_datadir(false); - BOOST_LOG_TRIVIAL(info) << "ConfigWizard applied"; - return true; - } else { - BOOST_LOG_TRIVIAL(info) << "ConfigWizard cancelled"; - return false; - } + BOOST_LOG_TRIVIAL(info) << "Running ConfigWizard, reason: " << p->run_reason; + if (ShowModal() == wxID_OK) { + auto *app_config = GUI::get_app_config(); + p->apply_config(app_config, preset_bundle, updater); + app_config->set_legacy_datadir(false); + BOOST_LOG_TRIVIAL(info) << "ConfigWizard applied"; + return true; + } else { + BOOST_LOG_TRIVIAL(info) << "ConfigWizard cancelled"; + return false; + } } const wxString& ConfigWizard::name(const bool from_menu/* = false*/) { - // A different naming convention is used for the Wizard on Windows vs. OSX & GTK. + // A different naming convention is used for the Wizard on Windows vs. OSX & GTK. #if WIN32 static const wxString config_wizard_name = L("Configuration Wizard"); static const wxString config_wizard_name_menu = L("Configuration &Wizard"); #else - static const wxString config_wizard_name = L("Configuration Assistant"); + static const wxString config_wizard_name = L("Configuration Assistant"); static const wxString config_wizard_name_menu = L("Configuration &Assistant"); #endif - return from_menu ? config_wizard_name_menu : config_wizard_name; + return from_menu ? config_wizard_name_menu : config_wizard_name; } } diff --git a/src/slic3r/GUI/ConfigWizard.hpp b/src/slic3r/GUI/ConfigWizard.hpp index c7fba76ee..c9ee05529 100644 --- a/src/slic3r/GUI/ConfigWizard.hpp +++ b/src/slic3r/GUI/ConfigWizard.hpp @@ -16,30 +16,30 @@ namespace GUI { class ConfigWizard: public wxDialog { public: - // Why is the Wizard run - enum RunReason { - RR_DATA_EMPTY, // No or empty datadir - RR_DATA_LEGACY, // Pre-updating datadir - RR_DATA_INCOMPAT, // Incompatible datadir - Slic3r downgrade situation - RR_USER, // User requested the Wizard from the menus - }; + // Why is the Wizard run + enum RunReason { + RR_DATA_EMPTY, // No or empty datadir + RR_DATA_LEGACY, // Pre-updating datadir + RR_DATA_INCOMPAT, // Incompatible datadir - Slic3r downgrade situation + RR_USER, // User requested the Wizard from the menus + }; - ConfigWizard(wxWindow *parent, RunReason run_reason); - ConfigWizard(ConfigWizard &&) = delete; - ConfigWizard(const ConfigWizard &) = delete; - ConfigWizard &operator=(ConfigWizard &&) = delete; - ConfigWizard &operator=(const ConfigWizard &) = delete; - ~ConfigWizard(); + ConfigWizard(wxWindow *parent, RunReason run_reason); + ConfigWizard(ConfigWizard &&) = delete; + ConfigWizard(const ConfigWizard &) = delete; + ConfigWizard &operator=(ConfigWizard &&) = delete; + ConfigWizard &operator=(const ConfigWizard &) = delete; + ~ConfigWizard(); - // Run the Wizard. Return whether it was completed. - bool run(PresetBundle *preset_bundle, const PresetUpdater *updater); + // Run the Wizard. Return whether it was completed. + bool run(PresetBundle *preset_bundle, const PresetUpdater *updater); - static const wxString& name(const bool from_menu = false); + static const wxString& name(const bool from_menu = false); private: - struct priv; - std::unique_ptr p; + struct priv; + std::unique_ptr p; - friend struct ConfigWizardPage; + friend struct ConfigWizardPage; }; diff --git a/src/slic3r/GUI/ConfigWizard_private.hpp b/src/slic3r/GUI/ConfigWizard_private.hpp index 2c8f23cd3..c31911429 100644 --- a/src/slic3r/GUI/ConfigWizard_private.hpp +++ b/src/slic3r/GUI/ConfigWizard_private.hpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -13,6 +14,7 @@ #include #include #include +#include #include "libslic3r/PrintConfig.hpp" #include "slic3r/Utils/PresetUpdater.hpp" @@ -26,211 +28,270 @@ namespace Slic3r { namespace GUI { enum { - WRAP_WIDTH = 500, - MODEL_MIN_WRAP = 150, + WRAP_WIDTH = 500, + MODEL_MIN_WRAP = 150, - DIALOG_MARGIN = 15, - INDEX_MARGIN = 40, - BTN_SPACING = 10, - INDENT_SPACING = 30, - VERTICAL_SPACING = 10, + DIALOG_MARGIN = 15, + INDEX_MARGIN = 40, + BTN_SPACING = 10, + INDENT_SPACING = 30, + VERTICAL_SPACING = 10, + + MAX_COLS = 4, + ROW_SPACING = 75, }; +typedef std::function ModelFilter; + struct PrinterPicker: wxPanel { - struct Checkbox : wxCheckBox - { - Checkbox(wxWindow *parent, const wxString &label, const std::string &model, const std::string &variant) : - wxCheckBox(parent, wxID_ANY, label), - model(model), - variant(variant) - {} + struct Checkbox : wxCheckBox + { + Checkbox(wxWindow *parent, const wxString &label, const std::string &model, const std::string &variant) : + wxCheckBox(parent, wxID_ANY, label), + model(model), + variant(variant) + {} - std::string model; - std::string variant; - }; + std::string model; + std::string variant; + }; - const std::string vendor_id; - std::vector cboxes; - unsigned variants_checked; + const std::string vendor_id; + std::vector cboxes; + std::vector cboxes_alt; - PrinterPicker(wxWindow *parent, const VendorProfile &vendor, const AppConfig &appconfig_vendors); + PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxString title, size_t max_cols, const AppConfig &appconfig_vendors, const ModelFilter &filter); + PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxString title, size_t max_cols, const AppConfig &appconfig_vendors); - void select_all(bool select); - void select_one(size_t i, bool select); - void on_checkbox(const Checkbox *cbox, bool checked); + void select_all(bool select); + void select_one(size_t i, bool select); + void on_checkbox(const Checkbox *cbox, bool checked); }; struct ConfigWizardPage: wxPanel { - ConfigWizard *parent; - const wxString shortname; - wxBoxSizer *content; + ConfigWizard *parent; + const wxString shortname; + wxBoxSizer *content; + const unsigned indent; - ConfigWizardPage(ConfigWizard *parent, wxString title, wxString shortname); + ConfigWizardPage(ConfigWizard *parent, wxString title, wxString shortname, unsigned indent = 0); + virtual ~ConfigWizardPage(); - virtual ~ConfigWizardPage(); + template + void append(T *thing, int proportion = 0, int flag = wxEXPAND|wxTOP|wxBOTTOM, int border = 10) + { + content->Add(thing, proportion, flag, border); + } - ConfigWizardPage* page_prev() const { return p_prev; } - ConfigWizardPage* page_next() const { return p_next; } - ConfigWizardPage* chain(ConfigWizardPage *page); + void append_text(wxString text); + void append_spacer(int space); - template - void append(T *thing, int proportion = 0, int flag = wxEXPAND|wxTOP|wxBOTTOM, int border = 10) - { - content->Add(thing, proportion, flag, border); - } + ConfigWizard::priv *wizard_p() const { return parent->p.get(); } - void append_text(wxString text); - void append_spacer(int space); - - ConfigWizard::priv *wizard_p() const { return parent->p.get(); } - - virtual bool Show(bool show = true); - virtual bool Hide() { return Show(false); } - virtual wxPanel* extra_buttons() { return nullptr; } - virtual void on_page_set() {} - virtual void apply_custom_config(DynamicPrintConfig &config) {} - - void enable_next(bool enable); -private: - ConfigWizardPage *p_prev; - ConfigWizardPage *p_next; + virtual bool Show(bool show = true); + virtual bool Hide() { return Show(false); } + virtual wxPanel* extra_buttons() { return nullptr; } // XXX + virtual void apply_custom_config(DynamicPrintConfig &config) {} }; struct PageWelcome: ConfigWizardPage { - PrinterPicker *printer_picker; - wxPanel *others_buttons; - wxCheckBox *cbox_reset; + wxCheckBox *cbox_reset; - PageWelcome(ConfigWizard *parent, bool check_first_variant); + PageWelcome(ConfigWizard *parent); - virtual wxPanel* extra_buttons() { return others_buttons; } - virtual void on_page_set(); + bool reset_user_profile() const { return cbox_reset != nullptr ? cbox_reset->GetValue() : false; } +}; + +struct PagePrinters: ConfigWizardPage +{ + enum Technology { + // Bitflag equivalent of PrinterTechnology + T_FFF = 0x1, + T_SLA = 0x2, + T_Any = ~0, + }; + + std::vector printer_pickers; + + PagePrinters(ConfigWizard *parent, wxString title, wxString shortname, const VendorProfile &vendor, unsigned indent, Technology technology); + + void select_all(bool select); +}; + +struct PageCustom: ConfigWizardPage +{ + PageCustom(ConfigWizard *parent); + + bool custom_wanted() const { return cb_custom->GetValue(); } + std::string profile_name() const { return into_u8(tc_profile_name->GetValue()); } + +private: + wxCheckBox *cb_custom; + wxTextCtrl *tc_profile_name; - bool reset_user_profile() const { return cbox_reset != nullptr ? cbox_reset->GetValue() : false; } - void on_variant_checked(); }; struct PageUpdate: ConfigWizardPage { - bool version_check; - bool preset_update; + bool version_check; + bool preset_update; - PageUpdate(ConfigWizard *parent); + PageUpdate(ConfigWizard *parent); }; struct PageVendors: ConfigWizardPage { - std::vector pickers; + std::vector pickers; - PageVendors(ConfigWizard *parent); + PageVendors(ConfigWizard *parent); - virtual void on_page_set(); - - void on_vendor_pick(size_t i); - void on_variant_checked(); + void on_vendor_pick(size_t i); }; struct PageFirmware: ConfigWizardPage { - const ConfigOptionDef &gcode_opt; - wxChoice *gcode_picker; + const ConfigOptionDef &gcode_opt; + wxChoice *gcode_picker; - PageFirmware(ConfigWizard *parent); - virtual void apply_custom_config(DynamicPrintConfig &config); + PageFirmware(ConfigWizard *parent); + virtual void apply_custom_config(DynamicPrintConfig &config); }; struct PageBedShape: ConfigWizardPage { - BedShapePanel *shape_panel; + BedShapePanel *shape_panel; - PageBedShape(ConfigWizard *parent); - virtual void apply_custom_config(DynamicPrintConfig &config); + PageBedShape(ConfigWizard *parent); + virtual void apply_custom_config(DynamicPrintConfig &config); }; struct PageDiameters: ConfigWizardPage { - wxSpinCtrlDouble *spin_nozzle; - wxSpinCtrlDouble *spin_filam; + wxSpinCtrlDouble *spin_nozzle; + wxSpinCtrlDouble *spin_filam; - PageDiameters(ConfigWizard *parent); - virtual void apply_custom_config(DynamicPrintConfig &config); + PageDiameters(ConfigWizard *parent); + virtual void apply_custom_config(DynamicPrintConfig &config); }; struct PageTemperatures: ConfigWizardPage { - wxSpinCtrlDouble *spin_extr; - wxSpinCtrlDouble *spin_bed; + wxSpinCtrlDouble *spin_extr; + wxSpinCtrlDouble *spin_bed; - PageTemperatures(ConfigWizard *parent); - virtual void apply_custom_config(DynamicPrintConfig &config); + PageTemperatures(ConfigWizard *parent); + virtual void apply_custom_config(DynamicPrintConfig &config); }; class ConfigWizardIndex: public wxPanel { public: - ConfigWizardIndex(wxWindow *parent); + ConfigWizardIndex(wxWindow *parent); - void load_items(ConfigWizardPage *firstpage); - void set_active(ConfigWizardPage *page); + void add_page(ConfigWizardPage *page); + void add_label(wxString label, unsigned indent = 0); + + size_t active_item() const { return item_active; } + ConfigWizardPage* active_page() const; + bool active_is_last() const { return item_active < items.size() && item_active == last_page; } + + void go_prev(); + void go_next(); + void go_to(size_t i); + void go_to(ConfigWizardPage *page); + + void clear(); + + // XXX + // void load_items(ConfigWizardPage *firstpage); + // void set_active(ConfigWizardPage *page); private: - const wxBitmap bg; - const wxBitmap bullet_black; - const wxBitmap bullet_blue; - const wxBitmap bullet_white; - int text_height; + // enum { + // // Units in em + // MARGIN = 1, + // SPACING = 1, + // }; - std::vector items; - std::vector::const_iterator item_active; + struct Item + { + wxString label; + unsigned indent; + ConfigWizardPage *page; // nullptr page => label-only item - void on_paint(wxPaintEvent &evt); + // bool operator==(const wxString &label) const { return this->label == label; } + bool operator==(ConfigWizardPage *page) const { return this->page == page; } + }; + + int em; + int em_h; + + const wxBitmap bg; + const wxBitmap bullet_black; + const wxBitmap bullet_blue; + const wxBitmap bullet_white; + + std::vector items; + // std::vector::const_iterator item_active; + size_t item_active; + ssize_t item_hover; + size_t last_page; + + int item_height() const { return std::max(bullet_black.GetSize().GetHeight(), em) + em; } + + void on_paint(wxPaintEvent &evt); + void on_mouse_move(wxMouseEvent &evt); }; +wxDEFINE_EVENT(EVT_INDEX_PAGE, wxCommandEvent); + struct ConfigWizard::priv { - ConfigWizard *q; - ConfigWizard::RunReason run_reason; - AppConfig appconfig_vendors; - std::unordered_map vendors; - std::unordered_map vendors_rsrc; - std::unique_ptr custom_config; + ConfigWizard *q; + ConfigWizard::RunReason run_reason; + AppConfig appconfig_vendors; + std::unordered_map vendors; + std::unordered_map vendors_rsrc; + std::unique_ptr custom_config; - wxScrolledWindow *hscroll = nullptr; - wxBoxSizer *hscroll_sizer = nullptr; - wxBoxSizer *btnsizer = nullptr; - ConfigWizardPage *page_current = nullptr; - ConfigWizardIndex *index = nullptr; - wxButton *btn_prev = nullptr; - wxButton *btn_next = nullptr; - wxButton *btn_finish = nullptr; - wxButton *btn_cancel = nullptr; + wxScrolledWindow *hscroll = nullptr; + wxBoxSizer *hscroll_sizer = nullptr; + wxBoxSizer *btnsizer = nullptr; + ConfigWizardPage *page_current = nullptr; + ConfigWizardIndex *index = nullptr; + wxButton *btn_prev = nullptr; + wxButton *btn_next = nullptr; + wxButton *btn_finish = nullptr; + wxButton *btn_cancel = nullptr; - PageWelcome *page_welcome = nullptr; - PageUpdate *page_update = nullptr; - PageVendors *page_vendors = nullptr; - PageFirmware *page_firmware = nullptr; - PageBedShape *page_bed = nullptr; - PageDiameters *page_diams = nullptr; - PageTemperatures *page_temps = nullptr; + PageWelcome *page_welcome = nullptr; + PagePrinters *page_fff = nullptr; + PagePrinters *page_msla = nullptr; + PageCustom *page_custom = nullptr; + PageUpdate *page_update = nullptr; + PageVendors *page_vendors = nullptr; // XXX: ? - priv(ConfigWizard *q) : q(q) {} + // Custom setup pages + PageFirmware *page_firmware = nullptr; + PageBedShape *page_bed = nullptr; + PageDiameters *page_diams = nullptr; + PageTemperatures *page_temps = nullptr; - void load_vendors(); - void add_page(ConfigWizardPage *page); - void index_refresh(); - void set_page(ConfigWizardPage *page); - void layout_fit(); - void go_prev() { if (page_current != nullptr) { set_page(page_current->page_prev()); } } - void go_next() { if (page_current != nullptr) { set_page(page_current->page_next()); } } - void enable_next(bool enable); + priv(ConfigWizard *q) : q(q) {} - void on_other_vendors(); - void on_custom_setup(); + void load_pages(bool custom_setup); - void apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater); + bool check_first_variant() const; + void load_vendors(); + void add_page(ConfigWizardPage *page); + void enable_next(bool enable); + + void on_custom_setup(bool custom_wanted); + + void apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater); }; diff --git a/src/slic3r/GUI/GUI.cpp b/src/slic3r/GUI/GUI.cpp index 8e80d64c3..a15f50032 100644 --- a/src/slic3r/GUI/GUI.cpp +++ b/src/slic3r/GUI/GUI.cpp @@ -355,21 +355,6 @@ boost::filesystem::path into_path(const wxString &str) return boost::filesystem::path(str.wx_str()); } -bool get_current_screen_size(wxWindow *window, unsigned &width, unsigned &height) -{ - const auto idx = wxDisplay::GetFromWindow(window); - if (idx == wxNOT_FOUND) { - return false; - } - - wxDisplay display(idx); - const auto disp_size = display.GetClientArea(); - width = disp_size.GetWidth(); - height = disp_size.GetHeight(); - - return true; -} - void about() { AboutDialog dlg; diff --git a/src/slic3r/GUI/GUI.hpp b/src/slic3r/GUI/GUI.hpp index f066c82a8..4074c2afc 100644 --- a/src/slic3r/GUI/GUI.hpp +++ b/src/slic3r/GUI/GUI.hpp @@ -71,9 +71,6 @@ wxString from_path(const boost::filesystem::path &path); // boost path from wxString boost::filesystem::path into_path(const wxString &str); -// Returns the dimensions of the screen on which the main frame is displayed -bool get_current_screen_size(wxWindow *window, unsigned &width, unsigned &height); - // Display an About dialog extern void about(); // Ask the destop to open the datadir using the default file explorer. diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 774e61024..f5471cdff 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -12,7 +12,6 @@ #include #include -#include #include #include #include diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp index ee182a01a..dedd902ef 100644 --- a/src/slic3r/GUI/Preset.cpp +++ b/src/slic3r/GUI/Preset.cpp @@ -11,6 +11,7 @@ #include #endif /* _MSC_VER */ +#include #include #include #include @@ -128,11 +129,14 @@ VendorProfile VendorProfile::from_ini(const ptree &tree, const boost::filesystem VendorProfile::PrinterModel model; model.id = section.first.substr(printer_model_key.size()); model.name = section.second.get("name", model.id); + auto technology_field = section.second.get("technology", "FFF"); if (! ConfigOptionEnum::from_string(technology_field, model.technology)) { BOOST_LOG_TRIVIAL(error) << boost::format("Vendor bundle: `%1%`: Invalid printer technology field: `%2%`") % id % technology_field; model.technology = ptFFF; } + + model.family = section.second.get("family", model.id); #if 0 // Remove SLA printers from the initial alpha. if (model.technology == ptSLA) @@ -157,6 +161,20 @@ VendorProfile VendorProfile::from_ini(const ptree &tree, const boost::filesystem return res; } +std::vector VendorProfile::families() const +{ + std::vector res; + unsigned num_familiies = 0; + + for (auto &model : models) { + if (!model.family.empty() && std::find(res.begin(), res.end(), model.family) == res.end()) { + res.push_back(model.family); + num_familiies++; + } + } + + return res; +} // Suffix to be added to a modified preset name in the combo box. static std::string g_suffix_modified = " (modified)"; diff --git a/src/slic3r/GUI/Preset.hpp b/src/slic3r/GUI/Preset.hpp index 73a921cf7..074e665c9 100644 --- a/src/slic3r/GUI/Preset.hpp +++ b/src/slic3r/GUI/Preset.hpp @@ -54,13 +54,16 @@ public: std::string id; std::string name; PrinterTechnology technology; + std::string family; std::vector variants; + PrinterVariant* variant(const std::string &name) { for (auto &v : this->variants) if (v.name == name) return &v; return nullptr; } + const PrinterVariant* variant(const std::string &name) const { return const_cast(this)->variant(name); } }; std::vector models; @@ -72,6 +75,7 @@ public: static VendorProfile from_ini(const boost::property_tree::ptree &tree, const boost::filesystem::path &path, bool load_all=true); size_t num_variants() const { size_t n = 0; for (auto &model : models) n += model.variants.size(); return n; } + std::vector families() const; bool operator< (const VendorProfile &rhs) const { return this->id < rhs.id; } bool operator==(const VendorProfile &rhs) const { return this->id == rhs.id; }