pax_global_header00006660000000000000000000000064151447664440014531gustar00rootroot0000000000000052 comment=4a3ff2b0382ec03d721cd926002ab59043ea0f33 showinfilemanager-1.1.6/000077500000000000000000000000001514476644400152405ustar00rootroot00000000000000showinfilemanager-1.1.6/.gitattributes000066400000000000000000000002731514476644400201350ustar00rootroot00000000000000# Set the default behavior, in case people don't have core.autocrlf set. * text=auto # Declare files that will always have LF line endings on checkout. *.py text eol=lf *.md text eol=lf showinfilemanager-1.1.6/.github/000077500000000000000000000000001514476644400166005ustar00rootroot00000000000000showinfilemanager-1.1.6/.github/documentviewer-gnome.png000066400000000000000000000556571514476644400234730ustar00rootroot00000000000000PNG  IHDR݄zTXtRaw profile type exifxڭ[9# ބ?IlZE8f?Zob*5-bwTog{s?7z> ?k~u낝wͅ|~1Å%hDzj =/Ӳv c?^?__ 1΄Λz't!>o^S" _ɾ>deį~K>^sw箸q:jُ i~N5!Oo}?jIʗv3]srt!F}IY 7?5)_B 0IoSc,޷MWrs~/~9Ѣ8q~s qUG`aLHY[p80^ " dfsı΅*M)p)(} !uo)| fQ!5-tr6J RL)TRM-r)\PbI%RjiPcM5R[S˭Z띛v9s@Ï0H#2hOgƙfeV3˯WYuշ۔Ҏ;ˮ~(N틬-ݴ Th1=O jܴ*asrkcv\rpm;~}Z8d.>3lqS>[sߞk89N"ebkI5c}혋kN{wcPMt q+vZu1∙S,vu<#ezEʝxv?Oj7n0>^l16eV {:)&j9}[*3(ްᚱ Ps$ #H+os 943ߟL?Ex(*HXHy=P:S칄$Ѻz Sk=2fœ&Ēa1Nt:3$-d3.$>4L9:澁}$tF'=R_X$6%G}0b۪w滪Ku7y"S sQ>}q7 @jHvc#Rbd{1oy>~AH!ąJFW@&JļL..imRXZ4D,{"db{fXTLhHI ڍYɇ Y䨥AƽA6"WƸ1#r,ThN V㺡h8n2j[3:%,ѧSJ:֠sgGFFkNT2pA=TdɖȢ\=-6cݬJ=zt1 jh.i2SQc&l4X]}ܞ bHDyM=dZ|CDƹ O]|6Ċə$4Q ݙ&3BHv3/cA.,ap40׬$3(YU2RBR"P2tkbxH1"xALEIS`s9&#}7`RP)N^fdhR "9yF9jRg6b*8(=>jG1'4|I/8EZ7K્-0Q%.#f78X]q499cLizRI1n{{K47bsKbը7T@JXqf!?&@VLu#f;>Dx Un7!oe$ٸ7oAn/I_j`*0Q&"sZZj\XrģV]]~IGpPIAYDnp10-qIPȚnL3O wxAM(N8O%)%2ؤGe qutW@I KkCғ \/.KnW!Aj:9uC.pʾM$kzi}9v]&DO[ ?6VC 21[R̋_Z ~L9v%Էi=Fn`y̤qIVVJ@585[#Q rtY 541H?Q#YrGKVx1B\j 7P9I@v$Qǹx; PLɪhZ C\mJg X OCvzI"y2"0 ! V%X$zr4f*EH{s1!W&78C<wAZ(69XL;Hˁo؎Z!{<^:,?!ʉ$cDƕB/KbZ WT'QJ 5ɥ PX ` =>h@l9/jK7O?{ň|FAT&d"0$=i K?J 4 6[R*HWtdT{#Uj 򅑖ݪfyWSq}}̴uPlhlO4Zk %:sJu?}:Ցx݁|΁͑ R[ŌW5T (,KzUv6F%==rd F ifuU٘ Eruԏ ]$wn֤7A+qUҀ jG r9EuVHχOr㾾ʠct%g\VWN_NMVKE@- +m &5r+S.ZEqy28}ivswE 4Dp # BǒrvO_G<]y߲)p髟 c۩L uVh9e-Ԉڇ›+zb¶xx/K u__aaxjTfe18"JH7k؁fal/K?^raZi)jYEkv0e2@pIL1 vTlihZ_JSHV~sh Zvzw 7p,rowHA0^;s!=\S*iG,Bj8~Ӹ۞(J4f1}&06:Fȃv`}81ۙm(; ^jöMyB` ٲt\[Je\Q?)bHK`#G /8TQ-B"&A̝zuV+zi큲5LCN̑a]M 5ib%1G\r M82j|YG-]jvkGc(XcW%{=nzvK89K1NEyqt \߮Ș/V ,P%FXL E8lcWNXVF'J)juk#NTԢ⾴!\'h[GD^8 w yli(b(;K&n=BP 촃`k%Cw!2@0]=Wh9//ØZ&s7K_߿٫@k<>30)sf`QY~H>ڣ ^n%w]H/ˣj)rLh vђ#SIngq G/gx ħ.N'o6ww,s,7 *]"췜LZPdMlޢQǁXꑘw5ޜe&fdÌX-ǻ6`M 1jvMpL1,p)i A@x 6T׻[+.K`Rg{CmKueijȪJ6qWkqܩ>A`ׂ JTÈ'ʝMt)SF-l+w_QpOEMr`}]$+_&ćL?K6i6 p`/ C^+6 UbwOIԡ`0DŽY,M2ıU0ż<+6.8mX|xh=G3iQ} PAXwZHN4eC7z p)o K&ߍ!մrg᫪o*xN:fG4w{NTKQAV!xrJSA1xS_H"Y Tg#^o~G{R;=Iil( @6oN1{Ra=>VsnFܧ4>LjaW0ZɞXxN?%B[v=;:) sCes Y C̠=+.='I0YHWp5=zyW㱙wc3&c3_of ߌsRl18;jjwU8/j iTXtXML:com.adobe.xmp $UiCCPICC profile(}=H@_SKTD ␡:Y-QP Vh/hbHR\ׂUg]\AIEJ_Shq?{ܽFiV鶙I%\~E "@\f1+Iix{zY}jbO$ai'6m>qexܤ ?r]qsόqX,uŬljq⨪/\V9oq֪5־'a//qRX"$PPCU؈Ѫb!CIp/K!W؀ZũI7)/1 wfqy+hӟ;Z.;\COl-OS(3<0x qd pp({ݡ=rƸ^AbKGD pHYs  tIME8[k IDATxwx?ujw*'Qc ۸$N\;ĎvTM/BBB !.!nH2BŦyx@hgݙμ7_IUUvʱ@ $m{PEQp\("DD  I,heY;\.ZZZ(--jNVl6t: EQŕߟDEK:RSS)--%** q!Np8(((`\veX,9@ЁJ֮]ˠA卢(8>f"Nbf@Qd˅ᠢH .ILLÁBU֙p`6EAxxxp8p:'f" AwӋc3@ 8B!g@ DD BD.P2v-Kp Ӕ&r/dcn#Nd>}?9;I'nAQs\N*sȩpH2@&$ٛ]Be'~C++Ũ1s22D*-PTL#EؕYHe-vb%1 @)f׫{5Sy j]:ax=]Tֽ)ohA6z2h"cܑz{aj2װzO1u.dL FJw{>-H:~DK ]F(ԥ0av7xS^" 0- .4SW@gc)d/#NJwd!w%_p7)ܯ-؆6F8(-*GK_ jtrdJπAc Uٻصz5ʔi$j}lZyh6p7!9k12dL?Lzh,Ncl^˸0=]L֑j0?dG!ix֘4֒!Yl5b]˚֯8q4q&w!"|ƶd`.N]RSAeK M'$-` 5>hK(vH qCӱʩtqb {`3'% nA%10W,hd q1Z]&nOyθˎ\jqIz4N].ō  D䒓i'Kh !"r@h11/T@21z2}ڗЙ =2is D''uD_ZXx-Edct2NdRߓTVc a招TC憵n7QӍ[xպoրA'#οĪ_?_tO ޚ&*k<"'# Ѯ#;;c}x(6Ғ:T?϶Dj %h>xʀjhRWAǰ>X²%H9*˩TmH":<'.ᅗ# ~?N$6K 7AEP!$D{*m-D T9܉ߚn`ŞȆ>٩!~q^,O]AVEj}K$$ěi;ǢJڦ0jm>%m1S)j̼x؛[7FzGPb&e=۵ \*ilK^D8 .7l'6֦ K#A/hDcٟK x6v]G㇒=&^hv@n`$4VΎf\w`4/c75u'>dž䉌`ߞd:4xRG^:iS=f"*๿g.jkp%o7^~9~ub#p5T{}W5wyP޸5-l_á 12֍porw y|o{"YLz@Ϙ?~\J߾ {& %9g}1!pT_IQ:EIN4戡ܗ~kE4?=@VK_]Ф$W9g SH&%9njrtS"#7K}^[D_l3Yjlډ|)aG2{o!瓱>VLWv+mmب0-m0$r孓NxT vC_J\ƾ>s8_=)ڼy+ҩWAH~2>JQ4D)\=kr֤:ɤ >~ʦ-hf$xۭncrճsu~=dS=wo,1w}?pO3Iw_>eOGػ;I7)tCTk>"jz [1w=D+mл%&j&|w|?ͫCљf.y2,cG#Ihb=#8:NȡdpFOjyO߽ b}yk^u-~E+v|=f\QB@Mo;/Ȫos)n@5O=z E[ D3nܸldݢ .nijD@ DD BD@ ""!"@@ \b"w(紞sVzS֑eWMH+i !rL ovfRYۄSc"bԵL9B' ^ɂfkA!{8(._"&hvqçys<[oϧ#$hL'Ow)ob0_'Aԧ<9Ig{Of&i8/2""腆qS H]j#e&dO g'aԣIKeSQQKۏQ=$hQkַ>=&B*aX C:v3_-#CD; wW[c13hhy$Ă7~wM`쥿˷&(T9d>tpѺycmoH- ǘACU1[311<@8DıEOxmޥ ~o%JS%~x1΀D_>zXb5ݘPӏ1}K53** (Jf,Lᄱ])||=1U|1&aO`/ 7J~&chϗ/]HGwtgT#N3juRQU 8}d7:(ػ>z] **uְ4i#1H1y#vMfP2v@JUUw7W@dTMwO^uχZӱ )'^q7Cd4PcMMM䐘3Fv(n<ɇ߸0jÂ6&^OQHlY T3:97Lo3k1 Ar%9$G͉&S5Kۿ_g-j6h0Dc!۾7lZzxo:b%,*72 +fAz`s`?i{q0 %H?(Ƞ+~^ex;)O3$  Eۡsp`*mK*]勾Jdd$& V+̫A︠ͫ. *K/ ̫!"?-y@p ޟBD@ DD !"@@ ""Ά[?@ D Jmagn=Ď ~*.brO_=-cg&s{OWA(\"pyZ0c~)@ D74fBnYlfjO)Zv)|Q+)m3=S8s#AKE [)?>9Ă#,@Y\ɔzN'NIB;d:㭏tyoCoJÃOzbde wHB>q-̝KMB:\>䁱?y@ 3g W  cVu3er8+W~cFV'1j>@ }䗃_sXUűyd5,)Yi*"IeIL6$!rbJ'_*uF5/]+X7zW̿e^) iPCձ1 &&RsX7Je FVy7ngb9øV#+h3S "cՁ2Yf201m})cOw3he'9vלUj5>~ ^W K&fk;}Kd&yDPm*cᒕlH£O/C*v`E~ጋſ#|G|<Ng2=|#H8XceO)@ˈp{.%>K4iNN[C±w0MW*@pFU2Yy9Z3Bh G@ DD \tȉ@@ 3@@ ""@ DD U+oYw""/.목mpLꪫmyw;R})dp Ek'&v\:HUi 46Pe-z7Ov<)ʦ>/n ^ \D%Z=5ncZW#UeE8\- Z fQڤ7?=Z\t$*Z X<=itኈ=?VMҔI $4"])JƮB*h;x(m؛_IM}Nɀ_q篥`7cƕIxU{I% xsB}q.M&c j7r(DEuaŕu4:(tUZ?c&L2ह&?!VnnŔaRq꼱uuL-:(5M9L1|R_ICmкycz[ ;m㪶AL3I! DYANОWL~J+YK9gnNTšFJ_)aSW7[2jcԀ,?G[gp._A%QF W)̜)['$|_C}b5~ZH۟Ih.xiE2wW#6ͨ [+l%LVO$J4u;ex{t{[H_"oĿ;L|$&[ )@M 4FbPQ;LA %tֲulPo 8DF‡ ?DAIn>م.&ؿuL[Ac?uyއpRSq%tZH z;{"MY(^ў>f[Oh66O:.~\hDG>~>hjKcHkcTtnZ$V Ӗ<2|%c"Ed~]Ԁ^(jOBG8o$/);29I[{'|E#o]NJoh2nϦ6~3"ݐ0h1R.[-d|UnNM6Llt f`;A*/8XceO)@ˈH !1TZyX0-zj6|:H -9OPv ޡ~ LX qL222-Y!Xi,O}̯e %32_pIi~r$}&agP/c&mH/m:WTJdd$& VTWWVVVzjn&q)={6&Lb`4/5-ңrNwy@ D7n@ؔH BDKHqq@ 8}q\""@,g@ DD !"@B9ʎ'KvwS*X5ߦT 'A!"MJa_a/\Y|qNsQ'];9p*glogF{Bv|SVj;ˮU"v~[9|<ç]3o+o;ySJ$@n=kCo*H5yw|QWXs8Hooh&^a4u]O^ɁZ!Q+/m}08(ʠΡylgb¹*:+~}Bmeb+H!"f+"Gȑt CG&sI!n1KvQrALGIYԵ$aLnf@GRgeKxifrw&eY-#t˟wE/iNgrGĐNZ^=ϯdÞzl #J}[#YȜS ŗ7?,K=|oC=Ԅ\=wk.eүoלMG>+ ]yoJ7LJy|8DnԶޱæ>̝c|SW̼g7bއ {{M=^7:Y:̀fPѭx3G8*CSƩI:lvcuwq]K,n߁޵I-9K$"JU1%2BA# S`F Cy2Av|mn}nbzk9{[21 o)g?7E8MQ(c+v~7߭."cb 2kinSJ )j=;ݿC7'2(anl{ YE(C19E=1,w+  w"-֔Bl  /@xcO>;YImq5q//{v}IqDΤ.x/eP "0y͔*. }ɟ~H6|:j:}zLzE2(`o.%P"zSgZKs;FrTKc۳zN^^)pwmu<~c"P>8)% r+qL sh;0~HTrGGa-a6FG~ p q;:OSJwn["JLEyUu { QNuZqZ{Jï_oYMK fj"vúشp=%Jr?y_)#`:{6jJ]xOS/O[ߗჽܲuΓ20P]J *ЇГ*%z-4w|%?%˯MB{h869D|YgbWv"]l^o1pL<_"y30 u+Y@]d攮P[$E(ߏ7h]SqKx|%c}6WPPϘQQ%W21ڛx4V :0b S'ʒFA&>9+,eexB(c C6aM]SIyO7A<3~|g04PlV=;f(NjALd略=a\,JN@:[pD$Dnzi|՛E FoCĕl7uiط?,>ڂ[@kMM<1fp!dLםJΧ-D/^ʺusٺ֌5'b K^ՀW|WL4 65݁hu ?fU9EkH ɝY,[EAB|$2+ߵ|v n5W[,l@љ2~ٽނ^txcǎ0Z=qKP^O_QK ]9!B!B!B!B!B!B!UJ5˫.B!INJVVV%B!LJJJ4`K^U_B!B3 :kB!B4&M])dB!"88-=PFEr-1 X,v;GtzF#?a,B) k x/ i5p^y`0UU)**BU7Áf#::76 !jJBZ!CC,=;80=/=.|Q,K\+!Y!TS; ^&uk78K{qvA( YЀ3 <fۃZm B!DBoI ^&uj7xC{q`|pب|ヨU׶f!"P$m k x/ }M<83_ gU SLL FN'~bPXXXBQp^=zo߾ p+Riӫ]טf-[ƕW^Illl}7G4\cǎy`jՊCW-t~TTTD.;;H|||J7Я_?>CvMΝ=nUU_YYY_~cǎL* _~;uZ,Z N> ڋ@Wk}V 8lg]5mΜ9wy\r%UnqFƎˉ'$ذa[o葒DRR;wfҥ(B߾}뱥gFTy]Ukβ2,X/BhxV\IBBG&<<ܽjn:V\Izzz=9ʕ+2d:tMff&K,뮫ӶJOxx8^{-_}_u͚5cذa$''eg>W櫤?􀂰Zz кuk.nOc֭Z.ބcPuWTVeU:jロ^z쩧b|!uy7ߐɨQ}p8a QnnI^9~)_̞={oCr@o65ˮKQZnYҬY:F/Y#G]( \sM,((ol6s-`0xWٱc}w܁f'dӦM\}tԉ{?K.'N˔)Sr 6̐ dƍ#|3f_Ț5k,5tPsY!C?t >3fSOQwQ6R[_Sgz%&&_ѿp;OرcGv3ł E]8m۶l*,K$$$PZZZ- jf+^zx#! C1`zRW`nu!t;UV5ʝ B$3g'Nfmذݻw3vؐvF… 8p ]vpV͡Cؾ}{E$ꃪ6uA W_1~xF#'NdÆ , f{M63[rzlYPPP)8t:N믩`^˖-c|sS㏓ܹsIOOgT&&&rԩHMM%333mJ +:u*kiR_B!P긺#@mrruA\x1#Fpk!iB`еkאhS%$$0n8ϟOQQ_~yH*[n%++!Cn `ZrqSp8ܟDzڵkgq\NcĈ?\w-Zx>@_,.g.'O<_#Gdƌ {aΜ9e[$pwf3O>$u5uב#Gxygk{sz"|:u"))7-1=bU]gSѻwoϟOΝ}j6ٴiSHN}+;1 ufu";"33p>tsuG6lX􄨷lΜ9//˜ؘƺ+V6l ??!Ct\.t:$v -qqq <_Һvt {ۼy>}:Wڶm[VZ'HHHnexٴiV`_M땓֭=P]9\偵?s0̒%K8s:uj<`u>[^j 0~\"8{|ǀ{@~ ---޽{3{lbccujUTUe޼yz|'*s 4G} p8,_bOpB.\ب;d2[o8X]tgϞ]_[rr2|o۶ ]lJ틢(|GՋ֭[@^^fӦMtЁpyuQY} 5 iLUCӪU+֭[%\ bu;f\}>s\AآE>|x~(W+XLL {ԧ?apfذaC@]BYxxxvtu7 u.Pk<EA9w׮]y;xhժ+Wdҥ]c^xNGΝٲe O?4[lƕyp4j(*zMFbb" 8?ի ##Ý߽{7~-͛7 /?x87ƍ7oׯwk׮oߞM6QPP]gfԩStؑja4yg?~<ٳݻ7p}q饗Ljj*?cks9pxb>~WwzmJ]Qԩ&L@EEEL8wuWӦMkP7@x{10.\ѣۙ;wn}{QUpYz5سgcƌ{!..νcΜ99rc2ydκ*㙨l߾&ojC0exmWCooJJ kK`u1=v !o -;\mh2:,B!L!B!`@(u !BjndNJm-w B5 F1Iwk(GHl܂BC~w! EQ&,,^t:ˆC% 6&h8TUl6w3BZY=EQ0L݌J'wF#:.$B›jB4H&(,,tbB!B*j\_bB@ߒ`-5L046io4_ re;mkmB!KݶP`h2WۤPM[u[ClBw.T5L04fiop4eaYYYVbX^XAӡ}CbB@5ste!^w mޔ1()*<6pj46 !jJҵju7 [&B!`B!BQG$B!B:"B!BE8(T+gM`뽷uQv]mHWCۦU٫k] ]+*ߪaݿ"B!B-sc}U.rcx?u<_]W=;\whS?FUAnuA灼ׁ|[KM!B4@> x| fs}gro<ӥ}ˬִbF#111 >BTb0f!)(ʃ0s5͈V5 =aw5,a |Dw3B.Q^mNQTgwDkmY._jA:upM8S!B4pC3:Twiq_\g.53F WUjHZ%cyębnp8j :^he! pxEzjy *e.v޳my= 3==+WQi[TUΪ$:6h „BgDgCMY QPUm㽝TU׫nyڳm" R/%]Y7!B4,U+s fCp:TU{n@^Մ t"4&yn!"v/YڵkKEQhݺ578vڝu|gyRP1 L8{Wx 5"|깅BPxR3 F NN܏;vHff&ogݶ*J9* ^`ZXU@:Ljλ[é)B!DZv;bp 4KfϺm> jyr]P*E]C]0f >_vwjZ^Wu B!hΦ_c0b3>6-CQڟ wʋiW?_!B!^N٫}~F <}l ?E84E9tÀ@Bp8Y#5)_ݶ| !B۟:uV}^}l~ ?$HU~5sIIj'WXP@]qEl3>fUY-YB!B` }y Gbr*\yp2v+W,Cr7G'<<>'w:hbbcޛ߮v%j,_uC-+t?7tcB!D(]9 pe_/bn<zs}(,,`ՃYwzY| zW <񧞥mZ;7O"**m[0())fӷ/,ojP^Q5ZLJr Dz*J||*(([na=SpBxwq[n'$99dd瞣C 4o>&M'|՝_~K/fOӽ{wf̘'Xf L6?~:-Kرs }^xB AزusGB!~ЫurdžDŽ;nCQ#r5Cd狗}C>}ٶ{-[VLzӏkr9q׍I'ѧ_|mڶo[= |O?=Mqـ<4>6l}\̚ IHh}7t !vnͽѦM[,^^w>:N~Ff/9 [Z9'ΫռX&OL &&#Fcѭ[7~g,XAZZÇ_e޼y<3ӇUVѯ_?F+裏0`\pw槟~r/Ӫ3grm/y}FTU^㩧{l0`'Om۶^gu^:;ѳElۺDSعk'ݻwe/{+j!Bԅ*&bT0W9 N70xȕ>v'&<<zW7^#QRZ+ )).旝;p8>6uFęB-#AСs9\Y2diiit:Ό3xꩧHLL$1 NGٷo_mO>aǎcF#YYY,X|\tE_mV{>l6[;B~ٺee2wգDž |f !BԦ* j :9:<。]un&A؄UCw"n6ߍC[Y ~[f~Í#g'1(cma4΀K2)KUvܝ:j}dddСCm!//͛{s8[k&sVwcǜc늊˘6mڵ#'9.E3F#ݻw';;; Ĩײx"v_ba~!#~ui>|+ϣ0BH T? 5+/Tt+ W_EEG*cK^F֭Y|;JHk缑[-q+/΢V MZNQ#(.*r?՛}{㢋{IUogִ`g, f{My68++s=c;8Vy`˖-c|sS㏓ܹsIOOg;+2b.:u* 8t:ᜟ0T ^yOB!DS{ VR.J*P0g"))KmRZ8N 7»o^}ݽneW0?ӏsiێ˗^}Z*i^t1|O5nϔ9x`?>KV|tރo}7fLXXE>}٨1`q޼yL>իW{tk۶-Vĉ$$$_11''ӭ[7zs$'; s][4b9sMvG]*[oO>/X?h }Q222HKKp|r>t:g:++;[oU+`u;G.]ٳ'k׮u_g𖜜Lzz:۶mpWg:1ٸa:tb\ w{f B&ulݶ+:7T*U`@yd匴TW6L)ʿpvATj{S1|sng֬Y̘1cҥKJ{fرL6'i&֭[̙3g1o72o<֯k׮oߞM6QPP]ذaNcǎU{3a3gGaرL<3U=^yB!S~~II̟଎sc|@IW 󭮬k2T$XXCT4;[hgLAϫSw G;T$TU\ !BZdP.uGXpG׍Z 즨W@((`pфht*8;& 瞧 OTY:QKrW5B!0**RkQd wVDԕg4F3;Vm1uzN!B=_79{o_bcB!D &Z3\9\W 7ڪ+fvVLuvAtP=u_U>%i)be_VU=I-LZ뱯׹B!eUR]yGf`LH 7|$ӌPq=v| ´^@)4a Zk[L}/B!DUe r-ԊS;6yw+ ,sMZyJLQ[ܱ;vpC֮ݺ_@l/Wj@(ܵB! &{@jztʡGW@b%;;\ر0*R ely׏f7>ƒ5UB!vUs}ܙȘ*6Hc<>N30_Ml\LJrTZsϣW,Yϙ]'-B!B_|TWЦ,QQL8 {ƯXPR\٧ϭeR2}^g -N\\<_|iM.=`K!BѴUVwc|WlT3***wllF#+3}-'gsǶ*/o!9!չC[F\u,x pUdgex߆eR!BFX7j{W*Z)HBP4k[S+ܱ=. 11٣ݪgt!))=.;Byo/{#<"{&M];K@,747_ǚ~B!vQ9,!LQqO)>:632OǛYNKT!p;k$N_Ѿ9hn@h`>ygdB!U`>]V2Uަ"PAsJ`D[g""v英bMt:5#W 7_ǹa`~-пgtj] nK>q^vc:ݽùkͬ^1):wwfAz$Rbx]^B!B.UXRh)ޫ4egUU; LAU*Mr*ށ̘iog3{x=eїٱ} m[0jP.b??΋/}cѳy|9 9yՊU?֮G33z>CoI0vM_&B!D)~|#^cʃ>X/wIzUuc ]{˹W߻T^;(SӫO_>b!%s։+..Ҳe;gwD y\x^r!B!DUU9z(O07oN֭+ P rTWBYLCcZڛ&\IyˡRe1 תn/`Xx!v{.0JK-,]dg)y:3z}ʓ>%?LxX8={f_g/"#*wlܰM~W>ζ*2+}9g>ûktMg 7!B!Grq2g*8`/rJEI\U2DMґk!a1kΡ'()>Ar$3K>PHBxZDѱM2eL~qyft%Qdl9F)+-:DǑ'xALbR,1cmnbRbMdRZdD9a4kf!JI՜7א$?9sw5fɉͱ|0 1D.}0K97Fc1PlEh֎#fQi%:*kD m&SWL!Ԏ-9T m0b6n :6bál暱*57ILLdfZ 6.=%fv젃Xbb9~SĴE(8,X-6L-UFiiX|&?7wQ) ᘰ =c 1U ' IDAT[u$[v%bbibaU Rif"t.:|j ayX8tl22e& ۷(&/\'D9Rhb)#/?GcZe"¨mDG(q2TTtz %%.&?-[`΂Ֆ+Fq~!9a:Z(*aale Na 3[XE6EbPT"TZ#\b :&p*F!FuI? rm䝴c2ţ*: 2b2#q =$*"BFoP1a)5X 9?MV(5C\RRh+ Jt\4H咗K\|3Ma$ơk@w2~Wm"LqaFA :@AQ>4oOjj )ɉt*ލ^aY1"h,'9uqDuM=v<3R4*gTQ= l8KԻ9ly,WpCPF6[aFvQ spXUu:Q(v-}0P\hAYcKI)qؕw 5BdLVK%fRRO0aee(pHSR&0QRTd$&Haq.)-(2b,,#?߂Ⰰr): $7"5oJv z=1IgV2z(;7?Hjی-9{QXOZVK0c/a 3Qj>͉ljo:=[&`-r[DT\,Ǐj-!**Q\DYY:ev;ZĐ_Kf1Ǖa \bȂ^o(zSdFShl6p(9I1͉O‘k##,~CfA/]Z(U)U[M0ҾGNS͎ČĂj"<„p"›'##FѬ 7oMvnqF8|b8JQP\X {eILÙ*+P @6l۬t Iz $YK7m&id*+Ouq hѦ38ʼn8GZ0-E^|L\d4l^qt( VWD;t]|t0,|rH-XoS[.;n;ۤ 9mQV)?:z~!1[駟g[\'`<L& Cvwf }=lKxy9g?a0(-yQ$)ښn@*pp{|; 'fFA=MҐ3\k|-?sJmq0 &ȡ²|dAؖ)%A0l}oe e]%uK^4mM(B),I(˂i(sFRJj74ҔçѽNж^Jٜt =jb48]'٬R~.//9~CN|{|Vqѵ=4hꖮmp=^7dyFUPk,/p,z{Cq}E&ʴ)nACڠ,m HEic*IʐBHHVfE&e ۖlk~s6-~7Xs憼(uL]3lqSd)=\<} 1>Wb3c)E K ,ÁFC$#Kkp@YV F5;ZKqI4a(9v][ٲ?! CorFӶ5Yabg# `3P~5QXHa M)m[ MmۘZ8#IJ6˄lC6,7KafT=圃 JKlKxia8ECNa\S38`=z/~v-%Mo[w=>;+ m0:2k!W<}/o!ywVHe'95"/}p[QV5 UG$vLՕCYmEUHV-0 hG*I ,ddۓW%-Qn@۵\_$iNM^| ⷪXƯhxf&=__=ߩ !m5]骖X-r2u|ڴ@6 =^SWvKg[؎OhŚhuI,KNNx&|Z5ì0ak֠o$P4L; ]7؎OoLoisl F4F(<8=~ƶ#mRu/Ҷl`«_{:io{CdIJ]v؞E]2HH)M|0:C軎h<؟<| C(ZT=vF, ʲA \Cwa(nq.7>'KTm7R\k&ш Vk Y}rkE0zh:{𔦮Y, M?b{/>{˶QI6N,B!]BwZ]iޤ,[%4V˘`ExU rziT4M1$sB'1V>ǧD"ˎh0^)˜ǏLh<ƲЃ)-szzBk|a 1U^S$5qa&144m"Z"moڌ=(ؖVd2B):T#)$%41-ޔ ܈|?c5a'}bq!]k0"qDUTixCBkgiC]4 Fq$mQX2{躣%ݤZ,{lK1mPSd9Vkf;@=3Yw;!]{CeMOuuEUFS^_C_Aߗm'_}]X<}[ᯕ%~z~qxmol?;zx+\DoP-6g^#PN! :"-B0ҥ%EJDO*<ߢ(K%ZBgQ6e^r ӉLi۞W_3tIA]5&zY.)˒LyiSC5ْ0q}mNЕ-˜UJIi5ۋM2q,$t[ʺa<Fh (Д50B*|0z\A)tgLAI#-AAUuIB׵2<kV5wws^|EUU_L&f;f}YEt<{O~>'Oq6MR5јh0<7wBb2q1}zt킻ņmNXV c[~@z4ql%,%KsLi2O \2)Ӝdc҈^" 4M:ݓ9&mwhhyzMz.^=hHf^ψ7)l˛kf]ѶŌ??Cɇ'@藷}{_ Pƕ~׽QYn"µm$&Wx]A"kꦢ; YoUI VKtBjwy=Eml-!H7VN7hco@ 4GH  b HNUeIǤ ijƗ_m _0c0Nc9E*8}a]ѵhDg] ]ﺌˆ,R͐ ǥm{#$aڱ(zCj /h:Lv0ap_临ui*#cʦbnYݮ\&4{b#]oh,|[lעj:a.EKQ(hЙ ٙJqxsvlv2[hp]gxU}ux8a4vp<0-y\"{z$ C!Gӧ COդi5͊I]ٸ_UI[ӼطC6LI--PJ6K)&|mS{ mK,ia=mWڮҖ|i$N_lW 6eڤe reino]MMEN\]2R5RX#.g4}Km} ׵88 ]FÈ@׵x2~خ,ئ+&8b혔*G+j 7h)۔gů1UIIhˊw)ۜ*1Z41h^ʌhAxpђ +q,A0q\ׯΑĒ sʊm+蚔 D3$KlWۖ {& G?7TYLPےlc><>;1$1C,c0y)mװGRIeo0mAZk&Va=€GTMjm twOdIm >;#7 S) igϟYC[m gs8% $I,f+$jZ]1\hjkۄ:f{ӳz9h1 /_:!9vmXΖt}r4O' avf9_|9ɔ,x5J 1~ o[wi4fN RaKd4dd[*z1mcZ{G4ewO>1MLKQWUY{hK֥x'O }R3pv0]8\~}Et|B %3ɗ7g /N{uC:wyK2b ǧ:elҖ5zKMppPt5B\]!*"F؁Outĵ,Nq8bl"_K_&8c슺iudz0m=T "/? 5v=@]Lw@qsl\rl>G! lOa-"yȆlbxQ "{_ E0\_ߠ[A65%),]vn@X"o+LSMbھc𔁠6AVhLXgLy%mY m9,Cjڎ5򛉷C3 $|&]%|=`t@t4em z`(E'{/( `=[Ya)=h9@7=mxWl;ls:EQ51*G7FMq#nGb l&d~8CҴŐgOQ]ߓd]=EM=vR>KȲaJ<lH lu/~*0a$IaH% CڶEIxA*IQ4- w<.UղZ#^zIBO4LwWn`ח 1}Q8˵;| f5+am+v0 (_`$tXIY*,+)ռR)w՗8לb75-;Zz-"? F뒥)s貘%f4]4^L'#֛ 2Nh k4^'o[uYJa`#6ʖi+GDch#C5f2<`'/b4y蘮QR1-y 躆4|3+qmNPيl5M[piToMSql60u0}^~Ʊ,$N{OTmFn0 3à;"O0eH-{SCjQ+:k.^⳯q1y1-R& mN`));Z]0_^R'8n>e^wjtq>)cJ% IDAT_$ׄIOE5Dqs_Gj*_"1?D#hk3MN}C5iVH[z'dEΧ}8??\ݬ wieeƗ/_  -WKjq|F9 p4zF ! Ï>fC0 iFWq'Q_?#{>]цCGye;h>wWk<5ktk9pHD(ч"s%3nN1Z eۖ;j<<ۤH3Tc麥"aS5Jh^M+UeC |>JjfE2:lcv0v(lf)4-Ĥ- "8FGxA`Hϟ>ᄪ/^o) ECCj5ƤAdqۖ@MɛOHݝ}-q\-!=b9g8.hro$ZhGKA^XvL˦+́I|IJlD`*CVHydo݌CJ M](=d-ȓdY0}}i(d2{j91ta GI^}itaX7lN[.ͯ t !)VX0Y6:Z)92Η*z䃐pzesyyxWx!L&C:}C4,2<M!gۖ&ݐt֐U-&t a1٥*74]- cXݗ ø7[h %$yv kI!doo) >u|Czl"%3˵K.`9CUʓoiQ & #s4ɓCvt+U&nDaHl;+}DH4I9yYxʐ607whV萖}IۑdW4M߶5%;uئÎ'_}cDBi:!ATPFOi`@k{l>g _e$ZC[([ݬ) 'cMZ-[dozНAUvbue~0>L!,O聢QڦzY4MEUtͩ\^];>d8ң%M0(x8 ٚc1wkLbﲼ4i1ؙNWlkZ\\UReu{G<9bي7W\|oR}uv^ҵ-`vN&o[w^|{?W=mm_$5et]xnȧ?RmcUYn 7YF8E=<e0ݟ .yAFC,aw0QeXKqBw?TXJ0azmC]tr`?LjC=C?ncVYmxrW_ I$k = clP)7)] 2 4=Fa0A 'o?fN2vSMYtfO x7嫯?')mwc{x8Ĵl,Sݗ  J)[Mtmc;XcqrwgחKFeҷ=qlMNΣgǏwKSL&!c7b6qh0$ʶp}" љ[ML<(#b8ؖ hBz&ǐ qo?NvOFSptSk؎thʲSbg2<999zLe;A):[Ke;K뻔"_dj"5M[>-ے |)A=L)': dV*a>zz}q߯3XN? tXn/PV`lC]HaeGI1Ir[6Co}fVMwmAtۊh&4>c3Lz4@. xx5=`;Q*|L &=:'4,K`vfC,$Oskmk{}YyZ H|F|ݟ2n˂]QR2B6-?4嗯[m9:aw0ZƤɖOlxsvv0C"X-Y{?6jn E^siMG -:i0؎6zӳ1xES7CR%{X2%m"$/ p_ZkE($Tm ھ"R,I5{ÐEE4-s!_=[@9&7Ȧ =rM1]/#vy\]ϐzjA9y^G ݐ7?E6nf>prS("͎C_8}M^nE䍨WŖYyOw1 B*'MI|#>p{>chvb"э{2:W.n5@h N4P%6Gr펢(!E1CJIUYF4 B-e!( C |:Iq*>6p}4/!J *Oc`q}}qϲN8;; MA 5]8lio[w:3(~o'7>:dNcF;c^Bi5wEhj 0ʖeԢDY&id(;%O*B-n( ۗT]&rрI3xMUCVgܞKʲ@)m8f,p||[z\>_mrw7c44PbYsu}nK wYl9fKB?O>a\3sh ^g_`t65~0OOfYMN4:ja (p4 mQ7ls:٠|*L_hQ`8hƉс7:ltE,3(-Yljul+zr^O3LwǬ3.Rz-ǂpO092:aBKۖ;?O>K{ NN'MW4ϰim;PN"?90b/] ݽӼzyaYRT46iC :]!$"5,Fѐ6iˆt |c9KMgn8P)OyXPS4|BmN`%mr|?W?d2PJcY&S5F))ՙteCd @Jldq3q0MlS-1mNh0z2CTy 2u[cu` u錎zNx>sEqo2hsN>z>_4{Byǁk)a8l;(`[7ԍK'R~un`9Ov6-QX4e:QHQt`hg!?qzў!PG(A9%QĚ&(-'!wKںIk,b8Вl[ME[o[wq e`R+/>fږ.mfkyl[5U4%w_ )L]vhwh↶Π9|$7w\s|8Ŷ֛ZT˟}Δ|G?/͟l7g8KMp0irvv+?m:G{Y)$`5Jgs6˄^rr7lS<]N:g "(@J:C& g l']6hw e+|&_C>6Sox69Z!7kEc$ A[TT=HWbDx:b|dLP[$cx| %=MҶp#>][t <.-A(Kdv3/y<]vfҶo ]bCSecdK[B&C.NnDIO8ݑp,>ۖ@`$ g`vuz|{nsFSfLeMcK}}SSn2=if2cb @ HN!Ʉ(X##Gx=^ٷ|xƌa8~nߗTv羮e#2BV]58mMԂӇE/ Y,VUĤk;".,+,b4nijR`ǡ5q@*~еW$0pIi04e?işP+fFv:JCui]Gj`&Պ>S!IxhܧRF!ERUyaIu82s>e?$ ȒRt[NGDQ0hP'-{fi3}xG'0G3MncQI7LF3TSc9I7ЌuUS51iQ)7lAa9_ ҸmWs^2h"lxtFP,z思.uE$,/K}#.O(`Z!vAh(IVeOZ ;dQp-Z2=..YWA9` siD"I9e1Ch`Y[4me6aSx֫-EQrր⊶999{(>~p6<..hax>vj0lnyITx&eW4YRoѴ-*0MeY\-Y_=/)jYnqbZ#$smDN%uSa9UqL;,ƴ[z= ʼkj2H˵ MMilzaK6C@oԼ>;;SϱWXATO''M2nH5m7Z!g?:]X׌Cd{czANWu3q-\U$Uu^a_DS,N 'ٞ{c"ZT&e(h2}>yp2SmFۄF4)M@ q<2pke!װ Y3`HJUnf8\cī5pV ٦kʲD:.qk`ؒvC+<̎#`Mv#N U#}ɢZw4E)$7̸DVYah469n&&C ]KUSUņݽ1!M%倩{uɲ&ܾqWí[G8ޮϣG|zx)ҒVtzK8H909}|`)C"g7є5MZ: &Z~ g.%-PFG# ܶ uKZ9mqD.p\xR79Pلk(  b8ِsaضx8c]L=tQT70SHOOZT2M!,Sh4RHF[#3-m\=eIlHWv\>?][g4J?SـV 0 Bs?)$h4vmïҶUӒĹxGhl0p B,l2c -w_K4x>uG'ԥ ) l/΋MlNj%;i$! Is>X.iJ?X-p|/| o}/ec͏W~tOK_6=;sg~Gosύ9;˨siH6-Jr):[Q6 =  i}/kxa;,6O焥-%iTᘨ$mQ֚m0͎vt[q !O*,K֋%àjdowÛGy*?i"+$)VuCN{>;CTՒSF=F vߧ QZ>͒q'zcB7)1+Zu!-r"Mf32d<6۟Gŭ[7O {] b$]bJ#.f% ٮS]cH|Yrѹ7T<*/ql LˊGC79yt+,&Y#p~16-b[>WKl0pV?ƍۜ^pyyFطq,pXGLF}>'-SA6ŏ$cGC}rA]Ws^}U )l@VHWst({|3?ϋ/@uR,Vt%ݴFM7`Xl?أls\G .Njn>g1OPzC.xIJ]&y^G]4yB$p{3g|>'w7~Gq[8h>78# Md HjI&Oq#IAq0;EMvh8cIpr9рpi >MS^qsOZKQ)&,YfyI@1#Ǭ5a:>'x/JGSF4 a?#`v!΢ {CrmuBte|=r$J`)wrl?1<-:EEV2 B&( ׎Pk&ɟ|/|& ?oǯ)lG/(ے+-ٟ!+?ap?,c\)G=ꪤK,hd.tUp֝*'/3\d: h]pSYqN.8;xv ]nuMԈNở }d]b6ǷodyI|G|&MW(:Stŷ%<:Nlt~M,:(+ He)քaȑR)1lHKr~%5]呶+i˖a״uKtdeId ki\-4I ats|[sY\]lMܸr9)zF~Y <Ph&ɺ|!DXme-#jK4(ױi EiEeجktA0o}?ccg£%6QT508?`{ŗ/\XgICYvhѵ8?<%3mkrx6nin -Sf? %`Ŗ^}?=nUEk,i7Uu?:ilSlJf{Y'[4xDv*^Y9K/y^/~ UX S p]- gh± ,C ]e]щ8ڻA h,9WX-I| ߓ}^4nБUc:`X̉LSD,Vp@ZxEV:G;o?i?y.)9mz3d+EZ-X$qhwGGd1__*?HyWwOҵwk>ܓVs~ǍhښL$[s{YB~i<-)TbBN]GdNDJjR0, [I%\iʖ "Yn{=l{.ՒFL]i|rP R\\ܟ |s_RmW>6FzW 7{tt IxWقso<ʒ7o3?kErŒK?, /xo+WMJGeɤ'fC 槅߷W4ɶ-B4yU² !l2,{C0 C?" AtL A˟^\^CHE#䘓%mYҏ"D$#ly 0tFcvGؔurӳ.͜eVЖ JQf9+lmh&^nZTɫ,99&;Oz}Y@k0(yP7jH*Aa ͊ℝ>31)⒠ob j}<cHYԭ&v{\y^U+vCDWR5>hDѶ$*l4tvNrAӶؖK !먳rEuݰ.2@oR'.jLK$%ۄ~bx LsPYF۶eAvBӲhjPu[9UY Q{>EU`Z6] u ۶AqOԙtu-OL34$-TeNן3TeIH |C(4Lt1 U4u~ϱ.SϼXQʒMZjSS5óC[ N+6Y,sqm4$ga/ܺCQfM4\'=vmA9[!A^TYMg* %MPbC_CbQ4uE^ք0Ƃft:0L|0$]1MAA.C萋S|ˡmj\Ǎ/Iobf,{&Ͽ@MjLӴخ3F'-SaՊ~?`X! M&̯DQH44MZ'C #m C`h]S ?16ݣlST ހBP9s6{{-ѨOk/ =x#ŽytrNVe:dInXZ1xx?cw|&i0ճA I6$ıGYp}yk6Bo?2|L+hf<޸!x$)6F^Ro~wuo>%m5J? k'`pki+J#0*+(-L%ՠء /*Lק,R,a;mTM'߷ȳʄӫ+Bm&r8")s6.h'+zoa*Rm`} p|Í-h%&]64 F'ӂL6lL<ߧ(K @1tH$$H a&}mRչpe)p%j\&S2c0ؤK"zYL^4hբ MULGS IDATc.ԆkR讣mZL ZrEZL+DŚWdREӋ|$6 V} 99Cڪ`EeUtT,CkZuVmXR@^Wjq<ښ*pLVXҤ+o-ֶ,?yu{9NvuW6c<@<H!F<ˆD2HiR:unۺ} ElbE 19+ZҚ}ES1{3 qQ5LҚ؋Y15f-PӪtx& a"u-hےmF5yozBgzϧQREIFߎx^o9Ob@W}٣I|y52黖b{4eUфmߦЍ?(Ld% dMnmc{7W7nHthN8j0ziƹTt+vo+iCUX 8>OIMòb+~w=&6kO5⼧DvpXDMeqtvcl۠z>|. i|? u^STd87^;-¢_X+KAϾ1`~Ro:%TA? /~Y}S25ҙ`+~k??пmM'#jZ78JuS-EѣPCOlw 0$o*ʪɲ8S-iQW%2 W9oQ.sg`x{c"tǽ#~w.hGo>±lɘxk5eZ.?{aHKdz.#&#['$ev{0LIyDdgO>^ m9یɁg])M>ʰ7=xY{m ߢiz CG= T؆at]"2e:D5h2ɲa2 l}o6 Xnl~@Ww! -ہL񃘳= AH6[ ۩zJ9Gk2#kN^;ubSBMO~3tϤ vp^I ˱$ۃ,ͱϥSsRف_sƇ۳WU կ?0/w7(i+{_7IwJzGǒV_O!+Nwk;Ggn :%Sɿ:G&U|A,폏DKz*GɥnuCH%I!z?>Y9H?y&lXmk˶qm) C]htu4c,;,a<$*D{(!nm2{y1Ͷpz ,rX.d²s|~-Bbi bl]h} 34iL'<ߦ:x>tuo8I4Ga ǰu5U -) $m[8cr[c;]?%i0 L`$]ȚF8_=|rC1-SH5VF34%]It!ɓ{=5Q'PÀm|OYUFzhieB=`6R=?3ґe<8Gg773dw7je~rl<` ! ȉx|lˊ3殠ZAadͨUsV ca umٺ];Dλ_!i趉; QTyMUhMm\\^bK 3,!+2Q@tB IYל;!JF;~"K'ߥz~> #\zЅMDjbnYn^ܯl8?|?6q҉EG3E!iƘURrrr꒡X'8ddCLzd!mkp m]XȩʞZtpoc.3*ttM?ٓOyx!LʼbU΀o[__}ÿ5n>#;^(_w_BKEkg4/kGW{o/u7/s:e˭o?4~M?=~w  /BבLd8Ge%m_gdX6dyNY4zGhJP-R*hihetHNKp]!8`f?:4mo-'\u KZ (U5 ,xuW`&J ?JcfY؆@a 5J|'ߢcP {s#t%hDUSُɞ0: 8&s\u<,bvZإ;lǦw#Ȋ4(MXCЙB@) B-h#.O^ܯx hK.^lx:cq# "Ƴ)/6߶plC3qu]$й>/2(m,xGKSUUݎw'x9xEAY.s!5o=⃷b=s|&j_ vZg޼K,ߠ>FM_S65Z#CB瘎۬; xD5XV Pd0]0ltv&;Nvh$\rN\/Eԝ1ý=<_r|c)a}>dKwxeZׂгhˎVzo1U3yfӓ9~2(hUKyPw,7Kl"i3С{@7ueIL[&c},kVYmNOHҜKjD# &#݆&h $ #(5ٮ]:@/S8 }F~}uv`6{ѕ`S\S,xV36[V$iy{s 2+t "+BƠxY3hʆL Џ؛b:W BlQ6ES6i!65 $ NYDKL iON5 ,lYe+m0xcXIB) i([.n 5 %[ml6#8NL^#Ani:C@3 g|f}b4AdHy;\vqC{u[푬7Te0,P@6-m ;.z3?>@!Xo6FcME!Q%Nzm^&]]4اn6upl _967y4vW=<4uMG҂m˳Y/mB CI}BIY(X;h-RxE) .{ xojhpaߙnw$S x-0 /ڮF+zdW: )*ěR7b%[0u,lӤ V[eL^p1BA l4Ch4µM6Qsri]3duiA۴U%_mOCEg1IGO.Q[)~YԃɌӳtfde+ OWHc|0xf趇;rPYOfYmކ#Gc g'|1/xcHϙGq?Ej9tOeM`\B0,7Xw -?w;^SFjI!-za'oIS\g3z>e]Qe93l@cJa:u^` pMr.w}Oqo\-B^5D8X6FbY6r \|xMHG(3ʠ+.`xz M ޗYw%)im nv&5&M"d8QMQ5MetF z/{i`IS3]а^t6a~zxIF8 /*,'%ng } L(ڌ$dPN`պh*^>yY\\]\98MK6v)뵤:veQ0ŨAX=p PQ;!A؊F՘8I2+u-iZ0?q|v4eB6޾OrȒ рw8vWr2ORcψF16uBW k!fCWT, a T-I٥ .;r{niw1w}*[D>@=M,sCnk>::>gD;}4AX"Nw0|ynUI׵ј8NG='/sx#vu?3e85v~%ͺ ';fzŦA:KH;a};R0Kx54W >g4}ɣG >2COjT UE[+taQd\-8MOծfXcM f3Za Ћ۱#M+]7`&PQ}Fi\`fPi!ݏ0 404Ӄ^\?pM<&aa-aN7% PC|ЄIwv;͒|;?qXח)4M|0ȡkv!b`pkZS踖I%{OSZAw Ua;e3|!_X }6kd8{ DӜ>˫sn^dŔiOthaDcق4`,Y]ՊF$՚no0t !40(P(芖:o8>y@x8˧?|.0`.-V㢔"?q1t '4i;0qKg}w+RXg ĉl&vL:ryd~-6؃ &ke#1gV*$^v$h`f ]J['8fb:<@ A0 yk?{tDZkylzAe[5Zc'Ccv6ېr꺦+c.?"GI^l[44/Y75\8c:%c4S hjor`XFGxΌvڞQ0B6 CQOϰ>;m+a&t=ϟ?atsJV8ɋ gaG:a1ptzN 麞3?w9?:Ο=%}矽@,hGcf== !%l1#4!(u"\`>i^mSnWur}MXd X=]9ޛ =>NN)A vEp|ȃ7XׯZׂT8ؾEuXœӷ$ӱ0= hC"ːfPf( EA+$`{z^_ݬË$`ذ71yr}hBY^)$D!z<`2v%r=&# !ҧZׂ"el6K1p2 IDATx5ahDa>Iqӂd"{exUJj<'/2tɊn8UuxMQf`tLfVӷQwnE`v|~-l(ͤ$)iĊmQ(Da8MGS{e&T-mv=Zb Y%{!:-lrb69x2kAQU`&36u sI:dU 8&\ܼs`[zlm_m !1tH.C!]@~Qt&M!\{H% }>#I!0fٷ`kfШj"Zi%`2sx4fcl..~CDR9E Yۆ(lt6wqJtXSg!ok"oNUt8 5LU獣 i\\1je\\Z0mkh/L[< {oQ 4 薆cel+~mF˗\(V7hFHљDß|Ȩj*8#zrDpw5הe#v "LF Jb CL'S|2EYxv0nC,b\EI@CO ίa}|f{C8tAd|1R2h4bU'ɏ>ⶏ*[o8;%5c60/R0 `|#^?EbkŌB 4tTQ-O.FT522GeM' ,0U]Qf%BlzIa@^H~"iU11#v r%0F>rH':FЕ6Z3xZׂ,tGJHx nO^֌dm J)0M(. PA\_n6:R-#/S$nP ca&q, :<||],WG.ٰf[Pi ZO^$ņ$ߡt&Yj_ Bn0?=dJlk4j0j G#!dC { , ti ,M cmJSe[{ M7;tcfx.0Mi B@*z)(7 gT2Zw9VR*4g%~- ,5foEo>|C_v4y-ڦfB MkNےr,f"vN8=O*Q6Op':&cz1 Uŧ7Bؘxa7d 9ޮyxrFI˂`bnp:s:Cf9wZaJ?W-Ow;^Sz$c2ٮV8xhI_tخE`{H :ZI5 2 |eX^.Ӻuq]8@gh }.e?>aHQ>L*!+Dk5pQ I+< ʡcO76]+,+f)'xTOlEG]EEՒ0p = )gܑf@߶Exaly1({(!(\L%-naHT`c&KxɧLfoT8aHRAшq0|4 z @7TLeQ%݂z˷l<8t%a1=J՗ ba!X/r4FIjGSn6k& 3ٳhMiX88}@U5lY ]<e 8j]4L&/w8!o*\E=M[ޫ=MK9rS`CƞMzg螐4qyB9c 6 \+&ߴC@?TL}uG4 pO4t }ۢB̦>j;W?;#2&}n. l}E@94B7 * W9ط|8&~PlnּMA bE1rV'P@N\FV40Y^ IfͮX1?:d<ݡsB ;1-]V WK:9n092XQxL;x xH 󨊄svkѫ';v)8@ FۨrI]DazxHZG14]cئAeiFc( ڶ% C4ea[:аA6\^&=ecy݇_MQ󧴢b`h,;#&LbFy3Tnp.5 @i50-j( - k<{qbuSs쩎f)S3hýpt7x:vIJWOY  dnG ˤ; än*Msv]:%'Hk,+p28\ $r0="˨˻S5YDDz]8^٬V e_F6d:3dho8쒄AooGp@ʞ]u#vKiM0P+EZ l ,W#ʤ%LgSvE-4]\^C5eL0IUZI]wMU˞Goxc仌ȍ؛ U阝%Ì\4%EW8>5M@W-6J ؖ޻O7t\].qy.q(hO>qL|? ,ܩoDui:&#/ LӢkںGUZ^ @R *&٢4wyVԟiusp>b7D.c)8=E_l5!qQ0&sʠik"GV$Ywgi@QiSns6%5$kFTUEQ]mF%]uk>=?3`wrhQ?jm0jz$iZbi&u9''tg<!8JqߦIRq)˒ϧb,o,薆ˇ~kaGуbazcjjTMxOmSeRUԇDLUL=ix'Yls4,iRktAIUhPM)(JB`}4]i,"F0ݛ@J&}˶(-A 蔖4 L4`R0 SW-Ƿy oZ#>y}Xㄳyј\VD$戮`h9C񒇯5i}͠z#6 V ]C[%b6;#9ʩBմ]MtW- MdzFTV.ahbS og}ƃ[佊Xr;*xf+PUxTEh EcTuY&MU{ł2/(Ys9R%~Nv:4muSC(Yz}Pjf7iOVT\ϯxv|ºF2*WX\_)WT֫q 3*hێ#rI/rq~ASeF:hɇ-)^NhqbXR N$Qĺ,liQW 0L($Kb,"2򲡡E7HCnNLf\]_2mNϟi"$ؒ8>>ytd),]GSmsX'5-I(9$"Tm6[p9x}QڎwOc.ɦE6MUrsHY J.ӌbГv3= (0Qi[(84 =%b:ѰZG5yB hY7Ύi ɳ/q`_dt(T64ӥgѵdNdzt8fyR\Rk:7&z$!ñ Z{YN(Ze7k`8J- Uz&0v  8e8Z_rMF[keUX%%1-'OwO~5Gc Hʌ?/9z}f;GU=_# #V׸DQk&}3 HOgX!<_\Ҕ1= -׳s2 }~o"^^c:/1qQHk-iIUjTql- kπ}v[6~y\ucu=u7tʸ<=gLpz:UD]hG<|,(JDњs \ |f<"˚*"PAF؊ ^ZtMa =!2ZQD8+s$Qroq즦јM05v,lOyiꂲm 1 ,m_4/cԹd]yif (73.?c3Mc( ;:n dL+Me_GZZٲ]@`MP)墫*Y^PK'g3~9<:TyBi@5U3KALBIm629s>c8C3XV Ӹ^^Rքi㟝qpק4K(똞O\ϟrܧI籜y1Ʉ*ifX|{ M3PxCf%E_9A',~gku>/kOy~ yy_|Qg{yOuڷ jCdKI TPjnVMʌ.@iҔ`M>j'i˒, )ԨB2Tt!.s d%ԨǶGvG>  Wקga%&&E)Ymf,WXo ut4\Siih:$2 `0OuC Ӥ4fkΈӄ ^q] ˶n\TLJ̊ôp=חqzZ֠[Z`Q{f*.:Wi Á$ɱ} !ٜ]9EJzQ &`JL4m jC'7O /DkQ@#D*԰M)D7dm4&UP;*GJA֔nwIERvMFrvvƽRe<7Gk WPw{Y먃.jkqXX2bg|3Y>[)5>F 4Kԅ ABp=oi&yՠ @MӘwP~ۻÝ|\)+4W+Lvy 9q!O>2+rC-hjX;tL=Zl"EgW5_&ھ@l;/^+)|"U闝y3lg/>OB*Ih[m$q IDATyRpSfs4U.ЅJ>A((%f*Sq ʹ-ǣ!BD ]KYԚ5d$eEQwX-:!k 8X+]JƳ#Uxkl_+bIiz Zlk8;D5)E6 rK؏$U;q]f۹tEN*i ?p(* = tC%iCto6WH-(G6-irr鴮KZX$,W1,lwİCY\>۠2utv Sl 6IBZ?`P>mC9: fsBT~ӟpMK/"۪uE_4K$ͧtئFIFm'~|j%_rOU-~^}vo'ʢoMX!39a]*NB3,c<c$] TCW544 piim&`SV 4]ۡi#z"b5>ܰgȶ R;Ee, Vɾ Zí{8%N[,`/EYbX6Ȗ 9G$aL[+8KTM㹠kM 9;-YuYT%B`;60 q$Q5Ue\..ϟa8XC Fi!͌hiUj`wDvl6f+$.^ UKU$iiFf*q=AY$InveAgf6opmMUUZb :sZHE)¬1{ ̈1 9@ y~}E}*t_*Ue\4fSURm9(hWhouU5989ikgpPh-IV<H [4S4K$ ي=DM~ y$ є'@W. [/o~c~wR~$R謦*IM](@~yؗA |Y/8zy"Y}̈́4(ޤO< x㭷QZ9e1NrvP[>PBgZ[&a!/JDI.t8cgta[X\]c)Ԣ:LyEjMkDx7򹾚sxɨ M^4d4NZ Dqt{o}.ϟcj$Au-Ղ8_]0&=~3h*2 l$/ am|jR5vn$Y0\œKVLIFݖaBJ00Xo9><&7tR?-j` TieI]7TeCN8ګD$ELX.Vd՜@z.i;sNO#3-xAi$q}l$&-m"tCXǗDUHJIU Ri($L<0 hJI{3~ܽwtBR2mI7h[M"*2ihd"fxAQդWU*"rLEsڞ`:賾X0Ti]*c0h$M O4 5Q~8Dz4\A5uZQc[e6.Ts9ꑦًz6 KP;ߟ#"Pu%YؠBQ)ia fWL)j?},|ڸ$xoئ 3`w)̆d1 q嵌;g_ogT+sY./i2$J;Je E2IfX0t S|:ͨ_kW ~Q_%{L5ܹu8ζ-òQBݶ:qaVסkC)H-)(Q ɊtNZD/+.Oci!uoBon^4/&M8buAQ8xsш7-1Ӓ' v4QM{p5szv6-$2LTUz>G4"uiMzԶ4qO]SSqM +uayARTU+].I\鏙LGVh&a"%:wɈHq*|ҸDQ5RPhtdQKz]˰Ra:=ikz=)˖t-|y6J{\C|=MJm( eIV (JMx>?c);6Q]fe{TMXC =[,iCŶ\,#I#|Ry=V0+Ik7nǔ⎬-RX:Qda@ᔴ"cp,s*tuv6{c4&*ܞ=W>EVq78ODs0ۋ{旉owLFpf6zzEQɓ2IA -˶I~M#ڎ?]2LUq<0b6t-0B}y5uRѱ:]anKD\%[6e0px~6ir}uES~;K 썩" j}ᐟߧNuHtBNᵻHdwd3K冫+\a4:a9A ]]-oagwk#\*$57 (?nC6%OgL!h_ y&,*4]:eEUW) %y9:Y ga Zfz<ɟ>BO5ֳ LL̡F?]6UY2̖x)]`ye)UbYjs ]t`XQ+ 'Ea0P|Nīr&ac ǦZl$(H޽#Ҭc%@4ajf(ЄEL^4/䊬FhI$iHIbf4݌*"r1<QUnZt}vȓ0t*) <$IhǤy?=Λu-q%\=F*ɲFdfƿd6`lUPS h^[ʼ_]7 o(=x7moIKҼpL'd&OS4yAE3IHclk4E^HbMfʲ@ʖxhT=Զs^:1F QjGSv PkqrKY"i_.;eJS)RTr]hkI<{opgzDݒHd8*ݽCETuSٙ1,, vLF,Yͮ,ZٰY-0U SHF>J'v8͝wч\_\2 È'|f/EQU%k), @;,7.rM[ )I)ZZ*4]$eU ۲9a|Iuޱk*b\,I"ԎrKUF׵t!tDנSj&q`z<% ⍂oOq'NvCr"\g?N_XΗdiE!rL1\dg{(f zK$DIJWNsOm(l3Ұj|︜}4pG(R1oVb],%kj>S\deskuOz$McMF lɽx!5>'-SZUMXg!Ŋf[H. 6H ϟh8 }(p]|U%( aaE7lb]>aRVyp0=@Z >zF:طiݵYF~ǘ=p^$/ BmUUn~4.q},>q:AP497d\4 v NtYI%z4Q"5II4Ԉ.S >2\""t744M-3AK-uj4O2h;ԙe%/c<}ER Y ;TzM' 6( MUEQfM* 4=6A*%EQ u rzı 9jae;o~lj^{c;c*7zg2"?!6Lw)&<͇op|pK&jM6<;8QRppp@vH-;/߬Nx茎{xa]YQ' 'Cu"Ӷbmz&p>/¨M&}&b0Q i\>;ݻ-p9^.YJUvEUJ^W?l9YrDb æM =ZchknyE7GɎZVVSm)%m\_/tOB'Of=X`*U-# !njtRRƉJ(KJXcj4mG|/t!Mȫ( _>)2QY |e(zRj&xΔxg(oYg;3, #9]a롨*]briZMڶC3! ڦuTǡmoȚ;uxzuPs%iQ6jC6QpsER`8 p؄&B6u1MBޒ%1mj7nѺ8fH*[z$R61=g2#\ntѥ*ʊhQ x$Πp \.L= 9%|.gsC704|IAi-y9e@-фA͚(`)ʌhHU؆8E{p˪PU[#CTdTZ>jk!11l[E$1v[A3(*N(Q "BQtZ?>[u H<{Ξ=uY}q69JDQvo?fbT:vqO{>#/.sC4*F"t4;6`nqݛNJl[?FE0D혲7J %1 QeeF6IrL>u'>gzκ2]ݥݚAgYZV#Ԏmr)'#kYH9eϮ31* gٲyRV'ʅNTZ#RKX,AJ֠(qPl Ƃ/,q$1FB!PT$^1x`]b?z#JRE)26|юؾ}VMX( Dӄ^ƍiu:Zsy}}{lٺ NRDV}FFHӔBH5^B@xRAjBӊی d8 -gL,I:-[R3m)sɈqL7iuuǝ$R9ħ^6cٵ~)ٱaZ`z mQk2ud(Q`rrJJرz("ISxJR,{];mǷӔfYJA@j,awvmɑ%FGضy+s:rD0~^+s@3dz\|cݺzՔ%:Bg &:j1:!6_R#M[X?Q]ҞMV;`y~fMZM/#iWDXuD I#KRҎ:QM[6PGhw;4-:0(m$h"(j^3=@i`>ċ0dic.=tZ 0nK ).FJ![o=?Ca:mmO$8OJiW :U2"u Nm,?n揰yFʒm{6qwqG3Om[iԛX$xi@ѣk2ݙX.9vY^@i{MJ=?b|d=](Qq'uO) aR')vW KXc\^tJ,ũRW',oPJu)CGqB1iAcu,p=wb(5gڎy\}5+ER3ID#DH4XM+9cf/tzBH;g|q4I+]]úQ8k'i*Wc|ܨakV4j{n\; 0Bmht05=?}P嚳.a}syfvnjApEfMcHbg'!J)j:6olR*X!NRGFxainxa#-!11!Sw-#ׇciE!J{Ľ*nڡҬ}V !fvA.6ML=$-[){%jԑ#L? 9&7ӪS(t:q"pf+26oaaa ~H’*`aii0P:SZBXRiCR!$&2IqJYj:3y6$>sKG!N)iR*K/"$B&JP/tPHa0ƍ+ : XabCHuI1)1Q@ޥ̖Mhk{ sG ǺBu$h7tKE^DAY\Z6)ĦT) s,()~ vacu-f*K-:vOF<bby;׼Roctg=|B$GazujK)~1jrFG}R`g6ɦcL-?wMqw#?P8Rbay=D7R_F;-p%K1Kzl[Җ1~GiҨFoFsN6zirʮS(H0{h` ^ ڄESم,u-uX pʖ"hYⴳ6RZW| NO'lߧX@5 B/hcSM/ѴcC7q/mZKtdhU醖0Ӓ¥ 8HEcZk6;k()+$:΁1NR\*b(I H)1Ơ$$_|JC t;V>ؿ!fiuk6J-HEyBѹs KHZXhp'6qE/DHEgg>an20U!H? ci+ѬSl'ŋC}ΰ( d@d󦭌lٲj@RHcOZMDMu"֏oXrezՔ@X^a:GiP.ظq#BH&&6yaRyIv06:J\&Ma@!1Z;#B_ذn;e;nݧa{{o0>uSJk@qh5[,..nw0&Kc6l`֝^xtZҸG!IDjh6:xB/1.b{i'~^*V2~ssst B1FGHm+L.ѩ ijhKezQ8r`n4fls6>l>e%4X޽waI(vGaSN I֩6*DQNZiѬGe=(P, Mu=F<Ld &6S jm2`zjg0Om:e@"ʶȃAF6W%6ma<gnjfX?NH@j,^6$Nf\g]1= Yq4)R5JbfWH&2D%Ԅ}!yo:x"4tcM&+=%X˫鵜{G?5ZxS6nq)<ԧ9YovR0R.PzQB-^2RIb86KG|^i ԜHx`(?̥%qT+MBXDYHۈ1ZTk2&adlTޮ1>>Nۣh/A1~# "#cOTgbAJ|#JSN=tce c$m%!TSZn߿W1v{SEyIIٱk;Q4J|_٨yV3!C1RIGuzt=Yѝozc&iM\Z>LA\t:mRj%TP`fFgn*"aS*Q*Ժ-(a~aݧ䮻M[6LXb(UUl2Ad`Z  G=y31ϧ$JLMAv=leHSP^ʖ:J`bѣ5un~W1y. ЍRן5aSOe϶]D{'*w)ٔ{":[mޜG-'>vmwP!ńI=Z.sFB,ѭ69='7 '7T ֝Vt=N$n;,ܳL\8G\o"1%)̃p=O'Pi,,`M>xKVL1+YUv / b+;)\/YkCf笨eR:C )OZw&7?%0iz|#ʫv״{TF^Lx#M'sg>˥^J!piЧщ() _ RAȋ_RxC)F; E$E?dcU5;{9瞇V5f֭I $ i:H}Bylݺ{ɇ1!~q=`nn_@'{w>( CdR35<\il( JAڢO2pDtf;ga4q55Ʋo^^o?q /[J*tc $X|Oҋ+<GqfNH%/})c7뮻Fg~A^::ck{kuV.}/ظ@|6>Ə~#uO/l잫Hp AՙɿcKP=㕖϶m۞pGy̟|cC'X IR,ݦL(Ԋ9FFR:CW5TVR߳H%A|%kt:E)8x?{(B宻看 JO |OatJӡ\*M/Ը/[38︃7ͼ~~뷸{Ï7&&6x2ռo`M+i6jx㍼Mʦ͛x[Wsλl~ /iÓ/'ؓy̟|cC'8%`0Ƭyv؝p%]f'"rm?m "ĥ6PxFl.@CGTct79.]EWH1_򍌎snhj|#IR炨|%W+ = |7RXs{1p|Jc\IW y";0Ibuy>23k-^xŕK(v/c%vmwpz__{<]`spXƒ] @(6% |L9 Sq\uܵT0\$G9rȑ#Gk׀ y4Xk^/ כk81TeZlÇL9\SfPBC─R0)>Iz#Mb5Vn:pJ:+!Z0IQڷ_lpk }jyJdL %@)T*\sa&rm6|Oя|73۾ sB hA rZkH)]L\&7lA&,AzHUoxWt:|}/B~3\f|ͼMo/8Ibtx^s>RT<ܽ=ܸ{+a|sk-_W8sٷoÎh"C#y<y_1Y`$A1_PˈW1:swZ%oz5,oe-+c};y$JDpX~m^333bBmo{1}ߵ;Wm1җ4831/oV CYkWj`׾52w~|.?K1c>?h"ysk#y'B ?֙k\ \< 8 K;(R)5q D/, W}%fvUļM6VjLZ؎4ڊ̊&B_ ظK+a9J*OsW78wC '}<)E 6vO#xR ݍHaժ)aiDDz} z\-XM'Ke9v.0Zz$&6IW;kP*կ AS(!(ˌbhjo0Ͽ^Vz$7jv"ayP(WrU3% J:"eLM_Kǰ227AЧGXK;hَB8'\?4KH&iJGaS*6HQ N5:MQF#&5A&%"ri4"B\ig$%iD=@3HSg=XlH:mX;N}{|chZUǍy??Z׿<1<ȗe o1p2;;}>6o1c...rW}͛7<׺G:Ɉ<טk?^2&?41cG k0c J*1x9)MٚW"W'=H0,%K3Zk:![=ѷϔVβ_Df!c TL-P $ 3FpX$hpʁ"MS anNZ#K}?#|BhG<H)j(EI|d8Ʊ&5y~P!5$jfJ)Rm((yStiYZ $z&uʣxhQzL~zx'0fgg˹˹Kp,84vmS;v8qOdq^r 7 җ+qAEssslڴiն(xǦMx;1g<;cyzzm۶==qOF1cO}/r-yC<+cĈ5 Kfѷ7€8MIL?A` |8.wjim!M"1I  H_'7PGZI9%&[~ط7L=I7 \seǓ)q:{BkTAje95lȎu׵֢>]ĉF hY^*@~/ X!Hrq8╤I !Q(_*DYsdOX5N=Iky>6SzxstQV_56n_?!<.ȑ#ٳgն'/2?'7'2Ʈ]7*统PTظq#Bw֭[۴\}tM|[b}7pi>oذaD=я?y.2nF9}<1c</6Fr)H(#Y_fZ2"-)V5ZY#($ykS%ڍ&S$x*k,W8 {ӓ$~ X"NR:QF(dV7&RZhΞg1Rc$=:Qj0EkͧW%Hs5rz+a7̎xiBsIvH!0:E*A@' $q8;ZBtt>ZI޾1'jo{ۨj|ӟ^K ox7xPZ hk^^UÍ?{sAnݺ׿zz=p:>Oww /iuo'2Ɉ<f̿/׾O|\z饏<c|y1gֶ^@E`c,C`P$ŎcNENt"\KI9p:, m,ĠN]KuFQj"޻[C)bRҥJc@g*IR puW#MSJ<8_ݻ;3Σ^bݺu;͛t:|d֭ݻ\uUm۶񖷼-[077ǕW^6.R4kٵkw3XȽ=Ըvmqn&(brrWǟ?1yի^ų,^Ww5lrLAxs`kṥjgɤp)fy}B0P<J vmKt` u꙱+(M241BF):#fO@\S`EomB !H(dnH)AX$N0$b d5=W{KK@KΎXFJ^)s.R&w(i3ҍb|%bqiu]]TΤZKv+z(DL>}7c$9rȑ#G9r<>v#l}_̺m foW RbErW P}#q{~ ZߐBzPXC)y[ fU1l[tt&n@IZу2ٽ;S9!vt;+]{c`SNQKӁZ uά8\X:&hm0Lr͐jg)$qv{xN( ]XD#ȔD5N$WѢQD?ϵt/ u^oȑ#G9rq8YYZgnqճcolb|Yo-OI0+jر#fí2GIA/Gip1%Z'Y2)5pt,V@%•1`%UW(1Vzๅ)ƂNS$q| IК42wϥ  k0i(q v;%]XvR8JtB9H6{FO$)#0(,"KM4 Srd0vD1u}Ȕh)L "GRӍ}њ0,qhtJսIƮ(Kt(+\ZZTAPN͓Y}`j̊j]*NZcZAI:nru:k9rȑ#G5$`plN|e>s?КeeZgX%Mt4I3KAM8]MȤOTIu`#AQϩ'8ݛPF*I-#VBQo6&$a!D*V'"5)'4AkT@W3FxR8¤#p )!"5Bg pxJ jѤH!)K;=R!KًJ{k)A)6MFSZqfi:Edͬ^յIdߌťS0;e2f=="#RbYlVT4LbΙW9rȑ#Gk׀ WXkmr@Aݒ;f>0Yfs*YXw 4 nnm)m&NjPsp90T^).jPL3 ,Ha-q`K7S#A5HƤfWR ,3Ƹfڠ"q:2%%Hĥ2q:T~AM'H%O$E<)3cKx^@t$IJ5BHqWsgv" +Ӑ2TX;#[/;d2~YIR&ɑ#G9rȑ1qQÑ۷2֥ =FK[[C:s B +9/;$BZ$&?{opZksHBEhڸT/!UJ՗DDiU"Qj_RmQ-JjAAѼJRE$9ɹm E$g眽YfVr9|<emasd\ӖжV 3}*)TxPD&A&S@R%F ȐEAӤ@ drJ,c=Pl2 _(6 Iǚ,#H%5 ``h# &Ul8d@!HJ@@Nh8ZL@\6kj24Kcʤ(X#0F)d}ӐNi )A"p*/<<<<<<<<<<64ƴva{ +dB $0D(Wm 6#F`nwD8ú1Ƒ1ΞCnBش R‚9ic DPEjZc J`&vGc &' F` Le-]̆F2t50TDuTPC}ղc\:j\0KR,CƸBB!B\c j 2Nӽ6ٰYs缘U +FEb<Ž%_9ba&eיPKȸT{}-b3BQ0ECF@$AJiQMǦLI]s;N"u),LXeS8k S$7 cqB(XzM7Đ*%TJ%RPRB0B L`aG !"6D(4(@p$0XB(0DQ`4r0B kLb=OP6Ni a 8GV3*2$A4Zy *VZi"C8EkqIRŚ\&qC+F̑ᛍD Q.&*-Dڐ%K|iTװK%VBPyqUB=Vs6LX.~Ԫ:M&= RDHo .Ԏ\2BK(b&d0 maAb\X#9KzHd W 8Gapp3TjS8ӎvO"M/R`%E aj|1( 0PI@A2PIm ~1䛙y< "C $R! 1ce$2AQ0|@@ u *sŠnJ#yfU*V 5ዖp8I\-8,љ ewYJڐB IDATi϶.}nRrMQJ75Ӕ:oNl,_gy&&LO<?z/J0}tL0s^ߖ|ƮiӦ%Oc s=8~kAW_|&LG) {/Oti[͏cYw;k^`C9 kIkA~5߯g}Cm݆D owm98SsϽXWf /1SN9%w\)_W8蠃pQGa…oٗ_4$`"?BςB\ ,6 7 ECnLْ) =+3D!T+#GԊ0PPL<4w.)J'S_a%qo^,DBr!tئQV - M}+P(fRJ*]{Kj}^S3b3!AC)aAPL`*ڀC (D@aII@!L4!ZFPQ(4I4ʚQiek;b}W( g) ss-T+{iL_ƹ6*\ %!YC=g9s;sE{{{f-܂C9>^z%zؠ 裏FGG~ӟbʔ)8hѢ!w}7oސ?88^83`̝;mբEX,>vi'pXtj`8s6x .\{xb-{~7kwy'?|vi]gn(pEaq7Y>|K_B\ƏcoϾ~7WfQTpg7l޼yя~N: SLK#\XB+vЪ.ZV^Koik͖6zմrmi練.ZzzzVu.Zѣo>ri::۽VuӪ.j衖jY::ک֮YCo\Eol7WI}48OmbZ=ttqcMT.ROo/u{\.QwQkG7utSGGGnrDm]nz{{ipJ4M twӚnWwC-]ERZۨ{JT. R4H-B]kzKS.Qip{i{:z:֮ۨiֵA=mi;;\.@_uwuR\rD}C=]vtʵz}e jzմ\Ӯ綣VתnZC+ۺilU[7jjZK+{ܹ;zhU{9EXE+ZhEK+X^_BoZK+ZhђO-{G O ]L ]BBk֬ɽ64tG\@J)w\.ʕ+Ͻ_lOzW9{.}_JGhy""RJGA]t;' bŊ}Q^JI{}|˶"͛GRo6蠏|#`wկ_;9wu577STz5PJы/;vg>{~Gƍw_L{l_W_Mgc3g]]][ТEܱg{SϯO?}&i`[c֥qaBH"B62BԖX}Q@"Vc`r*&1$RBUTk1JT5! aZ}j 1i'@s`4 @ҸYkioMh(SLJDQdnAf ?{yWN;-܂wܯ_wwsO_6,O?N|ӟv>3/_> = $FH$cAEŜAG @tQ`SWٙzZ Pa<[r >'p) RC!I$bDUƱ !tXa}C)(HB!(tQdAJғ1(.~xr>3S|R)#@5(#=c0tΘJj `Ϋcax Ln)q)EDP,j2':,(U !`"M~< B2]x[pGx\Y 3M4W ßҰD0ҹ_D!5׎Re)r@V=FKK  pg⡇ɓqgbm]oRgy*\|.lC;~x|{׿u\pa`}ݮދxꩧ000뻳[cb ֮]f\y啘0a/aʔ)Kp 'C2J3fX=`}kEWW.r׿ֿy5߸X!0w\y]v(z5+_ŋ|rWz6[cbɒ%sA@{{;ƎkNe}٘3g&M[o5%֣Fr6|sȅ#7_4H1':׈i%KǃKFBctak<-Y|?|`187L*΍0@ C.h..R|k_CGG~$}!FFIX."@MZO ٲڌCv˓JPpCt3T.RLDHrM m* (B%Q f'@[ aT0!MMMC*B_o8FRD"%(C)i a!1vJ) `L8TT1X_(Ii>BG̓#sw,u>ԟܤmHǬ˥S<5 Z B$F\)Zh @P(C#8fSJ}ٸ[d̙3#7ވTy]Vkc |s{OkVkN;(nVǡko'2e[ Xx9r$J,[ ,wsC?o5L;===_'Yt?FkGZӮadI\C\jF lJ6 F- 2RE7Rju̺6jkuTM`+$S:0TU]lڄOt#sqA3gI6%RB:?0 ?)% MMPR!B (1BcPd8XXO_ K)!UZC̅7ֹ'\C SH8QbDT`# 4lh/>|8[nsŲeO˹]z饮Q.zj̚5 {?o~|Ap x pwK/{(رcOg}/ }E-֮]-ҽ1cb wqO~}CC'g ƍ{kMK.ԩSs'~7]ӵy7qK.O>GƱR|E]^<_O<I?~<~ak8+`ʯF;c8 /'@KK ZZZOo9:;;svGS+[;iU[{Vm]`cu[zoiذ[ipzƛn#FЂ[Ok:z:۩u ]C=]4G=K=tqzUK=vuWniU[XN^+uOѰaSvock}h7?ELBzIZУ4i4|>F{.bv@?zoiĉ4|p0a͟?Vuњ.I&h#6}s/;ClK/&MDm}A[;AGk'/y Q4H}C4Gmk}-vQ\22zh:ROg;tQiPutuRipzڨ[:y}=վ[4O}=jM;Q\nm2ۧk4Z:zyF[7n]fkhik:NZMovʶnZMkLەm]'v6׳g+ۻie10^7Uu}>L?L_{w/V\U*@冀-[^::u*͘1c;m43gNロme9cN顇rKn[b3FI2^xƍGwכx!;]ZFƍ*wܯrͯc=HJ>-^د&KM ,-ZD߯F;{u׭wn_yz~y@/_M G j0 qĴiwcmk@o׎?Fy瞃8qe?83!:dl8l4<#YgL^ ]vv#֙[`̓i+c.`qajV$vLEF=ʄN2 xԄP0$N-e"JŹVs @Z1u˸Q )М[~f-e䚑)k hlxhFB3OE(}i?7lERթqvygGNXj=f|ӟvvJ),Y$dq)(Egpb_?<e]cb9+gyN*$I~s n&|x?1m4L:wjꫯTo1܍w6k׮ŪU:5OA@OD~3xYbgkqn]t<{Os>>U ll"GaÆaܹ9s&$6lkӧOްoYfaw0 pBǵ^A1c >ZZZp53?uϳf{ƍ%\9s渰;/Ƴ>cNٳq=`Ŋ8+?!x;RJzhoos=ӟz)^7oN;4._7xǏ[n$s/z!DQ'6t5|;kcasꝴk~!+ĬYpC)[o'Oƞ{p,~ߟ5;1|vi?^z 7pN9l ۼy饗p1`9r$. k4+5x'?wF9sଳB矏o}n~ߟ5VZMz$1c>w (ЄK(Tj2|1]>)tϰ&⸆[oY[oYְa,?vđbm1Yn,qÏfoϢT2Eܲ&o~W7W~<z\s=Zb8 \vefmsŞ{y桷򊻿,?bՅB!?Sc'bڴi=zE.ibԩom)Wk>n8,Y]w.BEL2W\q:5 . #ࢋ.6l?G}:7ogm>я]w݅+V??qW`̘1_ǵ'f̟?#Fmiߚh֮jr5 %8T,@R F+0P @\B5tbu :=?C©Ƥ}'EfQe]+./{n{ֶ7  )%OhoǗ?W_}5N#||>g1}t$ B*EZeШO0q0 H[͛&)M5] *2 VXhZ@*Z z|cM2MހsX' uLH&_pG1=%I >vJ(UX>,1 kH?wNxrJi&ĥ>K_z]g;֮] z뭏PP2e9`CŜ1@F] !@&-@p=kHTk`R^O׻ ;G 螂 IDAT/w_콏~}r]!R5y8߇ NbS/^_rcvYՅ8 fFH \pK.{ %\VF<9@ D|`\X .A)cM;V j ʱBXYv1-b9Zp4O8)vesahRf\"9$rcyl0-}޲Z^fGicAcl3n"V$!ӵaDMv" #8I6{ka͘3N3\.?ޅ2|vOE~]K\qo|O4Fl7b{8G3{=uY~b(àk}mtb㣏= rɚ/^|U+nT"$ f0DRLhb%!VոX TkRB(Y M3c0;2dP]\r7VU-{=7ca4Ji$ёL9Yݒ D j0! {vk88&Ц^p5$݄`͚ ƹ(.giXST +Ln&F1)$iCXJGZa EJPj a7I)%ȺVbҶ1PU@ L!TE8ބJ) !5ګXȑn)r ];ϲ{tqgn-mDd^,_ 8R"|r]!d%(0e:0kxBsdX B41m &̺qntQHٜ#Γ/0Q>Jp jM0UB]pDFf+FyD׺(|!DHː$ڈ @IBSd"!Hq5w~ȍ6Ga`qtrKg[Ss,UȒjfU5 Cʅ&9yMm H1JBnj+L:o\أS2=<<<<<<<<<646l4Q cHjU""ArC@H68E閸0avV@ح{u ! J5HX&`@h"}YS"G8HDPIj$쳌U"0BH)S,N=dH&18 Q:C%DEb{VU %eCK3a|t{H_fF-ɓA;,;V49iȒlajeT:pPtnKI6|ǦlXA ` M!GS(܎3œAVȩ[p?tH\6P(:$BWfB"j, qRN!FHg $( DRMb0XHȖ"ׇ&+yvl$&J=(U D"g 0:?3wǃ@kpd0I 1$CfΙШhuQRwypnqZVM͙>=:ԑR枛`Ő;IǦ,e8gHQBZ)FB `A.B0.$ c|XǻHi (E(Ul{8"ǠmU;SHuT 68+BTG(\\Y&s}H0:bK&$"B-Nt.ҡ:,%%R[uT(v?7,L.DH }[5QJ\&v5Hu剘!2\-ˊMd鯔~ͭ_Fm憙Fgw]M X90XMK=XTTXw%cUi7$E*BB$p{󼪑sW3TF1~$i*#er2/sʶMRR:YrC&1T Y-CPRk˃JnV&bR9Rc8;IKI !+5nNHbTgUJVR"?wQ6\=ٵRg7 21ȔCpT0.{37$&.d8H֊P'!&jdc@"36..\#DƪN \ tQ5:G&0 (6J9u Q 8=i+"(R M 'Dh"fX23>XVҤ0EHu- jȄ ,i˂1[MkS0UWShBfׅ9b:Qј6Z*MJOF^t1;L_iOI5)1Jiݪ}Ycɗ:YRcs*+RIM~2_EF"CGVBu,L7&DCˠ(4^A) 2TKy"v5@:>0PlQg.G !jٵH}SzhS1|.,UՎ~_V]5zK` C">J5j gFY~nClU2/B*/z?od6m3ȵsRB5XFwcKqK&G SLTaW&5Vγ6a?09e.c $9H:7{sn)QTCI $d뵱-u)qNזeHgim0["14)gF:%"Ta9Q27 (|C"HPJ3LΙI^$o=zw1dtp:e]>-9>K\xMIғ(jkR!..O-R])5QD,؜+/H *3`.;>˲ߡ$H9_AሗܜZ:T̝U> R۰.Xc#7"%.c<%EdzBp &J&PR&6lFr6?I."ڹ9 7TLafd)mT$=/"*:~ LLޖQ$u(-L5HIpF@+f/K]assmK"ˌؚ[4;˸qT=j|jN='sˁ:>"Uۧ!{ǦHHn5d |VZ8V&N)i@yT#Uv`B2״Lu 9(dsc0]3$9'A iй]f"=?^6,d/e!M~m8Grr-ʅ^f^-.vnmvtO.L2g:dfM>2l~!F aR(I#u2vBM@HladF)!K7&T.eb\dsKVj1ת޳i̍d4ؽkAf 9ײ_mS$*v3ࢎA'RPIM*I W X g֜jk)1˫{6k\cZ+O})3Oք%&6Puچf.cc`9`k6C#E&:=:'51 KH*Y7 Y54ӠT9I߻Ll $rԵ8!SճM6/sUܘ%Λyq*EeR҆IZARd 9Q!nH2;F1s,ho&G^O2eHܻeE)"J˺(OdCؖN cBCVK1HD#-Tlߛ!=}6ȐFYJ`҉6RڀCiJPB bvQJJp_,s#d <2F)$Ɛ:"M6'.c \+nN,+^>KtS>BFu\$!d (&"[MCEEQ輽sa>K^YT>̉5*nW4󼬓ZCO<<<<<<<<<6-4T US1! rBV49ajF8[tYU0K@`&'EF %V78fX?mOmW^S)O0]9Xu*FJ+X2]AbƴC"2k.ђ^QhBPh6F(UŠ$!nX~ kfAFh*Hs] (3Kɬ˼t[}qAC0aÊj T(KʀjJ`0 gKIUxv9#Jjcuev6,%yYSt+\oqksxq7JTK ͸Y~d&G1hMBίZJ ;H0ccØ xX 鄹0LY1ṇSm(%|& *[k\YJȡV^!׍[]zxxxxxxxxxl$L,WKB\cD Z)Mɗ;Q`؍u(Gͻ9Brgֹl*U?uU1i4`O† u&fQ>'*r)ANC)Ry)!Ppnj鍬"CC*taY "Lߴ0Ʉ~MSfw膌@xJ0ѥg~{ҾZI2{(Gc-e`6BQGҥ)v1e %MTBR)#(6kWB$B6ɌsD` Fa$!`]%ETԹZG S  >PfVde23ؘ1 ?B>tN[a*㶭= SU85zcBCVBQr25DaB!B ڕqZ Ґ 2rdiVկs+NeM<\ޖ&޸=i?L>/ v?$q]7a J<<<<<<<<<<64$`a"ՠHB@R@-I!q#J*m/edtlx+uE %KEe܅fU~F*Ā9d&񙡓Pg~*>h|xKS7&1.L.] d"KŹ g@V| "qmgLnMR5I2K2/KWy3VVm:狥ozk|`1C3f+l5vkMn=<<<<<<<<<64TXv  QBA1iOL-(,]*^+tͪ:Beǡ~mA41UDzP(4Ts,q B,`HZd﹗Z`- n=w n;r%d .8c,@C%SJ"HiPE9TC.PgT(4ʒƜ0AXhn=2&nΟNfV""eLVCr a4CR; !W:U~kh6 $8m8K_: ?n (FR T hBLm@Mi'`PH )AB9Uܺ$15D&udP\!wz~:ZeOG0]geCY<+JfR6YODǻ"(,krGB`-3ǡ;!Ck5xFcDh=I` pRr*cL,F0&@ 4 YbGqu"˓\/nՁʈ};zh ]c.̭>ζv})cә>3Oŏ.~t)&Mc+q _ƨQqY@X rK'@S M=O?'ѣ7tQ:xװ@p_ĨѣqapaMj|\xqիViGsZb! xO9n}Vkva; ^YeӦյKX9ϭ?uJ_/7HDl=<%sKJ+x)L#PI<*'}R>mn ܨB:NZEy Ce(6q^zj*|sLq/~&b3^}u9>`2}isWq#p__d}ؠ [C8ܳ]vmo~k| Sp =0~mq [oI pI/q-{fz:Z[qġScK :RLאiB:2_27٩ 9%ΕΜ17Y |Ć\s XLjaJD*Hvƙ fJ%I! 7NwsJ."ȸW",j5ԐʈfDPcs s>aq:/`Us>b8 > §vg2w{L;+ηCG1jBs $G~\* w}&fF Zx|hx$28kV`1f-X]>#}a}xGy?aOL;(.w_ Sl`! Gy3[lniJ5)*9Fy6qGuR&{@ xoq?]w|"YE {}5tcC!"P+ xy` 5Cn~\mm.RJs 4E9&TQgdc%5H@v&H:f]500j(w|i]_<ğqaG>lV B@I-s DJm`fCnDa-@mƁA 0j(=F?˞̞u f:%+WQ;zU|YSP!S*:ոnBcALJ&?S2n͸_Mb=<<<6֩r!tV`Ku/kU.I;BHKu$qj!nz3-@hg@nJI@ZZ5n3 Cr9I<v#oKs4_$Gj/{okkZ߸ܱꚠ)!DJb'&(`YGD Ac!nku3=o^kùu+({+u?9wo|>k{yxqVʫY70F @kJҨdcv/%FH%1|h4ɟY4XpMyo;[Z]&k_B]Ϻt֯zYUu5c j]HE9@E\x y1>cp\E\{)uejc\E+3!X֖Z"Dh-XUo*eR5JV\0P(/ D*ЧJ(~w7*]_o 7)tkQ&| _|߄~(job>sϿ}SL&HQ` NsYggܺusf} tմy + jΊGzۿ=߽C&x5ɥ೟yy|_r<͛< v|dKP%r.Kɸzknraio@gIYCh~]0avjkQ_5;[ߛ6Ց5b{}̿Zq1_E\}؊hMB/)d*YtQEt.@8o`+9xF[H-r--֭kkkO~3.ڝ6?P:MQa{Ev,KyW_UZ k;?[[׾;8;=_;Ϳ/o/|˿w|wQǷ۷ ?8}/M~~|F.~ ^Wx8g/oPo4(~3q/B, _wK_~'JP9o } KUM\/Wk2D/ +RH!ˁbSSOj w( )p3՘h_c֩dϸ_\q̮-\ ]H }}@ȅx[`Je-p®$l ]XVYaA9t|O}+ښI@&|3B{oq"B Wt{ʰ}!~ _?!~?wK~gV?-ʟsߊcn߾o??x__]D|1Y܃W?|/o/|w,2c?+|?\s9>qvv/Tr{ <iy`֘GX/0]9'6JC\drEo '|O3 4v.I6x4=1y  zt{=a|a@sR (f4@A Fz.GwOotr 2R3ѿ_5V䡎j̟ ^=Uk $(38(锣̈xs}Qf n fBrm?g<9AJx˷k+v6%$RQр^w+i)rAG\e)^(!k|~y17'Lﶸ{6;{Y)6{}>5/WJ<87Bz]34OW(G>Un[3Ҥw;lﶹ| j1dI6Q4e|6GV=٩30&<̏OYȟo`L9xW?:Wo\lpcpio )pDZaNƝMFbL=nq v7޿sE\Ļ}ꥉvehcVUV b.wk:4@ ^"eMxuBH-60+}'OwBV`QGo^_~'~?]Z֚wSOsK* gךy3V;tgsE\} X,=,,R ^+,U޲2IDryV ` [u)ƚs?Kj]PRXBm,ED wo_s00,wOrtzH0B.Q2p|UHHnPcsl0j]lH6Iu)=pxLMh7 v( n(:!t븎KPT[KǔJ; GcH D4I:e2h5f J vft)a4IVຒ\k,F`i6[D8P"d:aAf`0??:h,u|&O_=kc9=:Ӡ٩ѨձE5ڍKlo&'W( I6'36[8*$pZX,^O1)Zt\SZ6qc<83ܽ}h8SG"eADSMS6}F-m.]n{16t|tz L d.*\D!Iin|>ƔA9eyNQmGQ9Zk-s8&ISh99uiBx^@V'QJ)nj|QFiqzt̳[x@e4D^=D }l=8Z[ Su45P\/G)V ]@ar:Xl6#C$ƃ<9{@:]γMs^tL>yte,#4`tj4f&c08csc)zF9KafvBʔÔl#kY #σ1mЗ9%TJV/f r)XZ rR*9BbuQQ ]% c3%#fBHt.zQya7lwy|[Vy 5YXj?[_9\ "_Ғ2 %,-so*@%e-/YT]T= ~wwf9'Sܡ蒤9;{{Ɏ稚GAg)9XzI!or['t]FM p"])C8^rYJ2gabtJQu]ɈxJ^<أ~ '%RУv̈́{alpv'[1MMۗ&Sv{FLBg뢔OǤ:"S tSdيB/5yV*YFQSeyQ"$jBf)YQ-4RYn6 1FӬרiJ8ִe8V5ja'\$˫y;oNDN~!O#\/8_~^'wɴltܹ}0UKψՌߢs%SjuW6'(1 %=ZApMy(f<#( w,xJkZ$K"t~N&ݞRJՉc^q;5NMPX) ct"^h3 &gOc9u16SH6X<ǡ^o78x~NKzH"#rl$1Lt-[':vtbznx/=`XK6em** \U%9Pb[JXB:dzU-)-^PZ/;V1y ZK Fj+p{V٪HdJ*H.W ,k\!e v\.k%[նEsy-m붼wa0xe5KK] Aࢳ]|M/H8IQ6 XZV)Q7e}&E2 0$v tDVj ,&#rbO\̙EdNPM舓ۜSh &q $!My5+ӄ~vju8hFa0=8K=4!/r$&b4u [> k.Qw M4ZЉƚQ|HF8t;t&ت?מGg.G 裻<.tt(%]dIJ%M4o2>1!5aXέ#Ҹu|w|Ҕ4N4:^dDXZEbZmE,Hpu IDAT2;j[6I֒&%ZAբ\HU6\cYV6բ\,jll]& K*-O[ <[֔ηy<$skLֻ=q5yd-0,gu=e)%>6K]@p}Js0.G-M8@8NK%,sk_Ԝk *sv.5)7Ӗ2EC롼KPet^ޛZ:PDtU`OzHߗlntp]3^}-=g#ACQ^sZsxg)_yU^9Qq:8"=a:1lomyKie,Z]v3<ڛ_FJx:,s댆SYрã3r 8Ay8~ZNe>>6O|΃NC,hd>B\#@Sd$$^ Ist:E ( Cm>#ZR0(\''(R)Hjp.p <-&و Qm_×m&0g g3^ˤqDP8HjE3lluw3C6/M nuF4$Ӕxrv2/l0$qF#c(PNJ;=WB#WI'gcf&k>ͶOpu2VǑf3._~(JyhԚ{sz4 }<f0 `J$8<'*9&:uZ{z҃NC1w^ x09?ͷ vA+} ,>J9QCM"q<\ET$M1 tZr0h6ZK5yLE8J(hiqY:9kfaPIM.&ap1û̓Y2O8>y$JXآeDG H0@JҔ,Orq6q ♡UeוKרm~S C:=V4-OcL>RA2E2[y޾zPIzc["coo;f>S{0Ny/#tJ#Ҭopz7fxjuѹA!)錻w1Z1є$ܐzX.}fH# =z-d<>0}S,ҩ I@bn۵~+fI(fϳb͈b1fVWLw^w@߅[apF.rbj 6OW[5)5t4, -%CfyH fMۍ.i6hvZ`<3CQ2<18mǟ 6Ѡv3t9qV:L#z#KX f4-t6}6wަrm 36'ggg~XIP)dJJ6)ÀBF'y14RMP}yXrO°tK9dgf'L'GgNtJ u47\~ҬKYGJJ\0ȳ%6u%(HIKYRki6:H5.GX|a %GwѺ ]Ea:yLFtu\Wj1E"Ht#\:  #'9ǃ2bNNNhպH |E1a#=Y|k]v۾Lo!lt7hi|f)q# vׇN&I2;ڵGX.^+=_tto}Oyk=&A".⡌PUk|!y[0Y LVpMp,.L;'bQޯlym i6 dPn%34B5ٞ])_XR~X-jʗ .}S_+@,S<-Ar08sR3jV>J+`\ܡ\#RP/3Ɩ5yŢ1sy- ,,|jϔS VUn6@,m` Kϡ1,!kv/䲾!՚$ck$<8dRRRkeN{I.NLKY'K#6R& ~aL_>koprd `O[oIoKl`ň<)kHpYF21#YNi³=5S h F'XOI\-i}&SZhv WY>8nJ ܠ4PRUu,y^T6rxG^`AI4MPZlNOl K4wzܥm2 #؀zF#Mth=ύOWr-iտknQ.w%.:F%e+9Th”uaX_@lXwhsCjˆUg_eU߯Ufʷ0^->!Uy2ڔ,)?KDM|x'SY %롤ⱧNj_zV͵Wx6)gp^0b K5aͥt5j~^I KN1&K[E{yl9z@m2?r:;wQ͛oPFG'86qE[(t;*KfԂSdxO1V gC!zL) 6,S6D7Q/c4n%Mr@k VfAi7]4't; Z,e4:YSؘ9V SDQ;.Y6Q3BofCg8Np4.k7(Lq]Pj;5!YFBXCdi Ln4A-`2D&/b tpD3NOǘHNr;!YhT|EO 4t|MAIEsWkFP 7C^~6qΨyO,O<?&2<~lE\g>"=%c8"AO=8N ˯Ҩ h@#љGPtb< ]_IԶt/"ʸFK,b|b8]T,j0ٸX6Ϫ4,X`kN֔lY[SZ#BJYNbDZ~' `U {֒IZ0rF`|^` V.'q+UZ/ B f! rߥSeu~xKVͮ#e W]÷er) ]&d ^5[9eV J[E}(?Xe%A-g+b !Lc쩔`%^PY<p\oѺd%BSaGVh4co:'tmNo/& w5$ ],F=dtGdQ-Fua%ZZo[oCo캼#E ]Fi$Mc/jpuSZҪmmw}"[ L#%ֺFkyem\',@8CQE ]%r1lKe~Hglm9ZemJ/>٘0gexI64.v#@#$o>(1.o0"\sB!c680pu &p7_"w^E|5LIe- Cr*VևZqk PYkRdt3fHtȳPvM'IW ԋvmC2."y ^?׋r}y]}+Y@̢)1p2F#lkMT TɪY1K٣-*yf/`eebJजepe_pZaRr!\z>J, J<+Vk%`NTT r%s(A!9&KѺmy.8ZX&̣9Emp|6߯")Rzܧ]o1DO\e2h=x(\Not@yLyGI҄vpz6GB#V1\c\M[>m&s:aZc -"B#0&%C?IRmWQWzG&zQk-IQ`( MN4Q2<)ʺO:ϙLGDq&E^)2G\2H􌭭ʅ4IthB-jaP&Y3& t QRn EIm :>ĉ'HJO8RaLRF8ap4 }!k%:+hѻon;YĒw2%}sx@L&fєܤ 4"ӜrioN1I6ŢOF̣!W^sjd#o%2$@xNlB赠.{-\|C}f;7$ZWSnKQz¥:̆ 3Ͼ?A z龈x(laQe?uM,YZsu^V0m0.s\0)RJuѺl4Z-0V֚5;Vۊ{:;_u9]'g3< A?,I~lFo)g㘴0aR;e; 5@( 1G)Rk}k L1ExOyt6Š]zqwM=ܺ g8u)lAǕ hEgEh{ա\HQRN6q luwHKe4jҴ@Ze^ ȍ &iBLR$.i2ai7ڴFHQQ1Xk{R>{;vĊ~GY.B3O(zD J= "’y)|3ҁŭa<@)fp/ELjQ,(Q'sk{o붾KZ6vtujlW[`[OM$1m0l%uLq:ynߚ#Ŷޠ.8$ ǏqحzqICb ۲@2mPmpѲ@nUղImjݤ5-s6 p]V)]][Tem._״y^S[ݵ.te pK@QUckPKo^h,h0Lv%)(2x^Œ2GO{tv vGFǯN`l"qC^|mlrpǺX2s[ ^<;F =fk̦K)G= cxryzuum;Ֆ$L(Z L-c8ؽ]zIy:\M j~ZS+R0"3, Z|?mYr RtHA"=(AX+H8YV8B.y>~sV9 k`H\BI3S=!*V%Q?" R̥@ϱHGyHCYH25H"/jrMH{ 0VR9o?zHYx/>--Ig*ȉXI1.P0,3"RMXҖM?AHVbzi{0.SHOBXruQX \T6Ǎ9xIoxpq9gTIzɰFc"I 0_K8q&αUl g Bǥ2ýAJ7m36XKA۴/FFc7Ar IDAT][#-@liW{OZi[Xn2ËcS?qmtFO[t#F6  Ԁl;&]KF#`k1/-vkkKm(:ݞGE9t=po">g<{r*Wsie>d&G$wbd #BנSM8abN*ziDK2a\^ߣ,si'J.\^*,<ㆂΛT2T9EU4~քAH$M$w= mB ^4Qu9x5/_ؠF/ APʼC(.IS\θ!X o߻dʇ}Q* VJc[: 4 X%py5%{o붾ɱõ {VvcaM^ݐݔ}fTMD~w-buĞw!fWa/$|i7ъ&|´54u,xlgFeA1H,Z&!Ѩ!̈́mh]>/m1fi3iz&Q-&'6`+4]7漷ͻR:ҜE U^7|9K3g|1V5apȋ/R\͉ĺ(;ﰟD=`ѻL9O?t:E+͠?d9^{?bD^HG$i~"[1\2؉L&".x k 'tI=t&!~/D>͡_>eU?$Q1ZEH)Ql~k%Odٺ|uModu0iң,*\)=?6쌩4vps0çOF%Qg8C5J"hy\7b<9,$a1+7*qF̫&W%!"!gg />b<y)pdR0"r=#} 7pӘ|q-q/2/%&8E}ȏH!RxD}Z<71"t1OqvW ӄb i8*-[#-<~1?{ʋϟ3 ÜOb#"CfQ+QBz=o붾,c|_J㸂u lIIc}R[z#Cz . RQ;=FQ&gXk==ϮӗS~!yݻz=|7ӏcĉCxkf22+LT!#A6^MJ~}K /MxyTelpOe9i?S\g*hOsk*|vww Yzb\|1a0\.Y ^3"1'<:1i??# ^p/u+#|7FHt:j|AJs8Y̗eيTH)eefA4tr;ۺ//g6 -u븨F۲a"ET7X}s%$a;Z lzDцN8ہtHmM !kh"ӝ6o#o-FktU \(Hm23:k3`M۷MR ٲdlm`VnH..񼁞m;S׼_-pH@:v7xKʶKX^?t2lb; "b ohjCKgy$5UN%y?-.^x{ȟ~XB]"ݚj'%_{`g#JB"O^xg3f GHw"W \tP%Lk$iɲA@FA@F[? <<`=I^/ H =1U!qWkpmhZk4E&hZ 6qD6x<E[CQTSgkOƃopy %)`o^^ȋ |焸;;,f =_*K +t,%~B?MR8cYVՌ8ImU泚0 pѥb9$Q5gWcy빨ӀιX*Ta8gT2>iO#ytAh% m5a슪.^t%jxdp^jM/DM%q(`1V}[=tsj ?Hϖ8BpÔ)'n|P7sFD&ݵPmۭJs 568X\?F8 0 )hU"]]?M06Z`% tD]U8nN$a23 1K%NO'>s<9j1g2[VoMf)V? e^Q1=8g:w`9Cjjzp,#AT`$ #֋d![Ś^j ߏLVz& $'J#4`崙Ǯ=<@k$ڶkgCEIFMkR>=YX0^D{gTCUz(%QK^*2q2q mFQ,%eQ"#Cc!}j$KC$ºT^"]f% kb4ڣ65ý>.Km$ܡX8!2{#i|=^QY X&$AxaHb#\{$rJ^!EcUx+|=y΀X2<[j[H+Bp *2\Or>ӏ .݈ZȠB5X VŌ8%/yL0eؿ_P%~wʞe $*;hIgOm֟44el+I4fx.ytHC:N@l o6*rFNSj%6 t5_d,q7+tUI+F=gAv2͞،_D>nnևּ@4b tGǮY#ÚF|om=xl@|OuC#W?=}Ke}v)jy؞/Q7/p{v\ &66TeT+J!߼mNN_g%I<(ѵd>g9yϗp'{t~/j zq5!WW3|E }~rOIP K]Jȧߗ8M9:bww m (T#I];T^D*r+I WWX+H> G;BR$&|C5͘UZ C* ܀(U!Ao" 1/?QK>tď$и8xxLI9% K>ƔuOW$.a2OˊNxݝ]B2_ ɳ{uGb<>'ހ{{X)ܽx)J+,cwKY,BJT<]()𫛅(*B/h`&+/{#Sʪ& `Kyݐ|HC`1ajTJ6,gSx{=*URU+6hmHǁb=s#.eYRۊ^ ScdFe :G1ԅ!}9X95Mqk5H,uI])))IRpvq]4ŚPJ\cl,A^(Kof"ϻ.S}N";_?⛿5>gD~@YxaHk.Ύ#""mrLDx.crZN|6'!eU땄"4ҷvb:. {E |j2' 9ΰG,*^)Ak j&K.NL$S|B3_,^tm}%gK[i]n(Ųƻ5ִްN*'Ůƚ׶)]ȭ1]7 # V\oɲXN-(]_{]k]!`um-_~1]el6s8 80͓imU[ێ[%n.?¸L&:6wպ lz^Vo:قkPڦ\ Ⱥ9O v`ׂD,7P;uU4^?oK7ܳ?hlK(3U^p#NNϘ-V vAYzsͽ?3>jl3 }J0U|y~ F1IY3\%CT&΀x!'x{?...7O?ynBm+^"N"VْdDaEe0>yi/s򈂰iHNOЪiƞBuGj"MS^<0VTAFI-m6  3zYqǝ(ۺd}y |\l@zue!hX\)|[f[l e&kdi%cT+ `c4?|~5Bn3_v+;mץ h@i%Z¯9v$] @:5m`aYݼnDx HZ*s7RoHZ`NEwMhJ=#6|uCd_`H^>G76Bc=w0P]xP Udkr=M-mLKUŨÇYK^<{x;ZϯȖ@NOV:}S)j!Z GCG#K9<r"'ܹDXc‚YJ;|EK_{ON]ODEx~`׀d hS*r ^t%*vÏ?^Ii|zIDdܻb|1BW:u\T" BTU5;ڛ?mIQ'_0ΈR<|k.ϧha7b&kakѽ}_qe6~:j|rHpUieYS% ..tH4MvZm,y>HI^T(eCܠh4,WkCq2H+P%4M.nHj0% JTLX,eѶT9yZk? p\PQLN(``ucMx3uHF}ri(?@[Yhw{'lzVk*Pe\NI!zಘOpbrLQ?|OŇD!]&X-2M40FHl *Xec<~|'ǯ^tm}%g|$hY077l2&m:he/Z+pҽVgh,Eln4487'||}o8'J |wڤ2ڭNHMȷ/$ui^m@ ma)QM}+LxxiϿLYunFy vF od1yhǞm|Yc6 IDAT4!%* /% )|*I#\ M]NONO3R F#,}<0ZO?`4#}?|4Z`tHip'2g:YA#%IV)B xqR AJLm |^CXA˧Dz3?_J([ `sBʄ`:ɋ wߎy%LƊ ( FHl帪H{}y^bfZzłtb%J),P NYV<{2eI1m.^!2!2dEχWXZ7iZbhģ^eG(}$Ƒ8J.Csê׏ VV p Ax^,V+.&ܹw:0i40w,.WJadMWDq:[y!eU6AUB)EUi\y>J1P׆(1}$q2z1Fjj,f֞sQ0ZAy~Bs^N#{)H)No|O>#\E`]QO(z񣏟Q,ky٘{ww9\M>lQQ.sN& wHܔ|U0],99o龭Jֿ$\h\C-$d7ƴma'a-˶aC1tM m1&ms㖩Dړj Ky}/.Z8iAFo"nCW pƎ$z9 &QΓtրjy:y=iF–J66S뢎| 57/ҎAPZaLv5{m SjnXws`T ;qڈm8ͪ>9W,SWsOWZ0bx-L.y9i/wc*9dh7rJƒS,[?uEf89>nn6TAY)V˫W,w>d3?8UPJ9Mպ(Q1ZQ%A84D.qtBY WPk1$1Ȋ,G:0̋ƊCW+JѨ"PޑR FJʲl6 $q0 ! ZWe,>`3bkyG~1,q(vy w~ CV9a".,1äý_ XT/0S..EVk+o#Uw HtʎPf<a7+Cʩq= W,/f\ ݷɳ=ݷu[_r nH] lU+#-$ 7muf h4pmD}%Z'[mGuMxE):QY|k~o~s.֔H!O6c?8E}V_]{K!^xDΦ3~x ׁJ).Y}sЍq#d+'B|n ^:K 8A7[D ĵ ιb&Zx^뵒Fe(Fp_, #/)Jxf/)%>;\_NA#kxG|txd>_ɂҐ eֆhGz.HWx:5g'5u89g4cwoAkZ)rCHڋȫ fpO] /XNk\ʵ E @!e=.'кieaF>mQq\i>Ot;R J"ǡֆeu=ԵfZ{nHY3X㰘Q *+xM!!0O!}p`m9DQbd4 de[Sa>Y`m|:%$Hأd@^EFU! bX{Zif RTtx}" =3SD\^\HUZPhʂe6PKk\F]|%u-C1~8@wxD8\#ϨHΞV;BSW8d '9BAEd=_Rf59.eՒA#FW9}G`2#/Z`*KUVLi%ؖFXCDQR%YS%(UI Z pq2' gA]8^ۏCیZs`Ŋ NW^=$ܐzj!*A AjMmj$a6["W;=\QZgMS8 ,M RQ,<%GZ#++q\OVW89R8K^uV.1Z5*iES$Zk<'d]-!n/0eA )W!,+\>uѝ,9eb:#=؉sj\)6%׊<#+;:RijZ*W9Q%~%!(a:㉀^;|ǟC..ϱ{-,پJ֗08[&T_FasǥP 5R'-,;\r5F8Mtz(G˼]^]?f0?`o# |~3>_t!5k :gw-[jynCHfܴc7lS <sn௧Pvm5>ց69Ǜ0zo~ ZwDnCG4? =X/: /Z!d"&×B4~ź*ZPױu5^r;d|5GZD/4vaM4H'xyrB,YpRՊ L+*rMV,ѵC4JL+\uM5ґ'D4j**z񐪮8;2חLqE{blEooc A^խ `1yq?rfit*/ zDrF̦Jm|JE7b] qڀR uQJF^jMGLgS O:$ql`3bʰ K[Ą__W(uN!hj z]!k|liYrv$8B˜|]P*rQJERגw0F`/I*2!̮%J|/Vx~ƀt&[xU&k|qBI|gs? %U]^9w3**ABF.YB`z\)^affI%ϟ?% ޛGKr}{o/o7oV,$ܴXh-rؑ-bsrВbYB-%Evd)Zc7k#I0̛]{ݛ?HQGw޼]UfI A8xm=V.\{{y?v~'kx7]fm$c*A2a_~'h2!RV봝|}:IAۤYvVCm=LoOt'yZZEofڤdYL Mc8!M21Vy^H-{0wv\!3,%7Y h;$( Ð0 c$ fjIBʚ `ۮ#cȸ|q|V>i?.$BdyDp]ga!*.s 4u]D3EI!J㌃! mTtp׷.Hu$hFݦZo0#0iH$IFF\(ri6ZǚZE X²uY~,5c+~a0j:.# eQ|RAZ:^c ln=MO}k[xK4 HJ͡ns۩.W>x6¶f$FXҢiUZ]|ѽ|/?G_?ճ,5Wh-p zt]:'겳Z鴎:UQ.!dX̀ZCXb!3C3] #$(fkئZ:^"Z*,ҰXRj %5`y21F݀f{~~<,o~ӷpw̎2sG]d)\╯z5&M\dķv?R|w& f|#g?M=_j~&=+'\֯0wxus w}/y oyۏqe~_,IO~sos>?ex%k0Fs`_5)+lI9-Z1:K=sgx8|rNMVEˎF/Y5<ȵ@YU%Oa `@Zag͙giO!Zho7Hg C'esr,)|eo?S FHR0$ux4.(C梌s B6ӄ~8NJ%(@v ׄim6TwfƱQp]RhvټGeŦѭ3 ^ wN0&ѩ3 v7cFfyY_QԊ5nJ-"WB"7wfYbŚY20}*ҲPCIbn|XEvv~ٸ˷ݿ|C;w}Ͻ|ZH,)xy~,pdACoykѳEo Y8_گSO{@ȏ<,YGy~#|w|'k4ᑣ1F#`EiW'tRoxN߿rRc<q~GY[;?:Y}%,XEd} K>b*B~G@<Ř4.1cNZ6:ö޹O}"_oșҫenC@́DB^üU/M[\#ҽ;\#{*;ң]P tV,BZ8 l2BY_1ghE9Vo𼼐jҟ2&H"'rBr{ !0 tXu, dy,spv£t0y\0" $duxt2"O! X^ xV׮2Gbm$;"'K#k8^ZP6NHcf:c0꣔b)̐e!Nak( IDATCU^fwg@hGHUN/E] /?шv`_ZJ&Br碁#= Jw9w9vA8Hӄϵ=Xta?gltߝqmhT[]tm1 v p6yPJ:l_?AzeY0HXf1{=|J~bngh@f"Z,4u|k,M5Ix*'\Fٰ4CRxqy9: 00XYy[[;,Ek08r9&!ZJ@:6ay&yE渮&#.^# \}S6v]=fi鸸`w系[H<# b2;涋*/|yD4c2dmgzLУӮ>q}zO)%E)sx@GOo7_}/GaYZv?/j8٧^QZIFCa2 q''Y !JN߲h<`6rйMppx@aYZBY[[LfW& $'+C8u&Yc$X][&R& Sm87ϏSMo=fuujgwo<ϘNAem 6]R ʱשf!-,GȔ0"S)ymZIe #\kWMgFc,P7ŲRx_a(ƚ41rz߲Dy!9aae.4ip9' yV Z9:a`oEsm/=wbsЇ>RJ8DU$ܐh~!$V'.zb- B_#<̧k^W~7I۲pf0:-ث`P,>ËRڼ/{O} {Ԫ1fozӛ7} owo)VCFc%X,JBو@ԕ>RdD1Miap!5:#O"XX%vYCi;ERc){<EJ!e* ;|Va64W$˫kl]ɍ vcYXO88}n߫T[&"-(Rrfrr$'6+86Q16JԫXpzqN4׫ CJo:A`1 #t*\9}B923$Y(n9{+׮^c4顬co@a; i'6:x Q[s!0Y29gcem( b$L0& C&a,E/˶5X& yx]^mpw1;[ۀaum|az>Z'{ʪEE_kUƣ! –nvU G#legcg2"CN>e+l @C^q*Ehƣ yTQVlt JSO^Z!@(IU9 g#rS$C|HbMEZyJ a-eL1]p G<H!jmf vz cx :'Njl{ǾivDEF2P\<<RE皃y3 i/ ,*JAgxRbq},ǖg cJ$I`7Bl>0(u\sו1H@7̃.%uvƘ+T,«Tny`LF%3z9&q#Kcr;_k|BhW?l$-$E?s?ϛ|7}=}?y_?~oxk8m|&1|/n@^!&_s_p93 v(T2Ey$ QIE8,iRm,]:;r<'sZkgMx*811O2 5ګm`Fŀa2b[i뢔h2Fje:eyv4wgSoY_YB81I4RiT)lSؾCu=.+9FIIB8Ɖda{>Al Y= ɏuy/ė NqĤ8CKwxU$u$eo3 )b1< yvJeBh_M̰\ ,"5i A2%ܚEZ۲߫bY_*,ƴ*n6 YPo8e4[MOD I&1 $MsјLgPo g1e&i1BDkY&PwCi8Aᑯ.&9RkV8qnjm<};︍rɸO.6i@FLHS+ԺMfIʗd@kCjBZlOw.'&z<ɨՙN($rjlL J[.wu'dDƄ aiHPgtp;Z8gߙ<+@\XӜIHd^93|W0R|d, Y,9YϽK>R,B~Kr\_IgŢvO( Tu ,F?!@Ǽࡇނ19>wy/PUx"~o{W?w/ܿw۶yKJowx;_{/$1z?0gʤ cEnYLU1X:EZvbrPg-YMa9HeE Qu1*fdQ`~~3(7r&0y) '7 vu# Ғ,/01jdB^#2t^#h7;DqLFJ/=Ee߇ߨbr0`?"R4/}fg-tl>MR! #b'ʕ+;6X1Ft]nH*F9y^Z~,i4[$Iᵶ|,&6rp0ǘ$Nɥf:Pְm&%R J#$`2FKl2# iqh ~A IV8N Uo3= X1DV$Ikߪ.G $q mYݶAweIEkiש*4X[y7u\zn \E:A>.RҒ% i` UxʸrvY6UPRBZ책k !"0YJկzuPu~a.~?_ Y&}5,RkR4y~l0,!ˮco{h)Kh._!ȣ?O?ɏI`7uMWս!to rmogby ̩s=ugxiB*5t+^wq&]'Hl18uKOnR䞻esTѮfc퓩Ssȟ)UW>A$E{#6ζMS6QRqǶld^E:goYv_|Q*˫$z`6Dr'։<Θ!,<ɲ%?r۝sם/X][&O,;I&1gl=O`VG렫([cI{}lF٥Ѭ3i6]JǶID1@Yj) Ւ8V$HltF $Yn9 zcG1LuI0qa4VJ6Ü9d349 e)#DST6%C,dz,b(Y!3\̭: yRI"2b>u1ؖl beϳ GY3RJeAf^9jrC<. &s,"bgr2Pp! Y6u~-]0}:G:nyNyVR'aq)c }>5zUThkY,u<B5(ЉkWNLT6k }&IG?(ILCE`&$bJ&c RQ.n-FKcВO>˵[T%D1Ϭ{jmի Hiڧf@!zI8.Y8n2 m:KMF1feIpF ax6AB.a0`;˲ɍ+L2$@45 &Ks,e!PeDA P:LC," 3 r #GUT|t&Qu4`HIXpфՍEJ͉Kԛ"r(ʉdYNdmpocAcS*^ ) vUVV3`Y vJ^\mvY^a: :ʯ Jg_^i.9x>+Y:v}\tr~6Fҳ3g47}$ Gy@G 3-BȣB(-d=JO)ga F1@D A"UաWDfQoyiIiX$2OCX)YW.wu|͓*9% v$Ro.42pcv1TSηs$cfĈ{c vɳS2S^iY|m`U輸,*g2+elmy3@)EP5/$a:7cMF,T6-6t6pq=\kj&!>ӧ`5s9>^i8I!LgG&"G[#B89-]4}u!b2 h5-UAuiKr'Ш LW[T0= ן`'{]]Sl^#8NQ]"<{\$eټWKiF,\e,rrl#0)5j- !|:#Lct@6˘LcT G(vwOFTDNsY P^L}N<{Ip#WZdi Wl尿sp($afd:}j$Ix.~íH! &Z[YiPk:8Mmi($KsPhd1bL*O=u8h,/1Qfyrc;6KK].]ĩ[ʳW@ؖZ {`ހ^Ƌni3O۫zmR1q}zv@烀@q"Ey#`pPsff>(](!Jp)IsdCCP7=YP(qX4$b - hdE9E\G/B)j}#0-Y,"z}jrNKOޡds:-%$P. ²1*XhBJ`Ax7,rd,X@aepabuYqKuC/5J)x]%/LG1U[o/=,Yib9 B26O,Hɥr27l>1vh#Cz]*ZZ8MVWqrtcRݼӗ.qˋnfJ1ﱲmYtu}]_BpF"Göl|MD+5" `9y,ۛיM#F! c pP ,euøl;E1kwض"#¥ 0vh{l=;aXA Efe{{TeԛJJ JtY?AQZ48Nymv#,""le{8$a %려U_)R0ӤcmPEȕBB x1U<# &uBf"_ȮܿRRR|={۪nLSlJ,/CpUi%86Ot05&Zx U#RKffyY19/ f!C3Ny KR.!ƒ lvh!;ua.*Bw+ ,B'J"Dzi!d)ud0a !T` gB.m"PYHu4J)єlb=mʈRjJβBXM 5iPxVɫ4G_ n=}<tiR,Yn"2Q/tfcWW XK' #DqXْFA(eaиV!csKw4;e8KiPq(U 9[nZCE[ހFC<,V;,O͝/|>;Z.ҕf!Zs9ۧlc8,쓜68EđA(`YʥM*n4!y 0d$*(Z\J\/yt"6ra3 ıRXl^gA%!:7UǕ &;&f7% RʢU3bN[fb$#SODŽANU Vs2_[jo# H, 3*OT|i peW3OIg/s7]1g`<,M89+ݕeRM Ec~$)+S~ Qhvvl]aGQc4iRhl6!6ō$YzQd9i`D88.U6q.BXB.A6#@E J*,˧^p|ckH`X|Bկ#$m)J!ިcdD7σ)3|r=$y\HY=|A$ ,%xǻ3;;zZTRYU)Cz63pՍ?PՕ摑ģȊ:V*cNNE#&Clr X)fb|(G(޸lAY& cnq>NN0Xc0q\r9ء( V%YRBWpy~{CY`rŒ<ؿ1!3 ݰz99 Btme0%Χ-CV3Nvذ% PȆѨ nl7 WC=i]0ca0F8.[L7 J6m)tSrj@zJjBcر?m*ZՆj╵1gM(yh7`D(>4JjavmMs54^9!d)I!JIUPe]dT9 Bx^}nNir$uIJlcD5qلthy:ߖݓ l}lWzmٳq|]*0ﰳsH\.8~m-atٌlJe4_=U$Ђ@y =,+f)ڄ"r,u8]%Ib.Ȃ8wc\qP,3@cb]h9C@b-X{)BM/7b&7~޺ȧJs%VOξ$3 &gN| :XMI=q`1[7ڊɒaEis*S%EOccxzS_>hnK&'tYf<`1_R~ 7߈].U~o5bA$jsm8>~0LP#/Vd#UU+)T0)˂hKUV8MzCuLQFCXC3Z/o ]_>yxLr°egOYG.ӜPqč֫>yB9F]T:qC;rzI(%8;?gGץ,KNPBr[g+6?yKܿKQULKUIYGrwI (\17MrH^e:ĕVSUl}ZjVj@ W`DEl{t䰍۠ }ZDZuʡ4 ٤,'NڀVׂF&,26EHǥӆi`u?& J@͋` _5 %{zGPESP!y)Z/DX4n"kt</hd(ףSF6(GlZ:nsބ. s˔8}4<9~ &CPr9SǑ#UUq~>%C-'(@xHGv}(q]E?F9x5䅦+p` 'c/w:*3\PlDp0˓%*H`q]{)Ʊn'H[)LAȒw>K|Qcg8ǟ^C\%IBDE|>#{<~(9}G92BW>~0Yxbxd9] xuw/b\Oq1{CƓQס?1[^%dݛ}g۷qʲZE;)Q~bIYTzp}uӉBGQ'@9P KYQa-xG$ESΘalB(W^']2]^k w\_qۘa9 R%'<7- \8 WpHc1xw—O?S8{?8 "€X4Du\3`W؜2[v#frS,t\!$(qAN]wm] $h^Jdm` *QauZ 6"+!Hf  VؚiA"eMah:6^j (r> L& DzuHףʒ sT FQ?Bbta+r(0l6%UY4)6 vHR?'M Bb{cӜ[÷/|O`Gu2],OvwF$EZrl%9ُ~??A>r@.u1+RVYLiJ0%Ex 'T誎A([#v)Sw֫sv=Gw'Ls G_}۵V C^p,_*vvvXL/Y.R\r8%׬9TdLQ:,NPaRǟ?~nN5ӓ)fCU9,)72 /O݀[nQg`wDZ͹33:G8ܧ;XW0x+vw^ًKnq|ݿMTh[R$)n!CE OOx%{.>'H]XY viv"l࣬-,(AzD?(""be81_TAt "`2YB0u=<%MKƃ.Qc|;\eU9R St=k&:z#֫^wX]j|d[xSURp9rF~ G}=泟7w΃snC7첼\P"Y\빞OAm-4{GWue-4VWH#~4`+r@LgIm F;rDc*RmDWO5m' [K kU ,4?cLS@+U@o3UmxHmpp൩^?WI5@lNT|I!4EV3fJnJ_'겨Ƌ(qg5kEFhX-(ǩr} |+E:4RO!jcK0wYfdܽ}lAU ?`JO;0+w'~񗧤Y ;ƻ!*{ΘŴJ/y~G^J0 YsҤ,v]:Λ^o|{p@/%}6ws|=h"rb*s8}e$ UZ._} "r._|7wH$.VtBxtL//('<|1?" _>#>M"|K:<~$^֩'0z+gpϓ_R됦)2etkj7 tg< H^"!QaNД:)p|["᣻0F^d + q#WU!䳌# aN7`[:2$K,:W}tP 4s<n1%?OnEI'pȲ5BJqOs~❷%q"]-ǿع{+њ/x#nqnJu/H+7=`s=_7_albjͦIe߲Jb#[+i(vjSd5HFjGbD-54FoB?϶)٤ny`롺vê՛zEn.A !}kkr jman48u uAx2ǚcloUr?EY]lDBL)nPIWe/DjU(jW pԦLJ)fLYlmGlU5nM"6 S))v7_Qt_DC6҉ ,(?CKq^h^(nuBvv&\3_k?;oiܡI-'r\9?'4yq&e&4.uxT隬ȸ}x*yƫӧNn(MY%:sJ&\z>z`AIYIBU Bߡu$9rFg'B :ӜrB+W!efY P w}{w(E#7$ cq}"5@W8]30q-LQerr9]!OsJey-χ3߻E2yztyN{ 'Y=]u+{H|SE!,q>%<2~©܋%S ,~,NC&|q3(pAg[t=ˣB 5%uJj޽6ϧ3lO\~:*k J+A'I *1m(&Ų Ei䑍?OJ.ˮ /iM0'9_Lq/Nyy|gXR&r{ sϾ'q&u/$>ŔnR%Ӌ֣rry *@\(dg#dY;}+#,uw4AW9a7QɀuBh e*0H'/4i|t-~ Oxi*&.3bX0pG7Y \s]? Q S[t]d4p9u|CmҘc'F\_2_vzTVy`T`^ٗKELg?@X/Q`OB*:x" (Q.NOZ^bL9QPt͖$iL/A]-s {{\.-̗sŊ~~@f8ĊUP)dٹ%],NFB޼|O-`.D2/pݹra AX[>y,abʍ;HC) v'ƏvI E-*agtc kUdos qIa %"  b?`gU,؝rq|), 0es8=;}VqB Rś^빞o`hr#m44 v-S^Q5vS{H\9U-+5I;k?J L7ak(ƳVw9^۽%^$[j4uq_gaDcm-{ ,؍L` acr "S':.0BlI9wQ֘:tqX!f4e%$ !P5ljUY H?q7@kMsPy5W~+sMxÖVh<05&E6ů3ꓕS~_0DC:C,ʁ%n,LL˂E Oxt :sD!u(Ddξ}F"r)ϱ(\7"MK|>?1BTڐ9MX+g)arr#&2IPE}?KU IDAT17Wt=VIE0 "vB:#B8RQgώ O> vBLeǸKޜP݃].y9Ez*I4;&YX2bwdq,Y14EмWGJ!#=f9Quaq%ю!$ 6E2>ǘ뒧kb\R 4];yNjJ"tDĒAoD.}&>DsR:4IsBdt$UYQ!7[0d"R.𽷠Ͼ9Ei(󊋳; C(c$QWR0ʳ9}`(JkB)FnNw0 {G3CN w/^8LP5 NVp]? v;t>>{}=󍜯` Jڎ-[6b7 6oN~~WO:XflU ^;>l6.-A䆱Uk e8R"p2DmjO$?暄T>e߶nقK)o_cJ&FJXQn6w!EMc٫`#d 6e{_1$ ,p鸔E::aNÆ ܿ|ZCYYRMІT pæ4"ѯ{+&ժ`LQJw8yX͖C /=#=h29EU WkŏKM5RYv]eMG$:f5:]vnM?[PU.;c/+ܬfpJ[D8m,WyB(e{Ht:#Np`a>s:߿.d'8{R: %a $^x{8n31/~I.'f#U_8>>nޡ(Sv'{GQN/=&{Cfrz+-{;{.er:㩊EQ`Ȋ;=) V^oHeDDE0>I\2;e<"Mh"+-ѹn| 7QVOhuDnD$eu0!|IIfXH%ބ<+X r %Vdhi߽Npm+|/=¬5O\`ܐn'dgCy' I"E+í[{>\\L]ܾy{ϑRrtpb@*Űpj`ZR%iQ'tEd eg-Nȗ9Pt<)A.~gO {*r_|#u?5=WM'țdĶW]MFV*hpRsnMU5 fބ*8bnju+@R VvaMC޴VF=T3p !`FyL"FYѨs_VױHES$7@%|wN` V7ASwoY%cXAoO!@kr{zr{R9DZl"C5Lbpb[K4CqyX]mԶv#1>wnqꂟ˿; h*2#ŵ,9:3t:="?`r4b=Oc,9䜰'*L`=$_==_Ŧ diL$Ut;#̐% &f <_DdŚNk ;}u0sv :'/S>yx|SBf8=C(@Fхc:]`*JCz|3VG?X`X|ux};}I9^NZNq|I&0X :̦3l *UGߧ9#ypb0z!n2= 4(uɠ?&I PCRGV`21R1eU%uDp,)!`OQ(.Χth4s]OLg;I\"r[wS ?ẏ>[w]*O*eNX]hsRV%n 9~匓He0֝#y2br8O$_Xγ97H B6|]2GX . aDHRg\2:}~CfS/.W皤QBx**J*QPk$A[CgoJS%~]ބ̉(/Sntq5!~"|z4{Bsz"A %BpMv3Ɋi*zK-Xc&c~O9Ch⋒/OSܳA/G֭[Ȯ9}ql2^ry~V0HUß{ت"N"s7 ɊegK׋茇&a9|w:-psŻ ĚϘm|c:[0ZӬXe~EXwP WR9Yc!UZ؀(*P^p8N]l]aZOld.}PMBd}:t'5CְZR: 0ͽ†~Z%#p0G'}=ziSpyRq +]Ã];gZcDE-.0d9T"%r|/`/PKk*SQ&)0`prӉ"xrA[<3}6Z眞^r4")vYf1<)Ѹ 0:Ew'?]ܿ6=tgzO_@f^L]z|>G{`h䱳;)Frј,r}$J3~ßaJ=gO)LN(4:C K>=N?>#EϞ\OK5µ<|xO>}BFYxs@?R[fKzZekF1q/^\'o?=w`xShr빞 AcL^;Ckۓ1ZF誎#aњ4AS Q+RJ b0ep\jRl=]6eN[cJ4̌YMЃŊ+?m#@D-Mqe7 h0VwEhـ@km-f7t p3<^JUkuARI-lTT.*jl@y'ViCDu)2Ma6%|oRu LUO5Ky|7:sLcz{8a,R1M c"/btxa\N_(MFA1\g//a*t:cPC' 1yD0 %/㍐âAhbeư{ntgO"R("$*ږ mlZ4Ph:C`H{jk`U9%AU͛*!kX{ALvklX\h߮G;CZ3b6%Wsv'IƐ%uLFeAas"-N X9΄Wg/͘we2+CU5# 'XKnK]Fg,KҤw)bܪ".\9t)!^.R%7oeŢW!pr=uNeH m J9uf듗%T"ܦ0r|h$ee$%"x~>E|xJL3/=;q!`vx􄂊xi)r]~7?&s&1HG9qrxʧqLvvxTy$Mwz,KN^q=~!FkA".xAHqULҔa:xzkX~DW%mkl\݌W&EQl9mV|HtY &ԡjpe5Db,=eay@MauU,&ÂC"i@ٴ悶mu!՞[aT6׆pl%3Da i{ȠgM&m-`Z ڂc0`R8{n ';uhtU4_^^n>rтPSWسZRv}lyͣz>e#GY-g]~A%8^r5 ))˂,-|!C{ݿ,t; rfbF]qW"_A?]-ǧdX i%DUS\-))8 !'2c6qpH,agqp]ne3<&rщ"tR{#EF< ɊIoN|hot5빞4ٴ W,ZM#P{{tUmB0O] N*jT~44hrjW&@y4XJH@!۰lU\͒M2jvK+PuR%&e>U%[/[be  ֺY|ܨ@0U^{JN{[֯]gD.Poת 9֬ܬhv#ߢou>i,$-2+xGS:7HiTH\kG,nP1_'o̪N֡Dve ]m8P]Rih0*fɫSowyjk(H<՛ٵ+FJ;"O( ]dtډM3Y6WWRW!Dv u5ut+Wym%*|2k#2RTE@_)QȲPz6Ut !Ҭ[vE8igV.]F.[K"z ټI՛* | P.eɢEK6rJM)X@NhSr+VfA{yd:NdrT Z(\ N@ϾI.Yv}0E +4] z*) "J;SWUM]}5 ~G)lbSc5+TKk^l\M5ocAGr"G^=ظswoUI$@*HQyydoGA!URXPijk޳+2ZKQ][Oy=ERir46H d!n!HАz.L|>G*>dC.ۀD:J$/A6 7%(*(  Vol+)#͑m (U'wȒekYR:UtGmZEybJ2˲9elXG Xr52/{{`6UUR޾/}Y9J.o]XdiR'6$ؾ=:u|W8HJC=G&gƍ jxzuNcӡSgjj֑.(~={m<uGa&uE :Q\\;A[&`H8Dz L 7VnBEYb\XaŐ=)`ͦc/.dfuڪ\9/Ns‰'ˮBm e(%KaL$Z҃ qm &PJM M0@i"^MxDhNR is &,kaGN#)\"_C>Sj\}o 6yoq >7(N_4a:z_եUM!T> !HAiF%U 444d_f(/;ry֮['= t؍_ϊeӧU2i2*׭!6"#0IusTXKM G>'>NQajvߗ>CZ8_~ʺ:*+7}X\}PӣgOk}֬Y)-+j*{|pR\M+Щ~WWPҩ CCF6{xtir*WSZ\J]va|gl^_Mi3OY"RX!a B 9RS_/^Kf' ΐ*p[r i߱#'hߩŝ;RYtFgS6G}ӭ*ٵ.Tt뎨l ѫwOuR9 J'rA6O!)-鄐%=:| S_.mP^qذb3A'>4dpRE f(..\ry4Exi|:O')WHm#ИPX9 RTP Taɧ.mB6M߭ q(ϔ&XgɨCbͻu^ِXv-|>TM!r՜Bጒe:2t?t@s%S :%Daj9v:5 Sp=I_ 9:3F 9D+*'G8 GTQ.-2<:Jv r  Jx4 pg28^ /&2K ^ U1D ~q6l J`Iȿ B/Mi}T*E=@ !]j)}FyH $-p/H_Nk7֔v$9RgcvR6gke|>KUʌQCYyyLGuUfpFUkn>2h40tо :},VWW0zXN?,9 N%5GB?Be᦬nxH EtL AJ[ M"(@KnCxi{ ߈ߘ-(4fq !1 \+[1+!NPPS,)j}MC5#QlξHhB:"Y8$ D" Fj")N=F86^: 724Wfꁩ Njlt 7yǨ slH`ضu/Ԅ))H8ywljjj(r̙r9FM(.)]buHXq})<M$ҐV7#џk4ٱ)*-r,3ȗ P9Úg;C8СCV;d'tVy Z=?2n4w%H A $7DlnqCBNֺ֞J ʃ%VQЌ(H(YI8g%R|2C|{aS&"82cF!#6Uq w_#=:BdPd&NpaGݚ6Cӈ>M!W9SL-~?SaylF$iPYD}' k$7Zrik I e̗(b%ކRh A $H;RꊔR&TQ.yB-IdI[%J 4%6W@VQd]"mgY^CfuGfZQyđڌ8ycvHӯWw|1ە@Jv;:M)Yn(忾ȸg=9a87#pBΝxCۗʯolcSlӦML 4Ɣ+.gʕv4D !SPcU7Hя1 vqa}>cFnaWN9psYrK. #?}r?K3ʂ w/_s{Y?9]HSf9Mc̝jCF6bW/#k+T$QA.*DdKⲚ9ǑƃϐC_\KII <K/ObyٓI_纜sy] :Qk2+5Py $$]&Ь0̤]sis$R#}ovzua8lH\$H A {b /eU9:4ڥꓡJRyOƐ18Tپk2U)eN$1aEBB%ۯ?gsC;GI20L2@0#HR @ڌ2PڵS*ͅ΋>7>BT▬~ \' zu>ۛŋbill䓏?栃˨QG޻"k~sVX!䗿%Ay}Ե`^cAshO(GNAS92y3 r Ϧa"r=-rհXS[_|QXh]v'x"W_}5w}=fժUwqp 7b ~Q__K/Ĉ#(,,[nafȑ .d<-6}Q;8 u]ުKMoȑ#9yᇹ3駟f\yL>I&6E[ƶvxg?c}eԩ1j(VZj[aT_NR*g>/1QC;#s;Nr颣d/G|ɳLr1='֭okm\wY$ySq.i&pjչ>C9LT%A}sH^gn:!qnmk:9.7J<&a%*Ӑe5ː:eR0 b`ƙ^'j(!M=|'.F}2uT;8v\_L~o0f +!1yG8C4h9f⠃j֏ذaW^y%_?~<Ǐ'ɴiӸ袋ڵ+ÇgҤIӇ'2bڷo@v3f ckq~Z[[;v,?Oe]8p =3f3&M0g FK`n>pl.z&Om:_Ypo-4<@ʁW&9p'gJY}.\m;F>鈀ihTqP|0Q_S2`j<57`Ej3r.g6M.d^}Uwn Yn]Z3gu]9{BV^O?m7`;AZ[[-2׬m1 nW۸P̃:_PUdE>hU\k$mGkmG9a;TYйT/ձﮮ1p#uliᖕ֖vXzI ϶DWcɗj<ɳ}?ۋ-2tرI;0kdɗ+ن8UkQTX[@"VUXB*-R?&P(a޺y?D{i>!942NN8$"Km:0ފohh`mj-m,[!C0k,x-Z '^Hii)7nž_XXmaS\=أMlilL_|7nd}ms{TKVKڧ*kTIzPQ; >^ڶF2AU: >K7CRuκfcrЩvdbeךiG5;8$k5+k=`TK`mKH[ƿlXz5?0{,zjMເ-É\jq=Ul513nRf̥! Xs9~MI)UYԡPD D }rɥ݆I/BٙkP ,y81y- g+,RR^ZʝwPذ& 3v~vs<{#63_)1f8"*3G tT@H3EDI#W IDAT3!{8XC"*bɘsdžJ]>@zBb5:ܠp9uZ,J-w宻bƌ֪RQQ"khf-mtn/h/+0Zm]k:on vT+ylαO|W|a.zkvsg{kW'Nd<g㏫4 X8 Nよ;Jv]_W u 0c!Ax&OcM:Wi.em YѶU?LRTi I,鐾+sc^!(%8'?g BG2 HM¬'r"aA>M0ڨ&:%@Й"f([nl/z]!)9}"JH_P.v8  TA.AcVS@H5;nzj.b.bF `: ӠB;ZtqL2{w^zݶQVVaoRr1pRVV`Ck ֬YCΝceY:w$wС|G+WңG훴 p /[oِ!pYVr[Yt|cY͙3q8dOmͶi6LOGAe:iX_aJPuHu]7f^(_p]8MHۦټeOo ɚo5?=G&8@~_ɳ}>?jjj9sYn_|q[(AZf6Á`K"@A'fcXS(2c0œ1~>q͋^@Xe~7FpEb.مMr\z)v0ɘqӔ"v}KPD'dT?CG #Vb@E)Z !sQ+>F-TBu=cAyK ҄pFJsMڎ.@{x)u! 򍐏LynȒ o6mϦSNr-6o}㕐dͷo*ɂ%u ݡ>U;<۷=JQQQGOS~_0i$ \ 'R '(װ~6H64v:̴/WID|np\%2NXG?(&* yp=fA8'a}f6ejL0ʔ M6%ÒȜ4dK0P=_ i ˋcl_$LͶCKc ZLT1;t.f OW9Ά5k%tHlZT( TUUqRUU<Cpi[KM6OĒ,X'Ș1cY󶡵6>`}ϟoYt)uƄ bu\'Ço{gy٧L֖v^xN9ƍf{`R'<_I^GKP?ڣLг=zL?F{\<7_+ѻk' KTIE N g=XS_<>?!.Ot?C5W^k~@>M dw<׳nfa:w6wM໊f˯`ʈQ/F F؆ T|$) ʐ* jZ[8&TRE*Fc 3ĤPq\:5k"tٶG B,1G9̊9GD]B\W"v"=_DiȠMi 5ԝ \m%z FN!.m?iH+xnS8q">,^{-L pBٳ'{믿&nv***ꪫW^aРATWW.孶ѥKHnX`?/~azgI׮]Yf ^{-{.ƍ#s7s 7p9ЧO .\ȢE}[mk< 6N8 ktڵYFS]KwҹTޢEp`/Js7HWñ:ǡW1{K3|}AfGڃ?UkXVppo|v+ z3pa.[0n˻wI0v}; ~8e{> (rq.8, X+`Жm]):2̥> /8wx5rf-Yow+ ]Ne`'ஂISf,n^H%\Ÿ'Xb> vwqC ٮ AkXzѣ$r-pŐZlyvcM׶HʨBiGEչޯ㪜)F霭0/ j)Fdd6X3iW;ooXAhGŰNmu=z1'#aΚ60Zɣiڬ8V>_x9&TqE*Ykf cC۵v y)u\ ( -0 Yö!-ꫯfԩ,^;3~>+†h\s5z-^cuYnO>)jz!y|'? vF'w},_SN9?T*ŋu][.ŋ[|-cZgɒ%-^㫯j;GSt8yX˛y+%Raң`zɯ3ozNp 9>:|j烙S>|w YmxRx[__t7V7^m5o݊.{tt/TJ^9 b[yS$l'dټ[ 4::*f 9sСCmhpO2@W>4H/[n?K* p.ch I  u+J"JZb_,)_%lqhtB|uHtA8)6!B: EU@ ;W%Z2Z#NTj"S*S\Vȥ yH)mɪv`4y{&G.=ǎ6 1Η&ʪ1DHY3^Ĕy6QB x!MV00B{z̅a{hOa{;7K 6%ۂ b6rAH!!e\Ou SiZPԫ 絚^nCSʉ 9_?Z]0C)%B+(#46g+BWEDOjbhn몼!*]L""}B+GH(WE'7Ehh1 13Lk&4(a&˴)"Wʹ7[P 'T%&pTZ3025oS-A $H A [5{E6@JЦ AyEDX6լcCj㸑3MΤԩ@oY ԣ&CJLjP""Z!B;,߆(NZ53G;MT yEhT ĆAZ*:*NƈB]ǵXk~peH 1 ml )45Z3DύP1[>ʪs%fb!nJ A $H N p90j {ApA`kbR'E2Q9±D"J?/T< =aWLrjXA׍@8駙 :EL-86*Jdmc?OP&:LSavԱ0?B-PH TF 1\o&C2q9eMx7iOuk%H A $HsE,D0F.BbUfA͸)KZ:(7}C$GMk|z(mN&( ;^Bbs *_4l5AMq^*WDLODHl~XjtpBK҄)vcQA^cw$$-6QfvQϼn2k!"RjΨ A4Ъ[x͠=FZhACdy`x̯G)K $H A; ZV,n"z]*`Jq 6kH+i~>Z(e"gRDr~ k"BJ*o C#q-;yXbd!ZژHHT3̷19ȗ 9k2‰P@7"U>5 kڜC*J&I֥ȼ1g76ް@s/ vr&l1\ߨˎ A $H AKbyQd~%EsLeag EB|%0&&TNnUD~k ob @F+ͿAY(쳭7d؂A>GB&:\'EԢ!Y>K )Ӧ&|mu)$#mB"͍!SRJp" vC I7jre6b'/҄ר!rg5&<7&1 $H A vLLx!:-*Ȩ.9Gh2Ԣ0C󭫃jǐ9fEF9w=2ḩb"],e]04u=վo,u$22ul$ДxA.ϩc aY'%6$QDmݺ FUh.]Z58U)lN1 4YVVD 2ŠeQ0jS26."serX cC.#C-g $H A @l!| +P5J 1%N2%~ k=O=Qh6BsYȹA$Iŝ]/ΈJK ~.BtZyX裱7c!:\X?'N#i:C֧ k`hsQІ :Zrb#bbup8^Z*ߏxҐ_MeT.L/|ƈr $H A;-:DXiwq7 1UJU|#N*ml#0?Vɂ&*LJaRax^ZQ1ݴA>BV ;[hHCLaU*BGAP&.FZ!AT̉g}PJ}-ݶVdG >E5iF!tLWC*i-0u1sQˬ¥eӰƗ68O3&[ٜQaLahӮ@ $H A;ZA$ܸ9J@^ reįihؘ!dh([ ̴.sK4F} 1j7 X ӖYN*,݄c]ەڼ$tn{ ۷hA0,Qb^Z1$w*LŐ$ӎ:t/bntf\ua&%&V8"PLDfU0~y&\ԍ+jPĿ $H A vHD4!*h Y p1"ٔČ9F9 95qF~ e22dQ ՝"MCDoD-= mٞ w[QLV;YnEDa +:J?oYV"m*߸ڍcrلqfyEDVD2#q}a20c|oT/eM5thHgwɈz2dB#DSDF$V-TZxE4bّcQ/}p\eDdK ƸE12#&-a6D{)Y q څ!"*2ar9%9JQ(CUmI"8P'SQ.C5ZaTJי.G sHRِEWk>R5(jE)pA/L :]ӊ(~r"*d#`E`rzs&w.)lG Coo7n/gڴi߿>3838ロ/k !(++}ߟaÆ1g>hΝ[o΢Exg8c֭[ǥ^e]ƴiRJM_E]@׮]>|8&MO>[!CxW8C8c>}:<zhc( 8xcEO.)O*~[%5 >~Lr5OZ&v_YU+YQ^Q.#wPrX?Ep+>}S=~^x݁pYF[pP|0|[S2`ꭏΆu [Z6J/ _L]7 3osx;Vڳm ɳ=A6* 4a&?d>slKZ0vKu=4XKĈ:?WAJ:<0Z/ *Iq֯p#:Bʨ6{8|F|Ic$p(ZM6ȃ; : >TC"xuǓ?H_p+~ڙakkHb˄5lY’/hپg얐<$eDk8q>qQ:w)akƜuLOVpK}O@1˜?dA!WK` rQRtUDthzA8aT;qSi|.ҭckiW$/Fhyi]/E aA J7Qڬg >Ji˰Ɨ# Y(#sZRi!pf:ZC(ҪЮX;}T5@9[ ՎAee%~;ӟ𼭇d2Lu]!c=Q__G}ŋ;馛l8d߾}9쳹ku]yy/2O>$.koܸ Zaa!d͚5k׎;3~xF79رcӧso^`P_g>o%5s!L>峯4`ԀcU@N~Ow);eOW罽J鲀ɇEz^=~J዇9,Vo˳}kح!y'HD;BC*l M{"{aھ\ tOF]jyn;"J:-^J5sytPtH?[Pd/Mnk%еEϏ7҉'^mT?w'2/|0<͢E+0`k׮.㷿-K횚@aK.6aРAqL2*^}U>X z!6m&pgUczKg_|u5?Yo<7t]w=[{n ɳ=A&hM֚2!F0gGBYb ! OQKsn-`LhUl)iu2u0ra,ͨ+!{ ]&U.f||?׈B7G ɖUڴj[fhC|s?/E'}Mڴ$MhHh%raV;CCa_-Q&u|>\w!}`q73fL,LdDN?.2?xΝˬYXjVb֬Yl޼նZA߾}d2\/aÆ;kq90c >}Yz)Not:Mnݘ5k1͛g]2L?k֬s? N?t~лwfc={6SL;W^`wK*v0'xxw aʀvq8s_B()3u<ޮ_UDfN@ya$@$Ѡ@ `2[g+Bw;j+ *&!aLb{Tw:>@`^{!{ϩSͳVkc cM_KmApĖɱ=\+8+aMsĆ"g#c"=Ƕcc:ϾyC.W">$}o+}63?h{{k{gq,Y wq:(tݿw>cwFDik)RvdBx "in [cI!!ɑ*5/D$WqfUЉx:soaGp֧,x)4j,zAauPVC䯀Cz^.j` "O}CTJvlLb~^nεgSvcWX>_ۧ{& JR|NHO? ZT])f!RWUB cE,L&.=lyM$/B\UW]%;ĉqwϫ*n>q ̟? qo\1c`uG+70yd8ñF<@xqCy:uj1Gٳg˿]H}}a֒מz),X+R_ByI';%ubl _ᎅ%O%+kĞim]+]+aIcϥ;np̴ ??m^r\/kN0}KU 0o x`w]佷lrMk#6V v6\a׵_H ˳[P+cVΞ(+"&N|#e]$o~SN9~ a0_\wuxH ]S4&aB"(A,zܰ8BJE#F̥Anɓ$!oO>Cѿy7w.&O ;RO} ._qcLn;:DYm 1UHchLħ%wtd"=Ͻn>V)bEλDSϱ;*;A|82?BMQU){9GeWYՊTkR}}~<l{Y}$s4nm`ѢExz)kXc7 =)Z'Oo:nwixx/fmdo̘1zTLV[ᗿ%rK!oӦM`ҤInO?vM8'O[lx0c L>~zOcxxkf… y</k z?9TC+W#~|GT;uȷ=m;wǞ.5Lqa"S]R.>m!qVU$%7y9R5RüscJue+~^|E,\q_Fd3D8401dKbLvĺ8B "t| @K`Cя.5\XkqWƁ3Ĝ9sd7l'|/&oǛfs99swgC=_XkM_}᥏{{L1Pw>Hո?DOuޱc=6#L|}R%7]/޻Kp0uS"zVᬷDM ,g'O_>7xڵbZ/_c* H̸pOQgwn1k sqp56^Kk7y6lz?(ࡧ"6Xa<~d=3/~9s&6d7E];y5a W/˖q$LKoE5D<ciMT*\W(**yWwrz^cN$pa c"ƈǟЩQ)OO5"vYqcgtiXEC{JHV8,o'WR V\wG`NGR,`!z1<;K~;P7GI ,u&]h0{9+0v}ӧH؏mᆸq饗G?~_cԩ_8N;^x!.b?3x+V'Mn \p}Q~'p:,:}cxӛބs9K,3f(ƽ˱*x18}M뮻.O.E3f*j({WgO0@gl_Zê;8.޹6;q揺 xn8pS?%/0Pl1a}{ 8㺴9m{pw{Z{"`A?._#j\XG{++!b<ㅡ8g^#V1NÄ pM73:묃8CEG~c 6 G dZ&q8Z_m?SkzuljdRu-!Or3̟C= cƌYg}g!\ob7&OI>p.Y!wO9};Xay|gI|[Ɣv, ʫ}3g|0:MW8 ?*l/Y3q뼹{կ{_<w~ZkmIlv~~7pΡ ^x!ww菏c˭}&LL*Qi]K$[RrJ73cUOpHa NHb: %@}\&\Ez@_ED,7P; v-'t^G?^gn0 w}M8-xs,9%iY#'4&jhXʐ /Jsl "naG/.M, aĉxm&e ,7%Nav,8xM >{e4\/_<!DL+{-=xu&!Wu<.BjyϘq?ifw,^u"t 3? &/x[[~J`Oquj]yEbφBC'Ud KZ!<| `0 }-lv.ErΩy2k""\$=tr=)&撕D"B3,}ވ}׻q/c"_Hťc,!!=RY$\(?vڇ1v+:G`m&f+.[g- ܜy~ۅcaڮoO<K,^kA(>鳱{Xy3? `-T0)FnW]y>pf!s,s[큾QN "a,Qp9pU$HM!+i1d:+VB=#+H Cjm0 `0WTE֮axHU 9ҁ^kaQq"B҆^)pʍ!R|q3f9{މ{YflKğ0<Ž?Sv +0o{icM7ހqcAo/q/}9̛7{.YqC*\e%=ԓ ,~wy`'U-Fm=#O%'G*V*2$;fhHDqP+^JM;+, =.׭"א-`0 D_&%iͶ@E$4aISȹJGIdWRpTD#VU-*TgA'U=}h000w.8èe{% V<VSY'ZH'bψqM2(5뮻ν7&LigLi'8pus0oI^xThhD |E b&M ˭0VsDHB"{eK… q`ƤS080͛.31F$%k5;λ삕WZ [o37\?wy'[&_ >5NaSm]oBč_uǍZkKp]?x!KǓO=q]w;G7 Mx0Ͱ$םeoߒ q{_@hm,e,i'!t>BFF>RYsHcF9;| C++Oy>Ob~?ulÎ% cpw7.Lg?Sw])NjhE#r}9̹}Qg}Oz7~=̹,\_K'kԸ?77|Kz矇:,}G_GN7Vnc'.JUdJ7VdǧKjLj59ܿ>]i:c3x-`0V"Bi#$)cΕI]h]GUi n"^Lso)my)!t)nu^ܢ3*w|3xN\{lE|qOŚk/̹G߂Ʈ zaʫ`m&$EO +1?6W.sN:e6nva``߽rm1~xYUV]l)vqL^nY~+(j%?Q#rg@nj=1 rk`x# g>a`0oB2džLtȆ&Jb+S6BQ 2V") !S~Ta%WIw͘;w-5M'OI&>UYt<|gHpIpH-}:Xt|7$D٨N䫺xNlQS=[?˚Ԭ4Ǭ 35n f?EwqlaqΨ_eAĞk Aׂj,aRLӛxI6 IDATL̘8p|B" >+DF;iBkL2&OkMk^԰9Bl>I]blw%#e_4 0.N:OPyjN-<ykQ0HK$ `;w%|YsD$;iZgl)L^F"鸸 H(e'0kGilC>8zM6G'^gn0 ^Ij]8&(*LRXҐ rVM|qb}ɚ(H"6$\'"4@$RbHJSb01TV>fr{dQ|աV}1!3-=7L YR#O](+&͹N+ib9(X#jn7yPWYsB5NE .V?w#^^ֳuR~{쿹M7G`0^ L%V/XX0ޘ3&EY  4y?+& \QZR5B WUDEE>bJNz{Q^t!fe,O QP zRʈpu3yϬsR\L4jL 9szOVPW@ u':&y #)5 `02'`Hf(+1'jpNiU"bs!K_'e5X8!5p^ST?'%˱e d^8/&Q֌!=@ *ɒqQfHMDo$ gqA-{`0 їDŽt`A?g$;k)BG5MmKRrG;6N OvGn 0@Rn-Rb'N-[5\׉=HRoK9"gNM3Tİ\2Y,?ݓJ|l;{cO~lo_`0 Q0w8+M!9#ȑAjg tzȵ]LOf]ZG7TFܗ!%]4&uzOװM `0FF `7C`K!ouB@:C!T3T&lH2j+f4Koܣ"MO&_>Ӳ"fK׷ CAY9%'%$AL3U#4..eUyL~ HZTϬJWK(KHwCjMSDY)E7n/ŸLMeF`0 фB8 (b [g*Uപ\v LH :4Őu-$4$&-AsYkjqWY)W0gT\w^H%%D!R,Hrzҵkzk+`ZrB bD"aNgt( g/y_ג `0 0:0B#fQĤլ' ʇ-DR`VTFEX4BT~ChrMjp ^.9JC@ 'D@CNRsje*ׄAݗKkX,X"^TLSe2.y}IG61n1RlshtIT5sAC`0 a4Հ%L x-jE}AInz־hrl1j QfTG"4M7]%_CȆk?_^S*udbktX^.YW|_ Tn2Y) kLr>5Wd"W$}g ݳ~{IkN"N7{6e0 `]xEMs!HQ;1&TPiX Is4|PCFsE?)#<e2$ U*Zĵl1J} 7Ȅ  KZHQuHVV٦JZ[V|1 0]3[ ]a+ͦu4z.ŢIOg*IhjYC?VA׏"ڗ `0 /liEUblobkԱyR$BMLhK1FF!bijFȲ Q]}Cr!1sKLjŒ刑lkW(J`|~3#> GWuWMvO&M#D3J$9&=,HU `0 wؗ_V0ݸBeA1Q"ާbdc.w=B3 i)etS}̧ns<_?Q -8"ۥDD" TS勱|r*B,s*"zlAaQ"ɒ5R\~~=+3ʎh0 `0,{o̤WÓ-mJ׊/uylDDbld2WG&@8&ǬB-riU- "BGޔbǷCBM%2Ӂulh)[<1ם>d$A-HLh .\ -4+.dl uʖD϶Sf/*e.[3%N1h^\I_#"lwɖYuN `0 сbB!`佴SvnW[<4eGsLb1Bb@z뭏 _W/}7p=qoDF͍o'cׯ /H.Ra[^{4Z(+>6 )R^/}n?NVHXe|>Ni#x񋇡p&P<}rpiJ5|/|L,;b6`2`0 00r mx}INQ0]`lzniθT:ñ% 0JM?+2Ə_&M7Ù`jA&:84.xǡ#}K +42o+3"M/Vj&WGT$%!5ax_A \Y`nҗCoWDD!r,ɶM 'u^0zp`0 aT%B8*mg$H"+MXOuKbS2ule`LcԤv=i}<#8kfy̞SI*fK`Wo9bhX]"  F"l|[ u8A(6knN穟bB*|\RC";~+p1:<2 ui}R}R$AhjY]{͂}Bjؤ2uשyVBgA`0 ` VR-քXVB i#ˑ$M_|plqos! 6kFVPUi!&n83k144^9"3V84 8oÛw>):?? fr2&No~8唓kᑇyGcߏ;MO|cxRi?=0 j=dR:$qRȪwBye)VCVc PR\DLl_/g$0`0 (C0 uA6!rKz& h՛IYw?Ev9]]Tu'H O<n738_(m /<<W^q9 Ĉ|u000>Yђ2SN:͹;hu1ͯ?59@.b X`>s7{pa%RS~pr!= _z而`# EuS?D~]ݡzj"B)IXkw9"KϾݯ`0 `XoA 0cz!u8\z%WA Tׄ8uc''Щ>oiKg\͋J+g,5F9"i4\Ao{;¹{-l0w-8_WrZ*aIdr x{ 选QN?#tß\UW]p٢*Ɨ4H~fZ~>1T'. Eys6qcfk_R'1"F1tBl? gHK13`0 a`DI(Q #5҆^>o](KRֻtP JRLM&x4W*be8HLzM]s>H}*}.bۉq?VZi%Lu7|hbNx0oi'K$7Rs]y]j%ՐHsVЋbͫ;Y #='H"-UB )T!qL2|]fe hSK`YMBɳR0`0 (B_Ƥ'4BB]wj@Ty+HX6Gzn*T2F%H-Z[]w# l!.\PuQt}9UHJ"$3zj\zwqoEUUz⪴)axn.sq9.EhMpU;_|_U _wY4T(j1Y(HLőm'ݼVUEq1O+| DH$->' `0 #(`'r mAE+.[Q}e7k$5d35,XUlk[l%6h#{aYff"ejxB`/~>nZ%K`׽G}5s/16*>bpp rK:o."JC/x(*AEMG͞CFZ{1rh_թv[)܄UR&g]ƪ!) `0FF?ϒ0% mKa 5J1x-ˑ#nX8;V\qEqđXp\3?3,:E{+nE`-({jҤ8SNƪ^y%{Xn倘f@Ln"l'TG?~wtKq)`AoC^Xf9 ꦜ~O&nB=AY*#]@k$0x 458Q"s/0WU^O)ɷB0`0 }%%GeGKmc&sYڊ"\/mdrM+<,,X ``/1c{-UPT"f~s{p̱G\_ztl7Ə pn1>/_ ɏ0vp{ދSf(pO ]eJs[JG燀Ȏ"ihV;—K爊! ٣(d .ʚq# g \31J%Dڛ``0 0 ϠC d `VJ(kR…\g Ouf4)AKGFH+?}%nj-2YT G-;PHgdޣ$k1TT]8&r{Z3P,4xv|$Z IDATs[pN5Uv#4å7b@ WϊXp)E}P)%5+֊?(b`KL\k`0 `0R}#7qD,Zkϩ_t$_;R(; Ē @J?*B}80PIFFFmD3`EL`M9ɧstʠzEVWH"g9ept\%2.A.>]I5f1Sd:I dh"؃Mͳf2 cM͸)2^S!DRRch\u-%pJ|l({?W>7ՎMӚ`0 lџ9qFDlO5uwbŹd>V,8Ev:ԴT0ĵʦ}.H9#>w.yWk#QM2o$EvJ 7y>6d9TjWǴ1IKjZCv(-Ɗ{Yl3u`i,憎6FZo_չ\7~(α5yE MRV `0 eh&E8_עޑA@ Iu(.$5='$BҦݹ>ы|RNqrڣ"<.kUbfL`YeRݱ/dO4~3PJ` uJMT%Ee&xb޹df^v$" V.2iO ؐE$O="80<Ȋ)ջL#Z `0 рLG·ؐjj*Vt@9Vy#PCT6ԮPI M.lYc> NY,61C۪w2SZ颌I'qJ#+7e2Iљ\C./ viҚ[(ky+B.[B *=T],]DD=':V!};jAZ<=$P\”mt-/? &T! :RAe6IltOH^gBd& [\1!kTI0p?.[/`0 a٠ YT#~H9UM});4]"ݡD0DBldwr*nDڪ(q21QQKTTߣMb&Nȕ_Ycvz|j>J.ARU3.du3np nJ+!/+ `0 #րڮ7$e#HDԳX7RnBb"ŦA9k2XNB Պׇ]=}{ML@*TPAaT@,mB)IT4::>5Ch< QFUsj|]=s`0 a4*+>.,sk{|͑U- YHqԌ>8|i{3,~BJR")ɯ *8DORJ^dFH[-r|0jъ5$UUW.:5]Y#jSn[;]A6K&PNjb#C!4WĖL"y `0 a z>xZ/ʪfߓދe'=()qLr+LesiRzeh~W~=K}E"U-FtqtGEQRȖ%u %+>*vϊ`0 їPUiT0 ԗ)%ܥ؎1G)Zd9*WDt#eKt%ۚjG #I;d6EV@G=)NL|鍙}(JɊ0V1t]Y4$Ejj95T籣X-.:?t|V[50`0 A4 ;DCH|&^@Lk,E/P՘JIq .ND28J|3 NUUf'UT|DBsշrFh((1L׫3&0_Y0aOxe{z= E(%u)5!rMVbHcHg`0 atBmxy.շo"a~9V@*~s^M8O{JAf`NR*n-m/JR-bT5KHN@LJ)Nbt s#Θ}rdyJ3j` |gl*Re`E*ܢDR3׮gQ?[BkU A `0 т"bנM(7=E؀!#ԗkpIuR$))2 CeМ۵J9,7C0Xq"4 CH";DsoMs|:\@6tzX[?7cP">Yp /cҝ'Mn g'6\ (|-l`0 00s< iDd)%5`1GK]JgIwC>[n }7u/*SaH́@q2Dec#WUu _U;uDuLn>E{_u\UG]܃3縆,+S,4nQÔC#ruNI !\EFD~/B)WV*D َI/QA3ָ'&MTM K `0 } XFᬄ%ϙ\[6g5@>^01hBsN9XR|8aĤ,5N$̉Ր␚>DD8ă$"́ɪъmDRa Z]%t,8QwPAUqZɄZ|PAT@FhhCvPK2?&#ЖLG5vLˀR.UX)",WOv(+L `0 ф}^]'p_TΚϝs.v}!59\?g>x,s#ͳsbqȂǷy1~ç+=n~[.@8/ФZB@d+|VyG<!PZ7_ rF2 뱼c%KʶQu׬ia %7VL?ޡU/!zvP^tN UGU<7$1bF+# H`0 a%6) <%q=9'̚믻Hpݜkf~SNR El,}37G"izo)bs\d Nq v-g'$ l0~]gwCOƶ[m::O/^Om s7I߆ k<M{+կJ[{ |?>A$ Ưf? <``qO#"_܋wv(b3ʅ=wߍO: [n).[I+Yrއ8Dhݺ2پ(UYzbGI`Ho-5hyfEzj0 `0XVJ5`1b=B$pp 瞇i.iA,O-^'x&OZkH~^u%| z;qۭ[ŋh/GGy{Mx>7-XS{>xpBwxc { y\sM|_1)Z%CF" -ODC9 ǯUVYg~,鳰ر{p<;#/qX0>| L0AyqXI5]{NRJKH~^꺐 $lrZh4Jǁ67 `09ZcL}JxT8Gc;K4 `Gk?W_jhS=ds"9x 'n>Le :8?[;@6x]SN0sXwupYƂo88y9s"Yo8/=;*)1iq %IW>0|ۗi8C5koq|'ƈ /298lo|[\R[+3IVų-|]|||i?\s?%CADd-Y#JB"W:dՅ3Ș`0 a`FҘ* ȍnCDDn{쉣 |Uaiv|Q:a4Fڠ?⧄%]H1~mXs0qhB<ƭ6lsvۭNM7ۜ6!XWO=h7 Y}EEra„m͋|EP"I9@&!l=k@ŲߟbIN[zH@ȷ^j=>Q Ul$L+/߄x4#^`0 ч,YBjb$T0O:)T>X}uj41"2|!zyRr!ʑ.}9, xv3)~[q1 A'M: +K|h>6DMdʆe8jĦ2LkjbłiZB$'򚥉g®I$R^-D*쪌em# `0 уB)e)]"8s;z-D 9:~|k_C%zK4ah{E8ǯ}?aG$ꐯ*KHM̈́-7ߌM7 -VYu58xSr~Oqﮪ008ĉX0>f&Mɓ`Ҥɘ4irײӹ)8E{TA p>|:,vEY 37"TDKb^{u3O?+boePФiSLxu/0v+p8a2*f"`AAD9" 63 YsSFRqKHXb 9BBՎ6)|+͞eO,%a{`~5Q(a$3A(=V#dCq/ xD&:_ SBeLO_yz@ p޻\"1N&抮y8s3;4yf=M2%HAAG2E7D)kjFEpՂ 3Wha l3b3z/ą]1όU )lJ>MK@;7# 6$q>ۋh!9X(1'`KxoOĉ'u&Bb%:8w8HF]W*ʖ_~^i c W9F>_"  )k`b<4i[]s]g̽ZT@E}ٱ1UYDZz0~`ƠX@%2&iW uq~(`&ug3s#t nnz S$=P.@0wn+~LwaV¼+=-B]W¯L5N:>[fO  FrƜWjv'T嘨kTM<Εsy^ĤŽ&>_ 4.0<$R F1B3(tKĨ딴ȓJVtFce/TG e"6JI̭בij*V WRZ2^DuCFp٭3`Fƍ(# /  Tyܓ+'ϔݲci[/ @9zO{xܲpp0&n~b2rgBhk{+D1s݈Kby!6:J/߸Ds=+6NݜK`!YBsi{3IwP3h!-=ZMLps*%hIG풾wM#  rJF"ǥ+]rM#46ZWUi5X&.f1('dJTc.ЕH2 ιl7'<'(*:%MkfR>׆!J鹳:yNR[5+"̛q}"6TQ0t"R iGtψ5>1/66 Vz=&)s{HDAAQI*`@Z腹Elf@0e9θ6Dfo-uŭ & V_א:עO,fԺJ׀)#h"dA&%Q׫< GĠrҩR*gʙRD ͛puMt VdQRL>s'夗tH)HDO=Dt&!  rIrƹ.6 ߔLʢD9@Ҧ j8 /Yuu([8 #aa۳LGs$dIG+LW LdatqqbW |HΩV>Bss E W)n4ʙv7eQ.DD7*¥j ul=`^;}vtYTdgm7eMy!PQ!~fF KKMԇ!,L]S\u(>eΡy^$bDc{4\ P3a"dfrZʼn#7fˈVΕ0yG0MaȈ@6p~KK!#I_( FAARp:k( Z Kn ZrmCϑI|yĈO=ֳ(xXoDyB@h?P5AA喤Ly'UTsߤ3c2-RHfz!^(E#HDHdYYY QT\_vѤiSt옋 7 aC+L͗ V\{cJ j8<)v뙄rddiݺu8Us-6n Xw#œ*?@xRDOH%Sxx]GȘpm?>NNO55Ӧz/IS%|AAD9#y ăLy D8auIFP6t#LO,@:;D M{p13Af=3LkMbʢ^յR8s%P$y HBɈ3LAV.NGx6ZAAq$Inas&1fj]QyZsp& *hLDSw5\YꫴBHHGNѹsu(AV=33fYq[@N+BY̱OaqFy$R=IftG-  <^Q+1]w9TN E2\>PN}xLA]-ϟ2c}9:y½O82z*tݩ.k{ic ;L!Ο799}6m"`ѢE𢋑`֭ -Xk eM۶c fǶm۰xB#D2220mLwq7o[ -2Ǝ~}-Nf͛3+{_mZc#?s1QZ5|9<+*T%-F]B#]ŕqdMCo>! WCG}&R &%Q4iN"0  <33K" =! !qSX6R j53)"XM¼ysqSόFin\b@75VPYY):!m)>p.WT-Z`J<תӱp=ñhBv8ډwv+WAI_EIq1"(-㪙SmO>FN͛++Pn4nK?^!CG3~,c>nN--‰'AAAڶkgg∁p Rx4JCa˄u[4s mEM5t빴ܓ"߄'[(  (O$`z1lPS IDAT0xm!k#xlFU#G@"& q~ґ>WBs zU<` ;Q(,(@ժU1r l*d7ptMG㡇1 -[?F~;/0zDpAJo>D"s,CZ-52BL* EKTR ~[ओOFfgƍ p5<[ ?LG ~i)HD$1h&M%UkB V4Bͻ߷u:םAAQHLc2`x x:)/8ِsɧA9Ɛ;{sn~vK% (,,Gƍ =;&UQEm tjgb޽86FxvݻWFNS'|@ZZ:N9T,_T{1hѢ%v؎/V6[l݊kנEv9l'Xaڵkh߾-ZKlT\و˖!Ó V ֯3n~LւZ {5)^7\o0`:ӓ a52u 0|:7AAAG H{ϡ`!7uѱ-׋fƪܤq#:v̕uX h!l^yt,^sUV]||#DuC-SNBkhҤ)U+lLxuiσSuQv6l۶ gnj:p:?߂3kÓ@Qb%u=jrr:`waGM۶h![lG9J/z]@ض}=|#\޵3nrIStm3_\ŝCm2<'8 #  )jUbS7bC0nz鄁@+LL=XBݎYp(;qB[6u !#=ݻ_RuN됛^x^m99XG8 AC) u2AcF㙧͛1c1膁+Vi3fGƛoXZ7spqAv`ysPrehLvi]64mj4-["-jի׫3qnv KҜ>}tXlgD1'jl\ $Š"`#kdEOAAFѨm=`I:~isDXx9}:*J1C5PBc+a,xK8Fն%&87p&ũJ/;܍O(EH>W}7.C=1edy> /&v:>ƕ4U M C?J9n83%9/XfE 'xs  H-[?$lU֭oڵ^y JJKjNzqo1],.5O8GSkb4)虎L&"cn#cI$l9r =΍,=n{tOݱ̼٦A'JiQԽlCe-dPNE^sZ ͇#M͛[f&%03W];qM"Z#  ~#uAZL+VZSA_+u "JH\kp*60k~_ d0nIosuCm$!bʍ3h{2Qs;@K8cdqc p_2G&ĥ(QP{2x4cEݽ b8WL J<hZ|;T#CI+ ^ܵu$Q qI8   $)qs~bD1Ƙqqע,\Sek|l4)*e)󈄌>F L;B 䘇$):sNBHg@䉰[REJ'!1VMKN^ËDjB7PB,Fں~vTr/ %X,PJfcJuQɤ^$6$GAAđ!I{֔!9F׈^_rUJ3TǐAcOۋ+!5f l8T=OERaeRgΐL^ρrxDx߇iं1Y'Ϻ8l^`B+S hkl1Ys'O*~ёhuX"ӏاߘ)yKx{bBAACInj0BzQTD` lʝ+`a s]7Qw ߊ-S&θ$G\)XsB^U"|$70;CFpε~V׆6pEiDd _Nˎ)CX\0}I!LSE6sq1睈p+  (/$`A; Ue2উ XB ¤56K(u$Ly א6-񢮧"L /.SҤ$:7qO;Pu'KaEŎqF1L7Ryj\CGÔ{^M„c\$+n!OAAGrLsZndN;5Dj.#v e6BKoF{ 09$c+BߤFSyp $#2d e:"tU[Z3P!c$)|2(#u~GxOEx@AAQ~HBYXid۞_qSIĈ1#l=sQwH1…si6m1GYs Ԥ9_H:7 "N$ 4˪ĸ,:b`E>BKla_Y6X_5W~~A2lS%Ʃmb   ܐ҄9)[%4-F- E:^Hc@Ts 9`#>J9١ٚ*m3i3T+7ᔷx-y^OxSc&$l (c΀M]~p]B5\0cI"a΅z9ISuo}%Km$  #E0Y)QZ:)jƴB#'Vv](Sfhq 8\gi2y#$ܝ=O Twa  Ȓب)B(3ZIKCo @AALH'DRd /\#hzV  <4˶"_$!b%7  (rV)#USH]  PFq5gHhKiB/  |k'o⨅q@$  )M88/(F>/  \4CM| 9=  <iLWh%MAAG)K6a,qt KAA# 0)~?3V\͛7c׮]*U^z8묳Pz##=rg}ŋaE6mڄ磨H ]/G>ϒ_| muGGeԀGcyfr)}^ ߧܟ3foV\ 6a_M >|b֬YGz Ê+k6m:tN:aʕ ݟ8,]Z 0`j<`@nė_1HMh\ڵ˸ UV56Xt)ׯ\dgg㣏>:1^{;v,,XFQFػw/JKK`1ݺuw܁+WݴiF /МۨQ#<۷7.2vmxwPRRrc=B@0[nȑ#.]_>jݻzjb1x/x!fΜ㬳!CW_<~޽;?| 4'pCq*A`Ŋ5jի_~9@oȐ!`.2ga8? ycGfΜoڵ`kM8^]p:t(V^B^e;7n֭[< ? x^ cDؑ$cO5kt邴4\tE2e!/DoVj guƏ#-- .ēO>L 2ժUC߾}}v?"-- < ޽{wy'q]wᢋ.¼y{6GqE믿m݆=z`!ڵkqgÍ> 38#F={pEa۶mIYf `РA֭j֬Ucۥ}ѣe˖о}~ {̙3װaRsAVVƎ:u 77wuoF¶9s<~x1l0 '' VXnݺM6Ν;~_H+@ff&FK/?nlݺ1x`+Tݻ'xbh;c ]tA߾}q'\-_u/ZX*"5k`ݺupꩧ,Y:`ӦMXtyjժGȑ#駟K/Evv6&D9J_ְaCݻK`BL2/oߎ. @VVQx1m4{r7oѣ1gdeeo߾ԩj9aĉ9s&mۆK/ s='x?[K.Evv~gYlz)X>Zi?TVΝ;o>DQD"իك,_2d~2C$I{ ƍ 7܀ ocĉXnrss1x`|شiv/ ,m݆>}W_ŦM0e,Z[nE=p-bŊ|8ڵkgѭ[7`;v,~i5 5k<,c<(?+)}rss.y ?<իEa…޽{ѻwoaРA} 6mdoѶm[t?6oތ[o%%%֭JKK1`,[ Æ 39v튽{'MUAt~/СCQBL4)al[.*T9[~=~gq rrr0h 13f(3}0s /ĭފ}bÆ x뭷кukD"yR|}`>q%,jqtDIk̾L0/+WNEEEZ*.rts/^ݻc׮](**2b۶mCfffߏzM(|xꩧ0wܤ+dffG8cC&MO?+W8SЪU+ 2$!F-['0)s<֭N'/0)dݻw7>е=\pg{).LkO bL~@s>yt\pAN; 7i77= ѵkWdgg#77{z!Cq83m6<Xf ƎkRbEiϓ&M*s>ѱcGk֬Aff&ƍsbIiժU[n%iȺut͛kqe.r+Oπă>b _P;sw1})޽{Sր101qYkSNŬYpHXל9so>\r%IRJh޼yhgu"//8h"|8M֪UB駟SLA*UЦMTPCƍQZZPtS͛^^=TV -k6=tСC~z̚5 +V4o4irݾ}; !CCIݻ7-Z?7pwn%K}1AcҤI(,,y cɒ%h۶mݗ^w \l?|#$ ʸq0c |,9Zl{:uqUWYf90dFO^aÆ]F˗GQQ|M ^{r%IDATj1g4ivZiEEEHOOGQQ6oތ]wOnjBrwyf]tA~sΔ+Wƺu`]| sNG ($`rqQ\н{w^@fРAB|7ϑK.$}L6 W]uUB]Jzz:z쉗^z 7|3ڵkw}7n]t  0K/SOE&MpbܸqVZ[n ղz8q'xmڴ7ߌ `Ŋ8+TVZVZ&jְaCo,?as9cΝ߿?jԨz( 91e,_SLAƍcᦛnBժUѱcDŽ9tI ۢ(U?ӧOtT5 [ɓ1x`L>^s?oӦMÕW^?& >VUbȐ!رcx_g"##YYY馛о}{lܸD" bw}駟? W\qZnI񿠲W_c/`!v ᳣XӔJyС:,Z f2UTAѳgϤi5իhnf|d믿nUPL経 <8 ֹ8S/E޽{qwbAnnn~oݺ5Zn (_u8p [o%{{B Fvv6233qwG-ϢJ*[~9G=K*SOaƍ#Lqq_iӦxcS,ٓr>f̘^za̘1&Upq Q\\h4O>~;ZjVZah֬;d1V_+WE5uO/hHjz"!8Z¾}/tjm߫]*dJNgFΝqqAڵk\?c97o6$(4˞W"_5u?Z*aV̵iθld[khի#''H㰰gl߾k׮E5PRRuaڵ9r^#8Gy)d,((0uT=}>S2˭%,))W\3<Ӥ(޽;222pe/FϞ=qf͚ꫯ/`.]`͚5G͚5qw]v߿?O>AfеkW\q0az聡CJ*3g6l[o5;w7nk?8zp ())qfȐ!xwlٲL>ݺuC-pI'6ڴisH3331{l_5s}:6l_E۶mѰaä}klڴYGjpgƏI&aРAƍx'w|Gqw~@&Mj*<#=z~K*TAzΰE!b߾}Xv-z!DZS6lW^yX <xq'bΜ9xpBݻwԩSѸqcZ_|ZfҥҥKA$F2ͺjh0+68G8%#WFX;`ܹXn4h>]t) !  3F/1|p1"mӦM?~<,Xݻw.M7݄jժW^y3g֭[Ѷm[#̚5 wu~iG;v`ر3g222о}{D~W 5kكK.7xIK0aƌ++ڵkkn޼O<͛-[b1b СC$jdlܸ 4m0aZܹ,m7l؀~gϞ;;7vkk֬Aǎ1ddeeFo&^xOJߎ|ϧEY^^͛FCΝMǺu0fxX,3gbĉXr%Zh~cǎIبQ0m4|Iqw ??9n35k͛W^Mil21p@ 4A`ƌx_zdx/FO`ʔ)xgPbE\~hܸ1ڷooݻq 7nA^Z:? h(L"geڵ^y"` !@F|0"5%%%8o0ׯ_0 8Pb'?nh D_Q^>С]0|pt77)u]xGI|AGʮ|gH눒6m~v[;wİaÒ%# .S [elcF5Uʾ'i w!Dh$혊#-S "AAq8)"2\FοJlAP""AAQHɘ\~1xsV(pG<   )l@hQqtG$n&NAAHn!8G4=b 83Oc܃  GREQq t \F"$GYaҾ6;= <G ߙ>"6L^\G('A3H)"fE ECNv̅6AAXZL9,)A%tJҦ^G pn]%ZcQpW JIS_Ƒ#q#t W`RF䩇V7'&6h:Dl_计h"LZ;-bМl P&R3iլߣ|w"59x]TC.58gGίn+,'EAAR1ԗI1K 1wsڟ   H)bEk*r HCRH'7o   (#)Ft$LSux,P'xdW|)%  #W%"MG4iD~H.0#   v_: uUX,*v^    ~_sŗ[A   BfrTL!,-AAAD&Wˌ lD"   vrZ*b   }A]υ%IENDB`showinfilemanager-1.1.6/.github/photomechanic-win.png000066400000000000000000000272231514476644400227300ustar00rootroot00000000000000PNG  IHDRCxsRGBgAMA a pHYsod.(IDATx^M#ٕ{\xKEaEv =YAm`l@Be7T>`6*HP% Ioc(?0XdiK{ܸ/)e*>?`o|Ns=@Pg>cf_We 8P@%hgm. ԧCkkk٦i@KYP{MgTsI֡4+@pmRצMIùݰnl[oǛg^0~o,[Lm}6u4uO N}{uiy)(q#n\smPFtBv<zduaIeIx 0|d)/v]#x>HU:29v%̅˽8A:H[KAF|bD՚i-]ؿhaexE!Woce6*ֿRY|\04{L"6apՈM/bgҰױfǤb\pԯ{pNf.GT7v+~).!:Ni&,tHMڐpʣ+qAJ)˸o9ME5+޾5W<c5F HqIh`Xfz,޴ij܏N,J{\D* H)X 8P@Ewղ@0g` @A@p("@p(,8|7r&%Iϲ p3 z3tyc;!zeZt`FtJȲJ2r2tg,2L ҽDpgG,#ywWy!}^RTYi.1  ګY6R'@C^ (iGW/SS =h$vUL"6AGp-Wbm&)_R8{u; }n MPp%=Cw4ܜS[gdn)p<#nXmRVm+ȰMeĒBV;$٫5h@_709p"@p("@p("JEz#`A6< Bv`gW܏#bcœ4+&֨AhL*bRR'pD7mݧ;Ϗx};umO~;)1(8+BFg#q*{? 5 ]~loi`d0~*%C$߾IE^|Dl~JYڥOҿ8I&҇Snn{ ߓ~l[H)̙FlB1MJD6 ;%~YqDSA7N07$x6Ҵe>B(SsͶSFNo~N[5y%j.E !+;#{ʾ0~.e^*MOv=K|nzI(mS pZD']='I[&hAnj$y_Fuf玞Cjb-ǭ-ڻ;: ns\x!{;:祓[V% Z Xvѧ瑶"v# uyC(^82o_jjϲ\k> |jݝsi,+&0=(yq( λ onf6E#t5oHZ&沞-FN2ٰPI&jϻ4p'|<.ܓm>'oх cyޠH[ ӑ4LZӉbיiOU{z^{im==ogVFEpҟ5jI++NBm/koAҞr:|OJJbW~QG,S-ڻ'ER'%ymU?:-7 2Njh"iƑ+f5leLvA2;1gEg岳U  X,P@p("@p($8M(޹e1u7%=鼼heyi]d\Ob[-/ðMۧuq&o'DgAr~=cT/~?S :])nJVCӈN5\5/d~t GmotxH]5v(Qh#L*:UHrcƚ9C2Q27i{,\(M7-?>MI8 vm2#ǖ&%2~Л|Zg7Ja^ Nƙ]K3 _nԗ58"~%YmH*#9Ⱦ5I#2#Ǘe^"!ch{1!VL.%Z~#.r7jDDJ1۩\qw0l'DL#61+%Y]u(%6і@E$カޥK?^0LK1q~9s>~~' @ (Ng7F/"Wc2k0uVH~JsHDHfޗ%| 8B~#C:9O 8P@E 8߭V˾go]@A@p("@p($8I| n~ S;۔ߜi,"p.ƙj`F6@HdٔP7{w45 Fȴ6~6̸ʪ%J.ՙ1ގ'ḡZp6~1J&hs.ˢY1"fg>>||Y, .Fll'PP(Y6峲WOO;v0ܲ>\ Iɲ)4O^,:%8xS=w !ϗoSmZOLM,ӎ, )0]*;6nX:؜[m)GvMLÓ, HqIYOsnQ ne~Y]C : _ ,RɅ@E 8P@E 8Q ~׬nc;y)b-GYsMLYPCuj޵!A#e%)&L̹pTh](x2NưU7dO~ؽ`( y᧬^ZJ_%Eӳr}w+˫Ύ[0a}( α7֜Ko":2d#8;vrx e+}GF}K^Ťr)+~?J4T Ӥa1䅒!Qw`}?8:kY(HYYSJPOh2UڷDy;%HYi ɷe.I`J[u#::-` N$EGTL ǘc)i7=$ t5oX0t̆{2LbS>Ksp)_kBmoߦ-;\^ںgU_X`< N~7C: D8P@E 89sV3 P@E 8PDIp2*:1*Lbsl򌏉MyۀPo3Ť?$bRR-vFqeHMyo'HO'/c{.}ė2?ب(8&[3,&QkpyK^צM9cؒwZbM'1H+l qta2FcG⼍MC{~䓈6PcVI8:!)%7A$4++qnsA;b&,6҇vڈǧs /_Y"67s'#wEڨKO/ރe&T}jsl9)"T鐚!n_Ry\بفpH$:@A@p("!@E 8P@E 8QܐڛAymʽsF{6-.3>Moj7ۀ̨ ˀC#CR}ʔm\ryH?JCu8Ms#lyQwčs$RKZ}ʄG ȁJ[T8QF"2rG[4oYxi<(ԇ3NZHsTRX-ҟF[3=@}EJ[u#::@`1KH}Ԭ5e#G0e/) z)e'6іiY)"2;qwd E4ȨRfͩ ~|wYۂC'T?V= [8m+A:#CGn5 ٘Qܵ]@]4abDDѓuzҾ4e1;:U:,8O+^ ]L mDaEV[&spN3EJy{/!=;SE^L1vfCiNtzDGe:<] &ھv{(]4K# 9U鐚!/7ɍV'6`qx8 @nt,&p("@p("z47. fܘ n: Sy̸!a: #"eQlo_|9Ẁԇ;z) &&%ܰ X{˒Xo$(q'.Y+t<oYI'd憢,(G&I=J@o-Ic]|.%y(qm?xd* 8-\o&֣%0ƎvIA^.$xXolQdmw~ m"z8|EU+o (xP฿["@p("@p($8g_2 242Y?&YI+@gna%rrh lnh}sN@41`J,EJiOie씨O2&i%:VɎ9{DBOOMqQc 0a7L+uB J̘3';?`rrRX.wMw/- J$A/SSw 8t4RGG>Wю9?~^t~ڢZ(/(ՓHDe~Ce|>Oa;o~ܛ[_c$?|L;,_ mɛ?W|ؿ@[qd"g >"ןW^/|dݏ⧵?&h$bwEp+o?dd3K7NC-+'s4ɟ{y\?uˌ0j"74u>Œ9/1};YnzGNB7l2/|6n}\mJy~H^RtgFQp@v 1)tS+<_+M$>5n/ﳐvbYS$wg閠#8)|+*%:Xp>_ s>I@_ϩ뷫 ,\LGE,~2Ml}k?QRx?OJt.6ψ?9WeEܿw"Oj#u42 Ө~9Qh&iwk|;YсS~L<&>22#NfuanԔ~K쿥!9:Rʛ\0 G[EOٻߦ\E+t^O_;.y}yeD>^R"H[Kqr,3 p` P@Eۡ5ڡ7v6=4cئMr@GpkDk:]yӡKg?C_.J_N`( uwnվ#j&m'}=K77uڱQmw2C 9^~R-kԡ>57xAh?f{#if&l>\/~i7щGܼndM*w45O4Z:]^g^#>;%Z˚P.7~ǯco9.5BUҾ]*seK:~d)і [<%]JxMQW:ESj&gl~*)9f JMҥ:mPܥkZlg;AUQN/g$ JJ5N=~xhk;ŴY>/jI`h F P@p("@p(" .=޿;kO zۘ{\3<Xt' %iҼ>=~NOz_i3)'juu ^ҋofἿC8uޖ;XTt'#_&/hHṈsNb1Z TPÉ'G 7DO%]6Cċ0^׼49Otנsq/ߑnT#~6afA?EΙܧb}LO8=u1+%.p}0\qzװ92msCާGRޢŋ^d'<=$[,q![f/M/ XajdMM9,?ujQEYa< =M1&yީ4C.[(DŽs.)t{:^pқt/;&HP{1)>K o^1q L/F:XqOcJ? ; `^(]4y^:&҇x>燗~&A(N/g,Q ՝_GT0~xpNϨY.s{"43MƂO $M] >b3؞#$$Tői! ,'u?yd56?E 4s8asN?DZ`Z.$Y'YY|{ D!5N?x}=iˢa˺Q=%LOC0{IF.z3lx8 P@p("@p(" .KDX)?77#0~ez̃t\h=:zt=EIp4{NCKFc;eyHfh3_^~F(>AvIKeGtϱ's`ïøOK+9PUSD^DO% upOHmP5䔕4KƉD?SN}Bf|{лh%r>xj=G\48e|Gzs?xZFqSCuTXЏ\KOSW>DQp$/IM {=U4LX!m95||]I ҫs.HT#At^dG/%GYrƯ"tIPhK$D?HIbG>pz,r"ܧ{/=5ӿL+ xFcz&6O @.7["Ip3 8P@E 8߭V˾go]@A@p("@p("ϣ󚽯"ug |j›5 *H)PX%Xe< ni\Bؓh'uғ~Y#OJk|['`(8n4 VeOq@N["뉵ql|AG~Q ?SmN :)ew-"SҬe6i\1:"֗<܇ 1!bީ#5rGIuM=\xЬx showinfilemanager-1.1.6/.idea/copyright/000077500000000000000000000000001514476644400202305ustar00rootroot00000000000000showinfilemanager-1.1.6/.idea/copyright/Copyright_Damon_Lynch_MIT.xml000066400000000000000000000007011514476644400257040ustar00rootroot00000000000000 showinfilemanager-1.1.6/.idea/copyright/profiles_settings.xml000066400000000000000000000007521514476644400245210ustar00rootroot00000000000000 showinfilemanager-1.1.6/.idea/inspectionProfiles/000077500000000000000000000000001514476644400220775ustar00rootroot00000000000000showinfilemanager-1.1.6/.idea/inspectionProfiles/Project_Default.xml000066400000000000000000000012511514476644400256720ustar00rootroot00000000000000 showinfilemanager-1.1.6/.idea/inspectionProfiles/profiles_settings.xml000066400000000000000000000003441514476644400263650ustar00rootroot00000000000000 showinfilemanager-1.1.6/.idea/misc.xml000066400000000000000000000011221514476644400176710ustar00rootroot00000000000000 showinfilemanager-1.1.6/.idea/modules.xml000066400000000000000000000004361514476644400204150ustar00rootroot00000000000000 showinfilemanager-1.1.6/.idea/ruff.xml000066400000000000000000000003471514476644400177100ustar00rootroot00000000000000 showinfilemanager-1.1.6/.idea/scopes/000077500000000000000000000000001514476644400175145ustar00rootroot00000000000000showinfilemanager-1.1.6/.idea/scopes/Damon_Lynch_exclusive_MIT__code.xml000066400000000000000000000003451514476644400263640ustar00rootroot00000000000000 showinfilemanager-1.1.6/.idea/showinfilemanager.iml000066400000000000000000000013041514476644400224230ustar00rootroot00000000000000 showinfilemanager-1.1.6/.idea/vcs.xml000066400000000000000000000002641514476644400175370ustar00rootroot00000000000000 showinfilemanager-1.1.6/.pre-commit-config.yaml000066400000000000000000000003351514476644400215220ustar00rootroot00000000000000repos: - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. rev: v0.3.0 hooks: # Run the linter. - id: ruff # Run the formatter. - id: ruff-format showinfilemanager-1.1.6/CHANGELOG.md000066400000000000000000000104441514476644400170540ustar00rootroot00000000000000# Changelog for Show in File Manager ## 1.1.6 (2026-02-17) - Support [Cosmic Files](https://github.com/pop-os/cosmic-files). - Add check for "kde:plasma" in environment variable `XDG_CURRENT_DESKTOP`. - Fix [#27](https://github.com/damonlynch/showinfilemanager/issues/27): Incorrect behavior when Windows explorer hides file extensions. Thanks to tpl2go for the fix. - Fix [#29](https://github.com/damonlynch/showinfilemanager/issues/29): Directory names with Unicode do not open correctly in file managers that cannot select files. Thanks to nipatriknilsson for identifying the underlying problem. - Drop Python 3.8 and 3.9 support. Use Python 3.10+ typing syntax. - Add `__version__` to package. - `hatch build -t sdist` now produces an archive of the project's source code. - `hatch build -t wheel` now produces a wheel (zip archive) of the program's Python code; it also generates the manpage. ## 1.1.6a1 (2024-04-18) - Update SPDX headers to be compliant with spec. - Switch to [hatch](https://hatch.pypa.io/latest/) from setuptools. - New build dependency: [hatch-argparse-manpage](https://github.com/damonlynch/hatch-argparse-manpage). - Add [Release Notes](RELEASE_NOTES.md). - Refactor: use absolute imports, not relative. - Refactor: flatten code by using a new class. The API is unchanged. ## 1.1.5 (2024-03-06) - Drop setup.py and setup.cfg in favor of pyproject.toml. - Purge doc directory. - New build dependency: [argparse-manpage](https://github.com/praiskup/argparse-manpage) - Generate man page with argparse-manpage instead of pandoc. - Format and lint using ruff. Drop black. - Drop Python 3.6 and 3.7 support. ## 1.1.4 (2022-03-04) - Move debian directory into doc ## 1.1.3 (2022-02-18) - Fix [#3](https://github.com/damonlynch/showinfilemanager/issues/3): Missing dependency on packaging. ## 1.1.2 (2021-12-27) - Add check for "unity:unity7:ubuntu" in environment variable XDG_CURRENT_DESKTOP. See https://github.com/damonlynch/rapid-photo-downloader/issues/46. ## 1.1.1 (2021-10-31) - Add `allow_conversion` switch to `show_in_file_manager()`. Set to False if passing non-standard URIs. - Recognize non-standard URI prefix 'camera:/', used by KDE. - Added function `linux_desktop_humanize()`, to make Linux desktop environment variable name values human friendly. ## 1.1.0 (2021-10-29) - On WSL2, use a Linux file manager (if set) for WSL paths, and Windows Explorer for Windows paths. If no Linux file manager is installed, use Windows Explorer. To override the default choice of using Explorer for Windows paths, simply specify a file manager of your choice. - On both WSL1 and WSL2, use Windows style URIs to work around a [bug](https://github.com/microsoft/WSL/issues/7603) in WSL where using the /select switch while passing a path with spaces in it fails. - Don't mess up the terminal when launching Windows Explorer from WSL on Windows Terminal. ## 1.0.1 (2021-10-23) - Reformat code with black. ## 1.0.0 (2021-10-04) - Support [Lumina](https://lumina-desktop.org/). ## 0.9.0 (2021-09-08) - Add option to specify file manager to use from command line. - Support [Double Commander](https://doublecmd.sourceforge.io/). - Support [Krusader](https://krusader.org/). - Support [SpaceFM](https://ignorantguru.github.io/spacefm/). - Support [fman](https://fman.io/). ## 0.0.8 (2021-08-31) - Support [CutefishOS](https://en.cutefishos.com/). - Support [Index File Manager](https://invent.kde.org/maui/index-fm). ## 0.0.7 (2021-08-20) - Use `--select` command line switch in caja 1.26 or newer. ## 0.0.6 (2021-08-19) - Remove get\_ prefix from package level function names. ## 0.0.5 (2021-08-19) - Add setup.py for man page generation. - Improve README to clarify installation and usage. - Parse filename globs passed via the command line on Windows. - Use win32 API to execute explorer.exe on Windows, allowing for multiple file selection. ## 0.0.4 (2021-08-14) - Update README to include installation instructions. - Include CHANGELOG.md in package. - Generate man page for use in Linux. - Improve command line argument documentation. ## 0.0.3 (2021-08-12) - Update README. ## 0.0.2 (2021-08-12) - Move config data from `__about__.py` to static config in `pyproject.toml` [(PEP 621)](https://www.python.org/dev/peps/pep-0621/). - Added command line arguments `--debug` and `--verbose`. ## 0.0.1 (2021-08-12) - Initial release. showinfilemanager-1.1.6/LICENSE000066400000000000000000000020541514476644400162460ustar00rootroot00000000000000MIT License Copyright (c) 2021 Damon Lynch Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. showinfilemanager-1.1.6/README.md000066400000000000000000000727231514476644400165320ustar00rootroot00000000000000# Show in File Manager | | | |---------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Package | [![PyPI - Version](https://img.shields.io/pypi/v/show-in-file-manager.svg)](https://pypi.org/project/show-in-file-manager) [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/show-in-file-manager.svg?logo=python&label=Python&logoColor=gold)](https://pypi.org/project/show-in-file-manager/) | | Meta | [![Hatch project](https://img.shields.io/badge/%F0%9F%A5%9A-Hatch-4051b5.svg)](https://github.com/pypa/hatch) [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff) [![GitButler](https://img.shields.io/badge/GitButler-%23B9F4F2?logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB3aWR0aD0iMzkiIGhlaWdodD0iMjgiIHZpZXdCb3g9IjAgMCAzOSAyOCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTI1LjIxNDUgMTIuMTk5N0wyLjg3MTA3IDEuMzg5MTJDMS41NDI5NSAwLjc0NjUzMiAwIDEuNzE0MDYgMCAzLjE4OTQ3VjI0LjgxMDVDMCAyNi4yODU5IDEuNTQyOTUgMjcuMjUzNSAyLjg3MTA3IDI2LjYxMDlMMjUuMjE0NSAxNS44MDAzQzI2LjcxOTcgMTUuMDcyMSAyNi43MTk3IDEyLjkyNzkgMjUuMjE0NSAxMi4xOTk3WiIgZmlsbD0iYmxhY2siLz4KPHBhdGggZD0iTTEzLjc4NTUgMTIuMTk5N0wzNi4xMjg5IDEuMzg5MTJDMzcuNDU3MSAwLjc0NjUzMiAzOSAxLjcxNDA2IDM5IDMuMTg5NDdWMjQuODEwNUMzOSAyNi4yODU5IDM3LjQ1NzEgMjcuMjUzNSAzNi4xMjg5IDI2LjYxMDlMMTMuNzg1NSAxNS44MDAzQzEyLjI4MDMgMTUuMDcyMSAxMi4yODAzIDEyLjkyNzkgMTMuNzg1NSAxMi4xOTk3WiIgZmlsbD0idXJsKCNwYWludDBfcmFkaWFsXzMxMF8xMjkpIi8%2BCjxkZWZzPgo8cmFkaWFsR3JhZGllbnQgaWQ9InBhaW50MF9yYWRpYWxfMzEwXzEyOSIgY3g9IjAiIGN5PSIwIiByPSIxIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgZ3JhZGllbnRUcmFuc2Zvcm09InRyYW5zbGF0ZSgxNi41NzAxIDE0KSBzY2FsZSgxOS44NjQxIDE5LjgzODMpIj4KPHN0b3Agb2Zmc2V0PSIwLjMwMTA1NiIgc3RvcC1vcGFjaXR5PSIwIi8%2BCjxzdG9wIG9mZnNldD0iMSIvPgo8L3JhZGlhbEdyYWRpZW50Pgo8L2RlZnM%2BCjwvc3ZnPgo%3D)](https://gitbutler.com/) [![Checked with mypy](http://www.mypy-lang.org/static/mypy_badge.svg)](http://mypy-lang.org/) [![License - MIT](https://img.shields.io/badge/license-MIT-9400d3.svg)](https://spdx.org/licenses/) [![GitHub Sponsors](https://img.shields.io/github/sponsors/damonlynch?logo=GitHub%20Sponsors&style=social)](https://github.com/sponsors/damonlynch) | ______________________________________________________________________ **Show in File Manager** is a Python package to open the system file manager and optionally select files in it. The point is not to _open_ the files, but to _select_ them in the file manager, thereby highlighting the files and allowing the user to quickly do something with them. Plenty of programs expose this functionality in their user interface. On Windows terms like "Show in Windows Explorer", "Show in Explorer", and "Reveal in Explorer" are common. Cross-platform programs use terms like "Open Containing Folder" or "Open in File Browser", all doing something similar: ![Show in Windows Explorer](https://github.com/damonlynch/showinfilemanager/raw/main/.github/photomechanic-win.png) ![Open containing folder](https://github.com/damonlynch/showinfilemanager/raw/main/.github/documentviewer-gnome.png) Commands like these open the file manager, ideally with the files selected: ![Peony file manager](https://github.com/damonlynch/showinfilemanager/raw/main/.github/peony-kylin.png) With **Show in File Manager**, your Python program or command line script can do the same, with minimum effort from you. Although this package provides several functions to assist in identifying the system's file managers, in most circumstances you need to call only one function, the function `show_in_file_manager`, and it should just work. This package aspires to be platform independent, but it currently supports only Windows 10/11, Linux, [WSL1 and WSL2](https://docs.microsoft.com/en-us/windows/wsl/), and macOS. It works with 20 [supported file managers](#supported-file-managers). ## Install and run ```bash python3 -m pip install show-in-file-manager ``` You can import it as a Python module: ```python from showinfm import show_in_file_manager show_in_file_manager('/home/user/file.txt') ``` Or run it from the command line: ```bash showinfilemanager file1.txt file2.txt ``` ```commandline showinfilemanager.exe D:\Documents\*.docx ``` More [examples](#examples) are below. ## Rationale This package solves the following problems: - What is the operating system's stock file manager? - What is the user's choice of file manager? - Is the user's choice of file manager set correctly? - How do I supply command line arguments to select files in the file manager? - What about file managers with limited features? There is no standard command line argument with which to open an operating system's file manager and select files at a specified path. Moreover, not all file managers support specifying files to select — if you try to pass a file to some file managers, they will open the file instead of selecting it, or worse yet display an error message. Some file managers will only allow selecting one file at a time from the command line. On desktop Linux the problem is especially acute, as Linux provides a plethora of file managers, with widely varying command line arguments. Moreover, the user's default file manager can sometimes be incorrectly set to nonsensical values, such as an AppImage or Flatpak of a random application. Windows is not without its share of limitations. Windows Explorer will select only one file at a time when called from the command line, and the argument must be quoted in a way peculiar to it. Rather than using the command line to launch Windows Explorer, this package instead uses the [Win32 API](https://docs.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shopenfolderandselectitems) to programmatically select multiple files. Using the Win32 API is not possible when calling Windows Explorer from within WSL — this package will launch `explorer.exe` using the command line under WSL. Calling Windows Explorer from within WSL is not trivial due to differences in URI and path formats between Windows and Linux. ## Supported file managers This package takes care of calling the file managers with the correct arguments for you. The command line arguments shown here are for reference. All but two file managers accept [URIs](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier) like `file:///home/user/file.txt` in addition to regular paths like `/home/user/file.txt`. | File Manager | Used by | Command line | Can Select Files | Handles Multiple Files / Directories | Notes | |-------------------------------------------------------------------------------------------------|----------------------------------------------------|------------------------------------------|:----------------:|:------------------------------------:|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Windows File Explorer | Windows 10 / 11, Windows Subsystem for Linux (WSL) | `explorer.exe /select,URI` | ✅ | ⚠ | No space between comma and URI. Can specify only one URI via the command line, but multiple files can be specified via the Win32 API. | | Finder | macOS | `open --reveal URI` | ✅ | ❌ | | | [Nautilus (Files)](https://gitlab.gnome.org/GNOME/nautilus) | Gnome, Pop!_OS, Zorin | `nautilus --select URI1 URI2` | ✅ | ⚠ | Multiple URIs open multiple Nautilus windows. See [issue #1955](https://gitlab.gnome.org/GNOME/nautilus/-/issues/1955). | | [Dolphin](https://github.com/KDE/dolphin) | KDE | `dolphin --select URI1 URI2 ` | ✅ | ✅ | | | [Nemo](https://github.com/linuxmint/nemo) | Linux Mint | `nemo URI1 URI2` | ✅ | ⚠ | Multiple URIs open multiple Nemo windows. Cannot select folders. | | [Elementary OS Files](https://github.com/elementary/files) | Elementary OS | `io.elementary.files URI1 URI2` | ✅ | ⚠ | Multiple URIs open multiple Files tabs. Cannot select folders. | | [Deepin File Manager](https://github.com/linuxdeepin/dde-file-manager) | Deepin | `dde-file-manager --show-item URI1 URI2` | ✅ | ⚠ | Depending on version, multiple URIs either open multiple tabs, or only opens and selects the first URI. | | [Peony](https://github.com/ukui/peony) | Ubuntu Kylin | `peony --show-items URI1 URI2` | ✅ | ✅ | | | [Caja](https://github.com/mate-desktop/caja) | Mate | `caja --select URI1 URI2` | ⚠ | ⚠ | Starting with 1.26, can select a file or folder using `--select`. In all versions, specifying a file without this switch causes an error. Multiple URIs open multiple Caja windows. See [issue #1547](https://github.com/mate-desktop/caja/issues/1547). | | [Thunar](https://gitlab.xfce.org/xfce/thunar) | XFCE | `thunar URI1 URI2` | ❌ | ⚠ | Specifying a file opens it. Multiple URIs open multiple Thunar windows. | | PCManFM | LXDE | `pcmanfm URI` | ❌ | ❌ | Specifying a file opens it. Multiple URIs open only the first URI. | | [PCManFM-Qt](https://github.com/lxqt/pcmanfm-qt) | LXQt | `pcmanfm-qt URI1 URI2` | ❌ | ⚠ | Specifying a file opens it. Multiple URIs open multiple PCManFM-Qt windows. | | [CutefishOS File Manager](https://github.com/cutefishos/filemanager) | CutefishOS | `cutefish-filemanager URI` | ❌ | ❌ | Specifying a file causes File Manager to attempt to open it as if it is a folder. Multiple URIs open only the first URI. | | [Index](https://invent.kde.org/maui/index-fm) | Linux | `index URI1 URI2` | ❌ | ❌ | Specifying a file has no effect. Multiple URIs open multiple tabs, in addition to the user's home directory, which is always opened. | | [Double Commander](https://doublecmd.sourceforge.io/) | Windows, Linux | `doublecmd URI1 URI2` | ✅ | ⚠ | A double panel file manager accepting up to two URIs. Cannot select folders. | | [Krusader](https://krusader.org/) | KDE | `krusader URI` | ❌ | ⚠ | A double panel file manager accepting one URI. Two URIs can be specified using `--left` and `--right`, but that is unsupported by this package. Specifying a file causes an error. | | [SpaceFM](https://ignorantguru.github.io/spacefm/) | Linux | `spacefm URI1 URI2` | ❌ | ✅ | Specifying a file opens it. | | [fman](https://fman.io/) | Windows, Linux, macOS | `fman path1 path2` | ✅ | ⚠ | A double panel file manager accepting up to two paths. Cannot select folders. Does not accept URIs. | | [Insight](https://github.com/lumina-desktop/lumina/tree/master/src-qt5/desktop-utils/lumina-fm) | Lumina Desktop | `lumina-fm path1 path2` | ❌ | ✅ | Specifying a file displays it in the left pane as if it were a folder. Does not accept URIs. | | [Cosmic Files](https://github.com/pop-os/cosmic-files) | Cosmic | `cosmic-files URI1 URI2` | ✅ | ⚠ | Multiple URIs open multiple Files tabs. Cannot select folders. | ## Usage ### Open the file manager with the files to select ```python def show_in_file_manager( path_or_uri: str | Sequence[str] | None = None, open_not_select_directory: bool = True, file_manager: str | None = None, allow_conversion: bool = True, verbose: bool = False, debug: bool = False, ) -> None: """ Open the file manager and show zero or more directories or files in it. The path_or_uri is a sequence of items, or a single item. An item can be a regular path, or a URI. On non-Windows platforms, regular paths will be converted to URIs when passed as command line arguments to the file manager, because some file managers do not handle regular paths correctly. However, URIs will be convereted to paths to handle file managers that do not accept URIs. On Windows, Explorer is called using the Win32 API. On WSL1, all paths are opened using Windows Explorer. URIs can be specified using Linux or Windows formats. All formats are automatically converted to use the Windows URI format. WSL2 functions the same as WSL1, except if the WSL2 instance has a Linux file manager installed. On these systems, if a path on Linux is specified, that file manager will be used instead of Windows Explorer. Override this default behavior by using the parameter file_manager. The most common use of this function is to call it without specifying the file manager to use, which defaults to the value returned by valid_file_manager() For file managers unable to select files to display, the file manager will instead display the contents of the path. For file managers that can handle file selections, but only one at a time, multiple file manager windows will be opened. If a file manager executable is specified and this package does not recognize it, the executable will be called with the files as the only command line arguments. :param path_or_uri: zero or more files or directories to open, specified as a single URI or valid path, or a sequence of URIs/paths. :param open_not_select_directory: if the URI or path is a directory and not a file, open the directory itself in the file manager, rather than selecting it and displaying it in its parent directory. :param file_manager: executable name to use. If not specified, then valid_file_manager() will determine which file manager to use. :param allow_conversion: allow this function to automatically convert paths and URIs to the format needed by the file manager that will be called. Set to False if passing non-standard URIs. Ignored when running under WSL. :param verbose: if True print command to be executed before launching it :param debug: if True print debugging information to stderr """ ``` Other functions mentioned below are not necessary to call, but are provided for convenience and control. ### Determine the most sensible choice of file manager ```python def valid_file_manager() -> str: """ Get user's file manager, falling back to using sensible defaults. The user's choice of file manager is the default choice. However, this is not always set correctly. On Linux, it most likely is because the user's distro has not correctly set the default file manager. If the user's choice is unrecognized by this package, then reject it and choose the standard file manager for the detected desktop environment. All exceptions are caught, except those if this platform is not supported by this package. :return: If the user's default file manager is set and it is recognized as valid by this package, then return it. Otherwise return the stock file manager, if it exists. """ ``` This package makes opinionated choices about the most sensible choice of file manager: 1. A file manager is valid if and only if this package recognizes it, e.g. `nautilus`, `explorer.exe`. 2. If the user's choice of file manager is valid (i.e. an actual file manager, not some random application), that file manager is used. 3. If the user's choice of file manager is invalid or could not be determined, the desktop or OS's stock file manager is used. ### Get the operating system's stock file manager ```python def stock_file_manager() -> str: """ Get stock file manager for this operating system / desktop. On Windows the default is `explorer.exe`. On Linux the first step is to determine which desktop is running, and from that lookup its default file manager. On macOS, the default is finder, accessed via the command 'open'. Exceptions are not caught. :return: executable name """ ``` ### Get the user's choice of file manager ```python def user_file_manager() -> str: """ Get the file manager as set by the user. Exceptions are not caught. :return: executable name """ ``` On Windows and macOS, for now only the stock file manager is returned. That could change in future releases. On Linux, the file manager is probed using `xdg-mime query default inode/directory`, and the resulting `.desktop` file is parsed to extract the file manager command. ## Examples From Python, show file or directory in file manager, using the most sensible choice of file manager: ```python # Windows path, in Windows or from within WSL show_in_file_manager('C:\Documents\myfile.txt') # Windows URI, in Windows or from within WSL show_in_file_manager('file://C:/Documents/myfile.txt') # Mixing Windows and Linux style URIs and paths, from within WSL show_in_file_manager( ( 'file:///C:/Documents/myfile.txt', '/mnt/d/Data/database.sqlite', '/home/user/.profile', 'file:/etc/fstab' ), file_manager='explorer.exe' ) # Linux path show_in_file_manager('/home/user/myfile.txt') # Linux multiple paths show_in_file_manager(('/home/user/myfile.txt', '/home/user/other file.txt')) # Mixing Linux URI and Linux path show_in_file_manager( ('file:///home/user/other%20file.txt', '/home/otheruser/.bashrc') ) # Simply open the file manager show_in_file_manager() # Open the file manager at a directory show_in_file_manager('/home/user') # Select the user directory in the home folder show_in_file_manager('/home/user', open_not_select_directory=False) ``` Open the system home directory (`/home` on Linux, `/Users` on macOS) and select the user's home folder in it: ```bash showinfilemanager -s ~ ``` Open the user's home directory directly, without selecting it: ```bash showinfilemanager ~ ``` Select files in two different directories, and open a third directory: ```bash showinfilemanager myfile.txt ../anotherfile.txt ../../ ``` The previous command will open three different instances of the file manager, because of three different directories (macOS users may need to adjust finder preferences in order to display multiple finder windows). ## Limitations - Its behavior in a confined Linux environment like a Flatpak, Snap, or AppImage is untested. ## Contributing Please file issues or pull requests to improve the code. Discuss improvements in the GitHub discussion section for this project. The initial source of this code is from [Rapid Photo Downloader](https://github.com/damonlynch/rapid-photo-downloader). ## License [MIT](https://choosealicense.com/licenses/mit/) ## Authors - [@damonlynch](https://github.com/damonlynch) showinfilemanager-1.1.6/RELEASE_NOTES.md000066400000000000000000000011471514476644400176150ustar00rootroot00000000000000# Release Notes for Show in File Manager 1.1.6 - Show in File Manager 1.1.6 switches its build system from `setuptools` to [Hatch](https://github.com/pypa/hatch). - `hatch build -t sdist` now produces an archive of the project's source code. - `hatch build -t wheel` now produces a wheel (zip archive) of the program's Python code; it also generates the manpage. - To generate the manpage, a new plugin [Hatch-argparse-manpage](https://github.com/damonlynch/hatch-argparse-manpage) is used, which is a new build-time dependency. This plugin can be used with any Hatch project, not just Show in File Manager. showinfilemanager-1.1.6/mypy.ini000066400000000000000000000001751514476644400167420ustar00rootroot00000000000000[mypy] exclude = ["venv"] [mypy-packaging.*] ignore_missing_imports = True [mypy-win32com.*] ignore_missing_imports = True showinfilemanager-1.1.6/pyproject.toml000066400000000000000000000046541514476644400201650ustar00rootroot00000000000000[build-system] requires = ["hatchling", "hatch-argparse-manpage"] build-backend = "hatchling.build" [project] name = "show-in-file-manager" authors = [{ name = "Damon Lynch", email = "damonlynch@gmail.com" }] description = "Open the system file manager and select files in it" classifiers = [ "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", "License :: OSI Approved :: MIT License", "Operating System :: MacOS :: MacOS X", "Operating System :: Microsoft :: Windows :: Windows 10", "Operating System :: Microsoft :: Windows :: Windows 11", "Operating System :: POSIX :: Linux", "Development Status :: 5 - Production/Stable", "Topic :: Desktop Environment :: File Managers", "Typing :: Typed", ] requires-python = ">=3.10" dependencies = [ "pyxdg>=0.25; platform_system=='Linux'", "packaging", "pywin32>=301; platform_system=='Windows'", ] dynamic = ["version"] [project.readme] file = "README.md" content-type = "text/markdown" [project.urls] Homepage = "https://github.com/damonlynch/showinfilemanager" Issues = "https://github.com/damonlynch/showinfilemanager/issues" [project.scripts] showinfilemanager = "showinfm.showinfm:main" [tool.hatch.version] path = "src/showinfm/__about__.py" [tool.hatch.build.targets.sdist] include = ["src/showinfm", "/*.md"] exclude = [".github"] [tool.hatch.build.targets.wheel] packages = ["src/showinfm", "man", "/*.md"] [tool.hatch.build.targets.wheel.hooks.argparse-manpage] include-url = false manpages = [ "man/showinfilemanager.1:function=get_parser:pyfile=src/showinfm/argumentsparse.py:manual_title=General Commands Manual", ] [tool.ruff] exclude = [ ".bzr", ".direnv", ".eggs", ".git", ".git-rewrite", ".hg", ".ipynb_checkpoints", ".mypy_cache", ".nox", ".pants.d", ".pyenv", ".pytest_cache", ".pytype", ".ruff_cache", ".svn", ".tox", ".venv", ".vscode", "__pypackages__", "_build", "buck-out", "build", "dist", "node_modules", "site-packages", "venv", "urivalidate.py", ] # Same as Black. line-length = 88 indent-width = 4 [tool.ruff.lint] select = [ # pycodestyle "E", # Pyflakes "F", # pyupgrade "UP", # flake8-simplify "SIM", # isort "I", ] showinfilemanager-1.1.6/src/000077500000000000000000000000001514476644400160275ustar00rootroot00000000000000showinfilemanager-1.1.6/src/cli.py000066400000000000000000000003051514476644400171460ustar00rootroot00000000000000#!/usr/bin/python3 # SPDX-FileCopyrightText: 2021-2024 Damon Lynch # SPDX-License-Identifier: MIT from showinfm.showinfm import main if __name__ == "__main__": main() showinfilemanager-1.1.6/src/rundoctest.py000066400000000000000000000005401514476644400205720ustar00rootroot00000000000000#!/usr/bin/python3 # SPDX-FileCopyrightText: 2021-2026 Damon Lynch # SPDX-License-Identifier: MIT import doctest import platform from showinfm.system.linux import wsl_transform_path_uri if __name__ == "__main__": if platform.system() == "Linux": doctest.run_docstring_examples(wsl_transform_path_uri, globals()) showinfilemanager-1.1.6/src/showinfm/000077500000000000000000000000001514476644400176615ustar00rootroot00000000000000showinfilemanager-1.1.6/src/showinfm/__about__.py000066400000000000000000000001701514476644400221370ustar00rootroot00000000000000# SPDX-FileCopyrightText: 2024 Damon Lynch # SPDX-License-Identifier: MIT __version__ = "1.1.6" showinfilemanager-1.1.6/src/showinfm/__init__.py000066400000000000000000000007041514476644400217730ustar00rootroot00000000000000# SPDX-FileCopyrightText: 2021-2026 Damon Lynch # SPDX-License-Identifier: MIT # ruff: noqa: F401 from showinfm.__about__ import __version__ from showinfm.constants import cannot_open_uris, single_file_only from showinfm.showinfm import ( show_in_file_manager, stock_file_manager, user_file_manager, valid_file_manager, ) from showinfm.system.linux import LinuxDesktop, linux_desktop, linux_desktop_humanize showinfilemanager-1.1.6/src/showinfm/argumentsparse.py000066400000000000000000000042341514476644400232760ustar00rootroot00000000000000# SPDX-FileCopyrightText: 2016-2026 Damon Lynch # SPDX-License-Identifier: MIT """ Parse command line arguments """ import importlib.metadata from argparse import ArgumentParser, HelpFormatter from pathlib import Path try: from showinfm import __about__ as __about__ except ImportError: # The script is being run at build time # Module imports are unavailable here = Path(__file__).parent with open(here / "__about__.py") as f: about = {} exec(f.read(), about) # Convert about dictionary to class class About: pass __about__ = About() __about__.__dict__.update(about) def package_metadata(): """ Get Python package metadata :return: package summary """ try: metadata = importlib.metadata.metadata("show-in-file-manager") summary = metadata["summary"] except Exception: summary = ( "Platform independent Python module to open the system file manager " "and optionally select files in it " ) return summary def get_parser(formatter_class=HelpFormatter) -> ArgumentParser: """ Parse command line options for this script :param formatter_class: one of 4 argparse formatting classes :return: argparse.ArgumentParser """ summary = package_metadata() parser = ArgumentParser( prog="showinfilemanager", description=summary, formatter_class=formatter_class ) parser.add_argument( "--version", action="version", version=f"%(prog)s {__about__.__version__}" ) parser.add_argument("-f", "--file-manager", help="file manager to run") parser.add_argument( "-s", "--select-folder", action="store_true", help="select folder instead of displaying its contents", ) parser.add_argument( "--verbose", action="store_true", help="display command being run to stdout" ) parser.add_argument( "--debug", action="store_true", help="output debugging information to stdout" ) parser.add_argument( "path", nargs="*", help="zero or more URIs or paths of files or directories" ) return parser showinfilemanager-1.1.6/src/showinfm/constants.py000066400000000000000000000013201514476644400222430ustar00rootroot00000000000000# SPDX-FileCopyrightText: 2016-2021 Damon Lynch # SPDX-License-Identifier: MIT from enum import Enum class FileManagerType(Enum): regular = 1 # file_manager "File1" "File2" select = 2 # file_manager --select dir_only_uri = 3 # cannot select files show_item = 4 # file_manager --show-item show_items = 5 # file_manager --show-items win_select = 6 # explorer.exe /select reveal = 7 # open --reveal (macOS) dual_panel = 8 # file_manager "File1" "File2" class Platform(Enum): windows = 1 linux = 2 macos = 3 single_file_only = ("explorer.exe", "pcmanfm", "open", "cutefish-filemanager") cannot_open_uris = ("fman", "fman.exe", "lumina-fm") showinfilemanager-1.1.6/src/showinfm/filemanager.py000066400000000000000000000403521514476644400225110ustar00rootroot00000000000000# SPDX-FileCopyrightText: 2016-2026 Damon Lynch # SPDX-License-Identifier: MIT import os import platform import shlex import subprocess import sys import urllib.parse from collections.abc import Sequence from pathlib import Path from typing import NamedTuple from showinfm.constants import FileManagerType, Platform, single_file_only from showinfm.system import ( current_platform, is_wsl, is_wsl1, is_wsl2, linux, tools, windows, ) PathOrUri = str | Sequence[str] class ProcessPathOrUri(NamedTuple): fully_processed: bool path: Path | None uri: str | None def valid_file_manager() -> str: """ Get user's file manager, falling back to using sensible defaults. The user's choice of file manager is the default choice. However, this is not always set correctly. On Linux, it most likely is because the user's distro has not correctly set the default file manager. If the user's choice is unrecognized by this package, then reject it and choose the standard file manager for the detected desktop environment. All exceptions are caught, except those if this platform is not supported by this package. :return: If the user's default file manager is set and it is recognized as valid by this package, then return it. Otherwise return the stock file manager, if it exists. """ if current_platform == Platform.windows or is_wsl1: file_manager = "explorer.exe" elif current_platform == Platform.linux: file_manager = linux.valid_linux_file_manager() elif current_platform == Platform.macos: file_manager = "open" else: raise NotImplementedError return file_manager def _file_manager_type(fm: str) -> FileManagerType: """ Determine file manager type via the executable name :param fm: executable name :return: """ if current_platform == Platform.windows or is_wsl1: return windows.windows_file_manager_type(fm) elif current_platform == Platform.linux: return linux.linux_file_manager_type(fm) elif current_platform == Platform.macos: return FileManagerType.reveal else: raise NotImplementedError class FileManager: def __init__(self) -> None: self._valid_file_manager_probed: bool = False self._valid_file_manager: str | None = None self._valid_file_manager_type: FileManagerType | None = None self.file_manager: str | None self.file_manager_type: FileManagerType | None # Used for directories when open_not_select_directory is True self.directories: list[str] # Target locations self.locations: list[str] # Used for paths that will be opened in Windows explorer called from WSL2 self.wsl_windows_paths: list[str] self.wsl_windows_directories: list[str] # Argument to pass to a file manager on the command line self.arg: str = "" # whether to print command to be executed before launching the file manager self.verbose: bool self.debug: bool self.file_manager_specified: bool self.open_not_select_directory: bool self.allow_conversion: bool def _launch_file_manager(self, uris_or_paths: list[str]) -> None: """ Launch the file manager :param uris_or_paths: list of URIs, or a list of a single empty string """ for u in uris_or_paths: cmd = f"{self.file_manager} {self.arg}{u}" if self.verbose: print("Executing", cmd) # Do not check current_platform here, it makes no sense args = shlex.split(cmd) if platform.system() != "Windows" else cmd proc = subprocess.Popen(args) if is_wsl2 and self.file_manager == "explorer.exe": proc.wait() def _set_valid_file_manager(self) -> None: """ Set class level global variables to set a valid file manager for this user in this desktop environment. """ if not self._valid_file_manager_probed: fm = valid_file_manager() if fm: self._valid_file_manager = fm self._valid_file_manager_type = _file_manager_type(fm) self._valid_file_manager_probed = True def show_in_file_manager( self, path_or_uri: PathOrUri | None = None, open_not_select_directory: bool = True, file_manager: str | None = None, allow_conversion: bool = True, verbose: bool = False, debug: bool = False, ) -> None: self.file_manager = file_manager self.verbose = verbose self.debug = debug self.open_not_select_directory = open_not_select_directory self.allow_conversion = allow_conversion self.file_manager_specified = self.file_manager is not None if not self.file_manager: self._set_valid_file_manager() self.file_manager_type = self._valid_file_manager_type self.file_manager = self._valid_file_manager else: try: self.file_manager_type = _file_manager_type(self.file_manager) except Exception: self.file_manager_type = None if not self.file_manager: # There is no file manager -- there is nothing to be done return self.arg = "" self.locations = [] self.directories = [] # Set these WSL2 specific values even if not running it -- # it makes the launch process simpler. self.wsl_windows_paths = [] self.wsl_windows_directories = [] if not path_or_uri and current_platform == Platform.macos: # macOS finder requires a path to be able to launch it from the # command line path_or_uri = "file:///" if path_or_uri: self._process_path_or_uri(path_or_uri) self._set_file_manager_argument() self._launch() def _process_path_or_uri_wsl(self, pu: str) -> ProcessPathOrUri: """ Process the path or URI when running under WSL1 or WSL2. Is the path on the Windows file system, or alternately is Windows explorer going to be used to view the files? Also, what kind of path or URI has been passed? :param pu: path or URI to process :return: A tuple indicating whether the path or URI has been fully processed, and if not, a Path or URI to process further """ require_win_path = self.file_manager == "explorer.exe" wsl_details = linux.wsl_transform_path_uri(pu, require_win_path) if not wsl_details.exists: if self.debug: print(f"Path does not exist: '{pu}'", file=sys.stderr) return ProcessPathOrUri(fully_processed=True, path=None, uri=None) use_windows_explorer_via_wsl = ( wsl_details.is_win_location and not self.file_manager_specified ) or self.file_manager == "explorer.exe" if use_windows_explorer_via_wsl: if wsl_details.win_uri is None: if self.debug: print( f"Unable to convert '{pu}' into a Windows URI", file=sys.stderr, ) return ProcessPathOrUri(fully_processed=True, path=None, uri=None) if self.debug: print( f"Converted '{pu}' to '{wsl_details.win_uri}'", file=sys.stderr, ) if not (wsl_details.is_dir and self.open_not_select_directory): self.wsl_windows_paths.append(wsl_details.win_uri) else: self.wsl_windows_directories.append(wsl_details.win_uri) return ProcessPathOrUri(fully_processed=True, path=None, uri=None) else: if wsl_details.linux_path is None: if self.debug: print( f"Unable to convert '{pu}' into a Linux path", file=sys.stderr, ) return ProcessPathOrUri(fully_processed=True, path=None, uri=None) assert self.file_manager if tools.filemanager_requires_path(file_manager=self.file_manager): path = Path(wsl_details.linux_path).resolve() uri = None else: path = None uri = Path(wsl_details.linux_path).resolve().as_uri() return ProcessPathOrUri(fully_processed=False, path=path, uri=uri) def _process_path_or_uri_non_wsl(self, pu: str) -> ProcessPathOrUri: """ Process the path or URI when not running under WSL1 or WSL2. :param pu: path or URI to process :return: A tuple indicating whether the path or URI has been fully processed, and if not, a Path or URI to process further """ assert self.file_manager if tools.is_uri(pu): if ( tools.filemanager_requires_path(file_manager=self.file_manager) and self.allow_conversion ): # Convert URI to a regular path uri = None path = Path(tools.file_uri_to_path(pu)) else: uri = pu path = None else: if ( tools.filemanager_requires_path(file_manager=self.file_manager) or not self.allow_conversion ): path = Path(pu) uri = None else: uri = Path(pu).resolve().as_uri() path = None return ProcessPathOrUri(fully_processed=False, path=path, uri=uri) def _process_path_or_uri_no_select( self, path: Path | None, uri: str | None ) -> None: """ Process the path or URI when using a file manager that cannot select files or directories. """ assert current_platform != Platform.windows # Show only the directory: do not attempt to select the file, # because the file manager cannot handle it. if uri: # Do not use tools.file_url_to_path() here, because we need the # parse_result, and file_url_to_path() assumes file:// URIs. # In any case, this code block is not run under Windows, so # there is no need to use tools.file_url_to_path() to handle the # file:/// case that urllib.parse.urlparse fails with. parse_result = urllib.parse.urlparse(uri) path = Path(urllib.parse.unquote(parse_result.path)) else: parse_result = None assert path is not None if not (path.is_dir() and self.open_not_select_directory): path = path.parent if uri: path = urllib.parse.quote(str(path)) uri = str(urllib.parse.urlunparse(parse_result._replace(path=str(path)))) else: path = tools.quote_path(path=path) self.locations.append(uri or str(path)) def _process_path_or_uri_can_select( self, path: Path | None, uri: str | None ) -> None: """ Process the path or URI when using a file manager that can select files and potentially directories. """ # Whether to open a directory or select it depends on file manager capabilities # and the option open_not_select_directory open_directory = False if ( self.open_not_select_directory and self.file_manager_type != FileManagerType.dual_panel or self.file_manager_type == FileManagerType.regular ): if uri: path = Path(tools.file_uri_to_path(uri=uri)) open_directory = path.is_dir() else: assert path is not None open_directory = path.is_dir() if open_directory: if ( self.file_manager_type == FileManagerType.regular and not self.open_not_select_directory ): # This type of file manager cannot select directories, # because it provides no mechanism # to distinguish between selecting and opening a # directory. # So open the parent instead. path = path.parent if uri: uri = path.as_uri() if uri is None: path = tools.quote_path(path=path) self.directories.append(uri or str(path)) if not open_directory: if uri is None and self.file_manager != "explorer.exe": assert path is not None path = tools.quote_path(path=path) self.locations.append(uri or str(path)) def _process_path_or_uri(self, path_or_uri: PathOrUri) -> None: """ Examines the path or URI and processes it according to the needs of the file manager. :param path_or_uri: path or URI to process """ if isinstance(path_or_uri, str): # turn the single path / URI into a Sequence path_or_uri = (path_or_uri,) filtered_path_or_uri = (p_or_u for p_or_u in path_or_uri if p_or_u) for pu in filtered_path_or_uri: if is_wsl: p = self._process_path_or_uri_wsl(pu) else: p = self._process_path_or_uri_non_wsl(pu) if p.fully_processed: continue path = p.path uri = p.uri assert path is not None or uri is not None if self.file_manager_type == FileManagerType.dir_only_uri: self._process_path_or_uri_no_select(path, uri) else: self._process_path_or_uri_can_select(path, uri) def _set_file_manager_argument(self) -> None: self.arg = "" if self.file_manager_type == FileManagerType.win_select: self.arg = "/select," # no trailing space is necessary on Windows elif self.file_manager_type == FileManagerType.select: self.arg = "--select " # trailing space is necessary elif self.file_manager_type == FileManagerType.show_item: self.arg = "--show-item " # trailing space is necessary elif self.file_manager_type == FileManagerType.show_items: self.arg = "--show-items " # trailing space is necessary elif self.file_manager_type == FileManagerType.reveal: self.arg = "--reveal " # trailing space is necessary def _launch(self) -> None: if ( current_platform == Platform.windows and not is_wsl and self.file_manager == "explorer.exe" ): if self.locations: windows.launch_file_explorer(self.locations, self.verbose) for d in self.directories: if self.verbose: print("Executing Windows shell to open", d) os.startfile(d) # type: ignore[attr-defined] else: if self.locations: # Some file managers must be passed only one or zero paths / URIs if self.file_manager not in single_file_only: self.locations = [" ".join(self.locations)] self._launch_file_manager(uris_or_paths=self.locations) if self.directories: if self.file_manager not in single_file_only: self.directories = [" ".join(self.directories)] self.arg = "" self._launch_file_manager(uris_or_paths=self.directories) if self.wsl_windows_paths: self.arg = "/select," self.file_manager = "explorer.exe" self._launch_file_manager(uris_or_paths=self.wsl_windows_paths) if self.wsl_windows_directories: self.arg = "" self.file_manager = "explorer.exe" self._launch_file_manager(uris_or_paths=self.wsl_windows_directories) if ( not self.locations and not self.directories and not self.wsl_windows_paths and not self.wsl_windows_directories ): self.arg = "" self._launch_file_manager(uris_or_paths=[""]) showinfilemanager-1.1.6/src/showinfm/py.typed000066400000000000000000000000001514476644400213460ustar00rootroot00000000000000showinfilemanager-1.1.6/src/showinfm/showinfm.py000066400000000000000000000175501514476644400220750ustar00rootroot00000000000000# SPDX-FileCopyrightText: 2016-2026 Damon Lynch # SPDX-License-Identifier: MIT """ Show in File Manager Open the system file manager and optionally select files in it. """ import shutil import sys from collections.abc import Sequence import showinfm.filemanager from showinfm.argumentsparse import get_parser from showinfm.constants import Platform from showinfm.system import current_platform, is_wsl, is_wsl1, is_wsl2, linux, windows _file_manager = showinfm.filemanager.FileManager() def stock_file_manager() -> str: """ Get stock file manager for this operating system / desktop. On Windows the default is `explorer.exe`. On Linux the first step is to determine which desktop is running, and from that lookup its default file manager. On macOS, the default is finder, accessed via the command 'open'. Exceptions are not caught. :return: executable name """ if current_platform == Platform.windows or is_wsl1: file_manager = "explorer.exe" elif current_platform == Platform.linux: file_manager = linux.stock_linux_file_manager() elif current_platform == Platform.macos: file_manager = "open" else: raise NotImplementedError assert shutil.which(file_manager) is not None return file_manager def user_file_manager() -> str: """ Get the file manager as set by the user. The file manager executable is tested to see if it exists. Exceptions are not caught. :return: executable name """ if current_platform == Platform.windows or is_wsl1: file_manager = "explorer.exe" elif current_platform == Platform.linux: file_manager = linux.user_linux_file_manager() elif current_platform == Platform.macos: file_manager = "open" else: raise NotImplementedError assert shutil.which(file_manager) is not None return file_manager def valid_file_manager() -> str: """ Get user's file manager, falling back to using sensible defaults. The user's choice of file manager is the default choice. However, this is not always set correctly. On Linux, it most likely is because the user's distro has not correctly set the default file manager. If the user's choice is unrecognized by this package, then reject it and choose the standard file manager for the detected desktop environment. All exceptions are caught, except those if this platform is not supported by this package. :return: If the user's default file manager is set and it is recognized as valid by this package, then return it. Otherwise return the stock file manager, if it exists. """ return showinfm.filemanager.valid_file_manager() def show_in_file_manager( path_or_uri: str | Sequence[str] | None = None, open_not_select_directory: bool = True, file_manager: str | None = None, allow_conversion: bool = True, verbose: bool = False, debug: bool = False, ) -> None: """ Open the file manager and show zero or more directories or files in it. The path_or_uri is a sequence of items, or a single item. An item can be a regular path, or a URI. On non-Windows platforms, regular paths will be converted to URIs when passed as command line arguments to the file manager, because some file managers do not handle regular paths correctly. However, URIs will be convereted to paths to handle file managers that do not accept URIs. On Windows, Explorer is called using the Win32 API. On WSL1, all paths are opened using Windows Explorer. URIs can be specified using Linux or Windows formats. All formats are automatically converted to use the Windows URI format. WSL2 functions the same as WSL1, except if the WSL2 instance has a Linux file manager installed. On these systems, if a path on Linux is specified, that file manager will be used instead of Windows Explorer. Override this default behavior by using the parameter file_manager. The most common use of this function is to call it without specifying the file manager to use, which defaults to the value returned by valid_file_manager() For file managers unable to select files to display, the file manager will instead display the contents of the path. For file managers that can handle file selections, but only one at a time, multiple file manager windows will be opened. If a file manager executable is specified and this package does not recognize it, the executable will be called with the files as the only command line arguments. :param path_or_uri: zero or more files or directories to open, specified as a single URI or valid path, or a sequence of URIs/paths. :param open_not_select_directory: if the URI or path is a directory and not a file, open the directory itself in the file manager, rather than selecting it and displaying it in its parent directory. :param file_manager: executable name to use. If not specified, then valid_file_manager() will determine which file manager to use. :param allow_conversion: allow this function to automatically convert paths and URIs to the format needed by the file manager that will be called. Set to False if passing non-standard URIs. Ignored when running under WSL. :param verbose: if True print command to be executed before launching it :param debug: if True print debugging information to stderr """ global _file_manager _file_manager.show_in_file_manager( path_or_uri, open_not_select_directory, file_manager, allow_conversion, verbose, debug, ) class Diagnostics: """ Collect basic diagnostics information for this package. """ def __init__(self) -> None: self.desktop: linux.LinuxDesktop | None self.wsl_version: str try: self.stock_file_manager = stock_file_manager() except Exception as e: self.stock_file_manager = str(e) try: self.user_file_manager = user_file_manager() except Exception as e: self.user_file_manager = str(e) try: self.valid_file_manager = valid_file_manager() except Exception as e: self.valid_file_manager = str(e) if current_platform == Platform.linux: try: self.desktop = linux.linux_desktop() except Exception: self.desktop = linux.LinuxDesktop.unknown else: self.desktop = None if is_wsl: if is_wsl2: self.wsl_version = "2" else: self.wsl_version = "1" else: self.wsl_version = "" def __str__(self) -> str: desktop = f"Linux Desktop: {self.desktop.name}\n" if self.desktop else "" wsl = f"WSL version {self.wsl_version}\n" if self.wsl_version else "" file_managers = ( f"Stock: {self.stock_file_manager}\n" f"User's choice: {self.user_file_manager}\n" f"Valid: {self.valid_file_manager}" ) return desktop + wsl + file_managers def main() -> None: parser = get_parser() args = parser.parse_args() verbose = args.verbose debug = args.debug if debug: print(Diagnostics()) verbose = True if current_platform == Platform.windows and not is_wsl: path_or_uri = windows.parse_command_line_arguments(args.path) else: path_or_uri = args.path open_not_select_directory = not args.select_folder try: show_in_file_manager( file_manager=args.file_manager, path_or_uri=path_or_uri, verbose=verbose, open_not_select_directory=open_not_select_directory, debug=debug, ) except Exception as e: sys.stderr.write(str(e)) if args.debug: raise showinfilemanager-1.1.6/src/showinfm/system/000077500000000000000000000000001514476644400212055ustar00rootroot00000000000000showinfilemanager-1.1.6/src/showinfm/system/__init__.py000066400000000000000000000013331514476644400233160ustar00rootroot00000000000000# SPDX-FileCopyrightText: 2021-2026 Damon Lynch # SPDX-License-Identifier: MIT import platform from ..constants import Platform from . import linux current_platform: Platform | None system = platform.system() is_wsl: bool = False is_wsl1: bool = False is_wsl2: bool = False if system == "Windows": current_platform = Platform.windows elif system == "Linux": current_platform = Platform.linux if linux.detect_wsl(): is_wsl = True if linux.wsl_version() == linux.LinuxDesktop.wsl2: is_wsl2 = True else: is_wsl1 = True elif system == "Darwin": current_platform = Platform.macos else: current_platform = None raise NotImplementedError showinfilemanager-1.1.6/src/showinfm/system/linux.py000066400000000000000000000577521514476644400227360ustar00rootroot00000000000000# SPDX-FileCopyrightText: 2016-2026 Damon Lynch # SPDX-License-Identifier: MIT import functools import os import re import shlex import shutil import subprocess import urllib.request from enum import Enum from pathlib import Path, PureWindowsPath from typing import NamedTuple, Optional from urllib.parse import unquote, urlparse import packaging.version try: import xdg # type: ignore from xdg import BaseDirectory from xdg.DesktopEntry import DesktopEntry # type: ignore have_xdg = True except ImportError: have_xdg = False from showinfm.constants import FileManagerType _linux_desktop: Optional["LinuxDesktop"] = None def stock_linux_file_manager() -> str: """ Get stock (system default) file manager for the desktop environment. Looks up value only if the desktop environment can be determined. All exceptions are raised. :return: executable name """ global _linux_desktop if _linux_desktop is None: _linux_desktop = linux_desktop() desktop = _linux_desktop.name try: desktop = LinuxDesktopFamily.get(desktop) or desktop return StandardLinuxFileManager[desktop] except KeyError: raise Exception(f"The desktop {desktop} is unknown") def user_linux_file_manager() -> str: """ Determine the file manager for this desktop as set by the user. xdg-mime is used to get a .desktop file, from which the executable name is extracted. The executable is not examined to see if it is valid or if it even exists. All exceptions are raised. :return: executable name """ if not have_xdg: raise Exception( "xdg utilities and/or the python binding for xdg are not installed" ) xdg_cmd = "xdg-mime query default inode/directory" cmd = shlex.split(xdg_cmd) try: desktop_file: str = subprocess.check_output(cmd, universal_newlines=True) except Exception: raise Exception(f"Could not determine file manager using {xdg_cmd}") # Remove new line character from output desktop_file = desktop_file[:-1] if desktop_file.endswith(";"): desktop_file = desktop_file[:-1] for desktop_path in (Path(d) / "applications" for d in BaseDirectory.xdg_data_dirs): path = desktop_path / desktop_file if path.exists(): p = str(path) try: desktop_entry = DesktopEntry(p) except Exception: raise Exception(f"Could not open desktop entry at {p}") try: desktop_entry.parse(p) except xdg.Exceptions.ParsingError: raise Exception(f"Could not parse desktop entry at {p}") except Exception: raise Exception(f"Desktop entry at {p} might be malformed") fm = desktop_entry.getExec() # Strip away any extraneous arguments fm_cmd = fm.split()[0] # Strip away any path information fm_cmd = Path(fm_cmd).name # Strip away any quotes fm_cmd = fm_cmd.replace('"', "") fm_cmd = fm_cmd.replace("'", "") return fm_cmd return "" def valid_linux_file_manager() -> str: """ Get user's file manager, falling back to using sensible defaults for the particular desktop environment. All exceptions are caught. :return: If the user's default file manager is set and it is known by this module, then return it. Otherwise return the stock file manager, if it exists. """ try: stock = stock_linux_file_manager() except Exception: stock = "" try: user_fm = user_linux_file_manager() except Exception: user_fm = "" else: if user_fm not in known_linux_file_managers(): user_fm = "" if not (user_fm or stock): return "" fm = user_fm if user_fm else stock if fm and shutil.which(fm): return fm else: return "" def known_linux_file_managers() -> tuple[str, ...]: """ Generate a collection of Linux file managers this module knows about :return: tuple of executable names """ return tuple(LinuxFileManagerBehavior.keys()) def linux_file_manager_type(file_manager: str) -> FileManagerType: """ Determine the type of command line arguments the Linux file manager expects :param file_manager: executable name :return: FileManagerType matching with the executable name, else FileManagerType.regular as a fallback """ if file_manager == "caja" and caja_supports_select(): return FileManagerType.select return LinuxFileManagerBehavior.get(file_manager, FileManagerType.regular) def caja_version() -> packaging.version.Version | None: """ Get the version of Caja via a command line switch :return: parsed ver """ try: version_string = ( subprocess.run(["caja", "--version"], stdout=subprocess.PIPE, check=True) .stdout.decode() .strip() ) except subprocess.CalledProcessError: raise Exception("Failed to get version number from caja") result = re.search(r"\d", version_string) if result is None: return None version = version_string[result.start() :] return packaging.version.parse(version) def caja_supports_select() -> bool: """ Determine if caja supports --select command line switch. :return: True if caja version is >= version 1.26 """ try: version = caja_version() except Exception: return False if version is None: return False return version >= packaging.version.Version("1.26") def translate_wsl_path(path: str, from_windows_to_wsl: bool) -> str: """ Use the WSL command wslpath to translate between Windows and WSL paths. Uses subprocesss. Exceptions are not caught. :param path: path to convert in string format :param from_windows_to_wsl: whether to translate from Windows to WSL (True), or WSL to Windows (False) :return: the translated path """ arg = "-u" if from_windows_to_wsl else "-w" return ( subprocess.run(["wslpath", arg, path], capture_output=True, check=True) .stdout.decode() .strip() ) def wsl_path_is_for_windows(path_or_uri: str) -> bool: """ When running in WSL, detect if the path being passed is in Windows or Linux Reference: https://docs.microsoft.com/en-us/windows/wsl/filesystems :param path_or_uri: :return: """ if path_or_uri.startswith("file://"): # Assume valid URI # Look for drive letter, windows style if path_or_uri[3:4].isalpha() and path_or_uri[4:5] == ":": # noqa: SIM114 return True # Look for UNC host name: anything that does not start with a leading / elif path_or_uri[7].isalpha(): return True return False else: drive = PureWindowsPath(path_or_uri).drive if drive: # C:\ if drive[0].isalpha() and drive[1] == ":": return True # UNC share if drive[:2] == r"\\": return True # Assume anything under /mnt is Windows return path_or_uri.startswith("/mnt") class WSLTransformPathURI(NamedTuple): is_win_location: bool | None win_uri: str | None win_path: str | None linux_path: str | None is_dir: bool | None exists: bool def wsl_transform_path_uri( path_or_uri: str, generate_win_path: bool ) -> WSLTransformPathURI: r""" Transforms URI or path into path and URI suitable for working with WSL. Detects if working with path or URI, and whether it is POSIX or Windows Assumes all paths mounted on /mnt are located in Windows. :param path_or_uri: path or URI to examine :param generate_win_path: if passed a Linux path, generate path and URI for use in Windows. Will do so anyway if the path is located in Windows, not the Linux instance. :return: Named Tuple containing values in WSLTranformPathURI: is_win_location: if the path/URI is located within a Windows file system win_uri is: the URI as it appears to Windows win_path: the path as it appears to Windows linux_path: the path as it appears to Linux is_dir: whether the path/URI is a directory exists: whether the path/URI exists >>> import platform >>> assert platform.system() == "Linux" >>> r = wsl_transform_path_uri("file:///c:/Program%20Files/Common%20Files/", True) >>> r.win_path 'C:\\Program Files\\Common Files' >>> r.is_win_location True >>> r.linux_path '/mnt/c/Program Files/Common Files' >>> r.win_uri 'file:///c:/Program%20Files/Common%20Files/' >>> r.is_win_location True >>> r.is_dir if r.exists else True True >>> r = wsl_transform_path_uri("file:///c:/Program%20Files/Common%20Files", True) >>> r.win_uri 'file:///c:/Program%20Files/Common%20Files/' >>> r = wsl_transform_path_uri("file:///c:/Program Files/Common Files", True) >>> r.win_uri 'file:///c:/Program%20Files/Common%20Files/' >>> f ="file:///c:/Program%20Files/Barrier/barrier.conf" >>> r = wsl_transform_path_uri(f, True) >>> r.win_path 'C:\\Program Files\\Barrier\\barrier.conf' >>> r.is_win_location True >>> r.linux_path '/mnt/c/Program Files/Barrier/barrier.conf' >>> r.is_dir if r.exists else False False >>> r.win_uri 'file:///c:/Program%20Files/Barrier/barrier.conf' >>> from pathlib import Path >>> home = Path.home() >>> r = wsl_transform_path_uri(f"file://localhost{home}/.bashrc", True) >>> r.is_dir if r.exists else False False >>> r.is_win_location False >>> r.linux_path # doctest: +ELLIPSIS '/home/.../.bashrc' >>> r.win_path # doctest: +ELLIPSIS '\\\\...\\...\\home\\...\\.bashrc' >>> r.win_uri # doctest: +ELLIPSIS 'file://.../.../home/.../.bashrc' >>> r = wsl_transform_path_uri(f"file://{home}/.bashrc", True) >>> r.is_dir False >>> r.is_win_location False >>> r.linux_path # doctest: +ELLIPSIS '/home/.../.bashrc' >>> r.win_path # doctest: +ELLIPSIS '\\\\...\\...\\home\\...\\.bashrc' >>> r.win_uri # doctest: +ELLIPSIS 'file://.../.../home/.../.bashrc' >>> r = wsl_transform_path_uri(f"file://localhost{home}/my%20file.txt", True) >>> r.is_dir if r.exists else False False >>> f = r'\\wsl.localhost\Ubuntu-20.04\home\damon\my file.txt' >>> r.win_path if r.exists else f # doctest: +ELLIPSIS '\\\\...\\...\\home\\...\\my file.txt' >>> r.is_win_location False >>> f = 'file://wsl$/Ubuntu-20.04/home/damon/my%20file.txt' >>> r.win_uri if r.exists else f # doctest: +ELLIPSIS 'file://.../.../home/.../my%20file.txt' >>> r = wsl_transform_path_uri("file:///etc/fstab", True) >>> r.exists True >>> r.is_dir False >>> r.linux_path '/etc/fstab' >>> r.is_win_location False >>> r.win_path # doctest: +ELLIPSIS '\\\\...\\...\\etc\\fstab' >>> r.win_uri # doctest: +ELLIPSIS 'file://.../.../etc/fstab' >>> r = wsl_transform_path_uri("file:/etc/fstab", True) >>> r.exists True >>> r.is_dir False >>> r.linux_path '/etc/fstab' >>> r.is_win_location False >>> r.win_path # doctest: +ELLIPSIS '\\\\...\\...\\etc\\fstab' >>> r.win_uri # doctest: +ELLIPSIS 'file://.../.../etc/fstab' >>> r = wsl_transform_path_uri("file:///etc", True) >>> r.exists True >>> r.is_dir True >>> r.linux_path '/etc' >>> r.is_win_location False >>> r.win_path # doctest: +ELLIPSIS '\\\\...\\...\\etc' >>> r.win_uri # doctest: +ELLIPSIS 'file://.../.../etc/' >>> r = wsl_transform_path_uri("file:///etc", False) >>> r.win_path >>> r.win_uri >>> r = wsl_transform_path_uri(f"file://{home}/dir with spaces", True) >>> r.is_dir if r.exists else True True >>> r.linux_path # doctest: +ELLIPSIS '/home/.../dir with spaces' >>> r.is_win_location False >>> f = '\\\\wsl.localhost\\openSUSE-Leap-15.3\\home\\damon\\dir with spaces' >>> r.win_path if r.exists else f # doctest: +ELLIPSIS '\\\\...\\...\\home\\...\\dir with spaces' >>> f = 'file://wsl.localhost/openSUSE-Leap-15.3/home/damon/dir%20with%20spaces/' >>> r.win_uri if r.exists else f # doctest: +ELLIPSIS 'file://.../.../home/.../dir%20with%20spaces/' >>> r = wsl_transform_path_uri("file:///c:/Program%20Files/Common%20Files", True) >>> r.is_win_location True >>> r.win_path 'C:\\Program Files\\Common Files' >>> r.linux_path '/mnt/c/Program Files/Common Files' >>> r = wsl_transform_path_uri("file:///mnt/c/Program%20Files/", True) >>> r.is_win_location True >>> r.exists True >>> r.is_dir True >>> r.win_path 'C:\\Program Files' >>> r.linux_path '/mnt/c/Program Files' >>> r.win_uri 'file:///c:/Program%20Files/' >>> r = wsl_transform_path_uri(r"C:\Program Files", True) >>> r.is_win_location True >>> r.exists True >>> r.is_dir True >>> r.win_path 'C:\\Program Files' >>> r.linux_path '/mnt/c/Program Files' >>> r.win_uri 'file:///c:/Program%20Files/' >>> import os >>> import pwd >>> user = pwd.getpwuid(os.getuid())[0] >>> f = f"\\\\wsl.localhost\\openSUSE-Leap-15.3\\home\\{user}\\My Photos" >>> r = wsl_transform_path_uri(f, True) >>> r.is_win_location if r.exists else False False >>> r.win_path # doctest: +ELLIPSIS '\\\\wsl.localhost\\openSUSE-Leap-15.3\\home\\...\\My Photos' >>> r.win_uri # doctest: +ELLIPSIS 'file://wsl.localhost/openSUSE-Leap-15.3/home/.../My%20Photos...' >>> r.linux_path if r.exists else "/home/damon/My Photos" # doctest: +ELLIPSIS '/home/.../My Photos' >>> r = wsl_transform_path_uri("/mnt/c/Program Files/", True) >>> r.is_win_location True >>> r.exists True >>> r.is_dir True >>> r.win_path 'C:\\Program Files' >>> r.linux_path '/mnt/c/Program Files' >>> r.win_uri 'file:///c:/Program%20Files/' >>> r = wsl_transform_path_uri(str(home), True) >>> r.is_win_location False >>> r.exists True >>> r.is_dir True >>> r.win_path # doctest: +ELLIPSIS '\\\\...\\...\\home\\...' >>> r.linux_path # doctest: +ELLIPSIS '/home/...' >>> r.win_uri # doctest: +ELLIPSIS 'file://.../.../home/.../' >>> r = wsl_transform_path_uri(f"{home}/dir with spaces", True) >>> r.is_win_location False >>> r.is_dir if r.exists else True True >>> r.linux_path # doctest: +ELLIPSIS '/home/.../dir with spaces' >>> f = '\\\\wsl.localhost\\openSUSE-Leap-15.3\\home\\damon\\dir with spaces' >>> r.win_path if r.exists else f # doctest: +ELLIPSIS '\\\\...\\...\\home\\...\dir with spaces' >>> f = 'file://wsl.localhost/openSUSE-Leap-15.3/home/damon/dir%20with%20spaces/' >>> r.win_uri if r.exists else f # doctest: +ELLIPSIS 'file://.../.../home/.../dir%20with%20spaces/' >>> r = wsl_transform_path_uri(f"{home}/.bashrc", True) >>> r.is_win_location False >>> r.is_dir if r.exists else False False >>> r.linux_path # doctest: +ELLIPSIS '/home/.../.bashrc' >>> r.win_path # doctest: +ELLIPSIS '\\\\...\\...\\home\\...\.bashrc' >>> r.win_uri # doctest: +ELLIPSIS 'file://.../.../home/.../.bashrc' >>> cwd = os.getcwd() >>> os.chdir(home) >>> r = wsl_transform_path_uri(".bashrc", True) >>> r.linux_path # doctest: +ELLIPSIS '/home/.../.bashrc' >>> r.win_path # doctest: +ELLIPSIS '\\\\...\\...\\home\\...\.bashrc' >>> r.is_dir if r.exists else False False >>> r.is_win_location False >>> r.win_uri # doctest: +ELLIPSIS 'file://.../.../home/.../.bashrc' >>> os.chdir(cwd) """ win_uri: str | None = None win_path: str | None = None linux_path: str | None = None is_dir: bool | None = None exists: bool = False if path_or_uri.startswith("file:/"): is_win_uri = False parsed = urlparse(url=path_or_uri) path = unquote(parsed.path) netloc = parsed.netloc if len(path) > 2 and path[0] == "/" and path[1].isalpha() and path[2] == ":": is_win_uri = True # Remove first forward slash from e.g. /c:/Program Files path = path[1:] elif netloc and netloc != "localhost": is_win_uri = True if is_win_uri: win_uri = path_or_uri.replace(" ", "%20") win_path = path try: linux_path = translate_wsl_path(path, from_windows_to_wsl=True) except subprocess.CalledProcessError: exists = False else: linux_path = path else: path = path_or_uri if path.startswith("/") and not path.startswith("/mnt"): linux_path = path else: # Path must be either a Windows style path, or a relative path on Posix. # First, check if the path is Windows style, e.g. C:\Program Files # Note that UNC shares are also considered drives drive = PureWindowsPath(path).drive is_unc = drive.startswith("\\\\") if (drive and drive[0].isalpha() and drive[1] == ":") or is_unc: win_path = path try: linux_path = translate_wsl_path(path=path, from_windows_to_wsl=True) except subprocess.CalledProcessError: exists = False # Generate Windows URI if is_unc: wuri = urllib.request.pathname2url(path.replace("\\", "/")) win_uri = f"file:{wuri}" elif linux_path is not None: win_uri = wsl_path_to_uri_for_windows_explorer(linux_path) else: # relative path was passed linux_path = str(Path(path).resolve()) if linux_path is None: is_win_location = None else: lpath = Path(linux_path) exists = lpath.exists() is_win_location = linux_path.startswith("/mnt/") if exists: is_dir = lpath.is_dir() if generate_win_path or is_win_location: try: win_path = translate_wsl_path( path=linux_path, from_windows_to_wsl=False ) except subprocess.CalledProcessError: exists = False if win_path and not win_uri: if not is_win_location: wuri = urllib.request.pathname2url(win_path.replace("\\", "/")) win_uri = f"file:{wuri}" else: win_uri = wsl_path_to_uri_for_windows_explorer(linux_path) if is_dir: if linux_path is not None and linux_path[-1] == "/": linux_path = linux_path[:-1] if win_path is not None and win_path[-1] == "\\": win_path = win_path[:-1] if win_uri is not None and win_uri[-1] != "/": win_uri = f"{win_uri}/" if linux_path is not None: assert is_win_location is not None if win_uri is not None: assert win_path is not None if exists: assert is_dir is not None return WSLTransformPathURI( is_win_location=is_win_location, win_uri=win_uri, win_path=win_path, linux_path=linux_path, is_dir=is_dir, exists=exists, ) def wsl_path_to_uri_for_windows_explorer(path: str) -> str: r""" Convert a path to a URI accepted by Windows Explorer. Windows URIs are different from Linux URIs. As Wikipedia points out, Window specifies file:///c:/path/to/the%20file.txt (note the three slashes after file:), and file://hostname/path/to/the%20file.txt :param path: path format like '/mnt/c/some/path', '/home/user', 'C:\some\path' :return: a file URI accepted by Windows Explorer """ assert not path.startswith("\\\\") assert path.startswith("/mnt/") path = urllib.request.pathname2url(path) # Remove the /mnt portion, keep the drive letter, and insert a colon return f"file://{path[4:6]}:{path[6:]}" class LinuxDesktop(Enum): gnome = 1 unity = 2 cinnamon = 3 kde = 4 xfce = 5 mate = 6 lxde = 7 lxqt = 8 ubuntugnome = 9 popgnome = 10 deepin = 11 zorin = 12 ukui = 13 # Kylin pantheon = 14 enlightenment = 15 wsl = 16 wsl2 = 17 cutefish = 18 lumina = 19 unknown = 20 cosmic = 21 LinuxDesktopHumanize = dict( gnome="Gnome", unity="Unity", cinnamon="Cinnamon", kde="KDE", xfce="XFCE", mate="Mate", lxde="LXDE", lxqt="LxQt", ubuntugnome="Ubuntu Gnome", popgnome="Pop Gnome", deepin="Deepin", zorin="Zorin", ukui="UKUI", pantheon="Pantheon", enlightenment="Enlightenment", wsl="WSL1", wsl2="WSL2", cutefish="Cutefish", lumina="Lumina", unknown="Unknown", cosmic="Cosmic", ) LinuxDesktopFamily = dict( ubuntugnome="gnome", popgnome="gnome", zorin="gnome", unity="gnome", ) StandardLinuxFileManager = dict( gnome="nautilus", kde="dolphin", cinnamon="nemo", mate="caja", xfce="thunar", lxde="pcmanfm", lxqt="pcmanfm-qt", deepin="dde-file-manager", pantheon="io.elementary.files", ukui="peony", enlightenment="pcmanfm", wsl="explorer.exe", wsl2="explorer.exe", cutefish="cutefish-filemanager", lumina="lumina-fm", cosmic="cosmic-files", ) LinuxFileManagerBehavior = dict( nautilus=FileManagerType.select, dolphin=FileManagerType.select, caja=FileManagerType.dir_only_uri, thunar=FileManagerType.dir_only_uri, nemo=FileManagerType.regular, pcmanfm=FileManagerType.dir_only_uri, peony=FileManagerType.show_items, index=FileManagerType.dir_only_uri, doublecmd=FileManagerType.dual_panel, krusader=FileManagerType.dir_only_uri, spacefm=FileManagerType.dir_only_uri, fman=FileManagerType.dual_panel, ) LinuxFileManagerBehavior["pcmanfm-qt"] = FileManagerType.dir_only_uri LinuxFileManagerBehavior["dde-file-manager"] = FileManagerType.show_item LinuxFileManagerBehavior["io.elementary.files"] = FileManagerType.regular LinuxFileManagerBehavior["cutefish-filemanager"] = FileManagerType.dir_only_uri LinuxFileManagerBehavior["lumina-fm"] = FileManagerType.dir_only_uri LinuxFileManagerBehavior["cosmic-files"] = FileManagerType.regular def wsl_version() -> LinuxDesktop | None: with open("/proc/version") as f: p = f.read() if p.find("microsoft") > 0 and p.find("WSL2"): return LinuxDesktop.wsl2 if p.find("Microsoft") > 0: return LinuxDesktop.wsl return None def detect_wsl() -> bool: with open("/proc/version") as f: p = f.read() return p.lower().find("microsoft") > 0 @functools.cache def linux_desktop() -> LinuxDesktop: """ Determine Linux desktop environment :return: enum representing desktop environment, Desktop.unknown if unknown. """ try: env = os.getenv("XDG_CURRENT_DESKTOP").lower() # type: ignore except AttributeError: wsl = wsl_version() if wsl is not None: return wsl else: raise Exception("The value for XDG_CURRENT_DESKTOP is not set") if env in ("unity:unity7", "unity:unity7:ubuntu"): env = "unity" elif env == "x-cinnamon": env = "cinnamon" elif env == "ubuntu:gnome": env = "ubuntugnome" elif env == "pop:gnome": env = "popgnome" elif env in ("gnome-classic:gnome", "budgie:gnome"): env = "gnome" elif env == "zorin:gnome": env = "zorin" elif env == "kde:plasma": env = "kde" try: return LinuxDesktop[env] except KeyError: raise Exception(f"The desktop environment {env} is unknown") def linux_desktop_humanize(desktop: LinuxDesktop) -> str: """ Make LinuxDesktop name human readable. :return: desktop name spelled out """ return LinuxDesktopHumanize[desktop.name] showinfilemanager-1.1.6/src/showinfm/system/tools.py000066400000000000000000000066021514476644400227230ustar00rootroot00000000000000# SPDX-FileCopyrightText: 2008-2026 Damon Lynch # SPDX-FileCopyrightText: 2008-2021 The pip developers # SPDX-License-Identifier: MIT import os import re import shlex from collections import defaultdict from pathlib import Path from urllib.parse import urljoin, urlparse from urllib.request import pathname2url, url2pathname from showinfm.constants import Platform, cannot_open_uris from showinfm.system import current_platform, urivalidate def filemanager_requires_path(file_manager: str) -> bool: return current_platform == Platform.windows or file_manager in cannot_open_uris def is_uri(path_uri: str) -> bool: """ Checks if string is probably a uri of some kind. :param path_uri: the :return: True if probably a URI, else False """ if path_uri and path_uri.startswith("camera:/"): return True return re.match("^%s$" % urivalidate.URI, path_uri, re.VERBOSE) is not None def quote_path(path: Path) -> Path: """ Quote path in a way that works with file managers on Windows and Unix-like. If path is already quoted, returns it as is. Replaces single quoted string with double quotes on Windows. Uses shlex.quote on non-Windows platforms, but again only if the path is not already quoted. :param path: path to quote, if necessary :return: double-quoted path """ p = str(path) if current_platform == Platform.windows: # Double quotes are not allowed in paths names - they are used for quoting if re.match("""'(.*)'""", p) is not None: # Replace single quotes with double quotes return Path(f'"{p[1:-1]}"') if re.match(r"""\"(.*)\"""", p) is None: # Add double quotes where there was no quoting at all return Path(f'"{path}"') else: if not (p[0] in ('"', "'") and p[-1] == p[0]): return Path(shlex.quote(p)) return path def path_to_file_uri(path: str) -> str: """ Convert a path to a file: URL. The path will be made absolute and have quoted path parts. Taken from pip: https://github.com/pypa/pip/blob/main/src/pip/_internal/utils/urls.py Copyright (c) 2008-2021 The pip developers """ path = os.path.normpath(os.path.abspath(path)) url = urljoin("file:", pathname2url(path)) return url def file_uri_to_path(uri: str) -> str: """ Convert a file: URL to a path. On Windows this is more reliable than urllib.parse.urlparse, because that fails when run with a URI like file:///D:/some/directory Taken from https://stackoverflow.com/a/61922504/592623 and modified by Damon Lynch 2021, 2024 """ parsed = urlparse(uri) host = f"{os.path.sep}{os.path.sep}{parsed.netloc}{os.path.sep}" p = os.path.normpath(os.path.join(host, url2pathname(parsed.path))) return p def directories_and_their_files(paths: list[str]) -> defaultdict[str, list[str]]: """ Group paths into directories and their files. If path is a directory, the parent will be the directory, and the subfolder will be the child of that directory. :param paths: list of paths :return: default dict of folders with list of their files """ if isinstance(paths, str): paths = [paths] folder_contents = defaultdict(list) for path in paths: p = Path(path) folder_contents[str(p.parent)].append(p.name) return folder_contents showinfilemanager-1.1.6/src/showinfm/system/urivalidate.py000066400000000000000000000421041514476644400240710ustar00rootroot00000000000000# Source: https://gist.github.com/mnot/138549 # Converted from Python2 using https://www.pythonconverter.com/ # Not formatted using black """ Regex for URIs These regex are directly derived from the collected ABNF in RFC3986 (except for DIGIT, ALPHA and HEXDIG, defined by RFC2234). Additional regex are defined to validate the following schemes according to their respective specifications: - http - https - file - data - gopher - ws - wss - mailto See FIXME for areas that still need work. They should be processed with re.VERBOSE. """ __license__ = """ Copyright (c) 2009-2015 Mark Nottingham (code portions) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ ### basics - DIGIT = r"[\x30-\x39]" ALPHA = r"[\x41-\x5A\x61-\x7A]" HEXDIG = r"[\x30-\x39A-Fa-f]" DQUOTE = r"\"" # pct-encoded = "%" HEXDIG HEXDIG pct_encoded = r" %% %(HEXDIG)s %(HEXDIG)s" % locals() # unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" unreserved = r"(?: %(ALPHA)s | %(DIGIT)s | \- | \. | _ | ~ )" % locals() # gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@" gen_delims = r"(?: : | / | \? | \# | \[ | \] | @ )" # sub-delims = "!" / "$" / "&" / "'" / "(" / ")" # / "*" / "+" / "," / ";" / "=" sub_delims = r"""(?: ! | \$ | & | ' | \( | \) | \* | \+ | , | ; | = )""" # pchar = unreserved / pct-encoded / sub-delims / ":" / "@" pchar = r"(?: %(unreserved)s | %(pct_encoded)s | %(sub_delims)s | : | @ )" % locals() # reserved = gen-delims / sub-delims reserved = r"(?: %(gen_delims)s | %(sub_delims)s )" % locals() ### scheme # scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) scheme = r"%(ALPHA)s (?: %(ALPHA)s | %(DIGIT)s | \+ | \- | \. )*" % locals() ### authority # dec-octet = DIGIT ; 0-9 # / %x31-39 DIGIT ; 10-99 # / "1" 2DIGIT ; 100-199 # / "2" %x30-34 DIGIT ; 200-249 # / "25" %x30-35 ; 250-255 dec_octet = r"""(?: %(DIGIT)s | [\x31-\x39] %(DIGIT)s | 1 %(DIGIT)s{2} | 2 [\x30-\x34] %(DIGIT)s | 25 [\x30-\x35] ) """ % locals() # IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet IPv4address = r"%(dec_octet)s \. %(dec_octet)s \. %(dec_octet)s \. %(dec_octet)s" % locals() # h16 = 1*4HEXDIG h16 = r"(?: %(HEXDIG)s ){1,4}" % locals() # ls32 = ( h16 ":" h16 ) / IPv4address ls32 = r"(?: (?: %(h16)s : %(h16)s ) | %(IPv4address)s )" % locals() # IPv6address = 6( h16 ":" ) ls32 # / "::" 5( h16 ":" ) ls32 # / [ h16 ] "::" 4( h16 ":" ) ls32 # / [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32 # / [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32 # / [ *3( h16 ":" ) h16 ] "::" h16 ":" ls32 # / [ *4( h16 ":" ) h16 ] "::" ls32 # / [ *5( h16 ":" ) h16 ] "::" h16 # / [ *6( h16 ":" ) h16 ] "::" IPv6address = r"""(?: (?: %(h16)s : ){6} %(ls32)s | :: (?: %(h16)s : ){5} %(ls32)s | (?: %(h16)s )? :: (?: %(h16)s : ){4} %(ls32)s | (?: (?: %(h16)s : ){0,1} %(h16)s )? :: (?: %(h16)s : ){3} %(ls32)s | (?: (?: %(h16)s : ){0,2} %(h16)s )? :: (?: %(h16)s : ){2} %(ls32)s | (?: (?: %(h16)s : ){0,3} %(h16)s )? :: %(h16)s : %(ls32)s | (?: (?: %(h16)s : ){0,4} %(h16)s )? :: %(ls32)s | (?: (?: %(h16)s : ){0,5} %(h16)s )? :: %(h16)s | (?: (?: %(h16)s : ){0,6} %(h16)s )? :: ) """ % locals() # IPvFuture = "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" ) IPvFuture = r"v %(HEXDIG)s+ \. (?: %(unreserved)s | %(sub_delims)s | : )+" % locals() # IP-literal = "[" ( IPv6address / IPvFuture ) "]" IP_literal = r"\[ (?: %(IPv6address)s | %(IPvFuture)s ) \]" % locals() # reg-name = *( unreserved / pct-encoded / sub-delims ) reg_name = r"(?: %(unreserved)s | %(pct_encoded)s | %(sub_delims)s )*" % locals() # userinfo = *( unreserved / pct-encoded / sub-delims / ":" ) userinfo = r"(?: %(unreserved)s | %(pct_encoded)s | %(sub_delims)s | : )*" % locals() # host = IP-literal / IPv4address / reg-name host = r"(?: %(IP_literal)s | %(IPv4address)s | %(reg_name)s )" % locals() # port = *DIGIT port = r"(?: %(DIGIT)s )*" % locals() # authority = [ userinfo "@" ] host [ ":" port ] authority = r"(?: %(userinfo)s @)? %(host)s (?: : %(port)s)?" % locals() ### Path # segment = *pchar segment = r"%(pchar)s*" % locals() # segment-nz = 1*pchar segment_nz = r"%(pchar)s+" % locals() # segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" ) # ; non-zero-length segment without any colon ":" segment_nz_nc = r"(?: %(unreserved)s | %(pct_encoded)s | %(sub_delims)s | @ )+" % locals() # path-abempty = *( "/" segment ) path_abempty = r"(?: / %(segment)s )*" % locals() # path-absolute = "/" [ segment-nz *( "/" segment ) ] path_absolute = r"/ (?: %(segment_nz)s (?: / %(segment)s )* )?" % locals() # path-noscheme = segment-nz-nc *( "/" segment ) path_noscheme = r"%(segment_nz_nc)s (?: / %(segment)s )*" % locals() # path-rootless = segment-nz *( "/" segment ) path_rootless = r"%(segment_nz)s (?: / %(segment)s )*" % locals() # path-empty = 0 path_empty = r"" ### FIXME # path = path-abempty ; begins with "/" or is empty # / path-absolute ; begins with "/" but not "//" # / path-noscheme ; begins with a non-colon segment # / path-rootless ; begins with a segment # / path-empty ; zero characters path = r"""(?: %(path_abempty)s | %(path_absolute)s | %(path_noscheme)s | %(path_rootless)s | %(path_empty)s ) """ % locals() ### Query and Fragment # query = *( pchar / "/" / "?" ) query = r"(?: %(pchar)s | / | \? )*" % locals() # fragment = *( pchar / "/" / "?" ) fragment = r"(?: %(pchar)s | / | \? )*" % locals() ### URIs # hier-part = "//" authority path-abempty # / path-absolute # / path-rootless # / path-empty hier_part = r"""(?: (?: // %(authority)s %(path_abempty)s ) | %(path_absolute)s | %(path_rootless)s | %(path_empty)s ) """ % locals() # relative-part = "//" authority path-abempty # / path-absolute # / path-noscheme # / path-empty relative_part = r"""(?: (?: // %(authority)s %(path_abempty)s ) | %(path_absolute)s | %(path_noscheme)s | %(path_empty)s ) """ % locals() # relative-ref = relative-part [ "?" query ] [ "#" fragment ] relative_ref = r"%(relative_part)s (?: \? %(query)s)? (?: \# %(fragment)s)?" % locals() # URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ] URI = r"(?: %(scheme)s : %(hier_part)s (?: \? %(query)s )? (?: \# %(fragment)s )? )" % locals() # URI-reference = URI / relative-ref URI_reference = r"(?: %(URI)s | %(relative_ref)s )" % locals() # absolute-URI = scheme ":" hier-part [ "?" query ] absolute_URI = r"(?: %(scheme)s : %(hier_part)s (?: \? %(query)s )? )" % locals() ### HTTP[S] - RFC7230 # http-URI = "http:" "//" authority path-abempty [ "?" query ] # [ "#" fragment ] http_URI = r"(?: http: // %(authority)s %(path_abempty)s (?: \? %(query)s )? (?: \# %(fragment)s )? )" % locals() # https-URI = "https:" "//" authority path-abempty [ "?" query ] # [ "#" fragment ] https_URI = r"(?: https: // %(authority)s %(path_abempty)s (?: \? %(query)s )? (?: \# %(fragment)s )? )" % locals() ### WS[S] - RFC6455 # ws-URI = "ws:" "//" host [ ":" port ] path [ "?" query ] ws_URI = r"(?: ws: // %(host)s (?: : %(port)s )? %(path)s (?: \? %(query)s )? )" % locals() # wss-URI = "wss:" "//" host [ ":" port ] path [ "?" query ] wss_URI = r"(?: wss: // %(host)s (?: : %(port)s )? %(path)s (?: \? %(query)s )? )" % locals() ### mailto - RFC6068 # some-delims = "!" / "$" / "'" / "(" / ")" / "*" # / "+" / "," / ";" / ":" / "@" some_delims = r"""(?: ! | \$ | ' | \( | \) | \* \+ | , | ; | : | @ )""" # qchar = unreserved / pct-encoded / some-delims qchar = r"(?: %(unreserved)s | %(pct_encoded)s | %(some_delims)s )" % locals() # dtext-no-obs = %d33-90 / ; Printable US-ASCII # %d94-126 ; characters not including # ; "[", "]", or "\" dtext_no_obs = r"(?: [\x21-\x5B\x5E-\x7E] )" # atext = ALPHA / DIGIT / ; Printable US-ASCII # "!" / "#" / ; characters not including # "$" / "%" / ; specials. Used for atoms. # "&" / "'" / # "*" / "+" / # "-" / "/" / # "=" / "?" / # "^" / "_" / # "`" / "{" / # "|" / "}" / # "~" # # dot-atom-text = 1*atext *("." 1*atext) rfc5322_atext = r"""(?: %(ALPHA)s | %(DIGIT)s | ! | # | \$ | %% | & | ' | \* | \+ | - | / | = | \? | \^ | _ | ` | { | \| | } | ~ )""" % locals() rfc5322_dot_atom_text = r"(?: %(rfc5322_atext)s{1,} (?: . %(rfc5322_atext)s{1,} )* )" % locals() # FWS = ([*WSP CRLF] 1*WSP) / obs-FWS # ; Folding white space # # ctext = %d33-39 / ; Printable US-ASCII # %d42-91 / ; characters not including # %d93-126 / ; "(", ")", or "\" # obs-ctext # # ccontent = ctext / quoted-pair / comment # # comment = "(" *([FWS] ccontent) [FWS] ")" # # CFWS = (1*([FWS] comment) [FWS]) / FWS qcontent = rfc5322_FWS = rfc5322_CFWS = r"(?: )" ## FIXME # quoted-string = [CFWS] # DQUOTE *([FWS] qcontent) [FWS] DQUOTE # [CFWS] rfc5322_quoted_string = r"""(?: (?: %(rfc5322_CFWS)s )? %(DQUOTE)s (?: (?: %(rfc5322_FWS)s )? %(qcontent)s )* (?: %(rfc5322_FWS)s )? %(DQUOTE)s (?: %(rfc5322_CFWS)s )? ) """ % locals() # domain = dot-atom-text / "[" *dtext-no-obs "]" domain = r"(?: %(rfc5322_dot_atom_text)s | (?: \[ %(dtext_no_obs)s* \] ) )" % locals() # local-part = dot-atom-text / quoted-string local_part = r"(?: %(rfc5322_dot_atom_text)s | %(rfc5322_quoted_string)s )" % locals() # addr-spec = local-part "@" domain addr_spec = r"(?: %(local_part)s @ %(domain)s )" % locals() # hfvalue = *qchar hfvalue = r"(?: %(qchar)s* )" % locals() # hfname = *qchar hfname = r"(?: %(qchar)s* )" % locals() # hfield = hfname "=" hfvalue hfield = r"(?: %(hfname)s = %(hfvalue)s )" % locals() # to = addr-spec *("," addr-spec ) to = r"(?: %(addr_spec)s (?: , %(addr_spec)s )* )" % locals() # hfields = "?" hfield *( "&" hfield ) hfields = r"(?: \? %(hfield)s (?: & %(hfield)s )* )" % locals() # mailtoURI = "mailto:" [ to ] [ hfields ] mailto_URI = r"(?: mailto : (?: %(to)s )? (?: %(hfields)s )? )" % locals() ### data - RFC2397 (+ RFC2045) # ietf-token := rfc2045_token = r"(?: [\x30-\x7A]+ )" # FIXME rfc2045_ietf_token = rfc2045_token rfc2045_iana_tokens = rfc2045_token # x-token := rfc2045_x_token = r"(?: [xX] - %(rfc2045_token)s )" % locals() # extension-token := ietf-token / x-token rfc2045_extension_token = r"(?: %(rfc2045_ietf_token)s | %(rfc2045_x_token)s )" % locals() # discrete-type := "text" / "image" / "audio" / "video" / # "application" / extension-token rfc2045_discrete_type = r"(?: text | image | audio | video | application | %(rfc2045_extension_token)s )" % locals() # composite-type := "message" / "multipart" / extension-token rfc2045_composite_type = r"(?: message | multipart | %(rfc2045_extension_token)s )" % locals() # type := discrete-type / composite-type rfc2045_type = r"(?: %(rfc2045_discrete_type)s | %(rfc2045_composite_type)s )" % locals() # subtype := extension-token / iana-token rfc2045_subtype = r"(?: %(rfc2045_extension_token)s | %(rfc2045_iana_tokens)s )" % locals() # parameter := attribute "=" value # attribute := token # ; Matching of attributes # ; is ALWAYS case-insensitive. # # value := token / quoted-string rfc2045_quoted_string = r"(?: )" # FIXME rfc2045_attribute = r"(?: %(rfc2045_token)s )" % locals() rfc2045_value = r"(?: %(rfc2045_token)s | %(rfc2045_quoted_string)s )" % locals() rfc2045_parameter = r"(?: %(rfc2045_attribute)s = %(rfc2045_value)s )" % locals() # mediatype := [ type "/" subtype ] *( ";" parameter ) mediatype = r"""(?: (?: %(rfc2045_type)s / %(rfc2045_subtype)s )? (?: ; %(rfc2045_parameter)s )* )""" % locals() # uric = reserved | unreserved | escaped // 2396 # data := *urlchar rfc2396_uric = r"(?: %(reserved)s | %(unreserved)s | %(pct_encoded)s )" % locals() data = r"(?: %(rfc2396_uric)s* )" % locals() # dataurl := "data:" [ mediatype ] [ ";base64" ] "," data data_URI = r"(?: data : (?: %(mediatype)s )? (?: ;base64 )? , %(data)s )" % locals() ### gopher - RFC4266 # gopher://:/ gopher_path = path gopher_URI = r"(?: gopher :// %(host)s : %(port)s / %(gopher_path)s )" % locals() ### file - draft-kerwin-file-scheme-13 # f-scheme = "file" file_f_scheme = r"(?: file )" # f-auth = [ userinfo "@" ] host file_f_auth = r"(?: (?: %(userinfo)s @ )? %(host)s )" % locals() # unc-path = 2*3"/" authority path-absolute file_unc_path = r"(?: /{2,3} %(authority)s %(path_absolute)s )" % locals() # drive-marker = ":" / "|" file_drive_marker = r"(?: : | \| )" # drive-letter = ALPHA [ drive-marker ] file_drive_letter = r"(?: %(ALPHA)s (?: %(file_drive_marker)s )? )" % locals() # windows-path = drive-letter path-absolute file_windows_path = r"(?: %(file_drive_letter)s %(path_absolute)s )" % locals() # local-path = path-absolute # / windows-path file_local_path = r"(?: %(path_absolute)s | %(file_windows_path)s )" % locals() # auth-path = [ f-auth ] path-absolute # / unc-path # / windows-path file_auth_path = r"""(?: (?: %(file_f_auth)s? %(path_absolute)s ) | %(file_unc_path)s | %(file_windows_path)s )""" % locals() # f-hier-part = "//" auth-path # / local-path file_f_hier_part = r"(?: (?: // %(file_auth_path)s ) | %(file_local_path)s )" % locals() # file-URI = f-scheme ":" f-hier-part [ "?" query ] file_URI = r"(?: %(file_f_scheme)s : %(file_f_hier_part)s (?: \? %(query)s )? )" % locals() if "__main__" == __name__: import re import sys try: instr = sys.argv[1] except IndexError: print("usage: %s test-string" % sys.argv[0]) sys.exit(1) print('testing: "%s"' % instr) print("URI:", end=' ') if re.match("^%s$" % URI, instr, re.VERBOSE): print("yes") else: print("no") print("URI reference:", end=' ') if re.match("^%s$" % URI_reference, instr, re.VERBOSE): print("yes") else: print("no") print("Absolute URI:", end=' ') if re.match("^%s$" % absolute_URI, instr, re.VERBOSE): print("yes") else: print("no") scheme = instr.split(":", 1)[0].lower() scheme_validator = locals().get("%s_URI" % scheme, None) if scheme_validator: print("'%s' URI: " % scheme, end=' ') if re.match("^%s$" % scheme_validator, instr, re.VERBOSE): print("yes") else: print("no") showinfilemanager-1.1.6/src/showinfm/system/windows.py000066400000000000000000000062471514476644400232620ustar00rootroot00000000000000# SPDX-FileCopyrightText: 2021-2026 Damon Lynch # SPDX-License-Identifier: MIT import contextlib from pathlib import Path with contextlib.suppress(ImportError): from win32com.shell import shell import os from showinfm.constants import FileManagerType from showinfm.system.tools import ( directories_and_their_files, file_uri_to_path, is_uri, path_to_file_uri, ) WindowsFileManagerBehavior = { "doublecmd.exe": FileManagerType.dual_panel, "fman.exe": FileManagerType.dual_panel, } def windows_file_manager_type(file_manager: str) -> FileManagerType: """ Determine the type of command line arguments the Windows file manager expects :param file_manager: executable name :return: FileManagerType matching with the executable name, else FileManagerType.regular as a fallback """ if file_manager == "explorer.exe": return FileManagerType.win_select return WindowsFileManagerBehavior.get(file_manager, FileManagerType.regular) def parse_command_line_arguments(path_or_uri: list[str]) -> list[str]: """ Convert any glob component in the filename component of Windows paths, which the Windows shell does not do itself :param path_or_uri: list of paths or URIs :return: list of paths or URIs with resolved paths and no glob components """ paths = [] for pu in path_or_uri: if is_uri(pu): uri = pu path = Path(file_uri_to_path(uri=uri)) else: uri = None path = Path(pu) if not path.is_dir(): for globbed_pu in path.parent.resolve().glob(path.name): if uri: paths.append(path_to_file_uri(str(globbed_pu))) else: paths.append(str(globbed_pu)) else: if uri: paths.append(path_to_file_uri(str(path))) else: paths.append(str(path.resolve())) return paths def launch_file_explorer(paths: list[str], verbose: bool | None = False) -> None: """ Open Windows File Explorer, selecting files in their folders Inspired by https://mail.python.org/pipermail/python-win32/2012-September/012531.html and https://mail.python.org/pipermail/python-win32/2012-September/012533.html See also: https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shopenfolderandselectitems """ folder_contents = directories_and_their_files(paths) if folder_contents: for folder in folder_contents: folder_pidl = shell.SHILCreateFromPath(folder, 0)[0] if verbose: files = '", "'.join(folder_contents[folder]) if files: files = f'"{files}"' print( "Executing Windows shell to open file " f'explorer at "{folder}", selecting {files}' ) to_select = [ shell.SHParseDisplayName(os.path.join(folder, filename), 0, None)[0] for filename in folder_contents[folder] ] shell.SHOpenFolderAndSelectItems(folder_pidl, to_select, 0) # type: ignore