apvlv-0.7.0-alpha/0000755000175100017510000000000014726051136014256 5ustar atzlinuxatzlinuxapvlv-0.7.0-alpha/ScreenShots/0000755000175100017510000000000014726051136016516 5ustar atzlinuxatzlinuxapvlv-0.7.0-alpha/ScreenShots/apvlv-0.0.6-9-dirview.jpg0000644000175100017510000013343714726051136022637 0ustar atzlinuxatzlinuxJFIFHHC  !"$"$CX "U !1T"QU#3A245astwq$B7Ru&r%6bV1"! ?HjSpGmܒI;5nxQΘYJPKbL.|tO k@I$&Oy8;{omKoox=5~ŚyY)Ԛݼׂ7I,ѹs;$۞.hW>[h[2j;|nJ<\:&B\mOY4bgEϕeݭqvw "c$4L ]ɇOa9 ӆ@hܥ-RŌ^r|[*!la kp6}C')eK;^[l a1$|op- ;nA-R|S'K%e9kS5Qmvo` !rlMԘbعR-5,\ AxcG 7sZAwdп-R|S'Km'ESc27FVQIiq8o I Ww'wTYa!YaJ9<5AKNaQܨ\-R|S'J5&ȱ;UM_ǰғvj [z.[3ɛbѿn&9w5mH-P5&zc6+zh'sUQ1wjV3"̍I;csܒ'3T,~ܐGvF=ˋ\M~|Yۻbj>[͑WH9~GJzVbh#bpw4qVg(̖l9s^l t JE O'o5?ԹKgm),ݲ u%sd3>o>MgnuT{-Zw|Yڎ:FlS p` oT:G?OjW9 A6)͐ZP%$Mtc䘈i v5ZSˊbX53d 9Ogq$ ^_-Rol*͸ #]G-d6xcn;w(~nNmel׿Z7kbgf_2orN}kvSSDPgXZF^&<݊م)hZ)mi֬ @WZWh2S>9-Pm23hI(7Lr/t3UxcN# :#1ӗ[A1v;Դ#*_}Y#,i-{IW.[ neX_{?  j:iDR|qRp=𸹭Umyv{cV"exlNIU.CYUةH=NOWa9-MOP;K;%gK#j Zwg>i'u@c% ItU7׀=[K#ðةH=NOArZv&=;ē92847ng}KО17N29<5קsNok\=لT$'%]焟ٔW<$̩ob a;?  hb'e; ?*[ةH=NOArZ%خxINsOʖ*~fS{0v+fS\2لT$'%]焟ٔW<$̩ob a;?  hb'e; ?*[ةH=NOArZ%خxINsOʖ*~fS{0v+fS\2لT$'%]焟ٔW<$̩ob a;?  hb'e; ?*[ةH=NOArZ%خxINsOʖ*~fS{0v+fS\2لT$'%]焟ٔW<$̩ob a;?  hb'e; ?*[ةH=NOArZ%خxINsOʖ*~fS{0v+fS\2لT$'%]焟ٔW<$̩ob a;?  hb'e; ?*[ةH=NOArZ%خxINsOʖ*~fS{0v+fS\2لT$'%]焟ٔW<$̩ob a;?  hb'e; ?*[ةH=NOArZ%خxINsOʖ*~fS{0v+fS\2لT$'%]焟ٔW<$̩ob a;?  hb'e; ?*[ةH=NOArZ%خxINsOʖ*~fS{0v+fS\2لT$'%]焟ٔW<$̩ob a;?  hb'e; ?*[ةH=NOArZ%خxINsOʖ*~fS{0v+fS\2لT$'%]焟ٔW<$̩ob a;?  hb'e; ?*[ةH=NOArZ%خxINsOʖ*~fS{0v+fS\2لT$'%]焟ٔW<$̩ob a;?  hb'e; ?*[ةH=QMqBJ sXk5b7“?=焟ٕ]Bךjԭyq1w=<$@Id_ ] ݶUO7ԸjoR\XlK/m;mdrpJ@Y @Y֐ΝItuӵZ89k_;.__~yc\kFY@$vߗpQFGNdHϿ^͑eז86ɧlyX c$|΍śq`E骇vE/qYP&퍼|ݶ<,l DXUbGHLm͎o-NKxf4g5}>16:{1j}wI(eL}6-[KLf_eBGA{KjU8gk۸;8n7pQr܌ZlXz^[ tqcC)/m-FddjWj 4-^К; Z:QGdk^Z\<ֿ~.3Jf3Z\br%Iu%M't6j2^'1^pTq7q̷s [Ts5rڠ-Y=LbLv!ۅa|L˝wUt[pBblr斿 ٤+#}ŎkJHdvyls=mosqsy """ """ """ """ """ """ """ """ """ """ """ """ """ """ """ """ """ """ ""n +^f+ORi@clqր $)X)}O '\2zO۝=I1=-3_9~Ne/edڥw2yO~tWmRU3Ϟkt{-kHi6F?\+==h3{?޽2t2CZ[$,$n9cU]<&C$t5soa߰!q Yd]5ΐru ;q3ؽ7K;. WJ'KgG]#?dtzΟ}Yے7/0Il;XeXsL~E] |Fd357mvW$CCTkv'ig.jȾicӍA;DCi gCKiXi8okQ{ǷuDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDRDu,w'ŇRZy%u*W+P/2{twd,wd,HrN~?Tf:HIK[5ϗ/?j.5#,p` ;o˸(#tarNq$|߯fȎ2kgdgF80?SkM9L J`} n34e'coazjQ͹ro HڎgN2 ,36&]/yx`RN*k7,i2.TRK[Wc"<YJ:orW(-Z ֺ0́eDײY2ufOh$DH{]b^A& ,kmSC#$s1rs@,+sO6$<4]d>X:k$|ݜFtNrX3^MUI@ߕ,?5yA3 [ ]Q>}9 k/;=f/0roχſ5jUWIvB6*H̎glWXzyZwqdmt7c6*9Nw]-\-ou|pgc T;eUYv]ik˜S-fЖz 2H `! mDXoeԮ]ZkAsY.-7đ6]L5x[/M2A6<2V;wLAbH# %ܐ6R#pUuZBiRs/2"?W,cÇ+()Xk`M,Mc].0WFޮglp%c9Y|DA&xFsR+I[%5W,^`17mTݣ%gq"z{C #I; 4o8տ}&bua:9gYR)+n{\ ~lnv[H<ՎqػyE}giER~qQI]np8 ΅'q2~jv_aGOaۗ;GɜAԍ2Gnkߪ3^H%%qn ;RY uȇ[ӶMH^mݞF;ku̿+Nlg&::SpbBփc:$+7^߯%k1q7zRmK%0xZMfdU^ɤ,,,1gS %,q tmYw>5bf4 vYd0oZ&:2^Dv'AP=Kst 4>T<ۄ*aV?X.܊%tjBuR]piiNܨzS]/[!8A20D;$Ѿ&7C4閖gJ/ |9zg1S|Vz/<81۽_ZS.M9^7aoXJֻ#Y3$Z<^m{ƦRxN:83=ㅵJAT&".HkAG-W:;c+cE-,l0:s dIU-"ӼGoj 5Y<]\V3kug03 sxK%'sr$AVez CǨfmfbaƊuO1퓍a!!]Zޮv̀d3xb&b,bz9`cdHyqw] O8njfvaczk s#=&`h\Zeägodi2\GY}7P$%p棩ME`@+ը8FVnu`׮m.o[WZ :]{Ij)F*8_=ˏp'X"ȹz >֖gɚxLM+f%vp7c`oX3noi'A@~jw3WC#[bJ s>|ڤ;uO<.eLeJ]29_{K//y8ONim(V-͍A{>fÕļ@[[21#K`#@ HTdauz-pC}펴&IR-D3# \p 8AFK冐!RT}RRAUrW3ayvr@@M1Yur5 ̾qW傩x>)\.lh1 V[BlddAE\ڇpi vA'N۬k`,dn"s&1> ppw'~Rdsffz*ƥ b=\lI9۹{ KG ؈(:VZm-Yd/ ; Sd!dO FY.7XuePGhGF"ܓq*D~ԙxLZ|1G=߫ߜۭwugF80?tas:%QPY;b/4n?i xf"e,梂e1{yL@m5kɑ"@jͼ}9b;ڰGm ]'< 8l@ -DTZ]s]=O,|Qpݛڞz<TL5VNH9[a%qGs'vF8yA' K$ }\sLwdهME.,2 ]45%qaLZ1QC51e)9dt1Ewk>B8 ƽcp;=!}UEv2cSQjixS* P9lLxvz( Nة5C} K:u6]y?a.hxapp$ A8!͍bnY{%7Tk;- p1;@e)c9Z8"h9;o \nZ*Rk /f64v i)2 v= $H$+v\ޣ5wɷWc-ɉ1509{j/@$N?#~L;#Ηo'쭧dY"m0n\/LvӚe\ŎͩoZcEVxke; /8%T[q.Y<̥PCgGC 9g(梛XX*چFQ=LKN$F:[ 8n9쨬oS,۶r7ܯhuLkOlF08EW]RJv.vh4 .6]$0pǻ˃^X8~`ER+V[K,cl_+$Zww"G'(KTm>Z۹a;=Aw\<o r CWƹ. p%g{ȏA=adMڵTa_#e ٸ%g>+(:R%\Y٠#n~az AB@ 'aU% 5,zVCfI cxDVgJAr&T,ޣ&# &ALƷswV"ߟv3/= F8N?fBuش~#yC٪xA{": 9V͹u>4 [ZM:vp} Du,w.Qqe/1IaTV^e~I]JJ?̯.!]=C?:C?:9@80 pёӺY0'q8>kݟVt_L`2m,8vV5kpxXįtΞJ9ԌZ[M3#-س;yr[tALiߋtXѷ1myvsdfG+J)$d|pI#qvčf52i2MX0a +c(p xlyȈ #?Yc)rX)}O(?ҵ+JTV^e~Iq @Y @Y֐ΝItuӵZ89k_;.__~yc\kFY@$vߗpQFGNdHϿ^͑eז86ɧlyX c$|΍śq`E骇vE/qYP&퍼|ݶ<,l DXUbGHLm͎o-NKxf4g5}>16:{1j}wI(eL}]Xds]泹2ٟ$1D#N-ccc|<; QQCj%~Z3/~T.]GWu}nˉxzB!xKuZq Wؤ/39vzST:-eQj^/ke1;C{%xc3 !r(F.(kn=gZ}IqBܼv-aA +yOne?UC9ڳ 媷KhWnZ޷] .{7`8pQI_-f $I0ѵZhoI =폃=S0o&qR7P~zi"G cŻZH.ANP3a?&>?36x8 dB"(N(Nֹ;[,EwpW2_3wgq+Yj $2Xݸ {v;lwA_ul}#􇘚֬Ue㋰T1kA9os`" """ """ """ """ """ """ """ """ """ """ """ """ """ """ """ """ """ """ """ ""X)}\:;_zbĩ_C+A_\B52UeׅehvV=C짽^ޒ?Уȍ?5.߯N=Ak瑱ܛNNLGZkܢ:hl5&g]RGN7MjX 1 |}~V;^Za=XYڷosCܓ?X>Q_e=OtZ6Yde7|L߻{FAE-gN8n NÐH8y<>{ܟP)r|T78\^"gnm@3n3{7 ߉=2-AQeyr|W('M"u6#+1w[m ;9K.kpc ő /`tsp ỹyS=CܧO('ztRI}$6Ź [Y[:Q͕ KVw2~CA?ݽo=C짽O('o3l^{Uijp^{6yG}}>)SZ(zO{Q_e=Oբ zO{Q_e=Oբ zO{Q_e=Oբ zO{Q_e=Oբ zO{Q_e=Oբ zO{Q_e=Oբ zO{Q_e=Oբ zO{Q_e=Oբ zO{Q_e=Oբ zO{Q_e=Oբ zO{Q_e=Oբ zO{Q_e=Oբ zO{Q_e=Oբ zO{Q_e=Oբ zO{Q_e=Oբ zO{Q_e=Oբ zO{Q_e=Oբ zO{Q_e=Oբ zO{Q_e=Oբ zO{Q_e=Oբ zO{Q_e=Oբ zO{Q_e=Oբ zO{Q_e=Oբ zO{Q_e=Oբ zO{Q_e=Oբ zO{Q_e=Oբ zO{Q_e=Oբ zO{Q_e=Oբ zO{Q_e=Oբ zO{Q_e=Oբ zO{Q_e=Oբ zO{Q_e=Oբ zO{Q_e=Oբ zO{Q_e=Oբ zO{Q_e=Oբ zO{Q_e=Oբ zO{Q_e=Oբ zO{Q_e=Oբ zO{Q_e=Oբ zO{Q_e=Oբ zO{ZR^bԃ1UH^:N۟,_Ъ%J?̯+RZy$%#Oޏ <.<.ْżM >Y87 /#+[[O)N N].,rq$u XWpzzgM-챍 j=mqՎG%R ZSx(b݈Йf6p<<A+p;Ӷ_f':}ǹ/o{8t6oܖC%x],+mqsNI]/#/Ӻuw`@zMH r[rX6gzw#>l|pGww>g?tO{8ty;w,Vݒ?r[gI|ҸI\O2Wڵ-4t;`7q;޺'=_G_ӥC#m拣|ޡsOz䋣|ޡsOz䋣|ޡsOz䋣|ޡsOz䋣|ޡsOz䋣|ޡsOz䋣|ޡsOz䋣|ޡsOz䋣|ޡsOz䋣|ޡsOz䋣|ޡsOz䋣|ޡsOz䋣|ޡsOz䋣|ޡsOz䋣|ޡsOz䋣|ޡsOz䋣|ޡsOz䋣|ޡsOz䋣|ޡsOz䋣|ޡsOz䋣|ޡsOz䋣|ޡsOz䋣|ޡsOz䋣|ޡsOz䋣|ޡsOz䋣|ޡsOz䋣|ޡsOz䋣|ޡsOz䋣|ޡsOz䋣|ޡsOz䋣|ޡsOz䋣|ޡsOz䋣|ޡsOz䋣|ޡsOz䋣|ޡsOz䋣|ޡsOz䋣|ޡsOz䋣|ޡsOz䋣|ޡsOz䋣|ޡsOz䋣|ޡsOz䋣|ޡsOz,_Ы[=_GQUJ hԯV3v!1 _!aTV^e~I]JJ?̯) @Y @Y֐ΝItuӵZ89k_;.__~yc\kFY@$vߗpQFGNdHϿ^͑eז86ɧlyX c$|΍śq`E骇vE/qYP&퍼|ݶ<,l DXUbGHLm͎o-NKxf4g5}>16:{1j}wI(eL}s_ͤ4(3.twYfFbB=G2IWAEx-6_0h`sZ:-eִ[ű-;˸}9,1'$E7KGT2 ?HV(=[s@=ܗ2 Y U mAqlN̊1yHse[]w>;tyS2\I "198O oU8#R#6aɠ" """ """ """ """ """ """ """ """ """ """ """ """ """ """ """ """ """ """ """ ""X)}\:;_zbĩ_C+A_\Bz;{u;{u9a?fx ے@괏d"ʜ`w>llkyƪ3RfuE$tc{$֥-s˗yN"'9Yzr1]g f1ރ~%lyX c$|΍śq`EoO+YX2f8ipo\{;pCsCi nG.fR(9n*M7f |-,K^c[Ÿy߽ܳ>߽ܳ!Ýiqldc ~]G ; w#)]5gMA ܒU~c%1T;jHazjQ͹r'dhW[L ᑻlf+w&9zs*5Xj~.6\D5ۑ=/ؽ=itbzX8\Ap.sзH9к;PXțv7Ed[vIܓh)=}I)k]̅49r'uDH4Nzu"{ ゜qP;84 .XZR &ptr|͉l=awj2gOob) `捆wq~M-5-#Úw~[}Bߢ&_K-UruвZ"ݤVKOcNmph #?Yc)rX)}O(?ҵ+JTV^e~Iq @Y @Y֐ΝItuӵZ89k_;.__~yc\kFY@$vߗpQFGNdHϿ^͑eז86ɧlyX c$|΍śq`E馦:֛r22 78#fisA>OcmQ*X=9scg)}S1vұC>E}6> G+8v38okoYzrլ1M->fՊ.638|NswhTSz7Ygk[}laN&i6HkcA^eL1g aY:ή\E3h}jN46>} TX̕&G1 αJ c p gu~G3}4dbdx&>'H2"CjGK@= )fK y[R]h*.'Ms6%rI?[xhڏ/)hZ⍐ѿ3X,l@! w.ƈ(|I[u'tر kT,n#]%;nv9J+h~lݢB]G%2}~045@ܻS[SڗRn[TŦ!|lpՄe&{: 䌂Z3if..'Y|OKTXֱ+LBNq=\]̛qso9ո n&檞ɍ=N*O_bp148qsnbMWJk'ڄۀmm\(Qbpٺk( R2ޚKkVz=vq(|ܷ`ljiq+kxe0F+m13p7%VwW2P'sExc]ݹ.z s_kK3dk<&&3mg817h77yGQ.tl69`9m h4 [y 'YkMln 2jVg諝\v ˴u~{Njʔ㡏b-j[0GZ y"b鎙u]-Mr۹RmFI#1LIK->}c3N ][X8]~t*DkC$0ֲ_α@7gnn@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDRDu,w'ŇRZy%u*W+P/2{twd,wd,Hrv~Yp@}$?iLE8}N~?Tf:HIK[5ϗ/?j4DNsMz5bca{ mri[V)3qfsxyyW [t'C/$Ӓi[) c V01( kal  =:w؃ѹ yh36/DhUر2G0=Zpq`[};oIr[Zh"UwM"JL%{x{G tJ5kn#ҹ]4dR*|x1)^K HC,sa_Ŗf/%ٜqX`nX`㄃c#89!}$?,N$(>Gil_&6@ZސY :1&2s[411ݣ>bPxb/;p:A1щpCcmv7mg .+NZz527lG^pJy/589 [jf'9',J*Y&g5-,π`J.f׺'Ktn QXF_ q60N #[=b[H!&5'/Fax% kd 4[\ #?Yc)rX)}O(?ҵ+JTV^e~Iq @Y @Y֐4׶H.ࣆɅ;đ_ nI?S1*q5$|0TEV(ttf @yi{q7q?jr1]g f1ރ~ Q v@i%C  @z~ e&QW,|umFA~ߵOy<kp.5MzCKFŏ;ncyCLej8ZKk<' ŹNoq$5yD'Fpɉ4w,VY,BA,-; Him?jŬ&cY-zcmr-B vKdi߿M,Rwr;}øw$$nv[к;PXțv7Ed[vIܓ cIp뾼s#|l5<8y=0ۆLM)dڲb f 7qo=9_ \ PGhh=B+ fcۈ{-UZFQqu1ۮaA1xݮ;b?% rZi3Uk0Xx~JtVl1,Helѻ#F5w;B/,@F;"׵q}P}DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDRDu,w'ŇRZy%u*W+P/2{twd,wd,HrN~?Tf:HIK[5ϗ/?j.5#,p` ;o˸(#tarNq$|߯fȎ2kgdgF80?tas:%QPY;b/4n?i qazjQ͹ro liJ# 4Ze4X^ƃ{I^5>"oYem35[DHlRylRl@W*9SO芳ֳ ,nEhm@ #kĮx=p?նGSjan#s̐5̆5XG+#yHrދNJf"6f3M݀91h_(;lGMtEX #a'7kw G9cW?FKJ]=\&֤'иpchq{:h`=cB8v;׶GK&lmY jF̏/~"FN Iii:T⡂kOC,uFdz['amm?m֝k^~dH X!x'g4nviw$_ZkOӯzzmey<$JCwܜ4MjӘ[{|wJR˯uW=eN 6R< I>Ӷ11plQ\U #lCfpp[qleߋ#VZq_[*fq31o=O8C~#yC٪xW%&枻Sk0cfwp<[Đwܓd,p[6<2C l=k-5,T }v06D@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDRDu,w'ŇRZy%u*W+P/2{twd,wd,Hrv~Yp@}$?iLE8}N~?Tf:HIK[5ϗ/?j4DNsMz5bca{ mri[V)3qfsxyyW [t'C/$Ӓi[) c V01( kal  =:w؃ѹ yh36/DhUر2G0=Zpq`[};oIr[Zh"UwM"JL%{x{G tJ5kn#ҹ]4dR*|x1)^K HC,sa_Ŗf/%ٜqX`nX`㄃c#89! ֗wy@ie0X69.I^(>Gil_&6@_eW@?st[V,ת&usG{2y: !T`rZ$Ӌ%'B7kC}Lˑ܎ tt:G5pXcÁsC@A$ sXmbZ!N'+)rIcywn<|k;}*u(uSdw-/#ug%q9䵡cMoй^fHx+բ=d{r׶fW>n'mnt.;H'< 4o>0渖̿bKdc״lys5= Gة$թYs++X,jbql~*FHbI*9oyqay#fn7A}1{9k-p;r*]ha-L#W]on>{ltD>0ulݦO;s:G0J:s= ܉Y˥c-sioy -\>wUt[pBblr斿 ٤+#}ŎkJHdvyls=mosqsy""" """ """ """ """ """ """ """ """ """ """ """ """ """ """ """ """ ""X)}\:;_zbĩ_C+A_\Bz;{u;{u9xs.5#,p` ;o˸(#tarNq$|+?fx ے@괏d"ʜ`w>I1L/UbU9#369y;B>8Z^ƻMoڼ܌Wxc9w8/b1C9;sIinH;7Iq붋av_qO5Di d,; Ad0ސ1c۹r^ln8c?hc8Iny{+x5O`6JhY6XY[v@Ay~ՋXM=س+Zǽ8 9wr[A`Y,^:#lRNw.,a^AI2r`OrnifHK6sG`uc1تp҂x#EL `;@ܽhQcK8XjV)cR 2"_w Fj2׉b2=fiWNѣU_3UdmG!c/7sJ\lĐozyC/cѺFs gr7gF80?9m=cos %U'Y`.;$0TEV(ttf DjfWt{U]_e}e1Bώ|d'i v9_7O& 6C!cV1x{򸻃Ǚ6*)= zU:l>J/VYF+;!nLa pwƂ諹93^:ޚČ{*Ad8[ ˁ܈*<ϬfbɑbG^O(dFks;=Xp`u@ѫɈR}\|L/=7y&'pa 5E;̋9  *AZëZsK y=asv sI%7͘u^;42֢hqȚ=kXZq" fbϘ֭[$&ZC,.>V(Zxۻ%k_."Ŋ9ԣm]YlPyUo7k z\aAՠlQA].k!xˈ!q#uVUcfC(xFf-K5"kPq$XQfl#k֫ZixNѷm;`Cg /T(X=*N}ať?w\9SۨC[k@ވ:;_zb(e2ޘ*W+P/2{JPjWt`iXn٫:n"o#3)<<=[<G1ԙwGQI;X5cxƵG^}ӆ}iV{&^܌Wxc9w8/bi[V)3qfsxyB*#-&tͱ,v1~'[5›o߲rnC7jS7ɬ]/\ =1ac b kD-o[,u! ӝM\%{OIGc/˞ƐZ?9X'f[$i99+7`u0BD^1Sˑvfl2@tV=wxsCA 1{C3#Ic;\U5D`Zl69kNDtxonzXdQN",7þkyo  ؜αP ٜfR|>wmI#7w Y7kis.RU Yۤ~(*l2H\xK} :=*pGV5bG m44Ó@e@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@Qqe/1KG\~KLR|Xx+A_WR(?ҵ+KWOP~OrΰP~Orδ/v9ƽDe= @x'mw4dtL.IN$wg՝77rHVYSGѩ#6 ,UG#{s67'}[xVbX7;oA#>܌Wxc9w8/bCbc!R(VEؼ5؎"vǙwXi-NjT8+8Zw ?\?^H44fn<~1rɯVY;9ؓw~klR k4t*1Tcro[A`8_G2=la!9ѿн}*aNp"&~`=/J PRk!j\ͷqH" yF67~I """ """ """ """ """ """ """ """ """ """ """ """ """ """ """ """ """ """ """ """ ""X)}\:;_zbĩ_C+A_\Bz;{u;{u9xslkyƪ3RfuE$tc{$֥-s˗^y@80 pёӺY0'q8>k׳dGsm53i[V)3qfsxyj:jK\Vo/bɿcoc`^"Zr:Gg3nlsylw܅che+:F6{x\X @w7#&phs= بX WSRPr1ް~@]&H(_%1 kA-;r?M=mXf{ Ź٤np$JUX (O[NFkwVɤFo#/hxap 6}A㡊ڳCNYNg ygW&2 GBuE]I7$_潁psHwD@D_C "$>8Id|{9pkZ7$Qǹp7'ndG֗8y(>/!vFrA9hsA  #?Yc)rX)}O(?ҵ+JTV^e~Iq @Y @Y֐ΝItuӵZ89k_;.__~yc\kFY@$vߗpQFGNdHϿ^͑eז86ɧlyX c$|΍śq`EoO+YX2f8ipo\{;pCsCQ*X=9scg-O9Яl)ah2A#v?SVΝ E; sU-@ɢ,>?"toTճ'R,MC$q4o9S]OƜFj([YH<3#\:w G2tv{~UO C-d@NckZ8?Of-t&J]iW$<3pyw4ܽ<&pʘMKhls.~4 s5ohO.\ʵg1X6ش;Rt{T\*FfYlU,Sd^$-/ {Ov ]x]ъO=qi~*h[9 828"!'9bxC& slnjtpd#depH7nߵa+h?^fH$8ؘi4EtSߝc$yfxXG3!#k%J9YnDjcleұ4[m ڟ; PISfzz8!k{co69@s sK_wYl.2ى ZV^#+t}n8ymւ>bt5K$t]ֻ<69K z9۸<G\~KLRR%JPjWtԩ_C>߽ܳ>߽ܳ! 5gMA ܒU~c%1T;kg;c]5Q3)#k&,qosֿw>\HϿp9ϭ5d+8Ls49Q-? c8c#tn,o?(c1[kAbiB׀]d kZ󻃶݀;xf;w\ެ]_;!;_&lz{5[pHh6BEֲx|xa|XWYe2OXx=w>3GO7o߲z8qi~^\P6D5vf<-ጓ5^$x4A#g5nQ2v8x Y25ma? գ mdnF6fs3L-c_#̏s؀|s'E;ڱ:YQV%,/6lÚ\p_W7{P􉚷_NŏIT%=N7&3w6>TӘyefL(6V6]+k3K{h-;E6jط6c048\u!Y6,wCZTGK%]kc$'kx󝻏{Du,w.Qqe/1IaTV^e~I]JJ?̯.!]=C?:C?:9@80 pёӺY0'q8>kݟVtlB'9cCZKw4=D?riL 8;IJgBh1F:~ό7y? &c֍˷= F#,.ڮoiMxc.[wJ͈:Sic CeB({-h#u2F^.m;_CI^J,܆2{Ew G w~%R3[1$ޫ^j|tno܍-;\yG\~KLRR%JPjWtԩ_C>߽ܳ>߽ܳ!Ý;c]5Q3)#k&,qosֿw>\H4׶H.ࣆɅ;đ_~"8˟i,qmONH7NS1 x -佚 *m.'u8wks;{N56cid/EyX?۬c.km& 1c,RlkbW \An[*kOSNL:`5*a-۰;woM~C/sEmUj:Gh4٭zG0J:s= ܉Y˥c-sioy ȏJ;6b[ewmXZ6n\e)s5A G͎WF1psP[Su^wA?Q 1ڊlձoOG-v/lm(ax.ikp9 B?'lXdKgF>IacOV;w9ǚ"" """ """ """ """ """ """ """ """ """ """ """ """ """ """ """ """ """ """ """ ""X)}\:;_zbĩ_C+A_\Bz;{u;{u9a?fx ے@괏d"ʜ`w>llkyƪ3RfuE$tc{$֥-s˗yN"'9Yzr1]g f1ރ~%lyX c$|΍śq`EW)tK1nv7^h ~:E@hK6^į;0 E[,^sNŅh;wS+IV18{cUOt&,q8&<E9ihs.;7s{s:gfX3]F<ڀ3@G2׉\hz=m|G1n=% k hkkbWFG^) n!scQ9־Qv:l花GqOZ#n@9rǠ6~&znM-yIapA-^-9^y?b&5۹-@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@Qqe/1KG\~KLR|Xx+A_WR(?ҵ+KWOP~OrΰP~Orδ/v9ƽDe= @x'mw4dtL.IN$wg՝77rHVYSGѩ#6 ,UG#{s67'}R%bca{D@^ztS6 h1n@YA4pa]I;k#r\%ry3z]RWUqruxUTe6pv;wA>vzxzqI>k3߼yYdOVlu!#߻O#On[s|.hpapv y~>`N  ִ},`BC4TE: _ֳBN>oױc ~ۭ:*Ȑ CNhIrt֟^ >xI9̕<8x$qoeDjWNէ_R:5lrOYwwaߨfW̶Ż?W7;D|m*uluJ"os֏}R{lCY%G ?=gD|}*!|4d1i's[d{%q?ѩZ~V&߰9KI?Y%gDKWc[`٨": RD];2YqoDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@Qqe/1KG\~KLR|Xx+A_WR(?ҵ+KWOP~OrΰP~Orδ/tw#FjLλodԱżAcZAr#;^"2 <2:wK&$'G^}l.-~M? c8c#tn,o?(g9O/`)(RKh$Klhho.Gx3l DXUbGHLm͎o-NV)Unc:˚ZHH[75k^ 6&b xif>Gp={O էrZ(ϳex"\Fȥ,tq=.߉#Hѧh٩fpCiyh<[hbƺ᪙.jYI~,|vEZ tUcCpT=)ͫ袵;ÔM`vFJxk7a!{|e2enAkmEM а7pry[MTb<2[%xVdM @e߫-[tvo>\}Vb[1sڲEymXɢQðz,+NcL{fWc n#[5=Yq];Tu7yo.㹔~Nl6`8lV6lm mhjNx79eےv1ze:H>':.34%@[ /CCC!\=ۏ_RY0k{gcY/uzxghIժ:b@٧ ^6xuZٛp5[[PwAnt!ug>Q JcG\cn֪e.Wb޴9ff6m,s9 Q (ЯjqpFƆv %zAzU[5!o4fH;KK9w҂3}e²zPmߊQ:+Zj=Gd䯤nц:UK5wf~u[mY7LbP~OrΰP~Orδ,7g՝77rHVYSGѭtw#FjLλodԱżAcZAr#>DD>׫=X\nF+L1;{D-+dѸ9<=0 qYy恕!Gukog5nEo ӵ9.B:W ㌦k L0GFAnM:ŐXW4[rKk':z@TZHGTawC-0\ 1bg1K:f'Fװ37F]պSPi e[^V8!m`|m /sugx#cdHn{ƹsZvpB$1r01jnajZF xY,<|UC\^^6[}q֪ԥ`eczc{x\{-;l;Ab$E49 J<|XanbQhׇ7ù&z ;Tu"WX*f`lXݙ#-o88-ԋ.'>UA̮\EX29%"[vZcA2ӳK8 b"0PcGhef"Ba6 t`;w  \QNqsYils{ChWFDr8N#؜αP ٜfR|>wmI#7w Y7kꋜ3.RU Yۤ~(*l2H\xK} ZՈ16MQOћ[ SSfTńaFZɝwREk&u?q }{'gX(}{'gZC;^"2 <2:wK&$'GRjΛ$HLJb,hwԑQ*X=9scg-WR!qtb 0#nF+L1;{Qim9h/(ێi,;w A ^^&;0n4~@ DDY.g Ⱦ0fd-CmyڼZMkYnkN4`G}>ycTV(I#Ke~F2u6c{Ni-2pxZнk'4n X|{)2:vc#n0 {.ӸXXvDa!c#bǝsvhESu8pTc2y؀dcO27l6fH 7#l,m.bݿr ճNEQahls20 @a`oC nrRk!j\ͷqH" yF67~I1{摸  ҧ҂3}e0}-λgV)":aX_>߽ܳKU,pMÞyYm? r;c]5Q3)#k&,qosֿw>\Hن\k {%h$vpBӸId?V|[Mycco^b1:7o7ǟ)EfB,kK6W'vۏ0nB/UU}X=M Ei=[5m=9+קU'pID}lF[,`u `oRZc5wP:c-25W8 M`g1x˕u)̬FdkݾnImcPtU ŀue*Tm)]aNGuR𴈢qsCrκ r[\=IV<ߜw#}ֽvqV&ҳC =V! n#man37-BlqD AX_-yqpteS7q.i8o?&/l FVOa>@؃qvG 6hp+fj<gE3'A#k$aik[%z8-%8ڴjkY8DAp8bro ˠi ur:YEc0kD@(WH RNa硖u2ګ5Xtp|-$vXӧ :Ն8c4l I$ZV^cѺFs gr7<vWOzLv5l[ ]y^ Z.kCf;*Y#.tQϒXXյGsǽq榽\ ΋m;m?3i=NO v~.hp{@:,\ ΋m;m?3i=NO v~.hp{@:,\ ΋m;m?3i=NO v~.hp{@:,\ ΋m;m?3i=NO v~.hp{@:,\ ΋m;m?3i=NO v~.hp{@:,\ ΋m;m?3i=NO v~.hp{@:,\ ΋m;m?3i=NO v~.hp{@:,\ ΋m;m?3i=NO v~.hp{@:,\ ΋m;m?3i=NO v~.hp{@:,\ ΋m;m?3e2ޘRCbwd$'A,m|\tkiۉo0+gB\~%2Њ9#@i2FqE?(+lG^!N#j#~@ʘ͝I tE??/&7l[lr;7Q>rOCV4h+ s%z44A+HI p#]`Y?,VDDQ#N-nԂ'ty31۝Ζ.VW&_ac\+Kձ߆79pDD0n1[5-:OQp/6ԃmĥN))MeM̲57;Xi9NO"cZA_J)pJ MF$gkuW3ſ |I[{?9L)שr!I .hEAefHh5dI3.DQ7Zc$M8' hCjKmܗTelfٚaȟ)i%"kI2t̏}).1O3^c@BEL&-v'VjUJ=Y(jBe74%$!N"J6MD 4i#`t=g+ Yި:&\rKq-3h&IV2OUkN+\R[J#OS\[[mIҍ.~]g#{72uFMf[ GqsM|Re::-t~MMedΨ{qc0ĥp;曆ùIFw;)ҩ[#ʒ|%OҢeFFY>*fz ?S.a'H$%!*t$)!)6iyaUg|qpCū[e7eʪٞ_USv 'kZ#a ̧RQEd`0[8:!OogRInE޾8,~PlqjkJZzd 5Ȍʹ#{f4FF+̏gJ 2'SIӵ6\S;!6;jIY'De(hW,vb.TRNiH{?(rֶ8Zje4PSnFC^)F^Q@:#ˊ[am 4i"+gmhO暑nYH{ 9k[ZZ#FП5",*l.?(rֶ8klS-`4m6vJey*J̍)_P,xIy$N굒3%C`hO暑nYH{ 9k[ZZ#FП5",*l.?(rֶ8klS-n>$93gm*7 4Gy V.Wfc&h[VR-ZmHѴ'sMHs [=㋅-Z-`ʭQt#`J9+}LI82JQy$j2+dC{?O\K*%Fm$̈s3Zr4m R-«)zc-kcVX$h[VR-ZmUSyt4%1JRV$%$MFhZ'stqIRIy*4iZ&erbdh[VR-ZmHѴ'sMHs [=㋅-Z-`ʨcXM.f-Mm~sWhśR*LDf,҈ƶ.ErdHѴ'sMHs [=㋅-Z-`hO暑nYH{ 9k[ZZk^?[s(n㑔뉋庖'mFi;"?$3)f#*KK-*CdDD&dh[VR-Zmnk2a;%5)LGco%(wJ KH[3S+CGPԫݬs}g,q̱kagjWX5峆; HͶY=c79)gO`fz_b;XE6d*Lʋhe(l4i-i늺_Tdk7HŸ/4ةMI^ҡ.#PHKY+;D̰+tҵ)(SF CjrJjhDHZLl(ymQ"K*J&SVY# 4m$Є%)"t"!O/V#7?+n?6Wmv+SFrrQ|q{urs:c)o)B5,e|G; }}MQZk-'o 1ycFl5o16 W?:p4-f@PӲ KfddM8Wͭm67I5N8cgycFl5o169)gO`fz_`5-4Φ-;#Kћ (xqDӱiQe嶑te0t5" I4ma5ayR);GW!DmJJxmI ֥!)3"5dFJrmEM2r$*nDH$lፋ<4k[Vd]+tMhY<[jbmKpѴLlOqr7ŕұ}5Ekg 6p ռ۞2iұ01SMiY=9&$Zn֋#] DN6ÄiFdm/JImDy5%\Dc=+kTVp Ig lXѭn [M+[8ma᳆6O,xhַM&4p9]fϟ+_K@hOJ6v'<4k[Vd}'cpjXAi;xlፓ5ay}MQZk-'o 1ycFl5o16GzV>lᵄlᵄlᵄlᵄlᵄlᵄlᵄlᵄlᵄlᵄlᵄlᵄlᵄlᵄlᵄlᵄlᵄlᵄlᵄlᵄlᵄVԝ@3vl*C/7ƛZRiJ3#2'rQ 􎔖K4iHwy;Z٪ϝ}=-NyOSB7#mLetL(4v&Ø,U=lRGȹr[rS;GFO)&?  qيYd+#3Uȹs3;wt>nB46D-sIRHe̓~_ȀT4kVsf34ߦ+%VBJXYә=ԢSkNEfLC-vIIIHJATFV 1TRcgsp"AOyךoM%m!_jP0@}C8@5TZfB̨_mŒjSF֞OF6JĪk0`z#)}qOStdDVZmmWKs UkHSdP5cRѤDڪ4zeFt)!+5RdڎUer,#):jg4A9JT^n+6ҕfFeNc@7mK$~u Wڹ3:Ûz$fgF#TZfB̨_mŒjSF֞OF$Z);PN]"7NunB 56R$gmt Yҵ)(SF CjrJjhDHZLls4d%Rb&|c%[&V6ԒeԊ}z)H2KvAb;\oMiҖH&qI6ZFvߩWOP^=V%t2NC$5%Yq 2KtXbcyuݴsYQ۩g|, nUЖҴ+III 2ECfI{uhZVqJԒJZF,@RVNBBhb1kmM$I,6##˭swh:ʋY#K=ܲ_cmn;VHIՏx5DZ^R P>x'MKi*SIqKRѠKI%f"R/kO}JumSE/;}YdWfQ)`Sb$#';DA.9b_^X2(y' %%qGŞjM;}6e5KSxCV,%%HʏL*p82“t9)ۑKQMњFdIg$샄:Uyess>y_WxK%!ii*p$֒%)&W"t8T\e[In9Ryt31EZ`00(ɖAtO.\H>7mKXO|ߵ.@lڧSSM§*8ͺbk7m&vIf5k?K{{[ iU|Ӽի ufʃ#_j8HapfvVB>˫ꮪuZsoTU07FwZsnH0 )ʏR\7&lsۻ|wsG"3I#>?Ɛ]$u7DHpn{"ig{V7%/'f1n.Ix;.IxnZ[U<8j d F[I6J]GȌfeYDgBA.>W"A̖TgfDdi.jW1+-;LM]#i1Ta5sc^b9J,O#nĜKy?4Ȱ ?*#\ܧUBiqj3rKWʻ]>Ex;.IxmTR#F/>Z2뤓",Z^ Ɗ\TEj:bѐk<փ<2Rn139b3 ^1ﶳ'ﶳ'xmn.Ix;.Ix@i^?K{k?K{Wᵺﶳ'ﶳ'xmn.Ix;.Ix@i^?K{k?K{Wᵺﶳ'ﶳ'xmn.Ix;.Ix@i^?K{k?K{Wᵺﶳ'ﶳ'xmn.Ix;.Ix@i^?K{k?K{Wᵺﶳ'ﶳ'xmn.Ix;.Ix@i^?K{k?K{Wᵺﶳ'ﶳ'xmn.Ix;.Ix@i^?K{k?K{Wᵺﶳ'ﶳ'xmn.Ix;.Ix@i^?K{k?K{Wᵺﶳ'ﶳ'xmn.Ix;.Ix@i^?K{k?K{Wᵺﶳ'ﶳ'xmn.Ix;.Ix@i^?K{k?K{Wᵺﶳ'ﶳ'xmn.IxGjS:J #9I/q*,3ȏy=ZopskoZwX{[ iEצ4"d^o? (Ȯ+;={5tyⵈ6\hڼu(m JG!e+*߾,:ƓɬLiVR/8⟣66~= K/.:ĺҟ%+L9߶sKK{ExTҒ򱵬C{z Fa6gIPY)p}'Lu]&Uӵ @THei&23,w%[bNF}us׽ˊyu0A!B+/>ƐgM6hR $Ң23/ܝ٬]t/3`ܝ٬ܝ٬ uRMKO"&8lh5t1u3%U RtViJ*C;={5t;={5tO]?U[9Qr;ʓ:CTVFWA*Q^l`I#>NF}sFhksJi TH(z[.DHܴ}7J{f Б%iy e/W1碴YIint}:3EW\^niHYHi BR$oyNե(RViR4$$؈k[>Dhg Eh3"Z|̹gQ=tWn f ;s87Md+;e~^c3e̵%66I(܎.D;O:3YC:3YEW\ffQî64)HI%&e%*BuE;>dn!%o8kQ&v3c{={5t;={5t=\J={5t;={5t=\J={5t;={5t=\J={5t;={5t=\J={5t;={5t=\J={5t;={5t=\J={5t;={5t=\J={5t;={5t=\J={5t;={5t=\J={5t;={5t=\J={5t;={5t=\J={5t;={5t=\J={5t;={5t=\J={5t;={5t=\J={5t;={5t=\J={5t;={5t=\J={5t;={5t=\J={5t;={5t=\J={5t;={5t=\J={5t;={5t=\J={5t;={5t=\J={5t;={5t=\J={5t;={5t=\J={5t;={5t=\J={5t;={5t=\J={5t;={5t=\J={5t;={5t=\J={5t;={5t=\J={5t;={5t=\J={5t;={5t=\J={5t;={5t=\J={5t;={5t=\J={5t;={5t=\J={5t;={5t=\J={5t;={5t=\J={5t;={5t=\J={5t;={5t=\J={5t;={5t=\J={5t;={5t=\J<Տ}7;:3YFfVOӴr[BmcW#21f0>9Z>!_jP0@}C8ڃ9)gO`fz_b;XE6d*Lʋhe(l4i-i늺_Tdk7HŸ/* jur*(St[)(г[)Ci5(N-F|wI ő@j+ZB">h6$$D$3L&K2U&+g8^Z.ecnI&]HGרѥ QX΋S46Q{줣$RI$W$'{}~;H;QRSZ4t3YGd:4HҏI v$[|`^i.%] m+BĔ~s$T;>fdVo)4I,DjA!xBg*"VDJrmJLܕ-I4d15rQ2Q֏gT*:u"OQ"[q$ei'I>J#*t;]Tj( D 6gsJO#;ۭaPSdSWRWjQ+ؾWdzRD]IY7tG?Nyژ6m1қr"֠څZnbu% ؛4";NF|Cҕ_3YN mN* zd䋡y#5sH-Qћy茥%(-t$ jXTLU½UibjA$n䤧di$Ⅽ>ZDnZ%3hksS0-Ǒ%ik/)թ)S.%fIA$FV}'T4%R" J',NDDY5'!o)"Z$EXJJr2Ir@(RR FPB mL$FHR %2{-f|MQNڦăQʒS fr &֗<224TM*䬚%$(m|~~i?{XU95XJ%ЌU;30+[}8>7mK$~u Wڹ3Īk0`z#)}qOStdDVZmmWKs 6E,I KVsNHekJM)QyFdfY$J>;[4;#Sv]MZSnEE nq܅%ke(m&IŨϖ$2.D(}.Zj~ "kJ~.)Rh2"&Gs4yZWlcƒ&;δkpMV2'uh 2ECfI{uhZVqJԒJZF,@ĩ*RN">*^ޤɍZI^B)R <ƢM}O5&73<_Rg˨T5*j"r2(ַZe%#;IXd@:4.YcraEiz$wklk-Ɏ-Ղ 1 14 &YbJJ _Mͣkzj WO:R( Mi"Rer,@uԔI#2"ۙ Bд%hRTdw#/(ZCP #e`S-Va4NR)Hm$;rHCǥi9]48BiH"aa&pVJZ2mdKj#;ٴQiNuF-)QcUp52Id%J7mK$~u Wڹ3 cXE6d*Lʋhe(l4i-i늺_Tdk7dJ& 2yu:wADEey߯?F|ޔg9 nZV6E |HmNISV5-MH QIfL:dLWόqd]kdLXQUӵ+aRy4ғJT^QI;,X,fXpwm}Tvg'Ë=bc)yۤt% RR3S"2f.bcRV6FfI5u2#RO{RIuI*KmiZ${"3$=:-+yNѥjId-J#R ϖ_X9U+TDQӵj6bO$iamDZl)3zg>JS rۊFnmM*,Vn)ujtiA(t,Z^T8$p6#%xĖ[Gn9˰H%KB|KL" (ϭ+R"pclMi_٣"|B I.R oטGRBp5rS,w"Q54jJl(QNm|WġpM$19~RPS(KQ3Q(i5󚂌lɆɴݾrCj(RxĥDfImFEe3O("h};Az55&j[ęQc+))Ω&Ѫw, esME%b33ͣnt|iiM6㭒(QI o\ifARmMfev";W\K5G"CmlkJ f;}F =~t+[} !`@}C8~u "*0_eSy~@UzRaͽQK?33Uӵ+aRy4ғJT^QI; =vHW|(SVTۮÑQB:E!IFFJIDdqj3嶃I@̋x*}O&-;uwZ ݎˏ'Y$hf|Zk[I3hfۤ,ff'uI%ydg̓WTд:F%(H$/>Yb5WKDHU(Q%_-4hQr##27 i3]*4怪inQUo8gNK 'J`Zc dV Df#0q4E;}ct KRbdiQjB4/a l5>ilRo:זDiqCTiI"JiiS dFINomq+J"c(mL1RaD=KY heTu%+I茥^q?NmYiwѵ_//[5V!MB4xRSTՍKSF"B}cch%Ι*3Y/-Z2~.V#x:jg4A9JT^n+6ҕfFeNc@0Ɖ1:Q2=R]Ql6߷$,bs3L{̓V7o;Bh];FO-^28sQ73;j=&Fq)TYQd?=O|ߵ.Ac=~tHX>!_jP0@}C8@9)gO`fz_b;XE6d*Lʋhe(l4i-i늺_Tdk7HŸ/* jur*(St[)(г[)Ci5(N-F|wI ő@j+ZB">h6$$D$3L&K2U&+g8^Z.ecnI&]HGרҰJֳN܈XUVEj,QvܒڛRЕEnIs2\W'D$4%;u3LŞ11]mJZV))?Hv|5uOqN JS4iZY)KRԂB3( WUP_ :ۍJR%t"$EfNj+Jd2#":1PmI4ԣ7$5i45,I$-+N#32szvSqiwerCE|_w{@95G#MDp_b4Ӳa7 C1h']Z(?I uTȜvglr?]htzTU=YIInV++r(RUHAKS$M3Q'.f]VsieH.Sm6ԈT6RRjK2]wMFgh4b\MU}&IS D r%X,XSMŬ;YDIhh[;_b^ʥ3KU1aj[m(^5CU~DԬêi/Br>+ KnBP:#dDRّ.4\4CVB[69VJ$zrZdcNP&n̓ѩx٤̮X$eЈDDTW ,q\|'oI +\sgU$`FR/8⟎N6Ȉ;گҗ?+mYt'P:L杨')[ q֔R̲Iܔ}lv=iws:G B:v9- J24,PMJ#$Q-H0d]Ls%?)ح:RR~rZH$#BRPI2j-[-G%:IڢgBNjQRRڔ;e[tgd̺Zs$T;>fdVo)4I,DjA!x~RjY'$X!NY6+R4O ɴ|&̏$2#vYE~N&0<[M$8\LȌWIZt(9ui2a8ħɴ(FKIbX&Xq2,XH\MuTR==.Dd̏2D5: IMd륍_D rW.vƭSP& A}kf^VAr\YyKQJH+z&ȝ 85j'+fliТQ\wGc/(-.u  CMjDCj|k6=GSzZ^ʡ?gTTf.Dڃ$#u/%MGbY<(_̺̈y:J%WMZ%))m76fk[gr zjztɓK) "ǍF3v{ykrq+4:C|٭J2em1QK)VJB2<| cyGS"Ql%VPIfIdGdXqko]{w |BΡ` W?:pkU̅IQm ۅMԦ7%=qWKjZ?sXlU$`FR/8⟎N6Ȉ;گҗ?J֐ȡO<) *jƥI!j>14 LI,lXۿRIR+qe^JOv'Y]=f4KNo-HOϡY$DjHϙb1kmM$I,6##˭"@W%;d#q%b\-}?WcEiu9KJ6ddelOܺ)dwvm'"NQa*׵$E)SMӣK33(v'"QbYh=iqŒޠ$/ÄdDFdDWa!!Y7i$EC5hmziSI%ė#瓋QS5z:jAq7RIY5r#"V =~t+[} !`@}C8~u ިI?QbUِ3*-p)4Ѧ䵧*~-SG8 #ևw;C |Իd# gBn:vd$FLc?L@j+ZB">h6$$D$Qr(eCͼ/qm҃UFv#W?Q if:3QYa6lj2UDwkݡڧkJ>LL(Gu.) MS("5Aj?;V}RiG&*N*^ILL5%&qR)*:wժi*46d! c!Ce#;XYr{?ђ=; Femj]IffbbGRI|}-B֥jY 3;fKS9&bq(#0cJ32? c%>̠Wj&Yr媘(%fw%8.>_A2[V&)w\ ,So u-1@99W=1(+MOCHU:z US$nǝp-2fLxOe4|#Ca0ZcioD.W\|'oqko]$,sg |BΡ DV%TYK~:;""o<j_J_X`9)gO`fz_`:39-YW-¹Wj뚎+i*!n-MKqfYߝaܯk.ڋZdH:BVSa SmK鰔ڵ>I\ģiVTJ*%YoDTYv z,8y&.Qd_L5"SLġٓq!KW6~f-38ћeV+7(̈?ѮMySErY.R5oS|(dXI\|'oqko]$,sg |BΡ kĪ-Ui!RfT[C/FSdh5)MkO\UZ֏#\q%bUI50`=8Ӻ "+-6~69ktҵ)(SF CjrJjhDHZLmRhԹ.>qrˎTJhꈌFRuiڂr WmiI*/(̌$Gks؀GFcSAiҍ>qNy5;{gӠSFي0[A6[O\!qF!+M좹\̮?@Wl:I 26h^dYUݏ!GBQ! m RTj3V>Q3>Е%JRR3IDG@BSMpQOrT jĿS-{ȖlF5)DQsO;:asٯiʝ $BT$0_JRJu59N>xԒp+#6Vm$!{Ir.f`3 =~t+[} !`@}C8~u "*0_eSy~@UzRaͽQK?33Uӵ+aRy4ғJT^QI; =vHRvLPqW$JMuZ5,]&Lɼ?8PV#V%m/SߥD+b1LN4\$y؏_kQE}4*LJ8e"ϸ43KI>W|+I茥^q?NmYiwѵ_//[5V!MB4xRSTՍKSF"B}cjFqӋ\t̼2SFTDe2VsNHekJM)QyFdfY$J>;[fE:5& LƔn)tsQߧC=>4h0؎VIB~\P%hQ)*+Dw#/}J2UaPKC%V5DGR?Ydv;8IF'eOd*ӰUJ,#&Rv&okԣ+t53k*MuDƘpCnj.$/w!$"oO%]Z{hJ䰗 (3dGn`zn{fth&p lDk[h-\J 5&צ¦ď)ie)q RIqko]{w |BΡ` W?:pXsoTRv*UZlTQ JhrZtA?oC?>_`U C_4]MT2ʊ%Ly64QH,y ;ZV6E |HmNISV5-MH QICޠ>Jjs6m Z;T⢿Т1E]+zfq)ZJu4Ž yI%S!fDi4Dq?*fCoQ*2StuI)1MT) I32A""II="nOgȈqGO F3Sffffxۙ&S$ŎQPHIv"p{w W B W?:q+\W ,q\|'oI +\sg\|'oqko]$,sg |BΡ qko]{w |BΡ` W?:p{w W B W?:q+\W ,q\|'oI +\sg\|'oqko]$,sg |BΡ qko]{w |BΡ` W?:p{w W B W?:q*3mHe '\)dF^Yl?Ǭ! gl?Ǭ!Fp8~-XAlz6cq[gl?Ǭ `a=aűp8~-XAlz6cq[gl?Ǭ `a=aűp8~-XAlz6cq[gl?Ǭ `a=aűp8~-XAlz6cq[gl?Ǭ `a=aűp8~-XAlz6cq[gl?Ǭ `a=aűp8~-XAlz6cq[gl?Ǭ `a=aűp8~-XAlz6cq[gl?Ǭ `a=aűp8~-XAlz6cq[gl?Ǭ `a=aűp8~-XAlz6cq[gl?Ǭ `a=aűp8~-XAlz6cq[gl?Ǭ `a=aűp8~-XAlz6cq[gl?Ǭ `a=aűp8~-XAlz6cq[gl?Ǭ qko]a=a 5*3d7!.ؒ3>BI cQ~_`PB-IOrT]il5>$'a4 "Ejpmn%: $if׵#,r2 ω7 *bi67FDw%̧d#;eѝZۦ=تm?s}¼+ZW;s/$۲r|;2.BefB(A +>K6Lvr)i/!dm]FIR_oZZۄC6La ki8[x-$"$_1(D.;\*C;oDDē*[fD'MQ$iJѳX"Q5.WӬ9 igO8dH֣""fg )u:$RhɫG|k;&!V61iuh`NFid'ȹ}+V&UDL7a"CDhxrAhQ:=iĔEe!6J.R \bV"TiQ%1OC-,!2#dv;}6EM̱-ȮI($q2l,nYc^MBM3fe&MZÂp Jѥ)[lW fV?[:5t!rDiPfLy(Qk/o~W zVOzSsDɹ& D3)FF53f]A%=%ؙ O;ȬwpTӌh%n[$\ A@rJj7ԅ,$RD)#32JH$W;Xkj]>DqCrrPgy-yYe|qɽ jǤ: $_!=tgߕ PĬH\xIY ?R\Sxң#34,>IyдgU6j "Rm8V"%Y(X-Ga~MaK4~߿SX}0yW6Vqr)˛nI"t4 mcZW2O.[$[fdrpΚJ_b?ݷo)q/MܔG@!< #W\&dZși*jR9%$J(l/k OS'S\e+N (jS4H!|H$sInu.uYJ+=҄%)C< ): 'SP- [e[sԀ _= )~!R}dg] ~$$<^m|R(IP*@H)PUq hq2T /cdWw#lf}I'Is'RO5Oybf2ˁ8ťAn:R< dr׮SޞvgCZ3&mbήcee.xs=lA*SnKmY!]h-A`%^'u/pxst>q޹Ǭ5!IJhKN,,OABB{:NEwFڧLkMq|DuD(mIR~vgCO;3o ȳt2wl]X[2eJO>*rOuP.M3dv Lc4Ҽ5~Omqw^vgCP\']0=r.]naN; ģl.1hRTTQr=+UlϰWjě0.aj9GckY.8 [U5|O֯P>jf}S૧ٟ`Z~(j}֙-)u81 b-<-$ >C] N95iZfKG#إ2&i ĩ ?$-{(Mɷd5s_)m=m4RRFJ׿Y_^}E'Wm̖Eb2q) |ڸ))lvߩWu]pp[Sp"jmJK6ЀH RT>cwO֯P>iZfK{-E%2c–e!o[\춛}<9h#x#[jZ~(j}֙X٘m[S$~9[y\I],FоG~E'WmQI@3%1XCjPXKIC;óܵ[ nSΣ<4R~z>O^}2[sĹDi!d%O^}2['Q>Zn0ٕlÆs_RUS)*$~PAO֯P>iZfKfϢ?fJg3X_^}E'WmlגGL#fk Z~(j}֙-S>ٚy)DaR~z>O^}%^Jg3O%3/O֯P>iZdkL#fi}5QI@?R~z>Ly)D<Ϣ?f)?Z@huO֯P>iٯ%3GE'WmQI@2[5}4S>ٚ(j}֟)?Z@huKfϢ?fJg3X_^}E'WmlגGL#fk Z~(j}֙-S>ٚy)DaR~z>O^}%^Jg3O%3/O֯P>iZdkL#fi}5QI@?R~z>Ly)D<Ϣ?f)?Z@huO֯P>iٯ%3GE'WmQI@2[5}4S>ٚ(j}֟)?Z@huKfϢ?fJg3X_^}E'WmlגGL#fk Z~(j}֙-S>ٚy)DaR~z>O^}%^Jg3O%3/O֯P>iZdkL#fi}5QI@?R~z>Ly)D<Ϣ?f)?Z@huO֯P>iٯ%3GE'WmQI@2[5}4S>ٚ(j}֟)?Z@huKfϢ?fJg3X_^}E'WmlגGL#fk Z~(j}֙-S>ٚy)DaR~z>O^}%^Jg3O%3/O֯P>iZdkL#fi}5QI@?R~z>Ly)D<Ϣ?f)?Z@huO֯P>iٯ%3GE'WmQI@2[5}4S>ٚ(j}֟)?Z@huKfϢ?fJg3X_^}E'WmlגGL#fk Z~(j}֙-S>ٚy)DaR~z>O^}%^Jg3O%3/O֯P>iZdkL#fi}5QI@?R~z>Ly)D<Ϣ?f)?Z@hun>ueK| RH*h '}О$P)DtJA:rLXSϾHHږHO`ceI.e*j?+ï3nl0nAKIۊV5fo֏'՝UO?}Fh2}Yq%)JХ)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@&q.,1:tbč1}i HO`NF7aV\& <@Z'0BT'yV3'{[lY/LZ OyٟK RT/~mtReAއK~δ>z7GΫ)JV)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)JBp?hT֡YOcZ*}65Jyu]UJyuL5ko֏'՝UO?}Fh2}Yq%)JХ)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@VS.֊+)oEOæ_VC.«_VC.©~~:d߳O:>$)Z(((((qrg|9HqHmJp'h?-I+TL%|O2T 37ߕ.2um!cPx)m .z ]mʈa2`:/$$ Y QhWnۮVަ.䥢[hmH*Oy'kK[9ܽś-%N2J[> !Iq%|B=O%S\q4 7c J JgT@bnAki) s-HB[P+RGr++~-\}ԅ;9y-%KCFV@ ogȥ/6^0Y2VVg koEf|(;m$xJD|/tr\'8eK`nC҈fLDYOH֗ヨT_vED_A @H$trY(Q)p/b>F~7x蝃|>Ey9rdeŊχjث@s#J4ҫC->ƒ=|Bme6G:jBvvx/h[y)9J׭ ǗCK.:7[V=:?!ub+ZN $( m1A$:zPEcr&&n3mL Ze77*:;wڳ9'T1kɔLI(B pA$$kT@aې븵ol-~!SgF\{UԌ$s2; [_ J*Z̷N/` ٘.3V;DEp9R⺇XBl$'(ֿZw֦A-?.~ Z|R\ |$[! J96S@;[mnkP5_S$͉]7Km )RN k{ݠs?e2ƐO !I?Z!HWw,vLeDLs~]2$0ۅttG`;mgdf:L[ u:YIQKl'j 'dhd}mսyYq1Ǟ<x+䨏]1}\$=d}d|xq^.Pqa:HN:Yr1w\jTyf425 (BPlˬ\P$HE 0ZH,W,ˎl^qMC>oZ`.Y#o7;ɄK*|RRP A:Ie춨1j.t[A,[QBB!*?Y<W ?Y<W 7GΪǧ~δ>hRRRRRV.~u]h) :)IJD9YPFb`XIVIQ̴ Ej%t,(J ̓"y ҚRuM8A)ZHRU߲u(#10,V>3'5r-&TʔHx) 8\Zmʏd5_e`e6ŃjW(WRIBRV6{܎҃ m1t D%;~[ZqIq _%J]pI#XM0L֗䷃[De崕(]RPy@f&V:S-\Cee]w=o.l I ).I˭.đ-Bc헓4+RH lĹhKo.,-8R9ώ&5/N9p&:-kyGp9r{h&n8?*6,eeIZZYAR '%?^Pxo֛u&v$4TR7l~J1ŕgՕ)BQ (oQE*HP);jV[a\a5hXy}ٞ8G /)DN~uwF̷ِUȠ7ဓsm(:(gKi$7h:U++|xF3AJ|"#CIֻzT֔ =hr JKT$J꒔w=!=nՌbQ2KEͰ<_A[R7× Vt6NEJdI&BJt%ARW{44{Wǭ8ͯz7GΫ)JV)J)J)J)J)JW}e꽟K 7&sT"$)JHRNl׵X_1oq9t)H31ʤ!iKdT;QQ;~l[eo!;&z!C@@%*ByhT WCii-Pņ۠p(q[Z,.2;n?M+1U\h4^,)Aj/7;Mwq~[a,OjY;J[@'il==($}ac"7LTd4︑iRGu9=."c\E+*TPc.q F{=C0~LL)ŶZ*oUS[voٮ|< {[<߆eC~ahan\we3mma!6yn3)#C G~'{,ml->NyUKIK>6GrܙmxB2Lvy eN[O4B Q{@}1KԌuo+e[\A$%<ϰ9/ TdwFEnSlG4"Luͬ$e`p2r XW;֒BFgh P.޲{;s˶ĵrP)oJֻ6޾uD`ϠJx R kug-#mJ߂wK([9N=k-2;ɒY}I3#\G>6}=5|)*oQ|ET?ZׇZ=^f6#d{nRUonQm-%dk(z[QhuFq/6|)[b9-Q-B5~;\+GOoaLM}Uŋh-H,Ɔ`=Ψ+/UTܭ{Vӥ)!h ?ǺwOv$dMO\#>Nԡ^W^=R:!sW,͹ǽb),B-TA#$W^mdy%˥-XĜruIooędVV҇7IO/~d\ZfyWsSa.LF2B=L?S3r_rRpU_g=|X5 BL"ZÅŅ%`Mr:Wt8]M-G^'k._JTL9HB0JG*H:Q vw%+@!.-((R C]]Ul.8)8yeZ:JJNzֵt9jQ.I%CQ۽qfh{pQS!*pKثv*ٸdDB*\$oPZR}vIŲ`(ǮQsĹ-ݛ ږX%!={w+=sNGcfHLžbTR ˊOkU\ɹ?Qrv'i?J.%<ŝr~ av}e4eύm.ވDiI% m?=f]=%}1XQS%@*ϧYf:D1Oӗlߥ9QէM]nk :Jk!<Nj\̛ gm7ihY<@N, \n"FRP[Knza$ZQ*Z Vn_Y1 QL˫#قKꊤ%RႴ{z5O*?Hzr.|!FWa5V )$D) cK܉)dțr;^rFѴ#Gw!b_\,(QqB FDal 7G]D/>*Iz7GΫ)JV)J)J)J)J)JT]Dy]KvTU\Ȳ8(Ҡ qjkA 'ztvӞZp^aGƑv1Q<Ȑ YIHY6SǶSᗌr~Q~²x[m * 0S+Q@jǥMNOQ#e gX$m3>q{^:RJAַSl&٘[,e\QkGu[˾:PSHr $8TeHQPG!,oAzCk̼F4 t)j犸)(HJTwJ{S4QQ--F Q\7Z7Y*RZunwu#v$,۩ 2VÁc 4{҃V:]ZF/Ď[f܉mi-T]4Ocּqu܅f\$2ZBRT ;ug҂agRY7KƨNPkplk(cHm(iTCm(5:sޣ5乾PoTIbn):u!q{ī; 稝*|͂Ә,q:@TR:JN54zb+NȍwvyrPȨ <(:z}X&#[e>쯱3)[:Z(OrI5f҂'uSt eq,aDT%e! Əzt˒3KS3 hLBjk܇ SyCukW" YmۥW y13*i ;}`z̑5Ћ }MFʣ ٫VJWZxJ&\ 32Im%>АNw>2.`6W,˻1Q*yQ{RF J ӯ֙q,r%&Q]SqS[tl@z/9[0q+Kv-q.}t`WAHK 'nKտJ u=`zƥz9*)HhJ2jL)w!)sɘ\C.IzbAm6bBd/OũN)< ADj/&kf7 -JBQR`*dFoՏ1S uTK>T{{E%XZ6R(ha*ҥ((((((((((((((((((((((((( SZe?~эht+AUuU+AU0կQgZ?̟VuV=?ugUĔ+BYOcZ*kP\1>?Y<W ?Y<W 7GΪǧ~δ>hRRRRRRRRpu*tA:Wi}N$- BA)Jӭ:Zq\T=Ab(#`?U(i+( IO@pm֜[m-m.%*:@|B%hPRT6;|JZcRy6$Cc~b }8x6׈9'FI;)_BRTd֐[H J;AΔ!A%@( 5wP)_9'G! JP$4 Q$($pG\u[8m%KZR$ t$0p0nx+@::#@|JP:::>Z!A|M({ݷރ+T A@;q:R W¤[ ll%IP%* i\\ZA[J=J,aC/Je+u`Vr)uG)JP)JP)MRJWZao6 -T=N7R WmqiBvt6N Waiql!ԥ@Rtt~BAJQ_,,qGkG{ևK+B)RNi\K-eĄ|=]ʁJIHTɠJQ\I?05#[ llI*) QsG?Aʕ BiVtwʁJRJRJRJRJRJRP\15VS.֊MMR]WUR]S Z?ugUcQgZ?̟Vu\|IJR)JP)JP)JP)JP)JP)JP)JPW[ twi†-xZ”!*cԥ "NR$a9fgbE(\Z]m2Y!;Zj崫~vxʚ]upCE4x}k{FI3-0LUS!*>wCT~Z٬wۤ`P͈%9dL`P.Ǿ+"rɶlVd8)f: B sm/*HqA]734ĹZȊ2&/i-iJ8RNt*iquWee֮nΡ(_(AR.w#=Nn߲3˵~Yi>a v[*[V=Dn?u\6^8k 'ZP*pJ)@JS?XZ[\ t{?ie<%Jie Qa-ypcLGo5t%N . 4uJ 7J$.keU"{~dT(xԆl,ZH=1wp!LG˼l, $Gb5ڃ\pB.X$cRU1Oqd)ΩĠ,~~_ be'I?vhlܵ"ggDmhhl,%H:#'tsx7I|uϚ}նqZZ`voOo,+^:͚Ջ;ꣀJJCjR4-2k\D|zprm_ž2ی6HZpO e#ao,Ϟ"ц^2[ͦ{;ez1%(x⯸W?y:'[k;w6=ԛ.tLmM6JN{:WStYc"nDޭ}]<> \}7c9 ɑQg,ڊ6-)J7mΧ2BL]JGXG@lAr(d| c0$9('v}+it.Z'H jF$5+p|P $4ݲ>W2-rg%1=Id,<}蚬#0贳/, li43//o% DvU~x=͕ކl[6?ckIrDN>-B};EEdzTs">_zfQN 0VP m8 ‡%,{jt!}*>N1ej -,%h! 'g'j*R-HvXo\B!#%䅲U%6qD$G["p-Ɵr.Rӌ:$(IPh6Q',falv뼋6Z(?^={{7R-%ڟrlP@ԠuotzG4.zrEx!ԇ.V9fΡZ8K- IK\O.t:e&U]p̍@zjONAbюZ')W;8\$n:8KM,,,t$ h էLB/._%)L3SNNx͗޼.!8FQXd7=WlUf-2>iԲ|yԺl<ҋIUv6ChUn 80/SN%e^-H۩mM iDq?.^J2G/ȿŋ~yX|Ft2 ␄2K' X +:|Ƌ=aj섹 "&I$CiA--NŒ] ntSG֋zfM9;҄kl)XC}/~^&Z6UθfFJٟrr`/<ҕx}BcctQnJ1-8_~ZқRS-eIm)JXHk'Uc6:IzM%Pp Dݴ4a~s- ĶǗJ.<T;%dqڎ5?oɺ~VAtl{B} BBRNR׻d_\.=Z\>  c9"=l-.),>>.$׿_Zf8rKe\.BPxAmb(+;WT#Aff&9dۭHe1Jq IHOƱ,2"\霉Fs r󐭉d τ+w>MZO2͋v}h%.9%%$KsT6<˵Ŀ˴³hMyNqImY<>!+#DT>mRE=Znuc/9 "Ͳty1opSŧҐN&RgjvؠV&n"GȦhb^/"5τΓ Pz[m lXSfd.0O4\ P%\J8>?ᗻ=c0Fm;,9 b"덗y>+%$ 'gUyt+o[^\/%jLY䧟W$ſx{屣 jٮ'ƴlĸJv b[ )ДFԟtW׼T򨖷qs7,c!bd).CЫ~k!{-V۫zm,(VNJ@_wy`vuCwViUEao[r!u|+K'OFKv;G~YڗɈ )(J<[% .'qȟrDŽˣZƾ) OtC=C}>?~mvmv}~i)- (ԱDIɧ@lR Z5\R4;*HF?Gn Qܧ!-L(%n?*U'^@IPUO?ow+3#ͶD?Y<W ?Y<W 7GΪǧ~δ>hRRRRRWV% G@WڥsHȇQ-oN&ĆT/GT8TR̈(pkN%}Hk#Boy̸-,GZ;$S\ZqC8ZBAEF"y%~y4ۏ:G+iwխ2wJo&ڮ$X-2OesZHJRN RV J$yV+ӫ-az|~ޗ">QqSϼ[\H:y+Ce :w>PF;IƧ!ON!ShA(IRܒt}y%embt7hFɒ'cFB^S-!JbkэNe9VPFښ7W*ڸۀX Gꐑ(%R+M/C=?/665n8/xlXB滋dՋnn@ܳ!&RGÑCi)ׄ] qdhpi`nu(APڵbo[jQ̛n{Uਓ#h. m6Agc[Sk}wȬ.xkʹZz3#enrK( )iJ(}Nڴ/[`ʅ _ʁ \o=oH\)["P`ϝLDv[㐗rg9V[uLog[ uTIAP5E[q[-1- DJ:W6|Խ^檻!3²\WZISHtNe.IF;r74NC*(QJ҇?V?s\)ξM|%1Թli {:`- OO -Sm K-:#ֆ )s{A!.6 I;Wϱ)i[?XLzWtPJPHO}KeNCpsyQr O"8dokMriL3#%v.^oH[E%5vԝ ]atA?04qP#g1caoc'JfۏMF}Z^K/o oRww֢Z_\d,6R3Ɏ܈.-PRV6#|dpkIZU@ٝw 0 )Z>bnHB3.4~o쯑:;vbgpK| xZ|t7|wzemDNIÐ}Zc/&[lP[b_Ln"#T*T#E)Jˏ~ĊrՊ)II1&ɖ/>*|P~0m*9R @g_:6l-LIJk,t4]rQJRI*Pp%.aeCB@ .'}}棲u 4J%)Ԓ}SoMӧńWmIu ) G`GbzJ:>co1%v? - ~+um.'+Gz!}P\k(:nq(~DFoa$jCe#riM?e7LR,\縖!4NG (RU_Z%*IRN>^Ve{lDerdVΉ1HYm%XKJо$)#H7u COKa @ [Ac8eĘ7\o^;:[N#ͥii Fpx\H:#GbkؕkbUvŭV2ZayOR@#; RU*GGG֣fDFn)kMjT {Ey KKix-QHNs:"ݗ_%]nKd~>P@qKj)N,A!ֈlCJmwIRT6Hwv5ש0ðEiFppJb3O%3/iT\l kҧǞ#55ۥtr5N}ҕQtrk䠴└'>ru_j;ټsbjyR*2Ծ\Ӥ ]Z4$SZ+8A}|s]wXŒ%+mq\T %C)&c33+ za9^$-q%AhV[ C]l"tK|kÆru޵^bbV6d*퉐^G5if1WIQ#DJȽJ#<$ڜ;+$1ne)w{mA]CHP JJJBQA>[ֵ5,{\)$6jz ]̅%Ex!:b2hnđa+Z.GC~kX(ejWay.,65G#~ o#oz]o^CU/,-4=դȕ]j)7;[r%ʌ0bj$yI v.4(snBS7{6ҕR ش+ /ݤ͌j2O)|E%. yqoZ+8Xf w[žsn?$le$kZHJVP #}>5"KͶ5*oe]\#WLBxJJ7ፕ 6,f^tCܗfv-R F~F~DFֆT]ӭ:A}6N-6PttAs;ɮ/sɷ ;n0!j}?%NhVI6 ]_Ylq^|en)l<껤kj?-%0mV6PRT>pGc\'Z޷U]>~n lW=qTlSe\HE۳Zio?ֳ`g="vk96Sz6ʼ}L*6P!kQI?08%Iڥ:+.EX\͖d>ˌKT%iP[%eGٺ(((((( SZe?~эht+AUuU+AU0կQgZ?̟VuV=?ugUĔ+B 3\& >#>a.+RДVpy֘liT6NsNѻV [b~E~wi3y  $)zO&Γ%$l,>-tndWļ *iCGW)hJ) S*)IIHR*:>փ WcZ0֕ ICPqS nWCM^l5 ɖ.6J p{ĝqrRGNm vNsϵKqs9G6hW0Ip ^POc}#ۮ ,8VsI*UHL ^[|dHV]HH$IEua ej>+VYNr[5%W˄bxѻj_r^ H#|aRzfn'91םzijaBSRSgV L%2ٞ< r |Ku\BH$%(w6z"F͸ķE5O|Eǘ%Ji**+@Xl!j[?\9JtJp1.xڔ5n8ŝ(.AZZ 3=#K, uKhc\bB-Z`{U i%IPT6Fq R2mI[8#Ԓ{A]c],l﷗gߕR5K%i^6T5۰^@^$YAfnLi,J"K)bG岮D0OB;|5Qxoy',/{B ' H:@M&TRj8iA)aր_{Ҭ #G4V7ւ'],Y؅{iQնɁ%'_5/E[B{ޞυ/,L9"3|taA(訁fZam. m@)zuUܓ4i{4kty-ˎ\KI'=ۍEl$˭r ޻[Umy`8Km+t H*'],*e8P@P:#s`qМvB$C2֤GqquGvuߧy"6L,Qouo⬨cZpr}M`TPe 84)PaAű̮mޞ,˭.Q"EpњHX!6AeG?@{oCo1!'arNȄy~jS+H9O-oHI!IФhPRT6;Wisy6Z'@wI PE\8]`@~,Yodm44e9#4#425dSrxVir-PN޶sڧ)RT6U6>dr^>bGHmL%%iSdkGqYͳ[+c a):J)KTTBGƫ ˝T^K16V-qƜZ K|KA!)*;$zVVoFN2":TZo>}4)}FT^-Źu[7JK䴗YJtHsKCzjwBaGyDxj[J X䝔sR?ɭ]Y]lGqH]gAn! q)R@i7gm'voVl/oEc+B3W j٥-~r&oH7X7.b#iL:ʋM~Dou0,ϝM.x9 q cs lν*_;Kqm%! J@A]tˏ] weA-ʂ$| :}n ֻ-pTf":҉~XRܔ>5߹[TC:G>- dH(~Rju[B[#ˎeGgy)@)@)@)@)@)@)@VS.֊+)oEOæ_VC.«_VC.©~~:d߳O:>$)Z(((((J}cBjweafٞcu:8*kG{U &y5ċV[sm%`, GRfY˘g%]ݺsc[*e%+HO>#jd9nj>wN#Ӥۢ R\/0_`e.4[ s 6([)O4Qw?emܺ? uR ;|&-v2쥐.y +@|Nyɽ3bUtׅ!7!m|qSjp5P;V3?Nm\W{jĥ0fJJJQצ(72bV$Hsbe7WPĩn68xAOʼJ$BjdvssDs2k(*_r`TX.=k[=9vȭ>h ӍKUx2>_f]~ 7;clƒ)s. KG2A PH~5NDݯG$tU M6e&u]4ϧKe85u=w^d_jnx*\rZaI-4C/bV+c0w|bJ|eG$d(vjKkVS2}r[ o4u6ۈ:4Iffu$=^^!-08̭1ѳAl׻[bcsn9DÊ |wCl@lt{VdB]<.L$E"$IQIh7${)y>Cqq GIBTrQ$v Q9DZہjBdF2SrRyEJ=#C MO̢!œ2cz&Nai ө A-j{ܤm3ZX# ~+l'y/@\R~tCb%E$O^)n;4 KI vz1l1KYmX^mҝ;$w_p0mɥUJ$>%)1 -V/q/Hƛt\ru&5QYe '(/JV'#4a;3Bj><[bT<Ԧ˲B]R% @Ja9--fZo6#G\L> dJ)[BJHd)H )Z'}nSJt]t<@D]jm6q謂Y~HW/)d%Nˉj㑒T}SYq)%*Wqe1d>Ύo3vf3c4 8&;Ch!\VPWa"fQ0U)Q.<\nYF#Dy'ZWJ.پixEn"|Vv;M6mh]J|gp:+β<3-W;}]Z[;Z HKڕ{lVX=8{/]nfRHB 7x%-Ϡd,Kz<1+i%R#KlpA'J̃1Ff}PF@dWW]4T!9v]0Zr˾AUDc˺ci:qDO[)Z[M!fO̹Cg~:dۤgPmk攬qH9l%apȭǔꮱ-\~֧diR -/]P^h2 '²e sq!z) 1]Е,{b=N9tI7rSn%B X'z T2;՟\AEڎJ9!N. >C~4۸_ZVPćo‹no R$xzJyҥqlMKO NnP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP*F7 SW˯pW˯pa_~δ>z7GΫ)JV)J)J)J)J)J)J)JGY]5gDvħyOI[^a< ZJy pJ@IZc$gr8:‚CjI4(xY8 KoBC/*3 v)/;H:;1=G}`a&[w7'\o B\t#G@*ڥMp霉W\-DZXңB6V<6ڒTK=_n0 k672eMO)!r:ďAW((/Hۯ9ǬR r ])!JyIH,z&LuJ:SZeN㍣kRV JԒRwSJ>ev,tYSDlG;iq1a! TLRIWqw\]]덡۬{cژE>t.yQYo]t҂IU9qb|~lw8#᰿ rv[Gߺqb{mE0 Xg{zuud҂<$pEP\_׳i =>a+7``"zn`HCY :?J `Kma]B%>"zR (]0رK5Z[==ĝ"ek/+d$@oRcƺhsJٌ\ C cKIq Dgr^QhE +q~(F/lzk^ R R R R R R R R R R R R R R R R R R R R R R R R R R R e?~эhW=QeؒjDwCn+E*5>? ZItwr#!r3<ۨ*ma@5j~/0,mbW>d _mNÂ+R JIJA:B7GΪǧ~δ>hRRRRRRRgt;eJ?F8ͮNr i%(91Fq!_;/0L-CAe&@~"?%Z~ơ/U-W*yvbZeo6!%akl'{w Àfϙ>;RzEvov&r[[K-Ak\_^'"\̰m˓m\̄h{5Y,˭pFf|)LvLt%-8[CӚ$Ͼ4N 7|,rKjc]ʴ6! ;n:2\RPO 'GU-gK[$\d6|ر#-1]uԺAP└v(=tsdN^/L6Cm*BSjB~8V:W*[ɷH(7 ,LI2g07//)AE(u6; Y,d)m` e) 9%Sۏis/֘>20bX .'-Gtpډ"Gz ѐ˻Bw!Njgbt_HCTgDPwR;wžE2 bcIJF8 `65܊F˃vSn֦u` w|~dn6T?Yi0Cxs`M{2VKVydۍ%ei쒡脐H=q_6ǼqrU)HqzS KJ4BE%|7#+Ry c+>Yy/? .Ok{2f1o`ArX0p<8)JR)ED$_EՍ^ߐ iAaC ũޏ*<fԛ{պf0z9}sim8ۈ I)R}!'ыws&eɦIJk.Ɛ-h!T߆l#kW`\  ܮ!-Q>rLi,%Ò{** v=d.r3ʔۑ$n\(c8{i¼UH#N<1cvCvqmbE %ʼP^Ԑ!K^=:} ,ߏCᥤ%8"{A Bҕ%* SdZҫDHG)zMM9q(>jH{ܯ @g!*B3Q*A. €Ҩ6Qm )rmb %V((~ANUDRvTdMja9oO'S3Tjl ZcZ6s]{uqzK9y  $)Z((((((("}\nYV2g3"+R2̖IU΁'[ lChWQ2+D;1h;vqKJJ[*mRR\B㎝;J ![rv 2qs M! %%{k;ǃP2}W k+KzCįke zZ@C2XrH_.a_[* G镹0~T|3@}N2SBH_7@arV=$E<2LIi@Z)\HEy Idѓ}NGJ|$ŖJII!H'dXcjOȭlhrYYJtBOILAAXzW[2*mm΍%>G Rr]`?mn*\;cz䩤!kB w$vݱ}JC F,Y ߚGE*\uD)J[EAM@hTޔ^ݺxf=+ݧ\9 i$H[5N]oBqȸ珪d]1YNHSV݂NjťS7[i̻U_ J4x=ſw1+<~bMDKA+y6m{ZȫZWɫo⃇qJp xLHLiOR Jw~s^ژj߶n*aJ[L۱K W$)<@%y<טCmkKњCE$m.6ZVyi zsh#%2Zk$giCm0T '^(1;WE6)MFPƂKGG`F$vuFq-1[9I 5%~njYJ zWIn7'ǟ uο.OrD&q#q/S'_ewk+K)-nLJ$%kI`) ZG%oI](+lG Z_Feߟ,Q M:Kz_9h9OARŻ_w?ph)ARuW'7b-U~1D x0vv::b; z̧Nwנ,*PUxm.^v#*^%H[n8:x\WH2Hڳڙ-"Tp'JR$T}-YVo<>^7(#~[uPSoDݼܬ2(em1eT/}Wa_ wMb {HH zOAkD5t6 &Kpw;oCҠ~Mnr,MY`;2*kCάIq6HJ$`-P`1KfILy&EͶMDbQj.8{ǖ8^it/Z]2a229.ᰅ{˴BRtIPf,cCm\) iH ?`% *Q%$Yr/_Wnlqop\uNmR4wrJRRRRRRU}oC_ՃUQ WS鰏o_ EI*7կwo֏'՝UO?}Fh2}Yq%)JХ)@)@)@)@)@F՛կ7Z`+Zj!'[eHVfVkMز𮌨H SrgJZm]"uC 4܉`S ڈ$)ͥ7W.v16 J8 _ ں\qWI qR!SBPPE mAJJ5&!V6+/;:ijS;J{!~xf/=GD۹) j<'M260[95"S/rT%'@>-`aHu ^M۪s2tDaxCR>b1L_C6KNׯ{Rt~mPH`ZPRt$|ju7=~Xb/ۗnq _ڢ ZP7EZ}CN.(6V۲VV;i:Rv{hޝi9R``cWy#ʏҕWCi@ȧBbʔ>$ =TY$Vb$I"[êRʃAB9#J DF{u>1nؓryUey-=#E4T842_nP =@kF}clZiM/TT?6{W+a]z\.^)Z6ߕqkE!\[OJ~3h] C1垑>@T6vП}J'|gVnW$K;9#55pjixtQDx6ı\J#ͳ2v`ܒnkNx>!t)B[)mVIY$:ڰ}3![70*e帐O- ޼{VKJ8A͎Ūo-8ts'[ 6dT֤2vR-)-+iN#e@vy1n`9%es!D xoGi Ӌng*KU!M\?2t :<ma."lwD88䉎9)lR}ĕR},7S1S2cWI.L&[a2Hq-+NA- nnnx-(sHW6;6cuod胍|^^yD|$B$)dnbYZ2#0R!!)AZK='T6m]i>QsX :OD>4dKvV%IE 3|r )'F77ϓ2CY}ۃBq) Bր+ke9gIӛW. ؓ쥈oe:iR(ސT{=g{9{feqj3*K} ;q; {(:imf }6ܡ-- Ĩ5!-jli H%:Ogv;7}JTM) m%d )<]vjis.ڢݧ,Z Idl~I)DmECݠtgpM% ۠Jh<)]ԒN4ce6m!WPoO#FZiN+{DRˡXS}nMϏݦ['4 LE*k4>*Gb V5{p["7qaiIXm:BwqHj'HԹkyͮ ηMRtC-DoicUV{R-" ÎҘG;O/~JSksKb5Ͷk _q.y-|)L;<£u2vG{0y6֬rdCC!Q.:_G"h|oHَ;>>*=`:}k"ƐD4E~UZJuykTk Eu2uy:1ɦKuh>4D2!H9_!7GrEC 6o(68[J[{ՏPΒĕې&\gܖaJЩ+)PߪHʽ5bz|^p-lC%d ގ::Z[rrr?f>l.w[Ki!#ݩV"ǹ֑yf^7-e2\ 24Bץ<I$v0ە<Ɵ;*L~mtVm2!Z@R9s>,r,S9p"zV-B+p$XldDzZ_f2\u!ȢlP9%יVᛅEơkO>4fy%gtY 9We!;2Zp Psˋ.m Eű4a*C!~59dw|_$qD8o5jWyZIBq=xgwpu=l6+s'^7QA][m'9%fP[xCƓ d%O(SEʈ{ c٦W7'6wsTt F-ʔTaŝG5ࣚz7GΫ)JV)J)J)J)J)J)U/Q˔KFm2ŘBq"#-0߂ܛt!iO3A]Ja.mIɮrڟPbp)ЊĠy;iTs,Y2(O''!r8)zC+ϰ2%7z7O:L73gn- ݹภ/DYJ?Wz T}@;Sǜ~2T T鎺QbZ콸Mf<9 [WㄔT%]EF[ Z!C\ YVX879-KTT,o#E\mEcφ2#ȸE7NH{ZRێ "[F61{-\fm_Lx2,x% J{"qpyfMwGض\m 6%.҂Rnq}Ҿ^cɌ̱z1&i+/)i/>H=GkdtvMdt80z~K,ʹTJׇzzz?,"2iBTVʖhWFΒRk?.f*Dcf^ef%%8׊%>RVw;4E+\1NfCc(vu\6mFrR<)QZc RަV0̞j[DK]f[(N@$nݥkFų ;.Lc=6*W$%QMR\Tvx+Xp"؉LGZ#-Yi '>SΦ^yąx|f. CJP)P&T2gHʐo殎.NVxQ+Td|R>@A +Zgu3*; x2 *>C?1O/RH=Ȥ^9rαTK(HS.)Jb=PX?eu="Y^u5w`mZcxbalR;gO&;d#"2\SHPR9)1d؞HuS\KnrYb3mi*GG )T67\ܱ2'\d?2de Uنemz7GΫ)JV)J)J)J)J)JQ=W]E\O!:GVKb;aN)JOa/PQ.ij ѐ(\ʷD'd#>*]SKA$(' ZحpDv%!Cc6e^$)l/H)atY֙ wWU3',Ih+t+Cc)ADfkv6l]c eR_t`BF#آL?Vn~## -p:+!I# PS)4\bЛ[Wv\S0꽎Yr8&ծ.ƷM4 .,rv8+'J g~>~:qf /qH*\*[E?OUwfVXQ}5f0ċ[,`}m(rPHNRP`ۧX ͯhRvJR{Te2%2rҋَʎOĔ%(@*RHڒ2=0]}km+G4C~+{Þ+tbXz)q2+UY>zZ-[G/GÏ6y'jRciASd/O噍Z#1ym/%du 3ĤZ𫌾xy1!YaًXB!N)eB]HI NiA $'@r#v12쉲TEè oNܑRNbmZnp$X9Or蒛[^"T{%xR\ViA:yzKz-Dτxf[$yd-( B~9xfʟ™11o8T䣹e(^ƶux҂ ur߈=-96{'> _5D9YR$;l(![RI CpRn?krbߎخ ^6W;P9\qa> jKMw!J B@:X]0ί9\]pW9 4ȻbW.Di\65[QJ _3x1 >f4hheb6[*m]mi;HǸkNXD~+)쫋Du q)q©/(RvC̅M57]n{oLu4έx-)$v߭JmrOd!j0 >)֒xhc eA[ju1 bw$X\\~b9fq}IZh2FygO˧Lr6lǟ{d $Ksy#CVJPk4|z:a&)ăӆֵz V V] 7+I.q,ÎҝH-iPOt%zXI[tt=rtk*&\Gqj}o-mO2O8H I!D1ϰưXٙ)1 1%L[|-,dqP҃R=BgFkkoCarqOkSBK.QHd+BvH3RN{ɻ ̈́(hi!$Đ>zATO/،<(\jR -(4u >!E{#c;uud{'Cke)AZ1W\+l]mZn5ɥ+^*R8 IDv'F::=ƏoGe( ]R)'|،oHrCA˦7_%_t֌b+,6-!5-p{7:h: mIi6KIJoj>1]d\%caC]7%G21+J% y)$&9n}|96O6lI)(x,2 6TTRTt5pR;۳Y-09\!R#Kwւ;:#H* HJ)J)J)J)J)J)JWFW5]X5_u_Pu>F`tT}Z_*aO?}Fh2}YXo֏'՝WR R R R R R :6e YZVIP} $A߭YM>ܲyV>hONOv-)Gm!ZW`CQҞ=&gNUuliO?!6:Ki\] V13 ߬a=FB[ym%M%ĒH#ٮdjsSZ%Zim ]VREF.M0m; %_v/]tppjh(kRAPH$-]fNL'Oq+K {i<"Zt*kW7*clxRI} -” ƦW[ijAW] kI<4S܍5}ʣ@ϝĽY Y`qn,1ID!m (*5O:+:m=~nodh9A--1#e\r KpTnZe]7y U[L3 BOi/l$x~ c1}a74~EIsk2Ў#IBVoiWʪgTY2 N$5qyV6JׄwQ v$K P_n-BԈhA=>pC:Q˩:o? &x"L꜄eT BGmMwaaa_W.{kQ풣ca('[mlV0{ ؽȨmg&(i]}3 K8RyP`q{= 3{-߄iShT"+ϴLXfטGo*q G68AC|=BT[d9ŵ<{E D[0\SA JtgKړh=9OPg߶d1r4E}9|zՎ['bY=VLFr,Y9#͗dR'@_9^1Vm>7o.}wZ#-8!KIW%=MtuO 旨!v!mmiO8-(>A۔3qԦ]nI)TK;+ H҂t~zz\/&:>&:pn5"=6*VԔ! @ L-9O&E~%gJ6EGI}4.Hnľ#%]7"V) m( BR=TNө=_d,WJ$1&LtH)JAQ[8WLnO> lqmEz7[(MË;ZxT yG>lNoH.*9-2\Q {q'; ͛lϯ2LRҗn:xiCii oeDZs;ٷX76z[d-(n>9ix|TPBt)'|k u/ %ټ[&B"$B$t9q\'6x˲j[F/BXq.3o( JJPuш!@L&6=ԫaS? R%8 Z_gk+6wg\{{V R ٙ5wcLZ]gjS қ{A AJTFX;MVP1\o#ad;Rn;|JKk#vBIJAՓ L[{u'AJ\WdˈeBqGJHD WC'.7|[Gl A>6-»$qJamf$ݙAW7*eJAIYWXsqȸݐb3SBQ)'D}t{2,s38Iy ?|߯jxFK0 /y*(v*t ZWVg<=,dZI=OaZtmje~"gq)P".To056@ oւ.=?.L\ƛ-Z\4ctrIZN{OP"%t*1>Vo}d<.N:y k $8*̒-rv_fEEtXm4B)5llكٺsrKW\$r$<59Dkќ*j怕E(lE'6m5zn-&T >;)K~ HpNE&Y%ֱun~<x[W㍴P m J=#ʤZؕ eImrx==eN)FlҏH%5nkyM"ja@.H}HCP.:{!!GM#Ԛv);8rL,-JH65[ށ?NbtQ R?|:SKNl)Cz;B5ǘr6wۛ C4ݐGJʶw8&EiuŜ`AeW"7*а:!n9*5@T(#XVKP[`HTADt%^i ,( ( Hs㨶c޴@ua+"BfBwBPWHVkx.jm"39(?u,F˖OXe,,6[ )KzÃeoػܑ0ū6h(ujAH@ЖUȔ뎃oC7^~WvJm7"*RB}ՍO\o?T[6iαmSo#vtUGiςv J"&cz1̻-xŐ^Ym }II:Q;w?%Wrs"t^jLjwJJG: GvR@٪N 3[݇.1͊¨m)*CN%i RU&ukå߲4Lr=Er JH-rGT@XuR+711\.3mAӑJ ZOiGIR%C][6?o*9oP2R\NIe;RP;&|~k;Y'יK) Q0N򣲏`H s_eŸaL[ `q]^)G@JKqgHBJuFmHKm!(BF@|K}b=%mmΒJPQ}G鷠 ʈ)RDԺ<^h$O˭ԅ]DEq|v9 R3[J('Rw /-M"'!Yn$ 0m ĊR 6JBPJt1~Yr\زTmҞN3a#CzNu1S" % Ki!i%<*SK2RyQİ 2 K'L`G5bΓ3 vfE7u`sXe\Aq m !=T;[ ]sar#6G[jH'mG CރUP՗6{BxІ!ǰNe%K%kBv}˨[L+^=q6yُn4lNu849ȍ _Jb L=C2B£ċ)hqgczg^-/ȷ2NJ4NP Vuqhw5"\Tyyi NqcG7P{)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP*7G7կwg;S M7GΪǧ~δ>hRRRRRRRRV!a?2gCH2פ%~RC'I^Ym?`uf"Ot;Kj[iW'lh ҁOh.UqFQ?6)KWkzZMK8q(%I'Ja=Lg zn"x^w!I2R0H^t6d-%UfHĈ,Va$PQW,jOf˖V\m-.Tv0'R%GAeҨ]8>'eg9"-O4!! Gv'ӱ=+ ce%(K#5᧱YT6@,V{\s+)xpl~t%^)+$qJwVUvzFYm"tw*:R: ڂqJk}^a2s7do\ Ohwϳ%]8D͡)"&H͝A+sn+J>j eS{X1CW+n 7 :xL+Dړ ]S]P]c@8FBěC,C'V4t AjR^ufe U؈\c(ޢLe֋ZXK_Qk#hs2)1npl3 xj!)*pGАU[d)nuĦ3_ ښvH^5!%|R=l9M=ur]|hpyJ[i;K{ AiT6|"^6(QFeWeOI ==@קz}BGl=F1+Ŀ+d(OCjQ)ͥRl^2ȁHSrqjmJue(I<}Hߴ!Caܟv+ .bCjI P$({ o`,Uụ%}-NŇ JcTIK {ji(Xmqړxh02GqJqң+))U{]bś&",nsnMr R#;y_/~)#lޑaULlZ_N"C斠y +wUnaզ-Qno- uS n%Ҽ`e``_"_!5d=KpN5!=/D t˕o1ZȖL1 m!+B׸{eS|N^%6#+QB:=. R=EI}Pi6TJRHވR9y~qBw'_츇T?X$WLި-=7ȯW6Yr]ž:Y%8RӤ{.*U=uVkJ[7Sr,N9\|dn #GM$oDVM΂RyiK-?rO]- \e2Ӗm9x0TKNmIA[NRBT6.:UoDM ^߉)Dޛvۈy KBǢY՞eXOS$oFJYRi {?^NTCNYVI Y\f8.4YÎCC 42tR ^ Sr[GL(q~+cێ8 I)ᣳ [u=&?afCly+$8$aa%U4Gb}՛uZjf^.\#% ED&=7[V(SII)lq#)%2 f,v' U"$ˣH HiKt4 h'z/L pO/x>/{>/Ϸ-է溁u}Xũ5).NiuO5_?z *Ul.ka>AD_C+ˣOIPV{~] UIuql[{ۯJwJ *!wa٧Z`ʇ2cRgE@IB[H*Rd'Z4w۽T)q$%v@QؒkdTB-.CSd0#6K\}J@ APX"եT1|ۥxvbU"IpHJi }wS. yBS561WWTP &UJU6F<,Ja8;׾ζ Hh Otש#Xn)e׭qnZqC!J|-@Q4 +eظ] TuHҒo6+m(ߐRAR}nY d]m7$[cZ -QIߠS)J)J)J)J)J)JWFW5]X5_u_Pu>F`tT}Z_*aO?}Fh2}YXo֏'՝WR R R R R R R R\Z${ۉ q !-RJT@uִG̹;ql9zO}&[pmDV֫vk#gAY2< c^ι.eSWΔ !.":|%kI!'N^756d*!xBٍ>ISiޓw$u5% zPr16#uo*Ȳח2 7ٓ(q錥%SA oiC :bה92RGù%KoIA}ETNR6I ]Z kOq!±q^D-0Zt^֠ DF=T2quI?%2F>.\-ght6}4:sa8.+UOX %%IW8 #ZL#~BN6Ӑ[KH%<B!2Q׻VE()a&ULFG m<RORJLC&_:~=/f#$sq)6]L~*)^;GeRҾkNN=gE"2q7?&rm@yM$֢7#E `xz45* Lg^WG" Wou NqO.Z뾫6Fнzrą3X A mنiMy0f;Q[YУL[mʛJø"F΂?dvh.HımbҜd$\RNI#bh J jȭo1ӭI]& <3)/8@Or7^u/~l:r-2MР"O7n!)(ZG [;\ MŴxe%ŠY]͹m-`zr s;s_ Rym AAuv?3 ~lc"\HRZJ_pMFvd_2{V/d_&%Y%Im6A9$(vm4!Km (jANyYC*^/{G\2ۗ霩|P IJ ~Z //Izd7hrvf˒dmа()Oi쳥9M+̼r}UA%Ķ]I*X>o#ekJJx?6Pkcӛ\쒃-2䦥Ba>RY)6Opolz$Hp)v;AR=( M{H@$zb0ċ9*˟8 R)W$!@>E @A2#ZX!: \VhfhmЏ`*~1JTP E`g_/8px@\EL {JN9 kfnp_t\PT%:8w|JUtv6= .g[ucwU~:fŤ,qd66>DXxGC;=H0v5!,ť~_'҂˔J}/LfjɄHu{Lr5/M-/vɋ#\T\v\f0W#M~C~ }Fkv[pc\c8vܷ>4pAI I 꽘q؛)wN>኉giφ>6 'yRIؖ2P8ǑŰ,RP.JRJRJRJRJRJRJRUQ WV WFW5]O?Q~fs/$V39joQgZ?̟VuV=?ugUĔ+B^e.D7cdt~s#?4PQ)[)yȫVsx$ 7:-bʖyrܧR&8p cIUζdد|\; .֯{OA*I=Onu$\[Dɑ-:JJrҐy'Us}g"/12Z Q +T qmJe"R}OFݻLgb:6 fE23=2eq)SM\J KMhW0B+,w~츌F>jDMdHhgZfDSI#]CMVR4|S4Xz e]w;<Ƌ7?.eR$oO2}mާۼ.p! ZR: ^|X9wQq. DZˏ6D'_}miqQ*RO&1Ν\+]4#% yXK 1!8y u!R+,^]#RdXU![ۉRuJZvq;~=ql$a_~]b_M TC* tlY7/6dڂꭎN@Jxk}=ۿp!3 ͅf\5fi1N|Ryp+Ǻϒ\N1pR[ #L}*A)Ji ߪ\ S s+:j78ɗuKf:PRtuHUO2lb#B:Syv}JI hXNF9ԫJdbnx6UmJiHے]YÂv7@kֽ02nęwzKn/>k) C;l׶Yvȼ7+GǯJ\× *X[ 6pv*?hWMG)ÅnWaiаVINؔ$HglbȈemqi (oD|g][=hqRK V5۬ tz? Dm! OyzƱN/-՝ S|KLʔim{ 'dvF3lx݆C4S/Ls5mܦ)VSis_)s֫k9-|ɠj97eD6PP|7Ȏ6lm{7[D;`acJ ZBϣ]1+< 28 lEgz^lyqb= h{{j=]mp.,m̾ԛDTӮ%m A$CAocG!c\;bGnhjC.IeP!;pאoK\BK#+P+W${ZVj0wI&L{oC}>[oa@F-tolpq`OqmHfS 0$?"BCKHx $[9p6Fyܕ0RIP< 㺓][`˕Đ|):p*[JUxGJ|l.v?:2WןW]BƩP0uW'?x!px[m@l!{H@$I;º{fWݙ=2@Y䔨xI_dU{e']KrH1EYJgSNq IQ䐤$+N 64h-ݝ-8OuB^(OO {C Fq ,ے.6kԔ8L)P@6FuspKz+"q%$X(G$;r }yDXllH3"Gy\#6R\{<9qY{G:k yyNٹa) 9߈J$U t89 dXWtHapqZ:IRZ",fqsƮK֯8VfTDvšW ){pz˕ZolXl-c4vJ4Es[Y$=ݳW'۵hz+R:2a(-^]AOrʈt[/[>87 ̩O=-؊9/;0IXW$n]oʴG!9e*l*, {+w - w Xz;0ix/J <[l(Z2>7"14%)W+-Ӝ~0dP! TZ+!Z! #,ۓ z̽p$iMj%8 {W]3\;{/ -1J#ۡ6T޼T!jWtȽHLx}B_uԷRܵ(Q}izCzHK1~mƗ#jCK*Zd5oeI$VМu?0nspqڝ*%Q q1 q)$ dL$IݔSWY%n8o pX5oa & ?쐕$-YfC TT%4[H[ jGKhBCIlV}v|#`:?( R R R R R R R R R kj7}6k3%F`tTS~~:d߳O:>$)Z(((((((y>kfN=̆O<$4ӪCnS:ߙaV+u>wgTMA4H $^gÜch;L8ķ m'oiRUvB~\&iWzXvlr*dG*u)Il%aEE%;I=AmL7F℀6NЮu3 KŖl]#5$%` $*PbS]}rlڙ@JBAR )IZ>vqN4AJq[VGs͌3qM6BPVA JP;+"Ⱥls?Az=*sxI* :-pTzӂ{wIedDBjɋz- S)y+JSڽBڢ]lMHf{rn%G@O)  N~s\|0,88[6dȒ"4%㾅v}#&tީ Kժ31 RgNuc1l )jS((\6QiO˰ǩSb:x1uq$Hy6PG)ph,U[u*& vǕK،_eₜR&O߭!V~ ijv+p.-Զ *K%Brr(.zTϮLr`9;>gj3w +CuԬr4;:s$!Ӭe<:$%J.bPUFՇ|+<ɉ:3tOKH@2-6w+ZTRSn\Yy} Hyb[)+)mt^=RRlkRMGPlWc&:%.\)qHҘWF\) b^+Oj9!IOf^_+JHUOHX̾iuT2$<:.ij 0Bzg]_g/kV/93n&^LdH+o[ZOs&إRVRٟma2i.%MUȫW ;hX,˹f4^y/t6/8r4&ʥR8/Wrym~=NsmGҗ`)W/l gWjeZ}bBqiwR[0B8 PZI)PJq^_7ܚƠLv^n"-[P}m}!R> Zd[n`8L1Leێ\BJ%*P*I{oЅ_8d=׭AoCnՎBsfeғnD*GAu*N|U=B]ÝvUC)|dXۋ>J'`HI?!;TRl7q;R%u˞Vkm!HZT؁xK;n] @*EӃ uE•ȐvR=5rܝ6(n/+'|@10 H RRJ@*NI$n% oMԘH*sFyKh{auTDx;C $W>)gkCCH䒔æ[ڶ6qad_S@J8'A#$ ϶u z-R/odGRn1׊#5Thқ ʎ q@G=SԬɦC†byK\sJ :a_]:">t56ÌEmuh*q ZǺu'E $.}iUrfաˆCjib!4%BP)eeJ D^Dj/ U F_t[BZmgW!U`.;}=͐ʣ }טe*ZJHxjU#a{caISC(^ )N! XWp7$nw*)}`{[,ձ%؅hʥU7nǜ1v7[-F-9pW'k^n=El p50b|*C8xJR*5W}cR`q;L/6Wɥ- |K@)@)@)@)@)@)@aV39To_ EL57߳O:~:dJRJRJRJRJRJRJRJR1\jYWݐJ(i1/䡀tK I$hS7PRXY%ȯz\;$(%!EB:ӵ$zV/܏.uz.9mxJXa3$66nwN҃Y7ʭqx/+P]ָ6h,8yzVҲ< TJv5H!tTkųW**|[&^uǗg$ڔ NPRA5{ A6-}6ANۡt{*Do̽j^N“B'A@!q-CޤyLna;dW$rn1}89KJRI\=}lYSdD4 (Z#ݴct|Czϩ19=E[<ۂYRZB@@k'1RW(6[mjZȔO+'Bǧ!k49&v3Xmm)w2tb)Zߧ6عBww[v|v,y"2I}^ԐRH ڻU_R#DZD9m]V/C.rsLTW=@dC]t~FgxV@6M=z<Ї!YSXeh;hozַB cPkIm`*~ɉ&qXqmF]>ʋ:xbbZJPJAYPqkOpmEL!iQ%[׈V@$zzPV[2 0od)it[1XHe6ӊSj Wew5Tonbp&5k2Ʈɩ,^ҞV ;JPU=@/5ȧ4nd.L,{(Z֛w+'\oK,ȽϛbҒ܌d^ĂR޴vHe (uD%!)=(5g%r,& )xKaS  aL 67}bj3 }o?Q֝*n+i5:u_J:'Vܥ!#IwaBAH!^h5ϋyoPY C-;b^ m~,ŗ4֒ hG+Y씶w܁^L#kα"L?sȞ"6/*"nT 桰Ov 1)mA(${BeKɮyVFAsMSd,0LK.p)ggC堫uh5fuܥxVM*$ *V$)E"Sa-߭qfs 2\wR\:k df[I2~-%IG!M]6x:lLO gN-)qq ڬO1WiF92-T8p)4s+:P&&A4[./q*x9|;#zޘrkAq↻cQ ^Dͷ7O,toDt<@[iTB VRIQze5u\ž #7 ۢ-~7xm:};CG Ak>Cpa.$5n|R#xz}inW9׸f0鐶 sJEE '[j67mT8@ KS2#[} TOȄ+wmBՄ>g@|EB&$I<@I`u'%u{PK=eड)'IԞ;ϨUܾᵰXYď}Fо$wΙg!2ϙExUL_' l6#+ $)ϢH8eM |N8˒A; W%v˅ӧ9[v؊ĿnR8r$KGpuهT'XV66U-R^@ N[i )߾JUY\Ir9E>eJ vGoMou/m&ܢO -<3!P]ZAqZl/K̲nbmqP rB[%Jۀo=&Эb?{6fL9 jS{Zd)GX?JTK{Uhi) ==RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRU}oC_ՃUQ WS鰏o_ EI*7կwo֏'՝UO?}Fh2}Yq%)JХ)@)@)@)@)@=/\n5H* nHHRH$'(168sn%o5Z %)t>j _%aLEBd>-9(v=6;*o&-0Es<|n >R+r°ۓzXf:.Eʒw_:°Op)-@W󞍶Υ~P^1ȦQn n*#\mRR lxɶcotECk i }z@iXī:øR J$G&[J( /iQNJU(ۺduͲJJyˉ*;u"Au'D!hu[B)P"ťF$Y-tU>`i,:G=AO/zocÓ%6q%^(Z )$`U]6VM"S&YǑvfdF~ciR56@ A_4G.SN|JPV +V0'^1;sV'}ɥqRPR괔 ۳kJvI1nJPK&|kBTT@v7A)VoW,Lɍvel]]ñPZ@J|-!G}־]W[A6p]CX PvEDrJaZ,ɘ3.GDtx`KD$ jJjt%1"48|XPz(+kw-6l"\Fq%HeAx#IPz٘@g]q[1"|Qĩ% ,+ kgZ=Tk‘|?*+lޥ\}uvm yk]M׷+ -לJ-$[-I !>V$ u߾{Jݯſb@LuzpZ $Ʉ;ʉ+e](V33!Y81 +H zoAhR2cj,K{3Q% .+!zxB7W,DMLSu瓘 zO4l+D ]HYT/v/k"55u,Re¥JHm`%*'Z=a!Dܯ@C7[m6`dŋobQ.:\>[XIB܂|Ds[&r"̔8`J(䷥6R)$!j )KRGtAgҫ +3V6{Me/o+ ] ip >^oϙ ד*]s!B\Ju,6$$sAiҪ˗YZ, ۰$'ZRb? %/+lӭwd}Bؠ6\{?HC[u4JH ,((% <{$ďl}$\(Rۃ`짫1-S-Vnؙ̲ %k)aiEfKmK qc~J Aɭr,1qs~$ z=y!pt+dI[;Ak]xpa_w˷[eLHY$l od|_f`8{8䆮 GdiDZnK ;IC`$|{ &T]d+ze?ҒM9m1])Ջ xZs&멒SLv88}Vp/*%x󙽘XV?re#JnE`k|=Fc+\+;kF[3* (J*6'adݎM UK~` _(%J:CiPLXwSgˍJK'D(ڙJm^lТ{]v[૭>$ɌO$ l㷭vʶ[e[no$wJ(H׺@# |m9CV(5sFC·"SV݋3̱%hJ\SfJig,1u%ODT5!Ĺ!=q>*x;|uov UƓŵ̆@W|( x"2޵È]AdO0Ut/,ɄJP=BTIG{*yx$-oҙv"Dǔ$)N\u)JA:]#.4U쩠Pқ R=IH#^s'~zi^ߌWc<lM,ensY>]i>*ԒI)-3'N_e rWLǤ\cۭR\xJ AuT'@s-ٰ e$j)&;mtv sQ.̽ D%*q$id;ƒU]c]Lou݁T*lg8 ZPkIF's;M@Oxufݴ-\||IZKK'N<4H5{ϱY'DixXC ݎ%-+AHjvluof򖦒K',H{{&U_Pla{Py 2츲9(fYv[76=^Td5⺯qOsAE'[x|;69d-̸QL%YZƔ|oN`i|ATN7[{xs(eLlDqmC-)IRIJ$o{:e= |ZCFBJ BN:6 dS+#l7y R{O,NU+m)/ojʼ^k1_mbs1P 2pK}t(v?fMPH@|0r: ^7g6DE ^B8R6)O+*oN=ج\d <ҽs >)޽t>j g=FSnLsr&d&JK\)BPI:; 7<ڔʃ+ y{P#=r<\J{ܪJ\XTveN%h  ߮ֈʏlS 84ڸBHx#`5λ_.9L7Ļ.d\YNIrC+m2)Ũ$o->Ĩf4%7.dud.ɉEb%[m)┟_m$ 6nv*kqdv]YlK-L$Jm HuqVvI J DfgaeoTve AlIQzc dV ͦ tYlY!IwHqj+/JtDմ]P ÐV_XO2J'Z<$} /ȷ+k~p)MZO}ւ+6|r1msHv+ HkJ?#Fѭ3;nIPeR؇Dh6Ya\iBGu({JE?ǧݢ]Xr0TYoDB`B?5f6gHV[%(K%- >[:ߦϚ˲k>?*eh7z7GΫ)JV)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)JWFW5]X5\YezlO7Q~fs/9!?Ln2yOK&Ii*uE@w%޻nZto֏'՝U.X#0w\tIRIK$Dq$}~?Uҡ^ZL֯1|gi)5B~?Om_c~PRkJ{mj3ZL֕ f7m=~?MAI*o{mj3ZT+kW߾3f7m5&W֯1|gioj MiPm_c~kW߾3ҡ^ZL֯1|gi)5B~?Om_c~PRkJ{mj3ZL֕ f7m=~?MAI*o{mj3ZT+kW߾3f7m5&W֯1|gioj MiPm_c~kW߾3ҡ^ZL֯1|gi)5B~?Om_c~PRkJ{mj3ZL֕ f7m=~?MAI*o{mj3ZT+kW߾3f7m5&W֯1|gioj MiPm_c~kW߾3ҡ^ZL֯1|gi)5B~?Om_c~PRkJ{mj3ZL֕ f7m=~?MAI*o{mj3ZT+kW߾3f7m5&W֯1|gioj MiPm_c~kW߾3ҡ^ZL֯1|gi)5B~?Om_c~PRkJ{mj3ZL֕ f7m=~?MAI*o{mj3ZT+kW߾3f7m5&W֯1|gioj MiPm_c~kW߾3ҡ^ZL֯1|gi)5B~?Om_c~PRkJ{mj3ZL֕ f7m=~?MAI*o{mj3ZT+kW߾3f7m5&W֯1|gioj MjF7ZL-l`Qa ִٗ. A!cХCS}W_dZ,7raQ1˭T\I#'Qξ!M[ONS)dJ ^Sa%}eg@DG1dތ6ۊih4dR@۽cVYWuuuJ gL1vm]c.2u!\~kQsW6"|iAeG<m~F btl\RԛրyٟK AoƭyٿMկ4ٟK <ϥfU7VljtN|ϥvgCPoi_i6oSx5k:'>vgCO;3o7ٿMկ47;3olj{MT?ZNϝ[hj 6oSx5k=oƭyD[hif}-57fU7VӢsf}-4>{MT?ZOi_i9>yٟK AoƭyٿMկ4ٟK <ϥfU7VljtN|ϥvgCPoi_i6oSx5k:'>vgCO;3o7ٿMկ47;3olj{MT?ZNϝ[hj 6oSx5k=oƭyD[hif}-57fU7VӢsf}-4>{MT?ZOi_i9>yٟK AoƭyٿMկ4ٟK <ϥfU7VljtN|ϥvgCPoi_i6oSx5k:'>vgCO;3o7ٿMկ47;3olj{MT?ZNϝ[hj 6oSx5k=oƭyD[hif}-57fU7VӢsf}-4>{MT?ZOi_i9>yٟK AoƭyٿMկ4ٟK <ϥfU7VljtN|ϥvgCPoi_i6oSx5k:'>vgCO;3o7ٿMկ47;3olj{MT?ZNϝ[hj 6oSx5k=oƭyD[hif}-57fU7VӢsf}-4>{MT?ZOi_i9>yٟK AoƭyٿMկ4ٟK <ϥfU7VljtN|ϥvgCPoi_i6oSx5k:'>vgCO;3o7ٿMկ47;3olj{MT?ZNϝ[hj 6oSx5k=oƭyD[hif}-57fU7VӢsf}-4>{MT?ZOi_i9>yٟK AoƭyٿMկ4ٟK <ϥfU7VljtN|ϥvgCPoi_i6oSx5k:'>vgCO;3o7ٿMկ47;3oGJeUOKӔ4@0 6oSx5kbqf!Mj2.LV[dPP;O L:eXٟv=s~"jaꐅ?ѧ{qyŸwu-k;RK$}Mx-7܆EL.JüZLj*Z|rIJw;> 7O}yT^!Ԥo'J:ߙLKIA?dƧOWtQ)J4)@)@)@)@)@)@)@)@.e[k8\]il-*=( N5X,"c0x^"]C8R A5Ou[\L$ᰦar+V\HS@R&ظ(\[XTeV+I@=,ߓûyBTl'G$[վݫ1'ڵlhGɖ:.C@Zҥxe$t oJը۾Ck6׌<IOVTGFO@O'~rѲ!o]\.#H4B4[ ^q6vAr܅8VKm֧[!I o[<Ȋ>&4Ap[eL[2maW$(i@$hZۥ{TheO½D lw侠M!.n7y)pJ+(q(8 P>!ϲƒ. sڜI攴!r-BT:G -]njC6lac0^} hȿ(FZ%Iq33!"$n$2@RgQ!!E6CmXl.ȺoMK9BIaҶI=S*4)X((((((((((((((1c¶Of"Q*$jJ4=T6M`mԈg "'(mA[߯#;ak]ôޛ$*Te8[UzXg0ˑBcQڟ Pg PAH AZ3{[4$c.]VbYͮ6ܫR *Z#*?Qw5.Nk[>*Vz+\Q9>lqZ. 5jRo@|mOa=l9;(Х)m:S .o.x{;MH$#%7(;shқȄu5ǂw)J)J)J)J)J)J ٥|V$č`e1.6ɓ.rL~AHYNql c^u\9 ,VEZ:FCƯ1xC[+)&ү6rBh]v e;y |q\?'c ~ei8CܗR IKԕ\$]{*瀰ͽr` wmQ Ag-73Z)J)@)@)@6IcN: %l{F}OQcnFٴf!|O^'-pzAd1/ 5sf5CMihtO[]@)@?_2S)~4y7Q_RRBA;>ToQӯv+-{CtGݩQӯv)e:aj{CtGݩJYG=G'Nd}ڞ:ajRQQӯv=G'Nd}ڔ{CtGݩQӯv)e:aj{CtGݩJYG=G'Nd}ڞ:ajRQQӯv=G'Nd}ڔ{CtGݩQӯv)e:aj{CtGݩJYG=G'Nd}ڞ:ajRQQӯv=G'Nd}ڔ{CtGݩQӯv)e:aj{CtGݩJYG=G'Nd}ڞ:ajRQQӯv=G'Nd}ڔ{CtGݩQӯv)e:aj{CtGݩJYG=G'Nd}ڞ:ajRQQӯv=G'Nd}ڔ{CtGݩQӯv)e:aj{CtGݩJYG=G'Nd}ڞ:ajRQQӯv=G'Nd}ڔ{CtGݩQӯv)e:aj{CtGݩJYG=G'Nd}ڞ:ajRQQӯv=G'Nd}ڔ{CtGݩQӯv)e:aj{CtGݩJYG=G'Nd}ڞ:ajRQQӯv=G'Nd}ڔ{CtGݩQӯv)e:aj{CtGݩJYG=G'Nd}ڞ:ajRQQӯv=G'Nd}ڔ{CtGݩQӯv)e:aj{CtGݩJYG=G'Nd}ڞ:ajRQQӯv=G'Nd}ڔ{CtGݩQӯv)e:aj{CtGݩJYG=G'Nd}ڞ:ajRQQӯv=G'Nd}ڔ{CtGݩQӯv)e:aj{CtGݩJYG=G'Nd}ڞ:ajRQQӯv=G'Nd}ڔ{CtGݫ +YNvGOc7rb/$F7}|ԥ,apvlv-0.7.0-alpha/ScreenShots/apvlv-0.4.0-annot-underline.png0000644000175100017510000040635414726051136024125 0ustar atzlinuxatzlinuxPNG  IHDR"q1Br;W-_k+VBWr:~7I{cF=RNɒqYwql~_J,~?QWx|_?W 2ϲltO8G>f_Ca%߁l`{2ߕRIx_iiߜFH9655"Hñ9m{n|bpRf.ߢ䄮5//6G2gS?AZ Tu;~ƃ/N0 60iNv;堥;Ę;[_x-GpS\.# LS.%9 b;}kOp(}!ݵr.y6Y^- g-@1Y*̸SFI|v;R(w]uK/[qC^j,cOlۼCqiyEB1&'-_f0ɢ6vӭm5 ALPVL'3PMQ8`Uj:[M o;Zd5h٬9rRci~Na} ZܦN glVk;U[J<51d9ܒRv^aS]я7mkY+_K[k񚖽GMr)]@ʢlz)#PNTB-MVs5JWjEa%uv]{3[9]:.gٍZle5N^58=4jºFďZV %hc r+:v2cMI|{r{e eZmUb0 &ft0i=ew0I<ʌ & rڲv2HH>?x_n\UK2mg_B#JW,(Bҩmus9Jk !s($3kgm}lFVv8nڅ?0QX#?B=C] ߼Kd/IyPgHbP$(-WڕnBre8j*@%O!]J}Ins}SPHPb:?SqsOsqG*z0JSu0=D]J{qX5쩹3gVvJV>Z##F 1|ZF K~( EFI`*twժhgn ٷ Uܔ:N*[a ld&ۇPYiDz9̧ѾtQ͵R2vFџ'-4 yyF,G;FbXsnoGMl-7wn3s5&4}iǹC-:CGRR ّ .LM-d \\[F VEc3 sE,GwHWg~(lAdmP <{'z VTf`hʝAN(E2f*e!Rr4 yW͋id\w7sQ F 2 36F.ZA$C7t3qqt]!zLf]Z56!T6$[x#*  ɡuhyX9dqeDc25Np;=FHD)miX bd;nQ Ex/NjTdĮ&=712!K[(&8v`ּ\H#% `P)Ue,%lIB֬0CoID ㆞)ÿ!%"I7z&\ɻ䀯źS u s)vR#N.k~:K IbQxXCBfE*2E „RG}iBr px}[`a#Z>*BM1pRB ++Pec;(h>3q qv` 2ܢ xk!܃ٹsBn_,T6:ŠOJ@ pMDd:TIevA4hEѝCC }ptph?ylDPB=(B1sv,=B;(F@qHB &(rnt%!B4{yh͂g9b>y".qk 2q򜛈H.FI (\]h]Ѡ*(A2͒BFm8u^O.Q$TdɒT0|P]0SؚB=R!B<`TuU'YJ&ɥP돒0Cu֣XO`'J2+z;H{$Da$2k4oY>X" fX#n⟂<^,.MLmЈ)X]@AmV=o:D'I@*4$ԃWhrE򿬘!Ы- '%ͽ"(;)Od U\@lq4NEU Pt.ɤ0i wk 0ws< xfro Iri|v(F9] )qA K$Lב~jJ$؛QXtbR#Ιֈ5+-쵰wkݢn YA>> sb HaTt/=/+zg8Q{3xbv DBF%61 BF PIM$7JiD+#]ym5>"lkn!owDY ~cD*tN> &j'΁!AfOy2% k!#6_\Ie@WМ Ce(PIEDeI8.Au#X:a/?;hyGmW3k١OH$"H(f A}EÙ Y+/ 0>]9lvD"IDz͈FIBʘK!U%@9x+pgR 6iw??*6}ݺ}XlJ0Wk`` Bs8 t>}\۾Hk/~\_iѼ]S4w֊4Ƞ6<ΨXd^qb5.w áYd d ~#0+99hi4Ѝ#+IL8a3a\3&Khn:AvU.:JAMIq ˻2_[^A@ C#+x xLZ>w:Ugg]c[7M@sBITO IDATxwXG9@HbAE `QLkL&SX0nb9DA4AEPd;89 UR)!.ڛdIP } Z[=:@ D-X0155ͺ"04jyu ':ʕ~rWv|>_@u 44$Ik\JI\{%TP(uK$uu5D&}i[ḨPU*E 5Ppdqr'weomb1B$+[B"DM9D zt||5KCCM׫3gxTĬbO?+;H$={CzVV֋zw7DB̛7!f\z>^!:ԺUpo.߉Yjjқ| G:;}r֭-1ccqR&'gޠTmi@{!ao*F Xd&BґP7Z:~f_-+@BUPhDi ꪟ02ЃbOo3'wyo9[%'toxV$m-&#$C rc62||Fmf^">N\WƯ)d17@O%}B|#䎑' ʿE| eHfW2)4mAhhll`|H&3gZ8>eii",O@eQDq+JT's/PajZo. Oؽ{OfZs:i"{q(ɌBսx*BtB">OEM(*} X}lۆuB>.~iۛN#K Ovf s|T~dz&fh & ݽRܶyC_CFsaKGDWBS?="S*M3ںt BB~o.:nırr,0HwHyx(67Ɨnt.>]W6#$V*`uQ_'l3Pߐkq`4|uvgӺx)z︸-̬ew4־.C@H=yk"׵s,7os|"eL;iHY"*SQ՗cutZYVmWe2Bt34/~3B]=rb/GhiS޾{Ż AH Sa^`դO;v$~?ZZ}Lej1;zocƐq !<4g(35N`aYvI=AZl~C]6t7}{•⊰Ҝw"EIF=y?/26_֥;.O :0NF(6f'^L9|JD%)Cq6$ iﷳ "k?LbXxY9*NS*~:tvC/Ht̞_ b0m*^;#w:ŀk\D`ʱslb3Nힾa9+|)B+?r̲y_oMLvמ zY|3nD;k὿|1%Ն/}Y̴k]{0MoTuU;qg|W A{=Wܖ ~kVU^O<2 iv?q\op‹%jK[$HPQUR"F(mǘ3>}nuq4Tm_* D155!Dd(T!&Jd~ޫbPbZ΀N?P),I^kHK?!OiԵ[%†RܪʒR UKSvUUfee-'i@ D*szz|GӾr~I$]S%ɯBkmٍtcmIݰ׭fjFz?v6Eg_2/E_d6y7Tvq ç_!D5ESsXh BZT`Jϭ5R’ti#ՏU,T1SU=6Ҵ,UY=?Gفx4'_kJUӆwNU1E55D}YG:*#Gh/i m=-uj(*<\l9.N0S\':*[0q1qpˠ#0z1(t\,yG4M#Ed1dͨ,*:p#1*Q#t> jj Z=SrTRɅ 9.KtP3=`Ό_c!FKBN~H +?VKІނԹv/ .T&`{u8N64t\(37ʼn@s ƜyJsWy]SN? @74ː}%:BuwK>(ͺpfm!uW;z"Zz>L-nBO <YJiR@f(YJiR@f(YJiR@f(YJiR@f(YJiR@f(YJiR@f(YJiR@f(YJiR@f(YJiR@f(YJiR@f(YJiR@f(YJiR@f(YJiR@f(YJiR@f(YJiR@f(YJiR@f(YJiR@f(YJiR@f(YJiR@f(YJiR@f(YJiR@f(Y@)$NPPԩSǏ?rȱcN6ȝrJ }}%UqAkkG))>,eN:%1ϟ#&yΝ[vp٥KvI3BGZZZvvubb"核ʄdhiiuUϟ<|DDUf.3iҤI&!(Jw]x1??[f|LKKKKK] ,DK655r8EM2nժU׮]C}nnnK.;qDSSӲeZZZn߾mkki&:JIIxP(lmm555]~%+.{gϞikk|̽{r~kVTTp_{[{իWM6~jjjUV5ΝkjjZ`yAѣ555bX]]}ժUVVVk׮555^4,,,11_}{iyh4]\\Ϟ=;a„ŋϛ7oƌ999GyUBB–-[q:::w֭Æ 8q3g6ny[XX G5bĈ={߿ f̘|SLyYKK 9_~ p8-RWW_vmppց}#FHNN޶m099yҤI_|Bnݲ 3gGjjoggWZZgggږ]pm!P(eee8III4` zm``fBÆ C%%%[oǎcƌ)++a e˖-WխYl rvvn 111СC8%By{{O8b p TUUBl6N:e|_~?Θ12dB(..n eРAT*Ç]tYH$ÇӸBh{aXqqq<_~d]]]%$Idd$B8p`y}G {{.^QQfddB411~#P(T*#Az*QSSScmm"T*U 455VuNss3m2TL*X'gȑf͊}s ݋?޽!?644# X,yŋQXܹsnܸQPP DEEkkk%IVVV߾}Gӿ[n9999+V_ƂyB == v˗/!8x_&$$p\.QTT2mڴ< >|tԩX<|GEEݽ{7**jժU666wݶm[eeeeeehh̙3L&e̙BOJJ }455X<|hРA+^[[!۬4''AWW7==Օd>y;;;''';99X,P~~~^^!(;;VO>355eX}AUVV#FP(gϞ566aaaiii4ܼBhkks\XLPlll={&H utt^~ɓD ?nnn5jCE&\YYaggfZDb``PQQAP455{ UTTrrr?>fmmmc!8j(uuW^0!4`6}𡶶3t1kkVUUU xFFF7Z}}ӧOph4Y eeeT*ƦeW^3~xyyy yyyx;,..NKK2336??QGGujkk'M?:$wa--- jjjÇh2xekkX,P(yyy!KKK!ӼyϞ=;k֬GFFjjj._}ϟq\/^\$P8V[ZZqCtttbb]d$ ׮];a„W޹s!T__~ֻwo//"\~ƍ{}@  ݵk׽{'~;uÆ Uаr\%Ivvիo޼u O?=mڴ@P```HHɓ>|/4`ZZZW\Aq8OOOCC 6Hx<mll422\.USNrxegg#ܹcw岲2P|.prrWpTϞ=윕=m۶[ $ww&X^zU,ٳx~~~ox`ƌQF?~ X|Us| ue@@~Oٳg?z Ҹ\.Blނ +p8***xbڴi!???\ 88x2L-r8???( XbEPP.6ҽ{S(5jԃԌ(..= QUU 2U9R]]_ hRkrd)5JP(QQQxBkǎѸa2 عsy󬬬>쳡ChhhflllxwN6mڍ7\]]/]ݻ7Bh߾}'==7;&&F$}smj/vth3Cuuuqqqllplrk'&&^~իW:̃~~iĉyx[7k֬UVriXnnnKKDD5hР"'ODEEE3g;vɓ'T*=߭I2oCEnЌ3tuuQ[S+ _`l2̙֮]{QѻxH$JII q޽<8}zo߾s8ɔ* i|>_MMBlt5uT_r|ڵNNNxTss\]]kkk_x!3$WᨪM'Ӧ4,??s!🕼3g%%%/_>}z'*}4{{9Pqt}^t… /^hbb2{lP{?t\]*!!?67Nu.gOEΜ9STTDH2d'Mj2lйs -[fcc#s#w\\njjjmm%M>144!4{T2ݻw+-xƴiVXQTT^꭭\t|Д)SSEhh!C^~u!Ѐ8P]]moo/2QT|}ƌ[QQ`0̙n:777Pccsׯ_K$/_fgg4'>>OK$<(##l6#ɉ)S{}\pb2̘~!"## +Y]hhh<-nD\z@ C!MMM@ KJJ0KKKSSSrh$w(AiiiѪUvajjR۷o]In7oɫf'^|^L\,!"dX/Q$ һ-[ٳ"E#@2X0o4BsTTڽoijj9FJJJbccϜ9{~_K,Yr'^ȑ# , q_}TUU r:C===&?G' ,,|||ڬT[[ݻO>-((x!C<fbbf555O8ommmhhbp\/^lnn nnnVVVnnnVUU֭+..>xGtJIIIyyyQQB722r…!&SRR"Hh4ZzzzCCøq~ffeeeee577N0d~7"̀ :zv>|Ypp0~q\\\VV|gk׶m`0$ JWtttBBBtuuq˗3666 "(22rĉO...VSS8p J#|>?22RWW733<>>~ĈFFF***+--?~|>}d0===ww_~W^" lveee}}ٳg,Y{0BVVVW^ݼy_@ ꫯlSN뗕pݻwՍGQ4eww+W444~:''Mz?RGGΝ;˗/ի(7\О3=+@aݤM1,Ybkkqz[օXr6{RD"r6f̘'OQ(*++S:ހzX,]|!>_UUշoJECɫI&jjji4|h4ׯ !Yd@"VD7X,oTSS.u֌3Kv0 F>t6-=Fvf$^SSF:x466*>\eD"illҤWJ BuŐ?jjjtuu\nkkN{Z/ ܹ#vZś;\.J޳@aG;V?-nfJvy\/?YNIwD"H$jg~^q}Wb2Qo 6  ʓdzc42g] R"], {h{b UFd{c捇L{TLE.q( ŏα6nO?ƒ7W{jwoC] 0c.cп7>z}:wƅ ^zm%''2XXXDEEaS?YJiR@f(Y년 2ߛVTT'̟?Zddd޽Ņ|P{CCW_}5~xRPPе%;*.._f رc---FH@rpphIdjj~z# iӦ/^GKKk߾}'Oϻaaaa]"Ž|[2O3fԩSKߏV#tY@)d^ݍA]XX[裏Fi&r̻qٳgL_ےY}P;=,55(WׂTYYagg'=S(>~yԨQ/_lmm RҒ`۷T6=b R__>x疖盙X G 6|PLJMM322D!!!aaaiii4ֶ_~D"ܹ3((H,oܸs޽= p<== 7l |p{"ZZZgl777|a !t##^zyxx\pquuuL&`̚5k݉cǎe0+VطotpiӦ 6PT333kkÇO:xl6{֭)))ƍ5jÇ̒lRCa٣Gf0...oA e֭[dŋ׬Ycffsz##?X"[MMmܹv풹.%lܸqxUKKU-%cccB&Lptt)PSS#H+ҡ:̙3ͻyA|}} cǎ7&%%=zh!ww;vb##1c8;;6ɓ}qtt$/.2L+++{{{ߡ$_zo=fqɷĈDUUUb3f$$$TWW7447hjjb2}R)))t_}666mCFP(x9- kR8~+WbbbGXAn%HPP]]]\\|r<\(J[Bܹsy󬬬>쳡C[bEPP{r+Ŭby[[[ST N~ӦMqFCCKBjx`l g3t… /^hbb2{l\LGG,6uTL&Sz|P(l2'H))Kj$J(W@Ç.]m6\EEE  B?+W׮]񊤥۷AaaannSlrIoo߾啙I~Չ.\˟/c&)ݝ' B>>>{ܸqQ wU \]cʔ)>>>xD" ˖-;{H$CgϞJ.~]AxH$½]xooo<?>}xVYF|"Q0N;`}TUU <лwo77ÇFGGu! CCC544\#G6lpĉgϞYXX<_-,,8p`BBŋ $ߪ'O,ի?!4s̡C9'==}֭aaa666xqƎrssb݋l^vm۶m '7լ7oZj2u=|ɓ***+--?~,B0::͍Nbbb?(K*,,}dׯ_QQٳ3 DH<`j@G𚻻 =INSBY NRQQfh_sn_=-[رc,2l0lw$~N,rJ;;իWwb .|۷o ,..DXHHݻ80iҤScvvK'Olhhظp!C ÃsrrΝd2 Xx1sݺuBxzzRԆO>|XZov Є Bk֬~[UUիWW\|oڴ!$WZŋ_z5""bر^^^{}Ɍ3dTKiWKOO|rӬ0uuuf ۷oϙ3Gm1W)ƍuuu:::o8+b1FϞ=۹8Spp0ާ?ϊ{? m۶~֭[j#GW%t HBssYbqLL褤W^IϯGׯ^JKKïCDqqqEEEMMMRA<||3۷oMMMӧO Bb866ŋ ʬL3=zOII! (66611QLjkkcbb"##q˓;_b-S@^7o^@@˗/՝>}ĉxr8uuu!!!d*999䋺I\.oV IDAT%:::**!^|Y[[]__reeennnvv6~ y~~~NN$ ~❅DW\ KKK$充={LfD"QbbblllKKK\\BB.2򜜜[n˴<""J~t|$IRRN Abbbdd$B>>>>33,@SS.X[[kkk_rE F-89RAusνvwYYY#Am37N4 t2ccc:-=$(:::11x׮]ҋ_~!h``\.i&GG &9sw1 >7n 0 33>_]|?44!$BCCwu=''ݻw?~<22nӦMdqFOOϽ{2I <<|Ν!>ҥCk֬9rAjjjڛ)S]MMǎal---rb|_d2Ν@ت-Zf͚SN͚5 !p5\ܾ}|rr۷?\̐OO;v(nUaa-[F_TWWϜ9ի|>ӟ}oll\tiFFUx_lGݼys߾}---aaapÆ @"CᣎL_PP0y}IH$٫WyfTTԉ'\/Ϋcll<|p???2>{l``sVVVFFJ+&SRRB $$KJJڽ{LB)M$޽[.8p`ZZt SRRnݺ233u֭ӧ+ۻwo򴵵̙s9]A6%tY>>>׮]#$$$H$>}J/bff6n8A__ ӦMuְamիAAA.\=Jӥoۤ BX |6-YҥK#;;{РAw%-H@KMM=s̱cǞY [[[å P(uuukkk*JPbbbD"ޛ8Hbb_zj_)^) +L3f̟?ҥ'NզJ|+"fnn~z)iaaa81U jm`XonjjҒ Lg0PdggTiY~~̙3ǒ˗/KMMMqqqoӞ4;; &TVV>}t2/nrJ ͛7?(Kk1 yxxSNVq3Q uuuׯ_?pfWWVV/:t(&&fvvv}{VP@Z``E݋?644&&&1Bdmm裏|}}eH$JII 9*-L T@ B!pcllܧOD*pK.988kf_V+Wܳg)N,LBP[ B!d=BH:dD577{zz8pյŋx(Ώj455XKG477:uJ+k(ZYY# &?J;w{︨{eRw `DQ`j,XbI3j1j jlA""( ,KYdae}_sRĠɼ_1;;s3sgg;w.+ccc:H$J,Q---www h6 1'B,Yr]\lkk300XnدO jjjQSSsi yaOtttDFF}< C$Լ~؄X& p I ={Wd[n]|91:dds^JII1bĜ9s6l@pkzq5pBBBfܸqg'4ίyf%%oߊD"N$)88 ?.]tǎqqqxELdҤI ѣGpzI YutUUUrrr/_pWXd fH$JJJz\Yf3Lo ^nbj oqB֭[[|Y䎎555_/bL98CII g>tpW\ 7Cݻ,'d44F,^7n}>&()?bΝ'NPWWzzzΝ;QVVWSSSQQruttߗ*'''==K.)((rJuuuX/ѣJJJT*^$䢢v[[=++KGGGQQɓyyyl6ԩSnnnJJJ߾GSCGG'))IWWΝ;666111;w<S*++$3Śkoo!koo/++[d |'&&)S466J3g*q ΫW"##;FRPP ۾};>sLUUѣG/^0337rݻw*w500{׬YqFy<ޣGGٳf{{/&$$‰'EDDŠ0813jԨR(젠 ]]]@UU%) FdǕx< [gBH$R}}}cc#F2e ~wm2rHKKSQQ155UWW/)))))LOO>}۷---'OlaaqMuuO899E\(r?CEEEWW_'H1@RϜ9 @$''yyy>;we`[nUUUa+~~~xNDDիWAvv<NNNlj'fFl2ɠq&MsNWUU1bqVRDo A{6Rv>"zCYEuUWՉaałnV8֟]5nHA2jsshnnVVVg#Gq1 ߻%3#4 ===|>_UU[XFg,KUUuH"ZD!?ֆo&Dbbbmmmۜ$YѡJ%u0gXM,KMML&͂a *//.9Q<OWWAKKk}o۶ĉCWxxxH]enllljCӫiߤ9*|LP,ć |ID"῕) B14,a3  ڛxaJţO<)):+DCHj~ZU Ɇ.?s C~z --500PIIIQ>XgIϕJV E,TH$XLRA\? qԀ mll,tК f 45T߿_v.' Ԣ$M> 5 EyMC@|8͇ _ r@ B x/ 7 b!?nݺ5\M BɸtuuI0AOOO5(Ŀ14ܺu+55O> vqqa2aaasݱcǥK`7n+x  Λ7/"""66?kll 7͛| źrJll1caL{X"..Ç***&&&VVV---/_x"ԜJĈss熆&%%ƾz*(( ꖤ۷.ñIKK9rduuukkkTT ^QQ1m4ӱcbbb֭[giidll\WWWRR2c >_RRbkkk.v;;;\\\ʗ/_>tеk׈]۷]]]7w}cdTWWܹS^^~֭&L8sLHHHoOݻ7((h۶mR ;w%%%x)@Gd HuV++O!ðتCKSSÇh Ļff6'7+=7+A!f8pOGGG L>ҥKxΝ;7l؀|왏Nlqxٙ($::[& 'N<{,L_p!;;`'N+V^ωi짟~jmm0,44cORƍaY__7o`i޽0\.xDnaXkk/?k֬ϟYk׮bTTT|13<<N__ߺA$ƍ7,?㶕wuusuvvؽ{֮]r/|p UvΞ7o޻G{!lqfECĐ#Mś&_fMdd$@OO"<1ĉWXch4~n%&hggiÅEDD8::&7`n6, Oz'6;w/hjj0,&&҂GTXYYy<{{{Ǐ|x0pt%bfOO,Ǐa4ȇYFtttz /IyyuTjiiUUU3fA3V3Pqwwt!Ǐ>xGYbō7x<XWWgffg ܹsyƍE\ YxH_nn.-ѿ'YYYx?~<~^o``Y6lpʕ+Ve455%ŕ!Jl*ZPPٳ b,+555==W ///55NKojjzIyy9l"99y&)) zf͚(\1ctyyyJJJ[[H$NMM0,+++//jUXXH>db̙32544􆆆Bh椤$8B,ŋNJJJkk+()) 惾z j K-@% +Ǐ ֡iR!#˗6ٳ<.ѣ*P(LOOπ{1Fkkk{~2 "fg~ӧTjaaawwwIIIii7oT*A,?ބ ݻ?YK[[[WW)yxYNNݿ!>Ǎכd+OheggӬd7jddD<7ɓ999i?ccp*f,Y;8^믽׬Y8r䈆_uu))ĉ111_~]NNN__ʔ)bӧ+((> ___##s\CCCpp񺶶6$$d پ}{GGǴiž?fD"99Kvx<cðFEEM6`֭pD(XL&={JoܸΓ.Zpdk׮ݾ}{̙YYYN4$_?݈Z\\f8~ϟ?Dww9sѣG;w[gg]vUVV:99m۶-))IA$L===\.J///^8ڵkTF Yna\P|2GvAln]lٝ;wKdo߾;iOA=WMMm֬YOf0H]CC{̙Ru/֬YYXXO>Naaa1yd򲷷WTTtrrrR' rrr+W{0=777I&.\r]\\Fg]pOMM-88wшdffD"cc+V(((x{{{yymfii 3gIZZZii'|aGݜ9ssb5j|>Ν[n7nϟrwwkjj򲲲LOSUSX@{{]mF"֯_IIJӈ#TTTV^*١)10|x̢KJJ`μy󢢢O9mڴdvXXڵk~W "igpssVUU7oڵkᤗ;1?2$>3k֬/^s0&Dmpttt|||bcc9Z_Μ9sFFF:t(00?hސ\---|>޽ .ܶmsH$ҨQ`ZIIa͚5͏=:u PÅ'$$ 6ܸq`ѢEqs $'OA#ZZZJMM}ӧ/^hkk+{B '??Ĉ>+++@ Ej@=ga#FHKKf"  =zvC{!R࿗۰ ^z՛AY~ 6?~'D 7 1dD"˖-rJ{{f$?{ NbKoC\7$n qǏ@OOQQQS˫'H_uqر^^^ $ħpsss|zRQQf?}h+*`ҥo߾&ȑ#fϞ}҂ " 444l?wHaᔧ.րzb4ߜ={` s IDAT&ANNB| ',QF?{㩪zlvƁƍ6rTD d,PrJb{Sfk֬9<^W‹=L9| kZRGnVOR?wي1LPSSH$rijj[n\:Bd^xqFF̄;>z>Қ5kΝ;![L[[[QQQlkk300Xn&EPp4@UUuܹ7n$\xPPPAA> 1'MII?N>[FoQ'''@o}YZZ7*mmmT*:곰X|।a%G5D Ư=###88ep}zd?GBo-^ՅԺRL,# M6}ACHCc>]}cBQn>8&Mt:7bt̙GֺXXXzj|>ٳjjjƍz*|,;; ħ9ߟbcPH\\\TTԫW---ÇbccLfgg}pO3ĄΞ={ȑ7o޼~zYY{bb rrr]W_Y[[C?~~~bx;w^|roW_ϙ3.P&'' ɼtҝ;wt:!Fdd͛7Y, 4h񼲲ʕ+|>?--Wr7[VVVxxxUUSzzztttKKcll͛7\9EKK_~ԩSx oߎbnnnOkkk{Ǐ+**5ϟ?9r+Q'O***\nyy;Dollhh33 ]xcƌqvv666VRRȰѹ{ljozJr̙FwǏggg+++;::J5]yyǕx<_|=1cfBB^^^^UUUEEgLL­uم}رSN=qℲ2\=z4ѰSL4oDDP(HJJ #ɿ Cӕ$'ѣ ǎQTTuTii1c?~\]]aXSSӅ N<G{jj*n1;B$&&)SY1KK[n@n& 733n8Y4:^UUNr¶mN81Z ÑB|>L&3 }}> ah8d?Tcc:qaCUU|BwIYOOo'b-x<^KKIssڀz½hmm$ + |qcٻwƍ;::'ig"===|>_y<@ߥn>&Eyh1O4}XwɃNmd|5 ){Cc2سY]]TUU֖4س_QQ'===x ,[mmm1 7lo1b[${#L)I;QPP>Azzi!4ipkUB rZ4D|||@ >@ ^@n@ {YadJ榧(xI'B! _ ~ G D"bQ 3< (ř Q+@Пذ rLLLCWW vww/\IFkV;ׯڵ *- X> K~z>?jԨUVbbbƏoooœoo7o0̈-- #zoݺuǏ?|]~[V $E~w3~8z 4܊|:u*)))""B0 P( a`ݻw˖-C8bd?A,0矛 .B}gȑvvv166޼y3qܸq>ub>|811ҥK^^^/YdԩSW^ sfϞm۶'Omܸr…͓ԩS/\}]vuww[n֭ 駟Izw3~8?C qԩkӧODĊ 655ݸqj'?-v#YA&';;'e8ď )++;rHxx81O?;v,D"ͫH )Y@\XI777xN3~8 z!x<ȥ!vx>y6P A +3?ɬ(..CH$RYYٳaXeee ҈ugΜY\\1c|'۷o_<::Jyfʔ)G:t?~m޼ŋϗ{MNNa螞&/^,dgg'$$apd?ѣӧO+((_X+9c⢩)aÆwÌ3>XVV&iF@uuuYYagĉFFF_H${{:xZˊ+"##}}}nj#f޳gϒH$eeUV(++3vʴ())KKKCBBF}9cYVVK1_iҨT1cf̘怀 ˚W{xxPTl2:^TT޾|r8BSSSLR LMMMrssy>/>>>'NxÇw}wlNnVznV:!/˗/0lɒ%/^0L$mݺuʕ ðӧgggcp̙3 FS?b< 0>s zzz\]]=\9TJ e677ܽ{ðO>$%%ð۷o[0 ;s/3 fΜ ڹsgXXa/_NKKMر0@```@p[[ɓY,aaaa]"K,Y|l ܹrm(-[@ 2/_dmm ;88yðG:tH Hmx'466ɔZyPoǏaXWWiEE=zt…b6FӃa\|۷ocbb݋aX{{/ y0x9a_3gl2PaXppp[[[uuy`΂ %Mp /..޾}duqvvG0apȑ(1!6l0k„ X/N%%%&&&0-G'&ECyGG`ҤI djji``/));vf:99ihhHJ+ R7oy h ZZZ p|||'J __Hj_p"""7m$ǎ?fF ^xq֭yINFN>08zh@gg;dy5557oxƑZófRTTL6իMMM QRRchh(6,12~gϞ?rofɒ%uuussL1fffl6P__gLMM&***rrr&&&d2YCC#..NNNN!V߿>_tuu 嫯ڵk?&//ػgϞk¥1;;; 6sLpڵެjEEE}}} ;;cǎɨ#䮃۷okhh[paDDX999x+A~b---%%%&S]@ B Wă1d2x%qۻ)zb]I9K- K&uttX8000333***''xFFF qdժUp%kYYQ(|0SAAkxDI$RdddDDʕ+uF"-qF[j&ռW+Y&''(.**2S(u ϟTUU3痕UWWΘ1zK2K%ANNLKu 5agge˖ݻwkkk}|H`&//ԩd>GqL߿_UUb455N*Àٟx A8$ QVV&n9k֬ÇǷžH,yO? VHvad̙+Vp\1\ϟO}F#T[[F<@2Nŋ߾}?R(ٳgϝ;[o 904WBB3f̥K$,Yڵkp[AA6Jy}||bccT Yؘ5, #>G{;6͛ٳ*8'M.]*9') Ąx񢮮.==rpMMMI$w}WYYe˖ӧ+((Kzzz'% 55,,,( Tcƌ .+O?޽{Փz9RÇQ@Anbh366Oӆ>s/i&-iUF)UHo_tԹ4kkkH.^x شi_-5X/2&&LLLKzzz, @356nذak./_%l%ZhQAAtuuC/=sRkN jx_TVVŋw HNN---P/^l۶ƍdZZZeeݻs:::4nb A{{;p)++xh1!d2.OÞzx386s# ƈ }í0*|@WTT0 1cƌd;;;&ڪYQQlllbbb㶶'&bu[ZZ233 ƨQ4jԨgr8 &<} F+Wttt.bx<*,,8q"\044LOOYYYxaGGGkkׯh@ŋѣG8pرܹsO*///...//WTTkjj._pzzzoݺ.//f߿ڪngg7uԨƔoi[>Ŋ5$6mk::22Ҳ.++`ݺuq%''wwwl??؀S֪ꠠ Qz`hh(iHԤfll,ىƍ{AUUU@@N3T[[dd$ϟ;w.?ɓ'\.fwqqqŬRRR]\\miid2X,֌3dNNN2 r=A{6íDŽp"dl\0mcV &M%v Ԣj_l/cM}tP(KK˴4&iddb/_pzzzoݺ.//f߿ڪnccd2Ν(VMAAzܸq< P(O-[Grqqq;wOɍ7.8~AhBٳgnnn/^^paSSSر Q!gDKK3wB`OO~ղeˮ\29@putyyM;F.]l= ޽{l2P5JKK7l0fGG ޽{ܸqzzz_~eoUt[!$B ?4Ν;ﵡ駲,^xH{{{Ϝ9S2B۫G&&&2Z Z'|"9jԨ5k NSNN0\mooxbLyy]vI455ݸq 7l`hhݻwkii)))Ve@OF ?џ~rTTԠAMML&YLNnh~~HC&78!Ր3Z(tWر 0vtoJS٭:rH###ޞ={z2 @nb;o~>;wP(ܷonkkD7o>r.g˖-| ]YYq!bCYYY6eee%U .|++Sh\gcc|]! W\I&/]XlQH$JLLjjj'Oܾ}`T*544͛7SLt'OFa``s Fdd޺ujjj^jnnuAfʼnD1cƴ655Y&))IQQYf5d2666'O| mmm˖-{msss@@.^?ZaFT`ɿ@ Xbś7otz``ȑ#n޼igggddTTT|reee. xs zzz\]]=\yww7a˖-]p ̙3a:&&;;;ð9rO0N OOϦ˗/v!aK,yaBp֭cXs 111{0*ӧV h4ee唔 t1uԒm۶~Z 0 [vӧO1 c2qqqbWH0 {Upp0ammmGƻ $$XK s=pBaJU Nܾ}{RRak׮0,$$~u圜1;HvH$ںuʕ+!?av̙_~EF@SG ZjΝ0,##cْ]gϞk׮aqTWWc /"ԡ߀;`ڪ 3333-[-F$evM^3dDTvlll$#uAvv6n%"3&//^utt@ HMM6mˑq]RGN|f|"(222""lʕ (Dj̨bJ֢R""//[siӦA/*77 =:pq7ng2dJjBj*8@B 'ƍKOOsfϞma؛7oBCCVڿ)S}99+VܼyuժU2ڭܹ?ĈXjլY֬Y3 baag_x7O2e}^,&2022jjjܻfk֬WPP?ğ?O?Os  ڭUUU p13VVVcƌ9 _$Xf!]P__2 M.񉍍%Hb Zgii)FDj'Eb ABB@ صkץKHvh9sϜ9r{ghh܌a ??Ν;K.2eJ zh0QFp}@2#ϱ@4N/_NYboο|w_pA#jժ{=|ƩS`RZ[@?~޽{۷owO/YZ$xlƬ,ooo(Yܘ/afqq1>gE@~~y jii9a4//X[[? خ]pO˗o޼3l*pBڵkзEA ͛PTuuuAQVV6|iӦgA&D"Qee%\z] N;vGvv6?a)yyKBwhM|BC]]P(h Cj-CC9sua9T*v2 -Z0aXOOm۶7niii0:1LdB90Ԅ16BHCc>]}cBQn>n`ý{*++cǎMMMuuuUTT?ΫW9D& ZjjjNvNN[PP FeeenneˈVdd$Fuvv~MfffSSSppq\\N BiiiYf ܂#FccYrss^~}Ayy۷o~d:99A`XZZt)STUUUWW755uvv BCCK.q8UUU˗/s8EE[n)((3&===%%%55ʕ+.\`2,bĉ* ܼ`03f$''=y۷|>U^^^\\\^^H|ﮮeeeqqqׯwss#DGGfddd$ 3gμ~zgggii)ɱ׆,ϯ<11СCЧ,ZH144LOOp vEEEfffssQDOOyݾ}`644444TTT^èQ$;P>pScll,${: ݻ555ӧzzzƍ|ѣG᪥ӧy<^KK1q㪪^y_A$Iָ$\Z[[z{{{xx֪oSɼymׯ_oiihe8@a61HucuUPVQbh4{{{6=܊͛7?o?~„ czz{=:ZI" o}o>-,,oG6:Z bl ]VcZ u֜\4```PTT,//fk$'OYYYb?@>1bFwA988 J#45xl; !}߿P@YZܬ,@@nր@@  f!@@ ^@QW^'í 7h"f!/UUUw4iүgխ_oO<]]]=1ܼy> [bX>խ~ذa] 97nxGH41B,=,jooꫯv%~GAncaaq]Oo#cgx; ,hobȠqqquuuf211xH$_f |HHޙ5utbDD(kDՊEۺkк J7pXD((B00e#^ɽ{3s_~ƍ(؃.]Nm޼955`yyy{쩬|ɓutt|իW-Z$=PSSsܹC#Gwww3 +++>onnYZZc###>|͛7褥|:;; BOOO^a^^ތ3 ̙3tuux񢅅t>`0Νmۘ޽011 ]v'+W|;\\\֬YC a E;w:1))iӦM&&?L{Pܞ˗˳䞞#G >|˖-aaal6?ϟ?P 66VGG2g%gϞQ]]~QUU-++GewenL:+ $==*MM͙3g*; O,-?JJhoA^^^BP !ؽ{"]]]zzz߇8q8% PgϞC߿_J^WW!  /Ɂ9;;WUUA aaaFn  !AAAVB'Lb ΝCklltqq!Ę8qCP‚C/_Ld2!'q}v%e*_ݻwkG~WBGI2;vlƍ9sw8>E7FEEtooo`` J'%%eggK@i''El6zyyIT"tttL7Ç`xxt +7o6̙3\.W<{066&0a2+VA97qAE"&㙛C9W0222::Ϟ=̵O{kll@Kl! e2UVF.278ؕL%( mmm2 JǓŋtB .(.>g&SL!NQ(uuuVUUrss544 >kjj$LHH044400}CKi4F 0@ZfggfPf<^^^&LD---555D>FFF۷ϙ32dJCYFKK~I[r]]ݕ+W&O 044ܵkL&  ݻg̘ӦM Jŋx<ӝ;wcbb>3Ta(y+scJTKٕ@J_|ELLLkkkppBua0/v0#p8} rJkk+%i( JH$b1DJ,//722BiSSӡC*L*y*CRcbbN8ݭ">(Iljjz֭˗O2E lUTTvd2544Ϟ=;zxDmAAAnLHH駟dhiiyQiIc.ruttuVyzOIIinn~@ ,$P`ioo_~}NNFC䊊 uǏ\XS蝖}˥RH;d2966TlJܘJڕ(\]]'¼^UUED+Y|Âz:.O)ɢE\]],Y<$%/aÈ+(ggg"w466VWWFpI&رC ٓ2{쯾Jqf͗(ܹv_~) !D"]ٹJvVyzWSS TTXrdKZvb-ijjBnMNԅTUU Bܰ} D i IDATh4{{:°ew]Iʊ166v/^Ȉ~]a0¼|>p8@oiiQP088ȑ#36FlkkCG9JKԆ>|Ғ`bb_7Mgg76n܈|TJ"MTgf T[[vعs'1؈D:r:hgg'B}Ds\@PRR"lr sB;`׮]Ȑ!j899UTT(JooCԔ3%pttDS999d2AQhhӧk׮ @XhQxxĉe$,V3ic(Mب^--- Җi&b7g"ήBL]+ o>;ަO. T:::6l744ȼ8>wenLq+i4aÆ͛/2p4~i-i+}AՔHE \zUMM/TSSJOO'HAAA2K_paٲeHtȑ.Nytwwkhh={h…DX6uTt011gРAeeeW\Fb2B<%%oD!G hOYY&ӦMӻqㆅ%BIIIo?N"y<~РAhPOOt:}ɭO<100022*""Ͼ=~9//ڵkB:flٳ/_,Q?xj==TTT\|Y(6lԩ"?0`;w @"$v%###KKĖKIAʂN2ETΚ5K! b\GBDÇ(m5_zMf}Z/^ht.]믿njhh0220a©SF'ȳ䖖 駟" :tΝ;mll֮]+ qe룋38b@̽& /]DTUU%Y$ICC'>>Ϗ8%}ʔ)}ޘ>>>+ x GRY,.#-ZBV.vv02/^,Q@@+WޡH֦\\\,3555-))iiiy0f\y;ѬG ۺu뻖`f˖-aaaZTWW?{LQpP\VV3j({{Ǐ{xx(.{զ&VVVC^zzzbbbTԩSZ>m 6?UUUP]]jdd"|rJOIl!TSSG HKK׬Y#N;ӛ6m'nz2`0Y7B~~ӫLUeeyrrrDDĂ BaJJʯ*{Isss3bĈc߿?77wĈ֭|ɓΝSPPں|r@ZZg͚EZZZJJJV\b ׭[:ڵkuttP?02H 20ϟ?߲e !!K(vttHDf$9wСCdȑ#lv]]]AA+tuuKKKwaddܜ~z~iii}ٷ~{֭Ȑ3f>yDOOJSSs̙|>`888ϝ;kjjΝK%%%eڴi9T%Kwb"##BBB DFFƃz{{UTT===ǎ{~kڴid ֭[WSSiccƢ ZZZ̙#`dddsssdd$N0aBfffii~ee%?~yd`̘1$iٲeqqq ,pss[z5J=zʼoFA#y޽EE,k…&&&V O*++y}Z[[_J3Z+W BaBBBp8SNUUU͞=ML&x֬Y ʊ盛{zz*0YZ~ކK[[_~ٽ{C^^˫B( }||Z[[!˗/ꫯD"ў={z{{k۶m[tt4P$7 Bcǎ~Bx?0aB]]pŊqqq֭[B!0((hժUDfnnnΝCШ(666V uVBDaZZܹsQ%KJWzDE#6gg窪*aTTڵk }۷D"a[[Ç!l6BrbӦM6l(++8e``o+( K w>lٲ'Ntvv*ݻAAAHt gϞ=t萼{X\\ҋ/F11adICk&88xڵ{A?/^H< B.\h43332zj=zŅ١ׯ///߻waÈk֬ FCFfffr1*!!!SF(謸0h!Hdd;x...wܑ0!!ߣ+lkkե!_MMMM i Ϳ⋘¢"mmmG&S_|yLLO?}v+++ VVV2C9 h>:|'|~(Yj3f@iӦYdSNEi++6Mtdܹ;w†*BcLW"l߾]2dT#JXXX_vM^+S\\L"Nǻ5{{{dzzzƨ?|@ Xxqbb7 Y7'p8ĻpPxI#!k֬AC$iD#ׯ_CфB!qX4669 B!z]YYHbccϟ? F]]![CC̪h4ښ5kΝ;wԩӧ{yygظqx ų=\$9999VjժUVٵӧO;vLANMLL>}!C5qpp@0|*'6lQxٳgN6k_~%@0lذ24۫X*q>ʀܻwرcnuvvPzooUn}_~y3gŝ*D0iҤI&)lUUٚ'Ovtt|V qc8R #S8NSS\oqHq*2wiӦyxxx{{/]Q[[=[ZZd|>#FkhG(߿H!SC?'$$ӧOQ) &OkiiIHHDW\.W MM)lB"`ذaFFFO@%c$ K.|2RɓK.@G˘B>aHX.$e˖?@`=Cnľ}9N;vL<96l~:͛hVڰLP`5ܹM=ɓ'|V/\P^^d2 JWQQ0aBssFL>c233P`0 j. lAAD"ܬu1%K޽[prrx&&&&&&_5lqƍ`C#ߵ<TMZߙ><''m󣣣e>ohVY℅mݺ]K`0-[k)hK' _ fa0 Y/4`0 yS`7 `0 捀, `07v07HUUUZZ //`@L?n+66]KJgooTVVO??~?#g0sΥP(eeezj\\܌33u`@fffii~eey444 ƙ3g<wE 'OUUUijjΜ9S\Ψϯ]VGGf͛7  ޽K"222̙f1z{{9baaQRRbff񼽽 vIsss3bĈ+,/^puu8xرtOOϥKButt V&?ׯGq|}}8(۷o@b.\hbbfuuu+V@nDCCӧlll>|o߾Vqh:---"">ooݺ2c y> H$KX;Ŋ&H˖-+..^`իTmݛgϞ{ )Moo=_t[&22fʔ)ťՔ`P믿[lIMM}(0(8}txxx}}}aaQ۷o1W򳜬6_V\pB BiWWד'OrܳgBl6zyyALLL ===;v ! FP;##JJJ "Bvai9Ν;ҡQQQsΡ]\\cǎmܸB>gΜ^mۢ!"hܸqMMMJ^7фJ3%3ggg<BvZ>}Gٸq#hH/ٳg:$_Z# c謇˅N۽!BǧB$'!!?( ! %d{.p۶m'N@*6Q o߾}v^d }%@E"Ʉr`,ZBDQU<>{CAB2Y ޽KS˿+Hg$8gB:%SSǎ[YY'N$l‚C/_j*tdY;~X9YxڠRĀ@ IDATRRRUUF̝;H[[N}}O?ڵkT }ߏXYYl/immE#a A.{yyM0A$ԠS\\)?z ǫ^ծaXXظq^vهK8p)ͭ_A޽{ƌ(=mڴp ٷoٝ;w6q;x"NWSSP(:~%RT*`aaaooL+88ֶKCC#??eVLhB9%&gll LksP(lhhR---tTUU jUY*dիŇg) q=eqxb888Q}}=`p\-,D"WTT+vѢE'N477Kp8/3---W OHHpuuMNNV,iEEJĜ8q[EEݺu3!!᧟~z!͚5m HMM2?znDVAUU---.%MZ#FFF謗a'4LMM'u***vb2ȍ̞=ʕ+]]] ,MT'.2-y8%q2ᑜwئ&}}}"BAKi칻[QQPL䝕VB=K &J~~۫555 ˬVR0~,xvww :$^Kuuu066F;33vh` rW{ !$2Ο?ŋ",**rqq!H-ruu]d@ X={̞=tuuu$СCe~')S/ʗz IC !.~eex~i8;;{DAQBB㕕ÇW^B7qQQш#D"իhll0`{;3QqRHf6>ذa7o BNNLFoX700͹qТW4hPmm vU^^~ 4uH|q׷>dCϟ/k͛7e!"/+'''4NIP;r:uرKݻ=bcc>9NSSS``tɓ&444;;;%%%h,… L&(4QZ[[l6&YUQQ1a„fJ&O3@mm-Bȳ)S$''2L&uuuuvvՅlUEWO=޾@ :(PQ\fg{zzzzz!C8,qkuc IܹNNNnll FI(j2y|Hk/RSSjkk mmmgV ƦBߚSUUUT|Ȑ!666v>YYYh wBBBIIw}3&77Z_____???N[[[744'$$`L:ӧ999suwwfggx<hhhhkk777߻w/../{.vssCKFd2ϗRRRZZZQm *,,trrB=uuu#>;wܔ)S$>,%%ᨫ䘘444H,4)+W  SSSS't:ŋ7J766'O&N(=b`` +++:XVVfnn.a'd29 %%b˖-$z{{kjj4ZCPZZ&%={V__}v2{݆aÆ999xq=|PWWWMM~knnO(=///5jԓ'Ohff&Ӵ$i̙x666i***hkCW@pq<… F믿F-Ϣ&aqqqzzz"JWW<++$"i5|T[[+="%qV$EFF;ɓ'044gƍ{qUUɓI$Ύ;e…&MJJJ8p`mm7KKK}dB8y>)@iX,---o~[uåC8F\|xNNN^^GGGiN|W+,f<%NXX֭[ߵ ٲeKXXػ}f\ffa0 Y/4`0 yS`7 `0 捀, `07{̿'qB^Htڵf"GqqqBBw}'&EIZZZΞ=t7![ 33QRRikk+/MAAANNȑx,aG0r >|8{lkkkcc㦦 *&PL&[[[\68pmۈ_M6v֧O~T"]߼yS^:"<[[[[n]x;y`7 /MWWwСvvvt:= SSSsm&H|I?Ɓzzz6mtܹw-;Tb7Eab+'(NbVz`0ofa>.jkkzܺuMJVa2gNIIy|x q-,,ߕ0[¼Z[[]feef׬Y̙3GAq`yyy{쩬w^``Q^]ΫW.Yd%%%6m:x𠫫kDDD\\ŋ-,,?~ {{:;; BOOOb<553 so޼jҥIII'N$5kFkii)))Yr%*,,_n933T__r޼y"m+s5… +V@Ǐ}Ν'O,Y$<<˷oFV+Vppp@=<<233 ɓ'RSSUUU>|HӧN `0O/,,444?JKKwܩzرpkk_~tǎFFF>>>iiiׯ4hR͙3g,,,D" ]]]Bc… , )))ݻw{{{!yyy3fx1»wH9s899)0~MGGGUUOe ›7o666r8-[@bI$Ҳe⊋,Xzj*k.-[gff._gLOO߼y3cnnnOOOAAAkkiii766>|˖-uuueee~z\\\PPдiӤ{d2mllg͚ӳuVuuÇWPǎ555$~lٲ|>?,,f3>5(򳜬6ĈwG] ٳg:$̜===<>{ã߲1LGnܸ‚8흑NNN%%%(zI.{Yƍ+חۛfC\盙ݿ[BaPPЪUP &X,a^^^pp0:XVV S$鶔aƒ18emmۛ766jiiC {zz !uȑ#QٳgN6M^ c(r+**БcVVVĉ #FK.}(\UU!Zv-g555BHd2Qs碂K,IMM._D{D(CRRRvv62s޽B3P4pժUeo߾SXh ŋ*PMMM°Ç<&LX"..0`$Ucc |Ç2+;U&ؑL e>vD"&:䝿y?t3TP%bt:0TLsP(lhhR---4' EPNk֬DAqFR}(VwJJʷ~g%O+;U&ؑL e>vd2aL{v0?|}ٶmAUUr]]]P(T*@" EN`$I^6Er:::pu K?I$xpZZZDdB$ym tӧO߻w̚| py GB|WϟE^^dJ~~۫555k"}qh\d^R===\.WEE]COOO4/---="!2*sĉnHD\ )--Օ'}6mmm䬬{655+_޾~֧(@GGމO}K%2ǎ$$TQ`]L?npYoo﨨B 2ACCC9_ t:HS(J|x(%򉉉uvvqoo/xG%rÆ I^[y9ùpcbbp=Ԩ/I$ovZ\\\``vKKKQBpر79ub)qm?+ 6L|!_WWWPH\F"q;w֮]/\|HâE\]],Y ޾㥐ggώxyyedd$''?G,kڵBbDK.9Jݤ IϗFnذh͛7ԉH2۪P*#^!aaaL&sٲe-̞=~9'LR=Xw>z(աVxK_vj---PWt 'O}b?sBBS49+~{{{:#SSS4)a D_lAjkk?.Bttt6l؀ 'Ė̾3)S$''2L&JV.mliiQFG}oFW tyR1T*FΜ9ܹs!>e"npdeoVZ((($̙3ɛ7oS,IPRLYG)8&4U022 w2kkk˗,Yx+V,^dC&srrP| Ç /^l``cRaC}ş~whhwE8 n7 qF&)Ǐoݺ5cƌ &6wwwonjjR,IP^Ln}ҥKN={,//m #l xWi^k 7o\]]bƷo~ԩW#$$mtE9r޽?z*`Æ ˖-:u'O>}*QVQQ[RZ]AAwHcÚ:?! (` (E@kŵRkVZ_h]PT-n(*PA\싀%@d @B6< ]g2;s9s3s̙sԩĤ+W GFH ůn@ӿʫW^pA\]ϟ?{:رc <PT//[&$${{{effzyy566UVUUUǯ]W>?~<&&ٳgIIIz L0A!,,,455eX[n988ٚӧO8qB{Ee˖Q*ѣ(A\ ٿsTTɓ'?3eX,@ zyyeeeS###P:::zddBrԐ +WLJJ.^e(sǎc Btґ,… 2z/ڵkB>ollO0D"Ai4\paUUJ>}z;wLHH@"̺u|}} s7m!,**B9"0.\?>}z޽/_֮cEE̙3ϿsvŊ(入%JtR644dL&舦$2fk ߼l;cÆ w455 uvv2>0uԜ *0EE3fHϏ\BYYYDWiL={-@jkk?~xh履~P(<O[[[FHFFFEGG /,X̨ ,&@\nBPPPDh2yeyD6ptttYCCsYbŝ;wwl"hwoAUUUxuhhHUUUƵ>>>'N`XR/G f L4)""bժU˔fP|y{zzGgg{?~CCC?~- BKK+!!駟o FD"3l6u[na455!ocM7phcw\\\ff+`744xxxLyx{{_>..!3gΘxyy)((0 GD o] eo*)++c޵d2yT7([6VА{|2lggՅ )… G% F|77q`{2zYY%p8obblٲSNa #;v\|fcńׅrJ+Q$ip8cǎrJJK{{;*.mؽ{mmm!2aaa񢌝NNN/^>~ʔ)•ج_… ###EEEƍ;{lHHǫ.++Lf~~~ssseee___qqq]]]}}}gggIIIMMMkk%%%]]]uuuuuu}}}Rsbcc LJJjiiAE"mUUUUYYRQQQYYŋ7o^=m۶СCdmmu>|2 kkkl5 r߿r333;;;w588OѨT26"IBiӦw.\U" {J̌BtOx<&￳L eek׮Nwpp_}'fѫI5%>;0 gT:t{֬Yceeett.'}zjD:uҥKBFE5%59&UJJK,--HZZZzzz@@?h.Ѐl6ɓϗ6坈FEE1d58arz:;٬fN}}#v1Pxc;osgT,w-88-NC>y/,&''ס+%%%W^uttܲe չ\.WWWx<oٖzzz|FGG>|c8*T4!'O޽{{qppxvuu:uH$^!\.J9s;SF36mZ@@_(ο|6 _|nba4fϞ-6G}}}EEE_ݻO:k׮{zzz&&&N:uU-[zJbX7iPT,B !}||ލ3֮]fI.,,[zzz~~~2B3*%I.)|̜٘9UF,>0c 0$R/NwwQaUUUeee@ ,X =oh[IMMϟ?{+PZllV"+EX䜷AΞ'7>ygq'v~Аi+\.&RXX၁b_Faa$(džb4fEB(X ###bD CEtwwgBF7pnݺ%+!8hnyfS $P 2jiiIKK8} !~+|(\"[ZPԜ3: {†Vp"BS3S~ 0iii+VP(jjjgΜсG}'|PPxi||eff?0<Ç3={۷OD-ׯ_9sfccczzkVX'N|i^^#P)((/D"edd8::87ohiiKT<ɓEEE$髯 ܦȏa%744x(?~otvv+<,,쫯222{nIIVZLRRRR>s6}y捡޽jgg'jZT_~y ___ qիW`kkڼǏ^ZAAJ>}TMM;66M:UbO={ce٥٬A  PA 455LOOO !Xt)444Dab^zyرcǎׯH"/^һw~JgddܹB8<<<~ⶶW^A'aII'* !(*1**bd… WO޿dX,[v!r +LkkqT*B?z{{m۶šWaa!J牉(} :;;Bx%6-"TUUڢӧO;::BǍFFF_x˗Q̬Bc"zzzx<\lYJJx("Q-9zhdd$J;88S`ơr\555dJʕ+PzҤI(QXXhjj*bn+ ^^^YYY@zNMM͂ P/RF,**rwwG<ؼy3J{e '|2|= +SyYJ ECw¤IPuuuSNɡP(իW" l6rzzzzz:H_rڵk###eϏ***r8CCCtH ޽ hjjd0+ԬZ[[+++-,,W4sSSS555T'H$U\\(`cc}ILLDc/Xm"ر_~! 'ibw{``HpRJ K2~x& !LLLB}LOOV=zhƌXrX-q㆛x)^?vˣz(*((hhh SVXqḒXG$''O0iY*q#&/Wuuuۜ9sÇ% ǧ^?}d2 ͛?35|ԩϣu%cooh"LKKKIIILL|왴| vHdX<H$*))a%T*;..nʔ)K.H8}}}2^o: UJKKE 0 3,D"P6\]]曦Ƶk.+CCCyXFLT~(eMD@ݑ+4RlXXHxL<88(dCbx2333&XqУ*/nJ5kքں9dw9 \}U[[{``@b@`O=ۀfK0𮮮!++3gΘxyy)(([zzzٳ]N&H$RDDwLL@ vAQQ;[B,X@Yoo33x<4(ȵ|>{' {r8w0kggۋR(܄P( .̙3 (++#6xgՙO2lޯ(**a ׋:4O ؈Jc 77j%ަxC!n5"phTe˖:uJZyz΢E***TVV|G߫.\(WN,wI~~~CC>xիWUUUMV__p͋/]e__&H\.799YSSNGDD 1 ;;;G󛛛Add?={FӋ86lؐogg7n8}}}/QԊlJYYڵk'O͹孭cv IDATm۶:tHrdrVVNjhh@L&Heee___qq1dЈ r[9sܻwoddVVVnnn_&mmm4)''R뛖mJ577RUUU===Қhnq6n({Nܦ`Kz*`0*++lvDDį?e^ggg|x@~~~SSSnnԔE]]D255h KΝ;`0LfRRRKK zKM a%~(P(((&q rrvBi֮[ÇM)Oϙ?|-aQQ8}tf^IIIWWW]]]]]]qq1˗/GFFnݺ駟 By$}WOBM/8̡C;::0?Z;;ɓ'ݻwOYYyܹL&ԩSQQQӦM3 GG &饥ijj dC |Gu)77@ ,X@CC***B---OͩGSNUR(_xͥBX^^nnn>{쮮.CCCC$IAA@ @UTT̙UUUD"QYYа@ hii͞=Y\\?w\VWWΘ1A |>Nl@`aayܾ}{ӦM)**"҄@}\.WbYYY-Ç]]]wextv'l,++C766qƱX,DzƦwܹȁO6cjjm*>PTTmff4xT*S@ P ,Yul $G}f gΜ)ɓ'֭x]]]QQQ+VX~ԽkjjjkkLMME*aނ &N6MGGGbGnyfff***===d2yƌ[AEzEIIS`FᓇSM~>!88`׮]o,zꄄcDDԩSΟ@BBªUJJJ&Oldd{LmBBի߷<)mq`4:=k,2::Z!=a֨&;YdeeEFFRT *?0**d'''#qG2e]RRmy{$*fmm?9x|>_x|6kTT_:0%@ ls/DŢXN^88rvy/,>y/Y8wnܸQi4GFF cc!RVVVLLW\>:uH Յiii\.wҤI***===?{} =$1従x,W\_bb166]QQaiiC\8y$Lqㆪ+Aihh())[֭[۶m}SRR7rT̙3ehiipĉpmY8 9W^^^k X['xxxL>͛{ٓH$M%'ٳgߦi EEl^vg̘|r,ΣGLLL~WuAAA"ŏO?oٺu5kBCCJxlkcLx[aL啛>ZWf3Y#VXGGDZOd78n8ᘏ&M:~8FNG6lԐH$th dsTlݺӧ(e˖ybC>\XXw^˗!2HuҤIjjj`͇E_ڵ+)) }LMMOV"ȰfCCÌ39Jƀg]] z9Y8T-ȡN p8HdJDhiiIKKg򦷷W$NH 5!з2c/].X\9cE𵵵Ei qd[C hU |M)ʕ+o,!DTFL!̈́ V6ouL]]E8έ[<B3@*@qEF88,w DzAVVV```ss3yL."fff"""ݻ"##'L|ܹ[W"4NhQQQ +WD*4=7H=<<88ѣGtرDDDT֭[ӽ 233Pݬ9s:u*11V͍_v-jBxݺׯcRׯ_AJJJTT_y} Tػw/J/[,//Bdnn2===) J/^(mff !}:Z駟( z G[?):k1c&ED:ujNNBIMM^p7ܰWǏo?~A#-f␐Ǐر RPP@ RSSt۔ ifŊwܑGQyD6ptttк¤IPGGG.@D~LfMMMt ڝ;wnxx8@UUupp+044$6@y䌎vqq Pܼ`I&EDDZJ<2ҬY$_yǚ377̛7O8'HsjشiӥKE1322|>>&###77woM= EܒbdԩCw;v\|f ǷnŋٲeN9~v|.p8l6㑑\.H$d rCCCO>]QQښW"u Rijjk2\^^FѪ˻W]]M"\nrr2vޣ0666ׯpHQQQ{{qΞ=񪫫ʾ{&D+..,))immoڴiܼJ2ʶ<6}ܹXd2ZZZШAGGGF+**,,,|5/-ڱc;jkk'O<00Ctuudps]vAQGPSSAQQvĉ4M\\\ zzz ֨ kjj9ui4J֭λM(rh ׯ_nx߸h(;w|UMM OHHxwΜ93,,l۶m/_ B[=.]顡hؘ|rknnnQQ%Fx%\ؚ"HeeehObeen߾bbbFݪ.ZHEEqƼ<###v eϧR {ccĸsjyIΙNwpp@%%%h >@bT"M6cjjjffA |>NᇨE(cf''':^UUE$  F#JJJg2B333}}Z;;ɓ'UWW ssƑ@`cc3nܸ"mmm33'O oẕI555}}}B'||G:]]]MMM###hTnmmUZZjllӧ=-644 Sv 555D"QEEeaFof|JtMNN_jjjLp~etŨ8t萷7d]YY-!|5*/|0IG]f0}{c!w#GTP]]mmm}v?Ǐ|llzߘ/FWWɓ(tݻwLh"mm7o_`cc3{l;;W\nL&|||o}||SSS @HHҳHcSH0++Yfjkk)ڵk<y,w :~D"q(\.)--B H#P<󆆆ė\. FRp^ׯQ)tpÁʿ!DB_|!dTu"ZZZ@2̘_(gddN8--uV4BᓓaQJJU2i !RXv>i~㡡(JO{{{xx}D:֭[BI* s9ri6(((yN%,ckk{]ƬC+++idd*C0W&NPb |>] ~qp~ O!&&4-,,liii{Ǐ{{{Q1WWWΞ=[YY9//766 b VYYy+++all,@HNN~'|1O[ZZfffZYY4ŋ~iSS͛7H$r/_NIIbX&&&›7oN]fMJJ ɬ8}~|r.K6lذc>֎"W_@t:͛Hr{{Pooa*ww6-QEEE+Wtvv~̙3ӯ]b @w}t劊 ./. P]]TWW8q" 11w IDATQEEeĉ6m200tuu]vmWIE===Ǐ7778q"JKIIQPPhkk >}:_~p8$)##qƌoTcmm3g*++> ӧOCBBD [[[?ʕ+?&զ4m}}}~G}4iҤ8gggXDQO611yuNNNNN 8~xwwwiӦq~[z5L655 [ZZ,Ynڼy3磈i?Pςk]WWWgdd8qBCCۆ%%Ço߾]/_vwwWWWr999W^ussSRR~@ B%O%%'>}4//1))VKKÇH ~ /+-.-f q`XB, XŋSTi5C+++UW\YjV. PA 455LOOO !Xt)444d«W8pe;v,&&رc(2%߿L&@8::FEEX,2 !D)Ȉݻh̬BxĉK.7m!,**=z422}U\\ CuD"}'rJ=vXTT122*//Gi$"4nz]a^^ڵkQfaa)ł޺ukǎž;;;d eRRRdW>8(GDD˸ٹ)S|2*,,竩N!/E>}z޽/_} ?eFGGDiiŋQ… H|= =yp/^ľ?~Dprrz9J?~޽k׮C|>f(ҲbŊt͛7e//d㋆8MMM%$$+$&&Ja…KKUUUGCCC2"ھ~nԩ999 %55U z @$ @ `hD"~ʕ>>>k׮fds-'''ԄVTTTܢEH$#rpVTTTVVT:j̛7p 777}+l_@I{822h}0gŊH?>>>'N`XhAPBгW[[j* ۷?|Pg ,wʚ5k߿okk`0h ⵴رޮ`0L&):d0 'Oϟ?p8-ZhQPPБ#Gdڵk>3yqh bŊĆ~T*! -,,/p }}}`l "ݺhFiKHh\(Ԅ3g)LBBBcc#J޸q l}Mhcc$=qQGCW^?~M9sTTT^|)#G*l'lP ? hkk"9rϝ;'FT*;...3383(RlkZZȳp<¨F 秩ᡪ ?swwG~LLeoo43oGzddd͚5( BP({J,볶~a%9vuu YYY8q>f0Œ KK˭[zzza?d2* Hiii~EFFF_qssP(K,{{{%7lpٳgh}\ pBjjjШHBpd'.T>ꫯΜ9j*ʔ}eeO?T3g8pmp8k&Ec+a{{"%&&]{&&&'N퍊[l5kq.\o߾ ~\MPZ+"®ٕߖܹs?>>qqqpaL<VVV[7o׿Zmmm8lpps1VX cTTܟmll駟dPHuuuii)[zL{!DrwwG.[ZZt~6mddT__O MLLZ[[q8L:::644 JA+++iӶpbbJ^ݬeee8nٲeD"AWUUY`LC}}}ZZZeeed2f͝\vT+:Z[[---OѺ:"Ӄ( GcWqCI ~?99N$젴NNNzyycbbdd?ydٲeǎS8?\.;::BEE3X?3Ɠ ̚>%_|o(uuuׯ_?}_&@dd$Jʚ͛#\prݺu/R͛7O=Mˍ2|wm?NqK.]f`sF̬;7k`fIp˗/?P97/ p|]|y|||rrݻгdzXp8ǩp1sss#Bs)S"866#>}o͟?_$/qĄj 3vns1ߋfMl5K~ 'Ox^pe)fff/"r ƫvC"`YI!Lb``Xit 3n( `ob```````03 ㅀYx/[613dee-[,..nzcbb͙L6w^__1GsΝoH͛۶mb椤O_DAooݺ'''?Yz뭷zE7TPPbŊE,c`L˜VX!x(L4vٳ?{lXXXssD(JG쮨>'Or\>omccc?&HS/"yXCN<922"ess綧766kOx$|H˶r@>"Nν{x<Ν;w9F10c!cD"Q& λロg$/R:Ϟ={,v֭khhŽ,i44^Q6!,ʕ+赌Χ 0p´ ӥe{q =S'//oÆ Udkjj^z-b`@,377b)fdd ",++K~`0 HMMEǻ 444(u .Ntttܿ =z*[ooop8lDRVV>bkkk322|~ii)(- ,R tttH e(ѣ]]]CCC\.ѣGh4p8,:Lc;X67lrtebb"11qtt~YYY---hD~C?&''˷fQ0|:եP0eEEE0d|Q 2BH$Y;rHggguuu{{;LN2DdffvttH/bxzMMMzD}}=mnnnhh鍍 C&#^]]-3i{{{PL޽{.e?|(*-- PX@/\`0S]]= n ffa$˗/o'NpuuP(7oABCC _LL3'\pA~B&&&222n߾X[[sovpkݻGOHHHNN ,))H$&55uoEEE?^{nkk5kඑP(| nݺu=hhhhbbpxx8@F۷wڕ䔐p)W_ǏWVVNQl6B|#Baff_~ 9qℿ?L>r699߿>22RkZb~0GGtwwwtt455`?11?Ur4`pMM..?#: ??LJF;vL>^s8;w888wyG$(;wƜPhhhttX,őiiipPl6JD\+N2DDD\| &NLLdff.]̙3h ӧO3oAuq8>___hܻw/22RfHO---K, 8Nhhkhho-3g=yƽ Ҏ;;PClSN*,+rG7B|>p8̀! ffa$NNNǎ{w|H~~>H\d /++\|y֬Yx<_z4"""::zSlTMM  8uTOOLv횾|Ak׮-[p\'''w7ammrJAR]\\JJJ-,,,,,5߻w履~pmmm%IPPPJJؘ2 [F_L&g FFFׯ/.. ߝX{{sZ[[{zzh4==7¶D"ю;;.ÅI)DI >Ha7on޼:~aB]]=>>СCyyyeeeo߆6Ǎ7o.#9,,,>>^Ņ7G^\\<<<,MLLBCCs*X,޲e Lvqq166xtXr%ѹѣGGFF444շn-^xΜ9 ,JLl6Å6l@&ׯ_O:^ZZXjUyy9&166vss;Η.]D&a@eOOOT*uɒ%gf~uuu09f2򲕖nڴnӦMhGohhXb={[zzz... eTc103 c&A=6H$Rmm-FKMMvZZZ8\F㼞9su}={n`A͛x}bBqvv>ikkWUU8@"J"vp84V DBaDWQQ :99DpwwɁ8;;zϥXAf͚.6lxbccy3Z˵)2k׮]6===??ӧ+<˗/555usssX,NHH1`Bٳ_R[nRZ][jooh=gݺu E(ʦ4_`[JJ9Lh)[[L":66yMАơ7OH6Dח)H@pppddBـ- ߕN)A 3Ҫ߹s[LMM@ Tvww'UECC`іeQPF^!p#  42TҡKj fNHH}v\\D֞@ 6w8pT:Cyy9 7o~2UM۷oOLL422Σy椤$-z88( a\dY-?ɦ~1dF6? IDAT@.P-Ɵ[˜IP٧OƮ^>q{{{bqZZZHHH~~>)x<_ա wBt`0iiiŋPl܊V C"Hg/zЏONN.YD& |>ѣG'zSW.|`CM=D$**j߾}$I jUh&;;[GG'((hϞ=>J\\\hh(E0tߐ{1+V6o\ZZpO0,,ݻ)))۷oW(իWGGGTjxx_@Y'&&@;FL@etz*SfQ6pႚɓ'Yq g ]t:~nhhWWWߴi|lQÍ3& WжmۊX,_Zvm]]ji)Ma2blܸ Mn ^ATȀ1mtC1~PS{OW twwwuu]vӳf_MMMkkk/^~bHzꄄ$t'ND/[a/_&HIII͛]]]8nbbBG_jչs455+++uttΜ9%.\xB|r醊cbbZZZbcc,XP\\|ƍ ?~i||_5JzjRR544411@ikkKKKp8~UYYp8addt5uuu###D#kkk;;;3ghii=yH$\CCZKKˢ"77#!ىF$|XSSӶmۤ Hs^:#G^{5MM͋/2̳g^z^__ r3woO>Q=3 ZZZ\nssٳܐFT*S$fzwL"̙r/]d2TwVvĜ9sd2[ht)rihfggo߾}dd+))={竪LLL<== B||뽼ݽl2 SS\eGpOutt]]]_zu]]]]]Daaa@@򘮮mLLNQQ ekooWؑ Ha```bbO".\p?ZFFFwwPRRrҥfncDS؇@2w.88lIhjMo?qw[[۷oKG3XB:Ǐ^URR?!! o|g5k>zi#?_Kmmm/~=Bcbb-Z4'&VRRPSS;y>cޣP(2fɓ'ݕLIIFAT3ffa\v?S[eeJ |5 o߾>H:ntDD?# BӃ+-ZGݻw͏?yh* W /~/˷8nMRnnnwܙFY3 Y, \. /(()]]]=== 0),,[VV:;;=U]]=443x<[$xx:P(ٿ c Ed+%$NWTMM ][[j*o544.]zEݻ^^^0<2jŏ=ɑH$%%%hbi\.`00nOO2FK *TL###pi.p1 x-1:~Q]]UUUuww>|XQQrsss 0/FFF{{{x<^plA,v``)䐱#GtvvVWW˄Q/..2z FVVillT HiiiFFE"TWW?|P-Bܕ2dee=`0FFFtzFFZjtt433𾓩J$/ D3`fƌq tҰ0>3;;;@ y<޹sRW_}?xիW⚚Ç{zz;v %~~~111 fS(>p󠭭îGe2b~߾}wˋ b8222--Mif(x|xA yKJJ.../_njj/++hǎו#ɓ'lmmᵿ?LFrqqA+Z$IGGo:ڃʔDz|wa +_ᨨ+Vxzz~e˖8Ν; p8>8t?uTii@ HKK;vXFF/^ЦTftt#Gz{{mۦB 5 0] $''Wt:㕕\6MRE"ÑPSSS,X =dڵ+11S(___}@'\]])͛o~p޼yw)LyWQ&mmmmrr}}}dd$~w/ܽ{ѣO>ݴi`߿#.....|xxXaUPNL$%c̝qI!wMOH$ΝodDŋ#""SN#Gl߾]"BDrJ_"UUU111)$22ҥKh[AAA𺦦&00V؈ X,H$w ~{zzLMM'''efX4M$l666bxɒ%믿H$yyy*?˧9rAaqeE"䶶6D244diiNgg纺:DTQQ6T SSSx-|[\.W">ŋA[nI$jhhyyy 5557nH$t: &&''/[ ^GGG {`ϫi$'L7|S:EGGG~tuhoo!Sywwʕ+ƍᵗǏuzzM5JU؜|7rWʠP0zI$>KKKxoG&áC% pqq Dh$J&KbY3FfffYYYJJJWWoPCCǛ={6~᤯a7"(srrrrrBavv=|̙իW4 HNNG;;\PᴵmmmxϏ߿nnn))){_T֭[(888X,t.wɣ-"L-Px D)ݻTUU* Q/_MP}={\t STk@WW꼳FkwVVV赲*_%KGTip83P>ɟ) @2ep8Dh׮]KKKsѩnoo-Z oN*%yi7l{fee!W(<uzCunccW:;;mmmaªdP800 e _Ąٳg===GFFZ[[->׬Pon"bll [MMMGXUWW;88 (A"Hg&Nll /|;*6ܿ D000PQQqlٲN!28;;ϛ7pÆ h"v:"FFF2oZ^-@5 [op/RSS( EơGF(}]AALMMmmmgϞ]\\<::j``;Bcd2U;r(j\?/8NZQ*LҨʨGuwy-SSӄ( ԔnyVvWg6!!qqq$ Z~PݻwGGG[ZZ~zRRR{{;z( cppP(*H[˜rssա#vooX,¶677P/- x7uWWW]]]HHH~~>L ,]]]HkoopMQQQpp0~UQ$$555s̱{ZZZcvv6˽pႚɓ'[[[^:::JR}P/]??"M駟\\\^{5EMLLd kݻ}(MSS.B<|PPSSgt{BqGӧ7nB[ʦ0uuu2 }*Ԁ'L~-#3QkիW-d\t GZLs5OT"**j߾}$I 04e]ԓ'OBBB>t]aU蛛L&+HӆMOO-^9-? 55|2Ɇp=11.Ap8ܪUg͚PXXkmm=88x%&XWWO?uvvjii|||y<^GGGkk]gggqqH$]zѵkՍ|||~gAZZZ233O:%?eff:ꖖ޽[ٛEEE^^^N^^rg͚#-6_jչs455+++uttͣ?۷WTTpٳgЭO8z~羾ht飞 j!gΜD .<{laa!DZ||smiiAؔ .TfA\O?P&ƚm666nO`yeddtwwǓH r8dϟ011nhh0777oޗ_~b222nܸMMy"@*++q82ϙ3'))L&x<[[[eC&_ORRR||ࠃCjjjRRuvv^Y_MMMkkk/^`kk[^^nff644T^^NO8Ϟ={}A<<}k`` 3P+  zW^.sRo)`韄tbAÆgrr@Y6i8GD"6-D"aXp -U$%%(l6{֭_|ARfR𰾾>P(Dp8iG&A''' ~TTc맨yjjjPl>?gΜYfb!bhh(~޽7Jgט@  VkGf022"4Q+2w.SQ}WחP$;vx"o+fkkkK/HxijxRmb$UxuttfϞ= f?N$e98NQ… tq]fMAAQ$ϴhH$ ?Ts=/_3w\TE d2el,e9sh43- }}}:tH$*,,&1uuuȿ5֣z2LƚJ= A/sKjiiMus{W*CHFB", ]h4 *2,w˜Mmx 7Pp8YVVCNNNvvB133D:~O =ptt{A477s89sa!7nhiiܾ};6fl6 ئCEEW7 0W i\0T`:``<x o 300000000^j!U"B|*Y/P駟& DDDرL;wN&믿RS4L󧤤^~M߷o_wwwWׯ_'XN1X,bccTSPPbŊhtDr…d%%%Wf ճ mNZmmm}}}nTz?P" K)1=03 c o߆jjj'O`<xAr*씊y Huu5  E0013l 9@ @SSS,XP[[{A@ HNNꫯt+++Bohhp֭{޺u+Da0377GdFQp8|>_aH$C5448;;Ç{zz;vFGGCCCÇVXʋΝ;x<^TT[[[CBB:::ixǎ 822HyMzҎ;z/:88DDD||H$dH'""% MMM0eɒ%fl޽{ae3g׮]xd2[4: :;;ᾡTQŋH$*..qppmp8'''أ˗kkkS(.u`8X|իWfNU+ҥK__}y_|e.YDٳg/_wX)((DP'$ ˫ 6{YYY| Üy-3A|.@a*,++KIIb٨(]3Bvv6j9sfՙwmff4-55ڵkiiih/̐i0"; 999 Bmkk@WUUÚmB"## H$:̡Gfa<ՎH;khhHo2 B$svh%@5g6.o$D---a'ߣמz琐p8K Wnoo$ ?>|ҥKhNt{ sssAd6Tp8eGhoBQ139*?{Hkk@ izήASSSI 3B;wu떩iBB χki2CF& i _UpB@H$|(pVz9::^rN~rp8+[˜1tuucn11DcHH34>>>99 T''' zTWؐ<Ee۲eǏaP866x7`zWW|^G555:::AAA{Qk%_Z]]ݻwZJYמYך5k֬Y?{P4޽ژt۶mCu".HKo>$}jj O<ڪB3h3QH$P^eOŻ+77nIz{{bqZZzͮ|x-7l@ t:}Æ իWҽGm۶M  ƍЎܿ_F6YQQQpp0Wݯŋkii"w={H;ȩS jtCQB԰%ѵkՍT3gD"… Ϟ=[XXH" *++q8stt|qSS˗i}}UTT0̖www+W޼yܹsVVV Z|xO>@h򍖔\ta޼yJ dee /[,000>>utt\z5))0t>?66Դm6_̰&d2cbbZZZbccϟ:c``P\\MAٳ666JJJfϞq 膆sss oܸ111! }||̙D&y<ޢE`6Xlii$'' 8::z{{MLLl,.777H${{{>khhX[[kiiYTTꚐ ۷*Lqqq\\e<<<1)))>>~pp!55ƍzS\\>D"-\PF%33ԩSbZZZfffx<~ժUΝԬ~իW<h"y9Դ;UUUp]_~!IIIgvuuʴyEMwdܹۿ~ X`GCZJa_Y$ RPH$C}!|>ppt``@SSSWWwǎ/^|vPب4B^2#s8< nX,ATg냖  Boo/BrSRRR6lؠbR( Xf A("2::JRk Wbtuu fןG$lPvp Ph//]daa!}lȞtTQL*}}}ZU(N`||\CC `hIXbcuh4F3W+lT555xqV*03 3hcX#ްaDRpRSS`Pʎ S3DSQ6O4|.M?iJm>'S ffa```````03 ㅀY/xi tz^^Ë́mb9O4s48FEEcz۷oτh3ɑ#G^V Δ?t: }z$KNN&'O夯$%%+W 88p@:lTP5!!!<9eпrzzzcc_# 203 e"}={ [Ze"vΝ-((=1//O:l3Fsvv9ZZZyyyZO씹~餧K|:e$#lmʕ+k׮kPv }@ ̙3'88СCl6ԩS --رcnnngΜQrἂ ---K,A],Gfe/mNNΉ'p9rD>3;;;@2ޡ8*» AP`0\s`H466k׮D<陓:yjjj>I&w!SBJ$6MRE"QΝ;U}Ğ??sΥPT,#//:Vqㆲl7nR֭zzz7nFFFׯ/.. ߝϞ=dllرc;S~uuЦ&ggC=ztddDCCC]]}֭$i^r%RK,)((\v ~z2"-[l2WWWCCSNܹ{{{J$?8n׮]ϟH$l6Å?4}laG@DqDTZw۪ՊVkwbmKED@ Ⱦ#K'y1<%!,bѻ䜙kf̜t6-teχDZhǃe/{.TСC!!! 3gNU|N^Ty6D__׮]R|ҤIcǎ3t:S~?~qذaD">;;;}}} 6o~uvv~X,&NNND"8k֬f60Eқ|fAKKK,+Q*͵^z%=!77WOOɓ'|>Æ ...cǎY~7h4ڒ%K#o Wp=pNNNd2y111>>>W\)+K%H/__qcW#g3dhׯ_;::9kk^ࠐV*" R7%%%R mgg|>I=5H=4''GOOGs||< )) <8}t -Pð&<9==bAG 5Y~*zҥ@(f!iӦ:tǧR$|khh 2|5@__> rY5z9BD"84%) D0La DZbٳg[_ |r/˨PFR0 liiy~kaa1~O655ݿ… Uz3-zq}})yyy=F ۷oߐ!Cl2qDk555b1HPC p#4}= bb8VV*\kT[YYC S=P;kkkR>vS!L -z >߾}{EEũSd2aj,#8m`22e -ΆV|!a 0 ,,,55e2YBBBPPPaa!|y 2>kjj… "RSS.\H$$C֬YsyA]V.X,ngիWfff uttL&"""(ʸq[YYy7oh5k8;; 'b˖- ,غukhjTZ__ߛ&M*))innИ?ƍSb/+ QF1ccc!!!xũT"hWUUALJNd IDATED"ZS]{{{[[[܇NHHJ *6l I)zxxH$|ajjŋ/~ ^: KWWW iii=q<۰򝽻27o555p5!?HX. 28!EEEIk׮9::9/&Mtu2ܜijjJR>aÆ>C ƎK  RRRMMM~w==T'''*JR/\ARΝ;'J+**'~'OXYY!"!C233׭[fK{ii}EEٳg>uҚHfǏrǏ1 knn믿N8all|HgbbBӉDԩS; olll>ۖ/_ˊ W^^njj:zCzyyUO𨪪RYep`RMMMnL&#HAAA"rZmkkK ~GWW7J"Z/uS+Pe*N@`dd8P6m``/҂H$#_jLd2TjWWa+$immmjLoL& @hmb `‡L=BP>@ (x*B3~D"f(yDžüzoOׯ ...ݽeFvjlL&ט9VAz]԰Tzzz=讇* &p.[詤GpޅB%hҰIl6_;a0III---Cslkk}uZZZZZ7|ӿΟ?_ZZd2G!>"M 4iVIC@@}8%z@ ❀,@ wr@ B|xMYY~iAAAzzm6?b3,T@=BHRxlwo߾op@ oqq[QI||?@ ͚5 ު ...FsɔH$~~~O>0, @,?}TWWwb;v Bі,Ybjj*rsskkk%Ihh[[[Byz|lٲ-""V,Ǘmmm--- ΫSUUːkkk…$`$''Go޼㏕KrB333omm]lYTTTnnڵkmll(ڊaׯNjcc#dSS\y{{+ O򺯯yG СCo޼_ TYYbŊ\Oʋʒd...W\p8'Ovuu}1L5eW_I8O @zvvFJvF }zrd͛7/_dbs êΝ+J1 [`AyyH$z*aW\Yt)a`ʒ}}}KJJ?c~~>aN:x 4rZ ^zp蜔*%88 <F `{n :::bz9l6{;::=zjڵ)))zHHHfffo2z~!)) i4=zmRBKK Fsaf͂vui\z"h̘1RAVZwVV";55ZʪðZmmm!>̙3͞=.rSP<2ɓAAAkLLӧR޽{ݻaH$O0 sδi`̈O>D0*E!pdg|0㺈`eeehhO@sss+,,۷o̙pl)88ի eڵ >>w zjuuzQ 0s1c,[lrWD82 tR5~if͂?0ZYY BRxaMLLࠎJ,--[[[U|dRT"ŵ:88\pA9kNiooW^9KpܙPYҊ .0ݻwwϞ=KII<y{{GFFjii%PDM|Xe~mee_|Tp&>zc?+kiW0l]YC-\WزeݻwEv?yL5;i4ZGGRimm-򨫫l7n$%% wUYhxŋ)a0>>>$ 0&)333a̚qƶm21-[ܼy+DƤC;wsIR0 pH/rTb %d2yҥ*_MRpOF|zRիp iӦMeee;(SUUo1RNb1\Q.ɓ'\puna|>ujFp'N{{Ν;uV vKylRUE={ &'pw'J*0l0ahhpvv+((wcbbK3ɏ+#NoHX. 5l}>$(V="===11fVTTtOOO:~ꐐ>_~DbwSԪ:ccc{{b&SWWP^^^PPP^^琂dWSSz-___:.J 322VQQQNNNAAA38q",caaP(ܴiL&KNpp۷LfII'JU H:$Jkjj! Y,ք Gmbb2~xn;;Ɗ+VyFp8 bرۘ1c߿vZh+++cc/^߿_CC;MUuuu~~>L=r䈉 |ӰM, X_er{d@ +rFFF>D<~8KbAYf!w >m۶~2 ܞT" ><44k׮iiiH$ 7@68>iҤ_ /@ h@ ],@ wr@ B k:::***7D r `ѢE}M(J;;Ŏh}ͷ>#y oqqT @ f!(N_WTT)ɓ'G} zpx_ H{Ǧ)))}sܴwAo2D1t{'RRR=z4}tPVVfff6k,x˗!!!L&qڴiO<)..vppp8k׮^x!l3D;v Bі,Ybjj sBaaaa DZlٲ-""WR,Ǘx~L&I{iii `2GM81000...333((h鯜{lffn*@ KF &Mjoo?>cǎxv__uօr8ss &|LVwwcdž$%%555-[p©S(|嗞|Ɇ uVYf >>SmǎNNN6l@bb5kB΢E㏢"2aزe<==gΜ9nܸO?0___s3JKK _eFSpٳg/X`{( @ f!`eeehhHnnn D---++ѣeeeL677233H$EUU˵V)Jk׮ƎKMCCC[[ؘdlll˽-[q,+++NLL @u&nwwQ۹sgXX@__&==,@BRaD" fnݺ3g7ðz;;;]u'J"CCCٱcG/_BT0tP܋:t(FeeeTR5U=X\VVV]] 9}tC Zx:t(!!D"]rhaa1wܭ[VUU)244 r @W/ O`0lmm;;;Lf8cBa:moo>|8nVGG(3d@ wB bX,a LJD"ad2bqff&L&/]tpOxAUU7FFx6lرpʓ0>SbX>%Krssa>tRJmooqJ˨ꅇGGGË/^(--UW7 HX. 5l}>$(V="===11fVTT􀀀dWSSz-___:žPBTTTQQQkkaQQQϟ?"(77wUwՕ<؊ B &۷o'7n??3;;ϟ>|ROo~`k+D"`ѢEMŅH$29|Gz/d#Gttt,ZHOOoPPP0{pu@Hx 7 18pyҤIT/] /:u*$${޼y_QQ]v'ޗWw9riO<:BqssQ˽x" ޤQ?ҥK!6R_ǏsN$_߾7<b@nb?۷ZZZwRSSCLJJWD$mk׺3yq~ϋIK"ӛq4EЗQ4[[uEGG@OФJZZZLLL-Da}Qnx?:Ӱ3 ]vmݺL&xܹɓL[nxB__֭[ DYׯ_X,777 GVVJNMMp8ӧO# րUfii >yfaaa=~իWT*UGGG^~=;;pԨQ呑0tahmfrٳj*Q&nll\tL&SDDBpe6l0 9t萼2ĉU޽{P(__S$]zð+W,]0@zz cv1ؤPY;wnI$O?t۶m#ֈ޽{7G???X͛)Sco>+޽{aD|ISSs߼ycoo$VZ!pdgIC C BBB]]] ,}6a)))&MlnnD"ҲQϿw^rrX,޴i] baaaoomjjjaaA$̸\D"lݺuݺu0Un )//Zlo߾3gW*~ VVVxRD>|xii)@CC !'a=Br8߃zxZpaRRRSS~7<< /^\|B^ e$"hee۷o߻wo{{{wx=Xsss+bEDD444(Y$k֬pܯJCC/rP(k׮MNNXXXd2MM͠ :./_jH}z닗#عsgXX@__&==ȑ#SL F pn3g@?D(.\PY'|>;w| ԩS233|L* $ 044 ڻwoll,ruu/onbbr9qsQQ,O^+P:t(3VPP`0 ԩSk&G<[⒛aXVVoI~~> PcV@8s笭/_.?ad2X!y楧_t)++z\tjѣG^rkה3O?˗/q 2gΜׯwuuʯņ(Q,VTTpJh5[?^PP5j(e[ikkϟ?ڵke/Me7o^llX,fXc(tFab]]]|rVIqkⲲjܿO8^222R|X`L6MMހ,~gr劂~if͂OF CkAAAppgRRRpP(* y֖Jʊ@ t\rBh-3Z[[.\!3g3fٲeǏx<)}Ǐssssrrrrrrssw-!a0P\\ -^ɓ'3fP駟FFF޻wz(Q'WUH+.\`2w]?|䉟]SWWΝ;aaaW^ QJWyKK ^SjG^K.lmm#+l`0zog__ߘDқ z|H$Ҽyrrr}х &O,M,R,G3!H1LX hʯ544\~```0ocJ8{m6m2KKKf2h,2k*K!H? K.U6 q8@]]T*e2jqFPPr?8//yfgg[^b ~PPЦMdKa \F*獵uΝ|K,Y Ksrr.] *D"iiidJvI8:o}I%ISSӌ3ڝ7n _ B"<<<?O?:uj„ 0o P(ͅ@ HUUUw&H Mŋ etrrJKKEEET*599ёN?~X*vtt߿De%Fxҥ;wZXX(W"<%%E,{yy1?Xe``G }ٵ7gL &' *߹sӧT*f}PǏ////(((//RÇvŪ7o@ox&&&!!!7n`2iiiSLx =deeUSS#A7 Dk35.#jy_6aB3iҤӧOc[m`gg5(wh6lP*C˗haJehr\.w@* Uϔ)S$IFFF M4ig8}4H$:<+IB2L(޸qVs=xNY,aUU'Mt~8|pHnڴN?I+d@DRZZjkk?]paڴiUUUOggYuu[a2;w"T*u׮]q{ʕH9GׯMMM']~ׯ_?{Oz_!Ctu%ۈYK`0|}}mmmܹӏ]\\h45!!ȑ#O^'dgggsssGGG[[#FXb֬Y*zzzQQQʧ[[|>O"{IOOwppxˌsXZZFEE+`mm=lذ>%a2>wĿf!>`P(?X%<ow<0LCCzTںGwgʔ)ʇ~Zۼ}D"Q4}T&+Wy<ޞ={_nnɓǍ$%%YYYihhmE"љ3g#""\]]VKIOO666[|d^tiĈD"q̘1|>_P(N*((ؿO?$o SQQW_رf1P )))cǎZJ"0??)//oٲe2 ð;;;_|Ғ'$$=zڵk'w8pѣG/d~ʪN777oݺUA+ cX6l055ݸqK]]B)~Ç|R,o޼YM{+WlmmUjhkkSrUHU]]/P-[ֶ|wpMAD:vXMM͊+nݺu/Z̙3UUUF4iBv}_&IVD"P#G=رc7o޼~@Ok))NC_X,a[ny&āwݫo~ڵS B $I`` 0~XpD"0,444..ðP\Zrre`?qw % F pر}Lr544*++1 Dޯ^0*00P,cҢZNw t$77wʕ0NMM… 1 ^^^wݰaaӦM1oܸ')mc7n~ٳgǟð-[|R2lڴi566N:U&-^LHHz^x M$=}ðqNڵka)))K.~$%a---=S_ea6z3g466^xG+7G]U 99ݽðӧOO6͛7}'NN677Dc?_IR(G徯2IHH|Waخ]N>碜JMS`ڴi0RQQuߑ8a}3RФ!bؾ}^GGA^^yݺu'O,../oAAA0xuWWW8F"\]]]uppSBt:QYZDDWsssssÇ{PtP=B,]cǎdIw0lccSQQQ^^~=333SSS&.MWWW>,o :t&9T%KVZ{o6եD"1<:Kgnnlٲ2dHo-VWWWGG;N544r @cc3fDGGɓ'=<#5 VSS_H%9rd}}=~͍@ ܽ{jjjJ"pe@?{, Wf ‡~_cmm~ʕ+&Lc`0aaa|Ut+WLJJR۬J7@*ӝ &x|USStt֐]xQIRT+}û a򵦲##f!!CfʊSBL0A|W  ttt7o,^SuttpP7|s=x QD"rJa5:ٔ暛 6 IDATcccz[ @vv7ol6@$uvv|W$~۷y<Ν;/ӧOOKK @k[[[yJɁɕM!|Kڵ+"";&΀R6lpðh"xq8deaa7〫'|>,TGG@ JSSӽ{>y$55^T ð3f 2~P_eZN?Pw-V89k׮q_ȑ##QKL|WpqqÊp[9UwGggg8M$h*;2oxZ[n2r`!s<`T*ٳޝcǎU~ fo߾mbbBC\,ߺuKCCC$}G)))?^"dgg===ZZZ=<<700j=\~e222RRR5|?NTJbccly 4tP gjiimٲŋ|>_[[{Μ9*Imf``/[JW^=yb蘙]pRԔĹs纸ۀO>ׇLGGŋDΝ+`<{B 6lb\\\r>k,--R.[XXhjjJRǎU^^d˯\" utt6o|##7oޔx˗/TN<)J>C777*Q755ijjxIgggbDEEb ooK$gggKKKA <==qr:::222fΜ |U~~Bv?V I`ŋ+ T~8dWJ9sTagg7Tu\\ٴiYwv}8%rr8bX[YpaTT $a_8q͛7$#rDqK4i@-R499tw. }@KKÇBP$%&&^,,Xidd|AudĻ-G T*J>|zGoϱcn߾M ;6 .$''߸qcu:2]& 4D Ŀ 4i'Ф!@ ĻY@ f!kÚ~R[ ""U)WZ(Qk"jWQQ[p`P" (d%$d<}4KP+wqy6ϝ3{Y0+S\.7..N(X4>?~E|JS+NEEťKZ.##Cwv;.\x[@|0(ݼysOOχdn۶-//OCCܹsi_.Ʒ/0###-- {+WBBB x<ކ D p:>z &G޺u+B͝6mڸq:xEEE+V/(()..ظq# ##|󍚚ZKKKee]l#1BI&_|wʑ#GfΜl2M߽{@ deex;w>}ȑ#~=bRRRcc!Cބ]vh48)>]RTX͛歭BðOΝ;WW^Ax֭L&B >|@ř,^~ ǧð7.\P 9r~AtxU-a ***Q( .4.]:qaW0666(x)/aNNNuuumٲ9y`x͚5[n9::\x`z!Uر#,, (U!L2euhxFN0]]]/_ ֭"  :;;O0?99mҏ(֭KHHᠠ ޿?p9M0KKK3ս>a%%%0>&&+!!MEEJXθq={ImFFF`` ̲vG˜ B???1%%%//ON+޽/'Β%K0  `#G*Pz̙ˆ#7mڴb &66Lte3o_~N&Z]#H$h1HܼyPVhh/Q6l?tܹsaxpyn߾󛛛UTTN.uuu8򤦦 o366f0}~ذaFFFKi4 0dȐŋ e;R"ʩS@>>>C*}bikkx|fժU+/zjGGDzeh4Zii"+CBBss󊊊rae9}3jlll۶ѣBo͛7l6[KK&VSS$H@eaor3f#555Ѱ&5e@d2r2p\]]?t߾}hhjj1p a ͍bX,gg۷oج^4'֭KLLtsssrrӊ޳hѢ@KKKqqׯ|5HTe4F6L&؛f{rr899UUUt$gΜ)++۱cǘ1c`+#5SZZL .]zڵ .899566:$& pttě)) LvppPRRwwwBazzz\\ܢED}=h˖-ǎK9H5bee؈&9Mc...k׮eR~9JjIR>w%ҥKaaawro޲eKrr޽{6,$/<N@@իWڴ璊5rLQ  7 ?̛7f :`ٲ$駟L~~~kkkss3_=uLs8+>}zjj-H),,s=6nݺd+CCC|;>>~ǎ):Y ,lڴ)%% ɓ/^0\@ Y=bpr燄=zd:::zzz֭`Dپ}{\\hy-???8+GcǎP(Ξ=߈ϟ?ommܿ_YY׷ECC:xsv,#E~ ۷oB{ᅈZXe'^]yy~CCCee%p88u|:855EJۡb%矩T*ŋ4Jdĉ322Zʪp>֖bAW%WYfɁ6s-[hkkK,=#o #--u=ӆLVh>"DjjjvE&l6@hhhˢ I 6LAA޽{ϟ?:u*>d0bĈ)Snoog^|fmmMӕ mmm|||d}+^^^555EEEEEE6l022*++iaaرcX+** B Y^zooo+++ QUU;wn|||uudက+VqĂ#GFGG7ɓ'Ǐ>|YNNŲniirww-ðӧOٳgذaF>}*eee^QQ`mmmccも `ٲ ebbcmmLLL,,,ԩSA -6//O[[իWFFFsRnnn@FLpYz/4hb1 ]5y8*}X(̙3R+4iRXX|} oɇ1N@```+h_ ֭[7Ђ| t~k4i`$~bNGGG^^^UU38AFFF{{;~`dhh"t<)SF D_illQUUhq:vfxZc@ ܾ}?֏5-}wl]]݁1@o4D ĠM 4i@ @n@ {Y@ f!  666<<}/Sl /^IB"!r{5k|Haޅ7վ}$nG f! &aر׸#Cuvvk .2S!'N  9]|yPPЇ]xc1@ }7ZC #RwaP$AnHJJ:~Gƍ`օ&&&bE]rĤ;%%*p|ׯWX!^)ɼ|=H3fLSS֭[L&B>|ӥb/^455r> 2DNY.annfMLL<==aÇ,--9̙3CCCg̘rD jժsFDDDFF>|JMMիW...8PnbbBG=i$\4MWWwҥŒa0wVRR:ydUU|||222mۦ/t20sL<oΝO>=rHmm)DbHH\fMeeejjo^__+!$yFr-@ >\fddDdǎaaa1C>a%%%0ۻ0%VǏ:0,###00Ƭ]6==ðPL)qsskll0?Xnawko0L '$$H1&&&((`ϯ ׬Y[=eaؾ}ۇW}:u'`$..ðgX L8-ԔfK6V/׃S]]aaaa[lL_SS &Lӣۛ666^ZZaسg%k,I<==]{=w#wd߼L6 xccch,H$vwwf122 ߽{wVVw}P(nnn,b9;;߾}[4S*puu!+!!aȐ!"hee%E a|Nhx__ߐS|}}a@II /Mt횚5 [XXd2D>}ٙb566ZZZkjj+UUU566"}$!.--LC&9Noz:::mmׯJKKqEA$0جY>} b& ˅DRQQ CSSDQbbqF%%txzzΙ3G4+WI&5א!C4:.N.2j777r.ӈ1 xMMMAub)%+ErC >}:}tUUU>/u@@@TT_-[/`ok67Լ@0,D_eqrrjlltP( 33SIIÏ?sqqx%:99y_bIrtt|%KL*dx&5j,IH$ 2ࠤ7{Μ9SVVcǎ1c@ߥ7e_Qlmm3r4D$Jꜝ@ >7jjj$ p;;;l6==uEFF0 y?TO:Öfff?3Jx񢼼fOM hooR++zHKKKPTT$(DGGpjjjKK pѢE;v6mڔ'OlllZGGGyy9^ "Ѱ@ : ]]]55gϞ-YDtN2ٳ?ިn|^sss\\ܰa <<<`dTT ;~@@@UUUee%@?~ff&\̴tR72f gϞ)++Ĉk,NMMMLL wtttttq3227n\BBϷzKΝ+zTk_gffH^m,I-,,^x0f;;;˽|ʕ+ltc ! ZQrf!bPܬ>A+*@@ f!@@ ^@n@ {Y@ f!@e;FK+))=?fzlvRRD{}4k-˧Odd1Cܾ}[OOo̘<ð&eeEP ƍ?www,ƍMMMs="x hb ٰaógτBaWWet:*iiiyر:\'""u)**N8~0̤7&pǐ__w}AAA}ھK ;6_7n0336ofssswիW555Pmm 6(**Q ]P @EEEYY ѣGXb,@bdd$5^(UUǿ77riii1...wʪOU*w'O \7̝;U#@?z{{>{yy.`BӃWfM2q={mXbf!JjIWT*F[t)`Νpj#55U(bӹsHoƿ\.7""ܜfxzzYZZr83gfggΘ1cʕ eժUs΍<|pSSSUUW]\\WP( 711G4ih.tҤǏ;99|OOOggg>]`ggҲ" jժ9rd̙˖-'$$ddd?~oQSSkiiܵkE{n%%'OVUU߿_OOǧ-##c۶md2Y˗sxQ#= f/^hjj*[#"".^xqk׮^ 022xӦMPT_~bŊ7oפ(T*uС{%HׯWSS۳gOgg~8@&Ϝ9S\\|a {(yvu޽ +Wtww?~<::'OtrrjАN:ejjZQQallbG%iꅅ$iĈ_`Ahh(Lߕ+WSSSnܸagg7qD :z[`95553f(((Wm6߈sƍSN500;w.|$ֹ&ye{{{"8f̘[2L 2|ӧ C.^XYYY%:t(XTTTNNΈ#`B(++gdd]@ff3s2s2_wuaJJJgΜya655e0!C0  ~~~iJJJ^^d111AAA0~~~eee0f͚| JJJL#1 ۷o߾}`ڵk/\SN=q ac``# '[cǎ0\˗/:tðggg\Exɐd]]]^^^\.&0l'''KU~vv_|NNNuuumٲEj7n\p@ 8rP(P(Z.]3i$& 5x{{gee]EEa۶m ####00|zzѣGsssa6g3gΰlY)_h"5_VibUUUfX&&&=~v>>>xǙ3gv؁aXfff@@]!M6xaÆ#F0 |r 6oO?˗/?vaK. 0lر|jiiYPP 7b1 \֭0ݻze`˗/ϟ/GX~/$O!ƀy]9hjӃk&H$@ TWW[.11 c߾} |0 !!aȐ!"(:/)U9s` ͍bX,gg۷oÕԧOhT֬YsE #g#41D" -(<<|YYY*uYFN֥YVVF  & MMM>|*:ohooohhԴdɒ(@7,YfK/#ޥKl68ΥRp0U*~\]!!!3g΄ass ֪7pl4}]C@HY  )Jrrthn tM]]N`˰a`MLNԄaXSSSSSSpp0>/)E\.rs\ѹN@ sd5G~]iXѪq3 #Uxɒ" CAA*sƍf )..1cƞ={njjjkk{lA~T`h422JLL|ƍO$sI?11J㏲KKKSRRzAdddddNBBB[[X2mmmUUն6cc㴴4*:acS,Xp6}ʕ%KTʛdXw$;WEˁ5D_~},Ŀw-[$''ݻ7&&F2˗/aX(€13'''ѽMd2AII޾[Vbe XzڵkᛸO\YY 5J 9hոA&mmmqjkkE5DDTaQT577|\t;,,Ν;evڳgϾzO_{6Wjgff*))>|VVVyyyoPWWwȑ ,\PV˗/oڴ\r(66>ŝuu-Y͛ׯ_tMRTFݻֆʒ+ Q:`8::I4#cJJJݻ'!orl6======Bĉ122g D9p> 70.ZepdǎM6'OslllYGGGyy9.l0,:;;;;;ǎP(%/Evvv5553\(//ohh$H:::p$ >o<6  : `Z$fZ蹲S`̙3pvڴiZZZ%%%"\uJD"?tӧhYTTa?3J2x񢼼FQTL M633Vɓ'k$lhhl>ݍ+LFGt< Y`A g?iɤ* b7 |pr"Ż)Nl ݝv-^:;:noO$w񯠇vQ]?nP~+?B}Cw! bw+j {)#i c;:ˡ_c>|ͿrsTZqa BƸ}vw"ńrwKVS) k@ 0p?mwX~[8L9 8'1)ѸݺOb ϷU# dFoTpe)yH *o_wK 󿾑&g"V7M[oZR^-_iꍫ +h$\Wmo je<P[rY<ݺZjCYeVP( ٓVqyo2">i$k{@Lk-[kh/ȯQZV&u/ a9K~[>^UnA"AvH zDb<6Gg1mƐ1nuqPB".o_GsϞ5[t̶;K^mw~ힴA~cﱤ&Q0ii?<sbu3(8?>GWs*0@Vg"$1WU(:h`E/sEhu D~e\|ό9ڏ.s8,G38;|+@"AV& Ϗ?SI߶pݿ%>o- ^k%qe֏+{=L ,8|[wYج4չy5KdD BZښBHa;CIKKU[J2Aj +Evh- 2Y\^$ ڸF(}=>:y Y$c"jrwTmih$"&kIPgzTjK4 (*ׯyZr|'`LiB2!=}Yd'%S.>*(Mp%8v#%e%b>9{T֐?raDJ/~*MJʴػ "rշnqJ ; ?a:ڪ) 5A9̖Fn9EpxB d=x0]GgYL誽{)ٓuI"qߌ#RdioZO`|HtX`DyWIby߯9d- ѻA4jNQDwڌ_wd@% 엪)|u ܾ QkEH^&M\f_9xֱ+hh0_=_;d2ο쥦&_wO米|ȋ)Gzض2.o S7qrnVj `g,ibI~H13WXMag}hX oo6HXH㝨 k]U_;G>i׏A r\qhG+c0=9;wffq׷؍3 +2FP˴ޤA ֔}[sKyA+*@_@oUWނ嶹ycN" 4D hE P@ O ::ڪlq@ @ˀ@  z~ 4/JIENDB`apvlv-0.7.0-alpha/ScreenShots/apvlv-0.4.0-annot-comment.png0000644000175100017510000027160614726051136023602 0ustar atzlinuxatzlinuxPNG  IHDR!ma zTXtRaw profile type exifxڝa#)s= p`vMUOϼٵ_]DHP/^"R[סyQF|ukR+}c?>FzZ?ׇ|O}ÿYYGo|/p[VVn9#~#⫉K2yR]Vވ(#z.ԘF>ǟ ;ߎs> ˓,Wu+1~U yS+oI-\rN'.8;_YXpRf%(yC<}M\9?!?8*${D*|ɻumxW4?H*'̧X_&wzktB*cgZKQfZ9\JL͚W*Jwy QE'7^ϓW OJΐRg $KrOS:n-Zo&_"SX*kmwɨ:^y{ͺv))d4oV3;6zjչ^K88f!uO./hݶѸe>iցT۫E32dg[0.bW_qv9'^N>b-ISJhK؁n_!,nͽnTxtIξx6{UgUbH:쵼pjTXwqս.!8]eigNVʝk_\!61Yw1VbG(B#sviJ@użvYs5a')=>Allui%=K_ SVUW[kŽ$1.-q.~R-SqSR;% 4w X<( aLjmJ<;%n g<wI Ytdʄ՚IiMCuW.YW[, aƽd^n #%jbi *qk,'@h3UL06%wA1HM2[Y7+kc}o03F6Vi{V]aPLD!2Lfb|̍*=Ȣ햫6C5Fv1A%[~]Pr*,[iT:dnvXUG:l*vMD-w{&ݷj)FUJ+$?(nF~ *ޭ>T&GSqOc8ف#_RH5Lo]u=k|fv 8jAOI@@ȆnL5ŷ^]F[ -&02G:cF;Yȉб}uI kaA&pX66!ʐ͘垀uWȡLV s# mQ6gqv.镧Ӓ LeLŠ]OFrÎaX຃#ض}OR%(-7^Z]ko*k ޞ] ޭc_HG50 ؉7Dh޵Z6H HcL*b}Z)+cqs=QD ׋F`U,ZQٶ'$ _\ '!Md  \n \;{ ܍p^䌴K%PIٍ3zKmO"klhJdgٰGW9::T]R9F:500@ pyd,7,Q!K>6 5SGd!XL1h1=CRTxF@:|2 Fl:xavb*7Gnq, 9`W|i`FaPB *p "SI3#Mr䀂l@Eoxh7[3O*̓)xZ0A { R 5(mag17 0x^:in[%-L;:!*] P|,HB NJb7oNsD{|4FB` 5gQkŜN8pY&m:1'< ĠMSd}Յ?w@̌:xf揻 Ė#(NZ ȐF=hO?c#_KR\'|^TD[J`K8pKcxGɄa .})2HAN3 OfLdFL 1ÇDoSL 94Zjo 'OzC5&ɘn6جaޟf m0Hh'D!A*Q=&ʬ_^%c"P8R0|,} yz1X::#$տ6⽊}e+cAlH]ͳ;^ñ)bvwxb]Jfk=ctQ#,96sBITO IDATxwTW𻅥K! D"X]cCK`w@ *X 4ADz߾;Y`)1O7ya.oTSL&Nkl$1rBP=`@:}P?͘1*))1>}ڡtE5y(OX}BefڵK?xpP(dUU5cT*y T* Bee%PdȅZ*WgRJ ŀB}w7o\@ |>~ B> BMry$Q[\GB&MqOOpB{D^NM.-*B!!޹s&LX#^Nn**fflK͏`[2ߊXJJ c3G˓'MB! w&GqL< HR?C1YN]tw6U`8;'4PLDۍpa̱ڴQߝ_ ֈkOT9!& í(}r!7 6Hs%'tQ1 S_s EȚ 2DmKiɗiJ;FLON5%'$CaU*>r>!зJ9Zvq'._oA| 'PZI4'=sZ!Z dH Yd|r=V׮$?frk5~M^P3#k/T4]!&B|Q%^ϱėˊx̯\' *@'(T_0Z`ӨV4P4nU@QϵmDΉ,7vB}> <|fXl6W"sϏX@_o W9ASh}yG=F]Nsc>Š9yn%!/Z|BQ505RעSÆ\iA !/wmNiQI7DlF[MqZwT:]^O#6NƝc^xp妀'mj+wΰoGm&}_Р;c7ex!į)~ǦKfc@e|3~ ~i]f:jꔖwoy@cSk!dM.͉*^rHmɖ?d1Em}f&1E):;_ WYrCu)J ?n>}$V.m`YpUU=TuÉnk=,H?,fRC; N:^pl.ߢ6tQO70/M,ɚ>{OХɱv]q${OLLPB}ŷTA⟬7ɉBY=Bry:: Jy+StRQPj_~|W#`X,~YyK^2L.hK ҹ6Dܾu?hԟN]Rn^{7>%["_~9ޓ RSisУU-߾Pc=|C7>sZ2S%#J &^_ >-..WPWWWmf؜䆔:= ট/FM-pj!C)K uuF6r+bQL=%hTZLSr)3BWUWUU4 U^EE]]@SR);'Ie=\1oX@?MIIuj{3\c@jtTNJEeuuWU@S!UMU3B {q?ryTF#,'鴵kľ)RFvèOngGaz!h)42^w"aEUcmoPt(ѫQé]Lh]*&M|!Bl2*Emj?&֤wg(S]>wS!`?ME hj6N $ϕu[۽ qk1kh|^¨Qֲ/+竷`2~Nn15(rFEs42d8:Bmkd7@A["|uR$) #5{t6JFVL #6;CAk6դQGjƌKP-Fzu(Dh* )Pv8/y7iyy7-js×IP98veR5y9a9AhFҔ>G#]<Fcz}04^aEڇMns>G>$fĻv8;/+1SYqs%BF'.=.'!v$wbmKǎ:_!BǏ%̯ B .[񵻁B!]w>_EB}X̺݅oI_C!>`B!${c!BX!B9B!aB!${c!BX!B9B!aB!${c!BX!B9B!aB!${c!BX!B9B!aB!${c!BX!B9B!aB!${c!BX!B9B!aB!${c!Bkw}W޽{׭[7999F x:u[jւ d|i̘1fff/^md 6tccccnno߾/bG\~ʕ+vwB[XH6 F}^rE&+**~QFѣGϟ?~3g|||뗝8o߾]dI/z{{$%%ݺuݻsÇt2~bjÇIsxs,^x՟>}r2 B8p ٳutt9BPzԮ]O-ZtSN ۴isϟ73N\\ɬ_mmm-^bggchh(۩8?ST2k֬Qɇd\c'Ljjj2Bhp FrrY%uuI& B wM}} ߻zfƩٿWJRٳg8m>@``xY(-[dVc'BKy,$b]/a߿ N) 8pŊ%'Nѣǚ5k}||vؑYSSǏXnݹsΟ?{Apqq)))a2d;vxuMMWJJJ\\\BB˩Կ~KyիWTj]]ݘ1cױcv`ݺu?ӠAN8񮮮JJJdo]\\ >>>rrr555 ]]]kjj7vXOOO555'O4xd2O8[PᡯOv +WDEE)((BXH6&O,|Nb}H޽/^MD+DqB///uNf@QQ9.\F-[,>>޽{jjjd]]]]]]rm۶***M122j߾(lB;;; gbbb Νri RR̳g^zuJJJPTT{nQ'S {@dr:B̚5^|tǏFaÆ7vׯ^n6}!8vXhhի;vڵ7o^s!B2>y󣙙eeeYݻw4\$ wڥcZii)$''Ot`XK2խ!L1cƌ3x<˗/^zر={yf.+_&O]vsN)M@VVVuu5Y Z^?B!0Bt޽cǎxCΝ[v:t7 w% }||.]ھ}LEEw000 $%%uI"@  xq\:.ZbU_ci۷o۴iܹsoݺջworN1/^l۶- 0`eee]tfx<ݻ>hѢ#Gvرc5UUU+:qrvP___OOCrUee%D"xLHHwPP6 $g>ˀ^~}-Q<PTeee*jddQVVflloz5No,߼yׯ[ɈSNmذB^˗RWWWʊFߣ${fgΜ ^^^[nuww?x𠃃rL'q䄜r_1d^շo_hl%B Ƴg&L秩}~KD/ݻw,X@&d˅Bo߾r}1 #O|݊GUu`ڵ~~~d9AdC!$PyqݙoR?,P#j ??>?q℗F311;vӧE۷oرC[[{&L5kk˖->}zmm-`X3gΌ=ztΝn GNHH6l۷׭[XPPзo5k(**ݻ711QOOٳݻwO<;v˗={~>,sŋ8zQaae^~࠭=t+WܸqԩSk֬TTT0`ncc#jڵkWYYihhi&rW||իz]^^^WW۶mիWn"Q,f·$U"X9W!rrr Fǎ,xΠ8.[WWWOuuuݷ&lSSSyyy5xTVViӆJQXXX]]mddT}P(ԔXSS#}U}8AUUUa]]RRRRTTl^$BG9V`zc!һw|B29VJo!Y 7lذzJssssN'''aZ!~L8J8ĕk|ΠAD!8"8lhkkO4k!>!BH0B!=̱B!ds,B! !BH0B!=̱B!ds,B! !BH0B!=̱B!ds,B! !BH0B!=̱B!ds,B! @ ]@XHf233\rݒ5߾}ꆶm6gsss;;E%''"HeeeuJKKtuuٳݻwׯ_Kt˗gK-_Ҳj_UUU]pa 8sL# w s,$6mo߾}HHŋݴis޽޽궶lٲzꤤ$55s۷A,Yp>ydqq1#ni𲲲VE)S&Ox⼼j:uŋkǏ߿omB{9;wl[[cǎ-^DuLn4&Ҝ Cf_pauumK>|400(**222jϿ~P(Ckw}FmllL433{]nnx5/с贴6mL4)***33ֶG/^())=:99yeeerrr?3٫T>oaa1lذ&p8Çٳ'HR~~~:A4y }!,,xԨQT*ƍRVV6o޼Ǝ}qYY/booݫWZv}''?vvv/RǎИ;w.X={9R|(6o8d:wܯڡC&!W8d`#FkѢE cǎLmr L>} qqq˗/8p`:::999...v0gg碢"z;0 .,Y|}R!CHƶo߮Ԝjbaaabb?~x!Cp88 WPP8y򤞞> :tذarrr`iizj===ɓ'_|Nz'Ovqq=zӧ~p99SڿLL֭[UTTz5{˗H9 UUU-6mGZZڎ;X<ɼs玦1cիu~kגN2E(7Ǝ+O&Lr޽;hРvI@adիqqq|>_n]KKKWWȩ). .../^SSS;z|veڴi;vXvmffP(ޜ5k6obUTTɗm۶e2RNABRRRuuNkhhDDD4u ׿pٳgd:%ȈN#yfee'OTTT+O2n޼ɓ*%Bp=;w$&&h={?W^  Fd^"AEE%///:::::СCΝ[rh+**̶lr̙XXX۷/00Pz[A1ܾ}[GGŋuD ۴iSUUry>9|SЦMсdOJKKw$rʊb3GWWWy&yqB}p Fpp%KNxaCCCpss%|o޼>| .]JJLLOO!CXXX  IDATJ(J 6sLr>)//OT~pqNNNB0%%|ܣG4zݷo_S233l6I Q"˗/ @0EMMiC..h9oǏ\YYK.URR铫'O233^xܥK6cǎ,޽ʪ=zlڴ);;{/_LHHHJJYv~dddBBBffʕ+TB```QQQjjeddg 2THHȍ7ʪMQF9:: J+++;8p ++ϯǧm۶.\hȕ"T*uر>|QBB™3g֬YxⲲ2F' a $ׯ.ZΝ;IIIL&ݻ־M___ӧOB??)SXYYq|}}9Ύ;$fB>-)-* >_?E%,ʺ: $X,Ѻ(TRRjQA)**~]TPbeQtvvtҍ7* a!,fB_%z,#9PKH/܈J6׶m/!~c!oȐ!YYYR}B0B߂ ,X{B^!B!aB!${c!BX!B9B!aB!${c!BX!B9B!aB!${c!BX!B9B!adǭ;|999RKرԩS7o xPQQo>wwO*{c]E0BoMʕ+O>MfHо}ŋ2dٳ'TFLn+++I={422Ҩk׮5YѣG (A]]uJSgggr;###))IKKKkJJJΝ;+ۓ8gݻGg_LPs`d&== KKKT&..lh*jkkKJJD/+++E/KKKkjj$bX nWVV*f%>}fsQFuI޾M6%{IIIrJc|={9sիW%v5x߿)ćx{{oK?V@ hpjA I66rrr>,ӧO  #BRW69}hZJ\.YYP42S A Nh4QB@T.' E>_KJJ$/1!IyGlZGWzWE8^x<]B[-ad{ԩ0uT˼ۘ1c͛bddU]]ݣG\h>}:vXˡC7A=UUU/޽;p;v|!==A7o\|9:zheeeXX؊+$קOB99 6(++?~+..nH-22rȐ!f <,//vZ~~sʞ7nڳgOVV\pÇ>>>uݻw۷oʺ}NJsN```uuKN<)7(((111""bU\ѣpOzzz&''AT*N8_TTzsB<ƍaaa/_&G76V?~6mڤIy||-[sssWg̘QSSd2,XPUU%Qyzyy~E]]۷o]xq…/^ؿ͛%..qРAlNNΔ)SƏ)))-0frZ1<<{uޝ?WOIIuVffĜ+sss344 _/^HJJJsss׭[#GBCC\R?#li)]˖-ܹ֭[}{3g;~ʕ+W ^zԩS...R?.!&"!&YW@$ ߼yCD~~>B w/W&L>}ܹA8::;w OARRYIċ/w^9UUUrcǎADDDlڴ,x"A^^^d}||o"^G\۶m/\ 4]Fn,Ydڴi%AuU~ii۷vYro߾$ܼysvMM593dVVV/7o/؉'> GGj2Dڵk^^Av:y$AСC{%7nx̙! GAnǿzJ|/WTTdX߽{WX%$$=>z(zii9yA7.((>>}~# /_, &q^NNN'G{ F-cddtQmmmPXLD6ۓoߞ3gNhaaa3f ۷sNr{ժUϟ'zMMͤ$ 󍍍 r={"fӧ1 9s&Yjk%J4G|>K.EEEA z)}gTFFˆ 6v?~%D<}jjjv(ʲe˞={#c 277ر21cy9o޼h4rNw>|8yn޼`0BBBBBBh4۷o'L4uӧOϘ1C"f^oY{s]܁?~MB./^yzzzzzI.y{{4VQQ!uRRRKr6yI-Z$6&&BԔ<)rF6))dx"$$$**N˗/:TԺjm5c/F͛7]#Rm۶%ɏݻw55558ӉPad{3gO NW\E͛7W \~}Μ9R5ARݫX]]p7bĈ#F_~֭]v}ԩS={FO7rȈKCC˗!CHTVUU=wmo^bҧO[X˗/_̙3O}:??sЌw1 ՗7xJJ$d>:o Ǥ9D9q.\ˋ\X&~9p N8GZs,$l6[lo[n9eˡ`L>ի_^^ԩS&&&[#^"K"""RRRƌsy%''ۋ@RRg6mI2@ 5''Ç<OB(aر.]*++?+.._~/įIq㆔ B^!rrrlmmSRRD ɶ yjŢU)))vvvر"G122Zt%I榤D >Ļ'^pN>mee%2LOjjjСC$R?̙3,cϋNڊSWWWiAb'O {w`s4Vػss2jzVLlmm322DG5B_&?ڡCSS͛7K$ϟ1B1B|V[( ݻw---D%ꎎ˖-8qbPPжmyٳgܸq ~oܹ7onF! 6 ;; ?~P(}Y&===))޽{WEEӳ\7Zyy?ʕ+Dѭ%t :::rrr>>>֭#?zHWWWEEE޽[bELLLUUYRPPsرXOJgff&%%z*%%ٳ䝣aÆEEEƚ:thΝ</==˗YYY޽ lԮ]|--W^Iۢ䴴4rnС111rrr#K^Ჳ$޽{BŋyyyǪ6:::??|TXrrrRR҇'~]TT,H}ӧ9RIQ\\\YY߹soDEEUUU~ӧL&3&&ӧ<͛799Yfnݺ1LUQQQRR`@ *RRݻwnn._pPٳ'OX8eeӧOoܸ榦clٲ >-#ۦM7o>iҥYYYDz/_|q8p7A~ B$?0c;-QTRn#++KNN!RPPPXXؿgggSSS;;[[[MMMQݻXr*_|iee%Z#ㅄP([[ۛ7ojkk+))={L(8KDtttUUuttUTT Pillܷoߺ:eee@(~yy@aaÇU:ݻ"PhbbB. "K(@ hO\db"## v*:kNt ,,,4h(lRRƵ4-11cǎ?{k===&R(N: ޾}9f47//ӧO&&&~III)66M6VVVR> E]UUUIIǏ EWW|VYZZZaaa߾}322,--۷od|}}gϞ-y1Lr)Ґ!C ˗/[3;;[( }&''h4>?`dnn^cÆ HlYYYLLATTLϳgN<)ZٵkWknnNP%%%111CMXt:盙%%%t@@xBWWҒ/1&@y<;,t{)Ǐo޼>|l!Cdddywކ ?,ǀĥJ0j>2ruuA"""4K/ή133uɓ?'ӧOtիPcL&3<<|̘1k rr>j&̱Z$Ubs"(,,\(~frLA~kkkcbb[],ʕ+n'--ɓ6m"ЗXX[_#}***N?+Gx<yOyy,ohwB (/L .$?З!BX!B9B!aB!${c!BX!B9B!aB!${c!BX!B9B!aB!${c!BX!BG@ߏD555##ׯO6EǦ^vaȐ! V2e?7nVK痖tv5sQ˗(SnݚaYyLթ|. d 籐̜;w...`ǎ-=m۶UԩSaa;w ccc[=zDEE5(@ *111߿FFFhhh3;Bǁ9NѢJKK[AGGgذaR*tҥw'L__Ehjjh4Q z`;Bǁ9'O̙3'::СCo߾ݲeK5󍌌ufaa1h .+PSScjj1p@P܌|{pvvvuu-,,$k޾};==]EEbI:BG9w޵m۶.++K[[K|x…ڵȸvZ+̙3us%|M " 9Aٜhԕ+W4M8ݻw-?ísK  5HE̲Ok2r*O$E{eB;BGCCC |ZcՊ߿rss#""TUU<~[[۔ɓ'ݻw߾}ƍ|-˽ل N>f%%;wgffFEE-ZO>R:LR###Νf?~H[۷+V(--}铇ƾ}TgFFUTTRRRgϞXe>BǦXkY6YY =jUH x@}g0BpB #={ Fǎ]֣Gcǎ)((X:t;vTVV~ELLQ۶mdԩ@PtԩSLB6d`` ûvRSSKNNCTW?p̙d###*:uT6M6@R>@> а\Y/!m^4PyqbAsg IDAT~\&kBxNr:5nڒlcGe_^wz3$ 6[v?cwfu}^DDQ֢@\{]sa߾ԸGcLWlLo7_w']TᆵTm+QkR*K:lFQ'`}݄o<׏7R*]D<9VbHm% 4Fޞ4$B? ,T?ɽiAkyGq,揵~3JX}{Z#M\ bפ_MvlU|oc3|ʺ}MkzSy_eԄ._=wotsvCAOp?#8xI} vNZ`]{aE-X'iE/Y&LoĶN +N2D!7>F>mρ-Y n*uI g0>:VBcS಩i@:##.蔴P% [.2L t:ǚW nLS*xV7>OVVw]p@DphH36LE1P3+ 2O?A鵵ŚP j5['C*Bih48rVܸӟ# Pzƚ@}p ֭[ 'XRR ovsh%x+y3~ށ+6Yi1< —}' tz5R@00hۖjЯ<v hťU+'odk1LifZYu974E:pîyG#Oھքڮe"!} ЅgtSvm>=})irQ&(mס95%R\& t~hRۮaYPx tA7YH|wX{4W 'N3귆3K B9B? p!=%D ν5(o]e- ^X4DjI4KJDpjQ*YRS(S^P#Yy l;7KV{y-Ḇ&)+QL`~[UѣBW^[t>#\M^*Yr%}lMڡ}n~a m҇TbFj?pِ ݎz6D *r--Bs,I}M.VTbIufίxYk{*/[OIaAf3s{٩]i%;fv5kF{괨KJEۿ3߱d}tNE$$?s'k-c^pȖ vOvR(v0R#z0B2sH>/έjkk+Olݺuذa_n~̲Y k@ 5fĈee ,񱱱}vK;KkˆH%S ZZ)X~`{ 888t4h&+Sp+vsB* жn;0pE\m8{g0|({4߽;|܂=ǍCР9ڥ^ 0lٌa<<~Jr|tHQ0 hSTBkg>>ggg̙qq>rIgʔ)k׮61uvv>}zF ˽|ED%uuu|>ї.]ѣD}@pe˖֝%`S?A"XHrssCBBڷos.p 3^*!!q۷TXEEĉɓ'geePԇ"RRR>hpp0p8̙3Bҍ~t~`choo5jAC]ʕ+#F455533C]~}Ν6mrss[xqbbbO5())iii=_srrzرcp &M277/**4iRgg'Ǜ0ayjjԩS=zڳgOaaq)O>c4B`Ƈ b>_b'>H1}JnnIT`xp@ b{nݻzt|`>V?|,_'p @ ?ȱr, MXXX||<ݸq'm( իWc@%%%qqqUV999|V1`0111/_LLL舎oDrjkkMMM544*++m%$$rssGI&Byyy$ !Db,+//!2dPgggff&Ͷ7:tr, uuu'55U^^`(9gΜѣG<ۻΝ;?ddd&N{ɓ'/Z(??ѣGSvv Z[[WXO?1Looo---))udjjj`` ?))իW159s?ͦh %deeRDD[[ @ N0O`;H<ŇKD X9ݻwv=У  c|>LX9A ~cX@l\ƍRa}}=K9֖ʼnB%%x`w  #&&˗|Mג</77W__ܼN#xU]]../''G"p$ĄF!X,֫WLMMž9b*//`0-YUUW]]mnn^WWGSRR]{n{{ xԩww ʆT Lk p8ZZZ۶m[tiO%W&gee円 6M8 < >@YXX$&&888_W2|'O"W\9mڴu5H ģt:IIIT*5--¢666t:TFFFZZ".^`0jjjqbNOʢBc@ 777((L&߸q#55UAAARRRD%--?MLL٣_^^k׮gϞ9sfŊ!999:鉯zzz1S ]!;;;__3f\zUBB۷ok%~~~eee'N JKKC 4&-->***,,L>>j( *%%S3gΨ0߼y3%%%>>㮮C 촶Zbō7޼ycggѣzgg,]˫V255%Æ 駟s  A455)((9B"z`!BYgcX@ vMhwa?O0 WcInf*?ȱr, @ br7n( W^U1YfxJk׮M4iҤI'Nx&Mrvvf2:ȱ>>vvv4mŋLB&MZr%BHtrc1477xCmۦdɒnKjjjΘ1dٳGYYaÆ YYY'N3g9׬Yjoo?|y}ɞ={]\\wٳ3gάX!$''G===qn9bĈ~-%%%`lٲ<777,,l7o*4vX~WwƌW^~ZIffA{{{YY'B IKK KG"##}}}9ٲe^~􆆆N&9uT|⭧籀5 Cv=?WZZzy7o?~uȐ!^^^+Vqƛ7o=zT__|K^|yժUd2yذa?SO-vvv7444(66vO>ػwoSSӘ1ch4ڐ!C f?+7omm6 #Gכ_<oҤIEEESN<|_Δ7B`$t? &ӿp8AHHH+G&\n{{{:::h40|Xm—$73cDS#H)F\_X9ݻwg`^?`>V?|,_'p @ ?ȱr, @ ?ȱr, @ ?ȱr, .[QQQRRbX%%%| iӦgϞڎٿP555}qfff~}x<vڵ|rPvvc3gάEFE۶m{R[[k``ى7B AwWr, FFF⭳ʕ+7T///kk랞=uTjSRR255k ?.((Q@KKkʕx{̘1^^^hffft:?!((hܹ;\'O䬬,kkk+++6f͚1chiifW:%K΄OqߡMܜާǾ7l0e)` k.jEӛF{ri___eeޓ/!RSSy<ސ!C >cǎ& J@˗>uvv{+DSUU6q`***=bΜ9G)&&<""t;w1F͙3͛7?Zdɳg^zdeeůܹs^^^jjjݻ7oJJJΝ;W0ΧO&$$ <<<ƌ̌r&Lpss0hpuKKKJJ BhҤI Ν_`A@@@rrrC}|||pʔ)ӧOrsν}{zzz#F8{lSSWyyli^|y^ZWWw![[ۥKΞ=[i 33-[Z 'X555?_xb|D0foocǎ!bbwd IDATb<<DR.]p> USS@'Mc<& EJJ_~1cFpp'OnܸQt=<|8ed2YAA!$%%ۿ$Bڵk]eee-3bݻw/66YۆtuuqpGHR544)&((!DnKrǏo޼?>|8|>fVVV'O7oBpٲe.]ZlYPP+WkɎ;;x5k֬Z̄Yf\2>>شiӧq"r,IP(nTYYYQSNEGGs8#BW\\ꪭ=ztOM0nCUTTؿL&MRRR)17oVQQ_oW^MIIkk~rRWWg 422]Cgaaxb͟?_|ggÇiiiǏǏlj˗/NSuqqYvmkk+ހX``l߾Jݻ!$!!pLL15Fol>}zmPaa?O""SU֭[쎖֛7olllBo޼BDDDj޼yqqqܘ` cp8E8N EGGG555|ץmmmM6ݹs?[\? Cn`111'NP(ATVVr8>>/..>={Ο?ᅴyyy8qѣ?F{q+T[UIљrO!ϗD.3!9_rX~{`l>LXqRRRD o۶ST ?ȱr, @ ?ȱa<㵷wVTT/gVTTokk+..WS|KtvvVVVN &$$,88՞u_~eC稪j׮]˗/O8JKK1cݻw{_A" %t{mQ}oÇϞ=[u90-rrr8qORBϟ??<ѡSN t#--+W2DWW!4qDww>Un:II?!d99C7:|Çv>u+YYYVVV&&&6gaa8=z4VWC yu~o ׋BX``H$X[[;d YY >S托Wqĉ#F".\`jjyf16mZfMSSӿ#GNzJN\XXѣ񶞞^dddOQihh?}g?ݷD_|>7o,**qFVVܹsΔNGGGk׮555ٙl6b۫0ׯ,[,;;ݻC3gP+qqqA(**{IWW7;;!gffVWWsΧO>}ZBBbɒ%Bx񢮮SUUҥKW 555̜7o4|N %xC\2))ݻwd2۷)))K,8EDDh4&KRhii0aFTTdKK B@eddp…t:]Ęۗ200PMM_pÇ 믿$$$$%%'O,T۷o|ѣGCP(=g.^H{dd$N5kaMM˗>{lE>}4!!!!xFCN:Z[[;::Ə֭ѣGKaaal6322)`wGG߿~eԨQwȰ|QAAԩS_xQ[[;}t Wjjj>}tkAAAvvvooo_d F멆gϞ3<''gݺu6666==L&Ϛ5K>]&H%%%]xݓbٞT*5>>ӧ&Lo>|1nܸUVeee}̌?i;\̚5kС^^^ !dee"##YYY2lbbrVS[ GnB*++wѣׯw\.^^^JJJϞ= {nLL'dddVZ5{ÇL2}t.;w۷o߻w<""HKKԩSrrr999 ~x+BH(&-Z$$$L<'33V[[{ݺȕ?H'O<|p<;j߾}nnnmmmW^rpppss{ B(***99GĘtc/ȑ#o߾-ݻwӦMr]+3gнwGiӦ#))e˖iӦhTGGGjpppff]FF+k?~Ȩ! cu[ñ+--?~qNNB(++YZZZCCcڴiiii=o7nTjZZĉ###S˖-077?v؁Bׯ_?~|iiiGG]qq1Lnmm>}֭[O:i&tTNNΪU[pBaaa+VPQQٺukTUU+**zjFIII M?kFA~~~VVVcǎ]`AMMM/_D}6;;ѣOOOS~gڴi=]ttzE\[k  7n?JLL*fuuu %ؘ_ 88x͚5A$''jڵ'AG;-,,JJJvYYȑ# rƌe ,%6$$_ɓ'_&&&߿z YYY&IDcc ۉ'Q^^.AGGihhhccc}}}mmӧϟ| ^|O1鶏|] .DEE=!!!>>>xQcƌxx ϫ1k֬!𶟟_ppÇ)P  _fϞpŋƍ]\\=zL =AAA~-722?loo700òpǏ/^ğ N100࿫{ ֭[rrreeeF2pU p|[n["""bѢEw!;7nx aaaQ\\LS\\ޙ߄'/߫qp|$%%gΜ|r<5JJJx.Ԯn RRRm1x`~U_noo up򊋋_)\SRRUTT233曮GQTeeeB4h)BPrrrq_B[l ܹs%K삃.]ZYY9cƌƓ'OXYYegg:uؘp8IInǤ>~ekk"H~lٲ1cgC*++ǏOKK:uF񆽽}zzܹsmVXX(//_WWڊz򥓓Ssn:T]]}m %%%MMM %??_OO~UYYٹsBmmmG"/v텁A[[[kkkZZڸqN 33l;;;.9sokiikT*uܸq_6;;;kiiwAGG'55իWCߞpO?E|T?JQ}9,[3663g644hjjtZ%%%P)))|F566/߆III:޽koo744i4aB3WpףzIOOٙA\.Ç\.wӦMAAA{ݻwo~~)Sf̘}v*w^1驏=a'''}}}==8=jܹ/_ۃ.++cRRR7oΚ>*,,2dB~2e B&[QQ,--=`cc''u[ĉ׭[ǟC^P/Fss3ÑҺy&۷o0Q`"QpppPP`}fb ÇGYUUX` )c`` EEE~VV~6<<RRUU&$$lzL&255෻~0}Ν 6 (TgggaaLM6ݹso'''}kB׬Y?$//oȐ!"Ky+Yx@Ѝ7l6^JΙ3L&߿?77!dhh8~xԚ\mRRRcm{'?;>`Yf'4ѣG~wMJJg͚5uuuWTT̙33L<OP(mmm޽p8ΕGill|Acǎ9wܜ9sjjjzP۷o?{?ϟ??55yÆ [ :::&&_bB.\hhhسgϰaYs犊rrr6lذfرc 5jAuuuL&^DMMMl6d . -&ð0~nSs, ͚5 S3k֬t)JKKÿqQSSkhh?f͚h3++_~ݽ( Fg"T,h4"9s͛7wɩg%FuʄGGG<沲2< 8Z[[Ϝ9իcN0!###???;;}͚5` 8#teܸqYYYVVV%%%UUUrrrZZZyyy$I[[[YY%%%:JCx<1|ӧOs8*}}3gδlKKiӦݺu2;;RUUիW .,//ohh(((0aQtt4nnnvvvP(]Ǥk{.((*))ȔUUUikk%''gddzyyK~c&Y]]=i$iii)))|533s/_k'N̚5kǎ&vر IZ>>>/..>ׯ_ʖy&""b֬YNNN]k~uDDٳgo޼訠`hhhll|ӧOkkk>|XAR333Ϟ=eD&O~#G_""""lll.^;v"tuu+''JJJ,\֭[aaa`ϟ766w>7o&$$\vѣ666=5k׮cǎ566fdd7nРA$ 縂pNNNW^}'On݊OL%$$X55XCCÄJCCrsssGGǢ⪪֘7vئk׮͘1OWObAU$)wo2i9|I)'uYf}L իWC \]]ܹ#'''H1OOOWPPښQQQIJJlvff&bII ƞ~&z@xOqqqyś7o&MU .HIIQ(?+_Kf;;A}͛7ڵ _-ܹgm6хn݊WǠ*B`xzzVWW{{{閑cllm6oof.u9yyO KK>\+'VW D? ` \+V}  7A ~cO͛7gbJJJ۽9 / Ĉ gZ[[wQiib3gάbX{ ?>{,F?Lcc̙3eeeO>{chnnHR7m/# IDAT4dw1^|yO:u;5?D'"3˫lllVp eÆ ^ˋF333\A?{&22tܸq739}/W"sqt}!>=nbq>wD?Bڵ /wX7FTQQXV^mmmrmmmvMJJz͢EوhL&ח Ǐ\2)))77W$_DDDee%Ǜ?>B B͝;WVV޽{RRRl6ӓJ?}t„ |Ç=<<ƍ'TC{ۛL&}6%%eɒ%222jjj! y}˝;w۷ݻgnn.Jד'O޿Dmʔ)SL5kɤ3gqEEE|i+~babbNDOt:]]]=''Js8b+?z߫Æ KMMۃLz[%%%gΜDܞ.\rcbb\\\=P,@~?͛UTT.]zeX@bbbz}^KK پmgϞutt??Ѐw~?? ;}6כC޽{nhh؛۷oR{EIHH ***455,Yxb/Zcc#NYTTT$%%{Ľ{ݻ7??ʔ)3fꦌ˗/UTTLfQQO1===555Z[[D">RSSS%=4MSSxذaBh4!@Ve˖}wƽ|[TQQgD?ʳ<==͛8eʔ̾|<uuu}$3 000Oֹqd|}} p֬Y96gZCC.nϩ5 /1bСC t=BHIIuvv]B= Bx9%sssjt;_ׇܹaÆnC߿/?D" u355UKK ૢ$&&v-u/-o\xqƍ`ለgggn+Autt7mt399۷8Nmzm_c3<<_5J h`` EEŞ"EЊ+l"za \^^~ ·~K&}}ƍxzzӧE g`4{-BHuK>MbC?y򤬬B^^ȑ#<` >555Ǐ6mڭ[*++---{*ٵ VSSh"|)DJJի߿֭[FFF ,+''GMM-66PKKKC 6pϜ9f "##cddTTTtڵϟ=zܼ---aaaÇЈp8cƌBhhhnnneeaxxxiiiyyett4nnnvvv 2),`B*PQQ0aBFFF~~~vvv{{5k.{S n;%--АQPP0a`7uuu *+++++]]]MMMLfTTTmm-vOϟGGG3LEEłϟWWWkiiYYYiiiEFF{yyu&hhhXZZ uuuО'7kll|7o''ף)&&& X,Y~~~zz:\Oq3 3͟B&#-:/4]fC222Xm—$73/ڵkSRR^zHVV?A|PsssssGS%'Nt;\/D _.VOpߤS[[ğhoRVV1bĕ+W FBBBss{E TWW/--SPP]8&&F .]ѧJ>mmmȁG)fAV'pZ! \+VI@ ~cX`<~:U믐w@?~|yT]]޽{FI=z?jN:% CCÓ'O07nHLLR]oY bеQCC}1 whHEEE:::5?ӧxQuYZбc~~\__cǎ~W r,0`jdd4lذaÆݻ>sm۶ ttu…~?ov~ҚE{aEEuV?mС,\XX+bO:Bhܸq!iino`i)QQQ;vzj_X3-JO'};EGbooOPݠsq}L 111 X` R76mB͛gȑ#e*++wYYYCCûwݻF_~$///%%~QFihhlذUƗ/_ٳ'22R__ϟWPPPRR'M]RRѣX| 66vݺuA?;*}]z&U JU((M&FDc4%^A&*H& eK/KYǼvt <3;;s3w$''߾}{̘1}EEmjje޽QQQjjjjjjn޼s]]]ᐂ/^־z֖N;w%22Ԕ+$$$$77xȐ!=ILLLIIIIIIddN]]m-[dee={۷ZZZ޽xb{{P0aaammmo޼y𡇇?|}}glOh>,//UVVܹt驅ǍG,ݽ{06-6#G۷/yxxbYXXH%ڰ>>>D9 4Ncv̙ð/_.\=F!aaaO ZZZ\]]3O?a+etaBB«Wқڱ0W #|y۶m۶mðf---Ct -CLuugϞ455aVQQf aǏß>}{қ؛~H~ s 1mڴP\\իBꫯ O~!|Yiee={ӧO\nssbbb"BHEEEWW!}v:M5d|P CQ([[ۛ7o mcdd4cƌ+V{NUUL&WUUuwwKѣohHǏ}cO-lii/Ꚙ dee) *jڵO.((ذaBC IIIyO -BT*DMMD"Bvvv´iӎ9 311immyioZFYY㽅T;S(ѱ???ZBᶶ٩YYYyyyĻǏ'H+Vxoxx8B(88xƓ&Mh7n,[JMoe |֯_9PVVp8׏ Fc2ttthjjjM{q Aėlm(ccc<#ŒBM!YBӦM;vX^^߾}8>:=== Z?~-[EUVVbFdrhhfZFOOLr!jGø^^%{իW[ZZnfhhbٲeBYBYhQppp]]]VVԩS{-7 r,epttx_겲UUU ! QaFl j?މ111cƌAرފ !࣢$_ZZ?vW0*p˗/ KLLxamkذa=0 oL &ӧ;v숊ڿ?~ Ñ}r &;wݻwϞ=#:t(>2'ԒlXAՇ[`Ez:FQD^X]]mootمFC%`  ^aEcc#dR*zͳ۵kWHH=CkT*ٳg$Ie˖ݹs'""bܹl7AN/%yh/_&V=gΜ9vleeOedd~4eʔkkk2㓝_TҢյ777?p NwqAEB(vGGGiiٳtzqq1BJΝ;WhP[G[[nW^EavbE+=xӧPLM|Sf|Mpzjay<^gggggP<>DʺǏ7j۰Dt.8PQQ999| ðӧOu3gp8ϟ9Ŷ  -7=_!6ى-? \nBB 0s--- ˽{>zhƍk׮E,X`ӦMb xKڵkי3gľ՛؛+@wY%%%}}lGG;whhh455x< 4h21caaqP!77̚MMMMMl[[[ddd̼BCŧLLL R[[;|fkk[QQA\nII p8FFFɖzzz1{yyƪYZZdʟ~Ivsssnn.J]fرckkksrrgϞBknn.Tmmm˝Ķɰaì2330 ک.x8b[֭[ZZZ zzzݳ(++p¨Qrrr &8IP󒒒 ;;; <<ܹs=m'XD7666nll466FSܺukĈVVV\.֭[t:XSYYIш555E Yfaaajjjkjjjkk/_666"bcc #** _*"BQϧ?~˖-%?x~8;;iMM]i9yyy&9l0333:>rHo&M x… 曄m۶=zhΜ9WuuuCCCffwTj[[[VV9vXTTL lׯ׮]~ĉVggg77iӦݻws0L믶B+_zn9'BHGWwUUU'''MMM+++;888;;'&&r8SN"dee-[VUU5u 6P(*%%%uuu 99ÇM>}չUUUd2Y[[L&X,+++ )Shii%&&IA(444LLL{[q9KT_K26!d3ܩ(*)w?!|I r2/r @ >ȱr, @ >ȱr, @ >ȱr, @ >ȱO%%%x{{r68uTO2׽>RW~M6~:--c$ HDZc&O׽.^1X[[ih//Ǐ7c$B }ݥS#arrrp RzSXŋ'N7orKKKiiO?$##SPP[ j%%%;wN2!C&r ƪUBl6;//c֭xut:ڵk x BCCB .d.\hkk ')puuuvvnhh@;Ν;~~~˗//))Eݻw֬YǏۺue@@`|2""bΜ9 ,HHH@}wWDq8iӦ͝;5kViiؐΜ9=uԩSx]NN. @AA! ,X@: BPB4 !ddd4cƌ+VR_____oooSUTTtzvv6BѣGzzz!2laa!6~W:$$ |D0 !tСٳg?|1&&Fpch222MMMMMM`WWW```FF2EQT ɵs8j"SQQhL&SG r, ]|yXXXbb"sB"B9::r\ۿ!rrrvqqoooEEE|]yy9Q,KVVںYUUeiiX@:tzWWLӅևIB¢!D&Bvz!q~~~qqqKK*DB777M8Q]]!А,ѣG|̙3ǎCOBn9'BHGW'99F^^^YYJh4.[RRd29ÛsssT5kdeeՍ---TTTLr޽2777AΟ?FΙ3'>>Դ`!YXX%$$}vܸqcƌxAAAnnn:::\%r8—dlw7|BfS%QTR~FE7Cd\!A }cHX9A }cHX9A }cHX9A }cHX9A&33!I+zў={>i-ǀ Hͅ ^~]SSOZѐ!C˓>i-ǀ H*---((P(IIIZZZMMM 0_J(XӁ HAllŋ;}~I->|=\\\\\\\]]׭[7dȐ7o"JKK̼lmm+TΫWLLLqFݹs׬Y#$ȱ%%%XbAAA_d{nnnzz7|#++믿fgg899옘`C?#X֭[ѣG0 ;}'=j@ȱtPTmmm.+a㼼|yݺu !dlllee=qD---3fLpp/BBJJJFo>///|͍7<==>}cSSOt{AۣF /^,a{ѕB 'O455{zz nlذkh4Zzzzzz:]fd;o0{l<8''G]]]'99 !tYf2$$_d B(66VÇ/]ɓ'kkkܹ!TRRrFKGGGUUUeee#G|@O A0 r,իW,X0~xAR7mtƖ666'%%-ZoIKKkooOJJRVVWUU544tss[`F;vgΜdaaqΝ#GHbM0̙3W\K.UWWl# ߁|I;Kssree{HMMUVV1bLIIIMM%J+++֮z捑S~~>DRRR*//GY[[sܒYYYwwwaYYYNNNzzz lРA22 &8hРl 7ftw_Lȱ>XS ꓂L@ >ȱr, @ >ȱr, @ >ȱr, @ >ȱr, @ >ȱr, @ >ȱr, Hr HǪUvQ K?~\^^>c8p`|F`@ >ȱr, @ WUUUsssG7ow˗H$LfXNNN^^^\`CCÇ)ʡCzڦʕ+|>ʕƽ)T*588cCƞ:u}7...%%~z###--իWl6{޽Bw }0gg篾 Dcyfeee_ %%%6r8ñsG`l엑2#%[l6{V\\\$l뛝}bՉی3,,,=^zu̙#ѣG|XRRR$oP__z}|>y9jbObͰanܸ/-Y$((H:.RUUa|>?##c„ ^څb)((%ill|E222v%!2,Yf|ヒx:u ðCcww0;;;O`bMxxh *..0_DeggcFѦLoy ee厎kbp8>>+++00|Ν l6;((%(((!!ð7o]r姟~wr办^jj*BɓjjjeeeuUUU]px UUI&ݸqX9o޼Ϛ5k̘1,SL!63f۷oطo\[[kooO#B駘[n냂"##KJJ8PRR"BGzzz}}֭[Ŏp8m۶%$$oڴH,T}JJJݻ˻sNQQQPPГ'Of_]]]PPz8 cӦMaaa^:p'B޽:uߝ;w6mյwޔwvv2 69޽{֭|'Oӟ?셡Cjjj&&&/޻[VV# >"a䏉ӓBۈ.b==={YhѵkpTϝ;Wrщ',YbddDDY[[kii]±c~V^^ٳOfggsBqvvFyyyoӧO[ttt,Y2eʔ#G+9΂ Go߾͛7())xyy۷qÆ )))xb WWW-[ :T.MMm۶)++),YiiiUUըQBs Z|{rssCCCJKKB<l2b|rDHssڵkw7w\;why9rƍK,rmaGmooxbbVKѣGlĉwu[x͛Gaggh"KKˀ RPjj*>|ی3z:JJJǎkoo1cƸqvŋXYYmٲ͚5ȑ#ׯAzxxx{{{yyyzzر/hSL=zq4,\aędJJJ<Xs޽3gw ߁q m 6GJa˱tttݻ׺0c~fbb2iҤɓ'/Zo]dI|||Сo<x!dɒKNNne[ׯ__l`QNNN xw ?$'ƍ am`0˜ ӡq\;;; |N3//Mnnnƍ_&N{:d2qѴiBCCėQ>| &0eooohhh``Л{"8_?G:;;߽{<6999bMZZ7zY gϞMђy<BO)Yf-e_c^iovRׯ?z… Ϟ={.>@9())ܺu zK.saFZ[[E!1o"tKJJ|||E&NAt:7j*{{{B ^#>X"""/^,n^^^ww7:&!:.ؒ Ul[>}ҥKs!Dd)$*7 YѢ^~}v:.AOCIII5DH ``iii)Bm4 IDATLuuuD N|}}#""LMM{*MPzz㉳Рۛ&M/ 7+))͛7ʕ+DvKe$`ʕW\IJJ III!ݻvOO8v^~xcspp_ٳGU^^^2L\&'tQѣGMLLfΜI&t:aձwމVG"/첲2dJ*<99Y^^~ƍwݾ}tO&8Nxx8~3g*zE=y$,,lҥ=U:ff"x΢muYWW;v\tIpB\-x՞QyxxɉB$7##XD$ي+Ο?ˡP:/6IvvvNN͛BgϞ=z4Bs 6<<@p ~gܹsjkk~XbJ;::233&Zl~Ac~ŽJ}'N\v- >$Ǐ􎎎WTT(++={do߾P(---/^hoo2dHqq1z捵ujjjcc#B;66vРAqqq 1>>d,+WܸqcOݸqcɒ%k&LB\?n۶mԨQDڴt={ ׷?{PN&%%Q۷9rD]]]dddBBBrssBH >_uttvww'$$ׯ_;v_~p8o޼ٱcBhذa|&_%z:۷o섄z➟'N̙3:{3gZ[[ڂΞ=r[ZZRSSI1c$''755"nplJ H"s$YYYKKKmmmmh4ڻwD/Օnnn_U__;b 0 +*))y~JJ#~xRRRss{zz:3fLNN uwwP(ݯ^233/mhhYh-,,~ [yyysssql6{„ ķ{{{7o\]]뛚|UVVh4k{XXX޽knn0lFFFx3L i 222oְm۶C^ŅĒD"aX3;;L&%[ӎ,2.///:] 'r9'`0^x1yw~Wݹsg֬Yidd{/m\н7BΝ엨_~{3BggC߁A'90W>Gݡ=?g/_Gc҂?X_ZZz崴۷ocxR5O$w~3`8֧d2y<^o.lYYY_˃,+l$\bŊ? Ʊ 'S:@ZGGޯKZ>HA 0W }cHX@{}8| 7nxҥyw,wѣGڵ+)) }qW^yyy}Zh=aAAAO+WXXXyÀhcxqwwUVZ"h^[nMLL\n~2NiѣL"x'~2w>} رc}}}E;22RQ}_)Ar_|wŊէDI)cRvvvbq!]O> ^qJObw} C]IIBP=f}:)}TJ ƿ$wVSSζihhRSSsuu%Hmmmyyy 8p Juuu-,,vss1bbMMMAAرcGtvv&''0 ˍ|ivv6B8H$+++:Nda_nkksttğKъ--- >>?c8ydDDBhjj>}zDD:-[X,V{{UG߿`ȑ˖-Ӿcǎ便sέhoorbn޼IƏ'O lݻw׬YzA]]]_~M`ZZ`Q͋/>s wǟKxK,P(Ǐ?{lAAAE4@+++6M?{KJJׯ_'VWW?~|YYYXX@ׯ_?ky޼yuuuB 6ݴiiҤvwލbX!!!ǏG%$$zyyݻ? S msn>a„HK=+ ;n8#ʲ;wٳJٳg_z̬zG4 Y %SA$aXEEŋݻgmme ހ Hٶm͛d'8uԤ$??:995N^^ޞ>ƍBJKKH$Ҳe.^RSSx%I Ojժ.~;sSNYXXرܜxhE...ct,Xڪkbbuٳg#?ޮ}uرc^x1hРaÆxxx؈VUQQrSN!8eSyf˗/ǿ ŋ ?͘1VTT… ...d2"9[tݻuuummm`0D=&!X31VC'57n> 5srrIzz>? .Y!ᡮ,:ZzE{{;DZzVBBBaa1c֭[w!}w B ~?Sq+V())-_< @NNn…|>ٲe~~~ZZZgΜ C$t%0LAB3gpttИ;w$zr, eo޼QQQ!ᬬ={"Hs쉷H$>RPP033×\.Bhƌ_Kbbbx<ާ4{ݺuΝC=|gϞY[[R(uuLNO7o>^R^^ndd_QQALDGG޹s(JA8{c>toܸC4qqq5k֜?^L6-))N}J]ll,w;nmmm%1#Sy/"8;w3gŖ-[$%ݞS?'[Ⱥu͛1bĝ;w UUոJ hSֳNj#=&&Fl 6=F"!H$C AL^^^pr^!" ݹsE|||AF&---Z[[?$ k SSSd… CBBWF?k,zF dgg\~}RRҒ%K4>/:ba;;;99̺:H,\b(aǂNJ }@rueee>$H;w%vO@b xUNNfǧO< WSS?~Lraɻ*d, ÑP(BTB΂P 5H/C' Hae\.A%&&Ν;Wp]Dcǎ}7 l6?y!DRX߾}[^^.!02E__s}d2!eee&믿z}… [WWשS>z7>>XAS#_~ʕ͛7唗#t: zԩSk׮ݿจP^ŋ\.Wh.h|fXSuz"'kkG655/ מZPP 'NW\imm[z <Z YYYWWΜ93++'** IKbO%D"o`0kГ'Oϟ/9΂hl6[AzN @_Q-DKV?Ν+..VPP:qℂBBBڸqcQQѱc޼yC"FA"nݺ8|{ݺu}ĈgϞr#F0,>>^KK+//411bܸq~R쏀222Ο?rUUU8"""bK{{ .xvbddbnn͛aÆ>=Ç_9sBHCC#22RKK Ĺy󦅅Ewwٳg߽{gffVRRrҥ*kkk{{[nuww ///S(:>y'Otvv:ujB'OWXX8d|DCCk֬m++ʤ$.?qD==`H$ooӧOkjj|2>>RA"@RR꒒nܸ1jԨ۷owuuq8///33כ6mۥ{ 5pL*ZRRꪢB#[YYMRRjccSYY|mQ\\}YMM͸qF}>=K.566644 VPPHLL:tNO} !$'`899S)<$:: 9rdРAn266^rhItww= btpp?DMMMrH ȑ#111\.wСG}򥂂 .0M$cc;B6Ý;/5@Hss>ooo)veeep?~_[[;p.  6UUU "x\=iii L&wܹ&'ѐz \n{{`<}:! L{OáP(D?A)++[WWF4!f95C_^ ={}ا!x<==V555$!,A$ޫ!f~ 'r9ֿ֭[9 q/ѳgΝ;ws.HSOX,===bR| ꓂L+@0W/S\\٩cll߱|k׮|=ZSh4ڀ[A9c0`2uuup  ?cOq>)ɄHX9A }cOl… .@Yzz={;  :::;>uVZ[[?;,d Q$D(*(+*+*YUY]sE1 * 9 Ȑ Ar`f&G_,{PǧTթSӇ]:G`aarJ&A  ޱ;޾}UjM/3Os@@jkk9FKK a<D"M4 1t )Se)ϟMMM555{{{ 233%a߿666555pghhH >}TZZ:~xL&3##CZZʊD"566)++677JHH}ce?N0Ϥ>|IIIVWWSTiiiayyy'O7$ihnwd2u{zzlll\*++kjjtuu'O hll񣎎TQQ4i___VV(př3g666XZZJII566{eff*((XZZx<^nnnooy󲲲8 #77W]}E@@XQ}ҥϞ=cXcX===6m***2t:=66… /^qtt<{lVVO?XZZK׷vZ=== kvwwwttرðp̛7o6nhdddffvm۶r@GGǪU :냛 k+XWWݻ㝜޽{>}:}_uѢE---k׮4iҡCf̘qYfh4իWmۖ~Qo޼inn'@nnEHHat ]D"988Ο?_SSpsLK_^]; IDAT]mddիj}}}''Fz H3fm||<@ HK.;::;v aاO<<<"## =:y䁁?1,,,oPWWwwwRSSٿeTstt4 ))|[n=wKJJ/_/)ŋՕ---CBB8NAAAuuuff[OOϏ?o>uuʕ+$%%KJJh45̝;ىZ[[bmmm?>vXFFppݻw544VXjժgϞ~vׯ_={6999,, b}… +Wo޼퍊1ݻJJJD">'''++k…yfeewww،7<33TGGGGG'77`mmmdd$!!1qDSSS6 ?ΝraH$777affrW^\u?6uT8R"FUV͝;ɓZZZ222:::LHJJl@rr\||<bM8@ RɎ$\`|ŋ EVV6)) rpp`0޽;}4癈P[ QQQsvvVVVH$|JZZzԩ222RRR0f̘罹ֶ%0&`0SԆظ8N/((Fw999 ?3 4iRll5@ՅƒJFBBH$WWW##kkks\kd./fvv6H?~lffb \ߏ+**:88ŋ%)\POO/r.]zY=-\ &X,rlP#++ӣ ),,%%66ٳgCU ikk;v)?OQh޽suurrr\]] :_tҎ;$%%:;;y"<_ur~EE/#dFX_EY@ 1  @ bA>@ |,@ X@ @ Fc!@>B }@ 1  @ bA>@ |,@ X@ @ Fc!@>B }@ 1  @ bA>@ |,@ X@ @ Fc!@>B }@ 1  @ bA>bԨ yU[[P2,۷AAA#nk׮533;w-[>~ѱe'''33UV>XAAaΜ9nP)S~͍`dKKKccA ŋ6##)SzJBBAUU599*@ =y,( ..͛5k\nrr)SVZ8-GڥR(..?[ZZ:m46sNiii33|UUUm۶3FUUҥK222$)77Ǐ`„ :::$ 0uT//o08qB[[\LSSÇG B̚5NJ+P/daaaXxx8͛7h4 6ld2mȑ#~x-//looo6lXdIBBN0۷ob555?a$}}}{puu ìEpիW1 /ava2 0_~y<ƍᡎ͛apx(%%cqFll,a4H$:tٍ;ᤥA---(n:ha"##aLL ѣGZWLL`@3gHKK0 矉D".s1PPPӧ, ƍgddӗ.]|Rߔo{LAkɓ'߿წ,!z{{ᇫW^O3f)~9NFF3 puu7n۷drppҥKUTT'B;dEEŔa۷;;;ϛ7L&g0nnnOf2<Nqw=DSqLqq`(7~1pL"`CuEDDڵKKKH$H$G11eL;._zj6 _hkkCd2yƍ~~~۷osν{6mj?}DPa0ðAEYYٺ~)::ݻ___gge˖]pA C'&&~UAX?>-[nݺu~It"ȿ)p[>}222ɹtґ#Go>nٲEAAwJhUUAkhGPVVl!!!N8_D@9|,u֪+V\|Fy{{/_^UUue˖͞=NIxxxTTT36]VVOyyyajnnu>1Œ3d2?|ellljjZPPVVV[NC***Νw۶muvvE[nM>äSaeL& &&fЙB111iii(!7n`0IP(7(0TUU!r~n*!!fE ++cǎϟɉ&޼y#..6ڵky<WPP0yd++-[^z׮].0_5X i̘IjZbbM?./////TPP8|0 ##|۶mޏ?nhh(//hhhX~6w֭ǏWUUAðfffvwwwuu̞={ܹ7o޴tPPP(,,㽼N:Tکyyyw&ɋ/~mLLL^^۷8eyyy %;;JRRRjjjeee[nUVVjii)))LMMMOOWUUuVmmmGGJWeddϟ/(@M̜9sԩ~xuuuwwI gΜ)**JOO޽{%$$0, `ƍ o׮]{̤hǏommyfSSSkkٳ?:.++xJcc㈈h%K7jddoXPR۷o_x`ggWYY'`)Shii]p*44=88XUUUNN~~~7o޼z˗ڜ>|```tՕEӥ[ZZ=OMLL fkN4DL* KooD%磘L@c2x,0<H$t "?`0_/ cba+֠&\.劶6a꭭~~~ׯ_!?LBO_<{?...SPPsΘ9s UW πɠkN~@k>:$ :X`pD"Q22231+}_0@+fЈ,D?`0Ξ==̚D (##bEPƏoll@ |,@ X@ @ Fc!@>B }@ 1  @ bA>@ |,@ X@ @ppcǂ̼<Ǐ|C 6mtҥzWD0 m۶S]]`0H ݋-}CUTTdYw=<<*++_J߿̙3_Ç=<}d~Q@ dQsdY~}cccWW:;;gdd|6lFQ7 |ZV\zA<'!!СCP?GykEAAadcbbGV611qÆ #+ tpp#5H$sss 'O}QܹsG#$Fܝ\NC#`C 8B׮]g;;;9}A3~6hPAO b . 'l6łiV9<R@62x<&E*++G833@ GxNLGG@K ,ӃuYr8?;#~Q"`1Ԡp8?r}{c{v(# u|]K1/N9#A>b:uԎ;X,֣G<==a~MMͩS"##oݺ`هh-[L4ICR(baؓ'O*++>| >|}ݭ[ 7uԫWTWWK.;::zyyTe<==_xbŊ>1>>@IIiŊKxxѣG'LXtill,xl3yd|СChf'%%޸q333իW2 GGM6//_tuuww >|HIIٸq#|Ziǎ&L/^011Ᏹ+..~Eee[~wa'NKMMݹs',Y,^RRRBB?xΝM6tg{dP={6yd//bwwyUTT噚/PðHہ_@bbc*++_~ 3ʢ|2**ѣG7oWRRz˗===TPFQRRl~kٳÇP&??޽zշ .Ds^ !L6mڵ^sfΜ O8rrr0ݭ%PՑ#GaZSS,X.\xmoiij͚5eggHc0m߾]x[w܁GYZZr8 n޼.]0lɒ%!!!yzz^z0wbbTTT,)%%d2ƍI 3yqDqqq T+v q3ŗ/_nhhvZ1h)))cccᎋ67~k G h4/3$~6XQoo/^4j۶mUUUWWW/E>h@Ӣyⅹ͛Ӆ#6D"H0LMMm֬Yaaa ť>$$d3f \]xqhhh||r|TRZZZYƪUcbb^xqEs8 nڴC"d21 ?nnnaaaJJJE0??OΚ5 j.yC 277wppppp駟Y˒H$N/AeD#˜ xաIII"%Hjժdss/⫓{j[>}ڳg/|,ğYkk+\\\ALL \ ;wnAALQԋ/,[H$h4 F8c;hC{U?[l1cǏY<ٳgjjjp^jݺu/_|ٺuXr'ORSS/_.!JJJ8۷߾}dBSZ1[lx">GNق/-kk/N>]$JPXh]]]z/̾ttvv =U$$$y{{KKK422:qℝYQptt(h-#W_rehh(f[x_u+\wm۶-<{khΝ9s;wFn/|,hRWW<5,''w%???6]VVCJ}QQ˭nmm|rPPP]]F >~b >~HRh4ZIIICCCzz:[EEE>|9'Nd0L&7##+''455:;;+vZ||͚5;vغu͛7*kVVVssOן9sfɒ%zK&&&_x999uuuvDC`oo=enݺseddTUU1̣G޿_RR}(/Ydk׮~uGOOOKKkѢEvttڸq#$uttܹs't"nnn;vXr%>@ ̙3***_d0)))666p%L]]=++̬TWWx<.kjj*''+ckk+D611JJJ:::iii}nii)DԬ&xpI{{{cc)t:=77n677e 񹜊 YYYMMMx2iҤq YZZ:;;[^^~ƌ?~f͚A>PǏWSSkjjYT*@ ?mP^t .BuuuUTT$&&rSSSt-@(--511 P(;v쨭Ƈ fL<|erЦ!=cr8N ôa5//O[[{ԩEEEƍQ+N[[[ffٳta4ZIII಄O}2 P(d2嚘H$:#L& Ǐ'NTWW۳sAFFVTT}woFr$ÇZZZsйИ0ag5B{{&&&p|ƌZ{ǎ۴iD{{{jj*J_E\c6 ~@>A>?MCCC))|ŋ˗/UWW珩?vظqa߿qFzqwws0 P(W\R0EEDD ȋ_^^sN++ ᄉ.>==ں177[+{FB#/=*MXd+WYZZ V 022ߘJ{?@ JUuuu'N8~x@@T(.w?/b|kESƌxS0"PYY R622;w.|PRR~;^ZZѣ۪㑕hN<]-Yd#3sL;;իWQ/ELLLwwsihhJU fffR!ǫ!H'N݆ %%%O<ߴ-WVV_5 XсX,r(ps\}!?q8᪺wPaXCI*++ܹaaa4_SSqN5=t_65ZvGDCe2p@hϯj`0H$Mv1 V5>, J:A\P;=%Շ/<4pM!<?GxX,h`x_Fp;@233jl yyyWWW[:~-L&Ν;h-Z$}8.;Ńap_aSRR_->d#A\\>>>CKB/^~zҤIC55wnݺʳӧOƾ|%%%߿z*_IIիW/_ny֬Y tcf[ëΜ9#g: 66ƍuuuoߎܹ3..כ7oRPB=zȑ#IIIT*uٲe/^HHHXbŇMLL,--ݼy3;7n涴߿B i@Cbcc)Ç1 {ɸqrss{{{oݺ~֭7nxzzΛ7ӧ{&''GFF+smeek׮%''޽| {/?~G~:22233Saه_h[l4if{{{%%%%$$wIIIݴiѓ'O~۷7665@gϞ8qڵk0?''w%9х Jzzz֬Ycbb2sLSSSxejiis3f^|;|#Gݻ֐occcii9mڴd@xxΦMtuuᾜӧO߳gӧO C}0ݻ pЦeeeRW ðϟtoo5͟??\p!J0f[XXI aX aYYYzzz ð۷  WX988tnn.>rnmMMF \\\0 p8ZZZx79sfYYap`mmllaʕ+0 ے%Kazٲe9~͛WZZ .\aAn)))Q( 0 Ɂ5Bɔ͛7cC]yy9&~hޯ_xŋ\\\`}}}PٳГp8qWnѢE555{GR}||?^__/pSGG'??ð@̿x-[dŋ?.]zM}6Á= 0xON8^uuuihh`VUU5~x:aأGT*166...o۶M@gOOYfUUU ϟ?kii;opJ]999]|0:nbbfܹիWp1|'_^f Dd2? ' G͙3f***at:=>>>>>~رp:L&HII rBCC4blll`ZVVð"/`d=4""@$UUUaiӚ+++) ɉOKK#t:L&kiiIII.C;sڵVWW766"FFF?#ao:$$JVVVpȦRpJ__BK⭻ >J]]]IIJ+**JJJ?իWJJJ2m_ dejj)l/Cqȳgbbbǎ222:::%%%H냳gNKKݺ"O8qܹԩSۊC޾}4m45k`Ǐqq#Gˣqy֭^8 ,PQQ;w~\\zKKׯ D1ٴiرc7sss99w^|bPI&(++34ׯ_'&& O&׬YsM.;sL6 0q.fa~J]]}#--bq`b$˗/SRR~W7o,**JJJrss ^too/0Cn|t__aP %<%G$緰Y,ne˖ %OccPgڭà9;;N>]UU5::?Gh[XXغu디D{$6%%J._\;y5kch4LyŻ)..:LpQRRϬUVrL300$,|~aҥ!\u$%% ᡡtww߽{0aF@XA\\)bΜ909setǏEדrJ~~>2srbGeX,3f>|_ܹs?E`hhau'H[rpp g$*o9^lbbbKIJY:~šO^jàXZZ>}zƍ? vvv sssb+W [bhIyy;wxxx6::^ o޼_;3g~'>~,l6T:zcC1w\sN______))){~ĠJ ,!yYf~@GGgܸqχ:XZZ 444^xʕ+fff"jCA>bt5k|춶?RԴ4VRRА IDATd2/_SO>Э[Z[[&LPTTTWW"mХKJHH888ٳ'"">  ڬ|xQ%lƨׯ̜auFFFMMMZZZWWl.==1/pڴAM-/**rխ</==ݻwL&3)) `$&&~h̙f 0,;;wOKKΞ2e ݻ7$663;;N|0E<==s1 k.苤[nʕ G222o kjjٳgϞݻwݻSRRz{{E/lݻ?~ l066^l={N:%++o>&cOO+V۷#-[xxxDFFYf={NNNd2y߾}>jhh&LpI[[}-]4**j񩩩AAAOtŋ[ZZ||| V@ ȐقϾh@```~~ ?jkk? #c 4rT4f81.ADDbB@ X@ :1@ 7SK@Dd.X^U__qȑg~j/\.wϞ=cZZh6l o/ wA w^zȋdߟ;w0`0ol,'cƍjjjFڼy3F}ԩӧ_~ڴiZ:`3b!H^^^ ~x@WW0}𸹹 (**O$bowܹm۶]7O2zA[et2)?V^^6~x+?!>|@(_Bn #?Ъ)LDDt#55555a)~UTT&w@  oXRRߍ ʪvttȨ1T ìx<^zzzKK7fdee'Lf߼y3ydXZZfgg㞧((((--RԔhhhP(,ccc>yd2yʔ)D"NgffjiityyyEErkkk PY@*EEE";3fHHHl[1QSSs8d&;j(YY*iii%%%riii-QiM`0=--- dQͅ %%%CƬ,* ՕCPRRR444\ׯǍ}7hnnѣSRRGsVHAAmlljjj*++L Fr4M6H+8NpppDDDvv6DP e٣G)...//֖(++{BKKk+gg'?lhhP(555ӦMídyyy^ -++#H rkk+eYPPPSS[0''VBB"==۷ U[[kbb2nܸ+@ Z+D[`0_?p8Hkkv@UU՚5k+**ciiimm}r êϟr1Lmm햖e˖Y[[twwiiiYZZwvvhnW#G|s&}ܹ2KBP={RիW܂ZZZ<==Y,VppѣG>)\YTTVVBϻzzz7o~Vxx'`;w;99%''_p{n 3g^z8>>>55^EEСC#GXZZh47xl6;**-z-a¢ 9uT||sNNΝ;wJJJ۷ogX,+<<СCxb\\ݻ%Λ7ɓ'eee_}&.R!k׮ "4mئ&OOOqqq55FiPVV][  ú{{{'O;99>`0|||$===틉bbbp rrr$D`GG} ^AOKKԩSmmmoK߰aCaa!ey<^~~M?~ ???Ν;;;;?2p2=% C w?.\0%%EdLss< Ê3220 svv̄urrT*>tuu gcWWWىa?ϟ?aؾ}.]ԟӧO0_~NZz5|޲e͛7N6 ŋ0 0 cEGG@*TjEEabbb]]]t;;; UTT8dff╊Yreookנ¢ gC93qD\EϟiiiI& .߿> 4={೫klll 0o}CCJ VZ ---EVAdZ}: 7o,ZwuuMNN0ɓGvv_?򌈈cǎk.HÁÇ0P y+ G0lΜ9¢06M&\.{sss.\Qo_zK4oaÆ˗/̸\nll,'yֆs 8\JHHp8Iѣj8 7KJJBp8IIIMMM,ccc@ ?O'H%@Ѐ(**šReeeCIL͛=z4~߻wOIIiٲepMEE%44ƍ]]]]Ҳ~FxB__>۷/33SFFWndd$~ZNATpB9<< Aщ|L X`ƍ\.~r ±hxWYS --C$ BBB,̶ 9,BE~,+V,,ՕN{xx777"(//i#,X -- OKK[`@ZZ! ^{{{oڴIRRbBeb&#####sY2 ͍qvv^~=״ /0.\"+@ KDBLyr~~~!!!޽0ѣGmmmFywzii)~աorKKK?X.`0\"%%-!M6]xD"0/////˗3gΔPTTqㆸwѣG &0L??"--Rj==={{@&YYYYVVfoo3ѣG7;33@ XYYH%ǏGGGs8ӧOxBBBBSS+%%EAArƌ>>> ?bbbVZQZZ`0JJJƌcii)--}=UUU:nddjlllff6HqӧO?媩yF[[흞qلcc˗/3 #++'##loo믿JJJmݺݻwgΜINN3fĹs222ƍu…7444hEE~RRҭ[ Ə/&ccPLgϾ{XCCCJJVȴsӧO0DRQQ=ztpp0D ;w)SoljjJLL422JLL?޸qmE^VVV~~~ϟ?p8VVVō=NkjjBa\+##cccs~MCCcݺu򈈈>xTyD"Q^^犊奥FFFRRRjjjpkܸq"X8\. IUUB .%kf?c r2~,ĿW۷n#w"w&Q('Wdk@ g`}k`'' /!2L&k:.$XPva~;`6D"Q03xoD~+\ A XaX; d2922bX2󓕕5jT]]b"cA*Ej)hpB@(ZGQ"@ ?B ~@ 1  @ OGGǧ  1~wLϐ0u . -88o-0___&)2'L9"._,++;n߾|AFxϚ5뫯ޱcFh۷outttrr۷ESS>ŋ /))yY}}=RWWVQQ?vttݻ*;;w f_~ůt𒧥EFFx<ޛ7obcc1 KNNGQPPPPP{@mmmAAl*x4btzcccJJ6^[6]]]k?!%%bŊO*pk#''TLhh-a999Cqccc_| DGGwwwcn&PVV%JJJJKKMMM.] d2}||B >w\YYA__ߕ+Wv>.sÆ rnnn$ϯzDhӧOx@D"-ZpK,JLL4iRcc۷o;O?IKKC&\#Dz{n 3g^zߎ)..~m\\܉'#GXZZh4 lkksss jiid0'OǏO8!?'O988TTTܽ{W!{챴>p)ԩS999w:88TRRp8Pn777---KKK77v vYXXhnn.==x߿h"a+W G8'22nQZfMssp1 ?>fffX,VppѣG>,SP\\/R'<<0_}֓'O`N 1VW_}%%%\]]Efg[[[*zj@GGڵkD"> Ɖ'֭[ٓ'O8?lXaP]]][[wmjjtR=wر@ ]B3emm Z Tt:@pwwvJN2E\\*))&$$䯡1m4=mڴɓ'rƍee˗h4 ;w"K044zQ,--DbCC>#//?a򓔔$##,>|ĉv ӻ~[HHnݺj*|!UhaI <==EzhO<V[[[LMMDbjj*NGG0ggGihh;UU|iiiXJKꫯS/g@@@KK ܾfCCCWX43gNnn*ijt;w-ڼysMMMWWWccpq؛7o^|Ν;k 66ZUQQ qㆢ"3#?3ӧ-p8"[/kK,svv>tPr .\#{>1O8q"11B  +H&7Z} S` aÆ˗/={6++kɒ%"fd2YhZ l ߤikkKOOpܨA㷪$%%555v166i4Νd2q$???ljj:HafMLL!׹?G__@sNHHH@@$|X,|f32H>_EEeܸqD"qŊo633_8 "Mll,F!?s@@ncᴶ*((VZ)Bܿ a3glllػw_=aYd=<<蘘#~uaxx8{ӦM,ㅇG(a… +VoΝKv, O`Ϟ=366666ڵkpHeeܹs...)))"ޣGVZ_5d2˫L8ðlX?k,xh@d]]]]\\\;$gΜ>}Z0MٳgfVxxxgg׷oߎ ݻ||rdɒtf;::DX۰a-DV r^~ ) b\(D믿ۥ://?fΜYPPcINN%SciʟZd7N:p N>KIII[[3fP(LҲdr___\\ѣtfbb+++OWTTUZZ_]]$''_tĤ!O>ݼy3L~… NJLJJ 4iYUUURRÉ5kǏCCC߿`0edd  JJJbbb666gϞmjj:ѣGׯ_n:x~~~pp?.,,Q򋂂?@#x>^/dJKKllll +U^^~m8ϧ3j(uͬ, `0T`2ʲ2{{7BOOF×a+rg!:sȽGCBP ˋlggw% :;;\.O)---2a_`> 9-)DSS388[frmmmJJJnooAa=z`)&Huuu4m0Sa555A`S`Sa2T*JXCnn.L0LEVoرFo n"(r [C"cS"p W# '" 0 0zޠHKK |KDh`RԿ|{ovpX8 e ,LTVV3}*AEMC#))96;Ǎ7H3EFFF ,$hA2,Xѣr?UR***G)SLL Xdc!E\~]FF͛"3@@>˵ >k֬O-29Ⓚ Z+D Ĉ~h@ oX@ @ dc!$# ŋ[TTx 6zg%>ydƍSN񩬬x[/[lܿ1xܹsaϖb͝;W$[l),, rqqJ۷/PĿdc!>7***1Ì -0==m̙#Y?Suu;gϞ1L!d~,SLV˿ݻw{nn./kτ^t0;;  Pׯŋ Rɓ'wE[D"˝5kCKK0qmgϊP\\smmǏ!͛ŋ\۷:>uT(7 /ooo~ A?~jjԩSKJJ3t$A>UCXύ/^_Dn>/,//744P(C;Bo ***MpB;v!!!...g/fKMM%HO>[[[vޭ윓p>L 6o}رJYYYI sss]]ݡԹx<ީS~D >jDJ2_5OJJ'No͛b ãjjjCm۶mǏ_fܹs /_nddv]vI؎:::---NNN,+33gƌ rG&0qDR\\d2襥aĉeeeD"Q[[PJJJ]]ρfddHHHKKK{zz444 ZZZ޿O߾};fiivD"QNNNNNn[n~_SUUswwgXP(2?Jeeea؉'nܸ+7vXee嶶*mmmhx<:^SSd2y<^^^aD"N<|PGG~_1/aá'NTVV0tcbb_vO"~DV&22… MwwwQQ!5aNxK555q܎\O6H$Θ1COOFIIIIHH>wlcXK.MLL_5J{9@-K $$$V٠ș4@OOѣGϟ?d2g͚***ciiimm}r *++W\Gw`X<|ŋ⌍wލ/ܹ)))N:#3'N[.=yGh...p@]< upp /..~m\\܉'U$ G0؟JJJ۷ogXJJJ{urpLww o߾sݻ,Y=mڴ,رc...;w9N:?>ټyǏ777?w8k, R[[ H20JJJ |ވ?ӧOUTT`)ˎ W6Aooɓ'cccϟ\]]~]@ 1\{@ @ 1  @ bA6V{Mec湬r! KTCI}%j7έ#c cA__{P)B5~ԃ ,zhFu'TθU}o͔{7?; -_HlO Н}~_rvߵ^}Ymtp{:㔩%D|wuğ^L⃟ND`"f(˜B5lz80$!(Of^W=i9?QbuՓ8T)W!;4mۋgmSͼC3n/G>k1MdmG1l _Y3]VkTyd ~Y l\-ZXk;֞4Y~:r$0' M3 6rdqj.HJm/Jf ܅`퉗OZ= \zFjI*yaۉ|ta쬣bþ5'c}Q iP[H񻘢7s n{#g›DXčg贔7a1uW]8V~/b/#kÀY?.>F#VbF4CGidճ$3cRڅ,qx/_۶8bpHm:Mr:$G'D8x]2TOutv),սzlbV7*-y]#+_͎ۏ czF,q̗ u5/6w»zIDATs)B*Iw5Ck]]="N*3T5auM aU zNS F ͛\)udsN@`Y $|n;Vjދw~ZzB%gy">gl4"w6:w㞋w-C{gnZ 0nmgfoo/x Io&Yh0L'ϳ|ryYWOṷzƒD=](01H_by}mt)B[ivCTw9v۝?tt'm>@(:뻥ъL˟iY^rj~Jos{wx8|ms2_y}IJ0 @.<ⷵ믔ۧ< i=rS8a '9tFDg1a5>Wbٱf"@3iS:y**@;s˙svpHgNeL9ӅB՝Col%ꂶy~w1߮685eewtu@(@_4H6[! &Ā@ :FZ!@s@ "@k@ LANiEA   %nG NX|j@ >7R(\v IENDB`apvlv-0.7.0-alpha/.gitignore0000644000175100017510000000015514726051136016247 0ustar atzlinuxatzlinux/build *~ cscope.files cscope.out /.idea /.vscode share/doc/apvlv/translations/* share/doc/apvlv/Startup.pdf apvlv-0.7.0-alpha/THANKS0000644000175100017510000000226714726051136015200 0ustar atzlinuxatzlinuxI would like to give thanks to the following for their support and contributions: - Ian Munsie , for he fixing some memory leaks in ApvlvPDF::pagesearch - Nico , for his inverted feature code contribution. And, the code is copy from evince, and the author is Juanjo Marín. Thanks !!! - Daniel Friesel , for his more beautiful man page - Andrew Kudryashov , for his help about Startup.pdf - Adam , for his test and bug fix. - Stefan Ritter , for his man page of apvlv, his careful test and pack on Debian - Robert Smolinski , for his vimlike tabs code contribution. And, he is a developer of apvlv then. - Robby Workman , for his bug fix about doc directory - grandpa , for his test on windows - Ali Gholami Rudi , for his bug fixes - tocer , for his test on linux and pack on ArchLinux. - pk , for his advices apvlv-0.7.0-alpha/Startup.pdf0000644000175100017510000007014714726051136016424 0ustar atzlinuxatzlinux%PDF-1.4 %쏢 %%Invocation: path/gs -P- -dSAFER -dCompatibilityLevel=1.4 -q -P- -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sstdout=? -sOutputFile=? -P- -dSAFER -dCompatibilityLevel=1.4 - 5 0 obj <> stream xWrF+r,hJ%%C1IHW0 @QUE/~Q:h92b}R6MnJytqP Bp]e% soňzuUg%4'eSϋ5&9+AtLf7W olW7 &} Nn N8mxG+gO`zdͣ2@ l)g<8)ps t܎ߢ0a,D |}7;y8GQ=-7EIE: S)qVV \ TŘQn%<ݜᔡ_R7J8F mTj[5*5B03x+13YTeT1Գ5rԵ>iq{9nGzNzSqt;ͰСE$y3PĻ8[jGu¨Gd7Qcc)o;N+":v2A)e9ni#.lR|ţ3owXpX !]]a-ak=7Sؾ|=c!`¿'$6\Q!otH!J^&EPY5@3M^E&vQG54q%eTٝgq>XgC%}e,ؽz:z&`Q__\&!OD]:r.uKsc~% @[-*1uػhz])6A'j2d^[CNFg)T!۵Nyv!kP9#H) }N9NKYC!7Z&g7ӵi[X7`;I#FVyUXZmW9^R& 5~QX^tTEh K:w'S*ǀYY0^Gendstream endobj 6 0 obj 1506 endobj 19 0 obj <> stream xWKoFW,r U}y0ILVrɚ\Zqѝ\aHޙv曇&łÂvH t-V׾:v Lϐ8(*6oGբ_<9.G[04.nGQ:eUqml\`/°:vcޟ/{tlʔԺI*QWkAhoVRc9=z-3t)e`sń:[zz ASq0soc]}q>TXNF#,Ìb`FRpv^+ܻ7bW3. B:+fVzɔ$E>,)ѹk)3PE t CΏ:x=D!۔0ĄQ=+pDNߌ*uLSww@!Bhy+T6:d嶻2nSx(ElF|Ζ6~`VU2];Ji je'$Bhпǥ4`c6;`:= T"&ҩWH GO Pm!v1 $M%k績G2(iZ&YUڹnc][g tLJG]?Keze%q TAH<[yBZ4Is/4>ODǝU(*VC|*GǦ.7ޏg7ȧPïGuW8Jf8yF®n> stream xWˎ6+) c]tI )(YJdQdkE`>^"-s<1_iٗի6-qng0E!8(6̬8fGӌz· +EeGyRQ~2uy.I`nL/°:hc&,QqjP4~҂meԁEZ"At1| iJŶӮs)f\dZ[!ܶCV'i ;4hCg|na me@9,G|:|rͰ.{s;|Lmpx0A6XQ 0qGIF̆>0B+*,:9D]igyȒ㜹m6 ,ޯ6XoJkTDO$&O>Ͱ4FS>PB^V yj7P91p ef2PD b/:)X˼9 :jKŤu`=J炋mګ>A^-16d׻ټ w]K_ʪ6eltkVf3G)[ͣu؞k/\nL꽋=cab6.oA]{\y>5,ۺ'E-nЧCU *QU`maڔJ}o3(^+o; Q[`LYThrUCbk8 b:UP ym0(j)!۞GjkCkr&I mDnRU.Y\.9ԲKxHum*wyrV SuzֵZ"4[Dg賹LbkiUs6^b=:Ex'%Hn_%Wm.h Z~T\~l}Ąq;3 3(7A6mFqzJcZjz4=> stream xWMs6W`r ر`|Lqӱcd6$!S7Gw"%E$݇'D C|Q=]EWR7 |Լ̐ϑ(*FcJ6#/gtٽDϕ~y9P颈trf$ h8<#t S\R9cΤT:%*u ! 2%#.Gxf/,pXƝ**%l0^uu Տ %*S$ϖ3Y Mѣt,/m pw[2rY ^34W*N"&õ)Ah@YT *feBY\!_+nͱ&M6mкu+8EL[&ZINVJ8+i1ci92HJujkv V*Ѡ=[l)`TPa{Bƒ0pU aV:v ZCSO *4JE 8dlӟEA=uϠ7dNh6v|( nnn[뫛n-2j!Yz@m;Ϯ~r~wf|ѲR߿9%a ] `FPmySgsiA(F}` G18{ ]y#;qÞдеޯxX;+P xgcpx 6ש!o0W]&+ࠃ$mR?`BV-;堻OQ]{]7d-e%e/`n(17 IxZ/9Lf6b02/sU̔ n w=wO4I>S4 >vendstream endobj 28 0 obj 1361 endobj 31 0 obj <> stream xuRn0+V XIEm@ɔ@J/)) a rvvfw` ĝ9 أgDWC>G׉{+4)$ (\4yG?!8;K|4/Z):%A4.$2>&?fYA2(u_$ECY:zces͓O &gd꾗-gk*X>HF(YRR["j5< +PwFp{iY?4,IghSV4䦗ը-ݏT3p٥l?ȃ8c8nj7_[NB |XɢF?ɲ(27 =c($1J0trhKivskI:F{on6&&D[Zv6.* a;i*|J6u;bm|:BA< ]\RBqz]=#$fuk-B۔endstream endobj 32 0 obj 509 endobj 4 0 obj <> /Contents 5 0 R >> endobj 18 0 obj <> /Contents 19 0 R >> endobj 22 0 obj <> /Contents 23 0 R >> endobj 26 0 obj <> /Contents 27 0 R >> endobj 30 0 obj <> /Contents 31 0 R >> endobj 3 0 obj << /Type /Pages /Kids [ 4 0 R 18 0 R 22 0 R 26 0 R 30 0 R ] /Count 5 >> endobj 1 0 obj <> endobj 17 0 obj <> endobj 21 0 obj <> endobj 25 0 obj <> endobj 29 0 obj <> endobj 33 0 obj <> endobj 7 0 obj <> endobj 39 0 obj <> endobj 15 0 obj <> endobj 40 0 obj <> endobj 13 0 obj <> endobj 11 0 obj <> endobj 41 0 obj <> endobj 9 0 obj <> endobj 8 0 obj <> endobj 34 0 obj <>stream xyyXS׺!{ZJMDmkmkUu8U@fAy&@< (CkZNJviM=]~{s^}a"&,kAqa\X̜I\t̍` Lq3g&l LN8XwXy{Ѣw7'd$EG[lyZ7M!-4&>!64.ewdlpj O JwgSG$mMޖ=uGA`#$!C \?XÅomx{c݃.]\⽕5bJN% k&I,$o^ۄ7C,"|?b3'܈%DXJl%ۈvb1I"܉]jb7E61p!Hb.!$hBAydb>%i+Fb:1p&tx3@ '"?;)p rJti'')Z&ӓK&KSM)ԝSۧM;W/^9c$gsn$3_5͜5sfqfŸ_o w]]:]<3}9kι=盹/;cYcޕC&UQmddI2؉Tdb_I[ |md"k9 IŰ"`#Q#g o`+fg,4lNz6fR(|U?D HvnާFqG*PfP F_ ~RKOd46 >e325HRUFl8 !?'GAmmFkҿ!d|y*yBPlA2|#[ ^(Jʻ@Zt<M{v- ~\ᚋ7 nVuwڏ`@ې`l>u G`P)Oϥ4 BDgg*NPB >d -T̊FVӷ[641yhEH<]OC/*F/,BsyhƐ4Qp 9Bl{/ 7{`|xY0Fj2 HM|rٵˮХMa(@s`:0#kxw@'x ipZH5_#z cU̴_'%(FH)8Fut -߼?Pt"%Lb(En7#(Tcp k>9x{hzIE7n>,@ro.bh'+~8?Rb@[rh TR^VYO3?PJ#s䪬Ԍ(E  EiW4|-Q;&cs6O?^ &%ԨDQJ@i @XD ]p]tvY ̅s-Un쨄n\93]4;(;&TRRGO6XbMjՖVؖf O'=SeJ1X]g !hܕN)%LR\q &B6 4?ҿRno6d[4 /e2AUάq>uޘ:r6[2*S SggQ F5y0-+!EyF׷Xu54]]9dr۞N"ƗKkAa\s:ΨkKn5ӕEL hB2q8 9ӒFu8 B+$\ HAxLLHQ:$GoʨR!FԺz5TT`t=YIeێȍ_Xp|'d \G|2;bqqHJT@ T:U-d/Min)@EDې֙Uo)oԘԦl6i9y:([|7]/07-ftC:8WL>OTuH(̲֩D@'$Şĥp!qujWH5ʄ(H%P]l>nJ/$H[4HW#_]J Uվȷ @>(Z0!-Ԩ5[z¯8jʷGPӘs~Gxpݟ$2V uP`X$M<\Ћ7TV٫zHg3Z;%MŻ3"AaK}U×>vPeKh)FX2M$KNtD|{(2\//${6l ^3^GpJeC(RYW2P&h5 yz/*[e.m%\#R -~@o\cU\.QqsAIi>|;4/Oc5VR(`2/_a}6 ӿfc`YBV\ɤ &P^KVhz$:"҅N=;T*akL6F쀝>'mxt?:#NyÉCo'MNJJMQxǠA)krբwP`4Q6s'XF+ %Dz5W5)L-&e0g32,'E$_ FDX[Z\Tk8:&ibe3e-^RxY -UH}ÃE~Z/ce-† ?9{7GH8nD9J#w(|Jj}JwYHRZq?}J庿XnsT7ƛ֒[{A33ż]xyeE$$a56dž~rdNYyBUT4FVTq'k@AssH+4 ,`ZF&;4ۃЫWBnvN*/.Ey@VDUW{=ӡ;gNaQڳrv뽁Y\񘏣\ 1@["1ͧѷ8s1J{mPu+gjHo%͎ڨ>qd15 vp)\FUwk΃U><\i$ R?~qG|W|jKVN`g'NV;fd?'֥gPSzQ\; |ȭ;s|]M}Sif6j6b j?fK` {#4ti}~}FW[@*DŽH1XVCN)۔ږKsCeꦦ8lN:S ꋴ;ʞxIK#sRs H$44,0%J"b2X; >Rj私$z)d3T\JR s*x'iw_v ۗ"Mk=raKtib^lLp(ODOs5J{:3`3iX$l_$eF%˖aS%U52 & IjtO@oi]:Osmw( ɭx36NUjĢkI \DjzN-YYU2gsI,j^;Qy&tCIQ@w C{&)WP2i(b)ʠSh175Щ˟2G_ +K.hYCCqYRAK 㝁E O޹n p`;b֖n|x lӄJbvln6xF \ a":5 mz C{G-. K)ť-/QvL&eì}3Ôn bՀUTkh<{dr *jgr$&Gr*T dh8ngWK-|Pl(1+$+~|o#as~q R*YOFv#F24저l/@uFXJRz:p=FyY* ֊>WF)^z_oJ` N~o>/aƹ/p$줿Foq5b%3@ (\n/G9PIPţ{>`| na%p>s("9%2Zm.0 F.,dGŇ{76U8spd̙' +R0G Ėd+њTUptR\nqF*l9u.##;'y2X1S`# ^@AY'= O`VvmeB^ծw^P:m-㯽ߙp;SQå͝7;vw;̺~$p\,v unygJMd KZ\ZWVVWJEҜB`[O\:|~aN_((΋MINkijnN xF[x?W_հ{|"ڧUѸv{F&xM;aWL}z~<8CzN;;:va|;KT sECC4Dt_#Qnk1 kJBS@pDdFDxcDs o8.tŝp1FtEW.GKW`ȍECƕרBt~}Ihg*Nw[;A-ԱÆc3xu( 0niٰ![IZ /2`HQg¬]GwHbD&>_>~& zJUK.PF+* utn0+'hʄK5߰/?\ Ӭ2i왿9e=u*ãùOZd1!n^PDMڤ6D <|G! T-,an"o6ܡ(4pުU!0D4ƦFA<6@R?A3?SF}1[*4Z4G% 8? M;Lm sy}JeptipOw_z$L)Uihg=V;t ڊqlǮ-w~~?&6p1|Ļjt5YWR=V}ϝ͵a¸ YTџz}u`8s0%Zl=\Mu]gf>/P.&{okO F$%bҶo«̇vgxX310HOqvv> >'<&Ǭ9wlyd90L,M*PJzht=jQ!†w=ץgcѵ@Iwl8uJtX9p AiRƻ*%'?z4e)S ?ͥ endstream endobj 16 0 obj <> endobj 35 0 obj <>stream xmL[UϥutZv&.n n!1śn!&sc:c Bmàm)-#n20L0sKbH'ɒ}Q`ƄD3!&~s_%+3tc:`ȻEȌV{}H)q$/+< S*{_͗K* vݸ~ R\@o / pC= jAH-6MP qRԈL(3nHPm,o?"vUCK^*OjQo^Dž'̉ O_G{פ2QΝes$MjPVȗ≇C|b@[vA'a@`Xumt? lIjCM8pBd}}ձTԗH=,lY( {E(sD<2 0v} :fV[yԹ~Wl  kh*m|bRn+J<܌1 I"|}Sl)H?WmGg<`S ǚLݞȈz>ե B7F$m$wc!$N+=#YonmllX|zribT LuU޼=ET#Ɠ와\>oo55 endstream endobj 14 0 obj <> endobj 36 0 obj <>stream xU{PSg15ZF[Ȣ0b:vj]EW!@ `B@B XU_/u]TYm;uw :u_4?vOs~,㽺.0?35iLrV'nR.--䗠gaf 9ڇ>.s^x ZÊW bK+LϐI [&M.NO-LϑfeȢ3 rsrͲ}YM 7'7/@VXoJjZzFfV6ADO D">$6Mf"FD! j;YF|Op@0)+Jgom7Ǎ>dynd29Fn@dcp20l7L*9_=jТk)JA1PJ.r܁rO Av@C ̒ф+Bc >ayO]l[EQ0I}y0.?]} uWJ bư108b,Mc}?S7qDn`+0VPvrHP }o*`r_NYJO^I zstvcg9& hLVg# ˔eVm GtU&9"0EHI:M㔙4Uf rmuS)n@"H_ZY>g302T\%6P)1Vk[]=:uQ1&F,?Ve4FGX(2.:EZe+mѶȁ*;@ۨ8mh j%lSW::זƄ˚LgQmKשk/wT4ʁB握?6յԈTeJUg`}2?{C`;=t #I#kʨT ܱ5Xq?nJ^ES}"JwdtcehS1/by\ߘ6M@ o MQ:Tm‰C޵j* 4[^p ^"p x\#Q kdzoare}qǸ2.orvCЌKwvyyy]ӪRj@vOXɔoIe""zG#T]/[D?8~(8wFaf^s0<'g D(Iɑ[Ξk̩G h5i U :zwl-ų/wfκ>?v Tm pjzQmuXC;uIP䠢BUdg󐅯gqOAaֵae؃Ӈ?8o]ׯ3t3EG+.-cwbEx&@P8-iX0e (6؀C{! ¡Ͽt*5@uQ8Tmf9='{<|nx_4#9Pq12"ٓ7Ԙ'rؚVIYdo4we| /xjG"ɗ ='YWv݀:݊|2rLT>nk<[f$HӘmLD=H* Ns%ExU⥻|\-Fu o-N{Kk{r/]">'k8 Zr|~*m_ /fRHjj֩"XzfA![VU/ LHV|ѳ_: 7M vtISc0zf#';lIPw{4keQiR*ۀ ?SWa;V+Vi=sym{A X&h~ 5\+>>MѲ endstream endobj 12 0 obj <> endobj 37 0 obj <>stream xoLeǟz2Vd4$a 4nY"ɔM! TRR(P^[&iw+йd,Na[mF+㟘,KdKn/|,썹7KJQQ-*Uߩ. Q~15A؜zt {]~z˯J{堟/fcvciч'-'};q uXGoC(\=PU-׊6nUPuE\&K_r ďUT5KwRhߑ>}Kzݶ[ KZ1e2fa>RT^ ^8 9_CqY9 eGxv6sADg7u\9 n-ۡ-ιC)jZ7= tT|4>O( 3Aץr%$j@4Q~S8B `J/x0eY|B}5Pshk}P5y~aR!09!Gɛj~E |C9?ӱBbyzRG2c𠻵@;U5$\e(Νf SJ<4!Pԕ.O 4֛s-q}u􋒡iyp.r~a }Qu#g?]tܙhHgB̴gr Cٱ4BK8>%JA nbe>$-9Ð -E)(/` %XFKyO2 )> V1xϔ"hn^YK 9rF# oܣ*8ǧoIR!28V1J9wi0`"Sq)8ۼ66ogI >jV_G endstream endobj 10 0 obj <> endobj 38 0 obj <>stream x{PTe{N eRMcVA /)h$(<7 ,.Ⲁfyyʬ`ܙ +6r{í[-̺ݵp8r;} 쳳0^$ð }>8TAeGOj@ɣ62@:sЏ3h2Mf̌5p9Bg{BKr; aqб]F 4\mWQcNq*Q> Vd-k'ok 2 YAPdSQZd=3E zg_OېW8^d!LXe1Q9E :uyvM>yDgԳe%O0^7 ւR!*P Vch8HYIBVT%')+pS"`(L ˡ}Vsr;unYBc&rgEpcBXr)b< 92(ӀF;Zf`t<$:os^ݠg0Gu8ū"{&_#!zc*Mc\ OodYhk_8^^ Hk\^\a :1Oj\ݕXRF\#hЍڟ,4Gg0B -ERc˥K}T / n,g]S U_ !y& ^Ets rfo[=̥DŽ\)mpeD,]uyêoc!@zلL`q.M.g׽^ݬ1ca=✗{uFѠj{1 cD*KinÕXT#sIchâcOU_4[48N`MRev.i4%S,(!`xE%;>wu쑈Ks?ij1`UPd'?!_`oҝrj6e/vF#sT6T*L? *)Y*i3Zė#x67+ 6;125:;>stream 2023-04-23T21:03:11+08:00 2023-04-23T21:03:11+08:00 groff version 1.22.4 Untitled endstream endobj 2 0 obj <>endobj xref 0 43 0000000000 65535 f 0000007370 00000 n 0000026238 00000 n 0000007283 00000 n 0000006565 00000 n 0000000182 00000 n 0000001758 00000 n 0000007687 00000 n 0000009624 00000 n 0000009388 00000 n 0000022849 00000 n 0000008983 00000 n 0000021496 00000 n 0000008742 00000 n 0000019112 00000 n 0000008330 00000 n 0000017751 00000 n 0000007435 00000 n 0000006707 00000 n 0000001778 00000 n 0000003090 00000 n 0000007507 00000 n 0000006851 00000 n 0000003111 00000 n 0000004489 00000 n 0000007557 00000 n 0000006995 00000 n 0000004510 00000 n 0000005943 00000 n 0000007598 00000 n 0000007139 00000 n 0000005964 00000 n 0000006545 00000 n 0000007648 00000 n 0000010160 00000 n 0000018033 00000 n 0000019411 00000 n 0000021759 00000 n 0000023097 00000 n 0000008210 00000 n 0000008654 00000 n 0000009300 00000 n 0000024977 00000 n trailer << /Size 43 /Root 1 0 R /Info 2 0 R /ID [<0DF84B6B619C4B53BAB8BB46F639FD1D><0DF84B6B619C4B53BAB8BB46F639FD1D>] >> startxref 26394 %%EOF 22:13:34.454 ApvlvFrame.cc:114 apvlv::ApvlvFrame::ApvlvFrame(apvlv::ApvlvView*) ApvlvFrame: apvlv::ApvlvFrame(0xdc42630) be created 22:13:34.622 ApvlvFrame.cc:527 void apvlv::ApvlvFrame::ocrParse() widget is apvlv::ApvlvImage 22:13:34.633 ApvlvFrame.cc:183 void apvlv::ApvlvFrame::toggleContent(bool) file "/usr/share/doc/apvlv/Startup.pdf" has no content 22:13:34.633 ApvlvWindow.cc:40 apvlv::ApvlvWindow::ApvlvWindow() win: apvlv::ApvlvWindow(0xdd52950) , init 22:13:34.633 ApvlvWindow.cc:428 void apvlv::ApvlvWindow::setFrame(apvlv::ApvlvFrame*) win: apvlv::ApvlvWindow(0xdd52950) , set core: apvlv::ApvlvFrame(0xdc42630) 22:13:34.633 ApvlvView.cc:1260 void apvlv::ApvlvView::tabSwitched(int) tabwidget switch to: 0 22:13:37.110 ApvlvFrame.cc:183 void apvlv::ApvlvFrame::toggleContent(bool) file "/usr/share/doc/apvlv/Startup.pdf" has no content 22:13:37.811 ApvlvFrame.cc:183 void apvlv::ApvlvFrame::toggleContent(bool) file "/usr/share/doc/apvlv/Startup.pdf" has no content 22:13:39.879 ApvlvFrame.cc:119 virtual apvlv::ApvlvFrame::~ApvlvFrame() ApvlvFrame: apvlv::ApvlvFrame(0xdc42630) be freed 22:13:39.879 ApvlvImageWidget.cc:239 virtual apvlv::ApvlvImage::~ApvlvImage() ApvlvImage: apvlv::ApvlvImage(0xdc9c330) be freed 22:13:39.902 ApvlvWindow.cc:47 virtual apvlv::ApvlvWindow::~ApvlvWindow() win: apvlv::ApvlvWindow(0xdd52950) , released apvlv-0.7.0-alpha/src/0000755000175100017510000000000014726051136015045 5ustar atzlinuxatzlinuxapvlv-0.7.0-alpha/src/ApvlvView.cc0000644000175100017510000007321414726051136017306 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * * Copyright (C) 2008 Alf. * * Contact: Alf * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2.0 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ /* @CPPFILE ApvlvView.cc * * Author: Alf */ #include #include #include #include #include #include #include "ApvlvDired.h" #include "ApvlvInfo.h" #include "ApvlvParams.h" #include "ApvlvSearchDialog.h" #include "ApvlvView.h" namespace apvlv { using namespace std; using namespace CommandModeType; static bool isAltEscape (QKeyEvent *event) { if (event->key () == Qt::Key_BracketLeft && /* ...so we can ignore mod keys like numlock and capslock. */ (event->modifiers () & Qt::ControlModifier)) { return true; } return false; } void ApvlvCommandBar::keyPressEvent (QKeyEvent *evt) { if (evt->key () == Qt::Key_Escape || isAltEscape (evt) || evt->key () == Qt::Key_Tab || evt->key () == Qt::Key_Up || evt->key () == Qt::Key_Down) { emit keyPressed (evt); return; } QLineEdit::keyPressEvent (evt); } bool ApvlvCommandBar::eventFilter (QObject *obj, QEvent *event) { if (event->type () == QEvent::KeyPress && dynamic_cast (event)->key () == Qt::Key_Tab) { qDebug () << "Ate key press tab"; emit keyPressed (dynamic_cast (event)); return true; } else { return QObject::eventFilter (obj, event); } } ApvlvView::ApvlvView (ApvlvView *parent) : mCmds (this) { mParent = parent; if (mParent) { mParent->appendChild (this); } auto guiopt = ApvlvParams::instance ()->getStringOrDefault ("guioptions", "mTS"); mCmdType = CmdStatusType::CMD_NONE; mProCmd = 0; mProCmdCnt = 0; mCurrHistroy = -1; mHasFull = false; keyLastEnd = true; processInLast = false; int w = ApvlvParams::instance ()->getIntOrDefault ("width"); int h = ApvlvParams::instance ()->getIntOrDefault ("height"); if (ApvlvParams::instance ()->getBoolOrDefault ("fullscreen")) { fullScreen (); } else { resize (w > 1 ? w : 800, h > 1 ? h : 600); } show (); setCentralWidget (&mCentral); setupMenuBar (guiopt); setMenuBar (&mMenuBar); setupToolBar (); addToolBar (Qt::TopToolBarArea, &mToolBar); bool isMenuBarShow = guiopt.find ('m') != string::npos; if (!isMenuBarShow) { mMenuBar.hide (); } bool isToolBarShow = guiopt.find ('T') != string::npos; if (!isToolBarShow) { mToolBar.hide (); } mCentral.setLayout (&mVBoxLayout); mTabContainer.setTabBarAutoHide (true); mVBoxLayout.addWidget (&mTabContainer, 1); QObject::connect (&mTabContainer, SIGNAL (currentChanged (int)), this, SLOT (tabSwitched (int))); mVBoxLayout.addWidget (&mCommandBar, 0); QObject::connect (&mCommandBar, SIGNAL (textEdited (const QString &)), this, SLOT (commandbarEdited (const QString &))); QObject::connect (&mCommandBar, SIGNAL (returnPressed ()), this, SLOT (commandbarReturn ())); QObject::connect (&mCommandBar, SIGNAL (keyPressed (QKeyEvent *)), this, SLOT (commandbarKeyPressed (QKeyEvent *))); cmdHide (); } ApvlvView::~ApvlvView () { if (mParent) { mParent->eraseChild (this); } auto itr = mChildren.begin (); while (itr != mChildren.end ()) { delete *itr; itr = mChildren.begin (); } mCmdHistroy.clear (); } ApvlvWindow * ApvlvView::currentWindow () { auto index = mTabContainer.currentIndex (); if (index < 0) return nullptr; auto root_win = dynamic_cast (mTabContainer.widget (index)); Q_ASSERT (root_win); auto widget = QApplication::focusWidget (); if (widget) { auto win = root_win->findWindowByWidget (widget); if (win) { root_win->setActiveWindow (win); return win; } } auto win = root_win->getActiveWindow (); if (win) return win; return root_win->firstFrameWindow (); } void ApvlvView::delCurrentWindow () { if (auto win = currentWindow (); win) win->perish (); updateTabName (); } void ApvlvView::open () { QString dirname; auto fp = ApvlvInfo::instance ()->lastFile (); if (fp) { dirname = QString::fromLocal8Bit ( filesystem::path (fp.value ()->file).parent_path ().string ()); } else { dirname = QDir::homePath (); } // qDebug ("lastfile: [%s], dirname: [%s]", fp ? fp->file.c_str () : "", // dirname); auto const mimes = FileFactory::supportMimeTypes (); QString filters; for (const auto &m : mimes) { filters += m.first.c_str (); filters += "("; filters += ('*' + m.second[0]); for (decltype (m.second.size ()) index = 1; index < m.second.size (); ++index) filters += (" *" + m.second[index]); filters += ");;"; } QString selected; auto filename = QFileDialog::getOpenFileName (this, "open file", dirname, filters, &selected); if (!filename.isEmpty ()) { loadFile (string (filename.toLocal8Bit ().constData ())); } } void ApvlvView::openDir () { QString dirname; auto fp = ApvlvInfo::instance ()->lastFile (); if (fp) { dirname = QString::fromLocal8Bit ( filesystem::path (fp.value ()->file).parent_path ().string ()); } else { dirname = QDir::homePath (); } auto filename = QFileDialog::getExistingDirectory ( this, "open dir", dirname, QFileDialog::ShowDirsOnly); if (!filename.isEmpty ()) { loadDir (filename.toStdString ()); } } void ApvlvView::openRrl () { auto res = QInputDialog::getText (this, "url", tr ("input url: ")); if (!res.isEmpty ()) { currentFrame ()->loadUri (res.toStdString ()); } } bool ApvlvView::loadDir (const std::string &path) { currentFrame ()->setDirIndex (path); return true; } void ApvlvView::quit (bool only_tab) { if (mTabContainer.count () <= 1 || only_tab == false) { closeEvent (nullptr); return; } auto index = mTabContainer.currentIndex (); Q_ASSERT (index != -1); mTabContainer.removeTab (index); } void ApvlvView::search () { promptCommand ('/'); } void ApvlvView::backSearch () { promptCommand ('?'); } void ApvlvView::advancedSearch () { auto diag = SearchDialog (this); QObject::connect (&diag, SIGNAL (loadFile (const string &, int)), this, SLOT (loadFileOnPage (const string &, int))); diag.exec (); } void ApvlvView::dired () { auto diag = DiredDialog (this); QObject::connect (&diag, SIGNAL (loadFile (const string &, int)), this, SLOT (loadFileOnPage (const string &, int))); diag.exec (); } bool ApvlvView::newTab (const std::string &filename) { auto docname = filename; if (filesystem::is_directory (filename)) { docname = HelpPdf; } auto optndoc = hasLoaded (docname); if (!optndoc) { auto ndoc = new ApvlvFrame (this); if (!ndoc->loadFile (docname, true, true)) { delete ndoc; ndoc = nullptr; } if (ndoc) { regLoaded (ndoc); optndoc = make_optional (ndoc); } } if (optndoc) { newTab (optndoc.value ()); if (filesystem::is_directory (filename)) { loadDir (filename); } return true; } else { return false; } } bool ApvlvView::newTab (ApvlvFrame *core) { auto win = new ApvlvWindow (); win->setFrame (core); auto basename = core->filename () ? filesystem::path (core->filename ()).filename ().string () : "NONE"; auto pos = mTabContainer.currentIndex () + 1; mTabContainer.insertTab (pos, win, QString::fromLocal8Bit (basename)); mTabContainer.setCurrentIndex (pos); return true; } bool ApvlvView::loadFile (const std::string &filename) { auto abpath = filesystem::absolute (filename).string (); ApvlvWindow *win = currentWindow (); ApvlvFrame *ndoc = nullptr; auto optndoc = hasLoaded (abpath); if (!optndoc) { ndoc = new ApvlvFrame (this); if (!ndoc->loadFile (filename, true, true)) { delete ndoc; ndoc = nullptr; } else { regLoaded (ndoc); optndoc = make_optional (ndoc); } } if (optndoc) { win->setFrame (optndoc.value ()); updateTabName (); } return ndoc != nullptr; } void ApvlvView::loadFileOnPage (const string &filename, int pn) { auto cdoc = currentFrame (); if (cdoc) { if (loadFile (filename)) { cdoc->showPage (pn, 0.0); } } } optional ApvlvView::hasLoaded (string_view abpath) { for (auto &core : mDocs) { if (!core->inuse () && abpath == core->filename ()) { return make_optional (core.get ()); } } return nullopt; } void ApvlvView::regLoaded (ApvlvFrame *doc) { auto cache_count = ApvlvParams::instance ()->getIntOrDefault ("cache_count", 10); if (mDocs.size () >= static_cast (cache_count)) { auto found_itr = mDocs.end (); for (auto itr = mDocs.begin (); itr != mDocs.end (); ++itr) { if ((*itr)->inuse () == false) { found_itr = itr; break; } } if (found_itr != mDocs.end ()) { mDocs.erase (found_itr); } } mDocs.push_back (unique_ptr (doc)); } void ApvlvView::setupMenuBar (const string &guiopt) { // File -> open, openDir auto mfile = new QMenu (tr ("File")); mMenuBar.addMenu (mfile); auto action = mfile->addAction (tr ("Open")); QObject::connect (action, SIGNAL (triggered (bool)), this, SLOT (open ())); action = mfile->addAction (tr ("OpenDir")); QObject::connect (action, SIGNAL (triggered (bool)), this, SLOT (openDir ())); action = mfile->addAction (tr ("OpenUrl")); QObject::connect (action, SIGNAL (triggered (bool)), this, SLOT (openRrl ())); action = mfile->addAction (tr ("New Tab")); QObject::connect (action, SIGNAL (triggered (bool)), this, SLOT (newTab ())); action = mfile->addAction (tr ("Close Tab")); QObject::connect (action, SIGNAL (triggered (bool)), this, SLOT (closeTab ())); action = mfile->addAction (tr ("Quit")); QObject::connect (action, SIGNAL (triggered (bool)), this, SLOT (quit (bool))); // Edit -> auto medit = new QMenu (tr ("Edit")); mMenuBar.addMenu (medit); action = medit->addAction (tr ("Search")); QObject::connect (action, SIGNAL (triggered (bool)), this, SLOT (search ())); action = medit->addAction (tr ("Back Search")); QObject::connect (action, SIGNAL (triggered (bool)), this, SLOT (backSearch ())); // View -> auto mview = new QMenu (tr ("View")); mMenuBar.addMenu (mview); action = mview->addAction (tr ("ToolBar")); action->setCheckable (true); QObject::connect (action, SIGNAL (triggered (bool)), this, SLOT (toggleToolBar ())); bool isToolBarShow = guiopt.find ('T') != string::npos; action->setChecked (isToolBarShow); action = mview->addAction (tr ("StatusBar")); action->setCheckable (true); QObject::connect (action, SIGNAL (triggered (bool)), this, SLOT (toggleStatus ())); bool isStatusShow = guiopt.find ('S') != string::npos; action->setChecked (isStatusShow); action = mview->addAction (tr ("Horizontal Split")); QObject::connect (action, SIGNAL (triggered (bool)), this, SLOT (horizontalSplit ())); action = mview->addAction (tr ("Vertical Split")); QObject::connect (action, SIGNAL (triggered (bool)), this, SLOT (verticalSplit ())); action = mview->addAction (tr ("Close Split")); QObject::connect (action, SIGNAL (triggered (bool)), this, SLOT (unBirth ())); // Navigate -> auto mnavigate = new QMenu (tr ("Navigate")); mMenuBar.addMenu (mnavigate); action = mnavigate->addAction (tr ("Previous Page")); QObject::connect (action, SIGNAL (triggered (bool)), this, SLOT (previousPage ())); action = mnavigate->addAction (tr ("Next Page")); QObject::connect (action, SIGNAL (triggered (bool)), this, SLOT (nextPage ())); // Tools auto mtools = new QMenu (tr ("Tools")); mMenuBar.addMenu (mtools); action = mtools->addAction (tr ("Advanced Search")); QObject::connect (action, SIGNAL (triggered (bool)), this, SLOT (advancedSearch ())); action = mtools->addAction (tr ("Dired")); QObject::connect (action, SIGNAL (trigged (bool)), this, SLOT (dired ())); // Help -> about auto mhelp = new QMenu (tr ("Help")); mMenuBar.addMenu (mhelp); } void ApvlvView::setupToolBar () { auto action = mToolBar.addAction (tr ("Open")); action->setIcon ( QApplication::style ()->standardIcon (QStyle::SP_DialogOpenButton)); QObject::connect (action, SIGNAL (triggered (bool)), this, SLOT (open ())); action = mToolBar.addAction (tr ("OpenDir")); action->setIcon ( QApplication::style ()->standardIcon (QStyle::SP_DirOpenIcon)); QObject::connect (action, SIGNAL (triggered (bool)), this, SLOT (openDir ())); action = mToolBar.addAction (tr ("Toggle Directory")); QObject::connect (action, SIGNAL (triggered (bool)), this, SLOT (toggleDirectory ())); action = mToolBar.addAction (tr ("Quit")); QObject::connect (action, SIGNAL (triggered (bool)), this, SLOT (quit (bool))); } void ApvlvView::promptCommand (char ch) { QString s{ ch }; mCommandBar.setText (s); cmdShow (CmdStatusType::CMD_CMD); } void ApvlvView::promptCommand (const char *str) { mCommandBar.setText (str); cmdShow (CmdStatusType::CMD_CMD); } char * ApvlvView::input (const char *str, int width, int height, const string &content) { // need impl return nullptr; } void ApvlvView::cmdShow (CmdStatusType cmdtype) { mCmdType = cmdtype; mCommandBar.show (); mCommandBar.setCursorPosition (1); mCommandBar.setFocus (); } void ApvlvView::cmdHide () { mCmdType = CmdStatusType::CMD_NONE; mCmdTime = chrono::steady_clock::now (); mCommandBar.hide (); mTabContainer.setFocus (); } void ApvlvView::cmdAuto (const char *ps) { stringstream ss (ps); string cmd; string np; string argu; ss >> cmd >> np >> argu; if (np.empty ()) { return; } if (np[np.length () - 1] == '\\') { np.replace (np.length () - 1, 1, 1, ' '); np += argu; ss >> argu; } if (cmd.empty () || np.empty ()) { return; } auto comp = ApvlvCompletion{}; if (cmd == "o" || cmd == "open" || cmd == "TOtext") { comp.addPath (np); } else if (cmd == "doc") { vector items; for (auto &doc : mDocs) { items.emplace_back (doc->filename ()); } comp.addItems (items); } qDebug () << "find match: " << np; auto comtext = comp.complete (np); if (!comtext.empty ()) { qDebug () << "get a match: " << comtext; QString s = QString::fromLocal8Bit (comtext); s.replace (" ", "\\ "); QString linetext = QString::asprintf (":%s %s", cmd.c_str (), s.toStdString ().c_str ()); mCommandBar.setText (linetext); } else { qDebug () << "no get match"; } } void ApvlvView::fullScreen () { if (mHasFull == false) { ApvlvFrame *core = currentFrame (); if (core) core->toggleDirectory (false); showFullScreen (); mHasFull = true; } else { showNormal (); mHasFull = false; } } void ApvlvView::nextPage () { currentFrame ()->nextPage (1); } void ApvlvView::previousPage () { currentFrame ()->previousPage (1); } void ApvlvView::toggleDirectory () { currentFrame ()->toggleDirectory (); } void ApvlvView::toggleToolBar () { if (mToolBar.isHidden ()) { mToolBar.show (); } else { mToolBar.hide (); } } void ApvlvView::toggleStatus () { for (auto &doc : mDocs) { if (doc->isStatusHidden ()) { doc->statusShow (); } else { doc->statusHide (); } } } void ApvlvView::newTab () { auto doc = currentFrame ()->clone (); newTab (doc); } void ApvlvView::closeTab () { quit (true); } void ApvlvView::horizontalSplit () { currentWindow ()->birth (ApvlvWindow::WindowType::SP, nullptr); } void ApvlvView::verticalSplit () { currentWindow ()->birth (ApvlvWindow::WindowType::VSP, nullptr); } void ApvlvView::unBirth () { currentWindow ()->perish (); } ApvlvFrame * ApvlvView::currentFrame () { if (auto widget = QApplication::focusWidget (); widget) { if (auto doc = ApvlvFrame::findByWidget (widget); doc) return doc; } ApvlvWindow *curwin = currentWindow (); Q_ASSERT (curwin); return curwin->getFrame (); } CmdReturn ApvlvView::subProcess (int times, uint keyval) { uint procmd = mProCmd; mProCmd = 0; mProCmdCnt = 0; switch (procmd) { case 'Z': if (keyval == 'Z') quit (true); case ctrlValue ('w'): if (keyval == 'q' || keyval == ctrlValue ('Q')) { if (currentWindow ()->isRoot ()) { quit (true); } else { delCurrentWindow (); } } else { CmdReturn rv = currentWindow ()->process (times, keyval); updateTabName (); return rv; } break; case 'g': if (keyval == 't') { if (times == 0) switchTab (mTabContainer.currentIndex () + 1); else switchTab (times - 1); } else if (keyval == 'T') { if (times == 0) switchTab (mTabContainer.currentIndex () - 1); else switchTab (times - 1); } else if (keyval == 'g') { if (times == 0) times = 1; currentFrame ()->showPage (times - 1, 0.0); } break; default: return CmdReturn::NO_MATCH; } return CmdReturn::MATCH; } CmdReturn ApvlvView::process (int has, int ct, uint key) { if (mProCmd != 0) { auto ret = subProcess (mProCmdCnt, key); if (ret == CmdReturn::MATCH) { saveKey (has, ct, key, true); } return ret; } switch (key) { case 'Z': mProCmd = 'Z'; return CmdReturn::NEED_MORE; case ctrlValue ('w'): mProCmd = ctrlValue ('w'); mProCmdCnt = has ? ct : 1; saveKey (has, ct, key, false); return CmdReturn::NEED_MORE; case 'q': quit (true); break; case 'f': fullScreen (); break; case '.': processLastKey (); break; case 'g': mProCmd = 'g'; mProCmdCnt = has ? ct : 0; saveKey (has, ct, key, false); return CmdReturn::NEED_MORE; default: CmdReturn ret = currentFrame ()->process (has, ct, key); if (ret == CmdReturn::NEED_MORE) { saveKey (has, ct, key, false); } else if (ret == CmdReturn::MATCH) { saveKey (has, ct, key, true); } } return CmdReturn::MATCH; } bool ApvlvView::run (const char *str) { bool ret; switch (*str) { case SEARCH: currentFrame ()->markposition ('\''); ret = currentFrame ()->search (str + 1, false); break; case BACKSEARCH: currentFrame ()->markposition ('\''); ret = currentFrame ()->search (str + 1, true); break; case COMMANDMODE: ret = runCommand (str + 1); break; case FIND: ret = currentFrame ()->search (str + 1, false); break; default: ret = false; break; } return ret; } void ApvlvView::setTitle (const std::string &title) { setWindowTitle (QString::fromLocal8Bit (title)); } bool ApvlvView::runCommand (const char *str) { bool ret = true; if (*str == '!') { system (str + 1); } else { stringstream ss (str); string cmd; string subcmd; string argu; ss >> cmd >> subcmd >> argu; if (!subcmd.empty () && subcmd[subcmd.length () - 1] == '\\') { subcmd.replace (subcmd.length () - 1, 1, 1, ' '); subcmd += argu; ss >> argu; } if (cmd == "set") { if (subcmd == "skip") { currentFrame ()->setSkip ( int (strtol (argu.c_str (), nullptr, 10))); } else { ApvlvParams::instance ()->push (subcmd, argu); } } else if (cmd == "map" && !subcmd.empty ()) { ApvlvCmds::buildCommandMap (subcmd, argu); } else if ((cmd == "o" || cmd == "open" || cmd == "doc") && !subcmd.empty ()) { char *home; if (subcmd[0] == '~') { home = getenv ("HOME"); if (home) { subcmd.replace (0, 1, home); } } if (filesystem::is_directory (subcmd)) { ret = loadDir (subcmd); } else if (filesystem::is_regular_file (subcmd)) { ret = loadFile (subcmd); } else { errorMessage (string ("no file: "), subcmd); ret = false; } } else if (cmd == "TOtext" && !subcmd.empty ()) { currentFrame ()->totext (subcmd.c_str ()); } else if ((cmd == "pr" || cmd == "print")) { currentFrame ()->print ( subcmd.empty () ? -1 : int (strtol (subcmd.c_str (), nullptr, 10))); } else if (cmd == "sp") { if (currentWindow ()->birth (ApvlvWindow::WindowType::SP, nullptr)) { windowAdded (); } } else if (cmd == "vsp") { if (currentWindow ()->birth (ApvlvWindow::WindowType::VSP, nullptr)) { windowAdded (); } } else if ((cmd == "zoom" || cmd == "z") && !subcmd.empty ()) { currentFrame ()->setZoomString (subcmd.c_str ()); } else if (cmd == "forwardpage" || cmd == "fp") { if (subcmd.empty ()) currentFrame ()->nextPage (1); else currentFrame ()->nextPage ( int (strtol (subcmd.c_str (), nullptr, 10))); } else if (cmd == "prewardpage" || cmd == "bp") { if (subcmd.empty ()) currentFrame ()->previousPage (1); else currentFrame ()->previousPage ( int (strtol (subcmd.c_str (), nullptr, 10))); } else if (cmd == "directory") { currentFrame ()->toggleDirectory (); } else if (cmd == "goto" || cmd == "g") { currentFrame ()->markposition ('\''); auto p = strtol (subcmd.c_str (), nullptr, 10); p += currentFrame ()->getSkip (); currentFrame ()->showPage (int (p - 1), 0.0); } else if (cmd == "help" || cmd == "h") { loadFile (HelpPdf); } else if (cmd == "q" || cmd == "quit") { if (currentWindow ()->isRoot ()) { quit (true); } else { delCurrentWindow (); } } else if (cmd == "qall") { quit (false); } else if (cmd == "tabnew") { newTab (HelpPdf); } else if (cmd == "tabn" || cmd == "tabnext") { switchTab (mTabContainer.currentIndex () + 1); } else if (cmd == "tabp" || cmd == "tabprevious") { switchTab (mTabContainer.currentIndex () - 1); } else if (cmd == "toc") { loadFile (currentFrame ()->filename ()); } else { bool isn = true; for (char i : cmd) { if (i < '0' || i > '9') { isn = false; break; } } if (isn && currentFrame ()) { auto p = strtol (cmd.c_str (), nullptr, 10); p += currentFrame ()->getSkip (); if (p != currentFrame ()->pageNumber ()) { currentFrame ()->showPage (int (p - 1), 0.0); } } else { errorMessage (string ("no command: "), cmd); ret = false; } } } return ret; } void ApvlvView::keyPressEvent (QKeyEvent *evt) { if (mCmdType != CmdStatusType::CMD_NONE) { return; } auto now = chrono::steady_clock::now (); auto millis = chrono::duration_cast (now - mCmdTime).count (); if (millis < 1000L) return; mCmds.append (evt); } void ApvlvView::commandbarEdited ([[maybe_unused]] const QString &str) { // need impl } void ApvlvView::commandbarReturn () { if (mCmdType == CmdStatusType::CMD_CMD) { auto str = mCommandBar.text (); if (!str.isEmpty ()) { if (run (str.toStdString ().c_str ())) { mCmdHistroy.emplace_back (str.toStdString ()); mCurrHistroy = mCmdHistroy.size () - 1; cmdHide (); } } else { cmdHide (); } } else if (mCmdType == CmdStatusType::CMD_MESSAGE) { cmdHide (); } } void ApvlvView::commandbarKeyPressed (QKeyEvent *gek) { if (gek->key () == Qt::Key_Tab) { auto str = mCommandBar.text (); if (!str.isEmpty ()) { cmdAuto (str.toStdString ().c_str () + 1); } } else if (gek->key () == Qt::Key_Backspace) { auto str = mCommandBar.text (); if (str.length () == 1) { cmdHide (); mCurrHistroy = mCmdHistroy.size () - 1; } } else if (gek->key () == Qt::Key_Escape || isAltEscape (gek)) { cmdHide (); mCurrHistroy = mCmdHistroy.size () - 1; } else if (gek->key () == Qt::Key_Up) { if (!mCmdHistroy.empty ()) { mCommandBar.setText (QString::fromLocal8Bit ( mCurrHistroy > 0 ? mCmdHistroy[mCurrHistroy--] : mCmdHistroy[0])); } } else if (gek->key () == Qt::Key_Down) { if (!mCmdHistroy.empty ()) { mCommandBar.setText (QString::fromLocal8Bit ( (size_t)mCurrHistroy < mCmdHistroy.size () - 1 ? mCmdHistroy[++mCurrHistroy] : mCmdHistroy[mCmdHistroy.size () - 1])); } } } void ApvlvView::closeEvent (QCloseEvent *evt) { if (mParent == nullptr) { QGuiApplication::exit (); } } void ApvlvView::tabSwitched (int pnum) { qDebug () << "tabwidget switch to: " << pnum; if (pnum == -1) return; auto adoc = currentFrame (); if (adoc && adoc->filename ()) { auto filename = filesystem::path (currentFrame ()->filename ()).filename (); setTitle (filename.string ()); } } void ApvlvView::switchTab (int tabPos) { int ntabs = mTabContainer.count (); while (tabPos < 0) tabPos += ntabs; tabPos = tabPos % ntabs; mTabContainer.setCurrentIndex (tabPos); } void ApvlvView::windowAdded () { auto index = mTabContainer.currentIndex (); Q_ASSERT (index != -1); updateTabName (); } void ApvlvView::updateTabName () { auto win = currentWindow (); if (win == nullptr) return; auto frame = win->getFrame (); if (frame == nullptr) return; const char *filename = frame->filename (); string gfilename; if (filename == nullptr) gfilename = "None"; else gfilename = filesystem::path (filename).filename ().string (); setTitle (gfilename); auto index = mTabContainer.currentIndex (); Q_ASSERT (index != -1); auto tagname = QString::fromLocal8Bit (gfilename); // need impl tagname = QString("[%1] %2").arg (mTabList[index]).arg // (gfilename.c_str()); mTabContainer.setTabText (index, tagname); } void ApvlvView::appendChild (ApvlvView *view) { mChildren.push_back (view); } void ApvlvView::eraseChild (ApvlvView *view) { auto itr = mChildren.begin (); while (*itr != view && itr != mChildren.end ()) itr++; if (*itr == view) { mChildren.erase (itr); } } void ApvlvView::saveKey (int has, int ct, uint key, bool end) { if (processInLast) return; if (keyLastEnd) { keySquence.clear (); } struct keyNode key_node = { has, ct, key, end }; keySquence.push_back (key_node); keyLastEnd = end; } void ApvlvView::processLastKey () { processInLast = true; for (auto node : keySquence) { process (node.Has, node.Ct, node.Key); } processInLast = false; } } // Local Variables: // mode: c++ // End: apvlv-0.7.0-alpha/src/ApvlvFile.cc0000644000175100017510000001727514726051136017260 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * Copyright (C) <2008> * * Contact: Alf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ /* @CPPFILE File.cc * * Author: Alf */ #include #include #include #include #include #include #include #include #include "ApvlvFile.h" #include "ApvlvUtil.h" #include "ApvlvWebViewWidget.h" namespace apvlv { using namespace std; const string html_template = "\n" "\n" " \n" " \n" " \n" " \n" " \n" "
" " " "
" " \n" "\n"; map> FileFactory::mSupportMimeTypes; map FileFactory::mSupportClass; const map> & FileFactory::supportMimeTypes () { return mSupportMimeTypes; } vector FileFactory::supportFileExts () { vector exts; for (const auto &pair : mSupportMimeTypes) { exts.insert (exts.end (), pair.second.begin (), pair.second.end ()); } return exts; } ostream & FileFactory::typeEngineDescription (ostream &os) { os << "Engines: " << endl; for (auto &pair : mSupportClass) { string ext = pair.first; string engines; std::ranges::for_each (pair.second, [&engines] (ExtClass &cls) { engines.append (" "); engines.append (cls.first); }); os << "\t" << pair.first << ":\t\t" << engines << endl; } os << endl; return os; } int FileFactory::registerClass (const string &name, const function &fun, initializer_list exts) { mSupportMimeTypes.insert ({ name, exts }); std::ranges::for_each (exts, [=] (const string &t) { auto iter = mSupportClass.find (t); if (iter != mSupportClass.end ()) { auto &cls_list = iter->second; cls_list.emplace_back (name, fun); } else { auto cls_list = ExtClassList{ { name, fun } }; mSupportClass.insert ({ t, cls_list }); } }); return static_cast (mSupportMimeTypes.size ()); } optional FileFactory::findMatchClass (const std::string &filename) { auto ext = filenameExtension (filename); if (ext.empty ()) return nullopt; if (mSupportClass.find (ext) == mSupportClass.end ()) return nullopt; auto cls_list = mSupportClass[ext]; if (cls_list.size () == 1) return cls_list[0]; auto cls_name = ApvlvParams::instance ()->getGroupStringOrDefault (ext, "engine", ""); if (cls_name.empty ()) return cls_list[0]; for (auto const &cls : cls_list) { if (strcasecmp (cls.first.c_str (), cls_name.c_str ()) == 0) return cls; } return cls_list[0]; } unique_ptr FileFactory::loadFile (const string &filename) { auto cls = findMatchClass (filename); if (!cls.has_value ()) { qWarning () << "no engine to open " << filename; return nullptr; } auto file = unique_ptr (cls->second ()); if (file->setFilename (filename)) return file; else { qWarning () << "open " << filename << " error"; return nullptr; } } File::~File () { mPages.clear (); srcPages.clear (); srcMimeTypes.clear (); } unique_ptr File::grepFile (const string &seq, bool is_case, bool is_regex, atomic &is_abort) { vector page_matches; for (int pn = 0; pn < sum (); ++pn) { if (is_abort.load () == true) return nullptr; auto size = pageSizeF (pn, 0); string content; if (pageText (pn, { 0, 0, size.width, size.height }, content) == false) continue; istringstream iss{ content }; string line; SearchMatchList matches; while (getline (iss, line)) { if (is_abort.load () == true) return nullptr; auto founds = apvlv::grep (line, seq, is_case, is_regex); for (auto const &found : founds) { SearchMatch match{ line.substr (found.first, found.second), line, found.first, found.second }; matches.push_back (match); } } if (!matches.empty ()) { page_matches.push_back ({ pn, matches }); } } if (page_matches.empty ()) return nullptr; auto file_match = make_unique (); file_match->filename = getFilename (); file_match->page_matches = std::move (page_matches); return file_match; } bool File::pageRenderToImage (int pn, double zm, int rot, QImage *img) { return false; } bool File::pageRenderToWebView (int pn, double zm, int rot, WebView *webview) { webview->setZoomFactor (zm); QUrl pdfuri = QString ("apvlv:///%1-%2-%3-%4.html") .arg (pn) .arg (zm) .arg (rot) .arg (rand ()); webview->load (pdfuri); return true; } string File::pathMimeType (const string &path) { if (srcMimeTypes.find (path) != srcMimeTypes.end ()) return srcMimeTypes[path]; else if (QString::fromLocal8Bit (path).endsWith (".png")) return "image/png"; else return "text/html"; } int File::pathPageNumber (const string &path) { if (srcPages.find (path) != srcPages.end ()) return srcPages[path]; return -1; } optional File::pathContent (const string &path) { auto words = QString::fromLocal8Bit (path).split ("-"); int pn = words[0].toInt (); double zm = words[1].toDouble (); int rot = words[2].toInt (); if (QString::fromLocal8Bit (path).endsWith (".html")) return pathContentHtml (pn, zm, rot); else return pathContentPng (pn, zm, rot); } optional File::pathContentHtml (int pn, double zm, int rot) { auto src = QString ("apvlv:///%1-%2-%3-%4.png") .arg (pn) .arg (zm) .arg (rot) .arg (rand ()); auto html = QString::asprintf (html_template.c_str (), src.toStdString ().c_str ()); return QByteArray::fromStdString (html.toStdString ()); } optional File::pathContentPng (int pn, double zm, int rot) { QImage image; if (pageRenderToImage (pn, zm, rot, &image) == false) return nullopt; QByteArray array; QBuffer buffer (&array); buffer.open (QIODevice::WriteOnly); image.save (&buffer, "PNG"); buffer.close (); return array; } } // Local Variables: // mode: c++ // End: apvlv-0.7.0-alpha/src/ApvlvImageWidget.h0000644000175100017510000000707514726051136020426 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * * Copyright (C) 2008 Alf. * * Contact: Alf * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2.0 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ /* @CPPFILE ApvlvImageWidget.h * * Author: Alf */ #ifndef _APVLV_IMAGEWIDGET_H_ #define _APVLV_IMAGEWIDGET_H_ #include #include #include #include #include "ApvlvFileWidget.h" #include "ApvlvUtil.h" #ifdef APVLV_WITH_OCR #include "ApvlvEditor.h" #include "ApvlvOCR.h" #endif namespace apvlv { #ifdef APVLV_WITH_OCR class TextContainer : public Editor { Q_OBJECT public: explicit TextContainer (QWidget *parent = nullptr); ~TextContainer () override = default; }; #endif class ImageWidget; class ImageContainer : public QLabel { Q_OBJECT public: explicit ImageContainer (QWidget *parent = nullptr); void mousePressEvent (QMouseEvent *event) override; void mouseMoveEvent (QMouseEvent *event) override; void mouseReleaseEvent (QMouseEvent *event) override; virtual bool renderImage (int pn, double zm, int rot); virtual void redraw (); void setImageWidget (ImageWidget *image_widget) { mImageWidget = image_widget; } private: bool mIsSelected{ false }; QPointF mPressPosition; QPointF mMovePosition; ImageWidget *mImageWidget{ nullptr }; friend class ImageWidget; QImage mImage; QAction mCopyAction; QAction mUnderlineAction; QAction mCommentAction; std::pair selectionRange (); std::vector selectionArea (); std::string selectionText (); private slots: void copy (); void underline (); void comment (); }; class ApvlvImage : public QScrollArea { Q_OBJECT public: ApvlvImage (); ~ApvlvImage () override; #ifdef APVLV_WITH_OCR void ocrDisplay (bool replace); std::unique_ptr ocrGetText (); #endif private: ImageContainer mImageContainer; #ifdef APVLV_WITH_OCR TextContainer mTextContainer; OCR mOCR; #endif friend class ImageWidget; }; class ImageWidget : public FileWidget { public: ImageWidget () { mImage.mImageContainer.setImageWidget (this); mHalScrollBar = mImage.horizontalScrollBar (); mValScrollBar = mImage.verticalScrollBar (); } [[nodiscard]] QWidget * widget () override { return &mImage; } void showPage (int pn, double s) override; void showPage (int pn, const std::string &anchor) override; void setSearchResults (const WordListRectangle &wlr) override; void setZoomrate (double zm) override; void setRotate (int rotate) override; private: ApvlvImage mImage{}; }; bool imageSelectSearch (QImage *pix, double zm, int select, const WordListRectangle &poses); bool imageSelect (QImage *pix, double zm, const std::vector &poses); bool imageUnderline (QImage *pix, double zm, const std::vector &poses); } #endif /* Local Variables: */ /* mode: c++ */ /* End: */ apvlv-0.7.0-alpha/src/ApvlvQueue.h0000644000175100017510000000364514726051136017323 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * * Copyright (C) 2008 Alf. * * Contact: Alf * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2.0 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ /* @CPPFILE ApvlvCmds.h * * Author: Alf */ #ifndef _APVLV_QUEUE_H_ #define _APVLV_QUEUE_H_ #include #include namespace apvlv { template class LockQueue { public: LockQueue () = default; LockQueue (const LockQueue &) = delete; LockQueue &operator= (const LockQueue &) = delete; ~LockQueue () = default; void push (const T &node) { std::lock_guard lock (mMutex); mQueueInternal.push (node); } void push (T &&node) { std::lock_guard lock (mMutex); mQueueInternal.push (std::move (node)); } bool pop (T &node) { std::lock_guard lock (mMutex); if (mQueueInternal.empty ()) return false; node = std::move (mQueueInternal.front ()); mQueueInternal.pop (); return true; } void empty () { std::lock_guard lock (mMutex); return mQueueInternal.empty (); } void clear () { std::lock_guard lock (mMutex); mQueueInternal = {}; } private: std::queue mQueueInternal; std::mutex mMutex; }; } #endif apvlv-0.7.0-alpha/src/ApvlvDired.cc0000644000175100017510000000255314726051136017421 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * * Copyright (C) 2008 Alf. * * Contact: Alf * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2.0 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ /* @CPPFILE ApvlvDired.cc * * Author: Alf */ #include #include #include #include #include "ApvlvDired.h" namespace apvlv { using namespace std; DiredDialog::DiredDialog (QWidget *parent) : QDialog (parent) { setLayout (&mVboxLayout); } void DiredDialog::activateItem (QListWidgetItem *item) { auto words = item->data (Qt::UserRole).toStringList (); auto path = words[0]; auto pn = words[1].toInt (); emit loadFile (path.toStdString (), pn); accept (); } } apvlv-0.7.0-alpha/src/ApvlvContent.h0000644000175100017510000001102314726051136017636 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * * Copyright (C) 2008 Alf. * * Contact: Alf * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2.0 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ /* @CPPFILE ApvlvFrame.h * * Author: Alf */ #ifndef _APVLV_CONTENT_H_ #define _APVLV_CONTENT_H_ #include #include #include #include #include #include #include #include #include #include "ApvlvFile.h" #include "ApvlvUtil.h" #include "ApvlvWidget.h" namespace apvlv { class ContentTree : public QTreeWidget { protected: void keyPressEvent (QKeyEvent *event) override; }; class ApvlvFrame; class Directory final : public QFrame { Q_OBJECT public: Directory (); ~Directory () override = default; bool isReady (); enum class Column : int { Title = 0, MTime, FileSize, }; static std::vector ColumnString; static std::vector SortByColumnString; enum class FilterType : int { Title = 0, FileName, MTimeBe, MTimeLe, FileSizeBe, FileSizeLe, }; static std::vector FilterTypeString; FileIndex *currentItemFileIndex (); FileIndex *currentFileFileIndex (); QTreeWidgetItem *findTreeWidgetItem (QTreeWidgetItem *itr, FileIndexType type, const std::string &path, int pn, const std::string &anchor); bool setCurrentIndex (const std::string &path, int pn, const std::string &anchor); void setFrame (ApvlvFrame *frame) { mFrame = frame; } void focusFilter () { QTimer::singleShot (50, &mFilterText, SLOT (setFocus ())); } void scrollUp (int times); void scrollDown (int times); void scrollLeft (int times); void scrollRight (int times); void setActive (bool active) { if (active) { QTimer::singleShot (50, &mTreeWidget, SLOT (setFocus ())); } else { mTreeWidget.clearFocus (); } } bool isActive () { return mTreeWidget.hasFocus (); } private: QVBoxLayout mLayout; QToolBar mToolBar; ApvlvLineEdit mFilterText; QComboBox mFilterType; QComboBox mSortType; ContentTree mTreeWidget; QMenu mItemMenu; std::map mTypeIcons; FileIndex mIndex; Column mSortColumn{ Column::Title }; ApvlvFrame *mFrame{ nullptr }; bool mSortAscending{ true }; void setupToolBar (); void setupTree (); QTreeWidgetItem * selectedTreeItem () { auto selitems = mTreeWidget.selectedItems (); return selitems.isEmpty () ? nullptr : selitems[0]; } void setItemSelected (QTreeWidgetItem *item); void setIndex (FileIndex &index, QTreeWidgetItem *root_itr); void refreshIndex (const FileIndex &index); void setFileIndexToTreeItem (QTreeWidgetItem *item, FileIndex *index); FileIndex *getFileIndexFromTreeItem (QTreeWidgetItem *item); FileIndex *treeItemToFileIndex (QTreeWidgetItem *item); using filterFuncReturn = std::tuple; using filterFunc = std::function; void filterItemBy (QTreeWidgetItem *root, const filterFunc &filter_func); void setItemChildrenFilter (QTreeWidgetItem *root, bool is_filter); private slots: void onFileRename (); void onFileDelete (); void onRefresh (); void onFilter (); void sortBy (int method) { mSortAscending = !mSortAscending; mSortColumn = static_cast (method); sortItems (mTreeWidget.invisibleRootItem ()); } void sortItems (QTreeWidgetItem *root); void onRowActivated (QTreeWidgetItem *item, int column); void onRowDoubleClicked (); void onContextMenuRequest (const QPoint &point); void selectFirstItem (); void setIndex (const FileIndex &index); }; } #endif /* Local Variables: */ /* mode: c++ */ /* End: */ apvlv-0.7.0-alpha/src/main.cc0000644000175100017510000001402414726051136016301 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * * Copyright (C) 2008 Alf. * * Contact: Alf * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2.0 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ /*@CPPFILE main.cpp Apvlv start at here * * Author: Alf */ #include #include #include #include #include #include #include #include #include "ApvlvInfo.h" #include "ApvlvLog.h" #include "ApvlvParams.h" #include "ApvlvUtil.h" #include "ApvlvView.h" using namespace std; using namespace apvlv; #if defined WIN32 && defined NDEBUG #pragma comment(linker, "/subsystem:windows") #pragma comment(linker, "/ENTRY:mainCRTStartup") #endif static void registerUrlScheme () { QWebEngineUrlScheme scheme ("apvlv"); scheme.setSyntax (QWebEngineUrlScheme::Syntax::Path); QWebEngineUrlScheme::registerScheme (scheme); } static void usageExit () { std::cout << PACKAGE_NAME << " [options] paths" << endl; std::cout << endl; std::cout << "Options: " << endl; std::cout << "\t-h display this and exit\n" "\t-v display version info and exit\n" "\t-c [file] set user configuration file\n" "\t paths document path list" << endl; FileFactory::typeEngineDescription (std::cout); std::cout << "Please send bug report to " << PACKAGE_BUGREPORT << endl; exit (0); } static void versionExit () { fprintf (stdout, "%s %s-%s\n" "Please send bug report to %s\n" "\n", PACKAGE_NAME, PACKAGE_VERSION, RELEASE, PACKAGE_BUGREPORT); exit (0); } static list parseCommandLine (const QCoreApplication &app) { QCommandLineParser parser; auto versionOption = QCommandLineOption (QStringList () << "v" << "version", QObject::tr ("version number")); parser.addOption (versionOption); auto helpOption = QCommandLineOption (QStringList () << "h" << "help", QObject::tr ("help information")); parser.addOption (helpOption); auto configFileOption = QCommandLineOption (QStringList () << "c" << "config-file", QObject::tr ("config file"), "config"); parser.addOption (configFileOption); auto logFileOption = QCommandLineOption (QStringList () << "l" << "log-file", QObject::tr ("log file"), "log"); parser.addOption (logFileOption); parser.addPositionalArgument ("path", QObject::tr ("document path")); parser.process (app); if (parser.isSet (helpOption)) { usageExit (); } if (parser.isSet (versionOption)) { versionExit (); } if (parser.isSet (configFileOption)) { auto value = parser.value (configFileOption); IniFile = filesystem::absolute (value.toStdString ()).string (); } if (parser.isSet (logFileOption)) { auto value = parser.value (logFileOption); LogFile = filesystem::absolute (value.toStdString ()).string (); } /* * load the global sys conf file * */ auto sysIni = string (SYSCONFDIR) + "/apvlvrc"; auto params = ApvlvParams::instance (); params->loadFile (sysIni); /* * load the user conf file * */ qDebug () << "using config: " << IniFile; ApvlvParams::instance ()->loadFile (IniFile); list paths; auto pathlist = parser.positionalArguments (); for (const auto &path : pathlist) { paths.emplace_back (path.toStdString ()); } return paths; } static void loadTranslator (QTranslator &translator) { map lanuage_translator{ { "Chinese", "zh_CN" } }; auto lan = QLocale::system ().language (); auto lanstr = QLocale::languageToString (lan).toStdString (); if (lanuage_translator.find (lanstr) != lanuage_translator.end ()) { auto lantrans = lanuage_translator[lanstr]; if (!translator.load (QString::fromLocal8Bit (lantrans), QString::fromLocal8Bit (Translations))) { qWarning () << "Load i18n file failed, using English"; } else { QCoreApplication::installTranslator (&translator); } } } int main (int argc, char *argv[]) { registerUrlScheme (); QApplication app (argc, argv); getRuntimePaths (); QTranslator translator; loadTranslator (translator); ApvlvInfo::instance ()->loadFile (SessionFile); auto paths = parseCommandLine (app); NotesDir = ApvlvParams::instance ()->getGroupStringOrDefault ("notes", "dir", NotesDir); ApvlvLog::instance ()->setLogFile (LogFile); string path = HelpPdf; if (!paths.empty ()) { path = paths.front (); paths.pop_front (); } if (!filesystem::is_regular_file (path) && !filesystem::is_directory (path)) { qFatal () << "File '" << path << "' is not readable."; return 1; } ApvlvView sView (nullptr); if (!sView.newTab (path)) { exit (1); } while (!paths.empty ()) { path = paths.front (); paths.pop_front (); auto apath = filesystem::absolute (path).string (); if (!sView.newTab (apath)) { qCritical () << "Can't open document: " << apath; } } QApplication::exec (); return 0; } // Local Variables: // mode: c++ // End: apvlv-0.7.0-alpha/src/ApvlvNote.h0000644000175100017510000001665714726051136017153 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * * Copyright (C) 2008 Alf. * * Contact: Alf * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2.0 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ /* @CPPFILE ApvlvEditor.h * * Author: Alf */ #ifndef _APVLV_NOTE_H_ #define _APVLV_NOTE_H_ #include #include #include #include #include #include "ApvlvEditor.h" namespace apvlv { const float NoteScoreMin = 0.0f; const float NoteScoreMax = 10.0f; struct ApvlvPoint; struct Location { int page{ 0 }; double x{ 0 }; double y{ 0 }; int offset{ -1 }; std::string path; std::string anchor; friend bool operator< (const Location &a, const Location &b) { if (a.page != b.page) return a.page < b.page; if (a.y != b.y) return a.y < b.y; if (a.x != b.x) return a.x < b.x; if (a.offset != b.offset) return a.offset < b.offset; if (a.path != b.path) return a.path < b.path; return false; } friend std::istream & operator>> (std::istream &is, Location &location) { is >> location.page >> location.x >> location.y >> location.offset; std::string path; while (!is.eof ()) { is >> path; if (path.empty ()) { return is; } if (path.ends_with ("||")) { path = path.substr (0, path.length () - 2); location.path += path; break; } else { location.path += path; } } while (!is.eof ()) { is >> path; if (path.empty ()) { break; } location.anchor += path; } return is; } friend std::ostream & operator<< (std::ostream &os, const Location &location) { os << location.page << " " << location.x << " " << location.y << " " << location.offset; if (!location.path.empty ()) { os << " " << location.path << "|| " << location.anchor << "||"; } return os; } void set (int page1, const ApvlvPoint *point1, int offset1 = 0, const std::string &path1 = "", const std::string &anchor1 = ""); }; class File; class Note; struct Comment { Comment () : time{ std::chrono::system_clock::to_time_t ( std::chrono::system_clock::now ()) } { } std::string quoteText; std::string commentText; Location begin; Location end; time_t time; friend std::istream & operator>> (std::istream &is, Comment &comment) { std::string line; getline (is, line); while (line.starts_with ("> ")) { auto content = line.substr (2); content += '\n'; comment.quoteText += content; getline (is, line); } if (line.starts_with ("```")) { do { getline (is, line); line += '\n'; comment.commentText += line; } while (!line.starts_with ("```")); } getline (is, line); std::stringstream bs (line.substr (2)); bs >> comment.begin; getline (is, line); std::stringstream es (line.substr (2)); es >> comment.end; getline (is, line); std::stringstream ts (line.substr (2)); std::tm tm; ts >> std::get_time (&tm, "%a %b %d %H:%M:%S %Y"); comment.time = std::mktime (&tm); return is; } friend std::ostream & operator<< (std::ostream &os, const Comment &comment) { // put "> " at beginning of every line auto remain = comment.quoteText + '\n'; auto pos = remain.find ('\n'); while (pos != std::string::npos) { os << "> " << remain.substr (0, pos) << std::endl; remain = remain.substr (pos + 1); pos = remain.find ('\n'); } os << "```" << std::endl; os << comment.commentText << std::endl; os << "```" << std::endl; os << "- " << comment.begin << std::endl; os << "- " << comment.end << std::endl; os << "- " << std::ctime (&comment.time) << std::endl; os << std::endl; return os; } }; class Note : public QObject { Q_OBJECT public: explicit Note (File *file); ~Note () override; bool loadStreamV1 (std::ifstream &is); bool loadStream (std::ifstream &is); bool load (std::string_view path = ""); bool dumpStream (std::ostream &os); bool dump (std::string_view path = ""); void setScore (float score) { mScore = score; dump (); } float score () { return mScore; } void addTag (const std::string &tag) { mTagSet.insert (tag); dump (); } void removeTag (const std::string &tag) { mTagSet.erase (tag); dump (); } const std::unordered_set & tag () { return mTagSet; } void setRemark (const std::string &remark) { mRemark = remark; dump (); } const std::string & remark () { return mRemark; } void addReference (const std::string &ref) { mReferences.insert (ref); dump (); } void removeReference (const std::string &ref) { mReferences.erase (ref); dump (); } const std::unordered_set & references () { return mReferences; } void addLink (const std::string &link) { mLinks.insert (link); dump (); } void removeLink (const std::string &link) { mLinks.erase (link); dump (); } const std::unordered_set & links () { return mLinks; } void addComment (const Comment &comment) { mCommentList.insert ({ comment.begin, comment }); dump (); } void removeComment (const Comment &comment) { mCommentList.erase (comment.begin); dump (); } std::vector getCommentsInPage (int page) { std::vector comments; std::ranges::for_each ( mCommentList, [page, &comments] (const std::pair &pair1) { if (pair1.first.page == page) comments.push_back (pair1.second); }); return comments; } std::vector getCommentsInPath (const std::string &path) { std::vector comments; std::ranges::for_each ( mCommentList, [path, &comments] (const std::pair &pair1) { if (pair1.first.path == path) comments.push_back (pair1.second); }); return comments; } private: std::string notePathOfFile (File *file); std::string mPath; File *mFile{ nullptr }; float mScore{ 0.0f }; std::unordered_set mTagSet; std::string mRemark; std::unordered_set mReferences; std::unordered_set mLinks; std::map mCommentList; }; class CommentEdit : public Editor { Q_OBJECT public: private: Comment *mComment; }; class NoteEdit : public QFrame { Q_OBJECT public: private: Note *mNote; }; } #endif apvlv-0.7.0-alpha/src/ApvlvEditor.cc0000644000175100017510000000317414726051136017620 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * * Copyright (C) 2008 Alf. * * Contact: Alf * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2.0 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ /* @CPPFILE ApvlvEditor.cc * * Author: Alf */ #include "ApvlvEditor.h" namespace apvlv { Editor::Editor (QWidget *parent) : QTextEdit (parent) { auto font = currentFont (); mFontFamily = font.family (); mFontPointSize = font.pointSizeF (); mFontWeight = font.weight (); mFontPixelSize = font.pixelSize (); } void Editor::setZoomrate (double zm) { auto font = currentFont (); auto updated = false; if (mFontPointSize != -1) { auto pointsize = mFontPointSize * zm; font.setPointSizeF (pointsize); updated = true; } else if (mFontPixelSize != -1) { auto pixelsize = mFontPixelSize * zm; font.setPixelSize (static_cast (pixelsize)); updated = true; } if (updated) { setFont (font); update (); } } } apvlv-0.7.0-alpha/src/ApvlvLog.h0000644000175100017510000000313214726051136016747 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * Copyright (C) <2024> Alf * * Contact: Alf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ /* @CPPFILE ApvlvLog.h * * Author: Alf */ #ifndef _APVLV_LOG_H_ #define _APVLV_LOG_H_ #include #include #include #include #include namespace apvlv { class ApvlvLog final { public: ApvlvLog (const ApvlvLog &) = delete; const ApvlvLog &operator= (const ApvlvLog &) = delete; void setLogFile (const std::string &path); ~ApvlvLog (); static ApvlvLog *instance (); static void logMessage (QtMsgType type, const QMessageLogContext &context, const QString &msg); private: ApvlvLog () = default; void writeMessage (const QString &log); QFile mFile; QTextStream mTextStream; std::mutex mMutex; }; }; #endif /* Local Variables: */ /* mode: c++ */ /* End: */ apvlv-0.7.0-alpha/src/ApvlvWindow.h0000644000175100017510000000511714726051136017502 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * * Copyright (C) 2008 Alf. * * Contact: Alf * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2.0 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ /* @CPPFILE ApvlvWindow.h * * Author: Alf */ #ifndef _APVLV_WINDOW_H_ #define _APVLV_WINDOW_H_ #include "ApvlvFrame.h" namespace apvlv { class ApvlvFrame; class ApvlvWindowContext; class ApvlvWindow final : public QFrame { Q_OBJECT public: ApvlvWindow (); ~ApvlvWindow () override; /* WE operate the AW_DOC window * Any SP, VSP are a virtual window, just for contain the AW_DOC window * AW_NONE is an empty window, need free * So, ANY user interface function can only get the AW_DOC window * */ enum class WindowType { FRAME, SP, VSP, }; WindowType mType{ WindowType::FRAME }; bool birth (WindowType type, ApvlvFrame *doc); void perish (); void setActive (bool act); void setFrame (ApvlvFrame *doc); ApvlvFrame *stealFrame (); ApvlvFrame *getFrame (); ApvlvWindow *firstWindow (); ApvlvWindow *secondWindow (); ApvlvWindow *parentWindow (); ApvlvWindow *rootWindow (); ApvlvWindow *firstFrameWindow (); void setActiveWindow (ApvlvWindow *win) { mActive = win; } ApvlvWindow * getActiveWindow () { return mActive; } bool isRoot (); void smaller (int times = 1); void bigger (int times = 1); ApvlvWindow *getNeighbor (int count, uint key); ApvlvWindow *getNext (); CmdReturn process (int times, uint keyval); ApvlvWindow *findWindowByWidget (QWidget *widget); private: QVBoxLayout mLayout; QSplitter mPaned; ApvlvWindow *mActive{ nullptr }; ApvlvWindow *getLeft (); ApvlvWindow *getRight (); ApvlvWindow *getTop (); ApvlvWindow *getBottom (); void splitWidget (WindowType type, QWidget *one, QWidget *other); private slots: void perishWidget (); void setAsRootActive (); }; } #endif /* Local Variables: */ /* mode: c++ */ /* End: */ apvlv-0.7.0-alpha/src/ApvlvWidget.cc0000644000175100017510000000224614726051136017614 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * * Copyright (C) 2008 Alf. * * Contact: Alf * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2.0 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ /* @CPPFILE ApvlvWidget.cc * * Author: Alf */ #include #include "ApvlvWidget.h" namespace apvlv { void ApvlvLineEdit::keyPressEvent (QKeyEvent *event) { if (event->key () == Qt::Key_Escape) { clearFocus (); event->ignore (); } else { QLineEdit::keyPressEvent (event); } } }apvlv-0.7.0-alpha/src/ApvlvContent.cc0000644000175100017510000005632614726051136020013 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * * Copyright (C) 2008 Alf. * * Contact: Alf * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2.0 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ /* @CPPFILE ApvlvFrame.cc * * Author: Alf */ #include #include #include #include #include #include #include #include #include #include #include "ApvlvContent.h" #include "ApvlvFrame.h" #include "ApvlvParams.h" #include "ApvlvUtil.h" namespace apvlv { using namespace std; std::vector Directory::ColumnString = { QT_TR_NOOP ("Title"), QT_TR_NOOP ("Modified Time"), QT_TR_NOOP ("File Size"), }; std::vector Directory::SortByColumnString = { QT_TR_NOOP ("Sort By Title"), QT_TR_NOOP ("Sort By Modified Time"), QT_TR_NOOP ("Sort By File Size"), }; std::vector Directory::FilterTypeString = { QT_TR_NOOP ("Filter Title"), QT_TR_NOOP ("Filter File Name"), QT_TR_NOOP ("Filter Modified Time >="), QT_TR_NOOP ("Filter Modified Time <="), QT_TR_NOOP ("Filter File Size >="), QT_TR_NOOP ("Filter FileSize <="), }; void ContentTree::keyPressEvent (QKeyEvent *event) { event->ignore (); } Directory::Directory () { setLayout (&mLayout); mLayout.addWidget (&mToolBar, 0); mLayout.addWidget (&mTreeWidget); setupToolBar (); setupTree (); auto guioptions = ApvlvParams::instance ()->getStringOrDefault ("guioptions"); if (guioptions.find ('S') == string::npos) { mToolBar.hide (); } QTimer::singleShot (50, this, SLOT (selectFirstItem ())); } void Directory::setupToolBar () { mToolBar.addWidget (&mFilterText); QObject::connect (&mFilterText, SIGNAL (textEdited (const QString &)), this, SLOT (onFilter ())); mToolBar.addSeparator (); mToolBar.addWidget (&mFilterType); for (auto const &str : FilterTypeString) { mFilterType.addItem (tr (str)); } mToolBar.addSeparator (); QObject::connect (&mFilterType, SIGNAL (activated (int)), this, SLOT (onFilter ())); auto refresh = mToolBar.addAction (tr ("Refresh")); refresh->setIcon (QIcon::fromTheme (QIcon::ThemeIcon::ViewRefresh)); QObject::connect (refresh, SIGNAL (triggered (bool)), this, SLOT (onRefresh ())); mToolBar.addSeparator (); auto expand_all = mToolBar.addAction (tr ("Expand All")); expand_all->setIcon (QIcon::fromTheme (QIcon::ThemeIcon::ListAdd)); QObject::connect (expand_all, SIGNAL (triggered (bool)), &mTreeWidget, SLOT (expandAll ())); auto collapse_all = mToolBar.addAction (tr ("Collapse All")); collapse_all->setIcon (QIcon::fromTheme (QIcon::ThemeIcon::ListRemove)); QObject::connect (collapse_all, SIGNAL (triggered (bool)), &mTreeWidget, SLOT (collapseAll ())); mToolBar.addSeparator (); mToolBar.addWidget (&mSortType); for (auto const &str : SortByColumnString) { mSortType.addItem (tr (str)); } QObject::connect (&mSortType, SIGNAL (activated (int)), this, SLOT (sortBy (int))); } void Directory::setupTree () { mTreeWidget.setColumnCount (3); mTreeWidget.setColumnWidth (static_cast (Column::Title), 400); mTreeWidget.setColumnWidth (static_cast (Column::MTime), 150); mTreeWidget.setColumnWidth (static_cast (Column::FileSize), 150); mTreeWidget.setSortingEnabled (false); mTreeWidget.setHeaderHidden (false); QStringList labels; for (auto const &str : ColumnString) { labels.append (tr (str)); } mTreeWidget.setHeaderLabels (labels); auto headerview = mTreeWidget.header (); headerview->setSectionsClickable (true); QObject::connect (headerview, SIGNAL (sectionClicked (int)), this, SLOT (sortBy (int))); mTreeWidget.setVerticalScrollMode ( QAbstractItemView::ScrollMode::ScrollPerItem); mTreeWidget.setSelectionBehavior ( QAbstractItemView::SelectionBehavior::SelectRows); mTreeWidget.setSelectionMode ( QAbstractItemView::SelectionMode::ExtendedSelection); mTypeIcons[FileIndexType::DIR] = QIcon (IconDir.c_str ()); mTypeIcons[FileIndexType::FILE] = QIcon (IconFile.c_str ()); mTypeIcons[FileIndexType::PAGE] = QIcon (IconPage.c_str ()); QObject::connect (&mTreeWidget, SIGNAL (itemActivated (QTreeWidgetItem *, int)), this, SLOT (onRowActivated (QTreeWidgetItem *, int))); QObject::connect (&mTreeWidget, SIGNAL (itemDoubleClicked (QTreeWidgetItem *, int)), this, SLOT (onRowDoubleClicked ())); mTreeWidget.setContextMenuPolicy (Qt::ContextMenuPolicy::CustomContextMenu); QObject::connect (&mTreeWidget, SIGNAL (customContextMenuRequested (const QPoint &)), this, SLOT (onContextMenuRequest (const QPoint &))); } bool Directory::isReady () { return (mTreeWidget.topLevelItemCount () > 0); } void Directory::setIndex (const FileIndex &index) { using enum FileIndexType; if (mTreeWidget.topLevelItemCount () == 0 || index.type == DIR) { refreshIndex (index); return; } auto cur_index = currentItemFileIndex (); if (mIndex.type == DIR && cur_index->type == FILE && index.type == FILE) { if (cur_index->mChildrenIndex.empty ()) { auto cur_item = selectedTreeItem (); cur_index->moveChildChildren (index); for (auto &child : cur_index->mChildrenIndex) { setIndex (child, cur_item); } } } } void Directory::setItemSelected (QTreeWidgetItem *item) { auto selitems = mTreeWidget.selectedItems (); for (auto selitem : selitems) { selitem->setSelected (false); } auto parent = item->parent (); while (parent) { mTreeWidget.expandItem (parent); parent = parent->parent (); } item->setSelected (true); if (item->isExpanded ()) mTreeWidget.collapseItem (item); mTreeWidget.scrollToItem (item); } void Directory::setIndex (FileIndex &index, QTreeWidgetItem *root_itr) { auto itr = new QTreeWidgetItem (); setFileIndexToTreeItem (itr, &index); root_itr->addChild (itr); for (auto &child : index.mChildrenIndex) { setIndex (child, itr); } } void Directory::refreshIndex (const FileIndex &index) { mTreeWidget.clear (); mIndex = index; for (auto &child : mIndex.mChildrenIndex) { setIndex (child, mTreeWidget.invisibleRootItem ()); } sortItems (mTreeWidget.invisibleRootItem ()); QTimer::singleShot (50, this, SLOT (selectFirstItem ())); } void Directory::setFileIndexToTreeItem (QTreeWidgetItem *item, FileIndex *index) { using enum Column; auto variant = QVariant::fromValue (index); item->setData (static_cast (Title), Qt::UserRole, variant); item->setText (static_cast (Title), QString::fromLocal8Bit (index->title.c_str ())); item->setIcon (static_cast (Title), mTypeIcons[index->type]); item->setToolTip (static_cast (Title), QString::fromLocal8Bit (index->path)); using enum FileIndexType; if (index->type == FILE) { auto date = QDateTime::fromSecsSinceEpoch (index->mtime, QTimeZone::systemTimeZone ()); item->setText (static_cast (Column::MTime), date.toString ("yyyy-MM-dd HH:mm:ss")); auto size = QLocale ().formattedDataSize (static_cast (index->size)); item->setText (static_cast (Column::FileSize), size); } } FileIndex * Directory::getFileIndexFromTreeItem (QTreeWidgetItem *item) { if (item == nullptr) return nullptr; auto varaint = item->data (static_cast (Column::Title), Qt::UserRole); auto index = varaint.value (); return index; } FileIndex * Directory::treeItemToFileIndex (QTreeWidgetItem *item) { while (item != nullptr) { auto index = getFileIndexFromTreeItem (item); if (index->type == FileIndexType::FILE) return index; else item = item->parent (); } return nullptr; } void Directory::filterItemBy (QTreeWidgetItem *root, const filterFunc &filter_func) { std::stack item_stack; for (auto i = 0; i < root->childCount (); ++i) { auto item = root->child (i); item_stack.push (item); } while (!item_stack.empty ()) { auto item = item_stack.top (); item_stack.pop (); auto index = getFileIndexFromTreeItem (item); auto [is_filter, same_as_file] = filter_func (index); if (is_filter) { item->setHidden (false); auto parent = item->parent (); while (parent) { parent->setHidden (false); parent = parent->parent (); } } else { item->setHidden (true); } if (index->type == FileIndexType::FILE && same_as_file) { setItemChildrenFilter (item, is_filter); } else { for (int i = 0; i < item->childCount (); ++i) { auto child_item = item->child (i); item_stack.push (child_item); } } } } void Directory::setItemChildrenFilter (QTreeWidgetItem *root, bool is_filter) { filterItemBy (root, [is_filter] (const FileIndex *a) -> filterFuncReturn { return { is_filter, false }; }); } QTreeWidgetItem * Directory::findTreeWidgetItem (QTreeWidgetItem *itr, FileIndexType type, const string &path, int pn, const string &anchor) { if (itr == nullptr) return nullptr; auto index = getFileIndexFromTreeItem (itr); if (index == nullptr || index->type != type) { for (auto ind = 0; ind < itr->childCount (); ++ind) { auto child_itr = itr->child (ind); auto citr = findTreeWidgetItem (child_itr, type, path, pn, anchor); if (citr) return citr; } return nullptr; } auto file_index = treeItemToFileIndex (itr); if (file_index == nullptr) { if (index->page == pn && (anchor.empty () || index->anchor == anchor)) { return itr; } return nullptr; } if (file_index->path == path && index->page == pn && (anchor.empty () || index->anchor == anchor)) { return itr; } return nullptr; } bool Directory::setCurrentIndex (const string &path, int pn, const string &anchor) { auto itr = selectedTreeItem (); auto fitr = findTreeWidgetItem (itr, FileIndexType::PAGE, path, pn, anchor); if (fitr == nullptr) fitr = findTreeWidgetItem (mTreeWidget.invisibleRootItem (), FileIndexType::PAGE, path, pn, anchor); if (fitr == nullptr) fitr = findTreeWidgetItem (mTreeWidget.invisibleRootItem (), FileIndexType::FILE, path, pn, anchor); if (fitr) { setItemSelected (fitr); return true; } return false; } void Directory::scrollUp (int times) { auto item = selectedTreeItem (); if (item == nullptr) return; auto parent = item->parent (); if (parent == nullptr) { parent = mTreeWidget.invisibleRootItem (); } auto index = parent->indexOfChild (item); if (index > 0) { auto new_index = index > times ? index - times : 0; auto itr = parent->child (new_index); setItemSelected (itr); } } void Directory::scrollDown (int times) { auto item = selectedTreeItem (); if (item == nullptr) return; auto parent = item->parent (); if (parent == nullptr) { parent = mTreeWidget.invisibleRootItem (); } auto index = parent->indexOfChild (item); auto count = parent->childCount (); auto new_index = index + times < count ? index + times : count - 1; auto itr = parent->child (new_index); setItemSelected (itr); } void Directory::scrollLeft (int times) { auto item = selectedTreeItem (); if (item == nullptr) return; if (item->parent () == nullptr) return; auto parent = item->parent (); while (--times > 0 && parent->parent ()) { parent = parent->parent (); } setItemSelected (parent); } void Directory::scrollRight (int times) { auto item = selectedTreeItem (); if (item == nullptr) return; if (item->childCount () == 0) return; item = item->child (0); while (--times > 0 && item->childCount () > 0) { item = item->child (0); } setItemSelected (item); } void Directory::onFileRename () { auto items = mTreeWidget.selectedItems (); if (items.isEmpty ()) return; auto item = items[0]; auto index = getFileIndexFromTreeItem (item); if (index->type != FileIndexType::FILE) return; auto qpath = QString::fromLocal8Bit (index->path); auto text = QString (tr ("Input new name of %1")).arg (qpath); auto user_text = QInputDialog::getText (this, tr ("Rename"), text, QLineEdit::Normal, qpath); auto nname = user_text.trimmed (); if (nname.isEmpty ()) return; if (QFile (qpath).rename (nname)) { index->path = nname.toStdString (); index->title = index->path.substr (index->path.rfind ('/') + 1); setFileIndexToTreeItem (item, index); } else { text = QString (tr ("Rename %1 to %2 failed")).arg (qpath).arg (nname); QMessageBox::warning (this, tr ("Warning"), text); } } void Directory::onFileDelete () { auto items = mTreeWidget.selectedItems (); if (items.isEmpty ()) return; auto msg_res = QMessageBox::No; for (auto item : items) { auto index = getFileIndexFromTreeItem (item); if (index->type != FileIndexType::FILE) continue; auto qpath = QString::fromLocal8Bit (index->path); auto text = QString (tr ("Will delete the \n%1, confirm ?")).arg (qpath); if (msg_res == QMessageBox::No || msg_res == QMessageBox::Yes) { auto buttons = QMessageBox::Yes | QMessageBox::No; if (items.size () > 1) buttons = QMessageBox::Yes | QMessageBox::YesToAll | QMessageBox::No | QMessageBox::NoToAll; msg_res = QMessageBox::question (this, tr ("Confirm"), text, buttons, QMessageBox::No); } if (msg_res == QMessageBox::NoToAll) return; else if (msg_res == QMessageBox::No) continue; auto parent = item->parent (); if (parent == nullptr) { auto offset = mTreeWidget.indexOfTopLevelItem (item); mIndex.removeChild (*index); item = mTreeWidget.takeTopLevelItem (offset); delete item; } else { auto pindex = getFileIndexFromTreeItem (parent); pindex->removeChild (*index); parent->removeChild (item); } qDebug () << "delete " << qpath; QFile::remove (qpath); } } void Directory::onRefresh () { refreshIndex (mIndex); } void Directory::onFilter () { auto root = mTreeWidget.invisibleRootItem (); auto cur = mFilterType.currentIndex (); auto type = static_cast (cur); auto text = mFilterText.text ().trimmed (); if (text.isEmpty ()) { setItemChildrenFilter (root, true); return; } QDateTime datetime; qint64 qsize; decltype (FileIndex::size) size; switch (type) { case FilterType::Title: filterItemBy (root, [text] (const FileIndex *a) -> filterFuncReturn { return { a->title.find (text.toStdString ()) != string::npos, false }; }); break; case FilterType::FileName: filterItemBy (root, [text] (const FileIndex *a) -> filterFuncReturn { return { a->type == FileIndexType::FILE && a->title.find (text.toStdString ()) != string::npos, true }; }); break; case FilterType::MTimeBe: datetime = QDateTime::fromString (text); filterItemBy (root, [datetime] (const FileIndex *a) -> filterFuncReturn { return { a->type == FileIndexType::FILE && a->mtime >= datetime.toMSecsSinceEpoch (), true }; }); break; case FilterType::MTimeLe: datetime = QDateTime::fromString (text); filterItemBy (root, [datetime] (const FileIndex *a) -> filterFuncReturn { return { a->type == FileIndexType::FILE && a->mtime <= datetime.toMSecsSinceEpoch (), true }; }); break; case FilterType::FileSizeBe: qsize = parseFormattedDataSize (text); size = static_cast (qsize); filterItemBy (root, [size] (const FileIndex *a) -> filterFuncReturn { return { a->type == FileIndexType::FILE && a->size >= size, true }; }); break; case FilterType::FileSizeLe: qsize = parseFormattedDataSize (text); size = static_cast (qsize); filterItemBy (root, [size] (const FileIndex *a) -> filterFuncReturn { return { a->type == FileIndexType::FILE && a->size <= size, true }; }); break; default: qWarning () << tr ("Filter Type is invalid"); break; } mTreeWidget.expandAll (); } void Directory::sortItems (QTreeWidgetItem *tree_iter) { stack needSort; needSort.push (tree_iter); using itemState = pair; while (!needSort.empty ()) { auto root = needSort.top (); needSort.pop (); vector item_list; for (auto i = 0; i < root->childCount (); ++i) { auto item = root->child (i); auto index = getFileIndexFromTreeItem (item); if (index->type != FileIndexType::PAGE) item_list.emplace_back (item, item->isExpanded ()); } std::ranges::for_each (item_list, [this, &needSort] (itemState &p) { if (p.first->childCount () > 1) { auto index = getFileIndexFromTreeItem (p.first); if (index && index->type == FileIndexType::DIR) { needSort.push (p.first); } } }); if (mSortAscending) { switch (mSortColumn) { using enum Column; case Title: std::ranges::sort ( item_list, std::ranges::greater (), [this] (itemState &p) { auto index = getFileIndexFromTreeItem (p.first); return index->title; }); break; case MTime: std::ranges::sort ( item_list, std::ranges::greater (), [this] (itemState &p) { auto index = getFileIndexFromTreeItem (p.first); return index->mtime; }); break; case FileSize: std::ranges::sort ( item_list, std::ranges::greater (), [this] (itemState &p) { auto index = getFileIndexFromTreeItem (p.first); return index->size; }); break; } } else { switch (mSortColumn) { using enum Column; case Title: std::ranges::sort ( item_list, std::ranges::less (), [this] (itemState &p) { auto index = getFileIndexFromTreeItem (p.first); return index->title; }); break; case MTime: std::ranges::sort ( item_list, std::ranges::less (), [this] (itemState &p) { auto index = getFileIndexFromTreeItem (p.first); return index->mtime; }); break; case FileSize: std::ranges::sort ( item_list, std::ranges::less (), [this] (itemState &p) { auto index = getFileIndexFromTreeItem (p.first); return index->size; }); break; } } mTreeWidget.setUpdatesEnabled (false); for (auto i = 0; static_cast (i) < item_list.size (); ++i) { auto p = item_list[i]; auto oi = root->indexOfChild (p.first); if (oi != i) { root->takeChild (oi); root->insertChild (i, p.first); p.first->setExpanded (p.second); } } mTreeWidget.setUpdatesEnabled (true); } mTreeWidget.update (); } void Directory::onRowActivated ([[maybe_unused]] QTreeWidgetItem *item, [[maybe_unused]] int column) { mFrame->directoryShowPage (currentItemFileIndex (), true); mFrame->toggledControlDirectory (true); } void Directory::onRowDoubleClicked () { parentWidget ()->setFocus (); } void Directory::onContextMenuRequest (const QPoint &point) { auto items = mTreeWidget.selectedItems (); if (items.isEmpty ()) return; auto item = items[0]; auto index = getFileIndexFromTreeItem (item); if (index->type == FileIndexType::FILE) { mItemMenu.clear (); if (items.size () == 1) { auto rename_action = mItemMenu.addAction (tr ("Rename File")); QObject::connect (rename_action, SIGNAL (triggered (bool)), this, SLOT (onFileRename ())); } if (std::all_of (items.begin (), items.end (), [this] (QTreeWidgetItem *a) { auto i = getFileIndexFromTreeItem (a); return i && i->type == FileIndexType::FILE; })) { auto del_action = mItemMenu.addAction (tr ("Delete File")); del_action->setIcon ( QIcon::fromTheme (QIcon::ThemeIcon::EditDelete)); QObject::connect (del_action, SIGNAL (triggered (bool)), this, SLOT (onFileDelete ())); } mItemMenu.popup (mTreeWidget.mapToGlobal (point)); } } void Directory::selectFirstItem () { if (setCurrentIndex (mFrame->filename (), mFrame->pageNumber (), "")) return; if (mTreeWidget.topLevelItemCount () > 0) { auto itr = mTreeWidget.topLevelItem (0); setItemSelected (itr); } } FileIndex * Directory::currentItemFileIndex () { auto item = selectedTreeItem (); auto index = getFileIndexFromTreeItem (item); return index; } FileIndex * Directory::currentFileFileIndex () { auto item = selectedTreeItem (); auto index = treeItemToFileIndex (item); return index; } } // Local Variables: // mode: c++ // End: apvlv-0.7.0-alpha/src/ApvlvNote.cc0000644000175100017510000001322214726051136017272 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * * Copyright (C) 2008 Alf. * * Contact: Alf * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2.0 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ /* @CPPFILE ApvlvEditor.cc * * Author: Alf */ #include #include #include #include "ApvlvFile.h" #include "ApvlvNote.h" #include "ApvlvUtil.h" namespace apvlv { using namespace std; void Location::set (int page1, const ApvlvPoint *point1, int offset1, const std::string &path1, const std::string &anchor1) { page = page1; x = point1->x; y = point1->y; offset = offset1; path = path1; anchor = anchor1; } Note::Note (File *file) : mFile (file) {} Note::~Note () = default; bool Note::loadStreamV1 (std::ifstream &is) { string line; do { getline (is, line); // skip path } while (!line.starts_with ("# Meta Data")); // parse ^- tag: getline (is, line); if (line.starts_with ("- tag: ")) { stringstream s (line); string dash; string token; string tags; s >> dash >> token >> tags; for (auto it = tags.begin (); it != tags.end ();) { if (*it != ',') { ++it; continue; } auto offset = it - tags.begin (); string tag = tags.substr (0, offset); mTagSet.insert (tag); tags = tags.substr (offset + 1); it = tags.begin (); } } // parse ^- score: getline (is, line); if (line.starts_with ("- score: ")) { stringstream s (line); string pad; s >> pad >> pad >> mScore; } while (!line.starts_with ("# Comments")) { getline (is, line); } while (true) { getline (is, line); stringstream s (line); string dash; int number; s >> dash >> number; if (dash != "-") { break; } Comment comment; is >> comment; mCommentList.insert ({ comment.begin, comment }); } while (!line.starts_with ("# References")) { getline (is, line); } do { getline (is, line); stringstream s (line); string dash; string ref; s >> dash >> ref; mReferences.insert (ref); } while (line.starts_with ("- ")); while (!line.starts_with ("# Links")) { getline (is, line); } do { getline (is, line); stringstream s (line); string dash; string link; s >> dash >> link; mLinks.insert (link); } while (line.starts_with ("- ")); return true; } bool Note::loadStream (std::ifstream &is) { string line; getline (is, line); if (!line.starts_with ("---")) { qWarning () << "note header not found"; return false; } string version; is >> version >> version; if (version == "1") { return loadStreamV1 (is); } return false; } bool Note::load (std::string_view sv) { string path = string (sv); if (path.empty ()) path = notePathOfFile (mFile); ifstream ifs{ path }; if (!ifs.is_open ()) return false; auto ret = loadStream (ifs); ifs.close (); return ret; } bool Note::dumpStream (std::ostream &os) { os << "---" << endl; os << "version: 1" << endl; os << "path: " << mFile->getFilename () << endl; os << "---" << endl; os << "# Meta Data" << endl; os << "- tag: "; std::ranges::for_each (mTagSet, [&os] (const string &tag) { os << tag << ","; }); os << endl; os << "- score: " << mScore << endl; os << endl; os << "# Comments" << endl; auto index = 0; std::ranges::for_each (mCommentList, [&os, &index] (const pair &pair1) { os << " - " << index++ << endl; os << pair1.second; }); os << endl; os << "# References" << endl; std::ranges::for_each ( mReferences, [&os] (const string &ref) { os << "- " << ref << endl; }); os << endl; os << "# Links" << endl; std::ranges::for_each ( mLinks, [&os] (const string &link) { os << "- " << link << endl; }); os << endl; return true; } bool Note::dump (std::string_view sv) { string path = string (sv); if (path.empty ()) path = notePathOfFile (mFile); auto fspath = filesystem::path (path).parent_path (); std::error_code code; filesystem::create_directories (fspath, code); ofstream ofs{ path }; if (!ofs.is_open ()) return false; auto ret = dumpStream (ofs); ofs.close (); return ret; } std::string Note::notePathOfFile (File *file) { auto homedir = QDir::home ().filesystemAbsolutePath ().string (); auto filename = file->getFilename (); if (filename.find (homedir) == 0) filename = filename.substr (homedir.size () + 1); if (filename[0] == filesystem::path::preferred_separator) filename = filename.substr (1); if (filename[1] == ':') filename[1] = '-'; auto path = NotesDir + filesystem::path::preferred_separator + filename; return path + ".md"; } } apvlv-0.7.0-alpha/src/ApvlvUtil.cc0000644000175100017510000001616314726051136017311 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * * Copyright (C) 2008 Alf. * * Contact: Alf * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2.0 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ /* @CPPFILE ApvlvUtil.cc * * Author: Alf */ #include #include #include #include #include #include #include #include #include #include "ApvlvUtil.h" namespace apvlv { using namespace std; string HelpPdf; string IniExam; string IconDir; string IconFile; string IconPage; string Translations; string IniFile; string SessionFile; string LogFile; string NotesDir; static void getXdgOrHomeIni (const QString &appdir) { auto sysenv = QProcessEnvironment::systemEnvironment (); auto xdgdir = sysenv.value ("XDG_CONFIG_DIR").toStdString (); auto homedir = sysenv.value ("HOME").toStdString (); if (homedir.empty ()) { homedir = QDir::homePath ().toStdString (); } IniFile = appdir.toStdString () + "/.apvlvrc"; if (!xdgdir.empty ()) { IniFile = xdgdir + "/apvlv/apvlvrc"; } else if (!homedir.empty ()) { IniFile = homedir + "/.config/apvlv/apvlvrc"; if (!filesystem::is_regular_file (IniFile)) { IniFile = homedir + "/.apvlvrc"; } } } static void getXdgOrCachePath (const QString &appdir) { auto sysenv = QProcessEnvironment::systemEnvironment (); auto xdgdir = sysenv.value ("XDG_CACHE_HOME").toStdString (); auto homedir = sysenv.value ("HOME").toStdString (); if (homedir.empty ()) { homedir = QDir::homePath ().toStdString (); } SessionFile = appdir.toStdString () + "/apvlvinfo"; if (!xdgdir.empty ()) { SessionFile = xdgdir + "/apvlvinfo"; } else if (!homedir.empty ()) { SessionFile = homedir + "/.cache/apvlvinfo"; } } void getRuntimePaths () { auto dirpath = QDir (QCoreApplication::applicationDirPath ()); dirpath.cdUp (); auto prefix = dirpath.path ().toStdString (); auto apvlvdir = prefix + "/share/doc/apvlv"; HelpPdf = apvlvdir + "/Startup.pdf"; IconDir = apvlvdir + "/icons/dir.png"; IconFile = apvlvdir + "/icons/pdf.png"; IconPage = apvlvdir + "/icons/reg.png"; Translations = apvlvdir + "/translations"; #ifndef WIN32 IniExam = string (SYSCONFDIR) + "/apvlvrc.example"; #else IniExam = apvlvdir + "/apvlvrc.example"; #endif getXdgOrHomeIni (dirpath.path ()); getXdgOrCachePath (dirpath.path ()); auto homedir = QDir::homePath ().toStdString (); NotesDir = homedir + "/" + "ApvlvNotes"; } optional> xmlContentGetElement (const char *content, size_t length, const vector &names) { auto bytes = QByteArray{ content, (qsizetype)length }; auto xml = make_unique (bytes); std::ptrdiff_t state = 0; while (!xml->atEnd ()) { if (xml->isStartElement ()) { auto name = xml->name ().toString ().toStdString (); auto iter = std::ranges::find (names, name); if (iter == names.end ()) { xml->readNextStartElement (); continue; } auto pos = distance (names.begin (), iter); if (state == pos) { state++; } if (state == static_cast (names.size ())) { return xml; } } xml->readNextStartElement (); } return nullopt; } string xmlStreamGetAttributeValue (QXmlStreamReader *xml, const string &key) { auto attrs = xml->attributes (); for (auto &attr : attrs) { if (attr.name ().toString ().toStdString () == key) return attr.value ().toString ().toStdString (); } return ""; } string xmlContentGetAttributeValue (const char *content, size_t length, const vector &names, const string &key) { auto optxml = xmlContentGetElement (content, length, names); if (!optxml) return ""; auto xml = optxml->get (); return xmlStreamGetAttributeValue (xml, key); } string filenameExtension (const string &filename) { auto pointp = filename.rfind ('.'); if (pointp == string::npos) return ""; string ext = filename.substr (pointp); std::ranges::transform (ext, ext.begin (), ::tolower); return ext; } void imageArgb32ToRgb32 (QImage &image, int left, int top, int right, int bottom) { for (auto x = left; x < right; ++x) { for (auto y = top; y < bottom; ++y) { auto c = image.pixelColor (x, y); double ra = double (c.alpha ()) / 255.0; auto nr = static_cast (c.red () * ra + 255 * (1.0 - ra)); auto ng = static_cast (c.green () * ra + 255 * (1.0 - ra)); auto nb = static_cast (c.blue () * ra + 255 * (1.0 - ra)); auto pc = QColor::fromRgb (nr, ng, nb, 255); image.setPixelColor (x, y, pc); } } } string templateBuild (string_view temp, string_view token, string_view real) { auto pos = temp.find (token); if (pos == string::npos) return string (temp); auto first = temp.substr (0, pos); auto second = temp.substr (pos + token.length ()); auto outstr = string (first); outstr += string (real); outstr += string (second); return outstr; } qint64 parseFormattedDataSize (const QString &sizeStr) { QString cleanStr = sizeStr.simplified ().toUpper (); QRegularExpression re ("^(\\d+(?:\\.\\d+)?)(\\s*)(B?|KB?|MB?|GB?|TB?)$"); QRegularExpressionMatch match = re.match (cleanStr); if (match.hasMatch ()) { double number = match.captured (1).toDouble (); QString unit = match.captured (3); if (unit.isEmpty ()) return static_cast (number); switch (unit[0].unicode ()) { case 'T': return static_cast (number * 1024 * 1024 * 1024 * 1024); case 'G': return static_cast (number * 1024 * 1024 * 1024); case 'M': return static_cast (number * 1024 * 1024); case 'K': return static_cast (number * 1024); default: return static_cast (number); } } return -1; } qint64 filesystemTimeToMSeconds (std::filesystem::file_time_type ftt) { auto epoch = ftt.time_since_epoch () + chrono::seconds{ 6437664000 }; auto milliseconds = chrono::duration_cast (epoch); return static_cast (milliseconds.count ()); } } // Local Variables: // mode: c++ // End: apvlv-0.7.0-alpha/src/ApvlvFileWidget.h0000644000175100017510000000712714726051136020261 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * * Copyright (C) 2008 Alf. * * Contact: Alf * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2.0 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ /* @CPPFILE ApvlvFileWidget.h * * Author: Alf */ #ifndef _APVLV_FILE_WIDGET_H_ #define _APVLV_FILE_WIDGET_H_ #include #include #include "ApvlvFile.h" namespace apvlv { const int WORD_WIDTH_DEFAULT = 40; const int LINE_HEIGHT_DEFAULT = 15; const double DEFAULT_ZOOMRATE = 1.3f; const int INVALID_PAGENUM = -1; class File; class FileWidget : public QObject { Q_OBJECT public: FileWidget () = default; ~FileWidget () override = default; [[nodiscard]] virtual QWidget *widget () = 0; [[nodiscard]] virtual File * file () const { return mFile; } virtual void setFile (File *file) { mFile = file; } virtual int pageNumber () { return mPageNumber; } virtual std::string anchor () { return mAnchor; } virtual double zoomrate () { return mZoomrate; } virtual void showPage (int pn, double rate) { mPageNumber = pn; mScrollValue = rate; } virtual void showPage (int pn, const std::string &anchor) { mPageNumber = pn; mAnchor = anchor; } virtual void scroll (int times, int w, int h); virtual double scrollRate (); virtual void scrollTo (double s, double y); virtual void scrollUp (int times); virtual void scrollDown (int times); virtual void scrollLeft (int times); virtual void scrollRight (int times); virtual void setZoomrate (double zm) { mZoomrate = zm; } virtual void setRotate (int rot) { mRotate = rot; } virtual int rotate () { return mRotate; } void setAnchor (const std::string &anchor) { mAnchor = anchor; } virtual void setSearchSelect (int select) { mSearchSelect = select; } [[nodiscard]] virtual int searchSelect () const { return mSearchSelect; } virtual void setSearchStr (const std::string &str) { mSearchStr = str; } [[nodiscard]] virtual std::string searchStr () const { return mSearchStr; } virtual void setSearchResults (const WordListRectangle &wlr) { mSearchResults = wlr; } virtual const WordListRectangle & searchResults () { return mSearchResults; } virtual void setSelects (const std::vector &rect_list) { mSelects = rect_list; } virtual const std::vector & selects () { return mSelects; } protected: QScrollBar *mValScrollBar{ nullptr }; QScrollBar *mHalScrollBar{ nullptr }; File *mFile{ nullptr }; int mPageNumber{ INVALID_PAGENUM }; double mScrollValue{ 0.0f }; std::string mAnchor; double mZoomrate{ DEFAULT_ZOOMRATE }; int mRotate{ 0 }; std::string mSearchStr; WordListRectangle mSearchResults; int mSearchSelect{ 0 }; std::vector mSelects; }; } #endif /* Local Variables: */ /* mode: c++ */ /* End: */ apvlv-0.7.0-alpha/src/ApvlvParams.h0000644000175100017510000000406114726051136017453 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * * Copyright (C) 2008 Alf. * * Contact: Alf * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2.0 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ /* @CPPFILE ApvlvParams.h * * Author: Alf */ #ifndef _APVLV_PARAMS_H_ #define _APVLV_PARAMS_H_ #include #include #include namespace apvlv { class ApvlvParams final { public: ApvlvParams (const ApvlvParams &) = delete; const ApvlvParams &operator= (const ApvlvParams &) = delete; ApvlvParams (const ApvlvParams &&) = delete; const ApvlvParams &&operator= (const ApvlvParams &&) = delete; bool loadFile (const std::string &filename); bool push (std::string_view ch, std::string_view str); std::string getGroupStringOrDefault (std::string_view entry, std::string_view key, const std::string &defs = ""); std::string getStringOrDefault (std::string_view key, const std::string &defs = ""); int getIntOrDefault (std::string_view key, int defi = 0); bool getBoolOrDefault (std::string_view key, bool defb = false); static ApvlvParams * instance () { static ApvlvParams inst; return &inst; } private: ApvlvParams (); ~ApvlvParams (); std::map mParamMap; }; } #endif /* Local Variables: */ /* mode: c++ */ /* End: */ apvlv-0.7.0-alpha/src/ApvlvWindow.cc0000644000175100017510000002645614726051136017651 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * * Copyright (C) 2008 Alf. * * Contact: Alf * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2.0 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ /* @CPPFILE ApvlvWindow.cc * * Author: Alf */ #include #include #include #include "ApvlvParams.h" #include "ApvlvView.h" #include "ApvlvWindow.h" namespace apvlv { ApvlvWindow::ApvlvWindow () { qDebug () << "win: " << this << ", init"; setLayout (&mLayout); mLayout.addWidget (&mPaned); } ApvlvWindow::~ApvlvWindow () { qDebug () << "win: " << this << ", released"; if (mType == WindowType::FRAME) { auto frame = stealFrame (); if (frame) frame->inuse (false); } } CmdReturn ApvlvWindow::process (int ct, uint key) { ApvlvWindow *nwin; qDebug () << "input [" << key << "]"; switch (key) { case ctrlValue ('w'): case 'k': case 'j': case 'h': case 'l': nwin = getNeighbor (ct, key); if (nwin != nullptr && nwin != this) { nwin->setActive (true); } break; case '-': smaller (ct); break; case '+': bigger (ct); break; default: break; } return CmdReturn::MATCH; } ApvlvWindow * ApvlvWindow::findWindowByWidget (QWidget *widget) { auto doc = ApvlvFrame::findByWidget (widget); if (doc == nullptr) return nullptr; std::stack winstack; winstack.push (this); while (!winstack.empty ()) { auto win = winstack.top (); winstack.pop (); if (win->mType == ApvlvWindow::WindowType::FRAME) { if (win->getFrame () == doc) return win; } else { winstack.push (win->firstWindow ()); winstack.push (win->secondWindow ()); } } return nullptr; } ApvlvWindow * ApvlvWindow::getNeighbor (int count, uint key) { ApvlvWindow *last_win = nullptr; ApvlvWindow *win = this; for (int c = 0; c < count; ++c) { switch (key) { case ctrlValue ('w'): win = win->getNext (); break; case 'k': win = win->getTop (); break; case 'j': win = win->getBottom (); break; case 'h': win = win->getLeft (); break; case 'l': win = win->getRight (); break; default: break; } if (win != nullptr) last_win = win; else break; } return last_win; } ApvlvWindow * ApvlvWindow::getLeft () { if (mType == WindowType::FRAME && getFrame ()->toggledControlDirectory (false)) { return this; } ApvlvWindow *fwin = this; while (fwin->parentWindow ()) { fwin = fwin->parentWindow (); if (fwin->mType == WindowType::SP && fwin->firstWindow () != this) { if (fwin->secondWindow () == this) return fwin->firstWindow (); while (fwin->mType != WindowType::FRAME) { fwin = fwin->secondWindow (); } return fwin; } } return nullptr; } ApvlvWindow * ApvlvWindow::getRight () { if (mType == WindowType::FRAME && getFrame ()->toggledControlDirectory (true)) { return this; } ApvlvWindow *fwin = this; while (fwin->parentWindow ()) { fwin = fwin->parentWindow (); if (fwin->mType == WindowType::SP && fwin->secondWindow () != this) { if (fwin->firstWindow () == this) return fwin->secondWindow (); while (fwin->mType != WindowType::FRAME) { fwin = fwin->firstWindow (); } return fwin; } } return nullptr; } ApvlvWindow * ApvlvWindow::getTop () { ApvlvWindow *fwin = this; while (fwin->parentWindow ()) { fwin = fwin->parentWindow (); if (fwin->mType == WindowType::VSP && (fwin->firstWindow () != this)) { if (fwin->secondWindow () == this) return fwin->firstWindow (); while (fwin->mType != WindowType::FRAME) { fwin = fwin->secondWindow (); } return fwin; } } return nullptr; } ApvlvWindow * ApvlvWindow::getBottom () { ApvlvWindow *fwin = this; while (fwin->parentWindow ()) { fwin = fwin->parentWindow (); if (fwin->mType == WindowType::VSP && fwin->secondWindow () != this) { if (fwin->firstWindow () == this) return fwin->secondWindow (); while (fwin->mType != WindowType::FRAME) { fwin = fwin->firstWindow (); } return fwin; } } return nullptr; } void ApvlvWindow::splitWidget (WindowType type, QWidget *one, QWidget *other) { setFrameStyle (QFrame::NoFrame); setLineWidth (0); mPaned.setOrientation (type == WindowType::SP ? Qt::Horizontal : Qt::Vertical); mPaned.setHandleWidth (10); mPaned.setStretchFactor (0, 1); mPaned.setStretchFactor (1, 1); mPaned.addWidget (one); mPaned.addWidget (other); mType = type; } void ApvlvWindow::perishWidget () { auto par = parentWindow (); auto win1 = par->firstWindow (); auto win2 = par->secondWindow (); auto rewin = (this == win1 ? win2 : win1); auto par_par = par->parent (); if (par_par->inherits ("QStackedWidget")) { win1->setParent (nullptr); win2->setParent (nullptr); if (rewin->mType == WindowType::FRAME) { auto frame = rewin->stealFrame (); par->setFrame (frame); } else { par->splitWidget (rewin->mType, rewin->firstWindow (), rewin->secondWindow ()); } qDebug () << win1 << " will be deleted "; win1->deleteLater (); qDebug () << win2 << " will be deleted "; win2->deleteLater (); } else { auto par_splitter = dynamic_cast (par_par); auto par0 = dynamic_cast (par_splitter->widget ((0))); auto par1 = dynamic_cast (par_splitter->widget ((1))); rewin->setParent (nullptr); par0->setParent (nullptr); par1->setParent (nullptr); if (par0 == par) { qDebug () << rewin << " and " << par1 << " will be inserted "; par_splitter->addWidget (rewin); par_splitter->addWidget (par1); qDebug () << par0 << " will be deleted "; par0->deleteLater (); } else { qDebug () << par0 << " and " << rewin << " will be inserted "; par_splitter->addWidget (par0); par_splitter->addWidget (rewin); qDebug () << par1 << " will be deleted "; par1->deleteLater (); } } } void ApvlvWindow::setAsRootActive () { auto root_win = rootWindow (); if (root_win) root_win->setActiveWindow (this); } ApvlvWindow * ApvlvWindow::getNext () { auto *n = getRight (); if (n) return n; n = getBottom (); if (n) return n; n = getLeft (); if (n) return n; n = getTop (); return n; } // birth a new FRAME window, and the new window beyond the input doc // this made a FRAME window to SP|VSP bool ApvlvWindow::birth (WindowType type, ApvlvFrame *doc) { Q_ASSERT (mType == WindowType::FRAME); auto frame = dynamic_cast (mPaned.widget (0)); frame->setParent (nullptr); if (doc == nullptr) { doc = frame->clone (); if (doc == nullptr) { frame->mView->errorMessage ("can't split"); return false; } frame->mView->regLoaded (doc); } auto win1 = new ApvlvWindow (); win1->setFrame (frame); auto win2 = new ApvlvWindow (); win2->setFrame (doc); qDebug () << "win: " << this << " birth two: " << win1 << " and " << win2; splitWidget (type, win1, win2); if (auto root = rootWindow (); root && root->mActive == this) { root->mActive = win1; } return true; } void ApvlvWindow::perish () { qDebug () << "win: " << this << " try to perish "; if (mType != WindowType::FRAME) return; if (auto root = rootWindow (); root && root->mActive == this) { root->mActive = nullptr; } perishWidget (); } void ApvlvWindow::setActive (bool act) { Q_ASSERT (mType == WindowType::FRAME); QTimer::singleShot (50, getFrame (), SLOT (setFocus ())); } void ApvlvWindow::setFrame (ApvlvFrame *doc) { qDebug () << "win: " << this << ", set core: " << doc; setFrameStyle (QFrame::Raised | QFrame::Box); setLineWidth (1); if (mType == WindowType::FRAME) { auto frame = stealFrame (); if (frame) { frame->inuse (false); } } mPaned.setStretchFactor (0, 1); mPaned.addWidget (doc); doc->inuse (true); mType = WindowType::FRAME; QObject::connect (doc, SIGNAL (focusIn ()), this, SLOT (setAsRootActive ())); } ApvlvFrame * ApvlvWindow::stealFrame () { Q_ASSERT (mType == WindowType::FRAME); auto frame = dynamic_cast (mPaned.widget (0)); if (frame) { frame->setParent (nullptr); } return frame; } ApvlvFrame * ApvlvWindow::getFrame () { Q_ASSERT (mType == WindowType::FRAME); auto frame = dynamic_cast (mPaned.widget (0)); return frame; } ApvlvWindow * ApvlvWindow::firstWindow () { Q_ASSERT (mType != WindowType::FRAME); auto win = dynamic_cast (mPaned.widget (0)); return win; } ApvlvWindow * ApvlvWindow::secondWindow () { Q_ASSERT (mType != WindowType::FRAME); auto win = dynamic_cast (mPaned.widget (1)); return win; } ApvlvWindow * ApvlvWindow::parentWindow () { auto win = parent (); Q_ASSERT (win); if (win->inherits ("QSplitter")) win = win->parent (); return dynamic_cast (win); } ApvlvWindow * ApvlvWindow::rootWindow () { auto win = this; while (win && !win->isRoot ()) win = win->parentWindow (); return win; } ApvlvWindow * ApvlvWindow::firstFrameWindow () { auto win = this; while (win->mType != WindowType::FRAME) { win = win->firstWindow (); } return win; } bool ApvlvWindow::isRoot () { auto par = parentWindow (); if (par == nullptr) return true; else return false; } void ApvlvWindow::smaller (int times) { if (isRoot ()) return; auto pwin = parentWindow (); auto sizes = pwin->mPaned.sizes (); int len = 20 * times; if (pwin->firstWindow () == this) { sizes[0] -= len; sizes[1] += len; } else { sizes[0] += len; sizes[1] -= len; } pwin->mPaned.setSizes (sizes); } void ApvlvWindow::bigger (int times) { if (isRoot ()) return; smaller (0 - times); } } // Local Variables: // mode: c++ // End: apvlv-0.7.0-alpha/src/ApvlvCmds.cc0000644000175100017510000002236714726051136017265 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * * Copyright (C) 2008 Alf. * * Contact: Alf * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2.0 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ /* @CPPFILE ApvlvCmds.cc * * Author: Alf */ #include #include #include #include "ApvlvCmds.h" #include "ApvlvView.h" namespace apvlv { using namespace std; using namespace Qt; StringKeyMap Command::mKeyMap = { { "", Key_Backspace }, { "", Key_Tab }, { "", Key_Return }, { "", Key_Escape }, { "", Key_Space }, { "", Key_Less }, { "", Key_Backslash }, { "", Key_Bar }, { "", Key_Delete }, { "", Key_Up }, { "", Key_Down }, { "", Key_Left }, { "", Key_Right }, { "", Key_Help }, { "", Key_Insert }, { "", Key_Home }, { "", Key_End }, { "", Key_PageUp }, { "", Key_PageDown }, { "", Key_Up }, { "", Key_Down }, { "", Key_Left }, { "", Key_Right }, { "", Key_MediaPrevious }, { "", Key_MediaNext }, { "", Key_Home }, { "", Key_End }, }; CommandMap ApvlvCmds::mMaps; static int keyToControlChar (QKeyEvent *key) { int char_key = key->key (); if (key->modifiers () & Qt::ShiftModifier) { char_key = toupper (char_key); } else { char_key = tolower (char_key); } if (key->modifiers () & ControlModifier) char_key = ctrlValue (char_key); return char_key; } constexpr static bool isModifierKey (uint k) { switch (k) { case Key_Shift: case Key_CapsLock: case Key_Meta: case Key_Alt: case Key_Super_L: case Key_Super_R: case Key_Hyper_L: case Key_Hyper_R: case Key_Control: return true; default: return false; } } Command::Command () : mType (CmdType::CT_CMD), mHasPreCount (false), mPreCount (1), mOrigin (nullptr) { } void Command::type (CmdType type) { mType = type; } CmdType Command::type () { return mType; } void Command::push (string_view sv, CmdType type) { mType = type; mHasPreCount = false; mPreCount = 1; auto s = sv.data (); if (isdigit (*s)) { mHasPreCount = true; mPreCount = (signed int)strtol (s, nullptr, 10); do { s++; } while (isdigit (*s)); } if (*s == ':' || *s == '/' || *s == '?') { mStrCommand = s; mType = CmdType::CT_STRING; size_t off = mStrCommand.find (""); if (off != string::npos) { mStrCommand.erase (off, mStrCommand.length () - off); mType = CmdType::CT_STRING_RETURN; mNext = make_unique (); mNext->push (s + off + 4); } qDebug () << "set string type command: [" << mStrCommand << "]"; return; } while (*s != '\0') { s = append (s); } } void Command::process (ApvlvView *view) { if (type () == CmdType::CT_STRING) { view->promptCommand (mStrCommand.c_str ()); } else if (type () == CmdType::CT_STRING_RETURN) { view->run (mStrCommand.c_str ()); } else { for (uint k = 0; k < keyVals ()->size (); ++k) { int key = keyval (k); if (key > 0) view->process (mHasPreCount, preCount (), keyval (k)); } } if (next () != nullptr) { next ()->process (view); } } bool Command::append (QKeyEvent *key) { if (isModifierKey (key->key ())) return false; auto char_key = keyToControlChar (key); mKeyVals.push_back (char_key); return true; } const char * Command::append (const char *s) { size_t len; const char *e = strchr (s, '>'); len = strlen (s); if (len >= 4 && *s == '<' && (*e != '\0' && *(s + 2) != '-')) { e++; for (const auto &it : mKeyMap) { if (it.first.compare (0, e - s, s) == 0) { mKeyVals.push_back (it.second); return e; } } } if (len >= 5 && s[0] == '<' && s[2] == '-' && s[4] == '>') { if (s[1] == 'C') { mKeyVals.push_back (ctrlValue (s[3])); } else { qWarning () << "Can't recognize the symbol: " << s; } return s + 5; } else { mKeyVals.push_back (s[0]); return s + 1; } } void Command::setPreCount (int precount) { mPreCount = precount; mHasPreCount = true; } int Command::preCount () const { return mPreCount; } void Command::origin (Command *ori) { mOrigin = ori; } Command * Command::origin () { return mOrigin; } CommandKeyList * Command::keyVals () { return &mKeyVals; } CommandKeyList Command::keyvalv () { return mKeyVals; } Command * Command::next () { return mNext.get (); } int Command::keyval (uint id) { return id >= mKeyVals.size () ? -1 : mKeyVals[id]; } void ApvlvCmds::buildCommandMap (string_view os, string_view ms) { Command fir; fir.push (os); auto *secp = new Command (); secp->push (ms); for (auto &mMap : mMaps) { if (mMap.first == fir.keyvalv ()) { delete mMap.second; mMap.second = secp; return; } } mMaps[fir.keyvalv ()] = secp; } ApvlvCmds::ApvlvCmds (ApvlvView *view) { mView = view; mState = CmdState::CMD_OK; mTimeoutTimer = make_unique (this); QObject::connect (mTimeoutTimer.get (), SIGNAL (timeout ()), this, SLOT (timeoutCallback ())); } ApvlvCmds::~ApvlvCmds () { if (mTimeoutTimer->isActive ()) { mTimeoutTimer->stop (); } } void ApvlvCmds::append (QKeyEvent *gev) { if (mTimeoutTimer->isActive ()) { mTimeoutTimer->stop (); } if (mState == CmdState::GETTING_CMD) { CommandKeyList v = mCmdHead->keyvalv (); v.push_back (keyToControlChar (gev)); CmdReturn r = isMapCommand (&v); if (r == CmdReturn::NO_MATCH) { process (mCmdHead.get ()); mCmdHead.release (); mState = CmdState::CMD_OK; } } if (mCmdHead == nullptr) mCmdHead = make_unique (); if (mState == CmdState::CMD_OK) { if (isdigit (int (gev->key ())) && gev->key () != '0') { auto c = char (gev->key ()); mCountString += c; mState = CmdState::GETTING_COUNT; mTimeoutTimer->start (3000); return; } } else if (mState == CmdState::GETTING_COUNT) { if (isdigit (int (gev->key ()))) { auto c = char (gev->key ()); mCountString += c; mTimeoutTimer->start (3000); return; } else { if (!mCountString.empty ()) { mCmdHead->setPreCount ( int (strtol (mCountString.c_str (), nullptr, 10))); mCountString = ""; } } } bool valid = mCmdHead->append (gev); if (!valid) { mTimeoutTimer->start (3000); return; } mState = CmdState::GETTING_CMD; CmdReturn ret = isMapCommand (mCmdHead->keyVals ()); if (ret == CmdReturn::NEED_MORE) { mTimeoutTimer->start (3000); return; } Command *pcmd; if (ret == CmdReturn::MATCH) { pcmd = getMapCommand (mCmdHead.get ()); pcmd->origin (mCmdHead.get ()); process (pcmd); pcmd->origin (nullptr); pcmd = nullptr; } else { pcmd = process (mCmdHead.get ()); } mCmdHead.reset (pcmd); mState = CmdState::CMD_OK; } Command * ApvlvCmds::process (Command *cmd) { uint times = 1; Command *orig = cmd->origin (); if (orig != nullptr) { times = orig->preCount (); } for (uint i = 0; i < times; ++i) { cmd->process (mView); } return orig; } CmdReturn ApvlvCmds::isMapCommand (CommandKeyList *ack) { for (auto &mMap : mMaps) { if (*ack == mMap.first) { return CmdReturn::MATCH; } else { uint i; for (i = 0; i < ack->size (); ++i) { if ((*ack)[i] != mMap.first[i]) break; } if (i == ack->size ()) { return CmdReturn::NEED_MORE; } } } return CmdReturn::NO_MATCH; } Command * ApvlvCmds::getMapCommand (Command *cmd) { auto it = mMaps.find (*cmd->keyVals ()); return it != mMaps.end () ? it->second : nullptr; } void ApvlvCmds::timeoutCallback () { if (mCmdHead != nullptr) { process (mCmdHead.get ()); mCmdHead.release (); } mState = CmdState::CMD_OK; } } // Local Variables: // mode: c++ // End: apvlv-0.7.0-alpha/src/ApvlvLab.cc0000644000175100017510000001111014726051136017055 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * Copyright (C) <2010> * * Contact: Alf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ /* @CPPFILE ApvlvLab.cc * * Author: Alf */ #include "ApvlvLab.h" #include "ApvlvUtil.h" #include "ApvlvWebViewWidget.h" namespace apvlv { using namespace std; const string stylesheet_content = ".block_c {\n" " display: block;\n" " font-size: 2.5em;\n" " font-weight: normal;\n" " line-height: 33.6pt;\n" " text-align: center;\n" " text-indent: 0;\n" " margin: 17pt 0;\n" " padding: 0;\n" "}\n" ".block_ {\n" " display: block;\n" " font-size: 1.5em;\n" " font-weight: normal;\n" " line-height: 33.6pt;\n" " text-align: justify;\n" " text-indent: 0;\n" " margin: 17pt 0;\n" " padding: 0;\n" "}\n" ".block_1 {\n" " display: block;\n" " line-height: 1.2;\n" " text-align: justify;\n" " margin: 0 0 7pt;\n" " padding: 0;\n" "}\n"; const string title_template = "\n" "\n" " \n" " \n" " \n" " \n" " \n" " \n" "
\n" "
\n" "
\n" "
\n" " %s\n" " \n" "\n"; const string section_template = "\n" "\n" " \n" " \n" " \n" " \n" " \n" " \n" " %s\n" " \n" "\n"; bool ApvlvLab::load (const string &filename) { return false; } ApvlvLab::~ApvlvLab () = default; bool ApvlvLab::pageRenderToWebView (int pn, double zm, int rot, WebView *webview) { webview->setZoomFactor (zm); QUrl url = QString ("apvlv:///") + QString::number (pn); webview->load (url); return true; } } // Local Variables: // mode: c++ // End: apvlv-0.7.0-alpha/src/file/0000755000175100017510000000000014726051136015764 5ustar atzlinuxatzlinuxapvlv-0.7.0-alpha/src/file/ApvlvMuPdf.h0000644000175100017510000000430514726051136020163 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * Copyright (C) <2010> * * Contact: Alf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ /* @CPPFILE ApvlvPdf.h * * Author: Alf */ #ifndef _APVLV_MUPDF_H_ #define _APVLV_MUPDF_H_ #include #include #include "ApvlvFile.h" namespace apvlv { class ApvlvMuPDF : public File { FILE_TYPE_DECLARATION (ApvlvMuPDF); public: ApvlvMuPDF (); ~ApvlvMuPDF () override; bool load (const std::string &filename) override; [[nodiscard]] DISPLAY_TYPE getDisplayType () const override { return DISPLAY_TYPE::IMAGE; } SizeF pageSizeF (int page, int rot) override; int sum () override; bool pageIsOnlyImage (int pn) override; bool pageRenderToImage (int pn, double zm, int rot, QImage *img) override; std::optional > pageHighlight (int pn, const ApvlvPoint &pa, const ApvlvPoint &pb) override; bool pageText (int pn, const Rectangle &rect, std::string &text) override; std::unique_ptr pageSearch (int pn, const char *str) override; private: fz_context *mContext; fz_document *mDoc; void pageRenderComments (int pn, fz_pixmap *pixmap, const std::vector &comments, const fz_matrix &mat); void generateIndex (); void generateIndexRecursively (FileIndex &index, const fz_outline *outline); }; } #endif /* Local Variables: */ /* mode: c++ */ /* End: */ apvlv-0.7.0-alpha/src/file/ApvlvAxOffice.cc0000644000175100017510000002242214726051136020772 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * Copyright (C) <2010> * * Contact: Alf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ /* @CPPFILE ApvlvOffice.cc * * Author: Alf */ #include #include #include #include #include #include #include "ApvlvAxOffice.h" #include "ApvlvUtil.h" #include "ApvlvWebViewWidget.h" namespace apvlv { using namespace std; FILE_TYPE_DEFINITION ("MSOffice", ApvlvOfficeWord, { ".doc", ".docx" }); FILE_TYPE_DEFINITION ("MSOffice", ApvlvPowerPoint, { ".ppt", ".pptx" }); FILE_TYPE_DEFINITION ("MSOffice", ApvlvExcel, { ".xls", ".xlsx" }); bool ApvlvOfficeWord::load (const string &filename) { auto qname = QString::fromLocal8Bit (filename); mApp = new QAxWidget ("Word.Application"); mApp->setProperty ("Visible", false); mDocs = mApp->querySubObject ("Documents"); mDoc = mDocs->querySubObject ( "OpenNoRepairDialog(const QString&, bool, bool, bool)", qname, false, true, false); if (mDoc == nullptr) { mApp->dynamicCall ("Quit()"); delete mApp; return false; } return true; } int ApvlvOfficeWord::sum () { auto content = mDoc->querySubObject ("Content"); auto pages = content->dynamicCall ("Information(wdNumberOfPagesInDocument)"); return pages.toInt (); } SizeF ApvlvOfficeWord::pageSizeF (int pn, int rot) { auto pnstr = QString ("Pages(%1)").arg (pn + 1); auto win = mDoc->querySubObject ("ActiveWindow"); auto pane = win->querySubObject ("ActivePane"); auto page = pane->querySubObject (pnstr.toStdString ().c_str ()); auto width = page->property ("Width"); auto height = page->property ("Height"); return { width.toDouble (), height.toDouble () }; } bool ApvlvOfficeWord::pageText (int pn, const Rectangle &rect, string &text) { auto content = mDoc->querySubObject ("Content"); return false; } bool ApvlvOfficeWord::pageRenderToImage (int pn, double zm, int rot, QImage *pix) { char szFormatName[1024]; const char *lpFormatName; static UINT auPriorityList[] = { CF_TEXT, CF_BITMAP }; auto gf = GetPriorityClipboardFormat (auPriorityList, 2); auto pnstr = QString ("Pages(%1)").arg (pn + 1); auto win = mDoc->querySubObject ("ActiveWindow"); auto pane = win->querySubObject ("ActivePane"); auto page = pane->querySubObject (pnstr.toStdString ().c_str ()); auto selection = mApp->querySubObject ("Selection"); selection->dynamicCall ("GoTo(int, int, int, const QVariant&)", 1, 1, 1, page->property ("Start")); selection->dynamicCall ("MoveDown(int, int, int)", 5, 1, 0); selection->dynamicCall ("EndKey(int, int)", 6, 1); selection->dynamicCall ("CopyAsPicture()"); Sleep (1000); auto clip = QApplication::clipboard (); auto mime = clip->mimeData (); for (auto const &t : mime->formats ()) { qDebug () << "clipboard contains: " << t; } // HWND hWnd = (HWND)mApp->winId (); if (OpenClipboard (NULL) == FALSE) { qDebug ("open clipboard error: %d\n", GetLastError ()); return false; } auto uFormat = EnumClipboardFormats (0); while (uFormat) { if (GetClipboardFormatNameA (uFormat, szFormatName, sizeof (szFormatName))) lpFormatName = szFormatName; else lpFormatName = "(unknown)"; qDebug ("get file: %s\n", lpFormatName); uFormat = EnumClipboardFormats (uFormat); } if (IsClipboardFormatAvailable (CF_BITMAP) == FALSE) { qDebug ("no picture: %d\n", GetLastError ()); CloseClipboard (); return false; } HBITMAP hBitmap = static_cast (GetClipboardData (CF_BITMAP)); if (hBitmap == nullptr) { qDebug ("no image: %d\n", GetLastError ()); CloseClipboard (); return false; } IPicture *iPicture = nullptr; auto hr = OleCreatePictureIndirect (reinterpret_cast (hBitmap), IID_IPicture, TRUE, (void **)&iPicture); if (FAILED (hr)) { qDebug ("failed create: %d\n", GetLastError ()); CloseClipboard (); return false; } LPSTREAM stream = NULL; hr = SHCreateStreamOnFileA ("z:\\a.bmp", STGM_CREATE | STGM_WRITE, &stream); if (FAILED (hr)) { iPicture->Release (); CloseClipboard (); return false; } hr = iPicture->SaveAsFile (stream, FALSE, NULL); iPicture->Release (); CloseClipboard (); if (FAILED (hr)) { qDebug ("save failed\n"); return false; } return true; } bool ApvlvOfficeWord::pageRenderToWebView (int pn, double zm, int rot, WebView *webview) { webview->setZoomFactor (zm); QUrl url = QString ("apvlv:///%1").arg (pn); webview->load (url); return true; } optional ApvlvOfficeWord::pathContent (const string &path) { auto pn = QString::fromLocal8Bit (path).toInt (); auto pageRange = mDoc->querySubObject ( "GoTo(int, int, int, const QVariant&)", 1, 1, pn + 1); auto endRange = mDoc->querySubObject ("GoTo(int, int, int, const QVariant&)", 1, 1, pn + 2); if (pageRange && endRange) { int endPosition; if (endRange->property ("Start").toInt () == 1) { auto content = mDoc->querySubObject ("Content"); endPosition = content->property ("End").toInt (); delete content; } else { endPosition = endRange->property ("Start").toInt () - 1; } pageRange->setProperty ("End", endPosition); pageRange->dynamicCall ("Copy()"); } else { qWarning () << "Failed to get page range"; return nullptr; } auto clip = QApplication::clipboard (); auto mime = clip->mimeData (); return QByteArray::fromStdString (mime->html ().toStdString ()); } bool ApvlvPowerPoint::load (const string &filename) { auto qname = QString::fromLocal8Bit (filename); mApp = new QAxWidget ("PowerPoint.Application"); mApp->setProperty ("Visible", false); mDocs = mApp->querySubObject ("Presentations"); mDoc = mDocs->querySubObject ("Open(const QString&, bool, bool, bool)", qname, true, false, false); if (mDoc == nullptr) { mApp->dynamicCall ("Quit()"); delete mApp; return false; } return true; } int ApvlvPowerPoint::sum () { auto slides = mDoc->querySubObject ("Slides"); auto count = slides->property ("Count"); return count.toInt (); } SizeF ApvlvPowerPoint::pageSizeF (int pn, int rot) { auto page = mDoc->querySubObject ("PageSetup"); auto width = page->property ("SlideWidth"); auto height = page->property ("SlideHeight"); return { width.toDouble (), height.toDouble () }; } bool ApvlvPowerPoint::pageText (int pn, const Rectangle &rect, string &text) { auto content = mDoc->querySubObject ("Content"); return false; } bool ApvlvPowerPoint::pageRenderToImage (int pn, double zm, int rot, QImage *pix) { auto slides = mDoc->querySubObject ("Slides"); auto slide = slides->querySubObject ("Item(int)", pn + 1); auto temppath = QString ("%1/apvlv_%2.png").arg (QDir::tempPath ()).arg (rand ()); temppath.replace ("/", "\\"); slide->dynamicCall ("Export(const QString &, const QString &)", temppath, QString ("PNG")); if (QFile::exists (temppath) == false) { return false; } *pix = QImage (temppath); QFile::remove (temppath); return true; } ExcelWidget * ApvlvExcel::getWidget () { auto wid = new ExcelWidget (); wid->setFile (this); return wid; } void ExcelWidget::setFile (File *file) { mFile = file; auto qname = QString::fromLocal8Bit (mFile->getFilename ()); mAxWidget.setControl (qname); } void ExcelWidget::showPage (int p, double s) { auto sheets = mAxWidget.querySubObject ("Sheets"); auto sheet = sheets->querySubObject ("Item(int)", p + 1); sheet->dynamicCall ("Activate()"); mPageNumber = p; mScrollValue = s; } void ExcelWidget::showPage (int p, const string &anchor) { showPage (p, 0.0); mPageNumber = p; mScrollValue = 0; } bool ApvlvExcel::load (const string &filename) { auto qname = QString::fromLocal8Bit (filename); mApp = new QAxWidget ("Excel.Workbook"); mApp->setProperty ("Visible", false); mApp->setProperty ("ReadOnly", true); mApp->setControl (qname); mDoc = nullptr; return true; } int ApvlvExcel::sum () { auto sheets = mApp->querySubObject ("Sheets"); auto count = sheets->property ("Count"); return count.toInt (); } bool ApvlvExcel::pageText (int pn, const Rectangle &rect, string &text) { return false; // auto content = mDoc->querySubObject ("Content"); } } // Local Variables: // mode: c++ // End: apvlv-0.7.0-alpha/src/file/ApvlvImage.cc0000644000175100017510000000212614726051136020327 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * Copyright (C) <2010> * * Contact: Alf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ /* @CPPFILE ApvlvImage.cc * * Author: Alf */ #include "ApvlvImage.h" namespace apvlv { FILE_TYPE_DEFINITION ("Web", ApvlvIMAGE, { ".png", ".jpg", ".jpeg", ".gif", ".bmp" }); } // Local Variables: // mode: c++ // End: apvlv-0.7.0-alpha/src/file/ApvlvDjvu.h0000644000175100017510000000267314726051136020066 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * Copyright (C) <2010> * * Contact: Alf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ /* @CPPFILE ApvlvDjvu.h * * Author: Alf */ #ifndef _APVLV_DJVU_H_ #define _APVLV_DJVU_H_ #include #include "ApvlvFile.h" namespace apvlv { class ApvlvDJVU : public File { FILE_TYPE_DECLARATION (ApvlvDJVU); public: bool load (const std::string &filename) override; ~ApvlvDJVU () override; SizeF pageSizeF (int page, int rot) override; int sum () override; bool pageRenderToImage (int pn, double zm, int rot, QImage *img) override; private: ddjvu_context_t *mContext; ddjvu_document_t *mDoc; }; } #endif /* Local Variables: */ /* mode: c++ */ /* End: */ apvlv-0.7.0-alpha/src/file/ApvlvTxt.h0000644000175100017510000000274314726051136017733 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * Copyright (C) <2010> * * Contact: Alf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ /* @CPPFILE ApvlvTxt.h * * Author: Alf */ #ifndef _APVLV_TXT_H_ #define _APVLV_TXT_H_ #include "ApvlvHtm.h" namespace apvlv { class ApvlvTXT : public ApvlvHTML { FILE_TYPE_DECLARATION (ApvlvTXT); public: bool load (const std::string &filename) override { return true; } bool pageText (int pn, const Rectangle &rect, std::string &text) override; bool pageRenderToWebView (int pn, double zm, int rot, WebView *webview) override; std::string pathMimeType (const std::string &path) override { return "text/plain"; }; }; } #endif /* Local Variables: */ /* mode: c++ */ /* End: */ apvlv-0.7.0-alpha/src/file/ApvlvFb2.cc0000644000175100017510000002475114726051136017726 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * Copyright (C) <2010> * * Contact: Alf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ /* @CPPFILE ApvlvFb2.cc * * Author: Alf */ #include #include #include #include "ApvlvFb2.h" #include "ApvlvUtil.h" #include "ApvlvWebViewWidget.h" namespace apvlv { using namespace std; const string stylesheet_content = ".block_c {\n" " display: block;\n" " font-size: 2.5em;\n" " font-weight: normal;\n" " line-height: 33.6pt;\n" " text-align: center;\n" " text-indent: 0;\n" " margin: 17pt 0;\n" " padding: 0;\n" "}\n" ".block_ {\n" " display: block;\n" " font-size: 1.5em;\n" " font-weight: normal;\n" " line-height: 33.6pt;\n" " text-align: justify;\n" " text-indent: 0;\n" " margin: 17pt 0;\n" " padding: 0;\n" "}\n" ".block_1 {\n" " display: block;\n" " line-height: 1.2;\n" " text-align: justify;\n" " margin: 0 0 7pt;\n" " padding: 0;\n" "}\n"; const string title_template = "\n" "\n" " \n" " \n" " \n" " \n" " \n" " \n" "
\n" "
\n" "
\n" "
\n" " %s\n" " \n" "\n"; const string section_template = "\n" "\n" " \n" " \n" " \n" " \n" " \n" " \n" " %s\n" " \n" "\n"; FILE_TYPE_DEFINITION ("Web", ApvlvFB2, { ".fb2" }); bool ApvlvFB2::load (const string &filename) { QFile file (QString::fromLocal8Bit (filename)); if (!file.open (QFile::ReadOnly | QFile::Text)) { return false; } auto bytes = file.readAll (); parseFb2 (bytes.constData (), bytes.length ()); return true; } bool ApvlvFB2::parseFb2 (const char *content, size_t length) { parseDescription (content, length); parseBinary (content, length); parseBody (content, length); generateIndex (); return true; } bool ApvlvFB2::parseDescription (const char *content, size_t length) { vector keys{ "FictionBook", "description", "title-info", "coverpage", "image" }; auto value = xmlContentGetAttributeValue (content, length, keys, "href"); mCoverHref = value; return true; } bool ApvlvFB2::parseBody (const char *content, size_t length) { vector keys = { "FictionBook", "body" }; auto optxml = xmlContentGetElement (content, length, keys); if (!optxml) return false; auto xml = optxml->get (); while (!xml->atEnd () && !(xml->isEndElement () && xml->name ().toString () == "body")) { if (xml->isStartElement () && xml->name () == QString ("title")) { stringstream ss; while (!xml->atEnd () && !(xml->isEndElement () && xml->name ().toString () == "title")) { if (xml->isStartElement ()) { if (xml->name () == QString ("empty-line")) { ss << "
"; } else if (xml->name () == QString ("p")) { ss << "

"; auto xmltext = xml->readElementText ().trimmed (); ss << xmltext.toStdString (); ss << "

"; ss << "
"; } } xml->readNext (); } auto htmlstr = templateBuild (title_template, "%s", ss.str ()); appendTitle (htmlstr, "application/xhtml+xml"); } else if (xml->isStartElement () && xml->name ().toString () == "section") { stringstream ss; string title; while (!xml->atEnd () && !(xml->isEndElement () && xml->name ().toString () == "section")) { if (xml->isStartElement ()) { if (xml->name ().toString () == "title") { auto xmltext = xml->readElementText ( QXmlStreamReader::IncludeChildElements) .trimmed (); title = xmltext.toStdString (); ss << "

"; ss << title; ss << "

"; ss << "
"; } else if (xml->name ().toString () == "p") { ss << "

"; auto xmltext = xml->readElementText ().trimmed (); ss << xmltext.toStdString (); ss << "

"; } } xml->readNext (); } auto htmlstr = templateBuild (section_template, "%s", ss.str ()); appendSection (title, htmlstr, "application/xhtml+xml"); } xml->readNext (); } return true; } bool ApvlvFB2::parseBinary (const char *content, size_t length) { string idstr; vector keys = { "FictionBook", "binary" }; auto optxml = xmlContentGetElement (content, length, keys); if (!optxml) return false; auto xml = optxml->get (); idstr = xmlStreamGetAttributeValue (xml, "id"); if (mCoverHref.empty () || idstr == mCoverHref.substr (1)) { string mimetype = xmlStreamGetAttributeValue (xml, "content-type"); auto contents = xml->readElementText ().toStdString (); QByteArray b64contents{ contents.c_str (), (qsizetype)contents.length () }; auto bytes = QByteArray::fromBase64 (b64contents); auto section = bytes.toStdString (); appendCoverpage (section, mimetype); } return true; } void ApvlvFB2::appendCoverpage (const string §ion, const string &mime) { appendSection ("__cover__", section, mime); } void ApvlvFB2::appendTitle (const string §ion, const string &mime) { appendSection ("TITLE", section, mime); } void ApvlvFB2::appendSection (const string &title, const string §ion, const string &mime) { stringstream uri; uri << mPages.size (); appendPage (uri.str (), title, section, mime); } void ApvlvFB2::appendPage (const string &uri, const string &title, const string §ion, const string &mime) { srcPages[uri] = (int)mPages.size (); mPages.push_back (uri); titleSections.insert ({ uri, { title, section } }); srcMimeTypes.insert ({ uri, mime }); } bool ApvlvFB2::generateIndex () { stringstream pagenum; mIndex = { "", 0, getFilename (), FileIndexType::FILE }; for (int ind = 0; ind < (int)mPages.size (); ++ind) { pagenum << ind; if (mPages[ind] == "__cover__") continue; auto title = titleSections[mPages[ind]].first; auto chap = FileIndex (title, ind, pagenum.str (), FileIndexType::PAGE); mIndex.mChildrenIndex.emplace_back (chap); } return true; } int ApvlvFB2::sum () { return (int)mPages.size (); } bool ApvlvFB2::pageRenderToWebView (int pn, double zm, int rot, WebView *webview) { webview->setZoomFactor (zm); QUrl url = QString ("apvlv:///") + QString::number (pn); webview->load (url); return true; } optional ApvlvFB2::pathContent (const string &uri) { if (uri == "stylesheet.css") { auto byte_array = QByteArray::fromStdString (stylesheet_content); return byte_array; } auto byte_array = QByteArray::fromStdString (titleSections[uri].second); return byte_array; } } // Local Variables: // mode: c++ // End: apvlv-0.7.0-alpha/src/file/ApvlvHtm.h0000644000175100017510000000250214726051136017675 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * Copyright (C) <2010> * * Contact: Alf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ /* @CPPFILE ApvlvHtm.h * * Author: Alf */ #ifndef _APVLV_HTM_H_ #define _APVLV_HTM_H_ #include #include "ApvlvFile.h" namespace apvlv { class ApvlvHTML : public File { FILE_TYPE_DECLARATION (ApvlvHTML); public: bool load (const std::string &filename) override; bool pageRenderToWebView (int pn, double zm, int rot, WebView *webview) override; protected: QUrl mUrl; }; } #endif /* Local Variables: */ /* mode: c++ */ /* End: */ apvlv-0.7.0-alpha/src/file/ApvlvFb2.h0000644000175100017510000000431314726051136017560 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * Copyright (C) <2010> * * Contact: Alf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ /* @CPPFILE ApvlvFb2.h * * Author: Alf */ #ifndef _APVLV_FB2_H_ #define _APVLV_FB2_H_ #include #include "ApvlvFile.h" namespace apvlv { class ApvlvFB2 : public File { FILE_TYPE_DECLARATION (ApvlvFB2); public: bool load (const std::string &filename) override; ~ApvlvFB2 () override = default; int sum () override; bool pageRenderToWebView (int pn, double zm, int rot, WebView *webview) override; std::optional pathContent (const std::string &path) override; private: std::map> titleSections; std::string mCoverHref; bool parseFb2 (const char *content, size_t length); bool parseDescription (const char *content, size_t length); bool parseBody (const char *content, size_t length); bool parseBinary (const char *content, size_t length); void appendCoverpage (const std::string §ion, const std::string &mime); void appendTitle (const std::string §ion, const std::string &mime); void appendSection (const std::string &title, const std::string §ion, const std::string &mime); void appendPage (const std::string &uri, const std::string &title, const std::string §ion, const std::string &mime); bool generateIndex (); }; } #endif /* Local Variables: */ /* mode: c++ */ /* End: */ apvlv-0.7.0-alpha/src/file/ApvlvImage.h0000644000175100017510000000230314726051136020166 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * Copyright (C) <2010> * * Contact: Alf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ /* @CPPFILE ApvlvImage.h * * Author: Alf */ #ifndef _APVLV_IMAGE_H_ #define _APVLV_IMAGE_H_ #include "ApvlvHtm.h" namespace apvlv { class ApvlvIMAGE : public ApvlvHTML { FILE_TYPE_DECLARATION (ApvlvIMAGE); public: bool pageIsOnlyImage (int pn) override { return true; } }; } #endif /* Local Variables: */ /* mode: c++ */ /* End: */ apvlv-0.7.0-alpha/src/file/ApvlvHtm.cc0000644000175100017510000000260714726051136020041 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * Copyright (C) <2010> * * Contact: Alf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ /* @CPPFILE ApvlvHtm.cc * * Author: Alf */ #include "ApvlvHtm.h" #include "ApvlvWebViewWidget.h" namespace apvlv { FILE_TYPE_DEFINITION ("Web", ApvlvHTML, { ".htm", ".html" }); using namespace std; bool ApvlvHTML::load (const string &filename) { mUrl.setScheme ("file"); mUrl.setPath (QString::fromLocal8Bit (filename)); return true; } bool ApvlvHTML::pageRenderToWebView (int pn, double zm, int rot, WebView *webview) { webview->setZoomFactor (zm); webview->load (mUrl); return true; } } // Local Variables: // mode: c++ // End: apvlv-0.7.0-alpha/src/file/ApvlvPopplerPdf.cc0000644000175100017510000001005314726051136021356 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * Copyright (C) <2010> * * Contact: Alf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ /* @CPPFILE ApvlvPdf.cc * * Author: Alf */ #include #include #include #include #include #include "ApvlvPopplerPdf.h" #include "ApvlvView.h" namespace apvlv { FILE_TYPE_DEFINITION ("poppler", ApvlvPopplerPDF, { ".pdf" }); using namespace std; using namespace Poppler; bool ApvlvPopplerPDF::load (const string &filename) { mDoc = Document::load (QString::fromLocal8Bit (filename)); if (mDoc == nullptr) { auto text = QInputDialog::getText (nullptr, "password", "input password"); auto pass = QByteArray::fromStdString (text.toStdString ()); mDoc = Document::load (QString::fromStdString (filename), pass, pass); } if (mDoc == nullptr) { return false; } generateIndex (); return true; } SizeF ApvlvPopplerPDF::pageSizeF (int pn, int rot) { auto page = mDoc->page (pn); auto qsize = page->pageSizeF (); if (rot == 0 || rot == 180) { return { qsize.width (), qsize.height () }; } else { return { qsize.height (), qsize.width () }; } } int ApvlvPopplerPDF::sum () { return mDoc ? mDoc->numPages () : 0; } unique_ptr ApvlvPopplerPDF::pageSearch (int pn, const char *str) { if (mDoc == nullptr) return nullptr; auto page = mDoc->page (pn); auto results = page->search (str); if (results.empty ()) return nullptr; auto poses = make_unique (); for (auto const &res : results) { WordRectangle wr; wr.rect_list.push_back ( { res.left (), res.top (), res.right (), res.bottom () }); poses->push_back (wr); } return poses; } bool ApvlvPopplerPDF::pageIsOnlyImage (int pn) { auto page = mDoc->page (pn); auto list = page->textList (); return list.empty (); } bool ApvlvPopplerPDF::pageRenderToImage (int pn, double zm, int rot, QImage *pix) { if (mDoc == nullptr) return false; auto xres = 72.0 * zm; auto yres = 72.0 * zm; auto prot = Poppler::Page::Rotate0; if (rot == 90) prot = Poppler::Page::Rotate90; if (rot == 180) prot = Poppler::Page::Rotate180; if (rot == 270) prot = Poppler::Page::Rotate270; auto page = mDoc->page (pn); auto size = page->pageSizeF (); auto image = page->renderToImage (xres, yres, 0, 0, size.width () * zm, size.height () * zm, prot); *pix = std::move (image); return true; } bool ApvlvPopplerPDF::generateIndex () { auto outlines = mDoc->outline (); if (outlines.empty ()) return false; mIndex = { "", 0, getFilename (), FileIndexType::FILE }; generateChildrenIndex (mIndex, outlines); return true; } void ApvlvPopplerPDF::generateChildrenIndex (FileIndex &root_index, const QVector &outlines) { for (auto const &outline : outlines) { FileIndex index{ outline.name ().toStdString (), outline.destination ()->pageNumber () - 1, "", FileIndexType::PAGE }; auto child_outlines = outline.children (); generateChildrenIndex (index, child_outlines); root_index.mChildrenIndex.emplace_back (index); } } } // Local Variables: // mode: c++ // End: apvlv-0.7.0-alpha/src/file/ApvlvMuPdf.cc0000644000175100017510000002025614726051136020324 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * Copyright (C) <2010> * * Contact: Alf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ /* @CPPFILE ApvlvPdf.cc * * Author: Alf */ #include #include #include #include #include "ApvlvMuPdf.h" namespace apvlv { using namespace std; FILE_TYPE_DEFINITION ("MuPDF", ApvlvMuPDF, { ".pdf", ".xps", ".epub", ".mobi", ".fb2", ".cbz", ".svg", ".txt" }); ApvlvMuPDF::ApvlvMuPDF () : mDoc{ nullptr } { mContext = fz_new_context (nullptr, nullptr, FZ_STORE_UNLIMITED); fz_register_document_handlers (mContext); } ApvlvMuPDF::~ApvlvMuPDF () { fz_drop_document (mContext, mDoc); fz_drop_context (mContext); } bool ApvlvMuPDF::load (const string &filename) { mDoc = fz_open_document (mContext, filename.c_str ()); if (mDoc == nullptr) { return false; } generateIndex (); return true; } SizeF ApvlvMuPDF::pageSizeF (int pn, int rot) { auto page = fz_load_page (mContext, mDoc, pn); auto rect = fz_bound_page (mContext, page); fz_drop_page (mContext, page); SizeF sizef{ rect.x1 - rect.x0, rect.y1 - rect.y0 }; return sizef; } int ApvlvMuPDF::sum () { auto pages = fz_count_pages (mContext, mDoc); return pages; } bool ApvlvMuPDF::pageIsOnlyImage (int pn) { auto text_page = fz_new_stext_page_from_page_number (mContext, mDoc, pn, nullptr); if (text_page == nullptr) { return true; } auto only_image = (text_page->first_block == nullptr); fz_drop_stext_page (mContext, text_page); return only_image; } bool ApvlvMuPDF::pageRenderToImage (int pn, double zm, int rot, QImage *pix) { auto scale = fz_scale (static_cast (zm), static_cast (zm)); auto mat = fz_pre_rotate (scale, static_cast (rot)); auto color = fz_device_rgb (mContext); auto pixmap = fz_new_pixmap_from_page_number (mContext, mDoc, pn, mat, color, 0); if (pixmap == nullptr) return false; auto comments = mNote.getCommentsInPage (pn); pageRenderComments (pn, pixmap, comments, mat); QImage img{ pixmap->w, pixmap->h, QImage::Format_RGB32 }; for (auto y = 0; y < pixmap->h; ++y) { auto p = pixmap->samples + y * pixmap->stride; for (auto x = 0; x < pixmap->w; ++x) { QColor c{ int (p[0]), int (p[1]), int (p[2]) }; img.setPixelColor (x, y, c); p += pixmap->n; } } fz_drop_pixmap (mContext, pixmap); *pix = img; return true; } optional> ApvlvMuPDF::pageHighlight (int pn, const ApvlvPoint &pa, const ApvlvPoint &pb) { auto options = fz_stext_options{}; auto text_page = fz_new_stext_page_from_page_number (mContext, mDoc, pn, &options); auto fa = fz_point{ static_cast (pa.x), static_cast (pa.y) }; auto fb = fz_point{ static_cast (pb.x), static_cast (pb.y) }; std::array quad_array; auto quads = fz_highlight_selection (mContext, text_page, fa, fb, quad_array.data(), quad_array.size()); fz_drop_stext_page (mContext, text_page); if (quads == 0) return nullopt; auto rect_list = vector{}; for (auto i = 0; i < quads; ++i) { auto quad = &quad_array[i]; Rectangle r{ quad->ul.x, quad->ul.y, quad->lr.x, quad->lr.y }; rect_list.emplace_back (r); } return rect_list; } bool ApvlvMuPDF::pageText (int pn, const Rectangle &rect, string &text) { auto text_page = fz_new_stext_page_from_page_number (mContext, mDoc, pn, nullptr); auto fzrect = fz_rect{ .x0 = static_cast (rect.p1x), .y0 = static_cast (rect.p1y), .x1 = static_cast (rect.p2x), .y1 = static_cast (rect.p2y), }; text = fz_copy_rectangle (mContext, text_page, fzrect, 0); fz_drop_stext_page (mContext, text_page); return true; } unique_ptr ApvlvMuPDF::pageSearch (int pn, const char *str) { int hit; std::array quad_array; auto count = fz_search_page_number (mContext, mDoc, pn, str, &hit, quad_array.data (), quad_array.size ()); if (count == 0) return nullptr; auto list = make_unique (); for (auto i = 0; i < count; ++i) { WordRectangle rectangle; rectangle.word = str; auto quad = quad_array[i]; Rectangle rect{ quad.ul.x, quad.lr.y, quad.lr.x, quad.ul.y }; rectangle.rect_list.push_back (rect); list->push_back (rectangle); } return list; } void ApvlvMuPDF::pageRenderComments (int pn, fz_pixmap *pixmap, const vector &comments, const fz_matrix &mat) { if (comments.empty ()) return; auto dev = fz_new_draw_device (mContext, mat, pixmap); auto stroke = fz_new_stroke_state (mContext); stroke->linewidth = 0.4; fz_path *path = fz_new_path (mContext); auto scale = fz_scale (1.0, 1.0); std::array color = { 0.0, 0.0, 1.0 }; for (const auto &comment : comments) { ApvlvPoint pa{ comment.begin.x, comment.begin.y }; ApvlvPoint pb{ comment.end.x, comment.end.y }; auto rect_list = pageHighlight (pn, pa, pb); if (rect_list->empty ()) continue; for (auto const &rect : rect_list.value ()) { fz_moveto (mContext, path, static_cast (rect.p1x), static_cast (rect.p2y)); fz_lineto (mContext, path, static_cast (rect.p2x), static_cast (rect.p2y)); fz_stroke_path (mContext, dev, path, stroke, scale, fz_device_rgb (mContext), color.data (), 0.8, fz_default_color_params); } } fz_drop_path (mContext, path); fz_drop_stroke_state (mContext, stroke); fz_drop_device (mContext, dev); } void ApvlvMuPDF::generateIndex () { mIndex = { "", 0, "", FileIndexType::FILE }; fz_outline *top_toc; fz_try (mContext) top_toc = fz_load_outline (mContext, mDoc); fz_catch (mContext) { qCritical () << "load " << mFilename << " outline error"; fz_report_error (mContext); top_toc = nullptr; } if (top_toc == nullptr) return; auto toc = top_toc; while (toc != nullptr) { auto child_index = FileIndex{}; generateIndexRecursively (child_index, toc); mIndex.mChildrenIndex.push_back (child_index); toc = toc->next; } fz_drop_outline (mContext, top_toc); } void ApvlvMuPDF::generateIndexRecursively (FileIndex &index, const fz_outline *outline) { index.type = FileIndexType::PAGE; if (outline->title) index.title = outline->title; index.page = fz_page_number_from_location (mContext, mDoc, outline->page); if (outline->uri != nullptr) { index.path = outline->uri; auto pos = index.path.find ('#'); if (pos != string::npos) { index.anchor = index.path.substr (pos); index.path = index.path.substr (0, pos); } if (index.page == -1) { auto dest = fz_resolve_link (mContext, mDoc, outline->uri, nullptr, nullptr); index.page = fz_page_number_from_location (mContext, mDoc, dest); } } auto toc = outline->down; while (toc != nullptr) { auto child_index = FileIndex{}; generateIndexRecursively (child_index, toc); index.mChildrenIndex.push_back (child_index); toc = toc->next; } } } // Local Variables: // mode: c++ // End: apvlv-0.7.0-alpha/src/file/ApvlvPopplerPdf.h0000644000175100017510000000344114726051136021223 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * Copyright (C) <2010> * * Contact: Alf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ /* @CPPFILE ApvlvPdf.h * * Author: Alf */ #ifndef _APVLV_POPPLERPDF_H_ #define _APVLV_POPPLERPDF_H_ #include #include "ApvlvFile.h" namespace apvlv { class ApvlvPopplerPDF : public File { FILE_TYPE_DECLARATION (ApvlvPopplerPDF); public: bool load (const std::string &filename) override; ~ApvlvPopplerPDF () override = default; SizeF pageSizeF (int page, int rot) override; int sum () override; bool pageIsOnlyImage (int pn) override; bool pageRenderToImage (int pn, double zm, int rot, QImage *img) override; std::unique_ptr pageSearch (int pn, const char *s) override; private: bool generateIndex (); void generateChildrenIndex (FileIndex &root_index, const QVector &outlines); std::unique_ptr mDoc; }; } #endif /* Local Variables: */ /* mode: c++ */ /* End: */ apvlv-0.7.0-alpha/src/file/ApvlvEpub.cc0000644000175100017510000002310614726051136020201 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * Copyright (C) <2010> * * Contact: Alf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ /* @CPPFILE ApvlvHtm.cc * * Author: Alf */ #include #include #include #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #include #pragma GCC diagnostic pop #include "ApvlvEpub.h" #include "ApvlvUtil.h" #include "ApvlvWebViewWidget.h" namespace apvlv { FILE_TYPE_DEFINITION ("Web", ApvlvEPUB, { ".epub" }); using namespace std; bool ApvlvEPUB::load (const string &filename) { mQuaZip = make_unique (QString::fromLocal8Bit (filename)); if (mQuaZip->open (QuaZip::mdUnzip) == false) { return false; } auto filenames = mQuaZip->getFileNameList (); if (!filenames.contains ("META-INF/container.xml")) { return false; } auto optcontainer = getZipFileContents ("META-INF/container.xml"); if (!optcontainer) { return false; } string contentfile = containerGetContentfile (optcontainer->constData (), optcontainer->length ()); if (contentfile.empty ()) { return false; } if (contentGetMedia (contentfile) == false) { return false; } ncxSetIndex (idSrcs["ncx"]); return true; } ApvlvEPUB::~ApvlvEPUB () { mQuaZip->close (); } int ApvlvEPUB::sum () { return int (mPages.size ()); } bool ApvlvEPUB::pageRenderToWebView (int pn, double zm, int rot, WebView *webview) { webview->setZoomFactor (zm); QUrl epuburi = QString ("apvlv:///") + QString::fromLocal8Bit (mPages[pn]); webview->load (epuburi); return true; } unique_ptr ApvlvEPUB::pageSearch (int pn, const char *s) { auto qpath = QString::fromLocal8Bit (mPages[pn]); auto content = getZipFileContents (qpath); auto html = content->toStdString (); auto pos = html.find (s); if (pos == string::npos) return nullptr; auto wordlist = make_unique (); do { WordRectangle word{}; word.word = s; wordlist->push_back (word); pos = html.find (s, pos + 1); } while (pos != string::npos); return wordlist; } optional ApvlvEPUB::pathContent (const string &path) { auto optcontent = getZipFileContents (QString::fromLocal8Bit (path)); return optcontent; } optional ApvlvEPUB::getZipFileContents (const QString &name) { if (mQuaZip->setCurrentFile (name) == false) return nullopt; auto zipfile = make_unique (mQuaZip.get ()); zipfile->open (QIODevice::ReadOnly); auto qarray = zipfile->readAll (); zipfile->close (); return qarray; } string ApvlvEPUB::containerGetContentfile (const char *container, int len) { vector names{ "container", "rootfiles", "rootfile" }; return xmlContentGetAttributeValue (container, len, names, "full-path"); } bool ApvlvEPUB::contentGetMedia (const string &contentfile) { string cover_id = "cover"; auto optcontent = getZipFileContents (QString::fromLocal8Bit (contentfile)); if (!optcontent) { return false; } vector metas{ "package", "metadata" }; auto optcover = xmlContentGetElement (optcontent->constData (), optcontent->length (), metas); if (optcover) { auto xml = optcover->get (); while ( !xml->atEnd () && !(xml->isEndElement () && xml->name ().toString () == "metadata")) { xml->readNext (); if (xml->isStartElement () && xml->name ().toString () == "meta") { auto attrs = xml->attributes (); for (auto const &attr : attrs) { if (attr.name ().toString () == "name" && attr.value ().toString () == "cover") { cover_id = xml->attributes () .value ("content") .toString () .toStdString (); } } } } } vector items = { "package", "manifest", "item" }; auto optxml = xmlContentGetElement (optcontent->constData (), optcontent->length (), items); if (!optxml) return false; auto xml = optxml->get (); while (!xml->atEnd () && xml->name ().toString () == "item") { if (xml->isStartElement ()) { string href = xmlStreamGetAttributeValue (xml, "href"); if (href.empty ()) { xml->readNextStartElement (); continue; } if (contentfile.rfind ('/') != string::npos) { string dirname = contentfile.substr (0, contentfile.rfind ('/')); href = dirname + "/" + href; } string id = xmlStreamGetAttributeValue (xml, "id"); string type = xmlStreamGetAttributeValue (xml, "media-type"); if (id == cover_id) { idSrcs["cover"] = href; srcMimeTypes[href] = type; } else { idSrcs[id] = href; srcMimeTypes[href] = type; } } xml->readNextStartElement (); } vector names{ "package", "spine", "itemref" }; optxml = xmlContentGetElement (optcontent->constData (), optcontent->length (), names); if (!optxml) return false; xml = optxml->get (); while (!xml->atEnd () && xml->name ().toString () == "itemref") { if (xml->isStartElement ()) { string id = xmlStreamGetAttributeValue (xml, "idref"); mPages.push_back (idSrcs[id]); srcPages[idSrcs[id]] = static_cast (mPages.size () - 1); } xml->readNextStartElement (); } return true; } bool ApvlvEPUB::ncxSetIndex (const string &ncxfile) { auto opttoc = getZipFileContents (QString::fromLocal8Bit (ncxfile)); if (!opttoc) { return false; } vector names{ "ncx", "navMap" }; auto optxml = xmlContentGetElement (opttoc->constData (), opttoc->length (), names); if (!optxml) { return false; } mIndex = { "__cover__", 0, getFilename (), FileIndexType::FILE }; auto xml = optxml->get (); ncxNodeSetIndex (xml, "navMap", ncxfile, mIndex); return true; } void ApvlvEPUB::ncxNodeSetIndex (QXmlStreamReader *xml, const string &element_name, const string &ncxfile, FileIndex &index) { while (!xml->atEnd () && !(xml->isEndElement () && xml->name ().toString ().toStdString () == element_name)) { if (xml->isStartElement ()) { if (xml->name ().toString () == "navLabel") { while (!(xml->isEndElement () && xml->name ().toString () == "navLabel")) { xml->readNextStartElement (); if (xml->name ().toString () == "text") { auto text = xml->readElementText ( QXmlStreamReader::ReadElementTextBehaviour:: SkipChildElements); index.title = text.toStdString (); break; } } xml->readNextStartElement (); } if (xml->name ().toString () == "content") { string srcstr = xmlStreamGetAttributeValue (xml, "src"); if (srcstr.empty ()) continue; if (ncxfile.find ('/') != string::npos) { auto ncxdir = filesystem::path (ncxfile).parent_path ().string (); srcstr = string (ncxdir) + '/' + srcstr; } index.path = srcstr; auto href = srcstr; if (srcstr.find ('#') != string::npos) { index.anchor = srcstr.substr (srcstr.find ('#')); href = srcstr.substr (0, srcstr.find ('#')); } for (decltype (mPages.size ()) ind = 0; ind < mPages.size (); ++ind) { if (mPages[ind] == href) { index.page = int (ind); break; } } xml->readNextStartElement (); } if (xml->name ().toString () == "navPoint") { xml->readNextStartElement (); auto childindex = FileIndex{}; ncxNodeSetIndex (xml, "navPoint", ncxfile, childindex); index.mChildrenIndex.emplace_back (childindex); } } xml->readNextStartElement (); } } } // Local Variables: // mode: c++ // End: apvlv-0.7.0-alpha/src/file/ApvlvQtPdf.cc0000644000175100017510000001720014726051136020322 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * Copyright (C) <2010> * * Contact: Alf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ /* @CPPFILE ApvlvPdf.cc * * Author: Alf */ #include #include #include #include #include #include #include #include #include "ApvlvQtPdf.h" #include "ApvlvUtil.h" namespace apvlv { using namespace std; FILE_TYPE_DEFINITION ("QtPdf", ApvlvPDF, { ".pdf" }); bool ApvlvPDF::load (const string &filename) { mDoc = make_unique (); mView = nullptr; auto res = mDoc->load (QString::fromLocal8Bit (filename)); if (res == QPdfDocument::Error::IncorrectPassword) { if (QThread::currentThread () == QApplication::instance ()->thread ()) { auto text = QInputDialog::getText (nullptr, "password", "input password"); if (text.isEmpty ()) return false; auto pass = QByteArray::fromStdString (text.toStdString ()); mDoc->setPassword (pass); res = mDoc->load (QString::fromLocal8Bit (filename)); } else { qWarning () << "file: " << filename << " has password, skip !!!"; } } if (res != QPdfDocument::Error::None) { return false; } mSearchModel = make_unique (); mSearchModel->setDocument (mDoc.get ()); generateIndex (); return true; } PDFWidget * ApvlvPDF::getWidget () { auto wid = new PDFWidget (); wid->setFile (this); return wid; } SizeF ApvlvPDF::pageSizeF (int pn, int rot) { auto qsize = mDoc->pagePointSize (pn); if (rot == 0 || rot == 180) { return { qsize.width (), qsize.height () }; } else { return { qsize.height (), qsize.width () }; } } int ApvlvPDF::sum () { return mDoc ? mDoc->pageCount () : 0; } unique_ptr ApvlvPDF::pageSearch (int pn, const char *str) { if (mDoc == nullptr) return nullptr; mSearchModel->setSearchString (str); auto results = mSearchModel->resultsOnPage (pn); if (results.empty ()) return nullptr; auto poses = make_unique (); for (auto const &res : results) { WordRectangle word_rectangle; for (auto const &rect : res.rectangles ()) { word_rectangle.rect_list.push_back ( { rect.left (), rect.bottom (), rect.right (), rect.top () }); } poses->push_back (word_rectangle); } return poses; } bool ApvlvPDF::pageIsOnlyImage (int pn) { auto sel = mDoc->getAllText (pn); auto has_text = sel.isValid (); return !has_text; } bool ApvlvPDF::pageRenderToImage (int pn, double zm, int rot, QImage *pix) { if (mDoc == nullptr) return false; using enum QPdfDocumentRenderOptions::Rotation; auto sizeF = pageSizeF (pn, rot); QSize image_size{ int (sizeF.width * zm), int (sizeF.height * zm) }; auto prot = None; if (rot == 90) prot = Clockwise90; if (rot == 180) prot = Clockwise180; if (rot == 270) prot = Clockwise270; QPdfDocumentRenderOptions options{}; options.setRotation (prot); options.setScaledSize (image_size); *pix = mDoc->render (pn, image_size, options); if (auto comments = mNote.getCommentsInPage (pn); !comments.empty ()) { pageRenderComments (pn, pix, comments); } return true; } bool ApvlvPDF::pageText (int pn, const Rectangle &rect, string &text) { if (mDoc == nullptr) return false; auto selection = mDoc->getSelection (pn, { rect.p1x, rect.p1y }, { rect.p2x, rect.p2y }); text = selection.text ().toStdString (); return true; } void ApvlvPDF::pageRenderComments (int pn, QImage *img, const std::vector &comments) { auto model = make_unique (); model->setDocument (mDoc.get ()); QPainter painter (img); painter.setPen (QPen (Qt::blue, 0.4)); // TODO // search not work, need better impl for (auto const &comment : comments) { model->setSearchString (QString::fromLocal8Bit (comment.quoteText)); auto links = model->resultsOnPage (pn); if (links.empty ()) continue; for (auto const &link : links) { auto rects = link.rectangles (); auto brect = rects[0]; auto erect = rects[rects.count () - 1]; painter.drawLine (brect.x(), brect.y() + brect.height (), erect.x(), brect.y() + brect.height ()); } } painter.end (); } bool ApvlvPDF::generateIndex () { auto bookmark_model = make_unique (); bookmark_model->setDocument (mDoc.get ()); mIndex = { "", 0, getFilename (), FileIndexType::FILE }; getIndexIter (mIndex, bookmark_model.get (), QModelIndex ()); return true; } void ApvlvPDF::getIndexIter (FileIndex &file_index, const QPdfBookmarkModel *bookmark_model, const QModelIndex &parent) { for (auto row = 0; row < bookmark_model->rowCount (parent); ++row) { auto index = bookmark_model->index (row, 0, parent); auto title = bookmark_model->data (index, Qt::UserRole); auto level = bookmark_model->data (index, 257); auto page = bookmark_model->data (index, 258); auto location = bookmark_model->data (index, 259); FileIndex child_index (title.toString ().toStdString (), page.toInt (), "", FileIndexType::PAGE); if (bookmark_model->hasChildren (index)) { getIndexIter (child_index, bookmark_model, index); } file_index.mChildrenIndex.emplace_back (child_index); } } void PDFWidget::setFile (File *file) { mFile = file; auto pdf = dynamic_cast (mFile); mPdfView.setDocument (pdf->mDoc.get ()); mPdfView.setSearchModel (pdf->mSearchModel.get ()); } void PDFWidget::showPage (int p, double s) { auto nav = mPdfView.pageNavigator (); nav->jump (p, { 0, 0 }); scrollTo (s, 0.0); mPageNumber = p; mScrollValue = s; } void PDFWidget::showPage (int p, const string &anchor) { auto nav = mPdfView.pageNavigator (); nav->jump (p, { 0, 0 }); mPageNumber = p; mScrollValue = 0; } void PDFWidget::setSearchSelect (int select) { auto pdf = dynamic_cast (mFile); auto model = pdf->mSearchModel.get (); auto doc_select = 0; for (auto pn = 0; pn < mPageNumber; ++pn) { auto res = model->resultsOnPage (pn); doc_select += static_cast (res.size ()); } doc_select += select; mPdfView.setCurrentSearchResultIndex (doc_select); auto link = model->resultAtIndex (doc_select); auto scr = static_cast (link.location ().y ()); mValScrollBar->setValue (scr); qDebug ("link: %d,{%f,%f}: select: %d", link.page (), link.location ().x (), link.location ().y (), doc_select); mSearchSelect = select; } void PDFWidget::setZoomrate (double zm) { mZoomrate = zm; mPdfView.setZoomFactor (zm); } } // Local Variables: // mode: c++ // End: apvlv-0.7.0-alpha/src/file/ApvlvEpub.h0000644000175100017510000000416614726051136020050 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * Copyright (C) <2010> * * Contact: Alf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ /* @CPPFILE ApvlvEpub.h * * Author: Alf */ #ifndef _APVLV_EPUB_H_ #define _APVLV_EPUB_H_ #include #include #include #include #include #include "ApvlvFile.h" namespace apvlv { class ApvlvEPUB : public File { FILE_TYPE_DECLARATION (ApvlvEPUB); public: bool load (const std::string &filename) override; ~ApvlvEPUB () override; int sum () override; bool pageRenderToWebView (int pn, double zm, int rot, WebView *widget) override; std::unique_ptr pageSearch (int pn, const char *s) override; std::optional pathContent (const std::string &path) override; private: std::optional getZipFileContents (const QString &name); static std::string containerGetContentfile (const char *container, int len); bool contentGetMedia (const std::string &contentfile); bool ncxSetIndex (const std::string &ncxfile); void ncxNodeSetIndex (QXmlStreamReader *xml, const std::string &element_name, const std::string &ncxfile, FileIndex &index); std::unique_ptr mQuaZip; std::map idSrcs; }; } #endif /* Local Variables: */ /* mode: c++ */ /* End: */ apvlv-0.7.0-alpha/src/file/ApvlvDjvu.cc0000644000175100017510000001067614726051136020226 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * Copyright (C) <2010> * * Contact: Alf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ /* @CPPFILE ApvlvDjvu.cc * * Author: Alf */ #include #include "ApvlvDjvu.h" #include "ApvlvUtil.h" namespace apvlv { using namespace std; void handleDdjvuMessages (ddjvu_context_t *ctx, int wait) { const ddjvu_message_t *msg; if (wait) ddjvu_message_wait (ctx); while ((msg = ddjvu_message_peek (ctx))) { qDebug () << "tag: " << msg->m_any.tag; switch (msg->m_any.tag) { case DDJVU_ERROR: break; case DDJVU_INFO: break; case DDJVU_PAGEINFO: break; default: break; } ddjvu_message_pop (ctx); } } FILE_TYPE_DEFINITION ("djvulibre", ApvlvDJVU, { ".djv", ".djvu" }); bool ApvlvDJVU::load (const string &filename) { mContext = ddjvu_context_create ("apvlv"); if (mContext == nullptr) { qCritical () << "djvu context error"; return false; } mDoc = ddjvu_document_create_by_filename (mContext, filename.c_str (), false); if (mDoc == nullptr) { qCritical ("djvu create document error"); ddjvu_context_release (mContext); mContext = nullptr; return false; } if (ddjvu_document_get_type (mDoc) != DDJVU_DOCTYPE_SINGLEPAGE) { qCritical () << "djvu type: " << ddjvu_document_get_type (mDoc); ddjvu_document_release (mDoc); mDoc = nullptr; ddjvu_context_release (mContext); mContext = nullptr; return false; } return true; } ApvlvDJVU::~ApvlvDJVU () { if (mContext) { ddjvu_context_release (mContext); } if (mDoc) { ddjvu_document_release (mDoc); } } SizeF ApvlvDJVU::pageSizeF (int pn, int rot) { ddjvu_status_t t; ddjvu_pageinfo_t info; while ((t = ddjvu_document_get_pageinfo (mDoc, 0, &info)) < DDJVU_JOB_OK) { handleDdjvuMessages (mContext, true); } SizeF sizef{ 0.0f, 0.0f }; if (t == DDJVU_JOB_OK) { sizef.width = static_cast (info.width); sizef.height = static_cast (info.height); } return sizef; } int ApvlvDJVU::sum () { return mDoc ? ddjvu_document_get_pagenum (mDoc) : 0; } bool ApvlvDJVU::pageRenderToImage (int pn, double zm, int rot, QImage *pix) { ddjvu_page_t *tpage; if ((tpage = ddjvu_page_create_by_pageno (mDoc, pn)) == nullptr) { qDebug () << "no this page: " << pn; return false; } auto size = pageSizeF (pn, rot); auto dx = size.width * zm; auto dy = size.height * zm; auto ix = static_cast (dx); auto iy = static_cast (dy); ddjvu_rect_t prect = { 0, 0, static_cast (ix), static_cast (iy) }; ddjvu_rect_t rrect = { 0, 0, static_cast (ix), static_cast (iy) }; ddjvu_format_t *format = ddjvu_format_create (DDJVU_FORMAT_RGB24, 0, nullptr); ddjvu_format_set_row_order (format, true); auto psize = 3 * ix * iy; auto buffer = make_unique (psize); int retry = 0; while (retry <= 20 && ddjvu_page_render (tpage, DDJVU_RENDER_COLOR, &prect, &rrect, format, 3 * ix, buffer.get ()) == false) { this_thread::sleep_for (50ms); ++retry; qDebug () << "fender failed, retry " << retry; } auto image = QImage (ix, iy, QImage::Format_RGB888); for (auto y = 0; y < iy; ++y) { for (auto x = 0; x < ix; ++x) { auto rgb = buffer.get () + 3 * (x + y * ix); image.setPixel (x, y, qRgb (rgb[0], rgb[1], rgb[2])); } } *pix = std::move (image); return true; } } // Local Variables: // mode: c++ // End: apvlv-0.7.0-alpha/src/file/ApvlvLibreOffice.cc0000644000175100017510000000530114726051136021454 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * Copyright (C) <2010> * * Contact: Alf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ /* @CPPFILE ApvlvOffice.cc * * Author: Alf */ #include #include #include #include "ApvlvLibreOffice.h" namespace apvlv { FILE_TYPE_DEFINITION ("libreOffice", ApvlvOFFICE, { ".doc", ".docx", ".xls", ".xlsx" }); using namespace std; unique_ptr ApvlvOFFICE::mOffice; bool ApvlvOFFICE::load (const string &filename) { initLokInstance (); mDoc = unique_ptr{ mOffice->documentLoad (filename.c_str ()) }; if (mDoc == nullptr) return false; mDoc->initializeForRendering (); return true; } ApvlvOFFICE::~ApvlvOFFICE () { mDoc = nullptr; } int ApvlvOFFICE::sum () { return mDoc->getParts (); } bool ApvlvOFFICE::pageText (int pn, const Rectangle &rect, string &text) { mDoc->setPart (pn); auto tmpname = QString ("%1/apvlv.%2.txt") .arg (QDir::temp ().path ()) .arg (random ()); mDoc->saveAs (tmpname.toStdString ().c_str (), "txt"); QFile file (tmpname); if (file.open (QIODeviceBase::ReadOnly) == false) return false; auto bytes = file.readAll (); text.append (bytes.toStdString ()); file.close (); file.remove (); return true; } bool ApvlvOFFICE::pageRenderToImage (int pn, double zm, int rot, QImage *pix) { mDoc->setPart (pn); QTemporaryFile file; if (file.open ()) { mDoc->saveAs (file.fileName ().toStdString ().c_str (), "png"); *pix = QImage (file.fileName (), "png"); file.close (); } return true; } void ApvlvOFFICE::initLokInstance () { static std::once_flag flag; std::call_once (flag, [] () { auto lok_path = ApvlvParams::instance ()->getStringOrDefault ( "lok_path", DEFAULT_LOK_PATH); mOffice = unique_ptr{ lok::lok_cpp_init (lok_path.c_str ()) }; }); } } // Local Variables: // mode: c++ // End: apvlv-0.7.0-alpha/src/file/ApvlvQtPdf.h0000644000175100017510000000534714726051136020175 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * Copyright (C) <2010> * * Contact: Alf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ /* @CPPFILE ApvlvPdf.h * * Author: Alf */ #ifndef _APVLV_QTPDF_H_ #define _APVLV_QTPDF_H_ #include #include #include "ApvlvFile.h" #include "ApvlvFileWidget.h" namespace apvlv { class PDFWidget : public FileWidget { public: PDFWidget () { mHalScrollBar = mPdfView.horizontalScrollBar (); mValScrollBar = mPdfView.verticalScrollBar (); } [[nodiscard]] QWidget * widget () override { return &mPdfView; } void setFile (File *file) override; void showPage (int pn, double s) override; void showPage (int pn, const std::string &anchor) override; void setSearchSelect (int select) override; void setZoomrate (double zm) override; private: QPdfView mPdfView{}; }; class ApvlvPDF : public File { FILE_TYPE_DECLARATION (ApvlvPDF); public: bool load (const std::string &filename) override; ~ApvlvPDF () override = default; [[nodiscard]] DISPLAY_TYPE getDisplayType () const override { return DISPLAY_TYPE::IMAGE; } PDFWidget *getWidget () override; SizeF pageSizeF (int page, int rot) override; int sum () override; bool pageIsOnlyImage (int pn) override; bool pageRenderToImage (int pn, double zm, int rot, QImage *img) override; bool pageText (int, const Rectangle &rect, std::string &text) override; std::unique_ptr pageSearch (int pn, const char *s) override; private: void pageRenderComments(int pn, QImage *img, const std::vector &comments); bool generateIndex (); void getIndexIter (FileIndex &file_index, const QPdfBookmarkModel *bookmark_model, const QModelIndex &parent); std::unique_ptr mDoc; std::unique_ptr mSearchModel; QWidget *mView; friend class PDFWidget; }; } #endif /* Local Variables: */ /* mode: c++ */ /* End: */ apvlv-0.7.0-alpha/src/file/ApvlvTxt.cc0000644000175100017510000000321314726051136020062 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * Copyright (C) <2010> * * Contact: Alf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ /* @CPPFILE ApvlvTxt.cc * * Author: Alf */ #include #include #include #include "ApvlvTxt.h" #include "ApvlvWebViewWidget.h" namespace apvlv { FILE_TYPE_DEFINITION ("Web", ApvlvTXT, { ".txt", ".text" }); bool ApvlvTXT::pageText (int pn, const Rectangle &rect, std::string &text) { QFile file (mUrl.path ()); if (file.open (QIODeviceBase::ReadOnly) == false) return false; auto bytes = file.readAll (); text.append (bytes.toStdString ()); file.close (); return true; } bool ApvlvTXT::pageRenderToWebView (int pn, double zm, int rot, WebView *webview) { auto settings = webview->settings (); settings->setDefaultTextEncoding ("UTF-8"); return ApvlvHTML::pageRenderToWebView (pn, zm, rot, webview); } } // Local Variables: // mode: c++ // End: apvlv-0.7.0-alpha/src/file/ApvlvAxOffice.h0000644000175100017510000000621514726051136020636 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * Copyright (C) <2010> * * Contact: Alf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ /* @CPPFILE ApvlvOffice.h * * Author: Alf */ #ifndef _APVLV_AXOFFICE_H_ #define _APVLV_AXOFFICE_H_ #include #include "ApvlvFile.h" #include "ApvlvFileWidget.h" namespace apvlv { class AxOffice { public: virtual ~AxOffice () { if (mDoc) mDoc->dynamicCall ("Close()"); if (mApp) mApp->dynamicCall ("Quit()"); delete mDoc; delete mApp; } protected: QAxWidget *mApp; QAxObject *mDocs; QAxObject *mDoc; }; class ApvlvOfficeWord : public File, public AxOffice { FILE_TYPE_DECLARATION (ApvlvOfficeWord); public: bool load (const std::string &filename) override; int sum () override; SizeF pageSizeF (int page, int rot) override; bool pageText (int pn, const Rectangle &rect, std::string &text) override; bool pageRenderToImage (int pn, double zm, int rot, QImage *pix) override; bool pageRenderToWebView (int pn, double zm, int rot, WebView *webview) override; std::optional pathContent (const std::string &path) override; }; class ApvlvPowerPoint : public File, public AxOffice { FILE_TYPE_DECLARATION (ApvlvPowerPoint); public: bool load (const std::string &filename) override; int sum () override; SizeF pageSizeF (int page, int rot) override; bool pageText (int pn, const Rectangle &rect, std::string &text) override; bool pageRenderToImage (int pn, double zm, int rot, QImage *pix) override; }; class ExcelWidget : public FileWidget { public: ExcelWidget () { mAxWidget.setProperty ("Visible", true); mAxWidget.setProperty ("ReadOnly", true); } [[nodiscard]] QWidget * widget () override { return &mAxWidget; } void setFile (File *file) override; void showPage (int, double s) override; void showPage (int, const std::string &anchor) override; private: QAxWidget mAxWidget{ "Excel.Workbook" }; }; class ApvlvExcel : public File, public AxOffice { FILE_TYPE_DECLARATION (ApvlvExcel); public: bool load (const std::string &filename) override; [[nodiscard]] virtual DISPLAY_TYPE getDisplayType () const override { return DISPLAY_TYPE::CUSTOM; } ExcelWidget *getWidget () override; int sum () override; bool pageText (int pn, const Rectangle &rect, std::string &text) override; }; } #endif /* Local Variables: */ /* mode: c++ */ /* End: */ apvlv-0.7.0-alpha/src/file/ApvlvLibreOffice.h0000644000175100017510000000327314726051136021324 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * Copyright (C) <2010> * * Contact: Alf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ /* @CPPFILE ApvlvOffice.h * * Author: Alf */ #ifndef _APVLV_OFFICE_H_ #define _APVLV_OFFICE_H_ #include #include #include "ApvlvFile.h" namespace apvlv { const char *const DEFAULT_LOK_PATH = "/usr/lib64/libreoffice/program"; class ApvlvOFFICE : public File { FILE_TYPE_DECLARATION (ApvlvOFFICE); public: bool load (const std::string &filename) override; ~ApvlvOFFICE () override; int sum () override; bool pageText (int pn, const Rectangle &rect, std::string &text) override; bool pageRenderToImage (int pn, double zm, int rot, QImage *pix) override; protected: std::unique_ptr mDoc; private: static std::unique_ptr mOffice; static std::mutex mLokMutex; static void initLokInstance (); }; } #endif /* Local Variables: */ /* mode: c++ */ /* End: */ apvlv-0.7.0-alpha/src/ApvlvWeb.cc0000644000175100017510000000260114726051136017101 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * Copyright (C) <2010> * * Contact: Alf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ /* @CPPFILE ApvlvHtm.cc * * Author: Alf */ #include #include "ApvlvWeb.h" #include "ApvlvWebViewWidget.h" namespace apvlv { using namespace std; bool ApvlvWEB::load (const string &filename) { mUrl = filename.c_str (); return true; } bool ApvlvWEB::pageRenderToWebView (int pn, double zm, int rot, WebView *webview) { webview->setZoomFactor (zm); // do not load started page in browsing if (!webview->url ().isValid ()) { webview->load (mUrl); } return true; } } // Local Variables: // mode: c++ // End: apvlv-0.7.0-alpha/src/ApvlvFileIndex.h0000644000175100017510000000365614726051136020110 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * Copyright (C) <2008> * * Contact: Alf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ /* @CPPFILE File.h * * Author: Alf */ #ifndef _APVLV_FILE_INDEX_H_ #define _APVLV_FILE_INDEX_H_ #include #include #include #include "ApvlvSearch.h" namespace apvlv { enum class FileIndexType { PAGE, FILE, DIR }; class FileIndex { public: FileIndex () = default; FileIndex (const std::string &title, int page, const std::string &path, FileIndexType type); ~FileIndex (); friend bool operator== (const FileIndex &a, const FileIndex &b) { return a.title == b.title && a.page == b.page && a.path == b.path && a.anchor == b.anchor; } void loadDirectory (const std::string &path1); void moveChildChildren (const FileIndex &other_index); void removeChild (const FileIndex &child); /* public variables */ FileIndexType type{ FileIndexType::PAGE }; std::string title; int page{ 0 }; std::string path; std::string anchor; std::list mChildrenIndex; /* public file variables */ std::int64_t size{ 0 }; std::int64_t mtime{ 0 }; }; } #endif /* Local Variables: */ /* mode: c++ */ /* End: */ apvlv-0.7.0-alpha/src/ApvlvEditor.h0000644000175100017510000000241114726051136017453 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * * Copyright (C) 2008 Alf. * * Contact: Alf * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2.0 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ /* @CPPFILE ApvlvEditor.h * * Author: Alf */ #ifndef _APVLV_EDITOR_H_ #define _APVLV_EDITOR_H_ #include namespace apvlv { class Editor : public QTextEdit { Q_OBJECT public: explicit Editor (QWidget *parent = nullptr); ~Editor () override = default; void setZoomrate (double zm); private: QString mFontFamily; QFont::Weight mFontWeight; qreal mFontPointSize; int mFontPixelSize; }; } #endif apvlv-0.7.0-alpha/src/ApvlvFileIndex.cc0000644000175100017510000000653714726051136020247 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * Copyright (C) <2008> * * Contact: Alf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ /* @CPPFILE File.cc * * Author: Alf */ #include #include #include #include #include #include #include "ApvlvFile.h" #include "ApvlvFileIndex.h" #include "ApvlvUtil.h" namespace apvlv { using namespace std; void FileIndex::loadDirectory (const string &path1) { auto exts = FileFactory::supportFileExts (); try { for (auto &entry : filesystem::directory_iterator ( path1, filesystem::directory_options::follow_directory_symlink)) { if (entry.is_directory ()) { auto index = FileIndex (entry.path ().filename ().string (), 0, entry.path ().string (), FileIndexType::DIR); index.loadDirectory (entry.path ().string ()); auto last = entry.last_write_time (); index.mtime = filesystemTimeToMSeconds (last); if (!index.mChildrenIndex.empty ()) { mChildrenIndex.emplace_back (index); } } else if (entry.file_size () > 0) { auto file_ext = filenameExtension (entry.path ().string ()); if (find (exts.cbegin (), exts.cend (), file_ext) != exts.cend ()) { auto index = FileIndex (entry.path ().filename ().string (), 0, entry.path ().string (), FileIndexType::FILE); index.size = static_cast (entry.file_size ()); auto last = entry.last_write_time (); index.mtime = filesystemTimeToMSeconds (last); mChildrenIndex.emplace_back (index); } } } } catch (filesystem::filesystem_error &err) { qWarning () << "file system error: " << err.what (); } } void FileIndex::moveChildChildren (const FileIndex &other_index) { Q_ASSERT (type == FileIndexType::FILE); Q_ASSERT (other_index.type == FileIndexType::FILE); mChildrenIndex = other_index.mChildrenIndex; } void FileIndex::removeChild (const FileIndex &child) { mChildrenIndex.remove (child); } FileIndex::FileIndex (const string &title, int page, const string &path, FileIndexType type) { this->title = title; this->page = page; this->path = path; this->type = type; } FileIndex::~FileIndex () = default; } // Local Variables: // mode: c++ // End: apvlv-0.7.0-alpha/src/ApvlvWebViewWidget.h0000644000175100017510000000713614726051136020752 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * * Copyright (C) 2008 Alf. * * Contact: Alf * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2.0 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ /* @CPPFILE ApvlvWebViewWidget.h * * Author: Alf */ #ifndef _APVLV_WEBVIEW_WIDGET_H_ #define _APVLV_WEBVIEW_WIDGET_H_ #include #include #include #include #include #include #include #include #include #include "ApvlvFileWidget.h" namespace apvlv { class File; class ApvlvSchemeHandler : public QWebEngineUrlSchemeHandler { Q_OBJECT public: void setFile (File *file) { mFile = file; } void requestStarted (QWebEngineUrlRequestJob *job) override; private: File *mFile; QByteArray mArray; QBuffer mBuffer; signals: void webpageUpdated (const std::string &key); }; class WebView : public QWebEngineView { Q_OBJECT public: WebView (); void setFile (File *file) { mSchemeHandler.setFile (file); } private: QWebEngineProfile mProfile; std::unique_ptr mPage; ApvlvSchemeHandler mSchemeHandler; bool isScrolledToTop (); bool isScrolledToBottom (); friend class WebViewWidget; }; class WebViewWidget : public FileWidget { Q_OBJECT public: WebViewWidget () { QObject::connect (&mWebView, SIGNAL (loadFinished (bool)), this, SLOT (webviewLoadFinished (bool))); QObject::connect (&mWebView.mSchemeHandler, SIGNAL (webpageUpdated (const string &)), this, SLOT (webviewUpdate (const string &))); } [[nodiscard]] QWidget * widget () override { return &mWebView; } void setFile (File *file) override { mFile = file; mWebView.setFile (mFile); } void showPage (int pn, double s) override; void showPage (int pn, const std::string &anchor) override; void scroll (int times, int w, int h) override; void scrollTo (double x, double y) override; void scrollUp (int times) override; void scrollDown (int times) override; void scrollLeft (int times) override; void scrollRight (int times) override; void setSearchStr (const std::string &str) override; void setSearchSelect (int select) override; void setZoomrate (double zm) override; void setInternalScroll (bool internal) { mIsInternalScroll = internal; } bool internalScroll () { return mIsInternalScroll; } private: WebView mWebView{}; bool mIsInternalScroll{ false }; bool mIsScrollUp{ false }; QWebEngineFindTextResult mSearchResult; signals: void webpageUpdated (const std::string &msg); private slots: void webviewUpdate (const std::string &msg) { emit webpageUpdated (msg); }; void webviewLoadFinished (bool suc); void webviewFindTextFinished (const QWebEngineFindTextResult &result); }; } #endif /* Local Variables: */ /* mode: c++ */ /* End: */ apvlv-0.7.0-alpha/src/ApvlvParams.cc0000644000175100017510000001240414726051136017611 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * * Copyright (C) 2008 Alf. * * Contact: Alf * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2.0 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ /* @CPPFILE ApvlvParams.cc * * Author: Alf */ #include #include #include #include #include "ApvlvCmds.h" #include "ApvlvParams.h" #include "ApvlvUtil.h" namespace apvlv { using namespace std; ApvlvParams::ApvlvParams () { push ("inverted", "no"); push ("fullscreen", "no"); push ("zoom", "fitwidth"); push ("continuous", "yes"); push ("autoscrollpage", "yes"); push ("autoscrolldoc", "yes"); push ("noinfo", "no"); push ("width", "800"); push ("height", "600"); push ("fix_width", "0"); push ("fix_height", "0"); push ("background", ""); push ("warpscan", "yes"); push ("commandtimeout", "1000"); #ifdef WIN32 push ("defaultdir", "C:\\"); #else push ("defaultdir", "/tmp"); #endif push ("guioptions", "mTsS"); push ("autoreload", "3"); push ("thread_count", "auto"); push ("lok_path", "/usr/lib64/libreoffice/program"); push (".pdf:engine", "MuPDF"); push (".epub:engine", "Web"); push (".fb2:engine", "Web"); push (".txt:engine", "MuPDF"); push ("ocr:lang", "eng+chi_sim"); } ApvlvParams::~ApvlvParams () = default; bool ApvlvParams::loadFile (const std::string &filename) { string str; fstream os (filename, ios::in); if (!os.is_open ()) { qWarning () << "Open configure file " << filename << " error"; return false; } while ((getline (os, str))) { string argu; string data; string crap; stringstream is (str); is >> crap; if (crap[0] == '\"' || crap.empty ()) { continue; } if (crap == "set") { is >> argu; size_t off = argu.find ('='); if (off == string::npos) { is >> crap >> data; if (crap == "=") { push (argu, data); continue; } } else { argu[off] = ' '; stringstream ass{ argu }; ass >> argu >> data; push (argu, data); continue; } } // like "map n next-page" else if (crap == "map") { is >> argu; if (argu.empty ()) { qWarning () << "map command not complete"; continue; } getline (is, data); while (!data.empty () && isspace (data[0])) data.erase (0, 1); if (!argu.empty () && !data.empty ()) { ApvlvCmds::buildCommandMap (argu, data); } else { qWarning () << "Syntax error: map: " << str; } } else { qWarning () << "Unknown rc command: " << crap << ": " << str; } } return true; } bool ApvlvParams::push (string_view ch, string_view str) { mParamMap[string (ch)] = str; return true; } string ApvlvParams::getGroupStringOrDefault (std::string_view entry, std::string_view key, const std::string &defs) { auto itr = std::ranges::find_if ( mParamMap, [entry, key] (const pair &p) -> bool { if (p.first.find (':') == string::npos) return false; else { auto pos = p.first.find (':'); auto pentry = p.first.substr (0, pos); auto pkey = p.first.substr (pos + 1); return pentry == entry && pkey == key; } }); if (itr != mParamMap.cend ()) { return itr->second; } return defs; } string ApvlvParams::getStringOrDefault (string_view key, const string &defs) { auto itr = std::ranges::find_if ( mParamMap, [key] (const pair &p) -> bool { return p.first == key; }); if (itr != mParamMap.cend ()) { return itr->second; } return defs; } int ApvlvParams::getIntOrDefault (string_view key, int defi) { auto values = getStringOrDefault (key, ""); if (values.empty ()) return defi; return int (strtol (values.c_str (), nullptr, 10)); } bool ApvlvParams::getBoolOrDefault (string_view key, bool defb) { auto values = getStringOrDefault (key, ""); if (values.empty ()) return defb; if (values == "true" || values == "yes" || values == "on" || values == "1") { return true; } else { return false; } } } // Local Variables: // mode: c++ // End: apvlv-0.7.0-alpha/src/ApvlvInfo.cc0000644000175100017510000000771114726051136017266 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * Copyright (C) <2008> * * Contact: Alf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ /* @CPPFILE ApvlvInfo.cc * * Author: Alf */ #include #include #include #include #include #include #include "ApvlvInfo.h" #include "ApvlvParams.h" namespace apvlv { using namespace std; void ApvlvInfo::loadFile (std::string_view file) { mFileName = file; ifstream is (mFileName, ios::in); if (is.is_open ()) { string line; while (getline (is, line)) { auto p = line.c_str (); if (*p != '\'' /* the ' */ || !isdigit (*(p + 1))) /* the digit */ { continue; } addPosition (p); } is.close (); } } bool ApvlvInfo::update () { ofstream os (mFileName, ios::out); if (!os.is_open ()) { return false; } int i = 0; for (const auto &infofile : mInfoFiles) { os << "'" << i++ << "\t"; os << infofile.page << ':' << infofile.skip << "\t"; os << infofile.rate << "\t"; os << infofile.file << endl; } os.close (); return true; } std::optional ApvlvInfo::lastFile () { if (mInfoFiles.empty ()) return nullopt; else return &*(mInfoFiles.rbegin ()); } optional ApvlvInfo::file (const string &filename) { auto itr = std::ranges::find_if ( std::views::reverse (mInfoFiles), [filename] (auto const &infofile) { return infofile.file == filename; }); if (itr != mInfoFiles.rend ()) { return &(*itr); } return nullopt; } bool ApvlvInfo::updateFile (int page, int skip, double rate, const string &filename) { InfoFile infofile{ page, skip, rate, filename }; auto optinfofile = file (filename); if (optinfofile) { *optinfofile.value () = infofile; } else { mInfoFiles.push_back (infofile); if (mInfoFiles.size () > mMaxInfo) mInfoFiles.pop_front (); } return update (); } ApvlvInfo::ApvlvInfo () { mMaxInfo = ApvlvParams::instance ()->getIntOrDefault ("max_info", DEFAULT_MAX_INFO); } bool ApvlvInfo::addPosition (const char *str) { const char *p; const char *s; p = strchr (str + 2, '\t'); /* Skip the ' and the digit */ if (p == nullptr) { return false; } while (*p != '\0' && !isdigit (*p)) { p++; } int page = int (strtol (p, nullptr, 10)); int skip; s = strchr (p, ':'); for (; s && p < s; ++p) { if (!isdigit (*p)) { break; } } if (p == s) { ++p; skip = int (strtol (p, nullptr, 10)); } else { skip = 0; } p = strchr (p, '\t'); if (p == nullptr) { return false; } while (*p != '\0' && !isdigit (*p)) { p++; } double rate = strtod (p, nullptr); p = strchr (p, '\t'); if (p == nullptr) { return false; } while (*p != '\0' && isspace (*p)) { p++; } if (*p == '\0') { return false; } auto fp = InfoFile{ page, skip, rate, p }; mInfoFiles.emplace_back (fp); return true; } }; // Local Variables: // mode: c++ // End: apvlv-0.7.0-alpha/src/ApvlvWeb.h0000644000175100017510000000314014726051136016742 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * Copyright (C) <2010> * * Contact: Alf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ /* @CPPFILE ApvlvWeb.h * * Author: Alf */ #ifndef _APVLV_WEB_H_ #define _APVLV_WEB_H_ #include #include "ApvlvFile.h" #include "ApvlvWebViewWidget.h" namespace apvlv { class ApvlvWEB : public File { public: ApvlvWEB () = default; [[nodiscard]] DISPLAY_TYPE getDisplayType () const override { return DISPLAY_TYPE::CUSTOM; } FileWidget * getWidget () override { auto wid = new WebViewWidget (); wid->setFile (this); wid->setInternalScroll (true); return wid; } bool load (const std::string &filename) override; bool pageRenderToWebView (int pn, double zm, int rot, WebView *webview) override; protected: QUrl mUrl; }; } #endif /* Local Variables: */ /* mode: c++ */ /* End: */ apvlv-0.7.0-alpha/src/ApvlvCompletion.h0000644000175100017510000000272014726051136020341 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * Copyright (C) <2008> Alf * * Contact: Alf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ /* @CPPFILE ApvlvCompletion.h * * Author: Alf */ #ifndef _APVLV_COMPLETION_H_ #define _APVLV_COMPLETION_H_ #include #include namespace apvlv { class ApvlvCompletion final { public: explicit ApvlvCompletion (const std::vector &items) : mItems (items) { } ApvlvCompletion () = default; ~ApvlvCompletion () = default; void addItems (const std::vector &items); void addPath (const std::string &path); std::string complete (const std::string &np); private: std::vector mItems; }; }; #endif /* Local Variables: */ /* mode: c++ */ /* End: */ apvlv-0.7.0-alpha/src/ApvlvSearch.h0000644000175100017510000000477414726051136017450 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * * Copyright (C) 2008 Alf. * * Contact: Alf * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2.0 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ /* @CPPFILE SearchDialog.h * * Author: Alf */ #ifndef _APVLV_SEARCH_H_ #define _APVLV_SEARCH_H_ #include #include #include #include #include "ApvlvQueue.h" namespace apvlv { struct SearchMatch { std::string match; std::string line; size_t pos; size_t length; }; using SearchMatchList = std::vector; struct SearchPageMatch { int page; SearchMatchList matches; }; struct SearchFileMatch { std::string filename; std::vector page_matches; }; class SearchOptions { public: friend bool operator== (SearchOptions const &opt, SearchOptions const &other) { return opt.mText == other.mText && opt.mCaseSensitive != other.mCaseSensitive && opt.mRegex != other.mRegex && opt.mTypes != other.mTypes && opt.mFromDir != other.mFromDir; } std::string mText; bool mCaseSensitive; bool mRegex; std::string mFromDir; std::vector mTypes; }; class Searcher { public: Searcher (); ~Searcher (); void submit (const SearchOptions &options); std::unique_ptr get (); private: void dispatch (); void dirFunc (); void fileLoopFunc (); void fileFunc (const std::string &path); std::vector mTasks; SearchOptions mOptions; LockQueue mFilenameQueue; LockQueue> mResults; std::atomic mRestart; std::atomic mQuit; }; std::vector> grep (const std::string &source, const std::string &text, bool is_case, bool is_regex); } #endif apvlv-0.7.0-alpha/src/ApvlvFrame.h0000644000175100017510000001276714726051136017276 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * * Copyright (C) 2008 Alf. * * Contact: Alf * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2.0 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ /* @CPPFILE ApvlvFrame.h * * Author: Alf */ #ifndef _APVLV_FRAME_H_ #define _APVLV_FRAME_H_ #include #include #include #include #include #include #include #include "ApvlvCmds.h" #include "ApvlvContent.h" #include "ApvlvFile.h" #include "ApvlvFileWidget.h" #include "ApvlvWidget.h" namespace apvlv { struct ApvlvDocPosition { int pagenum; double scrollrate; }; using ApvlvDocPositionMap = std::map; struct ApvlvWord { CharRectangle pos; std::string word; }; struct ApvlvLine { CharRectangle pos; std::vector mWords; }; class ApvlvFrame; class ApvlvStatus : public QFrame { Q_OBJECT public: ApvlvStatus (); ~ApvlvStatus () override = default; void setActive (bool act); void showMessages (const std::vector &msgs); private: QHBoxLayout mLayout; }; class ApvlvToolStatus : public QToolBar { Q_OBJECT public: explicit ApvlvToolStatus (ApvlvFrame *frame); void updateValue (int pn, int totpn, double zm, double sr); private: ApvlvFrame *mFrame; ApvlvLineEdit mPageValue; QLabel mPageSum; QLabel mScrollRate; QComboBox mZoomType; ApvlvLineEdit mZoomValue; #ifdef APVLV_WITH_OCR QCheckBox mOcrParse; QAction mOcrCopy; #endif private slots: void gotoPage (); friend class ApvlvFrame; }; const int DEFAULT_CONTENT_WIDTH = 300; class FileWidget; class ApvlvView; class ApvlvFrame final : public QFrame { Q_OBJECT public: explicit ApvlvFrame (ApvlvView *view); ~ApvlvFrame () override; bool reload (); void inuse (bool use); bool inuse (); ApvlvFrame *clone (); void setDirIndex (const std::string &path); bool loadFile (const std::string &file, bool check, bool show_directory); bool loadUri (const std::string &uri); const char *filename (); int pageNumber (); void showPage (int pn, double s); void showPage (int pn, const std::string &anchor); void refresh (int pn, double s); void setActive (bool act); void updateStatus (); bool isStatusHidden (); void statusShow (); void statusHide (); bool print (int ct); bool totext (const char *name); bool rotate (int ct); void markposition (char s); void setZoomrate (double zm); void setZoomString (const char *z); void jump (char s); void nextPage (int times); void previousPage (int times); void halfNextPage (int times); void halfPreviousPage (int times); bool search (const char *str, bool reverse); void gotoLink (int ct); void returnLink (int ct); bool loadLastPosition (const std::string &filename); bool saveLastPosition (const std::string &filename); void directoryShowPage (const FileIndex *index, bool force); int getSkip (); void setSkip (int ct); void toggleDirectory (); void toggleDirectory (bool enabled); bool toggledControlDirectory (bool is_right); bool isShowDirectory (); bool isControlledDirectory (); void wheelEvent (QWheelEvent *event) override; CmdReturn process (int has, int times, uint keyval); ApvlvView *mView; void focusInEvent (QFocusEvent *event) override { emit focusIn (); } static ApvlvFrame *findByWidget (QWidget *widget); private: std::unique_ptr mFile; FileIndex mDirIndex{}; bool mInuse; std::unique_ptr mWatcher; std::string mFilestr; uint mProCmd; char mSearchCmd{}; std::unique_ptr mSearchResults; std::string mSearchStr; enum class ZoomMode { NORMAL, FITWIDTH, FITHEIGHT, CUSTOM }; ZoomMode mZoomMode; static std::vector ZoomLabel; bool mZoominit{}; int mSkip{}; ApvlvDocPositionMap mPositions; // the main menubar QVBoxLayout mVbox; // the main panel QSplitter mPaned; int mDirectoryWidth; QHBoxLayout mHBoxLayout; // directory panel Directory mDirectory; QFrame mTextFrame; QVBoxLayout mTextLayout; ApvlvToolStatus mToolStatus; std::unique_ptr mWidget; // status bar ApvlvStatus mStatus; // if active bool mActive{}; void setWidget (DISPLAY_TYPE type); void unsetHighlight (); void setHighlightAndIndex (const WordListRectangle &poses, int sel); bool needSearch (const std::string &str, bool reverse); CmdReturn subProcess (int ct, uint key); signals: void indexGenerited (const FileIndex &index); void focusIn (); private slots: void previousPage (); void nextPage (); void setZoomMode (int mode); void zoomIn (); void zoomOut (); #ifdef APVLV_WITH_OCR void ocrParse (); void ocrCopy (); #endif friend class ApvlvStats; friend class ApvlvToolStatus; }; } #endif /* Local Variables: */ /* mode: c++ */ /* End: */ apvlv-0.7.0-alpha/src/ApvlvCompletion.cc0000644000175100017510000000414114726051136020476 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * Copyright (C) <2008> Alf * * Contact: Alf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ /* @CPPFILE ApvlvCompletion.cc * * Author: Alf */ #include #include #include #include "ApvlvCompletion.h" #include "ApvlvUtil.h" namespace apvlv { using namespace std; string ApvlvCompletion::complete (const string &prefix) { auto iter = std::ranges::find_if (mItems, [prefix] (const string &item) { return item.find (prefix) == 0; }); if (iter != mItems.cend ()) return *iter; else return ""; } void ApvlvCompletion::addItems (const vector &items) { mItems.insert (mItems.end (), items.begin (), items.end ()); } void ApvlvCompletion::addPath (const string &path) { vector items; auto fspath = filesystem::path{ path }; auto filename = fspath.filename (); auto dirname = fspath.parent_path (); for (auto &entry : filesystem::directory_iterator (dirname)) { auto entry_filename = entry.path ().filename ().string (); if (filename.empty () || entry_filename.find (filename.string ()) == 0) { auto item = entry.path ().string () + (entry.is_directory () ? PATH_SEP_S : ""); qDebug () << "add a item: " << item; items.emplace_back (item); } } addItems (items); } } // Local Variables: // mode: c++ // End: apvlv-0.7.0-alpha/src/ApvlvCmds.h0000644000175100017510000000652214726051136017122 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * * Copyright (C) 2008 Alf. * * Contact: Alf * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2.0 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ /* @CPPFILE ApvlvCmds.h * * Author: Alf */ #ifndef _APVLV_CMDS_H_ #define _APVLV_CMDS_H_ #include #include #include #include namespace apvlv { enum class CmdType { CT_CMD, CT_STRING, CT_STRING_RETURN }; enum class CmdState { GETTING_COUNT, GETTING_CMD, CMD_OK, }; // command type enum class CmdStatusType { CMD_NONE, CMD_MESSAGE, CMD_CMD }; // function return type enum class CmdReturn { MATCH, NEED_MORE, NO_MATCH, }; // because every unsigned char is < 256, so use this marco to stand for // Ctrl+char, Shift+char constexpr int ctrlValue (int c) { return c + 256; } using StringKeyMap = std::map; class Command; using CommandKeyList = std::vector; using CommandMap = std::map; class ApvlvView; class Command final { public: Command (); ~Command () = default; void process (ApvlvView *view); void push (std::string_view s, CmdType type = CmdType::CT_CMD); bool append (QKeyEvent *key); const char *append (const char *s); void type (CmdType type); CmdType type (); CommandKeyList *keyVals (); CommandKeyList keyvalv (); void setPreCount (int precount); [[nodiscard]] int preCount () const; int keyval (uint id); Command *next (); void origin (Command *cmd); Command *origin (); private: static StringKeyMap mKeyMap; // command type CmdType mType; // if it has count bool mHasPreCount; // how to describe this command in .apvlvrc // like , s, or :run, :vsp, ... std::string mStrCommand; // key's value list CommandKeyList mKeyVals; // cmd's pre count int mPreCount; // next command std::unique_ptr mNext; // when a key is map to other, this is the origin cmd. // after a mapped key was processed, return to this cmds Command *mOrigin; }; class ApvlvCmds : public QObject { Q_OBJECT public: explicit ApvlvCmds (ApvlvView *view); ~ApvlvCmds () override; void append (QKeyEvent *gev); static void buildCommandMap (std::string_view os, std::string_view ms); private: Command *process (Command *cmd); static CmdReturn isMapCommand (CommandKeyList *ack); static Command *getMapCommand (Command *cmd); static CommandMap mMaps; std::unique_ptr mCmdHead; // command view ApvlvView *mView; CmdState mState; std::unique_ptr mTimeoutTimer; std::string mCountString; private slots: void timeoutCallback (); }; } #endif // Local Variables: // mode: c++ // End: apvlv-0.7.0-alpha/src/ApvlvFile.h0000644000175100017510000001410014726051136017102 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * Copyright (C) <2008> * * Contact: Alf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ /* @CPPFILE File.h * * Author: Alf */ #ifndef _APVLV_FILE_H_ #define _APVLV_FILE_H_ #include #include #include #include #include "ApvlvFileIndex.h" #include "ApvlvNote.h" #include "ApvlvParams.h" #include "ApvlvSearch.h" namespace apvlv { enum class DISPLAY_TYPE { IMAGE, HTML, CUSTOM, }; // // link to an url, or a page num // struct ApvlvLink { int mPage; }; struct ApvlvCover { std::string content; std::string mime_type; }; using ApvlvLinks = std::vector; struct ApvlvPoint { double x; double y; }; struct Size { int width; int height; }; struct SizeF { double width; double height; }; /* * position of a search result, or just an area */ struct Rectangle { double p1x; double p1y; double p2x; double p2y; }; using CharRectangle = Rectangle; struct WordRectangle { std::string word; std::vector rect_list; }; using WordListRectangle = std::vector; class WebView; class FileWidget; class File { public: virtual ~File (); virtual bool load (const std::string &filename) = 0; /* File methods */ [[nodiscard]] virtual DISPLAY_TYPE getDisplayType () const { return DISPLAY_TYPE::HTML; } virtual FileWidget * getWidget () { return nullptr; } const std::string & getFilename () { return mFilename; } bool setFilename (const std::string &filename) { if (load (filename)) { mFilename = filename; mNote.load (); return true; } return false; } const ApvlvCover & getCover () { return mCover; } Note * getNote () { return &mNote; } const FileIndex & getIndex () { return mIndex; } virtual std::unique_ptr grepFile (const std::string &seq, bool is_case, bool is_regex, std::atomic &is_abort); virtual int sum () { return 1; }; // Page methods Size pageSize (int page, int rot) { auto sizef = pageSizeF (page, rot); return { static_cast (sizef.width), static_cast (sizef.height) }; } virtual SizeF pageSizeF (int page, int rot) { return { 0, 0 }; } virtual int pageNumberWrap (int page) { auto scrdoc = ApvlvParams::instance ()->getBoolOrDefault ("autoscrolldoc"); int c = sum (); if (page >= 0 && page < c) { return page; } else if (page >= c && scrdoc) { return page % c; } else if (page < 0 && scrdoc) { while (page < 0) page += c; return page; } else { return -1; } } virtual bool pageRenderToImage (int pn, double zm, int rot, QImage *img); virtual bool pageRenderToWebView (int pn, double zm, int rot, WebView *webview); // some ebooks only have image as page virtual bool pageIsOnlyImage (int pn) { return false; } virtual std::unique_ptr pageLinks (int pn) { return nullptr; } virtual bool pageText (int pn, const Rectangle &rect, std::string &text) { return false; } virtual std::unique_ptr pageSearch (int pn, const char *str) { return nullptr; } virtual std::optional> pageHighlight (int pn, const ApvlvPoint &pa, const ApvlvPoint &pb) { return std::nullopt; } // path methods virtual std::optional pathContent (const std::string &path); virtual std::string pathMimeType (const std::string &path); virtual int pathPageNumber (const std::string &path); protected: File () : mNote (this) {} std::string mFilename; FileIndex mIndex; std::vector mPages; std::map srcPages; std::map srcMimeTypes; ApvlvCover mCover; Note mNote; private: std::optional pathContentHtml (int, double, int); std::optional pathContentPng (int, double, int); }; class FileFactory { public: static int registerClass (const std::string &name, const std::function &fun, std::initializer_list exts); static const std::map> & supportMimeTypes (); static std::vector supportFileExts (); static std::ostream &typeEngineDescription (std::ostream &os); using ExtClass = std::pair>; using ExtClassList = std::vector; static std::optional findMatchClass (const std::string &filename); static std::unique_ptr loadFile (const std::string &filename); private: static std::map> mSupportMimeTypes; static std::map mSupportClass; }; #define FILE_TYPE_DECLARATION(cls) \ private: \ static int class_id_of_##cls #define FILE_TYPE_DEFINITION(name, cls, ...) \ int cls::class_id_of_##cls = FileFactory::registerClass ( \ name, [] () -> File * { return new cls (); }, __VA_ARGS__) }; #endif /* Local Variables: */ /* mode: c++ */ /* End: */ apvlv-0.7.0-alpha/src/ApvlvInfo.h0000644000175100017510000000357514726051136017134 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * Copyright (C) <2008> * * Contact: Alf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ /* @CPPFILE ApvlvInfo.h * * Author: Alf */ #ifndef _APVLV_INFO_H_ #define _APVLV_INFO_H_ #include #include #include namespace apvlv { struct InfoFile { int page; int skip; double rate; std::string file; }; const int DEFAULT_MAX_INFO = 100; class ApvlvInfo final { public: ApvlvInfo (const ApvlvInfo &) = delete; ApvlvInfo &operator= (const ApvlvInfo &) = delete; void loadFile (std::string_view file); bool update (); std::optional lastFile (); std::optional file (const std::string &filename); bool updateFile (int page, int skip, double rate, const std::string &filename); static ApvlvInfo * instance () { static ApvlvInfo inst; return &inst; } private: ApvlvInfo (); ~ApvlvInfo () = default; std::string mFileName{}; std::deque mInfoFiles{}; std::deque::size_type mMaxInfo{ DEFAULT_MAX_INFO }; bool addPosition (const char *str); }; }; #endif /* Local Variables: */ /* mode: c++ */ /* End: */ apvlv-0.7.0-alpha/src/ApvlvFileWidget.cc0000644000175100017510000001006314726051136020410 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * * Copyright (C) 2008 Alf. * * Contact: Alf * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2.0 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ /* @CPPFILE ApvlvFileWidget.cc * * Author: Alf */ #include "ApvlvFileWidget.h" #include "ApvlvParams.h" namespace apvlv { using namespace std; void FileWidget::scroll (int times, int w, int h) { // need impl in child } double FileWidget::scrollRate () { if (mValScrollBar == nullptr) return mScrollValue; double maxv = mValScrollBar->maximum () - mValScrollBar->minimum (); double val = mValScrollBar->value () / maxv; if (val > 1.0) { return 1.00; } else if (val > 0.0) { return val; } else { return 0.00; } } void FileWidget::scrollTo (double s, double y) { if (!mValScrollBar) return; auto maxv = mValScrollBar->maximum () - mValScrollBar->minimum (); auto val = static_cast (y * maxv); mValScrollBar->setValue (val); } void FileWidget::scrollUp (int times) { if (mValScrollBar == nullptr) return; auto rate = LINE_HEIGHT_DEFAULT * times; if (mValScrollBar->value () - rate >= mValScrollBar->minimum ()) { mValScrollBar->setValue (mValScrollBar->value () - rate); } else if (mValScrollBar->value () > mValScrollBar->minimum ()) { mValScrollBar->setValue (mValScrollBar->minimum ()); } else { auto params = ApvlvParams::instance (); if (params->getBoolOrDefault ("autoscrollpage")) { if (mPageNumber == 0) { if (params->getBoolOrDefault ("autoscrolldoc")) { showPage (mFile->sum () - 1, 1.0); } } else { showPage (mPageNumber - 1, 1.0); } } } } void FileWidget::scrollDown (int times) { if (!mValScrollBar) return; auto rate = LINE_HEIGHT_DEFAULT * times; if (mValScrollBar->value () + rate <= mValScrollBar->maximum ()) { mValScrollBar->setValue (mValScrollBar->value () + rate); } else if (mValScrollBar->value () < mValScrollBar->maximum ()) { mValScrollBar->setValue (mValScrollBar->maximum ()); } else { auto params = ApvlvParams::instance (); if (params->getBoolOrDefault ("autoscrollpage")) { if (mPageNumber == mFile->sum () - 1) { if (params->getBoolOrDefault ("autoscrolldoc")) { showPage (0, 0.0); } } else { showPage (mPageNumber + 1, 0.0); } } } } void FileWidget::scrollLeft (int times) { if (!mHalScrollBar) return; auto val = mHalScrollBar->value () - WORD_WIDTH_DEFAULT * times; if (val > mHalScrollBar->minimumWidth ()) { mHalScrollBar->setValue (val); } else { mHalScrollBar->setValue (mHalScrollBar->minimumWidth ()); } } void FileWidget::scrollRight (int times) { if (!mHalScrollBar) return; auto val = mHalScrollBar->value () + WORD_WIDTH_DEFAULT * times; if (val + mHalScrollBar->width () < mHalScrollBar->maximumWidth ()) { mHalScrollBar->setValue (val); } else { mHalScrollBar->setValue (mHalScrollBar->maximumWidth () - mHalScrollBar->width ()); } } } /* Local Variables: */ /* mode: c++ */ /* End: */ apvlv-0.7.0-alpha/src/ApvlvOCR.h0000644000175100017510000000261314726051136016654 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * * Copyright (C) 2008 Alf. * * Contact: Alf * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2.0 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ /* @CPPFILE ApvlvOCR.h * * Author: Alf */ #ifndef _APVLV_OCR_H_ #define _APVLV_OCR_H_ #include #include #include #include #include namespace apvlv { using TextAreaVector = std::vector; class OCR final { public: OCR (); ~OCR (); std::unique_ptr getTextArea (const QPixmap &pixmap); std::unique_ptr getTextFromPixmap (const QPixmap &pixmap, QRect area = QRect ()); private: TessBaseAPI mTessBaseAPI; }; } #endif apvlv-0.7.0-alpha/src/ApvlvImageWidget.cc0000644000175100017510000002577314726051136020571 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * * Copyright (C) 2008 Alf. * * Contact: Alf * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2.0 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ /* @CPPFILE ApvlvImageWidget.cc * * Author: Alf */ #include #include #include #include #include #include "ApvlvImageWidget.h" namespace apvlv { using namespace std; #ifdef APVLV_WITH_OCR TextContainer::TextContainer (QWidget *parent) : Editor (parent) { setReadOnly (false); setHorizontalScrollBarPolicy (Qt::ScrollBarAlwaysOff); setVerticalScrollBarPolicy (Qt::ScrollBarAlwaysOff); } #endif ImageContainer::ImageContainer (QWidget *parent) : QLabel (parent) { mCopyAction.setText (tr ("Copy")); QObject::connect (&mCopyAction, SIGNAL (triggered (bool)), this, SLOT (copy ())); addAction (&mCopyAction); mUnderlineAction.setText (tr ("Underline")); QObject::connect (&mUnderlineAction, SIGNAL (triggered (bool)), this, SLOT (underline ())); addAction (&mUnderlineAction); mCommentAction.setText (tr ("Comment")); QObject::connect (&mCommentAction, SIGNAL (triggered (bool)), this, SLOT (comment ())); addAction (&mCommentAction); setContextMenuPolicy (Qt::ContextMenuPolicy::ActionsContextMenu); } void ImageContainer::mousePressEvent (QMouseEvent *event) { if (event->button () != Qt::MouseButton::LeftButton) return; redraw (); mIsSelected = true; mPressPosition = event->position (); } void ImageContainer::mouseMoveEvent (QMouseEvent *event) { if (!mIsSelected) return; mMovePosition = event->position (); auto range = selectionRange (); auto rect_list = mImageWidget->file ()->pageHighlight ( mImageWidget->pageNumber (), range.first, range.second); if (!rect_list) return; mImageWidget->setSelects (rect_list.value ()); redraw (); } void ImageContainer::mouseReleaseEvent (QMouseEvent *event) { mImageWidget->setSelects ({}); mIsSelected = false; } bool ImageContainer::renderImage (int pn, double zm, int rot) { return mImageWidget->file ()->pageRenderToImage (pn, zm, rot, &mImage); } void ImageContainer::redraw () { QImage img = mImage; if (!mImageWidget->searchResults ().empty ()) { imageSelectSearch (&img, mImageWidget->zoomrate (), mImageWidget->searchSelect (), mImageWidget->searchResults ()); } else if (!mImageWidget->selects ().empty ()) { imageSelect (&img, mImageWidget->zoomrate (), mImageWidget->selects ()); } setPixmap (QPixmap::fromImage (img)); resize (img.size ()); } pair ImageContainer::selectionRange () { double left = mPressPosition.x () / mImageWidget->zoomrate (); double top = mPressPosition.y () / mImageWidget->zoomrate (); double right = mMovePosition.x () / mImageWidget->zoomrate (); double bottom = mMovePosition.y () / mImageWidget->zoomrate (); return { { left, top }, { right, bottom } }; } vector ImageContainer::selectionArea () { auto range = selectionRange (); auto rect_list = mImageWidget->file ()->pageHighlight ( mImageWidget->pageNumber (), range.first, range.second); if (!rect_list) return {}; return rect_list.value (); } string ImageContainer::selectionText () { auto range = selectionRange (); string text; mImageWidget->file ()->pageText ( mImageWidget->pageNumber (), { range.first.x, range.first.y, range.second.x, range.second.y }, text); return text; } void ImageContainer::copy () { qDebug () << "copy text"; auto text = selectionText (); auto clipboard = QGuiApplication::clipboard (); clipboard->setText (QString::fromLocal8Bit (text)); mImageWidget->setSelects ({}); redraw (); } void ImageContainer::underline () { qDebug () << "underline text"; auto page = mImageWidget->pageNumber (); auto range = selectionRange (); auto text = selectionText (); if (!text.empty ()) { auto note = mImageWidget->file ()->getNote (); Comment comment; comment.quoteText = text; comment.begin.set (page, &range.first); comment.end.set (page, &range.second); note->addComment (comment); } mImageWidget->setSelects ({}); redraw (); } void ImageContainer::comment () { qDebug () << "comment text"; do { auto text = selectionText (); if (text.empty ()) break; auto input_text = QInputDialog::getMultiLineText (this, tr ("Input"), tr ("Comment")); auto commentText = input_text.trimmed (); if (commentText.isEmpty ()) break; auto page = mImageWidget->pageNumber (); auto range = selectionRange (); auto note = mImageWidget->file ()->getNote (); Comment comment; comment.quoteText = text, comment.commentText = commentText.toStdString (); comment.begin.set (page, &range.first); comment.end.set (page, &range.second); note->addComment (comment); } while (false); mImageWidget->setSelects ({}); redraw (); } ApvlvImage::ApvlvImage () { setAlignment (Qt::AlignCenter); setHorizontalScrollBarPolicy (Qt::ScrollBarPolicy::ScrollBarAsNeeded); setVerticalScrollBarPolicy (Qt::ScrollBarPolicy::ScrollBarAsNeeded); setWidget (&mImageContainer); } ApvlvImage::~ApvlvImage () { qDebug () << "ApvlvImage: " << this << " be freed"; } #ifdef APVLV_WITH_OCR void ApvlvImage::ocrDisplay (bool is_ocr) { if (is_ocr) { auto image = mImageContainer.pixmap (); auto text = mOCR.getTextFromPixmap (image); mTextContainer.setText (text.get ()); if (widget () != &mTextContainer) { takeWidget (); setWidget (&mTextContainer); } } else { if (widget () != &mImageContainer) { takeWidget (); setWidget (&mImageContainer); } } } std::unique_ptr ApvlvImage::ocrGetText () { auto image = mImageContainer.pixmap (); auto text = mOCR.getTextFromPixmap (image); return text; } #endif void ImageWidget::showPage (int p, double s) { if (p != mPageNumber) { if (!mImage.mImageContainer.renderImage (p, mZoomrate, mRotate)) return; } mImage.mImageContainer.redraw (); #ifdef APVLV_WITH_OCR mImage.mTextContainer.resize (mImage.mImageContainer.size ()); if (mImage.widget () == &mImage.mTextContainer) { mImage.mTextContainer.setZoomrate (mZoomrate); mImage.ocrDisplay (true); } #endif scrollTo (0.0, s); mPageNumber = p; } void ImageWidget::showPage (int p, const string &anchor) { showPage (p, 0.0f); mAnchor = anchor; } void ImageWidget::setSearchResults (const WordListRectangle &wlr) { mSearchResults = wlr; mImage.mImageContainer.redraw (); } void ImageWidget::setZoomrate (double zm) { if (mPageNumber != INVALID_PAGENUM) { if (mImage.mImageContainer.renderImage (mPageNumber, zm, mRotate)) { mImage.mImageContainer.redraw (); mZoomrate = zm; } #ifdef APVLV_WITH_OCR mImage.mTextContainer.setZoomrate (zm); #endif } else { mZoomrate = zm; } } void ImageWidget::setRotate (int rotate) { if (mPageNumber != INVALID_PAGENUM) { if (mImage.mImageContainer.renderImage (mPageNumber, mZoomrate, rotate)) { mImage.mImageContainer.redraw (); mRotate = rotate; } } else { mRotate = rotate; } } bool imageSelect (QImage *pix, double zm, const vector &rect_list) { for (auto const &rect : rect_list) { auto p1xz = static_cast (rect.p1x * zm); auto p2xz = static_cast (rect.p2x * zm); auto p1yz = static_cast (rect.p1y * zm); auto p2yz = static_cast (rect.p2y * zm); for (auto w = p1xz; w < p2xz; ++w) { for (auto h = p1yz; h < p2yz; ++h) { QColor c = pix->pixelColor (w, h); c.setRgb (255 - c.red (), 255 - c.red (), 255 - c.red ()); pix->setPixelColor (w, h, c); } } } return true; } bool imageUnderline (QImage *pix, double zm, const vector &rect_list) { for (auto const &rect : rect_list) { auto p1xz = static_cast (rect.p1x * zm); auto p2xz = static_cast (rect.p2x * zm); auto p1yz = static_cast (rect.p1y * zm); auto p2yz = static_cast (rect.p2y * zm); for (auto w = p1xz; w < p2xz; ++w) { for (auto h = p1yz; h < p2yz; ++h) { QColor c = pix->pixelColor (w, h); c.setRgb (255 - c.red (), 255 - c.red (), 255 - c.red ()); pix->setPixelColor (w, h, c); } } } return true; } bool imageSelectSearch (QImage *pix, double zm, int select, const WordListRectangle &wordlist) { for (auto itr = wordlist.begin (); itr != wordlist.end (); ++itr) { auto rectangles = *itr; for (auto const &pos : rectangles.rect_list) { auto p1xz = static_cast (pos.p1x * zm); auto p2xz = static_cast (pos.p2x * zm); auto p1yz = static_cast (pos.p2y * zm); auto p2yz = static_cast (pos.p1y * zm); if (pix->format () == QImage::Format_ARGB32) { imageArgb32ToRgb32 (*pix, p1xz, p1yz, p2xz, p2yz); } if (std::distance (wordlist.begin (), itr) == select) { for (int w = p1xz; w < p2xz; ++w) { for (int h = p1yz; h < p2yz; ++h) { QColor c = pix->pixelColor (w, h); c.setRgb (255 - c.red (), 255 - c.red (), 255 - c.red ()); pix->setPixelColor (w, h, c); } } } else { for (int w = p1xz; w < p2xz; ++w) { for (int h = p1yz; h < p2yz; ++h) { QColor c = pix->pixelColor (w, h); c.setRgb (255 - c.red () / 2, 255 - c.red () / 2, 255 - c.red () / 2); pix->setPixelColor (w, h, c); } } } } } return true; } } // Local Variables: // mode: c++ // End: apvlv-0.7.0-alpha/src/ApvlvSearch.cc0000644000175100017510000001400714726051136017574 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * * Copyright (C) 2008 Alf. * * Contact: Alf * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2.0 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ /* @CPPFILE SearchDialog.cc * * Author: Alf */ #include #include #include #include #include #include "ApvlvFile.h" #include "ApvlvParams.h" #include "ApvlvSearch.h" namespace apvlv { using namespace std; Searcher::Searcher () : mRestart (false), mQuit (false) { auto task = thread (&Searcher::dispatch, this); mTasks.emplace_back (std::move (task)); auto thread_count = thread::hardware_concurrency () - 1; auto thread_value = ApvlvParams::instance ()->getStringOrDefault ("thread_count", "auto"); if (thread_value != "auto") { thread_count = ApvlvParams::instance ()->getIntOrDefault ("thread_count", thread_count); } for (auto ind = 0u; ind < thread_count; ++ind) { task = thread (&Searcher::fileLoopFunc, this); mTasks.emplace_back (std::move (task)); } } Searcher::~Searcher () { mRestart.store (true); mQuit.store (true); std::ranges::for_each (mTasks, [] (thread &task) { task.join (); }); qDebug ("all search threads ended"); } void Searcher::submit (const SearchOptions &options) { mOptions = options; auto path = filesystem::path (options.mFromDir); if (is_regular_file (path)) { mFilenameQueue.push (absolute (path).string ()); } mRestart.store (true); } unique_ptr Searcher::get () { unique_ptr ptr; mResults.pop (ptr); return ptr; } void Searcher::dispatch () { while (mQuit.load () == false) { if (mRestart.load () == true) { this_thread::sleep_for (2s); mFilenameQueue.clear (); mResults.clear (); mRestart.store (false); try { dirFunc (); } catch (const exception &ext) { qWarning () << "search occurred error: " << ext.what (); } } else { this_thread::sleep_for (1s); } } } void Searcher::dirFunc () { qDebug () << "searching " << mOptions.mText << " from " << mOptions.mFromDir; stack dirs; dirs.push (mOptions.mFromDir); while (!dirs.empty ()) { if (mRestart.load () == true || mQuit.load () == true) { return; } auto dir = dirs.top (); dirs.pop (); filesystem::directory_iterator itr (dir); for (const auto &entry : itr) { if (mRestart.load () == true || mQuit.load () == true) { return; } if (entry.is_directory ()) { if (entry.path ().filename () != "." && entry.path ().filename () != "..") dirs.push (entry.path ().string ()); } else if (entry.is_regular_file ()) { auto ext = entry.path ().extension (); if (ext.empty ()) continue; auto titr = find (mOptions.mTypes.begin (), mOptions.mTypes.end (), entry.path ().extension ()); if (titr != mOptions.mTypes.end ()) { mFilenameQueue.push (entry.path ().string ()); } } } } } void Searcher::fileLoopFunc () { while (mQuit.load () == false) { string name; if (mFilenameQueue.pop (name)) { fileFunc (name); } else { this_thread::sleep_for (1s); } } } void Searcher::fileFunc (const string &path) { auto file = FileFactory::loadFile (path); if (file) { auto result = file->grepFile (mOptions.mText, mOptions.mCaseSensitive, mOptions.mRegex, mRestart); if (result) mResults.push (std::move (result)); } } vector> grep (const string &source, const string &text, bool is_case, bool is_regex) { vector> results; if (is_regex == true) { regex regex_1{ text }; const sregex_token_iterator end; sregex_token_iterator iter; vector regex_texts; while ((iter = regex_token_iterator (source.begin (), source.end (), regex_1)) != end) { regex_texts.push_back (iter->str ()); } size_t pos = 0; for (auto const &r_text : regex_texts) { pos = source.find (r_text, pos); pair res{ pos, r_text.size () }; results.emplace_back (std::move (res)); } } else { auto p_source = &source; auto p_text = &text; if (is_case == false) { auto nsource = source; auto ntext = text; std::ranges::transform (nsource, nsource.begin (), ::tolower); std::ranges::transform (ntext, ntext.begin (), ::tolower); p_source = &nsource; p_text = &ntext; } auto pos = p_source->find (*p_text); while (pos != string::npos) { pair res{ pos, p_text->size () }; results.emplace_back (std::move (res)); pos = p_source->find (*p_text, pos + 1); } } return results; } } apvlv-0.7.0-alpha/src/ApvlvUtil.h0000644000175100017510000000503214726051136017144 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * * Copyright (C) 2008 Alf. * * Contact: Alf * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2.0 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ /* @CPPFILE ApvlvUtil.h * * Author: Alf */ #ifndef _APVLV_UTIL_H_ #define _APVLV_UTIL_H_ #include #include #include #include #include namespace apvlv { // Global files extern std::string HelpPdf; extern std::string IniExam; extern std::string IconDir; extern std::string IconFile; extern std::string IconPage; extern std::string Translations; extern std::string IniFile; extern std::string SessionFile; extern std::string LogFile; extern std::string NotesDir; void getRuntimePaths (); #ifdef WIN32 const char PATH_SEP_C = '\\'; const char *const PATH_SEP_S = "\\"; #else const char PATH_SEP_C = '/'; const char *const PATH_SEP_S = "/"; #endif std::optional> xmlContentGetElement (const char *content, size_t length, const std::vector &names); std::string xmlStreamGetAttributeValue (QXmlStreamReader *xml, const std::string &key); std::string xmlContentGetAttributeValue (const char *content, size_t length, const std::vector &names, const std::string &key); std::string filenameExtension (const std::string &filename); void imageArgb32ToRgb32 (QImage &image, int left, int top, int right, int bottom); std::string templateBuild (std::string_view temp, std::string_view token, std::string_view real); qint64 parseFormattedDataSize (const QString &sizeStr); qint64 filesystemTimeToMSeconds (std::filesystem::file_time_type ftt); } #endif // Local Variables: // mode: c++ // mode: c++ // End: apvlv-0.7.0-alpha/src/ApvlvWebViewWidget.cc0000644000175100017510000001360014726051136021101 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * * Copyright (C) 2008 Alf. * * Contact: Alf * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2.0 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ /* @CPPFILE ApvlvWebViewWidget.cc * * Author: Alf */ #include #include #include #include #include "ApvlvWebViewWidget.h" namespace apvlv { using namespace std; void ApvlvSchemeHandler::requestStarted (QWebEngineUrlRequestJob *job) { auto url = job->requestUrl (); auto path = url.path ().toStdString (); auto key = path.substr (1); auto mime = mFile->pathMimeType (key); auto roptcont = mFile->pathContent (key); if (!roptcont) { job->fail (QWebEngineUrlRequestJob::UrlNotFound); return; } mArray = std::move (roptcont.value ()); mBuffer.setData (mArray); job->reply (QByteArray (mime.c_str ()), &mBuffer); emit webpageUpdated (key); } WebView::WebView () { mProfile.setHttpCacheType (QWebEngineProfile::NoCache); mProfile.installUrlSchemeHandler ("apvlv", &mSchemeHandler); mPage = make_unique (&mProfile); setPage (mPage.get ()); } void WebViewWidget::showPage (int p, double s) { mFile->pageRenderToWebView (p, mZoomrate, 0, &mWebView); mPageNumber = p; } void WebViewWidget::showPage (int p, const string &anchor) { mFile->pageRenderToWebView (p, mZoomrate, 0, &mWebView); mPageNumber = p; mAnchor = anchor; } void WebViewWidget::scroll (int times, int h, int v) { if (!mFile) return; stringstream scripts; scripts << "window.scrollBy(" << times * h << "," << times * v << ")"; auto page = mWebView.page (); page->runJavaScript (QString::fromLocal8Bit (scripts.str ())); } void WebViewWidget::scrollTo (double xrate, double yrate) { if (!mFile) return; stringstream scripts; scripts << "window.scroll(window.screenX * " << xrate << ","; scripts << " (document.body.offsetHeight - window.innerHeight) * " << yrate << ");"; auto page = mWebView.page (); page->runJavaScript (QString::fromLocal8Bit (scripts.str ())); } void WebViewWidget::scrollUp (int times) { scroll (times, 0, -50); if (mWebView.isScrolledToTop ()) { if (mIsInternalScroll) { // clang-format off auto rs = R"( var event = document.createEvent ('Event'); event.initEvent('keydown', true, true); event.keyCode = 37; document.dispatchEvent(event); )"; // clang-format on auto page = mWebView.page (); page->runJavaScript ( QString::fromLocal8Bit (rs), [] (const QVariant &v) { qDebug () << v.toString (); }); } else { auto p = mFile->pageNumberWrap (mPageNumber - 1); if (p >= 0) { mIsScrollUp = true; showPage (p, 0.0); } } } } void WebViewWidget::scrollDown (int times) { scroll (times, 0, 50); if (mWebView.isScrolledToBottom ()) { if (mIsInternalScroll) { // clang-format off auto rs = R"( var event = document.createEvent ('Event'); event.initEvent('keydown', true, true); event.keyCode = 39; document.dispatchEvent(event); )"; // clang-format on auto page = mWebView.page (); page->runJavaScript ( QString::fromLocal8Bit (rs), [] (const QVariant &v) { qDebug () << v.toString (); }); } else { auto p = mFile->pageNumberWrap (mPageNumber + 1); if (p >= 0) showPage (p, 0.0); } } } void WebViewWidget::scrollLeft (int times) { scroll (times, -50, 0); } void WebViewWidget::scrollRight (int times) { scroll (times, 50, 0); } void WebViewWidget::setSearchStr (const string &str) { auto qstr = QString::fromLocal8Bit (str); QWebEnginePage::FindFlags flags{}; mWebView.findText (qstr, flags); } void WebViewWidget::setSearchSelect (int select) { auto text = mWebView.selectedText (); QWebEnginePage::FindFlags flags{}; mWebView.findText (text, flags); mSearchSelect = select; } void WebViewWidget::setZoomrate (double zm) { mWebView.setZoomFactor (zm); mZoomrate = zm; } bool WebView::isScrolledToTop () { auto page = this->page (); auto p = page->scrollPosition (); return p.y () < 0.5; } bool WebView::isScrolledToBottom () { auto page = this->page (); auto p = page->scrollPosition (); auto cs = page->contentsSize (); return p.y () + QWebEngineView::height () + 0.5 > cs.height (); } void WebViewWidget::webviewLoadFinished (bool suc) { if (suc) { if (!mAnchor.empty ()) { auto page = mWebView.page (); stringstream javasrc; javasrc << "document.getElementById('"; javasrc << mAnchor.substr (1); javasrc << "').scrollIntoView();"; page->runJavaScript (QString::fromLocal8Bit (javasrc.str ())); } else if (mIsScrollUp) { scrollTo (0.0, 1.0); mIsScrollUp = false; } } } void WebViewWidget::webviewFindTextFinished (const QWebEngineFindTextResult &result) { mSearchResult = result; } } // Local Variables: // mode: c++ // End: apvlv-0.7.0-alpha/src/ApvlvWidget.h0000644000175100017510000000222014726051136017446 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * * Copyright (C) 2008 Alf. * * Contact: Alf * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2.0 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ /* @CPPFILE ApvlvWidget.h * * Author: Alf */ #ifndef _APVLV_WIDGET_H_ #define _APVLV_WIDGET_H_ #include namespace apvlv { class ApvlvLineEdit : public QLineEdit { protected: void keyPressEvent (QKeyEvent *event) override; }; } #endif /* Local Variables: */ /* mode: c++ */ /* End: */ apvlv-0.7.0-alpha/src/ApvlvOCR.cc0000644000175100017510000000353214726051136017013 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * * Copyright (C) 2008 Alf. * * Contact: Alf * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2.0 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ /* @CPPFILE ApvlvOCR.cc * * Author: Alf */ #include "ApvlvOCR.h" #include "ApvlvParams.h" namespace apvlv { using namespace std; OCR::OCR () { auto lang = ApvlvParams::instance ()->getGroupStringOrDefault ( "ocr", "lang", "eng+chi_sim"); mTessBaseAPI.Init (nullptr, lang.c_str ()); } OCR::~OCR () { mTessBaseAPI.End (); } std::unique_ptr OCR::getTextArea (const QPixmap &pixmap) { return nullptr; } std::unique_ptr OCR::getTextFromPixmap (const QPixmap &pixmap, QRect area) { auto image = pixmap.toImage (); image = image.convertToFormat (QImage::Format_RGB888); mTessBaseAPI.SetImage (image.bits (), image.width (), image.height (), 3, static_cast (image.bytesPerLine ())); auto text = mTessBaseAPI.GetUTF8Text (); if (area.isValid ()) { mTessBaseAPI.SetRectangle (area.left (), area.top (), area.width (), area.height ()); } mTessBaseAPI.Clear (); return unique_ptr (text); } } apvlv-0.7.0-alpha/src/ApvlvLab.h0000644000175100017510000000245714726051136016735 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * Copyright (C) <2010> * * Contact: Alf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ /* @CPPFILE ApvlvLab.h * * Author: Alf */ #ifndef _APVLV_LAB_H_ #define _APVLV_LAB_H_ #include "ApvlvFile.h" #include namespace apvlv { class ApvlvLab : public File { public: bool load (const std::string &filename) override; ~ApvlvLab () override; bool pageRenderToWebView (int pn, double zm, int rot, WebView *webview) override; private: }; } #endif /* Local Variables: */ /* mode: c++ */ /* End: */ apvlv-0.7.0-alpha/src/ApvlvLog.cc0000644000175100017510000000551714726051136017116 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * Copyright (C) <2024> Alf * * Contact: Alf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ /* @CPPFILE ApvlvLog.cc * * Author: Alf */ #include #include #include #include #include #include #include "ApvlvLog.h" namespace apvlv { void ApvlvLog::setLogFile (const std::string &path) { using FileFlag = QIODevice::OpenModeFlag; if (!path.empty ()) { mFile.setFileName (QString::fromLocal8Bit (path)); if (mFile.open (FileFlag::Text | FileFlag::WriteOnly | FileFlag::Append) == false) { std::cerr << "Open log file: " << path << "error: " << mFile.errorString ().toStdString () << std::endl; return; } mTextStream.setDevice (&mFile); } QLoggingCategory::setFilterRules ("qt.*=false\n" "default.debug=true\n" "default.*=true"); qInstallMessageHandler (ApvlvLog::logMessage); } void ApvlvLog::writeMessage (const QString &msg) { std::lock_guard lock (mMutex); #ifdef _DEBUG std::cout << msg.toStdString () << std::endl; #endif auto endstr = "\n"; #ifdef WIN32 endstr = "\r\n"; #endif if (mTextStream.device () && mTextStream.device ()->isOpen ()) { mTextStream << msg << endstr; } } ApvlvLog::~ApvlvLog () { if (mFile.isOpen ()) { mFile.close (); } } ApvlvLog * ApvlvLog::instance () { static ApvlvLog log; return &log; } void ApvlvLog::logMessage (QtMsgType type, const QMessageLogContext &context, const QString &msg) { auto now = QTime::currentTime (); auto nowstr = now.toString ("hh:mm:ss.zzz"); QString log = nowstr + " "; if (context.file) { auto filename = QFileInfo (context.file).fileName ().toStdString (); log += QString::asprintf ("%s:%d ", filename.c_str (), context.line); log += QString::asprintf ("%s ", context.function); } log += msg; ApvlvLog::instance ()->writeMessage (log); } } // Local Variables: // mode: c++ // End: apvlv-0.7.0-alpha/src/CMakeLists.txt0000644000175100017510000002467314726051136017621 0ustar atzlinuxatzlinux# compile options IF (WIN32) ADD_DEFINITIONS(-D_CRT_SECURE_NO_WARNINGS) ELSE () SET(CMAKE_CXX_FLAGS "-std=c++20 -Wall -fno-strict-aliasing") IF (${CMAKE_BUILD_TYPE} STREQUAL Debug) SET(CMAKE_CXX_FLAGS "-D_DEBUG -g ${CMAKE_CXX_FLAGS}") ELSE () SET(CMAKE_CXX_FLAGS "-O2 ${CMAKE_CXX_FLAGS}") ENDIF () ENDIF () MESSAGE("-- BUILD TYPE: ${CMAKE_BUILD_TYPE}") MESSAGE("-- CXX FLAGS: ${CMAKE_CXX_FLAGS}") OPTION(APVLV_WITH_MUPDF "If build binary with MuPDF." On) IF (WIN32) OPTION(APVLV_WITH_POPPLER "If build binary with Poppler-Qt6." On) ELSE () OPTION(APVLV_WITH_POPPLER "If build binary with Poppler-Qt6." Off) ENDIF () IF (WIN32) SET(Qt6_DIR "C:/Qt/6.7.2/msvc2019_64/lib/cmake/Qt6" CACHE STRING "Qt6config.cmake directory") SET(Qt6QmlTools_DIR "C:/Qt/6.7.2/msvc2019_64/lib/cmake/Qt6QmlTools" CACHE STRING "Qt6QmlToolsconfig.cmake directory") SET(Qt6AxContainer_DIR "C:/Qt/6.7.2/msvc2019_64/lib/cmake/Qt6AxContainer" CACHE STRING "Qt6config.cmake directory") SET(QUAZIP_INCLUDE_DIRS "C:/Qt/6.7.2/msvc2019_64/include/QuaZip-Qt6-1.4/quazip;C:/Qt/6.7.2/msvc2019_64/include" CACHE STRING "Quazip include directory") SET(QUAZIP_LIBRARY_DIRS "C:/Qt/6.7.2/msvc2019_64/lib" CACHE STRING "Quazip lib directory") SET(QUAZIP_LIBRARIES "C:/Qt/6.7.2/msvc2019_64/lib/quazip1-qt6.lib" CACHE STRING "Quazip library file") SET(DJVULIBRE_DIR "C:/Program Files (x86)/DjVuLibre" CACHE PATH "DjvuLibre dir") CMAKE_POLICY(SET CMP0010 NEW) CMAKE_POLICY(SET CMP0087 NEW) ELSE () FIND_PACKAGE(PkgConfig) IF (${APVLV_WITH_POPPLER}) PKG_CHECK_MODULES(POPPLER poppler-qt6) ENDIF () PKG_CHECK_MODULES(QUAZIP quazip1-qt6 REQUIRED) ENDIF () FIND_PACKAGE(Qt6 NAMES Qt6 COMPONENTS Core Gui Widgets WebEngineWidgets Pdf PdfWidgets Xml PrintSupport REQUIRED) SET(Qt_INCLUDE_DIRS ${Qt6Core_INCLUDE_DIRS} ${Qt6Gui_INCLUDE_DIRS} ${Qt6Widgets_INCLUDE_DIRS} ${Qt6WebEngineWidgets_INCLUDE_DIRS} ${Qt6Pdf_INCLUDE_DIRS} ${Qt6PdfWidgets_INCLUDE_DIRS} ${Qt6Xml_INCLUDE_DIRS} ${Qt6PrintSupport_INCLUDE_DIRS}) SET(Qt_LIBRARIES Qt6::Core Qt6::Gui Qt6::Widgets Qt6::WebEngineWidgets Qt6::Pdf Qt6::PdfWidgets Qt6::Xml Qt6::PrintSupport) GET_FILENAME_COMPONENT(_qt_bin_dir "${WINDEPLOYQT_EXECUTABLE}" DIRECTORY) MESSAGE("-- Qt6 binary directory: ${_qt_bin_dir}") INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_SOURCE_DIR} ${QUAZIP_INCLUDE_DIRS} ${Qt_INCLUDE_DIRS}) LINK_DIRECTORIES( ${QUAZIP_LIBRARY_DIRS} ${Qt_LIBRARY_DIRS}) SET(APVLV_REQ_LIBRARIES ${QUAZIP_LIBRARIES} ${Qt_LIBRARIES}) ADD_DEFINITIONS(-DQT_MESSAGELOGCONTEXT) SET(HEADERS ApvlvCmds.h ApvlvFrame.h ApvlvFile.h ApvlvFileIndex.h ApvlvFileWidget.h ApvlvWidget.h ApvlvWeb.h ApvlvInfo.h ApvlvParams.h ApvlvUtil.h ApvlvView.h ApvlvWindow.h ApvlvCompletion.h ApvlvContent.h ApvlvLab.h ApvlvLog.h ApvlvSearch.h ApvlvSearchDialog.h ApvlvDired.h ApvlvQueue.h ApvlvImageWidget.h ApvlvWebViewWidget.h ApvlvEditor.h ApvlvNote.h file/ApvlvHtm.h file/ApvlvImage.h file/ApvlvQtPdf.h file/ApvlvEpub.h file/ApvlvFb2.h file/ApvlvTxt.h ) SET(SOURCES ApvlvCmds.cc ApvlvFrame.cc ApvlvFile.cc ApvlvFileIndex.cc ApvlvFileWidget.cc ApvlvWidget.cc ApvlvWeb.cc ApvlvInfo.cc ApvlvParams.cc ApvlvUtil.cc ApvlvView.cc ApvlvWindow.cc ApvlvCompletion.cc ApvlvContent.cc ApvlvLab.cc ApvlvLog.cc ApvlvSearch.cc ApvlvSearchDialog.cc ApvlvDired.cc ApvlvImageWidget.cc ApvlvWebViewWidget.cc ApvlvEditor.cc ApvlvNote.cc file/ApvlvHtm.cc file/ApvlvImage.cc file/ApvlvQtPdf.cc file/ApvlvEpub.cc file/ApvlvFb2.cc file/ApvlvTxt.cc main.cc ) MESSAGE("-- will build Web as epub engine") MESSAGE("-- will build Web as fb2 engine") MESSAGE("-- will build QtPdf as pdf engine") IF (${APVLV_WITH_MUPDF}) MESSAGE("-- will build MuPDF as pdf engine") SET(HEADERS ${HEADERS} file/ApvlvMuPdf.h) SET(SOURCES ${SOURCES} file/ApvlvMuPdf.cc) IF (WIN32) SET(APVLV_REQ_LIBRARIES mupdf.lib ${APVLV_REQ_LIBRARIES}) ELSE () SET(APVLV_REQ_LIBRARIES -lmupdf ${APVLV_REQ_LIBRARIES}) ENDIF () ENDIF () IF (${APVLV_WITH_POPPLER}) MESSAGE("-- will build Poppler as pdf engine") SET(HEADERS ${HEADERS} file/ApvlvPopplerPdf.h) SET(SOURCES ${SOURCES} file/ApvlvPopplerPdf.cc) INCLUDE_DIRECTORIES(${POPPLER_INCLUDE_DIRS}) LINK_DIRECTORIES(${POPPLER_LIBRARY_DIRS}) SET(APVLV_REQ_LIBRARIES ${POPPLER_LIBRARIES} ${APVLV_REQ_LIBRARIES}) ENDIF () OPTION(APVLV_WITH_DJVU "If build apvlv with djvu format support." ON) IF (APVLV_WITH_DJVU) MESSAGE("-- will build djvulibre as djvu engine") IF (WIN32) INCLUDE_DIRECTORIES(${DJVULIBRE_DIR}/include) LINK_DIRECTORIES(${DJVULIBRE_DIR}) SET(APVLV_REQ_LIBRARIES libdjvulibre.lib ${APVLV_REQ_LIBRARIES}) ELSE (WIN32) SET(APVLV_REQ_LIBRARIES -ldjvulibre ${APVLV_REQ_LIBRARIES}) ENDIF (WIN32) SET(HEADERS ${HEADERS} file/ApvlvDjvu.h) SET(SOURCES ${SOURCES} file/ApvlvDjvu.cc) ENDIF (APVLV_WITH_DJVU) OPTION(APVLV_WITH_OFFICE "If build apvlv with office format support." ON) IF (APVLV_WITH_OFFICE) IF (WIN32) MESSAGE("-- will build MSOffice as office file engine") FIND_PACKAGE(Qt6AxContainer REQUIRED) INCLUDE_DIRECTORIES(${Qt6AxContainer_INCLUDE_DIRES}) SET(APVLV_REQ_LIBRARIES ${Qt6AxContainer_LIBRARIES} Shlwapi.lib ${APVLV_REQ_LIBRARIES}) SET(HEADERS ${HEADERS} file/ApvlvAxOffice.h) SET(SOURCES ${SOURCES} file/ApvlvAxOffice.cc) ELSE (WIN32) MESSAGE("-- will build libreOffice as office file engine") SET(APVLV_REQ_LIBRARIES -llibreofficekitgtk ${APVLV_REQ_LIBRARIES}) ADD_DEFINITIONS(-DLIBO_INTERNAL_ONLY=1) SET(HEADERS ${HEADERS} file/ApvlvLibreOffice.h) SET(SOURCES ${SOURCES} file/ApvlvLibreOffice.cc) ENDIF (WIN32) ENDIF (APVLV_WITH_OFFICE) OPTION(APVLV_WITH_OCR "If build apvlv with OCR support." ON) IF (${APVLV_WITH_OCR}) ADD_DEFINITIONS(-DAPVLV_WITH_OCR) SET(HEADERS ${HEADERS} ApvlvOCR.h) SET(SOURCES ${SOURCES} ApvlvOCR.cc) IF (WIN32) ELSE () MESSAGE("-- will build tesseract as OCR library") PKG_CHECK_MODULES(TESSERACT tesseract) INCLUDE_DIRECTORIES(${TESSERACT_INCLUDE_DIRS}) LINK_DIRECTORIES(${TESSERACT_LIBRARY_DIRS}) SET(APVLV_REQ_LIBRARIES ${TESSERACT_LIBRARIES} ${APVLV_REQ_LIBRARIES}) ENDIF () ENDIF () MESSAGE("-- link libraries: ${APVLV_REQ_LIBRARIES}") ADD_EXECUTABLE(apvlv ${HEADERS} ${SOURCES}) SET_PROPERTY(TARGET apvlv PROPERTY AUTOMOC ON) TARGET_LINK_LIBRARIES(apvlv ${APVLV_REQ_LIBRARIES}) # for debug IF (WIN32) ADD_CUSTOM_COMMAND(TARGET apvlv POST_BUILD COMMAND ${CMAKE_COMMAND} "-E" copy_directory ${CMAKE_SOURCE_DIR}/share ${CMAKE_CURRENT_BINARY_DIR}/share) ADD_CUSTOM_COMMAND(TARGET apvlv POST_BUILD COMMAND "${CMAKE_COMMAND}" -E env PATH="${_qt_bin_dir}" "${WINDEPLOYQT_EXECUTABLE}" --verbose 0 --dir "${CMAKE_CURRENT_BINARY_DIR}/Debug" $ COMMENT "Deploying Qt..." ) ELSE () ADD_CUSTOM_COMMAND(TARGET apvlv POST_BUILD COMMAND ${CMAKE_COMMAND} "-E" remove_directory ${CMAKE_BINARY_DIR}/share COMMAND ${CMAKE_COMMAND} "-E" copy_directory ${CMAKE_SOURCE_DIR}/share ${CMAKE_BINARY_DIR}/share) ENDIF () # translate FIND_PROGRAM(LUPDATE lupdate HINTS "${_qt_bin_dir}") FIND_PROGRAM(LRELEASE lrelease HINTS "${_qt_bin_dir}") IF (${LUPDATE} STREQUAL "LUPDATE-NOTFOUND") FIND_PROGRAM(LUPDATE lupdate-qt6 HINTS "${_qt_bin_dir}") FIND_PROGRAM(LRELEASE lrelease-qt6 HINTS "${_qt_bin_dir}") ENDIF () FILE(GLOB cppfiles *.cc) ADD_CUSTOM_TARGET(apvlv_lupdate COMMAND ${LUPDATE} -locations none -target-language zh_CN ${cppfiles} -ts ${CMAKE_SOURCE_DIR}/zh_CN.ts) ADD_CUSTOM_TARGET(apvlv_lrelease COMMAND ${LRELEASE} ${CMAKE_SOURCE_DIR}/zh_CN.ts -qm ${CMAKE_SOURCE_DIR}/share/doc/apvlv/translations/zh_CN.qm) ADD_DEPENDENCIES(apvlv_lrelease apvlv_lupdate) ADD_DEPENDENCIES(apvlv apvlv_lrelease) INSTALL(TARGETS apvlv DESTINATION bin) IF (WIN32) ADD_CUSTOM_COMMAND(TARGET apvlv POST_BUILD COMMAND "${CMAKE_COMMAND}" -E remove_directory "${CMAKE_CURRENT_BINARY_DIR}/winqt/" COMMAND "${CMAKE_COMMAND}" -E env PATH="${_qt_bin_dir}" "${WINDEPLOYQT_EXECUTABLE}" --release --verbose 0 --no-compiler-runtime --no-opengl-sw --no-libraries --dir "${CMAKE_CURRENT_BINARY_DIR}/winqt/" $ COMMENT "Deploying Qt..." ) INSTALL(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/winqt/" DESTINATION bin) INSTALL(CODE "set(MY_DEPENDENCY_PATHS \"$ENV{PATH}\" \"${_qt_bin_dir}\" )") INSTALL(CODE [[ MESSAGE("-- get runtime dependencies ...") LIST(APPEND pre_exclude_regexes "api-ms-.*") # windows API LIST(APPEND pre_exclude_regexes "ext-ms-.*") # windows API LIST(APPEND pre_exclude_regexes "ieshims.dll") # windows API LIST(APPEND pre_exclude_regexes "emclient.dll") # windows API LIST(APPEND pre_exclude_regexes "devicelockhelpers.dll") # windows API LIST(APPEND post_exclude_regexes ".*WINDOWS[\\/]system32.*") FILE(GET_RUNTIME_DEPENDENCIES EXECUTABLES $ RESOLVED_DEPENDENCIES_VAR _r_deps UNRESOLVED_DEPENDENCIES_VAR _u_deps PRE_EXCLUDE_REGEXES ${pre_exclude_regexes} POST_EXCLUDE_REGEXES ${post_exclude_regexes} DIRECTORIES ${MY_DEPENDENCY_PATHS} ) LIST(LENGTH _u_deps _u_length) IF("${_u_length}" GREATER 0) MESSAGE("Unresolved dependencies detected in [${MY_DEPENDENCY_PATHS}]!") FOREACH(_no ${_u_deps}) MESSAGE("Can not find ${_no}") ENDFOREACH() ENDIF() FOREACH(_file ${_r_deps}) MESSAGE("install " ${_file}) FILE(INSTALL FILES "${_file}" DESTINATION "${CMAKE_INSTALL_PREFIX}/bin" ) ENDFOREACH() ]]) ENDIF (WIN32) apvlv-0.7.0-alpha/src/ApvlvSearchDialog.h0000644000175100017510000000423314726051136020556 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * * Copyright (C) 2008 Alf. * * Contact: Alf * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2.0 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ /* @CPPFILE SearchDialog.h * * Author: Alf */ #ifndef _APVLV_SEARCH_DIALOG_H_ #define _APVLV_SEARCH_DIALOG_H_ #include #include #include #include #include #include #include #include #include #include #include #include "ApvlvWebViewWidget.h" namespace apvlv { class File; class SearchDialog : public QDialog { Q_OBJECT public: explicit SearchDialog (QWidget *parent = nullptr); ~SearchDialog () override = default; signals: void loadFile (const std::string &path, int pn); private slots: void search (); void getResults (); void previewItem (QListWidgetItem *item); void activateItem (QListWidgetItem *item); void loadFinish (bool ret); private: void displayResult (std::unique_ptr result); QVBoxLayout mVBox; QHBoxLayout mHBox; QHBoxLayout mHBox2; QHBoxLayout mHBox3; QSplitter mSplitter; QLabel mLabel; QPushButton mDirButton; SearchOptions mOptions; Searcher mSearcher; QTimer mGetTimer; QLineEdit mSearchEdit; QCheckBox mCaseSensitive; QCheckBox mRegex; std::vector mTypes; QLineEdit mFromDir; QListWidget mResults; WebView mPreview; std::unique_ptr mPreviewFile; bool mPreviewIsFinished; }; } #endif apvlv-0.7.0-alpha/src/ApvlvView.h0000644000175100017510000001147314726051136017147 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * * Copyright (C) 2008 Alf. * * Contact: Alf * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2.0 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ /* @CPPFILE ApvlvView.h * * Author: Alf */ #ifndef _APVLV_VIEW_H_ #define _APVLV_VIEW_H_ #include #include #include #include #include #include #include #include #include #include #include #include "ApvlvCmds.h" #include "ApvlvCompletion.h" #include "ApvlvFrame.h" #include "ApvlvWindow.h" namespace apvlv { namespace CommandModeType { const char SEARCH = '/'; const char BACKSEARCH = '?'; const char COMMANDMODE = ':'; const char FIND = 'F'; } class ApvlvFrame; class ApvlvWindow; class ApvlvCommandBar : public QLineEdit { Q_OBJECT public: ApvlvCommandBar () { installEventFilter (this); }; protected: void keyPressEvent (QKeyEvent *evt) override; bool eventFilter (QObject *obj, QEvent *event) override; signals: void keyPressed (QKeyEvent *evt); }; class ApvlvView final : public QMainWindow { Q_OBJECT public: explicit ApvlvView (ApvlvView *view = nullptr); ~ApvlvView () override; ApvlvWindow *currentWindow (); void delCurrentWindow (); bool newTab (const std::string &filename); bool newTab (ApvlvFrame *core); void promptCommand (char ch); void promptCommand (const char *str); template void errorMessage (T... args) { std::stringstream msg; msg << "ERROR: "; msg << (... + args); mCommandBar.setText (QString::fromLocal8Bit (msg.str ())); cmdShow (CmdStatusType::CMD_MESSAGE); } static char *input (const char *str, int width = 400, int height = 150, const std::string &content = ""); bool run (const char *str); bool loadFile (const std::string &filename); bool loadDir (const std::string &path); std::optional hasLoaded (std::string_view abpath); void regLoaded (ApvlvFrame *doc); CmdReturn process (int hastimes, int times, uint keyval); CmdReturn subProcess (int times, uint keyval); void cmdShow (CmdStatusType cmdtype); void cmdHide (); void cmdAuto (const char *str); void setTitle (const std::string &title); ApvlvFrame *currentFrame (); void appendChild (ApvlvView *view); void eraseChild (ApvlvView *view); public slots: void loadFileOnPage (const std::string &filename, int pn); void open (); void openDir (); void openRrl (); void quit (bool only_tab = true); void search (); void backSearch (); void advancedSearch (); void dired (); void fullScreen (); void nextPage (); void previousPage (); void toggleDirectory (); void toggleToolBar (); void toggleStatus (); void newTab (); void closeTab (); void horizontalSplit (); void verticalSplit (); void unBirth (); private: void setupMenuBar (const std::string &guiopt); void setupToolBar (); bool runCommand (const char *cmd); void switchTab (int tabPos); // Update the tab's context and update tab label. void windowAdded (); void updateTabName (); CmdStatusType mCmdType; std::chrono::time_point mCmdTime; uint mProCmd; int mProCmdCnt; QFrame mCentral; QVBoxLayout mVBoxLayout; QTabWidget mTabContainer; ApvlvCommandBar mCommandBar; QMenuBar mMenuBar; QToolBar mToolBar; bool mHasFull; struct keyNode { ; int Has; int Ct; uint Key; bool End; }; bool keyLastEnd; bool processInLast; std::vector keySquence; void saveKey (int has, int ct, uint key, bool end); void processLastKey (); void closeEvent (QCloseEvent *evt) override; void keyPressEvent (QKeyEvent *evt) override; ApvlvCmds mCmds; std::vector> mDocs; std::vector mCmdHistroy; size_t mCurrHistroy; ApvlvView *mParent; std::vector mChildren; private slots: void commandbarEdited (const QString &str); void commandbarReturn (); void commandbarKeyPressed (QKeyEvent *gek); void tabSwitched (int ind); }; } #endif /* Local Variables: */ /* mode: c++ */ /* End: */ apvlv-0.7.0-alpha/src/ApvlvSearchDialog.cc0000644000175100017510000001312114726051136020710 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * * Copyright (C) 2008 Alf. * * Contact: Alf * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2.0 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ /* @CPPFILE SearchDialog.cc * * Author: Alf */ #include #include #include #include #include "ApvlvFile.h" #include "ApvlvSearchDialog.h" namespace apvlv { using namespace std; SearchDialog::SearchDialog (QWidget *parent) : QDialog (parent), mPreviewIsFinished (true) { setLayout (&mVBox); // search line mVBox.addLayout (&mHBox); mHBox.addWidget (&mSearchEdit, 1); QObject::connect (&mSearchEdit, SIGNAL (returnPressed ()), this, SLOT (search ())); mCaseSensitive.setText (tr ("Case sensitive")); mHBox.addWidget (&mCaseSensitive); mRegex.setText (tr ("Regular expression")); mHBox.addWidget (&mRegex); mVBox.addLayout (&mHBox2); mLabel.setText (tr ("Find Directory: ")); mHBox2.addWidget (&mLabel, 0); mHBox2.addWidget (&mFromDir, 1); QObject::connect (&mFromDir, SIGNAL (returnPressed ()), this, SLOT (search ())); mFromDir.setText (QDir::homePath ()); mDirButton.setText (tr ("...")); mDirButton.setFocusPolicy (Qt::NoFocus); mHBox2.addWidget (&mDirButton, 0); QObject::connect (&mDirButton, &QPushButton::clicked, this, [&] () { auto dir = QFileDialog::getExistingDirectory (); if (!dir.isEmpty ()) mFromDir.setText (dir); }); // file type line mVBox.addLayout (&mHBox3); auto mime_types = FileFactory::supportMimeTypes (); std::ranges::for_each (mime_types, [&] (const auto &pair) { std::ranges::for_each (pair.second, [&] (const auto &ext) { auto checkbox = new QCheckBox (QString::fromLocal8Bit (ext)); checkbox->setChecked (true); mHBox3.addWidget (checkbox); mTypes.emplace_back (checkbox); }); }); mVBox.addWidget (&mSplitter); mSplitter.setOrientation (Qt::Vertical); mSplitter.addWidget (&mResults); mSplitter.addWidget (&mPreview); mPreview.resize (400, 300); QObject::connect ( &mResults, SIGNAL (currentItemChanged (QListWidgetItem *, QListWidgetItem *)), this, SLOT (previewItem (QListWidgetItem *))); QObject::connect (&mResults, SIGNAL (itemActivated (QListWidgetItem *)), this, SLOT (activateItem (QListWidgetItem *))); QObject::connect (&mPreview, SIGNAL (loadFinished (bool)), this, SLOT (loadFinish (bool))); QObject::connect (&mGetTimer, SIGNAL (timeout ()), this, SLOT (getResults ())); mGetTimer.start (100); } void SearchDialog::search () { SearchOptions options; options.mText = mSearchEdit.text ().trimmed ().toStdString (); options.mCaseSensitive = mCaseSensitive.isChecked (); options.mRegex = mRegex.isChecked (); options.mFromDir = mFromDir.text ().toStdString (); for (const auto &type : mTypes) { auto ext = type->text ().replace ("&", ""); if (type->isChecked ()) options.mTypes.emplace_back (ext.toStdString ()); } if (mOptions == options) return; mSearcher.submit (options); mResults.clear (); mOptions = options; } void SearchDialog::getResults () { unique_ptr result; while ((result = mSearcher.get ()) != nullptr) { displayResult (std::move (result)); } } void SearchDialog::previewItem (QListWidgetItem *item) { if (item == nullptr) return; if (mPreviewIsFinished == false) return; auto words = item->data (Qt::UserRole).toStringList (); auto path = words[0].toStdString (); auto pn = words[1].toInt (); if (mPreviewFile && mPreviewFile->getFilename () != path) mPreviewFile = nullptr; if (mPreviewFile == nullptr) mPreviewFile = FileFactory::loadFile (path); if (mPreviewFile) { mPreview.setFile (mPreviewFile.get ()); mPreviewIsFinished = false; mPreviewFile->pageRenderToWebView (pn, 1.0, 0, &mPreview); } } void SearchDialog::activateItem (QListWidgetItem *item) { auto words = item->data (Qt::UserRole).toStringList (); auto path = words[0]; auto pn = words[1].toInt (); emit loadFile (path.toStdString (), pn); accept (); } void SearchDialog::displayResult (unique_ptr result) { auto line = QString::fromLocal8Bit (result->filename); for (const auto &page : result->page_matches) { auto pos = line + ':' + QString::number (page.page + 1); for_each (page.matches.begin (), page.matches.end (), [&] (const auto &match) { auto matchitem = new QListWidgetItem ( { QString::fromLocal8Bit (match.line) }); matchitem->setToolTip (pos); QStringList data{ line, QString::number (page.page) }; matchitem->setData (Qt::UserRole, data); mResults.addItem (matchitem); }); } } void SearchDialog::loadFinish ([[maybe_unused]] bool ret) { mPreviewIsFinished = true; } } apvlv-0.7.0-alpha/src/ApvlvDired.h0000644000175100017510000000263414726051136017263 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * * Copyright (C) 2008 Alf. * * Contact: Alf * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2.0 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ /* @CPPFILE ApvlvDired.h * * Author: Alf */ #ifndef _APVLV_DIRED_H_ #define _APVLV_DIRED_H_ #include #include #include #include #include #include #include namespace apvlv { class DiredDialog : public QDialog { Q_OBJECT public: explicit DiredDialog (QWidget *parent = nullptr); ~DiredDialog () override = default; signals: void loadFile (const std::string &path, int pn); private: QVBoxLayout mVboxLayout; private slots: void activateItem (QListWidgetItem *item); }; } #endif apvlv-0.7.0-alpha/src/ApvlvFrame.cc0000644000175100017510000007110214726051136017420 0ustar atzlinuxatzlinux/* * This file is part of the apvlv package * * Copyright (C) 2008 Alf. * * Contact: Alf * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2.0 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ /* @CPPFILE ApvlvFrame.cc * * Author: Alf */ #include #include #include #include #include #include #include "ApvlvFileWidget.h" #include "ApvlvFrame.h" #include "ApvlvImageWidget.h" #include "ApvlvInfo.h" #include "ApvlvParams.h" #include "ApvlvView.h" #include "ApvlvWeb.h" #include "ApvlvWebViewWidget.h" namespace apvlv { using namespace std; using namespace Qt; using namespace CommandModeType; std::vector ApvlvFrame::ZoomLabel = { QT_TR_NOOP ("Default"), QT_TR_NOOP ("Fit Width"), QT_TR_NOOP ("Fit Height"), QT_TR_NOOP ("Custom"), }; ApvlvFrame::ApvlvFrame (ApvlvView *view) : mToolStatus (this) { mInuse = true; mView = view; mProCmd = 0; mZoomMode = ZoomMode::NORMAL; mSearchResults = nullptr; mSearchStr = ""; setLayout (&mVbox); mPaned.setHandleWidth (4); mDirectoryWidth = DEFAULT_CONTENT_WIDTH; auto f_width = ApvlvParams::instance ()->getIntOrDefault ("fix_width"); auto f_height = ApvlvParams::instance ()->getIntOrDefault ("fix_height"); if (f_width > 0 && f_height > 0) { mPaned.setFixedSize (f_width, f_height); mVbox.addLayout (&mHBoxLayout, 1); mHBoxLayout.addWidget (&mPaned, 0); } else { mVbox.addWidget (&mPaned, 1); } mDirectory.setFrame (this); QObject::connect (this, SIGNAL (indexGenerited (const FileIndex &)), &mDirectory, SLOT (setIndex (const FileIndex &))); // left is Directory mPaned.addWidget (&mDirectory); // Right is Text mPaned.addWidget (&mTextFrame); mTextFrame.setLayout (&mTextLayout); mTextLayout.addWidget (&mToolStatus, 0); auto guiopt = ApvlvParams::instance ()->getStringOrDefault ("guioptions"); if (guiopt.find ('S') == string::npos) { mToolStatus.hide (); } mVbox.addWidget (&mStatus, 0); if (guiopt.find ('s') == string::npos) { mStatus.hide (); } qDebug () << "ApvlvFrame: " << this << " be created"; } ApvlvFrame::~ApvlvFrame () { qDebug () << "ApvlvFrame: " << this << " be freed"; saveLastPosition (mFilestr); } void ApvlvFrame::inuse (bool use) { mInuse = use; } bool ApvlvFrame::inuse () { return mInuse; } bool ApvlvFrame::loadUri (const string &uri) { mFile = make_unique (); mFile->load (uri); setWidget (mFile->getDisplayType ()); refresh (0, 0.0); return true; } const char * ApvlvFrame::filename () { return mFilestr.empty () ? nullptr : mFilestr.c_str (); } bool ApvlvFrame::print ([[maybe_unused]] int ct) { return false; } int ApvlvFrame::getSkip () { return mSkip; } void ApvlvFrame::setSkip (int ct) { mSkip = ct; } void ApvlvFrame::toggleDirectory () { auto show = !isShowDirectory (); toggleDirectory (show); } void ApvlvFrame::toggleDirectory (bool show) { if (show) { if (!mDirectory.isReady ()) { qWarning () << "file " << mFilestr << " has no directory"; show = false; } } auto sizes = mPaned.sizes (); if (show) { mDirIndex = {}; if (sizes[0] == 0) { auto psize = mPaned.size (); sizes = { mDirectoryWidth, psize.width () - mPaned.handleWidth () - DEFAULT_CONTENT_WIDTH }; mPaned.setSizes (sizes); } } else { if (sizes[0] != 0) mDirectoryWidth = sizes[0]; auto psize = mPaned.size (); sizes = { 0, psize.width () - mPaned.handleWidth () }; mPaned.setSizes (sizes); } } void ApvlvFrame::setActive (bool act) { mActive = act; auto wid = mWidget->widget (); if (act) { QTimer::singleShot (50, wid, SLOT (setFocus ())); } else { wid->clearFocus (); clearFocus (); } if (mActive && filename ()) { auto path = filesystem::path (filename ()); auto base = path.filename (); mView->setTitle (base.string ()); } mStatus.setActive (act); } void ApvlvFrame::setDirIndex (const string &path) { mDirIndex = { "", 0, path, FileIndexType::DIR }; mDirIndex.loadDirectory (path); emit indexGenerited (mDirIndex); toggleDirectory (true); } bool ApvlvFrame::toggledControlDirectory (bool is_right) { if (!isShowDirectory ()) { return false; } if (auto controlled = isControlledDirectory (); !controlled && !is_right) { mDirectory.setActive (true); return true; } else if (controlled && is_right) { mDirectory.setActive (false); return true; } return false; } bool ApvlvFrame::isShowDirectory () { auto sizes = mPaned.sizes (); return sizes[0] > 1; } bool ApvlvFrame::isControlledDirectory () { if (!isShowDirectory ()) return false; return mDirectory.isActive (); } ApvlvFrame * ApvlvFrame::findByWidget (QWidget *widget) { for (auto doc = widget; doc != nullptr; doc = doc->parentWidget ()) { if (doc->inherits ("apvlv::ApvlvFrame")) return dynamic_cast (doc); } return nullptr; } ApvlvStatus::ApvlvStatus () { setFrameShape (QFrame::NoFrame); setLayout (&mLayout); } void ApvlvStatus::setActive (bool act) { auto children = findChildren (); for (auto child : children) { if (child) { child->setEnabled (act); } } } void ApvlvStatus::showMessages (const vector &msgs) { auto children = findChildren (); vector newlabels; for (std::size_t ind = 0; ind < msgs.size (); ++ind) { if (children.size () > (qsizetype)ind) { auto label = children[ind]; label->setText (QString::fromLocal8Bit (msgs[ind])); } else { auto label = new QLabel (); label->setText (QString::fromLocal8Bit (msgs[ind])); newlabels.push_back (label); } } auto hbox = layout (); for (auto label : newlabels) { hbox->addWidget (label); } } ApvlvToolStatus::ApvlvToolStatus (ApvlvFrame *frame) : mFrame (frame) { auto paction = addAction (QIcon::fromTheme (QIcon::ThemeIcon::GoPrevious), tr ("Previous Page")); QObject::connect (paction, SIGNAL (triggered (bool)), mFrame, SLOT (previousPage ())); auto naction = addAction (QIcon::fromTheme (QIcon::ThemeIcon::GoNext), tr ("Next Page")); QObject::connect (naction, SIGNAL (triggered (bool)), mFrame, SLOT (nextPage ())); mPageValue.setSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed); QObject::connect (&mPageValue, SIGNAL (editingFinished ()), this, SLOT (gotoPage ())); addWidget (&mPageValue); addWidget (&mPageSum); addSeparator (); addWidget (&mScrollRate); addSeparator (); auto iaction = addAction (QIcon::fromTheme (QIcon::ThemeIcon::ZoomIn), tr ("Zoom In")); QObject::connect (iaction, SIGNAL (triggered (bool)), mFrame, SLOT (zoomIn ())); auto oaction = addAction (QIcon::fromTheme (QIcon::ThemeIcon::ZoomOut), tr ("Zoom Out")); QObject::connect (oaction, SIGNAL (triggered (bool)), mFrame, SLOT (zoomOut ())); addWidget (&mZoomType); for (auto const &str : ApvlvFrame::ZoomLabel) { mZoomType.addItem (ApvlvFrame::tr (str)); } QObject::connect (&mZoomType, SIGNAL (currentIndexChanged (int)), mFrame, SLOT (setZoomMode (int))); mZoomType.setLineEdit (&mZoomValue); addSeparator (); #ifdef APVLV_WITH_OCR addWidget (&mOcrParse); mOcrParse.setText (tr ("OCR Parse")); QObject::connect (&mOcrParse, SIGNAL (checkStateChanged (Qt::CheckState)), mFrame, SLOT (ocrParse ())); addAction (&mOcrCopy); mOcrCopy.setIcon (QIcon::fromTheme (QIcon::ThemeIcon::Scanner)); mOcrCopy.setText (tr ("OCR Copy")); QObject::connect (&mOcrCopy, SIGNAL (triggered (bool)), mFrame, SLOT (ocrCopy ())); #endif } void ApvlvToolStatus::updateValue (int pn, int totpn, double zm, double sr) { mPageValue.setText (QString::number (pn)); mPageSum.setText (QString::fromLocal8Bit ("/%1").arg (totpn)); if (mZoomType.currentIndex () == 3) { mZoomValue.setText ( QString::fromLocal8Bit ("%1%").arg (static_cast (zm * 100))); } mScrollRate.setText ( QString::fromLocal8Bit ("%1%").arg (static_cast (sr * 100))); #ifdef APVLV_WITH_OCR auto need_ocr = mFrame->mFile->pageIsOnlyImage (mFrame->pageNumber ()); mOcrParse.setEnabled (need_ocr); mOcrCopy.setEnabled (need_ocr); #endif } void ApvlvToolStatus::gotoPage () { auto text = mPageValue.text ().trimmed (); auto pn = text.toInt (); if (pn != mFrame->pageNumber ()) { mFrame->showPage (pn, 0.f); } } CmdReturn ApvlvFrame::subProcess ([[maybe_unused]] int ct, uint key) { uint procmd = mProCmd; mProCmd = 0; switch (procmd) { case 'm': markposition (char (key)); break; case '\'': jump (char (key)); break; case 'z': if (key == 'i') { zoomIn (); } else if (key == 'o') { zoomOut (); } else if (key == 'h') { setZoomMode (static_cast (ZoomMode::FITHEIGHT)); } else if (key == 'w') { setZoomMode (static_cast (ZoomMode::FITWIDTH)); } break; default: return CmdReturn::NO_MATCH; break; } return CmdReturn::MATCH; } void ApvlvFrame::previousPage () { previousPage (1); } void ApvlvFrame::nextPage () { nextPage (1); } void ApvlvFrame::setZoomMode (int mode) { if (mode < static_cast (ZoomMode::CUSTOM)) { switch (static_cast (mode)) { using enum ZoomMode; case NORMAL: setZoomString ("normal"); break; case FITWIDTH: setZoomString ("fitwidth"); break; case FITHEIGHT: setZoomString ("fitheight"); break; case CUSTOM: break; } } updateStatus (); } void ApvlvFrame::zoomIn () { auto zoomrate = mWidget->zoomrate (); setZoomrate (zoomrate * 1.1); updateStatus (); } void ApvlvFrame::zoomOut () { auto zoomrate = mWidget->zoomrate (); setZoomrate (zoomrate / 1.1); updateStatus (); } #ifdef APVLV_WITH_OCR void ApvlvFrame::ocrParse () { auto meta = mWidget->widget ()->metaObject (); qDebug () << "widget is " << meta->className (); if (!mWidget->widget ()->inherits ("apvlv::ApvlvImage")) return; auto image = dynamic_cast (mWidget->widget ()); auto state = mToolStatus.mOcrParse.checkState (); image->ocrDisplay (state == Qt::Checked); } void ApvlvFrame::ocrCopy () { auto meta = mWidget->widget ()->metaObject (); qDebug () << "widget is " << meta->className (); if (!mWidget->widget ()->inherits ("apvlv::ApvlvImage")) return; auto image = dynamic_cast (mWidget->widget ()); auto text = image->ocrGetText (); #ifdef QT_DEBUG QMessageBox::information (this, tr ("text in clipboard"), QString::fromLocal8Bit (text.get ())); #endif auto clipboard = QGuiApplication::clipboard (); clipboard->setText (text.get ()); } #endif void ApvlvFrame::wheelEvent (QWheelEvent *event) { auto angel = event->angleDelta (); if (angel.y () > 0) { mWidget->scrollUp (1); updateStatus (); } else { mWidget->scrollDown (1); updateStatus (); } } CmdReturn ApvlvFrame::process (int has, int ct, uint key) { emit focusIn (); if (mProCmd != 0) { return subProcess (ct, key); } if (!has) { ct = 1; } switch (key) { case Key_PageDown: case ctrlValue ('f'): nextPage (ct); break; case Key_PageUp: case ctrlValue ('b'): previousPage (ct); break; case ctrlValue ('d'): halfNextPage (ct); break; case ctrlValue ('u'): halfPreviousPage (ct); break; case ':': case '/': case '?': case 'F': if (isControlledDirectory ()) { mDirectory.focusFilter (); } else { mView->promptCommand (char (key)); return CmdReturn::NEED_MORE; } case 'H': mWidget->scrollTo (0.0, 0.0); break; case 'M': mWidget->scrollTo (0.0, 0.5); break; case 'L': mWidget->scrollTo (0.0, 1.0); break; case '0': mWidget->scrollLeft (INT_MAX); break; case '$': mWidget->scrollRight (INT_MAX); break; case ctrlValue ('p'): case Key_Up: case 'k': if (isControlledDirectory ()) { mDirectory.scrollUp (ct); } else { mWidget->scrollUp (ct); updateStatus (); } break; case ctrlValue ('n'): case ctrlValue ('j'): case Key_Down: case 'j': if (isControlledDirectory ()) { mDirectory.scrollDown (ct); } else { mWidget->scrollDown (ct); updateStatus (); } break; case Key_Backspace: case Key_Left: case ctrlValue ('h'): case 'h': if (isControlledDirectory ()) { mDirectory.scrollLeft (ct); } else { mWidget->scrollLeft (ct); updateStatus (); } break; case Key_Space: case Key_Right: case ctrlValue ('l'): case 'l': if (isControlledDirectory ()) { mDirectory.scrollRight (ct); } else { mWidget->scrollRight (ct); updateStatus (); } break; case Key_Return: directoryShowPage (mDirectory.currentItemFileIndex (), false); break; case 'R': reload (); break; case ctrlValue (']'): gotoLink (ct); break; case ctrlValue ('t'): returnLink (ct); break; case 't': mView->newTab (HelpPdf); mView->open (); break; case 'T': mView->newTab (HelpPdf); mView->openDir (); break; case 'o': mView->open (); break; case 'O': mView->openDir (); break; case 'r': rotate (ct); break; case 'G': markposition ('\''); if (!has) { showPage (mFile->sum () - 1, 0.0); } else { showPage (ct - 1, 0.0); } break; case 'm': case '\'': case 'z': mProCmd = key; return CmdReturn::NEED_MORE; break; case 'n': if (mSearchCmd == SEARCH) { markposition ('\''); search ("", false); } else if (mSearchCmd == BACKSEARCH) { markposition ('\''); search ("", true); } break; case 'N': if (mSearchCmd == SEARCH) { markposition ('\''); search ("", true); } else if (mSearchCmd == BACKSEARCH) { markposition ('\''); search ("", false); } break; case 's': setSkip (ct); break; case 'c': toggleDirectory (); break; default: return CmdReturn::NO_MATCH; break; } return CmdReturn::MATCH; } ApvlvFrame * ApvlvFrame::clone () { auto *ndoc = new ApvlvFrame (mView); ndoc->loadFile (mFilestr, false, false); ndoc->showPage (mWidget->pageNumber (), mWidget->scrollRate ()); return ndoc; } void ApvlvFrame::setZoomrate (double zm) { mZoomMode = ZoomMode::CUSTOM; mWidget->setZoomrate (zm); } void ApvlvFrame::setZoomString (const char *z) { auto zoomrate = mWidget->zoomrate (); if (z != nullptr) { string_view sv (z); if (sv == "normal") { mZoomMode = ZoomMode::NORMAL; zoomrate = 1.2; } else if (sv == "fitwidth") { mZoomMode = ZoomMode::FITWIDTH; } else if (sv == "fitheight") { mZoomMode = ZoomMode::FITHEIGHT; } else { double d = strtod (z, nullptr); if (d > 0) { mZoomMode = ZoomMode::CUSTOM; zoomrate = d; } } } if (mFile) { int pn = std::max (0, pageNumber () - 1); auto size = mFile->pageSizeF (pn, 0); if (size.width > 0 && size.height > 0) { auto wid = mWidget->widget (); if (mZoomMode == ZoomMode::FITWIDTH) { auto x_root = wid->width (); zoomrate = x_root / size.width; } else if (mZoomMode == ZoomMode::FITHEIGHT) { auto y_root = wid->height (); zoomrate = y_root / size.height; } } mWidget->setZoomrate (zoomrate); } } bool ApvlvFrame::saveLastPosition (const string &filename) { if (filename.empty () || HelpPdf == filename || ApvlvParams::instance ()->getBoolOrDefault ("noinfo", false)) { return false; } bool ret = ApvlvInfo::instance ()->updateFile ( mWidget->pageNumber (), mSkip, mWidget->scrollRate (), filename); return ret; } bool ApvlvFrame::loadLastPosition (const string &filename) { if (filename.empty () || HelpPdf == filename || ApvlvParams::instance ()->getBoolOrDefault ("noinfo")) { showPage (0, 0.0); return false; } bool ret = false; auto optfp = ApvlvInfo::instance ()->file (filename); if (optfp) { // correctly check showPage (optfp.value ()->page, 0.0); setSkip (optfp.value ()->skip); } else { showPage (0, 0.0); ApvlvInfo::instance ()->updateFile (0, 0.0, mWidget->zoomrate (), filename); } return ret; } bool ApvlvFrame::reload () { return loadFile (mFilestr, false, isShowDirectory ()); } int ApvlvFrame::pageNumber () { return mWidget ? mWidget->pageNumber () : 0; } bool ApvlvFrame::loadFile (const std::string &file, bool check, bool show_directory) { if (check && file == mFilestr) { return false; } saveLastPosition (mFilestr); mFile = FileFactory::loadFile (file); if (mFile) { emit indexGenerited (mFile->getIndex ()); mFilestr = file; if (mFile->sum () <= 1) { qDebug () << "sum () = " << mFile->sum (); } setWidget (mFile->getDisplayType ()); loadLastPosition (file); setActive (true); mSearchStr = ""; mSearchResults = nullptr; if (ApvlvParams::instance ()->getIntOrDefault ("autoreload") > 0) { mWatcher = make_unique (); QObject::connect (mWatcher.get (), SIGNAL (fileChanged ()), this, SLOT (changed_cb ())); auto systempath = filesystem::path (file); if (filesystem::is_symlink (systempath)) { auto realname = filesystem::read_symlink (systempath).string (); if (filesystem::is_regular_file (realname)) { mWatcher->addPath (QString::fromLocal8Bit (realname)); } } else { mWatcher->addPath (QString::fromLocal8Bit (file)); } } } if (show_directory && mFile != nullptr) { toggleDirectory (true); } else { toggleDirectory (false); } return mFile != nullptr; } void ApvlvFrame::markposition (const char s) { ApvlvDocPosition adp = { mWidget->pageNumber (), mWidget->scrollRate () }; mPositions[s] = adp; } void ApvlvFrame::jump (const char s) { auto it = mPositions.find (s); if (it != mPositions.end ()) { ApvlvDocPosition adp = it->second; markposition ('\''); showPage (adp.pagenum, adp.scrollrate); } } void ApvlvFrame::showPage (int pn, double s) { auto rp = mFile->pageNumberWrap (pn); if (rp < 0) return; mWidget->setAnchor (""); if (!mZoominit) { mZoominit = true; setZoomString (nullptr); } refresh (rp, s); } void ApvlvFrame::showPage (int pn, const std::string &anchor) { auto rp = mFile->pageNumberWrap (pn); if (rp < 0) return; if (!mZoominit) { mZoominit = true; setZoomString (nullptr); } mWidget->showPage (rp, anchor); updateStatus (); } void ApvlvFrame::nextPage (int times) { showPage (mWidget->pageNumber () + times, 0.0f); } void ApvlvFrame::previousPage (int times) { showPage (mWidget->pageNumber () - times, 0.0f); } void ApvlvFrame::refresh (int pn, double s) { if (mFile == nullptr) return; mWidget->showPage (pn, s); updateStatus (); } void ApvlvFrame::halfNextPage (int times) { double sr = mWidget->scrollRate (); int rtimes = times / 2; if (times % 2 != 0) { if (sr > 0.5) { sr = 0; rtimes += 1; } else { sr = 1; } } showPage (mWidget->pageNumber () + rtimes, sr); } void ApvlvFrame::halfPreviousPage (int times) { double sr = mWidget->scrollRate (); int rtimes = times / 2; if (times % 2 != 0) { if (sr < 0.5) { sr = 1; rtimes += 1; } else { sr = 0; } } showPage (mWidget->pageNumber () - rtimes, sr); } bool ApvlvFrame::needSearch (const std::string &str, bool reverse) { if (mFile == nullptr) return false; // search a different string if (!str.empty () && str != mSearchStr) { qDebug () << "different string."; mSearchStr = str; return true; } else if (mSearchResults == nullptr) { qDebug () << "no result."; return true; } // same string, but need to search next page else if ((!reverse && mWidget->searchSelect () == (int)mSearchResults->size () - 1) || (reverse && mWidget->searchSelect () == 0)) { qDebug () << "same, but need next string: s: " << reverse << ", sel: " << mWidget->searchSelect () << ", max: " << mSearchResults->size (); return true; } // same string, not need search, but has zoomed else { qDebug () << "same, not need next string. sel: " << mWidget->searchSelect () << ", max: " << mSearchResults->size (); if (!reverse) { setHighlightAndIndex (*mSearchResults, mWidget->searchSelect () + 1); } else { setHighlightAndIndex (*mSearchResults, mWidget->searchSelect () - 1); } return false; } return false; } bool ApvlvFrame::search (const char *str, bool reverse) { if (*str == '\0' && mSearchStr.empty ()) { return false; } if (*str) { mSearchCmd = (reverse ? CommandModeType::BACKSEARCH : CommandModeType::SEARCH); } if (!needSearch (str, reverse)) { return true; } mSearchResults = nullptr; unsetHighlight (); auto wrap = ApvlvParams::instance ()->getBoolOrDefault ("wrapscan"); auto i = mWidget->pageNumber (); auto sum = mFile->sum (); auto from = i; bool search = false; while (true) { if (*str != 0 || search) { mSearchResults = mFile->pageSearch ((i + sum) % sum, mSearchStr.c_str ()); if (mSearchResults != nullptr && !mSearchResults->empty ()) { if (i != mWidget->pageNumber ()) showPage (i, 0.0); auto results = *mSearchResults; auto sel = 0; if (reverse) sel = static_cast (results.size () - 1); setHighlightAndIndex (results, sel); return true; } } search = true; if (!reverse && i < (wrap ? (from + sum) : (sum - 1))) { i++; } else if (reverse && i > (wrap ? (from - sum) : 0)) { i--; } else { mView->errorMessage (string ("can't find word: "), mSearchStr); return false; } } } bool ApvlvFrame::totext (const char *file) { if (mFile == nullptr) return false; auto pn = mWidget->pageNumber (); string txt; auto size = mFile->pageSizeF (pn, 0); bool ret = mFile->pageText (pn, { 0, 0, size.width, size.height }, txt); if (ret) { fstream fs{ filename (), ios::out }; if (fs.is_open ()) { fs.write (txt.c_str (), txt.length ()); fs.close (); return true; } } return false; } bool ApvlvFrame::rotate (int ct) { // just hack if (ct == 1) ct = 90; if (ct % 90 != 0) { mView->errorMessage ("Not a 90 times value: ", ct); return false; } auto rotate = mWidget->rotate (); rotate += ct; while (rotate < 0) { rotate += 360; } while (rotate > 360) { rotate -= 360; } mWidget->setRotate (rotate); refresh (mWidget->pageNumber (), 0.0); return true; } void ApvlvFrame::gotoLink ([[maybe_unused]] int ct) { // need impl } void ApvlvFrame::returnLink ([[maybe_unused]] int ct) { // need impl } void ApvlvFrame::directoryShowPage (const FileIndex *index, bool force) { if (index == nullptr) return; if (index->type == FileIndexType::FILE) { loadFile (index->path, true, true); return; } auto file = mDirectory.currentFileFileIndex (); if (file && file->path != mFilestr) loadFile (file->path, true, true); if (index->type == FileIndexType::PAGE) { if (index->page != mWidget->pageNumber () || index->anchor != mWidget->anchor ()) showPage (index->page, index->anchor); } } void ApvlvFrame::setWidget (DISPLAY_TYPE type) { auto sizes = mPaned.sizes (); if (type == DISPLAY_TYPE::IMAGE) { mWidget = make_unique (); mWidget->setFile (mFile.get ()); #ifdef APVLV_WITH_OCR ocrParse (); #endif } else if (type == DISPLAY_TYPE::HTML) { mWidget = make_unique (); mWidget->setFile (mFile.get ()); } else { mWidget.reset (mFile->getWidget ()); } mTextLayout.addWidget (mWidget->widget (), 1); mPaned.setSizes (sizes); } void ApvlvFrame::unsetHighlight () { mWidget->setSearchStr (""); mWidget->setSearchSelect (0); mWidget->setSearchResults ({}); } void ApvlvFrame::setHighlightAndIndex (const WordListRectangle &poses, int sel) { if (!poses[sel].word.empty ()) { mWidget->setSearchStr (poses[sel].word); } mWidget->setSearchSelect (sel); mWidget->setSearchResults (poses); auto sr = mWidget->scrollRate (); if (!poses[sel].rect_list.empty ()) { auto rect = poses[sel].rect_list[0]; auto size = mFile->pageSizeF (mWidget->pageNumber (), mWidget->rotate ()); if (size.height > 0) { sr = rect.p2y / size.height; } } refresh (mWidget->pageNumber (), sr); } void ApvlvFrame::updateStatus () { if (filename ()) { vector labels; int pn = pageNumber () + 1; int totpn = mFile->sum (); auto zm = mWidget->zoomrate (); auto sr = mWidget->scrollRate (); string anchor = mWidget->anchor (); auto systempath = filesystem::path (filename ()); auto bn = systempath.filename (); labels.emplace_back (bn.string ()); auto ss = QString ("%1/%2").arg (pn).arg (totpn); labels.emplace_back (ss.toStdString ()); ss = QString ("%1%").arg (static_cast (zm * 100)); labels.emplace_back (ss.toStdString ()); ss = QString ("%1%").arg (static_cast (sr * 100)); labels.emplace_back (ss.toStdString ()); mStatus.showMessages (labels); mToolStatus.updateValue (pn, totpn, zm, sr); mDirectory.setCurrentIndex (mFilestr, mWidget->pageNumber (), mWidget->anchor ()); } } bool ApvlvFrame::isStatusHidden () { return mStatus.isHidden (); } void ApvlvFrame::statusShow () { mStatus.show (); } void ApvlvFrame::statusHide () { mStatus.hide (); } } // Local Variables: // mode: c++ // End: apvlv-0.7.0-alpha/COPYING0000644000175100017510000004310314726051136015312 0ustar atzlinuxatzlinux GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. apvlv-0.7.0-alpha/apvlv.10000644000175100017510000001201514726051136015467 0ustar atzlinuxatzlinux.Dd December 24, 2009 .Dt apvlv 1 .Os .Sh NAME .Nm apvlv .Nd PDF/DJVU/EPUB/HTML/TXT/FB2/CZW viewer with vim-like behaviour .Sh SYNOPSIS .Nm .Op options .Op file .Sh DESCRIPTION apvlv is a PDF/DJVU/EPUB/HTML/TXT/FB2/CZW viewer, which behaves like vim. .Sh OPTIONS .Bl -tag -width "v" .It Fl c Ar file Load configuration from .Ar file instead of the default .Pa ~/.apvlvrc .It Fl h Show help message and exit .It Fl v Show version and exit .El .Sh COMMANDS The following command keys can be used inside apvlv. Some of them may be prefixed by a number (as in pressing "13G"), this is indicated by a .Ar count in their description. Unless noted otherwise, the default value for the number is 1. .Bl -tag -width "indent" .It o Display file chooser to open a PDF/DJVU/EPUB/HTML/TXT file .It O Select a directory to display .It t Display file chooser to open a PDF/DJVU/EPUB/HTML/TXT file in a new tab .It T Select a directory to display in a new tab .It R Reload the current file .It r Rotate the document clockwise by 90 degrees .It G Show page number .Ar count .It gt Show next tab .It gT Show previous tab .It PageDown, C-f Go forward .Ar count pages .It PageUp, C-b Go backward .Ar count pages .It C-d Go forward .Ar count half pages .It C-u Go backward .Ar count half pages .It H Scroll to page head .It M Scroll to page middle .It L Scroll to page bottom .It s skip some pages .Ar count .It C-p, Up, k Scroll up .Ar count units .It C-n, Down, j Scroll down .Ar count units .It Backspace, Left, h Scroll left .Ar count units .It Space, Right, l Scroll right .Ar count units .It / Ar string Search forwards for .Ar string .It ? Ar string Search backwards for .Ar string .It f Toggle between fullscreen and window mode .It zi Zoom in .It zo Zoom out .It zw Zoom to fit window width .It zh Zoom to fit window height .It m Ar char Mark the current position to .Ar char , so that it can be recalled by pressing .Ar char .It ' Ar char Return to the mark position .Ar char .It '' Return to the last position .It q Close the current window .It ZZ quit the viewer with ZZ, like Vim .It c toggle directory display .El .Sh SETTINGS These can be set in ~/.apvlvrc with .Qq set Ar setting Op = Ar value . .Bl -tag -width "indent" .It fullscreen = yes/no Enable/Disable fullscreen .It width = Ar int Default window width .It height = Ar int Default window height .It fix_width = Ar int fixed width .It fix_height = Ar int fixed height .It defaultdir = Ar path Default directory for the open dialogue .It zoom = Ar mode Set default zoom level .Bl -tag -width "indent" .It normal The application sets the default zoom value .It fitwidth Fit pages to window width .It fitheight Fit pages to window height .It Ar float 1.0 for 100%, 2.0 for 200%, etc. .El .It continuous = yes/no Show PDF/DJVU/EPUB/HTML/TXT pages continuously or not. .It autoscrollpage = yes/no Enable/Disable scrolling the pages when hitting a page tail/head .It noinfo = yes/no Disable/Enable the usage of ~/.apvlvinfo .It max_info = Ar int Max file information will be saved, default is 100 .It scrollbar = yes/no Set show scrollbar or not .It wrapscan = yes/no Set wrapscan to search text or not .It doubleclick = Ar action Set default double click action .Bl -tag -width "indent" .It none Selection nothing .It word Selection a word under the cursor to clipboard .It line Selection a line under the cursor to clipboard .It page Selection a page under the cursor to clipboard .El .It guioptions = mTsS Weather display menu, toolbar, status and status tool. .It .pdf:engine = MuPDF/Poppler/QtPdf Which engine to render .pdf file .It .epub:engine = MuPDF/Web Which engine to render .epub file .It .fb2:engine = MuPDF/Web Which engine to render .fb2 file .It ocr:lang = eng+chi_sim Pretrained languages which tesseract load to process .It notes:dir = Ar dir Directory to save ebook notes .It autoreload = Ar int If auto reload document after some seconds .El .Bl -tag -width "indent" .It inverted = yes/no If use inverted mode for pdf page .It background = Ar color Set background color .El .Sh PROMPT Like the COMMANDS, but prefixed with a colon: .Bl -tag -width "indent" .It :h[elp] Display the help document .It :q[uit] Close the current window .It :o[pen] Ar file Open .Ar file .It :doc Ar file Load .Ar file into the current window .It :TOtext Op Ar file Translate .Ar file (or the current page) to a text file .It :pr[int] Print the current document .It :tabnew Create a new tab .It :sp Horizontally split the current window .It :vsp Vertically split the current window .It :fp, :forwardpage Op Ar int Go forward .Ar int pages (1 by default) .It :bp, :prewardpage Op Ar int Go backward .Ar int pages (1 by default) .It :g, :goto Ar int Go to page .Ar int .It :z[oom] Ar mode Set zoom to .Ar mode (see "set zoom" in SETTINGS) .It : Ns Ar int Go to page .Ar int .It :directory toggle directory display .El .Sh AUTHORS apvlv was written by Alf . .Pp This manual page was originally written by Stefan Ritter for the Debian project (but may be used by others), and was rewritten more beautifully by Daniel Friesel . apvlv-0.7.0-alpha/NEWS0000644000175100017510000001526214726051136014763 0ustar atzlinuxatzlinuxapvlv 0.5.0 ========== apvlv 0.4.0 ========== Add a 'F' command for forwarding to a word in page when enable visual mode. use mouse motion to select text, which support Ctrl mask. Use mouse select and popup menu to annotate text, underline or comment. When using shortcut 'v' or 'C-v' to selected content, shortcut 'A' to annotate text, shortcut 'U' to underline and shortcut 'C' to comment. apvlv 0.3.0 =========== inner content toc display shortcut 'c' or command ':content' to toggle content shortcut '.' to repeat last action add background option auto set background=black when set inverted=yes add 'content_follow_mode' option for control follow mode of content view apvlv 0.2.0 =========== Add EPUB format support apvlv 0.1.4 =========== Add TXT format support apvlv 0.1.3 =========== Add HTML format support apvlv 0.1.2 =========== Just swapped source from svn to git apvlv 0.1.1 =========== News features * add '[int]s' to support skip some pages for some document * sorted in dir mode by filename * using 't' to open file in new tab window * using 'T' to open directory in new tab window apvlv 0.1.0 =========== News features * UMD file support * using CMake for auto build tool * add rpm and deb installation package =========== apvlv 0.0.9 News features * More like vim's info file scheme * Add `guioptions = "mT"` to support menu and tool bar * Add command bar response when command is failed or command is not valid * Add double click action on pdf pages for select a word, a line or a page of text * Mouse Copy Support: add left drag to select area, and right click to popup menu to copy to clipboard * Key Copy Support: add select area by pressing 'v' or and copy area by press 'y' * add 'wrapscan' options in $HOME/.apvlvrc * add search support of dir and content view * add Startup.tex to source tar * use \ to support space in file name when :o[pen] or :doc * use 'G' to go to the last page * keep search direction when search with 'n' or 'N' after / or ? * add autoreload parameter to support auto reload document or directory view * add reverted pdf page feature Bug Fix * display '*' when input password to open pdf docs * return false when search next but no first string was be searched. * return true when sub child dir has pdf files * fix a segfault when yank text * fix a memory leak when destroy a apvlv doc * fix the correct select mark when scroll up or down the page * fix the k or j 's length in continuous mode apvlv 0.0.8 =========== News features * add DJVU format document viewing support * add poppler-data support in Windows version * add :w[rite] filename to save document * add zw to fitwidth and zh to fitheight * made the scrollbar as a option in $HOME/.apvlvrc Bug Fix * If a pdf file is not encrypted, not ask password when can't open it * highlight correctly after zoom in or zoom out * :z[oom] fitwidth or :z[oom] fitheight works apvlv 0.0.7 =========== Changes for WARNING * apvlv don't support negative integer now Because the '-' cause many bugs in in - to smaller a window, apvlv don't support negative integer from 0.0.7.4. News features * add shell command line options, -h, -v and -c * add options to disable ~/.apvlvinfo * add a option "set autoscrolldoc=yes/no" to toggle auto scroll a doc from end to 1st * add a option "set pdfcache=[number]" to set the cache size of pdf object * set pdfcache >= 2 limit for apvlv works correctly * set no autoscroll and not continuous when pdf is a single page * add global /etc/apvlvrc * don't create ~/.apvlvrc automatically * made fullscreen really works under some WM * add key 'n' and 'N' to search and back search Bug Fix * fix file is not exist segment fault * fix the command history segment fault * add document of / and ? key which appear from 0.0.3 * fix lots of spelling errors in Startup.pdf apvlv 0.0.6 ============ News features * Warning !!! change 'goto' command's 'g' to 'G' * add continuous view of pdf page set in .apvlvrc "set continuous=yes/no" * add argument to support weather auto scroll page when k,j to page's end or head set in .apvlvrc "set autoscrollpage=yes/no" * add Up and Down key to get previous and next command in Command Prompt * add tab view add :tabnew and 'gt' 'gg' 'gT' 'g[n]' command to support tab switch, change goto page command from 'g' to 'G' * add content view * k, j to select up, down * h, l to expand, collapse * open selected page * 'o' open selected page in split window * 't' open selected page in new tab * add directory view by command 'O' * k, j to select up, down * h, l to expand, collapse * open selected file * 'o' open selected file in split window * 't' open selected file in new tab * add goto a hyperlink and to come back * add open encrypted pdf with password support * add open last directory * add mouse wheel scrollup and down support * add a padding argument to continuous page * add :number to goto page Bug fix * fix the windows open bug * fix the keys can't work on 64bit platform * fix some segment fault * change the width, height of apvlv can resize apvlv 0.0.5 ============ News features * add cache module to make the display faster. * add ':set [no]cache' to tell apvlv if use cache module. apvlv 0.0.4 ============= News features * remove the status bar from the bottom, and add it to every window. * make the active window highlight the status bar. * add 'r' command to rotate page. * add ':TOtext' to translate pdf doc page to a text file. * add ':pr[int]' to print the document. * add ':open file' to open a PDF document. * add ':doc file' to set the doc to current window. * support open multiple PDF files once, and load them in cache. can be set by ':doc file'. Bug Fix * fix the absolutepath () bug. apvlv 0.0.3 ============== News features * Replace word which like 'C-a' to like '' at every necessary place. * Support 'H', 'M' and 'L' to scroll one PDF page. * Support multiple window view and support below command to control them. 0. ':sp', ':vsp', 'q', 'quit' 1. ' ', ' q 2. ' k' 3. ' j' 4. ' h' 5. ' l' 6. ' ' 7. ' -' 8. ' +' * Support multiple PDF Doc to be opened at the memory. * Support map command mode like 'map a :vsp' apvlv-0.7.0-alpha/.clang-format0000644000175100017510000000002114726051136016622 0ustar atzlinuxatzlinuxBasedOnStyle: GNUapvlv-0.7.0-alpha/AUTHORS0000644000175100017510000000003014726051136015317 0ustar atzlinuxatzlinuxAlf apvlv-0.7.0-alpha/TODO0000644000175100017510000000002014726051136014736 0ustar atzlinuxatzlinux. Bug fix. apvlv-0.7.0-alpha/share/0000755000175100017510000000000014726051136015360 5ustar atzlinuxatzlinuxapvlv-0.7.0-alpha/share/doc/0000755000175100017510000000000014726051136016125 5ustar atzlinuxatzlinuxapvlv-0.7.0-alpha/share/doc/apvlv/0000755000175100017510000000000014726051136017255 5ustar atzlinuxatzlinuxapvlv-0.7.0-alpha/share/doc/apvlv/icons/0000755000175100017510000000000014726051136020370 5ustar atzlinuxatzlinuxapvlv-0.7.0-alpha/share/doc/apvlv/icons/dir.png0000644000175100017510000000377414726051136021667 0ustar atzlinuxatzlinuxPNG  IHDR00WbKGD{ḷIDATx۫W?{fNNB!4Q-^ ""D)O&PJmb4Z&95i697{-e֞ߜ) goΞ={w]k5s;U@?;)ӷϿ?,paM'P 7676Y_7=vx4~׸\9Qs_o?/S?|wߋ@34x˓8QPUTWh %ʎ"'q=  <,q yEWUкFP`;,ϩ/ro_~-7|^ET/YHcAQAxM7oN8~ wݽ/Pxd:kF6Y^]e2sq ]hyeuD4RUUp1.X ƠpWoPuEU9*WǕ%VM.Zv9^[O|7~GQTm[TCHW"I*x5 9`aaq6ti_L>y8~a\{-'Z +D*A~6W^S?чwoajB§?ukA0 ~,Sԇ5k<ǷE[ {`3w??H3g[_UUfg^rBNv!ڝ"jꂣZBqQGg?{sal,B vU2+FwQ3ǞDq,cTD&L`jpoEM+bA /Vxs5d D:NTgE]);1 GɊ4O#]0\=AZ+ƝW$T;m t}eg bU mJ(Q7+cb,)gJ]tfRDOxIa%m2A1Wxigt!2kfi: .4$< ]90Lh6A;@P\.ٕ6L#0q\Gs|fS Xˤ2;V )(VĂ-] -0A\!CfDp MgCV+ }fK2X{(iUUD﹬,LӪ+}f!Xvb$O X@N v%%V_t[ˮ;aJl-j`AԐ{~G|uU9pULI.^x  Vh5_|H֍em=5Eg9ǙӿF@nO~OL>p8sM M|1TҹMBYC"O=6jڟoMzy} Qs;[o&$J X*U7 tEIz|=uFM֖cWrޢ>kαs_MGCIENDB`apvlv-0.7.0-alpha/share/doc/apvlv/icons/reg.png0000644000175100017510000000340314726051136021653 0ustar atzlinuxatzlinuxPNG  IHDR04̓bKGDCIDATx՚KoWC4³*HtEʢ,RB+O]Y#P%XtQHx4< MTv`ĵӅ3;wSP+d߹w|?5w,N`u wP(|b(cccrQ_{@*VܺuKDDfffފ@Jr,bQdttW8%R!"H* ="ɰyf._ X Mlݺu^G\-[044DkkSЗ۷oϋ~abbLM6q :::J~y58yGww77~M85Bs&0 J%sccc\zRĆ ݜ:u {`O&hDhڪy_#GP,QY={| Ju.D>/_N.X\8q߸dF<zlP=QpF_]+!JQTi<8v.`B.rTz*j"U* |t/7,6`*y5SumۦYE8x2ո(fhnnv"(Tbd2&=S_+W.t67ѬEĚ@{}I9'ۿe$,1q>veO|jTytXD)U|DWDXL2,\kI5 $;w͛׆2.Nf`%X!+"ݻϟ%AH~:--UH*(HI|hDbg.\8ϲe^@$fۘML&@H)RT,ʈ5F6%B LNd׮ŋ5HB8i\LNZsZzp\B^'ĶCzXHk. $p?ԜkqUUx$!U}fr1IRcbDgYjM2 [j\Lb&-[IaWJ !vOgwe+l$k KYbbqR"S\4Bgck(Y(Da:khhrָ%Mk!~^|QFљ8؊ð)/ǡCd'B##tiFK(n&6MBJ&S(Ξ=Ú5%$Z%sypn)MCg9n[rp0'C/[ܫ*"ўXOnQ|b"̙_Xv]rzPv۞!Bx߾rO-r}ŵopkJnϟ?GW (Lpzh˸[Xӧfݺ=$0;--u34rps=٩WÇIC\ڀL.~}]rIeޅ)!Т.!k)0CHP*MZ0/dm(C@eIENDB`apvlv-0.7.0-alpha/share/doc/apvlv/icons/pdf.png0000644000175100017510000000244314726051136021652 0ustar atzlinuxatzlinuxPNG  IHDR szzsBIT|dIDATX[le盝iӲlRX,m|2ф$|AbL *x{(CbjK\rmwwf>ig~s9?9[${=OA:#E(#u>xGQuR/u^:|{חc/TxlL.\=5Z{/xP>9ƾ O< RTKi ǀ @.5#°3X{^uI6=t;s9Ξf®оBcI2,|~ rMoAp0#&Z) ag|(%,)e18NWwߘaވ{IEG']g@81ļ XIP-uWB~Qv>$Rʵ ɥohh~0q D7:AXBB^ ďo!Gj]YL,u']F 0;7?nͶ-2u72??2,JƎogou::h\JAfغXPaN/w)%|ϋX@(l<7t=IENDB`apvlv-0.7.0-alpha/share/applications/0000755000175100017510000000000014726051136020046 5ustar atzlinuxatzlinuxapvlv-0.7.0-alpha/share/applications/apvlv.desktop0000644000175100017510000000132614726051136022573 0ustar atzlinuxatzlinux[Desktop Entry] Version=1.0 Type=Application Name=apvlv Comment=A minimalistic document viewer Comment[de]=Ein minimalistischer Dokumenten-Betrachter Comment[fr]=Un visionneur de document minimaliste Comment[ru]=Минималистичный просмотрщик документов Comment[tr]=Minimalist bir belge görüntüleyicisi Comment[es_CL]=Un visor de documentos minimalista Comment[uk_UA]=Легкий переглядач документів Comment[it]=Un visualizzatore di documenti minimalista Comment[pl]=Minimalistyczna przeglądarka dokumentów Exec=apvlv %f Terminal=false Categories=Office;Viewer; MimeType=application/pdf;application/epub+zip;image/vnd.djvu; Keywords=vim;pdf; Icon=x-office-document apvlv-0.7.0-alpha/zh_CN.ts0000644000175100017510000004251214726051136015633 0ustar atzlinuxatzlinux ApvlvSearchDialog Case sensitive 大小写 Regular expression 正则表达式 ApvlvView File 文件 Open 打开 OpenDir 打开文件夹 New Tab 新建标签页 Close Tab 关闭标签页 Quit 退出 View 视图 ToolBar 工具栏 Horizontal Split 横向分割窗口 Vertical Split 纵向分割窗口 Close Split 关闭分割 Navigate 导航 Previous Page 前一页 Next Page 下一页 Tools 工具 Help 帮助 Toggle Content 显示/隐藏目录 QObject config file 配置文件 log file 日志文件 document path 文档路径 version number 版本号 help information 帮助信息 apvlv::ApvlvContent title 标题 Filter 过滤 Expand All 展开所有 Collapse All 收起所有 Refresh 刷新 Sort By Title 以标题排序 Sort By Modified Time 以更改时间排序 Sort By File Size 以文件大小排序 size 大小 modified time 更改时间 Title 标题 File Size 文件大小 Modified Time 更改时间 None Filter Title 过滤标题 Filter File Name 过滤文件名 Filter File Size >= 过滤文件>= Filter FileSize <= 过滤文件<= Filter Modified Time >= 过滤更改时间>= Filter Modified Time <= 过滤更改时间<= Filter Type is invalid 过滤类型错误 Confirm 确认 Delete This File 删除这个文件 Will delete the %1, confirm ? 将要删除%1,确定吗? Input new name of %1 输入%1的新文件名 Rename 重命名 Rename %1 to %2 failed 重命名%1为%2失败 Warning 警告 Rename File 重命名文件 Delete File 删除文件 apvlv::ApvlvCore no content 没有目录 the file has no content, if still display content? this file has no content, if close the content? 此文件没有目录,是否仍然显示目录? apvlv::ApvlvFrame Previous Page 前一页 Next Page 下一页 Default 默认 Fit Width 适应宽度 Fit Height 适应高度 Custom 自定义 text in clipboard 剪贴板文本 apvlv::ApvlvSearchDialog Case sensitive 大小写 Regular expression 正则表达式 ... …… Find Directory: 查找目录: apvlv::ApvlvToolStatus Previous Page 前一页 Next Page 后一页 Zoom In 放大 Zoom Out 缩小 OCR Copy OCR复制 OCR Parse OCR解析 apvlv::ApvlvView File 文件 文件 Open 打开 打开 Quit 退出 退出 Edit 编辑 编辑 Navigate 导航 导航 Tools 工具 工具 Help 帮助 帮助 Next Page 下一页 下一页 New Tab new tab 新建标签页 Close Tab close tab 关闭标签页 View 视图 ToolBar 工具栏 Horizontal Split 横向分割窗口 Vertical Split 纵向分割窗口 Close Split 关闭分割 Previous Page 前一页 Toggle Content 显示/隐藏目录 OpenDir 打开文件夹 打开文件夹 Search 查找 Advanced Search 高级查找 Back Search 反向查找 StatusBar Dired input url: 输入网址: OpenUrl 打开网址 apvlv::Directory Title 标题 Modified Time 更改时间 File Size 文件大小 Sort By Title 以标题排序 Sort By Modified Time 以更改时间排序 Sort By File Size 以文件大小排序 Filter Title 过滤标题 Filter File Name 过滤文件名 Filter Modified Time >= 过滤更改时间>= Filter Modified Time <= 过滤更改时间<= Filter File Size >= 过滤文件>= Filter FileSize <= 过滤文件<= Refresh 刷新 Expand All 展开所有 Collapse All 收起所有 Input new name of %1 输入%1的新文件名 Rename 重命名 Rename %1 to %2 failed 重命名%1为%2失败 Warning 警告 Will delete the %1, confirm ? 将要删除%1,确定吗? Confirm 确认 Filter Type is invalid 过滤类型错误 Rename File 重命名文件 Delete File 删除文件 apvlv::DiredDialog Regular expression 正则表达式 Find Directory: 查找目录: ... …… apvlv::ImageContainer Copy 复制 Underline 划线 Comment 注释 Input 输入 apvlv::SearchDialog Case sensitive 区分大小写 Regular expression 正则表达式 Find Directory: 查找目录: ... …… apvlv-0.7.0-alpha/README.md0000644000175100017510000000363014726051136015537 0ustar atzlinuxatzlinux# apvlv apvlv is a PDF/EPUB/TXT/FB2/MOBI/CBZ/HTML ... Viewer Under Linux/WIN32 and its behaviour like Vim. Apvlv is an open source software, was created by Alf and is hosted on [github](https://github.com/naihe2010/apvlv). Now, it is still growing. Like Vim, Apvlv makes people reading their PDF/EPUB files just like using Vim. So, Apvlv bindings lots of Vim command and its behaviour is like Vim. For example, < Ctrl-f > to forward page, < Ctrl-b > to previous page, 'k','j','h','l' to scrolling a page up, down, left or right, and so on. And, Apvlv can understand that how many times you want to run the command. The only thing you need to do is typing the number before the command. For example, typing '50' and < Ctrl-f > will go forward 50 pages, typing '30' and < Ctrl-b > will go previous 30 pages. What's more import is apvlv can support view a directory as content of a pdf/epub document. Pressing 'k' or 'j' to move selected up or down, 'h' or 'l' to collapse or expand a dir, and press 't' will open the selected document in a new tab. # Dependencies + Qt6 ( http://www.qt.org/ ) + Quazip ( https://github.com/stachenov/quazip ) # Optional Dependencies + Poppler-qt6 ( https://www.qt.io/ ) + MuPDF ( https://www.mupdf.com/ ) + Teeseract ( https://github.com/tesseract-ocr/tessdoc ) # Download + Releases (https://github.com/naihe2010/apvlv/releases) # Build 1. Using cmake to generate Makefile. ``` cmake . ``` 2. Execute make. ``` make ``` # Install + Make a package and install it. ``` make package ``` + Or install it directly. ``` sudo make install ``` # License apvlv is licensed under the GNU General Public License (GPL). # Contact + Email: Alf [naihe2010@126.com](mailto:naihe2010@126.com) # Develop Tools + Vim (https://www.vim.org/) + Emacs (https://www.gnu.org/software/emacs/) + CLion (https://www.jetbrains.com/clion/) Thanks these great tools. apvlv-0.7.0-alpha/.dir-locals.el0000644000175100017510000000144214726051136016710 0ustar atzlinuxatzlinux((nil (eval . (setq-local flycheck-clang-include-path '("/usr/include/poppler/glib" "/usr/include/glib-2.0" "/usr/lib64/glib-2.0/include" "/usr/include/cairo" "/usr/include/pixman-1" "/usr/include/freetype2" "/usr/include/libxml2" "/usr/include/poppler" "/usr/include/pango-1.0" "/usr/include/glib-2.0" "/usr/include/gtk-3.0" "/usr/include/gdk-pixbuf-2.0" "/usr/include/gio-unix-2.0" "/usr/include/atk-1.0"))))) apvlv-0.7.0-alpha/apvlvrc.example0000644000175100017510000000223714726051136017314 0ustar atzlinuxatzlinux" some map " map n to to goto next page "map n " and p to prepage "map p " map I to zi, and O to zo "map I zi "map O zo " if start apvlv as fullscreen mode, default is no "set fullscreen=no " zoom value, default is fitwidth " zoom has 4 styles " a float type number " fitwidth " fitheight " normal "set zoom=fitwidth " set window size "set width=800 "set height=600 " set command timeout between two key press "set commandtimeout=2000 " set default dir "set defaultdir=C:\ " set weather use continuous view " make sure the autoscrollpage is set to "yes" if you want to set this to yes "set continuous=yes " set a pad to continuous page "set continuouspad=2 " set if auto scroll page when at the end or begin of one pdf page "set autoscrollpage=yes " set if auto scroll doc from 1st page when goto the last page "set autoscrolldoc=yes " set if disable ~/.apvlvinfo, default is no "set noinfo=no " set if wrapscan text "set wrapscan = yes " set double click action " option value is 'word', 'line' or 'page' "set doubleclick = page " set GUI options " m means menu, T means toolbar "set guioptions = mT " set if reverted pdf page "set reverted = no apvlv-0.7.0-alpha/CMakeLists.txt0000644000175100017510000000506414726051136017023 0ustar atzlinuxatzlinuxCMAKE_MINIMUM_REQUIRED(VERSION 3.5) SET(CMAKE_CXX_STANDARD 20) SET(CMAKE_EXPORT_COMPILE_COMMANDS 1) PROJECT(apvlv) SET(PROJECT_MAJOR "0") SET(PROJECT_MINOR "7") SET(PROJECT_PATCH "0") FIND_PACKAGE(Git) IF (GIT_FOUND) EXECUTE_PROCESS(COMMAND ${GIT_EXECUTABLE} log -1 --format=%h WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE VERSION_HASH OUTPUT_STRIP_TRAILING_WHITESPACE ) IF (NOT VERSION_HASH STREQUAL "") MESSAGE(STATUS "Configure git: ${VERSION_HASH}") SET(PROJECT_PATCH ${VERSION_HASH}) ENDIF () ENDIF (GIT_FOUND) SET(PACKAGE_VERSION "${PROJECT_MAJOR}.${PROJECT_MINOR}.${PROJECT_PATCH}") SET(SYSCONFDIR "/etc" CACHE PATH "Directory for system configuration of apvlv [/etc]" ) ADD_DEFINITIONS(-DSYSCONFDIR="${SYSCONFDIR}") ADD_DEFINITIONS(-DPACKAGE_NAME="apvlv" -DPACKAGE_VERSION="${PACKAGE_VERSION}" -DPACKAGE_BUGREPORT="Alf " -DRELEASE="rel" ) ADD_SUBDIRECTORY(src) INSTALL(DIRECTORY share DESTINATION ".") IF (WIN32) INSTALL(FILES apvlvrc.example DESTINATION ".") ELSE (WIN32) ADD_CUSTOM_TARGET(Startup.pdf ALL COMMAND "man" "-t" "${CMAKE_SOURCE_DIR}/apvlv.1" "|" "ps2pdf" "-" "${CMAKE_SOURCE_DIR}/share/doc/apvlv/Startup.pdf" DEPENDS apvlv.1) INSTALL(FILES apvlvrc.example DESTINATION ${SYSCONFDIR}) INSTALL(FILES apvlv.1 TYPE MAN) ENDIF (WIN32) SET(CPACK_PACKAGE_VENDOR "Alf") SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "apvlv - Alf's PDF/DJVU/EPUB Viewer like Vim") SET(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_MAJOR}) SET(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_MINOR}) SET(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_PATCH}) IF (UNIX) SET(CPACK_SET_DESTDIR ON) SET(CPACK_PACKAGE_CONTACT "Alf ") SET(CPACK_GENERATOR DEB) SET(CPACK_DEBIAN_PACKAGE_DEPENDS "qt6 poppler-qt6 quazip") IF (EXISTS "/etc/redhat-release") SET(CPACK_GENERATOR RPM) SET(CPACK_RPM_PACKAGE_REQUIRES "qt6-qtwebengine poppler-qt6 quazip-qt6") ENDIF () SET(CPACK_SOURCE_GENERATOR TGZ) SET(CPACK_SOURCE_IGNORE_FILES ${CMAKE_BINARY_DIR} ".git" ".gitignore" "win32" "~$" ) ELSE (UNIX) SET(CPACK_GENERATOR NSIS) SET(CPACK_NSIS_CONTACT "Alf ") SET(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "CreateShortCut '\$DESKTOP\\\\apvlv.lnk' '\$INSTDIR\\\\bin\\\\apvlv.exe'") SET(CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS "Delete '\$DESKTOP\\\\apvlv.lnk'") ENDIF (UNIX) INCLUDE(CPack)