perroquet-1.1.1.orig/0000755000175000017500000000000011561510203014667 5ustar georgeskgeorgeskperroquet-1.1.1.orig/NEWS0000644000175000017500000000073111561344350015377 0ustar georgeskgeorgesk============= Version 1.1.1 ============= * Fix some small bugs ============= Version 1.1.0 ============= * Add a repository manage to install easily exercises * Fix various bugs and refactoring most of the code * Implement exercise languages * Add a lot small help and settings tools ============= Version 1.0.1 ============= * Bug fix release * Add turkish translation ============= Version 1.0.0 ============= * Initial version * English and french translation perroquet-1.1.1.orig/data/0000755000175000017500000000000011561344350015610 5ustar georgeskgeorgeskperroquet-1.1.1.orig/data/perroquet.png0000644000175000017500000003726411561344350020360 0ustar georgeskgeorgeskPNG  IHDR\rfsRGBbKGD pHYs jtIME!)/] IDATxy|\UΖL֦I$mڴRR}pA*u|WQPU"|Ad-Ph(-If,=#!L;w&|^y̝sϹs}>y瀀lrY7'$cT_  2F|hjhTR/j]F"xQ#@|NNNVA0l _-.O5 < ]% B4Ѱor_|

;?@L\aߐ{([+(jJ[}=^_OWK3g!` 8=,m[^t1ǜy6UU+}A ǻi=x`)p{O " ͮ?`6' N),2T>x %.|&ASU(p|2(ZXũW/ _Sm Ph2/7jFD *o.6KI3v{wMp2)l& ]?\%E?)/|J`o/xLv6 O t"& ?mpPfm^5zڼ)ktbtbwfbt2Egckn L_V?O *׬ʻ!dsGC{%~baY~,? EoUx/ +!IRxR]U%mW-bO;AsOpS3H"l|/_G֬YSU5 It"|_䥪\6?tp0Pw;G;^$Uޥ9~x ŋ'ߒ$/@lw)BI>v}k{YG0!qHP'?9OyYJ*@ _6]ӳE_rJ;L*;#++K?rJ2.pK[Q/ WS.8﫶{Hݵ_V|d^Vp$I|{v*aR%,- /%'gJt=L=Z˪J5]YofOہ%9r$E|s?~,UC`\RE0ORBa1e8בS+1owpkc>낋zd\ $D@4?G= XRX&FiٟtuϤ~rTTdgOƕ ̀ }o "I5RIa9 ̨^Qv<5-ǏXp׽K$I_>᩽aHgk% +% <ïQ(Pzw_jԱeǐֹWZ)4-z ޒ|TI $5*3GYps|?5P Es?y=`lEB_P%SQ9Y qb4d/Ӣs?|+V32?~+<x$e+%U8d0OL>/^~U)۝'š  M-]U%)/Y&)DǜWj[OguUapt|ޭBG 1 >ir0+r5?ѱ.v` 7ae|{Zhjj1{lJJJd޼y\3g:oVみ\9Ij\y\lyv!{ y_?c[l>gYX2bSNxہMчM/̮+)(+ḰXfDDzWǜ/_3n}b+믿;w&,³>֭[ijjb͚5diEx=/<*5 ibqf~2"IYT'}stw/ ػW`k./_Ihmm={wdѢKր<pu2Xup‰yJ/$[V/k%&$>-ܹsbnޟ*'OG]  sFZG{=`ݻ.?dgg~z."-Z ^êO}zhykkk_ ˀ;09GK ՒdOtLS:~oO~!{7r@aYy衇pc= ciWy<.A-1n= B~Lvfk+Vr̓(߱c'x"7o'?Je˖Ԥ\{=\.W/"Cx<; &2ppK `x)sf\zdeeq}a`b… ~?gqFxB<|_]]Tq3@MLAT9Q2dK-Zֹ%O<< _~9G'\pEEv;o~3a,AX['nw ~gLJCŸ-w.9jFGVҞtZc۠~D/kn{ L̬(ID)4*hlw~"S @iOXwLUՉh( HOgU X'X:BO;_Ș1 /D"^}U{/l͗^zQZZZ -&{vS@ꄿ|Ȭ:sQDǒdp8p@t޽{yw (8? d,?s[nU@r>C<kA5އj3G8rH?׻( _PU5vG}/,,k!FGnw B,6>kcxmM q饗vZϟOFFuQz|ͼ2UW]şe[[[Kx饗nmP(IJeG9y"749a7% X- Z:̬?e׿߂LI:t|;{¢E'?Ijs7|37tӸ㧜r gqw---ر?O'gyf{ggg$ PUUqz!#~#g ąx0euLN:i؆ƮB!n&~300`UVqwrJqwrWzW#׭( +²e/o_әgkʕLd8N:֭[g9M~_",T!i Zh8~ \xx^x .q6UصkvzUUk222Ru|/~Ӂ0 ,LɇTNO$Hԃ.,bӫJOFQ%( \/bΗIJRT86F.}WҖ'4^OOO?, <@̌=Fկ~uT@ @__)uyffҽ5n{&Ml%ZI7Y4UU5|>M?n]viS999<#\{ w֭[կ~RnS㢢"6oL~~SvOH8KRY."B*OCPU:?cJt?o9f>W'di*Xhjj;7 vms=_G P\\_7Qx -lr$e>*+и=&{E_um{ڔ~=nbݺu^<\ves9fECCC2uuuB!~ӟ[|;w.#W\x<uQ?$...fz󁿻CJi*KQY#iXcVh2畳~ o+,ƚ{/0U/Iffy饗(++c…^:555vLnӻsbMCgjDD:6ՙH};xg傄[ϴ2@|>dYpa>cd.HSv?x&@8C@BY/*YT{f@AA6-- .O& z1H?8vK-0e׊DUUr̰'!}߉ۀ+ݶYXf̈́~{9EAQ:;;ijj2ެ۷oz@eN8aBQDNcc#9"䭭HKK >//դp8׾wfn{ M<p :$$9OTﶘ┚hW\G͛7OX&c˖-#'''< 炙^'L(^2fϞ=* Yn2 PHyTr{ߞG?Q)dؽ|Jľ_nfMDի9RcRwD갇~+3m  p˧ٟy .H^y|xqwqgFjǒYHg",]]vѫ=ۺjkk /\'QNH2S*;!|~'z+zjRjr~?ɮ7 p8ظqB$D~2Mߑ|BI!7O o߬1MH/_nꍖe-[h#k׮HKa$aR?>\K>qJ&W9Q,JJZ x/WG VB ߛYf /63?UWibq i… ([:- `xlTCt>Hᇗf@v·6$|v;\r YYYرP 6x8Dm??qUVV&dç7^aZs]iM\y h6`DLu֯>>P^<<.8"gqJ$N:$/o9dvw[n_"B!~G|?я=-H[999Z Kb,B8lMhZ"Dyb!P>denVz)<MMMt:)))aΜ9lذ;j<߼<##ßrj?ޱ>]viMP2 ^GzF@:H %:]~*@R~7TA?7nvZN9Iɬw챝;w 9 <fMN e1, Kßwm7eࡇ? .l$l]*b]իY@RtYJVF9,uמd/&|550KO|EU^{QY())I[/уzHFUU>fmO'&Wlg$H:̱A f >a4tc+L[|(w…TUUMY]2ɡf-ݚ 4jH:qf 8T j?`_\2@}b_ո@(OM;B|`ŊN3K/i5I%M15e%0]<ޱyvE0?Òӊ^xq?ER~Mg{,;;;2yڷL4୉Q*)C} ISj1hި 19w96rZSO=u𬬬t͌kZ逸[o58jrkN{3Q@n~gE᥉ i6Ҭk%?8ȷ(?m,/^aCp۷K줨,nT9ŋu/1{#&W^gĒ\Yq~co/}yP0#iҨ8ȝ/oc,,rUc(店_~W^f~{Pf󪫫#iNG A$rKTWW#OH`H7sW hRYe_>wpcc>eҢ[&* *Aïux`SE"ۯm7\˅(i3 VX?uJlb46N.6z~F6H+;wdڧ/߾kddJ0/Y/0Co}Rv`(,?j=Q5%#?`{ &'@g:OUUW[}mAQt5p_J r4鰖?m-_\+lpY7)ɕ8~f;ϑ0% 1Zf#r5@(Xyy9E3(IDAT$K/'ڀ:ɲ'Ṵ0!| hyyy 3AbL3&W^e"I+43!P !ɀﻄvX;IR$ N'fR0b?ttq؍G! ]ڂ@L5c!2YYY9'[S}MZm^x46 I0WM3O @2B>4k tPSYonn.yyyRř 0FN̑T%UxU d@#L U'rQXXHaa!i}]ZZZrQYYIaaaɫݻwk%[\z nPErL;UX;^>KZ4xS~]ټY.CVvv6QPP0jtz{{ٶm]]]RN:餘$ :t>  JD9&Lrs@ҥKfa f~zzz8JKK)++#''gU~UU3W#G׿inOSJ0wO C<`d+3r~O-uZxvep(H3} fSS w 줾f X,@"Nx4662|dYNIhmm语}(!X̸s1it^2 9jFQ]O;m ~zxUUE<9r$ GiiiJ*p!-XV]]Z_ .-T2%u xc+4q k :sX+˼׳ "X?---Ӄ0LTeggc舙h> VkW fddgm8<0*L7qf\cY294zFϯ/Vxw[J0YV#?TCUUzzzhhhNfffҖPQQVeggmdgF}}=V[[Ǩ*7 ʾ~3yD:e] >faؤzN{{;999STTA0)hXXd ݻ7=00`2XjM,@x5&o* hfIamA,8*MnAOOO\9Qtww[oIyy9%%%xΊ+f޽e֬Yq ]dKCb%9 MmhON(G"%oAMt:fܹȲ(̡CI۷0o<ʰX,IJroAff&ͣ a#? %LX zo,QCxS$5(6DGЏqD)[=%m(**cf!2${ֲgm`…%M lDyG4q#x_dDr%+S{nۗ!-5O$@dEf+7o^RT~-岲X`P(tttt:liӚ?  J NI?k+Ho;9tFBiO9}e1rX֤ɲLII Ŵ188tdq8i4}25#咕QN"4'Je^ZdЪOui Ў>G{WǴ 7Ů70N'UUUۛ(Bχbdl^~W&"1.P#g:2jW[JYOEL7o޸$V㕕$|(,,d``p^h3RGVhDh HqSdnV z|Byע/??y؆Cx)hŔطouuuIK@F0dΜ9+]vK ?؂aQq\YY`i()y Nc8Fy35EUUUȲӲn  Fi=8=Y޷P?\3$`#u}M慥Av~"9u,GNNyyy& Ɯ9sXx1 o* Ff%N-EFhEZ Jdv>Ġ]@bJ1Sz>Nrŋrp8 cX> zZ.KNȐK$ ǮCox~ ƣ|/S333#H[Ml* r***$>hjjȑ#zf[[[$)p's?ơ™ !Do^&7Вcc&(((,inѡ+;&%hӀW%轐,I%+ /lyyQph!UPq,2ѧ.#(I477Zk`&)B!W6')%C闰$ޙҕ7f{*$ YСC:Rv'EQR~,/3Ҡc HyP#1=Ij^rbžc_D^~7RVk}ziWl~Zח# @#z-#d=I OLeAyGE’H1nu*-g٘={6| Ȓ`0lZFˀMz?$A 8PvRCl-2;dH75N !oFKL[$HD=6tD7QnT੮'{챑|td-cȉvTf&_`|t +jDpE|0jw p-Hoc!Dn u.%jN91łfK{<m6.K?`xDԃA3$dy[v|(_?<'cL?HII eeer[~!L' AY^VXY;6h)?ר( vuɈ ךk" UH{ `NFґY=2y@ (FVO /_;cXemVl߾]k8A0б`&z V rhD( !KF&"eUUe߾}޽;ÝՆ@)@! ifj չ\]*'Ty5OacI7:L&,2Ґ0Caxn;Y1IFfA.RWWݻMuuvvFFZ,\x^RvNWolLzBLDb:iP|'HfEzRшwD4aL75gAݻ5S_VIcQ)yiEQ"- W`ZZlD&ӓP~=O["y<voa(fܑȔxma'd};Rs+iq"B((HjEȨ>,#?A(Kݫ-]z kq%+e D=1ϒPy-bZgӝcN#iہG=.<OZ0L-` cޑ:3i]L_ft= Mx۵bx_&WQzED(Z28iu2GT{Gxܹs5M  ܬgH `_d]Ce{teɠ͑m1qfWZ @딠t/`=>.9d̦#3VŚ=ϡ4 )A9@ B@ FJNOY=] pɐ#@V~gAg6,"cs?L> O ~xq4W@P<f*ШX28epUY&$dM3JR]]"dw+,k@p@ `xjԨL %€CQRl=&ƮFwt3sl__4tG<ŌOUM,n?[4Ҿαd0 Hň>\Mx<{Ǫ&*- 8])g@Ե YRNB 6B x}_ftq^ݘxVeẕL;Sl4?O|̲Qpv_c$b @ at2&,܍4.F:%݉fZ/rt0Adm6Gh `D_bnoP&4Eooodbxz"0oɈch&1&W`$xOF-1;kM} |Zxz2SRž3 {D/F# (DD?=W"n `b_5^_Ũf@10&&rקZ   ߸ﯢ'}&40>)Tkf!=O24CbW B\y2C~D!c|gEYG>{8ѤZׯW}J^|{DJ<O&A'wsBL(K i~UIx}Db9E?9^q#}+z0 kPFYzP=L{(l CxF{/mn}VtZ8׀bVu'$T- @Cä7ehSxtRC ^莤AS6hSVX6g¯aRph +,,Ax+l $Vts^3nZ#W)3TmA3^ah+{7vHp@ΧGw¯G5GX~U\3~cC Iһ[:^=?#`b_c`1TR (uã[z?=w`iE^cf( 44lc]mh x[i!f%ȋʏq 5 normal False True vertical 2 True Do you want to reset the current exercise ? 1 True end Cancel True True True False False 0 Ok True True True False False 1 False end 0 buttonResetCancel buttonResetOk perroquet-1.1.1.orig/data/sources.conf0000644000175000017500000000007011561344350020137 0ustar georgeskgeorgeskhttp://perroquet.b219.org/exercises/1.1.0/exercises.xml perroquet-1.1.1.orig/data/properties.ui0000644000175000017500000002404011561344350020343 0ustar georgeskgeorgesk 400 5 Exercise properties True center-on-parent True normal True False True vertical 2 True 5 vertical 10 True 4 2 True Language: 3 4 True Translation: 2 3 True Exercise: 1 2 True Media: True False Select a video file 1 2 True False Select a subtitle file 1 2 1 2 True False Select a subtitle file 1 2 2 3 True 1 2 3 4 0 Repeat sequences after completed it True True False True False 1 Use dynamic correction True True False With dynamic correction, the correct words will be displayed as valid as soon as they are correctly typed. True False 2 1 True end gtk-cancel True True True True False False 0 gtk-ok True True True True 0.52999997138977051 False False 1 False end 0 buttonExercisePropCancel buttonExercisePropOk 100 0.10000000000000001 1 999 1 10 perroquet-1.1.1.orig/data/icons/0000755000175000017500000000000011561344350016723 5ustar georgeskgeorgeskperroquet-1.1.1.orig/data/icons/32x32/0000755000175000017500000000000011561344350017504 5ustar georgeskgeorgeskperroquet-1.1.1.orig/data/icons/32x32/apps/0000755000175000017500000000000011561344350020447 5ustar georgeskgeorgeskperroquet-1.1.1.orig/data/icons/32x32/apps/perroquet.png0000644000175000017500000000322011561344350023200 0ustar georgeskgeorgeskPNG  IHDR szzsRGBbKGD pHYs<<MtIME IDATX]lWwfN#_N6^@%*I)T$<,u+J@CB"w28fYRL2_Lx{ּ W~qQrL11Ԁuuu<7n*'&_9vpu߿oӶkRf̯,enT 5_&y^c*3~n+31\3Q>:3VtjJ/qBqWא]}M篼o Y.4pZ(:5zs^BO>8WM^yÇQJᘕ,3122bkkkЧ੐Pt7P()3::>N8]]]묃.k(VS@' N vgs'Oٳg9z(RAOOPvzzzaF5hUBVF^o`vvCK.q9ptww{\.WePb j./{6vsNR9rdͤi2 \|>OPR__innȸQF?@X[\K$V3y1v<Ïq)willBut]0ҳ&.7 ^\ox|RqZB^}|l r(b5A$ѯ^h42W9EVzB Җ]>͙i| v@:fvvU"R2==֭[u4zN6!/#omDQ62~>3ۉ3ƍ̧E&'' =dx<.e ?і{  ;dJFǝ|:NKK LݺEzfN*PQQA.+ HRvEAO@H|~RZaCm|͞G -uBOF"+*$$ n7|$"277g|NroԈo+Ŗ&A,Jj7l`K8L2… cY:\)y^H&`XFA`7B^{]s~-)%L)C!vh 橨r%0[h3E&+rH)sRΦ`0뺎m#~&wbUa|ضMPq.(R|c2ll۶RÆ*! ۵xyr儮1~!D_mh4ibv߲7ʷ^s64JIwrC<wR]bRrek7"/nWeMu~ +@<{g RB? |p_Dh.0 .F>Ĉݯ8$IENDB`perroquet-1.1.1.orig/data/icons/24x24/0000755000175000017500000000000011561344350017506 5ustar georgeskgeorgeskperroquet-1.1.1.orig/data/icons/24x24/apps/0000755000175000017500000000000011561344350020451 5ustar georgeskgeorgeskperroquet-1.1.1.orig/data/icons/24x24/apps/perroquet.png0000644000175000017500000000222711561344350023210 0ustar georgeskgeorgeskPNG  IHDRw=sRGBbKGD pHYsֲ4tIME"9 IDATHhe_;;vK9V"d(<"+JAA$Xg(HF贠F igRrly}=ۆ+~?yhs(^.7mO5يS_~-B';bn}s( %l_T%ϩ?ާ>շ{ޗn/>_{@qB]8**ɨ^u.kmT |,ohuBY83[]::;;iiikZcX ,R,}Ab͚5ttt0x-ˮ]Ryvs>/ŚJztm;,$!)8TxD"AP c;ݤiBPǿb]]]^ *z! 3f o&&''ioo$u:0Bq!tze__>Ϗ>(I Hz?+\Fti dGGB)E>Dz,._|^B>n>Rs7;IVR*C].u(^WX5C"QѾ "㦉J)&&&em^ h(е9 f- j;ed:%Gbغl6<16`Sl;b~8mv>/ńϻЯzs ȢK,>bL&illX Biy` hc+qJ١iZgd@IENDB`perroquet-1.1.1.orig/data/icons/scalable/0000755000175000017500000000000011561344350020471 5ustar georgeskgeorgeskperroquet-1.1.1.orig/data/icons/scalable/apps/0000755000175000017500000000000011561344350021434 5ustar georgeskgeorgeskperroquet-1.1.1.orig/data/icons/scalable/apps/perroquet.svg0000644000175000017500000002757411561344350024222 0ustar georgeskgeorgesk image/svg+xml perroquet-1.1.1.orig/data/icons/scalable/mimetypes/0000755000175000017500000000000011561344350022505 5ustar georgeskgeorgeskperroquet-1.1.1.orig/data/icons/scalable/mimetypes/application-x-perroquet.svg0000644000175000017500000002757411561344350030041 0ustar georgeskgeorgesk image/svg+xml perroquet-1.1.1.orig/data/icons/48x48/0000755000175000017500000000000011561344350017522 5ustar georgeskgeorgeskperroquet-1.1.1.orig/data/icons/48x48/apps/0000755000175000017500000000000011561344350020465 5ustar georgeskgeorgeskperroquet-1.1.1.orig/data/icons/48x48/apps/perroquet.png0000644000175000017500000000511211561344350023220 0ustar georgeskgeorgeskPNG  IHDR00WsRGBbKGD pHYs!`tIME9sV IDAThkpT9{9K4Y$l0U[Ģ~tiRiK5ӡU*ǩEElK@Զ~RGD+%D $!,ds~ew /yf9ϞgaB&dB&dB>1i[4 "`>@ v/7@ϫ_ps^=C-#gNuU{-K癭O-{?w{;>hfz*MBhyfdl PzGɔa"=:&Q}T!`x 'csu¤VPneJrT|O? u )€0E1(͌Poͪ= ?5>V&_Q HLȪgaèillFY~=)x  4,&uĴf:}'NvZ֭[ٳg}vZ֬Yӧ8|0===455!E *ؚT!B>f7nJSSvǏSUU ӧO'g_t)PX,Fkk+[lXLBGf6L7(Stdoo---Q\\ 봴pnJ `ɒ%lذx<@ `ժUB4 < ?j>lkWP/ 2xٽ _<tttPSSwܑuuuꫯr뭷GΝ;4/nլ\Rl6 < HuہI5¤HF8xxWv+V.MUU0Agg'XT*E:Fʡ e˖ +[z}=ʄ4a"aTm)vU"̜9@ @8M6xb݋]׳sUu 0 ~?Gmkk?l7:,T lƝ@Jgբ5æg$a,_UUQ%s===lܸ- En0ShQ{|wghMy ⢟N'~avM8vhW[[Vrd!)^R֎f ~}ϼ$wϽ!;ٱ}'s5̟?X,6 mgԩSlδ*Rc/C\ZG:#WCC;`!٫QUU`057Kx`0)rԩwI^䬱%P `v:;;Cuv;aOkk+o&'OFUլ]JI$HDe ֞PPc/kt{eӕeLrL&'4.+K:t:uN1 #sjy ,Lò%&GK+ioSNFjkk 0 H${Nr8͂-d2㪵Xmq6%w!0+< ꯫p D4f͜E<gw*AAoo/.\v_qU"V$R|6>h)A{IG73 +sPsMI ISphM0 #/}4ir*%$ FeN-2bϘA] "KL4 à)CmpkEB cH#_THc( h>4M}oHfz:;;F̋cX (WfȜSę&l6$T*OdVsad<$Wc41  lp6"(}-s2܆v̀#4MRT3:\$(KX,?<0o%$9mt"TA"]sqt,drv"@4cnO@&Cba|rr ҧ"LęƴcT ݉pv8R)=y`_4=gϞ0ߏ"LfoUȠn"XWh(J$!`ϊ@lEP($;P(UUTi7-d{f Xg`&0*8ip ioG4P@F̜_Ox#jMG)G??t.f `q` ~(Z8E_@fnY,m$sYNO;!2!\=ϥIENDB`perroquet-1.1.1.orig/data/icons/256x256/0000755000175000017500000000000011561344350017664 5ustar georgeskgeorgeskperroquet-1.1.1.orig/data/icons/256x256/apps/0000755000175000017500000000000011561344350020627 5ustar georgeskgeorgeskperroquet-1.1.1.orig/data/icons/256x256/apps/perroquet.png0000644000175000017500000003726411561344350023377 0ustar georgeskgeorgeskPNG  IHDR\rfsRGBbKGD pHYs jtIME!)/] IDATxy|\UΖL֦I$mڴRR}pA*u|WQPU"|Ad-Ph(-If,=#!L;w&|^y̝sϹs}>y瀀lrY7'$cT_  2F|hjhTR/j]F"xQ#@|NNNVA0l _-.O5 < ]% B4Ѱor_|

;?@L\aߐ{([+(jJ[}=^_OWK3g!` 8=,m[^t1ǜy6UU+}A ǻi=x`)p{O " ͮ?`6' N),2T>x %.|&ASU(p|2(ZXũW/ _Sm Ph2/7jFD *o.6KI3v{wMp2)l& ]?\%E?)/|J`o/xLv6 O t"& ?mpPfm^5zڼ)ktbtbwfbt2Egckn L_V?O *׬ʻ!dsGC{%~baY~,? EoUx/ +!IRxR]U%mW-bO;AsOpS3H"l|/_G֬YSU5 It"|_䥪\6?tp0Pw;G;^$Uޥ9~x ŋ'ߒ$/@lw)BI>v}k{YG0!qHP'?9OyYJ*@ _6]ӳE_rJ;L*;#++K?rJ2.pK[Q/ WS.8﫶{Hݵ_V|d^Vp$I|{v*aR%,- /%'gJt=L=Z˪J5]YofOہ%9r$E|s?~,UC`\RE0ORBa1e8בS+1owpkc>낋zd\ $D@4?G= XRX&FiٟtuϤ~rTTdgOƕ ̀ }o "I5RIa9 ̨^Qv<5-ǏXp׽K$I_>᩽aHgk% +% <ïQ(Pzw_jԱeǐֹWZ)4-z ޒ|TI $5*3GYps|?5P Es?y=`lEB_P%SQ9Y qb4d/Ӣs?|+V32?~+<x$e+%U8d0OL>/^~U)۝'š  M-]U%)/Y&)DǜWj[OguUapt|ޭBG 1 >ir0+r5?ѱ.v` 7ae|{Zhjj1{lJJJd޼y\3g:oVみ\9Ij\y\lyv!{ y_?c[l>gYX2bSNxہMчM/̮+)(+ḰXfDDzWǜ/_3n}b+믿;w&,³>֭[ijjb͚5diEx=/<*5 ibqf~2"IYT'}stw/ ػW`k./_Ihmm={wdѢKր<pu2Xup‰yJ/$[V/k%&$>-ܹsbnޟ*'OG]  sFZG{=`ݻ.?dgg~z."-Z ^êO}zhykkk_ ˀ;09GK ՒdOtLS:~oO~!{7r@aYy衇pc= ciWy<.A-1n= B~Lvfk+Vr̓(߱c'x"7o'?Je˖Ԥ\{=\.W/"Cx<; &2ppK `x)sf\zdeeq}a`b… ~?gqFxB<|_]]Tq3@MLAT9Q2dK-Zֹ%O<< _~9G'\pEEv;o~3a,AX['nw ~gLJCŸ-w.9jFGVҞtZc۠~D/kn{ L̬(ID)4*hlw~"S @iOXwLUՉh( HOgU X'X:BO;_Ș1 /D"^}U{/l͗^zQZZZ -&{vS@ꄿ|Ȭ:sQDǒdp8p@t޽{yw (8? d,?s[nU@r>C<kA5އj3G8rH?׻( _PU5vG}/,,k!FGnw B,6>kcxmM q饗vZϟOFFuQz|ͼ2UW]şe[[[Kx饗nmP(IJeG9y"749a7% X- Z:̬?e׿߂LI:t|;{¢E'?Ijs7|37tӸ㧜r gqw---ر?O'gyf{ggg$ PUUqz!#~#g ąx0euLN:i؆ƮB!n&~300`UVqwrJqwrWzW#׭( +²e/o_әgkʕLd8N:֭[g9M~_",T!i Zh8~ \xx^x .q6UصkvzUUk222Ru|/~Ӂ0 ,LɇTNO$Hԃ.,bӫJOFQ%( \/bΗIJRT86F.}WҖ'4^OOO?, <@̌=Fկ~uT@ @__)uyffҽ5n{&Ml%ZI7Y4UU5|>M?n]viS999<#\{ w֭[կ~RnS㢢"6oL~~SvOH8KRY."B*OCPU:?cJt?o9f>W'di*Xhjj;7 vms=_G P\\_7Qx -lr$e>*+и=&{E_um{ڔ~=nbݺu^<\ves9fECCC2uuuB!~ӟ[|;w.#W\x<uQ?$...fz󁿻CJi*KQY#iXcVh2畳~ o+,ƚ{/0U/Iffy饗(++c…^:555vLnӻsbMCgjDD:6ՙH};xg傄[ϴ2@|>dYpa>cd.HSv?x&@8C@BY/*YT{f@AA6-- .O& z1H?8vK-0e׊DUUr̰'!}߉ۀ+ݶYXf̈́~{9EAQ:;;ijj2ެ۷oz@eN8aBQDNcc#9"䭭HKK >//դp8׾wfn{ M<p :$$9OTﶘ┚hW\G͛7OX&c˖-#'''< 炙^'L(^2fϞ=* Yn2 PHyTr{ߞG?Q)dؽ|Jľ_nfMDի9RcRwD갇~+3m  p˧ٟy .H^y|xqwqgFjǒYHg",]]vѫ=ۺjkk /\'QNH2S*;!|~'z+zjRjr~?ɮ7 p8ظqB$D~2Mߑ|BI!7O o߬1MH/_nꍖe-[h#k׮HKa$aR?>\K>qJ&W9Q,JJZ x/WG VB ߛYf /63?UWibq i… ([:- `xlTCt>Hᇗf@v·6$|v;\r YYYرP 6x8Dm??qUVV&dç7^aZs]iM\y h6`DLu֯>>P^<<.8"gqJ$N:$/o9dvw[n_"B!~G|?я=-H[999Z Kb,B8lMhZ"Dyb!P>denVz)<MMMt:)))aΜ9lذ;j<߼<##ßrj?ޱ>]viMP2 ^GzF@:H %:]~*@R~7TA?7nvZN9Iɬw챝;w 9 <fMN e1, Kßwm7eࡇ? .l$l]*b]իY@RtYJVF9,uמd/&|550KO|EU^{QY())I[/уzHFUU>fmO'&Wlg$H:̱A f >a4tc+L[|(w…TUUMY]2ɡf-ݚ 4jH:qf 8T j?`_\2@}b_ո@(OM;B|`ŊN3K/i5I%M15e%0]<ޱyvE0?Òӊ^xq?ER~Mg{,;;;2yڷL4୉Q*)C} ISj1hި 19w96rZSO=u𬬬t͌kZ逸[o58jrkN{3Q@n~gE᥉ i6Ҭk%?8ȷ(?m,/^aCp۷K줨,nT9ŋu/1{#&W^gĒ\Yq~co/}yP0#iҨ8ȝ/oc,,rUc(店_~W^f~{Pf󪫫#iNG A$rKTWW#OH`H7sW hRYe_>wpcc>eҢ[&* *Aïux`SE"ۯm7\˅(i3 VX?uJlb46N.6z~F6H+;wdڧ/߾kddJ0/Y/0Co}Rv`(,?j=Q5%#?`{ &'@g:OUUW[}mAQt5p_J r4鰖?m-_\+lpY7)ɕ8~f;ϑ0% 1Zf#r5@(Xyy9E3(IDAT$K/'ڀ:ɲ'Ṵ0!| hyyy 3AbL3&W^e"I+43!P !ɀﻄvX;IR$ N'fR0b?ttq؍G! ]ڂ@L5c!2YYY9'[S}MZm^x46 I0WM3O @2B>4k tPSYonn.yyyRř 0FN̑T%UxU d@#L U'rQXXHaa!i}]ZZZrQYYIaaaɫݻwk%[\z nPErL;UX;^>KZ4xS~]ټY.CVvv6QPP0jtz{{ٶm]]]RN:餘$ :t>  JD9&Lrs@ҥKfa f~zzz8JKK)++#''gU~UU3W#G׿inOSJ0wO C<`d+3r~O-uZxvep(H3} fSS w 줾f X,@"Nx4662|dYNIhmm语}(!X̸s1it^2 9jFQ]O;m ~zxUUE<9r$ GiiiJ*p!-XV]]Z_ .-T2%u xc+4q k :sX+˼׳ "X?---Ӄ0LTeggc舙h> VkW fddgm8<0*L7qf\cY294zFϯ/Vxw[J0YV#?TCUUzzzhhhNfffҖPQQVeggmdgF}}=V[[Ǩ*7 ʾ~3yD:e] >faؤzN{{;999STTA0)hXXd ݻ7=00`2XjM,@x5&o* hfIamA,8*MnAOOO\9Qtww[oIyy9%%%xΊ+f޽e֬Yq ]dKCb%9 MmhON(G"%oAMt:fܹȲ(̡CI۷0o<ʰX,IJroAff&ͣ a#? %LX zo,QCxS$5(6DGЏqD)[=%m(**cf!2${ֲgm`…%M lDyG4q#x_dDr%+S{nۗ!-5O$@dEf+7o^RT~-岲X`P(tttt:liӚ?  J NI?k+Ho;9tFBiO9}e1rX֤ɲLII Ŵ188tdq8i4}25#咕QN"4'Je^ZdЪOui Ў>G{WǴ 7Ů70N'UUUۛ(Bχbdl^~W&"1.P#g:2jW[JYOEL7o޸$V㕕$|(,,d``p^h3RGVhDh HqSdnV z|Byע/??y؆Cx)hŔطouuuIK@F0dΜ9+]vK ?؂aQq\YY`i()y Nc8Fy35EUUUȲӲn  Fi=8=Y޷P?\3$`#u}M慥Av~"9u,GNNyyy& Ɯ9sXx1 o* Ff%N-EFhEZ Jdv>Ġ]@bJ1Sz>Nrŋrp8 cX> zZ.KNȐK$ ǮCox~ ƣ|/S333#H[Ml* r***$>hjjȑ#zf[[[$)p's?ơ™ !Do^&7Вcc&(((,inѡ+;&%hӀW%轐,I%+ /lyyQph!UPq,2ѧ.#(I477Zk`&)B!W6')%C闰$ޙҕ7f{*$ YСC:Rv'EQR~,/3Ҡc HyP#1=Ij^rbžc_D^~7RVk}ziWl~Zח# @#z-#d=I OLeAyGE’H1nu*-g٘={6| Ȓ`0lZFˀMz?$A 8PvRCl-2;dH75N !oFKL[$HD=6tD7QnT੮'{챑|td-cȉvTf&_`|t +jDpE|0jw p-Hoc!Dn u.%jN91łfK{<m6.K?`xDԃA3$dy[v|(_?<'cL?HII eeer[~!L' AY^VXY;6h)?ר( vuɈ ךk" UH{ `NFґY=2y@ (FVO /_;cXemVl߾]k8A0б`&z V rhD( !KF&"eUUe߾}޽;ÝՆ@)@! ifj չ\]*'Ty5OacI7:L&,2Ґ0Caxn;Y1IFfA.RWWݻMuuvvFFZ,\x^RvNWolLzBLDb:iP|'HfEzRшwD4aL75gAݻ5S_VIcQ)yiEQ"- W`ZZlD&ӓP~=O["y<voa(fܑȔxma'd};Rs+iq"B((HjEȨ>,#?A(Kݫ-]z kq%+e D=1ϒPy-bZgӝcN#iہG=.<OZ0L-` cޑ:3i]L_ft= Mx۵bx_&WQzED(Z28iu2GT{Gxܹs5M  ܬgH `_d]Ce{teɠ͑m1qfWZ @딠t/`=>.9d̦#3VŚ=ϡ4 )A9@ B@ FJNOY=] pɐ#@V~gAg6,"cs?L> O ~xq4W@P<f*ШX28epUY&$dM3JR]]"dw+,k@p@ `xjԨL %€CQRl=&ƮFwt3sl__4tG<ŌOUM,n?[4Ҿαd0 Hň>\Mx<{Ǫ&*- 8])g@Ե YRNB 6B x}_ftq^ݘxVeẕL;Sl4?O|̲Qpv_c$b @ at2&,܍4.F:%݉fZ/rt0Adm6Gh `D_bnoP&4Eooodbxz"0oɈch&1&W`$xOF-1;kM} |Zxz2SRž3 {D/F# (DD?=W"n `b_5^_Ũf@10&&rקZ   ߸ﯢ'}&40>)Tkf!=O24CbW B\y2C~D!c|gEYG>{8ѤZׯW}J^|{DJ<O&A'wsBL(K i~UIx}Db9E?9^q#}+z0 kPFYzP=L{(l CxF{/mn}VtZ8׀bVu'$T- @Cä7ehSxtRC ^莤AS6hSVX6g¯aRph +,,Ax+l $Vts^3nZ#W)3TmA3^ah+{7vHp@ΧGw¯G5GX~U\3~cC Iһ[:^=?#`b_c`1TR (uã[z?=w`iE^cf( 44lc]mh x[i!f%ȋʏqYum yV!d`{BhMSylXMx<".B Uuth '"H˯ v0Pv!kB=O:''ٱ˲pX,6L&{4}YKMB! lss2YNQ]U8_?555E>Lv8CO6`#L&,`?33S+7PPXNU@%ʖ.S_HL~!L]c#|)])"A:O6i+wR+lav8yOBb~yII7T+IӤi2 :;U6͔J%<+QPБ3{/)`U9JaˀIENDB`perroquet-1.1.1.orig/data/icons/22x22/0000755000175000017500000000000011561344350017502 5ustar georgeskgeorgeskperroquet-1.1.1.orig/data/icons/22x22/apps/0000755000175000017500000000000011561344350020445 5ustar georgeskgeorgeskperroquet-1.1.1.orig/data/icons/22x22/apps/perroquet.png0000644000175000017500000000204211561344350023177 0ustar georgeskgeorgeskPNG  IHDRĴl;sRGBbKGD pHYs"xtIME"+r[IDAT8ݕKhUofK:$4.PZњ)$X\$RRJ]XB`[Cf0#AmLyyg{]t֥ws^c5{`б#<!mBb9@3A.CCCtttCѣnm^S~!!9`b"RI MGJIS6y|er  3E}sK%t]'LdX]]1::>Z^z8x3I-nŶ#}JOntK7fd2d24iL& h>TBnJ R4yn>F\e%&4_k־Ma?+-g::աoWTAhw݃N05=3(qT >VvZ02##x7 t; ~<-YM6\2u^ߘf?& b#ꑘ>/V5|,56AlD5 )g|Сғ 6AlD-\BۍzS}kgqqMQ[_nӼ6њm%H yfJLI<67VbFTHͶ%&_sx+Ib?ZS¶eC7L]ؿYDW;Yz&0zzr}ŲHgcGn?@0͊MHvs[!m؈Tr~<.pQjr%${V %Yݝ6af0N*ӳ1@|z(bl6zvlᇽU#ժ&.)Ҵ >~8Z[Ua?0ĕw`o%={wLGRokZ<"8j䀋nje, 1$N̼\fz3ejJ|2I ~LvO:1Ū ! Yױ2#e$ڲp~#FN^Wt&7-'ם3!TN  ɁΌ;N /~gI;3>n!гm} c$GAľiފ%A >(aTo6p3'{vۗ"mG] q>:`M5UDK?0\J3{pI|刑̈I"z%=//G̼ݞv p>;JYz&}b%S  oaaN53O4 E.w[x=(8ƴ"#C #'QW{xNsRC}Td Irmh9u_V)\{*4ޝ{kP-:D \9  o"Dl`@O=jԅCVV~ l'Or&9/"HxV,9!Fg0Yi01}ްPe_Y^-ҡ(q06AAQQf^ QJOzdㄴBQS}:W@qYlKV$Zq?𱟏_D 59$`NM.,_<@ 3G9"'YfsTPEQ˶f&g͍z,[,YJY`]Wb˝o$ /~Kz,8%˯Bl(GG'IY3BJ@:QTUWjf#kvh{CErn]!*+2s ljuebŧ夅[n,&z$>={TT^ZQ |hįi1 LLϼ&d)ͫJApS~jj# bŊBMsl4VeeԜ!!ѳ^K+W\`URR9W:GC} P,yHCn# oUR2vk;lݚq7JpgkҼB2)@K{ދlAo:[E+W^ADD )XČcP]Uzͳ _K gu-V0𽾞&%eMZreXYoXN!6WZ64|TJG:.#]." ; WU. ArM6s4V.wC?G `x^@ׇOah"u^ް%[oTyV;Wh5fg{Po7,kg( _{Y)CMDت?e=d&(R8uEe|/!W%sH&zA3Z 3]OH"g"Ⱥu\DT`ueE<܍ʎimڏbs1&鳿hD=7\d͚5Rr3x=/x*H%3)Pg ͎?XU ^e@у+'"z,=i(KnGdT{w/+gBOVGЍhY:$57o AHg!ANg+ΝEބtg֓-%cVה1o AJKK/R1UdJ|tJzэ:{=`E2ڠO{-#͛/p8eDD< ^>efCp4xV"PK tfZB7a&=g.'Vh+5eFv3]馻Y7z 9a$9Q҂*o"Vf%F%"0&ՙ<"J@]3q`uR[Sod$,㏇8!QY) ?Č͍ ˢ|6ppՠ/N84m(A6m66dpˠ AՑ&oj"m70TUU ʲl3CWϯGJ~uJqwPU:u޳n=I2@Jp4&۾+g\`d$`yfGl1+ϜUzCUCՒ^w(A;ڜdbzx0TRNpW[aTS׷[qnR0&LČJM.wו [] ]:b qgS`(` }~ky"|) zە̩V$XtF-۟׫՗m[tCh(3C "Heee_)ZfCqj=].x4X[_th(Pvɚ RYYtp IJ\5WNt,,]kJ G} wRʇR.%qf>RGtaE32#㶝Ahۻh˝Ӣ$,_G%tA¦kM3_11DDWL]gŢ@t^ Nh#G͍),ըh !G/(q= lE ۺCDDⷛ3qdJ bQAj9RUaXLw꧰br|ap'ѦWL  4hlAX7l-$&xdaRuF ӏxiG;N7ю~m?5ΘVi̽^:N2i;v՜\2iD(BY"~g%}}Eeȡ6Ackś43DIL2 V HYwt/=HdOɈT0 &*Mpθn0&b<}5#/SLEGA n[jU :cqXZl ]ZӬ c(T:7h,?M*LiI\h)*ߥ0-f6 5ڕR2YT0JV6M.G@;Grt9 s:DJ@bMMb@|g&KtU2i.eJ;ec |da6F\ 7cfF\ƍ ]]75jY(n,K+&wEe.Vp fPH fϛT0pPMOR"UUu4I:d9n=mte-@l2NHeh HAu㪐gX9j8&fV) $$Һmgphu0,!FMHI*|Mx4 V{-bz&6opSA~mz_ Va:L,lKOaSq6BlAaov} $zj9a~&hRӵ-Ch:%Y9Fc\,_=ALُ@}b=G=Pco2 ta (z*Ε9@ H:: |,! ~rⓖ̧YS> @(K޲7@Zv>Xk:]w8f32uafb <#))RUUU*5QB{=P( 6n0E!~ukL8 j|bUFUSD010O " [RlBs'v;J1b. Lqx5TuT]iJ~C,,h dq?2H5ɵݑ$\2a+nNV,C փ㎮6* եR6qB\l AvLHIIDD8“ZN?--]WZ_Lܴ.DXǪG8wtⅆ9yG0elZ8輸 m:*XJBT /TDHYETm!%󰽵F-[Y̜0"mXT:ފjY`[4ʋu>] !Y?`f84g?pZ+i3W핤K?I~"cE韓*:aر Is2xuMIa).[^E2fnh2?v&RiW5:X۷,lCOlAN% $9wlTM=r4B[^]oiV,9bA`ܸqI:= =<֨_Z1FzEXѦ1L2Ijno0& *׏Ѯ[gMsڇg^iSd2½` A[nDa=SXhf:N(cu 5)_i@p~4$ tGis3*g%&G'x ݖ3F& gn5Ԅ *hbtD 0G" /.Yt$G 5\@<ZSP BrMudFF Wuv82@ZZV"J$!p<%J840ALl< Zt&u԰R&Lj:YJbՊ1!5׏ĴKV-Kz<˩ZK6zۄz7w> 4(tהY9 A^bs1+VLޯ A`ĉU<wL'*YUc3(AZY޸?98_b"L2|Ips_3|#,ʺ)rߌgK#32bwNYn H!IJ<'{%5,ANk$99'sOVTF ݐ'-(BI@p31<޸߆ 3/U:7awGiӾIn ч,,4Zmʹ}>垠 a9.0k֬/F6 g3 ΏPB B Nr;}/UUMIr fzg  y{BQ%Aí\gskK 89={][ 6#N냆9r(N B~8eϘ1+$ݩ>?{{g?^zHTsv$uDK-/ Y€AOkܿ ~c&tW<.V]<#^a+уZGpiso"¶xe7F9Ҳop^W_ft$Lȑʝz#DI2x]SF5[lɉ~f.dŲa9α촑. c4S{xL޲WͮMV=<u5VT& +c{3WM<)hnaO~e~UISCOvn#hYx6r08ob\o_ׅ7-+ĆJnȡKoi Yl Wz΍` ݫ'Ƭ2q;a=220/:)bEr. SpxBFuBj Cζe$O(C5ab0K `|p b0,0Z*on6)) ZDoyIENDB`perroquet-1.1.1.orig/data/perroquet.desktop.in0000644000175000017500000000032611561344350021637 0ustar georgeskgeorgesk[Desktop Entry] Encoding=UTF-8 _Name=Perroquet _Comment=Oral comprehension teacher Exec=perroquet Icon=perroquet MimeType=application/x-perroquet; Terminal=false Type=Application Categories=Application;Education; perroquet-1.1.1.orig/data/perroquet.ui0000644000175000017500000015610111561344350020201 0ustar georgeskgeorgesk 800 600 True True Perroquet True vertical True True _File True True gtk-new True True True gtk-open True True True gtk-save True False True True gtk-save-as True False True True True Import a package True image7 False True Export True True Export as template... True image4 False Export as package... True image6 False True gtk-quit True True True True E_dit True True _Exercise manager True True image2 False gtk-preferences True True True True _Exercise True True Reset exercise progress True image5 False True gtk-properties True True True _Advanced properties True True image3 False True H_elp True True True Lateral panel True True False Translation True True Correction Hint True False image1 False Reveal the word True False image8 False Reveal the sequence True False image9 False True gtk-about True True True False 0 True True New exercise New exercise True gtk-new False True True open exercise open saved exercise True gtk-open False True True False Save exercise Save exercise True gtk-save False True True False True True False Previous sequence Previous sequence True gtk-go-back False True True False Next sequence Next Sequence True gtk-go-forward False True True False Replay sequence Replay sequence True gtk-refresh False True False Pause Pause True gtk-media-pause False True False Play Play True gtk-media-play False True True False True True False Hint Hint True gtk-dialog-question False True True False Show/Hide translation Display translation True gtk-spell-check False True True Show/Hide correction Display correction True gtk-ok False True True 50 True False True Play speed adjustmentSpeed 200 False False True False True Exercise properties Properties True gtk-properties False True True Reset the exercise progress Erase True gtk-clear False True False 1 True True vertical 269 True True 522 True True gtk-close 0 True 1 False True True True True vertical True queue True Progress statistics - Sequences: --/-- - Words: --/-- - Repeat ratio: -- per words True False 0 True True Word filter This filter use regexp. Examples: abc = all words containing 'abc' ^abc = all words starting with 'abc' abc$ = all words ending with 'abc' The character . can replace any character: ^abc.. = all words of at least 5 letters starting with 'abc' ^...$ = all 4-letter word ^ab.c$ = all 4-letter word beginning with 'ab' and ending with 'c' For more information on regular expressions: http://en.wikipedia.org/wiki/Regular_expression False 1 True True automatic True True List of all words to find in the exercise False 10 False 2 False True Words False True True automatic automatic True True 1 True Last open files 1 False True True True True 300 True True automatic 500 True True word center textbufferView False True 2 True automatic True True Translation 1 False word center False 4 True True False True Position in current sequence adjustmentSequenceTime 200 False 0 True Position in current sequence -- s False 8 1 True vertical False 2 True False True Position in exercise adjustmentSequenceNum False 3 True Position in exercise --/-- False 8 4 False 5 400 5 New exercise True center-on-parent True normal True MainWindow False True vertical 2 True True The audio track should be in the exercise language Exercise video or audio: 0 True The audio track should be in the exercise language False Select a video file 1 1 True True These subtitles should be in the exercise language. Supported format: srt Exercise subtitle: 0 True These subtitles should be in the exercise language. Supported format: srt False Select a subtitle file 1 2 True True These subtitles should be in your mother language. Supported format: srt Translation subtitles (optional): 0 True These subtitles should be in your mother language. Supported format: srt False Select a subtitle file 1 3 True True Exercise language Language: 0 True 1 4 True end gtk-cancel True True True True False False 0 gtk-ok True True True True False False 1 False end 0 buttonNewExerciseCancel buttonNewExerciseOk 100 1 10 10 100 1 10 10 True gtk-dialog-question 5 center-on-parent normal True MainWindow False Perroquet 1.1.0 Copyright © 2009-2010 Perroquet Team An oral comprehension teacher http://perroquet.b219.org 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 3 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, see <http://www.gnu.org/licenses/>. Frédéric Bertolus <fred.bertolus@gmail.com> Matthieu Bizien <matthieu.bizien@m4x.org> Quentin Theuret <quentin.theuret@gmail.com> Eric Noulard <eric.noulard@gmail.com> Vincent Grangé-Pradéras <vincent.grange@gmail.com> Quentin Theuret Zarmakuizz Frédéric Bertolus Alessio Carminati Jacopo A Sgab jebbo zeugma Bora Akbay Carlos Alberto Ospina DiegoJ Jonay bixente Pukable Treecko True True vertical 2 True end False end 0 100 75 100 1 1 True gtk-missing-image True gtk-properties True gtk-missing-image True gtk-convert True gtk-convert True gtk-missing-image True gtk-dialog-question True gtk-dialog-question perroquet-1.1.1.orig/data/properties_advanced.ui0000644000175000017500000011152411561344350022174 0ustar georgeskgeorgesk 100 0.10000000000000001 1 999 1 10 5 Exercise properties True center-on-parent True normal True False True 2 True True True True True Exercise name: 0 True True 3 1 3 0 True True Language: 0 True 3 1 3 1 Disable help tools True True False True 3 2 True True Repeat count limit by sequence (0 for no limit): 3 0 True True 2 False False 10 1 3 True True True Lock exercise's correction True True False True 0 True True Correction password : 0 True True 1 1 Lock exercise's propreties True True False True 2 True True Properties password : 0 True True 1 3 True Locks 4 True Exercise False True 5 10 True True Media: 0 True False Select a video file 1 False 0 True True Exercise: 0 True False Select a subtitle file 1 False 1 True True Translation: 0 True False Select a subtitle file 1 False 2 True True True True True 0 0 True gtk-add True True True True False 0 gtk-remove True True True True False 1 gtk-go-up True True True True False 2 gtk-go-down True True True True False 3 False 1 True Paths list 3 1 True Paths 1 False True 5 True 5 True Time between sequences (s): 0 True True adjustmentTimeBetweenSequence 1 1 gtk-redo True True True True 3 2 3 0 True 5 True Maximum sequence time (s): 0 True True adjustmentMaximumSequenceTime 1 1 gtk-redo True True True True 3 2 3 1 True 5 True Playing time before sequences (ms): 0 True True adjustmentTimeBeforeSequence 1 gtk-redo True True True True 3 2 3 2 True 5 True Playing time after sequences (ms): 0 True True adjustmentTimeAfterSequence 1 gtk-redo True True True True 3 2 3 3 Repeat sequences after having completed it True True False True 4 Play sequence in a random order True True False True 5 Use dynamic correction True True False True False 6 2 True Sequences 2 False 1 True end gtk-cancel True True True True False False 0 gtk-ok True True True True 0.52999997138977051 False False 1 False end 0 buttonExercisePropCancel buttonExercisePropOk 1000 10 100 5000 10 100 perroquet-1.1.1.orig/data/perroquet.xml0000644000175000017500000000077711561344350020373 0ustar georgeskgeorgesk Perroquet exercise file Fichier d'exercice Perroquet perroquet-1.1.1.orig/data/languages.list0000644000175000017500000000023611561344350020454 0ustar georgeskgeorgesken English 0-9\'a-zA-Zé fr Français 0-9a-zA-ZàÀâÂæÆçÇéÉèÈêÊëËîÎïÏôÔœŒùÙûÛüÜÿŸ es Español 0-9a-zA-ZñÑáÁíÍóÓ¡!?¿ perroquet-1.1.1.orig/data/exercise_manager.ui0000644000175000017500000003766311561344350021467 0ustar georgeskgeorgesk 750 350 5 Perroquet exercises manager normal False True vertical 2 True True True vertical True vertical True True never automatic True True 1 0 True True 1 0 Use True False True True False False 1 False 1 1 1 0 True True True True automatic automatic True True True Details False 1 True Exercises False True vertical True True liststoreRepositories 0 True False 1 True True True 3 0 gtk-add True True True True False 5 1 gtk-remove True True True True False 2 False 2 1 True Repositories 1 False True vertical Tree view mode True True False True 0 2 True Settings 2 False 1 True end gtk-close True True True True False False 0 False end 0 button1 True True 5 True normal dialogExerciseManager False True vertical 2 True True 2 True end gtk-close True True True True False False 0 False end 0 buttonInfoClose perroquet-1.1.1.orig/data/languages_aliases.ini0000644000175000017500000000051611561344350021762 0ustar georgeskgeorgesk [stringlistlist] synonyms.en = 0::zero 1::one 2::two 3::three 4::four 5::five 6::six 7::seven 8::height 9::nine 10::ten ok::okay synonyms.fr = 0::zero 1::un 2::deux 3::trois 4::quatre 5::cinq 6::six 7::sept 8::huit 9::neuf 10::dix synonyms.es = perroquet-1.1.1.orig/data/gui_message_dialog.ui0000644000175000017500000000410511561344350021756 0ustar georgeskgeorgesk 5 False normal False True 2 True label 1 True end gtk-close True True True True False False 0 False end 0 button1 perroquet-1.1.1.orig/data/settings.ui0000644000175000017500000007337511561344350020026 0ustar georgeskgeorgesk 100 0.10000000000000001 1 999 1 10 5 Settings True center-on-parent True normal True False True vertical 2 True True True vertical Skip valid sequences using next and previous sequence buttons True True False True 3 0 Use speed changer True True False True 3 1 True General False True vertical True True True vertical 5 Show menu bar True True False True 3 0 Show settings True True False True 3 1 Show play and pause buttons True True False True 3 2 True Interface False True vertical True True Exercise type: 0 True 3 1 3 0 True True Language: 0 True 3 1 3 1 True 5 True Time between sequences (s): 0 True True adjustmentTimeBetweenSequence 1 1 gtk-redo True True True True 3 2 3 2 True 5 True Maximum sequence time (s): 0 True True adjustmentMaximumSequenceTime 1 1 gtk-redo True True True True 3 2 3 3 True 5 True Playing time before sequences (ms): 0 True True adjustmentTimeBeforeSequence 1 gtk-redo True True True True 3 2 3 4 True 5 True Playing time after sequences (ms): 0 True True adjustmentTimeAfterSequence 1 gtk-redo True True True True 3 2 3 5 Disable help tools True True False True 3 6 Repeat sequences after completed it True True False True 7 Play sequence in a random order True True False True 8 True True Limit repeat count by sequence (0 for no limit): 3 0 True True 2 False False 10 1 9 1 True Default exercise properties 1 False 0 1 True Advanced 1 False 1 True end gtk-cancel True True True True False False 0 gtk-ok True True True True 0.52999997138977051 False False 1 False end 0 buttonExercisePropCancel buttonExercisePropOk 1000 10 100 100 5000 10 100 100 perroquet-1.1.1.orig/data/config.ini0000644000175000017500000000140111561344350017552 0ustar georgeskgeorgesk[int] autosave = 1 showlateralpanel = 0 repositorymanager.displayonlyexercises = 1 default_exercise_time_between_sequences= 0 default_exercise_max_sequence_length = 10000 default_exercise_play_margin_after = 500 default_exercise_play_margin_before = 1000 default_exercise_help_tools = 1 default_exercise_repeat_after_competed = 1 default_exercise_random_order = 0 default_exercise_repeat_count_limit = 0 default_exercise_dynamic_correction = 1 default_repeat_count_limit_by_sequence = 0 interface_show_play_pause_buttons = 0 interface_lock_settings = 0 interface_use_speed_change = 0 navigation_skip_valid_sequences = 1 [string] lastopenfile = default_exercise_type=comprehension default_exercise_language=en default_debug_level=INFO [stringlistlist] lastopenfiles = perroquet-1.1.1.orig/data/gui_password_dialog.ui0000644000175000017500000001002511561344350022172 0ustar georgeskgeorgesk 5 Password for unlock correction normal False True 2 True True 5 Correction password : 0 True True True False 1 1 True end gtk-cancel True True True True False False 0 gtk-ok True True True True False False 1 False end 0 buttonResetCancel buttonResetOk perroquet-1.1.1.orig/AUTHORS0000644000175000017500000000205511561344350015751 0ustar georgeskgeorgeskAuthors ======= Frédéric Bertolus Nickname: Fredb219 Description: Initial programmer Website: fred.b219.org Email: fred.bertolus@gmail.com Country: France Matthieu Bizien Nickname: OAO Description: programmer Email: matthieu.bizien@m4x.org Country: France Quentin Theuret Nickname: Quentin Theuret Description: Authors of few patchs Email: quentin.theuret@gmail.com Country: France Eric Noulard Nickname: Erk Description: Authors of few patchs Email: eric.noulard@gmail.com Country: France Vincent Grangé-Pradéras Nickname: Bixente Description: spanish support Email: vincent.grange@gmail.com Country: France Translation =========== French translation: Quentin Theuret Zarmakuizz Frédéric Bertolus Italian translation: Alessio Carminati Jacopo A Sgab jebbo Turkish translation: zeugma Bora Akbay Spanish translation: Carlos Alberto Ospina DiegoJ Jonay Quentin THEURET bixente Swedish translation: Pukable Treecko Perroquet Team ============== perroquet-team@lists.launchpad.netperroquet-1.1.1.orig/runTests.py0000755000175000017500000000167211561344350017111 0ustar georgeskgeorgesk#!/usr/bin/env python # -*- coding: utf-8 -*- # Copyright (C) 2009-2010 Frédéric Bertolus. # Copyright (C) 2009-2010 Matthieu Bizien. # # This file is part of Perroquet. # # Perroquet 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 3 of the License, or # (at your option) any later version. # # Perroquet 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 Perroquet. If not, see . from test.testConfig import * from test.testSequence import * from test.testWord import * import unittest if __name__=="__main__": unittest.main() perroquet-1.1.1.orig/test/0000755000175000017500000000000011561344350015656 5ustar georgeskgeorgeskperroquet-1.1.1.orig/test/testConfigWritable.ini0000644000175000017500000000015711561344350022161 0ustar georgeskgeorgesk[int] firstint = 5 sndint = 8 [string] christmas = merry Christmas and happy new year easter = nothing :( perroquet-1.1.1.orig/test/testWord.py0000644000175000017500000001025211561344350020043 0ustar georgeskgeorgesk#!/usr/bin/env python # -*- coding: utf-8 -*- # Copyright (C) 2009-2010 Frédéric Bertolus. # Copyright (C) 2009-2010 Matthieu Bizien. # # This file is part of Perroquet. # # Perroquet 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 3 of the License, or # (at your option) any later version. # # Perroquet 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 Perroquet. If not, see . import unittest from perroquetlib.model.sequence import * from perroquetlib.model.languages_manager import LanguagesManager language = LanguagesManager().get_default_language() def word(text): w = Word(text, language) return w class TestLevenshtein(unittest.TestCase): knowValues=( (("", ""), 0), (("", "cheval"), 6), (("ch", "cheval"), 4), (("aa", "brebis"), 6), (("e", "brebis"), 5), ) def test(self): for (w1, w2),numeral in self.knowValues: result = levenshtein(w1, w2) self.assertEqual(numeral, result) class TestWord(unittest.TestCase): def test_usuals_func(self): w=word("batman") self.assert_(w.is_empty()) self.assertFalse(w.is_valid()) self.assertEqual(w.get_valid(), "batman") w.set_text("bat") self.assertEqual(w.get_text(), "bat") self.assertFalse(w.is_empty()) self.assertFalse(w.is_valid()) w.set_text("batman") self.assertFalse(w.is_empty()) self.assert_(w.is_valid()) w.set_text("tt") self.assertFalse(w.is_valid()) w.complete() self.assertFalse(w.is_empty()) self.assert_(w.is_valid()) def test_show_hint(self): w=word("joker") w.set_text ( "jo" ) for i in range(4): self.assertFalse(w.is_valid()) w.show_hint() self.assert_(w.is_valid()) #self.failUnlessRaises(ValidWordError, w.show_hint) def test_write_char(self): w=word("robin") for i, char in enumerate(w.get_valid()): w.write_char(char) self.assertEqual(w.get_text(), w.get_valid()[:i+1]) w=word("spiderman") for i in "spi": w.write_char(i) w.set_text("spi"+w._helpChar*6) w.write_char("d") self.assertEqual(w.get_text(), "spid"+w._helpChar*5) w.set_pos(9) w.write_char("m") self.assertEqual(w.get_text(), "spidm") w.set_text(w.get_valid()) #self.failUnlessRaises(ValidWordError, lambda: w.write_char("r")) def test_set_pos(self): w=word("superman") w.set_text("suprman") w.set_pos(3) self.assertEqual(w.get_pos(), 3) w.write_char("e") self.assert_(w.is_valid()) w.set_pos(-1) self.assertEqual(w.get_pos(), 8) self.failUnlessRaises(NoCharPossible, lambda :w.set_pos(len(w.get_valid())+1)) self.failUnlessRaises(NoCharPossible, lambda :w.set_pos(-2)) w.set_pos(len(w.get_valid())) def test_delete(self): w=word("angel") w.set_text("XYangel") w.set_pos(1) w.delete_next_char() #Y w.delete_previous_char() #X self.assertEqual(w.get_text(), w.get_valid()) w.set_text("WangelW") w.set_pos(0) self.failUnlessRaises(NoCharPossible, w.delete_previous_char) w.set_pos(len(w.get_text())) self.failUnlessRaises(NoCharPossible, w.delete_next_char) w.set_pos(1) w.delete_previous_char() w.set_pos(5) w.delete_next_char() self.assertEqual(w.get_text(), w.get_valid()) if __name__=="__main__": unittest.main() perroquet-1.1.1.orig/test/testRepo.py0000755000175000017500000000463411561344350020047 0ustar georgeskgeorgesk#!/usr/bin/env python # -*- coding: utf-8 -*- # Copyright (C) 2009-2010 Frédéric Bertolus. # Copyright (C) 2009-2010 Matthieu Bizien. # # This file is part of Perroquet. # # Perroquet 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 3 of the License, or # (at your option) any later version. # # Perroquet 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 Perroquet. If not, see . from perroquetlib.repository.exercise_repository_manager import ExerciseRepositoryManager if __name__=="__main__": manager = ExerciseRepositoryManager() print "get repository list" list = manager.get_exercise_repository_list() print str(len(list)) +" repository found:" for repo in list: print "/////////////////////////" print repo.get_id() print repo.get_name() print repo.get_local_path() print repo.get_version() print repo.get_description() print repo.get_type() print repo.get_url() for group in repo.get_groups(): print " "+group.get_name() print " "+group.get_description() for exo in group.get_exercises(): print " "+exo.get_licence() print " "+exo.get_language() print " "+exo.get_media_type() print " "+exo.get_version() print " "+exo.get_author() print " "+exo.get_author_website() print " "+exo.get_author_contact() print " "+exo.get_packager() print " "+exo.get_packager_website() print " "+exo.get_packager_contact() print " "+exo.get_file_path() if not exo.is_installed(): print " "+"Not installed" """exo.start_install() print " "+"Install started" exo.wait_install_end() print " "+"Install ended""" else: print " "+"Already installed" perroquet-1.1.1.orig/test/testConfigReference.ini0000644000175000017500000000031511561344350022302 0ustar georgeskgeorgesk[int] firstint = -1 sndint = -1 [string] christmas = wtf ? newyear = champagne [stringlist] liststr = one two [intlist] listint = 1 2 3 [stringlistlist] lls=a::1 b::2 c::2::ccc perroquet-1.1.1.orig/test/__init__.py0000644000175000017500000000000011561344350017755 0ustar georgeskgeorgeskperroquet-1.1.1.orig/test/testConfig.py0000644000175000017500000000564011561344350020342 0ustar georgeskgeorgesk#!/usr/bin/env python # -*- coding: utf-8 -*- # Copyright (C) 2009-2010 Frédéric Bertolus. # Copyright (C) 2009-2010 Matthieu Bizien. # # This file is part of Perroquet. # # Perroquet 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 3 of the License, or # (at your option) any later version. # # Perroquet 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 Perroquet. If not, see . import os import shutil import unittest from perroquetlib.config import Config pathRef = "./test/testConfigReference.ini" pathWritable = "./test/testConfigWritable.ini" pathTemp = "./test/testConfigTemp.ini" pathTemp2 = "./a/b/c/testConfigTemp.ini" class TestConfig(unittest.TestCase): def test_load(self): c = Config() c.load_writable_config_file(pathWritable, pathRef) self.assertEqual(c.get("christmas"), "merry Christmas\nand happy new year") self.assertEqual(c.get("newyear"), "champagne") self.assertEqual(c.get("firstint"), 5) self.assertEqual(c.get("sndint"), 8) self.assertEqual(c.get("liststr"), ["one", "two"]) self.assertEqual(c.get("listint"), [1, 2, 3]) self.assertEqual(c.get("lls"), [["a", "1"], ["b", "2"], ["c", "2", "ccc"]]) def test_save(self): shutil.copy(pathWritable, pathTemp) c = Config() c.load_writable_config_file(pathTemp, pathRef) c.set("firstint", 666) c.set("liststr", ["1", "2", "3"]) c.set("listint", [666, 666]) c.set("lls", [["A"], ["B", "BETA"], ["C", "CETA", "3"]]) c.save() c = Config() c.load_writable_config_file(pathTemp, pathRef) self.assertEqual(c.get("firstint"), 666) self.assertEqual(c.get("liststr"), ["1", "2", "3"]) self.assertEqual(c.get("listint"), [666, 666]) self.assertEqual(c.get("lls"), [["A"], ["B", "BETA"], ["C", "CETA", "3"]]) os.remove(pathTemp) c = Config() c.load_writable_config_file(pathTemp2, pathRef) c.set("firstint", 666) c.save() c.load_writable_config_file(pathTemp2, pathRef) self.assertEqual(c.get("firstint"), 666) shutil.rmtree("./a") def test_error(self): c = Config() self.failUnlessRaises(KeyError, lambda:c.set("MAJ", 3)) self.failUnlessRaises(KeyError, lambda:c.get("MAJ")) c.load_writable_config_file(pathWritable, pathRef) self.failUnlessRaises(KeyError, lambda:c.get("easter")) if __name__ == "__main__": unittest.main() perroquet-1.1.1.orig/test/testSequence.py0000644000175000017500000000641511561344350020706 0ustar georgeskgeorgesk#!/usr/bin/env python # -*- coding: utf-8 -*- # Copyright (C) 2009-2010 Frédéric Bertolus. # Copyright (C) 2009-2010 Matthieu Bizien. # # This file is part of Perroquet. # # Perroquet 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 3 of the License, or # (at your option) any later version. # # Perroquet 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 Perroquet. If not, see . import unittest from perroquetlib.model.sequence import * from perroquetlib.model.languages_manager import LanguagesManager language = LanguagesManager().get_default_language() def seq(text): s=SequenceDynamicCorrection(language) s.load(text) return s class TestSequence(unittest.TestCase): def test_simple(self): s=seq("the little cat") self.assertEqual( len(s.get_words()), 3) for w, w2 in zip(s.get_words(), ("the little cat").split(" ")): self.assertEqual(w.get_text(), "") self.assertEqual(w.get_valid(), w2) def testsimple_write(self): s=seq("a") s.write_char("a") self.assert_(s.is_valid()) s=seq("the") s._write_sentence("the") self.assert_(s.is_valid()) s=seq("the cat") s._write_sentence("thecat") for w, w2 in zip(s.get_words(), ("the cat").split()): self.assertEqual(w.get_text(), w2) self.assert_(s.is_valid()) s=seq("small birth") s._write_sentence("smallbirth") for w, w2 in zip(s.get_words(), ("small birth").split()): self.assertEqual(w.get_text(), w2) self.assert_(s.is_valid()) s=seq("a small birth") s._write_sentence("a small birth") for w, w2 in zip(s.get_words(), ("a small birth").split()): self.assertEqual(w.get_text(), w2) self.assert_(s.is_valid()) def test_next_prev_write(self): s=seq("a smart bird") s._write_sentence("a small+birth") for i in range(5): s.previous_char() s.delete_previous_char() s.delete_previous_char() for w, w2 in zip(s.get_words(), ("a sma birth").split()): self.assertEqual(w.get_text(), w2) s._write_sentence("rt") for w, w2 in zip(s.get_words(), ("a smart birth").split()): self.assertEqual(w.get_text(), w2) for i in range(3): s.next_char() self.assertEqual(s.get_active_word_index(), 2) s.delete_next_char() s.delete_next_char() for w, w2 in zip(s.get_words(), ("a smart bir").split()): self.assertEqual(w.get_text(), w2) s._write_sentence("d") for w, w2 in zip(s.get_words(), ("a smart bird").split()): self.assertEqual(w.get_text(), w2) self.assert_(s.is_valid()) if __name__=="__main__": unittest.main() perroquet-1.1.1.orig/utils/0000755000175000017500000000000011561344350016037 5ustar georgeskgeorgeskperroquet-1.1.1.orig/utils/reindent.py0000644000175000017500000002130111561344350020216 0ustar georgeskgeorgesk#! /usr/bin/env python # Released to the public domain, by Tim Peters, 03 October 2000. """reindent [-d][-r][-v] path ... -d Dry run. Analyze, but don't make any changes to, files. -r Recurse. Search for all .py files in subdirectories too. -v Verbose. Print informative msgs; else no output. Change Python (.py) files to use 4-space indents and no hard tab characters. Also trim excess whitespace from ends of lines, and empty lines at the ends of files. Ensure the last line ends with a newline. Pass one or more file and/or directory paths. When a directory path, all .py files within the directory will be examined, and, if the -r option is given, likewise recursively for subdirectories. Overwrites files in place, renaming the originals with a .bak extension. If reindent finds nothing to change, the file is left alone. If reindent does change a file, the changed file is a fixed-point for reindent (i.e., running reindent on the resulting .py file won't change it again). The hard part of reindenting is figuring out what to do with comment lines. So long as the input files get a clean bill of health from tabnanny.py, reindent should do a good job. """ __version__ = "1" import tokenize import os import sys verbose = 0 recurse = 0 dryrun = 0 def errprint(*args): sep = "" for arg in args: sys.stderr.write(sep + str(arg)) sep = " " sys.stderr.write("\n") def main(): import getopt global verbose, recurse, dryrun try: opts, args = getopt.getopt(sys.argv[1:], "drv") except getopt.error, msg: errprint(msg) return for o, a in opts: if o == '-d': dryrun += 1 elif o == '-r': recurse += 1 elif o == '-v': verbose += 1 if not args: errprint("Usage:", __doc__) return for arg in args: check(arg) def check(file): if os.path.isdir(file) and not os.path.islink(file): if verbose: print "listing directory", file names = os.listdir(file) for name in names: fullname = os.path.join(file, name) if ((recurse and os.path.isdir(fullname) and not os.path.islink(fullname)) or name.lower().endswith(".py")): check(fullname) return if verbose: print "checking", file, "...", try: f = open(file) except IOError, msg: errprint("%s: I/O Error: %s" % (file, str(msg))) return r = Reindenter(f) f.close() if r.run(): if verbose: print "changed." if dryrun: print "But this is a dry run, so leaving it alone." if not dryrun: bak = file + ".bak" if os.path.exists(bak): os.remove(bak) os.rename(file, bak) if verbose: print "renamed", file, "to", bak f = open(file, "w") r.write(f) f.close() if verbose: print "wrote new", file else: if verbose: print "unchanged." class Reindenter: def __init__(self, f, eol="\n"): self.find_stmt = 1 # next token begins a fresh stmt? self.level = 0 # current indent level self.eol = eol # Raw file lines. self.raw = f.readlines() # File lines, rstripped & tab-expanded. Dummy at start is so # that we can use tokenize's 1-based line numbering easily. # Note that a line is all-blank iff it's "\n". self.lines = [line.rstrip().expandtabs() + self.eol for line in self.raw] self.lines.insert(0, None) self.index = 1 # index into self.lines of next line # List of (lineno, indentlevel) pairs, one for each stmt and # comment line. indentlevel is -1 for comment lines, as a # signal that tokenize doesn't know what to do about them; # indeed, they're our headache! self.stats = [] def run(self): tokenize.tokenize(self.getline, self.tokeneater) # Remove trailing empty lines. lines = self.lines while lines and lines[-1] == self.eol: lines.pop() # Sentinel. stats = self.stats stats.append((len(lines), 0)) # Map count of leading spaces to # we want. have2want = {} # Program after transformation. after = self.after = [] for i in range(len(stats)-1): thisstmt, thislevel = stats[i] nextstmt = stats[i+1][0] have = getlspace(lines[thisstmt]) want = thislevel * 4 if want < 0: # A comment line. if have: # An indented comment line. If we saw the same # indentation before, reuse what it most recently # mapped to. want = have2want.get(have, -1) if want < 0: # Then it probably belongs to the next real stmt. for j in xrange(i+1, len(stats)-1): jline, jlevel = stats[j] if jlevel >= 0: if have == getlspace(lines[jline]): want = jlevel * 4 break if want < 0: # Maybe it's a hanging # comment like this one, # in which case we should shift it like its base # line got shifted. for j in xrange(i-1, -1, -1): jline, jlevel = stats[j] if jlevel >= 0: want = have + getlspace(after[jline-1]) - \ getlspace(lines[jline]) break if want < 0: # Still no luck -- leave it alone. want = have else: want = 0 assert want >= 0 have2want[have] = want diff = want - have if diff == 0 or have == 0: after.extend(lines[thisstmt:nextstmt]) else: for line in lines[thisstmt:nextstmt]: if diff > 0: if line == self.eol: after.append(line) else: after.append(" " * diff + line) else: remove = min(getlspace(line), -diff) after.append(line[remove:]) return self.raw != self.after def write(self, f): f.writelines(self.after) # Line-getter for tokenize. def getline(self): if self.index >= len(self.lines): line = "" else: line = self.lines[self.index] self.index += 1 return line # Line-eater for tokenize. def tokeneater(self, type, token, (sline, scol), end, line, INDENT=tokenize.INDENT, DEDENT=tokenize.DEDENT, NEWLINE=tokenize.NEWLINE, COMMENT=tokenize.COMMENT, NL=tokenize.NL): if type == NEWLINE: # A program statement, or ENDMARKER, will eventually follow, # after some (possibly empty) run of tokens of the form # (NL | COMMENT)* (INDENT | DEDENT+)? self.find_stmt = 1 elif type == INDENT: self.find_stmt = 1 self.level += 1 elif type == DEDENT: self.find_stmt = 1 self.level -= 1 elif type == COMMENT: if self.find_stmt: self.stats.append((sline, -1)) # but we're still looking for a new stmt, so leave # find_stmt alone elif type == NL: pass elif self.find_stmt: # This is the first "real token" following a NEWLINE, so it # must be the first token of the next program statement, or an # ENDMARKER. self.find_stmt = 0 if line: # not endmarker self.stats.append((sline, self.level)) # Count number of leading blanks. def getlspace(line): i, n = 0, len(line) while i < n and line[i] == " ": i += 1 return i if __name__ == '__main__': main() perroquet-1.1.1.orig/utils/pep8.py0000755000175000017500000000354511561344350017277 0ustar georgeskgeorgesk#!/usr/bin/env python # -*- coding: utf-8 -*- import re import sys from lib import apply_to_file def set_blanks_lines(string): #no more than one blank lines between methods string = re.sub(r'((\n|^) *def(.|\n)*?)(\n *)*\n(?= +def)', r'\1\n\n', string) #two blancks lines between class definition string = re.sub(r'((\n|^) *class(.|\n)*?)( *\n)*(?= *class)', r'\1\n\n\n', string) #two blancks lines between def definition string = re.sub(r'((\n|^)def(.|\n)*?)( *\n)*(?=def)', r'\1\n\n\n', string) #no blank line afted def or class string = re.sub(r'((\n|^) *((def)|(class)).*\n)( *\n)*', r'\1', string) return string #Set the functions names lowercase with underscores (aka loweru) def find_function_names(string): "find the functions with a name not lowercase with underscores" #get the list of functions that aren't good return [s[1] for s in re.findall("(def (\w*[A-Z]\w*))", string)] def set_loweru(string): def aux(s): return "_"+s.group(1).lower() string = string[0].lower() + string[1:] return re.sub("(?!^)_?([A-Z])", aux, string) def replace_words(list_tuple, string): for w1, w2 in list_tuple: string = re.sub("(?=2: dirs = sys.argv[1:] else: dirs = [os.path.abspath("./")] #perroquet/ print dirs print "REMOVED FILES:" for directory in dirs: print "\n".join(clean(directory)) perroquet-1.1.1.orig/utils/testPep.py0000755000175000017500000000601711561344350020044 0ustar georgeskgeorgesk#!/usr/bin/env python # -*- coding: utf-8 -*- import unittest import pep8 class Pep8FunctionsTestCase(unittest.TestCase): def test_set_lower(self): self.assertEqual(pep8.set_loweru("aZzzE"), "a_zzz_e") self.assertEqual(pep8.set_loweru("t_tt"), "t_tt") self.assertEqual(pep8.set_loweru("a_ZzzE"), "a_zzz_e") self.assertEqual(pep8.set_loweru("TestOne"), "test_one") def test_find_functions_names(self): text=""" def testAffd(): pass def testBddr(): pass def test_ee(): pass """ self.assertEqual(pep8.find_function_names(text), ["testAffd", "testBddr"]) def test_replace_word(self): text = "test a.test test.b a.test.a rrtestrr r_test_a" text_ = "TEST 1.TEST TEST.b 1.TEST.1 rrtestrr r_test_a" tuples = [("test", "TEST"), ("a", "1")] self.assertEqual(pep8.replace_words(tuples, text), text_) class Pep8FunctionsBlank(unittest.TestCase): def test_rm_blanks(self): text1=""" def a(): pass """ text1_=""" def a(): pass """ text11=""" def a(): pass """ self.assertEqual(pep8.set_blanks_lines(text1), text1_) self.assertEqual(pep8.set_blanks_lines(text11), text1_) text2=""" class a: def a(): pass def b() """ text22=""" class a: def a(): pass def b() """ text23=""" class a: def a(): pass def b() """ text2_=text2 self.assertEqual(pep8.set_blanks_lines(text2), text2) self.assertEqual(pep8.set_blanks_lines(text22), text2) self.assertEqual(pep8.set_blanks_lines(text23), text2) text2=""" class a: def a(): pass class b: """ text22=""" class a: def a(): pass class b: """ text23=""" class a: def a(): pass class b: """ text2_=text2 self.assertEqual(pep8.set_blanks_lines(text2), text2) self.assertEqual(pep8.set_blanks_lines(text22), text2) self.assertEqual(pep8.set_blanks_lines(text23), text2) text3=""" def a(): pass def b(): """ text32=""" def a(): pass def b(): """ text33=""" def a(): pass def b(): """ self.assertEqual(pep8.set_blanks_lines(text3), text3) self.assertEqual(pep8.set_blanks_lines(text32), text3) self.assertEqual(pep8.set_blanks_lines(text33), text3) text=""" "a new class" """ text2= ' """A general class to make parsers""" ' self.assertEqual(pep8.set_blanks_lines(text), text) self.assertEqual(pep8.set_blanks_lines(text2), text2) if __name__=="__main__": unittest.main() perroquet-1.1.1.orig/INSTALL0000644000175000017500000000440011561344350015726 0ustar georgeskgeorgeskBefore install Perroquet with this method, you should verify if there is not package for you OS : http://perroquet.b219.org/en/download.html Build and install ================= To build and install Perroquet, you need these dependancies: * Python 2.5+ Debian/Ubuntu: python Archlinux : python * Intltool 0.40.0+ Debian/Ubuntu: intltool (>= 0.40.0) Archlinux: intltool (>= 0.40.0) Fedora: intltool * In Fedora, you also need to install: python-setuptools-devel To build for local usage, run: ./setup.py build Note that build for local use is necessary only to have translation. To install properly, run: ./setup.py install --record=install-files.txt See --help for an overview of the available options; e.g. --prefix to install to a custom base directory, and --without-gettext to avoid installing natural language support files. Others options are usefull for packagers : --without-icon-cache --without-mime-database --without-desktop-database setup.py supports basic uninstallation provided --record was used for installation as above: ./setup.py uninstall --manifest=install-files.txt Note that uninstall will avoid removing most empty directories so it won't harm e.g. locale or icon directories which only contain Perroquet data. It also won't rebuild the icon cache, so you may wish to: gtk-update-icon-cache -q -f ${PREFIX}/share/icons/hicolor Where ${PREFIX} is the base install directory; e.g. /usr/local. Run === To use Perroquet, you need these dependancies * Python 2.5+ Debian/Ubuntu: python Archlinux : python * Gstreamer 0.10 Debian/Ubuntu: gstreamer0.10-plugins-good Archlinux: gstreamer0.10-good * Python-gstreamer0.10 Debian/Ubuntu: python-gst0.10 Archlinux: gstreamer0.10-python * Gstreamer 0.10 plugins bad (Optional, need for play speed change) Debian/Ubuntu: gstreamer0.10-plugins-bad Archlinux: gstreamer0.10-bas * Gtk2 2.16+ Debian/Ubuntu: libgtk2.0-0 Archlinux: gtk2 * Pygtk 2.16+ Debian/Ubuntu: python-gtk2 Archlinux: pygtk Without install, run: ./perroquet With install, just run in any directory: perroquet Others ====== If you have additionnal comment, contact the perroquet team at perroquet-team@lists.launchpad.net or fred.bertolus@gmail.com perroquet-1.1.1.orig/COPYING0000644000175000017500000010451311561344350015736 0ustar georgeskgeorgesk GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. 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 them 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 prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. 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. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey 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; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If 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 convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU 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 that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. 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. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 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. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. 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 state 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 3 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, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program 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, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU 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. But first, please read . perroquet-1.1.1.orig/po/0000755000175000017500000000000011561344350015315 5ustar georgeskgeorgeskperroquet-1.1.1.orig/po/tr.po0000644000175000017500000004622211561344350016310 0ustar georgeskgeorgesk# Turkish translation for perroquet # Copyright (c) 2010 Rosetta Contributors and Canonical Ltd 2010 # This file is distributed under the same license as the perroquet package. # FIRST AUTHOR , 2010. # msgid "" msgstr "" "Project-Id-Version: perroquet\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2010-06-19 10:43+0200\n" "PO-Revision-Date: 2010-06-02 04:30+0000\n" "Last-Translator: Fred Bertolus \n" "Language-Team: Turkish \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2011-01-17 05:02+0000\n" "X-Generator: Launchpad (build 12177)\n" #: ../data/perroquet.desktop.in.h:1 msgid "Oral comprehension teacher" msgstr "" #: ../data/perroquet.desktop.in.h:2 ../data/perroquet.ui.h:31 #: ../perroquetlib/gui/gui_controller.py:201 msgid "Perroquet" msgstr "" #: ../data/perroquet.ui.h:1 msgid "" "- Sequences: --/--\n" " - Words: --/--\n" " - Repeat ratio: -- per words" msgstr "" #: ../data/perroquet.ui.h:4 msgid "-- s" msgstr "-- s" #: ../data/perroquet.ui.h:5 msgid "--/--" msgstr "--/--" #: ../data/perroquet.ui.h:6 msgid "An oral comprehension teacher" msgstr "" #: ../data/perroquet.ui.h:7 msgid "Copyright © 2009-2010 Perroquet Team" msgstr "" #: ../data/perroquet.ui.h:8 msgid "Correction" msgstr "" #: ../data/perroquet.ui.h:9 msgid "Display correction" msgstr "" #: ../data/perroquet.ui.h:10 msgid "Display translation" msgstr "Çeviriyi göster" #: ../data/perroquet.ui.h:11 msgid "E_dit" msgstr "_Düzenle" #: ../data/perroquet.ui.h:12 msgid "Erase" msgstr "" #: ../data/perroquet.ui.h:13 msgid "Exercise language" msgstr "" #: ../data/perroquet.ui.h:14 ../data/properties.ui.h:1 #: ../data/properties_advanced.ui.h:5 msgid "Exercise properties" msgstr "" #: ../data/perroquet.ui.h:15 msgid "Exercise subtitle: " msgstr "" #: ../data/perroquet.ui.h:16 msgid "Exercise video or audio: " msgstr "" #: ../data/perroquet.ui.h:17 msgid "Export" msgstr "" #: ../data/perroquet.ui.h:18 msgid "Export as package..." msgstr "" #: ../data/perroquet.ui.h:19 msgid "Export as template..." msgstr "" #: ../data/perroquet.ui.h:20 msgid "H_elp" msgstr "_Yardım" #: ../data/perroquet.ui.h:21 msgid "Hint" msgstr "İpucu" #: ../data/perroquet.ui.h:22 msgid "Import a package" msgstr "" #: ../data/perroquet.ui.h:23 ../data/properties.ui.h:3 #: ../data/properties_advanced.ui.h:7 ../data/settings.ui.h:7 msgid "Language:" msgstr "" #: ../data/perroquet.ui.h:24 msgid "Last open files" msgstr "" #: ../data/perroquet.ui.h:25 msgid "Lateral panel" msgstr "" #: ../data/perroquet.ui.h:26 msgid "List of all words to find in the exercise" msgstr "" #: ../data/perroquet.ui.h:27 msgid "New exercise" msgstr "" #: ../data/perroquet.ui.h:28 msgid "Next Sequence" msgstr "" #: ../data/perroquet.ui.h:29 msgid "Next sequence" msgstr "" #: ../data/perroquet.ui.h:30 msgid "Pause" msgstr "Duraklat" #: ../data/perroquet.ui.h:32 msgid "Play" msgstr "Oynat" #: ../data/perroquet.ui.h:33 msgid "Play speed" msgstr "" #: ../data/perroquet.ui.h:34 msgid "Position in current sequence" msgstr "" #: ../data/perroquet.ui.h:35 msgid "Position in exercise" msgstr "" #: ../data/perroquet.ui.h:36 msgid "Previous sequence" msgstr "" #: ../data/perroquet.ui.h:37 msgid "Progress statistics" msgstr "Süreç istatistikleri" #: ../data/perroquet.ui.h:38 msgid "Properties" msgstr "Özellikler" #: ../data/perroquet.ui.h:39 msgid "Replay sequence" msgstr "" #: ../data/perroquet.ui.h:40 msgid "Reset exercise progress" msgstr "" #: ../data/perroquet.ui.h:41 msgid "Reset the exercise progress" msgstr "" #: ../data/perroquet.ui.h:42 msgid "Reveal the sequence" msgstr "" #: ../data/perroquet.ui.h:43 msgid "Reveal the word" msgstr "" #: ../data/perroquet.ui.h:44 msgid "Save exercise" msgstr "" #: ../data/perroquet.ui.h:45 ../data/properties.ui.h:6 #: ../data/properties_advanced.ui.h:21 msgid "Select a subtitle file" msgstr "Bir altyazı dosyası seç" #: ../data/perroquet.ui.h:46 ../data/properties.ui.h:7 #: ../data/properties_advanced.ui.h:22 msgid "Select a video file" msgstr "Bir video dosyası seçiniz" #: ../data/perroquet.ui.h:47 msgid "Show/Hide correction" msgstr "" #: ../data/perroquet.ui.h:48 msgid "Show/Hide translation" msgstr "Tercümeyi göster/gizle" #: ../data/perroquet.ui.h:49 msgid "The audio track should be in the exercise language" msgstr "" #: ../data/perroquet.ui.h:50 msgid "" "These subtitles should be in the exercise language.\n" "Supported format: srt" msgstr "" #: ../data/perroquet.ui.h:52 msgid "" "These subtitles should be in your mother language.\n" "Supported format: srt" msgstr "" "Bu alt yazılar anadilinizde olmalı.\n" "Desteklenen biçim: srt" #: ../data/perroquet.ui.h:54 msgid "" "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 3 of the License, or (at your option) " "any later version.\n" "\n" "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.\n" "\n" "You should have received a copy of the GNU General Public License along with " "this program. If not, see ." msgstr "" #: ../data/perroquet.ui.h:59 ../perroquetlib/gui/gui_exercise_manager.py:291 msgid "Translation" msgstr "Tercüme" #: ../data/perroquet.ui.h:60 msgid "Translation subtitles (optional): " msgstr "Çeviri altyazılar (seçimli): " #: ../data/perroquet.ui.h:61 msgid "" "Word filter\n" " This filter use regexp. Examples:\n" " abc = all words containing 'abc'\n" " ^abc = all words starting with 'abc'\n" " abc$ = all words ending with 'abc'\n" "\n" " The character . can replace any character:\n" " ^abc.. = all words of at least 5 letters starting with 'abc'\n" " ^...$ = all 4-letter word\n" " ^ab.c$ = all 4-letter word beginning with 'ab' and ending with 'c'\n" "\n" " For more information on regular expressions: " "http://en.wikipedia.org/wiki/Regular_expression" msgstr "" #: ../data/perroquet.ui.h:73 msgid "Words" msgstr "" #: ../data/perroquet.ui.h:74 msgid "_Advanced properties" msgstr "" #: ../data/perroquet.ui.h:75 msgid "_Exercise" msgstr "" #: ../data/perroquet.ui.h:76 msgid "_Exercise manager" msgstr "" #: ../data/perroquet.ui.h:77 msgid "_File" msgstr "_Dosya" #: ../data/perroquet.ui.h:78 msgid "open exercise" msgstr "" #: ../data/perroquet.ui.h:79 msgid "open saved exercise" msgstr "" #: ../data/properties.ui.h:2 ../data/properties_advanced.ui.h:6 msgid "Exercise: " msgstr "" #: ../data/properties.ui.h:4 ../data/properties_advanced.ui.h:12 msgid "Media: " msgstr "Medya: " #: ../data/properties.ui.h:5 ../data/settings.ui.h:13 msgid "Repeat sequences after completed it" msgstr "" #: ../data/properties.ui.h:8 ../data/properties_advanced.ui.h:25 msgid "Translation: " msgstr "Çeviri: " #: ../data/properties.ui.h:9 ../data/properties_advanced.ui.h:26 msgid "Use dynamic correction" msgstr "" #: ../data/properties.ui.h:10 msgid "" "With dynamic correction, the correct words will be displayed as valid as " "soon as they are correctly typed." msgstr "" #: ../data/properties_advanced.ui.h:1 msgid "Correction password : " msgstr "" #: ../data/properties_advanced.ui.h:2 ../data/settings.ui.h:3 msgid "Disable help tools" msgstr "" #: ../data/properties_advanced.ui.h:3 msgid "Exercise" msgstr "" #: ../data/properties_advanced.ui.h:4 msgid "Exercise name:" msgstr "" #: ../data/properties_advanced.ui.h:8 msgid "Lock exercise's correction" msgstr "" #: ../data/properties_advanced.ui.h:9 msgid "Lock exercise's propreties" msgstr "" #: ../data/properties_advanced.ui.h:10 msgid "Locks" msgstr "" #: ../data/properties_advanced.ui.h:11 ../data/settings.ui.h:9 msgid "Maximum sequence time (s): " msgstr "" #: ../data/properties_advanced.ui.h:13 msgid "Paths" msgstr "" #: ../data/properties_advanced.ui.h:14 msgid "Paths list" msgstr "" #: ../data/properties_advanced.ui.h:15 ../data/settings.ui.h:10 msgid "Play sequence in a random order" msgstr "" #: ../data/properties_advanced.ui.h:16 ../data/settings.ui.h:11 msgid "Playing time after sequences (ms):" msgstr "" #: ../data/properties_advanced.ui.h:17 ../data/settings.ui.h:12 msgid "Playing time before sequences (ms):" msgstr "" #: ../data/properties_advanced.ui.h:18 msgid "Properties password : " msgstr "" #: ../data/properties_advanced.ui.h:19 msgid "Repeat count limit by sequence (0 for no limit):" msgstr "" #: ../data/properties_advanced.ui.h:20 msgid "Repeat sequences after having completed it" msgstr "" #: ../data/properties_advanced.ui.h:23 msgid "Sequences" msgstr "" #: ../data/properties_advanced.ui.h:24 ../data/settings.ui.h:19 msgid "Time between sequences (s):" msgstr "" #: ../data/exercise_manager.ui.h:1 msgid "Details" msgstr "" #: ../data/exercise_manager.ui.h:2 msgid "Exercises" msgstr "" #: ../data/exercise_manager.ui.h:3 msgid "Perroquet exercises manager" msgstr "" #: ../data/exercise_manager.ui.h:4 msgid "Repositories" msgstr "" #: ../data/exercise_manager.ui.h:5 ../data/settings.ui.h:14 msgid "Settings" msgstr "" #: ../data/exercise_manager.ui.h:6 msgid "Tree view mode" msgstr "" #: ../data/exercise_manager.ui.h:7 #: ../perroquetlib/gui/gui_exercise_manager.py:364 msgid "Use" msgstr "" #: ../data/gui_message_dialog.ui.h:1 msgid "label" msgstr "" #: ../data/gui_password_dialog.ui.h:1 #: ../perroquetlib/gui/gui_password_dialog.py:46 msgid "Correction password :" msgstr "" #: ../data/gui_password_dialog.ui.h:2 msgid "Password for unlock correction" msgstr "" #: ../data/reset.ui.h:1 msgid "Cancel" msgstr "" #: ../data/reset.ui.h:2 msgid "Do you want to reset the current exercise ?" msgstr "" #: ../data/reset.ui.h:3 msgid "Ok" msgstr "" #: ../data/settings.ui.h:1 msgid "Advanced" msgstr "" #: ../data/settings.ui.h:2 msgid "Default exercise properties" msgstr "" #: ../data/settings.ui.h:4 msgid "Exercise type:" msgstr "" #: ../data/settings.ui.h:5 msgid "General" msgstr "" #: ../data/settings.ui.h:6 msgid "Interface" msgstr "" #: ../data/settings.ui.h:8 msgid "Limit repeat count by sequence (0 for no limit):" msgstr "" #: ../data/settings.ui.h:15 msgid "Show menu bar" msgstr "" #: ../data/settings.ui.h:16 msgid "Show play and pause buttons" msgstr "" #: ../data/settings.ui.h:17 msgid "Show settings" msgstr "" #: ../data/settings.ui.h:18 msgid "Skip valid sequences using next and previous sequence buttons" msgstr "" #: ../data/settings.ui.h:20 msgid "Use speed changer" msgstr "" #: ../perroquetlib/core.py:504 msgid "Untitled exercise" msgstr "" #: ../perroquetlib/core.py:556 msgid "" "Import finish succesfully. Use the exercises manager to use the newly " "installed exercise." msgstr "" #: ../perroquetlib/core.py:558 msgid "Import failed." msgstr "" #: ../perroquetlib/repository/exercise_repository_exercise.py:51 #: ../perroquetlib/repository/exercise_repository_exercise.py:52 #: ../perroquetlib/repository/exercise_repository_exercise.py:53 #: ../perroquetlib/repository/exercise_repository_exercise.py:54 #: ../perroquetlib/repository/exercise_repository_exercise.py:55 #: ../perroquetlib/repository/exercise_repository_exercise.py:56 #: ../perroquetlib/repository/exercise_repository_exercise.py:57 #: ../perroquetlib/repository/exercise_repository_exercise.py:58 #: ../perroquetlib/repository/exercise_repository_exercise.py:59 #: ../perroquetlib/repository/exercise_repository_exercise.py:60 msgid "Not specified" msgstr "" #: ../perroquetlib/repository/exercise_repository_exercise.py:451 msgid "Imported exercise" msgstr "" #: ../perroquetlib/repository/exercise_repository_manager.py:345 msgid "File not found: " msgstr "" #: ../perroquetlib/repository/exercise_repository_manager.py:359 msgid "Invalid package, missing template.perroquet." msgstr "" #: ../perroquetlib/repository/exercise_repository_manager.py:385 msgid "Exercise already exist." msgstr "" #: ../perroquetlib/gui/gui.py:85 #, python-format msgid "The file '%s' doesn't exist. Please modify exercise paths" msgstr "" #: ../perroquetlib/gui/gui.py:86 msgid "load error" msgstr "" #: ../perroquetlib/gui/gui.py:283 #: ../perroquetlib/gui/gui_exercise_manager.py:236 #: ../perroquetlib/gui/gui_sequence_properties_advanced.py:72 msgid "Path" msgstr "" #: ../perroquetlib/gui/gui.py:305 msgid "Do you really quit without save ?" msgstr "Kaydetmeden çıkmak istediğinize emin misiniz?" #: ../perroquetlib/gui/gui.py:306 msgid "Confirm quit" msgstr "" #: ../perroquetlib/gui/gui.py:320 msgid "Information" msgstr "" #: ../perroquetlib/gui/gui.py:809 msgid "Select File to open" msgstr "" #: ../perroquetlib/gui/gui.py:814 ../perroquetlib/gui/gui.py:852 msgid "Perroquet files" msgstr "" #: ../perroquetlib/gui/gui.py:819 ../perroquetlib/gui/gui.py:838 #: ../perroquetlib/gui/gui.py:857 ../perroquetlib/gui/gui.py:878 #: ../perroquetlib/gui/gui.py:899 msgid "All files" msgstr "Tüm dosyalar" #: ../perroquetlib/gui/gui.py:828 msgid "Select package to import" msgstr "" #: ../perroquetlib/gui/gui.py:833 ../perroquetlib/gui/gui.py:894 msgid "Perroquet package files" msgstr "" #: ../perroquetlib/gui/gui.py:847 msgid "Select File to Save to" msgstr "" #: ../perroquetlib/gui/gui.py:868 ../perroquetlib/gui/gui.py:889 msgid "Select File to Export to" msgstr "" #: ../perroquetlib/gui/gui.py:873 msgid "Perroquet template files" msgstr "" #: ../perroquetlib/gui/gui_controller.py:237 #, python-format msgid "- Sequences: %(found)s/%(count)s (%(percent)s %%)\n" msgstr "" #: ../perroquetlib/gui/gui_controller.py:238 #, python-format msgid "- Words: %(found)s/%(count)s (%(percent)s %%)\n" msgstr "" #: ../perroquetlib/gui/gui_controller.py:239 #, python-format msgid "- Repeat ratio: %s per words" msgstr "" #. Wrong password #: ../perroquetlib/gui/gui_controller.py:287 #: ../perroquetlib/gui/gui_controller.py:518 msgid "Wrong password" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:82 msgid "Updating repositories..." msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:108 msgid "Local repository" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:110 msgid "Distant repository" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:112 msgid "Offline repository" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:114 msgid "Orphan repository" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:132 msgid "Group" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:148 #: ../perroquetlib/gui/gui_exercise_manager.py:266 #: ../perroquetlib/gui/gui_exercise_manager.py:446 msgid "Installed" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:151 #: ../perroquetlib/gui/gui_exercise_manager.py:268 #: ../perroquetlib/gui/gui_exercise_manager.py:439 msgid "Available" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:154 #: ../perroquetlib/gui/gui_exercise_manager.py:270 #: ../perroquetlib/gui/gui_exercise_manager.py:454 msgid "Used" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:157 #: ../perroquetlib/gui/gui_exercise_manager.py:272 #: ../perroquetlib/gui/gui_exercise_manager.py:456 msgid "Done" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:168 msgid "Exercise" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:174 #: ../perroquetlib/gui/gui_exercise_manager.py:274 #: ../perroquetlib/gui/gui_exercise_manager.py:307 msgid "Name" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:179 msgid "Type" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:184 #: ../perroquetlib/gui/gui_exercise_manager.py:276 msgid "Description" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:189 msgid "Status" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:194 #: ../perroquetlib/gui/gui_exercise_manager.py:283 msgid "Words count" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:275 msgid "Licence" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:277 msgid "Author" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:278 msgid "Author website" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:279 msgid "Author contact" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:282 msgid "Version" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:284 msgid "Language" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:285 msgid "Media type" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:286 msgid "Id" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:287 msgid "Install status" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:295 msgid "Packager" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:296 msgid "Packager website" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:297 msgid "Packager contact" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:299 msgid "Exercise path" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:300 msgid "Package path" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:301 msgid "Template path" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:302 msgid "Instance path" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:303 msgid "Finished exercise path" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:312 msgid "Value" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:352 msgid "Install" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:356 #: ../perroquetlib/gui/gui_exercise_manager.py:360 msgid "Cancel install" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:368 #: ../perroquetlib/gui/gui_exercise_manager.py:376 msgid "Remove" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:372 msgid "Continue" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:416 #, python-format msgid "%d available exercises" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:418 #, python-format msgid "%d available exercise" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:422 #, python-format msgid " and %d installed exercises" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:424 #, python-format msgid " and %d installed exercise" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:441 msgid "Downloading" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:444 msgid "Installing" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:448 msgid "Corrupted" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:450 msgid "Canceled" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:452 msgid "Removing" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:464 #, python-format msgid "Downloading ... %d%%" msgstr "" #: ../perroquetlib/gui/gui_password_dialog.py:49 msgid "Properties password :" msgstr "" #: ../perroquetlib/video_player.py:49 msgid "" "You need to install the gstreamer soundtouch elements to use slowly play " "feature." msgstr "" #~ msgid "New exercice" #~ msgstr "Yeni alıştırma" #~ msgid "Exercice: " #~ msgstr "Alıştırma: " #~ msgid "Select File to Open" #~ msgstr "Açıklacak Dosyayı Seçin" #, python-format #~ msgid "The file '%s' doesn't exist. Please modify exercice paths" #~ msgstr "" #~ "'%s' konumunda aranan dosya bulunamadı. Lütfen dosyanın aranacağı konumu " #~ "kontrol ediniz." perroquet-1.1.1.orig/po/POTFILES.in0000644000175000017500000000352711561344350017101 0ustar georgeskgeorgesk[encoding: UTF-8] data/perroquet.desktop.in [type: gettext/glade]data/perroquet.ui [type: gettext/glade]data/properties.ui [type: gettext/glade]data/properties_advanced.ui [type: gettext/glade]data/exercise_manager.ui [type: gettext/glade]data/gui_message_dialog.ui [type: gettext/glade]data/gui_password_dialog.ui [type: gettext/glade]data/reset.ui [type: gettext/glade]data/settings.ui perroquetlib/__init__.py perroquetlib/perroquet.py perroquetlib/core.py perroquetlib/debug.py perroquetlib/config/perroquet_config.py perroquetlib/config/__init__.py perroquetlib/config/config_lib.py perroquetlib/repository/__init__.py perroquetlib/repository/exercise_repository_exercise.py perroquetlib/repository/exercise_repository.py perroquetlib/repository/exercise_repository_group.py perroquetlib/repository/exercise_repository_manager.py perroquetlib/model/__init__.py perroquetlib/model/languages_manager.py perroquetlib/model/sequence/__init__.py perroquetlib/model/sequence/sequence_dynamic_correction.py perroquetlib/model/sequence/sequence.py perroquetlib/model/sequence/word.py perroquetlib/model/sequence/sequence_simple.py perroquetlib/model/subtitles_loader.py perroquetlib/model/exercise.py perroquetlib/model/sub_exercise.py perroquetlib/model/exercise_parser/__init__.py perroquetlib/model/exercise_parser/parser_v1_0_0.py perroquetlib/model/exercise_parser/parser_v1_1_0.py perroquetlib/model/exercise_parser/lib.py perroquetlib/gui/gui_settings.py perroquetlib/gui/gui.py perroquetlib/gui/gui_reset_exercise.py perroquetlib/gui/__init__.py perroquetlib/gui/gui_controller.py perroquetlib/gui/gui_exercise_manager.py perroquetlib/gui/gui_message_dialog.py perroquetlib/gui/gui_sequence_properties.py perroquetlib/gui/gui_sequence_properties_advanced.py perroquetlib/gui/gui_exercise_controller.py perroquetlib/gui/gui_password_dialog.py perroquetlib/video_player.py perroquet-1.1.1.orig/po/sv.po0000644000175000017500000005243011561344350016311 0ustar georgeskgeorgesk# Swedish translation for perroquet # Copyright (c) 2010 Rosetta Contributors and Canonical Ltd 2010 # This file is distributed under the same license as the perroquet package. # FIRST AUTHOR , 2010. # msgid "" msgstr "" "Project-Id-Version: perroquet\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2010-06-19 10:43+0200\n" "PO-Revision-Date: 2010-01-16 05:46+0000\n" "Last-Translator: Fred Bertolus \n" "Language-Team: Swedish \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2011-01-17 05:02+0000\n" "X-Generator: Launchpad (build 12177)\n" #: ../data/perroquet.desktop.in.h:1 msgid "Oral comprehension teacher" msgstr "" #: ../data/perroquet.desktop.in.h:2 ../data/perroquet.ui.h:31 #: ../perroquetlib/gui/gui_controller.py:201 msgid "Perroquet" msgstr "Perroquet" #: ../data/perroquet.ui.h:1 msgid "" "- Sequences: --/--\n" " - Words: --/--\n" " - Repeat ratio: -- per words" msgstr "" #: ../data/perroquet.ui.h:4 msgid "-- s" msgstr "-- s" #: ../data/perroquet.ui.h:5 msgid "--/--" msgstr "--/--" #: ../data/perroquet.ui.h:6 msgid "An oral comprehension teacher" msgstr "" #: ../data/perroquet.ui.h:7 msgid "Copyright © 2009-2010 Perroquet Team" msgstr "" #: ../data/perroquet.ui.h:8 msgid "Correction" msgstr "" #: ../data/perroquet.ui.h:9 msgid "Display correction" msgstr "" #: ../data/perroquet.ui.h:10 msgid "Display translation" msgstr "Visa översättning" #: ../data/perroquet.ui.h:11 msgid "E_dit" msgstr "Re_digera" #: ../data/perroquet.ui.h:12 msgid "Erase" msgstr "" #: ../data/perroquet.ui.h:13 msgid "Exercise language" msgstr "" #: ../data/perroquet.ui.h:14 ../data/properties.ui.h:1 #: ../data/properties_advanced.ui.h:5 msgid "Exercise properties" msgstr "" #: ../data/perroquet.ui.h:15 msgid "Exercise subtitle: " msgstr "" #: ../data/perroquet.ui.h:16 msgid "Exercise video or audio: " msgstr "" #: ../data/perroquet.ui.h:17 msgid "Export" msgstr "" #: ../data/perroquet.ui.h:18 msgid "Export as package..." msgstr "" #: ../data/perroquet.ui.h:19 msgid "Export as template..." msgstr "" #: ../data/perroquet.ui.h:20 msgid "H_elp" msgstr "H_jälp" #: ../data/perroquet.ui.h:21 msgid "Hint" msgstr "Ledtråd" #: ../data/perroquet.ui.h:22 msgid "Import a package" msgstr "" #: ../data/perroquet.ui.h:23 ../data/properties.ui.h:3 #: ../data/properties_advanced.ui.h:7 ../data/settings.ui.h:7 msgid "Language:" msgstr "" #: ../data/perroquet.ui.h:24 msgid "Last open files" msgstr "" #: ../data/perroquet.ui.h:25 msgid "Lateral panel" msgstr "" #: ../data/perroquet.ui.h:26 msgid "List of all words to find in the exercise" msgstr "Ordlista som hittas i övningen" #: ../data/perroquet.ui.h:27 msgid "New exercise" msgstr "Ny övning" #: ../data/perroquet.ui.h:28 msgid "Next Sequence" msgstr "Nästa sekvens" #: ../data/perroquet.ui.h:29 msgid "Next sequence" msgstr "Nästa sekvens" #: ../data/perroquet.ui.h:30 msgid "Pause" msgstr "Pausa" #: ../data/perroquet.ui.h:32 msgid "Play" msgstr "Spela upp" #: ../data/perroquet.ui.h:33 msgid "Play speed" msgstr "" #: ../data/perroquet.ui.h:34 msgid "Position in current sequence" msgstr "Lokalisering i löpande ordning" #: ../data/perroquet.ui.h:35 msgid "Position in exercise" msgstr "Lokalisering i övningen" #: ../data/perroquet.ui.h:36 msgid "Previous sequence" msgstr "Föregående sekvens" #: ../data/perroquet.ui.h:37 msgid "Progress statistics" msgstr "Utveckling statistik" #: ../data/perroquet.ui.h:38 msgid "Properties" msgstr "Egenskaper" #: ../data/perroquet.ui.h:39 msgid "Replay sequence" msgstr "Spela om sekvens" #: ../data/perroquet.ui.h:40 msgid "Reset exercise progress" msgstr "" #: ../data/perroquet.ui.h:41 msgid "Reset the exercise progress" msgstr "" #: ../data/perroquet.ui.h:42 msgid "Reveal the sequence" msgstr "" #: ../data/perroquet.ui.h:43 msgid "Reveal the word" msgstr "" #: ../data/perroquet.ui.h:44 msgid "Save exercise" msgstr "Spara övning" #: ../data/perroquet.ui.h:45 ../data/properties.ui.h:6 #: ../data/properties_advanced.ui.h:21 msgid "Select a subtitle file" msgstr "Välj en fil med undertexter" #: ../data/perroquet.ui.h:46 ../data/properties.ui.h:7 #: ../data/properties_advanced.ui.h:22 msgid "Select a video file" msgstr "Välj en video fil" #: ../data/perroquet.ui.h:47 msgid "Show/Hide correction" msgstr "" #: ../data/perroquet.ui.h:48 msgid "Show/Hide translation" msgstr "Visa/Dölja översättning" #: ../data/perroquet.ui.h:49 msgid "The audio track should be in the exercise language" msgstr "" #: ../data/perroquet.ui.h:50 msgid "" "These subtitles should be in the exercise language.\n" "Supported format: srt" msgstr "" #: ../data/perroquet.ui.h:52 msgid "" "These subtitles should be in your mother language.\n" "Supported format: srt" msgstr "" "Dessa subtitler skuller vara i ditt modersmål.\n" "Stött format: srt" #: ../data/perroquet.ui.h:54 msgid "" "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 3 of the License, or (at your option) " "any later version.\n" "\n" "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.\n" "\n" "You should have received a copy of the GNU General Public License along with " "this program. If not, see ." msgstr "" "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 3 of the License, or (at your option) " "any later version.\n" "\n" "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.\n" "\n" "You should have received a copy of the GNU General Public License along with " "this program. If not, see ." #: ../data/perroquet.ui.h:59 ../perroquetlib/gui/gui_exercise_manager.py:291 msgid "Translation" msgstr "Översättning" #: ../data/perroquet.ui.h:60 msgid "Translation subtitles (optional): " msgstr "Översättning undertexter (fakultativ): " #: ../data/perroquet.ui.h:61 msgid "" "Word filter\n" " This filter use regexp. Examples:\n" " abc = all words containing 'abc'\n" " ^abc = all words starting with 'abc'\n" " abc$ = all words ending with 'abc'\n" "\n" " The character . can replace any character:\n" " ^abc.. = all words of at least 5 letters starting with 'abc'\n" " ^...$ = all 4-letter word\n" " ^ab.c$ = all 4-letter word beginning with 'ab' and ending with 'c'\n" "\n" " For more information on regular expressions: " "http://en.wikipedia.org/wiki/Regular_expression" msgstr "" #: ../data/perroquet.ui.h:73 msgid "Words" msgstr "" #: ../data/perroquet.ui.h:74 msgid "_Advanced properties" msgstr "" #: ../data/perroquet.ui.h:75 msgid "_Exercise" msgstr "" #: ../data/perroquet.ui.h:76 msgid "_Exercise manager" msgstr "" #: ../data/perroquet.ui.h:77 msgid "_File" msgstr "_Fil" #: ../data/perroquet.ui.h:78 msgid "open exercise" msgstr "" #: ../data/perroquet.ui.h:79 msgid "open saved exercise" msgstr "" #: ../data/properties.ui.h:2 ../data/properties_advanced.ui.h:6 msgid "Exercise: " msgstr "" #: ../data/properties.ui.h:4 ../data/properties_advanced.ui.h:12 msgid "Media: " msgstr "Media: " #: ../data/properties.ui.h:5 ../data/settings.ui.h:13 msgid "Repeat sequences after completed it" msgstr "" #: ../data/properties.ui.h:8 ../data/properties_advanced.ui.h:25 msgid "Translation: " msgstr "Översättning: " #: ../data/properties.ui.h:9 ../data/properties_advanced.ui.h:26 msgid "Use dynamic correction" msgstr "" #: ../data/properties.ui.h:10 msgid "" "With dynamic correction, the correct words will be displayed as valid as " "soon as they are correctly typed." msgstr "" #: ../data/properties_advanced.ui.h:1 msgid "Correction password : " msgstr "" #: ../data/properties_advanced.ui.h:2 ../data/settings.ui.h:3 msgid "Disable help tools" msgstr "" #: ../data/properties_advanced.ui.h:3 msgid "Exercise" msgstr "" #: ../data/properties_advanced.ui.h:4 msgid "Exercise name:" msgstr "" #: ../data/properties_advanced.ui.h:8 msgid "Lock exercise's correction" msgstr "" #: ../data/properties_advanced.ui.h:9 msgid "Lock exercise's propreties" msgstr "" #: ../data/properties_advanced.ui.h:10 msgid "Locks" msgstr "" #: ../data/properties_advanced.ui.h:11 ../data/settings.ui.h:9 msgid "Maximum sequence time (s): " msgstr "" #: ../data/properties_advanced.ui.h:13 msgid "Paths" msgstr "" #: ../data/properties_advanced.ui.h:14 msgid "Paths list" msgstr "" #: ../data/properties_advanced.ui.h:15 ../data/settings.ui.h:10 msgid "Play sequence in a random order" msgstr "" #: ../data/properties_advanced.ui.h:16 ../data/settings.ui.h:11 msgid "Playing time after sequences (ms):" msgstr "" #: ../data/properties_advanced.ui.h:17 ../data/settings.ui.h:12 msgid "Playing time before sequences (ms):" msgstr "" #: ../data/properties_advanced.ui.h:18 msgid "Properties password : " msgstr "" #: ../data/properties_advanced.ui.h:19 msgid "Repeat count limit by sequence (0 for no limit):" msgstr "" #: ../data/properties_advanced.ui.h:20 msgid "Repeat sequences after having completed it" msgstr "" #: ../data/properties_advanced.ui.h:23 msgid "Sequences" msgstr "" #: ../data/properties_advanced.ui.h:24 ../data/settings.ui.h:19 msgid "Time between sequences (s):" msgstr "" #: ../data/exercise_manager.ui.h:1 msgid "Details" msgstr "" #: ../data/exercise_manager.ui.h:2 msgid "Exercises" msgstr "" #: ../data/exercise_manager.ui.h:3 msgid "Perroquet exercises manager" msgstr "" #: ../data/exercise_manager.ui.h:4 msgid "Repositories" msgstr "" #: ../data/exercise_manager.ui.h:5 ../data/settings.ui.h:14 msgid "Settings" msgstr "" #: ../data/exercise_manager.ui.h:6 msgid "Tree view mode" msgstr "" #: ../data/exercise_manager.ui.h:7 #: ../perroquetlib/gui/gui_exercise_manager.py:364 msgid "Use" msgstr "" #: ../data/gui_message_dialog.ui.h:1 msgid "label" msgstr "" #: ../data/gui_password_dialog.ui.h:1 #: ../perroquetlib/gui/gui_password_dialog.py:46 msgid "Correction password :" msgstr "" #: ../data/gui_password_dialog.ui.h:2 msgid "Password for unlock correction" msgstr "" #: ../data/reset.ui.h:1 msgid "Cancel" msgstr "" #: ../data/reset.ui.h:2 msgid "Do you want to reset the current exercise ?" msgstr "" #: ../data/reset.ui.h:3 msgid "Ok" msgstr "" #: ../data/settings.ui.h:1 msgid "Advanced" msgstr "" #: ../data/settings.ui.h:2 msgid "Default exercise properties" msgstr "" #: ../data/settings.ui.h:4 msgid "Exercise type:" msgstr "" #: ../data/settings.ui.h:5 msgid "General" msgstr "" #: ../data/settings.ui.h:6 msgid "Interface" msgstr "" #: ../data/settings.ui.h:8 msgid "Limit repeat count by sequence (0 for no limit):" msgstr "" #: ../data/settings.ui.h:15 msgid "Show menu bar" msgstr "" #: ../data/settings.ui.h:16 msgid "Show play and pause buttons" msgstr "" #: ../data/settings.ui.h:17 msgid "Show settings" msgstr "" #: ../data/settings.ui.h:18 msgid "Skip valid sequences using next and previous sequence buttons" msgstr "" #: ../data/settings.ui.h:20 msgid "Use speed changer" msgstr "" #: ../perroquetlib/core.py:504 msgid "Untitled exercise" msgstr "" #: ../perroquetlib/core.py:556 msgid "" "Import finish succesfully. Use the exercises manager to use the newly " "installed exercise." msgstr "" #: ../perroquetlib/core.py:558 msgid "Import failed." msgstr "" #: ../perroquetlib/repository/exercise_repository_exercise.py:51 #: ../perroquetlib/repository/exercise_repository_exercise.py:52 #: ../perroquetlib/repository/exercise_repository_exercise.py:53 #: ../perroquetlib/repository/exercise_repository_exercise.py:54 #: ../perroquetlib/repository/exercise_repository_exercise.py:55 #: ../perroquetlib/repository/exercise_repository_exercise.py:56 #: ../perroquetlib/repository/exercise_repository_exercise.py:57 #: ../perroquetlib/repository/exercise_repository_exercise.py:58 #: ../perroquetlib/repository/exercise_repository_exercise.py:59 #: ../perroquetlib/repository/exercise_repository_exercise.py:60 msgid "Not specified" msgstr "" #: ../perroquetlib/repository/exercise_repository_exercise.py:451 msgid "Imported exercise" msgstr "" #: ../perroquetlib/repository/exercise_repository_manager.py:345 msgid "File not found: " msgstr "" #: ../perroquetlib/repository/exercise_repository_manager.py:359 msgid "Invalid package, missing template.perroquet." msgstr "" #: ../perroquetlib/repository/exercise_repository_manager.py:385 msgid "Exercise already exist." msgstr "" #: ../perroquetlib/gui/gui.py:85 #, python-format msgid "The file '%s' doesn't exist. Please modify exercise paths" msgstr "" #: ../perroquetlib/gui/gui.py:86 msgid "load error" msgstr "" #: ../perroquetlib/gui/gui.py:283 #: ../perroquetlib/gui/gui_exercise_manager.py:236 #: ../perroquetlib/gui/gui_sequence_properties_advanced.py:72 msgid "Path" msgstr "" #: ../perroquetlib/gui/gui.py:305 msgid "Do you really quit without save ?" msgstr "Vill du verkligen avsluta utan att spara?" #: ../perroquetlib/gui/gui.py:306 msgid "Confirm quit" msgstr "Konfirmera avsluta" #: ../perroquetlib/gui/gui.py:320 msgid "Information" msgstr "" #: ../perroquetlib/gui/gui.py:809 msgid "Select File to open" msgstr "" #: ../perroquetlib/gui/gui.py:814 ../perroquetlib/gui/gui.py:852 msgid "Perroquet files" msgstr "Perroquet filar" #: ../perroquetlib/gui/gui.py:819 ../perroquetlib/gui/gui.py:838 #: ../perroquetlib/gui/gui.py:857 ../perroquetlib/gui/gui.py:878 #: ../perroquetlib/gui/gui.py:899 msgid "All files" msgstr "Alla filer" #: ../perroquetlib/gui/gui.py:828 msgid "Select package to import" msgstr "" #: ../perroquetlib/gui/gui.py:833 ../perroquetlib/gui/gui.py:894 msgid "Perroquet package files" msgstr "" #: ../perroquetlib/gui/gui.py:847 msgid "Select File to Save to" msgstr "Välj fil att spara till" #: ../perroquetlib/gui/gui.py:868 ../perroquetlib/gui/gui.py:889 msgid "Select File to Export to" msgstr "" #: ../perroquetlib/gui/gui.py:873 msgid "Perroquet template files" msgstr "" #: ../perroquetlib/gui/gui_controller.py:237 #, python-format msgid "- Sequences: %(found)s/%(count)s (%(percent)s %%)\n" msgstr "- Sekvenser: %(found)s/%(count)s (%(percent)s %%)\n" #: ../perroquetlib/gui/gui_controller.py:238 #, python-format msgid "- Words: %(found)s/%(count)s (%(percent)s %%)\n" msgstr "- Ord: %(found)s/%(count)s (%(percent)s %%)\n" #: ../perroquetlib/gui/gui_controller.py:239 #, python-format msgid "- Repeat ratio: %s per words" msgstr "- Upprep förhållande: %s per words" #. Wrong password #: ../perroquetlib/gui/gui_controller.py:287 #: ../perroquetlib/gui/gui_controller.py:518 msgid "Wrong password" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:82 msgid "Updating repositories..." msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:108 msgid "Local repository" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:110 msgid "Distant repository" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:112 msgid "Offline repository" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:114 msgid "Orphan repository" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:132 msgid "Group" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:148 #: ../perroquetlib/gui/gui_exercise_manager.py:266 #: ../perroquetlib/gui/gui_exercise_manager.py:446 msgid "Installed" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:151 #: ../perroquetlib/gui/gui_exercise_manager.py:268 #: ../perroquetlib/gui/gui_exercise_manager.py:439 msgid "Available" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:154 #: ../perroquetlib/gui/gui_exercise_manager.py:270 #: ../perroquetlib/gui/gui_exercise_manager.py:454 msgid "Used" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:157 #: ../perroquetlib/gui/gui_exercise_manager.py:272 #: ../perroquetlib/gui/gui_exercise_manager.py:456 msgid "Done" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:168 msgid "Exercise" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:174 #: ../perroquetlib/gui/gui_exercise_manager.py:274 #: ../perroquetlib/gui/gui_exercise_manager.py:307 msgid "Name" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:179 msgid "Type" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:184 #: ../perroquetlib/gui/gui_exercise_manager.py:276 msgid "Description" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:189 msgid "Status" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:194 #: ../perroquetlib/gui/gui_exercise_manager.py:283 msgid "Words count" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:275 msgid "Licence" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:277 msgid "Author" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:278 msgid "Author website" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:279 msgid "Author contact" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:282 msgid "Version" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:284 msgid "Language" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:285 msgid "Media type" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:286 msgid "Id" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:287 msgid "Install status" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:295 msgid "Packager" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:296 msgid "Packager website" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:297 msgid "Packager contact" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:299 msgid "Exercise path" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:300 msgid "Package path" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:301 msgid "Template path" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:302 msgid "Instance path" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:303 msgid "Finished exercise path" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:312 msgid "Value" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:352 msgid "Install" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:356 #: ../perroquetlib/gui/gui_exercise_manager.py:360 msgid "Cancel install" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:368 #: ../perroquetlib/gui/gui_exercise_manager.py:376 msgid "Remove" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:372 msgid "Continue" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:416 #, python-format msgid "%d available exercises" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:418 #, python-format msgid "%d available exercise" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:422 #, python-format msgid " and %d installed exercises" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:424 #, python-format msgid " and %d installed exercise" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:441 msgid "Downloading" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:444 msgid "Installing" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:448 msgid "Corrupted" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:450 msgid "Canceled" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:452 msgid "Removing" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:464 #, python-format msgid "Downloading ... %d%%" msgstr "" #: ../perroquetlib/gui/gui_password_dialog.py:49 msgid "Properties password :" msgstr "" #: ../perroquetlib/video_player.py:49 msgid "" "You need to install the gstreamer soundtouch elements to use slowly play " "feature." msgstr "" #~ msgid "Select File to Open" #~ msgstr "Välj fil att öppna" #~ msgid "Load error" #~ msgstr "Fel-laddat" #, python-format #~ msgid "The file '%s' doesn't exist. Please modify exercice paths" #~ msgstr "Filen '% s' finns inte. Byta övningar vägar" #~ msgid "Oral comprehention teacher" #~ msgstr "Muntligs förståelse lärare" #~ msgid "Copyright © 2009-2010 Frédéric Bertolus" #~ msgstr "Copyright © 2009-2010 Frédéric Bertolus" #~ msgid "" #~ "- Sequences: --/--\n" #~ "- Words: --/--\n" #~ "- Repeat ratio: -- per words" #~ msgstr "" #~ "- Sekvenser: --/--\n" #~ "- Ord: --/--\n" #~ "- Upprepa förhållande: -- per words" #~ msgid "A oral comprehension teacher" #~ msgstr "En muntlig föståelse lärare" #~ msgid "New exercice" #~ msgstr "Ny övning" #~ msgid "Exercice video or audio: " #~ msgstr "Ljud eller video övningar " #~ msgid "Exercice properties" #~ msgstr "Övning egenskaper" #~ msgid "Exercice subtitle: " #~ msgstr "Övning undertexter " #~ msgid "Exercice: " #~ msgstr "Övning: " #~ msgid "Open saved exercice" #~ msgstr "Öpna besparad övning" #~ msgid "Open exercise" #~ msgstr "Öpna övning" #~ msgid "Save exercice" #~ msgstr "Spara övning" #~ msgid "The audio track should be in the exercice language" #~ msgstr "Ljudspåret skulle vara i språkövningen" #~ msgid "" #~ "These subtitles should be in the exercice language.\n" #~ "Supported format: srt" #~ msgstr "" #~ "Dessa undertexter skulle vara i språkövningen.\n" #~ "Stött format: SRT" perroquet-1.1.1.orig/po/fr.po0000644000175000017500000007214711561344350016277 0ustar georgeskgeorgesk# French translation for perroquet # Copyright (c) 2009 Rosetta Contributors and Canonical Ltd 2009 # This file is distributed under the same license as the perroquet package. # FIRST AUTHOR , 2009. # msgid "" msgstr "" "Project-Id-Version: perroquet\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2010-06-19 10:43+0200\n" "PO-Revision-Date: 2010-09-02 06:29+0000\n" "Last-Translator: Quentin THEURET \n" "Language-Team: French \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2011-01-17 05:02+0000\n" "X-Generator: Launchpad (build 12177)\n" #: ../data/perroquet.desktop.in.h:1 msgid "Oral comprehension teacher" msgstr "Assistant à la compréhension orale" #: ../data/perroquet.desktop.in.h:2 ../data/perroquet.ui.h:31 #: ../perroquetlib/gui/gui_controller.py:201 msgid "Perroquet" msgstr "Perroquet" #: ../data/perroquet.ui.h:1 msgid "" "- Sequences: --/--\n" " - Words: --/--\n" " - Repeat ratio: -- per words" msgstr "" "Séquences : --/--\n" " - Mots : --/--\n" " - Ratio de répétition : -- par mots" #: ../data/perroquet.ui.h:4 msgid "-- s" msgstr "-- s" #: ../data/perroquet.ui.h:5 msgid "--/--" msgstr "--/--" #: ../data/perroquet.ui.h:6 msgid "An oral comprehension teacher" msgstr "Un assistant à la compréhension orale" #: ../data/perroquet.ui.h:7 msgid "Copyright © 2009-2010 Perroquet Team" msgstr "Copyright © 2009-2010 Perroquet Team" #: ../data/perroquet.ui.h:8 msgid "Correction" msgstr "Correction" #: ../data/perroquet.ui.h:9 msgid "Display correction" msgstr "Afficher la correction" #: ../data/perroquet.ui.h:10 msgid "Display translation" msgstr "Afficher la traduction" #: ../data/perroquet.ui.h:11 msgid "E_dit" msgstr "Éditio_n" #: ../data/perroquet.ui.h:12 msgid "Erase" msgstr "Effacer" #: ../data/perroquet.ui.h:13 msgid "Exercise language" msgstr "Langue de l'exercice" #: ../data/perroquet.ui.h:14 ../data/properties.ui.h:1 #: ../data/properties_advanced.ui.h:5 msgid "Exercise properties" msgstr "Propriétés de l'exercice" #: ../data/perroquet.ui.h:15 msgid "Exercise subtitle: " msgstr "Sous-titres de l'exercice " #: ../data/perroquet.ui.h:16 msgid "Exercise video or audio: " msgstr "Fichier vidéo ou audio de l'exercice : " #: ../data/perroquet.ui.h:17 msgid "Export" msgstr "Exporter" #: ../data/perroquet.ui.h:18 msgid "Export as package..." msgstr "Exporter en tant que paquet..." #: ../data/perroquet.ui.h:19 msgid "Export as template..." msgstr "Exporter en tant que modèle..." #: ../data/perroquet.ui.h:20 msgid "H_elp" msgstr "Aid_e" #: ../data/perroquet.ui.h:21 msgid "Hint" msgstr "Indice" #: ../data/perroquet.ui.h:22 msgid "Import a package" msgstr "Importer un paquet" #: ../data/perroquet.ui.h:23 ../data/properties.ui.h:3 #: ../data/properties_advanced.ui.h:7 ../data/settings.ui.h:7 msgid "Language:" msgstr "Langue :" #: ../data/perroquet.ui.h:24 msgid "Last open files" msgstr "Derniers fichiers ouverts" #: ../data/perroquet.ui.h:25 msgid "Lateral panel" msgstr "Panneau latéral" #: ../data/perroquet.ui.h:26 msgid "List of all words to find in the exercise" msgstr "Liste de tous les mots à trouver dans l'exercice" #: ../data/perroquet.ui.h:27 msgid "New exercise" msgstr "Nouvel exercice" #: ../data/perroquet.ui.h:28 msgid "Next Sequence" msgstr "Séquence suivante" #: ../data/perroquet.ui.h:29 msgid "Next sequence" msgstr "Séquence suivante" #: ../data/perroquet.ui.h:30 msgid "Pause" msgstr "Pause" #: ../data/perroquet.ui.h:32 msgid "Play" msgstr "Lecture" #: ../data/perroquet.ui.h:33 msgid "Play speed" msgstr "Vitesse de lecture" #: ../data/perroquet.ui.h:34 msgid "Position in current sequence" msgstr "Position dans la séquence" #: ../data/perroquet.ui.h:35 msgid "Position in exercise" msgstr "Position dans l'exercice" #: ../data/perroquet.ui.h:36 msgid "Previous sequence" msgstr "Séquence précédente" #: ../data/perroquet.ui.h:37 msgid "Progress statistics" msgstr "Statistiques de progression" #: ../data/perroquet.ui.h:38 msgid "Properties" msgstr "Propriétés" #: ../data/perroquet.ui.h:39 msgid "Replay sequence" msgstr "Rejouer la séquence" #: ../data/perroquet.ui.h:40 msgid "Reset exercise progress" msgstr "Réinitialiser l'avancement de l'exercice" #: ../data/perroquet.ui.h:41 msgid "Reset the exercise progress" msgstr "Réinitialiser l'avancement de l'exercice" #: ../data/perroquet.ui.h:42 msgid "Reveal the sequence" msgstr "Révéler la séquence" #: ../data/perroquet.ui.h:43 msgid "Reveal the word" msgstr "Révéler le mot" #: ../data/perroquet.ui.h:44 msgid "Save exercise" msgstr "Enregistrer l'exercice" #: ../data/perroquet.ui.h:45 ../data/properties.ui.h:6 #: ../data/properties_advanced.ui.h:21 msgid "Select a subtitle file" msgstr "Sélectionner un fichier de sous-titrage" #: ../data/perroquet.ui.h:46 ../data/properties.ui.h:7 #: ../data/properties_advanced.ui.h:22 msgid "Select a video file" msgstr "Choisir un fichier vidéo" #: ../data/perroquet.ui.h:47 msgid "Show/Hide correction" msgstr "Afficher/Cacher la correction" #: ../data/perroquet.ui.h:48 msgid "Show/Hide translation" msgstr "Afficher/Cacher la traduction" #: ../data/perroquet.ui.h:49 msgid "The audio track should be in the exercise language" msgstr "La piste audio devrait être dans la langue de l'exercice" #: ../data/perroquet.ui.h:50 msgid "" "These subtitles should be in the exercise language.\n" "Supported format: srt" msgstr "" "Ces sous-titres devraient être dans la langue de l'exercice.\n" "Format supporté : srt" #: ../data/perroquet.ui.h:52 msgid "" "These subtitles should be in your mother language.\n" "Supported format: srt" msgstr "" "Ces sous-titres devraient être dans votre langue maternelle.\n" "Format supporté : srt" #: ../data/perroquet.ui.h:54 msgid "" "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 3 of the License, or (at your option) " "any later version.\n" "\n" "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.\n" "\n" "You should have received a copy of the GNU General Public License along with " "this program. If not, see ." msgstr "" "Ce programme est un logiciel libre ; vous pouvez le redistribuer et/ou le " "modifier conformément aux dispositions de la Licence Publique Générale GNU, " "telle que publiée par la Free Software Foundation ; version 3 de la licence, " "ou encore (à votre choix) toute version ultérieure.\n" "\n" "Ce programme est distribué dans l'espoir qu'il sera utile, mais SANS AUCUNE " "GARANTIE ; sans même la garantie implicite de COMMERCIALISATION ou " "D'ADAPTATION À UN OBJET PARTICULIER. Pour plus de détails, voir la Licence " "Publique Générale GNU.\n" "\n" "Un exemplaire de la Licence Publique Générale GNU doit être fourni avec ce " "programme ; si ce n'est pas le cas, écrivez à la Free Software Foundation, " "Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA." #: ../data/perroquet.ui.h:59 ../perroquetlib/gui/gui_exercise_manager.py:291 msgid "Translation" msgstr "Traduction" #: ../data/perroquet.ui.h:60 msgid "Translation subtitles (optional): " msgstr "Sous-titres de traduction (optionnel) : " #: ../data/perroquet.ui.h:61 msgid "" "Word filter\n" " This filter use regexp. Examples:\n" " abc = all words containing 'abc'\n" " ^abc = all words starting with 'abc'\n" " abc$ = all words ending with 'abc'\n" "\n" " The character . can replace any character:\n" " ^abc.. = all words of at least 5 letters starting with 'abc'\n" " ^...$ = all 4-letter word\n" " ^ab.c$ = all 4-letter word beginning with 'ab' and ending with 'c'\n" "\n" " For more information on regular expressions: " "http://en.wikipedia.org/wiki/Regular_expression" msgstr "" "Filtre : \\n\n" "Ce filtre utilise les expressions régulières. Par exemple : \\n\n" "abc correspond à tous les mots qui contiennent 'abc'\\n\n" "^abc correspond à tous les mots qui commencent par 'abc'\\n\n" "abc$ correspond à tous les mots qui se terminent par 'abc'\\n\n" "Le caractère . est un joker pour remplacer n'importe quel caractère : \\n\n" "^abc.. correspond à tous les mots d'au moins 5 lettres qui commencent par " "'abc'\\n\n" "^...$ correspond à tous les mots de 3 lettres\\n\n" "^ab.c$ correspond à tous les mots de 4 lettres commençant par 'ab' et " "finissant par 'c'" #: ../data/perroquet.ui.h:73 msgid "Words" msgstr "Mots" #: ../data/perroquet.ui.h:74 msgid "_Advanced properties" msgstr "_Propriétés avancées" #: ../data/perroquet.ui.h:75 msgid "_Exercise" msgstr "_Exercice" #: ../data/perroquet.ui.h:76 msgid "_Exercise manager" msgstr "Gestionnaire d'_exercices" #: ../data/perroquet.ui.h:77 msgid "_File" msgstr "_Fichier" #: ../data/perroquet.ui.h:78 msgid "open exercise" msgstr "Ouvrir un exercice" #: ../data/perroquet.ui.h:79 msgid "open saved exercise" msgstr "Ouvrir un exercice sauvegardé" #: ../data/properties.ui.h:2 ../data/properties_advanced.ui.h:6 msgid "Exercise: " msgstr "Exercice : " #: ../data/properties.ui.h:4 ../data/properties_advanced.ui.h:12 msgid "Media: " msgstr "Média : " #: ../data/properties.ui.h:5 ../data/settings.ui.h:13 msgid "Repeat sequences after completed it" msgstr "Répéter les séquences après les avoir complétées" #: ../data/properties.ui.h:8 ../data/properties_advanced.ui.h:25 msgid "Translation: " msgstr "Traduction : " #: ../data/properties.ui.h:9 ../data/properties_advanced.ui.h:26 msgid "Use dynamic correction" msgstr "Utiliser la correction dynamique" #: ../data/properties.ui.h:10 msgid "" "With dynamic correction, the correct words will be displayed as valid as " "soon as they are correctly typed." msgstr "" "Avec la correction dynamique, les mots valides seront affichés comme tels " "dès qu'ils seront correctement écris." #: ../data/properties_advanced.ui.h:1 msgid "Correction password : " msgstr "Mot de passe de correction : " #: ../data/properties_advanced.ui.h:2 ../data/settings.ui.h:3 msgid "Disable help tools" msgstr "Désactiver les outils d'aide" #: ../data/properties_advanced.ui.h:3 msgid "Exercise" msgstr "Exercice" #: ../data/properties_advanced.ui.h:4 msgid "Exercise name:" msgstr "Nom de l'exercice :" #: ../data/properties_advanced.ui.h:8 msgid "Lock exercise's correction" msgstr "Verrouiller la correction de l'exercice" #: ../data/properties_advanced.ui.h:9 msgid "Lock exercise's propreties" msgstr "Verrouiller les paramètres de l'exercice" #: ../data/properties_advanced.ui.h:10 msgid "Locks" msgstr "Protections" #: ../data/properties_advanced.ui.h:11 ../data/settings.ui.h:9 msgid "Maximum sequence time (s): " msgstr "Durée maximale d'une séquence (en seconde) " #: ../data/properties_advanced.ui.h:13 msgid "Paths" msgstr "Emplacements" #: ../data/properties_advanced.ui.h:14 msgid "Paths list" msgstr "Liste des emplacements" #: ../data/properties_advanced.ui.h:15 ../data/settings.ui.h:10 msgid "Play sequence in a random order" msgstr "Jouer les séquences dans un ordre aléatoire" #: ../data/properties_advanced.ui.h:16 ../data/settings.ui.h:11 msgid "Playing time after sequences (ms):" msgstr "Marge de lecture à la fin de chaque séquence (en millisecondes)" #: ../data/properties_advanced.ui.h:17 ../data/settings.ui.h:12 msgid "Playing time before sequences (ms):" msgstr "Marge de lecture au début de chaque séquence (en millisecondes)" #: ../data/properties_advanced.ui.h:18 msgid "Properties password : " msgstr "Mot de passe des paramètres " #: ../data/properties_advanced.ui.h:19 msgid "Repeat count limit by sequence (0 for no limit):" msgstr "" "Nombre limite de répétition(s) par séquence (indiquer 0 pour aucune limite)." #: ../data/properties_advanced.ui.h:20 msgid "Repeat sequences after having completed it" msgstr "Répéter les séquences après les avoir complétées" #: ../data/properties_advanced.ui.h:23 msgid "Sequences" msgstr "Séquences" #: ../data/properties_advanced.ui.h:24 ../data/settings.ui.h:19 msgid "Time between sequences (s):" msgstr "Temps entre 2 séquences (en seconde) :" #: ../data/exercise_manager.ui.h:1 msgid "Details" msgstr "Détails" #: ../data/exercise_manager.ui.h:2 msgid "Exercises" msgstr "Exercices" #: ../data/exercise_manager.ui.h:3 msgid "Perroquet exercises manager" msgstr "Gestionnaire d'exercices de Perroquet" #: ../data/exercise_manager.ui.h:4 msgid "Repositories" msgstr "Dépôts" #: ../data/exercise_manager.ui.h:5 ../data/settings.ui.h:14 msgid "Settings" msgstr "Paramètres" #: ../data/exercise_manager.ui.h:6 msgid "Tree view mode" msgstr "Mode de vue en arbre" #: ../data/exercise_manager.ui.h:7 #: ../perroquetlib/gui/gui_exercise_manager.py:364 msgid "Use" msgstr "Utiliser" #: ../data/gui_message_dialog.ui.h:1 msgid "label" msgstr "étiquette" #: ../data/gui_password_dialog.ui.h:1 #: ../perroquetlib/gui/gui_password_dialog.py:46 msgid "Correction password :" msgstr "Mot de passe de correction :" #: ../data/gui_password_dialog.ui.h:2 msgid "Password for unlock correction" msgstr "Mot de passe pour déverrouiller la correction" #: ../data/reset.ui.h:1 msgid "Cancel" msgstr "Annuler" #: ../data/reset.ui.h:2 msgid "Do you want to reset the current exercise ?" msgstr "Voulez-vous vraiment réinitialiser l'exercice en cours ?" #: ../data/reset.ui.h:3 msgid "Ok" msgstr "Ok" #: ../data/settings.ui.h:1 msgid "Advanced" msgstr "Avancé" #: ../data/settings.ui.h:2 msgid "Default exercise properties" msgstr "Propriétés par défaut pour les exercices" #: ../data/settings.ui.h:4 msgid "Exercise type:" msgstr "Type d'exercice :" #: ../data/settings.ui.h:5 msgid "General" msgstr "Général" #: ../data/settings.ui.h:6 msgid "Interface" msgstr "Interface" #: ../data/settings.ui.h:8 msgid "Limit repeat count by sequence (0 for no limit):" msgstr "" "Nombre limite de répétitions par séquence (mettez 0 pour aucune limite)" #: ../data/settings.ui.h:15 msgid "Show menu bar" msgstr "Afficher la barre de menu" #: ../data/settings.ui.h:16 msgid "Show play and pause buttons" msgstr "Afficher les boutons Lecture et Pause" #: ../data/settings.ui.h:17 msgid "Show settings" msgstr "Afficher les préférences" #: ../data/settings.ui.h:18 msgid "Skip valid sequences using next and previous sequence buttons" msgstr "" "Sauter les séquences complétées lors de l'utilisation des boutons Séquence " "suivante et Séquence précédente" #: ../data/settings.ui.h:20 msgid "Use speed changer" msgstr "Utiliser l'outil de changement de vitesse" #: ../perroquetlib/core.py:504 msgid "Untitled exercise" msgstr "Exercice sans titre" #: ../perroquetlib/core.py:556 msgid "" "Import finish succesfully. Use the exercises manager to use the newly " "installed exercise." msgstr "" "Importation réussie. Veuillez utiliser le gestionnaire d'exercices pour " "utiliser l'exercice que vous venez d'installer." #: ../perroquetlib/core.py:558 msgid "Import failed." msgstr "L'importation a échoué." #: ../perroquetlib/repository/exercise_repository_exercise.py:51 #: ../perroquetlib/repository/exercise_repository_exercise.py:52 #: ../perroquetlib/repository/exercise_repository_exercise.py:53 #: ../perroquetlib/repository/exercise_repository_exercise.py:54 #: ../perroquetlib/repository/exercise_repository_exercise.py:55 #: ../perroquetlib/repository/exercise_repository_exercise.py:56 #: ../perroquetlib/repository/exercise_repository_exercise.py:57 #: ../perroquetlib/repository/exercise_repository_exercise.py:58 #: ../perroquetlib/repository/exercise_repository_exercise.py:59 #: ../perroquetlib/repository/exercise_repository_exercise.py:60 msgid "Not specified" msgstr "Non spécifié" #: ../perroquetlib/repository/exercise_repository_exercise.py:451 msgid "Imported exercise" msgstr "Exercice importé" #: ../perroquetlib/repository/exercise_repository_manager.py:345 msgid "File not found: " msgstr "Fichier introuvable : " #: ../perroquetlib/repository/exercise_repository_manager.py:359 msgid "Invalid package, missing template.perroquet." msgstr "Paquet invalide : le fichier template.perroquet est manquant." #: ../perroquetlib/repository/exercise_repository_manager.py:385 msgid "Exercise already exist." msgstr "L'exercice existe déjà." #: ../perroquetlib/gui/gui.py:85 #, python-format msgid "The file '%s' doesn't exist. Please modify exercise paths" msgstr "" "Le fichier '%s' n'existe pas. Veuillez modifier l'emplacement des fichiers " "de l'exercice." #: ../perroquetlib/gui/gui.py:86 msgid "load error" msgstr "Erreur de chargement" #: ../perroquetlib/gui/gui.py:283 #: ../perroquetlib/gui/gui_exercise_manager.py:236 #: ../perroquetlib/gui/gui_sequence_properties_advanced.py:72 msgid "Path" msgstr "Emplacement" #: ../perroquetlib/gui/gui.py:305 msgid "Do you really quit without save ?" msgstr "Voulez-vous vraiment quitter sans enregistrer l'exercice ?" #: ../perroquetlib/gui/gui.py:306 msgid "Confirm quit" msgstr "Confirmer la fermeture" #: ../perroquetlib/gui/gui.py:320 msgid "Information" msgstr "Information" #: ../perroquetlib/gui/gui.py:809 msgid "Select File to open" msgstr "Sélectionner un ficher à ouvrir" #: ../perroquetlib/gui/gui.py:814 ../perroquetlib/gui/gui.py:852 msgid "Perroquet files" msgstr "Fichiers Perroquet" #: ../perroquetlib/gui/gui.py:819 ../perroquetlib/gui/gui.py:838 #: ../perroquetlib/gui/gui.py:857 ../perroquetlib/gui/gui.py:878 #: ../perroquetlib/gui/gui.py:899 msgid "All files" msgstr "Tous les fichiers" #: ../perroquetlib/gui/gui.py:828 msgid "Select package to import" msgstr "Selectionnez un paquet à installer" #: ../perroquetlib/gui/gui.py:833 ../perroquetlib/gui/gui.py:894 msgid "Perroquet package files" msgstr "Paquets perroquet" #: ../perroquetlib/gui/gui.py:847 msgid "Select File to Save to" msgstr "Enregistrer sous..." #: ../perroquetlib/gui/gui.py:868 ../perroquetlib/gui/gui.py:889 msgid "Select File to Export to" msgstr "Sélectionnez l'emplacement pour exporter" #: ../perroquetlib/gui/gui.py:873 msgid "Perroquet template files" msgstr "Fichiers de modèle perroquet" #: ../perroquetlib/gui/gui_controller.py:237 #, python-format msgid "- Sequences: %(found)s/%(count)s (%(percent)s %%)\n" msgstr "- Séquences : %(found)s/%(count)s (%(percent)s %%)\n" #: ../perroquetlib/gui/gui_controller.py:238 #, python-format msgid "- Words: %(found)s/%(count)s (%(percent)s %%)\n" msgstr "- Mots : %(found)s/%(count)s (%(percent)s %%)\n" #: ../perroquetlib/gui/gui_controller.py:239 #, python-format msgid "- Repeat ratio: %s per words" msgstr "Taux de répétition : %s par mot" #. Wrong password #: ../perroquetlib/gui/gui_controller.py:287 #: ../perroquetlib/gui/gui_controller.py:518 msgid "Wrong password" msgstr "Mauvais mot de passe" #: ../perroquetlib/gui/gui_exercise_manager.py:82 msgid "Updating repositories..." msgstr "Mise à jour de la liste des dépôts..." #: ../perroquetlib/gui/gui_exercise_manager.py:108 msgid "Local repository" msgstr "Dépôt local" #: ../perroquetlib/gui/gui_exercise_manager.py:110 msgid "Distant repository" msgstr "Dépôt distant" #: ../perroquetlib/gui/gui_exercise_manager.py:112 msgid "Offline repository" msgstr "Dépôt hors-ligne" #: ../perroquetlib/gui/gui_exercise_manager.py:114 msgid "Orphan repository" msgstr "Dépôt orphelin" #: ../perroquetlib/gui/gui_exercise_manager.py:132 msgid "Group" msgstr "Groupe" #: ../perroquetlib/gui/gui_exercise_manager.py:148 #: ../perroquetlib/gui/gui_exercise_manager.py:266 #: ../perroquetlib/gui/gui_exercise_manager.py:446 msgid "Installed" msgstr "Installé" #: ../perroquetlib/gui/gui_exercise_manager.py:151 #: ../perroquetlib/gui/gui_exercise_manager.py:268 #: ../perroquetlib/gui/gui_exercise_manager.py:439 msgid "Available" msgstr "Disponible" #: ../perroquetlib/gui/gui_exercise_manager.py:154 #: ../perroquetlib/gui/gui_exercise_manager.py:270 #: ../perroquetlib/gui/gui_exercise_manager.py:454 msgid "Used" msgstr "Utilisé" #: ../perroquetlib/gui/gui_exercise_manager.py:157 #: ../perroquetlib/gui/gui_exercise_manager.py:272 #: ../perroquetlib/gui/gui_exercise_manager.py:456 msgid "Done" msgstr "Terminé" #: ../perroquetlib/gui/gui_exercise_manager.py:168 msgid "Exercise" msgstr "Exercice" #: ../perroquetlib/gui/gui_exercise_manager.py:174 #: ../perroquetlib/gui/gui_exercise_manager.py:274 #: ../perroquetlib/gui/gui_exercise_manager.py:307 msgid "Name" msgstr "Nom" #: ../perroquetlib/gui/gui_exercise_manager.py:179 msgid "Type" msgstr "Type" #: ../perroquetlib/gui/gui_exercise_manager.py:184 #: ../perroquetlib/gui/gui_exercise_manager.py:276 msgid "Description" msgstr "Description" #: ../perroquetlib/gui/gui_exercise_manager.py:189 msgid "Status" msgstr "État" #: ../perroquetlib/gui/gui_exercise_manager.py:194 #: ../perroquetlib/gui/gui_exercise_manager.py:283 msgid "Words count" msgstr "Nombre de mots" #: ../perroquetlib/gui/gui_exercise_manager.py:275 msgid "Licence" msgstr "Licence" #: ../perroquetlib/gui/gui_exercise_manager.py:277 msgid "Author" msgstr "Auteur" #: ../perroquetlib/gui/gui_exercise_manager.py:278 msgid "Author website" msgstr "Site web de l'auteur" #: ../perroquetlib/gui/gui_exercise_manager.py:279 msgid "Author contact" msgstr "Contacter l'auteur" #: ../perroquetlib/gui/gui_exercise_manager.py:282 msgid "Version" msgstr "Version" #: ../perroquetlib/gui/gui_exercise_manager.py:284 msgid "Language" msgstr "Langue" #: ../perroquetlib/gui/gui_exercise_manager.py:285 msgid "Media type" msgstr "Type de média" #: ../perroquetlib/gui/gui_exercise_manager.py:286 msgid "Id" msgstr "Identifiant" #: ../perroquetlib/gui/gui_exercise_manager.py:287 msgid "Install status" msgstr "État de l'installation" #: ../perroquetlib/gui/gui_exercise_manager.py:295 msgid "Packager" msgstr "Créateur du paquet" #: ../perroquetlib/gui/gui_exercise_manager.py:296 msgid "Packager website" msgstr "Site web du créateur du paquet" #: ../perroquetlib/gui/gui_exercise_manager.py:297 msgid "Packager contact" msgstr "Contacter le créateur du paquet" #: ../perroquetlib/gui/gui_exercise_manager.py:299 msgid "Exercise path" msgstr "Emplacement de l'exercice" #: ../perroquetlib/gui/gui_exercise_manager.py:300 msgid "Package path" msgstr "Emplacement du paquet" #: ../perroquetlib/gui/gui_exercise_manager.py:301 msgid "Template path" msgstr "Emplacement du modèle" #: ../perroquetlib/gui/gui_exercise_manager.py:302 msgid "Instance path" msgstr "Emplacement de l'instance" #: ../perroquetlib/gui/gui_exercise_manager.py:303 msgid "Finished exercise path" msgstr "Emplacement de l'exercice terminé" #: ../perroquetlib/gui/gui_exercise_manager.py:312 msgid "Value" msgstr "Valeur" #: ../perroquetlib/gui/gui_exercise_manager.py:352 msgid "Install" msgstr "Installer" #: ../perroquetlib/gui/gui_exercise_manager.py:356 #: ../perroquetlib/gui/gui_exercise_manager.py:360 msgid "Cancel install" msgstr "Annuler l'installation" #: ../perroquetlib/gui/gui_exercise_manager.py:368 #: ../perroquetlib/gui/gui_exercise_manager.py:376 msgid "Remove" msgstr "Supprimer" #: ../perroquetlib/gui/gui_exercise_manager.py:372 msgid "Continue" msgstr "Continuer" #: ../perroquetlib/gui/gui_exercise_manager.py:416 #, python-format msgid "%d available exercises" msgstr "%d exercices disponibles" #: ../perroquetlib/gui/gui_exercise_manager.py:418 #, python-format msgid "%d available exercise" msgstr "%d exercice disponible" #: ../perroquetlib/gui/gui_exercise_manager.py:422 #, python-format msgid " and %d installed exercises" msgstr " et %d exercices installés" #: ../perroquetlib/gui/gui_exercise_manager.py:424 #, python-format msgid " and %d installed exercise" msgstr " et %d exercice installé" #: ../perroquetlib/gui/gui_exercise_manager.py:441 msgid "Downloading" msgstr "Téléchargement" #: ../perroquetlib/gui/gui_exercise_manager.py:444 msgid "Installing" msgstr "Installation" #: ../perroquetlib/gui/gui_exercise_manager.py:448 msgid "Corrupted" msgstr "Corrompu" #: ../perroquetlib/gui/gui_exercise_manager.py:450 msgid "Canceled" msgstr "Annulé" #: ../perroquetlib/gui/gui_exercise_manager.py:452 msgid "Removing" msgstr "Suppression" #: ../perroquetlib/gui/gui_exercise_manager.py:464 #, python-format msgid "Downloading ... %d%%" msgstr "Téléchargement ... %d%%" #: ../perroquetlib/gui/gui_password_dialog.py:49 msgid "Properties password :" msgstr "Mot de passe des propriétés :" #: ../perroquetlib/video_player.py:49 msgid "" "You need to install the gstreamer soundtouch elements to use slowly play " "feature." msgstr "" "Vous devez installer les éléments GStreamer SoundTouch pour utiliser la " "fonctionnalité de ralentissement de la lecture." #~ msgid "New exercice" #~ msgstr "Nouvel exercice" #~ msgid "Open saved exercice" #~ msgstr "Ouvrir un exercice" #~ msgid "Save exercice" #~ msgstr "Enregistrer l'exercice" #~ msgid "Exercice properties" #~ msgstr "Propriétés de l'exercice" #~ msgid "Copyright © 2009-2010 Frédéric Bertolus" #~ msgstr "Copyright © 2009-2010 Frédéric Bertolus" #~ msgid "Sélectionner un fichier de sous-titre" #~ msgstr "Sélectionner un fichier de sous-titre" #~ msgid "Translation : " #~ msgstr "Traduction : " #~ msgid "Sélectionner un fichier vidéo" #~ msgstr "Sélectionner un fichier vidéo" #~ msgid "Exercice : " #~ msgstr "Exercice : " #~ msgid "toggletoolbutton1" #~ msgstr "toggletoolbutton1" #~ msgid "playButton" #~ msgstr "playButton" #~ msgid "toolbutton1" #~ msgstr "toolbutton1" #~ msgid "Nouvel exercice" #~ msgstr "Nouvel exercice" #~ msgid "Media : " #~ msgstr "Media : " #~ msgid "Exercice: " #~ msgstr "Exercice : " #~ msgid "Load error" #~ msgstr "Erreur de chargement" #~ msgid "Select File to Open" #~ msgstr "Sélectionner le fichier à ouvrir" #~ msgid "A oral comprehension teacher" #~ msgstr "Un programme éducatif pour la compréhension orale" #~ msgid "Oral comprehention teacher" #~ msgstr "Un programme éducatif pour la compréhension orale" #~ msgid "Copyright © 2009-2010 Frédéric Bertolus" #~ msgstr "Copyright © 2009-2010 Frédéric Bertolus" #~ msgid "" #~ "- Sequences: --/--\n" #~ "- Words: --/--\n" #~ "- Repeat ratio: -- per words" #~ msgstr "" #~ "- Séquences : --/--\n" #~ "- Mots : --/--\n" #~ "- Ratio de répétition : -- par mots" #~ msgid "Exercice video or audio: " #~ msgstr "Exercice vidéo ou audio : " #~ msgid "Open exercise" #~ msgstr "Ouvrir un exercice" #~ msgid "" #~ "These subtitles should be in the exercice language.\n" #~ "Supported format: srt" #~ msgstr "" #~ "Ces sous-titres doivent être dans la langue de l'exercice.\n" #~ "Format supporté : srt" #~ msgid "" #~ "Word filter\n" #~ "This filter use regexp. Examples:\n" #~ "abc = all words containing 'abc'\n" #~ "^abc = all words starting with 'abc'\n" #~ "abc$ = all words ending with 'abc'\n" #~ "\n" #~ "The character . can replace any character:\n" #~ "^abc.. = all words of at least 5 letters starting with 'abc'\n" #~ "^...$ = all 4-letter word\n" #~ "^ab.c$ = all 4-letter word beginning with 'ab' and ending with 'c'\n" #~ "\n" #~ "For more information on regular expressions: " #~ "http://en.wikipedia.org/wiki/Regular_expression" #~ msgstr "" #~ "Filtre : \n" #~ "Ce filtre utilise les expressions régulières. Par exemple : \n" #~ "abc correspond à tous les mots qui contiennent 'abc'\n" #~ "^abc correspond à tous les mots qui commencent par 'abc'\n" #~ "abc$ correspond à tous les mots qui se terminent par 'abc'\n" #~ "Le caractère . est un joker pour remplacer n'importe quel caractère : \n" #~ "^abc.. correspond à tous les mots d'au moins 5 lettres qui commencent par " #~ "'abc'\n" #~ "^...$ correspond à tous les mots de 3 lettres\n" #~ "^ab.c$ correspond à tous les mots de 4 lettres commençant par 'ab' et " #~ "finissant par 'c'" #~ msgid "Exercice subtitle: " #~ msgstr "Sous-titre de l'exercice : " #~ msgid "The audio track should be in the exercice language" #~ msgstr "La piste audio doit être dans la langue de l'exercice" #, python-format #~ msgid "The file '%s' doesn't exist. Please modify exercice paths" #~ msgstr "" #~ "Le fichier '%s' n'existe pas. Veuillez modifier le chemin d'accès à " #~ "l'exercice." #~ msgid "List all words to find in the exercise" #~ msgstr "Liste des mots à trouver dans l'exercice" #~ msgid "Open saved exercise" #~ msgstr "Ouvrir un exercice enregistré" #~ msgid "" #~ "You need to install the gstreamer soundtouch elements for play it slowly to. " #~ "They are part of gstreamer-plugins-bad. Consult the README if you need more " #~ "information." #~ msgstr "" #~ "Vous devez installer les composants gstreamer soundtouch pour lire la vidéo " #~ "lentement. Ces composants font partie de gstreamer-plugins-bad. Consulter le " #~ "README pour plus d'informations." #~ msgid "_Exercice manager" #~ msgstr "_Gestionaire d'exercice" #~ msgid "Show all paths" #~ msgstr "Afficher tous les chemins" #~ msgid "Lock exercise's properties" #~ msgstr "Verrouiller les propriétés de l'exercice" #~ msgid "Done path" #~ msgstr "Emplacement créé" #~ msgid "" #~ "Properties menus will not be accessible. Modify the perroquet file to remove " #~ "the lock or modify properties" #~ msgstr "" #~ "Las propiedades de menu no serán accesibles. Modificar el archivo perroquet " #~ "para remover el bloqueo o modificar propiedades." perroquet-1.1.1.orig/po/perroquet.pot0000644000175000017500000004427311561344350020101 0ustar georgeskgeorgesk#, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2010-06-19 10:43+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2010-06-20 10:20+0000\n" "X-Generator: Launchpad (build Unknown)\n" #: ../data/perroquet.desktop.in.h:1 msgid "Oral comprehension teacher" msgstr "" #: ../data/perroquet.desktop.in.h:2 ../data/perroquet.ui.h:31 #: ../perroquetlib/gui/gui_controller.py:201 msgid "Perroquet" msgstr "" #: ../data/perroquet.ui.h:1 msgid "" "- Sequences: --/--\n" " - Words: --/--\n" " - Repeat ratio: -- per words" msgstr "" #: ../data/perroquet.ui.h:4 msgid "-- s" msgstr "" #: ../data/perroquet.ui.h:5 msgid "--/--" msgstr "" #: ../data/perroquet.ui.h:6 msgid "An oral comprehension teacher" msgstr "" #: ../data/perroquet.ui.h:7 msgid "Copyright © 2009-2010 Perroquet Team" msgstr "" #: ../data/perroquet.ui.h:8 msgid "Correction" msgstr "" #: ../data/perroquet.ui.h:9 msgid "Display correction" msgstr "" #: ../data/perroquet.ui.h:10 msgid "Display translation" msgstr "" #: ../data/perroquet.ui.h:11 msgid "E_dit" msgstr "" #: ../data/perroquet.ui.h:12 msgid "Erase" msgstr "" #: ../data/perroquet.ui.h:13 msgid "Exercise language" msgstr "" #: ../data/perroquet.ui.h:14 ../data/properties.ui.h:1 #: ../data/properties_advanced.ui.h:5 msgid "Exercise properties" msgstr "" #: ../data/perroquet.ui.h:15 msgid "Exercise subtitle: " msgstr "" #: ../data/perroquet.ui.h:16 msgid "Exercise video or audio: " msgstr "" #: ../data/perroquet.ui.h:17 msgid "Export" msgstr "" #: ../data/perroquet.ui.h:18 msgid "Export as package..." msgstr "" #: ../data/perroquet.ui.h:19 msgid "Export as template..." msgstr "" #: ../data/perroquet.ui.h:20 msgid "H_elp" msgstr "" #: ../data/perroquet.ui.h:21 msgid "Hint" msgstr "" #: ../data/perroquet.ui.h:22 msgid "Import a package" msgstr "" #: ../data/perroquet.ui.h:23 ../data/properties.ui.h:3 #: ../data/properties_advanced.ui.h:7 ../data/settings.ui.h:7 msgid "Language:" msgstr "" #: ../data/perroquet.ui.h:24 msgid "Last open files" msgstr "" #: ../data/perroquet.ui.h:25 msgid "Lateral panel" msgstr "" #: ../data/perroquet.ui.h:26 msgid "List of all words to find in the exercise" msgstr "" #: ../data/perroquet.ui.h:27 msgid "New exercise" msgstr "" #: ../data/perroquet.ui.h:28 msgid "Next Sequence" msgstr "" #: ../data/perroquet.ui.h:29 msgid "Next sequence" msgstr "" #: ../data/perroquet.ui.h:30 msgid "Pause" msgstr "" #: ../data/perroquet.ui.h:32 msgid "Play" msgstr "" #: ../data/perroquet.ui.h:33 msgid "Play speed" msgstr "" #: ../data/perroquet.ui.h:34 msgid "Position in current sequence" msgstr "" #: ../data/perroquet.ui.h:35 msgid "Position in exercise" msgstr "" #: ../data/perroquet.ui.h:36 msgid "Previous sequence" msgstr "" #: ../data/perroquet.ui.h:37 msgid "Progress statistics" msgstr "" #: ../data/perroquet.ui.h:38 msgid "Properties" msgstr "" #: ../data/perroquet.ui.h:39 msgid "Replay sequence" msgstr "" #: ../data/perroquet.ui.h:40 msgid "Reset exercise progress" msgstr "" #: ../data/perroquet.ui.h:41 msgid "Reset the exercise progress" msgstr "" #: ../data/perroquet.ui.h:42 msgid "Reveal the sequence" msgstr "" #: ../data/perroquet.ui.h:43 msgid "Reveal the word" msgstr "" #: ../data/perroquet.ui.h:44 msgid "Save exercise" msgstr "" #: ../data/perroquet.ui.h:45 ../data/properties.ui.h:6 #: ../data/properties_advanced.ui.h:21 msgid "Select a subtitle file" msgstr "" #: ../data/perroquet.ui.h:46 ../data/properties.ui.h:7 #: ../data/properties_advanced.ui.h:22 msgid "Select a video file" msgstr "" #: ../data/perroquet.ui.h:47 msgid "Show/Hide correction" msgstr "" #: ../data/perroquet.ui.h:48 msgid "Show/Hide translation" msgstr "" #: ../data/perroquet.ui.h:49 msgid "The audio track should be in the exercise language" msgstr "" #: ../data/perroquet.ui.h:50 msgid "" "These subtitles should be in the exercise language.\n" "Supported format: srt" msgstr "" #: ../data/perroquet.ui.h:52 msgid "" "These subtitles should be in your mother language.\n" "Supported format: srt" msgstr "" #: ../data/perroquet.ui.h:54 msgid "" "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 3 of the License, or (at your option) " "any later version.\n" "\n" "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.\n" "\n" "You should have received a copy of the GNU General Public License along with " "this program. If not, see ." msgstr "" #: ../data/perroquet.ui.h:59 ../perroquetlib/gui/gui_exercise_manager.py:291 msgid "Translation" msgstr "" #: ../data/perroquet.ui.h:60 msgid "Translation subtitles (optional): " msgstr "" #: ../data/perroquet.ui.h:61 msgid "" "Word filter\n" " This filter use regexp. Examples:\n" " abc = all words containing 'abc'\n" " ^abc = all words starting with 'abc'\n" " abc$ = all words ending with 'abc'\n" "\n" " The character . can replace any character:\n" " ^abc.. = all words of at least 5 letters starting with 'abc'\n" " ^...$ = all 4-letter word\n" " ^ab.c$ = all 4-letter word beginning with 'ab' and ending with 'c'\n" "\n" " For more information on regular expressions: " "http://en.wikipedia.org/wiki/Regular_expression" msgstr "" #: ../data/perroquet.ui.h:73 msgid "Words" msgstr "" #: ../data/perroquet.ui.h:74 msgid "_Advanced properties" msgstr "" #: ../data/perroquet.ui.h:75 msgid "_Exercise" msgstr "" #: ../data/perroquet.ui.h:76 msgid "_Exercise manager" msgstr "" #: ../data/perroquet.ui.h:77 msgid "_File" msgstr "" #: ../data/perroquet.ui.h:78 msgid "open exercise" msgstr "" #: ../data/perroquet.ui.h:79 msgid "open saved exercise" msgstr "" #: ../data/properties.ui.h:2 ../data/properties_advanced.ui.h:6 msgid "Exercise: " msgstr "" #: ../data/properties.ui.h:4 ../data/properties_advanced.ui.h:12 msgid "Media: " msgstr "" #: ../data/properties.ui.h:5 ../data/settings.ui.h:13 msgid "Repeat sequences after completed it" msgstr "" #: ../data/properties.ui.h:8 ../data/properties_advanced.ui.h:25 msgid "Translation: " msgstr "" #: ../data/properties.ui.h:9 ../data/properties_advanced.ui.h:26 msgid "Use dynamic correction" msgstr "" #: ../data/properties.ui.h:10 msgid "" "With dynamic correction, the correct words will be displayed as valid as " "soon as they are correctly typed." msgstr "" #: ../data/properties_advanced.ui.h:1 msgid "Correction password : " msgstr "" #: ../data/properties_advanced.ui.h:2 ../data/settings.ui.h:3 msgid "Disable help tools" msgstr "" #: ../data/properties_advanced.ui.h:3 msgid "Exercise" msgstr "" #: ../data/properties_advanced.ui.h:4 msgid "Exercise name:" msgstr "" #: ../data/properties_advanced.ui.h:8 msgid "Lock exercise's correction" msgstr "" #: ../data/properties_advanced.ui.h:9 msgid "Lock exercise's propreties" msgstr "" #: ../data/properties_advanced.ui.h:10 msgid "Locks" msgstr "" #: ../data/properties_advanced.ui.h:11 ../data/settings.ui.h:9 msgid "Maximum sequence time (s): " msgstr "" #: ../data/properties_advanced.ui.h:13 msgid "Paths" msgstr "" #: ../data/properties_advanced.ui.h:14 msgid "Paths list" msgstr "" #: ../data/properties_advanced.ui.h:15 ../data/settings.ui.h:10 msgid "Play sequence in a random order" msgstr "" #: ../data/properties_advanced.ui.h:16 ../data/settings.ui.h:11 msgid "Playing time after sequences (ms):" msgstr "" #: ../data/properties_advanced.ui.h:17 ../data/settings.ui.h:12 msgid "Playing time before sequences (ms):" msgstr "" #: ../data/properties_advanced.ui.h:18 msgid "Properties password : " msgstr "" #: ../data/properties_advanced.ui.h:19 msgid "Repeat count limit by sequence (0 for no limit):" msgstr "" #: ../data/properties_advanced.ui.h:20 msgid "Repeat sequences after having completed it" msgstr "" #: ../data/properties_advanced.ui.h:23 msgid "Sequences" msgstr "" #: ../data/properties_advanced.ui.h:24 ../data/settings.ui.h:19 msgid "Time between sequences (s):" msgstr "" #: ../data/exercise_manager.ui.h:1 msgid "Details" msgstr "" #: ../data/exercise_manager.ui.h:2 msgid "Exercises" msgstr "" #: ../data/exercise_manager.ui.h:3 msgid "Perroquet exercises manager" msgstr "" #: ../data/exercise_manager.ui.h:4 msgid "Repositories" msgstr "" #: ../data/exercise_manager.ui.h:5 ../data/settings.ui.h:14 msgid "Settings" msgstr "" #: ../data/exercise_manager.ui.h:6 msgid "Tree view mode" msgstr "" #: ../data/exercise_manager.ui.h:7 #: ../perroquetlib/gui/gui_exercise_manager.py:364 msgid "Use" msgstr "" #: ../data/gui_message_dialog.ui.h:1 msgid "label" msgstr "" #: ../data/gui_password_dialog.ui.h:1 #: ../perroquetlib/gui/gui_password_dialog.py:46 msgid "Correction password :" msgstr "" #: ../data/gui_password_dialog.ui.h:2 msgid "Password for unlock correction" msgstr "" #: ../data/reset.ui.h:1 msgid "Cancel" msgstr "" #: ../data/reset.ui.h:2 msgid "Do you want to reset the current exercise ?" msgstr "" #: ../data/reset.ui.h:3 msgid "Ok" msgstr "" #: ../data/settings.ui.h:1 msgid "Advanced" msgstr "" #: ../data/settings.ui.h:2 msgid "Default exercise properties" msgstr "" #: ../data/settings.ui.h:4 msgid "Exercise type:" msgstr "" #: ../data/settings.ui.h:5 msgid "General" msgstr "" #: ../data/settings.ui.h:6 msgid "Interface" msgstr "" #: ../data/settings.ui.h:8 msgid "Limit repeat count by sequence (0 for no limit):" msgstr "" #: ../data/settings.ui.h:15 msgid "Show menu bar" msgstr "" #: ../data/settings.ui.h:16 msgid "Show play and pause buttons" msgstr "" #: ../data/settings.ui.h:17 msgid "Show settings" msgstr "" #: ../data/settings.ui.h:18 msgid "Skip valid sequences using next and previous sequence buttons" msgstr "" #: ../data/settings.ui.h:20 msgid "Use speed changer" msgstr "" #: ../perroquetlib/core.py:504 msgid "Untitled exercise" msgstr "" #: ../perroquetlib/core.py:556 msgid "" "Import finish succesfully. Use the exercises manager to use the newly " "installed exercise." msgstr "" #: ../perroquetlib/core.py:558 msgid "Import failed." msgstr "" #: ../perroquetlib/repository/exercise_repository_exercise.py:51 #: ../perroquetlib/repository/exercise_repository_exercise.py:52 #: ../perroquetlib/repository/exercise_repository_exercise.py:53 #: ../perroquetlib/repository/exercise_repository_exercise.py:54 #: ../perroquetlib/repository/exercise_repository_exercise.py:55 #: ../perroquetlib/repository/exercise_repository_exercise.py:56 #: ../perroquetlib/repository/exercise_repository_exercise.py:57 #: ../perroquetlib/repository/exercise_repository_exercise.py:58 #: ../perroquetlib/repository/exercise_repository_exercise.py:59 #: ../perroquetlib/repository/exercise_repository_exercise.py:60 msgid "Not specified" msgstr "" #: ../perroquetlib/repository/exercise_repository_exercise.py:451 msgid "Imported exercise" msgstr "" #: ../perroquetlib/repository/exercise_repository_manager.py:345 msgid "File not found: " msgstr "" #: ../perroquetlib/repository/exercise_repository_manager.py:359 msgid "Invalid package, missing template.perroquet." msgstr "" #: ../perroquetlib/repository/exercise_repository_manager.py:385 msgid "Exercise already exist." msgstr "" #: ../perroquetlib/gui/gui.py:85 #, python-format msgid "The file '%s' doesn't exist. Please modify exercise paths" msgstr "" #: ../perroquetlib/gui/gui.py:86 msgid "load error" msgstr "" #: ../perroquetlib/gui/gui.py:283 #: ../perroquetlib/gui/gui_exercise_manager.py:236 #: ../perroquetlib/gui/gui_sequence_properties_advanced.py:72 msgid "Path" msgstr "" #: ../perroquetlib/gui/gui.py:305 msgid "Do you really quit without save ?" msgstr "" #: ../perroquetlib/gui/gui.py:306 msgid "Confirm quit" msgstr "" #: ../perroquetlib/gui/gui.py:320 msgid "Information" msgstr "" #: ../perroquetlib/gui/gui.py:809 msgid "Select File to open" msgstr "" #: ../perroquetlib/gui/gui.py:814 ../perroquetlib/gui/gui.py:852 msgid "Perroquet files" msgstr "" #: ../perroquetlib/gui/gui.py:819 ../perroquetlib/gui/gui.py:838 #: ../perroquetlib/gui/gui.py:857 ../perroquetlib/gui/gui.py:878 #: ../perroquetlib/gui/gui.py:899 msgid "All files" msgstr "" #: ../perroquetlib/gui/gui.py:828 msgid "Select package to import" msgstr "" #: ../perroquetlib/gui/gui.py:833 ../perroquetlib/gui/gui.py:894 msgid "Perroquet package files" msgstr "" #: ../perroquetlib/gui/gui.py:847 msgid "Select File to Save to" msgstr "" #: ../perroquetlib/gui/gui.py:868 ../perroquetlib/gui/gui.py:889 msgid "Select File to Export to" msgstr "" #: ../perroquetlib/gui/gui.py:873 msgid "Perroquet template files" msgstr "" #: ../perroquetlib/gui/gui_controller.py:237 #, python-format msgid "- Sequences: %(found)s/%(count)s (%(percent)s %%)\n" msgstr "" #: ../perroquetlib/gui/gui_controller.py:238 #, python-format msgid "- Words: %(found)s/%(count)s (%(percent)s %%)\n" msgstr "" #: ../perroquetlib/gui/gui_controller.py:239 #, python-format msgid "- Repeat ratio: %s per words" msgstr "" #. Wrong password #: ../perroquetlib/gui/gui_controller.py:287 #: ../perroquetlib/gui/gui_controller.py:518 msgid "Wrong password" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:82 msgid "Updating repositories..." msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:108 msgid "Local repository" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:110 msgid "Distant repository" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:112 msgid "Offline repository" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:114 msgid "Orphan repository" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:132 msgid "Group" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:148 #: ../perroquetlib/gui/gui_exercise_manager.py:266 #: ../perroquetlib/gui/gui_exercise_manager.py:446 msgid "Installed" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:151 #: ../perroquetlib/gui/gui_exercise_manager.py:268 #: ../perroquetlib/gui/gui_exercise_manager.py:439 msgid "Available" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:154 #: ../perroquetlib/gui/gui_exercise_manager.py:270 #: ../perroquetlib/gui/gui_exercise_manager.py:454 msgid "Used" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:157 #: ../perroquetlib/gui/gui_exercise_manager.py:272 #: ../perroquetlib/gui/gui_exercise_manager.py:456 msgid "Done" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:168 msgid "Exercise" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:174 #: ../perroquetlib/gui/gui_exercise_manager.py:274 #: ../perroquetlib/gui/gui_exercise_manager.py:307 msgid "Name" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:179 msgid "Type" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:184 #: ../perroquetlib/gui/gui_exercise_manager.py:276 msgid "Description" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:189 msgid "Status" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:194 #: ../perroquetlib/gui/gui_exercise_manager.py:283 msgid "Words count" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:275 msgid "Licence" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:277 msgid "Author" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:278 msgid "Author website" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:279 msgid "Author contact" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:282 msgid "Version" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:284 msgid "Language" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:285 msgid "Media type" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:286 msgid "Id" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:287 msgid "Install status" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:295 msgid "Packager" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:296 msgid "Packager website" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:297 msgid "Packager contact" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:299 msgid "Exercise path" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:300 msgid "Package path" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:301 msgid "Template path" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:302 msgid "Instance path" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:303 msgid "Finished exercise path" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:312 msgid "Value" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:352 msgid "Install" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:356 #: ../perroquetlib/gui/gui_exercise_manager.py:360 msgid "Cancel install" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:368 #: ../perroquetlib/gui/gui_exercise_manager.py:376 msgid "Remove" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:372 msgid "Continue" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:416 #, python-format msgid "%d available exercises" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:418 #, python-format msgid "%d available exercise" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:422 #, python-format msgid " and %d installed exercises" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:424 #, python-format msgid " and %d installed exercise" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:441 msgid "Downloading" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:444 msgid "Installing" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:448 msgid "Corrupted" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:450 msgid "Canceled" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:452 msgid "Removing" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:464 #, python-format msgid "Downloading ... %d%%" msgstr "" #: ../perroquetlib/gui/gui_password_dialog.py:49 msgid "Properties password :" msgstr "" #: ../perroquetlib/video_player.py:49 msgid "" "You need to install the gstreamer soundtouch elements to use slowly play " "feature." msgstr "" perroquet-1.1.1.orig/po/pt_BR.po0000644000175000017500000006537611561344350016704 0ustar georgeskgeorgesk# Brazilian Portuguese translation for perroquet # Copyright (c) 2010 Rosetta Contributors and Canonical Ltd 2010 # This file is distributed under the same license as the perroquet package. # FIRST AUTHOR , 2010. # msgid "" msgstr "" "Project-Id-Version: perroquet\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2010-06-19 10:43+0200\n" "PO-Revision-Date: 2010-07-31 04:38+0000\n" "Last-Translator: Fred Bertolus \n" "Language-Team: Brazilian Portuguese \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2011-01-17 05:02+0000\n" "X-Generator: Launchpad (build 12177)\n" #: ../data/perroquet.desktop.in.h:1 msgid "Oral comprehension teacher" msgstr "Instrutor de Compreensão Oral" #: ../data/perroquet.desktop.in.h:2 ../data/perroquet.ui.h:31 #: ../perroquetlib/gui/gui_controller.py:201 msgid "Perroquet" msgstr "Perroquet" #: ../data/perroquet.ui.h:1 msgid "" "- Sequences: --/--\n" " - Words: --/--\n" " - Repeat ratio: -- per words" msgstr "" "- Sequências: --/--\n" " - Palavras: --/--\n" " - Taxa de Repetição: -- por palavras" #: ../data/perroquet.ui.h:4 msgid "-- s" msgstr "-- s" #: ../data/perroquet.ui.h:5 msgid "--/--" msgstr "--/--" #: ../data/perroquet.ui.h:6 msgid "An oral comprehension teacher" msgstr "Um instrutor de compreensão oral" #: ../data/perroquet.ui.h:7 msgid "Copyright © 2009-2010 Perroquet Team" msgstr "Copyright © 2009-2010 Perroquet Team" #: ../data/perroquet.ui.h:8 msgid "Correction" msgstr "Correção" #: ../data/perroquet.ui.h:9 msgid "Display correction" msgstr "Correção de tela" #: ../data/perroquet.ui.h:10 msgid "Display translation" msgstr "Tradução de tela" #: ../data/perroquet.ui.h:11 msgid "E_dit" msgstr "E_ditar" #: ../data/perroquet.ui.h:12 msgid "Erase" msgstr "Apagar" #: ../data/perroquet.ui.h:13 msgid "Exercise language" msgstr "Exercitar linguagem" #: ../data/perroquet.ui.h:14 ../data/properties.ui.h:1 #: ../data/properties_advanced.ui.h:5 msgid "Exercise properties" msgstr "Propriedades do exercício" #: ../data/perroquet.ui.h:15 msgid "Exercise subtitle: " msgstr "Legenda do exercício: " #: ../data/perroquet.ui.h:16 msgid "Exercise video or audio: " msgstr "Vídeo ou áudio do exercício: " #: ../data/perroquet.ui.h:17 msgid "Export" msgstr "Exportar" #: ../data/perroquet.ui.h:18 msgid "Export as package..." msgstr "Exportar como pacote..." #: ../data/perroquet.ui.h:19 msgid "Export as template..." msgstr "Exportar como template" #: ../data/perroquet.ui.h:20 msgid "H_elp" msgstr "_Ajuda" #: ../data/perroquet.ui.h:21 msgid "Hint" msgstr "Dica" #: ../data/perroquet.ui.h:22 msgid "Import a package" msgstr "Importar como pacote" #: ../data/perroquet.ui.h:23 ../data/properties.ui.h:3 #: ../data/properties_advanced.ui.h:7 ../data/settings.ui.h:7 msgid "Language:" msgstr "Idioma:" #: ../data/perroquet.ui.h:24 msgid "Last open files" msgstr "últimos arquivos abertos" #: ../data/perroquet.ui.h:25 msgid "Lateral panel" msgstr "Painel lateral" #: ../data/perroquet.ui.h:26 msgid "List of all words to find in the exercise" msgstr "Lista de todas as palavras a serem encontradas no exercício" #: ../data/perroquet.ui.h:27 msgid "New exercise" msgstr "Novo exercício" #: ../data/perroquet.ui.h:28 msgid "Next Sequence" msgstr "Próxima Sequência" #: ../data/perroquet.ui.h:29 msgid "Next sequence" msgstr "Próxima sequência" #: ../data/perroquet.ui.h:30 msgid "Pause" msgstr "Pausar" #: ../data/perroquet.ui.h:32 msgid "Play" msgstr "Reproduzir" #: ../data/perroquet.ui.h:33 msgid "Play speed" msgstr "Velocidade de reprodução" #: ../data/perroquet.ui.h:34 msgid "Position in current sequence" msgstr "Posição na sequência atual" #: ../data/perroquet.ui.h:35 msgid "Position in exercise" msgstr "Posição no exercício" #: ../data/perroquet.ui.h:36 msgid "Previous sequence" msgstr "Sequência anterior" #: ../data/perroquet.ui.h:37 msgid "Progress statistics" msgstr "Estatísticas de progresso" #: ../data/perroquet.ui.h:38 msgid "Properties" msgstr "Propriedades" #: ../data/perroquet.ui.h:39 msgid "Replay sequence" msgstr "Sequência de reprodução" #: ../data/perroquet.ui.h:40 msgid "Reset exercise progress" msgstr "Limpar progresso do exercício" #: ../data/perroquet.ui.h:41 msgid "Reset the exercise progress" msgstr "Limpa o progresso do exercício" #: ../data/perroquet.ui.h:42 msgid "Reveal the sequence" msgstr "Revelar a sequência" #: ../data/perroquet.ui.h:43 msgid "Reveal the word" msgstr "Revelar a palavra" #: ../data/perroquet.ui.h:44 msgid "Save exercise" msgstr "Salvar exercício" #: ../data/perroquet.ui.h:45 ../data/properties.ui.h:6 #: ../data/properties_advanced.ui.h:21 msgid "Select a subtitle file" msgstr "Selecione um arquivo de legendas" #: ../data/perroquet.ui.h:46 ../data/properties.ui.h:7 #: ../data/properties_advanced.ui.h:22 msgid "Select a video file" msgstr "Selecione um arquivo de vídeo" #: ../data/perroquet.ui.h:47 msgid "Show/Hide correction" msgstr "Mostrar/Esconder correção" #: ../data/perroquet.ui.h:48 msgid "Show/Hide translation" msgstr "Mostrar/Esconder tradução" #: ../data/perroquet.ui.h:49 msgid "The audio track should be in the exercise language" msgstr "A trilha de áudio deve estar no idioma do exercício" #: ../data/perroquet.ui.h:50 msgid "" "These subtitles should be in the exercise language.\n" "Supported format: srt" msgstr "" "Estas legendas devem estar no idioma do exercício.\n" "Formato suportado; srt" #: ../data/perroquet.ui.h:52 msgid "" "These subtitles should be in your mother language.\n" "Supported format: srt" msgstr "" "Estas legendas devem estar no seu idioma natal.\n" "Formato suportado; srt" #: ../data/perroquet.ui.h:54 msgid "" "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 3 of the License, or (at your option) " "any later version.\n" "\n" "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.\n" "\n" "You should have received a copy of the GNU General Public License along with " "this program. If not, see ." msgstr "" "Este programa é software livre; Pode redistribuí-lo e/ou modificá-lo sob os " "termos da Licença Pública Geral GNU, conforme publicada pela Free Software " "Foundation; tanto a versão 3 da Licença como (a seu critério) qualquer " "versão mais actual.\n" "\n" "Este programa é distribuído na expectativa de ser útil, mas SEM QUALQUER " "GARANTIA; incluindo as garantias implícitas de COMERCIALIZAÇÃO ou de " "ADEQUAÇÃO A QUALQUER PROPÓSITO EM PARTICULAR. Consulte a Licença Pública " "Geral GNU para obter mais detalhes.\n" "\n" "Deverá ter recebido uma cópia da Licença Pública Geral GNU juntamente com " "este programa; caso contrário, visite ." #: ../data/perroquet.ui.h:59 ../perroquetlib/gui/gui_exercise_manager.py:291 msgid "Translation" msgstr "Tradução" #: ../data/perroquet.ui.h:60 msgid "Translation subtitles (optional): " msgstr "Legendas na tradução (opcional): " #: ../data/perroquet.ui.h:61 msgid "" "Word filter\n" " This filter use regexp. Examples:\n" " abc = all words containing 'abc'\n" " ^abc = all words starting with 'abc'\n" " abc$ = all words ending with 'abc'\n" "\n" " The character . can replace any character:\n" " ^abc.. = all words of at least 5 letters starting with 'abc'\n" " ^...$ = all 4-letter word\n" " ^ab.c$ = all 4-letter word beginning with 'ab' and ending with 'c'\n" "\n" " For more information on regular expressions: " "http://en.wikipedia.org/wiki/Regular_expression" msgstr "" "Filtro de palavras\n" " Este filtro utiliza regexp. Exemplos:\n" " abc = todas as palavras contendo 'abc'\n" " ^abc = todas as palavras começando com 'abc'\n" " abc$ = todas as palavras terminando com 'abc'\n" "\n" " O carectere . pode substituir qualquer outror:\n" " ^abc.. = todas as palavras com pelo menos 5 letras começando com 'abc'\n" " ^...$ = todas as palavras de 4 letras\n" " ^ab.c$ = todas as palavras de 4 letras começando com 'ab' e terminando " "com 'c'\n" "\n" " Para mais informações sobre expressões regulares, acesse " ":http://pt.wikipedia.org/wiki/Express%C3%A3o_regular" #: ../data/perroquet.ui.h:73 msgid "Words" msgstr "Palavras" #: ../data/perroquet.ui.h:74 msgid "_Advanced properties" msgstr "Propriedades _avançadas" #: ../data/perroquet.ui.h:75 msgid "_Exercise" msgstr "_Exercício" #: ../data/perroquet.ui.h:76 msgid "_Exercise manager" msgstr "Gerenciador de _Exercícios" #: ../data/perroquet.ui.h:77 msgid "_File" msgstr "_Arquivo" #: ../data/perroquet.ui.h:78 msgid "open exercise" msgstr "abrir exercício" #: ../data/perroquet.ui.h:79 msgid "open saved exercise" msgstr "abrir exercício salvo" #: ../data/properties.ui.h:2 ../data/properties_advanced.ui.h:6 msgid "Exercise: " msgstr "Exercício: " #: ../data/properties.ui.h:4 ../data/properties_advanced.ui.h:12 msgid "Media: " msgstr "Mídia: " #: ../data/properties.ui.h:5 ../data/settings.ui.h:13 msgid "Repeat sequences after completed it" msgstr "Repetir sequências após terminá-las" #: ../data/properties.ui.h:8 ../data/properties_advanced.ui.h:25 msgid "Translation: " msgstr "Tradução: " #: ../data/properties.ui.h:9 ../data/properties_advanced.ui.h:26 msgid "Use dynamic correction" msgstr "Usar correção dinâmica" #: ../data/properties.ui.h:10 msgid "" "With dynamic correction, the correct words will be displayed as valid as " "soon as they are correctly typed." msgstr "" "Com a correção dinâmica, as palavras corretas serão exibidas como vélidas ao " "serem escritas corretamente." #: ../data/properties_advanced.ui.h:1 msgid "Correction password : " msgstr "Senha de correção: " #: ../data/properties_advanced.ui.h:2 ../data/settings.ui.h:3 msgid "Disable help tools" msgstr "Desabilitar ferramentas de ajuda" #: ../data/properties_advanced.ui.h:3 msgid "Exercise" msgstr "Exercício" #: ../data/properties_advanced.ui.h:4 msgid "Exercise name:" msgstr "Nome do exercício" #: ../data/properties_advanced.ui.h:8 msgid "Lock exercise's correction" msgstr "Trancar correção do exercício" #: ../data/properties_advanced.ui.h:9 msgid "Lock exercise's propreties" msgstr "Trancar propriedades do exercício" #: ../data/properties_advanced.ui.h:10 msgid "Locks" msgstr "Travas" #: ../data/properties_advanced.ui.h:11 ../data/settings.ui.h:9 msgid "Maximum sequence time (s): " msgstr "Tempo máximo de sequências (s) " #: ../data/properties_advanced.ui.h:13 msgid "Paths" msgstr "Caminhos" #: ../data/properties_advanced.ui.h:14 msgid "Paths list" msgstr "Lista de caminhos" #: ../data/properties_advanced.ui.h:15 ../data/settings.ui.h:10 msgid "Play sequence in a random order" msgstr "Reproduzir sequência em ordem aleatória" #: ../data/properties_advanced.ui.h:16 ../data/settings.ui.h:11 msgid "Playing time after sequences (ms):" msgstr "Tempo de execução após sequências (ms):" #: ../data/properties_advanced.ui.h:17 ../data/settings.ui.h:12 msgid "Playing time before sequences (ms):" msgstr "Tempo de execução antes das sequências (ms):" #: ../data/properties_advanced.ui.h:18 msgid "Properties password : " msgstr "Senha das propriedades : " #: ../data/properties_advanced.ui.h:19 msgid "Repeat count limit by sequence (0 for no limit):" msgstr "Repetir limite de contagem por sequência (0 para limite nenhum)" #: ../data/properties_advanced.ui.h:20 msgid "Repeat sequences after having completed it" msgstr "Repetir sequências após completá-las" #: ../data/properties_advanced.ui.h:23 msgid "Sequences" msgstr "Sequências" #: ../data/properties_advanced.ui.h:24 ../data/settings.ui.h:19 msgid "Time between sequences (s):" msgstr "Tempo entre sequências (s):" #: ../data/exercise_manager.ui.h:1 msgid "Details" msgstr "Detalhes" #: ../data/exercise_manager.ui.h:2 msgid "Exercises" msgstr "Exercícios" #: ../data/exercise_manager.ui.h:3 msgid "Perroquet exercises manager" msgstr "Gerenciador de exercícios do perroquet" #: ../data/exercise_manager.ui.h:4 msgid "Repositories" msgstr "Repositórios" #: ../data/exercise_manager.ui.h:5 ../data/settings.ui.h:14 msgid "Settings" msgstr "Configurações" #: ../data/exercise_manager.ui.h:6 msgid "Tree view mode" msgstr "Modo de visão em árvore" #: ../data/exercise_manager.ui.h:7 #: ../perroquetlib/gui/gui_exercise_manager.py:364 msgid "Use" msgstr "Utilizar" #: ../data/gui_message_dialog.ui.h:1 msgid "label" msgstr "rótulo" #: ../data/gui_password_dialog.ui.h:1 #: ../perroquetlib/gui/gui_password_dialog.py:46 msgid "Correction password :" msgstr "Senha de correção:" #: ../data/gui_password_dialog.ui.h:2 msgid "Password for unlock correction" msgstr "Senha para desbloquear a correção" #: ../data/reset.ui.h:1 msgid "Cancel" msgstr "Cancelar" #: ../data/reset.ui.h:2 msgid "Do you want to reset the current exercise ?" msgstr "Você deseja restaurar o exercício atual?" #: ../data/reset.ui.h:3 msgid "Ok" msgstr "Ok" #: ../data/settings.ui.h:1 msgid "Advanced" msgstr "Avançado" #: ../data/settings.ui.h:2 msgid "Default exercise properties" msgstr "Propriedades padrões do exercício" #: ../data/settings.ui.h:4 msgid "Exercise type:" msgstr "Tipo do exercício:" #: ../data/settings.ui.h:5 msgid "General" msgstr "Geral" #: ../data/settings.ui.h:6 msgid "Interface" msgstr "Interface" #: ../data/settings.ui.h:8 msgid "Limit repeat count by sequence (0 for no limit):" msgstr "" "Limite de repatição de contagem por sequência (0 para nenhum limite):" #: ../data/settings.ui.h:15 msgid "Show menu bar" msgstr "Exibir barra de menu" #: ../data/settings.ui.h:16 msgid "Show play and pause buttons" msgstr "Exibir botões de reprodução e pausa" #: ../data/settings.ui.h:17 msgid "Show settings" msgstr "Exibir configurações" #: ../data/settings.ui.h:18 msgid "Skip valid sequences using next and previous sequence buttons" msgstr "" "Pular sequências válidas utilizando botões de anterior e próxima sequência" #: ../data/settings.ui.h:20 msgid "Use speed changer" msgstr "Utilizar alterador de velocidade" #: ../perroquetlib/core.py:504 msgid "Untitled exercise" msgstr "Exercício sem título" #: ../perroquetlib/core.py:556 msgid "" "Import finish succesfully. Use the exercises manager to use the newly " "installed exercise." msgstr "" "Importação concluída com sucesso. Utilize o gerenciador de exercícios para " "utilizar o novo exercício instalado." #: ../perroquetlib/core.py:558 msgid "Import failed." msgstr "A importação falhou." #: ../perroquetlib/repository/exercise_repository_exercise.py:51 #: ../perroquetlib/repository/exercise_repository_exercise.py:52 #: ../perroquetlib/repository/exercise_repository_exercise.py:53 #: ../perroquetlib/repository/exercise_repository_exercise.py:54 #: ../perroquetlib/repository/exercise_repository_exercise.py:55 #: ../perroquetlib/repository/exercise_repository_exercise.py:56 #: ../perroquetlib/repository/exercise_repository_exercise.py:57 #: ../perroquetlib/repository/exercise_repository_exercise.py:58 #: ../perroquetlib/repository/exercise_repository_exercise.py:59 #: ../perroquetlib/repository/exercise_repository_exercise.py:60 msgid "Not specified" msgstr "Não especificado" #: ../perroquetlib/repository/exercise_repository_exercise.py:451 msgid "Imported exercise" msgstr "Exercício importado" #: ../perroquetlib/repository/exercise_repository_manager.py:345 msgid "File not found: " msgstr "Arquivo não encontrado: " #: ../perroquetlib/repository/exercise_repository_manager.py:359 msgid "Invalid package, missing template.perroquet." msgstr "pacote inválido, faltando template.perroquet." #: ../perroquetlib/repository/exercise_repository_manager.py:385 msgid "Exercise already exist." msgstr "O exercício já existe." #: ../perroquetlib/gui/gui.py:85 #, python-format msgid "The file '%s' doesn't exist. Please modify exercise paths" msgstr "" "o arquivo '%s' não existe. Por favor modifique o caminho dos exercícios" #: ../perroquetlib/gui/gui.py:86 msgid "load error" msgstr "Erro ao carregar" #: ../perroquetlib/gui/gui.py:283 #: ../perroquetlib/gui/gui_exercise_manager.py:236 #: ../perroquetlib/gui/gui_sequence_properties_advanced.py:72 msgid "Path" msgstr "Caminho" #: ../perroquetlib/gui/gui.py:305 msgid "Do you really quit without save ?" msgstr "Deseja sair sem salvar?" #: ../perroquetlib/gui/gui.py:306 msgid "Confirm quit" msgstr "confirmar saída" #: ../perroquetlib/gui/gui.py:320 msgid "Information" msgstr "Informação" #: ../perroquetlib/gui/gui.py:809 msgid "Select File to open" msgstr "Selecione o arquivo a ser aberto" #: ../perroquetlib/gui/gui.py:814 ../perroquetlib/gui/gui.py:852 msgid "Perroquet files" msgstr "Arquivos do perroquet" #: ../perroquetlib/gui/gui.py:819 ../perroquetlib/gui/gui.py:838 #: ../perroquetlib/gui/gui.py:857 ../perroquetlib/gui/gui.py:878 #: ../perroquetlib/gui/gui.py:899 msgid "All files" msgstr "Todos os arquivos" #: ../perroquetlib/gui/gui.py:828 msgid "Select package to import" msgstr "Selecione o pacote a ser importado" #: ../perroquetlib/gui/gui.py:833 ../perroquetlib/gui/gui.py:894 msgid "Perroquet package files" msgstr "Arquivos do pacote do perroquet" #: ../perroquetlib/gui/gui.py:847 msgid "Select File to Save to" msgstr "Selecione o arquivo a ser salvo" #: ../perroquetlib/gui/gui.py:868 ../perroquetlib/gui/gui.py:889 msgid "Select File to Export to" msgstr "Selecione o arquivo a ser exportado" #: ../perroquetlib/gui/gui.py:873 msgid "Perroquet template files" msgstr "Arqiovos de template do perroquet" #: ../perroquetlib/gui/gui_controller.py:237 #, python-format msgid "- Sequences: %(found)s/%(count)s (%(percent)s %%)\n" msgstr "- Sequências: %(found)s/%(count)s (%(percent)s %%)\n" #: ../perroquetlib/gui/gui_controller.py:238 #, python-format msgid "- Words: %(found)s/%(count)s (%(percent)s %%)\n" msgstr "- Palavras: %(found)s/%(count)s (%(percent)s %%)\n" #: ../perroquetlib/gui/gui_controller.py:239 #, python-format msgid "- Repeat ratio: %s per words" msgstr "- Taxa de repetição: %s por palavras" #. Wrong password #: ../perroquetlib/gui/gui_controller.py:287 #: ../perroquetlib/gui/gui_controller.py:518 msgid "Wrong password" msgstr "Senha incorreta" #: ../perroquetlib/gui/gui_exercise_manager.py:82 msgid "Updating repositories..." msgstr "Atualizando repositórios..." #: ../perroquetlib/gui/gui_exercise_manager.py:108 msgid "Local repository" msgstr "Repositório local" #: ../perroquetlib/gui/gui_exercise_manager.py:110 msgid "Distant repository" msgstr "Repositório distante" #: ../perroquetlib/gui/gui_exercise_manager.py:112 msgid "Offline repository" msgstr "Repositório offline" #: ../perroquetlib/gui/gui_exercise_manager.py:114 msgid "Orphan repository" msgstr "Repositório órfão" #: ../perroquetlib/gui/gui_exercise_manager.py:132 msgid "Group" msgstr "Grupo" #: ../perroquetlib/gui/gui_exercise_manager.py:148 #: ../perroquetlib/gui/gui_exercise_manager.py:266 #: ../perroquetlib/gui/gui_exercise_manager.py:446 msgid "Installed" msgstr "Instalado" #: ../perroquetlib/gui/gui_exercise_manager.py:151 #: ../perroquetlib/gui/gui_exercise_manager.py:268 #: ../perroquetlib/gui/gui_exercise_manager.py:439 msgid "Available" msgstr "Disponível" #: ../perroquetlib/gui/gui_exercise_manager.py:154 #: ../perroquetlib/gui/gui_exercise_manager.py:270 #: ../perroquetlib/gui/gui_exercise_manager.py:454 msgid "Used" msgstr "Utilizado" #: ../perroquetlib/gui/gui_exercise_manager.py:157 #: ../perroquetlib/gui/gui_exercise_manager.py:272 #: ../perroquetlib/gui/gui_exercise_manager.py:456 msgid "Done" msgstr "Concluído" #: ../perroquetlib/gui/gui_exercise_manager.py:168 msgid "Exercise" msgstr "Exercício" #: ../perroquetlib/gui/gui_exercise_manager.py:174 #: ../perroquetlib/gui/gui_exercise_manager.py:274 #: ../perroquetlib/gui/gui_exercise_manager.py:307 msgid "Name" msgstr "Nome" #: ../perroquetlib/gui/gui_exercise_manager.py:179 msgid "Type" msgstr "Tipo" #: ../perroquetlib/gui/gui_exercise_manager.py:184 #: ../perroquetlib/gui/gui_exercise_manager.py:276 msgid "Description" msgstr "Descrição" #: ../perroquetlib/gui/gui_exercise_manager.py:189 msgid "Status" msgstr "Estado" #: ../perroquetlib/gui/gui_exercise_manager.py:194 #: ../perroquetlib/gui/gui_exercise_manager.py:283 msgid "Words count" msgstr "Contador de palavras" #: ../perroquetlib/gui/gui_exercise_manager.py:275 msgid "Licence" msgstr "Licença" #: ../perroquetlib/gui/gui_exercise_manager.py:277 msgid "Author" msgstr "Autor" #: ../perroquetlib/gui/gui_exercise_manager.py:278 msgid "Author website" msgstr "Síte do autor" #: ../perroquetlib/gui/gui_exercise_manager.py:279 msgid "Author contact" msgstr "Contato do autor" #: ../perroquetlib/gui/gui_exercise_manager.py:282 msgid "Version" msgstr "Versão" #: ../perroquetlib/gui/gui_exercise_manager.py:284 msgid "Language" msgstr "Idioma" #: ../perroquetlib/gui/gui_exercise_manager.py:285 msgid "Media type" msgstr "Tipo da mídia" #: ../perroquetlib/gui/gui_exercise_manager.py:286 msgid "Id" msgstr "Id" #: ../perroquetlib/gui/gui_exercise_manager.py:287 msgid "Install status" msgstr "Estado da instalação" #: ../perroquetlib/gui/gui_exercise_manager.py:295 msgid "Packager" msgstr "Empacotador" #: ../perroquetlib/gui/gui_exercise_manager.py:296 msgid "Packager website" msgstr "Site do Empacotador" #: ../perroquetlib/gui/gui_exercise_manager.py:297 msgid "Packager contact" msgstr "Contado do Empacotador" #: ../perroquetlib/gui/gui_exercise_manager.py:299 msgid "Exercise path" msgstr "Caminho do exercício" #: ../perroquetlib/gui/gui_exercise_manager.py:300 msgid "Package path" msgstr "Caminho do pacote" #: ../perroquetlib/gui/gui_exercise_manager.py:301 msgid "Template path" msgstr "Caminho do template" #: ../perroquetlib/gui/gui_exercise_manager.py:302 msgid "Instance path" msgstr "Caminho da instância" #: ../perroquetlib/gui/gui_exercise_manager.py:303 msgid "Finished exercise path" msgstr "Caminho do exercício concluído" #: ../perroquetlib/gui/gui_exercise_manager.py:312 msgid "Value" msgstr "Valor" #: ../perroquetlib/gui/gui_exercise_manager.py:352 msgid "Install" msgstr "Instalar" #: ../perroquetlib/gui/gui_exercise_manager.py:356 #: ../perroquetlib/gui/gui_exercise_manager.py:360 msgid "Cancel install" msgstr "Cancelar instalação" #: ../perroquetlib/gui/gui_exercise_manager.py:368 #: ../perroquetlib/gui/gui_exercise_manager.py:376 msgid "Remove" msgstr "Remover" #: ../perroquetlib/gui/gui_exercise_manager.py:372 msgid "Continue" msgstr "Continuar" #: ../perroquetlib/gui/gui_exercise_manager.py:416 #, python-format msgid "%d available exercises" msgstr "%d exercícios disponíveis" #: ../perroquetlib/gui/gui_exercise_manager.py:418 #, python-format msgid "%d available exercise" msgstr "%d exercícios disponíveis" #: ../perroquetlib/gui/gui_exercise_manager.py:422 #, python-format msgid " and %d installed exercises" msgstr " e %d exercícios instalados" #: ../perroquetlib/gui/gui_exercise_manager.py:424 #, python-format msgid " and %d installed exercise" msgstr " e %d exercício instalado" #: ../perroquetlib/gui/gui_exercise_manager.py:441 msgid "Downloading" msgstr "Recebendo" #: ../perroquetlib/gui/gui_exercise_manager.py:444 msgid "Installing" msgstr "Instalando" #: ../perroquetlib/gui/gui_exercise_manager.py:448 msgid "Corrupted" msgstr "Danificado" #: ../perroquetlib/gui/gui_exercise_manager.py:450 msgid "Canceled" msgstr "Cancelado" #: ../perroquetlib/gui/gui_exercise_manager.py:452 msgid "Removing" msgstr "Removendo" #: ../perroquetlib/gui/gui_exercise_manager.py:464 #, python-format msgid "Downloading ... %d%%" msgstr "Recebendo ... %d%%" #: ../perroquetlib/gui/gui_password_dialog.py:49 msgid "Properties password :" msgstr "Senha das propriedades :" #: ../perroquetlib/video_player.py:49 msgid "" "You need to install the gstreamer soundtouch elements to use slowly play " "feature." msgstr "" "Você precisa instalar os elementos soundtouch do gstreamer para utilizar o " "recurso de câmera lenta." #~ msgid "Select File to Open" #~ msgstr "Selecione o arquivo para abrir" #~ msgid "Load error" #~ msgstr "Erro de carregamento" #, python-format #~ msgid "The file '%s' doesn't exist. Please modify exercice paths" #~ msgstr "" #~ "O arquivo '%s' não existe. Por favor, modifique a localização dos exercícios." #~ msgid "A oral comprehension teacher" #~ msgstr "Um professor de compreensão oral" #~ msgid "Exercice properties" #~ msgstr "Propriedades do exercício" #~ msgid "Exercice video or audio: " #~ msgstr "Áudio ou vídeo do exercício: " #~ msgid "Oral comprehention teacher" #~ msgstr "Professor de compreensão oral" #~ msgid "Exercice subtitle: " #~ msgstr "Legenda do exercício: " #~ msgid "" #~ "- Sequences: --/--\n" #~ "- Words: --/--\n" #~ "- Repeat ratio: -- per words" #~ msgstr "" #~ "- Sequências: --/--\n" #~ "- Palavras: --/--\n" #~ "- Taxa de repetição: -- por palavra" #~ msgid "New exercice" #~ msgstr "Novo exercício" #~ msgid "Save exercice" #~ msgstr "Salvar exercício" #~ msgid "Open exercise" #~ msgstr "Abrir exercício" #~ msgid "Exercice: " #~ msgstr "Exercício: " #~ msgid "" #~ "These subtitles should be in the exercice language.\n" #~ "Supported format: srt" #~ msgstr "" #~ "Essa legenda deveria estar no idioma do exercício.\n" #~ "Formatos suportados: srt" #~ msgid "The audio track should be in the exercice language" #~ msgstr "O áudio deveria estar no idioma do exercício" #~ msgid "Open saved exercice" #~ msgstr "Abrir exercício salvo" #~ msgid "Copyright © 2009-2010 Frédéric Bertolus" #~ msgstr "Copyright © 2009-2010 Frédéric Bertolus" #~ msgid "" #~ "Word filter\n" #~ "This filter use regexp. Examples:\n" #~ "abc = all words containing 'abc'\n" #~ "^abc = all words starting with 'abc'\n" #~ "abc$ = all words ending with 'abc'\n" #~ "\n" #~ "The character . can replace any character:\n" #~ "^abc.. = all words of at least 5 letters starting with 'abc'\n" #~ "^...$ = all 4-letter word\n" #~ "^ab.c$ = all 4-letter word beginning with 'ab' and ending with 'c'\n" #~ "\n" #~ "For more information on regular expressions: " #~ "http://en.wikipedia.org/wiki/Regular_expression" #~ msgstr "" #~ "Filtro de palavras\n" #~ "Esse filtro usa o regexp. Exemplos:\n" #~ "abc = todas as palavras contendo 'abc'\n" #~ "^abc = todas as palavras começando com 'abc'\n" #~ "abc$ = todas as palavras terminando com 'abc'\n" #~ "\n" #~ "The character . can replace any character:\n" #~ "^abc.. = all words of at least 5 letters starting with 'abc'\n" #~ "^...$ = all 4-letter word\n" #~ "^ab.c$ = all 4-letter word beginning with 'ab' and ending with 'c'\n" #~ "\n" #~ "For more information on regular expressions: " #~ "http://en.wikipedia.org/wiki/Regular_expression" perroquet-1.1.1.orig/po/it.po0000644000175000017500000006077111561344350016304 0ustar georgeskgeorgesk# Italian translation for perroquet # Copyright (c) 2010 Rosetta Contributors and Canonical Ltd 2010 # This file is distributed under the same license as the perroquet package. # FIRST AUTHOR , 2010. # msgid "" msgstr "" "Project-Id-Version: perroquet\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2010-06-19 10:43+0200\n" "PO-Revision-Date: 2010-11-20 07:31+0000\n" "Last-Translator: Fred Bertolus \n" "Language-Team: Italian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2011-01-17 05:02+0000\n" "X-Generator: Launchpad (build 12177)\n" #: ../data/perroquet.desktop.in.h:1 msgid "Oral comprehension teacher" msgstr "Insegnante di comprensione orale" #: ../data/perroquet.desktop.in.h:2 ../data/perroquet.ui.h:31 #: ../perroquetlib/gui/gui_controller.py:201 msgid "Perroquet" msgstr "Perroquet" #: ../data/perroquet.ui.h:1 msgid "" "- Sequences: --/--\n" " - Words: --/--\n" " - Repeat ratio: -- per words" msgstr "" #: ../data/perroquet.ui.h:4 msgid "-- s" msgstr "-- s" #: ../data/perroquet.ui.h:5 msgid "--/--" msgstr "--/--" #: ../data/perroquet.ui.h:6 msgid "An oral comprehension teacher" msgstr "Un insegnante di comprensione orale" #: ../data/perroquet.ui.h:7 msgid "Copyright © 2009-2010 Perroquet Team" msgstr "Copyright © 2009-2010 Perroquet Team" #: ../data/perroquet.ui.h:8 msgid "Correction" msgstr "Correzione" #: ../data/perroquet.ui.h:9 msgid "Display correction" msgstr "Visualizza correzione" #: ../data/perroquet.ui.h:10 msgid "Display translation" msgstr "Mostra traduzione" #: ../data/perroquet.ui.h:11 msgid "E_dit" msgstr "_Modifica" #: ../data/perroquet.ui.h:12 msgid "Erase" msgstr "Elimina" #: ../data/perroquet.ui.h:13 msgid "Exercise language" msgstr "Lingua dell'esercizio" #: ../data/perroquet.ui.h:14 ../data/properties.ui.h:1 #: ../data/properties_advanced.ui.h:5 msgid "Exercise properties" msgstr "Proprietà esercizio" #: ../data/perroquet.ui.h:15 msgid "Exercise subtitle: " msgstr "Sottotitolo esercizio: " #: ../data/perroquet.ui.h:16 msgid "Exercise video or audio: " msgstr "Esercizio video o audio: " #: ../data/perroquet.ui.h:17 msgid "Export" msgstr "Esporta" #: ../data/perroquet.ui.h:18 msgid "Export as package..." msgstr "Esporta come pacchetto..." #: ../data/perroquet.ui.h:19 msgid "Export as template..." msgstr "" #: ../data/perroquet.ui.h:20 msgid "H_elp" msgstr "A_iuto" #: ../data/perroquet.ui.h:21 msgid "Hint" msgstr "Suggerimento" #: ../data/perroquet.ui.h:22 msgid "Import a package" msgstr "Importa un pacchetto" #: ../data/perroquet.ui.h:23 ../data/properties.ui.h:3 #: ../data/properties_advanced.ui.h:7 ../data/settings.ui.h:7 msgid "Language:" msgstr "Lingua:" #: ../data/perroquet.ui.h:24 msgid "Last open files" msgstr "Ultimi files aperti" #: ../data/perroquet.ui.h:25 msgid "Lateral panel" msgstr "Pannello laterale" #: ../data/perroquet.ui.h:26 msgid "List of all words to find in the exercise" msgstr "Elenco di tutte le parole da trovare nell'esercizio" #: ../data/perroquet.ui.h:27 msgid "New exercise" msgstr "Nuovo Esercizio" #: ../data/perroquet.ui.h:28 msgid "Next Sequence" msgstr "Prossima sequenza" #: ../data/perroquet.ui.h:29 msgid "Next sequence" msgstr "Prossima sequenza" #: ../data/perroquet.ui.h:30 msgid "Pause" msgstr "Pausa" #: ../data/perroquet.ui.h:32 msgid "Play" msgstr "Riproduci" #: ../data/perroquet.ui.h:33 msgid "Play speed" msgstr "Velocità di riproduzione" #: ../data/perroquet.ui.h:34 msgid "Position in current sequence" msgstr "Posizione nella sequenza attuale" #: ../data/perroquet.ui.h:35 msgid "Position in exercise" msgstr "Posizione nell'esercizio" #: ../data/perroquet.ui.h:36 msgid "Previous sequence" msgstr "Sequenza Precedente" #: ../data/perroquet.ui.h:37 msgid "Progress statistics" msgstr "Statistiche dei progressi" #: ../data/perroquet.ui.h:38 msgid "Properties" msgstr "Proprietà" #: ../data/perroquet.ui.h:39 msgid "Replay sequence" msgstr "Ripetere Sequenza" #: ../data/perroquet.ui.h:40 msgid "Reset exercise progress" msgstr "" #: ../data/perroquet.ui.h:41 msgid "Reset the exercise progress" msgstr "" #: ../data/perroquet.ui.h:42 msgid "Reveal the sequence" msgstr "" #: ../data/perroquet.ui.h:43 msgid "Reveal the word" msgstr "Rivela la parola" #: ../data/perroquet.ui.h:44 msgid "Save exercise" msgstr "Salva l'esercizio" #: ../data/perroquet.ui.h:45 ../data/properties.ui.h:6 #: ../data/properties_advanced.ui.h:21 msgid "Select a subtitle file" msgstr "Seleziona un File dei Sottotitoli" #: ../data/perroquet.ui.h:46 ../data/properties.ui.h:7 #: ../data/properties_advanced.ui.h:22 msgid "Select a video file" msgstr "Seleziona un file video" #: ../data/perroquet.ui.h:47 msgid "Show/Hide correction" msgstr "Mostra/Nascondi correzioni" #: ../data/perroquet.ui.h:48 msgid "Show/Hide translation" msgstr "Mostra/Nascondi traduzione" #: ../data/perroquet.ui.h:49 msgid "The audio track should be in the exercise language" msgstr "" #: ../data/perroquet.ui.h:50 msgid "" "These subtitles should be in the exercise language.\n" "Supported format: srt" msgstr "" #: ../data/perroquet.ui.h:52 msgid "" "These subtitles should be in your mother language.\n" "Supported format: srt" msgstr "" "Questi sottotitoli dovrebbero essere nella tua lingua madre.\n" "Formato supportato: srt" #: ../data/perroquet.ui.h:54 msgid "" "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 3 of the License, or (at your option) " "any later version.\n" "\n" "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.\n" "\n" "You should have received a copy of the GNU General Public License along with " "this program. If not, see ." msgstr "" "Questo programma è software libero; è possibile ridistribuirlo o modificarlo " "secondo i termini previsti dalla licenza GNU General Public License così " "come pubblicata dalla Free Software Foundation, sia la versione 3 della " "Licenza, sia (a vostra scelta) una qualsiasi versione più recente.\n" "\n" "Questo programma è distribuito nella speranza che possa risultare utile, ma " "SENZA ALCUNA GARANZIA, nemmeno la garanzia implicita di COMMERCIABILITÀ o " "APPLICABILITÀ PER UNO SCOPO PARTICOLARE. Per maggiori dettagli consultare la " "GNU General Public License.\n" "\n" "Una copia della GNU General Public License dovrebbe essere stata fornita con " "questo programma. In caso contrario, consultare " "." #: ../data/perroquet.ui.h:59 ../perroquetlib/gui/gui_exercise_manager.py:291 msgid "Translation" msgstr "Traduzione" #: ../data/perroquet.ui.h:60 msgid "Translation subtitles (optional): " msgstr "Sottotitoli di traduzione (opzionale): " #: ../data/perroquet.ui.h:61 msgid "" "Word filter\n" " This filter use regexp. Examples:\n" " abc = all words containing 'abc'\n" " ^abc = all words starting with 'abc'\n" " abc$ = all words ending with 'abc'\n" "\n" " The character . can replace any character:\n" " ^abc.. = all words of at least 5 letters starting with 'abc'\n" " ^...$ = all 4-letter word\n" " ^ab.c$ = all 4-letter word beginning with 'ab' and ending with 'c'\n" "\n" " For more information on regular expressions: " "http://en.wikipedia.org/wiki/Regular_expression" msgstr "" #: ../data/perroquet.ui.h:73 msgid "Words" msgstr "Parole" #: ../data/perroquet.ui.h:74 msgid "_Advanced properties" msgstr "_Proprietà avanzate" #: ../data/perroquet.ui.h:75 msgid "_Exercise" msgstr "_Esercizio" #: ../data/perroquet.ui.h:76 msgid "_Exercise manager" msgstr "_Gestore esercizio" #: ../data/perroquet.ui.h:77 msgid "_File" msgstr "_File" #: ../data/perroquet.ui.h:78 msgid "open exercise" msgstr "apri esercizio" #: ../data/perroquet.ui.h:79 msgid "open saved exercise" msgstr "apri esercizio salvato" #: ../data/properties.ui.h:2 ../data/properties_advanced.ui.h:6 msgid "Exercise: " msgstr "Esercizio: " #: ../data/properties.ui.h:4 ../data/properties_advanced.ui.h:12 msgid "Media: " msgstr "Media: " #: ../data/properties.ui.h:5 ../data/settings.ui.h:13 msgid "Repeat sequences after completed it" msgstr "Ripeti le sequenze dopo averle completate" #: ../data/properties.ui.h:8 ../data/properties_advanced.ui.h:25 msgid "Translation: " msgstr "Traduzione: " #: ../data/properties.ui.h:9 ../data/properties_advanced.ui.h:26 msgid "Use dynamic correction" msgstr "Usa correzione dinamica" #: ../data/properties.ui.h:10 msgid "" "With dynamic correction, the correct words will be displayed as valid as " "soon as they are correctly typed." msgstr "" #: ../data/properties_advanced.ui.h:1 msgid "Correction password : " msgstr "Correzione password : " #: ../data/properties_advanced.ui.h:2 ../data/settings.ui.h:3 msgid "Disable help tools" msgstr "Disabilita gli strumenti di aiuto" #: ../data/properties_advanced.ui.h:3 msgid "Exercise" msgstr "Esercizio" #: ../data/properties_advanced.ui.h:4 msgid "Exercise name:" msgstr "Nome dell'esercizio:" #: ../data/properties_advanced.ui.h:8 msgid "Lock exercise's correction" msgstr "Blocca la correzione dell'esercizio" #: ../data/properties_advanced.ui.h:9 msgid "Lock exercise's propreties" msgstr "Blocca le proprietà dell'esercizio" #: ../data/properties_advanced.ui.h:10 msgid "Locks" msgstr "Blocchi" #: ../data/properties_advanced.ui.h:11 ../data/settings.ui.h:9 msgid "Maximum sequence time (s): " msgstr "" #: ../data/properties_advanced.ui.h:13 msgid "Paths" msgstr "Percorsi" #: ../data/properties_advanced.ui.h:14 msgid "Paths list" msgstr "Lista percorsi" #: ../data/properties_advanced.ui.h:15 ../data/settings.ui.h:10 msgid "Play sequence in a random order" msgstr "Riproduci la sequenza in ordine casuale" #: ../data/properties_advanced.ui.h:16 ../data/settings.ui.h:11 msgid "Playing time after sequences (ms):" msgstr "" #: ../data/properties_advanced.ui.h:17 ../data/settings.ui.h:12 msgid "Playing time before sequences (ms):" msgstr "" #: ../data/properties_advanced.ui.h:18 msgid "Properties password : " msgstr "Proprietà password : " #: ../data/properties_advanced.ui.h:19 msgid "Repeat count limit by sequence (0 for no limit):" msgstr "" #: ../data/properties_advanced.ui.h:20 msgid "Repeat sequences after having completed it" msgstr "" #: ../data/properties_advanced.ui.h:23 msgid "Sequences" msgstr "Sequenze" #: ../data/properties_advanced.ui.h:24 ../data/settings.ui.h:19 msgid "Time between sequences (s):" msgstr "" #: ../data/exercise_manager.ui.h:1 msgid "Details" msgstr "Dettagli" #: ../data/exercise_manager.ui.h:2 msgid "Exercises" msgstr "Esercizi" #: ../data/exercise_manager.ui.h:3 msgid "Perroquet exercises manager" msgstr "" #: ../data/exercise_manager.ui.h:4 msgid "Repositories" msgstr "" #: ../data/exercise_manager.ui.h:5 ../data/settings.ui.h:14 msgid "Settings" msgstr "Impostazioni" #: ../data/exercise_manager.ui.h:6 msgid "Tree view mode" msgstr "" #: ../data/exercise_manager.ui.h:7 #: ../perroquetlib/gui/gui_exercise_manager.py:364 msgid "Use" msgstr "Usa" #: ../data/gui_message_dialog.ui.h:1 msgid "label" msgstr "etichetta" #: ../data/gui_password_dialog.ui.h:1 #: ../perroquetlib/gui/gui_password_dialog.py:46 msgid "Correction password :" msgstr "Correzione password :" #: ../data/gui_password_dialog.ui.h:2 msgid "Password for unlock correction" msgstr "" #: ../data/reset.ui.h:1 msgid "Cancel" msgstr "Cancella" #: ../data/reset.ui.h:2 msgid "Do you want to reset the current exercise ?" msgstr "" #: ../data/reset.ui.h:3 msgid "Ok" msgstr "Ok" #: ../data/settings.ui.h:1 msgid "Advanced" msgstr "Avanzate" #: ../data/settings.ui.h:2 msgid "Default exercise properties" msgstr "" #: ../data/settings.ui.h:4 msgid "Exercise type:" msgstr "Tipo di esercizio:" #: ../data/settings.ui.h:5 msgid "General" msgstr "Generale" #: ../data/settings.ui.h:6 msgid "Interface" msgstr "Interfaccia" #: ../data/settings.ui.h:8 msgid "Limit repeat count by sequence (0 for no limit):" msgstr "" #: ../data/settings.ui.h:15 msgid "Show menu bar" msgstr "" #: ../data/settings.ui.h:16 msgid "Show play and pause buttons" msgstr "" #: ../data/settings.ui.h:17 msgid "Show settings" msgstr "" #: ../data/settings.ui.h:18 msgid "Skip valid sequences using next and previous sequence buttons" msgstr "" #: ../data/settings.ui.h:20 msgid "Use speed changer" msgstr "" #: ../perroquetlib/core.py:504 msgid "Untitled exercise" msgstr "Esercizio senza titolo" #: ../perroquetlib/core.py:556 msgid "" "Import finish succesfully. Use the exercises manager to use the newly " "installed exercise." msgstr "" "Importazione avvenuta con successo. Usa il gestore degli esercizi per usare " "l'esercizio appena installato." #: ../perroquetlib/core.py:558 msgid "Import failed." msgstr "Importazione fallita." #: ../perroquetlib/repository/exercise_repository_exercise.py:51 #: ../perroquetlib/repository/exercise_repository_exercise.py:52 #: ../perroquetlib/repository/exercise_repository_exercise.py:53 #: ../perroquetlib/repository/exercise_repository_exercise.py:54 #: ../perroquetlib/repository/exercise_repository_exercise.py:55 #: ../perroquetlib/repository/exercise_repository_exercise.py:56 #: ../perroquetlib/repository/exercise_repository_exercise.py:57 #: ../perroquetlib/repository/exercise_repository_exercise.py:58 #: ../perroquetlib/repository/exercise_repository_exercise.py:59 #: ../perroquetlib/repository/exercise_repository_exercise.py:60 msgid "Not specified" msgstr "" #: ../perroquetlib/repository/exercise_repository_exercise.py:451 msgid "Imported exercise" msgstr "Esercizio importato" #: ../perroquetlib/repository/exercise_repository_manager.py:345 msgid "File not found: " msgstr "File non trovato: " #: ../perroquetlib/repository/exercise_repository_manager.py:359 msgid "Invalid package, missing template.perroquet." msgstr "" #: ../perroquetlib/repository/exercise_repository_manager.py:385 msgid "Exercise already exist." msgstr "Esercizio già esistente." #: ../perroquetlib/gui/gui.py:85 #, python-format msgid "The file '%s' doesn't exist. Please modify exercise paths" msgstr "Il file '%s' non esiste. Modifica i percorsi dell'esercizio" #: ../perroquetlib/gui/gui.py:86 msgid "load error" msgstr "errore di caricamento" #: ../perroquetlib/gui/gui.py:283 #: ../perroquetlib/gui/gui_exercise_manager.py:236 #: ../perroquetlib/gui/gui_sequence_properties_advanced.py:72 msgid "Path" msgstr "Percorso" #: ../perroquetlib/gui/gui.py:305 msgid "Do you really quit without save ?" msgstr "Vuoi veramente uscire senza salvare?" #: ../perroquetlib/gui/gui.py:306 msgid "Confirm quit" msgstr "Conferma uscita" #: ../perroquetlib/gui/gui.py:320 msgid "Information" msgstr "Informazione" #: ../perroquetlib/gui/gui.py:809 msgid "Select File to open" msgstr "Seleziona il file da aprire" #: ../perroquetlib/gui/gui.py:814 ../perroquetlib/gui/gui.py:852 msgid "Perroquet files" msgstr "File di Perroquet" #: ../perroquetlib/gui/gui.py:819 ../perroquetlib/gui/gui.py:838 #: ../perroquetlib/gui/gui.py:857 ../perroquetlib/gui/gui.py:878 #: ../perroquetlib/gui/gui.py:899 msgid "All files" msgstr "Tutti i file" #: ../perroquetlib/gui/gui.py:828 msgid "Select package to import" msgstr "Seleziona il pacchetto da importare" #: ../perroquetlib/gui/gui.py:833 ../perroquetlib/gui/gui.py:894 msgid "Perroquet package files" msgstr "Pacchetti di Perroquet" #: ../perroquetlib/gui/gui.py:847 msgid "Select File to Save to" msgstr "Seleziona File dove Salvare" #: ../perroquetlib/gui/gui.py:868 ../perroquetlib/gui/gui.py:889 msgid "Select File to Export to" msgstr "Seleziona il file da esportare" #: ../perroquetlib/gui/gui.py:873 msgid "Perroquet template files" msgstr "" #: ../perroquetlib/gui/gui_controller.py:237 #, python-format msgid "- Sequences: %(found)s/%(count)s (%(percent)s %%)\n" msgstr "- Sequenze: %(found)s/%(count)s (%(percent)s %%)\n" #: ../perroquetlib/gui/gui_controller.py:238 #, python-format msgid "- Words: %(found)s/%(count)s (%(percent)s %%)\n" msgstr "- Parole: %(found)s/%(count)s (%(percent)s %%)\n" #: ../perroquetlib/gui/gui_controller.py:239 #, python-format msgid "- Repeat ratio: %s per words" msgstr "Rapporto di ripetizione: %s per parole" #. Wrong password #: ../perroquetlib/gui/gui_controller.py:287 #: ../perroquetlib/gui/gui_controller.py:518 msgid "Wrong password" msgstr "Password errata" #: ../perroquetlib/gui/gui_exercise_manager.py:82 msgid "Updating repositories..." msgstr "Aggiornamento repositories..." #: ../perroquetlib/gui/gui_exercise_manager.py:108 msgid "Local repository" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:110 msgid "Distant repository" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:112 msgid "Offline repository" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:114 msgid "Orphan repository" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:132 msgid "Group" msgstr "Gruppo" #: ../perroquetlib/gui/gui_exercise_manager.py:148 #: ../perroquetlib/gui/gui_exercise_manager.py:266 #: ../perroquetlib/gui/gui_exercise_manager.py:446 msgid "Installed" msgstr "Installato" #: ../perroquetlib/gui/gui_exercise_manager.py:151 #: ../perroquetlib/gui/gui_exercise_manager.py:268 #: ../perroquetlib/gui/gui_exercise_manager.py:439 msgid "Available" msgstr "Disponibile" #: ../perroquetlib/gui/gui_exercise_manager.py:154 #: ../perroquetlib/gui/gui_exercise_manager.py:270 #: ../perroquetlib/gui/gui_exercise_manager.py:454 msgid "Used" msgstr "Usato" #: ../perroquetlib/gui/gui_exercise_manager.py:157 #: ../perroquetlib/gui/gui_exercise_manager.py:272 #: ../perroquetlib/gui/gui_exercise_manager.py:456 msgid "Done" msgstr "Completato" #: ../perroquetlib/gui/gui_exercise_manager.py:168 msgid "Exercise" msgstr "Esercizio" #: ../perroquetlib/gui/gui_exercise_manager.py:174 #: ../perroquetlib/gui/gui_exercise_manager.py:274 #: ../perroquetlib/gui/gui_exercise_manager.py:307 msgid "Name" msgstr "Nome" #: ../perroquetlib/gui/gui_exercise_manager.py:179 msgid "Type" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:184 #: ../perroquetlib/gui/gui_exercise_manager.py:276 msgid "Description" msgstr "Descrizione" #: ../perroquetlib/gui/gui_exercise_manager.py:189 msgid "Status" msgstr "Stato" #: ../perroquetlib/gui/gui_exercise_manager.py:194 #: ../perroquetlib/gui/gui_exercise_manager.py:283 msgid "Words count" msgstr "Conteggio parole" #: ../perroquetlib/gui/gui_exercise_manager.py:275 msgid "Licence" msgstr "Licenza" #: ../perroquetlib/gui/gui_exercise_manager.py:277 msgid "Author" msgstr "Autore" #: ../perroquetlib/gui/gui_exercise_manager.py:278 msgid "Author website" msgstr "Sito dell'autore" #: ../perroquetlib/gui/gui_exercise_manager.py:279 msgid "Author contact" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:282 msgid "Version" msgstr "Versione" #: ../perroquetlib/gui/gui_exercise_manager.py:284 msgid "Language" msgstr "Lingua" #: ../perroquetlib/gui/gui_exercise_manager.py:285 msgid "Media type" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:286 msgid "Id" msgstr "Id" #: ../perroquetlib/gui/gui_exercise_manager.py:287 msgid "Install status" msgstr "Stato installazione" #: ../perroquetlib/gui/gui_exercise_manager.py:295 msgid "Packager" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:296 msgid "Packager website" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:297 msgid "Packager contact" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:299 msgid "Exercise path" msgstr "Percorso esercizio" #: ../perroquetlib/gui/gui_exercise_manager.py:300 msgid "Package path" msgstr "Percorso pacchetto" #: ../perroquetlib/gui/gui_exercise_manager.py:301 msgid "Template path" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:302 msgid "Instance path" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:303 msgid "Finished exercise path" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:312 msgid "Value" msgstr "Valore" #: ../perroquetlib/gui/gui_exercise_manager.py:352 msgid "Install" msgstr "Installa" #: ../perroquetlib/gui/gui_exercise_manager.py:356 #: ../perroquetlib/gui/gui_exercise_manager.py:360 msgid "Cancel install" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:368 #: ../perroquetlib/gui/gui_exercise_manager.py:376 msgid "Remove" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:372 msgid "Continue" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:416 #, python-format msgid "%d available exercises" msgstr "%d esercizi disponibili" #: ../perroquetlib/gui/gui_exercise_manager.py:418 #, python-format msgid "%d available exercise" msgstr "%d esercizio disponibile" #: ../perroquetlib/gui/gui_exercise_manager.py:422 #, python-format msgid " and %d installed exercises" msgstr " e %d esercizi installati" #: ../perroquetlib/gui/gui_exercise_manager.py:424 #, python-format msgid " and %d installed exercise" msgstr " e %d esercizio installato" #: ../perroquetlib/gui/gui_exercise_manager.py:441 msgid "Downloading" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:444 msgid "Installing" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:448 msgid "Corrupted" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:450 msgid "Canceled" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:452 msgid "Removing" msgstr "" #: ../perroquetlib/gui/gui_exercise_manager.py:464 #, python-format msgid "Downloading ... %d%%" msgstr "" #: ../perroquetlib/gui/gui_password_dialog.py:49 msgid "Properties password :" msgstr "" #: ../perroquetlib/video_player.py:49 msgid "" "You need to install the gstreamer soundtouch elements to use slowly play " "feature." msgstr "" #~ msgid "Select File to Open" #~ msgstr "Seleziona file da aprire" #~ msgid "Copyright © 2009-2010 Frédéric Bertolus" #~ msgstr "Copyright © 2009-2010 Frédéric Bertolus" #~ msgid "A oral comprehension teacher" #~ msgstr "Un insegnante di comprensione orale" #~ msgid "New exercice" #~ msgstr "Nuovo Esercizio" #~ msgid "Open saved exercice" #~ msgstr "Apri esercizio salvato" #~ msgid "Open exercise" #~ msgstr "Apri esercizio" #~ msgid "Save exercice" #~ msgstr "Salva l'esercizio" #~ msgid "The audio track should be in the exercice language" #~ msgstr "La traccia audio dovrebbe essere nella lingua dell'esercizio" #~ msgid "" #~ "These subtitles should be in the exercice language.\n" #~ "Supported format: srt" #~ msgstr "" #~ "Questi sottotitoli dovrebbero essere nella lingua dell'esercizio.\n" #~ "Formato supportato: srt" #~ msgid "Load error" #~ msgstr "Errore di caricamento" #~ msgid "" #~ "- Sequences: --/--\n" #~ "- Words: --/--\n" #~ "- Repeat ratio: -- per words" #~ msgstr "" #~ "- Sequenze: --/--\n" #~ "- Parole: --/--\n" #~ "- Rapporto di ripetizione: -- per parole" #~ msgid "Exercice video or audio: " #~ msgstr "Audio o Video dell'Esercizio: " #~ msgid "Exercice properties" #~ msgstr "Proprietà dell'esercizio" #~ msgid "Exercice subtitle: " #~ msgstr "Sottotitolo dell'Esercizio: " #~ msgid "Exercice: " #~ msgstr "Esercizio: " #, python-format #~ msgid "The file '%s' doesn't exist. Please modify exercice paths" #~ msgstr "Il file '%s' non esiste. Per favore modifica i path dell'esercizio" #~ msgid "" #~ "Word filter\n" #~ "This filter use regexp. Examples:\n" #~ "abc = all words containing 'abc'\n" #~ "^abc = all words starting with 'abc'\n" #~ "abc$ = all words ending with 'abc'\n" #~ "\n" #~ "The character . can replace any character:\n" #~ "^abc.. = all words of at least 5 letters starting with 'abc'\n" #~ "^...$ = all 4-letter word\n" #~ "^ab.c$ = all 4-letter word beginning with 'ab' and ending with 'c'\n" #~ "\n" #~ "For more information on regular expressions: " #~ "http://en.wikipedia.org/wiki/Regular_expression" #~ msgstr "" #~ "Filtro parole\n" #~ "Questo filtro sfrutta le espressioni regolari. Esempi:\n" #~ "abc = tutte le parole contenenti \"abc\"\n" #~ "^abc = tutte le parole che cominciano con \"abc\"\n" #~ "abc$ = tutte le parole che finiscono con \"abc\"\n" #~ "\n" #~ "Il carattere . può sostituire ogni carattere:\n" #~ "^abc.. = Tutte le parole, di almeno 5 lettere, che cominciano con \"abc\"\n" #~ "^...$ = Tutte le parole di 4 lettere\n" #~ "^ab.c$ = Tutte le parole di 4 lettere che cominciano con \"ab\" e che " #~ "finiscono con 'c'\n" #~ "\n" #~ "Per maggiori informazioni sulle espressioni regolari: " #~ "http://en.wikipedia.org/wiki/Regular_expression" #~ msgid "Oral comprehention teacher" #~ msgstr "Insegnante di comprensione orale" #~ msgid "Open saved exercise" #~ msgstr "Apri esercizio salvato" perroquet-1.1.1.orig/po/es.po0000644000175000017500000006640311561344350016275 0ustar georgeskgeorgesk# Spanish translation for perroquet # Copyright (c) 2010 Rosetta Contributors and Canonical Ltd 2010 # This file is distributed under the same license as the perroquet package. # FIRST AUTHOR , 2010. # msgid "" msgstr "" "Project-Id-Version: perroquet\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2010-06-19 10:43+0200\n" "PO-Revision-Date: 2010-06-27 04:26+0000\n" "Last-Translator: Fred Bertolus \n" "Language-Team: Spanish \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2011-01-17 05:02+0000\n" "X-Generator: Launchpad (build 12177)\n" #: ../data/perroquet.desktop.in.h:1 msgid "Oral comprehension teacher" msgstr "Maestro de la comprensión oral" #: ../data/perroquet.desktop.in.h:2 ../data/perroquet.ui.h:31 #: ../perroquetlib/gui/gui_controller.py:201 msgid "Perroquet" msgstr "Perroquet" #: ../data/perroquet.ui.h:1 msgid "" "- Sequences: --/--\n" " - Words: --/--\n" " - Repeat ratio: -- per words" msgstr "" "- Frases: --/--\n" "- Palabras: --/--\n" "- Razón de repetición: -- por palabras" #: ../data/perroquet.ui.h:4 msgid "-- s" msgstr "-- s" #: ../data/perroquet.ui.h:5 msgid "--/--" msgstr "--/--" #: ../data/perroquet.ui.h:6 msgid "An oral comprehension teacher" msgstr "Un profesor de comprensión oral" #: ../data/perroquet.ui.h:7 msgid "Copyright © 2009-2010 Perroquet Team" msgstr "Copyright © 2009-2010 Perroquet Team" #: ../data/perroquet.ui.h:8 msgid "Correction" msgstr "Corrección" #: ../data/perroquet.ui.h:9 msgid "Display correction" msgstr "Mostrar correcciones" #: ../data/perroquet.ui.h:10 msgid "Display translation" msgstr "Mostrar la traducción" #: ../data/perroquet.ui.h:11 msgid "E_dit" msgstr "E_ditar" #: ../data/perroquet.ui.h:12 msgid "Erase" msgstr "Borrar" #: ../data/perroquet.ui.h:13 msgid "Exercise language" msgstr "Idioma del ejercicio" #: ../data/perroquet.ui.h:14 ../data/properties.ui.h:1 #: ../data/properties_advanced.ui.h:5 msgid "Exercise properties" msgstr "Propiedades del ejercicio" #: ../data/perroquet.ui.h:15 msgid "Exercise subtitle: " msgstr "Subtitulo del ejercicio " #: ../data/perroquet.ui.h:16 msgid "Exercise video or audio: " msgstr "Ejercicio audio o video " #: ../data/perroquet.ui.h:17 msgid "Export" msgstr "Exportar" #: ../data/perroquet.ui.h:18 msgid "Export as package..." msgstr "Exportar como paquete" #: ../data/perroquet.ui.h:19 msgid "Export as template..." msgstr "Exportar como plantilla" #: ../data/perroquet.ui.h:20 msgid "H_elp" msgstr "A_yuda" #: ../data/perroquet.ui.h:21 msgid "Hint" msgstr "Consejo" #: ../data/perroquet.ui.h:22 msgid "Import a package" msgstr "Importar un paquete" #: ../data/perroquet.ui.h:23 ../data/properties.ui.h:3 #: ../data/properties_advanced.ui.h:7 ../data/settings.ui.h:7 msgid "Language:" msgstr "Lenguaje" #: ../data/perroquet.ui.h:24 msgid "Last open files" msgstr "Ultimos archivos abiertos" #: ../data/perroquet.ui.h:25 msgid "Lateral panel" msgstr "Panel lateral" #: ../data/perroquet.ui.h:26 msgid "List of all words to find in the exercise" msgstr "Lista de todas las palabras encontradas en el ejercicio" #: ../data/perroquet.ui.h:27 msgid "New exercise" msgstr "Nuevo ejercicio" #: ../data/perroquet.ui.h:28 msgid "Next Sequence" msgstr "Próxima secuencia" #: ../data/perroquet.ui.h:29 msgid "Next sequence" msgstr "Próxima secuencia" #: ../data/perroquet.ui.h:30 msgid "Pause" msgstr "Pausa" #: ../data/perroquet.ui.h:32 msgid "Play" msgstr "Reproducir" #: ../data/perroquet.ui.h:33 msgid "Play speed" msgstr "Velocidad de reproducción" #: ../data/perroquet.ui.h:34 msgid "Position in current sequence" msgstr "Posición en la secuencia actual" #: ../data/perroquet.ui.h:35 msgid "Position in exercise" msgstr "Posición en ejercicio" #: ../data/perroquet.ui.h:36 msgid "Previous sequence" msgstr "Secuencia anterior" #: ../data/perroquet.ui.h:37 msgid "Progress statistics" msgstr "Estadísticas de progreso" #: ../data/perroquet.ui.h:38 msgid "Properties" msgstr "Propiedades" #: ../data/perroquet.ui.h:39 msgid "Replay sequence" msgstr "Reproducir la secuencia" #: ../data/perroquet.ui.h:40 msgid "Reset exercise progress" msgstr "Reset del progresso del ejercicio" #: ../data/perroquet.ui.h:41 msgid "Reset the exercise progress" msgstr "Reset del progresso del ejercicio" #: ../data/perroquet.ui.h:42 msgid "Reveal the sequence" msgstr "Mostrar la secuencia" #: ../data/perroquet.ui.h:43 msgid "Reveal the word" msgstr "Mostrar la palabra" #: ../data/perroquet.ui.h:44 msgid "Save exercise" msgstr "Guardar ejercicio" #: ../data/perroquet.ui.h:45 ../data/properties.ui.h:6 #: ../data/properties_advanced.ui.h:21 msgid "Select a subtitle file" msgstr "Seleccionar un archivo de subtítulos" #: ../data/perroquet.ui.h:46 ../data/properties.ui.h:7 #: ../data/properties_advanced.ui.h:22 msgid "Select a video file" msgstr "Seleccionar un archivo de video" #: ../data/perroquet.ui.h:47 msgid "Show/Hide correction" msgstr "Mostrar/Ocultar corrección" #: ../data/perroquet.ui.h:48 msgid "Show/Hide translation" msgstr "Mostrar/esconder traducción" #: ../data/perroquet.ui.h:49 msgid "The audio track should be in the exercise language" msgstr "La pista audio tiene que usar el idioma del ejercicio" #: ../data/perroquet.ui.h:50 msgid "" "These subtitles should be in the exercise language.\n" "Supported format: srt" msgstr "" "Los subtitlos tienen que usar el idioma del ejercicio.\n" "Formato aceptado : srt." #: ../data/perroquet.ui.h:52 msgid "" "These subtitles should be in your mother language.\n" "Supported format: srt" msgstr "" "Usar subtitlos en su lengua materna.\n" "Formato aceptado: srt." #: ../data/perroquet.ui.h:54 msgid "" "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 3 of the License, or (at your option) " "any later version.\n" "\n" "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.\n" "\n" "You should have received a copy of the GNU General Public License along with " "this program. If not, see ." msgstr "" "Este programa es software libre: usted puede redistribuirlo y / o " "modificarlo bajo los términos de la GNU General Public License publicada por " "la Free Software Foundation, bien de la versión 3 de la Licencia, o (a su " "elección) cualquier versión posterior.\n" "\n" "Este programa se distribuye con la esperanza de que será útil, pero SIN " "NINGUNA GARANTÍA, incluso sin la garantía implícita de COMERCIABILIDAD o " "IDONEIDAD PARA UN PROPÓSITO PARTICULAR. Consulte la GNU General Public " "License para más detalles.\n" "\n" "Usted debería haber recibido una copia de la Licencia Pública General GNU " "junto con este programa. Si no, vea, ." #: ../data/perroquet.ui.h:59 ../perroquetlib/gui/gui_exercise_manager.py:291 msgid "Translation" msgstr "Traducción" #: ../data/perroquet.ui.h:60 msgid "Translation subtitles (optional): " msgstr "Subtítulos de la traducción (opcional): " #: ../data/perroquet.ui.h:61 msgid "" "Word filter\n" " This filter use regexp. Examples:\n" " abc = all words containing 'abc'\n" " ^abc = all words starting with 'abc'\n" " abc$ = all words ending with 'abc'\n" "\n" " The character . can replace any character:\n" " ^abc.. = all words of at least 5 letters starting with 'abc'\n" " ^...$ = all 4-letter word\n" " ^ab.c$ = all 4-letter word beginning with 'ab' and ending with 'c'\n" "\n" " For more information on regular expressions: " "http://en.wikipedia.org/wiki/Regular_expression" msgstr "" "Filtro de palabras\n" "El filtro utiliza expresiones regulares. Ejemplo:\n" "abc = todas la palabras que contienen 'abc'\n" "^abc = todas las palabras que empezan por 'abc'\n" "abc$ = todas la palabras que accaban por 'abc'\n" "\n" "El carácter . puede substituir a cualquier otro carácter.\n" "^abc.. = todas la palabras de 5 letras al minimo que empezan por 'abc'\n" "^...$ = todas la palabras de 4 letras\n" "^ab.c$ = todas las palabras de 4 letras que empezan por 'ab' y accaban por " "'c'\n" "\n" "Para informaciones adicionales: " "http://es.wikipedia.org/wiki/Expresion_regular" #: ../data/perroquet.ui.h:73 msgid "Words" msgstr "Palabras" #: ../data/perroquet.ui.h:74 msgid "_Advanced properties" msgstr "_Propiedades avanzadas" #: ../data/perroquet.ui.h:75 msgid "_Exercise" msgstr "_Ejercicio" #: ../data/perroquet.ui.h:76 msgid "_Exercise manager" msgstr "_Manejador de ejercicio" #: ../data/perroquet.ui.h:77 msgid "_File" msgstr "_Archivo" #: ../data/perroquet.ui.h:78 msgid "open exercise" msgstr "abrir ejercicio" #: ../data/perroquet.ui.h:79 msgid "open saved exercise" msgstr "abrir ejercicio guardado" #: ../data/properties.ui.h:2 ../data/properties_advanced.ui.h:6 msgid "Exercise: " msgstr "Ejercicio " #: ../data/properties.ui.h:4 ../data/properties_advanced.ui.h:12 msgid "Media: " msgstr "Media " #: ../data/properties.ui.h:5 ../data/settings.ui.h:13 msgid "Repeat sequences after completed it" msgstr "Repetir secuencias despues de completarla" #: ../data/properties.ui.h:8 ../data/properties_advanced.ui.h:25 msgid "Translation: " msgstr "Traducción " #: ../data/properties.ui.h:9 ../data/properties_advanced.ui.h:26 msgid "Use dynamic correction" msgstr "Usar correción dinámica" #: ../data/properties.ui.h:10 msgid "" "With dynamic correction, the correct words will be displayed as valid as " "soon as they are correctly typed." msgstr "" "Con la correción dinámica, las palabras correctas se mostrarán como válidas " "apenas sean escritas." #: ../data/properties_advanced.ui.h:1 msgid "Correction password : " msgstr "Password para corregir : " #: ../data/properties_advanced.ui.h:2 ../data/settings.ui.h:3 msgid "Disable help tools" msgstr "Desactivar la ayuda" #: ../data/properties_advanced.ui.h:3 msgid "Exercise" msgstr "Ejercicio" #: ../data/properties_advanced.ui.h:4 msgid "Exercise name:" msgstr "Nombre de ejercicio:" #: ../data/properties_advanced.ui.h:8 msgid "Lock exercise's correction" msgstr "Bloquear la corrección del ejercicio" #: ../data/properties_advanced.ui.h:9 msgid "Lock exercise's propreties" msgstr "Bloquear las propiedades del ejercicio" #: ../data/properties_advanced.ui.h:10 msgid "Locks" msgstr "Bloquear" #: ../data/properties_advanced.ui.h:11 ../data/settings.ui.h:9 msgid "Maximum sequence time (s): " msgstr "Máximo tiempo de secuencia (s): " #: ../data/properties_advanced.ui.h:13 msgid "Paths" msgstr "Rutas" #: ../data/properties_advanced.ui.h:14 msgid "Paths list" msgstr "Lista de rutas" #: ../data/properties_advanced.ui.h:15 ../data/settings.ui.h:10 msgid "Play sequence in a random order" msgstr "Reproducir secuencias en orden aleatorio" #: ../data/properties_advanced.ui.h:16 ../data/settings.ui.h:11 msgid "Playing time after sequences (ms):" msgstr "Reproduciendo tiempo despues de secuencias (ms):" #: ../data/properties_advanced.ui.h:17 ../data/settings.ui.h:12 msgid "Playing time before sequences (ms):" msgstr "Reproduciendo tiempo antes de secuencias (ms):" #: ../data/properties_advanced.ui.h:18 msgid "Properties password : " msgstr "Password para las propiedades: " #: ../data/properties_advanced.ui.h:19 msgid "Repeat count limit by sequence (0 for no limit):" msgstr "" "Límite de cuenta de repeticiones por secuencia (0 para que no haya límite):" #: ../data/properties_advanced.ui.h:20 msgid "Repeat sequences after having completed it" msgstr "Repetir la secuencia después de completarla" #: ../data/properties_advanced.ui.h:23 msgid "Sequences" msgstr "Secuencias" #: ../data/properties_advanced.ui.h:24 ../data/settings.ui.h:19 msgid "Time between sequences (s):" msgstr "Tiempo entre secuencias (s):" #: ../data/exercise_manager.ui.h:1 msgid "Details" msgstr "Detalles" #: ../data/exercise_manager.ui.h:2 msgid "Exercises" msgstr "Ejercicios" #: ../data/exercise_manager.ui.h:3 msgid "Perroquet exercises manager" msgstr "Administrador de ejercicios de Perroquet" #: ../data/exercise_manager.ui.h:4 msgid "Repositories" msgstr "Repositorios" #: ../data/exercise_manager.ui.h:5 ../data/settings.ui.h:14 msgid "Settings" msgstr "Ajustes" #: ../data/exercise_manager.ui.h:6 msgid "Tree view mode" msgstr "Modo de vista de arbol" #: ../data/exercise_manager.ui.h:7 #: ../perroquetlib/gui/gui_exercise_manager.py:364 msgid "Use" msgstr "Usar" #: ../data/gui_message_dialog.ui.h:1 msgid "label" msgstr "Etiqueta" #: ../data/gui_password_dialog.ui.h:1 #: ../perroquetlib/gui/gui_password_dialog.py:46 msgid "Correction password :" msgstr "Correccion de contraseña :" #: ../data/gui_password_dialog.ui.h:2 msgid "Password for unlock correction" msgstr "Contraseña para desbloquear corrección" #: ../data/reset.ui.h:1 msgid "Cancel" msgstr "Cancelar" #: ../data/reset.ui.h:2 msgid "Do you want to reset the current exercise ?" msgstr "Quieres reestablecer el ejercicio actual" #: ../data/reset.ui.h:3 msgid "Ok" msgstr "Ok" #: ../data/settings.ui.h:1 msgid "Advanced" msgstr "Avanzado" #: ../data/settings.ui.h:2 msgid "Default exercise properties" msgstr "Propiedades predeterminadas del ejercicio" #: ../data/settings.ui.h:4 msgid "Exercise type:" msgstr "Tipo de ejercicio:" #: ../data/settings.ui.h:5 msgid "General" msgstr "Opciones Generales" #: ../data/settings.ui.h:6 msgid "Interface" msgstr "Interfaz:" #: ../data/settings.ui.h:8 msgid "Limit repeat count by sequence (0 for no limit):" msgstr "" "Limitar el numero de repeticiones por secuencia. (0 para \"sin limite\")" #: ../data/settings.ui.h:15 msgid "Show menu bar" msgstr "Mostrar la barra de menu" #: ../data/settings.ui.h:16 msgid "Show play and pause buttons" msgstr "Mostrar los botones de reproducir y pausa" #: ../data/settings.ui.h:17 msgid "Show settings" msgstr "Mostrar preferencias" #: ../data/settings.ui.h:18 msgid "Skip valid sequences using next and previous sequence buttons" msgstr "" "Saltar las secuencias validas usando los botones de secuencia siguiente y " "anterior" #: ../data/settings.ui.h:20 msgid "Use speed changer" msgstr "Usar el cambiador de velocidad" #: ../perroquetlib/core.py:504 msgid "Untitled exercise" msgstr "Ejercicio sin titulo" #: ../perroquetlib/core.py:556 msgid "" "Import finish succesfully. Use the exercises manager to use the newly " "installed exercise." msgstr "" "La importación se completó con éxito. Use el administrador de ejercicios " "para usar el ejercicio que se ha instalado." #: ../perroquetlib/core.py:558 msgid "Import failed." msgstr "Error en la importación." #: ../perroquetlib/repository/exercise_repository_exercise.py:51 #: ../perroquetlib/repository/exercise_repository_exercise.py:52 #: ../perroquetlib/repository/exercise_repository_exercise.py:53 #: ../perroquetlib/repository/exercise_repository_exercise.py:54 #: ../perroquetlib/repository/exercise_repository_exercise.py:55 #: ../perroquetlib/repository/exercise_repository_exercise.py:56 #: ../perroquetlib/repository/exercise_repository_exercise.py:57 #: ../perroquetlib/repository/exercise_repository_exercise.py:58 #: ../perroquetlib/repository/exercise_repository_exercise.py:59 #: ../perroquetlib/repository/exercise_repository_exercise.py:60 msgid "Not specified" msgstr "No especificado" #: ../perroquetlib/repository/exercise_repository_exercise.py:451 msgid "Imported exercise" msgstr "Ejercicio importado" #: ../perroquetlib/repository/exercise_repository_manager.py:345 msgid "File not found: " msgstr "Archivo no encontrado " #: ../perroquetlib/repository/exercise_repository_manager.py:359 msgid "Invalid package, missing template.perroquet." msgstr "Paquete invalido, falta la plantilla Perroquet" #: ../perroquetlib/repository/exercise_repository_manager.py:385 msgid "Exercise already exist." msgstr "Ejercicio ya existe" #: ../perroquetlib/gui/gui.py:85 #, python-format msgid "The file '%s' doesn't exist. Please modify exercise paths" msgstr "El archivo '%s' no existe. Modifica el camino del ejercicio." #: ../perroquetlib/gui/gui.py:86 msgid "load error" msgstr "de carga de error" #: ../perroquetlib/gui/gui.py:283 #: ../perroquetlib/gui/gui_exercise_manager.py:236 #: ../perroquetlib/gui/gui_sequence_properties_advanced.py:72 msgid "Path" msgstr "Ruta" #: ../perroquetlib/gui/gui.py:305 msgid "Do you really quit without save ?" msgstr "Realmente desea salir sin guardar cambios?" #: ../perroquetlib/gui/gui.py:306 msgid "Confirm quit" msgstr "Confirmar salida" #: ../perroquetlib/gui/gui.py:320 msgid "Information" msgstr "Información" #: ../perroquetlib/gui/gui.py:809 msgid "Select File to open" msgstr "Selecciona un archivo para abrir" #: ../perroquetlib/gui/gui.py:814 ../perroquetlib/gui/gui.py:852 msgid "Perroquet files" msgstr "Archivos de Perroquet" #: ../perroquetlib/gui/gui.py:819 ../perroquetlib/gui/gui.py:838 #: ../perroquetlib/gui/gui.py:857 ../perroquetlib/gui/gui.py:878 #: ../perroquetlib/gui/gui.py:899 msgid "All files" msgstr "Todos los archivos" #: ../perroquetlib/gui/gui.py:828 msgid "Select package to import" msgstr "Seleccionar paquetes a importar" #: ../perroquetlib/gui/gui.py:833 ../perroquetlib/gui/gui.py:894 msgid "Perroquet package files" msgstr "Paquete Perroquet" #: ../perroquetlib/gui/gui.py:847 msgid "Select File to Save to" msgstr "Seleccionar archivos a gardar" #: ../perroquetlib/gui/gui.py:868 ../perroquetlib/gui/gui.py:889 msgid "Select File to Export to" msgstr "Seleccionar el archivo a exportar" #: ../perroquetlib/gui/gui.py:873 msgid "Perroquet template files" msgstr "plantilla de archivos Perroquet" #: ../perroquetlib/gui/gui_controller.py:237 #, python-format msgid "- Sequences: %(found)s/%(count)s (%(percent)s %%)\n" msgstr "- Frases: %(found)s/%(count)s (%(percent)s %%)\n" #: ../perroquetlib/gui/gui_controller.py:238 #, python-format msgid "- Words: %(found)s/%(count)s (%(percent)s %%)\n" msgstr "- Palabras: %(found)s/%(count)s (%(percent)s %%)\n" #: ../perroquetlib/gui/gui_controller.py:239 #, python-format msgid "- Repeat ratio: %s per words" msgstr "- Relación de repetición: %s por palabras" #. Wrong password #: ../perroquetlib/gui/gui_controller.py:287 #: ../perroquetlib/gui/gui_controller.py:518 msgid "Wrong password" msgstr "Contraseña incorrecta" #: ../perroquetlib/gui/gui_exercise_manager.py:82 msgid "Updating repositories..." msgstr "actualizando repositorios" #: ../perroquetlib/gui/gui_exercise_manager.py:108 msgid "Local repository" msgstr "Repositorio local" #: ../perroquetlib/gui/gui_exercise_manager.py:110 msgid "Distant repository" msgstr "Repositorio distante" #: ../perroquetlib/gui/gui_exercise_manager.py:112 msgid "Offline repository" msgstr "Repositorio fuera de linea" #: ../perroquetlib/gui/gui_exercise_manager.py:114 msgid "Orphan repository" msgstr "Repositorio huerfano" #: ../perroquetlib/gui/gui_exercise_manager.py:132 msgid "Group" msgstr "Grupo" #: ../perroquetlib/gui/gui_exercise_manager.py:148 #: ../perroquetlib/gui/gui_exercise_manager.py:266 #: ../perroquetlib/gui/gui_exercise_manager.py:446 msgid "Installed" msgstr "Instalado" #: ../perroquetlib/gui/gui_exercise_manager.py:151 #: ../perroquetlib/gui/gui_exercise_manager.py:268 #: ../perroquetlib/gui/gui_exercise_manager.py:439 msgid "Available" msgstr "Disponible" #: ../perroquetlib/gui/gui_exercise_manager.py:154 #: ../perroquetlib/gui/gui_exercise_manager.py:270 #: ../perroquetlib/gui/gui_exercise_manager.py:454 msgid "Used" msgstr "Utilizado" #: ../perroquetlib/gui/gui_exercise_manager.py:157 #: ../perroquetlib/gui/gui_exercise_manager.py:272 #: ../perroquetlib/gui/gui_exercise_manager.py:456 msgid "Done" msgstr "Terminado" #: ../perroquetlib/gui/gui_exercise_manager.py:168 msgid "Exercise" msgstr "Ejercicio" #: ../perroquetlib/gui/gui_exercise_manager.py:174 #: ../perroquetlib/gui/gui_exercise_manager.py:274 #: ../perroquetlib/gui/gui_exercise_manager.py:307 msgid "Name" msgstr "Nombre" #: ../perroquetlib/gui/gui_exercise_manager.py:179 msgid "Type" msgstr "Tipo" #: ../perroquetlib/gui/gui_exercise_manager.py:184 #: ../perroquetlib/gui/gui_exercise_manager.py:276 msgid "Description" msgstr "Descripción" #: ../perroquetlib/gui/gui_exercise_manager.py:189 msgid "Status" msgstr "Estatus" #: ../perroquetlib/gui/gui_exercise_manager.py:194 #: ../perroquetlib/gui/gui_exercise_manager.py:283 msgid "Words count" msgstr "Cuenta de las palabras" #: ../perroquetlib/gui/gui_exercise_manager.py:275 msgid "Licence" msgstr "Licencia" #: ../perroquetlib/gui/gui_exercise_manager.py:277 msgid "Author" msgstr "Autor" #: ../perroquetlib/gui/gui_exercise_manager.py:278 msgid "Author website" msgstr "Página web del autor" #: ../perroquetlib/gui/gui_exercise_manager.py:279 msgid "Author contact" msgstr "Contacto del autor" #: ../perroquetlib/gui/gui_exercise_manager.py:282 msgid "Version" msgstr "Versión" #: ../perroquetlib/gui/gui_exercise_manager.py:284 msgid "Language" msgstr "Idioma" #: ../perroquetlib/gui/gui_exercise_manager.py:285 msgid "Media type" msgstr "Tipo de medio" #: ../perroquetlib/gui/gui_exercise_manager.py:286 msgid "Id" msgstr "Identificador" #: ../perroquetlib/gui/gui_exercise_manager.py:287 msgid "Install status" msgstr "Estatus de la instalación" #: ../perroquetlib/gui/gui_exercise_manager.py:295 msgid "Packager" msgstr "Empaquetador" #: ../perroquetlib/gui/gui_exercise_manager.py:296 msgid "Packager website" msgstr "Página web del empaquetador" #: ../perroquetlib/gui/gui_exercise_manager.py:297 msgid "Packager contact" msgstr "Contacto web del empaquetador" #: ../perroquetlib/gui/gui_exercise_manager.py:299 msgid "Exercise path" msgstr "Ruta del ejercicio" #: ../perroquetlib/gui/gui_exercise_manager.py:300 msgid "Package path" msgstr "Ruta del paquete" #: ../perroquetlib/gui/gui_exercise_manager.py:301 msgid "Template path" msgstr "Ruta de la plantilla" #: ../perroquetlib/gui/gui_exercise_manager.py:302 msgid "Instance path" msgstr "Ruta de la instancia" #: ../perroquetlib/gui/gui_exercise_manager.py:303 msgid "Finished exercise path" msgstr "Finalizado el ejercicio ruta" #: ../perroquetlib/gui/gui_exercise_manager.py:312 msgid "Value" msgstr "Valor" #: ../perroquetlib/gui/gui_exercise_manager.py:352 msgid "Install" msgstr "Instalar" #: ../perroquetlib/gui/gui_exercise_manager.py:356 #: ../perroquetlib/gui/gui_exercise_manager.py:360 msgid "Cancel install" msgstr "Cancelar instalación" #: ../perroquetlib/gui/gui_exercise_manager.py:368 #: ../perroquetlib/gui/gui_exercise_manager.py:376 msgid "Remove" msgstr "Borrar" #: ../perroquetlib/gui/gui_exercise_manager.py:372 msgid "Continue" msgstr "Continuar" #: ../perroquetlib/gui/gui_exercise_manager.py:416 #, python-format msgid "%d available exercises" msgstr "%d ejercicios disponibles" #: ../perroquetlib/gui/gui_exercise_manager.py:418 #, python-format msgid "%d available exercise" msgstr "%d ejercicio disponible" #: ../perroquetlib/gui/gui_exercise_manager.py:422 #, python-format msgid " and %d installed exercises" msgstr " and %d ejercicios disponibles" #: ../perroquetlib/gui/gui_exercise_manager.py:424 #, python-format msgid " and %d installed exercise" msgstr " and %d ejercicio instalado" #: ../perroquetlib/gui/gui_exercise_manager.py:441 msgid "Downloading" msgstr "Descargando" #: ../perroquetlib/gui/gui_exercise_manager.py:444 msgid "Installing" msgstr "Instalando" #: ../perroquetlib/gui/gui_exercise_manager.py:448 msgid "Corrupted" msgstr "Dañado" #: ../perroquetlib/gui/gui_exercise_manager.py:450 msgid "Canceled" msgstr "cancelado" #: ../perroquetlib/gui/gui_exercise_manager.py:452 msgid "Removing" msgstr "Borrando" #: ../perroquetlib/gui/gui_exercise_manager.py:464 #, python-format msgid "Downloading ... %d%%" msgstr "Descargando ... %d%%" #: ../perroquetlib/gui/gui_password_dialog.py:49 msgid "Properties password :" msgstr "Propiedades de la contraseña:" #: ../perroquetlib/video_player.py:49 msgid "" "You need to install the gstreamer soundtouch elements to use slowly play " "feature." msgstr "Necesitas gstreamer soundtouch para usar la lectura lenta." #~ msgid "Select File to Open" #~ msgstr "Seleccionar archivo a abrir" #~ msgid "Done path" #~ msgstr "Ruta hecho" #~ msgid "A oral comprehension teacher" #~ msgstr "Un profesor de comprensión oral" #~ msgid "Copyright © 2009-2010 Frédéric Bertolus" #~ msgstr "Derechos de autor © 2009-2010 Frédéric Bertolus" #~ msgid "Oral comprehention teacher" #~ msgstr "Profesor de comprensión oral" #~ msgid "Open exercise" #~ msgstr "Abrir ejercicio" #~ msgid "Open saved exercise" #~ msgstr "Abrir ejercicio guardado" #~ msgid "Lock exercise's properties" #~ msgstr "Bloquear propiedades del ejercicio" #~ msgid "" #~ "Properties menus will not be accessible. Modify the perroquet file to remove " #~ "the lock or modify properties" #~ msgstr "" #~ "Las propiedades de menu no serán accesibles. Modificar el archivo perroquet " #~ "para remover el bloqueo o modificar propiedades." #~ msgid "Load error" #~ msgstr "Error de cargamento" #~ msgid "New exercice" #~ msgstr "Nuevo ejercicio" #~ msgid "Open saved exercice" #~ msgstr "Abrir ejercicio guardado" #~ msgid "Save exercice" #~ msgstr "Guardar ejercicio" #~ msgid "Exercice: " #~ msgstr "Ejercicio: " #~ msgid "The audio track should be in the exercice language" #~ msgstr "La pista de audio debería estar en el idioma del ejercicio" #~ msgid "" #~ "These subtitles should be in the exercice language.\n" #~ "Supported format: srt" #~ msgstr "" #~ "Estos subtítulos deberían estar en el idioma del ejercicio.\n" #~ "Formato soportado: srt" #~ msgid "" #~ "Word filter\n" #~ "This filter use regexp. Examples:\n" #~ "abc = all words containing 'abc'\n" #~ "^abc = all words starting with 'abc'\n" #~ "abc$ = all words ending with 'abc'\n" #~ "\n" #~ "The character . can replace any character:\n" #~ "^abc.. = all words of at least 5 letters starting with 'abc'\n" #~ "^...$ = all 4-letter word\n" #~ "^ab.c$ = all 4-letter word beginning with 'ab' and ending with 'c'\n" #~ "\n" #~ "For more information on regular expressions: " #~ "http://en.wikipedia.org/wiki/Regular_expression" #~ msgstr "" #~ "Filtro de palabras\n" #~ "Este filtro usa expresiones regulares. Ejemplos:\n" #~ "abc = todas las palabras que contengan «abc»\n" #~ "^abc = todas las palabras que empiezan por «abc»\n" #~ "abc$ = todas las terminas que terminan en «abc»\n" #~ "\n" #~ "El carácter . puede reemplazar a cualquier otro carácter:\n" #~ "^abc.. = todas las palabras de al menos 5 letras que empiecen por «abc»\n" #~ "^...$ = todas las palabras de 4 letras\n" #~ "^ab.c$ = todas las palabras de 4 letras que empiecen por «ab» y que terminen " #~ "en «c»\n" #~ "\n" #~ "Para más información sobre las expresiones regulares: " #~ "http://es.wikipedia.org/wiki/Expresi%C3%B3n_regular" #~ msgid "" #~ "- Sequences: --/--\n" #~ "- Words: --/--\n" #~ "- Repeat ratio: -- per words" #~ msgstr "" #~ "- Secuencias: --/--\n" #~ "- Palabras: --/--\n" #~ "- Promedio de repeticiones: -- por palabras" #~ msgid "Exercice properties" #~ msgstr "Propiedades del ejercicio" #~ msgid "Exercice subtitle: " #~ msgstr "Subtítulos del ejercicio: " #, python-format #~ msgid "The file '%s' doesn't exist. Please modify exercice paths" #~ msgstr "El archivo «%s» no existe. Modifique las rutas a los ejercicios" #~ msgid "Exercice video or audio: " #~ msgstr "Vídeo o audio del ejercicio: " perroquet-1.1.1.orig/po/genpot.sh0000755000175000017500000000012411561344350017145 0ustar georgeskgeorgesk#!/bin/sh # Make translation files intltool-update -g perroquet -o perroquet.pot -p perroquet-1.1.1.orig/README0000644000175000017500000000357211561344350015566 0ustar georgeskgeorgesk Perroquet ========= These are the release notes for Perroquet 1/ What is Perroquet ? ====================== Perroquet force you to repeat what you hear. This programs give the possibility to make oral comprehension exercices using audio or video files and subtitles. With the help of subtitles time-codes, Perroquet cut the video you have to type the words you hear. 2/ How to use ? ================ - Click on "New exercice" button. - Select a video or audio file, the subtitle file (srt format) in same language and optionnaly, another subtitles file in an other language. - The media is loaded and the first segment is played. - In the typing area, small rectangles represents each word you have to find. Try to type. If a word become green, you found the right word. - You can replay the current sequence with the "Replay" button or pressing "Return". - When the whole sequence is valid, Perroquet will repeat a last time the sequence and continue to the next sequence. You also can use the exercise manager inedit menu to easily install exercises from online database. 3/ Controls ============= * Key 'return' : Replay sequence. * Key 'F1' : Hint on current word. * Key 'shift + F1' : Complete the current word. * Key 'control + shift + F1' : Complete the current sequence. * Key 'F2' : Show/hide translation * Key 'F9' : Show/hide lateral panel * Key '+' and '-' : Change play speed. The speed change features must be enabled * Key 'tab' and 'shift + tab' : next and previous tab * Key 'Up' and 'Down' or 'Page up' and 'Page down' : next and previous sequence * Key 'Pause' : Toogle pause 4/ Additional help ==================== You may found aditionnal help here: Official web site: http://perroquet.b219.org Project management page : https://launchpad.net/perroquet Contact : perroquet-team@lists.launchpad.net fred.bertolus@gmail.com perroquet-1.1.1.orig/perroquetlib/0000755000175000017500000000000011561344350017414 5ustar georgeskgeorgeskperroquet-1.1.1.orig/perroquetlib/core.py0000644000175000017500000005216411561344350020726 0ustar georgeskgeorgesk# -*- coding: utf-8 -*- # Copyright (C) 2009-2010 Frédéric Bertolus. # Copyright (C) 2009-2010 Matthieu Bizien. # # This file is part of Perroquet. # # Perroquet 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 3 of the License, or # (at your option) any later version. # # Perroquet 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 Perroquet. If not, see . import logging import thread import time from gettext import gettext as _ import gtk from debug import defaultLoggingHandler, defaultLoggingLevel from model.exercise import Exercise from model.exercise_parser import load_exercise, save_exercise from model.sequence import NoCharPossible from perroquetlib.config import config from perroquetlib.repository.exercise_repository_manager import ExerciseRepositoryManager from video_player import VideoPlayer # The Core make the link between the GUI, the vidéo player, the current # open exercise and all others part of the application class Core(object): WAIT_BEGIN = 0 WAIT_END = 1 def __init__(self): self.player = None self.last_save = False self.exercise = None self.config = config self.logger = logging.Logger("Core") self.logger.setLevel(defaultLoggingLevel) self.logger.addHandler(defaultLoggingHandler) #Call by the main, give an handler to the main gui def set_gui(self, gui): self.gui_controller = gui #Create a new exercice based on paths. load the new exercise and #begin to play def new_exercise(self, videoPath, exercisePath, translationPath, langId): self.exercise = Exercise() self.exercise.set_media_change_callback(self.media_change_call_back) self.exercise.new() self.exercise.set_language_id(langId) self._set_paths(videoPath, exercisePath, translationPath) self.exercise.initialize() self._reload(True); self._activate_sequence() self.gui_controller.set_title("", True) #Configure the paths for the current exercice. Reload subtitles list. def _set_paths(self, videoPath, exercisePath, translationPath): self.exercise.set_video_path(videoPath) self.exercise.set_exercise_path(exercisePath) self.exercise.set_translation_path(translationPath) self.exercise.initialize() #Reload media player and begin to play (if the params is True) def _reload(self, load): if self.player != None: self.player.close() self.player = VideoPlayer() self.player.set_window_id(self.gui_controller.get_video_window_id()) self.player.activate_video_callback(self.gui_controller.activate_video_area) self.player.open(self.exercise.get_video_path()) self.player.set_callback(self._time_callback) self.paused = False self.gui_controller.activate_video_area(False) self.gui_controller.activate("loaded") self._update_word_list() self.timeUpdateThreadId = thread.start_new_thread(self.time_update_thread, ()) if load and self.exercise.get_repeat_count_limit_by_sequence() == 0: #Auto start play only if repeat is not limited self.play() else: self.pause() #play the media def play(self): self.gui_controller.set_playing(True) self.player.play() self.paused = False #pause the media def pause(self): self.gui_controller.set_playing(False) self.player.pause() self.paused = True #Modify media speed def set_speed(self, speed): self.gui_controller.set_speed(speed) self.player.set_speed(speed) #Callback call by video player to notify change of media position. #Stop the media at the end of uncompleted sequences def _time_callback(self): if self.state == Core.WAIT_BEGIN: self.player.set_next_callback_time(self.exercise.get_current_sequence().get_time_end() + self.exercise.get_play_margin_after()) self.state = Core.WAIT_END elif self.state == Core.WAIT_END: self.state = Core.WAIT_BEGIN if self.exercise.get_current_sequence().is_valid(): gtk.gdk.threads_enter() self.next_sequence(False) gtk.gdk.threads_leave() else: self.pause() #Repeat the currence sequence def repeat_sequence(self): if not self.exercise.is_current_sequence_repeat_limit_reach(): #Repeat limit not reach or no limit self.goto_sequence_begin() self.play() self.exercise.increment_current_sequence_repeat_count() #Change the active sequence def select_sequence(self, num, load=True): if self.exercise.get_current_sequence_id() == num: return self.exercise.goto_sequence(num) self._activate_sequence() if load and self.exercise.get_repeat_count_limit_by_sequence() == 0: #Auto start play only if repeat is not limited self.repeat_sequence() self.set_can_save(True) #Goto next sequence def next_sequence(self, load=True): if self.exercise.goto_next_sequence(): self.set_can_save(True) self._activate_sequence() if load and self.exercise.get_repeat_count_limit_by_sequence() == 0: #Auto start play only if repeat is not limited self.repeat_sequence() #Goto previous sequence def previous_sequence(self, load=True): if self.exercise.goto_previous_sequence(): self.set_can_save(True) self._activate_sequence() if load and self.exercise.get_repeat_count_limit_by_sequence() == 0: #Auto start play only if repeat is not limited self.repeat_sequence() #Goto next valid sequence def next_valid_sequence(self, load=True): if self.exercise.goto_next_valid_sequence(): self.set_can_save(True) self._activate_sequence() if load and self.exercise.get_repeat_count_limit_by_sequence() == 0: #Auto start play only if repeat is not limited self.repeat_sequence() #Goto previous valid sequence def previous_valid_sequence(self, load=True): if self.exercise.goto_previous_valid_sequence(): self.set_can_save(True) self._activate_sequence() if load and self.exercise.get_repeat_count_limit_by_sequence() == 0: #Auto start play only if repeat is not limited self.repeat_sequence() #Update interface with new sequence. Configure stop media callback def _activate_sequence(self): self.state = Core.WAIT_BEGIN self.set_speed(1) self.player.set_next_callback_time(self.exercise.get_current_sequence().get_time_begin()) self.gui_controller.set_sequence_number(self.exercise.get_current_sequence_id(), self.exercise.get_sequence_count()) self.gui_controller.set_sequence(self.exercise.get_current_sequence()) self.__activate_translation() self._update_stats() #_update displayed translation on new active sequence def __activate_translation(self): if not self.exercise.get_translation_list(): self.gui_controller.set_translation("") else: translation = "" currentBegin = self.exercise.get_current_sequence().get_time_begin() currentEnd = self.exercise.get_current_sequence().get_time_end() for sub in self.exercise.get_translation_list(): begin = sub.get_time_begin() end = sub.get_time_end() if (begin >= currentBegin and begin <= currentEnd) or (end >= currentBegin and end <= currentEnd) or (begin <= currentBegin and end >= currentEnd): translation += sub.get_text() + " " self.gui_controller.set_translation(translation) #Update displayed stats on new active sequence def _update_stats(self): sequenceCount = self.exercise.get_sequence_count() sequenceFound = 0 wordCount = 0 wordFound = 0 for sequence in self.exercise.get_sequence_list(): wordCount = wordCount + sequence.get_word_count() if sequence.is_valid(): sequenceFound += 1 wordFound += sequence.get_word_count() else: wordFound += sequence.get_word_found() if wordFound == 0: repeatRate = float(0) else: repeatRate = float(self.exercise.get_repeat_count()) / float(wordFound) self.gui_controller.set_statitics(sequenceCount, sequenceFound, wordCount, wordFound, repeatRate) def _update(self): self.gui_controller.set_sequence(self.exercise.get_current_sequence()) self.__validate_sequence() #Verify if the sequence is complete def __validate_sequence(self): if self.exercise.get_current_sequence().is_valid(): if self.exercise.get_repeat_after_completed(): if self.exercise.get_repeat_count_limit_by_sequence() == 0: #Auto start play only if repeat is not limited self.repeat_sequence() else: self.next_sequence(False) if self.exercise.get_repeat_count_limit_by_sequence() == 0: #Auto start play only if repeat is not limited self.play() #Goto beginning of the current sequence. Can start to play as soon #as the media player is ready def goto_sequence_begin(self, asSoonAsReady=False): self.state = Core.WAIT_END begin_time = self.exercise.get_current_sequence().get_time_begin() - self.exercise.get_play_margin_before() if begin_time < 0: begin_time = 0 if asSoonAsReady: self.player.seek_as_soon_as_ready(begin_time) else: self.player.seek(begin_time) self.player.set_next_callback_time(self.exercise.get_current_sequence().get_time_end()) #Write a char in current sequence at cursor position def write_char(self, char): if self.exercise.is_character_match(char): self.exercise.get_current_sequence().write_char(char) self.exercise.get_current_sequence().update_cursor_position() self._update() self.set_can_save(True) else: self._update() #Goto next word in current sequence def next_word(self): self.exercise.get_current_sequence().next_word() self._update() #Goto previous word in current sequence def previous_word(self): self.exercise.get_current_sequence().previous_word() self._update() #Choose current word in current sequence def select_sequence_word(self, wordIndex, wordIndexPos): try: self.exercise.get_current_sequence().select_sequence_word(wordIndex, wordIndexPos) except NoCharPossible: self.exercise.get_current_sequence().select_sequence_word(wordIndex, -1) self._update() #Goto first word in current sequence def first_word(self): self.exercise.get_current_sequence().first_word() self._update() #Goto last word in current sequence def last_word(self): self.exercise.get_current_sequence().last_word() self._update() #Delete a char before the cursor in current sequence def delete_previous_char(self): self.exercise.get_current_sequence().delete_previous_char() self._update() self.set_can_save(True) #Delete a char after the cursor in current sequence def delete_next_char(self): self.exercise.get_current_sequence().delete_next_char() self._update() self.set_can_save(True) #Goto previous char in current sequence def previous_char(self): self.exercise.get_current_sequence().previous_char() #The sequence don't change but the cursor position is no more up to date self._update() #Goto next char in current sequence def next_char(self): self.exercise.get_current_sequence().next_char() #The sequence don't change but the cursor position is no more up to date self._update() #Reveal correction for word at cursor in current sequence def complete_word(self): self.exercise.get_current_sequence().show_hint() self._update() self.set_can_save(True) #Reveal correction for word at cursor in current sequence def reveal_word(self): self.exercise.get_current_sequence().complete_word() self.exercise.get_current_sequence().next_word() self._update() self.set_can_save(True) #Reveal correction for word at cursor in current sequence def reveal_sequence(self): self.exercise.get_current_sequence().complete_all() self._update() self.set_can_save(True) #reset whole exercise def reset_exercise_content(self): self.exercise.reset() self.exercise.goto_sequence(0) #FIXME self._update() self.set_can_save(True) self.logger.debug("need to stop the current sequence") #FIXME #pause or play media def toggle_pause(self): if self.player.is_paused() and self.paused: self.play() elif not self.player.is_paused() and not self.paused: self.pause() #Change position in media and play it def seek_sequence(self, time): begin_time = self.exercise.get_current_sequence().get_time_begin() - self.exercise.get_play_margin_before() if begin_time < 0: begin_time = 0 pos = begin_time + time self.player.seek(pos) self.player.set_next_callback_time(self.exercise.get_current_sequence().get_time_end() + self.exercise.get_play_margin_after()) self.state = Core.WAIT_END if self.exercise.get_repeat_count_limit_by_sequence() == 0: #Auto start play only if repeat is not limited self.play() #Thread to update slider position in gui def time_update_thread(self): timeUpdateThreadId = self.timeUpdateThreadId while timeUpdateThreadId == self.timeUpdateThreadId: time.sleep(0.5) pos_int = self.player.get_current_time() if pos_int != None: end_time = self.exercise.get_current_sequence().get_time_end() begin_time = self.exercise.get_current_sequence().get_time_begin() - self.exercise.get_play_margin_before() if begin_time < 0: begin_time = 0 duration = end_time - begin_time pos = pos_int -begin_time self.gui_controller.set_sequence_time(pos, duration) #Save current exercice def save(self, saveAs=False): if not self.exercise: self.logger.error("Save called but no exercise load") return if saveAs or self.exercise.get_output_save_path() == None: outputSavePath = self.gui_controller.ask_save_path() if not outputSavePath: return self.exercise.set_output_save_path(outputSavePath + ".perroquet") save_exercise(self.exercise, self.exercise.get_output_save_path()) self.config.set("lastopenfile", self.exercise.get_output_save_path()) #lastopenfileS l = self.config.get("lastopenfiles") path = self.exercise.get_output_save_path() name = self.exercise.get_name() or path self.config.set("lastopenfiles", [[path, name]] + [p for p in l if p[0] != path][:10]) self.set_can_save(False) #load the exercice at path def load_exercise(self, path): self.gui_controller.activate("closed") if self.exercise: self.save() try: self.exercise = load_exercise(path) self.exercise.set_media_change_callback(self.media_change_call_back) except IOError: self.logger.exception("No file at " + path) return if not self.exercise: return validPaths, errorList = self.exercise.is_paths_valid() if not validPaths: for error in errorList: self.gui_controller.signal_exercise_bad_path(error) self.set_can_save(False) self.gui_controller.activate("load_failed") self.gui_controller.ask_properties() return self._reload(False) if self.exercise.get_output_save_path() == None: self.set_can_save(True) else: self.set_can_save(False) self._activate_sequence() self.goto_sequence_begin(True) if self.exercise.get_repeat_count_limit_by_sequence() == 0: #Auto start play only if repeat is not limited self.play() #Change paths of current exercice and reload subtitles and video def _update_paths(self, videoPath, exercisePath, translationPath): self.exercise.set_video_path(videoPath) self.exercise.set_exercise_path(exercisePath) self.exercise.set_translation_path(translationPath) validPaths, errorList = self.exercise.is_paths_valid() if not validPaths: for error in errorList: self.gui_controller.signal_exercise_bad_path(error) self.gui_controller.activate("load_failed") self.set_can_save(False) return self._set_paths(videoPath, exercisePath, translationPath) self._reload(True) self.set_can_save(True) self._activate_sequence() self.goto_sequence_begin(True) if self.exercise.get_repeat_count_limit_by_sequence() == 0: #Auto start play only if repeat is not limited self.play() def update_properties(self): self.exercise.initialize() self._reload(True) self.set_can_save(True) self._activate_sequence() self.goto_sequence_begin(True) if self.exercise.get_repeat_count_limit_by_sequence() == 0: #Auto start play only if repeat is not limited self.play() #get paths of current exercise def get_paths(self): return (self.exercise.get_video_path(), self.exercise.get_exercise_path(), self.exercise.get_translation_path()) #Udpates vocabulary list in interface def _update_word_list(self): self.gui_controller.set_word_list(self.exercise.extract_word_list()) #Notify the user use the repeat command (for stats) def user_repeat(self): self.exercise.increment_repeat_count() self.set_can_save(True) #Signal to the gui that the exercise has unsaved changes def set_can_save(self, save): self.gui_controller.set_can_save(save) if self.exercise == None: title = "" elif self.exercise.get_name() != None: title = self.exercise.get_name() elif self.exercise.get_output_save_path() != None: title = self.exercise.get_output_save_path() else: title = _("Untitled exercise") self.last_save = save self.gui_controller.set_title(title, save) def get_can_save(self): return self.last_save def get_exercise(self): return self.exercise def get_player(self): return self.player def media_change_call_back(self): self.logger.info("new media : " + self.exercise.get_video_path()) """self.Pause() self.player.open(self.exercise.get_video_path()) """ self._reload(True) self._activate_sequence() self.goto_sequence_begin(True) if self.exercise.get_repeat_count_limit_by_sequence() == 0: #Auto start play only if repeat is not limited self.play() def export_as_template(self): self.gui_controller.ask_properties_advanced() path = self.gui_controller.ask_export_as_template_path() if path: self.exercise.set_template(True) save_exercise(self.exercise, path) self.exercise.set_template(False) def export_as_package(self): self.gui_controller.ask_properties_advanced() path = self.gui_controller.ask_export_as_package_path() if path: repoManager = ExerciseRepositoryManager() repoManager.export_as_package(self.exercise, path) self.logger.info("Export done") else: self.logger.warn("No valid path to export??") def import_package(self): import_path = self.gui_controller.ask_import_package() if import_path is not None: repo_manager = ExerciseRepositoryManager() error = repo_manager.import_package(import_path) if error is None: self.gui_controller.display_message(_("Import finish succesfully. Use the exercises manager to use the newly installed exercise.")) else: self.gui_controller.display_message(_("Import failed." + " " + error)) perroquet-1.1.1.orig/perroquetlib/perroquet.py0000644000175000017500000000334711561344350022023 0ustar georgeskgeorgesk#! /usr/bin/python # -*- coding: utf-8 -*- # Copyright (C) 2009-2010 Frédéric Bertolus. # Copyright (C) 2009-2010 Matthieu Bizien. # # This file is part of Perroquet. # # Perroquet 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 3 of the License, or # (at your option) any later version. # # Perroquet 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 Perroquet. If not, see . import logging import os import sys from perroquetlib.config.perroquet_config import config from perroquetlib.core import Core, defaultLoggingHandler, defaultLoggingLevel from perroquetlib.gui.gui_controller import GuiController class Perroquet(object): def __init__(self): self.core = Core() self.gui = GuiController() self.core.set_gui(self.gui) self.gui.set_core(self.core) self.gui.activate("closed") self.logger = logging.Logger(self.__class__.__name__) self.logger.setLevel(defaultLoggingLevel) self.logger.addHandler(defaultLoggingHandler) def run(self): if len(sys.argv) > 1: path = os.path.abspath(sys.argv[1]) self.core.load_exercise(path) elif config.get("lastopenfile"): self.logger.info("last open file : " + config.get("lastopenfile")) self.core.load_exercise(config.get("lastopenfile")) self.gui.run() perroquet-1.1.1.orig/perroquetlib/gui/0000755000175000017500000000000011561344350020200 5ustar georgeskgeorgeskperroquet-1.1.1.orig/perroquetlib/gui/gui_settings.py0000644000175000017500000002111011561344350023251 0ustar georgeskgeorgesk# -*- coding: utf-8 -*- # Copyright (C) 2009-2010 Frédéric Bertolus. # # This file is part of Perroquet. # # Perroquet 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 3 of the License, or # (at your option) any later version. # # Perroquet 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 Perroquet. If not, see . import gettext import gtk from perroquetlib.config import config from perroquetlib.model.languages_manager import LanguagesManager _ = gettext.gettext class Guisettings: def __init__(self, parent): self.config = config self.parent = parent self.builder = gtk.Builder() self.builder.set_translation_domain("perroquet") self.builder.add_from_file(self.config.get("ui_settings_path")) self.builder.connect_signals(self) self.dialog = self.builder.get_object("dialogSettings") self.dialog.set_modal(True) self.dialog.set_transient_for(self.parent) self.iterPath = None def run(self): self.load() self.dialog.run() self.dialog.destroy() def load(self): adjustmentTimeBetweenSequence = self.builder.get_object("adjustmentTimeBetweenSequence") adjustmentTimeBetweenSequence.set_value(float(config.get("default_exercise_time_between_sequences")) / 1000) adjustmentMaximumSequenceTime = self.builder.get_object("adjustmentMaximumSequenceTime") adjustmentMaximumSequenceTime.set_value(float(config.get("default_exercise_max_sequence_length")) / 1000) adjustmentTimeBeforeSequence = self.builder.get_object("adjustmentTimeBeforeSequence") adjustmentTimeBeforeSequence.set_value(float(config.get("default_exercise_play_margin_after"))) adjustmentTimeAfterSequence = self.builder.get_object("adjustmentTimeAfterSequence") adjustmentTimeAfterSequence.set_value(float(config.get("default_exercise_play_margin_before"))) checkbuttonRepeatAfterComplete = self.builder.get_object("checkbuttonRepeatAfterComplete") checkbuttonRepeatAfterComplete.set_active(config.get("default_exercise_repeat_after_completed") == 1) checkbuttonRandomOrder = self.builder.get_object("checkbuttonRandomOrder") checkbuttonRandomOrder.set_active(config.get("default_exercise_random_order") == 1) #General checkbutton_navigation_skip_valid_sequences = self.builder.get_object("checkbutton_navigation_skip_valid_sequences") checkbutton_navigation_skip_valid_sequences.set_active(config.get("navigation_skip_valid_sequences") == 1) checkbutton_use_speed_changer = self.builder.get_object("checkbutton_use_speed_changer") checkbutton_use_speed_changer.set_active(config.get("interface_use_speed_change") == 1) #Interface checkbuttonShowPlayPauseButtons = self.builder.get_object("checkbuttonShowPlayPauseButtons") checkbuttonShowPlayPauseButtons.set_active(config.get("interface_show_play_pause_buttons") == 1) checkbuttonShowSettings = self.builder.get_object("checkbuttonShowSettings") checkbuttonShowSettings.set_active(config.get("interface_lock_settings") != 1) # Language self.liststoreLanguage = gtk.ListStore(str, str) languageManager = LanguagesManager() languagesList = languageManager.get_languages_list() currentLangId = config.get("default_exercise_language") for language in languagesList: iter = self.liststoreLanguage.append([language.name, language.id]) if language.id == currentLangId: currentIter = iter comboboxLanguage = self.builder.get_object("comboboxLanguage") cell = gtk.CellRendererText() comboboxLanguage.set_model(self.liststoreLanguage) comboboxLanguage.pack_start(cell, True) comboboxLanguage.add_attribute(cell, 'text', 0) comboboxLanguage.set_active_iter(currentIter) def on_button_exercise_prop_ok_clicked(self, widget, data=None): adjustmentTimeBetweenSequence = self.builder.get_object("adjustmentTimeBetweenSequence") config.set("default_exercise_time_between_sequences", int(1000 * adjustmentTimeBetweenSequence.get_value())) adjustmentMaximumSequenceTime = self.builder.get_object("adjustmentMaximumSequenceTime") config.set("default_exercise_max_sequence_length", int(1000 * adjustmentMaximumSequenceTime.get_value())) adjustmentTimeBeforeSequence = self.builder.get_object("adjustmentTimeBeforeSequence") config.set("default_exercise_play_margin_before", int(adjustmentTimeBeforeSequence.get_value())) adjustmentTimeAfterSequence = self.builder.get_object("adjustmentTimeAfterSequence") config.set("default_exercise_play_margin_after", int(adjustmentTimeAfterSequence.get_value())) checkbuttonRepeatAfterComplete = self.builder.get_object("checkbuttonRepeatAfterComplete") if checkbuttonRepeatAfterComplete.get_active(): config.set("default_exercise_repeat_after_completed", 1) else: config.set("default_exercise_repeat_after_completed", 0) checkbuttonRandomOrder = self.builder.get_object("checkbuttonRandomOrder") if checkbuttonRandomOrder.get_active(): config.set("default_exercise_random_order", 1) else: config.set("default_exercise_random_order", 0) #General checkbutton_navigation_skip_valid_sequences = self.builder.get_object("checkbutton_navigation_skip_valid_sequences") if checkbutton_navigation_skip_valid_sequences.get_active(): config.set("navigation_skip_valid_sequences",1) else: config.set("navigation_skip_valid_sequences",0) checkbutton_use_speed_changer = self.builder.get_object("checkbutton_use_speed_changer") if checkbutton_use_speed_changer.get_active(): config.set("interface_use_speed_change",1) else: config.set("interface_use_speed_change",0) #Interface checkbuttonShowPlayPauseButtons = self.builder.get_object("checkbuttonShowPlayPauseButtons") if checkbuttonShowPlayPauseButtons.get_active(): config.set("interface_show_play_pause_buttons", 1) else: config.set("interface_show_play_pause_buttons", 0) checkbuttonShowSettings = self.builder.get_object("checkbuttonShowSettings") if checkbuttonShowSettings.get_active(): config.set("interface_lock_settings", 0) else: config.set("interface_lock_settings", 1) #Language comboboxLanguage = self.builder.get_object("comboboxLanguage") self.liststoreLanguage.get_iter_first() iter = comboboxLanguage.get_active_iter() langId = self.liststoreLanguage.get_value(iter, 1) config.set("default_exercise_language", langId) self.dialog.response(gtk.RESPONSE_OK) def on_button_exercise_prop_cancel_clicked(self, widget, data=None): self.dialog.response(gtk.RESPONSE_CANCEL) def on_button_defaut_time_between_sequences_clicked(self, widget, data=None): """adjustmentTimeBetweenSequence = self.builder.get_object("adjustmentTimeBetweenSequence") exercice = Exercise() adjustmentTimeBetweenSequence.set_value(exercice.get_time_between_sequence())""" print "TODO" def on_button_defaut_maximum_sequence_time_clicked(self, widget, data=None): """adjustmentMaximumSequenceTime = self.builder.get_object("adjustmentMaximumSequenceTime") exercice = Exercise() adjustmentMaximumSequenceTime.set_value(exercice.get_max_sequence_length())""" print "TODO" def on_button_defaut_time_before_sequence_clicked(self, widget, data=None): """adjustmentTimeBeforeSequence = self.builder.get_object("adjustmentTimeBeforeSequence") exercice = Exercise() adjustmentTimeBeforeSequence.set_value(exercice.get_play_margin_before())""" print "TODO" def on_button_defaut_time_after_sequence_clicked(self, widget, data=None): """adjustmentTimeAfterSequence = self.builder.get_object("adjustmentTimeAfterSequence") exercice = Exercise() adjustmentTimeAfterSequence.set_value(exercice.get_play_margin_after())""" print "TODO" perroquet-1.1.1.orig/perroquetlib/gui/gui_controller.py0000644000175000017500000004520611561344350023610 0ustar georgeskgeorgesk# -*- coding: utf-8 -*- # Copyright (C) 2009-2010 Frédéric Bertolus. # Copyright (C) 2009-2010 Matthieu Bizien. # # This file is part of Perroquet. # # Perroquet 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 3 of the License, or # (at your option) any later version. # # Perroquet 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 Perroquet. If not, see . import gettext import re from perroquetlib.config.perroquet_config import config from perroquetlib.gui.gui import Gui from perroquetlib.gui.gui_exercise_controller import GuiExerciseController _ = gettext.gettext class GuiController: def __init__(self): """GuiController constructor""" self.core = None self.word_list = None # Mode can be closed, loaded or load_failed self.mode = "closed" self.gui = Gui(self) self.gui.set_active_video_area(False) self.gui_exercise_controller = None self.translation_visible = False self.correction_visible = False self.current_speed = 1.0 if not config.get("showlateralpanel"): self.gui.set_visible_lateral_panel(False) else: self.gui.set_checked_lateral_panel(True) self.gui.set_visible_lateral_panel(True) config.set("showlateralpanel", 1) def set_core(self, core): """Define perroquet core to use""" self.core = core self.gui_exercise_controller = GuiExerciseController(self, self.core, self.gui) def activate(self, mode): self.mode = mode self.refresh() def refresh(self): "Enable or disable ihm component" if self.mode == "loaded": self.gui.set_enable_sequence_index_selection(True) self.gui.set_enable_sequence_time_selection(True) self.gui.set_enable_replay_sequence(True) self.gui.set_enable_save_as(True) self.gui.set_enable_save(True) self.gui.set_enable_export_as_template(True) self.gui.set_enable_export_as_package(True) if self.core.get_exercise().is_lock_correction() and not self.core.get_exercise().is_lock_correction_password(): self.gui.set_enable_correction(False) else: self.gui.set_enable_correction(True) if self.core.get_exercise().is_lock_properties() and not self.core.get_exercise().is_lock_properties_password(): self.gui.set_enable_properties(False) self.gui.set_enable_advanced_properties(False) else: self.gui.set_enable_properties(True) self.gui.set_enable_advanced_properties(True) #Help if self.core.get_exercise().is_lock_help(): self.gui.set_enable_hint(False) self.gui.set_enable_reveal_word(False) self.gui.set_enable_reveal_sequence(False) self.gui.set_enable_translation(False) else: self.gui.set_enable_hint(True) self.gui.set_enable_reveal_word(True) self.gui.set_enable_reveal_sequence(True) self.gui.set_enable_translation(True) #Disable speed change slider if the media player not support it if config.get("interface_use_speed_change") == 1 and self.core.get_player().is_speed_changeable() and not self.core.get_exercise().is_lock_help(): self.gui.set_enable_speed_selection(True) else: self.gui.set_enable_speed_selection(False) if self.mode == "load_failed": self.gui.set_enable_sequence_index_selection(False) self.gui.set_enable_sequence_time_selection(False) self.gui.set_enable_hint(False) self.gui.set_enable_reveal_word(False) self.gui.set_enable_reveal_sequence(False) self.gui.set_enable_replay_sequence(False) self.gui.set_enable_properties(True) self.gui.set_enable_advanced_properties(True) self.gui.set_enable_translation(False) self.gui.set_enable_save_as(False) self.gui.set_enable_save(False) self.gui.set_enable_export_as_template(False) self.gui.set_enable_export_as_package(False) self.gui.set_enable_speed_selection(False) self.gui.set_enable_correction(False) if self.mode == "closed": self.gui.set_enable_sequence_index_selection(False) self.gui.set_enable_sequence_time_selection(False) self.gui.set_enable_hint(False) self.gui.set_enable_reveal_word(False) self.gui.set_enable_reveal_sequence(False) self.gui.set_enable_replay_sequence(False) self.gui.set_enable_properties(False) self.gui.set_enable_advanced_properties(False) self.gui.set_enable_translation(False) self.gui.set_enable_save_as(False) self.gui.set_enable_save(False) self.gui.set_enable_export_as_template(False) self.gui.set_enable_export_as_package(False) self.gui.set_enable_speed_selection(False) self.gui.set_enable_correction(False) if config.get("interface_show_play_pause_buttons") == 1: self.gui.set_visible_play(True) self.gui.set_visible_pause(True) else: self.gui.set_visible_play(False) self.gui.set_visible_pause(False) if config.get("interface_lock_settings") != 1: self.gui.set_enable_settings(True) else: self.gui.set_enable_settings(False) def get_video_window_id(self): return self.gui.get_video_window_id() def activate_video_area(self, state): self.gui.set_active_video_area(state) def set_word_list(self, word_list): self.word_list = word_list self.update_word_list() def update_word_list(self): """Apply filter and send the new list to the gui""" filtered_word_list = [] filter_regexp = self.gui.get_words_filter() try: re.search(filter_regexp, "") except re.error: filter_regexp = "" pass if not self.core.get_exercise().is_lock_help(): for word in self.word_list: if re.search(filter_regexp, word): filtered_word_list.append(word) self.gui.set_word_list(filtered_word_list) def is_correction_visible(self): return self.correction_visible def set_playing(self, state): self.gui.set_enable_play(not state) self.gui.set_enable_pause(state) def set_can_save(self, state): self.gui.set_enable_save(state) def set_title(self, title, save): newTitle = _("Perroquet") if save: newTitle += " *" if title != "": newTitle += " - " + title self.gui.set_title(newTitle) def set_speed(self, speed): self.current_speed = speed self.gui.set_speed(speed) def set_sequence_number(self, sequenceNumber, sequenceCount): sequenceNumber = sequenceNumber + 1 self.gui.set_sequence_index_selection(sequenceNumber, sequenceCount) self.gui.set_enable_next_sequence(sequenceNumber != sequenceCount) self.gui.set_enable_previous_sequence(sequenceNumber != 1) def set_sequence_time(self, sequence_position, sequence_time): if sequence_position > sequence_time: sequence_position = sequence_time if sequence_position < 0: sequence_position = 0 self.gui.set_sequence_time_selection(sequence_position, sequence_time) def set_sequence(self, sequence): self.gui_exercise_controller.set_sequence(sequence) def set_translation(self, translation): self.gui.set_translation(translation) def set_statitics(self, sequenceCount, sequenceFound, wordCount, wordFound, repeatRate): text = "" text = text + _("- Sequences: %(found)s/%(count)s (%(percent)s %%)\n") % {'found': str(sequenceFound), 'count': str(sequenceCount), 'percent': str(round(100 * sequenceFound / sequenceCount, 1))} text = text + _("- Words: %(found)s/%(count)s (%(percent)s %%)\n") % {'found': str(wordFound), 'count': str(wordCount), 'percent': str(round(100 * wordFound / wordCount, 1))} text = text + _("- Repeat ratio: %s per words") % str(round(repeatRate, 1)) self.gui.set_statitics(text) def run(self): self.gui.run() def toggle_lateral_panel(self): if config.get("showlateralpanel"): self.gui.set_visible_lateral_panel(False) config.set("showlateralpanel", 0) else: self.gui.set_visible_lateral_panel(True) config.set("showlateralpanel", 1) def toggle_translation(self): if not self.translation_visible: self.gui.set_visible_translation_panel(True) self.gui.set_enable_translation(True) self.gui.set_active_translation(True) self.translation_visible = True else: self.gui.set_visible_translation_panel(False) self.gui.set_enable_translation(True) self.gui.set_active_translation(False) self.translation_visible = False def toggle_correction(self): if not self.correction_visible: self.gui.set_enable_correction(True) self.gui.set_active_correction(True) self.correction_visible = True self.gui_exercise_controller.repaint() else: self.gui.set_enable_correction(True) self.gui.set_active_correction(False) self.correction_visible = False self.gui_exercise_controller.repaint() def _allow_properties(self): if self.core.get_exercise().is_lock_properties() and self.core.get_exercise().is_lock_properties_password(): password = self.gui.ask_properties_password() if password is not None: #Do nothing if cancel if self.core.get_exercise().verify_lock_properties_password(password): #Good password, unlock passwor self.core.get_exercise().set_lock_properties(False) return True else: #Wrong password self.gui.display_message(_("Wrong password")) return False else: #Cancel by user return False else: return True def notify_typing(self, new_text): if self.mode != "loaded": self.gui.set_typing_area_text([]) return False for char in new_text: if char == " ": self.core.next_word() else: self.core.write_char(char) return True def notify_move_cursor(self, movement): if self.mode != "loaded": return True return self.gui_exercise_controller.notify_move_cursor(movement) def notify_key_press(self, keyname, shift, control): if keyname == "Return" or keyname == "KP_Enter": if not self.core.exercise.get_current_sequence().is_valid(): self.core.user_repeat() self.core.repeat_sequence() elif keyname == "BackSpace": if not self.core.exercise.get_current_sequence().is_valid(): self.core.delete_previous_char() elif keyname == "Delete": if not self.core.exercise.get_current_sequence().is_valid(): self.core.delete_next_char() elif keyname == "Page_Down": self.core.previous_sequence() elif keyname == "Page_Up": self.core.next_sequence() elif keyname == "Down": self.core.previous_sequence() elif keyname == "Up": self.core.next_sequence() elif keyname == "Tab": if not self.core.exercise.get_current_sequence().is_valid(): self.core.next_word() elif keyname == "ISO_Left_Tab": if not self.core.exercise.get_current_sequence().is_valid(): self.core.previous_word() elif keyname == "F1": if not self.core.exercise.get_current_sequence().is_valid() and not self.core.get_exercise().is_lock_help(): if shift and control: self.core.reveal_sequence() elif shift: self.core.reveal_word() else: self.core.complete_word() elif keyname == "F2" and not self.core.get_exercise().is_lock_help(): self.toggle_translation() elif keyname == "F9": self.toggle_lateral_panel() elif keyname == "Pause": self.core.toggle_pause() elif keyname == "KP_Add": if self.current_speed > 0.9: self.core.set_speed(1.0) else: self.core.set_speed(self.current_speed + 0.1) elif keyname == "KP_Subtract": if self.current_speed < 0.85: self.core.set_speed(0.75) else: self.core.set_speed(self.current_speed-0.1) else: return False return True; def notify_quit(self): if not config.get("autosave"): if not self.core.get_can_save(): self.gui.quit() return False #True for quit elif self.gui.ask_confirm_quit_without_save(): config.save() self.gui.quit() return False #True for quit else: return True #True for not quit else: self.core.save() config.save() self.gui.quit() return True #True for quit def notify_properties_advanced(self): if self._allow_properties(): self.ask_properties_advanced() def notify_properties(self): if self._allow_properties(): self.ask_properties() def ask_properties_advanced(self): self.gui.ask_properties_advanced(self.core) def ask_properties(self): self.gui.ask_properties(self.core) def ask_export_as_package_path(self): return self.gui.ask_export_as_package_path() def ask_export_as_template_path(self): return self.gui.ask_export_as_template_path() def ask_import_package(self): return self.gui.ask_import_package() def display_message(self, message): return self.gui.display_message(message) def notify_settings(self): self.gui.ask_settings() self.core.update_properties() self.refresh() def notify_reset_exercise_content(self): if self.gui.ask_reset_exercise_content(): self.core.reset_exercise_content() def notify_new_exercise(self): self.gui.ask_new_exercise() self.gui.set_visible_new_exercise_dialog(True) def notify_new_exercise_create(self, videoPath, exercisePath, translationPath, langId): self.gui.set_visible_new_exercise_dialog(False) self.core.new_exercise(videoPath, exercisePath, translationPath, langId) def notify_new_exercise_cancel(self): self.gui.set_visible_new_exercise_dialog(False) def notify_export_as_template(self): self.core.export_as_template() def notify_export_as_package(self): self.core.export_as_package() def notify_import_package(self): self.core.import_package() def notify_next_sequence(self): if config.get("navigation_skip_valid_sequences") == 0: self.core.next_sequence() else: self.core.next_valid_sequence() def notify_previous_sequence(self): if config.get("navigation_skip_valid_sequences") == 0: self.core.previous_sequence() else: self.core.previous_valid_sequence() def notify_repeat_sequence(self): self.core.user_repeat() self.core.repeat_sequence() def notify_select_sequence_number(self, value): self.core.select_sequence(value) def notify_select_sequence_time(self, value): self.core.seek_sequence(value) def notify_select_speed(self, value): self.core.set_speed(value) def notify_hint(self): self.core.complete_word() def notify_reveal_word(self): self.core.reveal_word() def notify_reveal_sequence(self): self.core.reveal_sequence() def notify_play(self): self.core.play() def notify_pause(self): self.core.pause() def notify_save(self): self.core.save() def notify_save_as(self): self.core.save(True) def ask_save_path(self): return self.gui.ask_save_path() def notify_load(self): path = self.gui.ask_load_exercise() if path: self.core.load_exercise(path) def notify_load_path(self, path): self.core.load_exercise(path) def notify_filter_change(self): self.update_word_list() def notify_toogle_translation(self, visible): if visible != self.translation_visible: self.toggle_translation() def notify_toogle_correction(self, visible): self.gui.logger.debug("notify_toogle_correction") self.gui.logger.debug("visible=" + str(visible) + " , correction_visible=" + str(self.correction_visible)) if visible != self.correction_visible: if not self.correction_visible and self.core.get_exercise().is_lock_correction() and self.core.get_exercise().is_lock_correction_password(): password = self.gui.ask_correction_password() if password is not None: #Do nothing if cancel if self.core.get_exercise().verify_lock_correction_password(password): #Good password, unlock password self.toggle_correction() self.core.get_exercise().set_lock_correction(False) else: #Wrong password self.gui.display_message(_("Wrong password")) self.gui.set_active_correction(False) else: #Cancel by user self.gui.set_active_correction(False) else: self.toggle_correction() def notify_exercise_manager(self): self.gui.display_exercice_manager(self.core) perroquet-1.1.1.orig/perroquetlib/gui/gui_sequence_properties.py0000644000175000017500000001222111561344350025500 0ustar georgeskgeorgesk# -*- coding: utf-8 -*- # Copyright (C) 2009-2010 Frédéric Bertolus. # # This file is part of Perroquet. # # Perroquet 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 3 of the License, or # (at your option) any later version. # # Perroquet 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 Perroquet. If not, see . import gettext import gtk from perroquetlib.config import config from perroquetlib.model.languages_manager import LanguagesManager _ = gettext.gettext class GuiSequenceProperties: def __init__(self, core, parent): self.core = core self.config = config self.parent = parent self.builder = gtk.Builder() self.builder.set_translation_domain("perroquet") self.builder.add_from_file(self.config.get("ui_sequence_properties_path")) self.builder.connect_signals(self) self.dialog = self.builder.get_object("dialogExerciseProperties") self.pagePaths = self.builder.get_object("pagePaths") self.pageSequences = self.builder.get_object("pageSequences") self.dialog.set_modal(True) self.dialog.set_transient_for(self.parent) def run(self): self.load() self.dialog.run() self.dialog.destroy() def load(self): (videoPath, exercisePath, translationPath) = self.core.get_paths() if videoPath == "": videoPath = "None" if exercisePath == "": exercisePath = "None" if translationPath == "": translationPath = "None" videoChooser = self.builder.get_object("filechooserbuttonVideoProp") exerciseChooser = self.builder.get_object("filechooserbuttonExerciseProp") translationChooser = self.builder.get_object("filechooserbuttonTranslationProp") videoChooser.set_filename(videoPath) exerciseChooser.set_filename(exercisePath) translationChooser.set_filename(translationPath) checkbuttonRepeatAfterComplete = self.builder.get_object("checkbuttonRepeatAfterComplete") checkbuttonRepeatAfterComplete.set_active(self.core.get_exercise().get_repeat_after_completed()) checkbuttonUseDynamicCorrection = self.builder.get_object("checkbuttonUseDynamicCorrection") checkbuttonUseDynamicCorrection.set_active(self.core.get_exercise().is_use_dynamic_correction()) self.liststoreLanguage = gtk.ListStore(str, str) languageManager = LanguagesManager() languagesList = languageManager.get_languages_list() currentLangId = self.core.get_exercise().get_language_id() for language in languagesList: iter = self.liststoreLanguage.append([language.name, language.id]) if language.id == currentLangId: currentIter = iter comboboxLanguage = self.builder.get_object("comboboxLanguage") cell = gtk.CellRendererText() comboboxLanguage.set_model(self.liststoreLanguage) comboboxLanguage.pack_start(cell, True) comboboxLanguage.add_attribute(cell, 'text', 0) comboboxLanguage.set_active_iter(currentIter) def on_button_exercise_prop_ok_clicked(self, widget, data=None): videoChooser = self.builder.get_object("filechooserbuttonVideoProp") videoPath = videoChooser.get_filename() exerciseChooser = self.builder.get_object("filechooserbuttonExerciseProp") exercisePath = exerciseChooser.get_filename() translationChooser = self.builder.get_object("filechooserbuttonTranslationProp") translationPath = translationChooser.get_filename() if videoPath == "None" or videoPath == None: videoPath = "" if exercisePath == "None" or exercisePath == None: exercisePath = "" if translationPath == "None" or translationPath == None: translationPath = "" checkbuttonRepeatAfterComplete = self.builder.get_object("checkbuttonRepeatAfterComplete") self.core.get_exercise().set_repeat_after_completed(checkbuttonRepeatAfterComplete.get_active()) checkbuttonUseDynamicCorrection = self.builder.get_object("checkbuttonUseDynamicCorrection") self.core.get_exercise().set_use_dynamic_correction(checkbuttonUseDynamicCorrection.get_active()) comboboxLanguage = self.builder.get_object("comboboxLanguage") self.liststoreLanguage.get_iter_first() iter = comboboxLanguage.get_active_iter() langId = self.liststoreLanguage.get_value(iter, 1) self.core.get_exercise().set_language_id(langId) self.core._update_paths(videoPath, exercisePath, translationPath) self.core.set_can_save(True) self.dialog.response(gtk.RESPONSE_OK) def on_button_exercise_prop_cancel_clicked(self, widget, data=None): self.dialog.response(gtk.RESPONSE_CANCEL) perroquet-1.1.1.orig/perroquetlib/gui/gui.py0000644000175000017500000010131511561344350021337 0ustar georgeskgeorgesk# -*- coding: utf-8 -*- # Copyright (C) 2009-2010 Frédéric Bertolus. # Copyright (C) 2009-2010 Matthieu Bizien. # # This file is part of Perroquet. # # Perroquet 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 3 of the License, or # (at your option) any later version. # # Perroquet 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 Perroquet. If not, see . import gettext import locale import logging import os import gtk from gui_exercise_manager import GuiExerciseManager from gui_message_dialog import GuiMessageDialog from gui_password_dialog import GuiPasswordDialog from gui_reset_exercise import GuiResetExercise from gui_sequence_properties import GuiSequenceProperties from gui_sequence_properties_advanced import GuiSequencePropertiesAdvanced from gui_settings import Guisettings from perroquetlib.config import config from perroquetlib.debug import defaultLoggingHandler, defaultLoggingLevel from perroquetlib.model.languages_manager import LanguagesManager _ = gettext.gettext class Gui: def __init__(self, controller): locale.bindtextdomain(config.get("gettext_package"), config.get("localedir")) self.logger = logging.Logger("GUI") self.logger.setLevel(defaultLoggingLevel) self.logger.addHandler(defaultLoggingHandler) self.controller = controller self.builder = gtk.Builder() self.builder.set_translation_domain("perroquet") self.builder.add_from_file(config.get("ui_path")) self.builder.connect_signals(self) self.window = self.builder.get_object("MainWindow") self.window.set_icon_from_file(config.get("logo_path")) self.aboutDialog = self.builder.get_object("aboutdialog") icon = gtk.gdk.pixbuf_new_from_file(config.get("logo_path")) self.aboutDialog.set_logo(icon) self.aboutDialog.set_version(config.get("version")) # Sound icon self.builder.get_object("imageAudio").set_from_file(config.get("audio_icon")) self.typeLabel = self.builder.get_object("typeView") self.disable_changed_text_event = False self.setted_speed = 100 self.setted_sequence_number = 0 self.setted_position = 0 self.setted_typing_area_text = "" self.newExerciseDialog = None self.liststoreLanguage = None self.disable_correction_event = False self._update_last_open_files_tab() def signal_exercise_bad_path(self, path): dialog = gtk.MessageDialog(self.window, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, _("The file '%s' doesn't exist. Please modify exercise paths") % path) dialog.set_title(_("load error")) dialog.run() dialog.destroy() def get_video_window_id(self): return self.builder.get_object("videoArea").window.xid def set_active_video_area(self, state): if state: self.builder.get_object("videoArea").show() self.builder.get_object("imageAudio").hide() else: self.builder.get_object("videoArea").hide() self.builder.get_object("imageAudio").show() def set_speed(self, speed): self.setted_speed = int(speed * 100) ajustement = self.builder.get_object("adjustmentSpeed") ajustement.configure (self.setted_speed, 75, 100, 1, 10, 0) def set_sequence_index_selection(self, sequenceNumber, sequenceCount): ajustement = self.builder.get_object("adjustmentSequenceNum") self.setted_sequence_number = sequenceNumber ajustement.configure (sequenceNumber, 1, sequenceCount, 1, 10, 0) self.builder.get_object("labelSequenceNumber").set_text(str(sequenceNumber) + "/" + str(sequenceCount)) def set_sequence_time_selection(self, sequence_position, sequence_time): """Documentation""" self.setted_position = sequence_position / 100 ajustement = self.builder.get_object("adjustmentSequenceTime") ajustement.configure (self.setted_position, 0, sequence_time / 100, 1, 10, 0) textTime = round(float(sequence_position) / 1000, 1) textDuration = round(float(sequence_time) / 1000, 1) self.builder.get_object("labelSequenceTime").set_text(str(textTime) + "/" + str(textDuration) + " s") def set_word_list(self, word_list): """Create a string compose by word separated with \n and update the text field""" buffer = self.builder.get_object("textviewWordList").get_buffer() iter1 = buffer.get_start_iter() iter2 = buffer.get_end_iter() buffer.delete(iter1, iter2) formattedWordList = "" for word in word_list: formattedWordList = formattedWordList + word + "\n" iter = buffer.get_end_iter() buffer.insert(iter, formattedWordList) def get_words_filter(self): return self.builder.get_object("entryFilter").get_text() def set_translation(self, translation): textviewTranslation = self.builder.get_object("textviewTranslation").get_buffer() textviewTranslation.set_text(translation) def set_statitics(self, text): labelProgress = self.builder.get_object("labelProgress") labelProgress.set_label(text) def set_title(self, title): self.window.set_title(title) def _clear_typing_area(self): buffer = self.typeLabel.get_buffer() iter1 = buffer.get_start_iter() iter2 = buffer.get_end_iter() buffer.delete(iter1, iter2) def set_typing_area_text(self, formatted_text): self.disable_changed_text_event = True self._clear_typing_area() buffer = self.typeLabel.get_buffer() for (text, style) in formatted_text: size = buffer.get_char_count() iter1 = buffer.get_end_iter() buffer.insert(iter1, text) iter1 = buffer.get_iter_at_offset(size) iter2 = buffer.get_end_iter() buffer.apply_tag_by_name(style, iter1, iter2) self.setted_typing_area_text = buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter()) self.disable_changed_text_event = False def set_typing_area_cursor_position(self, cursor_position): buffer = self.typeLabel.get_buffer() iter = buffer.get_iter_at_offset(cursor_position) buffer.place_cursor(iter) def set_focus_typing_area(self): self.window.set_focus(self.typeLabel) def set_typing_area_style_list(self, style_list): """creates the labels used for the coloration of the text""" buffer = self.typeLabel.get_buffer() # Remove existing tags tag_table = buffer.get_tag_table() tag_table.foreach(self._destroy_tag, tag_table) for (tag_name,size, foreground_color ,background_color, through) in style_list: if foreground_color: (red, green, bleu) = foreground_color gtk_foreground_color = self.window.get_colormap().alloc_color( red*256, green*256, bleu*256) else: gtk_foreground_color = None if background_color: (red, green, bleu) = background_color gtk_background_color = self.window.get_colormap().alloc_color( red*256, green*256, bleu*256) else: gtk_background_color = None buffer.create_tag(tag_name, background=gtk_background_color, foreground=gtk_foreground_color, strikethrough=through, size_points=size) def _destroy_tag(text_tag, tag_table): tag_table.remove(text_tag) def ask_save_path(self): saver = SaveFileSelector(self.window) path = saver.run() return path def ask_export_as_template_path(self): saver = ExportAsTemplateFileSelector(self.window) path = saver.run() if path == "None" or path == None: return None elif not path.endswith(".perroquet"): path = path + ".perroquet" return path def ask_export_as_package_path(self): saver = ExportAsPackageFileSelector(self.window) path = saver.run() if path == "None" or path == None: return None elif not path.endswith(".tar"): path = path + ".tar" return path def ask_import_package(self): loader = ImportFileSelector(self.window) result = loader.run() return result def ask_properties(self, core): dialogExerciseProperties = GuiSequenceProperties(core, self.window) dialogExerciseProperties.run() def ask_properties_advanced(self, core): dialogExerciseProperties = GuiSequencePropertiesAdvanced(core, self.window) dialogExerciseProperties.run() def ask_settings(self): dialogsettings = Guisettings(self.window) dialogsettings.run() def ask_correction_password(self): dialog_password = GuiPasswordDialog(self.window, "correction") return dialog_password.run() def ask_properties_password(self): dialog_password = GuiPasswordDialog(self.window, "properties") return dialog_password.run() def run(self): gtk.gdk.threads_init() self.window.show() gtk.main() def _update_last_open_files_tab(self): #TODO: move part in controller ? gtkTree = self.builder.get_object("lastopenfilesTreeView") cell = gtk.CellRendererText() treeViewColumn = gtk.TreeViewColumn(_("Path")) treeViewColumn.pack_start(cell, False) treeViewColumn.add_attribute(cell, 'markup', 0) treeViewColumn.set_expand(False) gtkTree.append_column(treeViewColumn) treeStore = gtk.TreeStore(str, str) for obj in config.get("lastopenfiles"): path = obj[0] name = obj[1] if len(obj) >= 2 else path treeStore.append(None, [name, path]) gtkTree.set_model(treeStore) def quit(self): gtk.main_quit() def ask_confirm_quit_without_save(self): dialog = gtk.MessageDialog(self.window, gtk.DIALOG_MODAL, gtk.MESSAGE_INFO, gtk.BUTTONS_YES_NO, _("Do you really quit without save ?")) dialog.set_title(_("Confirm quit")) response = dialog.run() dialog.destroy() return response == gtk.RESPONSE_YES def ask_reset_exercise_content(self): dialogExerciseProperties = GuiResetExercise(self.window) response = dialogExerciseProperties.run() return response def display_message(self, message): dialog_message = GuiMessageDialog(self.window) dialog_message.set_message(_("Information"), message) dialog_message.run() def set_enable_sequence_index_selection(self, state): self.builder.get_object("hscaleSequenceNum").set_sensitive(state) def set_enable_sequence_time_selection(self, state): self.builder.get_object("hscaleSequenceTime").set_sensitive(state) def set_enable_hint(self, state): self.builder.get_object("toolbuttonHint").set_sensitive(state) self.builder.get_object("imagemenuitemHint").set_sensitive(state) def set_enable_reveal_word(self, state): self.builder.get_object("imagemenuitem_reveal_word").set_sensitive(state) def set_enable_reveal_sequence(self, state): self.builder.get_object("imagemenuitem_reveal_sequence").set_sensitive(state) def set_enable_replay_sequence(self, state): self.builder.get_object("toolbuttonReplaySequence").set_sensitive(state) def set_enable_properties(self, state): self.builder.get_object("toolbuttonProperties").set_sensitive(state) self.builder.get_object("imagemenuitemProperties").set_sensitive(state) def set_enable_advanced_properties(self, state): self.builder.get_object("imagemenuitemAdvancedProperties").set_sensitive(state) def set_enable_translation(self, state): self.builder.get_object("toggletoolbuttonShowTranslation").set_sensitive(state) self.builder.get_object("checkmenuitemTranslation").set_sensitive(state) def set_enable_correction(self, state): self.builder.get_object("toolbutton_show_correction").set_sensitive(state) self.builder.get_object("checkmenuitem_correction").set_sensitive(state) def set_active_translation(self, state): self.builder.get_object("toggletoolbuttonShowTranslation").set_active(state) self.builder.get_object("checkmenuitemTranslation").set_active(state) def set_active_correction(self, state): self.disable_correction_event = True self.builder.get_object("toolbutton_show_correction").set_active(state) self.builder.get_object("checkmenuitem_correction").set_active(state) self.disable_correction_event = False def set_enable_save_as(self, state): self.builder.get_object("imagemenuitemSaveAs").set_sensitive(state) def set_enable_save(self, state): self.builder.get_object("imagemenuitemSave").set_sensitive(state) self.builder.get_object("saveButton").set_sensitive(state) def set_enable_export_as_template(self, state): self.builder.get_object("imagemenuitemExportAsTemplate").set_sensitive(state) def set_enable_export_as_package(self, state): self.builder.get_object("imagemenuitemExportAsPackage").set_sensitive(state) def set_enable_speed_selection(self, state): self.builder.get_object("hscaleSpeed").set_sensitive(state) def set_enable_play(self, state): self.builder.get_object("toolbuttonPlay").set_sensitive(state) def set_enable_pause(self, state): self.builder.get_object("toolbuttonPause").set_sensitive(state) def set_enable_next_sequence(self, state): self.builder.get_object("toolbuttonNextSequence").set_sensitive(state) def set_enable_previous_sequence(self, state): self.builder.get_object("toolbuttonPreviousSequence").set_sensitive(state) def set_enable_settings(self, state): self.builder.get_object("imagemenuitemSettings").set_sensitive(state) def set_visible_play(self, state): if state: self.builder.get_object("toolbuttonPlay").show() else: self.builder.get_object("toolbuttonPlay").hide() def set_visible_pause(self, state): if state: self.builder.get_object("toolbuttonPause").show() else: self.builder.get_object("toolbuttonPause").hide() def set_visible_lateral_panel(self, state): if state: self.builder.get_object("lateralPanel").show() else: self.builder.get_object("lateralPanel").hide() def set_visible_translation_panel(self, state): if state: self.builder.get_object("scrolledwindowTranslation").show() else: self.builder.get_object("scrolledwindowTranslation").hide() def set_visible_settings(self, state): if state: self.builder.get_object("imagemenuitemSettings").show() else: self.builder.get_object("imagemenuitemSettings").hide() def set_checked_lateral_panel(self, checked): self.builder.get_object("checkmenuitemLateralPanel").set_active(checked) def ask_new_exercise(self): self.newExerciseDialog = self.builder.get_object("newExerciseDialog") videoChooser = self.builder.get_object("filechooserbuttonVideo") exerciseChooser = self.builder.get_object("filechooserbuttonExercise") translationChooser = self.builder.get_object("filechooserbuttonTranslation") videoChooser.set_filename("None") exerciseChooser.set_filename("None") translationChooser.set_filename("None") self.liststoreLanguage = gtk.ListStore(str, str) languageManager = LanguagesManager() languagesList = languageManager.get_languages_list() for language in languagesList: iter = self.liststoreLanguage.append([language.name, language.id]) if language.id == config.get("default_exercise_language"): currentIter = iter comboboxLanguage = self.builder.get_object("comboboxLanguage") #Clear old values comboboxLanguage.clear() cell = gtk.CellRendererText() comboboxLanguage.set_model(self.liststoreLanguage) comboboxLanguage.pack_start(cell, True) comboboxLanguage.add_attribute(cell, 'text', 0) comboboxLanguage.set_active_iter(currentIter) def set_visible_new_exercise_dialog(self, state): if state: self.newExerciseDialog.show() else: self.newExerciseDialog.hide() def ask_load_exercise(self): loader = OpenFileSelector(self.window) result = loader.run() return result def display_exercice_manager(self, core): dialogExerciseManager = GuiExerciseManager(core, self.window) dialogExerciseManager.run() #---------------------- Now the functions called directly by the gui-------- def on_main_window_delete_event(self, widget, data=None): # returning True avoids it to signal "destroy-event" # returning False makes "destroy-event" be signalled for the window. return self.controller.notify_quit() def on_new_exercise_button_clicked(self, widget, data=None): return self.controller.notify_new_exercise() def on_button_new_exercise_ok_clicked(self, widget, data=None): videoChooser = self.builder.get_object("filechooserbuttonVideo") videoPath = videoChooser.get_filename() exerciseChooser = self.builder.get_object("filechooserbuttonExercise") exercisePath = exerciseChooser.get_filename() translationChooser = self.builder.get_object("filechooserbuttonTranslation") translationPath = translationChooser.get_filename() if videoPath == "None" or videoPath == None: videoPath = "" if exercisePath == "None" or exercisePath == None: exercisePath = "" if translationPath == "None" or translationPath == None: translationPath = "" comboboxLanguage = self.builder.get_object("comboboxLanguage") self.liststoreLanguage.get_iter_first() iter = comboboxLanguage.get_active_iter() langId = self.liststoreLanguage.get_value(iter, 1) self.controller.notify_new_exercise_create(videoPath, exercisePath, translationPath, langId) def on_newExerciseDialog_delete_event(self, widget, data=None): self.controller.notify_new_exercise_cancel() return True #True for stop event propagation def on_button_new_exercise_cancel_clicked(self, widget, data=None): self.controller.notify_new_exercise_cancel() def on_imagemenuitem_export_as_template_activate(self, widget, data=None): self.controller.notify_export_as_template() def on_imagemenuitem_export_as_package_activate(self, widget, data=None): self.controller.notify_export_as_package() def on_imagemenuitem_import_activate(self, widget, data=None): self.controller.notify_import_package() def on_textbuffer_view_changed(self, widget): if self.disable_changed_text_event: return False; buffer = self.typeLabel.get_buffer() oldText = self.setted_typing_area_text newText = buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter()) index = self.typeLabel.get_buffer().props.cursor_position newText = newText.decode("utf-8") oldText = oldText.decode("utf-8") newLength = len(newText) - len(oldText) newString = newText[index-newLength:index] return self.controller.notify_typing(newString) def on_type_view_key_press_event(self, widget, event): keyname = gtk.gdk.keyval_name(event.keyval) state = event.state shift = state & gtk.gdk.SHIFT_MASK control = state & gtk.gdk.CONTROL_MASK return self.controller.notify_key_press(keyname, shift, control) def on_toolbutton_next_sequence_clicked(self, widget, data=None): self.controller.notify_next_sequence() def on_toolbutton_previous_sequence_clicked(self, widget, data=None): self.controller.notify_previous_sequence() def on_toolbutton_replay_sequence_clicked(self, widget, data=None): self.controller.notify_repeat_sequence() def on_adjustment_sequence_num_value_changed(self, widget, data=None): value = int(self.builder.get_object("adjustmentSequenceNum").get_value()) if value != self.setted_sequence_number: self.controller.notify_select_sequence_number(value - 1) def on_adjustment_sequence_time_value_changed(self, widget, data=None): value = int(self.builder.get_object("adjustmentSequenceTime").get_value()) if value != self.setted_position: self.controller.notify_select_sequence_time(value * 100) def on_adjustment_speed_value_changed(self, widget, data=None): value = int(self.builder.get_object("adjustmentSpeed").get_value()) if value != self.setted_speed: self.controller.notify_select_speed(float(value) / 100) def on_toolbutton_hint_clicked(self, widget, data=None): self.controller.notify_hint() def on_toolbutton_play_clicked(self, widget, data=None): self.controller.notify_play() def on_toolbutton_pause_clicked(self, widget, data=None): self.controller.notify_pause() def on_save_button_clicked(self, widget, data=None): self.controller.notify_save() def on_load_button_clicked(self, widget, data=None): self.controller.notify_load() def on_button_save_exercise_ok_clicked(self, widget, data=None): #TODO : use controller saveChooser = self.builder.get_object("filechooserdialogSave") saveChooser.hide() def on_entry_filter_changed(self, widget, data=None): self.controller.notify_filter_change() def on_toggletoolbutton_show_translation_toggled(self, widget, data=None): toggletoolbuttonShowTranslation = self.builder.get_object("toggletoolbuttonShowTranslation") self.controller.notify_toogle_translation(toggletoolbuttonShowTranslation.props.active) def on_checkmenuitem_correction_toggled(self, widget, data=None): if self.disable_correction_event: return False toolbutton_show_correction = self.builder.get_object("toolbutton_show_correction") self.controller.notify_toogle_correction(not toolbutton_show_correction.props.active) def on_toolbutton_show_correction_toggled(self, widget, data=None): if self.disable_correction_event: return False toolbutton_show_correction = self.builder.get_object("toolbutton_show_correction") self.controller.notify_toogle_correction(toolbutton_show_correction.props.active) def on_type_view_move_cursor(self, textview, step_size, count, extend_selection): if step_size == gtk.MOVEMENT_VISUAL_POSITIONS: if count == -1: self.controller.notify_move_cursor("previous_char") elif count == 1: self.controller.notify_move_cursor("next_char") elif step_size == gtk.MOVEMENT_DISPLAY_LINE_ENDS: if count == -1: self.controller.notify_move_cursor("first_word") elif count == 1: self.controller.notify_move_cursor("last_word") elif step_size == gtk.MOVEMENT_WORDS: if count == -1: self.controller.notify_move_cursor("previous_word") elif count == 1: self.controller.notify_move_cursor("next_word") return True def on_type_view_button_release_event(self, widget, data=None): index = self.typeLabel.get_buffer().props.cursor_position return self.controller.notify_move_cursor(index) def on_toolbutton_properties_clicked(self, widget, data=None): self.controller.notify_properties() def on_imagemenuitem_exercice_manager_activate(self, widget, data=None): self.controller.notify_exercise_manager() def on_imagemenuitem_about_activate(self, widget, data=None): self.builder.get_object("aboutdialog").show() def on_imagemenuitem_hint_activate(self, widget, data=None): self.controller.notify_hint() def on_imagemenuitem_reveal_word_activate(self, widget, data=None): self.controller.notify_reveal_word() def on_imagemenuitem_reveal_sequence_activate(self, widget, data=None): self.controller.notify_reveal_sequence() def on_checkmenuitem_lateral_panel_toggled(self, widget, data=None): self.controller.toggle_lateral_panel() def on_checkmenuitem_translation_toggled(self, widget, data=None): checkmenuitemTranslation = self.builder.get_object("checkmenuitemTranslation") self.controller.notify_toogle_translation(checkmenuitemTranslation.props.active) def on_imagemenuitem_properties_activate(self, widget, data=None): self.controller.notify_properties() def on_imagemenuitem_advanced_properties_activate(self, widget, data=None): self.controller.notify_properties_advanced() def on_imagemenuitem_settings_activate(self, widget, data=None): self.controller.notify_settings() def on_imagemenuitem_quit_activate(self, widget, data=None): return self.controller.notify_quit() def on_imagemenuitem_save_as_activate(self, widget, data=None): self.controller.notify_save_as() def on_imagemenuitem_save_activate(self, widget, data=None): self.controller.notify_save() def on_imagemenuitem_open_activate(self, widget, data=None): self.controller.notify_load() def on_imagemenuitem_new_activate(self, widget, data=None): self.controller.notify_new_exercise() def on_filechooserbutton_video_file_set(self, widget, data=None): videoChooser = self.builder.get_object("filechooserbuttonVideo") exerciseChooser = self.builder.get_object("filechooserbuttonExercise") translationChooser = self.builder.get_object("filechooserbuttonTranslation") fileName = videoChooser.get_filename() if fileName and os.path.isfile(fileName): filePath = os.path.dirname(fileName) if not exerciseChooser.get_filename() or not os.path.isfile(exerciseChooser.get_filename()): exerciseChooser.set_current_folder(filePath) if not translationChooser.get_filename() or not os.path.isfile(translationChooser.get_filename()): translationChooser.set_current_folder(filePath) def on_aboutdialog_delete_event(self, widget, data=None): self.builder.get_object("aboutdialog").hide() return True def on_aboutdialog_response(self, widget, data=None): self.builder.get_object("aboutdialog").hide() return True def on_menuitem_reset_progress_activate(self, widget, data=None): self.controller.notify_reset_exercise_content() def on_reset_exercise_content_clicked(self, widget, data=None): self.controller.notify_reset_exercise_content() def on_lastopenfilesTreeView_cursor_changed(self, widget, data=None): gtkTree = self.builder.get_object("lastopenfilesTreeView") gtkSelection = gtkTree.get_selection() (modele, iter) = gtkSelection.get_selected() if iter is not None: path = modele.get(iter, 1)[0] self.controller.notify_load_path(path) EVENT_FILTER = None class FileSelector(gtk.FileChooserDialog): "A normal file selector" def __init__(self, parent, title=None, action=gtk.FILE_CHOOSER_ACTION_OPEN, stockbutton=None): if stockbutton is None: if action == gtk.FILE_CHOOSER_ACTION_OPEN: stockbutton = gtk.STOCK_OPEN elif action == gtk.FILE_CHOOSER_ACTION_SAVE: stockbutton = gtk.STOCK_SAVE gtk.FileChooserDialog.__init__( self, title, parent, action, (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, stockbutton, gtk.RESPONSE_OK) ) self.set_local_only(False) self.set_default_response(gtk.RESPONSE_OK) self.inputsection = None #FIXME unused function def add_widget(self, title, widget): "Adds a widget to the file selection" if self.inputsection == None: self.inputsection = ui.InputSection() #FIXME ui unknow var self.set_extra_widget(self.inputsection) self.inputsection.append_widget(title, widget) """def get_filename(self): "Returns the file URI" uri = self.get_filename() if uri == None: return None else: return urllib.unquote(uri)""" def run(self): "Displays and runs the file selector, returns the filename" self.show_all() if EVENT_FILTER != None: self.window.add_filter(EVENT_FILTER) response = gtk.FileChooserDialog.run(self) filename = self.get_filename() self.destroy() if response == gtk.RESPONSE_OK: return filename else: return None class OpenFileSelector(FileSelector): "A file selector for opening files" def __init__(self, parent): FileSelector.__init__( self, parent, _('Select File to open'), gtk.FILE_CHOOSER_ACTION_OPEN, gtk.STOCK_OPEN ) filter = gtk.FileFilter() filter.set_name(_('Perroquet files')) filter.add_pattern("*.perroquet") self.add_filter(filter) filter = gtk.FileFilter() filter.set_name(_('All files')) filter.add_pattern("*") self.add_filter(filter) class ImportFileSelector(FileSelector): "A file selector for import files" def __init__(self, parent): FileSelector.__init__( self, parent, _('Select package to import'), gtk.FILE_CHOOSER_ACTION_OPEN, gtk.STOCK_OPEN ) filter = gtk.FileFilter() filter.set_name(_('Perroquet package files')) filter.add_pattern("*.tar") self.add_filter(filter) filter = gtk.FileFilter() filter.set_name(_('All files')) filter.add_pattern("*") self.add_filter(filter) class SaveFileSelector(FileSelector): "A file selector for saving files" def __init__(self, parent): FileSelector.__init__( self, parent, _('Select File to Save to'), gtk.FILE_CHOOSER_ACTION_SAVE, gtk.STOCK_SAVE ) filter = gtk.FileFilter() filter.set_name(_('Perroquet files')) filter.add_pattern("*.perroquet") self.add_filter(filter) filter = gtk.FileFilter() filter.set_name(_('All files')) filter.add_pattern("*") self.add_filter(filter) self.set_do_overwrite_confirmation(True) class ExportAsTemplateFileSelector(FileSelector): "A file selector for saving files" def __init__(self, parent): FileSelector.__init__( self, parent, _('Select File to Export to'), gtk.FILE_CHOOSER_ACTION_SAVE, gtk.STOCK_SAVE ) filter = gtk.FileFilter() filter.set_name(_('Perroquet template files')) filter.add_pattern("*.perroquet") self.add_filter(filter) filter = gtk.FileFilter() filter.set_name(_('All files')) filter.add_pattern("*") self.add_filter(filter) self.set_do_overwrite_confirmation(True) class ExportAsPackageFileSelector(FileSelector): "A file selector for saving files" def __init__(self, parent): FileSelector.__init__( self, parent, _('Select File to Export to'), gtk.FILE_CHOOSER_ACTION_SAVE, gtk.STOCK_SAVE ) filter = gtk.FileFilter() filter.set_name(_('Perroquet package files')) filter.add_pattern("*.tar") self.add_filter(filter) filter = gtk.FileFilter() filter.set_name(_('All files')) filter.add_pattern("*") self.add_filter(filter) self.set_do_overwrite_confirmation(True) perroquet-1.1.1.orig/perroquetlib/gui/__init__.py0000644000175000017500000000145011561344350022311 0ustar georgeskgeorgesk# -*- coding: utf-8 -*- # Copyright (C) 2009-2010 Frédéric Bertolus. # Copyright (C) 2009-2010 Matthieu Bizien. # # This file is part of Perroquet. # # Perroquet 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 3 of the License, or # (at your option) any later version. # # Perroquet 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 Perroquet. If not, see . """The gui of perroquet. """ perroquet-1.1.1.orig/perroquetlib/gui/gui_reset_exercise.py0000644000175000017500000000345511561344350024436 0ustar georgeskgeorgesk# -*- coding: utf-8 -*- # Copyright (C) 2009-2010 Frédéric Bertolus. # # This file is part of Perroquet. # # Perroquet 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 3 of the License, or # (at your option) any later version. # # Perroquet 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 Perroquet. If not, see . import gettext import gtk from perroquetlib.config import config _ = gettext.gettext class GuiResetExercise: def __init__(self, parent): self.config = config self.parent = parent self.builder = gtk.Builder() self.builder.set_translation_domain("perroquet") self.builder.add_from_file(self.config.get("ui_reset_path")) self.builder.connect_signals(self) self.dialog = self.builder.get_object("dialogReset") self.pagePaths = self.builder.get_object("pagePaths") self.pageSequences = self.builder.get_object("pageSequences") self.dialog.set_modal(True) self.dialog.set_transient_for(self.parent) self.result = False def run(self): self.dialog.run() self.dialog.destroy() return self.result def on_button_reset_ok_clicked(self, widget, data=None): self.result = True self.dialog.response(gtk.RESPONSE_OK) def on_button_reset_cancel_clicked(self, widget, data=None): self.result = False self.dialog.response(gtk.RESPONSE_CANCEL) perroquet-1.1.1.orig/perroquetlib/gui/gui_message_dialog.py0000644000175000017500000000414511561344350024365 0ustar georgeskgeorgesk# -*- coding: utf-8 -*- # Copyright (C) 2009-2010 Frédéric Bertolus. # # This file is part of Perroquet. # # Perroquet 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 3 of the License, or # (at your option) any later version. # # Perroquet 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 Perroquet. If not, see . import gettext import gtk from perroquetlib.config import config _ = gettext.gettext class GuiMessageDialog: def __init__(self, parent): self.config = config self.parent = parent self.builder = gtk.Builder() self.builder.set_translation_domain("perroquet") self.builder.add_from_file(self.config.get("ui_message_path")) self.builder.connect_signals(self) self.dialog = self.builder.get_object("dialog_message") self.dialog.set_modal(True) self.dialog.set_transient_for(self.parent) self.result = False def set_message(self, title, message): self.builder.get_object("label_message").set_text(message) self.dialog.set_title(title) def run(self): self.dialog.run() self.dialog.destroy() def on_button_reset_ok_clicked(self, widget, data=None): self.dialog.response(gtk.RESPONSE_OK) def on_button_reset_cancel_clicked(self, widget, data=None): self.result = None self.dialog.response(gtk.RESPONSE_CANCEL) def on_entry_password_activate(self, widget, data=None): self.result = self.builder.get_object("entry_password").get_text() self.dialog.response(gtk.RESPONSE_OK) def on_dialog_password_delete_event(self, widget, data=None): self.result = None self.dialog.response(gtk.RESPONSE_CANCEL) return True perroquet-1.1.1.orig/perroquetlib/gui/gui_sequence_properties_advanced.py0000644000175000017500000004365711561344350027346 0ustar georgeskgeorgesk# -*- coding: utf-8 -*- # Copyright (C) 2009-2010 Frédéric Bertolus. # # This file is part of Perroquet. # # Perroquet 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 3 of the License, or # (at your option) any later version. # # Perroquet 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 Perroquet. If not, see . import gettext import os import gtk from perroquetlib.config import config from perroquetlib.model.exercise import Exercise from perroquetlib.model.languages_manager import LanguagesManager from perroquetlib.model.sub_exercise import SubExercise _ = gettext.gettext class GuiSequencePropertiesAdvanced: def __init__(self, core, parent): self.core = core self.config = config self.parent = parent self.builder = gtk.Builder() self.builder.set_translation_domain("perroquet") self.builder.add_from_file(self.config.get("ui_sequence_properties_advanced_path")) self.builder.connect_signals(self) self.dialog = self.builder.get_object("dialogExercisePropertiesAdvanced") self.treeviewPathsList = self.builder.get_object("treeviewPathsList") self.dialog.set_modal(True) self.dialog.set_transient_for(self.parent) self.iterPath = None def run(self): self.load() self.dialog.run() self.dialog.destroy() def load(self): exercise = self.core.get_exercise() if len(exercise.subExercisesList) > 0: self.__load_path(exercise.subExercisesList[0].get_video_path(), exercise.subExercisesList[0].get_exercise_path(), exercise.subExercisesList[0].get_translation_path()) else: self._Load("", "", "") self.pathListStore = gtk.ListStore(str, str, str, str) for subExercise in exercise.subExercisesList: name = os.path.basename(subExercise.get_video_path()) self.pathListStore.append([name, subExercise.get_video_path(), subExercise.get_exercise_path(), subExercise.get_translation_path()]) cell = gtk.CellRendererText() treeviewcolumnPath = gtk.TreeViewColumn(_("Path")) treeviewcolumnPath.pack_start(cell, True) treeviewcolumnPath.add_attribute(cell, 'markup', 0) treeviewcolumnPath.set_expand(True) columns = self.treeviewPathsList.get_columns() for column in columns: self.treeviewPathsList.remove_column(column) self.treeviewPathsList.append_column(treeviewcolumnPath) self.treeviewPathsList.set_model(self.pathListStore) self.treeviewSelectionPathsList = self.treeviewPathsList.get_selection() self.iterPath = self.pathListStore.get_iter_first() self.treeviewSelectionPathsList.select_iter(self.iterPath) checkbuttonRepeatAfterComplete = self.builder.get_object("checkbuttonRepeatAfterComplete") checkbuttonRepeatAfterComplete.set_active(self.core.get_exercise().get_repeat_after_completed()) checkbuttonUseDynamicCorrection = self.builder.get_object("checkbuttonUseDynamicCorrection") checkbuttonUseDynamicCorrection.set_active(self.core.get_exercise().is_use_dynamic_correction()) checkbuttonRandomOrder = self.builder.get_object("checkbuttonRandomOrder") checkbuttonRandomOrder.set_active(self.core.get_exercise().is_random_order()) checkbutton_disable_help = self.builder.get_object("checkbutton_disable_help") checkbutton_disable_help.set_active(self.core.get_exercise().is_lock_help()) self.liststoreLanguage = gtk.ListStore(str, str) languageManager = LanguagesManager() languagesList = languageManager.get_languages_list() currentLangId = self.core.get_exercise().get_language_id() for language in languagesList: iter = self.liststoreLanguage.append([language.name, language.id]) if language.id == currentLangId: currentIter = iter comboboxLanguage = self.builder.get_object("comboboxLanguage") cell = gtk.CellRendererText() comboboxLanguage.set_model(self.liststoreLanguage) comboboxLanguage.pack_start(cell, True) comboboxLanguage.add_attribute(cell, 'text', 0) comboboxLanguage.set_active_iter(currentIter) adjustmentTimeBetweenSequence = self.builder.get_object("adjustmentTimeBetweenSequence") adjustmentTimeBetweenSequence.set_value(self.core.get_exercise().get_time_between_sequence()) adjustmentMaximumSequenceTime = self.builder.get_object("adjustmentMaximumSequenceTime") adjustmentMaximumSequenceTime.set_value(self.core.get_exercise().get_max_sequence_length()) adjustmentTimeBeforeSequence = self.builder.get_object("adjustmentTimeBeforeSequence") adjustmentTimeBeforeSequence.set_value(self.core.get_exercise().get_play_margin_before()) adjustmentTimeAfterSequence = self.builder.get_object("adjustmentTimeAfterSequence") adjustmentTimeAfterSequence.set_value(self.core.get_exercise().get_play_margin_after()) entryExerciseName = self.builder.get_object("entryExerciseName") if self.core.get_exercise().get_name(): entryExerciseName.set_text(self.core.get_exercise().get_name()) else: entryExerciseName.set_text("") entryRepeatCountLimit = self.builder.get_object("entryRepeatCountLimit") entryRepeatCountLimit.set_text(str(self.core.get_exercise().get_repeat_count_limit_by_sequence())) #Locks checkbutton_lock_properties = self.builder.get_object("checkbutton_lock_properties") checkbutton_lock_properties.set_active(self.core.get_exercise().is_lock_properties()) checkbutton_lock_correction = self.builder.get_object("checkbutton_lock_correction") checkbutton_lock_correction.set_active(self.core.get_exercise().is_lock_correction()) self._update_path_buttons() def __load_path(self, videoPath, exercisePath, translationPath): if videoPath == "": videoPath = "None" if exercisePath == "": exercisePath = "None" if translationPath == "": translationPath = "None" videoChooser = self.builder.get_object("filechooserbuttonVideoProp") exerciseChooser = self.builder.get_object("filechooserbuttonExerciseProp") translationChooser = self.builder.get_object("filechooserbuttonTranslationProp") videoChooser.set_filename(videoPath) exerciseChooser.set_filename(exercisePath) translationChooser.set_filename(translationPath) if videoPath and os.path.isfile(videoPath): filePath = os.path.dirname(videoPath) if not exercisePath or not os.path.isfile(exercisePath): exerciseChooser.set_current_folder(filePath) if not translationPath or not os.path.isfile(translationPath): translationChooser.set_current_folder(filePath) def on_treeview_paths_list_cursor_changed(self, widget, data=None): (modele, iter) = self.treeviewSelectionPathsList.get_selected() self.__store_path_changes() self.iterPath = iter self._update_path_buttons() if iter == None: return videoPath, exercisePath, translationPath = modele.get(iter, 1, 2, 3) self.__load_path(videoPath, exercisePath, translationPath) def _update_path_buttons(self): if self.iterPath == None: buttonRemovePath = self.builder.get_object("buttonRemovePath") buttonRemovePath.set_sensitive(False) buttonUpPath = self.builder.get_object("buttonUpPath") buttonUpPath.set_sensitive(False) buttonDownPath = self.builder.get_object("buttonDownPath") buttonDownPath.set_sensitive(False) else: buttonRemovePath = self.builder.get_object("buttonRemovePath") buttonRemovePath.set_sensitive(True) buttonUpPath = self.builder.get_object("buttonUpPath") if self.previous_iter(self.pathListStore, self.iterPath) == None: buttonUpPath.set_sensitive(False) else: buttonUpPath.set_sensitive(True) buttonDownPath = self.builder.get_object("buttonDownPath") if self.pathListStore.iter_next(self.iterPath) == None: buttonDownPath.set_sensitive(False) else: buttonDownPath.set_sensitive(True) def on_button_exercise_prop_ok_clicked(self, widget, data=None): self.__store_path_changes() checkbuttonRepeatAfterComplete = self.builder.get_object("checkbuttonRepeatAfterComplete") self.core.get_exercise().set_repeat_after_completed(checkbuttonRepeatAfterComplete.get_active()) checkbuttonUseDynamicCorrection = self.builder.get_object("checkbuttonUseDynamicCorrection") self.core.get_exercise().set_use_dynamic_correction(checkbuttonUseDynamicCorrection.get_active()) checkbuttonRandomOrder = self.builder.get_object("checkbuttonRandomOrder") self.core.get_exercise().set_random_order(checkbuttonRandomOrder.get_active()) comboboxLanguage = self.builder.get_object("comboboxLanguage") self.liststoreLanguage.get_iter_first() iter = comboboxLanguage.get_active_iter() langId = self.liststoreLanguage.get_value(iter, 1) self.core.get_exercise().set_language_id(langId) adjustmentTimeBetweenSequence = self.builder.get_object("adjustmentTimeBetweenSequence") self.core.get_exercise().set_time_between_sequence(adjustmentTimeBetweenSequence.get_value()) adjustmentMaximumSequenceTime = self.builder.get_object("adjustmentMaximumSequenceTime") self.core.get_exercise().set_max_sequence_length(adjustmentMaximumSequenceTime.get_value()) adjustmentTimeBeforeSequence = self.builder.get_object("adjustmentTimeBeforeSequence") self.core.get_exercise().set_play_margin_before(int(adjustmentTimeBeforeSequence.get_value())) adjustmentTimeAfterSequence = self.builder.get_object("adjustmentTimeAfterSequence") self.core.get_exercise().set_play_margin_after(int(adjustmentTimeAfterSequence.get_value())) entryExerciseName = self.builder.get_object("entryExerciseName") self.core.get_exercise().set_name(entryExerciseName.get_text()) entryRepeatCountLimit = self.builder.get_object("entryRepeatCountLimit") self.core.get_exercise().set_repeat_count_limit_by_sequence(int(entryRepeatCountLimit.get_text())) entryRepeatCountLimit.set_text(str(self.core.get_exercise().get_repeat_count_limit_by_sequence())) if self.core.get_exercise().get_repeat_count_limit_by_sequence() == 0: self.core.get_exercise().clear_sequence_repeat_count() #Locks checkbutton_disable_help = self.builder.get_object("checkbutton_disable_help") self.core.get_exercise().set_lock_help(checkbutton_disable_help.get_active()) checkbutton_lock_properties = self.builder.get_object("checkbutton_lock_properties") lock_properties = checkbutton_lock_properties.get_active() entry_lock_properties = self.builder.get_object("entry_lock_properties") lock_properties_password = entry_lock_properties.get_text() if len(lock_properties_password) == 0: lock_properties_password = None if lock_properties != self.core.get_exercise().is_lock_properties() or lock_properties_password is not None: self.core.get_exercise().set_lock_properties(lock_properties, lock_properties_password) checkbutton_lock_correction = self.builder.get_object("checkbutton_lock_correction") lock_correction = checkbutton_lock_correction.get_active() entry_lock_correction = self.builder.get_object("entry_lock_correction") lock_correction_password = entry_lock_correction.get_text() if len(lock_correction_password) == 0: lock_correction_password = None if lock_correction != self.core.get_exercise().is_lock_correction() or lock_correction_password is not None: self.core.get_exercise().set_lock_correction(lock_correction, lock_correction_password) # Update paths if len(self.pathListStore) != len(self.core.get_exercise().subExercisesList): self.core.get_exercise().subExercisesList = [] for subPath in self.pathListStore: self.core.get_exercise().subExercisesList.append(SubExercise(self.core.get_exercise())) for i, subPath in enumerate(self.pathListStore): self.core.get_exercise().subExercisesList[i].set_video_path(subPath[1]) self.core.get_exercise().subExercisesList[i].set_exercise_path(subPath[2]) self.core.get_exercise().subExercisesList[i].set_translation_path(subPath[3]) self.core.update_properties() self.core.set_can_save(True) self.dialog.response(gtk.RESPONSE_OK) def on_button_exercise_prop_cancel_clicked(self, widget, data=None): self.dialog.response(gtk.RESPONSE_CANCEL) def __store_path_changes(self): if self.iterPath == None: return videoChooser = self.builder.get_object("filechooserbuttonVideoProp") videoPath = videoChooser.get_filename() exerciseChooser = self.builder.get_object("filechooserbuttonExerciseProp") exercisePath = exerciseChooser.get_filename() translationChooser = self.builder.get_object("filechooserbuttonTranslationProp") translationPath = translationChooser.get_filename() if videoPath == "None" or videoPath == None: videoPath = "" if exercisePath == "None" or exercisePath == None: exercisePath = "" if translationPath == "None" or translationPath == None: translationPath = "" if self.iterPath == None: return self.iterPath self.pathListStore.set_value(self.iterPath, 0, os.path.basename(videoPath)) self.pathListStore.set_value(self.iterPath, 1, videoPath) self.pathListStore.set_value(self.iterPath, 2, exercisePath) self.pathListStore.set_value(self.iterPath, 3, translationPath) def on_filechooserbutton_video_prop_file_set(self, widget, data=None): videoChooser = self.builder.get_object("filechooserbuttonVideoProp") exerciseChooser = self.builder.get_object("filechooserbuttonExerciseProp") translationChooser = self.builder.get_object("filechooserbuttonTranslationProp") fileName = videoChooser.get_filename() if fileName and os.path.isfile(fileName): filePath = os.path.dirname(fileName) if not exerciseChooser.get_filename() or not os.path.isfile(exerciseChooser.get_filename()): exerciseChooser.set_current_folder(filePath) if not translationChooser.get_filename() or not os.path.isfile(translationChooser.get_filename()): translationChooser.set_current_folder(filePath) self.__store_path_changes() def previous_iter(self, model, iter): if not iter: return None path = model.get_string_from_iter(iter) if not path: return None prow = int(path) - 1 if prow == -1: return None prev = model.get_iter_from_string("%d" % prow) return prev def on_button_down_path_clicked(self, widget, data=None): self.pathListStore.move_after(self.iterPath, self.pathListStore.iter_next(self.iterPath)) self._update_path_buttons() def on_button_up_path_clicked(self, widget, data=None): self.pathListStore.move_before(self.iterPath, self.previous_iter(self.pathListStore, self.iterPath)) self._update_path_buttons() def on_button_add_path_clicked(self, widget, data=None): self.__store_path_changes() if self.iterPath is None: self.iterPath = self.pathListStore.get_iter_first() while self.pathListStore.iter_next(self.iterPath) is not None: self.iterPath = self.pathListStore.iter_next(self.iterPath) iter = self.pathListStore.insert_after(self.iterPath, [self.pathListStore.get_value(self.iterPath, 0), self.pathListStore.get_value(self.iterPath, 1), self.pathListStore.get_value(self.iterPath, 2), self.pathListStore.get_value(self.iterPath, 3)]) self.iterPath = None self.treeviewSelectionPathsList.select_iter(iter) def on_button_remove_path_clicked(self, widget, data=None): self.pathListStore.remove(self.iterPath) self.iterPath = None self._update_path_buttons() def on_button_defaut_time_between_sequences_clicked(self, widget, data=None): adjustmentTimeBetweenSequence = self.builder.get_object("adjustmentTimeBetweenSequence") exercice = Exercise() adjustmentTimeBetweenSequence.set_value(exercice.get_time_between_sequence()) def on_button_defaut_maximum_sequence_time_clicked(self, widget, data=None): adjustmentMaximumSequenceTime = self.builder.get_object("adjustmentMaximumSequenceTime") exercice = Exercise() adjustmentMaximumSequenceTime.set_value(exercice.get_max_sequence_length()) def on_button_defaut_time_before_sequence_clicked(self, widget, data=None): adjustmentTimeBeforeSequence = self.builder.get_object("adjustmentTimeBeforeSequence") exercice = Exercise() adjustmentTimeBeforeSequence.set_value(exercice.get_play_margin_before()) def on_button_defaut_time_after_sequence_clicked(self, widget, data=None): adjustmentTimeAfterSequence = self.builder.get_object("adjustmentTimeAfterSequence") exercice = Exercise() adjustmentTimeAfterSequence.set_value(exercice.get_play_margin_after()) perroquet-1.1.1.orig/perroquetlib/gui/gui_password_dialog.py0000644000175000017500000000510111561344350024574 0ustar georgeskgeorgesk# -*- coding: utf-8 -*- # Copyright (C) 2009-2010 Frédéric Bertolus. # # This file is part of Perroquet. # # Perroquet 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 3 of the License, or # (at your option) any later version. # # Perroquet 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 Perroquet. If not, see . import gettext import gtk from perroquetlib.config import config _ = gettext.gettext class GuiPasswordDialog: def __init__(self, parent, type): self.config = config self.parent = parent self.builder = gtk.Builder() self.builder.set_translation_domain("perroquet") self.builder.add_from_file(self.config.get("ui_password_path")) self.builder.connect_signals(self) self.dialog = self.builder.get_object("dialog_password") self.pagePaths = self.builder.get_object("pagePaths") self.pageSequences = self.builder.get_object("pageSequences") self.dialog.set_modal(True) self.dialog.set_transient_for(self.parent) self.result = False if type == "correction": self.builder.get_object("labelLabel").set_text(_("Correction password :")) self.dialog.set_title("Password for unlock correction") elif type == "properties": self.builder.get_object("labelLabel").set_text(_("Properties password :")) self.dialog.set_title("Password for unlock properties") def run(self): self.dialog.run() self.dialog.destroy() return self.result def on_button_reset_ok_clicked(self, widget, data=None): self.result = self.builder.get_object("entry_password").get_text() self.dialog.response(gtk.RESPONSE_OK) def on_button_reset_cancel_clicked(self, widget, data=None): self.result = None self.dialog.response(gtk.RESPONSE_CANCEL) def on_entry_password_activate(self, widget, data=None): self.result = self.builder.get_object("entry_password").get_text() self.dialog.response(gtk.RESPONSE_OK) def on_dialog_password_delete_event(self, widget, data=None): self.result = None self.dialog.response(gtk.RESPONSE_CANCEL) return True perroquet-1.1.1.orig/perroquetlib/gui/gui_exercise_controller.py0000644000175000017500000002303211561344350025470 0ustar georgeskgeorgesk# -*- coding: utf-8 -*- # Copyright (C) 2009-2010 Frédéric Bertolus. # Copyright (C) 2009-2010 Matthieu Bizien. # # This file is part of Perroquet. # # Perroquet 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 3 of the License, or # (at your option) any later version. # # Perroquet 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 Perroquet. If not, see . class GuiExerciseController: def __init__(self, gui_controller, core, gui): self.gui = gui self.core = core self.gui_controller = gui_controller self.current_index = 0 self.current_word_index = -1 self.current_pos_index = 0 self.word_index_map = [] self.word_pos_map = [] self.cursor_position = 0 self.formatted_text = [] self.style_tag_list = [] self._genererate_style_tag_list() def set_sequence(self, sequence): self.sequence = sequence if self.gui_controller.is_correction_visible(): self._generate_formatted_corrected_exercise_text(sequence) elif self.core.get_exercise().is_use_dynamic_correction(): self._generate_formatted_dynamic_correction_exercise_text(sequence) else: self._generate_formatted_simple_exercise_text(sequence) self.gui.set_typing_area_text(self.formatted_text) self.gui.set_typing_area_cursor_position(self.cursor_position) self.gui.set_focus_typing_area() def repaint(self): """Draw the same sequence""" self.set_sequence(self.sequence); def _generate_formatted_dynamic_correction_exercise_text(self, sequence): self._clear() pos = 0 for i, symbol in enumerate(sequence.get_symbols()): pos += len(symbol) self._add_symbol(symbol) if i < len(sequence.get_words()): if sequence.get_active_word_index() == i: self.cursor_position = pos if sequence.get_words()[i].is_empty(): self._add_word(" ", 0, is_empty=True) pos += 1 elif sequence.get_words()[i].is_valid(): self._add_word(sequence.get_words()[i].get_valid(lower=False), 0, isFound=True) pos += len(sequence.get_words()[i].get_valid(lower=False)) else: self._add_word(sequence.get_words()[i].get_text(), sequence.get_words()[i].get_score()) pos += len(sequence.get_words()[i].get_text()) self.word_index_map.append(self.current_word_index) self.word_pos_map.append(self.current_pos_index) self.cursor_position = self.cursor_position + sequence.get_active_word().get_pos() def _generate_formatted_simple_exercise_text(self, sequence): self._clear() pos = 0 for i, symbol in enumerate(sequence.get_symbols()): pos += len(symbol) self._add_symbol(symbol) if i < len(sequence.get_words()): if sequence.get_active_word_index() == i: self.cursor_position = pos if sequence.get_words()[i].is_empty(): self._add_word(" ", 0, is_empty=True) pos += 1 else: self._add_word(sequence.get_words()[i].get_text(), 0) pos += len(sequence.get_words()[i].get_text()) self.word_index_map.append(self.current_word_index) self.word_pos_map.append(self.current_pos_index) self.cursor_position = self.cursor_position + sequence.get_active_word().get_pos() def _generate_formatted_corrected_exercise_text(self, sequence): self._clear() pos = 0 for i, symbol in enumerate(sequence.get_symbols()): pos += len(symbol) self._add_symbol(symbol) if i < len(sequence.get_words()): if sequence.get_active_word_index() == i: self.cursor_position = pos if sequence.get_words()[i].is_empty(): self._add_word(sequence.get_words()[i].get_valid(lower=False), 0) pos += 1 elif sequence.get_words()[i].is_valid(): self._add_word(sequence.get_words()[i].get_valid(lower=False), 0, isFound=True) pos += len(sequence.get_words()[i].get_text()) else: self._add_word(sequence.get_words()[i].get_text(), sequence.get_words()[i].get_score(), through=True) self._add_word(sequence.get_words()[i].get_valid(lower=False), 0, isFound=True) pos += len(sequence.get_words()[i].get_text()) self.word_index_map.append(self.current_word_index) self.word_pos_map.append(self.current_pos_index) self.cursor_position = self.cursor_position + sequence.get_active_word().get_pos() def _add_word(self, word, score, isFound=False, is_empty=False, through=False): score250 = int(score * 250) #score between -250 and 250 if through: tagName = "word_througt" elif is_empty: tagName = "word_empty" elif isFound: tagName = "word_found" elif score > 0: tagName = "word_near" + str(score250) else: tagName = "word_to_found" + str(score250) self.formatted_text.append((word, tagName)) self.current_word_index += 1 self.current_pos_index = 0 for _ in range(self.current_index, self.current_index + len(word)): self.word_index_map.append(self.current_word_index) self.word_pos_map.append(self.current_pos_index) self.current_pos_index += 1 self.current_index += len(word) def _add_symbol(self, symbol): if len(symbol) == 0: return self.formatted_text.append((symbol, "symbol")) for _ in range(self.current_index, self.current_index + len(symbol)): self.word_index_map.append(self.current_word_index) self.word_pos_map.append(self.current_pos_index) self.current_index += len(symbol) def _clear(self): self.current_index = 0 self.current_word_index = -1 self.current_pos_index = 0 self.word_index_map = [] self.word_pos_map = [] self.cursor_position = 0 self.formatted_text = [] def _genererate_style_tag_list(self): color_not_found = (0, 0, 80) color_found = (10, 150, 10) def get_bcolor_not_found(score250): return _compute_color_between( (150, 210, 250), (255, 100, 100), float(score250) / 250.0) def get_bcolor_near(score250): return _compute_color_between( (150, 210, 250), (150, 255, 100), float(score250) / 250.0) def _compute_color_between(colorFrom, colorTo, coeff): (redFrom, greenFrom, blueFrom) = colorFrom (redTo, greenTo, blueTo) = colorTo red = int((1-coeff) * redFrom + coeff * redTo) green = int((1-coeff) * greenFrom + coeff * greenTo) blue = int((1-coeff) * blueFrom + coeff * blueTo) return (red, green, blue) self.style_tag_list.append(("symbol", 18.0, None, None, False)) self.style_tag_list.append(("word_empty", 18.0, color_not_found, get_bcolor_not_found(0), False)) self.style_tag_list.append(("word_found", 18.0, color_found, None, False)) self.style_tag_list.append(("word_througt", 18.0, get_bcolor_not_found(250), None, True)) for score250 in xrange(251): self.style_tag_list.append(("word_to_found" + str(-score250), 18.0, color_not_found, get_bcolor_not_found(score250), False)) self.style_tag_list.append(("word_near" + str(score250), 18.0, color_not_found, get_bcolor_near(score250), False)) self.gui.set_typing_area_style_list(self.style_tag_list) def notify_move_cursor(self, movement): """Documentation""" if movement == "previous_char": self.core.previous_char() elif movement == "next_char": self.core.next_char() elif movement == "first_word": self.core.first_word() elif movement == "last_word": self.core.last_word() elif movement == "previous_word": self.core.previous_word() elif movement == "next_word": self.core.next_word() else: word_index = self.word_index_map[movement] word_index_position = self.word_pos_map[movement] if word_index == -1: word_index = 0 self.core.select_sequence_word(word_index, word_index_position) perroquet-1.1.1.orig/perroquetlib/gui/gui_exercise_manager.py0000644000175000017500000005026211561344350024724 0ustar georgeskgeorgesk# -*- coding: utf-8 -*- # Copyright (C) 2009-2010 Frédéric Bertolus. # # This file is part of Perroquet. # # Perroquet 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 3 of the License, or # (at your option) any later version. # # Perroquet 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 Perroquet. If not, see . import gettext import logging import textwrap import thread import time import gtk from perroquetlib.config import config from perroquetlib.debug import defaultLoggingHandler, defaultLoggingLevel from perroquetlib.repository.exercise_repository_manager import ExerciseRepositoryManager _ = gettext.gettext class GuiExerciseManager: def __init__(self, core, parent): self.logger = logging.Logger("GuiExerciseManager") self.logger.setLevel(defaultLoggingLevel) self.logger.addHandler(defaultLoggingHandler) self.core = core self.config = config self.parent = parent self.builder = gtk.Builder() self.builder.set_translation_domain("perroquet") self.builder.add_from_file(self.config.get("ui_exercise_manager_path")) self.builder.connect_signals(self) self.dialog = self.builder.get_object("dialogExerciseManager") self.dialogDetails = self.builder.get_object("dialogDetails") self.labelStatus = self.builder.get_object("labelStatus") self.treeviewExercises = self.builder.get_object("treeviewExercises") self.treeviewRepositories = self.builder.get_object("treeviewRepositories") self.buttonAction = self.builder.get_object("buttonAction") self.repositoryList = None self.treeviewDetails = self.builder.get_object("treeviewDetails") self.selectedExo = None self.dialog.set_modal(True) self.dialog.set_transient_for(self.parent) #self.dialog.set_functions (gtk.gdk.FUNC_RESIZE | gtk.gdk.FUNC_MOVE | gtk.gdk.FUNC_MINIMIZE |gtk.gdk.FUNC_MAXIMIZE) checkbuttonTreeViewMode = self.builder.get_object("checkbuttonTreeViewMode") checkbuttonTreeViewMode.props.active = (self.config.get("repositorymanager.displayonlyexercises") != 1) def run(self): self.load() self.dialog.run() self.dialog.destroy() def load(self): self.play_thread_id = thread.start_new_thread(self.update_exercise_list_thread, ()) # Use the following line to debug crash in the thread #self.update_exercise_list_thread() def update_exercise_list_thread(self): self.labelStatus.set_text(_("Updating repositories...")) self.repositoryManager = ExerciseRepositoryManager() self._update_repository_tree_view() self.repositoryList = self.repositoryManager.get_exercise_repository_list() self._update_exercise_tree_view() self._update_details_tree_view() def _update_exercise_tree_view(self): self.treeStoreExercices = gtk.TreeStore(str, str, str, str, bool, object, str) displayOnlyExercises = (self.config.get("repositorymanager.displayonlyexercises") == 1) self.availableExoCount = 0 self.installedExoCount = 0 if not self.repositoryList: self.update_exercise_list_thread() for repo in self.repositoryList: if not displayOnlyExercises: if repo.get_type() == "local": type = _("Local repository") elif repo.get_type() == "distant": type = _("Distant repository") elif repo.get_type() == "offline": type = _("Offline repository") elif repo.get_type() == "orphan": type = _("Orphan repository") else: type = _("") desc = repo.get_description() desc = "" + desc + "" type = "" + type + "" name = "" + repo.get_name() + "" iterRepo = self.treeStoreExercices.append(None, [name, type, desc, "", False, None, ""]) else: iterRepo = None for group in repo.get_groups(): if not displayOnlyExercises: desc = group.get_description() desc = "" + desc + "" name = "" + group.get_name() + "" iterGroup = self.treeStoreExercices.append(iterRepo, [name, _("Group"), desc, "", False, None, ""]) else: iterGroup = None for exo in group.get_exercises(): descList = textwrap.wrap(exo.get_description(), 40) desc = "" for i, line in enumerate(descList): desc += line if i < len(descList)-1: desc += "\n" desc = "" + desc + "" if exo.get_state() == "installed": installStatus = _("Installed") self.installedExoCount += 1 elif exo.get_state() == "available": installStatus = _("Available") self.availableExoCount += 1 elif exo.get_state() == "used": installStatus = _("Used") self.installedExoCount += 1 elif exo.get_state() == "done": installStatus = _("Done") self.installedExoCount += 1 name = "" + exo.get_name() + "" if exo.get_words_count() == 0: words = "-" else: words = str(exo.get_words_count()) iter = self.treeStoreExercices.append(iterGroup, [name, _("Exercise"), desc, installStatus, True, exo, words]) exo.set_state_change_callback(self.exercice_state_change_listener, iter) cell = gtk.CellRendererText() treeviewcolumnName = gtk.TreeViewColumn(_("Name")) treeviewcolumnName.pack_start(cell, False) treeviewcolumnName.add_attribute(cell, 'markup', 0) treeviewcolumnName.set_expand(False) treeviewcolumnType = gtk.TreeViewColumn(_("Type"), ) treeviewcolumnType.pack_start(cell, False) treeviewcolumnType.add_attribute(cell, 'markup', 1) treeviewcolumnType.set_expand(False) treeviewcolumnDescription = gtk.TreeViewColumn(_("Description")) treeviewcolumnDescription.pack_start(cell, True) treeviewcolumnDescription.add_attribute(cell, 'markup', 2) treeviewcolumnDescription.set_expand(True) treeviewcolumnStatus = gtk.TreeViewColumn(_("Status")) treeviewcolumnStatus.pack_start(cell, False) treeviewcolumnStatus.add_attribute(cell, 'markup', 3) treeviewcolumnStatus.set_expand(False) treeviewcolumnWordsCount = gtk.TreeViewColumn(_("Words count")) treeviewcolumnWordsCount.pack_start(cell, False) treeviewcolumnWordsCount.add_attribute(cell, 'markup', 6) treeviewcolumnWordsCount.set_expand(False) columns = self.treeviewExercises.get_columns() for column in columns: self.treeviewExercises.remove_column(column) if not displayOnlyExercises: self.treeviewExercises.append_column(treeviewcolumnType) self.treeviewExercises.append_column(treeviewcolumnName) self.treeviewExercises.append_column(treeviewcolumnDescription) if displayOnlyExercises: self.treeviewExercises.append_column(treeviewcolumnWordsCount) self.treeviewExercises.append_column(treeviewcolumnStatus) self.treeviewExercises.set_expander_column(treeviewcolumnName) self.treeviewExercises.set_model(self.treeStoreExercices) self.treeselectionExercises = self.treeviewExercises.get_selection() self._update_status() def _update_repository_tree_view(self): self.treeStoreRepository = gtk.TreeStore(str, str) personnalRepoList = self.repositoryManager.get_personal_exercise_repository_list() for repo in personnalRepoList: #self.treeStoreRepository.append(None,[""+repo+"", repo]) self.treeStoreRepository.append(None, [repo, repo]) cell = gtk.CellRendererText() treeviewcolumnPath = gtk.TreeViewColumn(_("Path")) treeviewcolumnPath.pack_start(cell, True) treeviewcolumnPath.add_attribute(cell, 'markup', 0) treeviewcolumnPath.set_expand(True) columns = self.treeviewRepositories.get_columns() for column in columns: self.treeviewRepositories.remove_column(column) self.treeviewRepositories.append_column(treeviewcolumnPath) self.treeviewRepositories.set_model(self.treeStoreRepository) self.treeselectionRepositories = self.treeviewRepositories.get_selection() def _update_details_tree_view(self): self.logger.debug("_update_details_tree_view") self.treeStoreDetails = gtk.TreeStore(str, str) #Clean old colomns columns = self.treeviewDetails.get_columns() for column in columns: self.treeviewDetails.remove_column(column) if self.selectedExo: exo = self.selectedExo if exo.get_state() == "installed": installStatus = _("Installed") elif exo.get_state() == "available": installStatus = _("Available") elif exo.get_state() == "used": installStatus = _("Used") elif exo.get_state() == "done": installStatus = _("Done") self.treeStoreDetails.append(None, ["" + _("Name") + "", exo.get_name()]) self.treeStoreDetails.append(None, ["" + _("Licence") + "", exo.get_licence()]) self.treeStoreDetails.append(None, ["" + _("Description") + "", exo.get_description()]) self.treeStoreDetails.append(None, ["" + _("Author") + "", exo.get_author()]) self.treeStoreDetails.append(None, ["" + _("Author website") + "", exo.get_author_website()]) self.treeStoreDetails.append(None, ["" + _("Author contact") + "", exo.get_author_contact()]) self.treeStoreDetails.append(None, ["" + _("Version") + "", exo.get_version()]) self.treeStoreDetails.append(None, ["" + _("Words count") + "", exo.get_words_count()]) self.treeStoreDetails.append(None, ["" + _("Language") + "", exo.get_language()]) self.treeStoreDetails.append(None, ["" + _("Media type") + "", exo.get_media_type()]) self.treeStoreDetails.append(None, ["" + _("Id") + "", exo.get_id()]) self.treeStoreDetails.append(None, ["" + _("Install status") + "", installStatus]) for translation in exo.get_translations_list(): self.treeStoreDetails.append(None, ["" + _("Translation") + "", translation]) self.treeStoreDetails.append(None, ["" + _("Packager") + "", exo.get_packager()]) self.treeStoreDetails.append(None, ["" + _("Packager website") + "", exo.get_packager_website()]) self.treeStoreDetails.append(None, ["" + _("Packager contact") + "", exo.get_packager_contact()]) self.treeStoreDetails.append(None, ["" + _("Exercise path") + "", exo.get_local_path()]) self.treeStoreDetails.append(None, ["" + _("Package path") + "", exo.get_file_path()]) self.treeStoreDetails.append(None, ["" + _("Template path") + "", exo.get_template_path()]) self.treeStoreDetails.append(None, ["" + _("Instance path") + "", exo.get_instance_path()]) self.treeStoreDetails.append(None, ["" + _("Finished exercise path") + "", exo.get_done_path()]) cell = gtk.CellRendererText() treeviewcolumnName = gtk.TreeViewColumn(_("Name")) treeviewcolumnName.pack_start(cell, False) treeviewcolumnName.add_attribute(cell, 'markup', 0) treeviewcolumnName.set_expand(False) treeviewcolumnValue = gtk.TreeViewColumn(_("Value")) treeviewcolumnValue.pack_start(cell, True) treeviewcolumnValue.add_attribute(cell, 'markup', 1) treeviewcolumnValue.set_expand(True) self.treeviewDetails.append_column(treeviewcolumnName) self.treeviewDetails.append_column(treeviewcolumnValue) self.treeviewDetails.set_model(self.treeStoreDetails) def on_treeview_exercises_cursor_changed(self, widget, data=None): (modele, iter) = self.treeselectionExercises.get_selected() self.iterExo = iter if iter == None: return isExo, exo = modele.get(iter, 4, 5) if isExo: self.selectedExo = exo else: self.selectedExo = None self._update_action_button() self._update_details_tree_view() def _update_action_button(self): if self.selectedExo == None: self.buttonAction.set_sensitive(False) return exo = self.selectedExo if exo.get_state() == "available" or exo.get_state() == "corrupted" or exo.get_state() == "canceled": self.buttonAction.set_label(_("Install")) self.action = "install" self.buttonAction.set_sensitive(True) elif exo.get_state() == "downloading": self.buttonAction.set_label(_("Cancel install")) self.action = "cancel" self.buttonAction.set_sensitive(True) elif exo.get_state() == "installing": self.buttonAction.set_label(_("Cancel install")) self.action = "cancel" self.buttonAction.set_sensitive(False) elif exo.get_state() == "installed": self.buttonAction.set_label(_("Use")) self.action = "use" self.buttonAction.set_sensitive(True) elif exo.get_state() == "removing": self.buttonAction.set_label(_("Remove")) self.action = "remove" self.buttonAction.set_sensitive(False) elif exo.get_state() == "used": self.buttonAction.set_label(_("Continue")) self.action = "continue" self.buttonAction.set_sensitive(True) elif exo.get_state() == "done": self.buttonAction.set_label(_("Remove")) self.action = "remove" self.buttonAction.set_sensitive(True) def on_button_action_clicked(self, widget, data=None): self.logger.debug("on_buttonAction_activate") if self.action == "install": self._install_selected_exercise() elif self.action == "cancel": self._cancel_selected_exercise() elif self.action == "use": self._use_selected_exercise() elif self.action == "continue": self._continue_selected_exercise() def _install_selected_exercise(self): (exo, ) = self.treeStoreExercices.get(self.iterExo, 5) exo.start_install() def _cancel_selected_exercise(self): (exo, ) = self.treeStoreExercices.get(self.iterExo, 5) exo.cancel_install() def _use_selected_exercise(self): (exo, ) = self.treeStoreExercices.get(self.iterExo, 5) self.core.load_exercise(exo.get_template_path()) self.core.exercise.set_output_save_path(exo.get_instance_path()) self.core.save() self.dialog.response(gtk.RESPONSE_OK) def _continue_selected_exercise(self): (exo, ) = self.treeStoreExercices.get(self.iterExo, 5) self.core.load_exercise(exo.get_instance_path()) self.dialog.response(gtk.RESPONSE_OK) def _update_status(self): if self.availableExoCount > 1: status = _("%d available exercises") % self.availableExoCount else: status = _("%d available exercise") % self.availableExoCount if self.installedExoCount > 1: status += _(" and %d installed exercises") % self.installedExoCount else: status += _(" and %d installed exercise") % self.installedExoCount self.labelStatus.set_text(status) def exercice_state_change_listener(self, oldState, iter): (exo, ) = self.treeStoreExercices.get(iter, 5) if oldState == "installed" and exo.get_state() != "installed": self.availableExoCount += 1 self.installedExoCount -= 1 elif oldState != "installed" and exo.get_state() == "installed": self.availableExoCount -= 1 self.installedExoCount += 1 if exo.get_state() == "available": self.treeStoreExercices.set_value(iter, 3, _("Available")) elif exo.get_state() == "downloading": self.treeStoreExercices.set_value(iter, 3, _("Downloading")) thread.start_new_thread(self._download_exercise_thread, (iter,)) elif exo.get_state() == "installing": self.treeStoreExercices.set_value(iter, 3, _("Installing")) elif exo.get_state() == "installed": self.treeStoreExercices.set_value(iter, 3, _("Installed")) elif exo.get_state() == "corrupted": self.treeStoreExercices.set_value(iter, 3, _("Corrupted")) elif exo.get_state() == "canceled": self.treeStoreExercices.set_value(iter, 3, _("Canceled")) elif exo.get_state() == "removing": self.treeStoreExercices.set_value(iter, 3, _("Removing")) elif exo.get_state() == "used": self.treeStoreExercices.set_value(iter, 3, _("Used")) elif exo.get_state() == "done": self.treeStoreExercices.set_value(iter, 3, _("Done")) self._update_status() self._update_action_button() def _download_exercise_thread(self, iter): (exo, ) = self.treeStoreExercices.get(iter, 5) while exo.get_state() == "downloading": self.treeStoreExercices.set_value(iter, 3, _("Downloading ... %d%%") % exo.get_download_percent()) time.sleep(0.5) def on_checkbutton_tree_view_mode_toggled(self, widget, data=None): checkbuttonTreeViewMode = self.builder.get_object("checkbuttonTreeViewMode") if checkbuttonTreeViewMode.props.active: self.config.set("repositorymanager.displayonlyexercises", 0) else: self.config.set("repositorymanager.displayonlyexercises", 1) self._update_exercise_tree_view() def on_button_add_repo_clicked(self, widget, data=None): personnalRepoList = self.repositoryManager.get_personal_exercise_repository_list() personnalRepoList.append(self.builder.get_object("entryNewRepo").get_text()) self.builder.get_object("entryNewRepo").set_text("") self.repositoryManager.write_personal_exercise_repository_list(personnalRepoList) self.load() def on_button_delete_repo_clicked(self, widget, data=None): personnalRepoList = self.repositoryManager.get_personal_exercise_repository_list() personnalRepoList.remove(self.builder.get_object("entryNewRepo").get_text()) self.builder.get_object("entryNewRepo").set_text("") self.repositoryManager.write_personal_exercise_repository_list(personnalRepoList) self.load() def on_treeview_repositories_cursor_changed(self, widget, data=None): (modele, iter) = self.treeselectionRepositories.get_selected() if iter == None: return (repo, ) = self.treeStoreRepository.get(iter, 0) self.builder.get_object("entryNewRepo").set_text(repo) def on_expander_details_activate(self, widget, data=None): if self.builder.get_object("box1").get_homogeneous(): self.builder.get_object("box1").set_homogeneous(False) else: self.builder.get_object("box1").set_homogeneous(True) perroquet-1.1.1.orig/perroquetlib/repository/0000755000175000017500000000000011561344350021633 5ustar georgeskgeorgeskperroquet-1.1.1.orig/perroquetlib/repository/exercise_repository_exercise.py0000644000175000017500000003571411561344350030214 0ustar georgeskgeorgesk#! /usr/bin/python # -*- coding: utf-8 -*- # Copyright (C) 2009-2010 Frédéric Bertolus. # Copyright (C) 2009-2010 Matthieu Bizien. # # This file is part of Perroquet. # # Perroquet 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 3 of the License, or # (at your option) any later version. # # Perroquet 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 Perroquet. If not, see . import errno import gettext import logging import os import tarfile import tempfile import thread import urllib2 from threading import Lock from xml.dom.minidom import getDOMImplementation, parse from perroquetlib.debug import defaultLoggingHandler, defaultLoggingLevel _ = gettext.gettext class ExerciseRepositoryExercise: def __init__(self): self.id = "no-id" self.name = "No name" self.description = "" self.mutexInstalling = Lock() self.downloadPercent = 0 self.state = "none" self.wordsCount = 0 self.translationList = [] self.version = None self.logger = logging.Logger("ExerciseRepositoryExercise") self.logger.setLevel(defaultLoggingLevel) self.logger.addHandler(defaultLoggingHandler) self.licence = _("Not specified") self.author = _("Not specified") self.authorWebsite = _("Not specified") self.authorContact = _("Not specified") self.packager = _("Not specified") self.packagerWebsite = _("Not specified") self.packagerContact = _("Not specified") self.language = _("Not specified") self.mediaType = _("Not specified") self.filePath = _("Not specified") self.system = False def set_system(self, system): """Define if the exo is a system exo or only a local one A system exo store common data in a system directory and only the progress in the local directory """ self.system = system def is_installed(self): return os.path.isfile(self.get_template_path()) def is_used(self): return os.path.isfile(self.get_instance_path()) def is_done(self): return os.path.isfile(self.get_done_path()) def start_install(self): self.mutexInstalling.acquire() self.canceled = False self.downloadPercent = 0 self.play_thread_id = thread.start_new_thread(self.install_thread, ()) def cancel_install(self): self.canceled = True def wait_install_end(self): self.mutexInstalling.acquire() self.mutexInstalling.release() def download(self): f = urllib2.urlopen(self.get_file_path()) _, tempPath = tempfile.mkstemp("", "perroquet-"); wf = open(tempPath, 'w+b') size = f.info().get('Content-Length') if size is None: size = 0 else: size = int(size) count = 0 sizeToRead = 50000 while not self.canceled: data = f.read(sizeToRead) wf.write(data) if len(data) != sizeToRead: break; count += sizeToRead self.downloadPercent = (round((float(count) / float(size)) * 100)) self.downloading = False return tempPath def get_download_percent(self): return self.downloadPercent def get_state(self): #available #downloading #installing #installed #corrupted #canceled #removing #used #done if self.state == "none": if self.is_done(): self.state = "done" elif self.is_used(): self.state = "used" elif self.is_installed(): self.state = "installed" else: self.state = "available" return self.state def set_state(self, state): oldState = self.state self.state = state self.notifyStateChange(oldState, self.callbackData) def set_state_change_callback(self, callback, callbackData): self.notifyStateChange = callback self.callbackData = callbackData def install_thread(self): self.set_state("downloading") tmpPath = self.download() if self.canceled: self.logger.info("remove temp file") self.set_state("canceled") os.remove(tmpPath) else: self.set_state("installing") tar = tarfile.open(tmpPath) outPath = self.get_local_path() try: os.makedirs(outPath) except OSError, (ErrorNumber, ErrorMessage): # Python <=2.5 if ErrorNumber == errno.EEXIST: pass else: raise tar.extractall(outPath) tar.close() os.remove(tmpPath) if self.is_installed(): self.set_state("installed") else: self.set_state("corrupted") self.mutexInstalling.release() def get_template_path(self): return os.path.join(self.get_local_path(), "template.perroquet") def get_instance_path(self): return os.path.join(self.get_personnal_local_path(), "instance.perroquet") def get_done_path(self): return os.path.join(self.get_personnal_local_path(), "done.perroquet") def set_name(self, name): self.name = name def get_name(self): return self.name def set_id(self, id): self.id = id def get_id(self): return self.id def set_description(self, description): self.description = description def get_description(self): return self.description def set_licence(self, licence): self.licence = licence def get_licence(self): return self.licence def set_language(self, language): self.language = language def get_language(self): return self.language def set_media_type(self, mediaType): self.mediaType = mediaType def get_media_type(self): return self.mediaType def set_version(self, version): self.version = version def get_version(self): return self.version def set_author(self, author): self.author = author def get_author(self): return self.author def set_words_count(self, wordsCount): self.wordsCount = wordsCount def get_words_count(self): return self.wordsCount def set_author_website(self, authorWebsite): self.authorWebsite = authorWebsite def get_author_website(self): return self.authorWebsite def set_author_contact(self, authorContact): self.authorContact = authorContact def get_author_contact(self): return self.authorContact def set_packager(self, packager): self.packager = packager def get_packager(self): return self.packager def set_packager_website(self, packagerWebsite): self.packagerWebsite = packagerWebsite def get_packager_website(self): return self.packagerWebsite def set_packager_contact(self, packagerContact): self.packagerContact = packagerContact def get_packager_contact(self): return self.packagerContact def set_file_path(self, filePath): self.filePath = filePath def get_file_path(self): return self.filePath def set_translations_list(self, translationList): self.translationList = translationList def get_translations_list(self): return self.translationList def set_parent(self, parent): self.parent = parent def get_local_path(self): versioned_id = None if self.version is not None: versioned_id = self.id + "_" + self.version else: versioned_id = self.id return os.path.join(self.parent.get_local_path(), versioned_id) def get_personnal_local_path(self): versioned_id = None if self.version is not None: versioned_id = self.id + "_" + self.version else: versioned_id = self.id return os.path.join(self.parent.get_personal_local_path(), versioned_id) def parse_description(self, xml_exercise): self.set_name(self._get_text(xml_exercise.getElementsByTagName("name")[0].childNodes)) self.set_id(self._get_text(xml_exercise.getElementsByTagName("id")[0].childNodes)) self.set_description(self._get_text(xml_exercise.getElementsByTagName("description")[0].childNodes)) self.set_licence(self._get_text(xml_exercise.getElementsByTagName("licence")[0].childNodes)) self.set_language(self._get_text(xml_exercise.getElementsByTagName("language")[0].childNodes)) self.set_media_type(self._get_text(xml_exercise.getElementsByTagName("media_type")[0].childNodes)) self.set_version(self._get_text(xml_exercise.getElementsByTagName("exercise_version")[0].childNodes)) self.set_author(self._get_text(xml_exercise.getElementsByTagName("author")[0].childNodes)) self.set_author_website(self._get_text(xml_exercise.getElementsByTagName("author_website")[0].childNodes)) self.set_author_contact(self._get_text(xml_exercise.getElementsByTagName("author_contact")[0].childNodes)) self.set_packager(self._get_text(xml_exercise.getElementsByTagName("packager")[0].childNodes)) self.set_packager_website(self._get_text(xml_exercise.getElementsByTagName("packager_website")[0].childNodes)) self.set_packager_contact(self._get_text(xml_exercise.getElementsByTagName("packager_contact")[0].childNodes)) if len(xml_exercise.getElementsByTagName("words_count")) > 0: self.set_words_count(self._get_text(xml_exercise.getElementsByTagName("words_count")[0].childNodes)) if len(xml_exercise.getElementsByTagName("file")) > 0: self.set_file_path(self._get_text(xml_exercise.getElementsByTagName("file")[0].childNodes)) if len(xml_exercise.getElementsByTagName("translations")) > 0: xml_translations = xml_exercise.getElementsByTagName("translations")[0] translationList = [] for xml_translation in xml_translations.getElementsByTagName("translation"): translationList.append(self._get_text(xml_translation.childNodes)) self.set_translations_list(translationList) def generate_description(self): self._generate_description() def _generate_description(self): if not os.path.isdir(self.get_local_path()): try: os.makedirs(self.get_local_path()) except OSError, (ErrorNumber, ErrorMessage): # Python <=2.5 if ErrorNumber == 666: #EEXIST ??? pass else: raise impl = getDOMImplementation() newdoc = impl.createDocument(None, "perroquet_exercise", None) root_element = newdoc.documentElement # Name xml_name = newdoc.createElement("name") xml_name.appendChild(newdoc.createTextNode(self.get_name())) root_element.appendChild(xml_name) # Id xml_id = newdoc.createElement("id") xml_id.appendChild(newdoc.createTextNode(self.get_id())) root_element.appendChild(xml_id) # Description xml_description = newdoc.createElement("description") xml_description.appendChild(newdoc.createTextNode(self.get_description())) root_element.appendChild(xml_description) # Words count xml_version = newdoc.createElement("words_count") xml_version.appendChild(newdoc.createTextNode(str(self.get_words_count()))) root_element.appendChild(xml_version) # Version xml_version = newdoc.createElement("exercise_version") xml_version.appendChild(newdoc.createTextNode(self.get_version())) root_element.appendChild(xml_version) # Licence xml_node = newdoc.createElement("licence") xml_node.appendChild(newdoc.createTextNode(self.get_licence())) root_element.appendChild(xml_node) # Language xml_node = newdoc.createElement("language") xml_node.appendChild(newdoc.createTextNode(self.get_language())) root_element.appendChild(xml_node) # Media type xml_node = newdoc.createElement("media_type") xml_node.appendChild(newdoc.createTextNode(self.get_media_type())) root_element.appendChild(xml_node) # author xml_node = newdoc.createElement("author") xml_node.appendChild(newdoc.createTextNode(self.get_author())) root_element.appendChild(xml_node) # author website xml_node = newdoc.createElement("author_website") xml_node.appendChild(newdoc.createTextNode(self.get_author_website())) root_element.appendChild(xml_node) # author contact xml_node = newdoc.createElement("author_contact") xml_node.appendChild(newdoc.createTextNode(self.get_author_contact())) root_element.appendChild(xml_node) # packager xml_node = newdoc.createElement("packager") xml_node.appendChild(newdoc.createTextNode(self.get_packager())) root_element.appendChild(xml_node) # packager website xml_node = newdoc.createElement("packager_website") xml_node.appendChild(newdoc.createTextNode(self.get_packager_website())) root_element.appendChild(xml_node) # packager contact xml_node = newdoc.createElement("packager_contact") xml_node.appendChild(newdoc.createTextNode(self.get_packager_contact())) root_element.appendChild(xml_node) # template path xml_node = newdoc.createElement("template") xml_node.appendChild(newdoc.createTextNode(self.get_template_path())) root_element.appendChild(xml_node) # translation #TODO xml_string = newdoc.toprettyxml() xml_string = xml_string.encode('utf8') repoDescriptionPath = os.path.join(self.get_local_path(), "exercise.xml") f = open(repoDescriptionPath, 'w') f.write(xml_string) f.close() def init_from_path(self, exercisePath): exerciseDescriptionPath = os.path.join(exercisePath, "exercise.xml") if os.path.isfile(exerciseDescriptionPath): f = open(exerciseDescriptionPath, 'r') dom = parse(f) self.parse_description(dom) else: self.id = os.path.basename(exercisePath) self.name = self.id self.description = gettext.gettext("Imported exercise") def _get_text(self, nodelist): rc = "" for node in nodelist: if node.nodeType == node.TEXT_NODE: rc = rc + node.data rc = rc.strip() return rc perroquet-1.1.1.orig/perroquetlib/repository/exercise_repository_group.py0000644000175000017500000001165711561344350027541 0ustar georgeskgeorgesk#! /usr/bin/python # -*- coding: utf-8 -*- # Copyright (C) 2009-2010 Frédéric Bertolus. # Copyright (C) 2009-2010 Matthieu Bizien. # # This file is part of Perroquet. # # Perroquet 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 3 of the License, or # (at your option) any later version. # # Perroquet 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 Perroquet. If not, see . import errno import os from xml.dom.minidom import getDOMImplementation, parse from perroquetlib.repository.exercise_repository_exercise import ExerciseRepositoryExercise class ExerciseRepositoryGroup: def __init__(self): self.name = "" self.description = "" self.exercisesList = [] self.system = False def set_system(self, system): """Define if the group is a system repository or only a local one A system group store common data in a system directory and only the progress in the local directory """ self.system = system; def set_name(self, name): self.name = name def set_description(self, description): self.description = description def get_name(self): return self.name def get_description(self): return self.description def set_id(self, id): self.id = id def get_id(self): return self.id def add_exercise(self, exercise): self.exercisesList.append(exercise) exercise.set_parent(self) def get_exercises(self): return self.exercisesList def set_parent(self, parent): self.parent = parent def get_local_path(self): return os.path.join(self.parent.get_local_path(), self.id) def get_personal_local_path(self): return os.path.join(self.parent.get_personal_local_path(), self.id) def parse_description(self, xml_group): self.set_name(self._get_text(xml_group.getElementsByTagName("name")[0].childNodes)) self.set_id(self._get_text(xml_group.getElementsByTagName("id")[0].childNodes)) self.set_description(self._get_text(xml_group.getElementsByTagName("description")[0].childNodes)) def generate_description(self): self._generate_description() for exo in self.get_exercises(): exo.generate_description() def _generate_description(self): if not os.path.isdir(self.get_local_path()): try: os.makedirs(self.get_local_path()) except OSError, (ErrorNumber, ErrorMessage): # Python <=2.5 if ErrorNumber == errno.EEXIST: pass else: raise impl = getDOMImplementation() newdoc = impl.createDocument(None, "perroquet_group", None) root_element = newdoc.documentElement # Name xml_name = newdoc.createElement("name") xml_name.appendChild(newdoc.createTextNode(self.get_name())) root_element.appendChild(xml_name) # Id xml_id = newdoc.createElement("id") xml_id.appendChild(newdoc.createTextNode(self.get_id())) root_element.appendChild(xml_id) # Description xml_description = newdoc.createElement("description") xml_description.appendChild(newdoc.createTextNode(self.get_description())) root_element.appendChild(xml_description) xml_string = newdoc.toprettyxml() xml_string = xml_string.encode('utf8') repoDescriptionPath = os.path.join(self.get_local_path(), "group.xml") f = open(repoDescriptionPath, 'w') f.write(xml_string) f.close() def init_from_path(self, groupPath): groupDescriptionPath = os.path.join(groupPath, "group.xml") # Read group infos from xml description file if os.path.isfile(groupDescriptionPath): f = open(groupDescriptionPath, 'r') dom = parse(f) self.parse_description(dom) else: self.set_id(os.path.basename(groupPath)) self.set_name(os.path.basename(groupPath)) exercisePathList = os.listdir(groupPath) for exercisePath in exercisePathList: path = os.path.join(groupPath, exercisePath) if os.path.isdir(path): exo = ExerciseRepositoryExercise() exo.set_system(self.system) exo.init_from_path(path) self.add_exercise(exo) def _get_text(self, nodelist): rc = "" for node in nodelist: if node.nodeType == node.TEXT_NODE: rc = rc + node.data rc = rc.strip() return rc perroquet-1.1.1.orig/perroquetlib/repository/exercise_repository.py0000644000175000017500000001711411561344350026317 0ustar georgeskgeorgesk#! /usr/bin/python # -*- coding: utf-8 -*- # Copyright (C) 2009-2010 Frédéric Bertolus. # Copyright (C) 2009-2010 Matthieu Bizien. # # This file is part of Perroquet. # # Perroquet 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 3 of the License, or # (at your option) any later version. # # Perroquet 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 Perroquet. If not, see . import errno import os from xml.dom.minidom import getDOMImplementation, parse from perroquetlib.config import config from perroquetlib.repository.exercise_repository_exercise import ExerciseRepositoryExercise from perroquetlib.repository.exercise_repository_group import ExerciseRepositoryGroup #FIXME import needed but bug #from perroquetlib.repository.exercise_repository_manager import ExerciseRepositoryManager class ExerciseRepository: def __init__(self): self.name = "" self.description = "" self.version = "" self.url = "" self.exercisesGroupList = [] self.config = config self.system = False def set_system(self, system): """Define if the repo is a system repository or only a local one A system repository store common data in a system directory and only the progress in the local directory """ self.system = system; def set_name(self, name): self.name = name def set_description(self, description): self.description = description def set_version(self, version): self.version = version def get_name(self): return self.name def set_type(self, type): self.type = type def get_type(self): return self.type def set_id(self, id): self.id = id def get_id(self): return self.id def set_url(self, url): self.url = url def get_url(self): return self.url def get_description(self): return self.description def get_version(self): return self.version def add_group(self, group): self.exercisesGroupList.append(group) group.set_parent(self) def get_groups(self): return self.exercisesGroupList def get_orphan_groups(self): #FIXME ugly from perroquetlib.repository.exercise_repository_manager import ExerciseRepositoryManager groupPathList = os.listDir(self.get_local_path()) groupUsedPath = [] orphanGroupList = [] for group in self.get_groups(): groupUsedPath.append(group.get_local_path()) for groupPath in groupPathList: if groupPath not in groupUsedPath: #FIXME used or not ??? group = ExerciseRepositoryManager.createGroupFromPath(repoPath) #FIXME repoPath unknow var orphanGroupList.append(group) return orphanGroupList def get_local_path(self): if self.system: return os.path.join(self.config.get("system_repo_root_dir"), self.id) else: return os.path.join(self.config.get("local_repo_root_dir"), self.id) def get_personal_local_path(self): return os.path.join(self.config.get("local_repo_root_dir"), self.id) def generate_description(self): self._generate_description() for group in self.get_groups(): group.generate_description() def _generate_description(self): if not os.path.isdir(self.get_local_path()): try: os.makedirs(self.get_local_path()) except OSError, (ErrorNumber, ErrorMessage): # Python <=2.5 if ErrorNumber == errno.EEXIST: pass else: raise impl = getDOMImplementation() newdoc = impl.createDocument(None, "perroquet_repository", None) root_element = newdoc.documentElement # Name xml_name = newdoc.createElement("name") xml_name.appendChild(newdoc.createTextNode(self.get_name())) root_element.appendChild(xml_name) # Id xml_id = newdoc.createElement("id") xml_id.appendChild(newdoc.createTextNode(self.get_id())) root_element.appendChild(xml_id) # Description xml_description = newdoc.createElement("description") xml_description.appendChild(newdoc.createTextNode(self.get_description())) root_element.appendChild(xml_description) # Version xml_version = newdoc.createElement("version") xml_version.appendChild(newdoc.createTextNode(self.get_version())) root_element.appendChild(xml_version) # Url xml_url = newdoc.createElement("url") xml_url.appendChild(newdoc.createTextNode(self.get_url())) root_element.appendChild(xml_url) xml_string = newdoc.toprettyxml() xml_string = xml_string.encode('utf8') repoDescriptionPath = os.path.join(self.get_local_path(), "repository.xml") f = open(repoDescriptionPath, 'w') f.write(xml_string) f.close() def init_from_path(self, path): # Read repositories from xml description file repoDescriptionPath = os.path.join(path, "repository.xml") if os.path.isfile(repoDescriptionPath): f = open(repoDescriptionPath, 'r') dom = parse(f) self.parse_description(dom) else: self.set_id(os.path.basename(path)) self.set_name(os.path.basename(path)) groupPathList = os.listdir(self.get_local_path()) for groupPath in groupPathList: path = os.path.join(self.get_local_path(), groupPath) if os.path.isdir(path): group = ExerciseRepositoryGroup() group.init_from_path(path) group.set_system(self.system) self.add_group(group) def parse_description(self, dom): self.set_name(self._get_text(dom.getElementsByTagName("name")[0].childNodes)) self.set_description(self._get_text(dom.getElementsByTagName("description")[0].childNodes)) self.set_id(self._get_text(dom.getElementsByTagName("id")[0].childNodes)) self.set_version(self._get_text(dom.getElementsByTagName("version")[0].childNodes)) if len(dom.getElementsByTagName("url")) > 0: self.set_url(self._get_text(dom.getElementsByTagName("url")[0].childNodes)) def parse_distant_repository_file(self, handle): dom = parse(handle) self.parse_description(dom) self.set_type("distant") xml_groups = dom.getElementsByTagName("exercises_groups")[0] for xml_group in xml_groups.getElementsByTagName("exercises_group"): group = ExerciseRepositoryGroup() group.parse_description(xml_group) self.add_group(group) xml_exercises = xml_group.getElementsByTagName("exercises")[0] for xml_exercise in xml_exercises.getElementsByTagName("exercise"): exercise = ExerciseRepositoryExercise() exercise.parse_description(xml_exercise) group.add_exercise(exercise) def _get_text(self, nodelist): rc = "" for node in nodelist: if node.nodeType == node.TEXT_NODE: rc = rc + node.data rc = rc.strip() return rc perroquet-1.1.1.orig/perroquetlib/repository/exercise_repository_manager.py0000644000175000017500000003650511561344350030016 0ustar georgeskgeorgesk#! /usr/bin/python # -*- coding: utf-8 -*- # Copyright (C) 2009-2010 Frédéric Bertolus. # Copyright (C) 2009-2010 Matthieu Bizien. # # This file is part of Perroquet. # # Perroquet 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 3 of the License, or # (at your option) any later version. # # Perroquet 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 Perroquet. If not, see . import errno import logging import os import shutil import tarfile import tempfile import urllib2 from gettext import gettext as _ from xml.dom.minidom import parse from perroquetlib.config import config from perroquetlib.debug import defaultLoggingHandler, defaultLoggingLevel from perroquetlib.model.exercise_parser import load_exercise, save_exercise from perroquetlib.repository.exercise_repository import ExerciseRepository class ExerciseRepositoryManager: """This class provide an interface to get a list of exercise repository, instance of ExerciseRepository. A repository is a tree of Perroquet exercises which can be synchronized with an online image. During synchronization, only the exercise's metadatas are downloaded but it's possible to install the real exercise then use it. A repository contain a list of exercises group. A group contains a list of exercises. The repositories metadatas are store in a directory tree in the local datas of the user. This path is call 'repo root directory'. """ def __init__(self): """Constructor""" self.logger = logging.Logger("ExerciseRepositoryManager") self.logger.setLevel(defaultLoggingLevel) self.logger.addHandler(defaultLoggingHandler) def get_exercise_repository_list(self): """Return the list of all find repositories. The list is build from multiple sources: - from the list url of repository online description found in the main config file - from the list url of repository online description found in the local config file - from the directory present in the repo_root directory whose not correspond with an url The returned list classify each repository in one of these type: - local: if a directory name 'local' is find in the repo_root directory, a repository named 'local' is initialized from this path. The local repository is not syncronised with distant description but contains all manually installed exercises. - distant: if an repository url is reachable, a distant repository is initialized after syncronization with the distant description file. - offline: if an repository url is not reachable, an offline repository is initialized from the directory created during the last syncronization. - orphan : an orphan repository is initialized from each directory in repo_root which not correspond to any others type of repository. An orphan repository car appear is a syncronized exercise description is remove from list of url. """ repositoryList = [] #Add the local repository if exist repositoryList += self._get_local_exercise_repository_list() #Add the list of distant and offline repository repositoryList += self._get_distant_exercise_repository_list() #Add the list of orphan repository repositoryList += self._get_orphan_exercise_repository_list(repositoryList) return repositoryList def _get_local_exercise_repository_list(self): """Build and return the list of local repository. In fact, this list will contain at maximum 2 repositories. A local personnal repository is added to the list if a directory named 'local' exist in the repo root directory. A local system repository is added to the list if a directory named 'system_repo' is found in the perroquet data files. The local system repository is special because it is composed by 2 path : a system path for common data and a path for personnal data as progress """ repositoryList = [] # Build potential local repository path localRepoPath = os.path.join(config.get("local_repo_root_dir"), "local") # Test local repository existance if os.path.isdir(localRepoPath): # Repository found so Initialize it from the path and set it as local repository = ExerciseRepository() repository.init_from_path(localRepoPath) repository.set_type("local") repositoryList.append(repository) systemRepoPath = os.path.join(config.get("system_repo_root_dir"), "system") print "system repo path "+systemRepoPath #Test systemrepository existance if os.path.isdir(systemRepoPath): print "system repo found" # Repository found so Initialize it from the path and set it as local repository = ExerciseRepository() repository.set_system(True); repository.init_from_path(systemRepoPath) repository.set_type("local") repositoryList.append(repository) return repositoryList def _get_distant_exercise_repository_list(self): """Build and return a list of distant and offline repositories. There is one repository initialized for each url found in the config files. If the url is reachable, a distant repository is initialized, else an offline repository is created. """ #Get the list of files containing url list repositoryPathList = config.get("repository_source_list") repositoryList = [] offlineRepoList = [] #Fetch repository urls file for path in repositoryPathList: f = open(path, 'r') #Fetch urls of repo description for line in f: line = line.rstrip() if line and line[0] == "#": #Comment line continue req = urllib2.Request(line) try: #try to download the file headers handle = urllib2.urlopen(req) except IOError: #if the download failed store the url in a list of #offline urls self.logger.error("Fail to connection to repository '" + line + "'") offlineRepoList.append(line) except ValueError: self.logger.exception("Unknown url type '" + line + "'") offlineRepoList.append(line) else: #if the download success, init a repository from the #downloaded file and regenerate the tree self.logger.debug("init distant repo") repository = ExerciseRepository() repository.parse_distant_repository_file(handle) repository.set_url(line) repositoryList.append(repository) repository.generate_description() #if the list of url is not empty, add a list of offline repository if len(offlineRepoList) > 0: repoPathList = os.listdir(config.get("local_repo_root_dir")) for repoPath in repoPathList: #for eaach directory in the repo root path analyse the #description file. If the url of this repo match with one #of offline url, then init an offline repository from this #path repository = ExerciseRepository() repoDescriptionPath = os.path.join(config.get("local_repo_root_dir"), repoPath, "repository.xml") if not os.path.isfile(repoDescriptionPath): continue f = open(repoDescriptionPath, 'r') dom = parse(f) repository.parse_description(dom) self.logger.debug("test offline url : " + repository.get_url()) if repository.get_url() in offlineRepoList: self.logger.info("add url : " + repository.get_url()) repository = ExerciseRepository() repository.init_from_path(os.path.join(config.get("local_repo_root_dir"), repoPath)) repository.set_type("offline") repositoryList.append(repository) return repositoryList def get_personal_exercise_repository_list(self): """Return the list of the url of the repository url add by the user""" repositoryPath = config.get("personal_repository_source_path") repositoryList = [] if os.path.isfile(repositoryPath): f = open(repositoryPath, 'r') for line in f: line = line.rstrip() if line and line[0] == "#": #Comment line continue repositoryList.append(line) return repositoryList def write_personal_exercise_repository_list(self, newRepositoryList): """The goal of this methods is to make an inteligent write of config file. Lines beginning with # are comment and must be keep in place""" repositoryPath = config.get("personal_repository_source_path") repositoryList = [] if os.path.isfile(repositoryPath): f = open(repositoryPath, 'r') for line in f: line = line.rstrip() if line[0] == "#": #Comment line, set as keep repositoryList.append(line) elif line in newRepositoryList: repositoryList.append(line) newRepositoryList.remove(line) f.close() for newRepo in newRepositoryList: repositoryList.append(newRepo) f = open(repositoryPath, 'w') for line in repositoryList: self.logger.debug(line) f.writelines(line + '\n') f.close() def _get_orphan_exercise_repository_list(self, repositoryListIn): repoPathList = os.listdir(config.get("local_repo_root_dir")) repoUsedPath = [] repositoryList = [] for repo in repositoryListIn: repoUsedPath.append(repo.get_local_path()) for repoPath in repoPathList: path = os.path.join(config.get("local_repo_root_dir"), repoPath) if path not in repoUsedPath: repository = ExerciseRepository() repository.init_from_path(path) repository.set_type("orphan") repositoryList.append(repository) return repositoryList def _get_text(self, nodelist): rc = "" for node in nodelist: if node.nodeType == node.TEXT_NODE: rc = rc + node.data rc = rc.strip() return rc def export_as_package(self, exercise, outPath): tempPath = tempfile.mkdtemp("", "perroquet-"); exercisePackage = exercise #Make data directory dataPath = os.path.join(tempPath, "data") if not os.path.isdir(dataPath): try: os.makedirs(dataPath) except OSError, (ErrorNumber, ErrorMessage): # Python <=2.5 if ErrorNumber == errno.EEXIST: pass else: raise #Exercise - SubExercises for i, subExo in enumerate(exercisePackage.subExercisesList): localPath = os.path.join("data", "sequence_" + str(i)) #Make sequence directory sequencePath = os.path.join(tempPath, localPath) if not os.path.isdir(sequencePath): try: os.makedirs(sequencePath) except OSError, (ErrorNumber, ErrorMessage): # Python <=2.5 if ErrorNumber == errno.EEXIST: pass else: raise #Copy resources locally videoPath = os.path.join(sequencePath, os.path.basename(subExo.get_video_path())) exercisePath = os.path.join(sequencePath, os.path.basename(subExo.get_exercise_path())) translationPath = os.path.join(sequencePath, os.path.basename(subExo.get_translation_path())) videoRelPath = os.path.join(localPath, os.path.basename(subExo.get_video_path())) exerciseRelPath = os.path.join(localPath, os.path.basename(subExo.get_exercise_path())) translationRelPath = os.path.join(localPath, os.path.basename(subExo.get_translation_path())) shutil.copyfile(subExo.get_video_path(), videoPath) shutil.copyfile(subExo.get_exercise_path(), exercisePath) if subExo.get_translation_path() != '': shutil.copyfile(subExo.get_translation_path(), translationPath) subExo.set_video_export_path(videoRelPath) subExo.set_exercise_export_path(exerciseRelPath) if subExo.get_translation_path() != '': subExo.set_translation_export_path(translationRelPath) templatePath = os.path.join(tempPath, "template.perroquet") exercisePackage.set_output_save_path(templatePath) exercisePackage.set_template(True) save_exercise(exercisePackage, exercisePackage.get_output_save_path()) # Create the tar tar = tarfile.open(outPath, 'w') tar.add(templatePath, "template.perroquet") tar.add(dataPath, "data") tar.close() shutil.rmtree(tempPath) def import_package(self, import_path): #Verify file existence if not os.path.isfile(import_path): return _("File not found: ") + import_path + "." #Create temporary directory to extract the tar before install it tempPath = tempfile.mkdtemp("", "perroquet-"); #Extract tar in temp directory tar = tarfile.open(import_path) tar.extractall(tempPath) tar.close() #Check package integrity template_path = os.path.join(tempPath, "template.perroquet") if not os.path.isfile(template_path): shutil.rmtree(tempPath) return _("Invalid package, missing template.perroquet.") #Find install path exercise = load_exercise(template_path) if exercise.get_name() != '': name = exercise.get_name() else: name = exercise.GetVideoPath() localRepoPath = os.path.join(config.get("local_repo_root_dir"), "local") group_path = os.path.join(localRepoPath, "Imported") install_path = os.path.join(group_path, name) #Copy files if not os.path.isdir(group_path): try: os.makedirs(group_path) except OSError, (ErrorNumber, ErrorMessage): # Python <=2.5 if ErrorNumber == errno.EEXIST: pass else: raise if os.path.isdir(install_path): shutil.rmtree(tempPath) return _("Exercise already exist.") shutil.move(tempPath, install_path) return None perroquet-1.1.1.orig/perroquetlib/repository/__init__.py0000644000175000017500000000000011561344350023732 0ustar georgeskgeorgeskperroquet-1.1.1.orig/perroquetlib/config/0000755000175000017500000000000011561344350020661 5ustar georgeskgeorgeskperroquet-1.1.1.orig/perroquetlib/config/perroquet_config.py0000644000175000017500000002061711561344350024614 0ustar georgeskgeorgesk# -*- coding: utf-8 -*- # Copyright (C) 2009-2010 Frédéric Bertolus. # Copyright (C) 2009-2010 Matthieu Bizien. # # This file is part of Perroquet. # # Perroquet 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 3 of the License, or # (at your option) any later version. # # Perroquet 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 Perroquet. If not, see . import gettext import os import sys from config_lib import Config APP_NAME = 'perroquet' APP_VERSION = '1.1.1' config = Config() #General config.set("version", APP_VERSION) config.set("app_name", APP_NAME) #Paths config.set("executable", os.path.dirname(sys.executable)) config.set("script", sys.path[0]) config.set("localconfigdir", os.path.join(os.path.expanduser("~"), ".config/perroquet")) config.set("system_repo_root_dir", os.path.join("/usr/share/perroquet/repo_root")) config.set("localdatadir", os.path.join(os.path.expanduser("~"), ".local/share/perroquet")) config.set("local_repo_root_dir", os.path.join(config.get("localdatadir"), "repo_root")) if os.path.exists("/etc/perroquet"): config.set("globalconfigdir", "/etc/perroquet") #FIXME ugly else: config.set("globalconfigdir", "/usr/local/etc/perroquet") #FIXME ugly #Path sources.conf localSourceFile = os.path.join(config.get("localconfigdir"), "sources.conf") globalSourceFile = os.path.join(config.get("globalconfigdir"), "sources.conf") scriptSourceFile = os.path.join(config.get("script"), "data/sources.conf") sourcePathList = [] if os.path.isfile(scriptSourceFile): sourcePathList.append(scriptSourceFile) if os.path.isfile(globalSourceFile): sourcePathList.append(globalSourceFile) if os.path.isfile(localSourceFile): sourcePathList.append(localSourceFile) config.set("repository_source_list", os.path.join(sourcePathList)) config.set("personal_repository_source_path", localSourceFile) #path locale if os.path.exists(os.path.join(config.get("script"), 'build/mo')): config.set("localedir", os.path.join(config.get("script"), 'build/mo')) else: config.set("localedir", os.path.join(config.get("script"), '../share/locale')) if os.path.isfile(os.path.join(config.get("script"), 'data/perroquet.png')): config.set("logo_path", os.path.join(config.get("script"), 'data/perroquet.png')) else: config.set("logo_path", os.path.join(config.get("script"), '../share/perroquet/perroquet.png')) if os.path.isfile(os.path.join(config.get("script"), 'data/audio_icon.png')): config.set("audio_icon", os.path.join(config.get("script"), 'data/audio_icon.png')) else: config.set("audio_icon", os.path.join(config.get("script"), '../share/perroquet/audio_icon.png')) #gettext config.set("gettext_package", "perroquet") gettext.install (config.get("gettext_package"), config.get("localedir")) #Loading files #perroquet.ui if os.path.isfile(os.path.join(config.get("script"), 'data/perroquet.ui')): config.set("ui_path", os.path.join(config.get("script"), 'data/perroquet.ui')) elif os.path.isfile(os.path.join(config.get("script"), '../share/perroquet/perroquet.ui')): config.set("ui_path", os.path.join(config.get("script"), '../share/perroquet/perroquet.ui')) else: raise IOError, "Error : gui file 'perroquet.ui' not found" #settings.ui if os.path.isfile(os.path.join(config.get("script"), 'data/settings.ui')): config.set("ui_settings_path", os.path.join(config.get("script"), 'data/settings.ui')) elif os.path.isfile(os.path.join(config.get("script"), '../share/perroquet/settings.ui')): config.set("ui_settings_path", os.path.join(config.get("script"), '../share/perroquet/settings.ui')) else: raise IOError, "Error : gui file 'settings.ui' not found" #properties.ui if os.path.isfile(os.path.join(config.get("script"), 'data/properties.ui')): config.set("ui_sequence_properties_path", os.path.join(config.get("script"), 'data/properties.ui')) elif os.path.isfile(os.path.join(config.get("script"), '../share/perroquet/properties.ui')): config.set("ui_sequence_properties_path", os.path.join(config.get("script"), '../share/perroquet/properties.ui')) else: raise IOError, "Error : gui file 'properties.ui' not found" #reset.ui if os.path.isfile(os.path.join(config.get("script"), 'data/reset.ui')): config.set("ui_reset_path", os.path.join(config.get("script"), 'data/reset.ui')) elif os.path.isfile(os.path.join(config.get("script"), '../share/perroquet/reset.ui')): config.set("ui_reset_path", os.path.join(config.get("script"), '../share/perroquet/reset.ui')) else: raise IOError, "Error : gui file 'reset.ui' not found" #properties_advanced.ui if os.path.isfile(os.path.join(config.get("script"), 'data/properties_advanced.ui')): config.set("ui_sequence_properties_advanced_path", os.path.join(config.get("script"), 'data/properties_advanced.ui')) elif os.path.isfile(os.path.join(config.get("script"), '../share/perroquet/properties.ui')): config.set("ui_sequence_properties_advanced_path", os.path.join(config.get("script"), '../share/perroquet/properties_advanced.ui')) else: raise IOError, "Error : gui file 'properties_advanced.ui' not found" #exercise_manager.ui if os.path.isfile(os.path.join(config.get("script"), 'data/exercise_manager.ui')): config.set("ui_exercise_manager_path", os.path.join(config.get("script"), 'data/exercise_manager.ui')) elif os.path.isfile(os.path.join(config.get("script"), '../share/perroquet/exercise_manager.ui')): config.set("ui_exercise_manager_path", os.path.join(config.get("script"), '../share/perroquet/exercise_manager.ui')) else: raise IOError, "Error : gui file 'exercise_manager.ui' not found" #gui_password_dialog.ui if os.path.isfile(os.path.join(config.get("script"), 'data/gui_password_dialog.ui')): config.set("ui_password_path", os.path.join(config.get("script"), 'data/gui_password_dialog.ui')) elif os.path.isfile(os.path.join(config.get("script"), '../share/perroquet/gui_password_dialog.ui')): config.set("ui_password_path", os.path.join(config.get("script"), '../share/perroquet/gui_password_dialog.ui')) else: raise IOError, "Error : gui file 'gui_password_dialog.ui' not found" #gui_message_dialog.ui if os.path.isfile(os.path.join(config.get("script"), 'data/gui_message_dialog.ui')): config.set("ui_message_path", os.path.join(config.get("script"), 'data/gui_message_dialog.ui')) elif os.path.isfile(os.path.join(config.get("script"), '../share/perroquet/gui_message_dialog.ui')): config.set("ui_message_path", os.path.join(config.get("script"), '../share/perroquet/gui_message_dialog.ui')) else: raise IOError, "Error : gui file 'gui_message_dialog.ui' not found" #languages.list if os.path.isfile(os.path.join(config.get("script"), 'data/languages.list')): config.set("languages_list_path", os.path.join(config.get("script"), 'data/languages.list')) elif os.path.isfile(os.path.join(config.get("script"), '../share/perroquet/languages.list')): config.set("languages_list_path", os.path.join(config.get("script"), '../share/perroquet/languages.list')) else: raise IOError, "Error : data file 'languages.list' not found" #config.ini globalPaths = ( os.path.join(config.get("globalconfigdir"), "config.ini"), os.path.join(config.get("script"), 'data/config.ini'), ) existantGlobalPaths = [path for path in globalPaths if os.path.isfile(path)] if existantGlobalPaths == []: raise IOError, "Error : global conf file 'config.ini' not found" else: globalPath = existantGlobalPaths[0] localPath = os.path.join(config.get("localconfigdir"), "config.ini") if not os.path.isfile(localPath): config.logger.warn("No local conf file found") config.load_writable_config_file(localPath, globalPath) #languages_aliases.ini paths = ( os.path.join(config.get("globalconfigdir"), "languages_aliases.ini"), os.path.join(config.get("script"), 'data/languages_aliases.ini'), ) existantPaths = [path for path in paths if os.path.isfile(path)] if existantPaths == []: raise IOError, "Error : global conf file 'languages_aliases.ini' not found" else: path = existantPaths[0] config.load_config_file(path) perroquet-1.1.1.orig/perroquetlib/config/__init__.py0000644000175000017500000000167511561344350023003 0ustar georgeskgeorgesk# -*- coding: utf-8 -*- # Copyright (C) 2009-2010 Frédéric Bertolus. # Copyright (C) 2009-2010 Matthieu Bizien. # # This file is part of Perroquet. # # Perroquet 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 3 of the License, or # (at your option) any later version. # # Perroquet 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 Perroquet. If not, see . """The configuration of perroquet. Config is the general class. config is the current configuration object.""" from config_lib import Config from perroquet_config import config perroquet-1.1.1.orig/perroquetlib/config/config_lib.py0000644000175000017500000001373411561344350023336 0ustar georgeskgeorgesk# -*- coding: utf-8 -*- # Copyright (C) 2009-2010 Frédéric Bertolus. # Copyright (C) 2009-2010 Matthieu Bizien. # # This file is part of Perroquet. # # Perroquet 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 3 of the License, or # (at your option) any later version. # # Perroquet 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 Perroquet. If not, see . """Library used to create easily configuration""" import logging import os import sys from ConfigParser import ConfigParser #config_lib must not be dependant of perroquetlib defaultLoggingHandler = logging.StreamHandler(sys.stdout) defaultLoggingHandler.setFormatter(logging.Formatter("%(asctime)s.%(msecs)d-[%(name)s::%(levelname)s] %(message)s", "%a %H:%M:%S")) defaultLoggingLevel = logging.DEBUG class Parser(ConfigParser): """A general class to make parsers""" str2object = { "int": int, "string": lambda x: x, "stringlist": lambda s: s.split("\n"), "intlist": lambda s: [int(x) for x in s.split("\n")], "stringlistlist": lambda ll: [l.split("::") for l in ll.split("\n")] , } object2str = { "int": str, "string": lambda x: x, "stringlist": lambda l: ("\n").join(l), "intlist": lambda l: ("\n").join(str(i) for i in l), "stringlistlist": lambda ll: "\n".join("::".join(l) for l in ll)} def __init__(self): ConfigParser.__init__(self) def get_options(self): "return {option1: section1, ...}" return dict([(option, section) for section in self.sections() for option in self.options(section)]) def set(self, section, option, value): "set an option." string = self.object2str[section](value) ConfigParser.set(self, section, option, string) def get(self, section, option): "get an option value for a given section." return self.str2object[section](ConfigParser.get(self, section, option)) def items(self, section): """Return a list of tuples with (name, value) for each option in the section.""" return [(option, self.get(section, option)) for option in self.options(section)] def __str__(self): return ""+option+": "+value for section in self.sections() for option, value in ConfigParser.items(self, section))+">" class WritableParser(Parser): """A class that deal with writable parsers""" def __init__(self, path): Parser.__init__(self) self.path = path self.read(self.path) self._options = Parser.get_options(self) def save(self): "Only save the options that have changed" def mkpath(path): "Create the directeries components of a path" path2 = os.path.dirname(path) if not os.path.isdir(path2): mkpath(path2) os.mkdir(path2) mkpath(self.path) self.write(open(self.path, "w")) def set_if_existant_key(self, key, value): if key in self.get_options().keys(): section = self.get_options()[key] if not self.has_section(section): self.add_section(section) self.set(section, key, value) def set_options(self, dictionnary): self._options = dictionnary def get_options(self): return self._options class Config: """Usage: config = Config() Warning: all keys must be lowercase""" def __init__(self): self._properties = {} self._writableParsers = [] self.logger = logging.Logger("Config") self.logger.setLevel(defaultLoggingLevel) self.logger.addHandler(defaultLoggingHandler) def load_config_file(self, path): """load an ini config file that can't be modified.""" parser = Parser() if not os.path.isfile(path): raise IOError, path parser.read(path) for (option, section) in parser.get_options().items(): self.set(option, parser.get(section, option)) def load_writable_config_file(self, writablePath, referencePath): """load an ini config file that can be modified.""" #localParser exists because we din't want to copy referencePath to # the wirtablePath in case the config change #load parser = Parser() if not os.path.isfile(referencePath): raise IOError, referencePath parser.read(referencePath) writableOptions = parser.get_options() parser.read(writablePath) #Write for (option, section) in writableOptions.items(): self.set(option, parser.get(section, option)) #Remember localParser = WritableParser(writablePath) localParser.set_options(writableOptions) self._writableParsers.append(localParser) def get(self, key): """get a propertie""" if not key.islower(): raise KeyError, key + " is not lowercase" return self._properties.get(key,None) def set(self, key, value): """set a propertie""" if not key.islower(): raise KeyError, key + " is not lowercase" self._properties[key] = value for writableParser in self._writableParsers: writableParser.set_if_existant_key(key, value) def save(self): """Save the properties that have changed""" for writableParser in self._writableParsers: writableParser.save() def __str__(self): return str(self._properties).replace(", ", "\n") def __repr__(self): return "" perroquet-1.1.1.orig/perroquetlib/__init__.py0000644000175000017500000000000011561344350021513 0ustar georgeskgeorgeskperroquet-1.1.1.orig/perroquetlib/model/0000755000175000017500000000000011561344350020514 5ustar georgeskgeorgeskperroquet-1.1.1.orig/perroquetlib/model/languages_manager.py0000644000175000017500000000631311561344350024531 0ustar georgeskgeorgesk#! /usr/bin/python # -*- coding: utf-8 -*- # Copyright (C) 2009-2010 Frédéric Bertolus. # Copyright (C) 2009-2010 Matthieu Bizien. # # This file is part of Perroquet. # # Perroquet 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 3 of the License, or # (at your option) any later version. # # Perroquet 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 Perroquet. If not, see . import warnings from perroquetlib.config import config class Language: def __init__(self, id, name, availableChars, helpChar="~"): self.id = id self.name = name self.availableChars = availableChars self.helpChar = helpChar self._aliases = [] try: synonyms_list = config.get("synonyms." + self.id) except KeyError: warnings.warn("No aliases defined for language " + self.id, UserWarning) else: for synonyms in synonyms_list: self.add_synonyms(synonyms) def add_alias(self, alias, meaning): self._aliases.append((alias, meaning)) def add_synonyms(self, l): for alias in l: for meaning in (e for e in l if e != alias): self.add_alias(alias, meaning) def is_alias(self, alias, meaning): return (alias, meaning) in self._aliases class LanguagesManager: LOOK_FOR_ID = 0 LOOK_FOR_NAME = 1 LOOK_FOR_CHARLIST = 2 def __init__(self): self.config = config self.languageList = [] self._load() def get_languages_list(self): return self.languageList def get_language_by_id(self, idToFind): for language in self.languageList: if language.id == idToFind: return language return None def get_default_language(self): return self.languageList[0] def _load(self): path = self.config.get("languages_list_path"); self._parse_language_file(path) def _parse_language_file(self, path): self.languageList = [] f = open(path, 'r') state = LanguagesManager.LOOK_FOR_ID for line in f: line = line.rstrip() line = line.decode("utf8") if state == LanguagesManager.LOOK_FOR_ID: if len(line) > 0: current_id = line state = LanguagesManager.LOOK_FOR_NAME elif state == LanguagesManager.LOOK_FOR_NAME: if len(line) > 0: current_name = line state = LanguagesManager.LOOK_FOR_CHARLIST elif state == LanguagesManager.LOOK_FOR_CHARLIST: current_availableChars = line state = LanguagesManager.LOOK_FOR_ID language = Language(current_id, current_name, current_availableChars) self.languageList.append(language) perroquet-1.1.1.orig/perroquetlib/model/subtitles_loader.py0000644000175000017500000001400211561344350024427 0ustar georgeskgeorgesk# -*- coding: utf-8 -*- # Copyright (C) 2009-2010 Frédéric Bertolus. # # This file is part of Perroquet. # # Perroquet 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 3 of the License, or # (at your option) any later version. # # Perroquet 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 Perroquet. If not, see . import codecs import logging import re from perroquetlib.debug import defaultLoggingHandler, defaultLoggingLevel class SubtitlesLoader(object): LOOK_FOR_ID = 0 LOOK_FOR_TIME = 1 LOOK_FOR_TEXT = 2 sourceFormats = ['utf-8', 'ascii', 'iso-8859-1'] targetFormat = 'utf-8' outputDir = 'converted' def __init__(self): self.logger = logging.Logger(self.__class__.__name__) self.logger.setLevel(defaultLoggingLevel) self.logger.addHandler(defaultLoggingHandler) def convert_file(self, fileName): for format in self.sourceFormats: try: sourceFile = codecs.open(fileName, 'rU', format) convertedFile = [] for line in sourceFile: convertedFile.append(line.encode("utf-8")) return convertedFile except UnicodeDecodeError: pass self.logger.error("Error: failed to convert '" + fileName + "'.") def get_subtitle_list(self, path): outputList = [] f = self.convert_file(path) #f = open(path, 'r') state = SubtitlesLoader.LOOK_FOR_ID for line in f: line = line.rstrip() line = line.decode("utf8") #Remove BOM of utf-8 : EFBBBF if len(line) >= 3 and ord(line[0]) == 0xEF and ord(line[1]) == 0xBB and ord(line[2]) == 0xBF: line = line[3:] if state == SubtitlesLoader.LOOK_FOR_ID: if len(line) > 0: current = Subtitle() try: current.set_id(int(line)) state = SubtitlesLoader.LOOK_FOR_TIME except: pass elif state == SubtitlesLoader.LOOK_FOR_TIME: if len(line) > 0: #00:00:06,290 --> 00:00:07,550 regexp = '([0-9]{2}):([0-9]{2}):([0-9]{2}),([0-9]+) --> ([0-9]{2}):([0-9]{2}):([0-9]{2}),([0-9]+)' if re.search(regexp, line): m = re.search(regexp, line) beginMili = m.group(4) while len(beginMili) < 3: beginMili += "0" endMili = m.group(8) while len(endMili) < 3: endMili += "0" beginTime = int(m.group(1)) * 1000 * 3600 beginTime += int(m.group(2)) * 1000 * 60 beginTime += int(m.group(3)) * 1000 beginTime += int(beginMili) endTime = int(m.group(5)) * 1000 * 3600 endTime += int(m.group(6)) * 1000 * 60 endTime += int(m.group(7)) * 1000 endTime += int(endMili) current.set_time_begin(beginTime) current.set_time_end(endTime) current.set_text('') state = SubtitlesLoader.LOOK_FOR_TEXT elif state == SubtitlesLoader.LOOK_FOR_TEXT: if len(line) > 0: if len(current.get_text()) == 0: current.set_text(line) else: current.set_text(current.get_text() + " " + line) else: state = SubtitlesLoader.LOOK_FOR_ID if len(current.get_text()) > 0: outputList.append(current) if len(current.get_text()) > 0: outputList.append(current) return outputList def compact_subtitles_list(self, list, timeToMerge, maxTime): outputList = [] id = 0 current = None for sub in list: if id == 0 or (sub.get_time_begin() - current.get_time_end() > int(timeToMerge * 1000)) or (sub.get_time_end() - current.get_time_begin() > int(maxTime * 1000)): if id > 0: outputList.append(current) id += 1 current = Subtitle() current.set_id(id) current.set_text(sub.get_text()) current.set_time_begin(sub.get_time_begin()) current.set_time_end(sub.get_time_end()) else: current.set_text(current.get_text() + " " + sub.get_text()) current.set_time_end(sub.get_time_end()) if current: outputList.append(current) return outputList class Subtitle(object): def __init__(self): self.text = "" def get_text(self): return self.text def get_time_begin(self): return self.timeBegin def get_time_end(self): return self.timeEnd def get_id(self): return self.id def set_text(self, text): # | mean new line in some srt files text = text.replace("|", "\n") #Some subs have ... or {]@^\`}. We need to delete them. text = re.sub("(<" "[^>]*" ">)" "|" "(\{" "[^\}]*" "\})" , "", text) self.text = text def set_time_begin(self, timeBegin): self.timeBegin = timeBegin def set_time_end(self, timeEnd): self.timeEnd = timeEnd def set_id(self, id): self.id = id perroquet-1.1.1.orig/perroquetlib/model/sequence/0000755000175000017500000000000011561344350022324 5ustar georgeskgeorgeskperroquet-1.1.1.orig/perroquetlib/model/sequence/sequence_simple.py0000644000175000017500000000756311561344350026072 0ustar georgeskgeorgesk# -*- coding: utf-8 -*- # Copyright (C) 2009-2010 Frédéric Bertolus. # # This file is part of Perroquet. # # Perroquet 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 3 of the License, or # (at your option) any later version. # # Perroquet 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 Perroquet. If not, see . from perroquetlib.model.sequence.sequence import Sequence from perroquetlib.model.sequence.word import NoCharPossible class SequenceSimple(Sequence): """Sequence which dont't lock valids words""" def select_sequence_word(self, wordIndex, wordIndexPos): self.get_active_word().set_pos(wordIndexPos) self.set_active_word_index(wordIndex) def is_valid(self): """An uncorrected sequence is never valid""" return False def delete_next_char(self): try: self.get_active_word().delete_next_char() except NoCharPossible: if self.get_active_word_index() < self.get_word_count(): self.next_word() self.delete_next_char() self.update_after_write() def next_char(self): try: self.get_active_word().next_char() except NoCharPossible: current_word = self.get_active_word_index() self.next_word() if current_word != self.get_active_word_index(): self.get_active_word().set_pos(0); def previous_char(self): try: self.get_active_word().previous_char() except NoCharPossible: current_word = self.get_active_word_index() self.previous_word() if current_word != self.get_active_word_index(): self.get_active_word().set_pos(-1); def delete_previous_char(self): try: self.get_active_word().delete_previous_char() except NoCharPossible: if self.get_active_word_index() > 0: self.previous_word() self.delete_previous_char() self.update_after_write() def first_word(self): self._activeWordIndex = 0 self.get_active_word().set_pos(0) def last_word(self): self._activeWordIndex = self.get_last_index() self.get_active_word().set_pos(self.get_active_word().get_last_pos()) def next_word(self, loop=False): "go to the next word" if self.get_active_word_index() < self.get_last_index(): self._activeWordIndex += 1 self.get_active_word().set_pos(0) else: self.get_active_word().set_pos(-1) if not loop: pass else: raise NotImplementedError def previous_word(self, loop=False): "go to the previous word" if self.get_active_word_index() > 0: self._activeWordIndex -= 1 self.get_active_word().set_pos( self.get_active_word().get_last_pos()) else: self.get_active_word().set_pos(0) if not loop: pass else: raise NotImplementedError def write_char(self, char): self.get_active_word().write_char(char) self.update_after_write() def _write_sentence(self, sentence): """write many chars. a ' ' mean next word. Only for tests""" for char in sentence: if char == " ": pass elif char == "+": self.next_word() else: self.write_char(char) perroquet-1.1.1.orig/perroquetlib/model/sequence/sequence.py0000644000175000017500000001773011561344350024516 0ustar georgeskgeorgesk# -*- coding: utf-8 -*- # Copyright (C) 2009-2010 Frédéric Bertolus. # # This file is part of Perroquet. # # Perroquet 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 3 of the License, or # (at your option) any later version. # # Perroquet 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 Perroquet. If not, see . """A module that deal with words. Usage: from sequence import Sequence""" import re from word import Word class Sequence: """A class that implement a sequence manipulation with a reference""" def __init__(self, language): # self._symbolList = what is between words (or "") # self._wordList = a list of Words items that we want to find # self._workValidityList = 1 if the word if good, 0 if it is empty # -levenshtein between it and the normal word otherwise # # self._activeWordIndex = the word that is currently being edited # self.get_active_word().get_pos() = the position in that word # # self._helpChar = the char printed when you want a hint # # Note: self._symbolList, self._wordList # have always the same length self._symbolList = [] self._wordList = [] self._activeWordIndex = 0 self._helpChar = '~' self.language = language allChar = self.language.availableChars self.validChar = "[" + allChar + "]" self.notValidChar = "[^" + allChar + "]" self.repeat_count = 0 self.beginTime = 0 self.endTime = 0 def load(self, text): textToParse = text #We want swswsw... (s=symbol, w=word) #So if the 1st char isn't a symbole, we want an empty symbole if re.match(self.validChar, textToParse[0]): self._symbolList.append('') while len(textToParse) > 0: # if the text begin with a word followed by a not word char if re.match('^(' + self.validChar + '+)' + self.notValidChar, textToParse): m = re.search('^(' + self.validChar + '+)' + self.notValidChar, textToParse) word = m.group(1) sizeToCut = len(word) textToParse = textToParse[sizeToCut:] self._wordList.append(Word(word, self.language)) # if the text begin with no word char but followed by a word elif re.match('^(' + self.notValidChar + '+)' + self.validChar, textToParse): m = re.search('^(' + self.notValidChar + '+)' + self.validChar, textToParse) symbol = m.group(1) sizeToCut = len(symbol) textToParse = textToParse[sizeToCut:] self._symbolList.append(symbol) # if there is only one word or one separator else: # if there is only one word if re.match('^(' + self.validChar + '+)', textToParse): self._wordList.append(Word(textToParse, self.language)) # if there is only one separator else: self._symbolList.append(textToParse) break def get_symbols(self): return self._symbolList def get_words(self): return self._wordList def get_word_count(self): return len(self._wordList) def get_active_word_index(self): return self._activeWordIndex def set_active_word_index(self, index): if index == -1: index = self.get_word_count() if index < 0 or index > self.get_word_count(): raise AttributeError, str(index) self._activeWordIndex = index def get_last_index(self): return len(self._wordList) - 1 def get_active_word(self): return self.get_words()[self.get_active_word_index()] def get_word_found(self): return len([w for w in self.get_words() if w.is_valid()]) def next_word(self, loop=False): "go to the next non valid word " def previous_word(self, loop=False): "go to the previous non valid word" def select_sequence_word(self, wordIndex, wordIndexPos): """Go to the first editable position after the position of wordIndex word and wordIndexPos character""" raise NotImplementedError def write_char(self, char): raise NotImplementedError def _write_sentence(self, sentence): """write many chars. a ' ' mean next word. Only for tests""" raise NotImplementedError def delete_next_char(self): """delete the next deletable character""" raise NotImplementedError def delete_previous_char(self): """delete the previous deletable character""" raise NotImplementedError def first_word(self): """goto first editable character""" raise NotImplementedError def last_word(self): """goto last editable character""" raise NotImplementedError def next_char(self): """goto next editable character""" raise NotImplementedError def previous_char(self): """goto to previous editable character""" raise NotImplementedError def is_valid(self): """Return True if the entire sequence is valid, else return False""" raise NotImplementedError def is_empty(self): return all(w.is_empty() for w in self.get_words()) def complete_all(self): """Reveal all words""" for w in self.get_words(): w.complete() def complete_word(self): self.get_active_word().complete() def reset(self): "RAZ the current seq" for w in self.get_words(): w.reset() def update_after_write(self): "update after a modification of the text" pass def get_time_begin(self): return self.beginTime def get_time_end(self): return self.endTime def set_time_begin(self, time): self.beginTime = time def set_time_end(self, time): self.endTime = time # Reveal a letter on the first invalid word after the cursor. # Warning: recusive method def show_hint(self): if not self.get_active_word().is_valid(): # Reveal a letter on the current word self.get_active_word().show_hint() if self.get_active_word().is_valid(): self.next_word() else: # The current word is complete try the next word if self.get_active_word_index() == self.get_word_count()-1: # The current word is the last word, so do nothing return else: # There is a next word, try the hint on the next word self.next_word() self.show_hint() def set_repeat_count(self, repeat_count): self.repeat_count = repeat_count def get_repeat_count(self): return self.repeat_count def update_cursor_position(self): if not self.is_valid(): if self.get_active_word().is_valid(): # if the sequence is invalide and the current word valid, # go to the end of the word. # Usefull with alias, when 2 is replace with two, the cursor is # at the position 1 so is displayed after the t self.get_active_word().end() def __print__(self): return "-".join(w.get_text() for w in self.get_words()) + " VS " + \ "-".join(w.get_valid() for w in self.get_words()) def __repr__(self): return self.__print__() perroquet-1.1.1.orig/perroquetlib/model/sequence/word.py0000644000175000017500000002063611561344350023660 0ustar georgeskgeorgesk# -*- coding: utf-8 -*- # Copyright (C) 2009-2010 Frédéric Bertolus. # # This file is part of Perroquet. # # Perroquet 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 3 of the License, or # (at your option) any later version. # # Perroquet 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 Perroquet. If not, see . """A module that deal with words. Usage: from word import Word""" class NoCharPossible(Exception): """exception raised when we can't do the selected action because they is no character at the right place""" pass def levenshtein(a, b): "Calculates the Levenshtein distance between a and b." n, m = len(a), len(b) if n > m: # Make sure n <= m, to use O(min(n,m)) space a, b = b, a n, m = m, n text = range(n + 1) for i in range(1, m + 1): previous, text = text, [i] + [0] * n for j in range(1, n + 1): add, delete = previous[j] + 1, text[j-1] + 1 change = previous[j-1] if a[j-1] != b[i-1]: change = change + 1 text[j] = min(add, delete, change) return text[n] class Word: """A class that implement a word manipulation with a reference text -> the word that is currently written valid -> the word we want to find NB: texts are stored lowercases""" def __init__(self, validText, language): if " " in validText: raise AttributeError, "validText=' '" self._text = "" self._valid = validText self.language = language self._helpChar = self.language.helpChar self._pos = 0 def levenshtein(self): "get the levenshtein distance between the current word an the valid one" return levenshtein(self.get_text(), self.get_valid()) def get_begin_right(self): """Check if the first chars of self.get_valid() is self.get_text())""" return self.get_valid().startswith(self.get_text(helpChar=False)) def get_score(self): """Show if we are near the solution. return a float from -1 to 1. more is better""" #score1 and score2 are between -1 and 1 score1_ = (2. * len(self.get_valid()) - 2 * self.levenshtein() - \ len(self.get_text(helpChar=False))) / \ max(len(self.get_valid()), len(self.get_text(helpChar=False))) score1 = max(score1_, -1) score2 = self.get_begin_right() return (score1 * 8 + score2 * 2) / 10 def is_equal(self, text): "check is the current word is equal or means the text word" return (self.get_text() == text or self.language.is_alias(self.get_text(), text)) def is_valid(self): "check is the current word is valid" return self.is_equal(self.get_valid()) def is_empty(self): "check is the current word is empty" return self.get_text() == "" def complete(self): "Reveal the correction" self.set_text(self.get_valid()) def reset(self): "RAZ the current word" self.set_text("") def write_char(self, char): "write a char at the current position" char = char.lower() if len(char) != 1 or char == " ": raise AttributeError, "char='" + char + "'" #if replacing an helpChar if self.get_pos() < len(self.get_text()) and \ self.get_text()[self.get_pos()] == self._helpChar: self.set_text(self.get_text()[:self.get_pos()] + char + \ self.get_text()[self.get_pos() + 1:]) else: if self._helpChar in self.get_text(): #removing helps chars self.set_text("".join(c for c in self.get_text() if c != self._helpChar)) self.set_text(self.get_text()[:self.get_pos()] + char + self.get_text()[self.get_pos():]) self._pos += 1 # if the word is valid, replace text by the alias if self.is_valid(): if self.language.is_alias(self.get_text(), self.get_valid()): self.set_text(self.get_valid()) def show_hint(self): """Reveal correction for word at cursor in text sequence""" outWord = "" # Place "~" on wrong or missing character for i in range(0, len(self.get_valid())): if i < len(self.get_text()) and \ self.get_text()[i] == self.get_valid()[i]: outWord += self.get_valid()[i] else: outWord += self._helpChar if not self.is_valid(): # Reveal the first character only if the size was correct because # the first hint reveal only the size if len(self.get_text()) == len(self.get_valid()): first_error = self.get_first_error_index() self.set_text(outWord[:first_error] + self.get_valid()[first_error] + \ outWord[first_error + 1:]) else: self.set_text(outWord) # Place the cursor on the new first wrong character first_error = self.get_first_error_index() if first_error: self.set_pos(first_error) def get_first_error_index(self): """ Return the index of the first worng character. If the word is valid, return None """ for i in range(0, len(self.get_valid())): if i >= len(self.get_text()) or \ self.get_text()[i] != self.get_valid()[i]: #The character at offset i is invalid return i #No error found return None def delete_previous_char(self): "delete the char before the current pos" if self.get_pos() == 0 or self.get_text() == "": raise NoCharPossible else: self.set_text(self.get_text()[:self.get_pos()-1] + self.get_text()[self.get_pos():]) self._pos -= 1 def delete_next_char(self): "delete the char after the current pos" if self.get_pos() == len(self.get_text()) or self.get_text() == "": raise NoCharPossible else: self.set_text(self.get_text()[:self.get_pos()] + self.get_text()[self.get_pos() + 1:]) def set_text(self, text): "set the text in the word" if " " in text: raise AttributeError self._text = text.lower() def get_text(self, helpChar=True): """get what is writen. if not helpChar (default=True) then don't show the help characters """ if helpChar: return self._text.lower() else: return "".join([i for i in self.get_text() if i != self._helpChar]) def get_valid(self, lower=True): "get the valid word" if lower: return self._valid.lower() else: return self._valid def set_pos(self, pos): "if pos=-1, set at the last position" if pos > len(self.get_text()) or -2 >= pos: raise NoCharPossible elif pos == -1: self._pos = len(self.get_text()) else: self._pos = pos def get_pos(self): "return the position" return self._pos def get_last_pos(self): "return the last possible position" return len(self.get_text()) def next_char(self): "go to the next char" if self.get_pos() < self.get_last_pos(): self._pos += 1 else: raise NoCharPossible def end(self): self.set_pos(self.get_last_pos()) def previous_char(self): "go to the previous char" if self.get_pos() > 0: self._pos -= 1 else: raise NoCharPossible def __print__(self): return str(self.is_valid()) + " " + self.get_text() + " instead of " + \ self.get_valid() def __repr__(self): return self.get_text() + " VS " + self.get_valid() perroquet-1.1.1.orig/perroquetlib/model/sequence/__init__.py0000644000175000017500000000163411561344350024441 0ustar georgeskgeorgesk# -*- coding: utf-8 -*- # Copyright (C) 2009-2010 Frédéric Bertolus. # # This file is part of Perroquet. # # Perroquet 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 3 of the License, or # (at your option) any later version. # # Perroquet 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 Perroquet. If not, see . from sequence import Sequence from sequence_dynamic_correction import SequenceDynamicCorrection from sequence_simple import SequenceSimple from word import NoCharPossible, Word, levenshtein perroquet-1.1.1.orig/perroquetlib/model/sequence/sequence_dynamic_correction.py0000644000175000017500000001546311561344350030452 0ustar georgeskgeorgesk# -*- coding: utf-8 -*- # Copyright (C) 2009-2010 Frédéric Bertolus. # # This file is part of Perroquet. # # Perroquet 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 3 of the License, or # (at your option) any later version. # # Perroquet 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 Perroquet. If not, see . from perroquetlib.model.sequence.sequence import Sequence from perroquetlib.model.sequence.word import NoCharPossible class SequenceDynamicCorrection(Sequence): """Sequence which lock valids words""" def is_valid(self): """Return True if the entire sequence is valid, else return False""" return all(w.is_valid() for w in self.get_words()) def select_sequence_word(self, wordIndex, wordIndexPos): self.set_active_word_index(wordIndex) self.get_active_word().set_pos(wordIndexPos) if self.get_active_word().is_valid(): self.next_word() def delete_next_char(self): try: self.get_active_word().delete_next_char() except NoCharPossible: current_word_index = self.get_active_word_index() self.next_word() if current_word_index != self.get_active_word_index(): self.delete_next_char() self.update_after_write() def delete_previous_char(self): if not self.get_active_word().is_valid(): try: self.get_active_word().delete_previous_char() except NoCharPossible: current_word_index = self.get_active_word_index() self.previous_word() if current_word_index != self.get_active_word_index(): self.delete_previous_char() else: self._previous_word() if self.get_active_word_index() > 0: self.delete_previous_char() self.update_after_write() def next_char(self): try: self.get_active_word().next_char() except NoCharPossible: current_word = self.get_active_word_index() self.next_word() if current_word != self.get_active_word_index(): self.get_active_word().set_pos(0); def previous_char(self): if self.get_active_word().is_valid(): current_word = self.get_active_word_index() self.previous_word() if current_word != self.get_active_word_index(): self.get_active_word().end() else: try: self.get_active_word().previous_char() except NoCharPossible: current_word = self.get_active_word_index() self.previous_word() if current_word != self.get_active_word_index(): self.get_active_word().end() def first_word(self): self._activeWordIndex = 0 self.get_active_word().set_pos(0) if self.get_active_word().is_valid() and not self.is_valid(): self.next_word() self.get_active_word().set_pos(0) def last_word(self): self._activeWordIndex = self.get_last_index() self.get_active_word().set_pos(self.get_active_word().get_last_pos()) if self.get_active_word().is_valid() and not self.is_valid(): self.previous_word() self.get_active_word().set_pos(-1) def update_after_write(self): "update after a modification of the text" self._check_location() def write_char(self, char): if not self.get_active_word().is_valid(): self.get_active_word().write_char(char) else: self.next_word() if not self.get_active_word().is_valid(): self.write_char(char) else: #Nothing to write return self.update_after_write() def _check_location(self): """Check if a word is correct but at a wrong place.""" for w1 in self.get_words(): for j, w2 in enumerate(self.get_words()): if w1.get_score() <= 0 and w1.is_equal(w2.get_valid()) and not w2.is_valid(): w2.set_text(w1.get_text()) w1.set_text("") self.set_active_word_index(j) self.get_active_word().end() def _next_word(self, loop=False): "go to the next word" if self.get_active_word_index() < self.get_last_index(): self._activeWordIndex += 1 self.get_active_word().set_pos(0) else: if not loop: pass else: raise NotImplementedError def _previous_word(self, loop=False): "go to the previous word" if self.get_active_word_index() > 0: self._activeWordIndex -= 1 self.get_active_word().set_pos( self.get_active_word().get_last_pos()) else: if not loop: pass else: raise NotImplementedError def next_word(self, loop=False): "go to the next non valid word" current_word = self.get_active_word_index() self._next_word() if current_word == self.get_active_word_index(): self.get_active_word().set_pos(-1) if loop: raise NotImplementedError if self.get_active_word().is_valid(): if self.is_valid() or self.get_active_word_index() == self.get_last_index(): self.last_word(); return False return self.next_word() else: return True def previous_word(self, loop=False): "go to the previous non valid word" current_word = self.get_active_word_index() self._previous_word() if current_word == self.get_active_word_index(): self.get_active_word().set_pos(0) if loop: raise NotImplementedError if self.get_active_word().is_valid(): if self.is_valid() or self.get_active_word_index() == 0: self.first_word(); return False return self.previous_word() else: return True def _write_sentence(self, sentence): """write many chars. a ' ' mean next word. Only for tests""" for char in sentence: if char == " ": pass elif char == "+": self.next_word() else: self.write_char(char) perroquet-1.1.1.orig/perroquetlib/model/exercise_parser/0000755000175000017500000000000011561344350023677 5ustar georgeskgeorgeskperroquet-1.1.1.orig/perroquetlib/model/exercise_parser/__init__.py0000644000175000017500000000415211561344350026012 0ustar georgeskgeorgesk# -*- coding: utf-8 -*- # Copyright (C) 2009-2010 Frédéric Bertolus. # # This file is part of Perroquet. # # Perroquet 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 3 of the License, or # (at your option) any later version. # # Perroquet 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 Perroquet. If not, see . import logging import os.path from xml.dom.minidom import parse from lib import get_text from parser_v1_0_0 import load as load_v1_0_0, save as save_v1_0_0 from parser_v1_1_0 import load as load_v1_1_0, save as save_v1_1_0 from perroquetlib.debug import defaultLoggingHandler, defaultLoggingLevel from perroquetlib.model.exercise import Exercise def load_exercise(path): """Load a perroquet exercise.""" exercise = Exercise() logger = logging.Logger("load_exercise") logger.setLevel(defaultLoggingLevel) logger.addHandler(defaultLoggingHandler) dom = parse(path) if len(dom.getElementsByTagName("version")) > 0: version = get_text(dom.getElementsByTagName("version")[0].childNodes) if version >= "1.1.0": load_v1_1_0(exercise, dom, path) elif version >= "1.0.0": raise NotImplementedError load_v1_0_0(exercise, dom, path) else: logger.error("Unknown file version: " + version) exercise = None else: logger.error("Invalid perroquet file") exercise = None dom.unlink() return exercise def save_exercise(exercise, outputPath): """Save a perroquet exercise. exercise must be an empty perroquet Exercise or inherited""" if not os.path.isdir(os.path.dirname(outputPath)): os.makedirs(os.path.dirname(outputPath)) save_v1_1_0(exercise, outputPath) perroquet-1.1.1.orig/perroquetlib/model/exercise_parser/parser_v1_0_0.py0000644000175000017500000001137011561344350026613 0ustar georgeskgeorgesk# -*- coding: utf-8 -*- # Copyright (C) 2009-2010 Frédéric Bertolus. # # This file is part of Perroquet. # # Perroquet 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 3 of the License, or # (at your option) any later version. # # Perroquet 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 Perroquet. If not, see . import os from lib import get_text from perroquetlib.model.languages_manager import LanguagesManager from perroquetlib.model.sub_exercise import SubExercise VERSION = "1.0.0" def save(path): raise NotImplementedError def load(self, exercise, dom, path): #Name exercise.set_name(None) #Language languageManager = LanguagesManager() exercise.set_language_id(languageManager.get_default_language().id) #Template exercise.set_template(False) #Random order exercise.set_random_order(False) xml_progress = dom.getElementsByTagName("progress")[0] currentSequence = int(get_text(xml_progress.getElementsByTagName("current_sequence")[0].childNodes)) currentWord = int(get_text(xml_progress.getElementsByTagName("current_word")[0].childNodes)) xml_sequences = xml_progress.getElementsByTagName("sequences")[0] progress = [] for xml_sequence in xml_sequences.getElementsByTagName("sequence"): id = int(get_text(xml_sequence.getElementsByTagName("id")[0].childNodes)) state = get_text(xml_sequence.getElementsByTagName("state")[0].childNodes) words = [] repeat_count = 0 if state == "in_progress": xml_words = xml_sequence.getElementsByTagName("words")[0] for xml_world in xml_words.getElementsByTagName("word"): words.append(get_text(xml_world.childNodes)) progress.append((id, state, words, repeat_count)) # Stats xml_stats = dom.getElementsByTagName("stats")[0] exercise.set_repeat_count(int(get_text(xml_stats.getElementsByTagName("repeat_count")[0].childNodes))) #Subexercises subExos = [] subExercise = SubExercise(exercise) #Sequences progress = [] xml_progress = dom.getElementsByTagName("progress")[0] xml_sequences = xml_progress.getElementsByTagName("sequences")[0] for xml_sequence in xml_sequences.getElementsByTagName("sequence"): id = int(get_text(xml_sequence.getElementsByTagName("id")[0].childNodes)) state = get_text(xml_sequence.getElementsByTagName("state")[0].childNodes) words = [] if state == "in_progress": xml_words = xml_sequence.getElementsByTagName("words")[0] for xml_world in xml_words.getElementsByTagName("word"): words.append(get_text(xml_world.childNodes)) progress.append((id, state, words)) #Paths xml_paths = dom.getElementsByTagName("paths")[0] subExercise.set_video_path(get_text(xml_paths.getElementsByTagName("video")[0].childNodes)) subExercise.set_exercise_path(get_text(xml_paths.getElementsByTagName("exercice")[0].childNodes)) subExercise.set_translation_path(get_text(xml_paths.getElementsByTagName("translation")[0].childNodes)) exercise.subExercisesList.append(subExercise) subExos.append(progress) #Convert relative path for subExo in exercise.subExercisesList: if not os.path.isfile(subExo.get_exercise_path()): absPath = os.path.join(os.path.dirname(path), subExo.get_exercise_path()) if not os.path.isfile(absPath): subExo.set_exercise_path("") else: subExo.set_exercise_path(absPath) if not os.path.isfile(subExo.get_video_path()): absPath = os.path.join(os.path.dirname(path), subExo.get_video_path()) if not os.path.isfile(absPath): subExo.set_video_path("") else: subExo.set_video_path(absPath) if not os.path.isfile(subExo.get_translation_path()): absPath = os.path.join(os.path.dirname(path), subExo.get_translation_path()) if not os.path.isfile(absPath): subExo.set_translation_path("") else: subExo.set_translation_path(absPath) exercise.initialize() self.update_sequence_list() exercise.goto_sequence(currentSequence) exercise.get_current_sequence().set_active_word_index(currentWord) if not exercise.is_template(): exercise.set_output_save_path(path) return exercise perroquet-1.1.1.orig/perroquetlib/model/exercise_parser/lib.py0000644000175000017500000000327011561344350025021 0ustar georgeskgeorgesk# -*- coding: utf-8 -*- # Copyright (C) 2009-2010 Frédéric Bertolus. # # This file is part of Perroquet. # # Perroquet 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 3 of the License, or # (at your option) any later version. # # Perroquet 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 Perroquet. If not, see . def get_text(nodelist): """Extract the text of a node""" rc = "" for node in nodelist: if node.nodeType == node.TEXT_NODE: rc = rc + node.data rc = rc.strip() return rc def update_sequence_list(exercise, subExos): "set the sequences empty, partially done or done" for subExo, progress in zip(exercise.subExercisesList, subExos): sequenceList = subExo.get_sequence_list() for (id, state, words, repeat_count) in progress: if id >= len(sequenceList): break sequence = sequenceList[id] sequence.set_repeat_count(repeat_count) if state == "done": sequence.complete_all() elif state == "in_progress": i = 0 for word in words: if i >= sequence.get_word_count(): break sequence.get_words()[i].set_text(word) i = i + 1 perroquet-1.1.1.orig/perroquetlib/model/exercise_parser/parser_v1_1_0.py0000644000175000017500000004112711561344350026617 0ustar georgeskgeorgesk# -*- coding: utf-8 -*- # Copyright (C) 2009-2010 Frédéric Bertolus. # # This file is part of Perroquet. # # Perroquet 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 3 of the License, or # (at your option) any later version. # # Perroquet 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 Perroquet. If not, see . import os from xml.dom.minidom import getDOMImplementation from lib import get_text, update_sequence_list from perroquetlib.model.languages_manager import LanguagesManager from perroquetlib.model.sub_exercise import SubExercise VERSION = "1.1.0" def save(exercise, outputPath): impl = getDOMImplementation() newdoc = impl.createDocument(None, "perroquet", None) root_element = newdoc.documentElement # Version xml_version = newdoc.createElement("version") xml_version.appendChild(newdoc.createTextNode(VERSION)) root_element.appendChild(xml_version) #Name if exercise.get_name() != None: xml_node = newdoc.createElement("name") xml_node.appendChild(newdoc.createTextNode(exercise.get_name())) root_element.appendChild(xml_node) #Language if exercise.get_language_id() != None: xml_node = newdoc.createElement("language") xml_node.appendChild(newdoc.createTextNode(exercise.get_language_id())) root_element.appendChild(xml_node) #Template xml_node = newdoc.createElement("template") xml_node.appendChild(newdoc.createTextNode(str(exercise.is_template()))) root_element.appendChild(xml_node) #RandomOrder xml_node = newdoc.createElement("random_order") xml_node.appendChild(newdoc.createTextNode(str(exercise.is_random_order()))) root_element.appendChild(xml_node) #Locks xml_locks = newdoc.createElement("locks") if exercise.is_lock_help(): xml_lock = newdoc.createElement("help_lock") xml_locks.appendChild(xml_lock) if exercise.is_lock_properties(): xml_lock = newdoc.createElement("properties_lock") if exercise.lock_properties_password is not None or exercise.lock_properties_salt is not None: xml_node = newdoc.createElement("hash") xml_node.appendChild(newdoc.createTextNode(str(exercise.lock_properties_password))) xml_lock.appendChild(xml_node) xml_node = newdoc.createElement("salt") xml_node.appendChild(newdoc.createTextNode(str(exercise.lock_properties_salt))) xml_lock.appendChild(xml_node) xml_locks.appendChild(xml_lock) if exercise.is_lock_properties(): xml_lock = newdoc.createElement("properties_lock") if exercise.lock_properties_password is not None or exercise.lock_properties_salt is not None: xml_node = newdoc.createElement("hash") xml_node.appendChild(newdoc.createTextNode(str(exercise.lock_properties_password))) xml_lock.appendChild(xml_node) xml_node = newdoc.createElement("salt") xml_node.appendChild(newdoc.createTextNode(str(exercise.lock_properties_salt))) xml_lock.appendChild(xml_node) xml_locks.appendChild(xml_lock) root_element.appendChild(xml_locks) #Exercise xml_exercise = newdoc.createElement("exercise") xml_current_sequence = newdoc.createElement("current_sequence") xml_current_sequence.appendChild(newdoc.createTextNode(str(exercise.get_current_sequence_id()))) xml_exercise.appendChild(xml_current_sequence) xml_current_word = newdoc.createElement("current_word") xml_current_word.appendChild(newdoc.createTextNode(str(exercise.get_current_sequence().get_active_word_index()))) xml_exercise.appendChild(xml_current_word) root_element.appendChild(xml_exercise) #Exercise - SubExercises for subExo in exercise.subExercisesList: xml_subExo = newdoc.createElement("sub_exercise") xml_exercise.appendChild(xml_subExo) #Paths xml_paths = newdoc.createElement("paths") xml_subExo.appendChild(xml_paths) xml_video_paths = newdoc.createElement("video") xml_video_paths.appendChild(newdoc.createTextNode(subExo.get_video_export_path())) xml_paths.appendChild(xml_video_paths) xml_exercice_paths = newdoc.createElement("exercise") xml_exercice_paths.appendChild(newdoc.createTextNode(subExo.get_exercise_export_path())) xml_paths.appendChild(xml_exercice_paths) xml_translation_paths = newdoc.createElement("translation") xml_translation_paths.appendChild(newdoc.createTextNode(subExo.get_translation_export_path())) xml_paths.appendChild(xml_translation_paths) xml_sequences = newdoc.createElement("sequences") xml_subExo.appendChild(xml_sequences) for id, sequence in enumerate(subExo.get_sequence_list()): if sequence.is_valid(): xml_sequence = newdoc.createElement("sequence") xml_sequence_id = newdoc.createElement("id") xml_sequence_id.appendChild(newdoc.createTextNode(str(id))) xml_sequence.appendChild(xml_sequence_id) xml_sequence_state = newdoc.createElement("state") xml_sequence_state.appendChild(newdoc.createTextNode("done")) xml_sequence.appendChild(xml_sequence_state) repeat_count = sequence.get_repeat_count() xml_sequence_repeat_count = newdoc.createElement("repeat_count") xml_sequence_repeat_count.appendChild(newdoc.createTextNode(str(repeat_count))) xml_sequence.appendChild(xml_sequence_repeat_count) xml_sequences.appendChild(xml_sequence) elif not sequence.is_empty(): xml_sequence = newdoc.createElement("sequence") xml_sequence_id = newdoc.createElement("id") xml_sequence_id.appendChild(newdoc.createTextNode(str(id))) xml_sequence.appendChild(xml_sequence_id) xml_sequence_state = newdoc.createElement("state") xml_sequence_state.appendChild(newdoc.createTextNode("in_progress")) xml_sequence.appendChild(xml_sequence_state) repeat_count = sequence.get_repeat_count() xml_sequence_repeat_count = newdoc.createElement("repeat_count") xml_sequence_repeat_count.appendChild(newdoc.createTextNode(str(repeat_count))) xml_sequence.appendChild(xml_sequence_repeat_count) xml_sequence_words = newdoc.createElement("words") for word in (w.get_text() for w in sequence.get_words()): xml_sequence_word = newdoc.createElement("word") xml_sequence_word.appendChild(newdoc.createTextNode(word)) xml_sequence_words.appendChild(xml_sequence_word) xml_sequence.appendChild(xml_sequence_words) xml_sequences.appendChild(xml_sequence) #Stats xml_stats = newdoc.createElement("stats") xml_repeatCount = newdoc.createElement("repeat_count") xml_repeatCount.appendChild(newdoc.createTextNode(str(exercise.get_repeat_count()))) xml_stats.appendChild(xml_repeatCount) root_element.appendChild(xml_stats) #Properties xml_properties = newdoc.createElement("properties") xml_repeatAfterComplete = newdoc.createElement("repeat_after_complete") xml_repeatAfterComplete.appendChild(newdoc.createTextNode(str(exercise.get_repeat_after_completed()))) xml_properties.appendChild(xml_repeatAfterComplete) xml_use_dynamic_correction = newdoc.createElement("use_dynamic_correction") xml_use_dynamic_correction.appendChild(newdoc.createTextNode(str(exercise.is_use_dynamic_correction()))) xml_properties.appendChild(xml_use_dynamic_correction) xml_maxSequenceLength = newdoc.createElement("max_sequence_length") xml_maxSequenceLength.appendChild(newdoc.createTextNode(str(exercise.get_max_sequence_length()))) xml_properties.appendChild(xml_maxSequenceLength) xml_timeBetweenSequences = newdoc.createElement("time_between_sequence") xml_timeBetweenSequences.appendChild(newdoc.createTextNode(str(exercise.get_time_between_sequence()))) xml_properties.appendChild(xml_timeBetweenSequences) xml_playMarginBefore = newdoc.createElement("play_margin_before") xml_playMarginBefore.appendChild(newdoc.createTextNode(str(exercise.get_play_margin_before()))) xml_properties.appendChild(xml_playMarginBefore) xml_playMarginAfter = newdoc.createElement("play_margin_after") xml_playMarginAfter.appendChild(newdoc.createTextNode(str(exercise.get_play_margin_after()))) xml_properties.appendChild(xml_playMarginAfter) xml_repeat_count_by_sequence_limit = newdoc.createElement("repeat_count_by_sequence_limit") xml_repeat_count_by_sequence_limit.appendChild(newdoc.createTextNode(str(exercise.get_repeat_count_limit_by_sequence()))) xml_properties.appendChild(xml_repeat_count_by_sequence_limit) root_element.appendChild(xml_properties) xml_string = newdoc.toprettyxml() xml_string = xml_string.encode('utf8') f = open(outputPath, 'w') f.write(xml_string) f.close() def load(exercise, dom, path): #Name if len(dom.getElementsByTagName("name")) > 0: exercise.set_name(get_text(dom.getElementsByTagName("name")[0].childNodes)) #Language if len(dom.getElementsByTagName("language")) > 0: exercise.set_language_id(get_text(dom.getElementsByTagName("language")[0].childNodes)) else: languageManager = LanguagesManager() exercise.set_language_id(languageManager.get_default_language().id) #Template if len(dom.getElementsByTagName("template")) > 0: exercise.set_template(get_text(dom.getElementsByTagName("template")[0].childNodes) == "True") #Random order if len(dom.getElementsByTagName("random_order")) > 0: exercise.set_random_order(get_text(dom.getElementsByTagName("random_order")[0].childNodes) == "True") #Locks if len(dom.getElementsByTagName("locks")) > 0: xml_locks = dom.getElementsByTagName("locks")[0] #Correction lock if len(xml_locks.getElementsByTagName("correction_lock")) > 0: exercise.lock_correction = True xml_lock = xml_locks.getElementsByTagName("correction_lock")[0] if len(xml_lock.getElementsByTagName("hash")) > 0: exercise.lock_correction_password = get_text(xml_lock.getElementsByTagName("hash")[0].childNodes) if len(xml_lock.getElementsByTagName("salt")) > 0: exercise.lock_correction_salt = get_text(xml_lock.getElementsByTagName("salt")[0].childNodes) #Properties lock if len(xml_locks.getElementsByTagName("properties_lock")) > 0: exercise.lock_properties = True xml_lock = xml_locks.getElementsByTagName("properties_lock")[0] if len(xml_lock.getElementsByTagName("hash")) > 0: exercise.lock_properties_password = get_text(xml_lock.getElementsByTagName("hash")[0].childNodes) if len(xml_lock.getElementsByTagName("salt")) > 0: exercise.lock_properties_salt = get_text(xml_lock.getElementsByTagName("salt")[0].childNodes) #Help lock if len(xml_locks.getElementsByTagName("help_lock")) > 0: exercise.lock_help = True #Exercise xml_exercise = dom.getElementsByTagName("exercise")[0] #Exercise - CurrentWord currentWord = int(get_text(xml_exercise.getElementsByTagName("current_word")[0].childNodes)) #Exercise - CurrentSequence currentSequence = int(get_text(xml_exercise.getElementsByTagName("current_sequence")[0].childNodes)) # Stats xml_stats = dom.getElementsByTagName("stats")[0] exercise.set_repeat_count(int(get_text(xml_stats.getElementsByTagName("repeat_count")[0].childNodes))) # Properties if len(dom.getElementsByTagName("properties")) > 0: xml_properties = dom.getElementsByTagName("properties")[0] if len(xml_properties.getElementsByTagName("repeat_after_complete")) > 0: exercise.set_repeat_after_completed(get_text(xml_properties.getElementsByTagName("repeat_after_complete")[0].childNodes) == "True") if len(xml_properties.getElementsByTagName("time_between_sequence")) > 0: exercise.set_time_between_sequence(float(get_text(xml_properties.getElementsByTagName("time_between_sequence")[0].childNodes))) if len(xml_properties.getElementsByTagName("max_sequence_length")) > 0: exercise.set_max_sequence_length(float(get_text(xml_properties.getElementsByTagName("max_sequence_length")[0].childNodes))) if len(xml_properties.getElementsByTagName("play_margin_before")) > 0: exercise.set_play_margin_before(int(get_text(xml_properties.getElementsByTagName("play_margin_before")[0].childNodes))) if len(xml_properties.getElementsByTagName("play_margin_after")) > 0: exercise.set_play_margin_after(int(get_text(xml_properties.getElementsByTagName("play_margin_after")[0].childNodes))) if len(xml_properties.getElementsByTagName("use_dynamic_correction")) > 0: exercise.set_use_dynamic_correction(get_text(xml_properties.getElementsByTagName("use_dynamic_correction")[0].childNodes) == "True") if len(xml_properties.getElementsByTagName("repeat_count_by_sequence_limit")) > 0: exercise.set_repeat_count_limit_by_sequence(int(get_text(xml_properties.getElementsByTagName("repeat_count_by_sequence_limit")[0].childNodes))) #Subexercises subExos = [] for xml_subExercise in xml_exercise.getElementsByTagName("sub_exercise"): subExercise = SubExercise(exercise) #Sequences xml_sequences = xml_subExercise.getElementsByTagName("sequences")[0] progress = [] for xml_sequence in xml_sequences.getElementsByTagName("sequence"): id = int(get_text(xml_sequence.getElementsByTagName("id")[0].childNodes)) state = get_text(xml_sequence.getElementsByTagName("state")[0].childNodes) #Sequence repeat count if len(xml_sequence.getElementsByTagName("repeat_count")) > 0: repeat_count = int(get_text(xml_sequence.getElementsByTagName("repeat_count")[0].childNodes)) else: repeat_count = 0 words = [] if state == "in_progress": xml_words = xml_sequence.getElementsByTagName("words")[0] for xml_world in xml_words.getElementsByTagName("word"): words.append(get_text(xml_world.childNodes)) progress.append((id, state, words, repeat_count)) #Paths xml_paths = xml_subExercise.getElementsByTagName("paths")[0] subExercise.set_video_path(get_text(xml_paths.getElementsByTagName("video")[0].childNodes)) subExercise.set_exercise_path(get_text(xml_paths.getElementsByTagName("exercise")[0].childNodes)) subExercise.set_translation_path(get_text(xml_paths.getElementsByTagName("translation")[0].childNodes)) exercise.subExercisesList.append(subExercise) subExos.append(progress) #Convert relative path for subExo in exercise.subExercisesList: if not os.path.isfile(subExo.get_exercise_path()): absPath = os.path.join(os.path.dirname(path), subExo.get_exercise_path()) if not os.path.isfile(absPath): subExo.set_exercise_path("") else: subExo.set_exercise_path(absPath) if not os.path.isfile(subExo.get_video_path()): absPath = os.path.join(os.path.dirname(path), subExo.get_video_path()) if not os.path.isfile(absPath): subExo.set_video_path("") else: subExo.set_video_path(absPath) if not os.path.isfile(subExo.get_translation_path()): absPath = os.path.join(os.path.dirname(path), subExo.get_translation_path()) if not os.path.isfile(absPath): subExo.set_translation_path("") else: subExo.set_translation_path(absPath) exercise.initialize() update_sequence_list(exercise, subExos) exercise.goto_sequence(currentSequence) exercise.get_current_sequence().set_active_word_index(currentWord) if not exercise.is_template(): exercise.set_output_save_path(path) return exercise perroquet-1.1.1.orig/perroquetlib/model/exercise.py0000644000175000017500000003107011561344350022676 0ustar georgeskgeorgesk# -*- coding: utf-8 -*- # Copyright (C) 2009-2010 Frédéric Bertolus. # # This file is part of Perroquet. # # Perroquet 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 3 of the License, or # (at your option) any later version. # # Perroquet 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 Perroquet. If not, see . import copy import hashlib import random import re import string import os.path from languages_manager import LanguagesManager from perroquetlib.config import config from sub_exercise import SubExercise from subtitles_loader import SubtitlesLoader class Exercise(object): def __init__(self): self.subtitles = SubtitlesLoader() self.repeatCount = 0 self.currentSubExerciseId = 0 self.subExercisesList = [] self.currentSubExercise = None self.repeatAfterCompleted = True self.maxSequenceLength = 60.0 self.timeBetweenSequence = 0.0 self.outputSavePath = None self.template = False self.name = None self.mediaChangeCallback = None self.language = None self.randomOrder = False self.playMarginAfter = 500 self.playMarginBefore = 1000 self.use_dynamic_correction = True self.repeat_count_limit_by_sequence = 0 self.lock_help = False self.lock_properties = False self.lock_correction = False self.lock_properties_password = None self.lock_correction_password = None self.lock_properties_salt = None self.lock_correction_salt = None def initialize(self): self.__load_subtitles() if self.randomOrder: self.order = [x for x in range(self.get_sequence_count())] random.shuffle(self.order) self.reverseOrder = copy.copy(self.order) for i, j in enumerate(self.order): self.reverseOrder[j] = i def new(self): if len(self.subExercisesList) == 0: self.currentSubExercise = SubExercise(self) self.subExercisesList.append(self.currentSubExercise) self.currentSubExerciseId = 0 self.currentSequenceId = 0 languageManager = LanguagesManager() self.language = languageManager.get_default_language() self.maxSequenceLength = float(config.get("default_exercise_max_sequence_length")) / 1000 self.timeBetweenSequence = float(config.get("default_exercise_time_between_sequences")) / 1000 self.playMarginAfter = config.get("default_exercise_play_margin_before") self.playMarginBefore = config.get("default_exercise_play_margin_after") self.repeatAfterCompleted = (config.get("default_exercise_repeat_after_completed") == 1) self.randomOrder = (config.get("default_exercise_random_order") == 1) self.use_dynamic_correction = (config.get("default_exercise_dynamic_correction") == 1) self.set_language_id(config.get("default_exercise_language")) self.repeat_count_limit_by_sequence = int(config.get("default_repeat_count_limit_by_sequence")) def __load_subtitles(self): for subExo in self.subExercisesList: subExo.load_subtitles() # Reset the work done in the exercise def reset(self): for sequence in self.get_sequence_list(): sequence.reset() def extract_word_list(self): wordList = [] for subExo in self.subExercisesList: wordList = wordList + subExo.extract_word_list() #Remove double words and sort wordList = list(set(wordList)) wordList.sort() return wordList def goto_sequence(self, id): self.currentSequenceId = id localId = id for subExo in self.subExercisesList: if localId < len(subExo.get_sequence_list()): subExo.set_current_sequence(localId) if self.currentSubExercise != subExo: self.currentSubExercise = subExo self.notify_media_change() return True else: localId -= len(subExo.get_sequence_list()) self.goto_sequence(self.get_sequence_count()-1) return False def goto_next_sequence(self): if self.randomOrder: randomId = self.reverseOrder[self.currentSequenceId] randomId += 1 if randomId >= len(self.order): randomId = 0 return self.goto_sequence(self.order[randomId]) else: return self.goto_sequence(self.currentSequenceId + 1) def goto_previous_sequence(self): if self.randomOrder: randomId = self.reverseOrder[self.currentSequenceId] randomId -= 1 if randomId < 0: randomId = len(self.order)-1 return self.goto_sequence(self.order[randomId]) else: if self.currentSequenceId > 0: return self.goto_sequence(self.currentSequenceId-1) else: return False def goto_next_valid_sequence(self): if not self.goto_next_sequence(): return False else: if not self.get_current_sequence().is_valid(): return True else: return self.goto_next_valid_sequence() def goto_previous_valid_sequence(self): if not self.goto_previous_sequence(): return False else: if not self.get_current_sequence().is_valid(): return True else: return self.goto_previous_valid_sequence() def is_paths_valid(self): error = False errorList = [] for subExo in self.subExercisesList: (valid, subErrorList) = subExo.is_paths_valid() if not valid: error = True errorList = errorList + subErrorList return (not error), errorList def increment_repeat_count(self): self.repeatCount += 1 def set_video_path(self, videoPath): self.currentSubExercise.set_video_path(videoPath) if not self.get_name(): self.set_name(os.path.basename(videoPath)) def set_exercise_path(self, exercisePath): self.currentSubExercise.set_exercise_path(exercisePath) def set_translation_path(self, translationPath): self.currentSubExercise.set_translation_path(translationPath) def get_current_sequence(self): return self.currentSubExercise.get_current_sequence() def get_current_sequence_id(self): return self.currentSequenceId def get_sequence_list(self): list = [] for subExo in self.subExercisesList: list += subExo.get_sequence_list() return list def get_sequence_count(self): count = 0 for subExo in self.subExercisesList: count += subExo.get_sequence_count() return count def set_repeat_count(self, count): self.repeatCount = count def get_repeat_count(self): return self.repeatCount def get_video_path(self): return self.currentSubExercise.get_video_path() def get_exercise_path(self): return self.currentSubExercise.get_exercise_path() def get_translation_path(self): return self.currentSubExercise.get_translation_path() def get_translation_list(self): return self.currentSubExercise.get_translation_list() def set_repeat_after_completed(self, state): self.repeatAfterCompleted = state def get_repeat_after_completed(self): return self.repeatAfterCompleted def set_time_between_sequence(self, time): self.timeBetweenSequence = time def get_time_between_sequence(self): return self.timeBetweenSequence def set_max_sequence_length(self, time): self.maxSequenceLength = time def get_max_sequence_length(self): return self.maxSequenceLength def get_output_save_path(self): return self.outputSavePath def set_output_save_path(self, outputSavePath): self.outputSavePath = outputSavePath self.set_template(False) def get_name(self): return self.name def set_name(self, name): self.name = name def is_template(self): return self.template def set_template(self, is_template): self.template = is_template def is_random_order(self): return self.randomOrder def set_random_order(self, is_random_order): self.randomOrder = is_random_order def set_media_change_callback(self, mediaChangeCallback): self.mediaChangeCallback = mediaChangeCallback def notify_media_change(self): if self.mediaChangeCallback != None: self.mediaChangeCallback() def set_language_id(self, langId): languageManager = LanguagesManager() self.language = languageManager.get_language_by_id(langId) def get_language_id(self): return self.language.id def is_character_match(self, char): langAvailableChars = self.language.availableChars return re.match('^[' + langAvailableChars + ']$', char) def get_play_margin_before(self): return self.playMarginBefore def set_play_margin_before(self, margin): self.playMarginBefore = margin def get_play_margin_after(self): return self.playMarginAfter def set_play_margin_after(self, margin): self.playMarginAfter = margin def is_use_dynamic_correction(self): return self.use_dynamic_correction def set_use_dynamic_correction(self, use): self.use_dynamic_correction = use def set_repeat_count_limit_by_sequence(self, repeat_count_limit): self.repeat_count_limit_by_sequence = repeat_count_limit def get_repeat_count_limit_by_sequence(self): return self.repeat_count_limit_by_sequence def is_current_sequence_repeat_limit_reach(self): return self.get_repeat_count_limit_by_sequence() != 0 and self.get_current_sequence().get_repeat_count() >= self.get_repeat_count_limit_by_sequence() def increment_current_sequence_repeat_count(self): self.get_current_sequence().set_repeat_count(self.get_current_sequence().get_repeat_count() + 1) def clear_sequence_repeat_count(self): for sequence in self.get_sequence_list(): sequence.set_repeat_count(0) def is_lock_properties(self): return self.lock_properties def is_lock_properties_password(self): return self.lock_properties_salt != None def set_lock_properties(self, state, new_password=None): self.lock_properties = state if new_password is not None: salt = "" pop = string.hexdigits while len(salt) < 6: salt += random.choice(pop) self.lock_properties_password = self.hash(salt, new_password) self.lock_properties_salt = salt else: self.lock_properties_salt = None self.lock_properties_password = None def verify_lock_properties_password(self, password): return self.lock_properties_password == self.hash(self.lock_properties_salt, password) def is_lock_correction(self): return self.lock_correction def is_lock_correction_password(self): return self.lock_correction_salt != None def set_lock_correction(self, state, new_password=None): self.lock_correction = state if new_password is not None: salt = "" pop = string.hexdigits while len(salt) < 6: salt += random.choice(pop) self.lock_correction_password = self.hash(salt, new_password) self.lock_correction_salt = salt else: self.lock_corrections_salt = None self.lock_correction_password = None def verify_lock_correction_password(self, password): return self.lock_correction_password == self.hash(self.lock_correction_salt, password) def hash(self, salt, password): """Compute the hashed password for the salt and the password""" m = hashlib.sha256() m.update(salt + password) return m.hexdigest() def is_lock_help(self): return self.lock_help def set_lock_help(self, state): self.lock_help = state perroquet-1.1.1.orig/perroquetlib/model/__init__.py0000644000175000017500000000000011561344350022613 0ustar georgeskgeorgeskperroquet-1.1.1.orig/perroquetlib/model/sub_exercise.py0000644000175000017500000001577011561344350023560 0ustar georgeskgeorgesk# -*- coding: utf-8 -*- # Copyright (C) 2009-2010 Frédéric Bertolus. # # This file is part of Perroquet. # # Perroquet 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 3 of the License, or # (at your option) any later version. # # Perroquet 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 Perroquet. If not, see . import os from perroquetlib.model.sequence import SequenceDynamicCorrection from perroquetlib.model.sequence import SequenceSimple from subtitles_loader import SubtitlesLoader class SubExercise(object): def __init__(self, parent): self.subtitles = SubtitlesLoader() self.currentSequenceId = 0 self.sequenceList = [] self.parent = parent self.videoExportPath = None self.exerciseExportPath = None self.translationExportPath = None def load_subtitles(self): self.subList = self.subtitles.get_subtitle_list(self.exercisePath) self.subList = self.subtitles.compact_subtitles_list(self.subList, self.parent.timeBetweenSequence, self.parent.maxSequenceLength) self.translationList = None if self.translationPath != "": self.translationList = self.subtitles.get_subtitle_list(self.translationPath) oldSequenceList = self.sequenceList self.sequenceList = [] for sub in self.subList: if self.parent.is_use_dynamic_correction(): sequence = SequenceDynamicCorrection(self.parent.language) else: sequence = SequenceSimple(self.parent.language) sequence.load(sub.get_text()) sequence.set_time_begin(sub.get_time_begin()) sequence.set_time_end(sub.get_time_end()) self.sequenceList.append(sequence) #Restore found words if len(oldSequenceList) > 0: oldSequenceIndex = 0 newSequenceIndex = 0 oldWordIndex = 0 newWordIndex = 0 while oldSequenceIndex < len(oldSequenceList) and newSequenceIndex < len(self.sequenceList): if oldWordIndex >= oldSequenceList[oldSequenceIndex].get_word_count(): oldSequenceIndex += 1 oldWordIndex = 0 if oldSequenceIndex >= len(oldSequenceList): break if newWordIndex >= len(self.sequenceList[newSequenceIndex].get_words()): newSequenceIndex += 1 newWordIndex = 0 if newSequenceIndex >= len(self.sequenceList): break self.sequenceList[newSequenceIndex].get_words()[newWordIndex].set_text(oldSequenceList[oldSequenceIndex].get_words()[oldWordIndex].get_text()) oldWordIndex += 1 newWordIndex += 1 def extract_word_list(self): wordList = [] for sequence in self.sequenceList: for word in sequence.get_words(): wordList.append(word.get_valid()) return wordList def goto_sequence(self, id): self.currentSequenceId = id self.update_current_infos() def goto_next_sequence(self): if self.currentSequenceId < len(self.sequenceList)-1: self.currentSequenceId += 1 self.update_current_infos() return True else: return False def goto_previous_sequence(self): if self.currentSequenceId > 0: self.currentSequenceId -= 1 self.update_current_infos() return True else: return False def update_current_infos(self): self.currentSequence = self.sequenceList[self.currentSequenceId] self.currentSequenceValid = self.currentSequence.is_valid() def is_paths_valid(self): error = False errorList = [] if not os.path.exists(self.videoPath): error = True; errorList.append(self.videoPath) if not os.path.exists(self.exercisePath): error = True; errorList.append(self.exercisePath) if self.translationPath != "" and not os.path.exists(self.translationPath): error = True; errorList.append(self.translationPath) return (not error), errorList def increment_repeat_count(self): self.repeatCount += 1 def set_video_path(self, videoPath): self.videoPath = videoPath def set_exercise_path(self, exercisePath): self.exercisePath = exercisePath def set_translation_path(self, translationPath): self.translationPath = translationPath #Define path to use when the parent file is exported def set_video_export_path(self, videoPath): self.videoExportPath = videoPath #Define path to use when the parent file is exported def set_exercise_export_path(self, exercisePath): self.exerciseExportPath = exercisePath #Define path to use when the parent file is exported def set_translation_export_path(self, translationPath): self.translationExportPath = translationPath def set_current_sequence(self, id): if id >= len(self.sequenceList): self.currentSequenceId = len(self.sequenceList)-1 else: self.currentSequenceId = id def get_sequence_list(self): return self.sequenceList def get_current_sequence(self): return self.sequenceList[self.currentSequenceId] def get_current_sequence_id(self): return self.currentSequenceId def get_sequence_count(self): return len(self.sequenceList) def get_video_path(self): return self.videoPath def get_exercise_path(self): return self.exercisePath def get_translation_path(self): return self.translationPath #get path to use when the parent file is exported. If no specila #path is set, the absolute path is used def get_video_export_path(self): if self.videoExportPath: return self.videoExportPath else: return self.videoPath #get path to use when the parent file is exported. If no specila #path is set, the absolute path is used def get_exercise_export_path(self): if self.exerciseExportPath: return self.exerciseExportPath else: return self.exercisePath #get path to use when the parent file is exported. If no specila #path is set, the absolute path is used def get_translation_export_path(self): if self.translationExportPath: return self.translationExportPath else: return self.translationPath def get_translation_list(self): return self.translationList perroquet-1.1.1.orig/perroquetlib/debug.py0000644000175000017500000000226311561344350021057 0ustar georgeskgeorgesk#! /usr/bin/python # -*- coding: utf-8 -*- # Copyright (C) 2009-2010 Frédéric Bertolus. # Copyright (C) 2009-2010 Matthieu Bizien. # # This file is part of Perroquet. # # Perroquet 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 3 of the License, or # (at your option) any later version. # # Perroquet 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 Perroquet. If not, see . import logging import sys from perroquetlib.config import config # Build some logger related objects defaultLoggingHandler = logging.StreamHandler(sys.stdout) defaultLoggingHandler.setFormatter(logging.Formatter("%(asctime)s.%(msecs)d-[%(name)s::%(levelname)s] %(message)s", "%a %H:%M:%S")) defaultLoggingLevel = logging.DEBUG defaultLoggingLevel = logging._levelNames[config.get("default_debug_level")]perroquet-1.1.1.orig/perroquetlib/video_player.py0000644000175000017500000001553411561344350022460 0ustar georgeskgeorgesk# -*- coding: utf-8 -*- # Copyright (C) 2009-2010 Frédéric Bertolus. # # This file is part of Perroquet. # # Perroquet 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 3 of the License, or # (at your option) any later version. # # Perroquet 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 Perroquet. If not, see . import logging import sys import thread import time import gst import gtk # Build some logger related objects defaultLoggingHandler = logging.StreamHandler(sys.stdout) defaultLoggingHandler.setFormatter(logging.Formatter("%(asctime)s.%(msecs)d-[%(name)s::%(levelname)s] %(message)s", "%a %H:%M:%S")) defaultLoggingLevel = logging.DEBUG from gettext import gettext as _ from perroquetlib.config import config class VideoPlayer: def __init__(self): self.player = gst.Pipeline() self.playbin = gst.element_factory_make("playbin2", "player") # Disable the subtitle display if there is embeded subtitles # (for example, in mkv files) # # Flags activates some things # (1 << 0) : video # (1 << 1) : audio # (1 << 4) : software volume # # The default value is 0, 1, 2, 4. (1 << 2) display the subtitles # # For more details, see the doc # http://www.gstreamer.net/data/doc/gstreamer/head/gst-plugins-base-plugins/html/gst-plugins-base-plugins-playbin2.html#GstPlayFlags # http://www.gstreamer.net/data/doc/gstreamer/head/gst-plugins-base-plugins/html/gst-plugins-base-plugins-playbin2.html#GstPlayBin2--flags self.playbin.set_property("flags", (1 << 0)|(1 << 1)|(1 << 4)) self.player.add(self.playbin) self.logger = logging.Logger("VideoPlayer") self.logger.setLevel(defaultLoggingLevel) self.logger.addHandler(defaultLoggingHandler) #Audio audiobin = gst.Bin("audio-speed-bin") try: self.audiospeedchanger = gst.element_factory_make("pitch") self.canChangeSpeed = True except gst.ElementNotFoundError: self.logger.warn(_(u"You need to install the gstreamer soundtouch elements to " "use slowly play feature.")) self.canChangeSpeed = False #Try to use the pitch element only if it is available if self.canChangeSpeed and config.get("interface_use_speed_change"): audiobin.add(self.audiospeedchanger) self.audiosink = gst.element_factory_make("autoaudiosink") audiobin.add(self.audiosink) convert = gst.element_factory_make("audioconvert") audiobin.add(convert) gst.element_link_many(self.audiospeedchanger, convert, self.audiosink) sink_pad = gst.GhostPad("sink", self.audiospeedchanger.get_pad("sink")) audiobin.add_pad(sink_pad) self.playbin.set_property("audio-sink", audiobin) bus = self.player.get_bus() bus.add_signal_watch() bus.enable_sync_message_emission() bus.connect("message", self.on_message) bus.connect("sync-message::element", self.on_sync_message) self.time_format = gst.Format(gst.FORMAT_TIME) self.timeToSeek = -1 self.speed = 1.0 self.nextCallbackTime = -1 def on_message(self, bus, message): t = message.type if t == gst.MESSAGE_EOS: self.player.set_state(gst.STATE_NULL) elif t == gst.MESSAGE_ERROR: self.player.set_state(gst.STATE_NULL) err, debug = message.parse_error() self.logger.error("Error: %s" % (err, debug)) def on_sync_message(self, bus, message): if message.structure is None: return message_name = message.structure.get_name() if message_name == "prepare-xwindow-id": gtk.gdk.threads_enter() gtk.gdk.display_get_default().sync() imagesink = message.src imagesink.set_property("force-aspect-ratio", True) imagesink.set_xwindow_id(self.windowId) self.activate_video_area(True) gtk.gdk.threads_leave() def open(self, path): self.playbin.set_property("uri", "file://" + path) self.play_thread_id = thread.start_new_thread(self.play_thread, ()) self.player.set_state(gst.STATE_PAUSED) self.playing = False def play(self): self.player.set_state(gst.STATE_PLAYING) self.playing = True def set_speed(self, speed): if self.canChangeSpeed: self.audiospeedchanger.set_property("tempo", speed) if self.nextCallbackTime != -1: self.nextCallbackTime = self.nextCallbackTime * speed / self.speed self.speed = speed def pause(self): self.player.set_state(gst.STATE_PAUSED) self.playing = False def is_paused(self): return not self.playing def is_speed_changeable(self): return self.canChangeSpeed def seek(self, time): value = int(time * 1000000) self.playbin.seek_simple(gst.FORMAT_TIME, gst.SEEK_FLAG_FLUSH, value) def seek_as_soon_as_ready(self, time): self.timeToSeek = time def set_callback(self, callback): self.callback = callback def set_next_callback_time(self, nextCallbackTime): self.nextCallbackTime = nextCallbackTime def set_window_id(self, windowId): self.windowId = windowId def activate_video_callback(self, activate_video): self.activate_video_area = activate_video def get_current_time(self): pos_int = -1 try: pos_int = self.playbin.query_position(self.time_format, None)[0] except: pass if pos_int != -1: return int(self.speed * pos_int / 1000000) else: return None def play_thread(self): play_thread_id = self.play_thread_id while play_thread_id == self.play_thread_id: time.sleep(0.1) pos_int = -1 try: pos_int = self.player.query_position(self.time_format, None)[0] except: pass if pos_int != -1 and self.nextCallbackTime != -1 and self.speed * pos_int > self.nextCallbackTime * 1000000: self.nextCallbackTime = -1 self.callback() if pos_int != -1 and self.timeToSeek != -1: self.seek(self.timeToSeek) self.timeToSeek = -1 def close(self): self.player.set_state(gst.STATE_NULL) perroquet-1.1.1.orig/TODO0000644000175000017500000000011011561344350015357 0ustar georgeskgeorgeskPour la 1.1: -Derniers exos (?) Pour la 1.2: -Désinstallation d'exos perroquet-1.1.1.orig/MAINTAINERS0000644000175000017500000000011311561344350016367 0ustar georgeskgeorgeskFrédéric Bertolus E-mail: fred.bertolus@gmail.com Userid: fred-bertolus perroquet-1.1.1.orig/setup.py0000755000175000017500000002233711561344350016423 0ustar georgeskgeorgesk#! /usr/bin/python # -*- coding: utf-8 -*- # Copyright (C) 2009-2010 Frédéric Bertolus. # Copyright (C) 2009-2010 Matthieu Bizien. # # This file is part of Perroquet. # # Perroquet 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 3 of the License, or # (at your option) any later version. # # Perroquet 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 Perroquet. If not, see . import glob import os import platform import subprocess import sys from distutils.cmd import Command from distutils.command.build import build from distutils.command.install_data import install_data from distutils.core import setup from distutils.dep_util import newer from distutils.dist import Distribution from distutils.errors import DistutilsFileError from distutils.log import error, info, warn from perroquetlib.config import config PO_DIR = 'po' MO_DIR = os.path.join('build', 'mo') class PerroquetDist(Distribution): global_options = Distribution.global_options + [ ("without-gettext", None, "Don't build/install gettext .mo files"), ("without-icon-cache", None, "Don't attempt to run gtk-update-icon-cache"), ("without-mime-database", None, "Don't attempt to run update-mime-database"), ("without-desktop-database", None, "Don't attempt to run update-desktop-database")] def __init__ (self, * args): self.without_gettext = False self.without_icon_cache = False self.without_mime_database = False self.without_desktop_database = False Distribution.__init__(self, * args) class BuildData(build): def run (self): build.run (self) if self.distribution.without_gettext: return for po in glob.glob (os.path.join (PO_DIR, '*.po')): lang = os.path.basename(po[:-3]) mo = os.path.join(MO_DIR, lang, 'LC_MESSAGES/perroquet.mo') directory = os.path.dirname(mo) if not os.path.exists(directory): info('creating %s' % directory) os.makedirs(directory) if newer(po, mo): info('compiling %s -> %s' % (po, mo)) try: rc = subprocess.call(['msgfmt', '-o', mo, po]) if rc != 0: raise Warning, "msgfmt returned %d" % rc except Exception, e: error("Building gettext files failed. Try setup.py --without-gettext [build|install]") error("Error: %s" % str(e)) sys.exit(1) TOP_BUILDDIR = '.' INTLTOOL_MERGE = 'intltool-merge' desktop_in = 'data/perroquet.desktop.in' desktop_data = 'data/perroquet.desktop' os.system ("C_ALL=C " + INTLTOOL_MERGE + " -d -u -c " + TOP_BUILDDIR + "/po/.intltool-merge-cache " + TOP_BUILDDIR + "/po " + desktop_in + " " + desktop_data) class Uninstall(Command): description = "Attempt an uninstall from an install --record file" user_options = [('manifest=', None, 'Installation record filename')] def initialize_options(self): self.manifest = None def finalize_options(self): pass def get_command_name(self): return 'uninstall' def run(self): self.ensure_filename('manifest') try: try: f = open(self.manifest) files = [file.strip() for file in f] except IOError, e: raise DistutilsFileError("unable to open install manifest: %s", str(e)) finally: f.close() for file in files: if os.path.isfile(file) or os.path.islink(file): info("removing %s" % repr(file)) if not self.dry_run: try: os.unlink(file) except OSError, e: warn("could not delete: %s" % repr(file)) elif not os.path.isdir(file): info("skipping %s" % repr(file)) dirs = set() for file in reversed(sorted(files)): dir = os.path.dirname(file) if dir not in dirs and os.path.isdir(dir) and len(os.listdir(dir)) == 0: dirs.add(dir) # Only nuke empty Python library directories, else we could destroy # e.g. locale directories we're the only app with a .mo installed for. if dir.find("site-packages/") > 0: info("removing %s" % repr(dir)) if not self.dry_run: try: os.rmdir(dir) except OSError, e: warn("could not remove directory: %s" % str(e)) else: info("skipping empty directory %s" % repr(dir)) class InstallData(install_data): def run (self): self.data_files.extend (self._find_mo_files ()) install_data.run (self) if not self.distribution.without_icon_cache: self._update_icon_cache () if not self.distribution.without_mime_database: self._update_mime_database() if not self.distribution.without_desktop_database: self._update_desktop_database() # We should do this on uninstall too def _update_icon_cache(self): info("running gtk-update-icon-cache") try: subprocess.call(["gtk-update-icon-cache", "-q", "-f", "-t", os.path.join(self.install_dir, "share/icons/hicolor")]) except Exception, e: warn("updating the GTK icon cache failed: %s" % str(e)) def _update_mime_database(self): info("running update-mime-database") try: subprocess.call(["update-mime-database", os.path.join(self.install_dir, "share/mime")]) except Exception, e: warn("updating mime database failed: %s" % str(e)) def _update_desktop_database(self): info("running update-desktop-database") try: subprocess.call(["update-desktop-database",]) except Exception, e: warn("updating desktop database failed: %s" % str(e)) def _find_mo_files (self): data_files = [] if not self.distribution.without_gettext: for mo in glob.glob (os.path.join (MO_DIR, '*', 'LC_MESSAGES/perroquet.mo')): lang = os.path.basename(os.path.dirname(os.path.dirname(mo))) dest = os.path.join('share', 'locale', lang, 'LC_MESSAGES') data_files.append((dest, [mo])) return data_files if platform.system() == 'FreeBSD': man_dir = 'man' else: man_dir = 'share/man' setup(name='perroquet', version=config.get("version"), description='Perroquet, listening comprehension tutor ', author='Perroquet Team', author_email='perroquet-team@lists.launchpad.net', url='http://perroquet.b219.org', license='GNU GPL v3', scripts=['perroquet'], data_files=[ ('share/applications', ['data/perroquet.desktop']), ('share/mime/packages', ['data/perroquet.xml']), ('share/pixmaps', ['data/icons/48x48/apps/perroquet.png']), ('share/icons/hicolor/scalable/apps', glob.glob('data/icons/scalable/apps/*.svg')), ('share/icons/hicolor/16x16/apps', glob.glob('data/icons/16x16/apps/*.png')), ('share/icons/hicolor/22x22/apps', glob.glob('data/icons/22x22/apps/*.png')), ('share/icons/hicolor/24x24/apps', glob.glob('data/icons/24x24/apps/*.png')), ('share/icons/hicolor/32x32/apps', glob.glob('data/icons/32x32/apps/*.png')), ('share/icons/hicolor/48x48/apps', glob.glob('data/icons/48x48/apps/*.png')), ('share/icons/hicolor/256x256/apps', glob.glob('data/icons/256x256/apps/*.png')), ('share/icons/hicolor/scalable/mimetypes', glob.glob('data/icons/scalable/mimetypes/*.svg')), ('share/perroquet/', ['data/gui_message_dialog.ui']), ('share/perroquet/', ['data/reset.ui']), ('share/perroquet/', ['data/perroquet.ui']), ('share/perroquet/', ['data/properties.ui']), ('share/perroquet/', ['data/settings.ui']), ('share/perroquet/', ['data/exercise_manager.ui']), ('share/perroquet/', ['data/gui_password_dialog.ui']), ('share/perroquet/', ['data/properties_advanced.ui']), ('share/perroquet/', ['data/audio_icon.png']), ('share/perroquet/', ['data/perroquet.png']), ('share/perroquet/', ['data/languages.list']), ('/etc/perroquet/', ['data/config.ini']), ('/etc/perroquet/', ['data/languages_aliases.ini']), ('/etc/perroquet/', ['data/sources.conf']), ], packages=['perroquetlib', 'perroquetlib.config', 'perroquetlib.gui', 'perroquetlib.model', 'perroquetlib.model.exercise_parser', 'perroquetlib.model.sequence', 'perroquetlib.repository'], cmdclass={'build': BuildData, 'install_data': InstallData, 'uninstall': Uninstall}, distclass=PerroquetDist ) perroquet-1.1.1.orig/ChangeLog0000644000175000017500000000316111561344350016452 0ustar georgeskgeorgesk> 2011/05/08 - 1.1.1 release - No more display subtitles embeded is a mkv file - Fix a project loading bug in in progress sequences - Place the cursor at the end of a moved word - Fix deletion bug - Fix the cursor position when an alias is used - Fix the cursor position whe the last word is completed - Fix the cursor position after using hint - Fix crash if a config key is missing in the config - Update the translation > 2010/06/19 - 1.1.0 release - Added a repository manager - Packages import and export - Refactoring most of the code - Implement exercise languages - Added aliases and synonyms - Show last open files - Added many teaching tools - Change exercise properties - Exercise template - Tool to play sequence in random order - Tool to reset an exercise - Add support of french and spannish exercises - Support multi files exercises - Add many new help tools (change the play speed, complete a word or a sequence...) - Words are now in color to indicate the distance to the word to find - A good word at the wrong place is automaticaly moved at the right place - Autosave exercise at exit - Settings - Fix various bugs - Add various translations > 2010/01/13 - 1.0.1 release - Improve window resize - Add new shortcuts - Remove html tags in srt files - replace | by new line in srt - Handle perroquet file, add mime type and icons on perroquet exercise - Improve exercice creation dialog - Add tooltips - Reimplement input system to support specials characters - Fix various bugs - Translation updates > 2010/01/01 - 1.0.0 release - Initial realease perroquet-1.1.1.orig/perroquet0000755000175000017500000000170611561344350016657 0ustar georgeskgeorgesk#! /usr/bin/env python # -*- coding: utf-8 -*- # Copyright (C) 2009-2010 Frédéric Bertolus. # # This file is part of Perroquet. # # Perroquet 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 3 of the License, or # (at your option) any later version. # # Perroquet 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 Perroquet. If not, see . import sys from perroquetlib.perroquet import Perroquet from perroquetlib.config import Config def main(): config = Config() app = Perroquet() app.run() if __name__ == '__main__': main()